From 64c4f70cd96a3b35388a1ee8abdeba11afabf38a Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 27 Mar 2024 10:00:38 -0700 Subject: [PATCH 0001/2019] [DO NOT REVIEW] Cherry-picked reverts for 24.2 (#35837) Co-authored-by: Brendan Hansknecht Co-authored-by: Rob Parolin [Internal Link] Co-authored-by: Rashid Kaleem Co-authored-by: Joe Loser Co-authored-by: Jakub Tucholski modular-orig-commit: 3e05a70a0502fdb9a0cfebd84dcf379b8e400cf9 --- docs/changelog.md | 13 ------------- examples/notebooks/HelloMojo.ipynb | 1 - stdlib/test/memory/test_memory.mojo | 1 - 3 files changed, 15 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0d6a93fd22..4dd0e8a43b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -533,19 +533,6 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. out for: a given struct should use one style or the other, mixing some of each won't work well. -- The `inout self` initializer form is **required** for initializers of - `@register_passable` types that may raise errors: - - ```mojo - @register_passable - struct RaisingCtor: - fn __init__(inout self) raises: - raise - ``` - -- `async` functions that may raise errors have been temporarily disabled in this - build. The implementation of Mojo async is undergoing a rework 🚧. - - The standard library `slice` type has been renamed to [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` function has been introduced. This makes Mojo closer to Python and makes the diff --git a/examples/notebooks/HelloMojo.ipynb b/examples/notebooks/HelloMojo.ipynb index a60f594dd6..0159f99c22 100644 --- a/examples/notebooks/HelloMojo.ipynb +++ b/examples/notebooks/HelloMojo.ipynb @@ -65,7 +65,6 @@ } ], "source": [ - "#| XFAIL: *\n", "#| CHECK: Hello Mojo!\n", "print(\"Hello Mojo!\")" ] diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 5703255fcf..4ec1217b02 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -279,7 +279,6 @@ def test_pointer_refitem(): def test_pointer_refitem_string(): alias payload = "$Modular!Mojo!HelloWorld^" var ptr = Pointer[String].alloc(1) - __get_address_as_uninit_lvalue(ptr.address) = String() ptr[] = payload assert_equal(ptr[], payload) ptr.free() From 01ec9cee2a9e3cbcd27037a4e5defb57731043ec Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 10:13:28 -0700 Subject: [PATCH 0002/2019] [docs] Cherrypick doc changes to `docs/external/` files (#35948) Various changes for the v24.2 release that did not make the cut. (OSS files coming in separately) NOTE: This required a manual conflict resolution due to the refactoring of mojo/changelog.md and changelog-release.md, which was not accounted for in [Internal Link] --------- Co-authored-by: Arthur Evans Co-authored-by: Nick Kreeger Co-authored-by: Jack Clayton Co-authored-by: modularbot [Internal Link] modular-orig-commit: a1f3c94fab6f75774e74b5184289633fe24ffe82 --- docs/changelog-released.md | 4356 +++++++++++++++++++++++++++++++++++ docs/changelog.md | 4364 +----------------------------------- docs/manual/packages.md | 8 +- docs/roadmap.md | 58 +- docs/tools/debugging.ipynb | 2 +- docs/upgrade-guide.md | 6 +- 6 files changed, 4427 insertions(+), 4367 deletions(-) create mode 100644 docs/changelog-released.md diff --git a/docs/changelog-released.md b/docs/changelog-released.md new file mode 100644 index 0000000000..c595ac18e9 --- /dev/null +++ b/docs/changelog-released.md @@ -0,0 +1,4356 @@ +--- +title: Mojo🔥 changelog +sidebar_label: Changelog +description: A history of significant Mojo changes. +toc_max_heading_level: 2 +website: + open-graph: + image: /static/images/mojo-social-card.png + twitter-card: + image: /static/images/mojo-social-card.png +--- + +This is a running list of significant changes for the Mojo language and tools. +It doesn't include all internal implementation changes. + +## Update Mojo + +If you don't have Mojo yet, see the [get started +guide](/mojo/manual/get-started/#get-the-mojo-sdk). + +To see your Mojo version, run this: + +```sh +mojo --version +``` + +To update Mojo, first [update `modular`](/cli/#description), and then run this: + +```sh +modular update mojo +``` + +## v24.1.1 (2024-03-18) + +This release includes installer improvements and enhanced error reporting for +installation issues. Otherwise it is functionally identical to Mojo 24.1. + +## v24.1 (2024-02-29) + +### 🔥 Legendary + +- Mojo is now bundled with [the MAX platform](/max)! + + As such, the Mojo package version now matches the MAX version, which follows + a `YY.MAJOR.MINOR` version scheme. Because this is our first release in 2024, + that makes this version `24.1`. + +- Mojo debugging support is here! The Mojo VS Code extension includes debugger + support. For details, see [Debugging](/mojo/tools/debugging) in the Mojo + Manual. + +### ⭐️ New + +- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our + collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` + checks, and requires member elements to conform to the `KeyElement` trait. + + ```mojo + from collections import Set + + var set = Set[Int](1, 2, 3) + print(len(set)) # 3 + set.add(4) + + for element in set: + print(element[]) + + set -= Set[Int](3, 4, 5) + print(set == Set[Int](1, 2)) # True + print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True + let element = set.pop() + print(len(set)) # 1 + ``` + +- Mojo now supports the `x in y` expression as syntax sugar for + `y.__contains__(x)` as well as `x not in y`. + +- Mojo now has support for keyword-only arguments and parameters. For example: + + ```mojo + fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2): + print(a * b * c * d) + + my_product(3, c=5) # prints '30' + my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c' + ``` + + This includes support for declaring signatures that use both variadic and + keyword-only arguments/parameters. For example, the following is now possible: + + ```mojo + fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: + var res = 1 + for i in range(len(args)): + res *= args[i] + return res + offset + + print(prod_with_offset(2, 3, 4, 10)) # prints 240 + print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 + ``` + + Note that variadic keyword-only arguments/parameters (for example, `**kwargs`) + are not supported yet. That is, the following is not allowed: + + ```mojo + fn variadic_kw_only(a: Int, **kwargs): ... + ``` + + For more information, see + [Positional-only and keyword-only arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments) + in the Mojo Manual. + +- The `print()` function now accepts a keyword-only argument for the `end` + which is useful for controlling whether a newline is printed or not + after printing the elements. By default, `end` defaults to "\n" as before. + +- The Mojo SDK can now be installed on AWS Graviton instances. + +- A new version of the [Mojo Playground](/mojo/playground) is available. The new + playground is a simple interactive editor for Mojo code, similar to the Rust + Playground or Go Playground. The old + [JupyterLab based playground](https://playground.modular.com) will remain + online until March 20th. + +- The Mojo LSP server will now generate fixits for populating empty + documentation strings: + + ```mojo + fn foo(arg: Int): + """""" # Unexpected empty documentation string + ``` + + Applying the fixit from above will generate: + + ```mojo + fn foo(arg: Int): + """[summary]. + + Args: + arg: [description]. + """ + ``` + +- Added new `*_` syntax that allows users to explicitly unbind any number of + positional parameters. For example: + + ```mojo + struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass + + alias all_unbound = StructWithDefault[*_] + # equivalent to + alias all_unbound = StructWithDefault[_, _, _, _] + + alias first_bound = StructWithDefault[5, *_] + # equivalent to + alias first_bound = StructWithDefault[5, _, _, _] + + alias last_bound = StructWithDefault[*_, 6] + # equivalent to + alias last_bound = StructWithDefault[_, _, _, 6] + + alias mid_unbound = StructWithDefault[3, *_, 4] + # equivalent to + alias mid_unbound = StructWithDefault[3, _, _, 4] + ``` + + As demonstrated above, this syntax can be used to explicitly unbind an + arbitrary number of parameters, at the beginning, at the end, or in the + middle of the operand list. Since these unbound parameters must be explicitly + specified at some point, default values for these parameters are not applied. + For example: + + ```mojo + alias last_bound = StructWithDefault[*_, 6] + # When using last_bound, you must specify a, b, and c. last_bound + # doesn't have a default value for `c`. + var s = last_bound[1, 2, 3]() + ``` + + For more information see the Mojo Manual sections on + [partially-bound types](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types) + and + [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). + +- [`DynamicVector`](/mojo/stdlib/collections/list#list) now + supports iteration. Iteration values are instances of + [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: + + ```mojo + var v: DynamicVector[String]() + v.append("Alice") + v.append("Bob") + v.append("Charlie") + for x in v: + x[] = str("Hello, ") + x[] + for x in v: + print(x[]) + ``` + +- `DynamicVector` now has + [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and + [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. + +- The `mojo package` command now produces compilation agnostic packages. + Compilation options such as O0, or --debug-level, are no longer needed or + accepted. As a result, packages are now smaller, and extremely portable. + +- Initializers for `@register_passable` values can (and should!) now be + specified with `inout self` arguments just like memory-only types: + + ```mojo + @register_passable + struct YourPair: + var a: Int + var b: Int + fn __init__(inout self): + self.a = 42 + self.b = 17 + fn __copyinit__(inout self, existing: Self): + self.a = existing.a + self.b = existing.b + ``` + + This form makes the language more consistent, more similar to Python, and + easier to implement advanced features for. There is also no performance + impact of using this new form: the compiler arranges to automatically return + the value in a register without requiring you to worry about it. + + The older `-> Self` syntax is still supported in this release, but will be + removed in a subsequent one, so please migrate your code. One thing to watch + out for: a given struct should use one style or the other, mixing some of + each won't work well. + +- The standard library `slice` type has been renamed to + [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` + function has been introduced. This makes Mojo closer to Python and makes the + `Slice` type follow the naming conventions of other types like `Int`. + +- "Slice" syntax in subscripts is no longer hard coded to the builtin `slice` + type: it now works with any type accepted by a container's `__getitem__()` + method. For example: + + ```mojo + @value + struct UnusualSlice: + var a: Int + var b: Float64 + var c: String + + struct YourContainer: + fn __getitem__(self, slice: UnusualSlice) -> T: ... + ``` + + Given this implementation, you can subscript into an instance of + `YourContainer` like `yc[42:3.14:"🔥"]` and the three values are passed to the + `UnusualSlice` constructor. + +- The `__refitem__()` accessor method may now return a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to + return an MLIR internal reference type. + +- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) + method, for moving a value from one pointer memory location to another. + +- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be + used to format any value whose type implements the + [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. + +- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements + `__is__` and `__isnot__` so that you can use expressions of the form `x is y` + and `x is not y` with `PythonObject`. + +- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the + `SizedRaising` trait. This means the built-in + [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. + +- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) + and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. + +- A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query + properties on paths. + +- The `os` package now has a + [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms + to the `PathLike` trait by implementing the `__fspath__()` function. + +- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to + query properties of the path. + +- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on + [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` + module to work on `PathLike` structs. For example, the following sample + lists all the directories in the `/tmp` directory: + + ```mojo + from pathlib import Path + + fn walktree(top: Path, inout files: DynamicVector[Path]): + try: + var ls = top.listdir() + for i in range(len(ls)): + var child = top / ls[i] + if child.is_dir(): + walktree(child, files) + elif child.is_file(): + files.append(child) + else: + print("Skipping '" + str(child) + "'") + except: + return + + fn main(): + var files = DynamicVector[Path]() + + walktree(Path("/tmp"), files) + + for i in range(len(files)): + print(files[i]) + ``` + +- The [`find()`](/mojo/stdlib/builtin/string_literal#find), + [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), + [`count()`](/mojo/stdlib/builtin/string_literal#count), and + [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods + now work on string literals. This means that you can write: + + ```mojo + if "Mojo" in "Hello Mojo": + ... + ``` + +- Breakpoints can now be inserted programmatically within the code using the + builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. + + Note: on Graviton instances, the debugger might not be able to resume after + hitting this kind of breakpoint. + +- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that + describes a type that can be represented as a boolean value. To conform to the + trait, a type must implement the `__bool__()` method. + +- Modules within packages can now use purely relative `from` imports: + + ```mojo + from . import another_module + ``` + +- Trivial types, like MLIR types and function types, can now be bound implicitly + to traits that require copy constructors or move constructors, such as + [`Movable`](/mojo/stdlib/builtin/value.html#movable), + [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). + +- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory + value. We hope and expect that this will eventually be replaced by + `Reference(expr).lifetime` as the parameter system evolves, but this is + important in the meantime for use in function signatures. + +- A new magic `__type_of(expr)` call will yield the type of a value. This allows + one to refer to types of other variables. For example: + + ```mojo + fn my_function(x: Int, y: __type_of(x)) -> Int: + let z: __type_of(x) = y + return z + ``` + +### 🦋 Changed + +- As another step towards [removing let + declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md) + we have removed support for let declarations inside the compiler. To ease + migration, we parse `let` declarations as a `var` declaration so your code + won't break. We emit a warning about this, but please switch your code to + using `var` explicitly, because this migration support will be removed in a + subsequent update. + + ```mojo + fn test(): + # treated as a var, but please update your code! + let x = 42 # warning: 'let' is being removed, please use 'var' instead + x = 9 + ``` + +- It is no longer possible to explicitly specify implicit argument parameters in + [automatically parameterized + functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). + This ability was an oversight and this is now an error: + + ```mojo + fn autoparameterized(x: SIMD): + pass + + autoparameterized[DType.int32, 1](3) # error: too many parameters + ``` + +- `vectorize_unroll` has been removed, and + [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter + named `unroll_factor` with a default value of 1. Increasing `unroll_factor` + may improve performance at the cost of binary size. See the + [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) + for more details. + +- The `vectorize` signatures have changed with the closure `func` moved to the + first parameter: + + ```mojo + vectorize[func, width, unroll_factor = 1](size) + vectorize[func, width, size, unroll_factor = 1]() + ``` + + The doc string has been updated with examples demonstrating the difference + between the two signatures. + +- The `unroll` signatures have changed with the closure `func` moved to the + first parameter: + + ```mojo + unroll[func, unroll_count]() + ``` + +- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) and + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types have changed. Now, both + take the type as the first parameter and no longer require the shape + parameter. This allows you to use these types and have sensible defaults. + For example: + + ```mojo + NDBuffer[DType.float32, 3] + ``` + + is equivalent to + + ```mojo + NDBuffer[DType.float32, 3, DimList.create_unknown[3]()] + ``` + + Users can still specify the static shape (if known) to the type: + + ```mojo + NDBuffer[DType.float32, 3, DimList(128, 128, 3)] + ``` + +- The error message for missing function arguments is improved: instead of + describing the number of arguments (e.g. `callee expects at least 3 arguments, + but 1 was specified`) the missing arguments are now described by + name (e.g. `missing 2 required positional arguments: 'b', 'c'`). + +- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait + is now a built-in trait and has been removed from `collections.vector`. + +- The `DynamicVector(capacity: Int)` constructor has been changed to take + `capacity` as a keyword-only argument to prevent implicit conversion from + `Int`. + +- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a + copy. + +- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` + and `toupper()` have been renamed to `str.lower()` and `str.upper()`. + +- The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. + We originally thought about using those as language sugar for references, but + we believe that generic language features combined with the + [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good + experience without dedicated sugar. + +### 🛠️ Fixed + +- [#435](https://github.com/modularml/mojo/issues/435) + Structs with Self type don't always work. +- [#1540](https://github.com/modularml/mojo/issues/1540) + Crash in register_passable self referencing struct. +- [#1664](https://github.com/modularml/mojo/issues/1664) - Improve error + message when `StaticTuple` is constructed with a negative size for + the number of elements. +- [#1679](https://github.com/modularml/mojo/issues/1679) - crash on SIMD of zero + elements. +- Various crashes on invalid code: + [#1230](https://github.com/modularml/mojo/issues/1230), + [#1699](https://github.com/modularml/mojo/issues/1699), + [#1708](https://github.com/modularml/mojo/issues/1708) +- [#1223](https://github.com/modularml/mojo/issues/1223) - Crash when parametric + function is passed as (runtime) argument. The parser now errors out instead. +- [#1530](https://github.com/modularml/mojo/issues/1530) - Crash during + diagnostic emission for parameter deduction failure. +- [#1538](https://github.com/modularml/mojo/issues/1538) and [#1607]( + https://github.com/modularml/mojo/issues/1607) - Crash when returning type + value instead of instance of expected type. This is a common mistake and the + error now includes a hint to point users to the problem. +- [#1613](https://github.com/modularml/mojo/issues/1613) - Wrong type name in + error for incorrect `self` argument type in trait method declaration. +- [#1670](https://github.com/modularml/mojo/issues/1670) - Crash on implicit + conversion in a global variable declaration. +- [#1741](https://github.com/modularml/mojo/issues/1741) - Mojo documentation + generation doesn't show `inout`/`owned` on variadic arguments. +- [#1621](https://github.com/modularml/mojo/issues/1621) - VS Code does not + highlight `raises` and `capturing` in functional type expressions. +- [#1617](https://github.com/modularml/mojo/issues/1617) - VS Code does not + highlight `fn` in specific contexts. +- [#1740](https://github.com/modularml/mojo/issues/1740) - LSP shows unrelated + info when hovering over a struct. +- [#1238](https://github.com/modularml/mojo/issues/1238) - File shadows Mojo + package path. +- [#1429](https://github.com/modularml/mojo/issues/1429) - Crash when using + nested import statement. +- [#1322](https://github.com/modularml/mojo/issues/1322) - Crash when missing + types in variadic argument. +- [#1314](https://github.com/modularml/mojo/issues/1314) - Typecheck error when + binding alias to parametric function with default argument. +- [#1248](https://github.com/modularml/mojo/issues/1248) - Crash when importing + from file the same name as another file in the search path. +- [#1354](https://github.com/modularml/mojo/issues/1354) - Crash when importing + from local package. +- [#1488](https://github.com/modularml/mojo/issues/1488) - Crash when setting + generic element field. +- [#1476](https://github.com/modularml/mojo/issues/1476) - Crash in interpreter + when calling functions in parameter context. +- [#1537](https://github.com/modularml/mojo/issues/1537) - Crash when copying + parameter value. +- [#1546](https://github.com/modularml/mojo/issues/1546) - Modify nested vector + element crashes parser. +- [#1558](https://github.com/modularml/mojo/issues/1558) - Invalid import causes + parser to crash. +- [#1562](https://github.com/modularml/mojo/issues/1562) - Crash when calling + parametric type member function. +- [#1577](https://github.com/modularml/mojo/issues/1577) - Crash when using + unresolved package as a variable. +- [#1579](https://github.com/modularml/mojo/issues/1579) - Member access into + type instances causes a crash. +- [#1602](https://github.com/modularml/mojo/issues/1602) - Interpreter failure + when constructing strings at compile time. +- [#1696](https://github.com/modularml/mojo/issues/1696) - Fixed an issue that + caused syntax highlighting to occasionally fail. +- [#1549](https://github.com/modularml/mojo/issues/1549) - Fixed an issue when + the shift amount is out of range in `SIMD.shift_left` and `SIMD.shift_right`. + +## v0.7.0 (2024-01-25) + +### ⭐️ New + +- A new Mojo-native dictionary type, + [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. + `Dict` stores values that conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + trait. Keys need to conform to the new + [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is + not yet implemented by other standard library types. In the short term, you + can create your own wrapper types to use as keys. For example, the following + sample defines a `StringKey` type and uses it to create a dictionary that maps + strings to `Int` values: + + ```mojo + from collections.dict import Dict, KeyElement + + @value + struct StringKey(KeyElement): + var s: String + + fn __init__(inout self, owned s: String): + self.s = s ^ + + fn __init__(inout self, s: StringLiteral): + self.s = String(s) + + fn __hash__(self) -> Int: + return hash(self.s) + + fn __eq__(self, other: Self) -> Bool: + return self.s == other.s + + fn main() raises: + var d = Dict[StringKey, Int]() + d["cats"] = 1 + d["dogs"] = 2 + print(len(d)) # prints 2 + print(d["cats"]) # prints 1 + print(d.pop("dogs")) # prints 2 + print(len(d)) # prints 1 + ``` + + We plan to add `KeyElement` conformance to standard library types in + subsequent releases. + +- Users can opt-in to assertions used in the standard library code by + specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to + compile your source file(s). In the case that an assertion is fired, + the assertion message will be printed along with the stack trace + before the program exits. By default, assertions are _not enabled_ + in the standard library right now for performance reasons. + +- The Mojo Language Server now implements the References request. IDEs use + this to provide support for **Go to References** and **Find All References**. + A current limitation is that references outside of the current document are + not supported, which will be addressed in the future. + +- The [`sys.info`](/mojo/stdlib/sys/info) module now includes + `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` + functions. + +- Homogenous variadic arguments consisting of memory-only types, such as + `String` are more powerful and easier to use. These arguments are projected + into a + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). + + (Previous releases made it easier to use variadic lists of register-passable + types, like `Int`.) + + Subscripting into a `VariadicListMem` now returns the element instead of an + obscure internal type. In addition, we now support `inout` and `owned` + variadic arguments: + + ```mojo + fn make_worldly(inout *strs: String): + # This "just works" as you'd expect! + for i in range(len(strs)): + strs[i] += " world" + fn main(): + var s1: String = "hello" + var s2: String = "konnichiwa" + var s3: String = "bonjour" + make_worldly(s1, s2, s3) + print(s1) # hello world + print(s2) # konnichiwa world + print(s3) # bonjour world + ``` + + (Previous releases made it easier to use variadic lists, but subscripting into + a `VariadicListMem` returned a low-level pointer, which required the user to + call `__get_address_as_lvalue()` to access the element.) + + Note that subscripting the variadic list works nicely as above, but + iterating over the variadic list directly with a `for` loop produces a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead + of the desired value, so an extra subscript is required; We intend to fix this + in the future. + + ```mojo + fn make_worldly(inout *strs: String): + # Requires extra [] to dereference the reference for now. + for i in strs: + i[] += " world" + ``` + + Heterogenous variadic arguments have not yet been moved to the new model, but + will in future updates. + + Note that for variadic arguments of register-passable types like `Int`, the + variadic list contains values, not references, so the dereference operator + (`[]`) is not required. This code continues to work as it did previously: + + ```mojo + fn print_ints(*nums: Int): + for num in nums: + print(num) + print(len(nums)) + ``` + +- Mojo now has a prototype version of a safe + [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's + lifetime tracking pass can reason about references to safely extend local + variable lifetime, and check indirect access safety. The `Reference` type + is brand new (and currently has no syntactic sugar) so it must be explicitly + dereferenced with an empty subscript: `ref[]` provides access to the + underlying value. + + ```mojo + fn main(): + var a: String = "hello" + var b: String = " references" + + var aref = Reference(a) + aref[] += b + print(a) # prints "hello references" + + aref[] += b + # ^last use of b, it is destroyed here. + + print(aref[]) # prints "hello references references" + # ^last use of a, it is destroyed here. + ``` + + While the `Reference` type has the same in-memory representation as a C + pointer or the Mojo `Pointer` type, it also tracks a symbolic "lifetime" value + so the compiler can reason about the potentially accessed set of values. This + lifetime is part of the static type of the reference, so it propagates through + generic algorithms and abstractions built around it. + + The `Reference` type can form references to both mutable and immutable memory + objects, e.g. those on the stack or borrowed/inout/owned function arguments. + It is fully parametric over mutability, eliminating the [problems with code + duplication due to mutability + specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and + provides the base for unified user-level types. For example, it could be + used to implement an array slice object that handles both mutable and immutable + array slices. + + While this is a major step forward for the lifetimes system in Mojo, it is + still _very_ early and awkward to use. Notably, there is no syntactic sugar + for using references, such as automatic dereferencing. Several aspects of it + need to be more baked. It is getting exercised by variadic memory arguments, + which is why they are starting to behave better now. + + Note: the safe `Reference` type and the unsafe pointer types are defined in + the same module, currently named `memory.unsafe`. We expect to restructure + this module in a future release. + +- Mojo now allows types to implement `__refattr__()` and `__refitem__()` to + enable attribute and subscript syntax with computed accessors that return + references. For common situations where these address a value in memory this + provides a more convenient and significantly more performant alternative to + implementing the traditional get/set pairs. Note: this may be changed in the + future when references auto-dereference—at that point we may switch to just + returning a reference from `__getattr__()`. +- Parametric closures can now capture register passable typed values by copy + using the `__copy_capture` decorator. For example, the following code will + print `5`, not `2`. + + ```mojo + fn foo(x: Int): + var z = x + + @__copy_capture(z) + @parameter + fn formatter() -> Int: + return z + z = 2 + print(formatter()) + + fn main(): + foo(5) + ``` + +- String now implements KeyElement and may be used as a key in Dict. +- More robust support for structs with fields of self referencing types. + For example, the following code will work and print `0`: + + ```mojo + struct Foo(CollectionElement): + var vec: DynamicVector[Self] + + fn __init__(inout self: Self): + self.vec = DynamicVector[Self]() + + fn __moveinit__(inout self: Self, owned existing: Self): + self.vec = existing.vec ^ + + fn __copyinit__(inout self: Self, existing: Self): + self.vec = existing.vec + + + fn main(): + var foo = Foo() + print(len(foo.vec)) + ``` + +### ❌ Removed + +- The `__takeinit__` special constructor form has been removed from the + language. This "non-destructive move" operation was previously wired into the + `x^` transfer operator, but had unpredictable behavior that wasn't consistent. + Now that Mojo has traits, it is better to model this as an explicit `.take()` + operation on a type, which would transfer out the contents of the type without + ending its lifetime. For example, for a type that holds a pointer, `take()` + might return a new instance pointing to the same data, and null out its own + internal pointer. + + This change makes it clear when a lifetime is ended versus when the + contents of an LValue are explicitly taken. + +- The current implementation of autotuning has been deprecated, as Mojo's + autotuning implementation is undergoing a redesign. Tutorials around the + current implementation have also been removed as they are being rewritten. + + Consequently, the `autotune()`, `autotune_fork()`, and `search()` functions + have been removed from the standard library. + +- The `_OldDynamicVector` type that worked only on register passable element + types has been removed. Please migrate uses to + [`DynamicVector`](/mojo/stdlib/collections/list#list) which + works on both register passable and memory types. + +- The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using + either [`DynamicVector`](/mojo/stdlib/collections/list#list) + or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + instead. + +- The `@adaptive` decorator has been removed from the language. Any uses of the + decorator in a non-search context can be replaced with `@parameter if`. For + example: + + ```mojo + @adaptive + fn foo[a: Bool](): + constrained[a]() + body1() + + @adaptive + fn foo[a: Bool](): + constrained[not a]() + body2() + ``` + + Can be rewritten as: + + ```mojo + fn foo[a: Bool](): + @parameter + if a: + body1() + else: + body2() + ``` + + Consequently, the special `__adaptive_set` attribute has been removed as well. + +- Result parameters have been removed from Mojo. Result parameter declarations + in function parameter lists are no longer allowed, nor are forward alias + declarations. This includes removing the `param_return` statement. + +- The `@noncapturing` and `@closure` decorators have been removed due to + refinements and improvements to the closure model. See below for more details! + +### 🦋 Changed + +- The Mojo closure model has been refined to be more straightforward and safe. + Mojo has two closure types: parameter closures and runtime closures. Parameter + closures can be used in higher-order functions and are the backbone of + functions like `vectorize` and `parallelize`. They are always denoted by + `@parameter` and have type `fn() capturing -> T` (where `T` is the return + type). + + On the other hand, runtime closures are always dynamic values, capture values + by invoking their copy constructor, and retain ownership of their capture + state. You can define a runtime closure by writing a nested function that + captures values: + + ```mojo + fn outer(b: Bool, x: String) -> fn() escaping -> None: + fn closure(): + print(x) # 'x' is captured by calling String.__copyinit__ + + fn bare_function(): + print("hello") # nothing is captured + + if b: + # closure can be safely returned because it owns its state + return closure^ + + # function pointers can be converted to runtime closures + return bare_function + ``` + + The type of runtime closures are of the form `fn() escaping -> T`. You + can pass equivalent function pointers as runtime closures. + + Stay tuned for capture list syntax for move capture and capture by reference, + and a more unified closure model! + +- The `@unroll(n)` decorator can now take a parameter expression for + the unroll factor, i.e. `n` can be a parameter expression that is + of integer type. + +- The `cpython` module in the `python` package has been moved to be an internal + module, i.e, `_cpython`. + +- `AnyType` and `Destructable` have been unified into a single trait, `AnyType`. + Every nominal type (i.e. all structs) now automatically conform to `AnyType`. + +- Previously, the `mojo package` command would output a Mojo package that + included both partly-compiled Mojo code, as well as fully-compiled machine + code for a specific computer architecture -- the architecture of the machine + being used to invoke the `mojo package` command. + + Now, `mojo package` only includes partly-compiled Mojo code. It is only fully + compiled for the specific computer architecture being used at the point that + the package is first `import`-ed. As a result, Mojo packages are smaller and + more portable. + +- The `simd_width` and `dtype` parameters of `polynomial_evaluate` have been + switched. Based on the request in + [#1587](https://github.com/modularml/mojo/issues/1587), the + `polynomial_evaluate` function has also been extended so that the + `coefficients` parameter can take either a either a + [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a + [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). + +- As a tiny step towards removing `let` declarations, this release removes the + warning: `'var' was never mutated, consider switching to a 'let'`. + +### 🛠️ Fixed + +- [#1595](https://github.com/modularml/mojo/issues/1595) - Improve error message + when trying to materialize `IntLiteral` in runtime code. +- Raising an error from the initializer of a memory-only type now works + correctly in the presence of complex control flow. Previously Mojo could run + the destructor on `self` before it was initialized when exiting with an + error. +- [#1096](https://github.com/modularml/mojo/issues/1096) - Improve warning + messages for dead code in conditionals like `or` expressions. +- [#1419](https://github.com/modularml/mojo/issues/1419) - Fix assertion failure + with uninitialized lattice values. +- [#1402](https://github.com/modularml/mojo/issues/1402) - Fix movable trait not + detected on recursive struct implemented with `AnyPointer`. +- [#1399](https://github.com/modularml/mojo/issues/1399) - Fix parser crash when + a parameter type in a struct that implements a trait is misspelled. +- [#1152](https://github.com/modularml/mojo/issues/1152) - Allow mutable `self` + argument when overloading operators using dunder methods. +- [#1493](https://github.com/modularml/mojo/issues/1493) - Fix crash in + `DynamicVector` copy constructor in certain situations. +- [#1316](https://github.com/modularml/mojo/issues/1316) - The `benchmark.keep` + function now properly handles vector types. +- [#1505](https://github.com/modularml/mojo/issues/1505) - The `simd.shuffle` + operation now works on 64 element permutations. +- [#1355](https://github.com/modularml/mojo/issues/1355) - Fix `String.find()` + returning wrong value when starting index is non-zero. +- [#1367](https://github.com/modularml/mojo/issues/1367) - Fix `String.replace()` + returning incorrect results for multi-character search strings. +- [#1535](https://github.com/modularml/mojo/issues/1535) - Invalid error `field + 'w.x.y' destroyed out of the middle of a value, preventing the overall value + from being destroyed`. +- [#1475](https://github.com/modularml/mojo/issues/1475) - Assertion failure in + nested loop. +- [#1591](https://github.com/modularml/mojo/issues/1591) - Assertion failure + when using `AnyType` struct member. +- [#1503](https://github.com/modularml/mojo/issues/1503) - Rename the mojo build + of LLDB to `mojo-lldb`, to prevent name collisions with the system's LLDB. +- [#1542](https://github.com/modularml/mojo/issues/1542) - `@unroll` does not + accept alias as unroll factor. +- [#1443](https://github.com/modularml/mojo/issues/1443) - Compiler crash on + variadic list of traits. +- [#1604](https://github.com/modularml/mojo/issues/1604) - Variable of trivial + type not destroyed by transferring ownership. +- [#1341](https://github.com/modularml/mojo/issues/1341) - Segmentation fault + when passing closures around. +- [#217](https://github.com/modularml/mojo/issues/217) - Closure state is + stack allocated. + +## v0.6.1 (2023-12-18) + +### ⭐️ New + +- The Mojo REPL now provides limited support for the `%cd` magic command. + + This command automatically maintains an internal stack of directories you + visit during the REPL session. Usage: + + - `%cd 'dir'`: change to directory `dir` and push it on the directory stack. + - `%cd -`: pop the directory stack and change to the last visited directory. + +- Structs decorated with `@value` now automatically conform to the + [`Movable`](/mojo/stdlib/builtin/value.html#movable) + and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. + +- [`String`](/mojo/stdlib/builtin/string.html#string) now has new + [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and + [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, + respectively, to Python's `str.toupper()` and `str.tolower()`. + +- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and + [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types + implementing the `__hash__()` method. Future releases will add `Hashable` + support to Standard Library types. In the meantime, the `hash` module includes + a version of the `hash()` function that works on arbitrary byte strings. To + generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you + use the internal `_hash_simd()` function: + + ```mojo + from builtin.hash import _hash_simd + + fn gen_simd_hash(): + let vector = SIMD[DType.int64, 4](1, 2, 3, 4) + let hash = _hash_simd(vector) + ``` + +- Several standard library types now conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), + [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), + [`DynamicVector`](/mojo/stdlib/collections/list#list), + [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), + [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), + and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). + +### 🦋 Changed + +- `utils.vector` has been moved to a new `collections` package to make + space for new collections. This means that if you had previous code + that did `from utils.vector import DynamicVector`, it now needs to + be `from collections.vector import DynamicVector` due to the move. + +- The special destructor method `__del__()` has been changed to enforce + that it cannot raise an error. Raising destructors are not supported properly + at the moment. + +### 🛠️ Fixed + +- [#1421](https://github.com/modularml/mojo/issues/1421) - Fixed a crash when + using Tuples in the REPL. + +- [#222](https://github.com/modularml/mojo/issues/222) - Generate an error + for obviously self recursive functions. + +- [#1408](https://github.com/modularml/mojo/issues/1408) - Fix overload + resolution when candidates can return generic types. + +- [#1413](https://github.com/modularml/mojo/issues/1413) and + [#1395](https://github.com/modularml/mojo/issues/1395) - Do not crash when + re-declaring a builtin declaration. + +- [#1307](https://github.com/modularml/mojo/issues/1307) - Fix compatibility of + function signatures that only differ in default argument values. + +- [#1380](https://github.com/modularml/mojo/issues/1380) - Fix printing + of empty `String`. + +## v0.6.0 (2023-12-04) + +### 🔥 Legendary + +- Traits have arrived! + + You can now define a _trait_, which consists of a required set of method + prototypes. A struct can _conform to_ the trait by implementing these methods. + This lets you write generic functions that work on any structs that conform to + a given trait. + + The following section gives a brief overview of traits—see the + [Mojo Manual](/mojo/manual/traits.html) and this + [traits blog post](https://modul.ar/traits-blog) for more details! + + Traits are declared with the `trait` keyword. The bodies of traits should + contain method signatures declared with `...` as their bodies. Default + method implementations are not supported yet. + + ```mojo + trait SomeTrait: + fn required_method(self, x: Int): ... + ``` + + The trait can be implemented on a struct by inheriting from it. + + ```mojo + struct SomeStruct(SomeTrait): + fn required_method(self, x: Int): + print("hello traits", x) + ``` + + You can then write a generic functions that accepts any type that conforms to + the trait. You do this by creating a parameterized function with a + trait-typed parameter: + + ```mojo + fn fun_with_traits[T: SomeTrait](x: T): + x.required_method(42) + ``` + + Which can be invoked with instances of types that conform to the trait: + + ```mojo + var thing = SomeStruct() + # Infer the parameter `T`! + fun_with_traits(thing) + ``` + + Traits can also inherit from other traits, which simply requires that + implementors of the child trait also conform to all parent traits. + + ```mojo + trait Parent: + fn parent_func(self): ... + + trait Child(Parent): + fn child_func(self): ... + ``` + + Then, both child and parent trait methods can be invoked on instances of + the trait `Child`. As well, an instance of the child trait can be converted to + an instance of the parent trait. + + ```mojo + fn the_parents[T: Parent](x: T): + x.parent_func() + + fn the_children[T: Child](x: T): + x.child_func() + x.parent_func() + # Upcast `x` from instance of `Child` to `Parent`. + the_parents(x) + ``` + + For more information, see the [Traits page](/mojo/manual/traits.html) + in the Mojo Manual. + +- A fundamental `Destructable` trait has been added to the language. This is a + core trait that every trait automatically conforms to. This enables + destruction of generic types and generic collections. + + **Note:** We're aware that this trait might be better spelled `Destructible`. + We're planning on removing it in the future and moving its functionality to + `AnyType` so that any type that doesn't provide its own destructor will have + a default, no-op destructor. + +- We've added some traits to the standard library, you can implement these on + your own types: + + - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) + - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) + - [`Movable`](/mojo/stdlib/builtin/value.html#movable) + - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) + - [`Intable`](/mojo/stdlib/builtin/int.html#intable) + - [`Sized`](/mojo/stdlib/builtin/len.html#sized) + - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + +- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), + [`str()`](/mojo/stdlib/builtin/str.html#str), and + [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with + types that implement the `Sized`, `Stringable`, and `Intable` traits, + respectively. + +- [`DynamicVector`](/mojo/stdlib/collections/list#list) is now a + proper generic collection that can use any type that implements the `Movable` + and `Copyable` traits. This means you can now write, for example, + `DynamicVector[String]`. Also, `DynamicVector` now invokes its element + destructors upon destruction, so `_del_old` has been deleted. + +- `print` now works on any types that implement `Stringable` by invoking their + `__str__` method: + + ```mojo + @value + struct BoxedInt(Stringable): + var value: Int + + fn __str__(self) -> String: + return self.value + + print(BoxedInt(11), "hello traits!", BoxedInt(42)) + ``` + +### ⭐️ New + +- The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. + It doesn't include _everything_ about Mojo yet, but it includes a lot, + and more than the original [programming + manual](/mojo/programming-manual.html) (now deprecated). + + Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on + GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love + to accept contributions to help us improve them! + +- Mojo now supports partial automatic parameterization: when a function is + declared with an argument of a partially bound type, the unbound parameters + of that type are implicitly added to the function's input parameters. For + example: + + ```mojo + @value + struct Fudge[a: Int, b: Int, c: Int = 7]: ... + + # These function declarations are roughly equivalent: + fn eat(f: Fudge[5]): ... # implicitly parameterized + fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized + ``` + + In the first signature for `eat()`, the `b` parameter isn't bound, so it's + _implicitly_ added as an input parameter on the function. + + In the second signature for `eat()`, the author has explicitly defined an + input parameter (`_b`), which is bound to the second parameter on the argument + type (which happens to be `b`). + + Both functions can be called like this: + + ```mojo + eat(Fudge[5, 8]()) + ``` + + Mojo infers the value of the `b` parameter from the argument (in this case, + 8). + + With the second signature, you can also pass the `_b` parameter value + explicitly: + + ```mojo + eat[3](Fudge[5, 3]()) + ``` + + Moreover, Mojo now allows you to explicitly mark parameters as unbound using + the `_` as syntax meaning "placeholder for an unbound parameter." For example: + + ```mojo + # These function declarations are roughly equivalent: + fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized + fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized + fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized + ``` + + The first two signatures explicitly unbind the `b` and `c` parameters. + + In the last signature, the `_b` and `_c` parameters are explicitly declared by + the author, and bound to the `b` and `c` parameters in the argument type. + + Any of these signatures can be called like this: + + ```mojo + eat(Fudge[5, 8]()) + eat(Fudge[5, 8, 9]()) + ``` + + Note that the default parameter values of struct parameters are bound, unless + explicitly unbound by the user. + + For more information, see the + [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). + +- Parametric types can now be partially bound in certain contexts. For example, + a new `Scalar` type alias has been added defined as: + + ```mojo + alias Scalar = SIMD[size=1] + ``` + + Which creates a parametric type alias `Scalar` with a single parameter of type + `DType`. Types can also be partially or fully bound in other contexts. For + instance, `alias` declarations of type values inside functions now work + properly: + + ```mojo + fn type_aliases(): + alias T = SIMD + print(T[DType.float32, 1]()) + alias Partial = T[type=DType.int32] + print(Partial[2]()) + ``` + +- The `__mlir_op` feature now supports operations that return multiple results. + To use them, you write the `_type` field as a `Tuple` of types. For example: + + ```mojo + # The `ret` variable has type `Tuple[Int, Int]`. + let ret = __mlir_op.`multi_result_op`[_type=(Int, Int)]() + ``` + +- Mojo now has the ability to read raw bytes from a file using the + [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. + For example: + + ```mojo + with open("file.binary", "r") as f: + data = f.read_bytes() + ``` + +- A size argument was added to the + [`read()`](/mojo/stdlib/builtin/file.html#read) and + [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the + builtin `file.FileHandle`. The size argument defaults to -1 and maintains the + previous "read to EOF" behavior when size is negative. + + ```mojo + with open("file.binary", "r") as f: + data1 = f.read_bytes(1024) + data2 = f.read_bytes(256) + ``` + +- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and + `read_text()` methods to read file contents from a path: + + ```mojo + let text_path = Path("file.txt") + let text = text_path.read_text() + + let binary_path = Path("file.binary") + let data = binary_path.read_bytes() + ``` + +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` + methods to save and load to file. These + methods preserve shape and datatype information. For example: + + ```mojo + let tensor = Tensor[DType.float32]() + tensor.save(path) + + let tensor_from_file = Tensor[DType.float32].load(path) + ``` + +- Subscripting added to + [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and + [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): + + ```mojo + let p = DTypePointer[DType.float16].alloc(4) + for i in range(4): + p[i] = i + print(p[i]) + ``` + +- `file.FileHandle` now has a `seek()` method. + +- [`String`](/mojo/stdlib/builtin/string.html#string) now has an + [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to + Python's `str.rfind()`. + +- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method + analogous to Python's `str.split()`. + +- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a + [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to + Python's `pathlib.Path.suffix`. + +- The Mojo REPL now supports indented expressions, making it a bit easier to + execute expressions copied from an indented block (such as a doc string). + +- The Mojo Language Server now implements the Document Symbols request. IDEs use + this to provide support for **Outline View** and **Go to Symbol**. This + addresses [Issue #960](https://github.com/modularml/mojo/issues/960). + +- The Mojo Language Server now shows documentation when code completing modules + or packages in `import` statements. + +- The Mojo Language Server now supports processing code examples, defined as + markdown Mojo code blocks, inside of doc strings. This enables IDE features + while writing examples in API documentation. + +- The Mojo Language Server now provides semantic token information, providing + better highlighting for symbols whose semantics are not statically analyzable. + +- The Mojo Language Server now classifies doc strings as folding ranges, + making them easier to collapse, reducing vertical space while editing. + +- Command line options for the `mojo` driver that take arguments can now be + written in either of two ways: both `--foo FOO` and `--foo=FOO`. Previously, + only the former was valid. + +### 🦋 Changed + +- Variadic list types + [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + are now iterable. Variadic arguments are automatically projected into one of + these types inside the function body, so var args can be iterated: + + ```mojo + fn print_ints(*nums: Int): + for num in nums: + print(num) + print(len(nums)) + ``` + +- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) + package now raise an `Error` when the assertion fails instead of returning a + `Bool` for whether the assertion succeeded or not. + +- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no + longer (implicitly) assumed to be register-passable. A new `AnyRegType` type + is used to represent generic types that are register passable. + +- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) + report is now an argument instead of a parameter: + + ```mojo + let report = benchmark.run[timer]() + report.print(Unit.ms) + ``` + +- Default values on `inout` arguments are no longer permitted, i.e. the + following will now raise an error: + + ```mojo + fn inout_default(inout x: Int = 2): ... + ``` + +- The `to_string()` function has been removed from + [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of + the new `__str__()` function. This composes better with traits so it can be + used with the generic `str()` function. + +### 🛠️ Fixed + +- [#734](https://github.com/modularml/mojo/issues/734) - Consumption of struct + works only for types with a `__del__` method. + +- [#910](https://github.com/modularml/mojo/issues/910) - Parser crash when + using memory-only generic type as return of function that `raise`s. + +- [#1060](https://github.com/modularml/mojo/issues/1060) - Mojo happily parses + code that has messed up indentation + +- [#1159](https://github.com/modularml/mojo/issues/1159) - The language server + doesn't warn about bad return type. + +- [#1166](https://github.com/modularml/mojo/issues/1166) - warning: unreachable + code after return statement with context manager + +- [#1098](https://github.com/modularml/mojo/issues/1098) - The language server + doesn't highlight properties of PythonObjects correctly. + +- [#1153](https://github.com/modularml/mojo/issues/1153) - The language server + crashes when parsing an invalid multi-nested module import. + +- [#1236](https://github.com/modularml/mojo/issues/1236) - The language server + doesn't show autocomplete in if statements. + +- [#1246](https://github.com/modularml/mojo/issues/1246) - Warning diagnostics + are transient in the presence of caching. + +### Known Issue + +- There is an issue affecting Jupyter notebooks that use autotuning and traits. + This issue only manifests on macOS, and the same code runs without issue + outside of the notebooks. This issue affects the _Matrix multiplication in + Mojo_ notebook. + +## v0.5.0 (2023-11-2) + +### ⭐️ New + +- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the + architectural SIMD width of the type. This means you can write + `SIMD[DType.float32]` which is equivalent to + `SIMD[DType.float32, simdwidthof[DType.float32]()]`. + +- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` + function that allows you to concatenate two `SIMD` values together and produce + a new `SIMD` value. + +- Mojo now supports compile-time _keyword parameters_, in addition to existing + support for [keyword + arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For + example: + + ```mojo + fn foo[a: Int, b: Int = 42](): + print(a, "+", b) + + foo[a=5]() # prints '5 + 42' + foo[a=7, b=13]() # prints '7 + 13' + foo[b=20, a=6]() # prints '6 + 20' + ``` + + Keyword parameters are also supported in structs: + + ```mojo + struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]: + fn __init__(inout self): + print(msg, a) + + fn use_kw_params(): + KwParamStruct[a=42]() # prints '🔥mojo🔥 42' + KwParamStruct[5, msg="hello"]() # prints 'hello 5' + KwParamStruct[msg="hello", a=42]() # prints 'hello 42' + ``` + + For more detail, see the [programming + manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). + + For the time being, the following notable limitations apply: + + - Keyword-only parameters are **not supported** yet: + + ```mojo + fn baz[*args: Int, b: Int](): pass # fails + fn baz[a: Int, *, b: Int](): pass # fails + ``` + + (The analogous keyword-only arguments in Python are described in + [PEP 3102](https://peps.python.org/pep-3102/).) + + - Variadic keyword parameters are **not supported** yet: + + ```mojo + fn baz[a: Int, **kwargs: Int](): pass # fails + ``` + +- Mojo now supports "automatic" parameterization of functions. What this means + is that if a function argument type is parametric but has no bound parameters, + they are automatically added as input parameters on the function. This works + with existing features to allow you to write parametric functions with less + boilerplate. + + ```mojo + @value + struct Thing[x: Int, y: Int]: + pass + + fn foo(v: Thing): + print(v.x) + print(v.y) + + fn main(): + let v = Thing[2, 3]() + foo(v) + ``` + + However, partial autoparameterization is **not supported** yet: + + ```mojo + fn foo(v: Thing[y=7]): # Partially bound type not allowed yet. + ... + ``` + +- Keyword argument passing is supported when invoking `__getitem__` using + the bracket syntax: + + ```mojo + @value + struct MyStruct: + fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: + return x * y + z + + MyStruct()[z=7, x=3, y=5] # returns 22 + ``` + + However, keyword argument passing to `__setitem__` using the bracket syntax is + **not supported** yet: + + ```mojo + @value + struct OtherStruct: + fn __setitem__(self, x: Int, y: Int): pass + + OtherStruct()[x=1] = 4 # fails + ``` + +- Function argument input parameters can now be referenced within the signature + of the function: + + ```mojo + fn foo(x: SIMD, y: SIMD[x.type, x.size]): + pass + ``` + +- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been + simplified and improved so you can now run: + + ```mojo + import benchmark + from time import sleep + + fn sleeper(): + sleep(.01) + + fn main(): + let report = benchmark.run[sleeper]() + print(report.mean()) + ``` + + It no longer requires a capturing `fn` so can benchmark functions outside the + same scope. + + You can print a report with: + + ```mojo + report.print() + ``` + + ```plaintext + --------------------- + Benchmark Report (s) + --------------------- + Mean: 0.012314264957264957 + Total: 1.440769 + Iters: 117 + Warmup Mean: 0.0119335 + Warmup Total: 0.023866999999999999 + Warmup Iters: 2 + Fastest Mean: 0.012227958333333334 + Slowest Mean: 0.012442699999999999 + ``` + + Units for all functions default to seconds, but can be changed with: + + ```mojo + from benchmark import Unit + + report.print[Unit.ms]() + ``` + +- Mojo now supports struct parameter deduction (a.k.a. class template argument + deduction, or CTAD) for partially bound types. Struct parameter deduction is + also possible from static methods. For example: + + ```mojo + @value + struct Thing[v: Int]: pass + + struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]: + fn __init__(inout self, x: Thing[a]): + print("hello", a, b, c) + + @staticmethod + fn foo(x: Thing[a]): + print("🔥", a, b, c) + + fn main(): + _ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8' + CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' + ``` + +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and + `tofile()` methods to save and load as bytes from a file. + +- The built-in `print()` function now works on the + [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. + +- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and + [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors + that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) + and [`StaticIntTuple`](/mojo/stdlib/utils/index_.html#staticinttuple) to + initialize shapes. + +- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the + `count()` and `find()` methods to enable counting the number of occurrences or + finding the offset index of a substring in a string. + +- The `String` type now has a `replace()` method which allows you to replace a + substring with another string. + +### 🦋 Changed + +- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + moved under builtins, and no longer need to be imported. + +- Variadic arguments are now automatically projected into a `VariadicList` or + `VariadicListMem` inside the function body. This allows for more flexibility + in using var args. For example: + + ```mojo + fn print_ints(*nums: Int): + let len = len(nums) + for i in range(len): + print(nums[i]) + print(len) + ``` + +- The parameters for + [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + have been switched. The parameters are now `[type, size]` instead of + `[size, type]`. The `InlinedFixedVector` now has a default size which means + that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` + and the default size is used. + +- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer.html#buffer) + and [`NDBuffer`](/mojo/stdlib/buffer/buffer.html#ndbuffer) is renamed to + `tofile()` to match the Python naming. + +- Mojo will now utilize all available cores across all NUMA sockets on the host + machine by default. The prior default behavior was to use all the cores on + the first socket. + +### ❌ Removed + +- The `math.numerics` module is now private, because its types (`FPUtils` and + `FlushDenormals`) should not be used externally. + +### 🛠️ Fixed + +- [#532](https://github.com/modularml/mojo/issues/532) - Compiler optimizing + while True loop away +- [#760](https://github.com/modularml/mojo/issues/760) - Compilation error: + 'hlcf.for.yield' op specifies 0 branch inputs but target expected 1 along + control-flow edge from here +- [#849](https://github.com/modularml/mojo/issues/849) - The `Tensor` type is + now initialized with zeros at construction time. +- [#912](https://github.com/modularml/mojo/issues/912) - Invalid load for + `__get_address_as_lvalue`. +- [#916](https://github.com/modularml/mojo/issues/916) - Parser crash when + specifying default values for `inout` arguments. +- [#943](https://github.com/modularml/mojo/issues/943) - Mojo hangs if you + use continue in the nested loop +- [#957](https://github.com/modularml/mojo/issues/957) - Parser crash when a + function call with variadic arguments of a memory-only type is evaluated at + compile time. +- [#990](https://github.com/modularml/mojo/issues/990) - Fixes rounding + issue with floor division with negative numerator. +- [#1018](https://github.com/modularml/mojo/issues/1018) - In some cases the + sort function was returning invalid results. This release fixes some of these + corner cases. +- [#1010](https://github.com/modularml/mojo/issues/1010) - Initializing tensor + in alias declaration results in crash. +- [#1110](https://github.com/modularml/mojo/issues/1110) - The `time.now()` + function now returns nanoseconds across all operating systems. +- [#1115](https://github.com/modularml/mojo/issues/1115) - cannot load + non-register passable type into SSA register. + +## v0.4.0 for Mac (2023-10-19) + +### 🔥 Legendary + +- Mojo for Mac! + + The Mojo SDK now works on macOS (Apple silicon). This is the same version + previously released for Linux. Get the latest version of the SDK for your Mac + system: + + [Download Now!](https://developer.modular.com/download) + +## v0.4.0 (2023-10-05) + +### ⭐️ New + +- Mojo now supports default parameter values. For example: + + ```mojo + fn foo[a: Int = 3, msg: StringLiteral = "woof"](): + print(msg, a) + + fn main(): + foo() # prints 'woof 3' + foo[5]() # prints 'woof 5' + foo[7, "meow"]() # prints 'meow 7' + ``` + + Inferred parameter values take precedence over defaults: + + ```mojo + @value + struct Bar[v: Int]: + pass + + fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]): + print(msg, a) + + fn main(): + foo(Bar[9]()) # prints 'quack 9' + ``` + + Structs also support default parameters: + + ```mojo + @value + struct DefaultParams[msg: StringLiteral = "woof"]: + alias message = msg + + fn main(): + print(DefaultParams[]().message) # prints 'woof' + print(DefaultParams["meow"]().message) # prints 'meow' + ``` + +- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O + support. You can now write: + + ```mojo + var f = open("my_file.txt", "r") + print(f.read()) + f.close() + ``` + + or + + ```mojo + with open("my_file.txt", "r") as f: + print(f.read()) + ``` + +- Mojo now allows context managers to support an `__enter__` method without + implementing support for an `__exit__` method, enabling idioms like this: + + ```mojo + # This context manager consumes itself and returns it as the value. + fn __enter__(owned self) -> Self: + return self^ + ``` + + Here Mojo _cannot_ invoke a noop `__exit__` method because the context + manager is consumed by the `__enter__` method. This can be used for types + (like file descriptors) that are traditionally used with `with` statements, + even though Mojo's guaranteed early destruction doesn't require that. + +- A very basic version of `pathlib` has been implemented in Mojo. The + module will be improved to achieve functional parity with Python in + the next few releases. + +- The `memory.unsafe` module now contains a `bitcast` function. This is a + low-level operation that enables bitcasting between pointers and scalars. + +- The input parameters of a parametric type can now be directly accessed as + attribute references on the type or variables of the type. For example: + + ```mojo + @value + struct Thing[param: Int]: + pass + + fn main(): + print(Thing[2].param) # prints '2' + let x = Thing[9]() + print(x.param) # prints '9' + ``` + + Input parameters on values can even be accessed in parameter contexts. For + example: + + ```mojo + fn foo[value: Int](): + print(value) + + let y = Thing[12]() + alias constant = y.param + 4 + foo[constant]() # prints '16' + ``` + +- The Mojo REPL now supports code completion. Press Tab while typing + to query potential completion results. + +- Error messages from Python are now exposed in Mojo. For example the following + should print `No module named 'my_uninstalled_module'`: + + ```mojo + fn main(): + try: + let my_module = Python.import_module("my_uninstalled_module") + except e: + print(e) + ``` + +- Error messages can now store dynamic messages. For example, the following + should print "Failed on: Hello" + + ```mojo + fn foo(x: String) raises: + raise Error("Failed on: " + x) + + fn main(): + try: + foo("Hello") + except e: + print(e) + ``` + +### 🦋 Changed + +- We have improved and simplified the `parallelize` function. The function + now elides some overhead by caching the Mojo parallel runtime. + +- The Mojo REPL and Jupyter environments no longer implicitly expose `Python`, + `PythonObject`, or `Pointer`. These symbols must now be imported explicitly, + for example: + + ```mojo + from python import Python + from python.object import PythonObject + from memory.unsafe import Pointer + ``` + +- The syntax for specifying attributes with the `__mlir_op` prefix have changed + to mimic Python's keyword argument passing syntax. That is, `=` should be used + instead of `:`, e.g.: + + ```mojo + # Old syntax, now fails. + __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() + # New syntax. + __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() + ``` + +- You can now print the `Error` object directly. The `message()` method + has been removed. + +### 🛠️ Fixed + +- [#794](https://github.com/modularml/mojo/issues/794) - Parser crash when + using the `in` operator. +- [#936](https://github.com/modularml/mojo/issues/936) - The `Int` constructor + now accepts other `Int` instances. +- [#921](https://github.com/modularml/mojo/issues/921) - Better error message + when running `mojo` on a module with no `main` function. +- [#556](https://github.com/modularml/mojo/issues/556) - UInt64s are now + printed correctly. +- [#804](https://github.com/modularml/mojo/issues/804) - Emit error instead of + crashing when passing variadic arguments of unsupported types. +- [#833](https://github.com/modularml/mojo/issues/833) - Parser crash when + assigning module value. +- [#752](https://github.com/modularml/mojo/issues/752) - Parser crash when + calling async def. +- [#711](https://github.com/modularml/mojo/issues/711) - The overload resolution + logic now correctly prioritizes instance methods over static methods (if + candidates are an equally good match otherwise), and no longer crashed if a + static method has a `Self` type as its first argument. +- [#859](https://github.com/modularml/mojo/issues/859) - Fix confusing error and + documentation of the `rebind` builtin. +- [#753](https://github.com/modularml/mojo/issues/753) - Direct use of LLVM + dialect produces strange errors in the compiler. +- [#926](https://github.com/modularml/mojo/issues/926) - Fixes an issue that + occurred when a function with a return type of `StringRef` raised an error. + When the function raised an error, it incorrectly returned the string value of + that error. +- [#536](https://github.com/modularml/mojo/issues/536) - Report More information + on python exception. + +## v0.3.1 (2023-09-28) + +Our first-ever patch release of the Mojo SDK is here! Release v0.3.1 +includes primarily installation-related fixes. If you’ve had trouble +installing the previous versions of the SDK, this release may be for you. + +### 🛠️ Fixed + +- [#538](https://github.com/modularml/mojo/issues/538) - Installation hangs + during the testing phase. This issue occurs on machines with a low number + of CPU cores, such as free AWS EC2 instances and GitHub Codespaces. +- [#590](https://github.com/modularml/mojo/issues/590) - Installation fails + with a “failed to run python” message. +- [#672](https://github.com/modularml/mojo/issues/672) - Language server hangs + on code completion. Related to #538, this occurs on machines with a low + number of CPU cores. +- [#913](https://github.com/modularml/mojo/issues/913) - In the REPL and Jupyter + notebooks, inline comments were being parsed incorrectly. + +## v0.3.0 (2023-09-21) + +There's more Mojo to love in this, the second release of the Mojo SDK! This +release includes new features, an API change, and bug fixes. + +There's also an updated version of the [Mojo extension for VS +Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + +### ⭐️ New + +- Mojo now has partial support for passing keyword arguments to functions and + methods. For example the following should work: + + ```mojo + fn foo(a: Int, b: Int = 3) -> Int: + return a * b + + fn main(): + print(foo(6, b=7)) # prints '42' + print(foo(a=6, b=7)) # prints '42' + print(foo(b=7, a=6)) # prints '42' + ``` + + Parameters can also be inferred from keyword arguments, for example: + + ```mojo + fn bar[A: AnyType, B: AnyType](a: A, b: B): + print("Hello 🔥") + + fn bar[B: AnyType](a: StringLiteral, b: B): + print(a) + + fn main(): + bar(1, 2) # prints `Hello 🔥` + bar(b=2, a="Yay!") # prints `Yay!` + ``` + + For the time being, the following notable limitations apply: + + - Keyword-only arguments are not supported: + + ```mojo + fn baz(*args: Int, b: Int): pass # fails + fn baz(a: Int, *, b: Int): pass # fails + ``` + + (Keyword-only arguments are described in + [PEP 3102](https://peps.python.org/pep-3102/).) + + - Variadic keyword arguments are not supported: + + ```mojo + fn baz(a: Int, **kwargs: Int): pass # fails + ``` + +- Mojo now supports the `@nonmaterializable` decorator. The purpose is to mark + data types that should only exist in the parameter domain. To use it, a + struct is decorated with `@nonmaterializable(TargetType)`. Any time the + nonmaterializable type is converted from the parameter domain, it is + automatically converted to `TargetType`. A nonmaterializable struct should + have all of its methods annotated as `@always_inline`, and must be computable + in the parameter domain. In the following example, the `NmStruct` type can + be added in the parameter domain, but are converted to `HasBool` when + materialized. + + ```mojo + @value + @register_passable("trivial") + struct HasBool: + var x: Bool + fn __init__(x: Bool) -> Self: + return Self {x: x} + @always_inline("nodebug") + fn __init__(nms: NmStruct) -> Self: + return Self {x: True if (nms.x == 77) else False} + + @value + @nonmaterializable(HasBool) + @register_passable("trivial") + struct NmStruct: + var x: Int + @always_inline("nodebug") + fn __add__(self: Self, rhs: Self) -> Self: + return NmStruct(self.x + rhs.x) + + alias stillNmStruct = NmStruct(1) + NmStruct(2) + # When materializing to a run-time variable, it is automatically converted, + # even without a type annotation. + let convertedToHasBool = stillNmStruct + ``` + +- Mojo integer literals now produce the `IntLiteral` infinite precision integer + type when used in the parameter domain. `IntLiteral` is materialized to the + `Int` type for runtime computation, but intermediate computations at compile + time, using supported operators, can now exceed the bit width of the `Int` + type. + +- The Mojo Language Server now supports top-level code completions, enabling + completion when typing a reference to a variable, type, etc. This resolves + [#679](https://github.com/modularml/mojo/issues/679). + +- The Mojo REPL now colorizes the resultant variables to help distinguish input + expressions from the output variables. + +### 🦋 Changed + +- Mojo allows types to implement two forms of move constructors, one that is + invoked when the lifetime of one value ends, and one that is invoked if the + compiler cannot prove that. These were previously both named `__moveinit__`, + with the following two signatures: + + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __moveinit__(inout self, inout existing: Self): ... + ``` + + We've changed the second form to get its own name to make it more clear that + these are two separate operations: the second has been renamed to + `__takeinit__`: + + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __takeinit__(inout self, inout existing: Self): ... + ``` + + The name is intended to connote that the operation takes the conceptual value + from the source (without destroying it) unlike the first one which "moves" a + value from one location to another. + + For more information, see the Mojo Manual section on + [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). + +- The Error type in Mojo has changed. Instead of extracting the error message + using `error.value` you will now extract the error message using + `error.message()`. + +### 🛠️ Fixed + +- [#503](https://github.com/modularml/mojo/issues/503) - Improve error message + for failure lowering `kgen.param.constant`. +- [#554](https://github.com/modularml/mojo/issues/554) - Alias of static tuple + fails to expand. +- [#500](https://github.com/modularml/mojo/issues/500) - Call expansion failed + due to verifier error. +- [#422](https://github.com/modularml/mojo/issues/422) - Incorrect comment + detection in multiline strings. +- [#729](https://github.com/modularml/mojo/issues/740) - Improve messaging on + how to exit the REPL. +- [#756](https://github.com/modularml/mojo/issues/756) - Fix initialization + errors of the VS Code extension. +- [#575](https://github.com/modularml/mojo/issues/575) - Build LLDB/REPL with + libedit for a nicer editing experience in the terminal. + +## v0.2.1 (2023-09-07) + +The first versioned release of Mojo! 🔥 + +All earlier releases were considered version 0.1. + +### 🔥 Legendary + +- First release of the Mojo SDK! + + You can now develop with Mojo locally. The Mojo SDK is currently available + for Ubuntu Linux systems, and support for Windows and macOS is coming soon. + You can still develop from a Windows or Mac computer using a container or + remote Linux system. + + The Mojo SDK includes the Mojo standard library and the [Mojo command-line + interface](/mojo/cli/) (CLI), which allows you to run, compile, and package + Mojo code. It also provides a REPL programming environment. + + [Get the Mojo SDK!](https://developer.modular.com/download) + +- First release of the [Mojo extension for VS + Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + + This provides essential Mojo language features in Visual Studio Code, such as + code completion, code quick fixes, docs tooltips, and more. Even when + developing on a remote system, using VS Code with this extension provides + a native-like IDE experience. + +### ⭐️ New + +- A new `clobber_memory` function has been added to the + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. + The clobber memory function tells the system to flush all memory operations + at the specified program point. This allows you to benchmark operations + without the compiler reordering memory operations. + +- A new `keep` function has been added to the + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` + function tries to tell the compiler not to optimize the variable away + if not used. This allows you to avoid compiler's dead code elimination + mechanism, with a low footprint side effect. + +- New `shift_right` and `shift_left` functions have been added to the + [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in + a SIMD vector right/left, filling elements with zeros as needed. + +- A new `cumsum` function has been added to the + [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes + the cumulative sum (also known as scan) of input elements. + +- Mojo Jupyter kernel now supports code completion. + +### 🦋 Changed + +- Extends `rotate_bits_left`, `rotate_left`, `rotate_bits_right`, and + `rotate_right` to operate on Int values. The ordering of parameters has also + been changed to enable type inference. Now it's possible to write + `rotate_right[shift_val](simd_val)` and have the `dtype` and `simd_width` + inferred from the argument. This addresses + [Issue #528](https://github.com/modularml/mojo/issues/528). + +### 🛠️ Fixed + +- Fixed a bug causing the parser to crash when the `with` statement was written + without a colon. + This addresses [Issue #529](https://github.com/modularml/mojo/issues/529). + +- Incorrect imports no longer crash when there are other errors at the top + level of a module. This fixes [Issue + #531](https://github.com/modularml/mojo/issues/531). + +## August 2023 + +### 2023-08-24 + +- Fixed issue where the `with expr as x` statement within `fn` behaved + as if it were in a `def`, binding `x` with function scope instead of using + lexical scope. + +#### ⭐️ New + +- Major refactoring of the standard library to enable packaging and better + import ergonomics: + - The packages are built as binaries to improve startup speed. + - Package and module names are now lowercase to align with the Python style. + - Modules have been moved to better reflect the purpose of the underlying + functions (e.g. `Pointer` is now within the `unsafe` module in the `memory` + package). + - The following modules are now included as built-ins: + `SIMD`, `DType`, `IO`, `Object`, and `String`. + This means it's no longer necessary to explicitly import these modules. + Instead, these modules will be implicitly imported for the user. Private + methods within the module are still accessible using the + `builtin.module_name._private_method` import syntax. + - New `math` package has been added to contain the `bit`, `math`, `numerics`, + and `polynomial` modules. The contents of the `math.math` module are + re-exported into the `math` package. + +- Mojo now supports using memory-only types in parameter expressions and as + function or type parameters: + + ```mojo + @value + struct IntPair: + var first: Int + var second: Int + + fn add_them[value: IntPair]() -> Int: + return value.first + value.second + + fn main(): + print(add_them[IntPair(1, 2)]()) # prints '3' + ``` + +- In addition, Mojo supports evaluating code that uses heap-allocated memory + at compile-time and materializing compile-time values with heap-allocated + memory into dynamic values: + + ```mojo + fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: + var result = DynamicVector[Int]() + for i in range(lowerBound, upperBound, step): + result.push_back(i) + return result + + fn main(): + alias values = fillVector(5, 23, 7) + for i in range(0, values.__len__()): + print(values[i]) # prints '5', '12', and then '19' + ``` + +#### 🦋 Changed + +- `def main():`, without the explicit `None` type, can now be used to define + the entry point to a Mojo program. + +- The `assert_param` function has been renamed to `constrained` and is now + a built-in function. + +- The `print` function now works on `Complex` values. + +#### 🛠️ Fixed + +- Fixed issues with print formatting for `DType.uint16` and `DType.int16`. +- [Issue #499](https://github.com/modularml/mojo/issues/499) - Two new + `rotate_right` and `rotate_left` functions have been added to the SIMD module. +- [Issue #429](https://github.com/modularml/mojo/issues/429) - You can now + construct a `Bool` from a `SIMD` type whose element-type is `DType.bool`. +- [Issue #350](https://github.com/modularml/mojo/issues/350) - Confusing Matrix + implementation +- [Issue #349](https://github.com/modularml/mojo/issues/349) - Missing load_tr + in struct Matrix +- [Issue #501](https://github.com/modularml/mojo/issues/501) - Missing syntax + error messages in Python expressions. + +### 2023-08-09 + +#### 🦋 Changed + +- The `ref` and `mutref` identifiers are now treated as keywords, which means + they cannot be used as variable, attribute, or function names. These keywords + are used by the "lifetimes" features, which is still in development. We can + consider renaming these (as well as other related keywords) when the + development work gels, support is enabled in public Mojo builds, and when we + have experience using them. + +- The argument handling in `def` functions has changed: previously, they had + special behavior that involved mutable copies in the callee. Now, we have a + simple rule, which is that `def` argument default to the `owned` convention + (`fn` arguments still default to the `borrowed` convention). + + This change is mostly an internal cleanup and simplification of the compiler + and argument model, but does enable one niche use-case: you can now pass + non-copyable types to `def` arguments by transferring ownership of a value + into the `def` call. Before, that would not be possible because the copy was + made on the callee side, not the caller's side. This also allows the explicit + use of the `borrowed` keyword with a `def` that wants to opt-in to that + behavior. + +### 2023-08-03 + +#### ⭐️ New + +- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. + This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which + are just views). Therefore, the tensor type performs its own allocation and + free. Here is a simple example of using the tensor type to represent an RGB + image and convert it to grayscale: + + ```mojo + from tensor import Tensor, TensorShape + from utils.index import Index + from random import rand + + let height = 256 + let width = 256 + let channels = 3 + + # Create the tensor of dimensions height, width, channels and fill with + # random value. + let image = rand[DType.float32](height, width, channels) + + # Declare the grayscale image. + var gray_scale_image = Tensor[DType.float32](height, width) + + # Perform the RGB to grayscale transform. + for y in range(height): + for x in range(width): + let r = image[y, x, 0] + let g = image[y, x, 1] + let b = image[y, x, 2] + gray_scale_image[Index(y, x)] = 0.299 * r + 0.587 * g + 0.114 * b + ``` + +#### 🛠️ Fixed + +- [Issue #53](https://github.com/modularml/mojo/issues/53) - `Int` now + implements true division with the `/` operator. Similar to Python, this + returns a 64-bit floating point number. The corresponding in-place operator, + `/=`, has the same semantics as `//=`. + +## July 2023 + +### 2023-07-26 + +#### ⭐️ New + +- Types that define both `__getitem__` and `__setitem__` (i.e. where + sub-scripting instances creates computed LValues) can now be indexed + in parameter expressions. + +- Unroll decorator for loops with constant bounds and steps: + - `@unroll`: Fully unroll a loop. + - `@unroll(n)`: Unroll a loop by factor of n, where `n` is a positive integer. + - Unroll decorator requires loop bounds and iteration step to be + compiler time constant value, otherwise unrolling will fail with + compilation error. This also doesn't make loop induction variable a parameter. + + ```mojo + # Fully unroll the loop. + @unroll + for i in range(5): + print(i) + + # Unroll the loop by a factor of 4 (with remainder iterations of 2). + @unroll(4) + for i in range(10): + print(i) + ``` + +- The Mojo REPL now prints the values of variables defined in the REPL. There is + full support for scalars and structs. Non-scalar SIMD vectors are not + supported at this time. + +#### 🛠️ Fixed + +- [Issue #437](https://github.com/modularml/mojo/issues/437) - Range can now + be instantiated with a PythonObject. + +- [Issue #288](https://github.com/modularml/mojo/issues/288) - Python strings + can now be safely copied. + +### 2023-07-20 + +#### ⭐️ New + +- Mojo now includes a `Limits` module, which contains functions to get the max + and min values representable by a type, as requested in [Issue + #51](https://github.com/modularml/mojo/issues/51). The following functions + moved from `Math` to `Limits`: `inf()`, `neginf()`, `isinf()`, `isfinite()`. + +- Mojo decorators are now distinguished between "signature" and "body" + decorators and are ordered. Signature decorators, like `@register_passable` + and `@parameter`, modify the type of declaration before the body is parsed. + Body decorators, like `@value`, modify the body of declaration after it is + fully parsed. Due to ordering, a signature decorator cannot be applied after + a body decorator. That means the following is now invalid: + + ```mojo + @register_passable # error: cannot apply signature decorator after a body one! + @value + struct Foo: + pass + ``` + +- Global variables can now be exported in Mojo compiled archives, using the + `@export` decorator. Exported global variables are public symbols in compiled + archives and use the variable name as its linkage name, by default. A custom + linkage name can be specified with `@export("new_name")`. This does not affect + variable names in Mojo code. + +- Mojo now supports packages! A Mojo package is defined by placing an + `__init__.mojo` or `__init__.🔥` within a directory. Other files in the same + directory form modules within the package (this works exactly like it + does [in Python](https://docs.python.org/3/tutorial/modules.html#packages)). + Example: + + ```bash + main.🔥 + my_package/ + __init__.🔥 + module.🔥 + my_other_package/ + __init__.🔥 + stuff.🔥 + ``` + + ```mojo + # main.🔥 + from my_package.module import some_function + from my_package.my_other_package.stuff import SomeType + + fn main(): + var x: SomeType = some_function() + ``` + +- Mojo now supports direct module and package imports! Modules and packages can + be imported and bound to names. Module and package elements, like functions, + types, global variables, and other modules, can be accessed using attribute + references, like `my_module.foo`. Note that modules lack runtime + representations, meaning module references cannot be instantiated. + + ```mojo + import builtin.io as io + import SIMD + + io.print("hello world") + var x: SIMD.Float32 = 1.2 + ``` + +#### 🦋 Changed + +- Reverted the feature from 2023-02-13 that allowed unqualified struct members. + Use the `Self` keyword to conveniently access struct members with bound + parameters instead. This was required to fix + [Issue #260](https://github.com/modularml/mojo/issues/260). + +- Updated the RayTracing notebook: added step 5 to create specular lighting for + more realistic images and step 6 to add a background image. + +#### 🛠️ Fixed + +- [Issue #260](https://github.com/modularml/mojo/issues/260) - Definitions + inside structs no longer shadow definitions outside of struct definitions. + +### 2023-07-12 + +#### ⭐️ New + +- Mojo now has support for global variables! This enables `var` and `let` + declaration at the top-level scope in Mojo files. Global variable initializers + are run when code modules are loaded by the platform according to the order of + dependencies between global variables, and their destructors are called in the + reverse order. + +- The [Mojo programming manual](/mojo/programming-manual.html) is now written + as a Jupyter notebook, and available in its entirety in the Mojo Playground + (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of + the same material, but it was not up-to-date.) + +- As a result, we've also re-written `HelloMojo.ipynb` to be much shorter and + provide a more gentle first-user experience. + +- [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now + available. Coroutines form the basis of Mojo's support for asynchronous + execution. Calls to `async fn`s can be stored into a `Coroutine`, from which + they can be resumed, awaited upon, and have their results retrieved upon + completion. + +#### 🦋 Changed + +- `simd_bit_width` in the `TargetInfo` module has been renamed to `simdbitwidth` + to better align with `simdwidthof`, `bitwidthof`, etc. + +#### 🛠️ Fixed + +- The walrus operator now works in if/while statements without parentheses, + e.g. `if x := function():`. + +- [Issue #428](https://github.com/modularml/mojo/issues/428) - The + `FloatLiteral` and `SIMD` types now support conversion to `Int` via the + `to_int` or `__int__` method calls. The behavior matches that of Python, which + rounds towards zero. + +### 2023-07-05 + +#### ⭐️ New + +- Tuple expressions now work without parentheses. For example, `a, b = b, a` + works as you'd expect in Python. +- Chained assignments (e.g. `a = b = 42`) and the walrus operator (e.g. + `some_function(b := 17)`) are now supported. + +#### 🦋 Changed + +- The `simd_width` and `dtype_simd_width` functions in the + [`TargetInfo`](/mojo/stdlib/sys/info) module + have been renamed to `simdwidthof`. + +- The `dtype_` prefix has been dropped from `alignof`, `sizeof`, and + `bitwidthof`. You can now use these functions (e.g. `alignof`) with any + argument type, including `DType`. + +- The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were + moved from the `Numerics` module to the [`Math`](/mojo/MojoStdlib/Math.html) + module, to better align with Python's library structure. + +#### 🛠️ Fixed + +- [Issue #253](https://github.com/modularml/mojo/issues/253) - Issue + when accessing a struct member alias without providing parameters. + +- [Issue #404](https://github.com/modularml/mojo/issues/404) - The docs now use + `snake_case` for variable names, which more closely conforms to Python's + style. + +- [Issue #379](https://github.com/modularml/mojo/issues/379) - Tuple + limitations have been addressed and multiple return values are now supported, + even without parentheses. + +- [Issue #347](https://github.com/modularml/mojo/issues/347) - Tuples no longer + require parentheses. + +- [Issue #320](https://github.com/modularml/mojo/issues/320) - Python objects + are now traversable via `for` loops. + +## June 2023 + +### 2023-06-29 + +#### ⭐️ New + +- You can now share `.ipynb` notebook files in Mojo Playground. Just save a + file in the `shared` directory, and then right-click the file and select + **Copy Sharable link**. To open a shared notebook, you must already have + [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); + when you open a shared notebook, click **Import** at the top of the notebook + to save your own copy. For more details about this feature, see the + instructions inside the `help` directory, in the Mojo Playground file browser. + +#### 🦋 Changed + +- The `unroll2()` and `unroll3()` functions in the + [`Functional`](/mojo/stdlib/algorithm/functional) module have been renamed to + overload the `unroll()` function. These functions unroll 2D and 3D loops and + `unroll()` can determine the intent based on the number of input parameters. + +#### 🛠️ Fixed + +- [Issue #229](https://github.com/modularml/mojo/issues/229) - Issue when + throwing an exception from `__init__` before all fields are initialized. + +- [Issue #74](https://github.com/modularml/mojo/issues/74) - Struct + definition with recursive reference crashes. + +- [Issue #285](https://github.com/modularml/mojo/issues/285) - The + [`TargetInfo`](/mojo/stdlib/sys/info) module now includes + `is_little_endian()` and `is_big_endian()` to check if the target host uses + either little or big endian. + +- [Issue #254](https://github.com/modularml/mojo/issues/254) - Parameter name + shadowing in nested scopes is now handled correctly. + +### 2023-06-21 + +#### ⭐️ New + +- Added support for overloading on parameter signature. For example, it is now +possible to write the following: + + ```mojo + fn foo[a: Int](x: Int): + pass + + fn foo[a: Int, b: Int](x: Int): + pass + ``` + + For details on the overload resolution logic, see the Mojo Manual section on + [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). + +- A new `cost_of()` function has been added to `Autotune`. This meta-function + must be invoked at compile time, and it returns the number of MLIR operations + in a function (at a certain stage in compilation), which can be used to + build basic heuristics in higher-order generators. + + ```mojo + from autotune import cost_of + + fn generator[f: fn(Int) -> Int]() -> Int: + @parameter + if cost_of[fn(Int) -> Int, f]() < 10: + return f() + else: + # Do something else for slower functions... + ``` + +- Added a new example notebook with a basic Ray Tracing algorithm. + +#### 🦋 Changed + +- The `constrained_msg()` in the `Assert` module has been renamed to + `constrained()`. + +#### 🛠️ Fixed + +- Overloads marked with `@adaptive` now correctly handle signatures that differ +only in declared parameter names, e.g. the following now works correctly: + + ```mojo + @adaptive + fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... + + @adaptive + fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ... + ``` + +- [Issue #219](https://github.com/modularml/mojo/issues/219) - Issue when + redefining a function and a struct defined in the same cell. + +- [Issue #355](https://github.com/modularml/mojo/issues/355) - The loop order + in the Matmul notebook for Python and naive mojo have been reordered for + consistency. The loop order now follows (M, K, N) ordering. + +- [Issue #309](https://github.com/modularml/mojo/issues/309) - Use snake case + naming within the testing package and move the asserts out of the TestSuite + struct. + +### 2023-06-14 + +#### ⭐️ New + +- Tuple type syntax is now supported, e.g. the following works: + + ```mojo + fn return_tuple() -> (Int, Int): + return (1, 2) + ``` + +#### 🦋 Changed + +- The `TupleLiteral` type was renamed to just `Tuple`, e.g. + `Tuple[Int, Float]`. + +#### 🛠️ Fixed + +- [Issue #354](https://github.com/modularml/mojo/issues/354) - Returning a tuple + doesn't work even with parens. +- [Issue #365](https://github.com/modularml/mojo/issues/365) - Copy-paste error + in `FloatLiteral` docs. +- [Issue #357](https://github.com/modularml/mojo/issues/357) - Crash when + missing input parameter to variadic parameter struct member function. + +### 2023-06-07 + +#### ⭐️ New + +- Tuple syntax now works on the left-hand side of assignments (in "lvalue" + positions), enabling things like `(a, b) = (b, a)`. There are several + caveats: the element types must exactly match (no implicit conversions), + this only works with values of `TupleLiteral` type (notably, it will not work + with `PythonObject` yet) and parentheses are required for tuple syntax. + +#### ❌ Removed + +- Mojo Playground no longer includes the following Python packages (due to size, + compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): + `torch`, `tensorflow`, `keras`, `transformers`. + +#### 🦋 Changed + +- The data types and scalar names now conform to the naming convention used + by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` + instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). + +#### 🛠️ Fixed + +- [Issue #287](https://github.com/modularml/mojo/issues/287) - computed + lvalues don't handle raising functions correctly +- [Issue #318](https://github.com/modularml/mojo/issues/318) - Large integers + are not being printed correctly +- [Issue #326](https://github.com/modularml/mojo/issues/326) - Float modulo + operator is not working as expected +- [Issue #282](https://github.com/modularml/mojo/issues/282) - Default arguments + are not working as expected +- [Issue #271](https://github.com/modularml/mojo/issues/271) - Confusing error + message when converting between function types with different result semantics + +## May 2023 + +### 2023-05-31 + +#### ⭐️ New + +- Mojo Playground now includes the following Python packages (in response to + [popular demand](https://github.com/modularml/mojo/discussions/173)): + `torch`, `tensorflow`, `polars`, `opencv-python`, `keras`, `Pillow`, `plotly`, + `seaborn`, `sympy`, `transformers`. + +- A new optimization is applied to non-trivial copyable values that are passed + as an owned value without using the transfer (`^`) operator. Consider code + like this: + + ```mojo + var someValue: T = ... + ... + takeValueAsOwned(someValue) + ... + ``` + + When `takeValueAsOwned()` takes its argument as an + [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) + value (this is + common in initializers for example), it is allowed to do whatever it wants + with the value and destroy it when it is finished. In order to support this, + the Mojo compiler is forced to make a temporary copy of the `someValue` + value, and pass that value instead of `someValue`, because there may be other + uses of `someValue` after the call. + + The Mojo compiler is now smart enough to detect when there are no uses of + `someValue` later, and it will elide the copy just as if you had manually + specified the transfer operator like `takeValueAsOwned(someValue^)`. This + provides a nice "it just works" behavior for non-trivial types without + requiring manual management of transfers. + + If you'd like to take full control and expose full ownership for your type, + just don't make it copyable. Move-only types require the explicit transfer + operator so you can see in your code where all ownership transfer happen. + +- Similarly, the Mojo compiler now transforms calls to `__copyinit__` methods + into calls to `__moveinit__` when that is the last use of the source value + along a control flow path. This allows types which are both copyable and + movable to get transparent move optimization. For example, the following code + is compiled into moves instead of copies even without the use of the transfer + operator: + + ```mojo + var someValue = somethingCopyableAndMovable() + use(someValue) + ... + let otherValue = someValue # Last use of someValue + use(otherValue) + ... + var yetAnother = otherValue # Last use of otherValue + mutate(yetAnother) + ``` + + This is a significant performance optimization for things like `PythonObject` + (and more complex value semantic types) that are commonly used in a fluid + programming style. These don't want extraneous reference counting operations + performed by its copy constructor. + + If you want explicit control over copying, it is recommended to use a + non-dunder `.copy()` method instead of `__copyinit__`, and recall that + non-copyable types must always use of the transfer operator for those that + want fully explicit behavior. + +#### 🛠️ Fixed + +- [Issue #231](https://github.com/modularml/mojo/issues/231) - Unexpected error + when a Python expression raises an exception +- [Issue #119](https://github.com/modularml/mojo/issues/119) - The REPL fails + when a python variable is redefined + +### 2023-05-24 + +#### ⭐️ New + +- `finally` clauses are now supported on `try` statements. In addition, `try` + statements no longer require `except` clauses, allowing `try-finally` blocks. + `finally` clauses contain code that is always executed from control-flow + leaves any of the other clauses of a `try` statement by any means. + +#### 🦋 Changed + +- `with` statement emission changed to use the new `finally` logic so that + + ```mojo + with ContextMgr(): + return + ``` + + Will correctly execute `ContextMgr.__exit__` before returning. + +#### 🛠️ Fixed + +- [Issue #204](https://github.com/modularml/mojo/issues/204) - Mojo REPL + crash when returning a String at compile-time +- [Issue #143](https://github.com/modularml/mojo/issues/143) - synthesized + init in `@register_passable` type doesn't get correct convention. +- [Issue #201](https://github.com/modularml/mojo/issues/201) - String literal + concatenation is too eager. +- [Issue #209](https://github.com/modularml/mojo/issues/209) - [QoI] Terrible + error message trying to convert a type to itself. +- [Issue #32](https://github.com/modularml/mojo/issues/32) - Include struct + fields in docgen +- [Issue #50](https://github.com/modularml/mojo/issues/50) - Int to string + conversion crashes due to buffer overflow +- [Issue #132](https://github.com/modularml/mojo/issues/132) - PythonObject + `to_int` method has a misleading name +- [Issue #189](https://github.com/modularml/mojo/issues/189) - PythonObject bool + conversion is incorrect +- [Issue #65](https://github.com/modularml/mojo/issues/65) - Add SIMD + constructor from Bool +- [Issue #153](https://github.com/modularml/mojo/issues/153) - Meaning of + `Time.now` function result is unclear +- [Issue #165](https://github.com/modularml/mojo/issues/165) - Type in + `Pointer.free` documentation +- [Issue #210](https://github.com/modularml/mojo/issues/210) - Parameter results + cannot be declared outside top-level in function +- [Issue #214](https://github.com/modularml/mojo/issues/214) - Pointer offset + calculations at compile-time are incorrect +- [Issue #115](https://github.com/modularml/mojo/issues/115) - Float printing + does not include the right number of digits +- [Issue #202](https://github.com/modularml/mojo/issues/202) - + `kgen.unreachable` inside nested functions is illegal +- [Issue #235](https://github.com/modularml/mojo/issues/235) - Crash when + register passable struct field is not register passable +- [Issue #237](https://github.com/modularml/mojo/issues/237) - Parameter + closure sharp edges are not documented + +### 2023-05-16 + +#### ⭐️ New + +- Added missing dunder methods to `PythonObject`, enabling the use of common + arithmetic and logical operators on imported Python values. + +- `PythonObject` is now printable from Mojo, instead of requiring you to import + Python's print function. + +#### 🛠️ Fixed + +- [Issue #98](https://github.com/modularml/mojo/issues/98): + Incorrect error with lifetime tracking in loop. + +- [Issue #49](https://github.com/modularml/mojo/issues/49): Type inference + issue (?) in 'ternary assignment' operation (FloatLiteral vs. 'SIMD[f32, 1]'). + +- [Issue #48](https://github.com/modularml/mojo/issues/48): + and/or don't work with memory-only types. + +- [Issue #11](https://github.com/modularml/mojo/issues/11): `setitem` Support + for `PythonObject`. + +### 2023-05-11 + +#### ⭐️ New + +- `NDBuffer` and `Buffer` are now constructable via `Pointer` and + `DTypePointer`. + +- `String` now supports indexing with either integers or slices. + +- Added factorial function to the `Math` module. + +#### 🦋 Changed + +- The "byref" syntax with the `&` sigil has changed to use an `inout` + keyword to be more similar to the `borrowed` and `owned` syntax in arguments. + Please see [Issue #7](https://github.com/modularml/mojo/issues/7) for more + information. + +- Optimized the Matrix multiplication implementation in the notebook. + Initially we were optimizing for expandability rather than performance. We + have found a way to get the best of both worlds and now the performance of the + optimized Matmul implementation is 3x faster. + +- Renamed the [`^` postfix +operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) +from "consume" to "transfer." + +#### 🛠️ Fixed + +- Fixed missing overloads for `Testing.assertEqual` so that they work on +`Integer` and `String` values. + +- [Issue #6](https://github.com/modularml/mojo/issues/6): +Playground stops evaluating cells when a simple generic is defined. + +- [Issue #18](https://github.com/modularml/mojo/issues/18): +Memory leak in Python interoperability was removed. + +### 2023-05-02 + +#### 📢 Released + +- Mojo publicly launched! This was epic, with lots of great coverage online +including a [wonderful post by Jeremy +Howard](https://www.fast.ai/posts/2023-05-03-mojo-launch.html). The team is +busy this week. + +#### ⭐️ New + +- Added a Base64 encoding function to perform base64 encoding on strings. + +#### 🦋 Changed + +- Decreased memory usage of serialization of integers to strings. + +- Speedup the sort function. + +#### 🛠️ Fixed + +- Fixed time unit in the `sleep` function. + +## April 2023 + +### Week of 2023-04-24 + +- 📢 The default behavior of nested functions has been changed. Mojo nested + functions that capture are by default are non-parametric, runtime closures, + meaning that: + + ```mojo + def foo(x): + # This: + def bar(y): return x * y + # Is the same as: + let bar = lambda y: x * y + ``` + + These closures cannot have input or result parameters, because they are always + materialized as runtime values. Values captured in the closure (`x` in the + above example), are captured by copy: values with copy constructors cannot be + copied and captures are immutable in the closure. + + Nested functions that don't capture anything are by default "parametric" + closures: they can have parameters and they can be used as parameter values. + To restore the previous behavior for capturing closures, "parametric, + capture-by-unsafe-reference closures", tag the nested function with the + `@parameter` decorator. + +- 📢 Mojo now has full support for "runtime" closures: nested functions that + capture state materialized as runtime values. This includes taking the address + of functions, indirect calls, and passing closures around through function + arguments. Note that capture-by-reference is still unsafe! + + You can also take references to member functions with instances of that class + using `foo.member_function`, which creates a closure with `foo` bound to the + `self` argument. + +- 📢 Mojo now supports Python style `with` statements and context managers. + + These things are very helpful for implementing things like our + trace region support and things like Runtime support. + + A context manager in Mojo implements three methods: + + ```mojo + fn __enter__(self) -> T: + fn __exit__(self): + fn __exit__(self, err: Error) -> Bool: + ``` + + The first is invoked when the context is entered, and returns a + value that may optionally be bound to a target for use in the with + body. If the with block exits normally, the second method is + invoked to clean it up. If an error is raised, the third method + is invoked with the Error value. If that method returns true, the + error is considered handled, if it returns false, the error is + re-thrown so propagation continues out of the 'with' block. + +- 📢 Mojo functions now support variable scopes! Explicit `var` and `let` + declarations inside functions can shadow declarations from higher "scopes", + where a scope is defined as any new indentation block. In addition, the + `for` loop iteration variable is now scoped to the loop body, so it is + finally possible to write + + ```mojo + for i in range(1): pass + for i in range(2): pass + ``` + +- 📢 Mojo now supports an `@value` decorator on structs to reduce boilerplate + and encourage best practices in value semantics. The `@value` decorator looks + to see the struct has a memberwise initializer (which has arguments for each + field of the struct), a `__copyinit__` method, and a `__moveinit__` method, + and synthesizes the missing ones if possible. For example, if you write: + + ```mojo + @value + struct MyPet: + var name: String + var age: Int + ``` + + The `@value` decorator will synthesize the following members for you: + + ```mojo + fn __init__(inout self, owned name: String, age: Int): + self.name = name^ + self.age = age + fn __copyinit__(inout self, existing: Self): + self.name = existing.name + self.age = existing.age + fn __moveinit__(inout self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age + ``` + + This decorator can greatly reduce the boilerplate needed to define common + aggregates, and gives you best practices in ownership management + automatically. The `@value` decorator can be used with types that need custom + copy constructors (your definition wins). We can explore having the decorator + take arguments to further customize its behavior in the future. + +- 📚 Memcpy and memcmp now consistently use count as the byte count. + +- 📚 Add a variadic sting join on strings. + +- 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all + elements in a SIMD vector. + +- 📚 Optimize the `pow` function if the exponent is integral. + +- 📚 Add a `len` function which dispatches to `__len__` across the different + structs that support it. + +### Week of 2023-04-17 + +- 📢 Error messages have been significantly improved, thanks to prettier + printing for Mojo types in diagnostics. + +- 📢 Variadic values can now be indexed directly without wrapping them in a + `VariadicList`! + +- 📢 `let` declarations in a function can now be lazily initialized, and `var` + declarations that are never mutated get a warning suggesting they be converted + to a `let` declaration. Lazy initialization allows more flexible patterns of + initialization than requiring the initializer be inline, e.g.: + + ```mojo + let x: Int + if cond: + x = foo() + else: + x = bar() + use(x) + ``` + +- 📢 Functions defined with `def` now return `object` by default, instead of + `None`. This means you can return values (convertible to `object`) inside + `def` functions without specifying a return type. + +- 📢 The `@raises` decorator has been removed. Raising `fn` should be declared + by specifying `raises` after the function argument list. The rationale is that + `raises` is part of the type system, instead of a function modifier. + +- 📢 The `BoolLiteral` type has been removed. Mojo now emits `True` and `False` + directly as `Bool`. + +- 📢 Syntax for function types has been added. You can now write function types + with `fn(Int) -> String` or `async def(&String, *Int) -> None`. No more + writing `!kgen.signature` types by hand! + +- 📢 Float literals are not emitted as `FloatLiteral` instead of an MLIR `f64` + type! + +- 📢 Automatic destructors are now supported by Mojo types, currently spelled + `fn __del___(owned self):` (the extra underscore will be dropped shortly). + These destructors work like Python object destructors and similar to C++ + destructors, with the major difference being that they run "as soon as + possible" after the last use of a value. This means they are not suitable + for use in C++-style RAII patterns (use the `with` statement for that, which + is currently unsupported). + + These should be generally reliable for both memory-only and register-passable + types, with the caveat that closures are known to _not_ capture values + correctly. Be very careful with interesting types in the vicinity of a + closure! + +- A new (extremely dangerous!) builtin function is available for low-level + ownership muckery. The `__get_address_as_owned_value(x)` builtin takes a + low-level address value (of `!kgen.pointer` type) and returns an `owned` value + for the memory that is pointed to. This value is assumed live at the + invocation of the builtin, but is "owned" so it needs to be consumed by the + caller, otherwise it will be automatically destroyed. This is an effective + way to do a "placement delete" on a pointer. + + ```mojo + # "Placement delete": destroy the initialized object begin pointed to. + _ = __get_address_as_owned_value(somePointer.value) + + # Result value can be consumed by anything that takes it as an 'owned' + # argument as well. + consume(__get_address_as_owned_value(somePointer.value)) + ``` + +- Another magic operator, named `__get_address_as_uninit_lvalue(x)` joins + the magic LValue operator family. This operator projects a pointer to + an LValue like `__get_address_as_lvalue(x)`. The difference is that + `__get_address_as_uninit_lvalue(x)` tells the compiler that the pointee is + uninitialized on entry and initialized on exit, which means that you can use + it as a "placement new" in C++ sense. `__get_address_as_lvalue(x)` tells the + compiler that the pointee is initialized already, so reassigning over it will + run the destructor. + + ```mojo + # "*Re*placement new": destroy the existing SomeHeavy value in the memory, + # then initialize a new value into the slot. + __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) + + # Ok to use an lvalue, convert to borrow etc. + use(__get_address_as_lvalue(somePointer.value)) + + # "Placement new": Initialize a new value into uninitialied memory. + __get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5) + + # Error, cannot read from uninitialized memory. + use(__get_address_as_uninit_lvalue(somePointer.value)) + ``` + + Note that `__get_address_as_lvalue` assumes that there is already a value at + the specified address, so the assignment above will run the `SomeHeavy` + destructor (if any) before reassigning over the value. + +- 📢 Implement full support for `__moveinit__` (aka move constructors) + + This implements the ability for memory-only types to define two different + types of move ctors if they'd like: + + 1. `fn __moveinit__(inout self, owned existing: Self)`: Traditional Rust + style moving constructors that shuffles data around while taking + ownership of the source binding. + 2. `fn __moveinit__(inout self, inout existing: Self):`: C++ style "stealing" + move constructors that can be used to take from an arbitrary LValue. + + This gives us great expressive capability (better than Rust/C++/Swift) + and composes naturally into our lifetime tracking and value + categorization system. + +- The `__call__` method of a callable type has been relaxed to take `self` by + borrow, allow non-copyable callees to be called. + +- Implicit conversions are now invoked in `raise` statements properly, allowing + converting strings to `Error` type. + +- Automatic destructors are turned on for `__del__` instead of `__del___`. + +- 📚 Add the builtin FloatLiteral type. + +- 📚 Add integral `floordiv` and `mod` for the SIMD type that handle negative + values. + +- 📚 Add an F64 to String converter. + +- 📚 Make the `print` function take variadic inputs. + +### Week of 2023-04-10 + +- 📢 Introduce consume operator `x^` + + This introduces the postfix consume operator, which produces an RValue given + a lifetime tracked object (and, someday, a movable LValue). + +- Mojo now automatically synthesizes empty destructor methods for certain types + when needed. + +- The `object` type has been built out into a fully-dynamic type, with dynamic + function dispatch, with full error handling support. + + ```mojo + def foo(a) -> object: + return (a + 3.45) < [1, 2, 3] # raises a TypeError + ``` + +- 📢 The `@always_inline` decorator is no longer required for passing capturing + closures as parameters, for both the functions themselves as functions with + capturing closures in their parameters. These functions are still inlined but + it is an implementation detail of capturing parameter closures. Mojo now + distinguishes between capturing and non-capturing closures. Nested functions + are capturing by default and can be made non-capturing with the + `@noncapturing` decorator. A top-level function can be passed as a capturing + closure by marking it with the `@closure` decorator. + +- 📢 Support for list literals has been added. List literals `[1, 2, 3]` + generate a variadic heterogeneous list type. + +- Variadics have been extended to work with memory-primary types. + +- Slice syntax is now fully-supported with a new builtin `slice` object, added + to the compiler builtins. Slice indexing with `a[1:2:3]` now emits calls to + `__setitem__` and `__getitem__` with a slice object. + +- Call syntax has been wired up to `__call__`. You can now `f()` on custom + types! + +- Closures are now explicitly typed as capturing or non-capturing. If a + function intends to accept a capturing closure, it must specify the + `capturing` function effect. + +- 📚 Add a `Tile2D` function to enable generic `2D` tiling optimizations. + +- 📚 Add the `slice` struct to enable getting/setting spans of elements via + `getitem`/`setitem`. + +- 📚 Add syntax sugar to autotuning for both specifying the autotuned values, + searching, and declaring the evaluation function. + +### Week of 2023-04-03 + +- The `AnyType` and `NoneType` aliases were added and auto-imported in all + files. + +- 📢 The Mojo VS Code extension has been improved with docstring validation. It + will now warn when a function's docstring has a wrong argument name, for + example. + +- 📢 A new built-in literal type `TupleLiteral` was added in `_CompilerBuiltin`. + It represents literal tuple values such as `(1, 2.0)` or `()`. + +- 📢 The `Int` type has been moved to a new `Builtin` module and is + auto-imported in all code. The type of integer literals has been changed from + the MLIR `index` type to the `Int` type. + +- Mojo now has a powerful flow-sensitive uninitialized variable checker. This + means that you need to initialize values before using them, even if you + overwrite all subcomponents. This enables the compiler to reason about the + true lifetime of values, which is an important stepping stone to getting + automatic value destruction in place. + +- 📢 Call syntax support has been added. Now you can directly call an object + that implements the `__call__` method, like `foo(5)`. + +- 📢 The name for copy constructors got renamed from `__copy__` to + `__copyinit__`. Furthermore, non-`@register_passable` types now implement + it like they do an init method where you fill in a by-reference self, for + example: + + ```mojo + fn __copyinit__(inout self, existing: Self): + self.first = existing.first + self.second = existing.second + ``` + + This makes copy construction work more similarly to initialization, and + still keeps copies `x = y` distinct from initialization `x = T(y)`. + +- 📢 Initializers for memory-primary types are now required to be in the form + `__init__(inout self, ...):` with a None result type, but for register primary + types, it remains in the form `__init__(...) -> Self:`. The `T{}` initializer + syntax has been removed for memory-primary types. + +- Mojo String literals now emit a builtin `StringLiteral` type! One less MLIR + type to worry about. + +- New `__getattr__` and `__setattr__` dunder methods were added. Mojo calls + these methods on a type when attempting member lookup of a non-static member. + This allows writing dynamic objects like `x.foo()` where `foo` is not a member + of `x`. + +- Early destructor support has been added. Types can now define a special + destructor method `__del___` (note three underscores). This is an early + feature and it is still being built out. There are many caveats, bugs, + and missing pieces. Stay tuned! + +- 📚 Integer division and mod have been corrected for rounding in the presence + of negative numbers. + +- 📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to + `SIMD[1, type]`. + +## March 2023 + +### Week of 2023-03-27 + +- 📢 Parameter names are no longer load-bearing in function signatures. This + gives more flexibility in defining higher-order functions, because the + functions passed as parameters do not need their parameter names to match. + + ```mojo + # Define a higher-order function... + fn generator[ + func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] + ](): + pass + + # Int parameter is named "foo". + fn f0[foo: Int](): + pass + + # Int parameter is named "bar". + fn f1[bar: Int](): + pass + + fn main(): + # Both can be used as `func`! + generator[f0]() + generator[f1]() + ``` + + Stay tuned for improved function type syntax... + +- 📢 Two magic operators, named `__get_lvalue_as_address(x)` and + `__get_address_as_lvalue` convert stored LValues to and from `!kgen.pointer` + types (respectively). This is most useful when using the `Pointer[T]` + library type. The `Pointer.address_of(lvalue)` method uses the first one + internally. The second one must currently be used explicitly, and can be + used to project a pointer to a reference that you can pass around and use + as a self value, for example: + + ```mojo + # "Replacement new" SomeHeavy value into the memory pointed to by a + # Pointer[SomeHeavy]. + __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) + ``` + + Note that `__get_address_as_lvalue` assumes that there is already a value at + the specified address, so the assignment above will run the `SomeHeavy` + destructor (if any) before reassigning over the value. + +- The `(((x)))` syntax is __mlir_op has been removed in favor of + `__get_lvalue_as_address` which solves the same problem and is more general. + +- 📢 When using a mutable `self` argument to a struct `__init__` method, it + now must be declared with `&`, like any other mutable method. This clarifies + the mutation model by making `__init__` consistent with other mutating + methods. + +- 📚 Add variadic string join function. + +- 📚 Default initialize values with 0 or null if possible. + +- 📚 Add compressed, aligned, and mask store intrinsics. + +### Week of 2023-03-20 + +- Initial `String` type is added to the standard library with some very basic + methods. + +- Add `DimList` to remove the need to use an MLIR list type throughout the + standard library. + +- 📢 The `__clone__` method for copying a value is now named `__copy__` to + better follow Python term of art. + +- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, + instead of taking it by reference. This makes it easier to write, works for + `@register_passable` types, and exposes more optimization opportunities to + the early optimizer and dataflow analysis passes. + + ```mojo + # Before: + fn __clone__(inout self) -> Self: ... + + # After: + fn __copy__(self) -> Self: ... + ``` + +- 📢 A new `@register_passable("trivial")` may be applied to structs that + have no need for a custom `__copy__` or `__del__` method, and whose state is + only made up of `@register_passable("trivial")` types. This eliminates the + need to define `__copy__` boilerplate and reduces the amount of IR generated + by the compiler for trivial types like `Int`. + +- You can now write back to attributes of structs that are produced by a + computed lvalue expression. For example `a[i].x = ..` works when `a[i]` + is produced with a `__getitem__`/`__setitem__` call. This is implemented by + performing a read of `a[i]`, updating the temporary, then doing a writeback. + +- The remaining hurdles to using non-parametric, `@register_passable` types as + parameter values have been cleared. Types like `Int` should enjoy full use as + parameter values. + +- Parameter pack inference has been added to function calls. Calls to functions + with parameter packs can now elide the pack types: + + ```mojo + fn foo[*Ts: AnyType](*args: *Ts): pass + + foo(1, 1.2, True, "hello") + ``` + + Note that the syntax for parameter packs has been changed as well. + +- 📚 Add the runtime string type. + +- 📚 Introduce the DimList struct to remove the need to use low-level MLIR + operations. + +### Week of 2023-03-13 + +- 📢 Initializers for structs now use `__init__` instead of `__new__`, + following standard practice in Python. You can write them in one of two + styles, either traditional where you mutate self: + + ```mojo + fn __init__(self, x: Int): + self.x = x + ``` + + or as a function that returns an instance: + + ```mojo + fn __init__(x: Int) -> Self: + return Self {x: x} + ``` + + Note that `@register_passable` types must use the later style. + +- 📢 The default argument convention is now the `borrowed` convention. A + "borrowed" argument is passed like a C++ `const&` so it doesn't need to + invoke the copy constructor (aka the `__clone__` method) when passing a value + to the function. There are two differences from C++ `const&`: + + 1. A future borrow checker will make sure there are no mutable + aliases with an immutable borrow. + 2. `@register_passable` values are passed directly in an SSA register (and + thus, usually in a machine register) instead of using an extra reference + wrapper. This is more efficient and is the 'right default' for + `@register_passable` values like integers and pointers. + + This also paves the way to remove the reference requirement from `__clone__` + method arguments, which will allow us to fill in more support for them. + +- Support for variadic pack arguments has been added to Mojo. You can now + write heterogeneous variadic packs like: + + ```mojo + fn foo[*Ts: AnyType](args*: Ts): pass + + foo[Int, F32, String, Bool](1, 1.5, "hello", True) + ``` + +- The `owned` argument convention has been added. This argument convention + indicates that the function takes ownership of the argument and is responsible + for managing its lifetime. + +- The `borrowed` argument convention has been added. This convention signifies + the callee gets an immutable shared reference to a value in the caller's + context. + +- 📚 Add the `getenv` function to the `OS` module to enable getting environment + variables. + +- 📚 Enable the use of dynamic strides in `NDBuffer`. + +### Week of 2023-03-06 + +- 📢 Support added for using capturing async functions as parameters. + +- 📢 Returning result parameters has been moved from `return` statements to a + new `param_return` statement. This allows returning result parameters from + throwing functions: + + ```mojo + @raises + fn foo[() -> out: Int](): + param_return[42] + raise Error() + ``` + + And returning different parameters along `@parameter if` branches: + + ```mojo + fn bar[in: Bool -> out: Int](): + @parameter + if in: + param_return[1] + else: + param_return[2] + ``` + +- 📢 Mojo now supports omitting returns at the end of functions when they would + not reachable. For instance, + + ```mojo + fn foo(cond: Bool) -> Int: + if cond: + return 0 + else: + return 1 + + fn bar() -> Int: + while True: + pass + ``` + +- String literals now support concatenation, so `"hello " "world"` is treated + the same as `"hello world"`. + +- Empty bodies on functions, structs, and control flow statements are no longer + allowed. Please use `pass` in them to explicitly mark that they are empty, + just like in Python. + +- 📢 Structs in Mojo now default to living in memory instead of being passed + around in registers. This is the right default for generality (large + structures, structures whose pointer identity matters, etc) and is a key + technology that enables the borrow model. For simple types like `Int` and + `SIMD`, they can be marked as `@register_passable`. + + Note that memory-only types currently have some limitations: they cannot be + used in generic algorithms that take and return a `!mlirtype` argument, and + they cannot be used in parameter expressions. Because of this, a lot of + types have to be marked `@register_passable` just to work around the + limitations. We expect to enable these use-cases over time. + +- 📢 Mojo now supports computed lvalues, which means you can finally assign to + subscript expressions instead of having to call `__setitem__` explicitly. + + Some details on this: Mojo allows you to define multiple `__setitem__` + overloads, but will pick the one that matches your `__getitem__` type if + present. It allows you to pass computed lvalues into inout arguments by + introducing a temporary copy of the value in question. + +- Mojo now has much better support for using register-primary struct types in + parameter expressions and as the types of parameter values. This will allow + migration of many standard library types away from using bare MLIR types like + `__mlir_type.index` and towards using `Int`. This moves us towards getting rid + of MLIR types everywhere and makes struct types first-class citizens in the + parameter system. + +- 📚 Add a `sort` function. + +- 📚 Add non-temporal store to enable cache bypass. + +## February 2023 + +### Week of 2023-02-27 + +- 📢 The `@interface`, `@implements`, and `@evaluator` trio of decorators have + been removed, replaced by the `@parameter if` and `@adaptive` features. + +- 📢 Parameter inference can now infer the type of variadic lists. + +- 📢 Memory primary types are now supported in function results. A result slot + is allocated in the caller, and the callee writes the result of the function + into that slow. This is more efficient for large types that don't fit into + registers neatly! And initializers for memory-primary types now initialize + the value in-place, instead of emitting a copy! + +- Support for `let` decls of memory primary types has been implemented. These + are constant, ready-only values of memory primary types but which are + allocated on the function stack. + +- Overload conversion resolution and parameter inference has been improved: + + 1. Inference now works with `let` decls in some scenarios that weren't + working before. + 2. Parameter bindings can now infer types into parameter expressions. This + helps resolve higher-order functions in parameter expressions. + +- 📚 Optimize floor, ceil, and ldexp on X86 hardware. + +- 📚 Implement the log math function. + +### Week of 2023-02-20 + +- 📢 A new `@__memory_primary` struct decorator has been introduced. Memory + primary types must always have an address. For instance, they are always + stack-allocated when declared in a function and their values are passed into + function calls by address instead of copy. This is in contract with register + primary types that may not have an address, and which are passed by value + in function calls. Memory-primary fields are not allowed inside + register-primary structs, because struct elements are stored in-line. + +- 📢 A new `_CompilerBuiltin` module was added. This module defines core types + and functions of the language that are referenced by the parser, and hence, is + auto-imported by all other modules. For example new types for literal values + like the boolean True/False will be included in `_CompilerBuiltin`. + +- 📢 A special `__adaptive_set` property can be accessed on a function reference + marked as `@adaptive`. The property returns the adaptive overload set of that + function. The return type is a `!kgen.variadic`. This feature is useful to + implement a generic `evaluate` function in the standard library. + +- 📢 A new built-in literal type `BoolLiteral` was added in `_CompilerBuiltin`. + It represents the literal boolean values `True` and `False`. This is the first + Mojo literal to be emitted as a standard library type! + +- 📚 Add the prefetch intrinsic to enable HW prefetching a cache line. + +- 📚 Add the InlinedFixedVector, which is optimized for small vectors and stores + values on both the stack and the heap. + +### Week of 2023-02-13 + +- Unqualified lookups of struct members apply contextual parameters. This means + for instance that you can refer to static methods without binding the + struct parameters. + + ```mojo + struct Foo[x: Int]: + @staticmethod + bar(): pass + + foo(self): + bar() # implicitly binds to Foo[x].bar() + Foo[2].bar() # explicitly bind to another parameter + ``` + +- 📢 A new `Self` type refers to the enclosing type with all parameters bound + to their current values. This is useful when working with complex parametric + types, e.g.: + + ```mojo + struct MyArray[size: Int, element_type: type]: + fn __new__() -> Self: + return Self {...} + ``` + + which is a lot nicer than having to say `MyArray[size, element_type]` over + and over again. + +- 📢 Mojo now supports an `@adaptive` decorator. This decorator will supersede + interfaces, and it represents an overloaded function that is allowed to + resolve to multiple valid candidates. In that case, the call is emitted as a + fork, resulting in multiple function candidates to search over. + + ```mojo + @adaptive + fn sort(arr: ArraySlice[Int]): + bubble_sort(arr) + + @adaptive + fn sort(arr: ArraySlice[Int]): + merge_sort(arr) + + fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]): + let arr = lhs + rhs + sort(arr) # this forks compilation, creating two instances + # of the surrounding function + ``` + +- 📢 Mojo now requires that types implement the `__clone__` special member in + order to copy them. This allows the safe definition of non-copyable types + like Atomic. Note that Mojo still doesn't implement destructors, and (due to + the absence of non-mutable references) it doesn't actually invoke the + `__clone__` member when copying a let value. As such, this forces to you as + a Mojo user to write maximal boilerplate without getting much value out of it. + + In the future, we will reduce the boilerplate with decorators, and we will + actually start using it. This will take some time to build out though. + +- 📢 A special `__mlir_region` statement was added to provide stronger + invariants around defining MLIR operation regions in Mojo. It similar syntax + to function declarations, except it there are no results and no input + conventions. + +- 📚 Implement the log math function. + +- 📚 Improve the DType struct to enable compile-time equality checks. + +- 📚 Add the Complex struct class. + +### Week of 2023-02-06 + +- 📢 The `if` statement now supports a `@parameter` decorator, which requires + its condition to be a parameter expression, but which only emits the 'True' + side of the condition to the binary, providing a "static if" functionality. + This should eliminate many uses of `@interface` that are just used to provide + different constraint on the implementations. + +- 📢 `fn main():` is now automatically exported and directly runnable by the + command-line `mojo` tool. This is a stop-gap solution to enable script-like + use cases until we have more of the language built out. + +- 🪦 The `@nodebug_inline` feature has been removed, please use + `@alwaysinline("nodebug")` for methods that must be inlined and that we don't + want to step into. + +- 📢 Python chained comparisons, ex. `a < b < c`, are now supported in Mojo. + +- 📢 Functions can now be defined with default argument values, such as + `def f(x: Int, y: Int = 5):`. The default argument value is used when callers + do not provide a value for that argument: `f(3)`, for example, uses the + default argument value of `y = 5`. + +- Unused coroutine results are now nicely diagnosed as "missing await" warnings. + +- 📚 Introduce a vectorized reduction operations to the SIMD type. + +## January 2023 + +### Week of 2023-01-30 + +- A basic Mojo language server has been added to the VS Code extension, which + parses your code as you write it, and provides warnings, errors, and fix-it + suggestions! + +- 💯 The Mojo standard library is now implicitly imported by default. + +- The coroutine lowering support was reworked and a new `Coroutine[T]` type was + implemented. Now, the result of a call to an async function MUST be wrapped in + a `Coroutine[T]`, or else memory will leak. In the future, when Mojo supports + destructors and library types as literal types, the results of async function + calls will automatically wrapped in a `Coroutine[T]`. But today, it must be + done manually. This type implements all the expected hooks, such as + `__await__`, and `get()` to retrieve the result. Typical usage: + + ```mojo + async fn add_three(a: Int, b: Int, c: Int) -> Int: + return a + b + c + + async fn call_it(): + let task: Coroutine[Int] = add_three(1, 2, 3) + print(await task) + ``` + +- ⭐️ We now diagnose unused expression values at statement context in `fn` + declarations (but not in `def`s). This catches bugs with unused values, e.g. + when you forget the parens to call a function. + +- 📢 An `@always_inline("nodebug")` function decorator can be used on functions + that need to be force inlined, but when they should not have debug info in + the result. This should be used on methods like `Int.__add__` which should + be treated as builtin. + +- 📢 The `@export` decorator now supports an explicit symbol name to export to, + for example: + + ```mojo + @export("baz") # exported as 'baz' + fn some_mojo_fn_name(): + ``` + +- 📢 🚧 Subscript syntax is now wired up to the `__getitem__` dunder method. + + This allows type authors to implement the `__getitem__` method to enable + values to be subscripted. This is an extended version of the Python semantics + (given we support overloading) that allows you to define N indices instead of + a single version that takes a tuple (also convenient because we don't have + tuples yet). + + Note that this has a very, very important limitation: subscripts are NOT + wired up to `__setitem__` yet. This means that you can read values with + `.. = v[i]` but you cannot store to them with `v[i] = ..`. For this, please + continue to call `__setitem__` directly. + +- 📢 Function calls support parameter inference. + + For calls to functions that have an insufficient number of parameters + specified at the callsite, we can now infer them from the argument list. We + do this by matching up the parallel type structure to infer what the + parameters must be. + + Note that this works left to right in the parameter list, applying explicitly + specified parameters before trying to infer new ones. This is similar to how + C++ does things, which means that you may want to reorder the list of + parameters with this in mind. For example, a `dyn_cast`-like function will be + more elegant when implemented as: + + `fn dyn_cast[DstType: type, SrcType: type](src: SrcType) -> DstType:` + + Than with the `SrcType`/`DstType` parameters flipped around. + +- 📚 Add the growable Dynamic vector struct. + +### Week of 2023-01-23 + +- Inplace operations like `+=`/`__iadd__` may now take `self` by-val if they + want to, instead of requiring it to be by-ref. +- ⭐️ Inplace operations are no longer allowed to return a non-None value. The + corresponding syntax is a statement, not an expression. + +- A new `TaskGroup` type was added to the standard library. This type can be + used to schedule multiple tasks on a multi-threaded workqueue to be executed + in parallel. An async function can `await` all the tasks at once with the + taskgroup. + +- 📢 We now support for loops! A type that defines an `__iter__` method that + returns a type that defines `__next__` and `__len__` methods is eligible to + be used in the statement `for el in X()`. Control flow exits the loop when + the length is zero. + + This means things like this now work: + + ```mojo + for item in range(start, end, step): + print(item) + ``` + +- Result parameters now have names. This is useful for referring to result + parameters in the return types of a function: + + ```mojo + fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]: + ``` + +- 📢 We now support homogeneous variadics in value argument lists, using the + standard Python `fn thing(*args: Int):` syntax! Variadics also have support + in parameter lists: + + ```mojo + fn variadic_params_and_args[*a: Int](*b: Int): + print(a[0]) + print(b[1]) + ``` + +- 📚 Add the range struct to enable `for ... range(...)` loops. + +- 📚 Introduce the unroll generator to allow one to unroll loops via a library + function. + +### Week of 2023-01-16 + +- 📢 Struct field references are now supported in parameter context, so you + can use `someInt.value` to get the underlying MLIR thing out of it. This + should allow using first-class types in parameters more widely. +- 📢 We now support "pretty" initialization syntax for structs, e.g.: + + ```mojo + struct Int: + var value: __mlir_type.index + fn __new__(value: __mlir_type.index) -> Int: + return Int {value: value} + ``` + + This eliminates the need to directly use the MLIR `lit.struct.create` op in + struct initializers. This syntax may change in the future when ownership + comes in, because we will be able to support the standard `__init__` model + then. +- 📢 It is now possible to attach regions to `__mlir_op` operations. This is + done with a hack that allows an optional `_region` attribute that lists + references to the region bodies (max 1 region right now due to lack of list + `[]` literal). +- Nested functions now parse, e.g.: + + ```mojo + fn foo(): + fn bar(): + pass + bar() + ``` + +- Python-style `async` functions should now work and the `await` expression + prefix is now supported. This provides the joy of async/await syntactic + sugar when working with asynchronous functions. This is still somewhat + dangerous to use because we don't have proper memory ownership support yet. + +- String literals are now supported. + +- Return processing is now handled by a dataflow pass inside the compiler, so + it is possible to return early out of if statements. + +- The parser now supports generating 'fixit' hints on diagnostics, and uses + them when a dictionary literal uses a colon instead of equal, e.g.: + + ```log + x.mojo:8:48: error: expected ':' in subscript slice, not '=' + return __mlir_op.`lit.struct.create`[value = 42]() + ^ + : + ``` + +- 📚 Add reduction methods which operate on buffers. + +- 📚 Add more math functions like sigmoid, sqrt, rsqrt, etc. + +- 📚 Add partial load / store which enable loads and stores that are predicated + on a condition. + +### Week of 2023-01-09 + +- The `/` and `*` markers in function signatures are now parsed and their + invariants are checked. We do not yet support keyword arguments yet though, + so they aren't very useful. +- Functions now support a new `@nodebug_inline` decorator. + (Historical note: this was later replaced with `@alwaysinline("nodebug")`). + + Many of the things at the bottom level of the Mojo stack are trivial + zero-abstraction wrappers around MLIR things, for example, the `+` + operator on Int or the `__bool__` method on Bool itself. These operators + need to be force inlined even at -O0, but they have some additional things + that we need to wrestle with: + + 1. In no case would a user actually want to step into the `__bool__` method on + Bool or the + method on Int. This would be terrible debugger QoI for + unless you're debugging Int itself. We need something like + `__always_inline__, __nodebug__` attributes that clang uses in headers + like xmmintrin.h. + + 2. Similarly, these "operators" should be treated by users as primitives: + they don't want to know about MLIR or internal implementation details of + Int. + + 3. These trivial zero abstraction things should be eliminated early in the + compiler pipeline so they don't slow down the compiler, bloating out the + call graph with trivial leaves. Such thing slows down the elaborator, + interferes with basic MLIR things like fold(), bloats out the IR, or + bloats out generated debug info. + + 4. In a parameter context, we want some of these things to get inlined so + they can be simplified by the attribute logic and play more nicely with + canonical types. This is just a nice to have thing those of us who have + to stare at generated IR. + + The solution to this is a new `@nodebug_inline` decorator. This decorator + causes the parser to force-inline the callee instead of generating a call to + it. While doing so, it gives the operations the location of the call itself + (that's the "nodebug" part) and strips out let decls that were part of the + internal implementation details. + + This is a super-power-user-feature intended for those building the standard + library itself, so it is intentionally limited in power and scope: It can + only be used on small functions, it doesn't support regions, by-ref, throws, + async, etc. + +- Separately, we now support an `@alwaysInline` decorator on functions. This + is a general decorator that works on any function, and indicates that the + function must be inlined. Unlike `@nodebug_inline`, this kind of inlining is + performed later in the compilation pipeline. + +- The `__include` hack has been removed now that we have proper import support. + +- `__mlir_op` can now get address of l-value: + + You can use magic `(((x)))` syntax in __mlir_op that forces the `x` + expression to be an lvalue, and yields its address. This provides an escape + hatch (isolated off in `__mlir_op` land) that allows unsafe access to lvalue + addresses. + +- We now support `__rlshift__` and `__rtruediv__`. + +- 📢 The parser now resolves scoped alias references. This allows us to support + things like `SomeType.someAlias`, forward substituting the value. This + unblocks use of aliases in types like `DType`. We'd like to eventually + preserve the reference in the AST, but this unblocks library development. + +- 📚 Add a `now` function and `Benchmark` struct to enable timing and + benchmarking. + +- 📚 Move more of the computation in NDBuffer from runtime to compile time if + possible (e.g. when the dimensions are known at compile time). + +### Week of 2023-01-02 + +- 📚 Added the `print` function which works on Integers and SIMD values. + +- The frontend now has a new diagnostic subsystem used by the `kgen` tool (but + not by `kgen-translate` for tests) that supports source ranges on + diagnostics. Before we'd emit an error like: + + ```log + x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' + callee(1.0+F32(2.0)) + ^ + x.lit:4:1: note: function declared here + fn callee(a: Int): + ^ + ``` + + now we produce: + + ```log + x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' + callee(1.0+F32(2.0)) + ^ ~~~~~~~~~~~~ + x.lit:4:1: note: function declared here + fn callee(a: Int): + ^ + ``` + +- 📢 Parameter results are now supported in a proper way. They are now forward + declared with an alias declaration and then bound in a call with an arrow, + e.g.: + + ```mojo + alias a: __mlir_type.index + alias b: __mlir_type.index + idx_result_params[xyz * 2 -> a, b]() + ``` + +- Various minor issues with implicit conversions are fixed. For instances, + implicit conversions are now supported in parameter binding contexts and + `alias` declarations with explicit types. +- Doc strings are allowed on functions and structs, but they are currently + discarded by the parser. + +- 📚 Add a `print` method!!! + +- 📚 Demonstrate a naive matmul in Mojo. + +- 📚 Initial work on functions that depend on types (e.g. FPUtils, nan, inf, + etc.) + +- 📚 Allow one to query hardware properties such as simd_width, os, etc. via + TargetInfo at compile time. + +## December 2022 + +### Week of 2022-12-26 + +- 📢 You can now call functions in a parameter context! Calling a function in + a parameter context will evaluate the function at compile time. The result + can then be used as parameter values. For example, + + ```mojo + fn fma(x: Int, y: Int, z: Int) -> Int: + return a + b * c + + fn parameter_call(): + alias nelts = fma(32, 2, 16) + var x: SIMD[f32, nelts] + ``` + +- You can now disable printing of types in an `__mlir_attr` substitution by + using unary `+` expression. + +- 📢 `let` declarations are now supported in functions. `let` declarations are + local run-time constant values, which are always rvalues. They complement + 'var' decls (which are mutable lvalues) and are the normal thing to use in + most cases. They also generate less IR and are always in SSA form when + initialized. + + We will want to extend this to support 'let' decls in structs at some point + and support lazy initialized 'let' declarations (using dataflow analysis) but + that isn't supported yet. + +- 📚 Add the NDBuffer struct. + +- Happy new year. + +### Week of 2022-12-19 + +- 📚 Start of the Standard library: + 1. Added Integer and SIMD structs to bootstrap the standard library. + 2. Added very basic buffer data structure. + +- We have basic support for parsing parameter results in function calls! Result + parameters are an important Mojo metaprogramming feature. They allow functions + to return compile-time constants. + + ```mojo + fn get_preferred_simdwidthof[() -> nelts: Int](): + return[2] + + fn vectorized_function(): + get_preferred_simdwidthof[() -> nelts]() + var x: SIMD[f32, nelts] + ``` + +- Types can now be used as parameters of `!kgen.mlirtype` in many more cases. + +- MLIR operations with zero results don't need to specify `_type: []` anymore. + +- We support parsing triple quoted strings, for writing docstrings for your + functions and structs! + +- A new `__mlir_type[a,b,c]` syntax is available for substituting into MLIR + types and attributes is available, and the old placeholder approach is + removed. This approach has a few advantages beyond what placeholders do: + + 1. It's simpler. + 2. It doesn't form the intermediate result with placeholders, which + gets rejected by MLIR's semantic analysis, e.g. the complex case + couldn't be expressed before. + 3. It provides a simple way to break long attrs/types across multiple + lines. + +- We now support an `@evaluator` decorator on functions for KGEN evaluators. + This enables specifying user-defined interface evaluators when performing + search during compilation. + +- 📢 `import` syntax is now supported! + + This handles packaging imported modules into file ops, enables effective + isolation from the other decls. "import" into the desired context is just + aliasing decls, with the proper symbols references handle automatically during + IR generation. As a starting point, this doesn't handle any notion of packages + (as those haven't been sketched out enough). + +- 📢 Reversed binary operators (like `__radd__`) are now looked up and used if + the forward version (like `__add__`) doesn't work for some reason. + +- 📢 Implicit conversions are now generally available, e.g. in assign + statements, variable initializers etc. There are probably a few more places + they should work, but we can start eliminating all the extraneous explicit + casts from literals now. + +- Happy Holidays + +### Week of 2022-12-12 + +- 📢 Function overloading now works. Call resolution filters candidate list + according to the actual parameter and value argument specified at the site of + the call, diagnosing an error if none of the candidates are viable or if + multiple are viable and ambiguous. We also consider implicit conversions in + overload look: + + ```mojo + fn foo(x: Int): pass + fn foo(x: F64): pass + + foo(Int(1)) # resolves to the first overload + foo(1.0) # resolves to the second overload + foo(1) # error: both candidates viable with 1 implicit conversion! + ``` + +- The short circuiting binary `and` and `or` expressions are now supported. + +- Unary operator processing is a lot more robust, now handling the `not` + expression and `~x` on Bool. + +- 📢 The compiler now generates debug information for use with GDB/LLDB that + describes variables and functions. + +- The first version of the Mojo Visual Studio Code extension has been released! + It supports syntax highlighting for Mojo files. + +- The first version of the `Bool` type has landed in the new Mojo standard + library! + +- 📢 Implicit conversions are now supported in return statements. + +### Week of 2022-12-05 + +- "Discard" patterns are now supported, e.g. `_ = foo()` + +- We now support implicit conversions in function call arguments, e.g. + converting an `index` value to `Int` automatically. This eliminates a bunch + of casts, e.g. the need to say F32(1.0) everywhere. + + This is limited for a few reasons that will be improved later: + 1. We don't support overloading, so lots of types aren't convertible + from all the things they should be, e.g. you can't pass "1" to + something that expects F32, because F32 can't be created from index. + 2. This doesn't "check to see if we can invoke `__new__`" it force applies + it on a mismatch, which leads to poor QoI. + 3. This doesn't fix things that need radd. + +## November 2022 + +### Week of 2022-11-28 + +- 📢 We support the `True` and `False` keywords as expressions. + +- 📢 A new `alias` declaration is supported which allows defining local + parameter values. This will eventually subsume type aliases and other + things as it gets built out. + +- 📢 We now have end-to-end execution of Mojo files using the `kgen` tool! + Functions exported with `@export` can be executed. + +- 📢 We have try-except-else and `raise` statements and implicit error + propagation! The error semantics are that `def` can raise by default, but `fn` + must explicitly declare raising with a `@raises` decorator. Stub out basic + `Error` type. + +- The `&` sigil for by-ref arguments is now specified after the identifier. + Postfix works better for ref and move operators on the expression + side because it chains an mentally associates correctly: + `thing.method().result^`. We don't do that yet, but align param + decl syntax to it so that things won't be odd looking when we do. + In practice this looks like: + + ```mojo + def mutate_argument(a&: index): + a = 25 + ``` + +### Week of 2022-11-21 + +- 📢 The magic `index` type is gone. Long live `__mlir_type.index`. + +- Implement parameter substitution into parametric `__mlir_type` decls. This + allows us to define parametric opaque MLIR types with exposed parameters using + a new "placeholder" attribute. This allows us to expose the power of the KGEN + type parametric system directly into Mojo. + +- 📢 Fully-parametric custom types can now be defined and work in Mojo, bringing + together a lot of the recent work. We can write the SIMD type directly as a + wrapper around the KGEN type, for example: + + ```mojo + struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]: + var value: + __mlir_type.`!pop.simd<#lit, + #lit>`[nelts, dt] + + fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]: + return __mlir_op.`pop.add`(self.value, rhs.value) + ``` + +### Week of 2022-11-14 + +- 📢 Implement a magic `__mlir_type` declaration that can be used to access any + MLIR type. E.g. `__mlir_type.f64`. + +- 📢 Add an `fn` declaration. These are like `def` declarations, but are more + strict in a few ways: they require type annotations on arguments, don't allow + implicit variable declarations in their body, and make their arguments rvalues + instead of lvalues. + +- Implemented Swift-style backtick identifiers, which are useful for code + migration where names may collide with new keywords. + +- 📢 A new `__include` directive has been added that performs source-level + textual includes. This is temporary until we have an `import` model. + +- Implement IR generation for arithmetic operators like `+` and `*` in terms + of the `__add__` and `__mul__` methods. + +- 📢 Added support for `break` and `continue` statements, as well as early + returns inside loops and conditionals! + +- 📢 Implemented augmented assignment operators, like `+=` and `@=`. + +- 📢 Mojo now has access to generating any MLIR operations (without regions) + with a new `__mlir_op` magic declaration. We can start to build out the + language's builtin types with this: + + ```mojo + struct Int: + var value: __mlir_type.index + + fn __add__(self, rhs: Int) -> Int: + return __mlir_op.`index.add`(self.value, rhs.value) + ``` + + Attributes can be attached to the declaration with subscript `[]` syntax, + and an explicit result type can be specified with a special `_type` attribute + if it cannot be inferred. Attributes can be accessed via the `__mlir_attr` + magic decl: + + ```mojo + __mlir_op.`index.cmp`[ + _type: __mlir_type.i1, + pred: __mlir_attr.`#index` + ](lhs, rhs) + ``` + +- Improved diagnostics emissions with ranges! Now errors highlight the whole + section of code and not just the first character. + +### Week of 2022-11-07 + +- Implemented the `@interface` and `@implements` decorators, which provide + access to KGEN generator interfaces. A function marked as an `@interface` + has no body, but it can be implemented by multiple other functions. + + ```mojo + @interface + def add(lhs: index, rhs: index): + + @implements(add) + def normal_add(lhs: index, rhs: index) -> index: + return lhs + rhs + + @implements(add) + def slow_add(lhs: index, rhs: index) -> index: + wait(1000) + return normal_add(lhs, rhs) + ``` + +- 📢 Support for static struct methods and initializer syntax has been added. + Initializing a struct with `Foo()` calls an implicitly static `__new__` + method. This method should be used instead of `__init__` inside structs. + + ```mojo + struct Foo: + var value: index + + def __new__() -> Foo: + var result: Foo + result.value = Foo.return_a_number() # static method! + return result + + @staticmethod + def return_a_number() -> index: + return 42 + ``` + +- 📢 Full by-ref argument support. It's now possible to define in-place + operators like `__iadd__` and functions like `swap(x, y)` correctly. + +- 📢 Implemented support for field extract from rvalues, like `x.value` where + `x` is not an lvalue (`var` declaration or by-ref function argument). + +## October 2022 + +### Week of 2022-10-31 + +- Revised `return` handling so that a return statement with no expression is + syntax sugar for `return None`. This enables early exits in functions that + implicitly return `None` to be cleaner: + + ```mojo + def just_return(): + return + ``` + +- Added support for parsing more expressions: if-else, bitwise operators, + shift operators, comparisons, floor division, remainder, and matmul. + +- 📢 The type of the `self` argument can now be omitted on member methods. + +### Week of 2022-10-24 + +- Added parser support for right-associativity and unary ops, like the power + operator `a ** b ** c` and negation operator `-a`. + +- Add support for `&expr` in Mojo, which allows denoting a by-ref argument in + functions. This is required because the `self` type of a struct method is + implicitly a pointer. + +- Implemented support for parametric function declarations, such as: + + ```mojo + struct SIMD[dt: DType, width: index]: + fn struct_method(self: &SIMD[dt, width]): + pass + + def fancy_add[dt: DType, width: index]( + lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index: + return width + ``` + +### Week of 2022-10-17 + +- Added explicit variable declarations with `var`, for declaring variables both + inside functions and structs, with support for type references. Added `index` + as a temporary built-in type. + + ```mojo + def foo(lhs: index, rhs: index) -> index: + var result: index = lhs + rhs + return result + ``` + +- Implemented support for parsing struct declarations and references to type + declarations in functions! In `def`, the type can be omitted to signal an + object type. + + ```mojo + struct Foo: + var member: index + + def bar(x: Foo, obj) -> index: + return x.member + ``` + +- Implemented parser support for `if` statements and `while` loops! + + ```mojo + def if_stmt(c: index, a: index, b: index) -> index: + var result: index = 0 + if c: + result = a + else: + result = b + return result + + def while_stmt(init: index): + while init > 1: + init = init - 1 + ``` + +- Significantly improved error emission and handling, allowing the parser to + emit multiple errors while parsing a file. + +### Week of 2022-10-10 + +- Added support for parsing integer, float, and string literals. + +- Implemented parser support for function input parameters and results. You can + now write parametric functions like, + + ```mojo + def foo[param: Int](arg: Int) -> Int: + result = param + arg + return result + ``` + +### Week of 2022-10-03 + +- Added some basic parser scaffolding and initial parser productions, including + trivial expressions and assignment parser productions. +- Implemented basic scope handling and function IR generation, with support for + forward declarations. Simple functions like, + + ```mojo + def foo(x: Int): + ``` + + Now parse! But all argument types are hard-coded to the MLIR `index` type. + +- Added IR emission for simple arithmetic expressions on builtin types, like + `x + y`. + +## September 2022 + +### Week of 2022-09-26 + +- Mojo's first patch to add a lexer was Sep 27, 2022. + +- Settled on `[]` for Mojo generics instead of `<>`. Square brackets are + consistent with Python generics and don't have the less than ambiguity + other languages have. diff --git a/docs/changelog.md b/docs/changelog.md index 4dd0e8a43b..f0b0f8ad0b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,34 +1,6 @@ ---- -title: Mojo🔥 changelog -sidebar_label: Changelog -description: A history of significant Mojo changes. -toc_max_heading_level: 2 -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png ---- -This is a running list of significant changes for the Mojo language and tools. -It doesn't include all internal implementation changes. - -## Update Mojo - -If you don't have Mojo yet, see the [get started -guide](/mojo/manual/get-started/#get-the-mojo-sdk). - -To see your Mojo version, run this: - -```sh -mojo --version -``` - -To update Mojo, first [update `modular`](/cli/#description), and then run this: - -```sh -modular update mojo -``` +This is a running list of significant UNRELEASED changes for the Mojo language +and tools. Please add any significant user-visible changes here. [//]: # Here's the template to use when starting a new batch of notes: [//]: ## UNRELEASED @@ -41,6 +13,10 @@ modular update mojo ### 🔥 Legendary +- The Mojo standard library is now open source! Check out the + [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) + for everything you need to get started. + - Structs and other nominal types are now allowed to implicitly conform to traits. A struct implicitly conforms to a trait if it implements all the requirements for the trait. For example, any struct that implements `__str__` @@ -198,7 +174,7 @@ modular update mojo instead of the kind of function they are unused in. This will help catch API usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. -- The [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) and +- The [`DynamicVector`](/mojo/stdlib/collections/list#list) and [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) types now support negative indexing. This means that you can write `vec[-1]` which is equivalent to `vec[len(vec)-1]`. @@ -331,4329 +307,3 @@ modular update mojo does not work when specifying `end=""` - [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods correctly handle edge cases where `size_out >= size`. - -## v24.1.1 (2024-03-18) - -This release includes installer improvements and enhanced error reporting for -installation issues. Otherwise it is functionally identical to Mojo 24.1. - -## v24.1 (2024-02-29) - -### 🔥 Legendary - -- Mojo is now bundled with [the MAX platform](/max)! - - As such, the Mojo package version now matches the MAX version, which follows - a `YY.MAJOR.MINOR` version scheme. Because this is our first release in 2024, - that makes this version `24.1`. - -- Mojo debugging support is here! The Mojo VS Code extension includes debugger - support. For details, see [Debugging](/mojo/tools/debugging) in the Mojo - Manual. - -### ⭐️ New - -- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our - collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` - checks, and requires member elements to conform to the `KeyElement` trait. - - ```mojo - from collections import Set - - var set = Set[Int](1, 2, 3) - print(len(set)) # 3 - set.add(4) - - for element in set: - print(element[]) - - set -= Set[Int](3, 4, 5) - print(set == Set[Int](1, 2)) # True - print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True - let element = set.pop() - print(len(set)) # 1 - ``` - -- Mojo now supports the `x in y` expression as syntax sugar for - `y.__contains__(x)` as well as `x not in y`. - -- Mojo now has support for keyword-only arguments and parameters. For example: - - ```mojo - fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2): - print(a * b * c * d) - - my_product(3, c=5) # prints '30' - my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c' - ``` - - This includes support for declaring signatures that use both variadic and - keyword-only arguments/parameters. For example, the following is now possible: - - ```mojo - fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: - var res = 1 - for i in range(len(args)): - res *= args[i] - return res + offset - - print(prod_with_offset(2, 3, 4, 10)) # prints 240 - print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 - ``` - - Note that variadic keyword-only arguments/parameters (for example, `**kwargs`) - are not supported yet. That is, the following is not allowed: - - ```mojo - fn variadic_kw_only(a: Int, **kwargs): ... - ``` - - For more information, see - [Positional-only and keyword-only arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments) - in the Mojo Manual. - -- The `print()` function now accepts a keyword-only argument for the `end` - which is useful for controlling whether a newline is printed or not - after printing the elements. By default, `end` defaults to "\n" as before. - -- The Mojo SDK can now be installed on AWS Graviton instances. - -- A new version of the [Mojo Playground](/mojo/playground) is available. The new - playground is a simple interactive editor for Mojo code, similar to the Rust - Playground or Go Playground. The old - [JupyterLab based playground](https://playground.modular.com) will remain - online until March 20th. - -- The Mojo LSP server will now generate fixits for populating empty - documentation strings: - - ```mojo - fn foo(arg: Int): - """""" # Unexpected empty documentation string - ``` - - Applying the fixit from above will generate: - - ```mojo - fn foo(arg: Int): - """[summary]. - - Args: - arg: [description]. - """ - ``` - -- Added new `*_` syntax that allows users to explicitly unbind any number of - positional parameters. For example: - - ```mojo - struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass - - alias all_unbound = StructWithDefault[*_] - # equivalent to - alias all_unbound = StructWithDefault[_, _, _, _] - - alias first_bound = StructWithDefault[5, *_] - # equivalent to - alias first_bound = StructWithDefault[5, _, _, _] - - alias last_bound = StructWithDefault[*_, 6] - # equivalent to - alias last_bound = StructWithDefault[_, _, _, 6] - - alias mid_unbound = StructWithDefault[3, *_, 4] - # equivalent to - alias mid_unbound = StructWithDefault[3, _, _, 4] - ``` - - As demonstrated above, this syntax can be used to explicitly unbind an - arbitrary number of parameters, at the beginning, at the end, or in the - middle of the operand list. Since these unbound parameters must be explicitly - specified at some point, default values for these parameters are not applied. - For example: - - ```mojo - alias last_bound = StructWithDefault[*_, 6] - # When using last_bound, you must specify a, b, and c. last_bound - # doesn't have a default value for `c`. - var s = last_bound[1, 2, 3]() - ``` - - For more information see the Mojo Manual sections on - [partially-bound types](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types) - and - [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). - -- [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) now - supports iteration. Iteration values are instances of - [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: - - ```mojo - var v: DynamicVector[String]() - v.append("Alice") - v.append("Bob") - v.append("Charlie") - for x in v: - x[] = str("Hello, ") + x[] - for x in v: - print(x[]) - ``` - -- `DynamicVector` now has - [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and - [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. - -- The `mojo package` command now produces compilation agnostic packages. - Compilation options such as O0, or --debug-level, are no longer needed or - accepted. As a result, packages are now smaller, and extremely portable. - -- Initializers for `@register_passable` values can (and should!) now be - specified with `inout self` arguments just like memory-only types: - - ```mojo - @register_passable - struct YourPair: - var a: Int - var b: Int - fn __init__(inout self): - self.a = 42 - self.b = 17 - fn __copyinit__(inout self, existing: Self): - self.a = existing.a - self.b = existing.b - ``` - - This form makes the language more consistent, more similar to Python, and - easier to implement advanced features for. There is also no performance - impact of using this new form: the compiler arranges to automatically return - the value in a register without requiring you to worry about it. - - The older `-> Self:` syntax is still supported in this release, but will be - removed in a subsequent one, so please migrate your code. One thing to watch - out for: a given struct should use one style or the other, mixing some of - each won't work well. - -- The standard library `slice` type has been renamed to - [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` - function has been introduced. This makes Mojo closer to Python and makes the - `Slice` type follow the naming conventions of other types like `Int`. - -- "Slice" syntax in subscripts is no longer hard coded to the builtin `slice` - type: it now works with any type accepted by a container's `__getitem__()` - method. For example: - - ```mojo - @value - struct UnusualSlice: - var a: Int - var b: Float64 - var c: String - - struct YourContainer: - fn __getitem__(self, slice: UnusualSlice) -> T: ... - ``` - - Given this implementation, you can subscript into an instance of - `YourContainer` like `yc[42:3.14:"🔥"]` and the three values are passed to the - `UnusualSlice` constructor. - -- The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to - return an MLIR internal reference type. - -- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) - method, for moving a value from one pointer memory location to another. - -- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be - used to format any value whose type implements the - [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. - -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements - `__is__` and `__isnot__` so that you can use expressions of the form `x is y` - and `x is not y` with `PythonObject`. - -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the - `SizedRaising` trait. This means the built-in - [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. - -- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) - and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. - -- A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query - properties on paths. - -- The `os` package now has a - [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms - to the `PathLike` trait by implementing the `__fspath__()` function. - -- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to - query properties of the path. - -- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on - [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` - module to work on `PathLike` structs. For example, the following sample - lists all the directories in the `/tmp` directory: - - ```mojo - from pathlib import Path - - fn walktree(top: Path, inout files: DynamicVector[Path]): - try: - var ls = top.listdir() - for i in range(len(ls)): - var child = top / ls[i] - if child.is_dir(): - walktree(child, files) - elif child.is_file(): - files.append(child) - else: - print("Skipping '" + str(child) + "'") - except: - return - - fn main(): - var files = DynamicVector[Path]() - - walktree(Path("/tmp"), files) - - for i in range(len(files)): - print(files[i]) - ``` - -- The [`find()`](/mojo/stdlib/builtin/string_literal#find), - [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), - [`count()`](/mojo/stdlib/builtin/string_literal#count), and - [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods - now work on string literals. This means that you can write: - - ```mojo - if "Mojo" in "Hello Mojo": - ... - ``` - -- Breakpoints can now be inserted programmatically within the code using the - builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. - - Note: on Graviton instances, the debugger might not be able to resume after - hitting this kind of breakpoint. - -- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that - describes a type that can be represented as a boolean value. To conform to the - trait, a type must implement the `__bool__()` method. - -- Modules within packages can now use purely relative `from` imports: - - ```mojo - from . import another_module - ``` - -- Trivial types, like MLIR types and function types, can now be bound implicitly - to traits that require copy constructors or move constructors, such as - [`Movable`](/mojo/stdlib/builtin/value.html#movable), - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). - -- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory - value. We hope and expect that this will eventually be replaced by - `Reference(expr).lifetime` as the parameter system evolves, but this is - important in the meantime for use in function signatures. - -- A new magic `__type_of(expr)` call will yield the type of a value. This allows - one to refer to types of other variables. For example: - - ```mojo - fn my_function(x: Int, y: __type_of(x)) -> Int: - let z : __type_of(x) = y - return z - ``` - -### 🦋 Changed - -- As another step towards [removing let - declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md) - we have removed support for let declarations inside the compiler. To ease - migration, we parse `let` declarations as a `var` declaration so your code - won't break. We emit a warning about this, but please switch your code to - using `var` explicitly, because this migration support will be removed in a - subsequent update. - - ```mojo - fn test(): - # treated as a var, but please update your code! - let x = 42 # warning: 'let' is being removed, please use 'var' instead - x = 9 - ``` - -- It is no longer possible to explicitly specify implicit argument parameters in - [automatically parameterized - functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). - This ability was an oversight and this is now an error: - - ```mojo - fn autoparameterized(x: SIMD): - pass - - autoparameterized[DType.int32, 1](3) # error: too many parameters - ``` - -- `vectorize_unroll` has been removed, and - [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter - named `unroll_factor` with a default value of 1. Increasing `unroll_factor` - may improve performance at the cost of binary size. See the - [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) - for more details. - -- The `vectorize` signatures have changed with the closure `func` moved to the - first parameter: - - ```mojo - vectorize[func, width, unroll_factor = 1](size) - vectorize[func, width, size, unroll_factor = 1]() - ``` - - The doc string has been updated with examples demonstrating the difference - between the two signatures. - -- The `unroll` signatures have changed with the closure `func` moved to the - first parameter: - - ```mojo - unroll[func, unroll_count]() - ``` - -- The signature of the [`NDBuffer`](/mojo/stdlib/memory/buffer#ndbuffer) and - [`Buffer`](/mojo/stdlib/memory/buffer#buffer) types have changed. Now, both - take the type as the first parameter and no longer require the shape - parameter. This allows you to use these types and have sensible defaults. - For example: - - ```mojo - NDBuffer[DType.float32, 3] - ``` - - is equivalent to - - ```mojo - NDBuffer[DType.float32, 3, DimList.create_unknown[3]()] - ``` - - Users can still specify the static shape (if known) to the type: - - ```mojo - NDBuffer[DType.float32, 3, DimList(128, 128, 3)] - ``` - -- The error message for missing function arguments is improved: instead of - describing the number of arguments (e.g. `callee expects at least 3 arguments, - but 1 was specified`) the missing arguments are now described by - name (e.g. `missing 2 required positional arguments: 'b', 'c'`). - -- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait - is now a built-in trait and has been removed from `collections.vector`. - -- The `DynamicVector(capacity: Int)` constructor has been changed to take - `capacity` as a keyword-only argument to prevent implicit conversion from - `Int`. - -- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a - copy. - -- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` - and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - -- The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. - We originally thought about using those as language sugar for references, but - we believe that generic language features combined with the - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good - experience without dedicated sugar. - -### 🛠️ Fixed - -- [#435](https://github.com/modularml/mojo/issues/435) - Structs with Self type don't always work. -- [#1540](https://github.com/modularml/mojo/issues/1540) - Crash in register_passable self referencing struct. -- [#1664](https://github.com/modularml/mojo/issues/1664) - Improve error - message when `StaticTuple` is constructed with a negative size for - the number of elements. -- [#1679](https://github.com/modularml/mojo/issues/1679) - crash on SIMD of zero - elements. -- Various crashes on invalid code: - [#1230](https://github.com/modularml/mojo/issues/1230), - [#1699](https://github.com/modularml/mojo/issues/1699), - [#1708](https://github.com/modularml/mojo/issues/1708) -- [#1223](https://github.com/modularml/mojo/issues/1223) - Crash when parametric - function is passed as (runtime) argument. The parser now errors out instead. -- [#1530](https://github.com/modularml/mojo/issues/1530) - Crash during - diagnostic emission for parameter deduction failure. -- [#1538](https://github.com/modularml/mojo/issues/1538) and [#1607]( - https://github.com/modularml/mojo/issues/1607) - Crash when returning type - value instead of instance of expected type. This is a common mistake and the - error now includes a hint to point users to the problem. -- [#1613](https://github.com/modularml/mojo/issues/1613) - Wrong type name in - error for incorrect `self` argument type in trait method declaration. -- [#1670](https://github.com/modularml/mojo/issues/1670) - Crash on implicit - conversion in a global variable declaration. -- [#1741](https://github.com/modularml/mojo/issues/1741) - Mojo documentation - generation doesn't show `inout`/`owned` on variadic arguments. -- [#1621](https://github.com/modularml/mojo/issues/1621) - VS Code does not - highlight `raises` and `capturing` in functional type expressions. -- [#1617](https://github.com/modularml/mojo/issues/1617) - VS Code does not - highlight `fn` in specific contexts. -- [#1740](https://github.com/modularml/mojo/issues/1740) - LSP shows unrelated - info when hovering over a struct. -- [#1238](https://github.com/modularml/mojo/issues/1238) - File shadows Mojo - package path. -- [#1429](https://github.com/modularml/mojo/issues/1429) - Crash when using - nested import statement. -- [#1322](https://github.com/modularml/mojo/issues/1322) - Crash when missing - types in variadic argument. -- [#1314](https://github.com/modularml/mojo/issues/1314) - Typecheck error when - binding alias to parametric function with default argument. -- [#1248](https://github.com/modularml/mojo/issues/1248) - Crash when importing - from file the same name as another file in the search path. -- [#1354](https://github.com/modularml/mojo/issues/1354) - Crash when importing - from local package. -- [#1488](https://github.com/modularml/mojo/issues/1488) - Crash when setting - generic element field. -- [#1476](https://github.com/modularml/mojo/issues/1476) - Crash in interpreter - when calling functions in parameter context. -- [#1537](https://github.com/modularml/mojo/issues/1537) - Crash when copying - parameter value. -- [#1546](https://github.com/modularml/mojo/issues/1546) - Modify nested vector - element crashes parser. -- [#1558](https://github.com/modularml/mojo/issues/1558) - Invalid import causes - parser to crash. -- [#1562](https://github.com/modularml/mojo/issues/1562) - Crash when calling - parametric type member function. -- [#1577](https://github.com/modularml/mojo/issues/1577) - Crash when using - unresolved package as a variable. -- [#1579](https://github.com/modularml/mojo/issues/1579) - Member access into - type instances causes a crash. -- [#1602](https://github.com/modularml/mojo/issues/1602) - Interpreter failure - when constructing strings at compile time. -- [#1696](https://github.com/modularml/mojo/issues/1696) - Fixed an issue that - caused syntax highlighting to occasionally fail. -- [#1549](https://github.com/modularml/mojo/issues/1549) - Fixed an issue when - the shift amount is out of range in `SIMD.shift_left` and `SIMD.shift_right`. - -## v0.7.0 (2024-01-25) - -### ⭐️ New - -- A new Mojo-native dictionary type, - [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. - `Dict` stores values that conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. Keys need to conform to the new - [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is - not yet implemented by other standard library types. In the short term, you - can create your own wrapper types to use as keys. For example, the following - sample defines a `StringKey` type and uses it to create a dictionary that maps - strings to `Int` values: - - ```mojo - from collections.dict import Dict, KeyElement - - @value - struct StringKey(KeyElement): - var s: String - - fn __init__(inout self, owned s: String): - self.s = s ^ - - fn __init__(inout self, s: StringLiteral): - self.s = String(s) - - fn __hash__(self) -> Int: - return hash(self.s) - - fn __eq__(self, other: Self) -> Bool: - return self.s == other.s - - fn main() raises: - var d = Dict[StringKey, Int]() - d["cats"] = 1 - d["dogs"] = 2 - print(len(d)) # prints 2 - print(d["cats"]) # prints 1 - print(d.pop("dogs")) # prints 2 - print(len(d)) # prints 1 - ``` - - We plan to add `KeyElement` conformance to standard library types in - subsequent releases. - -- Users can opt-in to assertions used in the standard library code by - specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to - compile your source file(s). In the case that an assertion is fired, - the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are _not enabled_ - in the standard library right now for performance reasons. - -- The Mojo Language Server now implements the References request. IDEs use - this to provide support for **Go to References** and **Find All References**. - A current limitation is that references outside of the current document are - not supported, which will be addressed in the future. - -- The [`sys.info`](/mojo/stdlib/sys/info) module now includes - `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` - functions. - -- Homogenous variadic arguments consisting of memory-only types, such as - `String` are more powerful and easier to use. These arguments are projected - into a - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). - - (Previous releases made it easier to use variadic lists of register-passable - types, like `Int`.) - - Subscripting into a `VariadicListMem` now returns the element instead of an - obscure internal type. In addition, we now support `inout` and `owned` - variadic arguments: - - ```mojo - fn make_worldly(inout *strs: String): - # This "just works" as you'd expect! - for i in range(len(strs)): - strs[i] += " world" - fn main(): - var s1: String = "hello" - var s2: String = "konnichiwa" - var s3: String = "bonjour" - make_worldly(s1, s2, s3) - print(s1) # hello world - print(s2) # konnichiwa world - print(s3) # bonjour world - ``` - - (Previous releases made it easier to use variadic lists, but subscripting into - a `VariadicListMem` returned a low-level pointer, which required the user to - call `__get_address_as_lvalue()` to access the element.) - - Note that subscripting the variadic list works nicely as above, but - iterating over the variadic list directly with a `for` loop produces a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead - of the desired value, so an extra subscript is required; We intend to fix this - in the future. - - ```mojo - fn make_worldly(inout *strs: String): - # Requires extra [] to dereference the reference for now. - for i in strs: - i[] += " world" - ``` - - Heterogenous variadic arguments have not yet been moved to the new model, but - will in future updates. - - Note that for variadic arguments of register-passable types like `Int`, the - variadic list contains values, not references, so the dereference operator - (`[]`) is not required. This code continues to work as it did previously: - - ```mojo - fn print_ints(*nums: Int): - for num in nums: - print(num) - print(len(nums)) - ``` - -- Mojo now has a prototype version of a safe - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's - lifetime tracking pass can reason about references to safely extend local - variable lifetime, and check indirect access safety. The `Reference` type - is brand new (and currently has no syntactic sugar) so it must be explicitly - dereferenced with an empty subscript: `ref[]` provides access to the - underlying value. - - ```mojo - fn main(): - var a : String = "hello" - var b : String = " references" - - var aref = Reference(a) - aref[] += b - print(a) # prints "hello references" - - aref[] += b - # ^last use of b, it is destroyed here. - - print(aref[]) # prints "hello references references" - # ^last use of a, it is destroyed here. - ``` - - While the `Reference` type has the same in-memory representation as a C - pointer or the Mojo `Pointer` type, it also tracks a symbolic "lifetime" value - so the compiler can reason about the potentially accessed set of values. This - lifetime is part of the static type of the reference, so it propagates through - generic algorithms and abstractions built around it. - - The `Reference` type can form references to both mutable and immutable memory - objects, e.g. those on the stack or borrowed/inout/owned function arguments. - It is fully parametric over mutability, eliminating the [problems with code - duplication due to mutability - specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and - provides the base for unified user-level types. For example, it could be - used to implement an array slice object that handles both mutable and immutable - array slices. - - While this is a major step forward for the lifetimes system in Mojo, it is - still _very_ early and awkward to use. Notably, there is no syntactic sugar - for using references, such as automatic dereferencing. Several aspects of it - need to be more baked. It is getting exercised by variadic memory arguments, - which is why they are starting to behave better now. - - Note: the safe `Reference` type and the unsafe pointer types are defined in - the same module, currently named `memory.unsafe`. We expect to restructure - this module in a future release. - -- Mojo now allows types to implement `__refattr__()` and `__refitem__()` to - enable attribute and subscript syntax with computed accessors that return - references. For common situations where these address a value in memory this - provides a more convenient and significantly more performant alternative to - implementing the traditional get/set pairs. Note: this may be changed in the - future when references auto-dereference—at that point we may switch to just - returning a reference from `__getattr__()`. -- Parametric closures can now capture register passable typed values by copy - using the `__copy_capture` decorator. For example, the following code will - print `5`, not `2`. - - ```mojo - fn foo(x: Int): - var z = x - - @__copy_capture(z) - @parameter - fn formatter() -> Int: - return z - z = 2 - print(formatter()) - - fn main(): - foo(5) - ``` - -- String now implements KeyElement and may be used as a key in Dict. -- More robust support for structs with fields of self referencing types. - For example, the following code will work and print `0`: - - ```mojo - struct Foo(CollectionElement): - var vec: DynamicVector[Self] - - fn __init__(inout self: Self): - self.vec = DynamicVector[Self]() - - fn __moveinit__(inout self: Self, owned existing: Self): - self.vec = existing.vec ^ - - fn __copyinit__(inout self: Self, existing: Self): - self.vec = existing.vec - - - fn main(): - var foo = Foo() - print(len(foo.vec)) - ``` - -### ❌ Removed - -- The `__takeinit__` special constructor form has been removed from the - language. This "non-destructive move" operation was previously wired into the - `x^` transfer operator, but had unpredictable behavior that wasn't consistent. - Now that Mojo has traits, it is better to model this as an explicit `.take()` - operation on a type, which would transfer out the contents of the type without - ending its lifetime. For example, for a type that holds a pointer, `take()` - might return a new instance pointing to the same data, and null out its own - internal pointer. - - This change makes it clear when a lifetime is ended versus when the - contents of an LValue are explicitly taken. - -- The current implementation of autotuning has been deprecated, as Mojo's - autotuning implementation is undergoing a redesign. Tutorials around the - current implementation have also been removed as they are being rewritten. - - Consequently, the `autotune()`, `autotune_fork()`, and `search()` functions - have been removed from the standard library. - -- The `_OldDynamicVector` type that worked only on register passable element - types has been removed. Please migrate uses to - [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) which - works on both register passable and memory types. - -- The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using - either [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) - or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - instead. - -- The `@adaptive` decorator has been removed from the language. Any uses of the - decorator in a non-search context can be replaced with `@parameter if`. For - example: - - ```mojo - @adaptive - fn foo[a: Bool](): - constrained[a]() - body1() - - @adaptive - fn foo[a: Bool](): - constrained[not a]() - body2() - ``` - - Can be rewritten as: - - ```mojo - fn foo[a: Bool](): - @parameter - if a: - body1() - else: - body2() - ``` - - Consequently, the special `__adaptive_set` attribute has been removed as well. - -- Result parameters have been removed from Mojo. Result parameter declarations - in function parameter lists are no longer allowed, nor are forward alias - declarations. This includes removing the `param_return` statement. - -- The `@noncapturing` and `@closure` decorators have been removed due to - refinements and improvements to the closure model. See below for more details! - -### 🦋 Changed - -- The Mojo closure model has been refined to be more straightforward and safe. - Mojo has two closure types: parameter closures and runtime closures. Parameter - closures can be used in higher-order functions and are the backbone of - functions like `vectorize` and `parallelize`. They are always denoted by - `@parameter` and have type `fn() capturing -> T` (where `T` is the return - type). - - On the other hand, runtime closures are always dynamic values, capture values - by invoking their copy constructor, and retain ownership of their capture - state. You can define a runtime closure by writing a nested function that - captures values: - - ```mojo - fn outer(b: Bool, x: String) -> fn() escaping -> None: - fn closure(): - print(x) # 'x' is captured by calling String.__copyinit__ - - fn bare_function(): - print("hello") # nothing is captured - - if b: - # closure can be safely returned because it owns its state - return closure^ - - # function pointers can be converted to runtime closures - return bare_function - ``` - - The type of runtime closures are of the form `fn() escaping -> T`. You - can pass equivalent function pointers as runtime closures. - - Stay tuned for capture list syntax for move capture and capture by reference, - and a more unified closure model! - -- The `@unroll(n)` decorator can now take a parameter expression for - the unroll factor, i.e. `n` can be a parameter expression that is - of integer type. - -- The `cpython` module in the `python` package has been moved to be an internal - module, i.e, `_cpython`. - -- `AnyType` and `Destructable` have been unified into a single trait, `AnyType`. - Every nominal type (i.e. all structs) now automatically conform to `AnyType`. - -- Previously, the `mojo package` command would output a Mojo package that - included both partly-compiled Mojo code, as well as fully-compiled machine - code for a specific computer architecture -- the architecture of the machine - being used to invoke the `mojo package` command. - - Now, `mojo package` only includes partly-compiled Mojo code. It is only fully - compiled for the specific computer architecture being used at the point that - the package is first `import`-ed. As a result, Mojo packages are smaller and - more portable. - -- The `simd_width` and `dtype` parameters of `polynomial_evaluate` have been - switched. Based on the request in - [#1587](https://github.com/modularml/mojo/issues/1587), the - `polynomial_evaluate` function has also been extended so that the - `coefficients` parameter can take either a either a - [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a - [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). - -- As a tiny step towards removing `let` declarations, this release removes the - warning: `'var' was never mutated, consider switching to a 'let'`. - -### 🛠️ Fixed - -- [#1595](https://github.com/modularml/mojo/issues/1595) - Improve error message - when trying to materialize `IntLiteral` in runtime code. -- Raising an error from the initializer of a memory-only type now works - correctly in the presence of complex control flow. Previously Mojo could run - the destructor on `self` before it was initialized when exiting with an - error. -- [#1096](https://github.com/modularml/mojo/issues/1096) - Improve warning - messages for dead code in conditionals like `or` expressions. -- [#1419](https://github.com/modularml/mojo/issues/1419) - Fix assertion failure - with uninitialized lattice values. -- [#1402](https://github.com/modularml/mojo/issues/1402) - Fix movable trait not - detected on recursive struct implemented with `AnyPointer`. -- [#1399](https://github.com/modularml/mojo/issues/1399) - Fix parser crash when - a parameter type in a struct that implements a trait is misspelled. -- [#1152](https://github.com/modularml/mojo/issues/1152) - Allow mutable `self` - argument when overloading operators using dunder methods. -- [#1493](https://github.com/modularml/mojo/issues/1493) - Fix crash in - `DynamicVector` copy constructor in certain situations. -- [#1316](https://github.com/modularml/mojo/issues/1316) - The `benchmark.keep` - function now properly handles vector types. -- [#1505](https://github.com/modularml/mojo/issues/1505) - The `simd.shuffle` - operation now works on 64 element permutations. -- [#1355](https://github.com/modularml/mojo/issues/1355) - Fix `String.find()` - returning wrong value when starting index is non-zero. -- [#1367](https://github.com/modularml/mojo/issues/1367) - Fix `String.replace()` - returning incorrect results for multi-character search strings. -- [#1535](https://github.com/modularml/mojo/issues/1535) - Invalid error `field - 'w.x.y' destroyed out of the middle of a value, preventing the overall value - from being destroyed`. -- [#1475](https://github.com/modularml/mojo/issues/1475) - Assertion failure in - nested loop. -- [#1591](https://github.com/modularml/mojo/issues/1591) - Assertion failure - when using `AnyType` struct member. -- [#1503](https://github.com/modularml/mojo/issues/1503) - Rename the mojo build - of LLDB to `mojo-lldb`, to prevent name collisions with the system's LLDB. -- [#1542](https://github.com/modularml/mojo/issues/1542) - `@unroll` does not - accept alias as unroll factor. -- [#1443](https://github.com/modularml/mojo/issues/1443) - Compiler crash on - variadic list of traits. -- [#1604](https://github.com/modularml/mojo/issues/1604) - Variable of trivial - type not destroyed by transferring ownership. -- [#1341](https://github.com/modularml/mojo/issues/1341) - Segmentation fault - when passing closures around. -- [#217](https://github.com/modularml/mojo/issues/217) - Closure state is - stack allocated. - -## v0.6.1 (2023-12-18) - -### ⭐️ New - -- The Mojo REPL now provides limited support for the `%cd` magic command. - - This command automatically maintains an internal stack of directories you - visit during the REPL session. Usage: - - - `%cd 'dir'`: change to directory `dir` and push it on the directory stack. - - `%cd -`: pop the directory stack and change to the last visited directory. - -- Structs decorated with `@value` now automatically conform to the - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. - -- [`String`](/mojo/stdlib/builtin/string.html#string) now has new - [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and - [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, - respectively, to Python's `str.toupper()` and `str.tolower()`. - -- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and - [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types - implementing the `__hash__()` method. Future releases will add `Hashable` - support to Standard Library types. In the meantime, the `hash` module includes - a version of the `hash()` function that works on arbitrary byte strings. To - generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you - use the internal `_hash_simd()` function: - - ```mojo - from builtin.hash import _hash_simd - - fn gen_simd_hash(): - let vector = SIMD[DType.int64, 4](1, 2, 3, 4) - let hash = _hash_simd(vector) - ``` - -- Several standard library types now conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), - [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), - [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector), - [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), - and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). - -### 🦋 Changed - -- `utils.vector` has been moved to a new `collections` package to make - space for new collections. This means that if you had previous code - that did `from utils.vector import DynamicVector`, it now needs to - be `from collections.vector import DynamicVector` due to the move. - -- The special destructor method `__del__()` has been changed to enforce - that it cannot raise an error. Raising destructors are not supported properly - at the moment. - -### 🛠️ Fixed - -- [#1421](https://github.com/modularml/mojo/issues/1421) - Fixed a crash when - using Tuples in the REPL. - -- [#222](https://github.com/modularml/mojo/issues/222) - Generate an error - for obviously self recursive functions. - -- [#1408](https://github.com/modularml/mojo/issues/1408) - Fix overload - resolution when candidates can return generic types. - -- [#1413](https://github.com/modularml/mojo/issues/1413) and - [#1395](https://github.com/modularml/mojo/issues/1395) - Do not crash when - re-declaring a builtin declaration. - -- [#1307](https://github.com/modularml/mojo/issues/1307) - Fix compatibility of - function signatures that only differ in default argument values. - -- [#1380](https://github.com/modularml/mojo/issues/1380) - Fix printing - of empty `String`. - -## v0.6.0 (2023-12-04) - -### 🔥 Legendary - -- Traits have arrived! - - You can now define a _trait_, which consists of a required set of method - prototypes. A struct can _conform to_ the trait by implementing these methods. - This lets you write generic functions that work on any structs that conform to - a given trait. - - The following section gives a brief overview of traits—see the - [Mojo Manual](/mojo/manual/traits.html) and this - [traits blog post](https://modul.ar/traits-blog) for more details! - - Traits are declared with the `trait` keyword. The bodies of traits should - contain method signatures declared with `...` as their bodies. Default - method implementations are not supported yet. - - ```mojo - trait SomeTrait: - fn required_method(self, x: Int): ... - ``` - - The trait can be implemented on a struct by inheriting from it. - - ```mojo - struct SomeStruct(SomeTrait): - fn required_method(self, x: Int): - print("hello traits", x) - ``` - - You can then write a generic functions that accepts any type that conforms to - the trait. You do this by creating a parameterized function with a - trait-typed parameter: - - ```mojo - fn fun_with_traits[T: SomeTrait](x: T): - x.required_method(42) - ``` - - Which can be invoked with instances of types that conform to the trait: - - ```mojo - var thing = SomeStruct() - # Infer the parameter `T`! - fun_with_traits(thing) - ``` - - Traits can also inherit from other traits, which simply requires that - implementors of the child trait also conform to all parent traits. - - ```mojo - trait Parent: - fn parent_func(self): ... - - trait Child(Parent): - fn child_func(self): ... - ``` - - Then, both child and parent trait methods can be invoked on instances of - the trait `Child`. As well, an instance of the child trait can be converted to - an instance of the parent trait. - - ```mojo - fn the_parents[T: Parent](x: T): - x.parent_func() - - fn the_children[T: Child](x: T): - x.child_func() - x.parent_func() - # Upcast `x` from instance of `Child` to `Parent`. - the_parents(x) - ``` - - For more information, see the [Traits page](/mojo/manual/traits.html) - in the Mojo Manual. - -- A fundamental `Destructable` trait has been added to the language. This is a - core trait that every trait automatically conforms to. This enables - destruction of generic types and generic collections. - - **Note:** We're aware that this trait might be better spelled `Destructible`. - We're planning on removing it in the future and moving its functionality to - `AnyType` so that any type that doesn't provide its own destructor will have - a default, no-op destructor. - -- We've added some traits to the standard library, you can implement these on - your own types: - - - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) - - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) - - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) - - [`Intable`](/mojo/stdlib/builtin/int.html#intable) - - [`Sized`](/mojo/stdlib/builtin/len.html#sized) - - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - -- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), - [`str()`](/mojo/stdlib/builtin/str.html#str), and - [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with - types that implement the `Sized`, `Stringable`, and `Intable` traits, - respectively. - -- [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) is now a - proper generic collection that can use any type that implements the `Movable` - and `Copyable` traits. This means you can now write, for example, - `DynamicVector[String]`. Also, `DynamicVector` now invokes its element - destructors upon destruction, so `_del_old` has been deleted. - -- `print` now works on any types that implement `Stringable` by invoking their - `__str__` method: - - ```mojo - @value - struct BoxedInt(Stringable): - var value: Int - - fn __str__(self) -> String: - return self.value - - print(BoxedInt(11), "hello traits!", BoxedInt(42)) - ``` - -### ⭐️ New - -- The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include _everything_ about Mojo yet, but it includes a lot, - and more than the original [programming - manual](/mojo/programming-manual.html) (now deprecated). - - Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on - GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love - to accept contributions to help us improve them! - -- Mojo now supports partial automatic parameterization: when a function is - declared with an argument of a partially bound type, the unbound parameters - of that type are implicitly added to the function's input parameters. For - example: - - ```mojo - @value - struct Fudge[a: Int, b: Int, c: Int = 7]: ... - - # These function declarations are roughly equivalent: - fn eat(f: Fudge[5]): ... # implicitly parameterized - fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized - ``` - - In the first signature for `eat()`, the `b` parameter isn't bound, so it's - _implicitly_ added as an input parameter on the function. - - In the second signature for `eat()`, the author has explicitly defined an - input parameter (`_b`), which is bound to the second parameter on the argument - type (which happens to be `b`). - - Both functions can be called like this: - - ```mojo - eat(Fudge[5, 8]()) - ``` - - Mojo infers the value of the `b` parameter from the argument (in this case, - 8). - - With the second signature, you can also pass the `_b` parameter value - explicitly: - - ```mojo - eat[3](Fudge[5, 3]()) - ``` - - Moreover, Mojo now allows you to explicitly mark parameters as unbound using - the `_` as syntax meaning "placeholder for an unbound parameter." For example: - - ```mojo - # These function declarations are roughly equivalent: - fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized - fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized - fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized - ``` - - The first two signatures explicitly unbind the `b` and `c` parameters. - - In the last signature, the `_b` and `_c` parameters are explicitly declared by - the author, and bound to the `b` and `c` parameters in the argument type. - - Any of these signatures can be called like this: - - ```mojo - eat(Fudge[5, 8]()) - eat(Fudge[5, 8, 9]()) - ``` - - Note that the default parameter values of struct parameters are bound, unless - explicitly unbound by the user. - - For more information, see the - [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). - -- Parametric types can now be partially bound in certain contexts. For example, - a new `Scalar` type alias has been added defined as: - - ```mojo - alias Scalar = SIMD[size=1] - ``` - - Which creates a parametric type alias `Scalar` with a single parameter of type - `DType`. Types can also be partially or fully bound in other contexts. For - instance, `alias` declarations of type values inside functions now work - properly: - - ```mojo - fn type_aliases(): - alias T = SIMD - print(T[DType.float32, 1]()) - alias Partial = T[type=DType.int32] - print(Partial[2]()) - ``` - -- The `__mlir_op` feature now supports operations that return multiple results. - To use them, you write the `_type` field as a `Tuple` of types. For example: - - ```mojo - # The `ret` variable has type `Tuple[Int, Int]`. - let ret = __mlir_op.`multi_result_op`[ _type = (Int, Int) ]() - ``` - -- Mojo now has the ability to read raw bytes from a file using the - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. - For example: - - ```mojo - with open("file.binary", "r") as f: - data = f.read_bytes() - ``` - -- A size argument was added to the - [`read()`](/mojo/stdlib/builtin/file.html#read) and - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the - builtin `file.FileHandle`. The size argument defaults to -1 and maintains the - previous "read to EOF" behavior when size is negative. - - ```mojo - with open("file.binary", "r") as f: - data1 = f.read_bytes(1024) - data2 = f.read_bytes(256) - ``` - -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and - `read_text()` methods to read file contents from a path: - - ```mojo - let text_path = Path("file.txt") - let text = text_path.read_text() - - let binary_path = Path("file.binary") - let data = binary_path.read_bytes() - ``` - -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` - methods to save and load to file. These - methods preserve shape and datatype information. For example: - - ```mojo - let tensor = Tensor[DType.float32]() - tensor.save(path) - - let tensor_from_file = Tensor[DType.float32].load(path) - ``` - -- Subscripting added to - [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): - - ```mojo - let p = DTypePointer[DType.float16].alloc(4) - for i in range(4): - p[i] = i - print(p[i]) - ``` - -- `file.FileHandle` now has a `seek()` method. - -- [`String`](/mojo/stdlib/builtin/string.html#string) now has an - [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to - Python's `str.rfind()`. - -- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method - analogous to Python's `str.split()`. - -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a - [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to - Python's `pathlib.Path.suffix`. - -- The Mojo REPL now supports indented expressions, making it a bit easier to - execute expressions copied from an indented block (such as a doc string). - -- The Mojo Language Server now implements the Document Symbols request. IDEs use - this to provide support for **Outline View** and **Go to Symbol**. This - addresses [Issue #960](https://github.com/modularml/mojo/issues/960). - -- The Mojo Language Server now shows documentation when code completing modules - or packages in `import` statements. - -- The Mojo Language Server now supports processing code examples, defined as - markdown Mojo code blocks, inside of doc strings. This enables IDE features - while writing examples in API documentation. - -- The Mojo Language Server now provides semantic token information, providing - better highlighting for symbols whose semantics are not statically analyzable. - -- The Mojo Language Server now classifies doc strings as folding ranges, - making them easier to collapse, reducing vertical space while editing. - -- Command line options for the `mojo` driver that take arguments can now be - written in either of two ways: both `--foo FOO` and `--foo=FOO`. Previously, - only the former was valid. - -### 🦋 Changed - -- Variadic list types - [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) - are now iterable. Variadic arguments are automatically projected into one of - these types inside the function body, so var args can be iterated: - - ```mojo - fn print_ints(*nums: Int): - for num in nums: - print(num) - print(len(nums)) - ``` - -- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) - package now raise an `Error` when the assertion fails instead of returning a - `Bool` for whether the assertion succeeded or not. - -- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no - longer (implicitly) assumed to be register-passable. A new `AnyRegType` type - is used to represent generic types that are register passable. - -- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) - report is now an argument instead of a parameter: - - ```mojo - let report = benchmark.run[timer]() - report.print(Unit.ms) - ``` - -- Default values on `inout` arguments are no longer permitted, i.e. the - following will now raise an error: - - ```mojo - fn inout_default(inout x: Int = 2): ... - ``` - -- The `to_string()` function has been removed from - [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of - the new `__str__()` function. This composes better with traits so it can be - used with the generic `str()` function. - -### 🛠️ Fixed - -- [#734](https://github.com/modularml/mojo/issues/734) - Consumption of struct - works only for types with a `__del__` method. - -- [#910](https://github.com/modularml/mojo/issues/910) - Parser crash when - using memory-only generic type as return of function that `raise`s. - -- [#1060](https://github.com/modularml/mojo/issues/1060) - Mojo happily parses - code that has messed up indentation - -- [#1159](https://github.com/modularml/mojo/issues/1159) - The language server - doesn't warn about bad return type. - -- [#1166](https://github.com/modularml/mojo/issues/1166) - warning: unreachable - code after return statement with context manager - -- [#1098](https://github.com/modularml/mojo/issues/1098) - The language server - doesn't highlight properties of PythonObjects correctly. - -- [#1153](https://github.com/modularml/mojo/issues/1153) - The language server - crashes when parsing an invalid multi-nested module import. - -- [#1236](https://github.com/modularml/mojo/issues/1236) - The language server - doesn't show autocomplete in if statements. - -- [#1246](https://github.com/modularml/mojo/issues/1246) - Warning diagnostics - are transient in the presence of caching. - -### Known Issue - -- There is an issue affecting Jupyter notebooks that use autotuning and traits. - This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the _Matrix multiplication in - Mojo_ notebook. - -## v0.5.0 (2023-11-2) - -### ⭐️ New - -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the - architectural SIMD width of the type. This means you can write - `SIMD[DType.float32]` which is equivalent to - `SIMD[DType.float32, simdwidthof[DType.float32]()]`. - -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` - function that allows you to concatenate two `SIMD` values together and produce - a new `SIMD` value. - -- Mojo now supports compile-time _keyword parameters_, in addition to existing - support for [keyword - arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For - example: - - ```mojo - fn foo[a: Int, b: Int = 42](): - print(a, "+", b) - - foo[a=5]() # prints '5 + 42' - foo[a=7, b=13]() # prints '7 + 13' - foo[b=20, a=6]() # prints '6 + 20' - ``` - - Keyword parameters are also supported in structs: - - ```mojo - struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]: - fn __init__(inout self): - print(msg, a) - - fn use_kw_params(): - KwParamStruct[a=42]() # prints '🔥mojo🔥 42' - KwParamStruct[5, msg="hello"]() # prints 'hello 5' - KwParamStruct[msg="hello", a=42]() # prints 'hello 42' - ``` - - For more detail, see the [programming - manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). - - For the time being, the following notable limitations apply: - - - Keyword-only parameters are **not supported** yet: - - ```mojo - fn baz[*args: Int, b: Int](): pass # fails - fn baz[a: Int, *, b: Int](): pass # fails - ``` - - (The analogous keyword-only arguments in Python are described in - [PEP 3102](https://peps.python.org/pep-3102/).) - - - Variadic keyword parameters are **not supported** yet: - - ```mojo - fn baz[a: Int, **kwargs: Int](): pass # fails - ``` - -- Mojo now supports "automatic" parameterization of functions. What this means - is that if a function argument type is parametric but has no bound parameters, - they are automatically added as input parameters on the function. This works - with existing features to allow you to write parametric functions with less - boilerplate. - - ```mojo - @value - struct Thing[x: Int, y: Int]: - pass - - fn foo(v: Thing): - print(v.x) - print(v.y) - - fn main(): - let v = Thing[2, 3]() - foo(v) - ``` - - However, partial autoparameterization is **not supported** yet: - - ```mojo - fn foo(v: Thing[y=7]): # Partially bound type not allowed yet. - ... - ``` - -- Keyword argument passing is supported when invoking `__getitem__` using - the bracket syntax: - - ```mojo - @value - struct MyStruct: - fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: - return x * y + z - - MyStruct()[z=7, x=3, y=5] # returns 22 - ``` - - However, keyword argument passing to `__setitem__` using the bracket syntax is - **not supported** yet: - - ```mojo - @value - struct OtherStruct: - fn __setitem__(self, x: Int, y: Int): pass - - OtherStruct()[x=1] = 4 # fails - ``` - -- Function argument input parameters can now be referenced within the signature - of the function: - - ```mojo - fn foo(x: SIMD, y: SIMD[x.type, x.size]): - pass - ``` - -- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been - simplified and improved so you can now run: - - ```mojo - import benchmark - from time import sleep - - fn sleeper(): - sleep(.01) - - fn main(): - let report = benchmark.run[sleeper]() - print(report.mean()) - ``` - - It no longer requires a capturing `fn` so can benchmark functions outside the - same scope. - - You can print a report with: - - ```mojo - report.print() - ``` - - ```plaintext - --------------------- - Benchmark Report (s) - --------------------- - Mean: 0.012314264957264957 - Total: 1.440769 - Iters: 117 - Warmup Mean: 0.0119335 - Warmup Total: 0.023866999999999999 - Warmup Iters: 2 - Fastest Mean: 0.012227958333333334 - Slowest Mean: 0.012442699999999999 - ``` - - Units for all functions default to seconds, but can be changed with: - - ```mojo - from benchmark import Unit - - report.print[Unit.ms]() - ``` - -- Mojo now supports struct parameter deduction (a.k.a. class template argument - deduction, or CTAD) for partially bound types. Struct parameter deduction is - also possible from static methods. For example: - - ```mojo - @value - struct Thing[v: Int]: pass - - struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]: - fn __init__(inout self, x: Thing[a]): - print("hello", a, b, c) - - @staticmethod - fn foo(x: Thing[a]): - print("🔥", a, b, c) - - fn main(): - _ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8' - CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' - ``` - -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and - `tofile()` methods to save and load as bytes from a file. - -- The built-in `print()` function now works on the - [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. - -- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and - [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors - that take [`DynamicVector[Int]`](/mojo/stdlib/collections/vector.html#dynamicvector) - and [`StaticIntTuple`](/mojo/stdlib/utils/index#staticinttuple) to - initialize shapes. - -- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the - `count()` and `find()` methods to enable counting the number of occurrences or - finding the offset index of a substring in a string. - -- The `String` type now has a `replace()` method which allows you to replace a - substring with another string. - -### 🦋 Changed - -- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) - moved under builtins, and no longer need to be imported. - -- Variadic arguments are now automatically projected into a `VariadicList` or - `VariadicListMem` inside the function body. This allows for more flexibility - in using var args. For example: - - ```mojo - fn print_ints(*nums: Int): - let len = len(nums) - for i in range(len): - print(nums[i]) - print(len) - ``` - -- The parameters for - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - have been switched. The parameters are now `[type, size]` instead of - `[size, type]`. The `InlinedFixedVector` now has a default size which means - that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` - and the default size is used. - -- `write_file()` method in [`Buffer`](/mojo/stdlib/memory/buffer.html#buffer) - and [`NDBuffer`](/mojo/stdlib/memory/buffer.html#ndbuffer) is renamed to - `tofile()` to match the Python naming. - -- Mojo will now utilize all available cores across all NUMA sockets on the host - machine by default. The prior default behavior was to use all the cores on - the first socket. - -### ❌ Removed - -- The `math.numerics` module is now private, because its types (`FPUtils` and - `FlushDenormals`) should not be used externally. - -### 🛠️ Fixed - -- [#532](https://github.com/modularml/mojo/issues/532) - Compiler optimizing - while True loop away -- [#760](https://github.com/modularml/mojo/issues/760) - Compilation error: - 'hlcf.for.yield' op specifies 0 branch inputs but target expected 1 along - control-flow edge from here -- [#849](https://github.com/modularml/mojo/issues/849) - The `Tensor` type is - now initialized with zeros at construction time. -- [#912](https://github.com/modularml/mojo/issues/912) - Invalid load for - `__get_address_as_lvalue`. -- [#916](https://github.com/modularml/mojo/issues/916) - Parser crash when - specifying default values for `inout` arguments. -- [#943](https://github.com/modularml/mojo/issues/943) - Mojo hangs if you - use continue in the nested loop -- [#957](https://github.com/modularml/mojo/issues/957) - Parser crash when a - function call with variadic arguments of a memory-only type is evaluated at - compile time. -- [#990](https://github.com/modularml/mojo/issues/990) - Fixes rounding - issue with floor division with negative numerator. -- [#1018](https://github.com/modularml/mojo/issues/1018) - In some cases the - sort function was returning invalid results. This release fixes some of these - corner cases. -- [#1010](https://github.com/modularml/mojo/issues/1010) - Initializing tensor - in alias declaration results in crash. -- [#1110](https://github.com/modularml/mojo/issues/1110) - The `time.now()` - function now returns nanoseconds across all operating systems. -- [#1115](https://github.com/modularml/mojo/issues/1115) - cannot load - non-register passable type into SSA register. - -## v0.4.0 for Mac (2023-10-19) - -### 🔥 Legendary - -- Mojo for Mac! - - The Mojo SDK now works on macOS (Apple silicon). This is the same version - previously released for Linux. Get the latest version of the SDK for your Mac - system: - - [Download Now!](https://developer.modular.com/download) - -## v0.4.0 (2023-10-05) - -### ⭐️ New - -- Mojo now supports default parameter values. For example: - - ```mojo - fn foo[a: Int = 3, msg: StringLiteral = "woof"](): - print(msg, a) - - fn main(): - foo() # prints 'woof 3' - foo[5]() # prints 'woof 5' - foo[7, "meow"]() # prints 'meow 7' - ``` - - Inferred parameter values take precedence over defaults: - - ```mojo - @value - struct Bar[v: Int]: - pass - - fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]): - print(msg, a) - - fn main(): - foo(Bar[9]()) # prints 'quack 9' - ``` - - Structs also support default parameters: - - ```mojo - @value - struct DefaultParams[msg: StringLiteral = "woof"]: - alias message = msg - - fn main(): - print(DefaultParams[]().message) # prints 'woof' - print(DefaultParams["meow"]().message) # prints 'meow' - ``` - -- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O - support. You can now write: - - ```mojo - var f = open("my_file.txt", "r") - print(f.read()) - f.close() - ``` - - or - - ```mojo - with open("my_file.txt", "r") as f: - print(f.read()) - ``` - -- Mojo now allows context managers to support an `__enter__` method without - implementing support for an `__exit__` method, enabling idioms like this: - - ```mojo - # This context manager consumes itself and returns it as the value. - fn __enter__(owned self) -> Self: - return self^ - ``` - - Here Mojo _cannot_ invoke a noop `__exit__` method because the context - manager is consumed by the `__enter__` method. This can be used for types - (like file descriptors) that are traditionally used with `with` statements, - even though Mojo's guaranteed early destruction doesn't require that. - -- A very basic version of `pathlib` has been implemented in Mojo. The - module will be improved to achieve functional parity with Python in - the next few releases. - -- The `memory.unsafe` module now contains a `bitcast` function. This is a - low-level operation that enables bitcasting between pointers and scalars. - -- The input parameters of a parametric type can now be directly accessed as - attribute references on the type or variables of the type. For example: - - ```mojo - @value - struct Thing[param: Int]: - pass - - fn main(): - print(Thing[2].param) # prints '2' - let x = Thing[9]() - print(x.param) # prints '9' - ``` - - Input parameters on values can even be accessed in parameter contexts. For - example: - - ```mojo - fn foo[value: Int](): - print(value) - - let y = Thing[12]() - alias constant = y.param + 4 - foo[constant]() # prints '16' - ``` - -- The Mojo REPL now supports code completion. Press Tab while typing - to query potential completion results. - -- Error messages from Python are now exposed in Mojo. For example the following - should print `No module named 'my_uninstalled_module'`: - - ```mojo - fn main(): - try: - let my_module = Python.import_module("my_uninstalled_module") - except e: - print(e) - ``` - -- Error messages can now store dynamic messages. For example, the following - should print "Failed on: Hello" - - ```mojo - fn foo(x:String) raises: - raise Error("Failed on: " + x) - - fn main(): - try: - foo("Hello") - except e: - print(e) - ``` - -### 🦋 Changed - -- We have improved and simplified the `parallelize` function. The function - now elides some overhead by caching the Mojo parallel runtime. - -- The Mojo REPL and Jupyter environments no longer implicitly expose `Python`, - `PythonObject`, or `Pointer`. These symbols must now be imported explicitly, - for example: - - ```mojo - from python import Python - from python.object import PythonObject - from memory.unsafe import Pointer - ``` - -- The syntax for specifying attributes with the `__mlir_op` prefix have changed - to mimic Python's keyword argument passing syntax. That is, `=` should be used - instead of `:`, e.g.: - - ```mojo - # Old syntax, now fails. - __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() - # New syntax. - __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() - ``` - -- You can now print the `Error` object directly. The `message()` method - has been removed. - -### 🛠️ Fixed - -- [#794](https://github.com/modularml/mojo/issues/794) - Parser crash when - using the `in` operator. -- [#936](https://github.com/modularml/mojo/issues/936) - The `Int` constructor - now accepts other `Int` instances. -- [#921](https://github.com/modularml/mojo/issues/921) - Better error message - when running `mojo` on a module with no `main` function. -- [#556](https://github.com/modularml/mojo/issues/556) - UInt64s are now - printed correctly. -- [#804](https://github.com/modularml/mojo/issues/804) - Emit error instead of - crashing when passing variadic arguments of unsupported types. -- [#833](https://github.com/modularml/mojo/issues/833) - Parser crash when - assigning module value. -- [#752](https://github.com/modularml/mojo/issues/752) - Parser crash when - calling async def. -- [#711](https://github.com/modularml/mojo/issues/711) - The overload resolution - logic now correctly prioritizes instance methods over static methods (if - candidates are an equally good match otherwise), and no longer crashed if a - static method has a `Self` type as its first argument. -- [#859](https://github.com/modularml/mojo/issues/859) - Fix confusing error and - documentation of the `rebind` builtin. -- [#753](https://github.com/modularml/mojo/issues/753) - Direct use of LLVM - dialect produces strange errors in the compiler. -- [#926](https://github.com/modularml/mojo/issues/926) - Fixes an issue that - occurred when a function with a return type of `StringRef` raised an error. - When the function raised an error, it incorrectly returned the string value of - that error. -- [#536](https://github.com/modularml/mojo/issues/536) - Report More information - on python exception. - -## v0.3.1 (2023-09-28) - -Our first-ever patch release of the Mojo SDK is here! Release v0.3.1 -includes primarily installation-related fixes. If you’ve had trouble -installing the previous versions of the SDK, this release may be for you. - -### 🛠️ Fixed - -- [#538](https://github.com/modularml/mojo/issues/538) - Installation hangs - during the testing phase. This issue occurs on machines with a low number - of CPU cores, such as free AWS EC2 instances and GitHub Codespaces. -- [#590](https://github.com/modularml/mojo/issues/590) - Installation fails - with a “failed to run python” message. -- [#672](https://github.com/modularml/mojo/issues/672) - Language server hangs - on code completion. Related to #538, this occurs on machines with a low - number of CPU cores. -- [#913](https://github.com/modularml/mojo/issues/913) - In the REPL and Jupyter - notebooks, inline comments were being parsed incorrectly. - -## v0.3.0 (2023-09-21) - -There's more Mojo to love in this, the second release of the Mojo SDK! This -release includes new features, an API change, and bug fixes. - -There's also an updated version of the [Mojo extension for VS -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - -### ⭐️ New - -- Mojo now has partial support for passing keyword arguments to functions and - methods. For example the following should work: - - ```mojo - fn foo(a: Int, b: Int = 3) -> Int: - return a * b - - fn main(): - print(foo(6, b=7)) # prints '42' - print(foo(a=6, b=7)) # prints '42' - print(foo(b=7, a=6)) # prints '42' - ``` - - Parameters can also be inferred from keyword arguments, for example: - - ```mojo - fn bar[A: AnyType, B: AnyType](a: A, b: B): - print("Hello 🔥") - - fn bar[B: AnyType](a: StringLiteral, b: B): - print(a) - - fn main(): - bar(1, 2) # prints `Hello 🔥` - bar(b=2, a="Yay!") # prints `Yay!` - ``` - - For the time being, the following notable limitations apply: - - - Keyword-only arguments are not supported: - - ```mojo - fn baz(*args: Int, b: Int): pass # fails - fn baz(a: Int, *, b: Int): pass # fails - ``` - - (Keyword-only arguments are described in - [PEP 3102](https://peps.python.org/pep-3102/).) - - - Variadic keyword arguments are not supported: - - ```mojo - fn baz(a: Int, **kwargs: Int): pass # fails - ``` - -- Mojo now supports the `@nonmaterializable` decorator. The purpose is to mark - data types that should only exist in the parameter domain. To use it, a - struct is decorated with `@nonmaterializable(TargetType)`. Any time the - nonmaterializable type is converted from the parameter domain, it is - automatically converted to `TargetType`. A nonmaterializable struct should - have all of its methods annotated as `@always_inline`, and must be computable - in the parameter domain. In the following example, the `NmStruct` type can - be added in the parameter domain, but are converted to `HasBool` when - materialized. - - ```mojo - @value - @register_passable("trivial") - struct HasBool: - var x: Bool - fn __init__(x: Bool) -> Self: - return Self {x: x} - @always_inline("nodebug") - fn __init__(nms: NmStruct) -> Self: - return Self {x: True if (nms.x == 77) else False} - - @value - @nonmaterializable(HasBool) - @register_passable("trivial") - struct NmStruct: - var x: Int - @always_inline("nodebug") - fn __add__(self: Self, rhs: Self) -> Self: - return NmStruct(self.x + rhs.x) - - alias stillNmStruct = NmStruct(1) + NmStruct(2) - # When materializing to a run-time variable, it is automatically converted, - # even without a type annotation. - let convertedToHasBool = stillNmStruct - ``` - -- Mojo integer literals now produce the `IntLiteral` infinite precision integer - type when used in the parameter domain. `IntLiteral` is materialized to the - `Int` type for runtime computation, but intermediate computations at compile - time, using supported operators, can now exceed the bit width of the `Int` - type. - -- The Mojo Language Server now supports top-level code completions, enabling - completion when typing a reference to a variable, type, etc. This resolves - [#679](https://github.com/modularml/mojo/issues/679). - -- The Mojo REPL now colorizes the resultant variables to help distinguish input - expressions from the output variables. - -### 🦋 Changed - -- Mojo allows types to implement two forms of move constructors, one that is - invoked when the lifetime of one value ends, and one that is invoked if the - compiler cannot prove that. These were previously both named `__moveinit__`, - with the following two signatures: - - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __moveinit__(inout self, inout existing: Self): ... - ``` - - We've changed the second form to get its own name to make it more clear that - these are two separate operations: the second has been renamed to - `__takeinit__`: - - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __takeinit__(inout self, inout existing: Self): ... - ``` - - The name is intended to connote that the operation takes the conceptual value - from the source (without destroying it) unlike the first one which "moves" a - value from one location to another. - - For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). - -- The Error type in Mojo has changed. Instead of extracting the error message - using `error.value` you will now extract the error message using - `error.message()`. - -### 🛠️ Fixed - -- [#503](https://github.com/modularml/mojo/issues/503) - Improve error message - for failure lowering `kgen.param.constant`. -- [#554](https://github.com/modularml/mojo/issues/554) - Alias of static tuple - fails to expand. -- [#500](https://github.com/modularml/mojo/issues/500) - Call expansion failed - due to verifier error. -- [#422](https://github.com/modularml/mojo/issues/422) - Incorrect comment - detection in multiline strings. -- [#729](https://github.com/modularml/mojo/issues/740) - Improve messaging on - how to exit the REPL. -- [#756](https://github.com/modularml/mojo/issues/756) - Fix initialization - errors of the VS Code extension. -- [#575](https://github.com/modularml/mojo/issues/575) - Build LLDB/REPL with - libedit for a nicer editing experience in the terminal. - -## v0.2.1 (2023-09-07) - -The first versioned release of Mojo! 🔥 - -All earlier releases were considered version 0.1. - -### 🔥 Legendary - -- First release of the Mojo SDK! - - You can now develop with Mojo locally. The Mojo SDK is currently available - for Ubuntu Linux systems, and support for Windows and macOS is coming soon. - You can still develop from a Windows or Mac computer using a container or - remote Linux system. - - The Mojo SDK includes the Mojo standard library and the [Mojo command-line - interface](/mojo/cli/) (CLI), which allows you to run, compile, and package - Mojo code. It also provides a REPL programming environment. - - [Get the Mojo SDK!](https://developer.modular.com/download) - -- First release of the [Mojo extension for VS - Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - - This provides essential Mojo language features in Visual Studio Code, such as - code completion, code quick fixes, docs tooltips, and more. Even when - developing on a remote system, using VS Code with this extension provides - a native-like IDE experience. - -### ⭐️ New - -- A new `clobber_memory` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. - The clobber memory function tells the system to flush all memory operations - at the specified program point. This allows you to benchmark operations - without the compiler reordering memory operations. - -- A new `keep` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` - function tries to tell the compiler not to optimize the variable away - if not used. This allows you to avoid compiler's dead code elimination - mechanism, with a low footprint side effect. - -- New `shift_right` and `shift_left` functions have been added to the - [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in - a SIMD vector right/left, filling elements with zeros as needed. - -- A new `cumsum` function has been added to the - [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes - the cumulative sum (also known as scan) of input elements. - -- Mojo Jupyter kernel now supports code completion. - -### 🦋 Changed - -- Extends `rotate_bits_left`, `rotate_left`, `rotate_bits_right`, and - `rotate_right` to operate on Int values. The ordering of parameters has also - been changed to enable type inference. Now it's possible to write - `rotate_right[shift_val](simd_val)` and have the `dtype` and `simd_width` - inferred from the argument. This addresses - [Issue #528](https://github.com/modularml/mojo/issues/528). - -### 🛠️ Fixed - -- Fixed a bug causing the parser to crash when the `with` statement was written - without a colon. - This addresses [Issue #529](https://github.com/modularml/mojo/issues/529). - -- Incorrect imports no longer crash when there are other errors at the top - level of a module. This fixes [Issue - #531](https://github.com/modularml/mojo/issues/531). - -## August 2023 - -### 2023-08-24 - -- Fixed issue where the `with expr as x` statement within `fn` behaved - as if it were in a `def`, binding `x` with function scope instead of using - lexical scope. - -#### ⭐️ New - -- Major refactoring of the standard library to enable packaging and better - import ergonomics: - - The packages are built as binaries to improve startup speed. - - Package and module names are now lowercase to align with the Python style. - - Modules have been moved to better reflect the purpose of the underlying - functions (e.g. `Pointer` is now within the `unsafe` module in the `memory` - package). - - The following modules are now included as built-ins: - `SIMD`, `DType`, `IO`, `Object`, and `String`. - This means it's no longer necessary to explicitly import these modules. - Instead, these modules will be implicitly imported for the user. Private - methods within the module are still accessible using the - `builtin.module_name._private_method` import syntax. - - New `math` package has been added to contain the `bit`, `math`, `numerics`, - and `polynomial` modules. The contents of the `math.math` module are - re-exported into the `math` package. - -- Mojo now supports using memory-only types in parameter expressions and as - function or type parameters: - - ```mojo - @value - struct IntPair: - var first: Int - var second: Int - - fn add_them[value: IntPair]() -> Int: - return value.first + value.second - - fn main(): - print(add_them[IntPair(1, 2)]()) # prints '3' - ``` - -- In addition, Mojo supports evaluating code that uses heap-allocated memory - at compile-time and materializing compile-time values with heap-allocated - memory into dynamic values: - - ```mojo - fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: - var result = DynamicVector[Int]() - for i in range(lowerBound, upperBound, step): - result.push_back(i) - return result - - fn main(): - alias values = fillVector(5, 23, 7) - for i in range(0, values.__len__()): - print(values[i]) # prints '5', '12', and then '19' - ``` - -#### 🦋 Changed - -- `def main():`, without the explicit `None` type, can now be used to define - the entry point to a Mojo program. - -- The `assert_param` function has been renamed to `constrained` and is now - a built-in function. - -- The `print` function now works on `Complex` values. - -#### 🛠️ Fixed - -- Fixed issues with print formatting for `DType.uint16` and `DType.int16`. -- [Issue #499](https://github.com/modularml/mojo/issues/499) - Two new - `rotate_right` and `rotate_left` functions have been added to the SIMD module. -- [Issue #429](https://github.com/modularml/mojo/issues/429) - You can now - construct a `Bool` from a `SIMD` type whose element-type is `DType.bool`. -- [Issue #350](https://github.com/modularml/mojo/issues/350) - Confusing Matrix - implementation -- [Issue #349](https://github.com/modularml/mojo/issues/349) - Missing load_tr - in struct Matrix -- [Issue #501](https://github.com/modularml/mojo/issues/501) - Missing syntax - error messages in Python expressions. - -### 2023-08-09 - -#### 🦋 Changed - -- The `ref` and `mutref` identifiers are now treated as keywords, which means - they cannot be used as variable, attribute, or function names. These keywords - are used by the "lifetimes" features, which is still in development. We can - consider renaming these (as well as other related keywords) when the - development work gels, support is enabled in public Mojo builds, and when we - have experience using them. - -- The argument handling in `def` functions has changed: previously, they had - special behavior that involved mutable copies in the callee. Now, we have a - simple rule, which is that `def` argument default to the `owned` convention - (`fn` arguments still default to the `borrowed` convention). - - This change is mostly an internal cleanup and simplification of the compiler - and argument model, but does enable one niche use-case: you can now pass - non-copyable types to `def` arguments by transferring ownership of a value - into the `def` call. Before, that would not be possible because the copy was - made on the callee side, not the caller's side. This also allows the explicit - use of the `borrowed` keyword with a `def` that wants to opt-in to that - behavior. - -### 2023-08-03 - -#### ⭐️ New - -- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. - This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which - are just views). Therefore, the tensor type performs its own allocation and - free. Here is a simple example of using the tensor type to represent an RGB - image and convert it to grayscale: - - ```mojo - from tensor import Tensor, TensorShape - from utils.index import Index - from random import rand - - let height = 256 - let width = 256 - let channels = 3 - - # Create the tensor of dimensions height, width, channels and fill with - # random value. - let image = rand[DType.float32](height, width, channels) - - # Declare the grayscale image. - var gray_scale_image = Tensor[DType.float32](height, width) - - # Perform the RGB to grayscale transform. - for y in range(height): - for x in range(width): - let r = image[y,x,0] - let g = image[y,x,1] - let b = image[y,x,2] - gray_scale_image[Index(y,x)] = 0.299 * r + 0.587 * g + 0.114 * b - ``` - -#### 🛠️ Fixed - -- [Issue #53](https://github.com/modularml/mojo/issues/53) - `Int` now - implements true division with the `/` operator. Similar to Python, this - returns a 64-bit floating point number. The corresponding in-place operator, - `/=`, has the same semantics as `//=`. - -## July 2023 - -### 2023-07-26 - -#### ⭐️ New - -- Types that define both `__getitem__` and `__setitem__` (i.e. where - sub-scripting instances creates computed LValues) can now be indexed - in parameter expressions. - -- Unroll decorator for loops with constant bounds and steps: - - `@unroll`: Fully unroll a loop. - - `@unroll(n)`: Unroll a loop by factor of n, where `n` is a positive integer. - - Unroll decorator requires loop bounds and iteration step to be - compiler time constant value, otherwise unrolling will fail with - compilation error. This also doesn't make loop induction variable a parameter. - - ```mojo - # Fully unroll the loop. - @unroll - for i in range(5): - print(i) - - # Unroll the loop by a factor of 4 (with remainder iterations of 2). - @unroll(4) - for i in range(10): - print(i) - ``` - -- The Mojo REPL now prints the values of variables defined in the REPL. There is - full support for scalars and structs. Non-scalar SIMD vectors are not - supported at this time. - -#### 🛠️ Fixed - -- [Issue #437](https://github.com/modularml/mojo/issues/437) - Range can now - be instantiated with a PythonObject. - -- [Issue #288](https://github.com/modularml/mojo/issues/288) - Python strings - can now be safely copied. - -### 2023-07-20 - -#### ⭐️ New - -- Mojo now includes a `Limits` module, which contains functions to get the max - and min values representable by a type, as requested in [Issue - #51](https://github.com/modularml/mojo/issues/51). The following functions - moved from `Math` to `Limits`: `inf()`, `neginf()`, `isinf()`, `isfinite()`. - -- Mojo decorators are now distinguished between "signature" and "body" - decorators and are ordered. Signature decorators, like `@register_passable` - and `@parameter`, modify the type of declaration before the body is parsed. - Body decorators, like `@value`, modify the body of declaration after it is - fully parsed. Due to ordering, a signature decorator cannot be applied after - a body decorator. That means the following is now invalid: - - ```mojo - @register_passable # error: cannot apply signature decorator after a body one! - @value - struct Foo: - pass - ``` - -- Global variables can now be exported in Mojo compiled archives, using the - `@export` decorator. Exported global variables are public symbols in compiled - archives and use the variable name as its linkage name, by default. A custom - linkage name can be specified with `@export("new_name")`. This does not affect - variable names in Mojo code. - -- Mojo now supports packages! A Mojo package is defined by placing an - `__init__.mojo` or `__init__.🔥` within a directory. Other files in the same - directory form modules within the package (this works exactly like it - does [in Python](https://docs.python.org/3/tutorial/modules.html#packages)). - Example: - - ```bash - main.🔥 - my_package/ - __init__.🔥 - module.🔥 - my_other_package/ - __init__.🔥 - stuff.🔥 - ``` - - ```mojo - # main.🔥 - from my_package.module import some_function - from my_package.my_other_package.stuff import SomeType - - fn main(): - var x: SomeType = some_function() - ``` - -- Mojo now supports direct module and package imports! Modules and packages can - be imported and bound to names. Module and package elements, like functions, - types, global variables, and other modules, can be accessed using attribute - references, like `my_module.foo`. Note that modules lack runtime - representations, meaning module references cannot be instantiated. - - ```mojo - import builtin.io as io - import SIMD - - io.print("hello world") - var x: SIMD.Float32 = 1.2 - ``` - -#### 🦋 Changed - -- Reverted the feature from 2023-02-13 that allowed unqualified struct members. - Use the `Self` keyword to conveniently access struct members with bound - parameters instead. This was required to fix - [Issue #260](https://github.com/modularml/mojo/issues/260). - -- Updated the RayTracing notebook: added step 5 to create specular lighting for - more realistic images and step 6 to add a background image. - -#### 🛠️ Fixed - -- [Issue #260](https://github.com/modularml/mojo/issues/260) - Definitions - inside structs no longer shadow definitions outside of struct definitions. - -### 2023-07-12 - -#### ⭐️ New - -- Mojo now has support for global variables! This enables `var` and `let` - declaration at the top-level scope in Mojo files. Global variable initializers - are run when code modules are loaded by the platform according to the order of - dependencies between global variables, and their destructors are called in the - reverse order. - -- The [Mojo programming manual](/mojo/programming-manual.html) is now written - as a Jupyter notebook, and available in its entirety in the Mojo Playground - (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of - the same material, but it was not up-to-date.) - -- As a result, we've also re-written `HelloMojo.ipynb` to be much shorter and - provide a more gentle first-user experience. - -- [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now - available. Coroutines form the basis of Mojo's support for asynchronous - execution. Calls to `async fn`s can be stored into a `Coroutine`, from which - they can be resumed, awaited upon, and have their results retrieved upon - completion. - -#### 🦋 Changed - -- `simd_bit_width` in the `TargetInfo` module has been renamed to `simdbitwidth` - to better align with `simdwidthof`, `bitwidthof`, etc. - -#### 🛠️ Fixed - -- The walrus operator now works in if/while statements without parentheses, - e.g. `if x := function():`. - -- [Issue #428](https://github.com/modularml/mojo/issues/428) - The - `FloatLiteral` and `SIMD` types now support conversion to `Int` via the - `to_int` or `__int__` method calls. The behavior matches that of Python, which - rounds towards zero. - -### 2023-07-05 - -#### ⭐️ New - -- Tuple expressions now work without parentheses. For example, `a, b = b, a` - works as you'd expect in Python. -- Chained assignments (e.g. `a = b = 42`) and the walrus operator (e.g. - `some_function(b := 17)`) are now supported. - -#### 🦋 Changed - -- The `simd_width` and `dtype_simd_width` functions in the - [`TargetInfo`](/mojo/stdlib/sys/info) module - have been renamed to `simdwidthof`. - -- The `dtype_` prefix has been dropped from `alignof`, `sizeof`, and - `bitwidthof`. You can now use these functions (e.g. `alignof`) with any - argument type, including `DType`. - -- The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were - moved from the `Numerics` module to the - [`Math`](/mojo/stdlib/math/math) module, to better align with Python's - library structure. - -#### 🛠️ Fixed - -- [Issue #253](https://github.com/modularml/mojo/issues/253) - Issue - when accessing a struct member alias without providing parameters. - -- [Issue #404](https://github.com/modularml/mojo/issues/404) - The docs now use - `snake_case` for variable names, which more closely conforms to Python's - style. - -- [Issue #379](https://github.com/modularml/mojo/issues/379) - Tuple - limitations have been addressed and multiple return values are now supported, - even without parentheses. - -- [Issue #347](https://github.com/modularml/mojo/issues/347) - Tuples no longer - require parentheses. - -- [Issue #320](https://github.com/modularml/mojo/issues/320) - Python objects - are now traversable via `for` loops. - -## June 2023 - -### 2023-06-29 - -#### ⭐️ New - -- You can now share `.ipynb` notebook files in Mojo Playground. Just save a - file in the `shared` directory, and then right-click the file and select - **Copy Sharable link**. To open a shared notebook, you must already have - [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); - when you open a shared notebook, click **Import** at the top of the notebook - to save your own copy. For more details about this feature, see the - instructions inside the `help` directory, in the Mojo Playground file browser. - -#### 🦋 Changed - -- The `unroll2()` and `unroll3()` functions in the - [`Functional`](/mojo/stdlib/algorithm/functional) module have been renamed to - overload the `unroll()` function. These functions unroll 2D and 3D loops and - `unroll()` can determine the intent based on the number of input parameters. - -#### 🛠️ Fixed - -- [Issue #229](https://github.com/modularml/mojo/issues/229) - Issue when - throwing an exception from `__init__` before all fields are initialized. - -- [Issue #74](https://github.com/modularml/mojo/issues/74) - Struct - definition with recursive reference crashes. - -- [Issue #285](https://github.com/modularml/mojo/issues/285) - The - [`TargetInfo`](/mojo/stdlib/sys/info) module now includes - `is_little_endian()` and `is_big_endian()` to check if the target host uses - either little or big endian. - -- [Issue #254](https://github.com/modularml/mojo/issues/254) - Parameter name - shadowing in nested scopes is now handled correctly. - -### 2023-06-21 - -#### ⭐️ New - -- Added support for overloading on parameter signature. For example, it is now -possible to write the following: - - ```mojo - fn foo[a: Int](x: Int): - pass - - fn foo[a: Int, b: Int](x: Int): - pass - ``` - - For details on the overload resolution logic, see the Mojo Manual section on - [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). - -- A new `cost_of()` function has been added to `Autotune`. This meta-function - must be invoked at compile time, and it returns the number of MLIR operations - in a function (at a certain stage in compilation), which can be used to - build basic heuristics in higher-order generators. - - ```mojo - from autotune import cost_of - - fn generator[f: fn(Int) -> Int]() -> Int: - @parameter - if cost_of[fn(Int) -> Int, f]() < 10: - return f() - else: - # Do something else for slower functions... - ``` - -- Added a new example notebook with a basic Ray Tracing algorithm. - -#### 🦋 Changed - -- The `constrained_msg()` in the `Assert` module has been renamed to - `constrained()`. - -#### 🛠️ Fixed - -- Overloads marked with `@adaptive` now correctly handle signatures that differ -only in declared parameter names, e.g. the following now works correctly: - - ```mojo - @adaptive - fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... - - @adaptive - fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ... - ``` - -- [Issue #219](https://github.com/modularml/mojo/issues/219) - Issue when - redefining a function and a struct defined in the same cell. - -- [Issue #355](https://github.com/modularml/mojo/issues/355) - The loop order - in the Matmul notebook for Python and naive mojo have been reordered for - consistency. The loop order now follows (M, K, N) ordering. - -- [Issue #309](https://github.com/modularml/mojo/issues/309) - Use snake case - naming within the testing package and move the asserts out of the TestSuite - struct. - -### 2023-06-14 - -#### ⭐️ New - -- Tuple type syntax is now supported, e.g. the following works: - - ```mojo - fn return_tuple() -> (Int, Int): - return (1, 2) - ``` - -#### 🦋 Changed - -- The `TupleLiteral` type was renamed to just `Tuple`, e.g. - `Tuple[Int, Float]`. - -#### 🛠️ Fixed - -- [Issue #354](https://github.com/modularml/mojo/issues/354) - Returning a tuple - doesn't work even with parens. -- [Issue #365](https://github.com/modularml/mojo/issues/365) - Copy-paste error - in `FloatLiteral` docs. -- [Issue #357](https://github.com/modularml/mojo/issues/357) - Crash when - missing input parameter to variadic parameter struct member function. - -### 2023-06-07 - -#### ⭐️ New - -- Tuple syntax now works on the left-hand side of assignments (in "lvalue" - positions), enabling things like `(a, b) = (b, a)`. There are several - caveats: the element types must exactly match (no implicit conversions), - this only works with values of `TupleLiteral` type (notably, it will not work - with `PythonObject` yet) and parentheses are required for tuple syntax. - -#### ❌ Removed - -- Mojo Playground no longer includes the following Python packages (due to size, - compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): - `torch`, `tensorflow`, `keras`, `transformers`. - -#### 🦋 Changed - -- The data types and scalar names now conform to the naming convention used - by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` - instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). - -#### 🛠️ Fixed - -- [Issue #287](https://github.com/modularml/mojo/issues/287) - computed - lvalues don't handle raising functions correctly -- [Issue #318](https://github.com/modularml/mojo/issues/318) - Large integers - are not being printed correctly -- [Issue #326](https://github.com/modularml/mojo/issues/326) - Float modulo - operator is not working as expected -- [Issue #282](https://github.com/modularml/mojo/issues/282) - Default arguments - are not working as expected -- [Issue #271](https://github.com/modularml/mojo/issues/271) - Confusing error - message when converting between function types with different result semantics - -## May 2023 - -### 2023-05-31 - -#### ⭐️ New - -- Mojo Playground now includes the following Python packages (in response to - [popular demand](https://github.com/modularml/mojo/discussions/173)): - `torch`, `tensorflow`, `polars`, `opencv-python`, `keras`, `Pillow`, `plotly`, - `seaborn`, `sympy`, `transformers`. - -- A new optimization is applied to non-trivial copyable values that are passed - as an owned value without using the transfer (`^`) operator. Consider code - like this: - - ```mojo - var someValue : T = ... - ... - takeValueAsOwned(someValue) - ... - ``` - - When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) - value (this is - common in initializers for example), it is allowed to do whatever it wants - with the value and destroy it when it is finished. In order to support this, - the Mojo compiler is forced to make a temporary copy of the `someValue` - value, and pass that value instead of `someValue`, because there may be other - uses of `someValue` after the call. - - The Mojo compiler is now smart enough to detect when there are no uses of - `someValue` later, and it will elide the copy just as if you had manually - specified the transfer operator like `takeValueAsOwned(someValue^)`. This - provides a nice "it just works" behavior for non-trivial types without - requiring manual management of transfers. - - If you'd like to take full control and expose full ownership for your type, - just don't make it copyable. Move-only types require the explicit transfer - operator so you can see in your code where all ownership transfer happen. - -- Similarly, the Mojo compiler now transforms calls to `__copyinit__` methods - into calls to `__moveinit__` when that is the last use of the source value - along a control flow path. This allows types which are both copyable and - movable to get transparent move optimization. For example, the following code - is compiled into moves instead of copies even without the use of the transfer - operator: - - ```mojo - var someValue = somethingCopyableAndMovable() - use(someValue) - ... - let otherValue = someValue # Last use of someValue - use(otherValue) - ... - var yetAnother = otherValue # Last use of otherValue - mutate(yetAnother) - ``` - - This is a significant performance optimization for things like `PythonObject` - (and more complex value semantic types) that are commonly used in a fluid - programming style. These don't want extraneous reference counting operations - performed by its copy constructor. - - If you want explicit control over copying, it is recommended to use a - non-dunder `.copy()` method instead of `__copyinit__`, and recall that - non-copyable types must always use of the transfer operator for those that - want fully explicit behavior. - -#### 🛠️ Fixed - -- [Issue #231](https://github.com/modularml/mojo/issues/231) - Unexpected error - when a Python expression raises an exception -- [Issue #119](https://github.com/modularml/mojo/issues/119) - The REPL fails - when a python variable is redefined - -### 2023-05-24 - -#### ⭐️ New - -- `finally` clauses are now supported on `try` statements. In addition, `try` - statements no longer require `except` clauses, allowing `try-finally` blocks. - `finally` clauses contain code that is always executed from control-flow - leaves any of the other clauses of a `try` statement by any means. - -#### 🦋 Changed - -- `with` statement emission changed to use the new `finally` logic so that - - ```mojo - with ContextMgr(): - return - ``` - - Will correctly execute `ContextMgr.__exit__` before returning. - -#### 🛠️ Fixed - -- [Issue #204](https://github.com/modularml/mojo/issues/204) - Mojo REPL - crash when returning a String at compile-time -- [Issue #143](https://github.com/modularml/mojo/issues/143) - synthesized - init in `@register_passable` type doesn't get correct convention. -- [Issue #201](https://github.com/modularml/mojo/issues/201) - String literal - concatenation is too eager. -- [Issue #209](https://github.com/modularml/mojo/issues/209) - [QoI] Terrible - error message trying to convert a type to itself. -- [Issue #32](https://github.com/modularml/mojo/issues/32) - Include struct - fields in docgen -- [Issue #50](https://github.com/modularml/mojo/issues/50) - Int to string - conversion crashes due to buffer overflow -- [Issue #132](https://github.com/modularml/mojo/issues/132) - PythonObject - `to_int` method has a misleading name -- [Issue #189](https://github.com/modularml/mojo/issues/189) - PythonObject bool - conversion is incorrect -- [Issue #65](https://github.com/modularml/mojo/issues/65) - Add SIMD - constructor from Bool -- [Issue #153](https://github.com/modularml/mojo/issues/153) - Meaning of - `Time.now` function result is unclear -- [Issue #165](https://github.com/modularml/mojo/issues/165) - Type in - `Pointer.free` documentation -- [Issue #210](https://github.com/modularml/mojo/issues/210) - Parameter results - cannot be declared outside top-level in function -- [Issue #214](https://github.com/modularml/mojo/issues/214) - Pointer offset - calculations at compile-time are incorrect -- [Issue #115](https://github.com/modularml/mojo/issues/115) - Float printing - does not include the right number of digits -- [Issue #202](https://github.com/modularml/mojo/issues/202) - - `kgen.unreachable` inside nested functions is illegal -- [Issue #235](https://github.com/modularml/mojo/issues/235) - Crash when - register passable struct field is not register passable -- [Issue #237](https://github.com/modularml/mojo/issues/237) - Parameter - closure sharp edges are not documented - -### 2023-05-16 - -#### ⭐️ New - -- Added missing dunder methods to `PythonObject`, enabling the use of common - arithmetic and logical operators on imported Python values. - -- `PythonObject` is now printable from Mojo, instead of requiring you to import -Python's print function. - -#### 🛠️ Fixed - -- [Issue #98](https://github.com/modularml/mojo/issues/98): - Incorrect error with lifetime tracking in loop. - -- [Issue #49](https://github.com/modularml/mojo/issues/49): Type inference - issue (?) in 'ternary assignment' operation (FloatLiteral vs. 'SIMD[f32, 1]'). - -- [Issue #48](https://github.com/modularml/mojo/issues/48): - and/or don't work with memory-only types. - -- [Issue #11](https://github.com/modularml/mojo/issues/11): `setitem` Support - for `PythonObject`. - -### 2023-05-11 - -#### ⭐️ New - -- `NDBuffer` and `Buffer` are now constructable via `Pointer` and - `DTypePointer`. - -- `String` now supports indexing with either integers or slices. - -- Added factorial function to the `Math` module. - -#### 🦋 Changed - -- The "byref" syntax with the `&` sigil has changed to use an `inout` - keyword to be more similar to the `borrowed` and `owned` syntax in arguments. - Please see [Issue #7](https://github.com/modularml/mojo/issues/7) for more - information. - -- Optimized the Matrix multiplication implementation in the notebook. - Initially we were optimizing for expandability rather than performance. We - have found a way to get the best of both worlds and now the performance of the - optimized Matmul implementation is 3x faster. - -- Renamed the [`^` postfix -operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) -from "consume" to "transfer." - -#### 🛠️ Fixed - -- Fixed missing overloads for `Testing.assertEqual` so that they work on -`Integer` and `String` values. - -- [Issue #6](https://github.com/modularml/mojo/issues/6): -Playground stops evaluating cells when a simple generic is defined. - -- [Issue #18](https://github.com/modularml/mojo/issues/18): -Memory leak in Python interoperability was removed. - -### 2023-05-02 - -#### 📢 Released - -- Mojo publicly launched! This was epic, with lots of great coverage online -including a [wonderful post by Jeremy -Howard](https://www.fast.ai/posts/2023-05-03-mojo-launch.html). The team is -busy this week. - -#### ⭐️ New - -- Added a Base64 encoding function to perform base64 encoding on strings. - -#### 🦋 Changed - -- Decreased memory usage of serialization of integers to strings. - -- Speedup the sort function. - -#### 🛠️ Fixed - -- Fixed time unit in the `sleep` function. - -## April 2023 - -### Week of 2023-04-24 - -- 📢 The default behavior of nested functions has been changed. Mojo nested - functions that capture are by default are non-parametric, runtime closures, - meaning that: - - ```mojo - def foo(x): - # This: - def bar(y): return x*y - # Is the same as: - let bar = lambda y: x*y - ``` - - These closures cannot have input or result parameters, because they are always - materialized as runtime values. Values captured in the closure (`x` in the - above example), are captured by copy: values with copy constructors cannot be - copied and captures are immutable in the closure. - - Nested functions that don't capture anything are by default "parametric" - closures: they can have parameters and they can be used as parameter values. - To restore the previous behavior for capturing closures, "parametric, - capture-by-unsafe-reference closures", tag the nested function with the - `@parameter` decorator. - -- 📢 Mojo now has full support for "runtime" closures: nested functions that - capture state materialized as runtime values. This includes taking the address - of functions, indirect calls, and passing closures around through function - arguments. Note that capture-by-reference is still unsafe! - - You can also take references to member functions with instances of that class - using `foo.member_function`, which creates a closure with `foo` bound to the - `self` argument. - -- 📢 Mojo now supports Python style `with` statements and context managers. - - These things are very helpful for implementing things like our - trace region support and things like Runtime support. - - A context manager in Mojo implements three methods: - - ```mojo - fn __enter__(self) -> T: - fn __exit__(self): - fn __exit__(self, err: Error) -> Bool: - ``` - - The first is invoked when the context is entered, and returns a - value that may optionally be bound to a target for use in the with - body. If the with block exits normally, the second method is - invoked to clean it up. If an error is raised, the third method - is invoked with the Error value. If that method returns true, the - error is considered handled, if it returns false, the error is - re-thrown so propagation continues out of the 'with' block. - -- 📢 Mojo functions now support variable scopes! Explicit `var` and `let` - declarations inside functions can shadow declarations from higher "scopes", - where a scope is defined as any new indentation block. In addition, the - `for` loop iteration variable is now scoped to the loop body, so it is - finally possible to write - - ```mojo - for i in range(1): pass - for i in range(2): pass - ``` - -- 📢 Mojo now supports an `@value` decorator on structs to reduce boilerplate - and encourage best practices in value semantics. The `@value` decorator looks - to see the struct has a memberwise initializer (which has arguments for each - field of the struct), a `__copyinit__` method, and a `__moveinit__` method, - and synthesizes the missing ones if possible. For example, if you write: - - ```mojo - @value - struct MyPet: - var name: String - var age: Int - ``` - - The `@value` decorator will synthesize the following members for you: - - ```mojo - fn __init__(inout self, owned name: String, age: Int): - self.name = name^ - self.age = age - fn __copyinit__(inout self, existing: Self): - self.name = existing.name - self.age = existing.age - fn __moveinit__(inout self, owned existing: Self): - self.name = existing.name^ - self.age = existing.age - ``` - - This decorator can greatly reduce the boilerplate needed to define common - aggregates, and gives you best practices in ownership management - automatically. The `@value` decorator can be used with types that need custom - copy constructors (your definition wins). We can explore having the decorator - take arguments to further customize its behavior in the future. - -- 📚 Memcpy and memcmp now consistently use count as the byte count. - -- 📚 Add a variadic sting join on strings. - -- 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all - elements in a SIMD vector. - -- 📚 Optimize the `pow` function if the exponent is integral. - -- 📚 Add a `len` function which dispatches to `__len__` across the different - structs that support it. - -### Week of 2023-04-17 - -- 📢 Error messages have been significantly improved, thanks to prettier - printing for Mojo types in diagnostics. - -- 📢 Variadic values can now be indexed directly without wrapping them in a - `VariadicList`! - -- 📢 `let` declarations in a function can now be lazily initialized, and `var` - declarations that are never mutated get a warning suggesting they be converted - to a `let` declaration. Lazy initialization allows more flexible patterns of - initialization than requiring the initializer be inline, e.g.: - - ```mojo - let x : Int - if cond: - x = foo() - else: - x = bar() - use(x) - ``` - -- 📢 Functions defined with `def` now return `object` by default, instead of - `None`. This means you can return values (convertible to `object`) inside - `def` functions without specifying a return type. - -- 📢 The `@raises` decorator has been removed. Raising `fn` should be declared - by specifying `raises` after the function argument list. The rationale is that - `raises` is part of the type system, instead of a function modifier. - -- 📢 The `BoolLiteral` type has been removed. Mojo now emits `True` and `False` - directly as `Bool`. - -- 📢 Syntax for function types has been added. You can now write function types - with `fn(Int) -> String` or `async def(&String, *Int) -> None`. No more - writing `!kgen.signature` types by hand! - -- 📢 Float literals are not emitted as `FloatLiteral` instead of an MLIR `f64` - type! - -- 📢 Automatic destructors are now supported by Mojo types, currently spelled - `fn __del___(owned self):` (the extra underscore will be dropped shortly). - These destructors work like Python object destructors and similar to C++ - destructors, with the major difference being that they run "as soon as - possible" after the last use of a value. This means they are not suitable - for use in C++-style RAII patterns (use the `with` statement for that, which - is currently unsupported). - - These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to _not_ capture values - correctly. Be very careful with interesting types in the vicinity of a - closure! - -- A new (extremely dangerous!) builtin function is available for low-level - ownership muckery. The `__get_address_as_owned_value(x)` builtin takes a - low-level address value (of `!kgen.pointer` type) and returns an `owned` value - for the memory that is pointed to. This value is assumed live at the - invocation of the builtin, but is "owned" so it needs to be consumed by the - caller, otherwise it will be automatically destroyed. This is an effective - way to do a "placement delete" on a pointer. - - ```mojo - # "Placement delete": destroy the initialized object begin pointed to. - _ = __get_address_as_owned_value(somePointer.value) - - # Result value can be consumed by anything that takes it as an 'owned' - # argument as well. - consume(__get_address_as_owned_value(somePointer.value)) - ``` - -- Another magic operator, named `__get_address_as_uninit_lvalue(x)` joins - the magic LValue operator family. This operator projects a pointer to - an LValue like `__get_address_as_lvalue(x)`. The difference is that - `__get_address_as_uninit_lvalue(x)` tells the compiler that the pointee is - uninitialized on entry and initialized on exit, which means that you can use - it as a "placement new" in C++ sense. `__get_address_as_lvalue(x)` tells the - compiler that the pointee is initialized already, so reassigning over it will - run the destructor. - - ```mojo - # "*Re*placement new": destroy the existing SomeHeavy value in the memory, - # then initialize a new value into the slot. - __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) - - # Ok to use an lvalue, convert to borrow etc. - use(__get_address_as_lvalue(somePointer.value)) - - # "Placement new": Initialize a new value into uninitialied memory. - __get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5) - - # Error, cannot read from uninitialized memory. - use(__get_address_as_uninit_lvalue(somePointer.value)) - ``` - - Note that `__get_address_as_lvalue` assumes that there is already a value at - the specified address, so the assignment above will run the `SomeHeavy` - destructor (if any) before reassigning over the value. - -- 📢 Implement full support for `__moveinit__` (aka move constructors) - - This implements the ability for memory-only types to define two different - types of move ctors if they'd like: - - 1. `fn __moveinit__(inout self, owned existing: Self)`: Traditional Rust - style moving constructors that shuffles data around while taking - ownership of the source binding. - 2. `fn __moveinit__(inout self, inout existing: Self):`: C++ style "stealing" - move constructors that can be used to take from an arbitrary LValue. - - This gives us great expressive capability (better than Rust/C++/Swift) - and composes naturally into our lifetime tracking and value - categorization system. - -- The `__call__` method of a callable type has been relaxed to take `self` by - borrow, allow non-copyable callees to be called. - -- Implicit conversions are now invoked in `raise` statements properly, allowing - converting strings to `Error` type. - -- Automatic destructors are turned on for `__del__` instead of `__del___`. - -- 📚 Add the builtin FloatLiteral type. - -- 📚 Add integral `floordiv` and `mod` for the SIMD type that handle negative - values. - -- 📚 Add an F64 to String converter. - -- 📚 Make the `print` function take variadic inputs. - -### Week of 2023-04-10 - -- 📢 Introduce consume operator `x^` - - This introduces the postfix consume operator, which produces an RValue given - a lifetime tracked object (and, someday, a movable LValue). - -- Mojo now automatically synthesizes empty destructor methods for certain types - when needed. - -- The `object` type has been built out into a fully-dynamic type, with dynamic - function dispatch, with full error handling support. - - ```mojo - def foo(a) -> object: - return (a + 3.45) < [1, 2, 3] # raises a TypeError - ``` - -- 📢 The `@always_inline` decorator is no longer required for passing capturing - closures as parameters, for both the functions themselves as functions with - capturing closures in their parameters. These functions are still inlined but - it is an implementation detail of capturing parameter closures. Mojo now - distinguishes between capturing and non-capturing closures. Nested functions - are capturing by default and can be made non-capturing with the - `@noncapturing` decorator. A top-level function can be passed as a capturing - closure by marking it with the `@closure` decorator. - -- 📢 Support for list literals has been added. List literals `[1, 2, 3]` - generate a variadic heterogeneous list type. - -- Variadics have been extended to work with memory-primary types. - -- Slice syntax is now fully-supported with a new builtin `slice` object, added - to the compiler builtins. Slice indexing with `a[1:2:3]` now emits calls to - `__setitem__` and `__getitem__` with a slice object. - -- Call syntax has been wired up to `__call__`. You can now `f()` on custom - types! - -- Closures are now explicitly typed as capturing or non-capturing. If a - function intends to accept a capturing closure, it must specify the - `capturing` function effect. - -- 📚 Add a `Tile2D` function to enable generic `2D` tiling optimizations. - -- 📚 Add the `slice` struct to enable getting/setting spans of elements via - `getitem`/`setitem`. - -- 📚 Add syntax sugar to autotuning for both specifying the autotuned values, - searching, and declaring the evaluation function. - -### Week of 2023-04-03 - -- The `AnyType` and `NoneType` aliases were added and auto-imported in all - files. - -- 📢 The Mojo VS Code extension has been improved with docstring validation. It - will now warn when a function's docstring has a wrong argument name, for - example. - -- 📢 A new built-in literal type `TupleLiteral` was added in `_CompilerBuiltin`. - It represents literal tuple values such as `(1, 2.0)` or `()`. - -- 📢 The `Int` type has been moved to a new `Builtin` module and is - auto-imported in all code. The type of integer literals has been changed from - the MLIR `index` type to the `Int` type. - -- Mojo now has a powerful flow-sensitive uninitialized variable checker. This - means that you need to initialize values before using them, even if you - overwrite all subcomponents. This enables the compiler to reason about the - true lifetime of values, which is an important stepping stone to getting - automatic value destruction in place. - -- 📢 Call syntax support has been added. Now you can directly call an object - that implements the `__call__` method, like `foo(5)`. - -- 📢 The name for copy constructors got renamed from `__copy__` to - `__copyinit__`. Furthermore, non-`@register_passable` types now implement - it like they do an init method where you fill in a by-reference self, for - example: - - ```mojo - fn __copyinit__(inout self, existing: Self): - self.first = existing.first - self.second = existing.second - ``` - - This makes copy construction work more similarly to initialization, and - still keeps copies `x = y` distinct from initialization `x = T(y)`. - -- 📢 Initializers for memory-primary types are now required to be in the form - `__init__(inout self, ...):` with a None result type, but for register primary - types, it remains in the form `__init__(...) -> Self:`. The `T{}` initializer - syntax has been removed for memory-primary types. - -- Mojo String literals now emit a builtin `StringLiteral` type! One less MLIR - type to worry about. - -- New `__getattr__` and `__setattr__` dunder methods were added. Mojo calls - these methods on a type when attempting member lookup of a non-static member. - This allows writing dynamic objects like `x.foo()` where `foo` is not a member - of `x`. - -- Early destructor support has been added. Types can now define a special - destructor method `__del___` (note three underscores). This is an early - feature and it is still being built out. There are many caveats, bugs, - and missing pieces. Stay tuned! - -- 📚 Integer division and mod have been corrected for rounding in the presence - of negative numbers. - -- 📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to - `SIMD[1, type]`. - -## March 2023 - -### Week of 2023-03-27 - -- 📢 Parameter names are no longer load-bearing in function signatures. This - gives more flexibility in defining higher-order functions, because the - functions passed as parameters do not need their parameter names to match. - - ```mojo - # Define a higher-order function... - fn generator[ - func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] - ](): - pass - - # Int parameter is named "foo". - fn f0[foo: Int](): - pass - - # Int parameter is named "bar". - fn f1[bar: Int](): - pass - - fn main(): - # Both can be used as `func`! - generator[f0]() - generator[f1]() - ``` - - Stay tuned for improved function type syntax... - -- 📢 Two magic operators, named `__get_lvalue_as_address(x)` and - `__get_address_as_lvalue` convert stored LValues to and from `!kgen.pointer` - types (respectively). This is most useful when using the `Pointer[T]` - library type. The `Pointer.address_of(lvalue)` method uses the first one - internally. The second one must currently be used explicitly, and can be - used to project a pointer to a reference that you can pass around and use - as a self value, for example: - - ```mojo - # "Replacement new" SomeHeavy value into the memory pointed to by a - # Pointer[SomeHeavy]. - __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) - ``` - - Note that `__get_address_as_lvalue` assumes that there is already a value at - the specified address, so the assignment above will run the `SomeHeavy` - destructor (if any) before reassigning over the value. - -- The `(((x)))` syntax is __mlir_op has been removed in favor of - `__get_lvalue_as_address` which solves the same problem and is more general. - -- 📢 When using a mutable `self` argument to a struct `__init__` method, it - now must be declared with `&`, like any other mutable method. This clarifies - the mutation model by making `__init__` consistent with other mutating - methods. - -- 📚 Add variadic string join function. - -- 📚 Default initialize values with 0 or null if possible. - -- 📚 Add compressed, aligned, and mask store intrinsics. - -### Week of 2023-03-20 - -- Initial `String` type is added to the standard library with some very basic - methods. - -- Add `DimList` to remove the need to use an MLIR list type throughout the - standard library. - -- 📢 The `__clone__` method for copying a value is now named `__copy__` to - better follow Python term of art. - -- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, - instead of taking it by reference. This makes it easier to write, works for - `@register_passable` types, and exposes more optimization opportunities to - the early optimizer and dataflow analysis passes. - - ```mojo - # Before: - fn __clone__(inout self) -> Self: ... - - # After: - fn __copy__(self) -> Self: ... - ``` - -- 📢 A new `@register_passable("trivial")` may be applied to structs that - have no need for a custom `__copy__` or `__del__` method, and whose state is - only made up of `@register_passable("trivial")` types. This eliminates the - need to define `__copy__` boilerplate and reduces the amount of IR generated - by the compiler for trivial types like `Int`. - -- You can now write back to attributes of structs that are produced by a - computed lvalue expression. For example `a[i].x = ..` works when `a[i]` - is produced with a `__getitem__`/`__setitem__` call. This is implemented by - performing a read of `a[i]`, updating the temporary, then doing a writeback. - -- The remaining hurdles to using non-parametric, `@register_passable` types as - parameter values have been cleared. Types like `Int` should enjoy full use as - parameter values. - -- Parameter pack inference has been added to function calls. Calls to functions - with parameter packs can now elide the pack types: - - ```mojo - fn foo[*Ts: AnyType](*args: *Ts): pass - - foo(1, 1.2, True, "hello") - ``` - - Note that the syntax for parameter packs has been changed as well. - -- 📚 Add the runtime string type. - -- 📚 Introduce the DimList struct to remove the need to use low-level MLIR - operations. - -### Week of 2023-03-13 - -- 📢 Initializers for structs now use `__init__` instead of `__new__`, - following standard practice in Python. You can write them in one of two - styles, either traditional where you mutate self: - - ```mojo - fn __init__(self, x: Int): - self.x = x - ``` - - or as a function that returns an instance: - - ```mojo - fn __init__(x: Int) -> Self: - return Self {x: x} - ``` - - Note that `@register_passable` types must use the later style. - -- 📢 The default argument convention is now the `borrowed` convention. A - "borrowed" argument is passed like a C++ `const&` so it doesn't need to - invoke the copy constructor (aka the `__clone__` method) when passing a value - to the function. There are two differences from C++ `const&`: - - 1. A future borrow checker will make sure there are no mutable - aliases with an immutable borrow. - 2. `@register_passable` values are passed directly in an SSA register (and - thus, usually in a machine register) instead of using an extra reference - wrapper. This is more efficient and is the 'right default' for - `@register_passable` values like integers and pointers. - - This also paves the way to remove the reference requirement from `__clone__` - method arguments, which will allow us to fill in more support for them. - -- Support for variadic pack arguments has been added to Mojo. You can now - write heterogeneous variadic packs like: - - ```mojo - fn foo[*Ts: AnyType](args*: Ts): pass - - foo[Int, F32, String, Bool](1, 1.5, "hello", True) - ``` - -- The `owned` argument convention has been added. This argument convention - indicates that the function takes ownership of the argument and is responsible - for managing its lifetime. - -- The `borrowed` argument convention has been added. This convention signifies - the callee gets an immutable shared reference to a value in the caller's - context. - -- 📚 Add the `getenv` function to the `OS` module to enable getting environment - variables. - -- 📚 Enable the use of dynamic strides in `NDBuffer`. - -### Week of 2023-03-06 - -- 📢 Support added for using capturing async functions as parameters. - -- 📢 Returning result parameters has been moved from `return` statements to a - new `param_return` statement. This allows returning result parameters from - throwing functions: - - ```mojo - @raises - fn foo[() -> out: Int](): - param_return[42] - raise Error() - ``` - - And returning different parameters along `@parameter if` branches: - - ```mojo - fn bar[in: Bool -> out: Int](): - @parameter - if in: - param_return[1] - else: - param_return[2] - ``` - -- 📢 Mojo now supports omitting returns at the end of functions when they would - not reachable. For instance, - - ```mojo - fn foo(cond: Bool) -> Int: - if cond: - return 0 - else: - return 1 - - fn bar() -> Int: - while True: - pass - ``` - -- String literals now support concatenation, so `"hello " "world"` is treated - the same as `"hello world"`. - -- Empty bodies on functions, structs, and control flow statements are no longer - allowed. Please use `pass` in them to explicitly mark that they are empty, - just like in Python. - -- 📢 Structs in Mojo now default to living in memory instead of being passed - around in registers. This is the right default for generality (large - structures, structures whose pointer identity matters, etc) and is a key - technology that enables the borrow model. For simple types like `Int` and - `SIMD`, they can be marked as `@register_passable`. - - Note that memory-only types currently have some limitations: they cannot be - used in generic algorithms that take and return a `!mlirtype` argument, and - they cannot be used in parameter expressions. Because of this, a lot of - types have to be marked `@register_passable` just to work around the - limitations. We expect to enable these use-cases over time. - -- 📢 Mojo now supports computed lvalues, which means you can finally assign to - subscript expressions instead of having to call `__setitem__` explicitly. - - Some details on this: Mojo allows you to define multiple `__setitem__` - overloads, but will pick the one that matches your `__getitem__` type if - present. It allows you to pass computed lvalues into inout arguments by - introducing a temporary copy of the value in question. - -- Mojo now has much better support for using register-primary struct types in - parameter expressions and as the types of parameter values. This will allow - migration of many standard library types away from using bare MLIR types like - `__mlir_type.index` and towards using `Int`. This moves us towards getting rid - of MLIR types everywhere and makes struct types first-class citizens in the - parameter system. - -- 📚 Add a `sort` function. - -- 📚 Add non-temporal store to enable cache bypass. - -## February 2023 - -### Week of 2023-02-27 - -- 📢 The `@interface`, `@implements`, and `@evaluator` trio of decorators have - been removed, replaced by the `@parameter if` and `@adaptive` features. - -- 📢 Parameter inference can now infer the type of variadic lists. - -- 📢 Memory primary types are now supported in function results. A result slot - is allocated in the caller, and the callee writes the result of the function - into that slow. This is more efficient for large types that don't fit into - registers neatly! And initializers for memory-primary types now initialize - the value in-place, instead of emitting a copy! - -- Support for `let` decls of memory primary types has been implemented. These - are constant, ready-only values of memory primary types but which are - allocated on the function stack. - -- Overload conversion resolution and parameter inference has been improved: - - 1. Inference now works with `let` decls in some scenarios that weren't - working before. - 2. Parameter bindings can now infer types into parameter expressions. This - helps resolve higher-order functions in parameter expressions. - -- 📚 Optimize floor, ceil, and ldexp on X86 hardware. - -- 📚 Implement the log math function. - -### Week of 2023-02-20 - -- 📢 A new `@__memory_primary` struct decorator has been introduced. Memory - primary types must always have an address. For instance, they are always - stack-allocated when declared in a function and their values are passed into - function calls by address instead of copy. This is in contract with register - primary types that may not have an address, and which are passed by value - in function calls. Memory-primary fields are not allowed inside - register-primary structs, because struct elements are stored in-line. - -- 📢 A new `_CompilerBuiltin` module was added. This module defines core types - and functions of the language that are referenced by the parser, and hence, is - auto-imported by all other modules. For example new types for literal values - like the boolean True/False will be included in `_CompilerBuiltin`. - -- 📢 A special `__adaptive_set` property can be accessed on a function reference - marked as `@adaptive`. The property returns the adaptive overload set of that - function. The return type is a `!kgen.variadic`. This feature is useful to - implement a generic `evaluate` function in the standard library. - -- 📢 A new built-in literal type `BoolLiteral` was added in `_CompilerBuiltin`. - It represents the literal boolean values `True` and `False`. This is the first - Mojo literal to be emitted as a standard library type! - -- 📚 Add the prefetch intrinsic to enable HW prefetching a cache line. - -- 📚 Add the InlinedFixedVector, which is optimized for small vectors and stores - values on both the stack and the heap. - -### Week of 2023-02-13 - -- Unqualified lookups of struct members apply contextual parameters. This means - for instance that you can refer to static methods without binding the - struct parameters. - - ```mojo - struct Foo[x: Int]: - @staticmethod - bar(): pass - - foo(self): - bar() # implicitly binds to Foo[x].bar() - Foo[2].bar() # explicitly bind to another parameter - ``` - -- 📢 A new `Self` type refers to the enclosing type with all parameters bound - to their current values. This is useful when working with complex parametric - types, e.g.: - - ```mojo - struct MyArray[size: Int, element_type: type]: - fn __new__() -> Self: - return Self {...} - ``` - - which is a lot nicer than having to say `MyArray[size, element_type]` over - and over again. - -- 📢 Mojo now supports an `@adaptive` decorator. This decorator will supersede - interfaces, and it represents an overloaded function that is allowed to - resolve to multiple valid candidates. In that case, the call is emitted as a - fork, resulting in multiple function candidates to search over. - - ```mojo - @adaptive - fn sort(arr: ArraySlice[Int]): - bubble_sort(arr) - - @adaptive - fn sort(arr: ArraySlice[Int]): - merge_sort(arr) - - fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]): - let arr = lhs + rhs - sort(arr) # this forks compilation, creating two instances - # of the surrounding function - ``` - -- 📢 Mojo now requires that types implement the `__clone__` special member in - order to copy them. This allows the safe definition of non-copyable types - like Atomic. Note that Mojo still doesn't implement destructors, and (due to - the absence of non-mutable references) it doesn't actually invoke the - `__clone__` member when copying a let value. As such, this forces to you as - a Mojo user to write maximal boilerplate without getting much value out of it. - - In the future, we will reduce the boilerplate with decorators, and we will - actually start using it. This will take some time to build out though. - -- 📢 A special `__mlir_region` statement was added to provide stronger - invariants around defining MLIR operation regions in Mojo. It similar syntax - to function declarations, except it there are no results and no input - conventions. - -- 📚 Implement the log math function. - -- 📚 Improve the DType struct to enable compile-time equality checks. - -- 📚 Add the Complex struct class. - -### Week of 2023-02-06 - -- 📢 The `if` statement now supports a `@parameter` decorator, which requires - its condition to be a parameter expression, but which only emits the 'True' - side of the condition to the binary, providing a "static if" functionality. - This should eliminate many uses of `@interface` that are just used to provide - different constraint on the implementations. - -- 📢 `fn main():` is now automatically exported and directly runnable by the - command-line `mojo` tool. This is a stop-gap solution to enable script-like - use cases until we have more of the language built out. - -- 🪦 The `@nodebug_inline` feature has been removed, please use - `@alwaysinline("nodebug")` for methods that must be inlined and that we don't - want to step into. - -- 📢 Python chained comparisons, ex. `a < b < c`, are now supported in Mojo. - -- 📢 Functions can now be defined with default argument values, such as - `def f(x: Int, y: Int = 5):`. The default argument value is used when callers - do not provide a value for that argument: `f(3)`, for example, uses the - default argument value of `y = 5`. - -- Unused coroutine results are now nicely diagnosed as "missing await" warnings. - -- 📚 Introduce a vectorized reduction operations to the SIMD type. - -## January 2023 - -### Week of 2023-01-30 - -- A basic Mojo language server has been added to the VS Code extension, which - parses your code as you write it, and provides warnings, errors, and fix-it - suggestions! - -- 💯 The Mojo standard library is now implicitly imported by default. - -- The coroutine lowering support was reworked and a new `Coroutine[T]` type was - implemented. Now, the result of a call to an async function MUST be wrapped in - a `Coroutine[T]`, or else memory will leak. In the future, when Mojo supports - destructors and library types as literal types, the results of async function - calls will automatically wrapped in a `Coroutine[T]`. But today, it must be - done manually. This type implements all the expected hooks, such as - `__await__`, and `get()` to retrieve the result. Typical usage: - - ```mojo - async fn add_three(a: Int, b: Int, c: Int) -> Int: - return a + b + c - - async fn call_it(): - let task: Coroutine[Int] = add_three(1, 2, 3) - print(await task) - ``` - -- ⭐️ We now diagnose unused expression values at statement context in `fn` - declarations (but not in `def`s). This catches bugs with unused values, e.g. - when you forget the parens to call a function. - -- 📢 An `@always_inline("nodebug")` function decorator can be used on functions - that need to be force inlined, but when they should not have debug info in - the result. This should be used on methods like `Int.__add__` which should - be treated as builtin. - -- 📢 The `@export` decorator now supports an explicit symbol name to export to, - for example: - - ```mojo - @export("baz") # exported as 'baz' - fn some_mojo_fn_name(): - ``` - -- 📢 🚧 Subscript syntax is now wired up to the `__getitem__` dunder method. - - This allows type authors to implement the `__getitem__` method to enable - values to be subscripted. This is an extended version of the Python semantics - (given we support overloading) that allows you to define N indices instead of - a single version that takes a tuple (also convenient because we don't have - tuples yet). - - Note that this has a very, very important limitation: subscripts are NOT - wired up to `__setitem__` yet. This means that you can read values with - `.. = v[i]` but you cannot store to them with `v[i] = ..`. For this, please - continue to call `__setitem__` directly. - -- 📢 Function calls support parameter inference. - - For calls to functions that have an insufficient number of parameters - specified at the callsite, we can now infer them from the argument list. We - do this by matching up the parallel type structure to infer what the - parameters must be. - - Note that this works left to right in the parameter list, applying explicitly - specified parameters before trying to infer new ones. This is similar to how - C++ does things, which means that you may want to reorder the list of - parameters with this in mind. For example, a `dyn_cast`-like function will be - more elegant when implemented as: - - `fn dyn_cast[DstType: type, SrcType: type](src: SrcType) -> DstType:` - - Than with the `SrcType`/`DstType` parameters flipped around. - -- 📚 Add the growable Dynamic vector struct. - -### Week of 2023-01-23 - -- Inplace operations like `+=`/`__iadd__` may now take `self` by-val if they - want to, instead of requiring it to be by-ref. -- ⭐️ Inplace operations are no longer allowed to return a non-None value. The - corresponding syntax is a statement, not an expression. - -- A new `TaskGroup` type was added to the standard library. This type can be - used to schedule multiple tasks on a multi-threaded workqueue to be executed - in parallel. An async function can `await` all the tasks at once with the - taskgroup. - -- 📢 We now support for loops! A type that defines an `__iter__` method that - returns a type that defines `__next__` and `__len__` methods is eligible to - be used in the statement `for el in X()`. Control flow exits the loop when - the length is zero. - - This means things like this now work: - - ```mojo - for item in range(start, end, step): - print(item) - ``` - -- Result parameters now have names. This is useful for referring to result - parameters in the return types of a function: - - ```mojo - fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]: - ``` - -- 📢 We now support homogeneous variadics in value argument lists, using the - standard Python `fn thing(*args: Int):` syntax! Variadics also have support - in parameter lists: - - ```mojo - fn variadic_params_and_args[*a: Int](*b: Int): - print(a[0]) - print(b[1]) - ``` - -- 📚 Add the range struct to enable `for ... range(...)` loops. - -- 📚 Introduce the unroll generator to allow one to unroll loops via a library - function. - -### Week of 2023-01-16 - -- 📢 Struct field references are now supported in parameter context, so you - can use `someInt.value` to get the underlying MLIR thing out of it. This - should allow using first-class types in parameters more widely. -- 📢 We now support "pretty" initialization syntax for structs, e.g.: - - ```mojo - struct Int: - var value: __mlir_type.index - fn __new__(value: __mlir_type.index) -> Int: - return Int {value: value} - ``` - - This eliminates the need to directly use the MLIR `lit.struct.create` op in - struct initializers. This syntax may change in the future when ownership - comes in, because we will be able to support the standard `__init__` model - then. -- 📢 It is now possible to attach regions to `__mlir_op` operations. This is - done with a hack that allows an optional `_region` attribute that lists - references to the region bodies (max 1 region right now due to lack of list - `[]` literal). -- Nested functions now parse, e.g.: - - ```mojo - fn foo(): - fn bar(): - pass - bar() - ``` - -- Python-style `async` functions should now work and the `await` expression - prefix is now supported. This provides the joy of async/await syntactic - sugar when working with asynchronous functions. This is still somewhat - dangerous to use because we don't have proper memory ownership support yet. - -- String literals are now supported. - -- Return processing is now handled by a dataflow pass inside the compiler, so - it is possible to return early out of if statements. - -- The parser now supports generating 'fixit' hints on diagnostics, and uses - them when a dictionary literal uses a colon instead of equal, e.g.: - - ```log - x.mojo:8:48: error: expected ':' in subscript slice, not '=' - return __mlir_op.`lit.struct.create`[value = 42]() - ^ - : - ``` - -- 📚 Add reduction methods which operate on buffers. - -- 📚 Add more math functions like sigmoid, sqrt, rsqrt, etc. - -- 📚 Add partial load / store which enable loads and stores that are predicated - on a condition. - -### Week of 2023-01-09 - -- The `/` and `*` markers in function signatures are now parsed and their - invariants are checked. We do not yet support keyword arguments yet though, - so they aren't very useful. -- Functions now support a new `@nodebug_inline` decorator. - (Historical note: this was later replaced with `@alwaysinline("nodebug")`). - - Many of the things at the bottom level of the Mojo stack are trivial - zero-abstraction wrappers around MLIR things, for example, the `+` - operator on Int or the `__bool__` method on Bool itself. These operators - need to be force inlined even at -O0, but they have some additional things - that we need to wrestle with: - - 1. In no case would a user actually want to step into the `__bool__` method on - Bool or the + method on Int. This would be terrible debugger QoI for - unless you're debugging Int itself. We need something like - `__always_inline__, __nodebug__` attributes that clang uses in headers - like xmmintrin.h. - - 2. Similarly, these "operators" should be treated by users as primitives: - they don't want to know about MLIR or internal implementation details of - Int. - - 3. These trivial zero abstraction things should be eliminated early in the - compiler pipeline so they don't slow down the compiler, bloating out the - call graph with trivial leaves. Such thing slows down the elaborator, - interferes with basic MLIR things like fold(), bloats out the IR, or - bloats out generated debug info. - - 4. In a parameter context, we want some of these things to get inlined so - they can be simplified by the attribute logic and play more nicely with - canonical types. This is just a nice to have thing those of us who have - to stare at generated IR. - - The solution to this is a new `@nodebug_inline` decorator. This decorator - causes the parser to force-inline the callee instead of generating a call to - it. While doing so, it gives the operations the location of the call itself - (that's the "nodebug" part) and strips out let decls that were part of the - internal implementation details. - - This is a super-power-user-feature intended for those building the standard - library itself, so it is intentionally limited in power and scope: It can - only be used on small functions, it doesn't support regions, by-ref, throws, - async, etc. - -- Separately, we now support an `@alwaysInline` decorator on functions. This - is a general decorator that works on any function, and indicates that the - function must be inlined. Unlike `@nodebug_inline`, this kind of inlining is - performed later in the compilation pipeline. - -- The `__include` hack has been removed now that we have proper import support. - -- `__mlir_op` can now get address of l-value: - - You can use magic `(((x)))` syntax in __mlir_op that forces the `x` - expression to be an lvalue, and yields its address. This provides an escape - hatch (isolated off in `__mlir_op` land) that allows unsafe access to lvalue - addresses. - -- We now support `__rlshift__` and `__rtruediv__`. - -- 📢 The parser now resolves scoped alias references. This allows us to support - things like `SomeType.someAlias`, forward substituting the value. This - unblocks use of aliases in types like `DType`. We'd like to eventually - preserve the reference in the AST, but this unblocks library development. - -- 📚 Add a `now` function and `Benchmark` struct to enable timing and - benchmarking. - -- 📚 Move more of the computation in NDBuffer from runtime to compile time if - possible (e.g. when the dimensions are known at compile time). - -### Week of 2023-01-02 - -- 📚 Added the `print` function which works on Integers and SIMD values. - -- The frontend now has a new diagnostic subsystem used by the `kgen` tool (but - not by `kgen-translate` for tests) that supports source ranges on - diagnostics. Before we'd emit an error like: - - ```log - x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' - callee(1.0+F32(2.0)) - ^ - x.lit:4:1: note: function declared here - fn callee(a: Int): - ^ - ``` - - now we produce: - - ```log - x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' - callee(1.0+F32(2.0)) - ^ ~~~~~~~~~~~~ - x.lit:4:1: note: function declared here - fn callee(a: Int): - ^ - ``` - -- 📢 Parameter results are now supported in a proper way. They are now forward - declared with an alias declaration and then bound in a call with an arrow, - e.g.: - - ```mojo - alias a : __mlir_type.index - alias b : __mlir_type.index - idx_result_params[xyz*2 -> a, b]() - ``` - -- Various minor issues with implicit conversions are fixed. For instances, - implicit conversions are now supported in parameter binding contexts and - `alias` declarations with explicit types. -- Doc strings are allowed on functions and structs, but they are currently - discarded by the parser. - -- 📚 Add a `print` method!!! - -- 📚 Demonstrate a naive matmul in Mojo. - -- 📚 Initial work on functions that depend on types (e.g. FPUtils, nan, inf, - etc.) - -- 📚 Allow one to query hardware properties such as simd_width, os, etc. via - TargetInfo at compile time. - -## December 2022 - -### Week of 2022-12-26 - -- 📢 You can now call functions in a parameter context! Calling a function in - a parameter context will evaluate the function at compile time. The result - can then be used as parameter values. For example, - - ```mojo - fn fma(x: Int, y: Int, z: Int) -> Int: - return a + b * c - - fn parameter_call(): - alias nelts = fma(32, 2, 16) - var x: SIMD[f32, nelts] - ``` - -- You can now disable printing of types in an `__mlir_attr` substitution by - using unary `+` expression. - -- 📢 `let` declarations are now supported in functions. `let` declarations are - local run-time constant values, which are always rvalues. They complement - 'var' decls (which are mutable lvalues) and are the normal thing to use in - most cases. They also generate less IR and are always in SSA form when - initialized. - - We will want to extend this to support 'let' decls in structs at some point - and support lazy initialized 'let' declarations (using dataflow analysis) but - that isn't supported yet. - -- 📚 Add the NDBuffer struct. - -- Happy new year. - -### Week of 2022-12-19 - -- 📚 Start of the Standard library: - 1. Added Integer and SIMD structs to bootstrap the standard library. - 2. Added very basic buffer data structure. - -- We have basic support for parsing parameter results in function calls! Result - parameters are an important Mojo metaprogramming feature. They allow functions - to return compile-time constants. - - ```mojo - fn get_preferred_simdwidthof[() -> nelts: Int](): - return[2] - - fn vectorized_function(): - get_preferred_simdwidthof[() -> nelts]() - var x: SIMD[f32, nelts] - ``` - -- Types can now be used as parameters of `!kgen.mlirtype` in many more cases. - -- MLIR operations with zero results don't need to specify `_type: []` anymore. - -- We support parsing triple quoted strings, for writing docstrings for your - functions and structs! - -- A new `__mlir_type[a,b,c]` syntax is available for substituting into MLIR - types and attributes is available, and the old placeholder approach is - removed. This approach has a few advantages beyond what placeholders do: - - 1. It's simpler. - 2. It doesn't form the intermediate result with placeholders, which - gets rejected by MLIR's semantic analysis, e.g. the complex case - couldn't be expressed before. - 3. It provides a simple way to break long attrs/types across multiple - lines. - -- We now support an `@evaluator` decorator on functions for KGEN evaluators. - This enables specifying user-defined interface evaluators when performing - search during compilation. - -- 📢 `import` syntax is now supported! - - This handles packaging imported modules into file ops, enables effective - isolation from the other decls. "import" into the desired context is just - aliasing decls, with the proper symbols references handle automatically during - IR generation. As a starting point, this doesn't handle any notion of packages - (as those haven't been sketched out enough). - -- 📢 Reversed binary operators (like `__radd__`) are now looked up and used if - the forward version (like `__add__`) doesn't work for some reason. - -- 📢 Implicit conversions are now generally available, e.g. in assign - statements, variable initializers etc. There are probably a few more places - they should work, but we can start eliminating all the extraneous explicit - casts from literals now. - -- Happy Holidays - -### Week of 2022-12-12 - -- 📢 Function overloading now works. Call resolution filters candidate list - according to the actual parameter and value argument specified at the site of - the call, diagnosing an error if none of the candidates are viable or if - multiple are viable and ambiguous. We also consider implicit conversions in - overload look: - - ```mojo - fn foo(x: Int): pass - fn foo(x: F64): pass - - foo(Int(1)) # resolves to the first overload - foo(1.0) # resolves to the second overload - foo(1) # error: both candidates viable with 1 implicit conversion! - ``` - -- The short circuiting binary `and` and `or` expressions are now supported. - -- Unary operator processing is a lot more robust, now handling the `not` - expression and `~x` on Bool. - -- 📢 The compiler now generates debug information for use with GDB/LLDB that - describes variables and functions. - -- The first version of the Mojo Visual Studio Code extension has been released! - It supports syntax highlighting for Mojo files. - -- The first version of the `Bool` type has landed in the new Mojo standard - library! - -- 📢 Implicit conversions are now supported in return statements. - -### Week of 2022-12-05 - -- "Discard" patterns are now supported, e.g. `_ = foo()` - -- We now support implicit conversions in function call arguments, e.g. - converting an `index` value to `Int` automatically. This eliminates a bunch - of casts, e.g. the need to say F32(1.0) everywhere. - - This is limited for a few reasons that will be improved later: - 1. We don't support overloading, so lots of types aren't convertible - from all the things they should be, e.g. you can't pass "1" to - something that expects F32, because F32 can't be created from index. - 2. This doesn't "check to see if we can invoke `__new__`" it force applies - it on a mismatch, which leads to poor QoI. - 3. This doesn't fix things that need radd. - -## November 2022 - -### Week of 2022-11-28 - -- 📢 We support the `True` and `False` keywords as expressions. - -- 📢 A new `alias` declaration is supported which allows defining local - parameter values. This will eventually subsume type aliases and other - things as it gets built out. - -- 📢 We now have end-to-end execution of Mojo files using the `kgen` tool! - Functions exported with `@export` can be executed. - -- 📢 We have try-except-else and `raise` statements and implicit error - propagation! The error semantics are that `def` can raise by default, but `fn` - must explicitly declare raising with a `@raises` decorator. Stub out basic - `Error` type. - -- The `&` sigil for by-ref arguments is now specified after the identifier. - Postfix works better for ref and move operators on the expression - side because it chains an mentally associates correctly: - `thing.method().result^`. We don't do that yet, but align param - decl syntax to it so that things won't be odd looking when we do. - In practice this looks like: - - ```mojo - def mutate_argument(a&: index): - a = 25 - ``` - -### Week of 2022-11-21 - -- 📢 The magic `index` type is gone. Long live `__mlir_type.index`. - -- Implement parameter substitution into parametric `__mlir_type` decls. This - allows us to define parametric opaque MLIR types with exposed parameters using - a new "placeholder" attribute. This allows us to expose the power of the KGEN - type parametric system directly into Mojo. - -- 📢 Fully-parametric custom types can now be defined and work in Mojo, bringing - together a lot of the recent work. We can write the SIMD type directly as a - wrapper around the KGEN type, for example: - - ```mojo - struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]: - var value: - __mlir_type.`!pop.simd<#lit, - #lit>`[nelts, dt] - - fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]: - return __mlir_op.`pop.add`(self.value, rhs.value) - ``` - -### Week of 2022-11-14 - -- 📢 Implement a magic `__mlir_type` declaration that can be used to access any - MLIR type. E.g. `__mlir_type.f64`. - -- 📢 Add an `fn` declaration. These are like `def` declarations, but are more - strict in a few ways: they require type annotations on arguments, don't allow - implicit variable declarations in their body, and make their arguments rvalues - instead of lvalues. - -- Implemented Swift-style backtick identifiers, which are useful for code - migration where names may collide with new keywords. - -- 📢 A new `__include` directive has been added that performs source-level - textual includes. This is temporary until we have an `import` model. - -- Implement IR generation for arithmetic operators like `+` and `*` in terms - of the `__add__` and `__mul__` methods. - -- 📢 Added support for `break` and `continue` statements, as well as early - returns inside loops and conditionals! - -- 📢 Implemented augmented assignment operators, like `+=` and `@=`. - -- 📢 Mojo now has access to generating any MLIR operations (without regions) - with a new `__mlir_op` magic declaration. We can start to build out the - language's builtin types with this: - - ```mojo - struct Int: - var value: __mlir_type.index - - fn __add__(self, rhs: Int) -> Int: - return __mlir_op.`index.add`(self.value, rhs.value) - ``` - - Attributes can be attached to the declaration with subscript `[]` syntax, - and an explicit result type can be specified with a special `_type` attribute - if it cannot be inferred. Attributes can be accessed via the `__mlir_attr` - magic decl: - - ```mojo - __mlir_op.`index.cmp`[ - _type: __mlir_type.i1, - pred: __mlir_attr.`#index` - ](lhs, rhs) - ``` - -- Improved diagnostics emissions with ranges! Now errors highlight the whole - section of code and not just the first character. - -### Week of 2022-11-07 - -- Implemented the `@interface` and `@implements` decorators, which provide - access to KGEN generator interfaces. A function marked as an `@interface` - has no body, but it can be implemented by multiple other functions. - - ```mojo - @interface - def add(lhs: index, rhs: index): - - @implements(add) - def normal_add(lhs: index, rhs: index) -> index: - return lhs + rhs - - @implements(add) - def slow_add(lhs: index, rhs: index) -> index: - wait(1000) - return normal_add(lhs, rhs) - ``` - -- 📢 Support for static struct methods and initializer syntax has been added. - Initializing a struct with `Foo()` calls an implicitly static `__new__` - method. This method should be used instead of `__init__` inside structs. - - ```mojo - struct Foo: - var value: index - - def __new__() -> Foo: - var result: Foo - result.value = Foo.return_a_number() # static method! - return result - - @staticmethod - def return_a_number() -> index: - return 42 - ``` - -- 📢 Full by-ref argument support. It's now possible to define in-place - operators like `__iadd__` and functions like `swap(x, y)` correctly. - -- 📢 Implemented support for field extract from rvalues, like `x.value` where - `x` is not an lvalue (`var` declaration or by-ref function argument). - -## October 2022 - -### Week of 2022-10-31 - -- Revised `return` handling so that a return statement with no expression is - syntax sugar for `return None`. This enables early exits in functions that - implicitly return `None` to be cleaner: - - ```mojo - def just_return(): - return - ``` - -- Added support for parsing more expressions: if-else, bitwise operators, - shift operators, comparisons, floor division, remainder, and matmul. - -- 📢 The type of the `self` argument can now be omitted on member methods. - -### Week of 2022-10-24 - -- Added parser support for right-associativity and unary ops, like the power - operator `a ** b ** c` and negation operator `-a`. - -- Add support for `&expr` in Mojo, which allows denoting a by-ref argument in - functions. This is required because the `self` type of a struct method is - implicitly a pointer. - -- Implemented support for parametric function declarations, such as: - - ```mojo - struct SIMD[dt: DType, width: index]: - fn struct_method(self: &SIMD[dt, width]): - pass - - def fancy_add[dt: DType, width: index]( - lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index: - return width - ``` - -### Week of 2022-10-17 - -- Added explicit variable declarations with `var`, for declaring variables both - inside functions and structs, with support for type references. Added `index` - as a temporary built-in type. - - ```mojo - def foo(lhs: index, rhs: index) -> index: - var result: index = lhs + rhs - return result - ``` - -- Implemented support for parsing struct declarations and references to type - declarations in functions! In `def`, the type can be omitted to signal an - object type. - - ```mojo - struct Foo: - var member: index - - def bar(x: Foo, obj) -> index: - return x.member - ``` - -- Implemented parser support for `if` statements and `while` loops! - - ```mojo - def if_stmt(c: index, a: index, b: index) -> index: - var result: index = 0 - if c: - result = a - else: - result = b - return result - - def while_stmt(init: index): - while init > 1: - init = init - 1 - ``` - -- Significantly improved error emission and handling, allowing the parser to - emit multiple errors while parsing a file. - -### Week of 2022-10-10 - -- Added support for parsing integer, float, and string literals. - -- Implemented parser support for function input parameters and results. You can - now write parametric functions like, - - ```mojo - def foo[param: Int](arg: Int) -> Int: - result = param + arg - return result - ``` - -### Week of 2022-10-03 - -- Added some basic parser scaffolding and initial parser productions, including - trivial expressions and assignment parser productions. -- Implemented basic scope handling and function IR generation, with support for - forward declarations. Simple functions like, - - ```mojo - def foo(x: Int): - ``` - - Now parse! But all argument types are hard-coded to the MLIR `index` type. - -- Added IR emission for simple arithmetic expressions on builtin types, like - `x + y`. - -## September 2022 - -### Week of 2022-09-26 - -- Mojo's first patch to add a lexer was Sep 27, 2022. - -- Settled on `[]` for Mojo generics instead of `<>`. Square brackets are - consistent with Python generics and don't have the less than ambiguity - other languages have. diff --git a/docs/manual/packages.md b/docs/manual/packages.md index 46e719921b..c94aac8437 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -51,7 +51,7 @@ that's in the same directory as `mymodule.mojo`: from mymodule import MyPair fn main(): - let mine = MyPair(2, 4) + var mine = MyPair(2, 4) mine.dump() ``` @@ -62,7 +62,7 @@ through the module name. For example: import mymodule fn main(): - let mine = mymodule.MyPair(2, 4) + var mine = mymodule.MyPair(2, 4) mine.dump() ``` @@ -72,7 +72,7 @@ You can also create an alias for an imported member with `as`, like this: import mymodule as my fn main(): - let mine = my.MyPair(2, 4) + var mine = my.MyPair(2, 4) mine.dump() ``` @@ -124,7 +124,7 @@ name like this: from mypackage.mymodule import MyPair fn main(): - let mine = MyPair(2, 4) + var mine = MyPair(2, 4) mine.dump() ``` diff --git a/docs/roadmap.md b/docs/roadmap.md index 92d2ca2aa1..34b4eaeccb 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -561,7 +561,7 @@ collections of types with lifetimes, like `String`, the elements have to be manually destructed. Doing so requires quite an ugly pattern, shown in the next section. -The `DynamicVector` type has been updated to use traits, and invokes destructors +The `List` type has been updated to use traits, and invokes destructors properly. ### No safe value references @@ -726,7 +726,7 @@ both of the following are true: - The struct has one or more fields that are self referencing (such as `Pointer[Self]`). -- The struct declares conformance to a trait that requires these dundner +- The struct declares conformance to a trait that requires these dunder methods. ```mojo @@ -739,3 +739,57 @@ struct A(CollectionElement): In the example above, adding the `__moveinit__()` and `__copyinit__()` methods required by `CollectionElement` resolves this error. + +### `or` expression is statically typed + +Because Mojo has static typing, the `or` expression can't currently mimic the +behavior of Python. In Python, the result type of the `or` expression is +dynamic, based on the runtime values: + +```python +i: int = 0 +s: str = "hello" +print(type(i or s)) # prints +i = 5 +print(type(i or s)) # prints +``` + +In Mojo, given the expression `(a or b)`, the compiler needs to statically +determine a result type that the types of `a` and `b` can both be converted to. + +For example, currently an `Int` can be implicitly converted to a `String`, but a +`String` can't be implicitly converted to an `Int`. So given an integer value +`i` and a string value `s`, the value of `(i or s)` will *always* be a `String`. + +### `StringLiteral` behaves differently than `String` + +String literals behave differently than `String` values in Mojo code. For +example: + +```mojo +fn main(): + var g: Int = 0 + var h: String = "hello" + print(g or h) # prints `hello` + print(g or "hello") # prints `True` +``` + +While the `IntLiteral` and `FloatLiteral` types convert or *materialize* at +runtime into `Int` and `Float64` values, respectively, string literals continue +to exist at runtime as `StringLiteral` values. This can result in surprising +behavior because `StringLiteral` has a more restricted API than `String`. + +In the example above, because the `or` expression is statically typed, +and `Int` cannot be implicitly converted to a `StringLiteral`, the compiler +chooses a result type that both `Int` and `StringLiteral` can be converted to—in +this case, `Bool`. + +We plan to address this issue in the future, but in the near term, you can avoid +the inconsistency between `StringLiteral` and `String` problems by explicitly +converting string literals to `String` values. For example: + +```mojo +var h: String = "hello" +# or +print(g or str("hello")) +``` diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 782e971f5f..8053940c72 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -338,7 +338,7 @@ "\n", "Currently Mojo expressions are limited to inspecting variables and their fields.\n", "The console also supports subscript notation (`vector[index]`) for certain data\n", - "structures in the standard library, including `DynamicVector`, `SIMD`,\n", + "structures in the standard library, including `List`, `SIMD`,\n", "and `ListLiteral`. \n", "\n", "In the future, we intend to provide a way for arbitrary data structures to\n", diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index 2e8118a635..a7cd64ac3c 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -64,7 +64,7 @@ vectorize[$3, $1, $2] ### `DynamicVector` constructor `capacity` now keyword-only -The [`DynamicVector`](/mojo/stdlib/collections/vector#dynamicvector) struct had +The [`DynamicVector`](/mojo/stdlib/collections/list#list) struct had a constructor that took a single integer value for the vector's capacity. This had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. This was not intended to support implicit conversion, so `capacity` is now a @@ -82,7 +82,7 @@ Which in VS Code looks like this: ### `NDBuffer` signature change The shape of an -[`NDBuffer`](https://docs.modular.com/mojo/stdlib/memory/buffer#ndbuffer) can +[`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) can now default to being unknown, so the parameter list has been rearranged to accommodate this: @@ -113,7 +113,7 @@ NDBuffer[$3, $1, $2] ### Dereference `Variant` with `[]` -Previously, using [`Variant`](/mojo/stdlib/collections/vector#dynamicvector) +Previously, using [`Variant`](/mojo/stdlib/utils/variant#variant) was unsafe with heap allocated objects, it now returns a reference. If you had code that looks like this: From ff0bbfc2afec51e2b0332584f0edf6871a6746ec Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 10:20:23 -0700 Subject: [PATCH 0003/2019] [docs] Cherrypick latest docs for `docs/oss-materials/` (#35953) These are all the changes to the open-source Mojo docs intended for the release with 24.2. --------- Co-authored-by: Joe Loser Co-authored-by: Jack Clayton Co-authored-by: ematejska Co-authored-by: Arthur Evans modular-orig-commit: 64924205cdb683fe2083a5814bc3455047739e4f --- CONTRIBUTING.md | 130 ++++++- README.md | 64 ++-- docs/oss-material/CODE_OF_CONDUCT.md | 6 +- docs/oss-material/images/base-branch.png | Bin 0 -> 163923 bytes docs/oss-material/images/create-fork.png | Bin 0 -> 179367 bytes .../oss-material/images/nightly-extension.png | Bin 0 -> 116383 bytes stdlib/README.md | 24 +- stdlib/docs/development.md | 326 +++++++++++++++--- stdlib/docs/faq.md | 53 +-- stdlib/docs/governance-structure.md | 4 +- stdlib/docs/roadmap.md | 44 +-- stdlib/docs/style-guide.md | 108 +++--- stdlib/docs/vision.md | 64 ++-- 13 files changed, 584 insertions(+), 239 deletions(-) create mode 100644 docs/oss-material/images/base-branch.png create mode 100644 docs/oss-material/images/create-fork.png create mode 100644 docs/oss-material/images/nightly-extension.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e2c1ea68d..c4fc579813 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Mojo Contributor Guide +# Mojo contributor guide Welcome to the Mojo community! 🔥 We’re very excited that you’re interested in contributing to the project. To help you get started and ensure a smooth @@ -8,7 +8,7 @@ There are many ways to contribute to the project, from joining the [Discord community](https://www.discord.gg/modular), to filing bugs, to contributing documentation, examples, or code. -## Submitting Bugs +## Submitting bugs Reporting issues is a great way to contribute to the project. Mojo uses GitHub Issues for tracking bugs. @@ -21,25 +21,25 @@ Also, before opening a new issue, take a moment to search through the already submitted issues to avoid creating duplicate issues for the maintainers to address. -### Writing High-Quality Bugs +### Writing high-quality bugs We encourage you to provide as much information about the issue as practical. The more details you provide, the faster we can resolve the issue. The following is a template of the information that should accompany every submitted issue. -#### Issue Template +#### Issue template - **Summary.** A descriptive summary of the issue. - **Description.** A detailed account of the bug, including what was expected and what occurred. -- **Environment Details.** +- **Environment details.** - Mojo Compiler Version - Operating System version - Hardware Specifications -- **Severity/Frequency.** An assessment of the impact ranging from inconvenience +- **Severity/frequency.** An assessment of the impact ranging from inconvenience to a blocker. -## Contributing to Docs and Examples +## Contributing to docs and examples We’re happy to accept pull requests for the docs and examples. If your change is any one of the following, please create a pull request and we @@ -67,7 +67,7 @@ require difficult reviews and rework, or that might get rejected. See [Pull Requests](#pull-requests) for information on creating your first pull request. -## Contributing to the Standard Library +## Contributing to the standard library The standard library team is dedicated to creating a vibrant technical community around the Mojo programming language. Our vision includes a diverse and @@ -84,12 +84,12 @@ For more information on our priorities, see the following documents: For technical details on developing for the standard library, see the following documents: -- [Developing the Standard Library](./stdlib/docs/development.md) covers building, +- [Developing the standard library](./stdlib/docs/development.md) covers building, testing, and other information you’ll need to work in the standard library. - [Coding Standards and Style Guide](./stdlib/docs/style-guide.md) provides guidelines for writing code for the standard library. -### Accepting Open Source PRs +### Accepting open source PRs To ensure a streamlined process, contributors are encouraged to focus on enhancements, bug fixes, and optimizations aligned with the library's @@ -153,7 +153,7 @@ This process is heavily inspired by the process used by several other open-source projects. We’ll add more documentation in the future as we gain experience with the process. -## Pull Requests +## Pull requests You can use a pull request to propose a change or bug fix to the Mojo Standard Library, Mojo examples, or Mojo documentation. This page gives an overview of @@ -162,28 +162,122 @@ the process, especially for first-time contributors. **Note:** Pull requests should be submitted against the `nightly` branch, which represents the most recent nightly build. -### Pull Request Process +### Pull request process -#### 1. First-time checklist +#### First-time checklist Before you start your first pull request, please complete this checklist: - Read this entire contributor guide. - Read the [Code of Conduct](./CODE_OF_CONDUCT.md). -#### 2. Evaluate and get buy-in on the change +#### Evaluate and get buy-in on the change We want to be sure that you spend your time efficiently and prepare changes that aren’t controversial and get stuck in long rounds of reviews. See the sections on [Contributing to Docs and Examples](#contributing-to-docs-and-examples) and -[Contributing to the Standard Library](#contributing-to-the-standard-library) +[Contributing to the standard library](#contributing-to-the-standard-library) for more details. -#### 3. Create a pull request +#### Fork and clone the repo + +Go to the [Mojo repo](https://github.com/modularml/mojo) and click the fork +button: + +![Create Fork](./images/create-fork.png) + +Clone your forked repo locally with the command: + +```bash +git clone git@github.com:[your-username]/mojo.git +cd mojo +``` + +Add the upstream remote and fetch it: + +```bash +git remote add upstream git@github.com:modularml/mojo.git +git fetch upstream +``` + +#### Branching off nightly + +Make sure to branch off `nightly` to work on your PR: + +```bash +git checkout upstream/nightly +git checkout -b my-fix-pr +``` + +You should periodically make sure you've synced the latest changes, especially +before raising a PR: + +```bash +git fetch upstream +git rebase upstream/nightly +``` + +#### Getting the nightly Mojo compiler + +Now that you're on the nightly branch, you need to install the latest nightly +Mojo compiler: + +```bash +curl https://get.modular.com | sh - + +modular auth + +modular install nightly/mojo +``` + +If you already have an older `nightly/mojo` compiler, replace +`modular install nightly/mojo` with `modular update nightly/mojo`. + +Then, follow the instructions from the `modular` tool in adding the `mojo` +compiler to your `PATH` such as: + +```bash +echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc +echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc +``` + +If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. + +#### Mojo nightly vscode extension + +Install the [Mojo nightly VS Code +extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly): + + + +You can only have one Mojo extension enabled at a time, remember to switch back +when using the stable release! + +#### Create a pull request If your change is one of the improvements described above or it has been discussed and agreed upon by the project maintainers, please create a pull -request into the `nightly` branch and include the following: +request into the `nightly` branch. + +First push your changes: + +```bash +git push -u [your-username] my-fix-pr +``` + +You'll see a link to create a PR: + +```plaintext +remote: Create a pull request for 'my-fix-pr' on GitHub by visiting: +remote: https://github.com/jackos/mojo/pull/new/my-fix-pr +``` + +Make sure you point it to the `nightly` branch: + +![Base Branch](images/base-branch.png) + +Now fill out the details: - A short commit title describing the change. - A detailed commit description that includes rationalization for the change @@ -251,7 +345,7 @@ By making a contribution to this project, I certify that: this project or the open source license(s) involved. ``` -### Review Time SLA +### Review time SLA The team commits to reviewing submitted pull requests within a week of submission. diff --git a/README.md b/README.md index 9178a6dd24..fe65959e21 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,6 @@ and production by combining Python syntax and ecosystem with systems programming and metaprogramming features. Mojo is still young, but it is designed to become a superset of Python over time. -To use Mojo, you can install the MAX SDK or the standalone Mojo SDK: - -- [Get the MAX SDK.](https://docs.modular.com/engine/get-started) -- [Get the Mojo SDK.](https://docs.modular.com/mojo/manual/get-started/) - -Then follow the docs to [write your first Mojo -program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). -When you want to report issues or request features, [please create a GitHub -issue here](https://github.com/modularml/mojo/issues). See the [Mojo -Contributor Guide](https://www.notion.so/f527254bc46b4cd3ba4b34bd949d4e57?pvs=21) -for guidelines on filing good bugs. - This repo includes source code for: - Mojo examples @@ -30,30 +18,56 @@ This repo includes source code for: This repo has two primary branches: - The [`main`](https://github.com/modularml/mojo/tree/main) branch, which is in -sync with the last released version of Mojo. Use the examples here if you’re -using a release build of Mojo. +sync with the last stable released version of Mojo. Use the examples here if you’re +using a [release build of Mojo](#latest-released). - The [`nightly`](https://github.com/modularml/mojo/tree/nightly) branch, which is in sync with the Mojo nightly build and subject to breakage. Use this branch -for [contributions](./CONTRIBUTING.md). +for [contributions](./CONTRIBUTING.md), or if you're using the latest +[nightly build of Mojo](#latest-nightly). + +To learn more about Mojo, see the +[Mojo Manual](https://docs.modular.com/mojo/manual/). + +## Installing Mojo + +### Latest Released + +To install the last released build of Mojo, you can install the MAX SDK +or the standalone Mojo SDK: -This repo represents the Mojo open source effort. We are continuing to open -parts of the Mojo codebase. The challenge is that we use Mojo pervasively -inside Modular and we need to make sure that community contributions can -proceed smoothly with good build and testing tools that will allow this repo to -become the source of truth (right now it is not). We'll progressively improve -the tools and add more source code over time. +- [Get the MAX SDK](https://docs.modular.com/engine/get-started) +- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) -If you’d like to contribute to Mojo, please first read our [Contributor +Then follow the docs to [write your first Mojo +program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). + +### Latest Nightly + +The nightly Mojo builds are subject to breakage and provide an inside +view of how the development of Mojo is progressing. Use at your own risk +and be patient! Intall them using the instructions [here](./CONTRIBUTING.md). + +## Contributing + +When you want to report issues or request features, [please create a GitHub +issue here](https://github.com/modularml/mojo/issues). +See [here](./CONTRIBUTING.md) for guidelines on filing good bugs. + +We welcome contributions to this repo on the +[`nightly`](https://github.com/modularml/mojo/tree/nightly) +branch. If you’d like to contribute to Mojo, please first read our [Contributor Guide](https://github.com/modularml/mojo/blob/main/CONTRIBUTING.md). For more general questions or to chat with other Mojo developers, check out our [Discord](https://discord.gg/modular). -To learn more about Mojo, see the -[Mojo Manual](https://docs.modular.com/mojo/manual/). +## License + +This repository is licensed under the Apache License v2.0 with LLVM Exceptions +(see the LLVM [License](https://llvm.org/LICENSE.txt)). -## Thanks To Our Contributors +## Thanks to our contributors diff --git a/docs/oss-material/CODE_OF_CONDUCT.md b/docs/oss-material/CODE_OF_CONDUCT.md index abf01d33f1..f4f5ad5a25 100644 --- a/docs/oss-material/CODE_OF_CONDUCT.md +++ b/docs/oss-material/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Code of Conduct +# Code of conduct In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and @@ -7,7 +7,7 @@ body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. -## Our Standards +## Our standards All community forums and spaces are meant for professional interactions that are friendly, inclusive, helpful, and collaborative. Examples of behavior that @@ -32,7 +32,7 @@ participants include: - Conduct which could reasonably be considered inappropriate for the forum in which it occurs. -## Our Responsibilities +## Our responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in diff --git a/docs/oss-material/images/base-branch.png b/docs/oss-material/images/base-branch.png new file mode 100644 index 0000000000000000000000000000000000000000..a65ba47490281d93c0d5f6ce695f05bacd80c3cd GIT binary patch literal 163923 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 Q0tq3FvBRFMvGGm$52szTO#lD@ literal 0 HcmV?d00001 diff --git a/docs/oss-material/images/create-fork.png b/docs/oss-material/images/create-fork.png new file mode 100644 index 0000000000000000000000000000000000000000..a708e4a4e459b15eab96c8c6d2c622f5b95adc93 GIT binary patch literal 179367 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

ZmP4lSCzr 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 ~/.zshrc -echo 'export PATH="/Users/joe/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc +./stdlib/scripts/build-stdlib.sh ``` -## Cloning the Repository +## Testing the standard library + +### Installing unit test dependencies + +To run the unit tests, you first need to install `lit`: ```bash -git clone https://github.com/modularml/mojo/ +python3 -m pip install lit ``` -## Building the Standard Library - -To build the Standard Library, you can run the script -[`build-stdlib.sh`](../scripts/build-stdlib.sh) from the `scripts` directory -inside the `stdlib` directory. This will create a build artifacts directory, -`build`, in the top-level of the repo and produce the `stdlib.mojopkg` inside. +And make sure that `FileCheck` from LLVM is on path. If your are on macOS, you +can `brew install llvm` and add it to your path in `~/.zshrc` or `~/.bashrc`: ```bash -./stdlib/scripts/build-stdlib.sh +export PATH="/opt/homebrew/opt/llvm/bin:$PATH" ``` -## Testing the Standard Library +If you are on Ubuntu you can: + +```bash +sudo apt update -### Installing Unit Test Dependencies +sudo apt install llvm +``` -To run the unit tests, you first need to install a few dependencies: +And it will be available in `PATH`. -1. [`lit`](https://llvm.org/docs/CommandGuide/lit.html) - can be downloaded via - `python3 -m pip install lit` -2. [`FileCheck`](https://llvm.org/docs/CommandGuide/FileCheck.html) - is part of - the LLVM distribution you can obtain from your package manager +In the near future, we will be moving away from `FileCheck` in favor of writing +the unit tests using our own `testing` module and remove this dependency +requirement for contributors. We are happy to welcome contributions in this +area! -When you download `lit`, make sure you add the path to `lit` to your `PATH` if -needed. Some of the tests use `FileCheck` or `not` binaries that ship with LLVM. -For example, if you download LLVM via `homebrew`, these would be in -`/opt/homebrew/Cellar/llvm//bin`. You need to add this path -to your `PATH` in order to run these tests. In the near future, we will be -moving away from `FileCheck` in favor of writing the unit tests using our own -`testing` module and remove this dependency requirement for contributors. We -are happy to welcome contributions in this area! +### Running the standard library tests -### Running the Standard Library Tests +We provide a simple Bash script to build the standard library package and +`test_utils` package that is used by the test suite. -We provide a simple Bash script to build the Standard Library package and -`test_utils` package that is used by the test suite. Just run -`./stdlib/scripts/run-tests.sh` which will produce the necessary -`mojopkg` files inside your `build` directory and then run -`lit -sv stdlib/test`. +Just run `./stdlib/scripts/run-tests.sh` which will produce the necessary +`mojopkg` files inside your `build` directory, and then run `lit -sv +stdlib/test`. ```bash ./stdlib/scripts/run-tests.sh + +lit -sv stdlib/test ``` +All the tests should pass on the `nightly` branch with the nightly Mojo +compiler. If you've pulled the latest changes and they're still failing please +[open a GitHub +issue](https://github.com/modularml/mojo/issues/new?assignees=&labels=bug%2Cmojo&projects=&template=mojo_bug_report.yaml&title=%5BBUG%5D). + ### Running a subset of the Standard Library Unit Tests If you’d like to run just a subset of the tests, feel free to use all of the normal options that the `lit` tool provides. For example, to run just the -builtin and collections tests, you can +`builtin` and `collections` tests: ```bash lit -sv stdlib/test/builtin stdlib/test/collections @@ -100,9 +105,9 @@ If you run into any issues when running the tests, [please file an issue](https://github.com/modularml/mojo/issues) and we’ll take a look. -## Formatting Changes +## Formatting changes -Please make sure your changes are formatted before submitting a Pull Request. +Please make sure your changes are formatted before submitting a pull request. Otherwise, CI will fail in its lint and formatting checks. The `mojo` compiler provides a `format` command. So, you can format your changes like so: @@ -119,3 +124,222 @@ git diff origin/main --name-only -- '*.mojo' | xargs mojo format You can also consider setting up your editor to automatically format Mojo files upon saving. + +## Tutorial + +Here is a complete walkthrough, showing how to make a change to the Mojo +standard library, test it, and raise a PR. + +First, follow everything in the [prerequisites](#prerequisites). + +__IMPORTANT__ We'll be in the `mojo/stdlib` folder for this tutorial, check and +make sure you're in that location if anything goes wrong: +`cd [path-to-repo]/stdlib` + +### A simple change + +Let's try adding a small piece of functionality to `path.mojo`: + +```mojo +# ./stdlib/src/pathlib/path.mojo + +fn print_cwd() raises: + print("cwd:", cwd()) +``` + +And make sure you're importing it from the module root: + +```mojo +# ./stdblib/src/pathlib/__init__.mojo + +from .path import ( + DIR_SEPARATOR, + cwd, + print_cwd, + Path, +) +``` + +Now you can create a temporary file named `main.mojo` for trying out the new +behavior. You wouldn't commit this file, it's just to experiment with the +functionality before you write tests: + +```mojo +# ./stdlib/main.mojo + +from src import pathlib + +def main(): + pathlib.print_cwd() +``` + +Now when you run `mojo main.mojo` it'll reflect the changes: + +```plaintext +cwd: /Users/jack/src/mojo/stdlib +``` + +### A change with dependencies + +Here's a more tricky example that modifies multiple standard library files that +depend on each other. + +Try adding this to `os.mojo`, which depends on what we just added to +`pathlib.mojo`: + +```mojo +# ./stdlib/src/os/os.mojo + +import pathlib + +fn print_paths() raises: + pathlib.print_cwd() + for path in cwd().listdir(): + print(path[]) +``` + +This won't work because it's importing `pathlib` from the `stdlib.mojopkg` that +comes with Mojo. We'll need to build our just-modified standard library running +the command: + +```bash +./scripts/build-stdlib.sh +``` + +This builds the standard library and places it at the root of the repo in +`../build/stdlib.mojopkg`. Now we can edit `main.mojo` to use the normal import +syntax: + +```mojo +import os + +def main(): + os.print_paths() +``` + +We also need to set the following environment variable that tells Mojo to +prioritize imports from the standard library we just built, over the one that +ships with Mojo: + +```bash +MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build mojo main.mojo +``` + +Which now outputs: + +```plaintext +cwd: /Users/jack/src/mojo/stdlib +main.mojo +test +docs +scripts +src +``` + +### Bonus tip: fast feedback loop + +If you like a fast feedback loop, try installing the file watcher `entr`, which +you can get with `sudo apt install entr` on Linux, or `brew install entr` on +macOS. Now run: + +```bash +export MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build + +ls **/*.mojo | entr sh -c "./scripts/build-stdlib.sh && mojo main.mojo" +``` + +Now, every time you save a Mojo file, it packages the standard library and +runs `main.mojo`. + +### Running tests + +If you haven't already, follow the steps at: +[Installing unit test dependencies](#installing-unit-test-dependencies) + +### Adding a test + +This will show you how the `FileCheck` utility works. First, turn it on by +adding it to the end of line 7 in `./stdlib/test/pathlib/test_pathlib.mojo`: + +```mojo +# RUN: %mojo -debug-level full -D TEMP_FILE=%t %s | FileCheck %s +``` + +Now we can add the test and force it to fail: + +```mojo +# CHECK-LABEL: test_print_cwd + +def test_print_cwd(): + print("== test_print_cwd") + + # CHECK: This will fail + print_cwd() +``` + +Don't forget to call it from `main()`: + +```bash +def main(): + test_print_cwd() +``` + +Now, instead of testing all the modules, we can just test `pathlib`: + +```bash +lit -sv test/pathlib +``` + +This will give you an error showing exactly what went wrong: + +```plaintext +/Users/jack/src/mojo-toy-2/stdlib/test/pathlib/test_pathlib.mojo:27:11: +error: CHECK: expected string not found in input + +# CHECK: This will fail + ^ +:1:18: note: scanning from here +== test_print_cwd + ^ +``` + +Lets fix the test that we just added: + +```plaintext +# CHECK-LABEL: test_print_cwd + +def test_print_cwd(): + print("== test_print_cwd") + + # CHECK: cwd: + print_cwd() +``` + +We're now checking that `print_cwd` is prefixed with `cwd:` just like the +function we added. Run the test again: + +```plaintext +Testing Time: 0.65s + +Total Discovered Tests: 1 + Passed: 1 (100.00%) +``` + +Success! Now we have a test for our new function. + +The last step is to [run mojo format](#formatting-changes) on all the files. + +### Raising a PR + +Make sure that you've had a look at all the materials from the standard library +[README.md](../README.md). This change wouldn't be accepted because it's missing +tests, and doesn't add useful functionality that warrants new functions. If you +did have a worthwhile change you wanted to raise, follow the steps to +[create a pull request](../../CONTRIBUTING.md#create-a-pull-request). + +Congratulations! You've now got an idea on how to contribute to the standard +library, test your changes, and raise a PR. + +If you're still having troubles make sure to reach out on +[GitHub](https://github.com/modularml/mojo/discussions/new?category=general) or +[Discord](modul.ar/discord)! diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index 8fc6d46217..be9780fa05 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -1,59 +1,68 @@ -# Frequently Asked Questions +# Frequently asked questions -A lot of questions about Mojo as a whole can be answered on the +A lot of questions about Mojo as a whole are answered in the [FAQ on our website](https://docs.modular.com/mojo/faq). -This document is specifically focused on the Standard Library with contributors +This FAQ is specifically focused on the standard library with contributors in mind. -## Standard Library +## Contributing & development -### Contributing & Development +### 1. What platforms does Mojo support? -#### 1. What platforms does Mojo support? - -The nightly Mojo compiler currently works on Linux and macOS. The Standard -Library works on both platforms too in conjunction with the compiler. Windows is +The nightly Mojo compiler currently works on Linux and macOS. The standard +library works on both platforms too in conjunction with the compiler. Windows is currently not a supported platform. -#### 2. I hit a bug! What do I do? +### 2. I hit a bug! What do I do? Don’t Panic! 😃 Check out our [bug submission guide](../../CONTRIBUTING.md#submitting-bugs) to make sure you include all the essential information to avoid unnecessary delays in getting your issues resolved. -### Standard Library Code +## Standard library code -#### 1. Why do we have both `AnyRegType` and `AnyType`? +### 1. Why do we have both `AnyRegType` and `AnyType`? This is largely a historical thing as the library only worked on `AnyRegType` when it was first written. As we introduced the notion of memory-only types and -traits, `AnyType` was born. Over time in Q2 2024, we expect to rewrite nearly +traits, `AnyType` was born. Over time, we expect to rewrite nearly the entire library to have everything work on `AnyType` and be generalized to -not just work on `AnyRegType`. Several things need to be worked in tandem with +not just work on `AnyRegType`. Several things need to happen in tandem with the compiler team to make this possible. -#### 2. LLCL and MLIR ops are private APIs? +### 2. Are the MLIR dialects private? -LLCL entry points and MLIR operators are private undocumented APIs. We provide +The standard library makes use of internal MLIR dialects such as `pop`, `kgen`, +and `lit`. Currently, these are private, undocumented APIs. We provide no backward compatibility guarantees and therefore they can change at any time. -These particular areas of the standard library are in active development and we -commit to releasing them when their public-facing API has stabilized. +These particular areas of the compiler and standard library are in active +development and we are exploring how we can release them when their +public-facing API has stabilized. + +### 3. What is the compiler-runtime? + +Mojo depends on certain features that are still written in C++, collectively +called "the compiler runtime." This may manifest in the standard library code +through references like `KGEN_CompilerRT_LLCL_CreateRuntime`. Like the MLIR +dialects, the compiler runtime is currently private and undocumented. + +We plan on reducing the C++ dependencies in the future. -#### 3. Why are some Standard Library modules missing from the open-source code? +### 4. Why are some standard library modules missing from the open-source code? -When we were preparing to open source the Standard Library, we realized that +When we were preparing to open source the standard library, we realized that some modules weren't ready for open-source release. For example: - Some modules are expected to change rapidly in the near term, and need to stabilize. - Some modules are too tightly integrated into other portions of MAX and need to be refactored. -- Some modules may have proprietary aspects that require additional review and +- Some modules have proprietary aspects that require additional review and refinement. For the short term, we've left these modules as closed source. The shipped -Mojo SDK contains the pre-built Mojo packages for these closed source modules +Mojo SDK contains the pre-built Mojo packages for these closed-source modules in addition to the open-source modules, so Mojo users still have the full set of primitives available to them. diff --git a/stdlib/docs/governance-structure.md b/stdlib/docs/governance-structure.md index 71b97bee08..9a26106e40 100644 --- a/stdlib/docs/governance-structure.md +++ b/stdlib/docs/governance-structure.md @@ -1,4 +1,4 @@ -# Mojo Standard Library Governance Structure +# Mojo standard library governance structure **Leads:** Rob Parolin ([@rparolin](https://github.com/rparolin)), @@ -8,6 +8,6 @@ Leads are responsible for reviewing code changes, reviewing proposals for evolving the standard library, and setting the roadmap. Other projects have governance structures with the concept of sub-teams. The -Mojo Standard Library isn’t large enough yet to support that level of +Mojo standard library isn’t large enough yet to support that level of governance. It very well may in the future but as we scale we will re-evaluate our governance structure and make changes accordingly. diff --git a/stdlib/docs/roadmap.md b/stdlib/docs/roadmap.md index 6b89c19d7f..3ff96a1c24 100644 --- a/stdlib/docs/roadmap.md +++ b/stdlib/docs/roadmap.md @@ -1,47 +1,47 @@ -# Mojo Standard Library Roadmap +# Mojo standard library roadmap -## Roadmap Cadence +This is a high-level plan for the Mojo standard library. -The stdlib open-source repository has opted for a 6-month roadmap refresh -cadence that aligns with our internal workflows. The roadmap updates act as a -forcing function for discussions with the Mojo community to ensure stdlib -contributors both internally and externally are aligned on the future technical -direction. +We plan to update this roadmap approximately every 6 months, in alignment with +Modular's internal workflows. The roadmap updates act as a forcing function for +discussions with the Mojo community to ensure the standard library contributors +both internally and externally are aligned on the future technical direction. -## 2024 Q2+ Roadmap +## 2024 Q2+ roadmap -The following are high-level themes the Mojo Standard Library team will be -working on over the next 6-months. Keep in mind that Mojo and the Mojo Standard -Library are in early development with many features landing over the months -ahead. Currently, that means we are focused on the core system programming -features that are essential to Mojo's mission. +The following are high-level themes the Mojo standard library team will be +working on over the next 6 months. Keep in mind that Mojo and the Mojo standard +library are still in early development and many features will land in the +months ahead. Currently, that means we are focused on the core system +programming features that are essential to [Mojo's +mission](https://docs.modular.com/mojo/why-mojo). -### Core Library Improvements +### Core library improvements -- Remove `AnyRegType` in the Standard Library in favor of `AnyType`. +- Remove `AnyRegType` in the standard library in favor of `AnyType`. -- Unify Pointer and AnyPointer. +- Unify `Pointer` and `AnyPointer`. - Apply `Reference` types and lifetimes throughout APIs and types. - Design API conventions and expected behavior for core collection types such - as `List`, `String`, `Dict` . + as `List`, `String`, and `Dict`. -### Generic Programming Improvements +### Generic programming improvements - Define core traits for generic programming models. - Define an iterator model and implement iterator support for core types. -- Standardize collection meta type names (eg. *element_type*, *key_type*, and - *value_type*). +- Standardize collection meta type names (such as *element_type*, *key_type*, + and *value_type*). -### Improve Python Interop +### Improve Python interop - Improve `PythonObject` (including `object`) using new Mojo language features. -### Performance Improvements +### Performance improvements - Set up performance benchmarking infrastructure along with regression tests for perf-sensitive data structures and algorithms. diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 81ec939f99..a7aff2e206 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -1,20 +1,22 @@ -# Coding Standards & Style Guide +# Coding standards & style guide -This document describes conventions that Mojo Standard Library code should -adhere to. Its coverages range from non-semantic conventions like code -formatting, to semantics like value lifecycle behavior that Standard Library +This document describes conventions that Mojo standard library code should +adhere to. Its coverage ranges from non-semantic conventions like code +formatting, to semantics like value lifecycle behavior that standard library types should generally conform to. -## Structure and Formatting +## Structure and formatting -### Files & Layout +### Files & layout -#### File Structure +#### File structure -The Mojo Standard Library uses the following high-level organization. Group -related functions within the same file. Group related files within the same -directory. Do not add dependencies to the stdlib module because, by definition, -it is required to be a leaf dependency. +The Mojo standard library uses the following high-level organization: + +- Group related functions within the same file. +- Group related files within the same directory. +- Do not add dependencies to the `stdlib` module because, by definition, it is +required to be a leaf dependency. ```text > stdlib # stdlib root directory @@ -30,11 +32,11 @@ it is required to be a leaf dependency. All Mojo source files must end with the extension `.mojo` or `.🔥`. -#### Mojo Format +#### Mojo format Mojo provides a command line formatting utility, `mojo format`, designed to automatically format your code according to the official Mojo style guidelines. -It adjusts indentation, spacing, and line breaks making code more readable and +It adjusts indentation, spacing, and line breaks, making code more readable and consistent. ```bash @@ -43,7 +45,7 @@ All done! ✨ 🍰 ✨ 1 file left unchanged. ``` -Unless otherwise noted, Mojo Standard Library code should follow the formatting +Unless otherwise noted, Mojo standard library code should follow the formatting produced by `mojo format`. #### Whitespace @@ -54,13 +56,13 @@ produced by `mojo format`. *We encourage updating your editor settings to be consistent with the above.* -#### Column Limit +#### Column limit -Mojo code has a column limit of 80 characters. +Mojo code has a column limit (line length) of 80 characters. -#### File License Header +#### File license header -Every file in the open source Mojo Standard Library should begin with the +Every file in the open source Mojo standard library should begin with the following license information header: ```mojo @@ -78,9 +80,9 @@ following license information header: # ===----------------------------------------------------------------------=== # ``` -#### Code Header Comments +#### Code header comments -Code in the Mojo Standard Library should use the following conventional +Code in the Mojo standard library should use the following conventional structure of header comments separating the various kinds of methods that can be defined on structs. @@ -91,7 +93,7 @@ defined on structs. struct MyStruct(Sized, Stringable): - """This is MyStruct.""" + """Description goes here.""" var field: Int @@ -117,14 +119,17 @@ struct MyStruct(Sized, Stringable): # ===------------------------------------------------------------------=== # ``` -## Code Conventions +## Code conventions + +### Identifier naming conventions -### Identifier Naming Conventions +There are several ways to capitalize and separate words, known as "case +styles." By following the same set of case styles in our code, Mojo developers +ensure their code is accessible and understandable to others in the community. -The following are the recommended types of `case styles` used in Mojo Standard -Library code. +This first table is just a definition of the various "case styles." -| Case Style | Description | Example +| Case style | Description | Example |------------------------|-------------------------------------------|----------------- | `snake_case` | All lowercase with underscores | `variable_name` | `PascalCase` | Each word starts with an uppercase letter | `StructName` @@ -132,11 +137,9 @@ Library code. | `kebab-case` | All lowercase with hyphens | `project-name` | `flatcase` | All lowercase without separators | `basename` -The following table outlines the appropriate use of various casing styles in the -Mojo Standard Library. By following these conventions, Mojo developers ensure -their code is accessible and understandable to others in the community. +The following table shows our preferred use of different case styles. -| Item Kind | Example | Case Convention +| Code kind | Example | Case style |----------------------|--------------------------------|--------------------------- | `fn` / `def` | `fn engage_hyperdrive()` | `snake_case` | `struct` | `struct Point` | `PascalCase` @@ -154,15 +157,14 @@ their code is accessible and understandable to others in the community. | `fn` type parameter | `fn do_it[Action: Actionable](action: Action)` | `PascalCase` | `fn` value parameter | `fn repeat[Count: Int]()` | `PascalCase` -The demonstrated style choices intend to illustrate the various naming -conventions used in the Standard Library. However, these choices may not match -the existing style in all code in its current state. When preparing a change, it -is important to adhere to the style and naming conventions already established -in that module. Therefore, if the module you are working on uses a different -style, continue using that style to maintain consistency. We are not currently -accepting pull requests that propose extensive formatting or renaming changes. +Although these are our style conventions, not all code currently adheres to it. +When preparing a new change, it is important to adhere to the style and naming +conventions already established in that module. Therefore, if the module you +are working on uses a different style, continue using that style to maintain +consistency. We are not currently accepting pull requests that propose +extensive formatting or renaming changes. -### Naming Guidelines +### Naming guidelines #### ℹ️ Prefer descriptive parameter names over single-letter names @@ -178,7 +180,7 @@ struct Array[LENGTH: Int, ElementType: Movable] # 🔴 Avoid struct Array[ElementType: Movable, Length: Int] # 🟢 Preferred ``` -### Container Lifecycle Semantics +### Container lifecycle semantics #### ℹ️ Prefer explicit copy constructors; avoid allowing implicit copies @@ -195,7 +197,7 @@ safe and inexpensive. However, copying types that dynamically allocate memory can be expensive. This includes common types like `List`, `Dict`, `Set`, `Tensor`, and `String`. -Some Standard Library types allow implicit copies where they shouldn’t. We will +Some standard library types allow implicit copies where they shouldn’t. We will resolve this shortly as new Mojo language features are shipped to help with this very situation. @@ -203,7 +205,7 @@ When designing a new type, don’t allow implicit copies unless the copy is trivial (order `O(1)`). In other words, don’t define a `__copyinit__()` function if the copy is expensive. Instead, define an *explicit* copy constructor: an `__init__()` constructor that takes a value of -the same type. +the same type: ```mojo struct MyStruct: @@ -212,29 +214,29 @@ struct MyStruct: # do a deep copy of MyStruct ``` -### Import Statements +### Import statements -- Explicitly import entities (functions, structs, aliases) used rather - than relying on transitive imports. -- Import only what you use; in general, prefer not to use +- Explicitly import entities used (functions, structs, aliases), rather + than rely on transitive imports. +- Import only what you use; in general, avoid using `from some_package import *`. - Import statements should be sorted lexicographically. -### API Docstrings +### API docstrings -Every public function and public struct (including data fields) in the Standard -Library must have doc strings. There is tooling to ensure public functions -adhere to the doc string validation. +Every public function and public struct (including data fields) in the standard +library must have docstrings (code comments that describe the API behavior). +Mojo includes tooling to ensure that public functions include docstrings. You can run `./stdlib/scripts/check-doc-strings.sh` to validate -doc strings. If the command exits with a 0 exit code, the doc strings are +docstrings. If the command exits with a `0` exit code, the docstrings are compliant; otherwise, an error will be shown. This is also enforced by the LSP with warnings for anything that doesn’t conform, you can generate docstrings based on the signature using an LSP Quick Fix: -![VS Code documentation lint quick fix](./images/doc-lint-quick-fix.png) + -We follow the Google convention for +We follow Google's Python convention for [docstrings outlined here](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods) which looks like this: @@ -273,7 +275,7 @@ fn add_param_arg[foo: Int](bar: Int) -> Int: ### Testing -#### Unit Test Filenames +#### Unit test filenames All test filenames should be prefixes with `test_`. For example `test_sort.mojo`. diff --git a/stdlib/docs/vision.md b/stdlib/docs/vision.md index ae8774b6fb..a015be8c79 100644 --- a/stdlib/docs/vision.md +++ b/stdlib/docs/vision.md @@ -1,23 +1,26 @@ # Vision +This page outlines the principles we aspire to follow and the more concrete +objectives and goals we aim to accomplish in the Mojo standard library. + ## Principles -The following are “North Star” principles for the Mojo Standard Library. -These principles will inform multiple future decisions from what features we +The following are “North Star” principles for the Mojo standard library. +These principles will inform multiple future decisions, from what features we work on to what bugs we prioritize during triage. In short, the standard library vision is the ideal we may never reach, but we collectively show up every day to work towards it. - **Foster a vibrant community collaborating globally.** The community is -encour aged to engage with the standard library and language evolution. We +encouraged to engage with the standard library and language evolution. We intend to ignite enthusiasm to contribute to the expanding Mojo package ecosystem. -- The Standard Library prioritizes - **Performance > Safety > Portability > Debuggability.** +- The standard library prioritizes + **Performance `>` Safety `>` Portability `>` Debuggability.** -- **Respectable performance by default.** Standard Library intends to provide -respectable performance by default — but not perfect performance. We support +- **Respectable performance by default.** The standard library should provide +respectable performance by default, but not perfect performance. We support low-level controls that enable performance tuning by systems engineers. While providing consistently strong performance over time, we do so with minimal regressions. @@ -25,34 +28,33 @@ regressions. - **Portability by default.** The use of MLIR enables portability across a variety of platforms without writing per-platform code in the standard library. -- **The standard library does not have special privileges.** Standard Library -types are not special and do not enjoy elevated privileges over user-contributed -code. This empowers Mojicians to create primitives equally as expressive as core -language primitives. +- **The standard library does not have special privileges.** The standard +library types are not special and do not enjoy elevated privileges over +user-contributed code. This empowers Mojicians to create primitives equally as +expressive as core language primitives. -- **Fully utilize available hardware.** The Standard Library should not inhibit -using any available hardware on the system. On the contrary, standard library -primitives will enable users to maximize the utility of all available system +- **Fully utilize available hardware.** The standard library should not restrict +the use of available hardware on the system. On the contrary, standard library +primitives should enable users to maximize the utility of all available system hardware. -- **Standard Library features prioritize AI workload optimizations.** Mojo +- **Standard library features prioritize AI workload optimizations.** Mojo ultimately aims to be a multi-purpose programming language used to solve problems in the systems programming domain. However, we will prioritize standard library features and optimizations that improve the state of the art for AI. -## What's Not in the Vision +### Non-principles -We reject the following vision statements, and the reasoning for each is written -inline. +We reject the following principle statements: - **Tensor operations are first-class citizens.** Mojo has a prime directive to optimize AI workloads. However, certain core AI primitives are tightly integrated with the MAX engine architecture, and will remain part of the MAX engine codebase. -## Objectives and Goals +## Objectives and goals -- Make unsafe or risky things explicit. Software using unsafe constructs is +- **Make unsafe or risky things explicit.** Software using unsafe constructs is inevitable, but it must be minimized and explicit to the reader. Safe things shouldn’t look like unsafe ones and unsafe constructs should leave artifacts to see in the code. @@ -65,19 +67,19 @@ inline. out of the box for kernel developers to use. The language runtime provides a decently performing default global allocator implementation (eg. thread local caches, automatic slab-size scaling based on heuristics, virtual memory-based - defragmentation, and more…). + defragmentation, and more). - **First-class support for parallelism and concurrency.** To fully utilize available hardware, the standard library will provide a complete suite of - primitives maximizing the parallelism potential of the system. + primitives that maximize the parallelism potential of the system. - **First-class debugging features.** Integration with Mojo debugger by - incorporating LLDB visualizers for the Standard Library types and collections + incorporating LLDB visualizers for the standard library types and collections to make debugging easy. -- **Consistent API and behavior across all stdlib primitives.** A higher-level - goal of the stdlib is to be an example of well-written mojo code. For example, - all collections should behave consistently with: +- **Consistent API and behavior across all primitives.** A higher-level goal of + the standard library is to be an example of well-written mojo code. For + example, all collections should behave consistently with: - Default-constructed collections do not allocate memory unless they are holding an element. @@ -87,15 +89,15 @@ inline. - Naming of public aliases/parameters common among collections (akin to `value_type` and friends in C++). -- **Interoperability with Python code** allows progressively migrating code to +- **Interoperability with Python code**. Allows progressively migrating code to Mojo over time to not force an entire rewrite just to improve the performance of code where it matters most. -## Non-Goals +### Non-goals -While some of these may be common goals of modern programming languages, the -value doesn’t outweigh the costs for us right now as we are moving fast to build -the language. While we don’t actively attempt to break the following we provide +While some of the following may be common goals of other languages, the +values don't outweigh the costs for us right now as we are moving fast to build +the language. While we don’t actively attempt to break the following, we provide no guarantees that they work — especially over multiple releases. - Stable ABI between language/compiler and library. From cfe777bec1545a7ee8cfbe128382a37ef62de06d Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 10:55:22 -0700 Subject: [PATCH 0004/2019] [Docs] Update changelog for 24.2. (#35920) (#35965) Trying something new here, dividing the items up a little differently to see if we can make them a little more manageable. cherrypick [Internal Link] Co-authored-by: Arthur Evans modular-orig-commit: b82c38b4535e532d5903c60102436311d84dae65 --- docs/changelog-released.md | 407 +++++++++++++++++++++++++++++++++++++ docs/changelog.md | 288 +------------------------- 2 files changed, 409 insertions(+), 286 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index c595ac18e9..df6ab829ed 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -30,6 +30,413 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## v24.2 (2024-03-28) + +### 🔥 Legendary + +- The Mojo standard library is now open source! Check out the + [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) + for everything you need to get started. + +- Structs and other nominal types are now allowed to implicitly conform to + traits. A struct implicitly conforms to a trait if it implements all the + requirements for the trait. For example, any struct that implements the + `__str__()` method implicitly conforms to `Stringable`, and is usable with + the `str()` built-in function. + + ```mojo + @value + struct Foo: + fn __str__(self) -> String: + return "foo!" + + fn main(): + print(str(Foo())) # prints 'foo!' + ``` + + We still strongly encourage you to explicitly list the traits a struct + conforms to when possible: + + ```mojo + @value + struct Foo(Stringable): ... + ``` + + Not only is this useful for documentation and for communicating intentions, + but in the future, explicit conformance will be useful for features like + default methods and extensions. + +- Mojo's Python interoperability now supports passing keyword arguments to + Python functions: + + ```mojo + from python import Python + + def main(): + plt = Python.import_module("matplotlib.pyplot") + plt.plot((5, 10), (10, 15), color="red") + plt.show() + ``` + +### Language changes + +#### ⭐️ New + +- Mojo now has support for variadic keyword arguments, often referred to as + `**kwargs`. This means you can now declare and call functions like this: + + ```mojo + fn print_nicely(**kwargs: Int) raises: + for key in kwargs.keys(): + print(key[], "=", kwargs[key[]]) + + # prints: + # `a = 7` + # `y = 8` + print_nicely(a=7, y=8) + ``` + + There are currently a few limitations: + + - The ownership semantics of variadic keyword arguments are always `owned`. + This is applied implicitly, and cannot be declared otherwise: + + ```mojo + # Not supported yet. + fn borrowed_var_kwargs(borrowed **kwargs: Int): ... + ``` + + - Functions with variadic keyword arguments cannot have default values for + keyword-only arguments. For example: + + ```mojo + # Not allowed yet, because `b` is keyword-only with a default. + fn not_yet(*, b: Int = 9, **kwargs: Int): ... + + # Okay, because `c` is positional-or-keyword, so it can have a default. + fn still_works(c: Int = 5, **kwargs: Int): ... + ``` + + - Dictionary unpacking is not supported yet: + + ```mojo + fn takes_dict(d: Dict[String, Int]): + print_nicely(**d) # Not supported yet. + ``` + + - Variadic keyword _parameters_ are not supported yet: + + ```mojo + # Not supported yet. + fn var_kwparams[**kwparams: Int](): ... + ``` + +#### 🦋 Changed or removed + +- `let` declarations now produce a compile time error instead of a warning, + our next step in [removing let + declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md). + The compiler still recognizes the `let` keyword for now in order to produce + a good error message, but that will be removed in subsequent releases. + +- Mojo now warns about unused values in both `def` and `fn` declarations, + instead of completely disabling the warning in `def`s. It never warns about + unused `object` or `PythonObject` values, tying the warning to these types + instead of the kind of function they are unused in. This will help catch API + usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. + +- For the time being, dynamic type values will be disabled in the language. For + example, the following will now fail with an error: + + ```mojo + var t = Int # dynamic type values not allowed + + struct SomeType: ... + + takes_type(SomeType) # dynamic type values not allowed + ``` + + We want to take a step back and (re)design type valued variables, + existentials, and other dynamic features. This does not affect type valued + **parameters**, so the following works as before: + + ```mojo + alias t = Int # still 🔥 + + struct SomeType: ... + + takes_type[SomeType]() # already 🔥 + + >fn uses_trait[T: SomeTrait](value: T): ... # still 🔥 + ``` + +- The `*_` expression in parameter expressions is now required to occur at the + end of a positional parameter list, instead of being allowed in the middle. + + ```mojo + # No longer supported + alias FirstUnbound = SomeStruct[*_, 42] + alias MidUnbound = SomeStruct[7, *_, 6] + # Still supported + alias LastUnbound = SomeStruct[42, *_] + ``` + + We narrowed this because we want to encourage type designers + to get the order of parameters right, and want to extend `*_` to support + keyword parameters as well in the future. + +### Standard library changes + +#### ⭐️ New + +- `DynamicVector` has been renamed to + [`List`](/mojo/stdlib/collections/list.html#list), and has moved from the + `collections.vector` module to the `collections.list` module. In addition: + + - You can now construct a `List` from a variadic number of values. For + example: + + ```mojo + var numbers = List[Int](1, 2, 3) + ``` + + - `List` and + [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + types now support negative indexing. This means that you can write `vec[-1]` + which is equivalent to `vec[len(vec)-1]`. + + - `List.push_back()` has been removed. Please use the `append()` function + instead. + +- The [`print()`](/mojo/stdlib/builtin/io#print) function now takes `sep` and + `end` keyword arguments. This means that you can write: + + ```mojo + print("Hello", "Mojo", sep=", ", end="!!!\n") # prints Hello, Mojo!!! + ``` + + `sep` defaults to the empty string and `end` defaults to "\n". + + Also, the `print_no_newline()` function has been removed. Please use + `print(end="")` instead. + +- The [`FloatLiteral`](/mojo/stdlib/builtin/float_literal#floatliteral) type is + now an infinite-precision nonmaterializable type. This means you can do + compile-time calculations using `FloatLiteral` without rounding errors. When + materialized at runtime, a `FloatLiteral` value is converted to a + [`Float64`](/mojo/stdlib/builtin/simd). + + ```mojo + # third is an infinite-precision FloatLiteral value + alias third = 1.0 / 3.0 + # t is a Float64 + var t = third + ``` + +- String types all conform to the + [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means + that you can now call `int("123")` to get the integer `123`. If the integer + cannot be parsed from the string, then an error is raised. + +- The `Tensor` type now has [`argmax()`](/mojo/stdlib/tensor/tensor#argmax) and + [`argmin()`](/mojo/stdlib/tensor/tensor#argmin) functions to compute the + position of the max or min value. + +- Added a new + [`collections.OptionalReg`](/mojo/stdlib/collections/optional#optionalreg) + type, a register-passable alternative to + [`Optional`](/mojo/stdlib/collections/optional#optional). + +- The [`ulp()`](/mojo/stdlib/math/math#ulp) function has been added to the + `math` module. This allows you to get the units of least precision (or units + of last place) of a floating point value. + +#### 🦋 Changed + +- The + [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable#equalitycomparable) + trait now requires the `__ne__()` method for conformance in addition to the + previously required `__eq__()` method. + +- Many types now declare conformance to `EqualityComparable` trait. + +- [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) parameter order + has changed to `StaticTuple[type, size]` for consistency with `SIMD` and + similar collection types. + +- The signature of the + [`elementwise()`](/mojo/stdlib/algorithm/functional#elementwise) function has + been changed. The new order is is `function`, `simd_width`, and then `rank`. + As a result, the rank parameter can now be inferred and one can call + `elementwise()` without it: + + ```mojo + elementwise[func, simd_width](shape) + ``` + +- `PythonObject` is now register-passable. + +- `PythonObject.__iter__()` now works correctly on more types of iterable Python + objects. Attempting to iterate over non-iterable objects will now raise an + exception instead of behaving as if iterating over an empty sequence. + `__iter__()` also now borrows `self` rather than requiring `inout`, allowing + code like: + + ```mojo + for value in my_dict.values(): + ... + ``` + +#### 🚚 Moved + +- We took the opportunity to rehome some modules into their correct package + as we were going through the process of open-sourcing the Mojo standard + library. Specifically, the following are some breaking changes worth + calling out. Please update your import statements accordingly. + + - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), + [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer), and friends have moved + from the `memory` package into a new `buffer` package. + + ```mojo + from buffer import Buffer, NDBuffer + ``` + + - `utils.list`, including the [`Dim`](/mojo/stdlib/buffer/list#dim) and + [`DimList`](/mojo/stdlib/buffer/list#dimlist) types, has moved to + the `buffer` package. + + ```mojo + from buffer import Dim, DimList + ``` + + - The [`parallel_memcpy()`](/mojo/stdlib/buffer/memory#parallel_memcpy) + function has moved from the `memory` package into the `buffer` package. + + ```mojo + from buffer import parallel_memcpy + ``` + + - The [`rand()`](/mojo/stdlib/tensor/random#rand) and + [`randn()`](/mojo/stdlib/tensor/random#randn) functions from the `random` + package that return a `Tensor` have moved to the `tensor` package. Note that + the overloads that write to a `DTypePointer` remain in the `random` package. + + If you happen to be using both versions in the same source file, you can + import them both using the `import as` syntax: + + ```mojo + from tensor import rand + from random import rand as rand_dt + ``` + + - The `trap()` function has been renamed to + [`abort()`](/mojo/stdlib/os/os#abort). It also has moved from the `debug` + module to the `os` module. + + ```mojo + from os import abort + ``` + + - The [`isinf()`](/mojo/stdlib/math/math#isinf) and + [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from + `math.limits` to the `math` module. + + ```mojo + from math import ininf, isfinite + ``` + +### Tooling changes + +#### ⭐️ New + +- Docstring code blocks can now use `%#` to hide lines of code from + documentation generation. + + For example: + + ```mojo + var value = 5 + %# print(value) + ``` + + Will generate documentation of the form: + + ```mojo + var value = 5 + ``` + + Hidden lines are processed as if they were normal code lines during test + execution. This allows for writing additional code within a docstring + example that is only used to ensure the example is runnable/testable. + +- The Mojo LSP server now allow you to specify additional search paths to use + when resolving imported modules in a document. You can specify search paths + on the command line, using the `-I` option, or you can add them to the + `mojo.lsp.includeDirs` setting in the VS Code extension. + +### ❌ Removed + +- The `__get_address_as_lvalue` magic function has been removed. You can now + get an LValue from a `Pointer` or `Reference` by using the dereference + operator (`[]`): + + ```mojo + var ptr: Pointer[MyRecord] + ... + # Doesn't work + __get_address_as_lvalue(ptr.value) = MyRecord(3, 5) + # Works + ptr[] = MyRecord(3, 5) + ``` + +- The type parameter for the `memcpy` function is now automatically inferred. + This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` will + no longer work and the user would have to change the code to `memcpy(...)`. + +- The [`memcpy()`](/mojo/stdlib/memory/memory#memcpy) overload that worked on + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types has been removed in favor + of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe#pointer) and + [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer): + + ```mojo + # Doesn't work + memcpy(destBuffer, srcBuffer, count) + # Works + memcpy(destBuffer.data, srcBuffer.data, count) + ``` + +- The functions `max_or_inf()`, `min_or_neginf()` have been removed from + `math.limit`. These functions were only used by the SIMD type. + +- As mentioned previously, the `print_no_newline()` function has been removed. + Please use `print(end="")` instead. + +### 🛠️ Fixed + +- [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference + now recursively matches function types. +- [#951](https://github.com/modularml/mojo/issues/951) - Functions that were + both `async` and `@always_inline` incorrectly errored. +- [#1858](https://github.com/modularml/mojo/issues/1858) - Trait with parametric + methods regression. +- [#1892](https://github.com/modularml/mojo/issues/1892) - Forbid unsupported + decorators on traits. +- [#1735](https://github.com/modularml/mojo/issues/1735) - Trait-typed values + are incorrectly considered equal. +- [#1909](https://github.com/modularml/mojo/issues/1909) - Crash due to nested + import in unreachable block. +- [#1921](https://github.com/modularml/mojo/issues/1921) - Parser crashes + binding Reference to lvalue with subtype lifetime. +- [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` + should return `T` instead of `Optional[T]`. +- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` + to floating point or integral types. +- [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` + does not work when specifying `end=""` +- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods + correctly handle edge cases where `size_out >= size`. + ## v24.1.1 (2024-03-18) This release includes installer improvements and enhanced error reporting for diff --git a/docs/changelog.md b/docs/changelog.md index f0b0f8ad0b..7a557e9c17 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,297 +13,13 @@ and tools. Please add any significant user-visible changes here. ### 🔥 Legendary -- The Mojo standard library is now open source! Check out the - [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) - for everything you need to get started. - -- Structs and other nominal types are now allowed to implicitly conform to - traits. A struct implicitly conforms to a trait if it implements all the - requirements for the trait. For example, any struct that implements `__str__` - will implicitly conform to `Stringable`, and then is usable with `str`. - - ```mojo - @value - struct Foo: - fn __str__(self) -> String: - return "foo!" - - fn main(): - print(str(Foo())) # prints 'foo!' - ``` - - Explicit conformance is still strongly encouraged where possible, because it - is useful for documentation and for communicating intentions. In the future, - explicit conformance will still be useful for features like default methods - and extensions. - -- Mojo's Python interoperability now supports passing keyword arguments to - Python callables: - - ```mojo - from python import Python - - def main(): - plt = Python.import_module("matplotlib.pyplot") - plt.plot((5, 10), (10, 15), color="red") - plt.show() - ``` - ### ⭐️ New -- String types all conform to the - [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means - that you can now call `int("123")` to get the integer `123`. If the integer - cannot be parsed from the string, then an error is raised. - -- The `Tensor` type now has an `argmax` and `argmin` function to compute the - position of the max or min value. - -- The `FloatLiteral` type is now an infinite precision nonmaterializable type. - When materialized, `FloatLiteral` is converted to `Float64`. - -- The [`List`](/mojo/stdlib/collections/list.html#list) type now supports - construction from a variadic number of values. For example, - `List[Int](1, 2, 3)` works now. - -- The `print` function now takes a `sep` and `end` as keyword. This means that - one can write `print("Hello", "Mojo", sep="/", end="!!!\n")` to print the - message `Hello/Mojo!!!\n` to the screen. - -- The Mojo LSP server, via the new `-I` argument, now allows specifying - additional search paths to use when resolving imported modules in a document. - A corresponding `mojo.lsp.includeDirs` setting was added to the VS Code - extension as well. - -- Mojo now has support for variadic keyword argument, often referred to as - `**kwargs`. This means you can now declare and call functions like this: - - ```mojo - fn print_nicely(**kwargs: Int) raises: - for key in kwargs.keys(): - print(key[], "=", kwargs[key[]]) - - # prints: - # `a = 7` - # `y = 8` - print_nicely(a=7, y=8) - ``` - - There are currently a few limitations: - - The ownership semantics of variadic keyword arguments are always `owned`. - This is applied implicitly, and cannot be declared otherwise: - - ```mojo - # Not supported yet. - fn borrowed_var_kwargs(borrowed **kwargs: Int): ... - ``` - - - Functions with variadic keyword arguments cannot have default values for - keyword-only arguments, e.g. - - ```mojo - # Not allowed yet, because `b` is keyword-only with a default. - fn not_yet(*, b: Int = 9, **kwargs: Int): ... - - # Okay, because `c` is positional-or-keyword, so it can have a default. - fn still_works(c: Int = 5, **kwargs: Int): ... - ``` - - - Dictionary unpacking is not supported yet: - - ```mojo - fn takes_dict(d: Dict[String, Int]): - print_nicely(**d) # Not supported yet. - ``` - - - Variadic keyword parameters are not supported yet: - - ```mojo - # Not supported yet. - fn var_kwparams[**kwparams: Int](): ... - ``` - -- Added new `collections.OptionalReg` type, a register-passable alternative - to `Optional`. - - - Doc string code blocks can now `%#` to hide lines of code from documentation - generation. - - For example: - - ```mojo - var value = 5 - %# print(value) - ``` - - will generate documentation of the form: - - ```mojo - var value = 5 - ``` - - Hidden lines are processed as if they were normal code lines during test - execution. This allows for writing additional code within a doc string - example that is only used to ensure the example is runnable/testable. - - - Doc string code blocks can now `%#` to hide lines of code from documentation - generation. - - For example: - - ```mojo - var value = 5 - %# print(value) - ``` - - will generate documentation of the form: - - ```mojo - var value = 5 - ``` - - Hidden lines are processed as if they were normal code lines during test - execution. This allows for writing additional code within a doc string - example that is only used to ensure the example is runnable/testable. +- The `sys` module now contains an `exit` function that would exit a Mojo + program with the specified error code. ### 🦋 Changed -- Mojo now warns about unused values in both `def` and `fn` declarations, - instead of completely disabling the warning in `def`s. It never warns about - unused `object` or `PythonObject` values, tying the warning to these types - instead of the kind of function they are unused in. This will help catch API - usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. - -- The [`DynamicVector`](/mojo/stdlib/collections/list#list) and - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - types now support negative indexing. This means that you can write `vec[-1]` - which is equivalent to `vec[len(vec)-1]`. - -- The [`isinf()`](/mojo/stdlib/math/math#isinf) and - [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from - `math.limits` to the `math` module. - -- The `ulp` function has been added to the `math` module. This allows one to get - the units of least precision (or units of last place) of a floating point - value. - -- `EqualityComparable` trait now requires `__ne__` function for conformance in addition - to the previously existing `__eq__` function. - -- Many types now declare conformance to `EqualityComparable` trait. - -- `DynamicVector` has been renamed to `List`. It has also moved from the `collections.vector` - module to `collections.list` module. - -- `StaticTuple` parameter order has changed to `StaticTuple[type, size]` for - consistency with `SIMD` and similar collection types. - -- The signature of the elementwise function has been changed. The new order is - is `function`, `simd_width`, and then `rank`. As a result, the rank parameter - can now be inferred and one can call elementwise via: - - ```mojo - elementwise[func, simd_width](shape) - ``` - -- For the time being, dynamic type value will be disabled in the language, e.g. - the following will now fail with an error: - - ```mojo - var t = Int # dynamic type values not allowed - - struct SomeType: ... - - takes_type(SomeType) # dynamic type values not allowed - ``` - - We want to take a step back and (re)design type valued variables, - existentials, and other dynamic features for more 🔥. This does not affect - type valued parameters, so the following will work as before: - - ```mojo - alias t = Int # still 🔥 - - struct SomeType: ... - - takes_type[SomeType]() # already 🔥 - ``` - -- `PythonObject` is now register-passable. - -- `PythonObject.__iter__` now works correctly on more types of iterable Python - objects. Attempting to iterate over non-iterable objects will now raise an - exception instead of behaving as if iterating over an empty sequence. - `__iter__` also now borrows `self` rather than requiring `inout`, allowing - code like `for value in my_dict.values():`. - -- `List.push_back` has been removed. Please use the `append` function instead. - -- We took the opportunity to rehome some modules into their correct package - as we were going through the process of open-sourcing the Mojo Standard - Library. Specifically, the following are some breaking changes worth - calling out. Please update your import statements accordingly. - - `utils.list` has moved to `buffer.list`. - - `rand` and `randn` functions in the `random` package that return a `Tensor` - have moved to the `tensor` package. - - `Buffer`, `NDBuffer`, and friends have moved from the `memory` package - into a new `buffer` package. - - The `trap` function has been renamed to `abort`. It also has moved from the - `debug` module to the `os` module. - - `parallel_memcpy` has moved from the `memory` package into - the `buffer` package. - -- The `*_` expression in parameter expressions is now required to occur at the - end of a positional parameter list, instead of being allowed in the middle. - This is no longer supported: `SomeStruct[*_, 42]` but `SomeStruct[42, *_]` is - still allowed. We narrowed this because we want to encourage type designers - to get the order of parameters right, and want to extend `*_` to support - keyword parameters as well in the future. - ### ❌ Removed -- `let` declarations now produce a compile time error instead of a warning, - our next step in [removing let - declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md). - The compiler still recognizes the `let` keyword for now in order to produce - a good error message, but that will be removed in subsequent releases. - -- The `__get_address_as_lvalue` magic function has been removed. You can now - get an LValue from a `Pointer` or `Reference` by using the `ptr[]` operator. - -- The type parameter for the `memcpy` function is now automatically inferred. - This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` would - no longer work and the user would have to change the code to `memcpy(...)`. - -- `print_no_newline` has been removed. Please use `print(end="")` instead. - -- `memcpy` on `Buffer` has been removed in favor of just overloads for `Pointer` - and `DTypePointer`. - -- The functions `max_or_inf`, `min_or_neginf` have been removed from - `math.limit` and just inlined into their use in SIMD. - ### 🛠️ Fixed - -- [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference - now recursively matches function types. -- [#951](https://github.com/modularml/mojo/issues/951) - Functions that were - both `async` and `@always_inline` incorrectly errored. -- [#1858](https://github.com/modularml/mojo/issues/1858) - Trait with parametric - methods regression. -- [#1892](https://github.com/modularml/mojo/issues/1892) - Forbid unsupported - decorators on traits. -- [#1735](https://github.com/modularml/mojo/issues/1735) - Trait-typed values - are incorrectly considered equal. -- [#1909](https://github.com/modularml/mojo/issues/1909) - Crash due to nested - import in unreachable block. -- [#1921](https://github.com/modularml/mojo/issues/1921) - Parser crashes - binding Reference to lvalue with subtype lifetime. -- [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` - should return `T` instead of `Optional[T]`. -- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` - to floating point or integral types. -- [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` - does not work when specifying `end=""` -- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods - correctly handle edge cases where `size_out >= size`. From 59aa21bf61a6d9bbcee4285863ca77d5e8f4f2d6 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 11:33:01 -0700 Subject: [PATCH 0005/2019] [Internal Change] modular-orig-commit: 83315f59b4ebf60a86ebe9bcc2bf820071984b75 --- docs/manual/get-started/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 0fcb56014a..3fe41ff422 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -55,7 +55,7 @@ Linux: - Ubuntu 20.04/22.04 LTS - x86-64 CPU (with [SSE4.2 or newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) - or AWS Graviton2 CPU or newer + or AWS Graviton2/3 CPU - Minimum 8 GiB RAM - Python 3.8 - 3.11 - g++ or clang++ C++ compiler From 737721b076bd77fc27551a3f9aba7953018c19ed Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 27 Mar 2024 17:12:47 -0500 Subject: [PATCH 0006/2019] [Docs] Move images into folder that gets pulled into public repo (#36008) (#36024) These are broken links in the public repos, as the root images folder isn't copied across. Good to keep them out of the root so moving to `stdlib/docs/images` with the other images, to keep them out of the way. modular-orig-commit: 4fbd0381ae221dde49ebcdce05de33739a42e8ed --- CONTRIBUTING.md | 6 +++--- stdlib/docs/development.md | 2 +- .../docs}/images/base-branch.png | Bin .../docs}/images/create-fork.png | Bin .../docs}/images/nightly-extension.png | Bin 5 files changed, 4 insertions(+), 4 deletions(-) rename {docs/oss-material => stdlib/docs}/images/base-branch.png (100%) rename {docs/oss-material => stdlib/docs}/images/create-fork.png (100%) rename {docs/oss-material => stdlib/docs}/images/nightly-extension.png (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4fc579813..7608951c78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -184,7 +184,7 @@ for more details. Go to the [Mojo repo](https://github.com/modularml/mojo) and click the fork button: -![Create Fork](./images/create-fork.png) +![Create Fork](stdlib/docs/images/create-fork.png) Clone your forked repo locally with the command: @@ -249,7 +249,7 @@ If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. Install the [Mojo nightly VS Code extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly): - + You can only have one Mojo extension enabled at a time, remember to switch back when using the stable release! @@ -275,7 +275,7 @@ remote: https://github.com/jackos/mojo/pull/new/my-fix-pr Make sure you point it to the `nightly` branch: -![Base Branch](images/base-branch.png) +![Base Branch](stdlib/docs/images/base-branch.png) Now fill out the details: diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index e9f2d19a00..362da08323 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -305,7 +305,7 @@ error: CHECK: expected string not found in input Lets fix the test that we just added: -```plaintext +```mojo # CHECK-LABEL: test_print_cwd def test_print_cwd(): diff --git a/docs/oss-material/images/base-branch.png b/stdlib/docs/images/base-branch.png similarity index 100% rename from docs/oss-material/images/base-branch.png rename to stdlib/docs/images/base-branch.png diff --git a/docs/oss-material/images/create-fork.png b/stdlib/docs/images/create-fork.png similarity index 100% rename from docs/oss-material/images/create-fork.png rename to stdlib/docs/images/create-fork.png diff --git a/docs/oss-material/images/nightly-extension.png b/stdlib/docs/images/nightly-extension.png similarity index 100% rename from docs/oss-material/images/nightly-extension.png rename to stdlib/docs/images/nightly-extension.png From 40dcf006b94918f5747cf7f6287456d73c63e771 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 27 Mar 2024 17:28:06 -0700 Subject: [PATCH 0007/2019] [Docs] Mojo documentation cherry picks (#36060) A few last changelog updates. Added sections on implicit trait conformance and variadic keyword arguments. Minor tweaks to the parameters page for 24.2. modular-orig-commit: 63cbb23b6a48880d59beb6c14848b9a9c2eef222 --- docs/changelog-released.md | 60 ++++++++------------ docs/manual/functions.ipynb | 88 ++++++++++++++++++++++++++---- docs/manual/parameters/index.ipynb | 76 ++++++++++++++++++-------- docs/manual/traits.ipynb | 63 +++++++++++++++------ 4 files changed, 198 insertions(+), 89 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index df6ab829ed..9d29c0df14 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -96,40 +96,9 @@ modular update mojo print_nicely(a=7, y=8) ``` - There are currently a few limitations: - - - The ownership semantics of variadic keyword arguments are always `owned`. - This is applied implicitly, and cannot be declared otherwise: - - ```mojo - # Not supported yet. - fn borrowed_var_kwargs(borrowed **kwargs: Int): ... - ``` - - - Functions with variadic keyword arguments cannot have default values for - keyword-only arguments. For example: - - ```mojo - # Not allowed yet, because `b` is keyword-only with a default. - fn not_yet(*, b: Int = 9, **kwargs: Int): ... - - # Okay, because `c` is positional-or-keyword, so it can have a default. - fn still_works(c: Int = 5, **kwargs: Int): ... - ``` - - - Dictionary unpacking is not supported yet: - - ```mojo - fn takes_dict(d: Dict[String, Int]): - print_nicely(**d) # Not supported yet. - ``` - - - Variadic keyword _parameters_ are not supported yet: - - ```mojo - # Not supported yet. - fn var_kwparams[**kwparams: Int](): ... - ``` + For more details (and a list of current limitations), see [Variadic keyword + arguments](/mojo/manual/functions#variadic-keyword-arguments) in the Mojo + manual. #### 🦋 Changed or removed @@ -240,7 +209,9 @@ modular update mojo - The `Tensor` type now has [`argmax()`](/mojo/stdlib/tensor/tensor#argmax) and [`argmin()`](/mojo/stdlib/tensor/tensor#argmin) functions to compute the - position of the max or min value. + position of the max or min value. Note: this should return a `Tensor[Int]` + but currently the output tensor is the same type as the input tensor. This + will be fixed in a future release. - Added a new [`collections.OptionalReg`](/mojo/stdlib/collections/optional#optionalreg) @@ -375,7 +346,9 @@ modular update mojo on the command line, using the `-I` option, or you can add them to the `mojo.lsp.includeDirs` setting in the VS Code extension. -### ❌ Removed +### Other changes + +#### ❌ Removed - The `__get_address_as_lvalue` magic function has been removed. You can now get an LValue from a `Pointer` or `Reference` by using the dereference @@ -406,13 +379,26 @@ modular update mojo memcpy(destBuffer.data, srcBuffer.data, count) ``` +- The `simd_load()` and `simd_store()` methods on + [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and + [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been removed in favor + of `load()` and `store()`: + + ```mojo + # Doesn't work + my_simd = my_buffer.simd_load[simd_width](index) + # Works + my_simd = my_buffer.load[simd_width, alignment](index) + ``` + - The functions `max_or_inf()`, `min_or_neginf()` have been removed from `math.limit`. These functions were only used by the SIMD type. - As mentioned previously, the `print_no_newline()` function has been removed. Please use `print(end="")` instead. -### 🛠️ Fixed +#### 🛠️ Fixed - [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference now recursively matches function types. diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index cd145d97f5..a29f6061dc 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -312,24 +312,17 @@ "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", "Mojo in the future.\n", "\n", - ":::note No variadic keyword-only arguments\n", - "\n", - "Variadic keyword-only arguments (`**kwargs`) are not yet supported in Mojo. We\n", - "plan to support these in the future.\n", - "\n", - ":::\n", - "\n", - "Inside the function, the variadic argument is projected into an iterable list\n", + "Inside the function body, the variadic argument is available an iterable list\n", "for ease of use. But there are some differences in handling the list depending\n", "on whether the arguments are register-passable types (such as `Int`) or\n", "memory-only types (such as `String`).\n", "\n", - "Register-passable types, such as `Int`, are projected into a \n", + "Register-passable types, such as `Int`, are available as a \n", "[`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", "loop.\n", "\n", - "Memory-only types, such as `String`, are projected into a \n", + "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", "[`Reference`](/mojo/stdlib/memory/unsafe#reference) for each value instead\n", @@ -373,12 +366,83 @@ "\n", "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", "(parameters are used for compile-time metaprogramming). However, most rules\n", - "that apply to argument lists also apply to parameter lists, including variadics.\n", - "Variadic parameters are declared and used like variadic arguments.\n", + "that apply to argument lists also apply to parameter lists. Variadic parameters\n", + "are supported, but with some limitations—for details see \n", + "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", "\n", ":::\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Variadic keyword arguments\n", + "\n", + "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", + "keyword arguments allow the user to pass an arbitrary number of keyword\n", + "arguments. To define a function that takes a variadic keyword argument, use the\n", + "variadic keyword argument syntax **kw_argument_name:\n", + "\n", + " ```mojo\n", + " fn print_nicely(**kwargs: Int) raises:\n", + " for key in kwargs.keys():\n", + " print(key[], \"=\", kwargs[key[]])\n", + "\n", + " # prints:\n", + " # `a = 7`\n", + " # `y = 8`\n", + " print_nicely(a=7, y=8)\n", + " ```\n", + "\n", + " In this example, the argument name `kwargs` is a placeholder that accepts any\n", + " number of keyword arguments. Inside the body of the function, you can access\n", + " the arguments as a [`Dict`](/mojo/stdlib/collections/dict) of keywords and\n", + " argument values.\n", + " \n", + " There are currently a few limitations:\n", + "\n", + " - Variadic keyword arguments are always implicitly treated as if they\n", + " were declared with the `owned` [argument \n", + " convention](/mojo/manual/values/ownership.html#argument-conventions), and\n", + " can't be declared otherwise:\n", + "\n", + " ```mojo\n", + " # Not supported yet.\n", + " fn borrowed_var_kwargs(borrowed **kwargs: Int): ...\n", + " ```\n", + "\n", + " - All the variadic keyword arguments must have the same type, and this\n", + " determines the type of the argument dictionary. For example, if the argument\n", + " is `**kwargs: Float64` then the argument dictionary will be a \n", + " `Dict[String, Float64]`.\n", + "\n", + " - Functions with variadic keyword arguments can't have default values for\n", + " keyword-only arguments. For example:\n", + "\n", + " ```mojo\n", + " # Not allowed yet, because `b` is keyword-only with a default.\n", + " fn not_yet(*, b: Int = 9, **kwargs: Int): ...\n", + "\n", + " # Okay, because `c` is positional-or-keyword, so it can have a default.\n", + " fn still_works(c: Int = 5, **kwargs: Int): ...\n", + " ```\n", + "\n", + " - Dictionary unpacking is not supported yet:\n", + "\n", + " ```mojo\n", + " fn takes_dict(d: Dict[String, Int]):\n", + " print_nicely(**d) # Not supported yet.\n", + " ```\n", + "\n", + " - Variadic keyword _parameters_ are not supported yet:\n", + "\n", + " ```mojo\n", + " # Not supported yet.\n", + " fn var_kwparams[**kwparams: Int](): ...\n", + " ```" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index d644bf3741..77704d5d60 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -127,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -193,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -291,7 +291,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -351,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -435,7 +435,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -484,7 +484,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -546,7 +546,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -578,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -626,8 +626,6 @@ "Mojo supports positional-only and keyword-only parameters, following the same\n", "rules as [positional-only and keyword-only\n", "arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).\n", - "As with arguments, variadic keyword arguments (for example, `**kwparams`) are\n", - "not supported yet.\n", "\n", ":::" ] @@ -638,13 +636,13 @@ "source": [ "## Variadic parameters\n", "\n", - "Mojo also supports variadic parameters, following the same rules described for \n", + "Mojo also supports variadic parameters, similar to \n", "[Variadic arguments](/mojo/manual/functions#variadic-arguments):" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -656,7 +654,33 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As with arguments, variadic keyword parameters (for example, `**kwparams`) are\n", + "Variadic parameters have some limitations that variadic arguments don't have:\n", + "\n", + "- Only variadic parameters of register-passable types are supported currently.\n", + "\n", + "- The parameters aren't automatically projected into a `VariadicList`, so you\n", + " need to construct the list explicitly:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn sum_params[*values: Int]() -> Int:\n", + " alias list = VariadicList(values)\n", + " var sum = 0\n", + " for v in list:\n", + " sum += v\n", + " return sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Variadic keyword parameters (for example, `**kwparams`) are\n", "not supported yet." ] }, @@ -846,6 +870,18 @@ " func(i)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "attachments": {}, "cell_type": "markdown", @@ -986,21 +1022,13 @@ " ```\n", "\n", "You can also use the star-underscore expression `*_` to unbind an arbitrary\n", - "number of positional parameters at the start, middle, or end of a parameter\n", + "number of positional parameters at the end of a parameter\n", "list.\n", "\n", "```mojo\n", "# These two types are equivalent\n", "MyType[\"Hello\", *_]\n", "MyType[\"Hello\", _, _, _]\n", - "\n", - "# These two types are equivalent\n", - "MyType[*_, False]\n", - "MyType[_, _, _, False]\n", - "\n", - "# These two types are equivalent\n", - "MyType[*_]\n", - "MyType[_, _, _, _]\n", "```\n", "\n", "When a parameter is explicitly unbound with the `_` or `*_` expression, you\n", diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 0052dc3630..40fdb69ba3 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -36,18 +36,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Quack.\n", - "Moo!\n" - ] - } - ], + "outputs": [], "source": [ "%%python\n", "class Duck:\n", @@ -128,10 +119,10 @@ "This isn't too bad with only two classes. But the more classes you want to\n", "support, the less practical this approach is.\n", "\n", - "You might notice that the Mojo versions dosn't include the `try/except` \n", - "statement. We don't need it because Mojo's static type checking ensures that\n", - "you can only pass instances of `Duck` or `StealthCow` into the `make_it_quack()`\n", - "function.\n", + "You might notice that the Mojo versions of `make_it_quack()` don't include the\n", + "`try/except` statement. We don't need it because Mojo's static type checking\n", + "ensures that you can only pass instances of `Duck` or `StealthCow` into the \n", + "`make_it_quack()`function.\n", "\n", "## Using traits\n", "\n", @@ -178,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -283,8 +274,48 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Implicit trait conformance\n", "\n", + "Mojo also supports _implicit_ trait conformance. That is, if a type implements\n", + "all of the methods required for a trait, it's treated as conforming to the\n", + "trait, even if it doesn't explicitly include the trait in its declaration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "struct RubberDucky:\n", + " fn quack(self):\n", + " print(\"Squeak!\")\n", + "\n", + "make_it_quack(RubberDucky())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implicit conformance can be handy if you're defining a trait and you want it to\n", + "work with types that you don't control—such as types from the standard library,\n", + "or a third-party library.\n", "\n", + "However, we still strongly recommend explicit trait conformance wherever\n", + "possible. This has two advantages:\n", + "\n", + "- Documentation. It makes it clear that the type conforms to the trait, without\n", + " having to scan all of its methods.\n", + "\n", + "- Future feature support. When default method implementations are added to\n", + " traits, they'll only work for types that explicitly conform to traits." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "## Trait inheritance\n", "\n", "Traits can inherit from other traits. A trait that inherits from another trait\n", From c9148a3f1cb6e955905cd118de7d5d71eec1f327 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 21:03:33 -0700 Subject: [PATCH 0008/2019] [docs] Fix docsite build (#36081) (#36082) Docsite began to fail becuase it attempted to read lines from this code block that is empty. Clearly a mistake to have an empty code cell, but also added a code check for this: [Internal Link] cherrypick [Internal Link] modular-orig-commit: 081e133c37790c9596ee2efa4fd8b8fdd8ae2a52 --- docs/manual/parameters/index.ipynb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 77704d5d60..31a4480582 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -870,18 +870,6 @@ " func(i)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "attachments": {}, "cell_type": "markdown", From 3090d2bf20461d6dbf22432823bace0cc7bb2d6a Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 28 Mar 2024 10:21:04 -0700 Subject: [PATCH 0009/2019] =?UTF-8?q?[Docs]=20Update=20load()/store()=20it?= =?UTF-8?q?em=20and=20move=20it=20to=20top=20of=20removed=20secti=E2=80=A6?= =?UTF-8?q?=20(#36120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …on. (#36110) Cherry-pick from 038e34d modular-orig-commit: cd0011c10f54eda58a38eb332b90211fdfb81b7c --- docs/changelog-released.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 9d29c0df14..b712a0121e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -224,6 +224,25 @@ modular update mojo #### 🦋 Changed +- The `simd_load()`, `simd_store()`, `aligned_simd_load()`, and + `aligned_simd_store()` methods on + [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and + [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been merged into + a more expressive set of `load()` and `store()` methods with keyword-only + `width` and `alignment` parameters: + + ```mojo + # Doesn't work + my_simd = my_buffer.simd_load[simd_width](index) + # Works + my_simd = my_buffer.load[width=simd_width](index) + # Doesn't work + my_buffer.aligned_simd_store[width, alignment](my_simd) + # Works + my_buffer.store[width=width, alignment=alignment](my_simd) + ``` + - The [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable#equalitycomparable) trait now requires the `__ne__()` method for conformance in addition to the @@ -379,19 +398,6 @@ modular update mojo memcpy(destBuffer.data, srcBuffer.data, count) ``` -- The `simd_load()` and `simd_store()` methods on - [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and - [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been removed in favor - of `load()` and `store()`: - - ```mojo - # Doesn't work - my_simd = my_buffer.simd_load[simd_width](index) - # Works - my_simd = my_buffer.load[simd_width, alignment](index) - ``` - - The functions `max_or_inf()`, `min_or_neginf()` have been removed from `math.limit`. These functions were only used by the SIMD type. From 1dd0d4598c6e1a8a223abc9833ffc821edc4c1af Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sat, 30 Mar 2024 00:01:59 +0900 Subject: [PATCH 0010/2019] Update README.md Intall -> Install Signed-off-by: Ikko Eltociear Ashimine --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe65959e21..0c83c48036 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk -and be patient! Intall them using the instructions [here](./CONTRIBUTING.md). +and be patient! Install them using the instructions [here](./CONTRIBUTING.md). ## Contributing From f1493c87ff8cbabb3cb88fb11fd1063403a7ffe2 Mon Sep 17 00:00:00 2001 From: "Xida Ren (Cedar)" Date: Mon, 1 Apr 2024 10:53:51 -0700 Subject: [PATCH 0011/2019] Fix invalid link (#2112) Fixes https://github.com/modularml/mojo/issues/2107 Signed-off-by: Xida Ren (Cedar) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c83c48036..6738ac4d40 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ or the standalone Mojo SDK: - [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) Then follow the docs to [write your first Mojo -program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). +program](https://docs.modular.com/mojo/manual/get-started/hello-world). ### Latest Nightly From 46889965656de7d4949f054e483335883a1f83e2 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Thu, 18 Apr 2024 17:44:26 +0200 Subject: [PATCH 0012/2019] [stdlib] [proposal] Standardize the representation of byte sequence as a sequence of unsigned 8 bit integers (#2099) Add a proposal to standardize the representation of byte sequence as a sequence of unsigned 8 bit integers. Signed-off-by: Maxim Zaks --- proposals/byte-as-uint8.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 proposals/byte-as-uint8.md diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md new file mode 100644 index 0000000000..e78a4f2a56 --- /dev/null +++ b/proposals/byte-as-uint8.md @@ -0,0 +1,25 @@ +# Standardise the representation of byte sequence as a sequence of unsigned 8 bit integers + +At this point in time, a sequence of bytes is often represented as a sequence of signed 8 bit integers in Mojo standard library. +Most noticeable example is the underlying data of string types `String`, `StringLiteral`, `StringRef` and `InlinedString`, but also APIs like for example the hash function `fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int:`. + +# Motivation + +Logically a byte is an integer value between `0` and `255`. Lots of algorithms make use of arithmetics ground by this assumption. +A signed 8 bit integer on the contrary represents values between `-128` and `127`. This introduces very subtle bugs, when an algorithm written for unsigned 8 bit integer is used on a signed 8 bit integer. + +Another motivation for this change is that Mojo aims to be familiar to Python users. Those Python users are familiar with the `bytes` class, which itself is working with values between `0` and `255`, not values between `-128` and `127`. + +## Examples: + +### Division: +A value `-4` represented as `Int8` has the same bit pattern as value `252` represented as `UInt8`. +`-4 // 4` equals to `-1` (`bx11111111`), where `252 // 4` equals to `63` (`bx00111111`) as we can see the bit patterns are different. + +### Bit shift: +Values `-1` and `255` have the same bit pattern as `Int8` and `UInt8` `bx11111111` but `-1 >> 1` results in `-1` (same bit pattern), where `255 >> 1` results in `127` (`bx01111111`) + +# Proposal + +A text based search for `DTypePointer[DType.int8]` and `Pointer[Int8]` on current open-sourced standard library revealed 29 results for `Pointer[Int8]` and 78 results for `DTypePointer[DType.int8]`. +Replacing `DTypePointer[DType.int8]` with `DTypePointer[DType.uint8]` and `Pointer[Int8]` with `Pointer[UInt8]` on case by case bases is a substantial refactoring effort, but it will prevent a certain class of logical bugs (see https://github.com/modularml/mojo/pull/2098). As it is a breaking change in sense of API design, it is sensible to do the refactoring as soon as possible. From 432b2c1c966b8f2d9866d0b5257ab603aacd62d1 Mon Sep 17 00:00:00 2001 From: Goldie Gadde <43185254+goldiegadde@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:56:09 -0700 Subject: [PATCH 0013/2019] [Issue template] Update the default labels for issues (#2440) Update the default labels for the issues created via the issue-templates. --------- Signed-off-by: Goldie Gadde <43185254+goldiegadde@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/doc_issue.yaml | 2 +- .github/ISSUE_TEMPLATE/modular_cli_issue.yaml | 2 +- .github/ISSUE_TEMPLATE/mojo_bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/mojo_feature_request.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/doc_issue.yaml b/.github/ISSUE_TEMPLATE/doc_issue.yaml index 14996bc7e1..5a1633728f 100644 --- a/.github/ISSUE_TEMPLATE/doc_issue.yaml +++ b/.github/ISSUE_TEMPLATE/doc_issue.yaml @@ -2,7 +2,7 @@ name: Documentation issue description: Report a problem with the Mojo docs title: "[Docs]" labels: - - documentation + - "documentation,mojo-repo" body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml b/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml index acaa5ece88..5a8027388b 100644 --- a/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml +++ b/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml @@ -2,7 +2,7 @@ name: Modular CLI issue description: Create an issue for the Modular CLI tool. title: "[Modular CLI]" labels: - - modular-cli + - "modular-cli,mojo-repo" body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml b/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml index 324132fcdb..f5920003f2 100644 --- a/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml @@ -2,7 +2,7 @@ name: Mojo bug report description: Create a bug report to help us improve Mojo title: "[BUG]" labels: - - "bug,mojo" + - "bug,mojo-repo" body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/mojo_feature_request.yaml b/.github/ISSUE_TEMPLATE/mojo_feature_request.yaml index d81855f5f0..a42844679d 100644 --- a/.github/ISSUE_TEMPLATE/mojo_feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/mojo_feature_request.yaml @@ -2,7 +2,7 @@ name: Mojo feature request description: Suggest an enhancement for Mojo title: "[Feature Request]" labels: - - "enhancement,mojo" + - "enhancement,mojo-repo" body: - type: markdown attributes: From 92c907c6b9d865dd5a10e6bd2ec0f8025b7e8086 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 25 Mar 2024 17:29:46 -0700 Subject: [PATCH 0014/2019] [Docs] Document more sharp edges. (#35696) Add notes about the static result type for `or` expressions, and the differences between `String` and `StringLiteral`. Related to: https://github.com/modularml/mojo/issues/1767 and [Internal link] MODULAR_ORIG_COMMIT_REV_ID: d368f2dd71e7f0f36ec20a884cbf944836a0b3f3 --- CONTRIBUTING.md | 130 +- README.md | 64 +- docs/changelog-released.md | 4755 ---------------------- docs/changelog.md | 4655 ++++++++++++++++++++- docs/manual/functions.ipynb | 88 +- docs/manual/get-started/index.md | 2 +- docs/manual/packages.md | 8 +- docs/manual/parameters/index.ipynb | 64 +- docs/manual/traits.ipynb | 63 +- docs/oss-material/CODE_OF_CONDUCT.md | 71 - docs/oss-material/LICENSE | 235 -- docs/tools/debugging.ipynb | 2 +- docs/upgrade-guide.md | 6 +- examples/notebooks/HelloMojo.ipynb | 1 + stdlib/README.md | 24 +- stdlib/docs/development.md | 326 +- stdlib/docs/faq.md | 53 +- stdlib/docs/governance-structure.md | 4 +- stdlib/docs/images/base-branch.png | Bin 163923 -> 0 bytes stdlib/docs/images/create-fork.png | Bin 179367 -> 0 bytes stdlib/docs/images/nightly-extension.png | Bin 116383 -> 0 bytes stdlib/docs/roadmap.md | 44 +- stdlib/docs/style-guide.md | 108 +- stdlib/docs/vision.md | 64 +- stdlib/test/memory/test_memory.mojo | 1 + 25 files changed, 4950 insertions(+), 5818 deletions(-) delete mode 100644 docs/changelog-released.md delete mode 100644 docs/oss-material/CODE_OF_CONDUCT.md delete mode 100644 docs/oss-material/LICENSE delete mode 100644 stdlib/docs/images/base-branch.png delete mode 100644 stdlib/docs/images/create-fork.png delete mode 100644 stdlib/docs/images/nightly-extension.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7608951c78..9e2c1ea68d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Mojo contributor guide +# Mojo Contributor Guide Welcome to the Mojo community! 🔥 We’re very excited that you’re interested in contributing to the project. To help you get started and ensure a smooth @@ -8,7 +8,7 @@ There are many ways to contribute to the project, from joining the [Discord community](https://www.discord.gg/modular), to filing bugs, to contributing documentation, examples, or code. -## Submitting bugs +## Submitting Bugs Reporting issues is a great way to contribute to the project. Mojo uses GitHub Issues for tracking bugs. @@ -21,25 +21,25 @@ Also, before opening a new issue, take a moment to search through the already submitted issues to avoid creating duplicate issues for the maintainers to address. -### Writing high-quality bugs +### Writing High-Quality Bugs We encourage you to provide as much information about the issue as practical. The more details you provide, the faster we can resolve the issue. The following is a template of the information that should accompany every submitted issue. -#### Issue template +#### Issue Template - **Summary.** A descriptive summary of the issue. - **Description.** A detailed account of the bug, including what was expected and what occurred. -- **Environment details.** +- **Environment Details.** - Mojo Compiler Version - Operating System version - Hardware Specifications -- **Severity/frequency.** An assessment of the impact ranging from inconvenience +- **Severity/Frequency.** An assessment of the impact ranging from inconvenience to a blocker. -## Contributing to docs and examples +## Contributing to Docs and Examples We’re happy to accept pull requests for the docs and examples. If your change is any one of the following, please create a pull request and we @@ -67,7 +67,7 @@ require difficult reviews and rework, or that might get rejected. See [Pull Requests](#pull-requests) for information on creating your first pull request. -## Contributing to the standard library +## Contributing to the Standard Library The standard library team is dedicated to creating a vibrant technical community around the Mojo programming language. Our vision includes a diverse and @@ -84,12 +84,12 @@ For more information on our priorities, see the following documents: For technical details on developing for the standard library, see the following documents: -- [Developing the standard library](./stdlib/docs/development.md) covers building, +- [Developing the Standard Library](./stdlib/docs/development.md) covers building, testing, and other information you’ll need to work in the standard library. - [Coding Standards and Style Guide](./stdlib/docs/style-guide.md) provides guidelines for writing code for the standard library. -### Accepting open source PRs +### Accepting Open Source PRs To ensure a streamlined process, contributors are encouraged to focus on enhancements, bug fixes, and optimizations aligned with the library's @@ -153,7 +153,7 @@ This process is heavily inspired by the process used by several other open-source projects. We’ll add more documentation in the future as we gain experience with the process. -## Pull requests +## Pull Requests You can use a pull request to propose a change or bug fix to the Mojo Standard Library, Mojo examples, or Mojo documentation. This page gives an overview of @@ -162,122 +162,28 @@ the process, especially for first-time contributors. **Note:** Pull requests should be submitted against the `nightly` branch, which represents the most recent nightly build. -### Pull request process +### Pull Request Process -#### First-time checklist +#### 1. First-time checklist Before you start your first pull request, please complete this checklist: - Read this entire contributor guide. - Read the [Code of Conduct](./CODE_OF_CONDUCT.md). -#### Evaluate and get buy-in on the change +#### 2. Evaluate and get buy-in on the change We want to be sure that you spend your time efficiently and prepare changes that aren’t controversial and get stuck in long rounds of reviews. See the sections on [Contributing to Docs and Examples](#contributing-to-docs-and-examples) and -[Contributing to the standard library](#contributing-to-the-standard-library) +[Contributing to the Standard Library](#contributing-to-the-standard-library) for more details. -#### Fork and clone the repo - -Go to the [Mojo repo](https://github.com/modularml/mojo) and click the fork -button: - -![Create Fork](stdlib/docs/images/create-fork.png) - -Clone your forked repo locally with the command: - -```bash -git clone git@github.com:[your-username]/mojo.git -cd mojo -``` - -Add the upstream remote and fetch it: - -```bash -git remote add upstream git@github.com:modularml/mojo.git -git fetch upstream -``` - -#### Branching off nightly - -Make sure to branch off `nightly` to work on your PR: - -```bash -git checkout upstream/nightly -git checkout -b my-fix-pr -``` - -You should periodically make sure you've synced the latest changes, especially -before raising a PR: - -```bash -git fetch upstream -git rebase upstream/nightly -``` - -#### Getting the nightly Mojo compiler - -Now that you're on the nightly branch, you need to install the latest nightly -Mojo compiler: - -```bash -curl https://get.modular.com | sh - - -modular auth - -modular install nightly/mojo -``` - -If you already have an older `nightly/mojo` compiler, replace -`modular install nightly/mojo` with `modular update nightly/mojo`. - -Then, follow the instructions from the `modular` tool in adding the `mojo` -compiler to your `PATH` such as: - -```bash -echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc -echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc -``` - -If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. - -#### Mojo nightly vscode extension - -Install the [Mojo nightly VS Code -extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly): - - - -You can only have one Mojo extension enabled at a time, remember to switch back -when using the stable release! - -#### Create a pull request +#### 3. Create a pull request If your change is one of the improvements described above or it has been discussed and agreed upon by the project maintainers, please create a pull -request into the `nightly` branch. - -First push your changes: - -```bash -git push -u [your-username] my-fix-pr -``` - -You'll see a link to create a PR: - -```plaintext -remote: Create a pull request for 'my-fix-pr' on GitHub by visiting: -remote: https://github.com/jackos/mojo/pull/new/my-fix-pr -``` - -Make sure you point it to the `nightly` branch: - -![Base Branch](stdlib/docs/images/base-branch.png) - -Now fill out the details: +request into the `nightly` branch and include the following: - A short commit title describing the change. - A detailed commit description that includes rationalization for the change @@ -345,7 +251,7 @@ By making a contribution to this project, I certify that: this project or the open source license(s) involved. ``` -### Review time SLA +### Review Time SLA The team commits to reviewing submitted pull requests within a week of submission. diff --git a/README.md b/README.md index 6738ac4d40..9178a6dd24 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,18 @@ and production by combining Python syntax and ecosystem with systems programming and metaprogramming features. Mojo is still young, but it is designed to become a superset of Python over time. +To use Mojo, you can install the MAX SDK or the standalone Mojo SDK: + +- [Get the MAX SDK.](https://docs.modular.com/engine/get-started) +- [Get the Mojo SDK.](https://docs.modular.com/mojo/manual/get-started/) + +Then follow the docs to [write your first Mojo +program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). +When you want to report issues or request features, [please create a GitHub +issue here](https://github.com/modularml/mojo/issues). See the [Mojo +Contributor Guide](https://www.notion.so/f527254bc46b4cd3ba4b34bd949d4e57?pvs=21) +for guidelines on filing good bugs. + This repo includes source code for: - Mojo examples @@ -18,56 +30,30 @@ This repo includes source code for: This repo has two primary branches: - The [`main`](https://github.com/modularml/mojo/tree/main) branch, which is in -sync with the last stable released version of Mojo. Use the examples here if you’re -using a [release build of Mojo](#latest-released). +sync with the last released version of Mojo. Use the examples here if you’re +using a release build of Mojo. - The [`nightly`](https://github.com/modularml/mojo/tree/nightly) branch, which is in sync with the Mojo nightly build and subject to breakage. Use this branch -for [contributions](./CONTRIBUTING.md), or if you're using the latest -[nightly build of Mojo](#latest-nightly). - -To learn more about Mojo, see the -[Mojo Manual](https://docs.modular.com/mojo/manual/). - -## Installing Mojo - -### Latest Released - -To install the last released build of Mojo, you can install the MAX SDK -or the standalone Mojo SDK: +for [contributions](./CONTRIBUTING.md). -- [Get the MAX SDK](https://docs.modular.com/engine/get-started) -- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) +This repo represents the Mojo open source effort. We are continuing to open +parts of the Mojo codebase. The challenge is that we use Mojo pervasively +inside Modular and we need to make sure that community contributions can +proceed smoothly with good build and testing tools that will allow this repo to +become the source of truth (right now it is not). We'll progressively improve +the tools and add more source code over time. -Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started/hello-world). - -### Latest Nightly - -The nightly Mojo builds are subject to breakage and provide an inside -view of how the development of Mojo is progressing. Use at your own risk -and be patient! Install them using the instructions [here](./CONTRIBUTING.md). - -## Contributing - -When you want to report issues or request features, [please create a GitHub -issue here](https://github.com/modularml/mojo/issues). -See [here](./CONTRIBUTING.md) for guidelines on filing good bugs. - -We welcome contributions to this repo on the -[`nightly`](https://github.com/modularml/mojo/tree/nightly) -branch. If you’d like to contribute to Mojo, please first read our [Contributor +If you’d like to contribute to Mojo, please first read our [Contributor Guide](https://github.com/modularml/mojo/blob/main/CONTRIBUTING.md). For more general questions or to chat with other Mojo developers, check out our [Discord](https://discord.gg/modular). -## License - -This repository is licensed under the Apache License v2.0 with LLVM Exceptions -(see the LLVM [License](https://llvm.org/LICENSE.txt)). +To learn more about Mojo, see the +[Mojo Manual](https://docs.modular.com/mojo/manual/). -## Thanks to our contributors +## Thanks To Our Contributors diff --git a/docs/changelog-released.md b/docs/changelog-released.md deleted file mode 100644 index b712a0121e..0000000000 --- a/docs/changelog-released.md +++ /dev/null @@ -1,4755 +0,0 @@ ---- -title: Mojo🔥 changelog -sidebar_label: Changelog -description: A history of significant Mojo changes. -toc_max_heading_level: 2 -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png ---- - -This is a running list of significant changes for the Mojo language and tools. -It doesn't include all internal implementation changes. - -## Update Mojo - -If you don't have Mojo yet, see the [get started -guide](/mojo/manual/get-started/#get-the-mojo-sdk). - -To see your Mojo version, run this: - -```sh -mojo --version -``` - -To update Mojo, first [update `modular`](/cli/#description), and then run this: - -```sh -modular update mojo -``` - -## v24.2 (2024-03-28) - -### 🔥 Legendary - -- The Mojo standard library is now open source! Check out the - [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) - for everything you need to get started. - -- Structs and other nominal types are now allowed to implicitly conform to - traits. A struct implicitly conforms to a trait if it implements all the - requirements for the trait. For example, any struct that implements the - `__str__()` method implicitly conforms to `Stringable`, and is usable with - the `str()` built-in function. - - ```mojo - @value - struct Foo: - fn __str__(self) -> String: - return "foo!" - - fn main(): - print(str(Foo())) # prints 'foo!' - ``` - - We still strongly encourage you to explicitly list the traits a struct - conforms to when possible: - - ```mojo - @value - struct Foo(Stringable): ... - ``` - - Not only is this useful for documentation and for communicating intentions, - but in the future, explicit conformance will be useful for features like - default methods and extensions. - -- Mojo's Python interoperability now supports passing keyword arguments to - Python functions: - - ```mojo - from python import Python - - def main(): - plt = Python.import_module("matplotlib.pyplot") - plt.plot((5, 10), (10, 15), color="red") - plt.show() - ``` - -### Language changes - -#### ⭐️ New - -- Mojo now has support for variadic keyword arguments, often referred to as - `**kwargs`. This means you can now declare and call functions like this: - - ```mojo - fn print_nicely(**kwargs: Int) raises: - for key in kwargs.keys(): - print(key[], "=", kwargs[key[]]) - - # prints: - # `a = 7` - # `y = 8` - print_nicely(a=7, y=8) - ``` - - For more details (and a list of current limitations), see [Variadic keyword - arguments](/mojo/manual/functions#variadic-keyword-arguments) in the Mojo - manual. - -#### 🦋 Changed or removed - -- `let` declarations now produce a compile time error instead of a warning, - our next step in [removing let - declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md). - The compiler still recognizes the `let` keyword for now in order to produce - a good error message, but that will be removed in subsequent releases. - -- Mojo now warns about unused values in both `def` and `fn` declarations, - instead of completely disabling the warning in `def`s. It never warns about - unused `object` or `PythonObject` values, tying the warning to these types - instead of the kind of function they are unused in. This will help catch API - usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. - -- For the time being, dynamic type values will be disabled in the language. For - example, the following will now fail with an error: - - ```mojo - var t = Int # dynamic type values not allowed - - struct SomeType: ... - - takes_type(SomeType) # dynamic type values not allowed - ``` - - We want to take a step back and (re)design type valued variables, - existentials, and other dynamic features. This does not affect type valued - **parameters**, so the following works as before: - - ```mojo - alias t = Int # still 🔥 - - struct SomeType: ... - - takes_type[SomeType]() # already 🔥 - - >fn uses_trait[T: SomeTrait](value: T): ... # still 🔥 - ``` - -- The `*_` expression in parameter expressions is now required to occur at the - end of a positional parameter list, instead of being allowed in the middle. - - ```mojo - # No longer supported - alias FirstUnbound = SomeStruct[*_, 42] - alias MidUnbound = SomeStruct[7, *_, 6] - # Still supported - alias LastUnbound = SomeStruct[42, *_] - ``` - - We narrowed this because we want to encourage type designers - to get the order of parameters right, and want to extend `*_` to support - keyword parameters as well in the future. - -### Standard library changes - -#### ⭐️ New - -- `DynamicVector` has been renamed to - [`List`](/mojo/stdlib/collections/list.html#list), and has moved from the - `collections.vector` module to the `collections.list` module. In addition: - - - You can now construct a `List` from a variadic number of values. For - example: - - ```mojo - var numbers = List[Int](1, 2, 3) - ``` - - - `List` and - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - types now support negative indexing. This means that you can write `vec[-1]` - which is equivalent to `vec[len(vec)-1]`. - - - `List.push_back()` has been removed. Please use the `append()` function - instead. - -- The [`print()`](/mojo/stdlib/builtin/io#print) function now takes `sep` and - `end` keyword arguments. This means that you can write: - - ```mojo - print("Hello", "Mojo", sep=", ", end="!!!\n") # prints Hello, Mojo!!! - ``` - - `sep` defaults to the empty string and `end` defaults to "\n". - - Also, the `print_no_newline()` function has been removed. Please use - `print(end="")` instead. - -- The [`FloatLiteral`](/mojo/stdlib/builtin/float_literal#floatliteral) type is - now an infinite-precision nonmaterializable type. This means you can do - compile-time calculations using `FloatLiteral` without rounding errors. When - materialized at runtime, a `FloatLiteral` value is converted to a - [`Float64`](/mojo/stdlib/builtin/simd). - - ```mojo - # third is an infinite-precision FloatLiteral value - alias third = 1.0 / 3.0 - # t is a Float64 - var t = third - ``` - -- String types all conform to the - [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means - that you can now call `int("123")` to get the integer `123`. If the integer - cannot be parsed from the string, then an error is raised. - -- The `Tensor` type now has [`argmax()`](/mojo/stdlib/tensor/tensor#argmax) and - [`argmin()`](/mojo/stdlib/tensor/tensor#argmin) functions to compute the - position of the max or min value. Note: this should return a `Tensor[Int]` - but currently the output tensor is the same type as the input tensor. This - will be fixed in a future release. - -- Added a new - [`collections.OptionalReg`](/mojo/stdlib/collections/optional#optionalreg) - type, a register-passable alternative to - [`Optional`](/mojo/stdlib/collections/optional#optional). - -- The [`ulp()`](/mojo/stdlib/math/math#ulp) function has been added to the - `math` module. This allows you to get the units of least precision (or units - of last place) of a floating point value. - -#### 🦋 Changed - -- The `simd_load()`, `simd_store()`, `aligned_simd_load()`, and - `aligned_simd_store()` methods on - [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and - [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been merged into - a more expressive set of `load()` and `store()` methods with keyword-only - `width` and `alignment` parameters: - - ```mojo - # Doesn't work - my_simd = my_buffer.simd_load[simd_width](index) - # Works - my_simd = my_buffer.load[width=simd_width](index) - # Doesn't work - my_buffer.aligned_simd_store[width, alignment](my_simd) - # Works - my_buffer.store[width=width, alignment=alignment](my_simd) - ``` - -- The - [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable#equalitycomparable) - trait now requires the `__ne__()` method for conformance in addition to the - previously required `__eq__()` method. - -- Many types now declare conformance to `EqualityComparable` trait. - -- [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) parameter order - has changed to `StaticTuple[type, size]` for consistency with `SIMD` and - similar collection types. - -- The signature of the - [`elementwise()`](/mojo/stdlib/algorithm/functional#elementwise) function has - been changed. The new order is is `function`, `simd_width`, and then `rank`. - As a result, the rank parameter can now be inferred and one can call - `elementwise()` without it: - - ```mojo - elementwise[func, simd_width](shape) - ``` - -- `PythonObject` is now register-passable. - -- `PythonObject.__iter__()` now works correctly on more types of iterable Python - objects. Attempting to iterate over non-iterable objects will now raise an - exception instead of behaving as if iterating over an empty sequence. - `__iter__()` also now borrows `self` rather than requiring `inout`, allowing - code like: - - ```mojo - for value in my_dict.values(): - ... - ``` - -#### 🚚 Moved - -- We took the opportunity to rehome some modules into their correct package - as we were going through the process of open-sourcing the Mojo standard - library. Specifically, the following are some breaking changes worth - calling out. Please update your import statements accordingly. - - - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), - [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer), and friends have moved - from the `memory` package into a new `buffer` package. - - ```mojo - from buffer import Buffer, NDBuffer - ``` - - - `utils.list`, including the [`Dim`](/mojo/stdlib/buffer/list#dim) and - [`DimList`](/mojo/stdlib/buffer/list#dimlist) types, has moved to - the `buffer` package. - - ```mojo - from buffer import Dim, DimList - ``` - - - The [`parallel_memcpy()`](/mojo/stdlib/buffer/memory#parallel_memcpy) - function has moved from the `memory` package into the `buffer` package. - - ```mojo - from buffer import parallel_memcpy - ``` - - - The [`rand()`](/mojo/stdlib/tensor/random#rand) and - [`randn()`](/mojo/stdlib/tensor/random#randn) functions from the `random` - package that return a `Tensor` have moved to the `tensor` package. Note that - the overloads that write to a `DTypePointer` remain in the `random` package. - - If you happen to be using both versions in the same source file, you can - import them both using the `import as` syntax: - - ```mojo - from tensor import rand - from random import rand as rand_dt - ``` - - - The `trap()` function has been renamed to - [`abort()`](/mojo/stdlib/os/os#abort). It also has moved from the `debug` - module to the `os` module. - - ```mojo - from os import abort - ``` - - - The [`isinf()`](/mojo/stdlib/math/math#isinf) and - [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from - `math.limits` to the `math` module. - - ```mojo - from math import ininf, isfinite - ``` - -### Tooling changes - -#### ⭐️ New - -- Docstring code blocks can now use `%#` to hide lines of code from - documentation generation. - - For example: - - ```mojo - var value = 5 - %# print(value) - ``` - - Will generate documentation of the form: - - ```mojo - var value = 5 - ``` - - Hidden lines are processed as if they were normal code lines during test - execution. This allows for writing additional code within a docstring - example that is only used to ensure the example is runnable/testable. - -- The Mojo LSP server now allow you to specify additional search paths to use - when resolving imported modules in a document. You can specify search paths - on the command line, using the `-I` option, or you can add them to the - `mojo.lsp.includeDirs` setting in the VS Code extension. - -### Other changes - -#### ❌ Removed - -- The `__get_address_as_lvalue` magic function has been removed. You can now - get an LValue from a `Pointer` or `Reference` by using the dereference - operator (`[]`): - - ```mojo - var ptr: Pointer[MyRecord] - ... - # Doesn't work - __get_address_as_lvalue(ptr.value) = MyRecord(3, 5) - # Works - ptr[] = MyRecord(3, 5) - ``` - -- The type parameter for the `memcpy` function is now automatically inferred. - This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` will - no longer work and the user would have to change the code to `memcpy(...)`. - -- The [`memcpy()`](/mojo/stdlib/memory/memory#memcpy) overload that worked on - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types has been removed in favor - of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe#pointer) and - [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer): - - ```mojo - # Doesn't work - memcpy(destBuffer, srcBuffer, count) - # Works - memcpy(destBuffer.data, srcBuffer.data, count) - ``` - -- The functions `max_or_inf()`, `min_or_neginf()` have been removed from - `math.limit`. These functions were only used by the SIMD type. - -- As mentioned previously, the `print_no_newline()` function has been removed. - Please use `print(end="")` instead. - -#### 🛠️ Fixed - -- [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference - now recursively matches function types. -- [#951](https://github.com/modularml/mojo/issues/951) - Functions that were - both `async` and `@always_inline` incorrectly errored. -- [#1858](https://github.com/modularml/mojo/issues/1858) - Trait with parametric - methods regression. -- [#1892](https://github.com/modularml/mojo/issues/1892) - Forbid unsupported - decorators on traits. -- [#1735](https://github.com/modularml/mojo/issues/1735) - Trait-typed values - are incorrectly considered equal. -- [#1909](https://github.com/modularml/mojo/issues/1909) - Crash due to nested - import in unreachable block. -- [#1921](https://github.com/modularml/mojo/issues/1921) - Parser crashes - binding Reference to lvalue with subtype lifetime. -- [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` - should return `T` instead of `Optional[T]`. -- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` - to floating point or integral types. -- [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` - does not work when specifying `end=""` -- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods - correctly handle edge cases where `size_out >= size`. - -## v24.1.1 (2024-03-18) - -This release includes installer improvements and enhanced error reporting for -installation issues. Otherwise it is functionally identical to Mojo 24.1. - -## v24.1 (2024-02-29) - -### 🔥 Legendary - -- Mojo is now bundled with [the MAX platform](/max)! - - As such, the Mojo package version now matches the MAX version, which follows - a `YY.MAJOR.MINOR` version scheme. Because this is our first release in 2024, - that makes this version `24.1`. - -- Mojo debugging support is here! The Mojo VS Code extension includes debugger - support. For details, see [Debugging](/mojo/tools/debugging) in the Mojo - Manual. - -### ⭐️ New - -- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our - collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` - checks, and requires member elements to conform to the `KeyElement` trait. - - ```mojo - from collections import Set - - var set = Set[Int](1, 2, 3) - print(len(set)) # 3 - set.add(4) - - for element in set: - print(element[]) - - set -= Set[Int](3, 4, 5) - print(set == Set[Int](1, 2)) # True - print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True - let element = set.pop() - print(len(set)) # 1 - ``` - -- Mojo now supports the `x in y` expression as syntax sugar for - `y.__contains__(x)` as well as `x not in y`. - -- Mojo now has support for keyword-only arguments and parameters. For example: - - ```mojo - fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2): - print(a * b * c * d) - - my_product(3, c=5) # prints '30' - my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c' - ``` - - This includes support for declaring signatures that use both variadic and - keyword-only arguments/parameters. For example, the following is now possible: - - ```mojo - fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: - var res = 1 - for i in range(len(args)): - res *= args[i] - return res + offset - - print(prod_with_offset(2, 3, 4, 10)) # prints 240 - print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 - ``` - - Note that variadic keyword-only arguments/parameters (for example, `**kwargs`) - are not supported yet. That is, the following is not allowed: - - ```mojo - fn variadic_kw_only(a: Int, **kwargs): ... - ``` - - For more information, see - [Positional-only and keyword-only arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments) - in the Mojo Manual. - -- The `print()` function now accepts a keyword-only argument for the `end` - which is useful for controlling whether a newline is printed or not - after printing the elements. By default, `end` defaults to "\n" as before. - -- The Mojo SDK can now be installed on AWS Graviton instances. - -- A new version of the [Mojo Playground](/mojo/playground) is available. The new - playground is a simple interactive editor for Mojo code, similar to the Rust - Playground or Go Playground. The old - [JupyterLab based playground](https://playground.modular.com) will remain - online until March 20th. - -- The Mojo LSP server will now generate fixits for populating empty - documentation strings: - - ```mojo - fn foo(arg: Int): - """""" # Unexpected empty documentation string - ``` - - Applying the fixit from above will generate: - - ```mojo - fn foo(arg: Int): - """[summary]. - - Args: - arg: [description]. - """ - ``` - -- Added new `*_` syntax that allows users to explicitly unbind any number of - positional parameters. For example: - - ```mojo - struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass - - alias all_unbound = StructWithDefault[*_] - # equivalent to - alias all_unbound = StructWithDefault[_, _, _, _] - - alias first_bound = StructWithDefault[5, *_] - # equivalent to - alias first_bound = StructWithDefault[5, _, _, _] - - alias last_bound = StructWithDefault[*_, 6] - # equivalent to - alias last_bound = StructWithDefault[_, _, _, 6] - - alias mid_unbound = StructWithDefault[3, *_, 4] - # equivalent to - alias mid_unbound = StructWithDefault[3, _, _, 4] - ``` - - As demonstrated above, this syntax can be used to explicitly unbind an - arbitrary number of parameters, at the beginning, at the end, or in the - middle of the operand list. Since these unbound parameters must be explicitly - specified at some point, default values for these parameters are not applied. - For example: - - ```mojo - alias last_bound = StructWithDefault[*_, 6] - # When using last_bound, you must specify a, b, and c. last_bound - # doesn't have a default value for `c`. - var s = last_bound[1, 2, 3]() - ``` - - For more information see the Mojo Manual sections on - [partially-bound types](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types) - and - [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). - -- [`DynamicVector`](/mojo/stdlib/collections/list#list) now - supports iteration. Iteration values are instances of - [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: - - ```mojo - var v: DynamicVector[String]() - v.append("Alice") - v.append("Bob") - v.append("Charlie") - for x in v: - x[] = str("Hello, ") + x[] - for x in v: - print(x[]) - ``` - -- `DynamicVector` now has - [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and - [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. - -- The `mojo package` command now produces compilation agnostic packages. - Compilation options such as O0, or --debug-level, are no longer needed or - accepted. As a result, packages are now smaller, and extremely portable. - -- Initializers for `@register_passable` values can (and should!) now be - specified with `inout self` arguments just like memory-only types: - - ```mojo - @register_passable - struct YourPair: - var a: Int - var b: Int - fn __init__(inout self): - self.a = 42 - self.b = 17 - fn __copyinit__(inout self, existing: Self): - self.a = existing.a - self.b = existing.b - ``` - - This form makes the language more consistent, more similar to Python, and - easier to implement advanced features for. There is also no performance - impact of using this new form: the compiler arranges to automatically return - the value in a register without requiring you to worry about it. - - The older `-> Self` syntax is still supported in this release, but will be - removed in a subsequent one, so please migrate your code. One thing to watch - out for: a given struct should use one style or the other, mixing some of - each won't work well. - -- The standard library `slice` type has been renamed to - [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` - function has been introduced. This makes Mojo closer to Python and makes the - `Slice` type follow the naming conventions of other types like `Int`. - -- "Slice" syntax in subscripts is no longer hard coded to the builtin `slice` - type: it now works with any type accepted by a container's `__getitem__()` - method. For example: - - ```mojo - @value - struct UnusualSlice: - var a: Int - var b: Float64 - var c: String - - struct YourContainer: - fn __getitem__(self, slice: UnusualSlice) -> T: ... - ``` - - Given this implementation, you can subscript into an instance of - `YourContainer` like `yc[42:3.14:"🔥"]` and the three values are passed to the - `UnusualSlice` constructor. - -- The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to - return an MLIR internal reference type. - -- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) - method, for moving a value from one pointer memory location to another. - -- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be - used to format any value whose type implements the - [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. - -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements - `__is__` and `__isnot__` so that you can use expressions of the form `x is y` - and `x is not y` with `PythonObject`. - -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the - `SizedRaising` trait. This means the built-in - [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. - -- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) - and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. - -- A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query - properties on paths. - -- The `os` package now has a - [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms - to the `PathLike` trait by implementing the `__fspath__()` function. - -- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to - query properties of the path. - -- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on - [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` - module to work on `PathLike` structs. For example, the following sample - lists all the directories in the `/tmp` directory: - - ```mojo - from pathlib import Path - - fn walktree(top: Path, inout files: DynamicVector[Path]): - try: - var ls = top.listdir() - for i in range(len(ls)): - var child = top / ls[i] - if child.is_dir(): - walktree(child, files) - elif child.is_file(): - files.append(child) - else: - print("Skipping '" + str(child) + "'") - except: - return - - fn main(): - var files = DynamicVector[Path]() - - walktree(Path("/tmp"), files) - - for i in range(len(files)): - print(files[i]) - ``` - -- The [`find()`](/mojo/stdlib/builtin/string_literal#find), - [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), - [`count()`](/mojo/stdlib/builtin/string_literal#count), and - [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods - now work on string literals. This means that you can write: - - ```mojo - if "Mojo" in "Hello Mojo": - ... - ``` - -- Breakpoints can now be inserted programmatically within the code using the - builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. - - Note: on Graviton instances, the debugger might not be able to resume after - hitting this kind of breakpoint. - -- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that - describes a type that can be represented as a boolean value. To conform to the - trait, a type must implement the `__bool__()` method. - -- Modules within packages can now use purely relative `from` imports: - - ```mojo - from . import another_module - ``` - -- Trivial types, like MLIR types and function types, can now be bound implicitly - to traits that require copy constructors or move constructors, such as - [`Movable`](/mojo/stdlib/builtin/value.html#movable), - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). - -- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory - value. We hope and expect that this will eventually be replaced by - `Reference(expr).lifetime` as the parameter system evolves, but this is - important in the meantime for use in function signatures. - -- A new magic `__type_of(expr)` call will yield the type of a value. This allows - one to refer to types of other variables. For example: - - ```mojo - fn my_function(x: Int, y: __type_of(x)) -> Int: - let z: __type_of(x) = y - return z - ``` - -### 🦋 Changed - -- As another step towards [removing let - declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md) - we have removed support for let declarations inside the compiler. To ease - migration, we parse `let` declarations as a `var` declaration so your code - won't break. We emit a warning about this, but please switch your code to - using `var` explicitly, because this migration support will be removed in a - subsequent update. - - ```mojo - fn test(): - # treated as a var, but please update your code! - let x = 42 # warning: 'let' is being removed, please use 'var' instead - x = 9 - ``` - -- It is no longer possible to explicitly specify implicit argument parameters in - [automatically parameterized - functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). - This ability was an oversight and this is now an error: - - ```mojo - fn autoparameterized(x: SIMD): - pass - - autoparameterized[DType.int32, 1](3) # error: too many parameters - ``` - -- `vectorize_unroll` has been removed, and - [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter - named `unroll_factor` with a default value of 1. Increasing `unroll_factor` - may improve performance at the cost of binary size. See the - [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) - for more details. - -- The `vectorize` signatures have changed with the closure `func` moved to the - first parameter: - - ```mojo - vectorize[func, width, unroll_factor = 1](size) - vectorize[func, width, size, unroll_factor = 1]() - ``` - - The doc string has been updated with examples demonstrating the difference - between the two signatures. - -- The `unroll` signatures have changed with the closure `func` moved to the - first parameter: - - ```mojo - unroll[func, unroll_count]() - ``` - -- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) and - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types have changed. Now, both - take the type as the first parameter and no longer require the shape - parameter. This allows you to use these types and have sensible defaults. - For example: - - ```mojo - NDBuffer[DType.float32, 3] - ``` - - is equivalent to - - ```mojo - NDBuffer[DType.float32, 3, DimList.create_unknown[3]()] - ``` - - Users can still specify the static shape (if known) to the type: - - ```mojo - NDBuffer[DType.float32, 3, DimList(128, 128, 3)] - ``` - -- The error message for missing function arguments is improved: instead of - describing the number of arguments (e.g. `callee expects at least 3 arguments, - but 1 was specified`) the missing arguments are now described by - name (e.g. `missing 2 required positional arguments: 'b', 'c'`). - -- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait - is now a built-in trait and has been removed from `collections.vector`. - -- The `DynamicVector(capacity: Int)` constructor has been changed to take - `capacity` as a keyword-only argument to prevent implicit conversion from - `Int`. - -- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a - copy. - -- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` - and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - -- The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. - We originally thought about using those as language sugar for references, but - we believe that generic language features combined with the - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good - experience without dedicated sugar. - -### 🛠️ Fixed - -- [#435](https://github.com/modularml/mojo/issues/435) - Structs with Self type don't always work. -- [#1540](https://github.com/modularml/mojo/issues/1540) - Crash in register_passable self referencing struct. -- [#1664](https://github.com/modularml/mojo/issues/1664) - Improve error - message when `StaticTuple` is constructed with a negative size for - the number of elements. -- [#1679](https://github.com/modularml/mojo/issues/1679) - crash on SIMD of zero - elements. -- Various crashes on invalid code: - [#1230](https://github.com/modularml/mojo/issues/1230), - [#1699](https://github.com/modularml/mojo/issues/1699), - [#1708](https://github.com/modularml/mojo/issues/1708) -- [#1223](https://github.com/modularml/mojo/issues/1223) - Crash when parametric - function is passed as (runtime) argument. The parser now errors out instead. -- [#1530](https://github.com/modularml/mojo/issues/1530) - Crash during - diagnostic emission for parameter deduction failure. -- [#1538](https://github.com/modularml/mojo/issues/1538) and [#1607]( - https://github.com/modularml/mojo/issues/1607) - Crash when returning type - value instead of instance of expected type. This is a common mistake and the - error now includes a hint to point users to the problem. -- [#1613](https://github.com/modularml/mojo/issues/1613) - Wrong type name in - error for incorrect `self` argument type in trait method declaration. -- [#1670](https://github.com/modularml/mojo/issues/1670) - Crash on implicit - conversion in a global variable declaration. -- [#1741](https://github.com/modularml/mojo/issues/1741) - Mojo documentation - generation doesn't show `inout`/`owned` on variadic arguments. -- [#1621](https://github.com/modularml/mojo/issues/1621) - VS Code does not - highlight `raises` and `capturing` in functional type expressions. -- [#1617](https://github.com/modularml/mojo/issues/1617) - VS Code does not - highlight `fn` in specific contexts. -- [#1740](https://github.com/modularml/mojo/issues/1740) - LSP shows unrelated - info when hovering over a struct. -- [#1238](https://github.com/modularml/mojo/issues/1238) - File shadows Mojo - package path. -- [#1429](https://github.com/modularml/mojo/issues/1429) - Crash when using - nested import statement. -- [#1322](https://github.com/modularml/mojo/issues/1322) - Crash when missing - types in variadic argument. -- [#1314](https://github.com/modularml/mojo/issues/1314) - Typecheck error when - binding alias to parametric function with default argument. -- [#1248](https://github.com/modularml/mojo/issues/1248) - Crash when importing - from file the same name as another file in the search path. -- [#1354](https://github.com/modularml/mojo/issues/1354) - Crash when importing - from local package. -- [#1488](https://github.com/modularml/mojo/issues/1488) - Crash when setting - generic element field. -- [#1476](https://github.com/modularml/mojo/issues/1476) - Crash in interpreter - when calling functions in parameter context. -- [#1537](https://github.com/modularml/mojo/issues/1537) - Crash when copying - parameter value. -- [#1546](https://github.com/modularml/mojo/issues/1546) - Modify nested vector - element crashes parser. -- [#1558](https://github.com/modularml/mojo/issues/1558) - Invalid import causes - parser to crash. -- [#1562](https://github.com/modularml/mojo/issues/1562) - Crash when calling - parametric type member function. -- [#1577](https://github.com/modularml/mojo/issues/1577) - Crash when using - unresolved package as a variable. -- [#1579](https://github.com/modularml/mojo/issues/1579) - Member access into - type instances causes a crash. -- [#1602](https://github.com/modularml/mojo/issues/1602) - Interpreter failure - when constructing strings at compile time. -- [#1696](https://github.com/modularml/mojo/issues/1696) - Fixed an issue that - caused syntax highlighting to occasionally fail. -- [#1549](https://github.com/modularml/mojo/issues/1549) - Fixed an issue when - the shift amount is out of range in `SIMD.shift_left` and `SIMD.shift_right`. - -## v0.7.0 (2024-01-25) - -### ⭐️ New - -- A new Mojo-native dictionary type, - [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. - `Dict` stores values that conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. Keys need to conform to the new - [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is - not yet implemented by other standard library types. In the short term, you - can create your own wrapper types to use as keys. For example, the following - sample defines a `StringKey` type and uses it to create a dictionary that maps - strings to `Int` values: - - ```mojo - from collections.dict import Dict, KeyElement - - @value - struct StringKey(KeyElement): - var s: String - - fn __init__(inout self, owned s: String): - self.s = s ^ - - fn __init__(inout self, s: StringLiteral): - self.s = String(s) - - fn __hash__(self) -> Int: - return hash(self.s) - - fn __eq__(self, other: Self) -> Bool: - return self.s == other.s - - fn main() raises: - var d = Dict[StringKey, Int]() - d["cats"] = 1 - d["dogs"] = 2 - print(len(d)) # prints 2 - print(d["cats"]) # prints 1 - print(d.pop("dogs")) # prints 2 - print(len(d)) # prints 1 - ``` - - We plan to add `KeyElement` conformance to standard library types in - subsequent releases. - -- Users can opt-in to assertions used in the standard library code by - specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to - compile your source file(s). In the case that an assertion is fired, - the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are _not enabled_ - in the standard library right now for performance reasons. - -- The Mojo Language Server now implements the References request. IDEs use - this to provide support for **Go to References** and **Find All References**. - A current limitation is that references outside of the current document are - not supported, which will be addressed in the future. - -- The [`sys.info`](/mojo/stdlib/sys/info) module now includes - `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` - functions. - -- Homogenous variadic arguments consisting of memory-only types, such as - `String` are more powerful and easier to use. These arguments are projected - into a - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). - - (Previous releases made it easier to use variadic lists of register-passable - types, like `Int`.) - - Subscripting into a `VariadicListMem` now returns the element instead of an - obscure internal type. In addition, we now support `inout` and `owned` - variadic arguments: - - ```mojo - fn make_worldly(inout *strs: String): - # This "just works" as you'd expect! - for i in range(len(strs)): - strs[i] += " world" - fn main(): - var s1: String = "hello" - var s2: String = "konnichiwa" - var s3: String = "bonjour" - make_worldly(s1, s2, s3) - print(s1) # hello world - print(s2) # konnichiwa world - print(s3) # bonjour world - ``` - - (Previous releases made it easier to use variadic lists, but subscripting into - a `VariadicListMem` returned a low-level pointer, which required the user to - call `__get_address_as_lvalue()` to access the element.) - - Note that subscripting the variadic list works nicely as above, but - iterating over the variadic list directly with a `for` loop produces a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead - of the desired value, so an extra subscript is required; We intend to fix this - in the future. - - ```mojo - fn make_worldly(inout *strs: String): - # Requires extra [] to dereference the reference for now. - for i in strs: - i[] += " world" - ``` - - Heterogenous variadic arguments have not yet been moved to the new model, but - will in future updates. - - Note that for variadic arguments of register-passable types like `Int`, the - variadic list contains values, not references, so the dereference operator - (`[]`) is not required. This code continues to work as it did previously: - - ```mojo - fn print_ints(*nums: Int): - for num in nums: - print(num) - print(len(nums)) - ``` - -- Mojo now has a prototype version of a safe - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's - lifetime tracking pass can reason about references to safely extend local - variable lifetime, and check indirect access safety. The `Reference` type - is brand new (and currently has no syntactic sugar) so it must be explicitly - dereferenced with an empty subscript: `ref[]` provides access to the - underlying value. - - ```mojo - fn main(): - var a: String = "hello" - var b: String = " references" - - var aref = Reference(a) - aref[] += b - print(a) # prints "hello references" - - aref[] += b - # ^last use of b, it is destroyed here. - - print(aref[]) # prints "hello references references" - # ^last use of a, it is destroyed here. - ``` - - While the `Reference` type has the same in-memory representation as a C - pointer or the Mojo `Pointer` type, it also tracks a symbolic "lifetime" value - so the compiler can reason about the potentially accessed set of values. This - lifetime is part of the static type of the reference, so it propagates through - generic algorithms and abstractions built around it. - - The `Reference` type can form references to both mutable and immutable memory - objects, e.g. those on the stack or borrowed/inout/owned function arguments. - It is fully parametric over mutability, eliminating the [problems with code - duplication due to mutability - specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and - provides the base for unified user-level types. For example, it could be - used to implement an array slice object that handles both mutable and immutable - array slices. - - While this is a major step forward for the lifetimes system in Mojo, it is - still _very_ early and awkward to use. Notably, there is no syntactic sugar - for using references, such as automatic dereferencing. Several aspects of it - need to be more baked. It is getting exercised by variadic memory arguments, - which is why they are starting to behave better now. - - Note: the safe `Reference` type and the unsafe pointer types are defined in - the same module, currently named `memory.unsafe`. We expect to restructure - this module in a future release. - -- Mojo now allows types to implement `__refattr__()` and `__refitem__()` to - enable attribute and subscript syntax with computed accessors that return - references. For common situations where these address a value in memory this - provides a more convenient and significantly more performant alternative to - implementing the traditional get/set pairs. Note: this may be changed in the - future when references auto-dereference—at that point we may switch to just - returning a reference from `__getattr__()`. -- Parametric closures can now capture register passable typed values by copy - using the `__copy_capture` decorator. For example, the following code will - print `5`, not `2`. - - ```mojo - fn foo(x: Int): - var z = x - - @__copy_capture(z) - @parameter - fn formatter() -> Int: - return z - z = 2 - print(formatter()) - - fn main(): - foo(5) - ``` - -- String now implements KeyElement and may be used as a key in Dict. -- More robust support for structs with fields of self referencing types. - For example, the following code will work and print `0`: - - ```mojo - struct Foo(CollectionElement): - var vec: DynamicVector[Self] - - fn __init__(inout self: Self): - self.vec = DynamicVector[Self]() - - fn __moveinit__(inout self: Self, owned existing: Self): - self.vec = existing.vec ^ - - fn __copyinit__(inout self: Self, existing: Self): - self.vec = existing.vec - - - fn main(): - var foo = Foo() - print(len(foo.vec)) - ``` - -### ❌ Removed - -- The `__takeinit__` special constructor form has been removed from the - language. This "non-destructive move" operation was previously wired into the - `x^` transfer operator, but had unpredictable behavior that wasn't consistent. - Now that Mojo has traits, it is better to model this as an explicit `.take()` - operation on a type, which would transfer out the contents of the type without - ending its lifetime. For example, for a type that holds a pointer, `take()` - might return a new instance pointing to the same data, and null out its own - internal pointer. - - This change makes it clear when a lifetime is ended versus when the - contents of an LValue are explicitly taken. - -- The current implementation of autotuning has been deprecated, as Mojo's - autotuning implementation is undergoing a redesign. Tutorials around the - current implementation have also been removed as they are being rewritten. - - Consequently, the `autotune()`, `autotune_fork()`, and `search()` functions - have been removed from the standard library. - -- The `_OldDynamicVector` type that worked only on register passable element - types has been removed. Please migrate uses to - [`DynamicVector`](/mojo/stdlib/collections/list#list) which - works on both register passable and memory types. - -- The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using - either [`DynamicVector`](/mojo/stdlib/collections/list#list) - or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - instead. - -- The `@adaptive` decorator has been removed from the language. Any uses of the - decorator in a non-search context can be replaced with `@parameter if`. For - example: - - ```mojo - @adaptive - fn foo[a: Bool](): - constrained[a]() - body1() - - @adaptive - fn foo[a: Bool](): - constrained[not a]() - body2() - ``` - - Can be rewritten as: - - ```mojo - fn foo[a: Bool](): - @parameter - if a: - body1() - else: - body2() - ``` - - Consequently, the special `__adaptive_set` attribute has been removed as well. - -- Result parameters have been removed from Mojo. Result parameter declarations - in function parameter lists are no longer allowed, nor are forward alias - declarations. This includes removing the `param_return` statement. - -- The `@noncapturing` and `@closure` decorators have been removed due to - refinements and improvements to the closure model. See below for more details! - -### 🦋 Changed - -- The Mojo closure model has been refined to be more straightforward and safe. - Mojo has two closure types: parameter closures and runtime closures. Parameter - closures can be used in higher-order functions and are the backbone of - functions like `vectorize` and `parallelize`. They are always denoted by - `@parameter` and have type `fn() capturing -> T` (where `T` is the return - type). - - On the other hand, runtime closures are always dynamic values, capture values - by invoking their copy constructor, and retain ownership of their capture - state. You can define a runtime closure by writing a nested function that - captures values: - - ```mojo - fn outer(b: Bool, x: String) -> fn() escaping -> None: - fn closure(): - print(x) # 'x' is captured by calling String.__copyinit__ - - fn bare_function(): - print("hello") # nothing is captured - - if b: - # closure can be safely returned because it owns its state - return closure^ - - # function pointers can be converted to runtime closures - return bare_function - ``` - - The type of runtime closures are of the form `fn() escaping -> T`. You - can pass equivalent function pointers as runtime closures. - - Stay tuned for capture list syntax for move capture and capture by reference, - and a more unified closure model! - -- The `@unroll(n)` decorator can now take a parameter expression for - the unroll factor, i.e. `n` can be a parameter expression that is - of integer type. - -- The `cpython` module in the `python` package has been moved to be an internal - module, i.e, `_cpython`. - -- `AnyType` and `Destructable` have been unified into a single trait, `AnyType`. - Every nominal type (i.e. all structs) now automatically conform to `AnyType`. - -- Previously, the `mojo package` command would output a Mojo package that - included both partly-compiled Mojo code, as well as fully-compiled machine - code for a specific computer architecture -- the architecture of the machine - being used to invoke the `mojo package` command. - - Now, `mojo package` only includes partly-compiled Mojo code. It is only fully - compiled for the specific computer architecture being used at the point that - the package is first `import`-ed. As a result, Mojo packages are smaller and - more portable. - -- The `simd_width` and `dtype` parameters of `polynomial_evaluate` have been - switched. Based on the request in - [#1587](https://github.com/modularml/mojo/issues/1587), the - `polynomial_evaluate` function has also been extended so that the - `coefficients` parameter can take either a either a - [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a - [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). - -- As a tiny step towards removing `let` declarations, this release removes the - warning: `'var' was never mutated, consider switching to a 'let'`. - -### 🛠️ Fixed - -- [#1595](https://github.com/modularml/mojo/issues/1595) - Improve error message - when trying to materialize `IntLiteral` in runtime code. -- Raising an error from the initializer of a memory-only type now works - correctly in the presence of complex control flow. Previously Mojo could run - the destructor on `self` before it was initialized when exiting with an - error. -- [#1096](https://github.com/modularml/mojo/issues/1096) - Improve warning - messages for dead code in conditionals like `or` expressions. -- [#1419](https://github.com/modularml/mojo/issues/1419) - Fix assertion failure - with uninitialized lattice values. -- [#1402](https://github.com/modularml/mojo/issues/1402) - Fix movable trait not - detected on recursive struct implemented with `AnyPointer`. -- [#1399](https://github.com/modularml/mojo/issues/1399) - Fix parser crash when - a parameter type in a struct that implements a trait is misspelled. -- [#1152](https://github.com/modularml/mojo/issues/1152) - Allow mutable `self` - argument when overloading operators using dunder methods. -- [#1493](https://github.com/modularml/mojo/issues/1493) - Fix crash in - `DynamicVector` copy constructor in certain situations. -- [#1316](https://github.com/modularml/mojo/issues/1316) - The `benchmark.keep` - function now properly handles vector types. -- [#1505](https://github.com/modularml/mojo/issues/1505) - The `simd.shuffle` - operation now works on 64 element permutations. -- [#1355](https://github.com/modularml/mojo/issues/1355) - Fix `String.find()` - returning wrong value when starting index is non-zero. -- [#1367](https://github.com/modularml/mojo/issues/1367) - Fix `String.replace()` - returning incorrect results for multi-character search strings. -- [#1535](https://github.com/modularml/mojo/issues/1535) - Invalid error `field - 'w.x.y' destroyed out of the middle of a value, preventing the overall value - from being destroyed`. -- [#1475](https://github.com/modularml/mojo/issues/1475) - Assertion failure in - nested loop. -- [#1591](https://github.com/modularml/mojo/issues/1591) - Assertion failure - when using `AnyType` struct member. -- [#1503](https://github.com/modularml/mojo/issues/1503) - Rename the mojo build - of LLDB to `mojo-lldb`, to prevent name collisions with the system's LLDB. -- [#1542](https://github.com/modularml/mojo/issues/1542) - `@unroll` does not - accept alias as unroll factor. -- [#1443](https://github.com/modularml/mojo/issues/1443) - Compiler crash on - variadic list of traits. -- [#1604](https://github.com/modularml/mojo/issues/1604) - Variable of trivial - type not destroyed by transferring ownership. -- [#1341](https://github.com/modularml/mojo/issues/1341) - Segmentation fault - when passing closures around. -- [#217](https://github.com/modularml/mojo/issues/217) - Closure state is - stack allocated. - -## v0.6.1 (2023-12-18) - -### ⭐️ New - -- The Mojo REPL now provides limited support for the `%cd` magic command. - - This command automatically maintains an internal stack of directories you - visit during the REPL session. Usage: - - - `%cd 'dir'`: change to directory `dir` and push it on the directory stack. - - `%cd -`: pop the directory stack and change to the last visited directory. - -- Structs decorated with `@value` now automatically conform to the - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. - -- [`String`](/mojo/stdlib/builtin/string.html#string) now has new - [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and - [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, - respectively, to Python's `str.toupper()` and `str.tolower()`. - -- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and - [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types - implementing the `__hash__()` method. Future releases will add `Hashable` - support to Standard Library types. In the meantime, the `hash` module includes - a version of the `hash()` function that works on arbitrary byte strings. To - generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you - use the internal `_hash_simd()` function: - - ```mojo - from builtin.hash import _hash_simd - - fn gen_simd_hash(): - let vector = SIMD[DType.int64, 4](1, 2, 3, 4) - let hash = _hash_simd(vector) - ``` - -- Several standard library types now conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), - [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), - [`DynamicVector`](/mojo/stdlib/collections/list#list), - [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), - and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). - -### 🦋 Changed - -- `utils.vector` has been moved to a new `collections` package to make - space for new collections. This means that if you had previous code - that did `from utils.vector import DynamicVector`, it now needs to - be `from collections.vector import DynamicVector` due to the move. - -- The special destructor method `__del__()` has been changed to enforce - that it cannot raise an error. Raising destructors are not supported properly - at the moment. - -### 🛠️ Fixed - -- [#1421](https://github.com/modularml/mojo/issues/1421) - Fixed a crash when - using Tuples in the REPL. - -- [#222](https://github.com/modularml/mojo/issues/222) - Generate an error - for obviously self recursive functions. - -- [#1408](https://github.com/modularml/mojo/issues/1408) - Fix overload - resolution when candidates can return generic types. - -- [#1413](https://github.com/modularml/mojo/issues/1413) and - [#1395](https://github.com/modularml/mojo/issues/1395) - Do not crash when - re-declaring a builtin declaration. - -- [#1307](https://github.com/modularml/mojo/issues/1307) - Fix compatibility of - function signatures that only differ in default argument values. - -- [#1380](https://github.com/modularml/mojo/issues/1380) - Fix printing - of empty `String`. - -## v0.6.0 (2023-12-04) - -### 🔥 Legendary - -- Traits have arrived! - - You can now define a _trait_, which consists of a required set of method - prototypes. A struct can _conform to_ the trait by implementing these methods. - This lets you write generic functions that work on any structs that conform to - a given trait. - - The following section gives a brief overview of traits—see the - [Mojo Manual](/mojo/manual/traits.html) and this - [traits blog post](https://modul.ar/traits-blog) for more details! - - Traits are declared with the `trait` keyword. The bodies of traits should - contain method signatures declared with `...` as their bodies. Default - method implementations are not supported yet. - - ```mojo - trait SomeTrait: - fn required_method(self, x: Int): ... - ``` - - The trait can be implemented on a struct by inheriting from it. - - ```mojo - struct SomeStruct(SomeTrait): - fn required_method(self, x: Int): - print("hello traits", x) - ``` - - You can then write a generic functions that accepts any type that conforms to - the trait. You do this by creating a parameterized function with a - trait-typed parameter: - - ```mojo - fn fun_with_traits[T: SomeTrait](x: T): - x.required_method(42) - ``` - - Which can be invoked with instances of types that conform to the trait: - - ```mojo - var thing = SomeStruct() - # Infer the parameter `T`! - fun_with_traits(thing) - ``` - - Traits can also inherit from other traits, which simply requires that - implementors of the child trait also conform to all parent traits. - - ```mojo - trait Parent: - fn parent_func(self): ... - - trait Child(Parent): - fn child_func(self): ... - ``` - - Then, both child and parent trait methods can be invoked on instances of - the trait `Child`. As well, an instance of the child trait can be converted to - an instance of the parent trait. - - ```mojo - fn the_parents[T: Parent](x: T): - x.parent_func() - - fn the_children[T: Child](x: T): - x.child_func() - x.parent_func() - # Upcast `x` from instance of `Child` to `Parent`. - the_parents(x) - ``` - - For more information, see the [Traits page](/mojo/manual/traits.html) - in the Mojo Manual. - -- A fundamental `Destructable` trait has been added to the language. This is a - core trait that every trait automatically conforms to. This enables - destruction of generic types and generic collections. - - **Note:** We're aware that this trait might be better spelled `Destructible`. - We're planning on removing it in the future and moving its functionality to - `AnyType` so that any type that doesn't provide its own destructor will have - a default, no-op destructor. - -- We've added some traits to the standard library, you can implement these on - your own types: - - - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) - - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) - - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) - - [`Intable`](/mojo/stdlib/builtin/int.html#intable) - - [`Sized`](/mojo/stdlib/builtin/len.html#sized) - - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - -- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), - [`str()`](/mojo/stdlib/builtin/str.html#str), and - [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with - types that implement the `Sized`, `Stringable`, and `Intable` traits, - respectively. - -- [`DynamicVector`](/mojo/stdlib/collections/list#list) is now a - proper generic collection that can use any type that implements the `Movable` - and `Copyable` traits. This means you can now write, for example, - `DynamicVector[String]`. Also, `DynamicVector` now invokes its element - destructors upon destruction, so `_del_old` has been deleted. - -- `print` now works on any types that implement `Stringable` by invoking their - `__str__` method: - - ```mojo - @value - struct BoxedInt(Stringable): - var value: Int - - fn __str__(self) -> String: - return self.value - - print(BoxedInt(11), "hello traits!", BoxedInt(42)) - ``` - -### ⭐️ New - -- The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include _everything_ about Mojo yet, but it includes a lot, - and more than the original [programming - manual](/mojo/programming-manual.html) (now deprecated). - - Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on - GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love - to accept contributions to help us improve them! - -- Mojo now supports partial automatic parameterization: when a function is - declared with an argument of a partially bound type, the unbound parameters - of that type are implicitly added to the function's input parameters. For - example: - - ```mojo - @value - struct Fudge[a: Int, b: Int, c: Int = 7]: ... - - # These function declarations are roughly equivalent: - fn eat(f: Fudge[5]): ... # implicitly parameterized - fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized - ``` - - In the first signature for `eat()`, the `b` parameter isn't bound, so it's - _implicitly_ added as an input parameter on the function. - - In the second signature for `eat()`, the author has explicitly defined an - input parameter (`_b`), which is bound to the second parameter on the argument - type (which happens to be `b`). - - Both functions can be called like this: - - ```mojo - eat(Fudge[5, 8]()) - ``` - - Mojo infers the value of the `b` parameter from the argument (in this case, - 8). - - With the second signature, you can also pass the `_b` parameter value - explicitly: - - ```mojo - eat[3](Fudge[5, 3]()) - ``` - - Moreover, Mojo now allows you to explicitly mark parameters as unbound using - the `_` as syntax meaning "placeholder for an unbound parameter." For example: - - ```mojo - # These function declarations are roughly equivalent: - fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized - fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized - fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized - ``` - - The first two signatures explicitly unbind the `b` and `c` parameters. - - In the last signature, the `_b` and `_c` parameters are explicitly declared by - the author, and bound to the `b` and `c` parameters in the argument type. - - Any of these signatures can be called like this: - - ```mojo - eat(Fudge[5, 8]()) - eat(Fudge[5, 8, 9]()) - ``` - - Note that the default parameter values of struct parameters are bound, unless - explicitly unbound by the user. - - For more information, see the - [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). - -- Parametric types can now be partially bound in certain contexts. For example, - a new `Scalar` type alias has been added defined as: - - ```mojo - alias Scalar = SIMD[size=1] - ``` - - Which creates a parametric type alias `Scalar` with a single parameter of type - `DType`. Types can also be partially or fully bound in other contexts. For - instance, `alias` declarations of type values inside functions now work - properly: - - ```mojo - fn type_aliases(): - alias T = SIMD - print(T[DType.float32, 1]()) - alias Partial = T[type=DType.int32] - print(Partial[2]()) - ``` - -- The `__mlir_op` feature now supports operations that return multiple results. - To use them, you write the `_type` field as a `Tuple` of types. For example: - - ```mojo - # The `ret` variable has type `Tuple[Int, Int]`. - let ret = __mlir_op.`multi_result_op`[_type=(Int, Int)]() - ``` - -- Mojo now has the ability to read raw bytes from a file using the - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. - For example: - - ```mojo - with open("file.binary", "r") as f: - data = f.read_bytes() - ``` - -- A size argument was added to the - [`read()`](/mojo/stdlib/builtin/file.html#read) and - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the - builtin `file.FileHandle`. The size argument defaults to -1 and maintains the - previous "read to EOF" behavior when size is negative. - - ```mojo - with open("file.binary", "r") as f: - data1 = f.read_bytes(1024) - data2 = f.read_bytes(256) - ``` - -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and - `read_text()` methods to read file contents from a path: - - ```mojo - let text_path = Path("file.txt") - let text = text_path.read_text() - - let binary_path = Path("file.binary") - let data = binary_path.read_bytes() - ``` - -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` - methods to save and load to file. These - methods preserve shape and datatype information. For example: - - ```mojo - let tensor = Tensor[DType.float32]() - tensor.save(path) - - let tensor_from_file = Tensor[DType.float32].load(path) - ``` - -- Subscripting added to - [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): - - ```mojo - let p = DTypePointer[DType.float16].alloc(4) - for i in range(4): - p[i] = i - print(p[i]) - ``` - -- `file.FileHandle` now has a `seek()` method. - -- [`String`](/mojo/stdlib/builtin/string.html#string) now has an - [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to - Python's `str.rfind()`. - -- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method - analogous to Python's `str.split()`. - -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a - [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to - Python's `pathlib.Path.suffix`. - -- The Mojo REPL now supports indented expressions, making it a bit easier to - execute expressions copied from an indented block (such as a doc string). - -- The Mojo Language Server now implements the Document Symbols request. IDEs use - this to provide support for **Outline View** and **Go to Symbol**. This - addresses [Issue #960](https://github.com/modularml/mojo/issues/960). - -- The Mojo Language Server now shows documentation when code completing modules - or packages in `import` statements. - -- The Mojo Language Server now supports processing code examples, defined as - markdown Mojo code blocks, inside of doc strings. This enables IDE features - while writing examples in API documentation. - -- The Mojo Language Server now provides semantic token information, providing - better highlighting for symbols whose semantics are not statically analyzable. - -- The Mojo Language Server now classifies doc strings as folding ranges, - making them easier to collapse, reducing vertical space while editing. - -- Command line options for the `mojo` driver that take arguments can now be - written in either of two ways: both `--foo FOO` and `--foo=FOO`. Previously, - only the former was valid. - -### 🦋 Changed - -- Variadic list types - [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) - are now iterable. Variadic arguments are automatically projected into one of - these types inside the function body, so var args can be iterated: - - ```mojo - fn print_ints(*nums: Int): - for num in nums: - print(num) - print(len(nums)) - ``` - -- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) - package now raise an `Error` when the assertion fails instead of returning a - `Bool` for whether the assertion succeeded or not. - -- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no - longer (implicitly) assumed to be register-passable. A new `AnyRegType` type - is used to represent generic types that are register passable. - -- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) - report is now an argument instead of a parameter: - - ```mojo - let report = benchmark.run[timer]() - report.print(Unit.ms) - ``` - -- Default values on `inout` arguments are no longer permitted, i.e. the - following will now raise an error: - - ```mojo - fn inout_default(inout x: Int = 2): ... - ``` - -- The `to_string()` function has been removed from - [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of - the new `__str__()` function. This composes better with traits so it can be - used with the generic `str()` function. - -### 🛠️ Fixed - -- [#734](https://github.com/modularml/mojo/issues/734) - Consumption of struct - works only for types with a `__del__` method. - -- [#910](https://github.com/modularml/mojo/issues/910) - Parser crash when - using memory-only generic type as return of function that `raise`s. - -- [#1060](https://github.com/modularml/mojo/issues/1060) - Mojo happily parses - code that has messed up indentation - -- [#1159](https://github.com/modularml/mojo/issues/1159) - The language server - doesn't warn about bad return type. - -- [#1166](https://github.com/modularml/mojo/issues/1166) - warning: unreachable - code after return statement with context manager - -- [#1098](https://github.com/modularml/mojo/issues/1098) - The language server - doesn't highlight properties of PythonObjects correctly. - -- [#1153](https://github.com/modularml/mojo/issues/1153) - The language server - crashes when parsing an invalid multi-nested module import. - -- [#1236](https://github.com/modularml/mojo/issues/1236) - The language server - doesn't show autocomplete in if statements. - -- [#1246](https://github.com/modularml/mojo/issues/1246) - Warning diagnostics - are transient in the presence of caching. - -### Known Issue - -- There is an issue affecting Jupyter notebooks that use autotuning and traits. - This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the _Matrix multiplication in - Mojo_ notebook. - -## v0.5.0 (2023-11-2) - -### ⭐️ New - -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the - architectural SIMD width of the type. This means you can write - `SIMD[DType.float32]` which is equivalent to - `SIMD[DType.float32, simdwidthof[DType.float32]()]`. - -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` - function that allows you to concatenate two `SIMD` values together and produce - a new `SIMD` value. - -- Mojo now supports compile-time _keyword parameters_, in addition to existing - support for [keyword - arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For - example: - - ```mojo - fn foo[a: Int, b: Int = 42](): - print(a, "+", b) - - foo[a=5]() # prints '5 + 42' - foo[a=7, b=13]() # prints '7 + 13' - foo[b=20, a=6]() # prints '6 + 20' - ``` - - Keyword parameters are also supported in structs: - - ```mojo - struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]: - fn __init__(inout self): - print(msg, a) - - fn use_kw_params(): - KwParamStruct[a=42]() # prints '🔥mojo🔥 42' - KwParamStruct[5, msg="hello"]() # prints 'hello 5' - KwParamStruct[msg="hello", a=42]() # prints 'hello 42' - ``` - - For more detail, see the [programming - manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). - - For the time being, the following notable limitations apply: - - - Keyword-only parameters are **not supported** yet: - - ```mojo - fn baz[*args: Int, b: Int](): pass # fails - fn baz[a: Int, *, b: Int](): pass # fails - ``` - - (The analogous keyword-only arguments in Python are described in - [PEP 3102](https://peps.python.org/pep-3102/).) - - - Variadic keyword parameters are **not supported** yet: - - ```mojo - fn baz[a: Int, **kwargs: Int](): pass # fails - ``` - -- Mojo now supports "automatic" parameterization of functions. What this means - is that if a function argument type is parametric but has no bound parameters, - they are automatically added as input parameters on the function. This works - with existing features to allow you to write parametric functions with less - boilerplate. - - ```mojo - @value - struct Thing[x: Int, y: Int]: - pass - - fn foo(v: Thing): - print(v.x) - print(v.y) - - fn main(): - let v = Thing[2, 3]() - foo(v) - ``` - - However, partial autoparameterization is **not supported** yet: - - ```mojo - fn foo(v: Thing[y=7]): # Partially bound type not allowed yet. - ... - ``` - -- Keyword argument passing is supported when invoking `__getitem__` using - the bracket syntax: - - ```mojo - @value - struct MyStruct: - fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: - return x * y + z - - MyStruct()[z=7, x=3, y=5] # returns 22 - ``` - - However, keyword argument passing to `__setitem__` using the bracket syntax is - **not supported** yet: - - ```mojo - @value - struct OtherStruct: - fn __setitem__(self, x: Int, y: Int): pass - - OtherStruct()[x=1] = 4 # fails - ``` - -- Function argument input parameters can now be referenced within the signature - of the function: - - ```mojo - fn foo(x: SIMD, y: SIMD[x.type, x.size]): - pass - ``` - -- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been - simplified and improved so you can now run: - - ```mojo - import benchmark - from time import sleep - - fn sleeper(): - sleep(.01) - - fn main(): - let report = benchmark.run[sleeper]() - print(report.mean()) - ``` - - It no longer requires a capturing `fn` so can benchmark functions outside the - same scope. - - You can print a report with: - - ```mojo - report.print() - ``` - - ```plaintext - --------------------- - Benchmark Report (s) - --------------------- - Mean: 0.012314264957264957 - Total: 1.440769 - Iters: 117 - Warmup Mean: 0.0119335 - Warmup Total: 0.023866999999999999 - Warmup Iters: 2 - Fastest Mean: 0.012227958333333334 - Slowest Mean: 0.012442699999999999 - ``` - - Units for all functions default to seconds, but can be changed with: - - ```mojo - from benchmark import Unit - - report.print[Unit.ms]() - ``` - -- Mojo now supports struct parameter deduction (a.k.a. class template argument - deduction, or CTAD) for partially bound types. Struct parameter deduction is - also possible from static methods. For example: - - ```mojo - @value - struct Thing[v: Int]: pass - - struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]: - fn __init__(inout self, x: Thing[a]): - print("hello", a, b, c) - - @staticmethod - fn foo(x: Thing[a]): - print("🔥", a, b, c) - - fn main(): - _ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8' - CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' - ``` - -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and - `tofile()` methods to save and load as bytes from a file. - -- The built-in `print()` function now works on the - [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. - -- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and - [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors - that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) - and [`StaticIntTuple`](/mojo/stdlib/utils/index_.html#staticinttuple) to - initialize shapes. - -- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the - `count()` and `find()` methods to enable counting the number of occurrences or - finding the offset index of a substring in a string. - -- The `String` type now has a `replace()` method which allows you to replace a - substring with another string. - -### 🦋 Changed - -- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) - moved under builtins, and no longer need to be imported. - -- Variadic arguments are now automatically projected into a `VariadicList` or - `VariadicListMem` inside the function body. This allows for more flexibility - in using var args. For example: - - ```mojo - fn print_ints(*nums: Int): - let len = len(nums) - for i in range(len): - print(nums[i]) - print(len) - ``` - -- The parameters for - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - have been switched. The parameters are now `[type, size]` instead of - `[size, type]`. The `InlinedFixedVector` now has a default size which means - that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` - and the default size is used. - -- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer.html#buffer) - and [`NDBuffer`](/mojo/stdlib/buffer/buffer.html#ndbuffer) is renamed to - `tofile()` to match the Python naming. - -- Mojo will now utilize all available cores across all NUMA sockets on the host - machine by default. The prior default behavior was to use all the cores on - the first socket. - -### ❌ Removed - -- The `math.numerics` module is now private, because its types (`FPUtils` and - `FlushDenormals`) should not be used externally. - -### 🛠️ Fixed - -- [#532](https://github.com/modularml/mojo/issues/532) - Compiler optimizing - while True loop away -- [#760](https://github.com/modularml/mojo/issues/760) - Compilation error: - 'hlcf.for.yield' op specifies 0 branch inputs but target expected 1 along - control-flow edge from here -- [#849](https://github.com/modularml/mojo/issues/849) - The `Tensor` type is - now initialized with zeros at construction time. -- [#912](https://github.com/modularml/mojo/issues/912) - Invalid load for - `__get_address_as_lvalue`. -- [#916](https://github.com/modularml/mojo/issues/916) - Parser crash when - specifying default values for `inout` arguments. -- [#943](https://github.com/modularml/mojo/issues/943) - Mojo hangs if you - use continue in the nested loop -- [#957](https://github.com/modularml/mojo/issues/957) - Parser crash when a - function call with variadic arguments of a memory-only type is evaluated at - compile time. -- [#990](https://github.com/modularml/mojo/issues/990) - Fixes rounding - issue with floor division with negative numerator. -- [#1018](https://github.com/modularml/mojo/issues/1018) - In some cases the - sort function was returning invalid results. This release fixes some of these - corner cases. -- [#1010](https://github.com/modularml/mojo/issues/1010) - Initializing tensor - in alias declaration results in crash. -- [#1110](https://github.com/modularml/mojo/issues/1110) - The `time.now()` - function now returns nanoseconds across all operating systems. -- [#1115](https://github.com/modularml/mojo/issues/1115) - cannot load - non-register passable type into SSA register. - -## v0.4.0 for Mac (2023-10-19) - -### 🔥 Legendary - -- Mojo for Mac! - - The Mojo SDK now works on macOS (Apple silicon). This is the same version - previously released for Linux. Get the latest version of the SDK for your Mac - system: - - [Download Now!](https://developer.modular.com/download) - -## v0.4.0 (2023-10-05) - -### ⭐️ New - -- Mojo now supports default parameter values. For example: - - ```mojo - fn foo[a: Int = 3, msg: StringLiteral = "woof"](): - print(msg, a) - - fn main(): - foo() # prints 'woof 3' - foo[5]() # prints 'woof 5' - foo[7, "meow"]() # prints 'meow 7' - ``` - - Inferred parameter values take precedence over defaults: - - ```mojo - @value - struct Bar[v: Int]: - pass - - fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]): - print(msg, a) - - fn main(): - foo(Bar[9]()) # prints 'quack 9' - ``` - - Structs also support default parameters: - - ```mojo - @value - struct DefaultParams[msg: StringLiteral = "woof"]: - alias message = msg - - fn main(): - print(DefaultParams[]().message) # prints 'woof' - print(DefaultParams["meow"]().message) # prints 'meow' - ``` - -- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O - support. You can now write: - - ```mojo - var f = open("my_file.txt", "r") - print(f.read()) - f.close() - ``` - - or - - ```mojo - with open("my_file.txt", "r") as f: - print(f.read()) - ``` - -- Mojo now allows context managers to support an `__enter__` method without - implementing support for an `__exit__` method, enabling idioms like this: - - ```mojo - # This context manager consumes itself and returns it as the value. - fn __enter__(owned self) -> Self: - return self^ - ``` - - Here Mojo _cannot_ invoke a noop `__exit__` method because the context - manager is consumed by the `__enter__` method. This can be used for types - (like file descriptors) that are traditionally used with `with` statements, - even though Mojo's guaranteed early destruction doesn't require that. - -- A very basic version of `pathlib` has been implemented in Mojo. The - module will be improved to achieve functional parity with Python in - the next few releases. - -- The `memory.unsafe` module now contains a `bitcast` function. This is a - low-level operation that enables bitcasting between pointers and scalars. - -- The input parameters of a parametric type can now be directly accessed as - attribute references on the type or variables of the type. For example: - - ```mojo - @value - struct Thing[param: Int]: - pass - - fn main(): - print(Thing[2].param) # prints '2' - let x = Thing[9]() - print(x.param) # prints '9' - ``` - - Input parameters on values can even be accessed in parameter contexts. For - example: - - ```mojo - fn foo[value: Int](): - print(value) - - let y = Thing[12]() - alias constant = y.param + 4 - foo[constant]() # prints '16' - ``` - -- The Mojo REPL now supports code completion. Press Tab while typing - to query potential completion results. - -- Error messages from Python are now exposed in Mojo. For example the following - should print `No module named 'my_uninstalled_module'`: - - ```mojo - fn main(): - try: - let my_module = Python.import_module("my_uninstalled_module") - except e: - print(e) - ``` - -- Error messages can now store dynamic messages. For example, the following - should print "Failed on: Hello" - - ```mojo - fn foo(x: String) raises: - raise Error("Failed on: " + x) - - fn main(): - try: - foo("Hello") - except e: - print(e) - ``` - -### 🦋 Changed - -- We have improved and simplified the `parallelize` function. The function - now elides some overhead by caching the Mojo parallel runtime. - -- The Mojo REPL and Jupyter environments no longer implicitly expose `Python`, - `PythonObject`, or `Pointer`. These symbols must now be imported explicitly, - for example: - - ```mojo - from python import Python - from python.object import PythonObject - from memory.unsafe import Pointer - ``` - -- The syntax for specifying attributes with the `__mlir_op` prefix have changed - to mimic Python's keyword argument passing syntax. That is, `=` should be used - instead of `:`, e.g.: - - ```mojo - # Old syntax, now fails. - __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() - # New syntax. - __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() - ``` - -- You can now print the `Error` object directly. The `message()` method - has been removed. - -### 🛠️ Fixed - -- [#794](https://github.com/modularml/mojo/issues/794) - Parser crash when - using the `in` operator. -- [#936](https://github.com/modularml/mojo/issues/936) - The `Int` constructor - now accepts other `Int` instances. -- [#921](https://github.com/modularml/mojo/issues/921) - Better error message - when running `mojo` on a module with no `main` function. -- [#556](https://github.com/modularml/mojo/issues/556) - UInt64s are now - printed correctly. -- [#804](https://github.com/modularml/mojo/issues/804) - Emit error instead of - crashing when passing variadic arguments of unsupported types. -- [#833](https://github.com/modularml/mojo/issues/833) - Parser crash when - assigning module value. -- [#752](https://github.com/modularml/mojo/issues/752) - Parser crash when - calling async def. -- [#711](https://github.com/modularml/mojo/issues/711) - The overload resolution - logic now correctly prioritizes instance methods over static methods (if - candidates are an equally good match otherwise), and no longer crashed if a - static method has a `Self` type as its first argument. -- [#859](https://github.com/modularml/mojo/issues/859) - Fix confusing error and - documentation of the `rebind` builtin. -- [#753](https://github.com/modularml/mojo/issues/753) - Direct use of LLVM - dialect produces strange errors in the compiler. -- [#926](https://github.com/modularml/mojo/issues/926) - Fixes an issue that - occurred when a function with a return type of `StringRef` raised an error. - When the function raised an error, it incorrectly returned the string value of - that error. -- [#536](https://github.com/modularml/mojo/issues/536) - Report More information - on python exception. - -## v0.3.1 (2023-09-28) - -Our first-ever patch release of the Mojo SDK is here! Release v0.3.1 -includes primarily installation-related fixes. If you’ve had trouble -installing the previous versions of the SDK, this release may be for you. - -### 🛠️ Fixed - -- [#538](https://github.com/modularml/mojo/issues/538) - Installation hangs - during the testing phase. This issue occurs on machines with a low number - of CPU cores, such as free AWS EC2 instances and GitHub Codespaces. -- [#590](https://github.com/modularml/mojo/issues/590) - Installation fails - with a “failed to run python” message. -- [#672](https://github.com/modularml/mojo/issues/672) - Language server hangs - on code completion. Related to #538, this occurs on machines with a low - number of CPU cores. -- [#913](https://github.com/modularml/mojo/issues/913) - In the REPL and Jupyter - notebooks, inline comments were being parsed incorrectly. - -## v0.3.0 (2023-09-21) - -There's more Mojo to love in this, the second release of the Mojo SDK! This -release includes new features, an API change, and bug fixes. - -There's also an updated version of the [Mojo extension for VS -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - -### ⭐️ New - -- Mojo now has partial support for passing keyword arguments to functions and - methods. For example the following should work: - - ```mojo - fn foo(a: Int, b: Int = 3) -> Int: - return a * b - - fn main(): - print(foo(6, b=7)) # prints '42' - print(foo(a=6, b=7)) # prints '42' - print(foo(b=7, a=6)) # prints '42' - ``` - - Parameters can also be inferred from keyword arguments, for example: - - ```mojo - fn bar[A: AnyType, B: AnyType](a: A, b: B): - print("Hello 🔥") - - fn bar[B: AnyType](a: StringLiteral, b: B): - print(a) - - fn main(): - bar(1, 2) # prints `Hello 🔥` - bar(b=2, a="Yay!") # prints `Yay!` - ``` - - For the time being, the following notable limitations apply: - - - Keyword-only arguments are not supported: - - ```mojo - fn baz(*args: Int, b: Int): pass # fails - fn baz(a: Int, *, b: Int): pass # fails - ``` - - (Keyword-only arguments are described in - [PEP 3102](https://peps.python.org/pep-3102/).) - - - Variadic keyword arguments are not supported: - - ```mojo - fn baz(a: Int, **kwargs: Int): pass # fails - ``` - -- Mojo now supports the `@nonmaterializable` decorator. The purpose is to mark - data types that should only exist in the parameter domain. To use it, a - struct is decorated with `@nonmaterializable(TargetType)`. Any time the - nonmaterializable type is converted from the parameter domain, it is - automatically converted to `TargetType`. A nonmaterializable struct should - have all of its methods annotated as `@always_inline`, and must be computable - in the parameter domain. In the following example, the `NmStruct` type can - be added in the parameter domain, but are converted to `HasBool` when - materialized. - - ```mojo - @value - @register_passable("trivial") - struct HasBool: - var x: Bool - fn __init__(x: Bool) -> Self: - return Self {x: x} - @always_inline("nodebug") - fn __init__(nms: NmStruct) -> Self: - return Self {x: True if (nms.x == 77) else False} - - @value - @nonmaterializable(HasBool) - @register_passable("trivial") - struct NmStruct: - var x: Int - @always_inline("nodebug") - fn __add__(self: Self, rhs: Self) -> Self: - return NmStruct(self.x + rhs.x) - - alias stillNmStruct = NmStruct(1) + NmStruct(2) - # When materializing to a run-time variable, it is automatically converted, - # even without a type annotation. - let convertedToHasBool = stillNmStruct - ``` - -- Mojo integer literals now produce the `IntLiteral` infinite precision integer - type when used in the parameter domain. `IntLiteral` is materialized to the - `Int` type for runtime computation, but intermediate computations at compile - time, using supported operators, can now exceed the bit width of the `Int` - type. - -- The Mojo Language Server now supports top-level code completions, enabling - completion when typing a reference to a variable, type, etc. This resolves - [#679](https://github.com/modularml/mojo/issues/679). - -- The Mojo REPL now colorizes the resultant variables to help distinguish input - expressions from the output variables. - -### 🦋 Changed - -- Mojo allows types to implement two forms of move constructors, one that is - invoked when the lifetime of one value ends, and one that is invoked if the - compiler cannot prove that. These were previously both named `__moveinit__`, - with the following two signatures: - - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __moveinit__(inout self, inout existing: Self): ... - ``` - - We've changed the second form to get its own name to make it more clear that - these are two separate operations: the second has been renamed to - `__takeinit__`: - - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __takeinit__(inout self, inout existing: Self): ... - ``` - - The name is intended to connote that the operation takes the conceptual value - from the source (without destroying it) unlike the first one which "moves" a - value from one location to another. - - For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). - -- The Error type in Mojo has changed. Instead of extracting the error message - using `error.value` you will now extract the error message using - `error.message()`. - -### 🛠️ Fixed - -- [#503](https://github.com/modularml/mojo/issues/503) - Improve error message - for failure lowering `kgen.param.constant`. -- [#554](https://github.com/modularml/mojo/issues/554) - Alias of static tuple - fails to expand. -- [#500](https://github.com/modularml/mojo/issues/500) - Call expansion failed - due to verifier error. -- [#422](https://github.com/modularml/mojo/issues/422) - Incorrect comment - detection in multiline strings. -- [#729](https://github.com/modularml/mojo/issues/740) - Improve messaging on - how to exit the REPL. -- [#756](https://github.com/modularml/mojo/issues/756) - Fix initialization - errors of the VS Code extension. -- [#575](https://github.com/modularml/mojo/issues/575) - Build LLDB/REPL with - libedit for a nicer editing experience in the terminal. - -## v0.2.1 (2023-09-07) - -The first versioned release of Mojo! 🔥 - -All earlier releases were considered version 0.1. - -### 🔥 Legendary - -- First release of the Mojo SDK! - - You can now develop with Mojo locally. The Mojo SDK is currently available - for Ubuntu Linux systems, and support for Windows and macOS is coming soon. - You can still develop from a Windows or Mac computer using a container or - remote Linux system. - - The Mojo SDK includes the Mojo standard library and the [Mojo command-line - interface](/mojo/cli/) (CLI), which allows you to run, compile, and package - Mojo code. It also provides a REPL programming environment. - - [Get the Mojo SDK!](https://developer.modular.com/download) - -- First release of the [Mojo extension for VS - Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - - This provides essential Mojo language features in Visual Studio Code, such as - code completion, code quick fixes, docs tooltips, and more. Even when - developing on a remote system, using VS Code with this extension provides - a native-like IDE experience. - -### ⭐️ New - -- A new `clobber_memory` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. - The clobber memory function tells the system to flush all memory operations - at the specified program point. This allows you to benchmark operations - without the compiler reordering memory operations. - -- A new `keep` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` - function tries to tell the compiler not to optimize the variable away - if not used. This allows you to avoid compiler's dead code elimination - mechanism, with a low footprint side effect. - -- New `shift_right` and `shift_left` functions have been added to the - [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in - a SIMD vector right/left, filling elements with zeros as needed. - -- A new `cumsum` function has been added to the - [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes - the cumulative sum (also known as scan) of input elements. - -- Mojo Jupyter kernel now supports code completion. - -### 🦋 Changed - -- Extends `rotate_bits_left`, `rotate_left`, `rotate_bits_right`, and - `rotate_right` to operate on Int values. The ordering of parameters has also - been changed to enable type inference. Now it's possible to write - `rotate_right[shift_val](simd_val)` and have the `dtype` and `simd_width` - inferred from the argument. This addresses - [Issue #528](https://github.com/modularml/mojo/issues/528). - -### 🛠️ Fixed - -- Fixed a bug causing the parser to crash when the `with` statement was written - without a colon. - This addresses [Issue #529](https://github.com/modularml/mojo/issues/529). - -- Incorrect imports no longer crash when there are other errors at the top - level of a module. This fixes [Issue - #531](https://github.com/modularml/mojo/issues/531). - -## August 2023 - -### 2023-08-24 - -- Fixed issue where the `with expr as x` statement within `fn` behaved - as if it were in a `def`, binding `x` with function scope instead of using - lexical scope. - -#### ⭐️ New - -- Major refactoring of the standard library to enable packaging and better - import ergonomics: - - The packages are built as binaries to improve startup speed. - - Package and module names are now lowercase to align with the Python style. - - Modules have been moved to better reflect the purpose of the underlying - functions (e.g. `Pointer` is now within the `unsafe` module in the `memory` - package). - - The following modules are now included as built-ins: - `SIMD`, `DType`, `IO`, `Object`, and `String`. - This means it's no longer necessary to explicitly import these modules. - Instead, these modules will be implicitly imported for the user. Private - methods within the module are still accessible using the - `builtin.module_name._private_method` import syntax. - - New `math` package has been added to contain the `bit`, `math`, `numerics`, - and `polynomial` modules. The contents of the `math.math` module are - re-exported into the `math` package. - -- Mojo now supports using memory-only types in parameter expressions and as - function or type parameters: - - ```mojo - @value - struct IntPair: - var first: Int - var second: Int - - fn add_them[value: IntPair]() -> Int: - return value.first + value.second - - fn main(): - print(add_them[IntPair(1, 2)]()) # prints '3' - ``` - -- In addition, Mojo supports evaluating code that uses heap-allocated memory - at compile-time and materializing compile-time values with heap-allocated - memory into dynamic values: - - ```mojo - fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: - var result = DynamicVector[Int]() - for i in range(lowerBound, upperBound, step): - result.push_back(i) - return result - - fn main(): - alias values = fillVector(5, 23, 7) - for i in range(0, values.__len__()): - print(values[i]) # prints '5', '12', and then '19' - ``` - -#### 🦋 Changed - -- `def main():`, without the explicit `None` type, can now be used to define - the entry point to a Mojo program. - -- The `assert_param` function has been renamed to `constrained` and is now - a built-in function. - -- The `print` function now works on `Complex` values. - -#### 🛠️ Fixed - -- Fixed issues with print formatting for `DType.uint16` and `DType.int16`. -- [Issue #499](https://github.com/modularml/mojo/issues/499) - Two new - `rotate_right` and `rotate_left` functions have been added to the SIMD module. -- [Issue #429](https://github.com/modularml/mojo/issues/429) - You can now - construct a `Bool` from a `SIMD` type whose element-type is `DType.bool`. -- [Issue #350](https://github.com/modularml/mojo/issues/350) - Confusing Matrix - implementation -- [Issue #349](https://github.com/modularml/mojo/issues/349) - Missing load_tr - in struct Matrix -- [Issue #501](https://github.com/modularml/mojo/issues/501) - Missing syntax - error messages in Python expressions. - -### 2023-08-09 - -#### 🦋 Changed - -- The `ref` and `mutref` identifiers are now treated as keywords, which means - they cannot be used as variable, attribute, or function names. These keywords - are used by the "lifetimes" features, which is still in development. We can - consider renaming these (as well as other related keywords) when the - development work gels, support is enabled in public Mojo builds, and when we - have experience using them. - -- The argument handling in `def` functions has changed: previously, they had - special behavior that involved mutable copies in the callee. Now, we have a - simple rule, which is that `def` argument default to the `owned` convention - (`fn` arguments still default to the `borrowed` convention). - - This change is mostly an internal cleanup and simplification of the compiler - and argument model, but does enable one niche use-case: you can now pass - non-copyable types to `def` arguments by transferring ownership of a value - into the `def` call. Before, that would not be possible because the copy was - made on the callee side, not the caller's side. This also allows the explicit - use of the `borrowed` keyword with a `def` that wants to opt-in to that - behavior. - -### 2023-08-03 - -#### ⭐️ New - -- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. - This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which - are just views). Therefore, the tensor type performs its own allocation and - free. Here is a simple example of using the tensor type to represent an RGB - image and convert it to grayscale: - - ```mojo - from tensor import Tensor, TensorShape - from utils.index import Index - from random import rand - - let height = 256 - let width = 256 - let channels = 3 - - # Create the tensor of dimensions height, width, channels and fill with - # random value. - let image = rand[DType.float32](height, width, channels) - - # Declare the grayscale image. - var gray_scale_image = Tensor[DType.float32](height, width) - - # Perform the RGB to grayscale transform. - for y in range(height): - for x in range(width): - let r = image[y, x, 0] - let g = image[y, x, 1] - let b = image[y, x, 2] - gray_scale_image[Index(y, x)] = 0.299 * r + 0.587 * g + 0.114 * b - ``` - -#### 🛠️ Fixed - -- [Issue #53](https://github.com/modularml/mojo/issues/53) - `Int` now - implements true division with the `/` operator. Similar to Python, this - returns a 64-bit floating point number. The corresponding in-place operator, - `/=`, has the same semantics as `//=`. - -## July 2023 - -### 2023-07-26 - -#### ⭐️ New - -- Types that define both `__getitem__` and `__setitem__` (i.e. where - sub-scripting instances creates computed LValues) can now be indexed - in parameter expressions. - -- Unroll decorator for loops with constant bounds and steps: - - `@unroll`: Fully unroll a loop. - - `@unroll(n)`: Unroll a loop by factor of n, where `n` is a positive integer. - - Unroll decorator requires loop bounds and iteration step to be - compiler time constant value, otherwise unrolling will fail with - compilation error. This also doesn't make loop induction variable a parameter. - - ```mojo - # Fully unroll the loop. - @unroll - for i in range(5): - print(i) - - # Unroll the loop by a factor of 4 (with remainder iterations of 2). - @unroll(4) - for i in range(10): - print(i) - ``` - -- The Mojo REPL now prints the values of variables defined in the REPL. There is - full support for scalars and structs. Non-scalar SIMD vectors are not - supported at this time. - -#### 🛠️ Fixed - -- [Issue #437](https://github.com/modularml/mojo/issues/437) - Range can now - be instantiated with a PythonObject. - -- [Issue #288](https://github.com/modularml/mojo/issues/288) - Python strings - can now be safely copied. - -### 2023-07-20 - -#### ⭐️ New - -- Mojo now includes a `Limits` module, which contains functions to get the max - and min values representable by a type, as requested in [Issue - #51](https://github.com/modularml/mojo/issues/51). The following functions - moved from `Math` to `Limits`: `inf()`, `neginf()`, `isinf()`, `isfinite()`. - -- Mojo decorators are now distinguished between "signature" and "body" - decorators and are ordered. Signature decorators, like `@register_passable` - and `@parameter`, modify the type of declaration before the body is parsed. - Body decorators, like `@value`, modify the body of declaration after it is - fully parsed. Due to ordering, a signature decorator cannot be applied after - a body decorator. That means the following is now invalid: - - ```mojo - @register_passable # error: cannot apply signature decorator after a body one! - @value - struct Foo: - pass - ``` - -- Global variables can now be exported in Mojo compiled archives, using the - `@export` decorator. Exported global variables are public symbols in compiled - archives and use the variable name as its linkage name, by default. A custom - linkage name can be specified with `@export("new_name")`. This does not affect - variable names in Mojo code. - -- Mojo now supports packages! A Mojo package is defined by placing an - `__init__.mojo` or `__init__.🔥` within a directory. Other files in the same - directory form modules within the package (this works exactly like it - does [in Python](https://docs.python.org/3/tutorial/modules.html#packages)). - Example: - - ```bash - main.🔥 - my_package/ - __init__.🔥 - module.🔥 - my_other_package/ - __init__.🔥 - stuff.🔥 - ``` - - ```mojo - # main.🔥 - from my_package.module import some_function - from my_package.my_other_package.stuff import SomeType - - fn main(): - var x: SomeType = some_function() - ``` - -- Mojo now supports direct module and package imports! Modules and packages can - be imported and bound to names. Module and package elements, like functions, - types, global variables, and other modules, can be accessed using attribute - references, like `my_module.foo`. Note that modules lack runtime - representations, meaning module references cannot be instantiated. - - ```mojo - import builtin.io as io - import SIMD - - io.print("hello world") - var x: SIMD.Float32 = 1.2 - ``` - -#### 🦋 Changed - -- Reverted the feature from 2023-02-13 that allowed unqualified struct members. - Use the `Self` keyword to conveniently access struct members with bound - parameters instead. This was required to fix - [Issue #260](https://github.com/modularml/mojo/issues/260). - -- Updated the RayTracing notebook: added step 5 to create specular lighting for - more realistic images and step 6 to add a background image. - -#### 🛠️ Fixed - -- [Issue #260](https://github.com/modularml/mojo/issues/260) - Definitions - inside structs no longer shadow definitions outside of struct definitions. - -### 2023-07-12 - -#### ⭐️ New - -- Mojo now has support for global variables! This enables `var` and `let` - declaration at the top-level scope in Mojo files. Global variable initializers - are run when code modules are loaded by the platform according to the order of - dependencies between global variables, and their destructors are called in the - reverse order. - -- The [Mojo programming manual](/mojo/programming-manual.html) is now written - as a Jupyter notebook, and available in its entirety in the Mojo Playground - (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of - the same material, but it was not up-to-date.) - -- As a result, we've also re-written `HelloMojo.ipynb` to be much shorter and - provide a more gentle first-user experience. - -- [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now - available. Coroutines form the basis of Mojo's support for asynchronous - execution. Calls to `async fn`s can be stored into a `Coroutine`, from which - they can be resumed, awaited upon, and have their results retrieved upon - completion. - -#### 🦋 Changed - -- `simd_bit_width` in the `TargetInfo` module has been renamed to `simdbitwidth` - to better align with `simdwidthof`, `bitwidthof`, etc. - -#### 🛠️ Fixed - -- The walrus operator now works in if/while statements without parentheses, - e.g. `if x := function():`. - -- [Issue #428](https://github.com/modularml/mojo/issues/428) - The - `FloatLiteral` and `SIMD` types now support conversion to `Int` via the - `to_int` or `__int__` method calls. The behavior matches that of Python, which - rounds towards zero. - -### 2023-07-05 - -#### ⭐️ New - -- Tuple expressions now work without parentheses. For example, `a, b = b, a` - works as you'd expect in Python. -- Chained assignments (e.g. `a = b = 42`) and the walrus operator (e.g. - `some_function(b := 17)`) are now supported. - -#### 🦋 Changed - -- The `simd_width` and `dtype_simd_width` functions in the - [`TargetInfo`](/mojo/stdlib/sys/info) module - have been renamed to `simdwidthof`. - -- The `dtype_` prefix has been dropped from `alignof`, `sizeof`, and - `bitwidthof`. You can now use these functions (e.g. `alignof`) with any - argument type, including `DType`. - -- The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were - moved from the `Numerics` module to the [`Math`](/mojo/MojoStdlib/Math.html) - module, to better align with Python's library structure. - -#### 🛠️ Fixed - -- [Issue #253](https://github.com/modularml/mojo/issues/253) - Issue - when accessing a struct member alias without providing parameters. - -- [Issue #404](https://github.com/modularml/mojo/issues/404) - The docs now use - `snake_case` for variable names, which more closely conforms to Python's - style. - -- [Issue #379](https://github.com/modularml/mojo/issues/379) - Tuple - limitations have been addressed and multiple return values are now supported, - even without parentheses. - -- [Issue #347](https://github.com/modularml/mojo/issues/347) - Tuples no longer - require parentheses. - -- [Issue #320](https://github.com/modularml/mojo/issues/320) - Python objects - are now traversable via `for` loops. - -## June 2023 - -### 2023-06-29 - -#### ⭐️ New - -- You can now share `.ipynb` notebook files in Mojo Playground. Just save a - file in the `shared` directory, and then right-click the file and select - **Copy Sharable link**. To open a shared notebook, you must already have - [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); - when you open a shared notebook, click **Import** at the top of the notebook - to save your own copy. For more details about this feature, see the - instructions inside the `help` directory, in the Mojo Playground file browser. - -#### 🦋 Changed - -- The `unroll2()` and `unroll3()` functions in the - [`Functional`](/mojo/stdlib/algorithm/functional) module have been renamed to - overload the `unroll()` function. These functions unroll 2D and 3D loops and - `unroll()` can determine the intent based on the number of input parameters. - -#### 🛠️ Fixed - -- [Issue #229](https://github.com/modularml/mojo/issues/229) - Issue when - throwing an exception from `__init__` before all fields are initialized. - -- [Issue #74](https://github.com/modularml/mojo/issues/74) - Struct - definition with recursive reference crashes. - -- [Issue #285](https://github.com/modularml/mojo/issues/285) - The - [`TargetInfo`](/mojo/stdlib/sys/info) module now includes - `is_little_endian()` and `is_big_endian()` to check if the target host uses - either little or big endian. - -- [Issue #254](https://github.com/modularml/mojo/issues/254) - Parameter name - shadowing in nested scopes is now handled correctly. - -### 2023-06-21 - -#### ⭐️ New - -- Added support for overloading on parameter signature. For example, it is now -possible to write the following: - - ```mojo - fn foo[a: Int](x: Int): - pass - - fn foo[a: Int, b: Int](x: Int): - pass - ``` - - For details on the overload resolution logic, see the Mojo Manual section on - [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). - -- A new `cost_of()` function has been added to `Autotune`. This meta-function - must be invoked at compile time, and it returns the number of MLIR operations - in a function (at a certain stage in compilation), which can be used to - build basic heuristics in higher-order generators. - - ```mojo - from autotune import cost_of - - fn generator[f: fn(Int) -> Int]() -> Int: - @parameter - if cost_of[fn(Int) -> Int, f]() < 10: - return f() - else: - # Do something else for slower functions... - ``` - -- Added a new example notebook with a basic Ray Tracing algorithm. - -#### 🦋 Changed - -- The `constrained_msg()` in the `Assert` module has been renamed to - `constrained()`. - -#### 🛠️ Fixed - -- Overloads marked with `@adaptive` now correctly handle signatures that differ -only in declared parameter names, e.g. the following now works correctly: - - ```mojo - @adaptive - fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... - - @adaptive - fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ... - ``` - -- [Issue #219](https://github.com/modularml/mojo/issues/219) - Issue when - redefining a function and a struct defined in the same cell. - -- [Issue #355](https://github.com/modularml/mojo/issues/355) - The loop order - in the Matmul notebook for Python and naive mojo have been reordered for - consistency. The loop order now follows (M, K, N) ordering. - -- [Issue #309](https://github.com/modularml/mojo/issues/309) - Use snake case - naming within the testing package and move the asserts out of the TestSuite - struct. - -### 2023-06-14 - -#### ⭐️ New - -- Tuple type syntax is now supported, e.g. the following works: - - ```mojo - fn return_tuple() -> (Int, Int): - return (1, 2) - ``` - -#### 🦋 Changed - -- The `TupleLiteral` type was renamed to just `Tuple`, e.g. - `Tuple[Int, Float]`. - -#### 🛠️ Fixed - -- [Issue #354](https://github.com/modularml/mojo/issues/354) - Returning a tuple - doesn't work even with parens. -- [Issue #365](https://github.com/modularml/mojo/issues/365) - Copy-paste error - in `FloatLiteral` docs. -- [Issue #357](https://github.com/modularml/mojo/issues/357) - Crash when - missing input parameter to variadic parameter struct member function. - -### 2023-06-07 - -#### ⭐️ New - -- Tuple syntax now works on the left-hand side of assignments (in "lvalue" - positions), enabling things like `(a, b) = (b, a)`. There are several - caveats: the element types must exactly match (no implicit conversions), - this only works with values of `TupleLiteral` type (notably, it will not work - with `PythonObject` yet) and parentheses are required for tuple syntax. - -#### ❌ Removed - -- Mojo Playground no longer includes the following Python packages (due to size, - compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): - `torch`, `tensorflow`, `keras`, `transformers`. - -#### 🦋 Changed - -- The data types and scalar names now conform to the naming convention used - by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` - instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). - -#### 🛠️ Fixed - -- [Issue #287](https://github.com/modularml/mojo/issues/287) - computed - lvalues don't handle raising functions correctly -- [Issue #318](https://github.com/modularml/mojo/issues/318) - Large integers - are not being printed correctly -- [Issue #326](https://github.com/modularml/mojo/issues/326) - Float modulo - operator is not working as expected -- [Issue #282](https://github.com/modularml/mojo/issues/282) - Default arguments - are not working as expected -- [Issue #271](https://github.com/modularml/mojo/issues/271) - Confusing error - message when converting between function types with different result semantics - -## May 2023 - -### 2023-05-31 - -#### ⭐️ New - -- Mojo Playground now includes the following Python packages (in response to - [popular demand](https://github.com/modularml/mojo/discussions/173)): - `torch`, `tensorflow`, `polars`, `opencv-python`, `keras`, `Pillow`, `plotly`, - `seaborn`, `sympy`, `transformers`. - -- A new optimization is applied to non-trivial copyable values that are passed - as an owned value without using the transfer (`^`) operator. Consider code - like this: - - ```mojo - var someValue: T = ... - ... - takeValueAsOwned(someValue) - ... - ``` - - When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) - value (this is - common in initializers for example), it is allowed to do whatever it wants - with the value and destroy it when it is finished. In order to support this, - the Mojo compiler is forced to make a temporary copy of the `someValue` - value, and pass that value instead of `someValue`, because there may be other - uses of `someValue` after the call. - - The Mojo compiler is now smart enough to detect when there are no uses of - `someValue` later, and it will elide the copy just as if you had manually - specified the transfer operator like `takeValueAsOwned(someValue^)`. This - provides a nice "it just works" behavior for non-trivial types without - requiring manual management of transfers. - - If you'd like to take full control and expose full ownership for your type, - just don't make it copyable. Move-only types require the explicit transfer - operator so you can see in your code where all ownership transfer happen. - -- Similarly, the Mojo compiler now transforms calls to `__copyinit__` methods - into calls to `__moveinit__` when that is the last use of the source value - along a control flow path. This allows types which are both copyable and - movable to get transparent move optimization. For example, the following code - is compiled into moves instead of copies even without the use of the transfer - operator: - - ```mojo - var someValue = somethingCopyableAndMovable() - use(someValue) - ... - let otherValue = someValue # Last use of someValue - use(otherValue) - ... - var yetAnother = otherValue # Last use of otherValue - mutate(yetAnother) - ``` - - This is a significant performance optimization for things like `PythonObject` - (and more complex value semantic types) that are commonly used in a fluid - programming style. These don't want extraneous reference counting operations - performed by its copy constructor. - - If you want explicit control over copying, it is recommended to use a - non-dunder `.copy()` method instead of `__copyinit__`, and recall that - non-copyable types must always use of the transfer operator for those that - want fully explicit behavior. - -#### 🛠️ Fixed - -- [Issue #231](https://github.com/modularml/mojo/issues/231) - Unexpected error - when a Python expression raises an exception -- [Issue #119](https://github.com/modularml/mojo/issues/119) - The REPL fails - when a python variable is redefined - -### 2023-05-24 - -#### ⭐️ New - -- `finally` clauses are now supported on `try` statements. In addition, `try` - statements no longer require `except` clauses, allowing `try-finally` blocks. - `finally` clauses contain code that is always executed from control-flow - leaves any of the other clauses of a `try` statement by any means. - -#### 🦋 Changed - -- `with` statement emission changed to use the new `finally` logic so that - - ```mojo - with ContextMgr(): - return - ``` - - Will correctly execute `ContextMgr.__exit__` before returning. - -#### 🛠️ Fixed - -- [Issue #204](https://github.com/modularml/mojo/issues/204) - Mojo REPL - crash when returning a String at compile-time -- [Issue #143](https://github.com/modularml/mojo/issues/143) - synthesized - init in `@register_passable` type doesn't get correct convention. -- [Issue #201](https://github.com/modularml/mojo/issues/201) - String literal - concatenation is too eager. -- [Issue #209](https://github.com/modularml/mojo/issues/209) - [QoI] Terrible - error message trying to convert a type to itself. -- [Issue #32](https://github.com/modularml/mojo/issues/32) - Include struct - fields in docgen -- [Issue #50](https://github.com/modularml/mojo/issues/50) - Int to string - conversion crashes due to buffer overflow -- [Issue #132](https://github.com/modularml/mojo/issues/132) - PythonObject - `to_int` method has a misleading name -- [Issue #189](https://github.com/modularml/mojo/issues/189) - PythonObject bool - conversion is incorrect -- [Issue #65](https://github.com/modularml/mojo/issues/65) - Add SIMD - constructor from Bool -- [Issue #153](https://github.com/modularml/mojo/issues/153) - Meaning of - `Time.now` function result is unclear -- [Issue #165](https://github.com/modularml/mojo/issues/165) - Type in - `Pointer.free` documentation -- [Issue #210](https://github.com/modularml/mojo/issues/210) - Parameter results - cannot be declared outside top-level in function -- [Issue #214](https://github.com/modularml/mojo/issues/214) - Pointer offset - calculations at compile-time are incorrect -- [Issue #115](https://github.com/modularml/mojo/issues/115) - Float printing - does not include the right number of digits -- [Issue #202](https://github.com/modularml/mojo/issues/202) - - `kgen.unreachable` inside nested functions is illegal -- [Issue #235](https://github.com/modularml/mojo/issues/235) - Crash when - register passable struct field is not register passable -- [Issue #237](https://github.com/modularml/mojo/issues/237) - Parameter - closure sharp edges are not documented - -### 2023-05-16 - -#### ⭐️ New - -- Added missing dunder methods to `PythonObject`, enabling the use of common - arithmetic and logical operators on imported Python values. - -- `PythonObject` is now printable from Mojo, instead of requiring you to import - Python's print function. - -#### 🛠️ Fixed - -- [Issue #98](https://github.com/modularml/mojo/issues/98): - Incorrect error with lifetime tracking in loop. - -- [Issue #49](https://github.com/modularml/mojo/issues/49): Type inference - issue (?) in 'ternary assignment' operation (FloatLiteral vs. 'SIMD[f32, 1]'). - -- [Issue #48](https://github.com/modularml/mojo/issues/48): - and/or don't work with memory-only types. - -- [Issue #11](https://github.com/modularml/mojo/issues/11): `setitem` Support - for `PythonObject`. - -### 2023-05-11 - -#### ⭐️ New - -- `NDBuffer` and `Buffer` are now constructable via `Pointer` and - `DTypePointer`. - -- `String` now supports indexing with either integers or slices. - -- Added factorial function to the `Math` module. - -#### 🦋 Changed - -- The "byref" syntax with the `&` sigil has changed to use an `inout` - keyword to be more similar to the `borrowed` and `owned` syntax in arguments. - Please see [Issue #7](https://github.com/modularml/mojo/issues/7) for more - information. - -- Optimized the Matrix multiplication implementation in the notebook. - Initially we were optimizing for expandability rather than performance. We - have found a way to get the best of both worlds and now the performance of the - optimized Matmul implementation is 3x faster. - -- Renamed the [`^` postfix -operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) -from "consume" to "transfer." - -#### 🛠️ Fixed - -- Fixed missing overloads for `Testing.assertEqual` so that they work on -`Integer` and `String` values. - -- [Issue #6](https://github.com/modularml/mojo/issues/6): -Playground stops evaluating cells when a simple generic is defined. - -- [Issue #18](https://github.com/modularml/mojo/issues/18): -Memory leak in Python interoperability was removed. - -### 2023-05-02 - -#### 📢 Released - -- Mojo publicly launched! This was epic, with lots of great coverage online -including a [wonderful post by Jeremy -Howard](https://www.fast.ai/posts/2023-05-03-mojo-launch.html). The team is -busy this week. - -#### ⭐️ New - -- Added a Base64 encoding function to perform base64 encoding on strings. - -#### 🦋 Changed - -- Decreased memory usage of serialization of integers to strings. - -- Speedup the sort function. - -#### 🛠️ Fixed - -- Fixed time unit in the `sleep` function. - -## April 2023 - -### Week of 2023-04-24 - -- 📢 The default behavior of nested functions has been changed. Mojo nested - functions that capture are by default are non-parametric, runtime closures, - meaning that: - - ```mojo - def foo(x): - # This: - def bar(y): return x * y - # Is the same as: - let bar = lambda y: x * y - ``` - - These closures cannot have input or result parameters, because they are always - materialized as runtime values. Values captured in the closure (`x` in the - above example), are captured by copy: values with copy constructors cannot be - copied and captures are immutable in the closure. - - Nested functions that don't capture anything are by default "parametric" - closures: they can have parameters and they can be used as parameter values. - To restore the previous behavior for capturing closures, "parametric, - capture-by-unsafe-reference closures", tag the nested function with the - `@parameter` decorator. - -- 📢 Mojo now has full support for "runtime" closures: nested functions that - capture state materialized as runtime values. This includes taking the address - of functions, indirect calls, and passing closures around through function - arguments. Note that capture-by-reference is still unsafe! - - You can also take references to member functions with instances of that class - using `foo.member_function`, which creates a closure with `foo` bound to the - `self` argument. - -- 📢 Mojo now supports Python style `with` statements and context managers. - - These things are very helpful for implementing things like our - trace region support and things like Runtime support. - - A context manager in Mojo implements three methods: - - ```mojo - fn __enter__(self) -> T: - fn __exit__(self): - fn __exit__(self, err: Error) -> Bool: - ``` - - The first is invoked when the context is entered, and returns a - value that may optionally be bound to a target for use in the with - body. If the with block exits normally, the second method is - invoked to clean it up. If an error is raised, the third method - is invoked with the Error value. If that method returns true, the - error is considered handled, if it returns false, the error is - re-thrown so propagation continues out of the 'with' block. - -- 📢 Mojo functions now support variable scopes! Explicit `var` and `let` - declarations inside functions can shadow declarations from higher "scopes", - where a scope is defined as any new indentation block. In addition, the - `for` loop iteration variable is now scoped to the loop body, so it is - finally possible to write - - ```mojo - for i in range(1): pass - for i in range(2): pass - ``` - -- 📢 Mojo now supports an `@value` decorator on structs to reduce boilerplate - and encourage best practices in value semantics. The `@value` decorator looks - to see the struct has a memberwise initializer (which has arguments for each - field of the struct), a `__copyinit__` method, and a `__moveinit__` method, - and synthesizes the missing ones if possible. For example, if you write: - - ```mojo - @value - struct MyPet: - var name: String - var age: Int - ``` - - The `@value` decorator will synthesize the following members for you: - - ```mojo - fn __init__(inout self, owned name: String, age: Int): - self.name = name^ - self.age = age - fn __copyinit__(inout self, existing: Self): - self.name = existing.name - self.age = existing.age - fn __moveinit__(inout self, owned existing: Self): - self.name = existing.name^ - self.age = existing.age - ``` - - This decorator can greatly reduce the boilerplate needed to define common - aggregates, and gives you best practices in ownership management - automatically. The `@value` decorator can be used with types that need custom - copy constructors (your definition wins). We can explore having the decorator - take arguments to further customize its behavior in the future. - -- 📚 Memcpy and memcmp now consistently use count as the byte count. - -- 📚 Add a variadic sting join on strings. - -- 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all - elements in a SIMD vector. - -- 📚 Optimize the `pow` function if the exponent is integral. - -- 📚 Add a `len` function which dispatches to `__len__` across the different - structs that support it. - -### Week of 2023-04-17 - -- 📢 Error messages have been significantly improved, thanks to prettier - printing for Mojo types in diagnostics. - -- 📢 Variadic values can now be indexed directly without wrapping them in a - `VariadicList`! - -- 📢 `let` declarations in a function can now be lazily initialized, and `var` - declarations that are never mutated get a warning suggesting they be converted - to a `let` declaration. Lazy initialization allows more flexible patterns of - initialization than requiring the initializer be inline, e.g.: - - ```mojo - let x: Int - if cond: - x = foo() - else: - x = bar() - use(x) - ``` - -- 📢 Functions defined with `def` now return `object` by default, instead of - `None`. This means you can return values (convertible to `object`) inside - `def` functions without specifying a return type. - -- 📢 The `@raises` decorator has been removed. Raising `fn` should be declared - by specifying `raises` after the function argument list. The rationale is that - `raises` is part of the type system, instead of a function modifier. - -- 📢 The `BoolLiteral` type has been removed. Mojo now emits `True` and `False` - directly as `Bool`. - -- 📢 Syntax for function types has been added. You can now write function types - with `fn(Int) -> String` or `async def(&String, *Int) -> None`. No more - writing `!kgen.signature` types by hand! - -- 📢 Float literals are not emitted as `FloatLiteral` instead of an MLIR `f64` - type! - -- 📢 Automatic destructors are now supported by Mojo types, currently spelled - `fn __del___(owned self):` (the extra underscore will be dropped shortly). - These destructors work like Python object destructors and similar to C++ - destructors, with the major difference being that they run "as soon as - possible" after the last use of a value. This means they are not suitable - for use in C++-style RAII patterns (use the `with` statement for that, which - is currently unsupported). - - These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to _not_ capture values - correctly. Be very careful with interesting types in the vicinity of a - closure! - -- A new (extremely dangerous!) builtin function is available for low-level - ownership muckery. The `__get_address_as_owned_value(x)` builtin takes a - low-level address value (of `!kgen.pointer` type) and returns an `owned` value - for the memory that is pointed to. This value is assumed live at the - invocation of the builtin, but is "owned" so it needs to be consumed by the - caller, otherwise it will be automatically destroyed. This is an effective - way to do a "placement delete" on a pointer. - - ```mojo - # "Placement delete": destroy the initialized object begin pointed to. - _ = __get_address_as_owned_value(somePointer.value) - - # Result value can be consumed by anything that takes it as an 'owned' - # argument as well. - consume(__get_address_as_owned_value(somePointer.value)) - ``` - -- Another magic operator, named `__get_address_as_uninit_lvalue(x)` joins - the magic LValue operator family. This operator projects a pointer to - an LValue like `__get_address_as_lvalue(x)`. The difference is that - `__get_address_as_uninit_lvalue(x)` tells the compiler that the pointee is - uninitialized on entry and initialized on exit, which means that you can use - it as a "placement new" in C++ sense. `__get_address_as_lvalue(x)` tells the - compiler that the pointee is initialized already, so reassigning over it will - run the destructor. - - ```mojo - # "*Re*placement new": destroy the existing SomeHeavy value in the memory, - # then initialize a new value into the slot. - __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) - - # Ok to use an lvalue, convert to borrow etc. - use(__get_address_as_lvalue(somePointer.value)) - - # "Placement new": Initialize a new value into uninitialied memory. - __get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5) - - # Error, cannot read from uninitialized memory. - use(__get_address_as_uninit_lvalue(somePointer.value)) - ``` - - Note that `__get_address_as_lvalue` assumes that there is already a value at - the specified address, so the assignment above will run the `SomeHeavy` - destructor (if any) before reassigning over the value. - -- 📢 Implement full support for `__moveinit__` (aka move constructors) - - This implements the ability for memory-only types to define two different - types of move ctors if they'd like: - - 1. `fn __moveinit__(inout self, owned existing: Self)`: Traditional Rust - style moving constructors that shuffles data around while taking - ownership of the source binding. - 2. `fn __moveinit__(inout self, inout existing: Self):`: C++ style "stealing" - move constructors that can be used to take from an arbitrary LValue. - - This gives us great expressive capability (better than Rust/C++/Swift) - and composes naturally into our lifetime tracking and value - categorization system. - -- The `__call__` method of a callable type has been relaxed to take `self` by - borrow, allow non-copyable callees to be called. - -- Implicit conversions are now invoked in `raise` statements properly, allowing - converting strings to `Error` type. - -- Automatic destructors are turned on for `__del__` instead of `__del___`. - -- 📚 Add the builtin FloatLiteral type. - -- 📚 Add integral `floordiv` and `mod` for the SIMD type that handle negative - values. - -- 📚 Add an F64 to String converter. - -- 📚 Make the `print` function take variadic inputs. - -### Week of 2023-04-10 - -- 📢 Introduce consume operator `x^` - - This introduces the postfix consume operator, which produces an RValue given - a lifetime tracked object (and, someday, a movable LValue). - -- Mojo now automatically synthesizes empty destructor methods for certain types - when needed. - -- The `object` type has been built out into a fully-dynamic type, with dynamic - function dispatch, with full error handling support. - - ```mojo - def foo(a) -> object: - return (a + 3.45) < [1, 2, 3] # raises a TypeError - ``` - -- 📢 The `@always_inline` decorator is no longer required for passing capturing - closures as parameters, for both the functions themselves as functions with - capturing closures in their parameters. These functions are still inlined but - it is an implementation detail of capturing parameter closures. Mojo now - distinguishes between capturing and non-capturing closures. Nested functions - are capturing by default and can be made non-capturing with the - `@noncapturing` decorator. A top-level function can be passed as a capturing - closure by marking it with the `@closure` decorator. - -- 📢 Support for list literals has been added. List literals `[1, 2, 3]` - generate a variadic heterogeneous list type. - -- Variadics have been extended to work with memory-primary types. - -- Slice syntax is now fully-supported with a new builtin `slice` object, added - to the compiler builtins. Slice indexing with `a[1:2:3]` now emits calls to - `__setitem__` and `__getitem__` with a slice object. - -- Call syntax has been wired up to `__call__`. You can now `f()` on custom - types! - -- Closures are now explicitly typed as capturing or non-capturing. If a - function intends to accept a capturing closure, it must specify the - `capturing` function effect. - -- 📚 Add a `Tile2D` function to enable generic `2D` tiling optimizations. - -- 📚 Add the `slice` struct to enable getting/setting spans of elements via - `getitem`/`setitem`. - -- 📚 Add syntax sugar to autotuning for both specifying the autotuned values, - searching, and declaring the evaluation function. - -### Week of 2023-04-03 - -- The `AnyType` and `NoneType` aliases were added and auto-imported in all - files. - -- 📢 The Mojo VS Code extension has been improved with docstring validation. It - will now warn when a function's docstring has a wrong argument name, for - example. - -- 📢 A new built-in literal type `TupleLiteral` was added in `_CompilerBuiltin`. - It represents literal tuple values such as `(1, 2.0)` or `()`. - -- 📢 The `Int` type has been moved to a new `Builtin` module and is - auto-imported in all code. The type of integer literals has been changed from - the MLIR `index` type to the `Int` type. - -- Mojo now has a powerful flow-sensitive uninitialized variable checker. This - means that you need to initialize values before using them, even if you - overwrite all subcomponents. This enables the compiler to reason about the - true lifetime of values, which is an important stepping stone to getting - automatic value destruction in place. - -- 📢 Call syntax support has been added. Now you can directly call an object - that implements the `__call__` method, like `foo(5)`. - -- 📢 The name for copy constructors got renamed from `__copy__` to - `__copyinit__`. Furthermore, non-`@register_passable` types now implement - it like they do an init method where you fill in a by-reference self, for - example: - - ```mojo - fn __copyinit__(inout self, existing: Self): - self.first = existing.first - self.second = existing.second - ``` - - This makes copy construction work more similarly to initialization, and - still keeps copies `x = y` distinct from initialization `x = T(y)`. - -- 📢 Initializers for memory-primary types are now required to be in the form - `__init__(inout self, ...):` with a None result type, but for register primary - types, it remains in the form `__init__(...) -> Self:`. The `T{}` initializer - syntax has been removed for memory-primary types. - -- Mojo String literals now emit a builtin `StringLiteral` type! One less MLIR - type to worry about. - -- New `__getattr__` and `__setattr__` dunder methods were added. Mojo calls - these methods on a type when attempting member lookup of a non-static member. - This allows writing dynamic objects like `x.foo()` where `foo` is not a member - of `x`. - -- Early destructor support has been added. Types can now define a special - destructor method `__del___` (note three underscores). This is an early - feature and it is still being built out. There are many caveats, bugs, - and missing pieces. Stay tuned! - -- 📚 Integer division and mod have been corrected for rounding in the presence - of negative numbers. - -- 📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to - `SIMD[1, type]`. - -## March 2023 - -### Week of 2023-03-27 - -- 📢 Parameter names are no longer load-bearing in function signatures. This - gives more flexibility in defining higher-order functions, because the - functions passed as parameters do not need their parameter names to match. - - ```mojo - # Define a higher-order function... - fn generator[ - func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] - ](): - pass - - # Int parameter is named "foo". - fn f0[foo: Int](): - pass - - # Int parameter is named "bar". - fn f1[bar: Int](): - pass - - fn main(): - # Both can be used as `func`! - generator[f0]() - generator[f1]() - ``` - - Stay tuned for improved function type syntax... - -- 📢 Two magic operators, named `__get_lvalue_as_address(x)` and - `__get_address_as_lvalue` convert stored LValues to and from `!kgen.pointer` - types (respectively). This is most useful when using the `Pointer[T]` - library type. The `Pointer.address_of(lvalue)` method uses the first one - internally. The second one must currently be used explicitly, and can be - used to project a pointer to a reference that you can pass around and use - as a self value, for example: - - ```mojo - # "Replacement new" SomeHeavy value into the memory pointed to by a - # Pointer[SomeHeavy]. - __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) - ``` - - Note that `__get_address_as_lvalue` assumes that there is already a value at - the specified address, so the assignment above will run the `SomeHeavy` - destructor (if any) before reassigning over the value. - -- The `(((x)))` syntax is __mlir_op has been removed in favor of - `__get_lvalue_as_address` which solves the same problem and is more general. - -- 📢 When using a mutable `self` argument to a struct `__init__` method, it - now must be declared with `&`, like any other mutable method. This clarifies - the mutation model by making `__init__` consistent with other mutating - methods. - -- 📚 Add variadic string join function. - -- 📚 Default initialize values with 0 or null if possible. - -- 📚 Add compressed, aligned, and mask store intrinsics. - -### Week of 2023-03-20 - -- Initial `String` type is added to the standard library with some very basic - methods. - -- Add `DimList` to remove the need to use an MLIR list type throughout the - standard library. - -- 📢 The `__clone__` method for copying a value is now named `__copy__` to - better follow Python term of art. - -- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, - instead of taking it by reference. This makes it easier to write, works for - `@register_passable` types, and exposes more optimization opportunities to - the early optimizer and dataflow analysis passes. - - ```mojo - # Before: - fn __clone__(inout self) -> Self: ... - - # After: - fn __copy__(self) -> Self: ... - ``` - -- 📢 A new `@register_passable("trivial")` may be applied to structs that - have no need for a custom `__copy__` or `__del__` method, and whose state is - only made up of `@register_passable("trivial")` types. This eliminates the - need to define `__copy__` boilerplate and reduces the amount of IR generated - by the compiler for trivial types like `Int`. - -- You can now write back to attributes of structs that are produced by a - computed lvalue expression. For example `a[i].x = ..` works when `a[i]` - is produced with a `__getitem__`/`__setitem__` call. This is implemented by - performing a read of `a[i]`, updating the temporary, then doing a writeback. - -- The remaining hurdles to using non-parametric, `@register_passable` types as - parameter values have been cleared. Types like `Int` should enjoy full use as - parameter values. - -- Parameter pack inference has been added to function calls. Calls to functions - with parameter packs can now elide the pack types: - - ```mojo - fn foo[*Ts: AnyType](*args: *Ts): pass - - foo(1, 1.2, True, "hello") - ``` - - Note that the syntax for parameter packs has been changed as well. - -- 📚 Add the runtime string type. - -- 📚 Introduce the DimList struct to remove the need to use low-level MLIR - operations. - -### Week of 2023-03-13 - -- 📢 Initializers for structs now use `__init__` instead of `__new__`, - following standard practice in Python. You can write them in one of two - styles, either traditional where you mutate self: - - ```mojo - fn __init__(self, x: Int): - self.x = x - ``` - - or as a function that returns an instance: - - ```mojo - fn __init__(x: Int) -> Self: - return Self {x: x} - ``` - - Note that `@register_passable` types must use the later style. - -- 📢 The default argument convention is now the `borrowed` convention. A - "borrowed" argument is passed like a C++ `const&` so it doesn't need to - invoke the copy constructor (aka the `__clone__` method) when passing a value - to the function. There are two differences from C++ `const&`: - - 1. A future borrow checker will make sure there are no mutable - aliases with an immutable borrow. - 2. `@register_passable` values are passed directly in an SSA register (and - thus, usually in a machine register) instead of using an extra reference - wrapper. This is more efficient and is the 'right default' for - `@register_passable` values like integers and pointers. - - This also paves the way to remove the reference requirement from `__clone__` - method arguments, which will allow us to fill in more support for them. - -- Support for variadic pack arguments has been added to Mojo. You can now - write heterogeneous variadic packs like: - - ```mojo - fn foo[*Ts: AnyType](args*: Ts): pass - - foo[Int, F32, String, Bool](1, 1.5, "hello", True) - ``` - -- The `owned` argument convention has been added. This argument convention - indicates that the function takes ownership of the argument and is responsible - for managing its lifetime. - -- The `borrowed` argument convention has been added. This convention signifies - the callee gets an immutable shared reference to a value in the caller's - context. - -- 📚 Add the `getenv` function to the `OS` module to enable getting environment - variables. - -- 📚 Enable the use of dynamic strides in `NDBuffer`. - -### Week of 2023-03-06 - -- 📢 Support added for using capturing async functions as parameters. - -- 📢 Returning result parameters has been moved from `return` statements to a - new `param_return` statement. This allows returning result parameters from - throwing functions: - - ```mojo - @raises - fn foo[() -> out: Int](): - param_return[42] - raise Error() - ``` - - And returning different parameters along `@parameter if` branches: - - ```mojo - fn bar[in: Bool -> out: Int](): - @parameter - if in: - param_return[1] - else: - param_return[2] - ``` - -- 📢 Mojo now supports omitting returns at the end of functions when they would - not reachable. For instance, - - ```mojo - fn foo(cond: Bool) -> Int: - if cond: - return 0 - else: - return 1 - - fn bar() -> Int: - while True: - pass - ``` - -- String literals now support concatenation, so `"hello " "world"` is treated - the same as `"hello world"`. - -- Empty bodies on functions, structs, and control flow statements are no longer - allowed. Please use `pass` in them to explicitly mark that they are empty, - just like in Python. - -- 📢 Structs in Mojo now default to living in memory instead of being passed - around in registers. This is the right default for generality (large - structures, structures whose pointer identity matters, etc) and is a key - technology that enables the borrow model. For simple types like `Int` and - `SIMD`, they can be marked as `@register_passable`. - - Note that memory-only types currently have some limitations: they cannot be - used in generic algorithms that take and return a `!mlirtype` argument, and - they cannot be used in parameter expressions. Because of this, a lot of - types have to be marked `@register_passable` just to work around the - limitations. We expect to enable these use-cases over time. - -- 📢 Mojo now supports computed lvalues, which means you can finally assign to - subscript expressions instead of having to call `__setitem__` explicitly. - - Some details on this: Mojo allows you to define multiple `__setitem__` - overloads, but will pick the one that matches your `__getitem__` type if - present. It allows you to pass computed lvalues into inout arguments by - introducing a temporary copy of the value in question. - -- Mojo now has much better support for using register-primary struct types in - parameter expressions and as the types of parameter values. This will allow - migration of many standard library types away from using bare MLIR types like - `__mlir_type.index` and towards using `Int`. This moves us towards getting rid - of MLIR types everywhere and makes struct types first-class citizens in the - parameter system. - -- 📚 Add a `sort` function. - -- 📚 Add non-temporal store to enable cache bypass. - -## February 2023 - -### Week of 2023-02-27 - -- 📢 The `@interface`, `@implements`, and `@evaluator` trio of decorators have - been removed, replaced by the `@parameter if` and `@adaptive` features. - -- 📢 Parameter inference can now infer the type of variadic lists. - -- 📢 Memory primary types are now supported in function results. A result slot - is allocated in the caller, and the callee writes the result of the function - into that slow. This is more efficient for large types that don't fit into - registers neatly! And initializers for memory-primary types now initialize - the value in-place, instead of emitting a copy! - -- Support for `let` decls of memory primary types has been implemented. These - are constant, ready-only values of memory primary types but which are - allocated on the function stack. - -- Overload conversion resolution and parameter inference has been improved: - - 1. Inference now works with `let` decls in some scenarios that weren't - working before. - 2. Parameter bindings can now infer types into parameter expressions. This - helps resolve higher-order functions in parameter expressions. - -- 📚 Optimize floor, ceil, and ldexp on X86 hardware. - -- 📚 Implement the log math function. - -### Week of 2023-02-20 - -- 📢 A new `@__memory_primary` struct decorator has been introduced. Memory - primary types must always have an address. For instance, they are always - stack-allocated when declared in a function and their values are passed into - function calls by address instead of copy. This is in contract with register - primary types that may not have an address, and which are passed by value - in function calls. Memory-primary fields are not allowed inside - register-primary structs, because struct elements are stored in-line. - -- 📢 A new `_CompilerBuiltin` module was added. This module defines core types - and functions of the language that are referenced by the parser, and hence, is - auto-imported by all other modules. For example new types for literal values - like the boolean True/False will be included in `_CompilerBuiltin`. - -- 📢 A special `__adaptive_set` property can be accessed on a function reference - marked as `@adaptive`. The property returns the adaptive overload set of that - function. The return type is a `!kgen.variadic`. This feature is useful to - implement a generic `evaluate` function in the standard library. - -- 📢 A new built-in literal type `BoolLiteral` was added in `_CompilerBuiltin`. - It represents the literal boolean values `True` and `False`. This is the first - Mojo literal to be emitted as a standard library type! - -- 📚 Add the prefetch intrinsic to enable HW prefetching a cache line. - -- 📚 Add the InlinedFixedVector, which is optimized for small vectors and stores - values on both the stack and the heap. - -### Week of 2023-02-13 - -- Unqualified lookups of struct members apply contextual parameters. This means - for instance that you can refer to static methods without binding the - struct parameters. - - ```mojo - struct Foo[x: Int]: - @staticmethod - bar(): pass - - foo(self): - bar() # implicitly binds to Foo[x].bar() - Foo[2].bar() # explicitly bind to another parameter - ``` - -- 📢 A new `Self` type refers to the enclosing type with all parameters bound - to their current values. This is useful when working with complex parametric - types, e.g.: - - ```mojo - struct MyArray[size: Int, element_type: type]: - fn __new__() -> Self: - return Self {...} - ``` - - which is a lot nicer than having to say `MyArray[size, element_type]` over - and over again. - -- 📢 Mojo now supports an `@adaptive` decorator. This decorator will supersede - interfaces, and it represents an overloaded function that is allowed to - resolve to multiple valid candidates. In that case, the call is emitted as a - fork, resulting in multiple function candidates to search over. - - ```mojo - @adaptive - fn sort(arr: ArraySlice[Int]): - bubble_sort(arr) - - @adaptive - fn sort(arr: ArraySlice[Int]): - merge_sort(arr) - - fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]): - let arr = lhs + rhs - sort(arr) # this forks compilation, creating two instances - # of the surrounding function - ``` - -- 📢 Mojo now requires that types implement the `__clone__` special member in - order to copy them. This allows the safe definition of non-copyable types - like Atomic. Note that Mojo still doesn't implement destructors, and (due to - the absence of non-mutable references) it doesn't actually invoke the - `__clone__` member when copying a let value. As such, this forces to you as - a Mojo user to write maximal boilerplate without getting much value out of it. - - In the future, we will reduce the boilerplate with decorators, and we will - actually start using it. This will take some time to build out though. - -- 📢 A special `__mlir_region` statement was added to provide stronger - invariants around defining MLIR operation regions in Mojo. It similar syntax - to function declarations, except it there are no results and no input - conventions. - -- 📚 Implement the log math function. - -- 📚 Improve the DType struct to enable compile-time equality checks. - -- 📚 Add the Complex struct class. - -### Week of 2023-02-06 - -- 📢 The `if` statement now supports a `@parameter` decorator, which requires - its condition to be a parameter expression, but which only emits the 'True' - side of the condition to the binary, providing a "static if" functionality. - This should eliminate many uses of `@interface` that are just used to provide - different constraint on the implementations. - -- 📢 `fn main():` is now automatically exported and directly runnable by the - command-line `mojo` tool. This is a stop-gap solution to enable script-like - use cases until we have more of the language built out. - -- 🪦 The `@nodebug_inline` feature has been removed, please use - `@alwaysinline("nodebug")` for methods that must be inlined and that we don't - want to step into. - -- 📢 Python chained comparisons, ex. `a < b < c`, are now supported in Mojo. - -- 📢 Functions can now be defined with default argument values, such as - `def f(x: Int, y: Int = 5):`. The default argument value is used when callers - do not provide a value for that argument: `f(3)`, for example, uses the - default argument value of `y = 5`. - -- Unused coroutine results are now nicely diagnosed as "missing await" warnings. - -- 📚 Introduce a vectorized reduction operations to the SIMD type. - -## January 2023 - -### Week of 2023-01-30 - -- A basic Mojo language server has been added to the VS Code extension, which - parses your code as you write it, and provides warnings, errors, and fix-it - suggestions! - -- 💯 The Mojo standard library is now implicitly imported by default. - -- The coroutine lowering support was reworked and a new `Coroutine[T]` type was - implemented. Now, the result of a call to an async function MUST be wrapped in - a `Coroutine[T]`, or else memory will leak. In the future, when Mojo supports - destructors and library types as literal types, the results of async function - calls will automatically wrapped in a `Coroutine[T]`. But today, it must be - done manually. This type implements all the expected hooks, such as - `__await__`, and `get()` to retrieve the result. Typical usage: - - ```mojo - async fn add_three(a: Int, b: Int, c: Int) -> Int: - return a + b + c - - async fn call_it(): - let task: Coroutine[Int] = add_three(1, 2, 3) - print(await task) - ``` - -- ⭐️ We now diagnose unused expression values at statement context in `fn` - declarations (but not in `def`s). This catches bugs with unused values, e.g. - when you forget the parens to call a function. - -- 📢 An `@always_inline("nodebug")` function decorator can be used on functions - that need to be force inlined, but when they should not have debug info in - the result. This should be used on methods like `Int.__add__` which should - be treated as builtin. - -- 📢 The `@export` decorator now supports an explicit symbol name to export to, - for example: - - ```mojo - @export("baz") # exported as 'baz' - fn some_mojo_fn_name(): - ``` - -- 📢 🚧 Subscript syntax is now wired up to the `__getitem__` dunder method. - - This allows type authors to implement the `__getitem__` method to enable - values to be subscripted. This is an extended version of the Python semantics - (given we support overloading) that allows you to define N indices instead of - a single version that takes a tuple (also convenient because we don't have - tuples yet). - - Note that this has a very, very important limitation: subscripts are NOT - wired up to `__setitem__` yet. This means that you can read values with - `.. = v[i]` but you cannot store to them with `v[i] = ..`. For this, please - continue to call `__setitem__` directly. - -- 📢 Function calls support parameter inference. - - For calls to functions that have an insufficient number of parameters - specified at the callsite, we can now infer them from the argument list. We - do this by matching up the parallel type structure to infer what the - parameters must be. - - Note that this works left to right in the parameter list, applying explicitly - specified parameters before trying to infer new ones. This is similar to how - C++ does things, which means that you may want to reorder the list of - parameters with this in mind. For example, a `dyn_cast`-like function will be - more elegant when implemented as: - - `fn dyn_cast[DstType: type, SrcType: type](src: SrcType) -> DstType:` - - Than with the `SrcType`/`DstType` parameters flipped around. - -- 📚 Add the growable Dynamic vector struct. - -### Week of 2023-01-23 - -- Inplace operations like `+=`/`__iadd__` may now take `self` by-val if they - want to, instead of requiring it to be by-ref. -- ⭐️ Inplace operations are no longer allowed to return a non-None value. The - corresponding syntax is a statement, not an expression. - -- A new `TaskGroup` type was added to the standard library. This type can be - used to schedule multiple tasks on a multi-threaded workqueue to be executed - in parallel. An async function can `await` all the tasks at once with the - taskgroup. - -- 📢 We now support for loops! A type that defines an `__iter__` method that - returns a type that defines `__next__` and `__len__` methods is eligible to - be used in the statement `for el in X()`. Control flow exits the loop when - the length is zero. - - This means things like this now work: - - ```mojo - for item in range(start, end, step): - print(item) - ``` - -- Result parameters now have names. This is useful for referring to result - parameters in the return types of a function: - - ```mojo - fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]: - ``` - -- 📢 We now support homogeneous variadics in value argument lists, using the - standard Python `fn thing(*args: Int):` syntax! Variadics also have support - in parameter lists: - - ```mojo - fn variadic_params_and_args[*a: Int](*b: Int): - print(a[0]) - print(b[1]) - ``` - -- 📚 Add the range struct to enable `for ... range(...)` loops. - -- 📚 Introduce the unroll generator to allow one to unroll loops via a library - function. - -### Week of 2023-01-16 - -- 📢 Struct field references are now supported in parameter context, so you - can use `someInt.value` to get the underlying MLIR thing out of it. This - should allow using first-class types in parameters more widely. -- 📢 We now support "pretty" initialization syntax for structs, e.g.: - - ```mojo - struct Int: - var value: __mlir_type.index - fn __new__(value: __mlir_type.index) -> Int: - return Int {value: value} - ``` - - This eliminates the need to directly use the MLIR `lit.struct.create` op in - struct initializers. This syntax may change in the future when ownership - comes in, because we will be able to support the standard `__init__` model - then. -- 📢 It is now possible to attach regions to `__mlir_op` operations. This is - done with a hack that allows an optional `_region` attribute that lists - references to the region bodies (max 1 region right now due to lack of list - `[]` literal). -- Nested functions now parse, e.g.: - - ```mojo - fn foo(): - fn bar(): - pass - bar() - ``` - -- Python-style `async` functions should now work and the `await` expression - prefix is now supported. This provides the joy of async/await syntactic - sugar when working with asynchronous functions. This is still somewhat - dangerous to use because we don't have proper memory ownership support yet. - -- String literals are now supported. - -- Return processing is now handled by a dataflow pass inside the compiler, so - it is possible to return early out of if statements. - -- The parser now supports generating 'fixit' hints on diagnostics, and uses - them when a dictionary literal uses a colon instead of equal, e.g.: - - ```log - x.mojo:8:48: error: expected ':' in subscript slice, not '=' - return __mlir_op.`lit.struct.create`[value = 42]() - ^ - : - ``` - -- 📚 Add reduction methods which operate on buffers. - -- 📚 Add more math functions like sigmoid, sqrt, rsqrt, etc. - -- 📚 Add partial load / store which enable loads and stores that are predicated - on a condition. - -### Week of 2023-01-09 - -- The `/` and `*` markers in function signatures are now parsed and their - invariants are checked. We do not yet support keyword arguments yet though, - so they aren't very useful. -- Functions now support a new `@nodebug_inline` decorator. - (Historical note: this was later replaced with `@alwaysinline("nodebug")`). - - Many of the things at the bottom level of the Mojo stack are trivial - zero-abstraction wrappers around MLIR things, for example, the `+` - operator on Int or the `__bool__` method on Bool itself. These operators - need to be force inlined even at -O0, but they have some additional things - that we need to wrestle with: - - 1. In no case would a user actually want to step into the `__bool__` method on - Bool or the + method on Int. This would be terrible debugger QoI for - unless you're debugging Int itself. We need something like - `__always_inline__, __nodebug__` attributes that clang uses in headers - like xmmintrin.h. - - 2. Similarly, these "operators" should be treated by users as primitives: - they don't want to know about MLIR or internal implementation details of - Int. - - 3. These trivial zero abstraction things should be eliminated early in the - compiler pipeline so they don't slow down the compiler, bloating out the - call graph with trivial leaves. Such thing slows down the elaborator, - interferes with basic MLIR things like fold(), bloats out the IR, or - bloats out generated debug info. - - 4. In a parameter context, we want some of these things to get inlined so - they can be simplified by the attribute logic and play more nicely with - canonical types. This is just a nice to have thing those of us who have - to stare at generated IR. - - The solution to this is a new `@nodebug_inline` decorator. This decorator - causes the parser to force-inline the callee instead of generating a call to - it. While doing so, it gives the operations the location of the call itself - (that's the "nodebug" part) and strips out let decls that were part of the - internal implementation details. - - This is a super-power-user-feature intended for those building the standard - library itself, so it is intentionally limited in power and scope: It can - only be used on small functions, it doesn't support regions, by-ref, throws, - async, etc. - -- Separately, we now support an `@alwaysInline` decorator on functions. This - is a general decorator that works on any function, and indicates that the - function must be inlined. Unlike `@nodebug_inline`, this kind of inlining is - performed later in the compilation pipeline. - -- The `__include` hack has been removed now that we have proper import support. - -- `__mlir_op` can now get address of l-value: - - You can use magic `(((x)))` syntax in __mlir_op that forces the `x` - expression to be an lvalue, and yields its address. This provides an escape - hatch (isolated off in `__mlir_op` land) that allows unsafe access to lvalue - addresses. - -- We now support `__rlshift__` and `__rtruediv__`. - -- 📢 The parser now resolves scoped alias references. This allows us to support - things like `SomeType.someAlias`, forward substituting the value. This - unblocks use of aliases in types like `DType`. We'd like to eventually - preserve the reference in the AST, but this unblocks library development. - -- 📚 Add a `now` function and `Benchmark` struct to enable timing and - benchmarking. - -- 📚 Move more of the computation in NDBuffer from runtime to compile time if - possible (e.g. when the dimensions are known at compile time). - -### Week of 2023-01-02 - -- 📚 Added the `print` function which works on Integers and SIMD values. - -- The frontend now has a new diagnostic subsystem used by the `kgen` tool (but - not by `kgen-translate` for tests) that supports source ranges on - diagnostics. Before we'd emit an error like: - - ```log - x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' - callee(1.0+F32(2.0)) - ^ - x.lit:4:1: note: function declared here - fn callee(a: Int): - ^ - ``` - - now we produce: - - ```log - x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' - callee(1.0+F32(2.0)) - ^ ~~~~~~~~~~~~ - x.lit:4:1: note: function declared here - fn callee(a: Int): - ^ - ``` - -- 📢 Parameter results are now supported in a proper way. They are now forward - declared with an alias declaration and then bound in a call with an arrow, - e.g.: - - ```mojo - alias a: __mlir_type.index - alias b: __mlir_type.index - idx_result_params[xyz * 2 -> a, b]() - ``` - -- Various minor issues with implicit conversions are fixed. For instances, - implicit conversions are now supported in parameter binding contexts and - `alias` declarations with explicit types. -- Doc strings are allowed on functions and structs, but they are currently - discarded by the parser. - -- 📚 Add a `print` method!!! - -- 📚 Demonstrate a naive matmul in Mojo. - -- 📚 Initial work on functions that depend on types (e.g. FPUtils, nan, inf, - etc.) - -- 📚 Allow one to query hardware properties such as simd_width, os, etc. via - TargetInfo at compile time. - -## December 2022 - -### Week of 2022-12-26 - -- 📢 You can now call functions in a parameter context! Calling a function in - a parameter context will evaluate the function at compile time. The result - can then be used as parameter values. For example, - - ```mojo - fn fma(x: Int, y: Int, z: Int) -> Int: - return a + b * c - - fn parameter_call(): - alias nelts = fma(32, 2, 16) - var x: SIMD[f32, nelts] - ``` - -- You can now disable printing of types in an `__mlir_attr` substitution by - using unary `+` expression. - -- 📢 `let` declarations are now supported in functions. `let` declarations are - local run-time constant values, which are always rvalues. They complement - 'var' decls (which are mutable lvalues) and are the normal thing to use in - most cases. They also generate less IR and are always in SSA form when - initialized. - - We will want to extend this to support 'let' decls in structs at some point - and support lazy initialized 'let' declarations (using dataflow analysis) but - that isn't supported yet. - -- 📚 Add the NDBuffer struct. - -- Happy new year. - -### Week of 2022-12-19 - -- 📚 Start of the Standard library: - 1. Added Integer and SIMD structs to bootstrap the standard library. - 2. Added very basic buffer data structure. - -- We have basic support for parsing parameter results in function calls! Result - parameters are an important Mojo metaprogramming feature. They allow functions - to return compile-time constants. - - ```mojo - fn get_preferred_simdwidthof[() -> nelts: Int](): - return[2] - - fn vectorized_function(): - get_preferred_simdwidthof[() -> nelts]() - var x: SIMD[f32, nelts] - ``` - -- Types can now be used as parameters of `!kgen.mlirtype` in many more cases. - -- MLIR operations with zero results don't need to specify `_type: []` anymore. - -- We support parsing triple quoted strings, for writing docstrings for your - functions and structs! - -- A new `__mlir_type[a,b,c]` syntax is available for substituting into MLIR - types and attributes is available, and the old placeholder approach is - removed. This approach has a few advantages beyond what placeholders do: - - 1. It's simpler. - 2. It doesn't form the intermediate result with placeholders, which - gets rejected by MLIR's semantic analysis, e.g. the complex case - couldn't be expressed before. - 3. It provides a simple way to break long attrs/types across multiple - lines. - -- We now support an `@evaluator` decorator on functions for KGEN evaluators. - This enables specifying user-defined interface evaluators when performing - search during compilation. - -- 📢 `import` syntax is now supported! - - This handles packaging imported modules into file ops, enables effective - isolation from the other decls. "import" into the desired context is just - aliasing decls, with the proper symbols references handle automatically during - IR generation. As a starting point, this doesn't handle any notion of packages - (as those haven't been sketched out enough). - -- 📢 Reversed binary operators (like `__radd__`) are now looked up and used if - the forward version (like `__add__`) doesn't work for some reason. - -- 📢 Implicit conversions are now generally available, e.g. in assign - statements, variable initializers etc. There are probably a few more places - they should work, but we can start eliminating all the extraneous explicit - casts from literals now. - -- Happy Holidays - -### Week of 2022-12-12 - -- 📢 Function overloading now works. Call resolution filters candidate list - according to the actual parameter and value argument specified at the site of - the call, diagnosing an error if none of the candidates are viable or if - multiple are viable and ambiguous. We also consider implicit conversions in - overload look: - - ```mojo - fn foo(x: Int): pass - fn foo(x: F64): pass - - foo(Int(1)) # resolves to the first overload - foo(1.0) # resolves to the second overload - foo(1) # error: both candidates viable with 1 implicit conversion! - ``` - -- The short circuiting binary `and` and `or` expressions are now supported. - -- Unary operator processing is a lot more robust, now handling the `not` - expression and `~x` on Bool. - -- 📢 The compiler now generates debug information for use with GDB/LLDB that - describes variables and functions. - -- The first version of the Mojo Visual Studio Code extension has been released! - It supports syntax highlighting for Mojo files. - -- The first version of the `Bool` type has landed in the new Mojo standard - library! - -- 📢 Implicit conversions are now supported in return statements. - -### Week of 2022-12-05 - -- "Discard" patterns are now supported, e.g. `_ = foo()` - -- We now support implicit conversions in function call arguments, e.g. - converting an `index` value to `Int` automatically. This eliminates a bunch - of casts, e.g. the need to say F32(1.0) everywhere. - - This is limited for a few reasons that will be improved later: - 1. We don't support overloading, so lots of types aren't convertible - from all the things they should be, e.g. you can't pass "1" to - something that expects F32, because F32 can't be created from index. - 2. This doesn't "check to see if we can invoke `__new__`" it force applies - it on a mismatch, which leads to poor QoI. - 3. This doesn't fix things that need radd. - -## November 2022 - -### Week of 2022-11-28 - -- 📢 We support the `True` and `False` keywords as expressions. - -- 📢 A new `alias` declaration is supported which allows defining local - parameter values. This will eventually subsume type aliases and other - things as it gets built out. - -- 📢 We now have end-to-end execution of Mojo files using the `kgen` tool! - Functions exported with `@export` can be executed. - -- 📢 We have try-except-else and `raise` statements and implicit error - propagation! The error semantics are that `def` can raise by default, but `fn` - must explicitly declare raising with a `@raises` decorator. Stub out basic - `Error` type. - -- The `&` sigil for by-ref arguments is now specified after the identifier. - Postfix works better for ref and move operators on the expression - side because it chains an mentally associates correctly: - `thing.method().result^`. We don't do that yet, but align param - decl syntax to it so that things won't be odd looking when we do. - In practice this looks like: - - ```mojo - def mutate_argument(a&: index): - a = 25 - ``` - -### Week of 2022-11-21 - -- 📢 The magic `index` type is gone. Long live `__mlir_type.index`. - -- Implement parameter substitution into parametric `__mlir_type` decls. This - allows us to define parametric opaque MLIR types with exposed parameters using - a new "placeholder" attribute. This allows us to expose the power of the KGEN - type parametric system directly into Mojo. - -- 📢 Fully-parametric custom types can now be defined and work in Mojo, bringing - together a lot of the recent work. We can write the SIMD type directly as a - wrapper around the KGEN type, for example: - - ```mojo - struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]: - var value: - __mlir_type.`!pop.simd<#lit, - #lit>`[nelts, dt] - - fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]: - return __mlir_op.`pop.add`(self.value, rhs.value) - ``` - -### Week of 2022-11-14 - -- 📢 Implement a magic `__mlir_type` declaration that can be used to access any - MLIR type. E.g. `__mlir_type.f64`. - -- 📢 Add an `fn` declaration. These are like `def` declarations, but are more - strict in a few ways: they require type annotations on arguments, don't allow - implicit variable declarations in their body, and make their arguments rvalues - instead of lvalues. - -- Implemented Swift-style backtick identifiers, which are useful for code - migration where names may collide with new keywords. - -- 📢 A new `__include` directive has been added that performs source-level - textual includes. This is temporary until we have an `import` model. - -- Implement IR generation for arithmetic operators like `+` and `*` in terms - of the `__add__` and `__mul__` methods. - -- 📢 Added support for `break` and `continue` statements, as well as early - returns inside loops and conditionals! - -- 📢 Implemented augmented assignment operators, like `+=` and `@=`. - -- 📢 Mojo now has access to generating any MLIR operations (without regions) - with a new `__mlir_op` magic declaration. We can start to build out the - language's builtin types with this: - - ```mojo - struct Int: - var value: __mlir_type.index - - fn __add__(self, rhs: Int) -> Int: - return __mlir_op.`index.add`(self.value, rhs.value) - ``` - - Attributes can be attached to the declaration with subscript `[]` syntax, - and an explicit result type can be specified with a special `_type` attribute - if it cannot be inferred. Attributes can be accessed via the `__mlir_attr` - magic decl: - - ```mojo - __mlir_op.`index.cmp`[ - _type: __mlir_type.i1, - pred: __mlir_attr.`#index` - ](lhs, rhs) - ``` - -- Improved diagnostics emissions with ranges! Now errors highlight the whole - section of code and not just the first character. - -### Week of 2022-11-07 - -- Implemented the `@interface` and `@implements` decorators, which provide - access to KGEN generator interfaces. A function marked as an `@interface` - has no body, but it can be implemented by multiple other functions. - - ```mojo - @interface - def add(lhs: index, rhs: index): - - @implements(add) - def normal_add(lhs: index, rhs: index) -> index: - return lhs + rhs - - @implements(add) - def slow_add(lhs: index, rhs: index) -> index: - wait(1000) - return normal_add(lhs, rhs) - ``` - -- 📢 Support for static struct methods and initializer syntax has been added. - Initializing a struct with `Foo()` calls an implicitly static `__new__` - method. This method should be used instead of `__init__` inside structs. - - ```mojo - struct Foo: - var value: index - - def __new__() -> Foo: - var result: Foo - result.value = Foo.return_a_number() # static method! - return result - - @staticmethod - def return_a_number() -> index: - return 42 - ``` - -- 📢 Full by-ref argument support. It's now possible to define in-place - operators like `__iadd__` and functions like `swap(x, y)` correctly. - -- 📢 Implemented support for field extract from rvalues, like `x.value` where - `x` is not an lvalue (`var` declaration or by-ref function argument). - -## October 2022 - -### Week of 2022-10-31 - -- Revised `return` handling so that a return statement with no expression is - syntax sugar for `return None`. This enables early exits in functions that - implicitly return `None` to be cleaner: - - ```mojo - def just_return(): - return - ``` - -- Added support for parsing more expressions: if-else, bitwise operators, - shift operators, comparisons, floor division, remainder, and matmul. - -- 📢 The type of the `self` argument can now be omitted on member methods. - -### Week of 2022-10-24 - -- Added parser support for right-associativity and unary ops, like the power - operator `a ** b ** c` and negation operator `-a`. - -- Add support for `&expr` in Mojo, which allows denoting a by-ref argument in - functions. This is required because the `self` type of a struct method is - implicitly a pointer. - -- Implemented support for parametric function declarations, such as: - - ```mojo - struct SIMD[dt: DType, width: index]: - fn struct_method(self: &SIMD[dt, width]): - pass - - def fancy_add[dt: DType, width: index]( - lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index: - return width - ``` - -### Week of 2022-10-17 - -- Added explicit variable declarations with `var`, for declaring variables both - inside functions and structs, with support for type references. Added `index` - as a temporary built-in type. - - ```mojo - def foo(lhs: index, rhs: index) -> index: - var result: index = lhs + rhs - return result - ``` - -- Implemented support for parsing struct declarations and references to type - declarations in functions! In `def`, the type can be omitted to signal an - object type. - - ```mojo - struct Foo: - var member: index - - def bar(x: Foo, obj) -> index: - return x.member - ``` - -- Implemented parser support for `if` statements and `while` loops! - - ```mojo - def if_stmt(c: index, a: index, b: index) -> index: - var result: index = 0 - if c: - result = a - else: - result = b - return result - - def while_stmt(init: index): - while init > 1: - init = init - 1 - ``` - -- Significantly improved error emission and handling, allowing the parser to - emit multiple errors while parsing a file. - -### Week of 2022-10-10 - -- Added support for parsing integer, float, and string literals. - -- Implemented parser support for function input parameters and results. You can - now write parametric functions like, - - ```mojo - def foo[param: Int](arg: Int) -> Int: - result = param + arg - return result - ``` - -### Week of 2022-10-03 - -- Added some basic parser scaffolding and initial parser productions, including - trivial expressions and assignment parser productions. -- Implemented basic scope handling and function IR generation, with support for - forward declarations. Simple functions like, - - ```mojo - def foo(x: Int): - ``` - - Now parse! But all argument types are hard-coded to the MLIR `index` type. - -- Added IR emission for simple arithmetic expressions on builtin types, like - `x + y`. - -## September 2022 - -### Week of 2022-09-26 - -- Mojo's first patch to add a lexer was Sep 27, 2022. - -- Settled on `[]` for Mojo generics instead of `<>`. Square brackets are - consistent with Python generics and don't have the less than ambiguity - other languages have. diff --git a/docs/changelog.md b/docs/changelog.md index 7a557e9c17..0d6a93fd22 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,34 @@ +--- +title: Mojo🔥 changelog +sidebar_label: Changelog +description: A history of significant Mojo changes. +toc_max_heading_level: 2 +website: + open-graph: + image: /static/images/mojo-social-card.png + twitter-card: + image: /static/images/mojo-social-card.png +--- -This is a running list of significant UNRELEASED changes for the Mojo language -and tools. Please add any significant user-visible changes here. +This is a running list of significant changes for the Mojo language and tools. +It doesn't include all internal implementation changes. + +## Update Mojo + +If you don't have Mojo yet, see the [get started +guide](/mojo/manual/get-started/#get-the-mojo-sdk). + +To see your Mojo version, run this: + +```sh +mojo --version +``` + +To update Mojo, first [update `modular`](/cli/#description), and then run this: + +```sh +modular update mojo +``` [//]: # Here's the template to use when starting a new batch of notes: [//]: ## UNRELEASED @@ -13,13 +41,4632 @@ and tools. Please add any significant user-visible changes here. ### 🔥 Legendary +- Structs and other nominal types are now allowed to implicitly conform to + traits. A struct implicitly conforms to a trait if it implements all the + requirements for the trait. For example, any struct that implements `__str__` + will implicitly conform to `Stringable`, and then is usable with `str`. + + ```mojo + @value + struct Foo: + fn __str__(self) -> String: + return "foo!" + + fn main(): + print(str(Foo())) # prints 'foo!' + ``` + + Explicit conformance is still strongly encouraged where possible, because it + is useful for documentation and for communicating intentions. In the future, + explicit conformance will still be useful for features like default methods + and extensions. + +- Mojo's Python interoperability now supports passing keyword arguments to + Python callables: + + ```mojo + from python import Python + + def main(): + plt = Python.import_module("matplotlib.pyplot") + plt.plot((5, 10), (10, 15), color="red") + plt.show() + ``` + ### ⭐️ New -- The `sys` module now contains an `exit` function that would exit a Mojo - program with the specified error code. +- String types all conform to the + [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means + that you can now call `int("123")` to get the integer `123`. If the integer + cannot be parsed from the string, then an error is raised. + +- The `Tensor` type now has an `argmax` and `argmin` function to compute the + position of the max or min value. + +- The `FloatLiteral` type is now an infinite precision nonmaterializable type. + When materialized, `FloatLiteral` is converted to `Float64`. + +- The [`List`](/mojo/stdlib/collections/list.html#list) type now supports + construction from a variadic number of values. For example, + `List[Int](1, 2, 3)` works now. + +- The `print` function now takes a `sep` and `end` as keyword. This means that + one can write `print("Hello", "Mojo", sep="/", end="!!!\n")` to print the + message `Hello/Mojo!!!\n` to the screen. + +- The Mojo LSP server, via the new `-I` argument, now allows specifying + additional search paths to use when resolving imported modules in a document. + A corresponding `mojo.lsp.includeDirs` setting was added to the VS Code + extension as well. + +- Mojo now has support for variadic keyword argument, often referred to as + `**kwargs`. This means you can now declare and call functions like this: + + ```mojo + fn print_nicely(**kwargs: Int) raises: + for key in kwargs.keys(): + print(key[], "=", kwargs[key[]]) + + # prints: + # `a = 7` + # `y = 8` + print_nicely(a=7, y=8) + ``` + + There are currently a few limitations: + - The ownership semantics of variadic keyword arguments are always `owned`. + This is applied implicitly, and cannot be declared otherwise: + + ```mojo + # Not supported yet. + fn borrowed_var_kwargs(borrowed **kwargs: Int): ... + ``` + + - Functions with variadic keyword arguments cannot have default values for + keyword-only arguments, e.g. + + ```mojo + # Not allowed yet, because `b` is keyword-only with a default. + fn not_yet(*, b: Int = 9, **kwargs: Int): ... + + # Okay, because `c` is positional-or-keyword, so it can have a default. + fn still_works(c: Int = 5, **kwargs: Int): ... + ``` + + - Dictionary unpacking is not supported yet: + + ```mojo + fn takes_dict(d: Dict[String, Int]): + print_nicely(**d) # Not supported yet. + ``` + + - Variadic keyword parameters are not supported yet: + + ```mojo + # Not supported yet. + fn var_kwparams[**kwparams: Int](): ... + ``` + +- Added new `collections.OptionalReg` type, a register-passable alternative + to `Optional`. + + - Doc string code blocks can now `%#` to hide lines of code from documentation + generation. + + For example: + + ```mojo + var value = 5 + %# print(value) + ``` + + will generate documentation of the form: + + ```mojo + var value = 5 + ``` + + Hidden lines are processed as if they were normal code lines during test + execution. This allows for writing additional code within a doc string + example that is only used to ensure the example is runnable/testable. + + - Doc string code blocks can now `%#` to hide lines of code from documentation + generation. + + For example: + + ```mojo + var value = 5 + %# print(value) + ``` + + will generate documentation of the form: + + ```mojo + var value = 5 + ``` + + Hidden lines are processed as if they were normal code lines during test + execution. This allows for writing additional code within a doc string + example that is only used to ensure the example is runnable/testable. ### 🦋 Changed +- Mojo now warns about unused values in both `def` and `fn` declarations, + instead of completely disabling the warning in `def`s. It never warns about + unused `object` or `PythonObject` values, tying the warning to these types + instead of the kind of function they are unused in. This will help catch API + usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. + +- The [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) and + [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + types now support negative indexing. This means that you can write `vec[-1]` + which is equivalent to `vec[len(vec)-1]`. + +- The [`isinf()`](/mojo/stdlib/math/math#isinf) and + [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from + `math.limits` to the `math` module. + +- The `ulp` function has been added to the `math` module. This allows one to get + the units of least precision (or units of last place) of a floating point + value. + +- `EqualityComparable` trait now requires `__ne__` function for conformance in addition + to the previously existing `__eq__` function. + +- Many types now declare conformance to `EqualityComparable` trait. + +- `DynamicVector` has been renamed to `List`. It has also moved from the `collections.vector` + module to `collections.list` module. + +- `StaticTuple` parameter order has changed to `StaticTuple[type, size]` for + consistency with `SIMD` and similar collection types. + +- The signature of the elementwise function has been changed. The new order is + is `function`, `simd_width`, and then `rank`. As a result, the rank parameter + can now be inferred and one can call elementwise via: + + ```mojo + elementwise[func, simd_width](shape) + ``` + +- For the time being, dynamic type value will be disabled in the language, e.g. + the following will now fail with an error: + + ```mojo + var t = Int # dynamic type values not allowed + + struct SomeType: ... + + takes_type(SomeType) # dynamic type values not allowed + ``` + + We want to take a step back and (re)design type valued variables, + existentials, and other dynamic features for more 🔥. This does not affect + type valued parameters, so the following will work as before: + + ```mojo + alias t = Int # still 🔥 + + struct SomeType: ... + + takes_type[SomeType]() # already 🔥 + ``` + +- `PythonObject` is now register-passable. + +- `PythonObject.__iter__` now works correctly on more types of iterable Python + objects. Attempting to iterate over non-iterable objects will now raise an + exception instead of behaving as if iterating over an empty sequence. + `__iter__` also now borrows `self` rather than requiring `inout`, allowing + code like `for value in my_dict.values():`. + +- `List.push_back` has been removed. Please use the `append` function instead. + +- We took the opportunity to rehome some modules into their correct package + as we were going through the process of open-sourcing the Mojo Standard + Library. Specifically, the following are some breaking changes worth + calling out. Please update your import statements accordingly. + - `utils.list` has moved to `buffer.list`. + - `rand` and `randn` functions in the `random` package that return a `Tensor` + have moved to the `tensor` package. + - `Buffer`, `NDBuffer`, and friends have moved from the `memory` package + into a new `buffer` package. + - The `trap` function has been renamed to `abort`. It also has moved from the + `debug` module to the `os` module. + - `parallel_memcpy` has moved from the `memory` package into + the `buffer` package. + +- The `*_` expression in parameter expressions is now required to occur at the + end of a positional parameter list, instead of being allowed in the middle. + This is no longer supported: `SomeStruct[*_, 42]` but `SomeStruct[42, *_]` is + still allowed. We narrowed this because we want to encourage type designers + to get the order of parameters right, and want to extend `*_` to support + keyword parameters as well in the future. + ### ❌ Removed +- `let` declarations now produce a compile time error instead of a warning, + our next step in [removing let + declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md). + The compiler still recognizes the `let` keyword for now in order to produce + a good error message, but that will be removed in subsequent releases. + +- The `__get_address_as_lvalue` magic function has been removed. You can now + get an LValue from a `Pointer` or `Reference` by using the `ptr[]` operator. + +- The type parameter for the `memcpy` function is now automatically inferred. + This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` would + no longer work and the user would have to change the code to `memcpy(...)`. + +- `print_no_newline` has been removed. Please use `print(end="")` instead. + +- `memcpy` on `Buffer` has been removed in favor of just overloads for `Pointer` + and `DTypePointer`. + +- The functions `max_or_inf`, `min_or_neginf` have been removed from + `math.limit` and just inlined into their use in SIMD. + +### 🛠️ Fixed + +- [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference + now recursively matches function types. +- [#951](https://github.com/modularml/mojo/issues/951) - Functions that were + both `async` and `@always_inline` incorrectly errored. +- [#1858](https://github.com/modularml/mojo/issues/1858) - Trait with parametric + methods regression. +- [#1892](https://github.com/modularml/mojo/issues/1892) - Forbid unsupported + decorators on traits. +- [#1735](https://github.com/modularml/mojo/issues/1735) - Trait-typed values + are incorrectly considered equal. +- [#1909](https://github.com/modularml/mojo/issues/1909) - Crash due to nested + import in unreachable block. +- [#1921](https://github.com/modularml/mojo/issues/1921) - Parser crashes + binding Reference to lvalue with subtype lifetime. +- [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` + should return `T` instead of `Optional[T]`. +- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` + to floating point or integral types. +- [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` + does not work when specifying `end=""` +- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods + correctly handle edge cases where `size_out >= size`. + +## v24.1.1 (2024-03-18) + +This release includes installer improvements and enhanced error reporting for +installation issues. Otherwise it is functionally identical to Mojo 24.1. + +## v24.1 (2024-02-29) + +### 🔥 Legendary + +- Mojo is now bundled with [the MAX platform](/max)! + + As such, the Mojo package version now matches the MAX version, which follows + a `YY.MAJOR.MINOR` version scheme. Because this is our first release in 2024, + that makes this version `24.1`. + +- Mojo debugging support is here! The Mojo VS Code extension includes debugger + support. For details, see [Debugging](/mojo/tools/debugging) in the Mojo + Manual. + +### ⭐️ New + +- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our + collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` + checks, and requires member elements to conform to the `KeyElement` trait. + + ```mojo + from collections import Set + + var set = Set[Int](1, 2, 3) + print(len(set)) # 3 + set.add(4) + + for element in set: + print(element[]) + + set -= Set[Int](3, 4, 5) + print(set == Set[Int](1, 2)) # True + print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True + let element = set.pop() + print(len(set)) # 1 + ``` + +- Mojo now supports the `x in y` expression as syntax sugar for + `y.__contains__(x)` as well as `x not in y`. + +- Mojo now has support for keyword-only arguments and parameters. For example: + + ```mojo + fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2): + print(a * b * c * d) + + my_product(3, c=5) # prints '30' + my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c' + ``` + + This includes support for declaring signatures that use both variadic and + keyword-only arguments/parameters. For example, the following is now possible: + + ```mojo + fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: + var res = 1 + for i in range(len(args)): + res *= args[i] + return res + offset + + print(prod_with_offset(2, 3, 4, 10)) # prints 240 + print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 + ``` + + Note that variadic keyword-only arguments/parameters (for example, `**kwargs`) + are not supported yet. That is, the following is not allowed: + + ```mojo + fn variadic_kw_only(a: Int, **kwargs): ... + ``` + + For more information, see + [Positional-only and keyword-only arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments) + in the Mojo Manual. + +- The `print()` function now accepts a keyword-only argument for the `end` + which is useful for controlling whether a newline is printed or not + after printing the elements. By default, `end` defaults to "\n" as before. + +- The Mojo SDK can now be installed on AWS Graviton instances. + +- A new version of the [Mojo Playground](/mojo/playground) is available. The new + playground is a simple interactive editor for Mojo code, similar to the Rust + Playground or Go Playground. The old + [JupyterLab based playground](https://playground.modular.com) will remain + online until March 20th. + +- The Mojo LSP server will now generate fixits for populating empty + documentation strings: + + ```mojo + fn foo(arg: Int): + """""" # Unexpected empty documentation string + ``` + + Applying the fixit from above will generate: + + ```mojo + fn foo(arg: Int): + """[summary]. + + Args: + arg: [description]. + """ + ``` + +- Added new `*_` syntax that allows users to explicitly unbind any number of + positional parameters. For example: + + ```mojo + struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass + + alias all_unbound = StructWithDefault[*_] + # equivalent to + alias all_unbound = StructWithDefault[_, _, _, _] + + alias first_bound = StructWithDefault[5, *_] + # equivalent to + alias first_bound = StructWithDefault[5, _, _, _] + + alias last_bound = StructWithDefault[*_, 6] + # equivalent to + alias last_bound = StructWithDefault[_, _, _, 6] + + alias mid_unbound = StructWithDefault[3, *_, 4] + # equivalent to + alias mid_unbound = StructWithDefault[3, _, _, 4] + ``` + + As demonstrated above, this syntax can be used to explicitly unbind an + arbitrary number of parameters, at the beginning, at the end, or in the + middle of the operand list. Since these unbound parameters must be explicitly + specified at some point, default values for these parameters are not applied. + For example: + + ```mojo + alias last_bound = StructWithDefault[*_, 6] + # When using last_bound, you must specify a, b, and c. last_bound + # doesn't have a default value for `c`. + var s = last_bound[1, 2, 3]() + ``` + + For more information see the Mojo Manual sections on + [partially-bound types](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types) + and + [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). + +- [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) now + supports iteration. Iteration values are instances of + [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: + + ```mojo + var v: DynamicVector[String]() + v.append("Alice") + v.append("Bob") + v.append("Charlie") + for x in v: + x[] = str("Hello, ") + x[] + for x in v: + print(x[]) + ``` + +- `DynamicVector` now has + [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and + [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. + +- The `mojo package` command now produces compilation agnostic packages. + Compilation options such as O0, or --debug-level, are no longer needed or + accepted. As a result, packages are now smaller, and extremely portable. + +- Initializers for `@register_passable` values can (and should!) now be + specified with `inout self` arguments just like memory-only types: + + ```mojo + @register_passable + struct YourPair: + var a: Int + var b: Int + fn __init__(inout self): + self.a = 42 + self.b = 17 + fn __copyinit__(inout self, existing: Self): + self.a = existing.a + self.b = existing.b + ``` + + This form makes the language more consistent, more similar to Python, and + easier to implement advanced features for. There is also no performance + impact of using this new form: the compiler arranges to automatically return + the value in a register without requiring you to worry about it. + + The older `-> Self:` syntax is still supported in this release, but will be + removed in a subsequent one, so please migrate your code. One thing to watch + out for: a given struct should use one style or the other, mixing some of + each won't work well. + +- The `inout self` initializer form is **required** for initializers of + `@register_passable` types that may raise errors: + + ```mojo + @register_passable + struct RaisingCtor: + fn __init__(inout self) raises: + raise + ``` + +- `async` functions that may raise errors have been temporarily disabled in this + build. The implementation of Mojo async is undergoing a rework 🚧. + +- The standard library `slice` type has been renamed to + [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` + function has been introduced. This makes Mojo closer to Python and makes the + `Slice` type follow the naming conventions of other types like `Int`. + +- "Slice" syntax in subscripts is no longer hard coded to the builtin `slice` + type: it now works with any type accepted by a container's `__getitem__()` + method. For example: + + ```mojo + @value + struct UnusualSlice: + var a: Int + var b: Float64 + var c: String + + struct YourContainer: + fn __getitem__(self, slice: UnusualSlice) -> T: ... + ``` + + Given this implementation, you can subscript into an instance of + `YourContainer` like `yc[42:3.14:"🔥"]` and the three values are passed to the + `UnusualSlice` constructor. + +- The `__refitem__()` accessor method may now return a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to + return an MLIR internal reference type. + +- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) + method, for moving a value from one pointer memory location to another. + +- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be + used to format any value whose type implements the + [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. + +- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements + `__is__` and `__isnot__` so that you can use expressions of the form `x is y` + and `x is not y` with `PythonObject`. + +- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the + `SizedRaising` trait. This means the built-in + [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. + +- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) + and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. + +- A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query + properties on paths. + +- The `os` package now has a + [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms + to the `PathLike` trait by implementing the `__fspath__()` function. + +- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to + query properties of the path. + +- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on + [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` + module to work on `PathLike` structs. For example, the following sample + lists all the directories in the `/tmp` directory: + + ```mojo + from pathlib import Path + + fn walktree(top: Path, inout files: DynamicVector[Path]): + try: + var ls = top.listdir() + for i in range(len(ls)): + var child = top / ls[i] + if child.is_dir(): + walktree(child, files) + elif child.is_file(): + files.append(child) + else: + print("Skipping '" + str(child) + "'") + except: + return + + fn main(): + var files = DynamicVector[Path]() + + walktree(Path("/tmp"), files) + + for i in range(len(files)): + print(files[i]) + ``` + +- The [`find()`](/mojo/stdlib/builtin/string_literal#find), + [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), + [`count()`](/mojo/stdlib/builtin/string_literal#count), and + [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods + now work on string literals. This means that you can write: + + ```mojo + if "Mojo" in "Hello Mojo": + ... + ``` + +- Breakpoints can now be inserted programmatically within the code using the + builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. + + Note: on Graviton instances, the debugger might not be able to resume after + hitting this kind of breakpoint. + +- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that + describes a type that can be represented as a boolean value. To conform to the + trait, a type must implement the `__bool__()` method. + +- Modules within packages can now use purely relative `from` imports: + + ```mojo + from . import another_module + ``` + +- Trivial types, like MLIR types and function types, can now be bound implicitly + to traits that require copy constructors or move constructors, such as + [`Movable`](/mojo/stdlib/builtin/value.html#movable), + [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). + +- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory + value. We hope and expect that this will eventually be replaced by + `Reference(expr).lifetime` as the parameter system evolves, but this is + important in the meantime for use in function signatures. + +- A new magic `__type_of(expr)` call will yield the type of a value. This allows + one to refer to types of other variables. For example: + + ```mojo + fn my_function(x: Int, y: __type_of(x)) -> Int: + let z : __type_of(x) = y + return z + ``` + +### 🦋 Changed + +- As another step towards [removing let + declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md) + we have removed support for let declarations inside the compiler. To ease + migration, we parse `let` declarations as a `var` declaration so your code + won't break. We emit a warning about this, but please switch your code to + using `var` explicitly, because this migration support will be removed in a + subsequent update. + + ```mojo + fn test(): + # treated as a var, but please update your code! + let x = 42 # warning: 'let' is being removed, please use 'var' instead + x = 9 + ``` + +- It is no longer possible to explicitly specify implicit argument parameters in + [automatically parameterized + functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). + This ability was an oversight and this is now an error: + + ```mojo + fn autoparameterized(x: SIMD): + pass + + autoparameterized[DType.int32, 1](3) # error: too many parameters + ``` + +- `vectorize_unroll` has been removed, and + [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter + named `unroll_factor` with a default value of 1. Increasing `unroll_factor` + may improve performance at the cost of binary size. See the + [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) + for more details. + +- The `vectorize` signatures have changed with the closure `func` moved to the + first parameter: + + ```mojo + vectorize[func, width, unroll_factor = 1](size) + vectorize[func, width, size, unroll_factor = 1]() + ``` + + The doc string has been updated with examples demonstrating the difference + between the two signatures. + +- The `unroll` signatures have changed with the closure `func` moved to the + first parameter: + + ```mojo + unroll[func, unroll_count]() + ``` + +- The signature of the [`NDBuffer`](/mojo/stdlib/memory/buffer#ndbuffer) and + [`Buffer`](/mojo/stdlib/memory/buffer#buffer) types have changed. Now, both + take the type as the first parameter and no longer require the shape + parameter. This allows you to use these types and have sensible defaults. + For example: + + ```mojo + NDBuffer[DType.float32, 3] + ``` + + is equivalent to + + ```mojo + NDBuffer[DType.float32, 3, DimList.create_unknown[3]()] + ``` + + Users can still specify the static shape (if known) to the type: + + ```mojo + NDBuffer[DType.float32, 3, DimList(128, 128, 3)] + ``` + +- The error message for missing function arguments is improved: instead of + describing the number of arguments (e.g. `callee expects at least 3 arguments, + but 1 was specified`) the missing arguments are now described by + name (e.g. `missing 2 required positional arguments: 'b', 'c'`). + +- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait + is now a built-in trait and has been removed from `collections.vector`. + +- The `DynamicVector(capacity: Int)` constructor has been changed to take + `capacity` as a keyword-only argument to prevent implicit conversion from + `Int`. + +- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a + copy. + +- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` + and `toupper()` have been renamed to `str.lower()` and `str.upper()`. + +- The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. + We originally thought about using those as language sugar for references, but + we believe that generic language features combined with the + [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good + experience without dedicated sugar. + ### 🛠️ Fixed + +- [#435](https://github.com/modularml/mojo/issues/435) + Structs with Self type don't always work. +- [#1540](https://github.com/modularml/mojo/issues/1540) + Crash in register_passable self referencing struct. +- [#1664](https://github.com/modularml/mojo/issues/1664) - Improve error + message when `StaticTuple` is constructed with a negative size for + the number of elements. +- [#1679](https://github.com/modularml/mojo/issues/1679) - crash on SIMD of zero + elements. +- Various crashes on invalid code: + [#1230](https://github.com/modularml/mojo/issues/1230), + [#1699](https://github.com/modularml/mojo/issues/1699), + [#1708](https://github.com/modularml/mojo/issues/1708) +- [#1223](https://github.com/modularml/mojo/issues/1223) - Crash when parametric + function is passed as (runtime) argument. The parser now errors out instead. +- [#1530](https://github.com/modularml/mojo/issues/1530) - Crash during + diagnostic emission for parameter deduction failure. +- [#1538](https://github.com/modularml/mojo/issues/1538) and [#1607]( + https://github.com/modularml/mojo/issues/1607) - Crash when returning type + value instead of instance of expected type. This is a common mistake and the + error now includes a hint to point users to the problem. +- [#1613](https://github.com/modularml/mojo/issues/1613) - Wrong type name in + error for incorrect `self` argument type in trait method declaration. +- [#1670](https://github.com/modularml/mojo/issues/1670) - Crash on implicit + conversion in a global variable declaration. +- [#1741](https://github.com/modularml/mojo/issues/1741) - Mojo documentation + generation doesn't show `inout`/`owned` on variadic arguments. +- [#1621](https://github.com/modularml/mojo/issues/1621) - VS Code does not + highlight `raises` and `capturing` in functional type expressions. +- [#1617](https://github.com/modularml/mojo/issues/1617) - VS Code does not + highlight `fn` in specific contexts. +- [#1740](https://github.com/modularml/mojo/issues/1740) - LSP shows unrelated + info when hovering over a struct. +- [#1238](https://github.com/modularml/mojo/issues/1238) - File shadows Mojo + package path. +- [#1429](https://github.com/modularml/mojo/issues/1429) - Crash when using + nested import statement. +- [#1322](https://github.com/modularml/mojo/issues/1322) - Crash when missing + types in variadic argument. +- [#1314](https://github.com/modularml/mojo/issues/1314) - Typecheck error when + binding alias to parametric function with default argument. +- [#1248](https://github.com/modularml/mojo/issues/1248) - Crash when importing + from file the same name as another file in the search path. +- [#1354](https://github.com/modularml/mojo/issues/1354) - Crash when importing + from local package. +- [#1488](https://github.com/modularml/mojo/issues/1488) - Crash when setting + generic element field. +- [#1476](https://github.com/modularml/mojo/issues/1476) - Crash in interpreter + when calling functions in parameter context. +- [#1537](https://github.com/modularml/mojo/issues/1537) - Crash when copying + parameter value. +- [#1546](https://github.com/modularml/mojo/issues/1546) - Modify nested vector + element crashes parser. +- [#1558](https://github.com/modularml/mojo/issues/1558) - Invalid import causes + parser to crash. +- [#1562](https://github.com/modularml/mojo/issues/1562) - Crash when calling + parametric type member function. +- [#1577](https://github.com/modularml/mojo/issues/1577) - Crash when using + unresolved package as a variable. +- [#1579](https://github.com/modularml/mojo/issues/1579) - Member access into + type instances causes a crash. +- [#1602](https://github.com/modularml/mojo/issues/1602) - Interpreter failure + when constructing strings at compile time. +- [#1696](https://github.com/modularml/mojo/issues/1696) - Fixed an issue that + caused syntax highlighting to occasionally fail. +- [#1549](https://github.com/modularml/mojo/issues/1549) - Fixed an issue when + the shift amount is out of range in `SIMD.shift_left` and `SIMD.shift_right`. + +## v0.7.0 (2024-01-25) + +### ⭐️ New + +- A new Mojo-native dictionary type, + [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. + `Dict` stores values that conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + trait. Keys need to conform to the new + [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is + not yet implemented by other standard library types. In the short term, you + can create your own wrapper types to use as keys. For example, the following + sample defines a `StringKey` type and uses it to create a dictionary that maps + strings to `Int` values: + + ```mojo + from collections.dict import Dict, KeyElement + + @value + struct StringKey(KeyElement): + var s: String + + fn __init__(inout self, owned s: String): + self.s = s ^ + + fn __init__(inout self, s: StringLiteral): + self.s = String(s) + + fn __hash__(self) -> Int: + return hash(self.s) + + fn __eq__(self, other: Self) -> Bool: + return self.s == other.s + + fn main() raises: + var d = Dict[StringKey, Int]() + d["cats"] = 1 + d["dogs"] = 2 + print(len(d)) # prints 2 + print(d["cats"]) # prints 1 + print(d.pop("dogs")) # prints 2 + print(len(d)) # prints 1 + ``` + + We plan to add `KeyElement` conformance to standard library types in + subsequent releases. + +- Users can opt-in to assertions used in the standard library code by + specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to + compile your source file(s). In the case that an assertion is fired, + the assertion message will be printed along with the stack trace + before the program exits. By default, assertions are _not enabled_ + in the standard library right now for performance reasons. + +- The Mojo Language Server now implements the References request. IDEs use + this to provide support for **Go to References** and **Find All References**. + A current limitation is that references outside of the current document are + not supported, which will be addressed in the future. + +- The [`sys.info`](/mojo/stdlib/sys/info) module now includes + `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` + functions. + +- Homogenous variadic arguments consisting of memory-only types, such as + `String` are more powerful and easier to use. These arguments are projected + into a + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). + + (Previous releases made it easier to use variadic lists of register-passable + types, like `Int`.) + + Subscripting into a `VariadicListMem` now returns the element instead of an + obscure internal type. In addition, we now support `inout` and `owned` + variadic arguments: + + ```mojo + fn make_worldly(inout *strs: String): + # This "just works" as you'd expect! + for i in range(len(strs)): + strs[i] += " world" + fn main(): + var s1: String = "hello" + var s2: String = "konnichiwa" + var s3: String = "bonjour" + make_worldly(s1, s2, s3) + print(s1) # hello world + print(s2) # konnichiwa world + print(s3) # bonjour world + ``` + + (Previous releases made it easier to use variadic lists, but subscripting into + a `VariadicListMem` returned a low-level pointer, which required the user to + call `__get_address_as_lvalue()` to access the element.) + + Note that subscripting the variadic list works nicely as above, but + iterating over the variadic list directly with a `for` loop produces a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead + of the desired value, so an extra subscript is required; We intend to fix this + in the future. + + ```mojo + fn make_worldly(inout *strs: String): + # Requires extra [] to dereference the reference for now. + for i in strs: + i[] += " world" + ``` + + Heterogenous variadic arguments have not yet been moved to the new model, but + will in future updates. + + Note that for variadic arguments of register-passable types like `Int`, the + variadic list contains values, not references, so the dereference operator + (`[]`) is not required. This code continues to work as it did previously: + + ```mojo + fn print_ints(*nums: Int): + for num in nums: + print(num) + print(len(nums)) + ``` + +- Mojo now has a prototype version of a safe + [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's + lifetime tracking pass can reason about references to safely extend local + variable lifetime, and check indirect access safety. The `Reference` type + is brand new (and currently has no syntactic sugar) so it must be explicitly + dereferenced with an empty subscript: `ref[]` provides access to the + underlying value. + + ```mojo + fn main(): + var a : String = "hello" + var b : String = " references" + + var aref = Reference(a) + aref[] += b + print(a) # prints "hello references" + + aref[] += b + # ^last use of b, it is destroyed here. + + print(aref[]) # prints "hello references references" + # ^last use of a, it is destroyed here. + ``` + + While the `Reference` type has the same in-memory representation as a C + pointer or the Mojo `Pointer` type, it also tracks a symbolic "lifetime" value + so the compiler can reason about the potentially accessed set of values. This + lifetime is part of the static type of the reference, so it propagates through + generic algorithms and abstractions built around it. + + The `Reference` type can form references to both mutable and immutable memory + objects, e.g. those on the stack or borrowed/inout/owned function arguments. + It is fully parametric over mutability, eliminating the [problems with code + duplication due to mutability + specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and + provides the base for unified user-level types. For example, it could be + used to implement an array slice object that handles both mutable and immutable + array slices. + + While this is a major step forward for the lifetimes system in Mojo, it is + still _very_ early and awkward to use. Notably, there is no syntactic sugar + for using references, such as automatic dereferencing. Several aspects of it + need to be more baked. It is getting exercised by variadic memory arguments, + which is why they are starting to behave better now. + + Note: the safe `Reference` type and the unsafe pointer types are defined in + the same module, currently named `memory.unsafe`. We expect to restructure + this module in a future release. + +- Mojo now allows types to implement `__refattr__()` and `__refitem__()` to + enable attribute and subscript syntax with computed accessors that return + references. For common situations where these address a value in memory this + provides a more convenient and significantly more performant alternative to + implementing the traditional get/set pairs. Note: this may be changed in the + future when references auto-dereference—at that point we may switch to just + returning a reference from `__getattr__()`. +- Parametric closures can now capture register passable typed values by copy + using the `__copy_capture` decorator. For example, the following code will + print `5`, not `2`. + + ```mojo + fn foo(x: Int): + var z = x + + @__copy_capture(z) + @parameter + fn formatter() -> Int: + return z + z = 2 + print(formatter()) + + fn main(): + foo(5) + ``` + +- String now implements KeyElement and may be used as a key in Dict. +- More robust support for structs with fields of self referencing types. + For example, the following code will work and print `0`: + + ```mojo + struct Foo(CollectionElement): + var vec: DynamicVector[Self] + + fn __init__(inout self: Self): + self.vec = DynamicVector[Self]() + + fn __moveinit__(inout self: Self, owned existing: Self): + self.vec = existing.vec ^ + + fn __copyinit__(inout self: Self, existing: Self): + self.vec = existing.vec + + + fn main(): + var foo = Foo() + print(len(foo.vec)) + ``` + +### ❌ Removed + +- The `__takeinit__` special constructor form has been removed from the + language. This "non-destructive move" operation was previously wired into the + `x^` transfer operator, but had unpredictable behavior that wasn't consistent. + Now that Mojo has traits, it is better to model this as an explicit `.take()` + operation on a type, which would transfer out the contents of the type without + ending its lifetime. For example, for a type that holds a pointer, `take()` + might return a new instance pointing to the same data, and null out its own + internal pointer. + + This change makes it clear when a lifetime is ended versus when the + contents of an LValue are explicitly taken. + +- The current implementation of autotuning has been deprecated, as Mojo's + autotuning implementation is undergoing a redesign. Tutorials around the + current implementation have also been removed as they are being rewritten. + + Consequently, the `autotune()`, `autotune_fork()`, and `search()` functions + have been removed from the standard library. + +- The `_OldDynamicVector` type that worked only on register passable element + types has been removed. Please migrate uses to + [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) which + works on both register passable and memory types. + +- The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using + either [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) + or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + instead. + +- The `@adaptive` decorator has been removed from the language. Any uses of the + decorator in a non-search context can be replaced with `@parameter if`. For + example: + + ```mojo + @adaptive + fn foo[a: Bool](): + constrained[a]() + body1() + + @adaptive + fn foo[a: Bool](): + constrained[not a]() + body2() + ``` + + Can be rewritten as: + + ```mojo + fn foo[a: Bool](): + @parameter + if a: + body1() + else: + body2() + ``` + + Consequently, the special `__adaptive_set` attribute has been removed as well. + +- Result parameters have been removed from Mojo. Result parameter declarations + in function parameter lists are no longer allowed, nor are forward alias + declarations. This includes removing the `param_return` statement. + +- The `@noncapturing` and `@closure` decorators have been removed due to + refinements and improvements to the closure model. See below for more details! + +### 🦋 Changed + +- The Mojo closure model has been refined to be more straightforward and safe. + Mojo has two closure types: parameter closures and runtime closures. Parameter + closures can be used in higher-order functions and are the backbone of + functions like `vectorize` and `parallelize`. They are always denoted by + `@parameter` and have type `fn() capturing -> T` (where `T` is the return + type). + + On the other hand, runtime closures are always dynamic values, capture values + by invoking their copy constructor, and retain ownership of their capture + state. You can define a runtime closure by writing a nested function that + captures values: + + ```mojo + fn outer(b: Bool, x: String) -> fn() escaping -> None: + fn closure(): + print(x) # 'x' is captured by calling String.__copyinit__ + + fn bare_function(): + print("hello") # nothing is captured + + if b: + # closure can be safely returned because it owns its state + return closure^ + + # function pointers can be converted to runtime closures + return bare_function + ``` + + The type of runtime closures are of the form `fn() escaping -> T`. You + can pass equivalent function pointers as runtime closures. + + Stay tuned for capture list syntax for move capture and capture by reference, + and a more unified closure model! + +- The `@unroll(n)` decorator can now take a parameter expression for + the unroll factor, i.e. `n` can be a parameter expression that is + of integer type. + +- The `cpython` module in the `python` package has been moved to be an internal + module, i.e, `_cpython`. + +- `AnyType` and `Destructable` have been unified into a single trait, `AnyType`. + Every nominal type (i.e. all structs) now automatically conform to `AnyType`. + +- Previously, the `mojo package` command would output a Mojo package that + included both partly-compiled Mojo code, as well as fully-compiled machine + code for a specific computer architecture -- the architecture of the machine + being used to invoke the `mojo package` command. + + Now, `mojo package` only includes partly-compiled Mojo code. It is only fully + compiled for the specific computer architecture being used at the point that + the package is first `import`-ed. As a result, Mojo packages are smaller and + more portable. + +- The `simd_width` and `dtype` parameters of `polynomial_evaluate` have been + switched. Based on the request in + [#1587](https://github.com/modularml/mojo/issues/1587), the + `polynomial_evaluate` function has also been extended so that the + `coefficients` parameter can take either a either a + [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a + [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). + +- As a tiny step towards removing `let` declarations, this release removes the + warning: `'var' was never mutated, consider switching to a 'let'`. + +### 🛠️ Fixed + +- [#1595](https://github.com/modularml/mojo/issues/1595) - Improve error message + when trying to materialize `IntLiteral` in runtime code. +- Raising an error from the initializer of a memory-only type now works + correctly in the presence of complex control flow. Previously Mojo could run + the destructor on `self` before it was initialized when exiting with an + error. +- [#1096](https://github.com/modularml/mojo/issues/1096) - Improve warning + messages for dead code in conditionals like `or` expressions. +- [#1419](https://github.com/modularml/mojo/issues/1419) - Fix assertion failure + with uninitialized lattice values. +- [#1402](https://github.com/modularml/mojo/issues/1402) - Fix movable trait not + detected on recursive struct implemented with `AnyPointer`. +- [#1399](https://github.com/modularml/mojo/issues/1399) - Fix parser crash when + a parameter type in a struct that implements a trait is misspelled. +- [#1152](https://github.com/modularml/mojo/issues/1152) - Allow mutable `self` + argument when overloading operators using dunder methods. +- [#1493](https://github.com/modularml/mojo/issues/1493) - Fix crash in + `DynamicVector` copy constructor in certain situations. +- [#1316](https://github.com/modularml/mojo/issues/1316) - The `benchmark.keep` + function now properly handles vector types. +- [#1505](https://github.com/modularml/mojo/issues/1505) - The `simd.shuffle` + operation now works on 64 element permutations. +- [#1355](https://github.com/modularml/mojo/issues/1355) - Fix `String.find()` + returning wrong value when starting index is non-zero. +- [#1367](https://github.com/modularml/mojo/issues/1367) - Fix `String.replace()` + returning incorrect results for multi-character search strings. +- [#1535](https://github.com/modularml/mojo/issues/1535) - Invalid error `field + 'w.x.y' destroyed out of the middle of a value, preventing the overall value + from being destroyed`. +- [#1475](https://github.com/modularml/mojo/issues/1475) - Assertion failure in + nested loop. +- [#1591](https://github.com/modularml/mojo/issues/1591) - Assertion failure + when using `AnyType` struct member. +- [#1503](https://github.com/modularml/mojo/issues/1503) - Rename the mojo build + of LLDB to `mojo-lldb`, to prevent name collisions with the system's LLDB. +- [#1542](https://github.com/modularml/mojo/issues/1542) - `@unroll` does not + accept alias as unroll factor. +- [#1443](https://github.com/modularml/mojo/issues/1443) - Compiler crash on + variadic list of traits. +- [#1604](https://github.com/modularml/mojo/issues/1604) - Variable of trivial + type not destroyed by transferring ownership. +- [#1341](https://github.com/modularml/mojo/issues/1341) - Segmentation fault + when passing closures around. +- [#217](https://github.com/modularml/mojo/issues/217) - Closure state is + stack allocated. + +## v0.6.1 (2023-12-18) + +### ⭐️ New + +- The Mojo REPL now provides limited support for the `%cd` magic command. + + This command automatically maintains an internal stack of directories you + visit during the REPL session. Usage: + + - `%cd 'dir'`: change to directory `dir` and push it on the directory stack. + - `%cd -`: pop the directory stack and change to the last visited directory. + +- Structs decorated with `@value` now automatically conform to the + [`Movable`](/mojo/stdlib/builtin/value.html#movable) + and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. + +- [`String`](/mojo/stdlib/builtin/string.html#string) now has new + [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and + [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, + respectively, to Python's `str.toupper()` and `str.tolower()`. + +- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and + [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types + implementing the `__hash__()` method. Future releases will add `Hashable` + support to Standard Library types. In the meantime, the `hash` module includes + a version of the `hash()` function that works on arbitrary byte strings. To + generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you + use the internal `_hash_simd()` function: + + ```mojo + from builtin.hash import _hash_simd + + fn gen_simd_hash(): + let vector = SIMD[DType.int64, 4](1, 2, 3, 4) + let hash = _hash_simd(vector) + ``` + +- Several standard library types now conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), + [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), + [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector), + [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), + [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), + and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). + +### 🦋 Changed + +- `utils.vector` has been moved to a new `collections` package to make + space for new collections. This means that if you had previous code + that did `from utils.vector import DynamicVector`, it now needs to + be `from collections.vector import DynamicVector` due to the move. + +- The special destructor method `__del__()` has been changed to enforce + that it cannot raise an error. Raising destructors are not supported properly + at the moment. + +### 🛠️ Fixed + +- [#1421](https://github.com/modularml/mojo/issues/1421) - Fixed a crash when + using Tuples in the REPL. + +- [#222](https://github.com/modularml/mojo/issues/222) - Generate an error + for obviously self recursive functions. + +- [#1408](https://github.com/modularml/mojo/issues/1408) - Fix overload + resolution when candidates can return generic types. + +- [#1413](https://github.com/modularml/mojo/issues/1413) and + [#1395](https://github.com/modularml/mojo/issues/1395) - Do not crash when + re-declaring a builtin declaration. + +- [#1307](https://github.com/modularml/mojo/issues/1307) - Fix compatibility of + function signatures that only differ in default argument values. + +- [#1380](https://github.com/modularml/mojo/issues/1380) - Fix printing + of empty `String`. + +## v0.6.0 (2023-12-04) + +### 🔥 Legendary + +- Traits have arrived! + + You can now define a _trait_, which consists of a required set of method + prototypes. A struct can _conform to_ the trait by implementing these methods. + This lets you write generic functions that work on any structs that conform to + a given trait. + + The following section gives a brief overview of traits—see the + [Mojo Manual](/mojo/manual/traits.html) and this + [traits blog post](https://modul.ar/traits-blog) for more details! + + Traits are declared with the `trait` keyword. The bodies of traits should + contain method signatures declared with `...` as their bodies. Default + method implementations are not supported yet. + + ```mojo + trait SomeTrait: + fn required_method(self, x: Int): ... + ``` + + The trait can be implemented on a struct by inheriting from it. + + ```mojo + struct SomeStruct(SomeTrait): + fn required_method(self, x: Int): + print("hello traits", x) + ``` + + You can then write a generic functions that accepts any type that conforms to + the trait. You do this by creating a parameterized function with a + trait-typed parameter: + + ```mojo + fn fun_with_traits[T: SomeTrait](x: T): + x.required_method(42) + ``` + + Which can be invoked with instances of types that conform to the trait: + + ```mojo + var thing = SomeStruct() + # Infer the parameter `T`! + fun_with_traits(thing) + ``` + + Traits can also inherit from other traits, which simply requires that + implementors of the child trait also conform to all parent traits. + + ```mojo + trait Parent: + fn parent_func(self): ... + + trait Child(Parent): + fn child_func(self): ... + ``` + + Then, both child and parent trait methods can be invoked on instances of + the trait `Child`. As well, an instance of the child trait can be converted to + an instance of the parent trait. + + ```mojo + fn the_parents[T: Parent](x: T): + x.parent_func() + + fn the_children[T: Child](x: T): + x.child_func() + x.parent_func() + # Upcast `x` from instance of `Child` to `Parent`. + the_parents(x) + ``` + + For more information, see the [Traits page](/mojo/manual/traits.html) + in the Mojo Manual. + +- A fundamental `Destructable` trait has been added to the language. This is a + core trait that every trait automatically conforms to. This enables + destruction of generic types and generic collections. + + **Note:** We're aware that this trait might be better spelled `Destructible`. + We're planning on removing it in the future and moving its functionality to + `AnyType` so that any type that doesn't provide its own destructor will have + a default, no-op destructor. + +- We've added some traits to the standard library, you can implement these on + your own types: + + - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) + - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) + - [`Movable`](/mojo/stdlib/builtin/value.html#movable) + - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) + - [`Intable`](/mojo/stdlib/builtin/int.html#intable) + - [`Sized`](/mojo/stdlib/builtin/len.html#sized) + - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + +- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), + [`str()`](/mojo/stdlib/builtin/str.html#str), and + [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with + types that implement the `Sized`, `Stringable`, and `Intable` traits, + respectively. + +- [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) is now a + proper generic collection that can use any type that implements the `Movable` + and `Copyable` traits. This means you can now write, for example, + `DynamicVector[String]`. Also, `DynamicVector` now invokes its element + destructors upon destruction, so `_del_old` has been deleted. + +- `print` now works on any types that implement `Stringable` by invoking their + `__str__` method: + + ```mojo + @value + struct BoxedInt(Stringable): + var value: Int + + fn __str__(self) -> String: + return self.value + + print(BoxedInt(11), "hello traits!", BoxedInt(42)) + ``` + +### ⭐️ New + +- The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. + It doesn't include _everything_ about Mojo yet, but it includes a lot, + and more than the original [programming + manual](/mojo/programming-manual.html) (now deprecated). + + Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on + GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love + to accept contributions to help us improve them! + +- Mojo now supports partial automatic parameterization: when a function is + declared with an argument of a partially bound type, the unbound parameters + of that type are implicitly added to the function's input parameters. For + example: + + ```mojo + @value + struct Fudge[a: Int, b: Int, c: Int = 7]: ... + + # These function declarations are roughly equivalent: + fn eat(f: Fudge[5]): ... # implicitly parameterized + fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized + ``` + + In the first signature for `eat()`, the `b` parameter isn't bound, so it's + _implicitly_ added as an input parameter on the function. + + In the second signature for `eat()`, the author has explicitly defined an + input parameter (`_b`), which is bound to the second parameter on the argument + type (which happens to be `b`). + + Both functions can be called like this: + + ```mojo + eat(Fudge[5, 8]()) + ``` + + Mojo infers the value of the `b` parameter from the argument (in this case, + 8). + + With the second signature, you can also pass the `_b` parameter value + explicitly: + + ```mojo + eat[3](Fudge[5, 3]()) + ``` + + Moreover, Mojo now allows you to explicitly mark parameters as unbound using + the `_` as syntax meaning "placeholder for an unbound parameter." For example: + + ```mojo + # These function declarations are roughly equivalent: + fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized + fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized + fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized + ``` + + The first two signatures explicitly unbind the `b` and `c` parameters. + + In the last signature, the `_b` and `_c` parameters are explicitly declared by + the author, and bound to the `b` and `c` parameters in the argument type. + + Any of these signatures can be called like this: + + ```mojo + eat(Fudge[5, 8]()) + eat(Fudge[5, 8, 9]()) + ``` + + Note that the default parameter values of struct parameters are bound, unless + explicitly unbound by the user. + + For more information, see the + [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). + +- Parametric types can now be partially bound in certain contexts. For example, + a new `Scalar` type alias has been added defined as: + + ```mojo + alias Scalar = SIMD[size=1] + ``` + + Which creates a parametric type alias `Scalar` with a single parameter of type + `DType`. Types can also be partially or fully bound in other contexts. For + instance, `alias` declarations of type values inside functions now work + properly: + + ```mojo + fn type_aliases(): + alias T = SIMD + print(T[DType.float32, 1]()) + alias Partial = T[type=DType.int32] + print(Partial[2]()) + ``` + +- The `__mlir_op` feature now supports operations that return multiple results. + To use them, you write the `_type` field as a `Tuple` of types. For example: + + ```mojo + # The `ret` variable has type `Tuple[Int, Int]`. + let ret = __mlir_op.`multi_result_op`[ _type = (Int, Int) ]() + ``` + +- Mojo now has the ability to read raw bytes from a file using the + [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. + For example: + + ```mojo + with open("file.binary", "r") as f: + data = f.read_bytes() + ``` + +- A size argument was added to the + [`read()`](/mojo/stdlib/builtin/file.html#read) and + [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the + builtin `file.FileHandle`. The size argument defaults to -1 and maintains the + previous "read to EOF" behavior when size is negative. + + ```mojo + with open("file.binary", "r") as f: + data1 = f.read_bytes(1024) + data2 = f.read_bytes(256) + ``` + +- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and + `read_text()` methods to read file contents from a path: + + ```mojo + let text_path = Path("file.txt") + let text = text_path.read_text() + + let binary_path = Path("file.binary") + let data = binary_path.read_bytes() + ``` + +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` + methods to save and load to file. These + methods preserve shape and datatype information. For example: + + ```mojo + let tensor = Tensor[DType.float32]() + tensor.save(path) + + let tensor_from_file = Tensor[DType.float32].load(path) + ``` + +- Subscripting added to + [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and + [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): + + ```mojo + let p = DTypePointer[DType.float16].alloc(4) + for i in range(4): + p[i] = i + print(p[i]) + ``` + +- `file.FileHandle` now has a `seek()` method. + +- [`String`](/mojo/stdlib/builtin/string.html#string) now has an + [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to + Python's `str.rfind()`. + +- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method + analogous to Python's `str.split()`. + +- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a + [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to + Python's `pathlib.Path.suffix`. + +- The Mojo REPL now supports indented expressions, making it a bit easier to + execute expressions copied from an indented block (such as a doc string). + +- The Mojo Language Server now implements the Document Symbols request. IDEs use + this to provide support for **Outline View** and **Go to Symbol**. This + addresses [Issue #960](https://github.com/modularml/mojo/issues/960). + +- The Mojo Language Server now shows documentation when code completing modules + or packages in `import` statements. + +- The Mojo Language Server now supports processing code examples, defined as + markdown Mojo code blocks, inside of doc strings. This enables IDE features + while writing examples in API documentation. + +- The Mojo Language Server now provides semantic token information, providing + better highlighting for symbols whose semantics are not statically analyzable. + +- The Mojo Language Server now classifies doc strings as folding ranges, + making them easier to collapse, reducing vertical space while editing. + +- Command line options for the `mojo` driver that take arguments can now be + written in either of two ways: both `--foo FOO` and `--foo=FOO`. Previously, + only the former was valid. + +### 🦋 Changed + +- Variadic list types + [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + are now iterable. Variadic arguments are automatically projected into one of + these types inside the function body, so var args can be iterated: + + ```mojo + fn print_ints(*nums: Int): + for num in nums: + print(num) + print(len(nums)) + ``` + +- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) + package now raise an `Error` when the assertion fails instead of returning a + `Bool` for whether the assertion succeeded or not. + +- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no + longer (implicitly) assumed to be register-passable. A new `AnyRegType` type + is used to represent generic types that are register passable. + +- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) + report is now an argument instead of a parameter: + + ```mojo + let report = benchmark.run[timer]() + report.print(Unit.ms) + ``` + +- Default values on `inout` arguments are no longer permitted, i.e. the + following will now raise an error: + + ```mojo + fn inout_default(inout x: Int = 2): ... + ``` + +- The `to_string()` function has been removed from + [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of + the new `__str__()` function. This composes better with traits so it can be + used with the generic `str()` function. + +### 🛠️ Fixed + +- [#734](https://github.com/modularml/mojo/issues/734) - Consumption of struct + works only for types with a `__del__` method. + +- [#910](https://github.com/modularml/mojo/issues/910) - Parser crash when + using memory-only generic type as return of function that `raise`s. + +- [#1060](https://github.com/modularml/mojo/issues/1060) - Mojo happily parses + code that has messed up indentation + +- [#1159](https://github.com/modularml/mojo/issues/1159) - The language server + doesn't warn about bad return type. + +- [#1166](https://github.com/modularml/mojo/issues/1166) - warning: unreachable + code after return statement with context manager + +- [#1098](https://github.com/modularml/mojo/issues/1098) - The language server + doesn't highlight properties of PythonObjects correctly. + +- [#1153](https://github.com/modularml/mojo/issues/1153) - The language server + crashes when parsing an invalid multi-nested module import. + +- [#1236](https://github.com/modularml/mojo/issues/1236) - The language server + doesn't show autocomplete in if statements. + +- [#1246](https://github.com/modularml/mojo/issues/1246) - Warning diagnostics + are transient in the presence of caching. + +### Known Issue + +- There is an issue affecting Jupyter notebooks that use autotuning and traits. + This issue only manifests on macOS, and the same code runs without issue + outside of the notebooks. This issue affects the _Matrix multiplication in + Mojo_ notebook. + +## v0.5.0 (2023-11-2) + +### ⭐️ New + +- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the + architectural SIMD width of the type. This means you can write + `SIMD[DType.float32]` which is equivalent to + `SIMD[DType.float32, simdwidthof[DType.float32]()]`. + +- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` + function that allows you to concatenate two `SIMD` values together and produce + a new `SIMD` value. + +- Mojo now supports compile-time _keyword parameters_, in addition to existing + support for [keyword + arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For + example: + + ```mojo + fn foo[a: Int, b: Int = 42](): + print(a, "+", b) + + foo[a=5]() # prints '5 + 42' + foo[a=7, b=13]() # prints '7 + 13' + foo[b=20, a=6]() # prints '6 + 20' + ``` + + Keyword parameters are also supported in structs: + + ```mojo + struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]: + fn __init__(inout self): + print(msg, a) + + fn use_kw_params(): + KwParamStruct[a=42]() # prints '🔥mojo🔥 42' + KwParamStruct[5, msg="hello"]() # prints 'hello 5' + KwParamStruct[msg="hello", a=42]() # prints 'hello 42' + ``` + + For more detail, see the [programming + manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). + + For the time being, the following notable limitations apply: + + - Keyword-only parameters are **not supported** yet: + + ```mojo + fn baz[*args: Int, b: Int](): pass # fails + fn baz[a: Int, *, b: Int](): pass # fails + ``` + + (The analogous keyword-only arguments in Python are described in + [PEP 3102](https://peps.python.org/pep-3102/).) + + - Variadic keyword parameters are **not supported** yet: + + ```mojo + fn baz[a: Int, **kwargs: Int](): pass # fails + ``` + +- Mojo now supports "automatic" parameterization of functions. What this means + is that if a function argument type is parametric but has no bound parameters, + they are automatically added as input parameters on the function. This works + with existing features to allow you to write parametric functions with less + boilerplate. + + ```mojo + @value + struct Thing[x: Int, y: Int]: + pass + + fn foo(v: Thing): + print(v.x) + print(v.y) + + fn main(): + let v = Thing[2, 3]() + foo(v) + ``` + + However, partial autoparameterization is **not supported** yet: + + ```mojo + fn foo(v: Thing[y=7]): # Partially bound type not allowed yet. + ... + ``` + +- Keyword argument passing is supported when invoking `__getitem__` using + the bracket syntax: + + ```mojo + @value + struct MyStruct: + fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: + return x * y + z + + MyStruct()[z=7, x=3, y=5] # returns 22 + ``` + + However, keyword argument passing to `__setitem__` using the bracket syntax is + **not supported** yet: + + ```mojo + @value + struct OtherStruct: + fn __setitem__(self, x: Int, y: Int): pass + + OtherStruct()[x=1] = 4 # fails + ``` + +- Function argument input parameters can now be referenced within the signature + of the function: + + ```mojo + fn foo(x: SIMD, y: SIMD[x.type, x.size]): + pass + ``` + +- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been + simplified and improved so you can now run: + + ```mojo + import benchmark + from time import sleep + + fn sleeper(): + sleep(.01) + + fn main(): + let report = benchmark.run[sleeper]() + print(report.mean()) + ``` + + It no longer requires a capturing `fn` so can benchmark functions outside the + same scope. + + You can print a report with: + + ```mojo + report.print() + ``` + + ```plaintext + --------------------- + Benchmark Report (s) + --------------------- + Mean: 0.012314264957264957 + Total: 1.440769 + Iters: 117 + Warmup Mean: 0.0119335 + Warmup Total: 0.023866999999999999 + Warmup Iters: 2 + Fastest Mean: 0.012227958333333334 + Slowest Mean: 0.012442699999999999 + ``` + + Units for all functions default to seconds, but can be changed with: + + ```mojo + from benchmark import Unit + + report.print[Unit.ms]() + ``` + +- Mojo now supports struct parameter deduction (a.k.a. class template argument + deduction, or CTAD) for partially bound types. Struct parameter deduction is + also possible from static methods. For example: + + ```mojo + @value + struct Thing[v: Int]: pass + + struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]: + fn __init__(inout self, x: Thing[a]): + print("hello", a, b, c) + + @staticmethod + fn foo(x: Thing[a]): + print("🔥", a, b, c) + + fn main(): + _ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8' + CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' + ``` + +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and + `tofile()` methods to save and load as bytes from a file. + +- The built-in `print()` function now works on the + [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. + +- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and + [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors + that take [`DynamicVector[Int]`](/mojo/stdlib/collections/vector.html#dynamicvector) + and [`StaticIntTuple`](/mojo/stdlib/utils/index#staticinttuple) to + initialize shapes. + +- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the + `count()` and `find()` methods to enable counting the number of occurrences or + finding the offset index of a substring in a string. + +- The `String` type now has a `replace()` method which allows you to replace a + substring with another string. + +### 🦋 Changed + +- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + moved under builtins, and no longer need to be imported. + +- Variadic arguments are now automatically projected into a `VariadicList` or + `VariadicListMem` inside the function body. This allows for more flexibility + in using var args. For example: + + ```mojo + fn print_ints(*nums: Int): + let len = len(nums) + for i in range(len): + print(nums[i]) + print(len) + ``` + +- The parameters for + [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + have been switched. The parameters are now `[type, size]` instead of + `[size, type]`. The `InlinedFixedVector` now has a default size which means + that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` + and the default size is used. + +- `write_file()` method in [`Buffer`](/mojo/stdlib/memory/buffer.html#buffer) + and [`NDBuffer`](/mojo/stdlib/memory/buffer.html#ndbuffer) is renamed to + `tofile()` to match the Python naming. + +- Mojo will now utilize all available cores across all NUMA sockets on the host + machine by default. The prior default behavior was to use all the cores on + the first socket. + +### ❌ Removed + +- The `math.numerics` module is now private, because its types (`FPUtils` and + `FlushDenormals`) should not be used externally. + +### 🛠️ Fixed + +- [#532](https://github.com/modularml/mojo/issues/532) - Compiler optimizing + while True loop away +- [#760](https://github.com/modularml/mojo/issues/760) - Compilation error: + 'hlcf.for.yield' op specifies 0 branch inputs but target expected 1 along + control-flow edge from here +- [#849](https://github.com/modularml/mojo/issues/849) - The `Tensor` type is + now initialized with zeros at construction time. +- [#912](https://github.com/modularml/mojo/issues/912) - Invalid load for + `__get_address_as_lvalue`. +- [#916](https://github.com/modularml/mojo/issues/916) - Parser crash when + specifying default values for `inout` arguments. +- [#943](https://github.com/modularml/mojo/issues/943) - Mojo hangs if you + use continue in the nested loop +- [#957](https://github.com/modularml/mojo/issues/957) - Parser crash when a + function call with variadic arguments of a memory-only type is evaluated at + compile time. +- [#990](https://github.com/modularml/mojo/issues/990) - Fixes rounding + issue with floor division with negative numerator. +- [#1018](https://github.com/modularml/mojo/issues/1018) - In some cases the + sort function was returning invalid results. This release fixes some of these + corner cases. +- [#1010](https://github.com/modularml/mojo/issues/1010) - Initializing tensor + in alias declaration results in crash. +- [#1110](https://github.com/modularml/mojo/issues/1110) - The `time.now()` + function now returns nanoseconds across all operating systems. +- [#1115](https://github.com/modularml/mojo/issues/1115) - cannot load + non-register passable type into SSA register. + +## v0.4.0 for Mac (2023-10-19) + +### 🔥 Legendary + +- Mojo for Mac! + + The Mojo SDK now works on macOS (Apple silicon). This is the same version + previously released for Linux. Get the latest version of the SDK for your Mac + system: + + [Download Now!](https://developer.modular.com/download) + +## v0.4.0 (2023-10-05) + +### ⭐️ New + +- Mojo now supports default parameter values. For example: + + ```mojo + fn foo[a: Int = 3, msg: StringLiteral = "woof"](): + print(msg, a) + + fn main(): + foo() # prints 'woof 3' + foo[5]() # prints 'woof 5' + foo[7, "meow"]() # prints 'meow 7' + ``` + + Inferred parameter values take precedence over defaults: + + ```mojo + @value + struct Bar[v: Int]: + pass + + fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]): + print(msg, a) + + fn main(): + foo(Bar[9]()) # prints 'quack 9' + ``` + + Structs also support default parameters: + + ```mojo + @value + struct DefaultParams[msg: StringLiteral = "woof"]: + alias message = msg + + fn main(): + print(DefaultParams[]().message) # prints 'woof' + print(DefaultParams["meow"]().message) # prints 'meow' + ``` + +- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O + support. You can now write: + + ```mojo + var f = open("my_file.txt", "r") + print(f.read()) + f.close() + ``` + + or + + ```mojo + with open("my_file.txt", "r") as f: + print(f.read()) + ``` + +- Mojo now allows context managers to support an `__enter__` method without + implementing support for an `__exit__` method, enabling idioms like this: + + ```mojo + # This context manager consumes itself and returns it as the value. + fn __enter__(owned self) -> Self: + return self^ + ``` + + Here Mojo _cannot_ invoke a noop `__exit__` method because the context + manager is consumed by the `__enter__` method. This can be used for types + (like file descriptors) that are traditionally used with `with` statements, + even though Mojo's guaranteed early destruction doesn't require that. + +- A very basic version of `pathlib` has been implemented in Mojo. The + module will be improved to achieve functional parity with Python in + the next few releases. + +- The `memory.unsafe` module now contains a `bitcast` function. This is a + low-level operation that enables bitcasting between pointers and scalars. + +- The input parameters of a parametric type can now be directly accessed as + attribute references on the type or variables of the type. For example: + + ```mojo + @value + struct Thing[param: Int]: + pass + + fn main(): + print(Thing[2].param) # prints '2' + let x = Thing[9]() + print(x.param) # prints '9' + ``` + + Input parameters on values can even be accessed in parameter contexts. For + example: + + ```mojo + fn foo[value: Int](): + print(value) + + let y = Thing[12]() + alias constant = y.param + 4 + foo[constant]() # prints '16' + ``` + +- The Mojo REPL now supports code completion. Press Tab while typing + to query potential completion results. + +- Error messages from Python are now exposed in Mojo. For example the following + should print `No module named 'my_uninstalled_module'`: + + ```mojo + fn main(): + try: + let my_module = Python.import_module("my_uninstalled_module") + except e: + print(e) + ``` + +- Error messages can now store dynamic messages. For example, the following + should print "Failed on: Hello" + + ```mojo + fn foo(x:String) raises: + raise Error("Failed on: " + x) + + fn main(): + try: + foo("Hello") + except e: + print(e) + ``` + +### 🦋 Changed + +- We have improved and simplified the `parallelize` function. The function + now elides some overhead by caching the Mojo parallel runtime. + +- The Mojo REPL and Jupyter environments no longer implicitly expose `Python`, + `PythonObject`, or `Pointer`. These symbols must now be imported explicitly, + for example: + + ```mojo + from python import Python + from python.object import PythonObject + from memory.unsafe import Pointer + ``` + +- The syntax for specifying attributes with the `__mlir_op` prefix have changed + to mimic Python's keyword argument passing syntax. That is, `=` should be used + instead of `:`, e.g.: + + ```mojo + # Old syntax, now fails. + __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() + # New syntax. + __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() + ``` + +- You can now print the `Error` object directly. The `message()` method + has been removed. + +### 🛠️ Fixed + +- [#794](https://github.com/modularml/mojo/issues/794) - Parser crash when + using the `in` operator. +- [#936](https://github.com/modularml/mojo/issues/936) - The `Int` constructor + now accepts other `Int` instances. +- [#921](https://github.com/modularml/mojo/issues/921) - Better error message + when running `mojo` on a module with no `main` function. +- [#556](https://github.com/modularml/mojo/issues/556) - UInt64s are now + printed correctly. +- [#804](https://github.com/modularml/mojo/issues/804) - Emit error instead of + crashing when passing variadic arguments of unsupported types. +- [#833](https://github.com/modularml/mojo/issues/833) - Parser crash when + assigning module value. +- [#752](https://github.com/modularml/mojo/issues/752) - Parser crash when + calling async def. +- [#711](https://github.com/modularml/mojo/issues/711) - The overload resolution + logic now correctly prioritizes instance methods over static methods (if + candidates are an equally good match otherwise), and no longer crashed if a + static method has a `Self` type as its first argument. +- [#859](https://github.com/modularml/mojo/issues/859) - Fix confusing error and + documentation of the `rebind` builtin. +- [#753](https://github.com/modularml/mojo/issues/753) - Direct use of LLVM + dialect produces strange errors in the compiler. +- [#926](https://github.com/modularml/mojo/issues/926) - Fixes an issue that + occurred when a function with a return type of `StringRef` raised an error. + When the function raised an error, it incorrectly returned the string value of + that error. +- [#536](https://github.com/modularml/mojo/issues/536) - Report More information + on python exception. + +## v0.3.1 (2023-09-28) + +Our first-ever patch release of the Mojo SDK is here! Release v0.3.1 +includes primarily installation-related fixes. If you’ve had trouble +installing the previous versions of the SDK, this release may be for you. + +### 🛠️ Fixed + +- [#538](https://github.com/modularml/mojo/issues/538) - Installation hangs + during the testing phase. This issue occurs on machines with a low number + of CPU cores, such as free AWS EC2 instances and GitHub Codespaces. +- [#590](https://github.com/modularml/mojo/issues/590) - Installation fails + with a “failed to run python” message. +- [#672](https://github.com/modularml/mojo/issues/672) - Language server hangs + on code completion. Related to #538, this occurs on machines with a low + number of CPU cores. +- [#913](https://github.com/modularml/mojo/issues/913) - In the REPL and Jupyter + notebooks, inline comments were being parsed incorrectly. + +## v0.3.0 (2023-09-21) + +There's more Mojo to love in this, the second release of the Mojo SDK! This +release includes new features, an API change, and bug fixes. + +There's also an updated version of the [Mojo extension for VS +Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + +### ⭐️ New + +- Mojo now has partial support for passing keyword arguments to functions and + methods. For example the following should work: + + ```mojo + fn foo(a: Int, b: Int = 3) -> Int: + return a * b + + fn main(): + print(foo(6, b=7)) # prints '42' + print(foo(a=6, b=7)) # prints '42' + print(foo(b=7, a=6)) # prints '42' + ``` + + Parameters can also be inferred from keyword arguments, for example: + + ```mojo + fn bar[A: AnyType, B: AnyType](a: A, b: B): + print("Hello 🔥") + + fn bar[B: AnyType](a: StringLiteral, b: B): + print(a) + + fn main(): + bar(1, 2) # prints `Hello 🔥` + bar(b=2, a="Yay!") # prints `Yay!` + ``` + + For the time being, the following notable limitations apply: + + - Keyword-only arguments are not supported: + + ```mojo + fn baz(*args: Int, b: Int): pass # fails + fn baz(a: Int, *, b: Int): pass # fails + ``` + + (Keyword-only arguments are described in + [PEP 3102](https://peps.python.org/pep-3102/).) + + - Variadic keyword arguments are not supported: + + ```mojo + fn baz(a: Int, **kwargs: Int): pass # fails + ``` + +- Mojo now supports the `@nonmaterializable` decorator. The purpose is to mark + data types that should only exist in the parameter domain. To use it, a + struct is decorated with `@nonmaterializable(TargetType)`. Any time the + nonmaterializable type is converted from the parameter domain, it is + automatically converted to `TargetType`. A nonmaterializable struct should + have all of its methods annotated as `@always_inline`, and must be computable + in the parameter domain. In the following example, the `NmStruct` type can + be added in the parameter domain, but are converted to `HasBool` when + materialized. + + ```mojo + @value + @register_passable("trivial") + struct HasBool: + var x: Bool + fn __init__(x: Bool) -> Self: + return Self {x: x} + @always_inline("nodebug") + fn __init__(nms: NmStruct) -> Self: + return Self {x: True if (nms.x == 77) else False} + + @value + @nonmaterializable(HasBool) + @register_passable("trivial") + struct NmStruct: + var x: Int + @always_inline("nodebug") + fn __add__(self: Self, rhs: Self) -> Self: + return NmStruct(self.x + rhs.x) + + alias stillNmStruct = NmStruct(1) + NmStruct(2) + # When materializing to a run-time variable, it is automatically converted, + # even without a type annotation. + let convertedToHasBool = stillNmStruct + ``` + +- Mojo integer literals now produce the `IntLiteral` infinite precision integer + type when used in the parameter domain. `IntLiteral` is materialized to the + `Int` type for runtime computation, but intermediate computations at compile + time, using supported operators, can now exceed the bit width of the `Int` + type. + +- The Mojo Language Server now supports top-level code completions, enabling + completion when typing a reference to a variable, type, etc. This resolves + [#679](https://github.com/modularml/mojo/issues/679). + +- The Mojo REPL now colorizes the resultant variables to help distinguish input + expressions from the output variables. + +### 🦋 Changed + +- Mojo allows types to implement two forms of move constructors, one that is + invoked when the lifetime of one value ends, and one that is invoked if the + compiler cannot prove that. These were previously both named `__moveinit__`, + with the following two signatures: + + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __moveinit__(inout self, inout existing: Self): ... + ``` + + We've changed the second form to get its own name to make it more clear that + these are two separate operations: the second has been renamed to + `__takeinit__`: + + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __takeinit__(inout self, inout existing: Self): ... + ``` + + The name is intended to connote that the operation takes the conceptual value + from the source (without destroying it) unlike the first one which "moves" a + value from one location to another. + + For more information, see the Mojo Manual section on + [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). + +- The Error type in Mojo has changed. Instead of extracting the error message + using `error.value` you will now extract the error message using + `error.message()`. + +### 🛠️ Fixed + +- [#503](https://github.com/modularml/mojo/issues/503) - Improve error message + for failure lowering `kgen.param.constant`. +- [#554](https://github.com/modularml/mojo/issues/554) - Alias of static tuple + fails to expand. +- [#500](https://github.com/modularml/mojo/issues/500) - Call expansion failed + due to verifier error. +- [#422](https://github.com/modularml/mojo/issues/422) - Incorrect comment + detection in multiline strings. +- [#729](https://github.com/modularml/mojo/issues/740) - Improve messaging on + how to exit the REPL. +- [#756](https://github.com/modularml/mojo/issues/756) - Fix initialization + errors of the VS Code extension. +- [#575](https://github.com/modularml/mojo/issues/575) - Build LLDB/REPL with + libedit for a nicer editing experience in the terminal. + +## v0.2.1 (2023-09-07) + +The first versioned release of Mojo! 🔥 + +All earlier releases were considered version 0.1. + +### 🔥 Legendary + +- First release of the Mojo SDK! + + You can now develop with Mojo locally. The Mojo SDK is currently available + for Ubuntu Linux systems, and support for Windows and macOS is coming soon. + You can still develop from a Windows or Mac computer using a container or + remote Linux system. + + The Mojo SDK includes the Mojo standard library and the [Mojo command-line + interface](/mojo/cli/) (CLI), which allows you to run, compile, and package + Mojo code. It also provides a REPL programming environment. + + [Get the Mojo SDK!](https://developer.modular.com/download) + +- First release of the [Mojo extension for VS + Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + + This provides essential Mojo language features in Visual Studio Code, such as + code completion, code quick fixes, docs tooltips, and more. Even when + developing on a remote system, using VS Code with this extension provides + a native-like IDE experience. + +### ⭐️ New + +- A new `clobber_memory` function has been added to the + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. + The clobber memory function tells the system to flush all memory operations + at the specified program point. This allows you to benchmark operations + without the compiler reordering memory operations. + +- A new `keep` function has been added to the + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` + function tries to tell the compiler not to optimize the variable away + if not used. This allows you to avoid compiler's dead code elimination + mechanism, with a low footprint side effect. + +- New `shift_right` and `shift_left` functions have been added to the + [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in + a SIMD vector right/left, filling elements with zeros as needed. + +- A new `cumsum` function has been added to the + [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes + the cumulative sum (also known as scan) of input elements. + +- Mojo Jupyter kernel now supports code completion. + +### 🦋 Changed + +- Extends `rotate_bits_left`, `rotate_left`, `rotate_bits_right`, and + `rotate_right` to operate on Int values. The ordering of parameters has also + been changed to enable type inference. Now it's possible to write + `rotate_right[shift_val](simd_val)` and have the `dtype` and `simd_width` + inferred from the argument. This addresses + [Issue #528](https://github.com/modularml/mojo/issues/528). + +### 🛠️ Fixed + +- Fixed a bug causing the parser to crash when the `with` statement was written + without a colon. + This addresses [Issue #529](https://github.com/modularml/mojo/issues/529). + +- Incorrect imports no longer crash when there are other errors at the top + level of a module. This fixes [Issue + #531](https://github.com/modularml/mojo/issues/531). + +## August 2023 + +### 2023-08-24 + +- Fixed issue where the `with expr as x` statement within `fn` behaved + as if it were in a `def`, binding `x` with function scope instead of using + lexical scope. + +#### ⭐️ New + +- Major refactoring of the standard library to enable packaging and better + import ergonomics: + - The packages are built as binaries to improve startup speed. + - Package and module names are now lowercase to align with the Python style. + - Modules have been moved to better reflect the purpose of the underlying + functions (e.g. `Pointer` is now within the `unsafe` module in the `memory` + package). + - The following modules are now included as built-ins: + `SIMD`, `DType`, `IO`, `Object`, and `String`. + This means it's no longer necessary to explicitly import these modules. + Instead, these modules will be implicitly imported for the user. Private + methods within the module are still accessible using the + `builtin.module_name._private_method` import syntax. + - New `math` package has been added to contain the `bit`, `math`, `numerics`, + and `polynomial` modules. The contents of the `math.math` module are + re-exported into the `math` package. + +- Mojo now supports using memory-only types in parameter expressions and as + function or type parameters: + + ```mojo + @value + struct IntPair: + var first: Int + var second: Int + + fn add_them[value: IntPair]() -> Int: + return value.first + value.second + + fn main(): + print(add_them[IntPair(1, 2)]()) # prints '3' + ``` + +- In addition, Mojo supports evaluating code that uses heap-allocated memory + at compile-time and materializing compile-time values with heap-allocated + memory into dynamic values: + + ```mojo + fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: + var result = DynamicVector[Int]() + for i in range(lowerBound, upperBound, step): + result.push_back(i) + return result + + fn main(): + alias values = fillVector(5, 23, 7) + for i in range(0, values.__len__()): + print(values[i]) # prints '5', '12', and then '19' + ``` + +#### 🦋 Changed + +- `def main():`, without the explicit `None` type, can now be used to define + the entry point to a Mojo program. + +- The `assert_param` function has been renamed to `constrained` and is now + a built-in function. + +- The `print` function now works on `Complex` values. + +#### 🛠️ Fixed + +- Fixed issues with print formatting for `DType.uint16` and `DType.int16`. +- [Issue #499](https://github.com/modularml/mojo/issues/499) - Two new + `rotate_right` and `rotate_left` functions have been added to the SIMD module. +- [Issue #429](https://github.com/modularml/mojo/issues/429) - You can now + construct a `Bool` from a `SIMD` type whose element-type is `DType.bool`. +- [Issue #350](https://github.com/modularml/mojo/issues/350) - Confusing Matrix + implementation +- [Issue #349](https://github.com/modularml/mojo/issues/349) - Missing load_tr + in struct Matrix +- [Issue #501](https://github.com/modularml/mojo/issues/501) - Missing syntax + error messages in Python expressions. + +### 2023-08-09 + +#### 🦋 Changed + +- The `ref` and `mutref` identifiers are now treated as keywords, which means + they cannot be used as variable, attribute, or function names. These keywords + are used by the "lifetimes" features, which is still in development. We can + consider renaming these (as well as other related keywords) when the + development work gels, support is enabled in public Mojo builds, and when we + have experience using them. + +- The argument handling in `def` functions has changed: previously, they had + special behavior that involved mutable copies in the callee. Now, we have a + simple rule, which is that `def` argument default to the `owned` convention + (`fn` arguments still default to the `borrowed` convention). + + This change is mostly an internal cleanup and simplification of the compiler + and argument model, but does enable one niche use-case: you can now pass + non-copyable types to `def` arguments by transferring ownership of a value + into the `def` call. Before, that would not be possible because the copy was + made on the callee side, not the caller's side. This also allows the explicit + use of the `borrowed` keyword with a `def` that wants to opt-in to that + behavior. + +### 2023-08-03 + +#### ⭐️ New + +- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. + This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which + are just views). Therefore, the tensor type performs its own allocation and + free. Here is a simple example of using the tensor type to represent an RGB + image and convert it to grayscale: + + ```mojo + from tensor import Tensor, TensorShape + from utils.index import Index + from random import rand + + let height = 256 + let width = 256 + let channels = 3 + + # Create the tensor of dimensions height, width, channels and fill with + # random value. + let image = rand[DType.float32](height, width, channels) + + # Declare the grayscale image. + var gray_scale_image = Tensor[DType.float32](height, width) + + # Perform the RGB to grayscale transform. + for y in range(height): + for x in range(width): + let r = image[y,x,0] + let g = image[y,x,1] + let b = image[y,x,2] + gray_scale_image[Index(y,x)] = 0.299 * r + 0.587 * g + 0.114 * b + ``` + +#### 🛠️ Fixed + +- [Issue #53](https://github.com/modularml/mojo/issues/53) - `Int` now + implements true division with the `/` operator. Similar to Python, this + returns a 64-bit floating point number. The corresponding in-place operator, + `/=`, has the same semantics as `//=`. + +## July 2023 + +### 2023-07-26 + +#### ⭐️ New + +- Types that define both `__getitem__` and `__setitem__` (i.e. where + sub-scripting instances creates computed LValues) can now be indexed + in parameter expressions. + +- Unroll decorator for loops with constant bounds and steps: + - `@unroll`: Fully unroll a loop. + - `@unroll(n)`: Unroll a loop by factor of n, where `n` is a positive integer. + - Unroll decorator requires loop bounds and iteration step to be + compiler time constant value, otherwise unrolling will fail with + compilation error. This also doesn't make loop induction variable a parameter. + + ```mojo + # Fully unroll the loop. + @unroll + for i in range(5): + print(i) + + # Unroll the loop by a factor of 4 (with remainder iterations of 2). + @unroll(4) + for i in range(10): + print(i) + ``` + +- The Mojo REPL now prints the values of variables defined in the REPL. There is + full support for scalars and structs. Non-scalar SIMD vectors are not + supported at this time. + +#### 🛠️ Fixed + +- [Issue #437](https://github.com/modularml/mojo/issues/437) - Range can now + be instantiated with a PythonObject. + +- [Issue #288](https://github.com/modularml/mojo/issues/288) - Python strings + can now be safely copied. + +### 2023-07-20 + +#### ⭐️ New + +- Mojo now includes a `Limits` module, which contains functions to get the max + and min values representable by a type, as requested in [Issue + #51](https://github.com/modularml/mojo/issues/51). The following functions + moved from `Math` to `Limits`: `inf()`, `neginf()`, `isinf()`, `isfinite()`. + +- Mojo decorators are now distinguished between "signature" and "body" + decorators and are ordered. Signature decorators, like `@register_passable` + and `@parameter`, modify the type of declaration before the body is parsed. + Body decorators, like `@value`, modify the body of declaration after it is + fully parsed. Due to ordering, a signature decorator cannot be applied after + a body decorator. That means the following is now invalid: + + ```mojo + @register_passable # error: cannot apply signature decorator after a body one! + @value + struct Foo: + pass + ``` + +- Global variables can now be exported in Mojo compiled archives, using the + `@export` decorator. Exported global variables are public symbols in compiled + archives and use the variable name as its linkage name, by default. A custom + linkage name can be specified with `@export("new_name")`. This does not affect + variable names in Mojo code. + +- Mojo now supports packages! A Mojo package is defined by placing an + `__init__.mojo` or `__init__.🔥` within a directory. Other files in the same + directory form modules within the package (this works exactly like it + does [in Python](https://docs.python.org/3/tutorial/modules.html#packages)). + Example: + + ```bash + main.🔥 + my_package/ + __init__.🔥 + module.🔥 + my_other_package/ + __init__.🔥 + stuff.🔥 + ``` + + ```mojo + # main.🔥 + from my_package.module import some_function + from my_package.my_other_package.stuff import SomeType + + fn main(): + var x: SomeType = some_function() + ``` + +- Mojo now supports direct module and package imports! Modules and packages can + be imported and bound to names. Module and package elements, like functions, + types, global variables, and other modules, can be accessed using attribute + references, like `my_module.foo`. Note that modules lack runtime + representations, meaning module references cannot be instantiated. + + ```mojo + import builtin.io as io + import SIMD + + io.print("hello world") + var x: SIMD.Float32 = 1.2 + ``` + +#### 🦋 Changed + +- Reverted the feature from 2023-02-13 that allowed unqualified struct members. + Use the `Self` keyword to conveniently access struct members with bound + parameters instead. This was required to fix + [Issue #260](https://github.com/modularml/mojo/issues/260). + +- Updated the RayTracing notebook: added step 5 to create specular lighting for + more realistic images and step 6 to add a background image. + +#### 🛠️ Fixed + +- [Issue #260](https://github.com/modularml/mojo/issues/260) - Definitions + inside structs no longer shadow definitions outside of struct definitions. + +### 2023-07-12 + +#### ⭐️ New + +- Mojo now has support for global variables! This enables `var` and `let` + declaration at the top-level scope in Mojo files. Global variable initializers + are run when code modules are loaded by the platform according to the order of + dependencies between global variables, and their destructors are called in the + reverse order. + +- The [Mojo programming manual](/mojo/programming-manual.html) is now written + as a Jupyter notebook, and available in its entirety in the Mojo Playground + (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of + the same material, but it was not up-to-date.) + +- As a result, we've also re-written `HelloMojo.ipynb` to be much shorter and + provide a more gentle first-user experience. + +- [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now + available. Coroutines form the basis of Mojo's support for asynchronous + execution. Calls to `async fn`s can be stored into a `Coroutine`, from which + they can be resumed, awaited upon, and have their results retrieved upon + completion. + +#### 🦋 Changed + +- `simd_bit_width` in the `TargetInfo` module has been renamed to `simdbitwidth` + to better align with `simdwidthof`, `bitwidthof`, etc. + +#### 🛠️ Fixed + +- The walrus operator now works in if/while statements without parentheses, + e.g. `if x := function():`. + +- [Issue #428](https://github.com/modularml/mojo/issues/428) - The + `FloatLiteral` and `SIMD` types now support conversion to `Int` via the + `to_int` or `__int__` method calls. The behavior matches that of Python, which + rounds towards zero. + +### 2023-07-05 + +#### ⭐️ New + +- Tuple expressions now work without parentheses. For example, `a, b = b, a` + works as you'd expect in Python. +- Chained assignments (e.g. `a = b = 42`) and the walrus operator (e.g. + `some_function(b := 17)`) are now supported. + +#### 🦋 Changed + +- The `simd_width` and `dtype_simd_width` functions in the + [`TargetInfo`](/mojo/stdlib/sys/info) module + have been renamed to `simdwidthof`. + +- The `dtype_` prefix has been dropped from `alignof`, `sizeof`, and + `bitwidthof`. You can now use these functions (e.g. `alignof`) with any + argument type, including `DType`. + +- The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were + moved from the `Numerics` module to the + [`Math`](/mojo/stdlib/math/math) module, to better align with Python's + library structure. + +#### 🛠️ Fixed + +- [Issue #253](https://github.com/modularml/mojo/issues/253) - Issue + when accessing a struct member alias without providing parameters. + +- [Issue #404](https://github.com/modularml/mojo/issues/404) - The docs now use + `snake_case` for variable names, which more closely conforms to Python's + style. + +- [Issue #379](https://github.com/modularml/mojo/issues/379) - Tuple + limitations have been addressed and multiple return values are now supported, + even without parentheses. + +- [Issue #347](https://github.com/modularml/mojo/issues/347) - Tuples no longer + require parentheses. + +- [Issue #320](https://github.com/modularml/mojo/issues/320) - Python objects + are now traversable via `for` loops. + +## June 2023 + +### 2023-06-29 + +#### ⭐️ New + +- You can now share `.ipynb` notebook files in Mojo Playground. Just save a + file in the `shared` directory, and then right-click the file and select + **Copy Sharable link**. To open a shared notebook, you must already have + [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); + when you open a shared notebook, click **Import** at the top of the notebook + to save your own copy. For more details about this feature, see the + instructions inside the `help` directory, in the Mojo Playground file browser. + +#### 🦋 Changed + +- The `unroll2()` and `unroll3()` functions in the + [`Functional`](/mojo/stdlib/algorithm/functional) module have been renamed to + overload the `unroll()` function. These functions unroll 2D and 3D loops and + `unroll()` can determine the intent based on the number of input parameters. + +#### 🛠️ Fixed + +- [Issue #229](https://github.com/modularml/mojo/issues/229) - Issue when + throwing an exception from `__init__` before all fields are initialized. + +- [Issue #74](https://github.com/modularml/mojo/issues/74) - Struct + definition with recursive reference crashes. + +- [Issue #285](https://github.com/modularml/mojo/issues/285) - The + [`TargetInfo`](/mojo/stdlib/sys/info) module now includes + `is_little_endian()` and `is_big_endian()` to check if the target host uses + either little or big endian. + +- [Issue #254](https://github.com/modularml/mojo/issues/254) - Parameter name + shadowing in nested scopes is now handled correctly. + +### 2023-06-21 + +#### ⭐️ New + +- Added support for overloading on parameter signature. For example, it is now +possible to write the following: + + ```mojo + fn foo[a: Int](x: Int): + pass + + fn foo[a: Int, b: Int](x: Int): + pass + ``` + + For details on the overload resolution logic, see the Mojo Manual section on + [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). + +- A new `cost_of()` function has been added to `Autotune`. This meta-function + must be invoked at compile time, and it returns the number of MLIR operations + in a function (at a certain stage in compilation), which can be used to + build basic heuristics in higher-order generators. + + ```mojo + from autotune import cost_of + + fn generator[f: fn(Int) -> Int]() -> Int: + @parameter + if cost_of[fn(Int) -> Int, f]() < 10: + return f() + else: + # Do something else for slower functions... + ``` + +- Added a new example notebook with a basic Ray Tracing algorithm. + +#### 🦋 Changed + +- The `constrained_msg()` in the `Assert` module has been renamed to + `constrained()`. + +#### 🛠️ Fixed + +- Overloads marked with `@adaptive` now correctly handle signatures that differ +only in declared parameter names, e.g. the following now works correctly: + + ```mojo + @adaptive + fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... + + @adaptive + fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ... + ``` + +- [Issue #219](https://github.com/modularml/mojo/issues/219) - Issue when + redefining a function and a struct defined in the same cell. + +- [Issue #355](https://github.com/modularml/mojo/issues/355) - The loop order + in the Matmul notebook for Python and naive mojo have been reordered for + consistency. The loop order now follows (M, K, N) ordering. + +- [Issue #309](https://github.com/modularml/mojo/issues/309) - Use snake case + naming within the testing package and move the asserts out of the TestSuite + struct. + +### 2023-06-14 + +#### ⭐️ New + +- Tuple type syntax is now supported, e.g. the following works: + + ```mojo + fn return_tuple() -> (Int, Int): + return (1, 2) + ``` + +#### 🦋 Changed + +- The `TupleLiteral` type was renamed to just `Tuple`, e.g. + `Tuple[Int, Float]`. + +#### 🛠️ Fixed + +- [Issue #354](https://github.com/modularml/mojo/issues/354) - Returning a tuple + doesn't work even with parens. +- [Issue #365](https://github.com/modularml/mojo/issues/365) - Copy-paste error + in `FloatLiteral` docs. +- [Issue #357](https://github.com/modularml/mojo/issues/357) - Crash when + missing input parameter to variadic parameter struct member function. + +### 2023-06-07 + +#### ⭐️ New + +- Tuple syntax now works on the left-hand side of assignments (in "lvalue" + positions), enabling things like `(a, b) = (b, a)`. There are several + caveats: the element types must exactly match (no implicit conversions), + this only works with values of `TupleLiteral` type (notably, it will not work + with `PythonObject` yet) and parentheses are required for tuple syntax. + +#### ❌ Removed + +- Mojo Playground no longer includes the following Python packages (due to size, + compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): + `torch`, `tensorflow`, `keras`, `transformers`. + +#### 🦋 Changed + +- The data types and scalar names now conform to the naming convention used + by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` + instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). + +#### 🛠️ Fixed + +- [Issue #287](https://github.com/modularml/mojo/issues/287) - computed + lvalues don't handle raising functions correctly +- [Issue #318](https://github.com/modularml/mojo/issues/318) - Large integers + are not being printed correctly +- [Issue #326](https://github.com/modularml/mojo/issues/326) - Float modulo + operator is not working as expected +- [Issue #282](https://github.com/modularml/mojo/issues/282) - Default arguments + are not working as expected +- [Issue #271](https://github.com/modularml/mojo/issues/271) - Confusing error + message when converting between function types with different result semantics + +## May 2023 + +### 2023-05-31 + +#### ⭐️ New + +- Mojo Playground now includes the following Python packages (in response to + [popular demand](https://github.com/modularml/mojo/discussions/173)): + `torch`, `tensorflow`, `polars`, `opencv-python`, `keras`, `Pillow`, `plotly`, + `seaborn`, `sympy`, `transformers`. + +- A new optimization is applied to non-trivial copyable values that are passed + as an owned value without using the transfer (`^`) operator. Consider code + like this: + + ```mojo + var someValue : T = ... + ... + takeValueAsOwned(someValue) + ... + ``` + + When `takeValueAsOwned()` takes its argument as an + [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) + value (this is + common in initializers for example), it is allowed to do whatever it wants + with the value and destroy it when it is finished. In order to support this, + the Mojo compiler is forced to make a temporary copy of the `someValue` + value, and pass that value instead of `someValue`, because there may be other + uses of `someValue` after the call. + + The Mojo compiler is now smart enough to detect when there are no uses of + `someValue` later, and it will elide the copy just as if you had manually + specified the transfer operator like `takeValueAsOwned(someValue^)`. This + provides a nice "it just works" behavior for non-trivial types without + requiring manual management of transfers. + + If you'd like to take full control and expose full ownership for your type, + just don't make it copyable. Move-only types require the explicit transfer + operator so you can see in your code where all ownership transfer happen. + +- Similarly, the Mojo compiler now transforms calls to `__copyinit__` methods + into calls to `__moveinit__` when that is the last use of the source value + along a control flow path. This allows types which are both copyable and + movable to get transparent move optimization. For example, the following code + is compiled into moves instead of copies even without the use of the transfer + operator: + + ```mojo + var someValue = somethingCopyableAndMovable() + use(someValue) + ... + let otherValue = someValue # Last use of someValue + use(otherValue) + ... + var yetAnother = otherValue # Last use of otherValue + mutate(yetAnother) + ``` + + This is a significant performance optimization for things like `PythonObject` + (and more complex value semantic types) that are commonly used in a fluid + programming style. These don't want extraneous reference counting operations + performed by its copy constructor. + + If you want explicit control over copying, it is recommended to use a + non-dunder `.copy()` method instead of `__copyinit__`, and recall that + non-copyable types must always use of the transfer operator for those that + want fully explicit behavior. + +#### 🛠️ Fixed + +- [Issue #231](https://github.com/modularml/mojo/issues/231) - Unexpected error + when a Python expression raises an exception +- [Issue #119](https://github.com/modularml/mojo/issues/119) - The REPL fails + when a python variable is redefined + +### 2023-05-24 + +#### ⭐️ New + +- `finally` clauses are now supported on `try` statements. In addition, `try` + statements no longer require `except` clauses, allowing `try-finally` blocks. + `finally` clauses contain code that is always executed from control-flow + leaves any of the other clauses of a `try` statement by any means. + +#### 🦋 Changed + +- `with` statement emission changed to use the new `finally` logic so that + + ```mojo + with ContextMgr(): + return + ``` + + Will correctly execute `ContextMgr.__exit__` before returning. + +#### 🛠️ Fixed + +- [Issue #204](https://github.com/modularml/mojo/issues/204) - Mojo REPL + crash when returning a String at compile-time +- [Issue #143](https://github.com/modularml/mojo/issues/143) - synthesized + init in `@register_passable` type doesn't get correct convention. +- [Issue #201](https://github.com/modularml/mojo/issues/201) - String literal + concatenation is too eager. +- [Issue #209](https://github.com/modularml/mojo/issues/209) - [QoI] Terrible + error message trying to convert a type to itself. +- [Issue #32](https://github.com/modularml/mojo/issues/32) - Include struct + fields in docgen +- [Issue #50](https://github.com/modularml/mojo/issues/50) - Int to string + conversion crashes due to buffer overflow +- [Issue #132](https://github.com/modularml/mojo/issues/132) - PythonObject + `to_int` method has a misleading name +- [Issue #189](https://github.com/modularml/mojo/issues/189) - PythonObject bool + conversion is incorrect +- [Issue #65](https://github.com/modularml/mojo/issues/65) - Add SIMD + constructor from Bool +- [Issue #153](https://github.com/modularml/mojo/issues/153) - Meaning of + `Time.now` function result is unclear +- [Issue #165](https://github.com/modularml/mojo/issues/165) - Type in + `Pointer.free` documentation +- [Issue #210](https://github.com/modularml/mojo/issues/210) - Parameter results + cannot be declared outside top-level in function +- [Issue #214](https://github.com/modularml/mojo/issues/214) - Pointer offset + calculations at compile-time are incorrect +- [Issue #115](https://github.com/modularml/mojo/issues/115) - Float printing + does not include the right number of digits +- [Issue #202](https://github.com/modularml/mojo/issues/202) - + `kgen.unreachable` inside nested functions is illegal +- [Issue #235](https://github.com/modularml/mojo/issues/235) - Crash when + register passable struct field is not register passable +- [Issue #237](https://github.com/modularml/mojo/issues/237) - Parameter + closure sharp edges are not documented + +### 2023-05-16 + +#### ⭐️ New + +- Added missing dunder methods to `PythonObject`, enabling the use of common + arithmetic and logical operators on imported Python values. + +- `PythonObject` is now printable from Mojo, instead of requiring you to import +Python's print function. + +#### 🛠️ Fixed + +- [Issue #98](https://github.com/modularml/mojo/issues/98): + Incorrect error with lifetime tracking in loop. + +- [Issue #49](https://github.com/modularml/mojo/issues/49): Type inference + issue (?) in 'ternary assignment' operation (FloatLiteral vs. 'SIMD[f32, 1]'). + +- [Issue #48](https://github.com/modularml/mojo/issues/48): + and/or don't work with memory-only types. + +- [Issue #11](https://github.com/modularml/mojo/issues/11): `setitem` Support + for `PythonObject`. + +### 2023-05-11 + +#### ⭐️ New + +- `NDBuffer` and `Buffer` are now constructable via `Pointer` and + `DTypePointer`. + +- `String` now supports indexing with either integers or slices. + +- Added factorial function to the `Math` module. + +#### 🦋 Changed + +- The "byref" syntax with the `&` sigil has changed to use an `inout` + keyword to be more similar to the `borrowed` and `owned` syntax in arguments. + Please see [Issue #7](https://github.com/modularml/mojo/issues/7) for more + information. + +- Optimized the Matrix multiplication implementation in the notebook. + Initially we were optimizing for expandability rather than performance. We + have found a way to get the best of both worlds and now the performance of the + optimized Matmul implementation is 3x faster. + +- Renamed the [`^` postfix +operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) +from "consume" to "transfer." + +#### 🛠️ Fixed + +- Fixed missing overloads for `Testing.assertEqual` so that they work on +`Integer` and `String` values. + +- [Issue #6](https://github.com/modularml/mojo/issues/6): +Playground stops evaluating cells when a simple generic is defined. + +- [Issue #18](https://github.com/modularml/mojo/issues/18): +Memory leak in Python interoperability was removed. + +### 2023-05-02 + +#### 📢 Released + +- Mojo publicly launched! This was epic, with lots of great coverage online +including a [wonderful post by Jeremy +Howard](https://www.fast.ai/posts/2023-05-03-mojo-launch.html). The team is +busy this week. + +#### ⭐️ New + +- Added a Base64 encoding function to perform base64 encoding on strings. + +#### 🦋 Changed + +- Decreased memory usage of serialization of integers to strings. + +- Speedup the sort function. + +#### 🛠️ Fixed + +- Fixed time unit in the `sleep` function. + +## April 2023 + +### Week of 2023-04-24 + +- 📢 The default behavior of nested functions has been changed. Mojo nested + functions that capture are by default are non-parametric, runtime closures, + meaning that: + + ```mojo + def foo(x): + # This: + def bar(y): return x*y + # Is the same as: + let bar = lambda y: x*y + ``` + + These closures cannot have input or result parameters, because they are always + materialized as runtime values. Values captured in the closure (`x` in the + above example), are captured by copy: values with copy constructors cannot be + copied and captures are immutable in the closure. + + Nested functions that don't capture anything are by default "parametric" + closures: they can have parameters and they can be used as parameter values. + To restore the previous behavior for capturing closures, "parametric, + capture-by-unsafe-reference closures", tag the nested function with the + `@parameter` decorator. + +- 📢 Mojo now has full support for "runtime" closures: nested functions that + capture state materialized as runtime values. This includes taking the address + of functions, indirect calls, and passing closures around through function + arguments. Note that capture-by-reference is still unsafe! + + You can also take references to member functions with instances of that class + using `foo.member_function`, which creates a closure with `foo` bound to the + `self` argument. + +- 📢 Mojo now supports Python style `with` statements and context managers. + + These things are very helpful for implementing things like our + trace region support and things like Runtime support. + + A context manager in Mojo implements three methods: + + ```mojo + fn __enter__(self) -> T: + fn __exit__(self): + fn __exit__(self, err: Error) -> Bool: + ``` + + The first is invoked when the context is entered, and returns a + value that may optionally be bound to a target for use in the with + body. If the with block exits normally, the second method is + invoked to clean it up. If an error is raised, the third method + is invoked with the Error value. If that method returns true, the + error is considered handled, if it returns false, the error is + re-thrown so propagation continues out of the 'with' block. + +- 📢 Mojo functions now support variable scopes! Explicit `var` and `let` + declarations inside functions can shadow declarations from higher "scopes", + where a scope is defined as any new indentation block. In addition, the + `for` loop iteration variable is now scoped to the loop body, so it is + finally possible to write + + ```mojo + for i in range(1): pass + for i in range(2): pass + ``` + +- 📢 Mojo now supports an `@value` decorator on structs to reduce boilerplate + and encourage best practices in value semantics. The `@value` decorator looks + to see the struct has a memberwise initializer (which has arguments for each + field of the struct), a `__copyinit__` method, and a `__moveinit__` method, + and synthesizes the missing ones if possible. For example, if you write: + + ```mojo + @value + struct MyPet: + var name: String + var age: Int + ``` + + The `@value` decorator will synthesize the following members for you: + + ```mojo + fn __init__(inout self, owned name: String, age: Int): + self.name = name^ + self.age = age + fn __copyinit__(inout self, existing: Self): + self.name = existing.name + self.age = existing.age + fn __moveinit__(inout self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age + ``` + + This decorator can greatly reduce the boilerplate needed to define common + aggregates, and gives you best practices in ownership management + automatically. The `@value` decorator can be used with types that need custom + copy constructors (your definition wins). We can explore having the decorator + take arguments to further customize its behavior in the future. + +- 📚 Memcpy and memcmp now consistently use count as the byte count. + +- 📚 Add a variadic sting join on strings. + +- 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all + elements in a SIMD vector. + +- 📚 Optimize the `pow` function if the exponent is integral. + +- 📚 Add a `len` function which dispatches to `__len__` across the different + structs that support it. + +### Week of 2023-04-17 + +- 📢 Error messages have been significantly improved, thanks to prettier + printing for Mojo types in diagnostics. + +- 📢 Variadic values can now be indexed directly without wrapping them in a + `VariadicList`! + +- 📢 `let` declarations in a function can now be lazily initialized, and `var` + declarations that are never mutated get a warning suggesting they be converted + to a `let` declaration. Lazy initialization allows more flexible patterns of + initialization than requiring the initializer be inline, e.g.: + + ```mojo + let x : Int + if cond: + x = foo() + else: + x = bar() + use(x) + ``` + +- 📢 Functions defined with `def` now return `object` by default, instead of + `None`. This means you can return values (convertible to `object`) inside + `def` functions without specifying a return type. + +- 📢 The `@raises` decorator has been removed. Raising `fn` should be declared + by specifying `raises` after the function argument list. The rationale is that + `raises` is part of the type system, instead of a function modifier. + +- 📢 The `BoolLiteral` type has been removed. Mojo now emits `True` and `False` + directly as `Bool`. + +- 📢 Syntax for function types has been added. You can now write function types + with `fn(Int) -> String` or `async def(&String, *Int) -> None`. No more + writing `!kgen.signature` types by hand! + +- 📢 Float literals are not emitted as `FloatLiteral` instead of an MLIR `f64` + type! + +- 📢 Automatic destructors are now supported by Mojo types, currently spelled + `fn __del___(owned self):` (the extra underscore will be dropped shortly). + These destructors work like Python object destructors and similar to C++ + destructors, with the major difference being that they run "as soon as + possible" after the last use of a value. This means they are not suitable + for use in C++-style RAII patterns (use the `with` statement for that, which + is currently unsupported). + + These should be generally reliable for both memory-only and register-passable + types, with the caveat that closures are known to _not_ capture values + correctly. Be very careful with interesting types in the vicinity of a + closure! + +- A new (extremely dangerous!) builtin function is available for low-level + ownership muckery. The `__get_address_as_owned_value(x)` builtin takes a + low-level address value (of `!kgen.pointer` type) and returns an `owned` value + for the memory that is pointed to. This value is assumed live at the + invocation of the builtin, but is "owned" so it needs to be consumed by the + caller, otherwise it will be automatically destroyed. This is an effective + way to do a "placement delete" on a pointer. + + ```mojo + # "Placement delete": destroy the initialized object begin pointed to. + _ = __get_address_as_owned_value(somePointer.value) + + # Result value can be consumed by anything that takes it as an 'owned' + # argument as well. + consume(__get_address_as_owned_value(somePointer.value)) + ``` + +- Another magic operator, named `__get_address_as_uninit_lvalue(x)` joins + the magic LValue operator family. This operator projects a pointer to + an LValue like `__get_address_as_lvalue(x)`. The difference is that + `__get_address_as_uninit_lvalue(x)` tells the compiler that the pointee is + uninitialized on entry and initialized on exit, which means that you can use + it as a "placement new" in C++ sense. `__get_address_as_lvalue(x)` tells the + compiler that the pointee is initialized already, so reassigning over it will + run the destructor. + + ```mojo + # "*Re*placement new": destroy the existing SomeHeavy value in the memory, + # then initialize a new value into the slot. + __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) + + # Ok to use an lvalue, convert to borrow etc. + use(__get_address_as_lvalue(somePointer.value)) + + # "Placement new": Initialize a new value into uninitialied memory. + __get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5) + + # Error, cannot read from uninitialized memory. + use(__get_address_as_uninit_lvalue(somePointer.value)) + ``` + + Note that `__get_address_as_lvalue` assumes that there is already a value at + the specified address, so the assignment above will run the `SomeHeavy` + destructor (if any) before reassigning over the value. + +- 📢 Implement full support for `__moveinit__` (aka move constructors) + + This implements the ability for memory-only types to define two different + types of move ctors if they'd like: + + 1. `fn __moveinit__(inout self, owned existing: Self)`: Traditional Rust + style moving constructors that shuffles data around while taking + ownership of the source binding. + 2. `fn __moveinit__(inout self, inout existing: Self):`: C++ style "stealing" + move constructors that can be used to take from an arbitrary LValue. + + This gives us great expressive capability (better than Rust/C++/Swift) + and composes naturally into our lifetime tracking and value + categorization system. + +- The `__call__` method of a callable type has been relaxed to take `self` by + borrow, allow non-copyable callees to be called. + +- Implicit conversions are now invoked in `raise` statements properly, allowing + converting strings to `Error` type. + +- Automatic destructors are turned on for `__del__` instead of `__del___`. + +- 📚 Add the builtin FloatLiteral type. + +- 📚 Add integral `floordiv` and `mod` for the SIMD type that handle negative + values. + +- 📚 Add an F64 to String converter. + +- 📚 Make the `print` function take variadic inputs. + +### Week of 2023-04-10 + +- 📢 Introduce consume operator `x^` + + This introduces the postfix consume operator, which produces an RValue given + a lifetime tracked object (and, someday, a movable LValue). + +- Mojo now automatically synthesizes empty destructor methods for certain types + when needed. + +- The `object` type has been built out into a fully-dynamic type, with dynamic + function dispatch, with full error handling support. + + ```mojo + def foo(a) -> object: + return (a + 3.45) < [1, 2, 3] # raises a TypeError + ``` + +- 📢 The `@always_inline` decorator is no longer required for passing capturing + closures as parameters, for both the functions themselves as functions with + capturing closures in their parameters. These functions are still inlined but + it is an implementation detail of capturing parameter closures. Mojo now + distinguishes between capturing and non-capturing closures. Nested functions + are capturing by default and can be made non-capturing with the + `@noncapturing` decorator. A top-level function can be passed as a capturing + closure by marking it with the `@closure` decorator. + +- 📢 Support for list literals has been added. List literals `[1, 2, 3]` + generate a variadic heterogeneous list type. + +- Variadics have been extended to work with memory-primary types. + +- Slice syntax is now fully-supported with a new builtin `slice` object, added + to the compiler builtins. Slice indexing with `a[1:2:3]` now emits calls to + `__setitem__` and `__getitem__` with a slice object. + +- Call syntax has been wired up to `__call__`. You can now `f()` on custom + types! + +- Closures are now explicitly typed as capturing or non-capturing. If a + function intends to accept a capturing closure, it must specify the + `capturing` function effect. + +- 📚 Add a `Tile2D` function to enable generic `2D` tiling optimizations. + +- 📚 Add the `slice` struct to enable getting/setting spans of elements via + `getitem`/`setitem`. + +- 📚 Add syntax sugar to autotuning for both specifying the autotuned values, + searching, and declaring the evaluation function. + +### Week of 2023-04-03 + +- The `AnyType` and `NoneType` aliases were added and auto-imported in all + files. + +- 📢 The Mojo VS Code extension has been improved with docstring validation. It + will now warn when a function's docstring has a wrong argument name, for + example. + +- 📢 A new built-in literal type `TupleLiteral` was added in `_CompilerBuiltin`. + It represents literal tuple values such as `(1, 2.0)` or `()`. + +- 📢 The `Int` type has been moved to a new `Builtin` module and is + auto-imported in all code. The type of integer literals has been changed from + the MLIR `index` type to the `Int` type. + +- Mojo now has a powerful flow-sensitive uninitialized variable checker. This + means that you need to initialize values before using them, even if you + overwrite all subcomponents. This enables the compiler to reason about the + true lifetime of values, which is an important stepping stone to getting + automatic value destruction in place. + +- 📢 Call syntax support has been added. Now you can directly call an object + that implements the `__call__` method, like `foo(5)`. + +- 📢 The name for copy constructors got renamed from `__copy__` to + `__copyinit__`. Furthermore, non-`@register_passable` types now implement + it like they do an init method where you fill in a by-reference self, for + example: + + ```mojo + fn __copyinit__(inout self, existing: Self): + self.first = existing.first + self.second = existing.second + ``` + + This makes copy construction work more similarly to initialization, and + still keeps copies `x = y` distinct from initialization `x = T(y)`. + +- 📢 Initializers for memory-primary types are now required to be in the form + `__init__(inout self, ...):` with a None result type, but for register primary + types, it remains in the form `__init__(...) -> Self:`. The `T{}` initializer + syntax has been removed for memory-primary types. + +- Mojo String literals now emit a builtin `StringLiteral` type! One less MLIR + type to worry about. + +- New `__getattr__` and `__setattr__` dunder methods were added. Mojo calls + these methods on a type when attempting member lookup of a non-static member. + This allows writing dynamic objects like `x.foo()` where `foo` is not a member + of `x`. + +- Early destructor support has been added. Types can now define a special + destructor method `__del___` (note three underscores). This is an early + feature and it is still being built out. There are many caveats, bugs, + and missing pieces. Stay tuned! + +- 📚 Integer division and mod have been corrected for rounding in the presence + of negative numbers. + +- 📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to + `SIMD[1, type]`. + +## March 2023 + +### Week of 2023-03-27 + +- 📢 Parameter names are no longer load-bearing in function signatures. This + gives more flexibility in defining higher-order functions, because the + functions passed as parameters do not need their parameter names to match. + + ```mojo + # Define a higher-order function... + fn generator[ + func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] + ](): + pass + + # Int parameter is named "foo". + fn f0[foo: Int](): + pass + + # Int parameter is named "bar". + fn f1[bar: Int](): + pass + + fn main(): + # Both can be used as `func`! + generator[f0]() + generator[f1]() + ``` + + Stay tuned for improved function type syntax... + +- 📢 Two magic operators, named `__get_lvalue_as_address(x)` and + `__get_address_as_lvalue` convert stored LValues to and from `!kgen.pointer` + types (respectively). This is most useful when using the `Pointer[T]` + library type. The `Pointer.address_of(lvalue)` method uses the first one + internally. The second one must currently be used explicitly, and can be + used to project a pointer to a reference that you can pass around and use + as a self value, for example: + + ```mojo + # "Replacement new" SomeHeavy value into the memory pointed to by a + # Pointer[SomeHeavy]. + __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) + ``` + + Note that `__get_address_as_lvalue` assumes that there is already a value at + the specified address, so the assignment above will run the `SomeHeavy` + destructor (if any) before reassigning over the value. + +- The `(((x)))` syntax is __mlir_op has been removed in favor of + `__get_lvalue_as_address` which solves the same problem and is more general. + +- 📢 When using a mutable `self` argument to a struct `__init__` method, it + now must be declared with `&`, like any other mutable method. This clarifies + the mutation model by making `__init__` consistent with other mutating + methods. + +- 📚 Add variadic string join function. + +- 📚 Default initialize values with 0 or null if possible. + +- 📚 Add compressed, aligned, and mask store intrinsics. + +### Week of 2023-03-20 + +- Initial `String` type is added to the standard library with some very basic + methods. + +- Add `DimList` to remove the need to use an MLIR list type throughout the + standard library. + +- 📢 The `__clone__` method for copying a value is now named `__copy__` to + better follow Python term of art. + +- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, + instead of taking it by reference. This makes it easier to write, works for + `@register_passable` types, and exposes more optimization opportunities to + the early optimizer and dataflow analysis passes. + + ```mojo + # Before: + fn __clone__(inout self) -> Self: ... + + # After: + fn __copy__(self) -> Self: ... + ``` + +- 📢 A new `@register_passable("trivial")` may be applied to structs that + have no need for a custom `__copy__` or `__del__` method, and whose state is + only made up of `@register_passable("trivial")` types. This eliminates the + need to define `__copy__` boilerplate and reduces the amount of IR generated + by the compiler for trivial types like `Int`. + +- You can now write back to attributes of structs that are produced by a + computed lvalue expression. For example `a[i].x = ..` works when `a[i]` + is produced with a `__getitem__`/`__setitem__` call. This is implemented by + performing a read of `a[i]`, updating the temporary, then doing a writeback. + +- The remaining hurdles to using non-parametric, `@register_passable` types as + parameter values have been cleared. Types like `Int` should enjoy full use as + parameter values. + +- Parameter pack inference has been added to function calls. Calls to functions + with parameter packs can now elide the pack types: + + ```mojo + fn foo[*Ts: AnyType](*args: *Ts): pass + + foo(1, 1.2, True, "hello") + ``` + + Note that the syntax for parameter packs has been changed as well. + +- 📚 Add the runtime string type. + +- 📚 Introduce the DimList struct to remove the need to use low-level MLIR + operations. + +### Week of 2023-03-13 + +- 📢 Initializers for structs now use `__init__` instead of `__new__`, + following standard practice in Python. You can write them in one of two + styles, either traditional where you mutate self: + + ```mojo + fn __init__(self, x: Int): + self.x = x + ``` + + or as a function that returns an instance: + + ```mojo + fn __init__(x: Int) -> Self: + return Self {x: x} + ``` + + Note that `@register_passable` types must use the later style. + +- 📢 The default argument convention is now the `borrowed` convention. A + "borrowed" argument is passed like a C++ `const&` so it doesn't need to + invoke the copy constructor (aka the `__clone__` method) when passing a value + to the function. There are two differences from C++ `const&`: + + 1. A future borrow checker will make sure there are no mutable + aliases with an immutable borrow. + 2. `@register_passable` values are passed directly in an SSA register (and + thus, usually in a machine register) instead of using an extra reference + wrapper. This is more efficient and is the 'right default' for + `@register_passable` values like integers and pointers. + + This also paves the way to remove the reference requirement from `__clone__` + method arguments, which will allow us to fill in more support for them. + +- Support for variadic pack arguments has been added to Mojo. You can now + write heterogeneous variadic packs like: + + ```mojo + fn foo[*Ts: AnyType](args*: Ts): pass + + foo[Int, F32, String, Bool](1, 1.5, "hello", True) + ``` + +- The `owned` argument convention has been added. This argument convention + indicates that the function takes ownership of the argument and is responsible + for managing its lifetime. + +- The `borrowed` argument convention has been added. This convention signifies + the callee gets an immutable shared reference to a value in the caller's + context. + +- 📚 Add the `getenv` function to the `OS` module to enable getting environment + variables. + +- 📚 Enable the use of dynamic strides in `NDBuffer`. + +### Week of 2023-03-06 + +- 📢 Support added for using capturing async functions as parameters. + +- 📢 Returning result parameters has been moved from `return` statements to a + new `param_return` statement. This allows returning result parameters from + throwing functions: + + ```mojo + @raises + fn foo[() -> out: Int](): + param_return[42] + raise Error() + ``` + + And returning different parameters along `@parameter if` branches: + + ```mojo + fn bar[in: Bool -> out: Int](): + @parameter + if in: + param_return[1] + else: + param_return[2] + ``` + +- 📢 Mojo now supports omitting returns at the end of functions when they would + not reachable. For instance, + + ```mojo + fn foo(cond: Bool) -> Int: + if cond: + return 0 + else: + return 1 + + fn bar() -> Int: + while True: + pass + ``` + +- String literals now support concatenation, so `"hello " "world"` is treated + the same as `"hello world"`. + +- Empty bodies on functions, structs, and control flow statements are no longer + allowed. Please use `pass` in them to explicitly mark that they are empty, + just like in Python. + +- 📢 Structs in Mojo now default to living in memory instead of being passed + around in registers. This is the right default for generality (large + structures, structures whose pointer identity matters, etc) and is a key + technology that enables the borrow model. For simple types like `Int` and + `SIMD`, they can be marked as `@register_passable`. + + Note that memory-only types currently have some limitations: they cannot be + used in generic algorithms that take and return a `!mlirtype` argument, and + they cannot be used in parameter expressions. Because of this, a lot of + types have to be marked `@register_passable` just to work around the + limitations. We expect to enable these use-cases over time. + +- 📢 Mojo now supports computed lvalues, which means you can finally assign to + subscript expressions instead of having to call `__setitem__` explicitly. + + Some details on this: Mojo allows you to define multiple `__setitem__` + overloads, but will pick the one that matches your `__getitem__` type if + present. It allows you to pass computed lvalues into inout arguments by + introducing a temporary copy of the value in question. + +- Mojo now has much better support for using register-primary struct types in + parameter expressions and as the types of parameter values. This will allow + migration of many standard library types away from using bare MLIR types like + `__mlir_type.index` and towards using `Int`. This moves us towards getting rid + of MLIR types everywhere and makes struct types first-class citizens in the + parameter system. + +- 📚 Add a `sort` function. + +- 📚 Add non-temporal store to enable cache bypass. + +## February 2023 + +### Week of 2023-02-27 + +- 📢 The `@interface`, `@implements`, and `@evaluator` trio of decorators have + been removed, replaced by the `@parameter if` and `@adaptive` features. + +- 📢 Parameter inference can now infer the type of variadic lists. + +- 📢 Memory primary types are now supported in function results. A result slot + is allocated in the caller, and the callee writes the result of the function + into that slow. This is more efficient for large types that don't fit into + registers neatly! And initializers for memory-primary types now initialize + the value in-place, instead of emitting a copy! + +- Support for `let` decls of memory primary types has been implemented. These + are constant, ready-only values of memory primary types but which are + allocated on the function stack. + +- Overload conversion resolution and parameter inference has been improved: + + 1. Inference now works with `let` decls in some scenarios that weren't + working before. + 2. Parameter bindings can now infer types into parameter expressions. This + helps resolve higher-order functions in parameter expressions. + +- 📚 Optimize floor, ceil, and ldexp on X86 hardware. + +- 📚 Implement the log math function. + +### Week of 2023-02-20 + +- 📢 A new `@__memory_primary` struct decorator has been introduced. Memory + primary types must always have an address. For instance, they are always + stack-allocated when declared in a function and their values are passed into + function calls by address instead of copy. This is in contract with register + primary types that may not have an address, and which are passed by value + in function calls. Memory-primary fields are not allowed inside + register-primary structs, because struct elements are stored in-line. + +- 📢 A new `_CompilerBuiltin` module was added. This module defines core types + and functions of the language that are referenced by the parser, and hence, is + auto-imported by all other modules. For example new types for literal values + like the boolean True/False will be included in `_CompilerBuiltin`. + +- 📢 A special `__adaptive_set` property can be accessed on a function reference + marked as `@adaptive`. The property returns the adaptive overload set of that + function. The return type is a `!kgen.variadic`. This feature is useful to + implement a generic `evaluate` function in the standard library. + +- 📢 A new built-in literal type `BoolLiteral` was added in `_CompilerBuiltin`. + It represents the literal boolean values `True` and `False`. This is the first + Mojo literal to be emitted as a standard library type! + +- 📚 Add the prefetch intrinsic to enable HW prefetching a cache line. + +- 📚 Add the InlinedFixedVector, which is optimized for small vectors and stores + values on both the stack and the heap. + +### Week of 2023-02-13 + +- Unqualified lookups of struct members apply contextual parameters. This means + for instance that you can refer to static methods without binding the + struct parameters. + + ```mojo + struct Foo[x: Int]: + @staticmethod + bar(): pass + + foo(self): + bar() # implicitly binds to Foo[x].bar() + Foo[2].bar() # explicitly bind to another parameter + ``` + +- 📢 A new `Self` type refers to the enclosing type with all parameters bound + to their current values. This is useful when working with complex parametric + types, e.g.: + + ```mojo + struct MyArray[size: Int, element_type: type]: + fn __new__() -> Self: + return Self {...} + ``` + + which is a lot nicer than having to say `MyArray[size, element_type]` over + and over again. + +- 📢 Mojo now supports an `@adaptive` decorator. This decorator will supersede + interfaces, and it represents an overloaded function that is allowed to + resolve to multiple valid candidates. In that case, the call is emitted as a + fork, resulting in multiple function candidates to search over. + + ```mojo + @adaptive + fn sort(arr: ArraySlice[Int]): + bubble_sort(arr) + + @adaptive + fn sort(arr: ArraySlice[Int]): + merge_sort(arr) + + fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]): + let arr = lhs + rhs + sort(arr) # this forks compilation, creating two instances + # of the surrounding function + ``` + +- 📢 Mojo now requires that types implement the `__clone__` special member in + order to copy them. This allows the safe definition of non-copyable types + like Atomic. Note that Mojo still doesn't implement destructors, and (due to + the absence of non-mutable references) it doesn't actually invoke the + `__clone__` member when copying a let value. As such, this forces to you as + a Mojo user to write maximal boilerplate without getting much value out of it. + + In the future, we will reduce the boilerplate with decorators, and we will + actually start using it. This will take some time to build out though. + +- 📢 A special `__mlir_region` statement was added to provide stronger + invariants around defining MLIR operation regions in Mojo. It similar syntax + to function declarations, except it there are no results and no input + conventions. + +- 📚 Implement the log math function. + +- 📚 Improve the DType struct to enable compile-time equality checks. + +- 📚 Add the Complex struct class. + +### Week of 2023-02-06 + +- 📢 The `if` statement now supports a `@parameter` decorator, which requires + its condition to be a parameter expression, but which only emits the 'True' + side of the condition to the binary, providing a "static if" functionality. + This should eliminate many uses of `@interface` that are just used to provide + different constraint on the implementations. + +- 📢 `fn main():` is now automatically exported and directly runnable by the + command-line `mojo` tool. This is a stop-gap solution to enable script-like + use cases until we have more of the language built out. + +- 🪦 The `@nodebug_inline` feature has been removed, please use + `@alwaysinline("nodebug")` for methods that must be inlined and that we don't + want to step into. + +- 📢 Python chained comparisons, ex. `a < b < c`, are now supported in Mojo. + +- 📢 Functions can now be defined with default argument values, such as + `def f(x: Int, y: Int = 5):`. The default argument value is used when callers + do not provide a value for that argument: `f(3)`, for example, uses the + default argument value of `y = 5`. + +- Unused coroutine results are now nicely diagnosed as "missing await" warnings. + +- 📚 Introduce a vectorized reduction operations to the SIMD type. + +## January 2023 + +### Week of 2023-01-30 + +- A basic Mojo language server has been added to the VS Code extension, which + parses your code as you write it, and provides warnings, errors, and fix-it + suggestions! + +- 💯 The Mojo standard library is now implicitly imported by default. + +- The coroutine lowering support was reworked and a new `Coroutine[T]` type was + implemented. Now, the result of a call to an async function MUST be wrapped in + a `Coroutine[T]`, or else memory will leak. In the future, when Mojo supports + destructors and library types as literal types, the results of async function + calls will automatically wrapped in a `Coroutine[T]`. But today, it must be + done manually. This type implements all the expected hooks, such as + `__await__`, and `get()` to retrieve the result. Typical usage: + + ```mojo + async fn add_three(a: Int, b: Int, c: Int) -> Int: + return a + b + c + + async fn call_it(): + let task: Coroutine[Int] = add_three(1, 2, 3) + print(await task) + ``` + +- ⭐️ We now diagnose unused expression values at statement context in `fn` + declarations (but not in `def`s). This catches bugs with unused values, e.g. + when you forget the parens to call a function. + +- 📢 An `@always_inline("nodebug")` function decorator can be used on functions + that need to be force inlined, but when they should not have debug info in + the result. This should be used on methods like `Int.__add__` which should + be treated as builtin. + +- 📢 The `@export` decorator now supports an explicit symbol name to export to, + for example: + + ```mojo + @export("baz") # exported as 'baz' + fn some_mojo_fn_name(): + ``` + +- 📢 🚧 Subscript syntax is now wired up to the `__getitem__` dunder method. + + This allows type authors to implement the `__getitem__` method to enable + values to be subscripted. This is an extended version of the Python semantics + (given we support overloading) that allows you to define N indices instead of + a single version that takes a tuple (also convenient because we don't have + tuples yet). + + Note that this has a very, very important limitation: subscripts are NOT + wired up to `__setitem__` yet. This means that you can read values with + `.. = v[i]` but you cannot store to them with `v[i] = ..`. For this, please + continue to call `__setitem__` directly. + +- 📢 Function calls support parameter inference. + + For calls to functions that have an insufficient number of parameters + specified at the callsite, we can now infer them from the argument list. We + do this by matching up the parallel type structure to infer what the + parameters must be. + + Note that this works left to right in the parameter list, applying explicitly + specified parameters before trying to infer new ones. This is similar to how + C++ does things, which means that you may want to reorder the list of + parameters with this in mind. For example, a `dyn_cast`-like function will be + more elegant when implemented as: + + `fn dyn_cast[DstType: type, SrcType: type](src: SrcType) -> DstType:` + + Than with the `SrcType`/`DstType` parameters flipped around. + +- 📚 Add the growable Dynamic vector struct. + +### Week of 2023-01-23 + +- Inplace operations like `+=`/`__iadd__` may now take `self` by-val if they + want to, instead of requiring it to be by-ref. +- ⭐️ Inplace operations are no longer allowed to return a non-None value. The + corresponding syntax is a statement, not an expression. + +- A new `TaskGroup` type was added to the standard library. This type can be + used to schedule multiple tasks on a multi-threaded workqueue to be executed + in parallel. An async function can `await` all the tasks at once with the + taskgroup. + +- 📢 We now support for loops! A type that defines an `__iter__` method that + returns a type that defines `__next__` and `__len__` methods is eligible to + be used in the statement `for el in X()`. Control flow exits the loop when + the length is zero. + + This means things like this now work: + + ```mojo + for item in range(start, end, step): + print(item) + ``` + +- Result parameters now have names. This is useful for referring to result + parameters in the return types of a function: + + ```mojo + fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]: + ``` + +- 📢 We now support homogeneous variadics in value argument lists, using the + standard Python `fn thing(*args: Int):` syntax! Variadics also have support + in parameter lists: + + ```mojo + fn variadic_params_and_args[*a: Int](*b: Int): + print(a[0]) + print(b[1]) + ``` + +- 📚 Add the range struct to enable `for ... range(...)` loops. + +- 📚 Introduce the unroll generator to allow one to unroll loops via a library + function. + +### Week of 2023-01-16 + +- 📢 Struct field references are now supported in parameter context, so you + can use `someInt.value` to get the underlying MLIR thing out of it. This + should allow using first-class types in parameters more widely. +- 📢 We now support "pretty" initialization syntax for structs, e.g.: + + ```mojo + struct Int: + var value: __mlir_type.index + fn __new__(value: __mlir_type.index) -> Int: + return Int {value: value} + ``` + + This eliminates the need to directly use the MLIR `lit.struct.create` op in + struct initializers. This syntax may change in the future when ownership + comes in, because we will be able to support the standard `__init__` model + then. +- 📢 It is now possible to attach regions to `__mlir_op` operations. This is + done with a hack that allows an optional `_region` attribute that lists + references to the region bodies (max 1 region right now due to lack of list + `[]` literal). +- Nested functions now parse, e.g.: + + ```mojo + fn foo(): + fn bar(): + pass + bar() + ``` + +- Python-style `async` functions should now work and the `await` expression + prefix is now supported. This provides the joy of async/await syntactic + sugar when working with asynchronous functions. This is still somewhat + dangerous to use because we don't have proper memory ownership support yet. + +- String literals are now supported. + +- Return processing is now handled by a dataflow pass inside the compiler, so + it is possible to return early out of if statements. + +- The parser now supports generating 'fixit' hints on diagnostics, and uses + them when a dictionary literal uses a colon instead of equal, e.g.: + + ```log + x.mojo:8:48: error: expected ':' in subscript slice, not '=' + return __mlir_op.`lit.struct.create`[value = 42]() + ^ + : + ``` + +- 📚 Add reduction methods which operate on buffers. + +- 📚 Add more math functions like sigmoid, sqrt, rsqrt, etc. + +- 📚 Add partial load / store which enable loads and stores that are predicated + on a condition. + +### Week of 2023-01-09 + +- The `/` and `*` markers in function signatures are now parsed and their + invariants are checked. We do not yet support keyword arguments yet though, + so they aren't very useful. +- Functions now support a new `@nodebug_inline` decorator. + (Historical note: this was later replaced with `@alwaysinline("nodebug")`). + + Many of the things at the bottom level of the Mojo stack are trivial + zero-abstraction wrappers around MLIR things, for example, the `+` + operator on Int or the `__bool__` method on Bool itself. These operators + need to be force inlined even at -O0, but they have some additional things + that we need to wrestle with: + + 1. In no case would a user actually want to step into the `__bool__` method on + Bool or the + method on Int. This would be terrible debugger QoI for + unless you're debugging Int itself. We need something like + `__always_inline__, __nodebug__` attributes that clang uses in headers + like xmmintrin.h. + + 2. Similarly, these "operators" should be treated by users as primitives: + they don't want to know about MLIR or internal implementation details of + Int. + + 3. These trivial zero abstraction things should be eliminated early in the + compiler pipeline so they don't slow down the compiler, bloating out the + call graph with trivial leaves. Such thing slows down the elaborator, + interferes with basic MLIR things like fold(), bloats out the IR, or + bloats out generated debug info. + + 4. In a parameter context, we want some of these things to get inlined so + they can be simplified by the attribute logic and play more nicely with + canonical types. This is just a nice to have thing those of us who have + to stare at generated IR. + + The solution to this is a new `@nodebug_inline` decorator. This decorator + causes the parser to force-inline the callee instead of generating a call to + it. While doing so, it gives the operations the location of the call itself + (that's the "nodebug" part) and strips out let decls that were part of the + internal implementation details. + + This is a super-power-user-feature intended for those building the standard + library itself, so it is intentionally limited in power and scope: It can + only be used on small functions, it doesn't support regions, by-ref, throws, + async, etc. + +- Separately, we now support an `@alwaysInline` decorator on functions. This + is a general decorator that works on any function, and indicates that the + function must be inlined. Unlike `@nodebug_inline`, this kind of inlining is + performed later in the compilation pipeline. + +- The `__include` hack has been removed now that we have proper import support. + +- `__mlir_op` can now get address of l-value: + + You can use magic `(((x)))` syntax in __mlir_op that forces the `x` + expression to be an lvalue, and yields its address. This provides an escape + hatch (isolated off in `__mlir_op` land) that allows unsafe access to lvalue + addresses. + +- We now support `__rlshift__` and `__rtruediv__`. + +- 📢 The parser now resolves scoped alias references. This allows us to support + things like `SomeType.someAlias`, forward substituting the value. This + unblocks use of aliases in types like `DType`. We'd like to eventually + preserve the reference in the AST, but this unblocks library development. + +- 📚 Add a `now` function and `Benchmark` struct to enable timing and + benchmarking. + +- 📚 Move more of the computation in NDBuffer from runtime to compile time if + possible (e.g. when the dimensions are known at compile time). + +### Week of 2023-01-02 + +- 📚 Added the `print` function which works on Integers and SIMD values. + +- The frontend now has a new diagnostic subsystem used by the `kgen` tool (but + not by `kgen-translate` for tests) that supports source ranges on + diagnostics. Before we'd emit an error like: + + ```log + x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' + callee(1.0+F32(2.0)) + ^ + x.lit:4:1: note: function declared here + fn callee(a: Int): + ^ + ``` + + now we produce: + + ```log + x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' + callee(1.0+F32(2.0)) + ^ ~~~~~~~~~~~~ + x.lit:4:1: note: function declared here + fn callee(a: Int): + ^ + ``` + +- 📢 Parameter results are now supported in a proper way. They are now forward + declared with an alias declaration and then bound in a call with an arrow, + e.g.: + + ```mojo + alias a : __mlir_type.index + alias b : __mlir_type.index + idx_result_params[xyz*2 -> a, b]() + ``` + +- Various minor issues with implicit conversions are fixed. For instances, + implicit conversions are now supported in parameter binding contexts and + `alias` declarations with explicit types. +- Doc strings are allowed on functions and structs, but they are currently + discarded by the parser. + +- 📚 Add a `print` method!!! + +- 📚 Demonstrate a naive matmul in Mojo. + +- 📚 Initial work on functions that depend on types (e.g. FPUtils, nan, inf, + etc.) + +- 📚 Allow one to query hardware properties such as simd_width, os, etc. via + TargetInfo at compile time. + +## December 2022 + +### Week of 2022-12-26 + +- 📢 You can now call functions in a parameter context! Calling a function in + a parameter context will evaluate the function at compile time. The result + can then be used as parameter values. For example, + + ```mojo + fn fma(x: Int, y: Int, z: Int) -> Int: + return a + b * c + + fn parameter_call(): + alias nelts = fma(32, 2, 16) + var x: SIMD[f32, nelts] + ``` + +- You can now disable printing of types in an `__mlir_attr` substitution by + using unary `+` expression. + +- 📢 `let` declarations are now supported in functions. `let` declarations are + local run-time constant values, which are always rvalues. They complement + 'var' decls (which are mutable lvalues) and are the normal thing to use in + most cases. They also generate less IR and are always in SSA form when + initialized. + + We will want to extend this to support 'let' decls in structs at some point + and support lazy initialized 'let' declarations (using dataflow analysis) but + that isn't supported yet. + +- 📚 Add the NDBuffer struct. + +- Happy new year. + +### Week of 2022-12-19 + +- 📚 Start of the Standard library: + 1. Added Integer and SIMD structs to bootstrap the standard library. + 2. Added very basic buffer data structure. + +- We have basic support for parsing parameter results in function calls! Result + parameters are an important Mojo metaprogramming feature. They allow functions + to return compile-time constants. + + ```mojo + fn get_preferred_simdwidthof[() -> nelts: Int](): + return[2] + + fn vectorized_function(): + get_preferred_simdwidthof[() -> nelts]() + var x: SIMD[f32, nelts] + ``` + +- Types can now be used as parameters of `!kgen.mlirtype` in many more cases. + +- MLIR operations with zero results don't need to specify `_type: []` anymore. + +- We support parsing triple quoted strings, for writing docstrings for your + functions and structs! + +- A new `__mlir_type[a,b,c]` syntax is available for substituting into MLIR + types and attributes is available, and the old placeholder approach is + removed. This approach has a few advantages beyond what placeholders do: + + 1. It's simpler. + 2. It doesn't form the intermediate result with placeholders, which + gets rejected by MLIR's semantic analysis, e.g. the complex case + couldn't be expressed before. + 3. It provides a simple way to break long attrs/types across multiple + lines. + +- We now support an `@evaluator` decorator on functions for KGEN evaluators. + This enables specifying user-defined interface evaluators when performing + search during compilation. + +- 📢 `import` syntax is now supported! + + This handles packaging imported modules into file ops, enables effective + isolation from the other decls. "import" into the desired context is just + aliasing decls, with the proper symbols references handle automatically during + IR generation. As a starting point, this doesn't handle any notion of packages + (as those haven't been sketched out enough). + +- 📢 Reversed binary operators (like `__radd__`) are now looked up and used if + the forward version (like `__add__`) doesn't work for some reason. + +- 📢 Implicit conversions are now generally available, e.g. in assign + statements, variable initializers etc. There are probably a few more places + they should work, but we can start eliminating all the extraneous explicit + casts from literals now. + +- Happy Holidays + +### Week of 2022-12-12 + +- 📢 Function overloading now works. Call resolution filters candidate list + according to the actual parameter and value argument specified at the site of + the call, diagnosing an error if none of the candidates are viable or if + multiple are viable and ambiguous. We also consider implicit conversions in + overload look: + + ```mojo + fn foo(x: Int): pass + fn foo(x: F64): pass + + foo(Int(1)) # resolves to the first overload + foo(1.0) # resolves to the second overload + foo(1) # error: both candidates viable with 1 implicit conversion! + ``` + +- The short circuiting binary `and` and `or` expressions are now supported. + +- Unary operator processing is a lot more robust, now handling the `not` + expression and `~x` on Bool. + +- 📢 The compiler now generates debug information for use with GDB/LLDB that + describes variables and functions. + +- The first version of the Mojo Visual Studio Code extension has been released! + It supports syntax highlighting for Mojo files. + +- The first version of the `Bool` type has landed in the new Mojo standard + library! + +- 📢 Implicit conversions are now supported in return statements. + +### Week of 2022-12-05 + +- "Discard" patterns are now supported, e.g. `_ = foo()` + +- We now support implicit conversions in function call arguments, e.g. + converting an `index` value to `Int` automatically. This eliminates a bunch + of casts, e.g. the need to say F32(1.0) everywhere. + + This is limited for a few reasons that will be improved later: + 1. We don't support overloading, so lots of types aren't convertible + from all the things they should be, e.g. you can't pass "1" to + something that expects F32, because F32 can't be created from index. + 2. This doesn't "check to see if we can invoke `__new__`" it force applies + it on a mismatch, which leads to poor QoI. + 3. This doesn't fix things that need radd. + +## November 2022 + +### Week of 2022-11-28 + +- 📢 We support the `True` and `False` keywords as expressions. + +- 📢 A new `alias` declaration is supported which allows defining local + parameter values. This will eventually subsume type aliases and other + things as it gets built out. + +- 📢 We now have end-to-end execution of Mojo files using the `kgen` tool! + Functions exported with `@export` can be executed. + +- 📢 We have try-except-else and `raise` statements and implicit error + propagation! The error semantics are that `def` can raise by default, but `fn` + must explicitly declare raising with a `@raises` decorator. Stub out basic + `Error` type. + +- The `&` sigil for by-ref arguments is now specified after the identifier. + Postfix works better for ref and move operators on the expression + side because it chains an mentally associates correctly: + `thing.method().result^`. We don't do that yet, but align param + decl syntax to it so that things won't be odd looking when we do. + In practice this looks like: + + ```mojo + def mutate_argument(a&: index): + a = 25 + ``` + +### Week of 2022-11-21 + +- 📢 The magic `index` type is gone. Long live `__mlir_type.index`. + +- Implement parameter substitution into parametric `__mlir_type` decls. This + allows us to define parametric opaque MLIR types with exposed parameters using + a new "placeholder" attribute. This allows us to expose the power of the KGEN + type parametric system directly into Mojo. + +- 📢 Fully-parametric custom types can now be defined and work in Mojo, bringing + together a lot of the recent work. We can write the SIMD type directly as a + wrapper around the KGEN type, for example: + + ```mojo + struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]: + var value: + __mlir_type.`!pop.simd<#lit, + #lit>`[nelts, dt] + + fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]: + return __mlir_op.`pop.add`(self.value, rhs.value) + ``` + +### Week of 2022-11-14 + +- 📢 Implement a magic `__mlir_type` declaration that can be used to access any + MLIR type. E.g. `__mlir_type.f64`. + +- 📢 Add an `fn` declaration. These are like `def` declarations, but are more + strict in a few ways: they require type annotations on arguments, don't allow + implicit variable declarations in their body, and make their arguments rvalues + instead of lvalues. + +- Implemented Swift-style backtick identifiers, which are useful for code + migration where names may collide with new keywords. + +- 📢 A new `__include` directive has been added that performs source-level + textual includes. This is temporary until we have an `import` model. + +- Implement IR generation for arithmetic operators like `+` and `*` in terms + of the `__add__` and `__mul__` methods. + +- 📢 Added support for `break` and `continue` statements, as well as early + returns inside loops and conditionals! + +- 📢 Implemented augmented assignment operators, like `+=` and `@=`. + +- 📢 Mojo now has access to generating any MLIR operations (without regions) + with a new `__mlir_op` magic declaration. We can start to build out the + language's builtin types with this: + + ```mojo + struct Int: + var value: __mlir_type.index + + fn __add__(self, rhs: Int) -> Int: + return __mlir_op.`index.add`(self.value, rhs.value) + ``` + + Attributes can be attached to the declaration with subscript `[]` syntax, + and an explicit result type can be specified with a special `_type` attribute + if it cannot be inferred. Attributes can be accessed via the `__mlir_attr` + magic decl: + + ```mojo + __mlir_op.`index.cmp`[ + _type: __mlir_type.i1, + pred: __mlir_attr.`#index` + ](lhs, rhs) + ``` + +- Improved diagnostics emissions with ranges! Now errors highlight the whole + section of code and not just the first character. + +### Week of 2022-11-07 + +- Implemented the `@interface` and `@implements` decorators, which provide + access to KGEN generator interfaces. A function marked as an `@interface` + has no body, but it can be implemented by multiple other functions. + + ```mojo + @interface + def add(lhs: index, rhs: index): + + @implements(add) + def normal_add(lhs: index, rhs: index) -> index: + return lhs + rhs + + @implements(add) + def slow_add(lhs: index, rhs: index) -> index: + wait(1000) + return normal_add(lhs, rhs) + ``` + +- 📢 Support for static struct methods and initializer syntax has been added. + Initializing a struct with `Foo()` calls an implicitly static `__new__` + method. This method should be used instead of `__init__` inside structs. + + ```mojo + struct Foo: + var value: index + + def __new__() -> Foo: + var result: Foo + result.value = Foo.return_a_number() # static method! + return result + + @staticmethod + def return_a_number() -> index: + return 42 + ``` + +- 📢 Full by-ref argument support. It's now possible to define in-place + operators like `__iadd__` and functions like `swap(x, y)` correctly. + +- 📢 Implemented support for field extract from rvalues, like `x.value` where + `x` is not an lvalue (`var` declaration or by-ref function argument). + +## October 2022 + +### Week of 2022-10-31 + +- Revised `return` handling so that a return statement with no expression is + syntax sugar for `return None`. This enables early exits in functions that + implicitly return `None` to be cleaner: + + ```mojo + def just_return(): + return + ``` + +- Added support for parsing more expressions: if-else, bitwise operators, + shift operators, comparisons, floor division, remainder, and matmul. + +- 📢 The type of the `self` argument can now be omitted on member methods. + +### Week of 2022-10-24 + +- Added parser support for right-associativity and unary ops, like the power + operator `a ** b ** c` and negation operator `-a`. + +- Add support for `&expr` in Mojo, which allows denoting a by-ref argument in + functions. This is required because the `self` type of a struct method is + implicitly a pointer. + +- Implemented support for parametric function declarations, such as: + + ```mojo + struct SIMD[dt: DType, width: index]: + fn struct_method(self: &SIMD[dt, width]): + pass + + def fancy_add[dt: DType, width: index]( + lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index: + return width + ``` + +### Week of 2022-10-17 + +- Added explicit variable declarations with `var`, for declaring variables both + inside functions and structs, with support for type references. Added `index` + as a temporary built-in type. + + ```mojo + def foo(lhs: index, rhs: index) -> index: + var result: index = lhs + rhs + return result + ``` + +- Implemented support for parsing struct declarations and references to type + declarations in functions! In `def`, the type can be omitted to signal an + object type. + + ```mojo + struct Foo: + var member: index + + def bar(x: Foo, obj) -> index: + return x.member + ``` + +- Implemented parser support for `if` statements and `while` loops! + + ```mojo + def if_stmt(c: index, a: index, b: index) -> index: + var result: index = 0 + if c: + result = a + else: + result = b + return result + + def while_stmt(init: index): + while init > 1: + init = init - 1 + ``` + +- Significantly improved error emission and handling, allowing the parser to + emit multiple errors while parsing a file. + +### Week of 2022-10-10 + +- Added support for parsing integer, float, and string literals. + +- Implemented parser support for function input parameters and results. You can + now write parametric functions like, + + ```mojo + def foo[param: Int](arg: Int) -> Int: + result = param + arg + return result + ``` + +### Week of 2022-10-03 + +- Added some basic parser scaffolding and initial parser productions, including + trivial expressions and assignment parser productions. +- Implemented basic scope handling and function IR generation, with support for + forward declarations. Simple functions like, + + ```mojo + def foo(x: Int): + ``` + + Now parse! But all argument types are hard-coded to the MLIR `index` type. + +- Added IR emission for simple arithmetic expressions on builtin types, like + `x + y`. + +## September 2022 + +### Week of 2022-09-26 + +- Mojo's first patch to add a lexer was Sep 27, 2022. + +- Settled on `[]` for Mojo generics instead of `<>`. Square brackets are + consistent with Python generics and don't have the less than ambiguity + other languages have. diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index a29f6061dc..cd145d97f5 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -312,17 +312,24 @@ "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", "Mojo in the future.\n", "\n", - "Inside the function body, the variadic argument is available an iterable list\n", + ":::note No variadic keyword-only arguments\n", + "\n", + "Variadic keyword-only arguments (`**kwargs`) are not yet supported in Mojo. We\n", + "plan to support these in the future.\n", + "\n", + ":::\n", + "\n", + "Inside the function, the variadic argument is projected into an iterable list\n", "for ease of use. But there are some differences in handling the list depending\n", "on whether the arguments are register-passable types (such as `Int`) or\n", "memory-only types (such as `String`).\n", "\n", - "Register-passable types, such as `Int`, are available as a \n", + "Register-passable types, such as `Int`, are projected into a \n", "[`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", "loop.\n", "\n", - "Memory-only types, such as `String`, are available as a \n", + "Memory-only types, such as `String`, are projected into a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", "[`Reference`](/mojo/stdlib/memory/unsafe#reference) for each value instead\n", @@ -366,83 +373,12 @@ "\n", "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", "(parameters are used for compile-time metaprogramming). However, most rules\n", - "that apply to argument lists also apply to parameter lists. Variadic parameters\n", - "are supported, but with some limitations—for details see \n", - "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", + "that apply to argument lists also apply to parameter lists, including variadics.\n", + "Variadic parameters are declared and used like variadic arguments.\n", "\n", ":::\n" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Variadic keyword arguments\n", - "\n", - "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", - "keyword arguments allow the user to pass an arbitrary number of keyword\n", - "arguments. To define a function that takes a variadic keyword argument, use the\n", - "variadic keyword argument syntax **kw_argument_name:\n", - "\n", - " ```mojo\n", - " fn print_nicely(**kwargs: Int) raises:\n", - " for key in kwargs.keys():\n", - " print(key[], \"=\", kwargs[key[]])\n", - "\n", - " # prints:\n", - " # `a = 7`\n", - " # `y = 8`\n", - " print_nicely(a=7, y=8)\n", - " ```\n", - "\n", - " In this example, the argument name `kwargs` is a placeholder that accepts any\n", - " number of keyword arguments. Inside the body of the function, you can access\n", - " the arguments as a [`Dict`](/mojo/stdlib/collections/dict) of keywords and\n", - " argument values.\n", - " \n", - " There are currently a few limitations:\n", - "\n", - " - Variadic keyword arguments are always implicitly treated as if they\n", - " were declared with the `owned` [argument \n", - " convention](/mojo/manual/values/ownership.html#argument-conventions), and\n", - " can't be declared otherwise:\n", - "\n", - " ```mojo\n", - " # Not supported yet.\n", - " fn borrowed_var_kwargs(borrowed **kwargs: Int): ...\n", - " ```\n", - "\n", - " - All the variadic keyword arguments must have the same type, and this\n", - " determines the type of the argument dictionary. For example, if the argument\n", - " is `**kwargs: Float64` then the argument dictionary will be a \n", - " `Dict[String, Float64]`.\n", - "\n", - " - Functions with variadic keyword arguments can't have default values for\n", - " keyword-only arguments. For example:\n", - "\n", - " ```mojo\n", - " # Not allowed yet, because `b` is keyword-only with a default.\n", - " fn not_yet(*, b: Int = 9, **kwargs: Int): ...\n", - "\n", - " # Okay, because `c` is positional-or-keyword, so it can have a default.\n", - " fn still_works(c: Int = 5, **kwargs: Int): ...\n", - " ```\n", - "\n", - " - Dictionary unpacking is not supported yet:\n", - "\n", - " ```mojo\n", - " fn takes_dict(d: Dict[String, Int]):\n", - " print_nicely(**d) # Not supported yet.\n", - " ```\n", - "\n", - " - Variadic keyword _parameters_ are not supported yet:\n", - "\n", - " ```mojo\n", - " # Not supported yet.\n", - " fn var_kwparams[**kwparams: Int](): ...\n", - " ```" - ] - }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 3fe41ff422..0fcb56014a 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -55,7 +55,7 @@ Linux: - Ubuntu 20.04/22.04 LTS - x86-64 CPU (with [SSE4.2 or newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) - or AWS Graviton2/3 CPU + or AWS Graviton2 CPU or newer - Minimum 8 GiB RAM - Python 3.8 - 3.11 - g++ or clang++ C++ compiler diff --git a/docs/manual/packages.md b/docs/manual/packages.md index c94aac8437..46e719921b 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -51,7 +51,7 @@ that's in the same directory as `mymodule.mojo`: from mymodule import MyPair fn main(): - var mine = MyPair(2, 4) + let mine = MyPair(2, 4) mine.dump() ``` @@ -62,7 +62,7 @@ through the module name. For example: import mymodule fn main(): - var mine = mymodule.MyPair(2, 4) + let mine = mymodule.MyPair(2, 4) mine.dump() ``` @@ -72,7 +72,7 @@ You can also create an alias for an imported member with `as`, like this: import mymodule as my fn main(): - var mine = my.MyPair(2, 4) + let mine = my.MyPair(2, 4) mine.dump() ``` @@ -124,7 +124,7 @@ name like this: from mypackage.mymodule import MyPair fn main(): - var mine = MyPair(2, 4) + let mine = MyPair(2, 4) mine.dump() ``` diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 31a4480582..d644bf3741 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -127,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -193,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -291,7 +291,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -351,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -435,7 +435,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -484,7 +484,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -546,7 +546,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -578,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -626,6 +626,8 @@ "Mojo supports positional-only and keyword-only parameters, following the same\n", "rules as [positional-only and keyword-only\n", "arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).\n", + "As with arguments, variadic keyword arguments (for example, `**kwparams`) are\n", + "not supported yet.\n", "\n", ":::" ] @@ -636,13 +638,13 @@ "source": [ "## Variadic parameters\n", "\n", - "Mojo also supports variadic parameters, similar to \n", + "Mojo also supports variadic parameters, following the same rules described for \n", "[Variadic arguments](/mojo/manual/functions#variadic-arguments):" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -654,33 +656,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Variadic parameters have some limitations that variadic arguments don't have:\n", - "\n", - "- Only variadic parameters of register-passable types are supported currently.\n", - "\n", - "- The parameters aren't automatically projected into a `VariadicList`, so you\n", - " need to construct the list explicitly:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn sum_params[*values: Int]() -> Int:\n", - " alias list = VariadicList(values)\n", - " var sum = 0\n", - " for v in list:\n", - " sum += v\n", - " return sum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Variadic keyword parameters (for example, `**kwparams`) are\n", + "As with arguments, variadic keyword parameters (for example, `**kwparams`) are\n", "not supported yet." ] }, @@ -1010,13 +986,21 @@ " ```\n", "\n", "You can also use the star-underscore expression `*_` to unbind an arbitrary\n", - "number of positional parameters at the end of a parameter\n", + "number of positional parameters at the start, middle, or end of a parameter\n", "list.\n", "\n", "```mojo\n", "# These two types are equivalent\n", "MyType[\"Hello\", *_]\n", "MyType[\"Hello\", _, _, _]\n", + "\n", + "# These two types are equivalent\n", + "MyType[*_, False]\n", + "MyType[_, _, _, False]\n", + "\n", + "# These two types are equivalent\n", + "MyType[*_]\n", + "MyType[_, _, _, _]\n", "```\n", "\n", "When a parameter is explicitly unbound with the `_` or `*_` expression, you\n", diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 40fdb69ba3..0052dc3630 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -36,9 +36,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quack.\n", + "Moo!\n" + ] + } + ], "source": [ "%%python\n", "class Duck:\n", @@ -119,10 +128,10 @@ "This isn't too bad with only two classes. But the more classes you want to\n", "support, the less practical this approach is.\n", "\n", - "You might notice that the Mojo versions of `make_it_quack()` don't include the\n", - "`try/except` statement. We don't need it because Mojo's static type checking\n", - "ensures that you can only pass instances of `Duck` or `StealthCow` into the \n", - "`make_it_quack()`function.\n", + "You might notice that the Mojo versions dosn't include the `try/except` \n", + "statement. We don't need it because Mojo's static type checking ensures that\n", + "you can only pass instances of `Duck` or `StealthCow` into the `make_it_quack()`\n", + "function.\n", "\n", "## Using traits\n", "\n", @@ -169,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -274,48 +283,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Implicit trait conformance\n", "\n", - "Mojo also supports _implicit_ trait conformance. That is, if a type implements\n", - "all of the methods required for a trait, it's treated as conforming to the\n", - "trait, even if it doesn't explicitly include the trait in its declaration:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "struct RubberDucky:\n", - " fn quack(self):\n", - " print(\"Squeak!\")\n", - "\n", - "make_it_quack(RubberDucky())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Implicit conformance can be handy if you're defining a trait and you want it to\n", - "work with types that you don't control—such as types from the standard library,\n", - "or a third-party library.\n", "\n", - "However, we still strongly recommend explicit trait conformance wherever\n", - "possible. This has two advantages:\n", - "\n", - "- Documentation. It makes it clear that the type conforms to the trait, without\n", - " having to scan all of its methods.\n", - "\n", - "- Future feature support. When default method implementations are added to\n", - " traits, they'll only work for types that explicitly conform to traits." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ "## Trait inheritance\n", "\n", "Traits can inherit from other traits. A trait that inherits from another trait\n", diff --git a/docs/oss-material/CODE_OF_CONDUCT.md b/docs/oss-material/CODE_OF_CONDUCT.md deleted file mode 100644 index f4f5ad5a25..0000000000 --- a/docs/oss-material/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,71 +0,0 @@ -# Code of conduct - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to make participation in our project and -our community a harassment-free experience for everyone, regardless of age, -body size, disability, ethnicity, gender identity and expression, level of -experience, nationality, personal appearance, race, religion, or sexual -identity and orientation. - -## Our standards - -All community forums and spaces are meant for professional interactions that -are friendly, inclusive, helpful, and collaborative. Examples of behavior that -contributes to creating a positive environment include: - -- Using welcoming and inclusive language. -- Being respectful of differing viewpoints and experiences. -- Gracefully accepting constructive criticism. -- Focusing on what is best for the community. -- Showing empathy towards other community members. - -Any behavior that could reasonably be considered inappropriate in a -professional setting is unacceptable. Examples of unacceptable behavior by -participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or - advances. -- Trolling, insulting/derogatory comments, and personal or political attacks. -- Public or private harassment. -- Publishing others' private information, such as a physical or electronic - address, without explicit permission. -- Conduct which could reasonably be considered inappropriate for the forum in - which it occurs. - -## Our responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies to all project content and public spaces on the -Mojo GitHub repo, the rest of Modular’s GitHub organization, and all other -official Mojo community spaces and communication mediums, whether offline or -online. - -## Enforcement - -Instances of abusive, harassment, or otherwise unacceptable behavior should be -reported to the project team at . All complaints will -be reviewed and investigated and will result in a response that is deemed -necessary and appropriate to the circumstances. The project team is obligated -to maintain confidentiality with regard to the reporter of an incident. Further -details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the Contributor Covenant, version 1.4, -available at , and includes some -aspects of the Geek Feminism Code of Conduct and the Drupal Code of Conduct. diff --git a/docs/oss-material/LICENSE b/docs/oss-material/LICENSE deleted file mode 100644 index c1af4075d5..0000000000 --- a/docs/oss-material/LICENSE +++ /dev/null @@ -1,235 +0,0 @@ -============================================================================================== -The Mojo repository is licensed under the Apache License v2.0 with LLVM Exceptions: -============================================================================================== - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ----- LLVM Exceptions to the Apache 2.0 License ---- - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you -may redistribute such embedded portions in such Object form without complying -with the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a -court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of -the License, but only in their entirety and only with respect to the Combined -Software. - -============================================================================== -Software from third parties included in the LLVM Project: -============================================================================== - -The LLVM Project contains third party software which is under different license -terms. All such code will be identified clearly using at least one of two -mechanisms: - -1) It will be in a separate directory tree with its own `LICENSE.txt` or - `LICENSE` file at the top containing the specific license and restrictions - which apply to that software, or -2) It will contain specific license and restriction terms at the top of every - file. diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 8053940c72..782e971f5f 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -338,7 +338,7 @@ "\n", "Currently Mojo expressions are limited to inspecting variables and their fields.\n", "The console also supports subscript notation (`vector[index]`) for certain data\n", - "structures in the standard library, including `List`, `SIMD`,\n", + "structures in the standard library, including `DynamicVector`, `SIMD`,\n", "and `ListLiteral`. \n", "\n", "In the future, we intend to provide a way for arbitrary data structures to\n", diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index a7cd64ac3c..2e8118a635 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -64,7 +64,7 @@ vectorize[$3, $1, $2] ### `DynamicVector` constructor `capacity` now keyword-only -The [`DynamicVector`](/mojo/stdlib/collections/list#list) struct had +The [`DynamicVector`](/mojo/stdlib/collections/vector#dynamicvector) struct had a constructor that took a single integer value for the vector's capacity. This had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. This was not intended to support implicit conversion, so `capacity` is now a @@ -82,7 +82,7 @@ Which in VS Code looks like this: ### `NDBuffer` signature change The shape of an -[`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) can +[`NDBuffer`](https://docs.modular.com/mojo/stdlib/memory/buffer#ndbuffer) can now default to being unknown, so the parameter list has been rearranged to accommodate this: @@ -113,7 +113,7 @@ NDBuffer[$3, $1, $2] ### Dereference `Variant` with `[]` -Previously, using [`Variant`](/mojo/stdlib/utils/variant#variant) +Previously, using [`Variant`](/mojo/stdlib/collections/vector#dynamicvector) was unsafe with heap allocated objects, it now returns a reference. If you had code that looks like this: diff --git a/examples/notebooks/HelloMojo.ipynb b/examples/notebooks/HelloMojo.ipynb index 0159f99c22..a60f594dd6 100644 --- a/examples/notebooks/HelloMojo.ipynb +++ b/examples/notebooks/HelloMojo.ipynb @@ -65,6 +65,7 @@ } ], "source": [ + "#| XFAIL: *\n", "#| CHECK: Hello Mojo!\n", "print(\"Hello Mojo!\")" ] diff --git a/stdlib/README.md b/stdlib/README.md index f9ec6c7224..7d3f90533a 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -1,6 +1,6 @@ -# Welcome to the Mojo standard library +# Welcome to the Mojo Standard Library -This folder contains the open-source Mojo standard library! 🔥 +This folder contains the open-source Mojo Standard Library! 🔥 We're thrilled to have you join our community of developers eager to build a vibrant and supportive ecosystem. If you just want to *use* the Mojo Standard @@ -10,7 +10,7 @@ standalone Mojo SDK: - [Get the MAX SDK.](https://docs.modular.com/engine/get-started) - [Get the Mojo SDK.](https://docs.modular.com/mojo/manual/get-started/) -## Vision and roadmap +## Vision and Roadmap We have written down the principles that inform our decisions about what features we work on and what bugs we prioritize. Before you consider making @@ -22,28 +22,28 @@ guide our development efforts. - Our [Roadmap](./docs/roadmap.md) identifies concrete development goals as we work towards an even more robust and feature-rich standard library. -## Contributing to the Mojo standard library +## Contributing to the Mojo Standard Library As a contributor, your efforts and expertise are invaluable in driving the -evolution of the Mojo standard library. The [Mojo contributor -guide](../CONTRIBUTING.md) provides all the information necessary to make +evolution of the Mojo Standard Library. The [Mojo Contributor +Guide](../CONTRIBUTING.md) provides all the information necessary to make meaningful contributions—from understanding the submission process to adhering to best practices: -- [Mojo contributor guide](../CONTRIBUTING.md) +- [Mojo Contributor Guide](../CONTRIBUTING.md) -## Getting started +## Getting Started Follow our provided documentation to prepare your environment for building the standard library, and then test your setup with our automated testing suite. For additional information, the FAQ page is your go-to resource. -- [Mojo standard library development](./docs/development.md) +- [Mojo Standard Library Development](./docs/development.md) - [FAQ](./docs/faq.md) -## Code of conduct +## Code of Conduct -[Code of conduct](../CODE_OF_CONDUCT.md) +[Code of Conduct](../CODE_OF_CONDUCT.md) ## License @@ -55,5 +55,5 @@ See the license file in the repository for more details. For any inquiries, bug reports, or feature requests, please [open an issue](https://github.com/modularml/mojo/issues) on the GitHub repository. See -the [Mojo contributor guide](../CONTRIBUTING.md) for guidelines on filing good +the [Mojo Contributor Guide](../CONTRIBUTING.md) for guidelines on filing good bugs. diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 362da08323..e0847de2b6 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -1,90 +1,85 @@ -# Mojo standard library development +# Mojo Standard Library Development -This document covers the essentials of developing for the standard library. +This document covers the essentials of getting started developing for the +standard library. -## Prerequisites +## Getting the Nightly Mojo Compiler -If this is your first time contributing, first read everything in -[CONTRIBUTING.md](../../CONTRIBUTING.md#fork-and-clone-the-repo). Logistically, -you need to do the following: - -1. [Fork and clone the repo](../../CONTRIBUTING.md#fork-and-clone-the-repo) -2. [Branch off nightly](../../CONTRIBUTING.md#branching-off-nightly) -3. [Install the nightly Mojo compiler](../../CONTRIBUTING.md#getting-the-nightly-mojo-compiler) - -And if you're using VS Code: - -- [Install the nightly VS Code - extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly) - -## Building the standard library - -To build the standard library, you can run the -[`build-stdlib.sh`](../scripts/build-stdlib.sh) script from the -`mojo/stdlib/scripts/` directory. This will create a build artifacts directory, -`build`, in the top-level of the repo and produce the `stdlib.mojopkg` inside. +To get started, you need to install the latest nightly mojo compiler. The +Standard Library only guarantees compatibility with the latest nightly `mojo` +compiler. ```bash -./stdlib/scripts/build-stdlib.sh +modular auth +modular install nightly/mojo ``` -## Testing the standard library +If you already have an older `nightly/mojo` compiler, replace +`modular install nightly/mojo` with `modular update nightly/mojo`. -### Installing unit test dependencies - -To run the unit tests, you first need to install `lit`: +Then, follow the instructions from the `modular` tool in adding the `mojo` +compiler to your `PATH` such as: ```bash -python3 -m pip install lit +echo 'export MODULAR_HOME="/Users/joe/.modular"' >> ~/.zshrc +echo 'export PATH="/Users/joe/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc ``` -And make sure that `FileCheck` from LLVM is on path. If your are on macOS, you -can `brew install llvm` and add it to your path in `~/.zshrc` or `~/.bashrc`: +## Cloning the Repository ```bash -export PATH="/opt/homebrew/opt/llvm/bin:$PATH" +git clone https://github.com/modularml/mojo/ ``` -If you are on Ubuntu you can: +## Building the Standard Library -```bash -sudo apt update +To build the Standard Library, you can run the script +[`build-stdlib.sh`](../scripts/build-stdlib.sh) from the `scripts` directory +inside the `stdlib` directory. This will create a build artifacts directory, +`build`, in the top-level of the repo and produce the `stdlib.mojopkg` inside. -sudo apt install llvm +```bash +./stdlib/scripts/build-stdlib.sh ``` -And it will be available in `PATH`. +## Testing the Standard Library + +### Installing Unit Test Dependencies -In the near future, we will be moving away from `FileCheck` in favor of writing -the unit tests using our own `testing` module and remove this dependency -requirement for contributors. We are happy to welcome contributions in this -area! +To run the unit tests, you first need to install a few dependencies: -### Running the standard library tests +1. [`lit`](https://llvm.org/docs/CommandGuide/lit.html) - can be downloaded via + `python3 -m pip install lit` +2. [`FileCheck`](https://llvm.org/docs/CommandGuide/FileCheck.html) - is part of + the LLVM distribution you can obtain from your package manager -We provide a simple Bash script to build the standard library package and -`test_utils` package that is used by the test suite. +When you download `lit`, make sure you add the path to `lit` to your `PATH` if +needed. Some of the tests use `FileCheck` or `not` binaries that ship with LLVM. +For example, if you download LLVM via `homebrew`, these would be in +`/opt/homebrew/Cellar/llvm//bin`. You need to add this path +to your `PATH` in order to run these tests. In the near future, we will be +moving away from `FileCheck` in favor of writing the unit tests using our own +`testing` module and remove this dependency requirement for contributors. We +are happy to welcome contributions in this area! -Just run `./stdlib/scripts/run-tests.sh` which will produce the necessary -`mojopkg` files inside your `build` directory, and then run `lit -sv -stdlib/test`. +### Running the Standard Library Tests + +We provide a simple Bash script to build the Standard Library package and +`test_utils` package that is used by the test suite. Just run +`./stdlib/scripts/run-tests.sh` which will produce the necessary +`mojopkg` files inside your `build` directory and then run +`lit -sv stdlib/test`. ```bash ./stdlib/scripts/run-tests.sh - -lit -sv stdlib/test ``` -All the tests should pass on the `nightly` branch with the nightly Mojo -compiler. If you've pulled the latest changes and they're still failing please -[open a GitHub -issue](https://github.com/modularml/mojo/issues/new?assignees=&labels=bug%2Cmojo&projects=&template=mojo_bug_report.yaml&title=%5BBUG%5D). - ### Running a subset of the Standard Library Unit Tests If you’d like to run just a subset of the tests, feel free to use all of the normal options that the `lit` tool provides. For example, to run just the -`builtin` and `collections` tests: +builtin and collections tests, you can ```bash lit -sv stdlib/test/builtin stdlib/test/collections @@ -105,9 +100,9 @@ If you run into any issues when running the tests, [please file an issue](https://github.com/modularml/mojo/issues) and we’ll take a look. -## Formatting changes +## Formatting Changes -Please make sure your changes are formatted before submitting a pull request. +Please make sure your changes are formatted before submitting a Pull Request. Otherwise, CI will fail in its lint and formatting checks. The `mojo` compiler provides a `format` command. So, you can format your changes like so: @@ -124,222 +119,3 @@ git diff origin/main --name-only -- '*.mojo' | xargs mojo format You can also consider setting up your editor to automatically format Mojo files upon saving. - -## Tutorial - -Here is a complete walkthrough, showing how to make a change to the Mojo -standard library, test it, and raise a PR. - -First, follow everything in the [prerequisites](#prerequisites). - -__IMPORTANT__ We'll be in the `mojo/stdlib` folder for this tutorial, check and -make sure you're in that location if anything goes wrong: -`cd [path-to-repo]/stdlib` - -### A simple change - -Let's try adding a small piece of functionality to `path.mojo`: - -```mojo -# ./stdlib/src/pathlib/path.mojo - -fn print_cwd() raises: - print("cwd:", cwd()) -``` - -And make sure you're importing it from the module root: - -```mojo -# ./stdblib/src/pathlib/__init__.mojo - -from .path import ( - DIR_SEPARATOR, - cwd, - print_cwd, - Path, -) -``` - -Now you can create a temporary file named `main.mojo` for trying out the new -behavior. You wouldn't commit this file, it's just to experiment with the -functionality before you write tests: - -```mojo -# ./stdlib/main.mojo - -from src import pathlib - -def main(): - pathlib.print_cwd() -``` - -Now when you run `mojo main.mojo` it'll reflect the changes: - -```plaintext -cwd: /Users/jack/src/mojo/stdlib -``` - -### A change with dependencies - -Here's a more tricky example that modifies multiple standard library files that -depend on each other. - -Try adding this to `os.mojo`, which depends on what we just added to -`pathlib.mojo`: - -```mojo -# ./stdlib/src/os/os.mojo - -import pathlib - -fn print_paths() raises: - pathlib.print_cwd() - for path in cwd().listdir(): - print(path[]) -``` - -This won't work because it's importing `pathlib` from the `stdlib.mojopkg` that -comes with Mojo. We'll need to build our just-modified standard library running -the command: - -```bash -./scripts/build-stdlib.sh -``` - -This builds the standard library and places it at the root of the repo in -`../build/stdlib.mojopkg`. Now we can edit `main.mojo` to use the normal import -syntax: - -```mojo -import os - -def main(): - os.print_paths() -``` - -We also need to set the following environment variable that tells Mojo to -prioritize imports from the standard library we just built, over the one that -ships with Mojo: - -```bash -MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build mojo main.mojo -``` - -Which now outputs: - -```plaintext -cwd: /Users/jack/src/mojo/stdlib -main.mojo -test -docs -scripts -src -``` - -### Bonus tip: fast feedback loop - -If you like a fast feedback loop, try installing the file watcher `entr`, which -you can get with `sudo apt install entr` on Linux, or `brew install entr` on -macOS. Now run: - -```bash -export MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build - -ls **/*.mojo | entr sh -c "./scripts/build-stdlib.sh && mojo main.mojo" -``` - -Now, every time you save a Mojo file, it packages the standard library and -runs `main.mojo`. - -### Running tests - -If you haven't already, follow the steps at: -[Installing unit test dependencies](#installing-unit-test-dependencies) - -### Adding a test - -This will show you how the `FileCheck` utility works. First, turn it on by -adding it to the end of line 7 in `./stdlib/test/pathlib/test_pathlib.mojo`: - -```mojo -# RUN: %mojo -debug-level full -D TEMP_FILE=%t %s | FileCheck %s -``` - -Now we can add the test and force it to fail: - -```mojo -# CHECK-LABEL: test_print_cwd - -def test_print_cwd(): - print("== test_print_cwd") - - # CHECK: This will fail - print_cwd() -``` - -Don't forget to call it from `main()`: - -```bash -def main(): - test_print_cwd() -``` - -Now, instead of testing all the modules, we can just test `pathlib`: - -```bash -lit -sv test/pathlib -``` - -This will give you an error showing exactly what went wrong: - -```plaintext -/Users/jack/src/mojo-toy-2/stdlib/test/pathlib/test_pathlib.mojo:27:11: -error: CHECK: expected string not found in input - -# CHECK: This will fail - ^ -:1:18: note: scanning from here -== test_print_cwd - ^ -``` - -Lets fix the test that we just added: - -```mojo -# CHECK-LABEL: test_print_cwd - -def test_print_cwd(): - print("== test_print_cwd") - - # CHECK: cwd: - print_cwd() -``` - -We're now checking that `print_cwd` is prefixed with `cwd:` just like the -function we added. Run the test again: - -```plaintext -Testing Time: 0.65s - -Total Discovered Tests: 1 - Passed: 1 (100.00%) -``` - -Success! Now we have a test for our new function. - -The last step is to [run mojo format](#formatting-changes) on all the files. - -### Raising a PR - -Make sure that you've had a look at all the materials from the standard library -[README.md](../README.md). This change wouldn't be accepted because it's missing -tests, and doesn't add useful functionality that warrants new functions. If you -did have a worthwhile change you wanted to raise, follow the steps to -[create a pull request](../../CONTRIBUTING.md#create-a-pull-request). - -Congratulations! You've now got an idea on how to contribute to the standard -library, test your changes, and raise a PR. - -If you're still having troubles make sure to reach out on -[GitHub](https://github.com/modularml/mojo/discussions/new?category=general) or -[Discord](modul.ar/discord)! diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index be9780fa05..8fc6d46217 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -1,68 +1,59 @@ -# Frequently asked questions +# Frequently Asked Questions -A lot of questions about Mojo as a whole are answered in the +A lot of questions about Mojo as a whole can be answered on the [FAQ on our website](https://docs.modular.com/mojo/faq). -This FAQ is specifically focused on the standard library with contributors +This document is specifically focused on the Standard Library with contributors in mind. -## Contributing & development +## Standard Library -### 1. What platforms does Mojo support? +### Contributing & Development -The nightly Mojo compiler currently works on Linux and macOS. The standard -library works on both platforms too in conjunction with the compiler. Windows is +#### 1. What platforms does Mojo support? + +The nightly Mojo compiler currently works on Linux and macOS. The Standard +Library works on both platforms too in conjunction with the compiler. Windows is currently not a supported platform. -### 2. I hit a bug! What do I do? +#### 2. I hit a bug! What do I do? Don’t Panic! 😃 Check out our [bug submission guide](../../CONTRIBUTING.md#submitting-bugs) to make sure you include all the essential information to avoid unnecessary delays in getting your issues resolved. -## Standard library code +### Standard Library Code -### 1. Why do we have both `AnyRegType` and `AnyType`? +#### 1. Why do we have both `AnyRegType` and `AnyType`? This is largely a historical thing as the library only worked on `AnyRegType` when it was first written. As we introduced the notion of memory-only types and -traits, `AnyType` was born. Over time, we expect to rewrite nearly +traits, `AnyType` was born. Over time in Q2 2024, we expect to rewrite nearly the entire library to have everything work on `AnyType` and be generalized to -not just work on `AnyRegType`. Several things need to happen in tandem with +not just work on `AnyRegType`. Several things need to be worked in tandem with the compiler team to make this possible. -### 2. Are the MLIR dialects private? +#### 2. LLCL and MLIR ops are private APIs? -The standard library makes use of internal MLIR dialects such as `pop`, `kgen`, -and `lit`. Currently, these are private, undocumented APIs. We provide +LLCL entry points and MLIR operators are private undocumented APIs. We provide no backward compatibility guarantees and therefore they can change at any time. -These particular areas of the compiler and standard library are in active -development and we are exploring how we can release them when their -public-facing API has stabilized. - -### 3. What is the compiler-runtime? - -Mojo depends on certain features that are still written in C++, collectively -called "the compiler runtime." This may manifest in the standard library code -through references like `KGEN_CompilerRT_LLCL_CreateRuntime`. Like the MLIR -dialects, the compiler runtime is currently private and undocumented. - -We plan on reducing the C++ dependencies in the future. +These particular areas of the standard library are in active development and we +commit to releasing them when their public-facing API has stabilized. -### 4. Why are some standard library modules missing from the open-source code? +#### 3. Why are some Standard Library modules missing from the open-source code? -When we were preparing to open source the standard library, we realized that +When we were preparing to open source the Standard Library, we realized that some modules weren't ready for open-source release. For example: - Some modules are expected to change rapidly in the near term, and need to stabilize. - Some modules are too tightly integrated into other portions of MAX and need to be refactored. -- Some modules have proprietary aspects that require additional review and +- Some modules may have proprietary aspects that require additional review and refinement. For the short term, we've left these modules as closed source. The shipped -Mojo SDK contains the pre-built Mojo packages for these closed-source modules +Mojo SDK contains the pre-built Mojo packages for these closed source modules in addition to the open-source modules, so Mojo users still have the full set of primitives available to them. diff --git a/stdlib/docs/governance-structure.md b/stdlib/docs/governance-structure.md index 9a26106e40..71b97bee08 100644 --- a/stdlib/docs/governance-structure.md +++ b/stdlib/docs/governance-structure.md @@ -1,4 +1,4 @@ -# Mojo standard library governance structure +# Mojo Standard Library Governance Structure **Leads:** Rob Parolin ([@rparolin](https://github.com/rparolin)), @@ -8,6 +8,6 @@ Leads are responsible for reviewing code changes, reviewing proposals for evolving the standard library, and setting the roadmap. Other projects have governance structures with the concept of sub-teams. The -Mojo standard library isn’t large enough yet to support that level of +Mojo Standard Library isn’t large enough yet to support that level of governance. It very well may in the future but as we scale we will re-evaluate our governance structure and make changes accordingly. diff --git a/stdlib/docs/images/base-branch.png b/stdlib/docs/images/base-branch.png deleted file mode 100644 index a65ba47490281d93c0d5f6ce695f05bacd80c3cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163923 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 Q0tq3FvBRFMvGGm$52szTO#lD@ diff --git a/stdlib/docs/images/create-fork.png b/stdlib/docs/images/create-fork.png deleted file mode 100644 index a708e4a4e459b15eab96c8c6d2c622f5b95adc93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179367 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

ZmP4lSCzr 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 z stdlib # stdlib root directory @@ -32,11 +30,11 @@ required to be a leaf dependency. All Mojo source files must end with the extension `.mojo` or `.🔥`. -#### Mojo format +#### Mojo Format Mojo provides a command line formatting utility, `mojo format`, designed to automatically format your code according to the official Mojo style guidelines. -It adjusts indentation, spacing, and line breaks, making code more readable and +It adjusts indentation, spacing, and line breaks making code more readable and consistent. ```bash @@ -45,7 +43,7 @@ All done! ✨ 🍰 ✨ 1 file left unchanged. ``` -Unless otherwise noted, Mojo standard library code should follow the formatting +Unless otherwise noted, Mojo Standard Library code should follow the formatting produced by `mojo format`. #### Whitespace @@ -56,13 +54,13 @@ produced by `mojo format`. *We encourage updating your editor settings to be consistent with the above.* -#### Column limit +#### Column Limit -Mojo code has a column limit (line length) of 80 characters. +Mojo code has a column limit of 80 characters. -#### File license header +#### File License Header -Every file in the open source Mojo standard library should begin with the +Every file in the open source Mojo Standard Library should begin with the following license information header: ```mojo @@ -80,9 +78,9 @@ following license information header: # ===----------------------------------------------------------------------=== # ``` -#### Code header comments +#### Code Header Comments -Code in the Mojo standard library should use the following conventional +Code in the Mojo Standard Library should use the following conventional structure of header comments separating the various kinds of methods that can be defined on structs. @@ -93,7 +91,7 @@ defined on structs. struct MyStruct(Sized, Stringable): - """Description goes here.""" + """This is MyStruct.""" var field: Int @@ -119,17 +117,14 @@ struct MyStruct(Sized, Stringable): # ===------------------------------------------------------------------=== # ``` -## Code conventions - -### Identifier naming conventions +## Code Conventions -There are several ways to capitalize and separate words, known as "case -styles." By following the same set of case styles in our code, Mojo developers -ensure their code is accessible and understandable to others in the community. +### Identifier Naming Conventions -This first table is just a definition of the various "case styles." +The following are the recommended types of `case styles` used in Mojo Standard +Library code. -| Case style | Description | Example +| Case Style | Description | Example |------------------------|-------------------------------------------|----------------- | `snake_case` | All lowercase with underscores | `variable_name` | `PascalCase` | Each word starts with an uppercase letter | `StructName` @@ -137,9 +132,11 @@ This first table is just a definition of the various "case styles." | `kebab-case` | All lowercase with hyphens | `project-name` | `flatcase` | All lowercase without separators | `basename` -The following table shows our preferred use of different case styles. +The following table outlines the appropriate use of various casing styles in the +Mojo Standard Library. By following these conventions, Mojo developers ensure +their code is accessible and understandable to others in the community. -| Code kind | Example | Case style +| Item Kind | Example | Case Convention |----------------------|--------------------------------|--------------------------- | `fn` / `def` | `fn engage_hyperdrive()` | `snake_case` | `struct` | `struct Point` | `PascalCase` @@ -157,14 +154,15 @@ The following table shows our preferred use of different case styles. | `fn` type parameter | `fn do_it[Action: Actionable](action: Action)` | `PascalCase` | `fn` value parameter | `fn repeat[Count: Int]()` | `PascalCase` -Although these are our style conventions, not all code currently adheres to it. -When preparing a new change, it is important to adhere to the style and naming -conventions already established in that module. Therefore, if the module you -are working on uses a different style, continue using that style to maintain -consistency. We are not currently accepting pull requests that propose -extensive formatting or renaming changes. +The demonstrated style choices intend to illustrate the various naming +conventions used in the Standard Library. However, these choices may not match +the existing style in all code in its current state. When preparing a change, it +is important to adhere to the style and naming conventions already established +in that module. Therefore, if the module you are working on uses a different +style, continue using that style to maintain consistency. We are not currently +accepting pull requests that propose extensive formatting or renaming changes. -### Naming guidelines +### Naming Guidelines #### ℹ️ Prefer descriptive parameter names over single-letter names @@ -180,7 +178,7 @@ struct Array[LENGTH: Int, ElementType: Movable] # 🔴 Avoid struct Array[ElementType: Movable, Length: Int] # 🟢 Preferred ``` -### Container lifecycle semantics +### Container Lifecycle Semantics #### ℹ️ Prefer explicit copy constructors; avoid allowing implicit copies @@ -197,7 +195,7 @@ safe and inexpensive. However, copying types that dynamically allocate memory can be expensive. This includes common types like `List`, `Dict`, `Set`, `Tensor`, and `String`. -Some standard library types allow implicit copies where they shouldn’t. We will +Some Standard Library types allow implicit copies where they shouldn’t. We will resolve this shortly as new Mojo language features are shipped to help with this very situation. @@ -205,7 +203,7 @@ When designing a new type, don’t allow implicit copies unless the copy is trivial (order `O(1)`). In other words, don’t define a `__copyinit__()` function if the copy is expensive. Instead, define an *explicit* copy constructor: an `__init__()` constructor that takes a value of -the same type: +the same type. ```mojo struct MyStruct: @@ -214,29 +212,29 @@ struct MyStruct: # do a deep copy of MyStruct ``` -### Import statements +### Import Statements -- Explicitly import entities used (functions, structs, aliases), rather - than rely on transitive imports. -- Import only what you use; in general, avoid using +- Explicitly import entities (functions, structs, aliases) used rather + than relying on transitive imports. +- Import only what you use; in general, prefer not to use `from some_package import *`. - Import statements should be sorted lexicographically. -### API docstrings +### API Docstrings -Every public function and public struct (including data fields) in the standard -library must have docstrings (code comments that describe the API behavior). -Mojo includes tooling to ensure that public functions include docstrings. +Every public function and public struct (including data fields) in the Standard +Library must have doc strings. There is tooling to ensure public functions +adhere to the doc string validation. You can run `./stdlib/scripts/check-doc-strings.sh` to validate -docstrings. If the command exits with a `0` exit code, the docstrings are +doc strings. If the command exits with a 0 exit code, the doc strings are compliant; otherwise, an error will be shown. This is also enforced by the LSP with warnings for anything that doesn’t conform, you can generate docstrings based on the signature using an LSP Quick Fix: - +![VS Code documentation lint quick fix](./images/doc-lint-quick-fix.png) -We follow Google's Python convention for +We follow the Google convention for [docstrings outlined here](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods) which looks like this: @@ -275,7 +273,7 @@ fn add_param_arg[foo: Int](bar: Int) -> Int: ### Testing -#### Unit test filenames +#### Unit Test Filenames All test filenames should be prefixes with `test_`. For example `test_sort.mojo`. diff --git a/stdlib/docs/vision.md b/stdlib/docs/vision.md index a015be8c79..ae8774b6fb 100644 --- a/stdlib/docs/vision.md +++ b/stdlib/docs/vision.md @@ -1,26 +1,23 @@ # Vision -This page outlines the principles we aspire to follow and the more concrete -objectives and goals we aim to accomplish in the Mojo standard library. - ## Principles -The following are “North Star” principles for the Mojo standard library. -These principles will inform multiple future decisions, from what features we +The following are “North Star” principles for the Mojo Standard Library. +These principles will inform multiple future decisions from what features we work on to what bugs we prioritize during triage. In short, the standard library vision is the ideal we may never reach, but we collectively show up every day to work towards it. - **Foster a vibrant community collaborating globally.** The community is -encouraged to engage with the standard library and language evolution. We +encour aged to engage with the standard library and language evolution. We intend to ignite enthusiasm to contribute to the expanding Mojo package ecosystem. -- The standard library prioritizes - **Performance `>` Safety `>` Portability `>` Debuggability.** +- The Standard Library prioritizes + **Performance > Safety > Portability > Debuggability.** -- **Respectable performance by default.** The standard library should provide -respectable performance by default, but not perfect performance. We support +- **Respectable performance by default.** Standard Library intends to provide +respectable performance by default — but not perfect performance. We support low-level controls that enable performance tuning by systems engineers. While providing consistently strong performance over time, we do so with minimal regressions. @@ -28,33 +25,34 @@ regressions. - **Portability by default.** The use of MLIR enables portability across a variety of platforms without writing per-platform code in the standard library. -- **The standard library does not have special privileges.** The standard -library types are not special and do not enjoy elevated privileges over -user-contributed code. This empowers Mojicians to create primitives equally as -expressive as core language primitives. +- **The standard library does not have special privileges.** Standard Library +types are not special and do not enjoy elevated privileges over user-contributed +code. This empowers Mojicians to create primitives equally as expressive as core +language primitives. -- **Fully utilize available hardware.** The standard library should not restrict -the use of available hardware on the system. On the contrary, standard library -primitives should enable users to maximize the utility of all available system +- **Fully utilize available hardware.** The Standard Library should not inhibit +using any available hardware on the system. On the contrary, standard library +primitives will enable users to maximize the utility of all available system hardware. -- **Standard library features prioritize AI workload optimizations.** Mojo +- **Standard Library features prioritize AI workload optimizations.** Mojo ultimately aims to be a multi-purpose programming language used to solve problems in the systems programming domain. However, we will prioritize standard library features and optimizations that improve the state of the art for AI. -### Non-principles +## What's Not in the Vision -We reject the following principle statements: +We reject the following vision statements, and the reasoning for each is written +inline. - **Tensor operations are first-class citizens.** Mojo has a prime directive to optimize AI workloads. However, certain core AI primitives are tightly integrated with the MAX engine architecture, and will remain part of the MAX engine codebase. -## Objectives and goals +## Objectives and Goals -- **Make unsafe or risky things explicit.** Software using unsafe constructs is +- Make unsafe or risky things explicit. Software using unsafe constructs is inevitable, but it must be minimized and explicit to the reader. Safe things shouldn’t look like unsafe ones and unsafe constructs should leave artifacts to see in the code. @@ -67,19 +65,19 @@ We reject the following principle statements: out of the box for kernel developers to use. The language runtime provides a decently performing default global allocator implementation (eg. thread local caches, automatic slab-size scaling based on heuristics, virtual memory-based - defragmentation, and more). + defragmentation, and more…). - **First-class support for parallelism and concurrency.** To fully utilize available hardware, the standard library will provide a complete suite of - primitives that maximize the parallelism potential of the system. + primitives maximizing the parallelism potential of the system. - **First-class debugging features.** Integration with Mojo debugger by - incorporating LLDB visualizers for the standard library types and collections + incorporating LLDB visualizers for the Standard Library types and collections to make debugging easy. -- **Consistent API and behavior across all primitives.** A higher-level goal of - the standard library is to be an example of well-written mojo code. For - example, all collections should behave consistently with: +- **Consistent API and behavior across all stdlib primitives.** A higher-level + goal of the stdlib is to be an example of well-written mojo code. For example, + all collections should behave consistently with: - Default-constructed collections do not allocate memory unless they are holding an element. @@ -89,15 +87,15 @@ We reject the following principle statements: - Naming of public aliases/parameters common among collections (akin to `value_type` and friends in C++). -- **Interoperability with Python code**. Allows progressively migrating code to +- **Interoperability with Python code** allows progressively migrating code to Mojo over time to not force an entire rewrite just to improve the performance of code where it matters most. -### Non-goals +## Non-Goals -While some of the following may be common goals of other languages, the -values don't outweigh the costs for us right now as we are moving fast to build -the language. While we don’t actively attempt to break the following, we provide +While some of these may be common goals of modern programming languages, the +value doesn’t outweigh the costs for us right now as we are moving fast to build +the language. While we don’t actively attempt to break the following we provide no guarantees that they work — especially over multiple releases. - Stable ABI between language/compiler and library. diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 4ec1217b02..5703255fcf 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -279,6 +279,7 @@ def test_pointer_refitem(): def test_pointer_refitem_string(): alias payload = "$Modular!Mojo!HelloWorld^" var ptr = Pointer[String].alloc(1) + __get_address_as_uninit_lvalue(ptr.address) = String() ptr[] = payload assert_equal(ptr[], payload) ptr.free() From bc9621f98a88ae3979b0c6185aed579132694652 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 25 Mar 2024 21:41:00 -0600 Subject: [PATCH 0015/2019] [docs][mojo-stdlib] Clarify internal MLIR dialects in FAQ (#35732) Clarify the internal MLIR dialects used with clear examples of `pop`, `kgen`, and `lit` dialects. Additionally, add a question about the compiler-rt and move the part about LLCL symbols to that section. MODULAR_ORIG_COMMIT_REV_ID: 2a49890f49356591ef3d1d4470ac1dd16c5d274c --- stdlib/docs/faq.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index 8fc6d46217..cc4b5f43e6 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -33,14 +33,25 @@ the entire library to have everything work on `AnyType` and be generalized to not just work on `AnyRegType`. Several things need to be worked in tandem with the compiler team to make this possible. -#### 2. LLCL and MLIR ops are private APIs? +#### 2. Are the MLIR dialects private? -LLCL entry points and MLIR operators are private undocumented APIs. We provide +The Standard Library makes use of internal MLIR dialects such as `pop`, `kgen`, +and `lit`. Currently, these are private, undocumented APIs. We provide no backward compatibility guarantees and therefore they can change at any time. -These particular areas of the standard library are in active development and we -commit to releasing them when their public-facing API has stabilized. +These particular areas of the compiler and standard library are in active +development and we are exploring how we can release them when their +public-facing API has stabilized. -#### 3. Why are some Standard Library modules missing from the open-source code? +#### 3. What is the compiler-runtime? + +Mojo depends on certain features that are still written in C++, collectively +called "the compiler runtime." This may manifest in the Standard Library code +through references like `KGEN_CompilerRT_LLCL_CreateRuntime`. Like the MLIR +dialects, the compiler runtime is currently private and undocumented. + +We plan on reducing the C++ dependencies in the future. + +#### 4. Why are some Standard Library modules missing from the open-source code? When we were preparing to open source the Standard Library, we realized that some modules weren't ready for open-source release. For example: From 5878a8bac9b1a0135d256d46547a6af87d2df621 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 26 Mar 2024 03:14:39 -0700 Subject: [PATCH 0016/2019] [Stdlib] Remove rogue debug statement, NFC (#35768) This was left over from debugging. MODULAR_ORIG_COMMIT_REV_ID: 725904aa621e505df34ed56877d6ca775b976b08 --- stdlib/test/memory/test_memory.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 5703255fcf..a2535eec78 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -157,7 +157,6 @@ def test_memcmp_extensive[ 0, "for dtype=" + str(type) + ";count=" + str(count), ) - print("memcmp(ptr1, ptr2, count) = ", memcmp(ptr1, ptr2, count)) assert_equal( memcmp(ptr1, ptr2, count), -1, From 6f1d1ddd2fda5fd5db4fecda830014ce31ef9fda Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 26 Mar 2024 11:14:54 -0500 Subject: [PATCH 0017/2019] [Docs] Add open source to the changelog (#35751) Add a link back to README.md for the stdlib open source documentation --------- Co-authored-by: Scott Main MODULAR_ORIG_COMMIT_REV_ID: bdfd12f2bf92511e0abda224c7516ca4f91625d6 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 0d6a93fd22..69a32586fd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -41,6 +41,10 @@ modular update mojo ### 🔥 Legendary +- The Mojo standard library is now open source! Check out the + [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) + for everything you need to get started. + - Structs and other nominal types are now allowed to implicitly conform to traits. A struct implicitly conforms to a trait if it implements all the requirements for the trait. For example, any struct that implements `__str__` From 4f484a1dc7c2702960a3d417a9886d8fbb36e5fd Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 26 Mar 2024 11:15:53 -0500 Subject: [PATCH 0018/2019] [Docs] Add nightly extension to stdlib dev guide (#35558) Small addition the stdlib dev guide MODULAR_ORIG_COMMIT_REV_ID: afb5bfc33a94a17be9f9d1218703067d47e33097 --- stdlib/docs/development.md | 8 ++++++++ stdlib/docs/nightly-extension.png | Bin 0 -> 116383 bytes 2 files changed, 8 insertions(+) create mode 100644 stdlib/docs/nightly-extension.png diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index e0847de2b6..3e08c81672 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -26,6 +26,14 @@ echo 'export PATH="/Users/joe/.modular/pkg/packages.modular.com_nightly_mojo/bin source ~/.zshrc ``` +Install the nightly Mojo extension by searching for `Mojo nightly` in the +extensions marketplace: + +![mojo-nightly-extension](nightly-extension.png) + +You can only have one Mojo extension enabled at a time, remember to switch back +when using the stable release! + ## Cloning the Repository ```bash diff --git a/stdlib/docs/nightly-extension.png b/stdlib/docs/nightly-extension.png new file mode 100644 index 0000000000000000000000000000000000000000..368ce6cfa048a348760a09443dd192da2992735f GIT binary patch literal 116383 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

ZmP4lSCzr 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 z Date: Tue, 26 Mar 2024 13:11:29 -0400 Subject: [PATCH 0019/2019] [mojo-stdlib] Inline list.append and adjust lldb test. (#35790) Stacked PRs: * #35695 * #35694 * __->__#35790 --- --- --- ### Inline `list.append` and adjust lldb test. - [x] Mark `list.append` as `@always_inline` - [x] Adjust `list.mojo`'s breakpoint location because lldb can't break at callsites of always inline functions. MODULAR_ORIG_COMMIT_REV_ID: b1a59088666f4a75db7dc463a7910301ba722a6f --- stdlib/src/collections/list.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index efcf112807..bf85f4c93d 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -170,6 +170,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): self.data = new_data self.capacity = new_capacity + @always_inline fn append(inout self, owned value: T): """Appends a value to this list. From 5bf14957f81768de092452dcf1bb07c4c1be6b52 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 26 Mar 2024 11:15:53 -0700 Subject: [PATCH 0020/2019] [Docs] More DynamicVector->List cleanup. (#35807) Also fixes links for Buffer/NDBuffer which are now in their own package. In release-specific docs (changelog and upgrade guide) update the links to point to the new API docs, but don't change the link names. MODULAR_ORIG_COMMIT_REV_ID: 37218f59970aba5f8afc8b8ca23eedda072608db --- docs/changelog.md | 22 +++++++++++----------- docs/tools/debugging.ipynb | 2 +- docs/upgrade-guide.md | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 69a32586fd..b597bc2bb9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -202,7 +202,7 @@ modular update mojo instead of the kind of function they are unused in. This will help catch API usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. -- The [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) and +- The [`DynamicVector`](/mojo/stdlib/collections/list#list) and [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) types now support negative indexing. This means that you can write `vec[-1]` which is equivalent to `vec[len(vec)-1]`. @@ -488,7 +488,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. and [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). -- [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) now +- [`DynamicVector`](/mojo/stdlib/collections/list#list) now supports iteration. Iteration values are instances of [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: @@ -738,8 +738,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. unroll[func, unroll_count]() ``` -- The signature of the [`NDBuffer`](/mojo/stdlib/memory/buffer#ndbuffer) and - [`Buffer`](/mojo/stdlib/memory/buffer#buffer) types have changed. Now, both +- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) and + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types have changed. Now, both take the type as the first parameter and no longer require the shape parameter. This allows you to use these types and have sensible defaults. For example: @@ -1097,11 +1097,11 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - The `_OldDynamicVector` type that worked only on register passable element types has been removed. Please migrate uses to - [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) which + [`DynamicVector`](/mojo/stdlib/collections/list#list) which works on both register passable and memory types. - The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using - either [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) + either [`DynamicVector`](/mojo/stdlib/collections/list#list) or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) instead. @@ -1297,7 +1297,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), - [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector), + [`DynamicVector`](/mojo/stdlib/collections/list#list), [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). @@ -1438,7 +1438,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. types that implement the `Sized`, `Stringable`, and `Intable` traits, respectively. -- [`DynamicVector`](/mojo/stdlib/collections/vector.html#dynamicvector) is now a +- [`DynamicVector`](/mojo/stdlib/collections/list#list) is now a proper generic collection that can use any type that implements the `Movable` and `Copyable` traits. This means you can now write, for example, `DynamicVector[String]`. Also, `DynamicVector` now invokes its element @@ -1926,7 +1926,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors - that take [`DynamicVector[Int]`](/mojo/stdlib/collections/vector.html#dynamicvector) + that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) and [`StaticIntTuple`](/mojo/stdlib/utils/index#staticinttuple) to initialize shapes. @@ -1962,8 +1962,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` and the default size is used. -- `write_file()` method in [`Buffer`](/mojo/stdlib/memory/buffer.html#buffer) - and [`NDBuffer`](/mojo/stdlib/memory/buffer.html#ndbuffer) is renamed to +- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer.html#buffer) + and [`NDBuffer`](/mojo/stdlib/buffer/buffer.html#ndbuffer) is renamed to `tofile()` to match the Python naming. - Mojo will now utilize all available cores across all NUMA sockets on the host diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 782e971f5f..8053940c72 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -338,7 +338,7 @@ "\n", "Currently Mojo expressions are limited to inspecting variables and their fields.\n", "The console also supports subscript notation (`vector[index]`) for certain data\n", - "structures in the standard library, including `DynamicVector`, `SIMD`,\n", + "structures in the standard library, including `List`, `SIMD`,\n", "and `ListLiteral`. \n", "\n", "In the future, we intend to provide a way for arbitrary data structures to\n", diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index 2e8118a635..a7cd64ac3c 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -64,7 +64,7 @@ vectorize[$3, $1, $2] ### `DynamicVector` constructor `capacity` now keyword-only -The [`DynamicVector`](/mojo/stdlib/collections/vector#dynamicvector) struct had +The [`DynamicVector`](/mojo/stdlib/collections/list#list) struct had a constructor that took a single integer value for the vector's capacity. This had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. This was not intended to support implicit conversion, so `capacity` is now a @@ -82,7 +82,7 @@ Which in VS Code looks like this: ### `NDBuffer` signature change The shape of an -[`NDBuffer`](https://docs.modular.com/mojo/stdlib/memory/buffer#ndbuffer) can +[`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) can now default to being unknown, so the parameter list has been rearranged to accommodate this: @@ -113,7 +113,7 @@ NDBuffer[$3, $1, $2] ### Dereference `Variant` with `[]` -Previously, using [`Variant`](/mojo/stdlib/collections/vector#dynamicvector) +Previously, using [`Variant`](/mojo/stdlib/utils/variant#variant) was unsafe with heap allocated objects, it now returns a reference. If you had code that looks like this: From b78fd200ae08fd100332bcd001bd67b216c7f694 Mon Sep 17 00:00:00 2001 From: weiwei chen Date: Tue, 26 Mar 2024 15:23:35 -0400 Subject: [PATCH 0021/2019] [mojo-stdlib] Always inline some functions in stdlib. (#35694) Stacked PRs: * #35695 * __->__#35694 * #35790 --- --- --- ### Always inline some functions in stdlib. -[x] Working on addressing inline issue with per function llvm pipeline. There are the functions that llvm tells me it is inlining now with llvm IPO passes. Try to see if add `@always_inline` helps to rely less on llvm. MODULAR_ORIG_COMMIT_REV_ID: 4e7ae142a149959f538e7e67776d6aa4db126b2d --- stdlib/src/builtin/error.mojo | 1 + stdlib/src/builtin/file.mojo | 4 ++++ stdlib/src/builtin/hash.mojo | 1 + stdlib/src/builtin/hex.mojo | 2 ++ stdlib/src/builtin/io.mojo | 2 ++ stdlib/src/builtin/object.mojo | 1 + stdlib/src/builtin/simd.mojo | 1 + stdlib/src/builtin/string.mojo | 6 ++++++ stdlib/src/collections/dict.mojo | 5 +++++ stdlib/src/collections/list.mojo | 7 +++++++ stdlib/src/collections/optional.mojo | 2 ++ stdlib/src/memory/memory.mojo | 4 ++++ stdlib/src/pathlib/path.mojo | 4 ++++ stdlib/src/utils/inlined_string.mojo | 3 +++ stdlib/src/utils/variant.mojo | 3 +++ 15 files changed, 46 insertions(+) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 9b6568a3c3..7cfacf914d 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -140,6 +140,7 @@ struct Error(Stringable, Boolable): """ return self.__str__() + @always_inline fn _message(self) -> String: """Converts the Error to string representation. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 9afc1cd9c2..38b492f64a 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -100,6 +100,7 @@ struct FileHandle: self.handle = handle + @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -131,6 +132,7 @@ struct FileHandle: self.handle = existing.handle existing.handle = DTypePointer[DType.invalid]() + @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads the data from the file. @@ -236,6 +238,7 @@ struct FileHandle: """ self._write(data._as_ptr(), len(data)) + @always_inline fn write(self, data: StringRef) raises: """Write the data to the file. @@ -244,6 +247,7 @@ struct FileHandle: """ self._write(data.data, len(data)) + @always_inline fn _write[ address_space: AddressSpace ](self, ptr: DTypePointer[DType.int8, address_space], len: Int) raises: diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 93a8dbab18..de4624a9fc 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -130,6 +130,7 @@ alias _HASH_INIT = _djbx33a_init alias _HASH_UPDATE = _djbx33a_hash_update +@always_inline fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: """Hash a SIMD byte vector using direct DJBX33A hash algorithm. diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 12335f89cd..9dddb9914c 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -21,6 +21,7 @@ from collections import List alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" +@always_inline fn hex[T: Intable](value: T) -> String: """Returns the hex string represention of the given integer. @@ -75,6 +76,7 @@ fn _format_int[ return String._from_bytes(buf ^) +@always_inline fn _write_int[ T: Intable ]( diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 6b51cc44c6..dfa3556329 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -362,6 +362,7 @@ struct _StringableTuple[*Ts: Stringable](Sized): alignof[Ts[i]](), ) + @always_inline fn _print[i: Int](inout self, /, *, sep: StringLiteral = " "): # TODO: Allow controlling this separator from the caller. _put(sep) @@ -376,6 +377,7 @@ struct _StringableTuple[*Ts: Stringable](Sized): return len(VariadicList(Ts)) +@always_inline fn _print_elements[ T: Stringable, *Ts: Stringable ]( diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 2a56c72e84..024e0b36fd 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -953,6 +953,7 @@ struct object(IntableRaising, Boolable, Stringable): raise "object type cannot be converted to an integer" + @always_inline fn __str__(self) -> String: """Performs conversion to string according to Python semantics. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index bf31e0ee28..2d0ca67c04 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -508,6 +508,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rebind[Scalar[type]](self).value ) + @always_inline fn __str__(self) -> String: """Get the SIMD as a string. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 2cb108835f..43d2c12ce7 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -111,6 +111,7 @@ fn chr(c: Int) -> String: # TODO: this is hard coded for decimal base +@always_inline fn _atol(str: StringRef) raises -> Int: """Parses the given string as a base-10 integer and returns that value. @@ -275,6 +276,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): var _buffer: Self._buffer_type """The underlying storage for the string.""" + @always_inline fn __str__(self) -> String: return self @@ -385,6 +387,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): self._buffer = existing._buffer ^ @staticmethod + @always_inline fn _from_bytes(owned buff: DTypePointer[DType.int8]) -> String: """Construct a string from a sequence of bytes. @@ -950,6 +953,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): # outside of the standard ASCII letters. return self._toggle_ascii_case[_is_ascii_lowercase]() + @always_inline fn _toggle_ascii_case[check_case: fn (Int8) -> Bool](self) -> String: var copy: String = self @@ -995,6 +999,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): return self._endswith_impl(suffix, start) return self[start:end]._endswith_impl(suffix) + @always_inline fn _endswith_impl(self, suffix: String, start: Int = 0) -> Bool: return self.rfind(suffix, start) + len(suffix) == len(self) @@ -1116,6 +1121,7 @@ fn _calc_initial_buffer_size_int64(n0: UInt64) -> Int: result += 4 +@always_inline fn _calc_initial_buffer_size(n0: Int) -> Int: var n = _abs(n0) var sign = 0 if n0 > 0 else 1 diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 309f96f569..27dfee2a93 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -75,6 +75,7 @@ struct _DictEntryIter[ fn __iter__(self) -> Self: return self + @always_inline fn __next__(inout self) -> Self.ref_type: while True: debug_assert(self.index < self.src[]._reserved, "dict iter bounds") @@ -226,6 +227,7 @@ struct _DictIndex: var data: DTypePointer[DType.invalid] + @always_inline fn __init__(inout self, reserved: Int): if reserved <= 128: var data = DTypePointer[DType.int8].alloc(reserved) @@ -418,6 +420,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): var _index: _DictIndex var _entries: List[Optional[DictEntry[K, V]]] + @always_inline fn __init__(inout self): """Initialize an empty dictiontary.""" self.size = 0 @@ -426,6 +429,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): self._index = _DictIndex(self._reserved) self._entries = Self._new_entries(self._reserved) + @always_inline fn __init__(inout self, existing: Self): """Copy an existing dictiontary. @@ -635,6 +639,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): ) @staticmethod + @always_inline fn _new_entries(reserved: Int) -> List[Optional[DictEntry[K, V]]]: var entries = List[Optional[DictEntry[K, V]]](capacity=reserved) for i in range(reserved): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index bf85f4c93d..511c04c806 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -144,6 +144,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): for i in range(len(existing)): self.append(existing[i]) + @always_inline fn __del__(owned self): """Destroy all elements in the list and free its memory.""" for i in range(self.size): @@ -159,6 +160,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): """ return self.size + @always_inline fn _realloc(inout self, new_capacity: Int): var new_data = AnyPointer[T].alloc(new_capacity) @@ -182,6 +184,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): (self.data + self.size).emplace_value(value ^) self.size += 1 + @always_inline fn extend(inout self, owned other: List[T]): """Extends this list by consuming the elements of `other`. @@ -222,6 +225,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): # list. self.size = final_size + @always_inline fn pop_back(inout self) -> T: """Pops a value from the back of this list. @@ -235,6 +239,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): self._realloc(self.capacity // 2) return ret_val ^ + @always_inline fn reserve(inout self, new_capacity: Int): """Reserves the requested capacity. @@ -248,6 +253,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): return self._realloc(new_capacity) + @always_inline fn resize(inout self, new_size: Int, value: T): """Resizes the list to the given new size. @@ -272,6 +278,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): self._reverse() # This method is private to avoid exposing the non-Pythonic `start` argument. + @always_inline fn _reverse(inout self, start: Int = 0): """Reverses the elements of the list at positions after `start`. diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 24e130e313..fd49fc54f0 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -99,6 +99,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): """ self = Self() + @always_inline fn value(self) -> T: """Unsafely retrieve the value out of the Optional. @@ -280,6 +281,7 @@ struct OptionalReg[T: AnyRegType](Boolable): ](__mlir_attr.`false`) } + @always_inline fn value(self) -> T: """Get the optional value. diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 14b88bed1c..3604762f15 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -153,6 +153,7 @@ fn memcmp[ # ===----------------------------------------------------------------------===# +@always_inline fn memcpy[count: Int](dest: Pointer, src: __type_of(dest)): """Copies a memory area. @@ -207,6 +208,7 @@ fn memcpy[count: Int](dest: Pointer, src: __type_of(dest)): dest_dtype_ptr.store(i, src_dtype_ptr.load[width=1](i)) +@always_inline fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): """Copies a memory area. @@ -220,6 +222,7 @@ fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): memcpy[count](dest.address, src.address) +@always_inline fn memcpy(dest: Pointer, src: __type_of(dest), count: Int): """Copies a memory area. @@ -283,6 +286,7 @@ fn memcpy(dest: Pointer, src: __type_of(dest), count: Int): dest_dtype_ptr.store(i, src_dtype_ptr.load[width=1](i)) +@always_inline fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): """Copies a memory area. diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 6d34599336..a00f363e46 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -78,6 +78,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """ self.path = existing.path ^ + @always_inline fn __copyinit__(inout self, existing: Self): """Copy constructor for the path struct. @@ -132,6 +133,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): else: self.path += DIR_SEPARATOR + suffix + @always_inline fn __str__(self) -> String: """Returns a string representation of the path. @@ -140,6 +142,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """ return self.path + @always_inline fn __fspath__(self) -> String: """Returns a string representation of the path. @@ -205,6 +208,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """ return os.lstat(self) + @always_inline fn exists(self) -> Bool: """Returns True if the path exists and False otherwise. diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 8900812bda..b8bd5c96ee 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -285,6 +285,7 @@ struct _FixedString[CAP: Int](Sized, Stringable, CollectionElement): self.buffer = _ArrayMem[Int8, CAP]() self.size = 0 + @always_inline fn __init__(inout self, literal: StringLiteral) raises: """Constructs a FixedString value given a string literal. @@ -309,6 +310,7 @@ struct _FixedString[CAP: Int](Sized, Stringable, CollectionElement): # Trait Interfaces # ===------------------------------------------------------------------=== # + @always_inline fn __str__(self) -> String: return String(self._strref_dangerous()) @@ -331,6 +333,7 @@ struct _FixedString[CAP: Int](Sized, Stringable, CollectionElement): """ self.__iadd__(string._strref_dangerous()) + @always_inline fn __iadd__(inout self, strref: StringRef) raises: """Appends another string to this string. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 7ab44218ef..da4012839d 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -185,6 +185,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): self._get_state()[] = Self._check[T]() self._get_ptr[T]().emplace_value(value ^) + @always_inline fn __copyinit__(inout self, other: Self): """Creates a deep copy of an existing variant. @@ -205,6 +206,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): unroll[each, len(VariadicList(Ts))]() + @always_inline fn __moveinit__(inout self, owned other: Self): """Move initializer for the variant. @@ -229,6 +231,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """Destroy the variant.""" self._call_correct_deleter() + @always_inline fn _call_correct_deleter(inout self): @parameter fn each[i: Int](): From 19bcc38818396ee2e8007507d8e337f40f91ef08 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 26 Mar 2024 15:35:18 -0400 Subject: [PATCH 0022/2019] [mojo-tooling] Properly teach `mblack` about ownership transfer operator (`^`) (#35804) The `mblack` grammar was aware that `^` could be a postscript, but it would not format it correctly, and in some cases it would crash. This patch improves the existing hack for the implementation by modifying the actual black logic to correct emit spaces around postscript `^` operators, and also teaches the grammar about the case when `^` is followed by `.` (i.e. attribute access). All changes outside the `mblack` implementation are NFC reformatting. Closes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 60025badf6032b1a0a561f83f1b8e55870a7a41b --- stdlib/src/base64/base64.mojo | 2 +- stdlib/src/builtin/coroutine.mojo | 4 ++-- stdlib/src/builtin/file.mojo | 14 +++++++------- stdlib/src/builtin/hex.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string.mojo | 20 ++++++++++---------- stdlib/src/collections/dict.mojo | 16 ++++++++-------- stdlib/src/collections/list.mojo | 10 +++++----- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/collections/set.mojo | 8 ++++---- stdlib/src/memory/_arc.mojo | 8 +++----- stdlib/src/memory/anypointer.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 2 +- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/utils/index.mojo | 2 +- stdlib/src/utils/inlined_string.mojo | 10 +++++----- stdlib/src/utils/variant.mojo | 4 ++-- stdlib/test/collections/test_dict.mojo | 2 +- stdlib/test/collections/test_list.mojo | 8 ++++---- stdlib/test/collections/test_optional.mojo | 2 +- stdlib/test/memory/test_anypointer.mojo | 2 +- stdlib/test/memory/test_arc.mojo | 6 +++--- stdlib/test/test_utils/types.mojo | 8 ++++---- stdlib/test/utils/test_inlined_string.mojo | 2 +- stdlib/test/utils/test_variant.mojo | 6 +++--- 27 files changed, 74 insertions(+), 76 deletions(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 445a103db8..9fbd0c933e 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -73,4 +73,4 @@ fn b64encode(str: String) -> String: out.append(b64chars.load((si_1 * 4) % 64)) out.append(ord("=")) out.append(0) - return String(out ^) + return String(out^) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 1993ca0da5..9023ca82b7 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -141,7 +141,7 @@ struct Coroutine[type: AnyRegType]: _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl } ) - return self ^ + return self^ @always_inline fn __del__(owned self): @@ -264,7 +264,7 @@ struct RaisingCoroutine[type: AnyRegType]: _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl } ) - return self ^ + return self^ @always_inline fn __del__(owned self): diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 38b492f64a..446faad7ca 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -96,7 +96,7 @@ struct FileHandle: if err_msg: self.handle = DTypePointer[DType.invalid]() - raise (err_msg ^).consume_as_error() + raise (err_msg^).consume_as_error() self.handle = handle @@ -119,7 +119,7 @@ struct FileHandle: ) if err_msg: - raise (err_msg ^).consume_as_error() + raise (err_msg^).consume_as_error() self.handle = DTypePointer[DType.invalid]() @@ -155,7 +155,7 @@ struct FileHandle: ) if err_msg: - raise (err_msg ^).consume_as_error() + raise (err_msg^).consume_as_error() return String(buf, int(size_copy) + 1) @@ -184,7 +184,7 @@ struct FileHandle: ) if err_msg: - raise (err_msg ^).consume_as_error() + raise (err_msg^).consume_as_error() var list = List[Int8](capacity=int(size_copy)) @@ -218,7 +218,7 @@ struct FileHandle: ) if err_msg: - raise (err_msg ^).consume_as_error() + raise (err_msg^).consume_as_error() return pos @@ -272,11 +272,11 @@ struct FileHandle: ) if err_msg: - raise (err_msg ^).consume_as_error() + raise (err_msg^).consume_as_error() fn __enter__(owned self) -> Self: """The function to call when entering the context.""" - return self ^ + return self^ fn open(path: String, mode: String) raises -> FileHandle: diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 9dddb9914c..6e6b5e6fb3 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -73,7 +73,7 @@ fn _format_int[ _write_int(buf, value, radix, digit_chars, prefix) - return String._from_bytes(buf ^) + return String._from_bytes(buf^) @always_inline diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 7ac497bcc7..297e3e2ef1 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -311,7 +311,7 @@ struct Int(Intable, Stringable, KeyElement, Boolable): buf.reserve(initial_buffer_size) buf.size += _vec_fmt(buf.data, initial_buffer_size, "%li", self.value) buf.size += 1 # for the null terminator. - return buf ^ + return buf^ @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 024e0b36fd..31ea14a0d7 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -184,7 +184,7 @@ struct Attr: value: The object value of the attribute. """ self.key = key - self.value = value ^ + self.value = value^ @register_passable("trivial") diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2d0ca67c04..5f106b410e 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -547,7 +547,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( buf.size += _vec_fmt(buf.data + buf.size, 2, "]") buf.size += 1 # for the null terminator. - return String(buf ^) + return String(buf^) @always_inline("nodebug") fn to_int(self) -> Int: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 43d2c12ce7..ad35249844 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -297,7 +297,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): Args: impl: The buffer. """ - self._buffer = impl ^ + self._buffer = impl^ @always_inline fn __init__(inout self): @@ -316,7 +316,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): buffer.resize(length + 1, 0) memcpy(rebind[DTypePointer[DType.int8]](buffer.data), str.data, length) buffer[length] = 0 - self._buffer = buffer ^ + self._buffer = buffer^ @always_inline fn __init__(inout self, str: StringLiteral): @@ -384,7 +384,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): Args: existing: The string to move. """ - self._buffer = existing._buffer ^ + self._buffer = existing._buffer^ @staticmethod @always_inline @@ -415,7 +415,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): if buff[-1]: buff.append(0) - return String(buff ^) + return String(buff^) @always_inline fn __bool__(self) -> Bool: @@ -442,7 +442,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): var buf = Self._buffer_type(capacity=1) buf.append(self._buffer[idx]) buf.append(0) - return String(buf ^) + return String(buf^) @always_inline fn _adjust_span(self, span: Slice) -> Slice: @@ -489,7 +489,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): for i in range(adjusted_span_len): buffer[i] = ptr[adjusted_span[i]] buffer[adjusted_span_len] = 0 - return Self(buffer ^) + return Self(buffer^) @always_inline fn __len__(self) -> Int: @@ -562,7 +562,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): other._as_ptr().address, other_len + 1, # Also copy the terminator ) - return Self(buffer ^) + return Self(buffer^) @always_inline fn __radd__(self, other: String) -> String: @@ -862,7 +862,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): self_ptr += 1 res.append(0) - return String(res ^) + return String(res^) fn strip(self) -> String: """Return a copy of the string with leading and trailing whitespace characters removed. @@ -925,7 +925,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): res.append(val_ptr.load(j)) res.append(self_ptr.load(i)) res.append(0) - return String(res ^) + return String(res^) fn lower(self) -> String: """Returns a copy of the string with all ASCII cased characters @@ -1036,7 +1036,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): self._as_ptr(), len_self, ) - return String(buf ^) + return String(buf^) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 27dfee2a93..426745c31d 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -205,8 +205,8 @@ struct DictEntry[K: KeyElement, V: CollectionElement](CollectionElement): value: The value of the entry. """ self.hash = hash(key) - self.key = key ^ - self.value = value ^ + self.key = key^ + self.value = value^ alias _EMPTY = -1 @@ -268,7 +268,7 @@ struct _DictIndex: var data = self.data.bitcast[DType.int64]() var new_data = index.data.bitcast[DType.int64]() memcpy(new_data, data, reserved) - return index ^ + return index^ fn __moveinit__(inout self, owned existing: Self): self.data = existing.data @@ -463,8 +463,8 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): self.size = existing.size self._n_entries = existing._n_entries self._reserved = existing._reserved - self._index = existing._index ^ - self._entries = existing._entries ^ + self._index = existing._index^ + self._entries = existing._entries^ fn __getitem__(self, key: K) raises -> V: """Retrieve a value out of the dictionary. @@ -647,7 +647,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): return entries fn _insert(inout self, owned key: K, owned value: V): - self._insert(DictEntry[K, V](key ^, value ^)) + self._insert(DictEntry[K, V](key^, value^)) fn _insert(inout self, owned entry: DictEntry[K, V]): self._maybe_resize() @@ -656,7 +656,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): var index: Int found, slot, index = self._find_index(entry.hash, entry.key) - self._entries[index] = entry ^ + self._entries[index] = entry^ if not found: self._set_index(slot, index) self.size += 1 @@ -725,7 +725,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): self.size = 0 self._n_entries = 0 self._index = _DictIndex(self._reserved) - var old_entries = self._entries ^ + var old_entries = self._entries^ self._entries = self._new_entries(self._reserved) for i in range(len(old_entries)): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 511c04c806..be94ae7ac1 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -181,7 +181,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): """ if self.size >= self.capacity: self._realloc(_max(1, self.capacity * 2)) - (self.data + self.size).emplace_value(value ^) + (self.data + self.size).emplace_value(value^) self.size += 1 @always_inline @@ -237,7 +237,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): if self.size * 4 < self.capacity: if self.capacity > 1: self._realloc(self.capacity // 2) - return ret_val ^ + return ret_val^ @always_inline fn reserve(inout self, new_capacity: Int): @@ -305,7 +305,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): var tmp = earlier_ptr.take_value() later_ptr.move_into(earlier_ptr) - later_ptr.emplace_value(tmp ^) + later_ptr.emplace_value(tmp^) earlier_idx += 1 later_idx -= 1 @@ -342,7 +342,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): normalized_idx += len(self) _ = (self.data + normalized_idx).take_value() - (self.data + normalized_idx).emplace_value(value ^) + (self.data + normalized_idx).emplace_value(value^) @always_inline fn _adjust_span(self, span: Slice) -> Slice: @@ -385,7 +385,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): for i in range(len(adjusted_span)): res.append(self[adjusted_span[i]]) - return res ^ + return res^ @always_inline fn __getitem__(self, i: Int) -> T: diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index fd49fc54f0..f218c0ede8 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -89,7 +89,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): Args: value: The value to store in the optional. """ - self._value = Self._type(value ^) + self._value = Self._type(value^) fn __init__(inout self, value: NoneType): """Construct an empty Optional. diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 4a2368a55b..dbcfc60161 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -79,7 +79,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): Args: other: The existing Set instance to move from. """ - self._data = other._data ^ + self._data = other._data^ fn __contains__(self, t: T) -> Bool: """Whether or not the set contains an element. @@ -213,7 +213,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): for e in self: if e[] not in other: result.add(e[]) - return result ^ + return result^ fn __isub__(inout self, other: Self): """In-place set subtraction. @@ -299,7 +299,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): for o in other: result.add(o[]) - return result ^ + return result^ fn intersection(self, other: Self) -> Self: """Set intersection. @@ -316,7 +316,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): if v[] in other: result.add(v[]) - return result ^ + return result^ fn remove_all(inout self, other: Self): """In-place set subtraction. diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index ebcc7a1104..bc423d0f2f 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -33,7 +33,7 @@ struct _ArcInner[T: Movable]: fn __init__(inout self, owned value: T): self.refcount = 0 - self.data = value ^ + self.data = value^ fn increment(inout self) -> Int64: """Atomically increment the refcount. @@ -90,9 +90,7 @@ struct Arc[T: Movable](CollectionElement): value: The value to manage. """ self._inner = Pointer[Self._type].alloc(1) - __get_address_as_uninit_lvalue(self._inner.address) = Self._type( - value ^ - ) + __get_address_as_uninit_lvalue(self._inner.address) = Self._type(value^) _ = self._inner[].increment() fn __init__(inout self, *, owned inner: Pointer[Self._type]): @@ -137,7 +135,7 @@ struct Arc[T: Movable](CollectionElement): new_value: The new value to manage. Other pointers to the memory will now see the new value. """ - self._inner[].data = new_value ^ + self._inner[].data = new_value^ fn __refitem__[ mutability: __mlir_type.`i1`, diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index c764baa6d4..ba23dc84c3 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -148,7 +148,7 @@ struct AnyPointer[T: Movable]( Args: value: The value to emplace. """ - __get_address_as_uninit_lvalue(self.value) = value ^ + __get_address_as_uninit_lvalue(self.value) = value^ @always_inline fn move_into(self, dest: AnyPointer[T]): diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 2a65a2d1e2..817ae838b5 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -583,7 +583,7 @@ fn emplace_ref_unsafe[ value: The value to write into it. """ var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(dest.value) - __get_address_as_uninit_lvalue(kgen_ptr) = value ^ + __get_address_as_uninit_lvalue(kgen_ptr) = value^ # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index a00f363e46..2be4751f29 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -76,7 +76,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): Args: existing: The existing Path. """ - self.path = existing.path ^ + self.path = existing.path^ @always_inline fn __copyinit__(inout self, existing: Self): diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index d04c4fe94d..2851bb1275 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -685,7 +685,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): buf.size += _vec_fmt(buf.data + buf.size, 2, ")") buf.size += 1 # for the null terminator. - return buf ^ + return buf^ # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index b8bd5c96ee..85a39942ed 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -52,7 +52,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): fn __init__(inout self): """Constructs a new empty string.""" var fixed = _FixedString[Self.SMALL_CAP]() - self._storage = Self.Layout(fixed ^) + self._storage = Self.Layout(fixed^) fn __init__(inout self, literal: StringLiteral): """Constructs a InlinedString value given a string literal. @@ -64,7 +64,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): if len(literal) <= Self.SMALL_CAP: try: var fixed = _FixedString[Self.SMALL_CAP](literal) - self._storage = Self.Layout(fixed ^) + self._storage = Self.Layout(fixed^) except e: abort( "unreachable: Construction of FixedString of validated" @@ -76,7 +76,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): self._storage = Self.Layout(String("")) else: var heap = String(literal) - self._storage = Self.Layout(heap ^) + self._storage = Self.Layout(heap^) fn __init__(inout self, owned heap_string: String): """Construct a new small string by taking ownership of an existing @@ -85,7 +85,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): Args: heap_string: The heap string to take ownership of. """ - self._storage = Self.Layout(heap_string ^) + self._storage = Self.Layout(heap_string^) # ===------------------------------------------------------------------=== # # Trait Interfaces @@ -171,7 +171,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): # Add the NUL byte buffer.append(0) - self._storage = Self.Layout(String(buffer ^)) + self._storage = Self.Layout(String(buffer^)) fn __add__(self, other: StringLiteral) -> Self: """Construct a string by appending another string at the end of this string. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index da4012839d..4677f12ae1 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -183,7 +183,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ self._impl = __mlir_attr[`#kgen.unknown : `, self._type] self._get_state()[] = Self._check[T]() - self._get_ptr[T]().emplace_value(value ^) + self._get_ptr[T]().emplace_value(value^) @always_inline fn __copyinit__(inout self, other: Self): @@ -280,7 +280,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ self._call_correct_deleter() self._get_state()[] = Self._check[T]() - self._get_ptr[T]().emplace_value(value ^) + self._get_ptr[T]().emplace_value(value^) fn isa[T: CollectionElement](self) -> Bool: """Check if the variant contains the required type. diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 584ca407cf..ac4ccfb83f 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -182,7 +182,7 @@ def test_dict_copy_add_new_item(): def test_dict_copy_calls_copy_constructor(): var orig = Dict[String, CopyCounter]() - orig["a"] = CopyCounter() ^ + orig["a"] = CopyCounter()^ # test values copied to new Dict var copy = Dict(orig) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 2639107360..1b11735dbf 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -256,7 +256,7 @@ def test_list_reverse_move_count(): assert_equal(vec.data[4].move_count, 3) # Keep vec alive until after we've done the last `vec.data + N` read. - _ = vec ^ + _ = vec^ def test_list_extend(): @@ -331,7 +331,7 @@ def test_list_extend_non_trivial(): assert_equal(v1.data[4].move_count, 2) # Keep v1 alive until after we've done the last `vec.data + N` read. - _ = v1 ^ + _ = v1^ def test_2d_dynamic_list(): @@ -366,7 +366,7 @@ def test_2d_dynamic_list(): def test_list_explicit_copy(): var list = List[CopyCounter]() - list.append(CopyCounter() ^) + list.append(CopyCounter()^) var list_copy = List(list) assert_equal(0, list.__get_ref(0)[].copy_count) assert_equal(1, list_copy.__get_ref(0)[].copy_count) @@ -388,7 +388,7 @@ def test_list_copy_constructor(): var vec = List[Int](capacity=1) var vec_copy = vec vec_copy.append(1) # Ensure copy constructor doesn't crash - _ = vec ^ # To ensure previous one doesn't invoke move constuctor + _ = vec^ # To ensure previous one doesn't invoke move constuctor def test_list_iter(): diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index fc005a255c..1eff5ae14c 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -50,7 +50,7 @@ def test_basic(): assert_equal(1, a1) assert_equal(2, b1) - assert_equal(1, (a ^).take()) + assert_equal(1, (a^).take()) def test_optional_reg_basic(): diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_anypointer.mojo index 358b556a5b..ce9377089f 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_anypointer.mojo @@ -52,7 +52,7 @@ def test_anypointer_move_into_move_count(): var value = MoveCounter(5) assert_equal(0, value.move_count) - ptr.emplace_value(value ^) + ptr.emplace_value(value^) # ----- # Test that `AnyPointer.move_into` performs exactly one move. diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 3b64d3d5d3..c2748d1cfd 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -37,14 +37,14 @@ def test_deleter_not_called_until_no_references(): var deleted = False var p = Arc(ObservableDel(Pointer.address_of(deleted))) var p2 = p - _ = p ^ + _ = p^ assert_false(deleted) var vec = List[Arc[ObservableDel]]() vec.append(p2) - _ = p2 ^ + _ = p2^ assert_false(deleted) - _ = vec ^ + _ = vec^ assert_true(deleted) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index be9df8d0ab..07117974d4 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -21,7 +21,7 @@ struct MoveOnly[T: Movable](Movable): Args: i: The test data payload. """ - self.data = i ^ + self.data = i^ fn __moveinit__(inout self, owned other: Self): """Move construct a MoveOnly from an existing variable. @@ -29,7 +29,7 @@ struct MoveOnly[T: Movable](Movable): Args: other: The other instance that we copying the payload from. """ - self.data = other.data ^ + self.data = other.data^ struct CopyCounter(CollectionElement): @@ -56,11 +56,11 @@ struct MoveCounter[T: CollectionElement](CollectionElement): fn __init__(inout self, owned value: T): """Construct a new instance of this type. This initial move is not counted. """ - self.value = value ^ + self.value = value^ self.move_count = 0 fn __moveinit__(inout self, owned existing: Self): - self.value = existing.value ^ + self.value = existing.value^ self.move_count = existing.move_count + 1 # TODO: This type should not be Copyable, but has to be to satisfy diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index 963fd15ec8..01b21d34ec 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -100,7 +100,7 @@ def test_small_string_construction(): var heap_s1 = String("hello") var heap_s1_addr = int(heap_s1._as_ptr()) - var s3 = InlinedString(heap_s1 ^) + var s3 = InlinedString(heap_s1^) # Test that a InlinedString constructed from a String uses the same # allocation as the original String (even if the String size is small diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 4964557a58..709dee59e6 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -33,7 +33,7 @@ struct TestCounter(CollectionElement): self.moved = other.moved fn __moveinit__(inout self, owned other: Self): - self.copied = other.copied ^ + self.copied = other.copied^ self.moved = other.moved + 1 @@ -133,7 +133,7 @@ def test_del(): alias TestDeleterVariant = Variant[ObservableDel, Poison] var deleted: Bool = False var v1 = TestDeleterVariant(ObservableDel(Pointer.address_of(deleted))) - _ = v1 ^ # call __del__ + _ = v1^ # call __del__ assert_true(deleted) # test that we didn't call the other deleter too! assert_no_poison() @@ -147,7 +147,7 @@ def test_set_calls_deleter(): v1.set[ObservableDel](ObservableDel(Pointer.address_of(deleted2))) assert_true(deleted) assert_false(deleted2) - _ = v1 ^ + _ = v1^ assert_true(deleted2) # test that we didn't call the poison deleter too! assert_no_poison() From 1ab4b5149a5c2b24a634f215070ae7ba47905fd3 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 26 Mar 2024 13:41:01 -0700 Subject: [PATCH 0023/2019] [docs] Clean up use of capitalizations (#35767) Use sentence case for all titles and headings (as per [style [Internal link] and change "Mojo Standard Library" to "Mojo standard library" (as per [slack [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 07d5893f649549711b0d4e7439a1fd7bc9a679ba --- CODE_OF_CONDUCT.md | 6 ++-- CONTRIBUTING.md | 28 ++++++++-------- README.md | 2 +- stdlib/README.md | 24 +++++++------- stdlib/docs/development.md | 24 +++++++------- stdlib/docs/faq.md | 18 +++++------ stdlib/docs/governance-structure.md | 4 +-- stdlib/docs/roadmap.md | 27 ++++++++-------- stdlib/docs/style-guide.md | 50 ++++++++++++++--------------- stdlib/docs/vision.md | 20 ++++++------ 10 files changed, 101 insertions(+), 102 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index abf01d33f1..f4f5ad5a25 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Code of Conduct +# Code of conduct In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and @@ -7,7 +7,7 @@ body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. -## Our Standards +## Our standards All community forums and spaces are meant for professional interactions that are friendly, inclusive, helpful, and collaborative. Examples of behavior that @@ -32,7 +32,7 @@ participants include: - Conduct which could reasonably be considered inappropriate for the forum in which it occurs. -## Our Responsibilities +## Our responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e2c1ea68d..4066fd4f9e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Mojo Contributor Guide +# Mojo contributor guide Welcome to the Mojo community! 🔥 We’re very excited that you’re interested in contributing to the project. To help you get started and ensure a smooth @@ -8,7 +8,7 @@ There are many ways to contribute to the project, from joining the [Discord community](https://www.discord.gg/modular), to filing bugs, to contributing documentation, examples, or code. -## Submitting Bugs +## Submitting bugs Reporting issues is a great way to contribute to the project. Mojo uses GitHub Issues for tracking bugs. @@ -21,25 +21,25 @@ Also, before opening a new issue, take a moment to search through the already submitted issues to avoid creating duplicate issues for the maintainers to address. -### Writing High-Quality Bugs +### Writing high-quality bugs We encourage you to provide as much information about the issue as practical. The more details you provide, the faster we can resolve the issue. The following is a template of the information that should accompany every submitted issue. -#### Issue Template +#### Issue template - **Summary.** A descriptive summary of the issue. - **Description.** A detailed account of the bug, including what was expected and what occurred. -- **Environment Details.** +- **Environment details.** - Mojo Compiler Version - Operating System version - Hardware Specifications -- **Severity/Frequency.** An assessment of the impact ranging from inconvenience +- **Severity/frequency.** An assessment of the impact ranging from inconvenience to a blocker. -## Contributing to Docs and Examples +## Contributing to docs and examples We’re happy to accept pull requests for the docs and examples. If your change is any one of the following, please create a pull request and we @@ -67,7 +67,7 @@ require difficult reviews and rework, or that might get rejected. See [Pull Requests](#pull-requests) for information on creating your first pull request. -## Contributing to the Standard Library +## Contributing to the standard library The standard library team is dedicated to creating a vibrant technical community around the Mojo programming language. Our vision includes a diverse and @@ -84,12 +84,12 @@ For more information on our priorities, see the following documents: For technical details on developing for the standard library, see the following documents: -- [Developing the Standard Library](./stdlib/docs/development.md) covers building, +- [Developing the standard library](./stdlib/docs/development.md) covers building, testing, and other information you’ll need to work in the standard library. - [Coding Standards and Style Guide](./stdlib/docs/style-guide.md) provides guidelines for writing code for the standard library. -### Accepting Open Source PRs +### Accepting open source PRs To ensure a streamlined process, contributors are encouraged to focus on enhancements, bug fixes, and optimizations aligned with the library's @@ -153,7 +153,7 @@ This process is heavily inspired by the process used by several other open-source projects. We’ll add more documentation in the future as we gain experience with the process. -## Pull Requests +## Pull requests You can use a pull request to propose a change or bug fix to the Mojo Standard Library, Mojo examples, or Mojo documentation. This page gives an overview of @@ -162,7 +162,7 @@ the process, especially for first-time contributors. **Note:** Pull requests should be submitted against the `nightly` branch, which represents the most recent nightly build. -### Pull Request Process +### Pull request process #### 1. First-time checklist @@ -176,7 +176,7 @@ Before you start your first pull request, please complete this checklist: We want to be sure that you spend your time efficiently and prepare changes that aren’t controversial and get stuck in long rounds of reviews. See the sections on [Contributing to Docs and Examples](#contributing-to-docs-and-examples) and -[Contributing to the Standard Library](#contributing-to-the-standard-library) +[Contributing to the standard library](#contributing-to-the-standard-library) for more details. #### 3. Create a pull request @@ -251,7 +251,7 @@ By making a contribution to this project, I certify that: this project or the open source license(s) involved. ``` -### Review Time SLA +### Review time SLA The team commits to reviewing submitted pull requests within a week of submission. diff --git a/README.md b/README.md index 9178a6dd24..09ad90aed7 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ For more general questions or to chat with other Mojo developers, check out our To learn more about Mojo, see the [Mojo Manual](https://docs.modular.com/mojo/manual/). -## Thanks To Our Contributors +## Thanks to our contributors diff --git a/stdlib/README.md b/stdlib/README.md index 7d3f90533a..f9ec6c7224 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -1,6 +1,6 @@ -# Welcome to the Mojo Standard Library +# Welcome to the Mojo standard library -This folder contains the open-source Mojo Standard Library! 🔥 +This folder contains the open-source Mojo standard library! 🔥 We're thrilled to have you join our community of developers eager to build a vibrant and supportive ecosystem. If you just want to *use* the Mojo Standard @@ -10,7 +10,7 @@ standalone Mojo SDK: - [Get the MAX SDK.](https://docs.modular.com/engine/get-started) - [Get the Mojo SDK.](https://docs.modular.com/mojo/manual/get-started/) -## Vision and Roadmap +## Vision and roadmap We have written down the principles that inform our decisions about what features we work on and what bugs we prioritize. Before you consider making @@ -22,28 +22,28 @@ guide our development efforts. - Our [Roadmap](./docs/roadmap.md) identifies concrete development goals as we work towards an even more robust and feature-rich standard library. -## Contributing to the Mojo Standard Library +## Contributing to the Mojo standard library As a contributor, your efforts and expertise are invaluable in driving the -evolution of the Mojo Standard Library. The [Mojo Contributor -Guide](../CONTRIBUTING.md) provides all the information necessary to make +evolution of the Mojo standard library. The [Mojo contributor +guide](../CONTRIBUTING.md) provides all the information necessary to make meaningful contributions—from understanding the submission process to adhering to best practices: -- [Mojo Contributor Guide](../CONTRIBUTING.md) +- [Mojo contributor guide](../CONTRIBUTING.md) -## Getting Started +## Getting started Follow our provided documentation to prepare your environment for building the standard library, and then test your setup with our automated testing suite. For additional information, the FAQ page is your go-to resource. -- [Mojo Standard Library Development](./docs/development.md) +- [Mojo standard library development](./docs/development.md) - [FAQ](./docs/faq.md) -## Code of Conduct +## Code of conduct -[Code of Conduct](../CODE_OF_CONDUCT.md) +[Code of conduct](../CODE_OF_CONDUCT.md) ## License @@ -55,5 +55,5 @@ See the license file in the repository for more details. For any inquiries, bug reports, or feature requests, please [open an issue](https://github.com/modularml/mojo/issues) on the GitHub repository. See -the [Mojo Contributor Guide](../CONTRIBUTING.md) for guidelines on filing good +the [Mojo contributor guide](../CONTRIBUTING.md) for guidelines on filing good bugs. diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 3e08c81672..3d37b4ec97 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -1,12 +1,12 @@ -# Mojo Standard Library Development +# Mojo standard library development This document covers the essentials of getting started developing for the standard library. -## Getting the Nightly Mojo Compiler +## Getting the nightly Mojo compiler To get started, you need to install the latest nightly mojo compiler. The -Standard Library only guarantees compatibility with the latest nightly `mojo` +standard library only guarantees compatibility with the latest nightly `mojo` compiler. ```bash @@ -34,15 +34,15 @@ extensions marketplace: You can only have one Mojo extension enabled at a time, remember to switch back when using the stable release! -## Cloning the Repository +## Cloning the repository ```bash git clone https://github.com/modularml/mojo/ ``` -## Building the Standard Library +## Building the standard library -To build the Standard Library, you can run the script +To build the standard library, you can run the script [`build-stdlib.sh`](../scripts/build-stdlib.sh) from the `scripts` directory inside the `stdlib` directory. This will create a build artifacts directory, `build`, in the top-level of the repo and produce the `stdlib.mojopkg` inside. @@ -51,9 +51,9 @@ inside the `stdlib` directory. This will create a build artifacts directory, ./stdlib/scripts/build-stdlib.sh ``` -## Testing the Standard Library +## Testing the standard library -### Installing Unit Test Dependencies +### Installing unit test dependencies To run the unit tests, you first need to install a few dependencies: @@ -71,9 +71,9 @@ moving away from `FileCheck` in favor of writing the unit tests using our own `testing` module and remove this dependency requirement for contributors. We are happy to welcome contributions in this area! -### Running the Standard Library Tests +### Running the standard library tests -We provide a simple Bash script to build the Standard Library package and +We provide a simple Bash script to build the standard library package and `test_utils` package that is used by the test suite. Just run `./stdlib/scripts/run-tests.sh` which will produce the necessary `mojopkg` files inside your `build` directory and then run @@ -83,7 +83,7 @@ We provide a simple Bash script to build the Standard Library package and ./stdlib/scripts/run-tests.sh ``` -### Running a subset of the Standard Library Unit Tests +### Running a subset of the standard library unit tests If you’d like to run just a subset of the tests, feel free to use all of the normal options that the `lit` tool provides. For example, to run just the @@ -108,7 +108,7 @@ If you run into any issues when running the tests, [please file an issue](https://github.com/modularml/mojo/issues) and we’ll take a look. -## Formatting Changes +## Formatting changes Please make sure your changes are formatted before submitting a Pull Request. Otherwise, CI will fail in its lint and formatting checks. The `mojo` compiler diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index cc4b5f43e6..183b2419dc 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -1,13 +1,13 @@ -# Frequently Asked Questions +# Frequently asked questions A lot of questions about Mojo as a whole can be answered on the [FAQ on our website](https://docs.modular.com/mojo/faq). -This document is specifically focused on the Standard Library with contributors +This document is specifically focused on the standard library with contributors in mind. -## Standard Library +## Mojo standard library -### Contributing & Development +### Contributing & development #### 1. What platforms does Mojo support? @@ -22,7 +22,7 @@ Don’t Panic! 😃 Check out our include all the essential information to avoid unnecessary delays in getting your issues resolved. -### Standard Library Code +### Standard library code #### 1. Why do we have both `AnyRegType` and `AnyType`? @@ -35,7 +35,7 @@ the compiler team to make this possible. #### 2. Are the MLIR dialects private? -The Standard Library makes use of internal MLIR dialects such as `pop`, `kgen`, +The standard library makes use of internal MLIR dialects such as `pop`, `kgen`, and `lit`. Currently, these are private, undocumented APIs. We provide no backward compatibility guarantees and therefore they can change at any time. These particular areas of the compiler and standard library are in active @@ -45,15 +45,15 @@ public-facing API has stabilized. #### 3. What is the compiler-runtime? Mojo depends on certain features that are still written in C++, collectively -called "the compiler runtime." This may manifest in the Standard Library code +called "the compiler runtime." This may manifest in the standard library code through references like `KGEN_CompilerRT_LLCL_CreateRuntime`. Like the MLIR dialects, the compiler runtime is currently private and undocumented. We plan on reducing the C++ dependencies in the future. -#### 4. Why are some Standard Library modules missing from the open-source code? +#### 4. Why are some standard library modules missing from the open-source code? -When we were preparing to open source the Standard Library, we realized that +When we were preparing to open source the standard library, we realized that some modules weren't ready for open-source release. For example: - Some modules are expected to change rapidly in the near term, and need to diff --git a/stdlib/docs/governance-structure.md b/stdlib/docs/governance-structure.md index 71b97bee08..9a26106e40 100644 --- a/stdlib/docs/governance-structure.md +++ b/stdlib/docs/governance-structure.md @@ -1,4 +1,4 @@ -# Mojo Standard Library Governance Structure +# Mojo standard library governance structure **Leads:** Rob Parolin ([@rparolin](https://github.com/rparolin)), @@ -8,6 +8,6 @@ Leads are responsible for reviewing code changes, reviewing proposals for evolving the standard library, and setting the roadmap. Other projects have governance structures with the concept of sub-teams. The -Mojo Standard Library isn’t large enough yet to support that level of +Mojo standard library isn’t large enough yet to support that level of governance. It very well may in the future but as we scale we will re-evaluate our governance structure and make changes accordingly. diff --git a/stdlib/docs/roadmap.md b/stdlib/docs/roadmap.md index 6b89c19d7f..7682358192 100644 --- a/stdlib/docs/roadmap.md +++ b/stdlib/docs/roadmap.md @@ -1,24 +1,23 @@ -# Mojo Standard Library Roadmap +# Mojo standard library roadmap -## Roadmap Cadence +## Roadmap cadence -The stdlib open-source repository has opted for a 6-month roadmap refresh -cadence that aligns with our internal workflows. The roadmap updates act as a -forcing function for discussions with the Mojo community to ensure stdlib -contributors both internally and externally are aligned on the future technical -direction. +We plan to update this roadmap approximately every 6 months, in alignment with +Modular's internal workflows. The roadmap updates act as a forcing function for +discussions with the Mojo community to ensure the standard library contributors +both internally and externally are aligned on the future technical direction. -## 2024 Q2+ Roadmap +## 2024 Q2+ roadmap -The following are high-level themes the Mojo Standard Library team will be +The following are high-level themes the Mojo standard library team will be working on over the next 6-months. Keep in mind that Mojo and the Mojo Standard Library are in early development with many features landing over the months ahead. Currently, that means we are focused on the core system programming features that are essential to Mojo's mission. -### Core Library Improvements +### Core library improvements -- Remove `AnyRegType` in the Standard Library in favor of `AnyType`. +- Remove `AnyRegType` in the standard library in favor of `AnyType`. - Unify Pointer and AnyPointer. @@ -27,7 +26,7 @@ features that are essential to Mojo's mission. - Design API conventions and expected behavior for core collection types such as `List`, `String`, `Dict` . -### Generic Programming Improvements +### Generic programming improvements - Define core traits for generic programming models. @@ -36,12 +35,12 @@ features that are essential to Mojo's mission. - Standardize collection meta type names (eg. *element_type*, *key_type*, and *value_type*). -### Improve Python Interop +### Improve Python interop - Improve `PythonObject` (including `object`) using new Mojo language features. -### Performance Improvements +### Performance improvements - Set up performance benchmarking infrastructure along with regression tests for perf-sensitive data structures and algorithms. diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 81ec939f99..4135e31c9b 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -1,20 +1,20 @@ -# Coding Standards & Style Guide +# Coding standards & style guide -This document describes conventions that Mojo Standard Library code should +This document describes conventions that Mojo standard library code should adhere to. Its coverages range from non-semantic conventions like code -formatting, to semantics like value lifecycle behavior that Standard Library +formatting, to semantics like value lifecycle behavior that standard library types should generally conform to. -## Structure and Formatting +## Structure and formatting -### Files & Layout +### Files & layout -#### File Structure +#### File structure -The Mojo Standard Library uses the following high-level organization. Group +The Mojo standard library uses the following high-level organization. Group related functions within the same file. Group related files within the same -directory. Do not add dependencies to the stdlib module because, by definition, -it is required to be a leaf dependency. +directory. Do not add dependencies to the `stdlib` module because, by +definition, it is required to be a leaf dependency. ```text > stdlib # stdlib root directory @@ -30,7 +30,7 @@ it is required to be a leaf dependency. All Mojo source files must end with the extension `.mojo` or `.🔥`. -#### Mojo Format +#### Mojo format Mojo provides a command line formatting utility, `mojo format`, designed to automatically format your code according to the official Mojo style guidelines. @@ -43,7 +43,7 @@ All done! ✨ 🍰 ✨ 1 file left unchanged. ``` -Unless otherwise noted, Mojo Standard Library code should follow the formatting +Unless otherwise noted, Mojo standard library code should follow the formatting produced by `mojo format`. #### Whitespace @@ -54,13 +54,13 @@ produced by `mojo format`. *We encourage updating your editor settings to be consistent with the above.* -#### Column Limit +#### Column limit Mojo code has a column limit of 80 characters. -#### File License Header +#### File license header -Every file in the open source Mojo Standard Library should begin with the +Every file in the open source Mojo standard library should begin with the following license information header: ```mojo @@ -78,9 +78,9 @@ following license information header: # ===----------------------------------------------------------------------=== # ``` -#### Code Header Comments +#### Code header comments -Code in the Mojo Standard Library should use the following conventional +Code in the Mojo standard library should use the following conventional structure of header comments separating the various kinds of methods that can be defined on structs. @@ -117,9 +117,9 @@ struct MyStruct(Sized, Stringable): # ===------------------------------------------------------------------=== # ``` -## Code Conventions +## Code conventions -### Identifier Naming Conventions +### Identifier naming conventions The following are the recommended types of `case styles` used in Mojo Standard Library code. @@ -133,7 +133,7 @@ Library code. | `flatcase` | All lowercase without separators | `basename` The following table outlines the appropriate use of various casing styles in the -Mojo Standard Library. By following these conventions, Mojo developers ensure +Mojo standard library. By following these conventions, Mojo developers ensure their code is accessible and understandable to others in the community. | Item Kind | Example | Case Convention @@ -155,14 +155,14 @@ their code is accessible and understandable to others in the community. | `fn` value parameter | `fn repeat[Count: Int]()` | `PascalCase` The demonstrated style choices intend to illustrate the various naming -conventions used in the Standard Library. However, these choices may not match +conventions used in the standard library. However, these choices may not match the existing style in all code in its current state. When preparing a change, it is important to adhere to the style and naming conventions already established in that module. Therefore, if the module you are working on uses a different style, continue using that style to maintain consistency. We are not currently accepting pull requests that propose extensive formatting or renaming changes. -### Naming Guidelines +### Naming guidelines #### ℹ️ Prefer descriptive parameter names over single-letter names @@ -195,7 +195,7 @@ safe and inexpensive. However, copying types that dynamically allocate memory can be expensive. This includes common types like `List`, `Dict`, `Set`, `Tensor`, and `String`. -Some Standard Library types allow implicit copies where they shouldn’t. We will +Some standard library types allow implicit copies where they shouldn’t. We will resolve this shortly as new Mojo language features are shipped to help with this very situation. @@ -212,7 +212,7 @@ struct MyStruct: # do a deep copy of MyStruct ``` -### Import Statements +### Import statements - Explicitly import entities (functions, structs, aliases) used rather than relying on transitive imports. @@ -220,7 +220,7 @@ struct MyStruct: `from some_package import *`. - Import statements should be sorted lexicographically. -### API Docstrings +### API docstrings Every public function and public struct (including data fields) in the Standard Library must have doc strings. There is tooling to ensure public functions @@ -273,7 +273,7 @@ fn add_param_arg[foo: Int](bar: Int) -> Int: ### Testing -#### Unit Test Filenames +#### Unit test filenames All test filenames should be prefixes with `test_`. For example `test_sort.mojo`. diff --git a/stdlib/docs/vision.md b/stdlib/docs/vision.md index ae8774b6fb..507bfd77f1 100644 --- a/stdlib/docs/vision.md +++ b/stdlib/docs/vision.md @@ -2,7 +2,7 @@ ## Principles -The following are “North Star” principles for the Mojo Standard Library. +The following are “North Star” principles for the Mojo standard library. These principles will inform multiple future decisions from what features we work on to what bugs we prioritize during triage. In short, the standard library vision is the ideal we may never reach, but we collectively show up every day @@ -13,10 +13,10 @@ encour aged to engage with the standard library and language evolution. We intend to ignite enthusiasm to contribute to the expanding Mojo package ecosystem. -- The Standard Library prioritizes +- The standard library prioritizes **Performance > Safety > Portability > Debuggability.** -- **Respectable performance by default.** Standard Library intends to provide +- **Respectable performance by default.** standard library intends to provide respectable performance by default — but not perfect performance. We support low-level controls that enable performance tuning by systems engineers. While providing consistently strong performance over time, we do so with minimal @@ -25,22 +25,22 @@ regressions. - **Portability by default.** The use of MLIR enables portability across a variety of platforms without writing per-platform code in the standard library. -- **The standard library does not have special privileges.** Standard Library +- **The standard library does not have special privileges.** standard library types are not special and do not enjoy elevated privileges over user-contributed code. This empowers Mojicians to create primitives equally as expressive as core language primitives. -- **Fully utilize available hardware.** The Standard Library should not inhibit +- **Fully utilize available hardware.** The standard library should not inhibit using any available hardware on the system. On the contrary, standard library primitives will enable users to maximize the utility of all available system hardware. -- **Standard Library features prioritize AI workload optimizations.** Mojo +- **standard library features prioritize AI workload optimizations.** Mojo ultimately aims to be a multi-purpose programming language used to solve problems in the systems programming domain. However, we will prioritize standard library features and optimizations that improve the state of the art for AI. -## What's Not in the Vision +## What's not in the vision We reject the following vision statements, and the reasoning for each is written inline. @@ -50,7 +50,7 @@ inline. integrated with the MAX engine architecture, and will remain part of the MAX engine codebase. -## Objectives and Goals +## Objectives and goals - Make unsafe or risky things explicit. Software using unsafe constructs is inevitable, but it must be minimized and explicit to the reader. Safe things @@ -72,7 +72,7 @@ inline. primitives maximizing the parallelism potential of the system. - **First-class debugging features.** Integration with Mojo debugger by - incorporating LLDB visualizers for the Standard Library types and collections + incorporating LLDB visualizers for the standard library types and collections to make debugging easy. - **Consistent API and behavior across all stdlib primitives.** A higher-level @@ -91,7 +91,7 @@ inline. Mojo over time to not force an entire rewrite just to improve the performance of code where it matters most. -## Non-Goals +## Non-goals While some of these may be common goals of modern programming languages, the value doesn’t outweigh the costs for us right now as we are moving fast to build From 59910fb23d86a175380efd9b40ad9b5df42f3262 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 26 Mar 2024 14:58:19 -0700 Subject: [PATCH 0024/2019] [Docs] Update Mojo docs and examples from OSS (#35840) (#35855) Changes as of mojo@f6dc7343604a10f7035fdda6e41214c8432fc61b Manually reverted unwanted changes. --------- Co-authored-by: modularbot MODULAR_ORIG_COMMIT_REV_ID: 93c01016ff4f6529586381c4238357cd8252a1ff --- docs/changelog.md | 343 ++++++++++++++++++++-------------------- docs/manual/packages.md | 8 +- 2 files changed, 175 insertions(+), 176 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b597bc2bb9..2c8b1e6493 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -396,10 +396,10 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: - var res = 1 - for i in range(len(args)): - res *= args[i] - return res + offset + var res = 1 + for i in range(len(args)): + res *= args[i] + return res + offset print(prod_with_offset(2, 3, 4, 10)) # prints 240 print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 @@ -433,18 +433,18 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo fn foo(arg: Int): - """""" # Unexpected empty documentation string + """""" # Unexpected empty documentation string ``` Applying the fixit from above will generate: ```mojo fn foo(arg: Int): - """[summary]. + """[summary]. - Args: - arg: [description]. - """ + Args: + arg: [description]. + """ ``` - Added new `*_` syntax that allows users to explicitly unbind any number of @@ -498,9 +498,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. v.append("Bob") v.append("Charlie") for x in v: - x[] = str("Hello, ") + x[] + x[] = str("Hello, ") + x[] for x in v: - print(x[]) + print(x[]) ``` - `DynamicVector` now has @@ -517,14 +517,14 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo @register_passable struct YourPair: - var a: Int - var b: Int - fn __init__(inout self): - self.a = 42 - self.b = 17 - fn __copyinit__(inout self, existing: Self): - self.a = existing.a - self.b = existing.b + var a: Int + var b: Int + fn __init__(inout self): + self.a = 42 + self.b = 17 + fn __copyinit__(inout self, existing: Self): + self.a = existing.a + self.b = existing.b ``` This form makes the language more consistent, more similar to Python, and @@ -532,7 +532,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. impact of using this new form: the compiler arranges to automatically return the value in a register without requiring you to worry about it. - The older `-> Self:` syntax is still supported in this release, but will be + The older `-> Self` syntax is still supported in this release, but will be removed in a subsequent one, so please migrate your code. One thing to watch out for: a given struct should use one style or the other, mixing some of each won't work well. @@ -562,12 +562,12 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo @value struct UnusualSlice: - var a: Int - var b: Float64 - var c: String + var a: Int + var b: Float64 + var c: String struct YourContainer: - fn __getitem__(self, slice: UnusualSlice) -> T: ... + fn __getitem__(self, slice: UnusualSlice) -> T: ... ``` Given this implementation, you can subscript into an instance of @@ -645,7 +645,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo if "Mojo" in "Hello Mojo": - ... + ... ``` - Breakpoints can now be inserted programmatically within the code using the @@ -680,8 +680,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo fn my_function(x: Int, y: __type_of(x)) -> Int: - let z : __type_of(x) = y - return z + let z: __type_of(x) = y + return z ``` ### 🦋 Changed @@ -696,9 +696,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo fn test(): - # treated as a var, but please update your code! - let x = 42 # warning: 'let' is being removed, please use 'var' instead - x = 9 + # treated as a var, but please update your code! + let x = 42 # warning: 'let' is being removed, please use 'var' instead + x = 9 ``` - It is no longer possible to explicitly specify implicit argument parameters in @@ -986,8 +986,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo fn main(): - var a : String = "hello" - var b : String = " references" + var a: String = "hello" + var b: String = " references" var aref = Reference(a) aref[] += b @@ -1057,21 +1057,21 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo struct Foo(CollectionElement): - var vec: DynamicVector[Self] + var vec: DynamicVector[Self] - fn __init__(inout self: Self): - self.vec = DynamicVector[Self]() + fn __init__(inout self: Self): + self.vec = DynamicVector[Self]() - fn __moveinit__(inout self: Self, owned existing: Self): - self.vec = existing.vec ^ + fn __moveinit__(inout self: Self, owned existing: Self): + self.vec = existing.vec ^ - fn __copyinit__(inout self: Self, existing: Self): - self.vec = existing.vec + fn __copyinit__(inout self: Self, existing: Self): + self.vec = existing.vec fn main(): - var foo = Foo() - print(len(foo.vec)) + var foo = Foo() + print(len(foo.vec)) ``` ### ❌ Removed @@ -1112,13 +1112,13 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo @adaptive fn foo[a: Bool](): - constrained[a]() - body1() + constrained[a]() + body1() @adaptive fn foo[a: Bool](): - constrained[not a]() - body2() + constrained[not a]() + body2() ``` Can be rewritten as: @@ -1559,7 +1559,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo # The `ret` variable has type `Tuple[Int, Int]`. - let ret = __mlir_op.`multi_result_op`[ _type = (Int, Int) ]() + let ret = __mlir_op.`multi_result_op`[_type=(Int, Int)]() ``` - Mojo now has the ability to read raw bytes from a file using the @@ -1752,7 +1752,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo fn foo[a: Int, b: Int = 42](): - print(a, "+", b) + print(a, "+", b) foo[a=5]() # prints '5 + 42' foo[a=7, b=13]() # prints '7 + 13' @@ -1826,8 +1826,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo @value struct MyStruct: - fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: - return x * y + z + fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: + return x * y + z MyStruct()[z=7, x=3, y=5] # returns 22 ``` @@ -1927,7 +1927,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) - and [`StaticIntTuple`](/mojo/stdlib/utils/index#staticinttuple) to + and [`StaticIntTuple`](/mojo/stdlib/utils/index_.html#staticinttuple) to initialize shapes. - The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the @@ -2128,27 +2128,27 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Error messages from Python are now exposed in Mojo. For example the following should print `No module named 'my_uninstalled_module'`: - ```mojo - fn main(): - try: - let my_module = Python.import_module("my_uninstalled_module") - except e: - print(e) - ``` + ```mojo + fn main(): + try: + let my_module = Python.import_module("my_uninstalled_module") + except e: + print(e) + ``` - Error messages can now store dynamic messages. For example, the following should print "Failed on: Hello" - ```mojo - fn foo(x:String) raises: - raise Error("Failed on: " + x) - - fn main(): - try: - foo("Hello") - except e: - print(e) - ``` + ```mojo + fn foo(x: String) raises: + raise Error("Failed on: " + x) + + fn main(): + try: + foo("Hello") + except e: + print(e) + ``` ### 🦋 Changed @@ -2242,29 +2242,29 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco - Mojo now has partial support for passing keyword arguments to functions and methods. For example the following should work: - ```mojo - fn foo(a: Int, b: Int = 3) -> Int: - return a * b + ```mojo + fn foo(a: Int, b: Int = 3) -> Int: + return a * b - fn main(): - print(foo(6, b=7)) # prints '42' - print(foo(a=6, b=7)) # prints '42' - print(foo(b=7, a=6)) # prints '42' - ``` + fn main(): + print(foo(6, b=7)) # prints '42' + print(foo(a=6, b=7)) # prints '42' + print(foo(b=7, a=6)) # prints '42' + ``` Parameters can also be inferred from keyword arguments, for example: - ```mojo - fn bar[A: AnyType, B: AnyType](a: A, b: B): - print("Hello 🔥") + ```mojo + fn bar[A: AnyType, B: AnyType](a: A, b: B): + print("Hello 🔥") - fn bar[B: AnyType](a: StringLiteral, b: B): - print(a) + fn bar[B: AnyType](a: StringLiteral, b: B): + print(a) - fn main(): - bar(1, 2) # prints `Hello 🔥` - bar(b=2, a="Yay!") # prints `Yay!` - ``` + fn main(): + bar(1, 2) # prints `Hello 🔥` + bar(b=2, a="Yay!") # prints `Yay!` + ``` For the time being, the following notable limitations apply: @@ -2294,31 +2294,31 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco be added in the parameter domain, but are converted to `HasBool` when materialized. - ```mojo - @value - @register_passable("trivial") - struct HasBool: + ```mojo + @value + @register_passable("trivial") + struct HasBool: var x: Bool fn __init__(x: Bool) -> Self: - return Self {x: x} + return Self {x: x} @always_inline("nodebug") fn __init__(nms: NmStruct) -> Self: - return Self {x: True if (nms.x == 77) else False} + return Self {x: True if (nms.x == 77) else False} - @value - @nonmaterializable(HasBool) - @register_passable("trivial") - struct NmStruct: + @value + @nonmaterializable(HasBool) + @register_passable("trivial") + struct NmStruct: var x: Int @always_inline("nodebug") fn __add__(self: Self, rhs: Self) -> Self: - return NmStruct(self.x + rhs.x) + return NmStruct(self.x + rhs.x) - alias stillNmStruct = NmStruct(1) + NmStruct(2) - # When materializing to a run-time variable, it is automatically converted, - # even without a type annotation. - let convertedToHasBool = stillNmStruct - ``` + alias stillNmStruct = NmStruct(1) + NmStruct(2) + # When materializing to a run-time variable, it is automatically converted, + # even without a type annotation. + let convertedToHasBool = stillNmStruct + ``` - Mojo integer literals now produce the `IntLiteral` infinite precision integer type when used in the parameter domain. `IntLiteral` is materialized to the @@ -2340,19 +2340,19 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco compiler cannot prove that. These were previously both named `__moveinit__`, with the following two signatures: - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __moveinit__(inout self, inout existing: Self): ... - ``` + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __moveinit__(inout self, inout existing: Self): ... + ``` We've changed the second form to get its own name to make it more clear that these are two separate operations: the second has been renamed to `__takeinit__`: - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __takeinit__(inout self, inout existing: Self): ... - ``` + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __takeinit__(inout self, inout existing: Self): ... + ``` The name is intended to connote that the operation takes the conceptual value from the source (without destroying it) unlike the first one which "moves" a @@ -2484,35 +2484,35 @@ All earlier releases were considered version 0.1. - Mojo now supports using memory-only types in parameter expressions and as function or type parameters: - ```mojo - @value - struct IntPair: - var first: Int - var second: Int + ```mojo + @value + struct IntPair: + var first: Int + var second: Int - fn add_them[value: IntPair]() -> Int: - return value.first + value.second + fn add_them[value: IntPair]() -> Int: + return value.first + value.second - fn main(): - print(add_them[IntPair(1, 2)]()) # prints '3' - ``` + fn main(): + print(add_them[IntPair(1, 2)]()) # prints '3' + ``` - In addition, Mojo supports evaluating code that uses heap-allocated memory at compile-time and materializing compile-time values with heap-allocated memory into dynamic values: - ```mojo - fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: - var result = DynamicVector[Int]() - for i in range(lowerBound, upperBound, step): - result.push_back(i) - return result - - fn main(): - alias values = fillVector(5, 23, 7) - for i in range(0, values.__len__()): - print(values[i]) # prints '5', '12', and then '19' - ``` + ```mojo + fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: + var result = DynamicVector[Int]() + for i in range(lowerBound, upperBound, step): + result.push_back(i) + return result + + fn main(): + alias values = fillVector(5, 23, 7) + for i in range(0, values.__len__()): + print(values[i]) # prints '5', '12', and then '19' + ``` #### 🦋 Changed @@ -2590,11 +2590,11 @@ All earlier releases were considered version 0.1. # Perform the RGB to grayscale transform. for y in range(height): - for x in range(width): - let r = image[y,x,0] - let g = image[y,x,1] - let b = image[y,x,2] - gray_scale_image[Index(y,x)] = 0.299 * r + 0.587 * g + 0.114 * b + for x in range(width): + let r = image[y, x, 0] + let g = image[y, x, 1] + let b = image[y, x, 2] + gray_scale_image[Index(y, x)] = 0.299 * r + 0.587 * g + 0.114 * b ``` #### 🛠️ Fixed @@ -2621,17 +2621,17 @@ All earlier releases were considered version 0.1. compiler time constant value, otherwise unrolling will fail with compilation error. This also doesn't make loop induction variable a parameter. - ```mojo - # Fully unroll the loop. - @unroll - for i in range(5): - print(i) - - # Unroll the loop by a factor of 4 (with remainder iterations of 2). - @unroll(4) - for i in range(10): - print(i) - ``` + ```mojo + # Fully unroll the loop. + @unroll + for i in range(5): + print(i) + + # Unroll the loop by a factor of 4 (with remainder iterations of 2). + @unroll(4) + for i in range(10): + print(i) + ``` - The Mojo REPL now prints the values of variables defined in the REPL. There is full support for scalars and structs. Non-scalar SIMD vectors are not @@ -2665,7 +2665,7 @@ All earlier releases were considered version 0.1. @register_passable # error: cannot apply signature decorator after a body one! @value struct Foo: - pass + pass ``` - Global variables can now be exported in Mojo compiled archives, using the @@ -2747,8 +2747,8 @@ All earlier releases were considered version 0.1. provide a more gentle first-user experience. - [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now - available. Coroutines form the basis of Mojo's support for asynchronous - execution. Calls to `async fn`s can be stored into a `Coroutine`, from which + available. Coroutines form the basis of Mojo's support for asynchronous + execution. Calls to `async fn`s can be stored into a `Coroutine`, from which they can be resumed, awaited upon, and have their results retrieved upon completion. @@ -2787,9 +2787,8 @@ All earlier releases were considered version 0.1. argument type, including `DType`. - The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were - moved from the `Numerics` module to the - [`Math`](/mojo/stdlib/math/math) module, to better align with Python's - library structure. + moved from the `Numerics` module to the [`Math`](/mojo/MojoStdlib/Math.html) + module, to better align with Python's library structure. #### 🛠️ Fixed @@ -2988,10 +2987,10 @@ only in declared parameter names, e.g. the following now works correctly: like this: ```mojo - var someValue : T = ... - ... - takeValueAsOwned(someValue) - ... + var someValue: T = ... + ... + takeValueAsOwned(someValue) + ... ``` When `takeValueAsOwned()` takes its argument as an @@ -3113,7 +3112,7 @@ only in declared parameter names, e.g. the following now works correctly: arithmetic and logical operators on imported Python values. - `PythonObject` is now printable from Mojo, instead of requiring you to import -Python's print function. + Python's print function. #### 🛠️ Fixed @@ -3201,9 +3200,9 @@ busy this week. ```mojo def foo(x): # This: - def bar(y): return x*y + def bar(y): return x * y # Is the same as: - let bar = lambda y: x*y + let bar = lambda y: x * y ``` These closures cannot have input or result parameters, because they are always @@ -3274,15 +3273,15 @@ busy this week. The `@value` decorator will synthesize the following members for you: ```mojo - fn __init__(inout self, owned name: String, age: Int): - self.name = name^ - self.age = age - fn __copyinit__(inout self, existing: Self): - self.name = existing.name - self.age = existing.age - fn __moveinit__(inout self, owned existing: Self): - self.name = existing.name^ - self.age = existing.age + fn __init__(inout self, owned name: String, age: Int): + self.name = name^ + self.age = age + fn __copyinit__(inout self, existing: Self): + self.name = existing.name + self.age = existing.age + fn __moveinit__(inout self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age ``` This decorator can greatly reduce the boilerplate needed to define common @@ -3317,7 +3316,7 @@ busy this week. initialization than requiring the initializer be inline, e.g.: ```mojo - let x : Int + let x: Int if cond: x = foo() else: @@ -4243,9 +4242,9 @@ busy this week. e.g.: ```mojo - alias a : __mlir_type.index - alias b : __mlir_type.index - idx_result_params[xyz*2 -> a, b]() + alias a: __mlir_type.index + alias b: __mlir_type.index + idx_result_params[xyz * 2 -> a, b]() ``` - Various minor issues with implicit conversions are fixed. For instances, diff --git a/docs/manual/packages.md b/docs/manual/packages.md index 46e719921b..c94aac8437 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -51,7 +51,7 @@ that's in the same directory as `mymodule.mojo`: from mymodule import MyPair fn main(): - let mine = MyPair(2, 4) + var mine = MyPair(2, 4) mine.dump() ``` @@ -62,7 +62,7 @@ through the module name. For example: import mymodule fn main(): - let mine = mymodule.MyPair(2, 4) + var mine = mymodule.MyPair(2, 4) mine.dump() ``` @@ -72,7 +72,7 @@ You can also create an alias for an imported member with `as`, like this: import mymodule as my fn main(): - let mine = my.MyPair(2, 4) + var mine = my.MyPair(2, 4) mine.dump() ``` @@ -124,7 +124,7 @@ name like this: from mypackage.mymodule import MyPair fn main(): - let mine = MyPair(2, 4) + var mine = MyPair(2, 4) mine.dump() ``` From d99ca7741f860acb5ad18898b97a18679b2d6d77 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 26 Mar 2024 17:40:02 -0700 Subject: [PATCH 0025/2019] [Docs] Add separate released changelog, update docs build to publish it. (#35889) This release separates the Mojo changelog into "released" and "unreleased" changelog files. The released file only includes versioned changes; the unreleased file includes... yeah, you got it. Devs should be able to continue adding unreleased changes to `docs/external/changelog.md` as before. Docs DRI will update the released changelog as part of the release process. MODULAR_ORIG_COMMIT_REV_ID: 5dadeeb34d28064184bea9de93933611bb53fd36 --- docs/changelog-released.md | 4369 +++++++++++++++++++++++++++++++++++ docs/changelog.md | 4370 +----------------------------------- 2 files changed, 4371 insertions(+), 4368 deletions(-) create mode 100644 docs/changelog-released.md diff --git a/docs/changelog-released.md b/docs/changelog-released.md new file mode 100644 index 0000000000..7e16034919 --- /dev/null +++ b/docs/changelog-released.md @@ -0,0 +1,4369 @@ +--- +title: Mojo🔥 changelog +sidebar_label: Changelog +description: A history of significant Mojo changes. +toc_max_heading_level: 2 +website: + open-graph: + image: /static/images/mojo-social-card.png + twitter-card: + image: /static/images/mojo-social-card.png +--- + +This is a running list of significant changes for the Mojo language and tools. +It doesn't include all internal implementation changes. + +## Update Mojo + +If you don't have Mojo yet, see the [get started +guide](/mojo/manual/get-started/#get-the-mojo-sdk). + +To see your Mojo version, run this: + +```sh +mojo --version +``` + +To update Mojo, first [update `modular`](/cli/#description), and then run this: + +```sh +modular update mojo +``` + +## v24.1.1 (2024-03-18) + +This release includes installer improvements and enhanced error reporting for +installation issues. Otherwise it is functionally identical to Mojo 24.1. + +## v24.1 (2024-02-29) + +### 🔥 Legendary + +- Mojo is now bundled with [the MAX platform](/max)! + + As such, the Mojo package version now matches the MAX version, which follows + a `YY.MAJOR.MINOR` version scheme. Because this is our first release in 2024, + that makes this version `24.1`. + +- Mojo debugging support is here! The Mojo VS Code extension includes debugger + support. For details, see [Debugging](/mojo/tools/debugging) in the Mojo + Manual. + +### ⭐️ New + +- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our + collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` + checks, and requires member elements to conform to the `KeyElement` trait. + + ```mojo + from collections import Set + + var set = Set[Int](1, 2, 3) + print(len(set)) # 3 + set.add(4) + + for element in set: + print(element[]) + + set -= Set[Int](3, 4, 5) + print(set == Set[Int](1, 2)) # True + print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True + let element = set.pop() + print(len(set)) # 1 + ``` + +- Mojo now supports the `x in y` expression as syntax sugar for + `y.__contains__(x)` as well as `x not in y`. + +- Mojo now has support for keyword-only arguments and parameters. For example: + + ```mojo + fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2): + print(a * b * c * d) + + my_product(3, c=5) # prints '30' + my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c' + ``` + + This includes support for declaring signatures that use both variadic and + keyword-only arguments/parameters. For example, the following is now possible: + + ```mojo + fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: + var res = 1 + for i in range(len(args)): + res *= args[i] + return res + offset + + print(prod_with_offset(2, 3, 4, 10)) # prints 240 + print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 + ``` + + Note that variadic keyword-only arguments/parameters (for example, `**kwargs`) + are not supported yet. That is, the following is not allowed: + + ```mojo + fn variadic_kw_only(a: Int, **kwargs): ... + ``` + + For more information, see + [Positional-only and keyword-only arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments) + in the Mojo Manual. + +- The `print()` function now accepts a keyword-only argument for the `end` + which is useful for controlling whether a newline is printed or not + after printing the elements. By default, `end` defaults to "\n" as before. + +- The Mojo SDK can now be installed on AWS Graviton instances. + +- A new version of the [Mojo Playground](/mojo/playground) is available. The new + playground is a simple interactive editor for Mojo code, similar to the Rust + Playground or Go Playground. The old + [JupyterLab based playground](https://playground.modular.com) will remain + online until March 20th. + +- The Mojo LSP server will now generate fixits for populating empty + documentation strings: + + ```mojo + fn foo(arg: Int): + """""" # Unexpected empty documentation string + ``` + + Applying the fixit from above will generate: + + ```mojo + fn foo(arg: Int): + """[summary]. + + Args: + arg: [description]. + """ + ``` + +- Added new `*_` syntax that allows users to explicitly unbind any number of + positional parameters. For example: + + ```mojo + struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass + + alias all_unbound = StructWithDefault[*_] + # equivalent to + alias all_unbound = StructWithDefault[_, _, _, _] + + alias first_bound = StructWithDefault[5, *_] + # equivalent to + alias first_bound = StructWithDefault[5, _, _, _] + + alias last_bound = StructWithDefault[*_, 6] + # equivalent to + alias last_bound = StructWithDefault[_, _, _, 6] + + alias mid_unbound = StructWithDefault[3, *_, 4] + # equivalent to + alias mid_unbound = StructWithDefault[3, _, _, 4] + ``` + + As demonstrated above, this syntax can be used to explicitly unbind an + arbitrary number of parameters, at the beginning, at the end, or in the + middle of the operand list. Since these unbound parameters must be explicitly + specified at some point, default values for these parameters are not applied. + For example: + + ```mojo + alias last_bound = StructWithDefault[*_, 6] + # When using last_bound, you must specify a, b, and c. last_bound + # doesn't have a default value for `c`. + var s = last_bound[1, 2, 3]() + ``` + + For more information see the Mojo Manual sections on + [partially-bound types](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types) + and + [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). + +- [`DynamicVector`](/mojo/stdlib/collections/list#list) now + supports iteration. Iteration values are instances of + [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: + + ```mojo + var v: DynamicVector[String]() + v.append("Alice") + v.append("Bob") + v.append("Charlie") + for x in v: + x[] = str("Hello, ") + x[] + for x in v: + print(x[]) + ``` + +- `DynamicVector` now has + [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and + [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. + +- The `mojo package` command now produces compilation agnostic packages. + Compilation options such as O0, or --debug-level, are no longer needed or + accepted. As a result, packages are now smaller, and extremely portable. + +- Initializers for `@register_passable` values can (and should!) now be + specified with `inout self` arguments just like memory-only types: + + ```mojo + @register_passable + struct YourPair: + var a: Int + var b: Int + fn __init__(inout self): + self.a = 42 + self.b = 17 + fn __copyinit__(inout self, existing: Self): + self.a = existing.a + self.b = existing.b + ``` + + This form makes the language more consistent, more similar to Python, and + easier to implement advanced features for. There is also no performance + impact of using this new form: the compiler arranges to automatically return + the value in a register without requiring you to worry about it. + + The older `-> Self` syntax is still supported in this release, but will be + removed in a subsequent one, so please migrate your code. One thing to watch + out for: a given struct should use one style or the other, mixing some of + each won't work well. + +- The `inout self` initializer form is **required** for initializers of + `@register_passable` types that may raise errors: + + ```mojo + @register_passable + struct RaisingCtor: + fn __init__(inout self) raises: + raise + ``` + +- `async` functions that may raise errors have been temporarily disabled in this + build. The implementation of Mojo async is undergoing a rework 🚧. + +- The standard library `slice` type has been renamed to + [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` + function has been introduced. This makes Mojo closer to Python and makes the + `Slice` type follow the naming conventions of other types like `Int`. + +- "Slice" syntax in subscripts is no longer hard coded to the builtin `slice` + type: it now works with any type accepted by a container's `__getitem__()` + method. For example: + + ```mojo + @value + struct UnusualSlice: + var a: Int + var b: Float64 + var c: String + + struct YourContainer: + fn __getitem__(self, slice: UnusualSlice) -> T: ... + ``` + + Given this implementation, you can subscript into an instance of + `YourContainer` like `yc[42:3.14:"🔥"]` and the three values are passed to the + `UnusualSlice` constructor. + +- The `__refitem__()` accessor method may now return a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to + return an MLIR internal reference type. + +- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) + method, for moving a value from one pointer memory location to another. + +- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be + used to format any value whose type implements the + [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. + +- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements + `__is__` and `__isnot__` so that you can use expressions of the form `x is y` + and `x is not y` with `PythonObject`. + +- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the + `SizedRaising` trait. This means the built-in + [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. + +- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) + and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. + +- A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query + properties on paths. + +- The `os` package now has a + [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms + to the `PathLike` trait by implementing the `__fspath__()` function. + +- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to + query properties of the path. + +- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on + [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` + module to work on `PathLike` structs. For example, the following sample + lists all the directories in the `/tmp` directory: + + ```mojo + from pathlib import Path + + fn walktree(top: Path, inout files: DynamicVector[Path]): + try: + var ls = top.listdir() + for i in range(len(ls)): + var child = top / ls[i] + if child.is_dir(): + walktree(child, files) + elif child.is_file(): + files.append(child) + else: + print("Skipping '" + str(child) + "'") + except: + return + + fn main(): + var files = DynamicVector[Path]() + + walktree(Path("/tmp"), files) + + for i in range(len(files)): + print(files[i]) + ``` + +- The [`find()`](/mojo/stdlib/builtin/string_literal#find), + [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), + [`count()`](/mojo/stdlib/builtin/string_literal#count), and + [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods + now work on string literals. This means that you can write: + + ```mojo + if "Mojo" in "Hello Mojo": + ... + ``` + +- Breakpoints can now be inserted programmatically within the code using the + builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. + + Note: on Graviton instances, the debugger might not be able to resume after + hitting this kind of breakpoint. + +- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that + describes a type that can be represented as a boolean value. To conform to the + trait, a type must implement the `__bool__()` method. + +- Modules within packages can now use purely relative `from` imports: + + ```mojo + from . import another_module + ``` + +- Trivial types, like MLIR types and function types, can now be bound implicitly + to traits that require copy constructors or move constructors, such as + [`Movable`](/mojo/stdlib/builtin/value.html#movable), + [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). + +- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory + value. We hope and expect that this will eventually be replaced by + `Reference(expr).lifetime` as the parameter system evolves, but this is + important in the meantime for use in function signatures. + +- A new magic `__type_of(expr)` call will yield the type of a value. This allows + one to refer to types of other variables. For example: + + ```mojo + fn my_function(x: Int, y: __type_of(x)) -> Int: + let z: __type_of(x) = y + return z + ``` + +### 🦋 Changed + +- As another step towards [removing let + declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md) + we have removed support for let declarations inside the compiler. To ease + migration, we parse `let` declarations as a `var` declaration so your code + won't break. We emit a warning about this, but please switch your code to + using `var` explicitly, because this migration support will be removed in a + subsequent update. + + ```mojo + fn test(): + # treated as a var, but please update your code! + let x = 42 # warning: 'let' is being removed, please use 'var' instead + x = 9 + ``` + +- It is no longer possible to explicitly specify implicit argument parameters in + [automatically parameterized + functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). + This ability was an oversight and this is now an error: + + ```mojo + fn autoparameterized(x: SIMD): + pass + + autoparameterized[DType.int32, 1](3) # error: too many parameters + ``` + +- `vectorize_unroll` has been removed, and + [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter + named `unroll_factor` with a default value of 1. Increasing `unroll_factor` + may improve performance at the cost of binary size. See the + [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) + for more details. + +- The `vectorize` signatures have changed with the closure `func` moved to the + first parameter: + + ```mojo + vectorize[func, width, unroll_factor = 1](size) + vectorize[func, width, size, unroll_factor = 1]() + ``` + + The doc string has been updated with examples demonstrating the difference + between the two signatures. + +- The `unroll` signatures have changed with the closure `func` moved to the + first parameter: + + ```mojo + unroll[func, unroll_count]() + ``` + +- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) and + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types have changed. Now, both + take the type as the first parameter and no longer require the shape + parameter. This allows you to use these types and have sensible defaults. + For example: + + ```mojo + NDBuffer[DType.float32, 3] + ``` + + is equivalent to + + ```mojo + NDBuffer[DType.float32, 3, DimList.create_unknown[3]()] + ``` + + Users can still specify the static shape (if known) to the type: + + ```mojo + NDBuffer[DType.float32, 3, DimList(128, 128, 3)] + ``` + +- The error message for missing function arguments is improved: instead of + describing the number of arguments (e.g. `callee expects at least 3 arguments, + but 1 was specified`) the missing arguments are now described by + name (e.g. `missing 2 required positional arguments: 'b', 'c'`). + +- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait + is now a built-in trait and has been removed from `collections.vector`. + +- The `DynamicVector(capacity: Int)` constructor has been changed to take + `capacity` as a keyword-only argument to prevent implicit conversion from + `Int`. + +- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a + copy. + +- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` + and `toupper()` have been renamed to `str.lower()` and `str.upper()`. + +- The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. + We originally thought about using those as language sugar for references, but + we believe that generic language features combined with the + [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good + experience without dedicated sugar. + +### 🛠️ Fixed + +- [#435](https://github.com/modularml/mojo/issues/435) + Structs with Self type don't always work. +- [#1540](https://github.com/modularml/mojo/issues/1540) + Crash in register_passable self referencing struct. +- [#1664](https://github.com/modularml/mojo/issues/1664) - Improve error + message when `StaticTuple` is constructed with a negative size for + the number of elements. +- [#1679](https://github.com/modularml/mojo/issues/1679) - crash on SIMD of zero + elements. +- Various crashes on invalid code: + [#1230](https://github.com/modularml/mojo/issues/1230), + [#1699](https://github.com/modularml/mojo/issues/1699), + [#1708](https://github.com/modularml/mojo/issues/1708) +- [#1223](https://github.com/modularml/mojo/issues/1223) - Crash when parametric + function is passed as (runtime) argument. The parser now errors out instead. +- [#1530](https://github.com/modularml/mojo/issues/1530) - Crash during + diagnostic emission for parameter deduction failure. +- [#1538](https://github.com/modularml/mojo/issues/1538) and [#1607]( + https://github.com/modularml/mojo/issues/1607) - Crash when returning type + value instead of instance of expected type. This is a common mistake and the + error now includes a hint to point users to the problem. +- [#1613](https://github.com/modularml/mojo/issues/1613) - Wrong type name in + error for incorrect `self` argument type in trait method declaration. +- [#1670](https://github.com/modularml/mojo/issues/1670) - Crash on implicit + conversion in a global variable declaration. +- [#1741](https://github.com/modularml/mojo/issues/1741) - Mojo documentation + generation doesn't show `inout`/`owned` on variadic arguments. +- [#1621](https://github.com/modularml/mojo/issues/1621) - VS Code does not + highlight `raises` and `capturing` in functional type expressions. +- [#1617](https://github.com/modularml/mojo/issues/1617) - VS Code does not + highlight `fn` in specific contexts. +- [#1740](https://github.com/modularml/mojo/issues/1740) - LSP shows unrelated + info when hovering over a struct. +- [#1238](https://github.com/modularml/mojo/issues/1238) - File shadows Mojo + package path. +- [#1429](https://github.com/modularml/mojo/issues/1429) - Crash when using + nested import statement. +- [#1322](https://github.com/modularml/mojo/issues/1322) - Crash when missing + types in variadic argument. +- [#1314](https://github.com/modularml/mojo/issues/1314) - Typecheck error when + binding alias to parametric function with default argument. +- [#1248](https://github.com/modularml/mojo/issues/1248) - Crash when importing + from file the same name as another file in the search path. +- [#1354](https://github.com/modularml/mojo/issues/1354) - Crash when importing + from local package. +- [#1488](https://github.com/modularml/mojo/issues/1488) - Crash when setting + generic element field. +- [#1476](https://github.com/modularml/mojo/issues/1476) - Crash in interpreter + when calling functions in parameter context. +- [#1537](https://github.com/modularml/mojo/issues/1537) - Crash when copying + parameter value. +- [#1546](https://github.com/modularml/mojo/issues/1546) - Modify nested vector + element crashes parser. +- [#1558](https://github.com/modularml/mojo/issues/1558) - Invalid import causes + parser to crash. +- [#1562](https://github.com/modularml/mojo/issues/1562) - Crash when calling + parametric type member function. +- [#1577](https://github.com/modularml/mojo/issues/1577) - Crash when using + unresolved package as a variable. +- [#1579](https://github.com/modularml/mojo/issues/1579) - Member access into + type instances causes a crash. +- [#1602](https://github.com/modularml/mojo/issues/1602) - Interpreter failure + when constructing strings at compile time. +- [#1696](https://github.com/modularml/mojo/issues/1696) - Fixed an issue that + caused syntax highlighting to occasionally fail. +- [#1549](https://github.com/modularml/mojo/issues/1549) - Fixed an issue when + the shift amount is out of range in `SIMD.shift_left` and `SIMD.shift_right`. + +## v0.7.0 (2024-01-25) + +### ⭐️ New + +- A new Mojo-native dictionary type, + [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. + `Dict` stores values that conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + trait. Keys need to conform to the new + [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is + not yet implemented by other standard library types. In the short term, you + can create your own wrapper types to use as keys. For example, the following + sample defines a `StringKey` type and uses it to create a dictionary that maps + strings to `Int` values: + + ```mojo + from collections.dict import Dict, KeyElement + + @value + struct StringKey(KeyElement): + var s: String + + fn __init__(inout self, owned s: String): + self.s = s ^ + + fn __init__(inout self, s: StringLiteral): + self.s = String(s) + + fn __hash__(self) -> Int: + return hash(self.s) + + fn __eq__(self, other: Self) -> Bool: + return self.s == other.s + + fn main() raises: + var d = Dict[StringKey, Int]() + d["cats"] = 1 + d["dogs"] = 2 + print(len(d)) # prints 2 + print(d["cats"]) # prints 1 + print(d.pop("dogs")) # prints 2 + print(len(d)) # prints 1 + ``` + + We plan to add `KeyElement` conformance to standard library types in + subsequent releases. + +- Users can opt-in to assertions used in the standard library code by + specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to + compile your source file(s). In the case that an assertion is fired, + the assertion message will be printed along with the stack trace + before the program exits. By default, assertions are _not enabled_ + in the standard library right now for performance reasons. + +- The Mojo Language Server now implements the References request. IDEs use + this to provide support for **Go to References** and **Find All References**. + A current limitation is that references outside of the current document are + not supported, which will be addressed in the future. + +- The [`sys.info`](/mojo/stdlib/sys/info) module now includes + `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` + functions. + +- Homogenous variadic arguments consisting of memory-only types, such as + `String` are more powerful and easier to use. These arguments are projected + into a + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). + + (Previous releases made it easier to use variadic lists of register-passable + types, like `Int`.) + + Subscripting into a `VariadicListMem` now returns the element instead of an + obscure internal type. In addition, we now support `inout` and `owned` + variadic arguments: + + ```mojo + fn make_worldly(inout *strs: String): + # This "just works" as you'd expect! + for i in range(len(strs)): + strs[i] += " world" + fn main(): + var s1: String = "hello" + var s2: String = "konnichiwa" + var s3: String = "bonjour" + make_worldly(s1, s2, s3) + print(s1) # hello world + print(s2) # konnichiwa world + print(s3) # bonjour world + ``` + + (Previous releases made it easier to use variadic lists, but subscripting into + a `VariadicListMem` returned a low-level pointer, which required the user to + call `__get_address_as_lvalue()` to access the element.) + + Note that subscripting the variadic list works nicely as above, but + iterating over the variadic list directly with a `for` loop produces a + [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead + of the desired value, so an extra subscript is required; We intend to fix this + in the future. + + ```mojo + fn make_worldly(inout *strs: String): + # Requires extra [] to dereference the reference for now. + for i in strs: + i[] += " world" + ``` + + Heterogenous variadic arguments have not yet been moved to the new model, but + will in future updates. + + Note that for variadic arguments of register-passable types like `Int`, the + variadic list contains values, not references, so the dereference operator + (`[]`) is not required. This code continues to work as it did previously: + + ```mojo + fn print_ints(*nums: Int): + for num in nums: + print(num) + print(len(nums)) + ``` + +- Mojo now has a prototype version of a safe + [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's + lifetime tracking pass can reason about references to safely extend local + variable lifetime, and check indirect access safety. The `Reference` type + is brand new (and currently has no syntactic sugar) so it must be explicitly + dereferenced with an empty subscript: `ref[]` provides access to the + underlying value. + + ```mojo + fn main(): + var a: String = "hello" + var b: String = " references" + + var aref = Reference(a) + aref[] += b + print(a) # prints "hello references" + + aref[] += b + # ^last use of b, it is destroyed here. + + print(aref[]) # prints "hello references references" + # ^last use of a, it is destroyed here. + ``` + + While the `Reference` type has the same in-memory representation as a C + pointer or the Mojo `Pointer` type, it also tracks a symbolic "lifetime" value + so the compiler can reason about the potentially accessed set of values. This + lifetime is part of the static type of the reference, so it propagates through + generic algorithms and abstractions built around it. + + The `Reference` type can form references to both mutable and immutable memory + objects, e.g. those on the stack or borrowed/inout/owned function arguments. + It is fully parametric over mutability, eliminating the [problems with code + duplication due to mutability + specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and + provides the base for unified user-level types. For example, it could be + used to implement an array slice object that handles both mutable and immutable + array slices. + + While this is a major step forward for the lifetimes system in Mojo, it is + still _very_ early and awkward to use. Notably, there is no syntactic sugar + for using references, such as automatic dereferencing. Several aspects of it + need to be more baked. It is getting exercised by variadic memory arguments, + which is why they are starting to behave better now. + + Note: the safe `Reference` type and the unsafe pointer types are defined in + the same module, currently named `memory.unsafe`. We expect to restructure + this module in a future release. + +- Mojo now allows types to implement `__refattr__()` and `__refitem__()` to + enable attribute and subscript syntax with computed accessors that return + references. For common situations where these address a value in memory this + provides a more convenient and significantly more performant alternative to + implementing the traditional get/set pairs. Note: this may be changed in the + future when references auto-dereference—at that point we may switch to just + returning a reference from `__getattr__()`. +- Parametric closures can now capture register passable typed values by copy + using the `__copy_capture` decorator. For example, the following code will + print `5`, not `2`. + + ```mojo + fn foo(x: Int): + var z = x + + @__copy_capture(z) + @parameter + fn formatter() -> Int: + return z + z = 2 + print(formatter()) + + fn main(): + foo(5) + ``` + +- String now implements KeyElement and may be used as a key in Dict. +- More robust support for structs with fields of self referencing types. + For example, the following code will work and print `0`: + + ```mojo + struct Foo(CollectionElement): + var vec: DynamicVector[Self] + + fn __init__(inout self: Self): + self.vec = DynamicVector[Self]() + + fn __moveinit__(inout self: Self, owned existing: Self): + self.vec = existing.vec ^ + + fn __copyinit__(inout self: Self, existing: Self): + self.vec = existing.vec + + + fn main(): + var foo = Foo() + print(len(foo.vec)) + ``` + +### ❌ Removed + +- The `__takeinit__` special constructor form has been removed from the + language. This "non-destructive move" operation was previously wired into the + `x^` transfer operator, but had unpredictable behavior that wasn't consistent. + Now that Mojo has traits, it is better to model this as an explicit `.take()` + operation on a type, which would transfer out the contents of the type without + ending its lifetime. For example, for a type that holds a pointer, `take()` + might return a new instance pointing to the same data, and null out its own + internal pointer. + + This change makes it clear when a lifetime is ended versus when the + contents of an LValue are explicitly taken. + +- The current implementation of autotuning has been deprecated, as Mojo's + autotuning implementation is undergoing a redesign. Tutorials around the + current implementation have also been removed as they are being rewritten. + + Consequently, the `autotune()`, `autotune_fork()`, and `search()` functions + have been removed from the standard library. + +- The `_OldDynamicVector` type that worked only on register passable element + types has been removed. Please migrate uses to + [`DynamicVector`](/mojo/stdlib/collections/list#list) which + works on both register passable and memory types. + +- The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using + either [`DynamicVector`](/mojo/stdlib/collections/list#list) + or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + instead. + +- The `@adaptive` decorator has been removed from the language. Any uses of the + decorator in a non-search context can be replaced with `@parameter if`. For + example: + + ```mojo + @adaptive + fn foo[a: Bool](): + constrained[a]() + body1() + + @adaptive + fn foo[a: Bool](): + constrained[not a]() + body2() + ``` + + Can be rewritten as: + + ```mojo + fn foo[a: Bool](): + @parameter + if a: + body1() + else: + body2() + ``` + + Consequently, the special `__adaptive_set` attribute has been removed as well. + +- Result parameters have been removed from Mojo. Result parameter declarations + in function parameter lists are no longer allowed, nor are forward alias + declarations. This includes removing the `param_return` statement. + +- The `@noncapturing` and `@closure` decorators have been removed due to + refinements and improvements to the closure model. See below for more details! + +### 🦋 Changed + +- The Mojo closure model has been refined to be more straightforward and safe. + Mojo has two closure types: parameter closures and runtime closures. Parameter + closures can be used in higher-order functions and are the backbone of + functions like `vectorize` and `parallelize`. They are always denoted by + `@parameter` and have type `fn() capturing -> T` (where `T` is the return + type). + + On the other hand, runtime closures are always dynamic values, capture values + by invoking their copy constructor, and retain ownership of their capture + state. You can define a runtime closure by writing a nested function that + captures values: + + ```mojo + fn outer(b: Bool, x: String) -> fn() escaping -> None: + fn closure(): + print(x) # 'x' is captured by calling String.__copyinit__ + + fn bare_function(): + print("hello") # nothing is captured + + if b: + # closure can be safely returned because it owns its state + return closure^ + + # function pointers can be converted to runtime closures + return bare_function + ``` + + The type of runtime closures are of the form `fn() escaping -> T`. You + can pass equivalent function pointers as runtime closures. + + Stay tuned for capture list syntax for move capture and capture by reference, + and a more unified closure model! + +- The `@unroll(n)` decorator can now take a parameter expression for + the unroll factor, i.e. `n` can be a parameter expression that is + of integer type. + +- The `cpython` module in the `python` package has been moved to be an internal + module, i.e, `_cpython`. + +- `AnyType` and `Destructable` have been unified into a single trait, `AnyType`. + Every nominal type (i.e. all structs) now automatically conform to `AnyType`. + +- Previously, the `mojo package` command would output a Mojo package that + included both partly-compiled Mojo code, as well as fully-compiled machine + code for a specific computer architecture -- the architecture of the machine + being used to invoke the `mojo package` command. + + Now, `mojo package` only includes partly-compiled Mojo code. It is only fully + compiled for the specific computer architecture being used at the point that + the package is first `import`-ed. As a result, Mojo packages are smaller and + more portable. + +- The `simd_width` and `dtype` parameters of `polynomial_evaluate` have been + switched. Based on the request in + [#1587](https://github.com/modularml/mojo/issues/1587), the + `polynomial_evaluate` function has also been extended so that the + `coefficients` parameter can take either a either a + [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a + [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). + +- As a tiny step towards removing `let` declarations, this release removes the + warning: `'var' was never mutated, consider switching to a 'let'`. + +### 🛠️ Fixed + +- [#1595](https://github.com/modularml/mojo/issues/1595) - Improve error message + when trying to materialize `IntLiteral` in runtime code. +- Raising an error from the initializer of a memory-only type now works + correctly in the presence of complex control flow. Previously Mojo could run + the destructor on `self` before it was initialized when exiting with an + error. +- [#1096](https://github.com/modularml/mojo/issues/1096) - Improve warning + messages for dead code in conditionals like `or` expressions. +- [#1419](https://github.com/modularml/mojo/issues/1419) - Fix assertion failure + with uninitialized lattice values. +- [#1402](https://github.com/modularml/mojo/issues/1402) - Fix movable trait not + detected on recursive struct implemented with `AnyPointer`. +- [#1399](https://github.com/modularml/mojo/issues/1399) - Fix parser crash when + a parameter type in a struct that implements a trait is misspelled. +- [#1152](https://github.com/modularml/mojo/issues/1152) - Allow mutable `self` + argument when overloading operators using dunder methods. +- [#1493](https://github.com/modularml/mojo/issues/1493) - Fix crash in + `DynamicVector` copy constructor in certain situations. +- [#1316](https://github.com/modularml/mojo/issues/1316) - The `benchmark.keep` + function now properly handles vector types. +- [#1505](https://github.com/modularml/mojo/issues/1505) - The `simd.shuffle` + operation now works on 64 element permutations. +- [#1355](https://github.com/modularml/mojo/issues/1355) - Fix `String.find()` + returning wrong value when starting index is non-zero. +- [#1367](https://github.com/modularml/mojo/issues/1367) - Fix `String.replace()` + returning incorrect results for multi-character search strings. +- [#1535](https://github.com/modularml/mojo/issues/1535) - Invalid error `field + 'w.x.y' destroyed out of the middle of a value, preventing the overall value + from being destroyed`. +- [#1475](https://github.com/modularml/mojo/issues/1475) - Assertion failure in + nested loop. +- [#1591](https://github.com/modularml/mojo/issues/1591) - Assertion failure + when using `AnyType` struct member. +- [#1503](https://github.com/modularml/mojo/issues/1503) - Rename the mojo build + of LLDB to `mojo-lldb`, to prevent name collisions with the system's LLDB. +- [#1542](https://github.com/modularml/mojo/issues/1542) - `@unroll` does not + accept alias as unroll factor. +- [#1443](https://github.com/modularml/mojo/issues/1443) - Compiler crash on + variadic list of traits. +- [#1604](https://github.com/modularml/mojo/issues/1604) - Variable of trivial + type not destroyed by transferring ownership. +- [#1341](https://github.com/modularml/mojo/issues/1341) - Segmentation fault + when passing closures around. +- [#217](https://github.com/modularml/mojo/issues/217) - Closure state is + stack allocated. + +## v0.6.1 (2023-12-18) + +### ⭐️ New + +- The Mojo REPL now provides limited support for the `%cd` magic command. + + This command automatically maintains an internal stack of directories you + visit during the REPL session. Usage: + + - `%cd 'dir'`: change to directory `dir` and push it on the directory stack. + - `%cd -`: pop the directory stack and change to the last visited directory. + +- Structs decorated with `@value` now automatically conform to the + [`Movable`](/mojo/stdlib/builtin/value.html#movable) + and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. + +- [`String`](/mojo/stdlib/builtin/string.html#string) now has new + [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and + [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, + respectively, to Python's `str.toupper()` and `str.tolower()`. + +- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and + [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types + implementing the `__hash__()` method. Future releases will add `Hashable` + support to Standard Library types. In the meantime, the `hash` module includes + a version of the `hash()` function that works on arbitrary byte strings. To + generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you + use the internal `_hash_simd()` function: + + ```mojo + from builtin.hash import _hash_simd + + fn gen_simd_hash(): + let vector = SIMD[DType.int64, 4](1, 2, 3, 4) + let hash = _hash_simd(vector) + ``` + +- Several standard library types now conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), + [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), + [`DynamicVector`](/mojo/stdlib/collections/list#list), + [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), + [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), + and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). + +### 🦋 Changed + +- `utils.vector` has been moved to a new `collections` package to make + space for new collections. This means that if you had previous code + that did `from utils.vector import DynamicVector`, it now needs to + be `from collections.vector import DynamicVector` due to the move. + +- The special destructor method `__del__()` has been changed to enforce + that it cannot raise an error. Raising destructors are not supported properly + at the moment. + +### 🛠️ Fixed + +- [#1421](https://github.com/modularml/mojo/issues/1421) - Fixed a crash when + using Tuples in the REPL. + +- [#222](https://github.com/modularml/mojo/issues/222) - Generate an error + for obviously self recursive functions. + +- [#1408](https://github.com/modularml/mojo/issues/1408) - Fix overload + resolution when candidates can return generic types. + +- [#1413](https://github.com/modularml/mojo/issues/1413) and + [#1395](https://github.com/modularml/mojo/issues/1395) - Do not crash when + re-declaring a builtin declaration. + +- [#1307](https://github.com/modularml/mojo/issues/1307) - Fix compatibility of + function signatures that only differ in default argument values. + +- [#1380](https://github.com/modularml/mojo/issues/1380) - Fix printing + of empty `String`. + +## v0.6.0 (2023-12-04) + +### 🔥 Legendary + +- Traits have arrived! + + You can now define a _trait_, which consists of a required set of method + prototypes. A struct can _conform to_ the trait by implementing these methods. + This lets you write generic functions that work on any structs that conform to + a given trait. + + The following section gives a brief overview of traits—see the + [Mojo Manual](/mojo/manual/traits.html) and this + [traits blog post](https://modul.ar/traits-blog) for more details! + + Traits are declared with the `trait` keyword. The bodies of traits should + contain method signatures declared with `...` as their bodies. Default + method implementations are not supported yet. + + ```mojo + trait SomeTrait: + fn required_method(self, x: Int): ... + ``` + + The trait can be implemented on a struct by inheriting from it. + + ```mojo + struct SomeStruct(SomeTrait): + fn required_method(self, x: Int): + print("hello traits", x) + ``` + + You can then write a generic functions that accepts any type that conforms to + the trait. You do this by creating a parameterized function with a + trait-typed parameter: + + ```mojo + fn fun_with_traits[T: SomeTrait](x: T): + x.required_method(42) + ``` + + Which can be invoked with instances of types that conform to the trait: + + ```mojo + var thing = SomeStruct() + # Infer the parameter `T`! + fun_with_traits(thing) + ``` + + Traits can also inherit from other traits, which simply requires that + implementors of the child trait also conform to all parent traits. + + ```mojo + trait Parent: + fn parent_func(self): ... + + trait Child(Parent): + fn child_func(self): ... + ``` + + Then, both child and parent trait methods can be invoked on instances of + the trait `Child`. As well, an instance of the child trait can be converted to + an instance of the parent trait. + + ```mojo + fn the_parents[T: Parent](x: T): + x.parent_func() + + fn the_children[T: Child](x: T): + x.child_func() + x.parent_func() + # Upcast `x` from instance of `Child` to `Parent`. + the_parents(x) + ``` + + For more information, see the [Traits page](/mojo/manual/traits.html) + in the Mojo Manual. + +- A fundamental `Destructable` trait has been added to the language. This is a + core trait that every trait automatically conforms to. This enables + destruction of generic types and generic collections. + + **Note:** We're aware that this trait might be better spelled `Destructible`. + We're planning on removing it in the future and moving its functionality to + `AnyType` so that any type that doesn't provide its own destructor will have + a default, no-op destructor. + +- We've added some traits to the standard library, you can implement these on + your own types: + + - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) + - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) + - [`Movable`](/mojo/stdlib/builtin/value.html#movable) + - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) + - [`Intable`](/mojo/stdlib/builtin/int.html#intable) + - [`Sized`](/mojo/stdlib/builtin/len.html#sized) + - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + +- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), + [`str()`](/mojo/stdlib/builtin/str.html#str), and + [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with + types that implement the `Sized`, `Stringable`, and `Intable` traits, + respectively. + +- [`DynamicVector`](/mojo/stdlib/collections/list#list) is now a + proper generic collection that can use any type that implements the `Movable` + and `Copyable` traits. This means you can now write, for example, + `DynamicVector[String]`. Also, `DynamicVector` now invokes its element + destructors upon destruction, so `_del_old` has been deleted. + +- `print` now works on any types that implement `Stringable` by invoking their + `__str__` method: + + ```mojo + @value + struct BoxedInt(Stringable): + var value: Int + + fn __str__(self) -> String: + return self.value + + print(BoxedInt(11), "hello traits!", BoxedInt(42)) + ``` + +### ⭐️ New + +- The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. + It doesn't include _everything_ about Mojo yet, but it includes a lot, + and more than the original [programming + manual](/mojo/programming-manual.html) (now deprecated). + + Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on + GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love + to accept contributions to help us improve them! + +- Mojo now supports partial automatic parameterization: when a function is + declared with an argument of a partially bound type, the unbound parameters + of that type are implicitly added to the function's input parameters. For + example: + + ```mojo + @value + struct Fudge[a: Int, b: Int, c: Int = 7]: ... + + # These function declarations are roughly equivalent: + fn eat(f: Fudge[5]): ... # implicitly parameterized + fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized + ``` + + In the first signature for `eat()`, the `b` parameter isn't bound, so it's + _implicitly_ added as an input parameter on the function. + + In the second signature for `eat()`, the author has explicitly defined an + input parameter (`_b`), which is bound to the second parameter on the argument + type (which happens to be `b`). + + Both functions can be called like this: + + ```mojo + eat(Fudge[5, 8]()) + ``` + + Mojo infers the value of the `b` parameter from the argument (in this case, + 8). + + With the second signature, you can also pass the `_b` parameter value + explicitly: + + ```mojo + eat[3](Fudge[5, 3]()) + ``` + + Moreover, Mojo now allows you to explicitly mark parameters as unbound using + the `_` as syntax meaning "placeholder for an unbound parameter." For example: + + ```mojo + # These function declarations are roughly equivalent: + fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized + fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized + fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized + ``` + + The first two signatures explicitly unbind the `b` and `c` parameters. + + In the last signature, the `_b` and `_c` parameters are explicitly declared by + the author, and bound to the `b` and `c` parameters in the argument type. + + Any of these signatures can be called like this: + + ```mojo + eat(Fudge[5, 8]()) + eat(Fudge[5, 8, 9]()) + ``` + + Note that the default parameter values of struct parameters are bound, unless + explicitly unbound by the user. + + For more information, see the + [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). + +- Parametric types can now be partially bound in certain contexts. For example, + a new `Scalar` type alias has been added defined as: + + ```mojo + alias Scalar = SIMD[size=1] + ``` + + Which creates a parametric type alias `Scalar` with a single parameter of type + `DType`. Types can also be partially or fully bound in other contexts. For + instance, `alias` declarations of type values inside functions now work + properly: + + ```mojo + fn type_aliases(): + alias T = SIMD + print(T[DType.float32, 1]()) + alias Partial = T[type=DType.int32] + print(Partial[2]()) + ``` + +- The `__mlir_op` feature now supports operations that return multiple results. + To use them, you write the `_type` field as a `Tuple` of types. For example: + + ```mojo + # The `ret` variable has type `Tuple[Int, Int]`. + let ret = __mlir_op.`multi_result_op`[_type=(Int, Int)]() + ``` + +- Mojo now has the ability to read raw bytes from a file using the + [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. + For example: + + ```mojo + with open("file.binary", "r") as f: + data = f.read_bytes() + ``` + +- A size argument was added to the + [`read()`](/mojo/stdlib/builtin/file.html#read) and + [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the + builtin `file.FileHandle`. The size argument defaults to -1 and maintains the + previous "read to EOF" behavior when size is negative. + + ```mojo + with open("file.binary", "r") as f: + data1 = f.read_bytes(1024) + data2 = f.read_bytes(256) + ``` + +- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and + `read_text()` methods to read file contents from a path: + + ```mojo + let text_path = Path("file.txt") + let text = text_path.read_text() + + let binary_path = Path("file.binary") + let data = binary_path.read_bytes() + ``` + +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` + methods to save and load to file. These + methods preserve shape and datatype information. For example: + + ```mojo + let tensor = Tensor[DType.float32]() + tensor.save(path) + + let tensor_from_file = Tensor[DType.float32].load(path) + ``` + +- Subscripting added to + [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and + [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): + + ```mojo + let p = DTypePointer[DType.float16].alloc(4) + for i in range(4): + p[i] = i + print(p[i]) + ``` + +- `file.FileHandle` now has a `seek()` method. + +- [`String`](/mojo/stdlib/builtin/string.html#string) now has an + [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to + Python's `str.rfind()`. + +- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method + analogous to Python's `str.split()`. + +- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a + [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to + Python's `pathlib.Path.suffix`. + +- The Mojo REPL now supports indented expressions, making it a bit easier to + execute expressions copied from an indented block (such as a doc string). + +- The Mojo Language Server now implements the Document Symbols request. IDEs use + this to provide support for **Outline View** and **Go to Symbol**. This + addresses [Issue #960](https://github.com/modularml/mojo/issues/960). + +- The Mojo Language Server now shows documentation when code completing modules + or packages in `import` statements. + +- The Mojo Language Server now supports processing code examples, defined as + markdown Mojo code blocks, inside of doc strings. This enables IDE features + while writing examples in API documentation. + +- The Mojo Language Server now provides semantic token information, providing + better highlighting for symbols whose semantics are not statically analyzable. + +- The Mojo Language Server now classifies doc strings as folding ranges, + making them easier to collapse, reducing vertical space while editing. + +- Command line options for the `mojo` driver that take arguments can now be + written in either of two ways: both `--foo FOO` and `--foo=FOO`. Previously, + only the former was valid. + +### 🦋 Changed + +- Variadic list types + [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + are now iterable. Variadic arguments are automatically projected into one of + these types inside the function body, so var args can be iterated: + + ```mojo + fn print_ints(*nums: Int): + for num in nums: + print(num) + print(len(nums)) + ``` + +- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) + package now raise an `Error` when the assertion fails instead of returning a + `Bool` for whether the assertion succeeded or not. + +- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no + longer (implicitly) assumed to be register-passable. A new `AnyRegType` type + is used to represent generic types that are register passable. + +- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) + report is now an argument instead of a parameter: + + ```mojo + let report = benchmark.run[timer]() + report.print(Unit.ms) + ``` + +- Default values on `inout` arguments are no longer permitted, i.e. the + following will now raise an error: + + ```mojo + fn inout_default(inout x: Int = 2): ... + ``` + +- The `to_string()` function has been removed from + [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of + the new `__str__()` function. This composes better with traits so it can be + used with the generic `str()` function. + +### 🛠️ Fixed + +- [#734](https://github.com/modularml/mojo/issues/734) - Consumption of struct + works only for types with a `__del__` method. + +- [#910](https://github.com/modularml/mojo/issues/910) - Parser crash when + using memory-only generic type as return of function that `raise`s. + +- [#1060](https://github.com/modularml/mojo/issues/1060) - Mojo happily parses + code that has messed up indentation + +- [#1159](https://github.com/modularml/mojo/issues/1159) - The language server + doesn't warn about bad return type. + +- [#1166](https://github.com/modularml/mojo/issues/1166) - warning: unreachable + code after return statement with context manager + +- [#1098](https://github.com/modularml/mojo/issues/1098) - The language server + doesn't highlight properties of PythonObjects correctly. + +- [#1153](https://github.com/modularml/mojo/issues/1153) - The language server + crashes when parsing an invalid multi-nested module import. + +- [#1236](https://github.com/modularml/mojo/issues/1236) - The language server + doesn't show autocomplete in if statements. + +- [#1246](https://github.com/modularml/mojo/issues/1246) - Warning diagnostics + are transient in the presence of caching. + +### Known Issue + +- There is an issue affecting Jupyter notebooks that use autotuning and traits. + This issue only manifests on macOS, and the same code runs without issue + outside of the notebooks. This issue affects the _Matrix multiplication in + Mojo_ notebook. + +## v0.5.0 (2023-11-2) + +### ⭐️ New + +- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the + architectural SIMD width of the type. This means you can write + `SIMD[DType.float32]` which is equivalent to + `SIMD[DType.float32, simdwidthof[DType.float32]()]`. + +- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` + function that allows you to concatenate two `SIMD` values together and produce + a new `SIMD` value. + +- Mojo now supports compile-time _keyword parameters_, in addition to existing + support for [keyword + arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For + example: + + ```mojo + fn foo[a: Int, b: Int = 42](): + print(a, "+", b) + + foo[a=5]() # prints '5 + 42' + foo[a=7, b=13]() # prints '7 + 13' + foo[b=20, a=6]() # prints '6 + 20' + ``` + + Keyword parameters are also supported in structs: + + ```mojo + struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]: + fn __init__(inout self): + print(msg, a) + + fn use_kw_params(): + KwParamStruct[a=42]() # prints '🔥mojo🔥 42' + KwParamStruct[5, msg="hello"]() # prints 'hello 5' + KwParamStruct[msg="hello", a=42]() # prints 'hello 42' + ``` + + For more detail, see the [programming + manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). + + For the time being, the following notable limitations apply: + + - Keyword-only parameters are **not supported** yet: + + ```mojo + fn baz[*args: Int, b: Int](): pass # fails + fn baz[a: Int, *, b: Int](): pass # fails + ``` + + (The analogous keyword-only arguments in Python are described in + [PEP 3102](https://peps.python.org/pep-3102/).) + + - Variadic keyword parameters are **not supported** yet: + + ```mojo + fn baz[a: Int, **kwargs: Int](): pass # fails + ``` + +- Mojo now supports "automatic" parameterization of functions. What this means + is that if a function argument type is parametric but has no bound parameters, + they are automatically added as input parameters on the function. This works + with existing features to allow you to write parametric functions with less + boilerplate. + + ```mojo + @value + struct Thing[x: Int, y: Int]: + pass + + fn foo(v: Thing): + print(v.x) + print(v.y) + + fn main(): + let v = Thing[2, 3]() + foo(v) + ``` + + However, partial autoparameterization is **not supported** yet: + + ```mojo + fn foo(v: Thing[y=7]): # Partially bound type not allowed yet. + ... + ``` + +- Keyword argument passing is supported when invoking `__getitem__` using + the bracket syntax: + + ```mojo + @value + struct MyStruct: + fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: + return x * y + z + + MyStruct()[z=7, x=3, y=5] # returns 22 + ``` + + However, keyword argument passing to `__setitem__` using the bracket syntax is + **not supported** yet: + + ```mojo + @value + struct OtherStruct: + fn __setitem__(self, x: Int, y: Int): pass + + OtherStruct()[x=1] = 4 # fails + ``` + +- Function argument input parameters can now be referenced within the signature + of the function: + + ```mojo + fn foo(x: SIMD, y: SIMD[x.type, x.size]): + pass + ``` + +- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been + simplified and improved so you can now run: + + ```mojo + import benchmark + from time import sleep + + fn sleeper(): + sleep(.01) + + fn main(): + let report = benchmark.run[sleeper]() + print(report.mean()) + ``` + + It no longer requires a capturing `fn` so can benchmark functions outside the + same scope. + + You can print a report with: + + ```mojo + report.print() + ``` + + ```plaintext + --------------------- + Benchmark Report (s) + --------------------- + Mean: 0.012314264957264957 + Total: 1.440769 + Iters: 117 + Warmup Mean: 0.0119335 + Warmup Total: 0.023866999999999999 + Warmup Iters: 2 + Fastest Mean: 0.012227958333333334 + Slowest Mean: 0.012442699999999999 + ``` + + Units for all functions default to seconds, but can be changed with: + + ```mojo + from benchmark import Unit + + report.print[Unit.ms]() + ``` + +- Mojo now supports struct parameter deduction (a.k.a. class template argument + deduction, or CTAD) for partially bound types. Struct parameter deduction is + also possible from static methods. For example: + + ```mojo + @value + struct Thing[v: Int]: pass + + struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]: + fn __init__(inout self, x: Thing[a]): + print("hello", a, b, c) + + @staticmethod + fn foo(x: Thing[a]): + print("🔥", a, b, c) + + fn main(): + _ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8' + CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' + ``` + +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and + `tofile()` methods to save and load as bytes from a file. + +- The built-in `print()` function now works on the + [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. + +- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and + [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors + that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) + and [`StaticIntTuple`](/mojo/stdlib/utils/index_.html#staticinttuple) to + initialize shapes. + +- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the + `count()` and `find()` methods to enable counting the number of occurrences or + finding the offset index of a substring in a string. + +- The `String` type now has a `replace()` method which allows you to replace a + substring with another string. + +### 🦋 Changed + +- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + moved under builtins, and no longer need to be imported. + +- Variadic arguments are now automatically projected into a `VariadicList` or + `VariadicListMem` inside the function body. This allows for more flexibility + in using var args. For example: + + ```mojo + fn print_ints(*nums: Int): + let len = len(nums) + for i in range(len): + print(nums[i]) + print(len) + ``` + +- The parameters for + [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + have been switched. The parameters are now `[type, size]` instead of + `[size, type]`. The `InlinedFixedVector` now has a default size which means + that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` + and the default size is used. + +- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer.html#buffer) + and [`NDBuffer`](/mojo/stdlib/buffer/buffer.html#ndbuffer) is renamed to + `tofile()` to match the Python naming. + +- Mojo will now utilize all available cores across all NUMA sockets on the host + machine by default. The prior default behavior was to use all the cores on + the first socket. + +### ❌ Removed + +- The `math.numerics` module is now private, because its types (`FPUtils` and + `FlushDenormals`) should not be used externally. + +### 🛠️ Fixed + +- [#532](https://github.com/modularml/mojo/issues/532) - Compiler optimizing + while True loop away +- [#760](https://github.com/modularml/mojo/issues/760) - Compilation error: + 'hlcf.for.yield' op specifies 0 branch inputs but target expected 1 along + control-flow edge from here +- [#849](https://github.com/modularml/mojo/issues/849) - The `Tensor` type is + now initialized with zeros at construction time. +- [#912](https://github.com/modularml/mojo/issues/912) - Invalid load for + `__get_address_as_lvalue`. +- [#916](https://github.com/modularml/mojo/issues/916) - Parser crash when + specifying default values for `inout` arguments. +- [#943](https://github.com/modularml/mojo/issues/943) - Mojo hangs if you + use continue in the nested loop +- [#957](https://github.com/modularml/mojo/issues/957) - Parser crash when a + function call with variadic arguments of a memory-only type is evaluated at + compile time. +- [#990](https://github.com/modularml/mojo/issues/990) - Fixes rounding + issue with floor division with negative numerator. +- [#1018](https://github.com/modularml/mojo/issues/1018) - In some cases the + sort function was returning invalid results. This release fixes some of these + corner cases. +- [#1010](https://github.com/modularml/mojo/issues/1010) - Initializing tensor + in alias declaration results in crash. +- [#1110](https://github.com/modularml/mojo/issues/1110) - The `time.now()` + function now returns nanoseconds across all operating systems. +- [#1115](https://github.com/modularml/mojo/issues/1115) - cannot load + non-register passable type into SSA register. + +## v0.4.0 for Mac (2023-10-19) + +### 🔥 Legendary + +- Mojo for Mac! + + The Mojo SDK now works on macOS (Apple silicon). This is the same version + previously released for Linux. Get the latest version of the SDK for your Mac + system: + + [Download Now!](https://developer.modular.com/download) + +## v0.4.0 (2023-10-05) + +### ⭐️ New + +- Mojo now supports default parameter values. For example: + + ```mojo + fn foo[a: Int = 3, msg: StringLiteral = "woof"](): + print(msg, a) + + fn main(): + foo() # prints 'woof 3' + foo[5]() # prints 'woof 5' + foo[7, "meow"]() # prints 'meow 7' + ``` + + Inferred parameter values take precedence over defaults: + + ```mojo + @value + struct Bar[v: Int]: + pass + + fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]): + print(msg, a) + + fn main(): + foo(Bar[9]()) # prints 'quack 9' + ``` + + Structs also support default parameters: + + ```mojo + @value + struct DefaultParams[msg: StringLiteral = "woof"]: + alias message = msg + + fn main(): + print(DefaultParams[]().message) # prints 'woof' + print(DefaultParams["meow"]().message) # prints 'meow' + ``` + +- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O + support. You can now write: + + ```mojo + var f = open("my_file.txt", "r") + print(f.read()) + f.close() + ``` + + or + + ```mojo + with open("my_file.txt", "r") as f: + print(f.read()) + ``` + +- Mojo now allows context managers to support an `__enter__` method without + implementing support for an `__exit__` method, enabling idioms like this: + + ```mojo + # This context manager consumes itself and returns it as the value. + fn __enter__(owned self) -> Self: + return self^ + ``` + + Here Mojo _cannot_ invoke a noop `__exit__` method because the context + manager is consumed by the `__enter__` method. This can be used for types + (like file descriptors) that are traditionally used with `with` statements, + even though Mojo's guaranteed early destruction doesn't require that. + +- A very basic version of `pathlib` has been implemented in Mojo. The + module will be improved to achieve functional parity with Python in + the next few releases. + +- The `memory.unsafe` module now contains a `bitcast` function. This is a + low-level operation that enables bitcasting between pointers and scalars. + +- The input parameters of a parametric type can now be directly accessed as + attribute references on the type or variables of the type. For example: + + ```mojo + @value + struct Thing[param: Int]: + pass + + fn main(): + print(Thing[2].param) # prints '2' + let x = Thing[9]() + print(x.param) # prints '9' + ``` + + Input parameters on values can even be accessed in parameter contexts. For + example: + + ```mojo + fn foo[value: Int](): + print(value) + + let y = Thing[12]() + alias constant = y.param + 4 + foo[constant]() # prints '16' + ``` + +- The Mojo REPL now supports code completion. Press Tab while typing + to query potential completion results. + +- Error messages from Python are now exposed in Mojo. For example the following + should print `No module named 'my_uninstalled_module'`: + + ```mojo + fn main(): + try: + let my_module = Python.import_module("my_uninstalled_module") + except e: + print(e) + ``` + +- Error messages can now store dynamic messages. For example, the following + should print "Failed on: Hello" + + ```mojo + fn foo(x: String) raises: + raise Error("Failed on: " + x) + + fn main(): + try: + foo("Hello") + except e: + print(e) + ``` + +### 🦋 Changed + +- We have improved and simplified the `parallelize` function. The function + now elides some overhead by caching the Mojo parallel runtime. + +- The Mojo REPL and Jupyter environments no longer implicitly expose `Python`, + `PythonObject`, or `Pointer`. These symbols must now be imported explicitly, + for example: + + ```mojo + from python import Python + from python.object import PythonObject + from memory.unsafe import Pointer + ``` + +- The syntax for specifying attributes with the `__mlir_op` prefix have changed + to mimic Python's keyword argument passing syntax. That is, `=` should be used + instead of `:`, e.g.: + + ```mojo + # Old syntax, now fails. + __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() + # New syntax. + __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() + ``` + +- You can now print the `Error` object directly. The `message()` method + has been removed. + +### 🛠️ Fixed + +- [#794](https://github.com/modularml/mojo/issues/794) - Parser crash when + using the `in` operator. +- [#936](https://github.com/modularml/mojo/issues/936) - The `Int` constructor + now accepts other `Int` instances. +- [#921](https://github.com/modularml/mojo/issues/921) - Better error message + when running `mojo` on a module with no `main` function. +- [#556](https://github.com/modularml/mojo/issues/556) - UInt64s are now + printed correctly. +- [#804](https://github.com/modularml/mojo/issues/804) - Emit error instead of + crashing when passing variadic arguments of unsupported types. +- [#833](https://github.com/modularml/mojo/issues/833) - Parser crash when + assigning module value. +- [#752](https://github.com/modularml/mojo/issues/752) - Parser crash when + calling async def. +- [#711](https://github.com/modularml/mojo/issues/711) - The overload resolution + logic now correctly prioritizes instance methods over static methods (if + candidates are an equally good match otherwise), and no longer crashed if a + static method has a `Self` type as its first argument. +- [#859](https://github.com/modularml/mojo/issues/859) - Fix confusing error and + documentation of the `rebind` builtin. +- [#753](https://github.com/modularml/mojo/issues/753) - Direct use of LLVM + dialect produces strange errors in the compiler. +- [#926](https://github.com/modularml/mojo/issues/926) - Fixes an issue that + occurred when a function with a return type of `StringRef` raised an error. + When the function raised an error, it incorrectly returned the string value of + that error. +- [#536](https://github.com/modularml/mojo/issues/536) - Report More information + on python exception. + +## v0.3.1 (2023-09-28) + +Our first-ever patch release of the Mojo SDK is here! Release v0.3.1 +includes primarily installation-related fixes. If you’ve had trouble +installing the previous versions of the SDK, this release may be for you. + +### 🛠️ Fixed + +- [#538](https://github.com/modularml/mojo/issues/538) - Installation hangs + during the testing phase. This issue occurs on machines with a low number + of CPU cores, such as free AWS EC2 instances and GitHub Codespaces. +- [#590](https://github.com/modularml/mojo/issues/590) - Installation fails + with a “failed to run python” message. +- [#672](https://github.com/modularml/mojo/issues/672) - Language server hangs + on code completion. Related to #538, this occurs on machines with a low + number of CPU cores. +- [#913](https://github.com/modularml/mojo/issues/913) - In the REPL and Jupyter + notebooks, inline comments were being parsed incorrectly. + +## v0.3.0 (2023-09-21) + +There's more Mojo to love in this, the second release of the Mojo SDK! This +release includes new features, an API change, and bug fixes. + +There's also an updated version of the [Mojo extension for VS +Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + +### ⭐️ New + +- Mojo now has partial support for passing keyword arguments to functions and + methods. For example the following should work: + + ```mojo + fn foo(a: Int, b: Int = 3) -> Int: + return a * b + + fn main(): + print(foo(6, b=7)) # prints '42' + print(foo(a=6, b=7)) # prints '42' + print(foo(b=7, a=6)) # prints '42' + ``` + + Parameters can also be inferred from keyword arguments, for example: + + ```mojo + fn bar[A: AnyType, B: AnyType](a: A, b: B): + print("Hello 🔥") + + fn bar[B: AnyType](a: StringLiteral, b: B): + print(a) + + fn main(): + bar(1, 2) # prints `Hello 🔥` + bar(b=2, a="Yay!") # prints `Yay!` + ``` + + For the time being, the following notable limitations apply: + + - Keyword-only arguments are not supported: + + ```mojo + fn baz(*args: Int, b: Int): pass # fails + fn baz(a: Int, *, b: Int): pass # fails + ``` + + (Keyword-only arguments are described in + [PEP 3102](https://peps.python.org/pep-3102/).) + + - Variadic keyword arguments are not supported: + + ```mojo + fn baz(a: Int, **kwargs: Int): pass # fails + ``` + +- Mojo now supports the `@nonmaterializable` decorator. The purpose is to mark + data types that should only exist in the parameter domain. To use it, a + struct is decorated with `@nonmaterializable(TargetType)`. Any time the + nonmaterializable type is converted from the parameter domain, it is + automatically converted to `TargetType`. A nonmaterializable struct should + have all of its methods annotated as `@always_inline`, and must be computable + in the parameter domain. In the following example, the `NmStruct` type can + be added in the parameter domain, but are converted to `HasBool` when + materialized. + + ```mojo + @value + @register_passable("trivial") + struct HasBool: + var x: Bool + fn __init__(x: Bool) -> Self: + return Self {x: x} + @always_inline("nodebug") + fn __init__(nms: NmStruct) -> Self: + return Self {x: True if (nms.x == 77) else False} + + @value + @nonmaterializable(HasBool) + @register_passable("trivial") + struct NmStruct: + var x: Int + @always_inline("nodebug") + fn __add__(self: Self, rhs: Self) -> Self: + return NmStruct(self.x + rhs.x) + + alias stillNmStruct = NmStruct(1) + NmStruct(2) + # When materializing to a run-time variable, it is automatically converted, + # even without a type annotation. + let convertedToHasBool = stillNmStruct + ``` + +- Mojo integer literals now produce the `IntLiteral` infinite precision integer + type when used in the parameter domain. `IntLiteral` is materialized to the + `Int` type for runtime computation, but intermediate computations at compile + time, using supported operators, can now exceed the bit width of the `Int` + type. + +- The Mojo Language Server now supports top-level code completions, enabling + completion when typing a reference to a variable, type, etc. This resolves + [#679](https://github.com/modularml/mojo/issues/679). + +- The Mojo REPL now colorizes the resultant variables to help distinguish input + expressions from the output variables. + +### 🦋 Changed + +- Mojo allows types to implement two forms of move constructors, one that is + invoked when the lifetime of one value ends, and one that is invoked if the + compiler cannot prove that. These were previously both named `__moveinit__`, + with the following two signatures: + + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __moveinit__(inout self, inout existing: Self): ... + ``` + + We've changed the second form to get its own name to make it more clear that + these are two separate operations: the second has been renamed to + `__takeinit__`: + + ```mojo + fn __moveinit__(inout self, owned existing: Self): ... + fn __takeinit__(inout self, inout existing: Self): ... + ``` + + The name is intended to connote that the operation takes the conceptual value + from the source (without destroying it) unlike the first one which "moves" a + value from one location to another. + + For more information, see the Mojo Manual section on + [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). + +- The Error type in Mojo has changed. Instead of extracting the error message + using `error.value` you will now extract the error message using + `error.message()`. + +### 🛠️ Fixed + +- [#503](https://github.com/modularml/mojo/issues/503) - Improve error message + for failure lowering `kgen.param.constant`. +- [#554](https://github.com/modularml/mojo/issues/554) - Alias of static tuple + fails to expand. +- [#500](https://github.com/modularml/mojo/issues/500) - Call expansion failed + due to verifier error. +- [#422](https://github.com/modularml/mojo/issues/422) - Incorrect comment + detection in multiline strings. +- [#729](https://github.com/modularml/mojo/issues/740) - Improve messaging on + how to exit the REPL. +- [#756](https://github.com/modularml/mojo/issues/756) - Fix initialization + errors of the VS Code extension. +- [#575](https://github.com/modularml/mojo/issues/575) - Build LLDB/REPL with + libedit for a nicer editing experience in the terminal. + +## v0.2.1 (2023-09-07) + +The first versioned release of Mojo! 🔥 + +All earlier releases were considered version 0.1. + +### 🔥 Legendary + +- First release of the Mojo SDK! + + You can now develop with Mojo locally. The Mojo SDK is currently available + for Ubuntu Linux systems, and support for Windows and macOS is coming soon. + You can still develop from a Windows or Mac computer using a container or + remote Linux system. + + The Mojo SDK includes the Mojo standard library and the [Mojo command-line + interface](/mojo/cli/) (CLI), which allows you to run, compile, and package + Mojo code. It also provides a REPL programming environment. + + [Get the Mojo SDK!](https://developer.modular.com/download) + +- First release of the [Mojo extension for VS + Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + + This provides essential Mojo language features in Visual Studio Code, such as + code completion, code quick fixes, docs tooltips, and more. Even when + developing on a remote system, using VS Code with this extension provides + a native-like IDE experience. + +### ⭐️ New + +- A new `clobber_memory` function has been added to the + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. + The clobber memory function tells the system to flush all memory operations + at the specified program point. This allows you to benchmark operations + without the compiler reordering memory operations. + +- A new `keep` function has been added to the + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` + function tries to tell the compiler not to optimize the variable away + if not used. This allows you to avoid compiler's dead code elimination + mechanism, with a low footprint side effect. + +- New `shift_right` and `shift_left` functions have been added to the + [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in + a SIMD vector right/left, filling elements with zeros as needed. + +- A new `cumsum` function has been added to the + [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes + the cumulative sum (also known as scan) of input elements. + +- Mojo Jupyter kernel now supports code completion. + +### 🦋 Changed + +- Extends `rotate_bits_left`, `rotate_left`, `rotate_bits_right`, and + `rotate_right` to operate on Int values. The ordering of parameters has also + been changed to enable type inference. Now it's possible to write + `rotate_right[shift_val](simd_val)` and have the `dtype` and `simd_width` + inferred from the argument. This addresses + [Issue #528](https://github.com/modularml/mojo/issues/528). + +### 🛠️ Fixed + +- Fixed a bug causing the parser to crash when the `with` statement was written + without a colon. + This addresses [Issue #529](https://github.com/modularml/mojo/issues/529). + +- Incorrect imports no longer crash when there are other errors at the top + level of a module. This fixes [Issue + #531](https://github.com/modularml/mojo/issues/531). + +## August 2023 + +### 2023-08-24 + +- Fixed issue where the `with expr as x` statement within `fn` behaved + as if it were in a `def`, binding `x` with function scope instead of using + lexical scope. + +#### ⭐️ New + +- Major refactoring of the standard library to enable packaging and better + import ergonomics: + - The packages are built as binaries to improve startup speed. + - Package and module names are now lowercase to align with the Python style. + - Modules have been moved to better reflect the purpose of the underlying + functions (e.g. `Pointer` is now within the `unsafe` module in the `memory` + package). + - The following modules are now included as built-ins: + `SIMD`, `DType`, `IO`, `Object`, and `String`. + This means it's no longer necessary to explicitly import these modules. + Instead, these modules will be implicitly imported for the user. Private + methods within the module are still accessible using the + `builtin.module_name._private_method` import syntax. + - New `math` package has been added to contain the `bit`, `math`, `numerics`, + and `polynomial` modules. The contents of the `math.math` module are + re-exported into the `math` package. + +- Mojo now supports using memory-only types in parameter expressions and as + function or type parameters: + + ```mojo + @value + struct IntPair: + var first: Int + var second: Int + + fn add_them[value: IntPair]() -> Int: + return value.first + value.second + + fn main(): + print(add_them[IntPair(1, 2)]()) # prints '3' + ``` + +- In addition, Mojo supports evaluating code that uses heap-allocated memory + at compile-time and materializing compile-time values with heap-allocated + memory into dynamic values: + + ```mojo + fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: + var result = DynamicVector[Int]() + for i in range(lowerBound, upperBound, step): + result.push_back(i) + return result + + fn main(): + alias values = fillVector(5, 23, 7) + for i in range(0, values.__len__()): + print(values[i]) # prints '5', '12', and then '19' + ``` + +#### 🦋 Changed + +- `def main():`, without the explicit `None` type, can now be used to define + the entry point to a Mojo program. + +- The `assert_param` function has been renamed to `constrained` and is now + a built-in function. + +- The `print` function now works on `Complex` values. + +#### 🛠️ Fixed + +- Fixed issues with print formatting for `DType.uint16` and `DType.int16`. +- [Issue #499](https://github.com/modularml/mojo/issues/499) - Two new + `rotate_right` and `rotate_left` functions have been added to the SIMD module. +- [Issue #429](https://github.com/modularml/mojo/issues/429) - You can now + construct a `Bool` from a `SIMD` type whose element-type is `DType.bool`. +- [Issue #350](https://github.com/modularml/mojo/issues/350) - Confusing Matrix + implementation +- [Issue #349](https://github.com/modularml/mojo/issues/349) - Missing load_tr + in struct Matrix +- [Issue #501](https://github.com/modularml/mojo/issues/501) - Missing syntax + error messages in Python expressions. + +### 2023-08-09 + +#### 🦋 Changed + +- The `ref` and `mutref` identifiers are now treated as keywords, which means + they cannot be used as variable, attribute, or function names. These keywords + are used by the "lifetimes" features, which is still in development. We can + consider renaming these (as well as other related keywords) when the + development work gels, support is enabled in public Mojo builds, and when we + have experience using them. + +- The argument handling in `def` functions has changed: previously, they had + special behavior that involved mutable copies in the callee. Now, we have a + simple rule, which is that `def` argument default to the `owned` convention + (`fn` arguments still default to the `borrowed` convention). + + This change is mostly an internal cleanup and simplification of the compiler + and argument model, but does enable one niche use-case: you can now pass + non-copyable types to `def` arguments by transferring ownership of a value + into the `def` call. Before, that would not be possible because the copy was + made on the callee side, not the caller's side. This also allows the explicit + use of the `borrowed` keyword with a `def` that wants to opt-in to that + behavior. + +### 2023-08-03 + +#### ⭐️ New + +- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. + This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which + are just views). Therefore, the tensor type performs its own allocation and + free. Here is a simple example of using the tensor type to represent an RGB + image and convert it to grayscale: + + ```mojo + from tensor import Tensor, TensorShape + from utils.index import Index + from random import rand + + let height = 256 + let width = 256 + let channels = 3 + + # Create the tensor of dimensions height, width, channels and fill with + # random value. + let image = rand[DType.float32](height, width, channels) + + # Declare the grayscale image. + var gray_scale_image = Tensor[DType.float32](height, width) + + # Perform the RGB to grayscale transform. + for y in range(height): + for x in range(width): + let r = image[y, x, 0] + let g = image[y, x, 1] + let b = image[y, x, 2] + gray_scale_image[Index(y, x)] = 0.299 * r + 0.587 * g + 0.114 * b + ``` + +#### 🛠️ Fixed + +- [Issue #53](https://github.com/modularml/mojo/issues/53) - `Int` now + implements true division with the `/` operator. Similar to Python, this + returns a 64-bit floating point number. The corresponding in-place operator, + `/=`, has the same semantics as `//=`. + +## July 2023 + +### 2023-07-26 + +#### ⭐️ New + +- Types that define both `__getitem__` and `__setitem__` (i.e. where + sub-scripting instances creates computed LValues) can now be indexed + in parameter expressions. + +- Unroll decorator for loops with constant bounds and steps: + - `@unroll`: Fully unroll a loop. + - `@unroll(n)`: Unroll a loop by factor of n, where `n` is a positive integer. + - Unroll decorator requires loop bounds and iteration step to be + compiler time constant value, otherwise unrolling will fail with + compilation error. This also doesn't make loop induction variable a parameter. + + ```mojo + # Fully unroll the loop. + @unroll + for i in range(5): + print(i) + + # Unroll the loop by a factor of 4 (with remainder iterations of 2). + @unroll(4) + for i in range(10): + print(i) + ``` + +- The Mojo REPL now prints the values of variables defined in the REPL. There is + full support for scalars and structs. Non-scalar SIMD vectors are not + supported at this time. + +#### 🛠️ Fixed + +- [Issue #437](https://github.com/modularml/mojo/issues/437) - Range can now + be instantiated with a PythonObject. + +- [Issue #288](https://github.com/modularml/mojo/issues/288) - Python strings + can now be safely copied. + +### 2023-07-20 + +#### ⭐️ New + +- Mojo now includes a `Limits` module, which contains functions to get the max + and min values representable by a type, as requested in [Issue + #51](https://github.com/modularml/mojo/issues/51). The following functions + moved from `Math` to `Limits`: `inf()`, `neginf()`, `isinf()`, `isfinite()`. + +- Mojo decorators are now distinguished between "signature" and "body" + decorators and are ordered. Signature decorators, like `@register_passable` + and `@parameter`, modify the type of declaration before the body is parsed. + Body decorators, like `@value`, modify the body of declaration after it is + fully parsed. Due to ordering, a signature decorator cannot be applied after + a body decorator. That means the following is now invalid: + + ```mojo + @register_passable # error: cannot apply signature decorator after a body one! + @value + struct Foo: + pass + ``` + +- Global variables can now be exported in Mojo compiled archives, using the + `@export` decorator. Exported global variables are public symbols in compiled + archives and use the variable name as its linkage name, by default. A custom + linkage name can be specified with `@export("new_name")`. This does not affect + variable names in Mojo code. + +- Mojo now supports packages! A Mojo package is defined by placing an + `__init__.mojo` or `__init__.🔥` within a directory. Other files in the same + directory form modules within the package (this works exactly like it + does [in Python](https://docs.python.org/3/tutorial/modules.html#packages)). + Example: + + ```bash + main.🔥 + my_package/ + __init__.🔥 + module.🔥 + my_other_package/ + __init__.🔥 + stuff.🔥 + ``` + + ```mojo + # main.🔥 + from my_package.module import some_function + from my_package.my_other_package.stuff import SomeType + + fn main(): + var x: SomeType = some_function() + ``` + +- Mojo now supports direct module and package imports! Modules and packages can + be imported and bound to names. Module and package elements, like functions, + types, global variables, and other modules, can be accessed using attribute + references, like `my_module.foo`. Note that modules lack runtime + representations, meaning module references cannot be instantiated. + + ```mojo + import builtin.io as io + import SIMD + + io.print("hello world") + var x: SIMD.Float32 = 1.2 + ``` + +#### 🦋 Changed + +- Reverted the feature from 2023-02-13 that allowed unqualified struct members. + Use the `Self` keyword to conveniently access struct members with bound + parameters instead. This was required to fix + [Issue #260](https://github.com/modularml/mojo/issues/260). + +- Updated the RayTracing notebook: added step 5 to create specular lighting for + more realistic images and step 6 to add a background image. + +#### 🛠️ Fixed + +- [Issue #260](https://github.com/modularml/mojo/issues/260) - Definitions + inside structs no longer shadow definitions outside of struct definitions. + +### 2023-07-12 + +#### ⭐️ New + +- Mojo now has support for global variables! This enables `var` and `let` + declaration at the top-level scope in Mojo files. Global variable initializers + are run when code modules are loaded by the platform according to the order of + dependencies between global variables, and their destructors are called in the + reverse order. + +- The [Mojo programming manual](/mojo/programming-manual.html) is now written + as a Jupyter notebook, and available in its entirety in the Mojo Playground + (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of + the same material, but it was not up-to-date.) + +- As a result, we've also re-written `HelloMojo.ipynb` to be much shorter and + provide a more gentle first-user experience. + +- [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now + available. Coroutines form the basis of Mojo's support for asynchronous + execution. Calls to `async fn`s can be stored into a `Coroutine`, from which + they can be resumed, awaited upon, and have their results retrieved upon + completion. + +#### 🦋 Changed + +- `simd_bit_width` in the `TargetInfo` module has been renamed to `simdbitwidth` + to better align with `simdwidthof`, `bitwidthof`, etc. + +#### 🛠️ Fixed + +- The walrus operator now works in if/while statements without parentheses, + e.g. `if x := function():`. + +- [Issue #428](https://github.com/modularml/mojo/issues/428) - The + `FloatLiteral` and `SIMD` types now support conversion to `Int` via the + `to_int` or `__int__` method calls. The behavior matches that of Python, which + rounds towards zero. + +### 2023-07-05 + +#### ⭐️ New + +- Tuple expressions now work without parentheses. For example, `a, b = b, a` + works as you'd expect in Python. +- Chained assignments (e.g. `a = b = 42`) and the walrus operator (e.g. + `some_function(b := 17)`) are now supported. + +#### 🦋 Changed + +- The `simd_width` and `dtype_simd_width` functions in the + [`TargetInfo`](/mojo/stdlib/sys/info) module + have been renamed to `simdwidthof`. + +- The `dtype_` prefix has been dropped from `alignof`, `sizeof`, and + `bitwidthof`. You can now use these functions (e.g. `alignof`) with any + argument type, including `DType`. + +- The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were + moved from the `Numerics` module to the [`Math`](/mojo/MojoStdlib/Math.html) + module, to better align with Python's library structure. + +#### 🛠️ Fixed + +- [Issue #253](https://github.com/modularml/mojo/issues/253) - Issue + when accessing a struct member alias without providing parameters. + +- [Issue #404](https://github.com/modularml/mojo/issues/404) - The docs now use + `snake_case` for variable names, which more closely conforms to Python's + style. + +- [Issue #379](https://github.com/modularml/mojo/issues/379) - Tuple + limitations have been addressed and multiple return values are now supported, + even without parentheses. + +- [Issue #347](https://github.com/modularml/mojo/issues/347) - Tuples no longer + require parentheses. + +- [Issue #320](https://github.com/modularml/mojo/issues/320) - Python objects + are now traversable via `for` loops. + +## June 2023 + +### 2023-06-29 + +#### ⭐️ New + +- You can now share `.ipynb` notebook files in Mojo Playground. Just save a + file in the `shared` directory, and then right-click the file and select + **Copy Sharable link**. To open a shared notebook, you must already have + [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); + when you open a shared notebook, click **Import** at the top of the notebook + to save your own copy. For more details about this feature, see the + instructions inside the `help` directory, in the Mojo Playground file browser. + +#### 🦋 Changed + +- The `unroll2()` and `unroll3()` functions in the + [`Functional`](/mojo/stdlib/algorithm/functional) module have been renamed to + overload the `unroll()` function. These functions unroll 2D and 3D loops and + `unroll()` can determine the intent based on the number of input parameters. + +#### 🛠️ Fixed + +- [Issue #229](https://github.com/modularml/mojo/issues/229) - Issue when + throwing an exception from `__init__` before all fields are initialized. + +- [Issue #74](https://github.com/modularml/mojo/issues/74) - Struct + definition with recursive reference crashes. + +- [Issue #285](https://github.com/modularml/mojo/issues/285) - The + [`TargetInfo`](/mojo/stdlib/sys/info) module now includes + `is_little_endian()` and `is_big_endian()` to check if the target host uses + either little or big endian. + +- [Issue #254](https://github.com/modularml/mojo/issues/254) - Parameter name + shadowing in nested scopes is now handled correctly. + +### 2023-06-21 + +#### ⭐️ New + +- Added support for overloading on parameter signature. For example, it is now +possible to write the following: + + ```mojo + fn foo[a: Int](x: Int): + pass + + fn foo[a: Int, b: Int](x: Int): + pass + ``` + + For details on the overload resolution logic, see the Mojo Manual section on + [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). + +- A new `cost_of()` function has been added to `Autotune`. This meta-function + must be invoked at compile time, and it returns the number of MLIR operations + in a function (at a certain stage in compilation), which can be used to + build basic heuristics in higher-order generators. + + ```mojo + from autotune import cost_of + + fn generator[f: fn(Int) -> Int]() -> Int: + @parameter + if cost_of[fn(Int) -> Int, f]() < 10: + return f() + else: + # Do something else for slower functions... + ``` + +- Added a new example notebook with a basic Ray Tracing algorithm. + +#### 🦋 Changed + +- The `constrained_msg()` in the `Assert` module has been renamed to + `constrained()`. + +#### 🛠️ Fixed + +- Overloads marked with `@adaptive` now correctly handle signatures that differ +only in declared parameter names, e.g. the following now works correctly: + + ```mojo + @adaptive + fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... + + @adaptive + fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ... + ``` + +- [Issue #219](https://github.com/modularml/mojo/issues/219) - Issue when + redefining a function and a struct defined in the same cell. + +- [Issue #355](https://github.com/modularml/mojo/issues/355) - The loop order + in the Matmul notebook for Python and naive mojo have been reordered for + consistency. The loop order now follows (M, K, N) ordering. + +- [Issue #309](https://github.com/modularml/mojo/issues/309) - Use snake case + naming within the testing package and move the asserts out of the TestSuite + struct. + +### 2023-06-14 + +#### ⭐️ New + +- Tuple type syntax is now supported, e.g. the following works: + + ```mojo + fn return_tuple() -> (Int, Int): + return (1, 2) + ``` + +#### 🦋 Changed + +- The `TupleLiteral` type was renamed to just `Tuple`, e.g. + `Tuple[Int, Float]`. + +#### 🛠️ Fixed + +- [Issue #354](https://github.com/modularml/mojo/issues/354) - Returning a tuple + doesn't work even with parens. +- [Issue #365](https://github.com/modularml/mojo/issues/365) - Copy-paste error + in `FloatLiteral` docs. +- [Issue #357](https://github.com/modularml/mojo/issues/357) - Crash when + missing input parameter to variadic parameter struct member function. + +### 2023-06-07 + +#### ⭐️ New + +- Tuple syntax now works on the left-hand side of assignments (in "lvalue" + positions), enabling things like `(a, b) = (b, a)`. There are several + caveats: the element types must exactly match (no implicit conversions), + this only works with values of `TupleLiteral` type (notably, it will not work + with `PythonObject` yet) and parentheses are required for tuple syntax. + +#### ❌ Removed + +- Mojo Playground no longer includes the following Python packages (due to size, + compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): + `torch`, `tensorflow`, `keras`, `transformers`. + +#### 🦋 Changed + +- The data types and scalar names now conform to the naming convention used + by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` + instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). + +#### 🛠️ Fixed + +- [Issue #287](https://github.com/modularml/mojo/issues/287) - computed + lvalues don't handle raising functions correctly +- [Issue #318](https://github.com/modularml/mojo/issues/318) - Large integers + are not being printed correctly +- [Issue #326](https://github.com/modularml/mojo/issues/326) - Float modulo + operator is not working as expected +- [Issue #282](https://github.com/modularml/mojo/issues/282) - Default arguments + are not working as expected +- [Issue #271](https://github.com/modularml/mojo/issues/271) - Confusing error + message when converting between function types with different result semantics + +## May 2023 + +### 2023-05-31 + +#### ⭐️ New + +- Mojo Playground now includes the following Python packages (in response to + [popular demand](https://github.com/modularml/mojo/discussions/173)): + `torch`, `tensorflow`, `polars`, `opencv-python`, `keras`, `Pillow`, `plotly`, + `seaborn`, `sympy`, `transformers`. + +- A new optimization is applied to non-trivial copyable values that are passed + as an owned value without using the transfer (`^`) operator. Consider code + like this: + + ```mojo + var someValue: T = ... + ... + takeValueAsOwned(someValue) + ... + ``` + + When `takeValueAsOwned()` takes its argument as an + [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) + value (this is + common in initializers for example), it is allowed to do whatever it wants + with the value and destroy it when it is finished. In order to support this, + the Mojo compiler is forced to make a temporary copy of the `someValue` + value, and pass that value instead of `someValue`, because there may be other + uses of `someValue` after the call. + + The Mojo compiler is now smart enough to detect when there are no uses of + `someValue` later, and it will elide the copy just as if you had manually + specified the transfer operator like `takeValueAsOwned(someValue^)`. This + provides a nice "it just works" behavior for non-trivial types without + requiring manual management of transfers. + + If you'd like to take full control and expose full ownership for your type, + just don't make it copyable. Move-only types require the explicit transfer + operator so you can see in your code where all ownership transfer happen. + +- Similarly, the Mojo compiler now transforms calls to `__copyinit__` methods + into calls to `__moveinit__` when that is the last use of the source value + along a control flow path. This allows types which are both copyable and + movable to get transparent move optimization. For example, the following code + is compiled into moves instead of copies even without the use of the transfer + operator: + + ```mojo + var someValue = somethingCopyableAndMovable() + use(someValue) + ... + let otherValue = someValue # Last use of someValue + use(otherValue) + ... + var yetAnother = otherValue # Last use of otherValue + mutate(yetAnother) + ``` + + This is a significant performance optimization for things like `PythonObject` + (and more complex value semantic types) that are commonly used in a fluid + programming style. These don't want extraneous reference counting operations + performed by its copy constructor. + + If you want explicit control over copying, it is recommended to use a + non-dunder `.copy()` method instead of `__copyinit__`, and recall that + non-copyable types must always use of the transfer operator for those that + want fully explicit behavior. + +#### 🛠️ Fixed + +- [Issue #231](https://github.com/modularml/mojo/issues/231) - Unexpected error + when a Python expression raises an exception +- [Issue #119](https://github.com/modularml/mojo/issues/119) - The REPL fails + when a python variable is redefined + +### 2023-05-24 + +#### ⭐️ New + +- `finally` clauses are now supported on `try` statements. In addition, `try` + statements no longer require `except` clauses, allowing `try-finally` blocks. + `finally` clauses contain code that is always executed from control-flow + leaves any of the other clauses of a `try` statement by any means. + +#### 🦋 Changed + +- `with` statement emission changed to use the new `finally` logic so that + + ```mojo + with ContextMgr(): + return + ``` + + Will correctly execute `ContextMgr.__exit__` before returning. + +#### 🛠️ Fixed + +- [Issue #204](https://github.com/modularml/mojo/issues/204) - Mojo REPL + crash when returning a String at compile-time +- [Issue #143](https://github.com/modularml/mojo/issues/143) - synthesized + init in `@register_passable` type doesn't get correct convention. +- [Issue #201](https://github.com/modularml/mojo/issues/201) - String literal + concatenation is too eager. +- [Issue #209](https://github.com/modularml/mojo/issues/209) - [QoI] Terrible + error message trying to convert a type to itself. +- [Issue #32](https://github.com/modularml/mojo/issues/32) - Include struct + fields in docgen +- [Issue #50](https://github.com/modularml/mojo/issues/50) - Int to string + conversion crashes due to buffer overflow +- [Issue #132](https://github.com/modularml/mojo/issues/132) - PythonObject + `to_int` method has a misleading name +- [Issue #189](https://github.com/modularml/mojo/issues/189) - PythonObject bool + conversion is incorrect +- [Issue #65](https://github.com/modularml/mojo/issues/65) - Add SIMD + constructor from Bool +- [Issue #153](https://github.com/modularml/mojo/issues/153) - Meaning of + `Time.now` function result is unclear +- [Issue #165](https://github.com/modularml/mojo/issues/165) - Type in + `Pointer.free` documentation +- [Issue #210](https://github.com/modularml/mojo/issues/210) - Parameter results + cannot be declared outside top-level in function +- [Issue #214](https://github.com/modularml/mojo/issues/214) - Pointer offset + calculations at compile-time are incorrect +- [Issue #115](https://github.com/modularml/mojo/issues/115) - Float printing + does not include the right number of digits +- [Issue #202](https://github.com/modularml/mojo/issues/202) - + `kgen.unreachable` inside nested functions is illegal +- [Issue #235](https://github.com/modularml/mojo/issues/235) - Crash when + register passable struct field is not register passable +- [Issue #237](https://github.com/modularml/mojo/issues/237) - Parameter + closure sharp edges are not documented + +### 2023-05-16 + +#### ⭐️ New + +- Added missing dunder methods to `PythonObject`, enabling the use of common + arithmetic and logical operators on imported Python values. + +- `PythonObject` is now printable from Mojo, instead of requiring you to import + Python's print function. + +#### 🛠️ Fixed + +- [Issue #98](https://github.com/modularml/mojo/issues/98): + Incorrect error with lifetime tracking in loop. + +- [Issue #49](https://github.com/modularml/mojo/issues/49): Type inference + issue (?) in 'ternary assignment' operation (FloatLiteral vs. 'SIMD[f32, 1]'). + +- [Issue #48](https://github.com/modularml/mojo/issues/48): + and/or don't work with memory-only types. + +- [Issue #11](https://github.com/modularml/mojo/issues/11): `setitem` Support + for `PythonObject`. + +### 2023-05-11 + +#### ⭐️ New + +- `NDBuffer` and `Buffer` are now constructable via `Pointer` and + `DTypePointer`. + +- `String` now supports indexing with either integers or slices. + +- Added factorial function to the `Math` module. + +#### 🦋 Changed + +- The "byref" syntax with the `&` sigil has changed to use an `inout` + keyword to be more similar to the `borrowed` and `owned` syntax in arguments. + Please see [Issue #7](https://github.com/modularml/mojo/issues/7) for more + information. + +- Optimized the Matrix multiplication implementation in the notebook. + Initially we were optimizing for expandability rather than performance. We + have found a way to get the best of both worlds and now the performance of the + optimized Matmul implementation is 3x faster. + +- Renamed the [`^` postfix +operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) +from "consume" to "transfer." + +#### 🛠️ Fixed + +- Fixed missing overloads for `Testing.assertEqual` so that they work on +`Integer` and `String` values. + +- [Issue #6](https://github.com/modularml/mojo/issues/6): +Playground stops evaluating cells when a simple generic is defined. + +- [Issue #18](https://github.com/modularml/mojo/issues/18): +Memory leak in Python interoperability was removed. + +### 2023-05-02 + +#### 📢 Released + +- Mojo publicly launched! This was epic, with lots of great coverage online +including a [wonderful post by Jeremy +Howard](https://www.fast.ai/posts/2023-05-03-mojo-launch.html). The team is +busy this week. + +#### ⭐️ New + +- Added a Base64 encoding function to perform base64 encoding on strings. + +#### 🦋 Changed + +- Decreased memory usage of serialization of integers to strings. + +- Speedup the sort function. + +#### 🛠️ Fixed + +- Fixed time unit in the `sleep` function. + +## April 2023 + +### Week of 2023-04-24 + +- 📢 The default behavior of nested functions has been changed. Mojo nested + functions that capture are by default are non-parametric, runtime closures, + meaning that: + + ```mojo + def foo(x): + # This: + def bar(y): return x * y + # Is the same as: + let bar = lambda y: x * y + ``` + + These closures cannot have input or result parameters, because they are always + materialized as runtime values. Values captured in the closure (`x` in the + above example), are captured by copy: values with copy constructors cannot be + copied and captures are immutable in the closure. + + Nested functions that don't capture anything are by default "parametric" + closures: they can have parameters and they can be used as parameter values. + To restore the previous behavior for capturing closures, "parametric, + capture-by-unsafe-reference closures", tag the nested function with the + `@parameter` decorator. + +- 📢 Mojo now has full support for "runtime" closures: nested functions that + capture state materialized as runtime values. This includes taking the address + of functions, indirect calls, and passing closures around through function + arguments. Note that capture-by-reference is still unsafe! + + You can also take references to member functions with instances of that class + using `foo.member_function`, which creates a closure with `foo` bound to the + `self` argument. + +- 📢 Mojo now supports Python style `with` statements and context managers. + + These things are very helpful for implementing things like our + trace region support and things like Runtime support. + + A context manager in Mojo implements three methods: + + ```mojo + fn __enter__(self) -> T: + fn __exit__(self): + fn __exit__(self, err: Error) -> Bool: + ``` + + The first is invoked when the context is entered, and returns a + value that may optionally be bound to a target for use in the with + body. If the with block exits normally, the second method is + invoked to clean it up. If an error is raised, the third method + is invoked with the Error value. If that method returns true, the + error is considered handled, if it returns false, the error is + re-thrown so propagation continues out of the 'with' block. + +- 📢 Mojo functions now support variable scopes! Explicit `var` and `let` + declarations inside functions can shadow declarations from higher "scopes", + where a scope is defined as any new indentation block. In addition, the + `for` loop iteration variable is now scoped to the loop body, so it is + finally possible to write + + ```mojo + for i in range(1): pass + for i in range(2): pass + ``` + +- 📢 Mojo now supports an `@value` decorator on structs to reduce boilerplate + and encourage best practices in value semantics. The `@value` decorator looks + to see the struct has a memberwise initializer (which has arguments for each + field of the struct), a `__copyinit__` method, and a `__moveinit__` method, + and synthesizes the missing ones if possible. For example, if you write: + + ```mojo + @value + struct MyPet: + var name: String + var age: Int + ``` + + The `@value` decorator will synthesize the following members for you: + + ```mojo + fn __init__(inout self, owned name: String, age: Int): + self.name = name^ + self.age = age + fn __copyinit__(inout self, existing: Self): + self.name = existing.name + self.age = existing.age + fn __moveinit__(inout self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age + ``` + + This decorator can greatly reduce the boilerplate needed to define common + aggregates, and gives you best practices in ownership management + automatically. The `@value` decorator can be used with types that need custom + copy constructors (your definition wins). We can explore having the decorator + take arguments to further customize its behavior in the future. + +- 📚 Memcpy and memcmp now consistently use count as the byte count. + +- 📚 Add a variadic sting join on strings. + +- 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all + elements in a SIMD vector. + +- 📚 Optimize the `pow` function if the exponent is integral. + +- 📚 Add a `len` function which dispatches to `__len__` across the different + structs that support it. + +### Week of 2023-04-17 + +- 📢 Error messages have been significantly improved, thanks to prettier + printing for Mojo types in diagnostics. + +- 📢 Variadic values can now be indexed directly without wrapping them in a + `VariadicList`! + +- 📢 `let` declarations in a function can now be lazily initialized, and `var` + declarations that are never mutated get a warning suggesting they be converted + to a `let` declaration. Lazy initialization allows more flexible patterns of + initialization than requiring the initializer be inline, e.g.: + + ```mojo + let x: Int + if cond: + x = foo() + else: + x = bar() + use(x) + ``` + +- 📢 Functions defined with `def` now return `object` by default, instead of + `None`. This means you can return values (convertible to `object`) inside + `def` functions without specifying a return type. + +- 📢 The `@raises` decorator has been removed. Raising `fn` should be declared + by specifying `raises` after the function argument list. The rationale is that + `raises` is part of the type system, instead of a function modifier. + +- 📢 The `BoolLiteral` type has been removed. Mojo now emits `True` and `False` + directly as `Bool`. + +- 📢 Syntax for function types has been added. You can now write function types + with `fn(Int) -> String` or `async def(&String, *Int) -> None`. No more + writing `!kgen.signature` types by hand! + +- 📢 Float literals are not emitted as `FloatLiteral` instead of an MLIR `f64` + type! + +- 📢 Automatic destructors are now supported by Mojo types, currently spelled + `fn __del___(owned self):` (the extra underscore will be dropped shortly). + These destructors work like Python object destructors and similar to C++ + destructors, with the major difference being that they run "as soon as + possible" after the last use of a value. This means they are not suitable + for use in C++-style RAII patterns (use the `with` statement for that, which + is currently unsupported). + + These should be generally reliable for both memory-only and register-passable + types, with the caveat that closures are known to _not_ capture values + correctly. Be very careful with interesting types in the vicinity of a + closure! + +- A new (extremely dangerous!) builtin function is available for low-level + ownership muckery. The `__get_address_as_owned_value(x)` builtin takes a + low-level address value (of `!kgen.pointer` type) and returns an `owned` value + for the memory that is pointed to. This value is assumed live at the + invocation of the builtin, but is "owned" so it needs to be consumed by the + caller, otherwise it will be automatically destroyed. This is an effective + way to do a "placement delete" on a pointer. + + ```mojo + # "Placement delete": destroy the initialized object begin pointed to. + _ = __get_address_as_owned_value(somePointer.value) + + # Result value can be consumed by anything that takes it as an 'owned' + # argument as well. + consume(__get_address_as_owned_value(somePointer.value)) + ``` + +- Another magic operator, named `__get_address_as_uninit_lvalue(x)` joins + the magic LValue operator family. This operator projects a pointer to + an LValue like `__get_address_as_lvalue(x)`. The difference is that + `__get_address_as_uninit_lvalue(x)` tells the compiler that the pointee is + uninitialized on entry and initialized on exit, which means that you can use + it as a "placement new" in C++ sense. `__get_address_as_lvalue(x)` tells the + compiler that the pointee is initialized already, so reassigning over it will + run the destructor. + + ```mojo + # "*Re*placement new": destroy the existing SomeHeavy value in the memory, + # then initialize a new value into the slot. + __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) + + # Ok to use an lvalue, convert to borrow etc. + use(__get_address_as_lvalue(somePointer.value)) + + # "Placement new": Initialize a new value into uninitialied memory. + __get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5) + + # Error, cannot read from uninitialized memory. + use(__get_address_as_uninit_lvalue(somePointer.value)) + ``` + + Note that `__get_address_as_lvalue` assumes that there is already a value at + the specified address, so the assignment above will run the `SomeHeavy` + destructor (if any) before reassigning over the value. + +- 📢 Implement full support for `__moveinit__` (aka move constructors) + + This implements the ability for memory-only types to define two different + types of move ctors if they'd like: + + 1. `fn __moveinit__(inout self, owned existing: Self)`: Traditional Rust + style moving constructors that shuffles data around while taking + ownership of the source binding. + 2. `fn __moveinit__(inout self, inout existing: Self):`: C++ style "stealing" + move constructors that can be used to take from an arbitrary LValue. + + This gives us great expressive capability (better than Rust/C++/Swift) + and composes naturally into our lifetime tracking and value + categorization system. + +- The `__call__` method of a callable type has been relaxed to take `self` by + borrow, allow non-copyable callees to be called. + +- Implicit conversions are now invoked in `raise` statements properly, allowing + converting strings to `Error` type. + +- Automatic destructors are turned on for `__del__` instead of `__del___`. + +- 📚 Add the builtin FloatLiteral type. + +- 📚 Add integral `floordiv` and `mod` for the SIMD type that handle negative + values. + +- 📚 Add an F64 to String converter. + +- 📚 Make the `print` function take variadic inputs. + +### Week of 2023-04-10 + +- 📢 Introduce consume operator `x^` + + This introduces the postfix consume operator, which produces an RValue given + a lifetime tracked object (and, someday, a movable LValue). + +- Mojo now automatically synthesizes empty destructor methods for certain types + when needed. + +- The `object` type has been built out into a fully-dynamic type, with dynamic + function dispatch, with full error handling support. + + ```mojo + def foo(a) -> object: + return (a + 3.45) < [1, 2, 3] # raises a TypeError + ``` + +- 📢 The `@always_inline` decorator is no longer required for passing capturing + closures as parameters, for both the functions themselves as functions with + capturing closures in their parameters. These functions are still inlined but + it is an implementation detail of capturing parameter closures. Mojo now + distinguishes between capturing and non-capturing closures. Nested functions + are capturing by default and can be made non-capturing with the + `@noncapturing` decorator. A top-level function can be passed as a capturing + closure by marking it with the `@closure` decorator. + +- 📢 Support for list literals has been added. List literals `[1, 2, 3]` + generate a variadic heterogeneous list type. + +- Variadics have been extended to work with memory-primary types. + +- Slice syntax is now fully-supported with a new builtin `slice` object, added + to the compiler builtins. Slice indexing with `a[1:2:3]` now emits calls to + `__setitem__` and `__getitem__` with a slice object. + +- Call syntax has been wired up to `__call__`. You can now `f()` on custom + types! + +- Closures are now explicitly typed as capturing or non-capturing. If a + function intends to accept a capturing closure, it must specify the + `capturing` function effect. + +- 📚 Add a `Tile2D` function to enable generic `2D` tiling optimizations. + +- 📚 Add the `slice` struct to enable getting/setting spans of elements via + `getitem`/`setitem`. + +- 📚 Add syntax sugar to autotuning for both specifying the autotuned values, + searching, and declaring the evaluation function. + +### Week of 2023-04-03 + +- The `AnyType` and `NoneType` aliases were added and auto-imported in all + files. + +- 📢 The Mojo VS Code extension has been improved with docstring validation. It + will now warn when a function's docstring has a wrong argument name, for + example. + +- 📢 A new built-in literal type `TupleLiteral` was added in `_CompilerBuiltin`. + It represents literal tuple values such as `(1, 2.0)` or `()`. + +- 📢 The `Int` type has been moved to a new `Builtin` module and is + auto-imported in all code. The type of integer literals has been changed from + the MLIR `index` type to the `Int` type. + +- Mojo now has a powerful flow-sensitive uninitialized variable checker. This + means that you need to initialize values before using them, even if you + overwrite all subcomponents. This enables the compiler to reason about the + true lifetime of values, which is an important stepping stone to getting + automatic value destruction in place. + +- 📢 Call syntax support has been added. Now you can directly call an object + that implements the `__call__` method, like `foo(5)`. + +- 📢 The name for copy constructors got renamed from `__copy__` to + `__copyinit__`. Furthermore, non-`@register_passable` types now implement + it like they do an init method where you fill in a by-reference self, for + example: + + ```mojo + fn __copyinit__(inout self, existing: Self): + self.first = existing.first + self.second = existing.second + ``` + + This makes copy construction work more similarly to initialization, and + still keeps copies `x = y` distinct from initialization `x = T(y)`. + +- 📢 Initializers for memory-primary types are now required to be in the form + `__init__(inout self, ...):` with a None result type, but for register primary + types, it remains in the form `__init__(...) -> Self:`. The `T{}` initializer + syntax has been removed for memory-primary types. + +- Mojo String literals now emit a builtin `StringLiteral` type! One less MLIR + type to worry about. + +- New `__getattr__` and `__setattr__` dunder methods were added. Mojo calls + these methods on a type when attempting member lookup of a non-static member. + This allows writing dynamic objects like `x.foo()` where `foo` is not a member + of `x`. + +- Early destructor support has been added. Types can now define a special + destructor method `__del___` (note three underscores). This is an early + feature and it is still being built out. There are many caveats, bugs, + and missing pieces. Stay tuned! + +- 📚 Integer division and mod have been corrected for rounding in the presence + of negative numbers. + +- 📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to + `SIMD[1, type]`. + +## March 2023 + +### Week of 2023-03-27 + +- 📢 Parameter names are no longer load-bearing in function signatures. This + gives more flexibility in defining higher-order functions, because the + functions passed as parameters do not need their parameter names to match. + + ```mojo + # Define a higher-order function... + fn generator[ + func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] + ](): + pass + + # Int parameter is named "foo". + fn f0[foo: Int](): + pass + + # Int parameter is named "bar". + fn f1[bar: Int](): + pass + + fn main(): + # Both can be used as `func`! + generator[f0]() + generator[f1]() + ``` + + Stay tuned for improved function type syntax... + +- 📢 Two magic operators, named `__get_lvalue_as_address(x)` and + `__get_address_as_lvalue` convert stored LValues to and from `!kgen.pointer` + types (respectively). This is most useful when using the `Pointer[T]` + library type. The `Pointer.address_of(lvalue)` method uses the first one + internally. The second one must currently be used explicitly, and can be + used to project a pointer to a reference that you can pass around and use + as a self value, for example: + + ```mojo + # "Replacement new" SomeHeavy value into the memory pointed to by a + # Pointer[SomeHeavy]. + __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) + ``` + + Note that `__get_address_as_lvalue` assumes that there is already a value at + the specified address, so the assignment above will run the `SomeHeavy` + destructor (if any) before reassigning over the value. + +- The `(((x)))` syntax is __mlir_op has been removed in favor of + `__get_lvalue_as_address` which solves the same problem and is more general. + +- 📢 When using a mutable `self` argument to a struct `__init__` method, it + now must be declared with `&`, like any other mutable method. This clarifies + the mutation model by making `__init__` consistent with other mutating + methods. + +- 📚 Add variadic string join function. + +- 📚 Default initialize values with 0 or null if possible. + +- 📚 Add compressed, aligned, and mask store intrinsics. + +### Week of 2023-03-20 + +- Initial `String` type is added to the standard library with some very basic + methods. + +- Add `DimList` to remove the need to use an MLIR list type throughout the + standard library. + +- 📢 The `__clone__` method for copying a value is now named `__copy__` to + better follow Python term of art. + +- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, + instead of taking it by reference. This makes it easier to write, works for + `@register_passable` types, and exposes more optimization opportunities to + the early optimizer and dataflow analysis passes. + + ```mojo + # Before: + fn __clone__(inout self) -> Self: ... + + # After: + fn __copy__(self) -> Self: ... + ``` + +- 📢 A new `@register_passable("trivial")` may be applied to structs that + have no need for a custom `__copy__` or `__del__` method, and whose state is + only made up of `@register_passable("trivial")` types. This eliminates the + need to define `__copy__` boilerplate and reduces the amount of IR generated + by the compiler for trivial types like `Int`. + +- You can now write back to attributes of structs that are produced by a + computed lvalue expression. For example `a[i].x = ..` works when `a[i]` + is produced with a `__getitem__`/`__setitem__` call. This is implemented by + performing a read of `a[i]`, updating the temporary, then doing a writeback. + +- The remaining hurdles to using non-parametric, `@register_passable` types as + parameter values have been cleared. Types like `Int` should enjoy full use as + parameter values. + +- Parameter pack inference has been added to function calls. Calls to functions + with parameter packs can now elide the pack types: + + ```mojo + fn foo[*Ts: AnyType](*args: *Ts): pass + + foo(1, 1.2, True, "hello") + ``` + + Note that the syntax for parameter packs has been changed as well. + +- 📚 Add the runtime string type. + +- 📚 Introduce the DimList struct to remove the need to use low-level MLIR + operations. + +### Week of 2023-03-13 + +- 📢 Initializers for structs now use `__init__` instead of `__new__`, + following standard practice in Python. You can write them in one of two + styles, either traditional where you mutate self: + + ```mojo + fn __init__(self, x: Int): + self.x = x + ``` + + or as a function that returns an instance: + + ```mojo + fn __init__(x: Int) -> Self: + return Self {x: x} + ``` + + Note that `@register_passable` types must use the later style. + +- 📢 The default argument convention is now the `borrowed` convention. A + "borrowed" argument is passed like a C++ `const&` so it doesn't need to + invoke the copy constructor (aka the `__clone__` method) when passing a value + to the function. There are two differences from C++ `const&`: + + 1. A future borrow checker will make sure there are no mutable + aliases with an immutable borrow. + 2. `@register_passable` values are passed directly in an SSA register (and + thus, usually in a machine register) instead of using an extra reference + wrapper. This is more efficient and is the 'right default' for + `@register_passable` values like integers and pointers. + + This also paves the way to remove the reference requirement from `__clone__` + method arguments, which will allow us to fill in more support for them. + +- Support for variadic pack arguments has been added to Mojo. You can now + write heterogeneous variadic packs like: + + ```mojo + fn foo[*Ts: AnyType](args*: Ts): pass + + foo[Int, F32, String, Bool](1, 1.5, "hello", True) + ``` + +- The `owned` argument convention has been added. This argument convention + indicates that the function takes ownership of the argument and is responsible + for managing its lifetime. + +- The `borrowed` argument convention has been added. This convention signifies + the callee gets an immutable shared reference to a value in the caller's + context. + +- 📚 Add the `getenv` function to the `OS` module to enable getting environment + variables. + +- 📚 Enable the use of dynamic strides in `NDBuffer`. + +### Week of 2023-03-06 + +- 📢 Support added for using capturing async functions as parameters. + +- 📢 Returning result parameters has been moved from `return` statements to a + new `param_return` statement. This allows returning result parameters from + throwing functions: + + ```mojo + @raises + fn foo[() -> out: Int](): + param_return[42] + raise Error() + ``` + + And returning different parameters along `@parameter if` branches: + + ```mojo + fn bar[in: Bool -> out: Int](): + @parameter + if in: + param_return[1] + else: + param_return[2] + ``` + +- 📢 Mojo now supports omitting returns at the end of functions when they would + not reachable. For instance, + + ```mojo + fn foo(cond: Bool) -> Int: + if cond: + return 0 + else: + return 1 + + fn bar() -> Int: + while True: + pass + ``` + +- String literals now support concatenation, so `"hello " "world"` is treated + the same as `"hello world"`. + +- Empty bodies on functions, structs, and control flow statements are no longer + allowed. Please use `pass` in them to explicitly mark that they are empty, + just like in Python. + +- 📢 Structs in Mojo now default to living in memory instead of being passed + around in registers. This is the right default for generality (large + structures, structures whose pointer identity matters, etc) and is a key + technology that enables the borrow model. For simple types like `Int` and + `SIMD`, they can be marked as `@register_passable`. + + Note that memory-only types currently have some limitations: they cannot be + used in generic algorithms that take and return a `!mlirtype` argument, and + they cannot be used in parameter expressions. Because of this, a lot of + types have to be marked `@register_passable` just to work around the + limitations. We expect to enable these use-cases over time. + +- 📢 Mojo now supports computed lvalues, which means you can finally assign to + subscript expressions instead of having to call `__setitem__` explicitly. + + Some details on this: Mojo allows you to define multiple `__setitem__` + overloads, but will pick the one that matches your `__getitem__` type if + present. It allows you to pass computed lvalues into inout arguments by + introducing a temporary copy of the value in question. + +- Mojo now has much better support for using register-primary struct types in + parameter expressions and as the types of parameter values. This will allow + migration of many standard library types away from using bare MLIR types like + `__mlir_type.index` and towards using `Int`. This moves us towards getting rid + of MLIR types everywhere and makes struct types first-class citizens in the + parameter system. + +- 📚 Add a `sort` function. + +- 📚 Add non-temporal store to enable cache bypass. + +## February 2023 + +### Week of 2023-02-27 + +- 📢 The `@interface`, `@implements`, and `@evaluator` trio of decorators have + been removed, replaced by the `@parameter if` and `@adaptive` features. + +- 📢 Parameter inference can now infer the type of variadic lists. + +- 📢 Memory primary types are now supported in function results. A result slot + is allocated in the caller, and the callee writes the result of the function + into that slow. This is more efficient for large types that don't fit into + registers neatly! And initializers for memory-primary types now initialize + the value in-place, instead of emitting a copy! + +- Support for `let` decls of memory primary types has been implemented. These + are constant, ready-only values of memory primary types but which are + allocated on the function stack. + +- Overload conversion resolution and parameter inference has been improved: + + 1. Inference now works with `let` decls in some scenarios that weren't + working before. + 2. Parameter bindings can now infer types into parameter expressions. This + helps resolve higher-order functions in parameter expressions. + +- 📚 Optimize floor, ceil, and ldexp on X86 hardware. + +- 📚 Implement the log math function. + +### Week of 2023-02-20 + +- 📢 A new `@__memory_primary` struct decorator has been introduced. Memory + primary types must always have an address. For instance, they are always + stack-allocated when declared in a function and their values are passed into + function calls by address instead of copy. This is in contract with register + primary types that may not have an address, and which are passed by value + in function calls. Memory-primary fields are not allowed inside + register-primary structs, because struct elements are stored in-line. + +- 📢 A new `_CompilerBuiltin` module was added. This module defines core types + and functions of the language that are referenced by the parser, and hence, is + auto-imported by all other modules. For example new types for literal values + like the boolean True/False will be included in `_CompilerBuiltin`. + +- 📢 A special `__adaptive_set` property can be accessed on a function reference + marked as `@adaptive`. The property returns the adaptive overload set of that + function. The return type is a `!kgen.variadic`. This feature is useful to + implement a generic `evaluate` function in the standard library. + +- 📢 A new built-in literal type `BoolLiteral` was added in `_CompilerBuiltin`. + It represents the literal boolean values `True` and `False`. This is the first + Mojo literal to be emitted as a standard library type! + +- 📚 Add the prefetch intrinsic to enable HW prefetching a cache line. + +- 📚 Add the InlinedFixedVector, which is optimized for small vectors and stores + values on both the stack and the heap. + +### Week of 2023-02-13 + +- Unqualified lookups of struct members apply contextual parameters. This means + for instance that you can refer to static methods without binding the + struct parameters. + + ```mojo + struct Foo[x: Int]: + @staticmethod + bar(): pass + + foo(self): + bar() # implicitly binds to Foo[x].bar() + Foo[2].bar() # explicitly bind to another parameter + ``` + +- 📢 A new `Self` type refers to the enclosing type with all parameters bound + to their current values. This is useful when working with complex parametric + types, e.g.: + + ```mojo + struct MyArray[size: Int, element_type: type]: + fn __new__() -> Self: + return Self {...} + ``` + + which is a lot nicer than having to say `MyArray[size, element_type]` over + and over again. + +- 📢 Mojo now supports an `@adaptive` decorator. This decorator will supersede + interfaces, and it represents an overloaded function that is allowed to + resolve to multiple valid candidates. In that case, the call is emitted as a + fork, resulting in multiple function candidates to search over. + + ```mojo + @adaptive + fn sort(arr: ArraySlice[Int]): + bubble_sort(arr) + + @adaptive + fn sort(arr: ArraySlice[Int]): + merge_sort(arr) + + fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]): + let arr = lhs + rhs + sort(arr) # this forks compilation, creating two instances + # of the surrounding function + ``` + +- 📢 Mojo now requires that types implement the `__clone__` special member in + order to copy them. This allows the safe definition of non-copyable types + like Atomic. Note that Mojo still doesn't implement destructors, and (due to + the absence of non-mutable references) it doesn't actually invoke the + `__clone__` member when copying a let value. As such, this forces to you as + a Mojo user to write maximal boilerplate without getting much value out of it. + + In the future, we will reduce the boilerplate with decorators, and we will + actually start using it. This will take some time to build out though. + +- 📢 A special `__mlir_region` statement was added to provide stronger + invariants around defining MLIR operation regions in Mojo. It similar syntax + to function declarations, except it there are no results and no input + conventions. + +- 📚 Implement the log math function. + +- 📚 Improve the DType struct to enable compile-time equality checks. + +- 📚 Add the Complex struct class. + +### Week of 2023-02-06 + +- 📢 The `if` statement now supports a `@parameter` decorator, which requires + its condition to be a parameter expression, but which only emits the 'True' + side of the condition to the binary, providing a "static if" functionality. + This should eliminate many uses of `@interface` that are just used to provide + different constraint on the implementations. + +- 📢 `fn main():` is now automatically exported and directly runnable by the + command-line `mojo` tool. This is a stop-gap solution to enable script-like + use cases until we have more of the language built out. + +- 🪦 The `@nodebug_inline` feature has been removed, please use + `@alwaysinline("nodebug")` for methods that must be inlined and that we don't + want to step into. + +- 📢 Python chained comparisons, ex. `a < b < c`, are now supported in Mojo. + +- 📢 Functions can now be defined with default argument values, such as + `def f(x: Int, y: Int = 5):`. The default argument value is used when callers + do not provide a value for that argument: `f(3)`, for example, uses the + default argument value of `y = 5`. + +- Unused coroutine results are now nicely diagnosed as "missing await" warnings. + +- 📚 Introduce a vectorized reduction operations to the SIMD type. + +## January 2023 + +### Week of 2023-01-30 + +- A basic Mojo language server has been added to the VS Code extension, which + parses your code as you write it, and provides warnings, errors, and fix-it + suggestions! + +- 💯 The Mojo standard library is now implicitly imported by default. + +- The coroutine lowering support was reworked and a new `Coroutine[T]` type was + implemented. Now, the result of a call to an async function MUST be wrapped in + a `Coroutine[T]`, or else memory will leak. In the future, when Mojo supports + destructors and library types as literal types, the results of async function + calls will automatically wrapped in a `Coroutine[T]`. But today, it must be + done manually. This type implements all the expected hooks, such as + `__await__`, and `get()` to retrieve the result. Typical usage: + + ```mojo + async fn add_three(a: Int, b: Int, c: Int) -> Int: + return a + b + c + + async fn call_it(): + let task: Coroutine[Int] = add_three(1, 2, 3) + print(await task) + ``` + +- ⭐️ We now diagnose unused expression values at statement context in `fn` + declarations (but not in `def`s). This catches bugs with unused values, e.g. + when you forget the parens to call a function. + +- 📢 An `@always_inline("nodebug")` function decorator can be used on functions + that need to be force inlined, but when they should not have debug info in + the result. This should be used on methods like `Int.__add__` which should + be treated as builtin. + +- 📢 The `@export` decorator now supports an explicit symbol name to export to, + for example: + + ```mojo + @export("baz") # exported as 'baz' + fn some_mojo_fn_name(): + ``` + +- 📢 🚧 Subscript syntax is now wired up to the `__getitem__` dunder method. + + This allows type authors to implement the `__getitem__` method to enable + values to be subscripted. This is an extended version of the Python semantics + (given we support overloading) that allows you to define N indices instead of + a single version that takes a tuple (also convenient because we don't have + tuples yet). + + Note that this has a very, very important limitation: subscripts are NOT + wired up to `__setitem__` yet. This means that you can read values with + `.. = v[i]` but you cannot store to them with `v[i] = ..`. For this, please + continue to call `__setitem__` directly. + +- 📢 Function calls support parameter inference. + + For calls to functions that have an insufficient number of parameters + specified at the callsite, we can now infer them from the argument list. We + do this by matching up the parallel type structure to infer what the + parameters must be. + + Note that this works left to right in the parameter list, applying explicitly + specified parameters before trying to infer new ones. This is similar to how + C++ does things, which means that you may want to reorder the list of + parameters with this in mind. For example, a `dyn_cast`-like function will be + more elegant when implemented as: + + `fn dyn_cast[DstType: type, SrcType: type](src: SrcType) -> DstType:` + + Than with the `SrcType`/`DstType` parameters flipped around. + +- 📚 Add the growable Dynamic vector struct. + +### Week of 2023-01-23 + +- Inplace operations like `+=`/`__iadd__` may now take `self` by-val if they + want to, instead of requiring it to be by-ref. +- ⭐️ Inplace operations are no longer allowed to return a non-None value. The + corresponding syntax is a statement, not an expression. + +- A new `TaskGroup` type was added to the standard library. This type can be + used to schedule multiple tasks on a multi-threaded workqueue to be executed + in parallel. An async function can `await` all the tasks at once with the + taskgroup. + +- 📢 We now support for loops! A type that defines an `__iter__` method that + returns a type that defines `__next__` and `__len__` methods is eligible to + be used in the statement `for el in X()`. Control flow exits the loop when + the length is zero. + + This means things like this now work: + + ```mojo + for item in range(start, end, step): + print(item) + ``` + +- Result parameters now have names. This is useful for referring to result + parameters in the return types of a function: + + ```mojo + fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]: + ``` + +- 📢 We now support homogeneous variadics in value argument lists, using the + standard Python `fn thing(*args: Int):` syntax! Variadics also have support + in parameter lists: + + ```mojo + fn variadic_params_and_args[*a: Int](*b: Int): + print(a[0]) + print(b[1]) + ``` + +- 📚 Add the range struct to enable `for ... range(...)` loops. + +- 📚 Introduce the unroll generator to allow one to unroll loops via a library + function. + +### Week of 2023-01-16 + +- 📢 Struct field references are now supported in parameter context, so you + can use `someInt.value` to get the underlying MLIR thing out of it. This + should allow using first-class types in parameters more widely. +- 📢 We now support "pretty" initialization syntax for structs, e.g.: + + ```mojo + struct Int: + var value: __mlir_type.index + fn __new__(value: __mlir_type.index) -> Int: + return Int {value: value} + ``` + + This eliminates the need to directly use the MLIR `lit.struct.create` op in + struct initializers. This syntax may change in the future when ownership + comes in, because we will be able to support the standard `__init__` model + then. +- 📢 It is now possible to attach regions to `__mlir_op` operations. This is + done with a hack that allows an optional `_region` attribute that lists + references to the region bodies (max 1 region right now due to lack of list + `[]` literal). +- Nested functions now parse, e.g.: + + ```mojo + fn foo(): + fn bar(): + pass + bar() + ``` + +- Python-style `async` functions should now work and the `await` expression + prefix is now supported. This provides the joy of async/await syntactic + sugar when working with asynchronous functions. This is still somewhat + dangerous to use because we don't have proper memory ownership support yet. + +- String literals are now supported. + +- Return processing is now handled by a dataflow pass inside the compiler, so + it is possible to return early out of if statements. + +- The parser now supports generating 'fixit' hints on diagnostics, and uses + them when a dictionary literal uses a colon instead of equal, e.g.: + + ```log + x.mojo:8:48: error: expected ':' in subscript slice, not '=' + return __mlir_op.`lit.struct.create`[value = 42]() + ^ + : + ``` + +- 📚 Add reduction methods which operate on buffers. + +- 📚 Add more math functions like sigmoid, sqrt, rsqrt, etc. + +- 📚 Add partial load / store which enable loads and stores that are predicated + on a condition. + +### Week of 2023-01-09 + +- The `/` and `*` markers in function signatures are now parsed and their + invariants are checked. We do not yet support keyword arguments yet though, + so they aren't very useful. +- Functions now support a new `@nodebug_inline` decorator. + (Historical note: this was later replaced with `@alwaysinline("nodebug")`). + + Many of the things at the bottom level of the Mojo stack are trivial + zero-abstraction wrappers around MLIR things, for example, the `+` + operator on Int or the `__bool__` method on Bool itself. These operators + need to be force inlined even at -O0, but they have some additional things + that we need to wrestle with: + + 1. In no case would a user actually want to step into the `__bool__` method on + Bool or the + method on Int. This would be terrible debugger QoI for + unless you're debugging Int itself. We need something like + `__always_inline__, __nodebug__` attributes that clang uses in headers + like xmmintrin.h. + + 2. Similarly, these "operators" should be treated by users as primitives: + they don't want to know about MLIR or internal implementation details of + Int. + + 3. These trivial zero abstraction things should be eliminated early in the + compiler pipeline so they don't slow down the compiler, bloating out the + call graph with trivial leaves. Such thing slows down the elaborator, + interferes with basic MLIR things like fold(), bloats out the IR, or + bloats out generated debug info. + + 4. In a parameter context, we want some of these things to get inlined so + they can be simplified by the attribute logic and play more nicely with + canonical types. This is just a nice to have thing those of us who have + to stare at generated IR. + + The solution to this is a new `@nodebug_inline` decorator. This decorator + causes the parser to force-inline the callee instead of generating a call to + it. While doing so, it gives the operations the location of the call itself + (that's the "nodebug" part) and strips out let decls that were part of the + internal implementation details. + + This is a super-power-user-feature intended for those building the standard + library itself, so it is intentionally limited in power and scope: It can + only be used on small functions, it doesn't support regions, by-ref, throws, + async, etc. + +- Separately, we now support an `@alwaysInline` decorator on functions. This + is a general decorator that works on any function, and indicates that the + function must be inlined. Unlike `@nodebug_inline`, this kind of inlining is + performed later in the compilation pipeline. + +- The `__include` hack has been removed now that we have proper import support. + +- `__mlir_op` can now get address of l-value: + + You can use magic `(((x)))` syntax in __mlir_op that forces the `x` + expression to be an lvalue, and yields its address. This provides an escape + hatch (isolated off in `__mlir_op` land) that allows unsafe access to lvalue + addresses. + +- We now support `__rlshift__` and `__rtruediv__`. + +- 📢 The parser now resolves scoped alias references. This allows us to support + things like `SomeType.someAlias`, forward substituting the value. This + unblocks use of aliases in types like `DType`. We'd like to eventually + preserve the reference in the AST, but this unblocks library development. + +- 📚 Add a `now` function and `Benchmark` struct to enable timing and + benchmarking. + +- 📚 Move more of the computation in NDBuffer from runtime to compile time if + possible (e.g. when the dimensions are known at compile time). + +### Week of 2023-01-02 + +- 📚 Added the `print` function which works on Integers and SIMD values. + +- The frontend now has a new diagnostic subsystem used by the `kgen` tool (but + not by `kgen-translate` for tests) that supports source ranges on + diagnostics. Before we'd emit an error like: + + ```log + x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' + callee(1.0+F32(2.0)) + ^ + x.lit:4:1: note: function declared here + fn callee(a: Int): + ^ + ``` + + now we produce: + + ```log + x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' + callee(1.0+F32(2.0)) + ^ ~~~~~~~~~~~~ + x.lit:4:1: note: function declared here + fn callee(a: Int): + ^ + ``` + +- 📢 Parameter results are now supported in a proper way. They are now forward + declared with an alias declaration and then bound in a call with an arrow, + e.g.: + + ```mojo + alias a: __mlir_type.index + alias b: __mlir_type.index + idx_result_params[xyz * 2 -> a, b]() + ``` + +- Various minor issues with implicit conversions are fixed. For instances, + implicit conversions are now supported in parameter binding contexts and + `alias` declarations with explicit types. +- Doc strings are allowed on functions and structs, but they are currently + discarded by the parser. + +- 📚 Add a `print` method!!! + +- 📚 Demonstrate a naive matmul in Mojo. + +- 📚 Initial work on functions that depend on types (e.g. FPUtils, nan, inf, + etc.) + +- 📚 Allow one to query hardware properties such as simd_width, os, etc. via + TargetInfo at compile time. + +## December 2022 + +### Week of 2022-12-26 + +- 📢 You can now call functions in a parameter context! Calling a function in + a parameter context will evaluate the function at compile time. The result + can then be used as parameter values. For example, + + ```mojo + fn fma(x: Int, y: Int, z: Int) -> Int: + return a + b * c + + fn parameter_call(): + alias nelts = fma(32, 2, 16) + var x: SIMD[f32, nelts] + ``` + +- You can now disable printing of types in an `__mlir_attr` substitution by + using unary `+` expression. + +- 📢 `let` declarations are now supported in functions. `let` declarations are + local run-time constant values, which are always rvalues. They complement + 'var' decls (which are mutable lvalues) and are the normal thing to use in + most cases. They also generate less IR and are always in SSA form when + initialized. + + We will want to extend this to support 'let' decls in structs at some point + and support lazy initialized 'let' declarations (using dataflow analysis) but + that isn't supported yet. + +- 📚 Add the NDBuffer struct. + +- Happy new year. + +### Week of 2022-12-19 + +- 📚 Start of the Standard library: + 1. Added Integer and SIMD structs to bootstrap the standard library. + 2. Added very basic buffer data structure. + +- We have basic support for parsing parameter results in function calls! Result + parameters are an important Mojo metaprogramming feature. They allow functions + to return compile-time constants. + + ```mojo + fn get_preferred_simdwidthof[() -> nelts: Int](): + return[2] + + fn vectorized_function(): + get_preferred_simdwidthof[() -> nelts]() + var x: SIMD[f32, nelts] + ``` + +- Types can now be used as parameters of `!kgen.mlirtype` in many more cases. + +- MLIR operations with zero results don't need to specify `_type: []` anymore. + +- We support parsing triple quoted strings, for writing docstrings for your + functions and structs! + +- A new `__mlir_type[a,b,c]` syntax is available for substituting into MLIR + types and attributes is available, and the old placeholder approach is + removed. This approach has a few advantages beyond what placeholders do: + + 1. It's simpler. + 2. It doesn't form the intermediate result with placeholders, which + gets rejected by MLIR's semantic analysis, e.g. the complex case + couldn't be expressed before. + 3. It provides a simple way to break long attrs/types across multiple + lines. + +- We now support an `@evaluator` decorator on functions for KGEN evaluators. + This enables specifying user-defined interface evaluators when performing + search during compilation. + +- 📢 `import` syntax is now supported! + + This handles packaging imported modules into file ops, enables effective + isolation from the other decls. "import" into the desired context is just + aliasing decls, with the proper symbols references handle automatically during + IR generation. As a starting point, this doesn't handle any notion of packages + (as those haven't been sketched out enough). + +- 📢 Reversed binary operators (like `__radd__`) are now looked up and used if + the forward version (like `__add__`) doesn't work for some reason. + +- 📢 Implicit conversions are now generally available, e.g. in assign + statements, variable initializers etc. There are probably a few more places + they should work, but we can start eliminating all the extraneous explicit + casts from literals now. + +- Happy Holidays + +### Week of 2022-12-12 + +- 📢 Function overloading now works. Call resolution filters candidate list + according to the actual parameter and value argument specified at the site of + the call, diagnosing an error if none of the candidates are viable or if + multiple are viable and ambiguous. We also consider implicit conversions in + overload look: + + ```mojo + fn foo(x: Int): pass + fn foo(x: F64): pass + + foo(Int(1)) # resolves to the first overload + foo(1.0) # resolves to the second overload + foo(1) # error: both candidates viable with 1 implicit conversion! + ``` + +- The short circuiting binary `and` and `or` expressions are now supported. + +- Unary operator processing is a lot more robust, now handling the `not` + expression and `~x` on Bool. + +- 📢 The compiler now generates debug information for use with GDB/LLDB that + describes variables and functions. + +- The first version of the Mojo Visual Studio Code extension has been released! + It supports syntax highlighting for Mojo files. + +- The first version of the `Bool` type has landed in the new Mojo standard + library! + +- 📢 Implicit conversions are now supported in return statements. + +### Week of 2022-12-05 + +- "Discard" patterns are now supported, e.g. `_ = foo()` + +- We now support implicit conversions in function call arguments, e.g. + converting an `index` value to `Int` automatically. This eliminates a bunch + of casts, e.g. the need to say F32(1.0) everywhere. + + This is limited for a few reasons that will be improved later: + 1. We don't support overloading, so lots of types aren't convertible + from all the things they should be, e.g. you can't pass "1" to + something that expects F32, because F32 can't be created from index. + 2. This doesn't "check to see if we can invoke `__new__`" it force applies + it on a mismatch, which leads to poor QoI. + 3. This doesn't fix things that need radd. + +## November 2022 + +### Week of 2022-11-28 + +- 📢 We support the `True` and `False` keywords as expressions. + +- 📢 A new `alias` declaration is supported which allows defining local + parameter values. This will eventually subsume type aliases and other + things as it gets built out. + +- 📢 We now have end-to-end execution of Mojo files using the `kgen` tool! + Functions exported with `@export` can be executed. + +- 📢 We have try-except-else and `raise` statements and implicit error + propagation! The error semantics are that `def` can raise by default, but `fn` + must explicitly declare raising with a `@raises` decorator. Stub out basic + `Error` type. + +- The `&` sigil for by-ref arguments is now specified after the identifier. + Postfix works better for ref and move operators on the expression + side because it chains an mentally associates correctly: + `thing.method().result^`. We don't do that yet, but align param + decl syntax to it so that things won't be odd looking when we do. + In practice this looks like: + + ```mojo + def mutate_argument(a&: index): + a = 25 + ``` + +### Week of 2022-11-21 + +- 📢 The magic `index` type is gone. Long live `__mlir_type.index`. + +- Implement parameter substitution into parametric `__mlir_type` decls. This + allows us to define parametric opaque MLIR types with exposed parameters using + a new "placeholder" attribute. This allows us to expose the power of the KGEN + type parametric system directly into Mojo. + +- 📢 Fully-parametric custom types can now be defined and work in Mojo, bringing + together a lot of the recent work. We can write the SIMD type directly as a + wrapper around the KGEN type, for example: + + ```mojo + struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]: + var value: + __mlir_type.`!pop.simd<#lit, + #lit>`[nelts, dt] + + fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]: + return __mlir_op.`pop.add`(self.value, rhs.value) + ``` + +### Week of 2022-11-14 + +- 📢 Implement a magic `__mlir_type` declaration that can be used to access any + MLIR type. E.g. `__mlir_type.f64`. + +- 📢 Add an `fn` declaration. These are like `def` declarations, but are more + strict in a few ways: they require type annotations on arguments, don't allow + implicit variable declarations in their body, and make their arguments rvalues + instead of lvalues. + +- Implemented Swift-style backtick identifiers, which are useful for code + migration where names may collide with new keywords. + +- 📢 A new `__include` directive has been added that performs source-level + textual includes. This is temporary until we have an `import` model. + +- Implement IR generation for arithmetic operators like `+` and `*` in terms + of the `__add__` and `__mul__` methods. + +- 📢 Added support for `break` and `continue` statements, as well as early + returns inside loops and conditionals! + +- 📢 Implemented augmented assignment operators, like `+=` and `@=`. + +- 📢 Mojo now has access to generating any MLIR operations (without regions) + with a new `__mlir_op` magic declaration. We can start to build out the + language's builtin types with this: + + ```mojo + struct Int: + var value: __mlir_type.index + + fn __add__(self, rhs: Int) -> Int: + return __mlir_op.`index.add`(self.value, rhs.value) + ``` + + Attributes can be attached to the declaration with subscript `[]` syntax, + and an explicit result type can be specified with a special `_type` attribute + if it cannot be inferred. Attributes can be accessed via the `__mlir_attr` + magic decl: + + ```mojo + __mlir_op.`index.cmp`[ + _type: __mlir_type.i1, + pred: __mlir_attr.`#index` + ](lhs, rhs) + ``` + +- Improved diagnostics emissions with ranges! Now errors highlight the whole + section of code and not just the first character. + +### Week of 2022-11-07 + +- Implemented the `@interface` and `@implements` decorators, which provide + access to KGEN generator interfaces. A function marked as an `@interface` + has no body, but it can be implemented by multiple other functions. + + ```mojo + @interface + def add(lhs: index, rhs: index): + + @implements(add) + def normal_add(lhs: index, rhs: index) -> index: + return lhs + rhs + + @implements(add) + def slow_add(lhs: index, rhs: index) -> index: + wait(1000) + return normal_add(lhs, rhs) + ``` + +- 📢 Support for static struct methods and initializer syntax has been added. + Initializing a struct with `Foo()` calls an implicitly static `__new__` + method. This method should be used instead of `__init__` inside structs. + + ```mojo + struct Foo: + var value: index + + def __new__() -> Foo: + var result: Foo + result.value = Foo.return_a_number() # static method! + return result + + @staticmethod + def return_a_number() -> index: + return 42 + ``` + +- 📢 Full by-ref argument support. It's now possible to define in-place + operators like `__iadd__` and functions like `swap(x, y)` correctly. + +- 📢 Implemented support for field extract from rvalues, like `x.value` where + `x` is not an lvalue (`var` declaration or by-ref function argument). + +## October 2022 + +### Week of 2022-10-31 + +- Revised `return` handling so that a return statement with no expression is + syntax sugar for `return None`. This enables early exits in functions that + implicitly return `None` to be cleaner: + + ```mojo + def just_return(): + return + ``` + +- Added support for parsing more expressions: if-else, bitwise operators, + shift operators, comparisons, floor division, remainder, and matmul. + +- 📢 The type of the `self` argument can now be omitted on member methods. + +### Week of 2022-10-24 + +- Added parser support for right-associativity and unary ops, like the power + operator `a ** b ** c` and negation operator `-a`. + +- Add support for `&expr` in Mojo, which allows denoting a by-ref argument in + functions. This is required because the `self` type of a struct method is + implicitly a pointer. + +- Implemented support for parametric function declarations, such as: + + ```mojo + struct SIMD[dt: DType, width: index]: + fn struct_method(self: &SIMD[dt, width]): + pass + + def fancy_add[dt: DType, width: index]( + lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index: + return width + ``` + +### Week of 2022-10-17 + +- Added explicit variable declarations with `var`, for declaring variables both + inside functions and structs, with support for type references. Added `index` + as a temporary built-in type. + + ```mojo + def foo(lhs: index, rhs: index) -> index: + var result: index = lhs + rhs + return result + ``` + +- Implemented support for parsing struct declarations and references to type + declarations in functions! In `def`, the type can be omitted to signal an + object type. + + ```mojo + struct Foo: + var member: index + + def bar(x: Foo, obj) -> index: + return x.member + ``` + +- Implemented parser support for `if` statements and `while` loops! + + ```mojo + def if_stmt(c: index, a: index, b: index) -> index: + var result: index = 0 + if c: + result = a + else: + result = b + return result + + def while_stmt(init: index): + while init > 1: + init = init - 1 + ``` + +- Significantly improved error emission and handling, allowing the parser to + emit multiple errors while parsing a file. + +### Week of 2022-10-10 + +- Added support for parsing integer, float, and string literals. + +- Implemented parser support for function input parameters and results. You can + now write parametric functions like, + + ```mojo + def foo[param: Int](arg: Int) -> Int: + result = param + arg + return result + ``` + +### Week of 2022-10-03 + +- Added some basic parser scaffolding and initial parser productions, including + trivial expressions and assignment parser productions. +- Implemented basic scope handling and function IR generation, with support for + forward declarations. Simple functions like, + + ```mojo + def foo(x: Int): + ``` + + Now parse! But all argument types are hard-coded to the MLIR `index` type. + +- Added IR emission for simple arithmetic expressions on builtin types, like + `x + y`. + +## September 2022 + +### Week of 2022-09-26 + +- Mojo's first patch to add a lexer was Sep 27, 2022. + +- Settled on `[]` for Mojo generics instead of `<>`. Square brackets are + consistent with Python generics and don't have the less than ambiguity + other languages have. diff --git a/docs/changelog.md b/docs/changelog.md index 2c8b1e6493..f0b0f8ad0b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,34 +1,6 @@ ---- -title: Mojo🔥 changelog -sidebar_label: Changelog -description: A history of significant Mojo changes. -toc_max_heading_level: 2 -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png ---- -This is a running list of significant changes for the Mojo language and tools. -It doesn't include all internal implementation changes. - -## Update Mojo - -If you don't have Mojo yet, see the [get started -guide](/mojo/manual/get-started/#get-the-mojo-sdk). - -To see your Mojo version, run this: - -```sh -mojo --version -``` - -To update Mojo, first [update `modular`](/cli/#description), and then run this: - -```sh -modular update mojo -``` +This is a running list of significant UNRELEASED changes for the Mojo language +and tools. Please add any significant user-visible changes here. [//]: # Here's the template to use when starting a new batch of notes: [//]: ## UNRELEASED @@ -335,4341 +307,3 @@ modular update mojo does not work when specifying `end=""` - [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods correctly handle edge cases where `size_out >= size`. - -## v24.1.1 (2024-03-18) - -This release includes installer improvements and enhanced error reporting for -installation issues. Otherwise it is functionally identical to Mojo 24.1. - -## v24.1 (2024-02-29) - -### 🔥 Legendary - -- Mojo is now bundled with [the MAX platform](/max)! - - As such, the Mojo package version now matches the MAX version, which follows - a `YY.MAJOR.MINOR` version scheme. Because this is our first release in 2024, - that makes this version `24.1`. - -- Mojo debugging support is here! The Mojo VS Code extension includes debugger - support. For details, see [Debugging](/mojo/tools/debugging) in the Mojo - Manual. - -### ⭐️ New - -- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our - collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` - checks, and requires member elements to conform to the `KeyElement` trait. - - ```mojo - from collections import Set - - var set = Set[Int](1, 2, 3) - print(len(set)) # 3 - set.add(4) - - for element in set: - print(element[]) - - set -= Set[Int](3, 4, 5) - print(set == Set[Int](1, 2)) # True - print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True - let element = set.pop() - print(len(set)) # 1 - ``` - -- Mojo now supports the `x in y` expression as syntax sugar for - `y.__contains__(x)` as well as `x not in y`. - -- Mojo now has support for keyword-only arguments and parameters. For example: - - ```mojo - fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2): - print(a * b * c * d) - - my_product(3, c=5) # prints '30' - my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c' - ``` - - This includes support for declaring signatures that use both variadic and - keyword-only arguments/parameters. For example, the following is now possible: - - ```mojo - fn prod_with_offset(*args: Int, offset: Int = 0) -> Int: - var res = 1 - for i in range(len(args)): - res *= args[i] - return res + offset - - print(prod_with_offset(2, 3, 4, 10)) # prints 240 - print(prod_with_offset(2, 3, 4, offset=10)) # prints 34 - ``` - - Note that variadic keyword-only arguments/parameters (for example, `**kwargs`) - are not supported yet. That is, the following is not allowed: - - ```mojo - fn variadic_kw_only(a: Int, **kwargs): ... - ``` - - For more information, see - [Positional-only and keyword-only arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments) - in the Mojo Manual. - -- The `print()` function now accepts a keyword-only argument for the `end` - which is useful for controlling whether a newline is printed or not - after printing the elements. By default, `end` defaults to "\n" as before. - -- The Mojo SDK can now be installed on AWS Graviton instances. - -- A new version of the [Mojo Playground](/mojo/playground) is available. The new - playground is a simple interactive editor for Mojo code, similar to the Rust - Playground or Go Playground. The old - [JupyterLab based playground](https://playground.modular.com) will remain - online until March 20th. - -- The Mojo LSP server will now generate fixits for populating empty - documentation strings: - - ```mojo - fn foo(arg: Int): - """""" # Unexpected empty documentation string - ``` - - Applying the fixit from above will generate: - - ```mojo - fn foo(arg: Int): - """[summary]. - - Args: - arg: [description]. - """ - ``` - -- Added new `*_` syntax that allows users to explicitly unbind any number of - positional parameters. For example: - - ```mojo - struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass - - alias all_unbound = StructWithDefault[*_] - # equivalent to - alias all_unbound = StructWithDefault[_, _, _, _] - - alias first_bound = StructWithDefault[5, *_] - # equivalent to - alias first_bound = StructWithDefault[5, _, _, _] - - alias last_bound = StructWithDefault[*_, 6] - # equivalent to - alias last_bound = StructWithDefault[_, _, _, 6] - - alias mid_unbound = StructWithDefault[3, *_, 4] - # equivalent to - alias mid_unbound = StructWithDefault[3, _, _, 4] - ``` - - As demonstrated above, this syntax can be used to explicitly unbind an - arbitrary number of parameters, at the beginning, at the end, or in the - middle of the operand list. Since these unbound parameters must be explicitly - specified at some point, default values for these parameters are not applied. - For example: - - ```mojo - alias last_bound = StructWithDefault[*_, 6] - # When using last_bound, you must specify a, b, and c. last_bound - # doesn't have a default value for `c`. - var s = last_bound[1, 2, 3]() - ``` - - For more information see the Mojo Manual sections on - [partially-bound types](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types) - and - [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). - -- [`DynamicVector`](/mojo/stdlib/collections/list#list) now - supports iteration. Iteration values are instances of - [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: - - ```mojo - var v: DynamicVector[String]() - v.append("Alice") - v.append("Bob") - v.append("Charlie") - for x in v: - x[] = str("Hello, ") + x[] - for x in v: - print(x[]) - ``` - -- `DynamicVector` now has - [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and - [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. - -- The `mojo package` command now produces compilation agnostic packages. - Compilation options such as O0, or --debug-level, are no longer needed or - accepted. As a result, packages are now smaller, and extremely portable. - -- Initializers for `@register_passable` values can (and should!) now be - specified with `inout self` arguments just like memory-only types: - - ```mojo - @register_passable - struct YourPair: - var a: Int - var b: Int - fn __init__(inout self): - self.a = 42 - self.b = 17 - fn __copyinit__(inout self, existing: Self): - self.a = existing.a - self.b = existing.b - ``` - - This form makes the language more consistent, more similar to Python, and - easier to implement advanced features for. There is also no performance - impact of using this new form: the compiler arranges to automatically return - the value in a register without requiring you to worry about it. - - The older `-> Self` syntax is still supported in this release, but will be - removed in a subsequent one, so please migrate your code. One thing to watch - out for: a given struct should use one style or the other, mixing some of - each won't work well. - -- The `inout self` initializer form is **required** for initializers of - `@register_passable` types that may raise errors: - - ```mojo - @register_passable - struct RaisingCtor: - fn __init__(inout self) raises: - raise - ``` - -- `async` functions that may raise errors have been temporarily disabled in this - build. The implementation of Mojo async is undergoing a rework 🚧. - -- The standard library `slice` type has been renamed to - [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` - function has been introduced. This makes Mojo closer to Python and makes the - `Slice` type follow the naming conventions of other types like `Int`. - -- "Slice" syntax in subscripts is no longer hard coded to the builtin `slice` - type: it now works with any type accepted by a container's `__getitem__()` - method. For example: - - ```mojo - @value - struct UnusualSlice: - var a: Int - var b: Float64 - var c: String - - struct YourContainer: - fn __getitem__(self, slice: UnusualSlice) -> T: ... - ``` - - Given this implementation, you can subscript into an instance of - `YourContainer` like `yc[42:3.14:"🔥"]` and the three values are passed to the - `UnusualSlice` constructor. - -- The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to - return an MLIR internal reference type. - -- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) - method, for moving a value from one pointer memory location to another. - -- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be - used to format any value whose type implements the - [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. - -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements - `__is__` and `__isnot__` so that you can use expressions of the form `x is y` - and `x is not y` with `PythonObject`. - -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the - `SizedRaising` trait. This means the built-in - [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. - -- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) - and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. - -- A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query - properties on paths. - -- The `os` package now has a - [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms - to the `PathLike` trait by implementing the `__fspath__()` function. - -- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to - query properties of the path. - -- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on - [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` - module to work on `PathLike` structs. For example, the following sample - lists all the directories in the `/tmp` directory: - - ```mojo - from pathlib import Path - - fn walktree(top: Path, inout files: DynamicVector[Path]): - try: - var ls = top.listdir() - for i in range(len(ls)): - var child = top / ls[i] - if child.is_dir(): - walktree(child, files) - elif child.is_file(): - files.append(child) - else: - print("Skipping '" + str(child) + "'") - except: - return - - fn main(): - var files = DynamicVector[Path]() - - walktree(Path("/tmp"), files) - - for i in range(len(files)): - print(files[i]) - ``` - -- The [`find()`](/mojo/stdlib/builtin/string_literal#find), - [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), - [`count()`](/mojo/stdlib/builtin/string_literal#count), and - [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods - now work on string literals. This means that you can write: - - ```mojo - if "Mojo" in "Hello Mojo": - ... - ``` - -- Breakpoints can now be inserted programmatically within the code using the - builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. - - Note: on Graviton instances, the debugger might not be able to resume after - hitting this kind of breakpoint. - -- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that - describes a type that can be represented as a boolean value. To conform to the - trait, a type must implement the `__bool__()` method. - -- Modules within packages can now use purely relative `from` imports: - - ```mojo - from . import another_module - ``` - -- Trivial types, like MLIR types and function types, can now be bound implicitly - to traits that require copy constructors or move constructors, such as - [`Movable`](/mojo/stdlib/builtin/value.html#movable), - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). - -- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory - value. We hope and expect that this will eventually be replaced by - `Reference(expr).lifetime` as the parameter system evolves, but this is - important in the meantime for use in function signatures. - -- A new magic `__type_of(expr)` call will yield the type of a value. This allows - one to refer to types of other variables. For example: - - ```mojo - fn my_function(x: Int, y: __type_of(x)) -> Int: - let z: __type_of(x) = y - return z - ``` - -### 🦋 Changed - -- As another step towards [removing let - declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md) - we have removed support for let declarations inside the compiler. To ease - migration, we parse `let` declarations as a `var` declaration so your code - won't break. We emit a warning about this, but please switch your code to - using `var` explicitly, because this migration support will be removed in a - subsequent update. - - ```mojo - fn test(): - # treated as a var, but please update your code! - let x = 42 # warning: 'let' is being removed, please use 'var' instead - x = 9 - ``` - -- It is no longer possible to explicitly specify implicit argument parameters in - [automatically parameterized - functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). - This ability was an oversight and this is now an error: - - ```mojo - fn autoparameterized(x: SIMD): - pass - - autoparameterized[DType.int32, 1](3) # error: too many parameters - ``` - -- `vectorize_unroll` has been removed, and - [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter - named `unroll_factor` with a default value of 1. Increasing `unroll_factor` - may improve performance at the cost of binary size. See the - [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) - for more details. - -- The `vectorize` signatures have changed with the closure `func` moved to the - first parameter: - - ```mojo - vectorize[func, width, unroll_factor = 1](size) - vectorize[func, width, size, unroll_factor = 1]() - ``` - - The doc string has been updated with examples demonstrating the difference - between the two signatures. - -- The `unroll` signatures have changed with the closure `func` moved to the - first parameter: - - ```mojo - unroll[func, unroll_count]() - ``` - -- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) and - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types have changed. Now, both - take the type as the first parameter and no longer require the shape - parameter. This allows you to use these types and have sensible defaults. - For example: - - ```mojo - NDBuffer[DType.float32, 3] - ``` - - is equivalent to - - ```mojo - NDBuffer[DType.float32, 3, DimList.create_unknown[3]()] - ``` - - Users can still specify the static shape (if known) to the type: - - ```mojo - NDBuffer[DType.float32, 3, DimList(128, 128, 3)] - ``` - -- The error message for missing function arguments is improved: instead of - describing the number of arguments (e.g. `callee expects at least 3 arguments, - but 1 was specified`) the missing arguments are now described by - name (e.g. `missing 2 required positional arguments: 'b', 'c'`). - -- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait - is now a built-in trait and has been removed from `collections.vector`. - -- The `DynamicVector(capacity: Int)` constructor has been changed to take - `capacity` as a keyword-only argument to prevent implicit conversion from - `Int`. - -- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a - copy. - -- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` - and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - -- The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. - We originally thought about using those as language sugar for references, but - we believe that generic language features combined with the - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good - experience without dedicated sugar. - -### 🛠️ Fixed - -- [#435](https://github.com/modularml/mojo/issues/435) - Structs with Self type don't always work. -- [#1540](https://github.com/modularml/mojo/issues/1540) - Crash in register_passable self referencing struct. -- [#1664](https://github.com/modularml/mojo/issues/1664) - Improve error - message when `StaticTuple` is constructed with a negative size for - the number of elements. -- [#1679](https://github.com/modularml/mojo/issues/1679) - crash on SIMD of zero - elements. -- Various crashes on invalid code: - [#1230](https://github.com/modularml/mojo/issues/1230), - [#1699](https://github.com/modularml/mojo/issues/1699), - [#1708](https://github.com/modularml/mojo/issues/1708) -- [#1223](https://github.com/modularml/mojo/issues/1223) - Crash when parametric - function is passed as (runtime) argument. The parser now errors out instead. -- [#1530](https://github.com/modularml/mojo/issues/1530) - Crash during - diagnostic emission for parameter deduction failure. -- [#1538](https://github.com/modularml/mojo/issues/1538) and [#1607]( - https://github.com/modularml/mojo/issues/1607) - Crash when returning type - value instead of instance of expected type. This is a common mistake and the - error now includes a hint to point users to the problem. -- [#1613](https://github.com/modularml/mojo/issues/1613) - Wrong type name in - error for incorrect `self` argument type in trait method declaration. -- [#1670](https://github.com/modularml/mojo/issues/1670) - Crash on implicit - conversion in a global variable declaration. -- [#1741](https://github.com/modularml/mojo/issues/1741) - Mojo documentation - generation doesn't show `inout`/`owned` on variadic arguments. -- [#1621](https://github.com/modularml/mojo/issues/1621) - VS Code does not - highlight `raises` and `capturing` in functional type expressions. -- [#1617](https://github.com/modularml/mojo/issues/1617) - VS Code does not - highlight `fn` in specific contexts. -- [#1740](https://github.com/modularml/mojo/issues/1740) - LSP shows unrelated - info when hovering over a struct. -- [#1238](https://github.com/modularml/mojo/issues/1238) - File shadows Mojo - package path. -- [#1429](https://github.com/modularml/mojo/issues/1429) - Crash when using - nested import statement. -- [#1322](https://github.com/modularml/mojo/issues/1322) - Crash when missing - types in variadic argument. -- [#1314](https://github.com/modularml/mojo/issues/1314) - Typecheck error when - binding alias to parametric function with default argument. -- [#1248](https://github.com/modularml/mojo/issues/1248) - Crash when importing - from file the same name as another file in the search path. -- [#1354](https://github.com/modularml/mojo/issues/1354) - Crash when importing - from local package. -- [#1488](https://github.com/modularml/mojo/issues/1488) - Crash when setting - generic element field. -- [#1476](https://github.com/modularml/mojo/issues/1476) - Crash in interpreter - when calling functions in parameter context. -- [#1537](https://github.com/modularml/mojo/issues/1537) - Crash when copying - parameter value. -- [#1546](https://github.com/modularml/mojo/issues/1546) - Modify nested vector - element crashes parser. -- [#1558](https://github.com/modularml/mojo/issues/1558) - Invalid import causes - parser to crash. -- [#1562](https://github.com/modularml/mojo/issues/1562) - Crash when calling - parametric type member function. -- [#1577](https://github.com/modularml/mojo/issues/1577) - Crash when using - unresolved package as a variable. -- [#1579](https://github.com/modularml/mojo/issues/1579) - Member access into - type instances causes a crash. -- [#1602](https://github.com/modularml/mojo/issues/1602) - Interpreter failure - when constructing strings at compile time. -- [#1696](https://github.com/modularml/mojo/issues/1696) - Fixed an issue that - caused syntax highlighting to occasionally fail. -- [#1549](https://github.com/modularml/mojo/issues/1549) - Fixed an issue when - the shift amount is out of range in `SIMD.shift_left` and `SIMD.shift_right`. - -## v0.7.0 (2024-01-25) - -### ⭐️ New - -- A new Mojo-native dictionary type, - [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. - `Dict` stores values that conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. Keys need to conform to the new - [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is - not yet implemented by other standard library types. In the short term, you - can create your own wrapper types to use as keys. For example, the following - sample defines a `StringKey` type and uses it to create a dictionary that maps - strings to `Int` values: - - ```mojo - from collections.dict import Dict, KeyElement - - @value - struct StringKey(KeyElement): - var s: String - - fn __init__(inout self, owned s: String): - self.s = s ^ - - fn __init__(inout self, s: StringLiteral): - self.s = String(s) - - fn __hash__(self) -> Int: - return hash(self.s) - - fn __eq__(self, other: Self) -> Bool: - return self.s == other.s - - fn main() raises: - var d = Dict[StringKey, Int]() - d["cats"] = 1 - d["dogs"] = 2 - print(len(d)) # prints 2 - print(d["cats"]) # prints 1 - print(d.pop("dogs")) # prints 2 - print(len(d)) # prints 1 - ``` - - We plan to add `KeyElement` conformance to standard library types in - subsequent releases. - -- Users can opt-in to assertions used in the standard library code by - specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to - compile your source file(s). In the case that an assertion is fired, - the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are _not enabled_ - in the standard library right now for performance reasons. - -- The Mojo Language Server now implements the References request. IDEs use - this to provide support for **Go to References** and **Find All References**. - A current limitation is that references outside of the current document are - not supported, which will be addressed in the future. - -- The [`sys.info`](/mojo/stdlib/sys/info) module now includes - `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` - functions. - -- Homogenous variadic arguments consisting of memory-only types, such as - `String` are more powerful and easier to use. These arguments are projected - into a - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). - - (Previous releases made it easier to use variadic lists of register-passable - types, like `Int`.) - - Subscripting into a `VariadicListMem` now returns the element instead of an - obscure internal type. In addition, we now support `inout` and `owned` - variadic arguments: - - ```mojo - fn make_worldly(inout *strs: String): - # This "just works" as you'd expect! - for i in range(len(strs)): - strs[i] += " world" - fn main(): - var s1: String = "hello" - var s2: String = "konnichiwa" - var s3: String = "bonjour" - make_worldly(s1, s2, s3) - print(s1) # hello world - print(s2) # konnichiwa world - print(s3) # bonjour world - ``` - - (Previous releases made it easier to use variadic lists, but subscripting into - a `VariadicListMem` returned a low-level pointer, which required the user to - call `__get_address_as_lvalue()` to access the element.) - - Note that subscripting the variadic list works nicely as above, but - iterating over the variadic list directly with a `for` loop produces a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead - of the desired value, so an extra subscript is required; We intend to fix this - in the future. - - ```mojo - fn make_worldly(inout *strs: String): - # Requires extra [] to dereference the reference for now. - for i in strs: - i[] += " world" - ``` - - Heterogenous variadic arguments have not yet been moved to the new model, but - will in future updates. - - Note that for variadic arguments of register-passable types like `Int`, the - variadic list contains values, not references, so the dereference operator - (`[]`) is not required. This code continues to work as it did previously: - - ```mojo - fn print_ints(*nums: Int): - for num in nums: - print(num) - print(len(nums)) - ``` - -- Mojo now has a prototype version of a safe - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's - lifetime tracking pass can reason about references to safely extend local - variable lifetime, and check indirect access safety. The `Reference` type - is brand new (and currently has no syntactic sugar) so it must be explicitly - dereferenced with an empty subscript: `ref[]` provides access to the - underlying value. - - ```mojo - fn main(): - var a: String = "hello" - var b: String = " references" - - var aref = Reference(a) - aref[] += b - print(a) # prints "hello references" - - aref[] += b - # ^last use of b, it is destroyed here. - - print(aref[]) # prints "hello references references" - # ^last use of a, it is destroyed here. - ``` - - While the `Reference` type has the same in-memory representation as a C - pointer or the Mojo `Pointer` type, it also tracks a symbolic "lifetime" value - so the compiler can reason about the potentially accessed set of values. This - lifetime is part of the static type of the reference, so it propagates through - generic algorithms and abstractions built around it. - - The `Reference` type can form references to both mutable and immutable memory - objects, e.g. those on the stack or borrowed/inout/owned function arguments. - It is fully parametric over mutability, eliminating the [problems with code - duplication due to mutability - specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and - provides the base for unified user-level types. For example, it could be - used to implement an array slice object that handles both mutable and immutable - array slices. - - While this is a major step forward for the lifetimes system in Mojo, it is - still _very_ early and awkward to use. Notably, there is no syntactic sugar - for using references, such as automatic dereferencing. Several aspects of it - need to be more baked. It is getting exercised by variadic memory arguments, - which is why they are starting to behave better now. - - Note: the safe `Reference` type and the unsafe pointer types are defined in - the same module, currently named `memory.unsafe`. We expect to restructure - this module in a future release. - -- Mojo now allows types to implement `__refattr__()` and `__refitem__()` to - enable attribute and subscript syntax with computed accessors that return - references. For common situations where these address a value in memory this - provides a more convenient and significantly more performant alternative to - implementing the traditional get/set pairs. Note: this may be changed in the - future when references auto-dereference—at that point we may switch to just - returning a reference from `__getattr__()`. -- Parametric closures can now capture register passable typed values by copy - using the `__copy_capture` decorator. For example, the following code will - print `5`, not `2`. - - ```mojo - fn foo(x: Int): - var z = x - - @__copy_capture(z) - @parameter - fn formatter() -> Int: - return z - z = 2 - print(formatter()) - - fn main(): - foo(5) - ``` - -- String now implements KeyElement and may be used as a key in Dict. -- More robust support for structs with fields of self referencing types. - For example, the following code will work and print `0`: - - ```mojo - struct Foo(CollectionElement): - var vec: DynamicVector[Self] - - fn __init__(inout self: Self): - self.vec = DynamicVector[Self]() - - fn __moveinit__(inout self: Self, owned existing: Self): - self.vec = existing.vec ^ - - fn __copyinit__(inout self: Self, existing: Self): - self.vec = existing.vec - - - fn main(): - var foo = Foo() - print(len(foo.vec)) - ``` - -### ❌ Removed - -- The `__takeinit__` special constructor form has been removed from the - language. This "non-destructive move" operation was previously wired into the - `x^` transfer operator, but had unpredictable behavior that wasn't consistent. - Now that Mojo has traits, it is better to model this as an explicit `.take()` - operation on a type, which would transfer out the contents of the type without - ending its lifetime. For example, for a type that holds a pointer, `take()` - might return a new instance pointing to the same data, and null out its own - internal pointer. - - This change makes it clear when a lifetime is ended versus when the - contents of an LValue are explicitly taken. - -- The current implementation of autotuning has been deprecated, as Mojo's - autotuning implementation is undergoing a redesign. Tutorials around the - current implementation have also been removed as they are being rewritten. - - Consequently, the `autotune()`, `autotune_fork()`, and `search()` functions - have been removed from the standard library. - -- The `_OldDynamicVector` type that worked only on register passable element - types has been removed. Please migrate uses to - [`DynamicVector`](/mojo/stdlib/collections/list#list) which - works on both register passable and memory types. - -- The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using - either [`DynamicVector`](/mojo/stdlib/collections/list#list) - or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - instead. - -- The `@adaptive` decorator has been removed from the language. Any uses of the - decorator in a non-search context can be replaced with `@parameter if`. For - example: - - ```mojo - @adaptive - fn foo[a: Bool](): - constrained[a]() - body1() - - @adaptive - fn foo[a: Bool](): - constrained[not a]() - body2() - ``` - - Can be rewritten as: - - ```mojo - fn foo[a: Bool](): - @parameter - if a: - body1() - else: - body2() - ``` - - Consequently, the special `__adaptive_set` attribute has been removed as well. - -- Result parameters have been removed from Mojo. Result parameter declarations - in function parameter lists are no longer allowed, nor are forward alias - declarations. This includes removing the `param_return` statement. - -- The `@noncapturing` and `@closure` decorators have been removed due to - refinements and improvements to the closure model. See below for more details! - -### 🦋 Changed - -- The Mojo closure model has been refined to be more straightforward and safe. - Mojo has two closure types: parameter closures and runtime closures. Parameter - closures can be used in higher-order functions and are the backbone of - functions like `vectorize` and `parallelize`. They are always denoted by - `@parameter` and have type `fn() capturing -> T` (where `T` is the return - type). - - On the other hand, runtime closures are always dynamic values, capture values - by invoking their copy constructor, and retain ownership of their capture - state. You can define a runtime closure by writing a nested function that - captures values: - - ```mojo - fn outer(b: Bool, x: String) -> fn() escaping -> None: - fn closure(): - print(x) # 'x' is captured by calling String.__copyinit__ - - fn bare_function(): - print("hello") # nothing is captured - - if b: - # closure can be safely returned because it owns its state - return closure^ - - # function pointers can be converted to runtime closures - return bare_function - ``` - - The type of runtime closures are of the form `fn() escaping -> T`. You - can pass equivalent function pointers as runtime closures. - - Stay tuned for capture list syntax for move capture and capture by reference, - and a more unified closure model! - -- The `@unroll(n)` decorator can now take a parameter expression for - the unroll factor, i.e. `n` can be a parameter expression that is - of integer type. - -- The `cpython` module in the `python` package has been moved to be an internal - module, i.e, `_cpython`. - -- `AnyType` and `Destructable` have been unified into a single trait, `AnyType`. - Every nominal type (i.e. all structs) now automatically conform to `AnyType`. - -- Previously, the `mojo package` command would output a Mojo package that - included both partly-compiled Mojo code, as well as fully-compiled machine - code for a specific computer architecture -- the architecture of the machine - being used to invoke the `mojo package` command. - - Now, `mojo package` only includes partly-compiled Mojo code. It is only fully - compiled for the specific computer architecture being used at the point that - the package is first `import`-ed. As a result, Mojo packages are smaller and - more portable. - -- The `simd_width` and `dtype` parameters of `polynomial_evaluate` have been - switched. Based on the request in - [#1587](https://github.com/modularml/mojo/issues/1587), the - `polynomial_evaluate` function has also been extended so that the - `coefficients` parameter can take either a either a - [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a - [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). - -- As a tiny step towards removing `let` declarations, this release removes the - warning: `'var' was never mutated, consider switching to a 'let'`. - -### 🛠️ Fixed - -- [#1595](https://github.com/modularml/mojo/issues/1595) - Improve error message - when trying to materialize `IntLiteral` in runtime code. -- Raising an error from the initializer of a memory-only type now works - correctly in the presence of complex control flow. Previously Mojo could run - the destructor on `self` before it was initialized when exiting with an - error. -- [#1096](https://github.com/modularml/mojo/issues/1096) - Improve warning - messages for dead code in conditionals like `or` expressions. -- [#1419](https://github.com/modularml/mojo/issues/1419) - Fix assertion failure - with uninitialized lattice values. -- [#1402](https://github.com/modularml/mojo/issues/1402) - Fix movable trait not - detected on recursive struct implemented with `AnyPointer`. -- [#1399](https://github.com/modularml/mojo/issues/1399) - Fix parser crash when - a parameter type in a struct that implements a trait is misspelled. -- [#1152](https://github.com/modularml/mojo/issues/1152) - Allow mutable `self` - argument when overloading operators using dunder methods. -- [#1493](https://github.com/modularml/mojo/issues/1493) - Fix crash in - `DynamicVector` copy constructor in certain situations. -- [#1316](https://github.com/modularml/mojo/issues/1316) - The `benchmark.keep` - function now properly handles vector types. -- [#1505](https://github.com/modularml/mojo/issues/1505) - The `simd.shuffle` - operation now works on 64 element permutations. -- [#1355](https://github.com/modularml/mojo/issues/1355) - Fix `String.find()` - returning wrong value when starting index is non-zero. -- [#1367](https://github.com/modularml/mojo/issues/1367) - Fix `String.replace()` - returning incorrect results for multi-character search strings. -- [#1535](https://github.com/modularml/mojo/issues/1535) - Invalid error `field - 'w.x.y' destroyed out of the middle of a value, preventing the overall value - from being destroyed`. -- [#1475](https://github.com/modularml/mojo/issues/1475) - Assertion failure in - nested loop. -- [#1591](https://github.com/modularml/mojo/issues/1591) - Assertion failure - when using `AnyType` struct member. -- [#1503](https://github.com/modularml/mojo/issues/1503) - Rename the mojo build - of LLDB to `mojo-lldb`, to prevent name collisions with the system's LLDB. -- [#1542](https://github.com/modularml/mojo/issues/1542) - `@unroll` does not - accept alias as unroll factor. -- [#1443](https://github.com/modularml/mojo/issues/1443) - Compiler crash on - variadic list of traits. -- [#1604](https://github.com/modularml/mojo/issues/1604) - Variable of trivial - type not destroyed by transferring ownership. -- [#1341](https://github.com/modularml/mojo/issues/1341) - Segmentation fault - when passing closures around. -- [#217](https://github.com/modularml/mojo/issues/217) - Closure state is - stack allocated. - -## v0.6.1 (2023-12-18) - -### ⭐️ New - -- The Mojo REPL now provides limited support for the `%cd` magic command. - - This command automatically maintains an internal stack of directories you - visit during the REPL session. Usage: - - - `%cd 'dir'`: change to directory `dir` and push it on the directory stack. - - `%cd -`: pop the directory stack and change to the last visited directory. - -- Structs decorated with `@value` now automatically conform to the - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. - -- [`String`](/mojo/stdlib/builtin/string.html#string) now has new - [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and - [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, - respectively, to Python's `str.toupper()` and `str.tolower()`. - -- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and - [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types - implementing the `__hash__()` method. Future releases will add `Hashable` - support to Standard Library types. In the meantime, the `hash` module includes - a version of the `hash()` function that works on arbitrary byte strings. To - generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you - use the internal `_hash_simd()` function: - - ```mojo - from builtin.hash import _hash_simd - - fn gen_simd_hash(): - let vector = SIMD[DType.int64, 4](1, 2, 3, 4) - let hash = _hash_simd(vector) - ``` - -- Several standard library types now conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), - [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), - [`DynamicVector`](/mojo/stdlib/collections/list#list), - [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), - and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). - -### 🦋 Changed - -- `utils.vector` has been moved to a new `collections` package to make - space for new collections. This means that if you had previous code - that did `from utils.vector import DynamicVector`, it now needs to - be `from collections.vector import DynamicVector` due to the move. - -- The special destructor method `__del__()` has been changed to enforce - that it cannot raise an error. Raising destructors are not supported properly - at the moment. - -### 🛠️ Fixed - -- [#1421](https://github.com/modularml/mojo/issues/1421) - Fixed a crash when - using Tuples in the REPL. - -- [#222](https://github.com/modularml/mojo/issues/222) - Generate an error - for obviously self recursive functions. - -- [#1408](https://github.com/modularml/mojo/issues/1408) - Fix overload - resolution when candidates can return generic types. - -- [#1413](https://github.com/modularml/mojo/issues/1413) and - [#1395](https://github.com/modularml/mojo/issues/1395) - Do not crash when - re-declaring a builtin declaration. - -- [#1307](https://github.com/modularml/mojo/issues/1307) - Fix compatibility of - function signatures that only differ in default argument values. - -- [#1380](https://github.com/modularml/mojo/issues/1380) - Fix printing - of empty `String`. - -## v0.6.0 (2023-12-04) - -### 🔥 Legendary - -- Traits have arrived! - - You can now define a _trait_, which consists of a required set of method - prototypes. A struct can _conform to_ the trait by implementing these methods. - This lets you write generic functions that work on any structs that conform to - a given trait. - - The following section gives a brief overview of traits—see the - [Mojo Manual](/mojo/manual/traits.html) and this - [traits blog post](https://modul.ar/traits-blog) for more details! - - Traits are declared with the `trait` keyword. The bodies of traits should - contain method signatures declared with `...` as their bodies. Default - method implementations are not supported yet. - - ```mojo - trait SomeTrait: - fn required_method(self, x: Int): ... - ``` - - The trait can be implemented on a struct by inheriting from it. - - ```mojo - struct SomeStruct(SomeTrait): - fn required_method(self, x: Int): - print("hello traits", x) - ``` - - You can then write a generic functions that accepts any type that conforms to - the trait. You do this by creating a parameterized function with a - trait-typed parameter: - - ```mojo - fn fun_with_traits[T: SomeTrait](x: T): - x.required_method(42) - ``` - - Which can be invoked with instances of types that conform to the trait: - - ```mojo - var thing = SomeStruct() - # Infer the parameter `T`! - fun_with_traits(thing) - ``` - - Traits can also inherit from other traits, which simply requires that - implementors of the child trait also conform to all parent traits. - - ```mojo - trait Parent: - fn parent_func(self): ... - - trait Child(Parent): - fn child_func(self): ... - ``` - - Then, both child and parent trait methods can be invoked on instances of - the trait `Child`. As well, an instance of the child trait can be converted to - an instance of the parent trait. - - ```mojo - fn the_parents[T: Parent](x: T): - x.parent_func() - - fn the_children[T: Child](x: T): - x.child_func() - x.parent_func() - # Upcast `x` from instance of `Child` to `Parent`. - the_parents(x) - ``` - - For more information, see the [Traits page](/mojo/manual/traits.html) - in the Mojo Manual. - -- A fundamental `Destructable` trait has been added to the language. This is a - core trait that every trait automatically conforms to. This enables - destruction of generic types and generic collections. - - **Note:** We're aware that this trait might be better spelled `Destructible`. - We're planning on removing it in the future and moving its functionality to - `AnyType` so that any type that doesn't provide its own destructor will have - a default, no-op destructor. - -- We've added some traits to the standard library, you can implement these on - your own types: - - - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) - - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) - - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) - - [`Intable`](/mojo/stdlib/builtin/int.html#intable) - - [`Sized`](/mojo/stdlib/builtin/len.html#sized) - - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - -- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), - [`str()`](/mojo/stdlib/builtin/str.html#str), and - [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with - types that implement the `Sized`, `Stringable`, and `Intable` traits, - respectively. - -- [`DynamicVector`](/mojo/stdlib/collections/list#list) is now a - proper generic collection that can use any type that implements the `Movable` - and `Copyable` traits. This means you can now write, for example, - `DynamicVector[String]`. Also, `DynamicVector` now invokes its element - destructors upon destruction, so `_del_old` has been deleted. - -- `print` now works on any types that implement `Stringable` by invoking their - `__str__` method: - - ```mojo - @value - struct BoxedInt(Stringable): - var value: Int - - fn __str__(self) -> String: - return self.value - - print(BoxedInt(11), "hello traits!", BoxedInt(42)) - ``` - -### ⭐️ New - -- The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include _everything_ about Mojo yet, but it includes a lot, - and more than the original [programming - manual](/mojo/programming-manual.html) (now deprecated). - - Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on - GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love - to accept contributions to help us improve them! - -- Mojo now supports partial automatic parameterization: when a function is - declared with an argument of a partially bound type, the unbound parameters - of that type are implicitly added to the function's input parameters. For - example: - - ```mojo - @value - struct Fudge[a: Int, b: Int, c: Int = 7]: ... - - # These function declarations are roughly equivalent: - fn eat(f: Fudge[5]): ... # implicitly parameterized - fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized - ``` - - In the first signature for `eat()`, the `b` parameter isn't bound, so it's - _implicitly_ added as an input parameter on the function. - - In the second signature for `eat()`, the author has explicitly defined an - input parameter (`_b`), which is bound to the second parameter on the argument - type (which happens to be `b`). - - Both functions can be called like this: - - ```mojo - eat(Fudge[5, 8]()) - ``` - - Mojo infers the value of the `b` parameter from the argument (in this case, - 8). - - With the second signature, you can also pass the `_b` parameter value - explicitly: - - ```mojo - eat[3](Fudge[5, 3]()) - ``` - - Moreover, Mojo now allows you to explicitly mark parameters as unbound using - the `_` as syntax meaning "placeholder for an unbound parameter." For example: - - ```mojo - # These function declarations are roughly equivalent: - fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized - fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized - fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized - ``` - - The first two signatures explicitly unbind the `b` and `c` parameters. - - In the last signature, the `_b` and `_c` parameters are explicitly declared by - the author, and bound to the `b` and `c` parameters in the argument type. - - Any of these signatures can be called like this: - - ```mojo - eat(Fudge[5, 8]()) - eat(Fudge[5, 8, 9]()) - ``` - - Note that the default parameter values of struct parameters are bound, unless - explicitly unbound by the user. - - For more information, see the - [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). - -- Parametric types can now be partially bound in certain contexts. For example, - a new `Scalar` type alias has been added defined as: - - ```mojo - alias Scalar = SIMD[size=1] - ``` - - Which creates a parametric type alias `Scalar` with a single parameter of type - `DType`. Types can also be partially or fully bound in other contexts. For - instance, `alias` declarations of type values inside functions now work - properly: - - ```mojo - fn type_aliases(): - alias T = SIMD - print(T[DType.float32, 1]()) - alias Partial = T[type=DType.int32] - print(Partial[2]()) - ``` - -- The `__mlir_op` feature now supports operations that return multiple results. - To use them, you write the `_type` field as a `Tuple` of types. For example: - - ```mojo - # The `ret` variable has type `Tuple[Int, Int]`. - let ret = __mlir_op.`multi_result_op`[_type=(Int, Int)]() - ``` - -- Mojo now has the ability to read raw bytes from a file using the - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. - For example: - - ```mojo - with open("file.binary", "r") as f: - data = f.read_bytes() - ``` - -- A size argument was added to the - [`read()`](/mojo/stdlib/builtin/file.html#read) and - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the - builtin `file.FileHandle`. The size argument defaults to -1 and maintains the - previous "read to EOF" behavior when size is negative. - - ```mojo - with open("file.binary", "r") as f: - data1 = f.read_bytes(1024) - data2 = f.read_bytes(256) - ``` - -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and - `read_text()` methods to read file contents from a path: - - ```mojo - let text_path = Path("file.txt") - let text = text_path.read_text() - - let binary_path = Path("file.binary") - let data = binary_path.read_bytes() - ``` - -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` - methods to save and load to file. These - methods preserve shape and datatype information. For example: - - ```mojo - let tensor = Tensor[DType.float32]() - tensor.save(path) - - let tensor_from_file = Tensor[DType.float32].load(path) - ``` - -- Subscripting added to - [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): - - ```mojo - let p = DTypePointer[DType.float16].alloc(4) - for i in range(4): - p[i] = i - print(p[i]) - ``` - -- `file.FileHandle` now has a `seek()` method. - -- [`String`](/mojo/stdlib/builtin/string.html#string) now has an - [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to - Python's `str.rfind()`. - -- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method - analogous to Python's `str.split()`. - -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a - [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to - Python's `pathlib.Path.suffix`. - -- The Mojo REPL now supports indented expressions, making it a bit easier to - execute expressions copied from an indented block (such as a doc string). - -- The Mojo Language Server now implements the Document Symbols request. IDEs use - this to provide support for **Outline View** and **Go to Symbol**. This - addresses [Issue #960](https://github.com/modularml/mojo/issues/960). - -- The Mojo Language Server now shows documentation when code completing modules - or packages in `import` statements. - -- The Mojo Language Server now supports processing code examples, defined as - markdown Mojo code blocks, inside of doc strings. This enables IDE features - while writing examples in API documentation. - -- The Mojo Language Server now provides semantic token information, providing - better highlighting for symbols whose semantics are not statically analyzable. - -- The Mojo Language Server now classifies doc strings as folding ranges, - making them easier to collapse, reducing vertical space while editing. - -- Command line options for the `mojo` driver that take arguments can now be - written in either of two ways: both `--foo FOO` and `--foo=FOO`. Previously, - only the former was valid. - -### 🦋 Changed - -- Variadic list types - [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) - are now iterable. Variadic arguments are automatically projected into one of - these types inside the function body, so var args can be iterated: - - ```mojo - fn print_ints(*nums: Int): - for num in nums: - print(num) - print(len(nums)) - ``` - -- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) - package now raise an `Error` when the assertion fails instead of returning a - `Bool` for whether the assertion succeeded or not. - -- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no - longer (implicitly) assumed to be register-passable. A new `AnyRegType` type - is used to represent generic types that are register passable. - -- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) - report is now an argument instead of a parameter: - - ```mojo - let report = benchmark.run[timer]() - report.print(Unit.ms) - ``` - -- Default values on `inout` arguments are no longer permitted, i.e. the - following will now raise an error: - - ```mojo - fn inout_default(inout x: Int = 2): ... - ``` - -- The `to_string()` function has been removed from - [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of - the new `__str__()` function. This composes better with traits so it can be - used with the generic `str()` function. - -### 🛠️ Fixed - -- [#734](https://github.com/modularml/mojo/issues/734) - Consumption of struct - works only for types with a `__del__` method. - -- [#910](https://github.com/modularml/mojo/issues/910) - Parser crash when - using memory-only generic type as return of function that `raise`s. - -- [#1060](https://github.com/modularml/mojo/issues/1060) - Mojo happily parses - code that has messed up indentation - -- [#1159](https://github.com/modularml/mojo/issues/1159) - The language server - doesn't warn about bad return type. - -- [#1166](https://github.com/modularml/mojo/issues/1166) - warning: unreachable - code after return statement with context manager - -- [#1098](https://github.com/modularml/mojo/issues/1098) - The language server - doesn't highlight properties of PythonObjects correctly. - -- [#1153](https://github.com/modularml/mojo/issues/1153) - The language server - crashes when parsing an invalid multi-nested module import. - -- [#1236](https://github.com/modularml/mojo/issues/1236) - The language server - doesn't show autocomplete in if statements. - -- [#1246](https://github.com/modularml/mojo/issues/1246) - Warning diagnostics - are transient in the presence of caching. - -### Known Issue - -- There is an issue affecting Jupyter notebooks that use autotuning and traits. - This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the _Matrix multiplication in - Mojo_ notebook. - -## v0.5.0 (2023-11-2) - -### ⭐️ New - -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the - architectural SIMD width of the type. This means you can write - `SIMD[DType.float32]` which is equivalent to - `SIMD[DType.float32, simdwidthof[DType.float32]()]`. - -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` - function that allows you to concatenate two `SIMD` values together and produce - a new `SIMD` value. - -- Mojo now supports compile-time _keyword parameters_, in addition to existing - support for [keyword - arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For - example: - - ```mojo - fn foo[a: Int, b: Int = 42](): - print(a, "+", b) - - foo[a=5]() # prints '5 + 42' - foo[a=7, b=13]() # prints '7 + 13' - foo[b=20, a=6]() # prints '6 + 20' - ``` - - Keyword parameters are also supported in structs: - - ```mojo - struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]: - fn __init__(inout self): - print(msg, a) - - fn use_kw_params(): - KwParamStruct[a=42]() # prints '🔥mojo🔥 42' - KwParamStruct[5, msg="hello"]() # prints 'hello 5' - KwParamStruct[msg="hello", a=42]() # prints 'hello 42' - ``` - - For more detail, see the [programming - manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). - - For the time being, the following notable limitations apply: - - - Keyword-only parameters are **not supported** yet: - - ```mojo - fn baz[*args: Int, b: Int](): pass # fails - fn baz[a: Int, *, b: Int](): pass # fails - ``` - - (The analogous keyword-only arguments in Python are described in - [PEP 3102](https://peps.python.org/pep-3102/).) - - - Variadic keyword parameters are **not supported** yet: - - ```mojo - fn baz[a: Int, **kwargs: Int](): pass # fails - ``` - -- Mojo now supports "automatic" parameterization of functions. What this means - is that if a function argument type is parametric but has no bound parameters, - they are automatically added as input parameters on the function. This works - with existing features to allow you to write parametric functions with less - boilerplate. - - ```mojo - @value - struct Thing[x: Int, y: Int]: - pass - - fn foo(v: Thing): - print(v.x) - print(v.y) - - fn main(): - let v = Thing[2, 3]() - foo(v) - ``` - - However, partial autoparameterization is **not supported** yet: - - ```mojo - fn foo(v: Thing[y=7]): # Partially bound type not allowed yet. - ... - ``` - -- Keyword argument passing is supported when invoking `__getitem__` using - the bracket syntax: - - ```mojo - @value - struct MyStruct: - fn __getitem__(self, x: Int, y: Int, z: Int) -> Int: - return x * y + z - - MyStruct()[z=7, x=3, y=5] # returns 22 - ``` - - However, keyword argument passing to `__setitem__` using the bracket syntax is - **not supported** yet: - - ```mojo - @value - struct OtherStruct: - fn __setitem__(self, x: Int, y: Int): pass - - OtherStruct()[x=1] = 4 # fails - ``` - -- Function argument input parameters can now be referenced within the signature - of the function: - - ```mojo - fn foo(x: SIMD, y: SIMD[x.type, x.size]): - pass - ``` - -- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been - simplified and improved so you can now run: - - ```mojo - import benchmark - from time import sleep - - fn sleeper(): - sleep(.01) - - fn main(): - let report = benchmark.run[sleeper]() - print(report.mean()) - ``` - - It no longer requires a capturing `fn` so can benchmark functions outside the - same scope. - - You can print a report with: - - ```mojo - report.print() - ``` - - ```plaintext - --------------------- - Benchmark Report (s) - --------------------- - Mean: 0.012314264957264957 - Total: 1.440769 - Iters: 117 - Warmup Mean: 0.0119335 - Warmup Total: 0.023866999999999999 - Warmup Iters: 2 - Fastest Mean: 0.012227958333333334 - Slowest Mean: 0.012442699999999999 - ``` - - Units for all functions default to seconds, but can be changed with: - - ```mojo - from benchmark import Unit - - report.print[Unit.ms]() - ``` - -- Mojo now supports struct parameter deduction (a.k.a. class template argument - deduction, or CTAD) for partially bound types. Struct parameter deduction is - also possible from static methods. For example: - - ```mojo - @value - struct Thing[v: Int]: pass - - struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]: - fn __init__(inout self, x: Thing[a]): - print("hello", a, b, c) - - @staticmethod - fn foo(x: Thing[a]): - print("🔥", a, b, c) - - fn main(): - _ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8' - CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' - ``` - -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and - `tofile()` methods to save and load as bytes from a file. - -- The built-in `print()` function now works on the - [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. - -- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and - [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors - that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) - and [`StaticIntTuple`](/mojo/stdlib/utils/index_.html#staticinttuple) to - initialize shapes. - -- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the - `count()` and `find()` methods to enable counting the number of occurrences or - finding the offset index of a substring in a string. - -- The `String` type now has a `replace()` method which allows you to replace a - substring with another string. - -### 🦋 Changed - -- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) - moved under builtins, and no longer need to be imported. - -- Variadic arguments are now automatically projected into a `VariadicList` or - `VariadicListMem` inside the function body. This allows for more flexibility - in using var args. For example: - - ```mojo - fn print_ints(*nums: Int): - let len = len(nums) - for i in range(len): - print(nums[i]) - print(len) - ``` - -- The parameters for - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - have been switched. The parameters are now `[type, size]` instead of - `[size, type]`. The `InlinedFixedVector` now has a default size which means - that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` - and the default size is used. - -- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer.html#buffer) - and [`NDBuffer`](/mojo/stdlib/buffer/buffer.html#ndbuffer) is renamed to - `tofile()` to match the Python naming. - -- Mojo will now utilize all available cores across all NUMA sockets on the host - machine by default. The prior default behavior was to use all the cores on - the first socket. - -### ❌ Removed - -- The `math.numerics` module is now private, because its types (`FPUtils` and - `FlushDenormals`) should not be used externally. - -### 🛠️ Fixed - -- [#532](https://github.com/modularml/mojo/issues/532) - Compiler optimizing - while True loop away -- [#760](https://github.com/modularml/mojo/issues/760) - Compilation error: - 'hlcf.for.yield' op specifies 0 branch inputs but target expected 1 along - control-flow edge from here -- [#849](https://github.com/modularml/mojo/issues/849) - The `Tensor` type is - now initialized with zeros at construction time. -- [#912](https://github.com/modularml/mojo/issues/912) - Invalid load for - `__get_address_as_lvalue`. -- [#916](https://github.com/modularml/mojo/issues/916) - Parser crash when - specifying default values for `inout` arguments. -- [#943](https://github.com/modularml/mojo/issues/943) - Mojo hangs if you - use continue in the nested loop -- [#957](https://github.com/modularml/mojo/issues/957) - Parser crash when a - function call with variadic arguments of a memory-only type is evaluated at - compile time. -- [#990](https://github.com/modularml/mojo/issues/990) - Fixes rounding - issue with floor division with negative numerator. -- [#1018](https://github.com/modularml/mojo/issues/1018) - In some cases the - sort function was returning invalid results. This release fixes some of these - corner cases. -- [#1010](https://github.com/modularml/mojo/issues/1010) - Initializing tensor - in alias declaration results in crash. -- [#1110](https://github.com/modularml/mojo/issues/1110) - The `time.now()` - function now returns nanoseconds across all operating systems. -- [#1115](https://github.com/modularml/mojo/issues/1115) - cannot load - non-register passable type into SSA register. - -## v0.4.0 for Mac (2023-10-19) - -### 🔥 Legendary - -- Mojo for Mac! - - The Mojo SDK now works on macOS (Apple silicon). This is the same version - previously released for Linux. Get the latest version of the SDK for your Mac - system: - - [Download Now!](https://developer.modular.com/download) - -## v0.4.0 (2023-10-05) - -### ⭐️ New - -- Mojo now supports default parameter values. For example: - - ```mojo - fn foo[a: Int = 3, msg: StringLiteral = "woof"](): - print(msg, a) - - fn main(): - foo() # prints 'woof 3' - foo[5]() # prints 'woof 5' - foo[7, "meow"]() # prints 'meow 7' - ``` - - Inferred parameter values take precedence over defaults: - - ```mojo - @value - struct Bar[v: Int]: - pass - - fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]): - print(msg, a) - - fn main(): - foo(Bar[9]()) # prints 'quack 9' - ``` - - Structs also support default parameters: - - ```mojo - @value - struct DefaultParams[msg: StringLiteral = "woof"]: - alias message = msg - - fn main(): - print(DefaultParams[]().message) # prints 'woof' - print(DefaultParams["meow"]().message) # prints 'meow' - ``` - -- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O - support. You can now write: - - ```mojo - var f = open("my_file.txt", "r") - print(f.read()) - f.close() - ``` - - or - - ```mojo - with open("my_file.txt", "r") as f: - print(f.read()) - ``` - -- Mojo now allows context managers to support an `__enter__` method without - implementing support for an `__exit__` method, enabling idioms like this: - - ```mojo - # This context manager consumes itself and returns it as the value. - fn __enter__(owned self) -> Self: - return self^ - ``` - - Here Mojo _cannot_ invoke a noop `__exit__` method because the context - manager is consumed by the `__enter__` method. This can be used for types - (like file descriptors) that are traditionally used with `with` statements, - even though Mojo's guaranteed early destruction doesn't require that. - -- A very basic version of `pathlib` has been implemented in Mojo. The - module will be improved to achieve functional parity with Python in - the next few releases. - -- The `memory.unsafe` module now contains a `bitcast` function. This is a - low-level operation that enables bitcasting between pointers and scalars. - -- The input parameters of a parametric type can now be directly accessed as - attribute references on the type or variables of the type. For example: - - ```mojo - @value - struct Thing[param: Int]: - pass - - fn main(): - print(Thing[2].param) # prints '2' - let x = Thing[9]() - print(x.param) # prints '9' - ``` - - Input parameters on values can even be accessed in parameter contexts. For - example: - - ```mojo - fn foo[value: Int](): - print(value) - - let y = Thing[12]() - alias constant = y.param + 4 - foo[constant]() # prints '16' - ``` - -- The Mojo REPL now supports code completion. Press Tab while typing - to query potential completion results. - -- Error messages from Python are now exposed in Mojo. For example the following - should print `No module named 'my_uninstalled_module'`: - - ```mojo - fn main(): - try: - let my_module = Python.import_module("my_uninstalled_module") - except e: - print(e) - ``` - -- Error messages can now store dynamic messages. For example, the following - should print "Failed on: Hello" - - ```mojo - fn foo(x: String) raises: - raise Error("Failed on: " + x) - - fn main(): - try: - foo("Hello") - except e: - print(e) - ``` - -### 🦋 Changed - -- We have improved and simplified the `parallelize` function. The function - now elides some overhead by caching the Mojo parallel runtime. - -- The Mojo REPL and Jupyter environments no longer implicitly expose `Python`, - `PythonObject`, or `Pointer`. These symbols must now be imported explicitly, - for example: - - ```mojo - from python import Python - from python.object import PythonObject - from memory.unsafe import Pointer - ``` - -- The syntax for specifying attributes with the `__mlir_op` prefix have changed - to mimic Python's keyword argument passing syntax. That is, `=` should be used - instead of `:`, e.g.: - - ```mojo - # Old syntax, now fails. - __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() - # New syntax. - __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() - ``` - -- You can now print the `Error` object directly. The `message()` method - has been removed. - -### 🛠️ Fixed - -- [#794](https://github.com/modularml/mojo/issues/794) - Parser crash when - using the `in` operator. -- [#936](https://github.com/modularml/mojo/issues/936) - The `Int` constructor - now accepts other `Int` instances. -- [#921](https://github.com/modularml/mojo/issues/921) - Better error message - when running `mojo` on a module with no `main` function. -- [#556](https://github.com/modularml/mojo/issues/556) - UInt64s are now - printed correctly. -- [#804](https://github.com/modularml/mojo/issues/804) - Emit error instead of - crashing when passing variadic arguments of unsupported types. -- [#833](https://github.com/modularml/mojo/issues/833) - Parser crash when - assigning module value. -- [#752](https://github.com/modularml/mojo/issues/752) - Parser crash when - calling async def. -- [#711](https://github.com/modularml/mojo/issues/711) - The overload resolution - logic now correctly prioritizes instance methods over static methods (if - candidates are an equally good match otherwise), and no longer crashed if a - static method has a `Self` type as its first argument. -- [#859](https://github.com/modularml/mojo/issues/859) - Fix confusing error and - documentation of the `rebind` builtin. -- [#753](https://github.com/modularml/mojo/issues/753) - Direct use of LLVM - dialect produces strange errors in the compiler. -- [#926](https://github.com/modularml/mojo/issues/926) - Fixes an issue that - occurred when a function with a return type of `StringRef` raised an error. - When the function raised an error, it incorrectly returned the string value of - that error. -- [#536](https://github.com/modularml/mojo/issues/536) - Report More information - on python exception. - -## v0.3.1 (2023-09-28) - -Our first-ever patch release of the Mojo SDK is here! Release v0.3.1 -includes primarily installation-related fixes. If you’ve had trouble -installing the previous versions of the SDK, this release may be for you. - -### 🛠️ Fixed - -- [#538](https://github.com/modularml/mojo/issues/538) - Installation hangs - during the testing phase. This issue occurs on machines with a low number - of CPU cores, such as free AWS EC2 instances and GitHub Codespaces. -- [#590](https://github.com/modularml/mojo/issues/590) - Installation fails - with a “failed to run python” message. -- [#672](https://github.com/modularml/mojo/issues/672) - Language server hangs - on code completion. Related to #538, this occurs on machines with a low - number of CPU cores. -- [#913](https://github.com/modularml/mojo/issues/913) - In the REPL and Jupyter - notebooks, inline comments were being parsed incorrectly. - -## v0.3.0 (2023-09-21) - -There's more Mojo to love in this, the second release of the Mojo SDK! This -release includes new features, an API change, and bug fixes. - -There's also an updated version of the [Mojo extension for VS -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - -### ⭐️ New - -- Mojo now has partial support for passing keyword arguments to functions and - methods. For example the following should work: - - ```mojo - fn foo(a: Int, b: Int = 3) -> Int: - return a * b - - fn main(): - print(foo(6, b=7)) # prints '42' - print(foo(a=6, b=7)) # prints '42' - print(foo(b=7, a=6)) # prints '42' - ``` - - Parameters can also be inferred from keyword arguments, for example: - - ```mojo - fn bar[A: AnyType, B: AnyType](a: A, b: B): - print("Hello 🔥") - - fn bar[B: AnyType](a: StringLiteral, b: B): - print(a) - - fn main(): - bar(1, 2) # prints `Hello 🔥` - bar(b=2, a="Yay!") # prints `Yay!` - ``` - - For the time being, the following notable limitations apply: - - - Keyword-only arguments are not supported: - - ```mojo - fn baz(*args: Int, b: Int): pass # fails - fn baz(a: Int, *, b: Int): pass # fails - ``` - - (Keyword-only arguments are described in - [PEP 3102](https://peps.python.org/pep-3102/).) - - - Variadic keyword arguments are not supported: - - ```mojo - fn baz(a: Int, **kwargs: Int): pass # fails - ``` - -- Mojo now supports the `@nonmaterializable` decorator. The purpose is to mark - data types that should only exist in the parameter domain. To use it, a - struct is decorated with `@nonmaterializable(TargetType)`. Any time the - nonmaterializable type is converted from the parameter domain, it is - automatically converted to `TargetType`. A nonmaterializable struct should - have all of its methods annotated as `@always_inline`, and must be computable - in the parameter domain. In the following example, the `NmStruct` type can - be added in the parameter domain, but are converted to `HasBool` when - materialized. - - ```mojo - @value - @register_passable("trivial") - struct HasBool: - var x: Bool - fn __init__(x: Bool) -> Self: - return Self {x: x} - @always_inline("nodebug") - fn __init__(nms: NmStruct) -> Self: - return Self {x: True if (nms.x == 77) else False} - - @value - @nonmaterializable(HasBool) - @register_passable("trivial") - struct NmStruct: - var x: Int - @always_inline("nodebug") - fn __add__(self: Self, rhs: Self) -> Self: - return NmStruct(self.x + rhs.x) - - alias stillNmStruct = NmStruct(1) + NmStruct(2) - # When materializing to a run-time variable, it is automatically converted, - # even without a type annotation. - let convertedToHasBool = stillNmStruct - ``` - -- Mojo integer literals now produce the `IntLiteral` infinite precision integer - type when used in the parameter domain. `IntLiteral` is materialized to the - `Int` type for runtime computation, but intermediate computations at compile - time, using supported operators, can now exceed the bit width of the `Int` - type. - -- The Mojo Language Server now supports top-level code completions, enabling - completion when typing a reference to a variable, type, etc. This resolves - [#679](https://github.com/modularml/mojo/issues/679). - -- The Mojo REPL now colorizes the resultant variables to help distinguish input - expressions from the output variables. - -### 🦋 Changed - -- Mojo allows types to implement two forms of move constructors, one that is - invoked when the lifetime of one value ends, and one that is invoked if the - compiler cannot prove that. These were previously both named `__moveinit__`, - with the following two signatures: - - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __moveinit__(inout self, inout existing: Self): ... - ``` - - We've changed the second form to get its own name to make it more clear that - these are two separate operations: the second has been renamed to - `__takeinit__`: - - ```mojo - fn __moveinit__(inout self, owned existing: Self): ... - fn __takeinit__(inout self, inout existing: Self): ... - ``` - - The name is intended to connote that the operation takes the conceptual value - from the source (without destroying it) unlike the first one which "moves" a - value from one location to another. - - For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). - -- The Error type in Mojo has changed. Instead of extracting the error message - using `error.value` you will now extract the error message using - `error.message()`. - -### 🛠️ Fixed - -- [#503](https://github.com/modularml/mojo/issues/503) - Improve error message - for failure lowering `kgen.param.constant`. -- [#554](https://github.com/modularml/mojo/issues/554) - Alias of static tuple - fails to expand. -- [#500](https://github.com/modularml/mojo/issues/500) - Call expansion failed - due to verifier error. -- [#422](https://github.com/modularml/mojo/issues/422) - Incorrect comment - detection in multiline strings. -- [#729](https://github.com/modularml/mojo/issues/740) - Improve messaging on - how to exit the REPL. -- [#756](https://github.com/modularml/mojo/issues/756) - Fix initialization - errors of the VS Code extension. -- [#575](https://github.com/modularml/mojo/issues/575) - Build LLDB/REPL with - libedit for a nicer editing experience in the terminal. - -## v0.2.1 (2023-09-07) - -The first versioned release of Mojo! 🔥 - -All earlier releases were considered version 0.1. - -### 🔥 Legendary - -- First release of the Mojo SDK! - - You can now develop with Mojo locally. The Mojo SDK is currently available - for Ubuntu Linux systems, and support for Windows and macOS is coming soon. - You can still develop from a Windows or Mac computer using a container or - remote Linux system. - - The Mojo SDK includes the Mojo standard library and the [Mojo command-line - interface](/mojo/cli/) (CLI), which allows you to run, compile, and package - Mojo code. It also provides a REPL programming environment. - - [Get the Mojo SDK!](https://developer.modular.com/download) - -- First release of the [Mojo extension for VS - Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - - This provides essential Mojo language features in Visual Studio Code, such as - code completion, code quick fixes, docs tooltips, and more. Even when - developing on a remote system, using VS Code with this extension provides - a native-like IDE experience. - -### ⭐️ New - -- A new `clobber_memory` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. - The clobber memory function tells the system to flush all memory operations - at the specified program point. This allows you to benchmark operations - without the compiler reordering memory operations. - -- A new `keep` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` - function tries to tell the compiler not to optimize the variable away - if not used. This allows you to avoid compiler's dead code elimination - mechanism, with a low footprint side effect. - -- New `shift_right` and `shift_left` functions have been added to the - [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in - a SIMD vector right/left, filling elements with zeros as needed. - -- A new `cumsum` function has been added to the - [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes - the cumulative sum (also known as scan) of input elements. - -- Mojo Jupyter kernel now supports code completion. - -### 🦋 Changed - -- Extends `rotate_bits_left`, `rotate_left`, `rotate_bits_right`, and - `rotate_right` to operate on Int values. The ordering of parameters has also - been changed to enable type inference. Now it's possible to write - `rotate_right[shift_val](simd_val)` and have the `dtype` and `simd_width` - inferred from the argument. This addresses - [Issue #528](https://github.com/modularml/mojo/issues/528). - -### 🛠️ Fixed - -- Fixed a bug causing the parser to crash when the `with` statement was written - without a colon. - This addresses [Issue #529](https://github.com/modularml/mojo/issues/529). - -- Incorrect imports no longer crash when there are other errors at the top - level of a module. This fixes [Issue - #531](https://github.com/modularml/mojo/issues/531). - -## August 2023 - -### 2023-08-24 - -- Fixed issue where the `with expr as x` statement within `fn` behaved - as if it were in a `def`, binding `x` with function scope instead of using - lexical scope. - -#### ⭐️ New - -- Major refactoring of the standard library to enable packaging and better - import ergonomics: - - The packages are built as binaries to improve startup speed. - - Package and module names are now lowercase to align with the Python style. - - Modules have been moved to better reflect the purpose of the underlying - functions (e.g. `Pointer` is now within the `unsafe` module in the `memory` - package). - - The following modules are now included as built-ins: - `SIMD`, `DType`, `IO`, `Object`, and `String`. - This means it's no longer necessary to explicitly import these modules. - Instead, these modules will be implicitly imported for the user. Private - methods within the module are still accessible using the - `builtin.module_name._private_method` import syntax. - - New `math` package has been added to contain the `bit`, `math`, `numerics`, - and `polynomial` modules. The contents of the `math.math` module are - re-exported into the `math` package. - -- Mojo now supports using memory-only types in parameter expressions and as - function or type parameters: - - ```mojo - @value - struct IntPair: - var first: Int - var second: Int - - fn add_them[value: IntPair]() -> Int: - return value.first + value.second - - fn main(): - print(add_them[IntPair(1, 2)]()) # prints '3' - ``` - -- In addition, Mojo supports evaluating code that uses heap-allocated memory - at compile-time and materializing compile-time values with heap-allocated - memory into dynamic values: - - ```mojo - fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]: - var result = DynamicVector[Int]() - for i in range(lowerBound, upperBound, step): - result.push_back(i) - return result - - fn main(): - alias values = fillVector(5, 23, 7) - for i in range(0, values.__len__()): - print(values[i]) # prints '5', '12', and then '19' - ``` - -#### 🦋 Changed - -- `def main():`, without the explicit `None` type, can now be used to define - the entry point to a Mojo program. - -- The `assert_param` function has been renamed to `constrained` and is now - a built-in function. - -- The `print` function now works on `Complex` values. - -#### 🛠️ Fixed - -- Fixed issues with print formatting for `DType.uint16` and `DType.int16`. -- [Issue #499](https://github.com/modularml/mojo/issues/499) - Two new - `rotate_right` and `rotate_left` functions have been added to the SIMD module. -- [Issue #429](https://github.com/modularml/mojo/issues/429) - You can now - construct a `Bool` from a `SIMD` type whose element-type is `DType.bool`. -- [Issue #350](https://github.com/modularml/mojo/issues/350) - Confusing Matrix - implementation -- [Issue #349](https://github.com/modularml/mojo/issues/349) - Missing load_tr - in struct Matrix -- [Issue #501](https://github.com/modularml/mojo/issues/501) - Missing syntax - error messages in Python expressions. - -### 2023-08-09 - -#### 🦋 Changed - -- The `ref` and `mutref` identifiers are now treated as keywords, which means - they cannot be used as variable, attribute, or function names. These keywords - are used by the "lifetimes" features, which is still in development. We can - consider renaming these (as well as other related keywords) when the - development work gels, support is enabled in public Mojo builds, and when we - have experience using them. - -- The argument handling in `def` functions has changed: previously, they had - special behavior that involved mutable copies in the callee. Now, we have a - simple rule, which is that `def` argument default to the `owned` convention - (`fn` arguments still default to the `borrowed` convention). - - This change is mostly an internal cleanup and simplification of the compiler - and argument model, but does enable one niche use-case: you can now pass - non-copyable types to `def` arguments by transferring ownership of a value - into the `def` call. Before, that would not be possible because the copy was - made on the callee side, not the caller's side. This also allows the explicit - use of the `borrowed` keyword with a `def` that wants to opt-in to that - behavior. - -### 2023-08-03 - -#### ⭐️ New - -- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. - This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which - are just views). Therefore, the tensor type performs its own allocation and - free. Here is a simple example of using the tensor type to represent an RGB - image and convert it to grayscale: - - ```mojo - from tensor import Tensor, TensorShape - from utils.index import Index - from random import rand - - let height = 256 - let width = 256 - let channels = 3 - - # Create the tensor of dimensions height, width, channels and fill with - # random value. - let image = rand[DType.float32](height, width, channels) - - # Declare the grayscale image. - var gray_scale_image = Tensor[DType.float32](height, width) - - # Perform the RGB to grayscale transform. - for y in range(height): - for x in range(width): - let r = image[y, x, 0] - let g = image[y, x, 1] - let b = image[y, x, 2] - gray_scale_image[Index(y, x)] = 0.299 * r + 0.587 * g + 0.114 * b - ``` - -#### 🛠️ Fixed - -- [Issue #53](https://github.com/modularml/mojo/issues/53) - `Int` now - implements true division with the `/` operator. Similar to Python, this - returns a 64-bit floating point number. The corresponding in-place operator, - `/=`, has the same semantics as `//=`. - -## July 2023 - -### 2023-07-26 - -#### ⭐️ New - -- Types that define both `__getitem__` and `__setitem__` (i.e. where - sub-scripting instances creates computed LValues) can now be indexed - in parameter expressions. - -- Unroll decorator for loops with constant bounds and steps: - - `@unroll`: Fully unroll a loop. - - `@unroll(n)`: Unroll a loop by factor of n, where `n` is a positive integer. - - Unroll decorator requires loop bounds and iteration step to be - compiler time constant value, otherwise unrolling will fail with - compilation error. This also doesn't make loop induction variable a parameter. - - ```mojo - # Fully unroll the loop. - @unroll - for i in range(5): - print(i) - - # Unroll the loop by a factor of 4 (with remainder iterations of 2). - @unroll(4) - for i in range(10): - print(i) - ``` - -- The Mojo REPL now prints the values of variables defined in the REPL. There is - full support for scalars and structs. Non-scalar SIMD vectors are not - supported at this time. - -#### 🛠️ Fixed - -- [Issue #437](https://github.com/modularml/mojo/issues/437) - Range can now - be instantiated with a PythonObject. - -- [Issue #288](https://github.com/modularml/mojo/issues/288) - Python strings - can now be safely copied. - -### 2023-07-20 - -#### ⭐️ New - -- Mojo now includes a `Limits` module, which contains functions to get the max - and min values representable by a type, as requested in [Issue - #51](https://github.com/modularml/mojo/issues/51). The following functions - moved from `Math` to `Limits`: `inf()`, `neginf()`, `isinf()`, `isfinite()`. - -- Mojo decorators are now distinguished between "signature" and "body" - decorators and are ordered. Signature decorators, like `@register_passable` - and `@parameter`, modify the type of declaration before the body is parsed. - Body decorators, like `@value`, modify the body of declaration after it is - fully parsed. Due to ordering, a signature decorator cannot be applied after - a body decorator. That means the following is now invalid: - - ```mojo - @register_passable # error: cannot apply signature decorator after a body one! - @value - struct Foo: - pass - ``` - -- Global variables can now be exported in Mojo compiled archives, using the - `@export` decorator. Exported global variables are public symbols in compiled - archives and use the variable name as its linkage name, by default. A custom - linkage name can be specified with `@export("new_name")`. This does not affect - variable names in Mojo code. - -- Mojo now supports packages! A Mojo package is defined by placing an - `__init__.mojo` or `__init__.🔥` within a directory. Other files in the same - directory form modules within the package (this works exactly like it - does [in Python](https://docs.python.org/3/tutorial/modules.html#packages)). - Example: - - ```bash - main.🔥 - my_package/ - __init__.🔥 - module.🔥 - my_other_package/ - __init__.🔥 - stuff.🔥 - ``` - - ```mojo - # main.🔥 - from my_package.module import some_function - from my_package.my_other_package.stuff import SomeType - - fn main(): - var x: SomeType = some_function() - ``` - -- Mojo now supports direct module and package imports! Modules and packages can - be imported and bound to names. Module and package elements, like functions, - types, global variables, and other modules, can be accessed using attribute - references, like `my_module.foo`. Note that modules lack runtime - representations, meaning module references cannot be instantiated. - - ```mojo - import builtin.io as io - import SIMD - - io.print("hello world") - var x: SIMD.Float32 = 1.2 - ``` - -#### 🦋 Changed - -- Reverted the feature from 2023-02-13 that allowed unqualified struct members. - Use the `Self` keyword to conveniently access struct members with bound - parameters instead. This was required to fix - [Issue #260](https://github.com/modularml/mojo/issues/260). - -- Updated the RayTracing notebook: added step 5 to create specular lighting for - more realistic images and step 6 to add a background image. - -#### 🛠️ Fixed - -- [Issue #260](https://github.com/modularml/mojo/issues/260) - Definitions - inside structs no longer shadow definitions outside of struct definitions. - -### 2023-07-12 - -#### ⭐️ New - -- Mojo now has support for global variables! This enables `var` and `let` - declaration at the top-level scope in Mojo files. Global variable initializers - are run when code modules are loaded by the platform according to the order of - dependencies between global variables, and their destructors are called in the - reverse order. - -- The [Mojo programming manual](/mojo/programming-manual.html) is now written - as a Jupyter notebook, and available in its entirety in the Mojo Playground - (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of - the same material, but it was not up-to-date.) - -- As a result, we've also re-written `HelloMojo.ipynb` to be much shorter and - provide a more gentle first-user experience. - -- [`Coroutine` module documentation](/mojo/stdlib/builtin/coroutine) is now - available. Coroutines form the basis of Mojo's support for asynchronous - execution. Calls to `async fn`s can be stored into a `Coroutine`, from which - they can be resumed, awaited upon, and have their results retrieved upon - completion. - -#### 🦋 Changed - -- `simd_bit_width` in the `TargetInfo` module has been renamed to `simdbitwidth` - to better align with `simdwidthof`, `bitwidthof`, etc. - -#### 🛠️ Fixed - -- The walrus operator now works in if/while statements without parentheses, - e.g. `if x := function():`. - -- [Issue #428](https://github.com/modularml/mojo/issues/428) - The - `FloatLiteral` and `SIMD` types now support conversion to `Int` via the - `to_int` or `__int__` method calls. The behavior matches that of Python, which - rounds towards zero. - -### 2023-07-05 - -#### ⭐️ New - -- Tuple expressions now work without parentheses. For example, `a, b = b, a` - works as you'd expect in Python. -- Chained assignments (e.g. `a = b = 42`) and the walrus operator (e.g. - `some_function(b := 17)`) are now supported. - -#### 🦋 Changed - -- The `simd_width` and `dtype_simd_width` functions in the - [`TargetInfo`](/mojo/stdlib/sys/info) module - have been renamed to `simdwidthof`. - -- The `dtype_` prefix has been dropped from `alignof`, `sizeof`, and - `bitwidthof`. You can now use these functions (e.g. `alignof`) with any - argument type, including `DType`. - -- The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were - moved from the `Numerics` module to the [`Math`](/mojo/MojoStdlib/Math.html) - module, to better align with Python's library structure. - -#### 🛠️ Fixed - -- [Issue #253](https://github.com/modularml/mojo/issues/253) - Issue - when accessing a struct member alias without providing parameters. - -- [Issue #404](https://github.com/modularml/mojo/issues/404) - The docs now use - `snake_case` for variable names, which more closely conforms to Python's - style. - -- [Issue #379](https://github.com/modularml/mojo/issues/379) - Tuple - limitations have been addressed and multiple return values are now supported, - even without parentheses. - -- [Issue #347](https://github.com/modularml/mojo/issues/347) - Tuples no longer - require parentheses. - -- [Issue #320](https://github.com/modularml/mojo/issues/320) - Python objects - are now traversable via `for` loops. - -## June 2023 - -### 2023-06-29 - -#### ⭐️ New - -- You can now share `.ipynb` notebook files in Mojo Playground. Just save a - file in the `shared` directory, and then right-click the file and select - **Copy Sharable link**. To open a shared notebook, you must already have - [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); - when you open a shared notebook, click **Import** at the top of the notebook - to save your own copy. For more details about this feature, see the - instructions inside the `help` directory, in the Mojo Playground file browser. - -#### 🦋 Changed - -- The `unroll2()` and `unroll3()` functions in the - [`Functional`](/mojo/stdlib/algorithm/functional) module have been renamed to - overload the `unroll()` function. These functions unroll 2D and 3D loops and - `unroll()` can determine the intent based on the number of input parameters. - -#### 🛠️ Fixed - -- [Issue #229](https://github.com/modularml/mojo/issues/229) - Issue when - throwing an exception from `__init__` before all fields are initialized. - -- [Issue #74](https://github.com/modularml/mojo/issues/74) - Struct - definition with recursive reference crashes. - -- [Issue #285](https://github.com/modularml/mojo/issues/285) - The - [`TargetInfo`](/mojo/stdlib/sys/info) module now includes - `is_little_endian()` and `is_big_endian()` to check if the target host uses - either little or big endian. - -- [Issue #254](https://github.com/modularml/mojo/issues/254) - Parameter name - shadowing in nested scopes is now handled correctly. - -### 2023-06-21 - -#### ⭐️ New - -- Added support for overloading on parameter signature. For example, it is now -possible to write the following: - - ```mojo - fn foo[a: Int](x: Int): - pass - - fn foo[a: Int, b: Int](x: Int): - pass - ``` - - For details on the overload resolution logic, see the Mojo Manual section on - [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). - -- A new `cost_of()` function has been added to `Autotune`. This meta-function - must be invoked at compile time, and it returns the number of MLIR operations - in a function (at a certain stage in compilation), which can be used to - build basic heuristics in higher-order generators. - - ```mojo - from autotune import cost_of - - fn generator[f: fn(Int) -> Int]() -> Int: - @parameter - if cost_of[fn(Int) -> Int, f]() < 10: - return f() - else: - # Do something else for slower functions... - ``` - -- Added a new example notebook with a basic Ray Tracing algorithm. - -#### 🦋 Changed - -- The `constrained_msg()` in the `Assert` module has been renamed to - `constrained()`. - -#### 🛠️ Fixed - -- Overloads marked with `@adaptive` now correctly handle signatures that differ -only in declared parameter names, e.g. the following now works correctly: - - ```mojo - @adaptive - fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... - - @adaptive - fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ... - ``` - -- [Issue #219](https://github.com/modularml/mojo/issues/219) - Issue when - redefining a function and a struct defined in the same cell. - -- [Issue #355](https://github.com/modularml/mojo/issues/355) - The loop order - in the Matmul notebook for Python and naive mojo have been reordered for - consistency. The loop order now follows (M, K, N) ordering. - -- [Issue #309](https://github.com/modularml/mojo/issues/309) - Use snake case - naming within the testing package and move the asserts out of the TestSuite - struct. - -### 2023-06-14 - -#### ⭐️ New - -- Tuple type syntax is now supported, e.g. the following works: - - ```mojo - fn return_tuple() -> (Int, Int): - return (1, 2) - ``` - -#### 🦋 Changed - -- The `TupleLiteral` type was renamed to just `Tuple`, e.g. - `Tuple[Int, Float]`. - -#### 🛠️ Fixed - -- [Issue #354](https://github.com/modularml/mojo/issues/354) - Returning a tuple - doesn't work even with parens. -- [Issue #365](https://github.com/modularml/mojo/issues/365) - Copy-paste error - in `FloatLiteral` docs. -- [Issue #357](https://github.com/modularml/mojo/issues/357) - Crash when - missing input parameter to variadic parameter struct member function. - -### 2023-06-07 - -#### ⭐️ New - -- Tuple syntax now works on the left-hand side of assignments (in "lvalue" - positions), enabling things like `(a, b) = (b, a)`. There are several - caveats: the element types must exactly match (no implicit conversions), - this only works with values of `TupleLiteral` type (notably, it will not work - with `PythonObject` yet) and parentheses are required for tuple syntax. - -#### ❌ Removed - -- Mojo Playground no longer includes the following Python packages (due to size, - compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): - `torch`, `tensorflow`, `keras`, `transformers`. - -#### 🦋 Changed - -- The data types and scalar names now conform to the naming convention used - by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` - instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). - -#### 🛠️ Fixed - -- [Issue #287](https://github.com/modularml/mojo/issues/287) - computed - lvalues don't handle raising functions correctly -- [Issue #318](https://github.com/modularml/mojo/issues/318) - Large integers - are not being printed correctly -- [Issue #326](https://github.com/modularml/mojo/issues/326) - Float modulo - operator is not working as expected -- [Issue #282](https://github.com/modularml/mojo/issues/282) - Default arguments - are not working as expected -- [Issue #271](https://github.com/modularml/mojo/issues/271) - Confusing error - message when converting between function types with different result semantics - -## May 2023 - -### 2023-05-31 - -#### ⭐️ New - -- Mojo Playground now includes the following Python packages (in response to - [popular demand](https://github.com/modularml/mojo/discussions/173)): - `torch`, `tensorflow`, `polars`, `opencv-python`, `keras`, `Pillow`, `plotly`, - `seaborn`, `sympy`, `transformers`. - -- A new optimization is applied to non-trivial copyable values that are passed - as an owned value without using the transfer (`^`) operator. Consider code - like this: - - ```mojo - var someValue: T = ... - ... - takeValueAsOwned(someValue) - ... - ``` - - When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) - value (this is - common in initializers for example), it is allowed to do whatever it wants - with the value and destroy it when it is finished. In order to support this, - the Mojo compiler is forced to make a temporary copy of the `someValue` - value, and pass that value instead of `someValue`, because there may be other - uses of `someValue` after the call. - - The Mojo compiler is now smart enough to detect when there are no uses of - `someValue` later, and it will elide the copy just as if you had manually - specified the transfer operator like `takeValueAsOwned(someValue^)`. This - provides a nice "it just works" behavior for non-trivial types without - requiring manual management of transfers. - - If you'd like to take full control and expose full ownership for your type, - just don't make it copyable. Move-only types require the explicit transfer - operator so you can see in your code where all ownership transfer happen. - -- Similarly, the Mojo compiler now transforms calls to `__copyinit__` methods - into calls to `__moveinit__` when that is the last use of the source value - along a control flow path. This allows types which are both copyable and - movable to get transparent move optimization. For example, the following code - is compiled into moves instead of copies even without the use of the transfer - operator: - - ```mojo - var someValue = somethingCopyableAndMovable() - use(someValue) - ... - let otherValue = someValue # Last use of someValue - use(otherValue) - ... - var yetAnother = otherValue # Last use of otherValue - mutate(yetAnother) - ``` - - This is a significant performance optimization for things like `PythonObject` - (and more complex value semantic types) that are commonly used in a fluid - programming style. These don't want extraneous reference counting operations - performed by its copy constructor. - - If you want explicit control over copying, it is recommended to use a - non-dunder `.copy()` method instead of `__copyinit__`, and recall that - non-copyable types must always use of the transfer operator for those that - want fully explicit behavior. - -#### 🛠️ Fixed - -- [Issue #231](https://github.com/modularml/mojo/issues/231) - Unexpected error - when a Python expression raises an exception -- [Issue #119](https://github.com/modularml/mojo/issues/119) - The REPL fails - when a python variable is redefined - -### 2023-05-24 - -#### ⭐️ New - -- `finally` clauses are now supported on `try` statements. In addition, `try` - statements no longer require `except` clauses, allowing `try-finally` blocks. - `finally` clauses contain code that is always executed from control-flow - leaves any of the other clauses of a `try` statement by any means. - -#### 🦋 Changed - -- `with` statement emission changed to use the new `finally` logic so that - - ```mojo - with ContextMgr(): - return - ``` - - Will correctly execute `ContextMgr.__exit__` before returning. - -#### 🛠️ Fixed - -- [Issue #204](https://github.com/modularml/mojo/issues/204) - Mojo REPL - crash when returning a String at compile-time -- [Issue #143](https://github.com/modularml/mojo/issues/143) - synthesized - init in `@register_passable` type doesn't get correct convention. -- [Issue #201](https://github.com/modularml/mojo/issues/201) - String literal - concatenation is too eager. -- [Issue #209](https://github.com/modularml/mojo/issues/209) - [QoI] Terrible - error message trying to convert a type to itself. -- [Issue #32](https://github.com/modularml/mojo/issues/32) - Include struct - fields in docgen -- [Issue #50](https://github.com/modularml/mojo/issues/50) - Int to string - conversion crashes due to buffer overflow -- [Issue #132](https://github.com/modularml/mojo/issues/132) - PythonObject - `to_int` method has a misleading name -- [Issue #189](https://github.com/modularml/mojo/issues/189) - PythonObject bool - conversion is incorrect -- [Issue #65](https://github.com/modularml/mojo/issues/65) - Add SIMD - constructor from Bool -- [Issue #153](https://github.com/modularml/mojo/issues/153) - Meaning of - `Time.now` function result is unclear -- [Issue #165](https://github.com/modularml/mojo/issues/165) - Type in - `Pointer.free` documentation -- [Issue #210](https://github.com/modularml/mojo/issues/210) - Parameter results - cannot be declared outside top-level in function -- [Issue #214](https://github.com/modularml/mojo/issues/214) - Pointer offset - calculations at compile-time are incorrect -- [Issue #115](https://github.com/modularml/mojo/issues/115) - Float printing - does not include the right number of digits -- [Issue #202](https://github.com/modularml/mojo/issues/202) - - `kgen.unreachable` inside nested functions is illegal -- [Issue #235](https://github.com/modularml/mojo/issues/235) - Crash when - register passable struct field is not register passable -- [Issue #237](https://github.com/modularml/mojo/issues/237) - Parameter - closure sharp edges are not documented - -### 2023-05-16 - -#### ⭐️ New - -- Added missing dunder methods to `PythonObject`, enabling the use of common - arithmetic and logical operators on imported Python values. - -- `PythonObject` is now printable from Mojo, instead of requiring you to import - Python's print function. - -#### 🛠️ Fixed - -- [Issue #98](https://github.com/modularml/mojo/issues/98): - Incorrect error with lifetime tracking in loop. - -- [Issue #49](https://github.com/modularml/mojo/issues/49): Type inference - issue (?) in 'ternary assignment' operation (FloatLiteral vs. 'SIMD[f32, 1]'). - -- [Issue #48](https://github.com/modularml/mojo/issues/48): - and/or don't work with memory-only types. - -- [Issue #11](https://github.com/modularml/mojo/issues/11): `setitem` Support - for `PythonObject`. - -### 2023-05-11 - -#### ⭐️ New - -- `NDBuffer` and `Buffer` are now constructable via `Pointer` and - `DTypePointer`. - -- `String` now supports indexing with either integers or slices. - -- Added factorial function to the `Math` module. - -#### 🦋 Changed - -- The "byref" syntax with the `&` sigil has changed to use an `inout` - keyword to be more similar to the `borrowed` and `owned` syntax in arguments. - Please see [Issue #7](https://github.com/modularml/mojo/issues/7) for more - information. - -- Optimized the Matrix multiplication implementation in the notebook. - Initially we were optimizing for expandability rather than performance. We - have found a way to get the best of both worlds and now the performance of the - optimized Matmul implementation is 3x faster. - -- Renamed the [`^` postfix -operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) -from "consume" to "transfer." - -#### 🛠️ Fixed - -- Fixed missing overloads for `Testing.assertEqual` so that they work on -`Integer` and `String` values. - -- [Issue #6](https://github.com/modularml/mojo/issues/6): -Playground stops evaluating cells when a simple generic is defined. - -- [Issue #18](https://github.com/modularml/mojo/issues/18): -Memory leak in Python interoperability was removed. - -### 2023-05-02 - -#### 📢 Released - -- Mojo publicly launched! This was epic, with lots of great coverage online -including a [wonderful post by Jeremy -Howard](https://www.fast.ai/posts/2023-05-03-mojo-launch.html). The team is -busy this week. - -#### ⭐️ New - -- Added a Base64 encoding function to perform base64 encoding on strings. - -#### 🦋 Changed - -- Decreased memory usage of serialization of integers to strings. - -- Speedup the sort function. - -#### 🛠️ Fixed - -- Fixed time unit in the `sleep` function. - -## April 2023 - -### Week of 2023-04-24 - -- 📢 The default behavior of nested functions has been changed. Mojo nested - functions that capture are by default are non-parametric, runtime closures, - meaning that: - - ```mojo - def foo(x): - # This: - def bar(y): return x * y - # Is the same as: - let bar = lambda y: x * y - ``` - - These closures cannot have input or result parameters, because they are always - materialized as runtime values. Values captured in the closure (`x` in the - above example), are captured by copy: values with copy constructors cannot be - copied and captures are immutable in the closure. - - Nested functions that don't capture anything are by default "parametric" - closures: they can have parameters and they can be used as parameter values. - To restore the previous behavior for capturing closures, "parametric, - capture-by-unsafe-reference closures", tag the nested function with the - `@parameter` decorator. - -- 📢 Mojo now has full support for "runtime" closures: nested functions that - capture state materialized as runtime values. This includes taking the address - of functions, indirect calls, and passing closures around through function - arguments. Note that capture-by-reference is still unsafe! - - You can also take references to member functions with instances of that class - using `foo.member_function`, which creates a closure with `foo` bound to the - `self` argument. - -- 📢 Mojo now supports Python style `with` statements and context managers. - - These things are very helpful for implementing things like our - trace region support and things like Runtime support. - - A context manager in Mojo implements three methods: - - ```mojo - fn __enter__(self) -> T: - fn __exit__(self): - fn __exit__(self, err: Error) -> Bool: - ``` - - The first is invoked when the context is entered, and returns a - value that may optionally be bound to a target for use in the with - body. If the with block exits normally, the second method is - invoked to clean it up. If an error is raised, the third method - is invoked with the Error value. If that method returns true, the - error is considered handled, if it returns false, the error is - re-thrown so propagation continues out of the 'with' block. - -- 📢 Mojo functions now support variable scopes! Explicit `var` and `let` - declarations inside functions can shadow declarations from higher "scopes", - where a scope is defined as any new indentation block. In addition, the - `for` loop iteration variable is now scoped to the loop body, so it is - finally possible to write - - ```mojo - for i in range(1): pass - for i in range(2): pass - ``` - -- 📢 Mojo now supports an `@value` decorator on structs to reduce boilerplate - and encourage best practices in value semantics. The `@value` decorator looks - to see the struct has a memberwise initializer (which has arguments for each - field of the struct), a `__copyinit__` method, and a `__moveinit__` method, - and synthesizes the missing ones if possible. For example, if you write: - - ```mojo - @value - struct MyPet: - var name: String - var age: Int - ``` - - The `@value` decorator will synthesize the following members for you: - - ```mojo - fn __init__(inout self, owned name: String, age: Int): - self.name = name^ - self.age = age - fn __copyinit__(inout self, existing: Self): - self.name = existing.name - self.age = existing.age - fn __moveinit__(inout self, owned existing: Self): - self.name = existing.name^ - self.age = existing.age - ``` - - This decorator can greatly reduce the boilerplate needed to define common - aggregates, and gives you best practices in ownership management - automatically. The `@value` decorator can be used with types that need custom - copy constructors (your definition wins). We can explore having the decorator - take arguments to further customize its behavior in the future. - -- 📚 Memcpy and memcmp now consistently use count as the byte count. - -- 📚 Add a variadic sting join on strings. - -- 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all - elements in a SIMD vector. - -- 📚 Optimize the `pow` function if the exponent is integral. - -- 📚 Add a `len` function which dispatches to `__len__` across the different - structs that support it. - -### Week of 2023-04-17 - -- 📢 Error messages have been significantly improved, thanks to prettier - printing for Mojo types in diagnostics. - -- 📢 Variadic values can now be indexed directly without wrapping them in a - `VariadicList`! - -- 📢 `let` declarations in a function can now be lazily initialized, and `var` - declarations that are never mutated get a warning suggesting they be converted - to a `let` declaration. Lazy initialization allows more flexible patterns of - initialization than requiring the initializer be inline, e.g.: - - ```mojo - let x: Int - if cond: - x = foo() - else: - x = bar() - use(x) - ``` - -- 📢 Functions defined with `def` now return `object` by default, instead of - `None`. This means you can return values (convertible to `object`) inside - `def` functions without specifying a return type. - -- 📢 The `@raises` decorator has been removed. Raising `fn` should be declared - by specifying `raises` after the function argument list. The rationale is that - `raises` is part of the type system, instead of a function modifier. - -- 📢 The `BoolLiteral` type has been removed. Mojo now emits `True` and `False` - directly as `Bool`. - -- 📢 Syntax for function types has been added. You can now write function types - with `fn(Int) -> String` or `async def(&String, *Int) -> None`. No more - writing `!kgen.signature` types by hand! - -- 📢 Float literals are not emitted as `FloatLiteral` instead of an MLIR `f64` - type! - -- 📢 Automatic destructors are now supported by Mojo types, currently spelled - `fn __del___(owned self):` (the extra underscore will be dropped shortly). - These destructors work like Python object destructors and similar to C++ - destructors, with the major difference being that they run "as soon as - possible" after the last use of a value. This means they are not suitable - for use in C++-style RAII patterns (use the `with` statement for that, which - is currently unsupported). - - These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to _not_ capture values - correctly. Be very careful with interesting types in the vicinity of a - closure! - -- A new (extremely dangerous!) builtin function is available for low-level - ownership muckery. The `__get_address_as_owned_value(x)` builtin takes a - low-level address value (of `!kgen.pointer` type) and returns an `owned` value - for the memory that is pointed to. This value is assumed live at the - invocation of the builtin, but is "owned" so it needs to be consumed by the - caller, otherwise it will be automatically destroyed. This is an effective - way to do a "placement delete" on a pointer. - - ```mojo - # "Placement delete": destroy the initialized object begin pointed to. - _ = __get_address_as_owned_value(somePointer.value) - - # Result value can be consumed by anything that takes it as an 'owned' - # argument as well. - consume(__get_address_as_owned_value(somePointer.value)) - ``` - -- Another magic operator, named `__get_address_as_uninit_lvalue(x)` joins - the magic LValue operator family. This operator projects a pointer to - an LValue like `__get_address_as_lvalue(x)`. The difference is that - `__get_address_as_uninit_lvalue(x)` tells the compiler that the pointee is - uninitialized on entry and initialized on exit, which means that you can use - it as a "placement new" in C++ sense. `__get_address_as_lvalue(x)` tells the - compiler that the pointee is initialized already, so reassigning over it will - run the destructor. - - ```mojo - # "*Re*placement new": destroy the existing SomeHeavy value in the memory, - # then initialize a new value into the slot. - __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) - - # Ok to use an lvalue, convert to borrow etc. - use(__get_address_as_lvalue(somePointer.value)) - - # "Placement new": Initialize a new value into uninitialied memory. - __get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5) - - # Error, cannot read from uninitialized memory. - use(__get_address_as_uninit_lvalue(somePointer.value)) - ``` - - Note that `__get_address_as_lvalue` assumes that there is already a value at - the specified address, so the assignment above will run the `SomeHeavy` - destructor (if any) before reassigning over the value. - -- 📢 Implement full support for `__moveinit__` (aka move constructors) - - This implements the ability for memory-only types to define two different - types of move ctors if they'd like: - - 1. `fn __moveinit__(inout self, owned existing: Self)`: Traditional Rust - style moving constructors that shuffles data around while taking - ownership of the source binding. - 2. `fn __moveinit__(inout self, inout existing: Self):`: C++ style "stealing" - move constructors that can be used to take from an arbitrary LValue. - - This gives us great expressive capability (better than Rust/C++/Swift) - and composes naturally into our lifetime tracking and value - categorization system. - -- The `__call__` method of a callable type has been relaxed to take `self` by - borrow, allow non-copyable callees to be called. - -- Implicit conversions are now invoked in `raise` statements properly, allowing - converting strings to `Error` type. - -- Automatic destructors are turned on for `__del__` instead of `__del___`. - -- 📚 Add the builtin FloatLiteral type. - -- 📚 Add integral `floordiv` and `mod` for the SIMD type that handle negative - values. - -- 📚 Add an F64 to String converter. - -- 📚 Make the `print` function take variadic inputs. - -### Week of 2023-04-10 - -- 📢 Introduce consume operator `x^` - - This introduces the postfix consume operator, which produces an RValue given - a lifetime tracked object (and, someday, a movable LValue). - -- Mojo now automatically synthesizes empty destructor methods for certain types - when needed. - -- The `object` type has been built out into a fully-dynamic type, with dynamic - function dispatch, with full error handling support. - - ```mojo - def foo(a) -> object: - return (a + 3.45) < [1, 2, 3] # raises a TypeError - ``` - -- 📢 The `@always_inline` decorator is no longer required for passing capturing - closures as parameters, for both the functions themselves as functions with - capturing closures in their parameters. These functions are still inlined but - it is an implementation detail of capturing parameter closures. Mojo now - distinguishes between capturing and non-capturing closures. Nested functions - are capturing by default and can be made non-capturing with the - `@noncapturing` decorator. A top-level function can be passed as a capturing - closure by marking it with the `@closure` decorator. - -- 📢 Support for list literals has been added. List literals `[1, 2, 3]` - generate a variadic heterogeneous list type. - -- Variadics have been extended to work with memory-primary types. - -- Slice syntax is now fully-supported with a new builtin `slice` object, added - to the compiler builtins. Slice indexing with `a[1:2:3]` now emits calls to - `__setitem__` and `__getitem__` with a slice object. - -- Call syntax has been wired up to `__call__`. You can now `f()` on custom - types! - -- Closures are now explicitly typed as capturing or non-capturing. If a - function intends to accept a capturing closure, it must specify the - `capturing` function effect. - -- 📚 Add a `Tile2D` function to enable generic `2D` tiling optimizations. - -- 📚 Add the `slice` struct to enable getting/setting spans of elements via - `getitem`/`setitem`. - -- 📚 Add syntax sugar to autotuning for both specifying the autotuned values, - searching, and declaring the evaluation function. - -### Week of 2023-04-03 - -- The `AnyType` and `NoneType` aliases were added and auto-imported in all - files. - -- 📢 The Mojo VS Code extension has been improved with docstring validation. It - will now warn when a function's docstring has a wrong argument name, for - example. - -- 📢 A new built-in literal type `TupleLiteral` was added in `_CompilerBuiltin`. - It represents literal tuple values such as `(1, 2.0)` or `()`. - -- 📢 The `Int` type has been moved to a new `Builtin` module and is - auto-imported in all code. The type of integer literals has been changed from - the MLIR `index` type to the `Int` type. - -- Mojo now has a powerful flow-sensitive uninitialized variable checker. This - means that you need to initialize values before using them, even if you - overwrite all subcomponents. This enables the compiler to reason about the - true lifetime of values, which is an important stepping stone to getting - automatic value destruction in place. - -- 📢 Call syntax support has been added. Now you can directly call an object - that implements the `__call__` method, like `foo(5)`. - -- 📢 The name for copy constructors got renamed from `__copy__` to - `__copyinit__`. Furthermore, non-`@register_passable` types now implement - it like they do an init method where you fill in a by-reference self, for - example: - - ```mojo - fn __copyinit__(inout self, existing: Self): - self.first = existing.first - self.second = existing.second - ``` - - This makes copy construction work more similarly to initialization, and - still keeps copies `x = y` distinct from initialization `x = T(y)`. - -- 📢 Initializers for memory-primary types are now required to be in the form - `__init__(inout self, ...):` with a None result type, but for register primary - types, it remains in the form `__init__(...) -> Self:`. The `T{}` initializer - syntax has been removed for memory-primary types. - -- Mojo String literals now emit a builtin `StringLiteral` type! One less MLIR - type to worry about. - -- New `__getattr__` and `__setattr__` dunder methods were added. Mojo calls - these methods on a type when attempting member lookup of a non-static member. - This allows writing dynamic objects like `x.foo()` where `foo` is not a member - of `x`. - -- Early destructor support has been added. Types can now define a special - destructor method `__del___` (note three underscores). This is an early - feature and it is still being built out. There are many caveats, bugs, - and missing pieces. Stay tuned! - -- 📚 Integer division and mod have been corrected for rounding in the presence - of negative numbers. - -- 📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to - `SIMD[1, type]`. - -## March 2023 - -### Week of 2023-03-27 - -- 📢 Parameter names are no longer load-bearing in function signatures. This - gives more flexibility in defining higher-order functions, because the - functions passed as parameters do not need their parameter names to match. - - ```mojo - # Define a higher-order function... - fn generator[ - func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] - ](): - pass - - # Int parameter is named "foo". - fn f0[foo: Int](): - pass - - # Int parameter is named "bar". - fn f1[bar: Int](): - pass - - fn main(): - # Both can be used as `func`! - generator[f0]() - generator[f1]() - ``` - - Stay tuned for improved function type syntax... - -- 📢 Two magic operators, named `__get_lvalue_as_address(x)` and - `__get_address_as_lvalue` convert stored LValues to and from `!kgen.pointer` - types (respectively). This is most useful when using the `Pointer[T]` - library type. The `Pointer.address_of(lvalue)` method uses the first one - internally. The second one must currently be used explicitly, and can be - used to project a pointer to a reference that you can pass around and use - as a self value, for example: - - ```mojo - # "Replacement new" SomeHeavy value into the memory pointed to by a - # Pointer[SomeHeavy]. - __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5) - ``` - - Note that `__get_address_as_lvalue` assumes that there is already a value at - the specified address, so the assignment above will run the `SomeHeavy` - destructor (if any) before reassigning over the value. - -- The `(((x)))` syntax is __mlir_op has been removed in favor of - `__get_lvalue_as_address` which solves the same problem and is more general. - -- 📢 When using a mutable `self` argument to a struct `__init__` method, it - now must be declared with `&`, like any other mutable method. This clarifies - the mutation model by making `__init__` consistent with other mutating - methods. - -- 📚 Add variadic string join function. - -- 📚 Default initialize values with 0 or null if possible. - -- 📚 Add compressed, aligned, and mask store intrinsics. - -### Week of 2023-03-20 - -- Initial `String` type is added to the standard library with some very basic - methods. - -- Add `DimList` to remove the need to use an MLIR list type throughout the - standard library. - -- 📢 The `__clone__` method for copying a value is now named `__copy__` to - better follow Python term of art. - -- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, - instead of taking it by reference. This makes it easier to write, works for - `@register_passable` types, and exposes more optimization opportunities to - the early optimizer and dataflow analysis passes. - - ```mojo - # Before: - fn __clone__(inout self) -> Self: ... - - # After: - fn __copy__(self) -> Self: ... - ``` - -- 📢 A new `@register_passable("trivial")` may be applied to structs that - have no need for a custom `__copy__` or `__del__` method, and whose state is - only made up of `@register_passable("trivial")` types. This eliminates the - need to define `__copy__` boilerplate and reduces the amount of IR generated - by the compiler for trivial types like `Int`. - -- You can now write back to attributes of structs that are produced by a - computed lvalue expression. For example `a[i].x = ..` works when `a[i]` - is produced with a `__getitem__`/`__setitem__` call. This is implemented by - performing a read of `a[i]`, updating the temporary, then doing a writeback. - -- The remaining hurdles to using non-parametric, `@register_passable` types as - parameter values have been cleared. Types like `Int` should enjoy full use as - parameter values. - -- Parameter pack inference has been added to function calls. Calls to functions - with parameter packs can now elide the pack types: - - ```mojo - fn foo[*Ts: AnyType](*args: *Ts): pass - - foo(1, 1.2, True, "hello") - ``` - - Note that the syntax for parameter packs has been changed as well. - -- 📚 Add the runtime string type. - -- 📚 Introduce the DimList struct to remove the need to use low-level MLIR - operations. - -### Week of 2023-03-13 - -- 📢 Initializers for structs now use `__init__` instead of `__new__`, - following standard practice in Python. You can write them in one of two - styles, either traditional where you mutate self: - - ```mojo - fn __init__(self, x: Int): - self.x = x - ``` - - or as a function that returns an instance: - - ```mojo - fn __init__(x: Int) -> Self: - return Self {x: x} - ``` - - Note that `@register_passable` types must use the later style. - -- 📢 The default argument convention is now the `borrowed` convention. A - "borrowed" argument is passed like a C++ `const&` so it doesn't need to - invoke the copy constructor (aka the `__clone__` method) when passing a value - to the function. There are two differences from C++ `const&`: - - 1. A future borrow checker will make sure there are no mutable - aliases with an immutable borrow. - 2. `@register_passable` values are passed directly in an SSA register (and - thus, usually in a machine register) instead of using an extra reference - wrapper. This is more efficient and is the 'right default' for - `@register_passable` values like integers and pointers. - - This also paves the way to remove the reference requirement from `__clone__` - method arguments, which will allow us to fill in more support for them. - -- Support for variadic pack arguments has been added to Mojo. You can now - write heterogeneous variadic packs like: - - ```mojo - fn foo[*Ts: AnyType](args*: Ts): pass - - foo[Int, F32, String, Bool](1, 1.5, "hello", True) - ``` - -- The `owned` argument convention has been added. This argument convention - indicates that the function takes ownership of the argument and is responsible - for managing its lifetime. - -- The `borrowed` argument convention has been added. This convention signifies - the callee gets an immutable shared reference to a value in the caller's - context. - -- 📚 Add the `getenv` function to the `OS` module to enable getting environment - variables. - -- 📚 Enable the use of dynamic strides in `NDBuffer`. - -### Week of 2023-03-06 - -- 📢 Support added for using capturing async functions as parameters. - -- 📢 Returning result parameters has been moved from `return` statements to a - new `param_return` statement. This allows returning result parameters from - throwing functions: - - ```mojo - @raises - fn foo[() -> out: Int](): - param_return[42] - raise Error() - ``` - - And returning different parameters along `@parameter if` branches: - - ```mojo - fn bar[in: Bool -> out: Int](): - @parameter - if in: - param_return[1] - else: - param_return[2] - ``` - -- 📢 Mojo now supports omitting returns at the end of functions when they would - not reachable. For instance, - - ```mojo - fn foo(cond: Bool) -> Int: - if cond: - return 0 - else: - return 1 - - fn bar() -> Int: - while True: - pass - ``` - -- String literals now support concatenation, so `"hello " "world"` is treated - the same as `"hello world"`. - -- Empty bodies on functions, structs, and control flow statements are no longer - allowed. Please use `pass` in them to explicitly mark that they are empty, - just like in Python. - -- 📢 Structs in Mojo now default to living in memory instead of being passed - around in registers. This is the right default for generality (large - structures, structures whose pointer identity matters, etc) and is a key - technology that enables the borrow model. For simple types like `Int` and - `SIMD`, they can be marked as `@register_passable`. - - Note that memory-only types currently have some limitations: they cannot be - used in generic algorithms that take and return a `!mlirtype` argument, and - they cannot be used in parameter expressions. Because of this, a lot of - types have to be marked `@register_passable` just to work around the - limitations. We expect to enable these use-cases over time. - -- 📢 Mojo now supports computed lvalues, which means you can finally assign to - subscript expressions instead of having to call `__setitem__` explicitly. - - Some details on this: Mojo allows you to define multiple `__setitem__` - overloads, but will pick the one that matches your `__getitem__` type if - present. It allows you to pass computed lvalues into inout arguments by - introducing a temporary copy of the value in question. - -- Mojo now has much better support for using register-primary struct types in - parameter expressions and as the types of parameter values. This will allow - migration of many standard library types away from using bare MLIR types like - `__mlir_type.index` and towards using `Int`. This moves us towards getting rid - of MLIR types everywhere and makes struct types first-class citizens in the - parameter system. - -- 📚 Add a `sort` function. - -- 📚 Add non-temporal store to enable cache bypass. - -## February 2023 - -### Week of 2023-02-27 - -- 📢 The `@interface`, `@implements`, and `@evaluator` trio of decorators have - been removed, replaced by the `@parameter if` and `@adaptive` features. - -- 📢 Parameter inference can now infer the type of variadic lists. - -- 📢 Memory primary types are now supported in function results. A result slot - is allocated in the caller, and the callee writes the result of the function - into that slow. This is more efficient for large types that don't fit into - registers neatly! And initializers for memory-primary types now initialize - the value in-place, instead of emitting a copy! - -- Support for `let` decls of memory primary types has been implemented. These - are constant, ready-only values of memory primary types but which are - allocated on the function stack. - -- Overload conversion resolution and parameter inference has been improved: - - 1. Inference now works with `let` decls in some scenarios that weren't - working before. - 2. Parameter bindings can now infer types into parameter expressions. This - helps resolve higher-order functions in parameter expressions. - -- 📚 Optimize floor, ceil, and ldexp on X86 hardware. - -- 📚 Implement the log math function. - -### Week of 2023-02-20 - -- 📢 A new `@__memory_primary` struct decorator has been introduced. Memory - primary types must always have an address. For instance, they are always - stack-allocated when declared in a function and their values are passed into - function calls by address instead of copy. This is in contract with register - primary types that may not have an address, and which are passed by value - in function calls. Memory-primary fields are not allowed inside - register-primary structs, because struct elements are stored in-line. - -- 📢 A new `_CompilerBuiltin` module was added. This module defines core types - and functions of the language that are referenced by the parser, and hence, is - auto-imported by all other modules. For example new types for literal values - like the boolean True/False will be included in `_CompilerBuiltin`. - -- 📢 A special `__adaptive_set` property can be accessed on a function reference - marked as `@adaptive`. The property returns the adaptive overload set of that - function. The return type is a `!kgen.variadic`. This feature is useful to - implement a generic `evaluate` function in the standard library. - -- 📢 A new built-in literal type `BoolLiteral` was added in `_CompilerBuiltin`. - It represents the literal boolean values `True` and `False`. This is the first - Mojo literal to be emitted as a standard library type! - -- 📚 Add the prefetch intrinsic to enable HW prefetching a cache line. - -- 📚 Add the InlinedFixedVector, which is optimized for small vectors and stores - values on both the stack and the heap. - -### Week of 2023-02-13 - -- Unqualified lookups of struct members apply contextual parameters. This means - for instance that you can refer to static methods without binding the - struct parameters. - - ```mojo - struct Foo[x: Int]: - @staticmethod - bar(): pass - - foo(self): - bar() # implicitly binds to Foo[x].bar() - Foo[2].bar() # explicitly bind to another parameter - ``` - -- 📢 A new `Self` type refers to the enclosing type with all parameters bound - to their current values. This is useful when working with complex parametric - types, e.g.: - - ```mojo - struct MyArray[size: Int, element_type: type]: - fn __new__() -> Self: - return Self {...} - ``` - - which is a lot nicer than having to say `MyArray[size, element_type]` over - and over again. - -- 📢 Mojo now supports an `@adaptive` decorator. This decorator will supersede - interfaces, and it represents an overloaded function that is allowed to - resolve to multiple valid candidates. In that case, the call is emitted as a - fork, resulting in multiple function candidates to search over. - - ```mojo - @adaptive - fn sort(arr: ArraySlice[Int]): - bubble_sort(arr) - - @adaptive - fn sort(arr: ArraySlice[Int]): - merge_sort(arr) - - fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]): - let arr = lhs + rhs - sort(arr) # this forks compilation, creating two instances - # of the surrounding function - ``` - -- 📢 Mojo now requires that types implement the `__clone__` special member in - order to copy them. This allows the safe definition of non-copyable types - like Atomic. Note that Mojo still doesn't implement destructors, and (due to - the absence of non-mutable references) it doesn't actually invoke the - `__clone__` member when copying a let value. As such, this forces to you as - a Mojo user to write maximal boilerplate without getting much value out of it. - - In the future, we will reduce the boilerplate with decorators, and we will - actually start using it. This will take some time to build out though. - -- 📢 A special `__mlir_region` statement was added to provide stronger - invariants around defining MLIR operation regions in Mojo. It similar syntax - to function declarations, except it there are no results and no input - conventions. - -- 📚 Implement the log math function. - -- 📚 Improve the DType struct to enable compile-time equality checks. - -- 📚 Add the Complex struct class. - -### Week of 2023-02-06 - -- 📢 The `if` statement now supports a `@parameter` decorator, which requires - its condition to be a parameter expression, but which only emits the 'True' - side of the condition to the binary, providing a "static if" functionality. - This should eliminate many uses of `@interface` that are just used to provide - different constraint on the implementations. - -- 📢 `fn main():` is now automatically exported and directly runnable by the - command-line `mojo` tool. This is a stop-gap solution to enable script-like - use cases until we have more of the language built out. - -- 🪦 The `@nodebug_inline` feature has been removed, please use - `@alwaysinline("nodebug")` for methods that must be inlined and that we don't - want to step into. - -- 📢 Python chained comparisons, ex. `a < b < c`, are now supported in Mojo. - -- 📢 Functions can now be defined with default argument values, such as - `def f(x: Int, y: Int = 5):`. The default argument value is used when callers - do not provide a value for that argument: `f(3)`, for example, uses the - default argument value of `y = 5`. - -- Unused coroutine results are now nicely diagnosed as "missing await" warnings. - -- 📚 Introduce a vectorized reduction operations to the SIMD type. - -## January 2023 - -### Week of 2023-01-30 - -- A basic Mojo language server has been added to the VS Code extension, which - parses your code as you write it, and provides warnings, errors, and fix-it - suggestions! - -- 💯 The Mojo standard library is now implicitly imported by default. - -- The coroutine lowering support was reworked and a new `Coroutine[T]` type was - implemented. Now, the result of a call to an async function MUST be wrapped in - a `Coroutine[T]`, or else memory will leak. In the future, when Mojo supports - destructors and library types as literal types, the results of async function - calls will automatically wrapped in a `Coroutine[T]`. But today, it must be - done manually. This type implements all the expected hooks, such as - `__await__`, and `get()` to retrieve the result. Typical usage: - - ```mojo - async fn add_three(a: Int, b: Int, c: Int) -> Int: - return a + b + c - - async fn call_it(): - let task: Coroutine[Int] = add_three(1, 2, 3) - print(await task) - ``` - -- ⭐️ We now diagnose unused expression values at statement context in `fn` - declarations (but not in `def`s). This catches bugs with unused values, e.g. - when you forget the parens to call a function. - -- 📢 An `@always_inline("nodebug")` function decorator can be used on functions - that need to be force inlined, but when they should not have debug info in - the result. This should be used on methods like `Int.__add__` which should - be treated as builtin. - -- 📢 The `@export` decorator now supports an explicit symbol name to export to, - for example: - - ```mojo - @export("baz") # exported as 'baz' - fn some_mojo_fn_name(): - ``` - -- 📢 🚧 Subscript syntax is now wired up to the `__getitem__` dunder method. - - This allows type authors to implement the `__getitem__` method to enable - values to be subscripted. This is an extended version of the Python semantics - (given we support overloading) that allows you to define N indices instead of - a single version that takes a tuple (also convenient because we don't have - tuples yet). - - Note that this has a very, very important limitation: subscripts are NOT - wired up to `__setitem__` yet. This means that you can read values with - `.. = v[i]` but you cannot store to them with `v[i] = ..`. For this, please - continue to call `__setitem__` directly. - -- 📢 Function calls support parameter inference. - - For calls to functions that have an insufficient number of parameters - specified at the callsite, we can now infer them from the argument list. We - do this by matching up the parallel type structure to infer what the - parameters must be. - - Note that this works left to right in the parameter list, applying explicitly - specified parameters before trying to infer new ones. This is similar to how - C++ does things, which means that you may want to reorder the list of - parameters with this in mind. For example, a `dyn_cast`-like function will be - more elegant when implemented as: - - `fn dyn_cast[DstType: type, SrcType: type](src: SrcType) -> DstType:` - - Than with the `SrcType`/`DstType` parameters flipped around. - -- 📚 Add the growable Dynamic vector struct. - -### Week of 2023-01-23 - -- Inplace operations like `+=`/`__iadd__` may now take `self` by-val if they - want to, instead of requiring it to be by-ref. -- ⭐️ Inplace operations are no longer allowed to return a non-None value. The - corresponding syntax is a statement, not an expression. - -- A new `TaskGroup` type was added to the standard library. This type can be - used to schedule multiple tasks on a multi-threaded workqueue to be executed - in parallel. An async function can `await` all the tasks at once with the - taskgroup. - -- 📢 We now support for loops! A type that defines an `__iter__` method that - returns a type that defines `__next__` and `__len__` methods is eligible to - be used in the statement `for el in X()`. Control flow exits the loop when - the length is zero. - - This means things like this now work: - - ```mojo - for item in range(start, end, step): - print(item) - ``` - -- Result parameters now have names. This is useful for referring to result - parameters in the return types of a function: - - ```mojo - fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]: - ``` - -- 📢 We now support homogeneous variadics in value argument lists, using the - standard Python `fn thing(*args: Int):` syntax! Variadics also have support - in parameter lists: - - ```mojo - fn variadic_params_and_args[*a: Int](*b: Int): - print(a[0]) - print(b[1]) - ``` - -- 📚 Add the range struct to enable `for ... range(...)` loops. - -- 📚 Introduce the unroll generator to allow one to unroll loops via a library - function. - -### Week of 2023-01-16 - -- 📢 Struct field references are now supported in parameter context, so you - can use `someInt.value` to get the underlying MLIR thing out of it. This - should allow using first-class types in parameters more widely. -- 📢 We now support "pretty" initialization syntax for structs, e.g.: - - ```mojo - struct Int: - var value: __mlir_type.index - fn __new__(value: __mlir_type.index) -> Int: - return Int {value: value} - ``` - - This eliminates the need to directly use the MLIR `lit.struct.create` op in - struct initializers. This syntax may change in the future when ownership - comes in, because we will be able to support the standard `__init__` model - then. -- 📢 It is now possible to attach regions to `__mlir_op` operations. This is - done with a hack that allows an optional `_region` attribute that lists - references to the region bodies (max 1 region right now due to lack of list - `[]` literal). -- Nested functions now parse, e.g.: - - ```mojo - fn foo(): - fn bar(): - pass - bar() - ``` - -- Python-style `async` functions should now work and the `await` expression - prefix is now supported. This provides the joy of async/await syntactic - sugar when working with asynchronous functions. This is still somewhat - dangerous to use because we don't have proper memory ownership support yet. - -- String literals are now supported. - -- Return processing is now handled by a dataflow pass inside the compiler, so - it is possible to return early out of if statements. - -- The parser now supports generating 'fixit' hints on diagnostics, and uses - them when a dictionary literal uses a colon instead of equal, e.g.: - - ```log - x.mojo:8:48: error: expected ':' in subscript slice, not '=' - return __mlir_op.`lit.struct.create`[value = 42]() - ^ - : - ``` - -- 📚 Add reduction methods which operate on buffers. - -- 📚 Add more math functions like sigmoid, sqrt, rsqrt, etc. - -- 📚 Add partial load / store which enable loads and stores that are predicated - on a condition. - -### Week of 2023-01-09 - -- The `/` and `*` markers in function signatures are now parsed and their - invariants are checked. We do not yet support keyword arguments yet though, - so they aren't very useful. -- Functions now support a new `@nodebug_inline` decorator. - (Historical note: this was later replaced with `@alwaysinline("nodebug")`). - - Many of the things at the bottom level of the Mojo stack are trivial - zero-abstraction wrappers around MLIR things, for example, the `+` - operator on Int or the `__bool__` method on Bool itself. These operators - need to be force inlined even at -O0, but they have some additional things - that we need to wrestle with: - - 1. In no case would a user actually want to step into the `__bool__` method on - Bool or the + method on Int. This would be terrible debugger QoI for - unless you're debugging Int itself. We need something like - `__always_inline__, __nodebug__` attributes that clang uses in headers - like xmmintrin.h. - - 2. Similarly, these "operators" should be treated by users as primitives: - they don't want to know about MLIR or internal implementation details of - Int. - - 3. These trivial zero abstraction things should be eliminated early in the - compiler pipeline so they don't slow down the compiler, bloating out the - call graph with trivial leaves. Such thing slows down the elaborator, - interferes with basic MLIR things like fold(), bloats out the IR, or - bloats out generated debug info. - - 4. In a parameter context, we want some of these things to get inlined so - they can be simplified by the attribute logic and play more nicely with - canonical types. This is just a nice to have thing those of us who have - to stare at generated IR. - - The solution to this is a new `@nodebug_inline` decorator. This decorator - causes the parser to force-inline the callee instead of generating a call to - it. While doing so, it gives the operations the location of the call itself - (that's the "nodebug" part) and strips out let decls that were part of the - internal implementation details. - - This is a super-power-user-feature intended for those building the standard - library itself, so it is intentionally limited in power and scope: It can - only be used on small functions, it doesn't support regions, by-ref, throws, - async, etc. - -- Separately, we now support an `@alwaysInline` decorator on functions. This - is a general decorator that works on any function, and indicates that the - function must be inlined. Unlike `@nodebug_inline`, this kind of inlining is - performed later in the compilation pipeline. - -- The `__include` hack has been removed now that we have proper import support. - -- `__mlir_op` can now get address of l-value: - - You can use magic `(((x)))` syntax in __mlir_op that forces the `x` - expression to be an lvalue, and yields its address. This provides an escape - hatch (isolated off in `__mlir_op` land) that allows unsafe access to lvalue - addresses. - -- We now support `__rlshift__` and `__rtruediv__`. - -- 📢 The parser now resolves scoped alias references. This allows us to support - things like `SomeType.someAlias`, forward substituting the value. This - unblocks use of aliases in types like `DType`. We'd like to eventually - preserve the reference in the AST, but this unblocks library development. - -- 📚 Add a `now` function and `Benchmark` struct to enable timing and - benchmarking. - -- 📚 Move more of the computation in NDBuffer from runtime to compile time if - possible (e.g. when the dimensions are known at compile time). - -### Week of 2023-01-02 - -- 📚 Added the `print` function which works on Integers and SIMD values. - -- The frontend now has a new diagnostic subsystem used by the `kgen` tool (but - not by `kgen-translate` for tests) that supports source ranges on - diagnostics. Before we'd emit an error like: - - ```log - x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' - callee(1.0+F32(2.0)) - ^ - x.lit:4:1: note: function declared here - fn callee(a: Int): - ^ - ``` - - now we produce: - - ```log - x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int' - callee(1.0+F32(2.0)) - ^ ~~~~~~~~~~~~ - x.lit:4:1: note: function declared here - fn callee(a: Int): - ^ - ``` - -- 📢 Parameter results are now supported in a proper way. They are now forward - declared with an alias declaration and then bound in a call with an arrow, - e.g.: - - ```mojo - alias a: __mlir_type.index - alias b: __mlir_type.index - idx_result_params[xyz * 2 -> a, b]() - ``` - -- Various minor issues with implicit conversions are fixed. For instances, - implicit conversions are now supported in parameter binding contexts and - `alias` declarations with explicit types. -- Doc strings are allowed on functions and structs, but they are currently - discarded by the parser. - -- 📚 Add a `print` method!!! - -- 📚 Demonstrate a naive matmul in Mojo. - -- 📚 Initial work on functions that depend on types (e.g. FPUtils, nan, inf, - etc.) - -- 📚 Allow one to query hardware properties such as simd_width, os, etc. via - TargetInfo at compile time. - -## December 2022 - -### Week of 2022-12-26 - -- 📢 You can now call functions in a parameter context! Calling a function in - a parameter context will evaluate the function at compile time. The result - can then be used as parameter values. For example, - - ```mojo - fn fma(x: Int, y: Int, z: Int) -> Int: - return a + b * c - - fn parameter_call(): - alias nelts = fma(32, 2, 16) - var x: SIMD[f32, nelts] - ``` - -- You can now disable printing of types in an `__mlir_attr` substitution by - using unary `+` expression. - -- 📢 `let` declarations are now supported in functions. `let` declarations are - local run-time constant values, which are always rvalues. They complement - 'var' decls (which are mutable lvalues) and are the normal thing to use in - most cases. They also generate less IR and are always in SSA form when - initialized. - - We will want to extend this to support 'let' decls in structs at some point - and support lazy initialized 'let' declarations (using dataflow analysis) but - that isn't supported yet. - -- 📚 Add the NDBuffer struct. - -- Happy new year. - -### Week of 2022-12-19 - -- 📚 Start of the Standard library: - 1. Added Integer and SIMD structs to bootstrap the standard library. - 2. Added very basic buffer data structure. - -- We have basic support for parsing parameter results in function calls! Result - parameters are an important Mojo metaprogramming feature. They allow functions - to return compile-time constants. - - ```mojo - fn get_preferred_simdwidthof[() -> nelts: Int](): - return[2] - - fn vectorized_function(): - get_preferred_simdwidthof[() -> nelts]() - var x: SIMD[f32, nelts] - ``` - -- Types can now be used as parameters of `!kgen.mlirtype` in many more cases. - -- MLIR operations with zero results don't need to specify `_type: []` anymore. - -- We support parsing triple quoted strings, for writing docstrings for your - functions and structs! - -- A new `__mlir_type[a,b,c]` syntax is available for substituting into MLIR - types and attributes is available, and the old placeholder approach is - removed. This approach has a few advantages beyond what placeholders do: - - 1. It's simpler. - 2. It doesn't form the intermediate result with placeholders, which - gets rejected by MLIR's semantic analysis, e.g. the complex case - couldn't be expressed before. - 3. It provides a simple way to break long attrs/types across multiple - lines. - -- We now support an `@evaluator` decorator on functions for KGEN evaluators. - This enables specifying user-defined interface evaluators when performing - search during compilation. - -- 📢 `import` syntax is now supported! - - This handles packaging imported modules into file ops, enables effective - isolation from the other decls. "import" into the desired context is just - aliasing decls, with the proper symbols references handle automatically during - IR generation. As a starting point, this doesn't handle any notion of packages - (as those haven't been sketched out enough). - -- 📢 Reversed binary operators (like `__radd__`) are now looked up and used if - the forward version (like `__add__`) doesn't work for some reason. - -- 📢 Implicit conversions are now generally available, e.g. in assign - statements, variable initializers etc. There are probably a few more places - they should work, but we can start eliminating all the extraneous explicit - casts from literals now. - -- Happy Holidays - -### Week of 2022-12-12 - -- 📢 Function overloading now works. Call resolution filters candidate list - according to the actual parameter and value argument specified at the site of - the call, diagnosing an error if none of the candidates are viable or if - multiple are viable and ambiguous. We also consider implicit conversions in - overload look: - - ```mojo - fn foo(x: Int): pass - fn foo(x: F64): pass - - foo(Int(1)) # resolves to the first overload - foo(1.0) # resolves to the second overload - foo(1) # error: both candidates viable with 1 implicit conversion! - ``` - -- The short circuiting binary `and` and `or` expressions are now supported. - -- Unary operator processing is a lot more robust, now handling the `not` - expression and `~x` on Bool. - -- 📢 The compiler now generates debug information for use with GDB/LLDB that - describes variables and functions. - -- The first version of the Mojo Visual Studio Code extension has been released! - It supports syntax highlighting for Mojo files. - -- The first version of the `Bool` type has landed in the new Mojo standard - library! - -- 📢 Implicit conversions are now supported in return statements. - -### Week of 2022-12-05 - -- "Discard" patterns are now supported, e.g. `_ = foo()` - -- We now support implicit conversions in function call arguments, e.g. - converting an `index` value to `Int` automatically. This eliminates a bunch - of casts, e.g. the need to say F32(1.0) everywhere. - - This is limited for a few reasons that will be improved later: - 1. We don't support overloading, so lots of types aren't convertible - from all the things they should be, e.g. you can't pass "1" to - something that expects F32, because F32 can't be created from index. - 2. This doesn't "check to see if we can invoke `__new__`" it force applies - it on a mismatch, which leads to poor QoI. - 3. This doesn't fix things that need radd. - -## November 2022 - -### Week of 2022-11-28 - -- 📢 We support the `True` and `False` keywords as expressions. - -- 📢 A new `alias` declaration is supported which allows defining local - parameter values. This will eventually subsume type aliases and other - things as it gets built out. - -- 📢 We now have end-to-end execution of Mojo files using the `kgen` tool! - Functions exported with `@export` can be executed. - -- 📢 We have try-except-else and `raise` statements and implicit error - propagation! The error semantics are that `def` can raise by default, but `fn` - must explicitly declare raising with a `@raises` decorator. Stub out basic - `Error` type. - -- The `&` sigil for by-ref arguments is now specified after the identifier. - Postfix works better for ref and move operators on the expression - side because it chains an mentally associates correctly: - `thing.method().result^`. We don't do that yet, but align param - decl syntax to it so that things won't be odd looking when we do. - In practice this looks like: - - ```mojo - def mutate_argument(a&: index): - a = 25 - ``` - -### Week of 2022-11-21 - -- 📢 The magic `index` type is gone. Long live `__mlir_type.index`. - -- Implement parameter substitution into parametric `__mlir_type` decls. This - allows us to define parametric opaque MLIR types with exposed parameters using - a new "placeholder" attribute. This allows us to expose the power of the KGEN - type parametric system directly into Mojo. - -- 📢 Fully-parametric custom types can now be defined and work in Mojo, bringing - together a lot of the recent work. We can write the SIMD type directly as a - wrapper around the KGEN type, for example: - - ```mojo - struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]: - var value: - __mlir_type.`!pop.simd<#lit, - #lit>`[nelts, dt] - - fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]: - return __mlir_op.`pop.add`(self.value, rhs.value) - ``` - -### Week of 2022-11-14 - -- 📢 Implement a magic `__mlir_type` declaration that can be used to access any - MLIR type. E.g. `__mlir_type.f64`. - -- 📢 Add an `fn` declaration. These are like `def` declarations, but are more - strict in a few ways: they require type annotations on arguments, don't allow - implicit variable declarations in their body, and make their arguments rvalues - instead of lvalues. - -- Implemented Swift-style backtick identifiers, which are useful for code - migration where names may collide with new keywords. - -- 📢 A new `__include` directive has been added that performs source-level - textual includes. This is temporary until we have an `import` model. - -- Implement IR generation for arithmetic operators like `+` and `*` in terms - of the `__add__` and `__mul__` methods. - -- 📢 Added support for `break` and `continue` statements, as well as early - returns inside loops and conditionals! - -- 📢 Implemented augmented assignment operators, like `+=` and `@=`. - -- 📢 Mojo now has access to generating any MLIR operations (without regions) - with a new `__mlir_op` magic declaration. We can start to build out the - language's builtin types with this: - - ```mojo - struct Int: - var value: __mlir_type.index - - fn __add__(self, rhs: Int) -> Int: - return __mlir_op.`index.add`(self.value, rhs.value) - ``` - - Attributes can be attached to the declaration with subscript `[]` syntax, - and an explicit result type can be specified with a special `_type` attribute - if it cannot be inferred. Attributes can be accessed via the `__mlir_attr` - magic decl: - - ```mojo - __mlir_op.`index.cmp`[ - _type: __mlir_type.i1, - pred: __mlir_attr.`#index` - ](lhs, rhs) - ``` - -- Improved diagnostics emissions with ranges! Now errors highlight the whole - section of code and not just the first character. - -### Week of 2022-11-07 - -- Implemented the `@interface` and `@implements` decorators, which provide - access to KGEN generator interfaces. A function marked as an `@interface` - has no body, but it can be implemented by multiple other functions. - - ```mojo - @interface - def add(lhs: index, rhs: index): - - @implements(add) - def normal_add(lhs: index, rhs: index) -> index: - return lhs + rhs - - @implements(add) - def slow_add(lhs: index, rhs: index) -> index: - wait(1000) - return normal_add(lhs, rhs) - ``` - -- 📢 Support for static struct methods and initializer syntax has been added. - Initializing a struct with `Foo()` calls an implicitly static `__new__` - method. This method should be used instead of `__init__` inside structs. - - ```mojo - struct Foo: - var value: index - - def __new__() -> Foo: - var result: Foo - result.value = Foo.return_a_number() # static method! - return result - - @staticmethod - def return_a_number() -> index: - return 42 - ``` - -- 📢 Full by-ref argument support. It's now possible to define in-place - operators like `__iadd__` and functions like `swap(x, y)` correctly. - -- 📢 Implemented support for field extract from rvalues, like `x.value` where - `x` is not an lvalue (`var` declaration or by-ref function argument). - -## October 2022 - -### Week of 2022-10-31 - -- Revised `return` handling so that a return statement with no expression is - syntax sugar for `return None`. This enables early exits in functions that - implicitly return `None` to be cleaner: - - ```mojo - def just_return(): - return - ``` - -- Added support for parsing more expressions: if-else, bitwise operators, - shift operators, comparisons, floor division, remainder, and matmul. - -- 📢 The type of the `self` argument can now be omitted on member methods. - -### Week of 2022-10-24 - -- Added parser support for right-associativity and unary ops, like the power - operator `a ** b ** c` and negation operator `-a`. - -- Add support for `&expr` in Mojo, which allows denoting a by-ref argument in - functions. This is required because the `self` type of a struct method is - implicitly a pointer. - -- Implemented support for parametric function declarations, such as: - - ```mojo - struct SIMD[dt: DType, width: index]: - fn struct_method(self: &SIMD[dt, width]): - pass - - def fancy_add[dt: DType, width: index]( - lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index: - return width - ``` - -### Week of 2022-10-17 - -- Added explicit variable declarations with `var`, for declaring variables both - inside functions and structs, with support for type references. Added `index` - as a temporary built-in type. - - ```mojo - def foo(lhs: index, rhs: index) -> index: - var result: index = lhs + rhs - return result - ``` - -- Implemented support for parsing struct declarations and references to type - declarations in functions! In `def`, the type can be omitted to signal an - object type. - - ```mojo - struct Foo: - var member: index - - def bar(x: Foo, obj) -> index: - return x.member - ``` - -- Implemented parser support for `if` statements and `while` loops! - - ```mojo - def if_stmt(c: index, a: index, b: index) -> index: - var result: index = 0 - if c: - result = a - else: - result = b - return result - - def while_stmt(init: index): - while init > 1: - init = init - 1 - ``` - -- Significantly improved error emission and handling, allowing the parser to - emit multiple errors while parsing a file. - -### Week of 2022-10-10 - -- Added support for parsing integer, float, and string literals. - -- Implemented parser support for function input parameters and results. You can - now write parametric functions like, - - ```mojo - def foo[param: Int](arg: Int) -> Int: - result = param + arg - return result - ``` - -### Week of 2022-10-03 - -- Added some basic parser scaffolding and initial parser productions, including - trivial expressions and assignment parser productions. -- Implemented basic scope handling and function IR generation, with support for - forward declarations. Simple functions like, - - ```mojo - def foo(x: Int): - ``` - - Now parse! But all argument types are hard-coded to the MLIR `index` type. - -- Added IR emission for simple arithmetic expressions on builtin types, like - `x + y`. - -## September 2022 - -### Week of 2022-09-26 - -- Mojo's first patch to add a lexer was Sep 27, 2022. - -- Settled on `[]` for Mojo generics instead of `<>`. Square brackets are - consistent with Python generics and don't have the less than ambiguity - other languages have. From 30b612b6d4aa3cda6014755942419aab6be36a46 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 26 Mar 2024 19:42:17 -0600 Subject: [PATCH 0026/2019] [mojo-stdlib] Fix `test_python_error_handling.mojo` test for Python 3.12 (#35897) On Python 3.12, the error message from CPython is: "Can't instantiate abstract class AbstractPerson without an implementation for abstract method 'method'". Essentially, the CPython error changed at Python 3.12 a bit compared to the existing `CHECK`. Just loosen the `CHECK` string matching a bit so it works on all CPython versions, including Python 3.12. Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 656430e59071d2d619f72d7bf37b95699f6656ee --- stdlib/test/python/test_python_error_handling.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index c237deb207..cc3af1af9e 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -65,5 +65,5 @@ def main(): test_python_exception_getattr() # CHECK: list index out of range test_python_exception_getitem() - # CHECK: Can't instantiate abstract class AbstractPerson with abstract method{{s?}} method + # CHECK: Can't instantiate abstract class AbstractPerson test_python_exception_call() From c0d4a839e6004127487fac86f614561be510e60b Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 26 Mar 2024 21:38:55 -0500 Subject: [PATCH 0027/2019] [Docs] Update CONTRIBUTING.md with nightly branching (#35829) Adds a description for how to fork off the nightly branch, now that it's a requirement MODULAR_ORIG_COMMIT_REV_ID: 73c043191a373caf1777c5f695234bc14b92b41d --- CONTRIBUTING.md | 62 +++++++++++++++++++++-- docs/oss-material/images/base-branch.png | Bin 0 -> 163923 bytes docs/oss-material/images/create-fork.png | Bin 0 -> 179367 bytes 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 docs/oss-material/images/base-branch.png create mode 100644 docs/oss-material/images/create-fork.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4066fd4f9e..113bed1cf0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,14 +164,14 @@ which represents the most recent nightly build. ### Pull request process -#### 1. First-time checklist +#### First-time checklist Before you start your first pull request, please complete this checklist: - Read this entire contributor guide. - Read the [Code of Conduct](./CODE_OF_CONDUCT.md). -#### 2. Evaluate and get buy-in on the change +#### Evaluate and get buy-in on the change We want to be sure that you spend your time efficiently and prepare changes that aren’t controversial and get stuck in long rounds of reviews. See the sections @@ -179,11 +179,65 @@ on [Contributing to Docs and Examples](#contributing-to-docs-and-examples) and [Contributing to the standard library](#contributing-to-the-standard-library) for more details. -#### 3. Create a pull request +#### Fork and clone the repo + +Go to the [Mojo repo](https://github.com/modularml/mojo) and click the fork +button: + +![Create Fork](./images/create-fork.png) + +Clone your forked repo locally with the command: + +```bash +git clone git@github.com:[your-username]/mojo.git +cd mojo +``` + +Add the upstream remote and fetch it: + +```bash +git remote add upstream git@github.com:modularml/mojo.git +git fetch upstream +``` + +Branch off `nightly` to work on your PR: + +```bash +git checkout upstream/nightly +git checkout -b my-fix-pr +``` + +Before raising a PR make sure you've synched the latest changes: + +```bash +git fetch upstream +git rebase upstream/nightly +``` + +#### Create a pull request If your change is one of the improvements described above or it has been discussed and agreed upon by the project maintainers, please create a pull -request into the `nightly` branch and include the following: +request into the `nightly` branch. + +First push your changes: + +```bash +git push -u [your-username] my-fix-pr +``` + +You'll see a link to create a PR: + +```plaintext +remote: Create a pull request for 'my-fix-pr' on GitHub by visiting: +remote: https://github.com/jackos/mojo/pull/new/my-fix-pr +``` + +Make sure you point it to the `nightly` branch: + +![Base Branch](images/base-branch.png) + +Now fill out the details: - A short commit title describing the change. - A detailed commit description that includes rationalization for the change diff --git a/docs/oss-material/images/base-branch.png b/docs/oss-material/images/base-branch.png new file mode 100644 index 0000000000000000000000000000000000000000..a65ba47490281d93c0d5f6ce695f05bacd80c3cd GIT binary patch literal 163923 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 Q0tq3FvBRFMvGGm$52szTO#lD@ literal 0 HcmV?d00001 diff --git a/docs/oss-material/images/create-fork.png b/docs/oss-material/images/create-fork.png new file mode 100644 index 0000000000000000000000000000000000000000..a708e4a4e459b15eab96c8c6d2c622f5b95adc93 GIT binary patch literal 179367 zcmeFZ^;?wP8a9jyN=S*cgrv0O(1@gTNSD&xor($)10vl@DkXTgu8u2R~r>JH|v9|HD!T(|Eu ziK3uTqr8w5f9aXB0r%0@JG|`Jm3UMB3FQ{q?d+I`a@p_Yx6ww_Dm<+k%J=;f?bieg zt5#jz_W2xK;O3WJ>Gp!Df*hkdZiPyDm2OARSD|_wF?uEXJt7!*mV4*4+*mK(z&fGh z`-5Viqg@)8C5mFUh0+~5Wjc|kzj|&^y+IEueUz-Zg}KGI(imCQYR7o zzawY9+G)r=y{KE)jXfu)O}wW7gsoAJ9%^%S2;%HF#h*MqdWG( z!y{_QXIfkcuR3RDBs}5Jhz`gP9pOgA08FyW6eDDqjc?7hwy6IoZL8?9*lOP5i^pHT zMyYb?xE5%$R#tysZQb1}VlRG4`0Az3WW1phl$m9E`oqKzi}C&eh@YCO=Gx}-zjyI% zNDa9RSTvik7CyQqH_t z?CXUd-Wh|BPEL*RbCTl;Fls-16#tZ0j>xWYFxN27ztw1n4=IcIJ44vV8uD>4LsBrq zZ@#BDGh7EVRCY8ycvzgmFUJs;F4*TQ@3IgQUD$W`T?@Nix`)j`P2h^QZwH| z?-J$!r%8?RqM8LtGr8`Gy0g4is~2b%gh8Qgu}fx%BDBBPi3iu=K-#zC4rnN#HXc#_ zT<>oH3k>n#f`kmmg1euSKLZ)b_o)>zlBOQSzOP-+%J|`fn}@q+3EfO?cenrI?hHq7 zA3<0fqNq*7xI{ZPvEiV=?C`L{u8{42)?)##BLuF40j`55dU$i4O>mtcVlGI@9{>Jf zR}#7Mr%%pU@a8vO1bM%ItGs))GF*!IWH&xjeP&UGV2_e(F!(6+B5NqHEdE2VJkHk4 z`ljyRZSn^P-RUO`I=!pk_I{pr`*HnNJ4U3#s~uM)aB!|u2c$XqaD!Avy<`!cXRp?` zx3LJr7?p9j4P#}q;06@l4Tw68Q!=lV`KXR@w(h=t_s0unV^*tJ#7h*gCawIrD))b{ z)J&34N2=8euf_-KcxHHGSCAsGjyIjZmctThAO@6VWY%`p2adOH6<2&8^i7293q1;? zX6^0MtGYt_vlZOnrS_16^*&7huRm!v0K+w_cB%H9EN%?n2v!y zxu}v4a(Flx{HyQ&-FJq?5VtRwSXkQ|mJpJU@>qe?`T+}T>97Nr677x6=fdKwM9b-_ z&4TLxPWz4OQ;&sPV0<(%zP6$7&2hLT+<4WW>1R*V7k6uT)fF7EG~x5s!O_X0;Z+5# zzU`wB+dc}9;%d+As{EaDLgHe5mkv0h=&!4o%}l7p)2w;CTOTU-~Z?v;)kL zCM`ctp3BJ>fg4Z?9ip209|wK-5F8ZbT|%<`S(Xl0tWhcP z^Ze%X`WK0x_jVT6KmI4y^nzpi0xUUjnR)i~7|+lnt#x6&NjSa+1CqSGJ!~Cx_nwIR z8iu8%;}7NYri!tjjMfuB6L=G6-;&dA-DGMvk2h%G3<58uwWE(Fe0?*q=zk{`-S=Eg z7dmCKocg18+jL(jVE&UVT@}I~mx+J?Y;a-Kj6@arpFvRG;JZL5+hNdQ&!XFb9XDRp zizKo*eNP`LEo@b~>TDFy)X^{WI&r28>2ZbH@>z|qr1Rmw-)(+hLyns~>jEj7t-(a6 z{Ku2M7H;(KewUc6QnI=?ixN3m&D|8;w|#*T*xV-@UPA7mb?e_R?}&ow+XfE&a~G&R zFK+x-@b1YO(}0BSGbCdbsVNeXg z}-4~j?=*RVhz{+cHwp(j% z9kZXQelBP9f9ysPc;$)rsdY{i0~*hEPC!r=!d=)sRF|~GS0O?o*_teG3=X%4Lsv&f zwbEi-$$|K<&7stD7{uDu`c`L*gvEkHljGxW8b{z%!EKKO-(kFY6;_)2@4UhMcf<~A z#AmCIuY&2+LNaAE;^OTt&BHV^HJR0OTA5xZPVQA#?>-sWoYK`>x5;0(wpm-VE+0K<4Dg}i%fJ}6m}MO`h0f2VKU=zIQmeAJfm z?$5$XB~Kw9ryuftxqXMnrfMyYBkX2uIpgd=^wV{8|C-4QzGGi)b(mb-ur@~aceB9Y z=#HlLc<3N-g4M6KzO^eXBVQ+ohw@H@9KwcA{t`~5= zmTrIP9pQUbSAvLH+N#Um0f%U|%4Xvs>mS))Y`hocw`rBU~IaKPqFIA8x>hz~}T7k)vBLXa@wWN+VHe8Fzz@Y%&s+()@ zND+GU-vQr*fNJ0(Sy;)b5|*nBg%?q>S#u9U)DpSqmGFskeqmRX zUhTLvR$NtHQOWPb`riwq5MU>gpHz|C>$644r3h)}s|r1|z(;*OP?TB~A8QyeJV4CW zU&zXlpI`XB;E78{oq~ZP)au!=v{woZEsKyuY<73=nB@qESQDZ=`4XhQT&NaYkO$uv zgUGe6SC1ow{`X>@hTpVVwdgvHjZI4@R7$jIV&$^%bM$!E457Lr--g}XDm>g=%PV+5 z!fjYU^fnmWk_2WJpV#(j!@=XDV}edG(wSbTHHREReq&J;6_vKI#&z@6KhlRXIb8Gi^`0{@nqOqHI=d{hp|GZ=@yJq3CYTXZ z>I<#Q9WnP+!g0?!)bz~3JOhD?A(o>5E!mpyeF$yq9w{$(jrHOt50>m`mF{Tm?^dxI zY0wB}SGwLoTe-96plR6mTXmJc_;Dus`Rs?OFP!d3M3WMBDS^L%bn9y10bI zyY%|&2*ZHxX+pH>WQUrvB|I#b=IvFvwpDng@=2o(FyYs4EF*S}AT|AJQ zC-o$)`QQ3f`T;Xh4SI1<&tqKD^cy)aV8j6n2G*Q0zlPHUgBVTQT3Q z1hAapv6Y;RL}40}`$I$AxN#eK`9;p>CnO>IQC}1iNeKyT^eGfFx9n`}^YR78#)cMG zRC1c3TAym(8fA3#$$qxzb#y}2g`|Dx!_HE3YF5T-rCjuie9u{UOSFB@hJI$o65c!Ba?bXg zc*q_bL0QfDAgm4T14}5MpRjioxe+=H{Z>j%S7LfHzhAADm2!H9>hajSPaJ{c(g|&}|f#Yd!5yv&uhqOpP8$4er?~ zT935ui*8lp63_lX*!SVj9=6lZf03VXEa>M_qxUi!8|x-`qU>+%=?UCHZfHU| zUlSPmH^G;WwdlT;E_cglms)EE$+6pdONkZXp!G?s!rIkBv#4Qop+$clBy_fP5)^pe8EfXn@` zn}2_^mFA$q<=(q)cNobc(k;KOuWkG=G@#LEj+N@5V_+hP&It}>)S+T03X4yxtM}|U zI$L9gjAMs$hyAm*udKa7!y{ZXurKysGF+Q{;&E(kteB_DIqfQ~`vdjY^$2nqIeLYn z?IBwk3W{Y2&o8F&POnkTx>+^)_17f|eraT0?>U}EI&d{jO%5X9gZ}g93($qii|4P* z?E_KYRmuO5`Ba6qBTuYfJp|2cYpY>vT){2#>~*aL7yRhUDFct@g7 zRmo+qO8Rgh1{9kd>|o zP$}(sn8C$>#byi{3+A7iV-RHE6J*d7aEX(K`+PCgGWq2cyiO_qs))_)Kqb3Mo1{HV z(x7Vjs!8&?LUv#OqC_iO!FW{pM}?8mM--QzzP}hwHB^(Mf%5xVz3%rV$A;=ZOz^W0 zCw*dOozy2;Hf}YtR=dWm+bkd+8w*kg81GcN|Fxu^O;{Z`+#DiR{%~%N+Uz-~Bs0j? zv|?gwMIWMoL#_3sHJSk<@Hc$)tLCw1-J@x)GG|bwMe(hdw`OH+ZEZzF+sWWkogvrR z7;Vwgr7~V=xF022uq&$de+&r;`4~QA%`0MH&=En&BvKtP9&vpi8(Tp2oh+SNPF8%1#^$?Tpy?uL!bZOYfXh0Dt~uuTzb9}9R& z+lKDP7ySkIHB>&eG(f{{bKm$AvsKaOY;PGQVwZ}(f~<{`dYFz3id1bu|7xKHx(67;_ zq>m1fXtQ>6aFO4i3*9T!YqYhtb}fH1J3Bi`nz&EQrlgomA1x)*m4PO{vbDCcwpNf| zgwRvdS2Yym;a`C%$A1=d*~&nco8_DzILD^1hWxJ?G%S)x*tQlR}{Wa6Kbw&1CFcIZ4>->StTc zsfB`K3K4zu3p%wz&0(j##X;I&_lxD}Zs#W7*9;6yP~!U1A0wL{Gd|`mlVj7{^-DQ% zNCt5{WWUkQLOLwPGEO7DXJK)#de}UlUDs{0<d>^ClsbTy`RUUqvt>3Tw@E^-eDjcN3LQN2e6Dz-a%W3R%jt1F zQSWQ2%}qW;Dl5#l$L_;&@ejlmC?i;UdfJ7Bt-SX}$Kg*sT41bMh7XHmocUq*@>{aIopy9fwZ7GBFBAYvk8Y4g6-cJg9`cCGp;qGJVf?sAHjb z%Q@|L3O(VM^7Zy^C)6@J9=x~he{dzHP}ceQ@nZmn9@57fkP7?W)g_{!4U2f8JW%a9 zYGy=yc@7;nEZL7oiF$I9;xf=QpR`Uxo5^aQ^eFwyM`eCCk%f9fVylXz0RJ7t^7UEk zS0rg}_CVwAaut(BR)WLBFh(g!X$BUHUZa@vGClk2@mwkg&tp5GU^afA$t0m|PJ>H$ zCN6-o#?$U>pDj&Ya9<~@h-Qx)?Pp?bUOkq6`0yc5fa+ExdlT2_*jk#ZvD3>KRwvz| z4A@0aaqZE0(89In{$T@sTV|vcW&cFQg4n>toJV^PabfL7cYZO};H07S3 zmFMW~53quQn9Qp62bwoDDr}LI3D^<8zRoV}%#APeRio|Nf$72r0&F)!wpO(tvIh&~ z0q!jy;=@ao`(dX6p2#*a;nS6pJX(N+dAE(m8@Ls^-EGsi-H564ui>M2Lry!?n=6wF zxGY}-RLi->0NM>;t}Q`-d@s`~Rv!{VytCo+mjJge=aHgPF#{VQVMU(A8k4?NBQ0BX0kXAm25M!U+jemQ_-8v4l%#gYfxTcDT3dn==wq{O$s#diPEI1#zFX zl7#9uu?fV5Xt9>l#bo?G<}4Q7gAguyvyID*A9I&)x}&tuL77`l2H}D9bh_%9JT|J_ zsK-jvu0qf_nkdmdFz_F(@PPXSsHl9RMlW4inYrA(_IGU?ZJ||u9wwP#wrsGI9886U zgF{({QbTifyq;cn8aG{V2SW7o7ybClyIxnvT>39i~0It8G(hsHZSF?-NcWbvf_AwQW-*9r=tc<`ZoC0C7;s1p24Zo zp{ZPLUybWo1l{F4t0nPJ3w1Zu*?gsF2JJj%Al|d>PvjWt&b69AHNalKD5IsnZz7Z~ zMO;`vVjf9~1q`S9`s>UTX*DMX2DX9Qsm3wldN!*onnLh5<<0#NJq^xYekge64XCWW z74?85?trh;|AxI=)S${qE-an+rkW2I%C}D89MxvK{h3d1X(jyx1!aNlC2OUjqoWGs zhehtPAnd+ag?&dQ&H5(TuD!?ep@MoL^cqL?g!x7KC2MjZp%TLVUR=bCDDlfGv-Mo( z9d1&jYPyDx(X7whqZ|78-F%(0`XeazrKL41_1dj$?BGYSP>-%>8H1L50Y@QvL0s&_ z51Mc6G#&0OBWt;m;Db!{N1_oMKETI6{cWO5A4&F_v#YbW@gOu`W`>c|>TE&zaTU4myE+x=mVCQh#J@ zXk^TGvdX;tGC8^697Y1Atm7Qa8@&kxATyz;d!e8D0ph>P-myri(z8c?$is3;PCGu!}c|oMXFnz z>COuS&_v`1+K94%f(a1`>%eD7@1`D4p zvh-apl_}Vi8N#kc?=#tb`35Z%x9-#>fZ>c|~+;4mN)3td{lRCKh)0cJ}76a=4W96+gY)a?bdQ$p4_- zon}G{5y2Fq^+5jo`4ekE?`k)Ad%FIx{9tk%=9rmjqKp06>Cg>Qe~DaYk1zpY%XOM# zE4G74pHz|#(-80zO||EIO?s$*;G6Jtl@fk|(NZ(F(XV?^<|8+j@Y3y@u-(y*)4}q| zyu4pahE^#kC_jjpiS&-e*4HeH1z*m^dMo7NXgpCLVvS=0wSm;Vqb*(CYT=i{R=pzk z-0rjq$;gjRGInDxlG+Jtc6efuRV(^YGZW{80U}KXHD4ds zi%Vo6GGMQE-S|hihhX+B?`-v9pMrxDCcv!FI!77u*Wx!S92T zZbRtnJQ@!!xN}spc0V~Zo;mkX94B}ff>SZj**&ReoA{o$7R1Z2W<@ivRTd7>X&BE& zKmz%7&0{|I?mnBX1dB>dd2Q{9f$i$^kl+A2m&KdZuX@LLpUu1BY9?9id=_4%D3&vr z^&(mmL=1Yd+TIBh+J5D@csbbw>p|R{#!BR&mZ6|;^^o`SxS^lbWDT+YadfhR{NhK+ z7Wsa`Wq8xIT`xfhqhjrTn$d^%7sm^wnU{-4g+^2&&egl;L04D)6}28&cLt1{N_Jgm zAdif$gJ%-=MC{o5G-PlbF7sA0G~x~S7hbDUs?Q=Nf9)OyoWPI89t};wUb-#rK=c~* zy(VL|4EIg;C5TwN_rrP>B~4#uDKMb_YbDN9gJ{S%PWy~-!CLqY29KMpn zdw+Fxwcu9q?pfz4jLCwk{84u|`Sn2Zinp4c$2xRH~HE8j2Z7 z4BH<_5dO)fMqE^X(A?Mn%ArjrA6F1aJhv{;XyH?Ec*VZhbd#9BKIB_;2NMx{Ke^SR zpp|6nW>fX{;Jxu$!+(>4`yh^oDcuFgN9fSyf=4^}AAGW3BT}L*%*#}R7}F(0K^Z8! zvcyB3d)8%N{LlovY-KOALuP1*!eYN$84lbzEiQ_PD3nU>2#4QayIVS&*TkIS%^X+Sx(+c$1l(O6Ne zeBO;UU~XzIB`vK3I{;qG`*JkC02k2B93o9u0;4v-?dCwOtjcW7?Lmb&AY?LO(Og+v zRey284H#|j<)fqMb;^LnGjgpPGSJFI)4iWmKJLomTr({7U0|TU|8y!%na+i}_a%3L z=a6|%#@n}Vj}iX+D_^^Ps60)xLISADpIp1{Q~fB*y!_rlwYp(hRkQyr;5tWH4A7@_K`M5wr3k7-_?&ESqkT^beG8UOD9(7GE!B zVkhbdj45j7MN;@3ysfx%hY#m;`QCQFrkUAtFQ=VCs@Z8F^-K)^WfPV>FW(~w$pM0` zJ%?h(-sh5o%-A+kz z*9BSBVg5PD%Bxm#&4xxi=T8p<3PjyUd>3Y}kKzFu4L5%+E-oH#=vA?vnEdJYZ;f~Z zt9^!dEjz1XVi(|$1qB5ukc;_lr%KhF=8wZj@ky=xWTnmHsN78cRN4z{0&ULp>zTpK z9Tj@bz^wpCgxR9#w7uMwFp(T(2j6NnPSIXkN44`;xXt6P1%h5kPIi7LtokQOT_U&G z_!Fie=pFY&IvaZ(f^J|R972<0 zM~G>M2-keg{F~gDh=d`-c1u}a4Kv#e+Q!r}Gz1vf;232OS){x6GkLkmp6Q2idAJcSJh@N2r6W=Yi6 z)os<;^dUs*`i#o1CmRnrIK&)Wu0iGcW5+QJ+CnpkMqF546QHiXK-<(fKy!n7)>a)) zie36=1=*1%PUhIZP|_H zMlHI&UECxn*C>wn9)Oo&Yz_@q25ePn&dA-FNainn?Ui#qlaH&iAL*OiZ?r zw5+=C9>JO%dL@)r50PTCvRDTb2dnLoI2$Y_0RfpA1Ca4?UD1Hv(ed%M!(T~_zK2W8 zyqThyZWW`*f#@Gk$p_et2#6>txy?eU(E(?}rU*r_cc>{VAB#ciZ5j-U#SWK<`TVE3 zr*^i4;BbSnX%GKnJCArMm_4{z)6Kn!O)Bh>Wp37&F3QDa0*%z@ci+v8#-~?lzwI}j z^y;y*uQHlDWwEuN(W|!^&Lx)-%qyxyZWmv+Ar<$DSVN%|@SZ>5zrqvee7DUy!Kscz zpJi#?irStvET6G96ck})tI_L%{7kq^5{#sxf=4Idqjo$t*RG)(e@;bnJT|cE) znmeO*tKcPrZaU1}%jWyJwt{4^Y1rW_ykIU*9KZhRj^#X$xER*P|izM1NT47)JvKwj6Z4F*!`^ zdo`I`+;sMNccy1R%%lM`Q}NE7%n#Yp8N=AVeDxktsg?eAb)+F(UzElX{>~GRjqFG- zKmDVKDpY#7Af=v5kM$hXc#4cPY*3-bx zZPY_AtZ`bf9R~A6-@RbE#n?KzhC2IX6BX;`*iIpXYP_mor;Xoh*H#iRQ2KU?bw)I) z$X(nXS5VCK$_lZ;FV;XB5$W}@t4gm)plsH5cywegpEWxt8|~h`QNOpn>!9Evk#u(5H*#$HC&Buf|axd{%gD?<^B z?XrcM=i@e9PTr-vKQYb&%_zq_=g*_;$(XW6hhVPOp6@c|WT0b$Juul9F~y-*BFGXS z_st)t){3$TWc1O{5gUt*&sLnY)wQ%1S_n>23MWjEurpS02M7 zsEpO83MP?SERVdLDf6B{A~E|?czZ6!bXI>!Bbq!SFKKZBTKqC3K)|GH=mgcmx#K7} zuGj6@mHhtQV4T&>&BGSHr|g)7VGHvFsN%oZ4;5*1_3FQ6XHVV<)&^p!SZ-G0AOch% zG`n^*J52}%qs3B|H)JKXCBKnioqxk{emr@oR9c)h;r=35!06v(h-6OFh>C0k+Z#Ee z3tc*oYU$S2b!Ejz zDnxho+6sQKHDK;5wF|n70iLcwEm87D4$p zpUcqBG*5_u5z7C8RgmGylg_p%pW0OYoO^+%Kh+w?v4`pah;3)@P*EqQV^g*%!iGS7 z^x*YjeDQT!Ph%|Rp|_upUjAv_FfRS%)$2c8B}y}ciD(oCkLt&X={)`=Qxh>HWP3g+!ihK<&(?~ zTAVch5UI*fQ8nVW_TAb1CDtwjT3+!xFRGNY>f)!*u7n6z=DE(Q zDk$Cu+z9H|OH&&$udq33>t<8D`|c61ey5k#z|R7hj>t=x-@!FVZ8^dT$(R}bP_@md zxvKeWwYNJ7G17Dl<%)h>9G5W(KUp>v?_gyYdT zt@9q2K{07#1%3X-FVRl7+FQeBVL7bPjEY-?-Wyb2DY|Xd20vt72c~9NwNoHNI3SP} z_9-eVLIL+WK0Zn*`f?CHdbl1;PV<}R(DKu*>8 z+xYsW5|PSC?0DHx+K0Bv8h77`XS!bn$o;di)K4u(8yd?V1jmVKzPdQ=^v!M>kga`C z_tKcZC<^e!)QMGMq@h{xd_S|M*n~MsKg;C-@BhaUOo7JWe%Wm`A7S zo9}LR8dz_b4*ux(WrrDOGl#sK8XL2|?i|{^P%eJLKVcvN_Z}Akne3X0%yI07fcMGo z-Lt8pekG4otbPp42a2hDaL<~kUTAF8XU?N4qOeq&Dv>?(8d9Hwh@B3G8sVY}uZ`^# zG&Hof)~OSRAk9pH>?-*6-9KQMDG;R$9MnX$wR+C7zClY*EIv3VeX(o|LA7T{QU&Qv z)aMp{F!8=YiP*f98bp!teYgJh6_gX9Fn?U9h+>2pCrdG({CHk4qa8tx5>Ppn1S~Q+ zEmX%O=#3Q+qCVa%Kj6md=bLd}bX>Z(U9Vy2{+nuzD{2hSloalG0(!4d0ab?Ucy zRk>Ql<6)9CVVm7CFM%YhiuNlPci&N!sdsH%N>N^2&rt~z_b~H@ryo5qQ`{>S_cyUeP*|TFQG^#@Y~+h1aiMeaXDcTD8{q4U}Nhka+(5^XJ%{ zd;nI7z^{`BL=BBB8eOKWas^)Mt@S!>xRFv&Y{og=Ajn9q$VYje8!hJ;u#==#y6oNC zSn`uu!`LI^qq+{bSXlA{BtFpP9mlSeQ!HD=OTUe8!x{iTn?N3U(WdnLT(uyYVE^8I z`uU$FJ!`hCu(smnWfFMFO1ZrcB-iQz+IQfs+?Q@oTfF=lxth)C*7Ng2H^T<;suL4{ z48;I;%TejoQ4Ki2V0F#D@0$%`xEWy2<-54efhuc4)9s*hzMd&3A4uy!E>gZf0Z=rF z=U2yD1_o}D#bRduqrS@m`$QL)hfNa#&Y)yv=AQ0Pr643C;^TEn(hKVD?B!}Y3kcAK z@bH)pylr{>)t(9EC_(VDDWH>oP-S`ax$pSb&{EP$O%!39NiU$A%cSx_gofF=$AD_x z!QS}_S6T)~gXDhD7wR7KvN8=FK*eT8J|ZHzAz>!!zh~5sto~>9w*2-DPS$G9m;8`9 zP}kj5YF)i7!Y!+(-8})m(QR?q4h_hN(2HadxIH@T1Rttvx0`+7`2FR&twLc@j&=XH zd>IOn4PT22$9#@~bK7%VPeX8b#Alr>ol>GEQqu0*es;W9n;UN0``w6tZe>masTI#E z(s5-HpH^WT^L1_A`o_VG0=^Z{WR)(91sf?$&NY{@B#LRmlSM5qNL`N1h4(K2=qW9o zSX5b2nE=5iGf-3eypGmPIas0|?ob1YhWyN5(@Q7cLh%s>1KOn{=0qK)LyKK3?1ol8 zy;0%bwUN5hjN=r&>>u+`r9C&+vdi%ck-|PFeSCf+E1c)GxFs z;z&HzzJ37fm2fy>6NHq2!8{+fN?AI5rASS;Z^ki7d_ZMUBq>~oRmM(9Km^2D_X?#K z&ZxU%9**DJ<>)3Q*pfLS_6hSKy?=Lj^fb&Fh)||ozUWiNdmw+HdUU11ky^^Bf*Qj0 zX8lX!`VJNvEF5)1{UUve$@U92G-H??)xSep3ZIR$vxrt&u5vJ~Q=c*12% z$ZtdZEgXMyT|EhNvZA!G88lBWNd`BlP>TI>7gqZe8YBui;oyQKXafP5L;S3&`Xa&l z@*yn<2}}2I6N>JJNkR%6N7MM8iW&IjYYTU|R-8=?n#e?IZ|sl88zmU^b@vO@?bLJ1 zu=B4!($MTrsKW^SLQZld^t(VO7wXk{ffiQkf}G$!N#JSt75M8#m)yFWW5I{_K}C(5 z>{Sm@d=%u>$T(8?4VU`_eW#fk{P%Oo?GQY?PE(o6S1p1NQha=`fvJ1_PxyO~szA6Q zsK2^K6<+CImYthBjZ_q?SzQLi)WYoStFTQ#;0y0`61;x>nlEzdWbht)+~4xRz-{!4 zPP{#>#t}@V#8X&_n3*WQbXnK-(Fk_ul)bT;IY`N;Van!n7UZ`)|LZ;`IOt*K+^UEgT$T{=|0 zgw)$!SzTqCY#8>o$m}gh8MWCeFWZZlBb^i2C+;`;Vd?N3azHX7rUR6z(X&@UYisu1ZlZ%~q>kb!* zhl(^&D?qp^x}Negb;fU6#Yl|*TQti&@e(pOtN)Qz`7c1N;B0s_{1LU|kpKn>KyDfW zznq`mC`C`hjyEpGHgl7?14<;VYsfl5+_V?NM;YJ_TE5YY0!LL?q|~FdqaOVkH)f-< zrtA_Zj$mnpF)Z_JmD^`5bSs zBPvW(?HjFn1eVh{BEx(RvzMm}w)Q6`m>_WHWU1MlgW0#+l@1jGC;1#A#}n!ST)h1l zL?Qk)M{ftN4_AXcX13OcleS48ka_Rs^|06Nk*RNEzH52vy_^6*91!S`1lM9sm&602 z4MiCnUk>|SkZjs6%Oa{=;QlC08yF1fzQv`a97l3H42>FeC;C6EuxXnO=_jUdSPw}2QK>LaG0CRd1 zElk)q3zO1BFyfzf}MO0i+hLZEY-y zdXhAQU>#{bS9?GM0gxesR0=gKNn^F4jJDGUtJi{u6ap^?b4U9Vn;c_G-D=;a&e(~q zr?@EUWtf%*Qw3N9u~y6m!fjqsLd(L!)N~O@o=+{8VT5@k>cwqBvYZ@-pWMgFogi7~uaU-G7zRq65nB?>X9=Pil%}hXmhsOg9 z-oEiLV%*S6HGdEl4GkaLDN~Xt&bbu;x5qPA=cD7-BeCK*TAKCe$4xc)bO4(E$sORm zwz_Pbs~t4DkvpF5y?cG-T3Xm=pz%k6(+osh8ZlexWzBm7;~;w^Q!K7y=#N$(X02T% z0lzq|_^yvqWNFL4wfSJSUOcrZKhuD(p1{{w&3W`IrbDc$IuOye#T7-k6BFk@r@n#J zRE+f23_a4;L<*Ia;H##+otqWBg zkSSgP=}JXav2IUMms_qCw#ZiSQNlss$M0comvouyM8nbSfc{aW^S%eU+QrJ zUs{RjDoEuH4z0wzui*iHd&=WjK)~{PZjMtu2R-m956{HV$jYYk#jo}N`xPpZ>bacs z$qjez8~_m;dLkop54<69BXIcP?39oHVKXgvigjYnsP)x$WRqBRKr#7Wi9zO?iqw3$ z>5V+@Na|D`bOgNf@u=bV@4tZQkTS1t+Vv?m@S7>UP2((W}`L2;%gIrjgcpr^A-WoGjispw(pj{fz7m zO9nlL*OeQ9hUFS@!6776Ju*5byxV!Fp+V$m0|ub=x?Hp@50J<(~tg(*ROInb8qMlQO)}=U%u=(hLB_Z8nNmM#G^xhJ{)ql;VK5W zadJT2xWc0&E7DzUFSSzbU;XmgE-*S?Y;JxtEx!iF0h60LZI;$;r0;$W>g--`?5pE9jKR)prSk*hudp2lc?TP`Sxn_SX8R5G*TGZdq z=|M^Mh!tEjwF-6t+9JYdx-CH)DaX}J`WV9R91tCc?gxXXjdLg5fHrm5YUug;=rKwb z+XCrdEx=7NlJEwEx$R1Sv;Cab)vg{bgTyHA@vn=D-?USrxIIquSO{|UicpL*x=Tx@ z9-=gd)d~voXv$xinV$ciu5jZ7LSc{vv$UaxJANe8Y_@;$W?BuCA`F2^$(! zRe6rT^`j@Uz{SMu?i1U;2xE`XqLY-AJ~}$!85zIa|8>mLbLUi~#pHS0ynInL-xJxa z8&5u)`c+($+bu|zPVR+bPofZqhy;CDL`Zme=*RGlVQU}nTA$jwb?bcGt!X_~L+b#C zRnqb($ZI@WSy4|eO-NWIEsNnEZgn?xU5lW&X#udXcB3*%tNY3~;{>p+ z(uvZ!!tr`F3|}at5muUj)|Bli0G^4+tNd#wiN_p1vpgs$BfBT(4xL%kdV74L8{O9W z@L>_jrMh13upP_PqIYE%_cEr_nQZef<{o)?Y)#EUsLgF5m$HkDSo@nEvS)ZL(Mq5k z@!ok;LK`+cIywf*o^~rBP`T03ah8hQy?eLPU!(jmpGN2yLULcIX}WP=sJ1gAOgwe# zSJgEV$obO@i^VkE7PUw59f-LU92^EbX6(o`*P*_wP5A{X$OUiWWDCvm+X^?OPQhSQH-$8l$J-3tPt61T*Ao$ zn8!~dLxx8*U;?6MD!D@5-_&BcZ836J5+;BuW!Hw$Boe%?Bxr|0WpFic)>d}6>pytO zZ>-8Qq6ccT8YWbk>J6IDh{bo-{6ZPX&v&voSpcG6_XSJQrJJTjt!Q#_Jmxz*iKr+9 zGLBD4ZT^|eY#kYS1Q5W(MhCYLucwoe`TQ;#`|MG>6U&H@sjGdjkXI#6 zpg}=LAzW(>qQ-^>S!ymRn^mpLf3xZ38Vd1y$P|lZw&gTG|1RumT;wn2v`8 zd5h!4FqN@RQIO(V*;wuEx}cdO&HDq}tjaV1NG1tDduE@sSLk^+-6Xmf3T9s4>u$hx zefQCdSVFV6>qy0%j7A7P`+a+a<_~6rbC> zQ7JPr@V zurV=9Ya*rP8M*%$2{8QH?jgTq(W@_>9qz%J$!lwt96-LfaPFWLx5bSMBiWoRNtMb$ zY3?B0hS@j#d}@qnhC;dmll+sH8G9fc}#lVI}o zW+*i}D5~}D3Es*CsP-d~msSl|am6L1-`f*HJ+^hdE1`$`DLD!+ysly)ls=w?M$7VJ zFei@|fw=T%?97NwWYKdJlo_W*l_k)6QI-G45s>r&4tK(_zGTibTS%JL{f?M1=Xh5E z4Z}y!0;dt&J#20PW)Ts>reEURQOO1*?+dQhItmK@EnLZ|N_jZ35WYuLx%)~_$SQPc zD8}?@f!MLSH#&-|%~^mpmmoMzSPL}MXz0`y&(Hd&Lw)}bd+*`Ybk}taV?zZ66$R;v zf=Wk$&{08Ay7V4;C!u$Uf`W7r=}kmB2I(aPq)YDv2qE+yklx9AxXOJ$-#7Elynn!x zafXozzjDq#`|Q2;TKiZJECu@?b~3*ih95$7$0RbP***3OU4T+_Orq!_kW4QR)lTf3 zzH2JcCLP-XhW9MU3w9CoZ49zCTRj3ZhihT?VN65W;wyRW|AD32H$8ePJKAxKE> zf#R*tMsENBc-&F{=&*UdVy_-ua7vE*xwA41WLQ~jth#u9i)Sz(d#wA6Q*61%2}&D4 z)`rzn0<l`WR0Sa|mG?V^j*#Gj!OI2-y zgK<#=T*zRpY4qNsa_*0e;AydESS)llDP?C|qpMFIFI z+3?=3*8jWj%-h|to2b``$*<>tE2>Lj!~%{ZkW1N()6t|}qe<-<={10=x_nDB$kV_I z+g7M0#C=Ih`Yfmdr%%t7n)PM~4V+5GcIy{D{x3mNzfH{J@8<==aM0;S@23!a3peh4 z9Bl`+^0(6IwNwjvFNd3FEug^*2Z?l#>x z(Pqlksvl7J`UhrOk_i^HLBXJ9$w{#}%U`4Xw&h6D%Z-Ufy>idtsH5%ixek(ZXUT#m zNLRm21tgSNCS+>oXj^M1xE7;r%rc_whV0PVWd^92m>8?tqA|DLo9Ai9xUL5z)5aJb z9`oTG7)ayZY2V(fhSqp@CJ8%rHE_-+d8Djuin%la!$y9$6LvA)byUrSymCwsiaF;R!MbVM7A zz*isdb~@m^I`sd1WV_!OgSLDwwU#12zdGX7r=t3qaz#!_P8hLcs~Q4(pzzAFd~fyg zF%_z5SNY*Bf_ zG~05LF$pmVQ&Y)*v*AEn2hTaDSiKtO$zLgK3MOYr?1iQ+S>G_4cqJtK%s$i*g7+#pr!}~$y(Ir2FtbTK|6v?Qx&bF<5A3&?h8r|wYICug9 zBUfE@goea$6~?i>F2K~Dw0*9m6o2eJ^U%vJSD9UYpZ=bm#!ys52S(I*G`uC%cL;g^ zQ&j%3c{D2I@AV7b8H|jY;g7~vsvL;7D+-H zM!Z7y#g?B0IrD&l%E8hucyIcW3d&?weyP#h}X|P`;~yLFAekLF-?zJ8YA`b<{ zXL;Yz^V7xCyYte^m{xl20AP-K$l#<-w7qm>MB&k|GOk)XqK}hmnW180@^&2xE^KWR z)^O)P9g|2%60*qr?@qgqX1d^Q$1Av}_X_>0^}NcJKgA|G91YKfEerlQWSe%Uv!ds} z?j%>|56s~H8s4VeTmCgBY{6ku!qWLM-?LNHr&ZWnddQ_t?Gbl7JlD3&TLNijI=kWi?#4O4> zH{H4wa8zCGcf?>fbnCgGJ?CS{AKTOdJtMifm)G96y2;Ba6(FAn!}1g5(c3%{g;|HEHEPlm&3l2|h56?XlbjHO{@b3(q*3##H{;j;?TGqgb5g zdDI@>q$bB84O~vfMqaKQ%%=`PrnIk$RKVdu<7cR@joJ{F)WMfAYc&7b1o){WJx9s} z2X`M`vDFBgdXf0{u{T>Jz8_2uc2&ifvgr z7vrxjXNzV3JESB%msH$nNbIjnUr`N-<=j+%NU!HH{PB&;tWL>@HZbrbsb?RT$L|T< z=a;5px%9kDQ1pT5^3li>(0ZwE)-~@2I$S&B^iBTxSe}0ju@vq~6+Z-qD!wyNd=)9| zJ(CR{)oMApzBSsvv!eatT1`{F%D;aBe)6Anl1qCbTmfIpyt-_sy}7FGl%yYL4g=G2 zahKrY_UPa5J}tIq;cD9I#+jWV`SqlN+euYl->!=5x}07aUZef!g&<;Gx0*)-`Dz?* z|K+}sp>dx}y1sJC+#|3`!LRNAd!2Y0Oa*{OXqHaY(n=h~di1G&Qku(~&p_Dm~R)^C@6p zJQADV4GP@cp8fmc|JO^`cPMzoFP23>*5do)Yk$x2_;MJ9;w+1c>Gd|j_L8Dol<(tQ zLM}V&%IeAZ?!SGF>>%7jZCF@>UpM=milf{0yIoE02=On=k0E9mteMyvvW)f}to7el zTu#F*Lw_2TfaG+lYS-Om->ZNd!DBV^P`4h!MAvgV5aitwFDCwV4u+>=+w0nI>8)|5 zZnouGjT?9K;c}WDghw+-Tdp>u;5mDgx`tYX?b?qpHMY%l^|;lTlA*W{f}+b!7AfAa z#XN%O@R#rLyFEY^xZrZIIp1~3&mOGm&!20dRWJvz^ew7bM71_uK{&kbf~=(IkOMBN~K$jl3FcXUoI z6PuC2dh5sDvQd(0Tk~*OD(W4;nH7x(hN2{>U+pUdXYi6?)oIV?AqeRI=M-pya?IjeCH z2YC#j}CbM0aZc5W$;R?LXBMjXVxb`4%V&D z$pY%WlS8P5yZ+axI~naDU0}_m|IvQ2JTq=`2`_3feZ1|A>>5*3 zQ?o2Ch+8Gi9tc&QdCKmh{8rYkDzCFf<;|PP<_&(O`R)`S$H^mVvF-ujGg9@0h({yT zDIEX$R-yBD5XBeRuygC$FF0m)PA{D`b!>M=FBxz(?rJ9L>*yF)VcEop(YF|ps9ln*YJ*wL8P%oK#ob8y68 zEs+>q$tUOvYO1L@hpZW`IYne;SQi&3yLoWimrhsiq&q_Q%C0Glor8mez3sST>dncS+_tv1q9PRxP38sV9RGT3 z*%RpJeas8A;yiN0{S#OUAezCJA5q`9VZBZ_1V0?YCn(La9cwL-cZ<2$6Ac2=|3>M6 zbEdF^goB~n1hZt*Gj?jR3?aMatPV=2d-z6CQSOp;MqhV{^x`~ERJ+{vK|%su`OQ|U z*NvYAC+++N?#_`HBTcDi<}34FrDRJN;EQcq)=Sj>iYW(TviPGq!ocu$n#m328>oN7 zh!*hvYk5O&%jg4v!Ajw;e55Qx8 zBQ%?V+q|%zJH3rU;~|zMXw-FKYW&9eY&E0nk<{c@Nu{3o!rmA?V|AJxV2N%8lgsY% zNsqhS{o!Uo$cGCTV2{+vsW9l4xJ39O-7A%#wIp=~qylFn+)3rRx3}!j~!_M52 zfty*>|N8YF>t0tAo>AD?2Vh8~-6CKcDyCV3x6hq)fE0gxTq14qx`aecDKk4)Z|s6g zhMC!|S+nR{(dvWhNS+kz>&6>7^6Z-%JL@|;>JNR8S8HgaV`5D8PeQ!oO{|_j4~$@l z=65W4adaAALG$${2lOLo5$(9*RS5K3XO~0;qC4}dB=66EyZZhbu|(xC+d(=nrmDNM zD4p(#dCcH(%-2r@Lps9Vd}5QP>TAYj{CPFH!T}bFTkJR1kmYd`5HQ@mo91`N7JGo* z!9!4nS(ndIwTy`Y<--j$3_FCUSj8^;{U=c{e2!H}2X<`it0gV8OpT3-P5)td{=vT^HgUcLHw;{o+hlQYOkMG*Ij%GTqu?*- z2#b_v+1AWCK6f@hT;u$+YqD~3!DQqiWE*+8>^S}Zj(I)Uo9>Ll5-v;R{ZVG}0gD%N zXBvm02-}MbL?)!w9nkgQRAdWp_F0D`uh6n+E&@Tx7*s=WaVVHJe4lj z{2N8Rjd@>2e-B(xQXZ(Edq-th3# z|A=_O?^dKhg8x50KPdqk_&+|sB>wjT|2qbtS>XTL!T*i{@C*NI2mdc)aL>i<)P0|@ zMZTJGtd!@Dl{Hhzk!OhvW(ED;IBx`v>GO*YoPN|aH9=1}bqx&-HSq5?=;51|o7;wa zagOBct-|#xKdOIl7`m`iPjCMKT#qolOdw1X2=}5(-*Zq!0xjq3M&oDfQJ$B_GiSl( zZDt1c@eGh>2?z+lIPa2>aH-!zatJy69n*j94G`B>f z=hOde=Ns83ATu*F)u1XWDtbCPORGgBBt6fC*8clAOQ0Vh2}#8l&}EAKINJAw%S0_P zGdn+f;1ERk6EX@^k-cz9EmNFrc@9mpv9R+P;PEx=TsW)AKoP=|-&?cCl_>HlTChp?br)3Swi)tPpg?C(e$h_Ce_s75N6Qffi4IM?9`f>4 z)eEG#YR>>bm8Qo2#Wz!CyUG}?sACycsw9>WjNLx*d3>D~eLMOKpa*Mt_mhsB? z_N^Xw;T^6X`vTLIZq;%a#8=UiTzrc2D|pQUD_p-AZZzgk~1pDVFgtY7f2KU zpr*OMe;C0iSxEf#?7AIRpu*ANO>dh2VLAL*{8%fPylw7m@oJ)1!>~hXERWCX;=q@5 zBoAf6Z=v@lwz2Ll0c{}EZVobG5?Pp%N&TUcGnk25tJyw45_EA|OeJcLm6|`<)L5cH zj>;viZl(?}YQu*BD>1yN%G#b#pizb5>=HN#;M?s=hGSH7^YXSl(Z~uR2xNX{eQSEL zrB6jAHxDPqDIFNj*FNsu;_TMBq;8{Gkc%Fr>K4P7LlONr?1NB;7n5&?I;T$;W_rBt z!P3TXm+QcJP<#NX%#O$q zwz~HY(8%MGvE|jfL-a6CN@bcQQhGvj;k3p0?%Ss4-N!}UaRL@AdzUj&l;XUi%d(mD z?fx;cY_SpX?x?I#dy_6hUR#}Ag-zki(zvS3=F7@C8r~uAd7{vY}86@W7+c2@O z>9_TsvP6#Ynvcz3v)I4L(x*<%uSp(N^)?jHquqA9cqEU7cw%#wdqYdsZe#m}rG(mM zH!7HXj8Tou3gd1m&uHH1fJJ2?5GsJUk#k>*Dz~%idw;d6)$@i?nU?eCNofOVG0VZF znvc6v#eZ}b0ZyTmmq2Bx7U%lr=I-VuC3(chwQ*I@??>=({^~Mf-@}BJ*^vaZ(RjL$ z!_92Dufplg*2L~8fPmg>znh#3#W(0Gar(Iq?APzkmDhA{zye4~*f<^?Tx?On>GP9h zQ@Z#o@bOzo6Fyu%N9y6>5gkj#Nt*BEUf*pt97Y$-^jO`*1l@O^o%9?yv?Yog+4NO@o5FNZ6pSV(mF_{ch^MN8Qv`yz$q)mPWDrq%F!pTrEZx0o zebf4{-$|EEw?&c#Ig0N{mfzt~D&P?$8m;81vETH~{q;0j^41*%I((@^s>5DO7>$%C$b5&3@I@q~$LsG9P^t zZLZ#EuB8?~u{WPHbBn!T&&UJt_PsUv*FXbiWfkPr-kw#Ah+U&l(v|SRXZee zmA>}7Gv$81H0t}(791>)rzvb|^A_#7!#JFyt0&HJb7nS0GI1S~LB72u z@u(M6E=2m}S%AGrqq28;3v)%dMP?oMCnnRCGO`%OO9~!+xTr(oV)!$5>z=MJ_m20w6;f ztQS1?X=xu&K4AV`z4ANN$zXhZ>DR=O7@L#xZD>Z(31|ud%#=>tb+g%J+s(k)tKZN` zSxpTj?MP1zut(R?DQLM%|74Tqq}CM!bueaViF@xfc@Ec$<@JFl-EII?s@DIMiXe7a zt#T=CPWmke3mO@jG%J7>CS&pax(NKrAA-{qj4WPnJP-fWE%y1f^LLGu`#>3~Wa4$E zV#=#p_Y*z+2v3j0Y_;6CBz+_fRx?*D{NT+>!Bo?)X&7NTZ z-C>q*eN_?Pm|>B*01IWq^C@KYA0PEoQjD<~hP-b~BbbN`ZfEbvn(MSN>l~uuV-}og zTlyb|X94dt<+(L9z{hEq1#@t$T$vb8-hnY&@_mt&%t%d@5}YN>qPI#R1fQXCz{Wmw zbF7+I{wTM9+aYeZj#>)2R!=eS1&RwUY6Wk9M6_s}5 zhi0oCJc{x|cSgX<2SNT@H+no~vqL6xJWdAKuYyTg5&!>3lv+_Q*&t48{ zD1#$6-R^)U6iF*9N+-uPTsE?1MRih9t4Y4)FEn#MmJym2lC}sKsBqR>5;6aC1sqGg z#cruEhjqU@0AffgAy*s=qsFq1Z>!2`v&OcAExY_@UEu|ncvv~_(rootG;CyVT7(Y%R-?TTb8CBH5LH0F zQy+wK03lHo>XPP%@(|m)czSvOxvk(zP1cZ%;$FMS0u}~|Kl5nbjE|2yF?-IWMo6fs zyHU-_bxTfvHm-jGK&iRTl*v^U63kav&zpcND0AZ+lSeX}n;V~BhF4WR@4FPyK9;YI z4ldk(H}utPK;YJ`TaIZL+*uw&ehg&O!-^J?Op&>{ODH%IWrTAhm8!Iw9O--m2}EO+nfCr9mtVcL-3es6zk77ys0Q!X>SksJ2FNS>Y z4DL$s5~1)K9gWerpge1;AnDv+zmirs18si@*x>ofcIrW3ds5{LAj6&BB}q@oDie9lQ{E28e+`*)d zq$1J;tf#&lCrtn{7w@vzPr*1{ew1}|(qk&kuXw8)PAn8sE%V6RxNb#Ag`~Le#nTuF zGA|eU1THvGJt!cq>O!cmJhgd=`*3z@+l~j zC%QD9F}tudn1?6HYiI_6I@1`c;k8Q%~gJ-y#+{OoK2yjE}MK)y@8#&G)a%KfEvFLw@bGgwcum;zr>N z+cNumd+ax5(c|Ypdjg9yOHV^sWj^o5TbqR8MnF^O(p2NMOOT~scU%mW!D{u?Uyq#e z0drD<8skz_ciHv=oo7d}YjPQyh&bo9-YCy&UtJvcTt28f@)YQ@DLC#J$O}1$zPC9G zL5@Yv_uZo5(Ld^@f%=Nq+X{VaEmy)7+Q}Ip8%W*GlhD6`EIf4(_skx!?#C4s75N|T zf&_daI7DI7H@Io4PAbsusBqHb@|j=yr$LdYDeOH=+$*p=?8f=Y`jOC=jpJsH9>6D? zd^u6`9PO1lWPnP`$T5gTB6uds*{+GTjx1)6p?EGo{Osc03$F0m|_@Y$%$8nRtlyJM!))8x#=8QcTf0S!#Ep z5n~v5(p+O*@1$w4Nr;HIF4>OKHD9cE12tBu9UWc|w@*T}FG71^{c zdNJ>kEIknsEEHiN;>%g;RghQUDxcK_@)8mVn57qXpo??3)syE^ggAqri$V_NyyAxx zp&vul9ui?b9o_Bdqxlb^(^iVd*GKcgL}-G(Pn!kRFuLf|grx$u<%U1Opf8;38C8ZX zXh{_$YgrS=akFM-&GZ<3M7q`fgi%B57>VWFUqS2-ai^$HLIXG7L(0;J=~0W4mRjzp z+7{%>0GA?ntN^k19=zH^PoL7Ka3j6gLJ?b$+%2*1hJSye5vaX7gboagrw4IMsjhc{ z#IcC9+m%a~{!l9K?;R`!hkyf?LdeJzlU^waJML}es16RQ3j&2(kaXXJG~&eF*J(zR zpj@F6&nd|R0cqi%x?_|Z3<7^>N0uW`jYnJ5o}6PzAHA4;R^7Vv)&0K3V5id2P}K+h z;Ta6V&OdW=*AW*u_t>K^`+v6Psr6p0eabFQ?tv5 zNNM(~HdN!>?76z$-dSDxtT#A{?~C4gup0ASv-V7({&Yv|?Uuq2!VSQh!hf*2SIspX zM71bdb`;}`v+VyInGV-0-vKmZYe)>))4b=nuO_Xt2FFwv6M04|w;|JEXjECE1x%#b z*_w*lm9^h-&WyxT<8LZihknTw*EysQghp69`m* z*T;Le{{Y!&DKi-v+3G;X^6)xUa4;E!SiK(TViR_Ju16X~bpR=Bst436%u%*rF7Itu zIPr&T1a+le$|oV)L-RlZBuewGESOV!+j%5-nS=d(j_9_w+}s;zI}H00ce+#*vfbFd?XW3$19ucf}pxGt*yGUJ}x|c+9sj8gYP$wf-xGF^p+x zZY|lau?eS++=Im!p@pjASKC)yy`zSHC=`xD}%{p4{zM%RKUD6^?jKvqYqwp zF9KxnjYRse27TZRZ@*#uq3QN}cH_i9K>c=`ZnxEO%QimV0_G=>z=S?v5Kdb86ec?D zmVK%@5h`=SG}cK0i_RW=xAf~P*uahe6K*W&%RL0caJfuk^6aM#(f3a%wpYb{0Am)3 zn>yTK?%^wolFgt{&bjdHA?I(NhrcBea~%L;0(L{^W>9wR+E+ThrGxOoqp7vx|U`f3)z6KDK;`5c*7LvHxRcI;^Mgfb9xz!B|&&=WEdz>*{3J6)hv9XK%Hg zwkOgW=*7$8FER%E`}rSk(f6Q~@P-N>Wf`-T*)rFfHrZ|Osn(EUpctDT3aPNj`rU9pp?F2F?y^wf~HWrAJ;}iGgaq%dIZ7v zwPUlSvp630LaXdgwiZJHCzZH8pOHyJZ+ViSg_)Tph`Sy&8lVpWD}puVRhjKcEr6*i zQnvM-8nXkV=y*cUo>c>7I!^H#1cs#vm2bd;+}rySqZ1$9urQ|! zLle3dL6ly$($tM7cF{g*2~FJ_X(5iIyaL0_$S)~(Lie;H41AWfWjJHxt9-066|3zr zViwcpTp-=wJ-J9Ul3jK*hIzkeH z(ucoNrXXG`HNF!nrWizb9|OhJ!5>caUW+_;8;hJo6^-lze<$Y?zTXuOUP z>MvY!a&jsiS&L2N^VgAxw+MoZNOR-CAyL0Y4fYU^&U>fY7ugV~5T6JdIabQUvh9cY zWI1CVMxQh_ZN#6YAv9gY zt%hBcn{9C((|50klqd5YA(q(HgwD3v=1vNx1T{Uq(|rB-VDsg)RO$~M6OkM)Pvgl& z)dyUH3vIh^(l5umcH3_9b%!1u9UmVZ-y3~Fp+F~W*PKrTq^(V&9cS5oXukmWNf&B$ zaV#UT$q@?uP*CtZ{qiC;emdrLxtYPqa$dJ4lZc_&NkvwwSD|LAZ)t4az!H22Y8NN$ zD6}6<^W*5%`qpw_mC$q*z-qu`+#L85AwV7EWpX>Jp-Q=O`sQ%p8W2Rn zQYzvgt*$+~@ooc?@5-ZT0-3+j!I#U;SYO|hC+6L>{HAeg)&`4&+2y=Tc2t6Q9R8Hh z77OO<9v+tye-x#W4S1x!n=4A!R=LUbGJ5;N2>C?a7lhuR8*>3!14u z-jib^2j!sE#m2709vz16ixXKw^vNpzgw`tz}@boT}o)&oHJ7D^UJhsTJK zB64tIl<~ke)dPi`<3Zk&vt;0v;ZLU+`^_2T#1FL54@AG3{mejGHD)g}#}R+7yE-`Z zqTO&t)wp6eyay#0KgCr;(>Hv^U4=5CB+=GQ^U%aq*pb6=Y$`IF_(;*Zn)mot|607b zyXEI5O4q-LR3%QJ6^RTUkhl@G=G&Rfn8BKw;KhOc49s{@)vVWi7X+SJs1wu}d6nsV zv^gxF6zuPo(rr9|Dn44=EI2=`5X-IqgPW&!8T3Q3TpI$1o1kF?Na@8~AS7gq((N1r zdzA1d6CF%r5SC67y~G?LPRn%NsQm&uwU+pVr-p%ca6Eb}>5NEr7(#kjN$M?+#^R>0Fx<$2h)fl&pJ-pdA%HbSR>)EWNq`<}XCiDTms$2h?0V>^hM-)S8A&zzW4FhYnXH;y$F&1s`(#-F+K{3xs2&QMuQswDqRS7iyxEvY_4f1kr`1QFUE5}eR`-3nNrn|v*HY3-)d z8TLVf>KYwnatH#|9yM%=(&DTEngaUfP^uALV_7ZtMk=EBDkc>IR`$7z6n1@Qks#m+ zT@IcLpmJ-sUvl@W-$2`+B%wuv zL#>I8-1{EuxoQ}yDOux zl*%AWED}I8n>!4wVhhS*lS5coq9>-bSA4l@ccF5n(@oG$ijRhuTYfVBXY_50wX?oz zLCxg|$?Z@zwqi3g3_Km1Tpz6#lJiclUGW8yp06;2@)Xy@)88ML6n&)5Yhd8Mu4>m=;7z^`{y&M zmF4d#LhcKGA0n`~D)=@wHav}cr{4Jo3?EA0ZgLGO5O=f%VEF|P57?uKJD0#una^B^ zo!cC`IF*!4dI#LL6AfpH!+rMj;V%6Y&|V{`qN`sva3Q=Ky%)^o;?2fh>e;&~NAL`8 z_#ji_V>CkVI><=Lz&{f|4=u7(EuTm$2oPpwX3ptYZ`I#KDhiLXU1hU9p$2q`FEN6j zPJ7I-S02!4GyeH%Gd3u9r20TjqQi{kGr;(yu!a&57>brA6 z_bi0%I)Np&rb;YV%K13&bw-~|ir#?XMDuq|{Clv8_O|D}dmGX&U+Xm#s_jH~Wh`#* zfIY>a+VuC&$?bxjUw$7T;f!!_5X{U$V)MB0F%uTFfFQ5%%?^n|asVyVq6;%(&zFE$ zyn6NR{FS5%-BTHll?*Z4;?gI8s~|ZeypkncI$0gS;Du%1=W}RYQ+{^s=;(-sN84PE zqaae6v&N+@`H6XFA^JAOOsEvvONsgPiK{oNxZcr6fnsDxYz-Z@GDKf=>2|+WYhL>@ z9mtYw_cTdo%e^RmVQ9)?u@;{Br>)`@M=rb zx__fHPGBB{v36Dx5OC5VIL&3*pHk9#4MR+mNwMU$H@P1zOCzzPCR$vW#05aU)5#9& z>_fm+*}M$v8hj4ehEHz(n&w9$b5vuIV374Z8o@Y#K>Q`nzW*qY*#=yruS|aiNQAVn zj15{cj7&J_;DjLC=V271JzB=m>p`U`Sx7@K3^XFLp`5b*^2Vlv+u}CFK?VQ*i~{cW z``3#(kG4bGo|rA^_cEHS;PO*z85ubS-#y#y)V~8;@p!`?eKKA6z$v9~h>88`c2w&u zfFU@k0ZM9K;Z=nM*b^b$go_OxPN0i*n2M8ew&0_orj|H7@t`KW7Sp zfPQ$amA=>~fRrn@84uysuL`Sj=-^58%|$EZs8tzGPE$*1#e(X^a52ZGthqrrD5nV| zd~%I^bNSrGc&G#E`RvT@Y7BpGKuFHO(md3+)4Rs{FG6X=iNnG`&=vsXa`c!O@)t+) z+(5EFckw+pZYlyHs=GG34B7K10` zHe}~k9Z2g~TetOAGuVL|y9jT#90rE-+U8Sg04!Sr_x;i)qW8Cyu4PcJ_`dT3g{;`? zc?UpUVg_bW&Dtd}ZrH%*Aihf#NMFgRCGOr3H7y%wiwAycG#Wz#2>y-__uHb|Gcq#H zoXOo`{C0SM3nJMhm(jOR&K%o11JKpMCG)_m%-W*k&LYz@>n1s)40b>;p#w-amqFp} z=|br}rier#tKBZUx#aEQ;v#t;KkWMtiKCmjxp_SLLhzb|eOI04Z7KaEXZ^dEo~xV9 zzP2g;=o;drbnZ4ltL}=={>*8QVdw2JHuN98S5BuY}?OGx=3U z-wU2<)9Zo)K+4HDoa(S-`pnnhtt=zcbx@V8exW-D5Ff{qkwn3Kcq6{gvP)@Dtlp;s!1Rh# zKp1Z=$|-U7&Y8i%K`*y8h9@vTt5yJ-)}Aaq(Ph_VaDNDh;w1z&`5GUv<5QE9lauYN z;g081X#nJTd~{S_e~VIbX)ZcoX1)4e!*(l{_+QIqZNzws%wZC5vdQ$3%~8+pfdg8&D4Yu zDE#2392W}YXed|kiODX2Ds~FQ0r)4i=JMm>o3?Ldw>v_3c)abyt=fTDqTc~@hRISG z9wdtz8)MisC!pD(SXIj)Y;TCAa^TH-{PR%fkvzc7#0 zu5IN+y?F7#+Qra2B1PPDyDaWbkLh?r1a}f7t75qA&Us+QCe1h%i~&dz6u)OdTdP-Q z35<+X^wiWaBL40rzdyhHDYZM_0mk3SpW_5jn&UPQS|bn`2PHgz{d%T>x)jp%8AvoG z3hjOUozxoifP}SFwCJ=}6H~7x^tl?bw{6l93vePpwZfew;tk4`QjL6*0ex_%_mDVR z?xjgcqd_@`crElk=v2K_4yrSyt{@qpXF(EH_i+#?T#M*KNRxR!`W{D|kXpxiCB$4| z;Qy|mH5@H38jx=P<>DL@+UnZc_I4L}+}7Xh1#d`GFhrE7 zKlU?42I&|I*~pG&MI6gjN_GyPcezQO#6j z_L&|`oAB}WUPBY>=2Lt-06K1D_^s(95BO^+VtZt*_Q=*@lJ2WYXZ6~<#<~W1rIKAG z*9t}bD#awRCht~gEVu8@dSK3@n9Y|US^yAKIda3o@!h-syYc@RSMu5ldx80o}x6qqxq%t;-QDzuL)H-P*o-bmk7CgO*k|du9DlUjEte?+ z)HqDS&VOPRBo*RCe8%3to>O8Fbo?^d5%!;812jPvd#a(YUux4*Hkzkx3IiyvsLP^j zUw~xiyTGYt{a4tBxjuod7z!aBVAgm5LT%57QA3FU!E+z}xV)Mo&{F+w0`S~+*Kj^! z1TBBjrGXr|HQ(b4&$5w7o$V%zM8PA&QQxvVN#eMVV5I!zc^4{VUhdai$iiszqQx}>Z<69E8kP47QlY5p-5x(crr zLbZ3dnT39r+y?_0zy~4j9qUZv049_e-i@0=@faNBT6MbMk>f}0YK<1cNCg zAG_@#4dk2o#fKBA7z&!jD^Xcv+L*Ui2nN}cT`%~p87L#xuHj&9^uvoFHbuOjh&(TSqdHGCDcA9l(KJ+VshskO%YIu}~qm zowcpcfW|13C+=)de0T0}PVz{L@TT!^oJm_#Q%CC-wyEfzoOqN^MVv#zjm}c>L_ix6 z*u)P~dwY9#9F}UTYt|OpzFvxy%tv~&h}-QK#TvNJx~MK2Dl6NA05%i;uJzL-#yM6S z4^F-#gAn9?5nhaJ7_v<pwhFT`SrzaApb85?f=ANFY+bQV>3v{M~ipQTb0FVPJI#Vzp=-UxI|+&h{Pph=3?K`6YGhse`g3V6{!*V`#z8|fjvOj}{(9`sed&2~ zE0wU$kNu+xCnzR5P$)@AU0!a&e84@jqS>U2R0 zbSfI&T!ZuK+J*vM zRx8~G>dokrqCw`*tgHt>D-LQ7apmE=a<`whmu#NW4$H;Lqivk^0T%;60&7VvK<9>7 zA8Fa{c(7b_@1=ec#{KT4dhy+HxCGxT7_6&UN#b*&Nd|>9=-Dt=l(9)8A;@p_-D>9pT0?xGmG%{W#(eMHNF_5j2Wz`|L|M)Q9t3I|ejb@lit(be}K zWVUilG(cT!r+TN4{Pc7qIT{|Z+J)C4k--YE+fK##8mR$4|9sp_LFXfDfj`xZ4oJN7lnCb!0f%##LZQ++nl2A!-oPS7?dhs=3Ow2(;+lq;?+=j=I~`Q|>A zd`<6)7litUC#mSeaGND-W)k11r*}R*9cE#n*Jfvt8XMBK`r)btLR;~1BowNvE4uQt zth8aMOy|eY8<|oaz3dm#Ph)JJIY#LiZOZwXbpT9k>1ldixXIJaA zBTtfOx<;-&SD~9UO3Ft&m`a9n(Ylzub+(8RTPvlM<>cVt@rTd$Dcawc?)K{H@SW#! z>$}X$1&BYrEi_yPxwQ7_GfPV%h+Zxt_K)w%7k4M*Z_~#B>i61Az8b|wd~&FIpZeJM zsoWZ+2-C+WZGem<&&H!&W;~s##O$>Z{~{!|OI|KQ?v=bkdxhCzQC5oAIyyo^zr_7f zm+AMpJFCmLAAB)_RL+-l%_3C@$uU9t>{GsaUT?DKRTlx$%_?Zsrzo$x7t!-4 zfy`0`!WsPFSY~JYAT^eel6AWuXCUk{l#CTy5W)Rwh3^(!FR}_}9EI?p40Ph+4J+X% zR!u+HO4%>x3<_=TO8*!fCIz3HH5j0?Pj_THynnDB++}E}@|wO~c$Bg#w48p#-K+N3 zqETOlrNU*~YuCHeF8P`M(D6?>0BVr09AnZs#pyoXU8%iZ)VLyPL@U?FkGbPzM>O<V-2(Yjs7YqQ;&hl1?zRB zx3z*(OT}KXTylIa{`PV%=fru=LCkj@pT*%wd@XT@L;MV6sDO|EI=}n7JFlKToyZs7 z?BM~ln?s#*Fg4esKa3)aMX|ANZ5_`3@Mo0SBX94~(Rje}sqE8D=SN`9+dhASQq9Yq zn`Dd1*!KSPOqBi5byhf-+4RtrB$~EEB$Xcy4+t0-jMEmIo3Noc^&>M}f@i>4dJ%ZL zL4l~bL>@>wRl?%@%#K`!D z;LxZfMfJ?&h3R8j+~VS*RYmRi|6%GaprZV{s8LiTrCTHf1f{zqq?Jaxy9I=yQ@TM# zq+41*M7pHA8Mez%)!0sOP6QvFJ3On!m12Ywk9*Gj6VO&@49w97Ipew z6y4L)b6CGlr}|am+b3}*uMTc6lK#Xf3a@9WVL3T%om22cM`Mlluo}6uc%RLTNj1=| zjQP)3)3cd>50|C<+po3Ig4JAaJw2jly=?}Jikwe=lbl4p>sKO-{9`g^HrW2MrKOwi z^M;GA67*uGuChoqEGfwf*WxZc?>>9Lv++x8?G<)9byfv6BsdSJA~FJ|il;I%TeL)3 zEd3jnltm!LzL8#k6&S8@Ga@O;#6xbRr_En0NLS)bxg_>52t@do+_`Uko8Wu5;@nJ= z%$d13yR@D7s`&-K()k1kAw2cync`^MHo|fXes~WhY~wbIzx1_4Oq^!jX7si!Emy!T0xam zcVNldX026@05-evIE_=`a+Dfyc));`E3ZH&{&_WhOoGHg$K;9`9gUEYzs0pD&DY4UGdIRk!I* z>)hlpG`$Lq=o&M$y=s<;#%s+Mnt9#HaBmt4ml#5O_T{W+L5F8WwlA~8(MH(i4(nq0 zVI{`7e4tG@?p=X+m$Jw7P_&Ts_wwHHJ2lC1Ud2Hi;iRW?VDh{`t(?-*He_LeUg*Wl z)OnV@!^Xls_p9Pg*|(EGr20(RB5dR(2|E{7HfTZ&uk*^F-`v(Pp;L3;ej*IYbU*ln zQB-1MU~pf56_2MzoNb%l!Orn87{0uPhoe#M+GTpG4lGAQ2W4U{=`c?BhO>SUVrG(o zH{@WcFX@+7VoWF;dfV4}Ub~ATcgM^5k~k`w<@o_Rjj^$!Yk@&dotB;aprOC1@rXo0+%1fn>B$+at|p&?ci zVZnXVXPq?-k_jO(@!e^ulkXW62Dg5c+|dOMmCV`3yF}NTS{i2{i2h)R)uRs7Smjv( zzuaDZP-G>gP)yIv6r#*v5W4MjPA8!a=YjXE2IJ)va1AA=gvHfkVrD#}ASs(8#<&)& zs*8_{GZ6GZTLgr@2NFbUYlIqUL$NriEY>^!0$A*3jS_0*BL6h#gB*>CPO7A4xp&v!ubeU2-+_3D4dJA4fzM)Ed7DxWQmGe8jCOF6NmbYt&j*f0~eR`9`zB@9I|HDn59IZeZ z595hl>sSfQvseRuo`**+!iJ*UG1F3D9*(;kPgvt0n-f{rhK(w*#qs|2jj?wXLx}+k zi)_yV>iRbS^dmd|m56ka$pQES0eSbv-f!hIPql-zTaJ&9pJ2z2zNgOlwQ~9*m;-t1 z8#i_De1f^Hgw~<3cU<`9ufX++9~UeJ@knEILUZit-rjlZui|30J;XKYK3(S<7Hwu| z4_TUcRVk5nQY@b>X)I=2h;F|xi17Y8jR~Fp8tGT zEVAx4I$|%X%@x8L>l(@%krHRv_(`96>G?FL?Nl|UEB$k?LwLVaAY1MOD*Wqj64I**8H!-fRD|!r6>J9AFR#{Gh)sSU4&x3eC%HOur+gUU5D&QT zvfgHGTvrEqi9I|InS6~}tq+lCS>5_0uNyh1#LOyrz{n@loRgN7#6q|V?tA}Sk~8N!%6Z<~(-O5$+cQz&wr$Q?pUMb&at#ZAg^AoP(WJZ^u%V75PGg5xBWxkJE8j5po zb6!CjB0Og1Eagrfh=d=3>B>{BykKYriLFbPz19d+>SLu1rr)M~PGf%mREo$sm<;jYfxo}Gg88zIZFdTHc6gVt|M};V zrSh`!PGbfo)v5<_8-r(kA4ou>ku~e+X>cHVeaMR^SwTrcS?Xq53{Pdgf9Lp3FO^bG zc4lcwaS-u&v@g@O!A`U5m2i-Dzk&kiUbKCKf0<&As*Pb17QL1qmBfpBgRR}&r-7-Y zKi}n;)0uy(S$?1Wiq__#6<+*6^~y71g;hws2s>F-oc^0pR5=PS2S-~%z}Odh$ zyc)jk0jkmrzlX>o$}j`=(u#^SVTT60kp^@cOf0~sUlEfeTi{BGuP~0gE zz&mJ1{5+v!G!6I4)`qS;t9K;er^E;a)ojbbULeB1Oz!li$=H7h(aus)Oh~KpB;{K& zZyB1geM@Ua(#X1+fdQa@X$Ub9pqg^0*dOY>4p?Oqcf8d)+Zz=Yaj*Wh(s@Epx7r#& zMfjr;+?bmliaI?Fr}i`8yRrwJVf`8D#@cY539B|U$q4Ezl>N^-to^OueD=3sgCpRKcI@RY1jClc3nCz`qgTMU_1oWzFX3DGpHtMgG z=zRYg96DLX1kUGnna|rEJP4%m|80p}GLFV#U)O~W_$rV3k9LXmWJRC} zIIbw#>{7`aBv6k>dg99T_`W2@N>{vY^(4dH}oe`=46Se7+;6>^EgoRrG^s_p}V`4ShzZPMp$8vN|JXV1usN{h!K zB0;o|Z@fy6^h)PkJv_2fzUSCKAdoALGB<;o-IlPh#CUtTLEX+@$Hv7v*iMPvXyw29Kc*V!ZKS}A^UF#nkD6zdFGe}&`n67qo z_;+dX8gB~kzs$(L)m`&1Gm^XKl^%60E@ntF4q&9|>~~Cx;rz+Zr+`s_d4#FZM)x*y zXT3D49usRk^N?i3L6dtoxLOYgKhrMTPr`PB?>8D> z&ku?_=x8V^8sxfsA3Pp4f%wHvF=)!}99epeLlMU*VhHzZgR(>oI^d`23OdaaQQoHq zE#cJ7>*dwc{R#sL(WNXERnS83b!T{m_}HQ^1L4m1`~et;o`JrhuGV`4w|l+mA?1Ol z2MQS4S?43WhupiH^z?Sv+1@_p$Zo2=iP4QUNsX58bG!X%NJKt3?g5!h`q~9#Ujfvw z;cFmzFD)#FyJ!8*I%@S3kwtLHG=R4qsdmPAG(>yy9pEfpyQx4KL}uV`U8g&DdJ_Ee#7t zi=L6uoT|Q(D@JHuEsZNhR_(8ooASV__lxS^o-h)O^{V;bl@7^UFzV zIwj1bC+>z(cO4z=@g;ZTDY24P0(Fxl((4WAOq-fh=;=R*a_k%(@st&%V(gMUAeB&E z&W>WrsQftF56($N{ifrQ@_Lrk{MEhXJSu?75Z$rNG)k}-E24o>QN?Bv6}dq_hOw*-{}u;Qlddw zoz~Rc0%~dMT>onGYiDY&k>L2WJG}`TqcKO_stU}JAB-&1 zs`&&PVc=*FLM*^>?5pK=21wKsU)2j<6w+d}uf|MGPlEdgQ`68tynGZSsSeJ`{Tr8j zz>!}T;lL3~fsDHVN!jsR#E@0hEL`|K*PPQIql%YVnd!L;F;Q_Z68dD>6!TPH%F52r z)9*K=?&_vMjv72NTv}@dAkYktqdPT+GGoQ+zgATv=?I09zQ?`@VELw1)}m0~XTq7A zXTH0Kq5J7s*x{!P^6kC(O24!+h1D6^i#R5*(6uhAmdVRC4-oh7PiOJ=;?ZPt;Ly;< z&Kfbt!=rR#*78WgF?S^2Ejt%QM{>eG80|vPh_Sk^i~~;E3iUaq20+FgsB$#UbZazV zw{LXLt8A%hsi`Ti>7R+m#glUq-{ux`GF?A2d}JM}YpQ1rvR#+*0Wvy+*Hi%EtlNO0GNyhNUiWrV1 z73Iy2o$sm0amNI<*Vef0FAdH!|AiXtSDUS@U21q?;TH2-Tn!R)dp3F^^YfzcTOZG- z*X3-tJ_FR$OD7$*Bdw>xRsqr)uF@Lki+hJ;k-MFVk{vb^AYb{I<`$Z}subEgcm`P>7r~;q>vvBtc<2 zjMC_fMr5J+!F!d-qDNM!l9njegGvG7nUd>72tjPgH)gwN|NSIUDaJvNy={6 zH>rdAR05yIxb?AucIJqf<7o;X_Li;hKwlW@!#262vVhAj*N1wgZuvrJ9V`x7R&zfUQ6;W@ji47%O$*V} z^5uj{0Sb5xaVTOF*&UUd&$cHINM zCtKj6V&H4`#spt@O`TOMjoSJ0{9R?v$+LTEmWzHB0Cs`g2T7$qb1PbaD&{Ik;o16~v8ZPe0 zIy*b}^`@U)-9VD>{{@QhT`U`OP5Q zWYN`Q5g<`|_C?3NMeQsF-x$QWBITjnxeH5EC8^8r#_za-fM|hJH$Tci?mH)QiGaab z{a)5|zq8?E6+=Zvbpr!dq&CQ3v|HV@c(FE2)FbD%id-j=02}+}>wDjxYc{83Xdz!^ z!v3^_-#v~;xBqvsG4E17kGFhF@r)BDG)UHZNT!Cz0sje)l$rfh)A`@S4PMff5FM^py~+e!^&kGLG!2>6%~g2MDKpj}WB34FpxUI*_}fJKyK$urn_IqsI=RIsuq z_Idkw)6pUO`Y4EF41Jc~@9zRH=;RKQtI|T5f3>m*7#CtaUWA&pGd!Pn&zp!jDjJuv zfBN;ZhSCXf6{-&+t$NFGkeo+WN7_P1dLzUBXkg^_7W6;Cj~{ueBVyidK=8MtfJOlQMgMgAq2t*Q^p*%Di&t&n|pz#k1$od=2%Erdm-V-yqSd-{GO1*lfE$u8x_NAqc#(pq>?ATG=tGPHk zzk8ed{WI@Dpd2-F?|O`=CobZN64N6SgEpcr`?IIPAT|^W6GjUjC4r%$XHA1k0CVE~ zKIe+;JCzxn_@2@-)>>sQV5|WKnW=9_wmVh0`-=|yn6|ugVR+(DcwNiMfq*8LK*Rf{ zHN4~L#8=@w!Zqi`<)zo?XlwN$0iRm@Az-dxB*x;p(l~pC-&85Lp0aiHNr;6dT+R$h z&Skbns1#Q}#+n>|cunmCBn9t{w79t}#`nFj3z!O}d%C~&5gVfSba%BPPqx4@%PbLp zjS->ObMrO+h*!a@i9#Mm@^-SmW?wVKUm`z!f*E^>zF@m=EHh57d01-8>F^tj5s*`j zJ>9~==VCZY3}1)Y_4d|X1hGOTp<_;4OfKO5g7kxP8MU@-8)c1-#5wdUe1zLdElO`Vz- z-QoYMM}@>huOBzh&dsq-aG7I?M~j4;bt}9?9n7R720_B-M3cNqAt10$N#bZ1i&Z(p z|A=JSw3&;ID{F?r+ z+xwUdlf4+Xz@p^l{;v~{`osh`ccF6D>QHC7N5$IVq5oCG?S$130wCS@_kL5XOek@~ zTYP9a!DKTf^U&@AIw_D#BJTNJ74eC(is~n*LCtXuTa=d3XWIfxiZHlv8JEEGoCJVC zdi<%SaI3ivVK6*1_tr6w6d3!$W%J-V*AyQW_)3j7WOY!yt|~7@MO*D>!6&v51h2;j zsDKyI!S22%padK@J)6N41;gzA!MmBThDJwfp}M2qHa}|WOaP>uM5?b766Tqiy#m*v ziBc53R=)HIrH?IO3&BVFR%0?>XZRhu)SK@D5rd3pRiLRht>gsOaGw0=bZ#cf~I`?k`J6 z*j%jY4cPIIHF?4nbyPLAzctB_-%-~a#4&GKxrW>aApHXC+5v_LpG$?$Jw=rObkX&M zg6BJ?v&nGBirl(tLSCK}1mud>);r9Q(nBfJT-? z@h&2pI@ih<%|{=ZC9?mbwPp|J=uP527P zGz?{ZprjL*N%)xloLkgVccZF5dQc{==XJY#u6Bv6!MRKI)p@iB_CU|#BjHzBy3C?-tTzL_p@WI?nV zCHe-EX7Hbb=Yv8^OLkwn@I>Jj&ZZBBLwWMw+e#TKNf{2Ehve z!_)s|bJCU0)BlC-jN&eMv$&SEtY)J|Neo9$F2A8ZTUdBZa4n*=3pA0qUG02!XP7F{No3rLzEgqIYz zBI2x?v;w`8^tlt-j=uD40zKNg$V?C#I{GQceMZ0zxV}LCq+*BX{kYXoW=iT|R0(o% zagoM zU)fQjL-wJJgxo@-IlS!mho_L>soAlm*)jh^SNxNrhOJQEWXd4}w)y^t_27yao7{r91Bsx?*(SR=0=DPCzeU>tE3{p0GUoMb z3F!)qU;2cVMO*OdXH4=g9p@))cfkIPvRU=bSmMC;(01}G5Eo-U`@fi{@*5kmn<=O% z0E|FCNw0eA#&`5coucAo@PGU`{#`?+8=3iu(w?0)-*s!U4;=Y0 zf{3Rm`h%IIr52$S_^IMXT}egsDmN=mZ7^t{ep^W_etLF^l$0P+XmD}S?=t-+z*w*l zDQSPGTj&;`r1`=-0lL@)JK??kz4N3yw`%}E;-ipAO^*twF32&)!ya zrv8%n+~tDqiz;IRh5lQTTyY$tm&7iFeW%#fo}S)Izb#Tuk=(_9N^&2LH2s}<U|V%d4Omb$7$RrP%flVLngz51jF=8vFc~!L@+BSc91~KDLxk zV5*0Sv6$a_P?3$LKI&|cNQ!aCo0pR&^Qf}2g&iz&pj)8suH`4CCVFD4?Ly z{;j6VVZZll1~R`@YhypMXCi9V5EmOAzvl0{bke^%0FIAZ&9eV&p$_NYF8li&lN2O`BU%yCRhKl#-jf=P4TI zJN1(*!p`ol#=00c-CAD0-pvAWb)wh^BZBN`%Ud+(-NO&9T6z}yn)^*oABUSBTb$}V zQl!N<;1N^P1sgoj|Es?~xC;%){)#oDulWF@7EVR{#!oN)J^Mp4DK_Pi8!Cgrr#A?8!IAeYrza!NR7Z_{F){R zil@)N;)xOTjdDS$=f{i#O#^5XRiF z;b}O1ilUYa$*qa5p=L1!B$&2W6zP?{BA@y8s=<2<9K}^qnS$q~+Epi0f*IS;jcO5E z{JMS{E9fv>RWKnT~(Ao3{6k;RD_H-lpa4)nU8W z#YxP_rb%Y0s3--axV8Wk8Y0)t=@zp1D;#Zp=MHHj0aRN^!};!lmm=nU8F`ctNB(CN~16hOx2Z-@Z-NDm7Z&KAD7Yuo*OIRNqY5b1d4}YB}h^ zCTPDI6wuOHOWU8#6iS2X=$+EY$gMt7VZoO7Ty9ad@bc})ylOr+di5J5*eDmoBwU$TbCJQ^_m+_8$ipp6GdW~-(}XUf(zN=c%y9)8{*d3YsqOy9rK#^X)OI0 za*+@4m=vv-F^POr&=NDpmlmhL^{a4c7Q)Y}&&ZZe7X~Vd`L7P=PNuU0Vls4_VK&2e zp+5ezrpNVfY#)?Jc8>%to9B584wTg{28wSnev~|(IttJlYGh+!Te=&QJ%?ubuUbN_ zcA&Y&{_v$0AsI~5nZ_p(cNtFxssz08>#!5HylB%td+iU}iq`34<5ZTIHPt2?;=#> zr#eYm#hw+DH_B^pRi0HOxA_1qa~cg5t&1rxHhW;>bjoUv&Hm#^!z~$`Vfoxbt96pg znEt(^yZfKoYT5gFSr=zFa0$^Y=QQjg%K0y^Sg@Dqiv4aCsq<5h^|dYhNjg#U!omV! zp|ZBash7yzL=t67_c9+G_l0AzfXMiS>6l8U&)80MDoYRdKc)Ohtj6VTni79VSe(x81Zq<^4UV+RJZ^N6p67uSg^H(tZs^rtUH?wMGI{fnhUp&l4_>rCfpu{Z#? zfwlR9Kez|~zdf>=mO;oNT6&5Y3#u`>{F^r>TO!p${`qxPhbi&d|2A`^N0gIZ&;M(j z)O-8|KzvmmwY057KL4`Il15zOQMeMWYsAdp%561V=2l{M*0*{3+pM|-EkI?ZgwG={ zShbY7zwG<;0}z#OL&{-=E1B9c#q$Se?;fDsTHx`aeF-M}efpaa${4C>l#5FKCB0rl3kT&z0pNZNX;vzgxo0;#NrL+|(gMJ@kzkxC-i1j7qPYSGXrYL`}bTK}-B%jAtMd0U%* zs!><#-LN+DeQ~kuiv4e69vT<_*yITwj_IYR_tVN}b&QcZ=?Q*Kb+jrZULDj=S=oCB z`otXD7jStC@Z4n!3^(JUcvbtGKZPJCO?JB))fO7TMWOQqs5=L0(LBA$v3uzNBY5?# zQlwUD7bcA_{S(0#DkoiE*RU&I2p-uIsK;u(H(0if@(sqM6sJb_+XAoeEAhLR-lOHf zyShBnG&)WZ|M)AxD32x+pr7L|;pfTQK{w5+fvNw`8wm zXs7R03dFVMK~eeS>myx(=bo&tr|%DCDrG$~br>2xI{y3|P0CSkxx+n#YK0MW4Tp;& z&Z~GmQtd|StE?WM?Z_>F+2H=~-MjAW@82gZ5B%_K%7u4P8-!nvUQhXk)7YaODwnaUb{G49Kjdh~a_yrCB9o~dS)g7rZCc)CxN^SpD43Qo5 zc6vIp1#Z#_dT^L!(x(Cr_a4m)17MA_vQd_>QduA`?`S+sQ%^|ZRwC)d3!5_QXznFp z<+Wb%s+zk>!*FU3-2>2l7_c^IPSa0PK&nNH|Ev(^}fa z{9w4crSTqI5h7#NdqzN!KWBl5$H!liZ-E~@=}%*3UPvwi8=Q1Cyh$7T--_F$vZ1s> zyNLgl#p$|=s6FQ_UJz}(*afR_avfMi_p{U!oy=pfcCGmZ>$!QsX>RWn(Eowh|3urn;DtSwhN{^mE$9K~INw@$9Q{=llhQRZD$ejXAf{#V z%<7@xMog4uwwqe%g|Wn4@PZ*L&%B4a6R>XafBwMy#Pz|*9A5yiKYLU56SjvmOOGV4 zQtUV7^Ouk-^xL9cPX#dU{;M5A=8pKc7>$+lpD0l!He6;s^lLHPDz-)K%gAT9#{jApum@>T(^+`ter}GC# zzQ^z0yvfe2&C^6|8kC9R0&GBhxxr!a5ni~AbVFmKda)z;t0bV8i9Yf-_~V(fb%GJJ zzjP8@PEvt_=0kp)qVlARFH5l%u$0sH*Tb(4MVdUUl`owk-@m;dd(u44+O*g{=wa-$ z9K57hfJl;-mEmZ1d*Q7T3g7=YXmK(qboRtu(0hCbspgb#Ivh!h5zZ53WH*_h+h>yZpOH!6G0gm*uVBO-uwAD-!UmGBnHYaSLJu6*aLaTZco zr4kil4tad2*LXNfW{jKhOxBq~Uq4}MmCH@9DulJdL>Ss|AVAbYT7TWYRs}Sq( z`IlZ6r^fY2>r8CPO(BT0o08Ii=fw~*$H`Q4nq_oRkrZ9Z!$gGq{gJ6LWV+ekAK@LR zlEvq%OP*z!dqt!8qavDh#|) z`%uq^M%YS5T(Y(245pl?-HwGF@3|R!0q<~R3{%u=J)uAEHEY{6P{e1lcKuUU{kR!+ zQkW%>%zp%!33+DS)M$X&Db*a9^U#%xGyQ+Xt&jKJvJnuCKmHe z&<##B(v*#Sfd_8=DPC-l7XGq*3k#6K@{LS^fM#Z&L(23#&<#4P z1u9yZQ7tXG2SXP_mw0@Z9uBHw5@=R3isIO&Ki%EaQ!6c{#W>wXHj4_e8jL7_pAx*) zgFiP9Q+Lr296+-A-uV)$IXxDP4faYNi&e@FYBXj{jw;d1K3D1p7~R1xT)q(<@4Kr* z`P3rv z^Hdgi;5h0mF*6a~p^}vcdmOH1kFP<~&uZjfTHfM=l_J9D)vaL>)>gXxoFE#I&P!{T zZ{I>pf@Sl{>Tj3$cHiB))w@22>FE{=y`a#B`}lDBi0nMU6V9l!OjBByD6}80nFJ3c zbCb(W&FgNhFD9N|e!n-y~NxG0N(bMY*YHIU9R#q*KJb09?X5w`H zGhA=@ZJLwQ0`NwK8vy+fFhw+so+yF#ABChNu2#v>{U3JPlK+=d1KfHqFpY$t(o z3icBzBQV=chmXLXb@kCU`#RwICmH*wm#h8?50{mb1-&o%Q{5?P$+6+A7P6>6;rabP z#tmQBv|jg9IvyewTha-Wj=PbBGaVfr`=&tL8(@;12B+g_7X1mVL&>is$T16ygHKSZ zc&oz;VFAkq$OG*x^DR1XN{mq*c!6Es(s(@_F|b3|)H2X8(D*E7&#H5L=ys(NsW5Z1 z&Og*uA}S*4)Ve;b>9Him81Dui)mq!;G~TJ{%}t~9)?-pAbwT`5p|O9X(-j99WY{FA ztGPKPUgYE)EVz!7(npV+Lxz^F_R8hd{LXo-3LKXj4q!*E#U)K~LdlYhBl>@?ThOU5 zFRAy00D(mg_i#c4V%oNeG3osb;1mMB*E_>BwK>`{8aX`V1KLaeSItNXaaQ6jSR_s; z?kL3*K=CO%qx*l;!+$9u2&9C=;mhQ@AAl05X#Ala#c&X>4$w*6-Q0Ph+VSB|c2oO8 zM6MhON^Tx!R&dVdl$?Z)Kf~;sXI^RiEx~=MRu2KkmjLw@JM&8x_S6(Hg=!e+6w`@? zy^ap4e5o%_oLe?GF z4@Cv3`chYvx4mdM_QjR(09X1tW^gPXEWQ|5pA{jD>T_|;*%f# zN(ey)am*TjQQqR!sWG!NU+?F|f)tIL`C|{wu;Fu+$29JJ&z;w~s)0dM+ed{*OhWEI z6tDD#fiQS_M7#;UHQycg8w2%WDqOIf8q8*(7n2eXp)9RwIPE!V<+|Bi|18aF1<*8a zJt@hKR8ZZorgF7hPoSjdu@Q(4Y z+n8m*o?r`WLJg{IlD>528jRRTipR}NGA zvk+M68nz1c>)&+tiESYv4+Bl+r;VF#R|`w}L!{|IQEI-M`Z@l!lx-+B`2L<4%Tn^V zbM)8oQoxtf663nCS9${4+S@OrKk#e$mh-L|$s zWB-MvQlHR^({Vz;Fql8m2_ANkV*KLkckf#ryYK`DcYn4z7#d4eDZ6J{>3_coL#Oe1 z*6d0=(Sv>l%bpycth-Ih8ryM?c(V;U_6(b!73P%rX)4 z)ES7cui-Bkl`~pxD($?7wzuDc6%?aa1?1XxUZ7HaZH#YZa)UxYU`EYYU)}3|?(dsc z9R@6{Cs?>C-vE(+(IJ*LLWAz+a$u-It7fU${PxdIpCXMhl=|rqXjhDNfU@+3Oz6); zcJ?HI$j8RTrF=<^%T$}29drEnoH@6(r>ao7pukd5i8}fhUMl|GFnZQ6oy8ZSkNy0b z)5i#m#gUk8L<#CvXtqyyj(`;qp2C3Wrs8E^B7mO|ifcp(HNIz3=6;3_N+hL?(Ie|# zp5AbDRAs=({mvC#LhTEdt<*D(`u2fcMb-1~7{(JunqBXDkMi>9HA>Rb(&mST;{tFY zcmJ25U2V%?=ImfuZ-)=g4uP{JO?l_tGx4v$thPp^%5l zmXIvoXz1=AEpyOVN6TPR)mWgN`trvFQ&>oMr*dR$bWhVIAYDAe<^NKp!^+(BHC)+h zf5@Wcc9luy@lC#VbVGyiY)6NYkr9E~SQU&-<3-{Zz|TZD>mX6tz=AAoSqvz;3=Jgd z;AdIi(@%#ksX)(H!d!uebIyX_IX%tcJa-O8yJTJxUuvgY0b@O{^QX^GYYCF$vs>B_ z)LPD_nU9$+W+BV8R;I;cV_$ZetG=F+l5FIG(Ev5&b%1FwT)^z4LS-$_+fWZK zRh`e`Js_i|_9Tq7c*5pxqslitJlx{qVgMNu^f&#5{eH0cWtsHTXS zGYT&R0J$L09fAu~ypvNPP%%O!oeJUuOiVN^T*uEX23gnUcIEfv1Rk!S{+nQUJ#!|< z7d-Ed+Zqq=@^->jrh!Y;Q&|Bv@rj+R z;|(@_r`=)jsqc)%8oQgDqNB?dshZgTXphQ3?=(ZV-(A_@$;{F(?(KBH=j1Rd{5~Q9 z2oOO-vD1p+y?*;DX+Rvg(E%oW%cUUwbDH8r$sED!`HG%+>hS=bcWSG6%f2`J3KeU3 zjm0ln0P|D7fet&s)PKGiXlfU{=c%rZb6u*B`+;(fwko9(?xULw+x^u`?h{>)8QnR> zf8UVzV1J@Kt5#07=ej6hvaOA1ti!xvf4Q+#Z~nZcst_UnZs0GgeZPt4kl`;3BGj_t zbGucVa6G&FG0@I(=vKPO8$9&32827JkzGX{LoI_p0Y`@7spPCW#>3s4N{$GMur06Jg?&g5v&xjDZm!Q|p@N1UFGSdFCB-c7L% z(sHHh(6yxBdK}(73slWUhJdS2Y-~gJ)Aw@7x4+&3OXfP?)qRR!CTaX1l{tseDgtGO zS8qNTq?em}iWm*2OoVEmMwB1q6TVtd{p(4aG~j`3x-Y{y%A@&VXkb{SRW_EGZovHI z!_9SYT_=Nm;#i4b`WW8Q{qk!{@l(x_Bf$P9Ps(;W*PtVHx;dv86~gp1IP5uc@`MG) zH&5~e0v9t;tIbU%DdW?e; z4d9~2FB!HXn&U^C<1Dx>0SrH3Jve#R4j#$)zy2U8Cg+U;5boe;3i@iK6fu{Etd zX`_J8IxN$sD7O80@Ur2n)7ILj{>^-_87;cB0J!umjAj&*JF+N>!2Id>HPwxuc*lUx zP5=DpYaolS(xT8gyfho01^lG6k1}Uq*C6Jpjhl?&KHsn5aSE55Cnm=3gRa($STR%Sdj+_t*Ex@PF{&)m%JAeSB+U zF+9pzld*SnbbH)fdmA(8#e$Df6Ht56Nh9|Mp~0N!cys+|$dc+jVPMm`O2r?%yZIn=LMwsB}fenqq_#q+lqsyUi?O#mh#^BQz49%ZKBx5Lew` z@5003z`X`!R5w7Z`jQ|TAGNsNpN;7@x$BYRe!bI2z0)V>ze_NJh&L$5tfum}qvn2E zp>^2XxnOOPM{9RQaN*eAfw$ri_Y1H=8c`e$O95NV^U*+>T0aM?^RGhSj42ggr@g!*khF+q!3uL)_yifV#%94v=Hb9w&%ru;3 zic2}>HS%g++qBNqfm}7SIJHV4qclWWr~20Ac*z`~(xZi@e3CgR%_wXV7VmBS9F+XZ zx?FTGE-uqW^C3asY`t}Ust4TL&O)AIhaC=XNE8-Y4&zPgm;qK`=z!Mc#V>i*B&&tg zX%{V^j{!-{g2!?bjY~ipvKM zaD|Vuv950JP4G3I(xWD8pfcFAEr$r^y$-l*;asi@r^;=Wi`2OrBFoAE|GrHwD_@TKJ5VRPdwwM3T+?7usGS z)taD$X_~otG@taJvHW&yyrD;{` zd;@SlTQI@Y{D-0W-)@^$E_vS6cMUi-!&=Zeb)sxW62M+Wg8XpSqhMHL8z!C&OutkQ{m$0G=UPz8|qJ;E@3Y zVH(eklN4JDc>K?oEVETUg`x{^FdZeG{vJoC_F|RSkH*MrJ^+Unphw5W)xoN4~py^lNgp52yaAVRhan(7BdJ zuK12yjZ8mjl)(Q{RzG(4i?Ro&t_{$RKnGsDQw3OqTq$CJ!{t-}P<{j4@D2(pDpLWV zCxdDUK&L5Q52&*+L)pZ*Y(WkGqBdrW?9L^2Zs#NxXj!at-in#1AjI;k?yTWMy}XpT*bwB>hpL-sGw^IJcmo=TcqMFuw858TI)gm^Pol(Wo*k zOU=wF$7KP7`t3U<73Fu}yo&|5-}V!<_#P_Ls@vP!@}#81M1un$vqLg}&-cBHjEPGj z7M@K|zRLFc+-pG{Kd|oN1h#a%d|F!HD1E9P3EDao>#bH7R3b<+GV|@8I5D0TF5YUU zftql1v&kgF!C-bQ`NBr>qn<4A)OZC}9{_$3@bF*{=izR5bZ{&wZ*bJm?;Tq718uQY z!3Y)x2EdD=Wax%58XFt;lCu0q`CwQeHQ;w^58_Db%*0NhE})pezz_W=pS>ZJNgodn z=byl+l&+YyPcOEQ;PHLg-^iPr$KInCN(v9B+F++>=(oV?KyFi_kYh;C{NwQx3H-`` z+u^}Kyl7Cmr~2QBYVuwMc*?$S5c#PThusIJpA*j6%@A z-ae^c{~z|=GAzojZ5SOzLRvveN(BU^8A?h-Kw6|@Xol`?gjF)X#_j5nbe&6@__P_mazt=H8VCI@x=XI`gogE4s)bnT%Gcai{+Bh%xTjHCb^&m5x z56@XWJUC0HBwNxz8biW&bO_M0AAAb%puMK_|M{3Bq7iAqDrwEOj&h>-NSxu^Q(6TtiT8zgdW>HFU;1<6CXKm#CwAauqAf-{k&sKV?X#M_q)kGpJtk1}A)p#*k&kM5`q zI%-0S(@N?H&S77(o71WRRbf7(f0hu~AhNp+g3Rar+6a1iA41;B6H+B??~jbdyKf1h zR(F$%Tzw!8`a(1gMJaohI%a$#W`wOv80nmxp8nPeePQjYx${etxx|5?jipMnZKY`- zX&tm&(K%84PQ*Dnx>uzs7^9(~RzQlGe*TybP!NQNKFjJ3NzbR$<0nLXT|G_TSxJko zD+RF#;{vo84{&3EN#YFWCX5Og5NnJj?p3rwBFAB*iB8vdx*n-JEN;>>_YC(u-(1=> z>Lq3BQe%vl;*@GhTYUEm-TN+?6S&(nV)U*SgF```w?<6UXA{Q&z4$K{`5|c9dj8}0 zArSPHqixJe8;!p}qiEZ8{;=-Rt8m_0HrHrs|KDIV_iv4VL2>@muuse?Q1qQf)B}jl zcN+?j)AvC_IQdnGwUd!s(LM#V71br@cS0t@^p1evoM{$}2~B{s-m$pBw8YlOJkL}Q z#lbD^7PSo!o|M7xN7?U9H$Mf7e+sa-Z;3;v1EdfvB0E#y{j>sgziI|#trS*)BKN(_ z>XK_o%h0%5Hhf2Lig3~YrWzh}%($)9RU&f!eGmXG>`?!5Bd@Jpm@kP6VGRt*$I@|o zlA-dtH2*d*59npe9$-STboom07|~}7tQ6&KFv!uRmt{mT;-%SKi&BgIr!O?4{1-l zn84h1dxuYw~yN*U}WKO=`kT7EGRacN#A5m1%-w$d7qB^T@=pwy5n$Aw| z3}XGBpE723T_W9O0ztj>^=-r2J~ef-=5;Nj9Aawwh0O)_Uq3(g+2Dl*N+fcL{0B4PslI8DPk86&j5g^!f=D-00K_~fC^x+=@X2F`NgL)z;#JdLn;zpVZK=U zy~TvVRZH*oj?~204DLvcj~t^K?6yCOhc8$=G57-wXgD#DfyGFpxL^1#i#n{-|+WNqMlf&aWR(j6+VPmpA3dJ=@ zyRW1!tS)Je*nI$Lhw?+`H#Rm_cPux3c$mUJ^6*2!!Qgo!7?Y4-hwovW|Ldrh&xH0mFkf3 zm=XXTiBn+?*qhuP27d=+sO84F66j5GoDArImb4-B zJv)$SG1YYt;nQ7<&NA<@JC#O?t1#)v`1Vx18Pn2tc#}E0B0B%XL$aC+u z1FYBJ`4%vthWKUnxVQ8e4{=HygoBkG%XEx?%#CG$&)d;lz->$ShsU}3+3Be1CniLd zCPjR&YxBy-=uQUkg$+>}806^*g>K&h6%nBObHHCsjh_Wc?Uwf5Ler%by0Q+UFmc0>Pe-1z^I(yW@OIL9e68@S&gAmw<@a+>ku}!NI?7KPu;< z$(+(1{-LE=L}Y8>^UW+}M>d8xFQ`3U$MyrQ1in?0_w5aM=Yx7)2xQ`snc7_ijm*t?q`_+Mp>SUs-6h#Nq*te^zg%^39a z0E``c=8Y_BzB?~>Km4z&jUzB7tp4u<0fA)E5(S;}CkN#Q<>uz*cP~O7+`oCNo$`_X zw}bHA?QRBEcT5*7_Rke;hYtt^TzZ`=wt`_VBPC%lWCvk!evp0^Q0d&9t^Wc6)ou$I zAWE<~lM?^X#Xnz%`~$M8|NG~wwqlRo3ukic z0k=2ie-1Ll+Xh5O2iLXi{bB&<`Q3W@p8~#jldjl`Kf(Nx4@R*Ail#9ue?8v@$o}6K z>m36ISGjQdAU{6|R1WV>>J+PjI1m2bnY*AkHU0#G5!~P%NnP;IuK)AhA6PAS#0_bP z21w@XZAK=o?#SBh^l*O)P^2fs_;Nqh8}D%G4#&?`fB*Nd>frzLRW0CLdlj`InSlVV z_R7jn^vnjB0q>vlDL*F%-t&(gE9_sS>7pcQKV`pvP88It7bgwhOuPHZC5*oRzJuPK z|2)YZNdN6x`ep(9Z*TnPA9Cz}f$^U={{4pz`JKr8zg!JG|LpYt{LI_t|8@Ddz%cp0 zU2Xqt`~UdtO33Y$f~wtP?%eC*1jgtMI}-b}RU4_dyMc^gB(ADBQb^99S5L8lsmB?T~hK=KCJ`i8%G+A;dU6szrP1IU5&+!H@3oGUu~M$`_UN+ zuyOx=QHK=ARGqsxL4O)qSV0P~)|>jP)2l3jI`N;Y#GfB%-&g6aRfareh^mNfBjuZ5AOe-&;R97 zz-;(mKK=g>9HcKJ?$nf65kgV6xL&k$2_>AsoUTB|fS(Qbmg8b6gT&Ipt$E0C(NH|= z`$0P@TjVe3JU$MjRb_dnzruMXqAeW#TTMC0WU4H&AV0OK z=TC9*aB#pVI^%-r2zJ~-h&N;U+ye-8$RE{v)D<0f3Lv#Fp(q1AV=-T1l_dtmp!r9Z z*N;MO?OFEayOo&!vy!dP---}>#*g2>es@G|Dns6uA0Ip79UZSCo^UrlBZIJt3|QTV zd=s5${x47{jnc%tQ32D`$Y1Nbs z)Re^v2{6vwstX?Ye%|8V&E$EH0x1sgB!H~4Cm_RPanaz5HiElf)95jL=p`cPB_n7g zhyFO+Jx9X7o}<^?u{!ssdrOllBN6VGrUiWB9tfONo={0ifm4Rep|Ipq@^WBaM^>Ga z@(vL*X>$*}dB60?WWsRDyKMP`B7*@wi$xrXVwMqiDqpMsd0&&BlgU9uYympT=eG@1 zA!2r;uOJ=2K&LL;FGsP=43Y;SvO%Ln!}^WhLjD#~ZFfi@07z(<(Pbqe7#{pUaZ6U~F|I#~Ad5;LWYq`U_R#;mM1&!4(SUh* z&$sGCa-b}8U|!QfdQc54i^ax|RT=35^$v^`?of=X4?3(k8KcQ40IGRa%*aqoBf0pe z%d9H1y;?$mDCqqQQ1eZW`^7g^hkkq;NJo-Rctu#HGw#{-0_z$RWECGaj^e2eO(T^V zN6%t;<=CDUJ~MW-3bsA=^B?YekPk0l7HqsGm&7N0b*qt#{n`G|A8zrpv?dM}AJ=+@WfOoJ<4kb8hb{ zBO^4SCnv41CXkB(UsPU6F|yO3T%A@T@ULb>!5Tsy*x|CT845ps-y}ZN`~dBHPX-oc*uEPFL>MV<^-~|!yC;qy?x{SXg`*pAQVDY` zi&`XfM!p~XK?<6h(FZQzi%viwZ!HN4i6*x~Tg)4F*A_`3dPT96!vvhQPeeyi1?EqU zsz~p)%H=a4S2!&@cU9UZ;>YGt*}op6Z;MI;B=k>{JBmh#do5$3`ST|~OBvri^?#RY z4PuO~#dA#w5i}A?gfhd@bdkO0MZDy2-)AnU^7!V;37zJY2iWXWgb+vvyQn;7kY>Q7 zHBXOo4+NhW*WW-?qNYq+b(xIv=zXzsdK`F4>ac&E!a-xjh@bDb`~Xq28$=sfl84?8 zYGv2crGJbV8bxvs>>jU*^8-^ERRqun6v{tCYmeolQxuDZl}KwDlW808zz8MSvz!$Whm@lJ3d#Rnx1JbI4C|?F6Jf* z5>*<#xw$d^PUZnP(-MtZIbUMHEG5NXa3Vkerjr^NhsaFv9NK;MK!*iYA&u34RA4=V2!DgnRz1b9sNRE33m*^Owsng41ux$5LGhym2;`?cluQP< zdZ6h3+o|5BH{3ayPl%p$E*|p?&i}4Rt!mut4h#R8`6ItXUuQr;1!h#CMe#u@e7Pwt zt!{SK`l}%D0T_I1HI`x4!pJY_5bx+%1u2St?p{#{Y+FPsS|+mAj_ z4;uftq~`biJD3WEKncA}VA}iVakZb8@NslZ-^+hA5dEYms!Zb&t=K^%D*s(n_?yA` zMUyI&C_qdv+?s}LVQ@`ar2b)#a!GMa9Ek@$+(#AqO0#k%zqRsQINd>h7*&w#{(9JOzXmFHKZ^Y>Lsj`WyYer)0%FlV^cx zlX`4%`E-$LqX+4kljhF@)93*;=lZ~*FqV&`xL>Y3Q|7q04qbU??y05!GSzz{c7y|= z`|h47E$9iy0JtRpPsc$*!;msd(Ss{bh=Z1%W16bNN{+jN;I$*Zru#Pj$1u}R5F;+V zT`$1ojWUEorY?oIC!!~06t3{|{aXcNW6KU*efU?uRX-)EnD(qE66I6oaRr3vJ+APe zR?`xVyc73e)jrpp%t$!xF#+qMC4{x;|G^na7>xwb z&E(;outw}~noeVOh8!~NGkuBvU*9;765b}K#l#5$tdUW?_KwqcOypg5+FYWMC|0)! zn9Lj_Dh#vv{-Cgbd}QjAg4&amGIfjD1>L@WZy?78wdPBwv3{9G5(E5Bkt-*>AjibP z#l^-wvXjL#2&K`&OFpbXgAhmw%FQn64uof77nKgOSlA@LFz|Va%{insm45yL&WroNM44=y$DPLPm|Y?lg1El%4!_n zXiSzarOcWCYhv4;2nYKJ_vqNLJ{u@Z5Y}{(V7ibPiBCq*cp3>@w5vOfvd8!c__3)T zKw|UZ!+!%6%M{&W4+e7KYOte}RaCJ2$`6uDU?O7YZdg_WY+bkL%~(ffxOK-S#mRZi zy_7I#ndo%{Wr$e%;4gg4i!Zt9;sI&K(1c#Fp?XRqPhE{AqOpQzxT;I)3yquyB7^Ae zl;O9b;3oD2Ll>@@hdmEvh7HsX>W`T6fZH<9J7dYlPNwMZ2-0Tf>I zXLmbe5JJ-pFEMNpWzH0fpe>nPJalnceh9Gvi;LWC4Jv#SEROya+ix>2q3-5}yxgEo z@}`XD76A!X-eP8Qj0xo#!#g_V;?~^Q-KMjS6i*v8_Of!aYt8_k|K*i5`nFq^wH)2M zwZ3td9Q(m>0s6T#Xw>=~L?G0se+-L(w1RvahwsOUcp6zked%DA^@~`jwSuoIgBv#w z&)+qkmC^*p3wK+%dB;hFuN-ulR;N?Dr1ta8OpOeT4^srS+K#eHi!%~!ZmwHenC`jw zN5)K3psqxXViNzlTNlnEPbedHm{*e;r*Wls@9n%!?2D@@pp}_AI_aJZlz-0z98rW) z4v<|ljtf6LhEFg*+wUV7mU==BtJ_--ld173*qk(xs2hfgNT_|uL4NzNxs6!d|0rO_ zh4IBFX*(a+zoY{ep=u0`>waZcU|;&BAmj0!1*gLW?6-)-xx(pQ)d~J)$7;YuXD{E{RfRlpuM}NZXkov-=CkdeC+8TMl0Dk zq!I3UQkA?SC!0VEY1^4!&Ftgx(e-6*vIB}%m7#YWBvRe4pR8Q$vyxz+Xs%-b{n`A< zP~Xxz-h-=FT0^wv(VEO0lZ+=y0NQ6a+9+E6~y;E8inFqThW%iYq}t@VeLPD&(|nF`n0mHQs=?_w<^PsJlkP zGP|IlOw|G&H2H&6B=yJK?j~Yra%{@>e6bY-E21g#Rcps|1|A3-n;QSwL9qAclVbZl z$sKRW>aRIgrk+o+yaWvHftoV5m;$Se2Ff?0O)kq>xA}piio@gjs|{>Yy_-~Vp^w~N z!tNJYLRS@;egVvE#SDdRaRE%Km4BjJr5*_LT#l`0X0|01c(o%>mBz=~vb?UB2`reH z;>?jsfP=spm4>;jkma_$>Ork<7GDI7nii=e-(^!{n2qJ1UV5y}x$Q1XiKC-4AtnYFJoLyI3R_RPs@rAO8m!xi1Sfeq&Ne^ScxX(Si zkXq2^DEXjQvQg(pZN?x>-LhzEQ3@FAOodzKW~oy+5SzyGda8Wrg4iZa8c_MuFcTwwVr0jnFP7q31)m7z=TTxMicFEyAPv*4R^z8Ad2W>pXMublWPzMZ=M9cyX0 zVPt2G>o%fQD*9$;yuQYL-#v5Mk6iLHt~vJ8Mq`Efbe-$=g~`r-K*|-pO;!9{+*64v zN~(t$o|Ztlppj4xYE{xS=SpbPkQsL<9G@OVE*+nolpgXs-Jn!d96AAkp5ZU=+TY*W`ZctUN?U27&AqIx=$OJ z_Wiq(eJPhHybVhlauA;O%P>l*V2C|^e}lo$%1K4steaY&u+M_I;_}9S?slV-fJN|B zx?uYL9e~>R^GKMM1Yh$fS3cI!uSg$0Lk9Z>v3;FVcW<0g-qwtjMsYj+yvH zqhdRD%Bj87xHIDYJRL!>m;&^1DlVIM)eAs@2|#hkSXm*Ra-O=wXBT?8KVAXi;_Je| z#xHtIAs(&@J#L4?wYl56)9!Rhp+5-0KcssuwOrNBdS#)ZDuR1^=^&NbWxXRv&786L z<+c&7ewPbg@><-}{fi&k3nQDDk`X_%O_dB=3$~|Cb8_1>#mA40q3Ux3XN!aS$00KN zv zq-11U##U}?M+wIb!R{lFF`V7p~RSwiA>FGIALP zH!PZpF3`$-#jPnyTs0%g4EFyrnN;!%c?{I8hXfWvj2Y4e9hJyOdr~qV-!J2{&Lb6t4#~$jme<4Q9!` zMv9!_H4pzWQP}hDh)h$%j0`iCLakccszDt^LN)xqq>ga}YHN`K0+jtf zsHM5>BConTn}jN-Tn_6m$_nLvH#NMyT@yMFZ^@&PaKoD^cbog1vk|{M8jO~XT=Oe!!I@DAFr^_kGhE;o}Q)A>a z_12%S^vh8SMHh?&rRDHBZo{fAnMjm!E*j0 zsaeTa>qnr7cC zrJ&;t>%n;VhutL%g4Uvu8hpiiEir&95hC37q~@GeK|R?uXo%qXkO_KZ~_)?_l1l`G_ywX zpUbYU*X1*arHpijIn24r^D(`c*Gx>r4}=L@J zA+UdcdfN$kc<@1|bi* zn6Gfeux2zo6KQQ56YaTrsI6(;ot99brloe=flf7t@K_UaUn=C|vvf{m30$o6tq?l+ zj3;Lct?bI#=y$&f0*z!(=e({9Gvjc>X-qErkf%aN#=15(lG;!W#;4p?$V!*ZTD+QF zBcbz!%`92jEYfwQPwFQP&AW3o4jPgVndAx0HdaSRdim_v1c2B&D)icKX=v!F-7DwA ze5{Klj9=3%qK2pOH6A_y^=&m}VJK8Z*vbH&7dH$lu;DB&zR3l$YSB!}8t!8|^cbip zzURsX58qn$YS$RlG{|TQwpqd%LBolV`ir?L%hpPJ7}cL$@Up6OUq4nTobkBzMeWN) z9epo9gV0}N@w`Z1KGt?#J@qdP;e8UaPXnw5%J^qVr$5FE=5&fwAD0lbBnn;4aD+dV zlDVvzO=sbETED6iahi#8erP(`6}4OAF%{Wm@%f;ui*s`ov0|}Ph#DPrJEi&_ z#bz4Q2e{_jzAE#J6I%Tm^?DD9txkmtO?rnV+v>I6dRn&Wp^>u@E6*p@YFo$o+}e0( zzD{#q#}*d3z2)a)6#gkj?k8%rwLsp+tK;3ABv^r|e$1mDpb4h)@znADI=8kP=edg> zuzoTn2p`10m~SIRg?0oyECOlz^N z?uA3i4AtV0R)!W;4wvnM$5n%dCUhTR8mFU$9@iRkSJmSMUlfDshKX;wRiX(KL$f&zDlp>|I1xM(QY4Au z2$ei;0ZF>eb4kRzPDsgrzxLmmC^DMc^*mlK^t_?su*P6g8V%H>4VctnKLV*a{Q z97wSfF`T7kX=d5upuY-=4^Iwq)SJt+>N}JcPtPl*QCTH`CY3wtIXwCm`7@|s^W669 z&X)0QZ=@Xm$yqzWn&Khxj2@IMQhO{wEWO4a3-W=bS0}b_ zYj5ieD6c4^lYyjLuF-mE_Du))m2q^ zM^%876{gQTy>)?R{rc8AcTICPa;$GAIy~#3B8D^!&PqsJE!o|*+FReM^vU*+rRT#P zC4{Ig)9nue0*ljOJuqe zk$T<3TaC3^h2sfR^TEow7VE{iS;o2xutcsBgroDEC_H;r-H!%-knVW>3Gep;CA!6DAGw|&W;$uzT?_0D(y+o>^%g!FZ{0R6u!YeC z=H*S-^eRPUobG{cH`ubhOL)@Cd{Q;YOqLt*5Q49@isR?GN=8ksYb7o?*D4Ud&ba~4q_4B&t8MBH&C4FG^DaF^A?T7x&{WlBQsz-i8SG=$_ zc&Raz2-n}=&mYt}(aPIbYbxh*3f?GMI5^nJ?%?D<(w{C-%MOL4EY@ zQxOZJk0S~>CZ2V<9^Ds=GMqFo0|743M3$IH8_(ryTBT)oe*ev?gqC}MybkNYv_r-p z^%FpB>f52ZXy^B>IliB)7Fz9gPfm|Um@8Xh>Vk_ea*#d3`AjmK+!nGuZ-QV= z=ikku@5jcb{Q~#<4jQvqWBPpKKNEGY{M`82a~F(dZg@+>zLV?Uu=T^#k6{i@8p*eg zli@u#H#_AzT$sprC~IK>bDsiLZcIdTG_w?%r3MmuJ^OY>b4-p^L9hY_&U0Y3I`LtW z@2^L;hanMmrIPq(IutQ*}yoRU2u=T&!Z z?rREM|4frqYiCeM3t}kmJK<$Ood7RYMmTKzMfe9PjcJbldN43j{^R@v!X!O2yOz)uc0ub1uNtB&ULiq4kQa%yP@&Xbo*a zPxFXPp|cMC)0x|Wqf10=HcB7-Gf14$-*VEvWKVPxc6-UPjVn&aCFs@2VKimf8KDYO zJ97Bsm|$6gKE<8IX^VQfy>tOX0|Po<+PhUHMl6Y5EQxDt75W>9p*Vgst`KY!nUGOa zjVic>i>P7Iv;`~4<)!O|>*X81DcSmcbsF-x5hC+)ptLG0Dp$=|Mo#ACsI!vnUAluV z?lL$yfKfm651`Y@R3IExQh&2g;uZQ-fv36I7gV$$lalOM-4#B4LbxB3RaB3xIEw2J z-ptO{fr-=I#Qic3i7VKHP>aSUQ6)~!jFl*&l2!xuhd%h2U;O;RD3n4dH z015>ikrzM@0qu>DT1(K=1^gaSN<=L?r{^ylC;cU1_zpM_i? zPi)Em-DAN!*IBH|ZFO_?B}lUSH6u|_rvWb7{n>?uO?gcgVOGY$ZpXb~*W^1jCEDvYZuF*|DknJ{^mdXBJ zH6HxX!ZBb4g~`bOe4P%>&HQPnXcfgN^OKA2nB=QTxuN1Znr_ZAhoET2*9=S` z90L6MOZ(W^M*ts59&7obCnL5WloKol7c;Pkn1QcAHNV`N1`6ewZ~&!AaU3!@F(n+X zD&Qd6cI?nk*lw1e*=v{`y6q;D*5bw`Jbj-oUq3fDICiR8r!>!jzQMgqKV% zSXaUdh8{b~>*`v0)q71jxhRT)Gy$Mr9SlV*EqC}c4DF@EU-G;(;Hv^6t|1*VJq1sBUJVdy|O4J#a6s5 z1|VU^P)`u3L5Md~d{gScGKn+R!hOP%lyWsgya?1xLC>>uz)7qLdGL@fDxeG5GvK}M z+K30%xVf>J$3m46C?AXzk`YX++n>r_yV)CYc5^=~!W$nMrG=-sY;b4IkyLavuGO8b zyl))j*7iD{oV#T{S5T9`^XFPF!IypwN=OIlu_wT2X{BNcXT1J|s>mhtcy4ukJ^ew% ztme9lqW=?Gx7V&MA?>xRedF?;@Du)Dz*f7inBz@DFPf%stWM9X(QD3RQYo+(yd1Ft z1IKPwugh@8W?Xi!UlbHdNp?$0c7wxPr+XM36Iycy%mry%GX{A=(THJwpPET~4-$L1 z+nQ~>Za9ONL^Po& z>An<%y&c9SR=eRRMSS6kcpz*Y6|E9RhCL2b=87|rUM)`?Ct^`Un9VglLU$=?)hk!k z1kV}G1PmIMdi!NyuSxIK?fCU-0A~h%%FX_1J+OReB!;$~=Bn+_k3a3urZdyL)SpY8 z2G&|^tibU>Ky!aqeynTq2|IzYg~j!Dp(mT`c^~r9{KV!3a=6rEd}rmg%1b!#olJGJ z$eeJ_C#2!sw_R+=NvI#xWy3&M{*uBUm5iRwG2HOh>+_*$L~&`JX7tK;Nfr0Y1TUf0 zgKXqZ!s@8uI}^2Yf5DT^vm?-8CyYvf5u*u#li;{36kD0AtR>I8avTZ#_r|-8)8wH( z%b<>56ug?;b&-BDOfoVoR6=8_UM4+-FBKFGyUUXjxU6J$XrCy7p!`n65P%T$YZN%{ z*489-a7fgO}(=~D`9lyl_L=$hL3ed?zA4)>%fSBchdBQzw&zx=;o-6Npd@>Jv>=gk*)c@P9gJ_u+_&Wt!3-2mz6R6 z`|P6!3kP59l*$gt$7LnSWwUZfn>aFE?1bqF$^Yy!S!-PrfBN`wd40EEc8d6|U{20c zmmpsGWxCH3GKutoPBV7e2NcVLz_J8h2UT@t$Ni@BJWfWe8E&)|1+rdO5Qx12js~Zg z(5G)ICbuf)917EZWwnzciyPM6mwm&gx49+MpN6!FIL#_7+t#0L4G){Jese@iFUo<> z5)N^_sO;`S8m_=7fPQh@m|Yi~ObpzRJ1%<*4|AAfloSPlE5z%z`gUm4<=Ro>x)m#6 zI=;-|u>G=trh6rddxkl$Apa>agGkFV<8{TGy?ZfKV5Jx74_Bnz+Q>(8fhhutqf5Vi_j;*l00 zHGt!yTXc~-}LB_)^HfV`qGSDYUULtjG2awWRP+FXV4#s1P4^P=t6=u=3epLHA; z=rjpn12HU^8y+L*+ioOuaYKVO`!&sZ+~e*I^Z4Frr#~m0n_M)}HMUn1;ib{?{li-D z`#?$S=i_&E@ssonOsllGJ7&tX(KY8&*^w_pvz%CfVjHc7-PEWQDH%9YG-B+{zKV2oIFe;~=JP_+!cIGST>`YJa! ztY&W}JLcsEAMoxqhj~n_P?9Q2uG&@!oej!eT@VW;IEM{?58&{xWIUY*Js{PKY1nc` z02KTv`e6~mtE*@TW-z1AnrG|q+||_$IEWRaWBPM*9k6`p1C@g^$`OccL`Fuw%%3F7 zYm~@NMI|+P7|f(%no#u7gT~EM&a%=mZs^I;5s8c#YybzXSC4JlTv-^u2OgR8Dvchq zgxNM7xLpaZsDWNoH%lt-XS4%`@RsonMo z)2AYPRJniYPATWbN@CB0Y3?X~;K*e@H8eTP1dNamdusl(bKE~nEBTP<55nBbd{jo?AjZ79&$@l(+@XCeQuN_Hd0V>5FG+97edjJ zNuYQL@OFGsP;uHkfr)FAUm}YorffG)wyRG=Crq@htXN6tt%;`0$4}`47k50+n-=z* z1KKK4v4|-uulvp_no>XNi_{wFyd51$OIr-g*;qc=o%H%$mXTeZYxZWFN|+E;RK{yz zXYOW$I3OAwCDQf%$B3iC*Cg1oe(e~?z<-d(_Rr9x!h4`4Wfb{axJH$+aqEOmt8)?O*(Uz-k z1SO-oZdej!RylYF8y7&i7EI~aE1Cdd(tL?x_x4&m=$JO89p7+FDu;g}Yn-%3`F5}z zFlS^xmPhyW^1L71Jmlu$sL}McV$Wn+k%wk1&M<%@jMIX#Ue91)=N4di2P^@7SOq zy&PJUKY?G>m*#x{mpq1IHgr3U4eFq6~(NXH&dTBeknc4M6^_dfSu#w5EL~+tSi2_MX#$20& zstvoRuKs+}>TjmvIiNf?AlQNp4DrHfMA0-B$Zsc+ksp~t#S&)>e&3X|MB}vPyaflr zN029AH2Ve+WkB*yE*=@DukQLm)Lld}#tnvWZyj+wI$kV3%qAa1UASiaqL)!j|8g%l{HYaV0MRXmi&V!^Yn~r z%GKkACeffs^j{mB+X6OQ9l)0mQ8*5gg#tOvDrOJ@;Rk9Jx=Z`|{shw|7M2(5c(+V~ z9=pC_Bn7qHx6A#W5%iW*DfX7yYM_hdcyfz5H9pV5l^uv5QPgqego9@**FTLve)`xS zn0hBELYD<|LY7vK@n*nzj(n;0#${2J#{Jrek@3~6)85DJ8q|F?HTiPOx%1qVti!T* zqJ16L0&~kbxIZ4F#s51OfCl*3^p208<0lai<;yEM+%`@xI?<#J{;`w071TO#=?V;l zB^2OH@eIt}9u$>^jD^1vRc`uD3W0|!nT^q+TLK;zknTSxcnwce_^4>Ou$WPOvI7Lc zJ}q;F+iBgk+^uDrMz0U`cNJx;$?_L>K6KFTW9QIy`X%g03WVVNcT{V4F7kIln7YP( zxmCi`BjTVaCXN?GY3j`Si=T|YuCD``J=u{gM#fG+ml%_->yVk*Vs@~*sdcJ;Q$afp z*QzvFuR+CP(dPF$K{|nWp|r`b?7tMPPBT_EY^BB0_fOn*WHc#36$;4o!@mBn&+aUw4La;}NW83@VCJ%%sDhAghzldB#RVvGk#d4)J#g$o``~|gM!Ry%C_Sgc`j!=^C`GK1}W`U8btvA1|z9-;p>R zn4jmqxnQ}Ilju|~=8H0ypAS$RK8&Dj~!4tJBT6AC>zSv@b-q6k=E zHP$=zx98fGNLm6b)7{V0o`<>GG(xmpWF@U>bV?*-wO8xnrZGb8IIy9QQK8}PBf-a7hwG^}br2~OReW4eYct&w5H|FgMH zMoEczf#REd0|If)Cak3S${M;w)2ycCrw`HxOHHpcquVZ`+ZfQ7;E7SW%2LJT7!N!T zu6IM2-A6}0-ckvHUWkI{aV;`!APMks+*8>2(@hpy%d63m(U8chY>L|7nVFXMt73$$ z8ylPVlB9^4>AVo0hhr^A0|WD6^|#0toJOEsoq=?+>Nk9p&S%&A)zLYbp#L;C>{5I6 z(3JrFYPtKb)wVVr>*c3=#f(&l!{p`a6rrox`cR(t=vOyu$(}h?MK`nllmRUAN*33d zz3pu*Pw|EJZZ0}sGAFuOwTOGv3dr4TWl=d_EfqxM#`JJ5;Z1RJMzFuoii6c!`U+%o z?Ije}b2WBNJMPHlU3KlpT)1W_1t=>od-z)h5u1a9!NPy&Pd-){L8vtE^0N3j2}bar zdCAwg>X8VJ@n2^6yUSYK{WH;rp_6)(QT8g6Ylqiomva<@lcG)GCA&39>-C`*RA<(U zAL~tfFTge;$A)Tn;M5v@z zYCj259-0=crR?+-JUm3MS6a-RBf~eg>q<3Yp(e)&oPDzpP`%OIFW_-L)EYo=KT@r} zL9Nnb{CFxCsLyg&%lMwPy=TaXbRC^16^% z@emj9b{YM4%uT;BNH}Z_WMXoSXnXF3w`X!q_0aTw^xpQ* zy7_TOBstY7b&x*5`1T;6Lz=5PVJorO{`80@L|nHmf~j*-D{FJJcHDJmLlE~;b++29 zkm;q`$%JHjIP_qdKM94;sU(i$!}437_sj-AcF;kk3+Q6_n7V&=!T;6X{Kzx;Uy(Pv zx22%mVM*sT2b+I^?9woqo%eaE7-841F|ulq9j3tjo{S= zWbNEMR6gIFCb+fhg3X9_FA*u7{Z!Y+GFl$bW$UmJj5~Zw)>Grq2`*h~4+7Ju!#tl3 zd&m9HOHEZXHEg*F(mwX|KA<2-K2bQF_0575-@ZFt5r8~V*JG~HGqvnm-vZwIJ~xRa z7nA)`dEHW0ze&}=U%9;vbyeHA?TIh+m=BGd7e)#}1Ux3u=ojKx1BKIL&R)JLMxPSp zZ1ofU@eXyck{Z~B+ITLMXtGEe9E}tSm#Ljw(g#m{F0?2-b3F``v(_e-EvJNmoI!$n z1EegtK&Qs#?P`qz|EGNk1#{akR1s~Tbd6`m%=_6fhEolzrjL@?3faOoSNS_E8+Zoi z1T{CC=-zxZ7TJ8@$q)(8J#D|Q1*Hx&!BQqSJ};Q;j;*{!NF_lDr0tWPLgbSN8lhQ5 z!YX8*h~C`k1DX*&Q%vU{Cm)TG-#L~!FEJAsx+4e9-Q4_hn?{L%aq|iqZO^_V7x-lI zE{n+9Q*6!eXi73~YKZVJ2C%_O7QK|h*{k;3&>Y2}-tZHO-B&x6`Ep7W1}mj|y9)M> z+kh8SuilYVXvpoVH>C>5=9O$j6jNsMcGZ_EFAvBR130eHthN`Dj>ku(-9_rJr!Q}N zQq#7tY&`cjcE&{q`5FmBHj1)*%y0kpz1^6D+It{5Zfm2;+f^i$;FuAK0_J0@Bi9>k zEzxB0>P~MHn%@1lk9nWEObuJq;iCvV~0@f$YLC-9tSRIdZ<5Kz%TGpi#Leb~_ z0=D*8;d0!t+hei2vsGLHv|IA!eHz^JJ3}!a5gDf-^9dG{ySwf^6JLfsQ+4)Pb_hM{ zXVrA22Rv#VREv-B2;6)KJ)7sA?E8=w&`p6=XkLo!)_Egf+O1*A7RMt`bFDZW)y2){ zDriiB%`I1eIfdkG{4ql%S~?GCK5bzedivL%t=h#%^aB1_w<`^hZ6YAoS}K}MIBD3J zmI$N$`Z5bA30Q%nl8PS73`oqz2>l#Sd@z&ZBEa{ZOr+7{84b1)pVfX7)NcGds#=Jz zBtaSTk8D9mDVeL?j0mIed|LH=k~}re+H;X(+zjc$N&S4l9r^3dyVvB#1=!`dj)}|L zZuX?xAG?KJm=tfAW982Fa>Tu+!#64-u}JyD374h2HsCgkroMt$ZwD*hueP05WPBi}s+BNgsv$uOjuqvjeH)nXEq8SaliLQ>C4VNv4gMskt4qGp z;d3@n4bN)KL5;)kog!?|OwBf>NEbLY$*5GHAc492#$7+POM4@8>1nTWrVs975d5=A zqWg`<%0dFdq%9zze1GHLQR7ez+E^c-odr<@l29IH-jpw~ddzk_X~#dvzA#YuXSrL4 z%jWS7;*UaPWOtO?{(K*_R)%6YV#1(nrzD@A(DX*FkCXd6H_3Aill#dSghe|{D3$fg z?c;gA@aFz^#rPkVe=!@3Y%NkxKiad806+-$IvWpa*<7X zhZbCOx}MYa_|B}pBu38iOjgGaCuvyZdZwu^cH?X|f-pSYg<;l?d1+uO_W8V9YeP3U zM~&gY^oPb9;Hl2EN@MZ!mjJzQ08!Btv6_zJhm4Ln+64{T$Npts7OO%Q%19;R!nOPQ z;w4vtNo-JlsyRtx_t_J)hc*TjHXaqX0cxO3CC# zPV7&|A(56y**x;rOWOOdUYi#fNO>N6AuOXOY4bl{QC+4rt>e@y^w9kdmS&^pmr;I5 zDjN#9Kb*Z7Z&LgStB!eTd;IB9jBY*s8LJ?-JP%jXd2~#v$(VKXLfl0PAv_laXMzOE zm?sGs^4|-i1OlNIr3Mb1hEFJfw)DypjwwY!093EBnybNh_yb-rLnTKourqrQ#F08G z+R?EduU*%>4n4BO0TRWUrr#Ld-Mt0&R%(+cv+1U*Kv(}3II}!eYY(@FgK)a+ zV`c?2iBff1|7+g5-UsGUjgIzQ(c?qn8?Y2YzWD;(K>u;FB2?H%mQ?>C$BcpiIsfM7Y3 zi}^aOpQ-b&O}Zg|H*bWKnCgD?0K}9!+XZgu2AePlH}Sbw8!Wb9(^$M%>$e`|AVDU& z#6+*E7;9rjuzJ*qRF$*dw3FA<>Wjz+t0-&)0bNqT6A-`R9U(`x-b|UP>q2?z4XZOP zi58hEyVL%_%)l0CRe+}_@6jU+cdD|Y!tzm@0=r6|WdS~2Ay0mCSvZDCL1=yzHk+#^ ziQQxELXC+TRY2owo?sC4k!@^zBLE*OwByzBHC9-K8mFfvmgf8DF1?d}wJb!rL06a5 zBpTVLIlSi@#?8m28Arx9`HO-G<42%G>yB#4f>9EG$)AlLO&bzoK2GKSt@Clw2QxRC zOH8EhGtIrsPRQJHtN*O&@;tQHI!m-R@dj89H)*@mzh4d)<3)lh>nM4mC zK~SYFx&C+?v&P0HUCbI8{_MHFLuj-_!2QxK)vqd&;LE95qE(=fF?!53*xk~A>(#f` z#r`yc&h~G)?Neiu^Yz8EP{YOVAHsFB!&<297+HcYST3$X6#J_ghTYLaH3L~H`i~UA zW);32B+k;_a77$@`@B)i|8<3lQ1oP_skGGRbFGoeM6?_UNdXl4IVvMLBcM&va=LDsmUbtH%9^N?=)js4&kTdB86!&1NM%9)hyk?XTl z<){Zde~b8gb||H$rMh$Ue>1=P>;1b%n)3Y4{GSTxS1{xo0z6V4pj-#GmnG%iIlO-)z(Amcm5Mu&}cDF zzDhxLyyxxi+m~PLpAa#+KN_#(MKd+WckhDe1*%kvy?+hj3%QF`>sfOgGP%2z(anvi zC`Sp0WJ570r76KYBuSt|ztCvDx-!5$gt6Sw>@~=6UU3N{xVb#X`3tT$*xT0|v^_|< zUo-0MaFDlUGd5rD6JjqYST~Iu^A-HY)P&+E`Sho10pV=GZmgb!o7h-Qw}0mx(T(y6 z-d=Fny;Mc$vl>Ol2~~?N7)G@5qkAK`36}17e&5O)(Uju!Rm{kDVQA6F(D-UG(l7IUAO7)j-5`m@*18#liN#O!y@=U~U5TR% zRN}XxMt^m;5!C;VeBuBjSfjwsLA#+|zSc6TP_0Bn=m7x5#5`|Lg724)LSgj4ljF|V zVP&{^aaV30F(^k1`fI@{L-l^dS!ZhE+y2v0okOG^hTz8({EmBAinOpvl!ELK55 zZ3{^RZAMlGtsdH6xYRUK1o$^?o%tHczuY890qex+uK;hjf^3AxDE+Z}2eM=^>p+a@ z=%)^cig1OLw`zt#p>5NlPOBtKNv?vy`D$XJIF1?a^v=$~zCY6f*63IJ>uhI<4}gRf zP3h>f=l*b}Fke_(Z66$t+5@ zo4Sygh=BU<6K#vrAL-fjcAUiA4i1CUA$^5wFVC-E?k4kK2l6eW1~jZ`1&jaF=wW_e zM>@Z!{Y;+3eiY}@dw#c?@v-IBX&R>iXzi3^#-YLYO<(9#>7o-Cmq&h_1%S3So;{ZNkh&CCbu~PDflXk z8EQTXJ-GiiocHDnk`NOR)v*jN$b~0I1B%CgkMs7x*2ia`pS?hdbcJj?o3J|Xls3ga zoxI*A^C5$*+0J(D$(}+10{bf&(#YID~E6^y2ci{)&$p9nR{1G4VsBA3_Hb&z0cnR54 z`&5}REl!x({j2Q?kj-ljkHX2rVIn^I?%xW{9B}!?&)q~F3`#Q6eN!l?_i%fk2@8*o{m?BH}i=WeU*#;971pOR_ zf2i1y^80T8-Jp7d!13YQI&Kie&CCYl(+$vky7{!gUuFwnMu@|tVZcS4Cw05j;xkP? zP{-N={1cWM?6_5}mcYiYFd2Cu1#R3cwS8aUBfv?fjMNGD%H{z_q7)XHV#}bVisE_} zRzUE_#gdHERZz;v)RYdeS)w%0kYi2+8WH`YlAqx}*cR001Qk;gEpSh?8CwhW*9L=2#{NK9U2muwz31~quZ0~G zOTxXHcn7Ln;GG}vIeWOC3?!lr9YIFNRS3?YOs1oC@ctTGpWd&fTO#fa_IFyTtJ19< zj33mhDw1d@!&6U%1cgQ|w}~X{YHyb$m=r4TLyo1}q+>5~^^AZpB=VHjPc-J}Aa{5; z{Gj0Y++tV|3eO`pXt&(5>z5=4;gs1I2d8=5JhW>;7OT@Qe=W;Jovfgx-p-uP2!& zWer>_!2uvrUR$0fMVTTQ$+dUvdu4H#xF9F$nwn<2S~eCT9bvLcHF~p&1In1h%(dshwms-7ZPhmW?mKAUw%%Tn!m;<~Q7+-q>RsknC zOqLRVf#`2Px22`CgrQ#h8BGhzH;OwInvYGA`jAnm9JMuPa*q|k-lbyV_Z&kbin(Po z(W!**-;YG4I?a0BV}bODc3bqV^SiU4rQv!~+B2GdYmPnca%Ul3-K3-=k~wBCYN)c> zL91sfKFJnpe=N&hUti5=xGh+2QGFXRJU7SeSFg#^H=8QG>e$o=z;e>=+whjh^~sQ~ z-CGGYe!SGIOlA{;IA!&z#s?q*x>bgw{5NyVixn@o+SBy`cZ?nq>3-`}6dT3>v%1xF;ikhp)zO zLrs&S>3xS45lyOY8t(ZLqE7Mbfv|Vsxy^_yiOrJbw!PeZL)SeTz%+~DjS{$d-UtJ! z*)LL{0QOm!P-VSbnkO-L^GrFW%DF#oxBq@IX;W77fAN)+|LgJGsmAcuGcaGMCHAcp zzQJ_98vWzDj|XeDd{+3g*@mqy_UuKUCN`+(dX8)Z>102T%1Wt2b2P?8FPW`7oVN}W zrVOeH;XTp(WyMT0GYRyCSbOhAN3ekz&8)UE)4+1NXk0aFCTx4G9*j%zPci%kL-9_k!g z$5e0O>^`#MJ`9^;Bn=K0Y61NdLXHSa*q4=av1ftWIV(PSHN3jyNP@WYzF9>81Oq5s z?3?W(jS{-?^yl@AhVWMDL7Sv|upMqvsCjZ2$tR%N_`XrLsO8(Ozw-ir`*ZqjUNg~Z z0l4L_uTFZR`uk<&+tGoc0Xw???f2~I15SNr-j<9(%PKud-ULRR*}=3lD`jY>8M~af z(}*QI%{7j`W}KGB_}--%5f(xq6H}>9%Sv_4=Jf;4?8Xt6-1q+R>XJ&+83LmwiH?QZ zFLV-bSflC5a@ip-GGWqP+4F=tO(qTEM&G(OHs6vDV+J+lu!c+j_K@2&F#hP}>7l8^ z1ftcj&!7#$nqEec*~Ibxnfm~}dq;x0B^05)r=Mj@hhqhM4@e70Qb}lQf9p8DffGNs zG~yhS=f~qbBqTjb``H_(?oO2`r>On98@R?#ce=LwHZ`p*K6{*p*mdo3{*;s-;tHV9 zk)^Lnw1Bm1Lv0bE#8Z;!qb1neg);-c-VcQr%bu7Ry1FFxOC4E2j7z@@*^zhTq1TCi zVKU>;acB$G`w`hr-90FAGpCweEYubpPmD<*&Zb*2xF*%P9{z9!4;-$p?(*|{MYYqK5sNr2Sp@P{ zqVd$(YY1kRMw*O+l9~Wo3?bs*16?Re-5tD(lh1Do>${L+4t|e;;Q#XQu0(5b-v7BF z_VK3{@yU}**5kdZzm~XzKuMTM;UWrR5D?r+5G?t;$>wkM%mprAeEvTzKyIe@;W6?l zK}&>Eh{BBooVc<}E1k|xAc?TX8}C-S8`wbN;#{7^cj=rB{}a(UqwHG zVFa`s|DKMj;G} z{U#n-@aTq?Qjd6%qf&I<#_y@+>=-#}QnYfx#4_tIh&MPj8<#PM`WZ|lw*mC z`%s`(ESV0?Oq2FL_7wi-oW$3R7*M+I2pHK^A!bkR8w+{Fu#rc|WH3c-s}7Rn&5sos zu7EJlkbYx$BW;C8sxg>(YnTLf{)I9nfsH#B=U}sQt2wp~Kg(Wr4WWQzsoCPy-Sfal6Jy{g1P7N%HrAf4^KHpYqnzAH+v=|v zA?Bd_DNAm%Xp}tg8$mx>uQIg~^3@jLQm-}2RN_aJ_%&$x(=s*6c5$)&Y>2JE@P{l_ zOhN*F!|;KTu`|$55t1$=t-?)3s%HM;&vrAi??}@(Bu#PdX;9#w_ zi170WNYtcdYZU7V9Z7GlJ+z5YdmZ4aW{8~6=pSDJh+AHCC3K7wlLWX-yG&x_h|#8(wLIOM zn$(xrp1LmNzv%DnFi@p3VB1S%Cf zh6_JwokodHN*~=N5w$ZvX@W+M4uhwW%EenLY)kbb)0qQ5|A3m^n&f+&Jp zo{`@Qljay z>tTqm11D!PiF}YxW+uRJ^c=l!O<>m_MSanElS%?i5;A-p|k}sajTWYGeX7bdkBwCmTVjJ z0r0)V*_V#ezng#!`kWfxYeA(h8ohSy2$Rm*Ryf2bF!0 zc(Obu3j13=wf?zd`}+o6V)w)a@`*I%``XFksjt~Q{loZ$nHw7q9<9aB z%!UDw4AY(Hq6M!>o9CqI7zYl8>Hi#~AZ%82^C7hqID#=vmUx>RdJ6zqClPyzadD&Z z67<}%({*GE@Y{H}YPyVAyjLD^(<3r0OG2f1xn1)j8UAs-zIrkj<-YoG@e;E|7wK(W zoDX9GDotnEogGN3-j2lgA8fB0d4-O=osUB>))6yq>x<{8a+{XLfLN#Nj!hCGZrG3W z*lz&<$Q{f6YAet9 zg#u+0!q1qX;lS-IKuSdUn7joMuuIS=*Rb$tC6^#@Z4Q?C7>Mv~z0huBN23ivzNlXj zd5}hA(CZq0;2=YDZh1Wc!R(O=A5TIma1WGp>3ZuYoP>D+A#uo@mzno^IQ zK0`u>MG@m|(TY4Zz@F(tWOmSBbUN200gotjiXloD31895{+-*$Rxsf`=3=1jSG&Yg zo`d%{_>Hbs;=UwI`Itu~HQ2oQ{dSy3Qe1u)*B9@{~z!B=gn(wl?rtkF3%vo^>gkhh*CeH8;m;_ zHuZ6U`y|7pp_B;U8ji2qf-s+L+bK(rUkZZg82ba*llhZAQ!+LoOAU zpxk2h%G3SB5Q?9Jmb}2LRJ&wuy;Pwx;5DcF>Y73rPe$zd?0N8fvW7KeGspx1zNV#U z=>sv;{pEgqqe1d?t&Iy{iM7l1-^PCP$Nlx57;54hB5hsxCx7Y9Q*5Q0oK6WtA}C+& zU4qsdm-D!2_D)O$iAlNb3!5z8mmM}bRRPj5V6+Li*zlogwP=EQw~avUbpcEa_^Gc~ zbJzCct!EY&@uSw{0{%~j1rcd8f^cwW2tm_-GIx8Z`Wxf3zYfWUlO=4UYVo+VR7EVX3I?K$fGtosB=UqwyMcIx zaG=aYeno=YSXyu9-)6#Z!KzJ0$ zitbCn(PA@41OXf9Y;>kMleU;p&gWeB+EnS*|Gl4pjxCen<4DcQZ<>>4)x1fcM`X^z z!a$0jDtY#cEsaK^_uFcNibccV<_CAEh_{-P&36x+oy&VIVwSa>O1z>EGU|UgEgEG` zmNoar=%TgD%O~oXAO(zG#Av~ZStA2qCXQnNWBub9N5i{6*@NXXn~hcW4($|n*fDNz zPWn`kD@{{~hVilWucX~k=S65(Vjxour2%{YUbfGdk4Gkge0q6v3l}`jsv6rxy&`k> z*P-@tUxKMw!{|1nXli2kav!{j9UOY6PA@lZTi+h_cdGre3}z1wG*xyN)II-1knrJ+ z4hArVU}r@?<~O-%_#0T(P5lACudG_@V&1^}>t<1t_EWi09@>_#&zU!=_4X8iKAT`E zW7jVYjvlXf3qMim!pk8F0F&|VAS(v!EYXlX`=^dhwZ+?gL`hYDbT^z2P>xux&oW`4 zgllyiTRWv*93~v{Yg^WK41THBYi+fp zW6pfrGP<~2!~67$3D0s>59w2 z5^A|mNXIMKbz zK&*>slcLD_AqI;1vC1>c;;@EyoqZbw+2y5l#@~M4`FcB$?Y0^FwbY=3*@-Pj$)Y1# zouf>I#&<3>Zo+{o!7N)lrg{&G{%VU0;DW@xAEuiV6H<+CF$Ke=H65&fwCcF_^tSdm)>g+_p0&) z{`X9#y;_{n}Ptdh{k>jhb&*aT3tg$Q%$;#=of&MOOK@=sa zBn8d*A=$05ESmt}jHx_|W>g`5EE(KT<6gB%%j4UgEk6fp*!dY_#!i$vR}Uo~Vk>XF zfYI40Gu0FMyflqJug?ZBn#8|UtJ$TB>;Nf_r zuZO|Ejhuiouww0SdaOX!;082g!qnpqqt;FEYx9liDuG=eVDT_3j^t5V;8QBBE%LAy z^hn0eP!aPzrpCy@M##bTJ?hg2>Hh_<5M>qfIFPdP*~F_Dex`d7(~%tFgV6ICk*Tuk zS`^*W^1n?b(a5F;Y-~kLvIar*dJb-qZ=(CHIEmaOb6N|4xM&7!6dZeSvQmf^V+;=u zkZa1C^tQW-T;f3g4L1aNdE z@aLA-+l`iNh9uf+Ehj#33yR39+_CAM-kTdo72TjzRX4xo%%50rRAPcfDk><(r?~w7-SLX* z8_xcV?5RIP`1Wyqa@0y?P2VZ#e}b5!u-6(AKKVUjgGj+|dT?wNcmW@Kz#(E}oBp0I zM~{-mRy!Ab>RMj*t4shEx$=lLmD&1wRDfv_Hq*380TUKRdY0rS?v*_Vjm|Qd7>mow zJYHy0H0p>cws5y@{>F>fm+KB5e+j}y0D$J(zY@%kK^!b0{=LrN zfnKs($RjFKIN?9x(#=1yv-Q;#PGhjkW41xQiqYC?MyFq`+#fDYunm?2U$#QAHB9*$ z9ZZ3enw28ze&KqNtWc2Y_W%Q$O|D8};P-!iM1K1{eOTuJG;~};gb@)D8cn83=5c*o zjBD)N0zp8!I$a6a->=?2uUYZGT(mmYZANsUjFDq4)u4~}L&n+)NQ(iqDfp}2|9f5; z-I(f0h%k^K{*(;gWE^V#Vgowg|DmtMfAP>6Vub=6@GK(@U(zUQQnxx&f?WY8LnH9@ zW25XtW!pN5P!QHf-KyZXyOI=R864=BzbVEJ+p-kVDZjacl>GeBB}kk1_e#}L1Mb0v z2TqOJ$OOEDixevLn?1C@-$uXd(T7lq`ss(M3P%OtcMlkMU$spk>AU#o9wh0q&p2fG zz4ZwTM*lg^Cvd@EG}2H{MB*R$k#88k1^EUWuL3s=i%eAKMb<&{thOg=3~V95fYA+b zpqSQ<6>i}}?1IAF51tOS|D8|FZ9CRXahh;`nlwkq{X zRkDc>kr~hS(Lkn~1*f$h7v>f%JRT!Yk?-Ne*TA4_p{SIhnP?#(u@63hiMvcUZx zd>|o}^NV5HwCx~Ut*IW_U`eQI3szF1H@gjA;Xibn^9$iBZHWMAs7GCVvVWB}8kwlV zDm5^(>BIdJC{Pxw?L8vW=|8Y0^$U4#;<^9M$S4`X*$4E($Gqt7-hcq>AyC7|65)!I z#vJ5|kcH!Hw&wr-0AzJvbKr+fftrBE{LU^+w~`&Hs1)4aN{awMPXeUF*69j`r5^p~ znyN7YJ|2#ybYrt_-jG~4-Ff8{V}hW`?CvftK=SAFBCy#jh!yH`cBX#p3cd;BCq+1C zTTgerB^P9N08tm0H#pkdxAxgLkZ9C_f6h1%pi2x!YOUbrr*>*_`Xk7P3KvW81H z4M1-L;sP4SHtIlxEAKoL#~wW4Z@&=-cewLxS%mfS>)PTP{}$f;$8;Cac2n|yIJ+!} z{1Moxppsi(WR9A|Ms1o*N3oL=npJ`P)Q(rK)CrIlt<{(xvZ#p)Nk!du7Y>Tz#CDwi zE@j@x!Tsg!Ol8o@^{q*}saUl}oJ4Z?53Wuj{n*^m``<^P90f%PB6+(~J1)Eio6V@E zjvf!#NotqPOY)QmSIFCC{Ub7QL{Rm`+3)!~mGniS%eZjAkw?9h`b_rlxO|;2_5T#^ zIRFnYqWe*k0=);X$8a~vYp}`J&3@`@+N?^^Y{4+k_&LnvWd3jWnn^vqWgGAc5PQ8y zB00{dM;=fK+1&DQUwWKWUCmg|suIMgS*JF{r zxy`}CQ&e+;U#OxvLam8HfcL9g6UL(tx*0aTXN>6WGAT)RMi0-;@_YJJ?=MrJF)ns1 zS)wChUz%j=;P{#$c1Lx*cijA}ivP`|!yEN6k{<66D9pouG>kF*)EqY4D-JIP9~44n zcM7yW^E^<&qZ9+cKsf+e8MWPg@F&?htHvE~ySq=1*#J7{)0NjQMbp0~sG(ATqKVpH zw{NE%ZG=^EtoxB9#HRe@<6c(4d-AKO&|ZtSr(x6ni#X}LNq`Szup$##D26H&v_I`s zT9Z`MYyGy#d=1V`Rgr_bO99CMtZOI|e+*pmaYxmp|o*+?H2%6lxr4vbQ zGy+h>spJv)+tjkh8v3CfCxcl#sKMw?nIFwBaWi#Cy%~~U;&2PgS^|oS0wJFd=Xi5T zD1@f^3ez=)(qTL3e|I3VRPP0Yio3xMN6A?k4A~dyjJdP33GlyJh`3|E%?mN21#7e$ zHQ@(I4n7ZHqg+lba}VR|X#Vq5ZnBdy+=V1wNV!&&{h^=FghEGIXr-{$jH|U4CmO92 zNCDIBZJaMVh@wrX25C}2M9DAA%Kkb~L!nRw-$3do6h?>G65!BV)}H)dL8VrMPrKjJDoUko#<$UxmNN-He7*mintCNzo&RY^67h4{gI_x! zs!0!cK|D@~s`v-IWKN#8?~0dpTw9jqODHo`l6bipwGJ~{kTntqK?Ij=OF_Xr+3Wzx zBT;03dfe6Vm6w=vECBOJl+Tmk&t-~zRPR+YV%aXh?<2tvoaG@iXz~Ut_-)Q;ve?4U z_0j5&^(huW`~D2dP+&KDpLf+}85&xa;fDF2Gz8o(CPZ*);w~_~un`IQN38O+OLf~n75_3dZ8H1VfSn2v&b=(+ zJhQ};n;hiZn~%cj!U;^;LXXI2V|j?0_H7tX(p{T&XdOn*{C%=yr(}AHPjvRS2me+$ z0MW$-y0$#ImLc^uM`{%5BQYQ}Gnuj)Dnl#w|4lcj<$ww_7d1;|Nn(%Nr2ouU8|*!F zRZnMnlGQ6~la+t2MT=DHGwZZT=W5w{Kf2F~rSNU_B`BiAQk~2F-_v$C;4~g3I_nKT zVf_4-Q~$C&ZJyu{i8>P=npOYU3ZTXK`CTpd1v7ZiESc!z!O}{W@Xq;t$3@!f z4uJ6jdOE!^G(e(K_4>E7`*WNhjekkikD0H0)NyrY3~ZlJY78%Ln%<n(hq>iF;a@Y_4);3o}RiU3ol<(>TDL z;>8|F4zaYvlp5Bef?HXW)I0Pt{EvZuMpHVqBR=6dtopsOq0e{}^izMucaavP8&46k zta&LLcIi#;R%2M*Fd^{$Y-U~F2d+1B1#4Vi=(aaoHgsbj-adwst+F*P8qkqqt+abR zk4%r7RbJhk9G)!UNB6I=7R_(%I0;Cae~cLI>tMo0F>1fF?Il59D9lz%5JXR`H_z*i z<|n;5pSC$;tFQMkGs&6!`a?3u8XfGy%+hvWJ3pMmPX`Fpl5(2>Rl)~F?`78LrC?J` zX?3llS?kjKf8`{AKvCWp9iv+{LkQ6Fzb}Qwb6)^bbF>1qrH;sciwN|^D7WE18P9J~ z@#>bC0Z~j=*(W0Ls;J{$HeXw7J5Ew8j+C;{+ka*+;#+y$jL^acM_m92+Yr8ON*c=u z09*CGM+-TWc5|b2R@lEUP!+3S#H7sRY&!kfE3MxE*P~TLgJbsiD}Lqcq99_fFvbRC z)2#`pX-f)Dah6x-RCR^;iaruH>?&D80h=YYOi@+{=e7EvYUxxQ-YQUoX@_rh*9mE> ze|c>SA$qrv+wMVdQ&YN7lE9lbB~&k`Q?Q{A9dEsW@9__bhU9if1M2K|jFB3HU;t{J zD?x%OkD`KOG2V&fPaYIG3&bC|KK%RMmORH!0PWr6Kngnl!H?6wV#xQI%qB51PX8ZM z|8Y%OHvPlk56QAK5+{59`g3E_3yUy8n||?)U7gxuJoFIzOMbiEt$>UZel$ zA_%U@sYYdM{NU~RI{9p|GM@y}X}u0CEPtMu{DK@!ru!RRDYeOp$lHlcWFbSLU`{bV zwZ0yx7UAoCrsRXp5uvj4P0NK`N~Ab8AdAk`2M-2ppU3cq1Tx~$5PQ0qq~-V9De)T)Syb$Ib@ zB(rIWr}nVPoUn}?s}>%c@Ox72d*5OAqn@*gp8W}4yygFj{XX((s{jv2NB~gWd3gs7 zmIN(XF7P^`Fv}-cbihw5>oTIruK=h`{-p?5O=n2oZ8sBV)0DKB++VL_T40D96b5?J z!Akgt>_B}tX8aUOG!$?>Qd*kjZ&_d?ifpyza-+r3%1{BIBAE%8^40;}ml2R$v3^|& zv!ktD){TN$H31{K*nQ`1y6P^HIj6pI4S}*@i`L>V%EpvbqL!5B4&d5c^?d$Z8b!xE z%YYZ&*ltpx6?SxV9+(DjqVpEK3I9}K*|II4T6g=v4Ftbng*pvce2925KmvQvMxBm5nJntALR#+S%Br!DCmfOERL6 zEWZsxlgF3R?IS>1@;{AjFXRC!A@M7Bmqq31CK0zRHYLQR#GtDaJ3Mz`%VO_uyP+^=lwp8xCW!ud)836tE}G=8T^`DQu$)azg$eG` z!VTSmks99f*-C>OIyxW~8Q#qX$RJkVhsl{cy}q^G;H4dJr_(W@YDZMs9XW4oy>rkR z9xNKGfk~q;pL_a(EIrQ_t=9Wd>B)b(;+O(6{r_eJtnIlqX#}2HL3Vz3$0Sj6wBf+( z=w3u<;3(#Edi~z|Grz;=-{K7R9s+S6i#Qbrm^qGN(&5SM*%Xp5ci6LxLx?`KnnZuG zm0xTmhs_$YFvv4g`Yh5v-ae`mUgyuH#;kKgZ~Epr)rhIZ)IMXxGmv&`lqFCm@`?m0rR z!6x+kNfU_y4$iss8^u3x;sR&hMhG8|Vl-_uO`2SuA$_sm04e>X&nffBIM5lZPzE<+>A3~RtP)q|f2F!#gQn}+oB=UyPDYxL@72OTk;ehy z!=TQ;S3Ny#LP0$erqoG~3ZgA#a3+;|pVxe@*EjNjyQ4Fdws8IS9&FSmERacshamA( z>GwKI)vx+b%riNbCbRzp;&(Qd=AM?I!WPk7szeo9BbhfzLd=Wfx4`m&(#=IzqxlB( z&TJu?0zE>|OVP#Sv(E(&Q0V7=a~PI+2RXwwJSi3F!y!$}ywJjO?d~l|T;=`qPj_`d zfN@EM@@r`ZZ5sDWkE#h!?-DAX55)o=9p!Z7@}@f;N;^^>o2_bpq-144W%B+Ut2&_s zjtM`HTr&|ukwgXsRntYM7w1%r`haR;q!Npth|p>lm^m2vn^t_XXQ;+_u`y9ggV}Z= zV7q7GtQ(tEJn=sf=(YR51;lRl@Z1==pMy%!p;4{N)cB%2n@~0CR5Efv;}+KuXs)2C zsCZf-BD_reXZ~cUID{d#Y4F^-39V%!YQ;x-^C4wWJ*z(`;r*=Q<1INJ3SYf8!JbW+ zrUgF6|3{dkYtq7?f8-PYMW~Girg^OS=9OLN(F=+S3Ui6TRwrSw6{;9sCof+?=i=OJ%9j>!(?+~O_y9ndW1~lm%B4mXzXo^UzyBJvsMRY25+?zt`8Sq>;M!y* z?-h9Yeuf4>N%JXe9w;XAjxv@U@*+Ls5)ZNs}1bz@hWcbVC^M; zSgEj=#W_)s!6xE#r@bz6r4=-XnaT0x_!?)905!DZF?x;FJXnC?uQs64jSmM#s}M50 zql^?N@csd!rmnC&uNE*hP8r^*h8?T%11NLCj@8NXNUjbE)*exxaI44OMw`$!Tk0v* zd0WFPSLKCeagGVl3Y%_o0kIBVENczEPn|7Z2_0vHpYKfqg@Lv(v&)iC+^P0j&d!vP zp#=sH>_G$7a`(D4h_8Z&r{PbX2T-Q@jaWm6cbpo(!efR?Z8$9es+#(xGJ;x@204vl z1xQonk6Oi+(bRc7p|uMlfI$X!ai!j!Hh8$!xw+0MQX8>GcbZY#6zY`97tPx3?ctzm z#GC;Xa^LJQ@M9isxAh+Gs7KO9WNa~Vz*rjI&A}u3dv=WOb&s0R06m+ODl{E$BIrwx z#bf~Bw~Pn-R(^I;&Mrrh%}11g*3^FlxFBdTCJb-b%`nEnF4iZpgC!-Lk2u&dEBDo2$4^Zs4hY=55g43*tBBt>`tM8u>YoCTK2jX1u&?X(`9wKMzBPKs z#Zkw~#Ei62-`=%F3W}l%KWVeu(j#!7R|4aC3<=+J619QJ1DhtIIoJV8n!#HIJ*>BR~q4(&VDeO(pW5 zp|`jFwKKQ;)uzxu%ML3PT|!Mt(*=0_C#rnHLrVbgy5gGLn6o^ehN1T?z@QiUZ&lFy zfz-X%I{3GKj^xuST%cNsR(p2Tm}hR>O|x+=?hdDC|NnKJB14~A)bp(p00Kpusj&q3 zZt}lD7vKTZ7i{efwr*Y~{H?xuem62B?cK!&m)*s$OFD)hsz=$|Ay+in^570EuEzPA z3F?wkmxEmw1uov11Jv|vLxhSVR8_7%#gfMIXaAPG_Gi?iGIbn4xI2-zk@HzVQI;}> zWWHkg?~avR|LLW1(;35{V*i(FfWp0peTdyDjgw|Zd4 z!d)A_mQVPISDZWu`#}nl?KLD6jj-QZ8t%1LfJp&;_#(4)TCY(Tnmp@mg1fsuN8tCr zcl%BXNZWycjT&&4k{lyal1~I6#WY{C7(WqVq_P0m@HV6g$b1zh+k@}}qW(M$)L==$ z46MpwPBWB1^W^cZh6dY3LmpvPLYYZG`gZM2Naz;f{T2sBoT6F_T2-^@>2&KpX3=ro z5>REc_C3AV3p=^nvlGEFTUC{U# z%2)6OEpdcbt4V0Lxv7DPn;=yTN{|GwcR@@-LYzO<+$`Alz{&#!_T4>k!Ws+o=O!qu zI8&6lJ%29jSe|-sPzqmFXPOL4dP`RR8d#W98 zy9!R%4(`es&ZoLW>$U*Qtl#Z0&((g}YXAgMz>Q?Zp0fwsZz79e*tW7wf=dVr*8b{kKIcC}|1m6`gYGjzW!Hm%_zA`;Y~ST{TiU1m~R zZ1x0sUoXSk1J4?Pi6idw`5&Q<3K7RMQSY@Gw3tC>Sm7b+b6R{8*d$nNl#*iRe^Pua z_*_KLV>#tT4amiUW~(ubg#nA=a^7%d53yMZF?KjdB9A^hN}Dm{vc_3p?y(w2aaZWQ zMGp1BhZmuqaek`f3%X2+j_06UB1H9vl>mkK{i0E0zRfR^vhEHR&pLoS^XvAi`%D|8 zJI%wjw>yrp6>WWe(IVRszX4791mgq8vAB(*^ORxAQ*k8T!}y z6|SUyeOERIC=XN$sCh$cN_x5w*m~h?o!Z+O6SxgRY)*FccrmvZSE)yFJ z2&*_~G~Pi-#QL;yK>=t$Y}J&$6DW|@M9d@Z$$J~YeUg0BNNz+}tMI1(^N41Q<22_T zh{ij60jDWxf1A63uOEUWNNJD+DT!)Jg7petrdX{|6N(=mjFb~z;p^vWfIs)VQV4*f z<5>05LQo7kELj35YgSX#TQR`&E z;sYKwxl!$#JH~EqEYR40&_v)(-{F5VocDc58-WKAkCil?9cpfvfl!0~ms4K@WL_np ziDn+ervEWn+=I&bL>gd0@*mC?o1&(`QSXLHSVT^m@m=~f>#&6(Bg<9$R-vIm!^^4# z8rTX;PIWdQTS$XmQvl>M{F1?yt{64Lj~C>^&h&XFOm(r6ecJC8LEU^|P}KSAoBR(@ z_v7~J)L;Lbuoh$XQq`{(z2*yN&=ck7sY#wspd%qUTE5fo8qA-kLG%HT*6L49Mf|4F zSbD&sz6z=KJ%J6cc{GA~i{m|rlwy*hwp2i!9Bq1;1!H$dBij0A7XR#mTQ)C^ zq>RTPzqh&4u59`hO!+C%+wNsMzM@=RL`)49x(`O$i(FtvEP-Zeun@h%C;xNWA6_al zR`l9|0L0ccgxmS~w%El=uzSC%Gss(r_P<`A4d(At6+|g|^4{SZWVUHhtmeOZE_Mn0 zjKi!JvvGJWzwQp=0mEQN7o?DcHR0{|xgQSXfVr>Ikj&BdvNSx%v4)|pYE*$8Z%w2j zi5yaRXQ5HUJW*_x+J%h(Aj(%Eb*^8$WgsJzT%CPKNihs)3tEGWtM1(1dHn(S^Y z&hmfZzt#qk2JT0{%*%BW9EdY7fT8yp+C?Td2wEE(QrKKu2Yf=GW=VUaNV5zF1ww4v z5JeHNh7d<8ZoiBST&I%~wY9X{W3kedNU`tWeYik?R9ESO97DDTk#M99wG$VEjx4HP zgeu(B92D(un2%-FsV;gAY%S9OCO1Wkiu42wpGjU^-EQmqgH)nbf^TwEk<&+{x4X(D zq?NhFCkwl6xBH8?6Wjao)ibDAk7bGSE+0%p@RxA`x4FNmB;A7;9c;*yXv~8-dYYK| zr@EjULfXNn@4Fn;K$?Jj5fQ&??u{KlaO0J)8C>FzSRIWS#!6@ZnsI-;d?-c|sq|i0 zIsitXVhd`>jFo6WWIxE#(=ORpX5!`>)C%8}_+pOl3UY?0vT>7n-;v+1O%gv>JZKIxlfU-&8*`1ABx>vmd_i0n5lH; z@!ubGP*8rKlvDlnx5u3S@%mz;+qsEcdccy(hM51wjQh=!LpFTZF?i`1w&R7pElGs! z0YOao%Wj_G^?MQF(aH~n=S5lv%T1!Hsjx91RMYpmjKBggtyD#Sa-5m#DuAk zHHgY2^$Kkx7{&r$hIecEq}J z;RfZ08(I&R$DAh3kC!NrzHkY(tiidrNUrg8HZSgzy&Ia%BONx&L@Z>?X_k78q{XGaIANNiWIY_xX zsLf)j5+F=ajwKhl1A|eBZypl7j)mYa`AHF!d7_%a`AZe*q5Xs5EM}LWMf5Pd4t!> z`}BQpY?c%MT@`15SThjc`w$h;9G`e_!c-v-nvj@XS|x}bUG;F!>+Rc6^E+X&kWb^} z@!01#O=!K}LnPYv417V31v3!=_8pUby;`22J1#=)pB4fh2eNb4g0vT(-Gn5l+T_uM zWI-VrIVFdtrUy{ZCi_V-91Nd(UJSH0qs@yv2jNi3{1?~Fz58XC?l2KA$9u))c12o| z=NT>6{H%)GAcDdD_#Q7`Z%-o*C>Z4TpUc_)q+O)s@9uSiA`n!1O%$%s2Yi{Ct`B zy`*9JTl*#r*87vwgBI}V_|l|@QPM!*QFQXGrj+Se&!8AgUBrgu(|pZ(jJFb6@OVkZ zu>uZ(tVn}|{c%RNJ6PLWcO)LXp@rn2c_kO3g66*B=P_8UTO!%<9eL~%P{Rjci)YPN zK$NZ21Kh9yt)fvgH*eus*%0aM|EBDu_XYoVEF7Bs$Q}c@>BR%UlcO-!4$k@5ouVr}s*bC{j1XlKLnJ`k) zwIbn&v==pJDSMhA_&+7ZZI{Z*_Sb!^5Y*R{MvuY2;GA4OJ_E_J!`p5`mcUah6J9hp z#YkB*-b?ysNYqKDv<-^a=MnPM>RQ0W}Q> zoKvXu1Q9CCJtk#9Rh|t}Vb)pge-07J|Y|q&g^0MI1iD#SLP1?;c1TGQq7jJFq6TB$fB3ud% zWo9DJ)`PZc8D^sYYROe6IM(|Z`7j_54U7X|Wa=mdOd zOccnfbRTC2mcL^P>Wh*6^u}YMPy>8O{@v>K9uzCsTUrv3N{W&FT9Rj((a_UtpEb4AxthzYz5F5Sy^%tgkU*HZp z#*3O_2|Bwr9A>NrRHPB$1N=%@X8ezA4{HNWkqRAAKd1%i+byRxd44OF=d?kQXZf-9 z4u*x9EZrLv$DTJIa6kJDGTVZrHG}Kj`o+olNrUmPDLUHP2IoPvy#ccKiHIK=e{98# zbKZEG)=+;S(jr#aaPh6F#;L?lgjzJUP`zePgm7ca`NCH$BOCKHI-Nr3ns|GTO1a)t znCjQp&PbeQNdcmn^D!;fxUBt0*_> z%SDW}Gwc65UMg~CMo&K_3a=NDmv@g!)aS=YRzz>Fhj0ne3#rUL=`ajm(Uy4b{mP=6 zUuUtwS2coSax4vA>ri#(T<~L&05ybIMdt@2jloXWSWj;jDgqPo6iY!7=IOT_q)w9e z#6$gEJLbJ3Y7q%B7T&@iN0r0h%_?RKMPe~>I;(#0?k=MY*!uKo;AFfIH(yErIPE#$ zb}QY`%rh~PbCrY%`USjQ^zakzKGdj9AXf2=x=w3v9Gc5dCNas1V*-J|0yH5XvF~7n zhjn!^eHs;Zn?s-mf`C;QcUM<8myiB-{-6AfFZ)3s?|FVMt^#YDle{b4j!$L1y}em+ z?Sq4ZOO85NTrN%?UP7`qm|h%74KwQb@}LrihgD{g<%^#Mv%7n`UmSmQlx+=RYmQbJ zXU&S7E{^^l5$A=fYGuX}6V}T0aJhkmI8dNmya3ol!G7L5rZ#G{^NcP?GJ1`1sZ1eQ zo52`hWd|)Vw$k$-z6+(@$Lz!^0#Y9p1j(sM+MxC&6vT$WNv#vY^&EJpiXh^?L zOCPW%Z%T@uzFkG}-s^?$StKyWq4DN)-3(GQEfpFB)R4;uwG zC|R}KF-nI+lJ>#0mrFkQx?7n-gqt!mnP?52oeGYx;7RVHU%?u$*0vdd7=7MMz5KgRWSHUWH4;-gYiIG`Gwu5&(lhJPbyzPoRBc z<^9_;at&?yMC#7;)c8|}O0jFFlZ*D-pj_eYKI~G?MgWcy~}`qA4>{Q~Iujvs5-Z zS~KUDfFtGd9u;q+kg#yM_pb#2P0V1=qwly1%D==smrVgSa;oLvfI?>T=@Em`4e!MEPG_PjZVe40_lChH$ zz}em#!ZLxhoTn{Tdcp#fzdd}S^gz9|%32a^#qaa?o}f2JMRB!-cQP@pT5Ly zo=!xDlEF>fZmAN*ebrIC&U;4s*Jcu>a?4InYNMcUY+{KS{>W2;GIED@ z-*pwxqF&LOPw;TEd($z5?)=#uRj5?1=|}6~=$I{fO`0u9)5SX8Kb9b6sj^NxN4mRFumKR<~t zkN@1?++-;&&1!be|FsxF5{dI&VXbS`i8!WC6{?7isvan=IzCeQ1|junbUz}(WGI_7 zLNZK7I!xY$FUfZANeu}dra>trQ^)0Kq)-9ISNQd}dw-A2?S9Qf1I>Gck7Ot9<7#a47ICztNeNZ7dkfK6%Q(iz{m0 z_jWkbOLHLlVb8Y4EY9U_c$kTNgQeb3smQ-rfE$85x}ULwCo(kxXt}k+*Rg}|^NieH z{ik(QSh3u@GMGhk{?+g8oN==XR1p&u7X5vac~Nk}eT|VMn(3CRk;=gkFKdmsv>Wa- zFr2WSFos@3rbSP%C+6qU_Er5u8M(ju;n&W7CHz#Z@RyTmFJ&=qTAM+Xe-Zy(f8qy_|r(}b_l*BSIK(g^JoWEG) za&DjuuESREd&pSbsR99u3>?n%WKI4~C1^-Y5h)$&zE+ww{fk;yP-Fgl;;TvNZl zxrZ0Eo68O_5(%w;5D@~7hl%l;p5W*()9){fkJ2d%e@#njwmUV|*i13z3#3IZ>}AXzkE1q z7T0kUl(oIpo&n$6`753%4c=VZ*UhSMPO96%_Q`Y!k%-Ra?r5sLE!~l^nM8`CbGXk9 zqHJ`jEcUhHa$?YhX~p!6kkIVRbG&83Wm6{1%OY-u%(jkCs(H=`wz@%BX&Yw(cpfxk z_B>z#%hxok7_PD?6~7)W_l>Y9BC@(aszXl}S^El>*UxUPpP3#1R{HClpSo+p?VQVo z7{4iSE3*{$MyURL#{5Lo9J}4}zLQB$>B>pQ$BAqDhIG;#2$?!BS%fqlN6oBe#vj^} z#(IDs-dJJKEHpFQJNMCbgEdr*)aAxwC)S8k$Rl&6B3}$llx|7I(!KNs$4lSaQr`CR zY-*!pN;b8e+4U=|rl!)hvbu^~tqfYQloc4UUuL6-YxQ*0YU+svYsj?y$;bRUl0=z% zd!!O| z*LnTBpKE?mY>7G(Vh@?ayc!y!DV}QqD$88h+gn*yi5VDd{JoQL^ZVBCNNh4)sww3m zHI9<9yUXdBYw;U9K7DT)OK+L=m5#HO4i9G8#!@%W#^$xeZ)+rxVZni|6FnowywEz% zOfx{!R%vplGn8(sar`EfBDER$cmaCG?gQ65PbshyFP)_PBTAwN{rnhFh?H3Xm_hgg zkjH?FG_Poer!Yu|7uZEWR%{hhkRadux&`w?wo^=X=7)ALYh^>d$(1pTd>NlUvuCBE z!DP$u(-e-D7*YT17XB(KfTwFLxsvo0?~EC1Nli%oJWI zQb0B~4l+OouJqqMVV70O9HkWca{Il-XCqqqwq%B|zyS!yKh8!GiJ+!hiP?2~h~!5) zlBheV>f_^qBS*YZvbKToW6MBj^lNG9JwOv6i+cY)H8tg7>D6377s*7z!^&C8N=u9Y zmz;RF%Q;kGog0E;bl-@Q&C(^GuaH%s)&e6bE;^-K_~4Su6y>q2hO|8 z?-L`wJrw1Cxq3T?TULqEXV>i8XVzt@Vo-5KUex*c{oZ0?<@8Zgu8r#pY>4yL_Ve)i z;W;cC1X|!4Vy$C26o>Rf-Kc)8J=I2pFqpeGLU+V>FVNRo#> z!Vb;LL%U{r|7)?v@=Ht0%(xweI^i<7>?tYj?b9w8e@bA5k5A5g8=d(^69Nj`fU}Ma zU&bWX15C4x3O{Ll*9~}`O8Mva^@K(F_=Nem)q3Sn4?*t_ zoY0{3=}*B*d69@^h;eU0Bzm|+8}?jR%5c&~%H={;?JtDwe+}gMbkGPZHV%{h-wJze zu!>oGKKP(T+*d|>HN-Dnm5O*Zs5;b&2gI`rm+04hU;LH7B$&3hvMA3?^saMx^~UyX z=W>>c>}lPmg*96I%a?wC0Jrt4jLN@7kX^|}_zj(Xbw`U3i}^0QEXrf{TuELM!ZMvG z|Md~9WO(a@hPt}*&*<@WCr-T)_s_SI3F{ignj1>X>4Fz?}IRRXxsm+mdwK67mIc ztHk~EnQn5V#U+8k=&xAlDW$HW9RhL~qQ*)R8F4fSt>wfnW%DP7T^*k!^aH0uawjvtrqtsUWe};=$ds8Pb-s) zJ-~{0?At=J2V1U2%2=g`Yu+75RoAA=roWrOlz$_4@{c2DbTY77LTOGbuvDF$H;_C0;Z8Cw(7+W_!0&1MS6 zxIj~nqvs5Ks|uZ_bUWR#f5I0Is8Yin3QTbPN_PMYoDG zM^tHVTPLE}hyhtEojIQ~vqpn3aNXnPPlPs(v3R)B=eh`__1^migx9yn9Y2DJTdzj3 znsP(#LkoKMBTi0EI>r+jZ{OzjpHK?vOv-7Fga7KKT=nL~EfZF1p+Kcrh(D%4eN4B$ z%o9!sBbr#^`e$`vV)GW#LEtrpG9_GNT@so3O`diB6O;_#A zcTC7i=kD>0UH$Oj=n~YAfts8WW13LwCYxbpeoX_@Iq!X} zJLkA)eRP1#Q3E%{GiL}<0&IYaL)ZS2GHpI0PbrUw%2}$;8683}&y_l~G9J5>rDOhU z=N;^Kl6$5>&`%0uM+!Nvm5Iuu5sUKt69IV z0M-)wdbR6pI-f77;<~)D4h5i<;fkC0hhjrLxPAx`VH|Oxs2&{&r(R{PgX)1bJ;5W& z+#(_fgt=7Z>BjLT<@@C8|H8)$tWyxyKwXDuB(-lIdHMF{WvMt?{g7PEdw`AM+dSqI zF+^jdUY)LTl#Uu8t^GU$j)YkVSw)S;Y9fz<2S6spZnf_bH7e_eW zkdF?yVgf{n(zpa(VcvZIt^JD+&(rHvY}Wj>`o454AW<@7=Hd@ZGK+hXawjVlTHGd? zVa;KU7k>EwNPOh?%E9Dzp-4D^bNwsIv@|zCQl^xT*q{RD!DI!7W*}Mi*iQvj*7+ zQn!Twl5 z&>ofIIAb27@OcflZe^y!@moz;(i1^~@t@grba!CDLCYZZ^|hk4=;AA^sU;WCE7PM< z6^e-_&Zh>A{tm1=$HDDPm`q*Wh0k(BWCcFQuMO_XyHw{Z-tgOL<+wzxO67k*gH$O% zH}}01GvF_)euGK^=6W+z;G76^M}Z6+8Likh)@XF0OBHAnGw9UlG%7+jU4(Y=!~Go1 zJRD7Y8mI1(h6Cz|Dn{Yqm%w7Dx`+M}7%Ko*!<^OAd)`oAS^G^E-NCEAUc30!+boQJvBzLH>3H1#GE4|WzZpvqXx(htX`2~);@AN^j# z7V5R6(sRE7SibS|&cmPyTF#=%kUgzrVZ-1IF%Z* zvx}Q*@@7Fo%kusOJLcBuImyvcYPE@!yG+t}`Uh=b=gH2dgO>dU5fGCP@X<}n-n0}5 z3eqC^p;DeacJB#qKo?!E3-UN0-gJ^nE*BKyb!^;n-`Wm+ndQN@bx7VK`8<{5^)-Xg zFy-Qylri_5DHJSZDV-*TNyf9dJj)V%hMBzzF&XDvjK zPC=ak+;CaOz$MXTQgx0#Z&4vgiJ#QJdh7wezFGUGmCShXR>nivO*0+^3 zySurw2^y8GB>{7VFiEuSf9zD+(1LgD3Xn_;IhNdbAi)4>gVN1=E9yQdG3Dst_?QA; z)6Yw{Za#fzkIH?9b5yUy=o8M&7xq~W&J0F~N_I}X+JFyiX3Pf`_Ofj$tIg|bZsLPH zdD|EUgpK!E4I{OTMAezAsP?LEZ$7FQRE{M+282XG;e(9oIJ5M?GZ)(y=tv)Lk)<*C z8_zooLZ-ZI2{^M_A7X6{fmoAndZcuh>)A8GXM(-mNarI9NFf8fcQPFvqA}C+dt|D| zvC1-bXrR2T%HWnksAkp4uQ(U_MbtbB2Xff9(K9k7t9TUGmsy;h-4qO9|M84o=y`W&CSWpe-|9NixVq{m%C%M2fBGbp>TX4@v2wpW&saE z>yl%ot4nodwYE1cMCC5wa<(a5dl?yfGHlM+btv=Av4w)i-CY)cbh&z-7$Tveay$)FXw@@Mf~!hLh}o=u|B-D0w5;wrOO8p$>7>%Q3JYdU%*q?pLC;&Hvw@nu%Nd$0_xgjgynpCm>N(e3{-I9Ap5v=LH)UGC5SiW zIQW@svW1Z4aU6dT;8DkRhL(V5f{&GCn27p#qD|s=Xa5JtFH{K2=TKr9JWWMWY+pcn zBC=-)kzYd~4DoIm@%bS92?F6QT%jEj@`<}}a`Mc_S?rmpVf_{giNWFKNB#=o4I{|0 zpHc(PB}hPhqWYP|NmYl{P>@l%WLHNGDt1MvMma} z;3L15c+;g%{D^?}k)e8@%5Ui$MhCK8Wef=P&A^|xLtNQw4*0B3WVbE1<2Qhp5)x3( z>M^Q1zFnwb?%=YSG7-d2TQy09?f`kr{&z|fr4*?Au8Gww{?*cxTMm@qA@R;00uGU} zw8H8{D(n6Q@RqR-*d6dtE5YCCZNf~atg6&sCN{5Mvz#>%MP2A>{1 zLU-8UL5HvaM+owxVuZcJq!}eLN+H2VZ-@Mbm+BDOEflI=h4tR`AWT#p62tUw6|t4B zb~Y@Iaa=Ypd%!6TX%#lp!8VWhpkmd}u>x#Z5F%n` z!mS5UZ3zewlF2)E;#HW)_8_${)M1RIF*#};D>iGG2!|DNNcrzsiRxBG;xRrg__y4l zFEJ5+7Srfd%Jz2fC}&qnGiyzqq!*rgq41yq<>_K9==XpexNB0w6F#ChD!1><9e0SH9-#9&~CS_=y_-zqeYLb~5dQQb1jMm1NYshVY z1dzoW{Xo33hNmv`ps(7km2@K5kYCp)TBv4+{Fjh0zQxpXr8x6jvFr(Zq^SB=ZZHai z8=7%C&0oYR<_fd5;jxeNA3}VkAwTg%f^+4`db66Kyg_ol51oMLiUUEo)?-he=kaHQ zAO8pT=$OA?ciX7pdO}jmVl;!Oq=A=Txgj4Pkg-l8Jui8FU}#K4*v1uR<0O=C)(fY= zUf3|@XQ*X{Oo;x&t2cd^umkzo72+xu7cara=#f8EMN3@wIff(}yFoeyO9_z>E5%vh zf|h)BXS{13R;ZD-_W7JAtkRTBs?E7NeOVr)lJe2IK9PyPu zS83sX0}2G~?<+OZq<%1F1k!S72URLItCEkG5x@H$vx1y=j7kapo7KXXh^r4b0^`pQ z?k=vxHL}R>Fb>`?BZox2`@3h8=9IOn)_Y=Xx<3$8m5PR{@HuBb&m0pdNoKD`%pVHB zuFzW|`hv^cSd!OS&iVaHac>Fk^MT(yVtxZUk7dFSS-blW0$A!l7~3C&GHZImDaQGd zo%!na9M-)k>i&@ZCQ*gq2z!jv7IUt*=-BbHg1+e?Vc)aw}{sK3oX*#W~m{?IIvns-{Wny zmvXPmc2qYPi$7CA)o;4I9>mK}!*|dfpre+35A&0K(;3}f7J#+?8+%f1zg3Z8j1TJH zHjy-@DDR)CmMG;{%a4rC5RWR?o0g4aQ@YShNGUSnaZiPXnlso=!xehr1jos=V2)=0 z_3q9QB~_39N@hHL6ZLB(&q~2Mbs})Q?eTY##8dAvj)I zEEK6}GkuH9HHqWzfI%_^{*BbwcL!E}8rP?C#(15346hqIU?ZV{uh2|4ND>ctHJdj+ z2Ezr+By{Vs9`@z)k}3nFUSUX3>AT;S?0Y*$%XJLH97lJOTUY_It)GJy7<|C;-{4P8 zYZ-Hs)q2tR&-3g*8;%Z7(^{HtU?vbu^$H$-umJJ;y?3m@_>g$Li%{u39HvNhGreYd z&SV|qO7jVF@S_+)Pqx38G>)kTGlxhgAM)=v9z*2Ag$6AEdHk=(xZ^GG`M5vd4|VB)mp zqahysM=l&e*&2NpvMmSD-bGp85C(I69xkf!&ys5^ivOWIu$k~n=nyxe1cXWaB^)Sp z8kgPl2O{1Y9$&Z?Rw!HmFC@|bY(21tPWmx~MjZK?OnPjcS7t^f$={{xrL<>U62{M&JCEDrCt!2aEgy$9AZXI4fVmd*m>ncjrhv%}K%O~zi zlk5XO52uCFf*FWX=$ne#6hzWtz+XI1c#S)k&r948i9NRxS1s3+=tnrg=iWRGOvRwB z8;CkW+MLte!si+7(T9M#4G3IJ(Hbd210xft67?`8{k^nFM3M!nAdg5YFHK1EP85-Z z?z5;qt>1|FGIE1(7C@LzP-|J7N|W9cAto7Y-Bn$5)6BAfL9Hcve92Kf&WN8v8`Iy z8ucO3Qh_QjBSaCa;HZWZI=V19U1L=SpW#E^qH=GJ%0b^dJcgABcZeufj@~xM^FIM{ zFD_1Kt|pgOff(&Qgi7>l4DR+aAY*K>BabVHxb7-Z8H?|at}WxDRz;f`X)I8gV!EQW zJWk{3Q;@1)I-0MRqW0D2J1z3=%+#EC2`C&`Icnz({o|HaL8-5d7Hf|$Vno@!*!dW24Zx_^%<3Qq-Q%cl`@6G8T>?O;S zCwBS=i`hk*aPS=7hqTAV5tdoS$Nbrv34%>WAvj$B6|80{v@LKM*uQv`H4s|rbtlQJ zu5m0v3Mf{%M~OgG2a{76bD->V;au@tqebNpJK&ReQ+@J&4|t~CrAzT4$-KyqUJ)b}mL7je!@K#udclkgvymPe#ey z`bQb~B!O@O;yM;aM>=&>er^WG@#*<&|Mj!8jg_-nTKhjjU6rD>o}-sgN#~J$5GWrF z-H|3U;qOH^V=&AwVB4#k^$6Mx;#iV3rL2I{J@&ZSb2y$fsi9R4HO zsxXvU@e*O5U)`*{0j1G9X=7J@axl1M8@)^vaf{niDVo9qXnx$GwR;!2F35<-*l8VS zb&{Ufv_`4)eag`I`R3%kQgk<*LQs$wI8Kq{ZKWVOmGSWyGrWWsz>mvc zUHIIxEwmC6-puQ8gt*FE)I(A7#s0E?<}7a41gP`25iF3rT{F%BXoH}yS+D#1=;$#u zY~*lc;=&}9)YOWiyBk|>0u&M5Ea(m)_dktH4emVgRD6f84k5$+%RTy-6|;7}4l#_2 zjYsRE!;UcT(&P}RRk^#-V`i6GSqbUX*Zo1iLKa@icU!O`t6w{^pYO1f7KC=d#=0F3 zH2Xq%AQ~ipM}v0itVru>HnGTQ7XbZ2jm6s6GE+kW3qCkHCX`h7h?;U$R026etH(Az zE+rF+^0wiFYxhkoVDsrS4Bh7U~zG8 znXG_<_Md&Qdb$tXTKG&EL+ogSW#q|k=HJY`nFm@2QvDdpW%2zN3jkgzE~g`92Xz|S zIj%p%2dwJlRO{zA!iDDkIMhGU0!ux`pw-VUUk*>XOv43?@f0 z@!o++as-lx&u{K~#3`GqzWt-Kh@*{ho&v_+zL)CFuD&B}_GaF!g^S(@oH;5r|0t_5 zm6p#FCcieO*{3n7FBjPwr4iPoQM#DqLxHRC->=8uJrG`IVqDNec!M-pLgzDyr78c~ zp7JA8{_~{z-fD5g1YW?!KM9lIiBv3kq(LVzmDZK0k&r%HtYgmU7SUc|B*G^ZmPZaI z9yQK+Aq*?+gica4B%hme$i+EV!6oRpk<-ffH!W7~b<5;I7?%a>l>LL+UVB?LC_^fT z`+;CoE^?5}z)ezLr6heuI^IVG$?e>oFtx=PcXP63`f?G>05ZwHWKNsAu%%t-eNgU_ zFlOY%=V+=r+Vzy45Dh=id5;MC1ojCzR=`ZI(GdelFi9D?bEt;-g9a`tllj+$ItcS; zxekyCj(_AQSr9q55&pRWq}zW{(`|tEt^~}#&UjARkc1p%r1mJXdBxj);~qKA=pDWD>r{|;fU?}=J^xQuUQqf5!a?VgZ= zGw0~=82S8weuezkc<|Ejg1XM-S4)qqANDT?J<{dKU?-gc{-S@w2#p=QhH=lMTX_sJ zN`5O*y8}Z$4Iy!gn%@|7{aC))&0WlW$mU8nE{YN`BlQ=BzUF*OW;qc?j4DLss?|dW zW!arf**vpx=Hko0{xPFumgL9hDAO7yU{W8HW&*~-t}8z~jtwb{{40NT zsv*u4W2Io+BJw4x$JyX?xOp~6R7>OX=kPjA;WI%L)r>%t?v+MB6&V}lml}FcEVfGC}m0dRB@w;1MLVsgEM(dXk?o6-}CLu_ol7va8oaSq_Pj<3J93 z27Bty3p3sbES>cY?oHFwh%e;){uO>f9Ryg6_v})HOI|-%=wX%KI)VV z;g*mxD{UT()TP$=FrHgmnT?MpQu4pUjD^~n52m?#ZU)U z3r?pvD0YHZoq`k$*C02?FO~8Sm-<*^krYyK9hqCk9MxIIGwgEena{S}KbBa}#okmG zcSpSVpDF1z+X;!h{z_NFCx*C(FM z6lg+qzD-K!vzi!4zTn>#<=@?moMVp6SFnkNPqaq$ebanv4@Is8Oe5&o=5vLDN=gLo zu;zgW&Z8%FsN%tnY(}nLf2CCMViix`$PZygp&Vp!H@whOe-`!(1wxg2BgM;9HT8O% z1t&Loje-uA9N9W+Eb!^sdEfQnZm}yq+`dJDH3dmS; zh}5uh)og_57vs1ehbUOx&IA0o&0jX|PBJF=hLTuw{9oFpy zqY`*9>#D4E!d1dyINHs~zS_`i)a*#5zD+42&}?2w5?d|Jz2tFpKZBsm2%XvTsOjS4Df-E)0GN4F@FSrHJXCK z<2bbowa*u{ymBm*ZwFT`)JW3n-SbFd6&IL4h;YTp?CV1EQQq4qBUcTuo^clA6SVI8 zNF^Q1aUFTG+KM!Mn>E2p+MG3Y{G;fFc7~q!TK$LAtpX;hhV>HagcXfVpEYXHmZJ&v zW4geCf|m0s%zm#?)QNuKCz%dQpZZH0z1o7BOVSq-6W4UusPMxjvPN#Nck+-?EtZx` zXw(-Qb=$-IxPCjqHPT;~3I~t|vnrY@wy3dV=M-N97M5qZc08Q2Sra?AcWFX25Cnp1 zq@Pu_w$&&nTZ_~nhwn{q)6*u?{{Rr;`a)ap`M$7g_S1Ed_6~ypP+Nc8H)QNcVGw2w zP5zJ`%Tn`b=vks#J@0#ai*hFmPR$77BIRM%a!AO1iMgZd8Y8ds1isl!@VGdK?kIrhR5eA)cVR{zqU8AYh&s%@B3uA>2wXDo!icw4R!*{G5?(F{8j>DVMzG zsSKU%B7JuJZ49&Y)4@T9cls6ChSjvJaxNI|>LL*!H!FuRfqnET@X~}rcX1dj<>x;Vaz5gID%fpGaF-fl zMO3{Lj=T{9A=LcAn6+;ob{19E6ICv%@KN4f-_B8O1%o+oFMZ+Lw`!YVBoZ>Sm==(~ zwplgvv3lP(z^;)nQYvU(-_E9-Q6Z7E1W}Qm_-Fpp*?GK0F2Bjg8s+`bIhxrCEnY)e zIok90@2uc^bKb*C?crB92oE;Hb6w<)p!e5JMBVS=P<7%kG95K!U8T*n`-%&xdi!_% zb@_eQE@Qv9=Ws}*U}FeJi=n4Rzp$=4gHOe-BEt`SlO_}Yi}2mdxOJE=RmDE$9zFn3 z!F=~bZ{XoZ3uOja4?d<8p1lJv3Q+qZ+nJ-)@}O5=$fC>Aluw9d(w?<@z zgPM?mRFI%1=hk_r?0Re=0{`vw+{{0h_gEVr5^(N4AeMO3c0>5Q8unHyn?cyqIybTY zff_UXqX8F!?=82Sr!~jnC$&9;7zyUIRx{V{}Mn~TJvr~Rp=(JHiiSyU4{>JI~Je4x@!&H2wd-zAxqK?MhCrKwe3k9q( z5Bt<@ZD-F*>mBE(*Flw->gwuFlhNLY-eAm=mP-4>!{Hm<@q;(VN9?a$+Cocp8(;?7 zmNkgVtiZr^zMJz?Wzh%4JHtFqS3K3zcKwN^I+t&Gw2MWvi;Iiv>N+=F6qS?|l$25h z{Or2rXWi@;`2@LCSIp+9PR)~yd@oX}d?QIHMO}WsFTL@OWg3vO)xW$r

+

+ +[Mojo]: https://www.modular.com/mojo/ +[Getting Started]: https://docs.modular.com/mojo/manual/get-started/ +[API Documentation]: https://docs.modular.com/mojo/lib +[Contributing]: ./CONTRIBUTING.md +[Changelog]: ./docs/changelog.md # Welcome to Mojo 🔥 From f878c3dfaabf7b9a91d5bc09ff0584940f001e5e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 24 Apr 2024 14:59:41 -0400 Subject: [PATCH 0300/2019] [mojo-stdlib] Add new source and call location accessors (#38439) The new `__call_loc()` function allows users to retrieve the location of a call to a function whose body it appears in, while `__source_loc()` is roughly a replacement for the old `__source_location()` primitive (which will be removed in a subsequent patch). MODULAR_ORIG_COMMIT_REV_ID: 05088abfb7b7bea05b90189979616d08d09f3edb --- stdlib/src/builtin/_location.mojo | 66 +++++++++ stdlib/test/builtin/test_location.mojo | 196 ++++++++++++++++++++++++- 2 files changed, 257 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 8aa6ebc738..b149492a8b 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -29,3 +29,69 @@ struct _SourceLocation(Stringable): + ":" + str(self.line) ) + + +@value +@register_passable("trivial") +struct _SourceLocInfo: + """Type to carry file name, line, and column information.""" + + var line: Int + var col: Int + var file_name: StringLiteral + + +@always_inline("nodebug") +fn __source_loc() -> _SourceLocInfo: + """Returns the location where it's called. + + This currently doesn't work when called in a parameter expression. + + Returns: + The location information of the __source_loc() call. + """ + var line: __mlir_type.index + var col: __mlir_type.index + var file_name: __mlir_type.`!kgen.string` + line, col, file_name = __mlir_op.`kgen.source_loc`[ + _properties = __mlir_attr.`{inlineCount = 0 : i64}`, + _type = ( + __mlir_type.index, + __mlir_type.index, + __mlir_type.`!kgen.string`, + ), + ]() + + return _SourceLocInfo(line, col, file_name) + + +@always_inline("nodebug") +fn __call_loc() -> _SourceLocInfo: + """Returns the location where the enclosing function is called. + + This should only be used in `@always_inline` and `@always_inline("nodebug")` + functions. When the enclosing function is `@always_inline`, the call + location will not be correct if inside a `@always_inline("nodebug")` + function. This is intended behavior since `@always_inline("nodebug")` is + meant to erase debug information, including locations. + + This currently doesn't work when this or the enclosing function is called in + a parameter expression. + + Returns: + The location information of where the enclosing function (i.e. the + function whose body __call_loc() is used in) is called. + """ + var line: __mlir_type.index + var col: __mlir_type.index + var file_name: __mlir_type.`!kgen.string` + line, col, file_name = __mlir_op.`kgen.source_loc`[ + _properties = __mlir_attr.`{inlineCount = 1 : i64}`, + _type = ( + __mlir_type.index, + __mlir_type.index, + __mlir_type.`!kgen.string`, + ), + ]() + + return _SourceLocInfo(line, col, file_name) diff --git a/stdlib/test/builtin/test_location.mojo b/stdlib/test/builtin/test_location.mojo index 500230e460..932e88e964 100644 --- a/stdlib/test/builtin/test_location.mojo +++ b/stdlib/test/builtin/test_location.mojo @@ -10,11 +10,197 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: mojo --debug-level full %s +# RUN: %mojo %s +# RUN: %mojo -debug-level full %s -from builtin._location import _SourceLocation -from testing import assert_equal +from builtin._location import _SourceLocInfo, __source_loc, __call_loc +from testing import assert_equal, assert_true -def main(): - assert_equal(str(_SourceLocation("foo.txt", "bar", 4)), "foo.txt:bar:4") +fn check_source_loc(line: Int, col: Int, source_loc: _SourceLocInfo) raises: + """Utility function to help writing source location tests.""" + assert_equal(source_loc.line, line) + assert_equal(source_loc.col, col) + assert_true(String(source_loc.file_name).endswith("test_location.mojo")) + + +fn get_locs() -> (_SourceLocInfo, _SourceLocInfo): + return ( + __source_loc(), + source_loc_with_debug(), + ) + + +@always_inline +fn get_locs_inlined() -> (_SourceLocInfo, _SourceLocInfo): + return ( + __source_loc(), + source_loc_with_debug(), + ) + + +fn get_four_locs() -> ( + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, +): + var p1 = get_locs() + var p2 = get_locs_inlined() + return (p1[0], p1[1], p2[0], p2[1]) + + +@always_inline +fn get_four_locs_inlined() -> ( + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, +): + var p1 = get_locs() + var p2 = get_locs_inlined() + return (p1[0], p1[1], p2[0], p2[1]) + + +fn test_builtin_source_loc() raises: + var source_loc = __source_loc() + check_source_loc(66, 34, source_loc) + check_source_loc(68, 42, __source_loc()) + + var l = (29, 30, 37, 38) + var c = (21, 30, 21, 30) + var loc_pair = get_locs() + check_source_loc(l[0], c[0], loc_pair[0]) + check_source_loc(l[1], c[1], loc_pair[1]) + + loc_pair = get_locs_inlined() + check_source_loc(l[2], c[2], loc_pair[0]) + check_source_loc(l[3], c[3], loc_pair[1]) + + var loc_quad = get_four_locs() + check_source_loc(l[0], c[0], loc_quad[0]) + check_source_loc(l[1], c[1], loc_quad[1]) + check_source_loc(l[2], c[2], loc_quad[2]) + check_source_loc(l[3], c[3], loc_quad[3]) + + loc_quad = get_four_locs_inlined() + check_source_loc(l[0], c[0], loc_quad[0]) + check_source_loc(l[1], c[1], loc_quad[1]) + check_source_loc(l[2], c[2], loc_quad[2]) + check_source_loc(l[3], c[3], loc_quad[3]) + + +fn get_inner_location_statically() -> _SourceLocInfo: + return __source_loc() + + +fn get_inner_location_statically_with_debug() -> _SourceLocInfo: + return source_loc_with_debug() + + +@always_inline("nodebug") +fn get_callsite_statically() -> _SourceLocInfo: + return __call_loc() + + +fn test_parameter_context() raises: + # TODO: enable these in parameter contexts + alias sloc = __source_loc() + assert_equal(sloc.line, 0) + assert_equal(sloc.col, 0) + assert_equal(sloc.file_name, "") + + alias cloc = get_callsite_statically() + assert_equal(cloc.line, 0) + assert_equal(cloc.col, 0) + assert_equal(cloc.file_name, "") + + alias iloc = get_inner_location_statically() + check_source_loc(94, 24, iloc) + alias iloc2 = get_inner_location_statically_with_debug() + check_source_loc(98, 33, iloc2) + + +@always_inline +fn capture_call_loc(cond: Bool = False) -> _SourceLocInfo: + if not cond: # NOTE: we test that __call_loc works even in a nested scope. + return __call_loc() + return _SourceLocInfo(-1, -1, "") + + +@always_inline("nodebug") +fn capture_call_loc_nodebug(cond: Bool = False) -> _SourceLocInfo: + if not cond: # NOTE: we test that __call_loc works even in a nested scope. + return __call_loc() + return _SourceLocInfo(-1, -1, "") + + +fn get_call_locs() -> (_SourceLocInfo, _SourceLocInfo): + return ( + capture_call_loc(), + capture_call_loc_nodebug(), + ) + + +@always_inline("nodebug") +fn get_call_locs_inlined() -> (_SourceLocInfo, _SourceLocInfo): + return ( + capture_call_loc(), + capture_call_loc_nodebug(), + ) + + +fn get_four_call_locs() -> ( + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, +): + var p1 = get_call_locs() + var p2 = get_call_locs_inlined() + return (p1[0], p1[1], p2[0], p2[1]) + + +@always_inline +fn get_four_call_locs_inlined() -> ( + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, + _SourceLocInfo, +): + var p1 = get_call_locs() + var p2 = get_call_locs_inlined() + return (p1[0], p1[1], p2[0], p2[1]) + + +fn test_builtin_call_loc() raises: + var loc_pair = get_call_locs() + + loc_pair = get_call_locs_inlined() + + var loc_quad = get_four_call_locs() + + loc_quad = get_four_call_locs_inlined() + + +@always_inline +fn source_loc_with_debug() -> _SourceLocInfo: + var line: __mlir_type.index + var col: __mlir_type.index + var file_name: __mlir_type.`!kgen.string` + line, col, file_name = __mlir_op.`kgen.source_loc`[ + _properties = __mlir_attr.`{inlineCount = 0 : i64}`, + _type = ( + __mlir_type.index, + __mlir_type.index, + __mlir_type.`!kgen.string`, + ), + ]() + + return _SourceLocInfo(line, col, file_name) + + +fn main() raises: + test_builtin_source_loc() + test_parameter_context() + test_builtin_call_loc() From 1adb6e2c832b7ef0cf023923e4922a9e8e00b827 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 24 Apr 2024 15:38:27 -0400 Subject: [PATCH 0301/2019] [mojo-lang][mojo-stdlib] Remove the old implementation of `__source_location()` (#38444) This means that the parser hack to allow `__source_location` is deleted. This patch also renamed `__call_loc()` to `__call_location()` for consistency and replaces the old `_SourceLocation` struct implementation. The changelog is updated to include the new `__source_location()` and `__call_location()` functionis. MODULAR_ORIG_COMMIT_REV_ID: 2177e6629ea6f9c408fd5a2dabf5540acd9bfc86 --- docs/changelog.md | 19 +++++ stdlib/src/builtin/_location.mojo | 32 +++----- stdlib/test/builtin/test_location.mojo | 108 ++++++++++++++----------- stdlib/test/os/path/test_exists.mojo | 1 + stdlib/test/os/path/test_isdir.mojo | 1 + stdlib/test/os/path/test_isfile.mojo | 1 + stdlib/test/os/test_stat.mojo | 1 + stdlib/test/pathlib/test_pathlib.mojo | 1 + 8 files changed, 94 insertions(+), 70 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 607ffbe82d..de6b0a3af8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -198,6 +198,25 @@ what we publish. As the example shows above, the protected `_properties` attribute can be passed during op construction, with an MLIR `DictionaryAttr` value. +- Mojo now allows users to capture source location of code and call location of + functions dynamically. For example: + + ```mojo + @always_inline + fn my_assert(cond: Bool, msg: String): + if not cond: + var call_loc = __call_location() + print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) + + fn main(): + my_assert(False, "always fails") # some_file.mojo, line 193 + ``` + + will print `In /path/to/some_file.mojo on line 193: always fails`. Note that + `__call_location` only works in `@always_inline("nodebug")` and + `@always_inline` functions, as well as limitations on its use in parameter + contexts (see the documentation for more details). + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index b149492a8b..0e06b54643 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -17,38 +17,24 @@ @value @register_passable("trivial") struct _SourceLocation(Stringable): - var file_name: StringLiteral - var function_name: StringLiteral - var line: Int - - fn __str__(self) -> String: - return ( - str(self.file_name) - + ":" - + str(self.function_name) - + ":" - + str(self.line) - ) - - -@value -@register_passable("trivial") -struct _SourceLocInfo: """Type to carry file name, line, and column information.""" var line: Int var col: Int var file_name: StringLiteral + fn __str__(self) -> String: + return str(self.file_name) + ":" + str(self.line) + ":" + str(self.col) + @always_inline("nodebug") -fn __source_loc() -> _SourceLocInfo: +fn __source_location() -> _SourceLocation: """Returns the location where it's called. This currently doesn't work when called in a parameter expression. Returns: - The location information of the __source_loc() call. + The location information of the __source_location() call. """ var line: __mlir_type.index var col: __mlir_type.index @@ -62,11 +48,11 @@ fn __source_loc() -> _SourceLocInfo: ), ]() - return _SourceLocInfo(line, col, file_name) + return _SourceLocation(line, col, file_name) @always_inline("nodebug") -fn __call_loc() -> _SourceLocInfo: +fn __call_location() -> _SourceLocation: """Returns the location where the enclosing function is called. This should only be used in `@always_inline` and `@always_inline("nodebug")` @@ -80,7 +66,7 @@ fn __call_loc() -> _SourceLocInfo: Returns: The location information of where the enclosing function (i.e. the - function whose body __call_loc() is used in) is called. + function whose body __call_location() is used in) is called. """ var line: __mlir_type.index var col: __mlir_type.index @@ -94,4 +80,4 @@ fn __call_loc() -> _SourceLocInfo: ), ]() - return _SourceLocInfo(line, col, file_name) + return _SourceLocation(line, col, file_name) diff --git a/stdlib/test/builtin/test_location.mojo b/stdlib/test/builtin/test_location.mojo index 932e88e964..6e9080d325 100644 --- a/stdlib/test/builtin/test_location.mojo +++ b/stdlib/test/builtin/test_location.mojo @@ -13,37 +13,41 @@ # RUN: %mojo %s # RUN: %mojo -debug-level full %s -from builtin._location import _SourceLocInfo, __source_loc, __call_loc +from builtin._location import ( + _SourceLocation, + __source_location, + __call_location, +) from testing import assert_equal, assert_true -fn check_source_loc(line: Int, col: Int, source_loc: _SourceLocInfo) raises: +fn check_source_loc(line: Int, col: Int, source_loc: _SourceLocation) raises: """Utility function to help writing source location tests.""" assert_equal(source_loc.line, line) assert_equal(source_loc.col, col) assert_true(String(source_loc.file_name).endswith("test_location.mojo")) -fn get_locs() -> (_SourceLocInfo, _SourceLocInfo): +fn get_locs() -> (_SourceLocation, _SourceLocation): return ( - __source_loc(), + __source_location(), source_loc_with_debug(), ) @always_inline -fn get_locs_inlined() -> (_SourceLocInfo, _SourceLocInfo): +fn get_locs_inlined() -> (_SourceLocation, _SourceLocation): return ( - __source_loc(), + __source_location(), source_loc_with_debug(), ) fn get_four_locs() -> ( - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, + _SourceLocation, + _SourceLocation, + _SourceLocation, + _SourceLocation, ): var p1 = get_locs() var p2 = get_locs_inlined() @@ -52,10 +56,10 @@ fn get_four_locs() -> ( @always_inline fn get_four_locs_inlined() -> ( - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, + _SourceLocation, + _SourceLocation, + _SourceLocation, + _SourceLocation, ): var p1 = get_locs() var p2 = get_locs_inlined() @@ -63,12 +67,12 @@ fn get_four_locs_inlined() -> ( fn test_builtin_source_loc() raises: - var source_loc = __source_loc() - check_source_loc(66, 34, source_loc) - check_source_loc(68, 42, __source_loc()) + var source_loc = __source_location() + check_source_loc(70, 39, source_loc) + check_source_loc(72, 47, __source_location()) - var l = (29, 30, 37, 38) - var c = (21, 30, 21, 30) + var l = (33, 34, 41, 42) + var c = (26, 30, 26, 30) var loc_pair = get_locs() check_source_loc(l[0], c[0], loc_pair[0]) check_source_loc(l[1], c[1], loc_pair[1]) @@ -90,22 +94,22 @@ fn test_builtin_source_loc() raises: check_source_loc(l[3], c[3], loc_quad[3]) -fn get_inner_location_statically() -> _SourceLocInfo: - return __source_loc() +fn get_inner_location_statically() -> _SourceLocation: + return __source_location() -fn get_inner_location_statically_with_debug() -> _SourceLocInfo: +fn get_inner_location_statically_with_debug() -> _SourceLocation: return source_loc_with_debug() @always_inline("nodebug") -fn get_callsite_statically() -> _SourceLocInfo: - return __call_loc() +fn get_callsite_statically() -> _SourceLocation: + return __call_location() fn test_parameter_context() raises: # TODO: enable these in parameter contexts - alias sloc = __source_loc() + alias sloc = __source_location() assert_equal(sloc.line, 0) assert_equal(sloc.col, 0) assert_equal(sloc.file_name, "") @@ -116,26 +120,30 @@ fn test_parameter_context() raises: assert_equal(cloc.file_name, "") alias iloc = get_inner_location_statically() - check_source_loc(94, 24, iloc) + check_source_loc(98, 29, iloc) alias iloc2 = get_inner_location_statically_with_debug() - check_source_loc(98, 33, iloc2) + check_source_loc(102, 33, iloc2) @always_inline -fn capture_call_loc(cond: Bool = False) -> _SourceLocInfo: - if not cond: # NOTE: we test that __call_loc works even in a nested scope. - return __call_loc() - return _SourceLocInfo(-1, -1, "") +fn capture_call_loc(cond: Bool = False) -> _SourceLocation: + if ( + not cond + ): # NOTE: we test that __call_location works even in a nested scope. + return __call_location() + return _SourceLocation(-1, -1, "") @always_inline("nodebug") -fn capture_call_loc_nodebug(cond: Bool = False) -> _SourceLocInfo: - if not cond: # NOTE: we test that __call_loc works even in a nested scope. - return __call_loc() - return _SourceLocInfo(-1, -1, "") +fn capture_call_loc_nodebug(cond: Bool = False) -> _SourceLocation: + if ( + not cond + ): # NOTE: we test that __call_location works even in a nested scope. + return __call_location() + return _SourceLocation(-1, -1, "") -fn get_call_locs() -> (_SourceLocInfo, _SourceLocInfo): +fn get_call_locs() -> (_SourceLocation, _SourceLocation): return ( capture_call_loc(), capture_call_loc_nodebug(), @@ -143,7 +151,7 @@ fn get_call_locs() -> (_SourceLocInfo, _SourceLocInfo): @always_inline("nodebug") -fn get_call_locs_inlined() -> (_SourceLocInfo, _SourceLocInfo): +fn get_call_locs_inlined() -> (_SourceLocation, _SourceLocation): return ( capture_call_loc(), capture_call_loc_nodebug(), @@ -151,10 +159,10 @@ fn get_call_locs_inlined() -> (_SourceLocInfo, _SourceLocInfo): fn get_four_call_locs() -> ( - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, + _SourceLocation, + _SourceLocation, + _SourceLocation, + _SourceLocation, ): var p1 = get_call_locs() var p2 = get_call_locs_inlined() @@ -163,10 +171,10 @@ fn get_four_call_locs() -> ( @always_inline fn get_four_call_locs_inlined() -> ( - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, - _SourceLocInfo, + _SourceLocation, + _SourceLocation, + _SourceLocation, + _SourceLocation, ): var p1 = get_call_locs() var p2 = get_call_locs_inlined() @@ -184,7 +192,7 @@ fn test_builtin_call_loc() raises: @always_inline -fn source_loc_with_debug() -> _SourceLocInfo: +fn source_loc_with_debug() -> _SourceLocation: var line: __mlir_type.index var col: __mlir_type.index var file_name: __mlir_type.`!kgen.string` @@ -197,10 +205,16 @@ fn source_loc_with_debug() -> _SourceLocInfo: ), ]() - return _SourceLocInfo(line, col, file_name) + return _SourceLocation(line, col, file_name) + + +fn test_source_location_struct() raises: + var source_loc = _SourceLocation(50, 60, "/path/to/some_file.mojo") + assert_equal(str(source_loc), "/path/to/some_file.mojo:50:60") fn main() raises: + test_source_location_struct() test_builtin_source_loc() test_parameter_context() test_builtin_call_loc() diff --git a/stdlib/test/os/path/test_exists.mojo b/stdlib/test/os/path/test_exists.mojo index 4086aec969..171d9b8059 100644 --- a/stdlib/test/os/path/test_exists.mojo +++ b/stdlib/test/os/path/test_exists.mojo @@ -15,6 +15,7 @@ from os.path import exists, lexists from pathlib import Path, cwd +from builtin._location import __source_location from testing import assert_true, assert_false diff --git a/stdlib/test/os/path/test_isdir.mojo b/stdlib/test/os/path/test_isdir.mojo index 872f891604..140937430a 100644 --- a/stdlib/test/os/path/test_isdir.mojo +++ b/stdlib/test/os/path/test_isdir.mojo @@ -14,6 +14,7 @@ from os.path import isdir from pathlib import Path, cwd +from builtin._location import __source_location from testing import assert_true, assert_false diff --git a/stdlib/test/os/path/test_isfile.mojo b/stdlib/test/os/path/test_isfile.mojo index 74f0d25454..1a57c3fd2e 100644 --- a/stdlib/test/os/path/test_isfile.mojo +++ b/stdlib/test/os/path/test_isfile.mojo @@ -14,6 +14,7 @@ from os.path import isfile from pathlib import Path +from builtin._location import __source_location from testing import assert_true, assert_false diff --git a/stdlib/test/os/test_stat.mojo b/stdlib/test/os/test_stat.mojo index 353df7671f..bfe7169fcc 100644 --- a/stdlib/test/os/test_stat.mojo +++ b/stdlib/test/os/test_stat.mojo @@ -14,6 +14,7 @@ from os import stat from stat import S_ISREG +from builtin._location import __source_location from testing import assert_not_equal, assert_true diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index c2f29ce63e..aa416249d4 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -15,6 +15,7 @@ from pathlib import cwd, Path, DIR_SEPARATOR from sys import env_get_string +from builtin._location import __source_location from testing import assert_true, assert_false, assert_equal, assert_not_equal From 3f60752a9e849de1bd0af5d47a9199d3c71953d6 Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Thu, 25 Apr 2024 09:19:14 -0400 Subject: [PATCH 0302/2019] [******] consolidate accum_type logic to helper methods (#38514) We have a lot of duplicated code to determine the right type to use for accumulation operations, and require in a few cases that the caller make this determination. This change sets a reasonable default but still lets the caller override the default if necessary. MODULAR_ORIG_COMMIT_REV_ID: 6cb7d8605e08417cb108bcf4ff8ebdbc6182d02e --- stdlib/src/builtin/dtype.mojo | 11 +++++++++++ stdlib/src/utils/_numerics.mojo | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 331e90f95d..107db3dd3c 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -427,6 +427,17 @@ struct DType(Stringable, KeyElement): ) ) + @always_inline("nodebug") + fn is_half_float(self) -> Bool: + """Returns True if the type is a half-precision floating point type, + e.g. either fp16 or bf16. + + Returns: + True if the type is a half-precision float, false otherwise.. + """ + + return self.is_float16() or self.is_bfloat16() + @always_inline("nodebug") fn is_numeric(self) -> Bool: """Returns True if the type parameter is numeric (i.e. you can perform diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index 8fa6949a74..db9dd0b752 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -713,3 +713,27 @@ fn isfinite[ return llvm_intrinsic["llvm.is.fpclass", SIMD[DType.bool, simd_width]]( val.value, UInt32(0x1F8).value ) + + +# ===----------------------------------------------------------------------===# +# get_accum_type +# ===----------------------------------------------------------------------===# + + +@always_inline +fn get_accum_type[type: DType]() -> DType: + """Returns the recommended type for accumulation operations. + + Half precision types can introduce numerical error if they are used + in reduction/accumulation operations. This method returns a higher precision + type to use for accumulation if a half precision types is provided, + otherwise it returns the original type. + + Parameters: + type: The type of some accumulation operation. + + Returns: + DType.float32 if type is a half-precision float, type otherwise. + """ + + return DType.float32 if type.is_half_float() else type From b96118d1f13ee2e55322dfd841d2b17fc5f3730a Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Thu, 25 Apr 2024 11:27:59 -0600 Subject: [PATCH 0303/2019] [External] [stdlib] Fix dict probing error (#38545) [External] [stdlib] Fix dict probing error Fixes modularml/mojo#1729 Co-authored-by: Maxim Zaks Closes modularml/mojo#2351 MODULAR_ORIG_COMMIT_REV_ID: d48102f8ac6ad9cfdd5df57c344ce41d7b5cff4c --- stdlib/src/collections/dict.mojo | 24 ++++++------------ stdlib/test/collections/test_dict.mojo | 35 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index dcbfd0bc34..54ac1ef529 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -763,36 +763,30 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _set_index(inout self, slot: Int, index: Int): return self._index.set_index(self._reserved, slot, index) - fn _next_index_slot(self, inout slot: Int, inout perturb: Int): + fn _next_index_slot(self, inout slot: Int, inout perturb: UInt64): alias PERTURB_SHIFT = 5 perturb >>= PERTURB_SHIFT - slot = ((5 * slot) + perturb + 1) % self._reserved + slot = ((5 * slot) + int(perturb + 1)) % self._reserved fn _find_empty_index(self, hash: Int) -> Int: var slot = hash % self._reserved - var perturb = hash - for _ in range(self._reserved): + var perturb = bitcast[DType.uint64](Int64(hash)) + while True: var index = self._get_index(slot) if index == Self.EMPTY: return slot self._next_index_slot(slot, perturb) - abort("Dict: no empty index in _find_empty_index") - return 0 fn _find_index(self, hash: Int, key: K) -> (Bool, Int, Int): # Return (found, slot, index) - var insert_slot = Optional[Int]() - var insert_index = Optional[Int]() var slot = hash % self._reserved - var perturb = hash - for _ in range(self._reserved): + var perturb = bitcast[DType.uint64](Int64(hash)) + while True: var index = self._get_index(slot) if index == Self.EMPTY: return (False, slot, self._n_entries) elif index == Self.REMOVED: - if not insert_slot: - insert_slot = slot - insert_index = self._n_entries + return (False, slot, self._n_entries) else: var ev = self._entries.__get_ref(index)[] debug_assert(ev.__bool__(), "entry in index must be full") @@ -801,10 +795,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( return (True, slot, index) self._next_index_slot(slot, perturb) - debug_assert(insert_slot.__bool__(), "never found a slot") - debug_assert(insert_index.__bool__(), "slot populated but not index!!") - return (False, insert_slot.value()[], insert_index.value()[]) - fn _over_load_factor(self) -> Bool: return 3 * self.size > 2 * self._reserved diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 22f3825799..ac0eeb2350 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -274,6 +274,40 @@ def test_dict_update_empty_new(): assert_equal(len(orig), 2) +@value +struct DummyKey(KeyElement): + var value: Int + + fn __hash__(self) -> Int: + return self.value + + fn __eq__(self, other: DummyKey) -> Bool: + return self.value == other.value + + fn __ne__(self, other: DummyKey) -> Bool: + return self.value != other.value + + +def test_mojo_issue_1729(): + var keys = List( + 7005684093727295727, + 2833576045803927472, + -446534169874157203, + -5597438459201014662, + -7007119737006385570, + 7237741981002255125, + -649171104678427962, + -6981562940350531355, + ) + var d = Dict[DummyKey, Int]() + for i in range(len(keys)): + d[DummyKey(keys[i])] = i + assert_equal(len(d), len(keys)) + for i in range(len(d)): + var k = keys[i] + assert_equal(i, d[k]) + + fn test[name: String, test_fn: fn () raises -> object]() raises: var name_val = name # FIXME(#26974): Can't pass 'name' directly. print("Test", name_val, "...", end="") @@ -308,6 +342,7 @@ def test_dict(): test["test_dict_update_nominal", test_dict_update_nominal]() test["test_dict_update_empty_origin", test_dict_update_empty_origin]() test["test_dict_update_empty_new", test_dict_update_empty_new]() + test["test_mojo_issue_1729", test_mojo_issue_1729]() def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): From 087145c8342284a31833ca4c496d37c8e06808bc Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 25 Apr 2024 14:41:22 -0400 Subject: [PATCH 0304/2019] [mojo-stdlib] Generialize `debug_assert` to take `Stringable` (#38540) There is nothing in the implementation that requires a `StringLiteral`; in fact, we actually need to instantiate a string from it anyway to concatenate for warnings. This patch also simplifies the implementation and renames some test files to conform to naming conventions. A redundant overload is also removed. MODULAR_ORIG_COMMIT_REV_ID: 252c211943a50ad2c84bcb169626ca2a4b0104d7 --- stdlib/src/builtin/debug_assert.mojo | 48 +++++++------------ stdlib/test/builtin/test_debug_assert.mojo | 1 + ...rror.mojo => test_debug_assert_error.mojo} | 2 +- ..._debug_assert_mojo_enable_assertions.mojo} | 0 .../builtin/test_debug_assert_warning.mojo | 2 + 5 files changed, 22 insertions(+), 31 deletions(-) rename stdlib/test/builtin/{test_debug_assert-error.mojo => test_debug_assert_error.mojo} (90%) rename stdlib/test/builtin/{test_debug_assert-mojo-enable-assertions.mojo => test_debug_assert_mojo_enable_assertions.mojo} (100%) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index f314afb412..36310e1143 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -21,24 +21,9 @@ from sys._build import is_kernels_debug_build from sys import triple_is_nvidia_cuda, is_defined -fn debug_assert(cond: Bool, msg: StringLiteral): - """Asserts that the condition is true. - - The `debug_assert` is similar to `assert` in C++. It is a no-op in release - builds unless MOJO_ENABLE_ASSERTIONS is defined. - - Right now, users of the mojo-sdk must explicitly specify `-D MOJO_ENABLE_ASSERTIONS` - to enable assertions. It is not sufficient to compile programs with `-debug-level full` - for enabling assertions in the library. - - Args: - cond: The bool value to assert. - msg: The message to display on failure. - """ - _debug_assert_impl(cond, msg) - - -fn debug_assert[boolable: Boolable](cond: boolable, msg: StringLiteral): +fn debug_assert[ + boolable: Boolable, stringable: Stringable +](cond: boolable, msg: stringable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -49,7 +34,8 @@ fn debug_assert[boolable: Boolable](cond: boolable, msg: StringLiteral): for enabling assertions in the library. Parameters: - boolable: The trait of the conditional. + boolable: The type of the condition. + stringable: The type of the message. Args: cond: The bool value to assert. @@ -58,7 +44,9 @@ fn debug_assert[boolable: Boolable](cond: boolable, msg: StringLiteral): _debug_assert_impl(cond, msg) -fn _debug_assert_impl[boolable: Boolable](cond: boolable, msg: StringLiteral): +fn _debug_assert_impl[ + boolable: Boolable, stringable: Stringable +](cond: boolable, msg: stringable): """Asserts that the condition is true.""" # Print an error and fail. @@ -71,22 +59,22 @@ fn _debug_assert_impl[boolable: Boolable](cond: boolable, msg: StringLiteral): @parameter if err or warn: - if cond.__bool__(): + if cond: return @parameter - if err: + if triple_is_nvidia_cuda(): + # On GPUs, assert shouldn't allocate. @parameter - if triple_is_nvidia_cuda(): + if err: abort() - return + else: + print("Assert Warning") + return + @parameter + if err: abort("Assert Error: " + str(msg)) else: - - @parameter - if triple_is_nvidia_cuda(): - print("Assert Warning") - return - print("Assert Warning:", msg) + print("Assert Warning:", str(msg)) diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index cd37b02e9f..594e54ce16 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -21,5 +21,6 @@ fn main(): print("== test_ok") debug_assert(True, "ok") + debug_assert(3, Error("also ok")) # CHECK-OK: is reached print("is reached") diff --git a/stdlib/test/builtin/test_debug_assert-error.mojo b/stdlib/test/builtin/test_debug_assert_error.mojo similarity index 90% rename from stdlib/test/builtin/test_debug_assert-error.mojo rename to stdlib/test/builtin/test_debug_assert_error.mojo index 4095ca4229..a796e8cd2a 100644 --- a/stdlib/test/builtin/test_debug_assert-error.mojo +++ b/stdlib/test/builtin/test_debug_assert_error.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %mojo -D KERNELS_BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL +# RUN: not --crash %mojo -D KERNELS_BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL # CHECK-FAIL-LABEL: test_fail diff --git a/stdlib/test/builtin/test_debug_assert-mojo-enable-assertions.mojo b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo similarity index 100% rename from stdlib/test/builtin/test_debug_assert-mojo-enable-assertions.mojo rename to stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo diff --git a/stdlib/test/builtin/test_debug_assert_warning.mojo b/stdlib/test/builtin/test_debug_assert_warning.mojo index 23d1b48d8d..1eb95d5baf 100644 --- a/stdlib/test/builtin/test_debug_assert_warning.mojo +++ b/stdlib/test/builtin/test_debug_assert_warning.mojo @@ -22,5 +22,7 @@ fn main(): print("== test_ok") # CHECK-WARN: Assert Warning: failed, but we don't terminate debug_assert(False, "failed, but we don't terminate") + # CHECK-WARN: Assert Warning: also failed, but in a Boolable + debug_assert(0, Error("also failed, but in a Boolable")) # CHECK-WARN: is reached print("is reached") From 06b3e8531486219c32ad7025b874a9c860722bee Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 25 Apr 2024 15:11:39 -0400 Subject: [PATCH 0305/2019] [mojo-stdlib] Enhance `debug_assert` message with location info (#38623) By making `debug_assert` @always_inline, we can capture its call location using the new `__call_location()` magic function. This location can be passed down to the implementation, which can handle the pretty printing. MODULAR_ORIG_COMMIT_REV_ID: 35348efd74552a38a336b2018b811c135decb089 --- docs/changelog.md | 3 ++ stdlib/src/builtin/_location.mojo | 11 +++++ stdlib/src/builtin/debug_assert.mojo | 47 +++++++++++-------- .../builtin/test_debug_assert_warning.mojo | 6 ++- 4 files changed, 45 insertions(+), 22 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index de6b0a3af8..f2263090ad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -217,6 +217,9 @@ what we publish. `@always_inline` functions, as well as limitations on its use in parameter contexts (see the documentation for more details). +- `debug_assert` now prints its location (filename, line, and column where it + was called) in its error message. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 0e06b54643..215d44046f 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -26,6 +26,17 @@ struct _SourceLocation(Stringable): fn __str__(self) -> String: return str(self.file_name) + ":" + str(self.line) + ":" + str(self.col) + fn prefix[T: Stringable](self, msg: T) -> String: + """Return the given message prefixed with the pretty-printer location. + + Parameters: + T: The type of the message. + + Args: + msg: The message to attach the prefix to. + """ + return "At " + str(self) + ": " + msg + @always_inline("nodebug") fn __source_location() -> _SourceLocation: diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 36310e1143..3a4773d514 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -19,8 +19,10 @@ These are Mojo built-ins, so you don't need to import them. from os import abort from sys._build import is_kernels_debug_build from sys import triple_is_nvidia_cuda, is_defined +from builtin._location import __call_location, _SourceLocation +@always_inline fn debug_assert[ boolable: Boolable, stringable: Stringable ](cond: boolable, msg: stringable): @@ -41,13 +43,6 @@ fn debug_assert[ cond: The bool value to assert. msg: The message to display on failure. """ - _debug_assert_impl(cond, msg) - - -fn _debug_assert_impl[ - boolable: Boolable, stringable: Stringable -](cond: boolable, msg: stringable): - """Asserts that the condition is true.""" # Print an error and fail. alias err = is_kernels_debug_build() or is_defined[ @@ -59,22 +54,34 @@ fn _debug_assert_impl[ @parameter if err or warn: - if cond: - return + if not cond: + _debug_assert_msg[err](msg, __call_location()) - @parameter - if triple_is_nvidia_cuda(): - # On GPUs, assert shouldn't allocate. - @parameter - if err: - abort() - else: - print("Assert Warning") - return +@no_inline +fn _debug_assert_msg[ + err: Bool, stringable: Stringable +](msg: stringable, loc: _SourceLocation): + """Aborts with (or prints) the given message and location. + + Note that it's important that this function doesn't get inlined; otherwise, + an indirect recursion of @always_inline functions is possible (e.g. because + abort's implementation could use debug_assert) + """ + + @parameter + if triple_is_nvidia_cuda(): + # On GPUs, assert shouldn't allocate. @parameter if err: - abort("Assert Error: " + str(msg)) + abort() else: - print("Assert Warning:", str(msg)) + print("Assert Warning") + return + + @parameter + if err: + abort(loc.prefix("Assert Error: " + str(msg))) + else: + print(loc.prefix("Assert Warning:"), str(msg)) diff --git a/stdlib/test/builtin/test_debug_assert_warning.mojo b/stdlib/test/builtin/test_debug_assert_warning.mojo index 1eb95d5baf..7928909e61 100644 --- a/stdlib/test/builtin/test_debug_assert_warning.mojo +++ b/stdlib/test/builtin/test_debug_assert_warning.mojo @@ -20,9 +20,11 @@ # CHECK-WARN: test_ok fn main(): print("== test_ok") - # CHECK-WARN: Assert Warning: failed, but we don't terminate + # CHECK-WARN: At {{.*}}test_debug_assert_warning.mojo:25:17: + # CHECK-WARN-SAME: Assert Warning: failed, but we don't terminate debug_assert(False, "failed, but we don't terminate") - # CHECK-WARN: Assert Warning: also failed, but in a Boolable + # CHECK-WARN: At {{.*}}test_debug_assert_warning.mojo:28:17: + # CHECK-WARN-SAME: Assert Warning: also failed, but in a Boolable debug_assert(0, Error("also failed, but in a Boolable")) # CHECK-WARN: is reached print("is reached") From 8a285314aedf3abe77512235f2a5bcc9024d396c Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 25 Apr 2024 14:56:07 -0500 Subject: [PATCH 0306/2019] [docs] Add PR links to every Mojo open source changelog entry (#38660) MODULAR_ORIG_COMMIT_REV_ID: af02416f98c8aa3b108ccff0cbf477e0dfe030e7 --- docs/changelog.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f2263090ad..70e452cd0b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -68,48 +68,66 @@ what we publish. ``` - `List` now has several new methods: - - `pop(index)` for removing an element at a particular index.\ + - `pop(index)` for removing an element at a particular index. + ([PR #2041](https://github.com/modularml/mojo/pull/2041))\ By default, `List.pop()` removes the last element in the list. -- `resize(new_size)` for resizing the list without the need to + - `resize(new_size)` for resizing the list without the need to specify an additional value. -- `insert(index, value)` for inserting a value at a specified index + ([PR #2140](https://github.com/modularml/mojo/pull/2140)) + + - `insert(index, value)` for inserting a value at a specified index into the `List`. -- constructor from `(ptr, size, capacity)` to to avoid needing to do a deep + ([PR #2148](https://github.com/modularml/mojo/pull/2148)) + + - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep copy of an existing contiguous memory allocation when constructing a new `List`. + ([PR #2182](https://github.com/modularml/mojo/pull/2182)) - `Dict` now has a `update()` method to update keys/values from another `Dict`. + ([PR #2085](https://github.com/modularml/mojo/pull/2085)) -- `Set` now has named methods for set operations: +- `Set` now has named methods for set operations + ([PR #2214](https://github.com/modularml/mojo/pull/2214)): - `Set.difference()` mapping to `-` - `Set.difference_update()` mapping to `-=` - `Set.intersection_update()` mapping to `&=` - `Set.update()` mapping to `|=` - `String` now has `removeprefix()` and `removesuffix()` methods. + ([PR #2038](https://github.com/modularml/mojo/pull/2038)) - `Optional` now implements `__is__` and `__isnot__` methods so that you can compare an `Optional` with `None`, e.g. `Optional(1) is not None` for example. + ([PR #2082](https://github.com/modularml/mojo/pull/2082)) - The `ord` and `chr` functions have been improved to accept any Unicode character. + ([PR #2149](https://github.com/modularml/mojo/pull/2149)) - `Atomic` is now movable. + ([PR #2088](https://github.com/modularml/mojo/pull/2088)) - `Dict` and `List` are both `Boolable` now. + ([PR #2262](https://github.com/modularml/mojo/pull/2262)) - `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` instead of raising an error. + ([PR #2225](https://github.com/modularml/mojo/pull/2225)) - `SIMD` now implements `__rmod__`. + ([PR #2186](https://github.com/modularml/mojo/pull/2186)) - `bool(None)` is now implemented. + ([PR #2249](https://github.com/modularml/mojo/pull/2249)) - The `DTypePointer` type now implements `gather` for gathering a `SIMD` vector from offsets of a current pointer. Similarly, support for `scatter` was added to scatter a `SIMD` vector into offsets of the current pointer. + ([PR #2268](https://github.com/modularml/mojo/pull/2268)) - The `len` function for unary `range` with negative end values has been fixed so that things like `len(range(-1))` work correctly now. + ([PR #2204](https://github.com/modularml/mojo/pull/2204)) - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking @@ -119,6 +137,7 @@ what we publish. - The `testing.assert_equal[SIMD]()` now raises if any of the elements mismatch in the two `SIMD` arguments being compared. + ([PR #2279](https://github.com/modularml/mojo/pull/2279)) - The `testing.assert_almost_equal` and `math.isclose` functions now have an `equal_nan` flag. When set to True, then NaNs are considered equal. @@ -248,6 +267,7 @@ what we publish. - All of the pointers got a pass of cleanup to make them more consistent, for example the `unsafe.bitcast` global function is now a consistent `bitcast` method on the pointers, which can convert element type and address space. + - The `Reference` type has several changes, including: 1) It is now located in `memory.reference` instead of `memory.unsafe`. 2) `Reference` now has an unsafe `unsafe_bitcast` method like `UnsafePointer`. From 4e521fa035c4181f54bfb1dc332c88606ac8f6f9 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 25 Apr 2024 16:11:58 -0400 Subject: [PATCH 0307/2019] [mojo-stdlib] Make `Bool` implicitly convertible from `Boolable` (#38556) This means we can pass types conforming to `Boolable` to functions that expect a `Bool`. MODULAR_ORIG_COMMIT_REV_ID: 95b50e709a4fc67e42675c78f1178a26aa689f1f --- docs/changelog.md | 31 ++++++++++++++++++++++++++++ stdlib/src/builtin/bool.mojo | 15 ++++++++++++++ stdlib/src/builtin/debug_assert.mojo | 5 +---- stdlib/src/builtin/simd.mojo | 26 ----------------------- stdlib/src/testing/testing.mojo | 18 ++++++---------- stdlib/test/builtin/test_bool.mojo | 16 +++++++++++++- 6 files changed, 68 insertions(+), 43 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 70e452cd0b..37cebd7b64 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -337,6 +337,37 @@ what we publish. [PR #2370](https://github.com/modularml/mojo/pull/2370), [PR #2371](https://github.com/modularml/mojo/pull/2371)) +- `Bool` can now be implicitly converted from any type conforming to `Boolable`. + This means that you no longer need to write things like this: + + ```mojo + @value + struct MyBoolable: + fn __bool__(self) -> Bool: ... + + fn takes_boolable[T: Boolable](cond: T): ... + + takes_boolable(MyBoolable()) + ``` + + Instead, you can simply have + + ```mojo + fn takes_bool(cond: Bool): ... + + takes_bool(MyBoolable()) + ``` + + Note that calls to `takes_bool` will perform the implicit conversion, so in + some cases is it still better to explicitly declare type parameter, e.g.: + + ```mojo + fn takes_two_boolables[T: Boolable](a: T, b: T): + # Short circuit means `b.__bool__()` might not be evaluated. + if a.__bool__() and b.__bool__(): + ... + ``` + ### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index fb2eaef300..780dde1c80 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -86,6 +86,21 @@ struct Bool( """ return value.__bool__() + @always_inline("nodebug") + fn __init__[boolable: Boolable](value: boolable) -> Bool: + """Implicitly convert a Boolable value to a Bool. + + Parameters: + boolable: The Boolable type. + + Args: + value: The boolable value. + + Returns: + The constructed Bool value. + """ + return value.__bool__() + @always_inline("nodebug") fn __bool__(self) -> Bool: """Convert to Bool. diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 3a4773d514..ec101b76e2 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -23,9 +23,7 @@ from builtin._location import __call_location, _SourceLocation @always_inline -fn debug_assert[ - boolable: Boolable, stringable: Stringable -](cond: boolable, msg: stringable): +fn debug_assert[stringable: Stringable](cond: Bool, msg: stringable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -36,7 +34,6 @@ fn debug_assert[ for enabling assertions in the library. Parameters: - boolable: The type of the condition. stringable: The type of the message. Args: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8e8e6d62f3..74c162d825 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -374,32 +374,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return self.reduce_and() return (self != 0).reduce_and() - @staticmethod - @always_inline("nodebug") - fn splat(x: Bool) -> Self: - """Splats (broadcasts) the element onto the vector. - - Args: - x: The input value. - - Returns: - A new SIMD vector whose elements are the same as the input value. - """ - _simd_construction_checks[type, size]() - constrained[type == DType.bool, "input type must be boolean"]() - var val = SIMD[DType.bool, size] { - value: __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - DType.bool.value, - `>`, - ] - ](x.value) - } - return rebind[Self](val) - @staticmethod @always_inline("nodebug") fn splat(x: Scalar[type]) -> Self: diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 1d48ff0cde..3f8fffdb67 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -63,15 +63,12 @@ fn _isclose( @always_inline -fn assert_true[ - T: Boolable -](val: T, msg: String = "condition was unexpectedly False") raises: +fn assert_true( + val: Bool, msg: String = "condition was unexpectedly False" +) raises: """Asserts that the input value is True. If it is not then an Error is raised. - Parameters: - T: A Boolable type. - Args: val: The value to assert to be True. msg: The message to be printed if the assertion fails. @@ -84,15 +81,12 @@ fn assert_true[ @always_inline -fn assert_false[ - T: Boolable -](val: T, msg: String = "condition was unexpectedly True") raises: +fn assert_false( + val: Bool, msg: String = "condition was unexpectedly True" +) raises: """Asserts that the input value is False. If it is not then an Error is raised. - Parameters: - T: A Boolable type. - Args: val: The value to assert to be False. msg: The message to be printed if the assertion fails. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 9b343c4e88..a987d34930 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import assert_equal +from testing import assert_equal, assert_true def test_bool_cast_to_int(): @@ -29,6 +29,20 @@ def test_bool_none(): assert_equal(bool(test), False) +@value +struct MyTrue: + fn __bool__(self) -> Bool: + return True + + +fn takes_bool(cond: Bool) -> Bool: + return cond + + +def test_convert_from_boolable(): + assert_true(takes_bool(MyTrue())) + + def main(): test_bool_cast_to_int() test_bool_none() From e6f1fad6965fcf6e305936982ee74150775c3010 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 25 Apr 2024 16:37:08 -0500 Subject: [PATCH 0308/2019] [docs] Add 'Fixes #xyz' to changelog entries that fix feature requests (#38676) MODULAR_ORIG_COMMIT_REV_ID: 4c7d20747d8d2179684ae162449d85ba6f14f229 --- docs/changelog.md | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 37cebd7b64..5d866660e8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -69,20 +69,24 @@ what we publish. - `List` now has several new methods: - `pop(index)` for removing an element at a particular index. - ([PR #2041](https://github.com/modularml/mojo/pull/2041))\ + ([PR #2041](https://github.com/modularml/mojo/pull/2041)) + (Fixes [#2017](https://github.com/modularml/mojo/issues/2017))\ By default, `List.pop()` removes the last element in the list. - `resize(new_size)` for resizing the list without the need to specify an additional value. - ([PR #2140](https://github.com/modularml/mojo/pull/2140)) + ([PR #2140](https://github.com/modularml/mojo/pull/2140), + Fixes [#2133](https://github.com/modularml/mojo/issues/2133)) - `insert(index, value)` for inserting a value at a specified index into the `List`. - ([PR #2148](https://github.com/modularml/mojo/pull/2148)) + ([PR #2148](https://github.com/modularml/mojo/pull/2148), + Fixes [#2134](https://github.com/modularml/mojo/issues/2134)) - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep copy of an existing contiguous memory allocation when constructing a new `List`. - ([PR #2182](https://github.com/modularml/mojo/pull/2182)) + ([PR #2182](https://github.com/modularml/mojo/pull/2182), + Fixes [#2170](https://github.com/modularml/mojo/issues/2170)) - `Dict` now has a `update()` method to update keys/values from another `Dict`. ([PR #2085](https://github.com/modularml/mojo/pull/2085)) @@ -102,10 +106,12 @@ what we publish. ([PR #2082](https://github.com/modularml/mojo/pull/2082)) - The `ord` and `chr` functions have been improved to accept any Unicode character. - ([PR #2149](https://github.com/modularml/mojo/pull/2149)) + ([PR #2149](https://github.com/modularml/mojo/pull/2149), + Contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) - `Atomic` is now movable. - ([PR #2088](https://github.com/modularml/mojo/pull/2088)) + ([PR #2088](https://github.com/modularml/mojo/pull/2088), + Fixes [#2026](https://github.com/modularml/mojo/issues/2026)) - `Dict` and `List` are both `Boolable` now. ([PR #2262](https://github.com/modularml/mojo/pull/2262)) @@ -115,7 +121,8 @@ what we publish. ([PR #2225](https://github.com/modularml/mojo/pull/2225)) - `SIMD` now implements `__rmod__`. - ([PR #2186](https://github.com/modularml/mojo/pull/2186)) + ([PR #2186](https://github.com/modularml/mojo/pull/2186), + Fixes [#1482](https://github.com/modularml/mojo/issues/1482)) - `bool(None)` is now implemented. ([PR #2249](https://github.com/modularml/mojo/pull/2249)) @@ -181,10 +188,24 @@ what we publish. - Added `reversed()` for creating reversed iterators. Several range types, `List`, and `Dict` now support iterating in reverse. ([PR #2215](https://github.com/modularml/mojo/pull/2215), - [PR #2327](https://github.com/modularml/mojo/pull/2327)) + [PR #2327](https://github.com/modularml/mojo/pull/2327), + Contributes towards [#2325](https://github.com/modularml/mojo/issues/2325)) -- Added left and right shift operations for `object` - ([PR #2247](https://github.com/modularml/mojo/pull/2247)) +- `object` now supports the division, modulo, and left and right shift + operators. + ([PR #2230](https://github.com/modularml/mojo/pull/2230), + [PR #2247](https://github.com/modularml/mojo/pull/2247), + Fixes [#2224](https://github.com/modularml/mojo/issues/2224)) + + The following operator dunder methods were added: + + - `object.__mod__` + - `object.__truediv__` + - `object.__floordiv__` + - `object.__lshift__` + - `object.__rshift__` + + As well as the in-place and reverse variants. - Added checked arithmetic operations. ([PR #2138](https://github.com/modularml/mojo/pull/2138)) @@ -201,7 +222,8 @@ what we publish. the numeric limits of the type. - Added `os.remove()` and `os.unlink()` for deleting files. - ([PR #2310](https://github.com/modularml/mojo/pull/2310)) + ([PR #2310](https://github.com/modularml/mojo/pull/2310), + Fixes [#2306](https://github.com/modularml/mojo/issues/2306)) - Properties can now be specified on inline mlir ops: @@ -313,7 +335,8 @@ what we publish. - `Optional.value()` will now return a reference instead of a copy of the contained value. - ([PR #2226](https://github.com/modularml/mojo/pull/2226)) + ([PR #2226](https://github.com/modularml/mojo/pull/2226), + Fixes [#2179](https://github.com/modularml/mojo/issues/2179)) To perform a copy manually, dereference the result: @@ -327,7 +350,8 @@ what we publish. [`proposals/byte-as-uint8.md`](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), began transition to using `UInt8` by changing the data pointer of `Error` to `DTypePointer[DType.uint8]`. - ([PR #2318](https://github.com/modularml/mojo/pull/2318)) + ([PR #2318](https://github.com/modularml/mojo/pull/2318), + Contributes towards [#2317](https://github.com/modularml/mojo/issues/2317)) - Continued transition to `UnsafePointer` away from the legacy `Pointer` type in various standard library APIs and internals. @@ -444,8 +468,3 @@ what we publish. - [#2068](https://github.com/modularml/mojo/issues/2068) Fix simd.reduce for size_out == 2 ([PR #2102](https://github.com/modularml/mojo/pull/2102)) - -- [#2224](https://github.com/modularml/mojo/issues/2224) - `object` now implements `__truediv__`, `__floordiv__` and related divison - and modulo operators. - ([PR #2230](https://github.com/modularml/mojo/pull/2230)) From e7f8a57f0611c90ed205c9741d36ccbc3e12fc67 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 25 Apr 2024 21:55:56 -0400 Subject: [PATCH 0309/2019] [mojo-stdlib] Enhance `testing.assert_*` with location info in their messages (#38653) The patch also includes some refactoring and cleanup in the `testing.mojo` to improve readability and code reuse. MODULAR_ORIG_COMMIT_REV_ID: e02d18b05450fb0cb096e417ba313e373917ab53 --- docs/changelog.md | 3 +- stdlib/src/testing/testing.mojo | 60 ++++++++++++++++--------- stdlib/test/testing/test_assertion.mojo | 32 ++++++++++++- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5d866660e8..51db2004ca 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -259,7 +259,8 @@ what we publish. contexts (see the documentation for more details). - `debug_assert` now prints its location (filename, line, and column where it - was called) in its error message. + was called) in its error message. Similarly, the `assert_*` helpers in the + `testing` module now include location information in their messages. ### 🦋 Changed diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 3f8fffdb67..a7b25e7c10 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -20,6 +20,7 @@ from testing import assert_true """ from collections import Optional from utils._numerics import isnan +from builtin._location import __call_location, _SourceLocation # ===----------------------------------------------------------------------=== # # Utilities @@ -62,6 +63,11 @@ fn _isclose( # ===----------------------------------------------------------------------=== # +@always_inline +fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: + return loc.prefix("AssertionError: " + str(msg)) + + @always_inline fn assert_true( val: Bool, msg: String = "condition was unexpectedly False" @@ -77,7 +83,7 @@ fn assert_true( An Error with the provided message if assert fails and `None` otherwise. """ if not val: - raise Error("AssertionError: " + msg) + raise _assert_error(msg, __call_location()) @always_inline @@ -95,7 +101,7 @@ fn assert_false( An Error with the provided message if assert fails and `None` otherwise. """ if val: - raise Error("AssertionError: " + msg) + raise _assert_error(msg, __call_location()) trait Testable(EqualityComparable, Stringable): @@ -122,7 +128,7 @@ fn assert_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs != rhs: - raise _assert_equal_error(str(lhs), str(rhs), msg=msg) + raise _assert_equal_error(str(lhs), str(rhs), msg, __call_location()) # TODO: Remove the String and SIMD overloads once we have more powerful traits. @@ -140,7 +146,7 @@ fn assert_equal(lhs: String, rhs: String, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs != rhs: - raise _assert_equal_error(lhs, rhs, msg=msg) + raise _assert_equal_error(lhs, rhs, msg, __call_location()) @always_inline @@ -162,10 +168,10 @@ fn assert_equal[ Raises: An Error with the provided message if assert fails and `None` otherwise. """ - # `if lhs != rhs:` is not enough. `reduce_or()` must be used here, otherwise, if any of the elements are - # equal, the error is not triggered. + # `if lhs != rhs:` is not enough. `reduce_or()` must be used here, + # otherwise, if any of the elements are equal, the error is not triggered. if (lhs != rhs).reduce_or(): - raise _assert_equal_error(str(lhs), str(rhs), msg=msg) + raise _assert_equal_error(str(lhs), str(rhs), msg, __call_location()) @always_inline @@ -185,7 +191,9 @@ fn assert_not_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: - raise _assert_not_equal_error(str(lhs), str(rhs), msg=msg) + raise _assert_not_equal_error( + str(lhs), str(rhs), msg, __call_location() + ) @always_inline @@ -202,7 +210,7 @@ fn assert_not_equal(lhs: String, rhs: String, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: - raise _assert_not_equal_error(str(lhs), str(rhs), msg=msg) + raise _assert_not_equal_error(lhs, rhs, msg, __call_location()) @always_inline @@ -225,7 +233,9 @@ fn assert_not_equal[ An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: - raise _assert_not_equal_error(str(lhs), str(rhs), msg=msg) + raise _assert_not_equal_error( + str(lhs), str(rhs), msg, __call_location() + ) @always_inline @@ -263,30 +273,40 @@ fn assert_almost_equal[ lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan ) if not almost_equal: - var err = "AssertionError: " + str(lhs) + " is not close to " + str( + var err = str(lhs) + " is not close to " + str( rhs ) + " with a diff of " + _abs(lhs - rhs) if msg: err += " (" + msg + ")" - raise err + raise _assert_error(err, __call_location()) fn _assert_equal_error( - lhs: String, rhs: String, msg: String = "" -) raises -> Error: - var err = "AssertionError: `left == right` comparison failed:\n left: " + lhs + "\n right: " + rhs + lhs: String, rhs: String, msg: String, loc: _SourceLocation +) -> String: + var err = ( + "`left == right` comparison failed:\n left: " + + lhs + + "\n right: " + + rhs + ) if msg: err += "\n reason: " + msg - raise err + return _assert_error(err, loc) fn _assert_not_equal_error( - lhs: String, rhs: String, msg: String = "" -) raises -> Error: - var err = "AssertionError: `left != right` comparison failed:\n left: " + lhs + "\n right: " + rhs + lhs: String, rhs: String, msg: String, loc: _SourceLocation +) -> String: + var err = ( + "`left != right` comparison failed:\n left: " + + lhs + + "\n right: " + + rhs + ) if msg: err += "\n reason: " + msg - raise err + return _assert_error(err, loc) struct assert_raises: diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 9251bb1d40..5a50096ba1 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -12,7 +12,14 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import assert_equal, assert_not_equal, assert_raises +from testing import ( + assert_equal, + assert_not_equal, + assert_raises, + assert_true, + assert_false, + assert_almost_equal, +) @value @@ -50,7 +57,30 @@ def test_assert_equal_with_simd(): assert_equal(SIMD[DType.uint8, 2](1, 1), SIMD[DType.uint8, 2](1, 2)) +def test_assert_messages(): + try: + assert_true(False) + except e: + assert_true("test_assertion.mojo:62:20: AssertionError:" in str(e)) + + try: + assert_false(True) + except e: + assert_true("test_assertion.mojo:67:21: AssertionError:" in str(e)) + + try: + assert_equal(1, 0) + except e: + assert_true("test_assertion.mojo:72:21: AssertionError:" in str(e)) + + try: + assert_not_equal(0, 0) + except e: + assert_true("test_assertion.mojo:77:25: AssertionError:" in str(e)) + + def main(): test_assert_equal_is_generic() test_assert_not_equal_is_generic() test_assert_equal_with_simd() + test_assert_messages() From cf75566466abdba5b22ae5e9712df2afa3a70a7d Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 30 Apr 2024 19:17:53 -0700 Subject: [PATCH 0310/2019] [docs] Update docs about telemetry for 24.3 (#38356) (#39002) docs cherrypick from [Internal link] L0 telemetry is now required and cannot be disabled. As such, update docs to document the `telemetry.level` config instead of `telemetry.enabled` which is misleading because if set `false` it will still collect the L0 data. RE: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: ef5828a2a74c5ab8545f76bb840ccb93e722464e --- docs/faq.md | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 11a21e5fab..3054b4786b 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -346,27 +346,37 @@ containers to enable remote development in Mojo. ### Does the Mojo SDK collect telemetry? -Yes, in combination with the Modular CLI tool, the Mojo SDK collects some basic -system information and crash reports that enable us to identify, analyze, and -prioritize Mojo issues. +Yes, the Mojo SDK collects some basic system information, basic +compiler/runtime events, and crash reports that enable us to identify, analyze, +and prioritize Mojo issues. -Mojo is still in its early days, and this telemetry is crucial to help us -quickly identify problems and improve Mojo. Without this telemetry, we would -have to rely on user-submitted bug reports, and in our decades of building -developer products, we know that most people don’t bother. Plus, a lot of -product issues are not easily identified by users or quantifiable with -individual bug reports. The telemetry provides us the insights we need to build -Mojo into a premier developer product. +This telemetry is crucial to help us quickly identify problems and improve our +products. Without this telemetry, we would have to rely on user-submitted bug +reports, and in our decades of experience building developer products, we know +that most people don’t do that. The telemetry provides us the insights we need +to build better products for you. -Of course, if you don't want to share this information with us, you can easily -opt-out of all telemetry, using the [`modular` CLI](/cli). To stop sharing -system information, run this: +You can opt-out of the crash report and compiler/runtime telemetry, but +package install/update/uninstall events cannot be +disabled (see the [MAX SDK terms](https://www.modular.com/legal/max)). -`modular config-set telemetry.enabled=false` +To disable crash reports, use this command: -To stop sharing crash reports, run this: +```sh +modular config-set crash_reporting.enabled=false +``` -`modular config-set crash_reporting.enabled=false` +To reduce other telemetry to only the required telemetry events, use this +command: + +```sh +modular config-set telemetry.level=0 +``` + +There are 3 telemetry levels: `0` currently records nothing (unless you're also +using MAX, which records hardware information and session durations); `1` +records high-level events such as when the compiler is invoked; and `2` records +more detail such as the time spend compiling. ## Versioning & compatibility From 0e9a73955e1a9f40c8d2a266437c9f670dd558d1 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 1 May 2024 14:59:22 -0700 Subject: [PATCH 0311/2019] [Docs] New Mojo Types doc. (#38241) (cherry-pick from main) (#38839) Fixes #36699 MODULAR_ORIG_COMMIT_REV_ID: 90158c3c3d0f7700e3236fe9d0a98e3dcc55e3eb --- docs/manual/types.ipynb | 1002 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1002 insertions(+) create mode 100644 docs/manual/types.ipynb diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb new file mode 100644 index 0000000000..4b03fe549f --- /dev/null +++ b/docs/manual/types.ipynb @@ -0,0 +1,1002 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Types\n", + "sidebar_position: 1\n", + "description: Standard Mojo data types.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All values in Mojo have an associated data type. Most of the types are\n", + "_nominal_ types, defined by a [`struct`](/mojo/manual/structs). These types are\n", + "nominal (or \"named\") because type equality is determined by the type's _name_,\n", + "not its _structure_.\n", + "\n", + "There are a some types that aren't defined as structs:\n", + "\n", + "* Functions are typed based on their signatures.\n", + "* `NoneType` is a type with one instance, the `None` object, which is used to\n", + " signal \"no value.\"\n", + "\n", + "Mojo comes with a standard library that provides a number of useful types and\n", + "utility functions. These standard types aren’t privileged. Each of the standard\n", + "library types is defined just like user-defined types—even basic types like \n", + "[`Int`](/mojo/stdlib/builtin/int/Int) and \n", + "[`String`](/mojo/stdlib/builtin/string/String). But these standard library types\n", + "are the building blocks you'll use for most Mojo programs.\n", + "\n", + "The most common types are _built-in types_, which are always available and\n", + "don't need to be imported. These include types for numeric values, strings,\n", + "boolean values, and others.\n", + "\n", + "The standard library also includes many more types that you can import as\n", + "needed, including collection types, utilities for interacting with the\n", + "filesystem and getting system information, and so on.\n", + "\n", + "## Numeric types\n", + "\n", + "Mojo's most basic numeric type is `Int`, which represents a signed integer of\n", + "the largest size supported by the system—typically 64 bits or 32 bits.\n", + "\n", + "Mojo also has built-in types for integer and floating-point values of various\n", + "precisions:\n", + "\n", + "
\n", + "\n", + "| Type name | Description |\n", + "|---------------|--------------|\n", + "| `Int8` | 8-bit signed integer |\n", + "| `UInt8` | 8-bit unsigned integer |\n", + "| `Int16` | 16-bit signed integer |\n", + "| `UInt16` | 16-bit unsigned integer |\n", + "| `Int32` | 32-bit signed integer |\n", + "| `UInt32` | 32-bit unsigned integer |\n", + "| `Int64` | 64-bit signed integer |\n", + "| `UInt64` | 64-bit unsigned integer |\n", + "| `Float16` | 16-bit floating point number (IEEE 754-2008 binary16) |\n", + "| `Float32` | 32-bit floating point number (IEEE 754-2008 binary32) |\n", + "| `Float64` | 64-bit floating point number (IEEE 754-2008 binary64) |\n", + "
Table 1. Numeric types with specific precision
\n", + "
\n", + "\n", + "The types in Table 1 are actually all aliases to a single type, \n", + "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD), which is discussed later.\n", + "\n", + "All of the numeric types support the usual numeric and bitwise operators. The \n", + "[`math`](/mojo/stdlib/math/) module provides a number of additional math\n", + "functions.\n", + "\n", + "You may wonder when to use `Int` and when to use the other integer \n", + "types. In general, `Int` is good safe default when you need an integer type and\n", + "you don't require a specific bit width. Using `Int` as the default integer type\n", + "for APIs makes APIs more consistent and predictable.\n", + "\n", + "\n", + "### Floating-point numbers\n", + "\n", + "Floating-point types represent real numbers. Because not all real numbers can be\n", + "expressed in a finite number of bits, floating-point numbers can't represent\n", + "every value exactly.\n", + "\n", + "The floating-point types listed in Table 1—`Float64`, `Float32`, and \n", + "`Float16`—follow the IEEE 754-2008 standard for representing floating-point \n", + "values. Each type includes a sign bit, one set of bits representing an exponent,\n", + "and another set representing the fraction or mantissa. Table 2 shows how each of \n", + "these types are represented in memory.\n", + "\n", + "
\n", + "\n", + "| Type name | Sign | Exponent | Mantissa |\n", + "|------------|-------|-----------|----------|\n", + "| `Float64` | 1 bit | 11 bits | 52 bits |\n", + "| `Float32` | 1 bit | 8 bits | 23 bits |\n", + "| `Float16` | 1 bit | 5 bits | 10 bits |\n", + "
Table 2. Details of floating-point types
\n", + "
\n", + "\n", + "Numbers with exponent values of all ones or all zeros represent special values,\n", + "allowing floating-point numbers to represent infinity, negative infinity,\n", + "signed zeros, and not-a-number (NaN). For more details on how numbers are\n", + "represented, see [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) on\n", + "Wikipedia.\n", + "\n", + "A few things to note with floating-point values:\n", + "\n", + "- Rounding errors. Rounding may produce unexpected results. For example, 1/3\n", + " can't be represented exactly in these floating-point formats. The more\n", + " operations you perform with floating-point numbers, the more the rounding\n", + " errors accumulate. \n", + "\n", + "- Space between consecutive numbers. The space between consecutive numbers is\n", + " variable across the range of a floating-point number format. For numbers close\n", + " to zero, the distance between consecutive numbers is very small. is For large\n", + " positive and negative numbers, the space between consecutive numbers is\n", + " greater than 1, so it may not be possible to represent consecutive integers.\n", + "\n", + "Because the values are approximate, it is rarely useful to compare them with\n", + "the equality operator (`==`). Consider the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "var big_num = 1.0e16\n", + "var bigger_num = big_num+1.0\n", + "print(big_num == bigger_num)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparison operators (`<` `>=` and so on) work with floating point numbers. You\n", + "can also use the [`math.isclose()`](/mojo/stdlib/math/math/isclose) function to\n", + "compare whether two floating-point numbers are equal within a specified\n", + "tolerance." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Numeric literals\n", + "\n", + "In addition to these numeric types, the standard libraries provides integer and\n", + "floating-point literal types, \n", + "[`IntLiteral`](/mojo/stdlib/builtin/int_literal/IntLiteral) and \n", + "[`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral).\n", + "\n", + "These literal types are used at compile time to represent literal numbers that\n", + "appear in the code. In general, you should never instantiate these types\n", + "yourself.\n", + "\n", + "Table 3 summarizes the literal formats you can use to represent numbers.\n", + "\n", + "
\n", + "\n", + "| Format | Examples | Notes |\n", + "|--------|---------|-------|\n", + "| Integer literal | `1760` | Integer literal, in decimal format. |\n", + "| Hexadecimal literal | `0xaa`, `0xFF` | Integer literal, in hexadecimal format.
Hex digits are case-insensitive. |\n", + "| Octal literal | `0o77` | Integer literal, in octal format. |\n", + "| Binary literal | `0b0111` | Integer literal, in binary format. |\n", + "| Floating-point literal | `3.14`, `1.2e9` | Floating-point literal.
Must include the decimal point to be interpreted as floating-point. |\n", + "
Table 3. Numeric literal formats
\n", + "
\n", + "\n", + "At compile time, the literal types are arbitrary-precision (also called \n", + "infinite-precision) values, so the compiler can perform compile-time \n", + "calculations without overflow or rounding errors.\n", + "\n", + "At runtime the values are converted to finite-precision types—`Int` for\n", + "integer values, and `Float64` for floating-point values. (This process of \n", + "converting a value that can only exist at compile time into a runtime value is\n", + "called _materialization_.) \n", + "\n", + "The following code sample shows the difference between an arbitrary-precision \n", + "calculation and the same calculation done using `Float64` values at runtime,\n", + "which suffers from rounding errors." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 0.99999999999999978\n" + ] + } + ], + "source": [ + "var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0)\n", + "# use a variable to force the following calculation to occur at runtime\n", + "var three = 3.0\n", + "var finite_precision = three * (4.0 / three - 1.0)\n", + "print(arbitrary_precision, finite_precision)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### `SIMD` and `DType`\n", + "\n", + "To support high-performance numeric processing, Mojo uses the\n", + "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type as the basis for its numeric\n", + "types. SIMD (single instruction, multiple data) is a processor technology that\n", + "allows you to perform an operation on an entire set of operands at once. Mojo's\n", + "`SIMD` type abstracts SIMD operations. A `SIMD` value represents a SIMD\n", + "_vector_—that is, a fixed-size array of values that can fit into a processor's\n", + "register. SIMD vectors are defined by two\n", + "[_parameters_](/mojo/manual/parameters/):\n", + "\n", + "- A `DType` value, defining the data type in the vector (for example, \n", + " 32-bit floating-point numbers).\n", + "- The number of elements in the vector, which must be a power of two.\n", + "\n", + "For example, you can define a vector of four `Float32` values like this:" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Math operations on SIMD values are \n", + "applied _elementwise_, on each individual element in the vector. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2, 6, 15, 28]\n" + ] + } + ], + "source": [ + "var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7)\n", + "var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4)\n", + "var product = vec1 * vec2\n", + "print(product)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "### Scalar values\n", + "\n", + "The `SIMD` module defines several _type aliases_ that are shorthand for\n", + "different types of `SIMD` vectors. In particular, the `Scalar` type is just a\n", + "`SIMD` vector with a single element. The numeric types listed in \n", + "[Table 1](#table-1), like `Int8` and `Float32` are actually type aliases for\n", + "different types of scalar values:\n", + "\n", + "```mojo\n", + "alias Scalar = SIMD[size=1]\n", + "alias Int8 = Scalar[DType.int8]\n", + "alias Float32 = Scalar[DType.float32]\n", + "```\n", + "\n", + "This may seem a little confusing at first, but it means that whether you're \n", + "working with a single `Float32` value or a vector of float32 values,\n", + "the math operations go through exactly the same code path." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### The `DType` type\n", + "\n", + "The `DType` struct describes the different data types that a `SIMD` vector can\n", + "hold, and defines a number of utility functions for operating on those data\n", + "types. The `DType` struct defines a set of aliases that act as identifiers for\n", + "the different data types, like `DType.int8` and `DType.float32`. You use\n", + "these aliases when declaring a `SIMD` vector:\n", + "\n", + "```mojo\n", + "var v: SIMD[DType.float64, 16]\n", + "```\n", + "\n", + "Note that `DType.float64` isn't a _type_, it's a value that describes a data\n", + "type. You can't create a variable with the type `DType.float64`. You can create\n", + "a variable with the type `SIMD[DType.float64, 1]` (or `Float64`, which is the\n", + "same thing).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "float32 is floating point: True\n", + "float32 is integral: False\n", + "Min/max finite values for float32\n", + "-3.4028234663852886e+38 3.4028234663852886e+38\n" + ] + } + ], + "source": [ + "from math.limit import max_finite, min_finite\n", + "\n", + "def describeDType[dtype: DType]():\n", + " print(dtype, \"is floating point:\", dtype.is_floating_point())\n", + " print(dtype, \"is integral:\", dtype.is_integral())\n", + " print(\"Min/max finite values for\", dtype)\n", + " print(min_finite[dtype](), max_finite[dtype]())\n", + "\n", + "describeDType[DType.float32]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "There are several other data types in the standard library that also use\n", + "the `DType` abstraction. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Strings\n", + "\n", + "Mojo's `String` type represents a mutable string. (For Python programmers, note\n", + "that this is different from Python's standard string, which is immutable.)\n", + "Strings support a variety of operators and common methods.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Testing Mojo strings\n" + ] + } + ], + "source": [ + "var s: String = \"Testing\"\n", + "s += \" Mojo strings\"\n", + "print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Most standard library types conform to the \n", + "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", + "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", + "explicitly convert a value to string.\n", + "\n", + "When concatenating values to a string using the `+` operator, `Stringable`\n", + "will implicitly convert `String`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Items in list: 5\n" + ] + } + ], + "source": [ + "var s = str(\"Items in list: \") + 5\n", + "print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### String literals\n", + "\n", + "As with numeric types, the standard library includes a string literal type used\n", + "to represent literal strings in the program source. String literals are\n", + "enclosed in either single or double quotes.\n", + "\n", + "Adjacent literals are concatenated together, so you can define a long string\n", + "using a series of literals broken up over several lines:\n", + "\n", + "```\n", + "var s = \"A very long string which is \"\n", + " \"broken into two literals for legibility.\"\n", + "```\n", + "\n", + "To define a multi-line string, enclose the literal in three single or double\n", + "quotes:\n", + "\n", + "```\n", + "var s = \"\"\"\n", + "Multi-line string literals let you \n", + "enter long blocks of text, including \n", + "newlines.\"\"\"\n", + "```\n", + "\n", + "Note that the triple double quote form is also used for API documentation\n", + "strings.\n", + "\n", + "Unlike `IntLiteral` and `FloatLiteral`, `StringLiteral` doesn't automatically\n", + "materialize to a runtime type. In some cases, you may need to manually convert\n", + "`StringLiteral` values to `String` using the built-in \n", + "[`str()`](/mojo/stdlib/builtin/str/str) method. \n", + "\n", + "For example, when concatenating strings with other values" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "var a = 5\n", + "var b: String = \"String literals may not concatenate \"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "## Booleans\n", + "\n", + "Mojo's `Bool` type represents a boolean value. It can take one of two values, \n", + "`True` or `False`. You can negate a boolean value using the `not` operator." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False True\n" + ] + } + ], + "source": [ + "var conditionA = False\n", + "var conditionB: Bool\n", + "conditionB = not conditionA\n", + "print(conditionA, conditionB)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many types have a boolean representation. Any type that implements the \n", + "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait has a boolean \n", + "representation. As a general principle, collections evaluate as True if they \n", + "contain any elements, False if the are empty; strings evaluate as True if they\n", + "have a non-zero length." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collection types\n", + "\n", + "The Mojo standard library also includes a set of basic collection types that\n", + "can be used to build more complex data structures:\n", + "\n", + "- [`List`](/mojo/stdlib/collections/list/List), a dynamically-sized array of \n", + " items.\n", + "- [`Dict`](/mojo/stdlib/collections/dict/Dict), an associative array of \n", + " key-value pairs.\n", + "- [`Set`](/mojo/stdlib/collections/set/Set), an unordered collection of unique\n", + " items.\n", + "- [`Optional`](/mojo/stdlib/collections/optional/Optional)\n", + " represents a value that may or may not be present. \n", + "\n", + "The collection types are _generic types_: while a given collection can only\n", + "hold a specific type of value (such as `Int` or `Float64`), you specify the\n", + "type at compile time using a [parameter]((/mojo/manual/parameters/)). For\n", + "example, you can create a `List` of `Int` values like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "var l = List[Int](1, 2, 3, 4)\n", + "# l.append(3.14) # error: FloatLiteral cannot be converted to Int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You don't always need to specify the type explicitly. If Mojo can _infer_ the\n", + "type, you can omit it. For example, when you construct a list from a set of \n", + "integer literals, Mojo creates a `List[Int]`." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# Inferred type == Int\n", + "var l1 = List(1, 2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Where you need a more flexible collection, the \n", + "[`Variant`](/mojo/stdlib/utils/variant/Variant) type can hold different types \n", + "of values. For example, a `Variant[Int32, Float64]` can hold either an `Int32`\n", + "_or_ a `Float64` value at any given time. (Using `Variant` is not covered in\n", + "this section, see the [API docs](/mojo/stdlib/utils/variant/Variant) for more\n", + "information.)\n", + "\n", + "The following sections give brief introduction to the main collection types. \n", + "\n", + "### List\n", + "\n", + "[`List`](/mojo/stdlib/collections/list/List) is a dynamically-sized array of \n", + "elements. List elements need to conform to the \n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait, which\n", + "just means that the items must be copyable and movable. Most of the common\n", + "standard library primitives, like `Int`, `String`, and `SIMD` conform to this\n", + "trait. You can create a `List` by passing the element type as a parameter, like\n", + "this:\n", + "\n", + "\n", + "```mojo\n", + "var l = List[String]()\n", + "```\n", + "\n", + "The `List` type supports a subset of the Python `list` API, including the\n", + "ability to append to the list, pop items out of the list, and access list items\n", + "using subscript notation." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Popping last item from list: 11\n", + "2, 3, 5, 7, " + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "var list = List(2, 3, 5)\n", + "list.append(7)\n", + "list.append(11)\n", + "print(\"Popping last item from list: \", list.pop())\n", + "for idx in range(len(list)):\n", + " print(list[idx], end=\", \")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the previous code sample leaves out the type parameter when creating \n", + "the list. Because the list is being created with a set of `Int` values, Mojo can\n", + "_infer_ the type from the arguments. \n", + "\n", + "There are some notable limitations when using `List`:\n", + "\n", + "- You can't currently initialize a list from a list literal, like this:\n", + "\n", + " ```mojo\n", + " # Doesn't work!\n", + " var list: List[Int] = [2, 3, 5]\n", + "\n", + " But you can use variadic arguments to achieve the same thing:\n", + "\n", + " ```mojo\n", + " var list = List(2, 3, 5)\n", + " ```\n", + "\n", + "- You can't `print()` a list, or convert it directly into a string.\n", + "\n", + " ```mojo\n", + " # Does not work\n", + " print(list)\n", + " ```\n", + "\n", + " As shown above, you can print the individual elements in a list as long as\n", + " they're a [`Stringable`](/mojo/stdlib/builtin/str/Stringable) type.\n", + "\n", + "- Iterating a `List` currently returns a \n", + " [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", + " item itself. You can access the item using the dereference operator, `[]`:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2, 3, 4, " + ] + } + ], + "source": [ + "#: from collections import List\n", + "var list = List(2, 3, 4)\n", + "for item in list:\n", + " print(item[], end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " Subscripting in to a list, however, returns the item directly—no need to \n", + " dereference:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2, 3, 4, " + ] + } + ], + "source": [ + "#: from collections import List\n", + "#: var list = List[Int](2, 3, 4)\n", + "for i in range(len(list)):\n", + " print(list[i], end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dict\n", + "\n", + "The [`Dict`](/mojo/stdlib/collections/dict/Dict) type is an associative array\n", + "that holds key-value pairs. You can create a `Dict` by specifying the key type\n", + "and value type as parameters, like this:\n", + "\n", + "```mojo\n", + "var values = Dict[String, Float64]()\n", + "```\n", + "\n", + "They dictionary's key type must conform to the \n", + "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, and value \n", + "elements must conform to the \n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", + "\n", + "You can insert and remove key-value pairs, update the value assigned to a key,\n", + "and iterate through keys, values, or items in the dictionary. \n", + "\n", + "The `Dict` iterators all yield references, so you need to use the dereference\n", + "operator `[]` as shown in the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "plasticity 3.1000000000000001\n", + "elasticity 1.3\n", + "electricity 9.6999999999999993\n" + ] + } + ], + "source": [ + "from collections import Dict\n", + "\n", + "var d = Dict[String, Float64]()\n", + "d[\"plasticity\"] = 3.1\n", + "d[\"elasticity\"] = 1.3\n", + "d[\"electricity\"] = 9.7\n", + "for item in d.items():\n", + " print(item[].key, item[].value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Set\n", + "\n", + "The [`Set`](/mojo/stdlib/collections/set/Set) type represent a set of unique\n", + "values. You can add and remove elements from the set, test whether a value \n", + "exists in the set, and perform set algebra operations, like unions and \n", + "intersections between two sets. \n", + "\n", + "Sets are generic and the element type must conform to the\n", + "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We both like:\n", + "- ice cream\n", + "- tacos\n" + ] + } + ], + "source": [ + "from collections import Set\n", + "\n", + "i_like = Set(\"sushi\", \"ice cream\", \"tacos\", \"pho\")\n", + "you_like = Set(\"burgers\", \"tacos\", \"salad\", \"ice cream\")\n", + "we_like = i_like.intersection(you_like)\n", + "\n", + "print(\"We both like:\")\n", + "for item in we_like:\n", + " print(\"-\", item[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Optional\n", + "\n", + "The [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", + "value that may or may not be present. Like the other collectypetion types, it is\n", + "generic, and can hold any type that conforms to the\n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "# Two ways to initialize an Optional with a value\n", + "var opt1 = Optional(5)\n", + "var opt2: Optional[Int] = 5\n", + "# Two ways to initalize an Optional with no value\n", + "var opt3 = Optional[Int]()\n", + "var opt4: Optional[Int] = None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "An `Optional` evaluates as `True` when it holds a value, `False` otherwise. If\n", + "the `Optional` holds a value, you can retrieve a reference to the value using \n", + "the `value()` method. But calling `value()` on an `Optional` with no value\n", + "results in undefined behavior, so you should always guard a call to `value()`\n", + "inside a conditional that checks whether a value exists." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Testing\n" + ] + } + ], + "source": [ + "var opt: Optional[String] = str(\"Testing\")\n", + "if opt:\n", + " var value_ref = opt.value()\n", + " print(value_ref[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Alternately, you can use the `or_else()` method, which returns the stored\n", + "value if there is one, or a user-specified default value otherwise:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello\n", + "Hi\n" + ] + } + ], + "source": [ + "var custom_greeting: Optional[String] = None\n", + "print(custom_greeting.or_else(\"Hello\"))\n", + "\n", + "custom_greeting = str(\"Hi\")\n", + "print(custom_greeting.or_else(\"Hello\"))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Register-passable, memory-only, and trivial types\n", + "\n", + "In various places in the documentation you'll see references to \n", + "register-passable, memory-only, and trivial types. Register-passable and \n", + "memory-only types are distinguished based on how they hold data:\n", + "\n", + "- Register-passable types are composed exclusively of fixed-size data types,\n", + " which can (theoretically) be stored in a machine register. A register-passable\n", + " type can include other types, as long as they are also register-passable.\n", + " `Int`, `Bool`, and `SIMD`, for example, are all register-passable types. So \n", + " a register-passable `struct` could include `Int` and `Bool` fields, but not a\n", + " `String` field. Register-passable types are declared with the \n", + " [`@register_passable`](/mojo/manual/decorators/register-passable) decorator.\n", + "\n", + " Register-passable types are always passed by value (that is, the values are\n", + " copied).\n", + "\n", + "- Memory-only types consist of any types that _don't_ fit the description of\n", + " register-passable types. In particular, these types usually use pointers or\n", + " references to manage heap-allocated memory. `String`, `List`, and `Dict` are\n", + " all examples of memory-only types.\n", + "\n", + "Our long-term goal is to make this distinction transparent to the user, and\n", + "ensure all APIs work with both register-passable and memory-only types.\n", + "But right now you will see some standard library types that only work with \n", + "register-passable types or only work with memory-only types.\n", + "\n", + "In addition to these two categories, Mojo also has \"trivial\" types. Conceptually\n", + "a trivial type is simply a type that doesn't require any custom logic in its\n", + "lifecycle methods. The bits that make up an instance of a trivial type can be\n", + "copied or moved without any knowledge of what they do. Currently, trivial types\n", + "are declared using the\n", + "[`@register_passable(trivial)`](/mojo/manual/decorators/register-passable#register_passabletrivial)\n", + "decorator. Trivial types shouldn't be limited to only register-passable types,\n", + "so in the future we intend to separate trivial types from the \n", + "`@register_passable` decorator." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `AnyType` and `AnyRegType`\n", + "\n", + "Two other things you'll see in Mojo APIs are references to `AnyType` and\n", + "`AnyRegType`. These are effectively _metatypes_, that is, types of types.\n", + "\n", + "- `AnyType` represents any Mojo type. Mojo treats `AnyType` as a special kind of\n", + " trait, and you'll find more discussion of it on the\n", + " [Traits page](/mojo/manual/traits#the-anytype-trait).\n", + "- `AnyRegType` is a metatype representing any Mojo type that's marked \n", + " register passable.\n", + "\n", + "You'll see them in signatures like this:\n", + "\n", + "```mojo\n", + "fn any_type_function[ValueType: AnyRegType](value: ValueType):\n", + " ...\n", + "```\n", + "\n", + "You can read this as `any_type_function` has an argument, `value` of type\n", + "`ValueType`,where `ValueType` is a register-passable type, determined at\n", + "compile time. \n", + "\n", + "There is still some code like this in the standard library, but it's gradually\n", + "being migrated to more generic code that doesn't distinguish between \n", + "register-passable and memory-only types.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From a7938a14dd94505cf8c73e777c9812085b91c91f Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 1 May 2024 15:00:08 -0700 Subject: [PATCH 0312/2019] [Docs] Add docs on heterogenous variadic arguments. (#38614) (cherry-pick from main) (#38841) MODULAR_ORIG_COMMIT_REV_ID: 0aa35ca858bf14a5df881c1aa3a9a5e1f16c274c --- docs/manual/functions.ipynb | 262 ++++++++++++++++++++++++----- docs/manual/parameters/index.ipynb | 9 +- 2 files changed, 226 insertions(+), 45 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 77d965493d..52922ab672 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -75,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -221,7 +221,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -239,7 +239,11 @@ "metadata": {}, "source": [ "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout)." + "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout).\n", + "\n", + "Any optional arguments must appear after any required arguments. [Keyword-only\n", + "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", + "also be either required or optional." ] }, { @@ -255,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -281,14 +285,14 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fn sum(*values: Int) -> Int:\n", " var sum: Int = 0\n", " for value in values:\n", - " sum = sum+value\n", + " sum = sum + value\n", " return sum" ] }, @@ -305,22 +309,75 @@ "specified by keyword (see \n", "[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)).\n", "\n", - "Currently variadic arguments must be a single type—all `Int`, or all `String`,\n", - "for example. A few standard library APIs, such as\n", - "[`print()`](/mojo/stdlib/builtin/io/print), support mixed-type, or\n", - "heterogeneous, variadic arguments, but this currently requires working with\n", - "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", - "Mojo in the future.\n", + "Variadic arguments can be divided into two categories:\n", + "\n", + "- Homogeneous variadic arguments, where all of the passed arguments are the same\n", + " type—all `Int`, or all `String`, for example. \n", + "- Heterogeneous variadic arguments, which can accept a set of different argument\n", + " types.\n", + "\n", + "The following sections describe how to work with homogeneous and heterogenous\n", + "variadic arguments.\n", + "\n", + ":::note Variadic parameters\n", + "\n", + "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", + "(parameters are used for compile-time metaprogramming). Variadic parameters\n", + "are supported, but with some limitations—for details see \n", + "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", + "\n", + ":::\n", "\n", - "Inside the function body, the variadic argument is available an iterable list\n", - "for ease of use. But there are some differences in handling the list depending\n", - "on whether the arguments are register-passable types (such as `Int`) or\n", - "memory-only types (such as `String`).\n", + "\n", + "### Homogeneous variadic arguments\n", + "\n", + "When defining a homogeneous variadic argument, use \n", + "*argument_name: argument_type:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def greet(*names: String):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Inside the function body, the variadic argument is available as an iterable list\n", + "for ease of use. Current there are some differences in handling the list \n", + "depending on whether the arguments are register-passable types (such as `Int`)\n", + "or memory-only types (such as `String`). TODO: We hope to remove these\n", + "differences in the future.\n", "\n", "Register-passable types, such as `Int`, are available as a \n", "[`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", - "loop.\n", + "loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn sum(*values: Int) -> Int:\n", + " var sum: Int = 0\n", + " for value in values:\n", + " sum = sum+value\n", + " return sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem).\n", @@ -332,11 +389,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "fn make_worldly(inout *strs: String):\n", + "def make_worldly(inout *strs: String):\n", " # Requires extra [] to dereference the reference for now.\n", " for i in strs:\n", " i[] += \" world\"\n" @@ -362,15 +419,129 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ":::note Variadic parameters\n", + "### Heterogeneous variadic arguments\n", "\n", - "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", - "(parameters are used for compile-time metaprogramming). However, most rules\n", - "that apply to argument lists also apply to parameter lists. Variadic parameters\n", - "are supported, but with some limitations—for details see \n", - "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", + "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", + "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", + "types requires [traits](/mojo/manual/traits) and \n", + "[parameters](/mojo/manual/parameters/). So the syntax may look a little\n", + "unfamiliar if you haven't worked with those features. The signature for a\n", + "function with a heterogeneous variadic argument looks like this:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```mojo\n", + "def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):\n", + " ...\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an\n", + "`ArgTypes` parameter, which is a list of types, all of which conform to the \n", + "[`Intable`](/mojo/stdlib/builtin/int/Intable) trait. The argument list, \n", + "`(*args: *ArgTypes)` has the familiar `*args` for the variadic argument, but \n", + "instead of a single type, its type is defined as _list_ of types, `*ArgTypes`.\n", + "\n", + "This means that each argument in `args` has a corresponding type in `ArgTypes`, \n", + "so args[n] is of type \n", + "ArgTypes[n].\n", + "\n", + "Inside the function, `args` is available as a\n", + "[`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack). The easiest\n", + "way to work with the arguments is to use the `each()` method to iterate through\n", + "the `VariadicPack`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "28\n" + ] + } + ], + "source": [ + "fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:\n", + " var total = 0\n", "\n", - ":::\n" + " @parameter\n", + " fn add[Type: Intable](value: Type):\n", + " total += int(value)\n", + "\n", + " args.each[add]()\n", + " return total\n", + "\n", + "print(count_many_things(5, 11.7, 12))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the example above, the `add()` function is called for each argument in turn,\n", + "with the appropriate `value` and `Type` values. For instance, `add()` is first\n", + "called with `value=5` and `Type=Int`, then with `value=11.7` and `Type=Float64`.\n", + "\n", + "Also, note that when calling `count_many_things()`, you don't actually pass in\n", + "a list of argument types. You only need to pass in the arguments, and Mojo\n", + "generates the `ArgTypes` list itself.\n", + "\n", + "As a small optimization, if your function is likely to be called with a single\n", + "argument frequently, you can define your function with a single argument\n", + "followed by a variadic argument. This lets the simple case bypass populating and\n", + "iterating through the `VariadicPack`.\n", + "\n", + "For example, given a `print_string()` function that prints a single string, you\n", + "could re-implement the variadic `print()` function with code like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bob0\n" + ] + } + ], + "source": [ + " fn print_string(s: String):\n", + " print(s, end=\"\")\n", + "\n", + " fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):\n", + " print_string(str(first))\n", + "\n", + " @parameter\n", + " fn print_elt[T: Stringable](a: T):\n", + " print_string(\" \")\n", + " print_string(a)\n", + " rest.each[print_elt]()\n", + " print_many(\"Bob\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you call `print_many()` with a single argument, it calls `print_string()`\n", + "directly. The `VariadicPack` is empty, so `each()` returns immediately without\n", + "calling the `print_elt()` function." ] }, { @@ -397,8 +568,10 @@ "\n", " In this example, the argument name `kwargs` is a placeholder that accepts any\n", " number of keyword arguments. Inside the body of the function, you can access\n", - " the arguments as a [`Dict`](/mojo/stdlib/collections/dict) of keywords and\n", - " argument values.\n", + " the arguments as a dictionary of keywords and argument values (specifically,\n", + " an instance of\n", + " [`OwnedKwargsDict`](/mojo/stdlib/collections/dict/OwnedKwargsDict)).\n", + " \n", " \n", " There are currently a few limitations:\n", "\n", @@ -415,18 +588,12 @@ " - All the variadic keyword arguments must have the same type, and this\n", " determines the type of the argument dictionary. For example, if the argument\n", " is `**kwargs: Float64` then the argument dictionary will be a \n", - " `Dict[String, Float64]`.\n", - "\n", - " - Functions with variadic keyword arguments can't have default values for\n", - " keyword-only arguments. For example:\n", + " `OwnedKwargsDict[Float64]`.\n", "\n", - " ```mojo\n", - " # Not allowed yet, because `b` is keyword-only with a default.\n", - " fn not_yet(*, b: Int = 9, **kwargs: Int): ...\n", - "\n", - " # Okay, because `c` is positional-or-keyword, so it can have a default.\n", - " fn still_works(c: Int = 5, **kwargs: Int): ...\n", - " ```\n", + " - The argument type must conform to the \n", + " [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", + " That is, the type must be both [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", + " and [`Copyable`](/mojo/stdlib/builtin/value/Copyable).\n", "\n", " - Dictionary unpacking is not supported yet:\n", "\n", @@ -529,7 +696,18 @@ "Keyword-only arguments often have default values, but this is not required. If a\n", "keyword-only argument doesn't have a default value, it is a _required \n", "keyword-only argument_. It must be specified, and it must be specified by \n", - "keyword.\n", + "keyword. \n", + "\n", + "Any required keyword-only arguments must appear in the signature before\n", + "any optional keyword-only arguments. That is, arguments appear in the following\n", + "sequence a function signature:\n", + "\n", + "* Required positional arguments.\n", + "* Optional positional arguments.\n", + "* Variadic arguments.\n", + "* Required keyword-only arguments.\n", + "* Optional keyword-only arguments.\n", + "* Variadic keyword arguments.\n", "\n", "For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only\n", "Arguments](https://peps.python.org/pep-3102/)." diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 2005e81c02..463005cad7 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -654,11 +654,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Variadic parameters have some limitations that variadic arguments don't have:\n", + "Variadic parameters currently have some limitations that variadic arguments don't have:\n", "\n", - "- Only variadic parameters of register-passable types are supported currently.\n", + "- Variadic parameters must be homogeneous—that is, all the values must be the\n", + " same type. \n", + " \n", + "- The parameter type must be register-passable.\n", "\n", - "- The parameters aren't automatically projected into a `VariadicList`, so you\n", + "- The parameter values aren't automatically projected into a `VariadicList`, so you\n", " need to construct the list explicitly:" ] }, From 9e65814edaaad423d8c19d6d965cc7ac6e6bd04e Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 1 May 2024 15:01:44 -0700 Subject: [PATCH 0313/2019] [Docs] Update text to match updated example. (#38701) (cherry-pick from main) (#38844) Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 1d832aabfdb84167f0def998ac386463b4ee266e --- docs/manual/lifecycle/death.ipynb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 01885c9128..c2d67ed01f 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -198,8 +198,8 @@ "metadata": {}, "source": [ "There's no need to define the `__del__()` destructor for this, because it's a\n", - "simple collection of other types (`String` and `Int`), and it doesn't dynamically\n", - "allocate memory. \n", + "simple collection of other types (`String` and `Int`), and it doesn't \n", + "dynamically allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" @@ -278,8 +278,11 @@ "\n", ":::\n", "\n", - "If `self.data` was an instance of `AnyPointer`, you'd need to use slightly\n", - "different code:\n", + "For instances of the \n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type, use\n", + "the [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", + "function instead of the discard pattern. For example, if the previous example\n", + "used `UnsafePointer`, the `__del__()` method would look like this:\n", "\n", "```mojo\n", "fn __del__(owned self):\n", From 60415bff73e97149b72b6dd2be588a8589173782 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 1 May 2024 17:08:35 -0700 Subject: [PATCH 0314/2019] [docs] misc cherrypicks (#39083) Co-authored-by: Ehsan M. Kermani <6980212+ehsanmok@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 838ccd91a087d5c216f296a65f686299e5948fa6 --- docs/faq.md | 2 +- docs/manual/get-started/index.md | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 3054b4786b..c515ca8c7a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -275,7 +275,7 @@ about our kernel performance in our [matrix multiplication blog post](https://www.modular.com/blog/the-worlds-fastest-unified-matrix-multiplication). For details about our end-to-end model performance relative to the latest releases of TensorFlow and PyTorch, check out our [performance -dashboard](https://performance.modular.com). +dashboard](https://www.modular.com/max/performance). ## Mojo SDK diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 0f226917f7..572c8b8e19 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -84,19 +84,24 @@ If you already have the `modular` tool, curl -s https://get.modular.com | sh - ``` -2. Then sign in to your Modular account with this command: +2. Then install the Mojo SDK: ```sh - modular auth + modular install mojo ``` -3. Now you can install the Mojo SDK: + :::note Get nightlies + + If you want the bleeding-edge (less stable) version, instead install the + nightly build: ```sh - modular install mojo + modular install nightly/mojo ``` -4. Set environment variables so you can access the + ::: + +3. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: From cc7a74306f1a4c5d5997ad8d952247abae6f8b32 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 1 May 2024 21:50:50 -0700 Subject: [PATCH 0315/2019] [Docs] Update Mojo changelog for 24.3 (cherry-pick from main) (#39109) MODULAR_ORIG_COMMIT_REV_ID: c414bcbd1731aaf80a339928c37af24394f8a564 --- docs/changelog-released.md | 614 ++++++++++++++++++++++++++++++++++++- docs/changelog.md | 462 ++-------------------------- 2 files changed, 635 insertions(+), 441 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index f423cad6d3..e115899cea 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -30,6 +30,598 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## v24.3 (2024-05-02) + +### ✨ Highlights + +- `AnyPointer` was renamed to + [`UnsafePointer`](mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and is now + Mojo's preferred unsafe pointer type. It has several enhancements, including: + + - The element type can now be any type: it doesn't require `Movable`. + + - Because of this, the `take_value()`, `emplace_value()`, and `move_into()` + methods have been changed to top-level functions and renamed. The new + functions are: + + - [`initialize_pointee_copy`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy) + - [`initialize_pointee_move`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) + - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) + - [`move_pointee`](/mojo/stdlib/memory/unsafe_pointer/move_pointee) + + - A new + [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) + function runs the destructor on the pointee. + + - `UnsafePointer` can be initialized directly from a + [`Reference`](/mojo/stdlib/memory/reference/Reference) with + `UnsafePointer(someRef)` and can convert to a reference with + `yourPointer[]`. Both infer element type and address space. Note that when + you convert a pointer to a reference, there's no way for Mojo to track the + lifetime of the original value. So the resulting reference is no safer than + the original pointer. + +- All of the pointer types received some cleanup to make them more consistent, + for example the `unsafe.bitcast()` global function is now a consistent + [`bitcast()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#bitcast) method + on the pointers, which can convert element type and address space. + +- Improvements to variadic arguments support. + + - Heterogenous variadic pack arguments now work reliably even with memory types, + and have a more convenient API to use, as defined by the + [`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack) type. For + example, a simplified version of `print` can be implemented like this: + + ```mojo + fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): + print_string(str(first)) + + @parameter + fn print_elt[T: Stringable](a: T): + print_string(" ") + print_string(a) + rest.each[print_elt]() + ``` + + - Mojo now supports declaring functions that have both optional and variadic + arguments, both positional and keyword-only. For example, this now works: + + ```mojo + fn variadic_arg_after_default( + a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int + ): ... + ``` + + Positional variadic parameters also work in the presence of optional + parameters. That is: + + ```mojo + fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): + pass + ``` + + Note that variadic keyword parameters are not supported yet. + + For more information, see + [Variadic arguments](/mojo/manual/functions#variadic-arguments) in the Mojo + Manual. + +- The `mojo build` and `mojo run` commands now support a `-g` option. This + shorter alias is equivalent to writing `--debug-level full`. This option is + also available in the `mojo debug` command, but is already the default. + +- Many new standard library APIs have been filled in, including many community + contributions. Changes are listed in the standard library section. + +- The Mojo Manual has a new page on [Types](/mojo/manual/types). + +### Language changes + +- Certain dunder methods that take indices + (`__getitem__()`, `__setitem__()`, and `__refitem__()`) or names + (`__getattr__()` and `__setattr__()`) can now take the index or name + as a parameter value instead of an argument value. This is enabled when you + define one of these methods with no argument other than `self` (for a getter) + or `self` and the set value (for a setter). + + This enables types that can only be subscripted into with parameters, as well + as things like the following example, which passes the attribute name as a + parameter so that attribute names can be checked at compile time. + + ```mojo + struct RGB: + fn __getattr__[name: StringLiteral](self) -> Int: + @parameter + if name == "r": return ... + elif name == "g": return ... + else: + constrained[name == "b", "can only access with r, g, or b members"]() + return ... + + var rgb = RGB() + print(rgb.b) # Works + print(rgb.q) # Compile error + ``` + +- Mojo now allows users to capture the source location of code and call location + of functions dynamically using the built-in `__source_location()` and + `__call_location()` functions. For example: + + ```mojo + @always_inline + fn my_assert(cond: Bool, msg: String): + if not cond: + var call_loc = __call_location() + print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) + + fn main(): + my_assert(False, "always fails") # some_file.mojo, line 193 + ``` + + This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that + `__call_location()` only works in `@always_inline` or + `@always_inline("nodebug")` functions. It gives incorrect results if placed in + an `@always_inline` function that's called *from* an + `@always_inline("nodebug")` function. + + Neither `__source_location()` nor `__call_location()` work when called in a + parameter context. For example: + + ```mojo + @always_inline + fn mystery_location() -> String: + var loc = __call_location() + return str(loc.file_name) + + def main(): + alias doesnt_work = mystery_location() # + ``` + +### Standard library changes + +#### ⭐️ New + +- [`List`](/mojo/stdlib/collections/list/List) has several new methods: + + - `pop(index)` for removing an element at a particular index. + By default, `List.pop()` removes the last element in the list. + ([@LJ-9801](https://github.com/LJ-9801), fixes + [#2017](https://github.com/modularml/mojo/issues/2017)) + + - `resize(new_size)` for resizing the list without the need to + specify an additional value. + ([@mikowals](https://github.com/mikowals), fixes + [#2133](https://github.com/modularml/mojo/issues/2133)) + + - `insert(index, value)` for inserting a value at a specified index + into the `List`. ([@whym1here](https://github.com/whym1here), fixes + [#2134](https://github.com/modularml/mojo/issues/2134)) + + - A new constructor `List(ptr, size, capacity)` to to avoid needing to + do a deep copy of an existing contiguous memory allocation when constructing + a new `List`. ([@StandinKP](https://github.com/StandinKP), fixes + [#2170](https://github.com/modularml/mojo/issues/2170)) + +- [`Dict`](/mojo/stdlib/collections/dict/Dict) now has a `update()` method to + update keys/values from another `Dict`. + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- [`Set`](/mojo/stdlib/collections/set/Set) now has named methods for set + operations: + - `difference()` mapping to `-` + - `difference_update()` mapping to `-=` + - `intersection_update()` mapping to `&=` + - `update()` mapping to `|=` + + ([@arvindavoudi](https://github.com/arvindavoudi)) + +- `Dict`, `List`, and `Set` all conform to the `Boolable` trait. The collections + evaluate to `True` if they contain any elements, `False` otherwise: + + ```mojo + def list_names(names: List[String]): + if names: + for name in names: + print(name[]) + else: + print("No names to list.") + ``` + + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) function for + creating reversed iterators. Several range types, `List`, and `Dict` now + support iterating in reverse. + + ```mojo + var numbers = List(1, 2, 3, 4, 5) + for number in reversed(numbers): + print(number) + ``` + + ([@helehex](https://github.com/helehex) and + [@jayzhan211](https://github.com/jayzhan211), contributes towards + [#2325](https://github.com/modularml/mojo/issues/2325)) + +- [`Optional`](/mojo/stdlib/collections/optional/Optional) now implements + `__is__` and `__isnot__` methods so that you can compare an `Optional` with + `None`. For example: + + ```mojo + var opt = Optional(1) + if opt is not None: + print(opt.value()[]) + ``` + + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now works with memory-only element + types like `String` and allows you to directly index into it with a parameter + expression. This means you can now simply use `x = tup[1]` like Python + instead of `x = tup.get[1, Int]()`. You can also assign into tuple elements + now as well with `tup[1] = x`. + + ```mojo + var tuple = ("Green", 9.3) + var name = tuple[0] + var value = tuple[1] + ``` + + Note that because the subscript must be a parameter expression, you can't + iterate through a `Tuple` using an ordinary `for` loop. + +- The [`Reference`](/mojo/stdlib/memory/reference/Reference) type has several + changes, including: + + - It has moved to the `memory.reference` module instead of `memory.unsafe`. + - `Reference` now has an + [`unsafe_bitcast()`](/mojo/stdlib/memory/reference/Reference#unsafe_bitcast) + method, similar to the pointer types. + + - Several unsafe methods were removed, including `offset()`, + `destroy_element_unsafe()` and `emplace_ref_unsafe()`. This is because + `Reference` is a safe type—use `UnsafePointer` to do unsafe operations. + +- [`Bool`](/mojo/stdlib/builtin/bool/Bool) can now be implicitly converted from + any type conforming to the [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) + trait. This means that you no longer need to write code like this: + + ```mojo + @value + struct MyBoolable: + fn __bool__(self) -> Bool: ... + + fn takes_boolable[T: Boolable](cond: T): ... + + takes_boolable(MyBoolable()) + ``` + + Instead, you can simply write: + + ```mojo + fn takes_bool(cond: Bool): ... + + takes_bool(MyBoolable()) + ``` + + Note that calls to `takes_bool()` will perform the implicit conversion, so in + some cases is it still better to explicitly declare a type parameter, e.g.: + + ```mojo + fn takes_two_boolables[T: Boolable](a: T, b: T): + # Short circuit means `b.__bool__()` might not be evaluated. + if a.__bool__() and b.__bool__(): + ... + ``` + +- [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now conforms to the + [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, meaning that + it can be used as key type for [`Dict`](/mojo/stdlib/collections/dict/Dict). + This allows you to easily build and interact with Python dictionaries in Mojo: + + ```mojo + def main(): + d = PythonObject(Dict[PythonObject, PythonObject]()) + d["foo"] = 12 + d[7] = "bar" + d["foo"] = [1, 2, "something else"] + print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` + ``` + +- [`FileHandle.seek()`](/mojo/stdlib/builtin/file/FileHandle#seek) now has a + `whence` argument that defaults to `os.SEEK_SET` to seek from the beginning of + the file. You can now set to `os.SEEK_CUR` to offset by the current + `FileHandle` seek position: + + ```mojo + var f = open("/tmp/example.txt") + # Skip 32 bytes + f.seek(os.SEEK_CUR, 32) + ``` + + Or `os.SEEK_END` to offset from the end of file: + + ```mojo + # Start from 32 bytes before the end of the file + f.seek(os.SEEK_END, -32) + ``` + +- [`FileHandle.read()`](/mojo/stdlib/builtin/file/FileHandle#read) can now + read straight into a + [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): + + ```mojo + var file = open("/tmp/example.txt", "r") + + # Allocate and load 8 elements + var ptr = DTypePointer[DType.float32].alloc(8) + var bytes = file.read(ptr, 8) + print("bytes read", bytes) + print(ptr.load[width=8]()) + ``` + +- The `sys` module now contains an `exit()` function that would exit a Mojo + program with the specified error code. + + ```mojo + from sys import exit + + exit(0) + ``` + +- The constructors for [`Tensor`](/mojo/stdlib/tensor/tensor/Tensor) have been + changed to be more consistent. As a result, constructors take the shape as the + first argument (instead of the second) when constructing a tensor with pointer + data. + + If you pass a single scalar value to the `Tensor` constructor, it now + broadcasts the value to all elements in the tensor. For example, + `Tensor[DType.float32](TensorShape(2,2), 0)` constructs a `2x2` tensor + initialized with all zeros. This provides an easy way to fill in the data of a + tensor. + +- [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and + `removesuffix()` methods. + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- The [`ord`](/mojo/stdlib/builtin/string/ord) and + [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to + accept any Unicode character. + ([@mzaks](https://github.com/mzaks), contributes towards + [#1616](https://github.com/modularml/mojo/issues/1616)) + +- [`Atomic`](/mojo/stdlib/os/atomic/Atomic) is now movable. + ([@StandinKP](https://github.com/StandinKP), fixes + [#2026](https://github.com/modularml/mojo/issues/2026)) + +- [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The + `atol()`function is used internally by `String.__int__()`, so + `int(String( " 10 "))` now returns `10` instead of raising an error. + ([@artemiogr97](https://github.com/artemiogr97)) + +- [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) now implements the `__rmod__()` + method. ([@bgreni](https://github.com/bgreni), fixes + [#1482](https://github.com/modularml/mojo/issues/1482)) + +- [`bool(None)`](/mojo/stdlib/builtin/bool/bool-function) is now implemented. + ([@zhoujingya](https://github.com/zhoujingya)) + +- The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) type now + implements `gather()` for gathering a `SIMD` vector from offsets of a current + pointer. Similarly, support for `scatter()` was added to scatter a `SIMD` + vector into offsets of the current pointer. + ([@leandrolcampos](https://github.com/leandrolcampos)) + +- The [`len()`](/mojo/stdlib/builtin/len/len) function now handles a + [`range()`](/mojo/stdlib/builtin/range/range) specified with a negative end + value, so that things like `len(range(-1))` work correctly. + ([@soraros](https://github.com/soraros)) + +- [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now prints + its location (filename, line, and column where it was called) in its error + message. Similarly, the `assert` helpers in the + [`testing`](/mojo/stdlib/testing/testing/) module now include location + information in their messages. + +- The [`testing.assert_equal[SIMD]()`](/mojo/stdlib/testing/testing/assert_equal) + function now raises if any of the elements mismatch in the two `SIMD` + arguments being compared. + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- The [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) + and [`math.isclose()`](/mojo/stdlib/math/math/isclose) functions now have an + `equal_nan` flag. When set to `True`, then NaNs are considered equal. + +- The [`object`](/mojo/stdlib/builtin/object/object) type now supports the + division, modulo, and left and right shift operators, including the in-place + and reverse variants. + ([@LJ-9801](https://github.com/LJ-9801), fixes + [#2224](https://github.com/modularml/mojo/issues/2224)) + +- Added checked arithmetic operations for `SIMD` integers. + + `SIMD` integer types (including the sized integer scalars like `Int64`) can + now perform checked additions, subtractions, and multiplications using the + following new methods: + + - [`add_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#add_with_overflow) + - [`sub_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#sub_with_overflow) + - [`mul_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#mul_with_overflow) + + Checked arithmetic allows the caller to determine if an operation exceeded + the numeric limits of the type. For example: + + ```mojo + var simd = SIMD[DType.int8, 4](7, 11, 13, 17) + var product: SIMD[DType.int8, 4] + var overflow: SIMD[DType.bool, 4] + (product, overflow) = simd.mul_with_overflow(simd) + for i in range(len(product)): + if overflow[i]: + print("") + else: + print(product[i]) + ``` + + ([@lsh](https://github.com/lsh)) + +- Added [`os.remove()`](/mojo/stdlib/os/os/remove) and + [`os.unlink()`](/mojo/stdlib/os/os/unlink) for deleting files. + ([@artemiogr97](https://github.com/artemiogr97), fixes + [#2306](https://github.com/modularml/mojo/issues/2306)) + +#### 🦋 Changed + +- The [`parallel_memcpy()`](/mojo/stdlib/algorithm/memory/parallel_memcpy) + function has moved from the `buffer` package to the `algorithm` package. + Please update your imports accordingly. + +- [`Optional.value()`](/mojo/stdlib/collections/optional/Optional#value) now + returns a reference instead of a copy of the contained value. + + To perform a copy manually, dereference the result: + + ```mojo + var result = Optional(123) + + var value = result.value()[] + ``` + + ([@lsh](https://github.com/lsh), fixes + [#2179](https://github.com/modularml/mojo/issues/2179)) + +- Per the accepted community proposal, + [Standardize the representation of byte sequence as a sequence of unsigned + 8-bit integers](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), + began transition to using `UInt8` by changing the data pointer of `Error` + to `DTypePointer[DType.uint8]`. + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes + towards [#2317](https://github.com/modularml/mojo/issues/2317)) + +- Continued transition to `UnsafePointer` from the legacy `Pointer` type + in various standard library APIs and internals. + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +### Tooling changes + +- The behavior of `mojo build` when invoked without an output `-o` argument has + changed slightly: `mojo build ./test-dir/program.mojo` now outputs an + executable to the path `./program`, whereas before it would output to the path + `./test-dir/program`. + +- The `mojo package` command no longer supports the `-D` flag. All compilation + environment flags should be provided at the point of package use (e.g. + `mojo run` or `mojo build`). + +- The REPL no longer allows type level variable declarations to be + uninitialized, e.g. it will reject `var s: String`. This is because it does + not do proper lifetime tracking (yet!) across cells, and so such code would + lead to a crash. You can work around this by initializing to a dummy value + and overwriting later. This limitation only applies to top level variables, + variables in functions work as they always have. + +### Other changes + +#### Low-level language changes + +- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to + the underlying memory representation as a `!lit.ref` value without checking + initialization status of the underlying value. This is useful in very + low-level logic but isn't designed for general usability and will likely + change in the future. + +- Properties can now be specified on inline MLIR ops: + + ```mojo + _ = __mlir_op.`kgen.source_loc`[ + _type = ( + __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` + ), + _properties = __mlir_attr.`{inlineCount = 1 : i64}`, + ]() + ``` + + As the example shows above, the protected `_properties` attribute can be + passed during op construction, with an MLIR `DictionaryAttr` value. + +#### ❌ Removed + +- Support for "register only" variadic packs has been removed. Instead of + `AnyRegType`, please upgrade your code to `AnyType` in examples like this: + + ```mojo + fn your_function[*Types: AnyRegType](*args: *Ts): ... + ``` + + This move gives you access to a nicer API and has the benefit of being memory + safe and correct for non-trivial types. If you need specific APIs on the + types, please use the correct trait instead of `AnyType`. + +- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults + to popping the last element in the list. + +- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. + +- The `__get_lvalue_as_address(x)` magic function has been removed. To get a + reference to a value use `Reference(x)` and if you need an unsafe pointer, you + can use `UnsafePointer.address_of(x)`. + +#### 🛠️ Fixed + +- [#516](https://github.com/modularml/mojo/issues/516) and + [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. + "Can't create a function that returns two strings." + +- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5). + +- [#1609](https://github.com/modularml/mojo/issues/1609) alias with + `DynamicVector[Tuple[Int]]` fails. + +- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` + in a Mojo package is an error, for now. This is not intended to work yet, + erroring for now will help to prevent accidental undefined behavior. + +- [#1215](https://github.com/modularml/mojo/issues/1215) and + [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no + longer cuts off hover previews for functions with functional arguments, + parameters, or results. + +- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and + documentation generation handling of inout arguments. + +- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer + crashes the Mojo parser. + +- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac + has been fixed. + +- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadic arguments + don't work with non-trivial register-only types. + +- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed + and formatted correctly by `mojo format`. + +- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to + `@value` decorator and structs with empty body. + +- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after + syntax error during tuple creation. + +- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now + properly supports signature types with named arguments and parameters. + +- [#2007](https://github.com/modularml/mojo/issues/2007) and + [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer + crashes on certain types of closures. + +- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` + decorator fails gracefully after duplicate field error. + +- [#2068](https://github.com/modularml/mojo/issues/2068) + Fix `SIMD.reduce()` for size_out == 2. + ([@soraros](https://github.com/soraros)) + ## v24.2.1 (2024-04-11) This release doesn't include any changes to Mojo. @@ -1004,7 +1596,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are _not enabled_ + before the program exits. By default, assertions are *not enabled* in the standard library right now for performance reasons. - The Mojo Language Server now implements the References request. IDEs use @@ -1114,7 +1706,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. array slices. While this is a major step forward for the lifetimes system in Mojo, it is - still _very_ early and awkward to use. Notably, there is no syntactic sugar + still *very* early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now. @@ -1437,8 +2029,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Traits have arrived! - You can now define a _trait_, which consists of a required set of method - prototypes. A struct can _conform to_ the trait by implementing these methods. + You can now define a *trait*, which consists of a required set of method + prototypes. A struct can *conform to* the trait by implementing these methods. This lets you write generic functions that work on any structs that conform to a given trait. @@ -1558,7 +2150,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### ⭐️ New - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include _everything_ about Mojo yet, but it includes a lot, + It doesn't include *everything* about Mojo yet, but it includes a lot, and more than the original [programming manual](/mojo/programming-manual.html) (now deprecated). @@ -1581,7 +2173,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` In the first signature for `eat()`, the `b` parameter isn't bound, so it's - _implicitly_ added as an input parameter on the function. + *implicitly* added as an input parameter on the function. In the second signature for `eat()`, the author has explicitly defined an input parameter (`_b`), which is bound to the second parameter on the argument @@ -1826,8 +2418,8 @@ the previous "read to EOF" behavior when size is negative. - There is an issue affecting Jupyter notebooks that use autotuning and traits. This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the _Matrix multiplication in - Mojo_ notebook. + outside of the notebooks. This issue affects the *Matrix multiplication in + Mojo* notebook. ## v0.5.0 (2023-11-2) @@ -1842,7 +2434,7 @@ the previous "read to EOF" behavior when size is negative. function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. -- Mojo now supports compile-time _keyword parameters_, in addition to existing +- Mojo now supports compile-time *keyword parameters*, in addition to existing support for [keyword arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For example: @@ -2181,7 +2773,7 @@ the previous "read to EOF" behavior when size is negative. return self^ ``` - Here Mojo _cannot_ invoke a noop `__exit__` method because the context + Here Mojo *cannot* invoke a noop `__exit__` method because the context manager is consumed by the `__enter__` method. This can be used for types (like file descriptors) that are traditionally used with `with` statements, even though Mojo's guaranteed early destruction doesn't require that. @@ -3448,7 +4040,7 @@ busy this week. is currently unsupported). These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to _not_ capture values + types, with the caveat that closures are known to *not* capture values correctly. Be very careful with interesting types in the vicinity of a closure! diff --git a/docs/changelog.md b/docs/changelog.md index 51db2004ca..e0ff7bcede 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,456 +16,58 @@ what we publish. ### 🔥 Legendary -- Tuple now works with memory-only element types like String and allows you to - directly index into it with a parameter exprssion. This means you can now - simply use `x = tup[1]` like Python instead of `x = tup.get[1, Int]()`. You - can also assign into tuple elements now as well with `tup[1] = x`. - ### ⭐️ New -- Heterogenous variadic pack arguments now work reliably even with memory types, - and have a more convenient API to use, as defined on the `VariadicPack` type. - For example, a simplified version of `print` can be implemented as: - - ```mojo - fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): - print_string(str(first)) - - @parameter - fn print_elt[T: Stringable](a: T): - print_string(" ") - print_string(a) - rest.each[print_elt]() - ``` - -- The `sys` module now contains an `exit` function that would exit a Mojo - program with the specified error code. - -- The constructors for `tensor.Tensor` have been changed to be more consistent. - As a result, one has to pass in the shape as first argument (instead of the - second) when constructing a tensor with pointer data. - -- The constructor for `tensor.Tensor` will now splat a scalar if its passed in. - For example, `Tensor[DType.float32](TensorShape(2,2), 0)` will construct a - `2x2` tensor which is initialized with all zeros. This provides an easy way - to fill the data of a tensor. - -- The `mojo build` and `mojo run` commands now support a `-g` option. This - shorter alias is equivalent to writing `--debug-level full`. This option is - also available in the `mojo debug` command, but is already the default. - -- `PythonObject` now conforms to the `KeyElement` trait, meaning that it can be - used as key type for `Dict`. This allows on to easily build and interact with - Python dictionaries in mojo: - - ```mojo - def main(): - d = PythonObject(Dict[PythonObject, PythonObject]()) - d["foo"] = 12 - d[7] = "bar" - d["foo"] = [1, 2, "something else"] - print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` - ``` - -- `List` now has several new methods: - - `pop(index)` for removing an element at a particular index. - ([PR #2041](https://github.com/modularml/mojo/pull/2041)) - (Fixes [#2017](https://github.com/modularml/mojo/issues/2017))\ - By default, `List.pop()` removes the last element in the list. - - - `resize(new_size)` for resizing the list without the need to - specify an additional value. - ([PR #2140](https://github.com/modularml/mojo/pull/2140), - Fixes [#2133](https://github.com/modularml/mojo/issues/2133)) - - - `insert(index, value)` for inserting a value at a specified index - into the `List`. - ([PR #2148](https://github.com/modularml/mojo/pull/2148), - Fixes [#2134](https://github.com/modularml/mojo/issues/2134)) - - - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep - copy of an existing contiguous memory allocation when constructing a new `List`. - ([PR #2182](https://github.com/modularml/mojo/pull/2182), - Fixes [#2170](https://github.com/modularml/mojo/issues/2170)) - -- `Dict` now has a `update()` method to update keys/values from another `Dict`. - ([PR #2085](https://github.com/modularml/mojo/pull/2085)) - -- `Set` now has named methods for set operations - ([PR #2214](https://github.com/modularml/mojo/pull/2214)): - - `Set.difference()` mapping to `-` - - `Set.difference_update()` mapping to `-=` - - `Set.intersection_update()` mapping to `&=` - - `Set.update()` mapping to `|=` - -- `String` now has `removeprefix()` and `removesuffix()` methods. - ([PR #2038](https://github.com/modularml/mojo/pull/2038)) - -- `Optional` now implements `__is__` and `__isnot__` methods so that you can compare - an `Optional` with `None`, e.g. `Optional(1) is not None` for example. - ([PR #2082](https://github.com/modularml/mojo/pull/2082)) - -- The `ord` and `chr` functions have been improved to accept any Unicode character. - ([PR #2149](https://github.com/modularml/mojo/pull/2149), - Contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) - -- `Atomic` is now movable. - ([PR #2088](https://github.com/modularml/mojo/pull/2088), - Fixes [#2026](https://github.com/modularml/mojo/issues/2026)) - -- `Dict` and `List` are both `Boolable` now. - ([PR #2262](https://github.com/modularml/mojo/pull/2262)) - -- `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` - instead of raising an error. - ([PR #2225](https://github.com/modularml/mojo/pull/2225)) - -- `SIMD` now implements `__rmod__`. - ([PR #2186](https://github.com/modularml/mojo/pull/2186), - Fixes [#1482](https://github.com/modularml/mojo/issues/1482)) - -- `bool(None)` is now implemented. - ([PR #2249](https://github.com/modularml/mojo/pull/2249)) - -- The `DTypePointer` type now implements `gather` for gathering a `SIMD` - vector from offsets of a current pointer. Similarly, support for `scatter` - was added to scatter a `SIMD` vector into offsets of the current pointer. - ([PR #2268](https://github.com/modularml/mojo/pull/2268)) - -- The `len` function for unary `range` with negative end values has been fixed - so that things like `len(range(-1))` work correctly now. - ([PR #2204](https://github.com/modularml/mojo/pull/2204)) +- `int()` can now take a string and a specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with the + base determined by whether the string contains the prefix `"0x"`, `"0o"`, or + `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2274](https://github.com/modularml/mojo/issues/2274)) -- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to - the underlying memory representation as a `!lit.ref` value without checking - initialization status of the underlying value. This is useful in very - low-level logic but isn't designed for general usability and will likely - change in the future. - -- The `testing.assert_equal[SIMD]()` now raises if any of the elements - mismatch in the two `SIMD` arguments being compared. - ([PR #2279](https://github.com/modularml/mojo/pull/2279)) - -- The `testing.assert_almost_equal` and `math.isclose` functions now have an - `equal_nan` flag. When set to True, then NaNs are considered equal. - -- Mojo now supports declaring functions that have both optional and variadic - arguments, both positional and keyword-only. E.g. this now works: +- Mojo now allows types to opt in to use the `abs()` function by implementing + the `__abs__()` method, defined by the new `Absable`: ```mojo - fn variadic_arg_after_default( - a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int - ): ... - ``` + from math import sqrt - Positional variadic parameters also work in the presence of optional - parameters, i.e.: + struct Point(Absable): + var x: Float64 + var y: Float64 - ```mojo - fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): - pass + fn __abs__(self) -> Self: + return sqrt(self.x * self.x + self.y * self.y) ``` - Note that variadic keyword parameters are not supported yet. +- The `abs()` function has also moved from `math` to `builtin`, so you no longer + need to do `from math import abs`. -- The `__getitem__`/`__getattr__` and related methods can now take indices as - parameter values instead of argument values. This is enabled when defining - these as taking no arguments other than 'self' and the set value in a setter. - This enables types that can only be subscript into with parameters, as well - as things like: +- Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in + the `math` module by implementing the `__floor__()` and `__ceil__()` methods + (and so conforming to the new `math.Floorable` and `math.Ceilable` traits, + respectively). For example: ```mojo - struct RGB: - fn __getattr__[name: StringLiteral](self) -> Int: - @parameter - if name == "r": return ... - elif name == "g": return ... - else: - constrained[name == "b", "can only access with r, g, or b members"]() - return ... - ``` - -- Added `reversed()` for creating reversed iterators. Several range types, - `List`, and `Dict` now support iterating in reverse. - ([PR #2215](https://github.com/modularml/mojo/pull/2215), - [PR #2327](https://github.com/modularml/mojo/pull/2327), - Contributes towards [#2325](https://github.com/modularml/mojo/issues/2325)) - -- `object` now supports the division, modulo, and left and right shift - operators. - ([PR #2230](https://github.com/modularml/mojo/pull/2230), - [PR #2247](https://github.com/modularml/mojo/pull/2247), - Fixes [#2224](https://github.com/modularml/mojo/issues/2224)) - - The following operator dunder methods were added: + from math import Ceilable, Floorable, ceil, floor - - `object.__mod__` - - `object.__truediv__` - - `object.__floordiv__` - - `object.__lshift__` - - `object.__rshift__` + @value + struct Complex(Ceilable, Floorable): + var re: Float64 + var im: Float64 - As well as the in-place and reverse variants. + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) -- Added checked arithmetic operations. - ([PR #2138](https://github.com/modularml/mojo/pull/2138)) - - SIMD integral types (including the sized integral scalars like `Int64`) can - now perform checked additions, substractions, and multiplications using the - following new methods: - - - `SIMD.add_with_overflow` - - `SIMD.sub_with_overflow` - - `SIMD.mul_with_overflow` - - Checked arithimetic allows the caller to determine if an operation exceeded - the numeric limits of the type. - -- Added `os.remove()` and `os.unlink()` for deleting files. - ([PR #2310](https://github.com/modularml/mojo/pull/2310), - Fixes [#2306](https://github.com/modularml/mojo/issues/2306)) - -- Properties can now be specified on inline mlir ops: - - ```mojo - _ = __mlir_op.`kgen.source_loc`[ - _type = ( - __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` - ), - _properties = __mlir_attr.`{inlineCount = 1 : i64}`, - ]() + fn __floor__(self) -> Self: + return Self(floor(re), floor(im)) ``` - As the example shows above, the protected `_properties` attribute can be - passed during op construction, with an MLIR `DictionaryAttr` value. - -- Mojo now allows users to capture source location of code and call location of - functions dynamically. For example: - - ```mojo - @always_inline - fn my_assert(cond: Bool, msg: String): - if not cond: - var call_loc = __call_location() - print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) - - fn main(): - my_assert(False, "always fails") # some_file.mojo, line 193 - ``` - - will print `In /path/to/some_file.mojo on line 193: always fails`. Note that - `__call_location` only works in `@always_inline("nodebug")` and - `@always_inline` functions, as well as limitations on its use in parameter - contexts (see the documentation for more details). - -- `debug_assert` now prints its location (filename, line, and column where it - was called) in its error message. Similarly, the `assert_*` helpers in the - `testing` module now include location information in their messages. - ### 🦋 Changed -- The behavior of `mojo build` when invoked without an output `-o` argument has - changed slightly: `mojo build ./test-dir/program.mojo` now outputs an - executable to the path `./program`, whereas before it would output to the path - `./test-dir/program`. - -- The REPL no longer allows type level variable declarations to be - uninitialized, e.g. it will reject `var s: String`. This is because it does - not do proper lifetime tracking (yet!) across cells, and so such code would - lead to a crash. You can work around this by initializing to a dummy value - and overwriting later. This limitation only applies to top level variables, - variables in functions work as they always have. - -- `AnyPointer` got renamed to `UnsafePointer` and is now Mojo's preferred unsafe - pointer type. It has several enhancements, including: - 1) The element type can now be `AnyType`: it doesn't require `Movable`. - 2) Because of this, the `take_value`, `emplace_value`, and `move_into` methods - have been changed to be top-level functions, and were renamed to - `move_from_pointee`, `initialize_pointee_*` and `move_pointee` respectively. - 3) A new `destroy_pointee` function runs the destructor on the pointee. - 4) `UnsafePointer` can be initialized directly from a `Reference` with - `UnsafePointer(someRef)` and can convert to an immortal reference with - `yourPointer[]`. Both infer element type and address space. - -- All of the pointers got a pass of cleanup to make them more consistent, for - example the `unsafe.bitcast` global function is now a consistent `bitcast` - method on the pointers, which can convert element type and address space. - -- The `Reference` type has several changes, including: - 1) It is now located in `memory.reference` instead of `memory.unsafe`. - 2) `Reference` now has an unsafe `unsafe_bitcast` method like `UnsafePointer`. - 3) Several unsafe methods were removed, including `offset`, - `destroy_element_unsafe` and `emplace_ref_unsafe`. This is because - `Reference` is a safe type - use `UnsafePointer` to do unsafe operations. - -- The `mojo package` command no longer supports the `-D` flag. All compilation - environment flags should be provided at the point of package use (e.g. - `mojo run` or `mojo build`). - -- `parallel_memcpy` function has moved from the `buffer` package to the - `algorithm` package. Please update your imports accordingly. - -- `FileHandle.seek()` now has a whence argument that defaults to `os.SEEK_SET` - to seek from the beginning of the file. You can now set to `os.SEEK_CUR` to - offset by the current `FileHandle` seek position: - - ```mojo - var f = open("/tmp/example.txt") - # Skip 32 bytes - f.seek(os.SEEK_CUR, 32) - ``` - - Or `os.SEEK_END` to offset from the end of file: - - ```mojo - # Start from 32 bytes before the end of the file - f.seek(os.SEEK_END, -32) - ``` - - - `FileHandle.read()` can now read straight into a `DTypePointer`: - - ```mojo - var file = open("/tmp/example.txt", "r") - - # Allocate and load 8 elements - var ptr = DTypePointer[DType.float32].alloc(8) - var bytes = file.read(ptr, 8) - print("bytes read", bytes) - print(ptr.load[width=8]()) - ``` - -- `Optional.value()` will now return a reference instead of a copy of the - contained value. - ([PR #2226](https://github.com/modularml/mojo/pull/2226), - Fixes [#2179](https://github.com/modularml/mojo/issues/2179)) - - To perform a copy manually, dereference the result: - - ```mojo - var result = Optional(123) - - var value = result.value()[] - ``` - -- Per the accepted community proposal - [`proposals/byte-as-uint8.md`](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), - began transition to using `UInt8` by changing the data pointer of `Error` - to `DTypePointer[DType.uint8]`. - ([PR #2318](https://github.com/modularml/mojo/pull/2318), - Contributes towards [#2317](https://github.com/modularml/mojo/issues/2317)) - -- Continued transition to `UnsafePointer` away from the legacy `Pointer` type - in various standard library APIs and internals. - ([PR #2365](https://github.com/modularml/mojo/pull/2365), - [PR #2367](https://github.com/modularml/mojo/pull/2367), - [PR #2368](https://github.com/modularml/mojo/pull/2368), - [PR #2370](https://github.com/modularml/mojo/pull/2370), - [PR #2371](https://github.com/modularml/mojo/pull/2371)) - -- `Bool` can now be implicitly converted from any type conforming to `Boolable`. - This means that you no longer need to write things like this: - - ```mojo - @value - struct MyBoolable: - fn __bool__(self) -> Bool: ... - - fn takes_boolable[T: Boolable](cond: T): ... - - takes_boolable(MyBoolable()) - ``` - - Instead, you can simply have - - ```mojo - fn takes_bool(cond: Bool): ... - - takes_bool(MyBoolable()) - ``` - - Note that calls to `takes_bool` will perform the implicit conversion, so in - some cases is it still better to explicitly declare type parameter, e.g.: - - ```mojo - fn takes_two_boolables[T: Boolable](a: T, b: T): - # Short circuit means `b.__bool__()` might not be evaluated. - if a.__bool__() and b.__bool__(): - ... - ``` - ### ❌ Removed -- Support for "register only" variadic packs has been removed. Instead of - `AnyRegType`, please upgrade your code to `AnyType` in examples like this: - - ```mojo - fn your_function[*Types: AnyRegType](*args: *Ts): ... - ``` - - This move gives you access to nicer API and has the benefit of being memory - safe and correct for non-trivial types. If you need specific APIs on the - types, please use the correct trait bound instead of `AnyType`. - -- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults - to popping the last element in the list. - -- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. - -- The `__get_lvalue_as_address(x)` magic function has been removed. To get a - reference to a value use `Reference(x)` and if you need an unsafe pointer, you - can use `UnsafePointer.address_of(x)`. +- The method `object.print()` has been removed. Since now, `object` has the + `Stringable` trait, you can use `print(my_object)` instead. ### 🛠️ Fixed - -- [#516](https://github.com/modularml/mojo/issues/516) and - [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. - "Can't create a function that returns two strings" - -- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5) - -- [#1609](https://github.com/modularml/mojo/issues/1609) alias with - `DynamicVector[Tuple[Int]]` fails. - -- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` - in a Mojo package is an error, for now. This is not intended to work yet, - erroring for now will help to prevent accidental undefined behavior. - -- [#1215](https://github.com/modularml/mojo/issues/1215) and - [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no - longer cuts off hover previews for functions with functional arguments, - parameters, or results. - -- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and - documentation generation handling of inout arguments. - -- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer - crashes the Mojo parser. - -- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac - has been fixed. - -- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadics don't - work with non-trivial register-only types. - -- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed - and formatted correctly by `mojo format`. - -- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to - `@value` decorator and structs with empty body. - -- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after - syntax error during tuple creation - -- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now - properly supports signature types with named arguments and parameters. - -- [#2007](https://github.com/modularml/mojo/issues/2007) and - [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer - crashes on certain types of closures. - -- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` - decorator fails gracefully after duplicate field error. - -- [#2068](https://github.com/modularml/mojo/issues/2068) - Fix simd.reduce for size_out == 2 - ([PR #2102](https://github.com/modularml/mojo/pull/2102)) From 61cf3c60101dde15cd6300d811e33c5531e77639 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 2 May 2024 10:20:58 -0700 Subject: [PATCH 0316/2019] [Docs] Remove note about Atomic from 24.3 changelog. (cherry-pick from main) (#39158) Related to MOCO-564. (#39155) MODULAR_ORIG_COMMIT_REV_ID: 8e5c4ff2796f0b30c00175b06e30223c01d9df51 --- docs/changelog-released.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index e115899cea..a423dd2124 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -391,10 +391,6 @@ modular update mojo ([@mzaks](https://github.com/mzaks), contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) -- [`Atomic`](/mojo/stdlib/os/atomic/Atomic) is now movable. - ([@StandinKP](https://github.com/StandinKP), fixes - [#2026](https://github.com/modularml/mojo/issues/2026)) - - [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The `atol()`function is used internally by `String.__int__()`, so `int(String( " 10 "))` now returns `10` instead of raising an error. From 00528f4c44f799966b3da0a866a00e24370c4b59 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 2 May 2024 14:46:52 -0500 Subject: [PATCH 0317/2019] [stdlib] Support testing on `main` branch The current test configs cover the `nightly` release but not the stable release. This change updates the configs so that tests can be run with the latest `mojo` release. Signed-off-by: Patrick Dougherty --- stdlib/COMPATIBLE_COMPILER_VERSION | 1 + stdlib/test/lit.cfg.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 stdlib/COMPATIBLE_COMPILER_VERSION diff --git a/stdlib/COMPATIBLE_COMPILER_VERSION b/stdlib/COMPATIBLE_COMPILER_VERSION new file mode 100644 index 0000000000..e9a9b4514e --- /dev/null +++ b/stdlib/COMPATIBLE_COMPILER_VERSION @@ -0,0 +1 @@ +24.3.0 diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index ca694eaae0..290d07630d 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -49,11 +49,13 @@ # to allow running the tests with LLVM sanitizers. config.substitutions.insert(0, ("%mojo", "mojo")) -# The `mojo` nightly compiler ships with its own `stdlib.mojopkg` -# For the open-source stdlib, we need to specify the paths to the -# just-built `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, -# without this, the `mojo` compiler would use its own `stdlib.mojopkg` -# it ships with which is not what we want. +# The `mojo` nightly compiler ships with its own `stdlib.mojopkg` For the +# open-source stdlib, we need to specify the paths to the just-built +# `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the +# `mojo` compiler would use its own `stdlib.mojopkg` it ships with which is not +# what we want. We override both the stable and nightly `mojo` import paths +# here to support both versions of the compiler. +os.environ["MODULAR_MOJO_IMPORT_PATH"] = str(build_root) os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = str(build_root) @@ -71,6 +73,7 @@ def has_not(): lit.llvm.llvm_config.with_system_environment( [ "MODULAR_HOME", + "MODULAR_MOJO_IMPORT_PATH", "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", ] ) From bf73717d79fbb79b4b2bf586b3a40072308b6184 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 8 May 2024 16:56:14 -0700 Subject: [PATCH 0318/2019] add a proposal for Reference autoderef --- proposals/auto-dereference.md | 277 ++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 proposals/auto-dereference.md diff --git a/proposals/auto-dereference.md b/proposals/auto-dereference.md new file mode 100644 index 0000000000..abfe44d95b --- /dev/null +++ b/proposals/auto-dereference.md @@ -0,0 +1,277 @@ +# Auto def-Reference for Mojo + +Chris Lattner, May 6, 2024 + +**TL;DR:** Add an ‘eager decay’ model for references to improve developer +ergonomics and work more like C++ References. Delete `__refitem__` as well. + +# Motivation + +The safe `Reference` type in Mojo is slowly making progress in terms of its +implementation and capabilities, but one of the major problems with it is +usability. This whitepaper tackles one specific part of the problem, that you +have to explicitly dereference references. + +The lack of automatic dereferencing leads to confusing user problems, e.g. when +iterating over a `List`, the iterator is returning references, so you have to +write: + +```mojo +for name in names: + print(name[]) +``` + +Yuck.🤮 Furthermore, other Mojo code end up being piles of punctuation: + +```mojo +fn offset_momentum(inout bodies: List[Planet]): + var p = SIMD[DType.float64, 4]() + + for body in bodies: + # What is this? + p += body[].velocity * body[].mass + + var body = bodies[0] + body.velocity = -p / SOLAR_MASS + + bodies[0] = body +``` + +Worse yet, we’re using subscript to mean two different things - in the case of a +reference, this is an explicit “dereference” operation that we want people to +think of as transparent. In the case of array (and pointers) we want an +explicit “index and subscript” operation, which is a “proper” use of subscript. + +Ok, so let’s eliminate the need for `foo[]` when `foo` is a `Reference`! + +# Background + +One very important thing to get out of the way: this document is **only** +talking about the `Reference` type, this is not talking about “things with +reference semantics”, and is definitely not changing `PythonObject` or anything +related to Python interop or compatibility. Python doesn’t have a notion like +`Reference`. + +### Current API of `Reference` + +For reference (haha, zing 🔥), the entire public API of `Reference` is currently: + +```mojo +@value +struct Reference[...]: + fn __init__(inout self, value: Self._mlir_type): + """Constructs a Reference from the MLIR reference.""" + + fn __refitem__(self) -> Self._mlir_type: + """Enable subscript syntax `ref[]` to access the element.""" + + fn __mlir_ref__(self) -> Self._mlir_type: + """Enable the Mojo compiler to see into `Reference`.""" + return self.value +``` + +It took quite a lot of work to get here, but these three methods are the key to +all the magic: + +1) The first is used by the compiler to form a `Reference` from an xValue. + +2) The second enables `foo[]` syntax + +3) The third allows the compiler to implement `__refitem__`. + +As you can tell, we don’t want typical users to interact directly with +`Reference`. It is something you can declare as part of a type signature, but +you don’t want (and shouldn’t have to) interact with it directly. + +For a point of intuition, compare `Reference` to a C++ reference like `int&`: +there is no API on the reference itself, it is just a part of the type system +that affects overloading and argument passing, but is fully transparent to +method resolution etc. + +### The difference between pointers and references + +It might be surprising to consider these two cases differently: why is `myPtr[]` +a good thing but `myRef[]` is not? Several reasons: + + 1) users generally expect and want references to be transparent. + + 2) pointers can be indexed with `myPtr[i]` : `myPtr[]` is just sugar for when + `i` is zero. + + 3) `Reference` is a safe type and “dereferencing” it is always safe and + correct, whereas `UnsafePointer` (and friends) are unsafe and dereferencing + should be explicit in source. + + 4) Dereferencing a `Reference` doesn’t actually do anything - it converts an + RValue of Reference type to an LValue or BValue in the compiler - it is only + a load or store to the resultant xValue that does something! + +### Not competing with `getattr` + +Mojo already has the ability to overload `x.y` syntax on a type both dynamically +and statically through `__getattr__` and `__setattr__`. This is a different +part of the design space here, because reference promotion needs to work even +when not accessing a member, e.g. in `var x = List(someRef)`. + +# Two possible designs + +There are two fundamental designs that we could take to make references +transparent, I call them the “*eager reference decay”* model and the +“*persistent reference*” model. Both models solve the most important problems +and allow these examples to work: + +```mojo +# Things common to both models. +fn show_commonality(a: Reference[String, _, _]): + # Gets the length of the string, not the reference. + var length = len(a) + + # call __add__ on the string, deref'ing the reference twice. + var doubled = a+a + + # Methods look through the reference. + var joined = a.join("x", "y") + + # r is Reference[String]: this copies the reference not string. + var r : Reference[...] = a + + # This gives a List of references in both models. + var reflist = List[Reference[...]](a, a, a) +``` + +Note the lack of any `a[]`’s in these examples! + +I think we should use the former, but this section outlines both approaches +and makes the case. + +## The “Eager Reference Decay” model + +One approach is to follow C++’s approach and make it so `Reference` decays to an +LValue or BValue of the underlying type. This decay specifically happens when +the `Reference` is produced as an RValue (e.g. as the result of a function call) +and during LValue to RValue conversion. This means that LValues of `Reference` +type are allowed, but RValue’s of `Reference` type will never be observable to a +Mojo programmer. + +Let’s look at an example: + +```mojo +fn show_differences(a: Reference[String, _, _]): + # 'T' equals String, not Reference[String] + alias T = __type_of(a) + + # 'v1' has type String, so this copies the string + var v1 = a + + # List of strings, not a list of references of strings + var list = List(a, a, a) + + # Slices the string, not a dereference! + var strslice = a[] +``` + +The intuition here is the compiler internally has a way to reason about +“[LValue](https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue)”s, and +the formal type of a variable doesn’t include how it can be accessed: whether it +is mutable or just readable etc. With this as a guide, it makes sense that +`Reference` immediately decays to this internal representation - the utility of +Reference is that it allows one to declare the lifetime and indirection as part +of a function signature or in a struct member in a safe way. Beyond that, we +want it to go away. + +This approach is proven to work (i.e. be teachable and usable) at scale because +it is used in the C++ programming language. It also has a number of other +advantages, which we explore after introducing the second model. + +## The “Persistent Reference” model + +An alternative approach is to try to maintain the `Reference` in the type system +and eliminate it *only when necessary* to type check the program, e.g. +auto-dereference `[x.join](http://x.foo)` because `Reference` doesn’t have a +`join` member, but the underlying `String` does. This model maintains the +reference as much as possible, which leads to differences in the examples shown +above: + +```mojo +fn show_differences(a: Reference[String, _, _]): + # 'T' equals Reference[String], not String + alias T = __type_of(a) + + # 'v1' has type Reference[String] so this doesn't copy the string + var v1 = a + + # List of references, not a list of strings. + var list = List(a, a, a) + + # Dereferences the reference(??): doesn't slice the string! + var strslice = a[] +``` + +This model seemed initially appealing to me, and is similar to Rust’s approach. +After exploring it, I noticed that it has two major downsides: + +1) Because references still exist as user-defined values, you need a way to + dereference them, and this leads to confusion when referring to things that + can be sliced or subscripted. + +2) This violates the general design point of Mojo that references shouldn’t + “infect” code that isn’t aware of them. + +3) This approach is also significantly more complicated to implement and teach + than the eager decay model. + +The first issue is perhaps tolerable - we could introduce new syntax to +dereference a reference, or come up with some other way to get that out of the +way. As we mentioned before, `Reference` intentionally has a minimal API, so +the probability of conflicts is low. + +That said, **the second issue is a showstopper, and this is a major intentional +difference between Rust and Mojo**. It is already very common for functions to +return references (e.g. this is why we have to solve the `[]` problem!), and we +don’t want lifetimes to invade types unexpectedly through type inference, e.g. +`var list = List(self.get_thing())` . + +Mojo is a language with value semantics and strong copying and moving support, +aimed at the Python community. We are embracing safe references and lifetimes, +but want to progress on the usability challenges that the Rust community is +still working through. The goal of Mojo is to be familiar and learnable by +Python programmers, and as such, we want management of references to be opt-in, +not implicit because you’re calling a function that happens to return one. + +Furthermore, both models are equally expressible - they both permit references +in structures, propagating references around when needed, and other fancy +tricks. The two models differ on the defaults observed and whether reference +propagation is opt-in or opt-out. + +For all these reasons, I recommend going with the “Eager Reference Decay” model. + +# Implementation approach + +There are three steps to implementing this: + +## Implement auto-deref itself + +The implementation approach is straightforward - all expressions that yield a +`Reference` will automatically decay to an LValue or BValue when the expression +is formed. + +We don’t want to hard code knowledge of `Reference` in the compiler, instead we +should key this off of the presence of the existing `__mlir_ref__` member. + +## Remove `Reference.__refitem__` + +Now that there are no persistent expressions of `Reference` type, we can further +narrow the interface by dropping the newly pointless refitem implementation. +You can never have a value of Reference type, so there is no way to apply `[]` +to it. + +## Remove `__refitem__` / `__refattr__` from the language + +Now that references automatically dereference, we don’t need `__refitem__` and +`__refattr__` anymore - types that use them can just implement `__getitem__` and +have it return a `Reference`. This eliminates a pile of complexity and magic +from the compiler. + +# Future directions + +This approach is simple, but could enable extensions to future things that need implicit dereference abilities, e.g. existentials. From 24dde10f03ab3532741f7424bdfadec90d0ca7c5 Mon Sep 17 00:00:00 2001 From: Artemio Garza Reyna Date: Fri, 26 Apr 2024 09:33:38 -0600 Subject: [PATCH 0319/2019] [External] [stdlib] String to Int with specified base (#38638) [External] [stdlib] String to Int with specified base Closes modularml/mojo#2273 MODULAR_ORIG_COMMIT_REV_ID: 035958da86d73058a7e9c4c92aebb57efcd64d5e --- docs/changelog-released.md | 610 +---------- docs/changelog.md | 462 +++++++- docs/faq.md | 44 +- docs/manual/functions.ipynb | 262 +---- docs/manual/get-started/index.md | 15 +- docs/manual/lifecycle/death.ipynb | 11 +- docs/manual/parameters/index.ipynb | 9 +- docs/manual/types.ipynb | 1002 ------------------ proposals/README.md | 8 - proposals/auto-dereference.md | 277 ----- proposals/byte-as-uint8.md | 25 - proposals/inferred-parameters.md | 143 --- proposals/lifetimes-and-provenance.md | 426 -------- proposals/lifetimes-keyword-renaming.md | 128 --- proposals/mojo-and-dynamism.md | 147 --- proposals/project-manifest-and-build-tool.md | 104 -- proposals/remove-let-decls.md | 153 --- proposals/value-ownership.md | 490 --------- stdlib/src/builtin/int.mojo | 20 + stdlib/src/builtin/string.mojo | 144 ++- stdlib/test/builtin/test_string.mojo | 143 ++- stdlib/test/lit.cfg.py | 13 +- 22 files changed, 773 insertions(+), 3863 deletions(-) delete mode 100644 docs/manual/types.ipynb delete mode 100644 proposals/README.md delete mode 100644 proposals/auto-dereference.md delete mode 100644 proposals/byte-as-uint8.md delete mode 100644 proposals/inferred-parameters.md delete mode 100644 proposals/lifetimes-and-provenance.md delete mode 100644 proposals/lifetimes-keyword-renaming.md delete mode 100644 proposals/mojo-and-dynamism.md delete mode 100644 proposals/project-manifest-and-build-tool.md delete mode 100644 proposals/remove-let-decls.md delete mode 100644 proposals/value-ownership.md diff --git a/docs/changelog-released.md b/docs/changelog-released.md index a423dd2124..f423cad6d3 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -30,594 +30,6 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` -## v24.3 (2024-05-02) - -### ✨ Highlights - -- `AnyPointer` was renamed to - [`UnsafePointer`](mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and is now - Mojo's preferred unsafe pointer type. It has several enhancements, including: - - - The element type can now be any type: it doesn't require `Movable`. - - - Because of this, the `take_value()`, `emplace_value()`, and `move_into()` - methods have been changed to top-level functions and renamed. The new - functions are: - - - [`initialize_pointee_copy`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy) - - [`initialize_pointee_move`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) - - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) - - [`move_pointee`](/mojo/stdlib/memory/unsafe_pointer/move_pointee) - - - A new - [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) - function runs the destructor on the pointee. - - - `UnsafePointer` can be initialized directly from a - [`Reference`](/mojo/stdlib/memory/reference/Reference) with - `UnsafePointer(someRef)` and can convert to a reference with - `yourPointer[]`. Both infer element type and address space. Note that when - you convert a pointer to a reference, there's no way for Mojo to track the - lifetime of the original value. So the resulting reference is no safer than - the original pointer. - -- All of the pointer types received some cleanup to make them more consistent, - for example the `unsafe.bitcast()` global function is now a consistent - [`bitcast()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#bitcast) method - on the pointers, which can convert element type and address space. - -- Improvements to variadic arguments support. - - - Heterogenous variadic pack arguments now work reliably even with memory types, - and have a more convenient API to use, as defined by the - [`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack) type. For - example, a simplified version of `print` can be implemented like this: - - ```mojo - fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): - print_string(str(first)) - - @parameter - fn print_elt[T: Stringable](a: T): - print_string(" ") - print_string(a) - rest.each[print_elt]() - ``` - - - Mojo now supports declaring functions that have both optional and variadic - arguments, both positional and keyword-only. For example, this now works: - - ```mojo - fn variadic_arg_after_default( - a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int - ): ... - ``` - - Positional variadic parameters also work in the presence of optional - parameters. That is: - - ```mojo - fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): - pass - ``` - - Note that variadic keyword parameters are not supported yet. - - For more information, see - [Variadic arguments](/mojo/manual/functions#variadic-arguments) in the Mojo - Manual. - -- The `mojo build` and `mojo run` commands now support a `-g` option. This - shorter alias is equivalent to writing `--debug-level full`. This option is - also available in the `mojo debug` command, but is already the default. - -- Many new standard library APIs have been filled in, including many community - contributions. Changes are listed in the standard library section. - -- The Mojo Manual has a new page on [Types](/mojo/manual/types). - -### Language changes - -- Certain dunder methods that take indices - (`__getitem__()`, `__setitem__()`, and `__refitem__()`) or names - (`__getattr__()` and `__setattr__()`) can now take the index or name - as a parameter value instead of an argument value. This is enabled when you - define one of these methods with no argument other than `self` (for a getter) - or `self` and the set value (for a setter). - - This enables types that can only be subscripted into with parameters, as well - as things like the following example, which passes the attribute name as a - parameter so that attribute names can be checked at compile time. - - ```mojo - struct RGB: - fn __getattr__[name: StringLiteral](self) -> Int: - @parameter - if name == "r": return ... - elif name == "g": return ... - else: - constrained[name == "b", "can only access with r, g, or b members"]() - return ... - - var rgb = RGB() - print(rgb.b) # Works - print(rgb.q) # Compile error - ``` - -- Mojo now allows users to capture the source location of code and call location - of functions dynamically using the built-in `__source_location()` and - `__call_location()` functions. For example: - - ```mojo - @always_inline - fn my_assert(cond: Bool, msg: String): - if not cond: - var call_loc = __call_location() - print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) - - fn main(): - my_assert(False, "always fails") # some_file.mojo, line 193 - ``` - - This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that - `__call_location()` only works in `@always_inline` or - `@always_inline("nodebug")` functions. It gives incorrect results if placed in - an `@always_inline` function that's called *from* an - `@always_inline("nodebug")` function. - - Neither `__source_location()` nor `__call_location()` work when called in a - parameter context. For example: - - ```mojo - @always_inline - fn mystery_location() -> String: - var loc = __call_location() - return str(loc.file_name) - - def main(): - alias doesnt_work = mystery_location() # - ``` - -### Standard library changes - -#### ⭐️ New - -- [`List`](/mojo/stdlib/collections/list/List) has several new methods: - - - `pop(index)` for removing an element at a particular index. - By default, `List.pop()` removes the last element in the list. - ([@LJ-9801](https://github.com/LJ-9801), fixes - [#2017](https://github.com/modularml/mojo/issues/2017)) - - - `resize(new_size)` for resizing the list without the need to - specify an additional value. - ([@mikowals](https://github.com/mikowals), fixes - [#2133](https://github.com/modularml/mojo/issues/2133)) - - - `insert(index, value)` for inserting a value at a specified index - into the `List`. ([@whym1here](https://github.com/whym1here), fixes - [#2134](https://github.com/modularml/mojo/issues/2134)) - - - A new constructor `List(ptr, size, capacity)` to to avoid needing to - do a deep copy of an existing contiguous memory allocation when constructing - a new `List`. ([@StandinKP](https://github.com/StandinKP), fixes - [#2170](https://github.com/modularml/mojo/issues/2170)) - -- [`Dict`](/mojo/stdlib/collections/dict/Dict) now has a `update()` method to - update keys/values from another `Dict`. - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- [`Set`](/mojo/stdlib/collections/set/Set) now has named methods for set - operations: - - `difference()` mapping to `-` - - `difference_update()` mapping to `-=` - - `intersection_update()` mapping to `&=` - - `update()` mapping to `|=` - - ([@arvindavoudi](https://github.com/arvindavoudi)) - -- `Dict`, `List`, and `Set` all conform to the `Boolable` trait. The collections - evaluate to `True` if they contain any elements, `False` otherwise: - - ```mojo - def list_names(names: List[String]): - if names: - for name in names: - print(name[]) - else: - print("No names to list.") - ``` - - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Added [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) function for - creating reversed iterators. Several range types, `List`, and `Dict` now - support iterating in reverse. - - ```mojo - var numbers = List(1, 2, 3, 4, 5) - for number in reversed(numbers): - print(number) - ``` - - ([@helehex](https://github.com/helehex) and - [@jayzhan211](https://github.com/jayzhan211), contributes towards - [#2325](https://github.com/modularml/mojo/issues/2325)) - -- [`Optional`](/mojo/stdlib/collections/optional/Optional) now implements - `__is__` and `__isnot__` methods so that you can compare an `Optional` with - `None`. For example: - - ```mojo - var opt = Optional(1) - if opt is not None: - print(opt.value()[]) - ``` - - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now works with memory-only element - types like `String` and allows you to directly index into it with a parameter - expression. This means you can now simply use `x = tup[1]` like Python - instead of `x = tup.get[1, Int]()`. You can also assign into tuple elements - now as well with `tup[1] = x`. - - ```mojo - var tuple = ("Green", 9.3) - var name = tuple[0] - var value = tuple[1] - ``` - - Note that because the subscript must be a parameter expression, you can't - iterate through a `Tuple` using an ordinary `for` loop. - -- The [`Reference`](/mojo/stdlib/memory/reference/Reference) type has several - changes, including: - - - It has moved to the `memory.reference` module instead of `memory.unsafe`. - - `Reference` now has an - [`unsafe_bitcast()`](/mojo/stdlib/memory/reference/Reference#unsafe_bitcast) - method, similar to the pointer types. - - - Several unsafe methods were removed, including `offset()`, - `destroy_element_unsafe()` and `emplace_ref_unsafe()`. This is because - `Reference` is a safe type—use `UnsafePointer` to do unsafe operations. - -- [`Bool`](/mojo/stdlib/builtin/bool/Bool) can now be implicitly converted from - any type conforming to the [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) - trait. This means that you no longer need to write code like this: - - ```mojo - @value - struct MyBoolable: - fn __bool__(self) -> Bool: ... - - fn takes_boolable[T: Boolable](cond: T): ... - - takes_boolable(MyBoolable()) - ``` - - Instead, you can simply write: - - ```mojo - fn takes_bool(cond: Bool): ... - - takes_bool(MyBoolable()) - ``` - - Note that calls to `takes_bool()` will perform the implicit conversion, so in - some cases is it still better to explicitly declare a type parameter, e.g.: - - ```mojo - fn takes_two_boolables[T: Boolable](a: T, b: T): - # Short circuit means `b.__bool__()` might not be evaluated. - if a.__bool__() and b.__bool__(): - ... - ``` - -- [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now conforms to the - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, meaning that - it can be used as key type for [`Dict`](/mojo/stdlib/collections/dict/Dict). - This allows you to easily build and interact with Python dictionaries in Mojo: - - ```mojo - def main(): - d = PythonObject(Dict[PythonObject, PythonObject]()) - d["foo"] = 12 - d[7] = "bar" - d["foo"] = [1, 2, "something else"] - print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` - ``` - -- [`FileHandle.seek()`](/mojo/stdlib/builtin/file/FileHandle#seek) now has a - `whence` argument that defaults to `os.SEEK_SET` to seek from the beginning of - the file. You can now set to `os.SEEK_CUR` to offset by the current - `FileHandle` seek position: - - ```mojo - var f = open("/tmp/example.txt") - # Skip 32 bytes - f.seek(os.SEEK_CUR, 32) - ``` - - Or `os.SEEK_END` to offset from the end of file: - - ```mojo - # Start from 32 bytes before the end of the file - f.seek(os.SEEK_END, -32) - ``` - -- [`FileHandle.read()`](/mojo/stdlib/builtin/file/FileHandle#read) can now - read straight into a - [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): - - ```mojo - var file = open("/tmp/example.txt", "r") - - # Allocate and load 8 elements - var ptr = DTypePointer[DType.float32].alloc(8) - var bytes = file.read(ptr, 8) - print("bytes read", bytes) - print(ptr.load[width=8]()) - ``` - -- The `sys` module now contains an `exit()` function that would exit a Mojo - program with the specified error code. - - ```mojo - from sys import exit - - exit(0) - ``` - -- The constructors for [`Tensor`](/mojo/stdlib/tensor/tensor/Tensor) have been - changed to be more consistent. As a result, constructors take the shape as the - first argument (instead of the second) when constructing a tensor with pointer - data. - - If you pass a single scalar value to the `Tensor` constructor, it now - broadcasts the value to all elements in the tensor. For example, - `Tensor[DType.float32](TensorShape(2,2), 0)` constructs a `2x2` tensor - initialized with all zeros. This provides an easy way to fill in the data of a - tensor. - -- [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and - `removesuffix()` methods. - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- The [`ord`](/mojo/stdlib/builtin/string/ord) and - [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to - accept any Unicode character. - ([@mzaks](https://github.com/mzaks), contributes towards - [#1616](https://github.com/modularml/mojo/issues/1616)) - -- [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The - `atol()`function is used internally by `String.__int__()`, so - `int(String( " 10 "))` now returns `10` instead of raising an error. - ([@artemiogr97](https://github.com/artemiogr97)) - -- [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) now implements the `__rmod__()` - method. ([@bgreni](https://github.com/bgreni), fixes - [#1482](https://github.com/modularml/mojo/issues/1482)) - -- [`bool(None)`](/mojo/stdlib/builtin/bool/bool-function) is now implemented. - ([@zhoujingya](https://github.com/zhoujingya)) - -- The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) type now - implements `gather()` for gathering a `SIMD` vector from offsets of a current - pointer. Similarly, support for `scatter()` was added to scatter a `SIMD` - vector into offsets of the current pointer. - ([@leandrolcampos](https://github.com/leandrolcampos)) - -- The [`len()`](/mojo/stdlib/builtin/len/len) function now handles a - [`range()`](/mojo/stdlib/builtin/range/range) specified with a negative end - value, so that things like `len(range(-1))` work correctly. - ([@soraros](https://github.com/soraros)) - -- [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now prints - its location (filename, line, and column where it was called) in its error - message. Similarly, the `assert` helpers in the - [`testing`](/mojo/stdlib/testing/testing/) module now include location - information in their messages. - -- The [`testing.assert_equal[SIMD]()`](/mojo/stdlib/testing/testing/assert_equal) - function now raises if any of the elements mismatch in the two `SIMD` - arguments being compared. - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- The [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) - and [`math.isclose()`](/mojo/stdlib/math/math/isclose) functions now have an - `equal_nan` flag. When set to `True`, then NaNs are considered equal. - -- The [`object`](/mojo/stdlib/builtin/object/object) type now supports the - division, modulo, and left and right shift operators, including the in-place - and reverse variants. - ([@LJ-9801](https://github.com/LJ-9801), fixes - [#2224](https://github.com/modularml/mojo/issues/2224)) - -- Added checked arithmetic operations for `SIMD` integers. - - `SIMD` integer types (including the sized integer scalars like `Int64`) can - now perform checked additions, subtractions, and multiplications using the - following new methods: - - - [`add_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#add_with_overflow) - - [`sub_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#sub_with_overflow) - - [`mul_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#mul_with_overflow) - - Checked arithmetic allows the caller to determine if an operation exceeded - the numeric limits of the type. For example: - - ```mojo - var simd = SIMD[DType.int8, 4](7, 11, 13, 17) - var product: SIMD[DType.int8, 4] - var overflow: SIMD[DType.bool, 4] - (product, overflow) = simd.mul_with_overflow(simd) - for i in range(len(product)): - if overflow[i]: - print("") - else: - print(product[i]) - ``` - - ([@lsh](https://github.com/lsh)) - -- Added [`os.remove()`](/mojo/stdlib/os/os/remove) and - [`os.unlink()`](/mojo/stdlib/os/os/unlink) for deleting files. - ([@artemiogr97](https://github.com/artemiogr97), fixes - [#2306](https://github.com/modularml/mojo/issues/2306)) - -#### 🦋 Changed - -- The [`parallel_memcpy()`](/mojo/stdlib/algorithm/memory/parallel_memcpy) - function has moved from the `buffer` package to the `algorithm` package. - Please update your imports accordingly. - -- [`Optional.value()`](/mojo/stdlib/collections/optional/Optional#value) now - returns a reference instead of a copy of the contained value. - - To perform a copy manually, dereference the result: - - ```mojo - var result = Optional(123) - - var value = result.value()[] - ``` - - ([@lsh](https://github.com/lsh), fixes - [#2179](https://github.com/modularml/mojo/issues/2179)) - -- Per the accepted community proposal, - [Standardize the representation of byte sequence as a sequence of unsigned - 8-bit integers](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), - began transition to using `UInt8` by changing the data pointer of `Error` - to `DTypePointer[DType.uint8]`. - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes - towards [#2317](https://github.com/modularml/mojo/issues/2317)) - -- Continued transition to `UnsafePointer` from the legacy `Pointer` type - in various standard library APIs and internals. - ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -### Tooling changes - -- The behavior of `mojo build` when invoked without an output `-o` argument has - changed slightly: `mojo build ./test-dir/program.mojo` now outputs an - executable to the path `./program`, whereas before it would output to the path - `./test-dir/program`. - -- The `mojo package` command no longer supports the `-D` flag. All compilation - environment flags should be provided at the point of package use (e.g. - `mojo run` or `mojo build`). - -- The REPL no longer allows type level variable declarations to be - uninitialized, e.g. it will reject `var s: String`. This is because it does - not do proper lifetime tracking (yet!) across cells, and so such code would - lead to a crash. You can work around this by initializing to a dummy value - and overwriting later. This limitation only applies to top level variables, - variables in functions work as they always have. - -### Other changes - -#### Low-level language changes - -- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to - the underlying memory representation as a `!lit.ref` value without checking - initialization status of the underlying value. This is useful in very - low-level logic but isn't designed for general usability and will likely - change in the future. - -- Properties can now be specified on inline MLIR ops: - - ```mojo - _ = __mlir_op.`kgen.source_loc`[ - _type = ( - __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` - ), - _properties = __mlir_attr.`{inlineCount = 1 : i64}`, - ]() - ``` - - As the example shows above, the protected `_properties` attribute can be - passed during op construction, with an MLIR `DictionaryAttr` value. - -#### ❌ Removed - -- Support for "register only" variadic packs has been removed. Instead of - `AnyRegType`, please upgrade your code to `AnyType` in examples like this: - - ```mojo - fn your_function[*Types: AnyRegType](*args: *Ts): ... - ``` - - This move gives you access to a nicer API and has the benefit of being memory - safe and correct for non-trivial types. If you need specific APIs on the - types, please use the correct trait instead of `AnyType`. - -- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults - to popping the last element in the list. - -- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. - -- The `__get_lvalue_as_address(x)` magic function has been removed. To get a - reference to a value use `Reference(x)` and if you need an unsafe pointer, you - can use `UnsafePointer.address_of(x)`. - -#### 🛠️ Fixed - -- [#516](https://github.com/modularml/mojo/issues/516) and - [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. - "Can't create a function that returns two strings." - -- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5). - -- [#1609](https://github.com/modularml/mojo/issues/1609) alias with - `DynamicVector[Tuple[Int]]` fails. - -- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` - in a Mojo package is an error, for now. This is not intended to work yet, - erroring for now will help to prevent accidental undefined behavior. - -- [#1215](https://github.com/modularml/mojo/issues/1215) and - [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no - longer cuts off hover previews for functions with functional arguments, - parameters, or results. - -- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and - documentation generation handling of inout arguments. - -- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer - crashes the Mojo parser. - -- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac - has been fixed. - -- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadic arguments - don't work with non-trivial register-only types. - -- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed - and formatted correctly by `mojo format`. - -- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to - `@value` decorator and structs with empty body. - -- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after - syntax error during tuple creation. - -- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now - properly supports signature types with named arguments and parameters. - -- [#2007](https://github.com/modularml/mojo/issues/2007) and - [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer - crashes on certain types of closures. - -- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` - decorator fails gracefully after duplicate field error. - -- [#2068](https://github.com/modularml/mojo/issues/2068) - Fix `SIMD.reduce()` for size_out == 2. - ([@soraros](https://github.com/soraros)) - ## v24.2.1 (2024-04-11) This release doesn't include any changes to Mojo. @@ -1592,7 +1004,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are *not enabled* + before the program exits. By default, assertions are _not enabled_ in the standard library right now for performance reasons. - The Mojo Language Server now implements the References request. IDEs use @@ -1702,7 +1114,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. array slices. While this is a major step forward for the lifetimes system in Mojo, it is - still *very* early and awkward to use. Notably, there is no syntactic sugar + still _very_ early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now. @@ -2025,8 +1437,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Traits have arrived! - You can now define a *trait*, which consists of a required set of method - prototypes. A struct can *conform to* the trait by implementing these methods. + You can now define a _trait_, which consists of a required set of method + prototypes. A struct can _conform to_ the trait by implementing these methods. This lets you write generic functions that work on any structs that conform to a given trait. @@ -2146,7 +1558,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### ⭐️ New - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include *everything* about Mojo yet, but it includes a lot, + It doesn't include _everything_ about Mojo yet, but it includes a lot, and more than the original [programming manual](/mojo/programming-manual.html) (now deprecated). @@ -2169,7 +1581,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` In the first signature for `eat()`, the `b` parameter isn't bound, so it's - *implicitly* added as an input parameter on the function. + _implicitly_ added as an input parameter on the function. In the second signature for `eat()`, the author has explicitly defined an input parameter (`_b`), which is bound to the second parameter on the argument @@ -2414,8 +1826,8 @@ the previous "read to EOF" behavior when size is negative. - There is an issue affecting Jupyter notebooks that use autotuning and traits. This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the *Matrix multiplication in - Mojo* notebook. + outside of the notebooks. This issue affects the _Matrix multiplication in + Mojo_ notebook. ## v0.5.0 (2023-11-2) @@ -2430,7 +1842,7 @@ the previous "read to EOF" behavior when size is negative. function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. -- Mojo now supports compile-time *keyword parameters*, in addition to existing +- Mojo now supports compile-time _keyword parameters_, in addition to existing support for [keyword arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For example: @@ -2769,7 +2181,7 @@ the previous "read to EOF" behavior when size is negative. return self^ ``` - Here Mojo *cannot* invoke a noop `__exit__` method because the context + Here Mojo _cannot_ invoke a noop `__exit__` method because the context manager is consumed by the `__enter__` method. This can be used for types (like file descriptors) that are traditionally used with `with` statements, even though Mojo's guaranteed early destruction doesn't require that. @@ -4036,7 +3448,7 @@ busy this week. is currently unsupported). These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to *not* capture values + types, with the caveat that closures are known to _not_ capture values correctly. Be very careful with interesting types in the vicinity of a closure! diff --git a/docs/changelog.md b/docs/changelog.md index e0ff7bcede..51db2004ca 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,58 +16,456 @@ what we publish. ### 🔥 Legendary +- Tuple now works with memory-only element types like String and allows you to + directly index into it with a parameter exprssion. This means you can now + simply use `x = tup[1]` like Python instead of `x = tup.get[1, Int]()`. You + can also assign into tuple elements now as well with `tup[1] = x`. + ### ⭐️ New -- `int()` can now take a string and a specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is - specified, the string will be parsed as if it was an integer literal, with the - base determined by whether the string contains the prefix `"0x"`, `"0o"`, or - `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by - [@artemiogr97](https://github.com/artemiogr97), fixes - [#2274](https://github.com/modularml/mojo/issues/2274)) +- Heterogenous variadic pack arguments now work reliably even with memory types, + and have a more convenient API to use, as defined on the `VariadicPack` type. + For example, a simplified version of `print` can be implemented as: + + ```mojo + fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): + print_string(str(first)) + + @parameter + fn print_elt[T: Stringable](a: T): + print_string(" ") + print_string(a) + rest.each[print_elt]() + ``` + +- The `sys` module now contains an `exit` function that would exit a Mojo + program with the specified error code. + +- The constructors for `tensor.Tensor` have been changed to be more consistent. + As a result, one has to pass in the shape as first argument (instead of the + second) when constructing a tensor with pointer data. + +- The constructor for `tensor.Tensor` will now splat a scalar if its passed in. + For example, `Tensor[DType.float32](TensorShape(2,2), 0)` will construct a + `2x2` tensor which is initialized with all zeros. This provides an easy way + to fill the data of a tensor. + +- The `mojo build` and `mojo run` commands now support a `-g` option. This + shorter alias is equivalent to writing `--debug-level full`. This option is + also available in the `mojo debug` command, but is already the default. + +- `PythonObject` now conforms to the `KeyElement` trait, meaning that it can be + used as key type for `Dict`. This allows on to easily build and interact with + Python dictionaries in mojo: + + ```mojo + def main(): + d = PythonObject(Dict[PythonObject, PythonObject]()) + d["foo"] = 12 + d[7] = "bar" + d["foo"] = [1, 2, "something else"] + print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` + ``` + +- `List` now has several new methods: + - `pop(index)` for removing an element at a particular index. + ([PR #2041](https://github.com/modularml/mojo/pull/2041)) + (Fixes [#2017](https://github.com/modularml/mojo/issues/2017))\ + By default, `List.pop()` removes the last element in the list. + + - `resize(new_size)` for resizing the list without the need to + specify an additional value. + ([PR #2140](https://github.com/modularml/mojo/pull/2140), + Fixes [#2133](https://github.com/modularml/mojo/issues/2133)) + + - `insert(index, value)` for inserting a value at a specified index + into the `List`. + ([PR #2148](https://github.com/modularml/mojo/pull/2148), + Fixes [#2134](https://github.com/modularml/mojo/issues/2134)) + + - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep + copy of an existing contiguous memory allocation when constructing a new `List`. + ([PR #2182](https://github.com/modularml/mojo/pull/2182), + Fixes [#2170](https://github.com/modularml/mojo/issues/2170)) + +- `Dict` now has a `update()` method to update keys/values from another `Dict`. + ([PR #2085](https://github.com/modularml/mojo/pull/2085)) + +- `Set` now has named methods for set operations + ([PR #2214](https://github.com/modularml/mojo/pull/2214)): + - `Set.difference()` mapping to `-` + - `Set.difference_update()` mapping to `-=` + - `Set.intersection_update()` mapping to `&=` + - `Set.update()` mapping to `|=` + +- `String` now has `removeprefix()` and `removesuffix()` methods. + ([PR #2038](https://github.com/modularml/mojo/pull/2038)) + +- `Optional` now implements `__is__` and `__isnot__` methods so that you can compare + an `Optional` with `None`, e.g. `Optional(1) is not None` for example. + ([PR #2082](https://github.com/modularml/mojo/pull/2082)) + +- The `ord` and `chr` functions have been improved to accept any Unicode character. + ([PR #2149](https://github.com/modularml/mojo/pull/2149), + Contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) + +- `Atomic` is now movable. + ([PR #2088](https://github.com/modularml/mojo/pull/2088), + Fixes [#2026](https://github.com/modularml/mojo/issues/2026)) + +- `Dict` and `List` are both `Boolable` now. + ([PR #2262](https://github.com/modularml/mojo/pull/2262)) + +- `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` + instead of raising an error. + ([PR #2225](https://github.com/modularml/mojo/pull/2225)) + +- `SIMD` now implements `__rmod__`. + ([PR #2186](https://github.com/modularml/mojo/pull/2186), + Fixes [#1482](https://github.com/modularml/mojo/issues/1482)) + +- `bool(None)` is now implemented. + ([PR #2249](https://github.com/modularml/mojo/pull/2249)) + +- The `DTypePointer` type now implements `gather` for gathering a `SIMD` + vector from offsets of a current pointer. Similarly, support for `scatter` + was added to scatter a `SIMD` vector into offsets of the current pointer. + ([PR #2268](https://github.com/modularml/mojo/pull/2268)) + +- The `len` function for unary `range` with negative end values has been fixed + so that things like `len(range(-1))` work correctly now. + ([PR #2204](https://github.com/modularml/mojo/pull/2204)) -- Mojo now allows types to opt in to use the `abs()` function by implementing - the `__abs__()` method, defined by the new `Absable`: +- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to + the underlying memory representation as a `!lit.ref` value without checking + initialization status of the underlying value. This is useful in very + low-level logic but isn't designed for general usability and will likely + change in the future. + +- The `testing.assert_equal[SIMD]()` now raises if any of the elements + mismatch in the two `SIMD` arguments being compared. + ([PR #2279](https://github.com/modularml/mojo/pull/2279)) + +- The `testing.assert_almost_equal` and `math.isclose` functions now have an + `equal_nan` flag. When set to True, then NaNs are considered equal. + +- Mojo now supports declaring functions that have both optional and variadic + arguments, both positional and keyword-only. E.g. this now works: ```mojo - from math import sqrt + fn variadic_arg_after_default( + a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int + ): ... + ``` - struct Point(Absable): - var x: Float64 - var y: Float64 + Positional variadic parameters also work in the presence of optional + parameters, i.e.: - fn __abs__(self) -> Self: - return sqrt(self.x * self.x + self.y * self.y) + ```mojo + fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): + pass ``` -- The `abs()` function has also moved from `math` to `builtin`, so you no longer - need to do `from math import abs`. + Note that variadic keyword parameters are not supported yet. -- Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in - the `math` module by implementing the `__floor__()` and `__ceil__()` methods - (and so conforming to the new `math.Floorable` and `math.Ceilable` traits, - respectively). For example: +- The `__getitem__`/`__getattr__` and related methods can now take indices as + parameter values instead of argument values. This is enabled when defining + these as taking no arguments other than 'self' and the set value in a setter. + This enables types that can only be subscript into with parameters, as well + as things like: ```mojo - from math import Ceilable, Floorable, ceil, floor + struct RGB: + fn __getattr__[name: StringLiteral](self) -> Int: + @parameter + if name == "r": return ... + elif name == "g": return ... + else: + constrained[name == "b", "can only access with r, g, or b members"]() + return ... + ``` + +- Added `reversed()` for creating reversed iterators. Several range types, + `List`, and `Dict` now support iterating in reverse. + ([PR #2215](https://github.com/modularml/mojo/pull/2215), + [PR #2327](https://github.com/modularml/mojo/pull/2327), + Contributes towards [#2325](https://github.com/modularml/mojo/issues/2325)) + +- `object` now supports the division, modulo, and left and right shift + operators. + ([PR #2230](https://github.com/modularml/mojo/pull/2230), + [PR #2247](https://github.com/modularml/mojo/pull/2247), + Fixes [#2224](https://github.com/modularml/mojo/issues/2224)) + + The following operator dunder methods were added: - @value - struct Complex(Ceilable, Floorable): - var re: Float64 - var im: Float64 + - `object.__mod__` + - `object.__truediv__` + - `object.__floordiv__` + - `object.__lshift__` + - `object.__rshift__` - fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) + As well as the in-place and reverse variants. - fn __floor__(self) -> Self: - return Self(floor(re), floor(im)) +- Added checked arithmetic operations. + ([PR #2138](https://github.com/modularml/mojo/pull/2138)) + + SIMD integral types (including the sized integral scalars like `Int64`) can + now perform checked additions, substractions, and multiplications using the + following new methods: + + - `SIMD.add_with_overflow` + - `SIMD.sub_with_overflow` + - `SIMD.mul_with_overflow` + + Checked arithimetic allows the caller to determine if an operation exceeded + the numeric limits of the type. + +- Added `os.remove()` and `os.unlink()` for deleting files. + ([PR #2310](https://github.com/modularml/mojo/pull/2310), + Fixes [#2306](https://github.com/modularml/mojo/issues/2306)) + +- Properties can now be specified on inline mlir ops: + + ```mojo + _ = __mlir_op.`kgen.source_loc`[ + _type = ( + __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` + ), + _properties = __mlir_attr.`{inlineCount = 1 : i64}`, + ]() ``` + As the example shows above, the protected `_properties` attribute can be + passed during op construction, with an MLIR `DictionaryAttr` value. + +- Mojo now allows users to capture source location of code and call location of + functions dynamically. For example: + + ```mojo + @always_inline + fn my_assert(cond: Bool, msg: String): + if not cond: + var call_loc = __call_location() + print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) + + fn main(): + my_assert(False, "always fails") # some_file.mojo, line 193 + ``` + + will print `In /path/to/some_file.mojo on line 193: always fails`. Note that + `__call_location` only works in `@always_inline("nodebug")` and + `@always_inline` functions, as well as limitations on its use in parameter + contexts (see the documentation for more details). + +- `debug_assert` now prints its location (filename, line, and column where it + was called) in its error message. Similarly, the `assert_*` helpers in the + `testing` module now include location information in their messages. + ### 🦋 Changed +- The behavior of `mojo build` when invoked without an output `-o` argument has + changed slightly: `mojo build ./test-dir/program.mojo` now outputs an + executable to the path `./program`, whereas before it would output to the path + `./test-dir/program`. + +- The REPL no longer allows type level variable declarations to be + uninitialized, e.g. it will reject `var s: String`. This is because it does + not do proper lifetime tracking (yet!) across cells, and so such code would + lead to a crash. You can work around this by initializing to a dummy value + and overwriting later. This limitation only applies to top level variables, + variables in functions work as they always have. + +- `AnyPointer` got renamed to `UnsafePointer` and is now Mojo's preferred unsafe + pointer type. It has several enhancements, including: + 1) The element type can now be `AnyType`: it doesn't require `Movable`. + 2) Because of this, the `take_value`, `emplace_value`, and `move_into` methods + have been changed to be top-level functions, and were renamed to + `move_from_pointee`, `initialize_pointee_*` and `move_pointee` respectively. + 3) A new `destroy_pointee` function runs the destructor on the pointee. + 4) `UnsafePointer` can be initialized directly from a `Reference` with + `UnsafePointer(someRef)` and can convert to an immortal reference with + `yourPointer[]`. Both infer element type and address space. + +- All of the pointers got a pass of cleanup to make them more consistent, for + example the `unsafe.bitcast` global function is now a consistent `bitcast` + method on the pointers, which can convert element type and address space. + +- The `Reference` type has several changes, including: + 1) It is now located in `memory.reference` instead of `memory.unsafe`. + 2) `Reference` now has an unsafe `unsafe_bitcast` method like `UnsafePointer`. + 3) Several unsafe methods were removed, including `offset`, + `destroy_element_unsafe` and `emplace_ref_unsafe`. This is because + `Reference` is a safe type - use `UnsafePointer` to do unsafe operations. + +- The `mojo package` command no longer supports the `-D` flag. All compilation + environment flags should be provided at the point of package use (e.g. + `mojo run` or `mojo build`). + +- `parallel_memcpy` function has moved from the `buffer` package to the + `algorithm` package. Please update your imports accordingly. + +- `FileHandle.seek()` now has a whence argument that defaults to `os.SEEK_SET` + to seek from the beginning of the file. You can now set to `os.SEEK_CUR` to + offset by the current `FileHandle` seek position: + + ```mojo + var f = open("/tmp/example.txt") + # Skip 32 bytes + f.seek(os.SEEK_CUR, 32) + ``` + + Or `os.SEEK_END` to offset from the end of file: + + ```mojo + # Start from 32 bytes before the end of the file + f.seek(os.SEEK_END, -32) + ``` + + - `FileHandle.read()` can now read straight into a `DTypePointer`: + + ```mojo + var file = open("/tmp/example.txt", "r") + + # Allocate and load 8 elements + var ptr = DTypePointer[DType.float32].alloc(8) + var bytes = file.read(ptr, 8) + print("bytes read", bytes) + print(ptr.load[width=8]()) + ``` + +- `Optional.value()` will now return a reference instead of a copy of the + contained value. + ([PR #2226](https://github.com/modularml/mojo/pull/2226), + Fixes [#2179](https://github.com/modularml/mojo/issues/2179)) + + To perform a copy manually, dereference the result: + + ```mojo + var result = Optional(123) + + var value = result.value()[] + ``` + +- Per the accepted community proposal + [`proposals/byte-as-uint8.md`](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), + began transition to using `UInt8` by changing the data pointer of `Error` + to `DTypePointer[DType.uint8]`. + ([PR #2318](https://github.com/modularml/mojo/pull/2318), + Contributes towards [#2317](https://github.com/modularml/mojo/issues/2317)) + +- Continued transition to `UnsafePointer` away from the legacy `Pointer` type + in various standard library APIs and internals. + ([PR #2365](https://github.com/modularml/mojo/pull/2365), + [PR #2367](https://github.com/modularml/mojo/pull/2367), + [PR #2368](https://github.com/modularml/mojo/pull/2368), + [PR #2370](https://github.com/modularml/mojo/pull/2370), + [PR #2371](https://github.com/modularml/mojo/pull/2371)) + +- `Bool` can now be implicitly converted from any type conforming to `Boolable`. + This means that you no longer need to write things like this: + + ```mojo + @value + struct MyBoolable: + fn __bool__(self) -> Bool: ... + + fn takes_boolable[T: Boolable](cond: T): ... + + takes_boolable(MyBoolable()) + ``` + + Instead, you can simply have + + ```mojo + fn takes_bool(cond: Bool): ... + + takes_bool(MyBoolable()) + ``` + + Note that calls to `takes_bool` will perform the implicit conversion, so in + some cases is it still better to explicitly declare type parameter, e.g.: + + ```mojo + fn takes_two_boolables[T: Boolable](a: T, b: T): + # Short circuit means `b.__bool__()` might not be evaluated. + if a.__bool__() and b.__bool__(): + ... + ``` + ### ❌ Removed -- The method `object.print()` has been removed. Since now, `object` has the - `Stringable` trait, you can use `print(my_object)` instead. +- Support for "register only" variadic packs has been removed. Instead of + `AnyRegType`, please upgrade your code to `AnyType` in examples like this: + + ```mojo + fn your_function[*Types: AnyRegType](*args: *Ts): ... + ``` + + This move gives you access to nicer API and has the benefit of being memory + safe and correct for non-trivial types. If you need specific APIs on the + types, please use the correct trait bound instead of `AnyType`. + +- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults + to popping the last element in the list. + +- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. + +- The `__get_lvalue_as_address(x)` magic function has been removed. To get a + reference to a value use `Reference(x)` and if you need an unsafe pointer, you + can use `UnsafePointer.address_of(x)`. ### 🛠️ Fixed + +- [#516](https://github.com/modularml/mojo/issues/516) and + [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. + "Can't create a function that returns two strings" + +- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5) + +- [#1609](https://github.com/modularml/mojo/issues/1609) alias with + `DynamicVector[Tuple[Int]]` fails. + +- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` + in a Mojo package is an error, for now. This is not intended to work yet, + erroring for now will help to prevent accidental undefined behavior. + +- [#1215](https://github.com/modularml/mojo/issues/1215) and + [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no + longer cuts off hover previews for functions with functional arguments, + parameters, or results. + +- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and + documentation generation handling of inout arguments. + +- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer + crashes the Mojo parser. + +- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac + has been fixed. + +- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadics don't + work with non-trivial register-only types. + +- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed + and formatted correctly by `mojo format`. + +- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to + `@value` decorator and structs with empty body. + +- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after + syntax error during tuple creation + +- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now + properly supports signature types with named arguments and parameters. + +- [#2007](https://github.com/modularml/mojo/issues/2007) and + [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer + crashes on certain types of closures. + +- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` + decorator fails gracefully after duplicate field error. + +- [#2068](https://github.com/modularml/mojo/issues/2068) + Fix simd.reduce for size_out == 2 + ([PR #2102](https://github.com/modularml/mojo/pull/2102)) diff --git a/docs/faq.md b/docs/faq.md index c515ca8c7a..11a21e5fab 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -275,7 +275,7 @@ about our kernel performance in our [matrix multiplication blog post](https://www.modular.com/blog/the-worlds-fastest-unified-matrix-multiplication). For details about our end-to-end model performance relative to the latest releases of TensorFlow and PyTorch, check out our [performance -dashboard](https://www.modular.com/max/performance). +dashboard](https://performance.modular.com). ## Mojo SDK @@ -346,37 +346,27 @@ containers to enable remote development in Mojo. ### Does the Mojo SDK collect telemetry? -Yes, the Mojo SDK collects some basic system information, basic -compiler/runtime events, and crash reports that enable us to identify, analyze, -and prioritize Mojo issues. +Yes, in combination with the Modular CLI tool, the Mojo SDK collects some basic +system information and crash reports that enable us to identify, analyze, and +prioritize Mojo issues. -This telemetry is crucial to help us quickly identify problems and improve our -products. Without this telemetry, we would have to rely on user-submitted bug -reports, and in our decades of experience building developer products, we know -that most people don’t do that. The telemetry provides us the insights we need -to build better products for you. +Mojo is still in its early days, and this telemetry is crucial to help us +quickly identify problems and improve Mojo. Without this telemetry, we would +have to rely on user-submitted bug reports, and in our decades of building +developer products, we know that most people don’t bother. Plus, a lot of +product issues are not easily identified by users or quantifiable with +individual bug reports. The telemetry provides us the insights we need to build +Mojo into a premier developer product. -You can opt-out of the crash report and compiler/runtime telemetry, but -package install/update/uninstall events cannot be -disabled (see the [MAX SDK terms](https://www.modular.com/legal/max)). +Of course, if you don't want to share this information with us, you can easily +opt-out of all telemetry, using the [`modular` CLI](/cli). To stop sharing +system information, run this: -To disable crash reports, use this command: +`modular config-set telemetry.enabled=false` -```sh -modular config-set crash_reporting.enabled=false -``` +To stop sharing crash reports, run this: -To reduce other telemetry to only the required telemetry events, use this -command: - -```sh -modular config-set telemetry.level=0 -``` - -There are 3 telemetry levels: `0` currently records nothing (unless you're also -using MAX, which records hardware information and session durations); `1` -records high-level events such as when the compiler is invoked; and `2` records -more detail such as the time spend compiling. +`modular config-set crash_reporting.enabled=false` ## Versioning & compatibility diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 52922ab672..77d965493d 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -75,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -221,7 +221,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -239,11 +239,7 @@ "metadata": {}, "source": [ "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout).\n", - "\n", - "Any optional arguments must appear after any required arguments. [Keyword-only\n", - "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", - "also be either required or optional." + "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout)." ] }, { @@ -259,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -285,14 +281,14 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "fn sum(*values: Int) -> Int:\n", " var sum: Int = 0\n", " for value in values:\n", - " sum = sum + value\n", + " sum = sum+value\n", " return sum" ] }, @@ -309,75 +305,22 @@ "specified by keyword (see \n", "[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)).\n", "\n", - "Variadic arguments can be divided into two categories:\n", - "\n", - "- Homogeneous variadic arguments, where all of the passed arguments are the same\n", - " type—all `Int`, or all `String`, for example. \n", - "- Heterogeneous variadic arguments, which can accept a set of different argument\n", - " types.\n", - "\n", - "The following sections describe how to work with homogeneous and heterogenous\n", - "variadic arguments.\n", - "\n", - ":::note Variadic parameters\n", - "\n", - "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", - "(parameters are used for compile-time metaprogramming). Variadic parameters\n", - "are supported, but with some limitations—for details see \n", - "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", - "\n", - ":::\n", + "Currently variadic arguments must be a single type—all `Int`, or all `String`,\n", + "for example. A few standard library APIs, such as\n", + "[`print()`](/mojo/stdlib/builtin/io/print), support mixed-type, or\n", + "heterogeneous, variadic arguments, but this currently requires working with\n", + "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", + "Mojo in the future.\n", "\n", - "\n", - "### Homogeneous variadic arguments\n", - "\n", - "When defining a homogeneous variadic argument, use \n", - "*argument_name: argument_type:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def greet(*names: String):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Inside the function body, the variadic argument is available as an iterable list\n", - "for ease of use. Current there are some differences in handling the list \n", - "depending on whether the arguments are register-passable types (such as `Int`)\n", - "or memory-only types (such as `String`). TODO: We hope to remove these\n", - "differences in the future.\n", + "Inside the function body, the variadic argument is available an iterable list\n", + "for ease of use. But there are some differences in handling the list depending\n", + "on whether the arguments are register-passable types (such as `Int`) or\n", + "memory-only types (such as `String`).\n", "\n", "Register-passable types, such as `Int`, are available as a \n", "[`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", - "loop." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn sum(*values: Int) -> Int:\n", - " var sum: Int = 0\n", - " for value in values:\n", - " sum = sum+value\n", - " return sum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "loop.\n", "\n", "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem).\n", @@ -389,11 +332,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "def make_worldly(inout *strs: String):\n", + "fn make_worldly(inout *strs: String):\n", " # Requires extra [] to dereference the reference for now.\n", " for i in strs:\n", " i[] += \" world\"\n" @@ -419,129 +362,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Heterogeneous variadic arguments\n", - "\n", - "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", - "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", - "types requires [traits](/mojo/manual/traits) and \n", - "[parameters](/mojo/manual/parameters/). So the syntax may look a little\n", - "unfamiliar if you haven't worked with those features. The signature for a\n", - "function with a heterogeneous variadic argument looks like this:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```mojo\n", - "def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):\n", - " ...\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an\n", - "`ArgTypes` parameter, which is a list of types, all of which conform to the \n", - "[`Intable`](/mojo/stdlib/builtin/int/Intable) trait. The argument list, \n", - "`(*args: *ArgTypes)` has the familiar `*args` for the variadic argument, but \n", - "instead of a single type, its type is defined as _list_ of types, `*ArgTypes`.\n", - "\n", - "This means that each argument in `args` has a corresponding type in `ArgTypes`, \n", - "so args[n] is of type \n", - "ArgTypes[n].\n", - "\n", - "Inside the function, `args` is available as a\n", - "[`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack). The easiest\n", - "way to work with the arguments is to use the `each()` method to iterate through\n", - "the `VariadicPack`:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "28\n" - ] - } - ], - "source": [ - "fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:\n", - " var total = 0\n", - "\n", - " @parameter\n", - " fn add[Type: Intable](value: Type):\n", - " total += int(value)\n", - "\n", - " args.each[add]()\n", - " return total\n", - "\n", - "print(count_many_things(5, 11.7, 12))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the example above, the `add()` function is called for each argument in turn,\n", - "with the appropriate `value` and `Type` values. For instance, `add()` is first\n", - "called with `value=5` and `Type=Int`, then with `value=11.7` and `Type=Float64`.\n", - "\n", - "Also, note that when calling `count_many_things()`, you don't actually pass in\n", - "a list of argument types. You only need to pass in the arguments, and Mojo\n", - "generates the `ArgTypes` list itself.\n", - "\n", - "As a small optimization, if your function is likely to be called with a single\n", - "argument frequently, you can define your function with a single argument\n", - "followed by a variadic argument. This lets the simple case bypass populating and\n", - "iterating through the `VariadicPack`.\n", - "\n", - "For example, given a `print_string()` function that prints a single string, you\n", - "could re-implement the variadic `print()` function with code like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Bob0\n" - ] - } - ], - "source": [ - " fn print_string(s: String):\n", - " print(s, end=\"\")\n", + ":::note Variadic parameters\n", "\n", - " fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):\n", - " print_string(str(first))\n", + "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", + "(parameters are used for compile-time metaprogramming). However, most rules\n", + "that apply to argument lists also apply to parameter lists. Variadic parameters\n", + "are supported, but with some limitations—for details see \n", + "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", "\n", - " @parameter\n", - " fn print_elt[T: Stringable](a: T):\n", - " print_string(\" \")\n", - " print_string(a)\n", - " rest.each[print_elt]()\n", - " print_many(\"Bob\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you call `print_many()` with a single argument, it calls `print_string()`\n", - "directly. The `VariadicPack` is empty, so `each()` returns immediately without\n", - "calling the `print_elt()` function." + ":::\n" ] }, { @@ -568,10 +397,8 @@ "\n", " In this example, the argument name `kwargs` is a placeholder that accepts any\n", " number of keyword arguments. Inside the body of the function, you can access\n", - " the arguments as a dictionary of keywords and argument values (specifically,\n", - " an instance of\n", - " [`OwnedKwargsDict`](/mojo/stdlib/collections/dict/OwnedKwargsDict)).\n", - " \n", + " the arguments as a [`Dict`](/mojo/stdlib/collections/dict) of keywords and\n", + " argument values.\n", " \n", " There are currently a few limitations:\n", "\n", @@ -588,12 +415,18 @@ " - All the variadic keyword arguments must have the same type, and this\n", " determines the type of the argument dictionary. For example, if the argument\n", " is `**kwargs: Float64` then the argument dictionary will be a \n", - " `OwnedKwargsDict[Float64]`.\n", + " `Dict[String, Float64]`.\n", + "\n", + " - Functions with variadic keyword arguments can't have default values for\n", + " keyword-only arguments. For example:\n", "\n", - " - The argument type must conform to the \n", - " [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", - " That is, the type must be both [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", - " and [`Copyable`](/mojo/stdlib/builtin/value/Copyable).\n", + " ```mojo\n", + " # Not allowed yet, because `b` is keyword-only with a default.\n", + " fn not_yet(*, b: Int = 9, **kwargs: Int): ...\n", + "\n", + " # Okay, because `c` is positional-or-keyword, so it can have a default.\n", + " fn still_works(c: Int = 5, **kwargs: Int): ...\n", + " ```\n", "\n", " - Dictionary unpacking is not supported yet:\n", "\n", @@ -696,18 +529,7 @@ "Keyword-only arguments often have default values, but this is not required. If a\n", "keyword-only argument doesn't have a default value, it is a _required \n", "keyword-only argument_. It must be specified, and it must be specified by \n", - "keyword. \n", - "\n", - "Any required keyword-only arguments must appear in the signature before\n", - "any optional keyword-only arguments. That is, arguments appear in the following\n", - "sequence a function signature:\n", - "\n", - "* Required positional arguments.\n", - "* Optional positional arguments.\n", - "* Variadic arguments.\n", - "* Required keyword-only arguments.\n", - "* Optional keyword-only arguments.\n", - "* Variadic keyword arguments.\n", + "keyword.\n", "\n", "For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only\n", "Arguments](https://peps.python.org/pep-3102/)." diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 572c8b8e19..0f226917f7 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -84,24 +84,19 @@ If you already have the `modular` tool, curl -s https://get.modular.com | sh - ``` -2. Then install the Mojo SDK: +2. Then sign in to your Modular account with this command: ```sh - modular install mojo + modular auth ``` - :::note Get nightlies - - If you want the bleeding-edge (less stable) version, instead install the - nightly build: +3. Now you can install the Mojo SDK: ```sh - modular install nightly/mojo + modular install mojo ``` - ::: - -3. Set environment variables so you can access the +4. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index c2d67ed01f..01885c9128 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -198,8 +198,8 @@ "metadata": {}, "source": [ "There's no need to define the `__del__()` destructor for this, because it's a\n", - "simple collection of other types (`String` and `Int`), and it doesn't \n", - "dynamically allocate memory. \n", + "simple collection of other types (`String` and `Int`), and it doesn't dynamically\n", + "allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" @@ -278,11 +278,8 @@ "\n", ":::\n", "\n", - "For instances of the \n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type, use\n", - "the [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", - "function instead of the discard pattern. For example, if the previous example\n", - "used `UnsafePointer`, the `__del__()` method would look like this:\n", + "If `self.data` was an instance of `AnyPointer`, you'd need to use slightly\n", + "different code:\n", "\n", "```mojo\n", "fn __del__(owned self):\n", diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 463005cad7..2005e81c02 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -654,14 +654,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Variadic parameters currently have some limitations that variadic arguments don't have:\n", + "Variadic parameters have some limitations that variadic arguments don't have:\n", "\n", - "- Variadic parameters must be homogeneous—that is, all the values must be the\n", - " same type. \n", - " \n", - "- The parameter type must be register-passable.\n", + "- Only variadic parameters of register-passable types are supported currently.\n", "\n", - "- The parameter values aren't automatically projected into a `VariadicList`, so you\n", + "- The parameters aren't automatically projected into a `VariadicList`, so you\n", " need to construct the list explicitly:" ] }, diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb deleted file mode 100644 index 4b03fe549f..0000000000 --- a/docs/manual/types.ipynb +++ /dev/null @@ -1,1002 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Types\n", - "sidebar_position: 1\n", - "description: Standard Mojo data types.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All values in Mojo have an associated data type. Most of the types are\n", - "_nominal_ types, defined by a [`struct`](/mojo/manual/structs). These types are\n", - "nominal (or \"named\") because type equality is determined by the type's _name_,\n", - "not its _structure_.\n", - "\n", - "There are a some types that aren't defined as structs:\n", - "\n", - "* Functions are typed based on their signatures.\n", - "* `NoneType` is a type with one instance, the `None` object, which is used to\n", - " signal \"no value.\"\n", - "\n", - "Mojo comes with a standard library that provides a number of useful types and\n", - "utility functions. These standard types aren’t privileged. Each of the standard\n", - "library types is defined just like user-defined types—even basic types like \n", - "[`Int`](/mojo/stdlib/builtin/int/Int) and \n", - "[`String`](/mojo/stdlib/builtin/string/String). But these standard library types\n", - "are the building blocks you'll use for most Mojo programs.\n", - "\n", - "The most common types are _built-in types_, which are always available and\n", - "don't need to be imported. These include types for numeric values, strings,\n", - "boolean values, and others.\n", - "\n", - "The standard library also includes many more types that you can import as\n", - "needed, including collection types, utilities for interacting with the\n", - "filesystem and getting system information, and so on.\n", - "\n", - "## Numeric types\n", - "\n", - "Mojo's most basic numeric type is `Int`, which represents a signed integer of\n", - "the largest size supported by the system—typically 64 bits or 32 bits.\n", - "\n", - "Mojo also has built-in types for integer and floating-point values of various\n", - "precisions:\n", - "\n", - "
\n", - "\n", - "| Type name | Description |\n", - "|---------------|--------------|\n", - "| `Int8` | 8-bit signed integer |\n", - "| `UInt8` | 8-bit unsigned integer |\n", - "| `Int16` | 16-bit signed integer |\n", - "| `UInt16` | 16-bit unsigned integer |\n", - "| `Int32` | 32-bit signed integer |\n", - "| `UInt32` | 32-bit unsigned integer |\n", - "| `Int64` | 64-bit signed integer |\n", - "| `UInt64` | 64-bit unsigned integer |\n", - "| `Float16` | 16-bit floating point number (IEEE 754-2008 binary16) |\n", - "| `Float32` | 32-bit floating point number (IEEE 754-2008 binary32) |\n", - "| `Float64` | 64-bit floating point number (IEEE 754-2008 binary64) |\n", - "
Table 1. Numeric types with specific precision
\n", - "
\n", - "\n", - "The types in Table 1 are actually all aliases to a single type, \n", - "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD), which is discussed later.\n", - "\n", - "All of the numeric types support the usual numeric and bitwise operators. The \n", - "[`math`](/mojo/stdlib/math/) module provides a number of additional math\n", - "functions.\n", - "\n", - "You may wonder when to use `Int` and when to use the other integer \n", - "types. In general, `Int` is good safe default when you need an integer type and\n", - "you don't require a specific bit width. Using `Int` as the default integer type\n", - "for APIs makes APIs more consistent and predictable.\n", - "\n", - "\n", - "### Floating-point numbers\n", - "\n", - "Floating-point types represent real numbers. Because not all real numbers can be\n", - "expressed in a finite number of bits, floating-point numbers can't represent\n", - "every value exactly.\n", - "\n", - "The floating-point types listed in Table 1—`Float64`, `Float32`, and \n", - "`Float16`—follow the IEEE 754-2008 standard for representing floating-point \n", - "values. Each type includes a sign bit, one set of bits representing an exponent,\n", - "and another set representing the fraction or mantissa. Table 2 shows how each of \n", - "these types are represented in memory.\n", - "\n", - "
\n", - "\n", - "| Type name | Sign | Exponent | Mantissa |\n", - "|------------|-------|-----------|----------|\n", - "| `Float64` | 1 bit | 11 bits | 52 bits |\n", - "| `Float32` | 1 bit | 8 bits | 23 bits |\n", - "| `Float16` | 1 bit | 5 bits | 10 bits |\n", - "
Table 2. Details of floating-point types
\n", - "
\n", - "\n", - "Numbers with exponent values of all ones or all zeros represent special values,\n", - "allowing floating-point numbers to represent infinity, negative infinity,\n", - "signed zeros, and not-a-number (NaN). For more details on how numbers are\n", - "represented, see [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) on\n", - "Wikipedia.\n", - "\n", - "A few things to note with floating-point values:\n", - "\n", - "- Rounding errors. Rounding may produce unexpected results. For example, 1/3\n", - " can't be represented exactly in these floating-point formats. The more\n", - " operations you perform with floating-point numbers, the more the rounding\n", - " errors accumulate. \n", - "\n", - "- Space between consecutive numbers. The space between consecutive numbers is\n", - " variable across the range of a floating-point number format. For numbers close\n", - " to zero, the distance between consecutive numbers is very small. is For large\n", - " positive and negative numbers, the space between consecutive numbers is\n", - " greater than 1, so it may not be possible to represent consecutive integers.\n", - "\n", - "Because the values are approximate, it is rarely useful to compare them with\n", - "the equality operator (`==`). Consider the following example:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - } - ], - "source": [ - "var big_num = 1.0e16\n", - "var bigger_num = big_num+1.0\n", - "print(big_num == bigger_num)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Comparison operators (`<` `>=` and so on) work with floating point numbers. You\n", - "can also use the [`math.isclose()`](/mojo/stdlib/math/math/isclose) function to\n", - "compare whether two floating-point numbers are equal within a specified\n", - "tolerance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Numeric literals\n", - "\n", - "In addition to these numeric types, the standard libraries provides integer and\n", - "floating-point literal types, \n", - "[`IntLiteral`](/mojo/stdlib/builtin/int_literal/IntLiteral) and \n", - "[`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral).\n", - "\n", - "These literal types are used at compile time to represent literal numbers that\n", - "appear in the code. In general, you should never instantiate these types\n", - "yourself.\n", - "\n", - "Table 3 summarizes the literal formats you can use to represent numbers.\n", - "\n", - "
\n", - "\n", - "| Format | Examples | Notes |\n", - "|--------|---------|-------|\n", - "| Integer literal | `1760` | Integer literal, in decimal format. |\n", - "| Hexadecimal literal | `0xaa`, `0xFF` | Integer literal, in hexadecimal format.
Hex digits are case-insensitive. |\n", - "| Octal literal | `0o77` | Integer literal, in octal format. |\n", - "| Binary literal | `0b0111` | Integer literal, in binary format. |\n", - "| Floating-point literal | `3.14`, `1.2e9` | Floating-point literal.
Must include the decimal point to be interpreted as floating-point. |\n", - "
Table 3. Numeric literal formats
\n", - "
\n", - "\n", - "At compile time, the literal types are arbitrary-precision (also called \n", - "infinite-precision) values, so the compiler can perform compile-time \n", - "calculations without overflow or rounding errors.\n", - "\n", - "At runtime the values are converted to finite-precision types—`Int` for\n", - "integer values, and `Float64` for floating-point values. (This process of \n", - "converting a value that can only exist at compile time into a runtime value is\n", - "called _materialization_.) \n", - "\n", - "The following code sample shows the difference between an arbitrary-precision \n", - "calculation and the same calculation done using `Float64` values at runtime,\n", - "which suffers from rounding errors." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0 0.99999999999999978\n" - ] - } - ], - "source": [ - "var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0)\n", - "# use a variable to force the following calculation to occur at runtime\n", - "var three = 3.0\n", - "var finite_precision = three * (4.0 / three - 1.0)\n", - "print(arbitrary_precision, finite_precision)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### `SIMD` and `DType`\n", - "\n", - "To support high-performance numeric processing, Mojo uses the\n", - "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type as the basis for its numeric\n", - "types. SIMD (single instruction, multiple data) is a processor technology that\n", - "allows you to perform an operation on an entire set of operands at once. Mojo's\n", - "`SIMD` type abstracts SIMD operations. A `SIMD` value represents a SIMD\n", - "_vector_—that is, a fixed-size array of values that can fit into a processor's\n", - "register. SIMD vectors are defined by two\n", - "[_parameters_](/mojo/manual/parameters/):\n", - "\n", - "- A `DType` value, defining the data type in the vector (for example, \n", - " 32-bit floating-point numbers).\n", - "- The number of elements in the vector, which must be a power of two.\n", - "\n", - "For example, you can define a vector of four `Float32` values like this:" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Math operations on SIMD values are \n", - "applied _elementwise_, on each individual element in the vector. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2, 6, 15, 28]\n" - ] - } - ], - "source": [ - "var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7)\n", - "var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4)\n", - "var product = vec1 * vec2\n", - "print(product)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "### Scalar values\n", - "\n", - "The `SIMD` module defines several _type aliases_ that are shorthand for\n", - "different types of `SIMD` vectors. In particular, the `Scalar` type is just a\n", - "`SIMD` vector with a single element. The numeric types listed in \n", - "[Table 1](#table-1), like `Int8` and `Float32` are actually type aliases for\n", - "different types of scalar values:\n", - "\n", - "```mojo\n", - "alias Scalar = SIMD[size=1]\n", - "alias Int8 = Scalar[DType.int8]\n", - "alias Float32 = Scalar[DType.float32]\n", - "```\n", - "\n", - "This may seem a little confusing at first, but it means that whether you're \n", - "working with a single `Float32` value or a vector of float32 values,\n", - "the math operations go through exactly the same code path." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### The `DType` type\n", - "\n", - "The `DType` struct describes the different data types that a `SIMD` vector can\n", - "hold, and defines a number of utility functions for operating on those data\n", - "types. The `DType` struct defines a set of aliases that act as identifiers for\n", - "the different data types, like `DType.int8` and `DType.float32`. You use\n", - "these aliases when declaring a `SIMD` vector:\n", - "\n", - "```mojo\n", - "var v: SIMD[DType.float64, 16]\n", - "```\n", - "\n", - "Note that `DType.float64` isn't a _type_, it's a value that describes a data\n", - "type. You can't create a variable with the type `DType.float64`. You can create\n", - "a variable with the type `SIMD[DType.float64, 1]` (or `Float64`, which is the\n", - "same thing).\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "float32 is floating point: True\n", - "float32 is integral: False\n", - "Min/max finite values for float32\n", - "-3.4028234663852886e+38 3.4028234663852886e+38\n" - ] - } - ], - "source": [ - "from math.limit import max_finite, min_finite\n", - "\n", - "def describeDType[dtype: DType]():\n", - " print(dtype, \"is floating point:\", dtype.is_floating_point())\n", - " print(dtype, \"is integral:\", dtype.is_integral())\n", - " print(\"Min/max finite values for\", dtype)\n", - " print(min_finite[dtype](), max_finite[dtype]())\n", - "\n", - "describeDType[DType.float32]()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "There are several other data types in the standard library that also use\n", - "the `DType` abstraction. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Strings\n", - "\n", - "Mojo's `String` type represents a mutable string. (For Python programmers, note\n", - "that this is different from Python's standard string, which is immutable.)\n", - "Strings support a variety of operators and common methods.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Testing Mojo strings\n" - ] - } - ], - "source": [ - "var s: String = \"Testing\"\n", - "s += \" Mojo strings\"\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Most standard library types conform to the \n", - "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", - "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", - "explicitly convert a value to string.\n", - "\n", - "When concatenating values to a string using the `+` operator, `Stringable`\n", - "will implicitly convert `String`:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Items in list: 5\n" - ] - } - ], - "source": [ - "var s = str(\"Items in list: \") + 5\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### String literals\n", - "\n", - "As with numeric types, the standard library includes a string literal type used\n", - "to represent literal strings in the program source. String literals are\n", - "enclosed in either single or double quotes.\n", - "\n", - "Adjacent literals are concatenated together, so you can define a long string\n", - "using a series of literals broken up over several lines:\n", - "\n", - "```\n", - "var s = \"A very long string which is \"\n", - " \"broken into two literals for legibility.\"\n", - "```\n", - "\n", - "To define a multi-line string, enclose the literal in three single or double\n", - "quotes:\n", - "\n", - "```\n", - "var s = \"\"\"\n", - "Multi-line string literals let you \n", - "enter long blocks of text, including \n", - "newlines.\"\"\"\n", - "```\n", - "\n", - "Note that the triple double quote form is also used for API documentation\n", - "strings.\n", - "\n", - "Unlike `IntLiteral` and `FloatLiteral`, `StringLiteral` doesn't automatically\n", - "materialize to a runtime type. In some cases, you may need to manually convert\n", - "`StringLiteral` values to `String` using the built-in \n", - "[`str()`](/mojo/stdlib/builtin/str/str) method. \n", - "\n", - "For example, when concatenating strings with other values" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "var a = 5\n", - "var b: String = \"String literals may not concatenate \"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Booleans\n", - "\n", - "Mojo's `Bool` type represents a boolean value. It can take one of two values, \n", - "`True` or `False`. You can negate a boolean value using the `not` operator." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "False True\n" - ] - } - ], - "source": [ - "var conditionA = False\n", - "var conditionB: Bool\n", - "conditionB = not conditionA\n", - "print(conditionA, conditionB)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Many types have a boolean representation. Any type that implements the \n", - "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait has a boolean \n", - "representation. As a general principle, collections evaluate as True if they \n", - "contain any elements, False if the are empty; strings evaluate as True if they\n", - "have a non-zero length." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Collection types\n", - "\n", - "The Mojo standard library also includes a set of basic collection types that\n", - "can be used to build more complex data structures:\n", - "\n", - "- [`List`](/mojo/stdlib/collections/list/List), a dynamically-sized array of \n", - " items.\n", - "- [`Dict`](/mojo/stdlib/collections/dict/Dict), an associative array of \n", - " key-value pairs.\n", - "- [`Set`](/mojo/stdlib/collections/set/Set), an unordered collection of unique\n", - " items.\n", - "- [`Optional`](/mojo/stdlib/collections/optional/Optional)\n", - " represents a value that may or may not be present. \n", - "\n", - "The collection types are _generic types_: while a given collection can only\n", - "hold a specific type of value (such as `Int` or `Float64`), you specify the\n", - "type at compile time using a [parameter]((/mojo/manual/parameters/)). For\n", - "example, you can create a `List` of `Int` values like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "var l = List[Int](1, 2, 3, 4)\n", - "# l.append(3.14) # error: FloatLiteral cannot be converted to Int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You don't always need to specify the type explicitly. If Mojo can _infer_ the\n", - "type, you can omit it. For example, when you construct a list from a set of \n", - "integer literals, Mojo creates a `List[Int]`." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Inferred type == Int\n", - "var l1 = List(1, 2, 3, 4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Where you need a more flexible collection, the \n", - "[`Variant`](/mojo/stdlib/utils/variant/Variant) type can hold different types \n", - "of values. For example, a `Variant[Int32, Float64]` can hold either an `Int32`\n", - "_or_ a `Float64` value at any given time. (Using `Variant` is not covered in\n", - "this section, see the [API docs](/mojo/stdlib/utils/variant/Variant) for more\n", - "information.)\n", - "\n", - "The following sections give brief introduction to the main collection types. \n", - "\n", - "### List\n", - "\n", - "[`List`](/mojo/stdlib/collections/list/List) is a dynamically-sized array of \n", - "elements. List elements need to conform to the \n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait, which\n", - "just means that the items must be copyable and movable. Most of the common\n", - "standard library primitives, like `Int`, `String`, and `SIMD` conform to this\n", - "trait. You can create a `List` by passing the element type as a parameter, like\n", - "this:\n", - "\n", - "\n", - "```mojo\n", - "var l = List[String]()\n", - "```\n", - "\n", - "The `List` type supports a subset of the Python `list` API, including the\n", - "ability to append to the list, pop items out of the list, and access list items\n", - "using subscript notation." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Popping last item from list: 11\n", - "2, 3, 5, 7, " - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "var list = List(2, 3, 5)\n", - "list.append(7)\n", - "list.append(11)\n", - "print(\"Popping last item from list: \", list.pop())\n", - "for idx in range(len(list)):\n", - " print(list[idx], end=\", \")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the previous code sample leaves out the type parameter when creating \n", - "the list. Because the list is being created with a set of `Int` values, Mojo can\n", - "_infer_ the type from the arguments. \n", - "\n", - "There are some notable limitations when using `List`:\n", - "\n", - "- You can't currently initialize a list from a list literal, like this:\n", - "\n", - " ```mojo\n", - " # Doesn't work!\n", - " var list: List[Int] = [2, 3, 5]\n", - "\n", - " But you can use variadic arguments to achieve the same thing:\n", - "\n", - " ```mojo\n", - " var list = List(2, 3, 5)\n", - " ```\n", - "\n", - "- You can't `print()` a list, or convert it directly into a string.\n", - "\n", - " ```mojo\n", - " # Does not work\n", - " print(list)\n", - " ```\n", - "\n", - " As shown above, you can print the individual elements in a list as long as\n", - " they're a [`Stringable`](/mojo/stdlib/builtin/str/Stringable) type.\n", - "\n", - "- Iterating a `List` currently returns a \n", - " [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", - " item itself. You can access the item using the dereference operator, `[]`:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2, 3, 4, " - ] - } - ], - "source": [ - "#: from collections import List\n", - "var list = List(2, 3, 4)\n", - "for item in list:\n", - " print(item[], end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - " Subscripting in to a list, however, returns the item directly—no need to \n", - " dereference:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2, 3, 4, " - ] - } - ], - "source": [ - "#: from collections import List\n", - "#: var list = List[Int](2, 3, 4)\n", - "for i in range(len(list)):\n", - " print(list[i], end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dict\n", - "\n", - "The [`Dict`](/mojo/stdlib/collections/dict/Dict) type is an associative array\n", - "that holds key-value pairs. You can create a `Dict` by specifying the key type\n", - "and value type as parameters, like this:\n", - "\n", - "```mojo\n", - "var values = Dict[String, Float64]()\n", - "```\n", - "\n", - "They dictionary's key type must conform to the \n", - "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, and value \n", - "elements must conform to the \n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", - "\n", - "You can insert and remove key-value pairs, update the value assigned to a key,\n", - "and iterate through keys, values, or items in the dictionary. \n", - "\n", - "The `Dict` iterators all yield references, so you need to use the dereference\n", - "operator `[]` as shown in the following example:" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "plasticity 3.1000000000000001\n", - "elasticity 1.3\n", - "electricity 9.6999999999999993\n" - ] - } - ], - "source": [ - "from collections import Dict\n", - "\n", - "var d = Dict[String, Float64]()\n", - "d[\"plasticity\"] = 3.1\n", - "d[\"elasticity\"] = 1.3\n", - "d[\"electricity\"] = 9.7\n", - "for item in d.items():\n", - " print(item[].key, item[].value)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Set\n", - "\n", - "The [`Set`](/mojo/stdlib/collections/set/Set) type represent a set of unique\n", - "values. You can add and remove elements from the set, test whether a value \n", - "exists in the set, and perform set algebra operations, like unions and \n", - "intersections between two sets. \n", - "\n", - "Sets are generic and the element type must conform to the\n", - "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "We both like:\n", - "- ice cream\n", - "- tacos\n" - ] - } - ], - "source": [ - "from collections import Set\n", - "\n", - "i_like = Set(\"sushi\", \"ice cream\", \"tacos\", \"pho\")\n", - "you_like = Set(\"burgers\", \"tacos\", \"salad\", \"ice cream\")\n", - "we_like = i_like.intersection(you_like)\n", - "\n", - "print(\"We both like:\")\n", - "for item in we_like:\n", - " print(\"-\", item[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optional\n", - "\n", - "The [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", - "value that may or may not be present. Like the other collectypetion types, it is\n", - "generic, and can hold any type that conforms to the\n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Two ways to initialize an Optional with a value\n", - "var opt1 = Optional(5)\n", - "var opt2: Optional[Int] = 5\n", - "# Two ways to initalize an Optional with no value\n", - "var opt3 = Optional[Int]()\n", - "var opt4: Optional[Int] = None" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "An `Optional` evaluates as `True` when it holds a value, `False` otherwise. If\n", - "the `Optional` holds a value, you can retrieve a reference to the value using \n", - "the `value()` method. But calling `value()` on an `Optional` with no value\n", - "results in undefined behavior, so you should always guard a call to `value()`\n", - "inside a conditional that checks whether a value exists." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Testing\n" - ] - } - ], - "source": [ - "var opt: Optional[String] = str(\"Testing\")\n", - "if opt:\n", - " var value_ref = opt.value()\n", - " print(value_ref[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Alternately, you can use the `or_else()` method, which returns the stored\n", - "value if there is one, or a user-specified default value otherwise:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello\n", - "Hi\n" - ] - } - ], - "source": [ - "var custom_greeting: Optional[String] = None\n", - "print(custom_greeting.or_else(\"Hello\"))\n", - "\n", - "custom_greeting = str(\"Hi\")\n", - "print(custom_greeting.or_else(\"Hello\"))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Register-passable, memory-only, and trivial types\n", - "\n", - "In various places in the documentation you'll see references to \n", - "register-passable, memory-only, and trivial types. Register-passable and \n", - "memory-only types are distinguished based on how they hold data:\n", - "\n", - "- Register-passable types are composed exclusively of fixed-size data types,\n", - " which can (theoretically) be stored in a machine register. A register-passable\n", - " type can include other types, as long as they are also register-passable.\n", - " `Int`, `Bool`, and `SIMD`, for example, are all register-passable types. So \n", - " a register-passable `struct` could include `Int` and `Bool` fields, but not a\n", - " `String` field. Register-passable types are declared with the \n", - " [`@register_passable`](/mojo/manual/decorators/register-passable) decorator.\n", - "\n", - " Register-passable types are always passed by value (that is, the values are\n", - " copied).\n", - "\n", - "- Memory-only types consist of any types that _don't_ fit the description of\n", - " register-passable types. In particular, these types usually use pointers or\n", - " references to manage heap-allocated memory. `String`, `List`, and `Dict` are\n", - " all examples of memory-only types.\n", - "\n", - "Our long-term goal is to make this distinction transparent to the user, and\n", - "ensure all APIs work with both register-passable and memory-only types.\n", - "But right now you will see some standard library types that only work with \n", - "register-passable types or only work with memory-only types.\n", - "\n", - "In addition to these two categories, Mojo also has \"trivial\" types. Conceptually\n", - "a trivial type is simply a type that doesn't require any custom logic in its\n", - "lifecycle methods. The bits that make up an instance of a trivial type can be\n", - "copied or moved without any knowledge of what they do. Currently, trivial types\n", - "are declared using the\n", - "[`@register_passable(trivial)`](/mojo/manual/decorators/register-passable#register_passabletrivial)\n", - "decorator. Trivial types shouldn't be limited to only register-passable types,\n", - "so in the future we intend to separate trivial types from the \n", - "`@register_passable` decorator." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `AnyType` and `AnyRegType`\n", - "\n", - "Two other things you'll see in Mojo APIs are references to `AnyType` and\n", - "`AnyRegType`. These are effectively _metatypes_, that is, types of types.\n", - "\n", - "- `AnyType` represents any Mojo type. Mojo treats `AnyType` as a special kind of\n", - " trait, and you'll find more discussion of it on the\n", - " [Traits page](/mojo/manual/traits#the-anytype-trait).\n", - "- `AnyRegType` is a metatype representing any Mojo type that's marked \n", - " register passable.\n", - "\n", - "You'll see them in signatures like this:\n", - "\n", - "```mojo\n", - "fn any_type_function[ValueType: AnyRegType](value: ValueType):\n", - " ...\n", - "```\n", - "\n", - "You can read this as `any_type_function` has an argument, `value` of type\n", - "`ValueType`,where `ValueType` is a register-passable type, determined at\n", - "compile time. \n", - "\n", - "There is still some code like this in the standard library, but it's gradually\n", - "being migrated to more generic code that doesn't distinguish between \n", - "register-passable and memory-only types.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/proposals/README.md b/proposals/README.md deleted file mode 100644 index 392782a030..0000000000 --- a/proposals/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Mojo🔥 engineering design proposals - -This directory contains ad-hoc design proposals put together by the Mojo -engineering team. These are meant to help shape discussion and refine the -design of various subsystems, but typically become obsolete and incorporated into -more canonical documentation when the implementation work concludes. There is -no attempt to keep these up-to-date as the language evolves, so they are -more for historical reference than as a user-guide for the language. diff --git a/proposals/auto-dereference.md b/proposals/auto-dereference.md deleted file mode 100644 index abfe44d95b..0000000000 --- a/proposals/auto-dereference.md +++ /dev/null @@ -1,277 +0,0 @@ -# Auto def-Reference for Mojo - -Chris Lattner, May 6, 2024 - -**TL;DR:** Add an ‘eager decay’ model for references to improve developer -ergonomics and work more like C++ References. Delete `__refitem__` as well. - -# Motivation - -The safe `Reference` type in Mojo is slowly making progress in terms of its -implementation and capabilities, but one of the major problems with it is -usability. This whitepaper tackles one specific part of the problem, that you -have to explicitly dereference references. - -The lack of automatic dereferencing leads to confusing user problems, e.g. when -iterating over a `List`, the iterator is returning references, so you have to -write: - -```mojo -for name in names: - print(name[]) -``` - -Yuck.🤮 Furthermore, other Mojo code end up being piles of punctuation: - -```mojo -fn offset_momentum(inout bodies: List[Planet]): - var p = SIMD[DType.float64, 4]() - - for body in bodies: - # What is this? - p += body[].velocity * body[].mass - - var body = bodies[0] - body.velocity = -p / SOLAR_MASS - - bodies[0] = body -``` - -Worse yet, we’re using subscript to mean two different things - in the case of a -reference, this is an explicit “dereference” operation that we want people to -think of as transparent. In the case of array (and pointers) we want an -explicit “index and subscript” operation, which is a “proper” use of subscript. - -Ok, so let’s eliminate the need for `foo[]` when `foo` is a `Reference`! - -# Background - -One very important thing to get out of the way: this document is **only** -talking about the `Reference` type, this is not talking about “things with -reference semantics”, and is definitely not changing `PythonObject` or anything -related to Python interop or compatibility. Python doesn’t have a notion like -`Reference`. - -### Current API of `Reference` - -For reference (haha, zing 🔥), the entire public API of `Reference` is currently: - -```mojo -@value -struct Reference[...]: - fn __init__(inout self, value: Self._mlir_type): - """Constructs a Reference from the MLIR reference.""" - - fn __refitem__(self) -> Self._mlir_type: - """Enable subscript syntax `ref[]` to access the element.""" - - fn __mlir_ref__(self) -> Self._mlir_type: - """Enable the Mojo compiler to see into `Reference`.""" - return self.value -``` - -It took quite a lot of work to get here, but these three methods are the key to -all the magic: - -1) The first is used by the compiler to form a `Reference` from an xValue. - -2) The second enables `foo[]` syntax - -3) The third allows the compiler to implement `__refitem__`. - -As you can tell, we don’t want typical users to interact directly with -`Reference`. It is something you can declare as part of a type signature, but -you don’t want (and shouldn’t have to) interact with it directly. - -For a point of intuition, compare `Reference` to a C++ reference like `int&`: -there is no API on the reference itself, it is just a part of the type system -that affects overloading and argument passing, but is fully transparent to -method resolution etc. - -### The difference between pointers and references - -It might be surprising to consider these two cases differently: why is `myPtr[]` -a good thing but `myRef[]` is not? Several reasons: - - 1) users generally expect and want references to be transparent. - - 2) pointers can be indexed with `myPtr[i]` : `myPtr[]` is just sugar for when - `i` is zero. - - 3) `Reference` is a safe type and “dereferencing” it is always safe and - correct, whereas `UnsafePointer` (and friends) are unsafe and dereferencing - should be explicit in source. - - 4) Dereferencing a `Reference` doesn’t actually do anything - it converts an - RValue of Reference type to an LValue or BValue in the compiler - it is only - a load or store to the resultant xValue that does something! - -### Not competing with `getattr` - -Mojo already has the ability to overload `x.y` syntax on a type both dynamically -and statically through `__getattr__` and `__setattr__`. This is a different -part of the design space here, because reference promotion needs to work even -when not accessing a member, e.g. in `var x = List(someRef)`. - -# Two possible designs - -There are two fundamental designs that we could take to make references -transparent, I call them the “*eager reference decay”* model and the -“*persistent reference*” model. Both models solve the most important problems -and allow these examples to work: - -```mojo -# Things common to both models. -fn show_commonality(a: Reference[String, _, _]): - # Gets the length of the string, not the reference. - var length = len(a) - - # call __add__ on the string, deref'ing the reference twice. - var doubled = a+a - - # Methods look through the reference. - var joined = a.join("x", "y") - - # r is Reference[String]: this copies the reference not string. - var r : Reference[...] = a - - # This gives a List of references in both models. - var reflist = List[Reference[...]](a, a, a) -``` - -Note the lack of any `a[]`’s in these examples! - -I think we should use the former, but this section outlines both approaches -and makes the case. - -## The “Eager Reference Decay” model - -One approach is to follow C++’s approach and make it so `Reference` decays to an -LValue or BValue of the underlying type. This decay specifically happens when -the `Reference` is produced as an RValue (e.g. as the result of a function call) -and during LValue to RValue conversion. This means that LValues of `Reference` -type are allowed, but RValue’s of `Reference` type will never be observable to a -Mojo programmer. - -Let’s look at an example: - -```mojo -fn show_differences(a: Reference[String, _, _]): - # 'T' equals String, not Reference[String] - alias T = __type_of(a) - - # 'v1' has type String, so this copies the string - var v1 = a - - # List of strings, not a list of references of strings - var list = List(a, a, a) - - # Slices the string, not a dereference! - var strslice = a[] -``` - -The intuition here is the compiler internally has a way to reason about -“[LValue](https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue)”s, and -the formal type of a variable doesn’t include how it can be accessed: whether it -is mutable or just readable etc. With this as a guide, it makes sense that -`Reference` immediately decays to this internal representation - the utility of -Reference is that it allows one to declare the lifetime and indirection as part -of a function signature or in a struct member in a safe way. Beyond that, we -want it to go away. - -This approach is proven to work (i.e. be teachable and usable) at scale because -it is used in the C++ programming language. It also has a number of other -advantages, which we explore after introducing the second model. - -## The “Persistent Reference” model - -An alternative approach is to try to maintain the `Reference` in the type system -and eliminate it *only when necessary* to type check the program, e.g. -auto-dereference `[x.join](http://x.foo)` because `Reference` doesn’t have a -`join` member, but the underlying `String` does. This model maintains the -reference as much as possible, which leads to differences in the examples shown -above: - -```mojo -fn show_differences(a: Reference[String, _, _]): - # 'T' equals Reference[String], not String - alias T = __type_of(a) - - # 'v1' has type Reference[String] so this doesn't copy the string - var v1 = a - - # List of references, not a list of strings. - var list = List(a, a, a) - - # Dereferences the reference(??): doesn't slice the string! - var strslice = a[] -``` - -This model seemed initially appealing to me, and is similar to Rust’s approach. -After exploring it, I noticed that it has two major downsides: - -1) Because references still exist as user-defined values, you need a way to - dereference them, and this leads to confusion when referring to things that - can be sliced or subscripted. - -2) This violates the general design point of Mojo that references shouldn’t - “infect” code that isn’t aware of them. - -3) This approach is also significantly more complicated to implement and teach - than the eager decay model. - -The first issue is perhaps tolerable - we could introduce new syntax to -dereference a reference, or come up with some other way to get that out of the -way. As we mentioned before, `Reference` intentionally has a minimal API, so -the probability of conflicts is low. - -That said, **the second issue is a showstopper, and this is a major intentional -difference between Rust and Mojo**. It is already very common for functions to -return references (e.g. this is why we have to solve the `[]` problem!), and we -don’t want lifetimes to invade types unexpectedly through type inference, e.g. -`var list = List(self.get_thing())` . - -Mojo is a language with value semantics and strong copying and moving support, -aimed at the Python community. We are embracing safe references and lifetimes, -but want to progress on the usability challenges that the Rust community is -still working through. The goal of Mojo is to be familiar and learnable by -Python programmers, and as such, we want management of references to be opt-in, -not implicit because you’re calling a function that happens to return one. - -Furthermore, both models are equally expressible - they both permit references -in structures, propagating references around when needed, and other fancy -tricks. The two models differ on the defaults observed and whether reference -propagation is opt-in or opt-out. - -For all these reasons, I recommend going with the “Eager Reference Decay” model. - -# Implementation approach - -There are three steps to implementing this: - -## Implement auto-deref itself - -The implementation approach is straightforward - all expressions that yield a -`Reference` will automatically decay to an LValue or BValue when the expression -is formed. - -We don’t want to hard code knowledge of `Reference` in the compiler, instead we -should key this off of the presence of the existing `__mlir_ref__` member. - -## Remove `Reference.__refitem__` - -Now that there are no persistent expressions of `Reference` type, we can further -narrow the interface by dropping the newly pointless refitem implementation. -You can never have a value of Reference type, so there is no way to apply `[]` -to it. - -## Remove `__refitem__` / `__refattr__` from the language - -Now that references automatically dereference, we don’t need `__refitem__` and -`__refattr__` anymore - types that use them can just implement `__getitem__` and -have it return a `Reference`. This eliminates a pile of complexity and magic -from the compiler. - -# Future directions - -This approach is simple, but could enable extensions to future things that need implicit dereference abilities, e.g. existentials. diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md deleted file mode 100644 index e78a4f2a56..0000000000 --- a/proposals/byte-as-uint8.md +++ /dev/null @@ -1,25 +0,0 @@ -# Standardise the representation of byte sequence as a sequence of unsigned 8 bit integers - -At this point in time, a sequence of bytes is often represented as a sequence of signed 8 bit integers in Mojo standard library. -Most noticeable example is the underlying data of string types `String`, `StringLiteral`, `StringRef` and `InlinedString`, but also APIs like for example the hash function `fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int:`. - -# Motivation - -Logically a byte is an integer value between `0` and `255`. Lots of algorithms make use of arithmetics ground by this assumption. -A signed 8 bit integer on the contrary represents values between `-128` and `127`. This introduces very subtle bugs, when an algorithm written for unsigned 8 bit integer is used on a signed 8 bit integer. - -Another motivation for this change is that Mojo aims to be familiar to Python users. Those Python users are familiar with the `bytes` class, which itself is working with values between `0` and `255`, not values between `-128` and `127`. - -## Examples: - -### Division: -A value `-4` represented as `Int8` has the same bit pattern as value `252` represented as `UInt8`. -`-4 // 4` equals to `-1` (`bx11111111`), where `252 // 4` equals to `63` (`bx00111111`) as we can see the bit patterns are different. - -### Bit shift: -Values `-1` and `255` have the same bit pattern as `Int8` and `UInt8` `bx11111111` but `-1 >> 1` results in `-1` (same bit pattern), where `255 >> 1` results in `127` (`bx01111111`) - -# Proposal - -A text based search for `DTypePointer[DType.int8]` and `Pointer[Int8]` on current open-sourced standard library revealed 29 results for `Pointer[Int8]` and 78 results for `DTypePointer[DType.int8]`. -Replacing `DTypePointer[DType.int8]` with `DTypePointer[DType.uint8]` and `Pointer[Int8]` with `Pointer[UInt8]` on case by case bases is a substantial refactoring effort, but it will prevent a certain class of logical bugs (see https://github.com/modularml/mojo/pull/2098). As it is a breaking change in sense of API design, it is sensible to do the refactoring as soon as possible. diff --git a/proposals/inferred-parameters.md b/proposals/inferred-parameters.md deleted file mode 100644 index 27c7f4d347..0000000000 --- a/proposals/inferred-parameters.md +++ /dev/null @@ -1,143 +0,0 @@ -# Inferring Parameters from Other Parameters - -A common feature in programming language with generics is the ability to infer the value of generics/templates/parameters from the argument types. Consider C++: - -```cpp -template -void inferMe(T x) {} - -int x = 1; -inferMe(x); -// Equivalent to -inferMe(x); -``` - -Mojo is a parametric language and also supports this feature in a variety of use cases that make code significantly less verbose: - -```python -fn infer_me[dt: DType, size: Int](x: SIMD[dt, size]): pass - -infer_me(Int32()) -# Equivalent to -infer_me[DType.int32, 1](Int32()) -``` - -But Mojo pushes these needs a step further. As a language that encourages heavy parameterization, dependent types are very common throughout the language. Consider: - -```python -fn higher_order_func[dt: DType, unary: fn(Scalar[dt]) -> Scalar[dt]](): pass - -fn scalar_param[dt: DType, x: Scalar[dt]](): pass -``` - -Language users commonly encounter cases where dependent types could infer their parameter values from other parameters in the same way from argument types. Consider `scalar_param` in the example above: `dt` could be inferred from the type of `x` if `x` were passed as an argument, but we have no syntax to express inferring it from `x` as a parameter since the user is required to pass `dt` as the first parameter. - -```python -scalar_param[DType.int32, Int32()]() # 'dt' parameter is required -``` - -This has been requested multiple times in various forms, especially given the new autoparameterization feature. The current tracking feature request: - -- https://github.com/modularml/mojo/issues/1245 - -# Proposal - -In the above example, we want to be able to infer `dt` instead of explicitly specifying it: - -```python -scalar_param[Int32()]() -``` - -Laszlo Kindrat and I proposed several options to remedy this and members of the “Mojo Language Committee” met to discuss these ideas, summarized below. - -We decided to move forward with the following option. Mojo will introduce a new keyword, `inferred`, as a specifier for parameters only. `inferred` parameters must precede all non-inferred parameters in the parameter list, and they **cannot** be specified by a caller — they can **only** be inferred from other parameters. This allows us to express: - -```python -fn scalar_param[inferred dt: DType, x: Scalar[dt]](): pass - -scalar_param[Int32()]() # 'dt' is skipped and 'Int32()' is bound to 'x' -``` - -Where `dt` is inferred from `x`. The decision to choose a keyword instead of introducing a new punctuation character [like Python does for keyword-only arguments](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) is because a keyword clearly indicates the intent of the syntax, and it’s easy to explain in documentation and find via internet search. - -# Aside: Inferring from Keyword Parameters - -Related but separate to the proposal, we can enable parameter inference from other parameters using keyword arguments. This allows specifying function (and type) parameters out-of-order, where we can infer parameters left-to-right: - -```python -scalar_param[x=Int32()]() # 'dt' is inferred from 'x' -``` - -We should absolutely enable this in the language, since this does not work today. However, with respect to the above proposal, in many cases this still ends up being more verbose than one would like, especially if the parameter name is long: - -```python -scalar_param[infer_stuff_from_me=Int32()]() - -# One would like to write: -scalar_param[Int32()]() -``` - -So this feature is orthogonal to the `inferred` parameter proposal. - -# Alternatives Considered - -Several alternative ideas were considered for this problem. - -## Non-Lexical Parameter Lists - -This solution would alter the name resolution rules inside parameter lists, allowing forward references to parameters within the same list. The above example would be expressed as: - -```python -fn scalar_param[x: Scalar[dt], dt: DType](): pass -``` - -Where any parameter is inferrable from any previous parameter. The benefits of this approach are that the order of parameters at the callsite match the order in the declaration: `scalar_param[Int32()]()` - -This alternative was rejected because: - -1. Non-lexical parameters are potentially confusing to users, who normally expect named declarations to be lexical. Relatedly, we are moving towards removing non-lexical parameters in general from the language. -2. This would incur a huge implementation burden on the compiler, because the type system needs to track the topological order of the parameters. - -## New Special Separator Parameter - -This solution is fundamentally the same as the accepted proposal, but differs only in syntax. Instead of annotating each parameter as `inferred`, they are separated from the rest using a new undecided sigil (`%%%` is a placeholder): - -```python -fn scalar_param[dt: DType, %%%, x: Scalar[dt]](): pass -``` - -The benefit of this approach is this matches the [Python syntax](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) for separating position-only and keyword-only parameters. It also structurally guarantees that all infer-only parameters appear at the beginning of the list. - -This alternative was rejected because: - -1. There was no agreement over the syntax, and any selected sigil would introduce additional noise into the language. -2. `inferred` clearly indicates the intent of the syntax, and can be found via internet search, and is overall easier to explain syntax than introducing a new argument separator. - -## Special Separator Parameter at the End - -This is a variation on the above, where the infer-only parameters would appear at the end of the parameter list, and subsequent parameters would be allowed to be non-lexical: - -```python -fn scalar_param[x: Scalar[dt], %%%, dt: DType](): pass -``` - -The benefit of this approach is that the parameters appear in the same position at the callsite. This alternative was rejected for a combination of the reasons for rejecting a new separator and non-lexical parameters. - -## Segmented Parameter Lists - -This proposal would allow functions to declare more than one parameter list and enable right-to-left inference of the parameter “segments”. The above would be expressed as: - -```python -fn scalar_param[dt: DType][x: Scalar[dt]](): pass -``` - -The callsite would look like - -```python -scalar_param[Int32()]() -``` - -And call resolution would match the specified parameter list to the last parameter list and infer `dt`. This proposal was rejected because - -1. The right-to-left inference rules are potentially confusing. -2. This is an overkill solution to the problem, because this opens to door to arbitrary higher-order parameterization of functions. diff --git a/proposals/lifetimes-and-provenance.md b/proposals/lifetimes-and-provenance.md deleted file mode 100644 index 3b8afe7e8f..0000000000 --- a/proposals/lifetimes-and-provenance.md +++ /dev/null @@ -1,426 +0,0 @@ -# Provenance Tracking and Lifetimes in Mojo - -As of mid-May 2023, Mojo has full support for ownership (including move -semantics, borrows and transfers, mutability, ASAP destruction of values, and -member synthesis). This provides more expressiveness than many languages, but does -not meet the expectations of Rust and C++ programmers because it is impossible -to **return references** and **put references in structs**. - -This makes Mojo unable to express common patterns like `StringRef` in the LLVM -APIs because it is a struct containing a reference, and this makes our `Pointer` -type a massively unsafe API. - - -## Goals of this document - -This document explores the first step in adding lifetimes to Mojo: what changes -we’ll have to introduce, some thinking on syntax we may want to use, and how -this may want us to reconsider existing design decisions. This is written in -the style of the "[Value ownership design for Mojo](value-ownership.md)" -document from January. - -This document is really just the “first step” of lifetimes. Rust includes a few -more exotic features, including [subtyping constraints between -lifetimes](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-subtyping-constraints-between-lifetimes-15), -[equality constraints between lifetime parameters](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-equality-constraints-between-lifetime-parameters-16), -[unbounded lifetimes](https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html) -and perhaps other features. We don't have all the mechanics of a generic system -and trait system yet to tie into - and it makes sense to lazily add complexity -based on need - so these are not included in this initial design. - -## Context - -Mojo already has much of the required infrastructure in place to support -lifetimes: we now have references, we just need to be able to return them. -Similarly, the Mojo parameter system provides a powerful way to model and -propagate lifetime information around in our type system. We have a -CheckLifetime compiler pass which infers lifetimes, diagnosing use of -uninitialized values and inserting destructor calls. - -Similarly, we can learn a lot from Rust’s design for lifetimes. That said, the -ownership system in Mojo is quite different than the one in Rust. In Rust, -scopes define the implicit lifetimes of values and references, and lifetimes are -used to verify that uses of the value happen when the value is still alive. Mojo -flips this on its head: values start their life when defined, but end their life -after their last use. This means that in Mojo, a lifetime reference **extends -the liveness of a value** for as long as derived references is used, which is a -bit different than Rust. - -For example, we expect this to behave like the comments indicate: - -```mojo - var some_str = String("hello") - - # The StringRef contains a reference to the some_str value - var some_str_ref = StringRef(some_str) - - # Last use of some_str, but don't destroy it because there is a use of - # the reference below! - use(some_str) - - # References must be propagatable through methods. - some_str_ref = some_str_ref.drop_front().drop_back() - print(some_str_ref) # Prints "ell" - # some_str destroyed here after last reference to it -``` - -The major thing that Mojo (and Rust) need from lifetimes is what is called -“local reasoning”: We need to be able to reason about the memory behavior of a -call just by looking at its signature. We cannot have to look into the body of a -function to understand its effects, and a function cannot know about its callers -to reason about the behavior of its arguments. Similarly, when accessing a -member `a.ref` that is a reference, we need to know what lifetime is being used -in the context of `a`. - -Because of this, the lifetimes in a function signature are something of a -"transfer function" that expresses mappings from input lifetimes to returned -lifetimes, and that allows reasoning about the lifetimes of field references. -Mojo already has a powerful parameter system that allows it to express these -sorts of relationships, so this all plugs together. - -### What to name / how to explain this set of functionality? - -Rust has a few things going on that are tightly bound and somewhat confusing: it -has scoping, lifetimes, and lifetime holes. It has a borrow checker that -enforces the rules, all together this is its ownership system. - -When it comes to the “lifetimes” part of the puzzle, it seems better to clarify -two very different concepts: on the one hand, stored **values** in memory each -have a conceptual “lifetime” that starts when the value is defined and ends at -the last use - this is where the destructor call is inserted. Because each -declared variable has an independently tracked lifetime, each also needs an -implicitly declared “lifetime parameter” that is tracked in the Mojo type -system. - -On the other hand, when reasoning about parametric functions, the type signature -defines a transfer function that expresses the “provenance” of the result values -from the function and how they relate to input values. This relationship is a -transfer function from the input lifetime parameters to output lifetime -parameters, and can be somewhat more complicated. The framing of “provenance -tracking” may be more general conceptually than “lifetime tracking” which seems -specific to the lifetime parameters. - -## Mojo Reference + Lifetime Design - -Lifetimes enable us to use references as first class types. This means they -can occur in nested positions: You can now have a reference to a reference, you -can have an array of references, etc. At the machine/execution level, a -reference is identical to a pointer, but the reference type system enables an -associated lifetime, tracking of mutability etc. - -### Writing a lifetime bound reference - -The first question is how to spell this. Rust uses the `'a` syntax which is -pretty unconventional and (weirdly but) distinctly Rust. For example, here are -some simple Rust functions: - -```rust -// This is Rust -fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {..} -fn longest2<'a>(x: &'a mut str, y: &'a mut str) -> &'a mut str {..} -``` - -Mojo already have a general set of values in our generic signature list: we just -need to "parameterize" references and make them explicit. For now, we recommend -using new `ref` and `mutref` keywords parameterized on a lifetime, and a new -`Lifetime` type. For example: - -```mojo -# Proposed Mojo syntax. -fn longest[a: Lifetime](x: ref[a] String, - y: ref[a] String) -> ref[a] String: - return x if len(x) >= len(y) else y - -fn longest2[a: Lifetime](x: mutref[a] String, - y: mutref[a] String) -> mutref[a] String: ... -``` - -There are many other options for syntax that we can consider, but this is simple -and will get us going until we have more experience. For example, we can make -any of these work: - -```mojo -fn f(x: ref[a] String, y: mutref[a] String): ... # Homage to Rust! -fn f(x: ref(a) String, y: mutref(a) String): ... # Homage to Rust! -fn f(x: ref a String, y: mutref a String): ... # Homage to Rust! -fn f(x: ref 'a String, y: mutref 'a String): ... # Homage to Rust! -fn f(x: 'a String, y: mut 'a String): ... # Homage to Rust! -``` - -The argument for square brackets vs parens is if we like the explanation that -we’re -"parameterizing the reference with a lifetime". However, remember types can -also be parametric, and those are spelled with square brackets **after** the -type name, so parens may be better to make these more syntactically distinct. - -The spelling in structs should flow naturally from this: - -```mojo -struct StringRef[life: Lifetime]: - var data : Pointer[UInt8, life] - var len : Int -``` - -Being first class types, we will naturally allow local references: these should -also allow late initialization and explicitly declared lifetimes as well: - -```mojo -fn example[life: Lifetime](cond: Bool, - x: ref[life] String, - y: ref[life] String): - # Late initialized local borrow with explicit lifetime - let str_ref: ref[life] String - - if cond: - str_ref = x - else: - str_ref = y - print(str_ref) -``` - -### Mojo Argument Conventions vs References - -One non-obvious thing is that function argument conventions and references are -different things: keeping them different allows argument conventions to be a -convenient sugar that avoids most users from having to know about references and -lifetimes. For example, the `borrowed` (which is usually implicit) and `inout` -argument conventions are sugar that avoids having to explicitly declare -lifetimes: - -``` -// Mojo today -fn example(inout a: Int, borrowed b: Float32): … - -struct S: - fn method(inout self): … - -// Written long-hand with explicit lifetimes. -fn example[a_life: Lifetime, b_life: Lifetime] - (a: mutref[a_life] Int, b: ref[b_life] Float32): … -struct S: - fn method[self_life: Lifetime](self: mutref[self_life]): … -``` - -This is very nice - every memory-only type passed into or returned from a -function must have a lifetime specified for it, but you really don't want to -have to deal with this in the normal case. In the normal case, you can just -specify that you're taking something by borrow (the default anyway) with an -implicit lifetime, or taking it `inout` if you want to mutate it but don't care -about the lifetime. - -NOTE: `inout` arguments also have one additional feature: function calls with -`inout` arguments know how to work with getter/setter pairs. For example -something like `mutate(a[i])` will call the getter for the element before -calling the function, then call the setter afterward. We cannot support this -for general mutable reference binding (because we wouldn't know where to perform -the writeback) so `inout` will have a bit more magic than just providing an -implicit lifetime. - -NOTE: Internally to the compiler, references (which are always pointer sized) -are treated as a register-passable types. This means they compose correctly -with implicit borrow semantics, and you can even pass a reference `inout` if you -want to. Such a thing is a "mutable reference to a reference", allowing the -callee to mutate the callers reference. - -### Keyword (?) for static lifetime - -I think we can have a useful feature set without requiring the ability to -specify a static lifetime - the uses in Rust appear to be more about -constraining input lifetimes than it is about the core propagation of lifetimes, -that said, we can definitely dream up a spelling when it is needed. - -Similarly, unsafe pointer tricks (e.g. when working with C) may want to use the -static lifetime marker to disable all tracking. We can start with a stub like -`__static_lifetime` and then re-syntax it later. - -### Implicitly declared lifetime parameter names and other sugar - -One common use of named lifetime parameters is to tie the lifetime of the result -of a function back to the lifetime of one of the function arguments. One -refinement over Rust we could permit is for arguments to implicitly declare -lifetimes on their first use. For example, we don’t need to require a -declaration of `life` in this example: - -```mojo -fn longest(x: ref[life] String, - y: ref[life] String) -> ref[life] String: - -# Alternatively follow Rust's lead. -fn longest(x: 'life String, y: 'life String) -> 'life String: -``` - -Let's **NOT** add this in the near future. Explicit lifetimes will be much less -common in Mojo than they are in Rust, and it is better for learnability to not -have magic like this. - -### Lifetime of `Self` - -The `Self` keyword (upper case) produces an elaborated type name for the current -struct, but that does not include the lifetime of `self` (lower case) which is -generally a reference. In a method you can name the lifetime of `self` by -declaring it explicitly like this: - -```mojo - struct MyExample: - fn method[self_life: Lifetime](self: mutref[self_life] Self) - -> Pointer[Int, self_life]: - ... - - fn callMethod(x: mutref[life1] MyExample): - use(x.method()) - - var y = MyExample() - use(y.method()) -``` - - -`self_life` will bind to the lifetime of whatever lvalue the method is called -on, which is the `life1 `lifetime in the first example, and the implicit -lifetime of y in the second example. This all composes nicely. - -One problem though - this won’t work for var definitions inside the struct, -because they don’t have a self available to them, and may need to reason about -it: - -```mojo - struct IntArray: - var ptr : Pointer[Int, Self_lifetime] -``` - -It isn’t clear to me how the compiler will remap this though. We’d have to pass -in the pointer/reference instead of the struct type. An alternative is to not -allow expressing this and require casts. We can start with that model and -explore adding this as the basic design comes up. - -### Extended `getitem`/`getattr` Model - -Once we have references, we’ll want to add support for them in the property -reference and subscripting logic. For example, many types store their enclosed -values in memory: instead of having `Pointer` and `Array` types implement both -`__getitem__` and `__setitem__` (therefore being a "computed LValue") we'd much -rather them to expose a reference to the value already in memory (therefore -being more efficient). We can do this by allowing: - -```mojo - struct Pointer[type: AnyType, life: Lifetime]: - # This __getref__ returns a reference, so no setitem needed. - fn __getref__(self, offset: Int) -> mutref[life] type: - return __get_address_as_lvalue[life](...) -``` - -We will also need to extend the magic `__get_address_as_lvalue` style functions -to take a lifetime. - -## Examples using Lifetimes - -This section attempts to build a few example data structures that are important -to express with lifetimes. They obviously haven’t been tested. - -### Pointer / UnsafePointer / Reference - -This is the bottom of the stack and needs to interface with other unsafe -features. Suggested model is to make Pointer be parameterized on the lifetime -that it needs to work with as well as element type: - -```mojo - @value - @register_passable("trivial") - struct MutablePointer[type: AnyType, life: Lifetime]: - alias pointer_type = __mlir_type[...] - var address: pointer_type - - fn __init__() -> Self: ... - fn __init__(address: pointer_type) -> Self: ... - - # Should this be an __init__ to allow implicit conversions? - @static_method - fn address_of(arg: mutref[life] type) -> Self: - ... - - fn __getitem__(self, offset: Int) -> inout[life] type: - ... - - @staticmethod - fn alloc(count: Int) -> Self: ... - fn free(self): ... - - fn exercise_pointer(): - # Allocated untracked data with static/immortal lifetime. - let ptr = MutablePointer[Int, __static_lifetime].alloc(42) - - # Use extended getitem through reference to support setter. - ptr[4] = 7 - - var localInt = 19 - let ptr2 = MutablePointer.address_of(localInt) - ptr2[0] += 1 # increment localInt - - # ERROR: Cannot mutate localInt while ptr2 lifetime is live - localInt += 1 - use(ptr2) -``` - -It’s not clear to me if we need to have a split between `Pointer` and -`MutablePointer` like Swift does. It will depend on details of how the -CheckLifetimes pass works - I’m hoping/expecting that the borrow checker will -allow mutable references to overlap with other references iff that reference is -only loaded and not mutated. NOTE: We probably won't be able to do this with the -proposed model, because generics can't be variant over mutability of the -reference. - -Another aspect of the model we should consider is whether we should have an -`UnsafePointer` that allows unchecked address arithmetic, but have a safe -`Reference` type that just allows dereferencing. This `Reference` type would be -completely safe when constructed from language references, which is pretty cool. -We may also want to wire up the prefix star operator into a dunder method. - - -### ArraySlice - -`ArraySlice` (aka `ArrayRef` in LLVM) should compose on top of this: - -``` - @value - @register_passable("trivial") - struct MutableArraySlice[type: AnyType, life: Lifetime]: - var ptr: MutablePointer[type, life] - var size: Int - - fn __init__() -> Self: - fn __init__(ptr: MutablePointer[type, life], size: Int) -> Self: - - # All the normal slicing operations etc, with bounds checks. - fn __getitem__(self, offset: Int) -> inout[life] type: - assert(offset < size) - return ptr[offset] -``` - - -As with `UnsafePointer`, this has to be parameterized based on the underlying -element type. `ArraySlice` is just a bound checked pointer, but because of -lifetimes, it is safe once constructed: the references it produces are bound to -the lifetime specified so can’t dangle. - - -### Array / ValueSemanticArray - -Given these low level types, we can start to build higher level abstractions. -One example of that is an `Array` type. I’d suggest that our default array type -be value semantic with lazy copy-on-write 🐄, but a simpler example can be -implemented with `std::vector` style eager copying: - -```mojo - # Doesn't require a lifetime param because it owns its data. - struct Array[type: AnyType]: - var ptr: MutablePointer[type, Self_lifetime] - var size: Int - var capacity: Int - - fn __getitem__[life: Lifetime](self: inout[life], start: Int, - stop: Int) -> MutableArraySlice[type, life]: - return MutableArraySlice(ptr, size) -``` - -By tying the lifetime of the produced slice to the lifetime of the Array `self`, -the borrow checker will prevent use/mutation of the `Array` itself while a -mutable slice is produced. diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md deleted file mode 100644 index 053ab5e8f9..0000000000 --- a/proposals/lifetimes-keyword-renaming.md +++ /dev/null @@ -1,128 +0,0 @@ -# Keyword naming and other topics to discuss - -This document is split off the [Provenance Tracking and Lifetimes in -Mojo](lifetimes-and-provenance.md) document to separate general syntactic -bikesheding issues from the core semantic issues in that proposal. - -Assuming that proposal goes through, I think we should consider a few changes to -the current Mojo keyword paint: - -## `borrowed` Keyword => `borrow` or `ref` - -`borrowed` as a keyword doesn’t really make sense in our new world. This is currently used to indicate an argument that is a borrowed version of an existing value. Given the introduction of lifetimes, these things can now appear in arbitrary places (e.g. you can have an array of references) so it makes sense to use a noun. - -Instead of reading an argument as “this function takes foo which is a borrowed string”, we would read it as “foo is a borrow/ref of a string”. This makes it consistent with local borrows on the stack: - -```mojo -fn do_stuff[a: Lifetime](x: ref[a] String): ... - -fn usage(): - var str = String("hello") - ref r = str # Defines a local borrow of str. - - do_stuff(str) # Bind a reference to 'str' - do_stuff(r) # Pass on existing reference 'str' -``` - -## `inout` Keyword => `ref` or `mutref` (??) - -The primary argument for the ‘`inout`’ keyword being named this was that Chris -wanted to get off the former ampersand syntax we used, and that (in an argument -position) there is copy-in and copy-out action that happens with computed -LValues. I think there is a principled argument to switch to something shorter -like `ref` which is used in other languages (e.g. C#), since they can exist in -other places that are not arguments, and those don’t get copy-in/copy-out -behavior. One challenge with the name `ref` is that it doesn't obviously -convey mutability, so we might need something weird like `mutref`. - -Note that copy-in/copy-out syntax is useful in more than function call -arguments, so the `inout` keyword may return in the future. For example, we may -actually want to bind computed values to mutable references: - -```mojo -for inout x in some_array_with_getitem_and_setitem: - x += 1 -``` - -This requires opening the reference with getitem, and writing it back with -setitem. We may also want to abstract over computed properties, e.g. form -something like `Array[inout Int]` where the elements of the array hold closers -over the get/set pairs. If we had this, this could decay to a classic mutable -reference at call sites providing the existing behavior we have. - -Given this possible direction and layering, I think we should go with something -like this: - -1. `ref`: immutable reference, this is spelled “`borrowed`” today -2. `mutref`: mutable reference, this is spelled “`inout`” today. I’d love a better keyword suggestion than `mutref`, perhaps just `mut`? -3. `inout`: abstracted computed mutable reference with getter/setter. - -`inout` can decay to `mutref` and `ref` in an argument position with writeback, -and `mutref` is a subtype of `ref` generally. - -## `owned` Keyword => `var` - -People on the forums have pointed out that the “`owned`” keyword in argument lists is very analogous to the `var` keyword. It defines a new, whole, value and it is mutable just like `var`. Switching to `var` eliminates a concept and reduces the number of keywords we are introducing. - -## Allow `let` in argument lists ... or remove them entirely (!) - -If we replace the `owned` keyword with `var`, then we need to decide what to do -with `let`. There are two different paths with different tradeoffs that I see. - -The easiest to explain and most contiguous would be to allow arguments to be -defined as `let` arguments, just like we define `var` arguments. This would -keep these two declarations symmetrical, and appease people that like to control -mutation tightly. - -The more extreme direction would be to remove `let` entirely. Some arguments -in favor of this approach: - -1. It has been observed on the forum that it adds very little - it doesn't - provide additional performance benefits over `var`, it only prevents - "accidental mutation" of a value. -2. Languages like C++ default to mutability everywhere (very few people bother - marking local variables constant, e.g. with `const int x = foo()`. -3. The more important (and completely necessary) thing that Mojo needs to model - are immutable borrows. Removing `let` would reduce confusion about these two - immutable things. -4. Mojo also has `alias`, which most programmers see as a “different type of - constant” further increasing our chance of confusing people. -5. `let` declarations require additional compiler complexity to check them, Mojo - doesn’t currently support struct fields market `let` for example because the - initialization rules are annoying to check for. Once you have them, it - messes with default values in structs. - -In my opinion, I think we are likely to want to remove `let`’s, but we should -only do so after the whole lifetime system is up and working. This will give us -more information about how things feel in practice and whether they are worth -the complexity. - -## More alternatives to consider: - -[@sa- suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6104926) the keyword `fix` instead of `let`. - -[@mojodojodev suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6105688): - -`ref[a]` - immutable reference -`mut[a]` - mutable reference -`let[a]` - immutable owned -`var[a]` - mutable owned - -Having three letters for all of the keywords will allow the user to understand -"this is related to ownership and mutability". The problem with the proposed removing let is that code ported from Python to Mojo won't behave the same, keeping let and var is advantageous in that it says this is a Mojo variable so you can add all the weird Python dynamic behavior when the keyword is elided. - -[@mzaks suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6134220) using numbers to identify lifetimes, e.g.: - -``` -fn example['1_life](cond: Bool, - x: borrowed'1 String, - y: borrowed'1 String): - # Late initialized local borrow with explicit lifetime - borrowed'1 str_ref : String - - if cond: - str_ref = x - else: - str_ref = y - print(str_ref) -``` diff --git a/proposals/mojo-and-dynamism.md b/proposals/mojo-and-dynamism.md deleted file mode 100644 index e70ef47263..0000000000 --- a/proposals/mojo-and-dynamism.md +++ /dev/null @@ -1,147 +0,0 @@ -# Mojo and Dynamism - -Mojo has the lofty goal of being a simple, powerful, and easy-to-use language like Python but with features that allow programmers to reach the performance of C. One of Mojo's approaches is to start from being a superset of Python and provide an incremental typing-for-performance story where the performance of Python code can be incrementally improved by adding type annotations, explicit variable declarations and error handling, switching `def` to `fn`, and so on. By making things like types explicit, dynamism is removed from the program and the compiler can generate faster code. The relationship between Mojo and dynamism has to be carefully managed to meet the goals of the language. The point of this post is to measure where that relationship stands now, what it will need going forward as Mojo grows more features, and develop a framework for managing that relationship. - -## Classes and Dynamism - -One feature that Mojo lacks at the moment are classes, an inherently dynamic feature that provides inheritance and runtime polymorphism, the foundations of object-oriented programming. - -Classes are implemented differently in many languages. In Python, for example, classes are much more flexible than in languages like C++ or Java -- they are more similar to classes in Javascript or Smalltalk. Methods can be defined and then deleted, and even conditionally defined! For example, this is valid Python code: - -```python -define = True - -class C: - print("hello") # prints 'hello' - if define: - def f(self): print(10) - else: - def f(self): print(20) - - -C().f() # prints '10' -``` - -In fact, the body of a Python class is just code that is executed, and the resulting local variables are bound to the attributes of a class object. When calling a class object, it returns a new object with a reference to the class object, in which it can perform attribute lookup. In addition, functions that would be member functions have their first argument bound to the new class instance through the Python [descriptor mechanism](https://docs.python.org/3/howto/descriptor.html#invocation-from-a-class). - -Mojo as a superset of Python has to support the full "hash-table" dynamism in classes for compatibility with Python, but reference semantic classes are also important for systems programming and application programming, where this level of dynamism isn't needed and is actively harmful. We need to decide how to handle this. - -One approach is to provide a decorator on class definitions (which can be opt-in or opt-out) to indicate whether the class is "fully dynamic" as in Python or whether it is "constrained dynamic" (e.g. has virtual methods that may be overridden but cannot have methods added or removed). - -"Constrained dynamic" Mojo classes will use vtables for a more limited but more efficient constrained dynamism than full hash table lookups. In addition to raw lookups, constrained dynamic classes can use "[class hierarchy analysis](https://dl.acm.org/doi/10.5555/646153.679523)" to devirtualize and inline method calls, which are not valid for "fully dynamic" classes. - -Swift has a similar issue, where the developers wanted to have constrained dynamism by default but needed full dynamism when working with Objective-C code: Objective-C is based on the Smalltalk object model and thus has the same issues as Python. Swift solved this by adding an opt-in [@objc](https://swiftunboxed.com/interop/objc-dynamic/) decorator, which provides full compatibility with Objective-C classes. Swift implicitly applies this decorator to subclasses of Objective-C or `@objc` classes for convenience. - -If we chose to follow this design in Mojo, we could introduce a `@dynamic` decorator, in which the class is an instance of a hash table and the body is executed at runtime: - -```python -@dynamic -class C: - def foo(): print("warming up") - foo() # prints 'warming up' - del foo - def foo(): print("huzzah") - foo() # prints 'huzzah' -``` - -We could of course make dynamic be the default, and have a decorator like `@strict` to opt-in to constrained dynamism as well. Regardless of the bias, we absolutely need to support full dynamism to maintain compatibility with Python. - -An implementation question here would be "when does the body get executed?" when the class is defined at the top-level. In this case, the class `C` could be treated as a global variable with a static initializer that is executed when the program is loaded. This ties into a discussion about how to treat global variables and top-level code in general, which will come in a subsequent section. Naturally, if the class is never referenced, the body is never parsed and the static initializer is never emitted. - -### Syntactic Compatibility and `@dynamic` - -A primary goal of Mojo is to [minimize the syntactic differences](https://docs.modular.com/mojo/why-mojo.html#intentional-differences-from-python) with Python. We also have to balance that need with what the right default for Mojo is, and this affects the bias on whether this decorator is "opt-in" or "opt-out". - -We find it appealing to follow the Swift approach by making "full dynamic" an opt-in choice for a Mojo class. This choice would add another syntactic divergence between Mojo and Python, but it is one that can be alleviated with an automatic mechanical transformer from Python code to Mojo code (e.g. to deal with new keywords we take). In this case, all Python classes will be translated by sticking `@dynamic` on them, and they can be removed for incremental boosts to performance. - -An alternate design is to require opt-in to "constraint dynamism" by adding a `@strict` (or use another keyword altogether) for vtable dynamism. We can evaluate tradeoffs as more of the model is implemented. - -## `def` and Dynamism - -In Mojo, the goal of `def` is to provide a syntactic feature set that enables compatibility with Python. It allows, for example, omitting type annotations, implicit variable declarations, implicit raises, etc. But the Mojo `def` is not the same as a Python `def`. A commonly reported issue is that Mojo scoping rules differ from Python's. In Python, local variables are scoped at the function level, but Python also supports behaviour like: - -```python -def foo(k): - for i in range(k): - print(i) - # Mojo complains that `i` is not defined, but this code should compile and - # dynamically raise an `UnboundLocalError` depending on the value of `k`! - print(i) -``` - -Python functions also have a notion of which names are supposed to be bound to local variables. In the following example, `bar` knows `i` refers to a captured local variable in `foo`, whereas `baz` tries to retrieve a value for `i` in its local variable map. - -```python -def foo(): - i = 2 - def bar(): - print(i) - def baz(): - print(i) - i = 10 - bar() # prints '2' - baz() # throws an 'UnboundLocalError' -``` - -This gets at the heart of how Mojo should treat implicitly declared variables in `def`s. The short answer is: exactly how Python does. `def`s should carry a function-scoped hash table of local variables that is populated and queried at runtime. In other words, lookup of implicitly-declared variables would be deferred to runtime. On the other hand, the function does need to have a notion of what variable *could* be available in the function, in order to emit `UnboundLocalError`s as required. Of course, the compiler can optimize the table away and do all the nice stuff compilers do if possible. - -Difficulty arises when discussing `def`s themselves. Although `def`s should internally support full hashtable dynamism, what kind of objects are `def`s themselves? For instance: - -```python -def foo(): - bar() - -def bar(): - print("hello") - -foo() # prints 'hello' - -def bar(): - print("goodbye") - -foo() # should this print 'goodbye'? -``` - -In Mojo today, the first time the name lookup of `bar` is resolved, it is baked into a direct call to the first `bar`. Therefore, shadowing of `bar` does not propagate into the body of `foo`. On the other hand, if all `def`s were treated as entries in a hashtable, then it would. - -One middle-ground approach would be to treat `bar` as a mutable global variable of type `def()` (one for each possible overload of `bar`). The dynamism can be escalated with a `@dynamic` decorator that removes static function overloading. However, both of these approaches risk creating confusing name lookup rules. For instance, would the following be allowed? - -```python -@dynamic -def bar(a): pass - -def bar(a: Int): pass -``` - -This gets into the "levels of dynamism" Mojo intends to provide, and how that relates to `def`s. The reality is that `def`s in Mojo today only resemble Python `def`s on the surface. They share similar syntax, but Mojo `def`s are really extra syntax sugar on top of `fn` and are altogether a different beast than Python `def`s. - -## Four Levels of Dynamism - -To summarize, in order to support incremental typing-for-performance, Mojo will have to support everything from strict, strongly-typed code to full Python hashtable dynamism but with syntax that provides a gradual transition from one end to the other. - -Given all that has been discussed and what the language looks like today, Mojo's dynamism is moving into four boxes: - -1. Compile-time static resolution. -2. Partial dynamism. -3. Full hashtable dynamism. -4. ABI interoperability with CPython. - -The fourth category isn't explored here, but will important when/if we support subclassing imported-from-CPython classes in Mojo, because that will fix the runtime in-memory representation to what CPython uses. - -The highest level of dynamism and the most faithful compatibility doesn't come from Mojo itself, it comes from Mojo's first class interoperability with CPython. This in effect will be Mojo's escape hatch for compatibility purposes and is what gives Mojo access to all of Python's vast ecosystem. Below that, Mojo will provide an emulation of Python's hash-table dynamism that is a faithful but not quite identical replication of Python behaviour (no GIL, for example!). Building this out will be a huge undertaking, and is something Mojo should do over time. - -The most important thing to remember is that Mojo is not a "Python compiler". The benefit of sharing the same syntax as Python, however, means seamless interop is on the table: - -```python -@python -def python_func(a, b=[]): - return a + [2] + b - -fn mojo_func(): - try: - print(python_func([3])) - except e: - print("error from Python:", e) -``` - -The goal of the "levels of dynamism" is to provide an offramp, starting by removing the `@python` decorator from `python_func`. diff --git a/proposals/project-manifest-and-build-tool.md b/proposals/project-manifest-and-build-tool.md deleted file mode 100644 index 1899b0ae3d..0000000000 --- a/proposals/project-manifest-and-build-tool.md +++ /dev/null @@ -1,104 +0,0 @@ -# Mojo project manifest and build tool - -A *project manifest* is a file that describes the source code files that make up -a library or executable, and how those files are meant to be built, distributed, -and interacted with by language tooling (such as language servers and -debuggers). Some examples include Rust’s `Cargo.toml`, Python’s `setup.py`, or -language-agnostic formats such as Bazel and its `BUILD` files. - -A *build tool*, on the other hand, is a program that can create the libraries or -executables described in a project manifest. For example, `cargo build` compiles -and links Rust executables and libraries described in a `Cargo.toml` file. In -Python, many different tools can process `setup.py` and `pyproject.toml` files, -in order to produce Python wheels or packages. - -This proposal is meant to: - -1. Announce our intent to define a project manifest format for Mojo. -2. Announce our intent to implement a build tool for Mojo projects. -3. Solicit community feedback and feature requests for the project manifest and - build tool. - -## Motivation - -No project manifest format exists for Mojo as of Mojo SDK v0.7.0. In the present -situation, Mojo projects can be built by invoking `mojo package` (to produce a -`.mojopkg` library) or `mojo build` (to produce an executable) on the command -line. - -This status quo has several drawbacks: - -1. There is no standard for building a Mojo project from source. If we examine - popular Mojo projects hosted on GitHub today, some are built via `docker`, - where the Dockerfile invokes `mojo run` to execute the Mojo source file. Some - are built via CMake, where project maintainers have used `add_custom_command` - to invoke `mojo build` and `mojo package`. Some are built via a - `mojo package` command that is documented only in the README. Some have build - instructions that are only known to the maintainers. The lack of - standardization makes it difficult to download and build a Mojo source - repository, which inhibits collaboration among the Mojo community of - developers. -2. Collaboration within the Mojo community aside, the lack of a project manifest - inhibits Mojo language tooling, such as the language server and debugger, - from functioning perfectly. For example, many Mojo projects make use of - compile time definitions, such as `-D ENABLE_TILING`. Without knowing which - definitions should be used when compiling Mojo source code, the language - server cannot provide the same diagnostics that the user will see when - actually building their project. - -Therefore, we think that a project manifest and build tool specific to Mojo will -resolve these issues: - -1. We aim to implement a single command that can be used to build any Mojo - project from source, addressing the first issue listed above. This is - analogous to how `cargo build` builds the default targets with the default - settings for any Rust project, or how `zig build` does so for any Zig - project. -2. Because a project’s manifest will specify which Mojo compiler options are to - be used, language tools would make use of those and function as intended. - This addresses the second issue listed above. - -## Guiding principles - -- As mentioned above, we aim for a single command to be capable of building any - Mojo project from source. -- We believe the ability to *download* a project’s dependencies from the - Internet — a “package manager” function — can be added at a later time. For - example, `zig build` did not originally include this functionality, but added - an implementation over six years later. The Mojo build tool will likely - implement the downloading and building of project dependencies soon, but it - will be the subject of a separate proposal. -- Although the project manifest and build tool we design is specific to Mojo, we - will aim for the best possible integration with other build systems, such as - Python setuptools, Bazel, Buck2, and CMake. We will make accommodations to - better support these tools whenever possible. -- Our design will benefit from community input and contributions, so **we will - develop this as an open-source tool, written primarily in Mojo**. We believe - doing so will also serve to drive additions and improvements to the Mojo - standard library. - -## Request for feedback - -As mentioned above, this proposal is primarily to announce our intent to develop -a project manifest format and build tool for Mojo, and to solicit feedback. -Below are some topics that we would love to hear community members’ opinions on: - -- Whether you agree with the motivations and guiding principles in this - proposal. -- Which project manifest formats and build tools you love, and why. We’re - drawing inspiration from a broad set of language ecosystems, including Rust, - Zig, Swift, and especially Python. -- Whether to adopt the [build server - protocol](https://build-server-protocol.github.io). We think doing so may help - with our guiding principle to integrate well into the existing ecosystem of - tools. -- Whether to define the project manifest as an executable program. Analogous to - how `build.zig` and `Package.swift` are programs that define a project, should - we define a `project.mojo` or similar construct? There are many arguments in - favor of doing so, but on the other hand, we see tradeoffs as well, and a - purely declarative form could be used. -- Any other thoughts you wish to contribute — we are build systems and language - tooling nerds! Send us your thoughts on [the GitHub Discussion thread - associated with this - proposal](https://github.com/modularml/mojo/discussions/1785), and let’s geek - out. diff --git a/proposals/remove-let-decls.md b/proposals/remove-let-decls.md deleted file mode 100644 index 40353fd2b9..0000000000 --- a/proposals/remove-let-decls.md +++ /dev/null @@ -1,153 +0,0 @@ -# Simplifying Mojo🔥 - let's get rid of `let`! - -Chris Lattner, Dec 5, 2023, Status: **Accepted**, [discussion thread](https://github.com/modularml/mojo/discussions/1456#discussioncomment-8358722) - -Mojo is still a new language, and is rapidly evolving. We’re learning a lot -from other languages, but Mojo poses its own set of tradeoffs that indicate a -unique design point. - -One of the early decisions made in Mojo's development is that it adopts the -`let` and `var` design point that Swift uses. This whitepaper argues that we -should switch to a simpler model by jettisoning `let` and just retaining `var` -(and implicit Python-style variable declarations in `def`). This has also been -[suggested by the community](https://github.com/modularml/mojo/issues/1205). - -Note that immutability and value semantics remain an important part of the Mojo -design, this is just about removing "named immutable variables". Immutable -references in particular are critical and will be part of a future "lifetimes" -feature. - -## Current Mojo 0.6 design - -Mojo initially followed the precedent of Swift, which allows a coder to choose -between the `let` and `var` keyword to determine whether something is locally -modifiable. That said, the design in Mojo 0.6 and earlier has a number of -particularly surprising and unfortunate aspects: - -1. The notion of immutable variables is entirely new to Python programmers, and -previous experience with Swift shows that this ends up being the [very first concept](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Constants-and-Variables) -a Swift programmer has to learn. This is unfortunate, because named immutable -variables aren't a core programming concept, and not something required -to achieve Mojo's goals. - -2. The naming of `let` caused a lot of early [heat and -debate](https://github.com/modularml/mojo/discussions/120). Other programming -languages have a wide range of design points (e.g. `const` in C/C++ and -Javascript) and there is a divergence of naming for all these things: -`let`, `val`, `const`, etc, etc. - -3. Mojo also has a notion of compile time value (`alias`), which means there are -three concepts going around: `alias`, `let`, and `var`. Most of the uses of -(e.g.) Javascript `const` is better served with `alias` than `let`. - -4. Both Swift and Rust encourage immutable values - Swift (and currently Mojo) -warn about unneeded mutability, Rust makes mutability more verbose (`let mut`), -and some propose that Mojo [make mutability more -verbose](https://github.com/modularml/mojo/issues/451). This cuts very hard -against a lot of the design center of Python, which doesn’t even have this -concept at all: it would be weird to make it the default, but if we don’t, -then why bother having it? - -5. There is no performance benefit to the Swift/Rust design, and I personally -haven’t seen any data that shows that there is any safety or readability -benefit. If anything, it is the argument when seeing `let x = foo()` that you -know `x` will never be reassigned, but any benefit here is small. - -6. The immutability only applies to the local value, and in the case of -reference semantic types (e.g. types like `Pointer` in today's Mojo, but also -*all classes* in tomorrow's Mojo) this is super confusing. We are often asked: -“Why do I get a warning that I should change a "`var` pointer" to `let` when I -clearly mutate what it is pointing to?” - -7. Mojo does not currently allow `let`’s as struct fields, (only `var`’s) which -is inconsistent. Swift has a very complex set of rules for how struct fields -get initialized that would be nice to not implement for Mojo. There also isn’t -a great way to define defaulted field values, e.g.: - -```mojo -struct Thing: - # This is not actually supported right now, but imagine it were. - let field = 42 - fn __init__(inout self): - self.field = 17 # shouldn't be able to overwrite field? -``` - -8. Mojo has a notion of ownership and will eventually have a notion of lifetimes -and safe references (including both mutable and immutable _references_) which -will be different from (but can compose with) the `let` vs `var` distinction. -It is unfortunate to have different forms of immutability floating around, and -we really do need immutable borrows and immutable references. - -Speaking subjectively as one of the principal designers of Swift, I will say -that it has several pretty pedantic language features intended to increase -safety (e.g. requiring all potentially-throwing values to be marked with a `try` -keyword) and many of the decisions were made early without a lot of data to -support them. I believe we should fight hard to keep Mojo easy to learn and -eliminate unnecessary concepts if we can. - -## Proposal: eliminate ‘`let`’ declarations - -The proposal here is straightforward: let’s just eliminate the concept of an -immutable named value entirely. This won’t eliminate immutability as a concept -from Mojo, but will instead push it into the world of borrowed arguments and -immutable references. This would have a number of advantages: - -This directly simplifies the conceptual Mojo language model: - -1. This eliminates one unfamiliar concept that a Python program would have to - learn. -2. This eliminates confusion between `let` vs `alias` directly. -3. This eliminates a fertile source of keyword bikeshedding. -4. This eliminates confusion in workbooks where top level values are mutable - even though they are declared `let`. - -This would eliminate a bunch of complexity in the compiler as well: - -1. Error messages currently have to make sure to say `let` and `var` correctly. -2. The IR representation needs to track this for later semantic checks. -3. This eliminates the need to implement missing features to support `let`’s. -4. This eliminates the complexity around detecting unmutated `var`s that warn - about changing to `let`. -5. Due to ASAP destruction, CheckLifetimes has extra complexity to reject code - like: “`let x: Int; x = 1; use(x); x = 2; use(x)`” even though the original - lifetime of the first “`x=1`” naturally ended and “`x`” is uninitialized - before being assigned to. This has always been a design smell, and it - [doesn’t work right](https://github.com/modularml/mojo/issues/1414). - -This proposal will not affect runtime performance at all as far as we know. - -### What about var? - -If this proposal is accepted, I think we should leave `var` as-is. Unlike -traditional Python behavior, `var` introduces an explicitly declared and -*lexically scoped* value: we need some introducer and do want scoped -declarations. - -The name `var` is also less controversial because it clearly stands for -“variable” in a less ambiguous way than using `let` to stand for "named -constant". If there is desire to rename `var` it would be an orthogonal -discussion to this one and should be kept separate. - -## Rolling out this proposal smoothly - -If we think this proposal is a good idea, then I think we should stage this to -make adoption more smooth and less disruptive. Rolling this out would look like -this: - -1. Build consensus with the Mojo community to get feedback and additional - perspective. -2. Do the engineering work to validate there is no performance hit etc, and - eliminate the IR representation and behavior for `let`. At this phase we - will keep parsing them for compatibility: parse them into the same IR as a - `var`, but emit a warning “let has been deprecated and will be removed in - the next release” with a fixit hint that renames the `let` to `var`. -3. In a release ~1 month later, change the warning into an error. -4. In a release ~1 month later, remove the keyword entirely along with the error - message. - -## Alternatives considered - -We can always keep this around and re-evaluate later. That said, I don’t think -anything will change here - the Mojo user community (both external to Modular -and internal) has already run into this several times, and this will keep coming -up. diff --git a/proposals/value-ownership.md b/proposals/value-ownership.md deleted file mode 100644 index 428f236503..0000000000 --- a/proposals/value-ownership.md +++ /dev/null @@ -1,490 +0,0 @@ - -# Value ownership design for Mojo - -**Written**: Jan 2, 2023 - -**Status**: Implemented but the design has been refined, kept for historical interest. - -This document explores a design for ownership support in Mojo. This learns from other contemporary languages including C++, Rust, Swift and Val, providing a novel blend that should integrate well with our base Python syntax, and be powerful enough to express a wide range of kernel and systems programming applications. - -Rust is the language that most people will naturally think of in this space, and when compared to it, I expect that we will provide support for a wider range of types than Rust, yet provide a more familiar programming model and friendly API than it. While I assume that we will extend this to support lifetime types for better generality and safety with reference semantic types, that design is not included in this proposal. - - -# Motivation and Background - -Modern systems languages aspire to provide memory safety, good low-level performance, and allow advanced library developers to build expressive high-level APIs that are easy to use by less experienced API users. - -In the case of Mojo, we have two specific “now” problems to solve: - - - -1. We need to provide a way to allocate memory for a Tensor-like type, and given our existing support for raising exceptions, we need for them to be cleaned up. Thus we need destructors. We also need to disable copying of this sort of type. -2. We also need to implement transparent interop with CPython with an “object” struct. This requires us to have copy constructors and destructors so we can maintain the CPython reference count with an ergonomic Python-like model. - -Over time, we want Mojo to be a full replacement for the C/C++ system programming use cases in Python, unifying the “two world problem” that Python has with C. This is important because we want to have a unifying technology, and because CPUs will always be an important accelerator (and are fully general), and because our bet is that accelerators will get more and more programmable over time. - - -## Related Work - -I am not going to summarize the related work fully here, but I recommend folks interested in this topic to read up on relevant work in the industry, including: - - - -1. C++’11 r-value references, move semantics, and its general modern programming model. It is yucky and has lots of problems, but is table-stakes knowledge and powers a tremendous amount of the industry. -2. Have a programmer-level understanding of [Rust’s Memory Ownership model](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html), and read [the Rustonomicon](https://doc.rust-lang.org/nomicon/) end-to-end for bonus points. -3. Swift has a quite different approach which made some mistakes (Swift suffers from pervasive implicit copying) but has nice things in its [initialization design](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization), [ARC design](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html), [exclusivity enforcement](https://www.swift.org/blog/swift-5-exclusivity/) [[more details](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md)], etc. -4. The [Val programming language](https://www.val-lang.dev/) is an early phase research system that is learning from Swift and trying to provide a much simpler programming model than Rust does with other advantages. It isn’t at all clear if it will be expressive enough to be useful at this point though. - -C++ and Rust are the most widely known in this space and they make different tradeoffs in defaults and what it means for a type author (ignoring lifetimes, which C++ lacks and is therefore generally unsafe). A few random observations that are related to the commentary below: - - - -* Rust in particular defaults to move’ing values but allows types to opt out of that by implementing the Copy trait. -* Rust’s type system doesn’t appear to support values like `std::atomic` which require a pinned address in memory (Rust assumes it can transport things around at will), nor does it support things like `llvm::SmallVector` which is movable, but has an interior pointer so needs custom move constructors. -* Because Rust defaults to moving everything around, it puts a huge amount of pressure on memcpy and LLVM memcpy optimization (e.g. see Patrick Walton’s recent work to improve this). In my opinion, this is a self imposed mistake that we can correct structurally. -* Rust could support C++-like moves from values that leave them inert-and-to-be-destroyed, but does not do that. For lack of this, there is a lot of complexity and unsafe APIs required (e.g. [Drain](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.drain) and other things) when you want to construct an array progressively or take elements out of an array. - - -## Relevant Existing Mojo Type System Features - -The Mojo design currently has a few basic features that are precursors to proper ownership. The design below makes significant extensions and some changes to it. - - -### L-Values and R-Values - -Lvalues and Rvalues follow a conventional design found in many programming languages: an Rvalue is an immutable value that can be passed around by copying its bits by-value in an SSA register. An Lvalue is mutable, represented by its address, and can be promoted to an Rvalue with a “load”. - - -### Argument Conventions - -Functions can be declared to take any of their arguments by-reference, with an & sigil in the function definition: - - -```mojo - fn globalFn(a: Int, b&: Int): - b = a # ok - a = 4 # error - - struct Vec: - ... - fn push_back(self&, item: Int): ... # mutable self - fn size(self): ... # immutable self - - fn workWithVecs(a: Vec, b&: Vec): - use(a.size()) # ok - use(b.size()) # ok - - a.push_back(4) # Error, a isn't mutable - b.push_back(4) # ok -``` - - -As shown, by-ref arguments are Lvalues and thus may be mutated, otherwise arguments are passed by-value as immutable Rvalues. There is another similar-but-different thing going on with “def” functions. By-value arguments are passed by copy but are allowed to be mutable for better compatibility with Python semantics: - - -```mojo - def exampleDef(a: Int, b&: Int): - b = 4 # mutable as before. - - # passed by value into mutable copy. - # This change isn't visible in the caller. - a = 4 -``` - - -The ‘def’ semantics are implemented by taking a value in by copy and introducing a local ‘var’ that shadows the argument. It is therefore a purely local transformation that we’ll ignore in the rest of this document. - - -### Type initializers - -Mojo supports Python type initializers and currently allows them to participate in implicit conversions: - - -```mojo - struct Int: - var value : __mlir_type.index - - fn __init__(value: __mlir_type.index) -> Int: - return Int { value: value } -``` - - - -# Proposed Mojo Additions / Changes - -This proposal introduces a number of new concepts to cover the space of value ownership and parameter passing conventions that Rust, Swift and C++ provide, but in a different way. We layer on the features in individual groups. Let’s explore them one piece at a time. - - -## Extended parameter conventions - -We should extend the existing type system to support owning (aka moving, aka consuming) parameter convention, immutable borrow semantics (“`&T"` in Rust, “`const T&"` in C++) and mutable borrow semantics (“`&mut T`” in Rust, and “`T&`” in C++). - - -### Change & to mean mutable borrow or “inout” - -Right now, & is a C++-like reference semantic sigil, we should keep it, but change it to mean a “mutable borrow” in Rust terminology or “inout” using Swift terminology. This won’t require Mojo source changes, but will be a terminology change inside of the compiler. The definitive initialization pass (below) will need to enforce its correct semantics. - - -Tangential to this proposal, we could require the use of `&` on the caller side of arguments passing a mutable borrow to make it more clear in the caller code. If we do this, I propose that it be a postfix operator, and use it for methods for consistency: - -```mojo - swap(x.first&, y.first&) # Obvious what is being mutated - b&.push_back(42) # Obvious that b is mutated. -``` - -This proposal will not use this syntax below, and this is probably way too -onerous for methods, this is probably a bad idea. - -### Introduce “owned” argument conventions - -We need a way to specify that ownership of a value is being passed into the function: - -```mojo - # This takes ownership of a unique vector, including its resources. - fn someFunction(owned v: Vec): - print(v) - v.push_back(42) # Ok: v is mutable because we own it -``` - - -In the code above, we show “owned v” takes and owns a value from the caller. Just like normal arguments, we would need a copy to get them as mutable: - - -```mojo - fn anotherFunction(a: Int, owned v: Vec): - a += 1 # Error: a is immutable - var a2 = a # Copy the argument to make it mutable - a2 += 1 # Ok, a2 is mutable. - - v.push_back(a) # Ok, owned values are mutable - var v2 = v^ # Transfer v argument into a new var binding. - v2.push_back(a) # Also ok -``` - - -This should be an owned _reference_ and thus lower to LLVM pointers, just like a mutable reference does, unless the value is `@register_passable` (see below). Not doing this would significantly impact our ability to model things like `std::atomic` and `SmallVector` whose address is significant. See the “extensions” at the bottom for why this will be efficient for trivial types like integers. - - -### Introduce “borrowed” argument conventions and change default - -Similarly we need the ability to specify that we are passing and returning an immutable borrow, which is like a `'const &`’ in C++. The spelling of this isn’t particularly important because it will frequently be defaulted, but we need something concrete to explain the examples. - -```mojo - # This takes a borrow and return it. - # Returned references need lifetimes for safety of course. - fn printSizeAndReturn(borrowed v: Vec) -> borrowed Vec - print(v.size()) - return v -``` - -At the LLVM level, immutable references are passed by pointer, just like mutable references. - -Note that C++ and Rust entered into a strange battle with programmers about how to pass values by default. Templates generally use “`const&`” to avoid copies, but this is an inefficient way to pass trivial types like “`int`”. We propose a type level annotation to directly address, which allows us to use borrow far more pervasively for arguments in the language. See the ‘extensions’ section below. - -### Change the default argument and result conventions - -Now we have the ability to express ownership clearly, but we don’t want all code everywhere to have words like `borrowed` on it: we want more progressive disclosure of complexity and better defaults. - -The first piece of this is the default return value convention. The right default convention for return values is to be owned: - - -```mojo - # Result defaults to being an owned reference. - fn getVec() -> Vec: # Equivalent to "...) -> Vec^" - ... -``` - -because we otherwise have no way to return newly created values. Code can override the return convention by using another sigil, e.g. `inout Vec` if a mutable reference is required. - -We also need to decide what to do with arguments. We don’t want to copy arguments by default (a mistake Swift made), because this requires types to be copyable, and depends on unpredictable copy elision that makes performance and COW optimization sad. I don’t think we want to depend on pass-by-move like Rust did because Rust forces tons of things to be marked “&” pervasively, this introduces a ton of `memcpy` operations, and Python programmers won’t think that passing a value to a function makes it unavailable for use by other things by default. - -In my opinion, the C++ got this almost right: `const&` is the right default argument convention (and is optimized to register value passing in an opt-in way, described below). This is good for both self and value arguments and is what Swift semantically did with its +0 ownership convention in a bunch of places. Consider this example: - -```mojo - struct Dictionary: - fn size(self) -> Int: ... -``` - -You don’t want to **copy the dictionary**, when calling size! This worked for Swift because of ARC optimizations and that its Dictionary type was a small thing implemented with COW optimizations, but this is very unpredictable. - -Passing arguments-by-borrow by default is also great because it eliminates the pervasive need to do a load operation when converting Lvalues to Rvalues. This is a huge improvement in the model, because “loading” a value is extremely expensive when the value is large or cannot be loaded (e.g. variable sized types, e.g. some languages representation for existential types and Rust’s DSTs, which we may or may not want to support anyway). - -It also means that we can support types like `std::atomic` which need a guaranteed ‘self’ address - natively and with no trouble - since we’re never trying to implicitly load the value as a whole. - -## Adding support for value destruction - -Now that we have a notion of ownership, we can complete it by destroying values when their lifetime has ended. This requires introducing the ability to declare a destructor, and the machinery to determine when to invoke the destructor. - -### User defined destructors - -We should embrace the existing Python convention of implementing the `__del__` method on a type. This takes ownership of the self value, so it should be defined as taking `owned self`. Here’s a reasonable implementation of Vec’s ctors and dtor, but without the push_back and associated methods (which are obvious): - -``` - struct Vec: - var data: Pointer # I just made this type up. - var capacity: Int - var size: Int - fn __init__(inout self, capacity: Int): - self.data = Pointer.malloc(capacity) - self.capacity = capacity - self.size = 0 - - # default args will be nice some day - fn __new__(inout self): return Vec(1) - fn __del__(owned self): # owning reference to self. - # Any int values don't need to be destroyed. - self.data.free() -``` - -There is some nuance here and a special case that we need to handle in the `__del__` method. Ideally, we should track the field sensitive liveness of the ‘self’ member that comes into del. This will allow us to handle sub-elements that are individually consumed, safely handle exceptions that early-exit from the destructor etc. This is something that Swift gets right that Rust apparently doesn’t. - -With respect to the simple definition of Vec above, it is enough to define a safe vector of integers which is creatable, destroyable, can be passed by borrowed and mutable reference, but isn’t enough to support movability or copyability. We’ll add those later. - -### When do we invoke destructors for value bindings? - -Now that we have a way to define a destructor for a value, we need to invoke it automatically. Where do we invoke the destructor for a local value binding? Two major choices exist: - -1. End of scope, ala C++ (and I think Rust). -2. After last use, ala Swift and Val (but Val has a better model). - -The difference can be seen in cases like this: - -```mojo - fn bindingLifetimeExample(): - var vec = Vec() - vec.push_back(12) - use(vec) - # Val/Swift destroys 'vec' here. - do_lots_of_other_stuff_unrelated_to_vec() - # C++/Rust destroy vec here. -``` - -I would advocate for following the Swift model. It reduces memory use, and I haven’t seen it cause problems in practice - it seems like the right default. Furthermore, this dovetails well with ownership, because you want (e.g.) immutable borrows to die early so you can form mutable borrows in other statements. It also makes the “form references within a statement” special case in C++ go away. - -The tradeoff on this is that this could be surprising to C++ programmers, something that Swift faced as well. The balance to that is that GC languages with finalizers are not used for RAII patterns, and Python has first-class language support for RAII things (the `with` statement). - -There are specific cases like RAII that want predictable end-of-scope destruction, so you end up needing a `@preciseLifetime` decorator on the struct or use closures - [both work fine](https://developer.apple.com/documentation/swift/withextendedlifetime(_:_:)-31nv4). - -NOTE: This was pushed forward during implementation to the "ASAP" model that -Mojo uses. - -### Taking a value from a binding - -The other thing you may want to do is to intentionally end a binding early, transferring ownership of the bound value out as an owned rvalue. Swift and Rust both support mutable value lifetimes with holes in them, and ending immutable bindings early (Rust with the `drop(x)` operator or by moving out of the binding, Swift with the recently proposed [consume/take/move](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md) operation). - -I propose supporting this with the `^` postfix operator, e.g.: - -```mojo - fn takeVec(owned v: Vec): ... - - fn showEarlyBindingEnd(): - var vec = Vec() - vec.push_back(12) - takeVec(vec^) # Ownership of vec is transferred to takeVec. - do_lots_of_other_stuff_unrelated_to_vec() - - var vec2 = Vec() - vec2.push_back(12) - ... - _ = vec2^ # force drop vec2. -``` - -This is postfix so it composes better in expressions, e.g. “`someValue^.someConsumingMethod()`”. - -### Supporting “taking” a value, with a convention (+ eventually a Trait) - -I believe it is important for common types to support a “destructive take” to support use-cases where you want to std::move an element out of the middle of a `std::vector`. C++ has `std::move` and move constructors for this, and Rust has a ton of complexity to work around the lack of this. Swift doesn’t appear to have a story for this yet. I think we just use a method convention (eventually formalized as a trait) where types who want it define a `take()` method: - -``` - struct Vec: - ... - fn __moveinit__(inout self, inout existing): - # Steal the contents of 'existing' - self.data = existing.data - self.capacity = existing.capacity - self.size = existing.size - - # Make sure 'existing's dtor doesn't do bad things. - existing.data = None - existing.size = 0 - existing.capacity = 0 -``` - -This is analogous to defining a move constructor in C++. Note that you only need to define this if you want to support this operation, and we eventually should be able to synthesize this as a default implementation of the “Takable” trait when we build out traits and metaprogramming features. - -## Value Lifetime Enforcement - -Now that we have all the mechanics in place, we actually have to check and enforce lifetime in the compiler. This entails a few bits and pieces. - -### Implement a “Definitive Initialization” Like Pass: CheckLifetimes - -The first thing we need is a dataflow pass that tracks the initialization status of local bindings. The basic mechanics needed here are implemented in the Swift Definitive Initialization pass, and a lot of the mechanics are well described in the [Drop Flags section of the Rustonomicon](https://doc.rust-lang.org/nomicon/drop-flags.html) and [slide 135+ in this talk](https://www.llvm.org/devmtg/2015-10/slides/GroffLattner-SILHighLevelIR.pdf). This is a combination of static analysis and dynamically generated booleans. - -When building this, we have the choice to implement this in a field sensitive way. I believe that this is a good thing to do in the medium term, because that will allow making `__new__` and `__del__` methods much easier to work with in common cases, and will compose better when we get to classes. That said, we should start with simple non-field-sensitive cases and extend it over time. This is what we did when bringing up Swift and it worked fine. - -### Implement support for variable exclusivity checking - -While it isn’t a high priority in the immediate future, we should also add support for variable exclusivity checking to detect dynamic situations where aliases are formed. See the [Swift proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md) for details on the issues involved here. Mojo will be working primarily with local variables in the immediate future, so we can get by with something very simple for the immediate future. - -### Synthesize destructors for structs - -The other thing necessary in the basic model is for the destructors of field members to be run as part of `__del__` methods on structs. We don’t want people to have to write this manually, we should synthesize a `__del__` when needed. For example in: - -```mojo - struct X: - var a: T1 - var b: T2 - - fn __del__(owned self): # This should be synthesized. - _ = self.a^ - _ = self.b^ -``` - - - -## Extensions to make the programming model nicer - -With the above support, we should have a system that is workable to cover the C++/Rust use-cases (incl `std::atomic` and `SmallVector`), handle the motivating Tensor type and Python interop, as well as provide a safe programming model (modulo dangling reference types which need lifetimes to support). That said, we still won’t have a super nice programming model, this includes some “table stakes” things we should include even though they are not strictly necessary. - -In particular, the support above completely eliminated the need to copy and move values, which is super pure, but it would be impractically painful to work with simple types that have trivial copy constructors. For example: - -```mojo - fn useIntegers(a: Int): - var b = a+4 # ok, b gets owned value returned by plus operator. - let c = b # error, cannot take ownership from an lvalue. - let c2 = b.copy() # Ok, but laughable for Int. -``` - -It is worth saying that the “c = b” case is something that we explicitly want to prohibit for non-trivial types like vectors and dictionaries: Swift implicitly copies the values and relies on COW optimization and compiler heroics (which are not amazingly great in practice) to make it “work”. Rust handles it by moving the value away, which breaks value semantics and a programmer model that people expect from Python. - -It is better for vectors and Dictionary’s (IMO) to make this a compile time error, and say “this non-copyable type cannot be copied”. We can then standardize a `b.copy()` method to make the expense explicit in source code. - -### Copy constructors for implicitly copyable types - -The solution to both of these problems is to allow types to opt-in to copyability (as in the Rust `Copy` trait). The obvious signature for this in Mojo seems to be a `__copyinit__` implementation (eventually formalized as a trait): - -```mojo - struct Int: - var value: __mlir_type.index - fn __copyinit__(inout self, borrowed existing: Int): - self.value = existing.value -``` - -Given this new initializer, a type that implements this is opt-ing into being implicitly copied by the compiler. This (re)enables lvalue-to-rvalue conversion with a “load” operation, but makes it explicit in user source code. It allows integers to work like trivial values, and allows the library designer to take control of what types support implicit copies like this. This is also required for the Python interop “object” type, since we obviously want `x = y` to work for Python objects! - -One nice-to-have thing that we should get to eventually (as we build out support for traits and metaprogramming) is `Copyable` trait with a default implementation. This would allow us to manually provide a copy constructor if we want above, or get a default synthesized one just by saying that our struct is `Copyable`. See the appendix at the end of the document for more explanation of how this composes together. - -All together, I believe this will provide a simple and clean programming model that is much more predictable than the C++ style, and is more powerful than the Swift or Rust designs, which don’t allow custom logic. - -### Opting into pass-by-register parameter convention - -The final problem we face is the inefficiency of passing small values by-reference everywhere. This is a problem that is internalized by C++ programmers through common wisdom (“pass complex values like `std::vector` as `const& or rvalue-ref` but trivial values like `int` by value!”), but ends up being a problem for some generic templated code - e.g. `std::vector` needs to declare many things as being passed by `const&` or rvalue reference, which becomes inefficient when instantiated for trivial types. There are ways to deal with this in C++, but it causes tons of boilerplate and complexity. - -The key insight I see here is that the decision is specific to an individual type, and should therefore be the decision of the type author. I think the simple way to handle this is to add a struct decorator that opts the struct into being passed by owning copy, equivalent to the Rust Copy convention: - -```mojo - @register_passable - struct Int: - ... -``` - -This decorator would require that the type have a copy constructor declared, and it uses that copy constructor in the callee side of an API to pass arguments by-register and return by-register. This would lead to an efficient ABIs for small values. - -This decorator should only be used on small values that makes sense to pass in registers or on the stack (e.g. 1-3 machine registers), and cannot be used on types like `llvm::SmallVector` that have interior pointers (such a type doesn’t make sense to pass by-register anyway!). - -# Conclusion - -This proposal attempts to synthesize ideas from a number of well known systems into something that will fit well with Mojo, be easy to use, and easy to teach - building on the Python ideology of “reducing magic”. It provides equivalent expressive power to C++, while being a building block to provide the full power for Rust-style lifetimes. - -## Parameter Conventions Summary - -This is the TL;DR: summary of what I think we end up with: - -```mojo - fn borrow_it(a: X) # takes X as borrow (sugar). - fn borrow_it(borrowed a: X) # takes X as borrow. - fn take_it(owned a: X) # takes owned X - fn ref_it(inout a: X) # takes mutable reference to X - fn register_it(a: Int) # by copy when Int is register-passable. - - fn ret_owned(self) -> X: # Return an owned X (sugar). - fn ret_owned(self) -> owned X: # Return an owned X. - fn ret_borrow(self) -> borrowed X: # Return an X as a borrow - fn ret_ref(self) -> inout X: # Return an X as a mutable ref - fn ret_register(self) -> Int: # Return by copy when Int is register-passable -``` - -## Extension for Lifetime types - -Lifetimes are necessary to support memory safety with non-trivial correlated lifetimes, and have been pretty well proven in the Rust world. They will require their own significant design process (particularly to get the defaulting rules) and will benefit from getting all of the above implemented. - -That said, when we get to them, I believe they will fit naturally into the Mojo and MLIR design. For example, you could imagine things like this: - -```mojo - # Take a non-copyable SomeTy as a borrow and return owned copy - fn life_ex1['a: lifetime](value: 'a SomeTy) -> SomeTy: - return value.copy() - - # Take a non-copyable SomeTy and return the reference - fn life_ex2['a: lifetime](value: 'a SomeTy) -> borrowed 'a SomeTy: - return value -``` - - -This is not a concrete syntax proposal, just a sketch. A full design is -outside the scope of this proposal though, it should be a subsequent one. - -# Appendix: Decorators and Type Traits - -Above we talk loosely about decorators and type traits. A decorator in Python is a modifier for a type or function definition. A Trait in Rust (aka protocol in Swift, aka typeclass in Haskell) is a set of common behavior that unifies types - sort of like an extended Java interface. - -Let’s see how these two concepts can come together in the future assuming we get Swift/Rust-style traits and extend Python’s decorator concept with metaprogramming features enabled by Mojo. - -Traits include “requirements”: signatures that conforming types are required to have, and may also include default implementations for those. The type can implement it manually if they want, but can also just inherit the default implementation if not. Let’s consider copy-ability. This isn’t a standard library proposal, but we could implement some copy traits like this: - -```mojo - trait Copyable: - # Just a signature, no body. - fn copy(self) -> Self: ... - - trait ImplicitlyCopyable(Copyable): # Could use a better name :) - # A __copyinit__ is required, and this is the default implementation. - fn __copyinit__(inout self, borrowed existing: Self): - self = existing.copy() -``` - -Type may conform to the `Copyable` trait, which allows generic algorithms to know it has a `copy()` method. Similarly, they may conform to `ImplicitlyCopyable` to know it is implicitly copable (supports “`let a = b`”). `ImplicitlyCopyable` requires the type to have a `copy()` method (because `ImplicitlyCopyable` refines `Copyable`) and a `__copyinit__` method with the specified signatures, and also provides a default implementation of the `__copyinit__` method. - -This allows types to use it like this: - -```mojo - struct Int(ImplicitlyCopyable): - var value: __mlir_type.index - fn copy(self: Self) -> Self: - return Int{value: self.value} - - # Don't have to write this, we get a default impl from ImplicitlyCopyable - # fn __copyinit__(inout self, borrowed existing: Self): - # self = existing.copy() -``` - -This allows clients to implement a simple `copy()` method, but get the internal machinery for free. - -The decorator is a different thing that layers on top of it. Decorators in Python are functions that use metaprogramming to change the declaration they are attached to. Python does this with dynamic metaprogramming, but we’ll use the interpreter + built-ins operations to also enable static metaprogramming in Mojo. - -I'm imagining that this will allow someone to write just: - -```mojo - @implicitlyCopyable - struct Int: - var value : __mlir_type.index -``` - -And the `implicitlyCopyable` function (which implements the decorator) would be implemented to do two things: - -1. When it understands all the stored properties of a copy, because they are built-in MLIR types like index, or because they themselves conform to at least `Copyable`, it synthesizes an implementation of a `copy()` method that builds a new instance of the type by invoking the `copy()` member for each element. -2. It adds conformance to the `ImplicitlyCopyable` trait, which provides the `__copyinit__` method above. - -This is all precedented in languages like Swift and Rust, but they both lack the metaprogramming support to make the decorator synthesis logic implementable in the standard library. Swift does this for things like the Hashable and `Equatable` protocols. I believe that Mojo will be able to support much nicer and more extensible designs. - -NOTE: The `@value` decorator provides some of this now. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b33aaaa681..423f2b27db 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -164,6 +164,26 @@ fn int[T: IntableRaising](value: T) raises -> Int: return value.__int__() +fn int(value: String, base: Int = 10) raises -> Int: + """Parses the given string as an integer in the given base and returns that value. + + For example, `atol("19")` returns `19`. If the given string cannot be parsed + as an integer value, an error is raised. For example, `atol("hi")` raises an + error. + + If base is 0 the the string is parsed as an Integer literal, + see: https://docs.python.org/3/reference/lexical_analysis.html#integers + + Args: + value: A string to be parsed as an integer in the given base. + base: Base used for conversion, value must be between 2 and 36, or 0. + + Returns: + An integer value that represents the string, or otherwise raises. + """ + return atol(value, base) + + # ===----------------------------------------------------------------------=== # # Int # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 46a6e91a31..779d027644 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -150,28 +150,39 @@ fn chr(c: Int) -> String: # ===----------------------------------------------------------------------===# -# TODO: this is hard coded for decimal base @always_inline -fn _atol(str_ref: StringRef) raises -> Int: - """Parses the given string as a base-10 integer and returns that value. +fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: + """Parses the given string as an integer in the given base and returns that value. For example, `atol("19")` returns `19`. If the given string cannot be parsed as an integer value, an error is raised. For example, `atol("hi")` raises an error. + If base is 0 the the string is parsed as an Integer literal, + see: https://docs.python.org/3/reference/lexical_analysis.html#integers + Args: - str_ref: A string to be parsed as a base-10 integer. + str_ref: A string to be parsed as an integer in the given base. + base: Base used for conversion, value must be between 2 and 36, or 0. Returns: An integer value that represents the string, or otherwise raises. """ + if (base != 0) and (base < 2 or base > 36): + raise Error("Base must be >= 2 and <= 36, or 0.") if not str_ref: - raise Error("Empty String cannot be converted to integer.") + raise Error(_atol_error(base, str_ref)) + + var real_base: Int + var ord_num_max: Int + + var ord_letter_max = (-1, -1) var result = 0 var is_negative: Bool = False var start: Int = 0 var str_len = len(str_ref) var buff = str_ref._as_ptr() + for pos in range(start, str_len): if isspace(buff[pos]): continue @@ -179,53 +190,150 @@ fn _atol(str_ref: StringRef) raises -> Int: if str_ref[pos] == "-": is_negative = True start = pos + 1 + elif str_ref[pos] == "+": + start = pos + 1 else: start = pos break alias ord_0 = ord("0") - alias ord_9 = ord("9") + # FIXME: + # Change this to `alias` after fixing support for __refitem__ of alias. + var ord_letter_min = (ord("a"), ord("A")) + alias ord_underscore = ord("_") + + if base == 0: + var real_base_new_start = _identify_base(str_ref, start) + real_base = real_base_new_start[0] + start = real_base_new_start[1] + if real_base == -1: + raise Error(_atol_error(base, str_ref)) + else: + real_base = base + + if real_base <= 10: + ord_num_max = ord(str(real_base - 1)) + else: + ord_num_max = ord("9") + ord_letter_max = ( + ord("a") + (real_base - 11), + ord("A") + (real_base - 11), + ) + + var found_valid_chars_after_start = False var has_space_after_number = False + # single underscores are only allowed between digits + # starting "was_last_digit_undescore" to true such that + # if the first digit is an undesrcore an error is raised + var was_last_digit_undescore = True for pos in range(start, str_len): - var digit = int(buff[pos]) - if ord_0 <= digit <= ord_9: - result += digit - ord_0 - elif isspace(digit): + var ord_current = int(buff[pos]) + if ord_current == ord_underscore: + if was_last_digit_undescore: + raise Error(_atol_error(base, str_ref)) + else: + was_last_digit_undescore = True + continue + else: + was_last_digit_undescore = False + if ord_0 <= ord_current <= ord_num_max: + result += ord_current - ord_0 + found_valid_chars_after_start = True + elif ord_letter_min[0] <= ord_current <= ord_letter_max[0]: + result += ord_current - ord_letter_min[0] + 10 + found_valid_chars_after_start = True + elif ord_letter_min[1] <= ord_current <= ord_letter_max[1]: + result += ord_current - ord_letter_min[1] + 10 + found_valid_chars_after_start = True + elif isspace(ord_current): has_space_after_number = True start = pos + 1 break else: - raise Error("String is not convertible to integer.") + raise Error(_atol_error(base, str_ref)) if pos + 1 < str_len and not isspace(buff[pos + 1]): - var nextresult = result * 10 + var nextresult = result * real_base if nextresult < result: raise Error( - "String expresses an integer too large to store in Int." + _atol_error(base, str_ref) + + " String expresses an integer too large to store in Int." ) result = nextresult + + if was_last_digit_undescore or (not found_valid_chars_after_start): + raise Error(_atol_error(base, str_ref)) + if has_space_after_number: for pos in range(start, str_len): if not isspace(buff[pos]): - raise Error("String is not convertible to integer.") + raise Error(_atol_error(base, str_ref)) if is_negative: result = -result return result -fn atol(str: String) raises -> Int: - """Parses the given string as a base-10 integer and returns that value. +fn _atol_error(base: Int, str_ref: StringRef) -> String: + return ( + "String is not convertible to integer with base " + + str(base) + + ": '" + + str(str_ref) + + "'" + ) + + +fn _identify_base(str_ref: StringRef, start: Int) -> Tuple[Int, Int]: + var length = len(str_ref) + # just 1 digit, assume base 10 + if start == (length - 1): + return 10, start + if str_ref[start] == "0": + var second_digit = str_ref[start + 1] + if second_digit == "b" or second_digit == "B": + return 2, start + 2 + if second_digit == "o" or second_digit == "O": + return 8, start + 2 + if second_digit == "x" or second_digit == "X": + return 16, start + 2 + # checking for special case of all "0", "_" are also allowed + var was_last_character_underscore = False + for i in range(start + 1, length): + if str_ref[i] == "_": + if was_last_character_underscore: + return -1, -1 + else: + was_last_character_underscore = True + continue + else: + was_last_character_underscore = False + if str_ref[i] != "0": + return -1, -1 + elif ord("1") <= ord(str_ref[start]) <= ord("9"): + return 10, start + else: + return -1, -1 + + return 10, start + + +fn atol(str: String, base: Int = 10) raises -> Int: + """Parses the given string as an integer in the given base and returns that value. For example, `atol("19")` returns `19`. If the given string cannot be parsed as an integer value, an error is raised. For example, `atol("hi")` raises an error. + If base is 0 the the string is parsed as an Integer literal, + see: https://docs.python.org/3/reference/lexical_analysis.html#integers + Args: - str: A string to be parsed as a base-10 integer. + str: A string to be parsed as an integer in the given base. + base: Base used for conversion, value must be between 2 and 36, or 0. Returns: An integer value that represents the string, or otherwise raises. """ - return _atol(str._strref_dangerous()) + return _atol(str._strref_dangerous(), base) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index c9042536a4..29bdfe5f70 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -10,9 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# TODO(37393): Reenabl once we debug why we are depending on some debug behavior -# on graviton. -# REQUIRES: Disabled # RUN: %mojo %s from builtin.string import ( @@ -186,11 +183,11 @@ fn test_ord() raises: alias single_byte2 = ord("!") assert_equal(single_byte2, 33) - alias multi_byte = ord("α") + var multi_byte = ord("α") assert_equal(multi_byte, 945) - alias multi_byte2 = ord("➿") + var multi_byte2 = ord("➿") assert_equal(multi_byte2, 10175) - alias multi_byte3 = ord("🔥") + var multi_byte3 = ord("🔥") assert_equal(multi_byte3, 128293) @@ -223,6 +220,7 @@ fn test_string_indexing() raises: fn test_atol() raises: + # base 10 assert_equal(375, atol(String("375"))) assert_equal(1, atol(String("001"))) assert_equal(5, atol(String(" 005"))) @@ -230,36 +228,116 @@ fn test_atol() raises: assert_equal(-89, atol(String("-89"))) assert_equal(-52, atol(String(" -52"))) assert_equal(-69, atol(String(" -69 "))) + assert_equal(1_100_200, atol(" 1_100_200")) + + # other bases + assert_equal(10, atol("A", 16)) + assert_equal(15, atol("f ", 16)) + assert_equal(255, atol(" FF", 16)) + assert_equal(18, atol("10010", 2)) + assert_equal(35, atol("Z", 36)) # Negative cases - try: + with assert_raises( + contains="String is not convertible to integer with base 10: '9.03'" + ): _ = atol(String("9.03")) - raise Error("Failed to raise when converting string to integer.") - except e: - assert_equal(str(e), "String is not convertible to integer.") - try: + with assert_raises( + contains="String is not convertible to integer with base 10: ' 10 1'" + ): _ = atol(String(" 10 1")) - raise Error("Failed to raise when converting string to integer.") - except e: - assert_equal(str(e), "String is not convertible to integer.") - try: + # start/end with underscore double underscores + with assert_raises( + contains="String is not convertible to integer with base 10: '5__5'" + ): + _ = atol("5__5") + + with assert_raises( + contains="String is not convertible to integer with base 10: ' _5'" + ): + _ = atol(" _5") + + with assert_raises( + contains="String is not convertible to integer with base 10: '5_'" + ): + _ = atol("5_") + + with assert_raises( + contains="String is not convertible to integer with base 5: '5'" + ): + _ = atol("5", 5) + + with assert_raises(contains="Base must be >= 2 and <= 36, or 0."): + _ = atol("0", 1) + + with assert_raises(contains="Base must be >= 2 and <= 36, or 0."): + _ = atol("0", 37) + + with assert_raises( + contains="String is not convertible to integer with base 10: ''" + ): _ = atol(String("")) - raise Error("Failed to raise when converting empty string to integer.") - except e: - assert_equal(str(e), "Empty String cannot be converted to integer.") - try: + with assert_raises( + contains="String expresses an integer too large to store in Int." + ): _ = atol(String("9223372036854775832")) - raise Error( - "Failed to raise when converting an integer too large to store in" - " Int." - ) - except e: - assert_equal( - str(e), "String expresses an integer too large to store in Int." - ) + + +fn test_atol_base_0() raises: + assert_equal(155, atol(" 155", base=0)) + assert_equal(155_155, atol("155_155 ", base=0)) + + assert_equal(0, atol(" 0000", base=0)) + assert_equal(0, atol(" 000_000", base=0)) + + assert_equal(3, atol("0b11", base=0)) + assert_equal(3, atol("0B1_1", base=0)) + + assert_equal(63, atol("0o77", base=0)) + assert_equal(63, atol(" 0O7_7 ", base=0)) + + assert_equal(17, atol("0x11", base=0)) + assert_equal(17, atol("0X1_1", base=0)) + + assert_equal(0, atol("0X0", base=0)) + + with assert_raises( + contains="String is not convertible to integer with base 0: ' 0x'" + ): + _ = atol(" 0x", base=0) + + with assert_raises( + contains="String is not convertible to integer with base 0: ' 0b '" + ): + _ = atol(" 0b ", base=0) + + with assert_raises( + contains="String is not convertible to integer with base 0: '00100'" + ): + _ = atol("00100", base=0) + + with assert_raises( + contains="String is not convertible to integer with base 0: '0r100'" + ): + _ = atol("0r100", base=0) + + with assert_raises( + contains="String is not convertible to integer with base 0: '0b_0'" + ): + _ = atol("0b_0", base=0) + + with assert_raises( + contains="String is not convertible to integer with base 0: '0xf__f'" + ): + _ = atol("0xf__f", base=0) + + with assert_raises( + contains="String is not convertible to integer with base 0: '0of_'" + ): + _ = atol("0of_", base=0) fn test_calc_initial_buffer_size_int32() raises: @@ -385,13 +463,10 @@ fn test_rfind() raises: fn test_split() raises: # Reject empty delimiters - try: + with assert_raises( + contains="empty delimiter not allowed to be passed to split." + ): _ = String("hello").split("") - raise Error("failed to reject empty delimiter") - except e: - assert_equal( - "empty delimiter not allowed to be passed to split.", str(e) - ) # Split in middle var d1 = String("n") @@ -606,6 +681,7 @@ def test_removesuffix(): def test_intable(): assert_equal(int(String("123")), 123) + assert_equal(int(String("10"), base=8), 8) with assert_raises(): _ = int(String("hi")) @@ -630,6 +706,7 @@ def main(): test_chr() test_string_indexing() test_atol() + test_atol_base_0() test_calc_initial_buffer_size_int32() test_calc_initial_buffer_size_int64() test_contains() diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 290d07630d..ca694eaae0 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -49,13 +49,11 @@ # to allow running the tests with LLVM sanitizers. config.substitutions.insert(0, ("%mojo", "mojo")) -# The `mojo` nightly compiler ships with its own `stdlib.mojopkg` For the -# open-source stdlib, we need to specify the paths to the just-built -# `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the -# `mojo` compiler would use its own `stdlib.mojopkg` it ships with which is not -# what we want. We override both the stable and nightly `mojo` import paths -# here to support both versions of the compiler. -os.environ["MODULAR_MOJO_IMPORT_PATH"] = str(build_root) +# The `mojo` nightly compiler ships with its own `stdlib.mojopkg` +# For the open-source stdlib, we need to specify the paths to the +# just-built `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, +# without this, the `mojo` compiler would use its own `stdlib.mojopkg` +# it ships with which is not what we want. os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = str(build_root) @@ -73,7 +71,6 @@ def has_not(): lit.llvm.llvm_config.with_system_environment( [ "MODULAR_HOME", - "MODULAR_MOJO_IMPORT_PATH", "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", ] ) From 7c41393ef47d999720d7a5434bd46f25e01abb9e Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 26 Apr 2024 12:57:08 -0500 Subject: [PATCH 0320/2019] [docs] Include changelog entry for `int(String, base: Int)` (#38747) Forgot to update the changelog in #38638. MODULAR_ORIG_COMMIT_REV_ID: bc7c8c74bb946cd89d8865c223633f5558e210c5 --- docs/changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 51db2004ca..4cfe878285 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -262,6 +262,15 @@ what we publish. was called) in its error message. Similarly, the `assert_*` helpers in the `testing` module now include location information in their messages. +- `int()` can now take a string and a specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. + ([PR #2273](https://github.com/modularml/mojo/pull/2273), + Fixes [#2274](https://github.com/modularml/mojo/issues/2274)) + + Additionally, if a base of zero is specified, the string will be parsed as if + it was an integer literal, with the base determined by whether the + string contains the prefix `"0x"`, `"0o"`, or `"0b"`. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From 2ba2d43b7af907fe83ac7186e80dbd8ec90dc93c Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Fri, 26 Apr 2024 12:17:25 -0600 Subject: [PATCH 0321/2019] [External] [stdlib] Remove some `# CHECK` and refactor `test_print.mojo` (#38630) [External] [stdlib] Remove some `# CHECK` and refactor `test_print.mojo` Related to #2024 Some tests in `test_print.mojo` are not related to printing at all. It's more testing the string conversion. So I took the liberty of putting them in the right files. This also helped remove some `# CHECK` as removing those when testing the print() function is really hard. Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2356 MODULAR_ORIG_COMMIT_REV_ID: 61e4a967465eda080b89d85a2c5ad020a6a38d80 --- stdlib/test/builtin/test_bool.mojo | 7 +++ stdlib/test/builtin/test_error.mojo | 13 ++++- stdlib/test/builtin/test_print.mojo | 75 +---------------------------- stdlib/test/builtin/test_simd.mojo | 60 +++++++++++++++++++++++ 4 files changed, 80 insertions(+), 75 deletions(-) diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index a987d34930..3d6599e8ff 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -43,6 +43,13 @@ def test_convert_from_boolable(): assert_true(takes_bool(MyTrue())) +def test_bool_to_string(): + assert_equal(str(True), "True") + assert_equal(str(False), "False") + + def main(): test_bool_cast_to_int() test_bool_none() + test_convert_from_boolable() + test_bool_to_string() diff --git a/stdlib/test/builtin/test_error.mojo b/stdlib/test/builtin/test_error.mojo index 54a4ec166c..1ee890241e 100644 --- a/stdlib/test/builtin/test_error.mojo +++ b/stdlib/test/builtin/test_error.mojo @@ -19,12 +19,21 @@ def raise_an_error(): raise Error("MojoError: This is an error!") -def main(): +def test_error_raising(): try: - _ = raise_an_error() + raise_an_error() except e: assert_equal(str(e), "MojoError: This is an error!") + +def test_from_and_to_string(): var myString: String = "FOO" var error = Error(myString) assert_equal(error, "FOO") + + assert_equal(str(Error("bad")), "bad") + + +def main(): + test_error_raising() + test_from_and_to_string() diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 1356384ab1..6ffb68f9c8 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -22,48 +22,17 @@ from utils import StringRef, StaticIntTuple fn test_print(): print("== test_print") - var a: SIMD[DType.float32, 2] = 5 - var b: SIMD[DType.float64, 4] = 6 - var c: SIMD[DType.index, 8] = 7 - - # CHECK: False - print(False) - - # CHECK: True - print(True) - - # CHECK: [5.0, 5.0] - print(a) - - # CHECK: [6.0, 6.0, 6.0, 6.0] - print(b) - - # CHECK: [7, 7, 7, 7, 7, 7, 7, 7] - print(c) - # CHECK: Hello print("Hello") # CHECK: World print("World", flush=True) - # CHECK: 4294967295 - print(UInt32(-1)) - - # CHECK: 184467440737095516 - print(UInt64(-1)) - - # CHECK: 0x16 - print(Scalar[DType.address](22)) - - # CHECK: 0xdeadbeaf - print(Scalar[DType.address](0xDEADBEAF)) - var hello: StringRef = "Hello," var world: String = "world!" var f: Bool = False - # CHECK: > Hello, world! 42 True False [5.0, 5.0] [7, 7, 7, 7, 7, 7, 7, 7] - print(">", hello, world, 42, True, f, a, c) + # CHECK: > Hello, world! 42 True False + print(">", hello, world, 42, True, f) # CHECK: > 3.14000{{[0-9]+}} 99.90000{{[0-9]+}} -129.29018{{[0-9]+}} (1, 2, 3) var float32: Float32 = 99.9 @@ -85,33 +54,6 @@ fn test_print(): # CHECK: Hello world print(String("Hello world")) - # CHECK: 32768 - print((UInt16(32768))) - # CHECK: 65535 - print((UInt16(65535))) - # CHECK: -2 - print((Int16(-2))) - - # CHECK: 16646288086500911323 - print(UInt64(16646288086500911323)) - - # https://github.com/modularml/mojo/issues/556 - # CHECK: [11562461410679940143, 16646288086500911323, 10285213230658275043, 6384245875588680899] - print( - SIMD[DType.uint64, 4]( - 0xA0761D6478BD642F, - 0xE7037ED1A0B428DB, - 0x8EBC6AF09C88C6E3, - 0x589965CC75374CC3, - ) - ) - - # CHECK: [-943274556, -875902520, -808530484, -741158448] - print(SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448)) - - # CHECK: bad - print(Error("bad")) - # CHECK-LABEL: test_print_end fn test_print_end(): @@ -131,20 +73,7 @@ fn test_print_sep(): print("a", 1, 2, sep="/", end="xx\n") -# CHECK-LABEL: test_issue_20421 -fn test_issue_20421(): - print("== test_issue_20421") - var a = DTypePointer[DType.uint8]().alloc(16 * 64, alignment=64) - for i in range(16 * 64): - a[i] = i & 255 - var av16 = a.offset(128 + 64 + 4).bitcast[DType.int32]().load[width=4]() - # CHECK: [-943274556, -875902520, -808530484, -741158448] - print(av16) - a.free() - - fn main(): test_print() test_print_end() test_print_sep() - test_issue_20421() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index a445741844..c02c3f6241 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -43,6 +43,64 @@ def test_simd_variadic(): assert_equal(str(SIMD[DType.index, 4](52, 12, 43, 5)), "[52, 12, 43, 5]") +def test_convert_simd_to_string(): + var a: SIMD[DType.float32, 2] = 5 + assert_equal(str(a), "[5.0, 5.0]") + + var b: SIMD[DType.float64, 4] = 6 + assert_equal(str(b), "[6.0, 6.0, 6.0, 6.0]") + + var c: SIMD[DType.index, 8] = 7 + assert_equal(str(c), "[7, 7, 7, 7, 7, 7, 7, 7]") + + # TODO: uncomment when https://github.com/modularml/mojo/issues/2353 is fixed + # assert_equal(str(UInt32(-1)), "4294967295") + assert_equal(str(UInt64(-1)), "18446744073709551615") + assert_equal(str(Scalar[DType.address](22)), "0x16") + assert_equal(str(Scalar[DType.address](0xDEADBEAF)), "0xdeadbeaf") + + assert_equal(str((UInt16(32768))), "32768") + assert_equal(str((UInt16(65535))), "65535") + assert_equal(str((Int16(-2))), "-2") + + assert_equal(str(UInt64(16646288086500911323)), "16646288086500911323") + + # https://github.com/modularml/mojo/issues/556 + assert_equal( + str( + SIMD[DType.uint64, 4]( + 0xA0761D6478BD642F, + 0xE7037ED1A0B428DB, + 0x8EBC6AF09C88C6E3, + 0x589965CC75374CC3, + ) + ), + ( + "[11562461410679940143, 16646288086500911323, 10285213230658275043," + " 6384245875588680899]" + ), + ) + + assert_equal( + str( + SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448) + ), + "[-943274556, -875902520, -808530484, -741158448]", + ) + + +def test_issue_20421(): + var a = DTypePointer[DType.uint8]().alloc(16 * 64, alignment=64) + for i in range(16 * 64): + a[i] = i & 255 + var av16 = a.offset(128 + 64 + 4).bitcast[DType.int32]().load[width=4]() + assert_equal( + av16, + SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448), + ) + a.free() + + def test_truthy(): alias dtypes = ( DType.bool, @@ -660,6 +718,8 @@ def test_mul_with_overflow(): def main(): test_cast() test_simd_variadic() + test_convert_simd_to_string() + test_issue_20421() test_truthy() test_floordiv() test_mod() From 7e907114c53f60024b03b6cba6de760098cf975b Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 26 Apr 2024 13:21:17 -0700 Subject: [PATCH 0322/2019] [Docs] Add contributors to the changelog (#38760) Adds contributor with links to GitHub next to each PR with `by @contributor`. Also has a small fix to indentation with one of the changes. --------- Co-authored-by: Arthur Evans MODULAR_ORIG_COMMIT_REV_ID: 016427e539670a707dece4d78afa750033bc8934 --- docs/changelog.md | 174 ++++++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 76 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4cfe878285..7616df0554 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -69,72 +69,86 @@ what we publish. - `List` now has several new methods: - `pop(index)` for removing an element at a particular index. - ([PR #2041](https://github.com/modularml/mojo/pull/2041)) - (Fixes [#2017](https://github.com/modularml/mojo/issues/2017))\ - By default, `List.pop()` removes the last element in the list. + ([PR #2041](https://github.com/modularml/mojo/pull/2041) by + [@LJ-9801](https://github.com/LJ-9801), fixes + [#2017](https://github.com/modularml/mojo/issues/2017)). By default, + `List.pop()` removes the last element in the list. - - `resize(new_size)` for resizing the list without the need to - specify an additional value. - ([PR #2140](https://github.com/modularml/mojo/pull/2140), - Fixes [#2133](https://github.com/modularml/mojo/issues/2133)) + - `resize(new_size)` for resizing the list without the need to specify an + additional value. ([PR #2140](https://github.com/modularml/mojo/pull/2140) + by [@mikowals](https://github.com/mikowals), fixes + [#2133](https://github.com/modularml/mojo/issues/2133)) - - `insert(index, value)` for inserting a value at a specified index - into the `List`. - ([PR #2148](https://github.com/modularml/mojo/pull/2148), - Fixes [#2134](https://github.com/modularml/mojo/issues/2134)) + - `insert(index, value)` for inserting a value at a specified index into the + `List`. ([PR #2148](https://github.com/modularml/mojo/pull/2148) by + [@whym1here](https://github.com/whym1here), fixes + [#2134](https://github.com/modularml/mojo/issues/2134)) - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep - copy of an existing contiguous memory allocation when constructing a new `List`. - ([PR #2182](https://github.com/modularml/mojo/pull/2182), - Fixes [#2170](https://github.com/modularml/mojo/issues/2170)) + copy of an existing contiguous memory allocation when constructing a new + `List`. ([PR #2182](https://github.com/modularml/mojo/pull/2182) by + [@StandinKP](https://github.com/StandinKP), fixes + [#2170](https://github.com/modularml/mojo/issues/2170)) - `Dict` now has a `update()` method to update keys/values from another `Dict`. - ([PR #2085](https://github.com/modularml/mojo/pull/2085)) + ([PR #2085](https://github.com/modularml/mojo/pull/2085) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)). - `Set` now has named methods for set operations - ([PR #2214](https://github.com/modularml/mojo/pull/2214)): + ([PR #2214](https://github.com/modularml/mojo/pull/2214) by + [@arvindavoudi](https://github.com/arvindavoudi)): - `Set.difference()` mapping to `-` - `Set.difference_update()` mapping to `-=` - `Set.intersection_update()` mapping to `&=` - `Set.update()` mapping to `|=` - `String` now has `removeprefix()` and `removesuffix()` methods. - ([PR #2038](https://github.com/modularml/mojo/pull/2038)) + ([PR #2038](https://github.com/modularml/mojo/pull/2038) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) -- `Optional` now implements `__is__` and `__isnot__` methods so that you can compare - an `Optional` with `None`, e.g. `Optional(1) is not None` for example. - ([PR #2082](https://github.com/modularml/mojo/pull/2082)) +- `Optional` now implements `__is__` and `__isnot__` methods so that you can + compare an `Optional` with `None`, e.g. `Optional(1) is not None` for example. + ([PR #2082](https://github.com/modularml/mojo/pull/2082) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) -- The `ord` and `chr` functions have been improved to accept any Unicode character. - ([PR #2149](https://github.com/modularml/mojo/pull/2149), - Contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) +- The `ord` and `chr` functions have been improved to accept any Unicode + character. ([PR #2149](https://github.com/modularml/mojo/pull/2149) by + [@mzaks](https://github.com/mzaks), contributes towards + [#1616](https://github.com/modularml/mojo/issues/1616)) - `Atomic` is now movable. - ([PR #2088](https://github.com/modularml/mojo/pull/2088), - Fixes [#2026](https://github.com/modularml/mojo/issues/2026)) + ([PR #2088](https://github.com/modularml/mojo/pull/2088) by + [@StandinKP](https://github.com/StandinKP), fixes + [#2026](https://github.com/modularml/mojo/issues/2026)) - `Dict` and `List` are both `Boolable` now. - ([PR #2262](https://github.com/modularml/mojo/pull/2262)) + ([PR #2262](https://github.com/modularml/mojo/pull/2262) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` instead of raising an error. - ([PR #2225](https://github.com/modularml/mojo/pull/2225)) + ([PR #2225](https://github.com/modularml/mojo/pull/2225) by + [@artemiogr97](https://github.com/artemiogr97)) - `SIMD` now implements `__rmod__`. - ([PR #2186](https://github.com/modularml/mojo/pull/2186), - Fixes [#1482](https://github.com/modularml/mojo/issues/1482)) + ([PR #2186](https://github.com/modularml/mojo/pull/2186) by + [@bgreni](https://github.com/bgreni), fixes + [#1482](https://github.com/modularml/mojo/issues/1482)) - `bool(None)` is now implemented. - ([PR #2249](https://github.com/modularml/mojo/pull/2249)) + ([PR #2249](https://github.com/modularml/mojo/pull/2249) by + [@zhoujingya](https://github.com/zhoujingya)) -- The `DTypePointer` type now implements `gather` for gathering a `SIMD` - vector from offsets of a current pointer. Similarly, support for `scatter` - was added to scatter a `SIMD` vector into offsets of the current pointer. - ([PR #2268](https://github.com/modularml/mojo/pull/2268)) +- The `DTypePointer` type now implements `gather` for gathering a `SIMD` vector + from offsets of a current pointer. Similarly, support for `scatter` was added + to scatter a `SIMD` vector into offsets of the current pointer. + ([PR #2268](https://github.com/modularml/mojo/pull/2268) by + [@leandrolcampos](https://github.com/leandrolcampos)) - The `len` function for unary `range` with negative end values has been fixed so that things like `len(range(-1))` work correctly now. - ([PR #2204](https://github.com/modularml/mojo/pull/2204)) + ([PR #2204](https://github.com/modularml/mojo/pull/2204) by + [@soraros](https://github.com/soraros)) - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking @@ -142,9 +156,10 @@ what we publish. low-level logic but isn't designed for general usability and will likely change in the future. -- The `testing.assert_equal[SIMD]()` now raises if any of the elements - mismatch in the two `SIMD` arguments being compared. - ([PR #2279](https://github.com/modularml/mojo/pull/2279)) +- The `testing.assert_equal[SIMD]()` now raises if any of the elements mismatch + in the two `SIMD` arguments being compared. + ([PR #2279](https://github.com/modularml/mojo/pull/2279) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - The `testing.assert_almost_equal` and `math.isclose` functions now have an `equal_nan` flag. When set to True, then NaNs are considered equal. @@ -187,15 +202,17 @@ what we publish. - Added `reversed()` for creating reversed iterators. Several range types, `List`, and `Dict` now support iterating in reverse. - ([PR #2215](https://github.com/modularml/mojo/pull/2215), - [PR #2327](https://github.com/modularml/mojo/pull/2327), - Contributes towards [#2325](https://github.com/modularml/mojo/issues/2325)) + ([PR #2215](https://github.com/modularml/mojo/pull/2215) by + [@helehex](https://github.com/helehex), + [PR #2327](https://github.com/modularml/mojo/pull/2327) by + [@jayzhan211](https://github.com/jayzhan211), contributes towards + [#2325](https://github.com/modularml/mojo/issues/2325)) - `object` now supports the division, modulo, and left and right shift - operators. - ([PR #2230](https://github.com/modularml/mojo/pull/2230), - [PR #2247](https://github.com/modularml/mojo/pull/2247), - Fixes [#2224](https://github.com/modularml/mojo/issues/2224)) + operators. ([PR #2230](https://github.com/modularml/mojo/pull/2230), + [PR #2247](https://github.com/modularml/mojo/pull/2247) by + [@LJ-9801](https://github.com/LJ-9801), fixes + [#2224](https://github.com/modularml/mojo/issues/2224)) The following operator dunder methods were added: @@ -208,7 +225,8 @@ what we publish. As well as the in-place and reverse variants. - Added checked arithmetic operations. - ([PR #2138](https://github.com/modularml/mojo/pull/2138)) + ([PR #2138](https://github.com/modularml/mojo/pull/2138) by + [#lsh](https://github.com/lsh)) SIMD integral types (including the sized integral scalars like `Int64`) can now perform checked additions, substractions, and multiplications using the @@ -222,8 +240,9 @@ what we publish. the numeric limits of the type. - Added `os.remove()` and `os.unlink()` for deleting files. - ([PR #2310](https://github.com/modularml/mojo/pull/2310), - Fixes [#2306](https://github.com/modularml/mojo/issues/2306)) + ([PR #2310](https://github.com/modularml/mojo/pull/2310) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2306](https://github.com/modularml/mojo/issues/2306)) - Properties can now be specified on inline mlir ops: @@ -264,8 +283,9 @@ what we publish. - `int()` can now take a string and a specified base to parse an integer from a string: `int("ff", 16)` returns `255`. - ([PR #2273](https://github.com/modularml/mojo/pull/2273), - Fixes [#2274](https://github.com/modularml/mojo/issues/2274)) + ([PR #2273](https://github.com/modularml/mojo/pull/2273) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2274](https://github.com/modularml/mojo/issues/2274)) Additionally, if a base of zero is specified, the string will be parsed as if it was an integer literal, with the base determined by whether the @@ -331,22 +351,22 @@ what we publish. f.seek(os.SEEK_END, -32) ``` - - `FileHandle.read()` can now read straight into a `DTypePointer`: +- `FileHandle.read()` can now read straight into a `DTypePointer`: - ```mojo - var file = open("/tmp/example.txt", "r") + ```mojo + var file = open("/tmp/example.txt", "r") - # Allocate and load 8 elements - var ptr = DTypePointer[DType.float32].alloc(8) - var bytes = file.read(ptr, 8) - print("bytes read", bytes) - print(ptr.load[width=8]()) - ``` + # Allocate and load 8 elements + var ptr = DTypePointer[DType.float32].alloc(8) + var bytes = file.read(ptr, 8) + print("bytes read", bytes) + print(ptr.load[width=8]()) + ``` - `Optional.value()` will now return a reference instead of a copy of the - contained value. - ([PR #2226](https://github.com/modularml/mojo/pull/2226), - Fixes [#2179](https://github.com/modularml/mojo/issues/2179)) + contained value. ([PR #2226](https://github.com/modularml/mojo/pull/2226) by + [#lsh](https://github.com/lsh), fixes + [#2179](https://github.com/modularml/mojo/issues/2179)) To perform a copy manually, dereference the result: @@ -358,18 +378,20 @@ what we publish. - Per the accepted community proposal [`proposals/byte-as-uint8.md`](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), - began transition to using `UInt8` by changing the data pointer of `Error` - to `DTypePointer[DType.uint8]`. - ([PR #2318](https://github.com/modularml/mojo/pull/2318), - Contributes towards [#2317](https://github.com/modularml/mojo/issues/2317)) - -- Continued transition to `UnsafePointer` away from the legacy `Pointer` type - in various standard library APIs and internals. + began transition to using `UInt8` by changing the data pointer of `Error` to + `DTypePointer[DType.uint8]`. + ([PR #2318](https://github.com/modularml/mojo/pull/2318) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes + towards [#2317](https://github.com/modularml/mojo/issues/2317)) + +- Continued transition to `UnsafePointer` away from the legacy `Pointer` type in + various standard library APIs and internals. ([PR #2365](https://github.com/modularml/mojo/pull/2365), - [PR #2367](https://github.com/modularml/mojo/pull/2367), - [PR #2368](https://github.com/modularml/mojo/pull/2368), - [PR #2370](https://github.com/modularml/mojo/pull/2370), - [PR #2371](https://github.com/modularml/mojo/pull/2371)) + [PR #2367](https://github.com/modularml/mojo/pull/2367), + [PR #2368](https://github.com/modularml/mojo/pull/2368), + [PR #2370](https://github.com/modularml/mojo/pull/2370), + [PR #2371](https://github.com/modularml/mojo/pull/2371) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - `Bool` can now be implicitly converted from any type conforming to `Boolable`. This means that you no longer need to write things like this: @@ -475,6 +497,6 @@ what we publish. - [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` decorator fails gracefully after duplicate field error. -- [#2068](https://github.com/modularml/mojo/issues/2068) - Fix simd.reduce for size_out == 2 - ([PR #2102](https://github.com/modularml/mojo/pull/2102)) +- [#2068](https://github.com/modularml/mojo/issues/2068) Fix simd.reduce for + size_out == 2 ([PR #2102](https://github.com/modularml/mojo/pull/2102) by + [@soraros](https://github.com/soraros)) From 21e7097a7c6ef2220b9a243f1ea371c59a50f9f7 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Sat, 27 Apr 2024 08:06:58 -0600 Subject: [PATCH 0323/2019] [External] [mojo-stdlib] Create `InlineArray` type (#38793) [External] [mojo-stdlib] Create `InlineArray` type This PR creates an `InlineArray` type that takes any `CollectionElement` rather than just `AnyRegType`. See also [this thread](https://discord.com/channels/1087530497313357884/1228515158234501231) on Discord. --------- Co-authored-by: Lukas Hermann Closes modularml/mojo#2294 MODULAR_ORIG_COMMIT_REV_ID: d53b6de6f9ecdf035df951ce679cea2aed1b8e65 --- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 160 +++++++++++++++++++++++++++++ stdlib/test/utils/test_tuple.mojo | 90 +++++++++++++++- 3 files changed, 250 insertions(+), 2 deletions(-) diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index be51dbe6cc..c20317abe5 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -19,6 +19,6 @@ from .index import ( ) from .inlined_string import InlinedString from .loop import unroll -from .static_tuple import StaticTuple +from .static_tuple import StaticTuple, InlineArray from .stringref import StringRef from .variant import Variant diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 6b2e0d2332..f9d8bdc106 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -240,3 +240,163 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): ) Pointer(ptr).store(val) self = tmp + + +# ===----------------------------------------------------------------------===# +# Array +# ===----------------------------------------------------------------------===# + + +@value +struct InlineArray[ElementType: CollectionElement, size: Int](Sized): + """A fixed-size sequence of size homogenous elements where size is a constant expression. + + Parameters: + ElementType: The type of the elements in the array. + size: The size of the array. + """ + + alias type = __mlir_type[ + `!pop.array<`, size.value, `, `, Self.ElementType, `>` + ] + var _array: Self.type + """The underlying storage for the array.""" + + @always_inline + fn __init__(inout self): + """This constructor will always cause a compile time error if used. + It is used to steer users away from uninitialized memory. + """ + constrained[ + False, + ( + "Initialize with either a variadic list of arguments or a" + " default fill element." + ), + ]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @always_inline + fn __init__(inout self, fill: Self.ElementType): + """Constructs an empty array where each element is the supplied `fill`. + + Args: + fill: The element to fill each index. + """ + _static_tuple_construction_checks[size]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @unroll + for i in range(size): + var ptr = self._get_reference_unsafe(i) + initialize_pointee_copy(UnsafePointer[Self.ElementType](ptr), fill) + + @always_inline + fn __init__(inout self, *elems: Self.ElementType): + """Constructs an array given a set of arguments. + + Args: + elems: The element types. + """ + debug_assert(len(elems) == size, "Elements must be of length size") + _static_tuple_construction_checks[size]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @unroll + for i in range(size): + var ref = self._get_reference_unsafe(i) + initialize_pointee_move( + UnsafePointer[Self.ElementType](ref), elems[i] + ) + + @always_inline("nodebug") + fn __len__(self) -> Int: + """Returns the length of the array. This is a known constant value. + + Returns: + The size of the list. + """ + return size + + @always_inline("nodebug") + fn _get_reference_unsafe[ + mutability: __mlir_type.i1, + self_life: AnyLifetime[mutability].type, + ]( + self: Reference[Self, mutability, self_life]._mlir_type, index: Int + ) -> Reference[Self.ElementType, mutability, self_life]: + """Get a reference to an element of self without checking index bounds. + + Users should opt for `__refitem__` instead of this method. + """ + var ptr = __mlir_op.`pop.array.gep`( + Reference(Reference(self)[]._array).get_legacy_pointer().address, + index.value, + ) + return Reference[Self.ElementType, mutability, self_life]( + UnsafePointer(ptr)[] + ) + + @always_inline("nodebug") + fn __refitem__[ + mutability: __mlir_type.i1, + self_life: AnyLifetime[mutability].type, + IntableType: Intable, + ]( + self: Reference[Self, mutability, self_life]._mlir_type, + index: IntableType, + ) -> Reference[Self.ElementType, mutability, self_life]: + """Get a `Reference` to the element at the given index. + + Parameters: + mutability: The inferred mutability of the reference. + self_life: The inferred lifetime of the reference. + IntableType: The inferred type of an intable argument. + + Args: + index: The index of the item. + + Returns: + A reference to the item at the given index. + """ + debug_assert(-size <= int(index) < size, "Index must be within bounds.") + var normalized_idx = int(index) + if normalized_idx < 0: + normalized_idx += size + + return Reference(self)[]._get_reference_unsafe[mutability, self_life]( + normalized_idx + ) + + @always_inline("nodebug") + fn __refitem__[ + mutability: __mlir_type.i1, + self_life: AnyLifetime[mutability].type, + IntableType: Intable, + index: IntableType, + ](self: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ + Self.ElementType, mutability, self_life + ]: + """Get a `Reference` to the element at the given index. + + Parameters: + mutability: The inferred mutability of the reference. + self_life: The inferred lifetime of the reference. + IntableType: The inferred type of an intable argument. + index: The index of the item. + + Returns: + A reference to the item at the given index. + """ + alias i = int(index) + constrained[-size <= i < size, "Index must be within bounds."]() + + var normalized_idx = i + + @parameter + if i < 0: + normalized_idx += size + + return Reference(self)[]._get_reference_unsafe[mutability, self_life]( + normalized_idx + ) diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index ada1d3c1fc..b671e6dc41 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -14,7 +14,7 @@ from testing import assert_equal, assert_false, assert_true -from utils import StaticTuple, StaticIntTuple +from utils import StaticTuple, StaticIntTuple, InlineArray def test_static_tuple(): @@ -80,7 +80,95 @@ def test_tuple_literal(): assert_equal(len(()), 0) +def test_array_int(): + var arr = InlineArray[Int, 3](0, 0, 0) + + assert_equal(arr[0], 0) + assert_equal(arr[1], 0) + assert_equal(arr[2], 0) + + arr[0] = 1 + arr[1] = 2 + arr[2] = 3 + + assert_equal(arr[0], 1) + assert_equal(arr[1], 2) + assert_equal(arr[2], 3) + + # test negative indexing + assert_equal(arr[-1], 3) + assert_equal(arr[-2], 2) + + # test negative indexing with dynamic index + var i = -1 + assert_equal(arr[i], 3) + i -= 1 + assert_equal(arr[i], 2) + + var copy = arr + assert_equal(arr[0], copy[0]) + assert_equal(arr[1], copy[1]) + assert_equal(arr[2], copy[2]) + + var move = arr^ + assert_equal(copy[0], move[0]) + assert_equal(copy[1], move[1]) + assert_equal(copy[2], move[2]) + + # fill element initializer + var arr2 = InlineArray[Int, 3](5) + assert_equal(arr2[0], 5) + assert_equal(arr2[1], 5) + assert_equal(arr2[2], 5) + + var arr3 = InlineArray[Int, 1](5) + assert_equal(arr2[0], 5) + + +def test_array_str(): + var arr = InlineArray[String, 3]("hi", "hello", "hey") + + assert_equal(arr[0], "hi") + assert_equal(arr[1], "hello") + assert_equal(arr[2], "hey") + + # Test mutating an array through its __refitem__ + arr[0] = "howdy" + arr[1] = "morning" + arr[2] = "wazzup" + + assert_equal(arr[0], "howdy") + assert_equal(arr[1], "morning") + assert_equal(arr[2], "wazzup") + + # test negative indexing + assert_equal(arr[-1], "wazzup") + assert_equal(arr[-2], "morning") + + var copy = arr + assert_equal(arr[0], copy[0]) + assert_equal(arr[1], copy[1]) + assert_equal(arr[2], copy[2]) + + var move = arr^ + assert_equal(copy[0], move[0]) + assert_equal(copy[1], move[1]) + assert_equal(copy[2], move[2]) + + # fill element initializer + var arr2 = InlineArray[String, 3]("hi") + assert_equal(arr2[0], "hi") + assert_equal(arr2[1], "hi") + assert_equal(arr2[2], "hi") + + # size 1 array to prevent regressions in the constructors + var arr3 = InlineArray[String, 1]("hi") + assert_equal(arr3[0], "hi") + + def main(): test_static_tuple() test_static_int_tuple() test_tuple_literal() + test_array_int() + test_array_str() From 480a03fc036863b7ea93c246c58051d88a76da2e Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 27 Apr 2024 08:20:34 -0600 Subject: [PATCH 0324/2019] [External] [stdlib] Remove FileCheck in `test_object.mojo` and removed `object.print()` (#38794) [External] [stdlib] Remove FileCheck in `test_object.mojo` and removed `object.print()` Related to #2024 The rationale is in the changelog. Furthermore, it's easier to test `str()` than `print()` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2334 MODULAR_ORIG_COMMIT_REV_ID: 9c31678d0add5e3df6488f82378afecc902a8767 --- docs/changelog.md | 3 + stdlib/src/builtin/object.mojo | 41 ------- stdlib/test/builtin/test_object.mojo | 163 ++++++++++----------------- 3 files changed, 65 insertions(+), 142 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7616df0554..5a11398180 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -446,6 +446,9 @@ what we publish. reference to a value use `Reference(x)` and if you need an unsafe pointer, you can use `UnsafePointer.address_of(x)`. +- The method `object.print()` has been removed. Since now, `object` has the + `Stringable` trait, you can use `print(my_object)` instead. + ### 🛠️ Fixed - [#516](https://github.com/modularml/mojo/issues/516) and diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f5a15cbde9..f51ad06484 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1845,44 +1845,3 @@ struct object(IntableRaising, Boolable, Stringable): if not self._value.is_func(): raise Error("TypeError: Object is not a function") return self._value.get_as_func().invoke(arg0, arg1, arg2) - - # ===------------------------------------------------------------------=== # - # Debugging - # ===------------------------------------------------------------------=== # - - fn print(self): - """Prints the value of the object.""" - if self._value.is_none(): - _put("None") - elif self._value.is_bool(): - if self._value.get_as_bool(): - _put("True") - else: - _put("False") - elif self._value.is_int(): - _put(self._value.get_as_int()) - elif self._value.is_float(): - _put(self._value.get_as_float()) - elif self._value.is_str(): - var str = self._value.get_as_string() - _printf("'%.*s'", str.length, str.data) - elif self._value.is_list(): - _put("[") - for j in range(self._value.get_list_length()): - if j != 0: - _put(", ") - object(self._value.get_list_element(j)).print() - _put("]") - elif self._value.is_func(): - _printf("function at %p", self._value.get_as_func().value) - else: - _put("{") - var ptr = self._value.get_obj_attrs_ptr() - var k = 0 - for entry in ptr[].impl[].items(): - if k != 0: - _put(", ") - _printf("'%s' = ", entry[].key) - object(entry[].value.copy()).print() - k += 1 - _put("}") diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 94b251dd1e..c4bf6c264f 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -10,11 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from random import random_float64 -from testing import assert_equal, assert_false, assert_true + +from testing import assert_equal, assert_false, assert_true, assert_raises def test_object_ctors(): @@ -172,21 +173,17 @@ def test_function_raises(borrowed a) -> object: def test_object_function(): var a: object = test_function - print(a) - print(a(1, 2)) + assert_true(str(a).startswith("Function at address 0x")) + assert_equal(a(1, 2), 3) a = test_function_raises - try: + with assert_raises(contains="Error from function type"): a(1) - except e: - print(e) def test_non_object_getattr(): var a: object = [2, 3, 4] - try: + with assert_raises(contains="Type 'list' does not have attribute 'foo'"): a.foo(2) - except e: - print(e) # These are all marked borrowed because 'object' doesn't support function @@ -245,97 +242,61 @@ def test_matrix(): C.append(row_zero) matmul_untyped(C, A, B) - for k in range(size): - C[k].print() - print() + assert_equal(str(C[0]), "[5, 8, 11]") + assert_equal(str(C[1]), "[8, 14, 20]") + assert_equal(str(C[2]), "[11, 20, 29]") + + +def test_convert_to_string(): + var a: object = True + assert_equal(str(a), "True") + a = 42 + assert_equal(str(a), "42") + a = 2.5 + assert_equal(str(a), "2.5") + a = "hello" + assert_equal(str(a), "'hello'") + a = [] + assert_equal(str(a), "[]") + a.append(3) + a.append(False) + a.append(5.5) + var b: object = [] + b.append("foo") + b.append("baz") + a.append(b) + assert_equal(str(a), "[3, False, 5.5, ['foo', 'baz']]") + assert_equal(str(a[3, 1]), "'baz'") + a[3, 1] = "bar" + assert_equal(str(a[3, 1]), "'bar'") + var c = a + b + assert_equal(str(c), "[3, False, 5.5, ['foo', 'bar'], 'foo', 'bar']") + b.append(False) + assert_equal(str(c), "[3, False, 5.5, ['foo', 'bar', False], 'foo', 'bar']") + assert_equal(str(a), "[3, False, 5.5, ['foo', 'bar', False]]") + assert_equal(str(c[3]), "['foo', 'bar', False]") + b[1] = object() + assert_equal(str(a), "[3, False, 5.5, ['foo', None, False]]") + a = "abc" + b = a[True] + assert_equal(str(b), "'b'") + b = a[2] + assert_equal(str(b), "'c'") + a = [1, 1.2, False, "true"] + assert_equal(str(a), "[1, 1.2, False, 'true']") + + a = object(Attr("foo", 5), Attr("bar", "hello"), Attr("baz", False)) + assert_equal(str(a.bar), "'hello'") + a.bar = [1, 2] + assert_equal(str(a), "{'foo' = 5, 'bar' = [1, 2], 'baz' = False}") def main(): - # CHECK-LABEL: == test_object - print("== test_object") - try: - test_object_ctors() - test_comparison_ops() - test_arithmetic_ops() - test_arithmetic_ops_div() - # CHECK: Function at address 0x{{[a-float0-9]+}} - # CHECK-NEXT: 3 - # CHECK-NEXT: Error from function type - test_object_function() - # CHECK: Type 'list' does not have attribute 'foo' - test_non_object_getattr() - # CHECK: [5, 8, 11] - # CHECK: [8, 14, 20] - # CHECK: [11, 20, 29] - test_matrix() - except e0: - print(e0) - # CHECK-NOT: TEST FAILED - print("TEST FAILED") - - try: - # CHECK-LABEL: Printing Tests - print("Printing Tests") - var a: object = True - # CHECK-NEXT: True - print(a) - a = 42 - # CHECK-NEXT: 42 - print(a) - a = 2.5 - # CHECK-NEXT: 2.5 - print(a) - a = "hello" - # CHECK-NEXT: 'hello' - print(a) - a = [] - # CHECK-NEXT: [] - print(a) - a.append(3) - a.append(False) - a.append(5.5) - var b: object = [] - b.append("foo") - b.append("baz") - a.append(b) - # CHECK: [3, False, 5.5{{.*}}, ['foo', 'baz']] - print(a) - # CHECK: 'baz' - print(a[3, 1]) - a[3, 1] = "bar" - # CHECK: 'bar' - print(a[3, 1]) - var c = a + b - # CHECK: [3, False, 5.5{{.*}}, ['foo', 'bar'], 'foo', 'bar'] - print(c) - b.append(False) - # CHECK: [3, {{.*}}, ['foo', 'bar', False], 'foo', 'bar'] - print(c) - # CHECK: [3, {{.*}}, ['foo', 'bar', False]] - print(a) - # CHECK: ['foo', 'bar', False] - print(c[3]) - b[1] = object() - # CHECK: [3, False, 5.5{{.*}}, ['foo', None, False]] - print(a) - a = "abc" - b = a[True] - # CHECK: 'b' - print(b) - b = a[2] - # CHECK: 'c' - print(b) - a = [1, 1.2, False, "true"] - # CHECK: [1, 1.2, False, 'true'] - print(a) - - a = object(Attr("foo", 5), Attr("bar", "hello"), Attr("baz", False)) - # CHECK: 'hello' - print(a.bar) - a.bar = [1, 2] - # CHECK: {'foo' = 5, 'bar' = [1, 2], 'baz' = False} - print(a) - except e1: - print(e1) - # CHECK-NOT: TEST FAILED - print("TEST FAILED") + test_object_ctors() + test_comparison_ops() + test_arithmetic_ops() + test_arithmetic_ops_div() + test_object_function() + test_non_object_getattr() + test_matrix() + test_convert_to_string() From 55ca585c96e84bd3997ae516aee937b6128b9c75 Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Sat, 27 Apr 2024 08:42:11 -0600 Subject: [PATCH 0325/2019] [External] [stdlib] Replace `Pointer` by `UnsafePointer` in `hash|simd.mojo` (#38795) [External] [stdlib] Replace `Pointer` by `UnsafePointer` in `hash|simd.mojo` Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2402 MODULAR_ORIG_COMMIT_REV_ID: 960497eeb7c7e46e3332c3e60e90c144f7d21705 --- stdlib/src/builtin/hash.mojo | 12 +++++++----- stdlib/src/builtin/simd.mojo | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 3bd44c749f..83e33c3d4c 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -63,14 +63,16 @@ fn _HASH_SECRET() -> Int: return ptr.bitcast[Int]()[0] -fn _initialize_hash_secret(payload: Pointer[NoneType]) -> Pointer[NoneType]: +fn _initialize_hash_secret( + payload: UnsafePointer[NoneType], +) -> UnsafePointer[NoneType]: var secret = random.random_ui64(0, UInt64.MAX) - var data = Pointer[Int].alloc(1) - data.store(int(secret)) + var data = UnsafePointer[Int].alloc(1) + data[] = int(secret) return data.bitcast[NoneType]() -fn _destroy_hash_secret(p: Pointer[NoneType]): +fn _destroy_hash_secret(p: UnsafePointer[NoneType]): p.free() @@ -291,7 +293,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: if r != 0: var remaining = StaticTuple[Int8, stride]() var ptr = DTypePointer[DType.int8]( - Pointer.address_of(remaining).bitcast[Int8]() + UnsafePointer.address_of(remaining).bitcast[Int8]() ) memcpy(ptr, bytes + k * stride, r) memset_zero(ptr + r, stride - r) # set the rest to 0 diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 74c162d825..e14129b532 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1435,7 +1435,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "invalid index in the shuffle operation", ]() var ptr = __mlir_op.`pop.array.gep`( - Pointer.address_of(array).address, idx.value + UnsafePointer.address_of(array).address, idx.value ) __mlir_op.`pop.store`(val, ptr) From 5bf959c44f6c00dd8f342a10fca5e77c1125198a Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Sat, 27 Apr 2024 08:50:39 -0600 Subject: [PATCH 0326/2019] [External] [stdlib] Use `UnsafePointer` in `hex|io|os.mojo` (#38796) [External] [stdlib] Use `UnsafePointer` in `hex|io|os.mojo` Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2403 MODULAR_ORIG_COMMIT_REV_ID: 6916ab8f901ede54c69faf09192983c3f8c58cbb --- stdlib/src/builtin/hex.mojo | 2 +- stdlib/src/builtin/io.mojo | 4 ++-- stdlib/src/os/os.mojo | 23 +++++++++++------------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 20780b1ae2..97320ce135 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -201,7 +201,7 @@ fn _try_write_int( # bytes from our final `buf_ptr` to the end of the buffer. var len = CAPACITY - offset - var strref = StringRef(rebind[Pointer[Int8]](buf_ptr), len) + var strref = StringRef(rebind[UnsafePointer[Int8]](buf_ptr), len) fmt.write_str(strref) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 0210a7100c..8a8b180418 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -322,9 +322,9 @@ fn _put(x: StringRef): @parameter if triple_is_nvidia_cuda(): var tmp = 0 - var arg_ptr = Pointer.address_of(tmp) + var arg_ptr = UnsafePointer.address_of(tmp) _ = external_call["vprintf", Int32]( - x.data, arg_ptr.bitcast[Pointer[NoneType]]() + x.data, arg_ptr.bitcast[UnsafePointer[NoneType]]() ) else: alias MAX_STR_LEN = 0x1000_0000 diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 6baf03abe1..fb01e1f756 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -24,7 +24,6 @@ from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda from memory import ( DTypePointer, - Pointer, ) from memory.unsafe_pointer import move_from_pointee @@ -86,9 +85,9 @@ struct _dirent_macos: """Name of entry.""" -fn _strnlen(ptr: Pointer[Int8], max: Int) -> Int: +fn _strnlen(ptr: UnsafePointer[Int8], max: Int) -> Int: var len = 0 - while len < max and ptr.load(len): + while len < max and move_from_pointee(ptr + len): len += 1 return len @@ -96,7 +95,7 @@ fn _strnlen(ptr: Pointer[Int8], max: Int) -> Int: struct _DirHandle: """Handle to an open directory descriptor opened via opendir.""" - var _handle: Pointer[NoneType] + var _handle: UnsafePointer[NoneType] fn __init__(inout self, path: String) raises: """Construct the _DirHandle using the path provided. @@ -111,7 +110,7 @@ struct _DirHandle: if not isdir(path): raise "the directory '" + path + "' does not exist" - self._handle = external_call["opendir", Pointer[NoneType]]( + self._handle = external_call["opendir", UnsafePointer[NoneType]]( path._as_ptr() ) @@ -144,13 +143,13 @@ struct _DirHandle: var res = List[String]() while True: - var ep = external_call["readdir", Pointer[_dirent_linux]]( + var ep = external_call["readdir", UnsafePointer[_dirent_linux]]( self._handle ) if not ep: break - var name = ep.load().name - var name_ptr = Pointer.address_of(name).bitcast[Int8]() + var name = move_from_pointee(ep).name + var name_ptr = UnsafePointer.address_of(name).bitcast[Int8]() var name_str = StringRef( name_ptr, _strnlen(name_ptr, _dirent_linux.MAX_NAME_SIZE) ) @@ -169,13 +168,13 @@ struct _DirHandle: var res = List[String]() while True: - var ep = external_call["readdir", Pointer[_dirent_macos]]( + var ep = external_call["readdir", UnsafePointer[_dirent_macos]]( self._handle ) if not ep: break - var name = ep.load().name - var name_ptr = Pointer.address_of(name).bitcast[Int8]() + var name = move_from_pointee(ep).name + var name_ptr = UnsafePointer.address_of(name).bitcast[Int8]() var name_str = StringRef( name_ptr, _strnlen(name_ptr, _dirent_macos.MAX_NAME_SIZE) ) @@ -283,7 +282,7 @@ fn remove(path: String) raises: if error != 0: # TODO get error message, the following code prints it # var error_str = String("Something went wrong") - # _ = external_call["perror", Pointer[NoneType]](error_str._as_ptr()) + # _ = external_call["perror", UnsafePointer[NoneType]](error_str._as_ptr()) # _ = error_str raise Error("Can not remove file: " + path) From 5eab55019aa05a06a1f1057393cb08a4e6f8d0c1 Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Sat, 27 Apr 2024 08:59:21 -0600 Subject: [PATCH 0327/2019] [External] [stdlib] Use UnsafePointer in `info.mojo` and some test files (#38797) [External] [stdlib] Use UnsafePointer in `info.mojo` and some test files Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2404 MODULAR_ORIG_COMMIT_REV_ID: f4b59539fd68892973c806d5a56e53fe22c2402c --- stdlib/src/sys/info.mojo | 4 +-- stdlib/test/sys/test_windows_target.mojo | 4 ++- stdlib/test/utils/test_variant.mojo | 41 ++++++++++++++---------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 4c3b4fe781..ff8c785db9 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -718,8 +718,8 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: var err = external_call["sysctlbyname", Int32]( "kern.osproductversion".data(), buf.data, - Pointer.address_of(buf_len), - Pointer[NoneType](), + UnsafePointer.address_of(buf_len), + UnsafePointer[NoneType](), Int(0), ) diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index 492fb36a5e..3cc60f5d10 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -44,7 +44,9 @@ def test_last_error(): # GetProcessId takes the handle to a process and returns its id. If the # handle is null this will fail and returns an invalid handle error (error # code 6). - var succeeded = external_call["GetProcessId", Int](Pointer[Int].get_null()) + var succeeded = external_call["GetProcessId", Int]( + UnsafePointer[Int].get_null() + ) assert_equal(succeeded, 0) diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 9163aa7f74..9bfd591e7e 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -14,7 +14,6 @@ from sys.ffi import _get_global -from memory import Pointer from testing import assert_equal, assert_false, assert_true from utils import Variant @@ -33,11 +32,11 @@ struct TestCounter(CollectionElement): self.moved = other.moved fn __moveinit__(inout self, owned other: Self): - self.copied = other.copied^ + self.copied = other.copied self.moved = other.moved + 1 -fn _poison_ptr() -> Pointer[Bool]: +fn _poison_ptr() -> UnsafePointer[Bool]: var ptr = _get_global[ "TEST_VARIANT_POISON", _initialize_poison, _destroy_poison ]() @@ -45,16 +44,18 @@ fn _poison_ptr() -> Pointer[Bool]: fn assert_no_poison() raises: - assert_false(_poison_ptr().load()) + assert_false(move_from_pointee(_poison_ptr())) -fn _initialize_poison(payload: Pointer[NoneType]) -> Pointer[NoneType]: - var poison = Pointer[Bool].alloc(1) - poison.store(False) +fn _initialize_poison( + payload: UnsafePointer[NoneType], +) -> UnsafePointer[NoneType]: + var poison = UnsafePointer[Bool].alloc(1) + initialize_pointee_move(poison, False) return poison.bitcast[NoneType]() -fn _destroy_poison(p: Pointer[NoneType]): +fn _destroy_poison(p: UnsafePointer[NoneType]): p.free() @@ -63,13 +64,13 @@ struct Poison(CollectionElement): pass fn __copyinit__(inout self, other: Self): - _poison_ptr().store(True) + initialize_pointee_move(_poison_ptr(), True) fn __moveinit__(inout self, owned other: Self): - _poison_ptr().store(True) + initialize_pointee_move(_poison_ptr(), True) fn __del__(owned self): - _poison_ptr().store(True) + initialize_pointee_move(_poison_ptr(), True) alias TestVariant = Variant[TestCounter, Poison] @@ -123,16 +124,18 @@ def test_move(): @value struct ObservableDel(CollectionElement): - var target: Pointer[Bool] + var target: UnsafePointer[Bool] fn __del__(owned self): - self.target.store(True) + initialize_pointee_move(self.target, True) def test_del(): alias TestDeleterVariant = Variant[ObservableDel, Poison] var deleted: Bool = False - var v1 = TestDeleterVariant(ObservableDel(Pointer.address_of(deleted))) + var v1 = TestDeleterVariant( + ObservableDel(UnsafePointer.address_of(deleted)) + ) _ = v1^ # call __del__ assert_true(deleted) # test that we didn't call the other deleter too! @@ -143,8 +146,10 @@ def test_set_calls_deleter(): alias TestDeleterVariant = Variant[ObservableDel, Poison] var deleted: Bool = False var deleted2: Bool = False - var v1 = TestDeleterVariant(ObservableDel(Pointer.address_of(deleted))) - v1.set[ObservableDel](ObservableDel(Pointer.address_of(deleted2))) + var v1 = TestDeleterVariant( + ObservableDel(UnsafePointer.address_of(deleted)) + ) + v1.set[ObservableDel](ObservableDel(UnsafePointer.address_of(deleted2))) assert_true(deleted) assert_false(deleted2) _ = v1^ @@ -156,7 +161,9 @@ def test_set_calls_deleter(): def test_take_doesnt_call_deleter(): alias TestDeleterVariant = Variant[ObservableDel, Poison] var deleted: Bool = False - var v1 = TestDeleterVariant(ObservableDel(Pointer.address_of(deleted))) + var v1 = TestDeleterVariant( + ObservableDel(UnsafePointer.address_of(deleted)) + ) assert_false(deleted) var v2 = v1.take[ObservableDel]() assert_false(deleted) From 46ea8784bfe1443413164d30b814be99099bc47f Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Sat, 27 Apr 2024 10:02:41 -0600 Subject: [PATCH 0328/2019] [External] [stdlib] Use `UnsafePointer` in `_cpython.mojo` (#38798) [External] [stdlib] Use `UnsafePointer` in `_cpython.mojo` Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2405 MODULAR_ORIG_COMMIT_REV_ID: 44ed3e851362efb44f26ef7fd44cacfa0a137a80 --- stdlib/src/python/_cpython.mojo | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index d2a2b065f9..acea0bec1c 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -15,7 +15,7 @@ from os import getenv from sys import external_call from sys.ffi import DLHandle -from memory import DTypePointer, LegacyPointer +from memory import DTypePointer, UnsafePointer from utils import StringRef, StaticIntTuple @@ -156,7 +156,7 @@ struct CPython: var dict_type: PyObjectPtr var logging_enabled: Bool var version: PythonVersion - var total_ref_count: LegacyPointer[Int] + var total_ref_count: UnsafePointer[Int] fn __init__(inout self: CPython): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" @@ -172,7 +172,7 @@ struct CPython: var null_pointer = DTypePointer[DType.int8].get_null() self.lib = DLHandle(python_lib) - self.total_ref_count = LegacyPointer[Int].alloc(1) + self.total_ref_count = UnsafePointer[Int].alloc(1) self.none_value = PyObjectPtr(null_pointer) self.dict_type = PyObjectPtr(null_pointer) self.logging_enabled = logging_enabled @@ -187,7 +187,12 @@ struct CPython: existing.Py_DecRef(existing.none_value) if existing.logging_enabled: print("CPython destroy") - print("Number of remaining refs:", existing.total_ref_count.load()) + var remaining_refs = move_from_pointee(existing.total_ref_count) + print("Number of remaining refs:", remaining_refs) + # Technically not necessary since we're working with register + # passable types, by it's good practice to re-initialize the + # pointer after a consuming move. + initialize_pointee_move(existing.total_ref_count, remaining_refs) _py_finalize(existing.lib) existing.lib.close() existing.total_ref_count.free() @@ -217,12 +222,12 @@ struct CPython: self.total_ref_count = existing.total_ref_count fn _inc_total_rc(inout self): - var v = self.total_ref_count.load() - self.total_ref_count[0] = v + 1 + var v = move_from_pointee(self.total_ref_count) + initialize_pointee_move(self.total_ref_count, v + 1) fn _dec_total_rc(inout self): - var v = self.total_ref_count.load() - self.total_ref_count[0] = v - 1 + var v = move_from_pointee(self.total_ref_count) + initialize_pointee_move(self.total_ref_count, v - 1) fn Py_IncRef(inout self, ptr: PyObjectPtr): if self.logging_enabled: @@ -772,7 +777,7 @@ struct CPython: var key = DTypePointer[DType.int8].get_null() var value = DTypePointer[DType.int8].get_null() var v = p - var position = LegacyPointer[Int].address_of(v) + var position = UnsafePointer[Int].address_of(v) var value_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( value ) @@ -780,7 +785,7 @@ struct CPython: var result = self.lib.get_function[ fn ( PyObjectPtr, - LegacyPointer[Int], + UnsafePointer[Int], UnsafePointer[DTypePointer[DType.int8]], UnsafePointer[DTypePointer[DType.int8]], ) -> Int @@ -809,6 +814,6 @@ struct CPython: return PyKeyValuePair { key: key, value: value, - position: position.load(), + position: move_from_pointee(position), success: result == 1, } From e67d970ff34eb94afed7385c3677d24b319124f1 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sat, 27 Apr 2024 13:53:08 -0400 Subject: [PATCH 0329/2019] [mojo-stdlib][NFC] Improve the docstring of the `_location` module (#38805) A nit, but still. MODULAR_ORIG_COMMIT_REV_ID: cb5e37d027952a1eff95e9e72657db60896b5515 --- stdlib/src/builtin/_location.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 215d44046f..d803d62586 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements the source range struct. +"""Implements utilities to capture and represent source code location. """ From 9eab9ece765ec46cba467bfc22da58f100b079b1 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Sun, 28 Apr 2024 07:13:18 -0600 Subject: [PATCH 0330/2019] [External] [stdlib] add b64decode (#38800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] add b64decode Followed the decode algorithm from the same paper used for `b64encode`. The Llama3 `tokenizer.model` stores the tokens with base64 encoding so demand for this may increase. 😃 ORIGINAL_AUTHOR=Michael Kowalski <1331470+mikowals@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2364 --------- Co-authored-by: Michael Kowalski <1331470+mikowals@users.noreply.github.com> Closes modularml/mojo#2364 MODULAR_ORIG_COMMIT_REV_ID: de91cca69272570a52fcbf28a5c51c8d7fe75364 --- stdlib/src/base64/__init__.mojo | 2 +- stdlib/src/base64/base64.mojo | 80 +++++++++++++++++++++++++++++ stdlib/test/base64/test_base64.mojo | 22 +++++++- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/stdlib/src/base64/__init__.mojo b/stdlib/src/base64/__init__.mojo index 7a864392b2..d76ce79f8f 100644 --- a/stdlib/src/base64/__init__.mojo +++ b/stdlib/src/base64/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the base64 package.""" -from .base64 import b64encode +from .base64 import b64encode, b64decode diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index a53afb2d55..d4a238431f 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -22,6 +22,39 @@ from base64 import b64encode from collections import List from sys import simdwidthof +# ===----------------------------------------------------------------------===# +# Utilities +# ===----------------------------------------------------------------------===# + + +@always_inline +fn _ascii_to_value(char: String) -> Int: + """Converts an ASCII character to its integer value for base64 decoding. + + Args: + char: A single character string. + + Returns: + The integer value of the character for base64 decoding, or -1 if invalid. + """ + var char_val = ord(char) + + if char == "=": + return 0 + elif ord("A") <= char_val <= ord("Z"): + return char_val - ord("A") + elif ord("a") <= char_val <= ord("z"): + return char_val - ord("a") + 26 + elif ord("0") <= char_val <= ord("9"): + return char_val - ord("0") + 52 + elif char == "+": + return 62 + elif char == "/": + return 63 + else: + return -1 + + # ===----------------------------------------------------------------------===# # b64encode # ===----------------------------------------------------------------------===# @@ -71,3 +104,50 @@ fn b64encode(str: String) -> String: out.append(ord("=")) out.append(0) return String(out^) + + +# ===----------------------------------------------------------------------===# +# b64decode +# ===----------------------------------------------------------------------===# + + +@always_inline +fn b64decode(str: String) -> String: + """Performs base64 decoding on the input string. + + Args: + str: A base64 encoded string. + + Returns: + The decoded string. + """ + var n = len(str) + debug_assert(n % 4 == 0, "Input length must be divisible by 4") + + var p = List[Int8](capacity=n + 1) + + # This algorithm is based on https://arxiv.org/abs/1704.00605 + for i in range(0, n, 4): + var a = _ascii_to_value(str[i]) + var b = _ascii_to_value(str[i + 1]) + var c = _ascii_to_value(str[i + 2]) + var d = _ascii_to_value(str[i + 3]) + + debug_assert( + a >= 0 and b >= 0 and c >= 0 and d >= 0, + "Unexpected character encountered", + ) + + p.append((a << 2) | (b >> 4)) + if str[i + 2] == "=": + break + + p.append(((b & 0x0F) << 4) | (c >> 2)) + + if str[i + 3] == "=": + break + + p.append(((c & 0x03) << 6) | d) + + p.append(0) + return p diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 43cf6685b8..8b6e078775 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from base64 import b64encode +from base64 import b64encode, b64decode from testing import assert_equal @@ -33,5 +33,25 @@ def test_b64encode(): assert_equal(b64encode("ABCDEFabcdef"), "QUJDREVGYWJjZGVm") +def test_b64decode(): + assert_equal(b64decode("YQ=="), "a") + + assert_equal(b64decode("Zm8="), "fo") + + assert_equal(b64decode("SGVsbG8gTW9qbyEhIQ=="), "Hello Mojo!!!") + + assert_equal(b64decode("SGVsbG8g8J+UpSEhIQ=="), "Hello 🔥!!!") + + assert_equal( + b64decode( + "dGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==" + ), + "the quick brown fox jumps over the lazy dog", + ) + + assert_equal(b64decode("QUJDREVGYWJjZGVm"), "ABCDEFabcdef") + + def main(): test_b64encode() + test_b64decode() From 69dcff38032537993c4624695a91c170b7c92977 Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Sun, 28 Apr 2024 10:30:41 -0600 Subject: [PATCH 0331/2019] [External] [stdlib] Use UnsafePointer in builtin_list.mojo (#38799) [External] [stdlib] Use UnsafePointer in builtin_list.mojo Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2407 MODULAR_ORIG_COMMIT_REV_ID: c5be190cd4a2bc68a21f57b93b97c1ad4c5be83a --- stdlib/src/builtin/builtin_list.mojo | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 16ef431d3b..ef0a88313d 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -278,9 +278,7 @@ struct VariadicListMem[ var tmp = value # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. - self.value = ( - LegacyPointer.address_of(tmp).bitcast[Self._mlir_type]().load() - ) + self.value = UnsafePointer.address_of(tmp).bitcast[Self._mlir_type]()[] self._is_owned = False # Provide support for variadics of *owned* arguments. The reference will @@ -300,9 +298,7 @@ struct VariadicListMem[ var tmp = value # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. - self.value = ( - LegacyPointer.address_of(tmp).bitcast[Self._mlir_type]().load() - ) + self.value = UnsafePointer.address_of(tmp).bitcast[Self._mlir_type]()[] self._is_owned = True @always_inline From 07be06f601ab53826b3ac2cb2ab6dc183f352885 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 28 Apr 2024 14:04:30 -0400 Subject: [PATCH 0332/2019] [mojo-stdlib] Add `is_{nan,neg_zero}` methods to `FloatLiteral` (#38815) This will allow us to write tests that check for these special values, since the `__eq__` comparison for these is not sufficient. MODULAR_ORIG_COMMIT_REV_ID: 3e62453d95bcbda6f516c237bdfeb87ebe7999da --- stdlib/src/builtin/float_literal.mojo | 35 ++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 4ee2b66faf..0f9850e7a7 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -70,10 +70,37 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): """ return Self(__mlir_op.`kgen.int_literal.to_float_literal`(value.value)) - alias nan = Self(__mlir_attr[`#kgen.float_literal`]) - alias infinity = Self(__mlir_attr[`#kgen.float_literal`]) - alias negative_infinity = Self(__mlir_attr[`#kgen.float_literal`]) - alias negative_zero = Self(__mlir_attr[`#kgen.float_literal`]) + alias nan = Self(__mlir_attr.`#kgen.float_literal`) + alias infinity = Self(__mlir_attr.`#kgen.float_literal`) + alias negative_infinity = Self(__mlir_attr.`#kgen.float_literal`) + alias negative_zero = Self(__mlir_attr.`#kgen.float_literal`) + + @always_inline("nodebug") + fn is_nan(self) -> Bool: + """Return whether the FloatLiteral is nan. + + Since `nan == nan` is False, this provides a way to check for nan-ness. + + Returns: + True, if the value is nan, False otherwise. + """ + return __mlir_op.`kgen.float_literal.isa`[ + special = __mlir_attr.`#kgen` + ](self.value) + + @always_inline("nodebug") + fn is_neg_zero(self) -> Bool: + """Return whether the FloatLiteral is negative zero. + + Since `FloatLiteral.negative_zero == 0.0` is True, this provides a way + to check if the FloatLiteral is negative zero. + + Returns: + True, if the value is negative zero, False otherwise. + """ + return __mlir_op.`kgen.float_literal.isa`[ + special = __mlir_attr.`#kgen` + ](self.value) # ===------------------------------------------------------------------===# # Conversion Operators From de2061677a41d4aa0cbab66a1d92ef36de7c59d4 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 28 Apr 2024 12:00:00 -0700 Subject: [PATCH 0333/2019] [mojo-lang] Improve parameter inference around reference mut upcasts. (#38816) It's ok to cast from a mutable lifetime to an immutable one. Our LValues decay to BValues in the face of conditional mutability (which is probably wrong...) so we need to use UnsafePointer in a couple places in dict. MODULAR_ORIG_COMMIT_REV_ID: 96a71ea8f4309be9a53f5b9c86a9a79e72dfd56b --- stdlib/src/collections/dict.mojo | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 54ac1ef529..6f27cd569e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -987,7 +987,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): # return self._dict.__iter__() return _DictKeyIter( _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, Reference(self)[]._dict + 0, 0, UnsafePointer(self)[]._dict ) ) @@ -1029,7 +1029,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): # return self._dict.values() return _DictValueIter( _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, Reference(self)[]._dict + # Use UnsafePointer here to cast away conditional mutability. + 0, + 0, + UnsafePointer(self)[]._dict, ) ) From 672a74d4c7cf4072d9e2e4fff3607767c902d9fd Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 28 Apr 2024 18:52:04 -0400 Subject: [PATCH 0334/2019] [mojo-stdlib] Move `abs` to `builtin` and implement it generically (#38697) This also required moving around some numeric helpers from `math` to `simd`. Literal types need a special overload of `abs` due to some parser bugs. Tests for `abs` for the `SIMD` type are extended, and new tests are added for the other types. `ComplexFloat32` also needed a special overload, and its tests were moved. MODULAR_ORIG_COMMIT_REV_ID: 9388cf9d47de2eacc0ba99d0485e02bd4834bd3c --- docs/changelog.md | 17 ++++ stdlib/src/builtin/abs.mojo | 87 +++++++++++++++++ stdlib/src/builtin/float_literal.mojo | 29 ++++-- stdlib/src/builtin/int.mojo | 11 ++- stdlib/src/builtin/int_literal.mojo | 13 ++- stdlib/src/builtin/simd.mojo | 52 ++++++++++ stdlib/test/builtin/test_float_literal.mojo | 101 ++++++++++++++++++++ stdlib/test/builtin/test_int.mojo | 10 +- stdlib/test/builtin/test_int_literal.mojo | 15 ++- stdlib/test/builtin/test_simd.mojo | 23 +++++ 10 files changed, 342 insertions(+), 16 deletions(-) create mode 100644 stdlib/src/builtin/abs.mojo create mode 100644 stdlib/test/builtin/test_float_literal.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 5a11398180..f19f92999d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -291,6 +291,20 @@ what we publish. it was an integer literal, with the base determined by whether the string contains the prefix `"0x"`, `"0o"`, or `"0b"`. +- Mojo now supports types to opt in to use the `abs` function by implementing + the `__abs__` method (i.e. by conforming to the now `Absable` trait), e.g.: + + ```mojo + from math import sqrt + + struct Point(Absable): + var x: Float64 + var y: Float64 + + fn __abs__(self) -> Self: + return sqrt(self.x * self.x + self.y * self.y) + ``` + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has @@ -424,6 +438,9 @@ what we publish. ... ``` +- The `abs` function has also moved from `math` to `builtin`, so you no longer + need to do `from math import abs`. + ### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of diff --git a/stdlib/src/builtin/abs.mojo b/stdlib/src/builtin/abs.mojo new file mode 100644 index 0000000000..5c77e570a4 --- /dev/null +++ b/stdlib/src/builtin/abs.mojo @@ -0,0 +1,87 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provides the `abs` function. + +These are Mojo built-ins, so you don't need to import them. +""" + + +trait Absable: + """ + The `Absable` trait describes a type that defines an absolute value + operation. + + Types that conform to `Absable` will work with the builtin `abs` function. + The absolute value operation always returns the same type as the input. + + For example: + ```mojo + struct Point(Absable): + var x: Float64 + var y: Float64 + + fn __abs__(self) -> Self: + return sqrt(self.x * self.x + self.y * self.y) + ``` + """ + + fn __abs__(self) -> Self: + ... + + +@always_inline +fn abs[T: Absable](value: T) -> T: + """Get the absolute value of the given object. + + Parameters: + T: The type conforming to Absable. + + Args: + value: The object to get the absolute value of. + + Returns: + The absolute value of the object. + """ + return value.__abs__() + + +# TODO: https://github.com/modularml/modular/issues/38694 +# TODO: https://github.com/modularml/modular/issues/38695 +# TODO: Remove this +@always_inline +fn abs(value: IntLiteral) -> IntLiteral: + """Get the absolute value of the given IntLiteral. + + Args: + value: The IntLiteral to get the absolute value of. + + Returns: + The absolute value of the IntLiteral. + """ + return value.__abs__() + + +# TODO: https://github.com/modularml/modular/issues/38694 +# TODO: https://github.com/modularml/modular/issues/38695 +# TODO: Remove this +@always_inline +fn abs(value: FloatLiteral) -> FloatLiteral: + """Get the absolute value of the given FloatLiteral. + + Args: + value: The FloatLiteral to get the absolute value of. + + Returns: + The absolute value of the FloatLiteral. + """ + return value.__abs__() diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 0f9850e7a7..77adb48dc8 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -23,7 +23,7 @@ These are Mojo built-ins, so you don't need to import them. @value @nonmaterializable(Float64) @register_passable("trivial") -struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): +struct FloatLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): """Mojo floating point literal type.""" alias fp_type = __mlir_type.`!kgen.float_literal` @@ -60,13 +60,13 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): @always_inline("nodebug") fn __init__(value: IntLiteral) -> Self: - """Convert an IntLiteral to a double value. + """Convert an IntLiteral to a FloatLiteral value. Args: value: The IntLiteral value. Returns: - The integer value as a double. + The integer value as a FloatLiteral. """ return Self(__mlir_op.`kgen.int_literal.to_float_literal`(value.value)) @@ -157,20 +157,31 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): @always_inline("nodebug") fn __neg__(self) -> FloatLiteral: - """Return the negation of the double value. + """Return the negation of the FloatLiteral value. Returns: - The negated double value. + The negated FloatLiteral value. """ return self * Self(-1) + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Return the absolute value of FloatLiteral value. + + Returns: + The absolute value. + """ + if self > 0: + return self + return -self + # ===------------------------------------------------------------------===# # Arithmetic Operators # ===------------------------------------------------------------------===# @always_inline("nodebug") fn __add__(self, rhs: FloatLiteral) -> FloatLiteral: - """Add two doubles. + """Add two FloatLiterals. Args: rhs: The value to add. @@ -184,7 +195,7 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): @always_inline("nodebug") fn __sub__(self, rhs: FloatLiteral) -> FloatLiteral: - """Subtract two doubles. + """Subtract two FloatLiterals. Args: rhs: The value to subtract. @@ -198,7 +209,7 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): @always_inline("nodebug") fn __mul__(self, rhs: FloatLiteral) -> FloatLiteral: - """Multiply two doubles. + """Multiply two FloatLiterals. Args: rhs: The value to multiply. @@ -212,7 +223,7 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): @always_inline("nodebug") fn __truediv__(self, rhs: FloatLiteral) -> FloatLiteral: - """Divide two doubles. + """Divide two FloatLiterals. Args: rhs: The value to divide. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 423f2b27db..4e9f679536 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -192,7 +192,7 @@ fn int(value: String, base: Int = 10) raises -> Int: @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct Int(Intable, Stringable, KeyElement, Boolable, Formattable): +struct Int(Absable, Intable, Stringable, KeyElement, Boolable, Formattable): """This type represents an integer value.""" var value: __mlir_type.index @@ -500,6 +500,15 @@ struct Int(Intable, Stringable, KeyElement, Boolable, Formattable): __mlir_op.`index.constant`[value = __mlir_attr.`-1:index`](), ) + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Return the absolute value of the Int value. + + Returns: + The absolute value. + """ + return -self if self < 0 else self + @always_inline("nodebug") fn __invert__(self) -> Int: """Return ~self. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index c9e21c0974..0ff335f42a 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -16,7 +16,7 @@ @value @nonmaterializable(Int) @register_passable("trivial") -struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): +struct IntLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and must be lowered to other integer types (like Int), but allow for @@ -229,6 +229,17 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): """ return Self() - self + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Return the absolute value of the IntLiteral value. + + Returns: + The absolute value. + """ + if self >= 0: + return self + return -self + @always_inline("nodebug") fn __invert__(self) -> Self: """Return ~self. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index e14129b532..6044c868e9 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -113,6 +113,7 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @lldb_formatter_wrapping_type @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( + Absable, Sized, Intable, CollectionElement, @@ -815,6 +816,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( self.value, rhs.value ) + # ===-------------------------------------------------------------------===# + # Unary operations. + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __pos__(self) -> Self: """Defines the unary `+` operation. @@ -835,6 +840,53 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() return __mlir_op.`pop.neg`(self.value) + @always_inline + fn _bits_to_float[dest_type: DType](self) -> SIMD[dest_type, size]: + """Bitcasts the integer value to a floating-point value. + + Parameters: + dest_type: DType to bitcast the input SIMD vector to. + + Returns: + A floating-point representation of the integer value. + """ + alias integral_type = FPUtils[type].integral_type + return bitcast[dest_type, size](self.cast[integral_type]()) + + @always_inline + fn _float_to_bits[dest_type: DType](self) -> SIMD[dest_type, size]: + """Bitcasts the floating-point value to an integer value. + + Parameters: + dest_type: DType to bitcast the input SIMD vector to. + + Returns: + An integer representation of the floating-point value. + """ + alias integral_type = FPUtils[type].integral_type + var v = bitcast[integral_type, size](self) + return v.cast[dest_type]() + + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Defines the absolute value operation. + + Returns: + The absolute value of this SIMD vector. + """ + + @parameter + if type.is_unsigned() or type.is_bool(): + return self + + @parameter + if type.is_floating_point(): + alias integral_type = FPUtils[type].integral_type + var m = self._float_to_bits[integral_type]() + return (m & (FPUtils[type].sign_mask() - 1))._bits_to_float[type]() + + return (self < 0).select(-self, self) + # ===-------------------------------------------------------------------===# # In place operations. # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo new file mode 100644 index 0000000000..a6e2008c6d --- /dev/null +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -0,0 +1,101 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import ( + assert_equal, + assert_not_equal, + assert_almost_equal, + assert_true, + assert_false, +) + +alias nan = FloatLiteral.nan +alias neg_zero = FloatLiteral.negative_zero +alias inf = FloatLiteral.infinity +alias neg_inf = FloatLiteral.negative_infinity + + +def test_division(): + # TODO: https://github.com/modularml/mojo/issues/1787 + # allow this at compile time + assert_equal(FloatLiteral(4.4) / 0.5, 8.8) + assert_equal(FloatLiteral(4.4) // 0.5, 8.0) + assert_equal(FloatLiteral(-4.4) // 0.5, -9.0) + assert_equal(FloatLiteral(4.4) // -0.5, -9.0) + assert_equal(FloatLiteral(-4.4) // -0.5, 8.0) + + +def test_power(): + assert_almost_equal(FloatLiteral(4.5) ** 2.5, 42.95673695) + assert_almost_equal(FloatLiteral(4.5) ** -2.5, 0.023279235) + # TODO (https://github.com/modularml/modular/issues/33045): Float64/SIMD has + # issues with negative numbers raised to fractional powers. + # assert_almost_equal(FloatLiteral(-4.5) ** 2.5, -42.95673695) + # assert_almost_equal(FloatLiteral(-4.5) ** -2.5, -0.023279235) + + +def test_int_conversion(): + assert_equal(int(FloatLiteral(-4.0)), -4) + assert_equal(int(FloatLiteral(-4.5)), -4) + assert_equal(int(FloatLiteral(-4.3)), -4) + assert_equal(int(FloatLiteral(4.5)), 4) + assert_equal(int(FloatLiteral(4.0)), 4) + + +def test_boolean_comparable(): + var f1 = FloatLiteral(0.0) + assert_false(f1) + + var f2 = FloatLiteral(2.0) + assert_true(f2) + + var f3 = FloatLiteral(1.0) + assert_true(f2) + + +def test_equality(): + var f1 = FloatLiteral(4.4) + var f2 = FloatLiteral(4.4) + var f3 = FloatLiteral(42.0) + assert_equal(f1, f2) + assert_not_equal(f1, f3) + + +def test_is_special_value(): + assert_true(nan.is_nan()) + assert_false(neg_zero.is_nan()) + assert_true(neg_zero.is_neg_zero()) + assert_false(nan.is_neg_zero()) + + +def test_abs(): + assert_equal(FloatLiteral(-4.4).__abs__(), 4.4) + assert_equal(FloatLiteral(4.4).__abs__(), 4.4) + assert_equal(FloatLiteral(0.0).__abs__(), 0.0) + + assert_true(FloatLiteral.__abs__(nan).is_nan()) + assert_false(FloatLiteral.__abs__(neg_zero).is_neg_zero()) + assert_equal(FloatLiteral.__abs__(neg_zero), 0.0) + assert_equal(FloatLiteral.__abs__(inf), inf) + assert_equal(FloatLiteral.__abs__(neg_inf), inf) + + +def main(): + test_division() + test_power() + test_int_conversion() + test_boolean_comparable() + test_equality() + test_is_special_value() + test_abs() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 2bce6c953c..9a32f53117 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal from sys.info import bitwidthof +from testing import assert_equal + def test_constructors(): var i1 = Int(3) # Constructible from IntLiteral @@ -68,6 +69,12 @@ def test_mod(): assert_equal(1, Int(-3) % Int(2)) +def test_abs(): + assert_equal(abs(Int(-5)), 5) + assert_equal(abs(Int(2)), 2) + assert_equal(abs(Int(0)), 0) + + def main(): test_constructors() test_properties() @@ -77,3 +84,4 @@ def main(): test_pow() test_floordiv() test_mod() + test_abs() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 55ac0af7f4..9f8765b80f 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -15,21 +15,21 @@ from testing import assert_equal -fn test_int() raises: +def test_int(): assert_equal(3, 3) assert_equal(3 + 3, 6) assert_equal(4 - 1, 3) assert_equal(6 - 1, 5) -fn test_floordiv() raises: +def test_floordiv(): assert_equal(2 // 2, 1) assert_equal(2 // 3, 0) assert_equal(2 // -2, -1) assert_equal(99 // -2, -50) -fn test_mod() raises: +def test_mod(): assert_equal(99 % 1, 0) assert_equal(99 % 3, 0) assert_equal(99 % -2, -1) @@ -41,15 +41,22 @@ fn test_mod() raises: assert_equal(-3 % 2, 1) -fn test_bit_width() raises: +def test_bit_width(): assert_equal((0)._bit_width(), 1) assert_equal((-1)._bit_width(), 1) assert_equal((255)._bit_width(), 9) assert_equal((-256)._bit_width(), 9) +def test_abs(): + assert_equal(abs(-5), 5) + assert_equal(abs(2), 2) + assert_equal(abs(0), 0) + + def main(): test_int() test_floordiv() test_mod() test_bit_width() + test_abs() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index c02c3f6241..96dd8c4c3d 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -715,6 +715,28 @@ def test_mul_with_overflow(): ) +def test_abs(): + assert_equal(abs(Float32(1.0)), 1) + assert_equal(abs(Float32(-1.0)), 1) + assert_equal(abs(Float32(0.0)), 0) + assert_equal( + abs(SIMD[DType.float32, 4](0.0, 1.5, -42.5, -12.7)), + SIMD[DType.float32, 4](0.0, 1.5, 42.5, 12.7), + ) + assert_equal( + abs(SIMD[DType.int32, 4](0, 2, -42, -12)), + SIMD[DType.int32, 4](0, 2, 42, 12), + ) + assert_equal( + abs(SIMD[DType.uint32, 4](0, 2, 42, 12)), + SIMD[DType.uint32, 4](0, 2, 42, 12), + ) + assert_equal( + abs(SIMD[DType.bool, 4](True, False, True, False)), + SIMD[DType.bool, 4](True, False, True, False), + ) + + def main(): test_cast() test_simd_variadic() @@ -734,3 +756,4 @@ def main(): test_add_with_overflow() test_sub_with_overflow() test_mul_with_overflow() + test_abs() From 92adf6cdec64bf8f127db94dc8210329af5082ae Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 28 Apr 2024 19:33:28 -0400 Subject: [PATCH 0335/2019] [******][mojo-stdlib][GenericML][NFC] Remove unused imports from some mojo files (#38728) Since `abs` is now builtin, I went through files to remove it, and cleaned up other unused imports on the way as well. MODULAR_ORIG_COMMIT_REV_ID: 3654374acd4726493ddeadf4710535ba6df6d55f --- examples/notebooks/RayTracing.ipynb | 3 --- stdlib/test/builtin/test_simd.mojo | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 8a40915b46..66afe90ff9 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -919,9 +919,6 @@ } ], "source": [ - "from math import abs\n", - "\n", - "\n", "fn cast_ray(\n", " orig: Vec3f,\n", " dir: Vec3f,\n", diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 96dd8c4c3d..61cd085143 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys import has_neon, simdwidthof +from sys import has_neon from testing import assert_equal, assert_not_equal, assert_true From 423fa77a97b8b7a42109021dd465a50f5e601805 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 28 Apr 2024 16:36:38 -0700 Subject: [PATCH 0336/2019] [mojo-lang] Use parameter inference for overload set resolution (#38818) This changes overload resolution for method lookup to not implicitly bind the parameters of `self` before doing overload resolution. This forces the type inference logic to infer the parameters on `Self` from the actual value provided by `self`. This new approach allows us (but does not switch us) to have methods in the overload set with different types for the first argument (the self argument). This is necessary for a few things: 1) We currently have hacks for Non-materializable types, where methods lookup looks to see whether the actual type has a method and then falls back to the materialization type if not. Throwing all the methods into one overload set allows more flexible disambiguation. 2) We need to allow self to be declared as `Reference` instead of forcing low-level `!lit.ref` usage for parametric mutability. 3) We want `x.foo` where x is a `Reference[T]` to resolve to either `Reference.foo` or `T.foo` when autoderef happens. All of these usecases end up with a method that can have unusual parameter inference and eventual parameter sets that are not just `ParamBindings::getForDeclaredType(callersSelf)`. This is quite a stress test for parameter inference, which has been the source of the shaking I've been doing on this for the last week. MODULAR_ORIG_COMMIT_REV_ID: fc2de26a32402bc8fad4b562067554676a9c9449 --- stdlib/src/collections/dict.mojo | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 6f27cd569e..ef32e7fcfe 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -1027,9 +1027,9 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ # TODO(#36448): Use this instead of the current workaround # return self._dict.values() + # Use UnsafePointer here to cast away conditional mutability. return _DictValueIter( _DictEntryIter[Self.key_type, V, mutability, self_life]( - # Use UnsafePointer here to cast away conditional mutability. 0, 0, UnsafePointer(self)[]._dict, @@ -1060,9 +1060,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ # TODO(#36448): Use this instead of the current workaround - # return Reference(self)[]._dict.items() + # return UnsafePointer(self)[]._dict.items() + # Use UnsafePointer here to cast away conditional mutability. return _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, Reference(self)[]._dict + 0, 0, UnsafePointer(self)[]._dict ) @always_inline("nodebug") From 20d1f9153dea4f63a1957e29b63f8fa6478357b6 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Sun, 28 Apr 2024 20:38:51 -0700 Subject: [PATCH 0337/2019] [Docs] New Mojo Types doc. (#38241) Fixes #36699 MODULAR_ORIG_COMMIT_REV_ID: c3d840ca766e6c76671c5ebdfe41093112668e19 --- docs/manual/types.ipynb | 1002 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1002 insertions(+) create mode 100644 docs/manual/types.ipynb diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb new file mode 100644 index 0000000000..4b03fe549f --- /dev/null +++ b/docs/manual/types.ipynb @@ -0,0 +1,1002 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Types\n", + "sidebar_position: 1\n", + "description: Standard Mojo data types.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All values in Mojo have an associated data type. Most of the types are\n", + "_nominal_ types, defined by a [`struct`](/mojo/manual/structs). These types are\n", + "nominal (or \"named\") because type equality is determined by the type's _name_,\n", + "not its _structure_.\n", + "\n", + "There are a some types that aren't defined as structs:\n", + "\n", + "* Functions are typed based on their signatures.\n", + "* `NoneType` is a type with one instance, the `None` object, which is used to\n", + " signal \"no value.\"\n", + "\n", + "Mojo comes with a standard library that provides a number of useful types and\n", + "utility functions. These standard types aren’t privileged. Each of the standard\n", + "library types is defined just like user-defined types—even basic types like \n", + "[`Int`](/mojo/stdlib/builtin/int/Int) and \n", + "[`String`](/mojo/stdlib/builtin/string/String). But these standard library types\n", + "are the building blocks you'll use for most Mojo programs.\n", + "\n", + "The most common types are _built-in types_, which are always available and\n", + "don't need to be imported. These include types for numeric values, strings,\n", + "boolean values, and others.\n", + "\n", + "The standard library also includes many more types that you can import as\n", + "needed, including collection types, utilities for interacting with the\n", + "filesystem and getting system information, and so on.\n", + "\n", + "## Numeric types\n", + "\n", + "Mojo's most basic numeric type is `Int`, which represents a signed integer of\n", + "the largest size supported by the system—typically 64 bits or 32 bits.\n", + "\n", + "Mojo also has built-in types for integer and floating-point values of various\n", + "precisions:\n", + "\n", + "
\n", + "\n", + "| Type name | Description |\n", + "|---------------|--------------|\n", + "| `Int8` | 8-bit signed integer |\n", + "| `UInt8` | 8-bit unsigned integer |\n", + "| `Int16` | 16-bit signed integer |\n", + "| `UInt16` | 16-bit unsigned integer |\n", + "| `Int32` | 32-bit signed integer |\n", + "| `UInt32` | 32-bit unsigned integer |\n", + "| `Int64` | 64-bit signed integer |\n", + "| `UInt64` | 64-bit unsigned integer |\n", + "| `Float16` | 16-bit floating point number (IEEE 754-2008 binary16) |\n", + "| `Float32` | 32-bit floating point number (IEEE 754-2008 binary32) |\n", + "| `Float64` | 64-bit floating point number (IEEE 754-2008 binary64) |\n", + "
Table 1. Numeric types with specific precision
\n", + "
\n", + "\n", + "The types in Table 1 are actually all aliases to a single type, \n", + "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD), which is discussed later.\n", + "\n", + "All of the numeric types support the usual numeric and bitwise operators. The \n", + "[`math`](/mojo/stdlib/math/) module provides a number of additional math\n", + "functions.\n", + "\n", + "You may wonder when to use `Int` and when to use the other integer \n", + "types. In general, `Int` is good safe default when you need an integer type and\n", + "you don't require a specific bit width. Using `Int` as the default integer type\n", + "for APIs makes APIs more consistent and predictable.\n", + "\n", + "\n", + "### Floating-point numbers\n", + "\n", + "Floating-point types represent real numbers. Because not all real numbers can be\n", + "expressed in a finite number of bits, floating-point numbers can't represent\n", + "every value exactly.\n", + "\n", + "The floating-point types listed in Table 1—`Float64`, `Float32`, and \n", + "`Float16`—follow the IEEE 754-2008 standard for representing floating-point \n", + "values. Each type includes a sign bit, one set of bits representing an exponent,\n", + "and another set representing the fraction or mantissa. Table 2 shows how each of \n", + "these types are represented in memory.\n", + "\n", + "
\n", + "\n", + "| Type name | Sign | Exponent | Mantissa |\n", + "|------------|-------|-----------|----------|\n", + "| `Float64` | 1 bit | 11 bits | 52 bits |\n", + "| `Float32` | 1 bit | 8 bits | 23 bits |\n", + "| `Float16` | 1 bit | 5 bits | 10 bits |\n", + "
Table 2. Details of floating-point types
\n", + "
\n", + "\n", + "Numbers with exponent values of all ones or all zeros represent special values,\n", + "allowing floating-point numbers to represent infinity, negative infinity,\n", + "signed zeros, and not-a-number (NaN). For more details on how numbers are\n", + "represented, see [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) on\n", + "Wikipedia.\n", + "\n", + "A few things to note with floating-point values:\n", + "\n", + "- Rounding errors. Rounding may produce unexpected results. For example, 1/3\n", + " can't be represented exactly in these floating-point formats. The more\n", + " operations you perform with floating-point numbers, the more the rounding\n", + " errors accumulate. \n", + "\n", + "- Space between consecutive numbers. The space between consecutive numbers is\n", + " variable across the range of a floating-point number format. For numbers close\n", + " to zero, the distance between consecutive numbers is very small. is For large\n", + " positive and negative numbers, the space between consecutive numbers is\n", + " greater than 1, so it may not be possible to represent consecutive integers.\n", + "\n", + "Because the values are approximate, it is rarely useful to compare them with\n", + "the equality operator (`==`). Consider the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "var big_num = 1.0e16\n", + "var bigger_num = big_num+1.0\n", + "print(big_num == bigger_num)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparison operators (`<` `>=` and so on) work with floating point numbers. You\n", + "can also use the [`math.isclose()`](/mojo/stdlib/math/math/isclose) function to\n", + "compare whether two floating-point numbers are equal within a specified\n", + "tolerance." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Numeric literals\n", + "\n", + "In addition to these numeric types, the standard libraries provides integer and\n", + "floating-point literal types, \n", + "[`IntLiteral`](/mojo/stdlib/builtin/int_literal/IntLiteral) and \n", + "[`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral).\n", + "\n", + "These literal types are used at compile time to represent literal numbers that\n", + "appear in the code. In general, you should never instantiate these types\n", + "yourself.\n", + "\n", + "Table 3 summarizes the literal formats you can use to represent numbers.\n", + "\n", + "
\n", + "\n", + "| Format | Examples | Notes |\n", + "|--------|---------|-------|\n", + "| Integer literal | `1760` | Integer literal, in decimal format. |\n", + "| Hexadecimal literal | `0xaa`, `0xFF` | Integer literal, in hexadecimal format.
Hex digits are case-insensitive. |\n", + "| Octal literal | `0o77` | Integer literal, in octal format. |\n", + "| Binary literal | `0b0111` | Integer literal, in binary format. |\n", + "| Floating-point literal | `3.14`, `1.2e9` | Floating-point literal.
Must include the decimal point to be interpreted as floating-point. |\n", + "
Table 3. Numeric literal formats
\n", + "
\n", + "\n", + "At compile time, the literal types are arbitrary-precision (also called \n", + "infinite-precision) values, so the compiler can perform compile-time \n", + "calculations without overflow or rounding errors.\n", + "\n", + "At runtime the values are converted to finite-precision types—`Int` for\n", + "integer values, and `Float64` for floating-point values. (This process of \n", + "converting a value that can only exist at compile time into a runtime value is\n", + "called _materialization_.) \n", + "\n", + "The following code sample shows the difference between an arbitrary-precision \n", + "calculation and the same calculation done using `Float64` values at runtime,\n", + "which suffers from rounding errors." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 0.99999999999999978\n" + ] + } + ], + "source": [ + "var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0)\n", + "# use a variable to force the following calculation to occur at runtime\n", + "var three = 3.0\n", + "var finite_precision = three * (4.0 / three - 1.0)\n", + "print(arbitrary_precision, finite_precision)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### `SIMD` and `DType`\n", + "\n", + "To support high-performance numeric processing, Mojo uses the\n", + "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type as the basis for its numeric\n", + "types. SIMD (single instruction, multiple data) is a processor technology that\n", + "allows you to perform an operation on an entire set of operands at once. Mojo's\n", + "`SIMD` type abstracts SIMD operations. A `SIMD` value represents a SIMD\n", + "_vector_—that is, a fixed-size array of values that can fit into a processor's\n", + "register. SIMD vectors are defined by two\n", + "[_parameters_](/mojo/manual/parameters/):\n", + "\n", + "- A `DType` value, defining the data type in the vector (for example, \n", + " 32-bit floating-point numbers).\n", + "- The number of elements in the vector, which must be a power of two.\n", + "\n", + "For example, you can define a vector of four `Float32` values like this:" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Math operations on SIMD values are \n", + "applied _elementwise_, on each individual element in the vector. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2, 6, 15, 28]\n" + ] + } + ], + "source": [ + "var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7)\n", + "var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4)\n", + "var product = vec1 * vec2\n", + "print(product)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "### Scalar values\n", + "\n", + "The `SIMD` module defines several _type aliases_ that are shorthand for\n", + "different types of `SIMD` vectors. In particular, the `Scalar` type is just a\n", + "`SIMD` vector with a single element. The numeric types listed in \n", + "[Table 1](#table-1), like `Int8` and `Float32` are actually type aliases for\n", + "different types of scalar values:\n", + "\n", + "```mojo\n", + "alias Scalar = SIMD[size=1]\n", + "alias Int8 = Scalar[DType.int8]\n", + "alias Float32 = Scalar[DType.float32]\n", + "```\n", + "\n", + "This may seem a little confusing at first, but it means that whether you're \n", + "working with a single `Float32` value or a vector of float32 values,\n", + "the math operations go through exactly the same code path." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### The `DType` type\n", + "\n", + "The `DType` struct describes the different data types that a `SIMD` vector can\n", + "hold, and defines a number of utility functions for operating on those data\n", + "types. The `DType` struct defines a set of aliases that act as identifiers for\n", + "the different data types, like `DType.int8` and `DType.float32`. You use\n", + "these aliases when declaring a `SIMD` vector:\n", + "\n", + "```mojo\n", + "var v: SIMD[DType.float64, 16]\n", + "```\n", + "\n", + "Note that `DType.float64` isn't a _type_, it's a value that describes a data\n", + "type. You can't create a variable with the type `DType.float64`. You can create\n", + "a variable with the type `SIMD[DType.float64, 1]` (or `Float64`, which is the\n", + "same thing).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "float32 is floating point: True\n", + "float32 is integral: False\n", + "Min/max finite values for float32\n", + "-3.4028234663852886e+38 3.4028234663852886e+38\n" + ] + } + ], + "source": [ + "from math.limit import max_finite, min_finite\n", + "\n", + "def describeDType[dtype: DType]():\n", + " print(dtype, \"is floating point:\", dtype.is_floating_point())\n", + " print(dtype, \"is integral:\", dtype.is_integral())\n", + " print(\"Min/max finite values for\", dtype)\n", + " print(min_finite[dtype](), max_finite[dtype]())\n", + "\n", + "describeDType[DType.float32]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "There are several other data types in the standard library that also use\n", + "the `DType` abstraction. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Strings\n", + "\n", + "Mojo's `String` type represents a mutable string. (For Python programmers, note\n", + "that this is different from Python's standard string, which is immutable.)\n", + "Strings support a variety of operators and common methods.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Testing Mojo strings\n" + ] + } + ], + "source": [ + "var s: String = \"Testing\"\n", + "s += \" Mojo strings\"\n", + "print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Most standard library types conform to the \n", + "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", + "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", + "explicitly convert a value to string.\n", + "\n", + "When concatenating values to a string using the `+` operator, `Stringable`\n", + "will implicitly convert `String`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Items in list: 5\n" + ] + } + ], + "source": [ + "var s = str(\"Items in list: \") + 5\n", + "print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### String literals\n", + "\n", + "As with numeric types, the standard library includes a string literal type used\n", + "to represent literal strings in the program source. String literals are\n", + "enclosed in either single or double quotes.\n", + "\n", + "Adjacent literals are concatenated together, so you can define a long string\n", + "using a series of literals broken up over several lines:\n", + "\n", + "```\n", + "var s = \"A very long string which is \"\n", + " \"broken into two literals for legibility.\"\n", + "```\n", + "\n", + "To define a multi-line string, enclose the literal in three single or double\n", + "quotes:\n", + "\n", + "```\n", + "var s = \"\"\"\n", + "Multi-line string literals let you \n", + "enter long blocks of text, including \n", + "newlines.\"\"\"\n", + "```\n", + "\n", + "Note that the triple double quote form is also used for API documentation\n", + "strings.\n", + "\n", + "Unlike `IntLiteral` and `FloatLiteral`, `StringLiteral` doesn't automatically\n", + "materialize to a runtime type. In some cases, you may need to manually convert\n", + "`StringLiteral` values to `String` using the built-in \n", + "[`str()`](/mojo/stdlib/builtin/str/str) method. \n", + "\n", + "For example, when concatenating strings with other values" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "var a = 5\n", + "var b: String = \"String literals may not concatenate \"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "## Booleans\n", + "\n", + "Mojo's `Bool` type represents a boolean value. It can take one of two values, \n", + "`True` or `False`. You can negate a boolean value using the `not` operator." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False True\n" + ] + } + ], + "source": [ + "var conditionA = False\n", + "var conditionB: Bool\n", + "conditionB = not conditionA\n", + "print(conditionA, conditionB)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many types have a boolean representation. Any type that implements the \n", + "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait has a boolean \n", + "representation. As a general principle, collections evaluate as True if they \n", + "contain any elements, False if the are empty; strings evaluate as True if they\n", + "have a non-zero length." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collection types\n", + "\n", + "The Mojo standard library also includes a set of basic collection types that\n", + "can be used to build more complex data structures:\n", + "\n", + "- [`List`](/mojo/stdlib/collections/list/List), a dynamically-sized array of \n", + " items.\n", + "- [`Dict`](/mojo/stdlib/collections/dict/Dict), an associative array of \n", + " key-value pairs.\n", + "- [`Set`](/mojo/stdlib/collections/set/Set), an unordered collection of unique\n", + " items.\n", + "- [`Optional`](/mojo/stdlib/collections/optional/Optional)\n", + " represents a value that may or may not be present. \n", + "\n", + "The collection types are _generic types_: while a given collection can only\n", + "hold a specific type of value (such as `Int` or `Float64`), you specify the\n", + "type at compile time using a [parameter]((/mojo/manual/parameters/)). For\n", + "example, you can create a `List` of `Int` values like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "var l = List[Int](1, 2, 3, 4)\n", + "# l.append(3.14) # error: FloatLiteral cannot be converted to Int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You don't always need to specify the type explicitly. If Mojo can _infer_ the\n", + "type, you can omit it. For example, when you construct a list from a set of \n", + "integer literals, Mojo creates a `List[Int]`." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# Inferred type == Int\n", + "var l1 = List(1, 2, 3, 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Where you need a more flexible collection, the \n", + "[`Variant`](/mojo/stdlib/utils/variant/Variant) type can hold different types \n", + "of values. For example, a `Variant[Int32, Float64]` can hold either an `Int32`\n", + "_or_ a `Float64` value at any given time. (Using `Variant` is not covered in\n", + "this section, see the [API docs](/mojo/stdlib/utils/variant/Variant) for more\n", + "information.)\n", + "\n", + "The following sections give brief introduction to the main collection types. \n", + "\n", + "### List\n", + "\n", + "[`List`](/mojo/stdlib/collections/list/List) is a dynamically-sized array of \n", + "elements. List elements need to conform to the \n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait, which\n", + "just means that the items must be copyable and movable. Most of the common\n", + "standard library primitives, like `Int`, `String`, and `SIMD` conform to this\n", + "trait. You can create a `List` by passing the element type as a parameter, like\n", + "this:\n", + "\n", + "\n", + "```mojo\n", + "var l = List[String]()\n", + "```\n", + "\n", + "The `List` type supports a subset of the Python `list` API, including the\n", + "ability to append to the list, pop items out of the list, and access list items\n", + "using subscript notation." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Popping last item from list: 11\n", + "2, 3, 5, 7, " + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "var list = List(2, 3, 5)\n", + "list.append(7)\n", + "list.append(11)\n", + "print(\"Popping last item from list: \", list.pop())\n", + "for idx in range(len(list)):\n", + " print(list[idx], end=\", \")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the previous code sample leaves out the type parameter when creating \n", + "the list. Because the list is being created with a set of `Int` values, Mojo can\n", + "_infer_ the type from the arguments. \n", + "\n", + "There are some notable limitations when using `List`:\n", + "\n", + "- You can't currently initialize a list from a list literal, like this:\n", + "\n", + " ```mojo\n", + " # Doesn't work!\n", + " var list: List[Int] = [2, 3, 5]\n", + "\n", + " But you can use variadic arguments to achieve the same thing:\n", + "\n", + " ```mojo\n", + " var list = List(2, 3, 5)\n", + " ```\n", + "\n", + "- You can't `print()` a list, or convert it directly into a string.\n", + "\n", + " ```mojo\n", + " # Does not work\n", + " print(list)\n", + " ```\n", + "\n", + " As shown above, you can print the individual elements in a list as long as\n", + " they're a [`Stringable`](/mojo/stdlib/builtin/str/Stringable) type.\n", + "\n", + "- Iterating a `List` currently returns a \n", + " [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", + " item itself. You can access the item using the dereference operator, `[]`:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2, 3, 4, " + ] + } + ], + "source": [ + "#: from collections import List\n", + "var list = List(2, 3, 4)\n", + "for item in list:\n", + " print(item[], end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " Subscripting in to a list, however, returns the item directly—no need to \n", + " dereference:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2, 3, 4, " + ] + } + ], + "source": [ + "#: from collections import List\n", + "#: var list = List[Int](2, 3, 4)\n", + "for i in range(len(list)):\n", + " print(list[i], end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dict\n", + "\n", + "The [`Dict`](/mojo/stdlib/collections/dict/Dict) type is an associative array\n", + "that holds key-value pairs. You can create a `Dict` by specifying the key type\n", + "and value type as parameters, like this:\n", + "\n", + "```mojo\n", + "var values = Dict[String, Float64]()\n", + "```\n", + "\n", + "They dictionary's key type must conform to the \n", + "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, and value \n", + "elements must conform to the \n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", + "\n", + "You can insert and remove key-value pairs, update the value assigned to a key,\n", + "and iterate through keys, values, or items in the dictionary. \n", + "\n", + "The `Dict` iterators all yield references, so you need to use the dereference\n", + "operator `[]` as shown in the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "plasticity 3.1000000000000001\n", + "elasticity 1.3\n", + "electricity 9.6999999999999993\n" + ] + } + ], + "source": [ + "from collections import Dict\n", + "\n", + "var d = Dict[String, Float64]()\n", + "d[\"plasticity\"] = 3.1\n", + "d[\"elasticity\"] = 1.3\n", + "d[\"electricity\"] = 9.7\n", + "for item in d.items():\n", + " print(item[].key, item[].value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Set\n", + "\n", + "The [`Set`](/mojo/stdlib/collections/set/Set) type represent a set of unique\n", + "values. You can add and remove elements from the set, test whether a value \n", + "exists in the set, and perform set algebra operations, like unions and \n", + "intersections between two sets. \n", + "\n", + "Sets are generic and the element type must conform to the\n", + "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "We both like:\n", + "- ice cream\n", + "- tacos\n" + ] + } + ], + "source": [ + "from collections import Set\n", + "\n", + "i_like = Set(\"sushi\", \"ice cream\", \"tacos\", \"pho\")\n", + "you_like = Set(\"burgers\", \"tacos\", \"salad\", \"ice cream\")\n", + "we_like = i_like.intersection(you_like)\n", + "\n", + "print(\"We both like:\")\n", + "for item in we_like:\n", + " print(\"-\", item[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Optional\n", + "\n", + "The [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", + "value that may or may not be present. Like the other collectypetion types, it is\n", + "generic, and can hold any type that conforms to the\n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "# Two ways to initialize an Optional with a value\n", + "var opt1 = Optional(5)\n", + "var opt2: Optional[Int] = 5\n", + "# Two ways to initalize an Optional with no value\n", + "var opt3 = Optional[Int]()\n", + "var opt4: Optional[Int] = None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "An `Optional` evaluates as `True` when it holds a value, `False` otherwise. If\n", + "the `Optional` holds a value, you can retrieve a reference to the value using \n", + "the `value()` method. But calling `value()` on an `Optional` with no value\n", + "results in undefined behavior, so you should always guard a call to `value()`\n", + "inside a conditional that checks whether a value exists." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Testing\n" + ] + } + ], + "source": [ + "var opt: Optional[String] = str(\"Testing\")\n", + "if opt:\n", + " var value_ref = opt.value()\n", + " print(value_ref[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Alternately, you can use the `or_else()` method, which returns the stored\n", + "value if there is one, or a user-specified default value otherwise:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello\n", + "Hi\n" + ] + } + ], + "source": [ + "var custom_greeting: Optional[String] = None\n", + "print(custom_greeting.or_else(\"Hello\"))\n", + "\n", + "custom_greeting = str(\"Hi\")\n", + "print(custom_greeting.or_else(\"Hello\"))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Register-passable, memory-only, and trivial types\n", + "\n", + "In various places in the documentation you'll see references to \n", + "register-passable, memory-only, and trivial types. Register-passable and \n", + "memory-only types are distinguished based on how they hold data:\n", + "\n", + "- Register-passable types are composed exclusively of fixed-size data types,\n", + " which can (theoretically) be stored in a machine register. A register-passable\n", + " type can include other types, as long as they are also register-passable.\n", + " `Int`, `Bool`, and `SIMD`, for example, are all register-passable types. So \n", + " a register-passable `struct` could include `Int` and `Bool` fields, but not a\n", + " `String` field. Register-passable types are declared with the \n", + " [`@register_passable`](/mojo/manual/decorators/register-passable) decorator.\n", + "\n", + " Register-passable types are always passed by value (that is, the values are\n", + " copied).\n", + "\n", + "- Memory-only types consist of any types that _don't_ fit the description of\n", + " register-passable types. In particular, these types usually use pointers or\n", + " references to manage heap-allocated memory. `String`, `List`, and `Dict` are\n", + " all examples of memory-only types.\n", + "\n", + "Our long-term goal is to make this distinction transparent to the user, and\n", + "ensure all APIs work with both register-passable and memory-only types.\n", + "But right now you will see some standard library types that only work with \n", + "register-passable types or only work with memory-only types.\n", + "\n", + "In addition to these two categories, Mojo also has \"trivial\" types. Conceptually\n", + "a trivial type is simply a type that doesn't require any custom logic in its\n", + "lifecycle methods. The bits that make up an instance of a trivial type can be\n", + "copied or moved without any knowledge of what they do. Currently, trivial types\n", + "are declared using the\n", + "[`@register_passable(trivial)`](/mojo/manual/decorators/register-passable#register_passabletrivial)\n", + "decorator. Trivial types shouldn't be limited to only register-passable types,\n", + "so in the future we intend to separate trivial types from the \n", + "`@register_passable` decorator." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `AnyType` and `AnyRegType`\n", + "\n", + "Two other things you'll see in Mojo APIs are references to `AnyType` and\n", + "`AnyRegType`. These are effectively _metatypes_, that is, types of types.\n", + "\n", + "- `AnyType` represents any Mojo type. Mojo treats `AnyType` as a special kind of\n", + " trait, and you'll find more discussion of it on the\n", + " [Traits page](/mojo/manual/traits#the-anytype-trait).\n", + "- `AnyRegType` is a metatype representing any Mojo type that's marked \n", + " register passable.\n", + "\n", + "You'll see them in signatures like this:\n", + "\n", + "```mojo\n", + "fn any_type_function[ValueType: AnyRegType](value: ValueType):\n", + " ...\n", + "```\n", + "\n", + "You can read this as `any_type_function` has an argument, `value` of type\n", + "`ValueType`,where `ValueType` is a register-passable type, determined at\n", + "compile time. \n", + "\n", + "There is still some code like this in the standard library, but it's gradually\n", + "being migrated to more generic code that doesn't distinguish between \n", + "register-passable and memory-only types.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 2e8037fb90c8727346cefc07684107928d720b3b Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 29 Apr 2024 06:28:36 -0600 Subject: [PATCH 0338/2019] [stdlib] Fix `Optional` module doc string example (#38825) The example fails to compile since `Optional.or_else()` gives back a `T` now and not an `Optional[T]`. Fix the example to just print the value directly rather than calling `.value()` on the previously-returned `Optional`. Fixes https://github.com/modularml/mojo/issues/2431 MODULAR_ORIG_COMMIT_REV_ID: 7dbdc84f355d7c10cac913a777f90e15be877c72 --- stdlib/src/collections/optional.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 1c01a369c8..076a71da3c 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -26,8 +26,8 @@ if b: # bool(b) is False, so no print print(b.value()[]) var c = a.or_else(2) var d = b.or_else(2) -print(c.value()) # prints 1 -print(d.value()) # prints 2 +print(c) # prints 1 +print(d) # prints 2 ``` """ From 644af8ddd52dfe0aebe2e4f7fc2a117eead2bad1 Mon Sep 17 00:00:00 2001 From: ARVIN DAVOUDI Date: Mon, 29 Apr 2024 07:06:20 -0600 Subject: [PATCH 0339/2019] [External] [mojo-stdlib] Add new `ComparableCollectionElement` trait and `index(list, value, start, end)` function APIs (#38819) [External] [mojo-stdlib] Add new `ComparableCollectionElement` trait and `index(list, value, start, end)` function APIs This pull request introduces the new `index` method to the `collections.List` struct within mojo-stdlib, mirroring the exact functionality found in Python's list, as detailed [here](https://docs.python.org/3/tutorial/datastructures.html). This also allows for the specification of start and end parameters when searching for an item's index within a list, with both parameters accepting positive and negative numbers. Also comprehensive suite of unit tests included meticulously designed to cover a wide range of scenarios for the `index` method's application. Co-authored-by: ARVIN DAVOUDI Closes modularml/mojo#2190 MODULAR_ORIG_COMMIT_REV_ID: efaab8ba57f6d6762e5ad71901db4fd96eea1e1c --- stdlib/src/builtin/value.mojo | 12 ++++ stdlib/src/collections/list.mojo | 64 ++++++++++++++++- stdlib/test/collections/test_list.mojo | 96 +++++++++++++++++++++++++- 3 files changed, 170 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index a1e7067733..f7247bafa7 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -121,3 +121,15 @@ trait StringableCollectionElement(CollectionElement, Stringable): """ pass + + +trait ComparableCollectionElement(CollectionElement, EqualityComparable): + """ + This trait is a temporary solution to enable comparison of + collection elements as utilized in the `index` and `count` methods of + a list. + This approach will be revised with the introduction of conditional trait + conformances. + """ + + pass diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index f610997366..38ac558172 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -23,7 +23,7 @@ from collections import List from builtin.value import StringableCollectionElement from memory import UnsafePointer, Reference from memory.unsafe_pointer import move_pointee, move_from_pointee - +from .optional import Optional # ===----------------------------------------------------------------------===# # Utilties @@ -416,6 +416,68 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): earlier_idx += 1 later_idx -= 1 + # TODO: Modify this to be regular method when issue 1876 is resolved + @staticmethod + fn index[ + C: ComparableCollectionElement + ]( + self: List[C], owned value: C, start: Int = 0, end: Optional[Int] = None + ) raises -> Int: + """ + Returns the index of the first occurrence of a value in a list, starting from the specified + index (default 0). Raises an Error if the value is not found. + + ```mojo + var my_list = List[Int](1, 2, 3) + print(__type_of(my_list).index(my_list, 2)) # Output: 1 + ``` + + Args: + self: The list to search in. + value: The value to search for. + start: The starting index of the search (default 0). + end: The ending index of the search (default None, which means the end of the list). + + Parameters: + C: The type of the elements in the list. Must implement the `ComparableCollectionElement` trait. + + Returns: + The index of the first occurrence of the value in the list. + + Raises: + ValueError If the value is not found in the list. + + """ + var normalized_start = (self.size + start) if start < 0 else start + # TODO: Once the min() and max() functions are available in Mojo, + # TODO: we can simplify the entire if-else block into a single line using the ternary operator: + # var normalized_end = self.size if end is None else min(max(end, 0), self.size) + var normalized_end: Int + if end is None: + normalized_end = self.size + else: + if end.value()[] < 0: + normalized_end = self.size + end.value()[] + else: + if end.value()[] > self.size: + normalized_end = self.size + else: + normalized_end = end.value()[] + + if not self.size: + raise "Cannot find index of a value in an empty list." + if normalized_start >= self.size: + raise "Given 'start' parameter (" + String( + normalized_start + ) + ") is out of range. List only has " + String( + self.size + ) + " elements." + + for i in range(normalized_start, normalized_end): + if self[i] == value: + return i + raise "ValueError: Given element is not in list" + fn clear(inout self): """Clears the elements in the list.""" for i in range(self.size): diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 867b1f137a..d5a73dcb11 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -15,7 +15,7 @@ from collections import List from test_utils import CopyCounter, MoveCounter -from testing import assert_equal, assert_false, assert_true +from testing import assert_equal, assert_false, assert_true, assert_raises def test_mojo_issue_698(): @@ -349,6 +349,99 @@ def test_list_insert(): assert_equal(v4[i], i + 1) +def test_list_index(): + var test_list_a = List[Int](10, 20, 30, 40, 50) + + # Basic Functionality Tests + assert_equal(__type_of(test_list_a).index(test_list_a, 10), 0) + assert_equal(__type_of(test_list_a).index(test_list_a, 30), 2) + assert_equal(__type_of(test_list_a).index(test_list_a, 50), 4) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 60) + + # Tests With Start Parameter + assert_equal(__type_of(test_list_a).index(test_list_a, 30, start=1), 2) + assert_equal(__type_of(test_list_a).index(test_list_a, 30, start=-4), 2) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 30, start=3) + with assert_raises( + contains=( + "Given 'start' parameter (5) is out of range. List only has 5" + " elements." + ) + ): + __type_of(test_list_a).index(test_list_a, 30, start=5) + + # Tests With Start and End Parameters + assert_equal( + __type_of(test_list_a).index(test_list_a, 30, start=1, end=3), 2 + ) + assert_equal( + __type_of(test_list_a).index(test_list_a, 30, start=-4, end=-2), 2 + ) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 30, start=1, end=2) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 30, start=3, end=1) + + # Tests With End Parameter Only + assert_equal(__type_of(test_list_a).index(test_list_a, 30, end=3), 2) + assert_equal(__type_of(test_list_a).index(test_list_a, 30, end=-2), 2) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 30, end=1) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 30, end=2) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 60, end=50) + + # Edge Cases and Special Conditions + assert_equal( + __type_of(test_list_a).index(test_list_a, 10, start=-5, end=-1), 0 + ) + assert_equal( + __type_of(test_list_a).index(test_list_a, 10, start=0, end=50), 0 + ) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 50, start=-5, end=-1) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 50, start=0, end=-1) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 10, start=-4, end=-1) + with assert_raises( + contains=( + "Given 'start' parameter (5) is out of range. List only has 5" + " elements." + ) + ): + __type_of(test_list_a).index(test_list_a, 10, start=5, end=50) + with assert_raises( + contains="Cannot find index of a value in an empty list." + ): + __type_of(List[Int]()).index(List[Int](), 10) + + var test_list_b = List[Int](10, 20, 30, 20, 10) + + # Test finding the first occurrence of an item + assert_equal(__type_of(test_list_b).index(test_list_b, 10), 0) + assert_equal(__type_of(test_list_b).index(test_list_b, 20), 1) + + # Test skipping the first occurrence with a start parameter + assert_equal(__type_of(test_list_b).index(test_list_b, 20, start=2), 3) + + # Test constraining search with start and end, excluding last occurrence + with assert_raises(contains="ValueError: Given element is not in list"): + _ = __type_of(test_list_b).index(test_list_b, 10, start=1, end=4) + + # Test search within a range that includes multiple occurrences + assert_equal( + __type_of(test_list_b).index(test_list_b, 20, start=1, end=4), 1 + ) + + # Verify error when constrained range excludes occurrences + with assert_raises(contains="ValueError: Given element is not in list"): + _ = __type_of(test_list_b).index(test_list_b, 20, start=4, end=5) + + def test_list_extend(): # # Test extending the list [1, 2, 3] with itself @@ -600,6 +693,7 @@ def main(): test_list_reverse() test_list_reverse_move_count() test_list_insert() + test_list_index() test_list_extend() test_list_extend_non_trivial() test_list_explicit_copy() From 0743675a0a810fb95c84a0899f817b7c5798ff28 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 29 Apr 2024 10:11:37 -0600 Subject: [PATCH 0340/2019] Internal commit MODULAR_ORIG_COMMIT_REV_ID: 19e5a4bc90bbd55d35b786a68cda410218d6f152 --- stdlib/src/utils/_math.mojo | 110 +++++++++++++++++++++++++++++++ stdlib/test/utils/test_math.mojo | 43 ++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 stdlib/src/utils/_math.mojo create mode 100644 stdlib/test/utils/test_math.mojo diff --git a/stdlib/src/utils/_math.mojo b/stdlib/src/utils/_math.mojo new file mode 100644 index 0000000000..d638753f34 --- /dev/null +++ b/stdlib/src/utils/_math.mojo @@ -0,0 +1,110 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines basic math functions for use in the open +source parts of the standard library since the `math` +package is currently closed source and cannot be depended +on in the open source parts of the standard library. + +You can import these APIs from the `utils` package. For example: + +```mojo +from utils._math import min, max +``` +""" + +# ===----------------------------------------------------------------------===# +# max +# ===----------------------------------------------------------------------===# + + +@always_inline +fn max(x: Int, y: Int) -> Int: + """Gets the maximum of two integers. + + Args: + x: Integer input to max. + y: Integer input to max. + + Returns: + Maximum of x and y. + """ + return __mlir_op.`index.maxs`(x.value, y.value) + + +@always_inline +fn max[ + type: DType, simd_width: Int +](x: SIMD[type, simd_width], y: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Performs elementwise maximum of x and y. + + An element of the result SIMD vector will be the maximum of the + corresponding elements in x and y. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: First SIMD vector. + y: Second SIMD vector. + + Returns: + A SIMD vector containing the elementwise maximum of x and y. + """ + return x.max(y) + + +# ===----------------------------------------------------------------------===# +# min +# ===----------------------------------------------------------------------===# + + +@always_inline +fn min(x: Int, y: Int) -> Int: + """Gets the minimum of two integers. + + Args: + x: Integer input to max. + y: Integer input to max. + + Returns: + Minimum of x and y. + """ + return __mlir_op.`index.mins`(x.value, y.value) + + +@always_inline +fn min[ + type: DType, simd_width: Int +](x: SIMD[type, simd_width], y: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Gets the elementwise minimum of x and y. + + An element of the result SIMD vector will be the minimum of the + corresponding elements in x and y. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: First SIMD vector. + y: Second SIMD vector. + + Returns: + A SIMD vector containing the elementwise minimum of x and y. + """ + return x.min(y) diff --git a/stdlib/test/utils/test_math.mojo b/stdlib/test/utils/test_math.mojo new file mode 100644 index 0000000000..9df461d78d --- /dev/null +++ b/stdlib/test/utils/test_math.mojo @@ -0,0 +1,43 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from utils._math import min, max +from testing import assert_equal + + +def test_min(): + assert_equal(0, min(0, 1)) + assert_equal(1, min(1, 42)) + + var lhs = SIMD[DType.int32, 4](1, 2, 3, 4) + var rhs = SIMD[DType.int32, 4](0, 1, 5, 7) + var expected = SIMD[DType.int32, 4](0, 1, 3, 4) + assert_equal(expected, lhs.min(rhs)) + assert_equal(expected, rhs.min(lhs)) + + +def test_max(): + assert_equal(1, max(0, 1)) + assert_equal(2, max(1, 2)) + + var lhs = SIMD[DType.int32, 4](1, 2, 3, 4) + var rhs = SIMD[DType.int32, 4](0, 1, 5, 7) + var expected = SIMD[DType.int32, 4](1, 2, 5, 7) + assert_equal(expected, lhs.max(rhs)) + assert_equal(expected, rhs.max(lhs)) + + +def main(): + test_min() + test_max() From 67e201e15d95a9a2b30a56b40dc316936eed3394 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 29 Apr 2024 09:51:19 -0700 Subject: [PATCH 0341/2019] [Docs] Add docs on heterogenous variadic arguments. (#38614) MODULAR_ORIG_COMMIT_REV_ID: e58fccc20f57501e67464dc6365aadde53396e54 --- docs/manual/functions.ipynb | 262 ++++++++++++++++++++++++----- docs/manual/parameters/index.ipynb | 9 +- 2 files changed, 226 insertions(+), 45 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 77d965493d..52922ab672 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -75,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -221,7 +221,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -239,7 +239,11 @@ "metadata": {}, "source": [ "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout)." + "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout).\n", + "\n", + "Any optional arguments must appear after any required arguments. [Keyword-only\n", + "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", + "also be either required or optional." ] }, { @@ -255,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -281,14 +285,14 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fn sum(*values: Int) -> Int:\n", " var sum: Int = 0\n", " for value in values:\n", - " sum = sum+value\n", + " sum = sum + value\n", " return sum" ] }, @@ -305,22 +309,75 @@ "specified by keyword (see \n", "[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)).\n", "\n", - "Currently variadic arguments must be a single type—all `Int`, or all `String`,\n", - "for example. A few standard library APIs, such as\n", - "[`print()`](/mojo/stdlib/builtin/io/print), support mixed-type, or\n", - "heterogeneous, variadic arguments, but this currently requires working with\n", - "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", - "Mojo in the future.\n", + "Variadic arguments can be divided into two categories:\n", + "\n", + "- Homogeneous variadic arguments, where all of the passed arguments are the same\n", + " type—all `Int`, or all `String`, for example. \n", + "- Heterogeneous variadic arguments, which can accept a set of different argument\n", + " types.\n", + "\n", + "The following sections describe how to work with homogeneous and heterogenous\n", + "variadic arguments.\n", + "\n", + ":::note Variadic parameters\n", + "\n", + "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", + "(parameters are used for compile-time metaprogramming). Variadic parameters\n", + "are supported, but with some limitations—for details see \n", + "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", + "\n", + ":::\n", "\n", - "Inside the function body, the variadic argument is available an iterable list\n", - "for ease of use. But there are some differences in handling the list depending\n", - "on whether the arguments are register-passable types (such as `Int`) or\n", - "memory-only types (such as `String`).\n", + "\n", + "### Homogeneous variadic arguments\n", + "\n", + "When defining a homogeneous variadic argument, use \n", + "*argument_name: argument_type:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def greet(*names: String):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Inside the function body, the variadic argument is available as an iterable list\n", + "for ease of use. Current there are some differences in handling the list \n", + "depending on whether the arguments are register-passable types (such as `Int`)\n", + "or memory-only types (such as `String`). TODO: We hope to remove these\n", + "differences in the future.\n", "\n", "Register-passable types, such as `Int`, are available as a \n", "[`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", - "loop.\n", + "loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn sum(*values: Int) -> Int:\n", + " var sum: Int = 0\n", + " for value in values:\n", + " sum = sum+value\n", + " return sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem).\n", @@ -332,11 +389,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "fn make_worldly(inout *strs: String):\n", + "def make_worldly(inout *strs: String):\n", " # Requires extra [] to dereference the reference for now.\n", " for i in strs:\n", " i[] += \" world\"\n" @@ -362,15 +419,129 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ":::note Variadic parameters\n", + "### Heterogeneous variadic arguments\n", "\n", - "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", - "(parameters are used for compile-time metaprogramming). However, most rules\n", - "that apply to argument lists also apply to parameter lists. Variadic parameters\n", - "are supported, but with some limitations—for details see \n", - "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", + "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", + "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", + "types requires [traits](/mojo/manual/traits) and \n", + "[parameters](/mojo/manual/parameters/). So the syntax may look a little\n", + "unfamiliar if you haven't worked with those features. The signature for a\n", + "function with a heterogeneous variadic argument looks like this:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```mojo\n", + "def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):\n", + " ...\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an\n", + "`ArgTypes` parameter, which is a list of types, all of which conform to the \n", + "[`Intable`](/mojo/stdlib/builtin/int/Intable) trait. The argument list, \n", + "`(*args: *ArgTypes)` has the familiar `*args` for the variadic argument, but \n", + "instead of a single type, its type is defined as _list_ of types, `*ArgTypes`.\n", + "\n", + "This means that each argument in `args` has a corresponding type in `ArgTypes`, \n", + "so args[n] is of type \n", + "ArgTypes[n].\n", + "\n", + "Inside the function, `args` is available as a\n", + "[`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack). The easiest\n", + "way to work with the arguments is to use the `each()` method to iterate through\n", + "the `VariadicPack`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "28\n" + ] + } + ], + "source": [ + "fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:\n", + " var total = 0\n", "\n", - ":::\n" + " @parameter\n", + " fn add[Type: Intable](value: Type):\n", + " total += int(value)\n", + "\n", + " args.each[add]()\n", + " return total\n", + "\n", + "print(count_many_things(5, 11.7, 12))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the example above, the `add()` function is called for each argument in turn,\n", + "with the appropriate `value` and `Type` values. For instance, `add()` is first\n", + "called with `value=5` and `Type=Int`, then with `value=11.7` and `Type=Float64`.\n", + "\n", + "Also, note that when calling `count_many_things()`, you don't actually pass in\n", + "a list of argument types. You only need to pass in the arguments, and Mojo\n", + "generates the `ArgTypes` list itself.\n", + "\n", + "As a small optimization, if your function is likely to be called with a single\n", + "argument frequently, you can define your function with a single argument\n", + "followed by a variadic argument. This lets the simple case bypass populating and\n", + "iterating through the `VariadicPack`.\n", + "\n", + "For example, given a `print_string()` function that prints a single string, you\n", + "could re-implement the variadic `print()` function with code like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bob0\n" + ] + } + ], + "source": [ + " fn print_string(s: String):\n", + " print(s, end=\"\")\n", + "\n", + " fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):\n", + " print_string(str(first))\n", + "\n", + " @parameter\n", + " fn print_elt[T: Stringable](a: T):\n", + " print_string(\" \")\n", + " print_string(a)\n", + " rest.each[print_elt]()\n", + " print_many(\"Bob\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you call `print_many()` with a single argument, it calls `print_string()`\n", + "directly. The `VariadicPack` is empty, so `each()` returns immediately without\n", + "calling the `print_elt()` function." ] }, { @@ -397,8 +568,10 @@ "\n", " In this example, the argument name `kwargs` is a placeholder that accepts any\n", " number of keyword arguments. Inside the body of the function, you can access\n", - " the arguments as a [`Dict`](/mojo/stdlib/collections/dict) of keywords and\n", - " argument values.\n", + " the arguments as a dictionary of keywords and argument values (specifically,\n", + " an instance of\n", + " [`OwnedKwargsDict`](/mojo/stdlib/collections/dict/OwnedKwargsDict)).\n", + " \n", " \n", " There are currently a few limitations:\n", "\n", @@ -415,18 +588,12 @@ " - All the variadic keyword arguments must have the same type, and this\n", " determines the type of the argument dictionary. For example, if the argument\n", " is `**kwargs: Float64` then the argument dictionary will be a \n", - " `Dict[String, Float64]`.\n", - "\n", - " - Functions with variadic keyword arguments can't have default values for\n", - " keyword-only arguments. For example:\n", + " `OwnedKwargsDict[Float64]`.\n", "\n", - " ```mojo\n", - " # Not allowed yet, because `b` is keyword-only with a default.\n", - " fn not_yet(*, b: Int = 9, **kwargs: Int): ...\n", - "\n", - " # Okay, because `c` is positional-or-keyword, so it can have a default.\n", - " fn still_works(c: Int = 5, **kwargs: Int): ...\n", - " ```\n", + " - The argument type must conform to the \n", + " [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", + " That is, the type must be both [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", + " and [`Copyable`](/mojo/stdlib/builtin/value/Copyable).\n", "\n", " - Dictionary unpacking is not supported yet:\n", "\n", @@ -529,7 +696,18 @@ "Keyword-only arguments often have default values, but this is not required. If a\n", "keyword-only argument doesn't have a default value, it is a _required \n", "keyword-only argument_. It must be specified, and it must be specified by \n", - "keyword.\n", + "keyword. \n", + "\n", + "Any required keyword-only arguments must appear in the signature before\n", + "any optional keyword-only arguments. That is, arguments appear in the following\n", + "sequence a function signature:\n", + "\n", + "* Required positional arguments.\n", + "* Optional positional arguments.\n", + "* Variadic arguments.\n", + "* Required keyword-only arguments.\n", + "* Optional keyword-only arguments.\n", + "* Variadic keyword arguments.\n", "\n", "For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only\n", "Arguments](https://peps.python.org/pep-3102/)." diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 2005e81c02..463005cad7 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -654,11 +654,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Variadic parameters have some limitations that variadic arguments don't have:\n", + "Variadic parameters currently have some limitations that variadic arguments don't have:\n", "\n", - "- Only variadic parameters of register-passable types are supported currently.\n", + "- Variadic parameters must be homogeneous—that is, all the values must be the\n", + " same type. \n", + " \n", + "- The parameter type must be register-passable.\n", "\n", - "- The parameters aren't automatically projected into a `VariadicList`, so you\n", + "- The parameter values aren't automatically projected into a `VariadicList`, so you\n", " need to construct the list explicitly:" ] }, From 576a520a8412e72db3e046d966b487e3776b5218 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 29 Apr 2024 09:51:55 -0700 Subject: [PATCH 0342/2019] [docs] Update perf dashboard links (#38824) Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: f4d0446952cf0127df0b0c04b1529f2cf2cdbfe3 --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 11a21e5fab..7b8ea4e505 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -275,7 +275,7 @@ about our kernel performance in our [matrix multiplication blog post](https://www.modular.com/blog/the-worlds-fastest-unified-matrix-multiplication). For details about our end-to-end model performance relative to the latest releases of TensorFlow and PyTorch, check out our [performance -dashboard](https://performance.modular.com). +dashboard](https://www.modular.com/max/performance). ## Mojo SDK From 27a539529933a1a4ac05836778fa9af542ea8507 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 29 Apr 2024 10:01:47 -0700 Subject: [PATCH 0343/2019] [mojo-lang] Allow MBValues to propagate parametric mutability. (#38822) This allows MBValues to propagate parametric mutability into the `Reference` constructor, eliminating some weird cases that forced use of `UnsafePointer`. MODULAR_ORIG_COMMIT_REV_ID: 1a9680a5e14b7fee47b6031a8afc723a0e46bc32 --- stdlib/src/collections/dict.mojo | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index ef32e7fcfe..f44446181f 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -31,7 +31,6 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ -from memory import UnsafePointer from builtin.value import StringableCollectionElement from .optional import Optional @@ -189,7 +188,8 @@ struct _DictValueIter[ fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() - # Cast through a pointer to grant additional mutability. + # Cast through a pointer to grant additional mutability because + # _DictEntryIter.next erases it. return UnsafePointer.address_of(entry_ref[].value)[] fn __len__(self) -> Int: @@ -987,7 +987,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): # return self._dict.__iter__() return _DictKeyIter( _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, UnsafePointer(self)[]._dict + 0, 0, Reference(self)[]._dict ) ) @@ -1027,12 +1027,9 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ # TODO(#36448): Use this instead of the current workaround # return self._dict.values() - # Use UnsafePointer here to cast away conditional mutability. return _DictValueIter( _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, - 0, - UnsafePointer(self)[]._dict, + 0, 0, Reference(self)[]._dict ) ) @@ -1060,10 +1057,9 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ # TODO(#36448): Use this instead of the current workaround - # return UnsafePointer(self)[]._dict.items() - # Use UnsafePointer here to cast away conditional mutability. + # return Reference(self)[]._dict.items() return _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, UnsafePointer(self)[]._dict + 0, 0, Reference(self)[]._dict ) @always_inline("nodebug") From d6d70c7dd81769409bb183f1ae919660cc8d0893 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 29 Apr 2024 10:14:13 -0700 Subject: [PATCH 0344/2019] [Docs] Update text to match updated example. (#38701) Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 64f9e89e8c0a823c0be3d8f2b74b7f5b25e043ef --- docs/manual/lifecycle/death.ipynb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 01885c9128..c2d67ed01f 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -198,8 +198,8 @@ "metadata": {}, "source": [ "There's no need to define the `__del__()` destructor for this, because it's a\n", - "simple collection of other types (`String` and `Int`), and it doesn't dynamically\n", - "allocate memory. \n", + "simple collection of other types (`String` and `Int`), and it doesn't \n", + "dynamically allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" @@ -278,8 +278,11 @@ "\n", ":::\n", "\n", - "If `self.data` was an instance of `AnyPointer`, you'd need to use slightly\n", - "different code:\n", + "For instances of the \n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type, use\n", + "the [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", + "function instead of the discard pattern. For example, if the previous example\n", + "used `UnsafePointer`, the `__del__()` method would look like this:\n", "\n", "```mojo\n", "fn __del__(owned self):\n", From 3bd86bf2925c212d7060e36265bd815d63228ab1 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 29 Apr 2024 12:17:14 -0700 Subject: [PATCH 0345/2019] [docs] Remove auth install step and suggest nightlies (#38857) ![Screenshot 2024-04-29 at 12 09 45 [Internal link] MODULAR_ORIG_COMMIT_REV_ID: f54d89e35b2e53a85fee37c4ea0abb61a747dc6d --- docs/manual/get-started/index.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 0f226917f7..572c8b8e19 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -84,19 +84,24 @@ If you already have the `modular` tool, curl -s https://get.modular.com | sh - ``` -2. Then sign in to your Modular account with this command: +2. Then install the Mojo SDK: ```sh - modular auth + modular install mojo ``` -3. Now you can install the Mojo SDK: + :::note Get nightlies + + If you want the bleeding-edge (less stable) version, instead install the + nightly build: ```sh - modular install mojo + modular install nightly/mojo ``` -4. Set environment variables so you can access the + ::: + +3. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: From 998253315925142401718bebac38bb65d4e7c51d Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Tue, 30 Apr 2024 06:45:31 -0600 Subject: [PATCH 0346/2019] [External] [stdlib] Removed FileCheck from the dev guide (#38904) [External] [stdlib] Removed FileCheck from the dev guide Related to #2024 Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2451 MODULAR_ORIG_COMMIT_REV_ID: a32efb8642527372845fa6eeb43b997824f8f2be --- stdlib/docs/development.md | 60 +++++++++++++++----------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 19263abdb6..5cf444ffcf 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -153,8 +153,8 @@ Let's try adding a small piece of functionality to `path.mojo`: ```mojo # ./stdlib/src/pathlib/path.mojo -fn print_cwd() raises: - print("cwd:", cwd()) +fn get_cwd_message() raises -> String: + return "Your cwd is: " + cwd() ``` And make sure you're importing it from the module root: @@ -165,7 +165,7 @@ And make sure you're importing it from the module root: from .path import ( DIR_SEPARATOR, cwd, - print_cwd, + get_cwd_message, Path, ) ``` @@ -180,13 +180,13 @@ functionality before you write tests: from src import pathlib def main(): - pathlib.print_cwd() + print(pathlib.get_cwd_message()) ``` Now when you run `mojo main.mojo` it'll reflect the changes: ```plaintext -cwd: /Users/jack/src/mojo/stdlib +Your cwd is: /Users/jack/src/mojo/stdlib ``` ### A change with dependencies @@ -202,10 +202,11 @@ Try adding this to `os.mojo`, which depends on what we just added to import pathlib -fn print_paths() raises: - pathlib.print_cwd() +fn get_cwd_and_paths() raises -> List[String]: + result = List[String]() + result.append(pathlib.get_cwd_message()) for path in cwd().listdir(): - print(path[]) + result.append(str(path[])) ``` This won't work because it's importing `pathlib` from the `stdlib.mojopkg` that @@ -224,7 +225,8 @@ syntax: import os def main(): - os.print_paths() + all_paths = os.get_cwd_and_paths() + print(__type_of(all_paths).__str__(all_paths)) ``` We also need to set the following environment variable that tells Mojo to @@ -268,30 +270,25 @@ If you haven't already, follow the steps at: ### Adding a test -This will show you how the `FileCheck` utility works. First, turn it on by -adding it to the end of line 7 in `./stdlib/test/pathlib/test_pathlib.mojo`: +This section will show you how to add a unit test. +Add the following at the top of `./stdlib/test/pathlib/test_pathlib.mojo`: ```mojo -# RUN: %mojo -debug-level full -D TEMP_FILE=%t %s | FileCheck %s +# RUN: %mojo %s ``` Now we can add the test and force it to fail: ```mojo -# CHECK-LABEL: test_print_cwd - -def test_print_cwd(): - print("== test_print_cwd") - - # CHECK: This will fail - print_cwd() +def test_get_cwd_message(): + assert_equal(get_cwd_message(), "Some random text") ``` Don't forget to call it from `main()`: ```bash def main(): - test_print_cwd() + test_get_cwd_message() ``` Now, instead of testing all the modules, we can just test `pathlib`: @@ -304,29 +301,20 @@ This will give you an error showing exactly what went wrong: ```plaintext /Users/jack/src/mojo-toy-2/stdlib/test/pathlib/test_pathlib.mojo:27:11: -error: CHECK: expected string not found in input - -# CHECK: This will fail - ^ -:1:18: note: scanning from here -== test_print_cwd - ^ +AssertionError: `left == right` comparison failed: + left: Your cwd is: /Users/jack/src/mojo/stdlib + right: Some random text ``` Lets fix the test that we just added: ```mojo -# CHECK-LABEL: test_print_cwd - -def test_print_cwd(): - print("== test_print_cwd") - - # CHECK: cwd: - print_cwd() +def test_get_cwd_message(): + assert_true(get_cwd_message().startswith("Your cwd is:")) ``` -We're now checking that `print_cwd` is prefixed with `cwd:` just like the -function we added. Run the test again: +We're now checking that `get_cwd_message` is prefixed with `Your cwd is:` just +like the function we added. Run the test again: ```plaintext Testing Time: 0.65s From f8c51efedfc7297629cfee2874da2a72a61088fc Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Apr 2024 06:47:41 -0600 Subject: [PATCH 0347/2019] [stdlib] Narrow disabling of test in `test_string.mojo` (#38209) Only one test case is failing in debug builds on Graviton; so, disable that one test only rather than the whole test file. We could do fancy things and keep the test enabled on other arch, but just disable the test for all arches right now for simplicity. MODULAR_ORIG_COMMIT_REV_ID: ee81e10cc7d6d6fb15fe6618674b75b7f3e800e2 --- stdlib/test/builtin/test_string.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 29bdfe5f70..56bd80794c 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -719,7 +719,9 @@ def main(): test_islower() test_lower() test_upper() - test_isspace() + # TODO(37393): Re-enable once we debug why we are depending on some debug behavior + # on graviton. Showing an error in our O3 LLVM pipeline; could be a bug in LLVM. + # test_isspace() test_rstrip() test_lstrip() test_strip() From 59bb074372c9cf410e621bd2f24564ed74f9783b Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 30 Apr 2024 13:57:32 -0400 Subject: [PATCH 0348/2019] [******][mojo-stdlib] Implement generic `floor` method in `math` (#38777) To achieve this, the patch also introduces a `Floorable` trait. This method is in `math` in Python, so we also should expose it there. However, unlike, Python, the generic `floor` method doesn't return an integer, but rather the same type as its input. To ensure that builtins can use `Floorable` without depending on `math`, a hidden `builtin._math` module is introduced, which should eventually be removed in favor of an open source `math` module. MODULAR_ORIG_COMMIT_REV_ID: 75c763ca35050fe81ca0e61d78686c705c9d3803 --- docs/changelog.md | 18 +++++- stdlib/src/builtin/_math.mojo | 49 +++++++++++++++ stdlib/src/builtin/float_literal.mojo | 40 +++++++++++- stdlib/src/builtin/int.mojo | 14 ++++- stdlib/src/builtin/int_literal.mojo | 15 ++++- stdlib/src/builtin/simd.mojo | 69 +++++++++------------ stdlib/src/time/time.mojo | 4 +- stdlib/test/builtin/test_float_literal.mojo | 15 +++++ stdlib/test/builtin/test_int.mojo | 7 +++ stdlib/test/builtin/test_int_literal.mojo | 7 +++ stdlib/test/builtin/test_simd.mojo | 24 +++++++ 11 files changed, 214 insertions(+), 48 deletions(-) create mode 100644 stdlib/src/builtin/_math.mojo diff --git a/docs/changelog.md b/docs/changelog.md index f19f92999d..fe87c520b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -292,7 +292,7 @@ what we publish. string contains the prefix `"0x"`, `"0o"`, or `"0b"`. - Mojo now supports types to opt in to use the `abs` function by implementing - the `__abs__` method (i.e. by conforming to the now `Absable` trait), e.g.: + the `__abs__` method (i.e. by conforming to the new `Absable` trait), e.g.: ```mojo from math import sqrt @@ -305,6 +305,22 @@ what we publish. return sqrt(self.x * self.x + self.y * self.y) ``` +- Mojo now supports types to opt in to use the `floor` function in the `math` + module by implementing the `__floor__` method (and so conforming to the new + `math.Floorable` trait), for example: + + ```mojo + from math import Floorable, floor + + @value + struct Complex(Floorable): + var re: Float64 + var im: Float64 + + fn __floor__(self) -> Self: + return Self(floor(re), floor(im)) + ``` + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo new file mode 100644 index 0000000000..5604eaff02 --- /dev/null +++ b/stdlib/src/builtin/_math.mojo @@ -0,0 +1,49 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Module to contain some components of the future math module. + +This is needed to work around some circular dependencies; all elements of this +module should be exposed by the current `math` module. The contents of this +module should be eventually moved to the `math` module when it's open sourced. +""" + +# ===----------------------------------------------------------------------===# +# Floorable +# ===----------------------------------------------------------------------===# + + +trait Floorable: + """ + The `Floorable` trait describes a type that defines a floor operation. + + Types that conform to `Floorable` will work with the builtin `floor` + function. The floor operation always returns the same type as the input. + + For example: + ```mojo + from math import Floorable, floor + + @value + struct Complex(Floorable): + var re: Float64 + var im: Float64 + + fn __floor__(self) -> Self: + return Self(floor(re), floor(im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __floor__(self) -> Self: + ... diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 77adb48dc8..e72517b869 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -15,6 +15,8 @@ These are Mojo built-ins, so you don't need to import them. """ +from builtin._math import Floorable + # ===----------------------------------------------------------------------===# # FloatLiteral # ===----------------------------------------------------------------------===# @@ -23,7 +25,9 @@ These are Mojo built-ins, so you don't need to import them. @value @nonmaterializable(Float64) @register_passable("trivial") -struct FloatLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): +struct FloatLiteral( + Absable, Boolable, EqualityComparable, Floorable, Intable, Stringable +): """Mojo floating point literal type.""" alias fp_type = __mlir_type.`!kgen.float_literal` @@ -102,6 +106,17 @@ struct FloatLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): special = __mlir_attr.`#kgen` ](self.value) + @always_inline("nodebug") + fn _is_normal(self) -> Bool: + """Return whether the FloatLiteral is a normal (i.e. not special) value. + + Returns: + True, if the value is a normal float, False otherwise. + """ + return __mlir_op.`kgen.float_literal.isa`[ + special = __mlir_attr.`#kgen` + ](self.value) + # ===------------------------------------------------------------------===# # Conversion Operators # ===------------------------------------------------------------------===# @@ -166,7 +181,7 @@ struct FloatLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): @always_inline("nodebug") fn __abs__(self) -> Self: - """Return the absolute value of FloatLiteral value. + """Return the absolute value of the FloatLiteral. Returns: The absolute value. @@ -175,6 +190,27 @@ struct FloatLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): return self return -self + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Return the floor value of the FloatLiteral. + + Returns: + The floor value. + """ + + # Handle special values first. + if not self._is_normal(): + return self + + # __int_literal__ rounds towards zero, so it's correct for integers and + # positive values. + var truncated: IntLiteral = self.__int_literal__() + + # Ensure this equality doesn't hit any implicit conversions. + if self >= 0 or self.__eq__(Self(truncated)): + return truncated + return truncated - 1 + # ===------------------------------------------------------------------===# # Arithmetic Operators # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 4e9f679536..c87983b8b7 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement +from builtin._math import Floorable from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf @@ -192,7 +193,9 @@ fn int(value: String, base: Int = 10) raises -> Int: @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct Int(Absable, Intable, Stringable, KeyElement, Boolable, Formattable): +struct Int( + Absable, Boolable, Floorable, Formattable, Intable, KeyElement, Stringable +): """This type represents an integer value.""" var value: __mlir_type.index @@ -509,6 +512,15 @@ struct Int(Absable, Intable, Stringable, KeyElement, Boolable, Formattable): """ return -self if self < 0 else self + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Return the floor of the Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + @always_inline("nodebug") fn __invert__(self) -> Int: """Return ~self. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 0ff335f42a..f948005aac 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,11 +12,15 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" +from builtin._math import Floorable + @value @nonmaterializable(Int) @register_passable("trivial") -struct IntLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): +struct IntLiteral( + Absable, Boolable, EqualityComparable, Floorable, Intable, Stringable +): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and must be lowered to other integer types (like Int), but allow for @@ -240,6 +244,15 @@ struct IntLiteral(Absable, Intable, Stringable, Boolable, EqualityComparable): return self return -self + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Return the floor of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + @always_inline("nodebug") fn __invert__(self) -> Self: """Return ~self. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6044c868e9..272fac111a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -18,6 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import llvm_intrinsic, has_neon, is_x86, simdwidthof, _RegisterPackType +from builtin._math import Floorable from builtin.hash import _hash_simd from memory import bitcast @@ -114,12 +115,13 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, - Sized, - Intable, + Boolable, CollectionElement, - Stringable, + Floorable, Hashable, - Boolable, + Intable, + Sized, + Stringable, ): """Represents a small vector that is backed by a hardware vector element. @@ -617,7 +619,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type.is_floating_point(): - return _floor(div) + return div.__floor__() elif type.is_unsigned(): return div else: @@ -887,6 +889,26 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return (self < 0).select(-self, self) + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Performs elementwise floor on the elements of a SIMD vector. + + Returns: + The elementwise floor of this SIMD vector. + """ + + @parameter + if type.is_bool() or type.is_integral(): + return self + + @parameter + if has_neon() and type == DType.bfloat16: + return self.cast[DType.float32]().__floor__().cast[type]() + + return llvm_intrinsic[ + "llvm.floor", SIMD[type, size], has_side_effect=False + ](self) + # ===-------------------------------------------------------------------===# # In place operations. # ===-------------------------------------------------------------------===# @@ -2327,7 +2349,7 @@ fn _pow[ @parameter if rhs_type.is_floating_point() and lhs_type == rhs_type: - var rhs_quotient = _floor(rhs) + var rhs_quotient = rhs.__floor__() if rhs >= 0 and rhs_quotient == rhs: return _pow(lhs, rhs_quotient.cast[_integral_type_of[rhs_type]()]()) @@ -2382,41 +2404,6 @@ fn _pow[ return SIMD[lhs_type, simd_width]() -# ===----------------------------------------------------------------------===# -# floor -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn _floor[ - type: DType, simd_width: Int -](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Performs elementwise floor on the elements of a SIMD vector. - - Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. - - Args: - x: SIMD vector to perform floor on. - - Returns: - The elementwise floor of x. - """ - - @parameter - if type.is_bool() or type.is_integral(): - return x - - @parameter - if has_neon() and type == DType.bfloat16: - return _floor(x.cast[DType.float32]()).cast[type]() - - return llvm_intrinsic[ - "llvm.floor", SIMD[type, simd_width], has_side_effect=False - ](x) - - # ===----------------------------------------------------------------------===# # bfloat16 # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 842cc4e7e2..0d1d848605 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -21,7 +21,7 @@ from time import now from sys import external_call, os_is_linux, os_is_windows -from builtin.simd import _floor +from math import floor from memory import UnsafePointer # ===----------------------------------------------------------------------===# @@ -246,7 +246,7 @@ fn sleep(sec: Float64): sec: The number of seconds to sleep for. """ alias NANOSECONDS_IN_SECOND = 1_000_000_000 - var total_secs = _floor(sec) + var total_secs = floor(sec) var tv_spec = _CTimeSpec( int(total_secs.cast[DType.index]()), int((sec - total_secs) * NANOSECONDS_IN_SECOND), diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index a6e2008c6d..1af9d40d4b 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -26,6 +26,20 @@ alias inf = FloatLiteral.infinity alias neg_inf = FloatLiteral.negative_infinity +def test_floor(): + assert_equal(FloatLiteral.__floor__(1.5), 1.0) + assert_equal(FloatLiteral.__floor__(1.6), 1.0) + assert_equal(FloatLiteral.__floor__(-1.5), -2.0) + assert_equal(FloatLiteral.__floor__(-3.4), -4.0) + assert_equal(FloatLiteral.__floor__(3.0), 3.0) + assert_equal(FloatLiteral.__floor__(0.0), 0.0) + + assert_true(FloatLiteral.__floor__(nan).is_nan()) + assert_true(FloatLiteral.__floor__(neg_zero).is_neg_zero()) + assert_equal(FloatLiteral.__floor__(inf), inf) + assert_equal(FloatLiteral.__floor__(neg_inf), neg_inf) + + def test_division(): # TODO: https://github.com/modularml/mojo/issues/1787 # allow this at compile time @@ -92,6 +106,7 @@ def test_abs(): def main(): + test_floor() test_division() test_power() test_int_conversion() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 9a32f53117..876382204f 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -49,6 +49,12 @@ def test_pow(): assert_equal(81, Int(3) ** Int(4)) +def test_floor(): + assert_equal(Int.__floor__(Int(5)), 5) + assert_equal(Int.__floor__(Int(0)), 0) + assert_equal(Int.__floor__(Int(-5)), -5) + + def test_floordiv(): assert_equal(1, Int(2) // Int(2)) assert_equal(0, Int(2) // Int(3)) @@ -82,6 +88,7 @@ def main(): test_sub() test_div() test_pow() + test_floor() test_floordiv() test_mod() test_abs() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 9f8765b80f..844bdcf422 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -22,6 +22,12 @@ def test_int(): assert_equal(6 - 1, 5) +def test_floor(): + assert_equal(IntLiteral.__floor__(5), 5) + assert_equal(IntLiteral.__floor__(0), 0) + assert_equal(IntLiteral.__floor__(-5), -5) + + def test_floordiv(): assert_equal(2 // 2, 1) assert_equal(2 // 3, 0) @@ -56,6 +62,7 @@ def test_abs(): def main(): test_int() + test_floor() test_floordiv() test_mod() test_bit_width() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 61cd085143..f038b34424 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -146,6 +146,29 @@ def test_truthy(): test_dtype[DType.bfloat16]() +def test_floor(): + assert_equal(Float32.__floor__(Float32(1.5)), 1.0) + assert_equal(Float32.__floor__(Float32(-1.5)), -2.0) + assert_equal(Float32.__floor__(Float32(3.0)), 3.0) + + alias F = SIMD[DType.float32, 4] + assert_equal( + F.__floor__(F(0.0, 1.6, -42.5, -12.4)), F(0.0, 1.0, -43.0, -13.0) + ) + + alias I = SIMD[DType.int32, 4] + var i = I(0, 2, -42, -12) + assert_equal(I.__floor__(i), i) + + alias U = SIMD[DType.uint32, 4] + var u = U(0, 2, 42, 12) + assert_equal(U.__floor__(u), u) + + alias B = SIMD[DType.bool, 4] + var b = B(True, False, True, False) + assert_equal(B.__floor__(b), b) + + def test_floordiv(): assert_equal(Int32(2) // Int32(2), 1) assert_equal(Int32(2) // Int32(3), 0) @@ -743,6 +766,7 @@ def main(): test_convert_simd_to_string() test_issue_20421() test_truthy() + test_floor() test_floordiv() test_mod() test_rotate() From d1b3fed6ac3f10fc2fe5ec534a6125f2e01976cb Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 30 Apr 2024 14:36:56 -0400 Subject: [PATCH 0349/2019] [******][mojo-stdlib] Implement generic `ceil` method in `math` (#38807) Similarly to `floor`, this relies on a `Ceilable` trait, implemented in `builtin._math`. The implementation of `SIMD.__{floor,ceil}__` is also refactored to allow more code reuse. MODULAR_ORIG_COMMIT_REV_ID: 96764b377b3c3efd13aab193b78e483d64bf7a86 --- docs/changelog.md | 14 ++++--- stdlib/src/builtin/_math.mojo | 32 ++++++++++++++++ stdlib/src/builtin/float_literal.mojo | 31 +++++++++++++++- stdlib/src/builtin/int.mojo | 20 +++++++++- stdlib/src/builtin/int_literal.mojo | 19 +++++++++- stdlib/src/builtin/simd.mojo | 41 ++++++++++++++++----- stdlib/test/builtin/test_float_literal.mojo | 14 +++++++ stdlib/test/builtin/test_int.mojo | 7 ++++ stdlib/test/builtin/test_int_literal.mojo | 7 ++++ stdlib/test/builtin/test_simd.mojo | 24 ++++++++++++ 10 files changed, 188 insertions(+), 21 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index fe87c520b5..70616aaad0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -305,18 +305,22 @@ what we publish. return sqrt(self.x * self.x + self.y * self.y) ``` -- Mojo now supports types to opt in to use the `floor` function in the `math` - module by implementing the `__floor__` method (and so conforming to the new - `math.Floorable` trait), for example: +- Mojo now supports types to opt in to use the `floor` and `ceil` functions in + the `math` module by implementing the `__floor__` and `__ceil__` methods (and + so conforming to the new `math.Floorable` and `math.Ceilable` traits) + respectively. For example: ```mojo - from math import Floorable, floor + from math import Ceilable, Floorable, ceil, floor @value - struct Complex(Floorable): + struct Complex(Ceilable, Floorable): var re: Float64 var im: Float64 + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) + fn __floor__(self) -> Self: return Self(floor(re), floor(im)) ``` diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 5604eaff02..8480dc8c7e 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -17,6 +17,38 @@ module should be exposed by the current `math` module. The contents of this module should be eventually moved to the `math` module when it's open sourced. """ +# ===----------------------------------------------------------------------===# +# Ceilable +# ===----------------------------------------------------------------------===# + + +trait Ceilable: + """ + The `Ceilable` trait describes a type that defines a ceiling operation. + + Types that conform to `Ceilable` will work with the builtin `ceil` + function. The ceiling operation always returns the same type as the input. + + For example: + ```mojo + from math import Ceilable, ceil + + @value + struct Complex(Ceilable): + var re: Float64 + var im: Float64 + + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __ceil__(self) -> Self: + ... + + # ===----------------------------------------------------------------------===# # Floorable # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index e72517b869..739454dd68 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin._math import Floorable +from builtin._math import Ceilable, Floorable # ===----------------------------------------------------------------------===# # FloatLiteral @@ -26,7 +26,13 @@ from builtin._math import Floorable @nonmaterializable(Float64) @register_passable("trivial") struct FloatLiteral( - Absable, Boolable, EqualityComparable, Floorable, Intable, Stringable + Absable, + Boolable, + Ceilable, + EqualityComparable, + Floorable, + Intable, + Stringable, ): """Mojo floating point literal type.""" @@ -211,6 +217,27 @@ struct FloatLiteral( return truncated return truncated - 1 + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Return the ceiling value of the FloatLiteral. + + Returns: + The ceiling value. + """ + + # Handle special values first. + if not self._is_normal(): + return self + + # __int_literal__ rounds towards zero, so it's correct for integers and + # negative values. + var truncated: IntLiteral = self.__int_literal__() + + # Ensure this equality doesn't hit any implicit conversions. + if self <= 0 or self.__eq__(Self(truncated)): + return truncated + return truncated + 1 + # ===------------------------------------------------------------------===# # Arithmetic Operators # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index c87983b8b7..f03b7ab47b 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement -from builtin._math import Floorable +from builtin._math import Ceilable, Floorable from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf @@ -194,7 +194,14 @@ fn int(value: String, base: Int = 10) raises -> Int: @value @register_passable("trivial") struct Int( - Absable, Boolable, Floorable, Formattable, Intable, KeyElement, Stringable + Absable, + Boolable, + Ceilable, + Floorable, + Formattable, + Intable, + KeyElement, + Stringable, ): """This type represents an integer value.""" @@ -512,6 +519,15 @@ struct Int( """ return -self if self < 0 else self + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Return the ceiling of the Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + @always_inline("nodebug") fn __floor__(self) -> Self: """Return the floor of the Int value, which is itself. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index f948005aac..44734d2d67 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,14 +12,20 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" -from builtin._math import Floorable +from builtin._math import Ceilable, Floorable @value @nonmaterializable(Int) @register_passable("trivial") struct IntLiteral( - Absable, Boolable, EqualityComparable, Floorable, Intable, Stringable + Absable, + Boolable, + Ceilable, + EqualityComparable, + Floorable, + Intable, + Stringable, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -244,6 +250,15 @@ struct IntLiteral( return self return -self + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Return the ceiling of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + @always_inline("nodebug") fn __floor__(self) -> Self: """Return the floor of the IntLiteral value, which is itself. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 272fac111a..4265c3c7f2 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import llvm_intrinsic, has_neon, is_x86, simdwidthof, _RegisterPackType -from builtin._math import Floorable +from builtin._math import Ceilable, Floorable from builtin.hash import _hash_simd from memory import bitcast @@ -116,6 +116,7 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, Boolable, + Ceilable, CollectionElement, Floorable, Hashable, @@ -889,13 +890,11 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return (self < 0).select(-self, self) - @always_inline("nodebug") - fn __floor__(self) -> Self: - """Performs elementwise floor on the elements of a SIMD vector. - - Returns: - The elementwise floor of this SIMD vector. - """ + fn _floor_ceil_impl[intrinsic: StringLiteral](self) -> Self: + constrained[ + intrinsic == "llvm.floor" or intrinsic == "llvm.ceil", + "unsupported intrinsic", + ]() @parameter if type.is_bool() or type.is_integral(): @@ -903,12 +902,34 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if has_neon() and type == DType.bfloat16: - return self.cast[DType.float32]().__floor__().cast[type]() + return ( + self.cast[DType.float32]() + ._floor_ceil_impl[intrinsic]() + .cast[type]() + ) return llvm_intrinsic[ - "llvm.floor", SIMD[type, size], has_side_effect=False + intrinsic, __type_of(self), has_side_effect=False ](self) + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Performs elementwise floor on the elements of a SIMD vector. + + Returns: + The elementwise floor of this SIMD vector. + """ + return self._floor_ceil_impl["llvm.floor"]() + + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Performs elementwise ceiling on the elements of a SIMD vector. + + Returns: + The elementwise ceiling of this SIMD vector. + """ + return self._floor_ceil_impl["llvm.ceil"]() + # ===-------------------------------------------------------------------===# # In place operations. # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 1af9d40d4b..fd9fdf18ec 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -26,6 +26,20 @@ alias inf = FloatLiteral.infinity alias neg_inf = FloatLiteral.negative_infinity +def test_ceil(): + assert_equal(FloatLiteral.__ceil__(1.5), 2.0) + assert_equal(FloatLiteral.__ceil__(1.4), 2.0) + assert_equal(FloatLiteral.__ceil__(-1.5), -1.0) + assert_equal(FloatLiteral.__ceil__(-3.6), -3.0) + assert_equal(FloatLiteral.__ceil__(3.0), 3.0) + assert_equal(FloatLiteral.__ceil__(0.0), 0.0) + + assert_true(FloatLiteral.__ceil__(nan).is_nan()) + assert_true(FloatLiteral.__ceil__(neg_zero).is_neg_zero()) + assert_equal(FloatLiteral.__ceil__(inf), inf) + assert_equal(FloatLiteral.__ceil__(neg_inf), neg_inf) + + def test_floor(): assert_equal(FloatLiteral.__floor__(1.5), 1.0) assert_equal(FloatLiteral.__floor__(1.6), 1.0) diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 876382204f..5f2c51bc7f 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -49,6 +49,12 @@ def test_pow(): assert_equal(81, Int(3) ** Int(4)) +def test_ceil(): + assert_equal(Int.__ceil__(Int(5)), 5) + assert_equal(Int.__ceil__(Int(0)), 0) + assert_equal(Int.__ceil__(Int(-5)), -5) + + def test_floor(): assert_equal(Int.__floor__(Int(5)), 5) assert_equal(Int.__floor__(Int(0)), 0) @@ -88,6 +94,7 @@ def main(): test_sub() test_div() test_pow() + test_ceil() test_floor() test_floordiv() test_mod() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 844bdcf422..edb4c531b7 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -22,6 +22,12 @@ def test_int(): assert_equal(6 - 1, 5) +def test_ceil(): + assert_equal(IntLiteral.__ceil__(5), 5) + assert_equal(IntLiteral.__ceil__(0), 0) + assert_equal(IntLiteral.__ceil__(-5), -5) + + def test_floor(): assert_equal(IntLiteral.__floor__(5), 5) assert_equal(IntLiteral.__floor__(0), 0) @@ -62,6 +68,7 @@ def test_abs(): def main(): test_int() + test_ceil() test_floor() test_floordiv() test_mod() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index f038b34424..1c9287111b 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -146,6 +146,29 @@ def test_truthy(): test_dtype[DType.bfloat16]() +def test_ceil(): + assert_equal(Float32.__ceil__(Float32(1.5)), 2.0) + assert_equal(Float32.__ceil__(Float32(-1.5)), -1.0) + assert_equal(Float32.__ceil__(Float32(3.0)), 3.0) + + alias F = SIMD[DType.float32, 4] + assert_equal( + F.__ceil__(F(0.0, 1.4, -42.5, -12.6)), F(0.0, 2.0, -42.0, -12.0) + ) + + alias I = SIMD[DType.int32, 4] + var i = I(0, 2, -42, -12) + assert_equal(I.__ceil__(i), i) + + alias U = SIMD[DType.uint32, 4] + var u = U(0, 2, 42, 12) + assert_equal(U.__ceil__(u), u) + + alias B = SIMD[DType.bool, 4] + var b = B(True, False, True, False) + assert_equal(B.__ceil__(b), b) + + def test_floor(): assert_equal(Float32.__floor__(Float32(1.5)), 1.0) assert_equal(Float32.__floor__(Float32(-1.5)), -2.0) @@ -766,6 +789,7 @@ def main(): test_convert_simd_to_string() test_issue_20421() test_truthy() + test_ceil() test_floor() test_floordiv() test_mod() From 29ed07607543f2cac02189a7d55e435736f84fce Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Tue, 30 Apr 2024 16:31:50 -0600 Subject: [PATCH 0350/2019] [External] [stdlib] Allow `String()` to be created from `UInt8` ptr and list too (#38859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Allow `String()` to be created from `UInt8` ptr and list too This is related to https://github.com/modularml/mojo/issues/2317. It allows a graceful and slow migration to UInt8 as the primary byte type. This allows everyone to give whatever (Int8 or UInt8) to the String struct and it just works™ When everything uses Uint8, we can remove the old methods that work with Int8, and the migration will be complete. Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2448 MODULAR_ORIG_COMMIT_REV_ID: d12649d6dfa178eb756d56df563ad155b5240093 --- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/string.mojo | 79 +++++++++++++++++++++++++++++++- stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 131dc48556..1066903b5d 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -189,7 +189,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() var buf = external_call[ - "KGEN_CompilerRT_IO_FileRead", UnsafePointer[Int8] + "KGEN_CompilerRT_IO_FileRead", UnsafePointer[UInt8] ]( self.handle, UnsafePointer.address_of(size_copy), diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 779d027644..3ab0fd0b38 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -456,6 +456,7 @@ struct String( # Initializers # ===------------------------------------------------------------------===# + # TODO: Remove this method when #2317 is done @always_inline fn __init__(inout self, owned impl: Self._buffer_type): """Construct a string from a buffer of bytes. @@ -470,6 +471,11 @@ struct String( var hi = String(buf) ``` + Note that you should use the constructor from `List[UInt8]` instead + as we are now storing the bytes as UInt8. + + See https://github.com/modularml/mojo/issues/2317 for more information. + Args: impl: The buffer. """ @@ -479,6 +485,34 @@ struct String( ) self._buffer = impl^ + @always_inline + fn __init__(inout self, owned impl: List[UInt8]): + """Construct a string from a buffer of bytes. + + The buffer must be terminated with a null byte: + + ```mojo + var buf = List[UInt8]() + buf.append(ord('H')) + buf.append(ord('i')) + buf.append(0) + var hi = String(buf) + ``` + + Args: + impl: The buffer. + """ + debug_assert( + impl[-1] == 0, + "expected last element of String buffer to be null terminator", + ) + # we store the length and capacity beforehand as `steal_data()` will invalidated `impl` + var length = len(impl) + var capacity = impl.capacity + self._buffer = List[Int8]( + impl.steal_data().bitcast[Int8](), size=length, capacity=capacity + ) + @always_inline fn __init__(inout self): """Construct an uninitialized string.""" @@ -520,6 +554,7 @@ struct String( self = str(value) + # TODO: Remove this method when #2317 is done @always_inline fn __init__(inout self, ptr: UnsafePointer[Int8], len: Int): """Creates a string from the buffer. Note that the string now owns @@ -527,6 +562,11 @@ struct String( The buffer must be terminated with a null byte. + Note that you should use the constructor from `UnsafePointer[UInt8]` instead + as we are now storing the bytes as UInt8. + + See https://github.com/modularml/mojo/issues/2317 for more information. + Args: ptr: The pointer to the buffer. len: The length of the buffer, including the null terminator. @@ -535,6 +575,23 @@ struct String( # larger than len self = Self(Self._buffer_type(ptr, size=len, capacity=len)) + @always_inline + fn __init__(inout self, ptr: UnsafePointer[UInt8], len: Int): + """Creates a string from the buffer. Note that the string now owns + the buffer. + + The buffer must be terminated with a null byte. + + Args: + ptr: The pointer to the buffer. + len: The length of the buffer, including the null terminator. + """ + # we don't know the capacity of ptr, but we'll assume it's the same or + # larger than len + self = Self( + Self._buffer_type(ptr.bitcast[Int8](), size=len, capacity=len) + ) + @always_inline fn __init__(inout self, ptr: LegacyPointer[Int8], len: Int): """Creates a string from the buffer. Note that the string now owns @@ -794,7 +851,11 @@ struct String( var total_len = self_len + other_len self._buffer.resize(total_len + 1, 0) # Copy the data alongside the terminator. - memcpy(self._as_ptr() + self_len, other._as_ptr(), other_len + 1) + memcpy( + self._as_uint8_ptr() + self_len, + other._as_uint8_ptr(), + other_len + 1, + ) # ===------------------------------------------------------------------=== # # Methods @@ -927,14 +988,30 @@ struct String( """ pass + # TODO: Remove this method when #2317 is done fn _as_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. + Note that you should use `_as_uint8_ptr()` if you need to access the + pointer as we are now storing the bytes as UInt8. + + See https://github.com/modularml/mojo/issues/2317 for more information. + Returns: The pointer to the underlying memory. """ return rebind[DTypePointer[DType.int8]](self._buffer.data) + fn _as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + """Retrieves a pointer to the underlying memory. + + Returns: + The pointer to the underlying memory. + """ + return rebind[DTypePointer[DType.uint8]]( + self._buffer.data.bitcast[UInt8]() + ) + fn as_bytes(self) -> List[Int8]: """Retrieves the underlying byte sequence encoding the characters in this string. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index f44446181f..9c20236cc0 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -566,7 +566,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( A string representation of the Dict. """ var minimum_capacity = self._minimum_size_of_string_representation() - var result = String(List[Int8](capacity=minimum_capacity)) + var result = String(List[UInt8](capacity=minimum_capacity)) result += "{" var i = 0 diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 38ac558172..9cb9a0d2d0 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -658,7 +658,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): + len(self) * 3 # str(x) and ", " - 2 # remove the last ", " ) - var result = String(List[Int8](capacity=minimum_capacity)) + var result = String(List[UInt8](capacity=minimum_capacity)) result += "[" for i in range(len(self)): result += str(self[i]) From fc5615c95e2ff521ab8f80c88459856c6036c373 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 30 Apr 2024 16:24:42 -0700 Subject: [PATCH 0351/2019] [mojo-stdlib] Fix aarch64 config (#38980) `platform.processor()` on Apple Silicon machines returns `arm`. This handles both Linux and macOS `aarch64` return values. Without this test_aarch64_target.mojo was not running. MODULAR_ORIG_COMMIT_REV_ID: 4c82ada0c567ff7b4a9187e9a38ca927054043cf --- stdlib/test/sys/lit.local.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/sys/lit.local.cfg b/stdlib/test/sys/lit.local.cfg index e5e276abf3..63129a7220 100644 --- a/stdlib/test/sys/lit.local.cfg +++ b/stdlib/test/sys/lit.local.cfg @@ -3,7 +3,7 @@ from pathlib import Path # Configuration file for the 'lit' test runner. -if platform.processor() == "aarch64": +if platform.machine() in ("aarch64", "arm64"): config.available_features.add("aarch64") if ( From 718e6ebd73292582b98051ce3aacdbe314514fec Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Apr 2024 19:54:04 -0600 Subject: [PATCH 0352/2019] [stdlib] Make `min` and `max` built-ins (#38901) Move the recently introduced `min` and `max` functions from `utils/_math.mojo` to be built-ins as they are in Python. Note the Python `min` and `max` functions work on any iterable, but ours aren't quite that powerful yet. We only support `min` and `max` on `Int` and `SIMD` types right now. Eventually, it will improve as we get further along with iterables in the library. MODULAR_ORIG_COMMIT_REV_ID: 545aed9299d0ff0093562d06ff600f427bb5b4e5 --- examples/notebooks/RayTracing.ipynb | 3 --- stdlib/src/{utils/_math.mojo => builtin/math.mojo} | 6 ------ stdlib/test/{utils => builtin}/test_math.mojo | 1 - 3 files changed, 10 deletions(-) rename stdlib/src/{utils/_math.mojo => builtin/math.mojo} (96%) rename stdlib/test/{utils => builtin}/test_math.mojo (97%) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 66afe90ff9..934e157b63 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -708,9 +708,6 @@ } ], "source": [ - "from math import max\n", - "\n", - "\n", "fn scene_intersect(\n", " orig: Vec3f,\n", " dir: Vec3f,\n", diff --git a/stdlib/src/utils/_math.mojo b/stdlib/src/builtin/math.mojo similarity index 96% rename from stdlib/src/utils/_math.mojo rename to stdlib/src/builtin/math.mojo index d638753f34..cdbc4783ab 100644 --- a/stdlib/src/utils/_math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -14,12 +14,6 @@ source parts of the standard library since the `math` package is currently closed source and cannot be depended on in the open source parts of the standard library. - -You can import these APIs from the `utils` package. For example: - -```mojo -from utils._math import min, max -``` """ # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/utils/test_math.mojo b/stdlib/test/builtin/test_math.mojo similarity index 97% rename from stdlib/test/utils/test_math.mojo rename to stdlib/test/builtin/test_math.mojo index 9df461d78d..8b801a96c3 100644 --- a/stdlib/test/utils/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -12,7 +12,6 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils._math import min, max from testing import assert_equal From f2695a532711a07c00e3b69da832303ddcca40be Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Apr 2024 20:15:33 -0600 Subject: [PATCH 0353/2019] [stdlib] Use built-in `max` and `min` functions (#38902) Now that the `max` and `min` functions are available in the open source parts of the standard library, apply it and remove all the file-local `max`-equivalent definitions. Similar for `min`. A follow-up will do the same for `abs`. MODULAR_ORIG_COMMIT_REV_ID: 2a352f4d9b6ff18514036bdec1bfb0a4a453aa4c --- stdlib/src/builtin/hash.mojo | 7 +------ stdlib/src/builtin/io.mojo | 6 +----- stdlib/src/builtin/object.mojo | 14 ++------------ stdlib/src/builtin/range.mojo | 9 ++------- stdlib/src/collections/list.mojo | 14 ++------------ stdlib/src/utils/_serialize.mojo | 7 +------ stdlib/src/utils/stringref.mojo | 14 ++------------ stdlib/src/utils/variant.mojo | 7 +------ 8 files changed, 12 insertions(+), 66 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 83e33c3d4c..f4f9eed4d3 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -40,11 +40,6 @@ fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: return (numerator + denominator - 1)._positive_div(denominator) -@always_inline -fn _max(a: Int, b: Int) -> Int: - return a if a > b else b - - # ===----------------------------------------------------------------------=== # # Implementation # ===----------------------------------------------------------------------=== # @@ -161,7 +156,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: # nondeterminism (read) or memory corruption (write) # TODO(#31160): use math.lcm # Technically this is LCM, but alignments should always be multiples of 2. - alias alignment = _max( + alias alignment = max( alignof[SIMD[type, size]](), alignof[SIMD[DType.uint8, int8_size]]() ) var bytes = stack_allocation[int8_size, DType.uint8, alignment=alignment]() diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 8a8b180418..4239cc3565 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -307,10 +307,6 @@ fn _put(x: String): _put(x._strref_dangerous()) -fn _min(x: Int, y: Int) -> Int: - return x if x < y else y - - @no_inline fn _put(x: StringRef): # Avoid printing "(null)" for an empty/default constructed `String` @@ -337,7 +333,7 @@ fn _put(x: StringRef): # The string is large, then we need to chunk it. var p = x.data while str_len: - var ll = _min(str_len, MAX_STR_LEN) + var ll = min(str_len, MAX_STR_LEN) _printf("%.*s", ll, p) str_len -= ll p += ll diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f51ad06484..f9e7c3e82c 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -28,16 +28,6 @@ from utils import StringRef, unroll from .io import _printf, _put -# ===----------------------------------------------------------------------=== # -# Utilities -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _min(a: Int, b: Int) -> Int: - return a if a < b else b - - # ===----------------------------------------------------------------------=== # # _ObjectImpl # ===----------------------------------------------------------------------=== # @@ -69,7 +59,7 @@ struct _ImmutableString: @always_inline fn string_compare(self, rhs: _ImmutableString) -> Int: - var res = memcmp(self.data, rhs.data, _min(self.length, rhs.length)) + var res = memcmp(self.data, rhs.data, min(self.length, rhs.length)) if res != 0: return -1 if res < 0 else 1 if self.length == rhs.length: @@ -1017,7 +1007,7 @@ struct object(IntableRaising, Boolable, Stringable): fn _list_compare(self, rhs: object) raises -> Int: var llen = self._value.get_list_length() var rlen = self._value.get_list_length() - var cmp_len = _min(llen, rlen) + var cmp_len = min(llen, rlen) for i in range(cmp_len): var lelt: object = self._value.get_list_element(i) var relt: object = rhs._value.get_list_element(i) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 2d2e9dc032..2b6a628d0c 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -57,11 +57,6 @@ fn _sign(x: Int) -> Int: return 0 -@always_inline -fn _max(a: Int, b: Int) -> Int: - return a if a > b else b - - # ===----------------------------------------------------------------------=== # # Range # ===----------------------------------------------------------------------=== # @@ -74,7 +69,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange): @always_inline("nodebug") fn __init__(inout self, end: Int): - self.curr = _max(0, end) + self.curr = max(0, end) self.end = end @always_inline("nodebug") @@ -119,7 +114,7 @@ struct _SequentialRange(Sized, ReversibleRange): @always_inline("nodebug") fn __len__(self) -> Int: # FIXME(#38392): - # return _max(0, self.end - self.start) + # return max(0, self.end - self.start) return self.end - self.start if self.start < self.end else 0 @always_inline("nodebug") diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9cb9a0d2d0..b63af8ba3e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -25,16 +25,6 @@ from memory import UnsafePointer, Reference from memory.unsafe_pointer import move_pointee, move_from_pointee from .optional import Optional -# ===----------------------------------------------------------------------===# -# Utilties -# ===----------------------------------------------------------------------===# - - -@always_inline -fn _max(a: Int, b: Int) -> Int: - return a if a > b else b - - # ===----------------------------------------------------------------------===# # List # ===----------------------------------------------------------------------===# @@ -220,7 +210,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): value: The value to append. """ if self.size >= self.capacity: - self._realloc(_max(1, self.capacity * 2)) + self._realloc(max(1, self.capacity * 2)) initialize_pointee_move(self.data + self.size, value^) self.size += 1 @@ -237,7 +227,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var normalized_idx = i if i < 0: - normalized_idx = _max(0, len(self) + i) + normalized_idx = max(0, len(self) + i) var earlier_idx = len(self) var later_idx = len(self) - 1 diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 8da3ceb2c8..ac632a6e29 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -22,11 +22,6 @@ alias _kCompactMaxElemsToPrint = 7 alias _kCompactElemPerSide = _kCompactMaxElemsToPrint // 2 -@always_inline -fn _max(a: Int, b: Int) -> Int: - return a if a > b else b - - fn _serialize_elements_compact[ serialize_fn: fn[T: Stringable] (elem: T) capturing -> None, ](ptr: DTypePointer, len: Int): @@ -107,7 +102,7 @@ fn _serialize[ # product of all dims other than last two. var num_matrices = 1 - for i in range(_max(rank - 2, 0)): + for i in range(max(rank - 2, 0)): num_matrices *= shape[i] var matrix_idx = 0 diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 86cb86f944..46a6e5e6c9 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -35,16 +35,6 @@ fn _align_down(value: Int, alignment: Int) -> Int: return value._positive_div(alignment) * alignment -@always_inline -fn _min(a: Int, b: Int) -> Int: - return a if a < b else b - - -@always_inline -fn _max(a: Int, b: Int) -> Int: - return a if a > b else b - - # ===----------------------------------------------------------------------===# # StringRef # ===----------------------------------------------------------------------===# @@ -343,12 +333,12 @@ struct StringRef( # Avoid out of bounds earlier than the start # len = 5, start = -3, then abs_start == 2, i.e. a partial string # len = 5, start = -10, then abs_start == 0, i.e. the full string - abs_start = _max(self_len + start, 0) + abs_start = max(self_len + start, 0) else: # Avoid out of bounds past the end # len = 5, start = 2, then abs_start == 2, i.e. a partial string # len = 5, start = 8, then abs_start == 5, i.e. an empty string - abs_start = _min(start, self_len) + abs_start = min(start, self_len) debug_assert( abs_start >= 0, "strref absolute start must be non-negative" diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 451098a887..2506119adc 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -60,11 +60,6 @@ fn _align_up(value: Int, alignment: Int) -> Int: return div_ceil * alignment -@always_inline -fn _max(a: Int, b: Int) -> Int: - return a if a > b else b - - # ===----------------------------------------------------------------------=== # # Variant # ===----------------------------------------------------------------------=== # @@ -78,7 +73,7 @@ struct _UnionSize[*Ts: CollectionElement](): @parameter fn each[i: Int](): - size = _max(size, _align_up(sizeof[Ts[i]](), alignof[Ts[i]]())) + size = max(size, _align_up(sizeof[Ts[i]](), alignof[Ts[i]]())) unroll[each, len(VariadicList(Ts))]() return _align_up(size, alignof[Int]()) From 91fefeccccb8bad0b209416f259006548753e08c Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 30 Apr 2024 19:16:04 -0700 Subject: [PATCH 0354/2019] [docs] Update docs about telemetry for 24.3 (#38356) L0 telemetry is now required and cannot be disabled. As such, update docs to document the `telemetry.level` config instead of `telemetry.enabled` which is misleading because if set `false` it will still collect the L0 data. RE: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 3b5a1b390c74a7cb0b4199d0cb8c6e0b7ea90be6 --- docs/faq.md | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 7b8ea4e505..c515ca8c7a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -346,27 +346,37 @@ containers to enable remote development in Mojo. ### Does the Mojo SDK collect telemetry? -Yes, in combination with the Modular CLI tool, the Mojo SDK collects some basic -system information and crash reports that enable us to identify, analyze, and -prioritize Mojo issues. +Yes, the Mojo SDK collects some basic system information, basic +compiler/runtime events, and crash reports that enable us to identify, analyze, +and prioritize Mojo issues. -Mojo is still in its early days, and this telemetry is crucial to help us -quickly identify problems and improve Mojo. Without this telemetry, we would -have to rely on user-submitted bug reports, and in our decades of building -developer products, we know that most people don’t bother. Plus, a lot of -product issues are not easily identified by users or quantifiable with -individual bug reports. The telemetry provides us the insights we need to build -Mojo into a premier developer product. +This telemetry is crucial to help us quickly identify problems and improve our +products. Without this telemetry, we would have to rely on user-submitted bug +reports, and in our decades of experience building developer products, we know +that most people don’t do that. The telemetry provides us the insights we need +to build better products for you. -Of course, if you don't want to share this information with us, you can easily -opt-out of all telemetry, using the [`modular` CLI](/cli). To stop sharing -system information, run this: +You can opt-out of the crash report and compiler/runtime telemetry, but +package install/update/uninstall events cannot be +disabled (see the [MAX SDK terms](https://www.modular.com/legal/max)). -`modular config-set telemetry.enabled=false` +To disable crash reports, use this command: -To stop sharing crash reports, run this: +```sh +modular config-set crash_reporting.enabled=false +``` -`modular config-set crash_reporting.enabled=false` +To reduce other telemetry to only the required telemetry events, use this +command: + +```sh +modular config-set telemetry.level=0 +``` + +There are 3 telemetry levels: `0` currently records nothing (unless you're also +using MAX, which records hardware information and session durations); `1` +records high-level events such as when the compiler is invoked; and `2` records +more detail such as the time spend compiling. ## Versioning & compatibility From 566566ba625be5b7b42b562d4eb3c5d1fcbfb87b Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:28:21 -0600 Subject: [PATCH 0355/2019] [External] [stdlib] Enhance Handling of Infinity and NaN in `assert_almost_equal` (#38991) [External] [stdlib] Enhance Handling of Infinity and NaN in `assert_almost_equal` This PR enhances the `assert_almost_equal` function to correctly handle cases involving infinity and NaN. According to `test_assert_almost_equal` added to `/test/testing/test_assertion.mojo`, the current implementation of `assert_almost_equal` results in errors in the following cases: ```mojo alias float_type = DType.float32 alias _inf = inf[float_type]() alias _nan = nan[float_type]() ... _should_succeed( SIMD[float_type, 2](-_inf, _inf), SIMD[float_type, 2](-_inf, _inf) ) ... _should_fail( SIMD[float_type, 2](-_inf, 0.0), SIMD[float_type, 2](_inf, 0.0), rtol=0.1, ) _should_fail( SIMD[float_type, 2](_inf, 0.0), SIMD[float_type, 2](0.0, 0.0), rtol=0.1, ) ... _should_fail( SIMD[float_type, 2](_nan, 0.0), SIMD[float_type, 2](0.0, 0.0), equal_nan=True, ) ``` This PR also: - Eliminates the use of `and` and `or` in the `_isclose` function due to the issue outlined in #2374. - Explicitly reduces boolean vectors to boolean scalar values instead of counting on implicit conversions for clarity. - Avoids arithmetic operations in `_isclose` and `assert_almost_equal` when the type is boolean, as these operations are not supported in this case. - Clarifies the behavior of `assert_almost_equal` in the docstring, highlighting differences from similar functions such as `numpy.testing.assert_allclose`. - Adds the `inf` function to `utils/_numerics` along with corresponding tests in `test/utils/test_numerics.mojo`. ORIGINAL_AUTHOR=Leandro Augusto Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2375 Co-authored-by: Leandro Augusto Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> Closes modularml/mojo#2375 MODULAR_ORIG_COMMIT_REV_ID: 2e8b24461dd3279bc841877cc0167acaa104f273 --- stdlib/src/testing/testing.mojo | 59 ++++++++---- stdlib/src/utils/_numerics.mojo | 54 +++++++++++ stdlib/test/testing/test_assertion.mojo | 114 ++++++++++++++++++++++-- stdlib/test/utils/test_numerics.mojo | 21 ++++- 4 files changed, 224 insertions(+), 24 deletions(-) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index a7b25e7c10..b1922ffb10 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,7 +19,7 @@ from testing import assert_true ``` """ from collections import Optional -from utils._numerics import isnan +from utils._numerics import isfinite, isnan from builtin._location import __call_location, _SourceLocation # ===----------------------------------------------------------------------=== # @@ -41,21 +41,29 @@ fn _isclose( rtol: Scalar[a.type], equal_nan: Bool, ) -> SIMD[DType.bool, a.size]: + constrained[ + a.type.is_bool() or a.type.is_integral() or a.type.is_floating_point(), + "input type must be boolean, integral, or floating-point", + ]() + @parameter if a.type.is_bool() or a.type.is_integral(): return a == b + else: + var both_nan = isnan(a) & isnan(b) + if equal_nan and both_nan.reduce_and(): + return True + + var res = (a == b) + var atol_vec = SIMD[a.type, a.size](atol) + var rtol_vec = SIMD[a.type, a.size](rtol) + res |= ( + isfinite(a) + & isfinite(b) + & (_abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b))))) + ) - if equal_nan and isnan(a) and isnan(b): - return True - - var atol_vec = SIMD[a.type, a.size](atol) - var rtol_vec = SIMD[a.type, a.size](rtol) - var res = _abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b)))) - - if not equal_nan: - return res - - return res.select(res, isnan(a) and isnan(b)) + return res | both_nan if equal_nan else res # ===----------------------------------------------------------------------=== # @@ -254,6 +262,14 @@ fn assert_almost_equal[ """Asserts that the input values are equal up to a tolerance. If it is not then an Error is raised. + When the type is boolean or integral, then equality is checked. When the + type is floating-point, then this checks if the two input values are + numerically the close using the $abs(lhs - rhs) <= max(rtol * max(abs(lhs), + abs(rhs)), atol)$ formula. + + Constraints: + The type must be boolean, integral, or floating-point. + Parameters: type: The dtype of the left- and right-hand-side SIMD vectors. size: The width of the left- and right-hand-side SIMD vectors. @@ -262,22 +278,31 @@ fn assert_almost_equal[ lhs: The lhs of the equality. rhs: The rhs of the equality. msg: The message to print. - atol: The _absolute tolerance. + atol: The absolute tolerance. rtol: The relative tolerance. equal_nan: Whether to treat nans as equal. Raises: An Error with the provided message if assert fails and `None` otherwise. """ + constrained[ + type.is_bool() or type.is_integral() or type.is_floating_point(), + "type must be boolean, integral, or floating-point", + ]() + var almost_equal = _isclose( lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan ) - if not almost_equal: - var err = str(lhs) + " is not close to " + str( - rhs - ) + " with a diff of " + _abs(lhs - rhs) + if not almost_equal.reduce_and(): + var err = "AssertionError: " + str(lhs) + " is not close to " + str(rhs) + + @parameter + if type.is_integral() or type.is_floating_point(): + err += " with a diff of " + str(_abs(lhs - rhs)) + if msg: err += " (" + msg + ")" + raise _assert_error(err, __call_location()) diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index db9dd0b752..27991fafab 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -647,6 +647,60 @@ fn isnan[ ](val.value, (signaling_nan_test | quiet_nan_test).value) +# ===----------------------------------------------------------------------===# +# inf +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn inf[type: DType]() -> Scalar[type]: + """Gets a +inf value for the given dtype. + + Constraints: + Can only be used for FP dtypes. + + Parameters: + type: The value dtype. + + Returns: + The +inf value of the given dtype. + """ + + @parameter + if type == DType.float16: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type == DType.bfloat16: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type == DType.float32: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type == DType.float64: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + else: + constrained[False, "+inf only support on floating point types"]() + + return 0 + + # ===----------------------------------------------------------------------===# # isinf # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 5a50096ba1..e9e8737c43 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -13,13 +13,14 @@ # RUN: %mojo -debug-level full %s from testing import ( + assert_almost_equal, assert_equal, + assert_false, assert_not_equal, assert_raises, assert_true, - assert_false, - assert_almost_equal, ) +from utils._numerics import inf, nan @value @@ -61,22 +62,122 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:62:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:63:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:67:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:68:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:72:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:73:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:77:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:78:25: AssertionError:" in str(e)) + + +def test_assert_almost_equal(): + alias float_type = DType.float32 + alias _inf = inf[float_type]() + alias _nan = nan[float_type]() + + @parameter + def _should_succeed[ + type: DType, size: Int + ]( + lhs: SIMD[type, size], + rhs: SIMD[type, size], + *, + atol: Scalar[type] = 0, + rtol: Scalar[type] = 0, + equal_nan: Bool = False, + ): + var msg = "`test_assert_almost_equal` should have succeeded" + assert_almost_equal( + lhs, rhs, msg=msg, atol=atol, rtol=rtol, equal_nan=equal_nan + ) + + _should_succeed[DType.bool, 1](True, True) + _should_succeed(SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, 1)) + _should_succeed( + SIMD[float_type, 2](-_inf, _inf), SIMD[float_type, 2](-_inf, _inf) + ) + _should_succeed( + SIMD[float_type, 2](-_nan, _nan), + SIMD[float_type, 2](-_nan, _nan), + equal_nan=True, + ) + _should_succeed( + SIMD[float_type, 2](1.0, -1.1), + SIMD[float_type, 2](1.1, -1.0), + atol=0.11, + ) + _should_succeed( + SIMD[float_type, 2](1.0, -1.1), + SIMD[float_type, 2](1.1, -1.0), + rtol=0.10, + ) + + @parameter + def _should_fail[ + type: DType, size: Int + ]( + lhs: SIMD[type, size], + rhs: SIMD[type, size], + *, + atol: Scalar[type] = 0, + rtol: Scalar[type] = 0, + equal_nan: Bool = False, + ): + var msg = "`test_assert_almost_equal` should have failed" + with assert_raises(contains=msg): + assert_almost_equal( + lhs, rhs, msg=msg, atol=atol, rtol=rtol, equal_nan=equal_nan + ) + + _should_fail[DType.bool, 1](True, False) + _should_fail( + SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, -1), atol=5.0 + ) + _should_fail( + SIMD[float_type, 2](-_inf, 0.0), + SIMD[float_type, 2](_inf, 0.0), + rtol=0.1, + ) + _should_fail( + SIMD[float_type, 2](_inf, 0.0), + SIMD[float_type, 2](0.0, 0.0), + rtol=0.1, + ) + _should_fail( + SIMD[float_type, 2](_nan, 0.0), + SIMD[float_type, 2](_nan, 0.0), + equal_nan=False, + ) + _should_fail( + SIMD[float_type, 2](_nan, 0.0), + SIMD[float_type, 2](0.0, 0.0), + equal_nan=False, + ) + _should_fail( + SIMD[float_type, 2](_nan, 0.0), + SIMD[float_type, 2](0.0, 0.0), + equal_nan=True, + ) + _should_fail( + SIMD[float_type, 2](1.0, 0.0), + SIMD[float_type, 2](1.1, 0.0), + atol=0.05, + ) + _should_fail( + SIMD[float_type, 2](-1.0, 0.0), + SIMD[float_type, 2](-1.1, 0.0), + rtol=0.05, + ) def main(): @@ -84,3 +185,4 @@ def main(): test_assert_not_equal_is_generic() test_assert_equal_with_simd() test_assert_messages() + test_assert_almost_equal() diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index d39d52dea8..f5dc97f9d6 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -12,8 +12,9 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils._numerics import FPUtils +from sys.info import has_neon from testing import assert_equal, assert_true, assert_false +from utils._numerics import FPUtils, inf, isinf alias FPU64 = FPUtils[DType.float64] @@ -45,5 +46,23 @@ fn test_numerics() raises: assert_equal(FPU64.get_mantissa(FPU64.pack(True, 6, 12)), 12) +fn test_inf() raises: + @parameter + fn _test_inf[type: DType]() raises: + var val = inf[type]() + var msg = "`test_inf` failed for `type == " + str(type) + "`" + assert_true((val > 0.0) & isinf(val), msg=msg) + + @parameter + if not has_neon(): + # "bf16 is not supported for ARM architectures" + _test_inf[DType.bfloat16]() + + _test_inf[DType.float16]() + _test_inf[DType.float32]() + _test_inf[DType.float64]() + + def main(): test_numerics() + test_inf() From 55210eba1a7396a024636858aac6bb0f39fa5cf7 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:30:58 -0600 Subject: [PATCH 0356/2019] [External] [stdlib] SIMD.shuffle with StaticIntTuple mask (#38832) [External] [stdlib] SIMD.shuffle with StaticIntTuple mask Implement #2293. New overloads for `SIMD.shuffle()` like this: ``` fn shuffle[mask: StaticIntTuple[size]](self) -> Self: ``` additionally: - added tests for `SIMD.join()` and `SIMD.shuffle()` - simplified `join` implementation and always use shuffle rather than 2 inserts when mask size > 32 - cleaned up doc strings about shuffle output length - minor refactor of original `_shuffle_list` to use `output_size` parameter rather than calculate `len(mask)` many times As I mentioned in the issue, this can be nicer when inferred parameters are implemented but I guess that is true of most things. ORIGINAL_AUTHOR=Michael Kowalski <1331470+mikowals@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2315 Co-authored-by: Michael Kowalski <1331470+mikowals@users.noreply.github.com> Closes modularml/mojo#2315 MODULAR_ORIG_COMMIT_REV_ID: db6215999badc9da09f81700ee9b070db8e93a5b --- stdlib/src/builtin/simd.mojo | 183 ++++++++++++++--------------- stdlib/test/builtin/test_simd.mojo | 46 ++++++++ 2 files changed, 133 insertions(+), 96 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 4265c3c7f2..740bd809a2 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1500,7 +1500,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( other: The other vector to shuffle with. Returns: - A new vector of length `len` where the value at position `i` is + A new vector with the same length as the mask where the value at position `i` is `(self+other)[permutation[i]]`. """ @@ -1511,13 +1511,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter fn _convert_variadic_to_pop_array[ *mask: Int - ]() -> __mlir_type[ - `!pop.array<`, variadic_len[mask]().value, `, `, Int, `>` - ]: - alias size = variadic_len[mask]() + ]() -> __mlir_type[`!pop.array<`, output_size.value, `, `, Int, `>`]: var array = __mlir_op.`kgen.undef`[ _type = __mlir_type[ - `!pop.array<`, variadic_len[mask]().value, `, `, Int, `>` + `!pop.array<`, output_size.value, `, `, Int, `>` ] ]() @@ -1534,7 +1531,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) __mlir_op.`pop.store`(val, ptr) - unroll[fill, size]() + unroll[fill, output_size]() return array alias length = variadic_len[mask]() @@ -1549,6 +1546,42 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ], ](self.value, other.value) + @always_inline("nodebug") + fn _shuffle_list[ + output_size: Int, mask: StaticIntTuple[output_size] + ](self, other: Self) -> SIMD[type, output_size]: + """Shuffles (also called blend) the values of the current vector with + the `other` value using the specified mask (permutation). The mask values + must be within `2*len(self)`. + + Parameters: + output_size: The size of the output vector. + mask: The permutation to use in the shuffle. + + Args: + other: The other vector to shuffle with. + + Returns: + A new vector with the same length as the mask where the value at position `i` is + `(self+other)[permutation[i]]`. + """ + + @parameter + fn _check[i: Int](): + constrained[ + 0 <= mask[i] < 2 * size, + "invalid index in the shuffle operation", + ]() + + unroll[_check, output_size]() + + return __mlir_op.`pop.simd.shuffle`[ + mask = mask.data.array, + _type = __mlir_type[ + `!pop.simd<`, output_size.value, `, `, type.value, `>` + ], + ](self.value, other.value) + @always_inline("nodebug") fn shuffle[*mask: Int](self) -> Self: """Shuffles (also called blend) the values of the current vector with @@ -1559,7 +1592,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( mask: The permutation to use in the shuffle. Returns: - A new vector of length `len` where the value at position `i` is + A new vector with the same length as the mask where the value at position `i` is `(self)[permutation[i]]`. """ return self._shuffle_list[mask](self) @@ -1577,11 +1610,44 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( other: The other vector to shuffle with. Returns: - A new vector of length `len` where the value at position `i` is + A new vector with the same length as the mask where the value at position `i` is `(self+other)[permutation[i]]`. """ return self._shuffle_list[mask](other) + @always_inline("nodebug") + fn shuffle[mask: StaticIntTuple[size]](self) -> Self: + """Shuffles (also called blend) the values of the current vector with + the `other` value using the specified mask (permutation). The mask values + must be within `2*len(self)`. + + Parameters: + mask: The permutation to use in the shuffle. + + Returns: + A new vector with the same length as the mask where the value at position `i` is + `(self)[permutation[i]]`. + """ + return self._shuffle_list[size, mask](self) + + @always_inline("nodebug") + fn shuffle[mask: StaticIntTuple[size]](self, other: Self) -> Self: + """Shuffles (also called blend) the values of the current vector with + the `other` value using the specified mask (permutation). The mask values + must be within `2*len(self)`. + + Parameters: + mask: The permutation to use in the shuffle. + + Args: + other: The other vector to shuffle with. + + Returns: + A new vector with the same length as the mask where the value at position `i` is + `(self+other)[permutation[i]]`. + """ + return self._shuffle_list[size, mask](other) + # ===-------------------------------------------------------------------===# # Indexing operations # ===-------------------------------------------------------------------===# @@ -1736,94 +1802,19 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( A new vector `self_0, self_1, ..., self_n, other_0, ..., other_n`. """ - # Common cases will use shuffle which the compiler understands well. + @always_inline @parameter - if size == 1: - return self._shuffle_list[ - 0, - 1, - output_size = 2 * size, - ](other) - elif size == 2: - return self._shuffle_list[ - 0, - 1, - 2, - 3, - output_size = 2 * size, - ](other) - elif size == 4: - return self._shuffle_list[ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - output_size = 2 * size, - ](other) - elif size == 8: - return self._shuffle_list[ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - output_size = 2 * size, - ](other) - elif size == 16: - return self._shuffle_list[ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - output_size = 2 * size, - ](other) - - var res = SIMD[type, 2 * size]() - res = res.insert(self) - return res.insert[offset=size](other) + fn build_indices() -> StaticIntTuple[2 * size]: + var indices = StaticIntTuple[2 * size]() + + @parameter + fn _fill[i: Int](): + indices[i] = i + + unroll[_fill, 2 * size]() + return indices + + return self._shuffle_list[2 * size, build_indices()](other) @always_inline("nodebug") fn interleave(self, other: Self) -> SIMD[type, 2 * size]: diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 1c9287111b..6f7c3ab77a 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -429,6 +429,41 @@ def test_shift(): ) +def test_shuffle(): + alias dtype = DType.int32 + alias width = 4 + + vec = SIMD[dtype, width](100, 101, 102, 103) + + assert_equal( + vec.shuffle[3, 2, 1, 0](), SIMD[dtype, width](103, 102, 101, 100) + ) + assert_equal( + vec.shuffle[0, 2, 4, 6](vec), SIMD[dtype, width](100, 102, 100, 102) + ) + + assert_equal( + vec._shuffle_list[7, 6, 5, 4, 3, 2, 1, 0, output_size = 2 * width](vec), + SIMD[dtype, 2 * width](103, 102, 101, 100, 103, 102, 101, 100), + ) + + assert_equal( + vec.shuffle[StaticIntTuple[width](3, 2, 1, 0)](), + SIMD[dtype, width](103, 102, 101, 100), + ) + assert_equal( + vec.shuffle[StaticIntTuple[width](0, 2, 4, 6)](vec), + SIMD[dtype, width](100, 102, 100, 102), + ) + + assert_equal( + vec._shuffle_list[ + 2 * width, StaticIntTuple[2 * width](7, 6, 5, 4, 3, 2, 1, 0) + ](vec), + SIMD[dtype, 2 * width](103, 102, 101, 100, 103, 102, 101, 100), + ) + + def test_insert(): assert_equal(Int32(3).insert(Int32(4)), 4) @@ -459,6 +494,15 @@ def test_insert(): ) +def test_join(): + vec = SIMD[DType.int32, 4](100, 101, 102, 103) + + assert_equal( + vec.join(vec), + SIMD[DType.int32, 8](100, 101, 102, 103, 100, 101, 102, 103), + ) + + def test_interleave(): assert_equal(Int32(0).interleave(Int32(1)), SIMD[DType.index, 2](0, 1)) @@ -795,7 +839,9 @@ def main(): test_mod() test_rotate() test_shift() + test_shuffle() test_insert() + test_join() test_interleave() test_deinterleave() test_address() From 8e137ee77866379fc22fe330c2d7ea1f012c5365 Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Tue, 30 Apr 2024 20:44:48 -0600 Subject: [PATCH 0357/2019] [External] [stdlib] Add `repr()` function and `Representable` trait (#38803) [External] [stdlib] Add `repr()` function and `Representable` trait This PR is a follow up on the discussion there: https://github.com/modularml/mojo/issues/2207 There is no `RepresentableRaising` trait yet, this can come later on. We assume here that `42` is the Mojo representation of `Int(42)` because this is how it materializes. Same we assume that `"hello"` is the Mojo string representation of `String("hello")` because StringLiteral materializes into String. We could have used `Int(42)` and `String("hello")` as Mojo representations but that seems very cumbersome and not aligned with the evolution of the language. The discussion is still open for other types. The `__repr__` implementation for String is very simplistic and I believe it's enough as a start, we can push the complexity in other separate PRs (handling `\` and so on). I will open an issue when this PR is merged requesting contributor help to improve it. I will create another issue after this PR is merged to track the conformance of (all?) library types to the `Representable` trait. Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2361 MODULAR_ORIG_COMMIT_REV_ID: 82c5e2b83582a61341b8e4a5bfa9d66f7c4f34ab --- stdlib/src/builtin/int.mojo | 8 +++ stdlib/src/builtin/repr.mojo | 89 ++++++++++++++++++++++++++ stdlib/src/builtin/str.mojo | 7 ++ stdlib/src/builtin/string.mojo | 11 ++++ stdlib/src/builtin/value.mojo | 12 ++++ stdlib/src/collections/dict.mojo | 13 ++-- stdlib/src/collections/list.mojo | 8 ++- stdlib/test/builtin/test_int.mojo | 18 ++++++ stdlib/test/builtin/test_repr.mojo | 53 +++++++++++++++ stdlib/test/builtin/test_string.mojo | 8 +++ stdlib/test/collections/test_dict.mojo | 2 +- stdlib/test/collections/test_list.mojo | 9 +-- 12 files changed, 223 insertions(+), 15 deletions(-) create mode 100644 stdlib/src/builtin/repr.mojo create mode 100644 stdlib/test/builtin/test_repr.mojo diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index f03b7ab47b..1e65f60d95 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -373,6 +373,14 @@ struct Int( # Keep buf alive until we've finished with the StringRef _ = buf^ + fn __repr__(self) -> String: + """Get the integer as a string. Returns the same `String` as `__str__`. + + Returns: + A string representation. + """ + return str(self) + @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: """Convert to index. diff --git a/stdlib/src/builtin/repr.mojo b/stdlib/src/builtin/repr.mojo new file mode 100644 index 0000000000..5ea538abce --- /dev/null +++ b/stdlib/src/builtin/repr.mojo @@ -0,0 +1,89 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provide the `repr` function. + +The functions and traits provided here are built-ins, so you don't need to import them. +""" + + +trait Representable: + """A trait that describes a type that has a String representation. + + Any type that conforms to the `Representable` trait can be used with the + `repr` function. Any conforming type must also implement the `__repr__` method. + Here is an example: + + ```mojo + @value + struct Dog(Representable): + var name: String + var age: Int + + fn __repr__(self) -> String: + return "Dog(name=" + repr(self.name) + ", age=" + repr(self.age) + ")" + + var dog = Dog("Rex", 5) + print(repr(dog)) + # Dog(name='Rex', age=5) + ``` + + The method `__repr__` should compute the "official" string representation of a type. + + If at all possible, this should look like a valid Mojo expression + that could be used to recreate a struct instance with the same + value (given an appropriate environment). + So a returned String of the form `module_name.SomeStruct(arg1=value1, arg2=value2)` is advised. + If this is not possible, a string of the form `<...some useful description...>` + should be returned. + + The return value must be a `String` instance. + This is typically used for debugging, so it is important that the representation is information-rich and unambiguous. + + Note that when computing the string representation of a collection (`Dict`, `List`, `Set`, etc...), + the `repr` function is called on each element, not the `str()` function. + """ + + fn __repr__(self) -> String: + """Get the string representation of the type instance, if possible, compatible with Mojo syntax. + + Returns: + The string representation of the instance. + """ + pass + + +fn repr[T: Representable](value: T) -> String: + """Returns the string representation of the given value. + + Args: + value: The value to get the string representation of. + + Parameters: + T: The type of `value`. Must implement the `Representable` trait. + + Returns: + The string representation of the given value. + """ + return value.__repr__() + + +fn repr(value: None) -> String: + """Returns the string representation of `None`. + + Args: + value: A `None` value. + + Returns: + The string representation of `None`. + """ + return "None" diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index cbc8584994..5bc2ff8a85 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -57,6 +57,13 @@ trait Stringable: **Note:** If the `__str__()` method might raise an error, use the [`StringableRaising`](/mojo/stdlib/builtin/str/stringableraising) trait, instead. + + About the difference between `__repr__()` and `__str__()`: + The method `__repr__` compute the compute the "official" string representation of an object + while `__str__` computes the "informal" or nicely printable string representation of an object. + + This method differs from `__repr__()` in that there is no expectation that `__str__()` + return a valid Mojo expression: a more convenient or concise representation can be used. """ fn __str__(self) -> String: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 3ab0fd0b38..579d3491c4 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -452,6 +452,17 @@ struct String( fn __str__(self) -> String: return self + @always_inline + fn __repr__(self) -> String: + """Return a Mojo-compatible representation of the `String` instance. + + You don't need to call this method directly, use `repr(my_string)` instead. + """ + if "'" in self: + return '"' + self + "'" + else: + return "'" + self + "'" + # ===------------------------------------------------------------------===# # Initializers # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index f7247bafa7..909a8197d4 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -133,3 +133,15 @@ trait ComparableCollectionElement(CollectionElement, EqualityComparable): """ pass + + +trait RepresentableCollectionElement(CollectionElement, Representable): + """The RepresentableCollectionElement trait denotes a trait composition + of the `CollectionElement` and `Representable` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `CollectionElement & Representable` in the parameter. + """ + + pass diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 9c20236cc0..c1374734d3 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -45,7 +45,7 @@ trait KeyElement(CollectionElement, Hashable, EqualityComparable): pass -trait StringableKeyElement(KeyElement, Stringable): +trait RepresentableKeyElement(KeyElement, Representable): """A trait composition for types which implement all requirements of dictionary keys and Stringable.""" @@ -534,7 +534,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( @staticmethod fn __str__[ - T: StringableKeyElement, U: StringableCollectionElement + T: RepresentableKeyElement, U: RepresentableCollectionElement ](self: Dict[T, U]) -> String: """Returns a string representation of a `Dict`. @@ -553,14 +553,17 @@ struct Dict[K: KeyElement, V: CollectionElement]( When the compiler supports conditional methods, then a simple `str(my_dict)` will be enough. + Note that both they keys and values' types must implement the `__repr__()` method + for this to work. See the `Representable` trait for more information. + Args: self: The Dict to represent as a string. Parameters: T: The type of the keys in the Dict. Must implement the - traits `Stringable` and `KeyElement`. + traits `Representable` and `KeyElement`. U: The type of the values in the Dict. Must implement the - traits `Stringable` and `CollectionElement`. + traits `Representable` and `CollectionElement`. Returns: A string representation of the Dict. @@ -571,7 +574,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var i = 0 for key_value in self.items(): - result += str(key_value[].key) + ": " + str(key_value[].value) + result += repr(key_value[].key) + ": " + repr(key_value[].value) if i < len(self) - 1: result += ", " i += 1 diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b63af8ba3e..9f2f324408 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -617,7 +617,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return _ListIter[T, mutability, self_life, False](len(ref[]), ref) @staticmethod - fn __str__[U: StringableCollectionElement](self: List[U]) -> String: + fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. Note that since we can't condition methods on a trait yet, @@ -631,12 +631,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): When the compiler supports conditional methods, then a simple `str(my_list)` will be enough. + The elements' type must implement the `__repr__()` for this to work. + Args: self: The list to represent as a string. Parameters: U: The type of the elements in the list. Must implement the - traits `Stringable` and `CollectionElement`. + traits `Representable` and `CollectionElement`. Returns: A string representation of the list. @@ -651,7 +653,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var result = String(List[UInt8](capacity=minimum_capacity)) result += "[" for i in range(len(self)): - result += str(self[i]) + result += repr(self[i]) if i < len(self) - 1: result += ", " result += "]" diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 5f2c51bc7f..25d3ad8a72 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -87,6 +87,22 @@ def test_abs(): assert_equal(abs(Int(0)), 0) +def test_string_conversion(): + assert_equal(str(Int(3)), "3") + assert_equal(str(Int(-3)), "-3") + assert_equal(str(Int(0)), "0") + assert_equal(str(Int(100)), "100") + assert_equal(str(Int(-100)), "-100") + + +def test_int_representation(): + assert_equal(repr(Int(3)), "3") + assert_equal(repr(Int(-3)), "-3") + assert_equal(repr(Int(0)), "0") + assert_equal(repr(Int(100)), "100") + assert_equal(repr(Int(-100)), "-100") + + def main(): test_constructors() test_properties() @@ -99,3 +115,5 @@ def main(): test_floordiv() test_mod() test_abs() + test_string_conversion() + test_int_representation() diff --git a/stdlib/test/builtin/test_repr.mojo b/stdlib/test/builtin/test_repr.mojo new file mode 100644 index 0000000000..9242af9169 --- /dev/null +++ b/stdlib/test/builtin/test_repr.mojo @@ -0,0 +1,53 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal + + +@value +struct Dog(Representable): + var name: String + var age: Int + + fn __repr__(self) -> String: + return "Dog(name=" + repr(self.name) + ", age=" + repr(self.age) + ")" + + +def test_explicit_conformance(): + dog = Dog(name="Fido", age=3) + assert_equal(repr(dog), "Dog(name='Fido', age=3)") + + +@value +struct Cat: + var name: String + var age: Int + + fn __repr__(self) -> String: + return "Cat(name=" + repr(self.name) + ", age=" + repr(self.age) + ")" + + +def test_implicit_conformance(): + cat = Cat(name="Whiskers", age=2) + assert_equal(repr(cat), "Cat(name='Whiskers', age=2)") + + +def test_none_representation(): + assert_equal(repr(None), "None") + + +def main(): + test_explicit_conformance() + test_implicit_conformance() + test_none_representation() diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 56bd80794c..b0b4143a44 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -40,6 +40,13 @@ fn test_stringable() raises: assert_equal("a string", str(String(AString()))) +fn test_representable() raises: + assert_equal(repr(String("hello")), "'hello'") + assert_equal(repr(String(0)), "'0'") + # TODO: Add more complex cases with "'", escape characters, etc + # and make String.__repr__ more robust to handle those cases. + + fn test_constructors() raises: # Default construction assert_equal(0, len(String())) @@ -699,6 +706,7 @@ def main(): test_equality_operators() test_add() test_stringable() + test_representable() test_string_join() test_stringref() test_stringref_from_dtypepointer() diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index ac0eeb2350..c83536422d 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -71,7 +71,7 @@ def test_dict_string_representation_string_int(): some_dict._minimum_size_of_string_representation() <= len(dict_as_string) ) - assert_equal(dict_as_string, "{a: 1, b: 2}") + assert_equal(dict_as_string, "{'a': 1, 'b': 2}") def test_dict_string_representation_int_int(): diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index d5a73dcb11..5650a2f15b 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -673,13 +673,10 @@ def test_converting_list_to_string(): var my_list = List[Int](1, 2, 3) assert_equal(__type_of(my_list).__str__(my_list), "[1, 2, 3]") - var my_list2 = List[SIMD[DType.int8, 2]]( - SIMD[DType.int8, 2](1, 2), SIMD[DType.int8, 2](3, 4) + var my_list4 = List[String]("a", "b", "c", "foo") + assert_equal( + __type_of(my_list4).__str__(my_list4), "['a', 'b', 'c', 'foo']" ) - assert_equal(__type_of(my_list2).__str__(my_list2), "[[1, 2], [3, 4]]") - - var my_list3 = List[Float64](1.0, 2.0, 3.0) - assert_equal(__type_of(my_list3).__str__(my_list3), "[1.0, 2.0, 3.0]") def main(): From f4c4b3874472c84f27ff1d230e8b189e47778111 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 30 Apr 2024 21:07:14 -0700 Subject: [PATCH 0358/2019] [Docs] Update release changelog for 24.3. (#38900) Staged here: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: ebdd02b89271a9bec41b9014147d7bae03d18ead --- docs/changelog-released.md | 643 ++++++++++++++++++++++++++++++++++++- docs/changelog.md | 495 +--------------------------- 2 files changed, 645 insertions(+), 493 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index f423cad6d3..c7ea7477da 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -30,6 +30,627 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## v24.3 (2024-05-02) + +### 🔥 Legendary + +- `AnyPointer` was renamed to + [`UnsafePointer`](mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and is now + Mojo's preferred unsafe pointer type. It has several enhancements, including: + + - The element type can now be any type: it doesn't require `Movable`. + + - Because of this, the `take_value()`, `emplace_value()`, and `move_into()` + methods have been changed to top-level functions and renamed. The new + functions are: + + - [`initialize_pointee_copy`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy) + - [`initialize_pointee_move`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) + - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) + - [`move_pointee`](/mojo/stdlib/memory/unsafe_pointer/move_pointee) + + - A new + [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) + function runs the destructor on the pointee. + + - `UnsafePointer` can be initialized directly from a + [`Reference`](/mojo/stdlib/memory/reference/Reference) with + `UnsafePointer(someRef)` and can convert to a reference with + `yourPointer[]`. Both infer element type and address space. Note that when + you convert a pointer to a reference, there's no way for Mojo to track the + lifetime of the original value. So the resulting reference is no safer than + the original pointer. + +- All of the pointer types received some cleanup to make them more consistent, + for example the `unsafe.bitcast()` global function is now a consistent + [`bitcast()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#bitcast) method + on the pointers, which can convert element type and address space. + +- Improvements to variadic arguments support. + + - Heterogenous variadic pack arguments now work reliably even with memory types, + and have a more convenient API to use, as defined by the + [`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack) type. For + example, a simplified version of `print` can be implemented like this: + + ```mojo + fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): + print_string(str(first)) + + @parameter + fn print_elt[T: Stringable](a: T): + print_string(" ") + print_string(a) + rest.each[print_elt]() + ``` + + - Mojo now supports declaring functions that have both optional and variadic + arguments, both positional and keyword-only. For example, this now works: + + ```mojo + fn variadic_arg_after_default( + a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int + ): ... + ``` + + Positional variadic parameters also work in the presence of optional + parameters. That is: + + ```mojo + fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): + pass + ``` + + Note that variadic keyword parameters are not supported yet. + +- [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now works with memory-only element + types like `String` and allows you to directly index into it with a parameter + expression. This means you can now simply use `x = tup[1]` like Python + instead of `x = tup.get[1, Int]()`. You can also assign into tuple elements + now as well with `tup[1] = x`. + + ```mojo + var tuple = ("Green", 9.3) + var name = tuple[0] + var value = tuple[1] + ``` + + Note that because the subscript must be a parameter expression, you can't + iterate through a `Tuple` using an ordinary `for` loop. + +### Language changes + +#### ⭐️ New + +- Certain dunder methods that take indices + (`__getitem__()`, `__setitem__()`, and `__refitem__()`) or names + (`__getattr__()` and `__setattr__()`) can now take the index or name + as a parameter value instead of an argument value. This is enabled when you + define one of these methods with no argument other than `self` (for a getter) + or `self` and the set value (for a setter). + + This enables types that can only be subscripted into with parameters, as well + as things like the following example, which passes the attribute name as a + parameter so that attribute names can be checked at compile time. + + ```mojo + struct RGB: + fn __getattr__[name: StringLiteral](self) -> Int: + @parameter + if name == "r": return ... + elif name == "g": return ... + else: + constrained[name == "b", "can only access with r, g, or b members"]() + return ... + + var rgb = RGB() + print(rgb.b) # Works + print(rgb.q) # Compile error + ``` + +- Mojo now allows users to capture the source location of code and call location + of functions dynamically using the built-in `__source_location()` and + `__call_location()` functions. For example: + + ```mojo + @always_inline + fn my_assert(cond: Bool, msg: String): + if not cond: + var call_loc = __call_location() + print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) + + fn main(): + my_assert(False, "always fails") # some_file.mojo, line 193 + ``` + + This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that + `__call_location()` only works in `@always_inline` or + `@always_inline("nodebug")` functions. It gives incorrect results if placed in + an `@always_inline` function that's called *from* an + `@always_inline("nodebug")` function. + + Neither `__source_location()` nor `__call_location()` work when called in a + parameter context. For example: + + ```mojo + @always_inline + fn mystery_location() -> String: + var loc = __call_location() + return str(loc.file_name) + + def main(): + alias doesnt_work = mystery_location() # + ``` + +### Standard library changes + +#### ⭐️ New + +- [`List`](/mojo/stdlib/collections/list/List) has several new methods: + + - `pop(index)` for removing an element at a particular index. + By default, `List.pop()` removes the last element in the list. + ([PR #2041](https://github.com/modularml/mojo/pull/2041) by + [@LJ-9801](https://github.com/LJ-9801), fixes + [#2017](https://github.com/modularml/mojo/issues/2017).) + + - `resize(new_size)` for resizing the list without the need to + specify an additional value. + ([PR #2140](https://github.com/modularml/mojo/pull/2140) + by [@mikowals](https://github.com/mikowals), fixes + [#2133](https://github.com/modularml/mojo/issues/2133).) + + - `insert(index, value)` for inserting a value at a specified index + into the `List`. ([PR #2148](https://github.com/modularml/mojo/pull/2148) by + [@whym1here](https://github.com/whym1here), fixes + [#2134](https://github.com/modularml/mojo/issues/2134).) + + - A new constructor `List(ptr, size, capacity)` to to avoid needing to + do a deep copy of an existing contiguous memory allocation when constructing + a new `List`. ([PR #2182](https://github.com/modularml/mojo/pull/2182) by + [@StandinKP](https://github.com/StandinKP), fixes + [#2170](https://github.com/modularml/mojo/issues/2170).) + +- [`Dict`](/mojo/stdlib/collections/dict/Dict) now has a `update()` method to + update keys/values from another `Dict`. + ([PR #2085](https://github.com/modularml/mojo/pull/2085) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + +- [`Set`](/mojo/stdlib/collections/set/Set) now has named methods for set + operations: + - `difference()` mapping to `-` + - `difference_update()` mapping to `-=` + - `intersection_update()` mapping to `&=` + - `update()` mapping to `|=` + + ([PR #2214](https://github.com/modularml/mojo/pull/2214) by + [@arvindavoudi](https://github.com/arvindavoudi).) + +- `Dict`, `List`, and `Set` all conform to the `Boolable` trait. The collections + evaluate to `True` if they contain any elements, `False` otherwise: + + ```mojo + def list_names(names: List[String]): + if names: + for name in names: + print(name[]) + else: + print("No names to list.") + ``` + + ([PR #2262](https://github.com/modularml/mojo/pull/2262) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + +- Added [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) function for + creating reversed iterators. Several range types, `List`, and `Dict` now + support iterating in reverse. + + ```mojo + var numbers = List(1, 2, 3, 4, 5) + for number in reversed(numbers): + print(number) + ``` + + ([PR #2215](https://github.com/modularml/mojo/pull/2215) by + [@helehex](https://github.com/helehex), + [PR #2327](https://github.com/modularml/mojo/pull/2327) by + [@jayzhan211](https://github.com/jayzhan211), contributes towards + [#2325](https://github.com/modularml/mojo/issues/2325).) + +- [`Optional`](/mojo/stdlib/collections/optional/Optional) now implements + `__is__` and `__isnot__` methods so that you can compare an `Optional` with + `None`. For example: + + ```mojo + var opt = Optional(1) + if opt is not None: + print(opt.value()[]) + ``` + + ([PR #2082](https://github.com/modularml/mojo/pull/2082) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + +- The [`Reference`](/mojo/stdlib/memory/reference/Reference) type has several + changes, including: + + - It has moved to the `memory.reference` module instead of `memory.unsafe`. + - `Reference` now has an + [`unsafe_bitcast()`](/mojo/stdlib/memory/reference/Reference#unsafe_bitcast) + method, similar to the pointer types. + + - Several unsafe methods were removed, including `offset()`, + `destroy_element_unsafe()` and `emplace_ref_unsafe()`. This is because + `Reference` is a safe type—use `UnsafePointer` to do unsafe operations. + +- [`Bool`](/mojo/stdlib/builtin/bool/Bool) can now be implicitly converted from + any type conforming to the [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) + trait. This means that you no longer need to write code like this: + + ```mojo + @value + struct MyBoolable: + fn __bool__(self) -> Bool: ... + + fn takes_boolable[T: Boolable](cond: T): ... + + takes_boolable(MyBoolable()) + ``` + + Instead, you can simply write: + + ```mojo + fn takes_bool(cond: Bool): ... + + takes_bool(MyBoolable()) + ``` + + Note that calls to `takes_bool()` will perform the implicit conversion, so in + some cases is it still better to explicitly declare a type parameter, e.g.: + + ```mojo + fn takes_two_boolables[T: Boolable](a: T, b: T): + # Short circuit means `b.__bool__()` might not be evaluated. + if a.__bool__() and b.__bool__(): + ... + ``` + +- [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now conforms to the + [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, meaning that + it can be used as key type for [`Dict`](/mojo/stdlib/collections/dict/Dict). + This allows you to easily build and interact with Python dictionaries in Mojo: + + ```mojo + def main(): + d = PythonObject(Dict[PythonObject, PythonObject]()) + d["foo"] = 12 + d[7] = "bar" + d["foo"] = [1, 2, "something else"] + print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` + ``` + +- [`FileHandle.seek()`](/mojo/stdlib/builtin/file/FileHandle#seek) now has a + `whence` argument that defaults to `os.SEEK_SET` to seek from the beginning of + the file. You can now set to `os.SEEK_CUR` to offset by the current + `FileHandle` seek position: + + ```mojo + var f = open("/tmp/example.txt") + # Skip 32 bytes + f.seek(os.SEEK_CUR, 32) + ``` + + Or `os.SEEK_END` to offset from the end of file: + + ```mojo + # Start from 32 bytes before the end of the file + f.seek(os.SEEK_END, -32) + ``` + +- [`FileHandle.read()`](/mojo/stdlib/builtin/file/FileHandle#read) can now + read straight into a + [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): + + ```mojo + var file = open("/tmp/example.txt", "r") + + # Allocate and load 8 elements + var ptr = DTypePointer[DType.float32].alloc(8) + var bytes = file.read(ptr, 8) + print("bytes read", bytes) + print(ptr.load[width=8]()) + ``` + +- The `sys` module now contains an `exit()` function that would exit a Mojo + program with the specified error code. + + ```mojo + from sys import exit + + exit(0) + ``` + +- The constructors for [`Tensor`](/mojo/stdlib/tensor/tensor/Tensor) have been + changed to be more consistent. As a result, constructors take the shape as the + first argument (instead of the second) when constructing a tensor with pointer + data. + + If you pass a single scalar value to the `Tensor` constructor, it now + broadcasts the value to all elements in the tensor. For example, + `Tensor[DType.float32](TensorShape(2,2), 0)` constructs a `2x2` tensor + initialized with all zeros. This provides an easy way to fill in the data of a + tensor. + +- [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and + `removesuffix()` methods. + ([PR #2038](https://github.com/modularml/mojo/pull/2038) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + +- The [`ord`](/mojo/stdlib/builtin/string/ord) and + [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to + accept any Unicode character. + ([PR #2149](https://github.com/modularml/mojo/pull/2149) by + [@mzaks](https://github.com/mzaks), contributes towards + [#1616](https://github.com/modularml/mojo/issues/1616).) + +- [`Atomic`](/mojo/stdlib/os/atomic/Atomic) is now movable. + ([PR #2088](https://github.com/modularml/mojo/pull/2088) by + [@StandinKP](https://github.com/StandinKP), fixes + [#2026](https://github.com/modularml/mojo/issues/2026).) + +- [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The + `atol()`function is used internally by `String.__int__()`, so + `int(String( " 10 "))` now returns `10` instead of raising an error. + ([PR #2225](https://github.com/modularml/mojo/pull/2225) by + [@artemiogr97](https://github.com/artemiogr97).) + +- [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) now implements the `__rmod__()` + method. ([PR #2186](https://github.com/modularml/mojo/pull/2186) by + [@bgreni](https://github.com/bgreni), fixes + [#1482](https://github.com/modularml/mojo/issues/1482).) + +- [`bool(None)`](/mojo/stdlib/builtin/bool/bool-function) is now implemented. + ([PR #2249](https://github.com/modularml/mojo/pull/2249) by + [@zhoujingya](https://github.com/zhoujingya).) + +- The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) type now + implements `gather()` for gathering a `SIMD` vector from offsets of a current + pointer. Similarly, support for `scatter()` was added to scatter a `SIMD` + vector into offsets of the current pointer. + ([PR #2268](https://github.com/modularml/mojo/pull/2268) by + [@leandrolcampos](https://github.com/leandrolcampos).) + +- The [`len()`](/mojo/stdlib/builtin/len/len) function now handles a + [`range()`](/mojo/stdlib/builtin/range/range) specified with a negative end + value, so that things like `len(range(-1))` work correctly. + ([PR #2204](https://github.com/modularml/mojo/pull/2204) by + [@soraros](https://github.com/soraros).) + +- [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now prints + its location (filename, line, and column where it was called) in its error + message. Similarly, the `assert` helpers in the + [`testing`](/mojo/stdlib/testing/testing/) module now include location + information in their messages. + +- The [`testing.assert_equal[SIMD]()`](/mojo/stdlib/testing/testing/assert_equal) + function now raises if any of the elements mismatch in the two `SIMD` + arguments being compared. + ([PR #2279](https://github.com/modularml/mojo/pull/2279) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + +- The [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) + and [`math.isclose()`](/mojo/stdlib/math/math/isclose) functions now have an + `equal_nan` flag. When set to `True`, then NaNs are considered equal. + +- The [`object`](/mojo/stdlib/builtin/object/object) type now supports the + division, modulo, and left and right shift operators, including the in-place + and reverse variants. + ([PR #2230](https://github.com/modularml/mojo/pull/2230), + [PR #2247](https://github.com/modularml/mojo/pull/2247) by + [@LJ-9801](https://github.com/LJ-9801), fixes + [#2224](https://github.com/modularml/mojo/issues/2224).) + +- Added checked arithmetic operations for `SIMD` integers. + + `SIMD` integer types (including the sized integer scalars like `Int64`) can + now perform checked additions, subtractions, and multiplications using the + following new methods: + + - [`add_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#add_with_overflow) + - [`sub_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#sub_with_overflow) + - [`mul_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#mul_with_overflow) + + Checked arithmetic allows the caller to determine if an operation exceeded + the numeric limits of the type. For example: + + ```mojo + var simd = SIMD[DType.int8, 4](7, 11, 13, 17) + var product: SIMD[DType.int8, 4] + var overflow: SIMD[DType.bool, 4] + (product, overflow) = simd.mul_with_overflow(simd) + for i in range(len(product)): + if overflow[i]: + print("") + else: + print(product[i]) + ``` + + ([PR #2138](https://github.com/modularml/mojo/pull/2138) by + [@lsh](https://github.com/lsh).) + +- Added [`os.remove()`](/mojo/stdlib/os/os/remove) and + [`os.unlink()`](/mojo/stdlib/os/os/unlink) for deleting files. + ([PR #2310](https://github.com/modularml/mojo/pull/2310) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2306](https://github.com/modularml/mojo/issues/2306)) + +#### 🦋 Changed + +- The [`parallel_memcpy()`](/mojo/stdlib/algorithm/memory/parallel_memcpy) + function has moved from the `buffer` package to the `algorithm` package. + Please update your imports accordingly. + +- [`Optional.value()`](/mojo/stdlib/collections/optional/Optional#value) now + returns a reference instead of a copy of the contained value. + ([PR #2226](https://github.com/modularml/mojo/pull/2226)) + + To perform a copy manually, dereference the result: + + ```mojo + var result = Optional(123) + + var value = result.value()[] + ``` + + ([PR #2226](https://github.com/modularml/mojo/pull/2226) by + [@lsh](https://github.com/lsh), fixes + [#2179](https://github.com/modularml/mojo/issues/2179).) + +- Per the accepted community proposal, + [Standardize the representation of byte sequence as a sequence of unsigned + 8-bit integers](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), + began transition to using `UInt8` by changing the data pointer of `Error` + to `DTypePointer[DType.uint8]`. + ([PR #2318](https://github.com/modularml/mojo/pull/2318) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes + towards [#2317](https://github.com/modularml/mojo/issues/2317).) + +- Continued transition to `UnsafePointer` from the legacy `Pointer` type + in various standard library APIs and internals. + ([PR #2365](https://github.com/modularml/mojo/pull/2365), + [PR #2367](https://github.com/modularml/mojo/pull/2367), + [PR #2368](https://github.com/modularml/mojo/pull/2368), + [PR #2370](https://github.com/modularml/mojo/pull/2370), + [PR #2371](https://github.com/modularml/mojo/pull/2371) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + +### Tooling changes + +#### ⭐️ New + +- The `mojo build` and `mojo run` commands now support a `-g` option. This + shorter alias is equivalent to writing `--debug-level full`. This option is + also available in the `mojo debug` command, but is already the default. + +#### 🦋 Changed + +- The behavior of `mojo build` when invoked without an output `-o` argument has + changed slightly: `mojo build ./test-dir/program.mojo` now outputs an + executable to the path `./program`, whereas before it would output to the path + `./test-dir/program`. + +- The `mojo package` command no longer supports the `-D` flag. All compilation + environment flags should be provided at the point of package use (e.g. + `mojo run` or `mojo build`). + +- The REPL no longer allows type level variable declarations to be + uninitialized, e.g. it will reject `var s: String`. This is because it does + not do proper lifetime tracking (yet!) across cells, and so such code would + lead to a crash. You can work around this by initializing to a dummy value + and overwriting later. This limitation only applies to top level variables, + variables in functions work as they always have. + +### Other changes + +#### Low-level language changes + +- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to + the underlying memory representation as a `!lit.ref` value without checking + initialization status of the underlying value. This is useful in very + low-level logic but isn't designed for general usability and will likely + change in the future. + +- Properties can now be specified on inline MLIR ops: + + ```mojo + _ = __mlir_op.`kgen.source_loc`[ + _type = ( + __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` + ), + _properties = __mlir_attr.`{inlineCount = 1 : i64}`, + ]() + ``` + + As the example shows above, the protected `_properties` attribute can be + passed during op construction, with an MLIR `DictionaryAttr` value. + +#### ❌ Removed + +- Support for "register only" variadic packs has been removed. Instead of + `AnyRegType`, please upgrade your code to `AnyType` in examples like this: + + ```mojo + fn your_function[*Types: AnyRegType](*args: *Ts): ... + ``` + + This move gives you access to a nicer API and has the benefit of being memory + safe and correct for non-trivial types. If you need specific APIs on the + types, please use the correct trait instead of `AnyType`. + +- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults + to popping the last element in the list. + +- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. + +- The `__get_lvalue_as_address(x)` magic function has been removed. To get a + reference to a value use `Reference(x)` and if you need an unsafe pointer, you + can use `UnsafePointer.address_of(x)`. + +#### 🛠️ Fixed + +- [#516](https://github.com/modularml/mojo/issues/516) and + [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. + "Can't create a function that returns two strings." + +- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5). + +- [#1609](https://github.com/modularml/mojo/issues/1609) alias with + `DynamicVector[Tuple[Int]]` fails. + +- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` + in a Mojo package is an error, for now. This is not intended to work yet, + erroring for now will help to prevent accidental undefined behavior. + +- [#1215](https://github.com/modularml/mojo/issues/1215) and + [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no + longer cuts off hover previews for functions with functional arguments, + parameters, or results. + +- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and + documentation generation handling of inout arguments. + +- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer + crashes the Mojo parser. + +- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac + has been fixed. + +- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadic arguments + don't work with non-trivial register-only types. + +- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed + and formatted correctly by `mojo format`. + +- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to + `@value` decorator and structs with empty body. + +- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after + syntax error during tuple creation. + +- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now + properly supports signature types with named arguments and parameters. + +- [#2007](https://github.com/modularml/mojo/issues/2007) and + [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer + crashes on certain types of closures. + +- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` + decorator fails gracefully after duplicate field error. + +- [#2068](https://github.com/modularml/mojo/issues/2068) + Fix `SIMD.reduce()` for size_out == 2. + ([PR #2102](https://github.com/modularml/mojo/pull/2102) by + [@soraros](https://github.com/soraros).) + ## v24.2.1 (2024-04-11) This release doesn't include any changes to Mojo. @@ -1004,7 +1625,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are _not enabled_ + before the program exits. By default, assertions are *not enabled* in the standard library right now for performance reasons. - The Mojo Language Server now implements the References request. IDEs use @@ -1114,7 +1735,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. array slices. While this is a major step forward for the lifetimes system in Mojo, it is - still _very_ early and awkward to use. Notably, there is no syntactic sugar + still *very* early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now. @@ -1437,8 +2058,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Traits have arrived! - You can now define a _trait_, which consists of a required set of method - prototypes. A struct can _conform to_ the trait by implementing these methods. + You can now define a *trait*, which consists of a required set of method + prototypes. A struct can *conform to* the trait by implementing these methods. This lets you write generic functions that work on any structs that conform to a given trait. @@ -1558,7 +2179,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### ⭐️ New - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include _everything_ about Mojo yet, but it includes a lot, + It doesn't include *everything* about Mojo yet, but it includes a lot, and more than the original [programming manual](/mojo/programming-manual.html) (now deprecated). @@ -1581,7 +2202,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` In the first signature for `eat()`, the `b` parameter isn't bound, so it's - _implicitly_ added as an input parameter on the function. + *implicitly* added as an input parameter on the function. In the second signature for `eat()`, the author has explicitly defined an input parameter (`_b`), which is bound to the second parameter on the argument @@ -1826,8 +2447,8 @@ the previous "read to EOF" behavior when size is negative. - There is an issue affecting Jupyter notebooks that use autotuning and traits. This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the _Matrix multiplication in - Mojo_ notebook. + outside of the notebooks. This issue affects the *Matrix multiplication in + Mojo* notebook. ## v0.5.0 (2023-11-2) @@ -1842,7 +2463,7 @@ the previous "read to EOF" behavior when size is negative. function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. -- Mojo now supports compile-time _keyword parameters_, in addition to existing +- Mojo now supports compile-time *keyword parameters*, in addition to existing support for [keyword arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For example: @@ -2181,7 +2802,7 @@ the previous "read to EOF" behavior when size is negative. return self^ ``` - Here Mojo _cannot_ invoke a noop `__exit__` method because the context + Here Mojo *cannot* invoke a noop `__exit__` method because the context manager is consumed by the `__enter__` method. This can be used for types (like file descriptors) that are traditionally used with `with` statements, even though Mojo's guaranteed early destruction doesn't require that. @@ -3448,7 +4069,7 @@ busy this week. is currently unsupported). These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to _not_ capture values + types, with the caveat that closures are known to *not* capture values correctly. Be very careful with interesting types in the vicinity of a closure! diff --git a/docs/changelog.md b/docs/changelog.md index 70616aaad0..e0ff7bcede 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,283 +16,18 @@ what we publish. ### 🔥 Legendary -- Tuple now works with memory-only element types like String and allows you to - directly index into it with a parameter exprssion. This means you can now - simply use `x = tup[1]` like Python instead of `x = tup.get[1, Int]()`. You - can also assign into tuple elements now as well with `tup[1] = x`. - ### ⭐️ New -- Heterogenous variadic pack arguments now work reliably even with memory types, - and have a more convenient API to use, as defined on the `VariadicPack` type. - For example, a simplified version of `print` can be implemented as: - - ```mojo - fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): - print_string(str(first)) - - @parameter - fn print_elt[T: Stringable](a: T): - print_string(" ") - print_string(a) - rest.each[print_elt]() - ``` - -- The `sys` module now contains an `exit` function that would exit a Mojo - program with the specified error code. - -- The constructors for `tensor.Tensor` have been changed to be more consistent. - As a result, one has to pass in the shape as first argument (instead of the - second) when constructing a tensor with pointer data. - -- The constructor for `tensor.Tensor` will now splat a scalar if its passed in. - For example, `Tensor[DType.float32](TensorShape(2,2), 0)` will construct a - `2x2` tensor which is initialized with all zeros. This provides an easy way - to fill the data of a tensor. - -- The `mojo build` and `mojo run` commands now support a `-g` option. This - shorter alias is equivalent to writing `--debug-level full`. This option is - also available in the `mojo debug` command, but is already the default. - -- `PythonObject` now conforms to the `KeyElement` trait, meaning that it can be - used as key type for `Dict`. This allows on to easily build and interact with - Python dictionaries in mojo: - - ```mojo - def main(): - d = PythonObject(Dict[PythonObject, PythonObject]()) - d["foo"] = 12 - d[7] = "bar" - d["foo"] = [1, 2, "something else"] - print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` - ``` - -- `List` now has several new methods: - - `pop(index)` for removing an element at a particular index. - ([PR #2041](https://github.com/modularml/mojo/pull/2041) by - [@LJ-9801](https://github.com/LJ-9801), fixes - [#2017](https://github.com/modularml/mojo/issues/2017)). By default, - `List.pop()` removes the last element in the list. - - - `resize(new_size)` for resizing the list without the need to specify an - additional value. ([PR #2140](https://github.com/modularml/mojo/pull/2140) - by [@mikowals](https://github.com/mikowals), fixes - [#2133](https://github.com/modularml/mojo/issues/2133)) - - - `insert(index, value)` for inserting a value at a specified index into the - `List`. ([PR #2148](https://github.com/modularml/mojo/pull/2148) by - [@whym1here](https://github.com/whym1here), fixes - [#2134](https://github.com/modularml/mojo/issues/2134)) - - - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep - copy of an existing contiguous memory allocation when constructing a new - `List`. ([PR #2182](https://github.com/modularml/mojo/pull/2182) by - [@StandinKP](https://github.com/StandinKP), fixes - [#2170](https://github.com/modularml/mojo/issues/2170)) - -- `Dict` now has a `update()` method to update keys/values from another `Dict`. - ([PR #2085](https://github.com/modularml/mojo/pull/2085) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)). - -- `Set` now has named methods for set operations - ([PR #2214](https://github.com/modularml/mojo/pull/2214) by - [@arvindavoudi](https://github.com/arvindavoudi)): - - `Set.difference()` mapping to `-` - - `Set.difference_update()` mapping to `-=` - - `Set.intersection_update()` mapping to `&=` - - `Set.update()` mapping to `|=` - -- `String` now has `removeprefix()` and `removesuffix()` methods. - ([PR #2038](https://github.com/modularml/mojo/pull/2038) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `Optional` now implements `__is__` and `__isnot__` methods so that you can - compare an `Optional` with `None`, e.g. `Optional(1) is not None` for example. - ([PR #2082](https://github.com/modularml/mojo/pull/2082) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- The `ord` and `chr` functions have been improved to accept any Unicode - character. ([PR #2149](https://github.com/modularml/mojo/pull/2149) by - [@mzaks](https://github.com/mzaks), contributes towards - [#1616](https://github.com/modularml/mojo/issues/1616)) - -- `Atomic` is now movable. - ([PR #2088](https://github.com/modularml/mojo/pull/2088) by - [@StandinKP](https://github.com/StandinKP), fixes - [#2026](https://github.com/modularml/mojo/issues/2026)) - -- `Dict` and `List` are both `Boolable` now. - ([PR #2262](https://github.com/modularml/mojo/pull/2262) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` - instead of raising an error. - ([PR #2225](https://github.com/modularml/mojo/pull/2225) by - [@artemiogr97](https://github.com/artemiogr97)) - -- `SIMD` now implements `__rmod__`. - ([PR #2186](https://github.com/modularml/mojo/pull/2186) by - [@bgreni](https://github.com/bgreni), fixes - [#1482](https://github.com/modularml/mojo/issues/1482)) - -- `bool(None)` is now implemented. - ([PR #2249](https://github.com/modularml/mojo/pull/2249) by - [@zhoujingya](https://github.com/zhoujingya)) - -- The `DTypePointer` type now implements `gather` for gathering a `SIMD` vector - from offsets of a current pointer. Similarly, support for `scatter` was added - to scatter a `SIMD` vector into offsets of the current pointer. - ([PR #2268](https://github.com/modularml/mojo/pull/2268) by - [@leandrolcampos](https://github.com/leandrolcampos)) - -- The `len` function for unary `range` with negative end values has been fixed - so that things like `len(range(-1))` work correctly now. - ([PR #2204](https://github.com/modularml/mojo/pull/2204) by - [@soraros](https://github.com/soraros)) - -- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to - the underlying memory representation as a `!lit.ref` value without checking - initialization status of the underlying value. This is useful in very - low-level logic but isn't designed for general usability and will likely - change in the future. - -- The `testing.assert_equal[SIMD]()` now raises if any of the elements mismatch - in the two `SIMD` arguments being compared. - ([PR #2279](https://github.com/modularml/mojo/pull/2279) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- The `testing.assert_almost_equal` and `math.isclose` functions now have an - `equal_nan` flag. When set to True, then NaNs are considered equal. - -- Mojo now supports declaring functions that have both optional and variadic - arguments, both positional and keyword-only. E.g. this now works: - - ```mojo - fn variadic_arg_after_default( - a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int - ): ... - ``` - - Positional variadic parameters also work in the presence of optional - parameters, i.e.: - - ```mojo - fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): - pass - ``` - - Note that variadic keyword parameters are not supported yet. - -- The `__getitem__`/`__getattr__` and related methods can now take indices as - parameter values instead of argument values. This is enabled when defining - these as taking no arguments other than 'self' and the set value in a setter. - This enables types that can only be subscript into with parameters, as well - as things like: - - ```mojo - struct RGB: - fn __getattr__[name: StringLiteral](self) -> Int: - @parameter - if name == "r": return ... - elif name == "g": return ... - else: - constrained[name == "b", "can only access with r, g, or b members"]() - return ... - ``` - -- Added `reversed()` for creating reversed iterators. Several range types, - `List`, and `Dict` now support iterating in reverse. - ([PR #2215](https://github.com/modularml/mojo/pull/2215) by - [@helehex](https://github.com/helehex), - [PR #2327](https://github.com/modularml/mojo/pull/2327) by - [@jayzhan211](https://github.com/jayzhan211), contributes towards - [#2325](https://github.com/modularml/mojo/issues/2325)) - -- `object` now supports the division, modulo, and left and right shift - operators. ([PR #2230](https://github.com/modularml/mojo/pull/2230), - [PR #2247](https://github.com/modularml/mojo/pull/2247) by - [@LJ-9801](https://github.com/LJ-9801), fixes - [#2224](https://github.com/modularml/mojo/issues/2224)) - - The following operator dunder methods were added: - - - `object.__mod__` - - `object.__truediv__` - - `object.__floordiv__` - - `object.__lshift__` - - `object.__rshift__` - - As well as the in-place and reverse variants. - -- Added checked arithmetic operations. - ([PR #2138](https://github.com/modularml/mojo/pull/2138) by - [#lsh](https://github.com/lsh)) - - SIMD integral types (including the sized integral scalars like `Int64`) can - now perform checked additions, substractions, and multiplications using the - following new methods: - - - `SIMD.add_with_overflow` - - `SIMD.sub_with_overflow` - - `SIMD.mul_with_overflow` - - Checked arithimetic allows the caller to determine if an operation exceeded - the numeric limits of the type. - -- Added `os.remove()` and `os.unlink()` for deleting files. - ([PR #2310](https://github.com/modularml/mojo/pull/2310) by - [@artemiogr97](https://github.com/artemiogr97), fixes - [#2306](https://github.com/modularml/mojo/issues/2306)) - -- Properties can now be specified on inline mlir ops: - - ```mojo - _ = __mlir_op.`kgen.source_loc`[ - _type = ( - __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` - ), - _properties = __mlir_attr.`{inlineCount = 1 : i64}`, - ]() - ``` - - As the example shows above, the protected `_properties` attribute can be - passed during op construction, with an MLIR `DictionaryAttr` value. - -- Mojo now allows users to capture source location of code and call location of - functions dynamically. For example: - - ```mojo - @always_inline - fn my_assert(cond: Bool, msg: String): - if not cond: - var call_loc = __call_location() - print("In", call_loc.file_name, "on line", str(call_loc.line) + ":", msg) - - fn main(): - my_assert(False, "always fails") # some_file.mojo, line 193 - ``` - - will print `In /path/to/some_file.mojo on line 193: always fails`. Note that - `__call_location` only works in `@always_inline("nodebug")` and - `@always_inline` functions, as well as limitations on its use in parameter - contexts (see the documentation for more details). - -- `debug_assert` now prints its location (filename, line, and column where it - was called) in its error message. Similarly, the `assert_*` helpers in the - `testing` module now include location information in their messages. - - `int()` can now take a string and a specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. - ([PR #2273](https://github.com/modularml/mojo/pull/2273) by + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with the + base determined by whether the string contains the prefix `"0x"`, `"0o"`, or + `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by [@artemiogr97](https://github.com/artemiogr97), fixes [#2274](https://github.com/modularml/mojo/issues/2274)) - Additionally, if a base of zero is specified, the string will be parsed as if - it was an integer literal, with the base determined by whether the - string contains the prefix `"0x"`, `"0o"`, or `"0b"`. - -- Mojo now supports types to opt in to use the `abs` function by implementing - the `__abs__` method (i.e. by conforming to the new `Absable` trait), e.g.: +- Mojo now allows types to opt in to use the `abs()` function by implementing + the `__abs__()` method, defined by the new `Absable`: ```mojo from math import sqrt @@ -305,10 +40,13 @@ what we publish. return sqrt(self.x * self.x + self.y * self.y) ``` -- Mojo now supports types to opt in to use the `floor` and `ceil` functions in - the `math` module by implementing the `__floor__` and `__ceil__` methods (and - so conforming to the new `math.Floorable` and `math.Ceilable` traits) - respectively. For example: +- The `abs()` function has also moved from `math` to `builtin`, so you no longer + need to do `from math import abs`. + +- Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in + the `math` module by implementing the `__floor__()` and `__ceil__()` methods + (and so conforming to the new `math.Floorable` and `math.Ceilable` traits, + respectively). For example: ```mojo from math import Ceilable, Floorable, ceil, floor @@ -327,216 +65,9 @@ what we publish. ### 🦋 Changed -- The behavior of `mojo build` when invoked without an output `-o` argument has - changed slightly: `mojo build ./test-dir/program.mojo` now outputs an - executable to the path `./program`, whereas before it would output to the path - `./test-dir/program`. - -- The REPL no longer allows type level variable declarations to be - uninitialized, e.g. it will reject `var s: String`. This is because it does - not do proper lifetime tracking (yet!) across cells, and so such code would - lead to a crash. You can work around this by initializing to a dummy value - and overwriting later. This limitation only applies to top level variables, - variables in functions work as they always have. - -- `AnyPointer` got renamed to `UnsafePointer` and is now Mojo's preferred unsafe - pointer type. It has several enhancements, including: - 1) The element type can now be `AnyType`: it doesn't require `Movable`. - 2) Because of this, the `take_value`, `emplace_value`, and `move_into` methods - have been changed to be top-level functions, and were renamed to - `move_from_pointee`, `initialize_pointee_*` and `move_pointee` respectively. - 3) A new `destroy_pointee` function runs the destructor on the pointee. - 4) `UnsafePointer` can be initialized directly from a `Reference` with - `UnsafePointer(someRef)` and can convert to an immortal reference with - `yourPointer[]`. Both infer element type and address space. - -- All of the pointers got a pass of cleanup to make them more consistent, for - example the `unsafe.bitcast` global function is now a consistent `bitcast` - method on the pointers, which can convert element type and address space. - -- The `Reference` type has several changes, including: - 1) It is now located in `memory.reference` instead of `memory.unsafe`. - 2) `Reference` now has an unsafe `unsafe_bitcast` method like `UnsafePointer`. - 3) Several unsafe methods were removed, including `offset`, - `destroy_element_unsafe` and `emplace_ref_unsafe`. This is because - `Reference` is a safe type - use `UnsafePointer` to do unsafe operations. - -- The `mojo package` command no longer supports the `-D` flag. All compilation - environment flags should be provided at the point of package use (e.g. - `mojo run` or `mojo build`). - -- `parallel_memcpy` function has moved from the `buffer` package to the - `algorithm` package. Please update your imports accordingly. - -- `FileHandle.seek()` now has a whence argument that defaults to `os.SEEK_SET` - to seek from the beginning of the file. You can now set to `os.SEEK_CUR` to - offset by the current `FileHandle` seek position: - - ```mojo - var f = open("/tmp/example.txt") - # Skip 32 bytes - f.seek(os.SEEK_CUR, 32) - ``` - - Or `os.SEEK_END` to offset from the end of file: - - ```mojo - # Start from 32 bytes before the end of the file - f.seek(os.SEEK_END, -32) - ``` - -- `FileHandle.read()` can now read straight into a `DTypePointer`: - - ```mojo - var file = open("/tmp/example.txt", "r") - - # Allocate and load 8 elements - var ptr = DTypePointer[DType.float32].alloc(8) - var bytes = file.read(ptr, 8) - print("bytes read", bytes) - print(ptr.load[width=8]()) - ``` - -- `Optional.value()` will now return a reference instead of a copy of the - contained value. ([PR #2226](https://github.com/modularml/mojo/pull/2226) by - [#lsh](https://github.com/lsh), fixes - [#2179](https://github.com/modularml/mojo/issues/2179)) - - To perform a copy manually, dereference the result: - - ```mojo - var result = Optional(123) - - var value = result.value()[] - ``` - -- Per the accepted community proposal - [`proposals/byte-as-uint8.md`](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), - began transition to using `UInt8` by changing the data pointer of `Error` to - `DTypePointer[DType.uint8]`. - ([PR #2318](https://github.com/modularml/mojo/pull/2318) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes - towards [#2317](https://github.com/modularml/mojo/issues/2317)) - -- Continued transition to `UnsafePointer` away from the legacy `Pointer` type in - various standard library APIs and internals. - ([PR #2365](https://github.com/modularml/mojo/pull/2365), - [PR #2367](https://github.com/modularml/mojo/pull/2367), - [PR #2368](https://github.com/modularml/mojo/pull/2368), - [PR #2370](https://github.com/modularml/mojo/pull/2370), - [PR #2371](https://github.com/modularml/mojo/pull/2371) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `Bool` can now be implicitly converted from any type conforming to `Boolable`. - This means that you no longer need to write things like this: - - ```mojo - @value - struct MyBoolable: - fn __bool__(self) -> Bool: ... - - fn takes_boolable[T: Boolable](cond: T): ... - - takes_boolable(MyBoolable()) - ``` - - Instead, you can simply have - - ```mojo - fn takes_bool(cond: Bool): ... - - takes_bool(MyBoolable()) - ``` - - Note that calls to `takes_bool` will perform the implicit conversion, so in - some cases is it still better to explicitly declare type parameter, e.g.: - - ```mojo - fn takes_two_boolables[T: Boolable](a: T, b: T): - # Short circuit means `b.__bool__()` might not be evaluated. - if a.__bool__() and b.__bool__(): - ... - ``` - -- The `abs` function has also moved from `math` to `builtin`, so you no longer - need to do `from math import abs`. - ### ❌ Removed -- Support for "register only" variadic packs has been removed. Instead of - `AnyRegType`, please upgrade your code to `AnyType` in examples like this: - - ```mojo - fn your_function[*Types: AnyRegType](*args: *Ts): ... - ``` - - This move gives you access to nicer API and has the benefit of being memory - safe and correct for non-trivial types. If you need specific APIs on the - types, please use the correct trait bound instead of `AnyType`. - -- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults - to popping the last element in the list. - -- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. - -- The `__get_lvalue_as_address(x)` magic function has been removed. To get a - reference to a value use `Reference(x)` and if you need an unsafe pointer, you - can use `UnsafePointer.address_of(x)`. - - The method `object.print()` has been removed. Since now, `object` has the `Stringable` trait, you can use `print(my_object)` instead. ### 🛠️ Fixed - -- [#516](https://github.com/modularml/mojo/issues/516) and - [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. - "Can't create a function that returns two strings" - -- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5) - -- [#1609](https://github.com/modularml/mojo/issues/1609) alias with - `DynamicVector[Tuple[Int]]` fails. - -- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` - in a Mojo package is an error, for now. This is not intended to work yet, - erroring for now will help to prevent accidental undefined behavior. - -- [#1215](https://github.com/modularml/mojo/issues/1215) and - [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no - longer cuts off hover previews for functions with functional arguments, - parameters, or results. - -- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and - documentation generation handling of inout arguments. - -- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer - crashes the Mojo parser. - -- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac - has been fixed. - -- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadics don't - work with non-trivial register-only types. - -- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed - and formatted correctly by `mojo format`. - -- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to - `@value` decorator and structs with empty body. - -- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after - syntax error during tuple creation - -- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now - properly supports signature types with named arguments and parameters. - -- [#2007](https://github.com/modularml/mojo/issues/2007) and - [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer - crashes on certain types of closures. - -- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` - decorator fails gracefully after duplicate field error. - -- [#2068](https://github.com/modularml/mojo/issues/2068) Fix simd.reduce for - size_out == 2 ([PR #2102](https://github.com/modularml/mojo/pull/2102) by - [@soraros](https://github.com/soraros)) From 8c96caa0ef505a396f78fe3971b004abe801dc16 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 1 May 2024 12:37:28 -0400 Subject: [PATCH 0359/2019] [break-glass] Revert "[External] [stdlib] Enhance Handling of Infinity and NaN in `assert_almost_equal`" (#39024) Reverts modularml/modular#38991 because it breaks some Framework tests. MODULAR_ORIG_COMMIT_REV_ID: db540c7ffc762b30c234ba2daa81f9def5a338af --- stdlib/src/testing/testing.mojo | 59 ++++-------- stdlib/src/utils/_numerics.mojo | 54 ----------- stdlib/test/testing/test_assertion.mojo | 114 ++---------------------- stdlib/test/utils/test_numerics.mojo | 21 +---- 4 files changed, 24 insertions(+), 224 deletions(-) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index b1922ffb10..a7b25e7c10 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,7 +19,7 @@ from testing import assert_true ``` """ from collections import Optional -from utils._numerics import isfinite, isnan +from utils._numerics import isnan from builtin._location import __call_location, _SourceLocation # ===----------------------------------------------------------------------=== # @@ -41,29 +41,21 @@ fn _isclose( rtol: Scalar[a.type], equal_nan: Bool, ) -> SIMD[DType.bool, a.size]: - constrained[ - a.type.is_bool() or a.type.is_integral() or a.type.is_floating_point(), - "input type must be boolean, integral, or floating-point", - ]() - @parameter if a.type.is_bool() or a.type.is_integral(): return a == b - else: - var both_nan = isnan(a) & isnan(b) - if equal_nan and both_nan.reduce_and(): - return True - - var res = (a == b) - var atol_vec = SIMD[a.type, a.size](atol) - var rtol_vec = SIMD[a.type, a.size](rtol) - res |= ( - isfinite(a) - & isfinite(b) - & (_abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b))))) - ) - return res | both_nan if equal_nan else res + if equal_nan and isnan(a) and isnan(b): + return True + + var atol_vec = SIMD[a.type, a.size](atol) + var rtol_vec = SIMD[a.type, a.size](rtol) + var res = _abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b)))) + + if not equal_nan: + return res + + return res.select(res, isnan(a) and isnan(b)) # ===----------------------------------------------------------------------=== # @@ -262,14 +254,6 @@ fn assert_almost_equal[ """Asserts that the input values are equal up to a tolerance. If it is not then an Error is raised. - When the type is boolean or integral, then equality is checked. When the - type is floating-point, then this checks if the two input values are - numerically the close using the $abs(lhs - rhs) <= max(rtol * max(abs(lhs), - abs(rhs)), atol)$ formula. - - Constraints: - The type must be boolean, integral, or floating-point. - Parameters: type: The dtype of the left- and right-hand-side SIMD vectors. size: The width of the left- and right-hand-side SIMD vectors. @@ -278,31 +262,22 @@ fn assert_almost_equal[ lhs: The lhs of the equality. rhs: The rhs of the equality. msg: The message to print. - atol: The absolute tolerance. + atol: The _absolute tolerance. rtol: The relative tolerance. equal_nan: Whether to treat nans as equal. Raises: An Error with the provided message if assert fails and `None` otherwise. """ - constrained[ - type.is_bool() or type.is_integral() or type.is_floating_point(), - "type must be boolean, integral, or floating-point", - ]() - var almost_equal = _isclose( lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan ) - if not almost_equal.reduce_and(): - var err = "AssertionError: " + str(lhs) + " is not close to " + str(rhs) - - @parameter - if type.is_integral() or type.is_floating_point(): - err += " with a diff of " + str(_abs(lhs - rhs)) - + if not almost_equal: + var err = str(lhs) + " is not close to " + str( + rhs + ) + " with a diff of " + _abs(lhs - rhs) if msg: err += " (" + msg + ")" - raise _assert_error(err, __call_location()) diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index 27991fafab..db9dd0b752 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -647,60 +647,6 @@ fn isnan[ ](val.value, (signaling_nan_test | quiet_nan_test).value) -# ===----------------------------------------------------------------------===# -# inf -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inf[type: DType]() -> Scalar[type]: - """Gets a +inf value for the given dtype. - - Constraints: - Can only be used for FP dtypes. - - Parameters: - type: The value dtype. - - Returns: - The +inf value of the given dtype. - """ - - @parameter - if type == DType.float16: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - elif type == DType.bfloat16: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - elif type == DType.float32: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - elif type == DType.float64: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - else: - constrained[False, "+inf only support on floating point types"]() - - return 0 - - # ===----------------------------------------------------------------------===# # isinf # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index e9e8737c43..5a50096ba1 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -13,14 +13,13 @@ # RUN: %mojo -debug-level full %s from testing import ( - assert_almost_equal, assert_equal, - assert_false, assert_not_equal, assert_raises, assert_true, + assert_false, + assert_almost_equal, ) -from utils._numerics import inf, nan @value @@ -62,122 +61,22 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:63:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:62:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:68:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:67:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:73:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:72:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:78:25: AssertionError:" in str(e)) - - -def test_assert_almost_equal(): - alias float_type = DType.float32 - alias _inf = inf[float_type]() - alias _nan = nan[float_type]() - - @parameter - def _should_succeed[ - type: DType, size: Int - ]( - lhs: SIMD[type, size], - rhs: SIMD[type, size], - *, - atol: Scalar[type] = 0, - rtol: Scalar[type] = 0, - equal_nan: Bool = False, - ): - var msg = "`test_assert_almost_equal` should have succeeded" - assert_almost_equal( - lhs, rhs, msg=msg, atol=atol, rtol=rtol, equal_nan=equal_nan - ) - - _should_succeed[DType.bool, 1](True, True) - _should_succeed(SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, 1)) - _should_succeed( - SIMD[float_type, 2](-_inf, _inf), SIMD[float_type, 2](-_inf, _inf) - ) - _should_succeed( - SIMD[float_type, 2](-_nan, _nan), - SIMD[float_type, 2](-_nan, _nan), - equal_nan=True, - ) - _should_succeed( - SIMD[float_type, 2](1.0, -1.1), - SIMD[float_type, 2](1.1, -1.0), - atol=0.11, - ) - _should_succeed( - SIMD[float_type, 2](1.0, -1.1), - SIMD[float_type, 2](1.1, -1.0), - rtol=0.10, - ) - - @parameter - def _should_fail[ - type: DType, size: Int - ]( - lhs: SIMD[type, size], - rhs: SIMD[type, size], - *, - atol: Scalar[type] = 0, - rtol: Scalar[type] = 0, - equal_nan: Bool = False, - ): - var msg = "`test_assert_almost_equal` should have failed" - with assert_raises(contains=msg): - assert_almost_equal( - lhs, rhs, msg=msg, atol=atol, rtol=rtol, equal_nan=equal_nan - ) - - _should_fail[DType.bool, 1](True, False) - _should_fail( - SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, -1), atol=5.0 - ) - _should_fail( - SIMD[float_type, 2](-_inf, 0.0), - SIMD[float_type, 2](_inf, 0.0), - rtol=0.1, - ) - _should_fail( - SIMD[float_type, 2](_inf, 0.0), - SIMD[float_type, 2](0.0, 0.0), - rtol=0.1, - ) - _should_fail( - SIMD[float_type, 2](_nan, 0.0), - SIMD[float_type, 2](_nan, 0.0), - equal_nan=False, - ) - _should_fail( - SIMD[float_type, 2](_nan, 0.0), - SIMD[float_type, 2](0.0, 0.0), - equal_nan=False, - ) - _should_fail( - SIMD[float_type, 2](_nan, 0.0), - SIMD[float_type, 2](0.0, 0.0), - equal_nan=True, - ) - _should_fail( - SIMD[float_type, 2](1.0, 0.0), - SIMD[float_type, 2](1.1, 0.0), - atol=0.05, - ) - _should_fail( - SIMD[float_type, 2](-1.0, 0.0), - SIMD[float_type, 2](-1.1, 0.0), - rtol=0.05, - ) + assert_true("test_assertion.mojo:77:25: AssertionError:" in str(e)) def main(): @@ -185,4 +84,3 @@ def main(): test_assert_not_equal_is_generic() test_assert_equal_with_simd() test_assert_messages() - test_assert_almost_equal() diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index f5dc97f9d6..d39d52dea8 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -12,9 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.info import has_neon +from utils._numerics import FPUtils from testing import assert_equal, assert_true, assert_false -from utils._numerics import FPUtils, inf, isinf alias FPU64 = FPUtils[DType.float64] @@ -46,23 +45,5 @@ fn test_numerics() raises: assert_equal(FPU64.get_mantissa(FPU64.pack(True, 6, 12)), 12) -fn test_inf() raises: - @parameter - fn _test_inf[type: DType]() raises: - var val = inf[type]() - var msg = "`test_inf` failed for `type == " + str(type) + "`" - assert_true((val > 0.0) & isinf(val), msg=msg) - - @parameter - if not has_neon(): - # "bf16 is not supported for ARM architectures" - _test_inf[DType.bfloat16]() - - _test_inf[DType.float16]() - _test_inf[DType.float32]() - _test_inf[DType.float64]() - - def main(): test_numerics() - test_inf() From 443cff9ef889ec743f77253f0f45df8c4f7973fb Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Wed, 1 May 2024 11:17:01 -0600 Subject: [PATCH 0360/2019] [External] [mojo-stdlib] Add `List.count(value)` method (#38915) [External] [mojo-stdlib] Add `List.count(value)` method Add `List.count(value)` method to count the number of occurrences of a value in the `List`. Fixes #2263 --------- Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2406 MODULAR_ORIG_COMMIT_REV_ID: e9e9be6db11d56f951e94daf22fd0ff8520615fa --- stdlib/src/collections/list.mojo | 32 +++++++++++++++++++++++++- stdlib/test/collections/test_list.mojo | 11 +++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9f2f324408..9963e29d0a 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,7 +20,6 @@ from collections import List """ -from builtin.value import StringableCollectionElement from memory import UnsafePointer, Reference from memory.unsafe_pointer import move_pointee, move_from_pointee from .optional import Optional @@ -658,3 +657,34 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): result += ", " result += "]" return result + + @staticmethod + fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: + """Counts the number of occurrences of a value in the list. + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below. + + ```mojo + var my_list = List[Int](1, 2, 3) + print(__type_of(my_list).count(my_list, 1)) + ``` + + When the compiler supports conditional methods, then a simple `my_list.count(1)` will + be enough. + + Parameters: + T: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + self: The list to search. + value: The value to count. + + Returns: + The number of occurrences of the value in the list. + """ + var count = 0 + for elem in self: + if elem[] == value: + count += 1 + return count diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 5650a2f15b..4f9f9ac0cb 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -679,6 +679,16 @@ def test_converting_list_to_string(): ) +def test_list_count(): + var list = List[Int](1, 2, 3, 2, 5, 6, 7, 8, 9, 10) + assert_equal(1, __type_of(list).count(list, 1)) + assert_equal(2, __type_of(list).count(list, 2)) + assert_equal(0, __type_of(list).count(list, 4)) + + var list2 = List[Int]() + assert_equal(0, __type_of(list2).count(list2, 1)) + + def main(): test_mojo_issue_698() test_list() @@ -703,3 +713,4 @@ def main(): test_constructor_from_pointer() test_constructor_from_other_list_through_pointer() test_converting_list_to_string() + test_list_count() From 1ea64ad9211c2cf8de631617235aaa97a6484d4e Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 1 May 2024 13:33:42 -0700 Subject: [PATCH 0361/2019] [docs] Add post-process to strip docs domain name from built docs (#39053) There are some situations in which we logically include the docsite domain, such as in the CLI man page descriptions because users will see these links outside the docs website. But we don't want these fully-qualified URLs in the generated docs because then they don't work properly in a local docsite build of on a staging website (because the links instead go do docs.modular.com). So this new post-process function finds and removes all instances of "docs.modular.com" in the docs build output. Fixes: [Internal link] Example diff: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: dbee988ab3677d129a3d3a3c708fd210b98bafb5 --- docs/manual/parameters/index.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 463005cad7..1356bf4253 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -517,7 +517,7 @@ "\n", "The Mojo compiler is also smart about type inference with parameters. Note\n", "that the above function is able to call the parametric\n", - "[`sqrt[]()`](https://docs.modular.com/mojo/stdlib/math/math/sqrt) function\n", + "[`sqrt[]()`](/mojo/stdlib/math/math/sqrt) function\n", "without specifying the parameters—the compiler infers its parameters based on\n", "the parametric `x` value passed into it, as if you\n", "wrote `sqrt[dt, width](x)` explicitly. Also note that `rsqrt()` chose to\n", From bb0a2003c633fe973ad8d4896ecb7da57da1e101 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Wed, 1 May 2024 15:27:11 -0600 Subject: [PATCH 0362/2019] [mojo-lsp] Add support for traits in DocumentSymbols (#38988) This treats traits as an "Interface" symbol type. This also required resetting IR values when clearing out trait function bodies (because otherwise ASTDecls carry around garbage state that crashes anything trying to introspect it). Fixes https://github.com/modularml/mojo/issues/2363 MODULAR_ORIG_COMMIT_REV_ID: 2472dad9150eb93185232cfe0e1237cde5fc9b5e --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e0ff7bcede..57eee738a4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -71,3 +71,6 @@ what we publish. `Stringable` trait, you can use `print(my_object)` instead. ### 🛠️ Fixed + +- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on + simple trait definitions. From b406858246a7fcc7463b3d49ad4680b750aaa7a8 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 1 May 2024 17:32:36 -0400 Subject: [PATCH 0363/2019] [******][mojo-stdlib] Move `clamp` from `math` to `simd` (#38932) There is no corresponding python method in `math`, and since the current implementation is not generic, there is no reason for it to live in `math`, especially since `SIMD` already has similar `min` and `max` methods. MODULAR_ORIG_COMMIT_REV_ID: dee81979ae0359fb5b722d2f64b032cd1d8350be --- docs/changelog.md | 3 +++ stdlib/src/builtin/simd.mojo | 18 ++++++++++++++++++ stdlib/test/builtin/test_simd.mojo | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 57eee738a4..99948472aa 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -70,6 +70,9 @@ what we publish. - The method `object.print()` has been removed. Since now, `object` has the `Stringable` trait, you can use `print(my_object)` instead. +- The `math.clamp` function has been removed in favor of a new `SIMD.clamp` + method. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 740bd809a2..be714f0322 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -930,6 +930,24 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ return self._floor_ceil_impl["llvm.ceil"]() + fn clamp(self, lower_bound: Self, upper_bound: Self) -> Self: + """Clamps the values in a SIMD vector to be in a certain range. + + Clamp cuts values in the input SIMD vector off at the upper bound and + lower bound values. For example, SIMD vector `[0, 1, 2, 3]` clamped to + a lower bound of 1 and an upper bound of 2 would return `[1, 1, 2, 2]`. + + Args: + lower_bound: Minimum of the range to clamp to. + upper_bound: Maximum of the range to clamp to. + + Returns: + A new SIMD vector containing x clamped to be within lower_bound and + upper_bound. + """ + + return self.min(upper_bound).max(lower_bound) + # ===-------------------------------------------------------------------===# # In place operations. # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 6f7c3ab77a..66b80875a8 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -827,6 +827,25 @@ def test_abs(): ) +def test_min_max_clamp(): + alias F = SIMD[DType.float32, 4] + + var f = F(-10.5, -5.0, 5.0, 10.0) + assert_equal(f.min(F(-9.0, -6.0, -4.0, 10.5)), F(-10.5, -6.0, -4.0, 10.0)) + assert_equal(f.min(-4.0), F(-10.5, -5.0, -4.0, -4.0)) + assert_equal(f.max(F(-9.0, -6.0, -4.0, 10.5)), F(-9.0, -5.0, 5.0, 10.5)) + assert_equal(f.max(-4.0), F(-4.0, -4.0, 5.0, 10.0)) + assert_equal(f.clamp(-6.0, 5.5), F(-6.0, -5.0, 5.0, 5.5)) + + alias I = SIMD[DType.float32, 4] + var i = I(-10, -5, 5, 10) + assert_equal(i.min(I(-9, -6, -4, 11)), I(-10, -6, -4, 10)) + assert_equal(i.min(-4), I(-10, -5, -4, -4)) + assert_equal(i.max(I(-9, -6, -4, 11)), I(-9, -5, 5, 11)) + assert_equal(i.max(-4), I(-4, -4, 5, 10)) + assert_equal(i.clamp(-7, 4), I(-7, -5, 4, 4)) + + def main(): test_cast() test_simd_variadic() @@ -851,3 +870,4 @@ def main(): test_sub_with_overflow() test_mul_with_overflow() test_abs() + test_min_max_clamp() From a84328eb8a2d5278c8b7cd846631162e9df8860b Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 1 May 2024 17:51:19 -0400 Subject: [PATCH 0364/2019] [mojo-stdlib] Implement `FloatLiteral.__floordiv__` (#39061) The implementation is just a straightforward call to division and floor. Fixes MSTDL-244 MODULAR_ORIG_COMMIT_REV_ID: 2ed9d01a7af8558108e55582cc9c7e563cdda9f6 --- stdlib/src/builtin/float_literal.mojo | 16 +++++++++++++++- stdlib/test/builtin/test_float_literal.mojo | 15 +++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 739454dd68..bb57025392 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -299,7 +299,21 @@ struct FloatLiteral( oper = __mlir_attr.`#kgen` ](self.value, rhs.value) - # TODO - __floordiv__ + @always_inline("nodebug") + fn __floordiv__(self, rhs: Self) -> Self: + """Returns self divided by rhs, rounded down to the nearest integer. + + Constraints: + The element type of the SIMD vector must be numeric. + + Args: + rhs: The value to divide on. + + Returns: + `floor(self / rhs)` value. + """ + return self.__truediv__(rhs).__floor__() + # TODO - maybe __mod__? # TODO - maybe __pow__? diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index fd9fdf18ec..479366c021 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -55,13 +55,16 @@ def test_floor(): def test_division(): - # TODO: https://github.com/modularml/mojo/issues/1787 - # allow this at compile time assert_equal(FloatLiteral(4.4) / 0.5, 8.8) - assert_equal(FloatLiteral(4.4) // 0.5, 8.0) - assert_equal(FloatLiteral(-4.4) // 0.5, -9.0) - assert_equal(FloatLiteral(4.4) // -0.5, -9.0) - assert_equal(FloatLiteral(-4.4) // -0.5, 8.0) + + alias f1 = 4.4 // 0.5 + assert_equal(f1, 8.0) + alias f2 = -4.4 // 0.5 + assert_equal(f2, -9.0) + alias f3 = 4.4 // -0.5 + assert_equal(f3, -9.0) + alias f4 = -4.4 // -0.5 + assert_equal(f4, 8.0) def test_power(): From a9551d59f57943df6e3ea9e4a467f9fa811464aa Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 1 May 2024 17:57:54 -0400 Subject: [PATCH 0365/2019] [******][mojo-stdlib] Remove `math.round_half{up,down}` (#38936) Because they are trivial, but untested, and only used in one place. MODULAR_ORIG_COMMIT_REV_ID: 51563e59314219b60dbf675f124207ec4b727907 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 99948472aa..e339e1fb83 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -73,6 +73,9 @@ what we publish. - The `math.clamp` function has been removed in favor of a new `SIMD.clamp` method. +- The `math.round_half_down` and `math.round_half_up` functions are removed. + These can be trivially implemented using the `ceil` and `floor` functions. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on From 5893f42c4989b0bd8f1c2c390095f1d322447287 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 1 May 2024 18:06:52 -0400 Subject: [PATCH 0366/2019] [mojo-stdlib][Docs] Update changelog with `FloatLiteral.__floordiv__` fix (#39065) MODULAR_ORIG_COMMIT_REV_ID: 58a6ad6ec925fd7cce0569a520bda2ced24927a8 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e339e1fb83..01371cce54 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -80,3 +80,5 @@ what we publish. - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on simple trait definitions. +- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using + `//` on `FloatLiteral` in alias expression. From 54f0047d1389a4c0bf17cdea0bea13ce37f2f8ac Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 1 May 2024 16:15:24 -0600 Subject: [PATCH 0367/2019] [docs] Mention `min` and `max` built-in in changelog (#39030) Like `abs`, mention `min` and `max` were previously in `math` package, but are now built-ins. MODULAR_ORIG_COMMIT_REV_ID: 4049380225b038132c44a7960a7cdd2e3b117032 --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 01371cce54..e0e3715499 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -40,8 +40,8 @@ what we publish. return sqrt(self.x * self.x + self.y * self.y) ``` -- The `abs()` function has also moved from `math` to `builtin`, so you no longer - need to do `from math import abs`. +- The `abs(), min(), and max()` functions have moved from `math` to `builtin`, + so you no longer need to do `from math import abs, min, max`. - Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in the `math` module by implementing the `__floor__()` and `__ceil__()` methods From 6d8f4c3f8f33595932a3876933a9a2dee87b088f Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Wed, 1 May 2024 16:20:39 -0600 Subject: [PATCH 0368/2019] [External] [stdlib] Allow `StringRef` to work with both Int8 and UInt8 (#38989) [External] [stdlib] Allow `StringRef` to work with both Int8 and UInt8 This is related to https://github.com/modularml/mojo/issues/2317 This is similar to https://github.com/modularml/mojo/pull/2448 but for StringRef. With this, users and maintainers can work with both Int8 and UInt8 and will allow a graceful transition. This should also allow users to switch during the next release. Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2454 MODULAR_ORIG_COMMIT_REV_ID: 4ef46b217579ad62ca22defc2d8a31930ef37c5a --- stdlib/src/os/env.mojo | 2 +- stdlib/src/utils/stringref.mojo | 90 ++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index 600df6db0e..821df8e39f 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -72,7 +72,7 @@ fn getenv(name: String, default: String = "") -> String: if not os_is_supported: return default - var ptr = external_call["getenv", DTypePointer[DType.int8]](name._as_ptr()) + var ptr = external_call["getenv", DTypePointer[DType.uint8]](name._as_ptr()) if not ptr: return default return String(StringRef(ptr)) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 46a6e5e6c9..83c955fcb3 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -81,6 +81,8 @@ struct StringRef( """ return self + # TODO: #2317 Drop support for this constructor when we have fully + # transitionned to UInt8 as the main byte type. @always_inline fn __init__(ptr: DTypePointer[DType.int8], len: Int) -> StringRef: """Construct a StringRef value given a (potentially non-0 terminated @@ -88,6 +90,10 @@ struct StringRef( The constructor takes a raw pointer and a length. + Note that you should use the constructor from `DTypePointer[DType.uint8]` instead + as we are now storing the bytes as UInt8. + See https://github.com/modularml/mojo/issues/2317 for more information. + Args: ptr: DTypePointer to the string. len: The length of the string. @@ -98,10 +104,33 @@ struct StringRef( return Self {data: ptr, length: len} + @always_inline + fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> StringRef: + """Construct a StringRef value given a (potentially non-0 terminated + string). + + The constructor takes a raw pointer and a length. + + Args: + ptr: DTypePointer to the string. + len: The length of the string. + + Returns: + Constructed `StringRef` object. + """ + + return Self {data: ptr.bitcast[DType.int8](), length: len} + + # TODO: #2317 Drop support for this constructor when we have fully + # transitionned to UInt8 as the main byte type. @always_inline fn __init__(ptr: UnsafePointer[Int8]) -> StringRef: """Construct a StringRef value given a null-terminated string. + Note that you should use the constructor from `UnsafePointer[UInt8]` instead + as we are now storing the bytes as UInt8. + See https://github.com/modularml/mojo/issues/2317 for more information. + Args: ptr: UnsafePointer to the string. @@ -111,10 +140,29 @@ struct StringRef( return DTypePointer[DType.int8](ptr) + @always_inline + fn __init__(ptr: UnsafePointer[UInt8]) -> StringRef: + """Construct a StringRef value given a null-terminated string. + + Args: + ptr: UnsafePointer to the string. + + Returns: + Constructed `StringRef` object. + """ + + return DTypePointer[DType.uint8](ptr) + + # TODO: #2317 Drop support for this constructor when we have fully + # transitionned to UInt8 as the main byte type. @always_inline fn __init__(ptr: DTypePointer[DType.int8]) -> StringRef: """Construct a StringRef value given a null-terminated string. + Note that you should use the constructor from `DTypePointer[DType.uint8]` instead + as we are now storing the bytes as UInt8. + See https://github.com/modularml/mojo/issues/2317 for more information. + Args: ptr: DTypePointer to the string. @@ -128,15 +176,45 @@ struct StringRef( return StringRef(ptr, len) + @always_inline + fn __init__(ptr: DTypePointer[DType.uint8]) -> StringRef: + """Construct a StringRef value given a null-terminated string. + + Args: + ptr: DTypePointer to the string. + + Returns: + Constructed `StringRef` object. + """ + + var len = 0 + while ptr.load(len): + len += 1 + + return StringRef(ptr.bitcast[DType.int8](), len) + + # TODO: #2317 Drop support for this method when we have fully + # transitionned to UInt8 as the main byte type. @always_inline fn _as_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. + Prefer to use `_as_uint8_ptr()` instead. + Returns: The DTypePointer to the underlying memory. """ return self.data + @always_inline + fn _as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + """Retrieves a pointer to the underlying memory. + + Returns: + The DTypePointer to the underlying memory. + """ + return self.data.bitcast[DType.uint8]() + @always_inline fn __bool__(self) -> Bool: """Checks if the string is empty or not. @@ -268,16 +346,16 @@ struct StringRef( var haystack_str = self._from_start(start) var loc = _memmem( - haystack_str._as_ptr(), + haystack_str._as_uint8_ptr(), len(haystack_str), - substr._as_ptr(), + substr._as_uint8_ptr(), len(substr), ) if not loc: return -1 - return int(loc) - int(self._as_ptr()) + return int(loc) - int(self._as_uint8_ptr()) fn rfind(self, substr: StringRef, start: Int = 0) -> Int: """Finds the offset of the last occurrence of `substr` starting at @@ -301,16 +379,16 @@ struct StringRef( var haystack_str = self._from_start(start) var loc = _memrmem( - haystack_str._as_ptr(), + haystack_str._as_uint8_ptr(), len(haystack_str), - substr._as_ptr(), + substr._as_uint8_ptr(), len(substr), ) if not loc: return -1 - return int(loc) - int(self._as_ptr()) + return int(loc) - int(self._as_uint8_ptr()) fn _from_start(self, start: Int) -> StringRef: """Gets the StringRef pointing to the substring after the specified slice start position. From 0f2b117f9487179356302ecb1e98af7a39f5c5e4 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 1 May 2024 18:37:10 -0400 Subject: [PATCH 0369/2019] [******][mojo-stdlib] Move binary elementwise wrappers out of `math` (#38943) These are not needed in the `math` module, so a new `nn.math` submodule is created, since they are expected by **** with a certain signature. Some other functions are also moved there from `nn.activations`. A few unused imports are also cleaned up along the way. MODULAR_ORIG_COMMIT_REV_ID: c5d9b36d8edf5a61ed81f4a8390baad6946620e1 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e0e3715499..e8460c214f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -76,6 +76,10 @@ what we publish. - The `math.round_half_down` and `math.round_half_up` functions are removed. These can be trivially implemented using the `ceil` and `floor` functions. +- The `add`, `sub`, `mul`, `div`, and `mod` functions have been removed from the + `math` module. Instead, users should rely on the `+`, `-`, `*`, `/`, and `%` + operators, respectively. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on From 82ca5b3a91b8096d6b9026e3a298d4c5309ec260 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 1 May 2024 16:42:20 -0600 Subject: [PATCH 0370/2019] [docs] Add recent OSS contributions to changelog (#39064) Several contributions made it in after the branch cut for MAX 24.3, but the changelog was in a holding state. So, update the changelog now that it's free to claim with post-24.3 changes. MODULAR_ORIG_COMMIT_REV_ID: ec5701466879ed3cf2d59fc832a8979b2c9bba71 --- docs/changelog.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e8460c214f..b0217513c8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -63,6 +63,20 @@ what we publish. return Self(floor(re), floor(im)) ``` +- Add an `InlinedArray` type that works on memory-only types. + Compare with the existing `StaticTuple` type, which is conceptually an array + type, but only worked on `AnyRegType`. + ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) + +- Base64 decoding support has been added. + ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) + +- Add `repr()` function and `Representable` trait. + ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@mikowals](https://github.com/gabrieldemarmiesse)) + +- Add `SIMD.shuffle()` with `StaticIntTuple` mask. + ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) + ### 🦋 Changed ### ❌ Removed From 3ac4d00d3af7c7ed4ba848f8a02d65081ca0fb71 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 1 May 2024 17:13:44 -0600 Subject: [PATCH 0371/2019] [stdlib] Use built-in `abs` function (#38903) Remove the file-local definitions of `_abs` now that `abs` is a built-in function in the open source parts of the standard library. MODULAR_ORIG_COMMIT_REV_ID: 472ee40c9d226d5413035c2cef3989593209c254 --- stdlib/src/builtin/hex.mojo | 7 +------ stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/range.mojo | 7 +------ stdlib/src/builtin/string.mojo | 14 ++------------ stdlib/src/testing/testing.mojo | 10 +++------- 5 files changed, 8 insertions(+), 32 deletions(-) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 97320ce135..672f0269bc 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -57,11 +57,6 @@ fn hex[T: Intable](value: T) -> String: # ===----------------------------------------------------------------------===# -@always_inline -fn _abs(x: SIMD) -> __type_of(x): - return (x > 0).select(x, -x) - - fn _format_int( value: Int64, radix: Int = 10, @@ -188,7 +183,7 @@ fn _try_write_int( @parameter fn neg_digit_value() -> Int64: - return _abs(remaining_int % -radix) + return abs(remaining_int % -radix) process_digits[neg_digit_value]() diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 1e65f60d95..bd1d0aa1a1 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -525,7 +525,7 @@ struct Int( Returns: The absolute value. """ - return -self if self < 0 else self + return self if self > 0 else -self @always_inline("nodebug") fn __ceil__(self) -> Self: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 2b6a628d0c..7d58fff846 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -43,11 +43,6 @@ fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: return (numerator + denominator - 1)._positive_div(denominator) -@always_inline -fn _abs(x: Int) -> Int: - return x if x > 0 else -x - - @always_inline fn _sign(x: Int) -> Int: if x > 0: @@ -187,7 +182,7 @@ struct _StridedRange(Sized, ReversibleRange): # FIXME(#38392) # if (self.step > 0) == (self.start > self.end): # return 0 - return _div_ceil_positive(_abs(self.start - self.end), _abs(self.step)) + return _div_ceil_positive(abs(self.start - self.end), abs(self.step)) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 579d3491c4..d6e0e36bfe 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -30,16 +30,6 @@ from .io import _snprintf # ===----------------------------------------------------------------------===# -@always_inline -fn _abs(x: Int) -> Int: - return x if x > 0 else -x - - -@always_inline -fn _abs(x: SIMD) -> __type_of(x): - return (x > 0).select(x, -x) - - @always_inline fn _ctlz(val: Int) -> Int: return llvm_intrinsic["llvm.ctlz", Int, has_side_effect=False](val, False) @@ -1522,7 +1512,7 @@ fn _calc_initial_buffer_size_int64(n0: UInt64) -> Int: @always_inline fn _calc_initial_buffer_size(n0: Int) -> Int: - var n = _abs(n0) + var n = abs(n0) var sign = 0 if n0 > 0 else 1 alias is_32bit_system = bitwidthof[DType.index]() == 32 @@ -1544,7 +1534,7 @@ fn _calc_initial_buffer_size(n: Float64) -> Int: fn _calc_initial_buffer_size[type: DType](n0: Scalar[type]) -> Int: @parameter if type.is_integral(): - var n = _abs(n0) + var n = abs(n0) var sign = 0 if n0 > 0 else 1 alias is_32bit_system = bitwidthof[DType.index]() == 32 diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index a7b25e7c10..035e02f896 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -27,11 +27,6 @@ from builtin._location import __call_location, _SourceLocation # ===----------------------------------------------------------------------=== # -@always_inline -fn _abs(x: SIMD) -> __type_of(x): - return (x > 0).select(x, -x) - - @always_inline fn _isclose( a: SIMD, @@ -50,7 +45,7 @@ fn _isclose( var atol_vec = SIMD[a.type, a.size](atol) var rtol_vec = SIMD[a.type, a.size](rtol) - var res = _abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b)))) + var res = abs(a - b) <= (atol_vec.max(rtol_vec * abs(a).max(abs(b)))) if not equal_nan: return res @@ -275,7 +270,8 @@ fn assert_almost_equal[ if not almost_equal: var err = str(lhs) + " is not close to " + str( rhs - ) + " with a diff of " + _abs(lhs - rhs) + ) + " with a diff of " + abs(lhs - rhs) + if msg: err += " (" + msg + ")" raise _assert_error(err, __call_location()) From d42f7366c4d40c8662981d8b2c832a63063735ed Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 1 May 2024 19:13:21 -0600 Subject: [PATCH 0372/2019] Internal commit MODULAR_ORIG_COMMIT_REV_ID: ea19b2d71729d72d8fc5554ec5d420e8bacd6660 --- stdlib/src/time/time.mojo | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 0d1d848605..e80fce7c87 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -21,7 +21,6 @@ from time import now from sys import external_call, os_is_linux, os_is_windows -from math import floor from memory import UnsafePointer # ===----------------------------------------------------------------------===# @@ -246,7 +245,7 @@ fn sleep(sec: Float64): sec: The number of seconds to sleep for. """ alias NANOSECONDS_IN_SECOND = 1_000_000_000 - var total_secs = floor(sec) + var total_secs = sec.__floor__() var tv_spec = _CTimeSpec( int(total_secs.cast[DType.index]()), int((sec - total_secs) * NANOSECONDS_IN_SECOND), From f3968a2d86319822b6982450a70de8212bafd0dd Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 1 May 2024 21:11:32 -0700 Subject: [PATCH 0373/2019] [docs] Mojo 24.3 changelog edits (#39106) - Legendary -> Highlights (saving legendary for the really big releases) - Added a few highlights, demoted tuple change to make room - Tweaked formatting for contributors - Removed some surplus headers MODULAR_ORIG_COMMIT_REV_ID: bd82abd605b4fb69ceb95b4d5705d84bf9fb34c6 --- docs/changelog-released.md | 163 ++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 92 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index c7ea7477da..96320b1e01 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -32,7 +32,7 @@ modular update mojo ## v24.3 (2024-05-02) -### 🔥 Legendary +### ✨ Highlights - `AnyPointer` was renamed to [`UnsafePointer`](mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and is now @@ -103,24 +103,20 @@ modular update mojo Note that variadic keyword parameters are not supported yet. -- [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now works with memory-only element - types like `String` and allows you to directly index into it with a parameter - expression. This means you can now simply use `x = tup[1]` like Python - instead of `x = tup.get[1, Int]()`. You can also assign into tuple elements - now as well with `tup[1] = x`. + For more information, see + [Variadic arguments](/mojo/manual/functions#variadic-arguments) in the Mojo + Manual. - ```mojo - var tuple = ("Green", 9.3) - var name = tuple[0] - var value = tuple[1] - ``` +- The `mojo build` and `mojo run` commands now support a `-g` option. This + shorter alias is equivalent to writing `--debug-level full`. This option is + also available in the `mojo debug` command, but is already the default. - Note that because the subscript must be a parameter expression, you can't - iterate through a `Tuple` using an ordinary `for` loop. +- Many new standard library APIs have been filled in, including many community + contributions. Changes are listed in the standard library section. -### Language changes +- The Mojo Manual has a new page on [Types](/mojo/manual/types). -#### ⭐️ New +### Language changes - Certain dunder methods that take indices (`__getitem__()`, `__setitem__()`, and `__refitem__()`) or names @@ -149,10 +145,12 @@ modular update mojo ``` - Mojo now allows users to capture the source location of code and call location - of functions dynamically using the built-in `__source_location()` and + of functions dynamically using the `__source_location()` and `__call_location()` functions. For example: ```mojo + from builtin._location import __call_location + @always_inline fn my_assert(cond: Bool, msg: String): if not cond: @@ -169,10 +167,16 @@ modular update mojo an `@always_inline` function that's called *from* an `@always_inline("nodebug")` function. + This feature is still evolving and for the time being you need to explicitly + import these APIs, as shown above. In the future, these will probably be + built-in functions and not require an import statement. + Neither `__source_location()` nor `__call_location()` work when called in a parameter context. For example: ```mojo + from builtin._location import __call_location + @always_inline fn mystery_location() -> String: var loc = __call_location() @@ -190,31 +194,26 @@ modular update mojo - `pop(index)` for removing an element at a particular index. By default, `List.pop()` removes the last element in the list. - ([PR #2041](https://github.com/modularml/mojo/pull/2041) by - [@LJ-9801](https://github.com/LJ-9801), fixes - [#2017](https://github.com/modularml/mojo/issues/2017).) + ([@LJ-9801](https://github.com/LJ-9801), fixes + [#2017](https://github.com/modularml/mojo/issues/2017)) - `resize(new_size)` for resizing the list without the need to specify an additional value. - ([PR #2140](https://github.com/modularml/mojo/pull/2140) - by [@mikowals](https://github.com/mikowals), fixes - [#2133](https://github.com/modularml/mojo/issues/2133).) + ([@mikowals](https://github.com/mikowals), fixes + [#2133](https://github.com/modularml/mojo/issues/2133)) - `insert(index, value)` for inserting a value at a specified index - into the `List`. ([PR #2148](https://github.com/modularml/mojo/pull/2148) by - [@whym1here](https://github.com/whym1here), fixes - [#2134](https://github.com/modularml/mojo/issues/2134).) + into the `List`. ([@whym1here](https://github.com/whym1here), fixes + [#2134](https://github.com/modularml/mojo/issues/2134)) - A new constructor `List(ptr, size, capacity)` to to avoid needing to do a deep copy of an existing contiguous memory allocation when constructing - a new `List`. ([PR #2182](https://github.com/modularml/mojo/pull/2182) by - [@StandinKP](https://github.com/StandinKP), fixes - [#2170](https://github.com/modularml/mojo/issues/2170).) + a new `List`. ([@StandinKP](https://github.com/StandinKP), fixes + [#2170](https://github.com/modularml/mojo/issues/2170)) - [`Dict`](/mojo/stdlib/collections/dict/Dict) now has a `update()` method to update keys/values from another `Dict`. - ([PR #2085](https://github.com/modularml/mojo/pull/2085) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - [`Set`](/mojo/stdlib/collections/set/Set) now has named methods for set operations: @@ -223,8 +222,7 @@ modular update mojo - `intersection_update()` mapping to `&=` - `update()` mapping to `|=` - ([PR #2214](https://github.com/modularml/mojo/pull/2214) by - [@arvindavoudi](https://github.com/arvindavoudi).) + ([@arvindavoudi](https://github.com/arvindavoudi)) - `Dict`, `List`, and `Set` all conform to the `Boolable` trait. The collections evaluate to `True` if they contain any elements, `False` otherwise: @@ -238,8 +236,7 @@ modular update mojo print("No names to list.") ``` - ([PR #2262](https://github.com/modularml/mojo/pull/2262) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - Added [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) function for creating reversed iterators. Several range types, `List`, and `Dict` now @@ -251,11 +248,9 @@ modular update mojo print(number) ``` - ([PR #2215](https://github.com/modularml/mojo/pull/2215) by - [@helehex](https://github.com/helehex), - [PR #2327](https://github.com/modularml/mojo/pull/2327) by + ([@helehex](https://github.com/helehex) and [@jayzhan211](https://github.com/jayzhan211), contributes towards - [#2325](https://github.com/modularml/mojo/issues/2325).) + [#2325](https://github.com/modularml/mojo/issues/2325)) - [`Optional`](/mojo/stdlib/collections/optional/Optional) now implements `__is__` and `__isnot__` methods so that you can compare an `Optional` with @@ -267,8 +262,22 @@ modular update mojo print(opt.value()[]) ``` - ([PR #2082](https://github.com/modularml/mojo/pull/2082) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now works with memory-only element + types like `String` and allows you to directly index into it with a parameter + expression. This means you can now simply use `x = tup[1]` like Python + instead of `x = tup.get[1, Int]()`. You can also assign into tuple elements + now as well with `tup[1] = x`. + + ```mojo + var tuple = ("Green", 9.3) + var name = tuple[0] + var value = tuple[1] + ``` + + Note that because the subscript must be a parameter expression, you can't + iterate through a `Tuple` using an ordinary `for` loop. - The [`Reference`](/mojo/stdlib/memory/reference/Reference) type has several changes, including: @@ -382,48 +391,40 @@ modular update mojo - [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and `removesuffix()` methods. - ([PR #2038](https://github.com/modularml/mojo/pull/2038) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - The [`ord`](/mojo/stdlib/builtin/string/ord) and [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to accept any Unicode character. - ([PR #2149](https://github.com/modularml/mojo/pull/2149) by - [@mzaks](https://github.com/mzaks), contributes towards - [#1616](https://github.com/modularml/mojo/issues/1616).) + ([@mzaks](https://github.com/mzaks), contributes towards + [#1616](https://github.com/modularml/mojo/issues/1616)) - [`Atomic`](/mojo/stdlib/os/atomic/Atomic) is now movable. - ([PR #2088](https://github.com/modularml/mojo/pull/2088) by - [@StandinKP](https://github.com/StandinKP), fixes - [#2026](https://github.com/modularml/mojo/issues/2026).) + ([@StandinKP](https://github.com/StandinKP), fixes + [#2026](https://github.com/modularml/mojo/issues/2026)) - [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The `atol()`function is used internally by `String.__int__()`, so `int(String( " 10 "))` now returns `10` instead of raising an error. - ([PR #2225](https://github.com/modularml/mojo/pull/2225) by - [@artemiogr97](https://github.com/artemiogr97).) + ([@artemiogr97](https://github.com/artemiogr97)) - [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) now implements the `__rmod__()` - method. ([PR #2186](https://github.com/modularml/mojo/pull/2186) by - [@bgreni](https://github.com/bgreni), fixes - [#1482](https://github.com/modularml/mojo/issues/1482).) + method. ([@bgreni](https://github.com/bgreni), fixes + [#1482](https://github.com/modularml/mojo/issues/1482)) - [`bool(None)`](/mojo/stdlib/builtin/bool/bool-function) is now implemented. - ([PR #2249](https://github.com/modularml/mojo/pull/2249) by - [@zhoujingya](https://github.com/zhoujingya).) + ([@zhoujingya](https://github.com/zhoujingya)) - The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) type now implements `gather()` for gathering a `SIMD` vector from offsets of a current pointer. Similarly, support for `scatter()` was added to scatter a `SIMD` vector into offsets of the current pointer. - ([PR #2268](https://github.com/modularml/mojo/pull/2268) by - [@leandrolcampos](https://github.com/leandrolcampos).) + ([@leandrolcampos](https://github.com/leandrolcampos)) - The [`len()`](/mojo/stdlib/builtin/len/len) function now handles a [`range()`](/mojo/stdlib/builtin/range/range) specified with a negative end value, so that things like `len(range(-1))` work correctly. - ([PR #2204](https://github.com/modularml/mojo/pull/2204) by - [@soraros](https://github.com/soraros).) + ([@soraros](https://github.com/soraros)) - [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now prints its location (filename, line, and column where it was called) in its error @@ -434,8 +435,7 @@ modular update mojo - The [`testing.assert_equal[SIMD]()`](/mojo/stdlib/testing/testing/assert_equal) function now raises if any of the elements mismatch in the two `SIMD` arguments being compared. - ([PR #2279](https://github.com/modularml/mojo/pull/2279) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - The [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) and [`math.isclose()`](/mojo/stdlib/math/math/isclose) functions now have an @@ -444,10 +444,8 @@ modular update mojo - The [`object`](/mojo/stdlib/builtin/object/object) type now supports the division, modulo, and left and right shift operators, including the in-place and reverse variants. - ([PR #2230](https://github.com/modularml/mojo/pull/2230), - [PR #2247](https://github.com/modularml/mojo/pull/2247) by - [@LJ-9801](https://github.com/LJ-9801), fixes - [#2224](https://github.com/modularml/mojo/issues/2224).) + ([@LJ-9801](https://github.com/LJ-9801), fixes + [#2224](https://github.com/modularml/mojo/issues/2224)) - Added checked arithmetic operations for `SIMD` integers. @@ -474,13 +472,11 @@ modular update mojo print(product[i]) ``` - ([PR #2138](https://github.com/modularml/mojo/pull/2138) by - [@lsh](https://github.com/lsh).) + ([@lsh](https://github.com/lsh)) - Added [`os.remove()`](/mojo/stdlib/os/os/remove) and [`os.unlink()`](/mojo/stdlib/os/os/unlink) for deleting files. - ([PR #2310](https://github.com/modularml/mojo/pull/2310) by - [@artemiogr97](https://github.com/artemiogr97), fixes + ([@artemiogr97](https://github.com/artemiogr97), fixes [#2306](https://github.com/modularml/mojo/issues/2306)) #### 🦋 Changed @@ -491,7 +487,6 @@ modular update mojo - [`Optional.value()`](/mojo/stdlib/collections/optional/Optional#value) now returns a reference instead of a copy of the contained value. - ([PR #2226](https://github.com/modularml/mojo/pull/2226)) To perform a copy manually, dereference the result: @@ -501,38 +496,23 @@ modular update mojo var value = result.value()[] ``` - ([PR #2226](https://github.com/modularml/mojo/pull/2226) by - [@lsh](https://github.com/lsh), fixes - [#2179](https://github.com/modularml/mojo/issues/2179).) + ([@lsh](https://github.com/lsh), fixes + [#2179](https://github.com/modularml/mojo/issues/2179)) - Per the accepted community proposal, [Standardize the representation of byte sequence as a sequence of unsigned 8-bit integers](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), began transition to using `UInt8` by changing the data pointer of `Error` to `DTypePointer[DType.uint8]`. - ([PR #2318](https://github.com/modularml/mojo/pull/2318) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes - towards [#2317](https://github.com/modularml/mojo/issues/2317).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), contributes + towards [#2317](https://github.com/modularml/mojo/issues/2317)) - Continued transition to `UnsafePointer` from the legacy `Pointer` type in various standard library APIs and internals. - ([PR #2365](https://github.com/modularml/mojo/pull/2365), - [PR #2367](https://github.com/modularml/mojo/pull/2367), - [PR #2368](https://github.com/modularml/mojo/pull/2368), - [PR #2370](https://github.com/modularml/mojo/pull/2370), - [PR #2371](https://github.com/modularml/mojo/pull/2371) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse).) + ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) ### Tooling changes -#### ⭐️ New - -- The `mojo build` and `mojo run` commands now support a `-g` option. This - shorter alias is equivalent to writing `--debug-level full`. This option is - also available in the `mojo debug` command, but is already the default. - -#### 🦋 Changed - - The behavior of `mojo build` when invoked without an output `-o` argument has changed slightly: `mojo build ./test-dir/program.mojo` now outputs an executable to the path `./program`, whereas before it would output to the path @@ -648,8 +628,7 @@ modular update mojo - [#2068](https://github.com/modularml/mojo/issues/2068) Fix `SIMD.reduce()` for size_out == 2. - ([PR #2102](https://github.com/modularml/mojo/pull/2102) by - [@soraros](https://github.com/soraros).) + ([@soraros](https://github.com/soraros)) ## v24.2.1 (2024-04-11) From 09128b272f3262b6d278c4de0c167a99c9de2c7c Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 2 May 2024 15:23:27 +0800 Subject: [PATCH 0374/2019] [******] Use reversed(range(...)) to visit range in reverse, NFC (#39115) This uses `reversed(range(x))` instead of `range(x-1,-1,-1)` to iterate in reverse order. This better conveys the intent of the traversal and makes it more literate. MODULAR_ORIG_COMMIT_REV_ID: 8f3549642f7ad16ee584829b1b1bb3b1f47069b8 --- stdlib/src/utils/stringref.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 83c955fcb3..961fd973f9 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -532,7 +532,7 @@ fn _memrchr[ ]: if not len: return DTypePointer[type]() - for i in range(len - 1, -1, -1): + for i in reversed(range(len)): if source[i] == char: return source + i return DTypePointer[type]() From cb65264116010ad900df61da376e34eb0f8db95f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 2 May 2024 10:51:13 -0400 Subject: [PATCH 0375/2019] [mojo-stdlib] Abort when using Python interop without `MOJO_PYTHON_LIBRARY` set (#39130) Currently, we just print but continue as normal, which can lead to segfaults and other ungraceful errors. This patch changes the logic so that we abort. This might not be the ideal solution, but at least it will guarantee a deterministic and early failure while we figure out a better solution. Fixes MSTDL-380 MODULAR_ORIG_COMMIT_REV_ID: 47c111f37e6fbce2141db1b88cdcf0c7acc1e335 --- stdlib/src/python/_cpython.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index acea0bec1c..2f347b51bb 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -164,7 +164,7 @@ struct CPython: print("CPython init") var python_lib = getenv("MOJO_PYTHON_LIBRARY") if python_lib == "": - print( + abort( "Mojo/Python interoperability error: Unable to locate a" " suitable libpython, please set `MOJO_PYTHON_LIBRARY`" ) From a8de6d63bf6f32a0e4235856d714c7f25e49b4df Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 2 May 2024 11:20:51 -0400 Subject: [PATCH 0376/2019] [******][mojo-stdlib] Move `roundeven` from `math` to `SIMD` (#38961) This method is currently not generic, and since it doesn't appear in python's `math` module, we shouldn't have it there. MODULAR_ORIG_COMMIT_REV_ID: 421b716cd344799af8627fa57b86552539ed71ab --- docs/changelog.md | 3 +++ stdlib/src/builtin/simd.mojo | 15 +++++++++++++++ stdlib/test/builtin/test_simd.mojo | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b0217513c8..7cf031ed75 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -94,6 +94,9 @@ what we publish. `math` module. Instead, users should rely on the `+`, `-`, `*`, `/`, and `%` operators, respectively. +- The `math.roundeven` function has been removed from the `math` module. The new + `SIMD.roundeven` method now provides the identical functionality. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index be714f0322..5c163217c7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -948,6 +948,21 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return self.min(upper_bound).max(lower_bound) + @always_inline("nodebug") + fn roundeven(self) -> Self: + """Performs elementwise banker's rounding on the elements of a SIMD + vector. + + This rounding goes to the nearest integer with ties toward the nearest + even integer. + + Returns: + The elementwise banker's rounding of this SIMD vector. + """ + return llvm_intrinsic[ + "llvm.roundeven", __type_of(self), has_side_effect=False + ](self) + # ===-------------------------------------------------------------------===# # In place operations. # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 66b80875a8..7eb03c6a53 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -192,6 +192,14 @@ def test_floor(): assert_equal(B.__floor__(b), b) +def test_roundeven(): + assert_equal(Float32(2.5).roundeven(), 2.0) + assert_equal(Float32(-3.5).roundeven(), -4.0) + + alias F = SIMD[DType.float32, 4] + assert_equal(F(1.5, 2.5, -2.5, -3.5).roundeven(), F(2.0, 2.0, -2.0, -4.0)) + + def test_floordiv(): assert_equal(Int32(2) // Int32(2), 1) assert_equal(Int32(2) // Int32(3), 0) @@ -854,6 +862,7 @@ def main(): test_truthy() test_ceil() test_floor() + test_roundeven() test_floordiv() test_mod() test_rotate() From 93148c87ec6fd0b1358872be8ab7533910b9e203 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 2 May 2024 11:48:02 -0400 Subject: [PATCH 0377/2019] [mojo-stdlib] Rename `sys._exit` to `sys.terminate` (#39136) This should enable documentation to be generated for the `exit` functions. Fixes MSTDL-351 MODULAR_ORIG_COMMIT_REV_ID: 0a382413c01500278599dee94e0b7bda9ea6d5e4 --- stdlib/src/sys/__init__.mojo | 2 +- stdlib/src/sys/{_exit.mojo => terminate.mojo} | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) rename stdlib/src/sys/{_exit.mojo => terminate.mojo} (74%) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 53d70901cd..c82137af02 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -14,7 +14,6 @@ from .arg import argv from .debug import breakpointhook -from ._exit import exit from .ffi import RTLD, DEFAULT_RTLD, DLHandle, external_call from .info import ( is_x86, @@ -69,3 +68,4 @@ from .param_env import ( env_get_int, env_get_string, ) +from .terminate import exit diff --git a/stdlib/src/sys/_exit.mojo b/stdlib/src/sys/terminate.mojo similarity index 74% rename from stdlib/src/sys/_exit.mojo rename to stdlib/src/sys/terminate.mojo index 319263f6fd..7b80e91429 100644 --- a/stdlib/src/sys/_exit.mojo +++ b/stdlib/src/sys/terminate.mojo @@ -17,20 +17,20 @@ from .ffi import external_call fn exit(): - """Exist from Mojo. Unlike the Python implementation this does not raise - an exception to exit. + """Exits from Mojo. Unlike the Python implementation this does not raise an + exception to exit. """ exit(0) fn exit[intable: Intable](code: intable): - """Exist from Mojo. Unlike the Python implementation this does not raise - an exception to exit. + """Exits from Mojo. Unlike the Python implementation this does not raise an + exception to exit. - Params: - intable: The type of the code. + Parameters: + intable: The type of the exit code. Args: - code: The exit code. + code: The exit code. """ external_call["exit", NoneType](Int32(int(code))) From fa87c6edd86840d3495641f497b19004c3ecb44d Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 2 May 2024 12:15:23 -0400 Subject: [PATCH 0378/2019] [mojo-stdlib] Move `round` to `builtin` and implement it generically (#38984) A new `builtin.round` module is introduced, also containing a `Roundable` trait. The implementation for `SIMD`, `Int`, and `IntLiteral are also added. The implementation for `FloatLiteral` will be done in a subsequent patch. MODULAR_ORIG_COMMIT_REV_ID: d0d2e7c444a81163dd4db6310385838d386183fe --- docs/changelog.md | 24 ++++++--- stdlib/src/builtin/_math.mojo | 24 ++++----- stdlib/src/builtin/abs.mojo | 2 + stdlib/src/builtin/float_literal.mojo | 2 + stdlib/src/builtin/int.mojo | 10 ++++ stdlib/src/builtin/int_literal.mojo | 10 ++++ stdlib/src/builtin/round.mojo | 58 +++++++++++++++++++++ stdlib/src/builtin/simd.mojo | 14 +++++ stdlib/test/builtin/test_float_literal.mojo | 15 ++++++ stdlib/test/builtin/test_int.mojo | 7 +++ stdlib/test/builtin/test_int_literal.mojo | 7 +++ stdlib/test/builtin/test_simd.mojo | 9 ++++ 12 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 stdlib/src/builtin/round.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 7cf031ed75..df1bcc5e48 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -26,22 +26,27 @@ what we publish. [@artemiogr97](https://github.com/artemiogr97), fixes [#2274](https://github.com/modularml/mojo/issues/2274)) -- Mojo now allows types to opt in to use the `abs()` function by implementing - the `__abs__()` method, defined by the new `Absable`: +- Mojo now supports types to opt in to use the `abs` and `round` functions by + implementing the `__abs__` and `__round__` methods (i.e. by conforming to the + new `Absable` and `Roundable` traits), respectively, e.g.: ```mojo from math import sqrt - struct Point(Absable): - var x: Float64 - var y: Float64 + @value + struct Complex(Absable, Roundable): + var re: Float64 + var im: Float64 fn __abs__(self) -> Self: - return sqrt(self.x * self.x + self.y * self.y) + return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) + + fn __round__(self) -> Self: + return Self(round(self.re), round(self.im)) ``` -- The `abs(), min(), and max()` functions have moved from `math` to `builtin`, - so you no longer need to do `from math import abs, min, max`. +- The `abs, round, min, and max` functions have moved from `math` to `builtin`, + so you no longer need to do `from math import abs, round, min, max`. - Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in the `math` module by implementing the `__floor__()` and `__ceil__()` methods @@ -79,6 +84,9 @@ what we publish. ### 🦋 Changed +- The `abs` and `round` functions have moved from `math` to `builtin`, so you no + longer need to do `from math import abs, round`. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 8480dc8c7e..89c3d32877 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -24,22 +24,22 @@ module should be eventually moved to the `math` module when it's open sourced. trait Ceilable: """ - The `Ceilable` trait describes a type that defines a ceiling operation. + The `Ceilable` trait describes a type that defines a ceiling operation. - Types that conform to `Ceilable` will work with the builtin `ceil` - function. The ceiling operation always returns the same type as the input. + Types that conform to `Ceilable` will work with the builtin `ceil` + function. The ceiling operation always returns the same type as the input. - For example: - ```mojo - from math import Ceilable, ceil + For example: + ```mojo + from math import Ceilable, ceil - @value - struct Complex(Ceilable): - var re: Float64 - var im: Float64 + @value + struct Complex(Ceilable): + var re: Float64 + var im: Float64 - fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) ``` """ diff --git a/stdlib/src/builtin/abs.mojo b/stdlib/src/builtin/abs.mojo index 5c77e570a4..e6e8030b81 100644 --- a/stdlib/src/builtin/abs.mojo +++ b/stdlib/src/builtin/abs.mojo @@ -35,6 +35,8 @@ trait Absable: ``` """ + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. fn __abs__(self) -> Self: ... diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index bb57025392..19d663afbc 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -238,6 +238,8 @@ struct FloatLiteral( return truncated return truncated + 1 + # TODO: implement __round__ + # ===------------------------------------------------------------------===# # Arithmetic Operators # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index bd1d0aa1a1..f8e0470f3b 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -201,6 +201,7 @@ struct Int( Formattable, Intable, KeyElement, + Roundable, Stringable, ): """This type represents an integer value.""" @@ -545,6 +546,15 @@ struct Int( """ return self + @always_inline("nodebug") + fn __round__(self) -> Self: + """Return the rounded value of the Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + @always_inline("nodebug") fn __invert__(self) -> Int: """Return ~self. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 44734d2d67..6a22ebfcb7 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -25,6 +25,7 @@ struct IntLiteral( EqualityComparable, Floorable, Intable, + Roundable, Stringable, ): """This type represents a static integer literal value with @@ -268,6 +269,15 @@ struct IntLiteral( """ return self + @always_inline("nodebug") + fn __round__(self) -> Self: + """Return the rounded value of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + @always_inline("nodebug") fn __invert__(self) -> Self: """Return ~self. diff --git a/stdlib/src/builtin/round.mojo b/stdlib/src/builtin/round.mojo new file mode 100644 index 0000000000..6193d76cff --- /dev/null +++ b/stdlib/src/builtin/round.mojo @@ -0,0 +1,58 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provides the `round` function. + +These are Mojo built-ins, so you don't need to import them. +""" + + +trait Roundable: + """ + The `Roundable` trait describes a type that defines an rounded value + operation. + + Types that conform to `Roundable` will work with the builtin `round` + function. The round operation always returns the same type as the input. + + For example: + ```mojo + @value + struct Complex(Roundable): + var re: Float64 + var im: Float64 + + fn __round__(self) -> Self: + return Self(round(re), round(im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __round__(self) -> Self: + ... + + +@always_inline +fn round[T: Roundable](value: T) -> T: + """Get the rounded value of the given object. + + Parameters: + T: The type conforming to Roundable. + + Args: + value: The object to get the rounded value of. + + Returns: + The rounded value of the object. + """ + return value.__round__() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 5c163217c7..2643dd0f0a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -121,6 +121,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Floorable, Hashable, Intable, + Roundable, Sized, Stringable, ): @@ -963,6 +964,19 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "llvm.roundeven", __type_of(self), has_side_effect=False ](self) + @always_inline("nodebug") + fn __round__(self) -> Self: + """Performs elementwise rounding on the elements of a SIMD vector. + + This rounding goes to the nearest integer with ties away from zero. + + Returns: + The elementwise rounded value of this SIMD vector. + """ + return llvm_intrinsic[ + "llvm.round", __type_of(self), has_side_effect=False + ](self) + # ===-------------------------------------------------------------------===# # In place operations. # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 479366c021..1974eb0301 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -54,6 +54,19 @@ def test_floor(): assert_equal(FloatLiteral.__floor__(neg_inf), neg_inf) +fn round10(x: Float64) -> Float64: + # TODO: implement __div__ on FloatLiteral? + return (round(Float64(x * 10)) / 10).value + + +def test_round10(): + assert_equal(round10(FloatLiteral(4.4) % 0.5), 0.4) + assert_equal(round10(FloatLiteral(-4.4) % 0.5), 0.1) + assert_equal(round10(FloatLiteral(4.4) % -0.5), -0.1) + assert_equal(round10(FloatLiteral(-4.4) % -0.5), -0.4) + assert_equal(round10(FloatLiteral(3.1) % 1.0), 0.1) + + def test_division(): assert_equal(FloatLiteral(4.4) / 0.5, 8.8) @@ -123,7 +136,9 @@ def test_abs(): def main(): + test_ceil() test_floor() + test_round10() test_division() test_power() test_int_conversion() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 25d3ad8a72..1457d402a5 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -61,6 +61,12 @@ def test_floor(): assert_equal(Int.__floor__(Int(-5)), -5) +def test_round(): + assert_equal(Int.__round__(Int(5)), 5) + assert_equal(Int.__round__(Int(0)), 0) + assert_equal(Int.__round__(Int(-5)), -5) + + def test_floordiv(): assert_equal(1, Int(2) // Int(2)) assert_equal(0, Int(2) // Int(3)) @@ -112,6 +118,7 @@ def main(): test_pow() test_ceil() test_floor() + test_round() test_floordiv() test_mod() test_abs() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index edb4c531b7..5c74aab764 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -34,6 +34,12 @@ def test_floor(): assert_equal(IntLiteral.__floor__(-5), -5) +def test_round(): + assert_equal(IntLiteral.__round__(5), 5) + assert_equal(IntLiteral.__round__(0), 0) + assert_equal(IntLiteral.__round__(-5), -5) + + def test_floordiv(): assert_equal(2 // 2, 1) assert_equal(2 // 3, 0) @@ -70,6 +76,7 @@ def main(): test_int() test_ceil() test_floor() + test_round() test_floordiv() test_mod() test_bit_width() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 7eb03c6a53..0f84102524 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -192,6 +192,14 @@ def test_floor(): assert_equal(B.__floor__(b), b) +def test_round(): + assert_equal(Float32.__round__(Float32(2.5)), 3.0) + assert_equal(Float32.__round__(Float32(-3.5)), -4.0) + + alias F = SIMD[DType.float32, 4] + assert_equal(F.__round__(F(1.5, 2.5, -2.5, -3.5)), F(2.0, 3.0, -3.0, -4.0)) + + def test_roundeven(): assert_equal(Float32(2.5).roundeven(), 2.0) assert_equal(Float32(-3.5).roundeven(), -4.0) @@ -862,6 +870,7 @@ def main(): test_truthy() test_ceil() test_floor() + test_round() test_roundeven() test_floordiv() test_mod() From 0b5cd04b19008dae61ce8d47bb33f93ef1441335 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 2 May 2024 10:06:07 -0700 Subject: [PATCH 0379/2019] [Docs] Remove note about Atomic from 24.3 changelog. Related to MOCO-564. (#39155) MODULAR_ORIG_COMMIT_REV_ID: 686f9578c2290e0103a6a43dc90cd4c39c75d2c2 --- docs/changelog-released.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 96320b1e01..59085c0b5e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -399,10 +399,6 @@ modular update mojo ([@mzaks](https://github.com/mzaks), contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) -- [`Atomic`](/mojo/stdlib/os/atomic/Atomic) is now movable. - ([@StandinKP](https://github.com/StandinKP), fixes - [#2026](https://github.com/modularml/mojo/issues/2026)) - - [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The `atol()`function is used internally by `String.__int__()`, so `int(String( " 10 "))` now returns `10` instead of raising an error. From 51cf7433d0910e2ee04e26a4a7596065eca44cfe Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 2 May 2024 11:15:00 -0700 Subject: [PATCH 0380/2019] [mojo-stdlib] Move more register passable types off `-> Self` inits. (#39168) This modernizes a random collection of register passable types to stop using legacy `-> Self` initializer syntax. NFC. MODULAR_ORIG_COMMIT_REV_ID: f90dbe21a089dfe55a2941f642461359a524d4b5 --- stdlib/src/builtin/builtin_slice.mojo | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index b6da005a8f..ccb8ec01f2 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -63,22 +63,21 @@ struct Slice(Sized, Stringable, EqualityComparable): """The step increment value of the slice.""" @always_inline("nodebug") - fn __init__(start: Int, end: Int) -> Self: + fn __init__(inout self, start: Int, end: Int): """Construct slice given the start and end values. Args: start: The start value. end: The end value. - - Returns: - The constructed slice. """ - return Self {start: start, end: end, step: 1} + self.start = start + self.end = end + self.step = 1 @always_inline("nodebug") fn __init__[ T0: AnyRegType, T1: AnyRegType, T2: AnyRegType - ](start: T0, end: T1, step: T2) -> Self: + ](inout self, start: T0, end: T1, step: T2): """Construct slice given the start, end and step values. Parameters: @@ -90,15 +89,10 @@ struct Slice(Sized, Stringable, EqualityComparable): start: The start value. end: The end value. step: The step value. - - Returns: - The constructed slice. """ - return Self { - start: _default_or(start, 0), - end: _default_or(end, _int_max_value()), - step: _default_or(step, 1), - } + self.start = _default_or(start, 0) + self.end = _default_or(end, _int_max_value()) + self.step = _default_or(step, 1) fn __str__(self) -> String: """Gets the string representation of the span. From 69ba6c351b316a09a9254964cda3de987437d052 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 2 May 2024 11:43:04 -0700 Subject: [PATCH 0381/2019] [docs] Fix broken links (#39176) MODULAR_ORIG_COMMIT_REV_ID: 04b3c94002b5e6dc461426405dbe7469783a55c7 --- docs/changelog-released.md | 38 ++++++++++++++++---------------- docs/manual/functions.ipynb | 2 +- docs/manual/lifecycle/life.ipynb | 2 +- docs/manual/types.ipynb | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 59085c0b5e..f660f85df4 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -35,7 +35,7 @@ modular update mojo ### ✨ Highlights - `AnyPointer` was renamed to - [`UnsafePointer`](mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and is now + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and is now Mojo's preferred unsafe pointer type. It has several enhancements, including: - The element type can now be any type: it doesn't require `Movable`. @@ -990,7 +990,7 @@ fixed in a future release. - The [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload that worked on [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) types has been removed in favor of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer) and - [`DTypePointer`](/mojo/stdlib/memory/unsafe/dtypepointer): + [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): ```mojo # Doesn't work @@ -1119,7 +1119,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - A new version of the [Mojo Playground](/mojo/playground) is available. The new playground is a simple interactive editor for Mojo code, similar to the Rust Playground or Go Playground. The old - [JupyterLab based playground](https://playground.modular.com) will remain + JupyterLab based playground will remain online until March 20th. - The Mojo LSP server will now generate fixits for populating empty @@ -1184,7 +1184,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - [`DynamicVector`](/mojo/stdlib/collections/list/List) now supports iteration. Iteration values are instances of - [Reference](/mojo/stdlib/memory/unsafe/Reference) and require dereferencing: + [Reference](/mojo/stdlib/memory/reference/Reference) and require dereferencing: ```mojo var v: DynamicVector[String]() @@ -1269,10 +1269,10 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `UnusualSlice` constructor. - The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/unsafe/reference) instead of having to + [`Reference`](/mojo/stdlib/memory/reference/reference) instead of having to return an MLIR internal reference type. -- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer/AnyPointer#move_into) +- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) method, for moving a value from one pointer memory location to another. - Added built-in [`hex()`](/mojo/stdlib/builtin/hex/hex) function, which can be @@ -1467,17 +1467,17 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `Int`. - [`Variant.get[T]()`](/mojo/stdlib/utils/variant/Variant#get) now returns a - [`Reference`](/mojo/stdlib/memory/unsafe/reference) to the value rather than a - copy. +[`Reference`](/mojo/stdlib/memory/reference/Reference) to the value rather than +a copy. - The [`String`](/mojo/stdlib/builtin/string/String) methods `tolower()` and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. - We originally thought about using those as language sugar for references, but - we believe that generic language features combined with the - [`Reference`](/mojo/stdlib/memory/unsafe/reference) type will provide a good - experience without dedicated sugar. +We originally thought about using those as language sugar for references, but +we believe that generic language features combined with the +[`Reference`](/mojo/stdlib/memory/reference/Reference) type will provide a good +experience without dedicated sugar. ### 🛠️ Fixed @@ -1643,11 +1643,11 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. a `VariadicListMem` returned a low-level pointer, which required the user to call `__get_address_as_lvalue()` to access the element.) - Note that subscripting the variadic list works nicely as above, but - iterating over the variadic list directly with a `for` loop produces a - [`Reference`](/mojo/stdlib/memory/unsafe/reference) (described below) instead - of the desired value, so an extra subscript is required; We intend to fix this - in the future. + Note that subscripting the variadic list works nicely as above, but iterating + over the variadic list directly with a `for` loop produces a + [`Reference`](/mojo/stdlib/memory/reference/Reference) (described below) + instead of the desired value, so an extra subscript is required; We intend to + fix this in the future. ```mojo fn make_worldly(inout *strs: String): @@ -1671,7 +1671,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - Mojo now has a prototype version of a safe - [`Reference`](/mojo/stdlib/memory/unsafe/reference) type. The compiler's + [`Reference`](/mojo/stdlib/memory/reference/Reference) type. The compiler's lifetime tracking pass can reason about references to safely extend local variable lifetime, and check indirect access safety. The `Reference` type is brand new (and currently has no syntactic sugar) so it must be explicitly @@ -2299,7 +2299,7 @@ the previous "read to EOF" behavior when size is negative. ``` - Subscripting added to - [`DTypePointer`](/mojo/stdlib/memory/unsafe/dtypepointer) and + [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) and [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer): ```mojo diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 52922ab672..dd5eda877e 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -380,7 +380,7 @@ "source": [ "\n", "Memory-only types, such as `String`, are available as a \n", - "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem).\n", + "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", "[`Reference`](/mojo/stdlib/memory/unsafe/reference) for each value instead\n", "of the value itself. You must add an empty subscript operator `[]` to\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index bbfa43b310..9421e6f3b3 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -28,7 +28,7 @@ "destroyed](/mojo/manual/lifecycle/death.html).)\n", "\n", "All data types in Mojo—including basic types in the standard library such as\n", - "[`Bool`](/mojo/stdlib/builtin/bool/bool),\n", + "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 4b03fe549f..fc2da0f793 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -541,7 +541,7 @@ "\n", "The collection types are _generic types_: while a given collection can only\n", "hold a specific type of value (such as `Int` or `Float64`), you specify the\n", - "type at compile time using a [parameter]((/mojo/manual/parameters/)). For\n", + "type at compile time using a [parameter](/mojo/manual/parameters/). For\n", "example, you can create a `List` of `Int` values like this:" ] }, From ae1362d08c8ddd127f8a8143844c510c534ba0e5 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 2 May 2024 12:21:51 -0700 Subject: [PATCH 0382/2019] [docs] Fix broken/redirecting links in mojodocs (#39178) Technically, URLs are case sensitive. Although our server still resolves urls with case insensitivity, it technically redirects and briefly fails which our linkchecker receives as a 404 (you can also see a flicker in the browser before getting the page) MODULAR_ORIG_COMMIT_REV_ID: b375ec2886fb4989b3013661c19cd2f6a3a033cb --- stdlib/src/builtin/int.mojo | 4 ++-- stdlib/src/builtin/len.mojo | 6 +++--- stdlib/src/builtin/str.mojo | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index f8e0470f3b..7e03ffe811 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -37,7 +37,7 @@ trait Intable: """The `Intable` trait describes a type that can be converted to an Int. Any type that conforms to `Intable` or - [`IntableRaising`](/mojo/stdlib/builtin/int/intableraising) works with + [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) works with the built-in [`int()`](/mojo/stdlib/builtin/int/int-function) function. This trait requires the type to implement the `__int__()` method. For @@ -83,7 +83,7 @@ trait IntableRaising: The `IntableRaising` trait describes a type can be converted to an Int, but the conversion might raise an error. - Any type that conforms to [`Intable`](/mojo/stdlib/builtin/int/intable) + Any type that conforms to [`Intable`](/mojo/stdlib/builtin/int/Intable) or `IntableRaising` works with the built-in [`int()`](/mojo/stdlib/builtin/int/int-function) function. diff --git a/stdlib/src/builtin/len.mojo b/stdlib/src/builtin/len.mojo index 887c907c13..adebf760bd 100644 --- a/stdlib/src/builtin/len.mojo +++ b/stdlib/src/builtin/len.mojo @@ -25,7 +25,7 @@ trait Sized: string or array). Any type that conforms to `Sized` or - [`SizedRaising`](/mojo/stdlib/builtin/len/sizedraising) works with the + [`SizedRaising`](/mojo/stdlib/builtin/len/SizedRaising) works with the built-in [`len()`](/mojo/stdlib/builtin/len/len) function. The `Sized` trait requires a type to implement the `__len__()` @@ -53,7 +53,7 @@ trait Sized: ``` **Note:** If the `__len__()` method can raise an error, use the - [`SizedRaising`](/mojo/stdlib/builtin/len/sizedraising) trait instead. + [`SizedRaising`](/mojo/stdlib/builtin/len/SizedRaising) trait instead. """ @@ -70,7 +70,7 @@ trait SizedRaising: """The `SizedRaising` trait describes a type that has an integer length, which might raise an error if the length can't be determined. - Any type that conforms to [`Sized`](/mojo/stdlib/builtin/len/sized) or + Any type that conforms to [`Sized`](/mojo/stdlib/builtin/len/Sized) or `SizedRaising` works with the built-in [`len()`](/mojo/stdlib/builtin/len/len) function. diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index 5bc2ff8a85..2bd46cd108 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -23,12 +23,12 @@ These are Mojo built-ins, so you don't need to import them. trait Stringable: """ The `Stringable` trait describes a type that can be converted to a - [`String`](https://docs.modular.com/mojo/stdlib/builtin/string.html). + [`String`](/mojo/stdlib/builtin/string/String). Any type that conforms to `Stringable` or - [`StringableRaising`](/mojo/stdlib/builtin/str/stringableraising) works + [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) works with the built-in [`print()`](/mojo/stdlib/builtin/io/print) and - [`str()`](/mojo/stdlib/builtin/str.html) functions. + [`str()`](/mojo/stdlib/builtin/str/str) functions. The `Stringable` trait requires the type to define the `__str__()` method. For example: @@ -55,7 +55,7 @@ trait Stringable: ``` **Note:** If the `__str__()` method might raise an error, use the - [`StringableRaising`](/mojo/stdlib/builtin/str/stringableraising) + [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) trait, instead. About the difference between `__repr__()` and `__str__()`: @@ -77,10 +77,10 @@ trait Stringable: trait StringableRaising: """The StringableRaising trait describes a type that can be converted to a - [`String`](https://docs.modular.com/mojo/stdlib/builtin/string.html). + [`String`](/mojo/stdlib/builtin/string/String). Any type that conforms to - [`Stringable`](/mojo/stdlib/builtin/str/stringable) or + [`Stringable`](/mojo/stdlib/builtin/str/Stringable) or `StringableRaising` works with the built-in [`print()`](/mojo/stdlib/builtin/io/print) and [`str()`](/mojo/stdlib/builtin/str/str) functions. From df77f706992c1a99ea38a8331126768f475e7de0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 2 May 2024 14:57:26 -0700 Subject: [PATCH 0383/2019] [mojo-stdlib] Move `builtin/object` off of `-> Self`. (#39192) This modernizes some more code. MODULAR_ORIG_COMMIT_REV_ID: 3fa22f2f7b60003a193974cb04193feab8947dba --- stdlib/src/builtin/object.mojo | 55 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f9e7c3e82c..43553dcf39 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -54,8 +54,9 @@ struct _ImmutableString: """The length of the string.""" @always_inline - fn __init__(data: UnsafePointer[Int8], length: Int) -> Self: - return Self {data: data.address, length: length} + fn __init__(inout self, data: UnsafePointer[Int8], length: Int): + self.data = data.address + self.length = length @always_inline fn string_compare(self, rhs: _ImmutableString) -> Int: @@ -88,10 +89,10 @@ struct _RefCountedListRef: """The reference to the list.""" @always_inline - fn __init__() -> Self: + fn __init__(inout self): var ptr = UnsafePointer[_RefCountedList].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = _RefCountedList() - return Self {lst: ptr.bitcast[NoneType]()} + self.lst = ptr.bitcast[NoneType]() @always_inline fn copy(self) -> Self: @@ -173,14 +174,14 @@ struct _RefCountedAttrsDictRef: """The reference to the dictionary.""" @always_inline - fn __init__(values: VariadicListMem[Attr, _, _]) -> Self: + fn __init__(inout self, values: VariadicListMem[Attr, _, _]): var ptr = UnsafePointer[_RefCountedAttrsDict].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() # Elements can only be added on construction. for i in range(len(values)): ptr[].impl[]._insert(values[i].key, values[i].value._value.copy()) - return Self {attrs: ptr.bitcast[Int8]()} + self.attrs = ptr.bitcast[Int8]() @always_inline fn copy(self) -> Self: @@ -200,11 +201,11 @@ struct _Function: """The function pointer.""" @always_inline - fn __init__[FnT: AnyRegType](value: FnT) -> Self: + fn __init__[FnT: AnyRegType](inout self, value: FnT): # FIXME: No "pointer bitcast" for signature function pointers. var f = UnsafePointer[Int16]() Reference(f).get_legacy_pointer().bitcast[FnT]().store(value) - return Self {value: f} + self.value = f alias fn0 = fn () raises -> object """Nullary function type.""" @@ -312,60 +313,60 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===------------------------------------------------------------------=== # @always_inline - fn __init__(value: Self.type) -> Self: - return Self {value: value} + fn __init__(inout self, value: Self.type): + self.value = value @always_inline - fn __init__() -> Self: - return __mlir_op.`kgen.variant.create`[ + fn __init__(inout self): + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.none.value ](_NoneMarker {}) @always_inline - fn __init__(value: Bool) -> Self: - return __mlir_op.`kgen.variant.create`[ + fn __init__(inout self, value: Bool): + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.bool.value ](value) @always_inline - fn __init__[dt: DType](value: SIMD[dt, 1]) -> Self: + fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): @parameter if dt.is_integral(): - return __mlir_op.`kgen.variant.create`[ + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.int.value ](value.cast[DType.int64]()) else: - return __mlir_op.`kgen.variant.create`[ + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.float.value ](value.cast[DType.float64]()) @always_inline - fn __init__(value: _ImmutableString) -> Self: - return __mlir_op.`kgen.variant.create`[ + fn __init__(inout self, value: _ImmutableString): + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.str.value ](value) @always_inline - fn __init__(value: _RefCountedListRef) -> Self: - return __mlir_op.`kgen.variant.create`[ + fn __init__(inout self, value: _RefCountedListRef): + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.list.value ](value) @always_inline - fn __init__(value: _Function) -> Self: - return __mlir_op.`kgen.variant.create`[ + fn __init__(inout self, value: _Function): + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.function.value ](value) @always_inline - fn __init__(value: _RefCountedAttrsDictRef) -> Self: - return __mlir_op.`kgen.variant.create`[ + fn __init__(inout self, value: _RefCountedAttrsDictRef): + self.value = __mlir_op.`kgen.variant.create`[ _type = Self.type, index = Self.obj.value ](value) @always_inline - fn __copyinit__(self) -> Self: - return self.value + fn __copyinit__(inout self, existing: Self): + self = existing.value @always_inline fn copy(self) -> Self: From 247e52e5f04436fa8866d60674e2e9e9181d4f9b Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 2 May 2024 18:28:09 -0400 Subject: [PATCH 0384/2019] [stdlib] Make `List.index` take borrowed values (#39195) `List.index` previously took `owned` values which copies the value. This may not be desirable for performance reasons. So remove the `owned` memory convention for the argument `value`. Unit tests cannot be made for this change yet since `List` type requires elements to be Copyable. MODULAR_ORIG_COMMIT_REV_ID: bbf364245ee88494f0d70a9b38763063b5d6f115 --- stdlib/src/collections/list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9963e29d0a..cceaffccd8 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -410,7 +410,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn index[ C: ComparableCollectionElement ]( - self: List[C], owned value: C, start: Int = 0, end: Optional[Int] = None + self: List[C], value: C, start: Int = 0, end: Optional[Int] = None ) raises -> Int: """ Returns the index of the first occurrence of a value in a list, starting from the specified From 1065a68ada71c4daa38f423f41e06a71f10737ca Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 3 May 2024 06:40:32 +0800 Subject: [PATCH 0385/2019] [******][GPU] Implement powfi function that does not go through llvm (#39110) The llvm intrinsic would not work on the GPU since we do not compile compilerrt for it. The function is simple to implement and we already have the implementation, so just use that and avoid the special case and use the russian peasant algorithm in both cases. --------- Co-authored-by: Hengjie Wang MODULAR_ORIG_COMMIT_REV_ID: 5600caab5cb05411af0a3e68dfb6e8680508e6d9 --- stdlib/src/builtin/simd.mojo | 62 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2643dd0f0a..363da6eaf8 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -16,7 +16,14 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys import llvm_intrinsic, has_neon, is_x86, simdwidthof, _RegisterPackType +from sys import ( + llvm_intrinsic, + has_neon, + is_x86, + triple_is_nvidia_cuda, + simdwidthof, + _RegisterPackType, +) from builtin._math import Ceilable, Floorable from builtin.hash import _hash_simd @@ -2430,39 +2437,38 @@ fn _pow[ var result = SIMD[lhs_type, simd_width]() - @parameter - if lhs_type.is_floating_point(): - - @unroll - for i in range(simd_width): - result[i] = llvm_intrinsic[ - "llvm.powi", Scalar[lhs_type], has_side_effect=False - ](lhs[i], rhs[i].cast[DType.int32]()) - else: - for i in range(simd_width): - if rhs[i] < 0: - # Not defined for Integers, this should raise an - # exception. - debug_assert( - False, "exponent < 0 is undefined for integers" - ) - result[i] = 0 - break - var res: Scalar[lhs_type] = 1 - var x = lhs[i] - var n = rhs[i] - while n > 0: - if n & 1 != 0: - res *= x - x *= x - n >>= 1 - result[i] = res + @unroll + for i in range(simd_width): + result[i] = _powi(lhs[i], rhs[i].cast[DType.int32]()) return result else: # Unsupported. return SIMD[lhs_type, simd_width]() +@always_inline +fn _powi[type: DType](lhs: Scalar[type], rhs: Int32) -> __type_of(lhs): + if type.is_integral() and rhs < 0: + # Not defined for Integers, this should raise an + # exception. + debug_assert(False, "exponent < 0 is undefined for integers") + return 0 + var a = lhs + var b = abs(rhs) if type.is_floating_point() else rhs + var res: Scalar[type] = 1 + while b > 0: + if b & 1: + res *= a + a *= a + b >>= 1 + + @parameter + if type.is_floating_point(): + if rhs < 0: + return 1 / res + return res + + # ===----------------------------------------------------------------------===# # bfloat16 # ===----------------------------------------------------------------------===# From 50c4ef7c8201138995ffc8275c3deb7cee3e6eec Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 2 May 2024 18:16:54 -0500 Subject: [PATCH 0386/2019] [SDLC] Add mojo proposals to Copybara (#39198) This change moves the source of truth for Mojo proposals to the monorepo. It contains a current snapshot of the public `proposals` directory and updates Copybara to sync `modular:open-source/mojo/proposals` to `mojo:proposals`. Fixes SDLC-326 MODULAR_ORIG_COMMIT_REV_ID: 6d8f8b5f1e7cab46d83841c3fcf5f9261dfa064d --- proposals/README.md | 8 + proposals/byte-as-uint8.md | 44 ++ proposals/inferred-parameters.md | 195 +++++ proposals/lifetimes-and-provenance.md | 423 +++++++++++ proposals/lifetimes-keyword-renaming.md | 147 ++++ proposals/mojo-and-dynamism.md | 253 +++++++ proposals/project-manifest-and-build-tool.md | 104 +++ proposals/remove-let-decls.md | 153 ++++ proposals/value-ownership.md | 735 +++++++++++++++++++ 9 files changed, 2062 insertions(+) create mode 100644 proposals/README.md create mode 100644 proposals/byte-as-uint8.md create mode 100644 proposals/inferred-parameters.md create mode 100644 proposals/lifetimes-and-provenance.md create mode 100644 proposals/lifetimes-keyword-renaming.md create mode 100644 proposals/mojo-and-dynamism.md create mode 100644 proposals/project-manifest-and-build-tool.md create mode 100644 proposals/remove-let-decls.md create mode 100644 proposals/value-ownership.md diff --git a/proposals/README.md b/proposals/README.md new file mode 100644 index 0000000000..392782a030 --- /dev/null +++ b/proposals/README.md @@ -0,0 +1,8 @@ +# Mojo🔥 engineering design proposals + +This directory contains ad-hoc design proposals put together by the Mojo +engineering team. These are meant to help shape discussion and refine the +design of various subsystems, but typically become obsolete and incorporated into +more canonical documentation when the implementation work concludes. There is +no attempt to keep these up-to-date as the language evolves, so they are +more for historical reference than as a user-guide for the language. diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md new file mode 100644 index 0000000000..342007be80 --- /dev/null +++ b/proposals/byte-as-uint8.md @@ -0,0 +1,44 @@ +# Standardise the representation of byte sequence as a sequence of unsigned 8 bit integers + +At this point in time, a sequence of bytes is often represented as a sequence of +signed 8 bit integers in Mojo standard library. Most noticeable example is the +underlying data of string types `String`, `StringLiteral`, `StringRef` and +`InlinedString`, but also APIs like for example the hash function `fn +hash(bytes: DTypePointer[DType.int8], n: Int) -> Int:`. + +## Motivation + +Logically a byte is an integer value between `0` and `255`. Lots of algorithms +make use of arithmetics ground by this assumption. A signed 8 bit integer on +the contrary represents values between `-128` and `127`. This introduces very +subtle bugs, when an algorithm written for unsigned 8 bit integer is used on a +signed 8 bit integer. + +Another motivation for this change is that Mojo aims to be familiar to Python +users. Those Python users are familiar with the `bytes` class, which itself is +working with values between `0` and `255`, not values between `-128` and `127`. + +## Examples + +### Division + +A value `-4` represented as `Int8` has the same bit pattern as value `252` +represented as `UInt8`. `-4 // 4` equals to `-1` (`bx11111111`), where `252 // +4` equals to `63` (`bx00111111`) as we can see the bit patterns are different. + +### Bit shift + +Values `-1` and `255` have the same bit pattern as `Int8` and `UInt8` +`bx11111111` but `-1 >> 1` results in `-1` (same bit pattern), where `255 >> 1` +results in `127` (`bx01111111`) + +## Proposal + +A text based search for `DTypePointer[DType.int8]` and `Pointer[Int8]` on +current open-sourced standard library revealed 29 results for `Pointer[Int8]` +and 78 results for `DTypePointer[DType.int8]`. Replacing +`DTypePointer[DType.int8]` with `DTypePointer[DType.uint8]` and `Pointer[Int8]` +with `Pointer[UInt8]` on case by case bases is a substantial refactoring effort, +but it will prevent a certain class of logical bugs (see +). As it is a breaking change in +sense of API design, it is sensible to do the refactoring as soon as possible. diff --git a/proposals/inferred-parameters.md b/proposals/inferred-parameters.md new file mode 100644 index 0000000000..704f590bb5 --- /dev/null +++ b/proposals/inferred-parameters.md @@ -0,0 +1,195 @@ +# Inferring Parameters from Other Parameters + +A common feature in programming language with generics is the ability to infer +the value of generics/templates/parameters from the argument types. Consider +C++: + +```cpp +template +void inferMe(T x) {} + +int x = 1; +inferMe(x); +// Equivalent to +inferMe(x); +``` + +Mojo is a parametric language and also supports this feature in a variety of use +cases that make code significantly less verbose: + +```python +fn infer_me[dt: DType, size: Int](x: SIMD[dt, size]): pass + +infer_me(Int32()) +# Equivalent to +infer_me[DType.int32, 1](Int32()) +``` + +But Mojo pushes these needs a step further. As a language that encourages heavy +parameterization, dependent types are very common throughout the language. +Consider: + +```python +fn higher_order_func[dt: DType, unary: fn(Scalar[dt]) -> Scalar[dt]](): pass + +fn scalar_param[dt: DType, x: Scalar[dt]](): pass +``` + +Language users commonly encounter cases where dependent types could infer their +parameter values from other parameters in the same way from argument types. +Consider `scalar_param` in the example above: `dt` could be inferred from the +type of `x` if `x` were passed as an argument, but we have no syntax to express +inferring it from `x` as a parameter since the user is required to pass `dt` as +the first parameter. + +```python +scalar_param[DType.int32, Int32()]() # 'dt' parameter is required +``` + +This has been requested multiple times in various forms, especially given the +new autoparameterization feature. The current tracking feature request: + +- + +## Proposal + +In the above example, we want to be able to infer `dt` instead of explicitly +specifying it: + +```python +scalar_param[Int32()]() +``` + +Laszlo Kindrat and I proposed several options to remedy this and members of the +“Mojo Language Committee” met to discuss these ideas, summarized below. + +We decided to move forward with the following option. Mojo will introduce a new +keyword, `inferred`, as a specifier for parameters only. `inferred` parameters +must precede all non-inferred parameters in the parameter list, and they +**cannot** be specified by a caller — they can **only** be inferred from other +parameters. This allows us to express: + +```python +fn scalar_param[inferred dt: DType, x: Scalar[dt]](): pass + +scalar_param[Int32()]() # 'dt' is skipped and 'Int32()' is bound to 'x' +``` + +Where `dt` is inferred from `x`. The decision to choose a keyword instead of +introducing a new punctuation character [like Python does for keyword-only +arguments](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) +is because a keyword clearly indicates the intent of the syntax, and it’s easy +to explain in documentation and find via internet search. + +## Aside: Inferring from Keyword Parameters + +Related but separate to the proposal, we can enable parameter inference from +other parameters using keyword arguments. This allows specifying function (and +type) parameters out-of-order, where we can infer parameters left-to-right: + +```python +scalar_param[x=Int32()]() # 'dt' is inferred from 'x' +``` + +We should absolutely enable this in the language, since this does not work +today. However, with respect to the above proposal, in many cases this still +ends up being more verbose than one would like, especially if the parameter name +is long: + +```python +scalar_param[infer_stuff_from_me=Int32()]() + +# One would like to write: +scalar_param[Int32()]() +``` + +So this feature is orthogonal to the `inferred` parameter proposal. + +## Alternatives Considered + +Several alternative ideas were considered for this problem. + +### Non-Lexical Parameter Lists + +This solution would alter the name resolution rules inside parameter lists, +allowing forward references to parameters within the same list. The above +example would be expressed as: + +```python +fn scalar_param[x: Scalar[dt], dt: DType](): pass +``` + +Where any parameter is inferrable from any previous parameter. The benefits of +this approach are that the order of parameters at the callsite match the order +in the declaration: `scalar_param[Int32()]()` + +This alternative was rejected because: + +1. Non-lexical parameters are potentially confusing to users, who normally + expect named declarations to be lexical. Relatedly, we are moving towards + removing non-lexical parameters in general from the language. + +2. This would incur a huge implementation burden on the compiler, because the + type system needs to track the topological order of the parameters. + +### New Special Separator Parameter + +This solution is fundamentally the same as the accepted proposal, but differs +only in syntax. Instead of annotating each parameter as `inferred`, they are +separated from the rest using a new undecided sigil (`%%%` is a placeholder): + +```python +fn scalar_param[dt: DType, %%%, x: Scalar[dt]](): pass +``` + +The benefit of this approach is this matches the [Python +syntax](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) +for separating position-only and keyword-only parameters. It also structurally +guarantees that all infer-only parameters appear at the beginning of the list. + +This alternative was rejected because: + +1. There was no agreement over the syntax, and any selected sigil would + introduce additional noise into the language. + +2. `inferred` clearly indicates the intent of the syntax, and can be found via + internet search, and is overall easier to explain syntax than introducing a new + argument separator. + +### Special Separator Parameter at the End + +This is a variation on the above, where the infer-only parameters would appear +at the end of the parameter list, and subsequent parameters would be allowed to +be non-lexical: + +```python +fn scalar_param[x: Scalar[dt], %%%, dt: DType](): pass +``` + +The benefit of this approach is that the parameters appear in the same position +at the callsite. This alternative was rejected for a combination of the reasons +for rejecting a new separator and non-lexical parameters. + +### Segmented Parameter Lists + +This proposal would allow functions to declare more than one parameter list and +enable right-to-left inference of the parameter “segments”. The above would be +expressed as: + +```python +fn scalar_param[dt: DType][x: Scalar[dt]](): pass +``` + +The callsite would look like + +```python +scalar_param[Int32()]() +``` + +And call resolution would match the specified parameter list to the last +parameter list and infer `dt`. This proposal was rejected because + +1. The right-to-left inference rules are potentially confusing. + +2. This is an overkill solution to the problem, because this opens to door to +arbitrary higher-order parameterization of functions. diff --git a/proposals/lifetimes-and-provenance.md b/proposals/lifetimes-and-provenance.md new file mode 100644 index 0000000000..176c751f3d --- /dev/null +++ b/proposals/lifetimes-and-provenance.md @@ -0,0 +1,423 @@ +# Provenance Tracking and Lifetimes in Mojo + +As of mid-May 2023, Mojo has full support for ownership (including move +semantics, borrows and transfers, mutability, ASAP destruction of values, and +member synthesis). This provides more expressiveness than many languages, but does +not meet the expectations of Rust and C++ programmers because it is impossible +to **return references** and **put references in structs**. + +This makes Mojo unable to express common patterns like `StringRef` in the LLVM +APIs because it is a struct containing a reference, and this makes our `Pointer` +type a massively unsafe API. + +## Goals of this document + +This document explores the first step in adding lifetimes to Mojo: what changes +we’ll have to introduce, some thinking on syntax we may want to use, and how +this may want us to reconsider existing design decisions. This is written in +the style of the "[Value ownership design for Mojo](value-ownership.md)" +document from January. + +This document is really just the “first step” of lifetimes. Rust includes a few +more exotic features, including [subtyping constraints between +lifetimes](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-subtyping-constraints-between-lifetimes-15), +[equality constraints between lifetime +parameters](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-equality-constraints-between-lifetime-parameters-16), +[unbounded +lifetimes](https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html) and +perhaps other features. We don't have all the mechanics of a generic system and +trait system yet to tie into - and it makes sense to lazily add complexity based +on need - so these are not included in this initial design. + +## Context + +Mojo already has much of the required infrastructure in place to support +lifetimes: we now have references, we just need to be able to return them. +Similarly, the Mojo parameter system provides a powerful way to model and +propagate lifetime information around in our type system. We have a +CheckLifetime compiler pass which infers lifetimes, diagnosing use of +uninitialized values and inserting destructor calls. + +Similarly, we can learn a lot from Rust’s design for lifetimes. That said, the +ownership system in Mojo is quite different than the one in Rust. In Rust, +scopes define the implicit lifetimes of values and references, and lifetimes are +used to verify that uses of the value happen when the value is still alive. Mojo +flips this on its head: values start their life when defined, but end their life +after their last use. This means that in Mojo, a lifetime reference **extends +the liveness of a value** for as long as derived references is used, which is a +bit different than Rust. + +For example, we expect this to behave like the comments indicate: + +```mojo + var some_str = String("hello") + + # The StringRef contains a reference to the some_str value + var some_str_ref = StringRef(some_str) + + # Last use of some_str, but don't destroy it because there is a use of + # the reference below! + use(some_str) + + # References must be propagatable through methods. + some_str_ref = some_str_ref.drop_front().drop_back() + print(some_str_ref) # Prints "ell" + # some_str destroyed here after last reference to it +``` + +The major thing that Mojo (and Rust) need from lifetimes is what is called +“local reasoning”: We need to be able to reason about the memory behavior of a +call just by looking at its signature. We cannot have to look into the body of a +function to understand its effects, and a function cannot know about its callers +to reason about the behavior of its arguments. Similarly, when accessing a +member `a.ref` that is a reference, we need to know what lifetime is being used +in the context of `a`. + +Because of this, the lifetimes in a function signature are something of a +"transfer function" that expresses mappings from input lifetimes to returned +lifetimes, and that allows reasoning about the lifetimes of field references. +Mojo already has a powerful parameter system that allows it to express these +sorts of relationships, so this all plugs together. + +### What to name / how to explain this set of functionality? + +Rust has a few things going on that are tightly bound and somewhat confusing: it +has scoping, lifetimes, and lifetime holes. It has a borrow checker that +enforces the rules, all together this is its ownership system. + +When it comes to the “lifetimes” part of the puzzle, it seems better to clarify +two very different concepts: on the one hand, stored **values** in memory each +have a conceptual “lifetime” that starts when the value is defined and ends at +the last use - this is where the destructor call is inserted. Because each +declared variable has an independently tracked lifetime, each also needs an +implicitly declared “lifetime parameter” that is tracked in the Mojo type +system. + +On the other hand, when reasoning about parametric functions, the type signature +defines a transfer function that expresses the “provenance” of the result values +from the function and how they relate to input values. This relationship is a +transfer function from the input lifetime parameters to output lifetime +parameters, and can be somewhat more complicated. The framing of “provenance +tracking” may be more general conceptually than “lifetime tracking” which seems +specific to the lifetime parameters. + +## Mojo Reference + Lifetime Design + +Lifetimes enable us to use references as first class types. This means they +can occur in nested positions: You can now have a reference to a reference, you +can have an array of references, etc. At the machine/execution level, a +reference is identical to a pointer, but the reference type system enables an +associated lifetime, tracking of mutability etc. + +### Writing a lifetime bound reference + +The first question is how to spell this. Rust uses the `'a` syntax which is +pretty unconventional and (weirdly but) distinctly Rust. For example, here are +some simple Rust functions: + +```rust +// This is Rust +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {..} +fn longest2<'a>(x: &'a mut str, y: &'a mut str) -> &'a mut str {..} +``` + +Mojo already have a general set of values in our generic signature list: we just +need to "parameterize" references and make them explicit. For now, we recommend +using new `ref` and `mutref` keywords parameterized on a lifetime, and a new +`Lifetime` type. For example: + +```mojo +# Proposed Mojo syntax. +fn longest[a: Lifetime](x: ref[a] String, + y: ref[a] String) -> ref[a] String: + return x if len(x) >= len(y) else y + +fn longest2[a: Lifetime](x: mutref[a] String, + y: mutref[a] String) -> mutref[a] String: ... +``` + +There are many other options for syntax that we can consider, but this is simple +and will get us going until we have more experience. For example, we can make +any of these work: + +```mojo +fn f(x: ref[a] String, y: mutref[a] String): ... # Homage to Rust! +fn f(x: ref(a) String, y: mutref(a) String): ... # Homage to Rust! +fn f(x: ref a String, y: mutref a String): ... # Homage to Rust! +fn f(x: ref 'a String, y: mutref 'a String): ... # Homage to Rust! +fn f(x: 'a String, y: mut 'a String): ... # Homage to Rust! +``` + +The argument for square brackets vs parens is if we like the explanation that +we’re +"parameterizing the reference with a lifetime". However, remember types can +also be parametric, and those are spelled with square brackets **after** the +type name, so parens may be better to make these more syntactically distinct. + +The spelling in structs should flow naturally from this: + +```mojo +struct StringRef[life: Lifetime]: + var data : Pointer[UInt8, life] + var len : Int +``` + +Being first class types, we will naturally allow local references: these should +also allow late initialization and explicitly declared lifetimes as well: + +```mojo +fn example[life: Lifetime](cond: Bool, + x: ref[life] String, + y: ref[life] String): + # Late initialized local borrow with explicit lifetime + let str_ref: ref[life] String + + if cond: + str_ref = x + else: + str_ref = y + print(str_ref) +``` + +### Mojo Argument Conventions vs References + +One non-obvious thing is that function argument conventions and references are +different things: keeping them different allows argument conventions to be a +convenient sugar that avoids most users from having to know about references and +lifetimes. For example, the `borrowed` (which is usually implicit) and `inout` +argument conventions are sugar that avoids having to explicitly declare +lifetimes: + +```mojo +// Mojo today +fn example(inout a: Int, borrowed b: Float32): … + +struct S: + fn method(inout self): … + +// Written long-hand with explicit lifetimes. +fn example[a_life: Lifetime, b_life: Lifetime] + (a: mutref[a_life] Int, b: ref[b_life] Float32): … +struct S: + fn method[self_life: Lifetime](self: mutref[self_life]): … +``` + +This is very nice - every memory-only type passed into or returned from a +function must have a lifetime specified for it, but you really don't want to +have to deal with this in the normal case. In the normal case, you can just +specify that you're taking something by borrow (the default anyway) with an +implicit lifetime, or taking it `inout` if you want to mutate it but don't care +about the lifetime. + +NOTE: `inout` arguments also have one additional feature: function calls with +`inout` arguments know how to work with getter/setter pairs. For example +something like `mutate(a[i])` will call the getter for the element before +calling the function, then call the setter afterward. We cannot support this +for general mutable reference binding (because we wouldn't know where to perform +the writeback) so `inout` will have a bit more magic than just providing an +implicit lifetime. + +NOTE: Internally to the compiler, references (which are always pointer sized) +are treated as a register-passable types. This means they compose correctly +with implicit borrow semantics, and you can even pass a reference `inout` if you +want to. Such a thing is a "mutable reference to a reference", allowing the +callee to mutate the callers reference. + +### Keyword (?) for static lifetime + +I think we can have a useful feature set without requiring the ability to +specify a static lifetime - the uses in Rust appear to be more about +constraining input lifetimes than it is about the core propagation of lifetimes, +that said, we can definitely dream up a spelling when it is needed. + +Similarly, unsafe pointer tricks (e.g. when working with C) may want to use the +static lifetime marker to disable all tracking. We can start with a stub like +`__static_lifetime` and then re-syntax it later. + +### Implicitly declared lifetime parameter names and other sugar + +One common use of named lifetime parameters is to tie the lifetime of the result +of a function back to the lifetime of one of the function arguments. One +refinement over Rust we could permit is for arguments to implicitly declare +lifetimes on their first use. For example, we don’t need to require a +declaration of `life` in this example: + +```mojo +fn longest(x: ref[life] String, + y: ref[life] String) -> ref[life] String: + +# Alternatively follow Rust's lead. +fn longest(x: 'life String, y: 'life String) -> 'life String: +``` + +Let's **NOT** add this in the near future. Explicit lifetimes will be much less +common in Mojo than they are in Rust, and it is better for learnability to not +have magic like this. + +### Lifetime of `Self` + +The `Self` keyword (upper case) produces an elaborated type name for the current +struct, but that does not include the lifetime of `self` (lower case) which is +generally a reference. In a method you can name the lifetime of `self` by +declaring it explicitly like this: + +```mojo + struct MyExample: + fn method[self_life: Lifetime](self: mutref[self_life] Self) + -> Pointer[Int, self_life]: + ... + + fn callMethod(x: mutref[life1] MyExample): + use(x.method()) + + var y = MyExample() + use(y.method()) +``` + +`self_life` will bind to the lifetime of whatever lvalue the method is called +on, which is the `life1` lifetime in the first example, and the implicit +lifetime of y in the second example. This all composes nicely. + +One problem though - this won’t work for var definitions inside the struct, +because they don’t have a self available to them, and may need to reason about +it: + +```mojo + struct IntArray: + var ptr : Pointer[Int, Self_lifetime] +``` + +It isn’t clear to me how the compiler will remap this though. We’d have to pass +in the pointer/reference instead of the struct type. An alternative is to not +allow expressing this and require casts. We can start with that model and +explore adding this as the basic design comes up. + +### Extended `getitem`/`getattr` Model + +Once we have references, we’ll want to add support for them in the property +reference and subscripting logic. For example, many types store their enclosed +values in memory: instead of having `Pointer` and `Array` types implement both +`__getitem__` and `__setitem__` (therefore being a "computed LValue") we'd much +rather them to expose a reference to the value already in memory (therefore +being more efficient). We can do this by allowing: + +```mojo + struct Pointer[type: AnyType, life: Lifetime]: + # This __getref__ returns a reference, so no setitem needed. + fn __getref__(self, offset: Int) -> mutref[life] type: + return __get_address_as_lvalue[life](...) +``` + +We will also need to extend the magic `__get_address_as_lvalue` style functions +to take a lifetime. + +## Examples using Lifetimes + +This section attempts to build a few example data structures that are important +to express with lifetimes. They obviously haven’t been tested. + +### Pointer / UnsafePointer / Reference + +This is the bottom of the stack and needs to interface with other unsafe +features. Suggested model is to make Pointer be parameterized on the lifetime +that it needs to work with as well as element type: + +```mojo + @value + @register_passable("trivial") + struct MutablePointer[type: AnyType, life: Lifetime]: + alias pointer_type = __mlir_type[...] + var address: pointer_type + + fn __init__() -> Self: ... + fn __init__(address: pointer_type) -> Self: ... + + # Should this be an __init__ to allow implicit conversions? + @static_method + fn address_of(arg: mutref[life] type) -> Self: + ... + + fn __getitem__(self, offset: Int) -> inout[life] type: + ... + + @staticmethod + fn alloc(count: Int) -> Self: ... + fn free(self): ... + + fn exercise_pointer(): + # Allocated untracked data with static/immortal lifetime. + let ptr = MutablePointer[Int, __static_lifetime].alloc(42) + + # Use extended getitem through reference to support setter. + ptr[4] = 7 + + var localInt = 19 + let ptr2 = MutablePointer.address_of(localInt) + ptr2[0] += 1 # increment localInt + + # ERROR: Cannot mutate localInt while ptr2 lifetime is live + localInt += 1 + use(ptr2) +``` + +It’s not clear to me if we need to have a split between `Pointer` and +`MutablePointer` like Swift does. It will depend on details of how the +CheckLifetimes pass works - I’m hoping/expecting that the borrow checker will +allow mutable references to overlap with other references iff that reference is +only loaded and not mutated. NOTE: We probably won't be able to do this with the +proposed model, because generics can't be variant over mutability of the +reference. + +Another aspect of the model we should consider is whether we should have an +`UnsafePointer` that allows unchecked address arithmetic, but have a safe +`Reference` type that just allows dereferencing. This `Reference` type would be +completely safe when constructed from language references, which is pretty cool. +We may also want to wire up the prefix star operator into a dunder method. + +### ArraySlice + +`ArraySlice` (aka `ArrayRef` in LLVM) should compose on top of this: + +```mojo + @value + @register_passable("trivial") + struct MutableArraySlice[type: AnyType, life: Lifetime]: + var ptr: MutablePointer[type, life] + var size: Int + + fn __init__() -> Self: + fn __init__(ptr: MutablePointer[type, life], size: Int) -> Self: + + # All the normal slicing operations etc, with bounds checks. + fn __getitem__(self, offset: Int) -> inout[life] type: + assert(offset < size) + return ptr[offset] +``` + +As with `UnsafePointer`, this has to be parameterized based on the underlying +element type. `ArraySlice` is just a bound checked pointer, but because of +lifetimes, it is safe once constructed: the references it produces are bound to +the lifetime specified so can’t dangle. + +### Array / ValueSemanticArray + +Given these low level types, we can start to build higher level abstractions. +One example of that is an `Array` type. I’d suggest that our default array type +be value semantic with lazy copy-on-write 🐄, but a simpler example can be +implemented with `std::vector` style eager copying: + +```mojo + # Doesn't require a lifetime param because it owns its data. + struct Array[type: AnyType]: + var ptr: MutablePointer[type, Self_lifetime] + var size: Int + var capacity: Int + + fn __getitem__[life: Lifetime](self: inout[life], start: Int, + stop: Int) -> MutableArraySlice[type, life]: + return MutableArraySlice(ptr, size) +``` + +By tying the lifetime of the produced slice to the lifetime of the Array `self`, +the borrow checker will prevent use/mutation of the `Array` itself while a +mutable slice is produced. diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md new file mode 100644 index 0000000000..e99bf2f77f --- /dev/null +++ b/proposals/lifetimes-keyword-renaming.md @@ -0,0 +1,147 @@ +# Keyword naming and other topics to discuss + +This document is split off the [Provenance Tracking and Lifetimes in +Mojo](lifetimes-and-provenance.md) document to separate general syntactic +bikesheding issues from the core semantic issues in that proposal. + +Assuming that proposal goes through, I think we should consider a few changes to +the current Mojo keyword paint: + +## `borrowed` Keyword => `borrow` or `ref` + +`borrowed` as a keyword doesn’t really make sense in our new world. This is +currently used to indicate an argument that is a borrowed version of an existing +value. Given the introduction of lifetimes, these things can now appear in +arbitrary places (e.g. you can have an array of references) so it makes sense to +use a noun. + +Instead of reading an argument as “this function takes foo which is a borrowed +string”, we would read it as “foo is a borrow/ref of a string”. This makes it +consistent with local borrows on the stack: + +```mojo +fn do_stuff[a: Lifetime](x: ref[a] String): ... + +fn usage(): + var str = String("hello") + ref r = str # Defines a local borrow of str. + + do_stuff(str) # Bind a reference to 'str' + do_stuff(r) # Pass on existing reference 'str' +``` + +## `inout` Keyword => `ref` or `mutref` (??) + +The primary argument for the ‘`inout`’ keyword being named this was that Chris +wanted to get off the former ampersand syntax we used, and that (in an argument +position) there is copy-in and copy-out action that happens with computed +LValues. I think there is a principled argument to switch to something shorter +like `ref` which is used in other languages (e.g. C#), since they can exist in +other places that are not arguments, and those don’t get copy-in/copy-out +behavior. One challenge with the name `ref` is that it doesn't obviously +convey mutability, so we might need something weird like `mutref`. + +Note that copy-in/copy-out syntax is useful in more than function call +arguments, so the `inout` keyword may return in the future. For example, we may +actually want to bind computed values to mutable references: + +```mojo +for inout x in some_array_with_getitem_and_setitem: + x += 1 +``` + +This requires opening the reference with getitem, and writing it back with +setitem. We may also want to abstract over computed properties, e.g. form +something like `Array[inout Int]` where the elements of the array hold closers +over the get/set pairs. If we had this, this could decay to a classic mutable +reference at call sites providing the existing behavior we have. + +Given this possible direction and layering, I think we should go with something +like this: + +1. `ref`: immutable reference, this is spelled “`borrowed`” today + +2. `mutref`: mutable reference, this is spelled “`inout`” today. I’d love a +better keyword suggestion than `mutref`, perhaps just `mut`? + +3. `inout`: abstracted computed mutable reference with getter/setter. + +`inout` can decay to `mutref` and `ref` in an argument position with writeback, +and `mutref` is a subtype of `ref` generally. + +## `owned` Keyword => `var` + +People on the forums have pointed out that the “`owned`” keyword in argument +lists is very analogous to the `var` keyword. It defines a new, whole, value +and it is mutable just like `var`. Switching to `var` eliminates a concept and +reduces the number of keywords we are introducing. + +## Allow `let` in argument lists ... or remove them entirely (!) + +If we replace the `owned` keyword with `var`, then we need to decide what to do +with `let`. There are two different paths with different tradeoffs that I see. + +The easiest to explain and most contiguous would be to allow arguments to be +defined as `let` arguments, just like we define `var` arguments. This would +keep these two declarations symmetrical, and appease people that like to control +mutation tightly. + +The more extreme direction would be to remove `let` entirely. Some arguments +in favor of this approach: + +1. It has been observed on the forum that it adds very little - it doesn't + provide additional performance benefits over `var`, it only prevents + "accidental mutation" of a value. +2. Languages like C++ default to mutability everywhere (very few people bother + marking local variables constant, e.g. with `const int x = foo()`. +3. The more important (and completely necessary) thing that Mojo needs to model + are immutable borrows. Removing `let` would reduce confusion about these two + immutable things. +4. Mojo also has `alias`, which most programmers see as a “different type of + constant” further increasing our chance of confusing people. +5. `let` declarations require additional compiler complexity to check them, Mojo + doesn’t currently support struct fields market `let` for example because the + initialization rules are annoying to check for. Once you have them, it + messes with default values in structs. + +In my opinion, I think we are likely to want to remove `let`’s, but we should +only do so after the whole lifetime system is up and working. This will give us +more information about how things feel in practice and whether they are worth +the complexity. + +## More alternatives to consider + +[@sa- +suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6104926) +the keyword `fix` instead of `let`. + +[@mojodojodev suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6105688): + +`ref[a]` - immutable reference +`mut[a]` - mutable reference +`let[a]` - immutable owned +`var[a]` - mutable owned + +Having three letters for all of the keywords will allow the user to understand +"this is related to ownership and mutability". The problem with the proposed +removing let is that code ported from Python to Mojo won't behave the same, +keeping let and var is advantageous in that it says this is a Mojo variable so +you can add all the weird Python dynamic behavior when the keyword is elided. + +[@mzaks +suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6134220) +using numbers to identify lifetimes, e.g.: + +```mojo +fn example['1_life](cond: Bool, + x: borrowed'1 String, + y: borrowed'1 String): + # Late initialized local borrow with explicit lifetime + borrowed'1 str_ref : String + + if cond: + str_ref = x + else: + str_ref = y + print(str_ref) +``` diff --git a/proposals/mojo-and-dynamism.md b/proposals/mojo-and-dynamism.md new file mode 100644 index 0000000000..845cc993b6 --- /dev/null +++ b/proposals/mojo-and-dynamism.md @@ -0,0 +1,253 @@ +# Mojo and Dynamism + +Mojo has the lofty goal of being a simple, powerful, and easy-to-use language +like Python but with features that allow programmers to reach the performance of +C. One of Mojo's approaches is to start from being a superset of Python and +provide an incremental typing-for-performance story where the performance of +Python code can be incrementally improved by adding type annotations, explicit +variable declarations and error handling, switching `def` to `fn`, and so on. +By making things like types explicit, dynamism is removed from the program and +the compiler can generate faster code. The relationship between Mojo and +dynamism has to be carefully managed to meet the goals of the language. The +point of this post is to measure where that relationship stands now, what it +will need going forward as Mojo grows more features, and develop a framework for +managing that relationship. + +## Classes and Dynamism + +One feature that Mojo lacks at the moment are classes, an inherently dynamic +feature that provides inheritance and runtime polymorphism, the foundations of +object-oriented programming. + +Classes are implemented differently in many languages. In Python, for example, +classes are much more flexible than in languages like C++ or Java -- they are +more similar to classes in Javascript or Smalltalk. Methods can be defined and +then deleted, and even conditionally defined! For example, this is valid Python +code: + +```python +define = True + +class C: + print("hello") # prints 'hello' + if define: + def f(self): print(10) + else: + def f(self): print(20) + + +C().f() # prints '10' +``` + +In fact, the body of a Python class is just code that is executed, and the +resulting local variables are bound to the attributes of a class object. When +calling a class object, it returns a new object with a reference to the class +object, in which it can perform attribute lookup. In addition, functions that +would be member functions have their first argument bound to the new class +instance through the Python [descriptor +mechanism](https://docs.python.org/3/howto/descriptor.html#invocation-from-a-class). + +Mojo as a superset of Python has to support the full "hash-table" dynamism in +classes for compatibility with Python, but reference semantic classes are also +important for systems programming and application programming, where this level +of dynamism isn't needed and is actively harmful. We need to decide how to +handle this. + +One approach is to provide a decorator on class definitions (which can be opt-in +or opt-out) to indicate whether the class is "fully dynamic" as in Python or +whether it is "constrained dynamic" (e.g. has virtual methods that may be +overridden but cannot have methods added or removed). + +"Constrained dynamic" Mojo classes will use vtables for a more limited but more +efficient constrained dynamism than full hash table lookups. In addition to raw +lookups, constrained dynamic classes can use "[class hierarchy +analysis](https://dl.acm.org/doi/10.5555/646153.679523)" to devirtualize and +inline method calls, which are not valid for "fully dynamic" classes. + +Swift has a similar issue, where the developers wanted to have constrained +dynamism by default but needed full dynamism when working with Objective-C code: +Objective-C is based on the Smalltalk object model and thus has the same issues +as Python. Swift solved this by adding an opt-in +[@objc](https://swiftunboxed.com/interop/objc-dynamic/) decorator, which +provides full compatibility with Objective-C classes. Swift implicitly applies +this decorator to subclasses of Objective-C or `@objc` classes for convenience. + +If we chose to follow this design in Mojo, we could introduce a `@dynamic` +decorator, in which the class is an instance of a hash table and the body is +executed at runtime: + +```python +@dynamic +class C: + def foo(): print("warming up") + foo() # prints 'warming up' + del foo + def foo(): print("huzzah") + foo() # prints 'huzzah' +``` + +We could of course make dynamic be the default, and have a decorator like +`@strict` to opt-in to constrained dynamism as well. Regardless of the bias, we +absolutely need to support full dynamism to maintain compatibility with Python. + +An implementation question here would be "when does the body get executed?" when +the class is defined at the top-level. In this case, the class `C` could be +treated as a global variable with a static initializer that is executed when the +program is loaded. This ties into a discussion about how to treat global +variables and top-level code in general, which will come in a subsequent +section. Naturally, if the class is never referenced, the body is never parsed +and the static initializer is never emitted. + +### Syntactic Compatibility and `@dynamic` + +A primary goal of Mojo is to [minimize the syntactic +differences](https://docs.modular.com/mojo/why-mojo.html#intentional-differences-from-python) +with Python. We also have to balance that need with what the right default for +Mojo is, and this affects the bias on whether this decorator is "opt-in" or +"opt-out". + +We find it appealing to follow the Swift approach by making "full dynamic" an +opt-in choice for a Mojo class. This choice would add another syntactic +divergence between Mojo and Python, but it is one that can be alleviated with an +automatic mechanical transformer from Python code to Mojo code (e.g. to deal +with new keywords we take). In this case, all Python classes will be translated +by sticking `@dynamic` on them, and they can be removed for incremental boosts +to performance. + +An alternate design is to require opt-in to "constraint dynamism" by adding a +`@strict` (or use another keyword altogether) for vtable dynamism. We can +evaluate tradeoffs as more of the model is implemented. + +## `def` and Dynamism + +In Mojo, the goal of `def` is to provide a syntactic feature set that enables +compatibility with Python. It allows, for example, omitting type annotations, +implicit variable declarations, implicit raises, etc. But the Mojo `def` is not +the same as a Python `def`. A commonly reported issue is that Mojo scoping rules +differ from Python's. In Python, local variables are scoped at the function +level, but Python also supports behaviour like: + +```python +def foo(k): + for i in range(k): + print(i) + # Mojo complains that `i` is not defined, but this code should compile and + # dynamically raise an `UnboundLocalError` depending on the value of `k`! + print(i) +``` + +Python functions also have a notion of which names are supposed to be bound to +local variables. In the following example, `bar` knows `i` refers to a captured +local variable in `foo`, whereas `baz` tries to retrieve a value for `i` in its +local variable map. + +```python +def foo(): + i = 2 + def bar(): + print(i) + def baz(): + print(i) + i = 10 + bar() # prints '2' + baz() # throws an 'UnboundLocalError' +``` + +This gets at the heart of how Mojo should treat implicitly declared variables in +`def`s. The short answer is: exactly how Python does. `def`s should carry a +function-scoped hash table of local variables that is populated and queried at +runtime. In other words, lookup of implicitly-declared variables would be +deferred to runtime. On the other hand, the function does need to have a notion +of what variable *could* be available in the function, in order to emit +`UnboundLocalError`s as required. Of course, the compiler can optimize the table +away and do all the nice stuff compilers do if possible. + +Difficulty arises when discussing `def`s themselves. Although `def`s should +internally support full hashtable dynamism, what kind of objects are `def`s +themselves? For instance: + +```python +def foo(): + bar() + +def bar(): + print("hello") + +foo() # prints 'hello' + +def bar(): + print("goodbye") + +foo() # should this print 'goodbye'? +``` + +In Mojo today, the first time the name lookup of `bar` is resolved, it is baked +into a direct call to the first `bar`. Therefore, shadowing of `bar` does not +propagate into the body of `foo`. On the other hand, if all `def`s were treated +as entries in a hashtable, then it would. + +One middle-ground approach would be to treat `bar` as a mutable global variable +of type `def()` (one for each possible overload of `bar`). The dynamism can be +escalated with a `@dynamic` decorator that removes static function overloading. +However, both of these approaches risk creating confusing name lookup rules. For +instance, would the following be allowed? + +```python +@dynamic +def bar(a): pass + +def bar(a: Int): pass +``` + +This gets into the "levels of dynamism" Mojo intends to provide, and how that +relates to `def`s. The reality is that `def`s in Mojo today only resemble Python +`def`s on the surface. They share similar syntax, but Mojo `def`s are really +extra syntax sugar on top of `fn` and are altogether a different beast than +Python `def`s. + +## Four Levels of Dynamism + +To summarize, in order to support incremental typing-for-performance, Mojo will +have to support everything from strict, strongly-typed code to full Python +hashtable dynamism but with syntax that provides a gradual transition from one +end to the other. + +Given all that has been discussed and what the language looks like today, Mojo's +dynamism is moving into four boxes: + +1. Compile-time static resolution. +2. Partial dynamism. +3. Full hashtable dynamism. +4. ABI interoperability with CPython. + +The fourth category isn't explored here, but will important when/if we support +subclassing imported-from-CPython classes in Mojo, because that will fix the +runtime in-memory representation to what CPython uses. + +The highest level of dynamism and the most faithful compatibility doesn't come +from Mojo itself, it comes from Mojo's first class interoperability with +CPython. This in effect will be Mojo's escape hatch for compatibility purposes +and is what gives Mojo access to all of Python's vast ecosystem. Below that, +Mojo will provide an emulation of Python's hash-table dynamism that is a +faithful but not quite identical replication of Python behaviour (no GIL, for +example!). Building this out will be a huge undertaking, and is something Mojo +should do over time. + +The most important thing to remember is that Mojo is not a "Python compiler". +The benefit of sharing the same syntax as Python, however, means seamless +interop is on the table: + +```python +@python +def python_func(a, b=[]): + return a + [2] + b + +fn mojo_func(): + try: + print(python_func([3])) + except e: + print("error from Python:", e) +``` + +The goal of the "levels of dynamism" is to provide an offramp, starting by +removing the `@python` decorator from `python_func`. diff --git a/proposals/project-manifest-and-build-tool.md b/proposals/project-manifest-and-build-tool.md new file mode 100644 index 0000000000..1899b0ae3d --- /dev/null +++ b/proposals/project-manifest-and-build-tool.md @@ -0,0 +1,104 @@ +# Mojo project manifest and build tool + +A *project manifest* is a file that describes the source code files that make up +a library or executable, and how those files are meant to be built, distributed, +and interacted with by language tooling (such as language servers and +debuggers). Some examples include Rust’s `Cargo.toml`, Python’s `setup.py`, or +language-agnostic formats such as Bazel and its `BUILD` files. + +A *build tool*, on the other hand, is a program that can create the libraries or +executables described in a project manifest. For example, `cargo build` compiles +and links Rust executables and libraries described in a `Cargo.toml` file. In +Python, many different tools can process `setup.py` and `pyproject.toml` files, +in order to produce Python wheels or packages. + +This proposal is meant to: + +1. Announce our intent to define a project manifest format for Mojo. +2. Announce our intent to implement a build tool for Mojo projects. +3. Solicit community feedback and feature requests for the project manifest and + build tool. + +## Motivation + +No project manifest format exists for Mojo as of Mojo SDK v0.7.0. In the present +situation, Mojo projects can be built by invoking `mojo package` (to produce a +`.mojopkg` library) or `mojo build` (to produce an executable) on the command +line. + +This status quo has several drawbacks: + +1. There is no standard for building a Mojo project from source. If we examine + popular Mojo projects hosted on GitHub today, some are built via `docker`, + where the Dockerfile invokes `mojo run` to execute the Mojo source file. Some + are built via CMake, where project maintainers have used `add_custom_command` + to invoke `mojo build` and `mojo package`. Some are built via a + `mojo package` command that is documented only in the README. Some have build + instructions that are only known to the maintainers. The lack of + standardization makes it difficult to download and build a Mojo source + repository, which inhibits collaboration among the Mojo community of + developers. +2. Collaboration within the Mojo community aside, the lack of a project manifest + inhibits Mojo language tooling, such as the language server and debugger, + from functioning perfectly. For example, many Mojo projects make use of + compile time definitions, such as `-D ENABLE_TILING`. Without knowing which + definitions should be used when compiling Mojo source code, the language + server cannot provide the same diagnostics that the user will see when + actually building their project. + +Therefore, we think that a project manifest and build tool specific to Mojo will +resolve these issues: + +1. We aim to implement a single command that can be used to build any Mojo + project from source, addressing the first issue listed above. This is + analogous to how `cargo build` builds the default targets with the default + settings for any Rust project, or how `zig build` does so for any Zig + project. +2. Because a project’s manifest will specify which Mojo compiler options are to + be used, language tools would make use of those and function as intended. + This addresses the second issue listed above. + +## Guiding principles + +- As mentioned above, we aim for a single command to be capable of building any + Mojo project from source. +- We believe the ability to *download* a project’s dependencies from the + Internet — a “package manager” function — can be added at a later time. For + example, `zig build` did not originally include this functionality, but added + an implementation over six years later. The Mojo build tool will likely + implement the downloading and building of project dependencies soon, but it + will be the subject of a separate proposal. +- Although the project manifest and build tool we design is specific to Mojo, we + will aim for the best possible integration with other build systems, such as + Python setuptools, Bazel, Buck2, and CMake. We will make accommodations to + better support these tools whenever possible. +- Our design will benefit from community input and contributions, so **we will + develop this as an open-source tool, written primarily in Mojo**. We believe + doing so will also serve to drive additions and improvements to the Mojo + standard library. + +## Request for feedback + +As mentioned above, this proposal is primarily to announce our intent to develop +a project manifest format and build tool for Mojo, and to solicit feedback. +Below are some topics that we would love to hear community members’ opinions on: + +- Whether you agree with the motivations and guiding principles in this + proposal. +- Which project manifest formats and build tools you love, and why. We’re + drawing inspiration from a broad set of language ecosystems, including Rust, + Zig, Swift, and especially Python. +- Whether to adopt the [build server + protocol](https://build-server-protocol.github.io). We think doing so may help + with our guiding principle to integrate well into the existing ecosystem of + tools. +- Whether to define the project manifest as an executable program. Analogous to + how `build.zig` and `Package.swift` are programs that define a project, should + we define a `project.mojo` or similar construct? There are many arguments in + favor of doing so, but on the other hand, we see tradeoffs as well, and a + purely declarative form could be used. +- Any other thoughts you wish to contribute — we are build systems and language + tooling nerds! Send us your thoughts on [the GitHub Discussion thread + associated with this + proposal](https://github.com/modularml/mojo/discussions/1785), and let’s geek + out. diff --git a/proposals/remove-let-decls.md b/proposals/remove-let-decls.md new file mode 100644 index 0000000000..ec09b19359 --- /dev/null +++ b/proposals/remove-let-decls.md @@ -0,0 +1,153 @@ +# Simplifying Mojo🔥 - let's get rid of `let` + +Chris Lattner, Dec 5, 2023, Status: **Accepted**, [discussion thread](https://github.com/modularml/mojo/discussions/1456#discussioncomment-8358722) + +Mojo is still a new language, and is rapidly evolving. We’re learning a lot +from other languages, but Mojo poses its own set of tradeoffs that indicate a +unique design point. + +One of the early decisions made in Mojo's development is that it adopts the +`let` and `var` design point that Swift uses. This whitepaper argues that we +should switch to a simpler model by jettisoning `let` and just retaining `var` +(and implicit Python-style variable declarations in `def`). This has also been +[suggested by the community](https://github.com/modularml/mojo/issues/1205). + +Note that immutability and value semantics remain an important part of the Mojo +design, this is just about removing "named immutable variables". Immutable +references in particular are critical and will be part of a future "lifetimes" +feature. + +## Current Mojo 0.6 design + +Mojo initially followed the precedent of Swift, which allows a coder to choose +between the `let` and `var` keyword to determine whether something is locally +modifiable. That said, the design in Mojo 0.6 and earlier has a number of +particularly surprising and unfortunate aspects: + +1. The notion of immutable variables is entirely new to Python programmers, and +previous experience with Swift shows that this ends up being the [very first concept](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Constants-and-Variables) +a Swift programmer has to learn. This is unfortunate, because named immutable +variables aren't a core programming concept, and not something required +to achieve Mojo's goals. + +2. The naming of `let` caused a lot of early [heat and +debate](https://github.com/modularml/mojo/discussions/120). Other programming +languages have a wide range of design points (e.g. `const` in C/C++ and +Javascript) and there is a divergence of naming for all these things: +`let`, `val`, `const`, etc, etc. + +3. Mojo also has a notion of compile time value (`alias`), which means there are +three concepts going around: `alias`, `let`, and `var`. Most of the uses of +(e.g.) Javascript `const` is better served with `alias` than `let`. + +4. Both Swift and Rust encourage immutable values - Swift (and currently Mojo) +warn about unneeded mutability, Rust makes mutability more verbose (`let mut`), +and some propose that Mojo [make mutability more +verbose](https://github.com/modularml/mojo/issues/451). This cuts very hard +against a lot of the design center of Python, which doesn’t even have this +concept at all: it would be weird to make it the default, but if we don’t, +then why bother having it? + +5. There is no performance benefit to the Swift/Rust design, and I personally +haven’t seen any data that shows that there is any safety or readability +benefit. If anything, it is the argument when seeing `let x = foo()` that you +know `x` will never be reassigned, but any benefit here is small. + +6. The immutability only applies to the local value, and in the case of +reference semantic types (e.g. types like `Pointer` in today's Mojo, but also +*all classes* in tomorrow's Mojo) this is super confusing. We are often asked: +“Why do I get a warning that I should change a "`var` pointer" to `let` when I +clearly mutate what it is pointing to?” + +7. Mojo does not currently allow `let`’s as struct fields, (only `var`’s) which +is inconsistent. Swift has a very complex set of rules for how struct fields +get initialized that would be nice to not implement for Mojo. There also isn’t +a great way to define defaulted field values, e.g.: + + ```mojo + struct Thing: + # This is not actually supported right now, but imagine it were. + let field = 42 + fn __init__(inout self): + self.field = 17 # shouldn't be able to overwrite field? + ``` + +8. Mojo has a notion of ownership and will eventually have a notion of lifetimes +and safe references (including both mutable and immutable *references*) which +will be different from (but can compose with) the `let` vs `var` distinction. +It is unfortunate to have different forms of immutability floating around, and +we really do need immutable borrows and immutable references. + +Speaking subjectively as one of the principal designers of Swift, I will say +that it has several pretty pedantic language features intended to increase +safety (e.g. requiring all potentially-throwing values to be marked with a `try` +keyword) and many of the decisions were made early without a lot of data to +support them. I believe we should fight hard to keep Mojo easy to learn and +eliminate unnecessary concepts if we can. + +## Proposal: eliminate ‘`let`’ declarations + +The proposal here is straightforward: let’s just eliminate the concept of an +immutable named value entirely. This won’t eliminate immutability as a concept +from Mojo, but will instead push it into the world of borrowed arguments and +immutable references. This would have a number of advantages: + +This directly simplifies the conceptual Mojo language model: + +1. This eliminates one unfamiliar concept that a Python program would have to + learn. +2. This eliminates confusion between `let` vs `alias` directly. +3. This eliminates a fertile source of keyword bikeshedding. +4. This eliminates confusion in workbooks where top level values are mutable + even though they are declared `let`. + +This would eliminate a bunch of complexity in the compiler as well: + +1. Error messages currently have to make sure to say `let` and `var` correctly. +2. The IR representation needs to track this for later semantic checks. +3. This eliminates the need to implement missing features to support `let`’s. +4. This eliminates the complexity around detecting unmutated `var`s that warn + about changing to `let`. +5. Due to ASAP destruction, CheckLifetimes has extra complexity to reject code + like: “`let x: Int; x = 1; use(x); x = 2; use(x)`” even though the original + lifetime of the first “`x=1`” naturally ended and “`x`” is uninitialized + before being assigned to. This has always been a design smell, and it + [doesn’t work right](https://github.com/modularml/mojo/issues/1414). + +This proposal will not affect runtime performance at all as far as we know. + +### What about var? + +If this proposal is accepted, I think we should leave `var` as-is. Unlike +traditional Python behavior, `var` introduces an explicitly declared and +*lexically scoped* value: we need some introducer and do want scoped +declarations. + +The name `var` is also less controversial because it clearly stands for +“variable” in a less ambiguous way than using `let` to stand for "named +constant". If there is desire to rename `var` it would be an orthogonal +discussion to this one and should be kept separate. + +## Rolling out this proposal smoothly + +If we think this proposal is a good idea, then I think we should stage this to +make adoption more smooth and less disruptive. Rolling this out would look like +this: + +1. Build consensus with the Mojo community to get feedback and additional + perspective. +2. Do the engineering work to validate there is no performance hit etc, and + eliminate the IR representation and behavior for `let`. At this phase we + will keep parsing them for compatibility: parse them into the same IR as a + `var`, but emit a warning “let has been deprecated and will be removed in + the next release” with a fixit hint that renames the `let` to `var`. +3. In a release ~1 month later, change the warning into an error. +4. In a release ~1 month later, remove the keyword entirely along with the error + message. + +## Alternatives considered + +We can always keep this around and re-evaluate later. That said, I don’t think +anything will change here - the Mojo user community (both external to Modular +and internal) has already run into this several times, and this will keep coming +up. diff --git a/proposals/value-ownership.md b/proposals/value-ownership.md new file mode 100644 index 0000000000..9303d1644e --- /dev/null +++ b/proposals/value-ownership.md @@ -0,0 +1,735 @@ + +# Value ownership design for Mojo + +**Written**: Jan 2, 2023 + +**Status**: Implemented but the design has been refined, kept for historical +*interest. + +This document explores a design for ownership support in Mojo. This learns from +other contemporary languages including C++, Rust, Swift and Val, providing a +novel blend that should integrate well with our base Python syntax, and be +powerful enough to express a wide range of kernel and systems programming +applications. + +Rust is the language that most people will naturally think of in this space, and +when compared to it, I expect that we will provide support for a wider range of +types than Rust, yet provide a more familiar programming model and friendly API +than it. While I assume that we will extend this to support lifetime types for +better generality and safety with reference semantic types, that design is not +included in this proposal. + +# Motivation and Background + +Modern systems languages aspire to provide memory safety, good low-level +performance, and allow advanced library developers to build expressive +high-level APIs that are easy to use by less experienced API users. + +In the case of Mojo, we have two specific “now” problems to solve: + +1. We need to provide a way to allocate memory for a Tensor-like type, and given +our existing support for raising exceptions, we need for them to be cleaned up. +Thus we need destructors. We also need to disable copying of this sort of type. + +2. We also need to implement transparent interop with CPython with an “object” +struct. This requires us to have copy constructors and destructors so we can +maintain the CPython reference count with an ergonomic Python-like model. + +Over time, we want Mojo to be a full replacement for the C/C++ system +programming use cases in Python, unifying the “two world problem” that Python +has with C. This is important because we want to have a unifying technology, +and because CPUs will always be an important accelerator (and are fully +general), and because our bet is that accelerators will get more and more +programmable over time. + +## Related Work + +I am not going to summarize the related work fully here, but I recommend folks +interested in this topic to read up on relevant work in the industry, including: + +1. C++’11 r-value references, move semantics, and its general modern programming +model. It is yucky and has lots of problems, but is table-stakes knowledge and +powers a tremendous amount of the industry. + +2. Have a programmer-level understanding of [Rust’s Memory Ownership +model](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html), and +read [the Rustonomicon](https://doc.rust-lang.org/nomicon/) end-to-end for bonus +points. + +3. Swift has a quite different approach which made some mistakes (Swift suffers +from pervasive implicit copying) but has nice things in its [initialization +design](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization), +[ARC +design](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html), +[exclusivity enforcement](https://www.swift.org/blog/swift-5-exclusivity/) +[[more +details](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md)], +etc. + +4. The [Val programming language](https://www.val-lang.dev/) is an early phase +research system that is learning from Swift and trying to provide a much simpler +programming model than Rust does with other advantages. It isn’t at all clear +if it will be expressive enough to be useful at this point though. + +C++ and Rust are the most widely known in this space and they make different +tradeoffs in defaults and what it means for a type author (ignoring lifetimes, +which C++ lacks and is therefore generally unsafe). A few random observations +that are related to the commentary below: + +- Rust in particular defaults to move’ing values but allows types to opt out of +that by implementing the Copy trait. + +- Rust’s type system doesn’t appear to support values like `std::atomic` which +require a pinned address in memory (Rust assumes it can transport things around +at will), nor does it support things like `llvm::SmallVector` which is movable, +but has an interior pointer so needs custom move constructors. + +- Because Rust defaults to moving everything around, it puts a huge amount of +pressure on memcpy and LLVM memcpy optimization (e.g. see Patrick Walton’s +recent work to improve this). In my opinion, this is a self imposed mistake +that we can correct structurally. + +- Rust could support C++-like moves from values that leave them +inert-and-to-be-destroyed, but does not do that. For lack of this, there is a +lot of complexity and unsafe APIs required (e.g. +[Drain](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.drain) +and other things) when you want to construct an array progressively or take +elements out of an array. + +## Relevant Existing Mojo Type System Features + +The Mojo design currently has a few basic features that are precursors to proper +ownership. The design below makes significant extensions and some changes to +it. + +### L-Values and R-Values + +Lvalues and Rvalues follow a conventional design found in many programming +languages: an Rvalue is an immutable value that can be passed around by copying +its bits by-value in an SSA register. An Lvalue is mutable, represented by its +address, and can be promoted to an Rvalue with a “load”. + +### Argument Conventions + +Functions can be declared to take any of their arguments by-reference, with an & +sigil in the function definition: + +```mojo + fn globalFn(a: Int, b&: Int): + b = a # ok + a = 4 # error + + struct Vec: + ... + fn push_back(self&, item: Int): ... # mutable self + fn size(self): ... # immutable self + + fn workWithVecs(a: Vec, b&: Vec): + use(a.size()) # ok + use(b.size()) # ok + + a.push_back(4) # Error, a isn't mutable + b.push_back(4) # ok +``` + +As shown, by-ref arguments are Lvalues and thus may be mutated, otherwise +arguments are passed by-value as immutable Rvalues. There is another +similar-but-different thing going on with “def” functions. By-value arguments +are passed by copy but are allowed to be mutable for better compatibility with +Python semantics: + +```mojo + def exampleDef(a: Int, b&: Int): + b = 4 # mutable as before. + + # passed by value into mutable copy. + # This change isn't visible in the caller. + a = 4 +``` + +The ‘def’ semantics are implemented by taking a value in by copy and introducing +a local ‘var’ that shadows the argument. It is therefore a purely local +transformation that we’ll ignore in the rest of this document. + +### Type initializers + +Mojo supports Python type initializers and currently allows them to participate +in implicit conversions: + +```mojo + struct Int: + var value : __mlir_type.index + + fn __init__(value: __mlir_type.index) -> Int: + return Int { value: value } +``` + +# Proposed Mojo Additions / Changes + +This proposal introduces a number of new concepts to cover the space of value +ownership and parameter passing conventions that Rust, Swift and C++ provide, +but in a different way. We layer on the features in individual groups. Let’s +explore them one piece at a time. + +## Extended parameter conventions + +We should extend the existing type system to support owning (aka moving, aka +consuming) parameter convention, immutable borrow semantics (“`&T"` in Rust, +“`const T&"` in C++) and mutable borrow semantics (“`&mut T`” in Rust, and +“`T&`” in C++). + +### Change & to mean mutable borrow or “inout” + +Right now, & is a C++-like reference semantic sigil, we should keep it, but +change it to mean a “mutable borrow” in Rust terminology or “inout” using Swift +terminology. This won’t require Mojo source changes, but will be a terminology +change inside of the compiler. The definitive initialization pass (below) will +need to enforce its correct semantics. + +Tangential to this proposal, we could require the use of `&` on the caller side +of arguments passing a mutable borrow to make it more clear in the caller code. +If we do this, I propose that it be a postfix operator, and use it for methods +for consistency: + +```mojo + swap(x.first&, y.first&) # Obvious what is being mutated + b&.push_back(42) # Obvious that b is mutated. +``` + +This proposal will not use this syntax below, and this is probably way too +onerous for methods, this is probably a bad idea. + +### Introduce “owned” argument conventions + +We need a way to specify that ownership of a value is being passed into the +function: + +```mojo + # This takes ownership of a unique vector, including its resources. + fn someFunction(owned v: Vec): + print(v) + v.push_back(42) # Ok: v is mutable because we own it +``` + +In the code above, we show “owned v” takes and owns a value from the caller. +Just like normal arguments, we would need a copy to get them as mutable: + +```mojo + fn anotherFunction(a: Int, owned v: Vec): + a += 1 # Error: a is immutable + var a2 = a # Copy the argument to make it mutable + a2 += 1 # Ok, a2 is mutable. + + v.push_back(a) # Ok, owned values are mutable + var v2 = v^ # Transfer v argument into a new var binding. + v2.push_back(a) # Also ok +``` + +This should be an owned _reference_ and thus lower to LLVM pointers, just like a +mutable reference does, unless the value is `@register_passable` (see below). +Not doing this would significantly impact our ability to model things like +`std::atomic` and `SmallVector` whose address is significant. See the +“extensions” at the bottom for why this will be efficient for trivial types like +integers. + +### Introduce “borrowed” argument conventions and change default + +Similarly we need the ability to specify that we are passing and returning an +immutable borrow, which is like a `'const &`’ in C++. The spelling of this +isn’t particularly important because it will frequently be defaulted, but we +need something concrete to explain the examples. + +```mojo + # This takes a borrow and return it. + # Returned references need lifetimes for safety of course. + fn printSizeAndReturn(borrowed v: Vec) -> borrowed Vec + print(v.size()) + return v +``` + +At the LLVM level, immutable references are passed by pointer, just like mutable +references. + +Note that C++ and Rust entered into a strange battle with programmers about how +to pass values by default. Templates generally use “`const&`” to avoid copies, +but this is an inefficient way to pass trivial types like “`int`”. We propose a +type level annotation to directly address, which allows us to use borrow far +more pervasively for arguments in the language. See the ‘extensions’ section +below. + +### Change the default argument and result conventions + +Now we have the ability to express ownership clearly, but we don’t want all code +everywhere to have words like `borrowed` on it: we want more progressive +disclosure of complexity and better defaults. + +The first piece of this is the default return value convention. The right +default convention for return values is to be owned: + +```mojo + # Result defaults to being an owned reference. + fn getVec() -> Vec: # Equivalent to "...) -> Vec^" + ... +``` + +because we otherwise have no way to return newly created values. Code can +override the return convention by using another sigil, e.g. `inout Vec` if a +mutable reference is required. + +We also need to decide what to do with arguments. We don’t want to copy +arguments by default (a mistake Swift made), because this requires types to be +copyable, and depends on unpredictable copy elision that makes performance and +COW optimization sad. I don’t think we want to depend on pass-by-move like Rust +did because Rust forces tons of things to be marked “&” pervasively, this +introduces a ton of `memcpy` operations, and Python programmers won’t think that +passing a value to a function makes it unavailable for use by other things by +default. + +In my opinion, the C++ got this almost right: `const&` is the right default +argument convention (and is optimized to register value passing in an opt-in +way, described below). This is good for both self and value arguments and is +what Swift semantically did with its +0 ownership convention in a bunch of +places. Consider this example: + +```mojo + struct Dictionary: + fn size(self) -> Int: ... +``` + +You don’t want to **copy the dictionary**, when calling size! This worked for +Swift because of ARC optimizations and that its Dictionary type was a small +thing implemented with COW optimizations, but this is very unpredictable. + +Passing arguments-by-borrow by default is also great because it eliminates the +pervasive need to do a load operation when converting Lvalues to Rvalues. This +is a huge improvement in the model, because “loading” a value is extremely +expensive when the value is large or cannot be loaded (e.g. variable sized +types, e.g. some languages representation for existential types and Rust’s DSTs, +which we may or may not want to support anyway). + +It also means that we can support types like `std::atomic` which need a +guaranteed ‘self’ address - natively and with no trouble - since we’re never +trying to implicitly load the value as a whole. + +## Adding support for value destruction + +Now that we have a notion of ownership, we can complete it by destroying values +when their lifetime has ended. This requires introducing the ability to declare +a destructor, and the machinery to determine when to invoke the destructor. + +### User defined destructors + +We should embrace the existing Python convention of implementing the `__del__` +method on a type. This takes ownership of the self value, so it should be +defined as taking `owned self`. Here’s a reasonable implementation of Vec’s +ctors and dtor, but without the push_back and associated methods (which are +obvious): + +```mojo + struct Vec: + var data: Pointer # I just made this type up. + var capacity: Int + var size: Int + fn __init__(inout self, capacity: Int): + self.data = Pointer.malloc(capacity) + self.capacity = capacity + self.size = 0 + + # default args will be nice some day + fn __new__(inout self): return Vec(1) + fn __del__(owned self): # owning reference to self. + # Any int values don't need to be destroyed. + self.data.free() +``` + +There is some nuance here and a special case that we need to handle in the +`__del__` method. Ideally, we should track the field sensitive liveness of the +‘self’ member that comes into del. This will allow us to handle sub-elements +that are individually consumed, safely handle exceptions that early-exit from +the destructor etc. This is something that Swift gets right that Rust +apparently doesn’t. + +With respect to the simple definition of Vec above, it is enough to define a +safe vector of integers which is creatable, destroyable, can be passed by +borrowed and mutable reference, but isn’t enough to support movability or +copyability. We’ll add those later. + +### When do we invoke destructors for value bindings? + +Now that we have a way to define a destructor for a value, we need to invoke it +automatically. Where do we invoke the destructor for a local value binding? +Two major choices exist: + +1. End of scope, ala C++ (and I think Rust). +2. After last use, ala Swift and Val (but Val has a better model). + +The difference can be seen in cases like this: + +```mojo + fn bindingLifetimeExample(): + var vec = Vec() + vec.push_back(12) + use(vec) + # Val/Swift destroys 'vec' here. + do_lots_of_other_stuff_unrelated_to_vec() + # C++/Rust destroy vec here. +``` + +I would advocate for following the Swift model. It reduces memory use, and I +haven’t seen it cause problems in practice - it seems like the right default. +Furthermore, this dovetails well with ownership, because you want (e.g.) +immutable borrows to die early so you can form mutable borrows in other +statements. It also makes the “form references within a statement” special case +in C++ go away. + +The tradeoff on this is that this could be surprising to C++ programmers, +something that Swift faced as well. The balance to that is that GC languages +with finalizers are not used for RAII patterns, and Python has first-class +language support for RAII things (the `with` statement). + +There are specific cases like RAII that want predictable end-of-scope +destruction, so you end up needing a `@preciseLifetime` decorator on the struct +or use closures - [both work +fine](https://developer.apple.com/documentation/swift/withextendedlifetime(_:_:)-31nv4). + +NOTE: This was pushed forward during implementation to the "ASAP" model that +Mojo uses. + +### Taking a value from a binding + +The other thing you may want to do is to intentionally end a binding early, +transferring ownership of the bound value out as an owned rvalue. Swift and +Rust both support mutable value lifetimes with holes in them, and ending +immutable bindings early (Rust with the `drop(x)` operator or by moving out of +the binding, Swift with the recently proposed +[consume/take/move](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md) +operation). + +I propose supporting this with the `^` postfix operator, e.g.: + +```mojo + fn takeVec(owned v: Vec): ... + + fn showEarlyBindingEnd(): + var vec = Vec() + vec.push_back(12) + takeVec(vec^) # Ownership of vec is transferred to takeVec. + do_lots_of_other_stuff_unrelated_to_vec() + + var vec2 = Vec() + vec2.push_back(12) + ... + _ = vec2^ # force drop vec2. +``` + +This is postfix so it composes better in expressions, e.g. +“`someValue^.someConsumingMethod()`”. + +### Supporting “taking” a value, with a convention (+ eventually a Trait) + +I believe it is important for common types to support a “destructive take” to +support use-cases where you want to std::move an element out of the middle of a +`std::vector`. C++ has `std::move` and move constructors for this, and Rust has +a ton of complexity to work around the lack of this. Swift doesn’t appear to +have a story for this yet. I think we just use a method convention (eventually +formalized as a trait) where types who want it define a `take()` method: + +```mojo + struct Vec: + ... + fn __moveinit__(inout self, inout existing): + # Steal the contents of 'existing' + self.data = existing.data + self.capacity = existing.capacity + self.size = existing.size + + # Make sure 'existing's dtor doesn't do bad things. + existing.data = None + existing.size = 0 + existing.capacity = 0 +``` + +This is analogous to defining a move constructor in C++. Note that you only +need to define this if you want to support this operation, and we eventually +should be able to synthesize this as a default implementation of the “Takable” +trait when we build out traits and metaprogramming features. + +## Value Lifetime Enforcement + +Now that we have all the mechanics in place, we actually have to check and +enforce lifetime in the compiler. This entails a few bits and pieces. + +### Implement a “Definitive Initialization” Like Pass: CheckLifetimes + +The first thing we need is a dataflow pass that tracks the initialization status +of local bindings. The basic mechanics needed here are implemented in the Swift +Definitive Initialization pass, and a lot of the mechanics are well described in +the [Drop Flags section of the +Rustonomicon](https://doc.rust-lang.org/nomicon/drop-flags.html) and [slide 135+ +in this +talk](https://www.llvm.org/devmtg/2015-10/slides/GroffLattner-SILHighLevelIR.pdf). +This is a combination of static analysis and dynamically generated booleans. + +When building this, we have the choice to implement this in a field sensitive +way. I believe that this is a good thing to do in the medium term, because that +will allow making `__new__` and `__del__` methods much easier to work with in +common cases, and will compose better when we get to classes. That said, we +should start with simple non-field-sensitive cases and extend it over time. +This is what we did when bringing up Swift and it worked fine. + +### Implement support for variable exclusivity checking + +While it isn’t a high priority in the immediate future, we should also add +support for variable exclusivity checking to detect dynamic situations where +aliases are formed. See the [Swift +proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md) +for details on the issues involved here. Mojo will be working primarily with +local variables in the immediate future, so we can get by with something very +simple for the immediate future. + +### Synthesize destructors for structs + +The other thing necessary in the basic model is for the destructors of field +members to be run as part of `__del__` methods on structs. We don’t want people +to have to write this manually, we should synthesize a `__del__` when needed. +For example in: + +```mojo + struct X: + var a: T1 + var b: T2 + + fn __del__(owned self): # This should be synthesized. + _ = self.a^ + _ = self.b^ +``` + +## Extensions to make the programming model nicer + +With the above support, we should have a system that is workable to cover the +C++/Rust use-cases (incl `std::atomic` and `SmallVector`), handle the motivating +Tensor type and Python interop, as well as provide a safe programming model +(modulo dangling reference types which need lifetimes to support). That said, +we still won’t have a super nice programming model, this includes some “table +stakes” things we should include even though they are not strictly necessary. + +In particular, the support above completely eliminated the need to copy and move +values, which is super pure, but it would be impractically painful to work with +simple types that have trivial copy constructors. For example: + +```mojo + fn useIntegers(a: Int): + var b = a+4 # ok, b gets owned value returned by plus operator. + let c = b # error, cannot take ownership from an lvalue. + let c2 = b.copy() # Ok, but laughable for Int. +``` + +It is worth saying that the “c = b” case is something that we explicitly want to +prohibit for non-trivial types like vectors and dictionaries: Swift implicitly +copies the values and relies on COW optimization and compiler heroics (which are +not amazingly great in practice) to make it “work”. Rust handles it by moving +the value away, which breaks value semantics and a programmer model that people +expect from Python. + +It is better for vectors and Dictionary’s (IMO) to make this a compile time +error, and say “this non-copyable type cannot be copied”. We can then +standardize a `b.copy()` method to make the expense explicit in source code. + +### Copy constructors for implicitly copyable types + +The solution to both of these problems is to allow types to opt-in to +copyability (as in the Rust `Copy` trait). The obvious signature for this in +Mojo seems to be a `__copyinit__` implementation (eventually formalized as a +trait): + +```mojo + struct Int: + var value: __mlir_type.index + fn __copyinit__(inout self, borrowed existing: Int): + self.value = existing.value +``` + +Given this new initializer, a type that implements this is opt-ing into being +implicitly copied by the compiler. This (re)enables lvalue-to-rvalue conversion +with a “load” operation, but makes it explicit in user source code. It allows +integers to work like trivial values, and allows the library designer to take +control of what types support implicit copies like this. This is also required +for the Python interop “object” type, since we obviously want `x = y` to work +for Python objects! + +One nice-to-have thing that we should get to eventually (as we build out support +for traits and metaprogramming) is `Copyable` trait with a default +implementation. This would allow us to manually provide a copy constructor if +we want above, or get a default synthesized one just by saying that our struct +is `Copyable`. See the appendix at the end of the document for more explanation +of how this composes together. + +All together, I believe this will provide a simple and clean programming model +that is much more predictable than the C++ style, and is more powerful than the +Swift or Rust designs, which don’t allow custom logic. + +### Opting into pass-by-register parameter convention + +The final problem we face is the inefficiency of passing small values +by-reference everywhere. This is a problem that is internalized by C++ +programmers through common wisdom (“pass complex values like `std::vector` as +`const& or rvalue-ref` but trivial values like `int` by value!”), but ends up +being a problem for some generic templated code - e.g. `std::vector` needs to +declare many things as being passed by `const&` or rvalue reference, which +becomes inefficient when instantiated for trivial types. There are ways to deal +with this in C++, but it causes tons of boilerplate and complexity. + +The key insight I see here is that the decision is specific to an individual +type, and should therefore be the decision of the type author. I think the +simple way to handle this is to add a struct decorator that opts the struct into +being passed by owning copy, equivalent to the Rust Copy convention: + +```mojo + @register_passable + struct Int: + ... +``` + +This decorator would require that the type have a copy constructor declared, and +it uses that copy constructor in the callee side of an API to pass arguments +by-register and return by-register. This would lead to an efficient ABIs for +small values. + +This decorator should only be used on small values that makes sense to pass in +registers or on the stack (e.g. 1-3 machine registers), and cannot be used on +types like `llvm::SmallVector` that have interior pointers (such a type doesn’t +make sense to pass by-register anyway!). + +# Conclusion + +This proposal attempts to synthesize ideas from a number of well known systems +into something that will fit well with Mojo, be easy to use, and easy to teach - +building on the Python ideology of “reducing magic”. It provides equivalent +expressive power to C++, while being a building block to provide the full power +for Rust-style lifetimes. + +## Parameter Conventions Summary + +This is the TL;DR: summary of what I think we end up with: + +```mojo + fn borrow_it(a: X) # takes X as borrow (sugar). + fn borrow_it(borrowed a: X) # takes X as borrow. + fn take_it(owned a: X) # takes owned X + fn ref_it(inout a: X) # takes mutable reference to X + fn register_it(a: Int) # by copy when Int is register-passable. + + fn ret_owned(self) -> X: # Return an owned X (sugar). + fn ret_owned(self) -> owned X: # Return an owned X. + fn ret_borrow(self) -> borrowed X: # Return an X as a borrow + fn ret_ref(self) -> inout X: # Return an X as a mutable ref + fn ret_register(self) -> Int: # Return by copy when Int is register-passable +``` + +## Extension for Lifetime types + +Lifetimes are necessary to support memory safety with non-trivial correlated +lifetimes, and have been pretty well proven in the Rust world. They will +require their own significant design process (particularly to get the defaulting +rules) and will benefit from getting all of the above implemented. + +That said, when we get to them, I believe they will fit naturally into the Mojo +and MLIR design. For example, you could imagine things like this: + +```mojo + # Take a non-copyable SomeTy as a borrow and return owned copy + fn life_ex1['a: lifetime](value: 'a SomeTy) -> SomeTy: + return value.copy() + + # Take a non-copyable SomeTy and return the reference + fn life_ex2['a: lifetime](value: 'a SomeTy) -> borrowed 'a SomeTy: + return value +``` + +This is not a concrete syntax proposal, just a sketch. A full design is outside +the scope of this proposal though, it should be a subsequent one. + +# Appendix: Decorators and Type Traits + +Above we talk loosely about decorators and type traits. A decorator in Python +is a modifier for a type or function definition. A Trait in Rust (aka protocol +in Swift, aka typeclass in Haskell) is a set of common behavior that unifies +types - sort of like an extended Java interface. + +Let’s see how these two concepts can come together in the future assuming we get +Swift/Rust-style traits and extend Python’s decorator concept with +metaprogramming features enabled by Mojo. + +Traits include “requirements”: signatures that conforming types are required to +have, and may also include default implementations for those. The type can +implement it manually if they want, but can also just inherit the default +implementation if not. Let’s consider copy-ability. This isn’t a standard +library proposal, but we could implement some copy traits like this: + +```mojo + trait Copyable: + # Just a signature, no body. + fn copy(self) -> Self: ... + + trait ImplicitlyCopyable(Copyable): # Could use a better name :) + # A __copyinit__ is required, and this is the default implementation. + fn __copyinit__(inout self, borrowed existing: Self): + self = existing.copy() +``` + +Type may conform to the `Copyable` trait, which allows generic algorithms to +know it has a `copy()` method. Similarly, they may conform to +`ImplicitlyCopyable` to know it is implicitly copable (supports “`let a = b`”). +`ImplicitlyCopyable` requires the type to have a `copy()` method (because +`ImplicitlyCopyable` refines `Copyable`) and a `__copyinit__` method with the +specified signatures, and also provides a default implementation of the +`__copyinit__` method. + +This allows types to use it like this: + +```mojo + struct Int(ImplicitlyCopyable): + var value: __mlir_type.index + fn copy(self: Self) -> Self: + return Int{value: self.value} + + # Don't have to write this, we get a default impl from ImplicitlyCopyable + # fn __copyinit__(inout self, borrowed existing: Self): + # self = existing.copy() +``` + +This allows clients to implement a simple `copy()` method, but get the internal +machinery for free. + +The decorator is a different thing that layers on top of it. Decorators in +Python are functions that use metaprogramming to change the declaration they are +attached to. Python does this with dynamic metaprogramming, but we’ll use the +interpreter + built-ins operations to also enable static metaprogramming in +Mojo. + +I'm imagining that this will allow someone to write just: + +```mojo + @implicitlyCopyable + struct Int: + var value : __mlir_type.index +``` + +And the `implicitlyCopyable` function (which implements the decorator) would be +implemented to do two things: + +1. When it understands all the stored properties of a copy, because they are +built-in MLIR types like index, or because they themselves conform to at least +`Copyable`, it synthesizes an implementation of a `copy()` method that builds a +new instance of the type by invoking the `copy()` member for each element. + +2. It adds conformance to the `ImplicitlyCopyable` trait, which provides the +`__copyinit__` method above. + +This is all precedented in languages like Swift and Rust, but they both lack the +metaprogramming support to make the decorator synthesis logic implementable in +the standard library. Swift does this for things like the Hashable and +`Equatable` protocols. I believe that Mojo will be able to support much nicer +and more extensible designs. + +NOTE: The `@value` decorator provides some of this now. From 5302de9adf805eff2df00d8036fa048c0a3151c4 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 2 May 2024 16:26:11 -0700 Subject: [PATCH 0387/2019] [mojo-stdlib] Remove more `-> Self` initializers, NFC. (#39181) This continues the purge. MODULAR_ORIG_COMMIT_REV_ID: 7e5a4b071a777474e7ba67c0afce325473ab7642 --- stdlib/src/builtin/_closure.mojo | 16 ++++++---------- stdlib/src/builtin/coroutine.mojo | 8 ++------ stdlib/src/builtin/int.mojo | 4 ++-- stdlib/src/builtin/int_literal.mojo | 20 ++++---------------- stdlib/src/builtin/string_literal.mojo | 7 ++----- stdlib/src/sys/ffi.mojo | 15 +++++---------- 6 files changed, 21 insertions(+), 49 deletions(-) diff --git a/stdlib/src/builtin/_closure.mojo b/stdlib/src/builtin/_closure.mojo index 43cda6b2da..cb2d35c45b 100644 --- a/stdlib/src/builtin/_closure.mojo +++ b/stdlib/src/builtin/_closure.mojo @@ -19,18 +19,14 @@ struct __ParameterClosureCaptureList[fn_type: AnyRegType, fn_ref: fn_type]: # Parameter closure invariant requires this function be marked 'capturing'. @parameter @always_inline - fn __init__() -> Self: - return Self { - value: __mlir_op.`kgen.capture_list.create`[callee=fn_ref]() - } + fn __init__(inout self): + self.value = __mlir_op.`kgen.capture_list.create`[callee=fn_ref]() @always_inline - fn __copyinit__(existing: Self) -> Self: - return Self { - value: __mlir_op.`kgen.capture_list.copy`[callee=fn_ref]( - existing.value - ) - } + fn __copyinit__(inout self, existing: Self): + self.value = __mlir_op.`kgen.capture_list.copy`[callee=fn_ref]( + existing.value + ) @always_inline fn __del__(owned self): diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 754c77a408..aa77884d0d 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -248,23 +248,19 @@ struct RaisingCoroutine[type: AnyRegType]: return self._get_promise().bitcast[ctx_type]() - 1 @always_inline - fn __init__(handle: Self._handle_type) -> Self: + fn __init__(inout self, handle: Self._handle_type): """Construct a coroutine object from a handle. Args: handle: The init handle. - - Returns: - The constructed coroutine object. """ - var self = Self {_handle: handle} + self = Self {_handle: handle} var parent_hdl = __mlir_op.`pop.coroutine.opaque_handle`() self._get_ctx[_CoroutineContext]().store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl } ) - return self^ @always_inline fn __del__(owned self): diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 7e03ffe811..283b2b3c38 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -287,7 +287,7 @@ struct Int( ) @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Int: + fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: """Construct Int from the given Index value. Args: @@ -303,7 +303,7 @@ struct Int( ) @always_inline("nodebug") - fn __init__(value: IntLiteral) -> Int: + fn __init__(value: IntLiteral) -> Self: """Construct Int from the given IntLiteral value. Args: diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 6a22ebfcb7..38322442ed 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -49,11 +49,11 @@ struct IntLiteral( """Default constructor. Returns: - The constructed Self object. + The constructed IntLiteral object. """ - return IntLiteral( - __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` - ) + return Self { + value: __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` + } @always_inline("nodebug") fn __init__(value: __mlir_type.`!kgen.int_literal`) -> Self: @@ -67,18 +67,6 @@ struct IntLiteral( """ return Self {value: value} - @always_inline("nodebug") - fn __init__(value: IntLiteral) -> Self: - """Construct IntLiteral from another one. - - Args: - value: The init value. - - Returns: - The constructed IntLiteral object. - """ - return Self {value: value.value} - @always_inline("nodebug") fn __int__(self) -> Int: """Convert from IntLiteral to Int. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 4edbee452b..c4fc0e428c 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -51,16 +51,13 @@ struct StringLiteral( """The underlying storage for the string literal.""" @always_inline("nodebug") - fn __init__(value: Self.type) -> Self: + fn __init__(inout self, value: Self.type): """Create a string literal from a builtin string type. Args: value: The string value. - - Returns: - A string literal object. """ - return StringLiteral {value: value} + self.value = value @always_inline("nodebug") fn __len__(self) -> Int: diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index d8729fff36..472b4e54cd 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -52,27 +52,22 @@ struct DLHandle(CollectionElement, Boolable): # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline - fn __init__(path: String, flags: Int = DEFAULT_RTLD) -> Self: + fn __init__(inout self, path: String, flags: Int = DEFAULT_RTLD): """Initialize a DLHandle object by loading the dynamic library at the given path. Args: path: The path to the dynamic library file. flags: The flags to load the dynamic library. - - Returns: - The constructed handle object. """ @parameter if not os_is_windows(): - return Self { - handle: external_call["dlopen", DTypePointer[DType.int8]]( - path._as_ptr(), flags - ) - } + self.handle = external_call["dlopen", DTypePointer[DType.int8]]( + path._as_ptr(), flags + ) else: - return Self {handle: DTypePointer[DType.int8]()} + self.handle = DTypePointer[DType.int8]() # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline From f43b16b3e5676017494e727278efae73cbfc848a Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 2 May 2024 17:59:15 -0700 Subject: [PATCH 0388/2019] [KGEN] Move `CoroutineType` to the `co` dialect (NFC) (#39180) This PR moves the `!pop.coroutine` type to th `co` dialect and renames it to the `!co.routine` type. Otherwise, this patch is NFC. MODULAR_ORIG_COMMIT_REV_ID: 3ff23ac4b4747d8145694dc725c4e36cebb68f47 --- stdlib/src/builtin/coroutine.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index aa77884d0d..c5fcb1c79a 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -82,7 +82,7 @@ struct Coroutine[type: AnyRegType]: type: Type of value returned upon completion of the coroutine. """ - alias _handle_type = __mlir_type[`!pop.coroutine<() -> `, type, `>`] + alias _handle_type = __mlir_type[`!co.routine<() -> `, type, `>`] alias _promise_type = __mlir_type[`!kgen.struct<(`, type, `)>`] var _handle: Self._handle_type @@ -201,7 +201,7 @@ struct RaisingCoroutine[type: AnyRegType]: alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] alias _handle_type = __mlir_type[ - `!pop.coroutine<() throws -> `, Self._var_type, `>` + `!co.routine<() throws -> `, Self._var_type, `>` ] alias _promise_type = __mlir_type[`!kgen.struct<(`, Self._var_type, `)>`] var _handle: Self._handle_type From 0032993f8d0828501ecfbb3114bacf4541cc6dc8 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 2 May 2024 19:09:13 -0600 Subject: [PATCH 0389/2019] Revert "[SDLC] Add mojo proposals to Copybara" (#39216) Reverts modularml/modular#39198. Breaking mojo nightly and can't control copybara options. Example failure is [Internal link] MODULAR_ORIG_COMMIT_REV_ID: b360249b068784e255b7a658f1077970799c923d --- proposals/README.md | 8 - proposals/byte-as-uint8.md | 44 -- proposals/inferred-parameters.md | 195 ----- proposals/lifetimes-and-provenance.md | 423 ----------- proposals/lifetimes-keyword-renaming.md | 147 ---- proposals/mojo-and-dynamism.md | 253 ------- proposals/project-manifest-and-build-tool.md | 104 --- proposals/remove-let-decls.md | 153 ---- proposals/value-ownership.md | 735 ------------------- 9 files changed, 2062 deletions(-) delete mode 100644 proposals/README.md delete mode 100644 proposals/byte-as-uint8.md delete mode 100644 proposals/inferred-parameters.md delete mode 100644 proposals/lifetimes-and-provenance.md delete mode 100644 proposals/lifetimes-keyword-renaming.md delete mode 100644 proposals/mojo-and-dynamism.md delete mode 100644 proposals/project-manifest-and-build-tool.md delete mode 100644 proposals/remove-let-decls.md delete mode 100644 proposals/value-ownership.md diff --git a/proposals/README.md b/proposals/README.md deleted file mode 100644 index 392782a030..0000000000 --- a/proposals/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Mojo🔥 engineering design proposals - -This directory contains ad-hoc design proposals put together by the Mojo -engineering team. These are meant to help shape discussion and refine the -design of various subsystems, but typically become obsolete and incorporated into -more canonical documentation when the implementation work concludes. There is -no attempt to keep these up-to-date as the language evolves, so they are -more for historical reference than as a user-guide for the language. diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md deleted file mode 100644 index 342007be80..0000000000 --- a/proposals/byte-as-uint8.md +++ /dev/null @@ -1,44 +0,0 @@ -# Standardise the representation of byte sequence as a sequence of unsigned 8 bit integers - -At this point in time, a sequence of bytes is often represented as a sequence of -signed 8 bit integers in Mojo standard library. Most noticeable example is the -underlying data of string types `String`, `StringLiteral`, `StringRef` and -`InlinedString`, but also APIs like for example the hash function `fn -hash(bytes: DTypePointer[DType.int8], n: Int) -> Int:`. - -## Motivation - -Logically a byte is an integer value between `0` and `255`. Lots of algorithms -make use of arithmetics ground by this assumption. A signed 8 bit integer on -the contrary represents values between `-128` and `127`. This introduces very -subtle bugs, when an algorithm written for unsigned 8 bit integer is used on a -signed 8 bit integer. - -Another motivation for this change is that Mojo aims to be familiar to Python -users. Those Python users are familiar with the `bytes` class, which itself is -working with values between `0` and `255`, not values between `-128` and `127`. - -## Examples - -### Division - -A value `-4` represented as `Int8` has the same bit pattern as value `252` -represented as `UInt8`. `-4 // 4` equals to `-1` (`bx11111111`), where `252 // -4` equals to `63` (`bx00111111`) as we can see the bit patterns are different. - -### Bit shift - -Values `-1` and `255` have the same bit pattern as `Int8` and `UInt8` -`bx11111111` but `-1 >> 1` results in `-1` (same bit pattern), where `255 >> 1` -results in `127` (`bx01111111`) - -## Proposal - -A text based search for `DTypePointer[DType.int8]` and `Pointer[Int8]` on -current open-sourced standard library revealed 29 results for `Pointer[Int8]` -and 78 results for `DTypePointer[DType.int8]`. Replacing -`DTypePointer[DType.int8]` with `DTypePointer[DType.uint8]` and `Pointer[Int8]` -with `Pointer[UInt8]` on case by case bases is a substantial refactoring effort, -but it will prevent a certain class of logical bugs (see -). As it is a breaking change in -sense of API design, it is sensible to do the refactoring as soon as possible. diff --git a/proposals/inferred-parameters.md b/proposals/inferred-parameters.md deleted file mode 100644 index 704f590bb5..0000000000 --- a/proposals/inferred-parameters.md +++ /dev/null @@ -1,195 +0,0 @@ -# Inferring Parameters from Other Parameters - -A common feature in programming language with generics is the ability to infer -the value of generics/templates/parameters from the argument types. Consider -C++: - -```cpp -template -void inferMe(T x) {} - -int x = 1; -inferMe(x); -// Equivalent to -inferMe(x); -``` - -Mojo is a parametric language and also supports this feature in a variety of use -cases that make code significantly less verbose: - -```python -fn infer_me[dt: DType, size: Int](x: SIMD[dt, size]): pass - -infer_me(Int32()) -# Equivalent to -infer_me[DType.int32, 1](Int32()) -``` - -But Mojo pushes these needs a step further. As a language that encourages heavy -parameterization, dependent types are very common throughout the language. -Consider: - -```python -fn higher_order_func[dt: DType, unary: fn(Scalar[dt]) -> Scalar[dt]](): pass - -fn scalar_param[dt: DType, x: Scalar[dt]](): pass -``` - -Language users commonly encounter cases where dependent types could infer their -parameter values from other parameters in the same way from argument types. -Consider `scalar_param` in the example above: `dt` could be inferred from the -type of `x` if `x` were passed as an argument, but we have no syntax to express -inferring it from `x` as a parameter since the user is required to pass `dt` as -the first parameter. - -```python -scalar_param[DType.int32, Int32()]() # 'dt' parameter is required -``` - -This has been requested multiple times in various forms, especially given the -new autoparameterization feature. The current tracking feature request: - -- - -## Proposal - -In the above example, we want to be able to infer `dt` instead of explicitly -specifying it: - -```python -scalar_param[Int32()]() -``` - -Laszlo Kindrat and I proposed several options to remedy this and members of the -“Mojo Language Committee” met to discuss these ideas, summarized below. - -We decided to move forward with the following option. Mojo will introduce a new -keyword, `inferred`, as a specifier for parameters only. `inferred` parameters -must precede all non-inferred parameters in the parameter list, and they -**cannot** be specified by a caller — they can **only** be inferred from other -parameters. This allows us to express: - -```python -fn scalar_param[inferred dt: DType, x: Scalar[dt]](): pass - -scalar_param[Int32()]() # 'dt' is skipped and 'Int32()' is bound to 'x' -``` - -Where `dt` is inferred from `x`. The decision to choose a keyword instead of -introducing a new punctuation character [like Python does for keyword-only -arguments](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) -is because a keyword clearly indicates the intent of the syntax, and it’s easy -to explain in documentation and find via internet search. - -## Aside: Inferring from Keyword Parameters - -Related but separate to the proposal, we can enable parameter inference from -other parameters using keyword arguments. This allows specifying function (and -type) parameters out-of-order, where we can infer parameters left-to-right: - -```python -scalar_param[x=Int32()]() # 'dt' is inferred from 'x' -``` - -We should absolutely enable this in the language, since this does not work -today. However, with respect to the above proposal, in many cases this still -ends up being more verbose than one would like, especially if the parameter name -is long: - -```python -scalar_param[infer_stuff_from_me=Int32()]() - -# One would like to write: -scalar_param[Int32()]() -``` - -So this feature is orthogonal to the `inferred` parameter proposal. - -## Alternatives Considered - -Several alternative ideas were considered for this problem. - -### Non-Lexical Parameter Lists - -This solution would alter the name resolution rules inside parameter lists, -allowing forward references to parameters within the same list. The above -example would be expressed as: - -```python -fn scalar_param[x: Scalar[dt], dt: DType](): pass -``` - -Where any parameter is inferrable from any previous parameter. The benefits of -this approach are that the order of parameters at the callsite match the order -in the declaration: `scalar_param[Int32()]()` - -This alternative was rejected because: - -1. Non-lexical parameters are potentially confusing to users, who normally - expect named declarations to be lexical. Relatedly, we are moving towards - removing non-lexical parameters in general from the language. - -2. This would incur a huge implementation burden on the compiler, because the - type system needs to track the topological order of the parameters. - -### New Special Separator Parameter - -This solution is fundamentally the same as the accepted proposal, but differs -only in syntax. Instead of annotating each parameter as `inferred`, they are -separated from the rest using a new undecided sigil (`%%%` is a placeholder): - -```python -fn scalar_param[dt: DType, %%%, x: Scalar[dt]](): pass -``` - -The benefit of this approach is this matches the [Python -syntax](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) -for separating position-only and keyword-only parameters. It also structurally -guarantees that all infer-only parameters appear at the beginning of the list. - -This alternative was rejected because: - -1. There was no agreement over the syntax, and any selected sigil would - introduce additional noise into the language. - -2. `inferred` clearly indicates the intent of the syntax, and can be found via - internet search, and is overall easier to explain syntax than introducing a new - argument separator. - -### Special Separator Parameter at the End - -This is a variation on the above, where the infer-only parameters would appear -at the end of the parameter list, and subsequent parameters would be allowed to -be non-lexical: - -```python -fn scalar_param[x: Scalar[dt], %%%, dt: DType](): pass -``` - -The benefit of this approach is that the parameters appear in the same position -at the callsite. This alternative was rejected for a combination of the reasons -for rejecting a new separator and non-lexical parameters. - -### Segmented Parameter Lists - -This proposal would allow functions to declare more than one parameter list and -enable right-to-left inference of the parameter “segments”. The above would be -expressed as: - -```python -fn scalar_param[dt: DType][x: Scalar[dt]](): pass -``` - -The callsite would look like - -```python -scalar_param[Int32()]() -``` - -And call resolution would match the specified parameter list to the last -parameter list and infer `dt`. This proposal was rejected because - -1. The right-to-left inference rules are potentially confusing. - -2. This is an overkill solution to the problem, because this opens to door to -arbitrary higher-order parameterization of functions. diff --git a/proposals/lifetimes-and-provenance.md b/proposals/lifetimes-and-provenance.md deleted file mode 100644 index 176c751f3d..0000000000 --- a/proposals/lifetimes-and-provenance.md +++ /dev/null @@ -1,423 +0,0 @@ -# Provenance Tracking and Lifetimes in Mojo - -As of mid-May 2023, Mojo has full support for ownership (including move -semantics, borrows and transfers, mutability, ASAP destruction of values, and -member synthesis). This provides more expressiveness than many languages, but does -not meet the expectations of Rust and C++ programmers because it is impossible -to **return references** and **put references in structs**. - -This makes Mojo unable to express common patterns like `StringRef` in the LLVM -APIs because it is a struct containing a reference, and this makes our `Pointer` -type a massively unsafe API. - -## Goals of this document - -This document explores the first step in adding lifetimes to Mojo: what changes -we’ll have to introduce, some thinking on syntax we may want to use, and how -this may want us to reconsider existing design decisions. This is written in -the style of the "[Value ownership design for Mojo](value-ownership.md)" -document from January. - -This document is really just the “first step” of lifetimes. Rust includes a few -more exotic features, including [subtyping constraints between -lifetimes](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-subtyping-constraints-between-lifetimes-15), -[equality constraints between lifetime -parameters](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-equality-constraints-between-lifetime-parameters-16), -[unbounded -lifetimes](https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html) and -perhaps other features. We don't have all the mechanics of a generic system and -trait system yet to tie into - and it makes sense to lazily add complexity based -on need - so these are not included in this initial design. - -## Context - -Mojo already has much of the required infrastructure in place to support -lifetimes: we now have references, we just need to be able to return them. -Similarly, the Mojo parameter system provides a powerful way to model and -propagate lifetime information around in our type system. We have a -CheckLifetime compiler pass which infers lifetimes, diagnosing use of -uninitialized values and inserting destructor calls. - -Similarly, we can learn a lot from Rust’s design for lifetimes. That said, the -ownership system in Mojo is quite different than the one in Rust. In Rust, -scopes define the implicit lifetimes of values and references, and lifetimes are -used to verify that uses of the value happen when the value is still alive. Mojo -flips this on its head: values start their life when defined, but end their life -after their last use. This means that in Mojo, a lifetime reference **extends -the liveness of a value** for as long as derived references is used, which is a -bit different than Rust. - -For example, we expect this to behave like the comments indicate: - -```mojo - var some_str = String("hello") - - # The StringRef contains a reference to the some_str value - var some_str_ref = StringRef(some_str) - - # Last use of some_str, but don't destroy it because there is a use of - # the reference below! - use(some_str) - - # References must be propagatable through methods. - some_str_ref = some_str_ref.drop_front().drop_back() - print(some_str_ref) # Prints "ell" - # some_str destroyed here after last reference to it -``` - -The major thing that Mojo (and Rust) need from lifetimes is what is called -“local reasoning”: We need to be able to reason about the memory behavior of a -call just by looking at its signature. We cannot have to look into the body of a -function to understand its effects, and a function cannot know about its callers -to reason about the behavior of its arguments. Similarly, when accessing a -member `a.ref` that is a reference, we need to know what lifetime is being used -in the context of `a`. - -Because of this, the lifetimes in a function signature are something of a -"transfer function" that expresses mappings from input lifetimes to returned -lifetimes, and that allows reasoning about the lifetimes of field references. -Mojo already has a powerful parameter system that allows it to express these -sorts of relationships, so this all plugs together. - -### What to name / how to explain this set of functionality? - -Rust has a few things going on that are tightly bound and somewhat confusing: it -has scoping, lifetimes, and lifetime holes. It has a borrow checker that -enforces the rules, all together this is its ownership system. - -When it comes to the “lifetimes” part of the puzzle, it seems better to clarify -two very different concepts: on the one hand, stored **values** in memory each -have a conceptual “lifetime” that starts when the value is defined and ends at -the last use - this is where the destructor call is inserted. Because each -declared variable has an independently tracked lifetime, each also needs an -implicitly declared “lifetime parameter” that is tracked in the Mojo type -system. - -On the other hand, when reasoning about parametric functions, the type signature -defines a transfer function that expresses the “provenance” of the result values -from the function and how they relate to input values. This relationship is a -transfer function from the input lifetime parameters to output lifetime -parameters, and can be somewhat more complicated. The framing of “provenance -tracking” may be more general conceptually than “lifetime tracking” which seems -specific to the lifetime parameters. - -## Mojo Reference + Lifetime Design - -Lifetimes enable us to use references as first class types. This means they -can occur in nested positions: You can now have a reference to a reference, you -can have an array of references, etc. At the machine/execution level, a -reference is identical to a pointer, but the reference type system enables an -associated lifetime, tracking of mutability etc. - -### Writing a lifetime bound reference - -The first question is how to spell this. Rust uses the `'a` syntax which is -pretty unconventional and (weirdly but) distinctly Rust. For example, here are -some simple Rust functions: - -```rust -// This is Rust -fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {..} -fn longest2<'a>(x: &'a mut str, y: &'a mut str) -> &'a mut str {..} -``` - -Mojo already have a general set of values in our generic signature list: we just -need to "parameterize" references and make them explicit. For now, we recommend -using new `ref` and `mutref` keywords parameterized on a lifetime, and a new -`Lifetime` type. For example: - -```mojo -# Proposed Mojo syntax. -fn longest[a: Lifetime](x: ref[a] String, - y: ref[a] String) -> ref[a] String: - return x if len(x) >= len(y) else y - -fn longest2[a: Lifetime](x: mutref[a] String, - y: mutref[a] String) -> mutref[a] String: ... -``` - -There are many other options for syntax that we can consider, but this is simple -and will get us going until we have more experience. For example, we can make -any of these work: - -```mojo -fn f(x: ref[a] String, y: mutref[a] String): ... # Homage to Rust! -fn f(x: ref(a) String, y: mutref(a) String): ... # Homage to Rust! -fn f(x: ref a String, y: mutref a String): ... # Homage to Rust! -fn f(x: ref 'a String, y: mutref 'a String): ... # Homage to Rust! -fn f(x: 'a String, y: mut 'a String): ... # Homage to Rust! -``` - -The argument for square brackets vs parens is if we like the explanation that -we’re -"parameterizing the reference with a lifetime". However, remember types can -also be parametric, and those are spelled with square brackets **after** the -type name, so parens may be better to make these more syntactically distinct. - -The spelling in structs should flow naturally from this: - -```mojo -struct StringRef[life: Lifetime]: - var data : Pointer[UInt8, life] - var len : Int -``` - -Being first class types, we will naturally allow local references: these should -also allow late initialization and explicitly declared lifetimes as well: - -```mojo -fn example[life: Lifetime](cond: Bool, - x: ref[life] String, - y: ref[life] String): - # Late initialized local borrow with explicit lifetime - let str_ref: ref[life] String - - if cond: - str_ref = x - else: - str_ref = y - print(str_ref) -``` - -### Mojo Argument Conventions vs References - -One non-obvious thing is that function argument conventions and references are -different things: keeping them different allows argument conventions to be a -convenient sugar that avoids most users from having to know about references and -lifetimes. For example, the `borrowed` (which is usually implicit) and `inout` -argument conventions are sugar that avoids having to explicitly declare -lifetimes: - -```mojo -// Mojo today -fn example(inout a: Int, borrowed b: Float32): … - -struct S: - fn method(inout self): … - -// Written long-hand with explicit lifetimes. -fn example[a_life: Lifetime, b_life: Lifetime] - (a: mutref[a_life] Int, b: ref[b_life] Float32): … -struct S: - fn method[self_life: Lifetime](self: mutref[self_life]): … -``` - -This is very nice - every memory-only type passed into or returned from a -function must have a lifetime specified for it, but you really don't want to -have to deal with this in the normal case. In the normal case, you can just -specify that you're taking something by borrow (the default anyway) with an -implicit lifetime, or taking it `inout` if you want to mutate it but don't care -about the lifetime. - -NOTE: `inout` arguments also have one additional feature: function calls with -`inout` arguments know how to work with getter/setter pairs. For example -something like `mutate(a[i])` will call the getter for the element before -calling the function, then call the setter afterward. We cannot support this -for general mutable reference binding (because we wouldn't know where to perform -the writeback) so `inout` will have a bit more magic than just providing an -implicit lifetime. - -NOTE: Internally to the compiler, references (which are always pointer sized) -are treated as a register-passable types. This means they compose correctly -with implicit borrow semantics, and you can even pass a reference `inout` if you -want to. Such a thing is a "mutable reference to a reference", allowing the -callee to mutate the callers reference. - -### Keyword (?) for static lifetime - -I think we can have a useful feature set without requiring the ability to -specify a static lifetime - the uses in Rust appear to be more about -constraining input lifetimes than it is about the core propagation of lifetimes, -that said, we can definitely dream up a spelling when it is needed. - -Similarly, unsafe pointer tricks (e.g. when working with C) may want to use the -static lifetime marker to disable all tracking. We can start with a stub like -`__static_lifetime` and then re-syntax it later. - -### Implicitly declared lifetime parameter names and other sugar - -One common use of named lifetime parameters is to tie the lifetime of the result -of a function back to the lifetime of one of the function arguments. One -refinement over Rust we could permit is for arguments to implicitly declare -lifetimes on their first use. For example, we don’t need to require a -declaration of `life` in this example: - -```mojo -fn longest(x: ref[life] String, - y: ref[life] String) -> ref[life] String: - -# Alternatively follow Rust's lead. -fn longest(x: 'life String, y: 'life String) -> 'life String: -``` - -Let's **NOT** add this in the near future. Explicit lifetimes will be much less -common in Mojo than they are in Rust, and it is better for learnability to not -have magic like this. - -### Lifetime of `Self` - -The `Self` keyword (upper case) produces an elaborated type name for the current -struct, but that does not include the lifetime of `self` (lower case) which is -generally a reference. In a method you can name the lifetime of `self` by -declaring it explicitly like this: - -```mojo - struct MyExample: - fn method[self_life: Lifetime](self: mutref[self_life] Self) - -> Pointer[Int, self_life]: - ... - - fn callMethod(x: mutref[life1] MyExample): - use(x.method()) - - var y = MyExample() - use(y.method()) -``` - -`self_life` will bind to the lifetime of whatever lvalue the method is called -on, which is the `life1` lifetime in the first example, and the implicit -lifetime of y in the second example. This all composes nicely. - -One problem though - this won’t work for var definitions inside the struct, -because they don’t have a self available to them, and may need to reason about -it: - -```mojo - struct IntArray: - var ptr : Pointer[Int, Self_lifetime] -``` - -It isn’t clear to me how the compiler will remap this though. We’d have to pass -in the pointer/reference instead of the struct type. An alternative is to not -allow expressing this and require casts. We can start with that model and -explore adding this as the basic design comes up. - -### Extended `getitem`/`getattr` Model - -Once we have references, we’ll want to add support for them in the property -reference and subscripting logic. For example, many types store their enclosed -values in memory: instead of having `Pointer` and `Array` types implement both -`__getitem__` and `__setitem__` (therefore being a "computed LValue") we'd much -rather them to expose a reference to the value already in memory (therefore -being more efficient). We can do this by allowing: - -```mojo - struct Pointer[type: AnyType, life: Lifetime]: - # This __getref__ returns a reference, so no setitem needed. - fn __getref__(self, offset: Int) -> mutref[life] type: - return __get_address_as_lvalue[life](...) -``` - -We will also need to extend the magic `__get_address_as_lvalue` style functions -to take a lifetime. - -## Examples using Lifetimes - -This section attempts to build a few example data structures that are important -to express with lifetimes. They obviously haven’t been tested. - -### Pointer / UnsafePointer / Reference - -This is the bottom of the stack and needs to interface with other unsafe -features. Suggested model is to make Pointer be parameterized on the lifetime -that it needs to work with as well as element type: - -```mojo - @value - @register_passable("trivial") - struct MutablePointer[type: AnyType, life: Lifetime]: - alias pointer_type = __mlir_type[...] - var address: pointer_type - - fn __init__() -> Self: ... - fn __init__(address: pointer_type) -> Self: ... - - # Should this be an __init__ to allow implicit conversions? - @static_method - fn address_of(arg: mutref[life] type) -> Self: - ... - - fn __getitem__(self, offset: Int) -> inout[life] type: - ... - - @staticmethod - fn alloc(count: Int) -> Self: ... - fn free(self): ... - - fn exercise_pointer(): - # Allocated untracked data with static/immortal lifetime. - let ptr = MutablePointer[Int, __static_lifetime].alloc(42) - - # Use extended getitem through reference to support setter. - ptr[4] = 7 - - var localInt = 19 - let ptr2 = MutablePointer.address_of(localInt) - ptr2[0] += 1 # increment localInt - - # ERROR: Cannot mutate localInt while ptr2 lifetime is live - localInt += 1 - use(ptr2) -``` - -It’s not clear to me if we need to have a split between `Pointer` and -`MutablePointer` like Swift does. It will depend on details of how the -CheckLifetimes pass works - I’m hoping/expecting that the borrow checker will -allow mutable references to overlap with other references iff that reference is -only loaded and not mutated. NOTE: We probably won't be able to do this with the -proposed model, because generics can't be variant over mutability of the -reference. - -Another aspect of the model we should consider is whether we should have an -`UnsafePointer` that allows unchecked address arithmetic, but have a safe -`Reference` type that just allows dereferencing. This `Reference` type would be -completely safe when constructed from language references, which is pretty cool. -We may also want to wire up the prefix star operator into a dunder method. - -### ArraySlice - -`ArraySlice` (aka `ArrayRef` in LLVM) should compose on top of this: - -```mojo - @value - @register_passable("trivial") - struct MutableArraySlice[type: AnyType, life: Lifetime]: - var ptr: MutablePointer[type, life] - var size: Int - - fn __init__() -> Self: - fn __init__(ptr: MutablePointer[type, life], size: Int) -> Self: - - # All the normal slicing operations etc, with bounds checks. - fn __getitem__(self, offset: Int) -> inout[life] type: - assert(offset < size) - return ptr[offset] -``` - -As with `UnsafePointer`, this has to be parameterized based on the underlying -element type. `ArraySlice` is just a bound checked pointer, but because of -lifetimes, it is safe once constructed: the references it produces are bound to -the lifetime specified so can’t dangle. - -### Array / ValueSemanticArray - -Given these low level types, we can start to build higher level abstractions. -One example of that is an `Array` type. I’d suggest that our default array type -be value semantic with lazy copy-on-write 🐄, but a simpler example can be -implemented with `std::vector` style eager copying: - -```mojo - # Doesn't require a lifetime param because it owns its data. - struct Array[type: AnyType]: - var ptr: MutablePointer[type, Self_lifetime] - var size: Int - var capacity: Int - - fn __getitem__[life: Lifetime](self: inout[life], start: Int, - stop: Int) -> MutableArraySlice[type, life]: - return MutableArraySlice(ptr, size) -``` - -By tying the lifetime of the produced slice to the lifetime of the Array `self`, -the borrow checker will prevent use/mutation of the `Array` itself while a -mutable slice is produced. diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md deleted file mode 100644 index e99bf2f77f..0000000000 --- a/proposals/lifetimes-keyword-renaming.md +++ /dev/null @@ -1,147 +0,0 @@ -# Keyword naming and other topics to discuss - -This document is split off the [Provenance Tracking and Lifetimes in -Mojo](lifetimes-and-provenance.md) document to separate general syntactic -bikesheding issues from the core semantic issues in that proposal. - -Assuming that proposal goes through, I think we should consider a few changes to -the current Mojo keyword paint: - -## `borrowed` Keyword => `borrow` or `ref` - -`borrowed` as a keyword doesn’t really make sense in our new world. This is -currently used to indicate an argument that is a borrowed version of an existing -value. Given the introduction of lifetimes, these things can now appear in -arbitrary places (e.g. you can have an array of references) so it makes sense to -use a noun. - -Instead of reading an argument as “this function takes foo which is a borrowed -string”, we would read it as “foo is a borrow/ref of a string”. This makes it -consistent with local borrows on the stack: - -```mojo -fn do_stuff[a: Lifetime](x: ref[a] String): ... - -fn usage(): - var str = String("hello") - ref r = str # Defines a local borrow of str. - - do_stuff(str) # Bind a reference to 'str' - do_stuff(r) # Pass on existing reference 'str' -``` - -## `inout` Keyword => `ref` or `mutref` (??) - -The primary argument for the ‘`inout`’ keyword being named this was that Chris -wanted to get off the former ampersand syntax we used, and that (in an argument -position) there is copy-in and copy-out action that happens with computed -LValues. I think there is a principled argument to switch to something shorter -like `ref` which is used in other languages (e.g. C#), since they can exist in -other places that are not arguments, and those don’t get copy-in/copy-out -behavior. One challenge with the name `ref` is that it doesn't obviously -convey mutability, so we might need something weird like `mutref`. - -Note that copy-in/copy-out syntax is useful in more than function call -arguments, so the `inout` keyword may return in the future. For example, we may -actually want to bind computed values to mutable references: - -```mojo -for inout x in some_array_with_getitem_and_setitem: - x += 1 -``` - -This requires opening the reference with getitem, and writing it back with -setitem. We may also want to abstract over computed properties, e.g. form -something like `Array[inout Int]` where the elements of the array hold closers -over the get/set pairs. If we had this, this could decay to a classic mutable -reference at call sites providing the existing behavior we have. - -Given this possible direction and layering, I think we should go with something -like this: - -1. `ref`: immutable reference, this is spelled “`borrowed`” today - -2. `mutref`: mutable reference, this is spelled “`inout`” today. I’d love a -better keyword suggestion than `mutref`, perhaps just `mut`? - -3. `inout`: abstracted computed mutable reference with getter/setter. - -`inout` can decay to `mutref` and `ref` in an argument position with writeback, -and `mutref` is a subtype of `ref` generally. - -## `owned` Keyword => `var` - -People on the forums have pointed out that the “`owned`” keyword in argument -lists is very analogous to the `var` keyword. It defines a new, whole, value -and it is mutable just like `var`. Switching to `var` eliminates a concept and -reduces the number of keywords we are introducing. - -## Allow `let` in argument lists ... or remove them entirely (!) - -If we replace the `owned` keyword with `var`, then we need to decide what to do -with `let`. There are two different paths with different tradeoffs that I see. - -The easiest to explain and most contiguous would be to allow arguments to be -defined as `let` arguments, just like we define `var` arguments. This would -keep these two declarations symmetrical, and appease people that like to control -mutation tightly. - -The more extreme direction would be to remove `let` entirely. Some arguments -in favor of this approach: - -1. It has been observed on the forum that it adds very little - it doesn't - provide additional performance benefits over `var`, it only prevents - "accidental mutation" of a value. -2. Languages like C++ default to mutability everywhere (very few people bother - marking local variables constant, e.g. with `const int x = foo()`. -3. The more important (and completely necessary) thing that Mojo needs to model - are immutable borrows. Removing `let` would reduce confusion about these two - immutable things. -4. Mojo also has `alias`, which most programmers see as a “different type of - constant” further increasing our chance of confusing people. -5. `let` declarations require additional compiler complexity to check them, Mojo - doesn’t currently support struct fields market `let` for example because the - initialization rules are annoying to check for. Once you have them, it - messes with default values in structs. - -In my opinion, I think we are likely to want to remove `let`’s, but we should -only do so after the whole lifetime system is up and working. This will give us -more information about how things feel in practice and whether they are worth -the complexity. - -## More alternatives to consider - -[@sa- -suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6104926) -the keyword `fix` instead of `let`. - -[@mojodojodev suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6105688): - -`ref[a]` - immutable reference -`mut[a]` - mutable reference -`let[a]` - immutable owned -`var[a]` - mutable owned - -Having three letters for all of the keywords will allow the user to understand -"this is related to ownership and mutability". The problem with the proposed -removing let is that code ported from Python to Mojo won't behave the same, -keeping let and var is advantageous in that it says this is a Mojo variable so -you can add all the weird Python dynamic behavior when the keyword is elided. - -[@mzaks -suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6134220) -using numbers to identify lifetimes, e.g.: - -```mojo -fn example['1_life](cond: Bool, - x: borrowed'1 String, - y: borrowed'1 String): - # Late initialized local borrow with explicit lifetime - borrowed'1 str_ref : String - - if cond: - str_ref = x - else: - str_ref = y - print(str_ref) -``` diff --git a/proposals/mojo-and-dynamism.md b/proposals/mojo-and-dynamism.md deleted file mode 100644 index 845cc993b6..0000000000 --- a/proposals/mojo-and-dynamism.md +++ /dev/null @@ -1,253 +0,0 @@ -# Mojo and Dynamism - -Mojo has the lofty goal of being a simple, powerful, and easy-to-use language -like Python but with features that allow programmers to reach the performance of -C. One of Mojo's approaches is to start from being a superset of Python and -provide an incremental typing-for-performance story where the performance of -Python code can be incrementally improved by adding type annotations, explicit -variable declarations and error handling, switching `def` to `fn`, and so on. -By making things like types explicit, dynamism is removed from the program and -the compiler can generate faster code. The relationship between Mojo and -dynamism has to be carefully managed to meet the goals of the language. The -point of this post is to measure where that relationship stands now, what it -will need going forward as Mojo grows more features, and develop a framework for -managing that relationship. - -## Classes and Dynamism - -One feature that Mojo lacks at the moment are classes, an inherently dynamic -feature that provides inheritance and runtime polymorphism, the foundations of -object-oriented programming. - -Classes are implemented differently in many languages. In Python, for example, -classes are much more flexible than in languages like C++ or Java -- they are -more similar to classes in Javascript or Smalltalk. Methods can be defined and -then deleted, and even conditionally defined! For example, this is valid Python -code: - -```python -define = True - -class C: - print("hello") # prints 'hello' - if define: - def f(self): print(10) - else: - def f(self): print(20) - - -C().f() # prints '10' -``` - -In fact, the body of a Python class is just code that is executed, and the -resulting local variables are bound to the attributes of a class object. When -calling a class object, it returns a new object with a reference to the class -object, in which it can perform attribute lookup. In addition, functions that -would be member functions have their first argument bound to the new class -instance through the Python [descriptor -mechanism](https://docs.python.org/3/howto/descriptor.html#invocation-from-a-class). - -Mojo as a superset of Python has to support the full "hash-table" dynamism in -classes for compatibility with Python, but reference semantic classes are also -important for systems programming and application programming, where this level -of dynamism isn't needed and is actively harmful. We need to decide how to -handle this. - -One approach is to provide a decorator on class definitions (which can be opt-in -or opt-out) to indicate whether the class is "fully dynamic" as in Python or -whether it is "constrained dynamic" (e.g. has virtual methods that may be -overridden but cannot have methods added or removed). - -"Constrained dynamic" Mojo classes will use vtables for a more limited but more -efficient constrained dynamism than full hash table lookups. In addition to raw -lookups, constrained dynamic classes can use "[class hierarchy -analysis](https://dl.acm.org/doi/10.5555/646153.679523)" to devirtualize and -inline method calls, which are not valid for "fully dynamic" classes. - -Swift has a similar issue, where the developers wanted to have constrained -dynamism by default but needed full dynamism when working with Objective-C code: -Objective-C is based on the Smalltalk object model and thus has the same issues -as Python. Swift solved this by adding an opt-in -[@objc](https://swiftunboxed.com/interop/objc-dynamic/) decorator, which -provides full compatibility with Objective-C classes. Swift implicitly applies -this decorator to subclasses of Objective-C or `@objc` classes for convenience. - -If we chose to follow this design in Mojo, we could introduce a `@dynamic` -decorator, in which the class is an instance of a hash table and the body is -executed at runtime: - -```python -@dynamic -class C: - def foo(): print("warming up") - foo() # prints 'warming up' - del foo - def foo(): print("huzzah") - foo() # prints 'huzzah' -``` - -We could of course make dynamic be the default, and have a decorator like -`@strict` to opt-in to constrained dynamism as well. Regardless of the bias, we -absolutely need to support full dynamism to maintain compatibility with Python. - -An implementation question here would be "when does the body get executed?" when -the class is defined at the top-level. In this case, the class `C` could be -treated as a global variable with a static initializer that is executed when the -program is loaded. This ties into a discussion about how to treat global -variables and top-level code in general, which will come in a subsequent -section. Naturally, if the class is never referenced, the body is never parsed -and the static initializer is never emitted. - -### Syntactic Compatibility and `@dynamic` - -A primary goal of Mojo is to [minimize the syntactic -differences](https://docs.modular.com/mojo/why-mojo.html#intentional-differences-from-python) -with Python. We also have to balance that need with what the right default for -Mojo is, and this affects the bias on whether this decorator is "opt-in" or -"opt-out". - -We find it appealing to follow the Swift approach by making "full dynamic" an -opt-in choice for a Mojo class. This choice would add another syntactic -divergence between Mojo and Python, but it is one that can be alleviated with an -automatic mechanical transformer from Python code to Mojo code (e.g. to deal -with new keywords we take). In this case, all Python classes will be translated -by sticking `@dynamic` on them, and they can be removed for incremental boosts -to performance. - -An alternate design is to require opt-in to "constraint dynamism" by adding a -`@strict` (or use another keyword altogether) for vtable dynamism. We can -evaluate tradeoffs as more of the model is implemented. - -## `def` and Dynamism - -In Mojo, the goal of `def` is to provide a syntactic feature set that enables -compatibility with Python. It allows, for example, omitting type annotations, -implicit variable declarations, implicit raises, etc. But the Mojo `def` is not -the same as a Python `def`. A commonly reported issue is that Mojo scoping rules -differ from Python's. In Python, local variables are scoped at the function -level, but Python also supports behaviour like: - -```python -def foo(k): - for i in range(k): - print(i) - # Mojo complains that `i` is not defined, but this code should compile and - # dynamically raise an `UnboundLocalError` depending on the value of `k`! - print(i) -``` - -Python functions also have a notion of which names are supposed to be bound to -local variables. In the following example, `bar` knows `i` refers to a captured -local variable in `foo`, whereas `baz` tries to retrieve a value for `i` in its -local variable map. - -```python -def foo(): - i = 2 - def bar(): - print(i) - def baz(): - print(i) - i = 10 - bar() # prints '2' - baz() # throws an 'UnboundLocalError' -``` - -This gets at the heart of how Mojo should treat implicitly declared variables in -`def`s. The short answer is: exactly how Python does. `def`s should carry a -function-scoped hash table of local variables that is populated and queried at -runtime. In other words, lookup of implicitly-declared variables would be -deferred to runtime. On the other hand, the function does need to have a notion -of what variable *could* be available in the function, in order to emit -`UnboundLocalError`s as required. Of course, the compiler can optimize the table -away and do all the nice stuff compilers do if possible. - -Difficulty arises when discussing `def`s themselves. Although `def`s should -internally support full hashtable dynamism, what kind of objects are `def`s -themselves? For instance: - -```python -def foo(): - bar() - -def bar(): - print("hello") - -foo() # prints 'hello' - -def bar(): - print("goodbye") - -foo() # should this print 'goodbye'? -``` - -In Mojo today, the first time the name lookup of `bar` is resolved, it is baked -into a direct call to the first `bar`. Therefore, shadowing of `bar` does not -propagate into the body of `foo`. On the other hand, if all `def`s were treated -as entries in a hashtable, then it would. - -One middle-ground approach would be to treat `bar` as a mutable global variable -of type `def()` (one for each possible overload of `bar`). The dynamism can be -escalated with a `@dynamic` decorator that removes static function overloading. -However, both of these approaches risk creating confusing name lookup rules. For -instance, would the following be allowed? - -```python -@dynamic -def bar(a): pass - -def bar(a: Int): pass -``` - -This gets into the "levels of dynamism" Mojo intends to provide, and how that -relates to `def`s. The reality is that `def`s in Mojo today only resemble Python -`def`s on the surface. They share similar syntax, but Mojo `def`s are really -extra syntax sugar on top of `fn` and are altogether a different beast than -Python `def`s. - -## Four Levels of Dynamism - -To summarize, in order to support incremental typing-for-performance, Mojo will -have to support everything from strict, strongly-typed code to full Python -hashtable dynamism but with syntax that provides a gradual transition from one -end to the other. - -Given all that has been discussed and what the language looks like today, Mojo's -dynamism is moving into four boxes: - -1. Compile-time static resolution. -2. Partial dynamism. -3. Full hashtable dynamism. -4. ABI interoperability with CPython. - -The fourth category isn't explored here, but will important when/if we support -subclassing imported-from-CPython classes in Mojo, because that will fix the -runtime in-memory representation to what CPython uses. - -The highest level of dynamism and the most faithful compatibility doesn't come -from Mojo itself, it comes from Mojo's first class interoperability with -CPython. This in effect will be Mojo's escape hatch for compatibility purposes -and is what gives Mojo access to all of Python's vast ecosystem. Below that, -Mojo will provide an emulation of Python's hash-table dynamism that is a -faithful but not quite identical replication of Python behaviour (no GIL, for -example!). Building this out will be a huge undertaking, and is something Mojo -should do over time. - -The most important thing to remember is that Mojo is not a "Python compiler". -The benefit of sharing the same syntax as Python, however, means seamless -interop is on the table: - -```python -@python -def python_func(a, b=[]): - return a + [2] + b - -fn mojo_func(): - try: - print(python_func([3])) - except e: - print("error from Python:", e) -``` - -The goal of the "levels of dynamism" is to provide an offramp, starting by -removing the `@python` decorator from `python_func`. diff --git a/proposals/project-manifest-and-build-tool.md b/proposals/project-manifest-and-build-tool.md deleted file mode 100644 index 1899b0ae3d..0000000000 --- a/proposals/project-manifest-and-build-tool.md +++ /dev/null @@ -1,104 +0,0 @@ -# Mojo project manifest and build tool - -A *project manifest* is a file that describes the source code files that make up -a library or executable, and how those files are meant to be built, distributed, -and interacted with by language tooling (such as language servers and -debuggers). Some examples include Rust’s `Cargo.toml`, Python’s `setup.py`, or -language-agnostic formats such as Bazel and its `BUILD` files. - -A *build tool*, on the other hand, is a program that can create the libraries or -executables described in a project manifest. For example, `cargo build` compiles -and links Rust executables and libraries described in a `Cargo.toml` file. In -Python, many different tools can process `setup.py` and `pyproject.toml` files, -in order to produce Python wheels or packages. - -This proposal is meant to: - -1. Announce our intent to define a project manifest format for Mojo. -2. Announce our intent to implement a build tool for Mojo projects. -3. Solicit community feedback and feature requests for the project manifest and - build tool. - -## Motivation - -No project manifest format exists for Mojo as of Mojo SDK v0.7.0. In the present -situation, Mojo projects can be built by invoking `mojo package` (to produce a -`.mojopkg` library) or `mojo build` (to produce an executable) on the command -line. - -This status quo has several drawbacks: - -1. There is no standard for building a Mojo project from source. If we examine - popular Mojo projects hosted on GitHub today, some are built via `docker`, - where the Dockerfile invokes `mojo run` to execute the Mojo source file. Some - are built via CMake, where project maintainers have used `add_custom_command` - to invoke `mojo build` and `mojo package`. Some are built via a - `mojo package` command that is documented only in the README. Some have build - instructions that are only known to the maintainers. The lack of - standardization makes it difficult to download and build a Mojo source - repository, which inhibits collaboration among the Mojo community of - developers. -2. Collaboration within the Mojo community aside, the lack of a project manifest - inhibits Mojo language tooling, such as the language server and debugger, - from functioning perfectly. For example, many Mojo projects make use of - compile time definitions, such as `-D ENABLE_TILING`. Without knowing which - definitions should be used when compiling Mojo source code, the language - server cannot provide the same diagnostics that the user will see when - actually building their project. - -Therefore, we think that a project manifest and build tool specific to Mojo will -resolve these issues: - -1. We aim to implement a single command that can be used to build any Mojo - project from source, addressing the first issue listed above. This is - analogous to how `cargo build` builds the default targets with the default - settings for any Rust project, or how `zig build` does so for any Zig - project. -2. Because a project’s manifest will specify which Mojo compiler options are to - be used, language tools would make use of those and function as intended. - This addresses the second issue listed above. - -## Guiding principles - -- As mentioned above, we aim for a single command to be capable of building any - Mojo project from source. -- We believe the ability to *download* a project’s dependencies from the - Internet — a “package manager” function — can be added at a later time. For - example, `zig build` did not originally include this functionality, but added - an implementation over six years later. The Mojo build tool will likely - implement the downloading and building of project dependencies soon, but it - will be the subject of a separate proposal. -- Although the project manifest and build tool we design is specific to Mojo, we - will aim for the best possible integration with other build systems, such as - Python setuptools, Bazel, Buck2, and CMake. We will make accommodations to - better support these tools whenever possible. -- Our design will benefit from community input and contributions, so **we will - develop this as an open-source tool, written primarily in Mojo**. We believe - doing so will also serve to drive additions and improvements to the Mojo - standard library. - -## Request for feedback - -As mentioned above, this proposal is primarily to announce our intent to develop -a project manifest format and build tool for Mojo, and to solicit feedback. -Below are some topics that we would love to hear community members’ opinions on: - -- Whether you agree with the motivations and guiding principles in this - proposal. -- Which project manifest formats and build tools you love, and why. We’re - drawing inspiration from a broad set of language ecosystems, including Rust, - Zig, Swift, and especially Python. -- Whether to adopt the [build server - protocol](https://build-server-protocol.github.io). We think doing so may help - with our guiding principle to integrate well into the existing ecosystem of - tools. -- Whether to define the project manifest as an executable program. Analogous to - how `build.zig` and `Package.swift` are programs that define a project, should - we define a `project.mojo` or similar construct? There are many arguments in - favor of doing so, but on the other hand, we see tradeoffs as well, and a - purely declarative form could be used. -- Any other thoughts you wish to contribute — we are build systems and language - tooling nerds! Send us your thoughts on [the GitHub Discussion thread - associated with this - proposal](https://github.com/modularml/mojo/discussions/1785), and let’s geek - out. diff --git a/proposals/remove-let-decls.md b/proposals/remove-let-decls.md deleted file mode 100644 index ec09b19359..0000000000 --- a/proposals/remove-let-decls.md +++ /dev/null @@ -1,153 +0,0 @@ -# Simplifying Mojo🔥 - let's get rid of `let` - -Chris Lattner, Dec 5, 2023, Status: **Accepted**, [discussion thread](https://github.com/modularml/mojo/discussions/1456#discussioncomment-8358722) - -Mojo is still a new language, and is rapidly evolving. We’re learning a lot -from other languages, but Mojo poses its own set of tradeoffs that indicate a -unique design point. - -One of the early decisions made in Mojo's development is that it adopts the -`let` and `var` design point that Swift uses. This whitepaper argues that we -should switch to a simpler model by jettisoning `let` and just retaining `var` -(and implicit Python-style variable declarations in `def`). This has also been -[suggested by the community](https://github.com/modularml/mojo/issues/1205). - -Note that immutability and value semantics remain an important part of the Mojo -design, this is just about removing "named immutable variables". Immutable -references in particular are critical and will be part of a future "lifetimes" -feature. - -## Current Mojo 0.6 design - -Mojo initially followed the precedent of Swift, which allows a coder to choose -between the `let` and `var` keyword to determine whether something is locally -modifiable. That said, the design in Mojo 0.6 and earlier has a number of -particularly surprising and unfortunate aspects: - -1. The notion of immutable variables is entirely new to Python programmers, and -previous experience with Swift shows that this ends up being the [very first concept](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Constants-and-Variables) -a Swift programmer has to learn. This is unfortunate, because named immutable -variables aren't a core programming concept, and not something required -to achieve Mojo's goals. - -2. The naming of `let` caused a lot of early [heat and -debate](https://github.com/modularml/mojo/discussions/120). Other programming -languages have a wide range of design points (e.g. `const` in C/C++ and -Javascript) and there is a divergence of naming for all these things: -`let`, `val`, `const`, etc, etc. - -3. Mojo also has a notion of compile time value (`alias`), which means there are -three concepts going around: `alias`, `let`, and `var`. Most of the uses of -(e.g.) Javascript `const` is better served with `alias` than `let`. - -4. Both Swift and Rust encourage immutable values - Swift (and currently Mojo) -warn about unneeded mutability, Rust makes mutability more verbose (`let mut`), -and some propose that Mojo [make mutability more -verbose](https://github.com/modularml/mojo/issues/451). This cuts very hard -against a lot of the design center of Python, which doesn’t even have this -concept at all: it would be weird to make it the default, but if we don’t, -then why bother having it? - -5. There is no performance benefit to the Swift/Rust design, and I personally -haven’t seen any data that shows that there is any safety or readability -benefit. If anything, it is the argument when seeing `let x = foo()` that you -know `x` will never be reassigned, but any benefit here is small. - -6. The immutability only applies to the local value, and in the case of -reference semantic types (e.g. types like `Pointer` in today's Mojo, but also -*all classes* in tomorrow's Mojo) this is super confusing. We are often asked: -“Why do I get a warning that I should change a "`var` pointer" to `let` when I -clearly mutate what it is pointing to?” - -7. Mojo does not currently allow `let`’s as struct fields, (only `var`’s) which -is inconsistent. Swift has a very complex set of rules for how struct fields -get initialized that would be nice to not implement for Mojo. There also isn’t -a great way to define defaulted field values, e.g.: - - ```mojo - struct Thing: - # This is not actually supported right now, but imagine it were. - let field = 42 - fn __init__(inout self): - self.field = 17 # shouldn't be able to overwrite field? - ``` - -8. Mojo has a notion of ownership and will eventually have a notion of lifetimes -and safe references (including both mutable and immutable *references*) which -will be different from (but can compose with) the `let` vs `var` distinction. -It is unfortunate to have different forms of immutability floating around, and -we really do need immutable borrows and immutable references. - -Speaking subjectively as one of the principal designers of Swift, I will say -that it has several pretty pedantic language features intended to increase -safety (e.g. requiring all potentially-throwing values to be marked with a `try` -keyword) and many of the decisions were made early without a lot of data to -support them. I believe we should fight hard to keep Mojo easy to learn and -eliminate unnecessary concepts if we can. - -## Proposal: eliminate ‘`let`’ declarations - -The proposal here is straightforward: let’s just eliminate the concept of an -immutable named value entirely. This won’t eliminate immutability as a concept -from Mojo, but will instead push it into the world of borrowed arguments and -immutable references. This would have a number of advantages: - -This directly simplifies the conceptual Mojo language model: - -1. This eliminates one unfamiliar concept that a Python program would have to - learn. -2. This eliminates confusion between `let` vs `alias` directly. -3. This eliminates a fertile source of keyword bikeshedding. -4. This eliminates confusion in workbooks where top level values are mutable - even though they are declared `let`. - -This would eliminate a bunch of complexity in the compiler as well: - -1. Error messages currently have to make sure to say `let` and `var` correctly. -2. The IR representation needs to track this for later semantic checks. -3. This eliminates the need to implement missing features to support `let`’s. -4. This eliminates the complexity around detecting unmutated `var`s that warn - about changing to `let`. -5. Due to ASAP destruction, CheckLifetimes has extra complexity to reject code - like: “`let x: Int; x = 1; use(x); x = 2; use(x)`” even though the original - lifetime of the first “`x=1`” naturally ended and “`x`” is uninitialized - before being assigned to. This has always been a design smell, and it - [doesn’t work right](https://github.com/modularml/mojo/issues/1414). - -This proposal will not affect runtime performance at all as far as we know. - -### What about var? - -If this proposal is accepted, I think we should leave `var` as-is. Unlike -traditional Python behavior, `var` introduces an explicitly declared and -*lexically scoped* value: we need some introducer and do want scoped -declarations. - -The name `var` is also less controversial because it clearly stands for -“variable” in a less ambiguous way than using `let` to stand for "named -constant". If there is desire to rename `var` it would be an orthogonal -discussion to this one and should be kept separate. - -## Rolling out this proposal smoothly - -If we think this proposal is a good idea, then I think we should stage this to -make adoption more smooth and less disruptive. Rolling this out would look like -this: - -1. Build consensus with the Mojo community to get feedback and additional - perspective. -2. Do the engineering work to validate there is no performance hit etc, and - eliminate the IR representation and behavior for `let`. At this phase we - will keep parsing them for compatibility: parse them into the same IR as a - `var`, but emit a warning “let has been deprecated and will be removed in - the next release” with a fixit hint that renames the `let` to `var`. -3. In a release ~1 month later, change the warning into an error. -4. In a release ~1 month later, remove the keyword entirely along with the error - message. - -## Alternatives considered - -We can always keep this around and re-evaluate later. That said, I don’t think -anything will change here - the Mojo user community (both external to Modular -and internal) has already run into this several times, and this will keep coming -up. diff --git a/proposals/value-ownership.md b/proposals/value-ownership.md deleted file mode 100644 index 9303d1644e..0000000000 --- a/proposals/value-ownership.md +++ /dev/null @@ -1,735 +0,0 @@ - -# Value ownership design for Mojo - -**Written**: Jan 2, 2023 - -**Status**: Implemented but the design has been refined, kept for historical -*interest. - -This document explores a design for ownership support in Mojo. This learns from -other contemporary languages including C++, Rust, Swift and Val, providing a -novel blend that should integrate well with our base Python syntax, and be -powerful enough to express a wide range of kernel and systems programming -applications. - -Rust is the language that most people will naturally think of in this space, and -when compared to it, I expect that we will provide support for a wider range of -types than Rust, yet provide a more familiar programming model and friendly API -than it. While I assume that we will extend this to support lifetime types for -better generality and safety with reference semantic types, that design is not -included in this proposal. - -# Motivation and Background - -Modern systems languages aspire to provide memory safety, good low-level -performance, and allow advanced library developers to build expressive -high-level APIs that are easy to use by less experienced API users. - -In the case of Mojo, we have two specific “now” problems to solve: - -1. We need to provide a way to allocate memory for a Tensor-like type, and given -our existing support for raising exceptions, we need for them to be cleaned up. -Thus we need destructors. We also need to disable copying of this sort of type. - -2. We also need to implement transparent interop with CPython with an “object” -struct. This requires us to have copy constructors and destructors so we can -maintain the CPython reference count with an ergonomic Python-like model. - -Over time, we want Mojo to be a full replacement for the C/C++ system -programming use cases in Python, unifying the “two world problem” that Python -has with C. This is important because we want to have a unifying technology, -and because CPUs will always be an important accelerator (and are fully -general), and because our bet is that accelerators will get more and more -programmable over time. - -## Related Work - -I am not going to summarize the related work fully here, but I recommend folks -interested in this topic to read up on relevant work in the industry, including: - -1. C++’11 r-value references, move semantics, and its general modern programming -model. It is yucky and has lots of problems, but is table-stakes knowledge and -powers a tremendous amount of the industry. - -2. Have a programmer-level understanding of [Rust’s Memory Ownership -model](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html), and -read [the Rustonomicon](https://doc.rust-lang.org/nomicon/) end-to-end for bonus -points. - -3. Swift has a quite different approach which made some mistakes (Swift suffers -from pervasive implicit copying) but has nice things in its [initialization -design](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization), -[ARC -design](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html), -[exclusivity enforcement](https://www.swift.org/blog/swift-5-exclusivity/) -[[more -details](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md)], -etc. - -4. The [Val programming language](https://www.val-lang.dev/) is an early phase -research system that is learning from Swift and trying to provide a much simpler -programming model than Rust does with other advantages. It isn’t at all clear -if it will be expressive enough to be useful at this point though. - -C++ and Rust are the most widely known in this space and they make different -tradeoffs in defaults and what it means for a type author (ignoring lifetimes, -which C++ lacks and is therefore generally unsafe). A few random observations -that are related to the commentary below: - -- Rust in particular defaults to move’ing values but allows types to opt out of -that by implementing the Copy trait. - -- Rust’s type system doesn’t appear to support values like `std::atomic` which -require a pinned address in memory (Rust assumes it can transport things around -at will), nor does it support things like `llvm::SmallVector` which is movable, -but has an interior pointer so needs custom move constructors. - -- Because Rust defaults to moving everything around, it puts a huge amount of -pressure on memcpy and LLVM memcpy optimization (e.g. see Patrick Walton’s -recent work to improve this). In my opinion, this is a self imposed mistake -that we can correct structurally. - -- Rust could support C++-like moves from values that leave them -inert-and-to-be-destroyed, but does not do that. For lack of this, there is a -lot of complexity and unsafe APIs required (e.g. -[Drain](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.drain) -and other things) when you want to construct an array progressively or take -elements out of an array. - -## Relevant Existing Mojo Type System Features - -The Mojo design currently has a few basic features that are precursors to proper -ownership. The design below makes significant extensions and some changes to -it. - -### L-Values and R-Values - -Lvalues and Rvalues follow a conventional design found in many programming -languages: an Rvalue is an immutable value that can be passed around by copying -its bits by-value in an SSA register. An Lvalue is mutable, represented by its -address, and can be promoted to an Rvalue with a “load”. - -### Argument Conventions - -Functions can be declared to take any of their arguments by-reference, with an & -sigil in the function definition: - -```mojo - fn globalFn(a: Int, b&: Int): - b = a # ok - a = 4 # error - - struct Vec: - ... - fn push_back(self&, item: Int): ... # mutable self - fn size(self): ... # immutable self - - fn workWithVecs(a: Vec, b&: Vec): - use(a.size()) # ok - use(b.size()) # ok - - a.push_back(4) # Error, a isn't mutable - b.push_back(4) # ok -``` - -As shown, by-ref arguments are Lvalues and thus may be mutated, otherwise -arguments are passed by-value as immutable Rvalues. There is another -similar-but-different thing going on with “def” functions. By-value arguments -are passed by copy but are allowed to be mutable for better compatibility with -Python semantics: - -```mojo - def exampleDef(a: Int, b&: Int): - b = 4 # mutable as before. - - # passed by value into mutable copy. - # This change isn't visible in the caller. - a = 4 -``` - -The ‘def’ semantics are implemented by taking a value in by copy and introducing -a local ‘var’ that shadows the argument. It is therefore a purely local -transformation that we’ll ignore in the rest of this document. - -### Type initializers - -Mojo supports Python type initializers and currently allows them to participate -in implicit conversions: - -```mojo - struct Int: - var value : __mlir_type.index - - fn __init__(value: __mlir_type.index) -> Int: - return Int { value: value } -``` - -# Proposed Mojo Additions / Changes - -This proposal introduces a number of new concepts to cover the space of value -ownership and parameter passing conventions that Rust, Swift and C++ provide, -but in a different way. We layer on the features in individual groups. Let’s -explore them one piece at a time. - -## Extended parameter conventions - -We should extend the existing type system to support owning (aka moving, aka -consuming) parameter convention, immutable borrow semantics (“`&T"` in Rust, -“`const T&"` in C++) and mutable borrow semantics (“`&mut T`” in Rust, and -“`T&`” in C++). - -### Change & to mean mutable borrow or “inout” - -Right now, & is a C++-like reference semantic sigil, we should keep it, but -change it to mean a “mutable borrow” in Rust terminology or “inout” using Swift -terminology. This won’t require Mojo source changes, but will be a terminology -change inside of the compiler. The definitive initialization pass (below) will -need to enforce its correct semantics. - -Tangential to this proposal, we could require the use of `&` on the caller side -of arguments passing a mutable borrow to make it more clear in the caller code. -If we do this, I propose that it be a postfix operator, and use it for methods -for consistency: - -```mojo - swap(x.first&, y.first&) # Obvious what is being mutated - b&.push_back(42) # Obvious that b is mutated. -``` - -This proposal will not use this syntax below, and this is probably way too -onerous for methods, this is probably a bad idea. - -### Introduce “owned” argument conventions - -We need a way to specify that ownership of a value is being passed into the -function: - -```mojo - # This takes ownership of a unique vector, including its resources. - fn someFunction(owned v: Vec): - print(v) - v.push_back(42) # Ok: v is mutable because we own it -``` - -In the code above, we show “owned v” takes and owns a value from the caller. -Just like normal arguments, we would need a copy to get them as mutable: - -```mojo - fn anotherFunction(a: Int, owned v: Vec): - a += 1 # Error: a is immutable - var a2 = a # Copy the argument to make it mutable - a2 += 1 # Ok, a2 is mutable. - - v.push_back(a) # Ok, owned values are mutable - var v2 = v^ # Transfer v argument into a new var binding. - v2.push_back(a) # Also ok -``` - -This should be an owned _reference_ and thus lower to LLVM pointers, just like a -mutable reference does, unless the value is `@register_passable` (see below). -Not doing this would significantly impact our ability to model things like -`std::atomic` and `SmallVector` whose address is significant. See the -“extensions” at the bottom for why this will be efficient for trivial types like -integers. - -### Introduce “borrowed” argument conventions and change default - -Similarly we need the ability to specify that we are passing and returning an -immutable borrow, which is like a `'const &`’ in C++. The spelling of this -isn’t particularly important because it will frequently be defaulted, but we -need something concrete to explain the examples. - -```mojo - # This takes a borrow and return it. - # Returned references need lifetimes for safety of course. - fn printSizeAndReturn(borrowed v: Vec) -> borrowed Vec - print(v.size()) - return v -``` - -At the LLVM level, immutable references are passed by pointer, just like mutable -references. - -Note that C++ and Rust entered into a strange battle with programmers about how -to pass values by default. Templates generally use “`const&`” to avoid copies, -but this is an inefficient way to pass trivial types like “`int`”. We propose a -type level annotation to directly address, which allows us to use borrow far -more pervasively for arguments in the language. See the ‘extensions’ section -below. - -### Change the default argument and result conventions - -Now we have the ability to express ownership clearly, but we don’t want all code -everywhere to have words like `borrowed` on it: we want more progressive -disclosure of complexity and better defaults. - -The first piece of this is the default return value convention. The right -default convention for return values is to be owned: - -```mojo - # Result defaults to being an owned reference. - fn getVec() -> Vec: # Equivalent to "...) -> Vec^" - ... -``` - -because we otherwise have no way to return newly created values. Code can -override the return convention by using another sigil, e.g. `inout Vec` if a -mutable reference is required. - -We also need to decide what to do with arguments. We don’t want to copy -arguments by default (a mistake Swift made), because this requires types to be -copyable, and depends on unpredictable copy elision that makes performance and -COW optimization sad. I don’t think we want to depend on pass-by-move like Rust -did because Rust forces tons of things to be marked “&” pervasively, this -introduces a ton of `memcpy` operations, and Python programmers won’t think that -passing a value to a function makes it unavailable for use by other things by -default. - -In my opinion, the C++ got this almost right: `const&` is the right default -argument convention (and is optimized to register value passing in an opt-in -way, described below). This is good for both self and value arguments and is -what Swift semantically did with its +0 ownership convention in a bunch of -places. Consider this example: - -```mojo - struct Dictionary: - fn size(self) -> Int: ... -``` - -You don’t want to **copy the dictionary**, when calling size! This worked for -Swift because of ARC optimizations and that its Dictionary type was a small -thing implemented with COW optimizations, but this is very unpredictable. - -Passing arguments-by-borrow by default is also great because it eliminates the -pervasive need to do a load operation when converting Lvalues to Rvalues. This -is a huge improvement in the model, because “loading” a value is extremely -expensive when the value is large or cannot be loaded (e.g. variable sized -types, e.g. some languages representation for existential types and Rust’s DSTs, -which we may or may not want to support anyway). - -It also means that we can support types like `std::atomic` which need a -guaranteed ‘self’ address - natively and with no trouble - since we’re never -trying to implicitly load the value as a whole. - -## Adding support for value destruction - -Now that we have a notion of ownership, we can complete it by destroying values -when their lifetime has ended. This requires introducing the ability to declare -a destructor, and the machinery to determine when to invoke the destructor. - -### User defined destructors - -We should embrace the existing Python convention of implementing the `__del__` -method on a type. This takes ownership of the self value, so it should be -defined as taking `owned self`. Here’s a reasonable implementation of Vec’s -ctors and dtor, but without the push_back and associated methods (which are -obvious): - -```mojo - struct Vec: - var data: Pointer # I just made this type up. - var capacity: Int - var size: Int - fn __init__(inout self, capacity: Int): - self.data = Pointer.malloc(capacity) - self.capacity = capacity - self.size = 0 - - # default args will be nice some day - fn __new__(inout self): return Vec(1) - fn __del__(owned self): # owning reference to self. - # Any int values don't need to be destroyed. - self.data.free() -``` - -There is some nuance here and a special case that we need to handle in the -`__del__` method. Ideally, we should track the field sensitive liveness of the -‘self’ member that comes into del. This will allow us to handle sub-elements -that are individually consumed, safely handle exceptions that early-exit from -the destructor etc. This is something that Swift gets right that Rust -apparently doesn’t. - -With respect to the simple definition of Vec above, it is enough to define a -safe vector of integers which is creatable, destroyable, can be passed by -borrowed and mutable reference, but isn’t enough to support movability or -copyability. We’ll add those later. - -### When do we invoke destructors for value bindings? - -Now that we have a way to define a destructor for a value, we need to invoke it -automatically. Where do we invoke the destructor for a local value binding? -Two major choices exist: - -1. End of scope, ala C++ (and I think Rust). -2. After last use, ala Swift and Val (but Val has a better model). - -The difference can be seen in cases like this: - -```mojo - fn bindingLifetimeExample(): - var vec = Vec() - vec.push_back(12) - use(vec) - # Val/Swift destroys 'vec' here. - do_lots_of_other_stuff_unrelated_to_vec() - # C++/Rust destroy vec here. -``` - -I would advocate for following the Swift model. It reduces memory use, and I -haven’t seen it cause problems in practice - it seems like the right default. -Furthermore, this dovetails well with ownership, because you want (e.g.) -immutable borrows to die early so you can form mutable borrows in other -statements. It also makes the “form references within a statement” special case -in C++ go away. - -The tradeoff on this is that this could be surprising to C++ programmers, -something that Swift faced as well. The balance to that is that GC languages -with finalizers are not used for RAII patterns, and Python has first-class -language support for RAII things (the `with` statement). - -There are specific cases like RAII that want predictable end-of-scope -destruction, so you end up needing a `@preciseLifetime` decorator on the struct -or use closures - [both work -fine](https://developer.apple.com/documentation/swift/withextendedlifetime(_:_:)-31nv4). - -NOTE: This was pushed forward during implementation to the "ASAP" model that -Mojo uses. - -### Taking a value from a binding - -The other thing you may want to do is to intentionally end a binding early, -transferring ownership of the bound value out as an owned rvalue. Swift and -Rust both support mutable value lifetimes with holes in them, and ending -immutable bindings early (Rust with the `drop(x)` operator or by moving out of -the binding, Swift with the recently proposed -[consume/take/move](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md) -operation). - -I propose supporting this with the `^` postfix operator, e.g.: - -```mojo - fn takeVec(owned v: Vec): ... - - fn showEarlyBindingEnd(): - var vec = Vec() - vec.push_back(12) - takeVec(vec^) # Ownership of vec is transferred to takeVec. - do_lots_of_other_stuff_unrelated_to_vec() - - var vec2 = Vec() - vec2.push_back(12) - ... - _ = vec2^ # force drop vec2. -``` - -This is postfix so it composes better in expressions, e.g. -“`someValue^.someConsumingMethod()`”. - -### Supporting “taking” a value, with a convention (+ eventually a Trait) - -I believe it is important for common types to support a “destructive take” to -support use-cases where you want to std::move an element out of the middle of a -`std::vector`. C++ has `std::move` and move constructors for this, and Rust has -a ton of complexity to work around the lack of this. Swift doesn’t appear to -have a story for this yet. I think we just use a method convention (eventually -formalized as a trait) where types who want it define a `take()` method: - -```mojo - struct Vec: - ... - fn __moveinit__(inout self, inout existing): - # Steal the contents of 'existing' - self.data = existing.data - self.capacity = existing.capacity - self.size = existing.size - - # Make sure 'existing's dtor doesn't do bad things. - existing.data = None - existing.size = 0 - existing.capacity = 0 -``` - -This is analogous to defining a move constructor in C++. Note that you only -need to define this if you want to support this operation, and we eventually -should be able to synthesize this as a default implementation of the “Takable” -trait when we build out traits and metaprogramming features. - -## Value Lifetime Enforcement - -Now that we have all the mechanics in place, we actually have to check and -enforce lifetime in the compiler. This entails a few bits and pieces. - -### Implement a “Definitive Initialization” Like Pass: CheckLifetimes - -The first thing we need is a dataflow pass that tracks the initialization status -of local bindings. The basic mechanics needed here are implemented in the Swift -Definitive Initialization pass, and a lot of the mechanics are well described in -the [Drop Flags section of the -Rustonomicon](https://doc.rust-lang.org/nomicon/drop-flags.html) and [slide 135+ -in this -talk](https://www.llvm.org/devmtg/2015-10/slides/GroffLattner-SILHighLevelIR.pdf). -This is a combination of static analysis and dynamically generated booleans. - -When building this, we have the choice to implement this in a field sensitive -way. I believe that this is a good thing to do in the medium term, because that -will allow making `__new__` and `__del__` methods much easier to work with in -common cases, and will compose better when we get to classes. That said, we -should start with simple non-field-sensitive cases and extend it over time. -This is what we did when bringing up Swift and it worked fine. - -### Implement support for variable exclusivity checking - -While it isn’t a high priority in the immediate future, we should also add -support for variable exclusivity checking to detect dynamic situations where -aliases are formed. See the [Swift -proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md) -for details on the issues involved here. Mojo will be working primarily with -local variables in the immediate future, so we can get by with something very -simple for the immediate future. - -### Synthesize destructors for structs - -The other thing necessary in the basic model is for the destructors of field -members to be run as part of `__del__` methods on structs. We don’t want people -to have to write this manually, we should synthesize a `__del__` when needed. -For example in: - -```mojo - struct X: - var a: T1 - var b: T2 - - fn __del__(owned self): # This should be synthesized. - _ = self.a^ - _ = self.b^ -``` - -## Extensions to make the programming model nicer - -With the above support, we should have a system that is workable to cover the -C++/Rust use-cases (incl `std::atomic` and `SmallVector`), handle the motivating -Tensor type and Python interop, as well as provide a safe programming model -(modulo dangling reference types which need lifetimes to support). That said, -we still won’t have a super nice programming model, this includes some “table -stakes” things we should include even though they are not strictly necessary. - -In particular, the support above completely eliminated the need to copy and move -values, which is super pure, but it would be impractically painful to work with -simple types that have trivial copy constructors. For example: - -```mojo - fn useIntegers(a: Int): - var b = a+4 # ok, b gets owned value returned by plus operator. - let c = b # error, cannot take ownership from an lvalue. - let c2 = b.copy() # Ok, but laughable for Int. -``` - -It is worth saying that the “c = b” case is something that we explicitly want to -prohibit for non-trivial types like vectors and dictionaries: Swift implicitly -copies the values and relies on COW optimization and compiler heroics (which are -not amazingly great in practice) to make it “work”. Rust handles it by moving -the value away, which breaks value semantics and a programmer model that people -expect from Python. - -It is better for vectors and Dictionary’s (IMO) to make this a compile time -error, and say “this non-copyable type cannot be copied”. We can then -standardize a `b.copy()` method to make the expense explicit in source code. - -### Copy constructors for implicitly copyable types - -The solution to both of these problems is to allow types to opt-in to -copyability (as in the Rust `Copy` trait). The obvious signature for this in -Mojo seems to be a `__copyinit__` implementation (eventually formalized as a -trait): - -```mojo - struct Int: - var value: __mlir_type.index - fn __copyinit__(inout self, borrowed existing: Int): - self.value = existing.value -``` - -Given this new initializer, a type that implements this is opt-ing into being -implicitly copied by the compiler. This (re)enables lvalue-to-rvalue conversion -with a “load” operation, but makes it explicit in user source code. It allows -integers to work like trivial values, and allows the library designer to take -control of what types support implicit copies like this. This is also required -for the Python interop “object” type, since we obviously want `x = y` to work -for Python objects! - -One nice-to-have thing that we should get to eventually (as we build out support -for traits and metaprogramming) is `Copyable` trait with a default -implementation. This would allow us to manually provide a copy constructor if -we want above, or get a default synthesized one just by saying that our struct -is `Copyable`. See the appendix at the end of the document for more explanation -of how this composes together. - -All together, I believe this will provide a simple and clean programming model -that is much more predictable than the C++ style, and is more powerful than the -Swift or Rust designs, which don’t allow custom logic. - -### Opting into pass-by-register parameter convention - -The final problem we face is the inefficiency of passing small values -by-reference everywhere. This is a problem that is internalized by C++ -programmers through common wisdom (“pass complex values like `std::vector` as -`const& or rvalue-ref` but trivial values like `int` by value!”), but ends up -being a problem for some generic templated code - e.g. `std::vector` needs to -declare many things as being passed by `const&` or rvalue reference, which -becomes inefficient when instantiated for trivial types. There are ways to deal -with this in C++, but it causes tons of boilerplate and complexity. - -The key insight I see here is that the decision is specific to an individual -type, and should therefore be the decision of the type author. I think the -simple way to handle this is to add a struct decorator that opts the struct into -being passed by owning copy, equivalent to the Rust Copy convention: - -```mojo - @register_passable - struct Int: - ... -``` - -This decorator would require that the type have a copy constructor declared, and -it uses that copy constructor in the callee side of an API to pass arguments -by-register and return by-register. This would lead to an efficient ABIs for -small values. - -This decorator should only be used on small values that makes sense to pass in -registers or on the stack (e.g. 1-3 machine registers), and cannot be used on -types like `llvm::SmallVector` that have interior pointers (such a type doesn’t -make sense to pass by-register anyway!). - -# Conclusion - -This proposal attempts to synthesize ideas from a number of well known systems -into something that will fit well with Mojo, be easy to use, and easy to teach - -building on the Python ideology of “reducing magic”. It provides equivalent -expressive power to C++, while being a building block to provide the full power -for Rust-style lifetimes. - -## Parameter Conventions Summary - -This is the TL;DR: summary of what I think we end up with: - -```mojo - fn borrow_it(a: X) # takes X as borrow (sugar). - fn borrow_it(borrowed a: X) # takes X as borrow. - fn take_it(owned a: X) # takes owned X - fn ref_it(inout a: X) # takes mutable reference to X - fn register_it(a: Int) # by copy when Int is register-passable. - - fn ret_owned(self) -> X: # Return an owned X (sugar). - fn ret_owned(self) -> owned X: # Return an owned X. - fn ret_borrow(self) -> borrowed X: # Return an X as a borrow - fn ret_ref(self) -> inout X: # Return an X as a mutable ref - fn ret_register(self) -> Int: # Return by copy when Int is register-passable -``` - -## Extension for Lifetime types - -Lifetimes are necessary to support memory safety with non-trivial correlated -lifetimes, and have been pretty well proven in the Rust world. They will -require their own significant design process (particularly to get the defaulting -rules) and will benefit from getting all of the above implemented. - -That said, when we get to them, I believe they will fit naturally into the Mojo -and MLIR design. For example, you could imagine things like this: - -```mojo - # Take a non-copyable SomeTy as a borrow and return owned copy - fn life_ex1['a: lifetime](value: 'a SomeTy) -> SomeTy: - return value.copy() - - # Take a non-copyable SomeTy and return the reference - fn life_ex2['a: lifetime](value: 'a SomeTy) -> borrowed 'a SomeTy: - return value -``` - -This is not a concrete syntax proposal, just a sketch. A full design is outside -the scope of this proposal though, it should be a subsequent one. - -# Appendix: Decorators and Type Traits - -Above we talk loosely about decorators and type traits. A decorator in Python -is a modifier for a type or function definition. A Trait in Rust (aka protocol -in Swift, aka typeclass in Haskell) is a set of common behavior that unifies -types - sort of like an extended Java interface. - -Let’s see how these two concepts can come together in the future assuming we get -Swift/Rust-style traits and extend Python’s decorator concept with -metaprogramming features enabled by Mojo. - -Traits include “requirements”: signatures that conforming types are required to -have, and may also include default implementations for those. The type can -implement it manually if they want, but can also just inherit the default -implementation if not. Let’s consider copy-ability. This isn’t a standard -library proposal, but we could implement some copy traits like this: - -```mojo - trait Copyable: - # Just a signature, no body. - fn copy(self) -> Self: ... - - trait ImplicitlyCopyable(Copyable): # Could use a better name :) - # A __copyinit__ is required, and this is the default implementation. - fn __copyinit__(inout self, borrowed existing: Self): - self = existing.copy() -``` - -Type may conform to the `Copyable` trait, which allows generic algorithms to -know it has a `copy()` method. Similarly, they may conform to -`ImplicitlyCopyable` to know it is implicitly copable (supports “`let a = b`”). -`ImplicitlyCopyable` requires the type to have a `copy()` method (because -`ImplicitlyCopyable` refines `Copyable`) and a `__copyinit__` method with the -specified signatures, and also provides a default implementation of the -`__copyinit__` method. - -This allows types to use it like this: - -```mojo - struct Int(ImplicitlyCopyable): - var value: __mlir_type.index - fn copy(self: Self) -> Self: - return Int{value: self.value} - - # Don't have to write this, we get a default impl from ImplicitlyCopyable - # fn __copyinit__(inout self, borrowed existing: Self): - # self = existing.copy() -``` - -This allows clients to implement a simple `copy()` method, but get the internal -machinery for free. - -The decorator is a different thing that layers on top of it. Decorators in -Python are functions that use metaprogramming to change the declaration they are -attached to. Python does this with dynamic metaprogramming, but we’ll use the -interpreter + built-ins operations to also enable static metaprogramming in -Mojo. - -I'm imagining that this will allow someone to write just: - -```mojo - @implicitlyCopyable - struct Int: - var value : __mlir_type.index -``` - -And the `implicitlyCopyable` function (which implements the decorator) would be -implemented to do two things: - -1. When it understands all the stored properties of a copy, because they are -built-in MLIR types like index, or because they themselves conform to at least -`Copyable`, it synthesizes an implementation of a `copy()` method that builds a -new instance of the type by invoking the `copy()` member for each element. - -2. It adds conformance to the `ImplicitlyCopyable` trait, which provides the -`__copyinit__` method above. - -This is all precedented in languages like Swift and Rust, but they both lack the -metaprogramming support to make the decorator synthesis logic implementable in -the standard library. Swift does this for things like the Hashable and -`Equatable` protocols. I believe that Mojo will be able to support much nicer -and more extensible designs. - -NOTE: The `@value` decorator provides some of this now. From 97cee04ef8b196ea46d114a95e6be0a1f8f0313e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 2 May 2024 19:51:37 -0700 Subject: [PATCH 0390/2019] [KGEN] Move the `pop.coroutine.*` ops to the `co` dialect (NFC) (#39182) Stacked PRs: * #39185 * __->__#39182 --- --- --- ### [KGEN] Move the `pop.coroutine.*` ops to the `co` dialect (NFC) This moves the rest of the coroutine op machinery over to the `co` dialect. The next steps are to clean up some of the dependencies and decouple `lit.async.call` and `lit.async.execute` from the LITDialect as well. (`lit.async.call` will have to stay for lifetime reasons). MODULAR_ORIG_COMMIT_REV_ID: 6d91d1bc1d50466d2cb2ce821c6d10c8f05d09e6 --- stdlib/src/builtin/coroutine.mojo | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index c5fcb1c79a..d357ce6e82 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -53,7 +53,7 @@ fn _coro_resume_callback( @always_inline fn _coro_resume_fn(handle: _CoroutineContext._opaque_handle): """This function is a generic coroutine resume function.""" - __mlir_op.`pop.coroutine.resume`(handle.address) + __mlir_op.`co.resume`(handle.address) fn _coro_resume_noop_callback( @@ -94,9 +94,9 @@ struct Coroutine[type: AnyRegType]: Returns: The coroutine promise. """ - var promise: Pointer[ - Self._promise_type - ] = __mlir_op.`pop.coroutine.promise`(self._handle) + var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`( + self._handle + ) return promise.bitcast[type]() @always_inline @@ -135,7 +135,7 @@ struct Coroutine[type: AnyRegType]: The constructed coroutine object. """ var self = Coroutine[type] {_handle: handle} - var parent_hdl = __mlir_op.`pop.coroutine.opaque_handle`() + var parent_hdl = __mlir_op.`co.opaque_handle`() self._get_ctx[_CoroutineContext]().store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl @@ -146,7 +146,7 @@ struct Coroutine[type: AnyRegType]: @always_inline fn __del__(owned self): """Destroy the coroutine object.""" - __mlir_op.`pop.coroutine.destroy`(self._handle) + __mlir_op.`co.destroy`(self._handle) @always_inline fn __call__(self) -> type: @@ -162,7 +162,7 @@ struct Coroutine[type: AnyRegType]: _parent_hdl: _CoroutineContext._opaque_handle.get_null(), } ) - __mlir_op.`pop.coroutine.resume`(self._handle) + __mlir_op.`co.resume`(self._handle) return self.get() @always_inline @@ -174,10 +174,10 @@ struct Coroutine[type: AnyRegType]: """ __mlir_region await_body(): - __mlir_op.`pop.coroutine.resume`(self._handle) - __mlir_op.`pop.coroutine.await.end`() + __mlir_op.`co.resume`(self._handle) + __mlir_op.`co.await.end`() - __mlir_op.`pop.coroutine.await`[_region = "await_body".value]() + __mlir_op.`co.await`[_region = "await_body".value]() return self.get() @@ -214,9 +214,9 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The coroutine promise. """ - var promise: Pointer[ - Self._promise_type - ] = __mlir_op.`pop.coroutine.promise`(self._handle) + var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`( + self._handle + ) return promise.bitcast[Self._var_type]() @always_inline @@ -255,7 +255,7 @@ struct RaisingCoroutine[type: AnyRegType]: handle: The init handle. """ self = Self {_handle: handle} - var parent_hdl = __mlir_op.`pop.coroutine.opaque_handle`() + var parent_hdl = __mlir_op.`co.opaque_handle`() self._get_ctx[_CoroutineContext]().store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl @@ -265,7 +265,7 @@ struct RaisingCoroutine[type: AnyRegType]: @always_inline fn __del__(owned self): """Destroy the coroutine object.""" - __mlir_op.`pop.coroutine.destroy`(self._handle) + __mlir_op.`co.destroy`(self._handle) @always_inline fn __call__(self) raises -> type: @@ -284,7 +284,7 @@ struct RaisingCoroutine[type: AnyRegType]: _parent_hdl: _CoroutineContext._opaque_handle.get_null(), } ) - __mlir_op.`pop.coroutine.resume`(self._handle) + __mlir_op.`co.resume`(self._handle) return self.get() @always_inline @@ -296,8 +296,8 @@ struct RaisingCoroutine[type: AnyRegType]: """ __mlir_region await_body(): - __mlir_op.`pop.coroutine.resume`(self._handle) - __mlir_op.`pop.coroutine.await.end`() + __mlir_op.`co.resume`(self._handle) + __mlir_op.`co.await.end`() - __mlir_op.`pop.coroutine.await`[_region = "await_body".value]() + __mlir_op.`co.await`[_region = "await_body".value]() return self.get() From 86d4be48c29eb632fac7391dc2ea513f0de88432 Mon Sep 17 00:00:00 2001 From: Jost Migenda Date: Thu, 2 May 2024 22:18:00 -0600 Subject: [PATCH 0391/2019] [External] [docs] fix typos in roadmap.md (#39231) [External] [docs] fix typos in roadmap.md Co-authored-by: Jost Migenda Closes modularml/mojo#2466 MODULAR_ORIG_COMMIT_REV_ID: 1662081326af3fba364859dabe5daf13c5696931 --- docs/roadmap.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index afcb0084f1..374bc97af3 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -430,7 +430,7 @@ This is because whether `i` is defined at this line is dynamic in Python. For instance the following Python program will fail: ```python -for i range(0): pass +for i in range(0): pass print(i) ``` @@ -461,7 +461,7 @@ fn bar(): ### Name scoping of nested function declarations In Python, nested function declarations produce dynamic values. They are -essentially syntax sugar for `bar = lambda ...`. +essentially syntactic sugar for `bar = lambda ...`. ```python def foo(): @@ -650,7 +650,7 @@ The upstream dialects available in the Playground are the ### `@value` is limited with trait conformance check -Structs with `@value` decorator still need to explicitly provide dundner +Structs with `@value` decorator still need to explicitly provide dunder methods such as `__init__`, `__copyinit__`, and `__moveinit__` when both of the following are true: From cd9c3fe68176da6bbcc62b9e7254445362945ded Mon Sep 17 00:00:00 2001 From: Yiwu Chen <210at85@gmail.com> Date: Thu, 2 May 2024 22:27:26 -0600 Subject: [PATCH 0392/2019] [External] [stdlib] Fix accidental use of `assert_true` in tests (#39229) [External] [stdlib] Fix accidental use of `assert_true` in tests Found when trying to remove `String` constructor from `Stringable`. Implicit conversion is just, evil. Co-authored-by: Yiwu Chen <210at85@gmail.com> Closes modularml/mojo#2475 MODULAR_ORIG_COMMIT_REV_ID: 4dec84c0069d44da8d4f991684a267291940a83a --- stdlib/test/builtin/test_slice.mojo | 2 +- stdlib/test/python/test_python_object.mojo | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index ca874a3511..672d14e799 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -18,7 +18,7 @@ from testing import assert_equal, assert_false, assert_true def test_none_end_folds(): alias all_def_slice = slice(0, None, 1) assert_equal(all_def_slice.start, 0) - assert_true(all_def_slice.end, Int.MAX) + assert_equal(all_def_slice.end, int(Int32.MAX)) assert_equal(all_def_slice.step, 1) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 1c760bdb92..b89838541a 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -26,11 +26,11 @@ def test_dunder_methods(inout python: Python): # __add__ var c = a + b - assert_true(c, 44) + assert_equal(c, 44) # __add__ c = a + 100 - assert_true(c, 134) + assert_equal(c, 134) # __iadd__ c += 100 From 05702e470edec2be376034e6a31bbd3063d6d8e0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 2 May 2024 22:02:10 -0700 Subject: [PATCH 0393/2019] [mojo-stdlib] Keep moving more stuff out from `-> Self` (#39217) This continues the purge. MODULAR_ORIG_COMMIT_REV_ID: 5f1c5e32bc8a0aefe7184c8d386371cc54b7d6fc --- .../manual/decorators/nonmaterializable.ipynb | 8 ++--- docs/manual/parameters/index.ipynb | 4 +-- examples/notebooks/BoolMLIR.ipynb | 32 +++++++++---------- examples/notebooks/RayTracing.ipynb | 19 ++++++----- examples/notebooks/programming-manual.ipynb | 4 +-- stdlib/src/builtin/int.mojo | 10 +++--- 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index 0c43b0ad2a..1078954e90 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -50,12 +50,12 @@ "struct HasBool:\n", " var x: Bool\n", "\n", - " fn __init__(x: Bool) -> Self:\n", - " return Self {x: x}\n", + " fn __init__(inout self, x: Bool):\n", + " self.x = x\n", "\n", " @always_inline(\"nodebug\")\n", - " fn __init__(nms: NmStruct) -> Self:\n", - " return Self {x: True if (nms.x == 77) else False}\n", + " fn __init__(inout self, nms: NmStruct):\n", + " self.x = True if (nms.x == 77) else False\n", "\n", "@value\n", "@nonmaterializable(HasBool)\n", diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 1356bf4253..b733e8c343 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -371,8 +371,8 @@ " var value: Int\n", "\n", " @always_inline(\"nodebug\")\n", - " fn __init__(_a: Int) -> Self:\n", - " return Self {value: _a}\n", + " fn __init__(inout self, _a: Int):\n", + " self.value = _a\n", "\n", "fn foo[x: MyInt, a: Int]():\n", " print(\"foo[x: MyInt, a: Int]()\")\n", diff --git a/examples/notebooks/BoolMLIR.ipynb b/examples/notebooks/BoolMLIR.ipynb index 37abdd8c3a..2e4a6a09b3 100644 --- a/examples/notebooks/BoolMLIR.ipynb +++ b/examples/notebooks/BoolMLIR.ipynb @@ -191,12 +191,10 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__() -> Self:\n", - " return Self {\n", - " value: __mlir_op.`index.bool.constant`[\n", + " fn __init__(inout self):\n", + " self.value = __mlir_op.`index.bool.constant`[\n", " value=__mlir_attr.false,\n", - " ]()\n", - " }" + " ]()" ] }, { @@ -241,8 +239,8 @@ "\n", " # ...\n", "\n", - " fn __init__(value: __mlir_type.i1) -> Self:\n", - " return Self {value: value}" + " fn __init__(inout self, value: __mlir_type.i1):\n", + " self.value = value" ] }, { @@ -305,11 +303,11 @@ " var value: __mlir_type.i1\n", "\n", " # We can simplify our no-argument constructor:\n", - " fn __init__() -> Self:\n", - " return OurFalse\n", + " fn __init__(inout self):\n", + " self = OurFalse\n", "\n", - " fn __init__(value: __mlir_type.i1) -> Self:\n", - " return Self {value: value}" + " fn __init__(inout self, value: __mlir_type.i1):\n", + " self.value = value" ] }, { @@ -371,8 +369,8 @@ "\n", " # ...\n", "\n", - " fn __init__(value: __mlir_type.i1) -> Self:\n", - " return Self {value: value}\n", + " fn __init__(inout self, value: __mlir_type.i1):\n", + " self.value = value\n", "\n", " # Our new method converts `OurBool` to `Bool`:\n", " fn __bool__(self) -> Bool:\n", @@ -443,8 +441,8 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__(value: __mlir_type.i1) -> Self:\n", - " return Self {value: value}\n", + " fn __init__(inout self, value: __mlir_type.i1):\n", + " self.value = value\n", "\n", " # Our new method converts `OurBool` to `i1`:\n", " fn __mlir_i1__(self) -> __mlir_type.i1:\n", @@ -509,8 +507,8 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__(value: __mlir_type.i1) -> Self:\n", - " return Self {value: value}\n", + " fn __init__(inout self, value: __mlir_type.i1):\n", + " self.value = value\n", "\n", " # ...\n", "\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 934e157b63..cfdd6383d0 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -71,12 +71,12 @@ " var data: SIMD[DType.float32, 4]\n", "\n", " @always_inline\n", - " fn __init__(x: Float32, y: Float32, z: Float32) -> Self:\n", - " return Vec3f {data: SIMD[DType.float32, 4](x, y, z, 0)}\n", + " fn __init__(inout self, x: Float32, y: Float32, z: Float32):\n", + " self.data = SIMD[DType.float32, 4](x, y, z, 0)\n", "\n", " @always_inline\n", - " fn __init__(data: SIMD[DType.float32, 4]) -> Self:\n", - " return Vec3f {data: data}\n", + " fn __init__(inout self, data: SIMD[DType.float32, 4]):\n", + " self.data = data\n", "\n", " @always_inline\n", " @staticmethod\n", @@ -439,8 +439,10 @@ " var radius: Float32\n", " var material: Material\n", "\n", - " fn __init__(c: Vec3f, r: Float32, material: Material) -> Self:\n", - " return Sphere {center: c, radius: r, material: material}\n", + " fn __init__(inout self, c: Vec3f, r: Float32, material: Material):\n", + " self.center = c\n", + " self.radius = r\n", + " self.material = material\n", "\n", " @always_inline\n", " fn intersects(self, orig: Vec3f, dir: Vec3f, inout dist: Float32) -> Bool:\n", @@ -675,8 +677,9 @@ " var position: Vec3f\n", " var intensity: Float32\n", "\n", - " fn __init__(p: Vec3f, i: Float32) -> Self:\n", - " return Light {position: p, intensity: i}\n" + " fn __init__(inout self, p: Vec3f, i: Float32):\n", + " self.position = p\n", + " self.intensity = i\n" ] }, { diff --git a/examples/notebooks/programming-manual.ipynb b/examples/notebooks/programming-manual.ipynb index ba8f6abe01..cd076d51e5 100644 --- a/examples/notebooks/programming-manual.ipynb +++ b/examples/notebooks/programming-manual.ipynb @@ -1623,8 +1623,8 @@ " var value: Int\n", "\n", " @always_inline(\"nodebug\")\n", - " fn __init__(_a: Int) -> Self:\n", - " return Self {value: _a}\n", + " fn __init__(inout self, _a: Int):\n", + " self.value = _a\n", "\n", "fn foo[x: MyInt, a: Int]():\n", " print(\"foo[x: MyInt, a: Int]()\")\n", diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 283b2b3c38..b0bc8c206e 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -216,7 +216,7 @@ struct Int( """Returns the minimum value of type.""" @always_inline("nodebug") - fn __init__() -> Int: + fn __init__() -> Self: """Default constructor. Returns: @@ -227,7 +227,7 @@ struct Int( } @always_inline("nodebug") - fn __init__(value: __mlir_type.index) -> Int: + fn __init__(value: __mlir_type.index) -> Self: """Construct Int from the given index value. Args: @@ -239,7 +239,7 @@ struct Int( return Self {value: value} @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Int: + fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: """Construct Int from the given Int16 value. Args: @@ -255,7 +255,7 @@ struct Int( ) @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Int: + fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: """Construct Int from the given Int32 value. Args: @@ -271,7 +271,7 @@ struct Int( ) @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Int: + fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: """Construct Int from the given Int64 value. Args: From 0fa59614150b081acba470cdfb86cf2d00d22e59 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 2 May 2024 23:20:42 -0700 Subject: [PATCH 0394/2019] [docs] Modernize Ray tracing notebook with UnsafePointer. (#39219) Also updates the register-passable types to use the standard constructor style. Contributes to DOCS-90 MODULAR_ORIG_COMMIT_REV_ID: 86b5bc5a4138d8dfe631ae37886920484251c7d9 --- examples/notebooks/RayTracing.ipynb | 184 ++++++++++++++-------------- 1 file changed, 90 insertions(+), 94 deletions(-) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index cfdd6383d0..374ddc2927 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -40,7 +40,13 @@ "id": "c4edb2c9-5109-4e00-98c0-3aa92dbca7d1", "metadata": {}, "source": [ - "This tutorial about [ray tracing](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) is based on the popular tutorial [Understandable RayTracing in C++](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing). The mathematical explanations are well described in that tutorial, so we'll just point you to the appropriate sections for reference as we implement a basic ray tracer in Mojo." + "This tutorial about [ray\n", + "tracing](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) is based on the\n", + "popular tutorial [Understandable RayTracing in\n", + "C++](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing).\n", + "The mathematical explanations are well described in that tutorial, so we'll\n", + "just point you to the appropriate sections for reference as we implement a\n", + "basic ray tracer in Mojo." ] }, { @@ -51,7 +57,11 @@ "source": [ "## Step 1: Basic definitions\n", "\n", - "We'll start by defining a `Vec3f` struct, which will use to represent a vector in 3D space as well as RGB pixels. We'll use a `SIMD` representation for our vector to enable vectorized operations. Note that since the SIMD type only allows a power of 2, we always pad the underlying storage with a 0." + "We'll start by defining a `Vec3f` struct, which will use to represent a vector\n", + "in 3D space as well as RGB pixels. We'll use a `SIMD` representation for our\n", + "vector to enable vectorized operations. The `SIMD` type is a fixed-size vector,\n", + "and its size must be a power of 2. So we'll use a size of 4 and always pad the\n", + "underlying storage with a 0." ] }, { @@ -126,7 +136,10 @@ "id": "2619eb1b-103b-4453-9725-4480e388452e", "metadata": {}, "source": [ - "We now define our `Image` struct, which will store the RGB pixels of our images. It also contains a method to convert this Mojo struct into a numpy image, which will be used for implementing a straightforward displaying mechanism. We will also implement a function for loading PNG files from disk." + "We now define our `Image` struct, which will store the RGB pixels of our\n", + "images. It also contains a method to convert this Mojo struct into a NumPy\n", + "image, which will be used for implementing a straightforward display\n", + "mechanism. We will also implement a function for loading PNG files from disk." ] }, { @@ -184,21 +197,22 @@ "source": [ "from python import Python\n", "from python import PythonObject\n", + "from memory.unsafe_pointer import UnsafePointer\n", "\n", "struct Image:\n", " # reference count used to make the object efficiently copyable\n", - " var rc: Pointer[Int]\n", + " var rc: UnsafePointer[Int]\n", " # the two dimensional image is represented as a flat array\n", - " var pixels: Pointer[Vec3f]\n", + " var pixels: UnsafePointer[Vec3f]\n", " var height: Int\n", " var width: Int\n", "\n", " fn __init__(inout self, height: Int, width: Int):\n", " self.height = height\n", " self.width = width\n", - " self.pixels = Pointer[Vec3f].alloc(self.height * self.width)\n", - " self.rc = Pointer[Int].alloc(1)\n", - " self.rc.store(1)\n", + " self.pixels = UnsafePointer[Vec3f].alloc(self.height * self.width)\n", + " self.rc = UnsafePointer[Int].alloc(1)\n", + " self.rc[] = 1\n", "\n", " fn __copyinit__(inout self, other: Self):\n", " other._inc_rc()\n", @@ -210,19 +224,14 @@ " fn __del__(owned self):\n", " self._dec_rc()\n", "\n", - " fn _get_rc(self) -> Int:\n", - " return self.rc.load()\n", - "\n", " fn _dec_rc(self):\n", - " var rc = self._get_rc()\n", - " if rc > 1:\n", - " self.rc.store(rc - 1)\n", + " if self.rc[] > 1:\n", + " self.rc[] -= 1\n", " return\n", " self._free()\n", "\n", " fn _inc_rc(self):\n", - " var rc = self._get_rc()\n", - " self.rc.store(rc + 1)\n", + " self.rc[] += 1\n", "\n", " fn _free(self):\n", " self.rc.free()\n", @@ -230,7 +239,7 @@ "\n", " @always_inline\n", " fn set(self, row: Int, col: Int, value: Vec3f) -> None:\n", - " self.pixels.store(self._pos_to_index(row, col), value)\n", + " self.pixels[self._pos_to_index(row, col)] = value\n", "\n", " @always_inline\n", " fn _pos_to_index(self, row: Int, col: Int) -> Int:\n", @@ -244,28 +253,16 @@ " var np_image = np.zeros((self.height, self.width, 3), np.float32)\n", "\n", " # We use raw pointers to efficiently copy the pixels to the numpy array\n", - " var out_pointer = Pointer(\n", - " __mlir_op.`pop.index_to_pointer`[\n", - " _type=__mlir_type[`!kgen.pointer>`]\n", - " ](\n", - " SIMD[DType.index, 1](\n", - " np_image.__array_interface__[\"data\"][0].__index__()\n", - " ).value\n", - " )\n", - " )\n", - " var in_pointer = Pointer(\n", - " __mlir_op.`pop.index_to_pointer`[\n", - " _type=__mlir_type[`!kgen.pointer>`]\n", - " ](SIMD[DType.index, 1](int(self.pixels)).value)\n", + " var out_pointer = UnsafePointer[Float32](\n", + " address=int(np_image.__array_interface__[\"data\"][0])\n", " )\n", + " var in_pointer = self.pixels.bitcast[Float32]()\n", "\n", " for row in range(self.height):\n", " for col in range(self.width):\n", " var index = self._pos_to_index(row, col)\n", " for dim in range(3):\n", - " out_pointer.store(\n", - " index * 3 + dim, in_pointer[index * 4 + dim]\n", - " )\n", + " out_pointer[index * 3 + dim] = in_pointer[index * 4 + dim]\n", "\n", " return np_image\n", "\n", @@ -275,31 +272,20 @@ " var plt = Python.import_module(\"matplotlib.pyplot\")\n", "\n", " var np_image = plt.imread(fname)\n", - " var rows = np_image.shape[0].__index__()\n", - " var cols = np_image.shape[1].__index__()\n", + " var rows = int(np_image.shape[0])\n", + " var cols = int(np_image.shape[1])\n", " var image = Image(rows, cols)\n", "\n", - " var in_pointer = Pointer(\n", - " __mlir_op.`pop.index_to_pointer`[\n", - " _type=__mlir_type[`!kgen.pointer>`]\n", - " ](\n", - " SIMD[DType.index, 1](\n", - " np_image.__array_interface__[\"data\"][0].__index__()\n", - " ).value\n", - " )\n", - " )\n", - " var out_pointer = Pointer(\n", - " __mlir_op.`pop.index_to_pointer`[\n", - " _type=__mlir_type[`!kgen.pointer>`]\n", - " ](SIMD[DType.index, 1](int(image.pixels)).value)\n", + " var in_pointer = UnsafePointer[Float32](\n", + " address=int(np_image.__array_interface__[\"data\"][0])\n", " )\n", + " var out_pointer = image.pixels.bitcast[Float32]()\n", + "\n", " for row in range(rows):\n", " for col in range(cols):\n", " var index = image._pos_to_index(row, col)\n", " for dim in range(3):\n", - " out_pointer.store(\n", - " index * 4 + dim, in_pointer[index * 3 + dim]\n", - " )\n", + " out_pointer[index * 4 + dim] = in_pointer[index * 3 + dim]\n", " return image\n" ] }, @@ -309,7 +295,8 @@ "id": "7a7dbe6f-f35f-42c5-ad37-b7400f6262f8", "metadata": {}, "source": [ - "We then add a function for quickly displaying an `Image` into the notebook. Our Python interop comes in quite handy." + "We then add a function for quickly displaying an `Image` into the notebook. Our\n", + "Python interop comes in quite handy." ] }, { @@ -337,7 +324,9 @@ "id": "5f42f174-d7d9-4f6c-9b4e-d0d586f8f5ec", "metadata": {}, "source": [ - "Finally, we test all our code so far with a simple image, which is the one rendered in the [Step 1 of the C++ tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-1-write-an-image-to-the-disk)." + "Finally, we test all our code so far with a simple image, which is the one\n", + "rendered in the [Step 1 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-1-write-an-image-to-the-disk)." ] }, { @@ -350,7 +339,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAFnCAYAAADjbJN9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAATsAAAE7AGKbv1yAAAGG0lEQVR4nO3ZwWoCQRRFQVv8/19+LtyMILjyTMtUbUICCWZ1uP3WzMwNAPi5+9kfAACu4nH8Zq11+ObD108/+/Z1x9/xmfb4+zt+Jv/zHr/z739/x890xf95o9+Z9XpUtnQBIPK+dM/6FABwAZYuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIWLoAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQ8bwMABFLFwAiogsAEc/LABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgYukCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJAxPMyAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiKULABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEc/LABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQGTNzJz9IQDgCixdAIiILgBEnv7cEL5KaxYBAAAAAElFTkSuQmCC" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAFnCAYAAADjbJN9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAAGG0lEQVR4nO3ZwWoCQRRFQVv8/19+LtyMILjyTMtUbUICCWZ1uP3WzMwNAPi5+9kfAACu4nH8Zq11+ObD108/+/Z1x9/xmfb4+zt+Jv/zHr/z739/x890xf95o9+Z9XpUtnQBIPK+dM/6FABwAZYuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIWLoAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQ8bwMABFLFwAiogsAEc/LABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgYukCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJAxPMyAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiKULABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEc/LABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQGTNzJz9IQDgCixdAIiILgBEnv7cEL5KaxYBAAAAAElFTkSuQmCC" }, "metadata": {}, "output_type": "display_data" @@ -385,7 +374,10 @@ "source": [ "## Step 2: Ray tracing\n", "\n", - "Now we'll perform ray tracing from a camera into a scene with a sphere. Before reading the code below, we suggest you read more about how this works conceptually from [Step 2 of the C++ tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-2-the-crucial-one-ray-tracing)." + "Now we'll perform ray tracing from a camera into a scene with a sphere. Before\n", + "reading the code below, we suggest you read more about how this works\n", + "conceptually from [Step 2 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-2-the-crucial-one-ray-tracing)." ] }, { @@ -394,37 +386,30 @@ "id": "778d8339-5b19-47ee-bdc6-a08e4ec5094c", "metadata": {}, "source": [ - "We first define the `Material` and `Sphere` structs, which are the new data structures we'll need." + "We first define the `Material` and `Sphere` structs, which are the new data\n", + "structures we'll need." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "id": "1c4a79de-0b65-48ac-97c9-187b74b96b9f", "metadata": {}, "outputs": [], "source": [ "from math import sqrt\n", "\n", - "\n", "@register_passable(\"trivial\")\n", "struct Material:\n", " var color: Vec3f\n", " var albedo: Vec3f\n", " var specular_component: Float32\n", "\n", - " fn __init__(color: Vec3f) -> Material:\n", - " return Material {\n", - " color: color, albedo: Vec3f(0, 0, 0), specular_component: 0\n", - " }\n", - "\n", - " fn __init__(\n", - " color: Vec3f, albedo: Vec3f, specular_component: Float32\n", - " ) -> Material:\n", - " return Material {\n", - " color: color, albedo: albedo, specular_component: specular_component\n", - " }\n", - "\n", + " fn __init__(inout self, color: Vec3f, albedo: Vec3f = Vec3f(0, 0, 0),\n", + " specular_component: Float32 = 0):\n", + " self.color = color\n", + " self.albedo = albedo\n", + " self.specular_component = specular_component\n", "\n", "alias W = 1024\n", "alias H = 768\n", @@ -433,17 +418,13 @@ "var green_rubber = Material(Vec3f( 0.3, 0.7, 0.3), Vec3f(0.9, 0.1, 0), 1.0)\n", "\n", "\n", + "@value\n", "@register_passable(\"trivial\")\n", "struct Sphere(CollectionElement):\n", " var center: Vec3f\n", " var radius: Float32\n", " var material: Material\n", "\n", - " fn __init__(inout self, c: Vec3f, r: Float32, material: Material):\n", - " self.center = c\n", - " self.radius = r\n", - " self.material = material\n", - "\n", " @always_inline\n", " fn intersects(self, orig: Vec3f, dir: Vec3f, inout dist: Float32) -> Bool:\n", " \"\"\"This method returns True if a given ray intersects this sphere.\n", @@ -482,7 +463,9 @@ "id": "e5ed8e76-efc1-4905-90ea-4cad6df0c553", "metadata": {}, "source": [ - "We then define a `cast_ray` method, which will be used to figure out the color of a particular pixel in the image we'll produce. It basically works by identifying whether this ray intersects the sphere or not." + "We then define a `cast_ray` method, which will be used to figure out the color\n", + "of a particular pixel in the image we'll produce. It basically works by\n", + "identifying whether this ray intersects the sphere or not." ] }, { @@ -521,7 +504,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAATsAAAE7AGKbv1yAABKUUlEQVR4nOzde5CddX3H8e85u3t2N9lkc11CCESEXEgD2CRY1KJStNrRabWidAZGOy3qH62O1dE6dRCYsVXqH7ZTW+8WrdVpB2+1DkWQKVQCbeQypSAkQAhJSHYJuWw2ezm7e57+EQ3BAiWw333O7nm9ZpjZk7MhnznJHzl5n9/zVIqiKAIAAAAAAACAKVUtewAAAAAAAADAbCTGAgAAAAAAACRoP/5BrVYrawcAAAAAAADAjFev14997WQsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEA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AABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAP63fTs4AQAEYCCG+w9dpzgESSbo/ygAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAIDA2bbXIwAAAAAAAAB+4xkLAAAAAAAAEBBjAQAAAAAAAAIXPgf3yqAAlNMAAAAASUVORK5CYII=" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAABKUUlEQVR4nOzde5CddX3H8e85u3t2N9lkc11CCESEXEgD2CRY1KJStNrRabWidAZGOy3qH62O1dE6dRCYsVXqH7ZTW+8WrdVpB2+1DkWQKVQCbeQypSAkQAhJSHYJuWw2ezm7e57+EQ3BAiWw333O7nm9ZpjZk7MhnznJHzl5n9/zVIqiKAIAAAAAAACAKVUtewAAAAAAAADAbCTGAgAAAAAAACRoP/5BrVYrawcAAAAAAADAjFev14997WQsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEA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AABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAP63fTs4AQAEYCCG+w9dpzgESSbo/ygAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAIDA2bbXIwAAAAAAAAB+4xkLAAAAAAAAEBBjAQAAAAAAAAIXPgf3yqAAlNMAAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" @@ -568,12 +551,14 @@ "source": [ "## Step 3: More spheres\n", "\n", - "This section corresponds to the [Step 3 of the C++ tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-3-add-more-spheres). \n", + "This section corresponds to the [Step 3 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-3-add-more-spheres).\n", "\n", "We include here all the necessary changes:\n", "\n", - "- We add 3 more spheres to the scene, 2 of them being of ivory material.\n", - "- When we intersect the ray with the sphere, we render the color of the closest sphere." + "- We add 3 more spheres to the scene, 2 of them being of green rubber material.\n", + "- When we intersect the ray with the sphere, we render the color of the closest\n", + " sphere." ] }, { @@ -584,7 +569,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAATsAAAE7AGKbv1yAACKqklEQVR4nOzdd5hdd30n/ve5d+70plGXbMm2uptcMN00h5YQAtmwIUsNkJBNNgUSkk3IhmUT8sumJ8+PFNLYpW1Ywoaw+2Npxiam2oANuFtuki1ZvY6m3vv7Q0a2cZWlM+fOzOv1PHqYe+femTdn7sx4zvt8vt+i1Wq1AgAAAAAAAMApVas6AAAAAAAAAMBcpIwFAAAAAAAAKEHHg290dnZWlQMAAAAAAABg1puYmDj+tslYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAA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nszHPnZ7PvWpuzM11aw6DsCMabVaueKKe/ORj9yWPXvGqo4DAJwiylgAAAAAAACAElimGAAAAACgQocPT+bw4cmMj09XHQUAOMWUsQAAAAAAFXr/+2/Ohz50S+69d7TqKADAKaaMBQAAANrO6OhUbrxxf5Yt68nKlX1JiqojAZxye/eOZceO0dx0077ccsuBquMAACUoWq1W63s3Ojs7q8wCAAAAkCTp6CgyMNCZV7zijPzBHzwjRaGMBeaeD3/41rzrXVdndHQqY2OWKAaAuWJiYuL42yZjAQAAgLYzNdXKvn3jOXx4Kq1WoosF5qLx8ens3TtedQwAoES1qgMAAAAAAAAAzEXKWAAAAKBt3Xbbgfzt396Yb3xjV9VRAE6ZrVsP5+/+7sZcddX2qqMAACWzZywAAADQ9t7xjs35j//xoqpjAJwSn//8trz2tZ/PxESz6igAQAkevGesyVgAAAAAAACAEnRUHQAAAAAAYD5otVqZnj7274H1CgGAuUwZCwAAAAAwA+6550h+93e/mS1bDmZqyhLFADAfKGMBAACAtjc2Np39+8fT29uRzs561XEATkiz2crhw5O5997RfPaz27J373jVkQCAGVK0Wg8siNHZ2VllFgAAAIBHtGRJT5Yt682v/doFeclLVlUdB+CE7N8/nre97cu56aZ92bLlYKanrVEMAHPZxMTE8bdNxgIAAABtb+fOo9m582j27TNNBsw+09Ot3Hrr/txyy4GqowAAM6xWdQAAAAAAAACAuUgZCwAAAMwat9xyIF/60vYcOGBCFmh/zWYr3/3u3nzlK/flyJGpquMAABWwZywAAAAwazQatfT1deSDH7wsz3jGsqrjADymycnp/ORPXpHLL78nExPTadkqFgDmBXvGAgAAALPS5GQz4+PTaTY1GkA7a+Xqq3flhhv25c47D2V8fLrqQABARZSxAAAAAACn2Mc+tiV/+7c3VR0DAKiYPWMBAACAWWVyspkPfODW/Pmffyd7945VHQfgEVmSGABIlLEAAADALDM11cr//J9b8r733ZD9+yce/wkAM6jVaqXZrDoFANAuLFMMAAAAUKpWkqLqELNcWcfwRD9u2Y+fD9rlGJb3tfnc57blox/dkmuv3VPKxwcAZhdlLAAAADArNZutjI5O5ejRqXR311MU7Vp6tWuu2aSsY3iiH7fsx88H7XIM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+plce+3uakPNMutftz5n/siZD9zhZcpjaSXT49P5xnu+kX037Ks6TZJje26fdlpf/uVfXppVqwaqjgNz0vj4dF772s/nC1+4Jw+czQQAaE8mYwEAYA677LKVueiiRUmSnp6OLFnSk6LQbpWlKIp87/B2d3fk9a9fnxe+cGWSZOvWI/nYx27P1PeWWOUhBtcMZvFFizO8cVgByxNXHNtPduXzV2bknJEkyfi+8dx75b1pTlbzvdZqJQcOTOR977shmzYtyKtetSadprvhpH3ta/fli188tiz51FQzd9xxUBELAMw6ylgAAJhDiiJ54QtPy0/91NlVR5mXurrqecMbNhy//eUv78i//MudythHMbR2KGtfvbbqGMxCRb3IyhesPH774JaD2fGVHZWVsUly8OBk/vIvb8hTnrI4L3/5GWk0alYkgCfpewv5ffWr9+X3fu9bFacBADg5likGAIBZ7vzzR/KWt2xKrXbshP/FFy/O+vXD1YYiSbJz59FceeW9+fKXd+S///dbqo7TNvpX9Wf1y1an//T+DHutcgpMHprM7mt3Z/8t+3P3p+5OKpycW7iwO8973oo8/elL88Y3brj/Z3MrStmT5RjOJ9/85q78wz/clOuv35frrttTdRwAgBNmmWIAAJgDiuLYJOaZZw7mx398bTo6alVH4vssWdKTV71qTYoi+djHbj8+6TMx0cz09DxcZ/H+5WV7l/ZmxXNXpOY1yynSGGhk+aXLU++u557L70lzsplWRd9je/aM5Z/+6fZMTjbz4z9+bLniRsNr/eQpYueyVquV8fHp40sQb9lyMP/jf2xJszkPf1cCAHOOyVgAAJilzjprML/zO5dk1aqBbNw4bF/YNrZz59HceuuBJK20Wsmf/Mm3c8UV91Yda8b1LOnJxp/cmJ6lPRlYNaBb4ZSbODiRI9uOZPtV27P101srzbJoUXfWrx/Kq161Jq9//YbHfwLMY7t3H82v//rXsmPH0STHLmq4+eb91YYCADgJJmMBAGAWq9WKLFzYldWrB/L0py/L0JCLKtvdkiU9WbKkJ8mx6Z///b/vyk037cveveOZmJjb+8k2Bhqp3T8V2LOkJws2LUhjoFFxKuaqzsHOdJ7dmcPbDqdrpCtTR6YyPT5dSZbdu8eye/dYzj57JNu3H7n/3iIjI13p6qpXkgnaTavVyt6949m69XC+/vWd2bbtyOM/CQBgljEZCwAAs8yiRd35q796TtavH8qyZX2p140Xzi6t7Nw5ll27juYXfuGqXHvt3N0Lr6gXOfutZ2fk7JEkx5Yo7hrpSlHzmqVck0cmM3lwMrf9j9uy/artlWYZHu7MggVdSY4tLf8nf/KsPPWpSyrNBO3i6NGp/MIvXJWrr96V7duPZGrKssQAwNxgMhYAAGahoji2NPHq1QNZu3YoK1f2Vx2JJ6XIkiU9GRxs5LzzFmZiopnbbjsw5yZkuxd1p2ukK/0r+9O7vLfqOMwzjb5GGn2N9K/uz+C9gxndPpqpo1OVZNm/fyL79x87EdPZWct3v7snvb0dWbt2MN3dTssw/+zYMZodO0aTJGNj07n11gPZuvVwxakAAMpjMhYAAGaJrq56/v7vn5dnPWtZ+voaqZkunNVarVZGR6eydevhvPrVn5tzJ6I3vGFDTvuB01LvqqcwvU1FmhPNTI1N5bo/vi57v7O36jhJkt7ejixf3puPfOQHsmbNUNVxYMb96Z9+O3/2Z99OkrRayejoVKanTcQCAHOLyVgAAJgl1q0bysaNw0mSRqOWVasGMjBwqi6ibCU5kZKs7MfPBw8ck6Io0tfXyJIlPXnhC0/LLbfsz1e/et+sX6Kxd3lvBlYPpH9Vfzp6/clJtWqdtTTqjYycO5J6Zz37bthX2YTs94yOTmX37rF8/vP35Prr9yVJli3ryVOessRFNo+prN8pfheWbc+esXztaw/8fvvOd/bk4MHJilMBAMwck7EAANDGfumXzs9v/MaFx2/XakWKYn6f1J1rWq1Wms1WvvnN3XnVqz6TQ4dm9wnq1S9bnQ1v2HDsdeqlSptoNVuZODCRa959TQ63yRT6g4vXF7/49Lz//c9PR0etwkRQjq98ZUde/erPZXT02IUQrVYrrdl93REAwOMyGQsAAG1u3bqhvOhFp+eZz1yaet3J+bmsKIrU60WWL+/NT//02bn++r359Ke3zroT1b3Le7PkkiVZcM6CFKb7aDNFrUhHT0dWvmBlDt99ONu/tD3NivdpbjYf+Ca//faDee97v5uLLlqcSy9dXmEqOHUOHBjPxz9+R66/fm/Gx6cf8poHAJhPTMYCAEAbeuUrz8z73vdcS1bOQ5/4xB15y1uunHUnrZc8dUku+JUL7A9L2zt89+F8/V1fz2QbLpP6Mz9zdt7znqd+372+p5hNHvjddccdh/Lyl38q9947WmEeAIBqPHgyVhkLAABtZPXq/vz0T5+djRsX5LnPXW5J4nnozjsP5oor7s3ll9+T//N/7q46zuPqXtSd1S9bnf7T+rPogkV6I9re5OHJ7Lx6Zw5uOZitn96aVhtd+HD22Qvy1KcuSZLU60Xe+MYNOfvskYpTwRPTbLbygQ/ckm9/e0+S5NChyXzqU3cfX54YAGA+sUwxAAC0oUajlhUr+vLa165Pf3+j6jhU5IwzBvPGNw7myJGpfPaz2zI11WrbKdmiXqRrQVdWvmBlGn1es8wOjf5GVj5/ZboWdOWeL9yT5kSzbQrZG27Ylxtu2Jck6ego8tznrsjatUNpNGouzqEtTU+3Mj3dPP725z+/bVZcSAQAMJNMxgIAQBtYtKg77373JVm7djAXXLAoHR32iZ3v7rrrUG677UDe974b87nPbas6zsM0+htZ//r16T+tP0NrhyxPzKwzcXAiB28/mF3X7Mrdn2q/8qgokvPOW5gzzxzIu971lKxePVB1JHiYz352a973vhuTJK1WK9/97t7s2jVWcSoAgOqZjAUAgDZRFMngYGdWrOjLpZcuy8qV/VVHok2sXj2Q1av7c+WV9+Zb39qdgwcnMjnZrDrWcbVGLSNnj6R3eW/VUeBJ6RzszKILFmV873g6BzszNTaV5kT7fI+1Wsm3v70nd999KFu3Hk5f37FTOI1GPYODDZOyVGJqqpmDByeOr9hw880Hcvnl91ScCgCgvZmMBQCACvX01PMnf/KsXHTRoqxa1Z9Go151JNpKKzt2HM3OnUfzq7/6lVx99a6qAx3XtaArT/3tpypjmfUmD09mfN947vjEHbn3C/dWHedh6vUip5/en+7uY78fLrlkSX7/95+ezk6/L5h5d9xxMD//81dl377xJMn+/ePZseNoxakAANqPyVgAAGgTtVqR1av7s2bNUNVRaEtFli3rzchIVzZtWpD9+ydy112HMlHl9F6R9CzuSc+SnhQdJvOY/Rr9jTT6Gxk4fSD9p/fn6O6jmT46XXWs46anW7nzzkPHb/f3N3LjjfuOl7FLlvRk4cLuquIxx7VarWzbdiSHD08mSe6441Buumn/8TIWAIDHZzIWAAAq1NfXkY997EV56lOXVh2FNtZqtXLo0GS2bx/Na1/7+dx++8HKstQatWx+++Ys2LQgHX0dKWoKWeaG6bHpTI1N5bvv/W52f3N31XEeVaNRy8BAI99bpfgd77ggP/VTZ1cbijlrcrKZn/3ZL+bKK49NjU9Pt3LgwEQeOJsIAMAjMRkLAABt4NxzR3LGGQMZGuqqOgptriiKDA52ptls5dJLl2Xx4u5885u7Z3wP2f5V/eld1puepT1pDDRm9HND2erd9dQ6axleP5zWVCsHbj2QqaNTVcd6mMnJZvbufWAq8dvf3pvPfW5bkqSjo8iFFy7O0JCL7Xny7r77UG655UCSY3vE3nHHoezZYxIWAODJMhkLAAAVKIrkve+9NK985Znp6KilZrqQJ6DVamVqqpXrr9+bH/uxz8z4MpHn/PtzsuJ5K1Kr1xIvWeao1nQrk4cnc81vX5NDdxx6/CdUrF4vjv8OGRho5KMffWEuvHBxxamYzf76r6/Pu951zfHbU1NNk7AAACfIZCwAAFTo4osX5YILFmX9+uHje/7BE1EURRqNIkuW9OQ1r1mXm2/en89/fluaJQ/IDq4ZzNC6ofSv6k+to1buJ4OKFfUi9e56lj1zWfpP68/Or+/M9Hj77CH7/aanW5mePtaUHTkylU984s588/uWWV67dijPec7yFIWrKHjA6OhU/r//764cODDxkPu/8pX7ZnzlBQCAucxkLAAAzLB3vvOivP3t599/y4lxnoxjf8Z9+tPb8vrXX56pqXJPmp/1Y2dl3U+sK/VzQDsa3T6ar/+nr2d8hqfQT7VXvWpN/uIvLrUKAw+xY8doXv7yT2XLlur2IQcAmKtMxgIAQAUuumhRfuRHzszTn74kSlhOzsy8fgbPHMzyS5dneMPwjHw+oBzXXbc773rX1Tk2GFvkla88MxdeuKjqWMygK6+8N5///LaH3HfkyFT27BmrKBEAwPyhjAUAgBmyadOC/NzPnWOZSE6Zoji2X+T0dErZz6/vtL6c8fIzXDvAvFbUimPfA7N4z8xbbjmQW245cPz2hg3DOf/8kYc9rl4v4ht+bmi1Wmk2H3jRXn31zrz3vddXmAgAYP5SxgIAAMxSmzcvzN/8zXPzmc9szQc/eGvVcWDO6VrQlbN/5uwc3HIwWz62Ja2pWdzIPsjf/d2N+cxntj7kvjPPHMiv/uqF6e11qmguuO22g/mDP7g24/fvd3zrrQce5xkAAJTFf2EDAEDJ6vUifX0d6enxn9+cWkuX9uaHfmh1du8ey7/8y10ZG5vKxMTJ7x9b1IrUu+upd9VPQUqYverd9Sy+aHE6ejpy9/+9O1OjU2megu+xql133Z5cd92eh9x3/vkjectbNmVysvGge4v09NTT2elnQTsaG5s6XrZ+v61bD+dTn7o7o6NTM5wKAIDvV7RaDyxm1dnZWWUWAACYky68cFHe856nZvny3qxa1R9LQHKq7dp1NPfccyR/8RfX55/+6faT/ngDqwey6S2b0r2wOz1Le05BQpjdpo5OZXT7aHZevTNbPrql6jil6O3tyJo1g/cvVfyAX/zF8/Pyl59RTSge09///Y350IceeVWEI0emsmXLwYcsVQwAwMyZmJg4/rZL8wEAoCSNRi3Ll/dm3bqhXHDBonSZMqQkixf33P+v+6Q+TlEv0j3Snb6VfRlcM2gyFu7X0dORwbMGc+jOQ1VHKc3o6FS+8529D7v/5pv35/bbDz7kvs7OY7/f6vXaTMWb1yYnp3PvvaOZnn5osXrjjftz7bV7HuVZAAC0C2UsAACUZMWK3rz//S/Iaaf1pbPTCWvaX9dwVzb/8ub0LO1J3bKkQJK/+Ivv5v3vv+kh961fP5y///vnZ8GCropSzS/33HMkb3zjF7Jr19GH3H/o0GRFiQAAOBHKWAAAKEm9XsuSJT0ZGTm5aUV4os48czDPetay3HjjvuzdO37Czy9qRTqHO9M5aAsbeCRdC7oycu5IRu8bzdiusarjzIiDBydz8OBDS7+urnq+8pUdGXyEnxVFkWzcuCALF/rddyLuvffIwyaQv+eee45k27Yj2bfvxH+uAwBQPXvGAgBASc46azCf/ORLs2xZb9VRmCcmJ5sZG5vKT//0lfnMZ7ad8PN7Fvfkkt++JD2L7RMLj6TVbKU51cyWj27JHf/rjqrjVKYoks7OeopH2AK9o6PIX//1c/OSl6ya+WCz2Pvff1Pe+c6vP+L7Wq1kfHx6hhMBAHAy7BkLAAAlajRqueyyldmwYTg9PZZ6ZeY0GrUURccJ7+NY1IssumBR+k7rS0e3PxPh0RS1IvXOeobWDWXF81dk3w37cvS+o4//xDnmscrBWq3IFVfc+7jT+Zs3L8w554yUEa9t3HDDvlx77e4n9NivfvW+jI0pXAEA5iJ/ZQMAwCnW29uRd7zjgmzevPARp4ag3dQatZz1b87K8IbhqqPArLD0aUuz5JIl+c6ff2delrGPpdls5W/+5sbHfdw733nRnC9jP/e5bXn3u6+pOgYAABVTxgIAQAmKIik0sVSgVivyYz92VjZuHM6HP3xr7nucomjZM5dlcM1guhfZ3xFOhJ/xJ+fKK+99UpOgHR21/Nt/uyZnnDFQQqqHu/vuQ/nHf9ySycnmCT/36qt3lpAIAIDZRhkLAACn0LESNkmcpKcatVqRV7zizDzveSvyuc9te+wytkiWPG1Jlj97+cwFhLmkuP9fq+ogs89VV+3IVVftOOHndXfX87SnLcmqVf0lpHq4rVsP58/+7Ns5etQSwgAAPDnKWAAAOEVqtSJvfeumXHLJzJ0kBqAiRbLqxauy8NyFuf2fb8/ovaNVJ5oXJiaa+dM//XY+/OFbZ+Tz7do1lomJE5+KBQCA71HGAgDAKVIUyTOesSw/9EOrq44CSZKeno709NQzNjad1vdN7hUdRWqNWmr1WjXhYA4Y3jicgTMGsu3ybcrYGdJstvLFL26vOgYAADxhylgAAIA5qL+/kd///afnllsO5Nd+7avZt2/8Ie9f+fyVWfm8leld0VtRQgAAAJj7lLEAAHAKDA11Zni4Mz09/hOb9tDRUct55y1MT09HGo2HT7/2LOnJ8MbhmQ8Gc02RdA13pWukKxP7J9Jq2kAWAAB4gDNFAABwCrz5zRvz2teuz+LFPVVHAWAG1Tvr2fSWTRm9bzTX/fF1Gd8z/vhPAgAA5g1lLAAAnAILFnRl9eqBqmPAw3R317N588Lceeeh3HbbgTQGOtOztCddC7qqjgZzQ5F0LehKa7qVWs0ezAAAwEMpYwEAAOawFSv68rd/+7xcddX2vOlNX8jCixZl05s3pdapNAIAAICy+esbAABgDqvVivT3N+7fz7hIUS/S0duRWoc/B+FUqnXVsvgpizNy3kiKelF1HAAAoE346xsAAADgJHUOdGbjmzdm3avXpdZwugUAADjGMsUAAHASzj9/YZ7//BW56KLFVUeBx9SzpCdn/MgZ6bG3MZSmKIrEUCwAAPAgylgAADgJl1yyOL/1WxfH2XfaXe+y3qz7iXWZbLaqjgJzWxG/EgAAgOOsmwMAADAPrOjtyE+uG8qlS3uqjgJzWu/S3mx686ac9gOnVR0FAABoA8pYAACAeWCkq57nLe/NxuGuqqPAnNY51JmVz1+ZkfNGTMgCAADKWAAAAAAAAIAyKGMBAOBJ6OysZfHi7gwMNKqOAkAbqnfW0zXclXp3veooAABAhZSxAADwJDzlKYvz8Y+/OG996zmxDiUA32/k3JFc8u5L7B0LAADzXEfVAQAAYDbq62tk3bqhNBomnphd+htFVvc3sn9iOgcmmlXHgTmro7cjHb0d6RzqrDoKAABQIZOxAAAA88gFI9155+aFeeaSnqqjAAAAwJxnMhYAAGAeadSKNGpFOmuW1wYAAICymYwFAAAAAAAAKIHJWAAAgDbXbLXyjT1j2TM2fcLP7euo5amLu9NVdy0uVGFozVBW/eCq7P3O3hzeerjqOAAAwAxTxgIAALS56VbyuXtG85194yf83OW9HTlvpCtd9RKCAY9r4eaFWbh5Yb77F99VxgIAwDykjAUAgBOwbFlP3vKWTdm4cUHqJg15Elpp5Uv3Hc3thyaf8HOareTe0akn9fkOTjTzP+84lK76Q/eIveXAxJP6eAAAAMATp4wFAIATMDLSnde/fkMWLuyuOgptqZXp1uM8opVct3c8X7rv6Iwk+v/bu/MYT+/Dvu+fmdmd3eXyECmSK9KkJCuS7MqSE1mu5dqx4xx1GxQNgsZA0ziFkQOF/yhcJ2laFKgTqEFg/ZGihqPYVlLVNpSoSaNETmzZoU2ROiJFoURS9JK7y13uvTOzu3PP776ep3+sNOJKFPfgfueZ4/UCBMz1036wAEnMvOf7fDvjKk8vdLfkzwIAAACuJ8YCAADcIYNJnX95rpXL3de/2/Vc++ZPxQIAAAA7lxgLAABwG0ZVncG3HIPtTaocWx2KrcC32XdwX/bfvT/j3jj1jY7QAwAAu4YYCwAAcBueW+7nt863r/tYVSdXerd3tyuwu739z709j/zYIzn+seNZP7Xe9BwAAGCLiLEAAACvo6rrLA8mGVXXf/xSZ5zzTsACN+nggwcze99sZg7OND0FAADYQmIsAADA6+iO63z0xHrmu9eH10HlMaMAAADA6xNjAQAArlNnvjvJymCSJOmOqyz1x1kbVjd4HQAAAMD1xFgAAIBv8Zn5Tp6c726+P3YKFgAAALgNYiwAALDnLfbHeWl1mG8k1/PtcUYCLAAAAPAGibEAANCYOslU0yN2uNf7O7z5v9/z7VE+dnItE/0VAAAAuIPEWAAAaIwQ+8a93t/hd/5cb1zl6YVuWqNr98Au9MZxEBYobWp6Ko/96cfypne9KRefvJjRxqjpSQAAQGFiLAAAsCfUdb35GOLepM6T891c7o0b3QTsLVMzU3nkxx7JYG2QK//xihgLAAB7gBgLAADsenXqPLXQzbG1YZJkWNVZG04aXgUAAADsdmIsAACwK03qOsNvPHu4Tl5eH+Y/XO01OwoAAADYU8RYAABgVzq2Osy/Ot9K/fUee8UjiQEAAIAtJsYCAAC7yqiqsz6cZK47zqn14eY9sQAAAABbTYwFAAB2lYudUX7l+Fpao0qIBQAAABolxgIAALvCqKpzsTPKmdYoS/3JN++LBQAAAGiIGAsAAOwK68NJfvX4WhaFWAAAAGCbEGMBAIAdqU6dk+ujLPXHSZLWqMrGqBJiAQAAgG1DjAUAAHamOnlyvpMvXem9+kMAAAAA24YYCwAA7DjH1gY5sTbMxfZYgAV2nH0H9+Xx/+LxtC+0M//5+VTDqulJAABAIWIsAACwg9Spk7y4OshvnW83PQbgtswcnMnb/qu3pXW2lStfviLGAgDALibGAgAAO8aJ9WE+t9DL2fao6SkAAAAANyTGAgAA215V1xnXyaXOOJ+73G16DgAAAMBNEWMBAIBt70J7nH9+diNL/UnTUwAAAABumhgLAABsO3VdpzWqMqmvvX+5N87xtWFGVd3sMAAAAIBbIMYCAADbTm9S55+cXM9cZ5wkGVZ1xkIsAAAAsMOIsQAAwLZRp87V3iTLg0kWuuNc7o2bngQAAABw28RYAABg26jq5JPnWnl+eZD+pGp6DgAAAMAbIsYCAADbwsXO6Npp2O443bEQCwAAAOx8YiwAALAtfO5yN//uUie1q2EBAACAXWK66QEAALCTLC/38+u/fiKf/vT5TCaq4Z1U19ceU+xvFQAAANgtxFgAALgFV6708ou/+Hw+/vGTqSqP0r0z6kiwAAAAwG7kMcUAAECjjq8N8x8X+3l5fdj0FAAAAIA7SowFAAAaUdd1JnVyvj3K7891mp4DAAAAcMeJsQAAQCPOtEf5rfPtXOmNm54CAAAAUIQYCwAAbKlJXac3rnOlN8nXlgeZ1O6LBQAAAHYnMRYAANhSV3uT/JOX17I8mKQSYgEAAIBdbLrpAQAAsBO126OcOLGWK1e6TU/ZMSZ1ncu9cS51R7nYGWexP4kUCwAAAOxmYiwAANyGr351MX/hL/x+PvrRY4mkeFM64yofPbGWf3xiPd1x1fQcAAAAgOI8phgAAG7DaFRlebmfdnvU9JRtr6rrnGuPcqU3yfJgko4QCwAAAOwRYiwAAFDUpE7+1blWjq4MM3FHLAAAALCHiLEAAEBxkzoZC7EAAADAHuPOWAAAAAAAAIACxFgAAHgDnnnmav7u3/1qvvSly01P2YbqPLPYy784s5GF7rjpMQAAAABbzmOKAQDgDTh6dCVHj67kyJFD+ZEfeUvTc7aNuq5TJ/nDlUGeWug2PQcAAACgEWIsAADsWXWSqSJf/+zyIP/+SjfnWqPbGQYAAACwK4ixAACwZ91KiL21r1/ojvPMYv8W//8BAAAAdhd3xgIAAAAAAAAUIMYCAMAdsLIyyLlzG+l09vZjefuTKld647THVdNTAAAAABonxgIAwB3wsY8dz5//80/ky1++0vSURh1fG+bvv7Ccz8x3m54CAAAA0Dh3xgIAwB2wsTFKpzNOvz9pekqjhlWdlcEkVd30EoDtqxpXaZ1rpX2+nWriSQIAALCbibEAAAAAW2jcHuelX30pnblOqpEYCwAAu5kYCwAAd0hdJ1/84uUMBpP8yT/5aO6//2DTk7ZMa1TlxdVBXl4fpnYqFuB11alTjSohFgAA9gB3xgIAwB1SVXU++tFj+dt/+z/kwoV203O21GJ/nP/n5Hr+YK4TLRYAAADgGidjAQDgDrv5k6F1kqmCS8rrjat87nIvlzqjDKtaiAUAAAB4FTEWAAAKqOukrutMTSXfObju7BCbJL1JnSfmOrnSGzc9BQAAAGDb8ZhiAAC4w7rdcT784efzoQ99NWtrw6bnAAAAANAQMRYAAO6w0ajKH/zBpXz60xfS70+anlNEnTqjqs5wUqe++ecyAwAAAOwpHlMMAADcsnGVfOL0Rk63RlkbVk3PAQAAANiWxFgAAChkMqly+XI3+/ZN581vPpCpqZ1/R+w3VHWd8+1RXtnwGGaAm1Ynw9Ywg5VBqolfZAEAgL1AjAUAgELm5jr5mZ95Kj/8w0fyy7/8x3PgwEzTkwBoUDWucuLXT2T1+GoGK4Om5wAAAFvAnbEAAFDIeFzn0qVOXnllPc89t5jz51tJ3K8KsJcNVgfpL/ZTT/z3AAAA9gIxFgAACjt6dCV/8S8+mX/0j15M7WfvAAAAAHuGxxQDAEBhk0mddnuUfn/S9JQ3rE6dMxujLPTGaY3cdwgAAADwesRYAADg5tXJH8x38++vdFM55QsAAADwujymGAAAtsixY6v5h//waL7ylatNT3lD6roWYgEAAABughgLAABb5Pnnl/KhDz2bz39+IUn99f/tJDttL8A241+jAACw53hMMQAAbLHf+70LmZ/v5Kd/+l35gR94qOk5N+255UGeW+7n1Mao6SkAO8785+az8tJKOnOdpqcAAABbSIwFAIAt9vzzS/na15byQz/0cN773geyb990pqenmp51Q2dbozw13216BsCOtPLSSuY+M9f0DAAAYIt5TDEAADSgrpOPfOTF/NW/+tmcOrXe9BwAAAAACnAyFgAAGnLs2GrOn29lff29TU8BAAAAoAAnYwEAAAAAAAAKcDIWAAAaVFV1zp5t5f77D+Rtb7sns7MzTU/6Np1xleX+JOvDSdNTAHac4fowg7VBxu1x01MAAIAGTNV1XX/jndnZ2Sa3AADAnnTvvfvztrfdk3/6T/90Hnvs7qbnfJsvX+3lN06tpz+pM6zqG78AgE3nf+d8Tn/ydCb9SapR1fQcAABgCwyHw823PaYYAAAatrExysJCN5/73EKeeeZqxuPt9cP6UVWnNaqEWIDbMBlOMmqNhFgAANijxFgAANgGlpb6+Zt/84v50Ie+mn7f44ABAAAAdgMxFgAAtonxuM78fCe/+Zsv56mn5vKqG0UAAAAA2IHEWAAA2EYuXGjn7/ydr+QTnzgVLRYAAABgZxNjAQBgGzp6dDm/8AvP5MknLzU9BYDbsHF2Iy//5stZfHax6SkAAECD9jU9AAAA+HavvLKRV145loMH9+UnfuLRzY/PzExlampqSzbUqVPVSbUlfxrA7tKZ6+Tcb59LPOUAAAD2NDEWAAC2sd/+7XM5eXItSTI7O52/9bf+aN7znge25M+e64zzqfPtXO6NtQQAAACA2yDGAgDANnb69EZOn95Ikhw4MJOf+qk/ksceuzuHD+/PzEzZE7KtUZXnlvsZTKRYgJtVV3UmvUkmg0nTUwAAgG1AjAUAgB1iOJzkF37hmbztbffkl37pR/P443c3PQmAb9G+2M6xf3wsg5WBRxQDAABiLAAA7BR1nZw928rGxiinTq0lSR599HDxE7IA3LxJf5LWuVYmfSdjAQCAZLrpAQAAwK1ZXR3kZ3/2C/n5n/9i2u1R03MAAAAA+A7EWAAA2GGqqs7ycj/nz7fyxS9ezksvraSuPQsToEnVqMray2vZOLORuvLvZAAA4BoxFgAAdqhz51r563/9s/kH/+CFTCZ+8A/QpOH6MC9+5MWc+I0TqYZV03MAAIBtwp2xAACwQ9V1MhhMcvbsRj7xiVObd8f+4A8+nO/5njc1Ow5gD6pGVeqxX44BAAC+SYwFAIAd7ujRlfyNv/Glzfc//OEPirEAAAAA24AYCwAAu8wTT1zM1au9JMmhQ/vyl//yu/Pww4du+vXtUZWnF7q52Bll7N5DgNdVjavMPz2f1vlWRp1R03MAAIBtRowFAIBd5umn5/P00/NJkgceOJCf/MnH8+CDBzM9nSRTN3x9Z1zliblOVgaTskMBdoF6XGfus3NZO7HW9BQAAGAbmm56AAAAUE67Pcrf+3tfzYc+9NVsbHzriS2nXgEAAABKEmMBAGAXGw6rPPnkXJ544mJWVwdpt0dpt0cZOPUK8IZVoyrj/ji1R7oDAADfgccUAwDAHnDxYjt/7a99Nvv3X/t9zD/7Z9+an/u59zY7CmAnq5MznzqTpeeW0r7UbnoNAACwTYmxAACwB/T7kzz//NLm+48/fncuXuxc9zXT08lDDx3a6mkAO1Z3vpv1U+tNzwAAALYxMRYAAPagJ564kGeeuXrdx+6+e39+7dd+PA/9kXsbWgUAAACwu4ixAACwB7Xb47Tb1z9W86679uVrX1vKke4ow6E7ZQG+k/5SP/3lfoYbw6anAAAA29xUXdf1N96ZnZ1tcgsAANCwQ4dmcviRw3nv//YDOfDAgabnAGxLpz95Omc/dTbVqEo9qW/8AgAAYE8ZDr/5i5tOxgIAAJt6vUnSHeVVv7MJwLeoRlUmfU8QAAAAbmy66QEAAAAAAAAAu5GTsQAAAAA3oXW+lcVnF7N2Yq3pKQAAwA4hxgIAAADchI3TGzn1z041PQMAANhBxFgAAACA19GZ6+T8p8+nfbHd9BQAAGCHEWMBAAAAXkudVOMqvcVe5p6eSzWsml4EAADsMGIsAAAAwGvor/Tz8m+8nO7lbqqREAsAANw6MRYAAADgNVTDKqvHVzNYHTQ9BQAA2KGmmx4AAAAAAAAAsBs5GQsAAADwKnVVp7vQTWeuk2ri8cQAAMDtE2MBAAAAXmXSn+TYR49l4+xGxr1x03MAAIAdTIwFAAAASJI6WX9lPd3L3fSWehl3hVgAAOCNEWMBAAAAktR1nbO/dTZXn7mauqqbngMAAOwCYiwAAACw560eW8366fV05jtCLAAAcMeIsQAAAMCed/UrV3Pu355regYAALDLiLEAAADAnrV6fDWXv3Q5q8dXm54CAADsQmIsAAAAsPfUSV3VaZ1t5cLvXmh6DQAAsEuJsQAAAMCes3FmI6c/eTrdy92mpwAAALuYGAsAAADsGXVVZ9wdp3u5m8VnF1NP6qYnAQAAu5gYCwAAAOwZvSu9vPirL6a/2BdiAQCA4sRYAAAAYNerqzr9xX7al9ppnWtl3Bk3PQkAANgDxFgAAABg1xt3xjn6kaNpX2xn3BViAQCArSHGAgAAALtXnWyc3Uj3Sje9xV5GrVHTiwAAgD1EjAUAAAB2rWpS5ZV/8UqWv7acalw1PQcAANhjxFgAAOA6k+4kC19YyN2P3Z0H3/9gpmammp4EcFtWj6+mfaGd3pWeEAsAADRCjAUAAK4z3Bjm5MdP5v733J8H3vdAZmZmmp4EcFsWvrCQi09cbHoGAACwh4mxAAAAwK6yenw1i88uZu3kWtNTAACAPU6MBQAAAHaHOqnrOmsn13L2U2ebXgMAACDGAgAAALvD+ivrOfc759K51Gl6CgAAQBIxFgAAANjh6qrOZDBJd6GbK1+6krqqm54EAACQRIwFAAAAdrjufDfH/u9j6S/1hVgAAGBbEWMBAACAnaVOBmuDTIaTJElnvpP1V9Yz6U0aHgYAAHA9MRYAAADYUapRleMfO571k+vX3h9XmfSFWAAAYPsRYwEAgNc07o6zdmItBx86mMOPHm56DkCSa6dg+0v9dBe66S/3m54DAADwuqbqut68TGV2drbJLQAAwHYylczMzuSRH3sk3/ez35dMNT0I2PPq5KWPvpSFzy9ce0Sx62EBAIBtaDgcbr7tZCwAAPDa6mQymKQaVU0vAfaw3pVeVl9e3QyvnUudTAYeSQwAAOwMYiwAAACwba2+vJqjv3z0mx9wGhYAANhBxFgAAABg2+kv9TP/+flsnN4QYAEAgB1LjAUAAG6oTp0pl8YCpb0quvYWeznzyTMeSQwAAOxoU3Vdb36rMzs72+QWAABgGzp05FDu/0/uz5EPHsnDP/Rw03OAXaoaVjn7b86me7mbJBmuD7P8wnLqyrFYAABgZxkOh5tvOxkLAAC8rt6VXnpXejn08CExFrij6nGdalIlSSa9SRa/upj1V9YbXgUAAHDniLEAAABAI+aensv8F+aTJPWkTnuu3fAiAACAO0uMBQAAALZENaoyao1Sf/1y2Na5VlZfWm14FQAAQDliLAAAALAl1k+v59ivHUs1uvZo4mFreINXAAAA7GxiLAAAcFMGq4NsnN7IoYcPZf89+5ueA+wAdVWnM99JNbgWX1vnWuksdFKP64aXAQAAbI2puq43vwOanZ1tcgsAALCNTe+fzvTsdN7zP7wnj/zxR5qeA+wAo/Yoz3/4+bTOt5Jcuxd2Mpg0vAoAAKCs4fCbTwFyMhYAALgp1ahKNaqcaANeV+t8K93L3STJpDdJf7mfcXfc8CoAAIBmiLEAAADAHTP31Fwu/O6FJEmdOqkaHgQAANAgMRYAAAC4ZcP1YS5/6XKq0fW1deP0RurKCXoAAIBEjAUAAABuw2BlkFf++SsZtUdNTwEAANi2xFgAAOCWzH12LhtnN/LW//KtueuRu5qeA2yBhS8sZPX46nUfG7VGmQwmDS0CAADYGcRYAADglqwcXcnaybUc+eARMRZ2kXpcX7vj9TUsH13O3GfmtngRAADAzifGAgAAwB43XB/m5MdPZrA2eM3Pty+0t3gRAADA7iDGAgAAt65ORp1RRq1R9h3el6npqaYXwZab9Ce3/JjefYf2ZXp2utCir/v6P5/15LVPub6W/ko/y0eX01/qFxwGAACw90zVdb353dns7GyTWwAAgJ1iKjn00KEcOnIo7/sf35eDDx5sehFsufO/cz6Xnrx0S69513/3rjz8wYcLLbpm3BvnpV95Ke2LN3+atRpX6V3t3VLABQAA4LUNh8PNt52MBQAAbl2d9K72Uk2qVOOq6TXQiOH68JaCZ5K0LraK37U87o3Tvti+5W0AAADceWIsAAAAbJGznzqb858+X/YPqZNxZ1z2zwAAAOCmiLEAAMBtq4ZVVl9azXB9mPveeV+mZtwdy+7XX+6nfb6d7uXuLb920p9k0r+1e2YBAADYudwZCwAAvCFTM1O575335QP/+wey7y6/78nuN/fUXF766EupqzrxlG4AAAC+hTtjAQCAO6ae1KkmihR7R13Vqcf1jb8QAACAPW+66QEAAAAAAAAAu5GTsQAAwBvWX+rn1CdO5b533pdH/8Sjiatj2YW6C91c+sylbJzZaHoKAAAAO4STsQAAwBs2XBvmwu9dyNWvXE01qVLXHuHK7lJXdbpXujn/O+ez/MJy03MAAADYIabqV/2UZHZ2tsktAADADnfggQO575335cgPH7l2QhZ2gf5KP6/8v6+kM9/J2strid81AAAA4HUMh8PNtz2mGAAAuGMGK4NcfeZqDj96uOkpcMdMepMsPreY4drwxl8MAAAAr+IxxQAAAAAAAAAFiLEAAMAdN2qP0l3oZtwdNz0Fbltd1ekv9dNb7KWuPJsYAACAW+fOWAAA4I6bOTiTfXfty7v/+3fn0R93dyw706gzygv/5wtpnWtluDF0VywAAAA3xZ2xAABAUZP+JJP+JK0zraw+uJq733p39t+9v+lZcHPqpH2xne7VbnpXehmuuysWAACA2+NkLAAAUMzUvqnMHJjJH/uf/1je/P1vbnoO3JR6UueF/+uFLD67mGpUORELAADALXEyFgAA2BL1uE6Vyn2b7DjVqEo1rJqeAQAAwA433fQAAAAAAAAAgN3IyVgAAKCouqoz/7n5tM618l1/6rsye6/rUdi+lp5byurLq+nMd5qeAgAAwC4gxgIAAEXVVZ2Fzy9k+b7lPPSBhzJ7z2wy1fQqeA11svj8Yi787oWmlwAAALBLTNV1vXl50+ys31AHAADKmN4/nTf/0Tfn7rfenXf8N+/IvkN+N5TtY+lrS5n/3Hw2Tm+kM+dULAAAALdvOBxuvu3OWAAAYEtUoyqLX13M4lcWM+6MU42qpidB6qrOZDBJ63wrC59fEGIBAAC4o5yMBQAAttTMgZnc/da7c/977s+7f/rdmZrxzGKas3ZiLSc/cTL9xX56V3tNzwEAAGAXePXJWM8FAwAAttRkMMn6qfXsv2d/6rrOlAtkaUA1rjJcH6Yz38naibXUk/rGLwIAAIBbJMYCAACw5/Qu9/LCL72QwcpAiAUAAKAYd8YCAACNGHfGWT+17tGwbKl6Uqd1rpX1M+vpXelluD688YsAAADgNrkzFgAAaMTU9FSmD0zn8f/88XzPz3xP03PYI4brwzz7959N51Ink8Gk6TkAAADsQq++M9bJWAAAoBF1VWfSm6R1oZXLX7ycznyn6UnsYnVVZ+WllVx95moGqwMhFgAAgC3hzlgAAKBRy19bzvILy/nev/K9Ofzo4abnsEvVkzpn/vWZLL+wnLgiFgAAgC0ixgIAAM2rk+UXllONqhz54JHc9chdTS9iF1l6finrp9fTu9wTYgEAANhSYiwAALAtLD67mKXnl3L40cNiLHfUlS9fyaUnLzU9AwAAgD1IjAUAALaNuq5z4d9dyMqLK3n7n3t7Dj54sOlJ7GBLzy/lypevZPX4atNTAAAA2KPEWAAAYPv4+uOK119ZzyM/9khm75u99vGpZHrfdLPb2DHqqk49qbN+et2JWAAAABo1Vdf15o05s7OzTW4BAABIkkzNTOXed9ybfYeu/f7oPd99T971l94lyHJTVl5ayZl/fSa9y710L3ebngMAAMAeMxwON992MhYAANh26kmd9VPrm+9PhpMMVgfZf3h/9t3l2xheWz2pM2qP0p3vZvmF5aS+8WsAAACgJCdjAQCAbW/mwEwOvvlgjvxnR/Kuv/SupuewTbXOtfLir7yYweogg5VB03MAAADYo159MtYzvgAAgG1vMpikM99J+2I7rXOtDNeHN34Re0Y9rtOZ66R1oZXOpY4QCwAAwLbhZCwAALBjTO+fzsyBmbzjp96Rt//Xb296DtvEYGWQ537xuXQvdzPujpueAwAAwB7nzlgAAGBHqkZVqlGV1tlWFp9dzL3ffW8OPHCg6Vk0YOPsRgbL107ADlvD9Ff6QiwAAADbjpOxAADAzjOVTM9M530/97685Uff0vQatlqdHP3I0Sx8YeGbH5rUr/MCAAAA2DpOxgIAADtbnVTjKovPLW7eHztzaCZHfvhI9h3ybc5utn5qPWsn19K+2BZgAQAA2Pb8lAIAANix5j87n/nPzidJDj10KA+89wExdpdbfHYxp//l6aZnAAAAwE3xUwoAAGBXGLVHOf3/nc49b78nj//k45neP930JO6A7uVuLv7+xc1TsGsvrzU7CAAAAG6BGAsAAOwK4944c0/N5U3vflMe/YlHMzU9de0T08nU1FSz47gt9aROb7GXC793IdWwanoOAAAA3LKpuq43L9mZnZ1tcgsAAMAbtv/u/bnv3fdtxti3/Mhb8uifeLThVdyqweogp/7ZqXTmO1k7tZZosQAAAOwQw+Fw820nYwEAgF1l1B5l6bmlzfcPP3I4ow+MMnNgxqOLt7lJf5JqfK269lf6WfraUgarg4ZXAQAAwO1zMhYAANjVZu+bzYH7D+Sd/+078/APPdz0HL6Duqpz8uMns/yHy0mSalSle7m7eVcsAAAA7BROxgIAAHvGcH2Y4fow7YvtHP6uw0mSqZmpHHzwYKb3OSnbpFFnlOHatW9Q66pO63wrrXOthlcBAADAneNkLAAAsCfsu2tfpmevxdcD9x3I+//X9+fQkUMNr9rb5j87n5c//vLm++POONXI5bAAAADsbE7GAgAAe864O066196ux3VWjq3k4JWDSa49yviet96TTDU4cA+Y9CfZOLOxeS/sxpmNzZOxAAAAsBs5GQsAAOxJ0/umN+Prw//pw/n+n//+TM2osSW1L7Xz7P/xbIYb33w0sTthAQAA2G2cjAUAAPa8b5zOTJLOQieXnrqUqalvxtip6am8+fvfnIMPHmxi3q5QV3WW/3A5/aV+kmSwMsioO/IoYgAAAPYMJ2MBAABew9S+qbz/f3l/HvrAQ01P2bGqcZXnP/x8lp5fanoKAAAAbBknYwEAAG6gntSZ/+x81k6sJbl2r+xjf+axzBycaXbYNtW+2M7CFxaSVz11uK7qdOY7zY0CAACAhomxAAAAr6VOLn/p8ua7hx87nLf86FsyfWD6ui+bytTm3bN7Rp3Uuf6u185cJ2c/dTZ15Q5YAAAA+AaPKQYAALgJ+w7tywPveyDT+6+PsY/++KN56Af3zqOMq3GVs586m/bF9nUf7y/3N08RAwAAwF7mMcUAAAC3aNwb5+ozV7/t43e/9e7c/333v+ZrpqanMnNg5z3WuK7qVMMqr/rd3U3VsMryC8tZPb7awDIAAADYWZyMBQAAeAMOPnQwB+4/8Jqfu/e77833/pXv/bbTtNtd63wrJ379RCaDybd/sk46lzoZ98ZbPwwAAAB2ACdjAQAA7pD+Yj/9xf5rf7JKeld6mZ69cYyd3j+d2TfNZmqqzAW01bDKYH2Q3MSVrt2FbtZPrr92jAUAAABumpOxAAAAhUzvn87sfbPJTfTVe99+b973P70v+w6V+Z3Z1ROrefEjL6YaVzf82mpUZbg+vKlwCwAAAFzPyVgAAIAtUI2q9Je+w6nZbzFzYCbrJ9czc7DMHbMbpzfSW+ylHiusAAAAsFWcjAUAANgOppKZ2ZmbOkV7O+qqTjW88alYAAAA4I1xMhYAAGC7qeOOVgAAANhlppseAAAAAAAAALAbibEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABUzVdV03PQIAAAAAAABgt3EyFgAAAAAAAKAAMRYAAAAAAACggP8fjymaWYXEdVMAAAAASUVORK5CYII=" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAACKqklEQVR4nOzdd5hdd30n/ve5d+70plGXbMm2uptcMN00h5YQAtmwIUsNkJBNNgUSkk3IhmUT8sumJ8+PFNLYpW1Ywoaw+2Npxiam2oANuFtuki1ZvY6m3vv7Q0a2cZWlM+fOzOv1PHqYe+femTdn7sx4zvt8vt+i1Wq1AgAAAAAAAMApVas6AAAAAAAAAMBcpIwFAAAAAAAAKEHHg290dnZWlQMAAAAAAABg1puYmDj+tslYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAA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nszHPnZ7PvWpuzM11aw6DsCMabVaueKKe/ORj9yWPXvGqo4DAJwiylgAAAAAAACAElimGAAAAACgQocPT+bw4cmMj09XHQUAOMWUsQAAAAAAFXr/+2/Ohz50S+69d7TqKADAKaaMBQAAANrO6OhUbrxxf5Yt68nKlX1JiqojAZxye/eOZceO0dx0077ccsuBquMAACUoWq1W63s3Ojs7q8wCAAAAkCTp6CgyMNCZV7zijPzBHzwjRaGMBeaeD3/41rzrXVdndHQqY2OWKAaAuWJiYuL42yZjAQAAgLYzNdXKvn3jOXx4Kq1WoosF5qLx8ens3TtedQwAoES1qgMAAAAAAAAAzEXKWAAAAKBt3Xbbgfzt396Yb3xjV9VRAE6ZrVsP5+/+7sZcddX2qqMAACWzZywAAADQ9t7xjs35j//xoqpjAJwSn//8trz2tZ/PxESz6igAQAkevGesyVgAAAAAAACAEnRUHQAAAAAAYD5otVqZnj7274H1CgGAuUwZCwAAAAAwA+6550h+93e/mS1bDmZqyhLFADAfKGMBAACAtjc2Np39+8fT29uRzs561XEATkiz2crhw5O5997RfPaz27J373jVkQCAGVK0Wg8siNHZ2VllFgAAAIBHtGRJT5Yt682v/doFeclLVlUdB+CE7N8/nre97cu56aZ92bLlYKanrVEMAHPZxMTE8bdNxgIAAABtb+fOo9m582j27TNNBsw+09Ot3Hrr/txyy4GqowAAM6xWdQAAAAAAAACAuUgZCwAAAMwat9xyIF/60vYcOGBCFmh/zWYr3/3u3nzlK/flyJGpquMAABWwZywAAAAwazQatfT1deSDH7wsz3jGsqrjADymycnp/ORPXpHLL78nExPTadkqFgDmBXvGAgAAALPS5GQz4+PTaTY1GkA7a+Xqq3flhhv25c47D2V8fLrqQABARZSxAAAAAACn2Mc+tiV/+7c3VR0DAKiYPWMBAACAWWVyspkPfODW/Pmffyd7945VHQfgEVmSGABIlLEAAADALDM11cr//J9b8r733ZD9+yce/wkAM6jVaqXZrDoFANAuLFMMAAAAUKpWkqLqELNcWcfwRD9u2Y+fD9rlGJb3tfnc57blox/dkmuv3VPKxwcAZhdlLAAAADArNZutjI5O5ejRqXR311MU7Vp6tWuu2aSsY3iiH7fsx88H7XIM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+plce+3uakPNMutftz5n/siZD9zhZcpjaSXT49P5xnu+kX037Ks6TZJje26fdlpf/uVfXppVqwaqjgNz0vj4dF772s/nC1+4Jw+czQQAaE8mYwEAYA677LKVueiiRUmSnp6OLFnSk6LQbpWlKIp87/B2d3fk9a9fnxe+cGWSZOvWI/nYx27P1PeWWOUhBtcMZvFFizO8cVgByxNXHNtPduXzV2bknJEkyfi+8dx75b1pTlbzvdZqJQcOTOR977shmzYtyKtetSadprvhpH3ta/fli188tiz51FQzd9xxUBELAMw6ylgAAJhDiiJ54QtPy0/91NlVR5mXurrqecMbNhy//eUv78i//MudythHMbR2KGtfvbbqGMxCRb3IyhesPH774JaD2fGVHZWVsUly8OBk/vIvb8hTnrI4L3/5GWk0alYkgCfpewv5ffWr9+X3fu9bFacBADg5likGAIBZ7vzzR/KWt2xKrXbshP/FFy/O+vXD1YYiSbJz59FceeW9+fKXd+S///dbqo7TNvpX9Wf1y1an//T+DHutcgpMHprM7mt3Z/8t+3P3p+5OKpycW7iwO8973oo8/elL88Y3brj/Z3MrStmT5RjOJ9/85q78wz/clOuv35frrttTdRwAgBNmmWIAAJgDiuLYJOaZZw7mx398bTo6alVH4vssWdKTV71qTYoi+djHbj8+6TMx0cz09DxcZ/H+5WV7l/ZmxXNXpOY1yynSGGhk+aXLU++u557L70lzsplWRd9je/aM5Z/+6fZMTjbz4z9+bLniRsNr/eQpYueyVquV8fHp40sQb9lyMP/jf2xJszkPf1cCAHOOyVgAAJilzjprML/zO5dk1aqBbNw4bF/YNrZz59HceuuBJK20Wsmf/Mm3c8UV91Yda8b1LOnJxp/cmJ6lPRlYNaBb4ZSbODiRI9uOZPtV27P101srzbJoUXfWrx/Kq161Jq9//YbHfwLMY7t3H82v//rXsmPH0STHLmq4+eb91YYCADgJJmMBAGAWq9WKLFzYldWrB/L0py/L0JCLKtvdkiU9WbKkJ8mx6Z///b/vyk037cveveOZmJjb+8k2Bhqp3T8V2LOkJws2LUhjoFFxKuaqzsHOdJ7dmcPbDqdrpCtTR6YyPT5dSZbdu8eye/dYzj57JNu3H7n/3iIjI13p6qpXkgnaTavVyt6949m69XC+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AAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABUzVdV03PQIAAAAAAABgt3EyFgAAAAAAAKAAMRYAAAAAAACggP8fjymaWYXEdVMAAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" @@ -662,7 +647,12 @@ "source": [ "## Step 4: Add lighting\n", "\n", - "This section corresponds to the [Step 4 of the C++ tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-4-lighting). Please read that section for an explanation of the trick used to estimate the light intensity of pixel based on the angle of intersection between each ray and the spheres. The changes are minimal and are primarily about handling this intersection angle." + "This section corresponds to the [Step 4 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-4-lighting).\n", + "Please read that section for an explanation of the trick used to estimate the\n", + "light intensity of pixel based on the angle of intersection between each ray\n", + "and the spheres. The changes are minimal and are primarily about handling this\n", + "intersection angle." ] }, { @@ -672,14 +662,11 @@ "metadata": {}, "outputs": [], "source": [ + "@value\n", "@register_passable(\"trivial\")\n", "struct Light(CollectionElement):\n", " var position: Vec3f\n", - " var intensity: Float32\n", - "\n", - " fn __init__(inout self, p: Vec3f, i: Float32):\n", - " self.position = p\n", - " self.intensity = i\n" + " var intensity: Float32" ] }, { @@ -697,7 +684,7 @@ }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd7a8syX0f9uqZOQ/3aR/uPnKXu3wQRVOk+KDYBkU7kWXHD3IcGVESGIkTJAaSGAaSF3l4nX8pSIDkRWJbQQLDEQwYgS1bgBNHpmSLkiiKS2p3ubv33jOdF33qdFV1dU/PnOl7zr37+QAHM1NdXV0zd2ZI7Hd+VU3btm0AAAAAAAAA4KhWNz0BAAAAAAAAgOeRMBYAAAAAAABgAZv0wenp6U3NAwAAAAAAAOCZ9+jRo6v7KmMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAA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AACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAADw/2/fDk4AAAEYiOH+Q9cpDkGSCfo/CgAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmfbXo8AAAAAAAAA+I1nLAAAAAAAAEBAjAUAAAAAAAAIXNpczcumkMt4AAAAAElFTkSuQmCC" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd7a8syX0f9uqZOQ/3aR/uPnKXu3wQRVOk+KDYBkU7kWXHD3IcGVESGIkTJAaSGAaSF3l4nX8pSIDkRWJbQQLDEQwYgS1bgBNHpmSLkiiKS2p3ubv33jOdF33qdFV1dU/PnOl7zr37+QAHM1NdXV0zd2ZI7Hd+VU3btm0AAAAAAAAA4KhWNz0BAAAAAAAAgOeRMBYAAAAAAABgAZv0wenp6U3NAwAAAAAAAOCZ9+jRo6v7KmMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABm5ueAAAAAADcVut1c9NTmOXior3pKQAAUCGMBQAAAICKt966G375lz8XTk5WYbXqQtmmCSGE5vI2l7a1I9loe3kgPd7db6/a+tuurb/t+m23fdt224ZHj7bhb//tfxX+4A8+PvzJAgCwCGEsAAAAAM+91SqEO3c2oWnSUHXaa6/dCT/7sw/D6WkXxjZNqJ7fVAaLwWn+OD+Whq9pSJuGr2kgW4awbduGi4s2fPzxRfiH//AH4aOPnoyGwOUctts2fPLJxWR/AACur2nb/v9ynZ6e3uRcAAAAAGARr756Hv7T//TL4f79k7DZrMJq1YWoMWCNeWpa+Xp6ugqvvHJeVMXOk4evw1B2biAbQhecxnFiSNuFsTGkbcMPf/hJePRom7XHADcNbrfbEB4/vggffPAk/C//y2+HH//40X4vJAAAOz161P9/LJWxAAAAADwX7t/fhBdfPAshDIPT1167E9599/5kGJtWvg7HqAeyfc7ajrSH0LbN1fE8jG2KZYubJIRtrkLVGAbnyxY3WWj7+ut3Qgh9SBsD2O22D2S7MLYNjx51Yeybb94J5+frZJ592PvBB4/Dxx9fTL3cAADMoDIWAAAAgOfCn/2zb4V/79/7Qlitmqu/GLCu1024d69bprisdI3VsGnbIdJgdrgscbw/Fdp256btaQVtuZdser9c2ri2pHFsi6Hshx8+CRcX26vHT5604eJiG5482YZf/dXvhX/0j354+IsBAPAppjIWAAAAgGdO04TwhS88CPfvnwz2bm2aEH7qp14IDx+eXwaxtcrX/pw+dG0GAeycQLa212ofpjaj/boq2dhv2Kc/ni5b3AwepxWy8VheNdvddkFsk4Wx63V3f7NZZRWzMZh98mQb3n33Qfjkk+3VEslpxe1HHz0Jv/d7H+1+kQAAUBkLAAAAwLPh5GQV/pv/5hvhq1996XKp4WH163pd2we2VgkbQi2Inau2PHFsmxqzFuKW4+VVsfF+up9s329XhWztdmop4/j35Mn2slK2+3v8+CJcXLTh448vwr/4F++H//F//G64uBh5MgAAn3IqYwEAAAC4lV544SR861uvhs0mBqvN1TLCm00T3n77brh37+QqhE1vQxiGsGkwmt+/xnrEmcPGKZcr7tr68YZLFjdFCFtWxDaXx/Kq2dr+sjGM7R7nQWw8vl6vwulpt3Rx27bh8eOuivbkZBU+85m74dvffv2qojZW1373u++HH/3oUfm0AAA+1YSxAAAAANwar712J/yH/+GXwv37m3B6us72f02XHi7D1+kq2OvvBzumPubUhdpBn+FSxvF2WHW7T4VsCCFst2mfWhhbVsqmwWwXsrZtCKen3ePz83V48OA0vPvu/asljR89ugiffLIN/9P/9F1hLABAQRgLAAAAwI34znfeCJ///IOw2XQVsKtVE15++TS89NJpODlZhc1mddkesuWI6/u/NrOXIb5uKFtW1e43Xlr5miurZYd7xXbnpyFsfFzbTzaeW1bGpiFsCMMwNi5d3LbhMoztQ9n1urncb7a5XM54FU5OVuHsbBu+/e3Xw5e+9GK4uOj2mn30aBt+9KNH4R//4x9eXQsA4NNGGAsAAADAU9NXtIbwcz/3Svj5n38jnJ2tL/d7XSXBa14Jm1fADu/HMaO0IjZv299Ude11gt1hRWwcrM2Ol6FsbNsdyrZZv+22SW6HYWxcsjhdvni1iiFsDGWbq9vttg2bTRu221W4uGjD17/+8CqEffJkGz766En47d/+MPzGb7wXHj/OnxMAwKdF0yb/L+709PQm5wIAAADAc+xnf/Zh+KVfeudqyeGf+qkXw6uvnl2GsM1gH9jxAHZsOeKuX/543NgSw+P7zPbjT4+x2zCUbLO2/P4wyJxaoji9X/ZL28tK2doyxmNLGPd/fduTJ9vL2/YylL0IH374JPzWb30QnjzpAtrf+I33wm/8xo8Oe9EAAJ4Rjx71WzeojAUAAADg6M7P11kVbAghvPPOvfDtb78eNpsueF2v++WJ6yFsuhRxGb4OQ9dh1er8pHTXHrP5UMfZg7YPV9urMWuBbL2tO6f2OA9oxx+P/TVNer+rhO1vu3+nYSDbXJ23WnWh7GrVhNPTdbh//+Rqb9n33vsk/OZvvp88ly7kffx4q2oWAHguqYwFAAAA4KjOz9fhP//PvxLefvteODnp9n1dr5vwwgun4Y037mRVsMPliOeFsGlbfn88Ia0vMVyvhN21L2wtnJ1nfLne4bLF0xWx6bG077BqdlgNG0K+THG8Haua7Y71yxgPK2X7+3Gf2SdP4u02XFx0tz/+8aPwox89umr7yU+ehPfffxz+7t/9nfDhh0/2eB0BAG4vlbEAAAAAHNWDByfh3r2T0DQh3L27CV/84gvhnXfuh9PT1aAKtr4XbD2M7W5rlar1UDa1axniqYB3zrLHh1TGxv1fy3NjRerlo6uq1Hisu22vAuRhCNvvK9uFqLFStR3cjwFtbcnlGMZ2oWtZVduEGCY3Tden/zcLl1Wz8VgTQthezWu16iplX375LLzwwml4/LhbtvjDDx+HH//4cXj11fNwevo4tG235+xPfiKYBQCeDypjAQAAALi2v/bXvhj+wl/4bFivV2GzacJLL52FzWZ1GcLGJYjrVbBpKFouFzw3HJ0KRneFu+nY08se99evX2fYtmvp3bbSoVYJm7Yfozo27Te2d+zYPrJ5hWxfKRv7xMrY7jaEi4tt2G7jbXtZJdvvK9tVzD4Ojx9fhI8+ehL+v//v/fB3/+7vWLYYAHhmqYwFAAAA4CBnZ6vwzjv3w8nJ6ipgbZoQPv/5B+G11+6EzWYVVqsQ1utVtg9s2jcNY2MAWj4OYX5I2rUNG3cFu+MVsdNVt8O2OQFtnizGoDGdQ9/W9e/3b+3D464trXaN7XFf1/5xuYdsWvkaQqxs7atlh3/xWBwrrazNw+H47xqXPO7mHPeBXYXVqg1NswoXF21omjY0zTZcXIQQwjqs16vw0ktNuLjYhp/8ZBNef/1J+NznHlwFvO+994kljAGAZ5bKWAAAAABme/vte+G/++++EV566Szcu7cJq1UTNpsmnJysw8nJsBK2tgRxLYjdFZam7eNtw4rXY1XFlsePIa167R+Xx7s+w2rZ4Z6yw2rYsgo2v1+vlK1XyW633dixEjYeS/eOTfeQTfeZ7Stk22xf2e62q5Z9/Li9vL0Ijx9vw0cfXYRHjy7Cxx9fhP/9f/9e+Cf/5L0jvOIAAE+HylgAAAAAZmmaEL72tZfDw4fnYb1uwiuvnIdXXjkP9+5twvn5+nI/2FW2F2y/FHF3P4QunO3Gm1qWeHwv2F0Vq7VQdTzoHQted1fglv33k4en6XPK94ztK2C7+/28+srYMKiEjVW0fWVsuKpkTStgQ+iraMvgdqxKtt9jtq+W7fepHe4hm7aVzzn+df1Xl9W028t9ZsPle6kJJyercHKyCl/4woOwXjfh8eNtePx4G37rtz4Ijx5t93ztAQBuhspYAAAAAEZtNk34b//bb4RvfevVcH6+DpvNKluKeLXKq2DjssS7liQOYaxKde6esMPgNB3v0KWJ51x3+nhuuO9pfbniettYRWw/ztQ+softITvcL3afPWTLKtm0MjZWxfZ/3T6ybRvCkyf9frLdnrLbq9tHj7oQ9sMPH4cf//hR+B/+h38R3nuvrzYBALhtVMYCAAAAMOrLX34xfP3rD8NmEysTXwh37mzC6Wm3DHHcD7ZbkjithO0rYMtK2PFq2Klq1OHSwHnf6fB1btg7Vhk7nE9dbb/aEPpq1aJ30ac8p14Rm/aNe7jW2soq175PXvUa28ce7/pL95+NFbHlsXgb95vt3h8hbLfNVSDbNKvLua8u+7RhtWrDatUtaxyD/s2mey7rdRO+/e3Xwx/8wcfhH/2jPwxPnlTSbACAW0QYCwAAAMCVpunC2H/33/1COD/fhJOTvhJ2s1ldBa4xJEuD2HJv2BDSUDaOf+iercOq1mHgOqfydvpac/apHZMuJVwbq1QuTdy1DYPWvm8f0ubnl8HrMGRN28rgNl1KuOwTj9Vu+/C1Sapj89ejC5Tby8C2uQpc+yrq9qqtC2e72+49tr0MZptwcdG9587P1+FP/snXwu/93kfhN37jR+HJkyeTrzEAwE0TxgIAAAAQQgjhp3/6xfBX/sq74XOfux/u3z8JJyersF531bB9ABuK2zyAjeFiHsKOV8PODULHKl93LXk8Z5ni/H59ueKxec1VW4542J6HpOXx8SWL592OVcSmVa398b6aNQ2Ny6rXvFq2O2e1Gp67WjVXSxPH+/k+t+3luTGMbS/3I27DatXtJxvPW61CeOONO+Gv/tV3w6NH3TLG3/3u++Gf/tP3ZvxLAAA8XcJYAAAAgE+x09PVVUD51lt3wy/+4ltJNWxzuTdsrILdtS9sHo7uDka79mh8ieBhxe3Ydcrjsc/wuvmcxuZQ63OI4XLDbdYel/ONbX1Q2ib3myJATcPMvnq2D0aHj2PFa3+NdPniNhkrtufHu6C1vnRxfB7DJZLz4DetxA1hWHWb/jtdXIQQwipst+1ln+52vV6Fr3/9YXj8eBs+/vgiPH68Df/P//Pjy3H7fWoBAG6aMBYAAADgU+qFF07D3/ybPxNeffU8bDZNePHFs3D37uayInG4FPEwhM3vT4em3fG0bao9Ps73ha0HvOPVsWMhbH9uPo/6vGrmBLS7wsDa8bFK2PTxoRWy6f6tadvYnrFdWwxky/vlX1fd2i1tHAPgWO3aL2PcVbi2YbuNbV3fi4ttaNvmMkTtK2TX6y5YjUsXp7frdRNOTtqw2azCN77xMLz11t3w8ccX4ZNPLsKv/dr3w+/+7k+m/wEAAJ4CYSwAAADAp8j9+yfh7GwdmiaEhw/Pwle+8lJ444074eRkdRnCdvvD1vaFre0PG8J4IBuPdbf9HHYFsOV5UxW3c5cnHlsauZxP7XhtfnPkVa+Do1kFbH9OWgnb7w/ba5Pzmqw9jlt/nI6d9hne7/rFKtzY3l5WtcY9YJvB+f3yxDGY7ebfVbN275/+9eirZ2PAWguD+7mnyyOvkorfNoTQhpdfPgv37m3CRx9dhI8+ehIePjwL77//OPzkJ0+uqmkBAG5C07b9/7U5PT29ybkAAAAAsLD/5D/5cvjOd94IJyercHKyCq+/fudyb9gueO1u8/1gx5cl7sbcZ+/W9Njlo0FIuivcnQ5jh6HqdfaJPcYSxande8e2g375/XbQ3oebU4/7c/vAsx3075cZ7s8p2/P75W1/P+4LG9u22/w2XU44Vsh2+8rW/rrq2e02hCdPtqFt2/DkSXf+kyfbcHGxDY8ft+Hx427J4vfeexR+/ONH4X/+n38r/OEffrLfPxIAwDU9evTo6r7KWAAAAIDn2IMHJ+H11+9c7f/6uc/dD2+9dTecnq7DatWEzabfD7avhq0HsF1lY30p4vkVqnl7fk53J1ZQjlfE1gPc2vWHbcP24fzq8zyeqUrNWrVr7Xh5rN/rNa+obUfP7faRrVXU9mOl7U3TVbmmFbKx8jUeS61WZWVsmyxTHEKsli2D39Wq/qLH57ReN2G7DUmlbRNCWIW23YYQuj2QX3zxNJycNOEzn+ne6xcXbXj06CL86EePqmMDACxFZSwAAADAc+w733kj/Bf/xc+Es7NVODlZh7t3N+H0dBU2m7gccb8s8dhSxLU9YkOohaLjIWna1j9Ow9zrLE28XzVuU0xkuiL22GlsX3WatVbbhlWw/f220lZWuw6rYYdt5fFdFbJT1bF5Wxe4ppWweQVsXi1btsc9ZscrZLvbrkK2u91eVco+frwNT5604YMPHodHjy7C++8/Dr/zOx+G//V//Vfh4sKyxQDAslTGAgAAADznXnjhJHz1qy+Hr33t5fDii6fh9HR1uRxxtyRxfVniWAFbC1+brGK1u90/hC0rVueEsGPLFefXy+cxNpep+dSOTyn7jS1BXJx1WXVajlULaaeqZNNxxqtf88rWcq/Z9rI6tuyfV8725wwrZbvjzVWQ2lfIdhWwbdvf9nvDdsfLvWPLZZXHtG187v1r0N2uQghddWzcTzb++CCEEF555Tz89E+/GN5775Pw+7//0fRFAACORBgLAAAA8Bx6++174b/+r78R7t3bhNPTdVivu5A13ubLEc+piC0D0OlwNO031l4uR1yOuzv0nRcCl4HrWMXsZctI+25zzolBY9c3rXzNg+QYMHb326vnNWzv5jzcL3YYcPahZdmnD1TTvun99C+EcFn1mreXIXPs01W/dsfT+9O33TLI8d9+tWrCxUUbVqs2NM3q8rmvLgPg9urYkychrNftZf/t5TW7HyDcvbsJr756Hv7JP/lh+Nt/+3d2/2MBAByBMBYAAADgObDZNOFP/ak3w8OHZ2GzWYU337xzWRW4DptNH8DGithyb9jaHrH1qtR5FapRLSwdVr6Oj3lYINvPoR68jgeuuwLVssq2Jl1eeGz8vEK1DDLb7HFfvZpX0Kbt9erYtH1qT9n+WJxXfFyvhA1ZhWs6Tgix+jVc7Qcbb+M+r/1yxvWK2L5St39fpMFuvE2D6f61XYWLiza07fYytA3h4qLNqnbffvte+Pa3Xw//8l9+EL73vZ+U/0QAAEcljAUAAAB4DpycrMNf/svvhC9/+aVwft5Vwnb7wubVsPND2H32bA1hV/iZVrsOw9hhn/5a/bH0WmMVsbUK3DI/HT6uB6yHVMeOjTdclji2t5W2/rnEYLK/ny9zHMPZGMz2laVle61fmyz122Zjx2rcNDhNq3rTtv7+VFtcmjjtk7825W0awtb/vbeXr9U2NE1zuRfs9iqU7ZZL7ipl423ThPDuu/fDG2/cCf/H//G7wlgAYHHCWAAAAIBnWNOE8Bf/4jvhK195Kbz77v1wfr4Op6ddCLvZrEK5L2wawoYwHsZ2Y++qgt0dgNbG6s8bjl8LZ+dcuwx+y3ml59SODdsOC2jHimK789pBv3RO+RLGw2WI06rapmkv75f7pvaVr3kwWz7uxu3P6QPS2L8PgueGsiHUgtjtNp97ueRxf5vuJVsPZfNjq8tzVqFf0jiGsO1VGLvdds83hrLxPf+1r70cXn75LHzyyUX4+OOL8Ou//sPw/vuPx/9xAQAOIIwFAAAAeMasViGs16vL+0341/61V8N3vvNGOD1dXVXExqWI+79w1ZaGrn0oG8J4der+QWh67lj16+4QtqzIHbaV182vXw9f0/Nqx8fM7TO+SvHYAGMnNBN9xo41WVsezOZLFKehbD5OU9yPx7r2OEZ9meKh1aoLZeNt27Zhu41jNVmwXNvvNi5vHNuGoW/ySlzeX6/L596Ei4sQTk6a0DTdYJ/97L3wxht3wvvvPw4ffvg4/It/8X748MMnVyEuAMAxCGMBAAAAnjHf/OYr4d/6tz4XTk5W4eRkFX7qp14I5+fr6rLEZUVsGsKmQetUGNvdDoPWWvha9i0rYseC2XLM+ljT10wf5/eHx4aP630OMTVGuuxw35ZfO6+Q7ZcXjsfGKmdjWzpmXjE7tkRxvE4zuF9/PB6K1itlxyti0/C6bI8VtXnlb9p3dfUabLd9/+22CU2zvbwNV/vFdssUd6Fw/ExsNt2yxmdn6/ALv/Bm+OEPPwl/7+/9Xvjoo4s5/9QAADsJYwEAAACeEZtNE+7dOwmf/ez98M1vvhLOzlbh9LQewq5WoVoNG9vKcHSsErZejVoPRLv7ebAaKxrnLoM8J4Qdv17ft+w3fDzcS7be7zjygLXJ2mPoOjKbpG8f4uYBbRrullWuaXtbnFtW1OZLH49Xypb369WzcWnhvF/cm7YLRrsq1P7fYrUKV5Wzq1V7Faj2AXb3ntpu28u+fcDc/cig61tW2cb34XodX7Pm8lj3GWqaJrz11t1w9+4mPHhwErbbED75RCALAFxf08afy4UQTk9Pb3IuAAAAAEz4Y3/spfC3/tZXw8OHZ+G11+6EzaYJ6/UqbDZlCLvKql/H9oUt20KYF8BOh6Hj18iPT507Pvau6+f3x0PjvG2B9HWH5D/JJW21x8N9ZtNjtSV6++rYet9av/x+37+831W+TrfHsco9W/N+3W1/LH/c3eb3t9sY1LaXlbBd+8VF36+7393Gx93tNmy3ITx5sg0XF93jJ0+6448eXYSLizZ8/PGT8OjRNnz/+x+F3/3dn4S/83d+Jzx5YsliAGB/jx49urqvMhYAAADgljs5WYU337wbvvCFB+Hdd+9fLkncBbF5NWwMX6eqYIdhaVpJWqs0re29WralgW3+N2/P2HScQ0LY/QLY6fD12JWxtT1kyznk1a1Zz6sx+r1VyxC5vWpLK2bHKl/Ttnyp47TydriXa3nN8YrZfh5xeeR6NW2/Z2ysok0rZPt9ZbvzuvnE93i/322slI3v/VhVG59TdxsrZPvK2PW6u9ZmswohbMPJSbfX8sOHZ+HJk2147bU74YMPHof3339c+4cBAJhFZSwAAADALffWW3fDf//f//Hw6qvn4cUXT8N63QWx6dLETdMtwZqHsH042weh0xWr3f2+f/94PBStBa/9XrR50Dt2vbFlktP5DNvqQXHZPm3Y72mEsePLEg/7lRWt/f12tH2qCjbeDtvaav9h5eyu6tjydvxYWT1bexz7x/ux+jXtk1bLpn+x75Mn/W2slO0qZNvw+PE2bLdtePRoGy4utuHjjy/Co0fb8OMffxL+3//3j8Lf+Tu/M/PfCgCgozIWAAAA4BZrmhC++MUXroLX1167E1599Tw8eHCSVcJ24WsYhK95CJsGn/XgtAxexypT87ax8cbGrVfI5veHVbL1ELa+ZHI51/I1vbw3+dovYaritTcMVmO/vuK1Vtk6rJgd7vka+7bJWHEOY9WzTXHNehVsWkE7HKdWRZsfK/8d456xsZp2tSr3lc33io3nxr1i07l3/UJ2P34m4vlt2+8j292uwmbTvU7375+EV189C1/84oPw3nufhPfe6/+jKgDAXMJYAAAAgFtmtWrCr/zKF8If/+Ovhjt3NmGzWYXz83VYrZpif9hyeeIyjB2vTh0LRWO/7rZWCZueNx3Gju0VOxXApn13z2UqWD28MnZZYxWxtXn0fdNgOj7OlxjO2+ISvulY8fWKlaZpcBsrX7ulhWP//Hhc6rdp2iTM7NvTeZXH87a02rYPZ9M+/Tzj/dinPxZvmyZcLXPcv1ZlFXD/eLsNoWlWoWnay79wuaxxCOt1d3tx0Q3yhS+8EN544274B//g++H/+r++X/k3AgCYJowFAAAAuEW++tWXwxe/+CC88879cOfOJpyfry+XJa6HsGUA2y1TPAxf8yrWMtycDkdr/eqhaz2c7edSPt4VwtYqYPM9YVO7lyZ++lWxQ3NC11rfYeVsH6622etTjtm393vLpv3q+8zWjk9Xyc6riM379fvAttX2sT1ju+cfro7H93wI3VLFafXsdptXycbXsb/fXJ6XLve9ChcXccnkdfjsZ++Fb33rlfDbv/1B+OEPPwkAAHMJYwEAAABuke98543wV/7Ku+HsbB02m1XYbFZhtQqXtzGAjVWw+f6w/f2xqtUQatWw+4SwIdQqbg8PYedVynZ9hvKKzKnjuy0Z1O7aH7a8dq1//5q0bX48D6HLMLVrK6tc0+rZfMy0+jUe7+/Xwtp0jC7YrIWuafVqH7SuVl1FbLzdbvP27ba5ClJDqO2B2ySP89cxBq7x5dluh8tA969LDG7zfWnje/ynfuqF8NnP3g//2//2r4SxAMBehLEAAAAAt8DXvvZy+PmffyN885uvhLOzdTg5WYX1ul+WuKvWS/eJHQawZUg6vjzxdFVqebzss0/gW4awacA6tjdt+Tg3Xhk7L1C9ierYqWuOB69jfcrwtVYtO9wLNm2r96mFsvVK13ysYVgbw9f2MuRsL/drTZci7m5jn7gv7GrVt5eP4/khpCFsN4uu+jUPZPNlkstwv7der0Jcermbd3wtVpevb18d/NWvvhQePjwLv/7rPww/+pE9ZAGA3YSxAAAAADdsvW7Cl770Yvirf/Vz4eRklVTE9iFsH74O94ft7w+D0hCmKmPHq1Brwexq1R3Lb+vXLIPg/v6cOdQqXscCzV3h6nXC10PO3VUFO3WNOfvJDpcf7l6jvr227HBs627rSxMPlyOO1567PHF+vx+rXi3b72PbhjJgXq36QLZ8nL9X+2WKu71g+2rWGODGoDZW2cbnmr5eIYSrHzp0YWy8XhfKxj5f+MIL4c0374bf+q0Pwo9//GhQaQsAUBLGAgAAANygn/7pF8O//+9/Mbzzzr2rpYm7PWK75Ym7qr1QhLJ5GNuHst2YteWD5wagZQibhry7QtjpSth5VbG5uW1zjh3S7xBzx55TEVvrNxbM1tvT17lfnrhcrjj2abPQtgxQY994LPZLlwiOFaZdJWy6bHFZXZtXvna3fVVqus9rXN64e9ydN1Yhm96f+oFBeny9jj80WF29Bt1yxd3j2Df9UcKf/tNvhB/84MXw9//+74cPP3wSAADGCGMBAAAAbsBq1YQ7d9bhrbfuhm9/+/VwdrYO63UXxKb7wqZ7wo5Vv5b7tIawO4itLdlatsVz0nA3vx0GsYdV45aVsPuEsHPCzyXD10PNqYhN+02Ft2PLGPcVr7E93S82DV37kDYGsnH8PpBN+8ZAtOwXw9c8+ByvjM1vw2X42Vzd1sbq+8T59P3S+2N9uuWO43u7D3zjMsfx/b5ex6C3uayO3V7uabsOb799Lzx4cBL+8T/+YXj8eBsePdoGAIAaYSwAAADADfjMZ+6G/+q/+lp4/fW74c6dTdhsmiyMjUumxsrYekXsPssT10LYcjnivi0da7gXbb1ffs00sJ1TCXuMytg5xw/te11zAtep/mWf3VWxtfaxStl++eJ879haYFtWxpZhat+nv3a37G96vNwztg9i27bfHzatfI1/8XF/2/eLVbVpn/5xE0LYXj3OPxsh9JWxzdVzi5WxaSi8Wm1D267DanUW/tJf+mz4/d//KPzqr34vPH4skAUAhoSxAAAAAE/Jw4dn4exsHVarJrzzzr3wUz/1Ynjw4CRsNrEatimqYUPxV1sWuB6Axtu54edYEFuvot21PHE5h2Zwv7zu1Nx2t+86tk+fpUyFqVP9d+0jOxbapksVx/Z2dI/TsjI2/hun1a99QHt5hdEK2mE1a74kcr5nbL58cl4tm441vfzweIVs/x7u+sQlkftj/Z608TMY96kNIVz+OCJcLqccfzTRhtPTEF599Ty0bQgvv3waPvzwiSWLAYABYSwAAADAU7DZNOE//o9/OvzMz7wc7t7dhNPTdXjppdOwXjdhs1ldVcOmoeywIjbft3VsKeBa+Fn2i3aFsGll7NgesfXK2/rSxMmVdzwea7vusSnHCmt3Ba1j15qzj+y+e8iOt/XvjT6cLPePTc8p94hNK2jTQDZWtcY+sfq1q3SN1bVtcduN1e0Z214uBxwrZPNK2K6KNj6OoW1/PM43rZZNHw8rY1eX81hdzad7bbbh4qI73jTb0DSry+fXhrZtw3rdXffNN++EX/7lz4Xvfvf98Ku/+r3RwBsA+HQSxgIAAAAs7K237obXXrsT3nrrXnj11fNw9+4mrNddhV2/N2xcdrgMOssK1f62Nx6OdrfDILbeZ6ridbxStzyeX7NWDXudIPZYIeySVbJzg9ap8/atiI3Hpitl8/fOWBVsWqWazCjZezaeG++n10/b+71p8z1g+yrcdJ/avK0frz8vPoe0KjfdZ7YfJxTnDCtlu790+eG4d2zXd7ttLveUjUsq9/vNxv1k1+smnJyswoMHJ+Hhw7Pw9tv3wvvvPw4//vGj+j8fAPCpI4wFAAAAWNhf/svvhl/8xc+EBw9Ow8nJKpyedksVn5x0YWtZERurXtP9YfNK2GFoOlYZ291OLTXcPcr3op2ugq0HuPU59A4NYK8bvi4Zuu5jbB67QtexPvtUxI611ytly3C1fn5aGZse65cBHlbV9lWwXZ+muO2ramNlbD9eU1TExgrXfnniWMFdq5ZNq2trttuQBMfbIvBdXc5lG2KVbNuuwmrVhu22r5B955374aWXzsKv//oPw6/92vfHLwYAfKoIYwEAAAAW8rnP3Q9f/OIL4fOffxDu3j0JJyersNmssvA1hq7p0sRlEDt128mD1RCmq2FrlbRjoevYsW68sQB430rYfatgdwWs1wlgr3PuIevTXrcaNh6fE74O+6bhZt+Wh7DdsTb5t22zc9J/3rTSNr1OH862Rf/xCtn+WmlFbfojg3itthg7rZYNRXtfDds0faVrVwEbrvaN7apg4xz75ZVXq27cLiwOYb1ehRC2VxWyd+6sw+uvn4cvf/nF8P3vfxR+9CMVsgDwaSeMBQAAAFjIz/3cq+E/+A++FM7P1+H0dBVOTlZhtVpVKmGPXxFbD2Lzx+l4+V60aejaXIVSu6/fPe4dqzp2qn3Xsev03deusXeFteX5c/eHTY/tXxFb65MHsrHProrZscraYYVsbCsrZOPSyOkPB/oK2bQiNvZps2rabqnhGKi22Xs439+20/WN+9heznJwmx5bhW5P23B524amWYXT035/289//kF4/fU74e///d8XxgIAwlgAAACAY3v33fvhT/2pN8JXv/pyODtbJxWxq7BadcsSlxWxZRAbl1zdVaXa9RlWrMb2/rbc03XXdfK2EMJVYDt2/Wj/UHaftjnH9unzNO0KW8f6Ty1TfFhFbNmW/pvloWVaydo97veEDcXjsbA2r5CNgWytQrbf27U235DMtX/fbbf9nrEhhKsgNl22OK3YjdeJPzIIIVwFtzHY7SqC420/0XhOv2dst4Txet0tY3xysgrb7Tpst234/OcfhNPTVfjN33w//NEfPa4+FwDg+SeMBQAAADiipgnhnXfuhX/n3/l8ODtbh7Ozfmni8eWJhxWxc5YLjtcrg9A8hM2Px8d5sLp7qeJ0nLwytgx/962GPXYIe90A9tDzl1qiOO133VB2rC0/NwahZeia9u+XCx4PYcvAdRjmxmrYEPplhfPbdJniPhyeug1FWwjxfR7bu/d+rG7t38uxqrbr016GsOk+tyGs190VYtAblyvebJoQwipsNm1o21Vo23V455174eHD0/D9738sjAWATzFhLAAAAMCRvPnm3fArv/L58NnP3g/n55uritjNJq+ITStjYzVeWqk6J4gtK1zj/SgPocqAdVgZW96OXTsUyxb3lgxhr1she0jffe0a+7pLFJd9ll2muAxp82rWMsAdBrJ5tWw6fn6/XJZ4WBk7vI1Bbb60cfe4W8J4uGdsXEK4C2Hje7/2GoTL/WGHyxWn92OIHMfableXz7vfT/fOne5+F9QCAJ9WwlgAAACAa2qaEE5P1+HVV8/Cz//8G+H+/ZNkaeJ8OeIu2OzDoPTxPhWx3XXLsLW/X97WQ9mmGGc6iC2v3V9jKmidG7oeu0L20L5LmVsFm/bfVS27b6XsrrZhABuD1fg4DWRj8Ng04aqKtAxr8/ZcvmRxP3YasKZBbLmUcf84H69eGduHs+k5/fu7Td7vad/+L+47G69T+zHDxUVzVQV/crIKFxfrcHraLVX+5Mk2C3YBgE8HYSwAAADANb3wwmn4z/6zr4S3374XXnjhNJyersLJSbdH7GZT7g1br4iNy6fOCWRDmKqOHVbIlteI1a1zg9+xQHa/EPYYlbBzQtXrBq+HnL9vwla7xq5K2FqffStld1fA7qqazZctrvWdul8/N+772u//Wj6P8UrYWKEaQr/McHosLqccq2LTAHW77feA7fXnxT1js6OX+8f243bX32yacHERwmazSvp2fb7zndfDV77yUvg//8/ftVwxAHwKCWMBAAAADtQ0Ibz44ml4/fU74UtfeiG8/vqdcHqa7g8bA9CmCF5jqFWGnbVANK9sTfsP7w/nVxujv99kt+VyxMMx8pC3uNq+r97MtrnjHxKiHqtidm64umuMOUsZj/UZO7Z/RWzZllaeZj2yitR4P69ILStk0/OiNDAdLk887Fdet/xs5NW8+fzSitn+WrUfOaR7x8aq2LQiNlbP9j+s6Pp1n/3ttrtt21V45ZXzcHa2Di++eBqePGnDRx89USELAJ8iwlgAAACAA52crMLf+Bt/LHz5yy+GN9+8e7UcaRfG9ksU53vF9hWxfVi7f0VsHqaWlax53/F9aHdXxpbh7OXII/cPeTzWNtW+69gh/Y5t7Lq7lh7e1beZOF6rfB1rrwWyU33y44dWyDZNmwWz41Wz5W0aiIbK43I/2XR/165tuGdsdz/27f/S6th6CN223R6x8TPdV8w2oWlWV+c1TQhnZ+uwWjXhF3/xM+EHP/g4/Oqv/m745JOLAAB8OghjAQAAAA7w5pt3wiuvnIe33roXXn21r4jtg9Z0X9hQDTjH9m4tq1ijtAq2DGJLZYhbq36drsStB66HL1O8q/+c9l3HDun3tI0Fpof03acadu7441WzUxWyaSVq1ycfZ3huDGa791S/X2xZzdrvTTs2VtqvrIwdPqd0n9nh/W68vno2jj/8DLdhteqXPI4/qlitmsvlj/uK+K5CtgkPHpyEJ0/a8OqrZ+GDDx6HH//YksUA8GkgjAUAAADYU9OE8G//258L3/nOG+Gll84u94hdh/W6CZvNKqxW4SqYjQFNba/YuH/rrsrU7prHrIiN1+7DpDRU2hXIPv0Qdk6wemj4eszQdp+1Z+dUwZZ9pyplD90zNrbPC2TrFbL1Y+NVr/39YQDa3cZlf2Nb03RVrGnFbNneBaFpZWysiM2XSo7VsNttm+wf218z/Sv3lY1z6pYi7o+VFbIhrK4C5bgf7vl5G155pQm/8Atvht/5nZ+Ev/f3fi9stwEAeM4JYwEAAAD28NnP3rv6u3//5GpZ4j5wrQWq9cdpEBtCXhFbVqPW24fzG1bX1gPZ2De/DcW5/RjjdoWZc4LYpxnCLlkxu0/AOnbuEqHsIW27KmTTitW+knS4V2teqZq+Z8erYodVtWWlbAxiu2WB22S8fux6GJzPLT6H+LmIc4n9xvZv7v/6CtlyT9kuEI6VseHqxxp37mzC2dl65N8BAHjeCGMBAAAA9vAn/+Rr4Zd/+XPh/v3TcHa2Dqenq7BaNWGzaQZLk8bliocVqmPB53R1ai0krQVHwyrX4fWXr4hdKoTdJ0xdMng99Pq7wrf0nKnliceOjwWtZf/rBbLl4zRUDSEGk8Px0z71CtqxtjIMLn+M0O8Xm1fYpnvHdifEKtjutju3q14NSUVu/Jx0QXIMVlertNq2ueofK2TTvWPX61UIYXt52159V9y9uwnn5+sAAHw6rHZ3AQAAACCKlW2np6urADavjI2Vr2kIOxWMltWp5V6vaUDaV7CWt8Ogd3r54/yaoXJsVyi7RBBbjjv3WK3fTQexY/aZ266++4bZtX+Xff/tyh8HpI/rPxyIPxoYe0/NrUTtx8k/K/04tc9WM7hWfd/k4bVjlXu6rHi51Hf/eY/nNIO/9Tr+dVX0Jyer8PLLZ+Fb33oYPvvZewEAeL4JYwEAAAD2cHKyCufnm3BysgqbTblEcbpUcR/OTAWx4wFUupxxJ6+MHYam/Tm796HNb4fLJY+HrYeHd/3jueHsnGPpmIcGsM01/657zbl99zm2z+t8vX/T/D2aL6mdBqmx73Ap7fy8PrBtquPM/wwN3/P5+eNB7PB436dcjrz8IUYZwqaPN5vue+Phw7Pwx//4q+Fzn7tf+fcAAJ4nlikGAAAAmOErX3kp/Lk/93b48pdfzILY9XqVVMjVK+bKirrpALa/ZhlA9e1Ndnz+Xz7HfIw54et1Qtl92qba5x4/1jnXGXfufqDp+bv2jJ1aunifZYuPt0RxP/9+yeLhcsPd/eEyxf3xuP9ruvdrPJ7uDRvFftlbN/R7yqbLFfdBbz9m+tmKyxGH0H02ymWLu+v0yxt3n6F0uePuAt0yxuFyn9j2as/Y2BZCV10fQginp+vw9tt3w7e//Vr47d/+IPzu734UAIDnjzAWAAAAYELTdOHJO+/cD//mv/l2ODnJq2HHK+aaQcg5VoFaVqKW1YXpXNIKw3h8vNJ1PASemk/y7Gfcn/N4n7ap9l3HjtH/mOaGrOU5z14gG99T/X6rMURNehd9+vY+pO2D2OH84rE+HM2D126/1jZrjwFuGcp2Y8S9X7vrdcf6/t212tHq2a5fGdA22fziDzO6fWW72y6o7Zcr3mxW4Y/+6LEwFgCeU8JYAAAAgAmf+9yD8Nf/+pfCm2/evdwnttsrtt8HMl+atNw/Mr0dBjkxrKvvqZn3zfuFEKphcHre3OWK4xxyh4Syu8aZaptq33XskH5PWzmvXYHrVL+p481E+5y+hweytWukwWoMQMv+aXsa4vZ902NpIJtW1jbZ4+HnJvYJ2XXiZyZWwsbK2K6qNQ1Sw9W8uuMhbLfNZRAbP3tdINy24Sp0DSG9XYWmacPFRff8T07asN2uw8VFG05O7CYHAM8rYSwAAABAxWrVhPv3T8Ibb9wJP/uzD8OdO5vBvpBp9Vst6AyhFrimVah54Br1bc2gvb8/Nu6cPTXzULe8/n7h69h5Y/33CWfnHp/b5zaphaPX6bfkNXc9To4UYWqsPM0rZfvANF2SuK987ZcrTq9ZLmGcVsp218jn0SYD1MPZ/NwyKE4rY+PzSUPdpumrb/vPVK2Stl+uPAa8/T6yq3BysgpnZ+tw5846PHq0vQprAYDngzAWAAAAoOLhw7PwX/6XXwtvvnk33L27KfaJba6WJO5vYzCbVsPuDkb7ar08xC2rYPN9YsuK2/rjeRWx3bUOv197fGifucfmHF/q3Cn7hmi7KmDLflNVstetkL3OksXzwtk0BA3Z3q4hpMsN75pbugxwWuVaq5LNf2hQ7iEbg9SQnVNWxkZxb9h8P9n26rl0t81VwBtvt9tYfdtVxm63bQhhdXkbQtuuw1e/+lJ4++274R/8gz8I3/veT6qvJQDwbBLGAgAAACRWqxDeeONueOutu+Gzn713uadjk1Wz5XvDhtAFOeP7vNaqX2O/YQXtVP+QharpGOXj8tplxW48flj4GiaOHVL5um//Q8LUp1E5OzdcHTt31zlTfcaOHRq+Xuf6w35pNWutLa1w3TXHco/ZONZwL9ryuv2Y6RjlfrLp+bX59p+jPvgtK2TjeGlFbPo5XK3a7Pvk7t1NaJoQXnzxNLz//uPwk588USELAM8JYSwAAABA4uxsE/7G3/hj4YtffBAePjy/qojtlhRNq2Lz5YrLytjh8sW1KtU8WE2rZePjWoXsVGVs3+cmKmLnBrFjweh1KmUP7buUcg5zlwfe1X9XlewxA9ldAe28atnacsUhdMsRD5crro0x1RaXLy73jC1vL89OrlFWxKYhany8WvXX7faGLfeX7a6dVs124WtfIdvvS9uN2e0huwrrdTx3FbbbdQghhK985cXw5pt3wj/8hz8If/RHjydeewDgWSGMBQAAALj0+c8/CG+8cSe89tp5ePDg5KoiNg9Zy3AzBkx5+JZXnabhaq0iNa2ErVWvxvv5HNI+ZTVsfr2puc29X7qNQextCGHHTIWoY/33CUIPaT8kkJ0X2JaVsGN7xsY++R6vwzFr1bDltfLrlVWt49fMPx55KNsFxiHkgW7ap19uOd72n9G+8rb80UTbtoNq+/W6CXfubMKTJ9vLwBYAeB4IYwEAAABCV/H2l/7SZ8PP/dyr4eWXz8Lpab5H7LAqNt8Xtl4Fm//1QU8Z5HZqQWrfPqywnaq8La/VjZOHw4fdP+TxWNtU+65j+/S5TdL5zlkWeKrfbQlkx86thar9/XQf2RiOjs0lXco4/eFCWiVbVsemVa9pUDqsnA1ZKJt/RuP+sF1la14ZG7JwNV4nPrcY5HbVtc1VhWzXv/s+adtV2GzasN2uQtuG8ODBSVI9CwA8D1Y3PQEAAACA2+LsbB3u3t1cLku8yqrW+uWB079hZeyuZYm7Pvl183ApbS/3fx0GrrVrHjeILT2NILaZOLZPnznnX/fvOuaef0hgPTa/ff595ho/P38P5++x4Y8Uwsjx+o8ZhtfKP2djbfnnIp1n/bMaP99lZWx6Tj/f8e+K7scT3W2693RaGbter8LZ2Tq8++798LnP3RfKAsBzQGUsAAAAQAghhCacnq7DnTubcHKyGlTC9qFJuPqrBTjD4KcfP4RayBOy9v5+eqwJ4yHvdDVuep0ykNp9f59jtcdjbYe0zz1+7PP2HXfuMsTl+XOqZPetkN1nrLLtOo/H55NXxnb98orX/tx0ieN0ieLudlgFG6tT08rX9Brp9dNK3Frg21fKNlfVsfk5+bldxWz985fu5dzvLV2GsF217MnJKrTtOnz5yy+GH/3ok/D7v/9RuLi4qL6WAMCzQRgLAAAAfOr9iT/xWvj61x+GL3zhQdhsyhC2thzwWAg7Jygd7glbq/YbjlkPWfOwdXidshpxaCqkXCKInXu9fY4do/+xHBrOzgllp/qMHRsLRucEuPsGsmPn1u+XwWhbbAabL1uch655MBuy8dIxu8A2DX7LsLa8Tn/tbrxy39d07n0gPAxg+3nFZY3TZYrjbRrIbrdN2Gy6pYvPz9fh7Gw98sMOAOBZIowFAAAAPrViQPKVr7wU/sJf+Gw4P1+H9XpVDWLT/WHr4Wu+dPCuIDIPZdP24e0wWB1bTrUfOx0jbRufW9n+NIPYXYnTPonUbUuv5oSsZf99q2uPNeZ1rz1+fi38LLLX4vMwPJ72q+0hGwPXGJKmlbXp3rH5GGlbDGrz68T78a+ryg0hhrrpmLXPZBrk1qpm00A23p6crMLp6Tqs1002DwDg2SOMBQAAAD61vvGNh+HP/bm3w+c//yCcnq7CZjNcnjgGJrHarbZfbD2AqVe2jkmDqPzcvLo2XfK0Xh1bhlrp7XXvz3m8T9tU+65jh/S7SfuEsrv6Th0fO1YLSue0XefxdLjb/3ghr14dOx7FJYrT2zRELa9RBqoxlM1/+JBXuZYVsfnj/NzVqhtvu+0/u7EytrvfTaBWGdu2eSC72axCCCGcnq7Dgwcn4U/8iVfDD37wSfiN33gvbLejLyUAcIsJYwEAAIBPrVdeOQ/f/OYr4fx8kwUiZdgaQrxtskCnDH/mLCmaV7iW1avDILUW5NauW59v2feQ8HXs/LG+gthp+4ayu5YtPvZesXPO2/e6lRGzas8+TE33ji2XLa71G/Yfzq2vaC2vv/t5pOeWlbNl8FyrfO0D5RDKgHe1ai6XMB7++GO1asLp6Sq88cady3lc/zUHAG6GMBYAAAD41FqvV+H8fDOoio1Lg+bhSFoZW1a9Hnuv2No4wzF3Vclejljczr2/z7FjtM05tk+fpzHGdZf0nTPGoYHsPu3XDVunzt99P75dx/aOjUFnery8TcdJx0/75XvF9vvJ1pYnrlXI9tW03ffBdtsmgWo/z3xZ4iarjN1u80rZ9bqrjt1uu/tdZew2nJx0FbJ37mzC2dl65r8DAHAbrW56AgAAAABP29nZOrz11t3w8OHZYEnisaA1rVRNA9R994odU6uYnQpxywrYkVF3Xvd45x72vPfvf93nFP+O4Zhj7brOIceP+e+/7+ORUbP38rC6PO1TWwJ87LZeAT7+GUmrW8c+a+l4eaVrfZ5j9+tLm5c/pBh+58Rli+OSxefnQlkAeBapjAUAAAA+db70pRfC3/pbXw0vvHAaNptV2GxiRezqsgK2uQpMumrYMiypB0X1MHcqgBrer1fV1sOasSBnqKncnwrTjhHE7RvOHho4HvucQ5TX2beqdM45zYw+c887tG3X47Fzx+5ftjQhq1Tt94JN+/SVqekSxemY6VK+sU/aPyT7wqaVsN048dy8IjaeE6ty+4rXUPTtKmD7fWLzOcTPZvf90l7tFRtCXiEbb9MK2VdeOQt/+k+/Ef7lv/ww/ON//MOR1xsAuK2EsQAAAMCnxtnZOnzhCw/Cl770Ynj55bNwdrauLj0cQhqi5lVteYAarvpex3io2wenw+q/9PxhBeDuUHas/XkIYp9WCDt1/X2D0znnzA0/j3W9pycPV9P2bp7DADaENCidGm9q7H7p4VC5TZdAri+HXAuHy++Qfuni9ir4je19v36J4/iDkPh3crIKd+9uwtmZRQ4B4FkkjAUAAAA+NV555Sz8zb/5M+Hll8/C+fn6cp/Yfq/YNAAZW1q0Vpk6rGrNw9wQynC1DGDn7BE7XhUbx+vtqoSdCl/HxpnzeNd4c65x6Fg3HcCW0vnMDT3nVMkeEsjODV6vGyLvXylbCzzT9/KuEDWteo2VremesGWIWo4dq3LHqoL7z22bnN9XxMb55J/5Nns8DFr7PWPjfrGrVXv13ROrZuMesm3bhjt31uH01DLFAPAs8nMqAAAA4FNjtWrC2dn6siI2XYa4DF6H1alj1XO1Stk0iNtVNTusiK3vEbur/7CiN5/HPNcNNMfC2TkVuce+7m1y7PkdMt6hFcxzAvix4/W+Y+/p/H5+bh6C5j9kGLtGfr3yRwnlDynqP6bI94AtP2f1pcrz+dWO1b5v4g9BusdldexmswoPHpyEd9+9F1544aT6nAGA20kYCwAAAHxqNE0IJyery4rYvBo2hiFjQUk8vxbidMfy0La8bnq/r+Qbjpe21Spv02vFMfJAalfANuf+MR7vMtV/KsA9pN9tsM9c5/Q9VsA993U+9Brz3ntjn5vxH0SUIekwAE0/W+WY6ePpueXXLMeoB7LNyF/cjzrv13/vpKFsk1XHbjbd3rE/+7Mvh9dfvzN8sQCAW8syxQAAAMBz7+xsHf7sn30rvPPO/XD37iYJYftwJFaklUFJrWouhHqgMwxp0xBoGD7VqluHFXnjyxOXwdKuQCm3Kwzd5/G+19jn2tftd9s0Yfm9WseuMffa+87xOM+pacLV8sIhTC8fPH+Mvj2EuGRxfnz4+c33hk3HjPe79nwe/WezX6Y4H79fXrlfuji9ny9THG9jKHtysrpcXv1Zfe8DwKeTMBYAAAB4rjVNCOfn6/Bn/sxnwjvv3A937qyvwthhZVpZkZqPU96vVfONnVN/XO41Obz2WCVfrV/yaMb9sf6HqJ2/VBB7zCBq37GOFaIeKxR9GsHuIebNK+7Fmu4LGwPMWt9yn9j0duq6aZ/4OekedyFo7Xr9e6MPZtMx8qA130M2vW7ftw9f43XT4134mrZ1feIPRvowdhM2G4sdAsCzRBgLAAAAPLdWqyb80i99Nnzxiy+E1167EzabVVitVtkyoGWVaW2J4nR54LzidbjEcB+KNsXj3u6q2GHl63hVbj+XypX2vL/r8dzQ9bYGscc+/zpBaB72Tfc7JJDdp31OeFr22fV47jiXrU2sWu37pO/zWgDb3w7HTIPT3ffz6w1v84rWPqDtw9x0TvXPcn/+atWG7TbeD2G7TStkY8V+F+B2QWy4Wq745KRbYh0AeHb4GRUAAADw3FqtQvjpn34xfOtbr4R79+LyxOWSxH1FbBqqdveHlatXRyvh6FilbLpEcRnWpvfTYLUPfobt5fHlHHP8mwxim2ue/7THrV3naZ533XPL8/cbq/Z5232N6THGfhSxe7z0RxX1z/Gc74H8+6Upzhv7UUiTLKPeL1fcBbP1fXYBgNtHGAsAAAA8105O1uHsbB02m9VVkJFWxvbBbJOFILWgJu3XBzO1UHVXIDNVDTsMYsvrjodITZia1+77+wZo4+HV7rbrXmfOOU8zLD30WnPPu+5ruKv/nLZdj69z/TD6ucv7dG1jlejlbfl5rT2O/YefweGPLsYep98d48eGbX11fr5kevodFb+zYnXsF77wIPwb/8ab4fXX78x4rQGAm2aZYgAAAOC5dP/+Sbh/fxPOz9fFssTDitfdIct4pd5+1Wn163bjDAeqVfalQc7+17+OOUHcMULB64556DnH0oTbuY/rTRt7Xea9Xk1T30s2XO6/Gg33ka2PUd6P545fP19GuTs/XVY5hLjUcfm5zpcyTveOzfuMfe+U4ey9e91/0j07U2cDAM8CYSwAAADwXPqLf/Gz4TvfeSO89tr5VVVsPZQt//ox8grYXeHt7jB1rAI2D25qt8PxkpF3tM25v2vM6wSx+7bv2+eQvkvrw7n9zrnO/rH7hp1LhMbHHLMWsvZ7t8ZQswxQY8gaP7tdYBr7dvNLx4lj155Dfr3hjzLGPpvpDyW682PYmt6GsFq1lX1mY7V+XFK9CW0bsiWKT05W4eys+5EJAHD7+fkUAAAA8Fy6d28TXn31/Cq0mFvtWq88jR13hx/7Vsp25+TLrg7HqC9RfLyq2NsY6tzGOS3tus/5ps8/bLypkPMY1537mSl/LDHVv/yhRjn+2HdM/rhepT+cy7BCdr1uwv37J+HFF0/Cev1p/KwAwLNDGAsAAAA8lzabVTg5WQ2qYstK2L6tD1jSfSRDqIUq9fCj7zMMg2p70tbD4Tygre0VW85vfE6HVMjOqYKduk7admhV7L4Vsbc1jDpkbsd8ba5zzti5x3i/7LrG+PEyJM1vx8PR+v3hjyHGrj/87A1D2+7+sJp9rNo9P5ZWxtb3ju2rY1eXe8feD9/85sNw/77FDwHgNvO/1AAAAMBz5TOfuRs+97n74c037yYVseN7xYZQC0THK9uiuZV7ZVgzPuZY8Jtfr7Yc8u5Q9piuO/6xg9hnQRPsIxtC/jrUX5Om2b3fa9mW3kZjfcu2Wt9yzv2er/nyxuU45XW6+93zHFb+5sswd+15W/8jjHSJ43AVzJ6erq/2xAYAbi9hLAAAAPBc+frXH4a/9td+Kpyfr6/2WOz2XgxXezHWKtSmqhjHA9pdFXXxvFrF3tg8htcqqwDjtStX2vP+1PlzHu8ab1/PYxC7hEPC3do5c9uuc/1955oHtjGcLEPXsX1er1qSoLUMSGP74MpNHpyOjZH27ecZLvenzecVg9S4d2x3Tnxe8bPfDj7vq1W43Cu2C3Tj/rGxOnazacLZ2TpcXGyEsQBwywljAQAAgOfCG2/cCd/61ivhZ37m5bDZDJf6LJcCLQPY4dKh+fj1QLU+l+HSpGNtcYD+tt4+vxJ3P0uFOGPjHut6z2L4tGSAeaxzDx1zmcrfPABdrrp4boVsDGpjtWt6bryff87bLASuVfIOq2TzZcn7itj0NmRLFter5QGA20IYCwAAADwX3njjTvilX3on3Lu3Cet1bZ/YeqXp1LLBeTDat1d7TwSz3W29OnUsAM7n2GRjHF4hO2Xe89zd59Ag9tB5PkuOGcgeK5xcLuQ89NrzwtHx8/OxmsuK1RiOxlC1b4/VqmNLKI+FqOPzq58f51MuWxzPGauSTQPYWoWsLBYAbrfVTU8AAAAA4BhWq27ZzpOTdRLE1ipS+2CzD0Br1arzTQexfVhSr5S96p31SSvkhtfYJwQd67Pv81wy8bkNQWyT/D3rrvscbu/55RLC5W35uSnPK9vGQ9H0szh2fv9+qX+XhMFnvx+r/l2QX7vvE/e/jkFsF8auwsnJOnzhC/fDT//0C+HkxH/qBYDbyP9CAwAAAM+0pgnh5GQVTk9X4eRkVQSxeaCRVrvWqlDTx4dXmw2rWPM5DPtPVeiWYW3tWrvmsr9Dq2KXdOzrNcXf2LFju8mw9zYEzWNzmJ7b1OexrHSfc87YONc7p1al2lSOpz8MKb8byram+AFJGtCGsF434dVXz8Prr5+HzeY2/PsCACXLFAMAAADPtNdeuxN+5Vc+H954404RxjZX1WRpiBHCWLXasGq0tjzwdDhaq24rj+eB8FgQnFfVZT1qV91xfKp917E5x5e89jEdcq30nGMt59scaaxjjXMMS82lH7dp4jLB3RLD8XHt+umx8n4I+RLDtbZ8vO5+H6CW1w6Dx/nYcb/XfLnj9Hi8Xy5TvFp1fVerJmy3bVYd2y3H3obz83V49GgTDqnqBwCWJ4wFAAAAnmlnZ6vwxS++EF544TQJYUPIA46Q3O/bL1tnXec6OcdYMNtfOw99l5hD/bpP4/xjTPo2jfE0w8/bFLYurx6KHmXkUO4LOxaejs1rPGgdPk5/jNGOXKSs4B2r7s3HHe5/vVo1YbNZqYoFgFvMMsUAAADAMy3uFXt6WlbFxn0W0wCjGQQf+e2wWnVs6eCp9jwwyatWx+ZRVr+my5nW7aqQndNeO3ZoqHPdqtNjjrvEGOlYn8bga9/3yZxq7RlXHfmszRt7bDny3T9+KD+/aQXrsF//nqj9yGNO8JpXw6ffW91fX+Wf7xvbh7GrI/5YAwA4JmEsAAAA8Exar5vwmc/cDW++eTdsNumyxHnQUi7522nCsEL2OurBaD1oKXpVquBqIfFx5nSIZz3hMf9lx1vO0wgX53wm68fm/cBhGLw2k336H2wM51IutZ4uZbxaPTv/rgDwaWOZYgAAAOCZdO/eJvz1v/6l8Oabd8Pdu5skkA1XVWSrVVqF2kyEorW9XfebT3+NWpjSFP3yUCedXxnWlCFy5cr7TXSRJYpva1XskgFVE663hPB1zz/m+EvPZa5d8xgery8hPLdf3j9+1sb6xTG6z2o7umds+hkv97RNj+XhapudW46TnhN/dNLtGxv3jp2qogcAbpLKWAAAAOCZ1DRNuHt3E+7e3STLEefha60KLR8jv51x1X1neTX+nAC4NrfDws9DU5nyvNscht7maz8PbvfrV1aL15cjXnoO04/H22tzb7LnlH6Hjf3II23fbFbhtdfOw8OHZ0JZALhlhLEAAADAM6lpQjg5WV3uFbvK9ootq2DTqrJ8GdDhnpKHVMR2t/k+sfWlkfNquPT89Pr1kPhYAesxxrztntbzel5fv6Xtft3mB5v5sV2hbO0zPlxCuHbN6R909I/LoDX9QUYZtu6eSy187Stju/b1ugnn5+vwMz/zUvjyl18I67X3JQDcJpYpBgAAAJ4pq1UTvv71h+Gtt+6G+/dPkqWJh2HrWNhxXLsqXOuVrXngMrdKdjhO/f6c/ofY5/xPSwjcLT/79M99Xlz/NZhegnj5cfMq9toJfftw+eJyjK5/07RZEFv2yytju4B2s1kJYgHgFhLGAgAAAM+U9boJ3/nO6+FLX3oxPHhwkixRXO4J2y9RvP9yxPsrK2Lz/SCHlXFpe9KSHRu50jGnfYRxPy3zua2e10D3eM9rLPyc2kM27gkb+9X7x/damx1Pv4f2CXP77602O1armI/fL3GP7NUqhM2mC2QBgNvF/zoDAAAAz5zNplueeGxp4tqSwHkQmi8BOjw+31Sg2l9juDzp9HjHChSvU536aQs1edYMPyZzfsxw7Gseds6uZYrzH5WkFf/l/tj9/bhcOwBwuwhjAQAAgGfOZrMKm82qEsTmFbLR9F6N5RLBU2FGV7GWVsDVTC0vWns8HszMDVYEMHAsuz52uz73SUt2rP49M/xhSNqnrJqth7dNsly77wIAuG2EsQAAAMAzZ73u90fslimOe8bWlvPsz5sfus43FgKnFbFjyyTPr7KrdRw7+XkIY56H5/C0PI9LFA8tUek6VrE6p/9Yhfuuz3htf+j4o4v+tqzqH1+muAtiu7auMvaYlfUAwDEIYwEAAIBnxtnZOty/fxI2m1USUDSX4cN0APG0wpxdfdKK3O5xXiG3XBB5WwKaJedxW54jz4rp4HKfH0Ec0m966fSxx2kQG8/t95BtwslJ90MVAOB22Nz0BAAAAADm+vN//u3wjW88DG+/fS+rii2D2bRSNV/acxhQHLoHZL6fY7nXYxqs1IKTOUscPwthyrMwx9vs01HVels0Tb/EeOVoOOTfY+pzPH69JjRNW6mkr8+l/x5pr46l3zfd8sRdEHv//kn4xjdeDj/4wcfhn//z9/d+PgDA8amMBQAAAJ4ZL710Gt588244O1tnS3vWlv7sLB8Wzl2udNcYtdtKz10jzb/o3p6F4FW4ye2TLlm+u1/t8XDJ4r5P/iOPzaYLZM/P19ebNABwNMJYAAAA4JmRLsG5WpWVsWWVbHfOMNi8fqhY7g+bj50vN1zfZ7Jcovg6AewxHPsaz0JweyzPawB8u57XeEXr8eyqVt9x9sHjlOFq/v1Vr+zPv++aq8rY1aoJp6ersNn4z74AcFtYphgAAAC49V555Sy8+up5eOmls2rwOuawitW4FOgueZ99A5v8vPrJh455fZ+mMPU6bldgmdtnbrf5eRANf2jSL1nc94k/UvEZBoDbQhgLAAAA3Hrf/OYr4c//+bfD/fsnYb1eVfaHzatja6arZedqiuAj3SN2WJlbr267zvX7eVyvj6Dm5s0NQI8dlN624HVsPrdtnlO6/Vz76t3h/rP9jy7aHXvX5uPGcco9Zbv9Y/PvoFgZ260ccPizAQCOy/8sAwAAALfeet2E09N1EcSmPZrB/WMVhnWhyTBYue74++9xK0Cd51kK8T6tbubfaFcAepNLIe/TN/9RSe2HICpjAeA2EcYCAAAAt95q1YTNpkn2ih1WoKYBbRlEHJJLpMFMvD9cUnjXwPvPY5kMRTBzfE8jULxNwfLTncucYHTY5za9XvOMVevH77Cyor/23ZZX4Mf9Y5ecNQCwD/+zDAAAANx6TdMkVbEhdJVgTagtS9w/Titkx8PIGOi0l3dqIVAagNRCk7HliWv9a66/fPLz5rqh2tKh3G0J/W5yHruuvczc5lavzu93W/4tp78Hxr534rE0vLVnLADcLsJYAAAA4FbrqryaLGTIA8/pfWLnGMtjxtp3h6fjSyWPBbXH3VP2pk0FXE8r/FrqOscY96YCwNsTPB7TcfLU8UGWymsP/ZyPfffVfgwCANy8zU1PAAAAAGDMF77wIPzr//qb4Z137oX1ehjEHhpgtm29f2zvj7chDVbnBMCHzKdy9nVOfk70r/3NjpGOdZvGuelr7HPNufOZP++xitZa874/ttjf8V7v+P1TOTJxnSY0TZusFqAyFgBuE2EsAAAAcGu99NJp+NrXXg7n5+skiM3DhqdRUXrda0xVq8lMlnasUPdpu5llgJcfe+Kq7fTj2rz6ZcaPE64eOsbTWe24C10HrZXq2NUqhJOTVbi42Ibt9mnMDQAYY5liAAAA4NZarZqw2ayu9ouNyxWHkAec+1SBzQl4ek1WDVseq/WfQwD7tLVh/4CxDYedt2vMpR3yPG96DhMjHTBUGtBeb9zbtaxzfUni+KOUJvs+fPjwLPzczz0Mb7119wZmCgCkhLEAAADArbZa5aFDCGEiID1u0Dkcqxkcfz73aTz+srLHOe+65gSsxw5gD3HT11/S/s9t/0D2eq/f06lyvY7xL5v4XXRysgoPHpyE8/P1U5wXAFAjjAUAAABuraZpwmq1uqqILUPZrk/tvPp4XcgSk5Z2Yn/J4bFyr9qx6w6D4rHbZ81tXKr3umPX/pa83k2OVTvnOm37HJ9jbIy+vR6S7nftecsZT3831MdL25/uZyX/QUi/t/Zq1YRn9/sGAJ4f9owFAAAAbp2zs3X4zGfuhtdfP88qY6fC0OnK1OG+oW2bn1M+nj/2dP/xMYUk8xxjz9ebtk84d+vLMsPx5jh/nOtWq15/yeLjXOsYxr86+j1l+z22l50LALCbMBYAAAC4dV5//U74j/6jL4X7908uq2KbpOqruw0hZPc79eQhVq/VgokYnDRNCG3bJnsw1udWb989h6kxl1cGmrseL3HNfY8f2ve2OXYQu2uJ5evO4ZjnHnjFtl6Zmh7vbneONDsYna56HfadV2Fbjp+fs1Roe8he2gDAcixTDAAAANw6q1UIp6ercHKyOmhf1v0Dj/FOAo1dnoVKzpty7Ndm6df6umHwMfrvGG10uN3XuU74WQtT97/WcV+Lqa+m53MvawB4NgljAQAAgFunaUJYr5ukKrbJjg0rYmuPa4FIWVE3Fo6U1bHzKl8Ho3zqg5Bj7jX6rIW+Tzu03Lcqdqmlhg+p3B3pPXNp4TQorYem9YH6Cty5lbb7GRvvmNWx5ZLo6VLuAMDtIIwFAAAAbqEugO33i833jA1hLGyYWx03DF92hSLPXrjxrIWXczwrz+lZmefSrr+k8vQPJ9pK+DpsG44xvnzx2LLEY9cfP78t2nYvmbzv0sdj+iXdn7kvLQB4LgljAQAAgFunaUK1Knas7yFq1XRz5vXpMS98WuY6x+z/tB0yv6X2ir1u/6fzWu+qaD1mxerx92ktQ9cDR7nGucPvpb6yXyALADdvc9MTAAAAAIju3t2EX/iFN8Obb9692i82hHxp4vFwoQ0hNKFtY9/ucdajnQpU8/679mOstI6f8Nwbvtbzji1xvZt0E0Hssc47ZB77LFF8vWuPB7bzxpyqgq89Hq+QHZ/fPu1TjlcdCwDcBsJYAAAA4NY4PV2Fr3zlpfDKK+dhvW6SpYnLPVz3Nx3E5sp+go2lHRKu3qZA9mmEpUuO8TTGnT/e3Gr13Xs/d/vCzr3myJGd5+1/7u4li3ddcw7fWwBwO1imGAAAALg1miaE9Xp1GcSGrDJ2zK6KtDIsKSvsYlhzSDhyO5YAPdaSs9cd57rL6B5aVXqTyxZf5/pzX5NDX9d9/p2WrM7dNdacwDJdCnj4ea3dH36e43j7VczW9p9t23YkhG0H5+2e11jbsHFsfmV7/7XUCGQB4BYQxgIAAAC3StznMAadcwPP8ZCjHvakYcfx95Gsy4Of64SXhwSih/S9yaBzHzcxz2fltUktFajOeXzgVXYMM/XDil3LGk+Pff2QvT7+db532uJ2miAWAG4HYSwAAABwizRhvW7CatVchbJXR2YEC334Uqsqq1fG7Rr/uksjH++8pxH+Xfcax6iOvU4Ilv4d27HGn3v+dSqZrxvKH/t9MHep4PryvfXP9WFz7KtJy2AzBrnDOQz7Dq8/VqUaj9Vu8+rf4fn542FF8BRBLADcHsJYAAAA4Nap79l6vHRhV8XcssaXSb2djhnWPc0ne6xQ9pjh7txxrhv2HnvMYzo8RK3dP+T8Ocfmtu2+7rz3T33/2/Flj+cQyALA7SCMBQAAAG6Vrio2/u0fKOShRVtUwfVLF+e3hzlGpV46r0NDl2KkkftzHh96nWONmY5xzAD0On/HcswgdslweE7bsd5H6ecyv99/Fmo/XsiPj+8POz/ELb830nmkx9O55VW25XfMtLTa9rDz5x0TyALAzdvc9AQAAAAAVqsQ3n33fnjttTvh5GS1Z4DQhlrVbAwk0rHatp29B+2xtG2cQz/Pvu3TqP7vdf2+t9WxK1KnxlsiIL/u/MfPv27omB4vA9KubXpp3+GywMcLuWtLDpdz2bdad84xAOD2EcYCAAAAN26zWYU/82c+Ez772fvh3r3NVUXs3MAyD1nb0LbN4Nw0AH26Yeh4oBjnUQts6+c/jXBy1xwOHePQ8Z7lQHaf1Oy6FbH7BrFzq2J39dmnSna6knzunqtzxxse6yvl86rbvCp1uGfsxNDFeXP2vB0PZPN51eZRVvmXlbqp7jvxWf3sAMDzwzLFAAAAwK2wWjVhvW72qlwtQ42xIGSsQu0YlXmVMyaWV91zpMk57htELeFpVVw+a6WA+bKz8/ofo88xr3eMax5yfn2J4uFS42Ofjfrnblel6e4Adnw55Kl51PeCnZ5DWa27z/Llte8eAOBmCWMBAACAW2G1asJq1QWxcwLZsZClrEKrHbt8dMAs69fcdex2LSs6p5Jxn6rJqfZdT3zfwPJWvZAVh8zxGEHsvv82x5rHPlWxRc9K4DjVp2wbW154+PkfHhufx/BYWc06XA65/j0z/r20+7qVWVausfu99uldCh0AbhdhLAAAAHArNE2TLU3c3e5a6na6LV/ys789fjh6WAh1e9zkpA4JL2/bi3jonG4iiD00fD9elWz+Q4k8LC0rSculeacqX8cCz/r3wPwfTkwvj5z2a4uQdRjkzplDbaz6OPnrVVsGWSALADdPGAsAAADcCl0Q2+1xuG+AUO6ZOHf54n3Gn3POIWNePqocGw+gRkbco/+hIeAhFZjHCBzHzrnpUPY6c1gyiH0ajlMlO7dafezzPfeaw0rU8YrZYQAc23eHwuPt03Msv8Pq7eMVxFOVtwDAzRLGAgAAADcuVsSmlbFDw8rWWjiShhbpUqX5kqPXSSrGw5Ty8VhlW61tKlDedy7DY8dMZm5TIBvPe5rB7HWvN/fc6wSx+/wb7RO6zzUVzNZCzvoPKGpBZvrZn1ryd85ywOV3wjCk7dvGlimeE6CW5/djt1lb+hynfpAxrJrN+wliAeB2EcYCAAAAN+rsbBXu3t2E1Wp3Rex1K1wP6T/nvLGquF39x8KYHWfPusb+bkOCc4wQcInncazAd+75NxnE7nu9fcbZf4xdy4rXlvwdWx44P29svOFcdn8+h3OonV8PiOe/t6ZC39q1LVEMALfD5qYnAAAAAHx6NU0I3/726+GLX3whvPLK+dW+sZdHs77D6rFmcKxtQ2ia/ljT9Mfi/bGAoqygm+qXjlXv217Nv96vPz6lbdvLpZuPobzmnDmM9dm3PR4LM685p9+cMW6LfebztILYuWPserzPeGXIWe6xWg9D82N92Lp7Xu3VbVnZWp/H/FB1+JdXrpbnlHOsVwKXFfxpWz722A9AyrkAADdPZSwAAABwo+7fPwkvv3waTk76/0yxa6niKWPLku63/O+8atXp6rr+vKmlUnddI54z77wlKjevE74dcvzQ695WNx3E7tP/kNd8v7B2KkRM7+/+bNWCyX7c8SrU9NrjcyjnsW8VexmI1s6fV20/bIvBcfk6CWEB4HYSxgIAAAA3ar1uwmazquwX24axMHNYkVYLVeK55b6PtWVP9wsx9g1R0rmU/cplVtNquPE5zg3ldgVl1wlvrhMM7hPIDv99b7995z2n76Gv99zQdYkgfvoHBv39vBI0bR+rlh0LJtP+tc97rbq1HHdYhV/OefgZq31uh1Wt+fMdm8/wOdX2nE2vne8/249hnWIAuA2EsQAAAMCNm7sS71S1XC202FWFVgtiho/HAqVh+/NRmLZPKLdUmHvsaz0thwTHc/of+tyP/ZpdJ8yvf17mfWbaQUi5r/Glg6dD2bHvjWF4ms91YiaDOcz5burGzX/UUZuH6lgAuH2EsQAAAMCNWq2asFo1O/dGrYcT9Wq1dDnSPKDYHQiVlbZ5ZVs96NgvzBlW7E2FzOW5Iz1H2mvH5gRqxwhk5wST+4aXbdj/nKfh0BD2GEHsIf8uh7Rd53F+LK8gHVa/jleH55/FWhA5XmlaXmP43TD2XTGsrq893+G1+nPGKndr803b2pHnMB3gxkrZ4WoDAMBNEMYCAAAAt9Ku0GEY3gyrzaYeJ0f2ms+wfff58yvWalVzhwSw+7rpcPOQ6990KHudYHjuObcliN3X7h89VM+a+FFDd3/uZ3W839g16n3KELneli4TXF5/9w8t4rnpe2kqiC4D3GHVsOJYALhdhLEAAADAjeqqt5qsgmssLJkX6NTCkvpYY6HT1DV2hR7D9lqgU7+tVcDN147cn/N413i7zpsab25geWiC1BZ/SznWdea+Foe+pksEscd4P132LELLWqVprb0MIPu2qc/w+LLltf5lVexYRWrtxx/18cpzhnvIjn3+0znV94wt3wOCWAC4rYSxAAAAwA1rRpfSrIWofSXZsCqtVjHWByvTeznuqpbLw6JhWx/89Ncbu0Y9WJm63jAcGl5//PzDHSuQnXM89rnuvMvQdN8xr3v+rnHn9Dv0+KH/Nvtcb7+xxoLVYehYO3f6WBpApssOj3++2uK8/DM7Npcy2E2OZAFrfm7+nVT/rghFn3QOte+PfCnlesgrjQWA20YYCwAAANxq+RKe4/usTlWU7g5ax/tNhSa7xrxupVr93EMH3BWq7RtY7tO+7zWOFYDWxtz1d2z7jPs0g9h9gvYpU++j/Fj5Q4KxStDa+bWQdZ957Q4/621TP/QY+8FGeW5+/ni17tjj2li1v9pzAwBunjAWAAAAuFHdMsX941qoMF5RmgYRaYXseOXb5ZmV88vrtoN+NXlQUlbD5fMcPod6IJY/p/p1y+cyvD/HnED2kJBvV2h4k6Hs07BvuDun700EsfsG+ONzHFZz5sfGQshy3No4terU8lj/WSq/I8auOwxSxz5rY6Fpf53h90L6I5N8z9m02rb2vNrs/Lzvs/hZAYDnnzAWAAAAuBHrdRPOztZhtWpCCPV1iqer2fIAa6wyrBb+XB6pXXHnvMeXDE6vXx+nHi71484fc5+Qb+rxXEuEPPuM+ayEsofMc07/2xjETqu9l3eFmrU+Uz+EqFx1cj718fKlf/PrlgFuPkYtuC2P19pqcynD19pzqv+go5zz6EsAANyQzU1PAAAAAPh0+vKXXwxf/vIL4TOfuTM4Vgsxyvtpv7LatG2b0AUUTWia7rY73h1rmq5vXpGb9puee3luek5s70OadA7dbTy/u+3nlIx21S+OWbvmyOxCHm6Xj+f0D5VzauOM9d11bJ8+tf77nLOk6yRf1w1hp44fO4idM9ZUwNpm9+thZB461oPG4R6xfZiah6NjgWd5jbJatV7dWp9nPk4+tzIg7StZd88rn0u8358/XgmsOhYAbiOVsQAAAMCNuHdvE1555Tycnq4netWDoqnqsUHvParFapV8aTizq2Jtzhzq1YHj/fPr5MFUbS673caw5pA5tcnf03ad684992kEsXNNBa/zxq59ttLAsjZeLTAtr1f/nMTbtnpsGPQO51+7ZhmkltX55fljlaz1vWTr4ezuMcsl2QGA20ZlLAAAAHAjVqsmnJysLpcpHgYVsYp1WP3WhLZtQ9Okx5rk3Cb0Va6xX1p9mvbvQ5WmGauKjWPmVazpfJukZDW2D6tY47n99Ye6a+2qup06d3j/kMdTbWGP9l3Hav3m9J06t3RoBe2xk6254x0awk4dm9te//HDdR7nlbBpYDi2Z2u+Z3I5Tq1KtaxuLW/LH1Xk3yfptafnU59X/j1Se47pHMs9XvO/MHG/nE8bttt6pWw5NwDgZqmMBQAAAG5E04QsxAxhrDKsXvE1r1rukDCiDFTmXLt+3TK42X8e/f2xPlPn73u9ZcY4ZKxjJkjtgX/Hvv7cvocev24Qexy1sDS21/rEfmOf8V2ftfLHGsNjV2eMXqv2eGz/1jIUHZN+/uuV9bXrT4W8af8y8M7DYADgdhHGAgAAADeiaZrLQLZ+fKpyLQ05yvayIi4ZsRrY9CHNWCiTBz7pOWU4k7aV444FRWWYVFYGDkOsw8Pd6cf7th0aEO8TTB47GH1a9p37rr5zjl+3vfb+2PWe2f2eKt/Lw8/GeADZt4/vFRvPK68//r1RjtkOHpfLG49V66bPoTbHMpge2+u1Ns98SfKx8/PK2vy1qP97AABPnzAWAAAAuDF9ENsHP1OhaLyf3vbnD/vXxsrHGAYYY1ln7fpTIdC8MWoBSv28Mkjqz0tPvn54dhy7xt03ZH1WQtmbeF5LBLFzzis/c8Pqzdrnozs2/MyNB531a459Hssgc/AsJr9f6sHwruvUx576Thj7YUg+Zn38YUA89l2363sFAHg6hLEAAADADcr3Wh0GNMPK0FAs+1mvSIsntaEMPvKxpgKbsaBsaknVNuszrOJL5xWK+8MQpgx35oUrS4R746/FdOi3VCh7W1KmNhw2p7n9D319rxvE7hvaD4PZ/n5/WwaY43vI5p+PYTCahq3lZysMjtW+F8rPZz7HMDpW+XyHVbPjFbLDaw73jS3HnN43dvjc4g9cBLEAcHsIYwEAAIAbMwwO6oFMH1TkS5Pm4cmw2qwWqqRBSH5suN/irsqz2nV2Bb/1QGl4jXoIO1ZtOJW87ArWaqHevknOPtcf63PINZ9mOFte75BrHiOEDQceO1YQW/vsVEYpPpfpWMNQtv4dUKuuzT8fte+D+g8g5n0vpG3lMsZTcxv/jtj1vTXePrUkcajcT1+XznZrD1kAuA2EsQAAAMCNGmYFfXhRC23ibVm5Ogws26J92FYLUOrZRW0Ow0rVXaFsPr968Do1Xm3sWng9L1DbZSyo2yfs2+d6u8afe+51Q9NjjDE25ty+hxzf999mbtsu5Xt2rIJ0KgRtR/uk7bUAdmze4z9qiNfaVS2b9+3HGtunOn2utWWI82B0eLz2fVT7fqsFt7XXtg3b7dh3EADwNG1uegIAAAAAu4OFPPho2yY0TRnONBPhZXdOvN80Xd90z9p4PPbpx0uvNzy379sdi9cuj4eQn5vf5tePc+r75/Ppr9Pfr7yqIV0Gev/HY22HtMdjYeL4oX3nuIlEap9rHhqO7zp2nXB2/z5TYWNtafE4Rj2Urf0gIw1i8wA3PycNT2tzKZ7FyHWGgWt5bm0J4XQuY8sU59W7wyWK8+feVbimAWtZHTusgH3vvUfhn/2zH4f3338cAICbJYwFAAAAbqU0eB1Wd9XCyzScjMf7ILUce+yaMfisHxuGp8Ox+zmU49SD2Dy8HTsvvVb5eKxtf8cKZMPIsTnHD+17W+wb/N7GIHbOeWVleB6O5v1rQeYwGK0FpbVj+W39hDzszM/rxxvuN5sGp2NzGOszttR5bfw0iE3nMz6PWoBbn0vbhvDRRxfhe9/7SfW1AQCeLmEsAAAAcKPyirCygqypVMA1WShRVozGStSy6rWsIs0DjSYLOuuBUF6R2/VvQ6yKjeFvWh1bKsPcPPitV8f2zyUNgON1++fd9w/ZmMephh0LRccC2V3Hpsac6pu6DQHtvsHrvucdEsJOHZsbxA6D16nHw6rRvNo1D0THK2HT8fLvgVAdPx17uH9qbQ61AHQsZK2Fnul31fgca8fGAtThX63qNVwtOZxXxoarqtl5/64AwE2wZywAAABwY9KAJn08DHtCFnaU7bUKsXLfxnKc2lzyY8NlRtO51SrS+vY269ufM7x4bZ5Tzz9tm2e/YG28be74+45znUDzpgKn61x77mtye4PYqffj9GcsPTD2+cz71M4tvwumjqXBbdq3dk56zVowO3wO+XPLr52PX1uaOb1Wfs3hD1Pi91E+Xvr9InwFgNtKZSwAAABwg/Lgo7vfL008VbGW7+kaH7fZbVntmu/jGrLrdNWmfdtwnnnFa1rVms6/XEo4FfedjVWxwyWU4/zztnSP2HjNYXVsCIdXxJaPp9rCHu27jpV9on2qXvcJofaZw7EcK9jedfy6IWytffd8ys/u1ZEkWB3eH4altYA1DyPLz38tyKx9nwyvOQw6+3AzD053VbXWKmTHq3XLtr7KNe4LOz5WXxmbV8tut+lrnoazAMBtoTIWAAAAuFFToUkZKpR9i5EG/Yb3a0ue1h/315muiqvtEVkbqw+R8uvVA+fy+QyXIk2DpNp4+wcy+4R2+4yRHps73pLB6NjfUtc7Vt9jBbFzx6i/18c/W/1tLSwdO1Z+Pmvt/TjlcsDjIWk6XjlO+Rmrfb7K51QcGRlv/LmO9a+9BuPfWenr071vx18rAOC2UBkLAAAA3IhhAFnu8djv9Zr3HdszNg0/miSw6Pd0LfdeTcObbr/ZfNx8vmWfco/Y7lp9xWp7OUYb+mrW/pqxSrY7vw1pVWu/d2wcc/j88irfJtvDduQVD8Nq2PJ51s4fayvPnXNszvGyX3Qb9ojd5Zjh9Zw++x6bG9ruPneq6rUMCct9Yuuh7O72YSiZBr5lW22MWsXr2F+5d2s5l10VsbU9sId71+bH8qrZWA3bV852lbDp/fFlk0f+qQGAp05lLAAAAHBD2uR2uJ9i7S+EWlXZrr1d28G4/Tht0m8YYIzNYdinHZxTfcbFc8yvUa9yq12jfC75dYchWXrebtcJ8fa5VjujT63/bUqZ2nD4vJYKYsfmMta2+z1Sq9bsbsc/l/Vz65/z/Jzh8sTpvMr+ZbhaO5bOuZ9DW1yjDGyHr0UthM2PDb+Dhu39fNIxhp/9NtSe167vi6nvHwDgZqiMBQAAAG7MWOiaBw7Dytfudri3at8+PK9tyz1j831p06rZNDSpVcim1y6rVmOla6ycjdWy6T6x3fX6trFrROW8u7Z0HmmFbqjuQzv9uHyewznV28bOn3Os7LOr39g5qSWrZ6+bcO0bPB/z2D7h7HTbWBVsPJaHh8OwMr1f/wFBft1aAFoLXYf9huFoPTAdBqO1ELVcLni8va3MqVZZW/+Lla99ZWy/p2y/X2ybVM3WAuD6sssAwM0QxgIAAAA3Yljd1QeVaRBbBjXpUsGdMhgdLkOchplxueJ+ieDhsXSO8Vh5nelQNgzGiUsUxxB25FUJ6RLEsS32r82ttlxx+tzKsDa9zrSxQDaMnDs15pzr7Rp/jrkJVLoM9NKOFcLuOn78IHa4H/JUEJsHnP05tcA17Ti2PHEtTE3D3XbQVo5Z3o5VwebB7DCEje3p8x2GxWPhcT/fYWjcDv7S5zD2WpTPJfXRRxfhN3/z/fDee59UXhcA4CYIYwEAAIAb0wcwbXa/U+7rmv/FcLM/N6+UHQtha2FtGV7m+U4aJPYBbJ9txrY8SE1Dmy5ALgPRsWukr0u9CjbdI7YMavvXZhg2TweyY+HrPsHrrkA2TBw/tO8hlg5h9x3/aYWwY+37BbHxnDSIrYWtZd+xwLXsU1sqOA99p/ZwHQtiQ3bdsft5lWwf+A6P10PcfI7l65DPrzy3/pzG/mr/Rl0Y+8/+2Y/D48fbAADcDsJYAAAA4EaNV371IWpZYdbdji9f3FWfpsFtv3Rwek65NHEektTG78PRWLUbb/swt72aQxoo9+Fx9uxDLQBNx+orgectV9y318bf9/FY2672MHIsPT7Vp9Z37jk34ZBwd845h4a0ywWx+WdtLHwd9i3vj++3mrbvXp64Nr/aX3/tqSrZPCitn19+P9XC07E+U8sV98sPp0sVl33i8sXb7b7/9gDATVnd9AQAAACAT6d6FVitQixWmI0HH+mYaVCUX6s2h/rein1YUw82xsYfXwZ1WK2XnF3c1oOnPNSaem5lcDbVt/rsRtr2ad91bJ8+Y+fcZOjUhsPnMfecqT67Xve57fsFseU5ZXAZ+4+9Z8v3Xxp05tccjjM2n/y29lmavvb49YffS0mP7LkNr9FWxhpW6g4rXUOhXg073h8AuI1UxgIAAAA3pBYupPebLOzYbtNK1hDSatDuWBNWq9oSxMPlissx8rCkXC44BkB59Wnali4RXAtI8iWE071w61Wo+VLHfb+xKtjhWGlFbW154lCcmz/vetsh7buOlX129Zs6b8y+lbTHTrj2GW9OeL3vsXkh7FQwm4aMZfiZh5HDKtg49li/w/daHWurVdOO9RmrUK19L+VtedVqHpB2x7rq1vI6/Xm1a6dVsWkVbF8NW1+ieKzqFwC4DVTGAgAAADdiKpAog5r0nP7cwYhZn/y88SVOy7HT8cs51EOOXVVqeUAyNY/x516GYrX5lFXC4/Oeev12t03ZFRbOGW9uv7naPf+Ofd19+h96fJ8gtt5v7L2VBqr9seE545Wsw3HLfrve7+OfmfHnN1aNPtyzdRiYjo1bzif+G+fnlZ/z2nMcC3nL5x3HHy5/DAA8O1TGAgAAADdiWFVWCyiaq70R456r/b6vZYVsVz3WVceWwUlzNV53Xr8na+yTVpLGc9K5hqLaNB0r7lGbVsyGrKI1JNdJb+OYXd90D9vLK4epKtQ4Rlo52587eMUvx99VETtnz9jauXOOpcen+pT95vS/SYekY3OD6UOOz22vB/DDAHReRezw/tSxehXtWNVreu1+7GG/WoA7/je/UrasSs0rZMcel20h2Qs2Hbe/Xn+d/txYTRsrZvf7kQoAcNNUxgIAAAA3Ziw4me5bVpD1wUUaLpVjl+OX1xlee1iNVhsrb68vEzoMo2qVuuOVebW28jnUzy2rZXc95/p19g/9dh3bp0/ZP/7dBofMZe45u17b6waxl62V9/XYeWNBbO3c8nMw9jksPwtpeznP9NrTVfXx+6HNrpE+rj33cu7D8co51J/TcC7D74Vh/9r3TZucn86hfF3ycQCA20VlLAAAAHAj8j0Vu6qvWDUa90cMoasgTStLY2Vs7J/uoVruMzsMTGOfNhsrDTH7yts8cIpVsX0VaxvSqtx4m841XFWVxnNiwJJW6O6uhE0rcuv7xKZ98mPDatm5FbJz2qba02Njx8s+U/12nVdznUraY4Zac8fa1e+Q8HteuD5cRrt83J1XBrH1+8NjZfswuBwLPOvVquk1837zbof7uQ6rXuvjj/VNH5fVrvl1Y3Vrej/vV+4Z22Z/5XdOfC3SYwDA7aEyFgAAALgxY4FGGRaV4U3ZXgY1wwq4dLxhJV65V2N+/WFINNa3FjL1x2vVsMPnVJ/nsMJ1eO36cyjnXbte+hqUc5jXFtsPCQv3HWcf7TX+jnntuX139dn32LC9vpRv+l6p3w5D1Px9n96vvZfSc8u5lOOUn8vac8nHql+vdt2peezz2sTPVP07LG8bm0c+p7YYsz5uMfOr8y8u2vD9738U/uAPPhbIAsAtozIWAAAAuBHD8KOvbI3VYF0laV+Rut02YbVqL/eGLfd4jeM0xXj5vqx5qBLDjLxCtt9TNu+b7jUbK1776tZQ7P+aXy/V7xvbn1+reE2rWuM5IQwrgssK2XTu/TXj65RXxMb+5bV3t4VK+9xjY8fH+s7pf9P2TcDmhrSHHJ8frA+D1zJ8HFa8Th1PA8LyBwLjFbLT+8TurlSt9+vH3XVb+2tHqmdre8NOh7LpPrC1yth4nbQKNq2yHdsrNvXJJxfh//6/fxg++ODxaB8A4GaojAUAAABuRBc+5Mt0pgFE7JNXmJXVovUwpT+3/hcqezCmFbnpdcs5p3NI29MAqQyB+nPi+G02XjynHy8PsdK2YXsevA6Xhy2fZxiVP4fsSDa/3e27jqXH54aYbdj/nCUdOp85/ee+dmPHhm3l+ySE4XtiGJ7W3ze1914ZbqZ98/d+/h7N51arAJ9apng8YE0/g/G5lZ/BsfPK13A4j+nPYvndNN6v/8u/B2NbDH3Lz3l5/XYwLwDg9lAZCwAAANyINHgoA4fdQWoI+V6x/eM+VOkrTmOVbFetGkJa+RrPSfdcbdsQYsFoOl5aqZpW2vZ9h4HXeBVsvM1eldDvLRvP76+ZH+/vh6Rqtzw2nM/uCtnu9Uj75PMbGmvvx5+ubJ3TZ+ycmmNU0R4z2Jo71pyQdv6xNHzM29vk+DAIrT2+TkVsGrjmQWb5w4G8vf65D4NzxkLkqfAz/4vXHq+2Hc5rV0Vs2t6HpbGiNq+sHY41Vk1bPh8A4PYTxgIAAAA3os2qwcrq2O5YCCGsVnmlWL88cT1Mqi1TnAay0+d0bd3xWt9y+eA0CG5HlikeBpV9QFsGtdkrlATE+f0ykI39++WNy0C2X/K5Nn7f5zpLFO8KVPcJZaNDQ9WbTqn2uf6cvvuFsOPttUCz1j4exM4Jamt9h+PkFbG19jIk7foMA9jhvPetlh2Gr7XvpN0hbC1cTZc0rt2WAWz/45S0MjZ9XWv/pqpiAeD2skwxAAAAcEPyAKL8u+pVtNWCkMHIO8ZKw530WN5vOH4ZZPXtw0CkNu+0f37bzSm+LuW4tfP7x2NLvtbmnE+8vlRzOf/6azwdAu4KD+cGR23Yr/9N2neuc/pO9Rk/Vv83G6ssrQextQC1Ns6wWnN8CeK837BPeW7Zfzoc3b08cdk/PT4euNbaxkLYse+nsSB26rz8b0zbhnBx0YaLi2fhMwIAn04qYwEAAIAbsd12IUJX/dWFsrFCs237ytim6StIu8rYskK2GyMWdca+sSo2ve1DkbxSNg+cyuWK82V900rbdJniYeiZL5PcV82G7Fh/m+rb8uWNQ6hXwg6XMu7vp+eUx8ceh6JKtjbOVPuuY+nxqT5j/VPHWJJ4rusEXvuEtIccH2+vh7DD92yt31SfstK1v9/3rfXb1acW0E4HpOM/6Ki1j58/dt2x8LR/nC87nF9juDRx/zj9/kuXJM7/8ueUPq+Lizb8+q//MPzhH34SPv74YuQ9AADcJJWxAAAAwI2ZClJqgUq8393WK8b6pvbqNg178jGGgVOsFsznWIZRY9V8w9AkvX79eYy/Fv3xMuiqXS89UF6rfO5l5WTtdWyLx8O2qfPzY3MCxjn9dp17nXHmjHudceb23f947TMT+0+93/rb8cB2bKxh+Jp/9sbCw9q808e1z9buufZzzudWfo7L74R8HsPP4Fhl+vT31HC54nqFbAi18LY29vjnrm3b8Id/+En4gz/4WHUsANxSKmMBAACAG7HdtuHiYptVgHVVr32Q0rYhbLddVWjT1Ctj+71lmyzA2G676zRNvu/sMNBpioCkKfqkj5vktmxLn119r9hoWF3bXp0XsorZ2JadXfRN+7Qj98cel+f2j/t9Z8eqZMu5jbXPPV7rN6f/PuMsbd9rzum/K+iutA7C97Egc07VbBm6DoP+2pjDwLYdnDPWp3Y/n0tbuV79dvh90hZzGKuA7a/dV7a2lQrYftyyMjathI3n5rdplWz9Lw1u83/TYeANANxOKmMBAACAG9GHFTFcGatWa4vH8XgtFCr3WMxva5Wn6XyG184r08rgauz8MiAaziMdpwxy6/3qlaq1x2m/smq3Nu7wdR0+xzwIKv9thnYlRO2MPrX+tzV5OvT5HN6nfP+n7d3t+Pts/Lb+Ph+Olfcvr1F/X+aPh2358sX56zA252FQWbtu7Xpx/vnnNBuhcp38OdYqd/v29Dut//7Jr5tfe+r7qZ/77u8iAOB2URkLAAAA3Ijttg1Pnmwvq2Oby31f476JIdlDtkn2j22ujjdNE5qmvRxrWBnbBRuxf9e369OGWO0ar5GfP6yI3W6bsFrFECSthO33nu2OpxWx/W2/5+v8vWLj/XrVbK1v2ONY/ji+zvk80j59W14lO9Y3bQ8jx+f22XVO6Zh7yB4r6Zo7zpyAduRIJUQfD1fL9lq/8WWJ4zn1+2UgPKciNgzau8r2XXu5Ds8t92Udu01/CFKeGytW+9u+vb43bL7nazwnrYQtK2Tr1bB9xezFRX0Z9vTfIK+cBQBuK5WxAAAAwI348MPH4Q/+4OPw8ccX1XAlhOkwpz8+DGTKtqh/XN8LMh+zq2xL28vbsSVEx8esj9OHTvXnmI9Tr9Kbeo61+Zfzq70m9SrLdnBu7fWqXGXiWNrnuslSe8S/Y81jbt/x42NVk2kleNmetvX/xtMBX/29UI5Vhq/1vmXl7Njnov5erYet6fXLMDW/ft43bS/HL+eez7Et5ldeu/a9Fc9pi3PSa5fPr6zsH74mtTnE5dgBgNtLZSwAAABwI37nd34Svve9j8LZ2Trcv39yWaXaXlXBptWwsXq1u22zxyH0oUSs8KxXzfV9+z59NWw/XpP17UOR4d6xtb5T1bBd5W9f7dqfG6t8m8vH+XmXvS7HSI+HSp+wx7H08f5twyrZsf7lsTDRp9ZvV/+bdkhwOzekHTlSBJ397VgIGyr9p0PWss8wxBwuFd73nVsRW4an02HrMMAc75Nep6y0LatVY5/pyth0H9iyurbcGzYUj8vb/lr1Y/m/Q/lvF0J/7mHvPwDgaRHGAgAAADciDU9iCLFaTQUuMYgoA9Dutgspp//665ZjN1fnp2FiLZiKSyXHIDJd9jiEPCCOYoAaQ9f0ev1147WbbIzYvxsnPTftm/YpX5M0xM3Hi9fsnlMY9EnnGK+RzzsPZfPgqL0KtyfeBVev9Txzw9wlXSf4mnvu+NKztRA2bR+GrtMB63i/8aC1NscyfE3nkI6Ttg3HGAtu02u31fmWn+t0TvWQOD8WRqqTh8FwGUaPBcdt8Xh4PA1z8wB4vDo+fb7f//7H4b33PgkffXQxmDcAcHsIYwEAAIAble+h2CSVr+mesbEqtg9sQ8jD2b5//EurX/tK1bxyNt1ntlYBWz7O95ztg8zhnGKwWoaYubbom7fFkLR2fG6F7LDfMGyth6LDtu68kMyrHsoOn+PVCJXjZZ+pfrvO22VOte4x7TP2VN96ZWt3f34IW/YfD2eHj3cFtVP3a4+HS/jOD2Knq2l33YbB9dIK2BCGFbLD5YjrlbH94+E10tvhvrHp/X51gPI9Uf7bffe774fvfveDAADcbsJYAAAA4EalIUQMWvslPmvVsn0QmIezTSUEyQPZrk9fGTrsG67a+9s8gMwDq7yiND0nD1TL0LUevo61ldWvtUB22GfYL4bbIQyD1b5PvUq2+Fe7HGMslO3/PVLdUswh7A5bDw1nd1kyeD1k/GGQmh0tDswNYdP28YC16zv9eBjE1sPRvG95rLY0cT5m/dzx42Hkuvve1uY0Vu2a359erji298sPt21Z9dpePU77jbwTiufaDt4bAMDtJYwFAAAAblQfTISrULZf1rcPX8vgtA978krW+jkxWIzXrI/Rh48hlGFiHmamgW4MPNvsnPRaZRBbLnWcXGVw3TSkza8xDGTT/sPrh6sxaoFt/jgM2uJzHy6rXAtl+3PSccrz4rm7LRXOXtd+gVgalu4adxiyDscog9OxMLUcZ04wO1YtO3W8nEc57/S8/PM6dp2pELUt+o8Hr2Nj9efnYwznN3a/DGGH3znlddMQt/Z4+O8XBto2hIuLtnoMALh9hLEAAADAjdpu23BxUS7X2VeJxdumiUsVd8Fn97i5Or5aDZccLv/icsdd3z7YzMOStMo0hDQcjZW45dLF8bZt27BadX3juTV5gNVcjXnZGvrwNoQwWjW7634YOTb+uNwPtqySTcethbJTywCPh66HBK1zU6hDQttjJ1zzxpuugu3HmRvMzg9hp4PVsaC2dm7atwxsp6pmy/Z0DlNVrOkyw/k5Y3/D89JAtL48cUj6lhWubWWM/i/vk96G7Mcn6f20Qjf990vn8tu//UH47nc/CD/84ScBALj9hLEAAADAjcrDjX5/2HqwWu7B2oeKZb9aGNNXi5bhYhw7hDSELR/ne62moezU+Ol58Zw06OwD3b4Cdvga5cfzQDjdWzaGnnFe6XLD+bLKZWVuvmxxbLscZTBWv9xx+tz712f4mtRD2SKDDCELL5vBa7GfYwer8687fF4jPWeGsOmx9N9n2DYMV2ttZShato33qc+nDF/TPnnV7PBzOXwu9UrYsXB2/Hm12djlOOXzCUmwnfdP28f71L5z8teoHek/rCROn0vZ9uMfPwr/6l99GACAZ4MwFgAAALhRFxdtePKkq45tmn6Z4r4ytrl83IWAq1VfDRtCCKtVrFhNg9xaMNJcjV2e043TXp7fV8aWj2MA2e1v2y9zHEIMTuI18n1iYzVvXz1bVrtG/TkxOK1XzZZhar1PGabGpZbLoLkMZdNzdrcN29MQtv4ckzObWp9639xNL1V8WNA7Fb72j+uhZrxurX0sJK0FtnOWKR4LYYeBab1v7dhUpWz8HM4LYut9+iV/+9taleuwMnZsyeC8Mjat3i8rXdM+ZYVsrQI2/YsrA9TeU2PBMADw7BDGAgAAADeqDz/aIggZhjYxzOzO6ytA+6CnXhEbj5dVo3nAVdvbtJ9jWYla9s2P5eOVlaLxnFpVbBrIptWztb1i0+vUqmX7a5YVq+USxMNgOK8CHi5dHJ9XOn79uabzDdm5+esWBnOo/VskR4rXd8lwdhiO7nt+PXQtH9evc4wgNp1LbX7lPPLz0hB1fB5xLuV8y+dW65OeW/v8l8+t3qctrjt8PHbueHVrG8afU/p6tMn9udcox56WjgkAPDuEsQAAAMCNurhow+PH2/DkyVhlbB+2dYFjrFaN+8bGkfq2/pxwVUkbb5umuaqCjRWyITSX99Mxm6s9YvvlkWN7P49+D9k8DO72ju2P9W29WDE7vi9sP+ZYhezwfri6ZtxXNz3Wh7J93/Lc3rCtr64d61u2l8dqx/tQtzS+1+xghJn9ljQerE2Fr+XxNPhLH5ehZ/3c+jn7VMOWj8uwNB4fX743D2/z84YVqnn7nIrY+rF5lbFp+9i4+ZjlfrDp+enj4V6w9YrYWAkbb8sQOH3t+tcmr8YFAJ4dwlgAAADgRsVQog9JyqWGmyIwyYPPqDuet8X2dNng2K87lvfrl/MNIQ0vy4CqXB44HzO/XllhGkK+z2q8zcdLK2j3rZAdPv/YL46R7/c6rJLNH8dzavu91itlw2Wla7mvbD+/PthOh8z7pPNP2/epnn0a5gewfduuEHY47vC9mreXYWqtLQ33ps4tA9XaNcfC17xP+VyHSxYP22tB7dj820Hf8vrlcxrOvRbClvOfqnZNrz9W5T9eETv+Hhne/+iji/Dee5+EDz98MjwJALi1hLEAAADAjXr8eBs++eQiPHmyCavVNmy3q6Iytq9W3W775XvL6tbVqgtAY5gbA724bHA8p7Z3bAjpnrP9krl9ZWz3OA2BY5Vtd4000EqrXLv9Yvuq1jYM95XtA56+4ref45wK2TKoTZ9D36983Aeu9eOhaOvb0yWPx/vWj5Xhc/64NkZpOnkd36t2X/stHzve3lbaao+nlzKeCmCHbWP9d4ewafBYnjN2vLw/fFyGn/058XM+Xf1aOz6vMjathE2vM1UZW9tLtqyEjcfHKmTT86b+hkssp/8mfRC83Ybwe7/3Ufi1X/t+tmc2AHD7CWMBAACAG/VHf/QohBDC3bubsNmsLpcUTitk+2AjVnWmVbAxtMyr1posXOmXGY7L99b2lu2rP/OQq39cVr/mfWLA2YeledVqH8jGILbckzWqVaf25+XVrWl7SPZeLc+Jffsq2bzvWFVsP+9eWikbx+73h6211+ZW7htbVs2G4nWsh57DSuD0uR7fVEBbu3atf962fwjb359XCZuOkx4bq36tB6392FNBbPqcyrml18znuasyth3cDkPU8jph4tjY9crq4Lx9uq0MhtOlhUMRFNeeY/3fOH0N4ioCAMCzRRgLAAAA3Kjf+72Pww9+8El47bXzcH6+DtvtKlxchLBe93vHxkrXGE7Eata0gjU+zpc47oPSWKHaV8Y2lX799eqVsSHEcLXfhzYNR9PgNVxec/ic8xBqWDkbj/VVtGFwfHg/TBzLlyWe3hc2f5yGrOOVr7srYuvHasfzvukSx+U59WPLKoPU8WPj50yFr93j6QA2bS8D0OE5tf7j1a9z+kxXyw6XIh4GoLUgdm5FbO0vr2StLQ+8a+/YfG/YWuCaV7KmfdLq2LJaNn88XJZ9+D7IK2XL5w4APHuEsQAAAMCtkC7pGYPSNBzplvDt+saAsA9Y86A29u2D2Sa7Tfd5HQZETRaK5JWx4erccu/WWhCWh7PpcsMxaGmyILevnA1XbXlla7xeCOkyx7tD2H7u8Vi6p+vw3P46c0LZdPzh3rjpfNNz8nCxf9xU+pTGQtqrM+utI7nv2DWmTAVjtX+vQwPY9NgwmO3G3zeEzdvLfvstWzwe0o5VvqbPc6q6NA9O8/77LF88Ht7WgtXy3D5MDVlbGq72Y+TfQ2XAO1yaeFz6mn/wwZPwz//5H4Uf/vDj6ZMAgFtJGAsAAADcuBhwXFy0lxWnfShbr2Ltqk6bpr28TUPXPGSJe8mm1awxJK1VxvYB6lRImy7124RhgJUvRzy1PHHxSoRhGNm11QPVfDni7vqxf0iuHSrnDK8XH6dLJ6dLF6dzGoa58fwQylA2PzYVzMY5lq9l3rc/Vtftnzu0KwA7pG/937B+/ljIWjse/13r7d2xWvuuELbW1gd/40HsrtB2LHwtxxj2mT4+nNN4qDp9my4hnN+Ohbvl/rB5pe1w/OFesHloW+5TW/47l//W3XNvwwcfPA7/9J++Z4liAHhGCWMBAACAW+HiYhuePOkC2S5kDSGELjxtmn6p4hC6MK8LbMPV/rIxsOyXM+6XHY7HumWF26uAtg9y8+WJ06Ak9ulDob5qN+4bm4aLfbiSh7/9ksN9IJsvTxyS+83V9eL56TzSpYxjCJsHq+1lmFqOHWY9jkFsfp20Txg5t2yvHSuPj/UZ6zvnnOubDmPHwrPp86fC1/7xIQHs8LyxIHPfEDbOuxb2lqHirmP1gHYsWJ3b1laC0dqyxPHzPB7S5ucOj/XVrnn4Gs/pQ97xv/R1Tf8d89e7nG8AAJ5hwlgAAADgVqgFF/n+r/mSwmmQ2gcmw2WH60sT90sD9yHIcKnh+m1fOdtVYKaVsf210mP1ythyeeJUHCMkwWh/3fx4HsKmywqnc++vF6p90wrYfAnhdB59yNuPm4e1+fl9lWr/moXs3O5YnzYNl2XOlxdOQ8XyWN/nGEHtMAHbFYrVjs8LYPvrpcdrAeqwTy2Ezc+pBalj54716Y+nY7bV9jRwjWOU7eMVsfXr5vNti/PG2/Nr1ZciHr9fC4ynA+JyueLaeeW/Z/Kvmfzb5XMGAJ5dwlgAAADgVnj8eBsePboI5+fr0DTt1ZKcXfVoCCFZvjZWyeZBXF5BG6tCuwravio1Pu4rZcPVddJK23T8+LjrFyteu6rZPmTt5hgD1vQaMdScrowdVsjGPjEAjQFvfD3icsflefH+MCRN+/XnDccZ9knb+nHTtjR4ja9/HiaX+uumbW2lrR9vOMblLLLDxw2vdoew9Q71cPbq3qBP3n9+ANvf3zfU7B9PBbfDkLZeOVseG6uMnbtc8Zzgs/usjZ+TV8jWlirO76d986rX9C/f47pWDXtx0S+7PrZXbHy9av8WcR5xDADg2SWMhf+fvT+LtSVL7/yw/1qx9z7n3CkzKwdmZmWxKlldZLHZYg9kd5Oi1HRDIyTZpiDoQYAmCGoIEiABhl78IL3YEOwHPRmGX2TDgB/9YFmQpbZlww3J3aAFscnuapPNbtZAZk1ZleMdzzl771jLDyu+tb41xY597rl5p/+POIyIFStWxI493Mr92/9vEUIIIYQQQgh56ngPXF46XFyMuHnTYxhEXLTSsDrVWs8lm88bW87zOnesnic2T3AmoVju8+q4IEq1bCnnTdVJ3joZq0Wr9NfCtTxOJ3Pzc8j1lHPKJnGZxk0SNU+11klaxGss55StU7xJxNUpWn2dKVGcX3v+WPR46XHm13ZImF4nc0nFJQK27HeshNXteuw5oZqWrX71ceUx5fGtvvm+tojN78HScsXtfel8vWPKfu2+9f65benfO3cpf+u0bPZsF9egnwfnPM7P97i4GEEIIYSQ5xfKWEIIIYQQQgghhDx1nPP43vce4MaNATdvrmEtsFpZAA7WAoCdkqoi+LxKigK6DHA5v2xKsYo0THPHaokoZZHlLyVmZX/algRtEqE6ERuO1wnbfN7XJIll7F5CNiVj0+OSMeRx6/SrCJ1wHxDH6aVkpW84l5SFLiWtjC1omVYnavWYdf9WYjbvo9Gljet9vrvvSXFI+B6Sru1t393fkq9pvSVaawHbautLWd8cR+TiXN+WoG2NkUtSxNTnfDI2b+slYg8nZNspWJ2Alfd63l6mY5Etyz7jWKdp289l/YLS1/Tw4R5/62/9BA8e7GOlAEIIIYQ8f1DGEkIIIYQQQggh5Jlgt3O4vDQYRwfnhkmilinXtAREXJRzwUobimNRHJ8nY3WCsxRfur0ttPRcsfkYeXo0nV/KFev0apmQTQlWud4yseozmZrOX6dadZpXzw0rfXvzwuo5Yesx82Prx5u3p+tENV6ZIpbx071si9k5QXoVUXtIuLb79oVqr21ewuZj1q/LWqTqMQ+J2JZYLbfL13l/f5nkbSdvy37leWvpOidx28K1116WKC7fz/W5ysfXTtzmIriVlk0/4kifHb7Yrq9FtkXIMhlLCCGEPN9QxhJCCCGEEEIIIeSZYr/32O8dxtHCGAdrg9QbxzSX6DiGVKnMCyukZGvqm+Z1FQmYxGiaMzaNoZOv5dyxIleCJE6CVISmFo2SnBWpDJWM1fPJeu9j31LIeo+ZZKxOtGoJq+eX1ePGu6TkauqbxKtI1OVJ2SR2S8mr710+P2w+HrIx6rb0HJVytyY//vFoDzA3br2vlpBlv558LfctEbD9/kvKGNcSNheEbQlbS868BHASknnSFUjJWHkf1kL2aonYMu3akqfHzRXbmju2PVdsOWdsEu/1C0HfCy1+ZQxCCCGEPP9QxhJCCCGEEEIIIeSZQgsN50yULdamEsA66RrkpIiMer7YVqo2T7np8cpjwzW1JFaaexZIgrNM1/q4T0vWOiGr70Dqm9KpWvzm/XQSFajnXU3jJer5Xeu5YnvbaUxk55THqxOveTLWq/VUqri+Zq/aysfcenwlfkbULmepyG33a6VAW8e2BWzaPixh6/2HReySssU9Udt6T/REbH6NuaDNH0tqmy9X7IuxUiK21Z4nZufGL9dbf179SSnjQ/3ylG26X6jQ93McPT788BE+/3xLIUsIIYS8AFDGEkIIIYQQQggh5JnBe4/dzk3JWA9jHMZRJ2NT+d6w1MnVfL5Y6ZPmek0p1TA/bEqMyhyxOnkq2zKGrIfEbJKl6Xok7SqPJVwTkCdh9bYIWWkP6GRsKtUs8+SW/cK+fJ7ZlAxO5YzbxwCSgG3PC1vOL9vup8fLjy/7y73JhWmZmk1jzomovnEtJe8xLJOwuahcMsZcWeJ8uyUq6zGOFbD6+KtI2Nb+cr0nbg/NCxv6hGuR96s+pp2abSdl85Srbk/HtNKv6dheMlbSsfqYVjo2JPfHsZ2I1s9lTzjvdg7f+tan+Pjjy/4AhBBCCHluoIwlhBBCCCGEEELIM4P3Ye7Y7dbh9NTBWhsTskGQ+ihOgSAAQxnfvKSwbpeUqxaxUh447QuSNZdTprmdBFSdjNVJziTJcrna2tbJ1vYxModrq9+hdVT7Upo2zQ1b9w1tZaniVj89Xistm6gTs2HbF9t5cjY/T7q2FmUp5DlpWzMvTJtHzArZqwnYsm8pSMvxe3I139cuQVz2ke3Dydh+WWItXMvz1eK2PHfrmP4yP39r2dvXWs+38xLHui0XsaWULa8v3dP0fLeea5HFInUJIYQQ8mJAGUsIIYQQQgghhJBnBu+By8sRq5XBOK6m0sQ+zhcrMlaSoKl0sYlzq6aSxiYeA+i5YJOIlfFElMj+vCxyPh+rlODN5Wu7bHE5D6z0KbfzdjT6lCLnahK2v29u+1Db0vZyX2t/r99cX9WjkLz9sZazTMjWnVrHlTJuqZxdKmZ7ErbcnpOwdR8taOvtnnztj1FL0XTtLcE7J1Pz9lZCNs1Xm4RrmZIt07NpX52O1cI0P0/dr7xP+ba+R+kxyfGEEEIIeXGgjCWEEEIIIYQQQsgzwzg6/PSnF7h1a41bt9YwxmC18rFscJKxQVZIuWFdJjiVHE4li0NfE+VuKilsVAJNHyvlikN7kriSsBUp20vG1qWJpY/34TqAVC5ZPw6Rv3kfXSq5LkkcKEsYI543rcu1pnuYSgTPS1l9TF+ypva8f+sYfdxcn17fEnlcB7odiRZp/f1z7b7RVm/PlTHuC9j8uL5cDddxnIRNx5SiVfct+wHtssTlWC2ZunRfWYq4XZq43adVrlgf3ytbXJcpTv2TRE3naT3fredXP1bnPL797Xv46KMLPHy46x9MCCGEkOcKylhCCCGEEEIIIYQ8MzgH3L27g/dhzsU8bZbEZErB+lheWERL2G+icBGJmsoSl8k6+atLDCfRqkWVLlfcT8ZqATufhPVZCeI0lyzUPn2XkmCVvmV7f70eMx+j3T/0S7LT+1S+WKPLDefnSDKvPG6+9HCvX9FLyeQ5AfY4HBq3fo7ax5T9WoI1bfclbEuuto5pidnDfWopm45N5+4JWz1Geb7D5YKXSNpj+7UlbV6GON/WKdh8nLxMceqbn6d4Jqv7pe+tPueHHz7CBx88BCGEEEJeHChjCSGEEEIIIYQQ8szhnMdu52CtwWqV5hk1xk7rPopB2afFn5Q1lrHCEtMxSYCW8i/NOZvKE6e0bRhQyheLOJXjwjJJUknESiJXJ2F7CVnpL0lgnZpN12qiDNWpYO8lTRv6iOSRY9N6LlPlMcr+sEz3N80Xm0o6637q7mXPifQP60at58elY8pULbLnRz+/+px6X73/8WgLWL+gT1+s9vensXv7WlJ2SdniOekq261jjilJrPvJdkrItuWr7tNf9oXrXEK2VUa4TMRKe52azedu1T8KGccwTm9Zku5deX/z+zGOMkY1BCGEEEKecyhjCSGEEEIIIYQQ8szhPbDfO6xWBs4NmSjJ060yv2s6LsmZPNGq53pNYxh1jBZOZQlixO32nLGlzGonYvV2SqSm9nyJbpuWqildm65NSIlU01hHJnKTcNXbKTmbtvN+6lnLriEX5Om+lAnXtK3Ftqn66NdGec6sJdt/VTPbNmJzoqwnbttitdw+XsKG9ZaILYVrPkZPvB4jYvX1tlOv+fWW5Yrra21dc3ldvQTs0mTsoUSu7+zz1fGlIC63W89l/rjKxwTsdiMuLkaMI20sIYQQ8qJBGUsIIYQQQgghhJBnDuc8zs/3AIDNZohpVWstAAdjbBSF1nqMo51SoTaOIenLcYTqqxOy9Xl12jRPyaaEqfSTZKyWlHqOV9kf5qUN8lLKK4tQlTliJSGby1fEdUnRhvPo69PlkFOKF0jXVCdjy9Srzx57LwGbH4PYL52rnIs2F6uSdE2CNj9WEEnVE7C6b7lPP4/T3nrnEczJVxm/16cvYefEaz1mS7Tq9rZYbbctKUesZerc/jkJW4rJkCSfk6CHpGpKsYbx2ttXT8TmZYdbqdd2WhYxzdqXr7Volj+duv2jP7qHb3/7Hh492tcDEUIIIeS5hjKWEEIIIYQQQgghzxxBxo6w1mIcPYYhlQkV6Zfki5lKB5soDSX5Ws8ZC+h5W7UYkaSsFpe1iMpTsmWKVCRrOCafC1aO14lYXbI4F7F5MlanU9P1aPR5Q4suF1wmY4V63DIZ6wsBC9RzzOZzwsr4+T0BdGK2vM7+sfk50uOp28syx9fPfAnZvowLx/b6Pa6Erdvq/m1he5yIbcnXsl89Rt3eTpDWyd2erE1j+ez886nW3v6yz9wx8nmTL/W19J7T+n6k5+7ycsSDBzt8/vkW9+7tQAghhJAXD8pYQgghhBBCCCGEPHNcXjr84AfneP31Ebdvr2NqMyRIHawVQWhhbUi/WqvnbvUYx7Cu554EQpLNWtOYn7GVOM3TsUIQvD6Omc8Z66cUL+K8r+XcsGVCVtolKauvo27Tc6z207KpPUlVnXzVaVrpK/eun4xtzRWbZG1YT8fLPdHH5LLYFNupT11+OJe5qT2upWfyqtWJm+Mu6dOTcf2+fSnblq9pvS1tl5Qnrrdr6dqSsrKvJ2HLZGyaK7YtQLVQlbbWnLE9iZrvy8XokkSstOtEbLktfXV6NSVk0195H1rPW34ffXat3//+Q/zO73yE/X7BC44QQgghzyWUsYQQQgghhBBCCHkmEfkxjg7jaLJkrEhQETdJjgYp6JxvJmJDUrZMu+V/Mt+rjJUEVp7Ibc8dq9O1Kd16lTlj87lZ9Rytdb/yfHmyFtmx+TgozgXkqVc9Vn6MpjcnbCv1mtr19eXnbSdj2xJTp2njVT4Br9UTr3PnK5OQcwI2bPckbP3YexK21TYnYvPxWsI09T0kYluJXN3eE7FaVOpj83FaZYznShwvScSKCC7Fbl36OBe3Sdj2n6Na0raStvu9w+WlAyGEEEJeXChjCSGEEEIIIYQQ8szinMflpYMxBsNgYK2BTsaGOWShUq4pPTqODoDNyutKWhXQadZ0Pp08lb5hzteUGHUu7y+J2nzM4xKyrTlj5ZgkoFOiVfrpxGuSlSklW84lW+4L1KlX2Zb96hFnbaGfjKvH0ueejvD1HLOCnFdv589NmaDVtCXt46Zjlwjduk9PGC8Xr2m7J28fJxnbEp7LyhXXfXOxqI+Tc6SEbE+EzrVpGRrGL0sDt0oGzydi6zSs7iOfI+WcsWUaVidiDz3XtVROsrcUuoQQQgh5MaGMJYQQQgghhBBCyDPL5aXDJ59c4tVXN1ivrSobGvanZGyaMzbJVDPJjlZCNiVng/Ss03giP2V/kKiIAjWXU+35ZYNAMgjOWI7zk1QuU7RayAKlnC3nka3bymPKdizYF7Z7idb6mLn2XOLWade0oefnTW15yrCmbVv78+pejfY4fZHaa+/PK6q3a5nbTlrW4x4jYct+bVGbS9t2ujW9r+T4w+K2J13nhaxe1mWQlyZidYliLWLLvrnwLf8OS9h+wjhsezx4sMMHHzzET396Xg9ECCGEkBcKylhCCCGEEEIIIYQ8s5yfjzg/P4cxwK1bKwyDzBPrEOSohfcp9aqTreMY7IcstcCVpCmALO2aJ2OTfA1LkbR6Ptl8nlVApEsSvpLo0wlZKW2cp17N9DiCmNSp2bRMJZl1GlZkcJ6wBeYkrE6zln3DPW3ND5tKM+vUaqLum4/Z2leOl+6jmY23ti2oSK+nk4yV9nrHUvmqt48vW7xEwh5Ky7b719u1rG31aUnJOWHanzM2JWT1djkHa1r2E7Hp+LaY7c0Zq/cfuqf186avCbh7d4u//bc/jmMRQggh5MWFMpYQQgghhBBCCCHPPM6FuRWHwU7CMggMa6X0cBKrQiorLEsRo+1yxbKepCvicTJOOGcaS/qGa8zlrCRzk5BNCVlJ8co+LVDz8+YJ2/R4WgnadExZnliXNNb7gHkpK9siUvUYiSSm5PH3JK4+ppSz+XY+dtZyUNKmfsDxUnaJhA39+h3nhNyh/bVgzc83J2BbbdcpYXXfnmQF5L11WL6Gvj35mgvalrDVcrOWr7mobSVi5ySsjNEqT9y6h+oZmH3ceixCCCGEvBxQxhJCCCGEEEIIIeSZxzlgv/dYrRyGIc2fmuaSTDJW2qVPkjBBUqa+JjtGyxKZzzUIVEm2pnLHQJCYSXCVZYsR27WQzb2NHK/njNWk623L117qdVky9urbvbYl7a195f5en7n+NVoMH8ucaM37HWr3jba6zyEBW7fnx7XGakvUuk228+NqadqStC1Bm4/fEpM9OVv+LSlZXEvacl89r6yslyJWy1ItbHUp4/I9XD+nrf3lYxpHNyX2KWQJIYSQlwHKWEIIIYQQQgghhDzz7PcO5+d7WGsmUSpleR1CsjNPWoZ9QXSk8r46tZeSm2VyMpxDpEtLluTlgVtjiTzVc9emtlRyWI6RNKxO5ko54rQEdNniND9uWtcJWDlfuC/pmnUSVvaFpcjrNGdsezs/Jr/vZco2Sel0bL5PJ23Tdi4A6xLGqaGXfF0qVI8lH9bP7Gv370vZ5fJV769labqulmCV7VrSzu9vCVa9ne+rxSuQJ1rLeZ8PlSee365lbJ1yTelXnXj13mMcRZLqPj7bzh9zfe/L+96Sxs4BDx7s8Lu/+wnu39/Ge0AIIYSQFxvKWEIIIYQQQgghhDzzjKPHdutwcuKwXluVjM0FkZQP1vOwln1DnyRp5U+EaBIrKblaCiZJwtbJWC220vyqklaVksPlUqddpX+Qn3op8nNJMjati0idS77K+PUYrf69Nn19aLS3+ucSS+Rvu4+mnKM39q7OLWMcou9u2zt6/VvXOi9g83McK2Hztrx/e7svYnuCVZ9Hv070+6fcrt8zvjl2fo29pOyhlGzeV5cjbgnRtF1K41K+5uI3fw7TPSzvR/lYRQZfXo54+HCPH/3oIS4vaWIJIYSQlwXKWEIIIYQQQgghhDzzPHiwx/n5CGMMhkGSsWE5DEH2DQMAuEnmORhj1VKnOP0kOG0lWLT4szbf1ssw92tqS4nY/rJsEwGbkq2pNLIxaSnH1ilYuYY6Jdtq14lZuQ8p+VqmXvV2utb8nKj6iSjW902na7UYzPvoksLl48ufH52iLZ+7Y8XpUg6J2tb+toBtS9dyf0u+tqVqfcycgJ3rMy9ol5Um7knYJXPIHpeMbZUl1mlXnYQN+yX9midk85RsOxnbez7ytpaU1dd7eenw27/9U3z++SW2W4pYQggh5GWCMpYQQgghhBBCCCHPPOMYxMhuF+Za1BJFzwub5nVNc7+GfQa5ANLbJkvFAsj6JWml069l2+GEbGoTeZnGTyWF66TsdESjLRDka7qOXko2pV/FGqUSy3LtZd88uapLA+cljhN5yrZMvqZzllJLb7SStVMvX/bNSyQ/OfoSNevVFHW+20cLvLl9pVjV47ak6/x2GrPVp1+eOL/OQyI2f+xzIrYlfY9LwuZiVtp6JZNTclbPDavFb16WuH7u58sTt++jcx53725x9+4OhBBCCHm5oIwlhBBCCCGEEELIc8NuF+aOHQYD721MyIZUKQC4aZ8BEJKx1lp4L8t8ntPgS9zU1wOwsFanZn1MwZZLIIlAaTsmGasTr+V2/ddKybbGyNOwIrd0Yra1L7TnSdn8WlPiNW0j65fkbNkGaFlcpmaBJFJb6dmwX78KTGzP99UmdEl54hZzwjXv1+5YNi+TrmF/a18p/MoxryJkewJW+pd9W+K1LVnTeKmMeE+o5hIUqFOwuWjNt8v0a94WtstEbEq/1olYvVx6b+dEs76WcXSLX1eEEEIIebGgjCWEEEIIIYQQQshzw3brcH4+4uRkgLX1vLA65VrPFSsSJSVidWpNp2it9ZB5W0O54DKJlydipU+SN3ru19BetyVqSRP61ctWn0SectX7RZD2ErTo9k3liMvtJFHza9Jj5e36mDwdqyVtLz1bPsZqTzwujnoN8qtMR7b3z7XXArbc7qVnH1/CtvrNida0f4l8Lccox6/3zydk8/MtWbblZ3pfI+ubtnVJ43wc5/R1Y5a5PvoaPv30Evfu7bDfszwxIYQQ8jJCGUsIIYQQQgghhJDnhrt3d7h/f4/NZpjmiw2C0Nok+YbBIIi5EFe11k998qXMHSv9BGuBcUxzxspfKZiAOlWap1/LNGq7rZ2MzeeRTY8xLVtpWedM9pj1+PncsOU6IBJNzymbrqucRzbvkyRtOVYSrLpkcZ2O1aWNa3mZC9ZSQMc91XFPgrY47+0r22qR3JKy7T7HlCs+LFjzY2pB217vS9h2enaZhBU5CmA2GRv6pCRsWWZYJ2XLNknISkI1zRmr9z1OeeJc6Mp17fcO3/rWp/jhDx/Fx0gIIYSQlwvKWEIIIYQQQgghhDxXBLHisNs5rNcWxqSyopKG1WV5hyHtq5e50AzJWp+lbq2V+WgxpebyNhE1IZFbJ2RlPN0m6VqdwG0nYdOcs+k8aY7ZPM0qbXlSt+7TWsfM9tI+S9pb+8r9vT513zo9e8W6xLPUAm0uDVkeOydfw3Zb5payL1+2RGF+vjmB2Be17VSsXu+L13QuKTl8WMTmf/l4rbRrLm7zFKzsk/by+JSM1SJWt/XvUUuU9xLD6fwffXSBTz+9xP37O4pYQggh5CWGMpYQQgghhBBCCCHPHdutwzCMWK9DqlUSsiJgtcSStFsQn8jELZCnRzHNETuOfkrG9gRKXdIXQJS0eo5Vkbj5vKtBAgdpqqVwnYiVa5Tyy1q6ymMMc+fWc8WW7a0+dVK2nYQtyxPLtlxfOq5OzErytUzNpj4mezzpPuk2/fjz9rTvyQmvnoBtCddWfy3x+vvb8jWsLxWwS8oXzyVh0xgtQdsSoYfSsksSsvpHEuX+MgnbSsz25pFtzR1bylhJy/buX4m+H7mI1dcGfPe79/CHf3i3PQghhBBCXhooYwkhhBBCCCGEEPJc4T3w8OEeu53DZjMACDJ2GGwUrsY4GBNEbSlfU59axqa2JEjDdpqX1jWmfUxlgtN2StAaWAsgplx9FKt2qpCcb+s+eUJX0rRJkOZz5krKNiRxUbWX60mCikwzhdRM+0vZqkWrenbic6QFa94u16CFbHou5Lx6fxwlE+71Pn3vH5+2NO2dt91WC9RWn1y66vX6GpYL11a/voSVMXoStp7btS1Vgb6wbQnZdopWi9dS0PbKE/ekbJ6YLUVsmYhtMV+eOL9fMqbIX0IIIYQQylhCCCGEEEIIIYQ8dzx8OOLy0uGVVxyGARhHC8BhGIJMHEdAhImIoVa5Yj1HK5AkbEq4AnlqNc3HGsRLkpetMsVBxOqxEMVqq1xxKGkcxpbSxknO6jQtsn1l+WEtYQFM8rVVYjhJVr1vTsrm27pNH6fHKsVsfkw9Xmt/6teStNmRM8L2GJaItJZ0bR2bb/fla9iuBW4pUvN9tVCdO/awhM3Fak/QttOweV/5cUI/9VofW/bRZYpr+bpUypYiNklT2V8+F737mUvqdnniIGMdSxMTQgghBABlLCGEEEIIIYQQQp5TvAcuL0cAHsNg4b2FtQ4hfRrkbBCpITm73xsMA1SyVIsSN4lESdNiKlOcesi2llZJ5va3U3tdprgsV5z6mEIWt/7y+W7LuXJ16eGyRLEuOZzvS8dpiTpfwhiQeW11Yjbco/xc8ryFvkat+2If4mMQtBzW0qwlZmuRuiQpe1iczQnaUri2+i8Rr2m9J20fLxXbl7K9JGy+71BJ4p6E1cuWdJ2XsX3pWiZle4I2JFV9sWwL9f599Y19dRL3hz98iO985z4+/fQShBBCCCGUsYQQQgghhBBCCHku8d5jt3NxjldjHMbRTOWCU5JVUqmpzHASQ7nE1KWMw3FpvlcRSDIPq6zXidg0Pyy6ZYpzIZaSsbpPXyCmvmUiVsbOU7WyH+o4dPZh5jgc2dZv1/PNto/Lj09yd67vHNeXUGwL2WXyNbTNC9he+5LUZlvClgI19Wnv76Vo6xRo3V6XMq5f77WA7ZUrPpykrUWtTqfKMbW01feivr8t2vc//xtHj+3W4bPPtviTP3kwPyAhhBBCXhooYwkhhBBCCCGEEPJc4hxw9+4Om43FZjPAOTulLUMqVkRn6GuQUp8hIWuMQ0jQApKiBSy8r1OoQC1epA1IfWQOWH1M2aaTnHIuaddpU0niHpOITaWJfTZmOx3bTr+mssyI+3WyNU/25uWXdWJW9olc1o87rafkbHnfUl9TiLJ+35K5csY9Dkm5Q/18saMvaHviNew7Rr622urXaV16WPbNydqeqNUp1yVlinsSVsvYQ3PG1vPA1glZ3UeXItZ/PRE7d5/7ZZnTNfzkJxf4vd/7GA8f7kEIIYQQIlDGEkIIIYQQQggh5Lllt/MwxmO/d1OK1U7zNSa5mCdhU/lfKQ0c5mg1MekaxIpRqdaQVi2TgZKSLY/Tc8ameWHTusx7CmjxpZOsPutbtiNLm9ZtQbL2Uq6H068iaVv7k6BNIreUi2mc9PjyUsZa1pbnyqkTtNJeCtr6OGC5WO1xOC3Z7tAWsL65P1/v9WkJ1zDmYZF4qFzxvKQt9/XEbpleLY/pidgkdeeEbesc6OxPc8K2xG1PxM6Ti9jysY2jx4MHO9y9u8VHH1089uuOEEIIIS8WlLGEEEIIIYQQQgh5rnHO49GjPfb7YZKjHpJ4lZSstWYqL+xUQlYEoYX3DsMQ1kOSVeaezZelzCmXOj3qPSbRKwKzXpZ9yzQu0EvIzs8Xq9O45Zit7bo9T77W6dgkboVeYlYkLlC2p2Na++ptLXPbtuu6JGzJ3Hj5Pt9oK/vVfQ4J2VKA5vtq+Voee0jQlqlPvd6SsYfae4K2J2N72ykdmydlZd7XlnCVZKzMCavniC2fh/L927o3wPxjcM7j88+3+O/+ux/j/HykiCWEEEJIBWUsIYQQQgghhBBCnmu8D/M0GmMwjh7W+rg0Ji+VWs4dq5OyxpiYcA1JWR/lriRgRQQG8aPnlpX9qdRvEltGSR0DFKlZkZGSwpXt1LeXji1TrqktjYtO33w9Cc52WvbQtk7B5v10+jVvV8+gGkMfV4vOum2u//XSkq7tfe3+ZZ9DAjat96WhbpubP3ZO3Jb72/taQlK3t/fPS8z8+Na2fq+VcrY8l0hYPSdsaivvXzsRW0v+/LGWfeXa9nuHhw/32G5dPSghhBBCXnooYwkhhBBCCCGEEPJcM47A/fsj9nuPzcbGsqcAJsET0q7WuqmMcJCjQRKW88amZKy1BsY4lUIVsVqWGRaJY2MyM4wRRKqkY1tzx4brSOlX6VemZ2XM9hyy7UStnGNJMracKza0B8HbS8v2luUcsXlCNkmwvFyxyYRXS6yWsna6808sDdujfZ6e3Cu329I1bc+nX/O2vP+ccJXt+pilMrYlVNNYbeFat83NHdubM7aVlJW2lJDNk7HlXLH5/T6cMC4fm15P5wrr+72LKVxCCCGEkBaUsYQQQgghhBBCCHkh2O99LBO6WlmVjA2WRMqUOhek6zAEkTeOutyvpGYx9ZW5Y1M6FsA0ruxDTNDKOKGfmZKzPiZow7GpxK+1dfo1T6iiSOTm6VmdVE370ny45f5ULjhPx4qcnq5Q7Zvbrtva45R9D+1r7e/1W3LMdVFKvWLvgqRlSwL25KA+fq7tahK23J/3bZUqPiRfgXx+5rAMx5SidU7Q5vI1b5Oka5mQ1ZK2FLZL7nV9n+vnrZbHHrudww9/+BCffbbNfgRCCCGEEKKhjCWEEEIIIYQQQsgLwW7n8dlnO9y+7bFeazmIKeUqctVgGBz2e0DmlA3zxgJB1vi4nZKcqT0JTJlPNkmcPBkb2lMitp0YLROqIlXrVGsSsL35YpME9vGx6/2hrUzA6rlWl8wV2587Vq5fz+uq+6b71ErIliWT8/uTe03TaK9lWJkUXsqSlKO8Lg4dW17fIfHa7jMvFkuB2jtuvkxxX7oC6ccEvQRs6FNL2KvK2L6UTeNJIlWnYMvj1Z1cJGLTPagfo74e5zwuLkb83u99gnv3diCEEEII6UEZSwghhBBCCCGEkBcK59IcsgAm+YooKMfRwXs7pUgdjLEqEVtKJRGYSYKWc7fWUjQdnydiocZAQ3DqZGsaU2SutKV9IlW1nBUpm1KwIlhlv0gpudZ8O60DiCI2T9UCdVo2tGmBrcsT67HCerofad0X+9J+fb/kvO32nCTfllrZwxZ2TtTOiddyu5ea1ZL3kIDV48zLxatI2HLbV2PNlSRut7XLEWthe1jGailby1h9jb3n4tC9S48vfxxyjnH0+O537+Ozzy5xeTmCEEIIIWQOylhCCCGEEEIIIYS8UIyjx3Y7xjRsKAUsc7da7Pcew+AwjinVaUxIxxoT0q4pWepjqeHcnYmQRSFD86Sp7NfSFhD5Y6L80WWFRbBKOeJQ/liuqSxTnNDXLfPUyrXJNYsQPSRl9XouWFPqVRKtuk95PWVCNhe96ZgkVsukcS7HZJz0HLTaWxyWrIcoU5b9fS1hO1eKOI13qH1OuJaidmnZ4lqwtvfpuVfn5Kvu258jtpeIzUsQJ0FbS9kyBZvL2PZ9P3QPy/vXWso8sd/+9j385CfnIIQQQgg5BGUsIYQQQgghhBBCXih2O4eHD0VYiiw0k9B0k6gM5YpT4jOUKzYmlSe2FlHOhm0PY0JMVZcyBtwkfFOKtS5TnCdiwzmgxq7LFev1UjSW5YfLMsVJFJf90jlLOayTu+V6ul5dTjmVMZZzyf0Oyzwh25Oz+phyLC3SSlGrH2PZd47yXi497vAxffGXb/eTmj1pu3zu2FxGzknWer/P2tui9tg5Y9vCVqdj8759AVuWJdZzxMr2nIidv3c+69cupRzO9e1v38OHH57j3r0tCCGEEEKWQBlLCCGEEEIIIYSQF4pxDOnYzcZhtTJYrQyMsVHkjKOf+oVlkKwe4whVJliWUNt5yjRIVBPLrBojKdaQdJWxZQxJq6bSu2XJ2XB8ELtSarjelqUWrDJ23jecQ8tZPSesLitsoqFMEltLw3x/LlZF3CbS+On49Hjr8sR6HNk2lTTFbLp1+eSwV5Gv+hp6x7fae/I13z5GGub9+ynPtoRNbfn+Urjq9bpPS8jWAjNPwYZz53L2+Lli6/a0PXdP8/veuqdz9wrxc2O/9/jww3N897v324MTQgghhDSgjCWEEEIIIYQQQsgLycXFiP0+WKDNRqSljbJUErOCrA9DmmtWkrKSfvXeYBjkOAPn9Haap1ZEZUjgyvhJTLaXfjpHmitW95Ht+i+VH5a+rT+5rrk22dbtZapXl10u+7eW9Xqe3M375KlaTS1njWp/LMN6FFr4tdvbffsytk5mlu2H5Wt+jlKqyjE9WdsTsEBbtgKIP0Jo7S/ngW3tLyVsr1yxpF+1hA0/rLhaWeL+PamvVcvg7373Pr797Xu4e5eJWEIIIYQcB2UsIYQQQgghhBBCXkhE2Oz3QaSuVh5Amit2HEXQ+riekq0inLSEDMJWUn46kSrzuuZJVRP3AYiitU42SqnftF+36e16zli9RNGWxpYyyu2+rXV09rX2H9Om2w/ta+2f69uiPn55meL2jn7isj42F6tl/56ArY8/RsJqQVoeU/ev+9aCtp+EBfL5lFNCtT/faqu9TsEiE63peFnX+/J73n9OyntZ3/NczIb1/d7h4mLEZ59t8eGHnCOWEEIIIcdDGUsIIYQQQgghhJAXEueC3Ly4cFG2rlZ22pcvg3wxqk3miJXSv3bqE8RuSM+a6Rxm6pOSsc4Bw4BYdjgJJUnk9hKySepKqV6drO2lX8u5amWZz+96OBE7n5At54X1xb48Mav7l+NNa6pkci1JdYK2tb/dfkjuXpW2OM16zMjW9v4wblvYLps79pB8le1e/2UJWZGu9bEtQVvK13BsX8r2JGw7IZuP27qH5b1emiKu5XA4z49+9Aj/w//wES4vRxBCCCGEXAXKWEIIIYQQQgghhLyweJ8SsPu9B+AmkeoxDEHujKOPc7uOIwDIHJRpPtgyIetckIXWBmkqydiwHc7tnKRhRSBJ+WPpo5OyJlvqfTJHrN6XL+V6UIyDRn8cWMcR+9Jj0KWC6/levbq2vD3RMq2l9ZxLyfaOuX76MnaJfAXSfar7tUvp9o45XJY47Uv925L2UGliLWXbwlb3y8VtuzxxX+L2U7O6Xd/L3n0vn5PymNZ9lOVu53D37haffnqJe/d2jXEIIYQQQpZBGUsIIYQQQgghhJAXmstLj93Ow9oR63VIpg6Dh0hWkTwiSyUtOwySTA2p1yBWQ0I2lCGWNhf7SBpXyhdLGlaEZRJKQEq/1knZcq5YoN5uzSHbGucqc8YuWV+yXLYvSeVeqlb6LEvHPjn6IjaudfvqPrkw1Ou+0ZYf05Ktve2erG1JV91vXszOtfWSsXlbS9C2krFlSlZfV/s+9e9l6z72rnscPT777BJ/42/8GNstE7GEEEIIeTwoYwkhhBBCCCGEEPLC4xyw2wXRslo5AHZKzHpY6yaxGdpE0gIpxZrK/vpKKlqbkqlpDlk5cxKN5b5QjhjQgkjStGWbCFa9LUud0M2XKZkrpY/La6rXMW2n9SB2DydkE+UxyPrq5KxI6Ryj9uXH1rT6Phly2edn9rX7HhKwur0UrK22x5Wwuu8h+QosKVOsf9iA7JySeK2lrF72pWxLEM/f//qe90Rsfp2Iafn93mG7HadEPSGEEELI1aGMJYQQQgghhBBCyEvBxYXDMIS5XIN0FQEU9ovQS2LVqKWdhKyD9wbDkOaaBZKslfFayVj5ayVFpV2OK9ta23OJ2HLZTsSaSi5fV0J26b56XcvhWrCmJK3gs3v4pKnP0ZJ+7WtpidW03kpxtqXiMYnPcn8tYXNR25szVotQEa29+WBzKevjMX0JG/qMY0vGppLHS+7zUnndE8rjGM652znsdg6EEEIIIdcBZSwhhBBCCCGEEPKEOX3jFKdvngIGMOH/LUPkAXwexPNFm0iU4DTgRge3czj/8Bx+ZKpL4xyw3To456e5Y8vSv6Hc8H5vMAw6GStpzzgSANuVnnO0pGdYplK8Om0r+yT1GvqISDVTujckYNMSaCVlSwGb2gARdjpFm1KsaV9Y+nhd4fF7dR/y1Gx+nB4LQDbnbBpHyMdLQrC8x1rK1eWNjyV/z8xJ3rZ0zcdoC8TDpYp1+1IB2zumFLJLJSxwbCK2LWj1dr5e9vHVseW9WnrP8seeL+vrDtvj6LHdjvje9+7j88+3KuFOCCGEEHJ1KGMJIYQQQgghhJAnzI13b+D1P/c6jA3zg0Y/1PNEpWTV6+VSZIZLf27rsLu/w+XHlxhHzneo8d7j4mLEamWxWtkoaEQkBpkp87gG0Shzx4ZlOGYYgCCJfEzNjmOeVK3Pnc51aBmuJRe0mlbStegBY3xW0ljapLRxLWXT+bSU1eJUjkmCVSdrRW6LqE3tWtamNsTHVj4P8uYQ0Vs+9rwp9dX3edpq3ZwrUUrWel9r21f7y+srZWFYb8nXeeEqy7ytl4jN983NGZtKDvclbEu6tuSsLjncK0dcy9hD97onYvP2+dSvn8oTOzx6tMfv//7nePRoX5+cEEIIIeQKUMYSQgghhBBCCCFX4NbP3sLN927C2CBXY3qvIVpP3zjF+ua6am+iRaxKv+rka1fKurDtNg7DZsDrf/51+L1v9vcuHOP2DuN2xMPvP4R/SeZG9N7COeDiYsR6nQsbaw2cM4WURZSSgIsliqVkcZCeHoCdZKWdhGQYQ4RVmne2Xsr+JGHzPuH6TBxXp2Lnkrgy/6w+97Fli2WfiNM5MZs/rraclWP1Y497VKq23peeJ/1GSine/n0ox2ix5Pi6X1sEtrd70jCNc0i+ttqWliTW60vkbClEtVgNy3CMTreWy3ZCtp2IDb8bSWWJH/d+lvdE1nMBG5b7fZgj9h/8g7v47LNLbLf8EQshhBBCrg/KWEIIIYQQQgghpIdBJk+N2rjx9g186c98CWYwuZCdjjm2HLFPNYmzZSsZC0ziFarNJxlrvYU/9Xjl1iuZeNXpWT96uNFhvBixf7TH+U/O+ynaF8rRhifFOY/dLjxeSb7KOmCx3zt4b2GtA2Bi2V9jLKREsRwzjm6SuEEgjqPPUq2CPJW9MsV6PU/EpoRqa4x07nIJlYxNAjRIYpG5aCRk89LDeQI2l9P1/nSPyxdOXfI4idVWmeL0WE11/3r3tU1Z5vgQ7U69Y+v2fgnisL1MIur9h0sQH5K0x0nYvE8/CbtMvtbbvYRsS8I+zj2Uc+v1WsgGEXt5OeKDDx7gk08u6wsghBBCCHkMKGMJIYQQQgghhJAGdm3x1l9+C5tXNjBDLluNMdi8usHq5iprA3C4BLHg6/VyHlhgRsZqCTutx6WIWqe2p6UuaTy4AcNmwOrGCm/+yptwowtzzHoEWbt3cHuHRz96hO1n26vfzGcQ74FxFFm6h3NDJlMBYBhk20zbIgUtvA+p2GEwk7QFADe9DhycM/A+SFprQ1njlGrVJXjTUq8H2amlqCn2pwRtkqKSvvVTIhYHE7GlFG5tz69radxO/ep+9b78eKGVom3vr0njCo/za4K2HIx7u+JwThbW4+brpVTN+y8VtL1yxT1BKWPMiVcAB5Owoc+xydhcsPbv7XIJqx+zXFN5bePosN97/P2//zl+8pNz3Lu3q09KCCGEEPKYUMYSQgghhBBCCHnpMYOBXdusbTgdcPMrN3H6+insygbhqud8bQlY6DKrfTLpoFaNN7WQVetmSjTG9qKf8Snx6CECz8PY6Tg39ZlkLHx67MNmCHJkTKlZtw1/27tb7B+k+ROj9B0fR3I9bUwUMrsdYG0QqcPgATisViEhK2JWlkEM5slYkYV6flYtLEOflDhN87aGfVKe2LmUcg1lkoEglPISwCJYw3mCrJRyxCEBiyopm14ssm4gskquS86VkrJ5wjWVSZZ1IHsBV4lYLZDr5KxI57Sux0AcK+3Ln7veW02P+zjMSdi0f14g5tvzqde8rde/LWV7SdB6/6GyxLq9vb+dLq239TgtOZsnYvv3sLxHZZ/+fcn3548/nH+7dbi8dPj44wt8+OF5fWJCCCGEkGuAMpYQQgghhBBCyEvPnT91B2/+6ptBtqq/zasb2JUNZYiBvBTxtI24WovZiEgBSQbC5A7HI0pYvc97n7anPsaH7VZi1vtivyzdNL5T7SolK2I1K2k8eriTkJR97Rdfw/j1Mcw/6zzGbShtfO8f3nvOhSymBKvH5aXHfr+H9x6rVbCdq1V4bCH9mpbOmUk+mZiWlTlmgxS1U7LVwlo/HWeyPiIMh0HmqAWs9XG+WhGyxhhY207GJrGryw7r8sN5n7k2IG8P23q9TMQGodtLzrb65PvqtnK9vy0yud3nEGX/Q9K1pBSsvTFyYVgL17ReytM0dku8yvYhOVvvz4WqHielXnX7svLEraTsofLEckz7ntb3bO7ezd+j8trD3LQhEevwR390D9/97n08erQHIYQQQsiTgjKWEEIIIYQQQsjLgwE2d6ayw9M2AJy9eYazt87i/K86AdudB7aSRG0j5L1H8rTTinZJHvDG54JWxKoxsW8UrXpdjvVJ2GopG5fw2bm15I1pW0ypWePhTbhm6yy8Dcna4WxIMvZyhBkM1rfWcHuXyV+3c8+doPXeRAm133sADuMY5oYVMTuODsaEpKxIy5D+A9L8qZJOTWI0l5H5PK6AmdKwIqpkbtpwXJJNKcUKpESstfJCkkRsSsjmCVh02/pJ2DrlKteQXut6n7xmgVReWd+bfHzdltb1mMjGTs+VFrJlW/uYkmPla3muJWMuSXL22mspe5yErdfbglL360nX8NoKfXWZX1keErPtRGx+Pe372b9/5f0o70H5OPW17Pce9+/vMI4Ou53D3btb3L37YpVgJ4QQQsizB2UsIYQQQgghhJCXhuF0wHv/9Hs4ee0Edm2DMJMyvScDYPPka5mCzWRsiW73utlU7T6thD4+ySxpF1kaE7OqhLHxhUD1eR/jpvHKuWNtkrSSlvXOx9LFOjELh1jOOP6tQh+7tlidrbC5vYlzy47bEePFiEc/eoTLjy+PfGaePpJ2BUbspmkjV6tQingYRMgGoeOcxWolydeUjA3LIH/yFKyZ5o5NiVjv7WwyVsSqlRLTMHGO2LDUIjUt64SsTsDWbUCaXzZPv/aSs2lf3jafmtXLVt/efhHDrd869BOx6foel1Y6M+2ba+vJ1npfT7qm5bEJ2cMitpaxrdSsiNQwvl7OzRlbyte0nZ+7vnf1jkMSuydhAVTX65zHRx9d4G/+zQ8xjuFa9nvXvhhCCCGEkGuEMpYQQgghhBBCyAvNyesn2NzeACbI2JPXTrB5ZVOnYEXEAnkidtoOixkZqyn7iDAwSaBmu80kU4skLAzqNOzUP6ZhJezoVR+LPA0ruNRXjtPJ2PjY3SR+p0Sw3A9vgpS1CIlZWMCPHmZvYFZTaedXNpnI3T/cP1dJ2SBcg6TxPghZ791UjthOJYNTOlaOSZI1tfckZBKiucwMwtXHuWPT3Jsmjg2IgErzwdZzxvp4vF6GY1ObyNYwThpbi9qUXK3nl5VrTonYVmo29JFzL503Nq2HsfLt1Ke8v/EI9Zo+1svO+NdGH99oa233Eq9pjFLAttpykVqOW4tW6deXsm1Ru6RE8XwyttendS/nJez8PWk/hrzdY7dz+OijC3z88QXOz/cx7UsIIYQQ8kVAGUsIIYQQQggh5IXmjb/wBl7/R16PslDmgDXW5NJVJ191Mla4atAu85yF+BSZpFKv2TF6qdaz/kqoxjStrE/p1+Y8srJ0+XaWkC3W47yy45SoHUOi1q4s/MZjdboKpYu/4uC2DuPliLt//y72z9F8jLudi8/RauVgDFSpYpnvMqRjvQfGMUhBnYwNy5SIdS7MLSttIcGakrKhTLGJqVhrEeeMlbLFes5YKW2cJ2LzZKsWUnliNu2TMsdyzXo+WUmjtuaNba1fZXmoben2ofbroi1pfbO9J1z1Pi105wSjHmNuu5eW7UlXOc8xArYvY9vJWClpnB5733T3ZfYSCasfb74cR48HD3b4W3/rJ3j0aN95HgkhhBBCnhyUsYQQQgghhJBr5733buL9929nX+Br6i9cW19SLymz2P/y9+7dLX7wg4dP5PGRZ5PhbMDtr90OsnVIc77efPsmhtMhtkUJq2UsUK3HNm1hj5E98rI2yERrTL/KPu+zBGw2D6ykXo0a74CozdqMEsA+tWfzyRqflzqexJ3cI+/UupmkS7m0k6y1U191r09/5hTjxQjvPNylw/bzZ3t+RmstvPdRvA5DSMgOg8hWgxAxDp9tQbJikquAlA4uxVEuQUthKp+V6bOwTIHqtG0uRFOaVkoY63NKUraVxpV9On2rk7tybdJezi8bBHBKrcp+fd3TI4z3Iolj3abvQfq812ldkcNp2+PQvy3lPTxMKQ5nenb2tf49y7d9ta8nYMu2tpBd9u9l2U8k6SH5Kn3n/u1tzRGrjy/vS33Pem3L7lVbxCIm3L///Ye4e3eL7dZRxBJCCCHkqUAZSwghhBBCCLl2/vSffg3/0r/0fkxhyRf3ifqL4/oLXSghgimJlv/J/I37fVjudg7j6LDdOvzDf3iXMvYlY3Nng3f+yjtYna2CfLUmL0VcJGEPSdiwMI+diI3lgn1jn1dzW+q2sj/q9Gw1p6xOxeo5aH06Vidko5B1aZ5ZnZTN1iUhO0zSdQj7nHWhfUrKutHBDCaULrYGdmNx+/3b8KPHuB2x/XyL7d1t9dieJZKMHTGOHsAe42hjOV8AMRUb1kOUehgAwE9pU0nGWgxDEkaSQJUSqUFIWpVENfEzUK+nNG0uXMv1VCo43x+uJ8lJ3Rauo/5rtUub3pfWwy8G5tOwuaxdnorNx673tyh/DHTci07LwP7+XlstEfV2T9guS8PW8jWJyNZ+va9Oysq8qoekrPw7HI7pzwer981x7P3rCevy8en5bMfRY7t1+Ht/71N89tmz/SMQQgghhLzYUMYSQgghhBBCjuIv/sU38bWv3cYgyTejv2QPfd5//zZu3FjBNL4lb30hPJe4kS95W1JWZOxqJUsD5yzWa4ef/dlb+Kt/9d3i+CBuLy9H/OEf3sXl5fgF3jly3Rhr8NovvYbNnQ3s2mJ9e43N7bBu16EUscwD2yxJrGWslq6VDDrOxkYJoYWuV+N6dOeOjSWHdcnicl+0EGn8mHxFSr1m6z7vlyVjrUrIIiRhjTcx6SrrWbniaRt2ejxTMtZYEwTtkFK1xiY5i1cQ5awbHXZ3d9jd3x11f78o8oSsgzFjliYUQrrUTFI2SNgwZ6v0NVOqNpQmTvLVTjLUZVUERL6KKJXPyp5oFXGrZWu4jvYxvba2eNWJXJ2KbUtbWZf+c3I2redz0Jb7wz2r9+VjxC21nuaxvSr1sW2J2msrhWQpUMtj5uRrub1ExvYEbE+8Ajox66OoDYKz9+90W+QeujftfcdJ2PRY8muT/33wve/dxyefXOL8nP/WE0IIIeTpQhlLCCGEEEII6dL60vzP/bnX8eu//jY2GwtrTVG6s/1l+ZIvT9OXqOUy7BsGPVdjT8paOOexXnucnKzwMz9zhnH02O8d9nuHcQwi9sGDPf74jx9gu62/oH2cL+7JF4tZGbzyC6/g5pdvYnW2gh1smBfWhESslNbNkrAiYhvytUrHZiebuZDiNVMmXeV4LWmjhM3dUfs8/sB6IVqrfR0hW/3p+zGJVknLtkoXe+9hjQU84IwLfa1PpY0tkoydSh2vV2sMJwPcPswp+3B8+EzK2CAezfQDjn0mqOSzTvDewlo3SVhMP1SRssZ+SsxKX0DkqzEOujQvkJKq0ldkqe7Tkp66hHF5TP1XlyvO2+qSyPElHcfOx9Dlicv94RgTxaC+trKks5an+phynz5P/pmdp2ev8/O8N1ZPuObbfck4J1+lrS1pl6Zhe/v6Mra3X0tY3Vek7JL71e5z/P1pPVYtYsfR40/+5AF++MNHhy+EEEIIIeQJQxlLCCGEEEIIafLlL9/Eb/3W17DZWAyDnVJWBj/3c3fwyiubKB60mNBf2GuWSFjZVyZttHyV/WVadhwdnAtL78MccWHbYBw91msbZexqZbHZDPjN33wH2+049fXY7Tx2uxHbbZhf7qOPLr6oW02uwJu/+iZuffUWbr13C6ubKwzrAeVcpVVJ4kYiVpZxXtiedO21zwnU6GQnmVSmYHt4dT2oZU8cxyMvW6zKGPfKFJepWCAI07j0CPO+eiSh6n2Wlo0JWT/NEeuUoJ1KGHvnYcbQJqWMpY8bHNzo4NYON969gc0rG7idg9s5nP/kHO7SLbtPXxDDMADwUwl0wPs9Npth+iwa1Nyy4X6Oo5k+lwxWq5CclT8pXazLEweZK2lY+UNs03OsSkUCEeTOhX66rUy/pj89b6x8fofHqNv0etjXSsGm8trz6dg0fkrAzqVk6x/0HNrutS3ZN8chmagla6t/KYlborFVrrgtXvN/R2W7J2cPzRlb/rsLHE7Btn40VZ7r8P1K9+PwfTtWwiL+74Lvfvc+fvjDh/j008v5iyKEEEII+YKgjCWEEEIIIYRETk6GKBTefPMUf+kvvYUbN1ZYrewkB8rSxEpsKeovk5MYSl+ehnYpySn7JC0VkjciDnTCKk9UhX52EhI2bjsXtq0NX86GpFoQCKuVxfvv38Y4Oux2Dvu9x3Y74uJixPn5HnfvbvH559v4JfN+vyDiQ54oxhqYlYni9OZXbuLVb74Ku7JpblgtYSWpLT8W0EJWtsv5YGNXU7X1LwzRK3glGHT7QdGrOSRx9X5fzC3r1f5J1GZzxJZzykqf8sJcPg6cWi+X8riKP2MMHFy8/957WNgofiWxbO4YrG6sMF6MYU7Zz7bwex8l8tOeW1YSss65SVa5+Dkkn4kA4ufmfu8wDCJdAWPk5qXI6ziGFO04+ulHLrmoE6StLUWlFHKY87Ns08fKX7jW/MmTuWvD9Xp13UnchmvRJYp1n7RPrjmJ4XgX5RFln/FhmfqlY+Xac8lXJmHDdqtP+z7m19Jj/gU39zzpMdoJz7Z81e1LBKxslwK23n+MhO1L15ag1cnYQ2i52m5fdp9K+Vw/nlAFY7t1+PjjC3z/+5wznhBCCCHPDsar/4Wz2Wye5rUQQgghhBBCniInJwP+nX/nF/HVr4b5YM/OVnjvvZsxqZXKV4b+5ZferS+fyy+SdanP8kvhsjSipF91Aqdsa5Us1mWL9TKkZ0MC1jmvErEhMRukbPgi9/79LR492mO7dbi4GPGtb32Khw/3T/L2kwPc+fodvP0bb8OsDOxgcfKlE6xvrlM54sHAIMnYQ2WJ5+aIBdo/MmjRmhvxsSmHLGWvL9q9729PwjWuq30iPLX4LNu894BT22UytjWXrLxnp8SoyFW/D/vd6MK+0YdyxfuQinV7h929HdzOYf9oj/35Hhc/uXjqQhZI9805B++TcD07W2G9tjg5GabUfagkIGXc12uLYTDTX2hbrcxU4j38yCUtg/iV8u/GmFjyWH4IU25LxQJZlm2A3lcvRSZL37xscfmZX/8Qp72ejyX70752WylvW6nZ8ti8bckzOd9vydu512euTK+Iz157LWFb7aWM7IvZ46SsvLbbQrYlYcvrbdyNxv1p9CpE7ZyMbj1Guabwb7rH97//AH/4h3fx6NEeFxecJ5YQQgghT5ftdhvXmYwlhBBCCCHkJeTsbMCdO5vsS/OzswHf+MYreP/9O1itkhTIv5g/PHb+JarJvkSVL9nzVEv4k+SrJHAE3a5TWOEYMyVfg0wIyzAXo3OyFLlgp3KiknCzUQBbG06YJMYGN2+ucXGxx/n5iNu31/ExhXlnn60yqi8kFkG2TnL19M1T3HzvZpoXVpKwem5YmDDPaUvCzgjYpnhdGKBbKm1nxy0lhUq+6u1snlkPeBNEqDEmvr2kVG22Lcl000jESkjSp75x6Xy8pzpdK9elyyVn124Q0rSYlkM6RxjOwhsPBwcLmx6fNcAtwO1dOKdFnF/WP+V0ehKHZiqBHj479ns3pe4tADclZB32e4NhSJ8tgbA+jvKZN93HmIw1sb9OtLaTseUyT66mssB++iz03WSsMYh98hRrPo+siLAkXtO2XpfHpvtKAjb1L9vCZ7vcjxJJ0qbtfG7Y0JY/Vz2u+vuJ3g8vyuaeYNTrj1ueWLavKmEBSUXPzw/bFrTVHWjel979qdvmSzjr+1le437v8OjRPs4N//nnW5YmJoQQQsgzCZOxhBBCCCGEvIT84//4O/hX/9VvYLOxWK1Scuu1106wXttMwgKlhDXxC3T9JWzrC+X6i2D9JXD9xbFeb6Vf0hfEeVI2zR2rt8tkbErPlMnYlJB109yxkpIdsd97PHiww27ncHGxxyefXOLv/t1Pr/yFPlnG+s4aX/uffA3r22sMJwOGkyEmYY0tyhGrFGxPxAJK0pSuRvq20M1LnvOeB1ood3vtem7Ysn83CYtiW95nWnYU7YdSsjoJ203KlsnYMfRxowNcWHrnYzpWErLeeYzbEX70GC9HuJ3D7uEO28+2ePSDRwdu4BeDTsg6pxOyA4bB4vQ0T8hKMlZ/zoay7yb+6GW1spPQTanYNHdsnojV6dmUkEWzgkHep5eMBSQ9e9Vk7JIk7OHtdI/b/+6k8+TbrT4tjvzRROcNeVgs1tI2F409Qft4Elb2z8nXOenaXpYCdvk9me9Tl+XuP+b8ceprHEePu3e3+Jt/8yfYbh1CmeLw7zchhBBCyLMAk7GEEEIIIYS8ZGw2Fl/72u0oAb7xjTt4550bWK+TjA1f5udfwgPll+RpPXxJaopt+TI1T8SmOWPDn4wT2uXL1Xw9/0I4JWTDepqTMCVl8+0wZ6zMF+tVW56MBYLETY8vrNtpisdhcADW2O8d1muD3c7j9ddP4xyS5+d7JmWvEwOcvXWG09dPYyni4WRI88KKiJXXqp4bdqmELeXrUk9zqF88TUf6Lhlby1dp9+oxTX0kFavP50PHkFidjtNzwmbJWJ+Ssd759J7S+2dSstm1Tm/+mMBFSuPqhKxcVzVXrzyGEbArC2+D1DXTPKZu67C+vY5lkN02lDl+GuiEbFg3U0IvpO73ewvv3TT3qps+UyzC3LF2OjbNnRoS/S6KU418ppYp2VJgWmviZ5mMKZ+nklotj0/zyubp2WlkyOf20mRsPrcs4udx9cIuttNne3r8OjGr77n8e5L66ftgsrb0fMW7iavQF419oZhv9+Wrbl8iI2W7JWlbEhZozQerf1Agy9S3nYJtz2l8iPqY3r3oP/aWSJYfWH366SU+/3yLhw/3FLCEEEIIeeahjCWEEEIIIeQl4M03z/Af/od/Fl/60glOTwdsNgNOToYoYPPkVF3msScJ0rr+Mtgg//K2FrZhvf2Fcvklcm8u2bSeErF6uz+PbJCwq5WbErI2ljiUpayv1yPG0WO1kqXByckKr7yyxm7ncHk54jvfuY8PPnh49SeHZNiNxVf+ma/gxjs3sDpbBQGrJOxsIhbI07GyXcrRzMNeQZxqGom+3v5ZSokU5ZJv9jHeJGHrizav+qql3i+iVV9jbFP749KEY42b+sjjmiRrOXesXpfUrLU2yNlQ0RfeerghiFUzmiRgZTl6mMFgOBmweWWD8WKM88ju7u0W3tgnQ0irDtM6sN26KBCHIdyjYbAqmW+xWnkMQxCnQaDmc8g6l5Kxsi5LkZWyneaM9dl6mjPWx8/isD8tQ6nlfJnKI6c+WsRKW+jjs38n6n3phzypFHPar7elXxLHug3ZMUAtrPuyte57VXqp0JagTG3t0rth3Vftjy9h56WsFrGlbO3NC7uUOWHd2n9IwOaPO11n+vc9zOX+3//3P8W9e7v4YypCCCGEkGcZylhCCCGEEEJeYNZri1/5lTfw1a/exhtvnOLWrfUkYcMch7qUZVmOUmiJ2Dwh61GnkkzxBWwrEZPLJp2GlbSWPr/MJWttEBKt+QZFygJpDtn02MI8soCb5k6UL53dJC/sJDMkORbGSSWQHbwfMAyhfbcbMQwGX/rSCfZ7j3EMAvfzz7fYP+X5LZ83zt46w8nrJ7CDhd1YnLx6gtXZCnZlAQvYwUYBO1uWuCVhCwHbSmVGjpA3mXgtj9PnWjwgKrkaz5NcVNg9yVA9h2zWFhxcSqhO6/oYnZKNc8KKEHPTfmdistbbScxan88Vq5KyMW2L9L6O90kEscnvi4WFR5g/Fgaw3iaZGxPv+T1Y3wlzCe8e7J7aXLK1eA/b4XPCYLczap5r6WOnzytJlLpJstpCRiEKKN2mE6HhszDcUPlc1H2kn07RSr9e0hYIUlaOy0WsHkv+zSgFLqDTtGGcUsKG8+jt1md+esxGreeCM7+3SQBPj35GEl6dQ/+uldu9ksVtCRnGrF8LfSlbli5O24dkbEvMtq/1ONr3pGzTgnvuselrlR9WffjhOe7d2+HiYqSIJYQQQshzA2UsIYQQQgghLzBnZyv8W//WN/H1r7+CzcbG1FWSsPWcf0AtYIVe0idxSD7Nz4unv4xNJRTrL4ylnGJa+ignQiIt7AtLScQGgWttWqaEWjhG0q8hLRvmgxxH6eMwDKF9tTLY7cJ8kF/96i28/fYZzs/3uLgY8fu//zkePNgfemqI4tVvvoo3f/VNDKcD7NrCri2MNUnCTvNkSmniXiK22kZbyArNJGsPk683E7Xl+HVDohQ4qpxwa3/W5mfa5pa+sQ2kUsJKmGZLj5Salb6yz6W+xptQPlmtR0ns0rY1Ns4tCxv2wSIkaM2UrDVhGxbwq5CQtSuL4SS8RsbLEQ+++wD7/bPxXjMmWOndzkWhOQzhBxw6qS+p2fA5ZKellBpOqVgpHZ8nY02UuSk9Gz73pF9KwKpEs5KrIoglPSsiVcoUh6WWsb74d0LatGjVUjf1l771vy15erbVT/ct2/VxaVtesL3nZ/lzCRwSkW3Re0g26vXrSMe2/u0sBe6ShKw7usJvfe3NXpWkbj3mfJ9cZ6syxn4fKlf8wR98jp/85PzYiyaEEEIIeapQxhJCCCGEEPKC8Rf/4pv45jdfxTBYnJ4OePvtGzg5CXPDphKWRiVigfaX4DVzAvbQl92SfgrrPmvLv1iupan3+ZeyQXaYqc1E0SHJrFSeGNNfmlcWCGk0WQZB4SeJIZLBT0kzM80ZK2lcYBzDY0gy20wpY4PVyuLLX76B8/MRl5cjLi8dPv98O39jXmJuvH0Dd/7UHdz+2m0MpwOGzQCzChJW5oiNidiWjAUyMRu3OwK2kq9LBE1L6urztMY5Yty0qRKk5X6PmGwFkCdTDaJ008fHlKovxvSqv6xbday0eXWsbtPozwOH/Dyu6GPybWMmOTv9WVh44+G8i9fjRw9JyMYxLDD4MIfw6VunGM9H7B6FhOz4aMTTRz4nwjXvdk7JL4/VKkRYJZ2vl+Gzz8BaG39cIqWOg7gVMZnSsEGmmTgHbEqkeuTzt+alimWfJGr1specPdR+aD0t68oHMidt3Vfd2dl/o/SctzPPzqJ/p5b08Y221hjtcsU98Vq2lcI1l7Ptcr75cf0krD7vkse9hLakXiph62t0zuOjjy7w4YePpn/LPR48eLolygkhhBBCrgJlLCGEEEIIIS8Yf/kvv4Xf+q33sdlYDIPFaiXCMC9HXKZigeO+qC6/XJ87Ph2XyhDXX8bmZYpDMtYgpV7TF82pTHEqVywJV+89xjFtl6JWp8PCOWyUseMoY4m0cJN8letB7DMMFtaGUsXrdSh1vNlYvPfeTWy3DvfubXH//g737m2vkDx6Objx7g2884+/E9KwKwuzMlHCGjPJWDRkLFBJ2FLIxjbkbRk9IVrub4nXOck7d84eLQmL6f0h5y+lqggtJWe9CQIzytayPLGSqpl0BYJAFSfWkbLVNRvV7ov23j6o/ciXFiE1a6YkKAB46+PzbayBgYEbHE7fOoXbOuBjYDwfMZ6P9TV+ocg1+6lccbgYmas6fHaFz7Bhem3LUpKy4bPOTVLWwHspq+7iD0LSZ2EuXPP0bC1ltbCVv7xvvb9MyQImK4m8RL7O7S/71vtqSVsep19oxyZgj6UUrXVbKWZrEanbe1K0LV/Tdi1q5bWWxs8FbN52HfSGKWV1LaP7pZjLxK5zHj/5yTm+9a3PruWaCSGEEEKeFpSxhBBCCCGEPOf83M/dwb/wL/wsTk4GrNcWv/RLX8LZ2QqrlVFliXUStiwZWZeALKm/HO99MV4OVH5Z3ZOwoa+sSyIWOFSmWCdkEWVsKNuJTMaGNg+ZnzElxtL+UKoY2O/Deph31itxiyh2w36DYRjj/Lv7fdi32zkYA5ycDPAeuH9/h48/vjz8ZL4knL11hrf+0ls4+5kzDCcqDTuYfF5YvYTalpSqSa/HSsDql2JsOiBny75L5W5r/LlztCiFDpRUbfTR87MeLE081ybiFcV4vT8tWIGsTHEmcycpZHyYd1bPaau3pYSxQ0jEOuNC2eLpMVpjQ0LWTFJ2qkosot6tHE5fO8V4cwzli89H7D5/+sm58DkTyqp6bwE47HaIJYtTuWIL50TGhnLqIQ2blykuyxXrcvPpDyr9mtL7ebuf2sIyfJ6lbV26WM8Nm/7daJcyDo859SlLFef/btRlitttSQDX7fpup/PPPyfLnrtDrrIlM2vhGK7rUHs/IZr6tfq05pFtCdpS3C55fMdSytfyHP3H3U/tjqPHJ59c4tvfvofPP+e/nYQQQgh5/qGMJYQQQggh5DnFGGC9tnj33Rv4J/6JL+PGjRVOT1dYrSyGIUnYXiJWxpjWFgnZep6/dHy+HfCxrGnri2ZTfHGMbF3+JK2V5pCbT3TJOO1kVzBGYWwpC5qSZ+H6w9yP4+hVsixJ4dDuoUtiGhPmhrTWwbkVhsFN88/aKHE//fSy+gL+ZcSsDDavbvClX/4Shk2Y/1NL2KaMNfmyFLJAQ8KWgvSAfI2bC+Xu7LhXSeflb5VcBsvrZkq9xuuS9nRQSLKaJF+yhKwaN8pcWfcmzM86tUc5K/unOV+NK67Lqv12GmeaCxZQ16FLK2shDCWCXepnBhMk7VQm3MLCwcHCQhLA3oUSy/6mh92E9p3dYXdvl8okPxVEJgPeO4TUPQC4KEqD0Axl0MOPOwyMsVPqNFjt8Plpq8+N8NljY0I1nQtZarW3LNf15/ZcmWIp3Z7mjNX7U3u+Lv/m+HhO2a+vq9UWPs+D3def7eVxqW/7g1X6Hvu5O9dfC8ZW30NCdkkydl7G9sVrEObpmC9CvurHUm8fmis+/Ru738sc7x737m3xve/dv96LJ4QQQgh5SlDGEkIIIYQQ8pzy7rs38e/9e7+Ed9+9gddeO8FqZbFeBxErSSk9R6xOwZbJ2JL8y3pTrZd95hDxmtb1F7Embpfr6Utaky2lRHGdiO0nY0PiLJU77gldSchKSjb0TcnY1CckY0V67/fhfu/3Ti1DQnYYDE5OBpydDXj0aI9Hj/a4d2+Hi4uXr3bx5tUNvvLPfgWnXzrF6mwFM5hQnlgErLxW5ySskqMtAVvN7ar3Q0nU4jhgZrxW39Z5inNdiZY48UpY+kbfuTZfr3sU5Yml3Scpmh3rgZh4NcjPNwnUKIpd3lf2xSSsPuf0HHrr4UcPb6ckrCuWNuyDRSxZHNOy09KtXZCbJ0Hw7+7usP3s6c7XHD5bBoyjxziOWK3sNGesi3Nhh/T9MJU7D+lVSceGz5fQFub8lvLoiIlW+bwv07I6Gat/kJP+PcjnkA0/SMnTs0m6hnXnpA+QS1ktUkspC4hMTf/+qB8KAEUffVzeXiZf5bi6bf5N2Ps3a15atqVrq21ujtRDYrK3XUrZ1r7WXLBPivn7cFhAl9ftnMfFxYjf/d1PcH6+h/fA5eWzMAc0IYQQQsj1QBlLCCGEEELIc4a1wCuvnODdd2/gz/7Z13HnzgYnJ3YqmWuLL+RN/OI8Jab0F+A1LQFbylt97DIZm6+3lnUyVkvZJFBb/cMyF7z6+mSeRknEyuNwLiTSZFk+rjTHrJ1kBOIyJGzDPI+AyBEbyxrv9wOMMdjvk30K8/cCFxcjdjsfJfKLzHA6wK4sYICT105w+6u3sb65ziSslq+lgK1ErBYzC6RpWO2IUzn2kNTtCdhizENlUpfgOwJFS+Syj+zT7WWb3m7ty9ol4ar66yUACZDH5wZuGsdOKdmp7Gycs1ZStghpVp3qNS6I+ChyZWzkS7km620odezC2NaF96DfhO21W8NtHcx9E/o8td89yOeHV59jSS6GeWVNnCNWErJyM3Qy1hg3vT9c/IyzMX0s63WpX70Mc8ZKSeL+D2ry1GudhNVSNlx3krIyh7fM9VqmY+WHLyJz82uUa9f90uewlpx1UtbEe6ElZOstecxnbq9veZ7Wv3OlwF2eFG219dbbcrbkcT+a5u9D3DooYfU1liL2wYM9PvroAo8e7R/vYgkhhBBCnkEoYwkhhBBCCHnOuHNng//4P/4L+NrXbuP110+xWoXUVD4/bJKoaSkj1CWJZVvmadXH1F/um+q4OfIvZ3NhWs4bWydkU5vMD2ttnZINsrWeO3YcU7vMGStzNMqX+ZKUlbSZJGMleRaWrkrIjqPMyetjOm0cgzCRpSRlNxs7JZcHnJ2tcHIy4OHDPX760wtcXr64CVljDd75K+/g1lduYXW2gj2x2NzexDliYZGErDExGQs7yTtZahkLpO0lwlTti5uFfC3HbfXJ2hpjVtdwJFGM+saxxXuoNVdslZyVNlUeOB7r82PjepGIjeWLW32muWKTgJuWTqVkTd5HJ2WzZKzxScJOfWTuWL20Nswf641PCVkXHpNZhdeRW4e5Z+3KYn1rjfOfnGN39+nOIRs+Twf1o5DwGQPY+MOO8LnhYxp2tQqf5+OY0rJhaadkflhaK8v0+S9zuqYf5KR/E/LUbPqsP7St06/5vxP9dGxaB0TMauEalkD6N6mdjs1Lwod9+XY4tm5LYx1Pbh+XyMiyTHIrpXpMKlaO0+Kyta91zvIePO6Pfkrp2mrvyebW45DlOHrsdg6/8zsf49NPL3F+ThFLCCGEkBcTylhCCCGEEEKeE4wB3nvvFt599wa+9rXbeOedm9hskoRNpYnlS3dAvtBuSdU0bvrSuy1j0zj58e3xNCndJEmnPL0kElZST1rKplSZTkilvyBQtbyVPuFYEayyXVyZOpccb7JzhPuR35eQWktLPUZIi1kMQ5Asq1UYZLVKc806N8B7j+12gPfA2dkAANhu3WN/Wf6ssb6zxvrWGqdvnuL0jaks8SpJs66IzV6rSYg2k7C97amt5CoStitgW+L1Ks4nHlof7KGkZqPfXBI2axMRWiZiRYxOfbP1IkEb24r7UyVni/H0UpKxWVLWTElZIM4166cfQsBO11hIZXmIxk2vk8GEcsaDD6nZdfgxxuAHrG6s4LYO7tKFlOxTQL+G5McigC59rj9rAWMcxtFO6zJHtcwdK8lYH7dbcrJcyg9PymV+Xv1vRVmqWItQkbLyGV+WJvaNMfW/B3qZErOpvdUm7YjnlzHLe11/lmrxO8+hz+HWvLS1EPVV+xL5mpZzc8XWQjYn//cw2/NYj78tYMvt1uNJbelxScn+ME+sw/37Ozx8SBFLCCGEkBcXylhCCCGEEEKeE9Zri3//3/8z+JVfeQN37mym+Qf1nIB1ognIv/TW29LWT9Hmidq58sa6rfVlbi5M5Ut2U3zxbLJ1+eI2iYM8DWttSsIGKZsSspImkzlj0xyyss9nkkNKDNfJWF8lYofBT/fNTQLcxQTaOIbYYCpVHO7heh3uc5rX1+LGjRHDYHB+vscPfvAIu92LZWPf/JU38fovv4717TXsxmJYD0G8DiafI1ZkrEGUcuWyl4RtJWOFZsngltSdE7T6POU5WoL0MWRsdB0iLGVcX/TR+7WgBGJCtWrT6VgRm5Mkqc4hx05/VZo2ubiAy/vH9OtUQjg7ZlpmSVm1Hp9nO80z6zwspjljp21v0jqAeB4/TtuDD6+rIaSvzdsGm9c2ePjBQ+wfPBuyxzn57HLT82VjUlZ+lBI+QwxWKzfNGZvmjg2fJT7+AEf/GCck/OXzKP+Rjl7qdUCnaEXClv8u6HllAS1d+/9u5H2AIJfDsk7Dln10W90e14rt/MW8REDOsSQRq5Og5b6+mC3To7Wk7QnbA1cc166ajp3r13vcvcdcJmFlLvfvfOce/vAP78b+4/hi/ftHCCGEEFJCGUsIIYQQQsgzzFe+chNf/eptDIPBycmA9967idu3N1ivLVLJybI08VyJYSCli3qlKHsyNj+2R+sLYPnSPZewiOIhJJxSylWnxdIy2BxdpjiMJXO46seFuF+uQYvd8k/66GuWcfR9CulXg2EIET7vHYbBTGk3o8RuKGMM2Pgl83qdSihvNmFSzLOzAcYAt26tcXk54tGjcf7mPgecvHaC0zdPcfbWGVY3V7BrG6SYkq+wiEnYao7YVsJVt5fSFMj6VxK2JW/nxutJWb2vvMbGdTwOWRliJV+z/Y19rdRsKw0rx1X7jMmOi0lYn/r35oyN88TKcdYADnEMnYjNkrLTHLMwaczY16bSx7DxgUVBXMn7KSlrnY1yeBhD+nx9ew1jDPYP908tIVviXPjcHkf5HEyfWSI93VQWOvTx6nPIRREbyren9fyzTsYu2xGTpekzPn0e69RsmWyVZflXy9cyVdte6mP1Pt2Wt+v3eXlMTt6+5A2avzaWydgkIMt9LfGq+y9JyM5dB9B/7Evl6xytlPES8awFs3Mel5cjPvroYvqBFPDZZ1vsdi9uiX5CCCGEkBLKWEIIIYQQQp5h/tF/9G38G//GL+DGjTDP6MlJSFVKEiokYqGkbC5g5Utuva37tVKxoW87FSvjHEP6stY0vnDWbSkNq8+jl3lqVkpZytIXwiCJAT+l8XRZUDmnSI0wL6wk0twkU93U18ZrC1LYwloHY+x0n9KXzkF4hLLDImKlrLFOpK1Woezo6Wm4mAcP9vj+9x/Ga3teufP1O3j3r76L1ekKdmNhV0HEminFbYa8LLGxSpAWYrYrUIG2lFXtsn5QwLZkrR6/JYf15twbYu69UkqOom9Mi8o15J4ulQOe5GqUs74hYY2SsF4dJ5JV1m1Kwer5YqO4DW+JfBkvKfSX45qpW3WdcSnjyPp0bS6Y3JCMlTllpz9rbJordhKwMSlrlSyW+7cyOHv7DOPFiPvfvQ9/+SzIWDN9RoS/kIz1UajKfgBTmtBNP/KQ1H9eoj7ML5v/uzAMdlrKD3b6c8f2krHtygny2Wri519rztj83yO9DxBRG4Sr3m4dE/ql9dA/3y73t5gXt0vkpUjS1jEtManb9bHph0D9hOzsR4u6J9chXdv056RN66UwrueGdc7js8+2+O3f/mn8N/HJXTMhhBBCyLMJZSwhhBBCCCHPIF/5yi382q+9hb/wF97AzZtBxK7XdipRabIv1+svyVF8yZ1L2P4xc4naJV90t2l9WV0mY8MXz3oe15RKLcdKxwSbk5btc6R1bbMCQZ7mc8dOjzguReY6hyg5wngWw+DgvYXM4ziO0scq8WsA2Chqg4AJab39foAxBmdn4T/NvvSlE1xcjHjwjJRTPYaT105w50/dwe2v3cZwMsCubRCvqiyxlq8tCdtcB5oiFShEaE/CIh8z25a+xTi98sez4ndJe9lnLu1WDNJMxzb2xSSr8ZCSwDFtqo5rbXfXzfQ+80hpVbXUCdmYhoUJclfP+aruaUzdTnPExtcEchHcvEfygw15LU1jxzlkvYHFlI6dBO+wCe+5k9dPMJ6P2H6+nb3/XzTeA/t9eOxDuNRqTtc8Keumku1mkrNJzKYkrJu20/zaIY2bp2XTD1bSj1vk34N2ClbPJ1sKWQDI2+VzNP/3KK3PLdO6KsFd7A/b9ef7Vf69KmklQ8v2Xkr2mIRsa6xyXP14Simc7z/2gdfStXcN5eOQtnJeW/kx0nY74vvff4i7d7dTuvvISyOEEEIIeUGgjCWEEEIIIeQZ5Otfv4O/9td+EWdnScQOg8EwhPkCJfHUE6tALlJbCai+xG2NU5cnPiZdlL7AzZM8KR2U1qWvc/UJdB99nJYGspSyxmG9vCfpcer0ayqJaZWYCFIjjC3tbhojiFhjbJQZMieennM2PPaUygpz04Yvq0OibYXVKgjbe/d2z6WMPX3jFO/+j97FcDIEGbtSMtY0ZKzJpawWpM1kLJDvV9vZviUStjHuwdRt+XKUY4q2o9D9tdyY0qy6TaSqHBcljpKRs9JVjp+OjWWCfdofhaval6VcoURrUba4krNKhOrnQCdmo6Qt74FPf9k8uLqfAayfErKDSdujz+4DPILw9aHf6Vun2D/cY3dvF+eZfRYQGRt+tBEuWp5ikbLymSiyK3y+pHlhZV2W8vmlKyjouWVlHlj974IkY2V/KL/uY59wPeHFli9zIduSs6kPsm3970vqD8iLJu3zVZsgx6mWa5Sx9eukLU1zCavbexI2vo2ra60f55ycbV3Hscz92132KVOwoS0lYYOMdXj0aI8/+IPPcXHx/JfgJ4QQQgh5HChjCSGEEEIIeYZ4++0b+Bf/xa/h619/BTdurLDZ1IlYXeq2lrFhHC1T6y/bWxK2n6gtv9Re+gV3XbKxn15tSdaWYJWEqvRLbbXolccjc85qKZsv+1/aD0MQpJJETlI2T8jqZOxqZSYha6OMlTLKgnPh+RAZu997WDtiv18DMNjtHB49ej4Ssuvba7z5q2+GOWJPpzliRcTa/C9LvFolZPUSyPvJNvoSNkrRhnjN0rXl+EeI3uwcJZ3XTyVrJ8qUaxwj+lVTtQGIadd4ndE31mJWyhxnolbSslq2dtalX5aSNUVitZSm3sTywTJvrIwbr8eopCwQ+lsxtUhLNWYzIesADOkxG2PgvMueVysTzZrQ31mHYRxgYHD2zhn2j/bYfrptP3lPCe/D54Ek6zcbKWEsPxax1Y9KwnFhGX4IkiRs+NFH+BFPLmPzf0PyagtJpOp/Y8J15EuRxEmwJtkq44RrrSVsrxxxLl/Ve1K1pW1TbMc70mlfxvKkaC0q0/ryJGgtWn2jPRe0150y7YlXvd6a61bSsPJDpD/+4/v4/PMtnPPY7TznhiWEEEIIAWUsIYQQQgghzwyrlcGbb57in/vnfhZ37mxwcjJgtQoitp4jti1S8/li24nYwzI2Hyct62+z+8mclFQK0jQXpSnZZeIXwLIuZYjlS/2QBpNElJ8SX1JyM6Wqyi+og4SV+RUR75v3ehnGsTYJ4/I6w3WlcwNpDthhkL5hDkdAkrCpz2oVZK3MESvlGwGL9dpOwiUs5UvrcdzA++0zL2ONNVjfWuP1X34dq5thjtiqNHGrPDFqEdtLsMZtIH8JdgRpJV8b4rUsU9w8R0/A6nPNCdoZepJWaMpaKNG6oGRxmYxttWXJ1mJd+sX2SXq2zp2lZW0hZGGCxI1vUMTHoMsUy3Hxvoe3TSpjbBv3TRz0YACnXluSzh0QSxZ752GNxbAOpcE3X9rArEwoV/wMuaLwGWGmH4qEBP1qleaKTVITAEz8MYp83qbSw/LZZjAMbipjLKn8JGTzH7x4lAlZ+QyU8YByvm5Uf4De1gnZsk/9+d0SzXNt5fpc2+NQS1hgqbhska7PFNv1eKVgTvt6IrpNfTl+dv+hZG8pYZ3zGEePH/3oHD/+8aP5iyGEEEIIecmgjCWEEEIIIeQZ4NVXN/hrf+0X8dWv3sZrr51gsxmw2QxTaeJemimXsPJFbC1feynatsTVyaTEVb/ZNlmSqRSmc8lYXVpYErHSXiZjZanHlMemk7H1eQ1CqhWQ5Fn+xb+N4rdd0ljkqYX3bro2O0lekb0exqQJcOU6rHVwbsgSsrIMxwShe+/eDo8ePXslHofTAe/85js4e/MM69tr2I3NSxOXqdj4I4BcynalqXpdNiVs2SavYTVuOcahpO1s+eOm8enfn2b/ia6kkXPp0r3Bf4XjJCnoTbYvS8w2hGs8b1GyOArR9FuH2C7yNUu/Sko2vBWCVHWTtHMpQQuL7Ljp5KlcMEzeLuNBtan7ERO1xmdtsrSwMbUbf/hhPJxxcZ/3Ht76WNZYrhtfAXb3d89cQlbY7Tz2e2C1GmGtwWZjVVng9OOS9NmtU6bIPr/GMf/3If2bkpcyLv+NKf/tOGbe8TD399y/NfozN7wI88/g9r8h6f2VP15NKTyPo5VMRbOtfD/PvffluvolhWvB2ne6tUydk7rdUbI+9XywYRlW5EcC8iMA5zx+8pNzfOc796OcvXv32XwvEUIIIYQ8TShjCSGEEEIIecrcvLnCG2+c4s//+Tfwzjs3cHIyTHPDlhLWzApVoGxvp2jL7XBc/SX5PO39ZQnJum+elM2ti5++eDZZelVsTUqx+iotm77E11/my+P3sYym3mdtSolJskwnZwEzpX5MnBtRxkn9JM1bzqeYlkGuIiabvc+XgJ3Ss8B6HeTvZjPg9DSkjC4uRlxcjFFGPyuYlcGtr9zC6RunGDZDLmBNIxGrhGsp2Wblqz7nEjmrZU25vlTCxk1Tnat7TUdwUNTmb5n8ekS+qn1LUrDS5uGz47K5VaHG01K2SL9ieg+LzNXL9gPWq1NSdlpmbbY+rxwbRWx4y8C4dIwka41VJZD9JJMnMSyS3g4WDg5W3nO313B7FwVt7yE8LdL73mMYPMYxpVnTD07k8xGQzy1APtfkc10+X5PgSz9qkYRqnZTVbSJ9y/NbqQadzRlb9w196m3dXrYJc2297dAm/1YcT+9HE4cEp/yYpr6OdHxPmub/hprqXIc+bo6Xr0BPwIb1JKVTEtZju3VRxt69u8OHH54fPjEhhBBCyEsMZSwhhBBCCCFPkZOTAf/uv/tL+OY3X8WXv3wTZ2dhnlhrDVarVJp4rsQwkCSrlrbSVyedgDzVlCi/4Z37xvd4+dQdSX3B3kvJtv5E2oa5YvMv/ctla85YnZ7VS5G8QSqEKF8uKpwSD4DUUpX1ccwTQ5KU1Wm1dG2hFOk4hoTsMJgoh0UIizA+ObH4XZLkswAA+tlJREFU7LMdtttnx8gaY7A6XWF1FuaJNdbArBYmYgv5erQsbR1j67G6qdiewG20ZedDe9/B9h4NcZKV81VjRjFkUCdjdZJV2mR+WGOifJWSszHpKnPAqrFaydgsQSt9/DRHrPWZ9Mz+nLonrW2ovuikZoEsNSvS1xufyg8P4UcM1ocUrJsOsAjiNaZnx+mejJOsHdK9sRuL7adb7O7uDjxpT4dxDJ9P+334DDo5MdOc1unHHbo8sXzm5589kkQNT3YqcSwl38s5YssqC/m/P4dK3pcVG8J1lCWLTXM9LfNKDfrfPE1fzD7uv1fFG3H2XKGf/vesHueY66o/IFoid3aEGTnbkq66PZUiTuvOeTx4sMPv/d4n2G5DVQnOCUsIIYQQchjKWEIIIYQQQp4ixgDvvnsDP/uzt3B6uqpKEqdSkf2ykDKO9BMRq79Aly+IW8flXwofI2Wbj6jYrr+ADucsv+D2xXo5pm8s9fWLMJVlGCd98S/JVz1HbTg2CE9JdyW5K39hfxhbzz8b9vlMgkhyrJTfkozVf8OARlLWYrXyca7g9XrAycmAcfRYr/cYRzPNN/t02by6wclrJ2mO2EK6VkJUpKmWp2mlLVmnfVm/xjITrijOi3q9JWJ7EvaggC0fy7Gow1oJVbUzXkuWnJ3eDlliVtpkflnpP42hjzc+pVPzyzLZNRmYbBw5Z5aWRd0m54upWf0WRj5OXDepFHI8Rp5DmT9WH6/nqLXTdTiTErGSkrXFcjChrPHKwK4tVmcrjCcjxs0It3fP1DyygZTCB4KcbQk0/RmXl2XPPz9l/lfZltS/yNkkccN+/QOXXkK2/NwMx9d9dXtrvWwr28u2cj21pX8rDqGPb6dR0785XpUR1+JV+sn9bo0Zf2uhTlKmZnvX1euzhHa55ZaABeQxaAn76NEeu52bZOwed+/uKGEJIYQQQo6AMpYQQgghhJCniDHArVtr3LmzwcmJnWRcKxFbplsB+bI3Cb5+MjYc07BNB+XrFSXTUWg7k395D7Tnis0TsmkfUH+pLAlUva+XkM2vwSDMEWswDC5ua7Ewl4zVS2McvE/zxgY566ckbF4uWUogyzV4H+Tsfu9wfj7i7t39UxWyZmXwlX/mK7j9tdvY3Nnkc8SqUsUiyspEbCZqO/JUlt3yw0CaG9Y2xtF9G+uHxs6uQ/dv7QMOt/dQT2MlnXVqTc8Ja0wmbKvE7OSfymRsNyk7iSU9H6z3U18tSv0kVlUpYWPU3KsujRGXU2pWnydLzmK6BiVPpS2KVLkn4e2XcGkpCVeZM9bBAdM0yzJnrL6PXuZyFjFrptfs68BwY8DFTy8wPoPzNAvOARcXkt4HNhuDzcZgtbLZvx1llQD9YxGgJ1LTPK7yIxP5MYreB9Rp15SqTe+l8t8f/aMgvd1aT0tTbOdteXvZVu+ojzOd9vbxuZid76NGbPTp9VV71A8xjmFJieW2tE8iVio8jKPHt771KT766CL2o4glhBBCCDkOylhCCCGEEEKeEt/4xiv48pdv4tVXT+KX6Fqo5onY9pfTeRnJJF/TF96m+LK5MEzN9dZ2id4/JwYbZim25xHANFeeStN5aU+p11ZKVqe3wlhA+rJ+WUK2lhJ+up8yb6yeM3aSOx5VMjbI1FyGhOfWZ8lY76UksVHzytopMesmMe+wXgfJe3IywDlgtRoBeIxPwRWdvXWGk9dOcPr6KdY315V81X9lGrUSn0qgRkop2pKlLUlq1L6yr6nP1S2D3DpnKUnLflnTcdKklUptJWNjyhU+/+2CEjVVclVSr2o7itb67ZfEq0hYPZ+rTynJMi2r07ZVMlaW07XE49W5dRq2nCu2TMxmz/Mkb/W4MSVrTNovYndqi+Wsi4SsHS3sxmJwA4bTAd55uEu3NFj5VNAp2f3eT58pHoDNfmiS0pjpBSLzyrY+T/PP7XIf4v40J23YL3Nv63LIUv5YqP89y0spl9fc+2zXbfrY9LmP4jM+u4p4jO4jidfUHj7XtRDV91X3Cfc0v5YyMRvaTNZH96sfi+y/2ouwPCxt+2xbJ2DDnLAhCfvgwS4K2YcP989UmXxCCCGEkOcNylhCCCGEEEKeAsYA/8q/8qfwT/6T7+HmzVUsS1uWsu2lW6V9GIxappSsns9vOmOxnFtvbc8+miP6CvoL/3I7rev5DeXLbv2nZYB8yd1OyNZt+V/6wr38ktx7YBhCGm0YoL60D3PIpjFNFMj59QSZGtK4DiJKjAkSJSxDyk1khnPDdAXDVBY5zBW5WllcXOxxeQmcn7vqy/YnzVt/6S28/mdfx3A6wK4s7GCT4JqWMQ0rrz8l0KI4VXL0qLlcTT1mto6iHY11PW5D+jblq5Y+epweh/aLpGkb3Zg8rXeZzIlliVl13VGOziVl5UcM6lw6GZslZqWf9/E8VTLWp+PiUpUPzuaXlXvgIW+J2DfeEpWUzVLCcq1T+eHY36H+WMGUjIWPaVnjDeAA62xKyE7XP2CAsQab1zYYzgZc/vQS7jlIAO73Hvs9AIwwBrhxw2O1MlivbfxMCj8S8fHfBv1DEZ16TT8UAbSIlbbwOZd+xKL35yIWSD+U0eWPU7v+gYxI1HKO21zEpn8X8u10L3Jpmz6vc8HZ+MUD0rXoMsOlwGwfV+7P+6RKCqlf3pYfpz/X230aZ89O75vt6d+qUsbmc53vdg7f//4D/IN/cLf57yghhBBCCDkeylhCCCGEEEK+YH7xF1/DN7/5Kt5//w5OTwesVlbNFVuXeizTQukL9HYZY52MnY5UX5zrZbne2u61zdH61tYU+1rbaV2+pNdJLlOlCHOpkL74r1NUkmSVZKsWAHrOWJGhIiJCSqgs0SlyPM0XG2RHOndKvwJlClanZ4chmMKwRCwxGv5k/lgH5yzWawvnPM7OVjBmxH4fykd+EQnZG2/fwI0v38DZz5zV88QW88X2pGlLxEaiR6xfa0tFbCV11XY3CaveEpWELa9pyVtlKb1ji8SotGUJWiUcs8Ss2p/NDTv1r5KyyBOt4dBJ2KJIxqJejw/FqETtdN6YdjXzCdnsPDJO+ZzI45PnURKvRWpWkq5w0/1RyVlYxFLIMRk7qKUH7MqG+WgdMJyEH0PY0/BjA3f57AtZIZSQ9VNa1sXPo9XK4OTExs9MkbAA1Gce4pzYrdLE0pZ/fuef3a3P+Cy93fm3QBKouQSd+9FOawwRzknAStWCcmzpG44zlWxsX0/6t6YtbFFce/o3qJV+bQtOfdxhCdrbX8rZMgULhOd6HB1+/ONz7HYuStlx9Pj0020s508IIYQQQh4fylhCCCGEEEK+YH7jN97Gv/6v/zxOTwes11rE5jI2Lz0MyBe/aU7Y+RRtLV9bMrbcfhzDNDfGnISV7Z6gTV+0yxfYqZxiEqD9hGyrvLCJYyRhmoREWE8yuL1M1yJjAogp5XBtYem9xTAESxQSSGGu2SBhLYYhxARXqzCYnnt2vQ5i6PR0gDEGN2+OWK0MttsRux2+kPlj73z9Dt75zXcwnKRErC5RHOfcFClbzA+blRBuyNJM0kobUp/W/LDHJmP1eHNSNizUtSDfFzeXRtaOoJWGjdcTOtTyFW0pmwnWTltre+l6PFZKDNtJyFqVjBXxWiZkVVliY02Yb7ZMzULJMjc9PlvMLzu9hyUlG0WwSvBmaWKT7pWZ3p/WWTi4UKrYhQ+AYQwJ2eHWAJwjpGOfIzG13U43RxHmJE8/BtGStP7Bj/yApSVa8zZJ24qklDdKTByr9dQHSG+oJcK1FrCpjH0av50OTcdqwZratJRNn+Nz49T7ZKxaurb65bQ/R45Posq/bfUYqVpD2JbS0c55nJ+P+Pt//3M8fLg/9oSEEEIIIeQIKGMJIYQQQgj5gviFX3gVv/mb7+BXfuVNbDY2JmJ1KjbNEdteShI2L0+sj5Uv15WB6srWQxL2cWWT/jZ5TsKW22k9lSieWjxUW0ppzSdkpfymTsYiO9a5fL+kZfW6nEOuQ5Za6AIpISsCXbaDlA3nKxOx+dJgHEPfcbRYrx28t1ivB3gPnJxoOetwefnkorFnb53h1T/9Km5/9TbsehKw0x8MknQt5GtPxLbmfJ0VsaboV46J9nliO9Rx5dhquylhl8jX6/Sxvj5HTLdWpzUp9Vm2Y5mU7W2X63JsuR77FOlXPUbcVyZky20Rsmqffl14O51/krZh9yRlpyRsPFaEL5IcBhD6AvmcsQhS1sKGcaZvSOw6dF7fWMNai/H+2BXlzwv7vceDB/v4b8nZ2YD1Ov0bksoWt+VsWR0gtJef0WE9/5xO7Wkd6P8AZ8l2v89Vpexc/7w9jtJIt9Z9DvN4r6tS2tYliNO/UR9/fIGf/vQC8hyFFKzjXLCEEEIIIV8AlLGEEEIIIYR8Qbz//m38y//yz+HsbJWVJhaJqssO94Ss7q/nhtXHZUbqKCHb2m5x6EvqVr9jvmxvJ2OBJFtln8gBKaeo5at8WR7EqJnkapIHoS2VFZZylmU6zLm0LMt26uRymhMxiXORsiJhU5liEbPhvFrGhvLFFs4Bq5XHfm+xWgGrlYX3HptNkrKhRPGTk7Enr5/grV99K5sjtidge+WCM5Ean0clU3siFvlYc+dpjteRunPn021Ve7Fvrr1VbhnoJF9n3I8+fyZmJeVZStki/VkJV1W6OEpP2e8LCaquV+9rjRfTqEZJUKMSsDoJW2zre6j3xetX7fHapjlm5XWo07GxTLFBStAqoVvJ2KlMsRkMjDMhIbsKMnZ1tmo/N88h4+hxfp7ud5CuQ1YRQEtY+RxNKdkW5We0R5qftZStjydhUwK2TK/OJ2f7/bSUzfvnadmyPT9Pfr05evxiT7P/UnQZ5LxNL1P6VaosfPzxBb797XuPdW5CCCGEEHI1KGMJIYQQQgj5ghgGg9PTFTabAauVnhu0LEuMSsT2ErFybDsJe2iJznavrUevb+/L6taX53Nf2rfkbJIE6cv/Mr0a+soX0ancc5rrVc+TKOv6uoIw1WWJTWwP2+l8eTI2LUW0hr4Gw6BLF/usz2oVDFL4At1Mc8WGPpuNhTHAyYmbkm0rGGMwjh7brcPFxfWnm+xgQ2linYq1xZ8ksnvCtBCgzZLCQN5PC9aZcbWUbSZjZyRsLwXbE7N6u5KtC98u1XG1H+omMPWxVZnizr4lZYmzbRPEZDMla/LyvzGJOpU8jVIUqWSxfi68CSWGJQnrvY+lgqeTpTFNErrZ23+SsMaYmHT1LiVrpS27PSLSfEPCGsB6VabY2zSmMfAnQfievnWK8WLE9tNt9bw8r5yfO2y3en7tMK/snTurrPx9EIr636b0WSkp2vJPkrHpRyvyuZnagdRP79M/isklb74/HRv2C2mu2PTYSrmbyhunfzt0/347suvS1RhkO9zLNE68gux93pO0h9EyuCxJLAlYIMj3hw93+M537sdS9g8e7K52UkIIIYQQ8thQxhJCCCGEEPKECRJ2wNnZqpojtp+CDcemNp2E1aWJk4jNv7xWVgple9mGA229Poe+TdZjlWK2t13ua+8XMQQY9cV9+nJcJ7tKAVCWMJb1+s8Uz4PPnp/UJoIijJXm9y2f05TGFWmc5qo18TolOSt/w2DgXFquViJpg7Q9ObEx4XvVL/grDDBsBtiNhVkpCWuShC2laCVOp+d/VsTqp1gtW4K1KWKTHa2FqhqzmZYtzt2VsPEUnf2PQzmOzx+TpEyzvkpCNdOyPsnMdBqVbI1SS21DHVO8vbO0qx4HxVjqPFk5YknP6rlio7XK70VMxkqyVz8+9Tzp8szyXEtyNv5JKlZSn24qYVwkY2VeW2PTfmMNMABmFQTt6tYqXN8kkQ9+9D0H7Pce+33+QNZrg7OzAcOA6UcjPvuMknlk5cctQf7JD1bS52y7RHH570bxwp3WRZLW+2R/Plds+SbS8rhOscrnivxbkcoV6/69xKy+jjROOr/+d6juV46x5ENES+ZiT5SuLspWLWTHMZSl/vDD8y9kXnFCCCGEEDIPZSwhhBBCCCFPmG984xX8B//Bn8Hbb9/Aem2zEsWlYNXyrhRysmwnYkvZ2lvOrc+1tTjUr5WMbX0RX243DFVT2ObliOv0UkrDilSQUsWAlA1OKVmdlA3rMq+eUVIiHJvm5Utf1OcJWR+fI+dM1jeUIg6TXcq+YQjbq1VYDoOdEr2pzOR6msdysxlgTEhZGwNstw6SVru4CGm3x+XszTN85Z/9Ck5eO4FdT+WJZa5YiyhjEV+vuSjtSlqh8ZI9mH6d24e8XxyvOFfWhsY1qfVM9JY02nqliXssmYO0HPOqidhW29x2a18sGexnErJ6vzcpYTrtg4S3LdJ+LW4LWStp1vgwyvTr9Od9kqzlvsiQHy/97RCSsXba4eHD54LxGNbhveZPw/W5NxzG8xH7+3u8iOz3Hp9+ulX/tgTu3Fnhxo2h+PfKZ8lZoE7KShuQt5c/jknraVunZuW4IFlT3/YyHS/tZVpWi9awrPun9fx8tbzVPwxo39f8h1JC7/1fp2bzBKxX/+Z4fPDBQ/zgB4+qMb0PzydFLCGEEELIswFlLCGEEEIIIU+IYTB4441T/OzP3sI3v/kabtxYoU7E9v7Sl8HhC+46OZsnYoEvVsQuoRSurTbT2N9Lxub7SvmaJGz68lt/4S9fxOdfmNdf7ufHyxf4IgVS8qt+znzxHJWJ29bcs2kcKVMdXiPpuW9JeWtDQjbMJxuSsquVn5Jrj/HluwU2tzY4ef0EN965gdXZKitHrOWnlrCZXEXazkoBl+35s5Bvz4jYTPAqYdpN2ipJo/f1+nclrCk3G8cfgYHJnqpyTtZmIhZF6lW9JbK0qzpHS+AeM3dsOS9s81xFQjaOFd93+Zyyco64jZSIzQSwSdeZvcZ0G/LHoKWufq1IAtYbn35QAIQ0rBbHkp6d+pjBhHT4aGBPLNzu+suBPyuIwJu2Yvt267BalT8gCj8Q0RI0zZudPiOlrXzRatma2uW8rX8n6m39+ZynZUOf9O9CXuZYH5uPH++Ekq2tN3f9b5SWvG0Ofy63Jaz8KMjj4sJFGSvLe/d2uHv3xSmfTQghhBDyokIZSwghhBBCyBPizTdP8R/9R7+Cd9+9gRs36hLF+bx8SdjJ+jDYLBGbxJx8iV3aCXTalq5jQftSWgJWtx/64r1sL4VsKe8wffEf+peliKW8Zj7Ha0rGyraeO1bKCQOpHGcrIZtSSmmcsAzbMh9sOF+aMzacI+9TJmNDOjacO+xLc8fudgMAYLdbwdoRAHB5+XiiaH1zja/+T7+K0y+dYnVjBTuoVGwxV2xTltokKbXszBKrWtCK/NRpRS1bi5KyXUkLdYyWcCj2N7Z7baXUnH1LPO7bRc4hTC/xVnq2l4bV+7QovWpK9uC6miNWjtVlilttcakFKJSYlR9G+CRtYRBeH04tpU3uVZmS9aZKxsY5YwfTTshOc8daYwGHsO4sYIFhnBKyo4ffe+zwcs29ef/+Hg8fjlnbem3wxhsnat7z9G+T/nHJ/A9YwlhLt8O6/mxP40u/dh8gCNPyxzzyuZKOL1Oz8iLKf8Sjt023z1IOzQPrHHB+vse3vvUZtlsH/cIuS00TQgghhJBnE8pYQgghhBBCnhDDYPH666d47bWT+GV16wvpQC5R0xfb+s+oL5DnErElx4jYa7BKcZxjvyTWx7SOL4VsZ5Tsy/nQN4jSurylTtNKm77foS1P9ulryJ9PnQrTz5mp9ovQ1f1SGraeN7b1JxJktTIYx9C2XltsNg67XV3qctEzYA3Wt9ZY3VrFdKCWo5Xz12IUtQjNZCnqMbTkbJYiRrHU5ytfysV4+tiuiJVjdJvu35Ow1/k2ERq/XxCBqOd7rfYblfCb+jRTqkqkzr6V1NilHJX7Vc4RW4rrbgnm6ZxzyVhApVj12Pqay/vfeE1FIa1eMzIuDGLq1lgTJK71YV5ZSc56E5eSkLUri2E9YDgd4PYO/iURYTIHqcYY4OJijD8Yyn9MZHByYuOPiFoSVn9WYnpNyQ9Y5EUp5YXlM1zWpyuIfVPKFtnx5Rus30+uIf17IJ/Pen95T/LrqZOt+vje57EuOXx56XB+vi9K3wcZe3Ex4uJij93u5XjNEUIIIYS8aFDGEkIIIYQQ8oQwBjg7G3ByMsSUazlXbF2qFlV6ttxWZ0Btx9BYzq23tg+19+hYnWyfWbB9bEI2LbXkRExCiQCFmuPVR/Ep2yEZm8vV1JbGdk4nbSU9i6z8sMhUEbayLXPQyrWU6dkwnyymZGyYU1bmmF2tQpxPErLrdUjPbjYulhe9eXPAZmNw9+7+yinZYTOEuTKnOWKzRKxplCxuvAxnZSka7WYSXy0hW5ynm4hV21dJx3YFbOdtkEudx2duDtlmGla9NZakX1ttB7e1kFWStbV+KA0LIMh9jzQ3rOoTH6JN541idXofwSKWE9bjxYc47dcJbXXjsqUZTPZxY7wBBoS5Y124Ru/C3LF+Ha5vOBmA2+E828+32H3+ciVkNfu9xyef9MvjvvXWCW7cWMW5s1N54/DCtVb/mCV9ngJa6mLaTn36idnjlofalm4fS5mCTelXjx//+Bzf/vb93pFT5QdCCCGEEPI8QhlLCCGEEELINbNaGfzqr76F99+/jbOzVVZiuJ0QKtNCImZLaRvGTyWKhUPfDi+VskvHW3Kenpg9RsKW663zpbRqnn7N94UUbFqXL/VzcQuUqdYwVp6IzROw+nlJ7XIN4Xn0MUWVJ8JqEZ8nokVeJBncmkM2zB0r67ZK+y7BWIPb79/G6ZunGE6HSr7qhGG89cXLsCUnq/LE6qmrhCv6AnZO8pbJ2yVp2Gwpxy2QsLMC9ph73nhJy9hZorXxNonJ1MbbRBKm+ncKMSWbXsJJsno1ppa86tyzCVkU7cXj0fPFVtfbePx6Pln9PMXHJf2k3GzjOS2Tr72/ZkJ2krAieSU1G98P0/yxdmPD3+k0h2xewfelYS59f34+Yhx9MzULhM+o09MBJyc27tMl5KVP/nmcz8NdzxOu94XPzXxOcS185Xx54rYUs1J+vrV/yf15+HCPBw92WZvqkQnZzz/fwjkmXwkhhBBCXkQoYwkhhBBCCLlmTk8H/Gv/2jfwC7/wKm7dWmMYbDbXqxasgrSX88gG+ZZEXduKlcueUWpZqFa/66C0SLrdd7Z762Xf1j7VqxCn0qaTVa1kbCnDUzJWvoAPaS6dgBV5Kwlaa4N8CMekMfNkrI/rkozVy5CMDWMMQyhBLKlq74Psl4Ss9x6rlcVq5WNSNt2D5ZiVwdu/8TZuvncTq9NVlohFvC/Ta3YqXdwqH9wUpvLUQfXpiNdjE7GZ7NXXgbxf7N8QrockbHUvr+Ot0hqj89zp+V9bfSoJqksXx1tgqr5RpmpBq0Sr7t9MyIrcKs+vxa56LqqyxHqpkrAxEQtkx2RStnjeshStTddWSVhJ0up7KdUGfDjWwMBMczmbcVoOBhY2zBnrPQY3YHVzBe899vf2cCMjiyX37u0P9nn99Q1ee22j/t0D5HNWf77qH9doGQvUErZsm1uG9RQxL0VsIP/3qvXRqtOuett74Kc/Pcf3vvfg4L0ghBBCCCEvNpSxhBBCCCGEXDshobhe20ZZYtWrkn+pnGO7lHEpWtP58uWh9lafY/e1aAnSlpQ9VsjOtZXiNZ8rUyek6i/i5Qv+dJ15Mlb6a4HbTsaKvJXryJ9bvd2aVxZKzqYEVxC75dyxaU7ZIGwthiHI2NXKwrmQEL55c8BqZfDw4f5gactXvvEKzt4+w+bVDezKJhHbEKIiwUy8XyaTdzr9WqVii/UlAnaRiG28zJemY8tUbXV8Y99ce1Uat0FVjriXFi3Ea0vKlhIVyStVsrWVki2TtEclZI3qVxwr6V19vky4ioj1ScCqmxjHlRRsVa54Wi9lr5a03qbrM1K+3KV5Y8s5Y/W6CNq4HNLSOgu/8rAbi+F0wPjwJY3FXgOPHo1wbquSsEmKyudyLUhD26uvrrHZDNm/n+lY/UMcPU6rLbTP/X7l8tLhk08uq3mYW5Rd7t17eUtZE0IIIYSQBGUsIYQQQggh14wxmNKKOhGrvyQuv2DWKdiUim31nY5AbaB6y/K4ue1e21JKU1Tuuy4hWx5T99Gli3U6Vpcs1l/Wt8td5inaeFbT+5PxWxJWttNzq9Oy8vyXQr5OTJcyNpQpHkdZBiF78+YKm43DxcU4X/bSAHf+1B28+s1Xsb65jiIWFmHezSIZW0rS7OVWtumnCZ1+019Ttoq0VSK3GgNqv3re9HmzpG55THGNiySsHqNHuStzjfU5KgGqxzgkZeUeeSSxOh1fliOuhOx0PXMJ2axvWUJYjzddY2su2Wqe24aAjv2NWvbKK6vXRSVhi20tcaOItUHMxjloRdZ605axImmHkJo1g8GwGQAH7AbKtqtyfj7i/Px4mW0MsNnk5Y+luoD+sQtQf4an93gtYMttEasPH+7wwQcPOGcrIYQQQgi5MpSxhBBCCCGEXBPGAL/1W+/jl37pNbz77o1GKrZOwSbxlovYXOLOmS6ott5ybn2u7aoUFilrv4qQLfe39tX9dDpKzx8YvqBvzxUr8wPmX+DrEsdeHZP/5SWMtSTwsSSx975IwCKTsjohW5atDnPEIpYrFhkbShgH+T+OHs6FpKz3NgriOezaYtgMIRU7iViTvWaT1KrW0RCYUP2QS7FeKrYSrMXYRyViVXsmXcttdWwvIZv1aUjUxRzoa7zJ+kU5Wh7r07X2pGwpPquyxa2ErCmELAqpqs8l16HkZ1a6GErYFr+VmC1X7JUwhUrEemRtgLpeeU4nqdqSsrBIiVidkJ2ErLGpVDI8chk75Es72HA9ow9/G4/17fAjhv2DPfz+cHKSPD7eAx9/fInPPw8iPL1922WEe6nX1mdXi93OUcQSQgghhJDHgjKWEEIIIYSQa8IYg1/+5S/hN37jbdy6tVYJ1/RXHDEdB+SytixP3D0jkiFpCVg02o4VsYe+rD4sRdvjzUnXJePM9BahNclOLWLL8pVpjtX6Lx2frkGna+vnVe8rSyPnZip/rvUS2fEi5EXGOuer+YeDpK3Ts8OAeHyruqaxBmZlYFc2ilgtYbUg1dIik5JzorP55CgRG1//JjtHr6Rw9nKXJi19C6E6K2JLgVs+HrXefLwHHmOTQ28V1ScTip3fK+hEatY+DVYmYsvlrJBFLlWjkJV2XaJ4On8mgpGfo5lyRX6+7G1efLRpIdyUskivhbJ8sdyLTASbfAmDmLit/mS+bpUWN0P4s4OFPbUYMGB8NOaPnzxRHj4cAbBENCGEEEIIeT6gjCWEEEIIIeQaWa0sNpthJhWrE7G9+UBlXUYt7QDUUtPaNydijxWzLcr+h+Rrb4yekSnXj10mWapFqlyXTsjq7VyO1vPN1qlYwDlZJhErz6f3XiVjRaqm57iVkNV/0iavq5SQNTERu1pZ7PcO1loMQ7juL31pg+3W4dNPt1Wy67U/8xpe+9Ov4eY7N2FWJs0Vq0oTV0K2WG+VEq4Eabkux1t1vGkvm+so2lvnkae01YairWjXorja19rOds3unJd18rLVh0iqtUi7ynozJauOzeaDLcdCLjljqtW091dp2TI5iyRHW32bZYsbx5Tp2UrKakFczCcbnzsLwE2CVZKx1ucJWVWeOKZhFyRjnXewKxuSuB4YToZwffHzmhBCCCGEEEJy+J8LhBBCCCGEXAM3b67w5punOD0dlIBNsi5QW50yDQlI/1K+tpgTs2X7kxCxh8451z63PXfdrWP692GuDGWZVi6fp1q8GvXchKUuIS37yuczH8uofSZbttOxrfLWqZS1Xm9J/ZOTAZuNRes+bO5scPPLN7G6uYoCtpSbMSU7Pexemd+DL51SrJbHd8arzi3r2b1tiNOsaeZ1Ub7FivNU11oKz+L/srdt+Vf0ry+qbiqP69273j1opYHLc5f34uB5es9/495UfaqHl15nenx9jZWgLx+vPn/j+ap+ONA4lzH585e992xawiBbGjulY6c/kbiEEEIIIYQQomEylhBCCCGEkGvgn//nv4p/+p9+D++9d/PAXLHpi/4yGasFm81+NjlnhQ4ty/Ul24+LjNdKyXZqrjbHuMo+vT8skyT10xKxPZQwTtt1eWEUx8j+1C9/XnUJ4vbcsXUJYj2HbJK11vq43/uUkJVEbJoz1mIYPFarlJIFHMYRartxlwaT5opVqdhWMjZLycotbrzkulJQpK6WY3qpU7I9gWiKc/QEXUsqtvqo66zEYfkYMNNnKT1ZCcSXs06lxnZ9DTrdeiglqxKx5bhVOhX5vLD6mOz4oixxNp9rIzWrry2WK26kY+P90Y/JpLFapYklyZolYg3grc/ntJXyw3Nzx06P33gTyxEDqBKydrBwCMlYSdHaMXRef2kNe2Gx+3QH33nPEUIIIYQQQl5OmIwlhBBCCCHkGrhzZ4133rmBs7NVlGy9UGYr5RjajTouGKV8jFLKXoWrilhT/F3lXIfOd2jc4x773HPQ7lOmmfsH6+NyIVv2KZ9T2SfPb7qndZq6lPllQhaV1M8Ts2HfMBhsNhbrdTj/cDbgxts3sLm9CZKpkKXx+gphKcu5tHF2j5DGbr5stJBtiNhmuWElRLuJWNNo61/kchG75KVfvk2WvmVMuhaDxuMqtufSq1WbfozZ7rxtUdnm1n0vn5vyuOJ5iMlUFMsyFdu4b63HXb124/sqP2f5emwts7HKP4usf3zOpjlk7drCrvkVCyGEEEIIIaSGyVhCCCGEEEKugdXK4uRkfq5YQYRZkrZGbWt5B8zbnJ516lmaGdvUHXvp/mNSrLqt3L+0f2/ZuAIT9vXSrtLHmJBclfNIyrU8NrWV8rRMzNbrkm51LqVqy2Rskqs+vi6cM1lCdhhQLMOfJGGdCxfsfShR/OabJzg/H/Hxx5e48/4dvPtX3w3liQcTU7GYXn8inTLRFe9jehqaSVZ5ipDGyIQXkuyqys+2+ut0rhKiVTpWnW9RIlYLxM5bZDYJe+jt0aJ3TPnyl3O30rIqOdpLycp1L07I6jljp9RpuS73tZwbtpxnNkuy6rlmjUq3tubGRZ6AjY+hNYesPr96PmMSVpKy1gAOgEWY31UnY+U8Ri2nRC2AlIgtl5KQdTaM7RFSsDJ3bOOxEUIIIYQQQgh/tkkIIYQQQsg1kASryaVV0adMNZbJyVLcds6GeQGLRttSEWtm9h26pqvsO3QtV72O5Qatl2Yt98nxvecoSct8v24vz1ufOx+vfr3Ur6NS/Ou5Y0X2rlZB1gJBKA1nU3lilQTsytXerZu51WX6Ma5r6VoNV0vU6ny9l/TMW6JVmjgmUOdEbOs6r/r2mKM3ppbLvePQeb7QeNy9sVr3vDd26znoXZ5OV5u8f5WCLa9Hn79o1wnX6n3Ye33pe9BLxqJOxuq0bJWatchKe8v8scPZAHvCr1oIIYQQQgghCSZjCSGEEEIIuQYknVgLM0CbACklW5ecTclY6XuU+cj6HGpb0ucqyBhL5ks0C/st6S/7Wn1CmxadbcGa5pBNaVnpH1Ktsp7Sge0/IE/Etuaj7YvWJFTz9GyejHUun29Y/mT+2HGUeWUtjPFYrTxWKxeu2xoM62Ku2CkR20qwamlWSrRMtnbEWLPsK/J9VYlk5MdVctvU69k1mPZ2JgCXyMeyn24++KOJw3ivXq96uCItK0nZ1tytWTq1MY9sNyHb2kZxDFDPH6vmaK2Sspja4bNrlDHKczXvbbnPpDGr45FeA9XcsdNcsfo1UKVpp2SsyNV4T6YkrLfTicSt2vBYzGBg3PSeGQyMN7FE8ea1DcaLEZcfXR73EUcIIYQQQgh5YaGMJYQQQggh5DH4+tfv4M//+Tfw8z//aibUtGXQEvBQMrY8Ztq6wpVdh1y9Tg7JV72/t750zL6U1es9lybPRygbjKl0cS5p9f7W8XJMkq+yT57vsvRx/pfa5TXiG8s0npQ6TiWOpbRxKL08DAYnr2zwxs/cxM33bsUkX0uQdpOFS15S6vhKpioxVqUmO/1KedpKHFf7eiIWDRFrOu3lum6+BgnbGqspZksp64vyvOo3CJmQLc6xWMhOY+ofHVRvHXVdWSnj6sEpYds4d1aaWESrLkvce6z6vOXzWFx3lK36eqQM8iRsWz8WyERuuZRy8jaUNs7SsYOBWZkocwkhhBBCCCEEoIwlhBBCCCHksfjmN1/Fv/lv/gLOzoYs2ZqkLNR2KWLLNjmm9UX+XFtv2WLp2I/DUkl6SNAeO/58vzL5qtvzPvr4JE5zOZsfo0WpTuKWc9CW58wlbKsMcTtJK6WJrRU5a1RbSlrLXLPWGpy+doqzP/cGhtNVFLFNYYq6rXlr9TLb1TheSdVmWeQWxbVk5zb5ejnGokSsFrHlJRTbBwXsMW+hzktXzjErZbXgLM9/ZEK2DJPHRGspX4tx43rxWHR76xozUdq8Aek4LVGbklYn1ctErnpNRKmq5tfN7kXnL6Zt5XhrwmO30/UV5YmlRDE8YFcWbkqhE0IIIYQQQgjAOWMJIYQQQgh5LKQ8cZq3M5d7QlvEmkZbPAJ9w1OaqTkO9b1uEXtd4z6Z6yrTp/p8pXAr+9TpVm1wyn4mrvdKV6c5L9O58/kpe6+RlIYV+apFrE7HpvLFBuZ8h/3vf4rxRw/ifJdQ58jkZ/cGqofbeznFe4NagjZEakvcVn2R79fXmQnb4tzNa1DL6vE2ztkUsZ1rXMSBY5vnbMjhSlQ37k33/CjuU+M57e4v7mdLuGd9tdDvPOdlyjm7hs7+qkS2GjN7/enXlW4rxtd/1VgG4dsTo943ZSJ2+rMri+FkwOa1DYabQ+dJIIQQQgghhLxMUMYSQgghhBDyGOiUYk/Ehn6tRGy/vzpywXpru9d2zH7p8zjm6Riu43obR3VusjwXdXt6XvL99f0v9+vjWsJXn6Ncb7WV+3slrkXOiqCVPiJkjTHA5YjxB/fhP7tsS67WQ1SCbS4d2pK5lcxriL5WKrZqK64zk40LXpJHidjyMdWDXf9boDPmISE7e+3lPWqNh+Je6+ev+dAXPHAtWmf6N/cVwrT1eFuvqUril48HqO+BOq4li3tSVuZXzpLlqmxxTMmuLVa3VhhOKWMJIYQQQgghlLGEEEIIIYQ8FsZIMla3lQLtkEjTSUvgKOtSX9GR7XPjzI21ZLxWn+s2WVdlXoyW+1vyvPUch219bKv0sGmOW//1SlnX46UfBKSkrP4bBoNhsFitLOyQpFIlpaaHHpOXcisyZzcjCQs5Vo7dS79WYqyUZI3r6Mo2U2zrayz7NR5DlUxtib7WvmP+WjT2da9F72/JZrk3rcdd9C2ldJV6Lccs98m4pj4+6zv32lDXMfta0des78l0Xi2TewnXbgLb1m1avLbaW8lYs1LrnDuWEEIIIYQQAspYQgghhBBCrsRqZfDKKxucna0mCdcpZzrRSsaKRcjX22nN6+c6TvKsjPH45PfcNNqW7CvHnHs9HB67lPrla6SWwK2/Mj1rYGdk6ZWfjkKYNVOYWrqhEGrVcDOiz2Qd837lNRV9muddMkZ9gZXAXPp/reMPnXPutVTJyWzX/HHV+Q6du7zu1uMoxOnia+o9r/r1osctX7em0d4aXy116tsgL1NcJmZbZYwr6asFrQjbwTTvAyGEEEIIIeTlYfW0L4AQQgghhJDnkZ//+Vfxb//b38Rbb51lAjVIgFwkhdTiYVmW07IeV+XYY4/pbwD4I8c/lrlzyL7Hvw5jAN8cIh87c1OluzPtPuF14RvyVEt6n6cC4ZUA8o3+6ZhWGtb79HjslNBzLk/RluJMhFQpV7NbgUY7kI8pj6GUWnJsQ2a1ztsVgaWIa1xfMxnakbmLJKwe4zEoU51eXls+61S1GWPg5Qkt9xvAeANvfNaWvTWmZTaOuqZ47HTvvPf5S1+Ol76YzhkaQzt8fs1q3HjejqD13sf+Mq4x6VzyeI2Z9nl1HUZdh76m6TWlt728zwzCujXwzlevydhvml/ZY+rrw9L4tM94AztYOO9gBxuuc20w3Bhw8uYJ9g/22D/Y1w+cEEIIIYQQ8lLAZCwhhBBCCCFX4OxswHvv3cJrr51UCUVNP9B2HVGpGTM2O/5V9z1LXOU6zexzdeUrOfj8G7W+bLywrK1hLX/zJLU+tpb+4c9vR4yfXcBfKDk091LqXSeSRF0sM4/1/OX5pvZSiJbnPzZ9OXde3XYwaTr31z2sIZU71zKbkG0d05PSS5LQ2evquDdMVi64cf7u+TrPT/Me9Z6TQtovuR/NayuO1SnabBwlfY01sdyxMSEVazc2pGMJIYQQQgghLy2UsYQQQgghhFwBYwxWqzAXZ8tTxABbIcLSnxZ4Ji4Xnv3I9qfJF31NV5O0er1Mr4ZlLUPbfspUxzZ7FQKzFLp5eja9jtKxuXC1No2ZkthpW88dO/74IR78Nx9g+717+XXMvKyqdGujTymmMkkl164EbmvZPFenLdun2nrzolYp28bz0BNyWYnh4vEulswzx8TxW2OVfefSwh2xW127Hkc9xkUCtDxPeW29+9G4zqbcLh5T9toqrrF83enHVK635optfzbX5YqjZFWiNaZiG392sLCr8Me5YwkhhBBCCCGUsYQQQgghhFyRJEXKL9p1OdrDacz50Bm/xH9SXGc69pjnqRS6S18Xed+6NHYuc2dKYzsPbEfAqZKxx9yLUoRG52VUl77Imx9ajdESwDPCsXeN3X2H2huP6+B5j6UnTQ9cdzepahrX2znX7P4lfXv3B0V777GUf0uuQb/mZ57HQ6+R7nNaXE95Hl3GuyV3YRDFrZ47lh/jhBBCCCGEvNxQxhJCCCGEEHJFrE1zcba+bS8lWN3WTtX2eR6/0X/S88k+HlV528YtbiVdDz9vWirmYv7wNdXXWErY8jWkt0MKFs15ZCUdu1rZ9Nqd5FWWzOyIttkkY0tQIbVlZV7R6NuTtWV7S0qWMq285sbb9FAitpuGvW6KcbPnYu6wmeuvHrPp34dDx8n6XKninjgvz5elweX1UL4u0Hi9NF4/2fXq12AhYpup6/K1qq63l9aWNGzZp5eOjYlYq85BCCGEEEIIeSlZPe0LIIQQQggh5HlE5FqJ9x6Q9FQpsppfyLfsCL+5f15piduiByQ53T5e9udLD+AHj/a4fzliHD2c8xj3Ds4B+73H6IC9MRitwX5lMQJTHw/rPE7dHsZrWdsQw4e2s10Nkdvq03tZF/KxPG9L2pXnrcYyxVgz4nF2DtSeEJ5j6Vt27rcJJt9vYOCNz48p+hwc50B/Y0z4zOr0MzDTq0/1h8/HR3HdZZ/GOFe6/mJd359sfJP6Zo+v95i1eAXycaS/8ZnQ9sandqQxpL0Us8PJgNWdFcbzEX73bP9AhRBCCCGEEHL9UMYSQgghhBByRXKfM32rj3K+WCgxq0vJmmyc6y2Z+zLQExrHiQ7vvzgxMuv/4utEXkc+E7veA9++t8WPHuwr8ee9iC8Dv7LAAPiVhfcebuewHj1+ZjfCesBaD+cmIavTjJUPPfCCNP2/ZloVB9Zb5yx/z1BsV6nmnhDunK/ZVgrd3nFz7XPoY1ovvSOFbBSL5bFL5GopLKc24wtxulDUHrx+fb7WgOpa5LjmtaBxjsZ42eNCcV4lWgFkUjWK1enHECJutZTV/Yw1SepKiWKfUrHGGAw3BtiNxeXHl9jv9vVjJ4QQQgghhLzQUMYSQgghhBByBK++usE/9U+9h5/7uTsYhjxdqL1eKV1zb9Rqy/c9PZJUXt7/STN3Dr+gT+fIlg9a2Pa4/N0PH+H7d7fwPshU5wH4kGb1PiRfnQe8m1KwzuPhymJ1U/0nnJfrC7IrStnpOO89/ElIxj7cDMA0lqRqd3sH+yf341girrzzWN9Y487X78AM/deCMe1EbNhZ99XJwkzYln/ZMKYtZbsXlZaLyhPPXPO1Sti5ccrXV9HeFLK6e0fIVpIyd/3t69FvfxGMIkRL2YtiX/lYiv7G9x9H9RjLY6EkbUvO6hSrvlajjlciNruWom/808cXIrYpZicpG/fJvLGDgfGG5YoJIYQQQgh5iaGMJYQQQggh5Ahu317jr/7Vd/H666ddqdObW7S1/nwnYq9iKctjvsiSnUvO1e+zRMp6D7hJqo4+bI+TbA3L0P5Hn17i7/74URA2HWlYXc5gsRpsEm9awKKQsSJoXVienwY5Cxf2eeeBvYf/8FEQt87Dj2Hp9g4nr53g9tduh3SiVWVd5XpLRLAq+ZVJV9Uv32ykWVvzj3b6du9bh9nyxL1ztM535LGLS/QeOveB5GvrmkoBelTJ4Na55851ICl79DFz68D84yrPXQj9eG+KfTJOJnZln0rS6rLEmcS1gHEmzjMrpYq99Qdff4QQQgghhJAXE8pYQgghhBBCjsJgGNpzbsp8sYDpSthng0Pp1yXp2McTm9fT/3i8T3+989b72+O02I4O/5e/8zF+cn8H2JT+TP7Uw3vgRxcjVmerw0/DdIwOAGsZW26XKdlSyurt2CYp2jFsu9HBjx6f/L1PpseqJO4YhNIbf/4NnH7ptEqtdud0NWqfbi+PK+kkYjOp1UvBLknQlmP09nfaDsrbRr+mfAw76vZeyrXc30rHtkRmb9m+6OY1SUq1e+1Fn3jtrXNretdcXhNQly+WfdMxVXlhdS3h/Zhfj/FJqEbRKsfD5HPEqmRs2RaXOhWr5o5d+qMBQgghhBBCyIsFZSwhhBBCCCFHEhJPeVtLzrVTUE/y2/ieRF0iV1vHYGa8p8HTOq+6AnUJu9HjfOtS6d/R49F2xD/46BwffHYJu7JJzBTzqBpjYNe2Mb46gfguEU+tbf0H1G0O6by63QRZ5J2PST5vgpS11gIOuPjkIkvM+jGkZmGAV3/+VYy3xpj+i4/RAHZtMWyG+DBKYVmK1FLoZvvKMTpytj70gFg9tv+xSd2SIsEZmhakSEshO1OuuMnSBG05v6qWv6YjP5eer9FWnm92nJ6slnG06NVj6+tWojYbc07cFyL3UN9Df/q9Et+XhBBCCCGEkJcCylhCCCGEEEKOIM0Fq1OWwW7F7+wN8GSk6zFS9TrSr9LvKrSOO2as6xSvh8fqpWFj6LSx83e//wD/+d/5JCTeLIBJ3HwyAuub69AOxHTdwacDStDpJCx8SO6pazEweTK26NdMxqJIyOqSxTo9K0lZN/WRMsbOw46hTPLHf/djmJXJRK0fPcbtiNf/kdfx9l9+uy43rKVrmUSVNiVqMwG7VLzOsGiu2AUi9ljRW/VTcjVs+rzfIZnZSY8unjtW2so5U/VxByRulKCF3NTS9mBatxoUWao1JlK9Khus2oH5ayzH1dcp65KIjUnZ1o8DlJSV13QsTzwlYOW6s4SsJGIHE37sYD3Wr60x3Bxw+fEl3CWNLCGEEEIIIS8LlLGEEEIIIYQcSUw4KZbMJ7qMq6ZYDx1zlXGvytKbcZ2id6Z3s7uf2Z83OO/x0/s7bPcOzgV55pzHD+9t8cHdy6oMqTEGdmWToFpQpzoro6qeKu99KqFaCleo9J/0wySK3HSMS2PBI0jjMklbP+RcXpUJP2ewe7QDHOD2Ls4z6/dBxl58coHLzy5j/5M7J7CbOgU8y5x8nbufpfg+dOsf5y1x1WMXplWfGJ3zz6Zj545ryN7eMXN9F81l25HMrWs1Pk8S63LJWelkGbc4T3Z8Q9B2xX0jDWuMgcH0uQCkH2oQQgghhBBCXgooYwkhhBBCCLkyOhF7TNI09ZNEZu6XdJ/WuDpi9jhf6j8JQduTKT3b19rurT/+teg5Y733lYjNqgRP6w8vR/xv/8YP8YPPLkNpYRPE63b0WN9YZ2k4AEnC6mRn45J0Cjb2LZK4UbBOCddsLk6VghUhK+3e+ly+yqW4JG69nZZFIjaKqEZSVtKyZgj7zRDGs3sbkrMnFvc/uI+HP3oYShoDeP9//D7uvH8nPfDefVL7l6Zdl0rXqjRyNkTjeZrrt+B8B5lLrs6kY4+aOzbrckSJ407y9iiukIDtpnQPrOukbJbYLe+LyS9IlzPWwlVeK+WYrTljY3+L+F7TZbu98Sk5r+aR/cJ+F0MIIYQQQgh5JqCMJYQQQggh5DG4aiK2lLC1kL3yFWFe5M71v45zf5EsFb9Tq+/vK5OyP/jsEh/d38IjyNiPH+1xd+tgp9tl7JR2G0wmcADM386sm6mkkUgpYybRWm5LiWIkmavLucb0bHkRkyjSAlbEnrGmkr9aChuYkLAtw61yCgdgCNsWQcruL/bw+yBxH334KLs/3nusb65x88s361LGM/eru162HfFyXix9e9f0OBwrZNHpe8R5rjxO73pmxPHivuj0PzTeNdMqG10J26K/TtfGRLZ6HWali3UbIYQQQggh5KWCMpYQQgghhJAr4VW60sNPEmvZF+1zadcnSe8cOnJ21XGfxP6lCVl/RJ92vzT3b+Cv//6n+K9//1PYwQIWGD2wurmq5oUEUCc5e7exTMW2LqlYxjk4PfJkrC/66j8gCNLeUgtgn/a1koKxVKudUrXGh9StS/u8D23eefghLM1o4vqPf/vHgAfczsGPHvvzPV79+Vfxi//mL8KsO6lU1TYnTGfn4p0TtnPrvfMs6Hc0x4jG60isHkNxjkw++k6/jnDN5pLtpXRVvyw1C18f05C43dRw8QOILCErT6tKusa/RvpWzxkbE7LIj4uJWDOlz9Ucstl7lBBCCCGEEPLSQBlLCCGEEELIFZBkq6yn9lp4lv3yssZLJeyhfnOi1Szot+QcvWOO3b9Uml5lf9kvLFsJZl2y+Kf3d/jdD+7DTf3+5O4WozFwIloHwIqA7SRh56RhNj+lVxKxk4iFQVYyOOuDThJ2OkaXKZbUa7XU6VhJy5ppHKuuS5Kx+jw9CdyTn3skgYxwTdv7W3z0tz8K1+w87nztDm69d+uwFP2iUoVPIbx4dOL18ID91OpVZe7jpmyXnHdG7vbKMGfHFv1Kibw4haylbO+jukjCyjIKWNTJ2OrzgxBCCCGEEPLCQxlLCCGEEELIkSQZEBKxbTeQ0rL5sfV2KlG8RJxepU99bYvim4v6zLFExLbioceM01s2jvA+E7C6/7c/Osf/5m/8CM4AdjCANRhOhpAIFUGJQqqgaJuj97A7ktMjyVYtWeM6TEi0qoRrbC/bZBz9J2WHdZvg0L+tpr9PUo3eevjRh9SsS8sow6zB9vMtvvNffAd+5zFuR/zcv/hzQcYeQ+O2t4R48/k5woU9sVRsOkFfBs691Y5Jyl71HEv7ZN3n57VdfMzS69FCtGxvyWi1Hn/o0LjeLKVbyFs9B20pXrOErJ3mjFWC9mmIfkIIIYQQQsjThTKWEEIIIYSQK6Clnki+Rq9mejYdP5WwnMRt7oy0MF0qXHvHXEXYSp+rcqyIvcrxC6/E1/f/0XbE/+1bn+Du+QhY4Iefb2HWFtYEWZgJFKBOtwkLb2E8VsmzmHwVETuXhm3NGSvp1zIpq/qkyyhKvtqGpJXrs8W2QShLbEKatitqy5LIRWpQ5pM1PojkAQOccYABPv+Hn2O8HOH3HmYw+PJf+TJOXjtZdo9fIK49Hbv8xI/3dn8SzFxTt2Qy8nvYLIlcSmxZ6r/GtZRSNit5rCWstDeSsjAALLB+ZQ27sdjd28GPz9qNJ4QQQgghhFw3lLGEEEIIIYQcQS5hJSUrQjXvUxw5tfWStIvOjmQSWsslxx5quw6uImJ7CdmrS9ky/eo84J2Hcx6Ptg7/5d/7FD/8/BJ2bWGMgVlbGCVO9LywTRErtG5ruU9LWOSCNexO8igOI4IVqU8lZXVqVvooUSXzvGZLKKGrJa0uYazKE3flays1e+geKSnr4GCNhbEG9753D5//0ecYL0bYtcUbv/wGTl49mR/zSfE05O91pE9l36FSvkdd1hHlh4HqtR/nOD5mrN741+ksyx9UZMHXYh7YVgq3FK96TJWSzX7MIXPGGmB9ew17YrF/sKeMJYQQQggh5CWAMpYQQgghhJAjSWnYVKZY5oFN88HmbVKyuBS2tbgtzdaxwnQuEdsTsmi0H8uxKdcl+5dK2UPLcI//y7/7CX7vg/swg8F29Li7dxhOBpjBBBlrixSsSsnFEqQl5e0t26RdS1gZy9fJV5GrkHSsbMtxSFLWu1zgalkrf94maRvnhZ1ka0zVytyxeunVXLIuiVo4xDZMctt7Hx+Ht1NpYlm6qWTx1Ccup2v3NvQVWSWP5Xv/5fdw450beP9feB/rm+v+PX8M5ub4fWE4VvIeELmZ6Mf1Sd/qtyWtfWUbEEsJHxzP5I/tkBiWvjF1rqRrdmz5YwEtaqdSxsaY/PNkSsdK+p4QQgghhBDy4kMZSwghhBBCyNFo2YpKSGjBqnclOdv6Bj4YA+m/ZBrS5dd6SMhKOzr7lpzjmH1PQtzWjM7jcufgPeC8x//vhw/x//4Hn4ckrDUwg4Fd2eacjpl8LW5Jd47YqbknqLQ0Xdzm0/my0quxxHXRrtd16WIRaEVpYp2SzVB9YDA/j2wrIauElIOD8QbOpyUMYL0NZYo9YAebhK7z+OwPP8P5R+d47zffC7IcBmZlMGyG5r0lzyHXnXZdek5ZbYjnLOnaOtw0RKzaV52nIWdlfdFc04QQQgghhJAXAspYQgghhBBCjsLDOcBJItAniVqmXFtli8u0rKRrwz6vvqDvJWQPLVGs97aByiZk+x6XJWnXcvvQ+lxbvfzbf3wf/9nf+CHMysKuLX58f4vV6QpmmOTrJPlglXw1HalSrLckqpw+O17d+tYcsZKOlRRslpaFSsYqkZolYIuHHtOKeh3I5pPV+yQBC484V2wsVywljV2ektWJ2CopqxOxLh83vkwtAAc468LYg4cbXSjXahHLtu7P9/iD/+MfwBiDcTfijT/7Br7+W1+vngPynKEkbJSbS9Owcx9PKsku78ODSVglTaMsLX9UoF+7uq/8wGH6EUe2rfqVQhZmeo/bmcdCCCGEEEIIeaGgjCWEEEIIIeSKtEsN+yhZdR9Z18u2SG2e6cD+Vr9DQvZJchURe31s9w6fPtrhTz65wO//6CHM2qZyxCuTkrBSFreUsFrQ5Aa2ItuvxasIppbAneSqbs+SsT5P50Wpajr9ZR7ZouRxlPu+TsbGEqxIZYzhwvV656NsysoVyzFa8MrjlsfVuCdyTjOYKG8BRCkOTPPITvfMw8OvQnnjBz94ALd3GC9HnH7pFOcfncehN69ssDrlf9KSI2lI3ceaz7Z3DjV2mYJlMpYQQgghhJCXC/6XKyGEEEIIIUfgfUjFyl9ItgJBwkri1StJK1JWp2HbIlfGCd/Rt6TqsanYpQlZFO1XYU5ktPYtScguTcrmY3334wv8L/+rP8bnFyPWtzdBwtpcwpaptWpOWCU+9fbcw8sEqjf50yD7lSwNu3KBmUnTMjmrzlfN11m2lSlZlb6W7Uyu6kSsLI1Kxvp6HlmdkJU+3oW0rKx752GtDRLW5vPPmtGE+WRVMjbr4zzGYYQdQynpz/7wM/zO/+p3MF6OGLcjfunf/iW88+vvzDwx5LniUPJ1ps/sXLe9lO2h/b7oqxKuck5Ave8b+6UssXzuxB86SDshhBBCCCHkpYAylhBCCCGEkCPJZSsQRGyZkG0lYbWczaWsMalcsTHpmBSeump6dmlCtjQPx/AkROzx59ruHb77yTn+8CeP8JMHO2wdYhJWC1hjG2VEp+2wMP3bMCN2orj1StCo2y2CtUy5hm4NKQvVR503k7rqHCJX051KqVddnjj2VUJW949LmwtYeATRCiVqbUreGmdCmWFZN8hKscbr8Uh/8hh9/liMN8AIWGfh4GBXFm7vsD/fBxl7OeL+H9/H6eunuP2V21jdOP4/bWfny/2iRdk1hjJfOpZI3Os+R/n6mNvWKdlSxFLIEkIIIYQQ8lJAGUsIIYQQQsiRSDrWe0nH6pRrKlOs253zk2Q1qp8+LqURp7NMy17ydS4VW4rVXiJ2Tso+Lr1xlorYpUnZtPz00Q7/yf/9A3x4f4f9YGFXoRSuCNcsEQvUidhCmnRp+Ttt3EUmSl9fyFVdOjgekvrnr4P6oRvfmGezERYuk7HxGCVCRcjq9ihpi6UWpTohK2WVvfVZAjbOJzuGfdaFhKwzLh3jwj45xlkXjjUAhjCWHS1GGxKybuVgBgO7tvjef/U9fPD//AC/8j//Fbz2C6/1n6/niKWlcheX1H3eJW9DtM6mYK/rnNmm+oFCdTH5evnjDnkft+aOZZliQgghhBBCXh4oYwkhhBBCCDmSWsCKXJXUq07FShlj00zKCrp/Py7VE7FLonytPo+Thp07z5L2x0/Heu/x9374EB/eu4SxBncv9ri7ddh6DztYwAaZF2VsqyxxUXI0svSWxASzSqTqOWFFYGqRo9OfphZrWeJVXU8UUOpYfb6stLFO2qokLIBU9lgSrXIvyjlj9VJOZlEnZKcUcBTQDikRW8jfeD7Xls4yhh1suk4DWG9jIljuw3g5wu0cPv47H2N7d4s3/uwbT3YO2SeVmF36llk63JMUlS8R8bW98Hb25p2NKfJyP10sIYQQQgghLw2UsYQQQgghhByBTrqmOWNb6dhS1rb2SYI2GDQpUyxCNniqpcK1l5TFzDaK9ta+JczZita+Q2K21V738QD+87/zU/y//v5nGDYDMJiYmhQJK2nYKhGrJWCRjG2Wrm1enW8n99TxWsDGOSajwVX9HydIPLfstZX7Dnl+6eNSm6RbsxSuaRwHdYydkrLGwxqbUrBj2C9tkpoVOQwAfvBxDDe4sNw7fOc//w5OXj3Br/0vfm25jF36doISyEuOewwWp13TAe31x7mGF1Xktp6zVhGBq4zZSNLqFGzsU/wghDKWEEIIIYSQlwfKWEIIIYQQQo5ERGyekNViti5TnLZN0WaifE1LwBhJf5ppvYhaduVryzCUBmnOQFynjDlWxC6Xsr/7/fv41g8f4HufXcKsLbDK5WtMxNqGhC1LiyYrehRRoippVz4dsaSqdr+mIV87T0WcexZ58hVAPnfsdN4yJStyLUvCSpnkMi3r1PiNP++mcsLlfLMyrjcpFVtKYKe29T0qnotyabxJ1zuGx+vggrT1qrzx3uFP/h9/gtXZCuPliNtfvY0v/5Uvp1TwoZKwByRrd37Zx+WYt9vjvDUPHdvZXwriLN39RfBFuuFDT29nf0yfFxcbk7AdEWyswclrJxgvRmzvbr/Yx0oIIYQQQgj5QqGMJYQQQggh5Eh0GjalY1viNckwLWHDdv4nbUAqa5wnY/X6nJDtXnWjz+PEwg6d69i2ZVJW7tHf/uA+/k///Yewaxv+yrLEdpIkU7lcLWG1nI0cewsaty4rO6xud1PizYk/XY5Yp2ZNvj+Ts0YJ0en6ooRU8nZWytqp3ZlczopIFcmqBW1xzti/lLGm2HbFchLlDi5bipAW4WXDExvmpwXg9g6DH+D3Hn/8X/0xxu2I7b0tvvybX8a7/9i7MMOsYb3aS/+60rFer/ruvkPHHtWnfF4eQwBee4r2SctI/TuVx+ijS3NXx+plua9IxJrBYPOlDcbzEbv7u/BjA0IIIYQQQsgLCWUsIYQQQgghR+C9xziGP52QFSnrnIed0oPOYRK2uah1DjBGtwUDkM8t66eEbC/dWl0Z6lRsz4T1pGxr3xLmJMKx6dhee1j/1o8e4K//waf4hx+fY9gMMENIw5pBJWJbElbEZiFMsrYrUM4DCyCbz1Wfa+k4VYpTSzud8lRCLUrR6WnX4jWmQ0Xs+vzYKFZ9nf6Mc13qNKxaltcn885mSdjWUp6TSWzJn4VNSxvObVxK73oTkoZxnlo77RvC+88M4THe/5P7+N3/9Hfx9q+9jS//5pfze2n05kwJ4mP6HsuxInau/5PmquJ3Qd+rCN0rS+DruG2lrC1fA3NCVtrV55Gx6bOKEEIIIYQQ8uJCGUsIIYQQQsiR6Hlj5+aFDX191pbmhc37i4QVSavPJebHxDRWLxW7ZF22gbYxuE7RcxUR2z7GOWDnHP740wv89T/4NEhY+dPliU2+bEnYroBdEi4u+uoyv7HPFSRdVSpZvwamcsQA8qRrbERelrjc9ml8OSaWT57OHZOwCKlT4021hEctW1VJYhHFHj5J0qJMcnaMR0y4VpJz2h+fVyWaJe0q996ugsAd1kM4v/O4/PQSH/w3H+Dk1RO8/Wtvp/T0sTwJIfs4adTWwYfkpv+CBG7r1HPitBT0zypXle768wgmvTdQvFevJWZNCCGEEEIIeZahjCWEEEIIIeRIxtE1k7Hpz8A5D2MQU7BpGdqDdDUxSQtI2WNM5YllX1hvy9VjhSyQm4XHTcSWHJOQnduu17/98Tn+s9/+EX7yYAe7sSEBa/M5YpsyFuimYoWD84nGK+k/vqvOOztzsiROJdGq9mXLVlsj+dptLx3/3F+ZDCxLEst+JW0lWVv1QRCqOvGaXaMLZYm983Hp4GBtSM3CJJnrBx9E1yoM4lYOsMCH/98P8fkffY5v/MvfwNu/9nZ+DXKfzQHB2hCy4XYVzwvQf/6bjnRerHbbriovfbl5YKAl13Jd51v6GK96TU9R+MqPHcp1nZIlhBBCCCGEvNhQxhJCCCGEEHIEIkyDdEUj+Zqvi2RNS5P1SwnFMhmr29I6IGVqryJkW9u6XTjWDhwjYVttdR/ngbuPdthP8vqHdy/xd370EDvvYYckY2GRkrFAmiu2k4oVlgpYjT5Gz+taPYRrkiutksGt/bq9SsA2tg+t67FimeLyOSrvqZ3EqvO5fLLTGGqu2vi8WMA7n56raa7fKHJV6jbKW3mefVqXRK8dLDw8/Cp0HjYDLj69wMMfPsTbf/ltvPqNV7G5s4FdX09d2CwlmxoXHdfZcZWL+MLISnL39mHm8S07ydX2PS8UH9mPWyadEEIIIYQQ8nxAGUsIIYQQQsiRhGSsg3MO42imlCxUMhYqGRsMgl7qpCwQJJS1rWRsErEhIdsTsGVkcW4dqK1GKwr4OCyNtfW3H13u8b/+69/DH39ygdWNFbbOw1kDa0woN1umYW0SfIASp6bYVm1XQqRgKXM7qdvHPU/c9EV6c1pGIVi2qflc9fyuzbljtfBc+rcgIWtckYhF49jp+cvSsSYd70K0NvaVhGycO3YMA/khX8Y5hAcDu7L47v/1u/jgv/kAf+5/9ufw+i+9Xt3rRelY1O09QV5ypRRq0e7Dk9sfIqtvPnNu315Wz1Wrb+v6Wv1b/Z4gV55HNhvk8YdYhPpMyhLhhBBCCCGEkBcWylhCCCGEEEKOwHuRrZiSsZKE1X86/brkLx0TzgEAfkrFmtgnT8RWV6ba59bROL7XfvTdOWJfe9t74EefX+An97b44LNL/PDeFqudS/PDlmWJJR05yVGditXt086cyqfWSdPqUg/douiAj7+XWfKwCDNnqVx1QSJW47yyBrH0b9wuErCxP9rpWH0fynRs2UeLUg81Z+yUjo3zY4YYYLZtrAEcopAydpLEUyI2zlUr90SWUx/jJgkvr4EhLY0PEhYO8GuP7f0tLu9e4t5372F1tsKt925hOBmq+39wPthee+O5WcxSEXuV8TqHLRnv2uaZfVK/7Xjc45dK5ychadVHOeeMJYQQQggh5MWHMpYQQgghhJAj8B7Y7x32+zBvbEjJ2mk9nzu2TMbWc8bKX5pj1lpJx5rYP7SF9GwSsiWtNGxPwvbs4nVah6URudyIjN7jP/v//BC//d272A4Gqxsr2NVUlngwKQUrc8OKiJPytaWEVbKwkh4dB1IlUFW/svzvIcGb9S1opipnUpmSjIyPTSUay3RsXJZJVJ/3z1Kzbnp96XaP7p+Mk6VzDVJK1uRj6usw3kRBG/9c4/r1PdVLNZ+sXLsIXCC8HqR8tbxuxt2I3/8//D5OXjnBr/8nv45b792qfrOwWMiis+8YFv52oVkeuPy9QCsZulQ26uOvQ0rKayRrSuO3JO/B+XMP/Zajc95nCkl8y48d1I8VCCGEEEIIIS82lLGEEEIIIYQcSS5eoRKxUElXXyVgncNUjrhMzgbro9uM0XPM+kne5pI1r5Q7Y/Fmk7HXZZbK8Q6119vf+ek5Pvj0Aj+8t8Wj0WNY25iGFblWiVgtWU1bwoZFR57O0bhNlTzJgrfHiZW5RFwrJWu8SpnqdpVk1WP3ts30mpJ7F9tNnYgtr7dKyZpiiZSOBcJzJPPI6kSszBWrk8zeTrJZ30eD5tyxIl4NTJ2MVXPJGmdiqtp6i/3lHrsHO/z0d36K84/O8fo/8nqaQ7b3Fuo9TVd96xwShteYzjw4P213d2aD6/3e5z8SWHYxs2MeTUfs6h8pPLOUv5chhBBCCCGEvNBQxhJCCCGEEHIE3nvsdiN2OzelYk2WjB3HPAFbJ2MNjEnpWAAxEavnjHUu7LTWx3VjPKwN3+KXU5bm3+4fkrBzUvY6WWKV0vZ//fc+xv/5d34Ce7rCcDrArvNEbDZHrFq2JGwlZTXHCBB9q7T87AjZ2FQ/Qf1TaGmkxase2yOVFp6kbJROU0linUCNZa2VmIoJWKNSrzKOpEonGZr10UlYaXP5tUVh6pDKC0u/QqaKsI3JWbmfkoxtLbW4mpYWNl6nlDiWe+RHJazlVg6hj9s5fOt/9y3c+dod/GP/6T+GzSsb9WQgK+EcH9/c03ldb52ZNOlsv0NzxRbi9Srlh7vXos/bkqOHPgKK9Wpe3Kve27nj/IL96Mhc32m/AvpHC4QQQgghhJAXG8pYQgghhBBCjuDRoz1+53c+xpe/fBN/4S+8gfU6L00s878mqSqpVq+SsZKSNVlqNkhZH0sSSwljIE/JluI1n0tW28OlUla3PS5Lk7F1mx8MsLYwKwM72FRqtpSuLRGrRF0zoVomWGcfQS5HZ9OwLel65K1sXU9L0uh+1byxB9rKeWDLfbFdCdLW/ngOEawiO0Wc2iCL4xjWw7hUkriVevUIfaqEbDGHbCZ2leA1dkqP65LVHmnu2CGUM7bewq/CQXZlsXuww3f/i+/izvt38M5vvJNKXR+bkL0OrlnELpGNx15T85xzhx/oV5VGXoqfuT/dQ2Y6Lr1XrfUjrz0KWP2RzXQsIYQQQgghLzyUsYQQQgghhBzBgwd7/Lf/7Y/xzW++il/+5S/FVKwWsvmcsSiWPpsPFiiTsSE5m5ZJ2gYpG8RTSsjmgjbJQW2PDklZ3VbyuJHABRLWiyAzGE6GNEes+oNBlYwFCik7J01bgrZDNv9qkcbM5GtH+B6SvZooZsJGGkqlWmVsLbfi41FiqiVrK/EaXy/pHKVojXKzELjZfKrCjJSNQtao12WROhQJKyWHYx9JWuo0bn3zwvPuQzliN7i4LUsLCwcXRKzxoTyyBYazAdv7W/z+//738favv423f/3tJGOnsZsJ2em5uFaWitjysJ7snJGyrfLDs/PFHpKOZcr0wG8xuvPFFueRa5L+0ufg3LiH9nVv2ReQUNW/j5FtHPd5QQghhBDy/2fv335t2fI8sesXMdfa+9wyszKzuu7VTV/cttsPtrrVkhGNhcAW70gI8QAS4m9ASIh3JASCByR44QUEQljAA8iIB8sYVGoZX7pcRbe7qqjKyqqsvJ7Mc/bZ17XmnDF4iMscMWJEzLnW3jPP7fNpLcWMiBEjRsScJ2XXd//GAL6chLEAAPAIKfVTEh8OabaGbD9NcZpNU5xv27aZPo/51OlYM1TG9uHsWCHbDQHXaVrjWgB7rcrYR5StXXy8P/YHP3wZ/88//TT+8Cev+iB2VwSx+Tqx+XYcdq3iMh4WwC60p4/VKY+bIkR55H0WfeRZWZNm+/m6t1MolYWP4zqwfb5bqYwd2lQrY7NpiRfhbHZ+Wuu1G44NVa9TgNtkoW1WvZqfy7+vKYQdpjeOGALadh7E5uOe6bKQPgthIyLa3RDE9uW30TVdtF0b0UTsbnfRNE3cvH8TL37wIn7/f/b78Wv/4Nfid/7Lv5N9Adn3kL//S/6zOaeaKW6EsFsVsbXzMf/tTKH9uf88K+0uDofX2l0S1m53/KA2a+vIPijIrfS3FtheFOQ22X9H+T9yyKv6AQCAryxhLAAAPEJKsVoR209VPE4zfNp2XT5tcQxVrv2xdgizxmPjdX0gG1P4ulUhOx6PiGiaMZzt2/ceWhl7SUpwLohYTzu6FHHsUvz/Pn4d/5c//Hk/NXEZxJYBbK0yNh9qrUL2IY8zNj0XwK5Uxc4Prd/wkgBnbVri/NwUwI7HsorOWbs8WL3w8yw0SvMK1zFUmrZjgJtPS3waxDyIarIgauyrbaZ1aPO1a/O24/ExgI8mpt9J6oppivuBT++k2fVVsmmX+u1N/952T3fx5uM38af/5z+Npm3it/6LvzX9BrOXfxpH+Z0+JJjd+MofOu3uar+p8tvaCHcvmnJ47fravcbTWb+1Z6vdd3Pcl1S4rgXcK8fLKZ5TuiCs3jqWVtrVCGABAOBrRRgLAACPkFKK/b6L/b6Lw6GLw6GN3S7FzU2Ktu0rZCNi2rZD1V9eFduMFXdTsBhxygf6cLWvkO33a+3H6Y/zv/z60+fY2I/ieKycv8TlacYf//RV/G/+o5/Gj57fVytiF9MSl5/z0LUWwj4w8FgNdotj/WYllL3wvrOpkMtr85Arr3TNvtKxajafzniqOk2ntlO7shovrzZt5pWnizZD8N+krGI1W6c1pTSvlG2HttmUxXn4N4XGQ8gbEbMwdQq18r+IU3/5++36oHV6N8PzN00TXeqmaYrHd9J2w3TFT1LEoQ9jxzD3J//hT+L3/nu/F3/rv/a34rf/jd+efy/5e8+/w0qbS9WmDK40mu9uVcTmv5ut8PCSULjsr3L/xfgvDSdTcb4Y12yK4q13ungV6RQSbwWrlWs3XfDvTRZr92bjKP9bBgAAvn6EsQAA8Ah9ZWw3TU2cV8WOn0/VrrH466ciHs+dqmIj8vZ5JWzEqTI2D217pyC3XEd2PBaz/eGq8qkqT7qVLF6SLCzbHLsUL++P8VfP7uL/8/3ncWyiD2ObPkQbQ9jyrxrE5uHomSB2tt5rzSUhbNnH21S41a4dA8Xx5BioZuciYqpUnR3Lq2CzYDIPEKd+hmcZ206fh37ydWWr0xaPx8bph5usenY831xWITt+t4spjCP1U1SPQW/2/Y8Bb358rMqdxpMFxWXVbNM2/TTGN31Qu3uyi9c/fR2f/dln8Rv/+m/M3mvt+5nec/beL3VR5erW8YfdrH7PyALEMqTdClDf1bi2+i/2H1Qx+5h7zU6l+Xso77UWMD/kftl/r4v/NgEAgK8kYSwAADxC16W4uzvGzU0bt7dt3N52sds1cTj0KdHNTYqI/lhERNt2EdFG0/TbiIjjsQ9QmyZN22YMpsbqsBTZVMT9vfPt+NdfH0OgOV+Tdmgd81RgrUzrbapjLytF++Gzu/gf/7s/iJ+92ke3a6Jtm1lV7LhG7GLa2TyQHfeL6tjZk5wLX6eG633N+sg/1vp+TDBbCWpmVXZjpevY/1itWFTA9l1l4eDYT3b9NM3u2E9aqZLtskA/FeFrGcjWKmTHtmWFbBaeT2Fvyl7aSsVkdXrgsV32fbWpPa19OxwfK2PbaCMdU8RNRNf1a8emNsWu69eOHadIjiaivW3n4znzn8T4fVzkcf9+4ewasdPvIj+X/y7KY5Ugtmy3WDt2bc3V4t6L6tm8Te3dbT1XOebsXtM2q4Kt9ZePoRr6XvK+V8a0FRrXAvDpv6sz/3AEAAD4ahHGAgDAI9zfd/Gzn72Jrov44IObanVs2zbDtMSntWFPVbAxWx82pmrEfBsRcWo7fs7bjIlUH+ie9nvZupyzStmYpjx+u/D1Eqf+jl2KH312H9/7xZv43i/exIt91wdfaxWw+fDG45Htv20QWwauF4S8D66Mzc9fmH3n95gFNykbW4pqZezUPi2DnylMzb/6dHquvKI2ImZVtZuBbBQVsmXbpnK/LDAer5u9j/J7b4bnLX8rcaqaTW3qpy3OKminytlx3eW2v6Zp+3fY7Jp+WuObJppjE+1NG3ef3sVnf/5ZfPBrH8TNBzfroWzxvb21lT4uCQYvmp74sdb6vOzfXtSPPTSUroWol9yzOL46NXQW1lbXwV0JbBfTOpfHAAAAQhgLAACP8sMfvop/+9/+s/gH/+Cvxb/1b/123Ny0cXPTxOHQRdNE3Ny0caqM7eJ4bIYq13kCMFa0tm1fKTvmcG0730aczuV5YNuOQexYGVv/K68bjsR6gvGQcq1z4UN//vmbQ/yP/h9/Gd//5E28ir4Csd21p2rYca3YIUzbrIgtA9R4eLXqbNrbONNn2c8Yil54r+r5WsAzBqz5/bMwcArmxorXscp1DEjHStdiTdla1exUMZuvK9v/XPvjbVpU1T6mQjZfMzavcmy6ISQd14sd24zF42NAlv03sNCd3kO+VmxZGTt+jhSnCtmmr5DNnzki4nv/1+/Fn/87fx7/4L//D+LX/+Gv17+vd1nN+JAQtiz8LNcqzT6vVZ0uqmKjqFpNxX55TdH3rPK0vF/K2qz0NYaY5W+k9ryzY+nU1yyMTvNzi8uLStrV6tzsPZRVuJvr5ebPk58r/61MxLv9HQEAAF9YwlgAAHiErkvx+vUx7u6OcTym6W+sju0rYmOqjJ3v9yFZvmbsWEnbB7Tz6tmui4gY16CNqc0Y5vby/0t//n/xLz+PFZOn/fH6eZb5dtVdef8pRfzxT17FD57dx09f7uP5vovdk91UsTiGq7Pgc6sithK6PqRitQxYN6cnLrtdmbb4UWpVsWNYmodh2deXT2Gdf9XlGrJjQDlNVRynUHW8z1T1mq8rm/2EZm2KIHazQjbmFbKL52iKvvO1a8fjTfEu8mrYSuVrvkbtrJK2jWmq5NSeqmTH4007VMbumn7q4psUh9eH6A5d/OKf/SKatolv/0vfjtsPbxffVfU7vNSZ/7wuCWJXQ8va5637nQk8NxVB5GKK4o17XLSO7ca9tvpePfc21z/0vdaM/23l/x0DAABfecJYAAB4C8djivv7Y9ze9pWx+30XTdNXyEa0cTymGCtjIyJbz7WL47EdKlq7aNsmjscUbRtTpWtEDNW0p89jBey47fsc14mdrxdbr4w9tRmvHXo/86QXlHVWpJRif0zxv/69H8V/+Bcvonm6i/a2na0RO60P256CtnI7jXUtNF0bZn6qCHsXoewDK2Pnuw8IaWcZVbbTnM7la7uW7fJq2dnxPHBdOZZPHVyres3vPVbPVkPXtUB2rIxNTV/xmlfGdjEPTdssJB2rZCOrxu2yMDXFVCG7WEN2DOnG++xi9nzjsTba6HZ91Wzb9evLpi5F27SnauB0Wkf2j/53fxQ3H9zEP/qf/KP41t/+Vr3y+h3maavhXCVQ3JxuN4owvxaWVqo/q31XrqlWwpafV4LL1arcxSOm2djKCtjqGrobYW3/j1tWrhn7y6/feMfVKaHL+1f2a9N3N8P/AwAAvtqEsQAA8Ba6LsV+382qY8cK2XGbV8T2VbMxC067rk/d2jZF1/XH27Y/nlfPRizXjB3XnB2ranunksk+tE1TEBtTmNEMoe4pmE2pXnU6nL34ncyq44YA+dg00TVN7NoihM2qHMcQcBbEjopq2YWtPCPre9rPQtaLQ9jZcJrte24p+4k4hUJNEfSMwxgrXccgLKuw65vPA9itdS/LtWTLsZTVrLX+VgPZPPDKc/7sO56qZrOq3UWVbJx+B3lwlVfEzqpl47Rm7HRsnPq4zYLdYerrfFrlSNH/44BuqJDd9RWycR/R3Xfxg3/vB/HZn38Wv/WPfit2T3eXrUf8AJsVkpUgdnW/FsTW7nMmCF2913hs5fpFVezQdmsa4EUgWt5363921rLrtHHvrM0idK6FrNm41qZjrq0du3Dpv3kBAAC+koSxAADwFrouxf19N1XHHg59levh0Jfp9ZWxMW3bNsXxGBGRV6ee2vSVsRGzssgY2zXZdc10XR/ojlWwy+rYPJQd+8nXmc1D2DF4eEzetCg8Gw50KUVz00T7ZF4Rm4dm4/SxterXzSrWvN3sZeUfl4FvNYQtQ9v8frXw9QoFbXmVXC1IqlW/1kLZxbTElfOzKYzH4CoLX/M+zh2bxjNUso5VsbNq2WI64WjjtLbsOL7mVE07hmrTfncKYPOgNVJ/bpqCuAz74hTCprbvv2mbaKPt+4x+HE1qok3tVMHbPmmj23fxR//bP4qPfuej+Gv/2l+L3dNdNTx9SEB7dnratRA01gPDahCb/0S2pj3Otosq2IeuFXtBgDt7jspYynVaF/2U9yvfTf65FvxuBNrn1oKtjXftORb9AQAAX1vCWAAAeAs/+tGr+L3f+3H8K//Kd+Jv/+1vxuGQom27OB67aJoYpidu43jspy1u2zEoTcNasH2gW4ary7VgoxrCjtvl3zyUPbXr+zsFsUOlYNHn/P71cPbUJq0cj/h//9ln8U9/9Cr+6rP9IoidpiceKxyzW06h5EOD2Dw8zc8X/ZShby2EvSSAfdsqydn6r9PBmL+DFIs1XbdC2anqtQzZs6C1v2UloC0/j90X107PXwSy47TGU/AaWRA7VsQ2WaibHYuIU1Cbvf9pv82C6rHStRv6GwPfcX+43xjOzipixyB4CGUjIppjM6+OTW20N/28yN2+i2a3/T2/k/U/17oog8lLgti1sVVCz817l/3nx1bC3qkytbgmP35RWJkHwpV3UKtYrd5ja/rnWlhbXlMLcMcxFPdOkWbbhSv8Aw4AAOCLTxgLAABv4Wc/exM/+9mb+M533ou//tc/isOhi92uX/+1acbK2HG64nH64loYOw9N+2mKx7vkFbGnwHWcurhp+umMT8HtKcDtpyM+VdOO0xL3od0phF2Gs+O9T+fPmeUXKUWXIv7D7z+Pf+c/+3ReFZtPTxynYHTalpWsay4JYov+8vuNbcpwd606drr2krGcM2awRX+L6YPz8LUWyo7TFxcBbL6/CJBTszkd8hh+nh7tNP3wFKaOfWT3mwWueUBbTos8bsdrs/B1CmrzELbYz38rU/A63mdYfza1ab7W7Pi+i1B22t/1lbFN20yfxzC2vel/u6lLpwrc/DnexmNC0LU2aXm8eq8yiJ0Oz38TayHuVmi82D8zjkUgWvZTC5c3X9r6deO1ZVhchqZ5NXA1lM2C17NDWTvfbJwDAAC+coSxAADwDuz3Xbx5c4gnT9pomiZub7tIKeLmpo2ILg6HJiK6IeRso2m6aJp2CkTzKYMj5tvxr+2zoVkoO27H68cgNyKmdWTnUxbPt7McsjklBI8t+BzH/Xt/9ln8u3/0afzpp3fR3DTL6YmHKYnzKtkpMM1D1TJEnQYbi8+z4DYP7SLm94jlua3+z06JfMm5XCUgHY/n91pd1zUPZfPjcQqqHrK/ViFbm5J4Ol9UtObvcDGF8RCYxviPC4ZgtWlP0xlPx8rK2HK64uF7HdeMnaYrHv/6AvTT2rHt6V3OKmGzEHbcb3ftvDrytv/Q3rZx/+w+fv9//vvx7X/x2/F3/5t/tw9qrxGm1QLY7Hi13dq5B1TEbk5PXLlPHtyuVcXm954FnJUXN1W5bq0bW4a32f1q951No1y5dhEEF/u1ZxxqXtfHtXavDaYyBgCArz5hLAAAvANdl2K/7+JwSHE8dsP6r2mokI2pIraveE3Rdc1UEXva9udTOq0BO+6n1AzVtRERzRB6pkiprzocg9eIsd2yfdNkVX0xhh9NNjVxyj5vVIGuyEOZv/zkLv5ff/osdk93fWVhOT3xSnJZVrWuNFp8Xg1i86B1JXSdVcJeGsIWlaMP0sQy0Cnvkb3/2VTG6TTmRfVrOlWpLvazsY4VrbN7FvurFbJjwFX0v6h+HftsTm2n/svxR3asCHfH+07vrPhup2P5vSp/07TF4+9vDGPHtYqzdW3H3+pUJbtr4nh3jJ/8Bz+J7r6L9N+4QnhWCzqL4+WxrSC2emwtCD0XBm6FnGv3rd2rDDjX7lsGxrUx1ELfsV0RNE9j3lB952l+brpnJbRdDWgjTr/tyvcliAUAgK8HYSwAALwDd3fHePFiH0+f7qJpIm5v20gpxW7XRErttG2asWQvIqKN3S6GytgUTdNOUxPnoej4N6+EHQOwcT+KytgxoD0dr1XErm1z5zLZ6tqxbRO7J7tpitfF9MTtKVArt1NAGqdXVZ0athbEtln7Zn7drI+VEPbi6tsH5q+ltQB3FnZW2ubhTXXd2MiCnwv2L62QvaRqdrHNAs7afh5S1daKjYhTZWxENN3p95Ha4tg4FXE2JXEtzFutjN010UYbXer66YqzaYp3t7vomi66YzdV4o6VtsWLP++SMG4rUI3sN7J2fqW6tBYYrlbJ1trUQs58yt9yrdjhmkVwmurnt9Z7HdvX+pqNrXg/iymHs3uWz5VSWr67Sv+ze2dtqmvGFtct1l2WxQIAwNeCMBYAAN6B168P8emn9/Hhh7fx5Ek7Vcb2VbBpWkM2r4odK1f7ytj+2LhWbF4hGzEGoqdq13FK4byCdiwR7Lpm6jefenhZohgxTwOaWeg7npsfW8pnQ32z7+KT14d4fndcBrBZleIYZD14euLCIrQtrp3ONfPPm0HsY0PYc2FcLXgpvoZZFWjlaysrXadxpzhVvDYxW9N1Cj3zitm4rEI2H9cUyNbWpM23WfvZlMZpPrXxFNKm5bHqu8x/Q8PaxmO163g+nxZ5Olf89raOj7/Xpm2mitn877g/xssfv4yn33oaT771JBtac3GwtlUReu74ufC2/G0s+i0D1ZXwc+1+1Yru2v0ueJb1y8f/3cnutRE+T4fysLUSMm+OZesd5cezv8V6s8X5iwlkAQDgK08YCwAA78A//+fP4nvfex7/5r/52/E3/+Y348mT47BmbDes91oGnUOYNIRKfRVrO6332nUxrRN7WjM2zaYVzteHzachbts+GTutHXtqf7r2NGVxHr7mwedj1o39/b96Gf/Lf/zjeN3Faa3Y3SngWoSvtbBz7b5FSFpOTZwHsYsq24eEsE0xnrUxRDGGWvtRmp87Nz1pk07jrFW+zo7ngWvW9pJ1Y9c+j+3W2uTvLq9sncLU6kPNn2EKadsU0c2PTYF0O7yLPCRus2fomtkasdMY2jQFtpFiqpieKqezCtkmmr4idghUm9Qfa3dtpEjR3XbRNm20hzY++7PP4h//D/5x/M5/6Xfi7/13/970POUavmeda57K3WWV5WoIWwSLD6qILfdr11TOL4LQ8ZraWrF5oFlO/VuGmdl1swrayv70ntaedWhbjrsMffOQtXbPRVVtMd7Fd5BrinPF+AEAgK8mYSwAALwD/XqxXdzdHWO/P8bh0EXbNosK2dPffG3XiP7/KN9188rY/G+seI04VcRG5NWSzXCuGapwx+0YOKbZWrDjGrF5wJsnBQ9dNzaliLtjFz9/dYzYNdHetouK1DKEnZ2rqN5/rXK1DGLz48XnqV2lv2oQWwSJi/PnlJntWNlZhJuz0HHYX1Sxjk3KCtVYBrKzY3kQ1Cz7nq3Zmorr87Axze89VrZO2yimK05FYFuuR1v0N45vFh7nv6NUGVf+G8rHlK8tm7XLp7HO15KdqmOHStiU0qxKNh1TvPnFm3j+l8/j53/48/jgNz6I9//a+++uurEWwlaOrwaxRZvqWqaVbXV63jNjWz1e2T+3VuzmP0644N3WpgSuTte81m/lc3VK5MrnxZTE58ab/e4PLw9xfHV8d78fAADgC0kYCwAA79Dr18d4/nwf7713EymVa8fGtL5rnxO0kVIXTdNOla19VWs7hbFt2/+Na8tGNJW1Yk8Vs/lfWRFbWzu2ti0/bx2LGJ/l9Lm9aSLtmsUUr7MgLLJALAs3q2u8DvuzsURxrhKy5efK4DUPbKf7xfLY6dnX0uL64U1jbl4+R4pF6Dm1S0WoOJyfgvXseG0t2bXK17HvaZrgLCidXZMFreO1teC3vN+sArbYn/rJqmrLY/n3Mu03zVQd23RDFWxZIdtm0x+P1bXjO43hfDTzNWRTnILX1EzryKZdii66aG/b6Jou2q6Nn/0nP4uf/Sc/i3/pv/Uvxd/5r/+d6js4azVD3QgRt0LYMmCNZZh46fS9ZbvF2qu1+5Sh64VrxeZ/i/Vja6FxrV3+DlauW9wva19WzC7e85nx5n3Mqm2beV+z13xM8fIvXsbh9UEYCwAAX3HCWAAAeEdSivjxj19F16X44IOb2O2aOBy6aJpxG3E8NrNK2Yhx+uEYgtgxdF2Go+thav9/8S9D2FP1bF/h1we8zaz/sSLxFOyO7ce+TynB1myaz++O8Z/+6FX885+/iahNRxx9qFauFbuYQjhO7U8PP/9cTmk8C2DLMLXSbxms1qYpPnXRbI/nMfLr0/z4OFXuJZWsq5Wlw7nZurGRhatlv1EJYdP4G8kC1/L7r9xr6qf2vGm5v3ielI0zb5uHssVat7UK2Xzt2PGaWhVs/htKTV8FOwa5+bEmzf9RQepSpEOK1KV3EqSdXYu1zF3Xgti1vsqAtewrrbRLZ8aWYnGfRbtKODo/vf4Cx7GsjX1xjxTz4HjjmWsVxYuwei1IjWJ643IcufL3n7WtVfQCAABfPcJYAAB4h/q1Y1/Eb/zGB/HkSRtPn+6GtWP7/4t8256SuKaJ2O2aLCQdz4//h/42q6KthbCn7VhFm4eq5V9+fKu/rc9rfvTZffyv/oOfxOtjivZ2d6qIbU5/ZTi78JCQcyWI3aqIrVbH5vfNQuNLAtiHTOFcmlXOTQdP95pC2cpapLMq2TzE3ApkZzc/3WM2NXB+jzEQLUPP4cB4bXW64qhMU9xkFbB5KDx2X645m4evxfH8+54FyMV6s2tTEEcTi8rYaY3ZFKcq2d3wzweybZva6NquH/RNnMZQeX/zV76a6q1bC07X2qwFp5WAda2/svK6DDMX68TmbfO1W1fWkJ21zQLU/PpF9WstIK1dF8X9y2fI/1b6GtuvVeDWKmwXVbXFO16zOTUzAADwlSKMBQCAdyylFG/eHOLVq0O8917//8m93/cBzhjGnqpSI5omTdMQ9/tdpDQmamPa1kzX5tMPjwFrPq1xLYitV9b295iHsc00jtFW5vhq38W/9yfP4i8/u499iohhmtdZ8Fr+RWX/EllAVwtip34jO1/5PLt/2Xcsj8+G8BYBbK2fWaA1dl0LZccgMp3azSpfzwWyeYiaV8vmYeoYzkbMr8+O1ypzV6crzp8jD4Xze+dhbTTzCtfsHSzG3JzGXA1sxz6KILZWGZv/Y4FFu/EfFQzTFo8h7XiPj//g4+j2Xfzmf+E34xt//RvZoz8yaFvLbC8NYfNz0+teBrG1NWKrgW0ZoNbGWwSus1A2C0JrVaqL91QLNGuha3mf2rTF5TbmQetsf+OZFqFwGehmz701RfFpbe9YvAcAAOCrTRgLAADvWEoRb94c49WrQ3z00TGaJuLJk3aohG2jbVP068P2C1yeAto0hatjKBtxmjK43J6rkF3+ncLXc9Mdj8Y+11LTl3fH+L/901/ET14e4vbD276KsJn/TX3F+JzNbL8WhC7C01wWrs4C17y/Sypi8/7GkDc7NmtSC2Efm8sWAc10uKyWLcPMPJDNj18QyM6GXQSjeUg0BZprx7Prq1MST0ObV8lO98qPpyyATfNrI2JR0buohq2Er2WFbBmwTuvNdmn6fU7TE6dsDdphWuJIcVpDdlxPdtdMz/Pxf/pxfPxPPo6PfuejWRh7sY1QbjX8LPbXqmH7zUYQO2uelvuVMHPWR3mPsl12LO+vWhVb3q927/JcMY6y3eo9K30tKl4r91rkxrV1ZoWsAABAhTAWAADeseOxiz/8w0/iO995Ev/6v/7r8dFHEbe37RCUHiMistC1jabpouv6gGe3a6Jp2ilwbduI3e4Uskb0oW1EM6usPU1zfOp7+TcGUGM/9UA3356kaiDZdRHtTRvtbTtbUzOvjs2nKF4NWc8Fm1lgWq4Z239slveL4n5l6FoLZ2dB9DK0PTfGhVo4U7abwvUimM1D2XHsKeYVpkMgG03MAtgykJ1V5eUhbmTn80Ase6+LisZqLl1MQVx7B8XzlO96Eazm0zHn39k43iyE3prSePru26Zf4zVO10/TFHenoLbphu04XXG+3UW0uza66CK6IZhtT+N6G9XwNX9/xbGLQ9j8+CMqYqfrKp9Xw9FyfOOxcvrgYqyL6tdxm1enlmOo3aMS8s6qdFOa7ZfjOzdN8ewe5bHC4r8J4S0AAHztCGMBAOAd67qIH/3oVbx8uY9/9V89xJMnbez3u2iaJm5u2mjbLto24nhsomm6qTK2m4KiU4Vsv3/qe6xwHcPV/lifBJUh6/h3CmibIZA9hXNjmz7MPU1PfLrnKbwtw6L9McXdoYvYNdHu2imUaprmFJhGzAPU2n5hdSrgMlwrgtby2nNB7FY17FpAuxjP9HF9zJtTseb9ZMfL8DQPNBcVqeP5YlsNZMcAqtbP2H80y2BwPF6EfLXwdbFmbDkdcVPvfzbGyvc0TadcVOyWQXQeNuf/EGBc4zYitqctbrP31p7ukYeyeQVtNBHdvovj3XH6RwmXWg1gI1ZD2H7zDoLYqPw2K32Xa8Pmny+enjgq150JQqvPVwaY+fVR3D8fVy34rD1fef/8HuUYKsesAwsAANQIYwEA4Eq6LsUnn9xFSimePGmj61K0Q9Az/t//u66dbU/rwvYJUts20XVN7HbNcO1YEdtv2zaibdMUpnZds5imuOvGz3llbFkl26dYywB4DHnnAdP+mOJ//09+Fn/2yV18etfNpieehbKVwHSSh6TLW8wD1jyArWzLfh4UxFaC3LXxbIXI1VOpcs0YbpeBWhHKztaVzc/lwWh+7IIpi8s1X/N+ZmvDNpXjWfvFdVGvip2mIx4rUbOK3CmkzdvEaYrhRSjb9GHoLEAd24zfc3tqkwesY7Aa3endprafljiaOAWrKfoq2bHKO2K+TdH/1lMfyEb07f/0//Sn8Vf//l/F3/vv/L346Hc/qvwQLrCW45Uh31qoeC6Eza9NRfuVwHZRETueS8u2U78rUxjXQtbNtW7LqtRI9eNZlWtZsVoGprWq1tlzluFwvu3Sop/FdV1cZLa2LAAA8JUnjAUAgCs5HlM8f34fNzdN/MqvPI22beJw6GK3a+J47IbK2BTHY/9/yd/t0nRdvz31VVvvNQ9bxzZtm7LwNWaVsWMQPFbGRuQVuHn5ZUzTHvefY7r39Gxdiu9/chd/8vGbaa3YWeBafn4HZuvELvLN4v7ZGC4JYovOqvtroer2oIv9lJ+qrAM7XpO3W6mSXYSf5wLZSt8Xq1xXnZK4di6/tgiF86A34sw9mpg/U7am7BRc522L7Wyt2/wdNafx5CFvHvSPQW/qTm2m+zYRL37wIl7/7HUcXh/OvsrJue+hDGFr15wJYqvX1vqN7NiZELfWd3WK3q22+flasLs2/sXrWPa16GN43rJtudbtIiA9d7+x7zGYPfOF5u+o23fR3XcCWQAA+JoQxgIAwJW8eXOM//g//nn82q+9H9/4xpM4HG6GStcUXbeLlE5TE/dBbIq2bYfjzfS32zXTmrH9GrNjJWxeIZuG7TgV8elY1+XbPNhtpsD2FLaeKmcjIgtgT6li0/Tr4ja3beye7PpKwaGScKyGXawNu7YtP5ea4i9rX4ZleUi2GsRWph/eWse2nF75rZV5bjrdu7Zm65Qt1gLVMUTMA6KVQDa/bjFdcaXKdfE5m9I4r5pdPt7KmrFZkLoIXscx5KFsFpCWlbJ5RezaurJNNKdK2La/11QtO1bEtjGtFZtPOTx+Tm3/jvLK2GaYkrtLfTV46vrxNTf93+y5LjXL/y4LX6e2GwHs6vqwtWNFgLmogs2Cy1qIWW1XWWe2Vrlau35rzdbymjLQTZFm7c5VvZbn86rX6a8b2nUxVchOx/K+z0jR9/Xy+y/j/tl9dPsLS2kBAIAvNWEsAABcSUoRr18f4/nzffz0p6/jO995Gu+/fxO7XRO7XRs3NymapqyI7aLrxqq7mIWlKfVpVjusY3la/zWiD1HT7LrxWLlW7Ph5HONpDdmI+Xq1TbaG7ClpSKn/i+YUwG6FrGOguRp6bhiDuLKPxfTEa/ctA7qyzcb6tBcFsZc+y1pQk4WlU3Vm3j4/f2mFbN5X7drK+TWz6t2NZ9mqkq0FwtPnmO9X17Et75OPuwh5Z+vLlmMoA+EU8wrbPNAvtnnVbNluDHkvWiu28mjV5629gguD2LU1Xt86iF2719S8+FxpsxhLeSxVztWCzuxY9V7FM82C1vI91D7Xzq28y8dMOTxWxgIAAF8PwlgAALiyTz65i3//3/9x/J2/88346KPb6LpxzdgUt7e7aJqI3a4PcsYq2H7bZtWxMa0Z22+7YYrifr9tywrZfv3Yfprivu9+m6b9Mag9fY7oA9xx5PnUxPnxPjhumuinJ25jHsqWIelGRrW1BussFItY9J+fm50vPi8C2zKILY9HcXw+4Ac9x2La3NOJap9jpewicByuWa2QrU1PnAep47XZjWfrt6as2jSKe0S9v6nt0Ge5duy0HacHboq1YdNpOuBZuzFQzUPg8V7DM05B6HjvcX+sri0C0/IvNWk+7fCwHux4blz3ONr+r4nmtGbsWAW+y7644XhEJQhec67ZVpBaa7MWwq5MAfzQILYMOvP71apgx6By7G9WUZufL46V58p1XWtVrVvVr2Xbrf1LKnLHCtnFfVdcNIU0AADwlSaMBQCAK0spYr/v4u7uGK9eHaJt+8rY3a4PMA+HPnjd7fo5Vdt2DJjm/xf8PpRos2rViMiCsylbbGKa1nickrgmr5JN6dR23s8prB3v++fP7uOnrw7xfN+dgs5+KFO/W7YC2KzRPMDMA8vy+iKUnV17aRBb9l0Oca1dbdyp3q5c33U4uNLNeuXqWiC7uT2nGM9sKuNKm3wMm1Wxw3Wz4DWl0/Nl96yNtdp32a7yO8mrVhfnm5gFw7PgtxL+19aOHf/xwdR/E5G6FD////487p/fx3f+3ndi92S3/k7WpHL3fAgbsRLE1gLAlSB2dUrj/LraV1weT/Vj1WmOZ01SdVxrz1JdK7boa9FfbcxrIXCsB7LTrR84RfHsOQSyAADwtSKMBQCAX5K7u2N88sldpJSGStaIrutDz37K4pimLO66ZrZ2bB+u9hWxY4VsHyh1w1qyY2XsqTp2rL4t7zWuIVtWw/bh77wC9jT18VD1FxH/3vc+i//gh6+ifbo7VcbmAdZ08SmMPBfQzmRh6logNjtWXJcHqpdWxF4Swl68fuxqTtvMw7C8bR5IDvuzCtFUnCumHR6fa6oMHdrn1a+L67KQdBzfIiAd+x3GMKuErdwvf9apCrYWHGfPOFbDrq0ZOxtDfjz7fsdrZ8+Qv8/iN5WHqGPAOo5tVhm70mYKZNNwrI3oDl38yf/xT+L9v/Z+/MP/4T+M93/1/dn3cy6Am4XOa8FnvrtSCbsIK8vjZTC7FcRW2iyqZ2vnxmuKsLJ2fhF25uei0ia79+yVrVTKPqQi9txfvoZstc8LPHRKYwAA4MtPGAsAAL8kz5/v43vfex6/8zsfxu1tO00XPE5N3LbNNO3wWJHab9uI6Ib27VQh2wel7bSG61jhevp8CmX76Yr7cYzbfE3avBq2ts11ERFtFpJGzMKuab/mTCY7hbpjiFq0n8Ld7MRseuLyunNBbN7XJUHsAzLlqrGb8fvdCmVTcV2R4SzC0JiHpfnYZ9ML5/0V911cX7nv7Jpmvp1dX7tHOYatZy7HXz5zra/ae8nC+zyonaZHzoLeqV2cqmbLtWOn6Y3biKY7TcHcP8qZQLVss9Fu7dxqCDvs19aGXbuuDGIXwezKsSmIzO61CGUrbWrna4HnIoiN7Hhl+uLNaYqjuN/Kc+Vrv15yj4vXis2/h/KdAAAAXwvCWAAA+CX57LN9fPbZPna7Nr75zSex2/Wp6G7XRNelWRjbdWMY2wexKbVDiNrNKmQjumlN2X4q4bFqNg3bmNaGnYez+Xqxefjap1fLUHYIJfq9qTIwD2TzIPRtwsu1tWLPrRlbhrer0ycX1ZeLcT4mhK2d38pb1kLZsk06jWERckY9eKytHzvrtlJVWwtG87VjZ1WqaRn4XqQWBteeZwxG83FVAt1ZhWzqfxtluJ0H9Yt1a/PK3crvqqyeHY83TTZNcZtOgWycjs/CyYdYC3DLvh4Swkaat18LXlPl2rWK2LzfWihba5NXj+bnyzGujacSZp5dKzbrd6tathYUn12PtsvGd6GLA1wAAOArRRgLAAC/ZD/60at48+YQf/fvfit+7dfej7aNePJkF23bxM1NM0xX3Fe83tyk6Lo2bm7SEN6O0xPXt20b0zTF/Zqx4zTFKbpunJp4HtKW0xVHzIPa0T/7+E3804/fxJ+/OiymcV1MGZy7JJCtha1R+RxZQBrzNtXpideC2Ji3L8d6NoS98JlmahlMFsrOQsgzoeVaFensXL6N4ljWblHNWo61dqy4z1ZF7hgOV9eNjdP6sYvwt9bfEDJPYWn+LEUgHU0WIJbPXQv1h89TwDv+I4BheuIxfJ1C2bY5BbHt8Eypif3Lffzx/+GP4xt/4xvxN/6rfyPa2/b0EBfmcNXAbiuAzc8/IoRdrCGbhaiLa4rzl0xTPKs4Lc4/ZHri2bgqAWl5fS14rQWx5XTFs21XtOuWn88Zr3/zszdx/+l9HF8fz18EAAB8ZQhjAQDgl+zTT+/j00/v41d/9b346KPbePJkFxERNzdtpDROUzwGoX1lbNO0kVIXEe0Qss7/ImKamrjvI2ZTF4/TFJfXna7NQ9lYbCMivv/sPn7vL1/Ezfs30d62fSXgVip5QWA5hnHVa2qfy7B2OlwJaLeUFZfZ580g9pIQ9tw9V8Kb6hS/2ec8jJxdV5muuNp3dm11LdfadU0lIC0rWYvgcHOq4/JzLO+9tlZsre0YjM6eZdyuBPuzUDjvO7t/HsrOrh0qY/Oq2Wla42HcxzfH+NHv/She/+x1/O5/5XejvWnjnM1qyUtD2OxztdI0v89GEHvumjKonfpZmaZ4Mc7yfBmMRraNWAakeVA7jKc29XMZyJbVu4tK3TIQXgmG8zbT+7hAihT7z/bx5qdvLmoPAAB8dQhjAQDgc/LHf/ws/uqvXsXf//vfjW9/+2lERNze5hWxKY7HFMdjO0xj3K8X209j3E5ryzbNaXrjPnAdPzdDCHvaRpz2xwA2Yvw8hq/58ZjOp4hob9ppeuJa4DWbNvgBpirEIQhbTFEc2ba4pgzczk1PXKuSnQWx7zqEXesrLY/NpgC+pIq1Ek4u2k2n1teOnYLJPPRdGePU/lwIW55bq47N75XfI+tvbd3YWUVxXhFbPnv2nU/TFUe21mtlSuKpz6EydjrW9veZpipusirftt9O/53EmaA1t9asFvqdC2CLNpeGsJtryFYCzqmvWhAbK8drAezGGrK1v+m6bj6+sq9FJWy3rIyd3bur9NXN73dpNWx17AAAwNeSMBYAAD4nz57t4+XLQ7x4sY/337+Jp0/HCtkuIvq1ZJumG0LSNna7btjGsCZsiqZpp8A0r4odzatmT/sRMbXLq2L7cKqskM3C2Xyd2LJCde3zhq2K1sVatOP5jb7LMLUavC4vqo+lMqbFqTPh82YQtxKmnl2TdS1Mze819lOppM3HfnFQeEn17NimFtquVcdeeI9Z3+W2bJd/HitXyymNi37H33NeDTsdG8LefO3iPABe/ITHgHYMcUuXhnJj+Ll17dsEsYuuLg9iZ2Ms2i3GUIa1w4nVMWT3mk1PPI6p9g4qIexsPGc+T9MU5/ct2pXB8up/O1vfr0AWAAC+loSxAADwOeq6iJ/85HXc3x8j4qN4773dUBnbV8Pe3LRxe5vi9jbFbtcMFbJNHI9jhey47av0druxInZcP7YpKmHLytjTNiI/dtofE4QupWh2zTyQje1AdSHFVEk4tc1Cs+meefg6faxXxS76WhtD2TbbVitiK308tPK3bL8IcLLgcjbOtFHFGtsh62pgmR9bHfCwye6/WMt1vHet39oY14LYPLSNrGJ2fO7xXURRCTuuGxvzMfZdpdkYIuJUedvE8p0087/a2rG1v7Eqtmn7v9QNv+l2uPdYPT6+n1zl/VfD940gtwxOy8+z31kZwpZhan6sDFfHvjZCyVq7ajCajWtt3dZZAFse75btqtcWAetY2bo4N1TJTpWuKfq25X2Gatjp7yFrxWYhcuoq0ykDAABfC8JYAAD4HKWU4uXLfex2TXznO4eIiHjypJ+qeLcbw8kxHG2jaVJWGRtTxWvEqdI1pYi27ROnlGKxZmzeZ23d2XId2TeHFJ/tj/Fi360/yMMyyvVryqrF2nbWpBKijufWqmIX2XG146t4SDXq1rTCj+p7KxTNw83LBne+gveB93+IRbXr2E9+Pn932T2mytVxyuTacxe/w3Gt2LXAv7xf0zTR3Xfx4gcv4umvPI2n33l6/qG23sFWCFvsLyphh88PnpY438/7GD/XAt/xc+X6RQVtEZ7m1631N+urvC7vM3+GSLNza+8hX1d2Ee5ujKUq6z+lFN2+i+PrY6SDMBYAAL6OhLEAAPA5SiniBz94He+9dxdPnrTxzW8+iYiIp0+76LoUt7dtPHmyi+Mxxc1NF7e3u2jbJm5v5xWxNzftrBK2/CvXjl2rjB3XlT1NTxzxR5/cxf/9L17E8aaN5qY9VRBGVkn4WEU14uxYxDL4albuWat6La9bprDL9rX9yMbzDlRD07KKsgwoi/3VytPyXkXQWFbVrga4KxW7i2PFNYupkWtjWzuW9T0Lose+YyWArX1/lfeSB7jTGrbNaVtWw86mJS6PD2szLypjm4hoh39k8cOX8U/+p/8kfu0f/lr8y//tf/n8fyeV9/qg8LU8lyrXXxjC1sLbsxWx+TUrbbYqYhfHulN/5fEyfK1V0U5ty8rYcb87tR2PT5WxXTaGsao1q4xd+85m0ypn59789E28+P6LSEdhLAAAfB0JYwEA4HPWdSnu77v49NP7OB5TfPjhzTBF8bwyNqU2mqYbpiKOSKmJpmmHitj++FgJO+Umwzqyfeh2qqStVcaW58Z7748pXh1StG2KXS0ofaTZtMPD5zIwXV3Hdbx/LXiNjfD0kqrY8pJ3GMTmfZ6tkH1E9emD1oEt7hXxiGrX8doz4WqtzVhNWq1Q3Qp+a8H0cGB1auK87RisVjPoYgwrv6/Z+eb0LNN7bPqA9vDmEMe74/R8q880Pe560Fc7tlWdejaIXR/E+SA2v2dx32pwnObnamNYTE+cB7ErxzefpXwHayHxsE1Rn/Z4bcrli43hsKpYAAD42hLGAgDAF8DhkOJ733sRH310G0+etPHRR7cREfHee10cDn1l7O1tG123m60dO253u3ZaL3bcHo+ndWP7NWSXlbHtWOG3qIzt06qmiTh2KdqbJppdVpH6kHxyCEVmoWZRfZgfm7WJSrvp8DLMrY5rrIptKseKdotLLwx1N61kMKsVsucym6HNZnVsVh1aVqtWpz+u3iYLObOx1q6fzq+FrimdqlHLwDe/ZqyALe9VjmsIVKsBbuU9RRPL8TX1v6lKNus3n6Z4rITNj0/rII/Xtf19213brx9bC0fPqbRbrYIdPq+tJVubmnfWXx5Opsp1lfOzqYKz/WoFbRlkjk3G6tQ8eC3D2yIcrbbbqHZdW1t2VhGbbWvVtWNV7GrYXNlfPBMAAPC1JYwFAIAviJQi7u+P8ZOfvIk3b45xc9PG8XgzVbl2Xf9ht2uHbZ8+tW1f9dp1zVAdW05THMNas80pGBvWg+2nOU5TCNt1Y2VsHsLFqVQ24rIgcgwIK5rh/xUHTx/H0KsMULNzi2tXQtiVAZxvM97r3PWX2Ag9L1nndQovyyDxGgFPrSJ1Leg9M45FkPrA8VfD4K0q3Mp9qmHtue+v+McB0zTF2bk8hJ1+f2P4HVl17rCO8+aznnkPmwHssL8Wwi6uL4/VgtdyfyuIjWW7qd/y2mIsa1P6LsLZWp95aBqntV5Xn3El6J3Wis3+NkPk2njXnkcQCwAADISxAADwBXJ318Vf/MXL+Pa3n8STJ20cDk+GoDXF06e7SCni5qaLiN0Uwo4Vsn3FbBvH46k6dqyUbZqY1pftK2PTtB2D2fo6simOXYqm7SsCFwFlLdgag9i00aasSIx6+Dodr12/ohzj6lTEv4wgtryuEswsAtkHBK3VytEyuDwTgm4GwmtjeUgIe8m1a+8nP157nq17jNs8LB3H15ymRV6EqsN1U6haC3NrlbRZn9FGNF0z/TczVgZvWf0OaofXAths/7EhbNlutXo2vyZVrhmP18LWc+vFxrJNbergMnCdzmcVsuU9qtW1XVTHMfUzfn6Isb9ynVkAAOBrRxgLAABfQK9eHeIv//JVfOc7x9jvu/jmN5/EBx+Ma8m2kVJMa8SOIexu18Rul2K3a+N47Cto2zai69ppeuLxrzZNcRnKRvTbviL3cSnkbK3MiGoV6+oUw0UlYx6QTsHtWpVrWQW5ds/atWseG8S+AxdNLXwupLxCZe3Z4HWl/UWBbUT0jzxfE3Zt6uQYpwjO989NA12rol0JXcdjeaXsNGXyOJ7s956Gf+gw3uOi97TWZMoczwSwleAzoh7M1oLYavA67uft18LXiGXAGvPjaxWqs22sB7GLa+MUlOZTCU9t86mKy6mIy8rYbv43hqllWL32/czGFhH7F/t4/aPXsX+xX7kQAAD4OhDGAgDAF9DdXRc//embSCnFbjdfy/X2tg9Mb27G6YrbaZri/m8MarshsOqiafp24xqzYzg7n6b4NF1xO06v2vSVsWUg9dhg8qIq1a1Q9UyfqxWttb5XjlX7eFdB7GNC0IeErO94PIvwswz78rZ5RW4M/Q79r62Pm68Pe+mYZm0e8L1M429iVq2ah8N5m/ntTmvGju1r0xfPwu58m5+7RBm+rvVxJoSNWAasixB2bFMEjlvrx+ahcC1wrbYpwtTVytczQewsSC3C5PJ5Z23G4HYj2D0XEj9ESimOr4/x6oevHnYhAADwlSOMBQCAL7Bnz/Zxd9fFj3/8Jp48aePv/J1vxre+9SSOx5thTdld7HZtHA5t3Nw0sdu1cXPTRds2cXPTRts2cTz2wdFul6JtYwhjm1koW5ui+Nkxxe+/2MfzocpwbRrhmllFbBmaFdO7XhKQltde1Dbq4e+56WKv7pKpgt9R5eql96+2iQvHsFZxW7vPyr0vqvzd6nNrPM3p2Ooastnvarbma1EJu2if/Y7LMDev3k5dqob8q9WytcN5eFoLxWsBbHb8MVMUlyFsef5cRew0te+FQWyk6KtXs/tvth+qXqfq19r0xJVz5fHpLy0/T+OpfB+1tWoX4wQAAL72hLEAAPAFdn/fxf19nwa0bcSv/dr7cXPTT0l8e5uGNWGH4CP10xan1MZuFxHRDevKttG2fSLadc0QyPZBwTgN8SmQjWkd2Zf7Lv781TG6myZ2T3eLsKpm7HPVamFscwq2LvGAQPZB47hmVewXSHWa4CsEwNM0vg8NWM+0uXia43P3Wgt31/6BQKVitpyyeDZlchORjikOrw/R3rbR3rbLfreePZW7Dw9hZ6HhY4PYvP9aKJtXn9ZC340gdhG6Zvcpg+C1oLNWhTt+vmTMs78oxla844VU+Zwiun0X6SiMBQAAhLEAAPCl0XURf/zHz+L993fxL/6L34oPPriJ/b6L29s2nj7t4uamjdvbdgpr+8rYmCpkd7uxQnZcOzZmFbLlGrKHY4pm2l9Jt8ogolZRmVfBFtbWi51V4dbOVT5XOq9vH+KXGMRWp/LNx1ELDS/Mes6Fl+X6qxcFteeqYB+iVrlansunQa7dqzn9HvIq29mxteep/EamitehQjY/Pq4HO52r/OOAJpqINuLTP/40/uB/8QfxG//534jf+ke/dfY9ra4LWzlWDV+Hz7UAdu2azRC2CFgXbYsAtdp+JYitnuvm/eXHV4/l68KOIWy+fmxtDdgu6hWx3am/auhbCV/z8DilFPuX+3j2z59Fd7dWVgsAAHydCGMBAOBL5PXrYxwOXXz22T66Lg1TFZ8qXCNiWj82Ylxrdq1Ctg9fT5WxKVI6Vch2XRpKZS8YWBnEPnDq4HfR9kvlmtMQ/xKcrUytX7Qd3r5l4HzR/fPtueOV66dpiGeHs2mKU7MIbw+vD3H//D7uP7ufPcvZ9WA3jq+GsPm5Wji7Fsym9XZrQexiDCmW96yErYv2ZTgbMQs3y34W/W3cZ1YZW9ynVn27OL+h+vsf+zmkOLw8qIwFAAAiQhgLAABfOvt9Gipkb+Jv/a2P4v33b+Ib37iN29tdPH16jNvbXdzcNPHkSRtt28btbTOtITuvjG2mNWTn68f2n/cRl00dvBK+TuvG5ueKdTanY1G0qXlsu63zZSXkY5SXfc3yl0cFs5d1vP0u16pp16pey8vHCtdUuX6lintaD7asim3WpylumiaadqjO3QhRa6qBYK0y8/Rhea4SzpbHqvsrgWw1wMzbrbWpbKvnuvX9vAq21s90vljzdap8XVs7Nq+IPab5GKpfzPJdLcYEAAAwEMYCAMCX0H6fom2P8ezZPvb7Lpom4unTLiJuhsrYfm3Kfj3ZdqiMjWGbh7FjIJuyaYr7StljRF8ZG3H5dL1lMPuQYPQa57muz7nCt1apuji/NbVx3jZb/zU7uFoRu9LJdJ8plM0rZTe62KzELE89JIQtzp8LXsvton0esmb3n1WkbgSxqxWx2b1mFbFFf+X9FtWv+Tu54L61v6ra4eJY6lLsn+1j/3wvkAUAACbCWAAA+JK6u+viz//8Rbz//i6++90n8Y1vPIlf+ZUn8d57N/HkSRtPn+6mdWR3uyZub8e1Y/u1ZMftPJg9Vcju2ybiyW4ReI7Tsc72+1LAPnxK6xWxl1hcG29RtXpttWG9q4DySz6V8apLgtGt9XPLPh7ynppYVsLOTmfnysrb/Dech7Tj+rHDfwcpTvuzSvBYCVzPjT0PGGvXlAFoebwWyqaifdm2FsLmoWa2/84qY8dp1rcqY/Nz3emavPp1vGa2xmy+Lmx+bd42XzN2K4QeP+fvdPg73h/j2R89i8Prw1fzv10AAOBRhLEAAPAlllLEft/FixeHOBz6gPZb37qNDz+8mdaU7bpd7HbNtDbszU2a7a+FsYebdjtAHcLX8fM0ResjNZeuT/tFkr+D/Ni76pvLXDqF9drlzbwqdnPq5Uv6zkLbWd8XBK/9ZiN8LfYfFMLGMmRcDV7H/TJYLatU8z7K4LIIXrcqVKf7boW35ZiGa6rTFRf707F8muOuMraa8v0WbVOkZZ8AAAADYSwAAHzJ7fcpPvlkH8Mqr/Gbv/lefOc7T+Ojj7p48mQXh0MXu10bT54sK2TH9WPzdWSnytgUsbIY7HQ4paFKtlmea04fpv23CWsf6pd2r1ogy+dvq2I2r3otpiauTkm8tobs8A8Qpm3Mt+M0xZuVsaOtAK8MabfC2coasZdUwq7ul6Ftea4MayttLl4rduOaaoVsN++vDFzzStfpWHZ89nmjKnb1aymC5qlSFwAAICOMBQCAr5jnzw9xOKT45JP7uL1t49d+7b14+nQX7713Ezc3zTBtcRs3N13sdqdQtmn6ULbbNfHJB7fxZtdcXOA1Tl2cmnk4+1aVsvUE7GJTIPbL8NAMRmbzOO9i6uaHVLbm98x+07N1YPPz+Ta//oI1Y2vnLglfI4qQNw9Py+sfEsKO+7WwNT9XmaZ4beri1SC2tu1O962tD1t+LgPXMiRdTE+c74/Vsiuq6+Rmz5e6FK9++Cr2L/bR7bvVfgAAgK8nYSwAAHzFvHhxiBcvDhERsds18eRJO5u2+OnTXex2XdzctHFz00TbpmHbh7H3N238VdvEsW3jJlayq1o4pDr0rTy4avKLEOh+EcZwqUf8PhdVsmt9jJW2w1q3+Zqxi8rxzGLt0TVb4Wt5Pg9Pi/NldWx1yuKNNouq17zvypTCF1XEPnC71d/seFevjJ0qWLt632eVIXREpGOK1z9+HffP7i/oAAAA+LoRxgIAwFdY16X48Y9fx9Onu/jud5/Gkyd9GHt7209b3Aey7RTG3ty0cXjS9hfnwVMqtuWxtw1iL73+XYe+WX9jiHbV+31OVtc/Hc9vBcHv2hc5wK1VxFbaTGvKjtWyWWhbm6Y4/40tnHkfZ6/JqzVrAWylavZR0xOP5/PAdiWsXUxhPBSLTlMRd6navloRm1e7dsWxbLriWZt8Pw9m0/p29XvIQtq1qtiU0tn/xgAAgK8vYSwAAHyFpdRPW/zmzTHee6+N/b6Nw6FfS7brdnFzk+LmJsXtbR/GHo8puhiyh7UQMsW0zuZsGuAxi2iv9zzvxOcdsNayta1Kx1+yS0KlvM2XOoR64LTHUwib7zd9kDcLaNPsoojoqyePd8dob9po2uUP8EGV0WU4+MgQtlYJuzh/wbTEU9/p1C6/3+yaMWjNA9dyG8vjW1Wym/tlMJuHuikW67wugukigF08zzFFOmyEuQAAwNeeMBYAAL4GDocUP/3pXbRtRNs28dFHN/Gd7zyN29u+Kvbmpo227deTjfdv6rlCGfhkwcQYSj16jdZKQProNV/fImy9enXsYwKbc9c8tM9UBEyX9FM7VwkAr+4d3acMVd+qr3Iq4/z3MlTRRhPx8R98HM/+9Fn89r/x2/Erf/dXLut8K3Qt9lenJi4D2NqxPJQtw9Xi81YIWwas58LU1YC0rIgdQ9MsXM3Xf51VynYxr47NtmN4mve3+d4rgXYZ+r74ixdx9/O7OLw8rHQGAAB83QljAQDgayCliPv7btpv2ybef/8Q+30fxu527XBsF+1Nu16ll8rdLDB9l6HlpR5yz89jfO/Iahh3vRt+pb2zat6m3B3+UUJarjV7/+w+3vz8Texf7msDWh/bRhC+CGDzcytBYq0Sdmua4tUpi2vtxj5WwtezgWws92v9ldfVKmMXlbor41l9Z/m7K4Pm7Pjx1TH2n1W+UwAAgIEwFgAAvoZevuynLu6LQJtomj6g/et//YN4f9dGpIi2OYWsZbBTTk+colJR+g5VK1bPXhSnKWNnw+3DsulYeX6tOjbi8WHuRvb3NlMUb06hWvaXzhxbdj7r/63Xla3df63pL3MN2wstqmmL6tdpzdiUVYk3wztsIpq26acnHqsyt1T+0UPt+Ozrr4Wv+fFLAtlam+Jz2XZaB7YMSiNO679m2+malUA2r4Cdtc0rYsu1YbOq17xCdlortvwrw9q172B6dfVxjvcDAADYIowFAICvoZT6qYuHvYiIaNs+pE3vHeL9Wr6wFgS9owz2wdMdFyHqF9ZDgtjKtVvrgj72vg8aw7k+N8ZVrdy8tO+HZFwP+b0+9h5nNE3Tv8t8zdiV9WM3fxOVgH1tfyvIr4Wwi2vWgtjKGq+LtuVvMw9iyxAz+x1MgWYl4Mz7yfutVsTGmbViiwrZMgBefZf52Ffee6SI45tjHF4fott3AQAAsEUYCwAARERE10X88Iev4+nrLv72oYs2naq+mpRNRTyEoNMUxdcoDBuD1jxwrYSvszFUgtnFurPl2Mv7jbtrlbi1YG3rGbZOr02Jek4RdF2kCOTOtru429MFtWllL+6/FoBt3/h0z4eGrI/8vVbH1sTy91n8ZptmqJBt0rS+7EO/+7PV07XQvvY7KQPZVFy/FsiufF6EqTE/VgtbF9uySrZSETtu8zZlFe1iP/urrS9bf9GV910Gw0Ofr3/8Ol58/8X5KmcAAOBrTxgLAABMUorouiJ2qgR5KaUpoF1Usq4Eo+9sjLE9zfDKRcs2Z647OzVyGcw+IJO5qBo1D9guaDvrtwzZZk2LNit9XdzmIeN6aF9fgpxrmqI4C2LHqYrz30XTDMfWgurBpes1VwPY7PMirF/5jZRVsWWbtZB9c93VC4PY1b9YXl8GsdHF4ty032X9dzG/dvY/ZMXzlu97fMe1MRz7PwAAgHOEsQAAwLpaAJSFlItgdNF8HtQuKlLX7jmEWKnZ7n9x6Ti2so/1AW5X3l66Vu3bBrGV93y2/yvnQJesU7p53fkbXHbNRtD8oPut3Hv1+KXdFlWw+VTF47TbYwXt9N/NWkXvyngWz/iY8LV2LA8+x+srIW31c7FfWzN2qhrN1459SIVsEb5OgWi2HmweuFbXkC3Wit0KnqdtcW72bF2KdEgqYgEAgIsJYwEAgLPyMGKaFnjMIi6crrcaxF7ax0b7c1MVV/uoTVWcnZsdujSQveTWD1if9WzVY3Fss4/5wfqxtNI+Nsb9wDxq7H/a1qbffdvgOX81lf5nUyu/TaKdXTqFrlkgO6uELa+rLDP6oO8qv2YlQN6skr4whC3Pzz6Xa8cWxx89VXEeuhZtZ0FspVo2r4jNj62tE1t7t9Xpnsf7dyn2L/bx5qdv4v6z+zOdAQAA9ISxAADAUh5c1MKkKELKSttZ2HlpUFq79tw418LT8Vy5za57SCAbEY8OZR80Te9GKNqffmBgutHvZsi6emoZEtf6WX2Gt8g/z/W9Nd3sRmfrp4r+yvvmUxRPUxPnv7eYrxk7/uYunaZ6cd+V/xYXY70klC3+m31QCDtel+1PlaIboesscI20WSk7ncuqXstQtqyWnULYcs3YfNwb77H2zGVV7P75Pl58/0UAAABcShgLAACsGoOIJmXVsClOU/8+JFjLQrGUhuvzcDSGcLRp5teM0w3H5dMjL2+9UT07PkNTHKv0/ZBQ9qGB29n92ufaLcpz567Lg6gta6Hm2hjHQ2UYuNJu7fylVcAXP0ft0pUpec/es6m0yYP/PJQdg9syGLzkPsW5akVx9rlWMbsawK7tF8HraghbqYCdhatF6FoGr1vTE0/haq0CNq+QPWbna0Fs5Te0OT1xEcSmlOLw8hAvvv8iDi8Pq18TAABAjTAWAABYKoO3Wkg4BqRDyJSHoymlU4C7kVvOro9KCLs1vjIIu7R9cWy1CndjDA+Zbnh1PKunKuHlmWsvrX68aFyXBpFr40qx7CcqAWHt2jPHVqdu3rj+IVW05TTKi/4q98qrY8d209qxfYP5Xzn+2Di2NtbKf4/V6XWjeO+XhLDjfqrsF+fe1fTE1ZC20rZaEZtdW4a1UwXuBb/j2fvLv+cUkY4pDq8P8erHr6pTTAMAAGwRxgIAAAtp+H+10KXPnSrTAA/XnatYXUxfHFGvSq30n5/P+5nuO/S1mKo4v28Z5DbL62f3Ko+9jTNh5MXrxObHL5wiuOx7a9rW2bqqWbvqb2LrvpUweLFe7Mo7KdcyrQaKZ97noq/yXBZiblbUVsZfm/o6Xzs2oghl4/TfzTSl7wVjL8+tVgpvhK8Ry/ddVrqWxxaVsPl3XwlnV0PYmIeus3O1aYqz0HVWOZsfP1cRe8y+09o72gify6rd45tjPPujZ3F8fRTEAgAAjyKMBQAAlrLgtbofRYB5rq+3GkoWuqaVaYzL+9WGlWI+vfJiqdiNdWrX+nyIS4PYx/Rx7twlrtn3Wl+VcLEaLj+kErkSvm22eei1uWwa4vHzVOk9BrHNvP1miFzcezXcrrVbGfslVdNbQe1iLdkiaN0MYov//diqlB0D6q1wd6sidgq5i/+dWryr2vTE08e0aJMOKe6f3Ud3J4kFAAAeRxgLAADMZcHrYs3Y4XxeATsLZcdzWQI1Bj+zNWbHds3pusm4RmyTVdnmgVc+zqwqcQpax0OV6tjV8HboezbWtXb5OLc8JnB7YNi2ufZoLXyrBVJFILqoSM37rYxnEbqtPdOZoLO6rue5MDK7dlatWfZ17n3lz1t8XguC87B1qngdq2Lz3+zYtskCvq3K2HxcxTOu/T4Wz1Y7Xvs9ZMcX1a7juRTrwWv2W3nQNMXlOrErlbDjdlEhO34+nipkp0rZ2rtae93Fs+V/U7/HynsHAAB4AGEsAACwME1T3O+cjo/hbKU6dVa1mgcbZ0LLRXhbqX4tg9bVgHXtftnx6nTF9YGdP/9YW6Flrc25YOlM0HbxWIsQdjFF8blr8z62xnnmvlttq++jlrGeq6Z9wPc3W0P13HXFPx5oUnNxqNyf2vhNVI49JIStHV+djjiK5873s+uqFaxbQeyZbXlsq//VKtwVq/8IoXbPLuLw8hD7l3thLAAA8FaEsQAAwFKKfn3EIgSZKmSzQGYRkpYhZnZsMRVwFlrVztVC2LyitgyAp2raqXl/g1mFbN5HGeRmYxmPTfd/h9MUX2X62Vq/lWOLMG68x6VBaRnObd17a13YSrg5BXzZ+1+0y7abla7FfVfXQy2vLdtXh376XU3ttv5xQP4TzqbkPWez8jnb31xreCt8za+vfa9F0LkIRos2Z8PYbrhXuVZsvl+phJ2Oj1WxYxVtXhFbew8b/71sVfWmLkV338WzP34W++f7qeIWAADgMYSxAADAUhHKVEPYcQrhrP2ym2K912w6477jyvW1qYjL9tm52rTIq2u/1saWh2f5PWvtyjFc4iEhbHbsoiC21lctZCvvVTuWj28t6Fwb+9YzlseqQ98+f7Gt0Hfr/Nqxreca14htmsV3Na4buzbGi9fAXRnb6vuq/XYuCWHLcdXC10p4mbd5dFVsOT1xWYVcnB/bjKHt2u9w9spqVcL5u8qfo0uxf7aPw6tDHO+OglgAAOCtCWMBAICF2fSgXURq5wHJGDaNIW25vuuskrYSXq4FpmOf0/kxiC1C18WUyBGztuV2bf3YqfJ2LRzOpjbOBnlR2Js/U+Xg5rGLg9hzwetaILd1n61wrwwCy3tnf9XwL5/aNrK+avdd3CZVn6MMDGeBciW0rAXO1fVSY/lci/CvNiVxtm5sXjk7hrZjAFnz0N/K1jqyq+Fr7djKd/bOwtihEnixVmy+X1kztjw+VcWO1bN5Rey5/14q72Xx/XZ9ny++/yLefPym/u4BAAAeSBgLAAAs5cFaRD3oGAKn/Nhi2uK8r2n3FICO7RfTEW+NK4r7jkFtHgZX+tsKZPsuV6pkIxaB8tn1U8+Nf+P4RWvE5sfL0DHv58x1+bGz7fP9Wh8r11YDw7Vx1K5fWfu0/F1V+47Kc+XhYszbzULitb7X1oEdzk1B7NhmDPRr0ySfU3uOjd9GLah/q4rY/PwF4WtEFrBGWk5HXASvZSA7W7e1CGVn0xNnVbFb39XZitg8hE0p7j+5j/tn93F4dRDEAgAA74wwFgAAWBiDkepasWPYmbJAdagAnLZZ+0XYGVkfp9LTk6KyNV8HNr+mul5sPl1xJXxdC2RnfdcC2DKYechUxWfCxtPHSqiUn69VxI7X5ccqYVvt+rPVhGVold9rZTxbIXW1Krb2vLW+Ks+wFrIunqkSNi7GlV9XfF67dqp6bYZK8bECtjZFcf5bS8W7WxnX1vnN61e+27NTF+dtys+1NWTHNl3WVyq247lKRexD1oydhbFdmge+W7/h2rNmzzvefxzD65+9jpd/8XLtWwAAAHgUYSwAADA3BhVdzAKLPMCYwtFIi5B1tb9agDkEKuMUrmOolYe7s6A177+ojp2FqGllGzEPZIt+qsHxyrjfyiJLWwkmx48rQeziva9sF8FUEcytrhVbCTZXx1MEXNXfROXYOJZFSFsei5V+xkAta7OYTjh/znK8tX7LMZbhZ/nbink4u/gtRsRHv/1RfPDrH8R7331vdZri1fvVxpc/U2Xsa0Hlouo1a1cG07WK2HMh7CKQTbE5PXEeuq6GsV2l3crzzd5f5bsux5m6viL29Y9fx/2z+/p7BwAAeAvCWAAAYCYPx6bK2DGcLAK3qQK2CDxnbZpTJeusr4hFdersWMQ8OM3Wb52mOY6sOvb0AMuq2rVAdvnwi/VpF2N9rEq+Vg3Siv2tcLQW4p6dQrl2ejX7y8K4rdDv3L3W7lkGouW9Ku22qlwXfayEjmshXn6/2Xqi+bk8gC2rYGv/HQze+9X34tv/8rfnz7A2hq1nWhtzrD/j1rqp0/7KOz63buxmCPuQULbsr1hDdmy/FrSvPevsN1r871d0EemYYv98Hy9/oCIWAAC4DmEsAACwMAtBhqlBp6Bp+JtNRTwEs9O2rDCtBIa1UHZc+3U6v1blml+TTm2naWPL9WO3AtkybM2DrWY81Sye4bFWA7XKsdUK1LGfWjBatlubina8phZu1kLIyrHqtLbls9bGtfLsswD0kvcTaR4kXvA+ZuMq3k31+vHzVnVsZWxTs6HqO1If/D1KZeyr58+Fkfmx7JmrVbBj28q5dxXClqFrdVrisV3lNz97DWshcyzHm7oU+8/28fzPnvdrxAIAAFyJMBYAAFiYhSmxHqpGxCk4za6drTFbrL06Bq4RMU0/XF3LNYrrslB1FubWpiteFMteUCE7PltlrdhZuPxIDwlhx7GtnX9UEJuPYeueedvadWeeoRqOFuMZ+1uEorU+18Ze+T1uvuOxTVlZmebjmQLe4v4zl1THbjzLJS79vVxcOb32mygDy2ysj14rNgtaZ9dngeosiM2umf0jkLJidu1dbfyjhfx5prZdRHffxeHVId58/OZB3wsAAMBDCWMBAICFdDxVpkUT/eeIU9VpFrrk+2PIugg/0jLInLWvVb6O7aK+duzseMzD3DzwnfopK2SH+06BcxnKjuPKji1Ct82XeOZ8pc3m9LVZaPiY0G22rbRZBFpl+5XArjruKALX2nW1cLJyvDo1bszXiM2vrQWK5TS803iyd7dokwePs689C1+L39L0DwvKgDayoPFC1fCx8r7OTh9d+12kol15rrJfe08XV8d28/21dWLzStiymnbtHdSC2HMVsYdXh/j0n30ax9fHy/47BQAAeAvCWAAAYC4LxGaBa8yPz8LVMrhbW5s1b7cyNfFULRune2xOKbwy7fAU3M4eLQtk164v38Va6Po2IU4tZ9sKYuNM6LY2lpXQbau/2bnyc6ocz++xURU7G0fRbxmUTveqjKe8Z+3aze+mbF+7di3YjJj+QUJqNt770K7sd6u6czGGjeNnv7vKu98KYWdjq30uAtla6FoNYlemJ14NYsvg/Mx3VZtiu3zO2di6iOOrYxxeHGL/fB/pIIkFAACuTxgLAAAspGOK7thF27X9mpdDkDF+LteLjYhToDqsHTuFuEWQk1cPzsKVLCCdgtkxkM0qZ/MgeKp2jexcVuE6Oz9235zGsVYlG7FSKVuO9aKXuXHqIdWP546XgdSs6UoQl7L25ecy0MrvWwvoyvtFVMdW9juburgILBdryBZBYd6uHFMZ6s2mvC2efxH8Fe3z38j4O5wdK5+x0i6l9PmuGVt8F2vh63Su8r4vroQd2wyVwIvAdWvN2GNaPmP5+6r8BsrvtQyOU5eiu+/ik3/6SRxeHgSxAADAL40wFgAAWJjWexxCjbWpiKf9LAgt15CtBXVTmBpZ6DqGufnasWvbrO986uJZWNsU5/Px5FMZV6pkp3cQsayWrTzTQ61WSFZCtrWq0LWK2Or0xGUoWgvvqsNJ83a1pmWAV469cs9qmJg9a21a4c17roSTtWrjxVq4aT72lCpTIK8ZfmO1aYmrY76w27Nr3659fw8MZTdD2CLMLNs8uCK27L8MYsfPa7+Z2vOuPOti7F3E/rN9HF4d4vjmGN2+W/QBAABwLcJYAABgLkV0hy66Y3cKSbrUT8/aNZHaU3CyCGnHwCn7yytiy2BoVrVahqJ5uDpW58Y8+FpdM7YMZCMLZPNqxbUq2Ww8tfCnGtBuvdKt6WnPBbCVEHOz0nHt2Mp10+eVoHIW2o19roWdxfmyXVmZOtvPxlEMehn+leOMU5tawFibunYWIA791KbInaq8x9/gWgA7Hs9+q/kzTKHjGY/+rZTvfOs3ku/n/03WqmDzduV7K7+XtVB2q0K2Nj3xuXdS/O/I4hmKbTqm+OxPP4u7n99V+wcAALgmYSwAADDT7bt48ecv4ul3nsa3/oVvRdPNpySurh+bsrVds2mKx3AqD43KKtUxSB0D1NnasakISYuq2PzYNEVximUgO5wf1/ssg99FlWyl/9xmYHaJtcvzcK1sVwtAa/2kjW0tnCtDzbLt2rFKADbrIg9gV55ls+/x95WdKIPXWXBa66s8v/Hey3OrlcCjMrw/JwslH2Tt+6md3/q91PYrxy4KYSP6EDXSfHtJhWw6hbLl+bNBbPF8lwSx95/cx/75Po6vj4JYAADgcyGMBQAAZo53x/j57/88PvjND+Ibf/Mb0eyWa8bmgcesOrZcUzbiFLJkVaqRYhaUzkKSfO3YIZDNpyLO15FdVNfG6T7l+rKzaYvT/FhEzKtki3Es+n+orRDoXYVttWN5UBVp+7qVtovANg9JywAzO1+9fxbm1SpTy/3FNMIbgd1iquGyKjht9Jf9VQPqsuo1r45t+jbj73TxTvIKzYeGsbX3Wx7Pv8ZLfwsPmZ44v6Z4d2tVsheHsF0xttpzVJ63rKSuTak83uvVj17Fq796tXy3AAAAvyTCWAAAoGqaTnSYSnQKN1MzrSebByCLAHbYThWxlfOzatimWDu2SD2nqYiz6yJiVkE7Bb1lhWyxDu1ibdp8TM0p6FkEs/mzvY0y7Kv1XbYp323Uw7fqd3CmOnJRfVoLPbNjs2mCN6b2XYyn8gy1kLUavtaCw7J6dauf8V1k77MMfmuB49t68o0n8d533oun33761mHsdPhctWh+PA8rx/1rhLFblbEr0xSv/pYf+mzd/DlSl+Luk7u4+/gu9s/29ZcIAADwSyKMBQAAVuVrOkaKxRTEY7VhuXbsVCGYimmKs4BzFrw2WVCbVbZGiu3pistrKtdXA9mI+bTFK33m4c8sHH6L6tjNALbWtgwbx4+V6X83K2JX2izWFq2FmTF/F7MQL4rzlXuthsZF36shaBEozu5bBImzMHk8VguMa/dcefZVF0xVfPvhbXzjP/eNvs/HTFMclXvUvpPs3KPXiy3PZX2drYg9F8YO24tC2Ac+02zq5NSvEXv/7D5efP9FvW8AAIBfImEsAABQN4Ynw1/TDNMFd82ySrY97c+qZCshV0TUpw+OoUo2ZdsxyG2WFbN5iJtiHqxOUw4XFbL97Zb3nYWy2bnZmJs8QXq793ru+EUhbH78IQFcuX8ukMs/l/cpr1sL6vJzWX9lcLp45vL6MpCttC/fQd6+Wvla/C36LcL9aariS6RTODjr74Lrzh1/yLTVi3edt0vzz7Xv6aLvdSWMrVXCnq2CzZ/nzO96Ng30McX+s328+MsXcXh5WHmJAAAAv1zCWAAAYFVeyZZv87B1MQ3xSriVB6d5aJpXx07VspXwtb9JnI6NfTVjqWucrZCNiHmVbNZuWvszinPTy8g+P7Qydiu7KwO2sn1+fitoW3S7ctOV0G6zQrEWjJXj3Lpndi6fGrjWfPZ7K/vNQ7niN1ZOP7wIeivPNRtLNqaHVMWO02yvVskOoWS/zHFzvt+1Zy76nD6uTFm8WiW9Fcjmx4v/fmf/GxAr4WsZypbTFF8SwuZjvWT86fS/U92+i8PLQ7z5yZtKpwAAAJ8PYSwAAFA1VpqlQ4q0G8KmZgg+mnQKmLohWG2L8Gv4m6YwLgKpxfqwq1lgtq5sXhW78jkiVveHnVOVbLO87xQCldWyw7Gh0eOs3avsr2x3LoRN83MProgtgsypbWV/EZTWpgDeqDwtA9VLKmirfQ/nF9Nmb+zX7jdNxd0V94qYfgPTtNuPqI6dKmPzaa8f4tKwMvu8Wk26Emw+JJStvr84vb9qlezWs5TjrjzTVggbKaI79iHsZ3/yWRzvjis3AQAA+HwIYwEAgLoh7BgDranyNSrhXVqpjI1YBCsRcZqmOAu9pumJm6w6tihBzac3ns4PYc1UIZvf68y6s1OVbN4+f/5IpwC5fJbRWpXsRu52adVjxHqYtRrErt145fsog66pTRH41da6nYV4lftOlaZ5OFiOIbJwLYpnLQO4cszl863co7buaVXtHUVW/fpQWZC5WJv43HXTx40wcyN8jZi/s8V7e0wIGzELrVcrYsuA/cxzVp+x8r3nY0spxfHu2P+DkWOKw6tD7J/v51NCAwAAfAEIYwEAgLoUcbw/RrNror1p++rAJiIdhymFh4q4qfK1Vg05rCcbXUTTLiti+6wzWyc2O5dXzV5aFbtVETs7loWyeRBUhr+zdWVLl1bJPuL8ajVhEZDm24vXjM3DuDh9Z4u2K+cW33PUz88C1pU+Llm7dapujaiGhLM2K/vV/lLM/rHB7O8hwevW9MRpWHP5kOZrDj9GcfnZ38jGb2Gr+rkWys7eba0SNnuXq69uNtyVsa+MfzG+rq+GffZHz2L/bB/TtMmCWAAA4AtIGAsAAFR1+y7efPwmnn7raeze2/WBaXcKXsug5lxl7NSurHaNomowRTXgyqtfF+vOlhWyQx9T5WvW5zjlbK0idjZ9cTb2ajXj2+Y+lesXU9/WQrbKtZcEseV3Uq2IjWXb6hTGURlLLVxdu292/RS0jqFtrNwjxeL8ov9y7LVK2EoAWf3djjZ+k2fl9xquXVRwb11+bo3V/Duq/VZW/huc2jwkhL1keuKV31M13C4PrfxuZ2Mc7rV/uY/jm2McXh1MSwwAAHzhCWMBAICq+2f38dN//NP4xt/6Rvz6d3+9Xyd2XCv2GBG7oWHXb6ZK2bY5BTMrlbHjfupSfzyGILVrIrXzati8ovXBVbH5+rCVELh2PL/mdOjyAO0hNsPXYf9Ra4OW7TYqYsvzswAua7dWHVv2nY9rMb6Vv2oV7UqbMhi8pCJ2qlBdW/e0tr7pWvhaTHVdDWrHe3cpumM3q9icBZMPDfnz91u2XQkza9/Dg6cnrr334b/36FbC1nPPU/sNl89R3rfr7/vi+y/izU/enMYAAADwBSaMBQAAVo3rMY4hyBhsNalZhDX5dMWLKtnI2o3VrVnV6ljROpueuGnqla1j8NrE1D4iZhWy+Rq0EbFaJTtel49rFrSN7yEL6R5S2bh4n7VKx/yexf5FoVvt2FYQVwti834qoeJivxxf1k81nK1Vv5bhZ9n/GMTl98jGPgsRIy1Cxeo0vnl/tdBxsFgjtonT1NoXSJGi2TXx5FtP4ub9m3pouFVpe+HxzWdc+Q7KMDbf35oGuhrQRiVM3TIb7kYIm48zRRxeH+L+0/spkD28OghiAQCALw1hLAAAsG1YizG1WSg7VsiOAWlWkdh0Q2g1BljDGo9TZexw3Wxq4Wzd2EkWzpTnL6mQnfaLaYvHMa+tHVsen6lUzT7KRti2NhXxVgg7O74VxNamgF3pdzYlcVktO/axEtZtBXhl9etWu1qlbDWQLf+yZ6hVy569dlT+DjbWhy3tnuziw9/6sK8A31rLtMz2t35exW+1DDMfum5wObXwNPVw7fvMpyW+RCp362OePq5V6HYp7j+5j0//2aeX3RcAAOALRhgLAABsSilFOgxh7K4PVlPTb6Ppw5KpInUIcGpVstPniFlwNE0/nOaB6jS9cGr6qtdxW4S4iwrZ7HgeHi6mKW4qa9UOx8fPeWXtOO5VDwnVijazisu1EDYP2vLjZQhbO1aGqHmb8VgRYlbDu1oQWtxn9XnWQs8yIC3uUwtPt6bZrYasxbnN6YkjHrc+7Pg4xVS+5T8QePCM17NXeUGgmR/LA9ozYWztu128n/K72xx2Zaxb4y3H0KU4vjnG65+8jv3z/WU3BQAA+AISxgIAANtSRHfsojk2U2VstJXAplgzNg9hI52qZKfgdug7r16dTU+c6hWqi/ZlVWxzCnXKqthpP+trrODtBz+/X2rmgdIinC3e0yXvcrrv1rXlGLdC2Lx9KtqXbdcCuayfakVs/jkPTcu24z1q7bKxlSHfIthdC2GjGE825sV+JdBdDRprgexD5UHleK/j6bnfZnrr6RYrVdHTu8mPV34LqyF7lxZtqtXQm4O7/Nzq7zG7b3fs4vDqEC/+/IUpiQEAgC81YSwAALDpzc/fxMf/0cfx0d/4KD76Gx9FatNUERsREbt+k4eyTdOcpiPumlMF7bAu7Dht8RTOdhGpHSpiuyyQHatmszU882MRy0B2bFOem52vhbKDsoI2N4a3FwWv652cPV5dh3MrhI1lwDUL4y4IZTfDupVAdHHdVtiZB7Gx0S6vLN2Y/rhss7V/7l41i3VjLzXet0v9P2JI89/m0PmD+ls7dlG19KWhbJpf96C1YLfGWo4nP5aPYZgO/cVfvojj62NEijjeH98+JAcAAPicCWMBAIBNh5eHePHyRdx+6zY+/N0PT8FJFppGikUl7Hgsr1KdwteI2bTF0zTEcQpsp/0ifB0tpiweKw+L7GacdnixdmwUoWw+rXG2pu10burwXb3Zylg3QtiIlSB2HHPt2FaQ99CK2GJ8i+mAN8Y3bcvP+bHseC2AW10rtri22v9GEJsiq6o9E7Q/JKAdA9nqb2je6UoH549fWh29eHdRfO6y/t7B73vx/W2NbVyHuuunQ7/7+M60xAAAwFeKMBYAALhMF5EOKbq2izbaSMcUqUn9VKwR07ZpmilgSk2Kpm2m0GVWGTt+bvvq2UVlbLusjF1UyKZTcLpaITsGsPmxwVZFbC1026qafYjV9TQrxy6uhB0+n52muGhTBpur64hWwtDVKYS7NDt2SRXsor9LK2S7eZt8f7Uytiv6q2lO6xVvBqPTq+3v07RNPPnmk2h2Tb/WcpNdXPx8zv2eLp3OuvYbWXzntRB2Ldh9G7XfYzaO6V7DOF798FW8+embaf/w6vBuxgEAAPAFIYwFAAAuMlX6dfNQa6pwLUOy4m8MT6dq1rKyNauM7W+Y3TtWKmTLNV7zwHVjndjx/BjUjs+3uHcTs/0pWJoN4S0DtZXjW9WF1Wu2gti00aYM5PJnLT+vBJi1tVynIHY4UU5PvHi28h3nbbO/cix52DftV35/i2raNL/vu9TetP003ONU3vlPZFp2+AFVtnmgeTp4+nhmGuBLwtjFOC9Ve4QyhM3um7oU3X13Cl9fHOL+0/tH3BgAAODLQRgLAABcJB1TdPsumraJtmmjuzlVyHbRH4+IPtAZPzbZ1MNNOp0b1pKdpgQegqumHdaPHSpqx0rDiGUgW64hO34e20bKqm8jlufHY6mZBbLllMRTQBvz49M1D0nzVppWqxK3KgzL45WQ7lwIW11PdiXwnK4p23TZPbPza4F8tW2XZp8X9ykrZMtK2KKPamVsV7TN9iNOU2j3O+vfU83sPQ6/3W7f9RXfkf03MXpAkD/1f9opT/abtcrY8vsuPKrSe3arlbGV4xq/xy7i/rP7+OyPP5vOdfsuAAAAvsqEsQAAwEWOb45x98ldPP32034K1i5FapehWVkl26R5RWxETOHQGISOVanTWq5jFW2xzmxMl2dTEtfWjs3C0ulzimlt2jxwm60pG6fjs4B2PJQHsw8I7NacDWGH/YuC2DwXK/otQ7PVIDaW56ZrzqwdW61izc/lbWN+vPpstSmJy7EXIe9qZWxkAW4U171L429mDHtXQthxv7YWcml1Suva933mWO3+a32etRYMF7/J1PVTD49V9fsX+zi8Plzn/QMAAHwBCWMBAICLvPzLl/Hqh6/iu3//u/HRX/8o2kMbKVJ0u6FCth0Cty5LWZps20ZfSTluxyrZLquMbZqI9hTUjpWxa9Ww+ZTH09qxafl5ClvHtikWFa95VWy+1uyszbtMkGpd5YFW2WbtXFmFWDtWBpnDsdXzxbnNqtnKdutcddvN9zfXiK20qVbGFsema/PjD7T2/U/9Db+XqdozqxDP9yeXFqZeEnyuyX/TtT4fOYZLKrJT11cJP/ujZ/1asGWADgAA8DUgjAUAAC4yTu+ajsPfON3rMDVrGarlFbL537TGbMQswJnC1lSvjJ1Vw5bhbL4u7FgFG/Mq2dn6sc0pbJxNJTuOpzkFkdP6stl4F84FWufCp60QtjxfhLARRag6bKtt14LYvO80H8da1Ww5De7mOGshXP6bGE5sVdeWnxd9royrnLJ47buYTVV8qez++XTIU7X2+DNvst/p7KYPu93svuesrXdc3Hf6b3H1Vtu/yYj5bymlFIeXhzi+Pvbv5Jiiu+siHSSwAADA15MwFgAAeJB0TNEduuiOXV+BeuzXkE3HNJ2PiNMasRGzbdM1p7BzqJBtoonUFtMND+3ytWOrlbERU1VtvlZsRMyqZCPmwdLUJkuVpmA2q3KcgsVFjlZMWbyWadUqCs+0mQVdebCZnatO8bsSwi6C1zxgLatjH1oRW9x3XEv2XNXs4ny3bH/JteM/Alhcm/1jgUjRb7NxRcSD14edyULkcWzdvjtVeG9VxTbF7+fSULasct2aBjn7jdfGPl5z0e8xP7X2Wxrf+zHFqx++itc/er3eCQAAwNeIMBYAAHiQMYxNh9RXxI7Vsrus8rTLwtimr76bAtM2TVMT5yFek4rK2CFsnSpjU70ytjw2fo7IpiaOtAi/ZpWy47ONO01RMVgJ7VKkeQB2Sai31WYrgM3Or62zulkde0EoOws983MrQWxtTFvjXlxf/mX3XJvu+NJpi/N+L62MvVgeQmf7YxCZuux30cSiSjYP+Wdh6VogWwa3kf32pp9r/eKz02pf+i5qv7OuD5/f/PzNInw/vDhc2DEAAMBXnzAWAAB4kO7YRXffRfek66tVj1koO2wjIqIdAs0hmJ0qXLs+mBpDq6katuvbzNpHmkLaaTsGrsV6shFZCLsV0GbBVbUqtnKudv7RVZXVQsRUP1cLwdaO10LY7PgieF0LavO2K0HsoiJ2vE+tXdbHufVjayHqWjg7temy/bG6tivC1/w5H/qlpZXP+fN3MZ/Ce1zDeKy+HivBx997Nn1xLaiNmFe1XlS9XVhb9/hi5Xc7HhuD9y7F4fUhXvz5C1MQAwAAbBDGAgAAD/Lqh69i/3wf3/oXvhXNN5tob9vomi6aYx+MpvZUOZeHUNN2rJbNgteIfj91KaId2mYBWtNklbVZ0DWbnjgqIWwa+k/LaYnLsGormC3P16xVJ1avXeuqbLYSwK4GZWshbLlfCdfK86tVspUpjtcqWWfh3ZkgdgxQz4azXSyC1tl6rbXK2C4LbR+p+h0Of8f7Yxzvj9MU3dP5fHrupvJ7zPbHcL+cYjs/Nn3Hzfwe0285TvdbDfjPP+jp/uM9U4q7j+9i/2I/++10h27+zAAAACwIYwEAgAe5/+Q+9p/t48Pf/jBuPrzpw5ih0nWqjG2ztWHHaYpTmk1FPFsvdgjQZtMRFyHj2rTEW1MVT9MQN6dwaVYpW05HHPO2EbGYynhss8zmHhFKVS6ZBbBlm5UQdrr/WwSx5dqxq0FsOc4i+FwEomWfa+03+rpkeuLaNbVg+V3Jv4du38Xx7tj/A4OIac3Y2RrHQ7i6WL846tXaaxXc5bkp0J0Pbl5le+4fF6z8JvIw++6Tu7j7+K72KgAAANggjAUAAB4uC6B2t7too410k6KLfuriKYjKpmOdQqOxqjVfU3YIVafPeUXtGNC18yHM1pWNU7XhVGmbsntUKhFn+3m/qZkFWVOFYvH8EfOpZB/0+srAteg3369WOF4SwI7H0rLdagg7Xl9eVwlBI+I0JXUliM0rXvN+q9tizdHq9MR55Wxe6VqEr6nr+xv/zlbErk03nb/7tfczjGNaQ7kdfo9DdfgYzk4V4dH/3qbfTTk98dp26/PWsWnolbA2+zw+w6sfvIpu3y3apkhxfH1cvwEAAACrhLEAAMCjdPddHN8co3u/i2gj2q7tw9AxXGvTVP06rgc7bbPq1ylITdlasEVl7Gw77VYqY5t06if7PFa7LiplK5Wx07Sxka3vmd+7DGrfxtrlayFstl+bwviS9WMXbcbPtfA1vyZvU95jY3rhvP+1v7HPzemJy/HHPLgt+5tVx76ttd/gMcVxf4xu3/UhZjv8ZobtFMo2p4C/2fXn838ksJiSOBW/w+x3O72niHkAm+ZVt+m4EUIXv5fUpejuu7h/dh/dfbdyEQAAAI8hjAUAAB4sdSme/fGzuPngJr77978btx/eRnfT9SFPG32lbFZxWq6Lma8dW634q227+TVldW00w9TIkebnU9Z+LfSK07hmAW6kWQg2tpumfX1cYewpDNuqWCz216YvroWrD1ontghsq9MSl4FsWRnbnfpYVMR2K8fz/fH8uB5sNz9XO162GStn8+0sEF7z0Kw2ncby6kev4tVfvYru2M37WfldNE0TT3/1adx842YKaKegtlIVPv5jgOl3cuHvLR1TvPyLl3F8c2E16/BMi6pYAAAA3powFgAAeJQx6On2XaRjOgVkXRaWjWvHjp+HNWObrpkqZ5u2mYWCqxWyg1lFbLGe63Qsv77J+t5amzNrV4ayizHkAW6MzbaTstU1ZWuH87B2JaCtVcaWwerqtnaPvE0lsJ0Ft2v3zP/igs9FAFx7R2OV62xcZz4vnuEh8vdSPGfZprvv4vDq8KDuj2+O0dw28zA24lQ5W05hnJ+75B8BDMH08fXx8jAWAACAqxHGAgAAj5ZSiuObYxxuD9HethGpD1fH0KiNNrpdF23Tn+uafk3ZfArWma3K2LF6sM3Wih0rM9vo751Xy5bXpaJSdlgbdlEtG/3nReDaZGFYpQpyNWytvrhydxnulvtrlbGLALZ2rBKuliHs5rTEReA5tR0rYvNtJSCdrQdbq5Qd99cqY8drxzZdpQI2u3Y6/gDV4LZ4N/m6tOn4uCmQ735xF/ef3D/soodWYKfopykGAADgcyeMBQAAHi11Ke4/vY90THHz/s1p7diumQK0JjVT8NakZlpPdloztkvTNMRjlexYITutJ5utDTtWvE6VruN6sHEKW8vjETFrP4WtYwBbVkNGUemaV83mx2K55uxF720tfD01OH08F8LWjhXVnJvTEtdC2Y0gdtbvcL5WwZsHmGXAW/7V2swC3OL4rK/hHuU0ye9KbT3b490x9p/t4/j6EZWn3alPAAAAvvqEsQAAwKOlfb927JNvPYkn334SN3ET3W5Yd7IZKmPTqTI2NSlSm6I9tn01a8RyO1wbMVS69ulqXxE7horFmrNjRWzTDcHrUB07XZ+KNWPjVAU7W6NzqJaNiFnbMTubKmezY+P4HvbiVg6vBa/l/fJzZeBaHqsFtcXn2fVd0a5SMbsIbC+oiC3Xla1VxJZh61jlOqt67eZ95FNjR1eM+bGy91J+BymluP/FfXz6zz99p6EvAAAAX03CWAAA4O2kiHRM0d130e266J50/fqw7bAubBPTlLGpyypYI04B6BjiDe2qFbLdqf1s3dhi23ebVcOO0xFXzuX7+bHyeO381K5Yt/aiV7YWFtYOp+y+tXB2LYQt91dC2fL8uSC2FuBuBbbl1Mara7tmwewUvq5U1c6mJy4C3a3phteOL95XLfDO7zPeGwAAAM4QxgIAAG8tpRSH14do2ibaJ200TRNd24eybbR9KJti2i7WhI1sf5iyOD/fNM00tXHq0qkSttjOqmEjWyc24mwoOx0bxjeFkk3M2teevfosqy9r+9ws8K2Fr5Hqx4tAcTp2aQhbmfp3K5A9N7XwWkXsovp1Yz3ZWdtxrdgxsM2umU1dfO4dn1NeW4y3O3ZvX3kLAADA14YwFgAAeGvdXRcv/+JlPP3209g93UXTNH1lbNOv5ZnaoSrymPrpiPOwNcu1mnZYU3YMUPNpiiMLXtt5Jex0/bim7FhZmwWx50LZvH3e3yKcHcddazc/eFKcq64ZWsv3zgSwEZUQNhXt10LY8Zri/Gr4WrbZ2G62WalorV03tc2rXvMpi4sK2XMh7CVrtVbfxfA+Dq8O8fIHL2P/Yn+2HwAAAIgQxgIAAO9At+/i9Y9fR+pSvP+b70fTNtHs+kC2a7podkMV6hDKzsLYPJyN7PhYkZpO675Op7shmB1D13aYzjirnh0D3DFkzT+P/UYT83Vks2A2D2xn5yoWIV8RMl9UqTnL/dLiWDWAzY9vhbDj/sb0xWuVsfl15ypjLw1rq5WxXZoHttmYF5WyXeXYpdWqa81SuTt/5tT11d8v/+rl8vcKAAAAK4SxAADAO3P/7D4++cNP4oPf+iA++t2PIpp+muKm7YPZaCKaXRNt085CwtnniOU0xvl0xcN0xHk2uqiSbWMKVKdgtAx4m3Q6noa+8ymHh2PjuMYK3dk487HlA7q0QvPSYHAlfJ36Ko/VQtdy/5IQdqMaNiJmawFP27VANpuCeFYRW6t6LQPavBq2S/N1ZcfgduXdrSrfzXis8nd8c4zP/uSzOLw6CGIBAAB4EGEsAADwznR3Xbz52Zu4/eg2umMXzbGvVk3HLKxrTuHdOA1x0wzTE7d90DZVvMawP4SiYzVsimL92CFQG6tom+40FXG+Nmy5TmweoE7HmtOUw7VpkIsDq20fpHJpNYAdPi/C3JVgdnMK41rYOrYt9qvrxw7XLsLgWhC7skbsLNytfK5dv9gv39XWuz0Tfte+w5RSdIcu3vz8TXT3klgAAAAeRhgLAAC8c92h66sIh/CsaZpoUjMFnSlSRBvRRRdNaqZtk7JpglP0Fa4Rp2B1DErbOFW6ZpWpU5g2HJ8qaaOoqo2YrUc77kfEfN3Y8lxeOTuOc8VUVTs2vWQa3bJJLXgtzxWB7OxeZeg6XlcGrXnbtSmJ8/Yboeu07Sr7keYVrSnmFbPFsVrb/G82trOvdhlur1XFzu55PP1jAgAAAHgoYSwAAPDOdfsu9i/3/bqxN020x7YPVHfpVBnbRD/laxOnStbUV7Wmtg/CZhWu6RS2ThWwcaqETU2awtz8XEQsPo9ttvZrVa9b68ZO1wwurtasnKsFh/nni0LYtf1zoezYfiOIvTiQLbcxv3YxrjIQrVTD1q5/F6Y1YrNOU0pxfHOM45vjO70XAAAAXx/CWAAA4J1787M3cfeLu/jmv/DN+OA3P4imaaLt2r5CtusTyyY10Ua/dmy3Gypj22b6HCn6gDVOFbXRxHw92LJCNhX7Y3XsWAGbrzXbDCHtWAm7sk7trO3YTy2YWzu+Za39LBdN83Yb4etateyla8auTVu8Fb5uVsaWlbARF1XEjmvFblXELkLr2vvcCK9X180d++5SdPddPPvnz2L/fB/d3hTFAAAAPJwwFgAAeOfGqV33z/dx9/Quml/tK1zbrp93OO2GYK7NgtAhzGxSM1XONpGtJZuG/SGEa6JfK7ZJfSVtrRJ2rSL2orVjiyrYPPjbWjv2cS+scp+1YHFlLdlqMLtVKXtJ+BpxPnRdOxfLNvm989C2vH4WuJbVsJGNfeUdLlz43eRjPrw4xOHVIQ6vD4JYAAAAHk0YCwAAXM3Lv3wZr374Kr77r303nn73aTRtE+2ujWgjmrYPaJv2VCEbbUSXuv5cxHxt1hSnqYybZl4hO2qLtWHz9WGHY7W1YmeVs6lSMZtObRdB4NsGsaNKH2vB62wMtYrPtf08gB37eUQIO/VTVL9WQ9luZX+ofp1dW6uITcOardm1D3mf59aKLatix/t99qefxd3Hd1NFLwAAADyGMBYAALieFJGO/XSv3X0X3W1fYdgch1BzqIxNxyLcHLfl1MJtnNaU7fogd7Zm7Lhts2mOm2Kt2TGUrVXHjoFvXgWbmun+KaVYFsWeWUf20ldVW2M2LT/Xpi2+ZGriRfCaVcVO7fM23bzNIpwtr1kJbfMK2LzytBbaVitii8rZ6R1svsyVd1W2Kd9NNo5pPAAAAPAWhLEAAMDVHe+OcXh9iPamnULUZndaC7aL7lQdG3HatsU2xaki9h2uHTtts2ubNIS1TRboZZWzs4C2dC6f3cr4ysCxbFup7twMYM9Vwpafu6yvMgQd133Nq1yHa1YrZCuVsXlFbLku7KJCtotZKHrpOrELReg89VWO7ZgiHTZCXAAAAHgAYSwAAHBdKeLNx2/i8PoQze82cfP+TR/EpqYPZaOJtmmjiy6a1H+OiPn0v3kYWm7zz0VQ27TNqZo1n7Z4rUI2FVMR5+FsWTlbjql45q2K2dXKzo1gsVYRG1EJYCvH8gCyDGHLKtmp6rVL8/Zp/vnsWrJj2FqZnniqfC2nLC6OT+FuZOO80Ga1cMzHn9/37ud3cffpXRxeHS6+FwAAAKwRxgIAAFd39/O72H+2P60be9tGm9ro2i7a1M5CtnF90IiYVbxO675GUeXaRl/NWWafzel4E80sbJ36SfO1YZsmC28jC1RTnNqlmJ9fCWUfEhwuiz0rlZm1sDU/npYBZHUq4vGalRD2XDXsavhaBK3TfYtq2c21Y8eK2LLydu1Vrryjc9MTl886BrFjGPvyBy9XLgYAAICHEcYCAAC/FN2hixfffxG3H97GN/7mN2L3dNcHmMP/X0kTTR/Qpq4PbKON1KV++uKmn8p4qlptUz+NcF4Jm09PPBqnNy7Xny2nLY55SLuowC0+N818vdlZFe/bKEPZcgrkWiBbVn+uVYRWPq+GsNnxS0LYshp2LaC9eHrifA3hMTQ+865Wg9jiGavvpYu4+/guXv3oVew/2y/vBQAAAI8kjAUAAH45UsT9L+6je9PF+7/5fh+I3jZTdes4VfE4TXHq+qB03M6mGY5llWzTNFP4mrp+f9pGmk1HfMk0xdHEVCWbT1EccTqeP9sY0D769dTWnk3Lz4u1ZMuq1/LYWvBaVs3WKmDLytmy6nU8V64le6YSdnZd1v8Y1s7GdUkQu6Z2aVkd3KXoDl3sX+zj9Y9fX9gxAAAAXEYYCwAA/FId3xzj2X/2LJ78ypP45t/+ZqQnp+rE5qYPPZu2OVW8RhaQDpWwKaVTlWxzCleb1PTHK5WwqUnTuelzxCzknX2O4ngU57L9aRrkx+axsxx1Garmn8+GsXn4WlTJLqpgx/ZnKmHXjk8hbDE98UVrxnan6YHLatnVatjynVz6XorP+RjuPr2Lz/7kszi+OdbvBwAAAG9BGAsAAPxSpS7F/vk+mpsmjvfHvvJ014ekbdNGak8BXxPNvDJ20ETTt+v64DalofK1G4LW9lQNO4Z04+exyjW12fmssrW2ZmwZDDZF6joLAh8TyK5VeuZB41oIeUkIW1bBroSsUz9FBex4fW0t18W5rQraPJythb55SPyQ91SeL99N9lzTuS7F8c0xDi8Pcf/p/fm+AQAA4BGEsQAAwOeiu+/izc/fxM2HN/E0nvaVksc+rGt3bYwVsk1q5tvsL9ohtG2bKYRtmmaqnM2rYRcVskOQOKuWjWa9GnZl/djV8PXSUPZMEDvtVqYhnh2/sCJ2umYljK1OV3xurdiiGrYMbtfWil2cv7QaNn+WrYrY/Pnz7THF/uU+PvmDT+J4dxTEAgAAcDXCWAAA4HMxBmJN28Tx/aFCtmkiHVN00U3VrW03zlW80tEwdfFU8Ropmq7ZrpBN/bmImFfLNqc247lpP6t6LacSrq4Xe0mV7FYRaFkhmuafayHkRSHsuF9UzK6tJ3uu0nVz221Xws4qZR8SxK61WXnO2TsZKrP3z/dxeH2IdJDEAgAAcD3CWAAA4HNxfHOM1z98Hd23u4g24vaj20hdil3azSpjx22b+imM27bfNm0Tza6JphsqZPPK2Dadr5BNp0rYzTVkI5ZVszE/v/j8tspwsghi+03aDiHzflYC2tU1YaOvVM2PP2jN2DyE3VorNh9P8fxb72Z6/uJZy3dQjjl1Kbq7Lj75p5/E4eUh0lEQCwAAwHUJYwEAgM9P14ey+2f7aJpm+ovbIYjdRb82bIromi6adKpwnQyFs3mA2kQzBahj0JqaNAtjp2MRfb/D+fzzVPU6Vs0Ot56C26H6dRZ4vk0oW8sGz4SPEWcqYCNmoWs1kI04H75GEb5W2tZC2UUFbFlhe+75195FLaBeqYgdg9i7X9zF4eUhjndHQSwAAAC/FMJYAADgc3V4eYjDy0N0h24K03bdLiIiml2fbE5rxu5OlbLRRh/WdqmvkM1C0yZtrB07hrd5iFtWw55bN3arQrY41pxJZ1crYCvHquvG5lW05ypia+dXKmQvWTN2s0K2m28XFbJloLr9kubv64IgdjYV8rAe8fM/ex53P7+78KYAAADw9oSxAADAF8LhxaEPWqNfNzaaiPam7atld020TT91cZe6PnxNTTTRzKcXXtvmf2m+nVXN5u1jOBfzc2O17WTsK07XRJyOra6FuuHcerFTv8XxPGCd9bNVIbsVwkaaV8x2p2PVYDY/Pk5VXIax5VTKmy8i/7gMnGfvq3zGbGxvPn4T+2f7OL4+XnBTAAAAeHeEsQAAwBfC4cUhDi8O0d62fUXrron0pK9obbqhQnbXRBt9KBu76LeVaYrzCtkpXG1jGcwWf1PQuhLs1sLZMQSc1p+trYGa95XbCiTLMPdcILsSwJYB6KIiNm+zFsJWQteIWE5DXISxeSg77l9kkUXXq2FnzzseLytjuxSvf/w6Xv3VqwtvDgAAAO+OMBYAAPhCuf/kPg6vDhFdxO79XUTqK2RTStHu+iC2afuAttk1pymMm9M2djEPZYcpjGsBbD6F8bjO7Nlpi2PlWG3/sS4IYyNWwteNCtnqdL4R1YrSramJy8DzbIXsuSC2cr46lfFK6FybOvnNx2/i9Y9fx/2n92duDgAAANchjAUAAL5QDi8PEa8jbj686QPY27ZfF7ZtoktdX8E6VshGnMK6vPJ13E/Z8YhqGJtPUzx9TvPPi+mJh6B3CnkjTufK8LApd08HFlMY1wLLskltmt9UBJe1/dpUvrVz4/qvZ8LXqW0tlC0rZLeshbC1c2eC2Gmcwxqx+2d7FbEAAAB8rpqUzen05MmTz3MsAAAAk/a9NtpdG81NE7cf3saHf+PDaG/b2D3Z9WHsTduvKdtmFbK7ZlpjNpqhgrbNPjdNRDsEom1MFbFjEDuGrrNjEbNpivP96fh4Te5tKmRXQtm1KtnV6Ypr4WVROTud6yrH1ypiK2HsLIQt+rrskbcD2MVzjM+eirF0Ke4+uYvnf/Y8Dq8PcXxlnVgAAAB+ue7vTzM0qYwFAAC+kLo3XXTD4qSpS/He6/ciuj5UbaON1KTomi6a1ETbtFMF6+xvUF3rtYtZ6DpOVzxWuDYpWz92rJRNp/1oYlqTdhaGjgFtKoLbS0xZY1ocW7Qp10qNZVh5UQhb7M+C1sjO5SFsZUrg2faSaYmnoZ9/1tqzLcLp4f7Hu2McXhzi7ud3lw0AAAAArkhlLAAA8MXXRuye7OLJt5/EB7/zQexud9HettHetlOVbNP222ijX1s2q4xdVMcOn2cVsZUq2VmlbGTnhs+j6VhxvLbfFAceOlVxLYTdrI4dz6+Er6vTFK9MRVydlng81q08U81Wk0tC2GIs3bGL/fN9fPrPPo3j3TG6u+78GAAAAOAKVMYCAABfLl3E8c0xjq/7qsd4P6awtE3ttLbrWCnbRdeHrbly3djxc7627FgBm68hO1bUjuvGZtWxY1A7HYuYQsMpoF1krWOD5bnSLHgt+yoD2NqxWghbCTKne62FrmuVsLWK2dwFz7j6fBEXBbGp6/8OLw6xf76Pw8tDpONDbgoAAADXozIWAAD48hgqWt/79ffivV99L3bvnSpk212/jSYWa8muVcmOgeu4lmzERqXscP+8zTSmmJ9ffI5lRWzN2SrZlLVLyzbVaYpTsT+cn4WulYC2uu0q+2Ug/BCV685Ov1yMKR1SHN4c4pM/+CQOrw+RDoJYAAAAPl8qYwEAgC+nFJGOKY6vjnH/2X3cdrexe2/XnxvWgG3a5rSWbLSRUjpVyY7BZJ6LtjFfF3ZcJ7ay/uwYwuaVsLPq2SwHnE1dHDGtRXvJM853l9Wv08etytFyvxLQXhLCrlbG5vd/iJVLLp1+eRzD/Wf3cXx97NeJvT/G8e4oiAUAAOALR2UsAADw5dREvPfr78XtN2/j9sPbaG/a2D3dndaQ3Z0qY6cq2abfztaPbYtq2PYUpE4hbl4pG5V1Y8u1ZfNz+aGmcrBQDTgrYezmFMVrFaVl+BoRZ4PXlcrYd2F1GuaNEHacmvjTf/ppvPrRq+W1AAAA8DlTGQsAAHz5pZjWBz2+OkZ728bT7zyN9naohj1moWzbRJPm20h9tWqTmtPasWN1a1P5HNnnfD/frh2LYb3ZsYL2zHP1m0qlaHm+djyleqj5LsLYLOh9rM2wuXy2csxdivtn93H/6X3sX+wFsAAAAHzhCWMBAIAvrcPzQxyeHyIi+nVjn7axe28Xu7SLdtdGpIjmOFTFdtsVsk3bT1E8TlXcNE2ktk/78mPRxBSq5hWyY3i6qJodjNMUn1s7Np0SydrJ08d3NEVxLYhdDWPfpVpV73i8GO9UlduleP3T1/Hiey/e7VgAAADgSoSxAADAV0I6pnjzkzexe28XT77zJNrbftridtdGc9P027aJ5mYIX7thmuLdUCHbnqYqnoLZbghfs5A2ImahbBm+piYtpyzOzz+0nLOcybcMYYfPZRA7ta1VmJYVsmuhbHm/87MsX/wciwC2Mu6UUhxeHeLlX77s16lNEffP7gMAAAC+LISxAADAV0LqUtx/ch/te22077Wxe7Lrj9+kaFMb0UVfFZsq0xYPVbBjIJtSWlbDZp/zqYinQLY8thHIXv5Q9f1a8BpRBKcXBLDTNRth7OZ4HqMWwo7Hi6mT03EIY/9iCGMBAADgS0YYCwAAfKV09128+cmbPmhtm7j91m08+ZW+Ura9aU/ryO6W0xY3TRPRnipfxymMxwB2Op4fi6FidiyQHSply/VhpwraR5hNEXzp9L4Ri8D17PTE+TXjtMuzh3jIoOf9LMZfGesUDHcpjnfHeP6nz/t1gQWxAAAAfEkJYwEAgK+WLuL46jjtNjdN7N7b9eFfNxzrmmhTG6kdqmbb6EPANkWThqB1PJZNYTz7i5X9fLv1+SE2qkn7zSlYjbgsjK0GspVx5mFqky57gNUAtjbm8flSinToq2G7rovj62O8+fhNdPfdRfcEAACALyJhLAAA8JW2f7aPw8tD3H7zNnbv7+Lmg5u+QvZJXyHb3rSnCtk2+8uqZPP1ZFerZCM7PnweNU0zDygfUWE67daqZCtrxi5C17F9Gbzm/TTF/mIoj6hQ3ZhaOZ8iOXUpnv/J87j75K4/3kV0e0EsAAAAX27CWAAA4CstHftqy+Ob4zR1cHvTxk3cRLNrIlI/HfFUIdvGtH7sWCU7rSHbDmvBtjGFsrU1Y2fhbDSLKYvfJoytVsk+pCI2iv0mm4o4v9fjZ1We9TO773jvYVzdvovj/TGi66cm3r/Yx/75/i1uDAAAAF8sTcr+SfSTJ08+z7EAAABcT1bR2uyaePKrT2L3ZBe793Z9hextG+2unVfI7uZrx04VsmWlbCzXkS2nLJ6tGfuYoHOtQnarOrZsU76Pmqbc3R5smievq2Mup0ROKcXrH72Oz/5/n01tukM3TSUNAAAAX1b39/fTZ5WxAADA10NWIZpSiuPrY79G6SFFc9vEzfs3kXapD2Z3w9TF3RDAjtWyqQ9lU5dVyo4hazvcpxnWma0Esotc85JQdiXgrFacVqYCjphPSzyNd60KdjGD8QOnJq7ddwxgjyn2n+0jdf13sH++tyYsAAAAX2nCWAAA4Ouni9h/sp9CyN37u3j6q0/7qthhHdn2JquSLdaTndaSzath276zq1XIXloZG1k1ayX8LdeLbZpmdY3Yi4e2sgZtWQ17eHGIX/zhL05rwb7lfQEAAOCLThgLAAB8fY2h4b4PCqcpiYfw9faj22iftH2FbJNNW7wrpi1umkhNmk1dPB2L+XTG+fqxq6Hp2jhr6WXtUHMKameBcKUadhGk5k2y4Hir3dZ0xHe/uIvuruurke/6amRTEQMAAPB1IYwFAAC+9rp9F/e/uF+e+M2I3Ye7U/iab9v5GrJTCNuepi2egtCVKtmIolL2oZr559r6rmWAO43jgqrUzQC273y5Tu2wn7p+WuKXf/Ey7n5xd/5mAAAA8BUkjAUAAFixf76P45tjH3TeNPHkW0/6IDZllbHtvBp2CmGHNWTzqtj88xieTlWsw7F+00QlV523qUwvnAevtWC2bFPvvtlut7JO7eHlIV7/5PViiuLDq8Pm/QAAAOCrTBgLAACw4vDiFCS2t23cfHATTWqiTW2kJi0qY8upiiPma8lOwes4jXFk7YfjEf00w9X1ZrOAdRa85sHsUPU6nW8imnR59e0lIew0FfH4uYvYv9jHi++/sA4sAAAAZISxAAAAF+iOXbz52Zt5FWxE3H7zNm4+upkFs+PUxNN0wPl+zNeYnc5HnPpNw7qvw7km9X3OAtiUrUObTuvQRiqmPh6D2TyPfUhgWlS6pi7Fyx+8jMPLw6zN8e4oiAUAAICCMBYAAOASXcTx5XFxuLlton3aTuvJRhuzqtnYDdMRD9P5ThWxMZ8SuJweuFYFu1oZW0xJXKtuXZu2uCalfr3XrMN+M6wDe/fzu9h/tr+4PwAAAPi6EsYCAAC8hf2zfR/S5lnn8Hn33i7e+/X3+mmMd6epjKdpjLOK2Ol4zI/PqmmzqYfH7RT0lllrsZ8HvvmxbGfaHl4e4vn3nkfqFovSRoR1YAEAAOBSwlgAAIC3kA4pjodlxWxERHQR3V3XB7HjNMZZEDuGr2NFbXvTntaTzacpbpr6VMTjdMbDsb6zoQq2zFFTiuO+GGdZQJv6dodXh9h/tl+GsQAAAMCDNCml6f/v+smTJ5/nWAAAAL5amohm15wC0uJcbvfeLj743Q+i3bURbSzWps0rZGfXT7tZh1loG9EHrPsX+3jxvReXBaxdRLfvLnlCAAAAoHB/fz99VhkLAABwLamvnO0/nglBm3564Gnt2byKdjjfRBHEZp/X1oQd16o9vDzE8c1xWQ0LAAAAXI3KWAAAgC+Ktn54LWh9iBQpQrErAAAAXJ3KWAAAgC+ilbD0bFUtAAAA8IW08u+uAQAAAAAAAHgbwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAADw/2/fDk4AAAEYiOH+Q9cpDkGSCfo/CgAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmfbXo8AAAAAAAAA+I1nLAAAAAAAAEBAjAUAAAAAAAAIXNpczcumkMt4AAAAAElFTkSuQmCC" }, "metadata": {}, "output_type": "display_data" @@ -729,7 +716,7 @@ " N = (hit - spheres[i].center).normalize()\n", " material = spheres[i].material\n", "\n", - " return (spheres_dist != inf[DType.float32]()).__bool__()\n", + " return (spheres_dist != inf[DType.float32]())\n", "\n", "\n", "fn cast_ray(\n", @@ -790,7 +777,10 @@ "source": [ "## Step 5: Add specular lighting\n", "\n", - "This section corresponds to the [Step 5 of the C++ tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-5-specular-lighting). The changes to the code are quite minimal, but the rendered picture looks much more realistic!" + "This section corresponds to the [Step 5 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-5-specular-lighting).\n", + "The changes to the code are quite minimal, but the rendered picture looks much\n", + "more realistic!" ] }, { @@ -801,7 +791,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd6XM0SX4f9qxqAM81z9wzO7vckzK5q4OySJk2fTEsH7LCDof8yn+z7VCELdEKSZYVJk1LFimSy13O7FwPuv2ikMi7uhoPeoCZ+XwiEN1VlZWVVd14ZmO/+GVOh8PhEAAAAAAAAAC4V/NDDwAAAAAAAADgm0gYCwAAAAAAAHAGF/nG1dXVQ40DAAAAAAAA4Gvviy++uH2vMhYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAA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XjcWAAAAAAC8PipjAQAAAODLQq8f+6bN68ECAAAAAIDLojIWAAAAAL6MCEwBAAAAAHirDF/0AAAAAAAAAAAAAADgbUQYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwD///bt4AQAEICBGO4/dJ3iECSZoP+jAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABA42/Z6BAAAAAAAAMBvPGMBAAAAAAAAAmIsAAAAAAAAQOACaazXSuW+u20AAAAASUVORK5CYII=" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd6XM0SX4f9qxqAM81z9wzO7vckzK5q4OySJk2fTEsH7LCDof8yn+z7VCELdEKSZYVJk1LFimSy13O7FwPuv2ikMi7uhoPeoCZ+XwiEN1VlZWVVd14ZmO/+GVOh8PhEAAAAAAAAAC4V/NDDwAAAAAAAADgm0gYCwAAAAAAAHAGF/nG1dXVQ40DAAAAAAAA4Gvviy++uH2vMhYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOIOLhx4AAAAAADxW8zw99BAqh+7e/f4rHgYAAJsIYwEAAACg4913n4Tf/d0PwsXFFKYphBCWYHbq5LP1vkM/M+0cOxTb8f3yeii2Dzcb8X18ffXqEP7xP/7z8ItffLn95gAA+EoIYwEAAAD4xpumEK6udrehaS9Qrb355mX44Q9fhMvLOUxTGcROWzqoHLLUtQ1gD90gNg9hl+rXZf9+n16//HIf/tW/+mX4/POyPLYNhA/F/i+/3K+GxgAAvL7pkP2vwKurq4ccCwAAAACcxZtvXoa/9/e+F54924XdbgohLNWu0zR1A9ppmsLFxRRevry6aXe369ZhZy+QTbtSAJu3zbfT8fR+vz+Ejz/+Mrx6dSgqZvMwNwW4SyXtZ59dh3/0j/4s/OpXr+52YwAADH3xxRe371XGAgAAAPCN8PTpLrx40f7fXdM0hTffvAwffPA0PH26C7vdXASxdbXrlmmIa6MK0zaM3f7+cJhCGdBOt8Hq8n4JWne7Kbz99lUTvpYhbBnGfvrpq/DOO1fh6mqurrm8+eyz6/DFFxaiBQB4XSpjAQAAAPhG+K3feif8x//xd8I8T03YOs9TePJk1wSwsUK2dkolbC+IjdWra23K/YemXfm+rpJdzulXzbaBbB3O7veH8Pnn12G/T9v7/SFcXy8/f/AHfxH+8A8/3v4QAAC4pTIWAAAAgK+daQrhww+fhWfPdqEOUacphI8+eh5evrzMAtfjla95KDu6ZjQKVFObFI4u+9LJa2uzrlXP5tvttMYhC2DzqtmQBbBTFsgu7+d5uq2ozStmr69TIPvBB0+LNWXzcPfzz/fhL/8y/R+MAACMqYwFAAAA4Gtht5vCP/yHPwo/+MEbt9WvqQp2eR+3e2FtDEfXql57x9aC1FPadM5a7aNeS7be36uWLfe1VbEhLOFrfB1NZxyD2fjz6tXy+sUX1+FP//Sz8L/8L38a9mYxBgDoUhkLAAAAwKP0/PlF+PGP3wgXF3NT2brbTeHdd5+EJ0/mMM9TCGEK87Lk6W3b5X0IeRjbn4Z4+zzEp0xZvNUSmrYdn7bG7DSYvrisil220/vl3g+3r/v9Mo5YIXs4TGG3W4LZ6+tl38XFEsZeXEzhnXeehN/8zbdugtrlXvb7Q/jTP/0sfPLJq9d7MAAA3zDCWAAAAAAejTffvAy///vfDU+f7sLFxRzmOVa/9qYebgPXPGStQ9RjUxIfF9PQ8vy7hbVTp5q2XTd22c6nO67bTEUVbNzOQ9h4Xv2+XFO2vy+vlo3h7LNnF+GDD57eVtC+erUPX365D//r//rvhLEAABVhLAAAAAAP4mc/eyt88MGzsNuloPWNNy7DixcXN0HsUvmaql5TEDte/3U8FfHrV7jO99RP3xKWHrLt+ngIeTVs3qZ9XfrKt+sq2bJ6tty3hK9TmOcyjF22D2G3W6piY7Xs5eUcfuM33gofffT8tsL21atD+OSTL8Mf/uHHd5zGGQDg608YCwAAAMBXJq9g/clP3gw//elb4fJyLtZ7XaYgTq+98LXdLq7S2fd6403b4063XG8tlOxNXdxOTRyD07bN2mv7Pg9fl2PLdMUxdJ1uKmPTVMbLdh7WLmHsbrcc+/GP3yjWl/388+vw7/7dZ+GP//iTcH3dr/oFAPimmw7Zn9JdXV095FgAAAAA+Ab70Y/eCL/92+/dTjv80UfPwptvXhUhbB283mU64rTvtDR2vZr2/gLenjKkHE1XnI7Xgevyvrd+7KEKYg+D96EIZ+Ox/T695m3y7XwK48Nhmbo4hrKffXYd/uzPPg3X18v+P/7jT8If//Enr/ewAAAeuS+++OL2vcpYAAAAAO7dUu0aQh6Ivvfe0/Cbv/n27bTEMZRd2uWVsHUYOw5f60rVu4aleeg6Xmu22PtawWyqcm2vEdd2jftimDpNaV3YaVr2pzaHME3T7b7ldZlquN7O28T+6zVjY/Vtfp0lhE3jjdvLdQ83+6ab6xzCPC9TGD979sbt2rIff/xl+Lf/9tPsXpdBvHqlZBYA+GZSGQsAAADAvbq6msN//V//Wnjvvadht0vrvj5/fhHefvtJsQ5sOwVx2p/vS+9D8z4PfLcGpPX5477bcaz3NbY+Re+he7w/TfH4WDtFcV0tW7dpq2HrKtllmuLlnFGlbJrCuF8pG18/+eRV+NWvXoVXr/Zhvw/hiy+uw69+9Sr8wR/8PHz22fXaAwIA+NpQGQsAAADAvXr2bBeePt2FEKbw5MkcPvroeXj//afh4mKpfo3VsP0Q9pQA9vh0wcfC0bUpjuvQ9XWus9a+DV5TBWt5TgpPY+Vp3t+hOGFqziurZVP1bF4pm9qX1bB5xexSvVyuaZva5MHuVNxf7G+/Xypm33jjMjx/fnETxi7TGD99ugsvX16Gi4vl+q9e7cPnn+9Pe7gAAI+UylgAAAAAXtt/+p9+J/ydv7OsB7vbTeHFi8ubqti1EHa0JuyxSth2euKR0ZTG/b7HUw+fa63YaL0qtl1Dtrd+bL5/y/qxeVVsPKedurg8Vq8rW68jG0Kqmi1fl4rZ/X7ZjtWyr17tw/X14aZa9hA+//w6/MmffBr+8T/+i5OfIQDAY6EyFgAAAIA7ubiYwwcfPL0NWkNYgsoPP3wW3nrrKszzdPvTC2HrNWDzitjxdMH9kPR4Bezp/d5H6Jtbn5q4aLnWe9M2VdHWFbWH4pxUERvXcy23lzb1WrSHm/1TFshOIVbGLpWuh+z4VIS481yHwlPxHMq1cOcwz4fw4sVFuL4+hKurOXz22XX48MOnt9Mef/zxq/D556YwBgC+nlTGAgAAALDZe+89Cf/wH/4ovHhxGZ482d1Wwl5cTGG3m2+ms52q1zqIXY6V223weWwK4dqxwHVUJTu+Vupj7Vqn6IezbeXr6JxD1aheI7a3bmy975Qq2W3rym6rkK3XlY1Vsr1K2S++2Icvv9yHL7+8Dn/wBz8P/8//88n4oQIAPDIqYwEAAADYZJpC+MEP3ghvvLFMO/zy5WV4+fIqPH26C5eXc1MFOwphtwSwx6pSt63f2q+Cfah1Ykfnl5nqNDjWprNxzdckVrHWlbJl9Wze/tQq2d66smn92LxatryHskK2DY5TxXS+f6mUjesMX1xM4TvfeRbmeQrX10tY++/+3Wfh1avNJccAAA9KZSwAAAAAQ/M8hf/xf/xR+PGPX95Uwoaw281hmsJNCLsEryl0LacfzreX19F6sMfC0lrdb95+/RpbqmDPFciuTVvcC2Hr9qN1ZEfVs1vWjI3b/X1tJWy8xmjt2LZStt7Xr5A9HJYK2WV7qZCNAez19SF89tl1+NWvXoX/+X/+0/Dxx6/GDxIA4IGpjAUAAABg6Hvfex5+/OM3wjzPYbebwocfPgtPnuzCxcUyDfEohB1VwLaB7NIm3xdtDUbvEr7WfW8LfY9Pj3yKdI1DEaDmx2IlarlvOaduE6tfpymFpnn1bH/N2FTlWm73K2bTtacsoC0rZ1Mw2/7Eay2Ba7m+bBxjqs49hHk+hOvrOUzTIauSXRrP8xR+4zfeCr/85RfhD//w43B9rUIWAHjchLEAAAAAFL73vefh937vO+Hycg4XF0sgO03h5jX+pKC1VwW7JYA9Fo7eHumGsf3+j01R3OuvF7bepfq1PwXxsPWw/bK/DGvzqYTT9tKunGa4DFeXfdPNvvVgNp2fgte6/zJ8PTWUjVWy+TinYkrlfLrj/X4JcKdpqaCd5xAuL+fwG7/xZvj5zz8Pf/zHnwhjAYBHzzTFAAAAAIQQQvjud5+H/+A/eD988MGz8NFHz24qYdOasPnasMcC2PXw9ZSq1P70xf0wdq19Gk/ver2K3GNjex1tYLtlauK4fRgez6cVLo/XUxS3bY9PU9xOW5y/3zpt8TJ18XJunJ44n7Y4Hi9/Qri+3of9PoRXr/bhs8+uw//3//0qfPnlMoXxn/zJp+GP//iT3qMGAPjKmaYYAAAAgBBCCBcX022A+d57T8Jv/da7YbebmnVhyzA2hBiojkLYXhB6LICtK1jrdr0pkG/edatt14PZ5bzRWI7tv3+9C42qPqeVNqNjvf3TSru2TZq2eDkWq1pTu8NtRWustu1XyS4VtYfDIcxzqsBN1bfZaKbY7yGEMN++Pn8+hR/+8I1wfX0IX355HV692od/829+dXteDHUBAB6aMBYAAADgW+r584vw9//+r4U337wK8zyFFy8uwtXV7qYaNhQhbAjpNa+OXba3Txmct+8dK/ev97s2BXL5fksl7nia5GPj3GK0PmzZ5lAdS9ML5/3EaYyX7XK64/T+0JyfKlnL0LNfMZuC1DIobdd9zX/i+NemK86nKd7v4zlxuuI0bfE8x2mK430ebgLcOG3xcny3m8KPf/wyvPvuk/DFF/vw6tU+/It/8Yvw85+nihQAgIcijAUAAAD4Fnn6dBcuL5eq1zfeuAzf//4b4e23r8JuN3WmJG6nJS4rYtN2CKet25of6+8b9zsKgfO2vf7rqYjXr79uS7t6zddj7epnFEPJvF0/qD1UoWve11rVa77/WHXsqJo2VcTmVbN1n7FidtmXXuf5cBu05tvx/uP10hTHZcAcr/vixUV48mQXvvjiOnzxxXV4+fIyfPrpdfj882sVsgDAg7JmLAAAAMC3yN/7e98LP/3pW+HiYg673RTeeusqXFxMt+FrPhVxHsaGEIr9cXt5baf7HU0RXG73K2Tr0LfeV7a/ewXuaBy9tufQDwn70+tuXTc27S77qdeJ7e0brRVbt9mydmwIaV3YtfVky7Vjy/31a/3++nqplL2+TtuvXu3Dq1eH8MknX4ZPPnkV/tE/+rPwV3/1Ze9BAwCcjTVjAQAAAL4lnj3bhbfeuroJWkP44IOn4d13n4SLiznM8xR2u/VK2Ho64q3Vqvm+8v3xkPYua9COpiTuX2M9pN167C6OVcvm0w6nqYh7UxXnUw0fmr5SP4fq/FRpGqcKXqYGDjfTAOfPqKyWLSt162rZ8n0aY6puratiQwhhntP9LdWz+bNJUxHHKbJjWBvXms2nRz4cQtjtlnbPn1+E3W4K77yz/LHBfh/Cq1f78Mknr9qHDgBwRipjAQAAAL7BfvrTt8Lf//vfD5eXc7i4mMOTJ/NtEBsD2vx1awC7LXwdB6/5vlGl7bHgt7/v+LXbsUyD/etG7U+ZFrds2692HbXtVb6m94fh/v4asXV1bd3uUAWf/WrYuvo17WurYrdWyvaqZGNF7OEQuhWy19f7cH19CJ99dh2+/HIfPv30VfjzP/88/O//+5/fBr4AAOeiMhYAAADgG+7Zs1344Q/fCD/84RvhxYuL22mJl0rYem3YNohtg9fXX7O13He8v34oO5oWOV23P6Z03dHY1vafYksfaZ3Y4szqWDmlcFkd21bQlu2m4thof37dtX15pW08nipl6/N7FbH1/v66tUvF66Goqp3npWJ2qaKNwe9UbMe1ZPPnE8IUrq4O4eJiGcebb+7Dd7/7PHz88avwi198EQAAvgrCWAAAAIBvoPfeexr+h//hR+HJk124vJzDPIemGja+D2FrVey4ajXfF62FoK879fHouvV0vMerYttrrNka1h6rjo391Gu/lsfK+87Xgy236+dzaI4t7cuwNU1TnKYVjqFtmsZ4Ks5NoeehaZ+3zdvt9+3+9icey/tZQth4jfg+D4KXtktFbGwTr7ffx/OWsT55sgtvvnkV/uiPPg7/+B//xfoHBABwT4SxAAAAAN8Au90Ufvazt8Mbb1yG3W4Kb799dRvE9taF7VXEhhBuK2G3Th2c76vf58FqffyUELbsfz2APRa+jkLX+6iIvVt//Ybl+qxr57Rh7qhq9vWqZcvtGMSO142Nx6Ywz6maNW+zhKh1cBqac/f7WDUbA+rDbVA9Dr1joBsraOebcRzCe+89Cb/5m2+GP/uzz8Nf/MXnow4AAO6FMBYAAADgG2C3m8Lv/M774Xvfex6urpY1YXe7OUxTuJ2aOIawIeSvIcTgahTC9qYhvksAW/dXB8D1vvx6bT/brlUbV8aW97BVWaG6xdbGvTH0zq3b9ab/rfePq2VHbUeh7DiEzY+l/THEjRWwMaiN0xPH17xKNoav5Zq18TX1n77DIYQwh2Wa5/lmjdn9TagbwgcfPA1vv30V/uk//UthLABwdsJYAAAAgK+53/7t98L3v/8ifPDB03B1NYeLiyWMLdeFrcPYEEYh7CgQHQWwa6Fnv+o1X4t2WwA7qn5dG8c4qF1vt6bXNt93PJg9drG1Do4Fr0ubNmQtz43TErd91qFqb1+aHjj1lU9DXI6hbpeqaftTCafXNIKlsjW1DaENZfOq2rpNDGf3+yWcjd/9H/zgRXjx4iJ8+eU+fPnlPvzRH30cPv30uvNMAQDuThgLAAAA8DVTV7j++q+/GX72s7fDxcV0UxEb14MdTUmcB6DbgtFeALot/Mz7L0PZ7dfaFsBuCV+3VcauWW+4VGMeVwaFZf9p+uAQ8vVf69C3fP6HTceW4yksbSuCeyFuXSnbbpfXHFXNtu/L71KsjA03FbPTTYCaHzs035c4lXFc/3i/TwFu/ixjFfM0zSGEfXj//aVC9tNPX4XPPrsOf/Inn4bPP78ugmAAgNcljAUAAAD4mvnJT16Gv/t33w8XF8t6sB99lKYmbiti88B123TE/WrWcl+5P9+3rd9TAtgt4WteTTseW7Nn2PbutnUWg8HFejVs2XbUvrz3/vTDy7kplJyaMcR9ZR91sJueW5wmOA938wrYfFriumJ2CV3DbfgawnRT/ZqvE9ubtjhkY2zXm43fr1Q9O4UQ9reB71J9u1wn/lHD5eUc/ubffCf81V99Gf7ZP/vL8MUXElkA4H4IYwEAAAC+Jna7KTx5sgvvvfc0/OQnL8PFxXwbyKYgtp6CuA1i8zCtF56W2/Hq/XA0bZdh4LZK2PXK2F4Iu3btcWVsuof+/rVz7leekdaVu6kCtq5krati4zM6NMdTH2UVaWozZaFpXi2aV7iW+9bupWyTh7DpWnE8ZQVtu67scixOe3wojtUhcL4/9hf/ACGGrUvV7DIFcgxdewHzxcUcpmkK77xzFa6u5vDs2S4cDiF8+aVAFgB4fdMh+zO5q6urhxwLAAAAACt+7ddehH/wD74f3njjMrz11lUnhJ1CDKBG1bC9IPTUStg6fC2PbQ9g6329duV4joWy/XH32/SOnTmJDSHk0w53j3aO9Spo83Z11ezoWPy/Advj6Zz0ejiync7rtUnVsP338ZxlGuK0Lz9evk+vSwXs8hq3l+P5vsNN1eyyb7/fh8MhhOvr5Vh83e8P4dWrfbi+PoQvv9yHV6/24S//8ovw859/Ef7JP/mLcH29bdppAIDcF198cfteZSwAAADAI7fbTeGdd56EDz98Gj744Gm4vKyrYUdVsO20xCG0gWkviD11OuBRJez6dbZXwdbt27FtD2C3BK/3VSHbhqv1OMvwtKyALffFKXpT5WmvKna6qS5N5x6rlo2VqGUFbl0Vm65dn5e3SZWw7X2na7dVsmW1b68iN1XH1tWzqYo2ZPvS/cf1Z+PxuC5tFKtml6mR5/Dy5WW4vj6EN9+8DJ99dh0+/fS6vhkAgM1UxgIAAAA8cu+++yT8T//Tr4c337wKz59fhHmewm6X1oXdEsaGMK6MTa/H12ltj9XVrscqYcfB7yj07Vfnjsc0Gveo3SnH72KtEnatzaGzs94VK1R7x/OK2t7++6qU7Z2TV7+W7/vH8mrYWPUaj+eVsKkiNp2zVMHm7+vXVCl7fb1s55Wx8f2rV8vrl19eh1evDuGTT74M/+bffBr+yT/5i+ZzAABYozIWAAAA4JH76KNn4cWLJXh9882r8OabV+HZs91tCBuD2PVq2HEQu70Sdr1Ctq6ETe/b41uC2NcNYU8JYPv77z+NTRWfSR2q9itiy2rUvF25Xa7V2lbF9qtl8/5jn71K2di2XGu2rrKtr5FXt+aVsOV2fg/pXvO+ygrYthq37DNed57zoDhV3S5ryE436yqXz3Kel+vsdnMIYR+ePbsIb755Gb7znWfhk0++DB9//CoAAJxKGAsAAADwyMzzFH7v974T/tpfexmePNmFeZ7C1dXudl3YaUrVsG1VbFupuiWE3RLApu1tFbenBrC98HW8vx3reLypr7W253XsYmvls71ze+2nleNbjvWnJS4D2Lxd3SZ+rilITf1ON23SuTFcrfvM9y8Vr0vwG6cXjj/7fRlOpwrdKdtuX/t/rBBCCPPNGrbz7frL3/nOs/D221fhX/7LX4Z/8S9+EQAATiWMBQAAAHhEfvCDF+E733ke3n//aXjyZBcuL+ebqYhDFcKOqmHb8HVrlWo8p96XnxPf16Fvfr36GqdUwW4JYUfVs23bcag8Puc81iphl+P5WNtph+vK2fgs6zVa8+rT5f2h2h+rWMtze9WuN1e+7ae39uxalWzeR71GbOq7XEd2vD8/Xj6T9vub2peVsmUFcfy+xirZshI4riE7hcNhDu+99yT85CdvhH/37z5TIQsAnEQYCwAAAPCI/Oxnb4e/+3ffD5eXc9jt5ptpics1YssANk65uqUyNrVL2/H9thA2th1XxpZj6fU9roRdC2DHwWodvo6C1fXAdf3cu8rDv7U2vQretL9cr3VU7VsGouVnt1ZJWwaw5b566uL2unmAmgfJeQCbtvNjIYTbatc6cF321+3z13A7FXH+POK+pcI1n+K4PH57F9Xvwn4fwm6X3/OS1H73u8sfSPxv/9ufC2MBgJMIYwEAAAAegR/+8I3wm7/5Vvjxj1+Gy8s5XFzMIVbCLtMTh9vtEPJQNgWV8dixkLRuk29Ho6rVU6YkPhbCHquCXauA3RLA9gPQabC/75S2dfVrOr/tJLVtg9o2dJ2q0LG/hmy9BmzeX6qkzQPT1P9yrA1lR1MXl/tG221gW4e37f3n5/QC3Pp6KbhdKlmXNnnouoS6KdxNVciH2/Vj62mNlzB2zp7VPoQwhx/84EV4+fIy/NEffRw++UQoCwAcJ4wFAAAAeGDzHMJHHz0L/+F/+EG4uIjTEqefshK2/z6qg9Fj1an5vvJ9P7jdHrwe2y776LVdG1t9rLdd30f36Alh6zHH+upVZNaVrXU//WmK4z31piBe+ozH62Ox4rNcCzYfyyFrt3wWoyrZtarZcgrkdLwee37OzSire14LZMtjbcicf8/yCtlDSBXlyzkxlI3P6XBI0xfvdstUxSHsw3e+8yy8885V+NM//Sz86levugE8AEBOGAsAAADwgL73vefhP/lPvhPee+/pzdTEeQibpidO61vGILIfzm6tTs33jfb3K2C3h7CjSthx2LoeDo+PpTbbK2RHx+8rnV1f97VpfSifS9rXq3KNW3nFbJsKpuCzrnptz12f1njLvi3bvQratt0S9NbTF+fv+4FsnPJ4maY43OzrVcgufeUBbP0dzacrDmEJYqdpvv2c/vpffyv88pfPwz//578In39+HQAARoSxAAAAAA9gmkK4utqFd955En7zN99aqYjNw9ZxEHvT68nVsMerTctpiUf9b6uEHQWurxPCnh7A9qpSt6j7XK+K7AXObUBbt2mrZ/NQsm2XtuNzKqtlU7u6WrXsq2zbq35NfYyqZNN919MdH0IKmw/FmNJ2WT2b7rcOcVOfvUA2jqu+t16F7DwvgWyqlk1t0pq1y2sMh3e7peP33nsSnj27CH/4hx+H6+t9ePVKiSwA0CeMBQAAAHgA77zzJPz3//0Pw9tvX4Wrq12Y5+m2KjZWwOaveSBbh5/Hq1VD2BrC1sFr/v7uIey26tg6VK6P9461x/v3d6z9XZzez9SEqEk5pXDv3NuW3XVfy2v0j+ed96pcR/vvo0q2PJYC3BTm5iFsvr8MXUPoB7HLsVgJW1bE1gHt0n6/XwLZ+MzSurHLsbRm7ByWsDZW4Kag+Ld/+93wi198Ef7gD34ukAUAuoSxAAAAAF+RN964DJeXc5imEN5//2n47nefhWfPLm4qYVNgVAevMYAcrQ/bC11LbXVp3N8LPcug9JTK23Houq0SdrS/HVf/fsbPYGtweh9B7VrFbN5/2a5dB7Y+p15Lta6YrStbl/f1GqxllWt+bjovXfyUKtn6GaxX5i7X6b3fXiGbnl19rL7nXjVvWS27BLj7ffweH7LtPOSNoe8ULi7m8OabVyGEEF68uAyffXZtymIAoCGMBQAAAPgK7HZT+C/+i++G73//RXjyZBcuL+fw/Pll2O3iurAxkJ061bBtQFuGtMs1tk4VvBbCnl4ZW/a3dSzp/VoAu1YhW/a71mb9WP+Eu4Sy/arXfoA4roKdOsfaqtl6jdet1bD1mrPLue16snX7tP7sekVsOY72eL6dB7ThtrL10ISwyzTBsU1eIRuy/spjMTyNYepyn+G2GjaG1Xn1bPEEiu/t/jagnaZ9uL5eqmaXfg7h7befhN/93ffDn/zJp+H/+D9+fmT6agDg20YYCwAAAHBm7777JLz55tXt65Mn8+20xEvIkwetMZwcB7KLOqh8/SA27ys/b3SdfhC7tTp2PJZ6PPWxLcfH+7eHt6fq9RVDv2xP0/b4+rEpCO1Vuabtttq1fF+u75qqRadQBq9lBWl9P3WVbD32uk0cVz7meky9e8nXkz21QjafmjgPoHvjKf+w4VBsLxW7qZI2/tFEnKo4riH77NkuvHx5Gd5990n49NPr8KtfvRqMEQD4thHGAgAAAJzZ7/zO++Fv/a13wrNnF+HiYg4XF1MRxvbWh53nEEKIr2U4u2zXIVK43d8POddD2DzwTduj92V/W0PYvJLzWCDcbzN1j/X3rbddP/f19KYYzvY07eqK09G6sqk6dWnfr5aNFZ9t+Flevzw/9t2rko3VrnVIW1fntlWzo0C2V0G7/n6aprDf1/vz11Dsm+d4vWWs+/2yP4a5y/FYKRvCfn+4/R2MzyytIdt+F+N6svmzef/9p+HFi4vwR3/0cfg//89fBgCAEISxAAAAAGfzwQdPw0cfPQsffvgsPH26CxcXc9jtpps1YvOK2NBMTVyGm3l1bNyX2qyHn2Vf9f64XVbGjoPYur9Tg9jROPr7j4ewo7BzLVy9z+B1a/+9qWvLIDUFinmIWrevq2UPWaP8+CiUTe3KitRUQTpaS7Z+LcPb3jqyIbShbTmevFJ3fa3YdmwpbG0rZcsgNt1rem0rgfNK2PLzib8Dcerj5fd16SeGt9fXU7i4mMLV1S689dZV+N73nodf/OKL8MknKmQB4NtOGAsAAABwJr/+62+G//w//yhcXc1FEPs6FbHLvl7Yebcq1DIALoPffLs9Z+n/dULYUSXsaQHsevi6JXitx3EfDlWSeiygzYPpFJrWUwTXAW4+9rrSNfbRD2VjoBqvs7aWbH2d0jQMZFNfdRVtWQWch7MpDM3b9d/X1yiPxb7KY/l2rIiNVbRxDPHZxfA13fdUPLv89eJiCtM0h8MhhA8/fBbeeusq/PN//gthLAAgjAUAAAC4bx988DT89Kdvhx/+8EW4vJzDbjffVsPGithl3ckybG1D135F7NYgdq0KtR++luvTpuuV/dVtx2PonTce2/YQdhzAruWq5whdT7nWsYC2nRJ3KsLANmjNt8t1ZXuhbK/idWkzXku2rHRdrhPHMgpYewFtWYlah64hjILXvBK23p/fUx7Iluu+HppjvdA2/04uIeyheK7Lz+EmwF1+j5cpjpeT4tqx+/0ULi+ncDjM4cMPn4bLyyn823/7afj00+sAAHw7CWMBAAAA7tE0hfDee0/D7/3eh+Hyci7WiC0D2XEQOw5ntwex9Zjq9+vhaz+IHV9zexhc30/5vg1Zy8ByqrZ7bfL928LX+85oe1MSL9eZqnb9cHa9YnZbKJsHr/FYXSW7ddri49Ww5dTC8V7XAtm6j3KsdVjbTmWcB6x1kNuuadt7nnl/+fXS+PK1ZPNpiuf5cDvGWF0cQ9n9fqmOvbwM4f33n4Q33rgIv/jFF8JYAPgWE8YCAAAA3JN33rkK/9F/9GF4//2n4epqqYhtg9i2InY8NXEZcN5HCNqGr+vTEt/l+u2+fghbB8f9ELYNaHvb9TXaY8ND927tWmXQ2g9n6/PLsLKdxrgXyubBazxWT10cQ8wyIA1FkNlb+zVVzZZty2A0bi/XORbI5teO5+bH2qrY/An1A+MyrI2Ba7m9VLiWlbHL63KNGLKGcLipiF3uPz7L+LpUxk5ht9vf9D2HJ092IYS8DwDg20gYCwAAAHAPLi/n8PLlVfjpT98Oz57twm6X1ohNa8PGgLOsiC2DoHLa4GXf6wWxZT+97fLa7ft0Tt336Pr5tdt9/eC2fl/fT3s8jbVnawB730HtqDI2v1avTR2g5uf0qmXzCtL6+Gh64vLa7fnL+7ayNK2RWlfE1q+9YHQc7MZwtR13OdZ2quO6rzp8TX2nZ1BWwpYVuKPvcqqMTdMfh2LfEuhOYZ7j+rNLaLvbzeHy8nC7XvT19coXAwD4xhLGAgAAALym588vwn/z3/xaePfdJ+H584twcTENKmLLIHZcETuuSO2Hrr1gtj3WC1tHIXB9rOy/v35see1RCDtl70Pn/fEAthe+rgWq7bHzViqW1ysDuHZd2N6xqdpfVoL2KmVTRWm9Hmwb8taBd95/HqrGMLKtiD1U26EJWEdryMbtukK2nWo4fw69qte4f33q4vL3aFnTNbaJ0w3v9/nzTNWz+Xd7nmO/4Wat2Phslvvf7abbZxLCHELY35y7Cz/72Vvh+99/Ef7pP/256YoB4FtIGAsAAADwGl68uAhvvXUVvvvd5+Gtt65uQ9iyAjYGSzHUPFYRG/e1QWgdJPYCz3SsDnFDsT3qfxzm1mNu26+N6/j7UWVvO561Nv1j44ZrfZyiXxGbd96u99obR30sr26N7dpK2BRQ5sfL9ynoHPWfQta2j3Eg21bTHltDNoaodXDcG2d+Th2y1mNtn2lZHdt7xu33+XB7bnqeqTI2Vcnm1bMptF2mMU5/hPHy5WW4uJjDixcXYb8/hM8/3/cHCwB8IwljAQAAAO7o4mIK/+V/+b3wve+9CG+//eSmInYO0zTdTlG8BDT1NMXLa3+N2Dz8PBaEjvblge96/3Wb1M+4Orbel197yzqyo/G3x9rwtT7e629L+3M4dp0UmOZty3Vf63561bK9StkUMsZnXa8Hux6C5tWzecgaBuvIbglkQ1HxGvvqV8im70E97XDqNz+n3J+C6FBUx/b2parYvEq2VxEb28Qq2P2+rZI9HFJ17LI93/S5v30fw+C/9bfeCb/85ZfhD/7g5+HLLwWyAPBtIYwFAAAAuIO3374KL19ehnfffRLeeuuyqIhdQtcy8CwD0TK8rIPEOjTNj5VGVall6LfWf93mWBCbXzs//1i7LUFsG8pO1XZo3FcI+7qB7do6sfU1yrZteFqfc0qlbBnKjqpk0/qv7fl1BWsewJ5WIZvur62QXXsux+4nto3XK/svr9kbdxxXDHLr9WRTKFyuSZvWjE2fWR7glj/TbZXsPE9htwvh6dNduL4+hDffvAyffvoq/OpXpiwGgG8DYSwAAADAHfzu734QfvrTt8KLF5fh8nIOFxdzmOe0dmRcKza+7wezo3VaywCzrVpN7ct9ddvtFbG9QDUfV7pOe91eCNvf1469bpOf2ztW97PWrn/OFlsa9wPFYetB5Ws6lj+vMpjN29eVsvU6sGvryfarYQ9NUNkPZE+pkO0F8ofmuvHYco086CyP1evh5oFpqR1LCkvz13jusm+el/bl2rEpnJ3nEPb7Q5jn5dzlfbitiI0/+fNf3i+Vsfv9cg9XV7swz1P4G3/jrfDnf/5F+Gf/7C83hfgAwNebMBYAAADgBO+99yS8//7T8N57T8OzZxc3IezUTEGcwsgY/vTWiQ3FsTaULYPJ5P4rYut2+Xbad94g9lgl7Kkh7PEAdv16x87tB2n9dK0OS9ePpcrL+pyyurMfyvbXkx1XyebrsN5PhWxb/RqvU49vfP+99V2n5pnE+6sD23JMscK1bJfa14FtO65yXd18rdjUNlXDLteJIW+cjny3m8LhMIWrq124vJybcQAA30zCWAAAAIAT/MZvvBV+93c/CM+e7W4qYqesCnbqrg8bK+ragDYPcEMYBbHHKlPrtqM+z10Rmyob633l+aOx947V/faOH9vfhsz3o99f2lmGjmUFa69Nfqyslj2tUrYOMY8FsnWFbDy3F8jmYzoWyPaC6XI92XB73TxojdfJw8+yWjZV0daBax4ul+9r7fTEcW3X8rubQtolVJ3Cfp+uH9vGdWUPh+l2TdlULbs02u2mEMIcnjzZhasrYSwAfFv4rz4AAADACXa7KVxdzWG3m4sQtlwrdgop7KyD2HD72gai6TrHgthcHaT2Ate1itg6yB0FsWWoW49vKtrl59b9leNbG1fqt3e8d883e7PrTt3zvgrl2Kbsp20zOvdmK9TPNm+3dv3y/egzCrf72z5639vQ7Ou91vc6vkb/+937Lrf31Xsua7877fet/b2pv+u934Pe+6n5NyH92zDdrB27/Lx4cRF+8pOX4b33nrQPBAD4RhHGAgAAAJxgCWN34eJiygLZUAUwbUjTD3FSv72pfUttiJfvr4PUNmDtB6ltRewoxGrHUQax/fCvH/ylc+prl9vHQ9hyXPXzD0O9IO51f9bUAV+ogtlRH/17HLfpBdztNcbfpTqQjH2OxlL2UX+3eiHqeBru9trbvlfpnPazL8feX7O53l9/78a/w/UfYKT2y78J4fbfhhjELq9zePnyMvy1v/YyfPDB0wAAfLOZphgAAABgg+9//0X4rd96N3zve89vqtvmLIRtQ5o6nKkDm9CEqGWA1A9G631taNTub6cmHh07Poa6TXkfvbbt2Pv76/56x/v7+tWW6+ecR+86/XVlU9s4hW1vGuN6WuJja6eW29PN9nja4ryPZV/qu13XtZ6GuD5vTeozXjufMrmcJjmfnng5tzctce99ea182uMpjKdMrtvn0x2HUK4lG7/r+VqxcZ3Y5d+CeI8xkA0hf11OitMVX1wcwnvvPQm/8Rtvhj/7s8/Cz3/+xbEHCQB8DamMBQAAAFgxTUt48v77T8Pf/tvvho8+ep4FsL3wtZ5qtTe9ar+yL7bvjKJpV4+xF4quV0i2HW0NfutxteHsaUFsP6Bux1Zfu37WvfanBLFteN7/OcWx88p7n5pj/bapfT/QHl+rfD+63vF1ifM+toX2/e/Vseu3bde/Q70/FOjrVwG32/37a6cv7v3xRaj251MWh9vpij/66Fl48ULNDAB8U/mvPAAAAMCKDz54Gn7/978b3nnnSbi4mG/XfNztyrVil+lKy+lLU7hYBkijUPbUdWLb89oq3bbt6Nx+21GbdpzlWLeGsPG8+li73W/Tbztqc2KSOuxn7WhbVTo6L2+XP4u0v61qzdumStbyutPUr5Btjx2rkA1FNWzsr62QrStfe+3yStZQVa7G+z4MxlW3bfuI1bbpnuv2cV9+rNyXzovb+TMv2+S/m7EyNoTptgo2Pc9DVhG73Nt+P4WlMnZpcziEsNupmQGAbyr/lQcAAADomKYQnj+/CG+//ST88IdvhPfff3q79mOvMi6dN1p3s2xbV+Pl1+29lvvK623pL7VpG43GW/df91f1MgyNe2PJz+sd2xrEju47v369jup5ld+P1ZYrwfL68+jv6wfdcXstEJ+qtseutf2PBqpejlx3rY/R9zb//vfO73XYv0j93EdryuY/sb/R1OL9daPTerLxDzsuL6dwdTUXYS4A8M2gMhYAAACg4+XLy/Df/Xc/CG+//SQ8fbq7DU1SIBua9yHkQU1v2uL+mrHLax0Q9QOy3nqw9XXya9djys+t+z0exPaD2V7QV9/PKAA8FsJuCSHT/m2B633nsuM1U+vx99ZiHfeTqlqXc2PbfpVsfJ5llWvevl6PdUuFbLmea1kNW6772q92jVWooyra+nr1PfXuJ353euvhpvPTtUNVAVs95dtjS3/5mrH5GrJl3/G687yMI6+M3e9DFqoebp9V/Ix2uzlM0yHs9yEcDvtweTmHH/zgRXjvvSfhX/7LX4a/+AtrxwLAN4m/tQIAAADITFMI77xzFT744Fl4772n4a23rrJq2DZsvTnr5typ2K77rQPJ4yHneIy9qYbj+/512vCzrOCr920dUxvEtmOtr3ksiJ3CaUHs8crXtprx/mzv+/g6t+P96885v8Zav8enjO7tW+9zXf97lB/fPr5RwF/+UcKxa/f66l9vvX19bv/3qa6krytkl2NPnuzCixeX4fnzi/Ds2U6FLAB8g6iMBQAAAMhcXs7hv/qvfi185zvPwsuXl2G3m4uK2PSzBCuxMrYftvQrYY9NFdwGVPG1vUbdb76/DqjqY3mf9XXWt8v9x/bl91XvL7ePHc/3jcOxcwSuW/Wu3VbO5p/voVvpWp8Xn9+oSrbcLitK4/HjFbLT7Tqn9Xn1Oqz9dWG3rx9bVttOt/fUHk/v4xjr9XGX8aTxrY+1XLs2fh712rGxbaxojdWyy3n9tWNDONxUxsZq+RD2+8Nt6BrHmNaQnW7Hfjgs6euv/drz8PbbV+Ff/au/Cp9+eh0AgK8/YSwAAADAjQ8/fBrefvtJePPNq/Ds2UUVtLbrxPYD036FXl4tl9tS0Zm3q0OkOvgdXa8OPPOxrl2n/1qOuX9f9ft2/L17Od7v/YSw9xHYjqcn7l+r3z6Fl73zetMal8FjG9rGQLaekncUyI7G3Atye+NvpytuX+9yjfp4CPV2eQ+j59yOKZ7XBsfja+VTJaewOw9l86mi29/L1D4dL/99meelQvb6OgW2AMDXnzAWAAAAICzByW//9vvh13/9ZXjjjctwcTGH3W4O8xxuK2Hj+yU4qcOUEHoBbTttcDu9cB10roeb9XTJZUBZhrBlKNtWwdbXP7bdf83bjvaPg9YtIexpa8eWx88VatXVlTd7VwLavH3ZLgV2a4FkuW9bILulQrYOG+tK1XKcvfVjmzuNd5mNqa2OLatz6/Vh8+Nl2/J+0nPorWMbA9v8GuV6tXFfqn7trR1br4fbC2VjyFr+jh6ydWSnsN+noDWuN7vbTWG/n0IIc3j6dHdzTBgLAN8UVh8AAAAAuHF5OYcnT3Y30xKHTuCaB4vxfRvElhW04facY1WwtdRvea1+yNsGrOthaRvUpmOj1+m2/3KcbRBbX7+9rzSG/rF4vN+m33dewdwf6/2Zqp9ybNuC4rbP0RTW/efUD8DL7fWBjKfMnjr74hjLfVtD++Mhexvc9+9jfE/1mHr95r8//T8syNvV73vXbn/n8t/TOJ7e2rHpjzuWf3N2uylcXs7hgw+ehA8+eCqUBYBvAJWxAAAAADcuLuZwdVVWxJbrwtaBaC9wKkOgUTA3Cjt7+8tQZ2mX+hlNT5wHRHVYtB6CrY2lHf8oEDwerPWP9cbXazNu17O13Raj6YazFs01R9MQ947FZ1pPIzxNvQrYeJG2kjVtl1Mhj69bV9DW1aprUyD31o+txzyFunK1rFJtn8Oxithja8f2K3/79533Eatl8/Vh8+dRrhlb/s6kZx2rZVPb9G/I4WYd2RDm+XC7puxuN4fLyxC+973n4ZNPXoVf/OKL8MUXG+fDBgAeJWEsAAAA8K337/17b4Yf/eiN8J3vPGuC2H4Im6rbQugHq73gsDzWhp2tXpt+dWw9vrrf+vw0li1BbDnAtSC2DoLXrn/asdSmv3/U/v71rlFP55sdKc45PZQ9DALW/rm97V4gW1+nnDK4f616zMfWhM1D29S+DG3rNnHs/XH0p10OK2vHjgLmOJY8ZO1Pc7w2jjS9dP07FUPZfIwxgI1TFS+vy5jm+XATxi6h7OXlHC4vTWoIAN8E/osOAAAAfGtN0xK6fv/7L8Lf+TvvhXfeueoEse3UovHc+Nrbn4em5ftluzOaYRBZV7a27dYqcOvwtR5nO47u3ua+Tw1ip1DfY/58joW0IUzD+xw9l6/SeAxTyJ/p2jj7+9ud43brn13v+af37efZu/6x7/C2ff0x9Mc4vqd6nOX3qb7GVLzvPYfyjxNig/YPL7Z+xr1/P2LfvWN5Jf7FxRwuLubbqnwA4OtLZSwAAADwrfWjH70R/vbffjd8+OFSERurYtMajv1q2DJgzUOYfmBzPExpw6P6enWIVE+XnI9hHJiWQemx6Yvba7bnrfXf62Nr+3K7d27Zbq393bUln+Mq0P4Yyil6U5+xTW/K3Hp/b9riul1ve+uUxfnUv/2pkfvTFfemJg6dKYyPn7eck9os2+XYYj95/6ld71nENula7djSWNrq4H6/8ZrltcvveZySuG2zTEd8CPv9Ug0b72m+KZnZ7ZZg9+JiDs+e7cJf+2svw1/91Zfhj//4k03fPQDg8RHGAgAAAN9ab755FX7yk5fh8nKXBa9t9eVou1dRd7Pndn+tDTb7r50zmyC2vlavinActNZjbAPhXhDbHdnGYPXU9msBbrv/HPKOt60D2/TQhKttKDuaunhtTdf1Ma/3vaWvOgg9dt31NuMpkrf0sb527LbPIW9bf4a9e+0Fwfl1637b8/LpkGOgHQP1fC3Z2Ee5puw8L2tYv/321cn3CQA8LsJYAAAA4FtrnqdwebkLFxfLWo2xGjafLrQMaMuq2DbgbAPRXuh5LODsVcXmQU3brj+G8TXL4/3Xu1fEbglbe8Hw2v7e8Xhv/f3HHE+2xuvALufX1zwWbpZtUijbHhufkwK9fpv4jNfXgE2Bbdumf+7onF41bd22vqdta8fWx/sBaV5FG/uo15fN94+eQX9s8bzYNl8rNq0Zmwes8fy0XmxaPzb+rs5zCPt9XEN2ueZyjeXfpBBC2O3mEMI+PHkS144dP1MA4HGzZiwAAADwrXN5OYd3330S3njj4nZa4jb0bM+rj7VBY9muPb+fGOb91QFrHmCu9V9Wy47H1d/XXwv3mLVgt30/NW3T/rr91Am18+vFtTbHa+VuGH3102mx8l3onbdlLKMwuX9sax/dVkfOHa8fu9bnWrtTw/2jVxsG83f6wFevUY+xbrP+b0I5nXnbb/46Veel73E8Xq8jG6csXkJZAODrRmUsAAAA8K3z0UfPwj/4Bz8Iz59f3KwVW1bG1uFKHrKkQDEP0fKAJXT3j+Vteuu9tmPp7U/X7AdMvTAov35/DOW18n3jILbe14aNx9ayrdvUx46FgutG1YWpw6Vysq2abHq6rbAs+87bjqa1LY/FE8pK0LX2S7t+dWvanoZ9js5b9q2vn5rvq6tj633r105tlr7r6Yzb8edt84rYdP1lf92mv6ZtuV5sfGax6jVWw8bK195asXX1dKyAzT+j8veq7CP/HZ7ncLuO7LJ27DJV8ZtvXoaf/eyt8Gd/9ln4oz/6pP8wAYBHSxgLAAAAfGtcXs7hww+fhe9+93l4443LcHk5D4PKfrVbW7U6qnbdph84lv2XY6v31+eWfdah5XrV3nCU3XbHg9h+H+Pwtx5zfU+jcZ72ESyNe4FrPa6l3Xhq2H6o2p/6dnT+1vVit5y/tt1/X463bNObfnj7WPrH19eOHY33rmM45X7Wjh8PXvthddxfh85lUJ76Kf/dWV7neflDkTRdMQDwdSOMBQAAAL41Xr68DP/tf/tr4Y03LsPV1XwbdKSK2FBVxo6mVm1D1DaMrMPKcRBah78pkGzPqffXU5y2Yyr7XB/PeJ3Y8n6PB7Ftm2nl/DagzY+3gW2ojp8mr1bsHSuvMQ2P1eMoK2XH67puObcXRrb71sPN2Cav2O0Fsnkfx68bQ8Q6eGyD6NVRNef3r5e3C9VauCGM1rpdxtILRmPVbj3WvPq33pdfI187Nr/vvNo1hs7pO5bC1vR7eAjzPIX9fjk3rh07z4eb1+V+lmmKp3B1tQsXF8JYAPg68l9wAAAA4Ftjmpbq2IuL+SZ4jWHm+tqjKTAtf6pWzTl3HGUnfG2DyVGYODp2fDzrQewoZI3njttMxVjqY2XV8ZTdX72e5lTtm7r7Rj/ls2jX6myPdZ5Qp6/yePk5nfIZrD3P9T7W134dfU+26Afxp/TXu//1au3yu3L8Wmu/t7G/9VB//fNcXvttRv9u1PcSv8/tvdXXqf89StOm73ZTePZsF95//0l4/nw3HDMA8PgIYwEAAIBvjSXUmMPFxWht2ONrxdbBYh2qrAVovdfyWqMgdCra967XW082tanD0vr624LCXkXrqMo1D956IVP7k4dPc3Vszj6v6bZdvW/8M75mGegWd70Stp0ayo6O9Z9zft7WQHbt+CisHYWU2665/r1Or+P1fev2W65df8+3jHNtf38sZWV63D8eZ+93tfc73fudb7+T8Y9Elu94uF3T+uXLy/DDH74Ib755tX4zAMCjYppiAAAA4Bvv8nIOv/Vb74T3338anjzZ3QZ5ZZjXC0bHwUqu3V8Hab0kaLxvHKTWlXaxXT80Ggdb/erE8tr9kLWn7rM3dXIdcrVtR2FyL2hcS9Z6U+X2pvM93Ewnm85J2/F4utY0xTb5WKasXeeq03gK33RsdM5p+0b9ZS2aMWw5L5/+9/g56TnHNvXr2tjy53Vs+uX4OeVTEOfb5b7y3tN3KO2L0xfHKaxH0zaPrhv7zM+v72fZzp9R2t/++3MottN0xXPY7Y4kywDAoyKMBQAAAL7xLi/n8Df/5rvh/fefhqurOdThah3C9sLJXB2G9o5v0Ya//bC0DmJ71xkHxceC4eNBbNumntJ4FJ5Oxb464I6voz7qkHk03vp+2rVdD6G87zqInW7CsTxwnZrt/lqudbtqNEUgG69XHzt+zpawdW3t17WAsXd+9wrFeW3AuT6+vF0/GB6PdfTs63Gkdr3vwVqI3I4nPo/4+cb+8muMQvI2cM2P59+Z/Pco/1nWjF3On+fDzfrWc7i83AljAeBrRhgLAAAAfGNNUwi/8zvvh48+ehbeeusy7Haj6W3bwDMPN9eqNNtKzeNBSS9sTOf1K0xH4+hNr5vGNlXnta/1vt74186P412rgK0D11EQ2wtqyzG0gXTrcBPetdtrYeVt60PsvxfSpn5i25veBvvzsW+vhB3tXwtY0zjWAtJ0/Fh4WgaivYB72xiO9b82juMB8vqz7ffTv5c0nrY6tr7Wcr14n6m6NY6jf28p/O///tQ/hzDPy5TFh8N0E8ZOt1OsAwBfH8JYAAAA4Btrmqbw3e8+Dz/60Rs30xP3Qo8ySMwDyriv99q/3vi8tf5OmwZ5LZDsr3FaH1+7j1EA3d/uV/AeC1xPDWO3B7Gxr/UKzxBCFrKmwDVVQdbVmPn7OJYt1arVyG77CuG0qtJtAefGUTR9bQk9m16mPJA8/Zr9/rZdN3Srn3v9HAufT60GXjveC8LLoLY9Fm6D316/vSnU4x+T7HZT2O+Pf88BgIcnjAUAAAC+0S4upnB5OYfdbr6pNDu2VmwZBMYQML3mIeKyvxfCZntu99cB5nrbccVubxxtCNsPUevjxZ7V4LUfkq6HrvH4VGyndmVA+/phbB5OpfcpuIv7DrdTzvbX+Fza5EFt+b4MaJftfIxbpi0+vr8XLNbt2u0yqDwtaO0FitvHedprvb5sP/BO/W8NdMvzQuiNN7UZj6d8jc8nfm/qqYvTtMNL33l/+fjy70j6tyf/nV8qYvf79P5wiP9uhbDbTeE733kanj/fhf/3//1V+Mu//GL1mQAAD08YCwAAAHwjPX26C8+e7cLV1e4mfB2Fl710rw4hj19vS5uyfRmwtmFqfyy9a/Xuo9d+/fXYDbT9b6l+Tfvy0DVvU+7r9ZcH2Hmfi0MRWKYArA3XygrYPHA73IaY7etalWwdGObPalw5uzVcbPvd1mbtnFPC4PL4KWuwdnsIp1THbhlP+uy3VENvq5genbsWUo8D5Pz35FjInapoy6A//W7M8/K9fPJkF0JY/tAEAHj8hLEAAADAN9Lf+TvvhZ/97O3uWrGpMjYPCUO1L4Q89OuFpVsD2Lyyrgwdl33xWLpOv3q23N+vzm37z6+xPZjt9zd196XttJ5luyZvvU5vajvPc3NO737XparXEJaAtayCTfuW7cPNdgzpUgDb6ztWQfbCsnjOKYFs3uZ262i4uaXPLf2u9XPq1L29a61XmY7ObcdVjmVr+Bu/Kr3zjj2L8rVf3Zr31Qtm8+9EHdDm7cp/cw7Z9iHM8xT2+xDm+XBTGbuMJ64be309WzsWAL4mhLEAAADAN9LTp7vw8uVluLiYOyFr+7ouDzPLE7YGsv22dWi61v74hdbGst5X/8QyLE77RkFsGRSXYWwMXMspons/eX9bw9hU7RpD1hispgrUUTh5uL3HFKqOqmTbQDY9v14g27teve94ILtl39agsnbsvDJ83LpW6+tf95T2o4C1bTPaX75fu8/RNesAON+Xrh1CWVUdmvHk5+Xbsbo2vp/n5d+458934bPP9mG/v1PJLwDwFRDGAgAAAN9IsYJsqYqtQ8C6Kq2uwuy/Rmm7H2r2zlvvuz+mvL+yr/WK3bL/qdpX32d/uz02ZWNq14VtA9X5NoStq2HT+xjQzsU5bRhbT1Ncy6tg42v8CeFw2C+tDofhT7zHfPu29w3TF+fVkL2g9ZRAtueUtr3rtaHjWjC5fUrfcUh6t3GuH0vPu610HR3r32fUVuu210/f+7ySOv/sR59t+fnX/x60P2kt2qVSNv99OhSV/RcXc/jww6fhjTcuwh/+4cfhV7+67j9EAODBCWMBAACAb5R33nkSPvzwaXjnnSdF8BdCGXokZcpXT/E7CgHXw8Fe322weOwa267ZH//W/tfG0ga6o6md+1MQl9tzN4xtpy1upzqu76u0FsTGn/lopWPeV7zW8UrZLRWy20LJ3FpAudau3D79uncfz9Zrjdvd9Z6PHTstLD6+bxQQbxlfbL+0qacujuFu+o6lqYtDsT3Py76LizlcXc0rvxsAwGMgjAUAAAC+UX70ozfCf/affSdcXs7ZOrHh9rUOE0MYVZfWIedaIJja9Pb1q27b0Le3DmyqyivPq6+Xj68MeMp7Wg9o8/PzQDSee6widrqtck3ha/06hWmau6FsHsauBbFl4DUKYZeK2HLfPhwOIez39f70s6zR2U/TxoFsPq5xILseFp4S6m0LP08NLk9xl/PjOfXrsX63XGtrmxCOV8Fumaq47itOI1yvl5sfq78r8fuyvD9k7fM2+fFy3eurqzns9ztrxwLAIyeMBQAAAL4R3n77KvzkJy/D97//4mZq4noN0vjaBnt58Jj2ba9YvWl9vEW3//a8OhzubcdrHhvjKeFrHv6W4XNdEduGsXXlax3C7nb5dgzK23NiGJte10LwtWrYJRg7HA5hv9+HEA5hv08BWx7Q7vfrzzCGriG0AWwcXztF8XZbzzm979ND29H7k666IXQdXb8dc387/zx6/YVwSpXw+PiWULwfII/HVj/j3rH8tf6Jf2Qyz8s07Kf9OwUAfNWEsQAAAMA3wttvX4Xf+Z33wpMnu6IiNv8JoQ42+kFoGXL2wttyfy8M6YUovf7roLXTU39vN5xd31cemwZ9tPeb2qdwdNlfV8Gmqtc2hN1l++swNn8NWSibj7O+oTxUjdO+Hm7D1zyUTcHYvghjl5B2DvMc39f9T7evMfRd+loqFfMwsAzfUriXgrdxcJc97bAlPM3bnhKYtm3Xrtc/tlYxerfwtves7h4EnzquPLhd2pWVrXmFa2pXh+/9e6jvr7xu7DRNRZxX2cZK2NRv/gcQqUo2/jsHADxewlgAAADgG2GapnB5OYfdbg75VJ7lNLq3rbPz1gPR42HpcETNtcZt8rAl7a+vXd5DPa6p2ldPSdw/t75eum49TXAdxvaqYFPYGkPY3W53G8rOc/vTVs22Ux+39xpuw9FeRWwMY+NrObXwPkxTCPv94fb1cFhC2RTQ5p/NVF23XCu2F06Ow7+1Ssr7mbK3Pn6foeb6NXuh4ynh8lrf7ft+m/x65bV7bU5/Nimo7Z9ffr7937/xVNTx93v5PuVrxqZ+80B2nkO4uJjChx8+Dc+fX4Q//dPPwvX1mT9sAOBkwlgAAADga2+3m8LFxRLqLdN2joPH+ud1bD2/DkPr17tdq61g3TKu9X7aKtQUxKbt/jTFsSq2rXZdplONoeyuCmRHFbLjNWPb6YnL9WHr8DWEvHJ2DodDuKmGDWGaliB2ec2vNYXRGqApiN1W5Xpa6Pf6AeZd3CW03XLOKLA8td9xEFuG4VvvYxwg3036boRQh7b9a6f38Xcpr7ZNgW4eypaVsfH37uXLy7DbTeHP//xzYSwAPELCWAAAAOBr7a23rsLv/d6H4e23r27Xip3ncPvaVpzWFY/ptQ1JT09rR4FvHmaW23XY2K+QLQPSUL1vq2Lr42WeuTaGOmhNz7C3xmsMU/OK2Hmeb8PXi4tdmKb59nUJzGMQuyuC2jg9cRmI9sPlFMCl12X911QRe319fRPKXt9sx8Dr+rZCdrqpjl363IdwMyXxfh9D6BjS5gFamro4n674HGvHbgl816thRxWi/eNbxrE2VfGoj7xa9PhzuZ+QtAxIx2PLr9kf5/ozOv2zTgFrPYV13W98Tb+LcYriw+00xVdXc3j1atf9PQEAHp4wFgAAAPhau7ycw3e+8yw8f36RrZ/YD1h7FbG9fa+vDno3nNEZw9q42kB2+9hG545C4DIMyoPapRo2/uTBbApud8X+WBm7BLLxWApj689veBdTG8bu91OY57ISdpmGOP/Zh8NhCvv9fBNsLVWxvamYUzg3ZderKyrrEK+/dmw7/vNPH3zM8TGcHoqeVpl6t+uMq2Tv/5me2mcvtG7/EOLQbNfnLZW1/Qrb+t+05Q8aJLEA8FgJYwEAAICvtWlaAtmLi+m2UmypiM1/lqCtPq8XYo6Dyn5lar2vH/62gWZbrVrvay/SD0vH46ivM76fPMRupyAug9c0jfASrC5hal4RO89zVhF7cbsdjy8/821/IczZPbefVT+sa8PYGK7O8xLEXl8v68DO85StERurb2Nl7P72uSzTHae+lmcz31wjjSkP2E6vnnz9qs/XCQi/ene/394zO+1eymuPKmVH/fbC1Lpqtn9e/3j/93BL+3L92GW92DRN8VJtvg+n/PEHAPDVEcYCAAAAX0vzvExR/M47T26nJy4DyHEF6Lpx0Ln1/C3nro2tV717X3oBcHytK1/TsxgHs7EiNlXHlj91SBtfU0VtHsDWFbFTZ5whpHA0fy3vbVkXdr6phF0C1f1+DvO8v6lsnW4qD6fiflNFbLz3OIVxHQjGix1bO7Z89uPphMf7HtJd7+2++/8qrY1p67Et99V+H1KVbLnucQqE0x+XpHb5H58AAI+PMBYAAAD4WnryZBd+//e/G9555yo8ebK7rYotw4kylC0rZUPoh353V4YhZchZHi+rUttzm56Lc3sVrv2K2/w65XZ5Th1it9WxcarhfOrhfMrh5f3utgL28vLi5vXypjL24nad2BS8xorYujK2HWtyqN4fqveHEEJc53W6qWydbqplQ9jv07H8OrEq9nA43E5hHNvla8PGtnH62Hj8tKmKj1WLvn717Dk8xuA0Oh5058+0DfC3r2eb+otTCcfwfryO7ug7ELKK1/a70/5u5u2n24rv+G8fAPA4CWMBAACAr6VpmsKTJ/NNELs91Gz7OU/laQp+x22O7V8bW3//thtpq4ZjVWjZV10pm0LZ8n1eBVtXwMbXuC5sCL1q2FFlbP0+ny44r4qtg64YpsYgdQ4h7G8Cq3y92LzK9xBSGJ2HrHnotlw3BWd1JWQbuj3mADOExzO+u4xjdM6WCuTsaDgt+E5Vqf0+Y39t4Fu/3zKO/LuW/qAir5RdvrO73RTefPMyXFxM4ZNPXj2KzxQAWAhjAQAAgK+laQrh4mIOFxfz7TqxMSTM12atf/Lz17ZPGUd9fj+AnTrt29CxrWCtq1tDc2xt3KOK2jimclztVMRlRexchK9xrdjd7iJcXCxrwaY1Yi/DbrdUxi5h5+62/6USdmsYW2vXih3/LMfTsy6nfU2B1RK27vepsnqZwjh/Lvl0xb3xHAvp7u71+3xslbanjadX5Rork7f23XuGxwPVbWNuQ/n0fvmDgLzK+jD8fW3/LUnf2TzYLdeQXX4uL+fw/e8/Dx9//GX4v/6vj1eqdAGAr5owFgAAAPhamaYQfvSjN8K77z4JT5/uisrNqmX3fdvuXkY1eN149gnNt7YdVeO1UzTngWNdCdtWxebrwi7rwe5uX2Ml7G6XpiWeplgRm6YMHoexofNaq0PY2HbfewpZIBZuxh7Xkg2368cuYfH+9jWviM0rZJdndQh5Vey4OvPYVMX35TEFrY9pLA9na3ie2k3N96pu0/tjkRTKTjfThqe1swGAx0MYCwAAAHytzPMUfvrTt8P3vvcsPHt2cRs+rFXBLtrwsXW3FKMXfKYgs66YnYbn5vu2VO2u7+uvHVv3VwaxU/G+DGLnolK2rIzdFZWxeRi7TBm8vKbQdb0yNlaf9kOlfkXs4TCHpYowrRmbB7vLPc1hv09h7OEwh8PhkK0Tuy9C6DimWIUYx1ZXZZYB2n0Fkuv9jEK7r9p9X/eruo9j1zk9UD3aMmyprI1t03csPy+viM2D2BD2+yWM3e0ksQDw2AhjAQAAgK+di4vpdnriuoozhDxoLIPa84jXrMPess2oUrU9vjbQ8bH1/kf7Uvia30e5lmo9PfFUrA+bXpcgNoaxKYDdhTKAXQti22dZytfijK/LzxLCxiB2DqMK2ng/ZQA7Z1Wy9fTEMRyeQpqGOK+SPR6+PlRQyusrK5xPOW9rmNv2n7577dTEZZvyWPz3EAB4XOaHHgAAAADAKaYp3ASCddBaBnm9cPMuU/z21BWJ5fv1k9dC09cb3/GTyymK8/ZlNey4Ojavis2rY5efNE3x7ibIXYLXFGbmgWz906uarYPbrYHu+k8cWx7mx6A2hrFtVe3aMxaAfZ2s/Z6NjrXV5MXRo/322m+5dvtvxNQdR/n7CwA8JipjAQAAgK+dfH3E3jTFp4cjyV0C0e3haB4MHw9k8uCl7a+9x34AXZ43rh7urQ3bC1/zdWHLithlauJdyCtiU4VsP0iN1ahtQBzvoy4vLCteD4d6auI59KYpTq/zbRXhfp+qZOc5hcZ1KJ1Pm1xWwqb3X3X1a32tr2Pl7delYng8zvrz71dJ1+Hp+N+Y+H3rVcrW7Q5FFXf6Y4lNtwQAfIX85xkAAAD42ri8nMPTp7uw29UVYONpgEfVp/dRQNa7Tjl96P1da3S97edN1XZdKVucMaiMjYFsXl1a/uTtj1WxporZtZ/R+Wnc6/2E4n28rxi6lhWz/c+s9/y2VDHyzXa3z7v9Pez11dvu/cFJe2wKFxdCWQB4TFTGAgAAAF8bf/tvvxt+/OM3wnvvPW2qYlO4tlhfwzV3eqJyLPitq1vjvlGAdyyIOXasV2W73ueUjasMXuPUvTFYbatjd2G3u7ipjL0IFxe7m3ViY1XsaI3YtjJ2qZoNt9t5BWraf7ipng2hXCt2WRd26SNu9ypi2/A23feyVux+34bP5flxrdj22Z/P1os9bGnp6zyTx1cVW1a29j7zbd+D9J0J4Xj7us/6Dzp6Fbd1ABtfnz3bhR/96EX45S+/DP/23352bKAAwFfA30gBAAAAXxsvXlyEd955Ei4v56a681gV6ilVbNvClvR6aoXceF3HrQHy3a5TB79l6FlOzVtu11MYt9MYtxWx9/ETOu/Dkbb56+h97ztTP/Bp02fw9aiGfXSp59fc/X3od/lDkbV/4+Z5Ck+f7sLVlf/bFwAeC5WxAAAAwNdGvlZsuV7icjyFiPl2a+344TDaf2iqbcswM38da/seBbrHg9ntU+WWIWY+7nrdyV7QuqwTu6wVm9aMjRWyy1qxaV3YYxWx42mHR9MmL2vCxurAskowBuftuf2K2HxfvTZsHiTHasVUmRjPOXzlFZ3nvN799v31DX3vq/L5tH7K7/La73n6/czbt+tlz3MIFxfL7y8A8DgIYwEAAIBH7+XLy/Dmm5fhxYuLatrfcWB5zorFtRD0lOvefXritYtsregsA8p2OuWyIrZcN3YqfrZUuS5TDafX9crW9p7KILZ8bZ/NVxcKbgneHt90vHz9rAW3S0ibfk/r328A4CEJYwEAAIBH78c/fiP8+//+e+HZs12Y56X6q1xXcRTKpil322OnyavQyv3H2/amwf2qspL+1MSpErVeMzavjO2vGRvXjV1+ljVbx1WxS1VpDGTLqth4bFQRO5YHsfW5h2x7HOLGYLj+jhy98iBYPRzuJ3Q9R3B7X31+FaHyNyG4vluVbfrDglSRnfbn7Zbv6+H2WvmMAHGq4tksxQDwaPjPMgAAAPDozfMULi7S9MT5VLJ5iPbVBJy96XDH1z91THe9h2PnrVcRHwtnU1VsWjc2rhU7Z30c+wnF9vb1ZUMIRVh6PLw9Ho4esp/++WXb/PXcHk8ieZ5w9PHcX3Se+3z9f5DGv9dlBXz6w4/830kA4DEQxgIAAACP3rJWbKzWLKs9e9MDj4OI0wKKtYBmVG26dr12WOvj6VXi9vvp7R+FxXnwWoY4sX2/SjYGsXWV7HqQ2gt120C2fhZTZ7veXz+EtD+t8bpeyVq+P9y+9trV7R+yCvYxVo8+xjF9Ve4j9+z9+7VlOvRj/xYCAA9PGAsAAAA8esvUmzF4GAWy4wRiFE604duhOnZ8XFutBajH+9l6oWPtegFmvl5sGZiWVbEpiM2nMU5TEqc+0/Sq6RptwNob15Sdn/bH8HNb4Heogq3yWP6a+qwrX5eK2TqIba8/rpjtV9bet3QfW6/dC6GbXm933ce4t/Wx9tk+9qD3qxrf2r8T5b+N29aNBgC+GsJYAAAA4FFrp8wN4Xh15LnHs9piUx93Oa/fV/+89XHW1bJ1pezUCXfKYDYFtiHk1a75GrHjauHe8bJtHcjmwewW5f0fPykPXMswshfUbtEGpcdCzt69PcYg8vDggzqc6bl8lfe1/fd92x9slG1NUwwAj8fFQw8AAAAAYOQ733kW/vpffzu8//7T28rYEPLAoQ0pXieDOBx6lZRtcNmzPkVo3sdDhCTlPZTVc/XUv+M1Y/Opi5e1YnvTEodwOKTq1nZd2PHY8jG2DivHloAwD1+Xz7IXrh2yNu1Pqoo9FJWz7VTG9b40ji3GzdbPP9b9epXs/fX7uu3u2v4+zj3XvdyvKYy+C/WMAOW0xtaLBYDHRhgLAAAAPFovXlyEH/7wRbi83BVT6S7KwOGh8odt67c+VinEqStje9MMl9XJIYzC1RjI3tf0ykt/9d52SukYpven3y2no16C1hS6pn319MVtFWa/2nXdXab+Xe+7P0XxV+l1phZ+8OLab4Tp9g8O6j8GmecQdrsp7PfnqiIGALYyTTEAAADwaC1rxU7d9WLrdvnr2DjA6odlKQCu16XtXWv7ONa97jTIeZDar4QdXWvqnDP+qa9V9xuD0fJ598a/Nm1vCpumKa9gjccON4HUodhehpfvD837w2F/09/+JrQqK2TLythDMZ42oE3jSq/je1u71/U2pxivF5sf7/c7+kxGa8z2q5CPr7u7dq3146O2W57TqM3WdYkfs2kK4Y03LsNPfvJGePfdJw89HAD41hPGAgAAAI9avT5pVFZnnp5+vk5V39aq3Puujj1ftW0dMk/Nvl5ou9bP2Ojhrk8pvKV9OS1xb63XtiK2rIwtg9i2MvZuUxI3I3/EWd5dqn5PvMJrtTn31MT30c9X8fn2/xgk7dztpvD8+S5cXn4tSvQB4BvNNMUAAADAoxUrMHvrxdbv23Pbfe2asOttjrV9/QrWrX29fvubs4rXtMZkWTlbV8aWlbanhbDbqpWn7H197qgKNu3rVceWP/tie6mErX9SGFtXybbTGZdVsMdC2WMB59Z92dFum3J7vUL0HIHhevXr6f3c9XjdZlS5u/X5tNXdW8d2/w95rSI/vs9nELB+LAA8PGEsAAAA8OhcXs7hnXeuwltvXTUB4eJ4wJCHqsdC2LXjvSmRTxnHWmD8kE4NVV+v6nWt/ZSdN1XH8vf9StfxVMSH0B/PWtiaV8YeOiHc8Slwy0CybrwWCB46++7Pfff5VVeIPlQ18VrQ/npjut8b6v8b8wj/4QGAbyFhLAAAAPDovPXWVfj93/9uePZsdzNNcTxyrnAhr84c++qmHT5viPI6z3O85mf9DHvhaTOSbP9UtdkSvrYVsIdDXSW7L177VbFp7djjlbEhC2rzADWvnO09r9cL3+p+1qtit1R9rgfDp01XvK0y+Nh6sadXCB9rOzq59z3b3v99fabnUVe6AwAPzZqxAAAAwKMzTSFcXk5ht0vT6ebT6MZ9d3Es1Pq6+OrH3atAHbVb66Pu69i+cGTftn7ysHV53Wfbo8rY+B1JrymATd+fYyHj7Z7ho9kWZp7DuacUXjunH9KOfy/7+14/5N4e4m7u9TXP3+bYlMUAwOOgMhYAAAB4dKYphHmesqrYqTh2un7laz09cdzuTU381do23q9OGS6VAdiomrUXnk7V65ZrjkLWfWf/PkzTPpRrxKb3+/0h7Pf7cH2dfnrrxtaVsfU9t+FhP6Detlbs+JzRMzm1UvV1wsbyHs4fUr5OuNt7f9e1X08cwbDa+KvUrh0rnAWAx0AYCwAAADxKsRK2N93mKQFDDDCPrxt7GKyjuuWaX6/E45R1T/O1U3sVpClYrUPW0eumERbvl8+uVwmbwtbDYd9pU05RvN9fd6YmPv2nrpytn1l7r9uqZO86TW8dDh+fonit7+3r4q7vOy2Y3F41vNbv61fJ3mff5wt/151/WncA4BTCWAAAAODRyYPYtO+rHsNXe72vWi+cTgFrHSqmNVN7FaOpnzyULXoOp4VZeciZws/lOr2K2LxNHdLGY/21YutwNlXFtveaP7v8fW+63eOhZ3XHRwPObVWx9fGtYeDxfkcNTqnsPf26ozb3HXLetb9TP+ft13/9G/ym/xsGAF8XwlgAAADg0XjyZA5/42+8E9555yrsdlMz1eZ9hQsx+FiCu+nBpv+923XLsPN4xe/o+KEIEfMK4hRu5pWxca3VKQsp92Ga5iwEjefGwHTKXvOgdsr2jaYszvuLQWzed1n1ukxPHLf7FbHX1+VrOz1xvo7suHI2PZt6nOl5pvejsPL0cPp1Q8nRVLqjMR27Xi+Ejv3Ux/qv64O/a0B8rO+7VP5mR9dPPsndqm0BgK8XYSwAAADwaFxczOH7338eXr68CvN8LKXckmL2qjQfp4cKhLMRhPis8qAxBrFlOFsGk6katewnbeev4+u25yyBeV6x26uIHVXJxvf9Cth8quJ9d53YYkSH8iddq763+wnY7ha6ba9SHR273+rO4ujdO8762Dru8XYebK9/bmtr5J4S6J5eLXw/VbHWjAWAx0EYCwAAADwq8zyFeZ5upiqemqrY1wkXYgATpz9OFbL99g8VZNxHMJv3kQdRy73nwevhtiK2Xwmapu/d7/dhmkK4vl6qU3e7XZjnEKZpDksV6hzayti4L2T7ppAHv+VavW1A1l8vtlch21bEHg778OrVq6Yy9vp6H66v98U6snkou2yXz6QMe3uVoYcqhDteFfs60xPX7Y6Hr+O+1voeX3utMnWtcnZ41ap9/zndV7i59kzr6/U+61PG01ZSj/vbMs7j152qVwDgoQhjAQAAgEclrRd7+rmjcPXhq05fV1k9Orqfu95nP9Qp14Xt/Szh7HTTdupUyPZe165b79haEbsvtpdxx3B1fxMkH6rq2P40xPU14zTNeQC7JRy7axC7/jz619qyvcX4nPuoau2HzMeuf8p9nGMK37UQeBSu3qUieetxAODrRxgLAAAAPBrTFG6rYuN2CFNVOXncUu1ZrgVbBpUxPFzeP+bqseMB6zL++v6WfYfs2aV9sX0ZDPanIY7hZQjh9nW3uw4hHMI8zyGEJUBf3tbVeL2Bp6rY8efaC3KPBbHL6xLALtWusSI2veZrxi6Vs9fXKbRNFbGHatricPts8srYvBo1D1/HQez6vq1tj4fYvfbHqkNHx0fhcVnFut5P/3mUr6P+emMt942qfutgtBeUHgtP18Zf7hv/0UG9P/9O5f2tfc53CdtP/XcTADgPYSwAAADw6PQrY08NZMPt9LvfvErZ+5CHs9neJoDsBbSH27Vc8+mM45TEKeTOX/chfYYxAO8F4Yfq/SiQ3d/uTyFpXg3b/izVsP11YvMq2BTuHYr3y/PphWhbK1i3hKZl2y2h2ylBXT/gOx42nnL8LtYC5te73paTtz/rU6971/Vf10JpAODrRRgLAAAAPCp1EFuGpvXaolvCvdQ+VcNme7Ng9lwB7bbwtx37+LwYpNbHl3sMod3fey71tLpl8LoP+/3yvPb7ZU3YWBl7fb1Uxi7Vx0snu90u7HapmnmRPpNlXFPneBpHWdlbv45+UmVsWg82/cTK2GXt2GUN2bwydglkU2XsEtaGsN+ngDaGsim47YdsbbVx/RmMw7WtbdvgdT1IHVWNHut3y/FegDqq9qyrYLeEjWtBdpo+uj22LfQePbf1tXfXxniX6tXjbdvv0yn9+qMTAHh4wlgAAADgwU1TCB988DS89dZVuLiY496TgoSt1a55u6+iQva+rnHXfvIK4RDCbdVqDKbz6VVjkBvPS9Wih5uAcn9zbAlqlwrZ/e3asWm64rxCNlnWlM1D83jtPFQP1fs8eA0hVcSm8DTcTE9ch7AxbM2rYuP6sfmase2UxHlVbMj21WNb2x+6+3u2tj1XleQoUNwWwB4b1/0OekuYnVcvl8dPq7a9r/B6HBqflyAWAB4HYSwAAADw4Ha7KfzNv/lOeP/9p+HJk/km2Ath69TE60HlaH3YtSraU23vq1zHNe5bD4hPOd6Oqbz/JRiaboLREMrq1HIa4lgdOk3LFMSpMnafXXN3e1+HwyHsdrswz4cwz/H6aV3ZfL3Y/Nr9scfX9erY/T5Vu8YQtvdaVsSmaYtTZWyqCE5VsP1q2Hp64vWKyPupiO2fu70qdnzdtYuNjvWrR49VxR67zlr7uoq728umZ7y2f7QGcP77sb3/uk3/3DbMX/tsRt+XUf8AwMMTxgIAAACPwjwvVZV1UFk6HnqOgsv6fQjnrhzrT4u8Nq77um6cwjiEcd9toHjIKmXr9WBjYFlOV7zf9ztPAVOslA0hhDmrjA2hrIzt3UP9mq9Ne7idVnipft1n1bB1CJvC1zKITRWxvcrYcRBbvpbjfP0g9ni7uG9LENvf3lpdeqx9eazXqDfG8VTG+XnlsfFz7J17imMhZu85HAuN18Z0LDitQ+D02g9mBbAA8PgJYwEAAIBHYVkrdlQpOdYLNmN4saxp+pin61wLl9tjo3uNIef6veZTE6fpilNwuxxfQtlUHRunJA5hDvMcpydeAtHr63hurK5dQtMltD2EaZqLNYDztoveYOuQsw5kl4rWGLSm4DUGrmUoG9uUoWw+RXF6X/+k5z4KZKuRv2YQuzVYG1WGdloe6XdtfdT23FEouH6NY8rr3KWftRA5D4tPCV7r8R3b9zrj7t379nC9DbwFtADwuAhjAQAAgEchhnbHgtM8bMiDyKxFaEO+PHR8iHB2LXTNWnXC1s1XWGmf33d8ZjEYTRWxcUrjEPb7w03wGsJS0bq8v76ewjxPN+HmHEK4vg1gD4dd2O2W99M0h8PhEOZ5ul1Pdp6XKYrj2rLLePphbAqHD7evZXi6v5kquf9at4mBawpl8ymJ+0HssYrYOgyrw9VtIezo/LX2x9tsDU1PuebplbLr1qpit45n7T5HfS3n1oF/fX/bw+7+c0nflWNhd6+fcqz1GA+bx/d4/wgFAL5dhLEAAADAIzENwrmtxoFnGVRubfcaIzlL6JvG3Q+hy+uXoW7etjwvhWIx8JluwtrpJvDKQ9FlquJ5PtyGrOV9Hm62l9f9fgq7XaxQnm9C2flmTGn92LJytgxCU+VqDFTT9MRpuwxe0zTEZRibT7l8LISN12/DsLsHsZ1PqnN+drQbQraVkKdsD0fyWhWp4wC1fD3l2fTD1vX1YtPzvOtzGYfOa8/90LTZGl6fpg2R19sAAI+BMBYAAAB4FPLgsl8hu15d2gtSl6rPqTm+NXQ9V3XqaLudbni87mx7vfrc8v7jdl1FHKtl81B2vw9hnvdhqZKdiqrlaTqE6+uQPdd9OBzmMM/Lz36/v32dpins9/NNNWwMY1NV7NJFXdWcQtBeaNoLWuNasMt23aYNdJft2H8dONdVjf1AthcsrlUsnlIROw4K14PYY0bhYhscnja2+whee/2MrjsKgbfc33h/73ppauP0s77mbS8IzvfnP/3vUHtsFOyWY+pVzCqNBYDHQBgLAAAAPKiLiyk8ebIL8zwOHY+pg9aoXB+1OCOcGlSs9XNqaLt1LHfvtw1x2+A2hN7Y82mLy6l5DzfTF4fsdR9CmG/6O4QQ9ln7cg3geY7PaqqOlWFsCqLiTxnKjoLWPIzNX8sQ9nB7jdT34Xb8bRA2noZ2FK52P407hIFtH8cb94K9rW03juJO54/Cy9Hau722efteu63jqD+L/DM/zZ0eYOg9w/a7dvw+x4H9Xe8HADgXYSwAAADwoH7zN98KH330LLx8eRlCGK0jOlYHsbGitLdubAoCTxvjKBTt7X+9YHZ8/RDiWq9TFaquh8GxGjZNVZyewyhci5WxS1Xr0m6eY9Aapy+ebkPQWBU7z8s0xEtFbLlW7LI/TU8cx5s+7zKMyoPSPEjNw9dlrG31a7kmbAyN8z5Sv+V18oDwMBxX/xn39jd7BvtH7cf9r4dz/YrL49c5rf1obP0gcP0Z9StFj4e1+bm9/VuDyTyovUtI3lsftgx/84PbP8/e53B8fGks1o0FgIcnjAUAAAAe1LNnu/DGGxdhtzteGdtWcW6fbjiE3rm90Pb0vqoWWZ95WDq+Vj9QPj6OuvK3PLe93trYU2g13fYbbqYx7gWVSxa6VMimcLStel3C4yW4XaYmTtMT9+9zrTo2vrZhbBnClgFuOQVxHryWQWwKyfohdT8w21qtuR7EjhwLIl/XXSpcX7efpX19wtbn2D++NTyOx7eM95RK1ezIzfEyzL/7c94eJN/n9wIAuD/CWAAAAOBBpWrKELasCZsCtN40vGXlaAoU4/qm5Rqqdd+n7KvD0FGI2guQyzH1141dewbjY2X1b2ofn0sMh9dSmyXEXKpa55tAs51OOAash8OUrRM7hXneZ2vEhjDP8+358SeOL4W38R7KACsFsik0zStkQ8jD2PWfdG4o9pUVmfk6nXkgWz6v+w5hR8e2Tstbh3ejKsu7OLVCsz7WPtet16yfeXqO7fH6GfeqrJurdMPWeurkPOisQ8/Rs1n7PNu+yu9c2Vd9z/W41qtxVcUCwOMgjAUAAAAe3LEQcxSihtvK0xgOphBy1G/edy9c3WYcmI7u4VTHg9f1sZxyb/k6sXkYHEPveureJayNUxnH0HafTUE83UxJnK5dV8WGcGxq5RTApuCpDlbrKYfXw9i8r/hc6n359ev3+Tm95925k5Vja+dtCV37+46Fnb1QdeuYjvV57HXr+cf23XWsx6cSPtrDSt+n7T92jfb+R4F2/l099VoAwFdBGAsAAAA8qBTQbVWGj+OgsQ0V1ypYizNfI0S96/mj8LQNovP76l0nfz55pXCsmo39ldtLX701UQ83a8eWB/LgNZ+CeOlzulljdrpda/ZYVWy8n7ZKsV0/Nt/uh7Gh2Zf6C1WbdM1TQ9hx+HX3ELZ37VH7LeHbuM3y2Z/S17Z2xwPLuvq0PNY7r95ZV5D2246C1tG+OqQf6z+3UTC+Xol+vJ/e70T6vai/z6dPiwwAnJcwFgAAAHhweVBXWwtb6ymJY+iYh5VL/3cf2/ZgdRSCpn7ysZRTLZcBc9weBbSjMW5pU147Xacef5JCu1gRm4LQ+bYCdvk5hBDmrK/p9jnk68jmz6F3zV5AOgpj+9t56FoGVW2fodrXG0v9TEZh1+uGsKef0ypD1rv1E8P6cYvXDfvK8LUNW0fXGIew5fvyOzQK19trt332g9+6/7KPeg3iNKb6uuPvW3mttXa9+wAAHg9hLAAAAPCg1oPOMqTMQ8d0bloPNa6Xmvrun3/sOlvH0xvXeqXqhit0+sirY5fjKeytt9vxtNWx5Zq6IbRryY4CvaXaNU5JPE3xNU1BnKaMXi4yz3UIm24uVcj2ArF+cFoHralCsP++H8KG4nj+LEbb5dhqx0PYY8ePV2Me7yvtGwey960MJ3vHD1W7fvDa63O0rzy+FlCO1GNqj9fHyrVd6/PaNYdXr34bzI7GcWiOrY03D3p7wTMA8LCEsQAAAMCD2xLIrgee62JQ0qu+rfvLt0fvR+ce39cGs2WwfPw+tk2x3Fbm5ueXz6M3fXEIvWls5zl/PYR5jpWv+TqxaXs5L4WweTXs+D7ya7bVg6P1ZNtA7Nhr+z5dqzeW8VhfP4Qtr3XsvG1ZWx6uj9ss/W0NNI+HvMfC2ePnjMPbNrCup+RtP9s6zOwFmb33sY/OaIvXtfPzgHS9z/XPYBTI9u4HAHh8hLEAAADAo5UHD2th5qga9ZSgc9zXEpTEdVWj1C4Pi4+Pbzym0bTLW8dZX7scV14d266lG9um7TqQjWvD5vew35fPIU1JnILvVDG7NYyNfeX3UYewx1974Wt+LL+P8nr1dbeMcdBi8/HtQexp13qdCtl07l3HsvV1tK8+Vofjdwsi85A2768NQ7eG6G1I3AtU2+903a4fHq+FvofiPMEsADxOwlgAAADg0amDh970u73t2tp6rIMrh7Re5tQNV+txvX5o2rbNg91xEF1PVXxsTKNwNg+mpuo1H9/hNljNQ9i0P1XXjteIrdeLPeZ4KNvf169+bdcNrb84xwLW7dPQHj8+brQtBNyq/u53WkzTYEzbwtwt4WV9/W2BZz+k7ge25TTC5b8h/c9tbV/ZVwpw8777YXI9/XV+kV4/o3C43C7blWPLr7X8PgpmAeAxEcYCAAAAD2Kep7DbTbdriiZ1CFkdXQk/twSdvX3rfZ52rdhfCOsVsTFs6U2dvDbGU8Y6CmnLEDtV/eYVsuUUxmX4uvW65RqyZcBcP5+qh9t+2u1xGDt+X4Zx/ZBv/b623ve2Nl9VEFuLD3zUUV4VPb7+XcLXuo/+8fpzP36t/v5D930vyE2B5vHP5tj1e32frr82bfm7cAh1Ja91YgHg8RLGAgAAAA/ie997Fr73vefhnXeuiv29StAYNqTgMoWGKUCaqoqwdH48XoaL42mB14PaNoStpzFuK1nHwW19z+V0vmUwnT+DGG6W1bHbt1PoNFXjj2PNX8fPpR7XqCI2f4Z5lWznaXT31SHeWlC7bJf9nBrAbgnmtuZf99nXKeoKybS9Vi1bB7ZpO043XZ6zXj27FqaWx8rKz95asXWgPm6b3vfC31542atSbcfe29e/Zm+s459DcW7v2dT99Nr3wmYA4OEJYwEAAIAH8eTJLrx8eRkuLubVaX47e0NdOZvCyv66rfehDIPL666ft7yWFbHtNL+9806b/vj2aFiflrkOeNtANQ+7U4VsGe6VQV+aovh+pkndFsiGUIdV7fm9IHQtHO31MW6z7v4D3ZHel6F/QvsZxs9ufI322PHpi4+Hr8fbj/vtBeu37zph6OnX7IWidfg7CmSzvSvX6H0n8/f9gHncHgB4rISxAAAAwIOI0xSPpiLO38eKzjwY6U2p26tYPbZOaTslb7ndr7oNN32267bW91FXuY6kgGxcsds7pzeGUdDa387Pz8cY38eb7lfJln0s58TK2zrsS89ty1qko7C03t8GY+MgaxyAHQ9nj9sSvt6t3+N/XFCH/fX3vr3e4fZ42X/63NJ3IvV3LLjNK2fXw9fe51a2r6tUt1al5tfoh7SHbpt+EH/o7Cv7TPd7KPb3qlbLKtm2Erd+X1fNtn0dsn62rWkMAHy1hLEAAADAg5mm8dTCayHkbetDaELLPIw4fR3Yft8nnhnyqZDz+0ljatdiHY8hhs69CtZ2vMcC2tGY0zjboDZvMw558/A6FNMet9PjnpIYbQ1lU9tR8HjsWNn/psFlbe8/hL1LhfcoKK0rYvO1e9tgNu9gFJyPg/o1W6pRU8i5qceTA8itlaZbQ+S1QHgwgk1jOu3YofkeCmYB4HEQxgIAAAAPIq43Gi1VXe30wzG4rMO/2EdsVwaOeYCZB4q3Vwt19Wmvv/p9fn4vKDstwG0D0NRH2X9d2Vo+k9HUzPW+9EzWjqfnHDrh6jjdaUPgGPDln1ceZK2F0esp0vHw81hA93qB1dpz6Lc/9QqnhrBl+/L3qt3fhrDlZ9Svij32vnfNtkq21259/dd2rdh4rNd2dKy+57rqtB5nW6HaP5bOq8PQslp1uVbvWR2K973K2aWv3k97fwJYAHh8hLEAAADAI3a8wrPczgPOfiiVB6+nhaed0RVjKgPgfkXs9uB3bWz9Z9EPiMv2dVVtG8jWIW8vCO8FtPWxPNiN95uHsOHIdLeduxgfORrO5ve39Xr1NU4/8W7Xaj/Ddvrr7V/a3jTF6fPN/xgg/3xS2Jp/nush7N2qY7c+o15guqWvNqQ9Ppa2n3Hg3OtzbSzjcHesvsYpIaxwFgAenjAWAAAAeDBxutRarJItg8k8OIxVtIcsEKyrA/NwdDSC8rxRUFpW1aX2x6YB7o17HJjW1bGheT+8i0N/LL0+R23SvfcD2TT+UN1P3M5TnzaYS+Hfodq3fm+du10/WnxWp3ud818v+NryINaqicud9Vh6fwwQg8ot6wnH37n8ufSnNS4dD04Pzb4UMtZVsWvvD6vv6zGV4XpbmdsLTss1XrcFpOM/FCjH2F6nvFav75BVzPafoyQWAB4DYSwAAADwgGKQUwai9bqq+bG4vw0rD01/ZSVoPwhtA9O632PasLUOddPYxxWyvX7zUHQ9NNsWyPb63damDGHzaYyXe8irJ+NYQ7GvnPY2ZPtG93+q7R31r/lVh6+5Y1+03vHROePAtv1Ol3+wcHy94Ng+/12rr90ea59TG9SP245D3P77MrQst9vrlm3LgHasf2/teNaC3Tr4HT2vQzPOOqhu76PdBgAejjAWAAAAeBC9KtIUYNThbBnA3hwJcU3TPADtX6MfxObXyNuPQqtjtgW4/fD2btMT1+NaC5zXzmvD3TYMTufVIW1ZPRnbrk9HnFfjjtx9PdktTkuq7ivYavvZXu062jcNvjBlQJ5/xw63560FsW1F7LHX40bBaxuW9teKbd+XfaytFdsPKA9N295asvX1yrA1thldo73e8GgRsJaBc7pWu5Zt777K8wGAhyKMBQAAAB5cHRjEcKE3XXAe7MX3axV/ZXgSw4x+9WodMMW29TTEdWDVb1dWFubTBefXLfsdTxlcjm/8XNpphtNY1qcnHl0/hHKq47oKNo4r7VtfK7ZfJZsfy+/v9Wzv4D5Dq+19rVe31tvp2fSP1/vGfxiQ/zFAvq8fPLbV3OXvx6lB7XogWwejdThat2kDx/IPO9qwsjcFcG8MZeh5qK53qMbWr85N12r76r3P72e9jziGQ/N8Qwjh449fhX/9r38VPvvsur1JAOArJYwFAAAAHpU69OwHjv225dSqW6pU22v3zitD4P70wKdqg63xdesxjELVtemK8yC3Pre85vq+XsCb9h+y93X4fMj6C7dj6d3zMadWzN5n0Hr+/k8JYuvAdT3crT+L/I8J4rTT+e9QGbrm38UyaN0y3XQvMK32DPYfOy/tr9eB7bcr26yFulvH0Qt6+9c/foEyDN7Wpr33EL74Yh9+/vMvjl4PADg/YSwAAADwIMaBQqpo7U2DW1aspu1yit3Utrpqtq/fV75/CRTL/eP+2kA4X4s2r57tnxdu2x1bv7WdsrkWg5nt4WseRI/2Le/bytl6fzmGqH12W0K80dqnX7X7v2a/mjV/3wtgy8+v32YUypZhatT+ToyDzNRP+v2Mn3t77vEANu2vA820/1C1adeKHQejIRtb/W9NMYLsWG8K4Hbf+Frl9uj8tjp2tN17rdv3nysA8DjMDz0AAAAA4NtsVMnWq5LrTy3ahrr94KS58pEQp/e+18ex9qOQ5nWu216/DZtGffTH3DYcr8u5tn8tiDs019kSJrUh1nnDp6/memVYuoSkozC1F8ROYZqm7Lwp27fsT+/Tz5Z2bd8h25/vWxvzNsd+Pzt7h+feZa3Ytd+Twai64xz9mzQKtUf99caYXyOfCrneBgAeL5WxAAAAwIMpA9R6jde8OjOvBo2Vp3Ff/Vpc4aavqQpeetPupmv3pgauRh5iOJXa5H0ux5e+lvdt36mP8v7qey+rYZfrra8BO2rX72t0zRRw1VXH/SrZ9GzTdjtldB3I1s/+Zm+3TfEJ3FsIdX9pVllxOppOuT8Ncb49Cj3LMLa3v39e/d2L+9KawPn3Mf1+hazaNfaXT0Wdjsf3obNdqoPS9lgb6Jf/LpTfs/yc/rG8/3ElbR5ulu/XqlpjR2XbPADu99c7Hs/bVpmbH8vupvvMAYCHJYwFAAAAHtQo6OwdG4dKdXDaTsc7uHpow7Cy363tx+ccPz8GYzG0XVuzdn0a5q3jKp/xeIrksn0vUI4hXX2N9DmE7rllu9742ja98x/COAguP5Mt467XfD0lfM0/q7xt3l/v2r3PqvwDhDJk7a0tG3/HeuH6sis/v73vOhDtP9ND0+64UQjcC35H7df2jb+b/X3rgx5VmueBbr//Q+ec8ZgAgIcjjAUAAAAeXF31Vld8prCvDAV7bfOK1/x1FJSVVYFlv+l9f53W/NjSV9uuF2COK2LbQDmNpQ1QR/e/1k/9XLdVyMZ+QvZZ5PeXh3T1/ebn5p/H1GnXGoW3j9da4L5WFbs+NXD7eqxKdv2PBpbXQ/H5pgr1Q3Yf8X1+H221dOp3+wc0CkTrELIOH8tpuQ/NOWWl6fr16urT+vmUbXrt2zVd47l1296+1LZ/rOy/Xi+2PK9+hgDA42DNWAAAAOBBjYKV/FhqW7crA5GyTXFmE6aMxlL3X46nDGPyY53eqiC4vtY4cMrHUx8vA6P1ACkfS33ten85ltH+dE4//DmsHm/HdAhl8NQ69pl9fbUVrG3l67KvDGDrtV23/sw3P1OY5+nmde606Z+fXy+Otc6WUyjcD52PfYbl59wGkvdzThl69vprX8cD7/0er/3ej8bS63M07fHadXqBMwDwsFTGAgAAAA+iDhZCNhVub63TVPWaV6Km8/O1Y5d+1qfczceR95Xv708tW56f74vn9PocX2c5t1eVW4+lfj1lPHXVbjqWrp/3W1cZt9PYhu7nsGzngey4Aja/hzqQ6n12x8K8c05hvCUMXr9+Xklahq3L+7YStgxfy+26QradunhcIZtXxMZ1Y/vVsOmm6+9H/X3Meq+uFX9n43b7ZNo/suiHjm2b8R8N1H9cMAr189Bz/AcO6d7a9r1rrq/x2htPfSzv81gfnSfa2wkAPBBhLAAAAPCgUgBSTj+c74tBYhloxlC2nFb3pofbfXUoE0OxFELW4W0Kdeu1MeuAsD+lb96unMI4TbHcD4rze+33t5x7fLri8nrHAtlen+n+QtFP+nzqe+odj323wW3brn4WZaCUB5cjWwLTc+oHzbVREJtXwubVp2XgOg5kR9MVp31pnHWgmb7jMeTb7+N5+5u+98U9tt+R+vnHULc1DhG7rYv34xC0DFTL+yv7aveX/S9tDtVrb8yHqq9yfHVAW4+5HXe+r+wznb++luwXX+zDn/zJp+GTT1517g8AeAjCWAAAAODB9KbUrdc9jcfXQrz6eHWV0IaSZV/9ELFtUwcgvf7q+xn1vbZ2bDvm1LYOVfvb43GdXiG7HrrGKtg6sG3v+XgoWz+z9pkeVtscC2u/SuWz6I2rV9EaK2JDyIPY1GZuKmZ7VbN5v/n18j8EKP/IIQ/8lp95XgLB/X6+PWfpN95f+tw7d58dG6eudSjaD1PrY+n6edDZD2j72/X1yyC0/+9SGtOhOq/cn2/nY6uv1T6DXkVsux5tfqz3bL/44jr863/9q3B9vTntBgDOTBgLAAAAPJgUKkxZyFCGIktoNKpmve0ppOAnVXjm29WVm0AyhH5w2g96y37zYGs5Lz+eX6s/xXJ+rbXr1kFr2aa897yqtheujipk6+mh0/vxtMXpntfD1jKUDbfjXQuze8d7bdIzWD/vVOV1yg6P9R+/u6l9qnhNr+2UwyGk0DWvhp3nMoStK2XTNcoq2f495QFf/f4Q9vsl7IsVsanffLv8fuT32a887YezW0LZMqAt2477aQPbtVC0fR2vNT0Oidtj9bTE+bOux95v2543CpgBgMdHGAsAAAA8iF6AkYLZXljYCwrritIQekFp7zUPK+tx9Spu28B13La+9pZQcH2N1u4Zt/dZh6+9fb0++9cZBbWxrxBGVbApbG2raPvPowxmR88p/66cErDef1gVO8wD/+S08De/3/Q+Tj/cBq1TmKb5ps2ctZuz88opjuP+29F3/nhhmqaw3x/CNB1upiYu76UMefPfrfTHBst3qZzyu6f8nd8WypbHur1W97UW7qY2cQx58Fv/G5Ffo9fX2vjqgLrs61Cc0+8ztkmvo2AYAHjchLEAAADAAykr8vIQL4aI9ZqxZbjYru1ahrDl2q2LNi2LoUm5LmzqO4ZLbaDZriVbriG7fe3YfhBaVgXn95CC1N70xOtr29bBaj9cTtdOz6itkq3D1TxISvvGlbLl/rpasR/OroVQ91EJu03/+zQOzvO2/amI0/sYtuaha/ypt/sVsnlVbLuechvwLd+j5Xdw2be/fR9CuAlrl/ue533Y78uQNn338srX9DsZf1fjZ5wHj+NgcxSsluu61n31A9Je+Jk/lP5asXX42VvHNe6vt3vXq/tM16mvFdftHU9T3COkBYDHSRgLAAAAPAplhVoZAqawp9yuj/UqU9twIg9D16pg2/f5+eW1+1Wt+T3UgWUd6h6/btnneiDbeyZl2D26xvYxlOPO76k8t/+8R88nO5J9duOq2V5fta8upA2hrNQeXXhqjsWANg9T14LX+DPPebVsDEnzCtm8OrYX8B+KMSzmEKcjTuFxGSaX91I/+N6+sbVw8VibdOxQbd9uDUPKXjCan58HrPU5vTH1A9n2wmWAW46zvkY9nvSHK6P7BQAeG2EsAAAA8CDKKrCpeB+PL0FjCG0laRnw5VWqMQiqpy+ug9666jUd21Z5G/uog9D8GrGvOMYUZPXXjo3Bbl79ml+3rAzuB6/5c6vbpXs7XiGb7i9U4ynPTdunVcrm7cq27bE6ECvbHU9azx1WrQXaNy2KgDXft1b5ugSty+uy3WvXVssuY0rXqceWB4bl+rCHsN/vbz/X/f4Q5jm1y/tdpkfeh2nKq6TT9/6YXqBZh6PLvrYStg1Jy/Vg+321a8bmIW4/gO1d+9Bst33WY+lfu18RW/YXq2OX12PB8vj3BAB4OPNDDwAAAAD49ioDkzwUKQOV5X0ZmuT76v1l/710Ylzt1l6jO/LmOr1+1vtYG2P/hPpZbG/ftm3vcS3FaZ9Xe53jn0W6fvv59tpvCZZSgHUYfNbn1D7Lsanzfqp+bvY20w7nwetcvI8BbQxs0/t4PP2k9mWYm0LeUbhbv6YAflz9W99zG5DWQWn6DrWh4t2+J6d8z9o1Y/sh8Pq/D6NAuPdv3bjvFOLmr73rje4ZAHg8VMYCAAAAD6KtKpuyUKSsfq3XYu1VsOb703S8eR/puiG0Vaj1sbidV4TGvmvtVMV5u3LsZfVgfu20XQcuo7Vhy7G0YzutQjZdJ91T/jyOVcnGfpc2+T20lbLxs4n72yl707HQtTZ98v0Yh4y9a+fPsjy/DmF7FbF1MBorYddf8+A0VtHW0xzXY1xe4+/dPixVl/H3bX9THRvHuhyf5304HOabz+xwUxWbPuvlWvH7F5//FLZWao7+mKFXCVuHnWXgOq6irduUfwTRWzO2HFvebq1KNr+P9t+4tQrZtsI2/9nvD0V17Hhsx583APDVUhkLAAAAPJBxgBG3i9bdCrTy2LjNIdQVY73QZ9z3WsJRVrndRX3ttt9l+3j3/Qaj8KZ/7dH+/jX6z3y9TWqX95k+oy2hUh1W3b/2O1Nfu91/fI3YFGCWx8bVsKPq2BTM5lWv6f3ctM/3lRWyZZ9L2Nqrjk2Bczs9dF3t23te4+90HYyufefy/b3Pog11e59XPzitx1aHo00vTeCb7mNt/P3fk15Y2/+9r3+nr68P4S//8ovwy19+eed/hwCA81AZCwAAADyIOuhIYUZeFRuPx8rXcLu/XiM2Xys2tk2Vqnl4sbQdr4GaqvzKitzbkVfXTPvyytU0luX92tqxvYrVZVz12rdxXGvrx+bXbfup76VXIRvC2jqyKUTKn2+6Vv4s2/Vk8zb9c+vAblw1W9zJmfKnVMHbXrj+/mRnFd+/1NeWsDWvks0rYcvttSrZ/Fq9McdKzOV7cQjTtL9ZN3apkp2mfZjnEPb7eP9z8TpN+9v7jNesp9JdCy7rP3TIt/OAvte+/uON1Pe4ErbXJm/b/rFC7w9FRmvE9kLd9t+3ct+oKnZ8rbh2bOephsMhhFevDuH//r8/Dp99dt1rBAA8IGEsAAAA8GDygDRNU5yHKCnsywPLOiRMIV4bLObbeYCaphUupz3ubbfha9tfO846dMxD2LxNGbjW42+D0Pq1H8j2xloGu2lcZQCcX7v/THtTK9fPIN+O4W7+edbPJ99fHjtUbbaFs/clD+jK6ta0f/T53Yxw0HM5ZXEZ0rbruKaq16m7/mu+ZmxviuJ8bDEAXALY5bOZ5yXUW6YkLoPhGCynYDMGvXdJwOvPs3yN78vn3oaso8A1D3HL/tpze9cfBcbNXTQhcRvmlsfK0LXuJ1XAjkLf8rxqNCvHAICHJowFAAAAHkQZQkzFdln9GUIvSIzn1ZWgZYAYA5gy6I1Baz847K9xWo2+UzFbhqNxjHWAl18/btcBaB42lwFyW+V6rEK2DHN7++oxbwtk8/Et2+O1d8v7DsX95M+ueMKdZ3tzpNNuPZG9v8A2PYu63zaELUZQhaTHKmLzKYlHa8bONxWyZVXs8n655jLG/Htarl+6bC8H9/tDmOdD2O/nMM/pOxHXoC3HmI7l99h9Yp0gsQ4l02sKJPNz877K+9h2nfL8/rqt6XvcVsq27WOno+36XsrwtxfO9gPYeqri/j9Ia8cAgIcnjAUAAAAeTBk25FMSl+FEDJT6QW0bQObH8wCnrmKNIW/UCxPLkK0ORou7yYLOpV1dsVuGsGWbMmAtQ7/Yxyho3RLIpjG3UzinMYdQh9R1qD16TqeGsmn/8WC2Pt62Ww+i0rl3T2W3fQeKMzrnLvvrEDNft7VX7Vqv9TrPu9t2vWmK26C0DjzjZ7W/fb9UxM5hmvZVsBunJU5Bcvkc8yrZslq2/MzqAPWQtRmt2ZyHsr3phNtANW/fa1NfPx9bGcQeitfU92ia4naN1/G0yMemKW4D2vK+8/vsPWsA4DGZH3oAAAAAwLdTb8rOZX8IZVBTBjZ1pVgZiKT2o6lLe9ccBUh5+NHbX4+rbpfGsmW7f426r/q6a9baja/VD3rS59K/x/z8Q9Wg/8zzzyn+lO233N/659qcceRn27Xq/WN5cFlWdpehaf99v2I2D2bzCtnyJ99ft62D27a6NlXElmOvk+f+VMjHnkv9LHu/y3m7OjhNx/q/4+X5+b8N5Xe7P47ynNH3q/c7PPo3Iv83q/2dyq+ZB7Plv2n97+eyb7+Pa8pKZAHgMVIZCwAAADyIwyEPEaaw3y/7l7Ur4/qUqaI0VWWmNV3LaYpT+7yytB9g1v3HNrG6dDnWq7QNIbVJVaT5tfPgp6x8jePvV8rWUxSnsd6M7qbvtkI2u4Ob11TB2m+3ViGbrjWenvh4FWy+vmjdJm9X7j9Ux9qpoHvn514vj6pPbtfYza/Rm1q5t1ZrGciu/+TVsOn9Lnudw25Xh61L+xCWYLWtvq3Dy32oKzqnaR/meb79vsTpjpdK2UMoq1/zatgtQeQozD6ENnQsw8uynzYkze8h9dkLhcfTE8c+6nsZVb22oeraOq+Ho/v3+/oZxf3x38f62aTr7/eH8Ed/9En4q7/6Mnz55T4AAI+PylgAAADgwZShR/wZBSLlOW271L4MfdbTuWPrP9ZhTTvu/jh6Y+xvj6Zf7Y21d+x4+pjfR+8extdfD9vq4KvXNg/c6uuPgtMyKOs3Ggd896k3vW1/LMf1qkrbSth6OuB+FWt6PV7hOt8eK8/pTWucX7seZ+8e7vIcUtv2d7f3+3N8CuNen/HcUb83LZrv0dq/N2m7DGn74x1VpB+qcaV/+3rjOOZwCOGv/urL8Mtffnn7By0AwOOiMhYAAAB4EIfD4bbya79fKmPztUtTKDFV4UaqXA231bBttWqqMCvXJE19LefH9iGM14w9HOo1ass1W8t9eRXq2hqxodqeivtP44r3mq8NG0JbEXu8QrbtK3SuH5pz8irZ8tjx9WTzcWd7uu3y9uWxQ/d4fo/3HciO1ohdjsXvXWqbnmW4eZZ5gJlP89sPYetAtT/F8C5bMzbf34ar7Wc/DgJTJeyhE87m424D/TZ4z3/38p92X3qm7R8+1IHkuPq1vGZ+b73gdlSlGq8xbtdOjd7bXrtGrHItn8WyvVTHls+rroqttc8fAHiMVMYCAAAAD6IMNPJ97XYvuOmvLVkGeGWY108t2rCmPhaqAKV3rTbsTfvHU6iOx96GXO01mjvp3t+WvtJzPXTPqe+p3/ch9O6vP95DqD/7uv2WECoPFMuf15dfoxdCrp2X1BWm8bVcjzXf3wtpt0xvPKp4rSttU1+hE7qGaiz1PYyfQfmZ9R5Q+92qf2fq3+vUb/93ffT7VF+3Dm7X7qf8vHtjGf0b1R9nb23s8ne+Nx3zsXsq7w8AeNxUxgIAAAAPYr8/hOvrvDo2VXwu76eb9WOXfTEk2e+nMN/8eXkKTtIasil8KSs9yzAmXxc2tUlrxKY+87Vjy3VO6/VlQ3ZeyM7Nr99fM7Z9318PNq1RW18rtelVyOb9pr5CcU91v6Mq2Zs9tz3l7dL9hbBWKZv21+FY/YxDV7UcajeAO11/fdjUf/t59ip0V69wG4LG69UB6pz9xArZtH5sXinbe7/0PWfVrPk9pN+LZTrbQ8jXDU7VvGWAvB4EHgsn+z+pXVkd2gsp6/b9646qY0dB6bEq1vbcOjBuX9P7ssq1d716rdhDtj7s4Xa7/8cR7TUBgMdNZSwAAADwID7/fB9++csvw5df7geBS10xlqcOZeByu7cKJsogpa9d4/Fwu78cxzi0aUPF/PprIVL9vg5fyk7r9Sz7oWO7b7Rmax5i1f2uPdf8Gv1nHtv0K2XLdmW/vUrC0bn3G0SNg8H62ql9ve94INtOW5z2x6A2D2vTa9pXV8umc/uVtOX0yKG55vaxj4LVPBjMv6P17+nxz7Zsn85pj/X/Dei1Kfe3wW1+vP19738f6vst++iNqf5+9X8/tnz3t7YFAB4HlbEAAADAg/jzP/88/MVffB4uL+fw9OmuqIxtA8JDKNeOLdevXPZPRbsUlJRrtrZ9lFWgsb9eZWi9jmi+Rm1/XdbymvUasbFNXkXaVqbm/YXimvlYSuW+3nnpnmKo1asSDt3nk67RVt32K4Hzz6isKO2dWwZ6ubZ69T7CqLLP2GG7Fu3aWrH5vnrM/TB0bfrhufM+rg1brim7VMX2pideu9/8M8vXgx1PSdz/nWzXoG2DxkPWtjwvDybz1/T5b18btm4/Wj+2/L2p+2iD2Pw+en2VP22bVPGat2m38xkC2qrX/u9D7F8gCwCPm8pYAAAA4MH0g5M6HDkWqoz7GJ3XhkvH167N27XBVLrezVa3n3JfHrDUYUsdsJTHt1bIro0xP1a3642lfyyFbe399bbzkG78bHrH0ljaMO91/f/s/VuvLEmanom9Zh6x1tqH3FmZWZV16Kru6iO7Z9jNbpEQSxRm2EOAnBmAwAwkgD9AN/oRutPf0G+QhLkTJEDAXOhuNIMRhuwhOV3sYnWxTpmVp73X3mtFuJkuzD+37/vMzCPWzp25T+9TiHZ3c3Nzcw9fsTvjiddsJNb8fb37+Xpy04vZ/rqdy3U8/2tP6o7PU5aja/DPRJWt9r77Z0vfq9Hfmm63nsuK1GWt8wxYOWrPW4/vP0/jv8vTnw0+/Zpdm618hRO6bX9s37Y+l/R70mvns89u8atfPcPhkNpKhBBCCHllYDKWEEIIIYQQQgghLxWZY7EmY+u8rT7lKglSm3LVbdUkpuzvSU2dBCzH1L5gmSNWkq+j1GCdR1aWUPWlju03lnlfpa9SR66vl1y158Ta33MSsrZvOnlb05H62iWZ7M/v07Oj/ku7fr/e1u1Jm/Z+Qe1DQy/Fui1IWyF6IjjaPb9O7bb31F5nnYN10KMgS33tVqyOU7P9l5evei5i+wzbpRX7rRwdyUIvZq1c9NK8P89pe07bvi6r976VlaP5YH2dXj/bfmwlX3sSt5eYHZWV7Tqn7PhebVPq/OpXz/CrXz07VZkQQgghLxkmYwkhhBBCCCGEEPJSaaXFqe2RxOgfhxNpOGyk3cpxtVwvR0Kpd009YSVtWxGV3bodqtSe08uk3nZ7XK+ur9+/HmnrVIq4ppl7+3WZPl9PgvUYybExuXn10rXjNtv7VO9VuxyV9VOqWsKiM19s77i+4T1HMPeeef+s9eXp1qt3jL0HvWd7S17ae967/+PnsH0W+8+9Pg4dEdu7R23fev3qXVNfysqryNlRH/XftG2HEEIIIa8HlLGEEEIIIYQQQgh5qfTFT7u91HbHjqXZtvzxwqQVK32h1B7f7rciRcsd33ddpy3v9eXLC9le3a1z9fuW0ZNarQjLzX7vkGx5Nq++8Oozfo9OHZ/dq9dme59Gfejj54q1ZSJgx/PK+vr9oYr1cf2+1Pta+3sXgdiT2L0fGtR6+p6299H+MEH66P9m/Hr7nPXqjNrZ/tu3/crNOfp/660k7d8b/0OM/j3VffX91mWn/iYIIYQQ8mrAYYoJIYQQQgghhBDyUsm5DFGcUh3ytSc9Sr2AGEVo+KFW67Cs5ZjQkRtiL4I5vx+WuJTVY+tQtNmdV9fJ0MMR17Sj7hvUfpg6dXhgfX2lrh2+uNQdDVlct2Hq6XPaIZDz0m5viOStoYu1jOoPX1y264WLVPQSqTeUcb12tWXuS8updOh58qq9pnJsRnvv9Lnre27P2Q5X/GUlmrzPtkw/A14+tgI1pbwuU0rLKyPnpPYls13rt+v6VZOe47Rt/Tu1gtYL0ZHgrNc1lspLrYEY7fVxJGdPXc+p9nv3Hsvw7D25u/XeZ/z612V44sePD8/x9BBCCCHk64bJWEIIIYQQQgghhLxUrLDYGv5U1x9JnOzaLtKnPZ+XOj511ibj7LFWdMm+8bnWks654M7Vu0e5qav73vajXarWNs/hjxn1rb2u/rCx9hzb70mP3j20r37dkVQ7n/a9knP489by/vnG5y9zy+p5c6Xc/mCgTdP22u49z6cTrs+TjN3+m5X3xra5JTrt35u9Bv3s9/ZDncvXyd1zqbvnlrUt33/9WdB7b9tr7X9m2Gu2bfr+u3d6Xbu+PuLjj29wc5MGdQkhhBDyKsFkLCGEEEIIIYQQQl4qKWXMs8icgLT4hZKADY3o0MlKKY+xpFV9fZ+WtdvBlOuy0Xa7hCvT6ERr3W+Tsjb1atvR7Z+qOyrzbWDtQ71uKasiaDslW4/zSdl6P0udXoq0TeqOE7N133I1XU9lC7fSsbpv59Cmmtv3oE0k2+S0Tsz2xa7d9tJQP6/yN6IFqFy/TpbbNnViNaFNw9rXPKflb9Ju21c/LasTs2PxO/qxRSsmvWT18rZXp97Hto6+531JPOrLOctWPksC1tfR5XW9fbjt89AKXUIIIYS8HjAZSwghhBBCCCGEkJdKmworr74kGQuc2p4XN9vyQu/zdcfHbtkQK438Me05bN97676uloHn9c0e1zv+3D6J3Brvq233r6Ff1kvM6nbPlVCtYOu1kd1rq63clMn19a7n3O2teqOUsy+rolH/HeS1rCdAzxOld5GqVgyP5GF7vdvysbfdv1f9bf0++b/H8WfL+J73/m5Hx7fp3NPp4y3a95wQQgghrxNMxhJCCCGEEEIIIeSlkhIwzwnzHBGCTcbWtF9QkqNNtwIBKdX5RXX9UjcsbWWVwizHlfqyns0+mZ/V77e0+0bzx9pjoMrlfFXk9FK1UrdNyKKp0yvT879u19Xldt+5Sdmy7YXs1pyrVvT6faN06YjzErJrielj26/Re9umjO111Pe0L1S1yOwJzL7wTCkvfx8JQEQIWNZ1YrttQ5Kx8zy79Ou8pmDneV5eCSnp8n6S1idkz5O4WiS3YrPeGy9Q22uT968nV/vlW2Xn7NtO1Grp3SZga706X2wtt9dmn8t6Pf0fORBCCCHk1YXJWEIIIYQQQgghhLxUihzqySfZv5UuaxNstu1RWV8ILTWaY3viBS4FKNdy6ryjvo3SvF7K+POoUozYbqseP7qXz5OU9ddRzzFOzI4kk382tq7Vt3fq1TnStN+7pvO3s7m/Zf/4Xulz67Rr+7fRJli9AO1tt8MJt0MQe6naF6t9ATmSrq3A1H8n/u/P/i34e9V75kbvZft3ZN+bXv3evdfvSXvt/nOht95er7zXo2fe9qlUur1N+OyzA25u5u2DCCGEEPJKwWQsIYQQQgghhBBCXirHY8bhMGOeI2KMazI2pSIgbDIWQGe+V52YtaK1Tch6ManTtVv7AJkPtNapKcmalrTyzc73WvaXcjvHajAJzOdLyPbr2DbaenbeV98fqDptWzWJXM+17Klr3Xlj28TsqK5uZ+19RyqP0KnaHmMZ1r//bVIWbr9PzvYEoshKO/9rfeYzYsSSgAUkSVmS3ZKATQghIueEEIJ6NvVzBtN2m4zVS0nIzk06VpKxOdc0bJnnWcvcOh/tWN72ZK6Xpv1kq17vy9y2jq9r97f19N+/f+m5XbeErK7XS8aWRKykZ1txq58/25+MTz65xb/5N5+7eoQQQgh51aGMJYQQQgghhBBCyEvl6dMjAODqaodp0nIjOCFyWqj646r8CmZby7kiVWW9SlktGv1+Lz1FuImoq2JMizkvYavMa8/jJWmVv7bvVcjqOjmHYZ9sGUw/ah9qG717oNvS11Dr1vs8qmPL6vvk6/pj9D0/h21x2w5JPDpv777obX3vdVv2WbXX4UVeLdOitu6rcjYhpYgQipAtkjZAD1Nc/zZsErZNxLbDDtdErB6G2O/ridf2mtpkqP5b7idP672167ZuX17advU974vaVsRm117bVvvySeCtffo9bZ/L1rPa6xVhTwghhJDXB8pYQgghhBBCCCGEvFQ+/fSAL7444t1397i4iBtyQ88VC/hELNBKEJ3itHVrG1rmFqFl2y9tWAm6tAidhLRzh9b6IlCrSOrNJevXgX5a1tfF0m90pKBPxOalP1be6uM1o5Ss7WOburV17Ty+uk4pM6dcJW4rpFpJ29bZpi9vbSP1/ev1yc8ZK+9De1/98yVlpZ12Kc+6zAUr0k3OLdIVKHPE5pwRoyRi5fmw/Zb31s7pKqnWGTlnHI8zck7N3LGSjpX5YmWpE7ZV3Lbp2PFQxVbaynugt7049RJV6vSGBfaCti9Ix8ncceq1txzJ5/bzSxKwbbq290zq96+Vu4QQQgh5/aCMJYQQQgghhBBCyEunSguRUD4Vq+WLFqpenmrR4ROyNs1ZRaRP2tbkaH+YYitDq/zsD1u71BoMU6zPOU7LeiHr069yjtFwvL2UrL1WK423hi7u3aey3k/LnitmbV2/rydp+22MOF9kST/t9ch52vu21b59lks9uc91KeJVDykcY0LOYUnAyvDEpZ2Sjg3r8MSSitX907JXp1yrWE2rlK0SNq1DEvcSszJMcZWNXr568VrXvWCVvrWisS9i9TMwEq6+Tis+/dC/+hz9ZOtWe62E7a2PkrHuSTHXZJfPns34+c+v8fjxsfeQEUIIIeQVhzKWEEIIIYQQQgghrwR2+NOe9OglY8uxVqqERnz0ErE+KasTsK2Q1Ocp9cfDFZ8zTLGWwbV/bYoW6pj+kLh+eOF2GGM09fxQwzYl2xO19lrGQziPpay/f6fErK2PTp1WXqla7YHdNvrU+6+vV19/K8Pre2fftzYJK23ZpQjZusQqYa1wTcv5WhFbBbKWnXaYYp2M1QJWL325vFqxa4cttkMX678/PxzxSKKOhybuC9VxgtYLTS9w7Tna98jew758BfKgXi/Z30pZ3T+LfbafPZvx059ec4hiQggh5DWFMpYQQgghhBBCCCGvAHmRRDInYkBKpVyLTQCIcVusWuExErhVrPmkoq4rQk7vGw1XbPdpkacFqz4G6EtDLwF7Ehhr20WI+uGIbUpXp259H1r5mtV6e0wVkO3wxfp437au46/JlrX35K7DE9c5a/vYNsZmtn3vtKzW72H/GdDX46WgzP+aksz7mhYBa0Vrla1Bvde52ddeYxWN7VyxeljirIYknpdkbB2i2ApZacvOL6vlbG84Xvlbbv8++9JzJGJ7iVh9r3uSV7/Xdn+bym0l77nDE4+Fq/6BifzIRMtW+57Zvuhk7egYQgghhLz6UMYSQgghhBBCCCHklUCkjZUQQcmNKrWsPLHDFVcBW+va4VtturJNl9Z26xyyrXCz6chsJFlPGG6lS7V0Fnk6Ssj2U61ZyTrZBnySthzTpmTtEMjo9qNNw46uZZyW7dXRZfKe1H3tUMH+2B7nCNvalq5sG9b3Xw8zbeWnFbZyz/zxbfrSSsPyI4QyZ2wVtXmRs3Wu2FLfp2Xb+2uHyLVzutbhiZNa9pKwbSLWpl99kr0nK8t19eRmvTdWzvdErL4+P6SvF7G6HXuv+wK1/76Mlrav9VyjoYr1eUYSVpe3ApkQQgghrzeUsYQQQgghhBBCCHklOB4TjseEeY4AsA7JWZKwNWFX5sssx5SlT8ZKIq3WTUkP9StJvdJ2aSebdoSxCNECuJd8HCdlt9fR3ddKU1/XtzUu03PAtmW+zdKGTsOO90H1T5/Py6Z+YlbqAj3ZNdp/wsoO2td4Eaxqm+PsszF6j6HKLSJW5Z7JUMRFrMblmKSeo7iI7zqEdYzl2DKPbDs8sb4mkX92CGFJsNZEbM4Zx+OMnMvfX6kzq6GLZUhjOaaK296wxV4Al2sv191K2lZKt5K0XZd7JO17AerbGUtVn+A9V8jW9G9NAVchXVKwvaSwlblb0lX3icMTE0IIIa83lLGEEEIIIYQQQgh56eQMHI8Zh0NaUoF+3tewITOsEJX6db/UAXrSqg7hq5OeVs7WtGOVb+VYvbTpVDlvrV9lau9cuk3dN51MbVOsPtnbJn3b1GybcrVlo/lkx0nZss9fV29/PUcp6yVR2/q9/bU/PfqSdizXe+e170s9XzunMEyyuSdq2ySwTWTWZKUI1DJfrNRJ6r6WeWNHMrb+bWwlY9thi0s9ScP2ju0lX20iVCS0Fqa9ZGyt1/6N+v22bn0D9WdAL1lqz9cK0F561ffJrrcyV5/fS159fO/aR1hBm3F7m3B7mzaPIYQQQsirDWUsIYQQQgghhBBCXjo5A7/61TNcXERcXk4IIWCaipVIykOUVKAXfXZeWBG3JRkrCdlap90OiDG7doqILcnZdtvKNrEnvX14znUM9p27jTuU+fK6r59gbY9rxaTe3kqv9sXUKDlr62zJ1TOsq22tk7r17YxSsXVZJG3v2ZC5YatQnef6HJYEd0bO0Yn1InlLIjasbbTDE2sZa4WoHlrYy9g6XPHsErN1qGKfhLWJWD0frRW45W+wFbFaSmr52heZ7VDD5V5u1+mdr192XjLWz4HbSmms9WxiVt+X/pNnBbOVuM+ezfirv/oMNzez+RwkhBBCyOsFZSwhhBBCCCGEEEJeCY7HMgxrmzLrpdr0sqZeWzmjE7M2/SnH9OZPtXX1fultO0esbafU0QLPJyfbOVNPJ1JrfZ/C9UK2X9ZP78o9tEJSDy/s+1XXtcQep2X1e9GbK9anYLWg8ulZf6zm1FyyY/rDNLf9knK5X1rE+iGkm96pNoqkrD8YyKuQrUMV9+aIjcsQx6WN0zLWCsEqTv3Sp2TrXLEic8cv+/dopWp/aGJ9L72Ure20klIf16uj2/GfFVWMt+9P7xpG673PJrkXul/9z7De9bfr+vibm5nJWEIIIeQ1hzKWEEIIIYQQQgghrww5A/OcMc9+vskqxmKs6VgZolULmH4yFmtZ7xggrMMja5FT6uu5ZKvgrJJGhKReoqnfX9d1e3X8vrts36Vsq7zu20rKFhFpRaat20/MbiVct+Wm1GmH6P1y9K+nvQdexLbvh8jrELD+0EDaKMMQl3ohpPW51e37OWK3hie298AmOHsyVovXkoSVcv3qD21cl1DS1p4P6M8Vq+VrO5xxv9wK3tN1fF0R37VPo7Rrf5mSF6xtMlbXKXPG9ud7teK1lcnynMnxhBBCCHn9oYwlhBBCCCGEEELIK0Sdn7LMm1nlqn/pBKoI1Ta5ZpOxst4usxGNsrTzoEoSsUrXNhVrE6ZyTf30aBWvNmHaple9ZOylees+ndKsddrUbb2/tk2RWOclZfWxIlx719u7r7WsTc2OrttzStb6FPK5aDFmhzHO63a9nowqbnvHeGlf6+WcAEQAWJ95ScLKjwFEwvrhiUdz7vbFYZ0LtjdnrJWxWuBuJWK9BNUJ0QwtYuWavVit/dX315ZbgTmu458F+/kA1zf9HvQTvOPr9J9HPglrhyYeLXsUsZvx6ae3ePLkSCFLCCGEvAFQxhJCCCGEEEIIIeSVQidjSypQlmiWQBVSJXnYyltJyGphawWul2S+zA8BqxOvfokzyso5rOhFU98mMOsxWoh62VolTz2m0jtOzgXYFKgIRn1eNG319+n9vg+jJKxPzdrjRlK6Rytex/N1do4etFnvhbTZHteeRO5zCHlJt8oSq2At88SWdZkbtpRJetZKWD00cU901+e7l461adde6lXLWS1w+4nYIpPlPDURa0WsFZytfK3Cdbt869h+/X5itvYXqk+jl0/I+jJ7Dr3deyZ6z4h/HY8Zf/M3j/HFF4eTxxNCCCHk1YcylhBCCCGEEEIIIa8MRUQkTFNYh2YVCSJJwSJW7fybVsL2kms9CSvbNkVaBWldFnpD9JreY0vI+vlVe8dtJyxtX+1xVsj2krpW1LbDCev+jVKf7ZywVtjafbKd1ba9/p5MrOW5s68VtJ7zxWv3aNVub/jjco/6cron4HVb5T7KDwNk6OwY01Ie1fDbbRK2L2J9H72orC+dcB0NPVy3+/PKWtlY/860fBURXPtwWsTK/erJVy9i6/WOErHniFg0fdgagnh7Xy23stb/CMA+mL7Pte82YUwIIYSQ1x/KWEIIIYQQQgghhLwySCpsmtKSiM1IKZiEbM51ftcqVPT8ilWWeRnTT8aKGOklYZcWnaTV4k2EpIjjdl7VVtK2ElMLz7H8bEWqlreyH6ZOK217Sc9atzd0cS3Xfe5J2f4QxnU7u7I2NWuvF25/T9Cue/sHqXbPwd7rtm177/T90s9FKatDPotklWR3TcjWpLeeD7Z9lXMH15f2WevLQytkW8Haitd+ndq2lbJaumopO5avPVFrr8Gun0rEtuUjEbudlm2P93VlLlh77e28svp56j+3o3NJYpcQQgghbwaUsYQQQgghhBBCCHllSCnjs88OuLqacO/ebpFW2YgswA5XDFRJKNKkHdJYS89aV7ZTCipxW4VmK2nbpU3polMXZr1Nv56zDrNthxW2+/sp2XpML/W6NQxxbfOcfXq/7rMq6bYFd03tMcBY0uq2+vPKbh13jqlt58JFM5R0K2Tl3DolHIIdpngkYGuZlbB2nlj9wwEtNdthitt061jKjhKxgJ9DtpWxVrD2k7F63f7dWPnp6/UErb3mU7L1HElbl1o4i2wdJYO1lLV/A1B9tNcqz5GUpQT8/OdP8fnnt7i5mZs2CCGEEPJ6QhlLCCGEEEIIIYSQV4acgevrGTmXuWNj1DLWJl5bESpypM4Z64frrXWrNNX7q6TU7ftEbK0jUsanYmXIX32MT93aYXz1fLC9+WT9Ma1IbYcZbocttlJYtwPVBy8PqyT15y/r/RSvld9tYlYfo99DW94e0ztWsy1eu0d02m0b1/2z75dsj5LTtl5P1Pr76mWsrNc2BlcyEKHnJmTrenLS1b/g2tZi1Z6/3zcrJOvf9Law1SJWP1tW0G6J2NMCtj1nTwL3rl/6rQVzf9m7Zrlnn3xyg48+uhm+x4QQQgh5/aCMJYQQQgghhBBCyCtHzmX+yhgDpklkbJ3PMwQgxrCKWqBuy36boq1DxpZ5Z0Ui+mQsAEi7ATFKf0zvTFmRuGXYZCtsRY5mJdWqqMsZyzHoHtdfB6yMy51tXWd0jC/zw+96+sfIPj2EcWmrJ0V9wXmJVI9PQ78IRlLY1TL9KuI0uG17hJbk8v7X9GvdHonXuq+eP7iTWMHnJWY/IduXskDOXs5KGVwy1orIKir7ydhz1k/v60tVe713E7F1aOG6ra9FrrV3zX4O3VrHPze9Z8k+R9KOtEEIIYSQNwvKWEIIIYQQQgghhLySzDMwTV6mhEau6OSpTl/qF1zK1KfSfOK2tCOipx2mWCdI9XJpEVrcWQmLta1+CrWto0Wo7l89b2+eWt83e29OD12MYR9LHZsi1sfKPavXXu+zLWtTs76ub8Pv06KrTcqeL2rPE2B1qOJ6XL0fth15/3RaVo6tArEvb/2ytiEJ7FHfeyJ0K91q54H1QxDbYXdr6rO2L9dq/x7vLmJtO9v7RonbVrrq+9OTs72+juqNz9Frbwt7/bX94zHjcEhq7mtCCCGEvClQxhJCCCGEEEIIIeSVIyUscyZmTFMAIPNsivySmmEZyliEmJVzMmespGZjrGU6YSlp2dpO3d8bflbPMRujlyd+qNpSVtOzMPuLcPPJWQBKBveErhWDWvZBtatlazb3reJTnrXcXtO5+/z+rXpb9a303eK8JO5d6Q9VLM+grlOlrK5nBXRPrIqw3U7AhsH7Vs9V17fkopWwADryVadmy/EiB3UyVp+jPZfth/SxlZB1X//Ynli1iVibZG371C/rpV2xXrNNyNa6vTljbZq1J8n71+zfq5Qyfv7za/z8509xe5ua95gQQgghrzeUsYQQQgghhBBCCHnlSKmkxKYpKPFRhxIurwCdlgV8clYnXovI9OWA3fZJ1NpmlacifYvktIlbYCQGewnV0mY7P2wvxSptyHmxtqFTslbmYm2vSsM2TauTqqVO6EhF3VeY47QY1vJR6tXt2r6u09bT5VZ8ftl5YkfHD1o15+6fy9+Xul3XW5lvE65hvYf2vqL7bPj+aOEnffBCtJ+S9dtatMrxvSRsew4tROv7bCWqvX/+PF4q2+P1OW3d8xKxvk3/vOg22nb7wnjU7x6988nycEh49mzGkycznj6d+w0QQggh5LWGMpYQQgghhBBCCCGvHMdjxscf3+Kdd3a4uppQkrEZ8+IqRIj6uWE1en5ZmTNWErFyjJ1nVstWSUC2Mq3OJ1vlml7qeWyXHkBLtH5C1q6LLG3l8PZ2EXh9YXde2TnlvX1+/6hOr95W3VPHbXGeJD+PrT7XFLJOO9tzagFrpf5WArZNK4+SsXboX1k+n5S1srGfhLUy1cvKu697mXpOUjavf8d3TcTKdUl5O1esLG2KVidh9XyzrRBvy2q5PddHH93gr//6c8zzcz+chBBCCHnFoYwlhBBCCCGEEELIK4kIDxlKtQwzHJRYCevwwqP0m5U7NR1a6+qEqJaqQE3cWonnU5/L1irb7HyugBWubdK0l5D1iUi/zwq+Nu0qfWqFpG2jnh+oAlC3D/ikbG0H6hp696ZNzI7un59r1tddr6jjq8ZpV1v5fBF7bnzWDhVd3+tSpt/b2h8t5jstZns9bZ/bY60EtOJSysZi1stYuw6XjG3bGEtW6e9Yvtp+j47R1+Pr+H6NRSyac/fLxse17W8NT9zi08HyKqMAUMQSQgghbzKUsYQQQgghhBBCCHllkeGKJYlaU6xYlzXl6iVHrePnjJW6KRU5VsrbZGxPHEnKtqRbq2yTuWglOSvLpQX4hCyWuWf18MJVCqOpb4UxXLt6G7ACECfqnFte9rXDJmNwXHu8yF2NTaxuSak7jTH8JTglxvyQxb25eG06ttbtyeiakq1tjOb39cfafnhBWcVmK1S9cG1l5EjC9trvn19v62Pbdnx5u0+f53kSsToZ6+vqtKu+Z/pYP1esvpYWPzyyFrG+HYpYQggh5E2HMpYQQgghhBBCCCGvLMdjxuPHR9y/D0xTEbIy7LBIrDqPbFjFClAlX0nQ1pSnTcRl+DlnAZEn/WSsnFcPOVuHM4ZLyGpZasXpaK7YvpvR+7KTmm27bULXplxF7Ep/5Rp1UrZN+I4TsX6fvv+2X9LfOqyvv95xMjQ3+2ud80Xt3eaN7eHfIJ9GruVt8lgPaayP90Nbe0G9JWT9ELlbwwZrwbotXv1xXoz22/f9OS1l6zbMvp6Ircf05OtIzo5efenqz9mTt/b+++e0X+7P+ezZjF//+hk+++zQP5gQQgghbwyUsYQQQgghhBBCCHllub1NuL1NAALu3ZtWqSoJ0ZKWDWsiFYAayrjKFCmT4yXFKiJMhJ6Vsl68VPEqbVZR1hsG2crapVSVobvfS1fdr3Eq1gvaXv2tMkCL4N7wxb1j+uI1u+2euLVitpaPZPRoeGjfH1u3W3PQfueMZ9azAlaXV/FqhyeucxDXZ7kVur00sxzv17NZH4vTVrpub4+PbdetbB3L2rFs7felrfN8c8XaaxlL29GcsVkNm+7vuX0vbFkrcHMGnjw54q//+os7PI+EEEIIeV2hjCWEEEIIIYQQQsgrT84Z85wRoxZYVaqWl5WyeijjKnKq2KzDFcvcs1rKWkFmZWg2dbSg1WnY3jFLa2hFn5ezGNRv97UpWZ1q1Wnd3rlGfdD3biRr9fnrcePEbKnj98sxgBbAft9Y0vo2Sjvbdc/jnEa2hnQu+61Yl2O0mD11/lEqtm3jtAzNrt5p0Tpq89S5ZN94iOJxe6eSse2xIxG7ta9K1/HQxnW/1B2+Wx3h2t+vh4gmhBBCyNsAZSwhhBBCCCGEEEJeeYqMTZimOvRrSawFJVFlPlhJu9aliFIrXL3gGSVjR0MS22SsiNh6rJ8P1qZe7dygvfRru66lpZWj9XxtO7Lthx7upWnb4YZL2Skpi/VapL1ekrU3x+yWnK3HjyWtrfd108rX0/vPKeu3MRJ8ZT2bspFg1WV9uTlKvvba08f59W0R68+t641ErH7W+tJ0u6w9biRtrbCt0rbt7+i9at+Tevw81/mvCSGEEPLmQxlLCCGEEEIIIYSQV57jMeP2Ni3zm8ZVqkpCtqRi6xDEIkxlKSJWhjT2dWPsJWpFFtZ2ZGjiKoHDktatwtcv6/nq3LU2YVv3F7aELJzkhWmjP7Tt85SdU97b5/eP6pR6PRF7/vDAvXN91Zzq21ZyciRd/fu20XpXxo4kbNnnRamtM95/SsKOz9OK11PC9pSs7clTLOl234aWqICe81UPOWylqx2OuMpXf+xIxLb3byy6nz6d8eMff4GnT+fOs08IIYSQNxHKWEIIIYQQQgghhLzypJRxPCbs99GJl5qA1elSm3TTc7r6+WG1ZKqp0WXPsmyHLS7H1UQqVDK2SFURbFW0+TSrnEMkbE3f+nlE+ynYsu7brX0W0elTs/4Ym7aFa9smZbU89WlZuSf62HqN9p6P5n8VyVzLbcrW3LmByNqaL/bLM0pErmc/eWy/7mkr1xex5dhtMdgTq7WOl4Vb+3UbvXP5c9xFyvp6ug399+yveZRytddur6/9jPDttPtHjN8X3Y/yOhwSbm5mfPLJDQ4HmlhCCCHkbYEylhBCCCGEEEIIIa88NzdpkRdhSZvKkMVVysQoy5pAFJkoc8nWuWVlHRBpWuaPxZqo08nYmoit5ynz1Qa3DZWI9cu6z4te2S/yVve71pG+AvX62pSs7C9t+Llj6zFVvtZr1W3X/sGdQ59nOWLtX3+/b9dK79MC09Yf0QrhF8X5CcZzK/ZTsneTfl6KbovSsZTV2/0hiHv7em1std+XpH5ff1jiuw5H3NvnE7F66GGbjM1NMlbfj9570RPS/poOh4R//a8/x5MnBxyPFLGEEELI2wRlLCGEEEIIIYQQQl55yryNeZ1rsYiSoKRHcALGzwkbOkugyp+g5IkXUfUYnwwtdcs+Eak6eSplUHPI6gRsbcf2qZc0teeS8pqS1e1V8WoTsFXMjpOytg0th3V/bFrW93WUmLXXIiVZlbXXstbq+CsrXG2F8wXq8zA2veedd7tSr41Wxta2rAi0ZedL09PD7Opt36bvl5eRdX826/46dPu6ju2T7Zsc7+/PeM7YUZ3esMSnaK9d75P2Usq4vj7i+no+p1FCCCGEvEFQxhJCCCGEEEIIIeS1YZ4zbm9nxBiw2yXEGJVgHB1T0qyylESsTslKGhawc8aKKC1zzeZ1f5ljts4jm7OdT3aUkJU0rRc4Oq2rk7BSt9aBOtamZKv0tQnYtr4uq+U23Volqly39MFSz9eew7fZr+PbG9MeZ8/9dTJKSN6hhRPHnhKyo6TmOPXa295Kwvrt/r5eu6NjR0K4t+90Mrat25Op/Tljz0/Eju71eNkOcZxS2yYhhBBC3h4oYwkhhBBCCCGEEPLacDwm3N4C+31ehCgWySlStMoPmcO1pmZ1+lXSoTVZWpN3dqjfkv4s5VV0lv16mGGfkJU2xmWFOk9q3e/nkfV1+gnbmnjV56l9tnPCenxS1s47i7U/fl5ZuXd2vtd+YrZek5S1yVnfbiWbfUvNOwmuLXH7IkXZXdraquuv39c9R8LWsm3RKuV3kbSnjveiUurq8rqvP0/r3YcoRrOvtt1Lv/aHRvb3rn0/WrKpUNt//PiIp0+PSIk2lhBCCHkboYwlhBBCCCGEEELIa8P1dcLTpwn7fUQIE2JMqLK0CsUY63yyIlJ1HUmiCiJ2gYAYbYJV0qolBVvnhrVJ2DYhW7BDFfckbeljFb6ASM9gBKfucy/1Wq+17tfy1A49DNcXuDJbriWvbaMeo6WtbdO3qxnJqXPjrneTW191MvHc9s8XsL6slYLnyNde2fNsb6VsT0vacVvnzA8LyN/odjJW6pSUa92nE7G9FKykV+31bd3z09eTcxla/W/+5jE++eTGJPAJIYQQ8vZAGUsIIYQQQgghhJDXjnnOmOdkhgQG6hDCRa4UYSr7RKTGWNoodfQ8sHb+WKxJ1HbuWF0mYrVNyGLdtyVkbQpUl/mUqU7G+rli/Zyvfl7YKnRFmvaTslXU9uaI7SdZtQgfpW61GNTpXl9P7kVrJEcp2rbe9v7n4Xkk7ukUZbe0u+/ctOZXJWHLshWxvWNGUrYnW9vtsYi1+7eFrG23L2zrEmYI4d6z3d77dinr+vXZZwc8fnzAs2czRSwhhBDyFkMZSwghhBBCCCGEkNeKnIHjMSPGhN2umNUYqxD0SJkkWGVZ536Veq3YrXO4AlhSsOVcYR0iuZeQ7UnXkZCV4ZLlXFqI2uPrugjWtt7WdlvWb0fXO7fc7t8Ssz3BdSoJW4dO3kYL6RfJKUG3Xf/8Ov2yPKzTk7J1/1iw9spejIRtxavU82J1lIzV6VQvanXqVdf188L2xGsrYnuJ2a37rPHvSU82Z/zyl0/xs59d+4MJIYQQ8pZBGUsIIYQQQgghhJDXjpubhHkO2O8TgIgYdXLTihJJx8q8snUpNfJSryZni1ipwhUobZchjMMqf4Fat4rSIlhrnZ6ILf0di9tT6xjsG22jW8emU3v1xuU67dqXoFrM2uPbVGx2ZVt1W7YStS+ac09xql57308LWi1f2315WP8uonZLwsr2lsjt7z83KduXmq3M7Qlbu6xSV0tXLWR12WnJPbpXdnjoek6dtiWEEELI2w1lLCGEEEIIIYQQQl47bm4SDgfg/v0JIWRMU03IacnoJSxQxZ3UkWGJq7RphwmuEqamYPVwx1ay6qXdp5c17dmbYxbuGJhye2zdV6XoSMCiOUbfL5tA9cMJt/XbtrfSqVrOerHbb68tt5J2rf0SpNf2ObcF66i8J7S3EprtPLF9kbhVdhcJ67dPidpW0o7rbYtWv74tYreHKG7lbO+++/vd3ie7z6dvJXFLCCGEEEIZSwghhBBCCCGEkNeWw6HEW6fJD91bkGGHRTRKmV6q2kt5XoYortuATs6KkLVtiZyVclmW4Y/zmqyV1GzpU1ja7w9DLEnfWhdm2wtWmQf2LkMVW3EY1H2pQioEe3y9n+Ohiv193a6zVbd/nH5PT3NuxfPk2bmO7bSI3Za258jXun6ugB0L1nPqeJFay84TstJWX9D25exIvtZhirf22eGIa93R0MTtG9F7v3Tf9HWnBPzmNzf4xS+e4vHjQ9MWIYQQQt4+KGMJIYQQQgghhBDyWpIzMM9l7th5jktZNjK0DktcRKOkZLU4kTRoFTM6CSvbNekqidQ6NK5d9qjnsKlZmzqtclTkpz2XP67us+u+PdmGqSPDDOskaymzyVdpXyda9Xn0MMQ+yeqTtr399t5lVd5P2D5fIjafFLdfJsR46thRQvK0gH2RyVjb3jnSVbfX2x6J2l4bW5JW2mjLt5Oxte1+ErYenzt1eve/vZ96nz9nrVuF7/GY8PjxAb/+9TPfACGEEELeUihjCSGEEEIIIYQQ8lqSM3B9PWO3i5imtEjEgGmq+7U8CSEhxoB5luFDI2JshWBJs/oEbZGosl7nkdVzzNptScoCIn2LDK5LqHStT8DqdK5P0QI1waqFal77XfZrm1RFrxawWpbWMpsk1nK2nr+mZWtde1xN+MKd276HWmzrcp9ytsfY85zDl5GtndbOanO03wvXXt1zxGtdb9vwslPvP1fAyvpIuo4EZStS/XY2x4zqtPJ0exhiP/SwTsLW5ehe9e7TWtKVv3JPpOyzz27x4x9/gZubBEIIIYQQgTKWEEIIIYQQQgghry3zXKRdSchilZxVSmKRNEUqlrRsSX9WgWPFYhUvNiHrk7F1aec29duVUr8u230+AWvrjtbLtpejrfCzqVotZOW6qsj1knWcltUJVlunimPZV+8xVFlWx9Rr8vevJ2zrtW3XdXvd9tio3lXgbtXv9bdX30rCvLGvttUXi+MUbK/Mi9jRsVuCsr9e645ErG//PBF76jVKy47ep9PPQU/aigR+9mzGkydHfPYZhyYmhBBCiIUylhBCCCGEEEIIIa81KWXc3s5IKS6CMQCQYYtLnRjLkMYlcRq7ckWSrH6pxZ5Py+qErE/Ttq86V6xOzxbpadOwNgGrpWcvMWv3Sb+qULVzw1axqocY7idj2wQuXBtlnxatVmz7NK1+53xytgq5nkztCdsR2xL1job1rHP12xz1oyfqR+nM3jlOyVdd/iIFbCs0t+eNHQva/vDEvXUs8zMD7TywW8nYckw2c8b6e+fvbe+e9+6JP+eTJ0f8y3/5KW5vmYglhBBCSAtlLCGEEEIIIYQQQl5rcgaOxyJa6/C/dsjelLKaL7YmXWUeVyuBglqv56hypopQnZDVSVzXQyVPbeq0ysh+GraIzJrQ7c0lWyWltFeP0fPJ6v6U+r1kbHB9q9fpU6998aqFam9f7YWWtDq1q4+pdfX7PZKp7XDHLwotPM+rd2rflhj021+dhNXnOFfM+jp9UWvr3nVY4rYfW/PJ+uPadvy1+OvwtNfeu8a6nOeMm5sZx+P280EIIYSQtxPKWEIIIYQQQgghhLzW5Aw8e5Yxzwm7XUDO0c03mta0aRGOkoxNiwBNq4CV9vSyn4wdzyEr88DWOWPrXLE1/dqmZmufbQL2dDLWpmR1mlb26xSsTsDWa7FDD9t6MO1rseuTrDVJW9vS75M+XotgLyd7CVr/HrS06dLt+oNWnsOn9QT8Vnv9sjys42Xw80hZuy+7OqPyntBsZelIVp4rYQGbXD01PPFWQlYnYVu56+/PWE5v30d7PukPIYQQQkgPylhCCCGEEEIIIYS8EaSUcXOTkDMQY2iGBy7CJKzipKRk0bzQTb9CJWtHdUr7WJKtfrsuNb19bQLW1t1al9TqeH7Ydm7Y/lyxVQ7qtuR8vXKdpK0yt/bLHi/n1PSTsa3Ube+jPd4c/QI82biNsUA91UYv4XsqufnlJaytP5awvXqteJV9bf2+pO2J2F5fxiJ2nIL1ArZ/r/r3tVfPrvdF7DxnfPzxDR4/PqxDKRNCCCGEeChjCSGEEEIIIYQQ8kYwz8D1dZGx0xQQgkRT9bCmGXGJrM5zP0kZI5oEq5SLcNlKyMpQyHXu1rps54y18tamYf18rZKeraK2N19sqauPGQnXWsdu13py/8r5/Hyt+ua1aVefjPUCVc7Zlo3qmhL0qCK4u/vO3EXkjuqOhlXuC0JgnNas+09JQ13uRWmvbEvKnk7LflkJ2yZfzxWyKZ0vZP21t/e1vb/++n069/Y24cc//gJPn869hgghhBBCAFDGEkIIIYQQQggh5A0jpYzjMS1Crg5ZLCJTJKweWrjKVSAlK0NFzKYUFiG7lZCVYYr7ydjStpdVRdIuPTD1axlMuRWj2dXxx2xt36XsdLlOu/o5Ynvy1tOmb83e7jH+eOBuEvUczmuvL/9Gx2/J116duwhYXe6F67kSVsvTfp1taVnX9XZveGJ7rm05W4Wrba8Vs1v3sr1X/XvTXkcpm2fgl798hidPDjgcGIklhBBCyDaUsYQQQgghhBBCCHmjEBlbhirOCKHMCSvyVdKtInS8jJWXJGFlWROywRxblpKQDctcsXmVujX9qvtoxa9IWitii/Dti1qgSiU736ze9+Kk7Lnlfl9vf6nTytqxbLXDJm/V2W7n+dhKUbqaJ2VsX5yO6t1dwLbHepnaK+tJWNk+Nxk7qtvb15eyPTlr5asXrlJXzxPr74m6O917pve34tpK2HIOIKWEX/ziKT799LbXECGEEEKIgTKWEEIIIYQQQgghbxTzDNzcZADFuoYQIUnYMnRu3S7767FaxlYhI0nPamiqqA1KMtV1kblAFa91mGJJwraS1iZftbDFUi6CudSt11HX7TC/vRSt3W/P3ZZJX6zMqsMY6+GI9bn8cXW7HdLY9hUNdtjkPvJ++aGPvyznSNhabyuROSrvC8SRfB3t64nb5xWw/XrnJWF13Z6s3Zaz28MTV/nalvfug76+/r1b1zauzZ7r5z9/ik8/vcH19RGEEEIIIedAGUsIIYQQQgghhJA3ipJcA3a7jHnOa7K0pOaCStnZeWFleGERpiJoaio2qHRcHbJY5oYF7PDEvWGKa7tlf0nNaqTuqMyuy3yvQJ0TttbD4Lje/nGZbXepEURe2WGXdYK19k3vq+fpJVnbtKw/X7PHHAucL0+fBy9Qx/tP7duaz1Rvb0nEkYTtD2ncl7atmN1Kwsp2Fa1jKTsWrl7sjuaE7e/Tx+jhivv3r96n/r2y92vr+JTK58mnn97gl798BkIIIYSQc6GMJYQQQgghhBBCyBvJ4ZAwzwnADpKsLFIxIkYswxfHtX4RL2kVpnVYYr0UiVrK9PDGdSlyVs89m9ehjIUqgPVLJ19LnyR9K/uxJkhrXb1d17EeJ/3WdcuyplHrPerXk/7YeWFtmtUnZvW9qcI2uO1WltUUrK1r9wEj0dbWuzt3kbpt3b4cHJflzTrb8rXs80Kxd8yWYO0de0rSbgnauv1iJKzIV789vqf1+s+9b2PxnPHLXz7Fz3/+lIlYQgghhNwZylhCCCGEEEIIIYS8kYi4meeEECKmSeRgdvO1+vlgsQ4jLMJHi1OdkJU6QF9g9ZKx/fRndnVLWU3a2rZqAlWkqN3Gmj4NSkrZulW0imTt9U+f26Zbe4lZmMRom5ot90UL6TY9W++dvg51huacaOrYNl4M4/bGQnir/HkFrN0ezXM6ErfnDUfst88TlrV937/zhi0eidjtV4/nEbFtG6VwnjMOhxmPHx85RywhhBBCngvKWEIIIYQQQgghhLyRlKGAgcMhI6V5mcc1oqZfpU6ZQ3aaACAhxoAQ4pLYjC4ZW5YigsYJ2Zqg1clXwZfrlGs/JSvDJ9vj9HZN4pZ1EazSH5ti1UMr1z6LZJXyXlq2Xl+bmK3nBUR2WcFq534dpWdrf3rlfQN3Sr6eO+fsXdg6xsvW0TFePPfFYdk/2tefG3Z8zJaAfR4p2xuOWB9zTvlWQtYnYnVb7b0cS+6e4O4JZj2U+W9+c4O//uvPcTik9oSEEEIIIWdAGUsIIYQQQgghhJA3lGLf0uJQ5hkAMlIKy1LkZ17mf5UhgYuEkeGBS4JWiyFbT6dla1I2mzK9reeVxSAR68sktWr323IZGtkKqTZFq4/TQvfctGy/vSq6+olZfd76/vQlrJRZqeaHNx4xkq7PI1vvcty58tWWtQK23f56JGzv2J6oHA1J3O6zUlX3eSRd7Wskac8TsVv31F6vr1cKj8eM6+sDnjw54Pp6bisSQgghhJwJZSwhhBBCCCGEEELeaI5HLKnYtA41HGNASchG5JwARJOMBeKSjqsJWZFEsU4z61KkdikJ2pKCrfPIAnlJ3+r5ZHuJWL2vJll7c8j6On7OWBHA/flla5mtI1K1n5YF7PFyXi1lgd4+m5zV97HWD51yL2f77/fzSte7snUeL1NHx2xJV7+/l+rU5X1hO55Ltre9JWltWStaRxK2LR9J2PPmifV4Cdu/P/17tJWIffz4gP/pf/oEh8PX9EARQggh5I2FMpYQQgghhBBCCCFvPDmXuR9zDpimrKSqzAdr5aYkY3sJWRE2QE3IApJQ1XPJFvla2qj90MtKLyWbO/uwtru0hN68sHKs3VfaqslWqPr++FrWJl19W8vWoF2fdN1KztZjvHTstbfZhKl7F+4qc0fiddTW8wrY0b4XIWFtvbFcbfePUrQ+6dor3x6eOCW7r8cpETsq763rPqaUMc8Zx2M2f++EEEIIIc8DZSwhhBBCCCGEEELeCg4HIMaMGBOmqcwLO00lFevnkS2p0QxJxAJo5o4tdcuynTO2Lmt61YrWNgFrU7IlWdsmZ4sUllRq3V+2tawcJ2VrmU63jtKyfp7WWmZFrE6z9lOz9Z7VfrT3MRhZJu35uvo+jzglfQdHna6xUaW/b1u61rIt8dq2Y9etwNaicku4jrZ70tYLWl3XJ031tgjNLfnql1rGtvepXlvvXpy6Z1uJ2JzLEMXlxxvtuQkhhBBC7gplLCGEEEIIIYQQ8hVz+f4lLj+4RECwwcOeJ+p9+Z+BLDuyK9PbuWznlJEOCc9+/Qx5pk3Q5FzSbjmHZThiAEgIoRhWkY4iZ6rM1K3U1KrMNWsFJ4BlbtoYpb2yXkVqSc6WPmTVZk22St3eXLIiSusD0M4n267ren7/Vp1R2bnl5+7v1Tun/hZfzfM/lnR5uL8vC8dC8S4CVpd7Wdoru6uEle3e/LBb+3rC1e63IlSWozSsvif98rbe1jVbYV1SsMdjxi9/+RRPnhy7fSCEEEIIuSuUsYQQQgghhBBCyFfMvW/fw3v/8XsIcZGxAUXMAl2X5SUrUEXrKmGTla85Z+RUyufDjOOTI24+uaGMdaQE3N6WZGwZYrjMDxtCQp0fFioh2iY8NT716ZOtIlS1tNWp2TqHbWmrDmdc5K2UlWU7V6zUlfPK8Mi+jk6zVtp5ZO01B3Xttp5cu29P3RlVR5+/7rfbvTbqPbbn8fu2j38xtNJz1J9x+d0F4ta+nmzV5SMBW9ZbIenFqm2rL1K39p0rZ3tzxPavr15jWz6+Rn8dvesWATzPGTc3M3760ye4uUkghBBCCHkRUMYSQgghhBBCCCHPwYPfeoB7371XBCsWQeVSryJcL9+/xO7+rinXGAFbCspitQjlpec1NGVKysZ9xLSf8P5//D7SnGwdVRcZSIeEdEi4/vk18vHNF7cynG7OwOGQkJK8FxE5JzVcr5acGSFEM+9rlbCtYO1tl3URrHWYYr9d5q4t5y2itpbJvLZVooal/QwtW3tSVg9PXK+v1l32yF0y2yMxq+tqYaoFbMXvkz5amarnldVoCezRAm4kbb8s5wQke8K1d6zdbsWj3T5/WGLdh94xpwTs6TrnSVigPLu2/Py5Ynvy+JSEPeee9YZQlqXMEfuzn13jyZMjjm/BZyEhhBBCvj4oYwkhhBBCCCGEkBFLirXH1YdX+Mbf+QbCFIokilbINuJoNJqrFBl7UJerpO3IWS9Y1/U9kK8ydg92RrzmlOtrLsv5Zsbx6RHPPnqGeZ77F/tGeYmwysnjsaRhY8yrxJNhhWW+yJJqDWtaD6hCt8hSO58rgFWilrr1eBGUMkwxsDVscakLiGjNSzJWztmrJ+tYjwOsdNXS9JSEPT1csS6/6z6/f1Rnq749dpReflGMZKvdv1WeO2W97ecZltge1xOQ+hpO19sejliv+326TpGy54nYXiL2HLk9ugej69Pvo5x/njMOh4SPPnqGL744ticghBBCCPkSUMYSQgghhBBCCCEd4i7ig//VB7h45wJhWiRRxDrv68W7F9jd31UBG+o+w5afUmWjOWFDDnU753IOWc9VtjUp2ZzLUMZ6SOMk8qHIWGQg7iN293Z4/8/eRz7mOuTxXF7pmPD0l09x+Ozwpe/pq0bOEfMM5JyWdFyJvkpaNsaakBVpO00ROQPTZOed1UnYnOtww3pb1xGkvEjSul/SsVry1mNs6lWnYesxkrat8lj2STJWD0dczptV/4LZlnp6uOKyL6g6tr4VpDqZa4+t261otKlZeyPsfWmH9v06GInDbWFY629JWSsNe3VOD0Gs29na7pf1Ba2uO5KzUmeckN1Kw54nYEf3onc/etegE7F/+7fX+PTTWzx9OvhBCiGEEELIl4AylhBCCCGEEELIW0+YAuIuqgIgXkTc/959XL53iTjFIluXOV+7Cdjnmaoyd9bPWWa7HZZU5VqWihjOIddlDsixCNmQSxlyuaacM672V0AG0pzW5KwMYXz44oD5el6li5z39Z6PtrxhJY2XUULBGdNUrkmWktaToYBFaNbhjWtKVQSkTvlJ0lWSsFJHhh4u97QmYFPKq2yVJGwdHjlDUrL1Gvx6P91a07jtvna7lsnwxP02YZ8JeFmqBVkrUvWxVXr7OrpEy2IM2/ZtPA9jsfs8orAeuyVf63Yrdc+Vju0x56VgZfuUlLXb/WGLfR3AJ2TbuWFHErZ3j9qy02nYXv9yBo7Hkoj9/PMDPv30tnt+QgghhJAvC2UsIYQQQgghhJC3nnd++A7e/7P3EWKownUKuHh0USStJF+deB3NYdk4rh4ZJg0rideymduybJOwPhUrQjYgrInYkJcUrUgPGa445/U4M4RxzghzWEVrvihS9tEfPEL6nbQObZwOCcfrIx7/u8evuZAtpBRwPGIZpjkjLvZzmmpCNmfZDpBE7DSFRSAmky4tydOwSh8732oduriXkNUpWVl6pLy+bIq1pmnbdK1PykqZrlvPP07H1uO0sPWp2dJuvS8wbS1bJj27lnaGHNZDLbs7Yvrj79WLYrutcTJ3LGDzyf2nxKsuPyVfe2VewPb3j2SsF7JbwxG3ZbrPp+5Zv/w8Me37llJNxP7859f4xS+e4eaGiVhCCCGEfHVQxhJCCCGEEEIIeXsIwP7hvg47vAwtfPnBJa6+ebXO/7oK2ajqdRKwQxmrzudZxUhQsjXYfSGHNei4JliXIZBF7pl0rIQil1eOSspKOjbnNQUrkraXupW6KI4RIQbsp32RsMeSmk23CSEG7O7vkOZUz5tzGer4tRK0SmoDS0I2LXO51vSezPmqh+WVtJ8dUlgkq8gf3X5oJJSeM7Y+DHZZxVIv/bqViNXl2KinZemovi7rJ2Z1mR+euG1b6lmh5ocuruVjcVeTss8Zg70T28Mhb0vEVoq2262gHUlG37aXsFti1qdFpeycuWN727207JaIHb2XvXvTLz8/IaxF7DwnPH06I6WM4zHjyZMjrq85RywhhBBCvlooYwkhhBBCCCGEvDVMlxO++4+/i4tvlMSriNe4j4gXcZWvWsICG0MRn+N+vFTqJGDXfUbcoUpYJTtCDvU4L1RFhKWlTsQ6PPGanJXlMsyuScYi13llU7sedkXwpn3CdDVh/3C/ziubDgnzzYynv3iK209ev+E+cy4i8XBIi5ANJhkLlARtzpKUHSdjgbjKw5x1WrbK9FLezhmrE7KjpKxPxLbJ11FS1q8XYeuTtFKvv9TXqSVpPVbX18dYMavrBbVt/2DaIYlr/Xbf+T8CsHPRnn2YYeu8vTa3hKvf7qVZ67oXpuNjzhm6+G7DFI/nX9V17bDE7f7RdZ/ed5dhmtv5az///IB/9a8+XeeynV+rH44QQggh5HWFMpYQQgghhBBCyBvN5XuX2D3cIYSAeBlx8Y0LXDy8QNiVFCziIouCWwLVEXkpexdUEHEdlngJLUra1ezDUr7Uq0PChrWe7Je5YE1AMsMkY31qdn2JGJTUrKRtl76u/Ux1PYaIHDIiYjlHCMipDG0cduVe7h/tq9zNGfOTeR0m+dWn3It5zpjnpIQrEKNIJis59XpPkKaEdQ5YALBzx4qoCkv77Zyx9c3V6DcckOGEq9jUx+g3E2fs09vn1tHlPjVb9/lyn3bdStD6c/WGMVYt261Oc3cVgOfXHbexJV/Ldl/A6n09Efm8Elbq9de35OYoIZub87fzwo6v39N7hs4XsbZ/85zx+ecHfP75LW5v03MLeEIIIYSQ54EylhBCCCGEEELIG817f/c9vPcn75UhiGMoc8BGdFOwJ+eEfZ5RULV8UkPOmn2LmDVJ2a3lYJ+ZN3aRsqZc5pDNuV0u88yaNKxIVbeep1IvxTJEcZgDpv2E6XLC/v4e6XslKZtuEz7/t59jfvr6zMd4PKb1PSrzutaErCRjJRGbc0SMWGRtOV4nY8t2FUO9lKsspW5vrtheWlbLX5GT5yVlx3PF6v21fzXR64+p27VuvQc6uWrL9X2p23bY434itm3TI0NEa16EeBu30UrCU8f5dGhfMPZkailvy+4mX0fbvRTs1r5eArUVttv3x3NXib113XL+lIBnz2b81V99htvbmSKWEEIIIV87lLGEEEIIIYSQF84HH1zi29++d1aKcPSl9OiL51NfFOdckjjX10d8/PHNi7gc8powXU148IMHiFMsc8IuEuret+9hupxqWaz79BLoJGKX9eAt7OjR7omXkE25SaCK5HJJWVMm9UORpWsSVp/vlLDN/bKQl2QrwpqyXeeoDTUxm+MiZJf7l+eMHHNJyuZlmfKanF3n3g0BV9+8wnwzr3PNHj4/DG7eq0GMcRE45fOkyNmwDlVshWIrJL0QrR+D7dDBVlLKcTYlG0JY56sV8VvP0U/Eljq2XI6z60CVfnau2zqUsq9fj5HrGpVXqez/MHp/QOfU2ap/inN+STFu824pTl3e7ujLxVZa9v4d1OV3mT/2LhJW9vdkqxed7fG6rH+9PXoStld+VxE7zxkff3yDJ0+Oy9/y6b4QQgghhLxoKGMJIYQQQgghL5zf/u2H+NGPPlxEQSkbzc/XGxaxtxQxIikX+ZK1Los0SSnjeMz4D//hmjL2LWP/zh4f/sMPsbu3w3QxrUnYVb72JCxQHI0IVydh62pH0npy3W+GHM6h9VWLWPWyFcAqQNe66jifnl0TsFImdZY/Kp2E1XPPSlIWwDq/bEh2Ttk1IZvrvjUhG0u5JGTztCRm54w0pyJ4l7l4H/z2g1J+SLj97BaHLw5392hfIyJj53lGzglAxjTFRYqW96VI1AggLZI2LsOxxjUZK0lZWQJoPg97y/7Qx1WkAkXWiujcEsB6+OIq1/16rVeOsfv721Xe6vIilKsUsynfmnwVwauvvYpp2dbSu95D/e9H7wc/Y9lmd4zavAs90brVppauvo6tP0q/1uO9gGzLTgnW9jxbwxSfO1esr7vFuQK2vQ/tPez1r/z/Awk/+cljPH58PN0hQgghhJCvCMpYQgghhBBCyJ34wz98hG99694yfKeWBJVvf/seLi8ndL4nBzBOsQB1fjm9HEnZaSpidprysl0SZLtdxre+dYU//dP3GombUsbhkPCzn13jcEht58hrQ4gB7/7Ru9i/s0fcR+we7LB/uEfcRcR9kWchhu6csIBd78rYpfz8DulVm34FUKWpS8HKPsPWdmc9ZCdp9XZWx6jtVbou174mY0XIxiJaZV1StMhADnmVszkt9dJSL5Z6KVYpK8v9oz0e/ODBKm4PXxxwfEUliU7IAgmHA5BSXARjEbE5R0wTEEJyKdW0ykWdEqxDGY+XvZStnnNWqHK2tu3laxWstdzPL1tkaU2/WsHaCtc2MVvryee5TgNL/0TOynZP0Na6XpaGpqyU94XtOTy/gD13X1+69uqNpGxPtOrykXztldV/Z8+bF1avt9vjpGzvesfXXu/BaF977/oy2svhlDJ++cunePz4iJsb/ltPCCGEkJcLZSwhhBBCCCFkSO/L7R/+8B388R9/A7tdWOdTrAKh/cK8fpE6+uI4mC9QS8osrwm0vozNyDms+6RM0mIpZez3Ed/4xgXmucgUWR4OCc+ezfjVr55Rxr7mhCng0e8/wr3v3MPu3q7OB7ukYEOwItYIV/8jAr3ae/DPET1eJAQrRxCWpKq0p+WKHvq2xmvH7edBmS/vSNhhWXDbSSUilbiVuiHX4YqNlF3k6xIcRQppHeJ4P+0xXUxIxzKnbJ7zKyljZYjlnDOOx+PymZWMUF0vEAEhlCTsPKf1hyprLZWMraJRPivzsqzlMZbPv97cr1Ju99uych6dnm2lrF7Xn91Vplbhqu7KWkeXeTFby+rz7+Ws3Avbvq1jpW7ulNljehL3RTJKcY7398pPzX9a2z1V3kuK9gSsPt6LVX9MT7y2+8oPm2z5OAm7Lb2fX8LKei+Rm1L59/7Xv77Bb37DETIIIYQQ8vIJWf2X4cXFxcvsCyGEEEIIIeQV4v33L/GjH32I3S6aFOx3vnMf77yzN2Xbpmp77rrRl6m9BI4eolhLWCkTOSvDF8sXsikVSSLL4zHj44+f4XDImOe0HFPKj8eEjz66weev+LyWbzvv/9n7ePj9h7j/vfvY3d8h7uLmkMTNsMRot82+3jPtizYlQ62Tz6nYqyJ/L3WlLfdlOQ+3c7b1Zdsvu/tSu70OW5zq9rq+JF9zzsjH5e/xWKSsSNh0TDg+OeJ4fSxlx4xnv36GdPvq/EhChmkuryJad7uI3S5gv4/Y7SKmKayfk2UJTFPZnqbyo5UYsf54ZZrKZ6dsy2epXdqy0bZdLw9ondu2v79dt9tlafcD7ZDz/phxWTm+X75uNRK1+5uIwb83X5WAHfGi0p29/b0fMOnyfvrV1t8SrrLdHnOejNXlMoLF6Dpb2r43Nc6UsFYu2//f4Be/eIrf/Kb8O377Cn2eEEIIIeTt4vb2dl1nMpYQQgghhBCyst/XeQ7fffcCf/iH7+LyMi7zJbYC4BRtgsWWVxGr07FBfekL9wVwieXldb7L0Pniti51aiyEMqdjCBHTlPHhh/fW+eQkMXs4JNzeJjx5csSTJ8e1Hf+FM/n6CTEg7Ko8ffC9B3j0B49qEnZaBNYJCdsIWEnL6rLm5Bvlpx4NnYbdamsgYzPqXK96TlkAZi5aU6aPlTljscxDK7I11H3+JUnYbmo2qb7KcrnPS0i0vnSdBETEIm+BMpxxCAgPA6arCfPNXOaU/fS2yFs9xPJLRBKyKaV1mPPjMSGEuHyulA7GWJbyWeETr3JDSuq1fpa2Aq0k/uVzuHz2haX9sHyGyZDEWIcvlkRt6YOXluUz06Zg65tXPivbhKzud7t9lzJd3tvn94/qjOp6vkozOxaua42ujN2WliIU/T673tvvZWtp59QPoHydnnjV6+22Ld/mPGG7JaxH11z/f4iynOfy+uKLAz76iIlYQgghhLw6MBlLCCGEEEIIAVBE7H/+n38f3/rWFWIMuLyc8MEHlyalBZyXhAVa8VrW+/PM6fVRIna8vyZi/bDFkoqtCdm8pmbLsMWSmM04HkvZ8Zjw9GmZY+54LIL2b/7mMeece8k8/J2H+PB//WGRslPA5XuX2N3fmSTsOiwxyhJQUlbWASNiy+I8GdtNy57ByfYXWdorL4tsBKhPyurEazcZq49V+00SFrmkWpelPrZJxeZcy9TSJGNzScYiA2lOdZnKMs9lv6ynQypJ2cclJXt8esT8bMbNRzcvXciu9wJYpGxa0q4BFxclHVt+yBLWH7RIUrYmY/1rlJRFNy2rk7FALyE7Tsr6/ZJC7SVk9b5RGtaXbaVmbb1xuW9ve7+nTdV+1Wh52t83KuvLXL3/XBnbO6YnX2W7J2z7crY/B2zbRr2eEadk7ZaA9ftHElaPiPHRR8/ws59d4+YmcRoCQgghhLx0mIwlhBBCCCHkLefiIuL+/fKfA/Kl+8VFxHe/ex/f+c49NXxm2dd++V7KKzrBUr9Il3Rq/YI1rNslqVXXyzG9ZKwu6ydny3l7y5IA8/1PKSDnpIRDifUVCRER4w5XVxm3tyUpe+/eDsBxOTbjcHgF7NCbTgT29/frnK9XH1zh/nfulwSsvEJZAmgSsack7Dp3rMbLn14dKT+FCcOeqL/0xwjZrNrw67pML/U6UFO16m9Q0rF+X8ihpFVzEdk557VPIap5Y1Ou9zN3+gS3npbrT0DMETnkkpBFRkIq66FI2xAC8ABIx1Te9xhwuDiUpOz8cv/mqrQsS/mxxzwHhJBWSSrJWBFEo6F2JR0LZJVkxfq5W9KxkoT1ydiwfhZKubz5OWOZK7YmZe2Dgs461mNLovdUGnaUen0RCdlSR/792K7Xa/McvkzqVtUcVLXlp+RrqTMWsG0bXrj2yk5JWNkeJ2L7+7YkdP/azq8zErG9fksf5jnj5mZefnSVcH19xONXcA5qQgghhBDKWEIIIYQQQt5C/uAP3sVf/uV3sduFZW7DkrR68GCP3a4mYbdSTJqshmEdz2fXl6ciAfyww6eHR6z7R3PH6qXUKclYYJ7DkoytSxniUNanqUjqP/qjR5jnjMNhxuefH/GTnzw+6wtn8vzsH+zx/X/6fewe7jBdTOV1NdV5YaOk9GoyFkAjYhvxOkjpdetiO9XaSNatts/EpGBrYT8d66WMSout8lQkrFpHbrdNWV7kcKptrEs1LG5GRkjLMQlV1i5LSc1iXvoWl8RszMjTcuxc6oWprIddWcZdRLqfMN2bcPjsgKc/f/pc9/NFE0LAbrdbE7KHQ8I8F7k6TeVeFIkqQwiHJiFbfohSfxASY1yHHZYfpJShkfPyw5j64xV51R+6QJWLqG33lb7bly+TbV1+TpncF/3DF72vV7/8CMY+6G0S1kvh8b9D/fKx7L07fVHYrdnsy03ZKeFq17fnjj0lLf2/o7LP7x8P/9u/hrswFtfj+3rqelLKuL4+4q/+6jMcjzL3O/9hJoQQQsirCWUsIYQQQgghbwG7XcCHH95bhsMM+N737uO99y4wTdFIgt7QlYXtJJFOMYkMkH3+eC0W1lobsraK19FcsrWPpW3pQ1YJNEmjlQRsOSYuMsAuQ0iqrPT33j2scvZ4zHjnnf0qem9vZyZlXyQBuPrmFS6/cYmLb1xg96DIWJGwjYiNSr7qHxAMJKyRpAN56utsJVuH7Q1StZv4P5fRdm+ZYVKxa9ny0nPG5mJbq2CVhGxSdbJLxGabjJX5aHPIdVho+ZtNeZXjZv5ZoM4568Xb8tmEDOSQaxsZyIeM3cPdKnjz4eUlZXVCtqZkw5qIlR97SCp1nntt6Ff53EpJt5/XUQnkRyrlcywvCdyMlGQO2LAkYev8sXZZJa1+oOTzUd4Iuz1+EKU9u7/Wkc9fX76dkO3t79UZ1Rvx1T0jYzHZl5atjM3dfecKWFtm67cidSxhdX3/wye/fle2hfW23O6J5joscVn/4osDnjw54tmzmRKWEEIIIa88lLGEEEIIIYS8Bbz77gX+6//6h3j4cG/mOCyprHZ+QdxhDr7+F8ZAz7iU8tDUL18Mh7Wsl9jxXw7bOWPrsJ2jhGydV05EhWyndVnmkQ3Y7fIyj2zAPAPHY1pk7Iz9vgzxLEnZX/ziGT766ObMd4KcIu4jvvePv4d7375Xk7BTFbEmCavlq54jFrD7lu11VQ85u1YP3TqaRrBqUXvi2HM5Jxm7ClQvaIyEycPtJgG7rK/XkOp5m2SsiFj5wYMI26S21fyxImyRyzCiIZXhkHPKdTllhOPSj1jOL+2EKSBeROzf2eN4s8wj++sbHF/yUKRlrtdp3T4eE0JIjQzd7SJSCktqNi6fO3H54YgsZe7Y+mOVkqyt0lWGjpf3wIpYGfLdp2al7vkJ2VPrp5c28domYnvrRQC3ydjmrjdp2nHdr5Zt0QjoP+JWNJqtgYxthaRuu/3R0li+yvZI1o4SsedybvVeu71rs/1u+5pSmULg3/7bz3F9fVx/yEAIIYQQ8ipDGUsIIYQQQsgbzDQF/P7vP8KHH97DO+/sce/ezknYrTQsutvt96k6UTiqM26j/VK5LusXseNUrG6rSNh6fJuM7Z0rLucoYqQkbLJK2Zbzl2FEo0q8JcSINSVbRG7G9TVTOnfl6ptXuHjvAnGKiPuIi3cvMF1OiFMsyVeZH3Y0L6xPxKp1YLx9dhpWC9teu6qsKb8r/jcMent5rEIOzbDFm8lYv+33JXVsRp07FkUCr3PHos4ruy6TK1eJVn1encAtF+Gueyr9iFOZQzbkAMxA3MX1fu6wQwgB6Z2EEAOOT44vPSFbqG+SJGSPx7zK1UpafnRSkq9FqFrhGdcfFehtO5T7KCEr7cmQxyKF9T6dkC0StyZi6+dd3VfXRe4CzQMJWyafvb3krK0Ld057XN2ubbRl0k7/36+vAv0jon75qOzUcMV9gdsfjti215OwPXG7JWG3xPDzMhK77X3oX6OWyPKjqs8+u8X19Yzb20QRSwghhJDXBspYQgghhBBC3mAuLyf803/6W/jOd+5jt2uTsFUC2CTs1hfaW18m2+PCsF77pa98+a6/OA5LvSoiylIP3VmXZW5GSZXVZRETOhlbUnsldQbEmJBSWLZL+TTJemiW8yzLiGlK+Na3Ar7xjT1ubhIOhxk//ek1ZewdefT7j/DB3/sA09WEuIuIu4GE9TJW5N65AlYdo8XgUMr6MnfcKFVr6p5BXiOu7phc92k5utbTknOpq5OswCI5jHip2yYRO5K0cOUynG5yCdlFxK5tJaz7pP85LOcMy/HLcMQ55XWfrpOnInbTXORrmhLiRXk+5ndnPPn3TzBfd8YB/tqpqdV5nl0KvwyPXl4R0wTknBZRmiDDustnV03FVkmm07NrOjlXiVvFbj8hu5WE7UtgLWn7x9W6VfDaf0NsWd3XJmflM78OcZ+bfW394MqtzPuqac8zPv9IPPp9vWSoXu8nZb3E7AvWntzU/66+KLZStf3/32EsYX0fZU73n/70Gp9+evviOk0IIYQQ8jVAGUsIIYQQQsgbxh/8wSN8//sPEGPAxcWEb3zjEvt9XCVsjDUVqGVsKTvd/otIHrUJH7gviMfr+uXFgyRje+U+GVtlRIRPqM1zvc5yTFwTb5KMjbEeIynjaQp4//1L3N4mHA4Jx2PCkyevgix6Nbn68Arv/M47ePiDh0XE7mMRsFMAIhBj7EvYQSIWUPvU+ipgfSK2I1OH6dctcev+Ju6ajJVzNiJjka46BbsOK7zsNwlUfYyI1g2xqocr7r10QtZf5yp+e4nYRfDmpI73/dRCO6h9yyvmkpBNOSGGCJnnVmRviAFXH1xhfjDj+PSIdExIz15+TC6EYjVrQjatklV+TAKEZf7qOl+1/lwrYrT+EEV/npXPmior675gPpP6L32sTctKf3UdaVcSsVUC1/Nruap/2CPHaNFallmVVbEqx9R27L8PdX5eKbHyt+cAT/8Z6gdvzClZOdq/NS+qvo7Rvv78sD15eX5CdlT/eThnOOOxnD5fwn7++QGffHKz/Fue8ewZ/00lhBBCyOsHZSwhhBBCCCFvGH/0R+/iRz/6ELtdEbDTki6U4S6jm1uzTTP18V+8Wil1Xt9qE2HwRXPvy9n+C6hy1UvYnpStw3kWIVLWZVjPknitZSUZK0sZXrS0U04+TSUhq+/xbhfwwQeXOB4Trq+PePp0xvX1/LWltV437n94Hx/+6MM1DWvmho3nD0vcpGR78lWXe/na+xtw8nVLyo5E7l3RCVcAfQELmGGKtXjtilotP5WAXUUssKZckdxxImWX+ije0C69EO6VbS1liOSlH2lKCDkgocwvCxSpm1Ck7PpMTAGX37xEOiQ8+/gZ5mcz0k065dW+UsozIfNWJ/MZJmnZImPj8jmclh/HiLC1n3FtArVd+oRr+Yz3QxP364ps7aVgfULW90d/jtbPWr+tJWorZ+t6X9Dqa/WiVvpv773l9Ofu8z0sd09/1nNt7R/9OKkvLr1QbROw9pj+/q+CsYAt/bT9sv3x/U0p49NPb/GTnzz5yvpLCCGEEPJ1QBlLCCGEEELIa853vnMP/+AffAv7fcQ0Bfz2bz/ExcWkJGxvWOK+jB1R07N9AXuqjf4X0MUO2S9lA2wyRm8H84WyTW3JvvG2DEvsU2YybLEkZ+srIIS0LLX4zQACUkooczfWZOw8RwAB81yGH93vi/l++nTGF18cT9/ot4SrD67wwV98gKtvXmG6KHPDronYgCJiQytjzTr6642A7chUI2WXsrLotKckr2lHt6Xa0JyTkO1JETMkcVlpRKveNnJ2WdfLdZjiDHhZK8MH62GER3LWSFn/WmSvOQb95dp3eU+X8+WYSwo2ZUTEso24Dn+cY6kDAHlarn0XcPGNC6TbhLiPSM8SDp8fTt73rxpJyM6zzBMbcDzW4dLLMMUy9HBYf9hhX1g/v2T+WT9csZ4vtspQXW6HIB6JWV+nXe8lYHP3GL08VdZfl4ep/bel/ZOSv++v18LbP9s8KO9t5+H+/jDEpf1R2fMI2hfJVpNbInokYeXHTillfPHFAb/4xVM8ecJ/OwkhhBDy+kMZSwghhBBCyGtKCFiHxf2zP3sfl5fTKmHly3wvYWVdt9G1SPB1bP3TX5BXdKKqllXBqlNaaIbmzLDzyfr0WBWuelnbsdu6T7ofKfUvIOeo5IbtbxGvCTUiKPM+ZsxzGfZzmqowe/z4aM79thJ2ARfvXuC9/+g9xP2SiI1hTcVqGYu4yE6d5tZSFliFaFDDb2+lYtcy2DLTPtrjddkoVev/lLbmjF3ngjVRP9eO3s6dcr1c9us5ZFeXG7KdS1Y/gwE1napSqj796ueFlTlfzRJlqGIRuupPw/RrlcT+XkjfZFcCMJXtiIi0jLUccgBmlGGtYwDuA2lf9h3DEYcvDvYav2ZqQjatn1XlxxySsJcfgJSE7DxvfS54CWplqEhSmddafnii59BGM6Rvm5qVz0A9dLHIPDtUcTm3f4hKPV1f6vrkrPQhq3X774AWq7WdsG7re+FF3zk/LpI+W04/MOeJx9relpjViVe9r5eUHYlLX3ZOQvb8+9PnnH+/zpGw/vr0vLAyx/v19Yxf/vLZl+swIYQQQsgrAmUsIYQQQgghrynvv3+J//K//AHef/8SDx/uMU1RiVgtYe3csOckWntDEI+StM+fim2/KK7itNZrh1fUr1bEbiVlRVgUYVqlhKRmUyr75aUFQp13ttQvr4AYE+Y5L1KlHCPL47Es9/siym9uZtzczHj6dMbh8PZZ2f2jPb73l9/D5XuXmC4nhCmUVGxcxOtIwjrRqtf18tQcsSOZasRuL0nrU7WqDVN3LTh9L4yo1bLCpWGB+ndiErPLfpOO1cJzWddDEjfb7u8DATYpm7ORsL6uScTKdSgxvKZkVbvmmIBVvudFwsi1JCSEGJBCGa44hWXu2GUI3jwty0UOx12ZX3i6KHMPH7444PDZy07IBoQwIaWElNLy+VzSsikVWRsCsNtFyDDnPh1bRjjAui2yUtL4MiRx+Vwqn2cyJLv/IUr5dwEAspo7XF41SWvL7bas+6Uty82/F+enYs/fHpWN9+uN0eevLb+bfNTb43Z6QnJUvpWcFYlpy1oButXXL4sXr71znBLMch23twk//vFj3N6Wof0Ph5c/BzQhhBBCyIuCMpYQQgghhJDXjBCABw92eP/9S/zwh+/g/v0d9vuo0rBexgJawtYvp9uEqz5HWfa+UO9L2a3+AjqZo+cHtClYLT/lS1pJgEkqViyOiFEREbIEaiJMb5fkV16voQwBWvuWcx2qU9DpWy017HFSVpaSMNvtSnL24mJSMrj0/3AoAje9Bd81T5cTwq5Iz8t3L/Hw+w+xe7Ar8ixifRmZ2hOiarhgEbUB7hiRsj3JKmU6IQt77HpOd75uX1D3CecMS9wlWMG6ti0pUlW2Cle4ff6YEKofEfnZEzEByMvfj07I6rYl8Yq81EVY96/LWKXtWq5Sshm5uf8IAOLS5tK+T+GGaIdbBlDSskAd1npaZONFEbK7tEM6JByfHJHnfE7o8Suhzpdqf1RSfthROiU/4thKyPrPpb4Mlc83n4gN7nMSpkw/JDX9Kp938nep1wH7YMHUHX+e12uWunXdX3Tv72j0Jm7/zdlE6PkPwvkStq14joz0+0ZzqEv7vuyc1OxXQdv+ORK21PP/3kqZiNhnz2Z8/vktbm7egn8YCSGEEPLWQRlLCCGEEELIa8b9+zv8i3/x+/jww3t49OgC0xTW+WElQVW/UN+SsZVWurb1vWi6i3eyX8qG5ovj3tKWaZlR1+uX/PUL/lFC1s6pmN2xkiirQ3FKyqzUlX3tMsYqhafJyg9Juu125T3a7SIuLyP2+4ibmxmffXbE8fiSTNHXQQQ+/NGHuP+9+9hd7RAvIvbv7Nf5YWVeWD08sRerANoyka5+HR2ZCth9arkmcL0AdmLWHLP0pyyUWNSYqrZC9vImd+rlWm8dchhKxIpwVUN4A2gSsKZMzyXr07FLYnWdM3b9u1EJ2WTnlfXzyZpErKxLHX1fslum5e9WUrPSZ5T5YfO8LIPaXtZFJANVKKddSdSGXcDuwQ43H9289Dlky/NZDHJKVVQC8vmWllR+WOaxDusoB5KM1XOAl1det+swxXVdJ2Rlu/5Ax843C5Qh1uXfCV1uE7M+PauPqfvLvrZOvR+lbr+8HuPL7T2VtdN1XxR3EZG+bHuu2NLWVtlIuo6EcOXL3pB+26eut9/3Wl5/6JRxPGb89V9/gS++OOD2liKWEEIIIW8mlLGEEEIIIYS8JoQAfPDBFd5//xIffngP779/id1OJGx/jthynP1yvK63X2Cff6w9/nTffSKotttLxMq57RfNobPeDiGs9+lEbIxaAtdUlpxDBKxQhv2EERr9IRZFZmBZlu1pCgAipikj54jdLiOlUkkE7MVFApDeSCG7f7jH7sEOl+9f4vK9S+zu7aqEjVXEIqIrW5tU6ikRqwTs1vDEPg27lY4dHbsiZWr7FM08svqxBmqCtDenqk/Eyt+PLg+haa+bZPXLkKsYTZ30bcQqYddz67liB23q1KxOxK59XZKx672U7YjSdnTzz8Z6zevw1VNdRkTkXV6F8HQ1Yb6dkW4T8JI8T5uYtkOv66RsPabuL58tYRGma431mJqK1Z/d9TNJPh/rS8vZvHx2Snt1KOTa797csXUfYD+/7Y9j6ue//LtT172pX1pUn8my7f/NqmW2HZuEPYW9hi22Eqc9SbuVEu0lXnX5OalX38b4el/cvy3n3oOtlK889zc3M25vE1LKmOcyPywTsYQQQgh5k6GMJYQQQggh5DVhmgL++T//bfze7z3C/fs7k4itX9aX5SmhKvSGMvbbte66NmzPc/oLabveJmJrWW9bXvXLf1suEkLWR18mt6KhCouSQIuL6K3zLpa2I3xaFsCasJW5Y6epiN0YI3a7hBACLi/L0NK3tzM+/viAeX6zhOz7f/Y+3vuT97B7uMO0L/N4IgJxKkPJSiJ2laKxStgm5arqrMMQ94Yp1scocTpsr1cO2OO1cF0XrZSF2dwQtPIc9yRJtsLVpGZzLRPhiYw1RbqWr38oMPubRCz6CVkzL6wcLxJWJ2ODOjfQT8oKOiErv5dIdrmec3k/UkjleZljEcWh1o8hIqdcxOvSlzUpOy99E/H/YcD+3T2uf3aN+Xpu7/lLIOdilo9HPWes7Csyq8hRkaRh+QwJLiErCX49NL1Ozeo0LNS2X4oMrsLX7y9ldWnTswCa1Gwt08f2y+r5fbk/1v+71K93Ls/3mbv170hPDLfb/bliT0nM5+nTi+Yu4tlLZP3v4y9/+RR/+7fX6zFv2r9/hBBCCCEeylhCCCGEEEJeYb75zSt861tX6xC3H3xwhfv3dyYRW8WhFa+nErF9CWuP2Zayp6mCobarU09l3aaT+olY2fbruk5ulmXozioZdCI2Lgm7siz1RYCIuACwlskcsa8R4QABAABJREFUsz0xXNbLeWRYUZk7ti4jck7IOWC/DwAiLi5KdO3qasbxmN+IZNDFuxe4+uAKV9+8wu5+EbGShl1TsGpo4kbEQslUEa5q+GKTiIUrRxWr67peRrfsSVh3jG9rrecErWHrb0QOy51KoQoMLXRNMnapY86vBG9vWGSdVvXH+HqjdrQMNdvlgJqC1ecLdqnbCLHOCYtUj1+Tr/K3GvL6fsm5IHOoLmnZ9f1MdRlzLP3alwvdP9wjxIDj9fGlJWRb5LNBfx7X+V6BgJSwJPzrZ40Vt/WzSX4oUuvU5KvdtuX6xydlHmv5vNT9susytHv9IUz9MUupK39H2fUHJj0rdSVBK9elj/Ep2FH6Vf97cncxu40WqeN9ve2+eK3b9ppHqdneNvDir7NHX/SeL2FzzjgcMj777Hb9scHjx0cKWEIIIYS8VVDGEkIIIYQQ8grzx3/8DfyTf/I9XF5O2O8jdrto5hHsD03s00mtYLUJpn46VtAS6nm/+G2HMAymfJSG1ev6i90yJ6zeV8VFLzWry7fOq+VET3z4urIsAqOkZIukCEsaNy+SJKv2yz2epmKUylDFZdjGjz66/doSTl8VD3/4EN/5R9/BdFnSsHEXi3yV4Ym35ocVIbusx2Wc1TURq0Wsk6+bQxO7Y80S6rxwZa6dtb0NEdsOSdtHJ1/N8SIhq2Fd5eaalM12e03B4nQyVu/Tr3W/W19fWuD2tlW7Zo5a1PO7G1AWIpa16FXtrelXc5NQ730CYo4lGQuUZCxQthdpG6aAqw+vMN/MePKTJ2XI4pdOuYicgXkWEZnXoYUl2Wo/u/KalC3zy2KdQ7amY+scsfLvg+yXuaz1vvqyc8PqH9Dol5RBJWFteV/ejvb7fVvrve1T5XetA4zk47p3WGckWnv7e4nXXkJWsH2vG6f+vTh1zef9e9NP5vauR9brv8vl38HHjw/4N//mcwpYQgghhLy1UMYSQgghhBDyCvLNb17hj/7oXfz+7z8yInaa6vCUNhnrBStwFwnbJmLbY5e1tY9bX/I27iToNFNQ+6uk3JozVvZrkSvi04rXvEqHaovOmzsWkHliw7Ku07D1XFLur1NSafU69DCjkpCNANIyd2zCfh9Rhiwu1/jw4Q6HQ8KzZ6+CLLobF+9e4J3ffQcPf/AQ08VUJKwMFRtUEjYq6TkSpJ3kaleoYjBMMXC2hO0laZu0rJOvzTDFap8tUtLEmVfThpYakiTM9ZwmCbu01UuuyhDDOjBuxKjep/stydQcjEw1QwkLMndrcttw5XpfaLczaip2+bNYheraj2Jj65yxHjnvhDVtCxQ5m5BKOna3nG+RQBfvXWB+NuPw+aErw18W8tkinzd2ntgqUquU9T86sT8E0Z/Psl8LWKlT57rWSz9ke9lnt33qdlvG2mGodb3yudwXsAG9uXTttv0M12+qr/s8P3TZOqYnTftp0dzs9/091Te939+T7ofPoD+n2e7POcleLWGPx4yPPnqG6+t5HaaYEEIIIeRthDKWEEIIIYSQV5DvfOce/tk/+z4uLyP2+2mVsHqO2LFM7Q8r3KZo6zGnZKxfX0oGvc+dL8Frn+xwlEGVyZe4fihLLWXD+mVvXpN2VbwW0doOKSxDfeZcRa0WBVbC1vXSNyt9RxJ6muQ8OiFbhicOIaHME1nMk/Sx1E+Y5/Iep5RxfT2/ljL28v1LfPt/8+0iYi9UIla9GuHq5aj5kUEwwwobsapE6l0k7HBYYtVm05YXrx05axg8H408BYw07Zb5dS9vF2Ep8tLUy53jpb5IWXlFV9eL1qjqiqCF2gYaKZuTSsUuP4AIKVTBmuswxaukk79p3bct3DXHXOaXTTkhYhGyxf4jX2SEKeDyg0vMT2ccHx9XQftqEJbhirOTfDXJKglFvVs+20IImKa+lLVLO19snSO4rQNYAVv/LcG6T9fprW8v60Nu/62pD1grX+09a8XkVv27sy1j17VhfV2nl4I9Ra//7bFjAb3FOX24i1zWYjbnjHnOuLmZ8bd/e43bVyKNTgghhBDy8qCMJYQQQggh5BXivfcu8KMffRvf+c69NRE7TUElYluhapNKpR29rYXtWMb2jtdfitvybapg9cdpwSrlNdUFI1t1glanYMW8iLCVxGwVprXMpmHbVJKcp4jRKmH9UMq9V+++yPUUQSJDgyaEUOaLlQhgkSVxSbtFXFyUJNw8T5Bk783N65GQ3T/c4/0/ex9X37wqQxPvYk3FioSNqKLVSViTfB3J19BuN8nYjbRtNx0L1YfeOtAXr7pMl/foSdZT+7Xo9Os9Oev39SSsLhcpG+rQxavM9XK294Kro9tWKVc99+sqZqNK3GKpk9QwxUuftbAtzefu+wAs55rUMizHhTLEsYjZnDNyzJguJyCgDFv8dMbtp7f+XXmplM+WgHkunyPTlDvysyZlsQxdHGNehjuuwkwPUyzzZ8vwxyJkZX5akb1++GL5EUzZtuncrWGKzxWy/bIqg33du5ZtUf9duttxPRk6lrC+/uhkodv/Xt+2rvN50r9bx47kq6x7AZsz8MtfPsWTJ0fkDMxzSccSQgghhLztUMYSQgghhBDyihBjwKNHF/j7f/+buH9/h4uLOj9sVHJKvlwHrGiV7UL9klzq+qWXsD0B235hfv633XpoYlWq1nOnXFJSwX0JbE1UbVvbKKj1svRp2Polf1b3raZsRcjWJC2WdmQf1rka16vIUPe6psYk6TpNYd3OWZbFWJV5Y/M6j95+X2zSPCfkjFdfxkZg92CH9/7kPewe7BD3VsL2ErH+pWWpF69Gzvo0rBewQHNMNz3r+rK2AdU/wP0dOBEIVd8z+hPx5V7ehAxkJ39zKyL1kMV6n1lXQw4P+6KlClSK1XW0OYfcu6TaWrZzzHVI4UXOyjE51GRsCOpcImR9Qlb/6cdF0AK1fRG/+l7KjzeWvzlMVejGFJGRkfcZCMD+G3uEKeD2s9vxfXop2B+O6OSn/KikZ+LLcOjyY5dWcEoaX/brdX9MOVc93v6gR9Kqepji+plajqmJ1bGMrcfUa5V2q+TTx9TjrCQ8JVVbeanbEOzBdxk2uJad30bbJ/3vZV/Mnmr3lIw+V9R6AauP1T9kku3ywydZZnzyyS0++eTV+pEDIYQQQsjLhjKWEEIIIYSQV4AHD3b4Z//s+/jww3t48GC/JGIjYoQamrhNw/aGF5b1c1K0fRlbynS7wrnJI5GYur7+sl+Sp35ozDrfKiDi1A6DKOkbGQK4HbpYErFlv9yLul/LV5ESvRSTT8H2El3+vpQkm1yTDBWalv3FIIVQlyJ9RQKX/tShQ2MErq9n3N6+UrYIADBdTvjwRx/i6oMr7B/uEfdqaGKXiu0lYvVwxUa+OiHb3YZrA/32TyViu8MR63V0pKvsX9a7bP2d5H6dkMM6X+xaL7hjRmV+2fsNg0+4hrouCdlhGlbXg+ujvwZk04eQAxJS2RfV/LWSytXy1vV1Fc+9321sLCOKeJVhiuW+5liGAJa5akMIuJfv4fjkiMOnB7yKzHPAPGfEWF67XVg/G+QHIJb6ppdhj+vnqxa28vehU7KyDUi5Fqv+2FbK1n9L6o9q6udmXvfXbbgyoErd+gdiZWz7B7T171J/3/mfp3cdpng568m+jEZXkPb65+3/uzxqc8S43207uq4fkliWn356i1/84tla5/r6eF5HCCGEEELeIihjCSGEEEIIeclcXU149OgCv/d77+C9967WoYlLynIkYu0X2D7N2huSeFvItmLXrm9/CTympmfssd4oSVl/2yenSllNudYhjqtYaPtb6tZElj1fvY9VBkgiVt8v2S8SV8sJGQpUDwtaJWsdSlnKdGpWljkH7HYR+33G5eWE29uMw2H+UsNPfhWEKeD+9+7j6v2rbiIWEUaE9l5esvqybpJ1S6gOBOxweGIvXoNbX9oti46A1WK2uUGdstyWa7mp21qToK5M5GQOnVSsKtfH++TraOn75dvPyOaemeGEZVvfr6VsvZfSv6DOHVwfXLtrWja4fakeiwA7PHJS/RKRHMu6pGbDLiAiYvdwhzxnHKdjOe4V+zuTv/uU8vrZoeVnHV5dG3b/8MkPWHoyVT7D2s93X6985vUTsXXY4/qZKMPSS1v1s1X/LdZj7I94at36+W+Hrvf3SK7F3r87/6Olj25Lzno++v/++B/2jNod/ztr/z3UbZ/qyxbj4YnL8T0JezyW0RtknvNPX7HhvgkhhBBCXjUoYwkhhBBCCHmJ7PcR/8V/8QN8//sP8MEHV7i4KPPEhoA1GSspKJGDWqJqgarTTkUWln322L6QlTb0di3z9IdQ7A0bKV9At1+q2zliyxfxudnWSdn2C+E6V2y5NishdOrVp1ylr2UIY6j7ZFOz/vp0G3JcvZdxHZa4JGRleFw1QeaynnNc7oEM0xkXWVvlrbyHOQP7fcCTJ/OrNfdeAHZXO0xXU5GxpxKxg+GDz07GYluyNkMaR3SPaaTsci3dlKzeVtddFqrOHe5ZF+/P5G9DRIpOjPql93ByfCelutbZKuu8THJWX04OSDnZ84YyR+w6ZGuvzy4JKwnZjLxK07Wu76sauVvmggVQ54zNS/mckUNJx5bHIZZkLMrcsUbQhoC4j7j99BbHL17FVF8duvj2tnw27HZpHca+fl5YgVp/MFJ/oKIfGBFr9TMsm+Ptj3h6+6oot4IWaNOxdn9fzNpy+aOo23pf2T8Sl/qYr4owOLn/9076YctD95j2uK3z17Y9z5uS7Y8SkdW/exnPns348Y8f43gsf4yv1L9LhBBCCCGvKJSxhBBCCCGEvERCAN5//xLf+lZJxEoC1idh65fONs0qbZSllrTofNGtxa2VuGMJO/6yu3ct/gtmnYyy5fULetUC9BfCNvWKtbxeizZYNfnVpnBL2zI0sh0u0yZk2/RWKxN8eXlltR6WoYf10J/opGQBOzQxlkRtEbm7XcQ8Z+z3ASmFdQ7a9ApMI3vx6AIX714gXpRELCKK/Fxkqhaf6wt1GWDTr71k7LoNkdobIjb065jjOzLWiFcnYY1o2RKwwW+O/2ByR5pIqrRXR6ddddvDuWKRTUr1LqlYk35dztUrk7bXe7IMryz3S6dndXLVlGvJ7OrWxHvb1vq+BKxCVd6DEFQbIa/CVyR9I2CXHw/EXUROGdPVhOlywvxsRj624vnls1zH8kMS/TlQhjD288n6H7/457KVs+PyejO03K3/luRG+rb7sH5Gy/76Yxs/d6z8LVtBKMfIurS93iH9d2Tev9Ct8zzYc2RVFlR5/1y23P7bd14ytt/eXfCCuNdOT8I+ezZjnsuPLJ49m/H06ZESlhBCCCHkDlDGEkIIIYQQ8pK5uppw//5ulbHTFI2QbcVsK1ZrImprrlh9DKBFaz+hVPFJpTH+y3MvBeTLfKkXTJn+ol0nZf3xIlZFkIr4lGGAZa7EKixEVlRJKucXkVvmb9VfRGMVp1JX96W5ciWB9dyxtd8RIfhkLNQyISWbmJV2p6nMG3l7Czx9ml6qkA1TwHf/8rt4+IOH2D/cF6E1xSLEVAq2mcNVJ1dDaJdasC7p1vWYLQk7SNz25Ou5yVizT/bD7rc3ZVvC1mqdOVd7jixsrPsyX+5TpVDl2a37Ommw39WV+V9XGSXXlbEOFbwmVHsJWR0WV+3KHLE55zrMsD9W3yf9kvcgYZ0nVuaNRQBiVnPHBiVml2uX9zteRdx+fIv56YxXlZwDjmuAN2O3A3a7Ojy6zCdbqT9CseK0HG8fHPnMrj8wAeDWqxj38lX/W2KHQq6f1/Jvjx1O3kpW/W+O+VPU8yoD9u/UlJs7Ntg/lrRts/XfKl9Hjwqh++T/rbD12/6N9z+PQbaNbA9FXOr3hiMu/2YCP/nJY3z++WHdP88UsYQQQgghd4EylhBCCCGEkJfEd797Hx98cIkHD/brl+gjgWq/NPZfeJ8jYtEcU9Zrmd6u6/ZL4FOJHZtaAnQqquyv7eo0VE1J+bRQ+yW1lwxVXAYjEGzdaqtE2tYv/YNZ1uGT+3PFapFQ761O3OrhPP2QofI+STK23ld5BkS+Shp2twvIOWK/j8gZuLlJm1L4q+Tqm1e4ePcCl+9dYnd/tw5LbETsIjx7r14itpuMXdKU5d7UdnvrmwJW7wNsuT+H3i+4eqYMtuxc1mOVVNVJUX3+NZGq52Rd2sg5r33xaVZpp5t+zaGZp9XP3+r7phO60u56LQGmL+aapH/PkZCVuuv51P71fqtzrnUlMSvPxpKEzTGvz5+kuHPOdXjtJSU7XU6IlyUtm27VEMyvMCkB84zlhyhFzurP9jqce5Wo8iOUQmv9q3isP4rRwjbI+76eo22nlb5A/SyW9u3na/38xVo+Tsai+TeiMv6jtNdW6vo0a5tu1f9ujOr0+hSa+v3+jj/TvYDe4tS/C3YY4tyUi3y9vZ3x9Om8PlM3N4lJWEIIIYSQLwFlLCGEEEIIIS+BEID/9D/9Lv7e33sfV1c7TFNQqabtRGwVq2GZl7SWj2Rse2yVPltCtre9lKp1nQhaSnKtV7/4BiQFKuerIla+iA+mzZqM1YLAfuGv60j6VaSmvFKq6a+akJX+lX0ytLAeUrhcS+mL7JPEkOyXftTkrBznl/oLbykrSdjdriSNdrs6h6xQUr4BV1cZ0zTj9rbMHXt723tfvlo++PMP8N5/9F6ZJ3YqQxSv6Vadho19OTpMxPbmigVMMnZUr0ng9uQt1D7A1JP213InX31itqw6Qfs8aNmZ3TlylYub6djecmlnHe432/LhdkZNqi7PuZHOy74cl/5KihauDbTrq+BFLZd5Yte2VXvrubO9P6ZP/pypU6aQtKz0PeSAGKK9vwDCFJDeTWtCNh9efQGVkowCUPp6cZExTVhGWbAS0f9QBIARtc2DBGnXf+b3t+2PbPSPWtAsy3r9bK/7tDT29bfXayq1lbSjkRqqlO3JXttX/W+bbaPti0/M1n2hOcbTk76n2KrXE6+yLvvk38V5Tvjooxv87GfXd+4DIYQQQgjpQxlLCCGEEELI18wPfvAAv/VbD/Dtb9/DxcXUka9KAKkvvEdiNQQ7THEvFStfduu2tyRsXe8LWovdqRNNHp1IrSK2/dJehIBO0vaSVbZenW+ynYMQ7tg2GVvFLNYye+/6SdkqwovELe+HiF0teOswyHGVjDVdG6OfOzYsw1YH5FwTshcXE0JImOcyXPHX8SX5vQ/v4d637+HqW1eI+1gEqDxzSsJ64anLtl7LXW2kqJaoTTLWnXMrGdsI2ebZV33W26MyVS59Pxc/b+xozljdpi/bnDPW/z320rGyHKRk5VzdOWPV9evE6trP9VaGtY5Pt64+VSf+6p8hNtO0QbUTgZzy+r7muAhe+S2DCGaUeuY5kb/BFBBzBCYgT+X46WICAMTLiBQS8u3rZaJ0SrZ+vpT5p+u/GfpHJdspWb1eRaNOzPq68tle69TPej8cfS2r2/1z1DYBEZpafMp+20/bXmkndPbJOaqglnOuV2j+PcmqrJ+Y9cfXfbmzv/139HnIgwN7idicy4+APv30FsdjXn9olFLG48cHClhCCCGEkBcIZSwhhBBCCCFfM3/8x9/Af/affQ8XFxOmSadbtXSyMtWL1VF6VsRsqVOWPQlrpaL9QrhxTud7Joc/MA/29VJYo+3+up17UMtUZXgGCSU9l2KMYUmiemFbRamtV+pqyVq+zIYRsGVZvwCXL/rlvS/z2IYlwVu2c44q1VuGKS4J2YRpCjgeE+Y543h87jfobB7+zkN8+0ffRryIiLtY5ohdhnpFRCtEdYL11ByxEXZbjkO/jd65AgYJWbRSdi1Dv2wkZL2I7HLqrciuHXWMnn+1Vl9EoyRDz5GyIqyUTF3P4/8ElxSpEbQh1JTqYG7ijFzndA1FqjYpV30/gj2/kaZpOb8MG5xqP7zAlXqAkrB6GOIcaqp3KV8vdwr1Xqp7GqZlmQPivpjb6XJCiAHH+0cgAvNh7v225JVlnsurdjpjtwvLDz7s56T8gETEbZGcIq51G/7z1+8r20WIbn2+Sz0rab3MLf2zctXX1+eq5SJ3YY6TOoCVsl6carnsr8WL21qWnbgM3br+2uz+F/OA+fNp+VtHZsAqXm9vE3760ye4uXmJE5ETQgghhLwFUMYSQgghhBDyNfFbv3Uff/fvvo/f//1H2O2iGprYS1j/8olYLWBHUrbKpJrC1G1AlVkBa+VrK2fPx3+53PuCXpf3v+jXc8naeWWLHJBUbJWvWqZqEWuX+pgQZNhirPdTywn5wr2I1bAKWOmriNkqbkXA1nlvZRhjEbflXPX9E8mbM5Y0bDlmmiJyTtjtirnaL8KoyNn8lc7jd/XNK7z7h+/iwfcfIO6LhF1TsTodK1LTydDNlx+C2MlYAF2BuwpYd57eum6rJ2ibddR1I2DN38fgD+KcvxMvRNEKVqnnhwn2qddT6ViTal3k4ygl2+unEaTL0L455DLErz5Ghi8O2dwvI2yxXLeI0rTM7Zrq/fTz1q7tLenXEEMVt4vEN3I3L1J26W9Oea0TYljPuT53yzLmiIRUE7IZyPvS8O7eDiEGpMdpmDh8XSjzfgLyGbXfR+x2uRmRoY4AIIg0zWZdPhsLvc9u/1z5MnuMiNdW5lq5WsterJQt235/7We/X638HMlZu38saZ8Xn7jV4rUsMz7//IDPPjtIjfXHPpwLlhBCCCHkq4cylhBCCCGEkK+JDz+8h3/0j769JmLr/LC9BGxdCiKaZDjb8TFauFqRW5athLUytpUzzydk6xfcIdgve+uQlO3QxPUYba70vlbI+nL5krxNxurUVxW2tj9tMkzfLztcsR1muIja0jM9bLGuo2WGiHI9PHEdphhmmOKyjEbG5pygv4B/0Vy+d4kP/t4HiJfL/LBTqPPDdiSsHhK4K0XVsrxPnXbk2N7+QYp2FbTYOCc6Ytb9bfg6fp8ur5uhWz6iGabYHZhLVNWc18tWOW5rvdduT8QO5a36W+kNMez7sgpYtH1f21NDD8s9k6GKdTJXD3cs7TVDFaO85zL8cNNHPf+tkrkybPGasF1EbpwiUk4IOSDsAiIidlc7BATcxtthSvh1ISXgVg23HEKCJEPrD4LqXNn28xKQz1BZ92lW+fzUQlYEZv08bocjHg1lDPhjS5t1SHqo88oxbXlZP0/KlrI2LVvq1Gta9jTt1Hr++FaSqhq+4ATt5/0oDVt+0FOTsF98ccAvfvH0jucjhBBCCCEvAspYQgghhBBCviamKeDiYsJ+30vFtgJWy1ORtj5F2xumuCdppZ2RhLUyCmr97ha2ftk9ShSNU7HtnLJeyEpdv27lbF/klqWkVWsytvZGErGjZKzc8/rFvL7fWOdhrOlZScbqhGy9HzHW8wLlOFlOU6knQxbvdhmAJGSBy8sJMrzo8QgcDi9eyoYYEC9jmSdWRKwsRYhKSlZk6lK2ClIZPtgPI+yHK/bPvq7jk7Buu5HBSgr3tr1Y3BSw6y4rIv1+W7SIvo44Wdv2AiUsElbOk53shJKy9pE2yzVRq9YbOav/PKTdsIhNBCMy13pabuZcU63J7U9VcOrhitdEq75nam5XObdOxJr2lmGNTUo21KTrKlpzPW5tX99vEWjrDyFKMhYZRcoiIqeMjIx4EYEAXH5wiflmxuHTA94Ubm9LItL/O3PvXlT/1uh/S7JZL8fUdf3ZXagp2vpZrveXOrbM/zE9X7n+N6cvZXvHVjlct0NHnvo+mBZGlTe4++e27ZNN4Yp4TQm4uZnxi188XUd9ePZsfo7+EUIIIYSQFwFlLCGEEEIIIV8xMQIXF5NJxPblqZajddkXrH2J26+n2wyurH55vCVh7+Jka4opNF+CS7n/AtoORdxK2DqcZD2H7VutX9tqz6OPtfcgL1/eS5qr3kefjNVJMZ2MFQFQ1tthkHvvtT0+mGSspGqLtM9rSlYnZXe78so54/AiPVFAGZb4og5NrOeHNa/1ECs6T8lRn9yUbS9XfTvDNG5vifbczT7U6/AStitgA5q+jxxMUw9YH8ns0uLr/KtLMnY9T7bpUWARRqEcs0rcoH9koBKHyLWe6sO6T/ooMlfV76Zvc72n0g99Xev5e2naUMv1Oc21u/akjawE4Hr9QSVig2tDvUIIq7iV4Yt9MnYty8tyAuKuDFs83Z+AABzi4bVPyAopYZF09bmYpoyLi7D+SEQ+m+IqteUz1b2BzXrZ1olX/QObXiq2toG1nfpZLJ/PukzabNOup4cvrv2Rz3Wg/bfBi1ldx9az/dcfI35o4y9H/TdSSCmv76OkYAFgnjOePZvx6ae3q4wlhBBCCCEvD8pYQgghhBBCvmK++90H+Of//Lfx3nuXbq5YL2X7orUOSxzWJKxeAm1CFoBpT75w9hL2+aVs+wVzT5j2vzD3X+D7Nn2itbfea0fXgTuuLv08srIdY16TqX7OWbkOfc8lOSvvUZGoWQ03nNdhi2W/DAkqaVn9/ullScaWYYjLl/kRKZVYYknIFsEfAnA8xqWfCYdDxvwCwk9XH1zhe3/5PVy8e4G4i3Z4Yp1W1alXqOfYDWXcfamkrDxnMca1PZ2AbepKHYRmDlDdFy1ee4J2XZf9Xr5K2bLueZ7k+CoVvaQROSV9UNJW110TplntU2JLErWyvvY998+VwzKHq7RVHrPajr9wn5p1yViZE3Y9j79el1CVBOuayg21vXV+WZWqzTFXSQ01V2xczrMkaM09F8Gc8ypc1+GKJ/XZJ4n1XUQOGXkuYneaSwr94r0LzM9mzE/ezIThPANPlmvTz/a9exEXF9GlZe2rHFNf8qOYKk/rZ2vdr3/cAoz+Xdga6r5KXt/GWnOpb4Vqrd8eb7dtO7YP/c8AK2zb486hl8jVoyvI8qOPnuHjj2+6x1dRSwghhBBCXjaUsYQQQgghhHxFxBjw6NEe3/rWFb7//QfLsLK9L7KD++LXitFW1NbyUkeO18eFTr26b2tbl9Vyv91j9CV4+VK+pqH0th/KcjlS1RUJWr4k7yWPar9652h6Gdrj9Dnr9We1ra+vJvTK/bWpXxEMInN1v+x7rof9bFO2In51slbEfH3FdbjraQqY5y8pYwOwf7jH5XuXuPfte5iupiKt1PDYa+Kwc6xOIvZe671dXV1NyG4lYhsRG0IVd6Gev9eWOafUcc9+L8E5krBDATv8u1Bk2+46jLFPlarEqU+1BqhkaCcZK+341OoqXmHngjUpWF2+HOznlJUyk5BV6Vmd5JU+rsdI35REluN1yrXbZyV19ZDOJoGL5dzuWTTPSYQZ4jik5RpCNsnYnHP5EcIyn2zelWGL8zFjxpspYwGoz4/6zB2PeUnn26T/NJV1P8+sPb5utynY3r8ZbZlN0y6tqvlnAf3Zr/8GpK7U2dpX2y3bobtf8G3affUaxsMcb6PblR/yHA7JCNmcgadPZ1xfv7nPIyGEEELImwJlLCGEEEIIIV8Rjx7t8S/+xe/h/fevcHk5YbcLg1Rs/2WlG+CTscB4zlgApn29XdYBEYe9bbuvfrF8Kgmo54oVkSpzpFoha784h4nuVSFVJeioXl3XQ7T6ZGvvS365NzLEowhYfVxprkrZkmrVc8bK+wJTJsnYWhdrKlauDZBkbG6SsdMU1mPKesQ0lYZ2u7K935fty8sJMZb40/GY1T25O7sHO3z/n30fl+9dYrqaTCrWSFmZszVWiRlCQAxxMxWr5ViTclXtNolYnYLtiNlhmerb8pgMBawuLwslZ1X5us+VnYWXQrqd1cueSLUqusnVUFOvTZkIUP2Y6O3oypPan1wZ7DGSYF23c9le/8bXv4O8PjvrvK66zbBclwjRZIWrmT9WJ2OXc/vE8SqTs0rHIpREbEKdOxbL3LEo1xJDmTtWlggoc8nOz//39bry7FnCs2dJ/ftQPpfeeWenRm6Qv8F2SHbALqWufMb3f6jTpmf1j170j3ikXStns1r3/3ZZ0Vn/nelJWdv/epz+d9Hv+zJp2Db9mnPG7W3CT37yBMejjbrOb+HzSAghhBDyOkIZSwghhBBCyFfENAU8enSBhw93q3z19OVnFUv1y+zel9vB7PPtjkVsWOu026N6Nn3bfvlcz9v/HrpKUZtw8ilZm3w9lZD1x/v29f31X3LbvoqA9QnZes36C3x7X30y1qUT0b4fUs8KjPo+6dStHCfzyRb5mzspWSxzypahKZ/HB4QQsH+wx+7+rhGfJqlar6IRoVqq+ZdJs0rZhrBd9yM09bekrO6b7o9Jyqq3drity84RsKN9TaquVlyfZWlekqRL+lPqm6Trerqlrkqm6nTrWqZTrS792psjtknfrunEWibSdE3l6nltRY7qe6/ldmj3+7p6GOX1Hqi++vsu7a9pY/VM5KCueenrulzSsFKGADt37LIMU0nIxn1EvCwJ2bdFzGoxqEpxOKTl37aamAWWz5F9UPsA/Rksn+H28xVmf3+71qv/LujPcv3vgv+s77flj63/9oROHduH8b7z0ff2eEy4uUlqX/036fY24fY2Ub4SQgghhLymUMYSQgghhBDylRGw30fs95NJxNZkbKnTplllOFo9V6yWVjVB2co7LZzQtC3lfttLKt1uLR/LX6B+oa2XJR2aV5makv/yuiZWa7J1nJDtpWUlTSV9sPt1Paz3yLxLZrumYnWyqpeIlbr2PtdrqAK1zh1byrCWtcnYvCzLcdMUkFJYUrWhScrmnLDfR+RcElKXlyU5e32dlpTsHQkosmm/JGLdXLE98Wpkqk+2+hdakernoDWJ2NDfNutLOnckZRsJ2xGwXhQu727rgKRu576duq8AjJTVw/aacjUErzy+TVoWth0tZdd6+k9J96OXit1KxPrXiYTsmnZd1gMCUki1bTlGp2KjKhOhKnWXNlZZHPNapvuds7pvaihiLVeb/i3HBYQ6Xy5Q545NsaR7l3ma80XG7uEOiMDhswOOnx/bN+QtISXgiy/6w+OGADx6tMPlZVT/ZtV9+qXLynovHWsTsPXfoHMTs/aY8Q9y/L9NY9nak7bb2B8D6fXybyTwm9/c4he/eDpugR6WEEIIIeS1hTKWEEIIIYSQF8w0BfzBHzzChx/eW4aQbb98Bqwo1dtV7vntrS+w11bUvqDa9sd4ASxlWsIGJ32V0FJLO7SilrF1iGJZr8P5yjHWGG0lZOWcPhU7OtYL2TosZlj6V8v9dh0W0/ZP37te6rUvELS01e9nXttqk7F1qE+RsHLvRNR6uR9jWIc39sL5JBF457ffweX7l5guJyNGVymmRKdfX8vkGVqfJSVJEdp6ro4/V0/6Nutoj61vGNp6UH8brrzZ1mVq+7kxzYQ2QaokrKROEbCmU+WRNilVLWL1ukqvSuJ1PValX7dSsmtiVNXVsnQd9njpOwCbkJXzh2COWaWxvifLe7eeO1RRatpQ19DcT9U3/d7re+fPpxOyq9BdJP865PKSjF3TsbuIeFESsumQADty7FuPJDlT0kMW28+mELD8WMn+21KTtHJM/exsHjbIZ6189uof3gS1Xo7xQxK7XrvRFGz7vWv0fenx7NmMZ8+stPZSVf59vL4+UrgSQgghhLyhUMYSQgghhBDygtnvI/7yL7+H3/qtB7h3b1Lzw+pXre8lXZ1/D6a+XQbzBbVe6rql/WDOU+vpL8DR6aPI2Lq0UrZeg6Rgy3peXynlZjuEvMzTClghG9bjqyCV/vrkK8y2TsfW9npUqWQTsL1zWGFaE7H2/SrbI5naO0a/15IWtlJWBKzIjDIssU7IFvEqy90uYJ4DJBJ4Vxkbp4hv/YNv4f737jcytidFvSD1CdaRuBVRu5ZFuxy114hdOQZhnfPz7GSslA3krNmvy+DqfgmMaHUnWBOzeihg2Ho+KduIWCcza+c7ZZ6AkipFHcIXQJmjNauyYOuu87Um19ZyrE+iIgM5VSkrQjQgrKnWADt8MlBFbCNfAfu+BpQUrRLcaz98QnZJ9q7pWElxyrM1BUTEkpDNwJQmpGMqn2tfZORbGjTP06enDfXDhxMePty5z8v+D4/80v8bqpf63wwRtXW9/vvg/5R13fpQ5abeFlrS5gx89tktfvWrm/MbIIQQQgghbySUsYQQQgghhHwFlLk7q9AcfbFsv1AeSVgvTFux6kVsbVOfL6gyLWRDI4xjjKrvdbvK2Cp72yRsWiVsCMlIWSCZ4YL13KY+5WolbPkCXX+ZLtc9SrEurcIncP2X87Ltpa4Xv/W+Vilk56ut16DvuU3A6lc7Z2ydF7Zux5iXfoWlrPSzl4ot9zPg8jIixoybm3QyafXO776Dq29dYf9ob4YlXuXVIrYakSn3RD2v9ZpVufxvIGdHItYL3+6x+r3p7FuP663r99SVmzIlbQ13EDTNoSpRus5x6uTsSMLqst72up6V6EQVoPq8a+rUL91xQ9mrE65Ac6y8FznVY+TerWlXGXY4BFNm5qpVglondYH6vvbmoN0StjlmhLTMPysJ2OV5zMtw4auMdXPHhilg2k/IlxnpOmFGf7hess3tbcIXXxy7/26VZVsm6/fv77DbVYFrj9M/ovHH22e2177UEw6HjC++OJx1Tf7z9vr67R3KmhBCCCGEVChjCSGEEEIIecGEUGVsFbFeyFYz0MrWnoTVYrUn9mp96cOWlLXnshK2yL0iX6dpWssbWaaw6deIGOsQlSllAPMqWIs0TUp2WuGKgYQt2/Ue+OGIbZcC9Jfptq/1fmjhateraJX7p/eHUCVwu6++R/LFv67r3y+bBhsPVywitrwfsh3WeWXLepWxu13G4ZAxz9s29p3ffQePfv8R9vf3VcQ6QdrIylBfXfkaXAJWiU2devUp2FMS1p+nEbDmb8Bud4VyR/p0y1S5XEevfJOsxKs+PNc+rYlZff6MOvSvlri5I0IX4boO9avKtbjdFLFumGIvb9f2RbqqczXbwQna0ErZddkrc+0ZERvq32FPvPp9Zvhj+buU65DzaykbYGUsUERsDoi7iHyRMeUJx0jZ9rzc3mbc3t5dZMu/sUBc592Wz0nZr6VrX8pulwk5Azc3Mz766IZDCBNCCCGEkOeGMpYQQgghhJAXRAjAP/yHH+K3f/sh3n//siNiQ+cLY7u/zi8b3HFKRplj7H5p157H79fni+tSJGyMEdM0LXJvQggR0xSXutH0o5CXIXVLCnaeE1Iqr3mekXPCPAeklCAS1c8JK+1UiVoFJiCyM6znao+tado2AVvbtHJU2qvlsl0TrrJPn7MmWaVfdThiLWHrMe175YflzIgRi1SV4YnlPZL5dkuDdrhiqGRsREppFbS9+Q97xF3EdDEh7ErizwhUJUKHUjT0j9GCrCddfZnIsEb6j84Z2/Z75zXiVa+v7y/qthaFUPVVnSGj/eo3A6FTaRW0aPebuVlduRGgcMJyqe/rjcqa61DDFDfDGi/CspGbUieiJmFlGGAlQNc+eilbRtiuEnZJx2qJHYKae7YjWI2UXYY9Bpb+xJLQRcSaiNXXJ/PFIvYlrCxjjshTLkI2ZUwPJ2AC5icz8okfPpAXQ87AF18ccX1dn8v+Z1046zNwfHw51zxnilhCCCGEEPKloIwlhBBCCCHkBREC8MMfvoM/+ZNv4Opqgp37dXzMWJ6O9uvjQ7e9U9tF8oV1KSJWkrBFxkbsdtMiaKdV1Nb5Y0ubNREradgZKckcpkUwyhfZpV4RiGWuVH9H7BDBerhiLVbLvgBokRVgBKSdR1adYRGoRQjr9mw7WuzadX0vrTT2/ZA+1PeptleH0syurpW2cq99QjbGKpXL+yfJWXl/t2XsOuTqImG1JG3kKpy4lP70ZGuo6dhGZo5eqO3pY7eErxeuvp+NiMUJEWvexBMSVrd3ilBl4oraNOnWnM35mtSsLpcy/TwHu29NxEodlXxt+hPs+jpM8CI99W8l5N6a5KkaStj0rSNNtVRd76f0UX4IAStegVYA6+OHyVj9fmkZLPcjtAlZmWtWP3855vojAD1c8eUEZGB+OoOjFX993NycnpOWEEIIIYSQVwXKWEIIIYQQQl4gMQbsdtGlYq287CVeRa55+TpOyEK158t69YJKVtb5YG0SNmK/3y3LvVnudjsja71ASiljnuclDXvEPCccj0ccjzNSmnE4SDIWCCGhmI4MGa64DGVc+ilzpmoB2huuWK69Hnfe3LF1ntfaRhW3Vq7W+6nnIGzlaVnHel6bqNWSFfDvndSPMSOlKmHr3LCSjEUnISsp2JKMnee87g8BePhwh+Mx4fHjGT7Z9e7feRfv/tG7uP/hfcRdtEMURytZtRitz3AwSdZGjKrtrUSsXyJgTb2ekrKjMnnLjZh1ZV7UDgWsLveP1JmY47X4hJOsIdhHd3lsmzTrIkJHMviu9dcEq+10/TPNqo5KuyKgpFn1uZe5WKXdnNW2lq9y3rQssdTN9Vg5t5+ntvfe9ZKxa3p6OYdJxko/5TpivUchBZuQnQIiSiIWKGly5FI/XpTOhxjQSHdCCCGEEEIIAWUsIYQQQgghL4SrqwlXVxMuLmIjUYFWvsl6rRNcuRZJ9pi67KVle8MyjkRwUGnXMhRxTcTuujK2JGStjE2pJGNjjEhpXtovEbGSTMuYpiIsiigsw+lKPyTdWWShF6o9CRs69Xw5mvtg07buDqnysdDdPsafVwtZPU9tufd1OGQ/P60+tn3P7DPQF/t1iOLdLqzvv7/u/Tt73P/OfUxXUytQUZdmvScz17sf1uWampVjVd1RgrZ60Jq4leMaIayWjeDr9NMnI5tj9LWYvzf3EA1c5p3wTfohiCW16oc3znafTsPq5Gs5xCVis0q4LufU86/q9OqahnUPjE6/rv3SKVgM+ukkqr+mVfq6+yPDLTfzxA7a691nSffq998Pc+yfDylbE7GqTvNjgxgQpyJpwxSqXCaEEEIIIYQQBWUsIYQQQgghL4B/8A++iT//82/igw8uMU1twrVQ5WoVZ3qJVXT647W0taLWCrMq6/qSzgvYMgzxhP1+h2macHFxgWmacHV1iWnardvTtFv6HxtBJcIVKPPEHo8HHI9HHA4HxBgxz8flvPMiXtO6lKidyFnVqhtyWK5Rn7ctk/IqH7XURNOml6i+TZtwtfdWErR6OFS53zWl699HoDdfrOyPsbQjQzlLarg+H6M5YzOmqaRk9bDQu11AzhE94hQRL2JJxcowxSoR6+WTSaEOhjQ+lYxdy1T6Vrfda/dUCtYnYk2fYff1pKxJWOp9qv7yrjdlL4T1eVRCU8qVpNyc61Weucb09stX0QknYN3S9DG1SyNhpY8xVCEp6dak2pF+Ia/yUlKlIVc5vD4nCDU9K3PZjqT6km7VwyOv77HMZTuYO3a911n1x88duyR8Y4o1tbsvB+7f3SNeRRw+PVDIEkIIIYQQQgyUsYQQQgghhLwA7t3b4b33LnFxMQGwqVjBOJ5QLYIVf+NjZNuKQVum22zbtUPE+kTsNE3Y7XbLa79uhzAtr4hq2nSfRMaWfdNURKrMI1tSs/OyDKYfZd3KUn2tNpyn7dRovVe3uauAzBWp5o2VvpyTiq39q7J2S+aOjm+XIhRzp14wZVbYe4lf98WYsdsFzHPGPAPT1YT9oz12D3aNfB0KVdNvK+m6ghQ2GdvUkX36WdCncY/ZMBkLDI/zkrUpO1PEnpSwd5Wzg0drPfcaKlXJT52ElXPqNOyyvqZSTYC1k4gVERtsErabkBVJGmzSVr+PZn5YPVxwrsJ2ff/0PV/+/NZz6z7pvqk5a/X1NDIWtX0zdyxsfbnX69yxeq5e9YyZ5Kx+LR+F8gOGEAPiPhbZq89HCCGEEEIIIaCMJYQQQggh5IUwTQH7fUkmSoqxJvWqUNPbtZ4Iq1awlfr9dKy0tayZ4+U4fXxJxhb5GuOE3a7K18vLC+x2O1xd3cNut8O9e1cIYUL5T4aoXs6SASgxsBJ/CyEtc+aW9kX6ylDEZU7ZGfNcomMxpmWeVC8h69yxdT7X0wnZ3vDEq7lalqMUrG8HqyiW+20Tsn7OWOm/3pez3P92zlg9T60W0vZ5yGZdJ2Pr+yrp2PIqc8jWa9rtIh492uH2NuPzz494+IOH+Pb/9tvY3dshTnWu2CaF6sTlWu5ePckqwmqtE92xo3P6slj7MTrfuYlYfT1bSdnlnW8fcy/8evji3CnbwMwf6w7sJWN1mU+pduu4ROxWP0ZJ2eGxoW1jve9qntnunK9xPcj+xgKwc7vK+b2IV+3k5IYzVuJWJ2R7ydgc7RDNTTJWJWTjFJFyQtiVZzRexDu/34QQQgghhJC3g/54VYQQQgghhJA7YZOJQZW39bAKJC9ZfTtarKJpsy97Q7eOnFOEbBF3NRE7TUXK7ve7JQ1rE7FFzEYAfinrZVvqlnOUIZBL+0UC92SeF8/tvTVbJ9+HU3Xtvey/V16g99rcqj+W5rW8fX99orn/Hnupq5+XdpjrWh6X//oLU8B0NSHuYyMsu5eqpad+rrS4lKX0R/6nJe0iSnvnaeSnrhfqOXrn8/U3RWxQfV/vnb3+UV/UVdlzjq6rd09Hx6lzj5LC/j0w90Xvl+2OWF/vXee9NIJb1/dtuT7ofuvr8XV7Un30PJh23LPTPEvu+OY9HT2/+j75e+auZ/gjBPl7kx8XTAHxqgz/TQghhBBCCCECk7GEEEIIIYS8ALQIk221dy3bEml98SZCL7g63hbZdqHETJ0rVpKT0QxJvN/vcXl5sSyvEOMO5T8VqmQty579yOurpEMTQpjXvu52ZS7Y4/GInIFpOi59miFDBWuJrYcNDgFqyOBaBtj5YG3ZyXdqbU+Sq7q83LM8aEuOdaWhn9iVcrmO+v7oNKxfYl1vn5E6x3BPwvr3We5JCEBKtSzE0M4TG1spV67BSa2gn3Erq4yg8wIvtjKrl64dJW69xOsmcv0Sqh+yjtrPcySsSYA66dc8Gk3R1gGOrBKccny2/eklX7eSsWZ44aU9PwQxoIYIVvtG9c2QyOr97vZND2UcOgld2DbM+sZyvR59vCRvZVjkgDp3bYCdO1b6EuqQyjq5u26n5TpjScximUsWwCpekVEELMrcywCwf7RHukm4/c3tOaOdE0IIIYQQQt4CKGMJIYQQQgj5EnznO/fwe7/3CN/73oOODG3rW9Fq9ph9/vjeuq1fjvHH6iRuSa1GM1esFrL7/R51btheEjaoF1BNQxmmuMrEvIjOjBjLsSUlmxBjRErJSre179lc0/b8sb6eFqW1LT1ssR+euN9Of1+7XObYDFoY63ufu8f562mXImKDaaN3vJW1eZGybTlQROzFowu8//13cP+791c52hOnzdu8SKye4NTla6oTHZmKjXNIXxHsefW5B8fIeY2I9fJVr5u/C7Tb/jhVx74Brs4WvlpP0On2ZIhpLWf1nLAiEE/MGbsK1qwEpIjVbOd2NfPGuofNzOcqMjPXPui+67lj6+W0c8iuYle1sfYlZNN3Mz+svw+j+6j7qJ65Zm5Yd4/8S/qySlmZQ9YNcZxzLj9uyOWHDvlIC0sIIYQQQgipUMYSQgghhBDyJfj+9x/gn/yT7+HiYjISoxWxizhqUoGtbOtt99ss7fbaqOfSYi644Yl32O0m7PciYy8QgswRuyVj5Xr0BI+99YQYJ4SQMU0RKdXhkcu8p6HppxWbVVL69S36x9gk7Km2zknI+vOM3h8vVXtitb+U+6KPb58bWx7UUMUZMQakVJa7dy/w7l98E/FqMnO2evm53C1VVEWVGfK2I0jXNlyfeonW3nC1TRJWn1tLV9hzmfPD1t8SsZtp2M7f05DBrq6w7RSJxNT7RV4CVsyuUlMlXrVo7MlXOa5JwWo5qec7VetNMrZzXV7CrqLUl2kB6u+z9Dmo84Vs3i/fzuZ2cPdPP2cieUVou/36GVylccA6t2wIocwvK39HuSzjVBPnhBBCCCGEECJwIhNCCCGEEEK+BCEETFNQicTQSBstzPS2HA8jVKtV0kMUL7U3xKxtWw8l60WsnitWUrExljli69DE/hVh54gt6zn7fRHVZsgcsVG9bBq2XmN7PVvX2jumJ7C7LQ/q9YXqKaky6r9/j9t9+v21S3SXrYTtS/0Q9NyxVc7mJwc8/Zcf4/CzJ0UWKVFp3hclooaiVJ43v+xI1tHQwv78Wv6NpKwXwWaYYrht3Vf/Xrhyfx79tjd/0x1pt/YboXsPNl/qmpvEuLsHQ1mt+6nvO1Q9fX+0FPf9dutNfS3wN849KjPPgLr35yybPpyx7fszeg67fYxoryWinSt2GbZYhgCPlxH7d/eY7k8ghBBCCCGEEMpYQgghhBBCvgRWip2qh64I3DrOH1/WQ1OmanbaD6afkkyVuWOnaUKMVZa2crUnWyNyrus9y9SK4OD6UvusJfQ592Kw56x7+VVj3yu7XLaa+nYZOtu999W/glvaV76ZcfvTx5g/eTaUVr23QGRde52LtHKirycL12tCaNtcFwMpCzTt+raacqnrZKW01YhY1xep35Wwvfujr31AcP9r2nXHGyHq7pPvu+6nbru5f6rMiGfdb70dbHvNNcPV7dwzcw2+XNU376f0072v/j3syml//zryuLkX/lh5ptw9MrI2unPL3MuLkJ3uT4iX/MqFEEIIIYQQwmGKCSGEEEII+dJIKhZoZeBIiPYEnJdosn9LPuo643NpCdsmY3c7ScXa1Os48Wr7XoYWjgghwZgL1Ufb57YNvR3CMnwqAvwEmyH44YUDZBjfrSGFu3sCusMW+/WyLO3o98XP+Vr7ra87N3VkHleZG1bm6bRLfT/677HdX9uLsc4fK0NBx8UJlRR3FUf+7WqEqJddXm6h7uvWcfv0cqu+2dZiDLZfvb70pOVIxDZCUZfpNs1maMpM22fS1FWPjh7mV7e7DhOcax/80MHd4YlRhwVenzNk047vyzqUsTq27j4xXLG6h34Y4bVczTsbgpsr1i+DnTvWDzPcO48ZXlifWy31sMiI9X6vc8LGDKT6nOW4nDMG5FSHKZayOEWkXSrDFk+hfZYIIYQQQgghbyX8mSYhhBBCCCHPwTQF3L+/w+WlzKXaisUWa418ArKpHfQxI7nZO2bUTitnQ4jLUls5/8KJ8nO4m5R4OQ7jbtdkRcu5z4Ace6rMpR1DfR+1bPfPka5TlnCvYE/kBWJ7SeglG32K0QgvL1VR9/Uk7Xq8Po/rR+/4RqAG259TIrb3dm8lYb2oNv3r3CPTRqfPw7pyLn0/0Sn3fXbX0yQ9/Tk2RLa+l73nQ6eem8sIYftaXBsjWW/Or+6xbsu8t07ir+fQ92f0UtfnBar/YYBO0/rnfe2HzMesf8dCCCGEEEIIeWthMpYQQgghhJDn4Hvfu49/+k+/j3ffvVBirGB8zio/MUg4whxnpBnQtNlfdqwS9BCgIl97CdmIGCUN21qKkqy0id+aGg0YpU5V7eWYjJxlWY8pq1KW1+0XQQht2lWXlWusaVR/bLn+032xbfo2ZF022oryntf0rH5/9T2W9Gu/Xn3pZGw5TpKxMS5JbjWnrBFZul9oRZY/plfWSDEnq7rHunPoa5J9PiFrBC3apVlXx/tyf4yXn41IC2pf55hmn2dLzC2J1LVqtv3spWDXdKikWZe0q0nDSlp2ScUCLiEr+xCaROwoVYuA4bnK7rpP15XrWZOry/VsJmOXpZaiQE3vmvMoWbuWhWDqmvfBPadrEjfmko5dPiMQXDI2l3u9JmOnRRVPoZTvAnb3dsAHwHw9Y34yb7zxhBBCCCGEkDcZJmMJIYQQQgh5Di4uJnzzm5d4+HAP57AaRvurSA2bbWwL2NPn9PJXZK9NT4qNqH06Te6s5/UlAlaX2X3ZtaFae8FS9pyyF8Wp99K/N6faOac9//7W5yo0+/JtwvE3z5CeHTfabaXVUDA6iVqLlRz1gtMdP0wjqvN35RncPt1m7fhYwm0wTH7CJXiDLW/2jV79k6JJCJvnxQpgf11d4Yx6L3rS2b9PvdSzeY/1cYNz+XrdRDHstTbLTn96PxoY4vpprkcve+uDZ84kef3zLe9dVK8pIO5jEbaEEEIIIYSQtxYmYwkhhBBCCHkOSvJQEodjmWrlp17a/eqI5titPpy3XsUclNCooq53EpnX1Jfp9otMLXOgZgAJrXhNyDkhJXllpFRlrKRlzVnuIGJ7qVRd1l8PZ7e/HKnqn3fsSADrvtY6pc1x0liemdw8O34pz1iMeU3aAuU5TamkYm9/9hjXf/sY7/zFt/DgTz/YFoVaQOmyQRK2l7gdLbvDFGuxpSRak34dCTsvcVU7+vieROxJRn8fdD3dj5FcPSchm/V7rh6zUCvUtKyeIzW7dKra1vOpmrlj3XyxOoW7nt91OWSVktXt6lSqnBv2fAg2zevTqTodux6/lYyV+xZc+25bv89mrllpTz1niCgfXbKd1XqobUhadr3+uNzD6BKyImGniISEMAfKWEIIIYQQQt5ymIwlhBBCCCHkOamCyJbbYXxfDbZTll4A+sSrTrC2KdetV5Ww5ZVzckMVj5ZfP1/V23Uqmev394Rre3wV61XWAtqkefm/vjKAQwLS9r02Cc2l6SZtGWx9qecF21rfn6Mjes359Dm8ZNViNqDfl3N5XhHrzqvP3xuq2bxUO+v/egnSUOsM77fbNgLavYe67900Kly53OvOedbzuWeld05T3nlGTiZkg7tP+twundp7joy498+U7lPneWuOCa5f7r0OIQDR7SOEEEIIIYS8tTAZSwghhBBCyHMismu0ryxDp24wdfwxquTsftRztMf1uljnb11LoOduLWlXiYvp6JhGkrAzajJWXjOAhJRmzPMR8zxjnmclZfO6HCVkXwa9pO1WPb28e/K21rPtlfI28arfX7tfLFBNz9Y0bSmvx8cITJNNSNce+YcSQ1nWiCi0wmwzEavEV6+N9fyofWj6qNvQ4lAf0ztHR8g1IlaX6+3OH9Rz/fCid0i2/Vj/JuTcOgUr55XkrCQ2fUJ2uQafhpV1sw9uHllVLudb551V55U+yr1a074BZl7ZtY4Lmq/zwer7EmyddV225Rp1Elede32/O+03ydtg56Tt1TFtBjTHrQJ2ScjGKSKlVLYpYwkhhBBCCHmroYwlhBBCCCHkOdEJxtH+sgym7LTE/TLf3LfHFtGp1/UwwUWOFonnhxyOnXVNUvutjM25LFOacTx6EWsTsnpY4rv62F79l+t0zxWxdyMD+CQBzxIwByDHgDRFpJgxx4C0y0hzxDwnpDljnjPyMiQ05ozd9QEh6+dv/OwaWYqOpIR7frVkXbb18d1zoBWuQ8l6h3Y7J7LLQXn3Gnv927oPo3PDXZuQ3RDFcowZynoRjFrK6uGHtZQcCVnToCrzj6p00T++6vqlXX3eVbQ2l2L3rUMY6/dQhgSW9uAEMZQgDvV6dV3Td/XMrH0NMO2JzNZieR26WLqlz+XvsbSpj9MSNwbktNSLpWy6mJAfZsw3M/Lh5f/ohBBCCCGEEPL1QhlLCCGEEELIc3O+Feo7m7Du6wnb887T25c75TX12hOyQFr6kJb6snRRNHMOLW5rGlYvi4xtk7F23tishHEdwvhFSNWXKWZ1yvVF8KuU8UnS8mcRdTsx7Yu0y0CW+3vMiIeE3bMjQrI/BmiGB3YC1ghT1H1aRp1qY00MqqURVy4F26RXYdeboWvdtu7LcNjcHqNjByK2K2F7UvnEOU1deRv1PK7iGiWJqiTqKjelrZGQ1SJyaWMVkVp++m24cmkDraw04hWqb9JPlSxdU71yT1U7jYB1fV3rw55L3zcjfdW90PPnennbPNsIJvnqE7CjdS1k1/mTY0C8F7G/2CP/JmM+zOc9H4QQQgghhJA3BspYQgghhBBC7sCDBzv8+Z9/gG9/+x5ibPfXIWvr0LDC84xkepqeJFUJr1zLtHzNOWGeE+a5JFd3OxmGWMtYPzaojrPpOrNaioQ94ng84HA4GBk7z37u2CqGtbT8KiTqqTZ1gvjF0grZnz6b8ZubhLzoITm3FtMZATkCCQE5ZhyuAna6g7ku12SiCNm0tJUywpxxjA+AtKRlc0nL3uSM+W8fq+uvbezu7/DuD99F2LUS0s/72R2m2MtSJTN7QyGP5qLtzU/a3FovSV3dk3Or+r54Eev+cBsR22vneZDz5UUu+vSqCFcvGkcJWSVrjaCEPbYZnjgrEQsnMaH6GFDTp9ImVN/dNfnhg3W/dV0vkE2aViVjpa11+OPecMi6j+r+aOnctBuUbIYt90M4GyEb6301QjaG0rfUeXYIIYQQQgghbwWUsYQQQgghhNyBe/d2+NM/fR/vvLM/+4v1c4YyfhGsI5mGtrymTfM6X2tKaZWk05SQs8wNC4QgclYa8+a5l4ytwxPPc5GxPRGbluFzJR2rhyi2c9iWpfGPd5Clemjmtu9fHRkZKQMpy3o2dysD+OVNwr9/emyGBG4IAZgA5LLcafG6XMo6hK1OxiqxmnNGurdb5Wx5yzKOGTj8+mlN0S5DG+djxuU3LvHotx+VROEiB/W5/DDDq4CFuxbn8U160KdZVf3enK5ejjZpWdVYT7LqY5s+enri2EvXDbl8J/zjKKJVxKafl9ULWdjEZ29eWH2ONamqZeOoH6rcnEe1Vapke90dMWuGGu5cq5Gc+vwiWlX9pq6uL+fyElZJWp249c+ofjZzyN2hnrWYlfX1mdWvWF4hhfYjlBBCCCGEEPLWQBlLCCGEEELIHenNtyki0e77elJQcl69XZZ5Kc/IOSxzwwbMc0IIIkwjQgiYpglAxjQBIYg1CCiS1duKUTL2uAxLXBKxt7e3uL0tQlakbEoiZSWhm5UstulYfS3PU9bbPxa0d2N0/DFn/He/fobPbhPKbZR03nLc8n8/jQHT1WSlozmBLJy8cvK19EWJV/Rl7LpMajvVpRa1aU5Ic8LH/+rjeh51PALwnT//Dq7ev7ISarncdTmSlb0yEWBtxa5sbQR2bxs4/SfoBJ4us0OHn5CwzeXc4W9fXLd+qNSfmp97VcvJZlhidX6TrBUJ6eaSbYYr1snR7NKxbtjjNU0qaVB5/qBEpfTNp3XlfV3a0MMI+/TpKo+dLB0lYP11yn3yxzRDIQd7v9bzuCGJR8MUr8nYGNalvBBVQpcQQgghhBDy1kEZSwghhBBCyB1p5sp8BfBCVpeXfYsGVEMV1/lbS0I2hIAYp+XIpK7Rmy5AErFFANVkrMwRW+TrOXPF2jlidYq3sZCD9ecXq1/OyM4543aWoX+L1LyZM37+9IiPn82Iu1iETVxkopZ9AWX/ia6tYkl3Vy/zYFv7clkqeWbW4yLnlvcmhghk4NlvnpUyec3lhQDc/uEt9g/3yFPZjrFe67SfsLvYNUnBZs5YVJEm+CGC1Y6zGSVkvSA9V6b2/tZHx36Z4YrlPI2U1c+CTo1mW8ckPU0Tdojgtf1evVESVEngpi0tMGHrrH3Tchho//TCoGy0r4NP6a6pYH8P/Xy3fk5aEd+qH34u3bVfSgb3krHyb4UcK2J2/bskhBBCCCGEvBVQxhJCCCGEEHJH2lSsNgUvRtJauapMx/ktrMeUYYmBlIoBmOcZgBIFS//neS4yLk7Y7eT4iFbGatOXkdIRKSUcDreY5xm3t7c4Ho84HI44HA6Y5xmHw3GVvlUGeyFb76Ofv9UL176A7YnZ0xanJ3/7Qybb9b/54oD/76+eApJ+W4J+17uI/YNoBQ2w/Rb6bkrKUJKRvW0ZMjjXbbMvAzkucm1ZnkrK5pzXFJ9JzqoXMvCr/9+vEKdohzieM9Ih4cM//RC/9aPfqtePgRTVMqvurFLPz+2qhO7arh/qWD+qG8stCdyds1bv0+fqXdNdce/9ek55z7VM1MJ2EY6laitIjYT1wxUrsWrmnVXXsKZHvZz159GCVmSn3BN1DTptuwpPLXD9XK7mhDDSV8/3aiSva8/ct6Xueg6dau1I1PU82QlctV/q9I4NoYhXScfmmLF/tMd0NeH2k1ukWxpZQgghhBBC3hYoYwkhhBBCCHkBeHn4ZYKzo5Tr6XpW2nrJ6NOoKWU1ZPEMIGCaRMiGZbji0qaWUnpI4ZzLcMcppXUo4uOxvHQitgxJnDCSr+fel68K27a9bykDn9/OOC4JWGQg5YxPDgkfH1IdinQVMEUldecn7Z7c1jGibRFQIoWabS27chXrq3BK9RgRqyGUFGxAlbRG5MLOE7teg7wScHhyMPPMpmNCPmbMtzOuP77G00+eQpKAl+9eYnexW/vVozt/7Eb9czj72A1JO2zvRYlYfVwnGernh91so7O/Ga4YMD/AuFs3B/PPbvXPXVuvDam3ymHdZq+97EQznJBW+4zEdW2Y/vUv2MjlRtr6a/Q/BHDrIQSEXUBE5PyxhBBCCCGEvGVQxhJCCCGEEHJH/PysI69xrlBd1mDNRUBj6eyRAIIaglgSc3VfCGU7pYwYyxIQ8VqFo0jSaSpzvk7ThOPxiBgjYpyW4Ytr+1XmzquELcnYA+ZZS9njKmSPx3kdGllEcDtcMdZlFcg+eWzv3Sg9e45rGtXx7dzOGf+vnzzGxzdl6OEQAMSAOQO7ezsr8VSqcFP0ZFlksw2gph3V8MR+uOJ1OythJ+tSrkXSImNDXMRrXISsiFiVnvVzyYYU1qTsum+qiVgkIEwBeZ8RLyI++8ln+OJnXyAdExCAP/6v/hjf+N1vNOnBkcQMWv6P7qETYiOZu57CtzcSsL7NXh/0e7zRx7uKZPNeqXP1hKwZIhgw6U0/f6vuj5m71c8NO9rWgjS7c+faRwQlRHNHlnaGDJZt31fX8bpU6VTz8ajeR5+41efWc8U27amXScSiPguNLA6wSdig/sZ8MjaUvztMd382CCGEEEIIIa83lLGEEEIIIYQ8J6eE37nhMy//TkncrflhvSgWUatFp8jQEBJSKkYhxiJoY4w1XRkCpik1c+SKPC2iNatk7LwORWzniW0FrE3r3uVejSpmVQfd9XP3A8AnNzM+v5mRAdzMGV/MGU9zRlwTpEW2hBiGMm8TJ4uMlJL0n0g1JdDKlbaJQLO+JF8BJYbQyqR1X6op2vV4kVCSVsz6wVJLkXGLQE1Y5iB+OhdRm4Evfv6FFZgZ2D/c49H3H50/9/LZ1e4muYbzy7p+efF6log9Q8b7Y7pS8pSQHR2vniOTmPbn0L8BOYFPpPb62/wIQPXF7Pf7nFg1zyra6zfrW3119Zp5c0fHisB1onuVyvr40Y8DXHm3jBBCCCGEEPLGQxlLCCGEEELIcyMpzuf9Zl0biL5MFSTlqte1aJXympjNS70qHkoyNSKEpMoypimtQxOnlBYJOyGEiGmKS9utjC2iNS/zwFoZKxJ2ntMylHFaj5G0br1GK2blvp5OrtY2esMMr0HSrNtu760uk/L/8aNn+B8/eoYwBSAG5Clgt9vZFBzQJvPOwMzPabvcLmXdC1BfN7t9oVM+eoX+UqSVmR90SdRqSZVzRgyxJm9TLsnZJUn70//PT8t7f0jIKWN+NuODv/MB/uL/8BclKbh2OVhR5ZbrvLFLgZlH1rPxVvTeJzNPrLFq54vYkwJ21D/jT1W6s3GmHSHrynqpVRH5wzljvfgMdq7YZthfdx4jK52s7V6LO8a0q48Prj/LjxPKLVM/MHBytEnIyg8bpEzdfy1dm5fe794z/V43cnZ5ibhdf7QRg//IJ4QQQgghhLwlUMYSQgghhBByR7zI64nDcxKug9bR+7a+tnf623ypK8I252IkZFmHLU7qqBk5RwBHhBBXCVfkrRgGK00l8VpkbG7SsDJXbDskMdZ2qnjNpv/2fuBEnfMZHffZ7Yy/+fxQ9gfgo0NCmhaJsgw3GtVQuM2wuOe810qgrfWVXFrbXfqg04xm2w1D2wgsn4QNSqBKCnYZjtjMHyviLbjyZbhaRJQyEagdGbwOz6p/VDBnWzcAN5/f4D/8d/9hPc97v/ceHn3/0fb9e9ESy0vfu5zny4jYUVtOyp6Slbq832wrUc9e3+qjEr0jqTrqj36O/XWV5nN7jZ2+eBksx47Oe2rIZo8ZplhkboYVt/q+QH0maDHc+5ygjCWEEEIIIeStgzKWEEIIIYSQ56RK2PPiTjaZWeToaLhhwMvc9hw+EZsXKVHnkK0CVs5ZZCzWtrQkLcnYMkfsPMd1eOLSflDnlXZqQlZLWUnMahEriVg/N2ybVtXS5NyEbN1+HkmbM/Cr6xn/z3//eJnjMQIRmC4mK1RU6m7lLmLF903kDqpQ0iLWi9l1O1dZJFJ2lUXZrS91EJfjohpyeClbl/Lq9Tm79cEyhriKX5G/KaR1TlHsgDAFPP3NU/zV/+2vkI4J6TbhT/73f1KGLe7c0KZs65533hudYm4SsE6qbaViX0gidgsnMofDDjtRuyUve230hjzu9kFfU08E+0MHqVzTljvcCNqRHN/qX6e9Jikcst0vf3OyLoJUvUy9oNr0fUH9fDDzy4Z67nWYd/dZQgghhBBCCHl7oIwlhBBCCCHkORgNebs1/K04Ib/u69oEbE/ClrJ2fti2H1rUynFVyCaEEAGk5ZiwtiOJWBFZvm0tcf2csLLt1+U+1Hlj9fXYfo/Eam+I4h6npOztnPE//Popro8JCAGf3MyI+4gcUNOwHXnSDIl7rlTp+XqdCFwvfGlWhJne1qJL6upXqOs+YbgmXFOVX9LmukxKKmpJG1ASsSJU/aPppay7PxFF0MYpVkG8lMv5PvrXH+H47Ig8Z8RdxA//8Q9x9f7VmTdXThW66926YVDXrPbla3POM56Bbvqyd8O8kHUp6EHj7bym/lnBedJ2OFdrp+5WmZ+ntrleEZ7rBxXa50dL0VM/snAC1dwHVcf0tXdO36y7l168jgStEa4ieEUMR2D/zh5xH3H84rjO70wIIYQQQgh5c6GMJYQQQggh5I5IgrPKxZH8a9OvveGLtYAczRmr8YnZeo42HVvFbW7aSwkIIa31tLyIUaW81Dn1EMNSt6ZerZRtX614rXPXlv6PrvuU8B7VrQQjhG8WGfvJTULYLQngXURwArY3N2xZ3DHa1hGxRvw5+bomEbWk1ft8unG5lz2ZtspUBOSYTcrR1ItLG9FJWp3aVf1Yh0N25c116mvNQMwROeZy3mUuzU/++hN8/G8+xvxsxnQx4dt/79u4eu/q+RKEp5KVX6YNL2tPtNedzxawIv6kEUSTBj0lKYdDFLs2vcAc1R8Oj3yXurLpJemp/ssxcr0iOkW4or0P/vrNUMb+fkK16c8FV+6vSx8bbDtyjE7GymfK7sEO8TLieH2U38IQQgghhBBC3mAoYwkhhBBCCHkOrFi0CVYtV/0xy9oqTVsrUOu2HscavJp+FfFq19vjtAitw2fmHBBjMQUp5SUZWyRt0CKiEaataO3L11bG9tvyfdai2+736+cMT/w//Oop/ubzWyAGzACehoB4Ec28sHqOx0bEuvVTycmusPX9zFXGaekpgklkpxafa+LQJ1tlCSVRc32vM0r6dZVXy2tN3eaNNkXQJjtXp55bM+cMJKx1csp1rtpchytOqEukZWjjJTEo9+1f/zf/Gg+/+xB//F/9MS4eXPRu7gthlKb1711XvG4lZk9NGK3+/Bspe47g7JQZQas+knrDEp8zL2tvt0+8bgrfXrmWwJ10apPm9j8YWFjnQu70xd+HNTWsrz+oNp2c1XMzd9PG7kca/j1Y23Qvk6qNOP2MEEIIIYQQQt4YKGMJIYQQQgi5IyIWy7p8eR+cIMzrl+16eOCyXSRnX7j2ykcStn9MXdeC2AtZa0FKSlauI6zrw2CfEavF6qVUt/vyVfffzgdrl36u2DNMq6Fc25wyDnNRJTkDP318wL/8+BniPq6JzDhFK2GDE29+u3Oq3pympszLKF2mt33acPx2jS65vnplGXXoYbW/Ebcyz2znleGSsrqvSbW7rIewSNfshitOGWGy9zUilnOkjF//q1/jya+e4Hf/ye+W9ygAcRfLPL4vmuCWbt9Z89ieqL957p4g3RKyp56FzdONpe7mEMXuOdXDIjeSU/2xnyVsXb1NUexlbu8c0s9eWy61OkroDqXzqE/BvT/Shu6X/kyR+WRPtU0IIYQQQgh5I6CMJYQQQggh5I7UFGhPJlrLJmLUL21bQE1+Bvghhj11SGKRBrXtst8nb0X+esNX+1yHI/bL/vVL21aeajnbE7Y92Zo793B8zt49X67aXVfAv/v0Bv/vH3+OsAuI+4hPbxN2V7tVAoa4XGOsKbdeGtYk2LRsWxd9+TaUclml6RaxpLvuhwZeBehofTlmbXJZ1wnYtWwR5b5Mi9i1LfdaBRyKMG2SsjGUJOwiVKVODItkXdKzOZZ9Uj/HjDQl5LmWIQC3T27x3/9f/nuEGJAOCd/5i+/gT/53f9K/p6cYSPPee3Uqsbgl5+88fHXn+HPmkv1S+89Z14eeGFa4kcQdYSoCF0B7fVI/WJl59q3UP6JQf1u9fq/DdufaL91GI02hUrXy+RBQy/Q9CG3f9TDFRsCGXD93CCGEEEIIIW88lLGEEEIIIYQ8J60sFYkq+2tZkY5BHWslqG23LPtOyJ6zn4LFKnTlmJrGFTkr/dlOZem+eB+zJVqtZO3J2eEZu+ffPkYo1zPnjC9uEz56esTfPr5F3E91OOKpJGK1WNFDE2uJorfLamj3u/Jur7yEUmLKdX1NJK7bQQlXte7F17r063DbJ17DZGxa+haVfJWhXnVSFksbUGW9tv391vUDEFNEnjM++/efIc0J6Tbh3vv3cP3r6/WWXb17hd29u/8n7ZcVphsNP/9xWW+en5bsDlXcOfacYY57fbnr+c9uS/1W5Owhk3vi+Jz+9uqEE/s7+0b3a5i+7cndoObn1uKZEEIIIYQQ8kZDGUsIIYQQQsid0EPwStIwqO2lVq4CU156W8/t6uVtEX1aluol1nU9LLEvtyK4XR8ldrWsPXkn1nN4ESv7+/vGidjstts2KyODEvCr6yP+r//2U1wfE/YPL8owxMuwxOvcsAh1jlIvXiVl11mW3Va+niv3fAIWnXvlk65aUq5JV3fMKjuX/U0iFihpU9VuNyGbcjk21bL1FUvfclrSszGvsniVSzmXemnZn2Dmk41xScguyVi9DFNAnjNSLPPIhli2wxQQ54g5zvjVv/oV/tv/83+L+XZGOiT8/f/j38cP/tEP1O2s8+puviV3SVze4ZgvJXl7QjbY93st9+Jv6EKfb17YcfU7DKl7ZttnD708ELHrfLi+up7jNdcfXJikrDrev3frXMhL39b3Q/dbtoN7Ae3z4uuETh1CCCGEEELIGwtlLCGEEEIIIXfASthxnbK0VsgO71uHF/b2aDsZ68/Tpmtru75tfx6RaFiSsoCI2Pba+rakFbJWeJw/FHHvZp6yOSqdGoBjyvjFkwP+w/UBnx0Sjijzk4YYzLywZqma0dt+uOaukNU96Y7nfKL7wCqSuiJvlASEWl+Wa+q0d8wiUwGUuVx1eVZlS7s5Lm3JHLCqr5KMDWkRVSJpoY6JaJfSp7S0r69X+7HlGkIKdRtlvth0THj6ydMiY28TPv13n+Le+/fwjd/5Bvb3921fT9iuc+TpnUT768BdBKx+1s6qfrf5T7/sfKl3SQGrnU0dL1pX+er3y99caOuYU6jPDj3EcdOP1+SRIYQQQgghhHx5KGMJIYQQQgi5I1rIpmXOTJ2QLXWq6JR9IjllyGCpl3UaEuPEq7dx8qW/Pq6cF2ZI5NbiCdXMZEmPuSSqlbq+X3VffzjhVsKeW3ZqqZHrfXJI+L//+HN8ejsj7wKmGDeHJPbDE4cQVuE6lLGALe8wEo0+2aqTryaxl/ttbCVjzfpWMlYSrToZq1OuyHXe1+jmhRVxu9wPZKxzzPokYjchO5cUbJxjLV/q5HmZR1a1o+eVDTEg7JZhpqeAtE/4n/+b/xn/y//jf8F/8n/6T/CtP/nW5nvxomTpcEjqr0OsDUTq1y1B73qOZu7WM4SwHO+Xm/X1nK3qI3OUJu5J2ObHDK6v69+rvjZVwSR9YZ+/9fNGZO6pX9sQQgghhBBC3hgoYwkhhBBCCLkjephiSbmWctmGEbP1OLs+SrDaoYLHX9j7BK0fctjVRp03Vo6rfa9DFMs+fU3j+9C/vuy2dR/aun3h2qvrCfj3n9/gk2czEAOujwlPU8YMtMMS9ySsT7uGYCWsFrCubik6Q6YomdPU76VYZV3wHn2QjF2PzZ02FzkKoJ+M1cfo84p4jWp7WZf0bE6LKJM0rTo2xyJyEWBStN3z6etehikOISDlVO59Cv9/9v4l9pYlu+/8VuTe//O4D95ikeJDZBWLJGTBgtSSjG43LFktd8ttoDU30IDhQU/sqQf2zBMPPOj2yJr1yPDABgy020DbgNW2G24YoiXZkommRFIUyaJYrFuPW3Uf5/V/7Z0RHmRG5IoVK3Ln/p+z7+Pc70f4I3dmRkZG5t6X6K7fWREyyFBdF++jHO+P8qPf/pHcPbuTX/rrvyRXT64uH4x6/1mu/6f6IA+q/HyNtpf0JsfhrpPrTOnsnbNh6XywGw5X9/KqW9W2VMLaZvY4GSwAAADwtUMYCwAAAJxpqXat14vNgacOEZc2ulJWypqstr2IXTO23FXq9E1ErzurEzi99qtWT0Ws7yfleH2/0+/Btj9VIds73gtiW1UaKv/4B6/kd35yI8OjqQo27IIMV+sVsSVYHeqKtZNBrP3cjMw5rr4GrzLWVtB515cqvKqCut7Pa6Xm6YJ1mzyFsa5gzftJ1JTDQcoar6VSVlXQlgrHHMzqZ9H7+bly8Kv6rcYVUj2Na34Fg8ggg6SUpm2u6t2lZf3f3RS2x2OU3/1PfleefvOp/J3/5d+R/S/tp+/0nO/oq+KLz1S7Pq9qWwnL770KSXtB7EPDbOcfR+jqW1sR24S7eWuP6/8T/hX+KQIAAAA4D2EsAAAAcCZdGTuFS940xXVYWv+1Uxrn/2V+CXbFXU/Wts/hra6GXSpklymL1ytiRfWV+ziVFCxBRL8CuB/QtuvK+kHs8nkZr4jIdz+7l3/1/E4+uhslXA0SdsPUZK6otGHsdK0JXYclfK0CWFsxaz93rK37WtYw7V3ey4aSCo/y95fqfW9btVn5a9bGTOoaUe1sf/a+al3Ysn6t1Mfy2LuVsbMQg8RdnMLcnYgEkSENS0icKxtzQByTjIdR/vD/+ody9fRK4n2Ub/z6N+Q7f/s7by7wav8zfLO+xEGrdcng9ey+ncpzb0pi97vrhbg2aM3H3DnSV8alPzsBbRiCXH3jSuJtlMOLw1fqNwAAAADgPISxAAAAwBlyWBpjWxlbT1O8nFtC2aXUqq6QXYJYHah60/zmqtbcJh/PgawNZ/M96nVq832X0NVfp/b8d6OvPadKdksQu/QQ5I+f3cl/+WcvZNgPpRI2hKliUkRKGFsFq/PnEsIO/TVimzVj80hOrPOYK1Sr8YZUgqLVyswcNJpj+Tq9Lqze10FpFajmtqY6tvSbTP82vM3H5996rnbN9yvr0aa5ujYsFbprQbGIlGpXu47mfFPJFctRYtnKOFXMxhBlCNO8y2lIspOdxEOUP/jP/kDiIcr9i3v59X/71+XX/tavieycd741WD03gH3Tge0lwrmvYOD3JsLfUlXb6adMy67Ol+mFO7feup5t1Z/+Rxk7kUffeCTj7SjHl0c/7AUAAADwViCMBQAAAM60BLEitkrWhrN6muI6hBWZwsdggsh6Otl+aVeuYl0qbPXUw/ORqr2uLrXVsF7wuZY7elMUt8e3h7D2Wj+IDfKnz+/ln350LT+8PshwNciwG5apa/P6sKK2qjpWb7vrw76Jqtiwcu6E0rZkpksYpKciXg1kc/jaqXRtwln1E8vrwOrq0+o6FcLqMVehlbqfnkJZP1f+HnIoK7KEVWV64rBsSzg7B7F5Dc583zhE2aXd9HtIIp/+yafyW//hb8m3/sa35Dv/9nc2v//N3mTw2sngNgWQr5Pfnbo2bWjzRelUtW4+n5vpKtj8m/R+33Obqvo2+G2acajzJeDV/82d+AceAAAAAL76CGMBAACAM/lBq10/VppjOfTU17Z95+PJ/I/0bTBrpzPW0xF7//u+nrZ4ua7tRz+nnzi1oUM/hM1ja8+dDmLrY2NK8tHNUX77J9cSdlMQG3ZzgNpZI7YEsyqAbULYlWrYpjJ2SVfWXkfd1rbv0f3MVXwhLWGwpLnKNh+zPwmdwefPUe174wyqTZyP5SmH1dTD3WrXQY1LlurYJGn6TuJUvWqnLZY4XzMsLybFNH1XKkzWIbHIsi2f53NDmNaWTUOSXdzJ9cfX8od//w/lyTeeyLf+xrdKcF8qE/NYej/vh+ZjbzKktf2Wj9sT0ktMJ3zptWE3y/94wBnPOdW0Xlv3eud7Lf8QwWtz4neg/28RAAAAgLcbYSwAAABwhjxF8fSXq2RFYhQZhqViNsYplRmGOrRdwlivIrauWl3CSpXGNZWy9nOu7lr2varaxakw4IzgZ0MAu3zeEsKGsv3R9UH+b3/2Qp7dz2vEDqH8rVW/NpWw9pz42+muoXo93QrXXuXsmRWyqU7cqtCwVOalHMQ7FbFOJWyu9MvtbFVsSFOYVFVjx7kvNT1xrpjNn2OI1bVDGOoq2yjLTzMu46l+kjoIzlWJuT9Zql8lTdMTpzjfZ0xLZexcWZuGeTvO72U3fcff+39/Tz7+lx/LX/73/7J86298q/fi2/+s8qlU/6OIXpi7KeT17vsGzlXrB2+4pqr8tNc9MGddDT7f0D222hLEnjX1sfdd2mP599z7Bw/5z7bVxwEAAAC8tQhjAQAAgDPVUxS3lbGmdal01ZWwXjiZQ9l6jdk6JdJTEZd1Dud2S6WrXTd2Oa+rZqfPSzqmx7S1YMt/3n4bHQStBbEpibw6jBLTFNJ9fDfKv3p+L2OYg7ahDVh1qFeOq3ZeRawbwurKWBPIlmMr3Gq3DZl3U0mbs8igKkuDCghNRWy1HuUcyJapUXUgK+22un7ohJC50lX/9kKq31FQ91Tj0cf0+07DtN5s/lxC5Vw1m5Znye+pBM1q7V8RmfoRKVMVp0dJrj++lhcfvpBf/W/9qvzcX/w5efIzT2T3aFc/71bnVL32/h2EPnfq2oe4xLVfkkJYa7V6Na2064WmW+5xRnjaHZ/6jVdjBgAAAPDWIowFAAAAzjJVxY5jMhWyU2VsjNNUwHGu2IsxzMdzlex0bhjyNUsIuoSyOoCtt1P/OnHwPrf7IeiQdD0hqteQtdfUJ7wphb3jOYQ9FcBmt8ck/+nvfyof3Rxl/2QvRxFJ+yBDmKth1dTDVQg7V8oOYaiC2aYqVqTZlpDQfi7vpZ+aNKGtc/1mqhJWxKmGndLNpSJWVb3mdquVsL3jea3YKKXqNZ9vqmSdithSMRvnoCnKMt3w/E6an7WqjC3TJOdK2PIfxvJOyzYu1bIibYWsnr562A3ye//H35M/+vt/JH/zf/Y35Rf/yi9u+g66/5l0/jNzw91zg0wVrHt9rFa1evdNzrGH0IH4A9pvrUL12vXehbYWfPaUa7x2zrGtUxd7/3ij+u85rxub6n8QAgAAAODtRRgLAAAAnClGXR2bPy+Vr+10vUtIllId6uVjujK1XsPVk6r2uiK2roD115HVVY/evfyAdWOY0glh7bnuZxH59OYon92N8tPbo3xyN8puroYddkMVxJYQL6jqSDtd8aCqXx8wPXEvUG2CWRPk1qf84+WBy8elUk6vjVqmKA4qhFX96srZXuVrqUyN/fN5vVYdjpZ7zVWxZTzmnU358HyPYQ50TfWsrqItfehq2WDOmWskyLRG7fze0jCdO1khm5LcPb+Tu2d38skffyJX71zJB9/6QHaPd+73Ue6pv6O1sOycitk1J4JY/5LUDT3T0mH3Xg+yobLXHceXyWt+X2dNc3xqDASxAAAAwFuPMBYAAAA4Q0oi4+hXxub9tjI2V8OGEuDmdkuoqkPZpQp2GLxyQjHHxBz39vUxe9yee7hk0li/+rU3lXGQmJL83//kufzBJ3cyXg2yf7qXYT9MQeE85bAOY/N2GIZlK9JOYyxmSmNpt/aztz+Nsg4hbdi6Gr6uaALYks2aENZbN1ZXkaY5LNKVsFEFsrYiVm2rdV/ne+uq2FI1O/+/HBxXa8jGaU3XXB1b/UztVlfEhrZtVSEb6r8hzOvI2grZtFTIyjD9BQkSj1H+yX/8T+TpN57Kv/sf/bvywbc/KP8Z2WmZyxjtf26y0lakTD17dlX0hiD2ZFVs7x3bfu0xW/Vq93V7206PPSV/XN54vXue0As/14LRpir1oSGq/l1uaR78+1T/+CCpqYoBAAAAvNUIYwEAAIAzLSFskqUqdlkz1q8ODarNsqbrdF5Xpi7TFOs2XiC7rC0rTXtbFav70Mfqqtp87LwgyQaw+Zna/dSc059//OogP72eqmHvRWQ3mCmJB1P1GkJTEVuO2+mJxVTHitTHVXjmBrC9kNZWiJYGDwjkggpYc58qpNNru5Y25pzup6p8HdbXjLV9ikhZO9YGzraKtlkPNlfR5krWXGFrA2x1rQxzkGrOlX1VEWtD2aZCdrdsQwoypEHSfmpzvD7K3Ys7+fD/86G8+skr+aW/+kuyuzIVsqbS1Zt+2A1knWs38YLYTpvNx8/t51x2zFv6PeMZmnfxkHF/gTmn938TG1TGAgAAAF8bhLEAAADAGVJKMo5RjscoMe6qitgcyuZjIktF7FIZa0PbNFfKTv2HMFXSTveazrfleZ2SvSYJWsoPlwC2Vymb778xSKjeydrxNoC1+/nzP/3hK/mHH76S4clOdo93S0XsLqyGsdU2V8qqNWNzoNebonh+Q21lq3pF3jU2pCyXnBlmazkwlfzqQnu8fNaVifbz/DOpKmNDWwlb2ue25tiUh6rjuaIvV8vKcu9SMetVcK5s9Xq31TM4FZ7TjaT6DgYZlqmag0zhq6o4LNWqQxBJIuNxlH/09/6RfPM3vin/3v/635PdB7slwM5TK+cAdn7mMkY9zJVAtppy2v531uSMp4NHr2LWHuuFufq34v1uVtenXem3N9at50t1tW5r/qoxJKnb9u73OsFwxxuZltjp73X+bwUAAACArw7CWAAAAOAMd3dR/uiPXsjP/dxj+c3f/Jk5hJUSqMYoJfis15QNZptDyOBUxubjXvi6tMvHchXs8nm63q4dO51fwk/d1p57iLoC1x6r99377ILI1SBhN0zVjXpqYlX12gSxTiVsaeNUxk7PWm/tZxET0HYqYatrvDBXHXfZ96PWf81fvZ7WdLqkrdQsYWIemwrf1ipjJUipYk1xDodi3XUaUlmHNffpBdNuJWuukDVt8n5TIZvb5Mpb/fxz5Wv56Zd/tFBXxubjYZjWGY4pTmsOzxWyIiLDfpDb57fye//p78k3f+Ob8mv/1q8t1+t7eN+X/fcOJ77fs0I8L4h1wu0mnLTnTVBuP7vTFs/XnFuVevL57Bi2hKpbPPDa6h88bO3nnHv12uZ/IBHq6YnPrqAHAAAA8JVDGAsAAACc4fZ2lN/93U/lV3/1XfnOd96T3S44UxaL2dogNpTQNoevMQaps8BlumIdyNYBqh/I5uttajSFrV7wmpwg1gSTq0FtfWK9CrbXNkjYDbJ7tJOwn4PWXkWsUw3rBbPTuJ0Q1lS0BlHnjN46s15FbXXNVrbSUvenv0Kby+eKzVydqY8n1XdvX1cfzkGtDPOxeXriEFVYm8/loHZQ/XpbPU1xPqbHkXPV/MxRynTDpZ2+r6qSzGvAlleYf9O7UL2bclymIDZKlCFOFdO7xzu5e34n/+Q//ify7b/xbfn23/x2qZwtY5N6PCnU1bKljQ7PX8epILbX1laOdrYnq2tPjU311wuDdVV3SnXla1MJa6/5MnnTQ5r/70wK5r2p/xYAAAAAvL0IYwEAAIAHmKYrTtX6sfkvhFCmHc7TFecpimOc0qJhkBKq6umKUwoyDGk+3iZwdeAq6lz+LCv7+pg93jzh1KKEZt476FxZHU8mjG3bfu/lQX7vszv5s+vjFMAO7bTDOpQdhqEKYSWoaYmHINWUxF4Ia6o6vWC2atO5zrYXafvbZA63SojrfY1OEOtuRZbq2l4oK+Z4Dk7NtgSNOpzdEMqmmOoq1vyMaQku9Vq0KaQShOYplSVM4ayI1Pc1IWKQsISu+b+NeQnYvHbsIFN17BAGSTFN26vp/vsne3n2Z8/kt/5XvyW/8t/8FfmN/+5vLNMR2+9FTEAbzFj0f5tnfPe9493pgzvH3fNb7peD7nyt3ddb9R9ws16sDvlP3de2s8/7ut5kmPrAvuy7s9701McAAAAAvrwIYwEAAIAHmKpZk6qKFTMt8RKu+hWyUoWwOZQNwQ9el6mPp/O9CtmprVclK6WNXj92yplCFZLqws4t0xb7bU6HsElEYkryo5uj/H9/cjsFsTqMNRWxJZR11ojN1ZLVdMKnpiheC2TV/fQ1TSCrz5UD+uN6KmenIC7Xq3Aq9+Ht28/VsTCvFWvOVWvHSmgqYr1nFJF6uuLcNrYBtQxTIGtD6zCEqQI2LBXASaYgNkV1bJh+HO7+oAck9fTL+R3OFbUlkE5Swt5hN1fI7qeOdo928uonr+R3/5PflbAL8p2//R0ZdkP57Zkvq/13Dfq/FfXu3eDbO+5ZCSa9itTulMWi+vHC3FNj6QSrq1W40h/7puB5pc2XghMib6b/T/XWsB4AAADAW4EwFgAAAHiAlETGMVV/w5Bkt5sCzlwRO1XDSqmI1evJxpgrZkVE0jx1cW6XK2QneVrjaTrjpKY1NmWRTerThrIpLVMiLwFw/Wx1vuglB6pCzgtsnPDVXvOD66P8gx9dy6eHtExNbP5EpJ2W2FTONhW0KoQtYa04Yay3XqwNYE0wuWWa4tLPlsSl9zWFzufe1v4M8jE7TbANk4K0lbGirtXXnDtdsUyBaJQ4BaJzlW01FXGSUkmbA9wqILZjt6JppwKvZirjOfQewlCtkytB5Cpeyff/0ffl7/9P/r78pf/+X5Jf/+/8ernWrsVbrY1r3qUNzJdOnLEb7pTENuQ079gGsXqt2HIubbu2+45zHyv/MqO7zqy+fx6Xnr7YCTbzdNRlWmp723PC0FPPt+U3tvW+vaDW/vdm9wlmAQAAgLceYSwAAADwIMlZKzb/LZWvSQUM9rheW3apTl2qWm21ag5Jc4C6VL/20jtvP1873dOuBetPS7whSWqu0cfqEDYmkbsxyad3o/zR84PEQUo1oq6InQa0/HXXhtXnzfTEOkx1w1f12b22t1asTqu94HVjwOKFdim0gZ6tgtXnm6rYuY+qGnZeW7apkp0rYctzJFWxKss0qvp49cx5vCa0zt9Jnua3qoCd9/U1ZTrgfA89JjHHzW9CgpRqXD3tsZ7eWAf7kmSqwJapEntIgwxXg7z88Uv59Lufyrf+5rekVNrm/x7U+8/fU/Mdq2PdUNaxZV3YterXph8vBFzp2w0Rt1xrw10vCBYnqF3tum17alrmTd3bd7n1ujfMTk286R9sAAAAAPjKI4wFAAAAHiBGkcMhym4X5HgcZLebKmPHcUoydrvpf3CfKmalrCFbZXghV8yKDIMOZdN8j2X92Gkt2rZCNqc/U9i2XJ8/T2wplpTjtgo2B7x25t1TbOi6HGv3P70b5f/ypy/k+TGJ7IIMQz09sQ5eJcxBrRfEmn0RFbrZ6th8bH4F1bTEXhhrwsWzpikuJ9ZeWKeNDcVsxr62Xfvcq5BNps38OU9jLEnqith8TlId/ulK1mjuJ+KPQ2RZXzbKNOXwHPDaNmXaYe9VzmvRDrtpTdiwm/oLu+V7GtIwhcZzeJxSKhWycYiyS7vS37AflkBbB7K6QlaW8LH6/u1v/iGJX/PfTRtG2kDRBrFVJaoORnPVaT6WlrZlvdi8n+r9amyqP3faYq+9+W1X13bem7332VMgnzq31l6Ny73vyrO57yMs+yGEfggPAAAA4K1DGAsAAAA8wPEY5fnzg6Qk8ujRTlXISpmaeKo6tWvH1n/5f53P56eANIeD+fOSytkK2Xz99D/uT/v686QOYPMxWwW7THvcBqnn6IWwMSX57G6Uj26nv7skMlwNJVRtqh1zKNv7s5WY+WPwA9i8LZWyedsJY+31bjgrUvXvVrqpEMa2FzHBWr7PHAbm4yW8CapKVe2XSli15qtXBVsqR9XxPAZbtSfJVKXKcq/mfeagLFetqnPlfnZbfqupaVvd01TM6t9HCnWlbT6fj5d287qzEmQKdnOF7BAkpLlCdj9Iukpy8+mNfPonn8p7v/iePHr3kf7PT2zFrEgb0j6Yl/et/YfYCUB7VbReXyerTnvj6jTWx931apv/42Da9f62+Cpkm/YfVwAAAAD4WiCMBQAAAB7g00/v5Ld+68fym7/5M/LX/to3ZTdXeOaK2N1uWpRyGKZSwLyGrK2MnYJDKeu2LmvGikyBab1+7FIZK6VNnrY4B4T9yth8Pge9yzim46HaP9eSsyTnmMjNIcp/9q9eyE/votwPQYbdXBGbq2AHqStiB1MR60xRvLpWbH6fJkDtVsK+5lqx3QpZdZ3/4kybtPK5t83tvGq9IKfDrSD12rEi7Zqxuu/eWrGxPZfD2iENU0WqzNt5P7frrR1bwk5VpdiE3nntWBvOm5C8VMhGWSpkh2EZp4j83v/p9+QP/s9/IH/7f/635Vf/zV+dvlf1Lksgq/rXAWg1tt73vpqxOmGqevbqehtmeud0X/qYbjd/D6U6Vu+LVOu4lnPetMhpaWvH5oau+VK9jqz/Utp9+07M8W6IbP8b8G5rz9tndIJm9zqnTfWPHghkAQAAgK8FwlgAAADgAWIUub+PcjjEee1YmStip2mJvSrYet3YperVXzNWr+uq14e16hB2ai+lr/p/7V8CXx3E6hB12T83JfADWJFpjdgfvTrIJ3ejPD9EuY1Jhv0SvuqKRv3ZBq3l//XWihUVktowzglae2GsDmLPWiv2IcFKqvvUa7hWa8Sqdno90qqqNKlK03msOTis1m4VUwErslTX2rVh9TjnylW9X1Ws6oB6/umValxZnsvdJiljbyph833Lb1ZV56rfQFV5a9qEMAe78z9qyNMl5+NlveJdkPF2lOP1UT765x9J2AX5hb/0C1OFrH4uqQNi/d1X68Wu5ItWd13Y1D+/Nl1xMz3xhv66IWRnTG7/Peq+axW5D5raeeVe3vHN+/qU8w671zpBrPffHQAAAICvB8JYAAAA4DXEmOa1Y6f1Y3e7KdCc1o5dKmKnbV7nVcrnqSp2+RMRFc7ma5fzOehdtssasbmqdlkHVpdK5n1RlbMyt9VhYL72/NDAy2NSSjImkf/y+6/kT57fS3iyl+FqkGE3TMMx1bCrlbB2jVgd1HrVrtLZOmGvPleFsTpg1OGrDkx7Iaw97r3SYM55+0ltq+5SqdosQapqW02fayoJq+fI5+Zq0RDna6K62bC0cfdzeDrIdP0g62vH6j7yqVwZm8Pk+XnKcw51O0lLH2lYnj9XrZbqVXXPMMzTW++mZ5W9SByjhH2YKnYliUSR8TDKb/9vf1sevftI/u7f+7vyc3/h55b/TvJ7zNXsXujqBdo9JwLP1el+O8fcIDat9KXOdfedSlh3WmJ1Tb6uqYr1nj211zXBsXe9GUfF+93pMYpzXj/f1mmizft017zV/w2rz6sV9QAAAADeCoSxAAAAwGtIKck4prk6NpUq2RjTPJ1wHcraqllRFbEx5nA17wcZBnu/5b5LYGvKEZfWzn6uQJT5cx04TPsPDQdSN5BNQ5C4C7If2iDVVriuBa696lmvmrUb0OZALjjnnGmNm1BWn5vPT5sT78053VSoyhKuzg38ilgVqOp1TEslbDAVsOqn0FtHVleh2gpZXd1aVew676aMXfe3ss3fX7lfEndM+vuxVbO6SrdZM1aFvmEIJTTOYW5IaprsuTp2SIPEQ5TxbpTv/j++K5/9yWfya//Wr8n+yX49lF2+2IezIaztr/Pf2HLaa2ACWqkD17VxuGPT91MBqg4mu9MH5/HYkLY63Xn2tbH28tjXWQD7nDFsua/6P9VlrWMAAAAAbz3CWAAAAOA1xChyPEY5HqfK2P1+KCGsDWOn/eXaXOk6Vb6Gqn1eJ3YKaOv7Te2mytq8FZlC1Fxtm0PevDZsDllz+LpUxgbRuWIOgh9SrOUHsdNf2AXZXe2mNWKHpRK2CkR15atqo4/3wtluACv+dMR2XdhmnVgTMrprxqr9hgot6/fhhK+2TQ5D535KpagOX1c+V9dnpgq1tA/TGq22Ajivq9rcx57LwegcapZq2Hn92Wp6ZBWGS1RhlApj8/kSMA9L2FdCWVV9q6tkU5qqelNQlbPqvQeZf1N5DCIyxEGixKm/NK0pm6t9h6thqpD93/y2fPDtD+SX/xu/LPsn+7riWJb3qqdiPhnM119E8714x6vw1DxbdSyZ9ua4rjhtwtmVtWJteLs6bbIem7pHE1Dqezqh7uq7cP8PjvM+1tqI3093SmIbOJt+qmcAAAAAgBlhLAAAAPAaPv30Tn7/95/Jt7/9rvzSL70j4xhlGIYyTfFuN7XLYewSFuZgNodUbWVsXjt2qpCd9ochyZT46K00n5fAVYdC+ryU6YiXz1LOP4Reezb7g2f38uH1UT49xBLE6hC2CVjz2p02aDXXNBWxovoTJ5R1+utVxa6GsCaldqcYXXl9vZBOh1i6jQ1b9Xm7/mtV8do5Zqtq83h1aKrfQRP2qvfRq5Qt70WFss19hjpwliCl/epYcpVwrprNQazpN8W0BNDzd1xCWlnC2DSokHY3BcphHySkIMN++lcQ8Tj9dueHXJ491b+Vct58n2vfu9fWC/J64aA7BbHT78kQ1wscvXF6YacOfVVw695LTGjpnRf/WV2d824g6z1XMuec9vrdVevd6ufvBMOEsgAAAABECGMBAACA1/L8+UGePz/I++9fyc///BMZx2GuaK0rY1MKc1XrUiGbq1VjTCWEXaYzXo7nQHaqcp2C1nw8B6tLSCvl+HTftVDWHvfOb9cswZhEvvviIP/Vp3elKjYHaKemJ24CU+lPXyxiQlETCJawLajPItILY6v+TDirw0fv9a1WzKqvaC2sS0mFmnr6YSeUdStjkwotzTEb1pawUsx0v2kJQqt7hFRd0wsmdTvbv7et3m+qK2ntlMT6u+pNV1wFvEnq9Wb1b0iHs0mWfwywW6YrHnbDVK0b01KJO4+1evbe9+58304D/7BTSdocN4Fhdd6Ep001q7r25Fqxa+MwYbANMMs13nPaULP3qnTYq++z5f9cde6r13YtQevaGKtDqf98hLAAAAAAFMJYAAAA4A04HqMcDlGurqKEEGS/n/7X+Fwhm7dTdayUzzbnyOdzlWycw6Mctub9pdI2t89hbQ4p68A1h7L5vEj+nNRn3b4e05o2q0nyh8/u5Z9/cic/OaYpzOpMT1xVw+ppilf+yrWyhKinKmG7FbLqGh0wdgNYG9Zaa+9KZeVrFbIhhCXsUsFrOb/yuXtsUOvDqjYSpEzLW4WbISzH9b1Ce42ILNMJq+mLS2Vsnk5YTVdcxjQfr6Y2HlS4lac/FlmC0CjVerJ6uuJcEVuF0oOUz2UKZFMhmytfQwoSYpBBBkljkhSSDMdBbj+7lX/wH/0D+XP/9T8nf/V/+Fdld7WrArdqjd/VH8FpvQA2fw9umKrbeAGpaVcFryrg3Dxdca5uVeeqMDW3EbUvZn/uw53uOHXG6ryTsq//pN42Fa3mHdZdtSGrHrd7H/P+3VBX/UOD5n4AAAAA3mqEsQAAAMAbkNeOHccku11dGRvmSsO8/usUnObANZXq2FzBmo/nQFZkmbJ4SrnyVtPH7HlvfwrcvKI3nSJ4gbF7hWnz8e0of/DsXnaPdtN0r6pa0U4zXMI/G9LmPxuEzv9PB6rl/BzQrU1PXP2Z66o+t4SwakybzU1tdaqICh5zBWgOVnOlagql6nT6/pZQsqqI1ZWwyYSgKpAtVbjzGEolrro+j6MEtapatwmYgzR9lrGailj97nNbvd5tUz2snjd/T6WN+X5SSOWepe18TAe+pSJW/15UlaykKag93B7kz/7hn8l4P8q/9j/416p3Xb7DeRy9KadX2bDR9pvfhwkaTwaxyXzWbUz/bkhpj4kZZ2qPdUPT3v8dyWGpnbr4VEi5YVynrmkC1bX30btXpz/9zstvd8uYAAAAALx1CGMBAACAN+B4jHJ7O8rV1U5CENnvg6Q0yG43lQZOoWsON/NCmtPcqXnqYl01O009LPN5KdcOg1Rtl79Q+sltQxVQJbUvIlJXz9rQVVfPniulJDKEaWrifaiqYm3oOgzDUh2rt95UsnMw252mWAe5qvKxqYRdm6ZYpDpmj89vTroZmw5we++mNFX95lArB62qDztNcBWY5uPSft66rcYXkoQ49x+d/r0KWBXI5krYslXhVA5Zy29rSEs7ma7J676KLO3Lu8lVr3l8YRlTCR3nQC/EsPSr+5jfWek7/zeWx5aWc8N+kBhi+ccEaUwloHW/S/U96u/uLN71TqC5FsLa/bWKWP25WwUr9b7+0+u/uhWyUrfR11Thq3ou73j5rMdsjpX3Yt+d10YHrV5A67Rtnl+kfS5xxh7MZwAAAABfO4SxAAAAwBtwfz/Kq1dHefJkJ/t9kHFMEkIq68QulbKhBJ9TtewUiuY1Y0XSvA2ljS4d9KYE9v/X/lR9nu653DtPaTyFskvouoTBDyvXOsQkr45Jbsc0hbAm8KxC1BzkzQFmqYLVx1VQqs+dqogt1+Tr52t7VbV6bVpbZVnG7YWwJ8JXy1bWlvec76mrTuevsKqGFVOtqqtk1TW5r6oitlMhW41vvlc1DlH3Tct7qCplRdq1X0UFl/q+amt/umuVsdW70CGwOZ/70b8j/bupKmRNu+q3OU+RHIZQtuP9KM9/+FyefPBEnn7jaRlD9W7EGdsa5z81d5rbBwSx7nHbl+7T+8++Nz63aScMtWO1bfJ9Nt6/c/MmZHWnBHaC27PvZdr2gt9z+gAAAADw9iKMBQAAAN6ADz+8lh//+Fb+6l/9pvziLw6y30+Lau52QVIKKmzVpYBLpasNQ22FrF4HtlcRW1fH2vZT6uVVx/rrwwbnWF/u43svj/Jf/PhaDmGuiJ2rYiVIU6VaVcN2KmNtcGurYd2KWL2vw2C9FX8/97tkanUA24TAxqlgtgk/TXsdfjb7QZbpceegqVcN6x3bUhkrQcrarXrN2CJXoUazL1IHwWr91nIub/W/G9DVq3NlbFnj1Ya+OXyW5TtKkpZ1ZZNMVbVJlgrWYO6Tg7h5jdlq7dgkIrslXB7iIFFUZew+ycd//LH85//T/1x+4+/8hvwb/+N/o/kO9dqxZ1sJIqtK0HzKhKs22PQqYvV1TQVqas/pfW/d2Oq63mdp+3YrZe0zmmC1vAcTelbrwdp3mZxrTD9epezqOri99yCq71OC6tv77wwAAADAW4UwFgAAAHgDjsck4zjK4RDleIwSY5Jx9NaODWX9WB2MDkMONL0K2SW9spWydbKl0rD58xL0zhWJVU6gr61D4XzOTl+8JiWRQ0ry8pim6YlV2FlVTNpw1VQTVgFpvkZCFXpVfUhY3df3mroL7r4bykr7uQpnTVh7SnmGHALpr2u+j1sVO7dp1onN31OuaFW/l6oC9qFryJpgV4/b3c+Pl9Szmfel146tAtOgnl//Xmwoq6pjq9BanzP9lnE499GhvbfWbAl7B5F4jHL902t59qfP5Ef/1Y/k/V9+X979xXer79V84X1r/12thLDl/dp2a0GsqHamv2o6YtWXnbbYBqNVf/Zzqj830wqb52zONdlqaq/1rjPj8PpyQ1lpQ91Noao3/lQ/f++6lJKMN6Mcb48PnokAAAAAwFcDYSwAAADwBt3djXJzc5Srq0FSWtaOHYYoKU+/mgbZ7WT+XAeeOqDNVbFLdexyrl8d27bzPuspiqegtg5dl8rcM5LG2bAfRHRFrFPlWlXA6spYp5LVTk+sw1p3XdleP/qYtIFuE8Ka49U5zYazHedWxeZjbpVsPqfC2ipUNdMa67VOS9jqBF4hhIdVxs771TquuU1Y2uaK15RSCThFL6GsQul8rQ17yz3yOb2O7DC/k1whq6YcLvfJ7eL0uyvvR+bQdTf/tuJ0bNgtFbIpJEkxyYf/5EP5wT/9gfz1/+Cvy1/59/9KG9zl4W/578cGr+a43rdh6qYQ1uy71ao5gPX2bTA7bzevFavu1exLvUZsFQTr57Fhq7q+el92HHYsnfC3Vwmrj/We022zQRqTXH94LePtuPkaAAAAAF9NhLEAAADAG5KSyGef3UtKIo8f72QYgozjlDBNlbFh3qaqGjbGIMOQ5uBTV8ROFbV6PVm9xuz0v+DrrczXLyFrSjpA1Gmc3hd1zfIsuY8tbsck37s5yo/uohuc6grFZvphWYLVHL6WoFVMX7K01/fR/VbH1datlFR99QLZal+ca53jjWRCVjudbb5/DqDmr7VUwqp7VQFUkHpNWVX9msPKXAmrv/aqQlUFvvmnVFW2yhLq6oDT2y8Vp/qZzPtu3pX6TvQ0xE0Im/d19ax6Fv2fwkPXjtXHc1WsXju2vOOYJI5R4hjr//zU9918V1uthbC2bxvEdrs0vzcVvNqws9nX7e11+t6dILQJU3OIqsNQ/Sym7+oZemNde83mnBvKrrVd61oHyiv3XB0bQSwAAADw1iOMBQAAAN6gDz+8lo8+upWf/dlHst8PcjhESSmvHRslhFyiN5Rq1GXd1iTDvLZl3i4Vqkt1bL9CdvnLYWpeK1aHsnot2Vz5qteO1fdc1GGtlpLIZ/dR/p8f3cq9iAxXQ6mKtaHsqTVhbXhahbJizuk+evfrVMlWfdnPa6Gs1PubNZltfUAHaSWUVRWvuo0NULd8tprpidW2N75c8aqnSW729WU6QNXhb65eTaqK175vVcnqVcbm/suUwmrfhrBNGD9IWTM2xVR+l5KkjE2SNOvJhl19PI/XBolr1dFb9cI9N1Q1x86qiHUC1qoathOu6kpZO61xc9yGr2npo6o2Nf00a9uaQNcLTt31atU7tNMnu4Fxqs+dWjO2eS4vwA3SjJcQFgAAAPj6IIwFAAAA3rCUktzfR7m7G+XRoylV3e+nFGgY8nTC07qxIiLjmOagtf1f53OYOgwiMeaQNpRK21xRu0xTvHwWyWuFSqmwXILaUILgacyirlkCWfVU7tqx9zHJ7z+7l0/uo4zShq26+tELX6tAVZYpiavpicUPWHt99ULZEuiJVGOrzp0bwqo+t6rCu9xtnsJarXeap/PNQY6uOm2C0zm8rSpipW1fbfU6sibg1WOtwuBUv6fcRxOa6neVx5PvFcy9ze8kv4dSpSqmfz2FsV07Vq/1Guo25U+kube9LoU5rE3LFM9hCNMUxrvlHf3wt38o42GU7/yt78g3vvON5T11fhJ2Cur5Q99KCKu/k9Jch6rqXBMummPlmrVpi6UNWasAUoeVpr8qlM3jP/Fs3XfTGbOnNz1xE8r2jqe2jQ1rdZumaliFsLrKHQAAAMDXC2EsAAAA8IalJHJ/P8rd3ShPnuxEJMg4JhGJMgzLFMK6MlZEh7HL/4KfQ9jpfCjXDUP+H/aDc90StNpQNk9HbIPZPG4dzk73XH/Wu2OS3/74Vp6PSfZP9xLmaV1tVaKeVjjvVxWLIvVnUe3UQT2dcRPoqj670xVLu78WxHrVnnkclS15bH6nJsys+lVhag5gc9Cag1A9JXE5J35Am/teq4TN0xz3KmSrPtVzljAzB8FSjz2/P32uel/O957b6umCSwirpx7O348TBJcw1PzpcFdvy5qz85q1pWp2DmSrMDZv5+/uh7/9Q/nh/++H8sG3PpAPvvPB8rvoZG4npy3WwaoX1nYCzCrk84JYqT97lZ9VRaq336uQtYFvavt3Q9ncl+mz98w23K1eW2/N16pR22+1Bq55Xnt/L0jNz9ad8lmkCmQBAAAAfD0RxgIAAABvWIxJvve9V/Lee/fyF//iz0hKU2VsSoMMwzRFscg0ZfEUgEap14tdqlDz9MQiUoLVPKVwPV2xro5tj0/7S5ibg9g6zK3DVz2NcU+SJMN+kCEHV3baYZEq/PQqYdf2beBahWteFW5o7+eFwk1lrNr2QtgqgHVey1qFrFstqYNwFWCKyLIOrOlbrwdb9vU59TlfV46r/vUYvCDW7VNVs5YxhbpNFbDm6/Mz6mmKowlE8+Xz1MFVhaosn0Xq76mqjA3L/aoq2rC0rapzZa56jVJPa2xC1yaM3anj8zvMgVsvbD352zDfidPIDWc3hbBe6GqPdaYttkGsXh/Vnf5Yhay2KrWZRliSOyYvfPXCTttf805V0Nurym0CYXPOTkV8cprirR5yDQAAAICvLMJYAAAA4A1LSeTTT+/l7m6Uu7v3ZL+PMo5JQogyjlP4GeMU2oqIxDglOcOQJMbpvJ6WeDovotd6zQFtjMt+rnLVFbF1Apf/l389FbFOA+rASFfKesYkcohJZJiipir8FGea4Xws38qrjlzLfm2gKyaEzfsizfHpchXEqv3SXlR7Na5eCNsEtqtDV8GdmHA2qTBRVcqGpKo80zIWHSqVqU/n9rkPG5RWQZR9Bj2usFTK5nGVsFUHr8kEock8kwqV8/NWga8JRsszztc2la75O8r3Ve+svDfnO9NBb/l9mXfghbbV71GFtCWQDqm6Jh6iHG+Psnu0q6ddLl/xSurWO+WFuzZo1cc6QWwVkHpBrBdSqv1SMdoJTHNfes1X3acOXXM/p6pP3eczUyY3TDBatfOO22fRXZnQuGqrg1jVf1Uhu0H1ngAAAAC81QhjAQAAgAuJUeTly4OIJNnvB4lxkLxWa644zdMWT4HrNI2xSJAYg+x2UkLTlNJ8bpKPT2vNLvv2T8Tf19vp85RS2WmJe9MUj0nkH/70Vj66i3Kzm6dyNVMUN+HpENpjJlhdXVPW/OV7lX0x+ypksxWxVdWtmGOijs+f6/fkOBXKqqBx2iwhaK8CtmpXuqnXde3tu5Wx7rCc9s60wk3VrXpf1b55H2Ua4BRKSNz0n38vwxK45YrVPHVwDsVsVWzpb65aLeck5QL05nsux2X5nIY5XFNTFnuVsSEFCbvp95risvbuP/s//DP57n/xXfnX/0f/unzw7Q+a97xWGeuGrvPxercNQ6tj0glh875XybqlItbu6+BRB5PO52Z6Yqdtr9K2WzVr7tPczzyztw5sNX5Zphq2x92+9bGY2nOneGEuAAAAgLcaYSwAAABwITEmubk5ym4X5N13owyDyDgOcwWsroxNqgJ2CpxylaxImqteQ2lfqiHnICilqb0u+8tVrTlkFZn6qKthdUBkk4H1dDGmJD+9G+XHd6NcPb0qUwZPV5rKwBy2iQlOV27XraLVQa+o4Db33Rt3DmL1+JwAtrQ1QezWMXuaKYr1q57vVa0dm1TQ6RzPlaESpKpi1fu6WrWEraG9f/4tLesGp6raVj9bDqfs2Kp1W9VPq0ynrIJUO41w9VNMy3uu2un3pJ81hOb5dNhrp08uz5T/UzHfQRUgh+Ue1W/OTIGdg/Tn33su1x9dy+H60H7H+Tew1VoIK8v7rNp2jolIG0Z67U3/3r47ZbDuS98vSTMOt2/7zL1rVsbsVph6/Tl9ekFv77lKcNu5VxWw6t+2qPP58zFJPEQCWQAAAOBrgjAWAAAAuJDDIcp3v/tSPvjgkTx9updx3JWgK6VpnuHdbirNy1WvuUJ2qoyd1pJNSYdkU8iag9Wlcla6lbH1mrP1ubo61v/s7cckMuwH2UWZKgVz1auqftWhla167a4fmwNWke71verZXPUYJJStrZDN/VbhbG+KYn0uX1c+huZYj50KuASX6li5T6rPP7QyVn8+WRkbTGWs095OVVzGlvedd1dVrop5/3k7f2e5gjW3tWu65vuUiui4tPWqZ8t2UO9Zfe/N+rWqXUihBMO66jZvh90gMcV6zdi9SNhP9/Wmql2rjG2m020b1O10c7surA1F9Tmv4nP+fE5FbAklnUpWW3la9mNarnH6a8Jbc8xrV013rP66Vazm2Zr7mPar/cU0/Qbzc87/UKb6Lp1ANvd3/eG1HF4cpkAWAAAAwFuPMBYAAAC4kJRE7u+j3Nwc5fnze4nxSh49GmQYpkrWqZo1qSA2lSmLc/XsYjo25Qhh3uo1ZnXJX12eNa0rWx9fQt0ctNqwyKYIJpybL8zhqw43lytUVaHqYq16Vbf11n+1VYo6FC37wbl37lZPU9zcPrRtzeOfE8K6zycyBXvSqVbN3ee1VvMUxvNXW6po56+zqmLV/c7jT5KWtWedsZRKWmdt17p5XX0qSZY1Y9W11fhVgNpUB+txqGev1sDV7yhIUwXchF3BPI/9TyFnp3m92tSG1VUlrjOltfs7zPfUgbp5z1umIe6dXw1hdTt7zAllq7BWfbbXVPtmHO50ybrqU9qxNZ+N0rdZF1YHx1VbO1Z7X73fubYE0fadiDTHq/apM7ZT3+cyCImHKPGeIBYAAAD4uiCMBQAAAC7s5cuj/PN//pn88i8/lSdPPpgrXadzOYCdwtRhDmin/bxmbK6MzevILpWxoUxFvFTGpvm4VH8i/c95X2/tZy2EaZpiGaRUxTYVqDowVdWvVXBrzlf3kNBtt1oZa67RYZq7L1IHbrISwvbCxHy+owm99DUmhC3txbTJoZ6aUji3tUFiFciKX+ma+7Th68nK2Hwf9c6q/dw+qP62VsjKEpTq6tbcpvSpjuV1ZstYgqqI1aFtqO9T7avK2rL2bJKlWjZXluu1Y3dBwri8jyChnPeC0vwuPJuC2mTaeYGqDRa9yti87RzrVsSK2XfWjF2tjO2037zV4071Z/23VhHrtVsbX3N9VO2j85xr9PcFAAAA4GuHMBYAAAD4HIxjksMhyt3dKNOasEF2u1DOpSQliF3WhtU9LNWyS8XoUhE7HRfJ683mdWSnkHYKbUVkDnHzZx086mPT/ZbPy3kRkZ8eojw/Jrmbp4+dh6dGaqYbzufDci63q58wP5cKTfW+nmpYTAhrVPdQwasXqroBofrsBrEbQljbv1epV12ezP1yCKa+omW94KVtFaTOlaO6XalMTeUidxy6qrSqQLX95efZsGasnSq5ep657xKKJhXehjnYDEuf3rHq+fJnXRlr/srzzUGv3u99994/NAhhCV/zsRij/Pif/VjuXtzJL/zlX5D94/r/d3s1iOucakI+Heql+ngviG2Om8/dCtn8l/vpTS3shJnV+ql63KdCS+dZTz63CUNTSk27akpj27/e5ufR18/7thLWnWK5N/4txwEAAAC8tQhjAQAAgM/J4RDl5cuDqmIV2e8HCWEKZpcK2WkN2ZSmStndLkhKwa2MzX0Nw9LnMgXxUiErspy3VbBtVewS2NbHJ7//8ijfvR1leLSbKgQ70wiX0CqUeNavUpS6fXcrTgAb6mPeWLzpjpvpjGVp1w1hVUBcOZXH6pC11z6HjPoafT913quEzfvlc+qvBavPlePJr2Bd1ipOZTzVfXMoa95l9Tz5Oq9CVgezOSxVoa3+zmxFrnesPNM8hbL3WyjrxOpAVt8nV+cO9TTJEmSqBp/XsQ1DWAK5ME09+zv/+9+Rd3/hXfl3/hf/juz+3O7ED6P9jfSOu0F+ao/3Qli9X7Vx1n1tgtfO8V4laS+grca9Eux6gWfVr5gx6HflhMe2v96zVFWusW4rUZq23nta+w6bzwAAAAC+NghjAQAAgM/J7e0oP/7xrfz8zyfZ70NZK3YKXoMMg8wB6jAHqkvp6VQ5K3P7aeuFqctfnsJYB7A5iMvtg+h1Y3N4KzIdy33WOUOY8gS1VmwVXlYt69BUh4tV1atHB7E2xNQBrGpfzom5Tt2/urcdn5g2a0HsqQDWPEvFC2Ry/2nZ9ypNp8vnMNRUijahaVLhrJgANqlgVAex8/U6DK4C3dy/qiYtx/OzJvUe9dqxOpAV0z4/oxf86teuzuvvxFbcVr8Zdbyq/NXfjw1sq3+MEOrfY54G2QuL5++3qeDcItldP4D1zjVhp7p/N3y1gacOa9Nyj27wqK+RTmWsd0/nz67f2qztaq5vpmx27puvscd6z+K+69yvGk/1XHYsa8x1AAAAAL4+CGMBAACAz8n19SjX1zey2wV55529E8aGucJ1CUPzurEi9VZPIbxUw9YVsjqUne6ztBdZwtrp2JSKVflmCWL1wVRVCFZVp7mpCVLd9WD1NlfOhvrPBmBVAGvuo/upAtmVQFdfp895Ie1qVeu5vD5Se04HmVXAmQNIHYyuTU+s7mED0WpqYqmnB676VcFoFWbmTFVNhVzeZ5J6SmF1ra2UteGrXTPWhsVVMB3Uc+l3ZoLe8n5C5zlzO/Pb8qpsy7q26lz53cxr0J41Ja/9HZhj3nqxIm2YeHKq4uS0cQLTbkWsDV/X7uNVxupwVW+9QFY/m30+E4p213l1QtkqDLV9qXvb8Lipnn1AsPqgkB4AAADAVx5hLAAAAPA5+/TTe7m/j/Irv/KOfPDBIxkGkd3Om654qpDNgey0puxQha8iS/A6VdbaMHaecjXU5/J1a9vp85Jo/eB+lA/vo/xUlrDUVh42a8KqANG9RpZry1aHpCZwLUGYCkfdvr3+dUjrhbPleevnqdrZbnuVvafYQEZ1U1WZigplTfVpVUVqA1kVjOoq2NxfNZ2vHZcOTU1l69RkCWh1WGz3q5BUndNBa6nenZ+vhK96fPqZVJ/6+7TTE5fxm9+Fnpq4hL522uTcVget87TFOtCVQabpjoOaEnoQuX9xL7/zv/sd+cavfUP+wt/9C7K7Om+64qbSssphTZiX2mNNQNurhtXHbAjq7atr1kLZXjWqHv/q9MT2OttelmPNcee5baDqBatr0y57waxEkRTTNH2xxz73PJa7j+/k8Owg4+3YuRAAAADA24gwFgAAAPicvXp1lFevjvIzP3MlT57sZL8Pc+i6VMjmaYpTyqV2WaoqXHUQG2N7bOovzVMQ52mLpzQvt8vnlumKl5BxqbIU+el9lD+8Psr+yV7C3oSQwXx2QtW6eXvMVh2Wdvm0VxVr7627M1WxTUgc6nF0pyF2+n9wCNvrU4VJpWozH1Thq4hUUwl3A1knyCxTC+v757YqzNT9lPuIs1ZsL4Q1z1BNS+y9h/ke+nO13q2aXnkasgpSqxe3fIclIDVVrVVfJnD1fo9VBazU/egqWx3oHm+P8qf/rz+VV3/xlfzmf+83ZdgPcjabx9oq2zNCWNvWhq1TExO4ekFsDl+lvV733VS+OhWu1X2l7aupjhUzFu+V5dDUjsdM4dx8doLgzZWx5jtYlUSOL45y9/HdxgsAAAAAvC0IYwEAAIAvyA9+cC2ffHInv/Eb78t7712JiMh+Dm6mYHaYtyIxTiHtbhdlGESGYVAh6xS4TtsljJ3C2TQHukuF7DRtcSjt8jmRvF+3KYJMwdKgAlMdis5tmurYqot2v5qe2KlQLPfIIZgNaL02Osy1YZsXvkp9vlcRe1YI6wXQms6IggmM9BiSVNP9rq4VqytZdaiqnksHs9U6suoaXY1bBaM5WE3L+7ahrIgKjPV9dQVqfj8mhNWVq2H+betxlvdiwmpdXWuD9KoPPTWySPX7seFsL8wNg3rfJpCVJNP03bsgMjhhoPoduAG1F+yZ4HI1gLWhqz7nhbAPmaY4zqFsnNuorT6vr2v6sIGrE8D2KlW9Y6vTFOdj0amAjW3I6lbGxvl5kyxb8xxd9vsAAAAA8LVDGAsAAAB8Qa6vR7m9jXJ7O8qjR4NcXQ0iEmW3y8GoLt2S+ZjIkmLVx0IJmKRUxooEiTGHrbn6dWk7hbn6Hrn6Mge5+h51CFsFhsulTTg6j041M0GpFsx5c21pc6JvPY5eH3rrVtnaUK8ZrNOuObV6cpKWdjlwtOGOrnQt16Zlq9eKrcac6rDVu3czJt2vOEGsbtfr79T19n55vPo57HeRzNaGsOqa8tvSAW9atvrdVD8ZHcBWt68rZO1+7k/Ccj+9dq7WrRKuG9VtbR9OtedqJWzveDL30PdJ9bFTFbHNPey14lzr/dnnVQGtPbY23XFzzvbnPUsv0M1NnCmfNyGHBQAAAL7WCGMBAACAL1BKST777E4Oh2kNwat5fcndLkiM03a/TxLjIMMQVIXsFKJOa80mGYYwn58+h5Akxjw1sd5K+SySP4tMYa4OgOuq2LkYc6r4y2Ho1KwKPWW5Wl2rrnHC11JxaKttzdZeX003rCscRcQGbFXoqkK8ckxd04TEXhC7NYDttfOCTJkDPXVMV3ratWPdClkdfKp2VfWr1OvINsdVYKmfqZqmOAdZob+vg0l9TlenlurdoO4R1Jjz1Mz58/y87rtQ330JWdVfNQ5V0SoiTUWsBCnnQwiSBtXfMH9/uUJ8CFO1pP6edtKtjF3lNPWCyPxOdJBog9teQNqEqirs3FIZe3ZFrA1nk9PvyrY5FvuVsN76r9Wx2LlHTM21+l65Mlb/2e+qmlI5h8/6Ozj3twAAAADgrUEYCwAAAHzBbm9HCSHIe+/lQHaaongYoogMVeXqUqGaA8pUjrXnpUxhnFJeQ9Zel9enXapmp+vzvsghidzOf9O5OjCcPoZSLditVF0Olm2+xgt0u3TA5lzkrTfbHUvT9YkgdksIu+U5chubzZjjTaVoMsekDUrL5xw+OvlPFYbaoNbp6+T4vf2km7d96TFMl5pQVrfVAXGSeusF/OWVLSdL6KunaFZBdglx9T1Dfc/8+y7vSk13XALuuY/xbpTnf/Zcnnzjibzzc+/479Hhfmdp5Xyqj7nTE5vjTTibz3v7+XoT/q712Yw1OePuPX+v+jSZZzvVl7lWj6GZ7lj3aYPT5PzlZ9tw/5SSxGOU8XaUNBLGAgAAAF9HhLEAAADAFyglkU8+uZf9/ihXV0GePp3+P9GvrgZJaVeqYvf7JOMYZL+vK2SnrczVsGGukF3WjxXJlbJSpivWVbIiqYS2IqKmOs77Ij86Jvln91HS1SDyaOgHp7oqVVerVk1yv2p92PmavN+rjtVhmHfPqtpVQjtOe049p72+Ombv5TxPL4A9GQCb0zZ41WPUYaQNJ5vpd3PImANPqathpw7NOHK4KUsgqqdPLhWwqu8SYDr7+p3qClldoVoFqkndV1WsVhWzaipg/f3Y/dxnteZrfk+hHotuV+2rfkqFrGpXPs+VsUGmCtqQpvf0/MPn8g/+w38gv/pv/qr8tf/gry2/tR4bYjrHdViqz9lpi7vTFHvBrJ2yN4eRneCy9OFUxDb38I7N1zb3V8eac7avPMZcoarXsc37uqJ1a2VsrnqNUsZkq2ZPVrc6p+9+eievPnxFGAsAAAB8TRHGAgAAAF+wGEXGMcmrV0eJUeTJk53EOAWsi0F2O5krVpOIDFLylBLaLa1t7mMrafPfdE6vN2tDWZFjSnIvIkMQ2an00F3ztWNLhawO07rrxZbnWQlUVd9ewOrdf8MDOIf6QezJd9PJZNbWjrUVo2uBrNd/CSBTXdlZpjTuXGcGWIW8JYQVqde0dca71l/53HtmEzI35/S9vD7184ewPtbQPl+qG1chbbXGrLpvikkOtwc53h7bEHWN06w7VfHGEFbv62pQW/Xq7tvAVkxYq4NYUfdMUj9LarfutMjeGHv3mcdaPbfZ1+PpVsaasSYxwWsSt12XHW9Mko4EsQAAAMDXFWEsAAAA8CUwjkl+/OM7efr0KLtdkKdPdxLCIxnHJFdXSWJMsttNodkw5KmFdYWsyDAsa8fmdWSnithcNTuFAVPI219Ddtrm/SnsHXbT2pi6etCGjWXKYemHqOVcFQSr4DL4f6VdbuP1uey050N7fnUdWCfY7T1D9z5rbLNUHy9T9+rQUAeS8zVNICuqojVXpCYnFFX3qSpg9TquSZp+87PbcekAtNxbV/nm8ecAODnvfg6wqrBUfVfVlMLJCX/1mrnB3CeovlPdRv/Gcrtq37TRFdtl3Vndf75+CBL2oawdW95N89Wb4LA+We964atuZ4414Wwn4FyrlHUrSSWdXxGbpNtfd2vHZPo5ua/Wl3W3cbnOWxu2BKn6z3m+U98JAAAAgK83wlgAAADgS+RwiPLs2UEOhyi73SCPH+9UBewgIkl2OxGROIeqwxwkhPlPylqveTsMMlfUhup4WyFbf87bJPpE3pwIW08c18GtOnhalamuB7BNYBy2jW2tXWlrT5+45iRblepUoJb7mHC1WxWq+5K6vyagde5XBb2y9KHbVqGrqEBUBandMDfU/ZWAU4el9rnM1q71Wq3jKqGZpri8Q7XGawlc1TndZ3WNHo8+poJy/V70FNMnK4WbXC+1x5N/7txpir2wtbR1QlQddHbXoM3jyX96zJ0gdvVPxA0+N4Ww6plWA1+px9Hry05P3J1G2tg8tTEAAACAtxphLAAAAPAlcjwm+elP7+Tdd/ey3wcZx+n/kz2lqTp22gZJaVAVsiK73VBVxk7nciXg1PcUyuo/XR07tfG2cRAJu9Cu16rp4NMEpk1o6rTN7WzloQ1US3+mmrHqfwtnrKvt8q5T+dqthl0Lrr1sxgSbq6GnDmlVEOgGuLq9vbcKTHVg2YSn+bMKS5uAU9pzds1Zt0LXvoug7qnCZ1sR6wa8Ul9bvQNVLVvO23ecj+ex5d+ZvUbqNlXFcn6GvJZsHncvtVvL6ex1+uNKAFud74SwTSCbr+ntrwWdqm1zD28q4DKcOhTN0wP3gtu1ELZ3fnNlbHSuzevHxhPfk/nO3DEBAAAA+NoijAUAAAC+hO7vR/n443u5u4tyPCZ55529PH48rSW7308Baw5jh0HK/jD0pitewlkbwuZgVmQJFvXUxak0kjaMOlNVVTqHbr3Q1b1GVu6vw09VqepNUdwd05ncIHYthLXjsNmkqqQs7dJyzpuS2O07h7QqINVTGOspe9cqNnuhrA1up0dJ3Wur77cTwDYhcX7G0I6tTDGc2+r7mXeW712eda6Wrdp7la5mOuKqTVimJNZjqd6tpGlqb5Ep1Dv3N2Ye24av08YEfSaUfWgIW4WWuq0KVLvhqxPEdqtpnbF4QaYNVbeEsJKkBKnl2mi2KVVtvAC2Vx176vsSETleH+X2o1s5vDz0rwMAAADw1iOMBQAAAL6EDockz54dJKU0B6zT8Wma4Wlnt5vSnWm64igxhnldWW+64mWa4rXqWFsZK0Ek6mBQpP3c4YaROSTdWJnqVtWeart1bF670O/n5LTKW0JY534i0oaynYDUBo46oNVTD3dDVH1f755mrdheu2nIbTDrhqhOxW21pqu6V1O5mrtdec4yFieEbcIzE2Q3VasqhF2bUriZAlkF3817lCUI3qoJWZcT/QBWlud3Q9h83Hy2oWh1LiU3jK3anghi9bjsmLqVuJLq8YlU7XpBrD2e/19zr07Q2wSwZo1Y952b953fSUpJxptRbn5043yRAAAAAL5OCGMBAACAL7Hr61EOhySffXaQ/X6QX/7lp6VKdrcLcnWVZLcLEuM0bfE4ToHsUhk7hbYhBBkGHbzqrT4+h1whyM0g8v39IHe5QrATJpVqWtk4jXFuq9ehDaZd8K/xPpfr1qyErJvCWxPEesHsm14ztgolU6fdSj9VcCt+H7ZKVqQOPnOg2AS6K+OppjIuzdpA+NR0yt31cm1gbO7dhLAm+K+qanO/nXViq7A1twmp6ctbX9dW5W5SvWLnfc+fm/BVB7ResGqPO4GobpuiCUe3THnsVdXaitjYCWJTe8/yF+tjOiB1g9WophzWlbGdrQ1edR/l80rwWn999dg3f+8AAAAA3mqEsQAAAMCX2PGY5HgcRUQkBJEPPrgqQWuMQVXKRhmGILvdVDU7hQphrpqVqjK2XTu2/hOZAtp7Efk4BEkisjsxzlNB5DkVrs7FqyHvagB8CeeGuZ3qV7fdhkDWBptuFeyJ0LZXLdurrnX7cKpKt4TGWyp1m3VqxUynrINZLywOpl8b5OZ72DA41H3mdiLij1nqd17CbTW2eIxyuD7I7monw9XgvxTNC/lsqGeD1vlYL4TV+274qgNSu9+7Vy+gtWukOiGxDVF1G9suv/em0jZJ24ceop5auBfcOtMdu5W2Z8hjj8dYAmYAAAAAX2+EsQAAAMBXREoiP/jBjTx6dCd//s8/lSdPdjKOO9ntBrm6GmS3C7LbpVIZO21FhmEwFbLSqZCV6vNxP0gYRGRQ67q+hqlfv6JVV8raEEy3K59tNa26R3WtvW51gBva2vBXjX/tWnda5CrbcgJNda0NGt0K0XnrVabqsbtVoyv33BT2zmOqnkU9t15H1QtKm8DLCTqrZ3QC3dJ/vi5Pg5wfMSxTCDeVwLraNYevZhpi+2enKi7TFOdq2fnZP/6XH8s//nv/WL71N74l3/5vf1s2834fqT5npyzuTU/cBKH5mKlcbapTTZ9Vv/bzSmXsakVspzJWTx1chaqmMras86rOVVWuvapY3bb3Z95nFVZ773B+F8fro7z44xcy3o+bv24AAAAAby/CWAAAAOAr5P4+yjgmubkZJaVpvdj9XuYK2SXlmj4PkpJISvGMCtk8bXEOR/zw9HXooNRWtb7WVL9fkLOqgusTJfDqVqGuhI/2Hmdff6J6drNeP+f2n4PO3jU2bO4EwM0zrwXrXnB9onJ7tV8dGs8O1we5e3End8/u/A6T3U3dcyUANee8NUu9MLUbxNp+TwWx+h56jDpEtWPW57w/5z66MtZrZ+9vpwmugmI7DtOPDZbdtua962esnndMcrw+ShrfxH9gAAAAAL7qCGMBAACAr5hxTHOF7CC/+ItP5NGjQZ4+3ct+H+TqapD9fphD2iTDILLb2crYpTo2ryvrVsbmtWJtNespNsO14ZbZr6pubQWirFx3gaDYo5/fVsCuVcRuCpZNICsizdS5XtvVY3p8aT3E7d7DqbI9FQiX60WWMLT3LM74q/Vrl4N+mKzH6fwmqqrWVG+9d1Kt76ruXaq5w9JGj7FaM1aWqZTLeIdpTCHO41qbtlYHoub48rENOu3x3vTEvTDSBpXlGhU+uufMvjtVcb72TVXG5s9OJaxtaytc3epZ/Tc619hKWOc7qY4lNVZyWAAAAAAzwlgAAADgK2gckxwOUa6vj3I8TmHr1VVeL3YKXkWmytm6Mlbmatn8OVVB7DAvaVkqY3tVgiczOeci1deDq0m/LE4M76zxP6RC1YaKZ/Shp/DdHNSqkLX0MaWk3emJt1TynhXwSqeSVbdb6aqaJlm/v27h8kq1sRlXcx8TYOd7d8eeuVmsE+yl9viWELYcN23WqkCbClgbAJtwuOo/92uCWPdPls9VZawNdlXfdrrg1f5tSOysHasDWPtcW46lmOTw4iDHl8ft/20BAAAAeOsRxgIAAABfUcdjkp/85E6uroK8//5enj7dy7vv7uXRo12pkp0qZAe1hqz+Wyr/7DqywzCtGVuYMK58dqZltVaDSVsxu3b+VF9vSA4JRUxV8Nqt1yp2z9SrAj1VaXoqPKzCx2Su88JZW1WaD68Fouc+23xMgtR9OmFzWe/WCYCbithU9537r0LgkPz7qArY8g8IvCpt1b6sR5vDwiDVPWT+T2m1MlbTYaY5JmLelQ1H52MnA9jeurB23wa/NnB1PnsBarcitlcZG+upgktla6cStvSl14PVVbK6Gla38ypkvffrVMnqd5lSkngfp7Vib8fuf6cAAAAAvn4IYwEAAICvsJRExlHk9jZKjEc5HpM8fRrlyZOdxDhVyOa1Zae1YutQNoSkzuUq2SQxBolb1jv0wlmlmuLV+NJVv6oQbYsv3fhFHlZl+4b72FTt2mm39dipcVbruqb+Natj9a41ofvJ8DqHtaKCxi2cZlsCWHusu16sDkj1tRsqY/U4VoNYMfdxtvavCl+lbWs/r1W6umGvOe/27Tx/LxQv+3pMMbVtAAAAAHytEcYCAAAAX3HjmOTVq1FevRpF5CDf+MaVvP/+Xp48ibLfDzKOOxmGIFdXYa6QHWQYRAWyy9qxensco3/DOXDSFaRbwofS9sQ0r58HPfbqOXrOCGnfhF71qQ0Ae2GiPr41HF0dTy/crBtVIeTm0LN/0/5Y5grhqorVXNut4NVryaq2VTWsuZ/eur/fXL2s15KVudp4/kcOVfiprb2WKgNsQz5vSt1zpifeFMLmfW9NWHs/Lxx1QtW1gNauFWuDVy9EbdaOzdfHtGznQNZ+bv50tasNopcP9fOJVO0AAAAAQCOMBQAAAN4yt7ejxDgFtLtdkA8+uJpD2WEOZZOZrniZpngYgsg+yO3PPJbj053IHFJZ3QDzwmHEqRBVr0PanJvXx72kL2W17JneRHj7uv1vDZ2960SccHY6ebKvai3c3Feeali3Cam9bj5WAlgVGpd/sxD9/27c8TgVmFXwlw+bitnedMVuFe1DK2NT51ovkJ2f22tTpgt2jnc/2zDVhrPmuJ2quAlw9fXeO9Xfh/ezme958+MbOb46SjqSyAIAAACoEcYCAAAAb5nb2yi3t1NV6zCI7PdBHj8eJKWd7HZBYkyy2+UK2VBVyQ5DkDjs5Nl7V5Ke7GSfs1gnGNIhU3POc+mcsjeeC1z3NoSur+1NTIn8Ju95srjZBLzBWYPXVrt29qtK4bU1edWas1UoumJL9ezWALZqq6s7T4Sybp+24tY7vlYZq0NdG8g62yZ81aGsF8J64Wtsr2/WnPVCXftuzXfRVPnGJLcf3crhxaH97gAAAAB87RHGAgAAAG+xGEU+/fRerq4Gef/9vez3Qa6uplB2vx/mUHb6y+vJJhF/nddOVVgJM/N2ZRrizz3ETFKqFPWxarwyBS4ErGf6AgoAe9Mvr1a75imIU3LbeVW4IiuhaW/d2Hw81Z/z/XNod4pX/apOuufXKlyb9ueGst60x70QVsQPZDdsvbVdT4Wy+pytmq3G4ExV3ExNnNQzzs9RPa/zhSRJpX8AAAAA6CGMBQAAAN5yt7dRDockV1dB9vsg45hkvx8kpSQxDhJjkBjnqtgYRPZD3UHqfDZtvqhAs1m7thOK5erEr0To+gUEnV9qQVWedpu0lanNurF5bVcbytoq2HOGNq8PW/oNqYxV3z+OUY53Rxn2g4ThxM10YGqO5c82ZF2rjl1r57b39reEr+IEqfmY3TqVsF6I64W7TYBrpimuztu1Z51KWfs+m1C68x3k+8Ux+hXNAAAAACCEsQAAAMDXwjgmef78OC0BG4I8eTLIe+9NlbLDkKcslmkrMgVLnbVic+gkSco6mWWt1rQedm4NQ0s7HajO42ouN0Hr2rqx5fp57CEtVYurwzp1/pK2hOFr55Oc7mPrPT7nvKmpPJ2/h7wu64PHo0NZs/6rF8w2VeLBaaenKw7Lfw+53Ue/85F89t3P5Nt/69vyzf/aN1eHd2qqYneaYtNm7XjVh+3XC2Rt8Ko/m3Z2emIveO2Fo9XWVrva9WKdSli36lW1rfbHOuit3pd9L/lZpB379fev5e7TOxlvxvY7AwAAAAAhjAUAAAC+No7HJWUZBpH7+yjjOE1NPAxTFd/jxyLDmNz/H4VeuJnSdO3FA8u1YPZzDkubd/E6939bCuq+RM+Rw9NT67OW9itTE+fq1tynV1Vbgtd5v/wewtL33fM7ufnkRu5f3m+rouyEgfp8Ux2b/DDxVJvVNWOd66pQ1oau6tiWKYur5zmzIrZ3jbt+rL4uSvsM3vPnY513KSIy3o5yfHkUAAAAAOghjAUAAAC+hu7uohwO95ITxKliVuTnf/6xPL4aZJfPqCAmpLAEE1516immTVNFm/vV91QVibpaNk8BuzoWFdTqdWN1kHoyVO0Fvp37NuvTnuK9N1uh5x3vhWilaXI/r97XtlfB1NZrz2qzlfpNuOd65+3xlbWMmzYyh6jBvL+V6Yz1dMVVIDtvQwgShqnNqbVjz6qMNefy/qaqWH1+LYBN0vzWqlBVt+mFr5KaKYOr7fxOvKrXbrVsNGFrrzI2LpWwcYz9cHh+Lvse7POVe+XqWgAAAABYQRgLAAAAfA2lJDKOIjptCGEKacNdlCcnAoYcSjTTt266+dopP8x8I2u92pBsS9uvsodkRJ1rmirGL5CuTC2hup1q+A30vRoC57Yh1QH9fF2ZqjiZNnO7kwGeF66fCMVXA9qSl6p+vBC2t98LZW1w6wWx0tn2glDbj7rvljVje3/elMiWd7x5Z/PfeDfKeDNKPMS2IwAAAABQCGMBAAAAiMgU0H766b08iiLvjSK7cmLtopXzW/KxJM3arU11qQq5pt22sjXJ3MfcLu/rClp9n9K+sy6uVy1rj9vKXW+sa8/dfR/6ZPUx1ce80Mge967vjMG976nv0DvvhVzq4JZKXRs+elMDe9007RylzVpV7Ql2TeLcn14ntlnXNsi2ylgdnJpjvbb22Mnfwtq0xHY/tdd0144168VuqYK155t1YNVUw7mytrdmbKl6VRWx3rH8fpopjkWq99BdKzYluf3oVq6/f01lLAAAAICTCGMBAAAAFCmJRBVWLCfMXz521oy8/emBbaXjSidtKOsd69zX62fLvc5hg+Gmz+0dnbxP1W5LcHoiKN02rJVQdyVMPvMmD7YlkD2n3cabljHnNWbtFMWlejeHj9YDgu8tFbNNmKrbOCFs1bYXyqb2XC+I7f6JuW6t6jXWx721X5sphKOZzjim9r75Her3ktrP+tma/gEAAADgBMJYAAAAAD4VWFQVgHo/ByN2nVXblVdFKiq49ELPHOzMFYeSZKlu1c3UdLC5MjFPIVuFtfn6XtVtMts8vpUq3V5gWwXFW+hKvJXzm630V6oCTch2zj02VQNuDZNPeFOh6RsNX5dO6ypYkfp3pO5b1qBNnRDvgd+xPXZ2Vaw95gS0a9MU26mHvYB1bdtUx8a6Hxuo2mvs8bW1Y6vx22cw7656j/mSOP2lI0EsAAAAgO0IYwEAAACsU4FFSCpg7ASRIrIEomLCUyfwdM9Jv+/VcXb6rELVUwHq1r7zNZ0q2HOqfTdXm3azWidE0td7VZEr92v6XWmXqwXd8eR2OuTr9Zev25JxvYkcrNPHg0LbHMja64LaqgD/tYO8U9+h2m/eqQ5Zbei6EsLa825FqzjHvADWBK+9c00Yq6tkeyFsJ5TVv7+q0jUf088mZvyyjO346ii3H9/K8cXxvO8MAAAAwNcWYSwAAACAVqq33WmFU6jbO/1UFaqyBLUlpLNVtV54KlJVtXbDVfPZq47tVeHq86WS1/TR9KfHshLYivih7Jbpar0g1lvz81Q/m8/ZNjZIXfmu9Wc30LQB3RzkNlW6Hd2g9KHZ5psIZO26s2G5vglow/LM9n6vO+Zu4L72u5k/u1MRi9m3n811Tegqc3AqUq0Rm49768jmsLW8I9V3OeeFrieC2HJfHa7qZ7bvy7yHcu+Y5PDyINffv+58OQAAAADQIowFAAAA4LJVcU1wobdB/FDD9imd9VudEFZPYawrUJs28xqdIdQBWJkW2fRt26xWxOrns8/QC2Qzr1L2VL5ng1gnrHWDsnm/WwWpz6+Npden/dwZb/fYBtXYtva3FuDa4+eOa3MWOweuOYTV39lKZayetvtk8Lv1GdWxkxWx3jG936mK7U5TnAPXLdMV63DWqZL1KmJTXAljR2erK2e9d2GDY/2c5t1IFDleH+X6w2s53lARCwAAAOA8hLEAAAAAzmLD0OZ4atvZQFOStOu3ev2sZqQnQlR7n15/NlTNa9h67Z3n6A7hNcO/k1MLn+hrSwWpXSvTfp/rt6hDLi/gLUHYhvF+Uaoq2IeMQ1dl5/0k5R8JVPfQgWznZu5veiWMXQ2v9U/o1Fqxuj8VcrtVsfqzE2q6Va06hD01dbGYa2O9r4PYJuyNdTvvWXR1ci+orcYQk4x3o9z85KZU+wIAAADAVoSxAAAAAFomZAkpVJ9FZJluuLosLYGm6qe019WtuU9VVVtVl4YlKAkS6umD9T31+rQSSvhaTTUsqoLWaVOexVbAJue+NkjOzxeW4O2s9+wcqwK2lfCsVxVbhaoqQCvX2vvawGplbG4fzn1tWzdcXrtHZ3yr16710Tm3GhavjU9VwubflMj830qY37laSzb/fqqQsHS1sUp2ayjrBbK6jRe+quPV78p+9sJYSWV64Sag1YGrqoh1z81h7Mn9tYpYtdXjt/vleU3wqp8/h7AvvvtCxpuRIBYAAADAgxDGAgAAAGjY0MZWoVbVsTlk9UI+p71z0p3St6lmzZ+T6c+rXp2Pb6qeNdemoKpj9am1QFbdt1fx693Lvf/WIHY1cWz777XfMnVxFR6uBIIn753a5+gGvJd06j69Z3nI+PQUxbmPcwJha0uA7f1u9PENIWzZ74WvOnQVaUJPL5Btgs/Utt+y353+WFXG6v+WTu3bz9U7GJMcnh8k3pPEAgAAAHgYwlgAAAAALRug2D9RW2kDxFL5Ooc2eurfEnZ695RUV96qytZSiaiD4Hyf4KwDm/vR97NTymahvl8z1pzB6gpaHQrbKZjVDbYEs83asHbXntdtTLDWBGhiwjMTSK2Fe9599RSvJSATZ/9E6Nit5PT2z+GFp85vtmpjg8J24M21+XdW9pOZiti7X/7dxNd5QKdfe0y/2s8jjE3LtW4Y660Rq86drJDVa8VuqIxtxiPLmO2Uyu7z5PuNy3gAAAAA4KEIYwEAAAC0bOiq/rzpid3r89ask9lMVRzU1oStts8y3asKQpvrzVTEeQzV9Mmd6YirYybw7Rf2nj6/6V31TjthWje8XQsb81hOtOmN56zqTd3ezZFVeCtpe98PCcXOvObc5/TWgz3Vf/7+zqrarjvpHutVeObPm0PY3F6fOxW62sBzbargHL7qwFb1V/rXQeyWytg8Dvtc+rj978B5jpSSHK+Pcrw5+v8YAgAAAAA2IowFAAAA0DLhhP6zVa5NkJHqUFRkPhekXoNVlgrWqgJVBUWlIlaHnfN9qzVb9XU2/DVrwuqq3bKOp6gx57BXVdTq8XmhbqkA9sK1jSGdbdMLXFeDMxUyVVMLe33bcEr15QVe1WfdRtQ5O9YctJrfUBWO2edwxrtJ7vdNtT/V18r02OU3HVRQm9vHKXgsIe5DeJepd161Mb+BNx3GdqclFlldIzYHsPbzybViVypjm/HZcar96vnMNo1J4jHKi+++kOOro6SRMBYAAADAwxHGAgAAAKipkK7bJC1Vp14laxXIJhWa6mvzsdBu8/3LPYIKO1XwWoWtej1Xqfd1aKqrb+20w+5W1LU6nM1Vt9L2b9/nSTaH7AWxpmFv2l99/cnpgJtbbRjwiZDXPbfWb+f5upfYe6yEkyf7OvcadcwGqlsC1lI9eqLdWtXs6rTVZv9NT1PcBLVeEHti24SzKpQtbU5UxuqwthfENr8Nve99B/P5w8uDjNejxPtIEAsAAADgtRHGAgAAAGiZ8CSkUAUlTRWpraCVZaun8S1hpQpxcz+5eraErjpU1SFtWtbrbK4XWdaWtdMVqzE1YasKYKsqWPWM5br8fqaB1qGsTNdrvSmX693kHq9CSue99kK0MkZbNZvb6v0cDurvUF/jhG69aV6rcel+vOpa+3sRZ1/qds176OVkzvHqHSez793PuX/vfvl3Vtrn6tj5giqkncPHHj099qpemOh87v4+zHe1JYwt18X6+s0hrEgVxq5Wwq5VxqoKWf1bX62I1c/uhMq5Kvb6+9dy98ld//cFAAAAAGcgjAUAAADQUgFLDmJzYOGuj2rCvTINcFqCSl0l6wWpOnSt+rWBqLpeRPxgVdr9UkUrqZ5qWdoxuIHs/AwlgM3jC6YatsnHNiQ6XhMbbto+NwRotn1TJdkJGL3wzn2OHGA5QafXXv9G7BTJb4wXSKpt7znsudVxBROwqntUx/X02fn+anpnL6h/478X79ntMW8/tftN+CqyBKxr0xbHJcDfXBm7ZZriU0Gs87ze95H7v392L4cXBxlvRv8dAwAAAMADEMYCAAAAaFTThObAMleAOiFtCUt1exXIukFnuZn4a8UmcUNQHcSW/kO9Dmx1f1tFq9eOlaX/k4GsmSK5qZIVNY3y5hfdP+YGSbK8G/ecNz1xL4BSbarwyvRVBZSmmrCq9rQPo4NHOwanvyZAtO9BxO3DXrNaLeq077Y1z9gNWC0z1XUIS9VsriiVKMv+2u/lVCC4Mn53emIx73zeuoG+PqeP2zB2LYRVQaeItCGqqYLVn5swdlTH85/3e7W/Zfu7Fn+cKSW5+/hOrj+8PvHSAQAAAOA8hLEAAAAAWiasKBWuOcRMJgz1prhNKijV0w7ba3M4okIp3T4HWuV6HcSKE8Ta6Yl1YJULWnMbMSGrnZrYrGOr14ydcrQ6fNWhz9qan+77ztebY+V9quAzP0Nv35ua1a0W1G3FhFi2jT7WG+Na8KpCsOa30ksd14JUNb7m/XljsNeYsZ0Mr9U4dfVr+R0F81s293jvl9+Td3/xXXn6c0+7QelJXtvec4r493G+42bf+S01vxdTEVu9JyeMzUF0tzJ2ZZpi/ZevWw1i9fj1784+x3yf+2f3cvuTWzk8P2z4EgAAAADgPISxAAAAABpNRaMKL6pqUVXhqqssy3ET4JQwM6lK1WDWc81bb9rhOejSoax3Xh/PoawOU91pllUlbLlPrshVIWwJeE1Aa/vVAWMvmK3CMjUWt40XsqV2v1ehavtvgl8nbHX7EvXbEPVbUde4U92aPt0AthOi9qqBqxDVPF/z2dvX99wSitrf5lq/xtOfeyrf/IvfnJrbNWND5zdyql/7fXee3auG9Y73QlivOtZWzus1ZL21YXXAWgJVHa6uTFNcBbFeRaz6jTTV2vY96N/P3P/x5VFufnhz4mUDAAAAwMMQxgIAAACo5WAqLsGqnpY4hxiletWEtuW8qnrVAW4ON0ugqoKwXHGax1GmFM73CMvxatpYkSp0mQ6r6YllqZYVqe+fp4qtpjvOla96ymJd3avWsNVTzVbBbPVKT6RqvQDSOX8yiE3ms+6zM7VsNV2x1OFYFZ45fVYBn/4N5D71mJwgr+ln7fntcfPO9HeyWiVr3oe9j/ebLn140xT3pi5Wx0v4+Kbo35/at+dtQFkdS3Xbk9MTi2xbI/aMylivElaSSByje9z7vdtK8CZ09X6DMcnh5UFefe/VtEYsAAAAAFwIYSwAAACARhXGSR3ETg3mP71uqq2UTarSVKQJR3JlbDkXpG4vS3Ciw8+uTkVs/WD1uSoQnoMzr0K2jNlZw7bqV8zxU5xnWg0Ie8dMuFZVoupgqnPPKsj0qnXttTlIk/r+NkBtqieb7pI/nt44T41r7ZgZezlsnnf1+Y3qHwUsB5d/iGCHoPq2v5GTgX3duHvMC17zdkso2w1h1yphbVWrCjz1td3KWGeaYt3GTj/sVYJ7FeTVuzHPGA9RxptR7j65O++3BgAAAABnIowFAAAA0NDTgkoUScMSkEx541IVq6ck1kGMXktWT1NcTe2rwk4d/JaKWr12rPjhV68Ctlqb1mvjbXUlrK6Q1RW6Uq9hm8dQBdNvKFgT6YSwOnjVIZQNZXN4JfX3oAO0JuDSfengzFbNqn5s5aGtfNX3b6pOzXM3Qaltm5y/fA8xx+b7rT1j06d+LjMFs0gngJ1OLP/AQFVKV4H1mErb6rez1anfivrum3POVMX6e7Nttk5TfKoyNqVUV8SKdNeFrY6NbQirx7w2VbH3fDoQHm9GefaHzyTeRf+dAgAAAMAbRBgLAAAAoOIFGFVlbJJ63VZvP7X7ZZ1ZM/VvCa/SFHJWUyCraYWbtWLVeMv6szqIDe25pko3OH2YQLa0S7J6vSQVrJ3K13oB0IZgzQtnm0BSt7Ohpwq3bKVkN+iyfdpnsPewfZs/XUVrt03fDjcMdcbQnFfbsyp17bEHrBmbg8mqErszDr+DjcfNd2nbeL8LG9S6IaitjHWCWG96YredM02x3m/6EnNf+/s8EcTqZxhvRzleH+X46ijpSBILAAAA4PIIYwEAAAA00rhUq+WA1AZqOaC1lbDVfkwig98mxFCtxTrdOG9UuJnqfVsl621zH03Fq9SBbbe9PpafX6Q6XgJklczp49lq5aMXDDrhmhfMdqtj1X43QFPnehWyXuBlr6uOSx2i6TZN+Gp+S1Vo5rUx4/ECvur92T47QZ4dpz1evTuZ/7GA91tUv5GGrpiOy3s4qyLW8m7lvSfd1vkd2WdeDWU7x1a3uSLWhrN2HVhnmuKmmtn7zXvfp71G3Tseojz7l88IYgEAAAB8rghjAQAAANRyOKVCkaYy1gvQdLu8rwOnOTippg9WUxLr6tMSeuYQV4ep5nNVEasDsmSmK9brxCYTyIZQjaGqrs3Vsnk8YQmJc78i7fqxy2NvDH1sMx2umeNbglgvyPSm8nXHoENO77PqJ4ewTchn+zWhnqd7Xt937XXqsdln8vrcelz/Ph8oh5NbK2K7Ye2pZ+x9D71Q9lQIq9vo7yGubG2Iq6cptv/3xVTCeuPp/ea71c1mzCklObw8yHgzyng7EsQCAAAA+FwRxgIAAABopGMq1bEhzBWuuRK0UxHrVsbqirhhWUOzBKh62lYneHHXjM2BaFJBrDhVrSpsLdeadjaQrfqWukI291HtBxXKmmAoB7jty1178aYfL1gzAat9Z6WtF2CasMurZmwqZHuB7FzxWu17VbFOdaUND5sQVv0e3PehnrMbEq9Vuqpns1XfetynwmNP9Q8EpO47xtiGrN2fiQkk/Ubd/SbQnD+vTedrp/Z9cGXsORWxthrW/L57//Bg7Tmqcc5Vt6/+9JXcfXp39vcJAAAAAK+LMBYAAABAJR6ivPr+K3n0jUfy/q+/L2moK2ObKXtTe14HtFVgokJYkaV6NZ/P29U1Y5MJW0Md7DbTwOp1YXUlbB5Tb8piE87qe+Z+q3dhQjVbHepVOnYrPb1QTR3fFKjlduKcz/3qYNIJPb1zpRI2t7NhX68y1z5bkuYdueGiFwZ6bfUYzHM3wZ4et9pvxnlOcLdSOVveWVx+09V9mq7Wp7Z+ncpY9/vxKmDF/3xOGJv3T4Wx1fWixiLJH+PK778aa0xy/+xejq+Ocrw9nvd9AgAAAMAbQhgLAAAAoBLvo3z6u5/K0194Ku996z1JuyVIKsGkCslKcNqplNWhWwk9kxPkZipc8daMnZr4FbHVtMRzp721ZTP3ej098Xx//VkHtCJzwJaW/jyr09I22Vpq3klz/EQotakiVsy+rZDtHLN92CrUUjWr2jX3t89vxypLH7331Dyb6UO/h2ordftyL3WdK1eHq7VjvfPeGHOFZtW2w/t9ludy+vb2vSB282/Gfsf6Xc3P4G7tb0eHrjI/v5qyuLwPMy4vhD01dbH+fel73H50Kzc/unFeHAAAAAB8PghjAQAAALh0NV8VbqmApkxZLHUoaytjdaCiK2PnGy2BSg67bAirg1A5PeVwVc2q26mK2EpQYzP3EFmmns2fq7GEOkyuAsRSRNu5r72mF6yZ0Mke601hvFoRm9t4lY16LOZYMy2w+l14UxdX15t+q+9epBqnPW/H0ExpK05/9n6mTRPAJvW79/oSqQNXFajqKbG1q/eu5Ok3n8rjbzzeHMZan1sYe6pCtvd7Md9JE8zaNWN1Rawejxp3txK29xymMvf++b3cfXInhxeHDW8YAAAAAC6HMBYAAACAL0mz9qOkujI2hx8loAzJrYwtVaSqTbmH3YYlaHHXjDWf7VqxOSwrUxw7UxgXYblvWevTPIsOaqupicNS3bt0Z6YrtmGh9447x90AbiW8asI0HbJJHYp2p+s1fTYVrp172jC0qZbN1+ixiBqLfkepvlc1tl4Yqe+79mypva55zyqALOdWpiA+5eqdK3n/W+9Pv514uj9bWe1VB69WDHd+G9W13r79/TjB7KlQtpmiOE9LbPYlnvgdq3NeqNz84wM7lpjk8Pwg19+/bl8wAAAAAHzOCGMBAAAAdOWpRFNUYeTgBK1xPh5DmbLXTlssMoeauY2IH5bFOegcllDGm55YpA1ktVNTGpdndM7rvr3P+Tq9LyLVdMV2HP2XfDpc84LZXvCqz7mBWg5DTdVqr1LWDdxy8Gna2ODeVrBWz5DaP69iuleJW/rX70a101W63rM1fZnnab6HBwSy5b55Kl/1DwRWr9nWubvvVZGKtAFm9b7Nu10LYk9udVXsiUrYXsBvz/WewQ1hXxzk+gfXMt6M294jAAAAAFwYYSwAAAAAXw45olSBh52CWJJUVaT62qqtzH3MUwg3FbJhOZ/paZCr63TQavsqQ1iCr3xNqZBVYfCmNWPzPeex6KmJS195vM1rPCPBM003B1L6XjbsVG3cqlHd1oSe3pTDTaAmbeDahKT22exzVhevnE+mrdfehKlVCCj1c9g2bhD4mpLMoaSZOnvLNMVNlaw3rl4oa8NXe8x8v/p8twJWZKlqjerZtoSxnRDW+z15v5VeEFv6iEniMcp4M8rdT+/a9wQAAAAAXxDCWAAAAACuJEnSMUncRQlxCoXKVL4xVCGNrpSVOHcQTEAzSDmfwtyH1MfDEMr53EcJXOK0Xyprg5S/qtq23H59SuO1bXN9CvW0xyY8LFMWrwSvvWpIdzpis9+ErFKHU72pZ702TUXshorH7ueo+u9Uxlb3kOSOpfkTafo6FZ42lZfmGm/freSNdd8PVcYRRdKYlum3lbKG8Vofy47XoL2fObdpqmKnErZce+K34YayzvTm9lm60xTrsfd+9/nzfM84TiHsiz95IfEuCgAAAAB8mRDGAgAAAPAlaYKpUolqKmObsEvqY2W6YV0hK8t6rpJkCV6DmWI4V5uqylk3PE1OZaudqthWyHrHg6qAzdfrfs2Y7PP0Kh7doPYBAVsTYql9rxrSC0Wre5mwS49Vj7n6bKaXtVMFu1Pe6nt4r8Kr3q2GagJEp13zvrx76mDwVOCqKr0fqqkc1r/vh1bgrvxueu+7+/7yNb3vLpnzvbDeBLBNRa0eo/ebNc/VrQpX4xjvRknjFAQfr49yfHmcgm8AAAAA+BIhjAUAAADgSyLxECXsggz7YTk8TIFonnq1TGWsKlxL21xNGoKkIS2hZZqC0FwxW9ad1RWx6joRKWvJlilfwxyihqVStlTI5rYmtSqBbXAqYvMUySoQbqpkk9RhqxMe69Bt7d1uOt4LDr0wrXc8mfO2StVWykaz1QHbPN1sE9Ll6kgbxqlxV4GwHZsdr7TjtZ+rIFAd08ebv1iPr6ny1cdXv76Ngd/8zuIxtr+Tzu9jdY1hHWaaY02bcyti10JYcX4X9n1F8+56wardt8+UzHHvt5uSpDHJiz9+IYcXh+U8QSwAAACALyHCWAAAAACueIhy9+mdXL1/JbsnO5EkdTWsyPI5SH08nzZTGefQp6pMzVMcSztta74+V5yWylWRbphq13ytKmSdtWNtRWx+FrfKNp+TZRxVeKgCt615ndtW92nO90LOJlDTwVs+71Wfmja6z7VwVfdp+/OCQHern8mOQ/enTrrjsH10nqvp31z34EpVjwkP8z9A8KptdQB7TtBbtXfe7alAtgqyRZrv2q2I7YWxJryt7u/tbx2/M9bxepTxbpTxdpR4z7TEAAAAAL7cCGMBAAAAuA4vDvLTf/pTee/b78njn308hZIhLRWxplIuDMv0xWUt0Rxe5sq5kKZ2eQ3ZeR1YEVn2RR1z1owtW+/PtrEVskm1tfupv7VVtlUlrTqWQ9zNek17gZYOsLyAzQvbpA6yTlbEdipH9TFbNetNZ1vdy2698M6GfjnkFWna2orYZszSjqX6HTrX22Byq2YNYfVdpThVcKZjatc0Dg8IYMUZX/XzMO9TTgSy9vNaCGu3eo1YfR8x9zk1TnPe/ccGeSxx+u29+v4rufvp3fJ/ZwAAAADgS4wwFgAAAEBXGlNZkzHEZaphb73YEsSmumpUhz6l0lWvOxvqNqWCMKmAc05mSoVsbhdMH3pdWVPVaqtZ5w6X/V5FbFCVu2aa2bJ2rMgStqWl3fYXbXdNWJWc42l93w06bRgqdZC2JYi1fdrPpU9J7hibZ0ptuyo8t2GqfVdOVa2t7K2mVlZ9e4HtVqsBahIJ+yCPnj6S3ZNd+QcMlTN/Irrv3rEtYWy1r9+7fm+xfUdNZaytmLbjcMbqVh6fGmcUGW9HuX9+X+433owEsQAAAAC+MghjAQAAAKzK6zOmYQpkc4VrtWZsDljjsh5rCUh1RayukFXru+b1ZkvFqUgdyqYgMswhj6qMrfpQlbBl/VlVebtWwWgrXKu1YnO4K051rNRBlJ5C+byX7O97odZquGb2vZB19ZwJKHvhrK1e7VXGekFqE77qNt4xEwI34xM5ee9uqKv6rypxRaopqc+VJMn+ai/v/NI7EoYgcazXjM1tvED2tdaM3fo7Me/6rIpYOxVxL3iV1Bxz262EsPm/9/tn9/L8D587nQEAAADAlx9hLAAAAIB1SaYwdpfKNKE6aNWBVll/1awtWwW2ObyT5bxua0PZqs3MC1arqlXVpglX1bHetrpHUMFw5z7zB7fyb1OVrBf6pc55L1wz+940tF5FqK6UXQvfekGtDfhsWOiGhjYUlXpsVf+dd9JMM6yfQ/yxrj2LGxo+hL3XPJXvsozw/JsJ/d/EpvDXNtGv+Yywvry7qM71AlhR7TtDdCtfnfG61bROCDvejXL7k1s5vjz6/QIAAADAVwBhLAAAAIBVKSWJY5QwhhLIlnVf57Vjg8zVqEMdxFbBj62QFSlTH+fzuUI2X1NNU5zqIFQHqPmY5bU5J4jtBbxlLdmVULi8v61J31pgpc53w7X8OXU+q2O9SlM3wF0JNJu2vf70/aXu12vbDWd7favfzGr4KuvPUgSz/xBJpn/EEFIdvob6N5Gn3j7V19rx7rTW3m9FvavNFbG9e54xVi+Abcao7ztO0xG/+v6rZS1pAAAAAPgKIowFAAAAsOr+03v55Hc+kXd/9V1591fendaNnStl9TTFuSK2BDtxDprmgLVUxKpphvNUwrldDnbL+qs5pFLrysqwjG1LhWwlLfeu9tXn0mc+Z4+LWo9Wh2rOsc28S1aCNrs2qN5fm4pYfz41TXGvWtYLOE9Vo7pbae+/JYTtPUu34tX01f286WtyqnW9dnMFeRzjNG13aK958JTW00Xuvv1diDhhp7TP7YXr+ZreNMSrw+sEuM15fc84vbfrD69lvB2n7/SQCGIBAAAAfOURxgIAAABYdbw+yvH6KFfvX8k7v/zOauC1OkWxWQ82nyvHVRu3IjbJFGqlbSGsWxU7r2ebpx0OYblfCur4XK2Y0lzVOI+l9BWWwMo7po+fRYdqaj9/tsHs6lTFdgphkTqwtPuqfz2VdLeKVId+5lg3CLdb73ltCOtc30x1bYLEfL78P2/64vnZemHo1pB0dSrlPKV35v0kzvmZdELYMo6tQaxI9V16of2m4ZwKsddC2Lw/h7ApTv/I4+6TOzm+YlpiAAAAAG8PwlgAAAAA20SRNCaJxyiDDBLHaZt2c5DjVbVGkTCEMkVxVRE7SD01sTNNcdnmz8H8DfN6rnmq43y9qp4tfZhrS/A7B7LlXOpsxRwTKeFuNd7gh8VdXm65Ulm4GsDmsK0TvJ2slDWh61p1bBVsmiC0Cvk6YW9zfVwJfdVxGyKuVsRGKesa59CvXBdT9R6WV31GGqm+nxz6hiHIo/cfTdNxjyrsLUsLm4rYLWFsb0hJ9bMWxIq433l1/oHPvXbcG5v9fdz++FZuf3pbzo+34/njAAAAAIAvMcJYAAAAAJvoAKsK5vLasbm6dZ6qOFfIluNzhWnuqwnOpP3sVcSKTPu6ejZXtOr72EpWXXXbBE9BmmrZ6XBo+injyM8sZt1PHZBVt+inbs1UxPldeOdt2Kbvmer2TVBr3nE3YBVzrRd46rHY8W0JYiVVY7Cf9bPbYNH+bvTz6vs1lbEi3d9e9Z3pZ7Q2HA+7sKyrnI/1piVeC2PX8lHv3evjW8PYN6X3Hs0YUkwSD7F8X4dXBzk8P7zhwQAAAADAlwdhLAAAAIBNcogShimgLBWxeTvM2zAHmnNIG+IcXOopW711IL3pXPVWVcPqdWdzBW61VX95PO5asV7Fq723PS/OZ5E23Dqj4rE31W3TRudcG6co7k3Pa4NSrwLWDWo7/XdD295f9NuXqWvVmKpxxOVz+ccAnT66lbGxDm3d76/6CjpTJrcNJaWpejwM6gcQVBj/gNmrvftuDWGrZ7wE27X3e1Xfz+HFQV5890U5Fw8sCgsAAADg7UYYCwAAAGCT8XaU+2f38uiDeQrWTuDWW1e0qWRNZn3ZoPoQU+2aK1HVvg2YqulfdaWrXjP2RHVqWUPWVNd2K2JVuzKOuQKyzVdX0z73mBcUroWwdr9Z99UGp1IfO/ldmvCvNy2yfWZ3emL7rOqYF/A291HjcQNh/f9MZaxbidzjtLNjaL7bHDTn/DWY3+tDA1n72mzwKmY852awp8a1EkI3Y8pjiUnG23GasjklOb46TlMRXzAfBgAAAIAvE8JYAAAAAJtc//Babn58I9/8K9+Ud37lHRmO08KsaZxDrjwlq1qTVYJIGpKEOK/rGudgKkyfo0QJEmSYF3lNIU2fw3wumGpCu2ass4ZsCXm8ylZdKWuOu59tX94asmLavE7I1Avb1Lmm4nBl361ktQGtnZ44qtCyVxEbOwGoN7Wx6s+9b2zvkz971bl6X1fGNuPoVMammJaw9OTXodZ89b4fHXqq30U8ztWe6nezuTJW/+78QS0fNzxD1a+sT5e99be7Nq12VYEdpyrh53/8XMbrcWlHEAsAAADga4QwFgAAAMA2Ocwal7VjS6iVp4CdK1J1MNasITsHOblSVodnueK2ux6sqoit1nMNdQiUt6X9HIZ114wVWapi7T3CPHaR6ni1r9fDdSogmwDMCaO61YxeCCt18Fn2U3tO99sNRGXZ2mmNvXVim4pYM14vnC1bfT8bNjvBq/5zz83vpje9sv7rhqnNK3fGvdI+v9MyDXJYfpciUqq6yz8C0M6tkj0nyLT/IEH83+eD7tn5LaaUZLweZbwbp3dyTBLvYvlHGwAAAADwdUMYCwAAAOAsKc4ByzHKIENTGZtC8td8nddzDVPZ69ROy2vBmuuaClldBZvZNWOdtWPdNWK9/dTZijkm5rjel+V4L/xtdILN8tGZktZduzUfX/m8Wh2bQ9mo+vb6c/px++pV4JoqVq/6tRfAVlWzujI2tseq40n9Q4LqNasx9b6e3jl9n2Oq1kb2KrsfvHZstQRtaK93c94TN3lAPtpdk3Z+zykmufnxjdx+dHt+5wAAAADwFiKMBQAAAHCWNM5B7DhICip8mgPQlKZpiUtomeZz8xTCtjK2TCkrUk8zrNuooLGEm6qStlw3OFWrThiqq2qrbQolJLbn8jHbt97PYa0OwTYFsflaveusq1odL5vkBmNVSOaFpvqcE5auBrupvW8V2q79Oe2b6l2nv6bC1TxXXgu2VxHrVcaufQ/6e+t+h+q9pJQkjrH+hwDSbpsK2U6ourq+caj/e3CnNlZrG7+2VH/O3308RLn/9L4KuyWJHK+Pb+a+AAAAAPAWIIwFAAAAcJY4RomHKOlRmsLYXBm7m0LZEJepgd3K2BDKtK2SZFlPNoe2ub2tOs0h15aKWBXOlj7me1eVu0kdU0ooO19XtrLy2V67ldc06Y+pG4Z5+2vrw+rPW6pjdYDqhqBijtmQdWW75Zg97h2rqmCjVO2q9mpt2rX335ui2L7T5r1EKVN457C1BPumQtabxjgr1+b/hvL55Ae03j80eGOS/z7yM4+3o7z6s1dMQQwAAAAAKwhjAQAAAJzl9qNbOb46yvu//r5cvXclwzhM4WqepngoJXvVdK0lcBWpq1plDjBzhW1Y2lRh1GCO29A1OW1slWJmQ9ik2qkANq8j6wWyISxr2eYxlira18mmnLDQW092y5qxTTBrQtYSaoq00xI7n6ug1Dl2TiC7dmzteDUtsQ5yY2q3MbVTFntfjj2u3qP7fer3KCLj/Tj9A4VxHk8O/PXvRJb90mb+TYW0BLN6HGE5OG3M79WeL//IQR9bOnOf22V+Uykluf/0Xo6vjtXzl2mZAQAAAABdhLEAAAAAznL/2b0cnh/knV96R/ZP91MYM0oJU/M2pFCFZiHNa8UOqYSvpQowqemAbYCaKwLzOrODmap4DkyraY/NdMU6JA2Dv4arNy2xPpf70m17571rt+pNT1ydK5s6GK1CVHXuZDWsCvBOBrH6vl44q8432xOBrDfNcXNc/MC2V2lbTVdcvdZUbR9CT9c73o0ShuW7zgFr+d2F9jdkx7J6fA5xvWeYL1z+W/EHe/I5qt+S/s6iyP2ze7n/5L7fCQAAAADARRgLAAAA4EHiMcp4GGU4DjLIsExXHNTWWTezTGM8yDJ1sUxVqmFQoavKlFKYgttqutf5r0z5aoXlOt2+mQo5mH1pt+60sjrc6qzZeVIvXLN9JeecCVx7Aaz+3ISiNlRV/bvrw6ZOv2dOUWxDVlvteqoytqmQNWuW2mrYKiTW716/Z/WO89qzvfdc/uJ0rzTOVaJDqqbhLuHsht+Y/u+gUOvHNlNfez/54P9DA6sKpvO7PSS5+dGNxGN0f3/j7XiyXwAAAABAizAWAAAAwNmSJBnvRxnvRtk/3k9rx+bK2JSaaYd1JWyerjhXzurKQR0ShTgHUHP1qwx1KKYrWd3ATLWxn7fwrvUqZPMz22v1u9rwQt1jW6YndkPYfHxtumJnPVk3lPUCW+c6fZ+q6tT27YW3ogJQE6L2Kmbz/bxqX7cq1r4vO06p+64kuzvfa0wSD9MayvEYl6rrQQWowXwewjJFt5rKWJLUUxfPv2tdXavDWb1ffSf5s13HdSWQTilJvI9yeH6QeIgCAAAAAHhzCGMBAAAAnC+KvPzuS9k93cnP/uWflf27ewn7ICFNQVOUKMOUSImIlICqqe6Tupovh7VBgsQYl6ld58pUXRmrq15TWqY8Xjo2bXSV7Hx+rTI2SaorcWUZv55ytrlvHm86Y3piJwDsTVfcVDV2glh9rglenfB6c2WspFJd/KDK2LiMpalkjbJUvYo0VbB2PVh9bbXNFasx+WuaeoFrOVW/Tx1e2lD75qMbufnRTVkrVkTWK6KDyONvPpbdu7tSNVvWLs4/NT2dsTlWxuH8d6SfLcUk1x9eS7zbFqzm7yEeCWIBAAAA4E0jjAUAAADwIOPdKEmSxGOcpmnNla+qAjZEFZameW3X+ZgO93SVbEihhJw5JPKqX92qVxUudtsodp3YqgJWhbw5hDu1dqyIqnJcS/w8KxWZ3rOdnKJYZD1Q7YW751bGJmnvm0PgTuDrVsaa4NO+Q69Cthv46vtuecf2Xr3rdR9pXiv25rzpe8e7UcJ++n3ZfxxQTYU9H1s+hvWgVw9xTDLejpvDWAAAAADA5RDGAgAAAHi4NIVLw36Q4WqYgtNcGRsGiRIlSJAhTOeqalSvklBVAupwtKpUnfup1ucM9bkyPNtGpumO873ydSX8ndvoNWL1mrR27VivMra7hu3G92n3t0xV3Fvf1E5J3Atv3RD2Ta8Zq6pd8/GU0lINqz97lbK5f1XxateQ9bb2PZatmVK5eQ71zPZZS8XtA7LO+0/v5fDZYXOw+lDNNMUAAAAAgC8EYSwAAACAB0sxyeHZQdKYZPd0Vypj8zSxYQjlswzzOrDDUv065ayhhFy6AjakUKb7LdWzsoRnuS8Rp0I1yrJW7bw2Z1nPcz6nr+ttRaSMw97HVsn2jlleUOsFrnZ/bXpiN4TNx1eCW7eN7csGsXo8Saq/1SmK1TV6fVgvyK3CWrPvVdd693Tfb/vim/NuW3Mo3kU5vDzIeHdeVWzuq5mCGgAAAADw1iKMBQAAAPBg6ZjkxZ+8kKv3r+TRB49ERCTsggwySApJYpjXjh1lCqFCWrZ2zVZRn/V2kCW8VddUFaiD1P3lNim168raPrZUxqbQjEtPZ1zeh/hryFbvbCWodU+pqk43nDWVm/acN8WxG+Z+XpWxpgq2BK2xDmirds5asbnPUjlrqmi9wLNZQ9d7Lj1m5x2lmOT+2b08/6Pn/vcFAAAAAIBCGAsAAADg9cyhWDxECbsg6WoKYcMwTQmcQprC0rldGEJdNTtMgVeIc3tVISsiUxtdIaurV2Oq1p/N98lTEZ9cMzZX34al+rYEsvk6VRlbXavXqc3rfYqUSlyv/fprbCs63fBVHT9V9bppemIVSDbXmJDWC2g3BbErFbG9ClhJUk9hnOoxNdMSq6rZ6hXad7CiCcqdYLoEywSxAAAAAIANCGMBAAAAvL4oMt6O03qxj+b1YocowzCU9WMlyRJqqlyyhJrD1E+uTC3thuUeXvVrk3GGpW1ZS7ZzXamATep4knptWG99W7udg8ZSFesEdaWStqMKApM0n5vq2LVg1V57qTVjVbVrN5g1bdwgtrdWrL7GWSe2VNdGZ7ze+1XnmoA4t7NryarrU0qSxlSeCQAAAACAUwhjAQAAALy28X6U6x9cy6MPHsnwaEpPw7Cs6ZrD1RK2mUyyBLL5+FzhWqphw7zWbG9t1xzUqsDWBrFrwWw1TXFQ4awOaUWq49X0xSJ1aKuPbeEFiHbXqfB0qz7PDWVXQthqX31ugs9otk4IW4WzXhDrVMo20xTraYjNtlwrZrze+7HTFJvndJ8/iow3o1z/8FqOr44CAAAAAMAWhLEAAAAAXls6Jrn96FZSTPLkF55IGIKEfZimIg5SphNOMZUKWBGpAtQQQz1lsUg9PXGcw9KhE8gmqaYpDmkJg/sDn+9tpykOYZluWIfHat9OX+xOZ5zyZfPzOBWb7ZBMkKr6EemEp522qyGsqnZt2pjz3amKc6hptyogrYLYTuBqQ9mT0xTHfn9r73Rz4K3Pq2cYb0e5+eFN9z4AAAAAAFiEsQAAAADemMPzgzz7F8/k6S8+lXf+/DsSJEhIoVSbpmCqD0WWaYgH1ZE3HfAgZZ3YXMWqq1tDCNX0sTlclSBTVe08hXCusC33U1WzVYXsPE1xNf2wGk+Zdjgs9xORpa3ihbDeGrbqgma/mabY+bzaRges6vjJNWL157y7ZXpiO01xryJWH7fTFKtpiXNf1Z+estg8nw2I7XvRVbS2KtY+w3g3yot/9ULGm5EgFgAAAABwFsJYAAAAAG9MvI9y9/Gd7N/dSxqTxHFaN7YK5GSewjjNlahz1WypiA1LqFbWcxUp4ZiujJ0O11Wy+T45XBWRUlVbKnBDWqY0lqX/Zjpi6Yem9vha9au3VuxaENtdP1btbwlnRTphpAlavXbdalipg9hTFbD5mt4573Ovjf2r1opdY8bdrRT2Lk1J4jHK/Sf3Eg/RbQMAAAAAQA9hLAAAAIA3Lh6jHG+Osp//fzlCCBJ2U9AahimYDEOQKLHsS5KqWrXZJrO/8hdETTOsK1/nbZBlDdrV9WOlXyWbpyQuIazZ1/nrlumJyzvYetxUebptbdCaVo7rylL7eW2a4q1buzasrnZN9WdvXdiUkqRRVcPOf8ujpnrcou6vK2Cdd9M8q96a+wAAAAAAcA7CWAAAAABvXDokOV4fJeymELZUv8a6SjJENTWwCvmChKltXoNVT1Oc2krYcl9VJTtd1q4vW9olp+I1tevHalU1rGrrnje86tj2xS3P0Tu3af+cEFakri7dEMQ2lbI28FzZ2mP6um4bWwWb2mfqPqP3bpL6HXrvMj9vTDLejTLeje16sgAAAAAAbEAYCwAAAOCNu/v4Tu4/u5f3fv09efoLT0WCyLAfpqrUYQ45B5kqY1Mo2xKyeZWxeZtkqqD1KmLVWrK5qtWtnpXlPlUFrK2IVVWwpT9Z1oVNstxL709DNUFxWA/zqimZe6p80QkSbTApfiDbXTvWBKy9MLa0ic62F8aesWZsHONSlWoqYatK1TwO+07U+/CmJ66eSdrny/eMhygv/uiFHF8dJR0JYwEAAAAA5yOMBQAAAPDG5cDs+PIo94/v5fHweAo6x1TOi8i0bqu04WY+V9aWnYPKUuWa14Qd6qpXryI293tq7dey1RWxc2Db9J1UCJvqoLW7dmxantN9Z3XSuvJyV/bLsNuwVUSFqbqtnarYhq/6s2nT21bVq16Vq9OfF+LaINetiM3jU89cxjMf895tNT2xLG3yPY/XRxlvRhlvR9aKBQAAAAA8GGEsAAAAgIu5/uG13Hx0I9/4S9+Qx998PFXIxqGsHTuEYaqEnCtjQ6rXW01pCkerNWN1VWymjpXqWFHtvMrYtf1UH7drxeo1ZEXqkFW3reR+z9W7xglh7edNVbFekKlCzyY8lSVMX52CuFcJ21kztqwdG8WtiC0Vs/o5zLPpZ/bWi/XG2zzzPKaXf/pS7j65K/9gAAAAAACAhyCMBQAAAHA5SSSN03Sv8T7KsB+m4HWcg81RhaaZmXa4hJ5JlqrYYa5ijfN0wmmuoB2W7XT7dp1YG+TaNWV11etapa2udK0qZUUFhVJPV2yrZc9ZR3b1mBdOqs+ra8d6bVK9X4WY+n45qMwZqQ1g7bVJmnWD9fTEvcC2urfzHkpVa+cdrK73qkPYtITAEoUgFgAAAADw2ghjAQAAAFxcvI9yvD1K2Idp7dghlGB0SMPUSFUqNtWvtnpVf17LM+16spKmvpNzXLdNau3Z/Dlfk/PjsAS3InO73E9SwWxoA8LuVMZrToWy6nMTvs6fV6tizXlvWuJuFay37VXC6sAzSbciVl+jK2LLezMhsrserAphu1Mkixn7mCQd03qACwAAAADARoSxAAAAAC4ridx9cifj7SjDbhB5IhJ2QYY0SApJ4i5O0xVndvrhfMyEr2XN2DkEDWGqmpUgU0Wjrm5V4Ws+l8dWAti5KrZMc5zqoLaEr2KC2Dl41aFruS7fQ9qpjNc0FbM6aF2pgG3arlXE5v21EFYHtd6asdHZbgxly3EdynbC2PpVqLH23qMeo7p38x5MOJ1ikvtP7+X+2b2MN6PfNwAAAAAAZyCMBQAAAHBx95/ey+HFQR797CORQabpilNcphO21aM2Y9NVq4MKXedzZbriIUyhWg5pc3VrcEJZXVWb2n0dqHanL05mGmUzbXG+thzTzyP1ea0KCduT7n5zTS+U9QLZvG/PJ2krTb1g1tv21ozVx2NqAlvbphqTDWJtYKyPmWeqjul3ocaYYpK7T+/k5oc3nZcPAAAAAMB5CGMBAAAAfC7SmOT6+9eye2cn7337PRkeDdM0xXEqfw0pTBWzMtShbJK6Qjbv58/B2Vfhaql0zUHq/Fmfs8fLvcw0ydWUxno89rO6xt1PZ05V7DVxgtduIJvMORvA6s/OfjOV7wPCWFvtWqpgTVCb/6qwdHnA9jltOKunK9Zh8MozpZjk/pN7ufnxjRxeHk5/HwAAAAAAbEQYCwAAAODzkUTuP7uX3d1Oxl+YpoAd9oMkSZKGJeAsVbKm8lVEpnaproTVlZE5tM3Vq7aKNQevy5BMGz1VsaRuYFpVu4Y2WLX3yM//oKmKTbPXnaa4el+2Tar31wLZU0FsFcqq/ppA1wSxzTUrQWwTvEqqj1cvo+2jBMHHJIdXB7n9ya39GgAAAAAAeC2EsQAAAAA+V+PdKM//8Lk8+uCRvPdr70l6NAV0Qxwk7KcAMgxhqpTN2zBVzYYYRIYplA1hmpLYrYjtrTUbZKmEHZaK2BCCWxFbzpnjZV/8Y3kNW1fncJcXxtoc1wtjnSDzoWFsE8rGZSsyh6/5eC+YPTVNsVoz1g1enRC2fHbC4pRS9azVu5jHLVHk/vm9vPiTFxLvogAAAAAA8KYRxgIAAAD4fEWR46ujhH2Q8TAu68AOSWRcKmNLdWqYz0WZQtZcGTuoUDLNla85YI2pXfPVVLqW6lpTRdtUyurrUttv7k/vnzqWeevFNm29AtpOGOtN35vHXY71QlgbXJrqVx3Uulsb3qp1YL1+ylTEZr3Yk9Ww6sGaIFZMRWzveWQKf8e7UY7XRzk8P/jvGQAAAACA10QYCwAAAOALEQ9R7j65k/27e3kkjyTFJMN+KGGrJFnC1yGI7GQK7oZUKmZFVcQmSctUxPP0xWGoq1qrKlhbCZvbSNumTG+sz5vjIvW5YqUS1k6bvJxYeXFbgljdLplzOozVbZx1VfX+pumJTeWrJGnXiHX2q37sM9kxS92uCYZzle5KsJxikuOro3z2Lz6bKmIJYgEAAAAAF0IYCwAAAOALkcYkx+ujhCHI+Hgs4WcOUHPla7WGbJgrZqNKz3LeGub1XiVMVbRBqgrZqip2Dm2bHDRJ1V6vCdv0oati07IebK9qdhmuumkOC/U4TgWDnTBWf9bT+LqhbOdYtyI2V56uhLBV8KrD2V5lrDrnBbH2c3f9W1HbDdW+KSY5vDzI8dVRxptR0kgSCwAAAAC4HMJYAAAAAF+IeBfl9se3U2XiIHL17pXsnuymYHM3B6u7zhqyQ1hCxVwZG6aQ1a2A7RybOnf+RNz1YteqYm2w21S96urZsxeOFT+kPSO49I5X67NurYgVKWG4F77qqtdNlbFOCNusCes8lzdF8paK2Hgf5fkfPJfj9bEO9QEAAAAAuADCWAAAAABfnCQy3o1yeHEoQWkIQQYZpspYmUJNGypWdGXsXGVapinurP+aUhIZpDpf+s8VuKoyNp/L1bcSZKmu1cdlubaq6pWpfT5vq2WXR+mEtE5zt/rVfO5WkzqhqxvC5mtOVcR6lbHOmrG9Stll6El649dTFleVsPN+N4iV5X73n93L8foo4/1IEAsAAAAA+FwQxgIAAAD4Qo3Xo4zXo6TjUiW5izsREQljKJWyQ5oC2iENIoMsa8cOS/BZKmPTPD2xVw2b/1TwOt2s/qvWjhWnrdpWa8XmayW0bcyxTTqZ4YPD2F51bKea1AaaTfgq4q4Taz/rithqHLISwppnKOOw4zlVEZuSpDHJy++9lPtP70+/cwAAAAAA3hDCWAAAAABfCuP1KPfpfqpIfTpVnw67oVShikzbKHGaqjiVlHPiBaUrfzms7bUPos7r8Na5l1cZW6piy+H2mMhybjq8Uq3pnTIVo03bZIJOG8Dmz/mcDVzFOXaqIvZUGKvvvRLC2rFVAax6hi1B7N3Hd3J4cZDxduy/XwAAAAAALoAwFgAAAMCXwvHVUY6vjhKuglylq2nd2Ks0TSccl7VjBxlK4DYdVNtktoOcDGXd4NULaO29OtuQlumSpRwO9XTLYs6thbCWbeoFslvC2HzeC2NlqXp902FsdX/9GCeqYfXxc4JYiSK3P7mVmx/dbHzBAAAAAAC8OYSxAAAAAL5UDs8OMt6MIklkeDwFr8N+EBEpUxKH3TQ9cUj1VpJU0xWXUDYHs+JsbTg7HytTHEt7rhvGynxdb4piZ3riavriU5wgNvextTJ2bc3YJtCUTih7xvTEEtuqVj1OHUS7UyjbbSegbYLYmOTukzu5/cmt3D9jamIAAAAAwBeDMBYAAADAl8p4Pcp4O8r+nb3s0k52V7tpDdghiOyknerXbnvTFefPsd4GmacY1lMXJ+c6NV1xCKb6tVcZm6SEsE1gKlLC3m5lbK9gdksouxbI2gDTC2GTOdariBWp1oN1g9kTIWy1/5BqWP1c+d5jksOLAxWxAAAAAIAvFGEsAAAAgC+fKHL38Z2EIcjd/k527+zk3V99V8I+yO5qJ2EfZNgNEuJcGRuXClkJUwVtCNOxFOYgN4ewc0VsGqaK1Lxdq4bNx3PVa57SWAetVbCaz8kSHttq2dJWn1uzFsDa817V6LxdDWJlqmQt164FsbrqNYq7Lmwz3bAZvx17r+LVVsiujjsmuf/sXl5+7yVrxAIAAAAAvnCEsQAAAAC+lOLdlK6NMkqKSY63R9k92pV1Y6PEaf1YkWVKYpmnMhYVgg5hCglNpWuIc7g6b0XmqlYVtJaK2flzqZzVbXOl7BwUhrBMlzwNRK0ZK8s4S1t5g5WxugLVq4xdCTrdKliRepriE9MT24rZJjD2jq0EsWXbCWn1uRwOj/ejHK+Pcv8pUxMDAAAAAL54Ian/v/VHjx59kWMBAAAAAN8gMlwN8ugbj+TpLz2V4WqQYT/IcDVM4ex+mNrs5u0wbXOFrAxSKmXzdMQyzBWpOc8N6lyoj1X74rQRqSplq+OZqpbVx+pdd1FZlxu8qs8nq2JlCVP1503TE69MUzx13wbP7jH1DF7Va3e9W2nHmWKS48ujPPuXzyTeR4n30X9xAAAAAABc2P398g+EqYwFAAAA8OUXp0rZ8WaU46uj7J7upuNhCmBjiFWlalWJmStbh7RMVRxUtWpc1oDV1bAlF02mvTpeKmLzWrF6vdm03F/ErCUrqg91oFshqzlVsU3Q2Qkwu1WyOqiNdRsviPUqY0tf7pBTM+5TQax+Dlu9qz+nOAex10c5vDrIeD0ulbkAAAAAAHzBqIwFAAAA8NUxrwf7+Ocfy6NvPpLd491SIbubKmTDECTs5jVjd6FfHaurZHVFbP4sUh8X53j+7FXE9ipkO8c2cYJY77NbFdsJYe25TWvF5q0ObvV9vKGboLYXwLrrzHaqYSWJpDHJeDfKZ7/3mYy3o6SRIBYAAAAA8MWiMhYAAADAV1MO325GOb44TpWuj2WqkE3DFN7N0w6XbVKfg7M1f2VdWLNubNU+OZ+dbbVW7Hx96VPrTVfcyRVPTVG8pcK0+Zx39dTDYgJQWxFr1nZtB9qGs701X5vxrVT0ppTk8OIg8S5KiknG+1HifSSIBQAAAAB86VAZCwAAAOCrKYg8/vnHsn9vL/t39jJcDbK72pUK2byGbBjCsn7sUFfGVlWxg6mSne/RrYL11pGV9njZl3q/OmaOi0g3iG3OOaHmg6cpPrM6th1Wsgfq41sCWDN+b4wpJXn2B8/k9qPbbe8LAAAAAIDPEZWxAAAAAL76ksh4M60PGm+jhH2Qxz/7WMI+LNWwScq0xZJkqqTdSalcTSGVcFZXu6aQqumJc2VtXlu2qZSV+Ro1XXEVrnY+53VqH/Ls9vPmMLZXEbs1lLX37IyrCVbNGG1176ZK3ihy/+JeDs8PMr4aCWABAAAAAF96hLEAAAAAvrKOL48iL6fP4SrI8GiQ3ePdFMLOAWwYgoS4VMWGZCpjhyWU1SFs+SyyhIPzlMM6qNUhbJLlmqqtSAlxbfiqpzHuBrNeFeqJqYqr4LQXburPW6piT6gCVW+cdgxrlbBmbCklSTHJ3U/v5NWfvTo5FgAAAAAAvgwIYwEAAAC8FdI4BXW7xzu5+sbVNG3xo2na4rALZcriEKfgdNgNVWVsDmfLMZFSEZuiqZRVFbEnQ9i8rwLbrIS9+RnWSj29U6dC2Hlbrbd6YpriXhVstxrWDsmGtk7o2oS2nbGnmOR4c5SbH96U9WoPLw6r9wcAAAAA4MuEMBYAAADA2yGKHJ4dZHw8yvB4kDROid6wG8rUxbkyNgxBosQpDM3TE+dpi0OQlOptCVxzSNsJZavtqWNW73gv+1wLM/Oxc6Ypjqne5nPeuJq89QEBrHfOBsQxyXgzyqsPX01TTAMAAAAA8BVDGAsAAADgrRIPUe5+ejdVu+6CXL1/JVc/M1XKhl2QYT+UdWTL1MXzX66QlTBVrYZhXiN2mCpbU0jlXA4pT1XETgfVFMRBHw79ENbTCTP159VA1n521owtl9rK2NVhtW3XwldvnDYYHu9GefmnL2W8GQliAQAAAABfWYSxAAAAAN4uUaYAbxZ2QYbHw1QZu5+SzzAEGWSYQr7dFASGFOpK2UGWyth8TP+J89nbrn329tecCGNtFWw5btdp7YWxZw3FCVu9fuyus9ZtHkc8RpE4h7G3o9x/ci/xQBILAAAAAPjqIowFAAAA8FY7vjjKeD3K/r29DE8G2T/dy7AfZHg0VcgO+6GsISvDsnasDEsFrK6WtdskqYSypXpWZKmcddaKbapjneOVrevFrkxJLKLC17Wpg72xeKGrtKGqe94Zpx1LSklSTPLyX72U+8/up/MxTeEsAAAAAABfYYSxAAAAAN5qaUySxmna27Im7H6eUni3rCUbJUqIYaqUDVOlrAxS1pPN1bIiarulUtZ+VvtBQjekXR6g92D1Z68qthvG2utP3NdbE7ZtvhLOdqY/joco8T6WMPb46ijHV0dnMAAAAAAAfDURxgIAAAD4WhivRxlvRjmGo4QhyNU3p3Vkd49301TGV521ZFWVrK2QddePVWFsVSk7H9Py+rKbOSFnU4G6FryeU3lbTp0xffHatMVmXCkmuf3Jrbz805flHJWwAAAAAIC3DWEsAAAAgK+HPCWuTFWY8TaWqtlhP68puwsSYmhD2RRKtWwIoVTYSpD6s6QqkE0hlWmKCy8QXctkO1mot/Zqd/rhLXnq66xdu2FsKU3v+vDiMK0Lm5IcXh4k3hPAAgAAAADeXoSxAAAAAL5+ksjh2WH6HER2T3Zy9bNXUwC7m9aRDbsgw26pls1rx56qkG2qY3X1q/4YwnlVp9IJYOfPuq/uOrRrUxOboTQhsjeG5qTTLofgMcnx5ijP/sWzqQL2vEcHAAAAAOAriTAWAAAAwNdbmqbHHV+N03TEaori/bv7afriaMLYXWgC2DRMVbBpmNejnUPYFFKZsliHm3r64rBSlrplvdbuNfN6t6X/XjBrz4lTXdvepNmvpkxOUyB7/9m9xLtpXdh4P1UjC8WwAAAAAICvCcJYAAAAAF976ZCWSlnl8S88lv07+6USdmfC2KGtkv08KmS9fpa8VQWvetpi9/K27Ulen2qa5FwRm+I0LfH1h9dy/9n9xs4BAAAAAHi7EMYCAAAAQMfx5VHiXSxB7NXPXJU1ZEMIIsO8LuwQlgrYOXQNQ12N2oSzsqwzW7XrTA+s25T+nAA1TaWwdUVsvrYpst2YwHrNkqqeTSLHm6Pc/fSump44pWlqYgAAAAAAvq4IYwEAAACgY7weZZRRRETCVZDd052EfZBBhimEjVMgW6YxDqFMP1xVyEo7XXEOTUvF7LxJIbWBbDDBqpg1YteC2dy3DWfVPbdOR2ynIRaZK2GTyPHVUV792auVjgAAAAAA+PohjAUAAACADdIxyf3H91P4qgLU/Xv7MpVxGtJSAav/5rYlrE1SAtaUVCXrHKraNiKqCtVORyyy9CHtufniaROSe7z/0OrjHLqmlOTmBzdLxevcJt6zECwAAAAAABZhLAAAAABskUTGm7E5HPZBhkfDtJ7sXCkrItM2LNsgy9qwVcC6st8cc9aBtcf1uWqcnepZ91FTkhRT3X4OYtOY5P6zezm8aNfYBQAAAAAANcJYAAAAAHgNxxdHGa/HdirhIDI8HuTJzz8R2U1haK6aTcGpoJ2vCbJMdazXmy1rwYppa4+r65bd4Aa0Iia4Veu/vvreqymQbS+Q8bYNpQEAAAAAQIswFgAAAABeQzomScdO0BmTjPejhCFIGNQasnMAq6c7DiFIuNwk0R0AABbUSURBVFJVrkEkpDm8tVMZ5/71Gq46DFbTGed2KXbGmdpAdrwZ5fDyIMLMwwAAAAAAvJaQUir/f92PHj36IscCAAAAAG+XIBJ2dbXs8rEuXx2eDPL0l59O0x0P07kquM3TEat+qmNrksjh1UGu/+zar3a1zVOSdNg4pzEAAAAAAKjc39+Xz1TGAgAAAMClJOlXzdppg4PIeD1O4a2uoJ3PhSWBrbdl1wlmVdXreD3KeDduXjcWAAAAAAC8PipjAQAAAODLQq8f+6bN68ECAAAAAIDLojIWAAAAAL6MCEwBAAAAAHirDF/0AAAAAAAAAAAAAADgbUQYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwD///bt4AQAEICBGO4/dJ3iECSZoP+jAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABA42/Z6BAAAAAAAAMBvPGMBAAAAAAAAAmIsAAAAAAAAQOACaazXSuW+u20AAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" @@ -894,7 +884,10 @@ "source": [ "## Step 6: Add background\n", "\n", - "As a last step, let's use an image for the background instead of a uniform fill. The only code that we need to change is the code where we used to return `bg_color`. Now we will determine a point in the background image to which the ray is directed and draw that." + "As a last step, let's use an image for the background instead of a uniform\n", + "fill. The only code that we need to change is the code where we used to return\n", + "`bg_color`. Now we will determine a point in the background image to which the\n", + "ray is directed and draw that." ] }, { @@ -905,7 +898,7 @@ "outputs": [ { "data": { - "image/png": "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" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -1007,7 +1000,10 @@ "source": [ "## Next steps\n", "\n", - "We've only explored the basics of ray tracing here, but you can add shadows, reflections and so much more! Fortunately these are explained in [the C++ tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing), and we leave the corresponding Mojo implementations as an exercise for you." + "We've only explored the basics of ray tracing here, but you can add shadows,\n", + "reflections and so much more! Fortunately these are explained in [the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing),\n", + "and we leave the corresponding Mojo implementations as an exercise for you." ] } ], From f19999556faa549a41fabefc004bf7d98873d725 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 2 May 2024 23:34:40 -0700 Subject: [PATCH 0395/2019] [mojo-stdlib] Move reference.mojo off deprecated `-> Self` init syntax. (#39237) This is split off another patch. MODULAR_ORIG_COMMIT_REV_ID: 7729f3884776e85c6a858d3eac42468a65b9756d --- stdlib/src/memory/reference.mojo | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 4ae88a8e3c..03c1658fac 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -44,8 +44,8 @@ struct _GPUAddressSpace(EqualityComparable): """Local address space.""" @always_inline("nodebug") - fn __init__(value: Int) -> Self: - return Self {_value: value} + fn __init__(inout self, value: Int): + self._value = value @always_inline("nodebug") fn value(self) -> Int: @@ -119,28 +119,22 @@ struct AddressSpace(EqualityComparable): """Generic address space.""" @always_inline("nodebug") - fn __init__(value: Int) -> Self: + fn __init__(inout self, value: Int): """Initializes the address space from the underlying integeral value. Args: value: The address space value. - - Returns: - The address space. """ - return Self {_value: value} + self._value = value @always_inline("nodebug") - fn __init__(value: _GPUAddressSpace) -> Self: + fn __init__(inout self, value: _GPUAddressSpace): """Initializes the address space from the underlying integeral value. Args: value: The address space value. - - Returns: - The address space. """ - return Self {_value: int(value)} + self._value = int(value) @always_inline("nodebug") fn value(self) -> Int: From 368168d6107af4d946a0feb4b9070def7088a3e2 Mon Sep 17 00:00:00 2001 From: ARVIN DAVOUDI Date: Fri, 3 May 2024 05:48:27 -0600 Subject: [PATCH 0396/2019] [External] [stdlib] Complete most of the remaining methods of `Set` (#38990) [External] [stdlib] Complete most of the remaining methods of `Set` I've been working on this for a while and I believe this PR is a big step toward reaching the point that our collection datatypes are almost identical to Python. Long story short, First, let me introduce you to the new methods and operators that are being implemented with this PR: ## Methods 1- `issubset` 2- `isdisjoint` 3- `issuperset` 4- `symmetric_difference` 5- `symmetric_difference_update` 6- `discard` 7- `clear` ## Operators 1- `__le__` 2- `__lt__` 3- `__ge__` 4- `__gt__` 5- `__xor__` 6- `__ixor__` ## Renamed methods - `remove_all` renamed to `difference_update` ## The EDGE CASES: The methods are designed to replicate the same functionality we get from them in Python. So, This PR assures that we have no methods/operators left that are not coded yet. Cool, right? But there are still some points that must be considered. I list them below: 1- The only method not implemented yet is `copy`. I haven't implemented this because of the differences between Python and Mojo and the related concepts of shallow/deep copy. Any suggestion is welcomed. 2- I have observed `pop` method has slightly different functionality than the Pythonic version. In Python, `pop` method works by randomly deleting an item, which is not what we have in Mojo's sets. We can keep the current functionality and also add the Pythonic one. They can be declared all in one single method but a default argument can determine if the user wants the random poping or the other. 3- In Python, most of the methods that accept another set (e.g. `issubset`), work with variadic arguments which means you can pass more than one set/collection to them. The newly added and the previously implemented methods do not still support such a thing. I'm deciding whether to add them here. Co-authored-by: ARVIN DAVOUDI Closes modularml/mojo#2456 MODULAR_ORIG_COMMIT_REV_ID: 2f00350ea0b211b5598207703f6168f8585f83f9 --- stdlib/src/collections/set.mojo | 188 +++++++++++++++++-- stdlib/test/collections/test_set.mojo | 250 +++++++++++++++++++++++++- 2 files changed, 418 insertions(+), 20 deletions(-) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 3dcdca60ba..bcb2c1dc25 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -218,6 +218,71 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): """ self.difference_update(other) + fn __le__(self, other: Self) -> Bool: + """Overloads the <= operator for sets. Works like as `issubset` method. + + Args: + other: Another Set instance to check against. + + Returns: + True if this set is a subset of the `other` set, False otherwise. + """ + return self.issubset(other) + + fn __ge__(self, other: Self) -> Bool: + """Overloads the >= operator for sets. Works like as `issuperset` method. + + Args: + other: Another Set instance to check against. + + Returns: + True if this set is a superset of the `other` set, False otherwise. + """ + return self.issuperset(other) + + fn __gt__(self, other: Self) -> Bool: + """Overloads the > operator for strict superset comparison of sets. + + Args: + other: The set to compare against for the strict superset relationship. + + Returns: + True if the set is a strict superset of the `other` set, False otherwise. + """ + return self >= other and self != other + + fn __lt__(self, other: Self) -> Bool: + """Overloads the < operator for strict subset comparison of sets. + + Args: + other: The set to compare against for the strict subset relationship. + + Returns: + True if the set is a strict subset of the `other` set, False otherwise. + """ + return self <= other and self != other + + fn __xor__(self, other: Self) -> Self: + """Overloads the ^ operator for sets. Works like as `symmetric_difference` method. + + Args: + other: The set to find the symmetric difference with. + + Returns: + A new set containing the symmetric difference of the two sets. + """ + return self.symmetric_difference(other) + + fn __ixor__(inout self, other: Self): + """Overloads the ^= operator. Works like as `symmetric_difference_update` method. + + Updates the set with the symmetric difference of itself and another set. + + Args: + other: The set to find the symmetric difference with. + """ + self.symmetric_difference_update(other) + fn __iter__[ mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type ]( @@ -341,17 +406,6 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): for e in other: self.add(e[]) - fn difference_update(inout self, other: Self): - """In-place set difference update. - - Updates the set by removing all elements found in the `other` set, - effectively keeping only elements that are unique to this set. - - Args: - other: Another Set instance to compare with this one. - """ - self.remove_all(other) - fn intersection_update(inout self, other: Self): """In-place set intersection update. @@ -363,12 +417,13 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): """ # Possible to do this without an extra allocation, but need to be # careful about concurrent iteration + mutation - self.remove_all(self - other) + self.difference_update(self - other) - fn remove_all(inout self, other: Self): + fn difference_update(inout self, other: Self): """In-place set subtraction. - Updates the set to remove any elements from the `other` set. + Updates the set by removing all elements found in the `other` set, + effectively keeping only elements that are unique to this set. Args: other: Another Set instance to subtract from this one. @@ -378,3 +433,108 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): self.remove(o[]) except: pass + + fn issubset(self, other: Self) -> Bool: + """Check if this set is a subset of another set. + + Args: + other: Another Set instance to check against. + + Returns: + True if this set is a subset of the `other` set, False otherwise. + """ + if len(self) > len(other): + return False + + for element in self: + if element[] not in other: + return False + + return True + + fn isdisjoint(self, other: Self) -> Bool: + """Check if this set is disjoint with another set. + + Args: + other: Another Set instance to check against. + + Returns: + True if this set is disjoint with the `other` set, False otherwise. + """ + for element in self: + if element[] in other: + return False + + return True + + fn issuperset(self, other: Self) -> Bool: + """Check if this set is a superset of another set. + + Args: + other: Another Set instance to check against. + + Returns: + True if this set is a superset of the `other` set, False otherwise. + """ + if len(self) < len(other): + return False + + for element in other: + if element[] not in self: + return False + + return True + + fn symmetric_difference(self, other: Self) -> Self: + """Returns the symmetric difference of two sets. + + Args: + other: The set to find the symmetric difference with. + + Returns: + A new set containing the symmetric difference of the two sets. + """ + var result = Set[T]() + + for element in self: + if element[] not in other: + result.add(element[]) + + for element in other: + if element[] not in self: + result.add(element[]) + + return result^ + + fn symmetric_difference_update(inout self, other: Self): + """Updates the set with the symmetric difference of itself and another set. + + Args: + other: The set to find the symmetric difference with. + """ + self = self.symmetric_difference(other) + + fn discard(inout self, value: T): + """Remove a value from the set if it exists. Pass otherwise. + + Args: + value: The element to remove from the set. + """ + try: + self._data.pop(value) + except: + pass + + fn clear(inout self) raises: + """Removes all elements from the set. + + This method modifies the set in-place, removing all of its elements. + After calling this method, the set will be empty. + """ + for _ in range(len(self)): + var a = self.pop() + + #! This code below (without using range function) won't pass tests + #! It leaves set with one remaining item. Is this a bug? + # for _ in self: + # var a = self.pop() diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index b8484051fe..9c22bce2c3 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -155,21 +155,21 @@ def test_subtract(): assert_equal(s2 - Set[Int](3, 4), Set[Int](1, 2)) -def test_remove_all(): +def test_difference_update(): var x = Set[Int]() - x.remove_all(Set[Int]()) + x.difference_update(Set[Int]()) assert_equal(x, Set[Int]()) x = Set[Int](1, 2, 3) - x.remove_all(Set[Int](1, 2, 3)) + x.difference_update(Set[Int](1, 2, 3)) assert_equal(x, Set[Int]()) x = Set[Int](1, 2, 3) - x.remove_all(Set[Int]()) + x.difference_update(Set[Int]()) assert_equal(x, Set[Int](1, 2, 3)) x = Set[Int](1, 2, 3) - x.remove_all(Set[Int](3, 4)) + x.difference_update(Set[Int](3, 4)) assert_equal(x, Set[Int](1, 2)) x = Set[Int]() @@ -255,6 +255,235 @@ def test_pop_insertion_order(): s.pop() # pop from empty set raises +def test_issubset(): + assert_true(Set[Int]().issubset(Set[Int](1, 2, 3))) + assert_true(Set[Int]() <= Set[Int](1, 2, 3)) + + assert_true(Set[Int](1, 2, 3).issubset(Set[Int](1, 2, 3))) + assert_true(Set[Int](1, 2, 3) <= Set[Int](1, 2, 3)) + + assert_true(Set[Int](2, 3).issubset(Set[Int](1, 2, 3, 4))) + assert_true(Set[Int](2, 3) <= Set[Int](1, 2, 3, 4)) + + assert_false(Set[Int](1, 2, 3, 4).issubset(Set[Int](2, 3))) + assert_false(Set[Int](1, 2, 3, 4) <= Set[Int](2, 3)) + + assert_false(Set[Int](1, 2, 3, 4, 5).issubset(Set[Int](2, 3))) + assert_false(Set[Int](1, 2, 3, 4, 5) <= Set[Int](2, 3)) + + assert_true(Set[Int]().issubset(Set[Int]())) + assert_true(Set[Int]() <= Set[Int]()) + + assert_false(Set[Int](1, 2, 3).issubset(Set[Int](4, 5, 6))) + assert_false(Set[Int](1, 2, 3) <= Set[Int](4, 5, 6)) + + +def test_disjoint(): + assert_true(Set[Int]().isdisjoint(Set[Int]())) + assert_false(Set[Int](1, 2, 3).isdisjoint(Set[Int](1, 2, 3))) + assert_true(Set[Int](1, 2, 3).isdisjoint(Set[Int](4, 5, 6))) + assert_false(Set[Int](1, 2, 3).isdisjoint(Set[Int](3, 4, 5))) + assert_true(Set[Int]().isdisjoint(Set[Int](1, 2, 3))) + assert_true(Set[Int](1, 2, 3).isdisjoint(Set[Int]())) + assert_false(Set[Int](1, 2, 3).isdisjoint(Set[Int](3))) + assert_true(Set[Int](1, 2, 3).isdisjoint(Set[Int](4))) + + +def test_issuperset(): + assert_true(Set[Int](1, 2, 3).issuperset(Set[Int]())) + assert_true(Set[Int](1, 2, 3) >= Set[Int]()) + + assert_true(Set[Int](1, 2, 3).issuperset(Set[Int](1, 2, 3))) + assert_true(Set[Int](1, 2, 3) >= Set[Int](1, 2, 3)) + + assert_true(Set[Int](1, 2, 3, 4).issuperset(Set[Int](2, 3))) + assert_true(Set[Int](1, 2, 3, 4) >= Set[Int](2, 3)) + + assert_false(Set[Int](2, 3).issuperset(Set[Int](1, 2, 3, 4))) + assert_false(Set[Int](2, 3) >= Set[Int](1, 2, 3, 4)) + + assert_false(Set[Int](1, 2, 3).issuperset(Set[Int](4, 5, 6))) + assert_false(Set[Int](1, 2, 3) >= Set[Int](4, 5, 6)) + + assert_false(Set[Int]().issuperset(Set[Int](1, 2, 3))) + assert_false(Set[Int]() >= Set[Int](1, 2, 3)) + + assert_false(Set[Int](1, 2, 3).issuperset(Set[Int](1, 2, 3, 4))) + assert_false(Set[Int](1, 2, 3) >= Set[Int](1, 2, 3, 4)) + + assert_true(Set[Int]().issuperset(Set[Int]())) + assert_true(Set[Int]() >= Set[Int]()) + + +def test_greaterthan(): + assert_true(Set[Int](1, 2, 3, 4) > Set[Int](2, 3)) + assert_false(Set[Int](2, 3) > Set[Int](1, 2, 3, 4)) + assert_false(Set[Int](1, 2, 3) > Set[Int](1, 2, 3)) + assert_false(Set[Int]() > Set[Int]()) + assert_true(Set[Int](1, 2, 3) > Set[Int]()) + + +def test_lessthan(): + assert_true(Set[Int](2, 3) < Set[Int](1, 2, 3, 4)) + assert_false(Set[Int](1, 2, 3, 4) < Set[Int](2, 3)) + assert_false(Set[Int](1, 2, 3) < Set[Int](1, 2, 3)) + assert_false(Set[Int]() < Set[Int]()) + assert_true(Set[Int]() < Set[Int](1, 2, 3)) + + +def test_symmetric_difference(): + assert_true( + Set[Int](1, 4) + == Set[Int](1, 2, 3).symmetric_difference(Set[Int](2, 3, 4)) + ) + assert_true(Set[Int](1, 4) == Set[Int](1, 2, 3) ^ Set[Int](2, 3, 4)) + + assert_true( + Set[Int](1, 2, 3, 4, 5, 6) + == Set[Int](1, 2, 3).symmetric_difference(Set[Int](4, 5, 6)) + ) + assert_true( + Set[Int](1, 2, 3, 4, 5, 6) == Set[Int](1, 2, 3) ^ Set[Int](4, 5, 6) + ) + + assert_true( + Set[Int](1, 2, 3) == Set[Int](1, 2, 3).symmetric_difference(Set[Int]()) + ) + assert_true(Set[Int](1, 2, 3) == Set[Int](1, 2, 3) ^ Set[Int]()) + + assert_true( + Set[Int](1, 2, 3) == Set[Int]().symmetric_difference(Set[Int](1, 2, 3)) + ) + assert_true(Set[Int](1, 2, 3) == Set[Int]() ^ Set[Int](1, 2, 3)) + + assert_true(Set[Int]() == Set[Int]().symmetric_difference(Set[Int]())) + assert_true(Set[Int]() == Set[Int]() ^ Set[Int]()) + + assert_true( + Set[Int]() == Set[Int](1, 2, 3).symmetric_difference(Set[Int](1, 2, 3)) + ) + assert_true(Set[Int]() == Set[Int](1, 2, 3) ^ Set[Int](1, 2, 3)) + + +def test_symmetric_difference_update(): + # Test case 1 + set1 = Set[Int](1, 2, 3) + set2 = Set[Int](2, 3, 4) + set1.symmetric_difference_update(set2) + assert_true(Set[Int](1, 4) == set1) + + set1 = Set[Int](1, 2, 3) + set2 = Set[Int](2, 3, 4) + set1 ^= set2 + assert_true(Set[Int](1, 4) == set1) + + # Test case 2 + set3 = Set[Int](1, 2, 3) + set4 = Set[Int](4, 5, 6) + set3.symmetric_difference_update(set4) + assert_true(Set[Int](1, 2, 3, 4, 5, 6) == set3) + + set3 = Set[Int](1, 2, 3) + set4 = Set[Int](4, 5, 6) + set3 ^= set4 + assert_true(Set[Int](1, 2, 3, 4, 5, 6) == set3) + + # Test case 3 + set5 = Set[Int](1, 2, 3) + set6 = Set[Int]() + set5.symmetric_difference_update(set6) + assert_true(Set[Int](1, 2, 3) == set5) + + set5 = Set[Int](1, 2, 3) + set6 = Set[Int]() + set5 ^= set6 + assert_true(Set[Int](1, 2, 3) == set5) + + # Test case 4 + set7 = Set[Int]() + set8 = Set[Int](1, 2, 3) + set7.symmetric_difference_update(set8) + assert_true(Set[Int](1, 2, 3) == set7) + + set7 = Set[Int]() + set8 = Set[Int](1, 2, 3) + set7 ^= set8 + assert_true(Set[Int](1, 2, 3) == set7) + + # Test case 5 + set9 = Set[Int]() + set10 = Set[Int]() + set9.symmetric_difference_update(set10) + assert_true(Set[Int]() == set9) + + set9 = Set[Int]() + set10 = Set[Int]() + set9 ^= set10 + assert_true(Set[Int]() == set9) + + # Test case 6 + set11 = Set[Int](1, 2, 3) + set12 = Set[Int](1, 2, 3) + set11.symmetric_difference_update(set12) + assert_true(Set[Int]() == set11) + + set11 = Set[Int](1, 2, 3) + set12 = Set[Int](1, 2, 3) + set11 ^= set12 + assert_true(Set[Int]() == set11) + + +def test_discard(): + set1 = Set[Int](1, 2, 3) + set1.discard(2) + assert_true(set1 == Set[Int](1, 3)) + + set2 = Set[Int](1, 2, 3) + set2.discard(4) + assert_true(set2 == Set[Int](1, 2, 3)) + + set3 = Set[Int]() + set3.discard(1) + assert_true(set3 == Set[Int]()) + + set4 = Set[Int](1, 2, 3, 4, 5) + set4.discard(2) + set4.discard(4) + assert_true(set4 == Set[Int](1, 3, 5)) + + set5 = Set[Int](1, 2, 3) + set5.discard(1) + set5.discard(2) + set5.discard(3) + assert_true(set5 == Set[Int]()) + + +def test_clear(): + set1 = Set[Int](1, 2, 3) + set1.clear() + assert_true(set1 == Set[Int]()) + + set2 = Set[Int]() + set2.clear() + assert_true(set2 == Set[Int]()) + + set3 = Set[Int](1, 2, 3) + set3.clear() + set3.add(4) + set3.add(5) + assert_true(set3 == Set[Int](4, 5)) + + set4 = Set[Int](1, 2, 3) + set4.clear() + set4.clear() + set4.clear() + assert_true(set4 == Set[Int]()) + + set5 = Set[Int](1, 2, 3) + set5.clear() + assert_true(len(set5) == 0) + + fn test[name: String, test_fn: fn () raises -> object]() raises: var name_val = name # FIXME(#26974): Can't pass 'name' directly. print("Test", name_val, "...", end="") @@ -275,8 +504,17 @@ def main(): test["test_intersection", test_intersection]() test["test_union", test_union]() test["test_subtract", test_subtract]() - test["test_remove_all", test_remove_all]() + test["test_difference_update", test_difference_update]() test["test_iter", test_iter]() test["test_add", test_add]() test["test_remove", test_remove]() test["test_pop_insertion_order", test_pop_insertion_order]() + test["test_issubset", test_issubset]() + test["test_disjoint", test_disjoint]() + test["test_issuperset", test_issuperset]() + test["test_greaterthan", test_greaterthan]() + test["test_lessthan", test_lessthan]() + test["test_symmetric_difference", test_symmetric_difference]() + test["test_symmetric_difference_update", test_symmetric_difference_update]() + test["test_discard", test_discard]() + test["test_clear", test_clear]() From fdc151a6b36f6eded17164e6dcc6a2e1effcb4be Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Fri, 3 May 2024 09:27:43 -0600 Subject: [PATCH 0397/2019] [External] [tools] Add a docstring check to the pre-commit (v2) (#39249) [External] [tools] Add a docstring check to the pre-commit (v2) Follow up on #2343 This will run automatically in the CI and for contributors using pre-commit :) Fixes #2331 Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2483 MODULAR_ORIG_COMMIT_REV_ID: dd111ea538e50db3f2e0748e90501c779606dc75 --- stdlib/docs/style-guide.md | 19 ++++++++++++++ stdlib/scripts/check-docstrings.py | 40 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 stdlib/scripts/check-docstrings.py diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 79dd099085..7f58ea863b 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -61,6 +61,25 @@ pre-commit install and that's it! +#### API Doc String Validation + +Mojo provides a command line utility, `mojo doc`, to validate the API doc +strings in your code. This ensures that your doc strings are correctly +formatted and consistent with the Mojo style guidelines. +Note that you should not have any warnings. + +```bash +> mojo doc --warn-missing-doc-strings -o /dev/null stdlib/src/ +``` + +Note that this is also included in the pre-commit. So if you have `pre-commit` +enabled, this will run automatically before committing. If you want to run it +manually with pre-commit, just run + +```bash +pre-commit run --all-files +``` + #### Whitespace - Use vertical whitespace only as needed to organize code into logical sections. diff --git a/stdlib/scripts/check-docstrings.py b/stdlib/scripts/check-docstrings.py new file mode 100644 index 0000000000..44eaf8e3d4 --- /dev/null +++ b/stdlib/scripts/check-docstrings.py @@ -0,0 +1,40 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +import subprocess +import sys + +# TODO: Use the "mojo doc" directly when there is an option to +# fail if warnings are present (something like -Werror for gcc). + + +def main(): + # This is actually faster than running "mojo doc" on each file since + # "mojo doc" only accept a single file/path as argument + command = [ + "mojo", + "doc", + "--warn-missing-doc-strings", + "-o", + "/dev/null", + "./stdlib/src", + ] + result = subprocess.run(command, capture_output=True) + if result.stderr or result.returncode != 0: + print(f"Docstring issue found in the stdlib: ") + print(result.stderr.decode()) + sys.exit(1) + + +if __name__ == "__main__": + main() From 6082a25a98b2317c0f9fc9d559c198999fb54b3c Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Fri, 3 May 2024 12:16:54 -0400 Subject: [PATCH 0398/2019] [mojo-tool] Support `mojo package -o dir/` (#39213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a convenience, when a user invokes `mojo package foo -o dir/`, output `dir/foo.mojopkg`. However, `dir/` must be an existing directory. This improves upon the current behavior, which is to error with "output path must have a '.mojopkg' or '.📦' extension." MODULAR_ORIG_COMMIT_REV_ID: 6e762de74a7767b043b50b0ce2bfd62bb1c0423c --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index df1bcc5e48..343c980087 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -82,6 +82,11 @@ what we publish. - Add `SIMD.shuffle()` with `StaticIntTuple` mask. ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) +- Invoking `mojo package my-package -o my-dir` on the command line, where + `my-package` is a Mojo package source directory, and `my-dir` is an existing + directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. + Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no From 16b8a97eaf4ef22592ced6128fa201eafd8508d9 Mon Sep 17 00:00:00 2001 From: Vasilis Themelis Date: Fri, 3 May 2024 10:54:50 -0600 Subject: [PATCH 0399/2019] [External] [docs] Add `fish` version of the set-up command (#39266) [External] [docs] Add `fish` version of the set-up command Co-authored-by: Vasilis Themelis Closes modularml/mojo#2423 MODULAR_ORIG_COMMIT_REV_ID: 376a924f470e84b9f1775382ee452386fbe3e281 --- docs/manual/get-started/index.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 572c8b8e19..bd75defef5 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -129,6 +129,17 @@ If you already have the `modular` tool, && source ~/.zshrc ``` + + + + If you're using fish, run this command: + + ```sh + set MOJO_PATH (modular config mojo.path) \ + && set -Ux MODULAR_HOME $HOME/.modular \ + && fish_add_path $MOJO_PATH/bin + ``` + From 6e9719e32994b52ff387d1e7345fae697271ec5b Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Fri, 3 May 2024 14:33:22 -0500 Subject: [PATCH 0400/2019] [External] [stdlib] Remove unnecessary arguments and overloads in print() (#39173) [External] [stdlib] Remove unnecessary args and overloads in print() The print function was using overloads and `rest.each` to be able to generate all the `_put` call necessary at compile time. It's possible to do the same thing without overloads and also with less arguments. This will increase maintainability and make the signature easily understandable in the docs. As discussed with Billy and Walter offline, disable the broken debug-info tests that result due to the code-gen differences of `print` and how we manage lifetime tracking in the debug-info pipeline. Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2463 MODULAR_ORIG_COMMIT_REV_ID: 8aff0f6735bdcd0ad29928a7197ea4d26ef24a5c --- stdlib/src/builtin/io.mojo | 39 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 4239cc3565..065672a1a6 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -354,28 +354,11 @@ fn _put(x: DType): # ===----------------------------------------------------------------------=== # -@no_inline -fn print( - *, sep: StringLiteral = " ", end: StringLiteral = "\n", flush: Bool = False -): - """Prints the end value. - - Args: - sep: The separator used between elements. - end: The String to write after printing the elements. - flush: If set to true, then the stream is forcibly flushed. - """ - _put(end) - if flush: - _flush() - - @no_inline fn print[ - T: Stringable, *Ts: Stringable + *Ts: Stringable ]( - first: T, - *rest: *Ts, + *values: *Ts, sep: StringLiteral = " ", end: StringLiteral = "\n", flush: Bool = False, @@ -384,24 +367,24 @@ fn print[ and followed by `end`. Parameters: - T: The first element type. - Ts: The remaining element types. + Ts: The elements types. Args: - first: The first element. - rest: The remaining elements. + values: The elements to print. sep: The separator used between elements. end: The String to write after printing the elements. flush: If set to true, then the stream is forcibly flushed. """ - _put(str(first)) @parameter - fn print_elt[T: Stringable](a: T): - _put(sep) - _put(a) + fn print_with_separator[i: Int, T: Stringable](value: T): + _put(value) - rest.each[print_elt]() + @parameter + if i < values.__len__() - 1: + _put(sep) + + values.each_idx[print_with_separator]() _put(end) if flush: From 07e9880d3b00d7dce0cbc647d02a2367aef805a6 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 3 May 2024 12:39:55 -0700 Subject: [PATCH 0401/2019] [stdlib] Migrating atomic.mojo Pointer -> UnsafePointer (#39244) Migrates the atomic.mojo implementation details to use UnsafePointer. This change also required updates to the MoggTensor and its helper utilities. MODULAR_ORIG_COMMIT_REV_ID: ad74552b991fdbfe4e8b8f3ad809d7ca27910907 --- stdlib/src/os/atomic.mojo | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 57cc2a57d4..2c3505a199 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -20,7 +20,7 @@ from os import Atomic """ from builtin.dtype import _integral_type_of -from memory import Pointer, bitcast +from memory import UnsafePointer, bitcast struct Atomic[type: DType]: @@ -78,7 +78,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline fn _fetch_add( - ptr: Pointer[Scalar[type]], rhs: Scalar[type] + ptr: UnsafePointer[Scalar[type]], rhs: Scalar[type] ) -> Scalar[type]: """Performs atomic in-place add. @@ -120,7 +120,7 @@ struct Atomic[type: DType]: Returns: The original value before addition. """ - var value_addr = Pointer.address_of(self.value) + var value_addr = UnsafePointer.address_of(self.value) return Self._fetch_add(value_addr, rhs) @always_inline @@ -154,7 +154,7 @@ struct Atomic[type: DType]: Returns: The original value before subtraction. """ - var value_addr = Pointer.address_of(self.value.value) + var value_addr = UnsafePointer.address_of(self.value.value) return __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, @@ -196,7 +196,7 @@ struct Atomic[type: DType]: @parameter if type.is_integral(): - var value_addr = Pointer.address_of(self.value.value) + var value_addr = UnsafePointer.address_of(self.value.value) var cmpxchg_res = __mlir_op.`pop.atomic.cmpxchg`[ bin_op = __mlir_attr.`#pop`, failure_ordering = __mlir_attr.`#pop`, @@ -220,9 +220,9 @@ struct Atomic[type: DType]: # operation on that. alias integral_type = _integral_type_of[type]() - var value_integral_addr = Pointer.address_of(self.value.value).bitcast[ - __mlir_type[`!pop.scalar<`, integral_type.value, `>`] - ]() + var value_integral_addr = UnsafePointer.address_of( + self.value.value + ).bitcast[__mlir_type[`!pop.scalar<`, integral_type.value, `>`]]() var expected_integral = bitcast[integral_type](expected) var desired_integral = bitcast[integral_type](desired) @@ -264,7 +264,7 @@ struct Atomic[type: DType]: "the input type must be arithmetic", ]() - var value_addr = Pointer.address_of(self.value.value) + var value_addr = UnsafePointer.address_of(self.value.value) _ = __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, @@ -292,7 +292,7 @@ struct Atomic[type: DType]: "the input type must be arithmetic", ]() - var value_addr = Pointer.address_of(self.value.value) + var value_addr = UnsafePointer.address_of(self.value.value) _ = __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, From e9803bd2927877078a744170a0f51720c0430d6b Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 3 May 2024 13:21:31 -0700 Subject: [PATCH 0402/2019] [stdlib] reference.mojo renaming Pointer -> LegacyPointer NFC (#39245) Renaming Pointer -> LegacyPointer in the `get_legacy_pointer` member function. MODULAR_ORIG_COMMIT_REV_ID: c7efbfdda9c2eabb76d5fd65b8a43587313e37ec --- stdlib/src/memory/reference.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 03c1658fac..a696da784f 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -256,15 +256,15 @@ struct Reference[ # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType # disagreement. Use UnsafePointer instead! @always_inline("nodebug") - fn get_legacy_pointer(self) -> Pointer[type, address_space]: - """Constructs a Pointer from a safe reference. + fn get_legacy_pointer(self) -> LegacyPointer[type, address_space]: + """Constructs a LegacyPointer from a safe reference. Returns: - Constructed Pointer object. + Constructed LegacyPointer object. """ # Work around AnyRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ - _type = Pointer[type, address_space]._mlir_type + _type = LegacyPointer[type, address_space]._mlir_type ](UnsafePointer(self).address) @always_inline("nodebug") From c245677b4ff5887be33a8863798bc4c069dea53a Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Fri, 3 May 2024 17:03:54 -0400 Subject: [PATCH 0403/2019] [mojo-stdlib] Remove `math.div_ceil` in favor of `math.ceildiv` (#39273) The implementation of `ceildiv` is also replaced with a more generic one. Some dead code and unused imports are also cleaned up along the way. Fixes MSTDL-146 MODULAR_ORIG_COMMIT_REV_ID: 69784acaf9de0904672daeb834dd02cf264aaf50 --- docs/changelog.md | 3 +++ stdlib/src/builtin/io.mojo | 12 +----------- stdlib/src/utils/stringref.mojo | 6 ------ stdlib/src/utils/variant.mojo | 2 +- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 343c980087..5f4f457af5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -110,6 +110,9 @@ what we publish. - The `math.roundeven` function has been removed from the `math` module. The new `SIMD.roundeven` method now provides the identical functionality. +- The `math.div_ceil` function has been removed in favor of the `math.ceildiv` + function. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 065672a1a6..9a26b76e2f 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys import bitwidthof, os_is_windows, triple_is_nvidia_cuda, external_call +from sys import os_is_windows, triple_is_nvidia_cuda, external_call from builtin.dtype import _get_dtype_printf_format from builtin.builtin_list import _LITRefPackHelper @@ -24,16 +24,6 @@ from memory import UnsafePointer from utils import StringRef, unroll from utils._format import Formattable, Formatter, write_to -# ===----------------------------------------------------------------------=== # -# Utilities -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment - # ===----------------------------------------------------------------------=== # # _file_handle diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 961fd973f9..b4d275c6e6 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -24,12 +24,6 @@ from memory import DTypePointer, UnsafePointer # ===----------------------------------------------------------------------=== # -@always_inline -fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment - - @always_inline fn _align_down(value: Int, alignment: Int) -> Int: return value._positive_div(alignment) * alignment diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 2506119adc..569fce8454 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -47,7 +47,7 @@ from memory.unsafe_pointer import ( move_from_pointee, move_pointee, ) -from utils import unroll, StaticTuple +from utils import unroll # ===----------------------------------------------------------------------=== # # Utilities From 0aabf9d7aedd2ce579c153f74181b75c0f1bb59a Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 3 May 2024 16:44:18 -0500 Subject: [PATCH 0404/2019] [stdlib] Combine device_print.mojo with stdlib _printf (#39292) This removes a GPU specific print function by combining it into the standard library `_printf` function so that everyone can use it. This is a precursor to #39200, where I'll add some more tests for the new combined `_printf`. MODULAR_ORIG_COMMIT_REV_ID: 4d547bcf2e22a8fca4340fb3e549b12cf1191e3d --- stdlib/src/builtin/io.mojo | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 9a26b76e2f..795634d876 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -100,17 +100,23 @@ fn _printf[*types: AnyType](fmt: StringLiteral, *arguments: *types): # aren't stripped off correctly. var loaded_pack = __mlir_op.`kgen.pack.load`(kgen_pack) - with _fdopen(_fdopen.STDOUT) as fd: - _ = __mlir_op.`pop.external_call`[ - func = "KGEN_CompilerRT_fprintf".value, - variadicType = __mlir_attr[ - `(`, - `!kgen.pointer,`, - `!kgen.pointer>`, - `) -> !pop.scalar`, - ], - _type=Int32, - ](fd, fmt.data(), loaded_pack) + @parameter + if triple_is_nvidia_cuda(): + _ = external_call["vprintf", Int32]( + fmt.data(), UnsafePointer.address_of(loaded_pack) + ) + else: + with _fdopen(_fdopen.STDOUT) as fd: + _ = __mlir_op.`pop.external_call`[ + func = "KGEN_CompilerRT_fprintf".value, + variadicType = __mlir_attr[ + `(`, + `!kgen.pointer,`, + `!kgen.pointer>`, + `) -> !pop.scalar`, + ], + _type=Int32, + ](fd, fmt.data(), loaded_pack) # ===----------------------------------------------------------------------=== # From d27e4f99ae5d32847adb28caf270f5f245664c00 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Fri, 3 May 2024 18:18:40 -0400 Subject: [PATCH 0405/2019] [mojo-stdlib] Make `ceildiv` generic (#39279) This is done by introducing `FloorDivable{Raising}` traits, and just relying on the existing implementation of `ceildiv`. Also add a few trivial implementations of `__rfloordiv__`. MODULAR_ORIG_COMMIT_REV_ID: af7356a6956907162c5b22493b137414d4067eca --- docs/changelog.md | 3 + stdlib/src/builtin/_math.mojo | 91 +++++++++++++++++++++++++-- stdlib/src/builtin/float_literal.mojo | 20 ++++-- stdlib/src/builtin/int.mojo | 3 +- stdlib/src/builtin/int_literal.mojo | 5 +- stdlib/src/builtin/simd.mojo | 21 ++++++- stdlib/src/utils/index.mojo | 16 ++++- 7 files changed, 143 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5f4f457af5..3b1db43454 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -87,6 +87,9 @@ what we publish. directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. +- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that + allow users to opt into the `math.ceildiv` function. + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 89c3d32877..6171a3716f 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -17,9 +17,9 @@ module should be exposed by the current `math` module. The contents of this module should be eventually moved to the `math` module when it's open sourced. """ -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # Ceilable -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # trait Ceilable: @@ -49,9 +49,9 @@ trait Ceilable: ... -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # Floorable -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # trait Floorable: @@ -79,3 +79,86 @@ trait Floorable: # associated types. fn __floor__(self) -> Self: ... + + +# ===----------------------------------------------------------------------=== # +# CeilDivable +# ===----------------------------------------------------------------------=== # + + +trait CeilDivable: + """ + The `CeilDivable` trait describes a type that defines a ceil divison + operation. + + Types that conform to `CeilDivable` will work with the `math.ceildiv` + function. + + For example: + ```mojo + from math import CeilDivable + + @value + struct Foo(CeilDivable): + var x: Float64 + + fn __floordiv__(self, other: Self) -> Self: + return self.x // other.x + + fn __rfloordiv__(self, other: Self) -> Self: + return other // self + + fn __neg__(self) -> Self: + return -self.x + ``` + """ + + # TODO(MOCO-333): Reconsider these signatures when we have parametric traits + # or associated types. + fn __floordiv__(self, other: Self) -> Self: + ... + + fn __rfloordiv__(self, other: Self) -> Self: + ... + + fn __neg__(self) -> Self: + ... + + +trait CeilDivableRaising: + """ + The `CeilDivable` trait describes a type that define a floor divison and + negation operation that can raise. + + Types that conform to `CeilDivableRaising` will work with the `//` operator + as well as the `math.ceildiv` function. + + For example: + ```mojo + from math import CeilDivableRaising + + @value + struct Foo(CeilDivableRaising): + var x: object + + fn __floordiv__(self, other: Self) raises -> Self: + return self.x // other.x + + fn __rfloordiv__(self, other: Self) raises -> Self: + return other // self + + fn __neg__(self) raises -> Self: + return -self.x + ``` + """ + + # TODO(MOCO-333): Reconsider these signatures when we have parametric traits + # or associated types. + fn __floordiv__(self, other: Self) raises -> Self: + ... + + fn __rfloordiv__(self, other: Self) raises -> Self: + ... + + fn __neg__(self) raises -> Self: + ... diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 19d663afbc..1b42ea9a29 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin._math import Ceilable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable # ===----------------------------------------------------------------------===# # FloatLiteral @@ -29,6 +29,7 @@ struct FloatLiteral( Absable, Boolable, Ceilable, + CeilDivable, EqualityComparable, Floorable, Intable, @@ -305,17 +306,26 @@ struct FloatLiteral( fn __floordiv__(self, rhs: Self) -> Self: """Returns self divided by rhs, rounded down to the nearest integer. - Constraints: - The element type of the SIMD vector must be numeric. - Args: - rhs: The value to divide on. + rhs: The divisor value. Returns: `floor(self / rhs)` value. """ return self.__truediv__(rhs).__floor__() + @always_inline("nodebug") + fn __rfloordiv__(self, rhs: Self) -> Self: + """Returns rhs divided by self, rounded down to the nearest integer. + + Args: + rhs: The value to be divided by self. + + Returns: + `floor(rhs / self)` value. + """ + return rhs // self + # TODO - maybe __mod__? # TODO - maybe __pow__? diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b0bc8c206e..b581306e7d 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement -from builtin._math import Ceilable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf @@ -197,6 +197,7 @@ struct Int( Absable, Boolable, Ceilable, + CeilDivable, Floorable, Formattable, Intable, diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 38322442ed..14d63ab354 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" -from builtin._math import Ceilable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable @value @@ -22,6 +22,7 @@ struct IntLiteral( Absable, Boolable, Ceilable, + CeilDivable, EqualityComparable, Floorable, Intable, @@ -582,7 +583,7 @@ struct IntLiteral( Returns: `value // self`. """ - return self // value + return value // self @always_inline("nodebug") fn __rlshift__(self, value: Self) -> Self: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 363da6eaf8..c6bbebd545 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -25,7 +25,7 @@ from sys import ( _RegisterPackType, ) -from builtin._math import Ceilable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable from builtin.hash import _hash_simd from memory import bitcast @@ -124,6 +124,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, Boolable, Ceilable, + CeilDivable, CollectionElement, Floorable, Hashable, @@ -613,7 +614,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( The element type of the SIMD vector must be numeric. Args: - rhs: The value to divide on. + rhs: The value to divide with. Returns: `floor(self / rhs)` value. @@ -639,6 +640,22 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var mask = ((rhs < 0) ^ (self < 0)) & (mod != 0) return div - mask.cast[type]() + @always_inline("nodebug") + fn __rfloordiv__(self, rhs: Self) -> Self: + """Returns the division of rhs and self rounded down to the nearest + integer. + + Constraints: + The element type of the SIMD vector must be numeric. + + Args: + rhs: The value to divide by self. + + Returns: + `floor(rhs / self)` value. + """ + return rhs // self + @always_inline("nodebug") fn __mod__(self, rhs: Self) -> Self: """Returns the remainder of self divided by rhs. diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 9d26582984..b285f0927c 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -456,11 +456,11 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): } @always_inline - fn __floordiv__(self, rhs: StaticIntTuple[size]) -> StaticIntTuple[size]: + fn __floordiv__(self, rhs: Self) -> Self: """Performs element-wise integer floor division. Args: - rhs: Right hand side operand. + rhs: The elementwise divisor. Returns: The resulting index tuple. @@ -474,6 +474,18 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): data: _int_tuple_binary_apply[size, apply_fn](self.data, rhs.data) } + @always_inline + fn __rfloordiv__(self, rhs: Self) -> Self: + """Floor divides rhs by this object. + + Args: + rhs: The value to elementwise divide by self. + + Returns: + The resulting index tuple. + """ + return rhs // self + @always_inline fn remu(self, rhs: StaticIntTuple[size]) -> StaticIntTuple[size]: """Performs element-wise integer unsigned modulo. From dd0fb7b6ae1fb684739049bd1408e39695b467f5 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 3 May 2024 15:41:43 -0700 Subject: [PATCH 0406/2019] [KGEN] Remove SignatureType from CoroutineType (#39274) We stored a SignatureType inside CoroutineType for legacy reasons, and over time there is no longer a need for this. Just store a list of types. Eventually, CoroutineType will always be opaque, and the result types will get moved somewhere else. MODULAR_ORIG_COMMIT_REV_ID: 495aea25c89438ba0ef344f365ad7f14c9cc7896 --- stdlib/src/builtin/coroutine.mojo | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index d357ce6e82..ec162f7c64 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -82,7 +82,7 @@ struct Coroutine[type: AnyRegType]: type: Type of value returned upon completion of the coroutine. """ - alias _handle_type = __mlir_type[`!co.routine<() -> `, type, `>`] + alias _handle_type = __mlir_type[`!co.routine<`, type, `>`] alias _promise_type = __mlir_type[`!kgen.struct<(`, type, `)>`] var _handle: Self._handle_type @@ -200,9 +200,7 @@ struct RaisingCoroutine[type: AnyRegType]: """ alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] - alias _handle_type = __mlir_type[ - `!co.routine<() throws -> `, Self._var_type, `>` - ] + alias _handle_type = __mlir_type[`!co.routine<`, Self._var_type, `>`] alias _promise_type = __mlir_type[`!kgen.struct<(`, Self._var_type, `)>`] var _handle: Self._handle_type From 9252e9e0a805d07650a9dd995fb7fce58ef72891 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 3 May 2024 15:41:52 -0700 Subject: [PATCH 0407/2019] [mojo-stdlib] Move `Coroutine` callback setting into `__await__` (#39283) This is more correct, because a coroutine can be awaited in a context different than where it was constructed. This is just a bit of rearranging to remove the `co.opaque_handle` operation. MODULAR_ORIG_COMMIT_REV_ID: e72c0c28ade7206a95899c0257ef791edbb8f278 --- stdlib/src/builtin/coroutine.mojo | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index ec162f7c64..9f5c09fefb 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -134,14 +134,7 @@ struct Coroutine[type: AnyRegType]: Returns: The constructed coroutine object. """ - var self = Coroutine[type] {_handle: handle} - var parent_hdl = __mlir_op.`co.opaque_handle`() - self._get_ctx[_CoroutineContext]().store( - _CoroutineContext { - _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl - } - ) - return self^ + return Self {_handle: handle} @always_inline fn __del__(owned self): @@ -172,6 +165,12 @@ struct Coroutine[type: AnyRegType]: Returns: The coroutine promise. """ + var parent_hdl = __mlir_op.`co.opaque_handle`() + self._get_ctx[_CoroutineContext]().store( + _CoroutineContext { + _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl + } + ) __mlir_region await_body(): __mlir_op.`co.resume`(self._handle) @@ -253,12 +252,6 @@ struct RaisingCoroutine[type: AnyRegType]: handle: The init handle. """ self = Self {_handle: handle} - var parent_hdl = __mlir_op.`co.opaque_handle`() - self._get_ctx[_CoroutineContext]().store( - _CoroutineContext { - _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl - } - ) @always_inline fn __del__(owned self): @@ -292,6 +285,12 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The coroutine promise. """ + var parent_hdl = __mlir_op.`co.opaque_handle`() + self._get_ctx[_CoroutineContext]().store( + _CoroutineContext { + _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl + } + ) __mlir_region await_body(): __mlir_op.`co.resume`(self._handle) From 5e5572d0fed224e9f67dc5412d99fea1179e1565 Mon Sep 17 00:00:00 2001 From: Lily Brown Date: Fri, 3 May 2024 17:33:04 -0700 Subject: [PATCH 0408/2019] [LSP] Detect unused variables (#39072) Detect specifically unused local variables in a file. This required surfacing a `walkSymbols` method of `SymbolIndex` in order to observe _symbols_ rather than symbol _refs_. As per convention in many languages (Rust, JavaScript/TypeScript, Luau, Elixir, ...), variables whose name starts with an underscore (e.g. `_var`) will not emit a warning even if they are unused. This allows developers to silence warnings if necessary. MODULAR_ORIG_COMMIT_REV_ID: 2d1d4412f367dfc0fbbce6fa1d9669a6d41abd09 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 3b1db43454..b6a81a2b1c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -87,6 +87,8 @@ what we publish. directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. +- The Mojo Language Server now reports a warning when a local variable is unused. + - The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that allow users to opt into the `math.ceildiv` function. From 550e748ce6022a80c6910947f5f9ab449a6e29f1 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 3 May 2024 18:29:07 -0700 Subject: [PATCH 0409/2019] [KGEN] Rename `co.await` to `co.suspend` (NFC) (#39285) This operation is mis-named. It's not an await, but it's a suspend of the current coroutine. Rename it to `co.suspend` to align with the proposal in the doc. MODULAR_ORIG_COMMIT_REV_ID: 400753726cf8a57e0b0652301cb6322e33596c7c --- stdlib/src/builtin/coroutine.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 9f5c09fefb..ff5fa5dcfa 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -174,9 +174,9 @@ struct Coroutine[type: AnyRegType]: __mlir_region await_body(): __mlir_op.`co.resume`(self._handle) - __mlir_op.`co.await.end`() + __mlir_op.`co.suspend.end`() - __mlir_op.`co.await`[_region = "await_body".value]() + __mlir_op.`co.suspend`[_region = "await_body".value]() return self.get() @@ -294,7 +294,7 @@ struct RaisingCoroutine[type: AnyRegType]: __mlir_region await_body(): __mlir_op.`co.resume`(self._handle) - __mlir_op.`co.await.end`() + __mlir_op.`co.suspend.end`() - __mlir_op.`co.await`[_region = "await_body".value]() + __mlir_op.`co.suspend`[_region = "await_body".value]() return self.get() From 5dfa9b58dd19668552181a00bdcaeff6adb854cb Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 3 May 2024 19:53:23 -0700 Subject: [PATCH 0410/2019] [mojo-stdlib] Wrap `co.suspend` raw op uses into a helper function (NFC) (#39289) This wraps direct uses of the `co.suspend` MLIR op into a helper function. This means end-users of coroutines don't need to directly interact with the ugly `__mlir_region` stuff. This will also help refactor the internal implementation detail of this primitive. MODULAR_ORIG_COMMIT_REV_ID: 2200ac2ec5000b1a5820dea1d04dfae43d0e3683 --- stdlib/src/builtin/coroutine.mojo | 52 ++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index ff5fa5dcfa..83d4a4cddd 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -19,6 +19,22 @@ from sys import sizeof from memory import Pointer +# ===----------------------------------------------------------------------=== # +# _suspend_async +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _suspend_async[body: fn (Pointer[__mlir_type.i8]) capturing -> None](): + var hdl = __mlir_op.`co.opaque_handle`() + + __mlir_region await_body(): + body(hdl) + __mlir_op.`co.suspend.end`() + + __mlir_op.`co.suspend`[_region = "await_body".value]() + + # ===----------------------------------------------------------------------=== # # _CoroutineContext # ===----------------------------------------------------------------------=== # @@ -165,18 +181,18 @@ struct Coroutine[type: AnyRegType]: Returns: The coroutine promise. """ - var parent_hdl = __mlir_op.`co.opaque_handle`() - self._get_ctx[_CoroutineContext]().store( - _CoroutineContext { - _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl - } - ) - __mlir_region await_body(): + @always_inline + @parameter + fn await_body(parent_hdl: Pointer[__mlir_type.i8]): + self._get_ctx[_CoroutineContext]().store( + _CoroutineContext { + _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl + } + ) __mlir_op.`co.resume`(self._handle) - __mlir_op.`co.suspend.end`() - __mlir_op.`co.suspend`[_region = "await_body".value]() + _suspend_async[await_body]() return self.get() @@ -285,16 +301,16 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The coroutine promise. """ - var parent_hdl = __mlir_op.`co.opaque_handle`() - self._get_ctx[_CoroutineContext]().store( - _CoroutineContext { - _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl - } - ) - __mlir_region await_body(): + @always_inline + @parameter + fn await_body(parent_hdl: Pointer[__mlir_type.i8]): + self._get_ctx[_CoroutineContext]().store( + _CoroutineContext { + _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl + } + ) __mlir_op.`co.resume`(self._handle) - __mlir_op.`co.suspend.end`() - __mlir_op.`co.suspend`[_region = "await_body".value]() + _suspend_async[await_body]() return self.get() From 0cf4aa9014f586b26a367029f4b0db1c4900635c Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 3 May 2024 21:57:02 -0500 Subject: [PATCH 0411/2019] [stdlib] feature: Change SIMD to use `Formattable` (#39200) This avoids intermediate allocations in code that formats scalar and non-scalar `SIMD` values, and improves support for debug printing of `SIMD` values on GPU by switching `SIMD` over to use the `Formatter` / `Formattable` abstraction instead of malloc'ing an intermediate buffer. MODULAR_ORIG_COMMIT_REV_ID: 1f6fb878591b831ab001b9a72dd753cfb735e48a --- stdlib/src/builtin/simd.mojo | 89 +++++++++++++++++++++------- stdlib/src/builtin/string.mojo | 16 +++++ stdlib/src/utils/inlined_string.mojo | 11 +++- 3 files changed, 94 insertions(+), 22 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c6bbebd545..a0d98a74b7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -33,11 +33,12 @@ from utils._numerics import FPUtils from utils._numerics import isnan as _isnan from utils._numerics import nan as _nan from utils._visualizers import lldb_formatter_wrapping_type +from utils.inlined_string import _ArrayMem from utils import StaticTuple -from .dtype import _integral_type_of -from .io import _snprintf_scalar, _snprintf -from .string import _calc_initial_buffer_size +from .dtype import _integral_type_of, _get_dtype_printf_format +from .io import _snprintf_scalar, _snprintf, _printf +from .string import _calc_initial_buffer_size, _calc_format_buffer_size # ===------------------------------------------------------------------------===# # Type Aliases @@ -508,38 +509,54 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( A string representation. """ - # Reserve space for opening and closing brackets, plus each element and - # its trailing commas. - var buf = String._buffer_type() - var initial_buffer_size = 2 - for i in range(size): - initial_buffer_size += _calc_initial_buffer_size(self[i]) + 2 - buf.reserve(initial_buffer_size) + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this SIMD value to the provided formatter. + + Args: + writer: The formatter to write to. + """ # Print an opening `[`. @parameter if size > 1: - buf.size += _snprintf(buf.data, 2, "[") + writer.write_str("[") + # Print each element. for i in range(size): var element = self[i] # Print separators between each element. if i != 0: - buf.size += _snprintf(buf.data + buf.size, 3, ", ") + writer.write_str(", ") - buf.size += _snprintf_scalar[type]( - buf.data + buf.size, - _calc_initial_buffer_size(element), - element, - ) + @parameter + if triple_is_nvidia_cuda(): + # FIXME(MSTDL-406): + # This prints "out of band" with the `Formatter` passed in, + # meaning this will only work if `Formatter` is an unbuffered + # wrapper around printf (which Formatter.stdout currently + # is by default). + # + # This is a workaround to permit debug formatting of + # floating-point values on GPU, where printing to stdout is + # the only way the Formatter framework is currently used. + var format = _get_dtype_printf_format[type]() + + @parameter + if type.is_floating_point(): + # get_dtype_printf_format hardcodes 17 digits of precision. + format = "%g" + + _printf(format, element) + else: + _format_scalar(writer, element) # Print a closing `]`. @parameter if size > 1: - buf.size += _snprintf(buf.data + buf.size, 2, "]") - - buf.size += 1 # for the null terminator. - return String(buf^) + writer.write_str("]") @always_inline("nodebug") fn __add__(self, rhs: Self) -> Self: @@ -2836,3 +2853,33 @@ fn _simd_apply[ result[i] = func[x.type, y.type, result_type](x[i], y[i]) return result + + +# ===----------------------------------------------------------------------===# +# _format_scalar +# ===----------------------------------------------------------------------===# + + +fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): + # Stack allocate enough bytes to store any formatted Scalar value of any + # type. + alias size: Int = _calc_format_buffer_size[dtype]() + + var buf = _ArrayMem[Int8, size]() + # TODO(MOCO-268): + # Remove this rebind(..) once compiler type comparision bug is fixed. + var buf_ptr: UnsafePointer[Int8] = rebind[UnsafePointer[Int8]]( + buf.unsafe_ptr() + ) + + var wrote = _snprintf_scalar[dtype]( + buf_ptr, + size, + value, + ) + + var strref = StringRef(buf_ptr, wrote) + + writer.write_str(strref) + + _ = buf^ # Keep alive diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index d6e0e36bfe..668bf2c239 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1549,3 +1549,19 @@ fn _calc_initial_buffer_size[type: DType](n0: Scalar[type]) -> Int: ) return 128 + 1 # Add 1 for the terminator + + +fn _calc_format_buffer_size[type: DType]() -> Int: + """ + Returns a buffer size in bytes that is large enough to store a formatted + number of the specified type. + """ + + # TODO: + # Use a smaller size based on the `dtype`, e.g. we don't need as much + # space to store a formatted int8 as a float64. + @parameter + if type.is_integral(): + return 64 + 1 + else: + return 128 + 1 # Add 1 for the terminator diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 0b3303a69d..63b72c21ee 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -17,7 +17,7 @@ from sys import sizeof -from memory import memcpy, LegacyPointer +from memory import memcpy, LegacyPointer, UnsafePointer from collections import Optional @@ -510,3 +510,12 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): """ return LegacyPointer.address_of(self.storage).bitcast[ElementType]() + + fn unsafe_ptr(inout self) -> UnsafePointer[ElementType]: + """Get a pointer to the elements contained by this array. + + Returns: + A pointer to the elements contained by this array. + """ + + return UnsafePointer.address_of(self.storage).bitcast[ElementType]() From fb9f9cca81842ebad0c8976f0d475567d89ffa82 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 3 May 2024 20:18:56 -0700 Subject: [PATCH 0412/2019] [KGEN] Drop the result types on `!co.routine` (#39325) This is a pretty big change to the async function type system. This PR drops the list of result types on the `!co.routine` type, turning it into a single, opaque type. We instead rely on the `Coroutine` type in the library to track the correct result types through the type system. This reduces the verbosity of the IR and moves towards a simpler IR model. At the same time, it removes the need to use `pointer` as a hack to represent opaque coroutines. At the same time, remove the `__call__` method on `Coroutine`. Directly resuming a coroutine is something that has to be done explicitly. Making it easily accessible via `()` is a huge trap, because of the async function suspends, the caller will not be ready to handle it. MODULAR_ORIG_COMMIT_REV_ID: c103e5ca6e3b645fe73f468497a88be73811d439 --- stdlib/src/builtin/coroutine.mojo | 99 +++++++++++-------------------- 1 file changed, 34 insertions(+), 65 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 83d4a4cddd..9586fa82c9 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -24,8 +24,11 @@ from memory import Pointer # ===----------------------------------------------------------------------=== # +alias AnyCoroutine = __mlir_type.`!co.routine` + + @always_inline -fn _suspend_async[body: fn (Pointer[__mlir_type.i8]) capturing -> None](): +fn _suspend_async[body: fn (AnyCoroutine) capturing -> None](): var hdl = __mlir_op.`co.opaque_handle`() __mlir_region await_body(): @@ -48,34 +51,28 @@ struct _CoroutineContext: interpretations of the payload, but which nevertheless be the same size and contain the resume function and a payload pointer.""" - alias _opaque_handle = Pointer[__mlir_type.i8] # Passed the coroutine being completed and its context's payload. - alias _resume_fn_type = fn ( - Self._opaque_handle, Self._opaque_handle - ) -> None + alias _resume_fn_type = fn (AnyCoroutine, AnyCoroutine) -> None var _resume_fn: Self._resume_fn_type - var _parent_hdl: Self._opaque_handle + var _parent_hdl: AnyCoroutine fn _coro_resume_callback( - handle: _CoroutineContext._opaque_handle, - parent: _CoroutineContext._opaque_handle, + handle: AnyCoroutine, + parent: AnyCoroutine, ): """Resume the parent Coroutine.""" _coro_resume_fn(parent) @always_inline -fn _coro_resume_fn(handle: _CoroutineContext._opaque_handle): +fn _coro_resume_fn(handle: AnyCoroutine): """This function is a generic coroutine resume function.""" - __mlir_op.`co.resume`(handle.address) + __mlir_op.`co.resume`(handle) -fn _coro_resume_noop_callback( - handle: _CoroutineContext._opaque_handle, - null: _CoroutineContext._opaque_handle, -): +fn _coro_resume_noop_callback(handle: AnyCoroutine, null: AnyCoroutine): """Return immediately since nothing to resume.""" return @@ -98,9 +95,8 @@ struct Coroutine[type: AnyRegType]: type: Type of value returned upon completion of the coroutine. """ - alias _handle_type = __mlir_type[`!co.routine<`, type, `>`] alias _promise_type = __mlir_type[`!kgen.struct<(`, type, `)>`] - var _handle: Self._handle_type + var _handle: AnyCoroutine @always_inline fn _get_promise(self) -> Pointer[type]: @@ -110,9 +106,9 @@ struct Coroutine[type: AnyRegType]: Returns: The coroutine promise. """ - var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`( - self._handle - ) + var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`[ + _type = __mlir_type[`!kgen.pointer<`, Self._promise_type, `>`] + ](self._handle) return promise.bitcast[type]() @always_inline @@ -141,7 +137,7 @@ struct Coroutine[type: AnyRegType]: return self._get_promise().bitcast[ctx_type]() - 1 @always_inline - fn __init__(handle: Self._handle_type) -> Coroutine[type]: + fn __init__(handle: AnyCoroutine) -> Coroutine[type]: """Construct a coroutine object from a handle. Args: @@ -157,23 +153,6 @@ struct Coroutine[type: AnyRegType]: """Destroy the coroutine object.""" __mlir_op.`co.destroy`(self._handle) - @always_inline - fn __call__(self) -> type: - """Execute the coroutine synchronously. - - Returns: - The coroutine promise. - """ - - self._get_ctx[_CoroutineContext]().store( - _CoroutineContext { - _resume_fn: _coro_resume_noop_callback, - _parent_hdl: _CoroutineContext._opaque_handle.get_null(), - } - ) - __mlir_op.`co.resume`(self._handle) - return self.get() - @always_inline fn __await__(self) -> type: """Suspends the current coroutine until the coroutine is complete. @@ -184,7 +163,7 @@ struct Coroutine[type: AnyRegType]: @always_inline @parameter - fn await_body(parent_hdl: Pointer[__mlir_type.i8]): + fn await_body(parent_hdl: AnyCoroutine): self._get_ctx[_CoroutineContext]().store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl @@ -195,6 +174,17 @@ struct Coroutine[type: AnyRegType]: _suspend_async[await_body]() return self.get() + # Never call this method. + fn _deprecated_direct_resume(self) -> type: + self._get_ctx[_CoroutineContext]().store( + _CoroutineContext { + _resume_fn: _coro_resume_noop_callback, + _parent_hdl: self._handle, + } + ) + __mlir_op.`co.resume`(self._handle) + return self.get() + # ===----------------------------------------------------------------------=== # # RaisingCoroutine @@ -215,9 +205,8 @@ struct RaisingCoroutine[type: AnyRegType]: """ alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] - alias _handle_type = __mlir_type[`!co.routine<`, Self._var_type, `>`] alias _promise_type = __mlir_type[`!kgen.struct<(`, Self._var_type, `)>`] - var _handle: Self._handle_type + var _handle: AnyCoroutine @always_inline fn _get_promise(self) -> Pointer[Self._var_type]: @@ -227,9 +216,9 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The coroutine promise. """ - var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`( - self._handle - ) + var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`[ + _type = __mlir_type[`!kgen.pointer<`, Self._promise_type, `>`] + ](self._handle) return promise.bitcast[Self._var_type]() @always_inline @@ -261,7 +250,7 @@ struct RaisingCoroutine[type: AnyRegType]: return self._get_promise().bitcast[ctx_type]() - 1 @always_inline - fn __init__(inout self, handle: Self._handle_type): + fn __init__(inout self, handle: AnyCoroutine): """Construct a coroutine object from a handle. Args: @@ -274,26 +263,6 @@ struct RaisingCoroutine[type: AnyRegType]: """Destroy the coroutine object.""" __mlir_op.`co.destroy`(self._handle) - @always_inline - fn __call__(self) raises -> type: - """Execute the coroutine synchronously. - - Returns: - The coroutine promise. - """ - - fn _coro_noop_fn(handle: _CoroutineContext._opaque_handle): - return - - self._get_ctx[_CoroutineContext]().store( - _CoroutineContext { - _resume_fn: _coro_resume_noop_callback, - _parent_hdl: _CoroutineContext._opaque_handle.get_null(), - } - ) - __mlir_op.`co.resume`(self._handle) - return self.get() - @always_inline fn __await__(self) raises -> type: """Suspends the current coroutine until the coroutine is complete. @@ -304,7 +273,7 @@ struct RaisingCoroutine[type: AnyRegType]: @always_inline @parameter - fn await_body(parent_hdl: Pointer[__mlir_type.i8]): + fn await_body(parent_hdl: AnyCoroutine): self._get_ctx[_CoroutineContext]().store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl From 2870114f13a72ebbf4aa93cd214e695eb1938bea Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 3 May 2024 22:34:56 -0700 Subject: [PATCH 0413/2019] [KGEN] Add the current coroutine handle as an argument to `co.suspend` (#39329) This allows replacing the only use of `co.opaque_handle` and is a tighter API for working with coroutines. MODULAR_ORIG_COMMIT_REV_ID: 09549ca47f2d79dd27152a43867270e3bf466426 --- stdlib/src/builtin/coroutine.mojo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 9586fa82c9..127ccb2790 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -29,9 +29,7 @@ alias AnyCoroutine = __mlir_type.`!co.routine` @always_inline fn _suspend_async[body: fn (AnyCoroutine) capturing -> None](): - var hdl = __mlir_op.`co.opaque_handle`() - - __mlir_region await_body(): + __mlir_region await_body(hdl: AnyCoroutine): body(hdl) __mlir_op.`co.suspend.end`() From 88657a387beaecd5427c5756c118aad37a57e89f Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Sat, 4 May 2024 10:14:47 -0500 Subject: [PATCH 0414/2019] [SDLC] Add mojo proposals to Copybara take 2 (#39277) This change moves the source of truth for Mojo proposals to the monorepo. It contains a current snapshot of the public proposals directory and updates Copybara to sync `modular:open-source/mojo/proposals` to `mojo:proposals`. The first attempt failed because Copybara validates that every transform has an effect. When running against older commits, the `open-source` folder does not exist. To resolve, we need to tell Copybara to allow the proposal transformation to be a noop. Example run here: [Internal link] Fixes SDLC-326 MODULAR_ORIG_COMMIT_REV_ID: 60f932e8336e1a9578b4147a791997549efd4d0e --- proposals/README.md | 8 + proposals/byte-as-uint8.md | 44 ++ proposals/inferred-parameters.md | 195 +++++ proposals/lifetimes-and-provenance.md | 423 +++++++++++ proposals/lifetimes-keyword-renaming.md | 147 ++++ proposals/mojo-and-dynamism.md | 253 +++++++ proposals/project-manifest-and-build-tool.md | 104 +++ proposals/remove-let-decls.md | 153 ++++ proposals/value-ownership.md | 735 +++++++++++++++++++ 9 files changed, 2062 insertions(+) create mode 100644 proposals/README.md create mode 100644 proposals/byte-as-uint8.md create mode 100644 proposals/inferred-parameters.md create mode 100644 proposals/lifetimes-and-provenance.md create mode 100644 proposals/lifetimes-keyword-renaming.md create mode 100644 proposals/mojo-and-dynamism.md create mode 100644 proposals/project-manifest-and-build-tool.md create mode 100644 proposals/remove-let-decls.md create mode 100644 proposals/value-ownership.md diff --git a/proposals/README.md b/proposals/README.md new file mode 100644 index 0000000000..392782a030 --- /dev/null +++ b/proposals/README.md @@ -0,0 +1,8 @@ +# Mojo🔥 engineering design proposals + +This directory contains ad-hoc design proposals put together by the Mojo +engineering team. These are meant to help shape discussion and refine the +design of various subsystems, but typically become obsolete and incorporated into +more canonical documentation when the implementation work concludes. There is +no attempt to keep these up-to-date as the language evolves, so they are +more for historical reference than as a user-guide for the language. diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md new file mode 100644 index 0000000000..342007be80 --- /dev/null +++ b/proposals/byte-as-uint8.md @@ -0,0 +1,44 @@ +# Standardise the representation of byte sequence as a sequence of unsigned 8 bit integers + +At this point in time, a sequence of bytes is often represented as a sequence of +signed 8 bit integers in Mojo standard library. Most noticeable example is the +underlying data of string types `String`, `StringLiteral`, `StringRef` and +`InlinedString`, but also APIs like for example the hash function `fn +hash(bytes: DTypePointer[DType.int8], n: Int) -> Int:`. + +## Motivation + +Logically a byte is an integer value between `0` and `255`. Lots of algorithms +make use of arithmetics ground by this assumption. A signed 8 bit integer on +the contrary represents values between `-128` and `127`. This introduces very +subtle bugs, when an algorithm written for unsigned 8 bit integer is used on a +signed 8 bit integer. + +Another motivation for this change is that Mojo aims to be familiar to Python +users. Those Python users are familiar with the `bytes` class, which itself is +working with values between `0` and `255`, not values between `-128` and `127`. + +## Examples + +### Division + +A value `-4` represented as `Int8` has the same bit pattern as value `252` +represented as `UInt8`. `-4 // 4` equals to `-1` (`bx11111111`), where `252 // +4` equals to `63` (`bx00111111`) as we can see the bit patterns are different. + +### Bit shift + +Values `-1` and `255` have the same bit pattern as `Int8` and `UInt8` +`bx11111111` but `-1 >> 1` results in `-1` (same bit pattern), where `255 >> 1` +results in `127` (`bx01111111`) + +## Proposal + +A text based search for `DTypePointer[DType.int8]` and `Pointer[Int8]` on +current open-sourced standard library revealed 29 results for `Pointer[Int8]` +and 78 results for `DTypePointer[DType.int8]`. Replacing +`DTypePointer[DType.int8]` with `DTypePointer[DType.uint8]` and `Pointer[Int8]` +with `Pointer[UInt8]` on case by case bases is a substantial refactoring effort, +but it will prevent a certain class of logical bugs (see +). As it is a breaking change in +sense of API design, it is sensible to do the refactoring as soon as possible. diff --git a/proposals/inferred-parameters.md b/proposals/inferred-parameters.md new file mode 100644 index 0000000000..704f590bb5 --- /dev/null +++ b/proposals/inferred-parameters.md @@ -0,0 +1,195 @@ +# Inferring Parameters from Other Parameters + +A common feature in programming language with generics is the ability to infer +the value of generics/templates/parameters from the argument types. Consider +C++: + +```cpp +template +void inferMe(T x) {} + +int x = 1; +inferMe(x); +// Equivalent to +inferMe(x); +``` + +Mojo is a parametric language and also supports this feature in a variety of use +cases that make code significantly less verbose: + +```python +fn infer_me[dt: DType, size: Int](x: SIMD[dt, size]): pass + +infer_me(Int32()) +# Equivalent to +infer_me[DType.int32, 1](Int32()) +``` + +But Mojo pushes these needs a step further. As a language that encourages heavy +parameterization, dependent types are very common throughout the language. +Consider: + +```python +fn higher_order_func[dt: DType, unary: fn(Scalar[dt]) -> Scalar[dt]](): pass + +fn scalar_param[dt: DType, x: Scalar[dt]](): pass +``` + +Language users commonly encounter cases where dependent types could infer their +parameter values from other parameters in the same way from argument types. +Consider `scalar_param` in the example above: `dt` could be inferred from the +type of `x` if `x` were passed as an argument, but we have no syntax to express +inferring it from `x` as a parameter since the user is required to pass `dt` as +the first parameter. + +```python +scalar_param[DType.int32, Int32()]() # 'dt' parameter is required +``` + +This has been requested multiple times in various forms, especially given the +new autoparameterization feature. The current tracking feature request: + +- + +## Proposal + +In the above example, we want to be able to infer `dt` instead of explicitly +specifying it: + +```python +scalar_param[Int32()]() +``` + +Laszlo Kindrat and I proposed several options to remedy this and members of the +“Mojo Language Committee” met to discuss these ideas, summarized below. + +We decided to move forward with the following option. Mojo will introduce a new +keyword, `inferred`, as a specifier for parameters only. `inferred` parameters +must precede all non-inferred parameters in the parameter list, and they +**cannot** be specified by a caller — they can **only** be inferred from other +parameters. This allows us to express: + +```python +fn scalar_param[inferred dt: DType, x: Scalar[dt]](): pass + +scalar_param[Int32()]() # 'dt' is skipped and 'Int32()' is bound to 'x' +``` + +Where `dt` is inferred from `x`. The decision to choose a keyword instead of +introducing a new punctuation character [like Python does for keyword-only +arguments](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) +is because a keyword clearly indicates the intent of the syntax, and it’s easy +to explain in documentation and find via internet search. + +## Aside: Inferring from Keyword Parameters + +Related but separate to the proposal, we can enable parameter inference from +other parameters using keyword arguments. This allows specifying function (and +type) parameters out-of-order, where we can infer parameters left-to-right: + +```python +scalar_param[x=Int32()]() # 'dt' is inferred from 'x' +``` + +We should absolutely enable this in the language, since this does not work +today. However, with respect to the above proposal, in many cases this still +ends up being more verbose than one would like, especially if the parameter name +is long: + +```python +scalar_param[infer_stuff_from_me=Int32()]() + +# One would like to write: +scalar_param[Int32()]() +``` + +So this feature is orthogonal to the `inferred` parameter proposal. + +## Alternatives Considered + +Several alternative ideas were considered for this problem. + +### Non-Lexical Parameter Lists + +This solution would alter the name resolution rules inside parameter lists, +allowing forward references to parameters within the same list. The above +example would be expressed as: + +```python +fn scalar_param[x: Scalar[dt], dt: DType](): pass +``` + +Where any parameter is inferrable from any previous parameter. The benefits of +this approach are that the order of parameters at the callsite match the order +in the declaration: `scalar_param[Int32()]()` + +This alternative was rejected because: + +1. Non-lexical parameters are potentially confusing to users, who normally + expect named declarations to be lexical. Relatedly, we are moving towards + removing non-lexical parameters in general from the language. + +2. This would incur a huge implementation burden on the compiler, because the + type system needs to track the topological order of the parameters. + +### New Special Separator Parameter + +This solution is fundamentally the same as the accepted proposal, but differs +only in syntax. Instead of annotating each parameter as `inferred`, they are +separated from the rest using a new undecided sigil (`%%%` is a placeholder): + +```python +fn scalar_param[dt: DType, %%%, x: Scalar[dt]](): pass +``` + +The benefit of this approach is this matches the [Python +syntax](https://docs.python.org/3/tutorial/controlflow.html#special-parameters) +for separating position-only and keyword-only parameters. It also structurally +guarantees that all infer-only parameters appear at the beginning of the list. + +This alternative was rejected because: + +1. There was no agreement over the syntax, and any selected sigil would + introduce additional noise into the language. + +2. `inferred` clearly indicates the intent of the syntax, and can be found via + internet search, and is overall easier to explain syntax than introducing a new + argument separator. + +### Special Separator Parameter at the End + +This is a variation on the above, where the infer-only parameters would appear +at the end of the parameter list, and subsequent parameters would be allowed to +be non-lexical: + +```python +fn scalar_param[x: Scalar[dt], %%%, dt: DType](): pass +``` + +The benefit of this approach is that the parameters appear in the same position +at the callsite. This alternative was rejected for a combination of the reasons +for rejecting a new separator and non-lexical parameters. + +### Segmented Parameter Lists + +This proposal would allow functions to declare more than one parameter list and +enable right-to-left inference of the parameter “segments”. The above would be +expressed as: + +```python +fn scalar_param[dt: DType][x: Scalar[dt]](): pass +``` + +The callsite would look like + +```python +scalar_param[Int32()]() +``` + +And call resolution would match the specified parameter list to the last +parameter list and infer `dt`. This proposal was rejected because + +1. The right-to-left inference rules are potentially confusing. + +2. This is an overkill solution to the problem, because this opens to door to +arbitrary higher-order parameterization of functions. diff --git a/proposals/lifetimes-and-provenance.md b/proposals/lifetimes-and-provenance.md new file mode 100644 index 0000000000..176c751f3d --- /dev/null +++ b/proposals/lifetimes-and-provenance.md @@ -0,0 +1,423 @@ +# Provenance Tracking and Lifetimes in Mojo + +As of mid-May 2023, Mojo has full support for ownership (including move +semantics, borrows and transfers, mutability, ASAP destruction of values, and +member synthesis). This provides more expressiveness than many languages, but does +not meet the expectations of Rust and C++ programmers because it is impossible +to **return references** and **put references in structs**. + +This makes Mojo unable to express common patterns like `StringRef` in the LLVM +APIs because it is a struct containing a reference, and this makes our `Pointer` +type a massively unsafe API. + +## Goals of this document + +This document explores the first step in adding lifetimes to Mojo: what changes +we’ll have to introduce, some thinking on syntax we may want to use, and how +this may want us to reconsider existing design decisions. This is written in +the style of the "[Value ownership design for Mojo](value-ownership.md)" +document from January. + +This document is really just the “first step” of lifetimes. Rust includes a few +more exotic features, including [subtyping constraints between +lifetimes](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-subtyping-constraints-between-lifetimes-15), +[equality constraints between lifetime +parameters](https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377#no-equality-constraints-between-lifetime-parameters-16), +[unbounded +lifetimes](https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html) and +perhaps other features. We don't have all the mechanics of a generic system and +trait system yet to tie into - and it makes sense to lazily add complexity based +on need - so these are not included in this initial design. + +## Context + +Mojo already has much of the required infrastructure in place to support +lifetimes: we now have references, we just need to be able to return them. +Similarly, the Mojo parameter system provides a powerful way to model and +propagate lifetime information around in our type system. We have a +CheckLifetime compiler pass which infers lifetimes, diagnosing use of +uninitialized values and inserting destructor calls. + +Similarly, we can learn a lot from Rust’s design for lifetimes. That said, the +ownership system in Mojo is quite different than the one in Rust. In Rust, +scopes define the implicit lifetimes of values and references, and lifetimes are +used to verify that uses of the value happen when the value is still alive. Mojo +flips this on its head: values start their life when defined, but end their life +after their last use. This means that in Mojo, a lifetime reference **extends +the liveness of a value** for as long as derived references is used, which is a +bit different than Rust. + +For example, we expect this to behave like the comments indicate: + +```mojo + var some_str = String("hello") + + # The StringRef contains a reference to the some_str value + var some_str_ref = StringRef(some_str) + + # Last use of some_str, but don't destroy it because there is a use of + # the reference below! + use(some_str) + + # References must be propagatable through methods. + some_str_ref = some_str_ref.drop_front().drop_back() + print(some_str_ref) # Prints "ell" + # some_str destroyed here after last reference to it +``` + +The major thing that Mojo (and Rust) need from lifetimes is what is called +“local reasoning”: We need to be able to reason about the memory behavior of a +call just by looking at its signature. We cannot have to look into the body of a +function to understand its effects, and a function cannot know about its callers +to reason about the behavior of its arguments. Similarly, when accessing a +member `a.ref` that is a reference, we need to know what lifetime is being used +in the context of `a`. + +Because of this, the lifetimes in a function signature are something of a +"transfer function" that expresses mappings from input lifetimes to returned +lifetimes, and that allows reasoning about the lifetimes of field references. +Mojo already has a powerful parameter system that allows it to express these +sorts of relationships, so this all plugs together. + +### What to name / how to explain this set of functionality? + +Rust has a few things going on that are tightly bound and somewhat confusing: it +has scoping, lifetimes, and lifetime holes. It has a borrow checker that +enforces the rules, all together this is its ownership system. + +When it comes to the “lifetimes” part of the puzzle, it seems better to clarify +two very different concepts: on the one hand, stored **values** in memory each +have a conceptual “lifetime” that starts when the value is defined and ends at +the last use - this is where the destructor call is inserted. Because each +declared variable has an independently tracked lifetime, each also needs an +implicitly declared “lifetime parameter” that is tracked in the Mojo type +system. + +On the other hand, when reasoning about parametric functions, the type signature +defines a transfer function that expresses the “provenance” of the result values +from the function and how they relate to input values. This relationship is a +transfer function from the input lifetime parameters to output lifetime +parameters, and can be somewhat more complicated. The framing of “provenance +tracking” may be more general conceptually than “lifetime tracking” which seems +specific to the lifetime parameters. + +## Mojo Reference + Lifetime Design + +Lifetimes enable us to use references as first class types. This means they +can occur in nested positions: You can now have a reference to a reference, you +can have an array of references, etc. At the machine/execution level, a +reference is identical to a pointer, but the reference type system enables an +associated lifetime, tracking of mutability etc. + +### Writing a lifetime bound reference + +The first question is how to spell this. Rust uses the `'a` syntax which is +pretty unconventional and (weirdly but) distinctly Rust. For example, here are +some simple Rust functions: + +```rust +// This is Rust +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {..} +fn longest2<'a>(x: &'a mut str, y: &'a mut str) -> &'a mut str {..} +``` + +Mojo already have a general set of values in our generic signature list: we just +need to "parameterize" references and make them explicit. For now, we recommend +using new `ref` and `mutref` keywords parameterized on a lifetime, and a new +`Lifetime` type. For example: + +```mojo +# Proposed Mojo syntax. +fn longest[a: Lifetime](x: ref[a] String, + y: ref[a] String) -> ref[a] String: + return x if len(x) >= len(y) else y + +fn longest2[a: Lifetime](x: mutref[a] String, + y: mutref[a] String) -> mutref[a] String: ... +``` + +There are many other options for syntax that we can consider, but this is simple +and will get us going until we have more experience. For example, we can make +any of these work: + +```mojo +fn f(x: ref[a] String, y: mutref[a] String): ... # Homage to Rust! +fn f(x: ref(a) String, y: mutref(a) String): ... # Homage to Rust! +fn f(x: ref a String, y: mutref a String): ... # Homage to Rust! +fn f(x: ref 'a String, y: mutref 'a String): ... # Homage to Rust! +fn f(x: 'a String, y: mut 'a String): ... # Homage to Rust! +``` + +The argument for square brackets vs parens is if we like the explanation that +we’re +"parameterizing the reference with a lifetime". However, remember types can +also be parametric, and those are spelled with square brackets **after** the +type name, so parens may be better to make these more syntactically distinct. + +The spelling in structs should flow naturally from this: + +```mojo +struct StringRef[life: Lifetime]: + var data : Pointer[UInt8, life] + var len : Int +``` + +Being first class types, we will naturally allow local references: these should +also allow late initialization and explicitly declared lifetimes as well: + +```mojo +fn example[life: Lifetime](cond: Bool, + x: ref[life] String, + y: ref[life] String): + # Late initialized local borrow with explicit lifetime + let str_ref: ref[life] String + + if cond: + str_ref = x + else: + str_ref = y + print(str_ref) +``` + +### Mojo Argument Conventions vs References + +One non-obvious thing is that function argument conventions and references are +different things: keeping them different allows argument conventions to be a +convenient sugar that avoids most users from having to know about references and +lifetimes. For example, the `borrowed` (which is usually implicit) and `inout` +argument conventions are sugar that avoids having to explicitly declare +lifetimes: + +```mojo +// Mojo today +fn example(inout a: Int, borrowed b: Float32): … + +struct S: + fn method(inout self): … + +// Written long-hand with explicit lifetimes. +fn example[a_life: Lifetime, b_life: Lifetime] + (a: mutref[a_life] Int, b: ref[b_life] Float32): … +struct S: + fn method[self_life: Lifetime](self: mutref[self_life]): … +``` + +This is very nice - every memory-only type passed into or returned from a +function must have a lifetime specified for it, but you really don't want to +have to deal with this in the normal case. In the normal case, you can just +specify that you're taking something by borrow (the default anyway) with an +implicit lifetime, or taking it `inout` if you want to mutate it but don't care +about the lifetime. + +NOTE: `inout` arguments also have one additional feature: function calls with +`inout` arguments know how to work with getter/setter pairs. For example +something like `mutate(a[i])` will call the getter for the element before +calling the function, then call the setter afterward. We cannot support this +for general mutable reference binding (because we wouldn't know where to perform +the writeback) so `inout` will have a bit more magic than just providing an +implicit lifetime. + +NOTE: Internally to the compiler, references (which are always pointer sized) +are treated as a register-passable types. This means they compose correctly +with implicit borrow semantics, and you can even pass a reference `inout` if you +want to. Such a thing is a "mutable reference to a reference", allowing the +callee to mutate the callers reference. + +### Keyword (?) for static lifetime + +I think we can have a useful feature set without requiring the ability to +specify a static lifetime - the uses in Rust appear to be more about +constraining input lifetimes than it is about the core propagation of lifetimes, +that said, we can definitely dream up a spelling when it is needed. + +Similarly, unsafe pointer tricks (e.g. when working with C) may want to use the +static lifetime marker to disable all tracking. We can start with a stub like +`__static_lifetime` and then re-syntax it later. + +### Implicitly declared lifetime parameter names and other sugar + +One common use of named lifetime parameters is to tie the lifetime of the result +of a function back to the lifetime of one of the function arguments. One +refinement over Rust we could permit is for arguments to implicitly declare +lifetimes on their first use. For example, we don’t need to require a +declaration of `life` in this example: + +```mojo +fn longest(x: ref[life] String, + y: ref[life] String) -> ref[life] String: + +# Alternatively follow Rust's lead. +fn longest(x: 'life String, y: 'life String) -> 'life String: +``` + +Let's **NOT** add this in the near future. Explicit lifetimes will be much less +common in Mojo than they are in Rust, and it is better for learnability to not +have magic like this. + +### Lifetime of `Self` + +The `Self` keyword (upper case) produces an elaborated type name for the current +struct, but that does not include the lifetime of `self` (lower case) which is +generally a reference. In a method you can name the lifetime of `self` by +declaring it explicitly like this: + +```mojo + struct MyExample: + fn method[self_life: Lifetime](self: mutref[self_life] Self) + -> Pointer[Int, self_life]: + ... + + fn callMethod(x: mutref[life1] MyExample): + use(x.method()) + + var y = MyExample() + use(y.method()) +``` + +`self_life` will bind to the lifetime of whatever lvalue the method is called +on, which is the `life1` lifetime in the first example, and the implicit +lifetime of y in the second example. This all composes nicely. + +One problem though - this won’t work for var definitions inside the struct, +because they don’t have a self available to them, and may need to reason about +it: + +```mojo + struct IntArray: + var ptr : Pointer[Int, Self_lifetime] +``` + +It isn’t clear to me how the compiler will remap this though. We’d have to pass +in the pointer/reference instead of the struct type. An alternative is to not +allow expressing this and require casts. We can start with that model and +explore adding this as the basic design comes up. + +### Extended `getitem`/`getattr` Model + +Once we have references, we’ll want to add support for them in the property +reference and subscripting logic. For example, many types store their enclosed +values in memory: instead of having `Pointer` and `Array` types implement both +`__getitem__` and `__setitem__` (therefore being a "computed LValue") we'd much +rather them to expose a reference to the value already in memory (therefore +being more efficient). We can do this by allowing: + +```mojo + struct Pointer[type: AnyType, life: Lifetime]: + # This __getref__ returns a reference, so no setitem needed. + fn __getref__(self, offset: Int) -> mutref[life] type: + return __get_address_as_lvalue[life](...) +``` + +We will also need to extend the magic `__get_address_as_lvalue` style functions +to take a lifetime. + +## Examples using Lifetimes + +This section attempts to build a few example data structures that are important +to express with lifetimes. They obviously haven’t been tested. + +### Pointer / UnsafePointer / Reference + +This is the bottom of the stack and needs to interface with other unsafe +features. Suggested model is to make Pointer be parameterized on the lifetime +that it needs to work with as well as element type: + +```mojo + @value + @register_passable("trivial") + struct MutablePointer[type: AnyType, life: Lifetime]: + alias pointer_type = __mlir_type[...] + var address: pointer_type + + fn __init__() -> Self: ... + fn __init__(address: pointer_type) -> Self: ... + + # Should this be an __init__ to allow implicit conversions? + @static_method + fn address_of(arg: mutref[life] type) -> Self: + ... + + fn __getitem__(self, offset: Int) -> inout[life] type: + ... + + @staticmethod + fn alloc(count: Int) -> Self: ... + fn free(self): ... + + fn exercise_pointer(): + # Allocated untracked data with static/immortal lifetime. + let ptr = MutablePointer[Int, __static_lifetime].alloc(42) + + # Use extended getitem through reference to support setter. + ptr[4] = 7 + + var localInt = 19 + let ptr2 = MutablePointer.address_of(localInt) + ptr2[0] += 1 # increment localInt + + # ERROR: Cannot mutate localInt while ptr2 lifetime is live + localInt += 1 + use(ptr2) +``` + +It’s not clear to me if we need to have a split between `Pointer` and +`MutablePointer` like Swift does. It will depend on details of how the +CheckLifetimes pass works - I’m hoping/expecting that the borrow checker will +allow mutable references to overlap with other references iff that reference is +only loaded and not mutated. NOTE: We probably won't be able to do this with the +proposed model, because generics can't be variant over mutability of the +reference. + +Another aspect of the model we should consider is whether we should have an +`UnsafePointer` that allows unchecked address arithmetic, but have a safe +`Reference` type that just allows dereferencing. This `Reference` type would be +completely safe when constructed from language references, which is pretty cool. +We may also want to wire up the prefix star operator into a dunder method. + +### ArraySlice + +`ArraySlice` (aka `ArrayRef` in LLVM) should compose on top of this: + +```mojo + @value + @register_passable("trivial") + struct MutableArraySlice[type: AnyType, life: Lifetime]: + var ptr: MutablePointer[type, life] + var size: Int + + fn __init__() -> Self: + fn __init__(ptr: MutablePointer[type, life], size: Int) -> Self: + + # All the normal slicing operations etc, with bounds checks. + fn __getitem__(self, offset: Int) -> inout[life] type: + assert(offset < size) + return ptr[offset] +``` + +As with `UnsafePointer`, this has to be parameterized based on the underlying +element type. `ArraySlice` is just a bound checked pointer, but because of +lifetimes, it is safe once constructed: the references it produces are bound to +the lifetime specified so can’t dangle. + +### Array / ValueSemanticArray + +Given these low level types, we can start to build higher level abstractions. +One example of that is an `Array` type. I’d suggest that our default array type +be value semantic with lazy copy-on-write 🐄, but a simpler example can be +implemented with `std::vector` style eager copying: + +```mojo + # Doesn't require a lifetime param because it owns its data. + struct Array[type: AnyType]: + var ptr: MutablePointer[type, Self_lifetime] + var size: Int + var capacity: Int + + fn __getitem__[life: Lifetime](self: inout[life], start: Int, + stop: Int) -> MutableArraySlice[type, life]: + return MutableArraySlice(ptr, size) +``` + +By tying the lifetime of the produced slice to the lifetime of the Array `self`, +the borrow checker will prevent use/mutation of the `Array` itself while a +mutable slice is produced. diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md new file mode 100644 index 0000000000..e99bf2f77f --- /dev/null +++ b/proposals/lifetimes-keyword-renaming.md @@ -0,0 +1,147 @@ +# Keyword naming and other topics to discuss + +This document is split off the [Provenance Tracking and Lifetimes in +Mojo](lifetimes-and-provenance.md) document to separate general syntactic +bikesheding issues from the core semantic issues in that proposal. + +Assuming that proposal goes through, I think we should consider a few changes to +the current Mojo keyword paint: + +## `borrowed` Keyword => `borrow` or `ref` + +`borrowed` as a keyword doesn’t really make sense in our new world. This is +currently used to indicate an argument that is a borrowed version of an existing +value. Given the introduction of lifetimes, these things can now appear in +arbitrary places (e.g. you can have an array of references) so it makes sense to +use a noun. + +Instead of reading an argument as “this function takes foo which is a borrowed +string”, we would read it as “foo is a borrow/ref of a string”. This makes it +consistent with local borrows on the stack: + +```mojo +fn do_stuff[a: Lifetime](x: ref[a] String): ... + +fn usage(): + var str = String("hello") + ref r = str # Defines a local borrow of str. + + do_stuff(str) # Bind a reference to 'str' + do_stuff(r) # Pass on existing reference 'str' +``` + +## `inout` Keyword => `ref` or `mutref` (??) + +The primary argument for the ‘`inout`’ keyword being named this was that Chris +wanted to get off the former ampersand syntax we used, and that (in an argument +position) there is copy-in and copy-out action that happens with computed +LValues. I think there is a principled argument to switch to something shorter +like `ref` which is used in other languages (e.g. C#), since they can exist in +other places that are not arguments, and those don’t get copy-in/copy-out +behavior. One challenge with the name `ref` is that it doesn't obviously +convey mutability, so we might need something weird like `mutref`. + +Note that copy-in/copy-out syntax is useful in more than function call +arguments, so the `inout` keyword may return in the future. For example, we may +actually want to bind computed values to mutable references: + +```mojo +for inout x in some_array_with_getitem_and_setitem: + x += 1 +``` + +This requires opening the reference with getitem, and writing it back with +setitem. We may also want to abstract over computed properties, e.g. form +something like `Array[inout Int]` where the elements of the array hold closers +over the get/set pairs. If we had this, this could decay to a classic mutable +reference at call sites providing the existing behavior we have. + +Given this possible direction and layering, I think we should go with something +like this: + +1. `ref`: immutable reference, this is spelled “`borrowed`” today + +2. `mutref`: mutable reference, this is spelled “`inout`” today. I’d love a +better keyword suggestion than `mutref`, perhaps just `mut`? + +3. `inout`: abstracted computed mutable reference with getter/setter. + +`inout` can decay to `mutref` and `ref` in an argument position with writeback, +and `mutref` is a subtype of `ref` generally. + +## `owned` Keyword => `var` + +People on the forums have pointed out that the “`owned`” keyword in argument +lists is very analogous to the `var` keyword. It defines a new, whole, value +and it is mutable just like `var`. Switching to `var` eliminates a concept and +reduces the number of keywords we are introducing. + +## Allow `let` in argument lists ... or remove them entirely (!) + +If we replace the `owned` keyword with `var`, then we need to decide what to do +with `let`. There are two different paths with different tradeoffs that I see. + +The easiest to explain and most contiguous would be to allow arguments to be +defined as `let` arguments, just like we define `var` arguments. This would +keep these two declarations symmetrical, and appease people that like to control +mutation tightly. + +The more extreme direction would be to remove `let` entirely. Some arguments +in favor of this approach: + +1. It has been observed on the forum that it adds very little - it doesn't + provide additional performance benefits over `var`, it only prevents + "accidental mutation" of a value. +2. Languages like C++ default to mutability everywhere (very few people bother + marking local variables constant, e.g. with `const int x = foo()`. +3. The more important (and completely necessary) thing that Mojo needs to model + are immutable borrows. Removing `let` would reduce confusion about these two + immutable things. +4. Mojo also has `alias`, which most programmers see as a “different type of + constant” further increasing our chance of confusing people. +5. `let` declarations require additional compiler complexity to check them, Mojo + doesn’t currently support struct fields market `let` for example because the + initialization rules are annoying to check for. Once you have them, it + messes with default values in structs. + +In my opinion, I think we are likely to want to remove `let`’s, but we should +only do so after the whole lifetime system is up and working. This will give us +more information about how things feel in practice and whether they are worth +the complexity. + +## More alternatives to consider + +[@sa- +suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6104926) +the keyword `fix` instead of `let`. + +[@mojodojodev suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6105688): + +`ref[a]` - immutable reference +`mut[a]` - mutable reference +`let[a]` - immutable owned +`var[a]` - mutable owned + +Having three letters for all of the keywords will allow the user to understand +"this is related to ownership and mutability". The problem with the proposed +removing let is that code ported from Python to Mojo won't behave the same, +keeping let and var is advantageous in that it says this is a Mojo variable so +you can add all the weird Python dynamic behavior when the keyword is elided. + +[@mzaks +suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6134220) +using numbers to identify lifetimes, e.g.: + +```mojo +fn example['1_life](cond: Bool, + x: borrowed'1 String, + y: borrowed'1 String): + # Late initialized local borrow with explicit lifetime + borrowed'1 str_ref : String + + if cond: + str_ref = x + else: + str_ref = y + print(str_ref) +``` diff --git a/proposals/mojo-and-dynamism.md b/proposals/mojo-and-dynamism.md new file mode 100644 index 0000000000..845cc993b6 --- /dev/null +++ b/proposals/mojo-and-dynamism.md @@ -0,0 +1,253 @@ +# Mojo and Dynamism + +Mojo has the lofty goal of being a simple, powerful, and easy-to-use language +like Python but with features that allow programmers to reach the performance of +C. One of Mojo's approaches is to start from being a superset of Python and +provide an incremental typing-for-performance story where the performance of +Python code can be incrementally improved by adding type annotations, explicit +variable declarations and error handling, switching `def` to `fn`, and so on. +By making things like types explicit, dynamism is removed from the program and +the compiler can generate faster code. The relationship between Mojo and +dynamism has to be carefully managed to meet the goals of the language. The +point of this post is to measure where that relationship stands now, what it +will need going forward as Mojo grows more features, and develop a framework for +managing that relationship. + +## Classes and Dynamism + +One feature that Mojo lacks at the moment are classes, an inherently dynamic +feature that provides inheritance and runtime polymorphism, the foundations of +object-oriented programming. + +Classes are implemented differently in many languages. In Python, for example, +classes are much more flexible than in languages like C++ or Java -- they are +more similar to classes in Javascript or Smalltalk. Methods can be defined and +then deleted, and even conditionally defined! For example, this is valid Python +code: + +```python +define = True + +class C: + print("hello") # prints 'hello' + if define: + def f(self): print(10) + else: + def f(self): print(20) + + +C().f() # prints '10' +``` + +In fact, the body of a Python class is just code that is executed, and the +resulting local variables are bound to the attributes of a class object. When +calling a class object, it returns a new object with a reference to the class +object, in which it can perform attribute lookup. In addition, functions that +would be member functions have their first argument bound to the new class +instance through the Python [descriptor +mechanism](https://docs.python.org/3/howto/descriptor.html#invocation-from-a-class). + +Mojo as a superset of Python has to support the full "hash-table" dynamism in +classes for compatibility with Python, but reference semantic classes are also +important for systems programming and application programming, where this level +of dynamism isn't needed and is actively harmful. We need to decide how to +handle this. + +One approach is to provide a decorator on class definitions (which can be opt-in +or opt-out) to indicate whether the class is "fully dynamic" as in Python or +whether it is "constrained dynamic" (e.g. has virtual methods that may be +overridden but cannot have methods added or removed). + +"Constrained dynamic" Mojo classes will use vtables for a more limited but more +efficient constrained dynamism than full hash table lookups. In addition to raw +lookups, constrained dynamic classes can use "[class hierarchy +analysis](https://dl.acm.org/doi/10.5555/646153.679523)" to devirtualize and +inline method calls, which are not valid for "fully dynamic" classes. + +Swift has a similar issue, where the developers wanted to have constrained +dynamism by default but needed full dynamism when working with Objective-C code: +Objective-C is based on the Smalltalk object model and thus has the same issues +as Python. Swift solved this by adding an opt-in +[@objc](https://swiftunboxed.com/interop/objc-dynamic/) decorator, which +provides full compatibility with Objective-C classes. Swift implicitly applies +this decorator to subclasses of Objective-C or `@objc` classes for convenience. + +If we chose to follow this design in Mojo, we could introduce a `@dynamic` +decorator, in which the class is an instance of a hash table and the body is +executed at runtime: + +```python +@dynamic +class C: + def foo(): print("warming up") + foo() # prints 'warming up' + del foo + def foo(): print("huzzah") + foo() # prints 'huzzah' +``` + +We could of course make dynamic be the default, and have a decorator like +`@strict` to opt-in to constrained dynamism as well. Regardless of the bias, we +absolutely need to support full dynamism to maintain compatibility with Python. + +An implementation question here would be "when does the body get executed?" when +the class is defined at the top-level. In this case, the class `C` could be +treated as a global variable with a static initializer that is executed when the +program is loaded. This ties into a discussion about how to treat global +variables and top-level code in general, which will come in a subsequent +section. Naturally, if the class is never referenced, the body is never parsed +and the static initializer is never emitted. + +### Syntactic Compatibility and `@dynamic` + +A primary goal of Mojo is to [minimize the syntactic +differences](https://docs.modular.com/mojo/why-mojo.html#intentional-differences-from-python) +with Python. We also have to balance that need with what the right default for +Mojo is, and this affects the bias on whether this decorator is "opt-in" or +"opt-out". + +We find it appealing to follow the Swift approach by making "full dynamic" an +opt-in choice for a Mojo class. This choice would add another syntactic +divergence between Mojo and Python, but it is one that can be alleviated with an +automatic mechanical transformer from Python code to Mojo code (e.g. to deal +with new keywords we take). In this case, all Python classes will be translated +by sticking `@dynamic` on them, and they can be removed for incremental boosts +to performance. + +An alternate design is to require opt-in to "constraint dynamism" by adding a +`@strict` (or use another keyword altogether) for vtable dynamism. We can +evaluate tradeoffs as more of the model is implemented. + +## `def` and Dynamism + +In Mojo, the goal of `def` is to provide a syntactic feature set that enables +compatibility with Python. It allows, for example, omitting type annotations, +implicit variable declarations, implicit raises, etc. But the Mojo `def` is not +the same as a Python `def`. A commonly reported issue is that Mojo scoping rules +differ from Python's. In Python, local variables are scoped at the function +level, but Python also supports behaviour like: + +```python +def foo(k): + for i in range(k): + print(i) + # Mojo complains that `i` is not defined, but this code should compile and + # dynamically raise an `UnboundLocalError` depending on the value of `k`! + print(i) +``` + +Python functions also have a notion of which names are supposed to be bound to +local variables. In the following example, `bar` knows `i` refers to a captured +local variable in `foo`, whereas `baz` tries to retrieve a value for `i` in its +local variable map. + +```python +def foo(): + i = 2 + def bar(): + print(i) + def baz(): + print(i) + i = 10 + bar() # prints '2' + baz() # throws an 'UnboundLocalError' +``` + +This gets at the heart of how Mojo should treat implicitly declared variables in +`def`s. The short answer is: exactly how Python does. `def`s should carry a +function-scoped hash table of local variables that is populated and queried at +runtime. In other words, lookup of implicitly-declared variables would be +deferred to runtime. On the other hand, the function does need to have a notion +of what variable *could* be available in the function, in order to emit +`UnboundLocalError`s as required. Of course, the compiler can optimize the table +away and do all the nice stuff compilers do if possible. + +Difficulty arises when discussing `def`s themselves. Although `def`s should +internally support full hashtable dynamism, what kind of objects are `def`s +themselves? For instance: + +```python +def foo(): + bar() + +def bar(): + print("hello") + +foo() # prints 'hello' + +def bar(): + print("goodbye") + +foo() # should this print 'goodbye'? +``` + +In Mojo today, the first time the name lookup of `bar` is resolved, it is baked +into a direct call to the first `bar`. Therefore, shadowing of `bar` does not +propagate into the body of `foo`. On the other hand, if all `def`s were treated +as entries in a hashtable, then it would. + +One middle-ground approach would be to treat `bar` as a mutable global variable +of type `def()` (one for each possible overload of `bar`). The dynamism can be +escalated with a `@dynamic` decorator that removes static function overloading. +However, both of these approaches risk creating confusing name lookup rules. For +instance, would the following be allowed? + +```python +@dynamic +def bar(a): pass + +def bar(a: Int): pass +``` + +This gets into the "levels of dynamism" Mojo intends to provide, and how that +relates to `def`s. The reality is that `def`s in Mojo today only resemble Python +`def`s on the surface. They share similar syntax, but Mojo `def`s are really +extra syntax sugar on top of `fn` and are altogether a different beast than +Python `def`s. + +## Four Levels of Dynamism + +To summarize, in order to support incremental typing-for-performance, Mojo will +have to support everything from strict, strongly-typed code to full Python +hashtable dynamism but with syntax that provides a gradual transition from one +end to the other. + +Given all that has been discussed and what the language looks like today, Mojo's +dynamism is moving into four boxes: + +1. Compile-time static resolution. +2. Partial dynamism. +3. Full hashtable dynamism. +4. ABI interoperability with CPython. + +The fourth category isn't explored here, but will important when/if we support +subclassing imported-from-CPython classes in Mojo, because that will fix the +runtime in-memory representation to what CPython uses. + +The highest level of dynamism and the most faithful compatibility doesn't come +from Mojo itself, it comes from Mojo's first class interoperability with +CPython. This in effect will be Mojo's escape hatch for compatibility purposes +and is what gives Mojo access to all of Python's vast ecosystem. Below that, +Mojo will provide an emulation of Python's hash-table dynamism that is a +faithful but not quite identical replication of Python behaviour (no GIL, for +example!). Building this out will be a huge undertaking, and is something Mojo +should do over time. + +The most important thing to remember is that Mojo is not a "Python compiler". +The benefit of sharing the same syntax as Python, however, means seamless +interop is on the table: + +```python +@python +def python_func(a, b=[]): + return a + [2] + b + +fn mojo_func(): + try: + print(python_func([3])) + except e: + print("error from Python:", e) +``` + +The goal of the "levels of dynamism" is to provide an offramp, starting by +removing the `@python` decorator from `python_func`. diff --git a/proposals/project-manifest-and-build-tool.md b/proposals/project-manifest-and-build-tool.md new file mode 100644 index 0000000000..1899b0ae3d --- /dev/null +++ b/proposals/project-manifest-and-build-tool.md @@ -0,0 +1,104 @@ +# Mojo project manifest and build tool + +A *project manifest* is a file that describes the source code files that make up +a library or executable, and how those files are meant to be built, distributed, +and interacted with by language tooling (such as language servers and +debuggers). Some examples include Rust’s `Cargo.toml`, Python’s `setup.py`, or +language-agnostic formats such as Bazel and its `BUILD` files. + +A *build tool*, on the other hand, is a program that can create the libraries or +executables described in a project manifest. For example, `cargo build` compiles +and links Rust executables and libraries described in a `Cargo.toml` file. In +Python, many different tools can process `setup.py` and `pyproject.toml` files, +in order to produce Python wheels or packages. + +This proposal is meant to: + +1. Announce our intent to define a project manifest format for Mojo. +2. Announce our intent to implement a build tool for Mojo projects. +3. Solicit community feedback and feature requests for the project manifest and + build tool. + +## Motivation + +No project manifest format exists for Mojo as of Mojo SDK v0.7.0. In the present +situation, Mojo projects can be built by invoking `mojo package` (to produce a +`.mojopkg` library) or `mojo build` (to produce an executable) on the command +line. + +This status quo has several drawbacks: + +1. There is no standard for building a Mojo project from source. If we examine + popular Mojo projects hosted on GitHub today, some are built via `docker`, + where the Dockerfile invokes `mojo run` to execute the Mojo source file. Some + are built via CMake, where project maintainers have used `add_custom_command` + to invoke `mojo build` and `mojo package`. Some are built via a + `mojo package` command that is documented only in the README. Some have build + instructions that are only known to the maintainers. The lack of + standardization makes it difficult to download and build a Mojo source + repository, which inhibits collaboration among the Mojo community of + developers. +2. Collaboration within the Mojo community aside, the lack of a project manifest + inhibits Mojo language tooling, such as the language server and debugger, + from functioning perfectly. For example, many Mojo projects make use of + compile time definitions, such as `-D ENABLE_TILING`. Without knowing which + definitions should be used when compiling Mojo source code, the language + server cannot provide the same diagnostics that the user will see when + actually building their project. + +Therefore, we think that a project manifest and build tool specific to Mojo will +resolve these issues: + +1. We aim to implement a single command that can be used to build any Mojo + project from source, addressing the first issue listed above. This is + analogous to how `cargo build` builds the default targets with the default + settings for any Rust project, or how `zig build` does so for any Zig + project. +2. Because a project’s manifest will specify which Mojo compiler options are to + be used, language tools would make use of those and function as intended. + This addresses the second issue listed above. + +## Guiding principles + +- As mentioned above, we aim for a single command to be capable of building any + Mojo project from source. +- We believe the ability to *download* a project’s dependencies from the + Internet — a “package manager” function — can be added at a later time. For + example, `zig build` did not originally include this functionality, but added + an implementation over six years later. The Mojo build tool will likely + implement the downloading and building of project dependencies soon, but it + will be the subject of a separate proposal. +- Although the project manifest and build tool we design is specific to Mojo, we + will aim for the best possible integration with other build systems, such as + Python setuptools, Bazel, Buck2, and CMake. We will make accommodations to + better support these tools whenever possible. +- Our design will benefit from community input and contributions, so **we will + develop this as an open-source tool, written primarily in Mojo**. We believe + doing so will also serve to drive additions and improvements to the Mojo + standard library. + +## Request for feedback + +As mentioned above, this proposal is primarily to announce our intent to develop +a project manifest format and build tool for Mojo, and to solicit feedback. +Below are some topics that we would love to hear community members’ opinions on: + +- Whether you agree with the motivations and guiding principles in this + proposal. +- Which project manifest formats and build tools you love, and why. We’re + drawing inspiration from a broad set of language ecosystems, including Rust, + Zig, Swift, and especially Python. +- Whether to adopt the [build server + protocol](https://build-server-protocol.github.io). We think doing so may help + with our guiding principle to integrate well into the existing ecosystem of + tools. +- Whether to define the project manifest as an executable program. Analogous to + how `build.zig` and `Package.swift` are programs that define a project, should + we define a `project.mojo` or similar construct? There are many arguments in + favor of doing so, but on the other hand, we see tradeoffs as well, and a + purely declarative form could be used. +- Any other thoughts you wish to contribute — we are build systems and language + tooling nerds! Send us your thoughts on [the GitHub Discussion thread + associated with this + proposal](https://github.com/modularml/mojo/discussions/1785), and let’s geek + out. diff --git a/proposals/remove-let-decls.md b/proposals/remove-let-decls.md new file mode 100644 index 0000000000..ec09b19359 --- /dev/null +++ b/proposals/remove-let-decls.md @@ -0,0 +1,153 @@ +# Simplifying Mojo🔥 - let's get rid of `let` + +Chris Lattner, Dec 5, 2023, Status: **Accepted**, [discussion thread](https://github.com/modularml/mojo/discussions/1456#discussioncomment-8358722) + +Mojo is still a new language, and is rapidly evolving. We’re learning a lot +from other languages, but Mojo poses its own set of tradeoffs that indicate a +unique design point. + +One of the early decisions made in Mojo's development is that it adopts the +`let` and `var` design point that Swift uses. This whitepaper argues that we +should switch to a simpler model by jettisoning `let` and just retaining `var` +(and implicit Python-style variable declarations in `def`). This has also been +[suggested by the community](https://github.com/modularml/mojo/issues/1205). + +Note that immutability and value semantics remain an important part of the Mojo +design, this is just about removing "named immutable variables". Immutable +references in particular are critical and will be part of a future "lifetimes" +feature. + +## Current Mojo 0.6 design + +Mojo initially followed the precedent of Swift, which allows a coder to choose +between the `let` and `var` keyword to determine whether something is locally +modifiable. That said, the design in Mojo 0.6 and earlier has a number of +particularly surprising and unfortunate aspects: + +1. The notion of immutable variables is entirely new to Python programmers, and +previous experience with Swift shows that this ends up being the [very first concept](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Constants-and-Variables) +a Swift programmer has to learn. This is unfortunate, because named immutable +variables aren't a core programming concept, and not something required +to achieve Mojo's goals. + +2. The naming of `let` caused a lot of early [heat and +debate](https://github.com/modularml/mojo/discussions/120). Other programming +languages have a wide range of design points (e.g. `const` in C/C++ and +Javascript) and there is a divergence of naming for all these things: +`let`, `val`, `const`, etc, etc. + +3. Mojo also has a notion of compile time value (`alias`), which means there are +three concepts going around: `alias`, `let`, and `var`. Most of the uses of +(e.g.) Javascript `const` is better served with `alias` than `let`. + +4. Both Swift and Rust encourage immutable values - Swift (and currently Mojo) +warn about unneeded mutability, Rust makes mutability more verbose (`let mut`), +and some propose that Mojo [make mutability more +verbose](https://github.com/modularml/mojo/issues/451). This cuts very hard +against a lot of the design center of Python, which doesn’t even have this +concept at all: it would be weird to make it the default, but if we don’t, +then why bother having it? + +5. There is no performance benefit to the Swift/Rust design, and I personally +haven’t seen any data that shows that there is any safety or readability +benefit. If anything, it is the argument when seeing `let x = foo()` that you +know `x` will never be reassigned, but any benefit here is small. + +6. The immutability only applies to the local value, and in the case of +reference semantic types (e.g. types like `Pointer` in today's Mojo, but also +*all classes* in tomorrow's Mojo) this is super confusing. We are often asked: +“Why do I get a warning that I should change a "`var` pointer" to `let` when I +clearly mutate what it is pointing to?” + +7. Mojo does not currently allow `let`’s as struct fields, (only `var`’s) which +is inconsistent. Swift has a very complex set of rules for how struct fields +get initialized that would be nice to not implement for Mojo. There also isn’t +a great way to define defaulted field values, e.g.: + + ```mojo + struct Thing: + # This is not actually supported right now, but imagine it were. + let field = 42 + fn __init__(inout self): + self.field = 17 # shouldn't be able to overwrite field? + ``` + +8. Mojo has a notion of ownership and will eventually have a notion of lifetimes +and safe references (including both mutable and immutable *references*) which +will be different from (but can compose with) the `let` vs `var` distinction. +It is unfortunate to have different forms of immutability floating around, and +we really do need immutable borrows and immutable references. + +Speaking subjectively as one of the principal designers of Swift, I will say +that it has several pretty pedantic language features intended to increase +safety (e.g. requiring all potentially-throwing values to be marked with a `try` +keyword) and many of the decisions were made early without a lot of data to +support them. I believe we should fight hard to keep Mojo easy to learn and +eliminate unnecessary concepts if we can. + +## Proposal: eliminate ‘`let`’ declarations + +The proposal here is straightforward: let’s just eliminate the concept of an +immutable named value entirely. This won’t eliminate immutability as a concept +from Mojo, but will instead push it into the world of borrowed arguments and +immutable references. This would have a number of advantages: + +This directly simplifies the conceptual Mojo language model: + +1. This eliminates one unfamiliar concept that a Python program would have to + learn. +2. This eliminates confusion between `let` vs `alias` directly. +3. This eliminates a fertile source of keyword bikeshedding. +4. This eliminates confusion in workbooks where top level values are mutable + even though they are declared `let`. + +This would eliminate a bunch of complexity in the compiler as well: + +1. Error messages currently have to make sure to say `let` and `var` correctly. +2. The IR representation needs to track this for later semantic checks. +3. This eliminates the need to implement missing features to support `let`’s. +4. This eliminates the complexity around detecting unmutated `var`s that warn + about changing to `let`. +5. Due to ASAP destruction, CheckLifetimes has extra complexity to reject code + like: “`let x: Int; x = 1; use(x); x = 2; use(x)`” even though the original + lifetime of the first “`x=1`” naturally ended and “`x`” is uninitialized + before being assigned to. This has always been a design smell, and it + [doesn’t work right](https://github.com/modularml/mojo/issues/1414). + +This proposal will not affect runtime performance at all as far as we know. + +### What about var? + +If this proposal is accepted, I think we should leave `var` as-is. Unlike +traditional Python behavior, `var` introduces an explicitly declared and +*lexically scoped* value: we need some introducer and do want scoped +declarations. + +The name `var` is also less controversial because it clearly stands for +“variable” in a less ambiguous way than using `let` to stand for "named +constant". If there is desire to rename `var` it would be an orthogonal +discussion to this one and should be kept separate. + +## Rolling out this proposal smoothly + +If we think this proposal is a good idea, then I think we should stage this to +make adoption more smooth and less disruptive. Rolling this out would look like +this: + +1. Build consensus with the Mojo community to get feedback and additional + perspective. +2. Do the engineering work to validate there is no performance hit etc, and + eliminate the IR representation and behavior for `let`. At this phase we + will keep parsing them for compatibility: parse them into the same IR as a + `var`, but emit a warning “let has been deprecated and will be removed in + the next release” with a fixit hint that renames the `let` to `var`. +3. In a release ~1 month later, change the warning into an error. +4. In a release ~1 month later, remove the keyword entirely along with the error + message. + +## Alternatives considered + +We can always keep this around and re-evaluate later. That said, I don’t think +anything will change here - the Mojo user community (both external to Modular +and internal) has already run into this several times, and this will keep coming +up. diff --git a/proposals/value-ownership.md b/proposals/value-ownership.md new file mode 100644 index 0000000000..9303d1644e --- /dev/null +++ b/proposals/value-ownership.md @@ -0,0 +1,735 @@ + +# Value ownership design for Mojo + +**Written**: Jan 2, 2023 + +**Status**: Implemented but the design has been refined, kept for historical +*interest. + +This document explores a design for ownership support in Mojo. This learns from +other contemporary languages including C++, Rust, Swift and Val, providing a +novel blend that should integrate well with our base Python syntax, and be +powerful enough to express a wide range of kernel and systems programming +applications. + +Rust is the language that most people will naturally think of in this space, and +when compared to it, I expect that we will provide support for a wider range of +types than Rust, yet provide a more familiar programming model and friendly API +than it. While I assume that we will extend this to support lifetime types for +better generality and safety with reference semantic types, that design is not +included in this proposal. + +# Motivation and Background + +Modern systems languages aspire to provide memory safety, good low-level +performance, and allow advanced library developers to build expressive +high-level APIs that are easy to use by less experienced API users. + +In the case of Mojo, we have two specific “now” problems to solve: + +1. We need to provide a way to allocate memory for a Tensor-like type, and given +our existing support for raising exceptions, we need for them to be cleaned up. +Thus we need destructors. We also need to disable copying of this sort of type. + +2. We also need to implement transparent interop with CPython with an “object” +struct. This requires us to have copy constructors and destructors so we can +maintain the CPython reference count with an ergonomic Python-like model. + +Over time, we want Mojo to be a full replacement for the C/C++ system +programming use cases in Python, unifying the “two world problem” that Python +has with C. This is important because we want to have a unifying technology, +and because CPUs will always be an important accelerator (and are fully +general), and because our bet is that accelerators will get more and more +programmable over time. + +## Related Work + +I am not going to summarize the related work fully here, but I recommend folks +interested in this topic to read up on relevant work in the industry, including: + +1. C++’11 r-value references, move semantics, and its general modern programming +model. It is yucky and has lots of problems, but is table-stakes knowledge and +powers a tremendous amount of the industry. + +2. Have a programmer-level understanding of [Rust’s Memory Ownership +model](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html), and +read [the Rustonomicon](https://doc.rust-lang.org/nomicon/) end-to-end for bonus +points. + +3. Swift has a quite different approach which made some mistakes (Swift suffers +from pervasive implicit copying) but has nice things in its [initialization +design](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization), +[ARC +design](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html), +[exclusivity enforcement](https://www.swift.org/blog/swift-5-exclusivity/) +[[more +details](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md)], +etc. + +4. The [Val programming language](https://www.val-lang.dev/) is an early phase +research system that is learning from Swift and trying to provide a much simpler +programming model than Rust does with other advantages. It isn’t at all clear +if it will be expressive enough to be useful at this point though. + +C++ and Rust are the most widely known in this space and they make different +tradeoffs in defaults and what it means for a type author (ignoring lifetimes, +which C++ lacks and is therefore generally unsafe). A few random observations +that are related to the commentary below: + +- Rust in particular defaults to move’ing values but allows types to opt out of +that by implementing the Copy trait. + +- Rust’s type system doesn’t appear to support values like `std::atomic` which +require a pinned address in memory (Rust assumes it can transport things around +at will), nor does it support things like `llvm::SmallVector` which is movable, +but has an interior pointer so needs custom move constructors. + +- Because Rust defaults to moving everything around, it puts a huge amount of +pressure on memcpy and LLVM memcpy optimization (e.g. see Patrick Walton’s +recent work to improve this). In my opinion, this is a self imposed mistake +that we can correct structurally. + +- Rust could support C++-like moves from values that leave them +inert-and-to-be-destroyed, but does not do that. For lack of this, there is a +lot of complexity and unsafe APIs required (e.g. +[Drain](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.drain) +and other things) when you want to construct an array progressively or take +elements out of an array. + +## Relevant Existing Mojo Type System Features + +The Mojo design currently has a few basic features that are precursors to proper +ownership. The design below makes significant extensions and some changes to +it. + +### L-Values and R-Values + +Lvalues and Rvalues follow a conventional design found in many programming +languages: an Rvalue is an immutable value that can be passed around by copying +its bits by-value in an SSA register. An Lvalue is mutable, represented by its +address, and can be promoted to an Rvalue with a “load”. + +### Argument Conventions + +Functions can be declared to take any of their arguments by-reference, with an & +sigil in the function definition: + +```mojo + fn globalFn(a: Int, b&: Int): + b = a # ok + a = 4 # error + + struct Vec: + ... + fn push_back(self&, item: Int): ... # mutable self + fn size(self): ... # immutable self + + fn workWithVecs(a: Vec, b&: Vec): + use(a.size()) # ok + use(b.size()) # ok + + a.push_back(4) # Error, a isn't mutable + b.push_back(4) # ok +``` + +As shown, by-ref arguments are Lvalues and thus may be mutated, otherwise +arguments are passed by-value as immutable Rvalues. There is another +similar-but-different thing going on with “def” functions. By-value arguments +are passed by copy but are allowed to be mutable for better compatibility with +Python semantics: + +```mojo + def exampleDef(a: Int, b&: Int): + b = 4 # mutable as before. + + # passed by value into mutable copy. + # This change isn't visible in the caller. + a = 4 +``` + +The ‘def’ semantics are implemented by taking a value in by copy and introducing +a local ‘var’ that shadows the argument. It is therefore a purely local +transformation that we’ll ignore in the rest of this document. + +### Type initializers + +Mojo supports Python type initializers and currently allows them to participate +in implicit conversions: + +```mojo + struct Int: + var value : __mlir_type.index + + fn __init__(value: __mlir_type.index) -> Int: + return Int { value: value } +``` + +# Proposed Mojo Additions / Changes + +This proposal introduces a number of new concepts to cover the space of value +ownership and parameter passing conventions that Rust, Swift and C++ provide, +but in a different way. We layer on the features in individual groups. Let’s +explore them one piece at a time. + +## Extended parameter conventions + +We should extend the existing type system to support owning (aka moving, aka +consuming) parameter convention, immutable borrow semantics (“`&T"` in Rust, +“`const T&"` in C++) and mutable borrow semantics (“`&mut T`” in Rust, and +“`T&`” in C++). + +### Change & to mean mutable borrow or “inout” + +Right now, & is a C++-like reference semantic sigil, we should keep it, but +change it to mean a “mutable borrow” in Rust terminology or “inout” using Swift +terminology. This won’t require Mojo source changes, but will be a terminology +change inside of the compiler. The definitive initialization pass (below) will +need to enforce its correct semantics. + +Tangential to this proposal, we could require the use of `&` on the caller side +of arguments passing a mutable borrow to make it more clear in the caller code. +If we do this, I propose that it be a postfix operator, and use it for methods +for consistency: + +```mojo + swap(x.first&, y.first&) # Obvious what is being mutated + b&.push_back(42) # Obvious that b is mutated. +``` + +This proposal will not use this syntax below, and this is probably way too +onerous for methods, this is probably a bad idea. + +### Introduce “owned” argument conventions + +We need a way to specify that ownership of a value is being passed into the +function: + +```mojo + # This takes ownership of a unique vector, including its resources. + fn someFunction(owned v: Vec): + print(v) + v.push_back(42) # Ok: v is mutable because we own it +``` + +In the code above, we show “owned v” takes and owns a value from the caller. +Just like normal arguments, we would need a copy to get them as mutable: + +```mojo + fn anotherFunction(a: Int, owned v: Vec): + a += 1 # Error: a is immutable + var a2 = a # Copy the argument to make it mutable + a2 += 1 # Ok, a2 is mutable. + + v.push_back(a) # Ok, owned values are mutable + var v2 = v^ # Transfer v argument into a new var binding. + v2.push_back(a) # Also ok +``` + +This should be an owned _reference_ and thus lower to LLVM pointers, just like a +mutable reference does, unless the value is `@register_passable` (see below). +Not doing this would significantly impact our ability to model things like +`std::atomic` and `SmallVector` whose address is significant. See the +“extensions” at the bottom for why this will be efficient for trivial types like +integers. + +### Introduce “borrowed” argument conventions and change default + +Similarly we need the ability to specify that we are passing and returning an +immutable borrow, which is like a `'const &`’ in C++. The spelling of this +isn’t particularly important because it will frequently be defaulted, but we +need something concrete to explain the examples. + +```mojo + # This takes a borrow and return it. + # Returned references need lifetimes for safety of course. + fn printSizeAndReturn(borrowed v: Vec) -> borrowed Vec + print(v.size()) + return v +``` + +At the LLVM level, immutable references are passed by pointer, just like mutable +references. + +Note that C++ and Rust entered into a strange battle with programmers about how +to pass values by default. Templates generally use “`const&`” to avoid copies, +but this is an inefficient way to pass trivial types like “`int`”. We propose a +type level annotation to directly address, which allows us to use borrow far +more pervasively for arguments in the language. See the ‘extensions’ section +below. + +### Change the default argument and result conventions + +Now we have the ability to express ownership clearly, but we don’t want all code +everywhere to have words like `borrowed` on it: we want more progressive +disclosure of complexity and better defaults. + +The first piece of this is the default return value convention. The right +default convention for return values is to be owned: + +```mojo + # Result defaults to being an owned reference. + fn getVec() -> Vec: # Equivalent to "...) -> Vec^" + ... +``` + +because we otherwise have no way to return newly created values. Code can +override the return convention by using another sigil, e.g. `inout Vec` if a +mutable reference is required. + +We also need to decide what to do with arguments. We don’t want to copy +arguments by default (a mistake Swift made), because this requires types to be +copyable, and depends on unpredictable copy elision that makes performance and +COW optimization sad. I don’t think we want to depend on pass-by-move like Rust +did because Rust forces tons of things to be marked “&” pervasively, this +introduces a ton of `memcpy` operations, and Python programmers won’t think that +passing a value to a function makes it unavailable for use by other things by +default. + +In my opinion, the C++ got this almost right: `const&` is the right default +argument convention (and is optimized to register value passing in an opt-in +way, described below). This is good for both self and value arguments and is +what Swift semantically did with its +0 ownership convention in a bunch of +places. Consider this example: + +```mojo + struct Dictionary: + fn size(self) -> Int: ... +``` + +You don’t want to **copy the dictionary**, when calling size! This worked for +Swift because of ARC optimizations and that its Dictionary type was a small +thing implemented with COW optimizations, but this is very unpredictable. + +Passing arguments-by-borrow by default is also great because it eliminates the +pervasive need to do a load operation when converting Lvalues to Rvalues. This +is a huge improvement in the model, because “loading” a value is extremely +expensive when the value is large or cannot be loaded (e.g. variable sized +types, e.g. some languages representation for existential types and Rust’s DSTs, +which we may or may not want to support anyway). + +It also means that we can support types like `std::atomic` which need a +guaranteed ‘self’ address - natively and with no trouble - since we’re never +trying to implicitly load the value as a whole. + +## Adding support for value destruction + +Now that we have a notion of ownership, we can complete it by destroying values +when their lifetime has ended. This requires introducing the ability to declare +a destructor, and the machinery to determine when to invoke the destructor. + +### User defined destructors + +We should embrace the existing Python convention of implementing the `__del__` +method on a type. This takes ownership of the self value, so it should be +defined as taking `owned self`. Here’s a reasonable implementation of Vec’s +ctors and dtor, but without the push_back and associated methods (which are +obvious): + +```mojo + struct Vec: + var data: Pointer # I just made this type up. + var capacity: Int + var size: Int + fn __init__(inout self, capacity: Int): + self.data = Pointer.malloc(capacity) + self.capacity = capacity + self.size = 0 + + # default args will be nice some day + fn __new__(inout self): return Vec(1) + fn __del__(owned self): # owning reference to self. + # Any int values don't need to be destroyed. + self.data.free() +``` + +There is some nuance here and a special case that we need to handle in the +`__del__` method. Ideally, we should track the field sensitive liveness of the +‘self’ member that comes into del. This will allow us to handle sub-elements +that are individually consumed, safely handle exceptions that early-exit from +the destructor etc. This is something that Swift gets right that Rust +apparently doesn’t. + +With respect to the simple definition of Vec above, it is enough to define a +safe vector of integers which is creatable, destroyable, can be passed by +borrowed and mutable reference, but isn’t enough to support movability or +copyability. We’ll add those later. + +### When do we invoke destructors for value bindings? + +Now that we have a way to define a destructor for a value, we need to invoke it +automatically. Where do we invoke the destructor for a local value binding? +Two major choices exist: + +1. End of scope, ala C++ (and I think Rust). +2. After last use, ala Swift and Val (but Val has a better model). + +The difference can be seen in cases like this: + +```mojo + fn bindingLifetimeExample(): + var vec = Vec() + vec.push_back(12) + use(vec) + # Val/Swift destroys 'vec' here. + do_lots_of_other_stuff_unrelated_to_vec() + # C++/Rust destroy vec here. +``` + +I would advocate for following the Swift model. It reduces memory use, and I +haven’t seen it cause problems in practice - it seems like the right default. +Furthermore, this dovetails well with ownership, because you want (e.g.) +immutable borrows to die early so you can form mutable borrows in other +statements. It also makes the “form references within a statement” special case +in C++ go away. + +The tradeoff on this is that this could be surprising to C++ programmers, +something that Swift faced as well. The balance to that is that GC languages +with finalizers are not used for RAII patterns, and Python has first-class +language support for RAII things (the `with` statement). + +There are specific cases like RAII that want predictable end-of-scope +destruction, so you end up needing a `@preciseLifetime` decorator on the struct +or use closures - [both work +fine](https://developer.apple.com/documentation/swift/withextendedlifetime(_:_:)-31nv4). + +NOTE: This was pushed forward during implementation to the "ASAP" model that +Mojo uses. + +### Taking a value from a binding + +The other thing you may want to do is to intentionally end a binding early, +transferring ownership of the bound value out as an owned rvalue. Swift and +Rust both support mutable value lifetimes with holes in them, and ending +immutable bindings early (Rust with the `drop(x)` operator or by moving out of +the binding, Swift with the recently proposed +[consume/take/move](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md) +operation). + +I propose supporting this with the `^` postfix operator, e.g.: + +```mojo + fn takeVec(owned v: Vec): ... + + fn showEarlyBindingEnd(): + var vec = Vec() + vec.push_back(12) + takeVec(vec^) # Ownership of vec is transferred to takeVec. + do_lots_of_other_stuff_unrelated_to_vec() + + var vec2 = Vec() + vec2.push_back(12) + ... + _ = vec2^ # force drop vec2. +``` + +This is postfix so it composes better in expressions, e.g. +“`someValue^.someConsumingMethod()`”. + +### Supporting “taking” a value, with a convention (+ eventually a Trait) + +I believe it is important for common types to support a “destructive take” to +support use-cases where you want to std::move an element out of the middle of a +`std::vector`. C++ has `std::move` and move constructors for this, and Rust has +a ton of complexity to work around the lack of this. Swift doesn’t appear to +have a story for this yet. I think we just use a method convention (eventually +formalized as a trait) where types who want it define a `take()` method: + +```mojo + struct Vec: + ... + fn __moveinit__(inout self, inout existing): + # Steal the contents of 'existing' + self.data = existing.data + self.capacity = existing.capacity + self.size = existing.size + + # Make sure 'existing's dtor doesn't do bad things. + existing.data = None + existing.size = 0 + existing.capacity = 0 +``` + +This is analogous to defining a move constructor in C++. Note that you only +need to define this if you want to support this operation, and we eventually +should be able to synthesize this as a default implementation of the “Takable” +trait when we build out traits and metaprogramming features. + +## Value Lifetime Enforcement + +Now that we have all the mechanics in place, we actually have to check and +enforce lifetime in the compiler. This entails a few bits and pieces. + +### Implement a “Definitive Initialization” Like Pass: CheckLifetimes + +The first thing we need is a dataflow pass that tracks the initialization status +of local bindings. The basic mechanics needed here are implemented in the Swift +Definitive Initialization pass, and a lot of the mechanics are well described in +the [Drop Flags section of the +Rustonomicon](https://doc.rust-lang.org/nomicon/drop-flags.html) and [slide 135+ +in this +talk](https://www.llvm.org/devmtg/2015-10/slides/GroffLattner-SILHighLevelIR.pdf). +This is a combination of static analysis and dynamically generated booleans. + +When building this, we have the choice to implement this in a field sensitive +way. I believe that this is a good thing to do in the medium term, because that +will allow making `__new__` and `__del__` methods much easier to work with in +common cases, and will compose better when we get to classes. That said, we +should start with simple non-field-sensitive cases and extend it over time. +This is what we did when bringing up Swift and it worked fine. + +### Implement support for variable exclusivity checking + +While it isn’t a high priority in the immediate future, we should also add +support for variable exclusivity checking to detect dynamic situations where +aliases are formed. See the [Swift +proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md) +for details on the issues involved here. Mojo will be working primarily with +local variables in the immediate future, so we can get by with something very +simple for the immediate future. + +### Synthesize destructors for structs + +The other thing necessary in the basic model is for the destructors of field +members to be run as part of `__del__` methods on structs. We don’t want people +to have to write this manually, we should synthesize a `__del__` when needed. +For example in: + +```mojo + struct X: + var a: T1 + var b: T2 + + fn __del__(owned self): # This should be synthesized. + _ = self.a^ + _ = self.b^ +``` + +## Extensions to make the programming model nicer + +With the above support, we should have a system that is workable to cover the +C++/Rust use-cases (incl `std::atomic` and `SmallVector`), handle the motivating +Tensor type and Python interop, as well as provide a safe programming model +(modulo dangling reference types which need lifetimes to support). That said, +we still won’t have a super nice programming model, this includes some “table +stakes” things we should include even though they are not strictly necessary. + +In particular, the support above completely eliminated the need to copy and move +values, which is super pure, but it would be impractically painful to work with +simple types that have trivial copy constructors. For example: + +```mojo + fn useIntegers(a: Int): + var b = a+4 # ok, b gets owned value returned by plus operator. + let c = b # error, cannot take ownership from an lvalue. + let c2 = b.copy() # Ok, but laughable for Int. +``` + +It is worth saying that the “c = b” case is something that we explicitly want to +prohibit for non-trivial types like vectors and dictionaries: Swift implicitly +copies the values and relies on COW optimization and compiler heroics (which are +not amazingly great in practice) to make it “work”. Rust handles it by moving +the value away, which breaks value semantics and a programmer model that people +expect from Python. + +It is better for vectors and Dictionary’s (IMO) to make this a compile time +error, and say “this non-copyable type cannot be copied”. We can then +standardize a `b.copy()` method to make the expense explicit in source code. + +### Copy constructors for implicitly copyable types + +The solution to both of these problems is to allow types to opt-in to +copyability (as in the Rust `Copy` trait). The obvious signature for this in +Mojo seems to be a `__copyinit__` implementation (eventually formalized as a +trait): + +```mojo + struct Int: + var value: __mlir_type.index + fn __copyinit__(inout self, borrowed existing: Int): + self.value = existing.value +``` + +Given this new initializer, a type that implements this is opt-ing into being +implicitly copied by the compiler. This (re)enables lvalue-to-rvalue conversion +with a “load” operation, but makes it explicit in user source code. It allows +integers to work like trivial values, and allows the library designer to take +control of what types support implicit copies like this. This is also required +for the Python interop “object” type, since we obviously want `x = y` to work +for Python objects! + +One nice-to-have thing that we should get to eventually (as we build out support +for traits and metaprogramming) is `Copyable` trait with a default +implementation. This would allow us to manually provide a copy constructor if +we want above, or get a default synthesized one just by saying that our struct +is `Copyable`. See the appendix at the end of the document for more explanation +of how this composes together. + +All together, I believe this will provide a simple and clean programming model +that is much more predictable than the C++ style, and is more powerful than the +Swift or Rust designs, which don’t allow custom logic. + +### Opting into pass-by-register parameter convention + +The final problem we face is the inefficiency of passing small values +by-reference everywhere. This is a problem that is internalized by C++ +programmers through common wisdom (“pass complex values like `std::vector` as +`const& or rvalue-ref` but trivial values like `int` by value!”), but ends up +being a problem for some generic templated code - e.g. `std::vector` needs to +declare many things as being passed by `const&` or rvalue reference, which +becomes inefficient when instantiated for trivial types. There are ways to deal +with this in C++, but it causes tons of boilerplate and complexity. + +The key insight I see here is that the decision is specific to an individual +type, and should therefore be the decision of the type author. I think the +simple way to handle this is to add a struct decorator that opts the struct into +being passed by owning copy, equivalent to the Rust Copy convention: + +```mojo + @register_passable + struct Int: + ... +``` + +This decorator would require that the type have a copy constructor declared, and +it uses that copy constructor in the callee side of an API to pass arguments +by-register and return by-register. This would lead to an efficient ABIs for +small values. + +This decorator should only be used on small values that makes sense to pass in +registers or on the stack (e.g. 1-3 machine registers), and cannot be used on +types like `llvm::SmallVector` that have interior pointers (such a type doesn’t +make sense to pass by-register anyway!). + +# Conclusion + +This proposal attempts to synthesize ideas from a number of well known systems +into something that will fit well with Mojo, be easy to use, and easy to teach - +building on the Python ideology of “reducing magic”. It provides equivalent +expressive power to C++, while being a building block to provide the full power +for Rust-style lifetimes. + +## Parameter Conventions Summary + +This is the TL;DR: summary of what I think we end up with: + +```mojo + fn borrow_it(a: X) # takes X as borrow (sugar). + fn borrow_it(borrowed a: X) # takes X as borrow. + fn take_it(owned a: X) # takes owned X + fn ref_it(inout a: X) # takes mutable reference to X + fn register_it(a: Int) # by copy when Int is register-passable. + + fn ret_owned(self) -> X: # Return an owned X (sugar). + fn ret_owned(self) -> owned X: # Return an owned X. + fn ret_borrow(self) -> borrowed X: # Return an X as a borrow + fn ret_ref(self) -> inout X: # Return an X as a mutable ref + fn ret_register(self) -> Int: # Return by copy when Int is register-passable +``` + +## Extension for Lifetime types + +Lifetimes are necessary to support memory safety with non-trivial correlated +lifetimes, and have been pretty well proven in the Rust world. They will +require their own significant design process (particularly to get the defaulting +rules) and will benefit from getting all of the above implemented. + +That said, when we get to them, I believe they will fit naturally into the Mojo +and MLIR design. For example, you could imagine things like this: + +```mojo + # Take a non-copyable SomeTy as a borrow and return owned copy + fn life_ex1['a: lifetime](value: 'a SomeTy) -> SomeTy: + return value.copy() + + # Take a non-copyable SomeTy and return the reference + fn life_ex2['a: lifetime](value: 'a SomeTy) -> borrowed 'a SomeTy: + return value +``` + +This is not a concrete syntax proposal, just a sketch. A full design is outside +the scope of this proposal though, it should be a subsequent one. + +# Appendix: Decorators and Type Traits + +Above we talk loosely about decorators and type traits. A decorator in Python +is a modifier for a type or function definition. A Trait in Rust (aka protocol +in Swift, aka typeclass in Haskell) is a set of common behavior that unifies +types - sort of like an extended Java interface. + +Let’s see how these two concepts can come together in the future assuming we get +Swift/Rust-style traits and extend Python’s decorator concept with +metaprogramming features enabled by Mojo. + +Traits include “requirements”: signatures that conforming types are required to +have, and may also include default implementations for those. The type can +implement it manually if they want, but can also just inherit the default +implementation if not. Let’s consider copy-ability. This isn’t a standard +library proposal, but we could implement some copy traits like this: + +```mojo + trait Copyable: + # Just a signature, no body. + fn copy(self) -> Self: ... + + trait ImplicitlyCopyable(Copyable): # Could use a better name :) + # A __copyinit__ is required, and this is the default implementation. + fn __copyinit__(inout self, borrowed existing: Self): + self = existing.copy() +``` + +Type may conform to the `Copyable` trait, which allows generic algorithms to +know it has a `copy()` method. Similarly, they may conform to +`ImplicitlyCopyable` to know it is implicitly copable (supports “`let a = b`”). +`ImplicitlyCopyable` requires the type to have a `copy()` method (because +`ImplicitlyCopyable` refines `Copyable`) and a `__copyinit__` method with the +specified signatures, and also provides a default implementation of the +`__copyinit__` method. + +This allows types to use it like this: + +```mojo + struct Int(ImplicitlyCopyable): + var value: __mlir_type.index + fn copy(self: Self) -> Self: + return Int{value: self.value} + + # Don't have to write this, we get a default impl from ImplicitlyCopyable + # fn __copyinit__(inout self, borrowed existing: Self): + # self = existing.copy() +``` + +This allows clients to implement a simple `copy()` method, but get the internal +machinery for free. + +The decorator is a different thing that layers on top of it. Decorators in +Python are functions that use metaprogramming to change the declaration they are +attached to. Python does this with dynamic metaprogramming, but we’ll use the +interpreter + built-ins operations to also enable static metaprogramming in +Mojo. + +I'm imagining that this will allow someone to write just: + +```mojo + @implicitlyCopyable + struct Int: + var value : __mlir_type.index +``` + +And the `implicitlyCopyable` function (which implements the decorator) would be +implemented to do two things: + +1. When it understands all the stored properties of a copy, because they are +built-in MLIR types like index, or because they themselves conform to at least +`Copyable`, it synthesizes an implementation of a `copy()` method that builds a +new instance of the type by invoking the `copy()` member for each element. + +2. It adds conformance to the `ImplicitlyCopyable` trait, which provides the +`__copyinit__` method above. + +This is all precedented in languages like Swift and Rust, but they both lack the +metaprogramming support to make the decorator synthesis logic implementable in +the standard library. Swift does this for things like the Hashable and +`Equatable` protocols. I believe that Mojo will be able to support much nicer +and more extensible designs. + +NOTE: The `@value` decorator provides some of this now. From 233023fcd28078ae55f07eb4196c5c925931578e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 4 May 2024 09:53:37 -0700 Subject: [PATCH 0415/2019] [mojo-stdlib] Move `Int` and `IntLiteral` __init__ of `-> Self` syntax (#39239) This moves off deprecated syntax. This patch also includes a type checker fix: use provided type in initself to bias overload resolution. When forming a constructor call to something like `IntLiteral()` we want to know that we're actually looking for `IntLiteral()` and not `Int()`. We know this by inferring the type from the placeholder self value. MODULAR_ORIG_COMMIT_REV_ID: 02c81f14bf2743c7c3835464a4e2389c65d3977e --- stdlib/src/builtin/int.mojo | 54 ++++++++--------------------- stdlib/src/builtin/int_literal.mojo | 19 +++------- 2 files changed, 20 insertions(+), 53 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b581306e7d..d526541b62 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -217,103 +217,79 @@ struct Int( """Returns the minimum value of type.""" @always_inline("nodebug") - fn __init__() -> Self: - """Default constructor. - - Returns: - The constructed Int object. - """ - return Self { - value: __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() - } + fn __init__(inout self): + """Default constructor that produces zero.""" + self.value = __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() @always_inline("nodebug") - fn __init__(value: __mlir_type.index) -> Self: + fn __init__(inout self, value: __mlir_type.index): """Construct Int from the given index value. Args: value: The init value. - - Returns: - The constructed Int object. """ - return Self {value: value} + self.value = value @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: + fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int16 value. Args: value: The init value. - - Returns: - The constructed Int object. """ - return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: + fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int32 value. Args: value: The init value. - - Returns: - The constructed Int object. """ - return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: + fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int64 value. Args: value: The init value. - - Returns: - The constructed Int object. """ - return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Self: + fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Index value. Args: value: The init value. - - Returns: - The constructed Int object. """ - return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) @always_inline("nodebug") - fn __init__(value: IntLiteral) -> Self: + fn __init__(inout self, value: IntLiteral): """Construct Int from the given IntLiteral value. Args: value: The init value. - - Returns: - The constructed Int object. """ - return value.__int__() + self = value.__int__() @always_inline("nodebug") fn __int__(self) -> Int: diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 14d63ab354..7a571d31d8 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -46,27 +46,18 @@ struct IntLiteral( ) @always_inline("nodebug") - fn __init__() -> Self: - """Default constructor. - - Returns: - The constructed IntLiteral object. - """ - return Self { - value: __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` - } + fn __init__(inout self): + """Default constructor.""" + self.value = __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` @always_inline("nodebug") - fn __init__(value: __mlir_type.`!kgen.int_literal`) -> Self: + fn __init__(inout self, value: __mlir_type.`!kgen.int_literal`): """Construct IntLiteral from the given mlir !kgen.int_literal value. Args: value: The init value. - - Returns: - The constructed IntLiteral object. """ - return Self {value: value} + self.value = value @always_inline("nodebug") fn __int__(self) -> Int: From a3e9e47fdfb06bd3bbe0128a0d3a961a024d3f3c Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sat, 4 May 2024 11:19:59 -0700 Subject: [PATCH 0416/2019] [Docs] Free memory in file read example (#39343) Remind users to free memory in example. MODULAR_ORIG_COMMIT_REV_ID: 0cb777a5190bccc7c1d77a3a8bbabd9c22c5d2b6 --- stdlib/src/builtin/file.mojo | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 1066903b5d..c4a7aab17b 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -251,6 +251,10 @@ struct FileHandle: var eleventh_element = ptr2[0] var twelvth_element = ptr2[1] print(eleventh_element, twelvth_element) + + # Free the memory + ptr.free() + ptr2.free() ``` . """ From f0741dd0f2d0735dc7b1e574ebbd1cae8caec1ee Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sat, 4 May 2024 11:22:01 -0700 Subject: [PATCH 0417/2019] [stdlib] Migrating memory.mojo use cases from Pointer -> UnsafePointer (#39341) Migrate more LegacyPointer use cases to UnsafePointer as possible. More couldn't be done in this PR because it causes knock-on changes in ffi.mojo and GPU host module mojo code. Those changes will come in future changes. MODULAR_ORIG_COMMIT_REV_ID: 5c9e8db87079781b38e4ceb9c6be30a707be7f5c --- stdlib/src/memory/memory.mojo | 58 +++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index adfc2a4c1e..014aeebdb5 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -24,7 +24,7 @@ from sys import llvm_intrinsic, sizeof, triple_is_nvidia_cuda from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace -from .unsafe import DTypePointer, Pointer +from .unsafe import DTypePointer, LegacyPointer # ===----------------------------------------------------------------------=== # # Utilities @@ -115,8 +115,8 @@ fn memcmp(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: fn memcmp[ type: AnyRegType, address_space: AddressSpace ]( - s1: Pointer[type, address_space], - s2: Pointer[type, address_space], + s1: LegacyPointer[type, address_space], + s2: LegacyPointer[type, address_space], count: Int, ) -> Int: """Compares two buffers. Both strings are assumed to be of the same length. @@ -154,7 +154,7 @@ fn memcmp[ @always_inline -fn memcpy[count: Int](dest: Pointer, src: __type_of(dest)): +fn memcpy[count: Int](dest: LegacyPointer, src: __type_of(dest)): """Copies a memory area. Parameters: @@ -223,7 +223,7 @@ fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): @always_inline -fn memcpy(dest: Pointer, src: __type_of(dest), count: Int): +fn memcpy(dest: LegacyPointer, src: __type_of(dest), count: Int): """Copies a memory area. Args: @@ -306,7 +306,7 @@ fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): @always_inline("nodebug") fn _memset_llvm[ address_space: AddressSpace -](ptr: Pointer[UInt8, address_space], value: UInt8, count: Int): +](ptr: UnsafePointer[UInt8, address_space], value: UInt8, count: Int): llvm_intrinsic["llvm.memset", NoneType]( ptr.address, value, count.value, False ) @@ -333,7 +333,7 @@ fn memset[ @always_inline fn memset[ type: AnyRegType, address_space: AddressSpace -](ptr: Pointer[type, address_space], value: UInt8, count: Int): +](ptr: UnsafePointer[type, address_space], value: UInt8, count: Int): """Fills memory with the given value. Parameters: @@ -348,6 +348,24 @@ fn memset[ _memset_llvm(ptr.bitcast[UInt8](), value, count * sizeof[type]()) +@always_inline +fn memset[ + type: AnyRegType, address_space: AddressSpace +](ptr: LegacyPointer[type, address_space], value: UInt8, count: Int): + """Fills memory with the given value. + + Parameters: + type: The element dtype. + address_space: The address space of the pointer. + + Args: + ptr: Pointer to the beginning of the memory block to fill. + value: The value to fill with. + count: Number of elements to fill (in elements, not bytes). + """ + _memset_llvm(ptr.bitcast[UInt8]().address, value, count * sizeof[type]()) + + # ===----------------------------------------------------------------------===# # memset_zero # ===----------------------------------------------------------------------===# @@ -373,7 +391,24 @@ fn memset_zero[ @always_inline fn memset_zero[ type: AnyRegType, address_space: AddressSpace -](ptr: Pointer[type, address_space], count: Int): +](ptr: UnsafePointer[type, address_space], count: Int): + """Fills memory with zeros. + + Parameters: + type: The element type. + address_space: The address space of the pointer. + + Args: + ptr: Pointer to the beginning of the memory block to fill. + count: Number of elements to fill (in elements, not bytes). + """ + memset(ptr, 0, count) + + +@always_inline +fn memset_zero[ + type: AnyRegType, address_space: AddressSpace +](ptr: LegacyPointer[type, address_space], count: Int): """Fills memory with zeros. Parameters: @@ -489,7 +524,7 @@ fn _malloc[ @always_inline -fn _free(ptr: Pointer): +fn _free(ptr: UnsafePointer): @parameter if triple_is_nvidia_cuda(): constrained[ @@ -504,3 +539,8 @@ fn _free(ptr: Pointer): @always_inline fn _free(ptr: DTypePointer): _free(ptr.address) + + +@always_inline +fn _free(ptr: LegacyPointer): + _free(UnsafePointer(ptr.address)) From f9d11e65c61920b92b6bc645b10f7efc494a9ba3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 4 May 2024 11:40:49 -0700 Subject: [PATCH 0418/2019] [mojo-lang] Enhance CheckLifetimes copy elision for 'inout self' regpass (#39345) This teaches CheckLifetimes to elide copies when used with the new 'inout self' pattern, and updates the remaining testcases that use the old `-> Self` pattern. MODULAR_ORIG_COMMIT_REV_ID: ca2bf66d59bf0e7556dcda85779fe109def75aaa --- proposals/lifetimes-and-provenance.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/lifetimes-and-provenance.md b/proposals/lifetimes-and-provenance.md index 176c751f3d..84286a8ef9 100644 --- a/proposals/lifetimes-and-provenance.md +++ b/proposals/lifetimes-and-provenance.md @@ -329,8 +329,8 @@ that it needs to work with as well as element type: alias pointer_type = __mlir_type[...] var address: pointer_type - fn __init__() -> Self: ... - fn __init__(address: pointer_type) -> Self: ... + fn __init__(inout self): ... + fn __init__(inout self, address: pointer_type): ... # Should this be an __init__ to allow implicit conversions? @static_method @@ -385,8 +385,8 @@ We may also want to wire up the prefix star operator into a dunder method. var ptr: MutablePointer[type, life] var size: Int - fn __init__() -> Self: - fn __init__(ptr: MutablePointer[type, life], size: Int) -> Self: + fn __init__(inout self): + fn __init__(inout self, ptr: MutablePointer[type, life], size: Int): # All the normal slicing operations etc, with bounds checks. fn __getitem__(self, offset: Int) -> inout[life] type: From 3a01ec981aa0ac1862f349ff33730cdbba1bec69 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 5 May 2024 07:09:04 -0700 Subject: [PATCH 0419/2019] [mojo-stdlib] Tidy up Arc (#39354) This makes a few changes to Arc: - renames ArcInner from 'data' to 'payload' to be more specific. - renames `Arc._type` to `Arc._inner_type` to be more specific. - Removes the `_data_ptr` unsafe member from Arc - you can use the existing `yourArc[]` thing to get a reference and do unsafe stuff with it instead if needed. - Removes the `set` method, which is redundant with `yourarc[] = newVal`. This also changes `EngineTensorView` to hold a TensorSpec instead of a (casted) Tensor pointer, simplifying the code and eliminating load bearing use of Tensor. This also removes an extraneous call to ownership.mark_destroyed from `Tuple.moveinit`. The destructor is never called on 'existing' in a moveinit or del. MODULAR_ORIG_COMMIT_REV_ID: b3e6d19ed58e17f6cbb139d8664916b2b8ca32a1 --- stdlib/src/builtin/tuple.mojo | 4 -- stdlib/src/memory/_arc.mojo | 74 +++++++++++++------------------- stdlib/test/memory/test_arc.mojo | 12 +----- 3 files changed, 30 insertions(+), 60 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 5857e59ae5..5bafc0bcbe 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -135,10 +135,6 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): unroll[initialize_elt, Self.__len__()]() - # We transfered all of the elements out of 'existing', so we need to - # disable its destructor so they aren't destroyed. - __mlir_op.`lit.ownership.mark_destroyed`(Reference(existing).value) - @always_inline @staticmethod fn __len__() -> Int: diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index ede308d33f..cd19143716 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -28,17 +28,15 @@ from memory import UnsafePointer, stack_allocation struct _ArcInner[T: Movable]: var refcount: Atomic[DType.int64] - var data: T + var payload: T fn __init__(inout self, owned value: T): self.refcount = 0 - self.data = value^ + self.payload = value^ - fn increment(inout self) -> Int64: - """Atomically increment the refcount. - `fetch_add` returns the old value, but for clarity of - correctness of the refcount logic we return the new value.""" - return self.refcount.fetch_add(1) + 1 + fn increment(inout self): + """Atomically increment the refcount.""" + _ = self.refcount.fetch_add(1) fn decrement(inout self) -> Int64: """Atomically decrement the refcount. @@ -65,7 +63,7 @@ struct Arc[T: Movable](CollectionElement): since a copy being created is always being created from another live copy on its own thread. - Note that the _interior_ of the data is not thread safe; the guarantees + Note that the _interior_ of the payload is not thread safe; the guarantees here apply only to the memory management. Calling `.set` or mutating the interior value in any way is not guaranteed to be safe! Any interior data that will be mutated should manage synchronization somehow. @@ -78,8 +76,8 @@ struct Arc[T: Movable](CollectionElement): T: The type of the stored value. """ - alias _type = _ArcInner[T] - var _inner: UnsafePointer[Self._type] + alias _inner_type = _ArcInner[T] + var _inner: UnsafePointer[Self._inner_type] fn __init__(inout self, owned value: T): """Construct a new thread-safe, reference-counted smart pointer, @@ -88,20 +86,19 @@ struct Arc[T: Movable](CollectionElement): Args: value: The value to manage. """ - self._inner = UnsafePointer[Self._type].alloc(1) - __get_address_as_uninit_lvalue(self._inner.address) = Self._type(value^) - _ = self._inner[].increment() - - fn __init__(inout self, *, owned inner: UnsafePointer[Self._type]): + self._inner = UnsafePointer[Self._inner_type].alloc(1) + # Cannot use initialize_pointee_move as _ArcInner isn't movable. + __get_address_as_uninit_lvalue(self._inner.address) = Self._inner_type( + value^ + ) + self._inner[].increment() + + fn __copyinit__(inout self, existing: Self): """Copy an existing reference. Increment the refcount to the object.""" - _ = inner[].increment() - self._inner = inner - - fn __copyinit__(inout self, other: Self): - """Copy an existing reference. Increment the refcount to the object.""" - # Order here does not matter since `other` is borrowed, and can't - # be destroyed until our copy completes. - self.__init__(inner=other._inner) + # Order here does not matter since `existing` can't be destroyed until + # sometime after we return. + existing._inner[].increment() + self._inner = existing._inner fn __moveinit__(inout self, owned existing: Self): """Move an existing reference.""" @@ -116,26 +113,11 @@ struct Arc[T: Movable](CollectionElement): var rc = self._inner[].decrement() if rc < 1: # Call inner destructor, then free the memory - _ = __get_address_as_owned_value(self._inner.address) + destroy_pointee(self._inner) self._inner.free() - fn set(self, owned new_value: T): - """Replace the existing value with a new value. The old value is deleted. - - Thread safety: This method is currently not thread-safe. The old value's - deleter is called and the new value's __moveinit__ is called. If either of - these occur while another thread is also trying to perform a `get` or `set` - operation, then functions may run or copy improperly initialized memory. - - If you want to mutate an Arc pointer, for now make sure you're doing it - in a single thread or with an internally-managed mutex. - - Args: - new_value: The new value to manage. Other pointers to the memory will - now see the new value. - """ - self._inner[].data = new_value^ - + # FIXME: This isn't right - the element should be mutable regardless + # of whether the 'self' type is mutable. fn __refitem__[ mutability: __mlir_type.i1, lifetime: AnyLifetime[mutability].type, @@ -147,10 +129,12 @@ struct Arc[T: Movable](CollectionElement): Returns: A Reference to the managed value. """ - return Reference(self)[]._data_ptr()[] + return Reference(self)[]._inner[].payload - fn _data_ptr(self) -> UnsafePointer[T]: - return UnsafePointer.address_of(self._inner[].data) + fn __init__(inout self, *, owned inner: UnsafePointer[Self._inner_type]): + """Copy an existing reference. Increment the refcount to the object.""" + inner[].increment() + self._inner = inner fn _bitcast[T2: Movable](self) -> Arc[T2]: constrained[ @@ -173,6 +157,6 @@ struct Arc[T: Movable](CollectionElement): # Add a +1 to the ref count, since we're creating a new `Arc` instance # pointing at the same data. - _ = self._inner[].increment() + self._inner[].increment() return Arc[T2](inner=ptr.bitcast[_ArcInner[T2]]()) diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index a3f0cbd9d1..22c1902870 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -22,7 +22,7 @@ from testing import assert_equal, assert_false, assert_true def test_basic(): var p = Arc(4) var p2 = p - p2.set(3) + p2[] = 3 assert_equal(3, p[]) @@ -49,16 +49,6 @@ def test_deleter_not_called_until_no_references(): assert_true(deleted) -def test_arc_bitcast(): - var arc_f32 = Arc[Scalar[DType.float32]](16.0) - - var arc_i32 = arc_f32._bitcast[Scalar[DType.int32]]() - - assert_equal(arc_f32[], 16.0) - assert_equal(arc_i32[], 1098907648) - - def main(): test_basic() test_deleter_not_called_until_no_references() - test_arc_bitcast() From bddcccd35fbd872f0bcc6103b562bf105a5922a7 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 5 May 2024 07:39:51 -0700 Subject: [PATCH 0420/2019] [mojo-stdlib] Continued cleanup of Arc. (#39356) This continues to clean up the Arc type: - It optimizes away the incref on construction by initializing the count to 1 instead of zero. - It renames the increment/decrement methods in ArcInner to add_ref/drop_ref to better model what they do. - It rewrites the doc comment to be more self contained instead of deferring to Rust. - It removes the `_bitcast` and `Arc(inner=)` initializer which was was wildly unsafe and incorrect in general. Removing the later requires refactoring some logic in NamedTensor, which exposed the fact that the `_bitcast` method left an extra reference. I filed MSDK-230 to track this. MODULAR_ORIG_COMMIT_REV_ID: 5c05e49c07edb56436c6cbfb49db9f6b3ca78135 --- stdlib/src/memory/_arc.mojo | 87 ++++++++----------------------------- 1 file changed, 19 insertions(+), 68 deletions(-) diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index cd19143716..d4e0b73bce 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -31,46 +31,30 @@ struct _ArcInner[T: Movable]: var payload: T fn __init__(inout self, owned value: T): - self.refcount = 0 + """Create an initialized instance of this with a refcount of 1.""" + self.refcount = 1 self.payload = value^ - fn increment(inout self): + fn add_ref(inout self): """Atomically increment the refcount.""" _ = self.refcount.fetch_add(1) - fn decrement(inout self) -> Int64: - """Atomically decrement the refcount. - `fetch_sub` returns the old value, but for clarity of - correctness of the refcount logic we return the new value.""" - return self.refcount.fetch_sub(1) - 1 + fn drop_ref(inout self) -> Bool: + """Atomically decrement the refcount and return true if the result + hits zero.""" + return self.refcount.fetch_sub(1) == 1 struct Arc[T: Movable](CollectionElement): - """Atomic reference-counted pointer inspired by Rust Arc. - - Semantics: - - Thread-safe, memory managed through atomic increments/decrements to the - reference count. - - Invariants: - - References to this object must not outlive the object (handled by language) - - References to this object must not cross a thread boundary - As long as both invariants hold, this type is thread-safe (although - the interior object may not be). Every reference points to a live - lvalue on its own thread, which means __copyinit__ may only - ever be called on an lvalue which is not currently in its __del__ - method. This prevents any race condition where one copy tries to - free the underlying memory while another copy is being created, - since a copy being created is always being created from another - live copy on its own thread. - - Note that the _interior_ of the payload is not thread safe; the guarantees - here apply only to the memory management. Calling `.set` or mutating the - interior value in any way is not guaranteed to be safe! Any interior data - that will be mutated should manage synchronization somehow. - - Copies use atomic reference counting for memory management. - - Copying the Arc object will increment the reference count in a thread-safe way. - - Deleting the Arc object will decrement the reference count in a thread-safe way, - and then call the custom deleter. + """Atomic reference-counted pointer. + + This smart pointer owns an instance of `T` indirectly managed on the heap. + This pointer is copyable, including across threads, maintaining a reference + count to the underlying data. + + This pointer itself is thread-safe using atomic accesses to reference count + the underlying data, but references returned to the underlying data are not + thread safe. Parameters: T: The type of the stored value. @@ -91,13 +75,12 @@ struct Arc[T: Movable](CollectionElement): __get_address_as_uninit_lvalue(self._inner.address) = Self._inner_type( value^ ) - self._inner[].increment() fn __copyinit__(inout self, existing: Self): """Copy an existing reference. Increment the refcount to the object.""" # Order here does not matter since `existing` can't be destroyed until # sometime after we return. - existing._inner[].increment() + existing._inner[].add_ref() self._inner = existing._inner fn __moveinit__(inout self, owned existing: Self): @@ -109,10 +92,8 @@ struct Arc[T: Movable](CollectionElement): Decrement the ref count for the reference. If there are no more references, delete the object and free its memory.""" - # Reference docs from Rust Arc: https://doc.rust-lang.org/src/alloc/sync.rs.html#2367-2402 - var rc = self._inner[].decrement() - if rc < 1: - # Call inner destructor, then free the memory + if self._inner[].drop_ref(): + # Call inner destructor, then free the memory. destroy_pointee(self._inner) self._inner.free() @@ -130,33 +111,3 @@ struct Arc[T: Movable](CollectionElement): A Reference to the managed value. """ return Reference(self)[]._inner[].payload - - fn __init__(inout self, *, owned inner: UnsafePointer[Self._inner_type]): - """Copy an existing reference. Increment the refcount to the object.""" - inner[].increment() - self._inner = inner - - fn _bitcast[T2: Movable](self) -> Arc[T2]: - constrained[ - sizeof[T]() == sizeof[T2](), - ( - "Arc._bitcast: Size of T and cast destination type T2 must be" - " the same" - ), - ]() - - constrained[ - alignof[T]() == alignof[T2](), - ( - "Arc._bitcast: Alignment of T and cast destination type T2 must" - " be the same" - ), - ]() - - var ptr: UnsafePointer[_ArcInner[T]] = self._inner - - # Add a +1 to the ref count, since we're creating a new `Arc` instance - # pointing at the same data. - self._inner[].increment() - - return Arc[T2](inner=ptr.bitcast[_ArcInner[T2]]()) From 3a7e7a280ebe6a8d9e9f3407543838f4dafae843 Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Sun, 5 May 2024 11:32:58 -0500 Subject: [PATCH 0421/2019] [External] [stdlib] Use `UnsafePointer` in `vector.mojo` (#38804) [External] [stdlib] Use `UnsafePointer` in `vector.mojo` As part of the migrating from `Pointer` to `UnsafePointer`, continue this in `vector.mojo`. This requires changing one bit to use `AnyType` rather than `AnyRegType` to play nicely with `UnsafePointer`. In the near future, we want to replace use of `AnyRegType` with `AnyType` throughout the library. --------- Co-authored-by: gabrieldemarmiesse Closes modularml/mojo#2377 MODULAR_ORIG_COMMIT_REV_ID: a9d3d17ea2dc2e092569829d632754c7c81a6a39 --- stdlib/src/collections/vector.mojo | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 51a9ff9632..58e1cd641f 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -19,7 +19,7 @@ from collections.vector import InlinedFixedVector ``` """ -from memory import Pointer, Reference +from memory import UnsafePointer, Reference from utils import StaticTuple @@ -31,14 +31,14 @@ from utils import StaticTuple @value struct _VecIter[ type: AnyRegType, - vec_type: AnyRegType, - deref: fn (Pointer[vec_type], Int) -> type, + vec_type: AnyType, + deref: fn (UnsafePointer[vec_type], Int) -> type, ](Sized): """Iterator for any random-access container""" var i: Int var size: Int - var vec: Pointer[vec_type] + var vec: UnsafePointer[vec_type] fn __next__(inout self) -> type: self.i += 1 @@ -100,7 +100,7 @@ struct InlinedFixedVector[ alias static_data_type = StaticTuple[type, size] var static_data: Self.static_data_type """The underlying static storage, used for small vectors.""" - var dynamic_data: Pointer[type] + var dynamic_data: UnsafePointer[type] """The underlying dynamic storage, used to grow large vectors.""" var current_size: Int """The number of elements in the vector.""" @@ -117,9 +117,9 @@ struct InlinedFixedVector[ capacity: The requested maximum capacity of the vector. """ self.static_data = Self.static_data_type() # Undef initialization - self.dynamic_data = Pointer[type]() + self.dynamic_data = UnsafePointer[type]() if capacity > Self.static_size: - self.dynamic_data = Pointer[type].alloc(capacity - size) + self.dynamic_data = UnsafePointer[type].alloc(capacity - size) self.current_size = 0 self.capacity = capacity @@ -231,7 +231,7 @@ struct InlinedFixedVector[ self.current_size = 0 @staticmethod - fn _deref_iter_impl(selfptr: Pointer[Self], i: Int) -> type: + fn _deref_iter_impl(selfptr: UnsafePointer[Self], i: Int, /) -> type: return selfptr[][i] alias _iterator = _VecIter[type, Self, Self._deref_iter_impl] @@ -243,5 +243,5 @@ struct InlinedFixedVector[ An iterator to the start of the vector. """ return Self._iterator( - 0, self.current_size, Reference(self).get_legacy_pointer() + 0, self.current_size, UnsafePointer(Reference(self)) ) From 59c5dadcadba4d718a6c4d05484279df9ec346fe Mon Sep 17 00:00:00 2001 From: Ethan Wu Date: Sun, 5 May 2024 11:34:41 -0500 Subject: [PATCH 0422/2019] [External] [docs] Correct link (#39359) [External] [docs] Correct link Fix link in `changelog-released.md`. Co-authored-by: Ethan Wu Closes https://github.com/modularml/mojo/pull/2500 MODULAR_ORIG_COMMIT_REV_ID: fd85d40b349792eee7f1c564ac98b5d0e160ae01 --- docs/changelog-released.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index f660f85df4..76cbfe4bb5 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -46,7 +46,7 @@ modular update mojo - [`initialize_pointee_copy`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy) - [`initialize_pointee_move`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) - - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) + - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee) - [`move_pointee`](/mojo/stdlib/memory/unsafe_pointer/move_pointee) - A new From 61a59e275d7bb62471d9e50d5d6057fd395acd32 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sun, 5 May 2024 11:56:37 -0500 Subject: [PATCH 0423/2019] [External] [stdlib] Remove FileCheck relics (#39360) [External] [stdlib] Remove FileCheck relics There are a lot of prints in the unit tests related to FileCheck. I removed the prints when FileCheck was not used. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2527 MODULAR_ORIG_COMMIT_REV_ID: 58a43c2c448288c786feaef444c1e0b5f02bd46c --- stdlib/test/builtin/test_file.mojo | 5 +---- stdlib/test/builtin/test_list.mojo | 4 ++-- stdlib/test/builtin/test_string.mojo | 5 ++--- stdlib/test/collections/test_optional.mojo | 2 -- stdlib/test/memory/test_memory.mojo | 4 ---- stdlib/test/os/test_getenv_setenv.mojo | 4 ---- stdlib/test/pathlib/test_pathlib.mojo | 9 --------- stdlib/test/utils/test_tuple.mojo | 4 ---- 8 files changed, 5 insertions(+), 32 deletions(-) diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 6ec549471b..8720ef2c60 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -129,7 +129,7 @@ def test_file_seek(): pos = f.seek(-16, os.SEEK_END) assert_equal(pos, 938) - print(f.read(6)) + f.read(6) # Seek from current possition, skip the space pos = f.seek(1, os.SEEK_CUR) @@ -195,10 +195,7 @@ struct Word: return word -# CHECK-LABEL: test_file_read_to_dtype_pointer def test_file_read_to_dtype_pointer(): - print("== test_file_read_to_dtype_pointer") - var f = open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") var ptr = DTypePointer[DType.int8].alloc(8) diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 4c4e1018ac..79bc5666b9 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -21,14 +21,14 @@ fn test_list() raises: fn test_variadic_list() raises: @parameter - fn print_list(*nums: Int) raises: + fn check_list(*nums: Int) raises: assert_equal(nums[0], 5) assert_equal(nums[1], 8) assert_equal(nums[2], 6) assert_equal(len(nums), 3) - print_list(5, 8, 6) + check_list(5, 8, 6) def main(): diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index b0b4143a44..6b491a88ac 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -558,7 +558,7 @@ fn test_upper() raises: fn test_isspace() raises: - print("checking true cases") + # checking true cases assert_true(isspace(ord(" "))) assert_true(isspace(ord("\n"))) assert_true(isspace(ord("\t"))) @@ -566,7 +566,7 @@ fn test_isspace() raises: assert_true(isspace(ord("\v"))) assert_true(isspace(ord("\f"))) - print("Checking false cases") + # Checking false cases assert_false(isspace(ord("a"))) assert_false(isspace(ord("u"))) assert_false(isspace(ord("s"))) @@ -612,7 +612,6 @@ fn test_lstrip() raises: fn test_strip() raises: - print("start strip") var empty_string = String("") assert_true(empty_string.strip() == "") diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index f2a843d369..828bc0b65b 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -61,8 +61,6 @@ def test_basic(): def test_optional_reg_basic(): - print("== test_optional_reg_basic") - var val: OptionalReg[Int] = None assert_false(val.__bool__()) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 8783f6f443..3165704608 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -36,7 +36,6 @@ struct Pair: def test_memcpy(): - print("== test_memcpy") var pair1 = Pair(1, 2) var pair2 = Pair(0, 0) @@ -89,7 +88,6 @@ def test_memcpy(): def test_memcpy_dtype(): - print("== test_memcpy_dtype") var a = DTypePointer[DType.int32].alloc(4) var b = DTypePointer[DType.int32].alloc(4) for i in range(4): @@ -113,7 +111,6 @@ def test_memcpy_dtype(): def test_memcmp(): - print("== test_memcmp") var pair1 = Pair(1, 2) var pair2 = Pair(1, 2) @@ -235,7 +232,6 @@ def test_memcmp_extensive(): def test_memset(): - print("== test_memset") var pair = Pair(1, 2) var ptr = Pointer.address_of(pair) diff --git a/stdlib/test/os/test_getenv_setenv.mojo b/stdlib/test/os/test_getenv_setenv.mojo index f24077571e..63fce8683f 100644 --- a/stdlib/test/os/test_getenv_setenv.mojo +++ b/stdlib/test/os/test_getenv_setenv.mojo @@ -19,8 +19,6 @@ from testing import assert_equal def test_getenv(): - print("== test_getenv") - assert_equal(getenv("TEST_MYVAR"), "MyValue") assert_equal(getenv("TEST_MYVAR", "DefaultValue"), "MyValue") @@ -30,8 +28,6 @@ def test_getenv(): # CHECK-OK-LABEL: test_setenv def test_setenv(): - print("== test_setenv") - assert_equal(setenv("NEW_VAR", "FOO", True), True) assert_equal(getenv("NEW_VAR"), "FOO") diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index aa416249d4..11e1812bc1 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -23,15 +23,10 @@ alias TEMP_FILE = env_get_string["TEMP_FILE"]() def test_cwd(): - print("== test_cwd") - - # CHECK-NOT: unable to query the current directory assert_true(str(cwd()).startswith("/")) def test_path(): - print("== test_path") - assert_true(str(Path() / "some" / "dir").endswith("/some/dir")) assert_equal(str(Path("/foo") / "bar" / "jar"), "/foo/bar/jar") @@ -46,21 +41,17 @@ def test_path(): def test_path_exists(): - print("== test_path_exists") - assert_true(Path(__source_location().file_name).exists(), "does not exist") assert_false((Path() / "this_path_does_not_exist.mojo").exists(), "exists") def test_path_isdir(): - print("== test_path_isdir") assert_true(Path().is_dir()) assert_false((Path() / "this_path_does_not_exist").is_dir()) def test_path_isfile(): - print("== test_path_isfile") assert_true(Path(__source_location().file_name).is_file()) assert_false(Path("this/file/does/not/exist").is_file()) diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index b671e6dc41..c28e68baf4 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -18,8 +18,6 @@ from utils import StaticTuple, StaticIntTuple, InlineArray def test_static_tuple(): - print("== test_static_tuple") - var tup1 = StaticTuple[Int, 1](1) assert_equal(tup1[0], 1) @@ -38,7 +36,6 @@ def test_static_tuple(): def test_static_int_tuple(): - print("== test_static_int_tuple") assert_equal(str(StaticIntTuple[1](1)), "(1,)") assert_equal(str(StaticIntTuple[3](2)), "(2, 2, 2)") @@ -75,7 +72,6 @@ def test_static_int_tuple(): def test_tuple_literal(): - print("== test_tuple_literal\n") assert_equal(len((1, 2, (3, 4), 5)), 4) assert_equal(len(()), 0) From 104acea6ae5d9fe1223e47963db0d2d7c692e200 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 5 May 2024 19:09:25 -0700 Subject: [PATCH 0424/2019] [mojo-stdlib] Massively simplify parametric mutability reference usage (#39369) Now that we can declare `self` to be a `Reference` and not just a `!lit.ref` we can use parameter inference to deduce the mutability and lifetime and use `_` expressions to avoid having to explicitly declare them. This radically simplifies the usage across the stdlib. MODULAR_ORIG_COMMIT_REV_ID: 123cd20425d4a4adcd609cc74a254e8ab116f29f --- stdlib/src/builtin/reversed.mojo | 24 ++-- stdlib/src/builtin/tuple.mojo | 14 +-- stdlib/src/collections/dict.mojo | 160 +++++++-------------------- stdlib/src/collections/list.mojo | 47 +++----- stdlib/src/collections/optional.mojo | 18 +-- stdlib/src/collections/set.mojo | 18 +-- stdlib/src/memory/_arc.mojo | 11 +- stdlib/src/testing/testing.mojo | 4 +- stdlib/src/utils/static_tuple.mojo | 42 ++----- stdlib/src/utils/variant.mojo | 24 ++-- 10 files changed, 103 insertions(+), 259 deletions(-) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index ab39416960..1e33320912 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -77,21 +77,17 @@ fn reversed[T: ReversibleRange](value: T) -> _StridedRangeIterator: fn reversed[ - mutability: __mlir_type.`i1`, - self_life: AnyLifetime[mutability].type, - T: CollectionElement, + T: CollectionElement ]( - value: Reference[List[T], mutability, self_life]._mlir_type, + value: Reference[List[T], _, _], ) -> _ListIter[ - T, mutability, self_life, False + T, value.is_mutable, value.lifetime, False ]: """Get a reversed iterator of the input list. **Note**: iterators are currently non-raising. Parameters: - mutability: Whether the reference to the list is mutable. - self_life: The lifetime of the list. T: The type of the elements in the list. Args: @@ -100,24 +96,22 @@ fn reversed[ Returns: The reversed iterator of the list. """ - return Reference(value)[].__reversed__[mutability, self_life]() + return value[].__reversed__() fn reversed[ - mutability: __mlir_type.`i1`, - self_life: AnyLifetime[mutability].type, K: KeyElement, V: CollectionElement, ]( - value: Reference[Dict[K, V], mutability, self_life]._mlir_type, -) -> _DictKeyIter[K, V, mutability, self_life, False]: + value: Reference[Dict[K, V], _, _], +) -> _DictKeyIter[ + K, V, value.is_mutable, value.lifetime, False +]: """Get a reversed iterator of the input dict. **Note**: iterators are currently non-raising. Parameters: - mutability: Whether the reference to the dict is mutable. - self_life: The lifetime of the dict. K: The type of the keys in the dict. V: The type of the values in the dict. @@ -127,4 +121,4 @@ fn reversed[ Returns: The reversed iterator of the dict. """ - return Reference(value)[].__reversed__[mutability, self_life]() + return value[].__reversed__() diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 5bafc0bcbe..5dc9622222 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -164,23 +164,19 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn __refitem__[ - idx: Int, - mutability: __mlir_type.i1, - self_life: AnyLifetime[mutability].type, - ](self_lit: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ - element_types[idx.value], mutability, self_life + idx: Int + ](self: Reference[Self, _, _]) -> Reference[ + element_types[idx.value], self.is_mutable, self.lifetime ]: # Return a reference to an element at the specified index, propagating # mutability of self. - var storage_kgen_ptr = UnsafePointer.address_of( - Reference(self_lit)[].storage - ).address + var storage_kgen_ptr = UnsafePointer.address_of(self[].storage).address # KGenPointer to the element. var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( storage_kgen_ptr ) - # Convert to an immortal mut reference, which conforms to self_life. + # Use an immortal mut reference, which converts to self's lifetime. return UnsafePointer(elt_kgen_ptr)[] # TODO(#38268): Remove this method when references and parameter expressions diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index c1374734d3..91fd7c9749 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -96,11 +96,8 @@ struct _DictEntryIter[ else: debug_assert(self.index >= 0, "dict iter bounds") - if self.src[]._entries.__get_ref(self.index)[]: - var opt_entry_ref = self.src[]._entries.__get_ref[ - __mlir_attr.`0: i1`, - Self.imm_dict_lifetime, - ](self.index) + var opt_entry_ref = self.src[]._entries.__get_ref(self.index) + if opt_entry_ref[]: @parameter if forward: @@ -109,9 +106,7 @@ struct _DictEntryIter[ self.index -= 1 self.seen += 1 - # Super unsafe, but otherwise we have to do a bunch of super - # unsafe reference lifetime casting. - return opt_entry_ref.unsafe_bitcast[DictEntry[K, V]]() + return opt_entry_ref[].value()[] @parameter if forward: @@ -644,67 +639,39 @@ struct Dict[K: KeyElement, V: CollectionElement]( return default.value()[] raise "KeyError" - fn __iter__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictKeyIter[ - K, V, mutability, self_life - ]: + fn __iter__( + self: Reference[Self, _, _], + ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: """Iterate over the dict's keys as immutable references. - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of immutable references to the dictionary keys. """ - return _DictKeyIter( - _DictEntryIter[K, V, mutability, self_life](0, 0, Reference(self)) - ) + return _DictKeyIter(_DictEntryIter(0, 0, self)) - fn keys[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictKeyIter[ - K, V, mutability, self_life - ]: + fn keys( + self: Reference[Self, _, _] + ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: """Iterate over the dict's keys as immutable references. - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of immutable references to the dictionary keys. """ return Self.__iter__(self) - fn values[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictValueIter[K, V, mutability, self_life]: + fn values( + self: Reference[Self, _, _] + ) -> _DictValueIter[K, V, self.is_mutable, self.lifetime]: """Iterate over the dict's values as references. - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of references to the dictionary values. """ - return _DictValueIter( - _DictEntryIter[K, V, mutability, self_life](0, 0, Reference(self)) - ) + return _DictValueIter(_DictEntryIter(0, 0, self)) - fn items[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictEntryIter[K, V, mutability, self_life]: + fn items( + self: Reference[Self, _, _] + ) -> _DictEntryIter[K, V, self.is_mutable, self.lifetime]: """Iterate over the dict's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -715,16 +682,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( print(e[].key, e[].value) ``` - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of immutable references to the dictionary entries. """ - return _DictEntryIter[K, V, mutability, self_life]( - 0, 0, Reference(self) - ) + return _DictEntryIter(0, 0, self) fn update(inout self, other: Self, /): """Update the dictionary with the key/value pairs from other, overwriting existing keys. @@ -838,23 +799,16 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._n_entries = self.size - fn __reversed__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictKeyIter[ - K, V, mutability, self_life, False - ]: + fn __reversed__( + self: Reference[Self, _, _] + ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime, False]: """Iterate backwards over the dict keys, returning immutable references. Returns: A reversed iterator of immutable references to the dict keys. """ - var ref = Reference(self) return _DictKeyIter( - _DictEntryIter[K, V, mutability, self_life, False]( - ref[]._reserved - 1, 0, ref - ) + _DictEntryIter[forward=False](self[]._reserved - 1, 0, self) ) @@ -970,43 +924,23 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ return self._dict.pop(key, default^) - fn __iter__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictKeyIter[ - Self.key_type, V, mutability, self_life - ]: + fn __iter__( + self: Reference[Self, _, _] + ) -> _DictKeyIter[Self.key_type, V, self.is_mutable, self.lifetime]: """Iterate over the keyword dict's keys as immutable references. - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of immutable references to the dictionary keys. """ # TODO(#36448): Use this instead of the current workaround # return self._dict.__iter__() - return _DictKeyIter( - _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, Reference(self)[]._dict - ) - ) + return _DictKeyIter(_DictEntryIter(0, 0, self[]._dict)) - fn keys[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictKeyIter[ - Self.key_type, V, mutability, self_life - ]: + fn keys( + self: Reference[Self, _, _], + ) -> _DictKeyIter[Self.key_type, V, self.is_mutable, self.lifetime]: """Iterate over the keyword dict's keys as immutable references. - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of immutable references to the dictionary keys. """ @@ -1014,33 +948,21 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): # return self._dict.keys() return Self.__iter__(self) - fn values[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictValueIter[Self.key_type, V, mutability, self_life]: + fn values( + self: Reference[Self, _, _], + ) -> _DictValueIter[Self.key_type, V, self.is_mutable, self.lifetime]: """Iterate over the keyword dict's values as references. - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of references to the dictionary values. """ # TODO(#36448): Use this instead of the current workaround # return self._dict.values() - return _DictValueIter( - _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, Reference(self)[]._dict - ) - ) + return _DictValueIter(_DictEntryIter(0, 0, self[]._dict)) - fn items[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictEntryIter[Self.key_type, V, mutability, self_life]: + fn items( + self: Reference[Self, _, _] + ) -> _DictEntryIter[Self.key_type, V, self.is_mutable, self.lifetime]: """Iterate over the keyword dictionary's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1051,19 +973,13 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): print(e[].key, e[].value) ``` - Parameters: - mutability: Whether the dict is mutable. - self_life: The dict's lifetime. - Returns: An iterator of immutable references to the dictionary entries. """ # TODO(#36448): Use this instead of the current workaround - # return Reference(self)[]._dict.items() - return _DictEntryIter[Self.key_type, V, mutability, self_life]( - 0, 0, Reference(self)[]._dict - ) + # return self[]._dict.items() + return _DictEntryIter(0, 0, self[]._dict) @always_inline("nodebug") fn _insert(inout self, owned key: Self.key_type, owned value: V): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index cceaffccd8..38b9b62b78 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -59,14 +59,10 @@ struct _ListIter[ @parameter if forward: self.index += 1 - return self.src[].__get_ref[list_mutability, list_lifetime]( - self.index - 1 - ) + return self.src[].__get_ref(self.index - 1) else: self.index -= 1 - return self.src[].__get_ref[list_mutability, list_lifetime]( - self.index - ) + return self.src[].__get_ref(self.index) fn __len__(self) -> Int: @parameter @@ -565,12 +561,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return (self.data + normalized_idx)[] # TODO(30737): Replace __getitem__ with this as __refitem__, but lots of places use it - fn __get_ref[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - i: Int, - ) -> Reference[T, mutability, self_life]: + fn __get_ref( + self: Reference[Self, _, _], i: Int + ) -> Reference[T, self.is_mutable, self.lifetime]: """Gets a reference to the list element at the given index. Args: @@ -581,39 +574,29 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ var normalized_idx = i if i < 0: - normalized_idx += Reference(self)[].size + normalized_idx += self[].size - var offset_ptr = Reference(self)[].data + normalized_idx - return offset_ptr[] + return (self[].data + normalized_idx)[] - fn __iter__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _ListIter[ - T, mutability, self_life - ]: + fn __iter__( + self: Reference[Self, _, _], + ) -> _ListIter[T, self.is_mutable, self.lifetime]: """Iterate over elements of the list, returning immutable references. Returns: An iterator of immutable references to the list elements. """ - return _ListIter[T, mutability, self_life](0, Reference(self)) + return _ListIter(0, self) - fn __reversed__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _ListIter[ - T, mutability, self_life, False - ]: + fn __reversed__( + self: Reference[Self, _, _] + ) -> _ListIter[T, self.is_mutable, self.lifetime, False]: """Iterate backwards over the list, returning immutable references. Returns: A reversed iterator of immutable references to the list elements. """ - var ref = Reference(self) - return _ListIter[T, mutability, self_life, False](len(ref[]), ref) + return _ListIter[forward=False](len(self[]), self) @staticmethod fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 076a71da3c..562c1515df 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -100,11 +100,9 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): self = Self() @always_inline - fn value[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type - ](self: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ - T, mutability, self_life - ]: + fn value( + self: Reference[Self, _, _] + ) -> Reference[T, self.is_mutable, self.lifetime]: """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. @@ -112,16 +110,12 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): eg. by `if my_option:` or without otherwise knowing that it contains a value (for instance with `or_else`), you'll get garbage unsafe data out. - Parameters: - mutability: Whether the Optional is mutable. - self_life: The Optional's lifetime. - Returns: A reference to the contained data of the option as a Reference[T]. """ - debug_assert(Reference(self)[].__bool__(), ".value() on empty Optional") - alias RefType = Reference[T, mutability, self_life] - var ptr = Reference(self)[]._value._get_ptr[T]().address + debug_assert(self[].__bool__(), ".value() on empty Optional") + alias RefType = Reference[T, self.is_mutable, self.lifetime] + var ptr = self[]._value._get_ptr[T]().address return __mlir_op.`lit.ref.from_pointer`[_type = RefType._mlir_type](ptr) @always_inline diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index bcb2c1dc25..08a9215f1c 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -283,27 +283,17 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): """ self.symmetric_difference_update(other) - fn __iter__[ - mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - ) -> _DictKeyIter[ - T, NoneType, mutability, self_life - ]: + fn __iter__( + self: Reference[Self, _, _], + ) -> _DictKeyIter[T, NoneType, self.is_mutable, self.lifetime]: """Iterate over elements of the set, returning immutable references. Returns: An iterator of immutable references to the set elements. """ - # self._data has its own lifetime that's not self_lifetime # here we rely on Set being a trivial wrapper of a Dict return _DictKeyIter( - _DictEntryIter[ - T, - NoneType, - mutability, - self_life, - ](0, 0, Reference(self).unsafe_bitcast[Dict[T, NoneType]]()) + _DictEntryIter(0, 0, self.unsafe_bitcast[Dict[T, NoneType]]()) ) fn add(inout self, t: T): diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index d4e0b73bce..140d7dc289 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -99,15 +99,12 @@ struct Arc[T: Movable](CollectionElement): # FIXME: This isn't right - the element should be mutable regardless # of whether the 'self' type is mutable. - fn __refitem__[ - mutability: __mlir_type.i1, - lifetime: AnyLifetime[mutability].type, - ](self: Reference[Self, mutability, lifetime]._mlir_type) -> Reference[ - T, mutability, lifetime - ]: + fn __refitem__( + self: Reference[Self, _, _] + ) -> Reference[T, self.is_mutable, self.lifetime]: """Returns a Reference to the managed value. Returns: A Reference to the managed value. """ - return Reference(self)[]._inner[].payload + return self[]._inner[].payload diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 035e02f896..bcc48ab732 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -367,7 +367,5 @@ struct assert_raises: Error: If the error raised doesn't match the expected error to raise. """ if self.message_contains: - return self.message_contains.value[ - __mlir_attr.`0: i1`, __lifetime_of(self) - ]()[] in str(error) + return self.message_contains.value()[] in str(error) return True diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index f9d8bdc106..e728193f38 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -319,38 +319,28 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): return size @always_inline("nodebug") - fn _get_reference_unsafe[ - mutability: __mlir_type.i1, - self_life: AnyLifetime[mutability].type, - ]( - self: Reference[Self, mutability, self_life]._mlir_type, index: Int - ) -> Reference[Self.ElementType, mutability, self_life]: + fn _get_reference_unsafe( + self: Reference[Self, _, _], index: Int + ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: """Get a reference to an element of self without checking index bounds. Users should opt for `__refitem__` instead of this method. """ var ptr = __mlir_op.`pop.array.gep`( - Reference(Reference(self)[]._array).get_legacy_pointer().address, + UnsafePointer.address_of(self[]._array).address, index.value, ) - return Reference[Self.ElementType, mutability, self_life]( - UnsafePointer(ptr)[] - ) + return UnsafePointer(ptr)[] @always_inline("nodebug") fn __refitem__[ - mutability: __mlir_type.i1, - self_life: AnyLifetime[mutability].type, IntableType: Intable, - ]( - self: Reference[Self, mutability, self_life]._mlir_type, - index: IntableType, - ) -> Reference[Self.ElementType, mutability, self_life]: + ](self: Reference[Self, _, _], index: IntableType) -> Reference[ + Self.ElementType, self.is_mutable, self.lifetime + ]: """Get a `Reference` to the element at the given index. Parameters: - mutability: The inferred mutability of the reference. - self_life: The inferred lifetime of the reference. IntableType: The inferred type of an intable argument. Args: @@ -364,24 +354,18 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): if normalized_idx < 0: normalized_idx += size - return Reference(self)[]._get_reference_unsafe[mutability, self_life]( - normalized_idx - ) + return self[]._get_reference_unsafe(normalized_idx) @always_inline("nodebug") fn __refitem__[ - mutability: __mlir_type.i1, - self_life: AnyLifetime[mutability].type, IntableType: Intable, index: IntableType, - ](self: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ - Self.ElementType, mutability, self_life + ](self: Reference[Self, _, _]) -> Reference[ + Self.ElementType, self.is_mutable, self.lifetime ]: """Get a `Reference` to the element at the given index. Parameters: - mutability: The inferred mutability of the reference. - self_life: The inferred lifetime of the reference. IntableType: The inferred type of an intable argument. index: The index of the item. @@ -397,6 +381,4 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): if i < 0: normalized_idx += size - return Reference(self)[]._get_reference_unsafe[mutability, self_life]( - normalized_idx - ) + return self[]._get_reference_unsafe(normalized_idx) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 569fce8454..63e1f21e5c 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -151,12 +151,10 @@ struct Variant[*Ts: CollectionElement](CollectionElement): ]() return UnsafePointer.address_of(self._impl).bitcast[T]() - fn _get_state[ - is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] - ](self: Reference[Self, is_mut, lt]._mlir_type) -> Reference[ - Int8, is_mut, lt - ]: - var int8_self = UnsafePointer.address_of(self).bitcast[Int8]() + fn _get_state( + self: Reference[Self, _, _] + ) -> Reference[Int8, self.is_mutable, self.lifetime]: + var int8_self = UnsafePointer(self).bitcast[Int8]() return (int8_self + _UnionSize[Ts].compute())[] fn __init__[T: CollectionElement](inout self, owned value: T): @@ -283,11 +281,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): return self._get_state()[] == idx fn get[ - T: CollectionElement, - mutability: __mlir_type.i1, - self_life: AnyLifetime[mutability].type, - ](self: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ - T, mutability, self_life + T: CollectionElement + ](self: Reference[Self, _, _]) -> Reference[ + T, self.is_mutable, self.lifetime ]: """Get the value out of the variant as a type-checked type. @@ -301,14 +297,12 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Parameters: T: The type of the value to get out. - mutability: The inferred mutability of the variant type. - self_life: The inferred lifetime of the variant type. Returns: The internal data represented as a `Reference[T]`. """ - debug_assert(Reference(self)[].isa[T](), "get: wrong variant type") - return Reference(self)[]._get_ptr[T]()[] + debug_assert(self[].isa[T](), "get: wrong variant type") + return self[]._get_ptr[T]()[] @staticmethod fn _check[T: CollectionElement]() -> Int8: From dc7e6f6457ae0a360723412020fb1d65875efdf2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 5 May 2024 22:44:35 -0700 Subject: [PATCH 0425/2019] [mojo-lang] Update the MOF + testsuite for self of `Reference` type. (#39371) This significantly improves the ModularFramework ball examples, adds a changelog entry, and improves the MojoParser testsuite examples. MODULAR_ORIG_COMMIT_REV_ID: 2e143a484cd335c800281b431e1baa70b2261bf4 --- docs/changelog.md | 5 +++++ stdlib/src/collections/optional.mojo | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b6a81a2b1c..5061e0d865 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -92,6 +92,11 @@ what we publish. - The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that allow users to opt into the `math.ceildiv` function. +- Mojo now allows methods to declare `self` as a `Reference` directly, which + can be useful for advanced cases of parametric mutabilty and custom lifetime + processing. Previously it required the use of an internal MLIR type to + achieve this. + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 562c1515df..1a2703a1f4 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -114,9 +114,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): A reference to the contained data of the option as a Reference[T]. """ debug_assert(self[].__bool__(), ".value() on empty Optional") - alias RefType = Reference[T, self.is_mutable, self.lifetime] - var ptr = self[]._value._get_ptr[T]().address - return __mlir_op.`lit.ref.from_pointer`[_type = RefType._mlir_type](ptr) + return self[]._value.get[T]() @always_inline fn _value_copy(self) -> T: From c9117e9064f5c5d6216af0203f8b369aae327a85 Mon Sep 17 00:00:00 2001 From: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Date: Mon, 6 May 2024 10:04:06 -0500 Subject: [PATCH 0426/2019] [External] [mojo-stdlib] implement all bitwise operators for `object` (#39352) [External] [mojo-stdlib] implement all bitwise operators for `object` Fix #2320 Co-authored-by: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Closes modularml/mojo#2324 MODULAR_ORIG_COMMIT_REV_ID: 2bfb70805647e43aed61c5df3602cb6976a583b9 --- docs/changelog.md | 3 + stdlib/src/builtin/object.mojo | 89 +++++++++++++++++++++------- stdlib/test/builtin/test_object.mojo | 24 +++++++- 3 files changed, 91 insertions(+), 25 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5061e0d865..df00d4c126 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -97,6 +97,9 @@ what we publish. processing. Previously it required the use of an internal MLIR type to achieve this. +- `object` now implements all the bitwise operators. + ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 43553dcf39..f7a5494312 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1206,6 +1206,30 @@ struct object(IntableRaising, Boolable, Stringable): return fp_func(lhsValue.get_as_float(), rhsValue.get_as_float()) return int_func(lhsValue.get_as_int(), rhsValue.get_as_int()) + @staticmethod + @always_inline + fn _arithmetic_bitwise_op[ + int_func: fn (Int64, Int64) -> Int64, + bool_func: fn (Bool, Bool) -> Bool, + ](lhs: object, rhs: object) raises -> object: + """Generic bitwise operator. + + Parameters: + int_func: Integer operator. + bool_func: Boolean operator. + + Returns: + The bitwise operation result. + """ + lhs._arithmetic_integral_type_check() + rhs._arithmetic_integral_type_check() + var lhsValue = lhs._value + var rhsValue = rhs._value + _ObjectImpl.coerce_integral_type(lhsValue, rhsValue) + if lhsValue.is_int(): + return int_func(lhsValue.get_as_int(), rhsValue.get_as_int()) + return bool_func(lhsValue.get_as_bool(), rhsValue.get_as_bool()) + @always_inline fn __neg__(self) raises -> object: """Negation operator. Only valid for bool, int, and float @@ -1386,8 +1410,7 @@ struct object(IntableRaising, Boolable, Stringable): @always_inline fn __and__(self, rhs: object) raises -> object: - """Bool AND operator. If the left hand value is False, return the - left-hand value. + """Bitwise AND operator. Args: rhs: Right hand value. @@ -1395,14 +1418,13 @@ struct object(IntableRaising, Boolable, Stringable): Returns: The current value if it is False. """ - if not self: - return self - return rhs + return Self._arithmetic_bitwise_op[Int64.__and__, Bool.__and__]( + self, rhs + ) @always_inline fn __or__(self, rhs: object) raises -> object: - """Bool OR operator. If the left hand value is True, return the - left-hand value. + """Bitwise OR operator. Args: rhs: Right hand value. @@ -1410,11 +1432,21 @@ struct object(IntableRaising, Boolable, Stringable): Returns: The current value if it is True. """ - if self: - return self - return rhs + return Self._arithmetic_bitwise_op[Int64.__or__, Bool.__or__](self, rhs) + + @always_inline + fn __xor__(self, rhs: object) raises -> object: + """Bitwise XOR operator. - # TODO: __xor__ + Args: + rhs: Right hand value. + + Returns: + The current value if it is True. + """ + return Self._arithmetic_bitwise_op[Int64.__xor__, Bool.__xor__]( + self, rhs + ) # ===------------------------------------------------------------------=== # # In-Place Operators @@ -1508,8 +1540,7 @@ struct object(IntableRaising, Boolable, Stringable): Args: rhs: Right hand value. """ - if self: - self = rhs + self = self & rhs @always_inline fn __ior__(inout self, rhs: object) raises: @@ -1518,10 +1549,16 @@ struct object(IntableRaising, Boolable, Stringable): Args: rhs: Right hand value. """ - if not self: - self = rhs + self = self | rhs - # TODO: __ixor__ + @always_inline + fn __ixor__(inout self, rhs: object) raises: + """In-place XOR operator. + + Args: + rhs: Right hand value. + """ + self = self ^ rhs # ===------------------------------------------------------------------=== # # Reversed Operators @@ -1645,9 +1682,7 @@ struct object(IntableRaising, Boolable, Stringable): Returns: The bitwise AND of the left-hand-side value and this. """ - if not lhs: - return lhs - return self + return lhs & self @always_inline fn __ror__(self, lhs: object) raises -> object: @@ -1659,11 +1694,19 @@ struct object(IntableRaising, Boolable, Stringable): Returns: The bitwise OR of the left-hand-side value and this. """ - if lhs: - return lhs - return self + return lhs | self + + @always_inline + fn __rxor__(self, lhs: object) raises -> object: + """Reverse XOR operator. - # TODO: __rxor__ + Args: + lhs: Left hand value. + + Returns: + The bitwise XOR of the left-hand-side value and this. + """ + return lhs ^ self # ===------------------------------------------------------------------=== # # Interface diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index c4bf6c264f..446d142101 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -146,7 +146,7 @@ def test_arithmetic_ops_div(): assert_true(5 // object(2) == 2) -def test_object_shift(): +def test_object_bitwise(): a = object(1) b = object(2) assert_true(a << b == 4) @@ -155,11 +155,30 @@ def test_object_shift(): b <<= a assert_true(b == 4) b >>= a - assert_true(b == 1) + assert_true(b == 2) assert_true(2 << object(1) == 4) assert_true(2 >> object(1) == 1) + assert_true(object(15) & object(7) == 7) + assert_true(object(15) | object(7) == 15) + assert_true(object(15) ^ object(7) == 8) + + a = object(15) + b = object(7) + a &= b + assert_true(a == 7) + a = object(15) + a |= b + assert_true(a == 15) + a = object(15) + a ^= b + assert_true(a == 8) + + assert_true(15 & object(7) == 7) + assert_true(15 | object(7) == 15) + assert_true(15 ^ object(7) == 8) + def test_function(borrowed lhs, borrowed rhs) -> object: return lhs + rhs @@ -296,6 +315,7 @@ def main(): test_comparison_ops() test_arithmetic_ops() test_arithmetic_ops_div() + test_object_bitwise() test_object_function() test_non_object_getattr() test_matrix() From 759f2c102492baec0ddb45478ec451ba4d5810da Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 6 May 2024 10:07:23 -0700 Subject: [PATCH 0427/2019] [mojo-stdlib] Remove `Reference.unsafe_bitcast`. (#39389) This is only used in two places and they have better spellings anyway. This gets Reference closer to it's ideal API - of effectively no methods. MODULAR_ORIG_COMMIT_REV_ID: 89e74fecc587ef91e4667975feb5edf36317eefb --- stdlib/src/collections/set.mojo | 4 +--- stdlib/src/memory/reference.mojo | 20 -------------------- stdlib/src/utils/variant.mojo | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 08a9215f1c..d653b40aa2 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -292,9 +292,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): An iterator of immutable references to the set elements. """ # here we rely on Set being a trivial wrapper of a Dict - return _DictKeyIter( - _DictEntryIter(0, 0, self.unsafe_bitcast[Dict[T, NoneType]]()) - ) + return _DictKeyIter(_DictEntryIter(0, 0, self[]._data)) fn add(inout self, t: T): """Add an element to the set. diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index a696da784f..38ef1122b8 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -266,23 +266,3 @@ struct Reference[ return __mlir_op.`pop.pointer.bitcast`[ _type = LegacyPointer[type, address_space]._mlir_type ](UnsafePointer(self).address) - - @always_inline("nodebug") - fn unsafe_bitcast[ - new_element_type: AnyType = type, - /, - address_space: AddressSpace = Self.address_space, - ](self) -> Reference[new_element_type, is_mutable, lifetime, address_space]: - """Cast the reference to one of another element type and AddressSpace, - but the same lifetime and mutability. - - Parameters: - new_element_type: The result type. - address_space: The address space of the result. - - Returns: - The new reference. - """ - # We don't have a `lit.ref.cast`` operation, so convert through a KGEN - # pointer. - return UnsafePointer(self).bitcast[new_element_type, address_space]()[] diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 63e1f21e5c..1ffefc1fd8 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -187,7 +187,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): alias T = Ts[i] initialize_pointee_move( UnsafePointer.address_of(self._impl).bitcast[T](), - Reference(other._impl).unsafe_bitcast[T]()[], + UnsafePointer.address_of(other._impl).bitcast[T]()[], ) unroll[each, len(VariadicList(Ts))]() From 2e30e8f36ce1b9c914b662f2d95c91fed403b9f7 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 6 May 2024 13:52:56 -0400 Subject: [PATCH 0428/2019] [stdlib] Publish Arc (#39385) After clean ups to `Arc` class, it is ready to be moved out of `_arc.mojo` into `arc.mojo`. MODULAR_ORIG_COMMIT_REV_ID: 8daa43d6064c5c706029fe24c01f9efbcf2f6d6e --- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/memory/__init__.mojo | 4 +++- stdlib/src/memory/{_arc.mojo => arc.mojo} | 12 ++++++++++-- stdlib/test/memory/test_arc.mojo | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) rename stdlib/src/memory/{_arc.mojo => arc.mojo} (94%) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f7a5494312..a9d09e38e1 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -21,7 +21,7 @@ from os import Atomic from sys.intrinsics import _type_is_eq from memory import memcmp, memcpy, DTypePointer -from memory._arc import Arc +from memory import Arc from memory.unsafe_pointer import move_from_pointee from utils import StringRef, unroll diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index c8e7b9da9d..d9962246cc 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -36,4 +36,6 @@ from .reference import ( AddressSpace, ) -# TODO: consider making Arc public and import it here +from .arc import ( + Arc, +) diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/arc.mojo similarity index 94% rename from stdlib/src/memory/_arc.mojo rename to stdlib/src/memory/arc.mojo index 140d7dc289..5143a86d93 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -77,14 +77,22 @@ struct Arc[T: Movable](CollectionElement): ) fn __copyinit__(inout self, existing: Self): - """Copy an existing reference. Increment the refcount to the object.""" + """Copy an existing reference. Increment the refcount to the object. + + Args: + existing: The existing reference. + """ # Order here does not matter since `existing` can't be destroyed until # sometime after we return. existing._inner[].add_ref() self._inner = existing._inner fn __moveinit__(inout self, owned existing: Self): - """Move an existing reference.""" + """Move an existing reference. + + Args: + existing: The existing reference. + """ self._inner = existing._inner fn __del__(owned self): diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 22c1902870..7cffd9e45e 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -14,7 +14,7 @@ from collections import List -from memory._arc import Arc +from memory import Arc from memory.unsafe_pointer import initialize_pointee_move from testing import assert_equal, assert_false, assert_true From 4b3d372889059b2d2eedf6e5f33556bc08b9969e Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 6 May 2024 12:05:15 -0600 Subject: [PATCH 0429/2019] Revert "[stdlib] Add move constructor to `Atomic` (#36718)" (#39147) This reverts commit 7654fad7242cdb9d88759558a1ba7ca360889144. Since atomic operations are shared across threads and defined by memory identity, it's not a great idea to define a move constructor for `Atomic`. Deleting the move constructor is the element of least surprise. In the future, we may want to make it easier to get the value out of an atomic, but it shouldn't be implicitly movable - this was a footgun before this patch. MODULAR_ORIG_COMMIT_REV_ID: 160d1d6e8afc52fb5568dea06733730cdff8d9ca --- stdlib/src/os/atomic.mojo | 9 --------- stdlib/test/os/test_atomic.mojo | 19 ------------------- 2 files changed, 28 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 2c3505a199..2123c09f89 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -57,15 +57,6 @@ struct Atomic[type: DType]: """ self.value = value - @always_inline - fn __moveinit__(inout self, owned existing: Self): - """Moves Constructor. - - Args: - existing: The existing Atomic. - """ - self.value = existing.value - @always_inline fn load(inout self) -> Scalar[type]: """Loads the current value from the atomic. diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 74466611e3..16c69abfdf 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -66,24 +66,6 @@ fn test_atomic_floating_point() raises: assert_equal(atom.value, 0.0) -def test_atomic_move_constructor(): - var atom: Atomic[DType.index] = 3 - var atom2 = atom^ - assert_equal(atom2.value, 3) - atom2 += 4 - assert_equal(atom2.value, 7) - atom2 -= 4 - assert_equal(atom2.value, 3) - atom2.max(0) - assert_equal(atom2.value, 3) - atom2.max(42) - assert_equal(atom2.value, 42) - atom2.min(3) - assert_equal(atom2.value, 3) - atom2.min(0) - assert_equal(atom2.value, 0) - - def test_compare_exchange_weak(): var atom: Atomic[DType.int64] = 3 var expected = Int64(3) @@ -110,5 +92,4 @@ def test_compare_exchange_weak(): def main(): test_atomic() test_atomic_floating_point() - test_atomic_move_constructor() test_compare_exchange_weak() From cef6b4ab7474e9f1dc45debc1b7590899046774f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 6 May 2024 11:05:38 -0700 Subject: [PATCH 0430/2019] [mojo-stdlib] Remove `Reference.get_legacy_pointer`. (#39401) This removes this by upgrading various uses to `UnsafePointer` to `LegacyPointer.address_of`. `Reference` is getting minimal! MODULAR_ORIG_COMMIT_REV_ID: 8c80f98200219fc269067869c2aa28b0b4416e13 --- stdlib/src/builtin/object.mojo | 30 +++++++----------------------- stdlib/src/memory/reference.mojo | 18 ------------------ stdlib/src/memory/unsafe.mojo | 7 +++++-- stdlib/src/sys/ffi.mojo | 21 ++++----------------- stdlib/src/utils/static_tuple.mojo | 4 ++-- 5 files changed, 18 insertions(+), 62 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index a9d09e38e1..6de517c859 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -204,7 +204,7 @@ struct _Function: fn __init__[FnT: AnyRegType](inout self, value: FnT): # FIXME: No "pointer bitcast" for signature function pointers. var f = UnsafePointer[Int16]() - Reference(f).get_legacy_pointer().bitcast[FnT]().store(value) + UnsafePointer.address_of(f).bitcast[FnT]()[] = value self.value = f alias fn0 = fn () raises -> object @@ -218,40 +218,24 @@ struct _Function: @always_inline fn invoke(owned self) raises -> object: - return ( - Reference(self.value) - .get_legacy_pointer() - .bitcast[Self.fn0]() - .load()() - ) + return UnsafePointer.address_of(self.value).bitcast[Self.fn0]()[]() @always_inline fn invoke(owned self, arg0: object) raises -> object: - return ( - Reference(self.value) - .get_legacy_pointer() - .bitcast[Self.fn1]() - .load()(arg0) - ) + return UnsafePointer.address_of(self.value).bitcast[Self.fn1]()[](arg0) @always_inline fn invoke(owned self, arg0: object, arg1: object) raises -> object: - return ( - Reference(self.value) - .get_legacy_pointer() - .bitcast[Self.fn2]() - .load()(arg0, arg1) + return UnsafePointer.address_of(self.value).bitcast[Self.fn2]()[]( + arg0, arg1 ) @always_inline fn invoke( owned self, arg0: object, arg1: object, arg2: object ) raises -> object: - return ( - Reference(self.value) - .get_legacy_pointer() - .bitcast[Self.fn3]() - .load()(arg0, arg1, arg2) + return UnsafePointer.address_of(self.value).bitcast[Self.fn3]()[]( + arg0, arg1, arg2 ) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 38ef1122b8..d95093492c 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -248,21 +248,3 @@ struct Reference[ The MLIR reference for the Mojo compiler to use. """ return self.value - - # ===------------------------------------------------------------------===# - # Methods - # ===------------------------------------------------------------------===# - - # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType - # disagreement. Use UnsafePointer instead! - @always_inline("nodebug") - fn get_legacy_pointer(self) -> LegacyPointer[type, address_space]: - """Constructs a LegacyPointer from a safe reference. - - Returns: - Constructed LegacyPointer object. - """ - # Work around AnyRegType vs AnyType. - return __mlir_op.`pop.pointer.bitcast`[ - _type = LegacyPointer[type, address_space]._mlir_type - ](UnsafePointer(self).address) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 3788ba39bc..f75912e493 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -275,7 +275,10 @@ struct LegacyPointer[ Returns: A LegacyPointer struct which contains the address of the argument. """ - return arg.get_legacy_pointer() + # Work around AnyRegType vs AnyType. + return __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( + UnsafePointer(arg).address + ) @always_inline("nodebug") fn __refitem__(self) -> Self._mlir_ref_type: @@ -711,7 +714,7 @@ struct DTypePointer[ Returns: A DTypePointer struct which contains the address of the argument. """ - return arg.get_legacy_pointer() + return LegacyPointer.address_of(arg[]) @always_inline("nodebug") fn __getitem__[T: Intable](self, offset: T) -> Scalar[type]: diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 472b4e54cd..5fc0f5eb94 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -128,14 +128,9 @@ struct DLHandle(CollectionElement, Boolable): var opaque_function_ptr = external_call[ "dlsym", DTypePointer[DType.int8] ](self.handle.address, name) - return ( - Reference(opaque_function_ptr) - .get_legacy_pointer() - .bitcast[result_type]() - .load() - ) + return UnsafePointer(opaque_function_ptr).bitcast[result_type]()[] else: - return Pointer[result_type].get_null().load() + return abort[result_type]("get_function isn't supported on windows") @always_inline fn _get_function[ @@ -214,21 +209,13 @@ fn _get_dylib_function[ alias func_cache_name = name + "/" + func_name var func_ptr = _get_global_or_null[func_cache_name]() if func_ptr: - return ( - Reference(func_ptr) - .get_legacy_pointer() - .bitcast[result_type]() - .load() - ) + return UnsafePointer(func_ptr).bitcast[result_type]()[] var dylib = _get_dylib[name, init_fn, destroy_fn](payload) var new_func = dylib._get_function[func_name, result_type]() external_call["KGEN_CompilerRT_InsertGlobal", NoneType]( StringRef(func_cache_name), - Reference(new_func) - .get_legacy_pointer() - .bitcast[Pointer[NoneType]]() - .load(), + UnsafePointer(new_func).bitcast[Pointer[NoneType]]()[], ) return new_func diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index e728193f38..ab5eb10f26 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -53,9 +53,9 @@ fn _set_array_elem[ array: the array which is captured by reference. """ var ptr = __mlir_op.`pop.array.gep`( - array.get_legacy_pointer().address, index.value + UnsafePointer(array).address, index.value ) - Pointer(ptr).store(val) + UnsafePointer(ptr)[] = val @always_inline From dbb8b15128940df5d40613c3bd85bfcff6d9c7b4 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 6 May 2024 15:25:17 -0400 Subject: [PATCH 0431/2019] [stdlib] Unified getters of pointer to contiguous collection of memory (#39271) Previously we have `List.data`, (this is a field and there is no equivalent getter), `String._as_ptr`, `StringRef._as_ptr`, `StringRef._as_uint8_ptr`, `StringLiteral.data()`, `Tensor[T].data`, `CTensor.data`, `EngineTensor.data`, `Variant._get_ptr[T]`, and `Arc._data_ptr`. Change the getter name to `unsafe_ptr()`. --------- Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: 68c63935d4e5afbb826e51344abc0a07d2c60370 --- docs/changelog.md | 3 ++ examples/mandelbrot.mojo | 2 +- examples/notebooks/Mandelbrot.ipynb | 6 ++-- stdlib/src/base64/base64.mojo | 4 +-- stdlib/src/builtin/error.mojo | 5 +-- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/hex.mojo | 4 +-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/io.mojo | 10 +++--- stdlib/src/builtin/string.mojo | 42 +++++++++++----------- stdlib/src/builtin/string_literal.mojo | 6 ++-- stdlib/src/collections/list.mojo | 9 +++++ stdlib/src/memory/arc.mojo | 8 +++++ stdlib/src/os/_linux_aarch64.mojo | 4 +-- stdlib/src/os/_linux_x86.mojo | 4 +-- stdlib/src/os/_macos.mojo | 4 +-- stdlib/src/os/env.mojo | 6 ++-- stdlib/src/os/os.mojo | 6 ++-- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/sys/ffi.mojo | 6 ++-- stdlib/src/sys/info.mojo | 2 +- stdlib/src/utils/inlined_string.mojo | 4 +-- stdlib/src/utils/stringref.mojo | 20 +++++------ stdlib/test/builtin/test_hash.mojo | 32 +++++++++++------ stdlib/test/collections/test_array.mojo | 2 +- stdlib/test/utils/test_inlined_string.mojo | 4 +-- 26 files changed, 117 insertions(+), 82 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index df00d4c126..c9c245172a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -105,6 +105,9 @@ what we publish. - The `abs` and `round` functions have moved from `math` to `builtin`, so you no longer need to do `from math import abs, round`. +- Many functions returning a pointer type have been unified to have a public + API function of `unsafe_ptr()`. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 6e8dedc439..ae936cb462 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -77,7 +77,7 @@ fn main() raises: var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x var cy = min_y + row * scale_y var c = ComplexSIMD[float_type, simd_width](cx, cy) - t.data().store[width=simd_width]( + t.unsafe_ptr().store[width=simd_width]( row * width + col, mandelbrot_kernel_SIMD[simd_width](c) ) diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index fd7f508d29..1789701aec 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -305,7 +305,7 @@ " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", - " t.data().store(\n", + " t.unsafe_ptr().store(\n", " row * width + col, mandelbrot_kernel_SIMD[simd_width](c)\n", " )\n", "\n", @@ -382,7 +382,7 @@ " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", - " t.data().store(row * width + col, mandelbrot_kernel_SIMD[simd_width](c))\n", + " t.unsafe_ptr().store(row * width + col, mandelbrot_kernel_SIMD[simd_width](c))\n", "\n", " # Vectorize the call to compute_vector where call gets a chunk of pixels.\n", " vectorize[compute_vector, simd_width](width)\n", @@ -436,7 +436,7 @@ " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", - " t.data().store(\n", + " t.unsafe_ptr().store(\n", " row * width + col, mandelbrot_kernel_SIMD[simd_width](c)\n", " )\n", "\n", diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index d4a238431f..6ac3e811c6 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -70,7 +70,7 @@ fn b64encode(str: String) -> String: Base64 encoding of the input string. """ alias lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - var b64chars = lookup.data() + var b64chars = lookup.unsafe_ptr() var length = len(str) var out = List[Int8](capacity=length + 1) @@ -78,7 +78,7 @@ fn b64encode(str: String) -> String: @parameter @always_inline fn s(idx: Int) -> Int: - return int(str._as_ptr().bitcast[DType.uint8]()[idx]) + return int(str.unsafe_ptr().bitcast[DType.uint8]()[idx]) # This algorithm is based on https://arxiv.org/abs/1704.00605 var end = length - (length % 3) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 89dbac4645..352451e700 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -62,7 +62,8 @@ struct Error(Stringable, Boolable): The constructed Error object. """ return Error { - data: value.data().bitcast[DType.uint8](), loaded_length: len(value) + data: value.unsafe_ptr().bitcast[DType.uint8](), + loaded_length: len(value), } @always_inline("nodebug") @@ -77,7 +78,7 @@ struct Error(Stringable, Boolable): """ var length = len(src) var dest = Self.StorageType.alloc(length + 1) - memcpy(dest, src._as_ptr().bitcast[DType.uint8](), length) + memcpy(dest, src.unsafe_ptr().bitcast[DType.uint8](), length) dest[length] = 0 return Error {data: dest, loaded_length: -length} diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index c4a7aab17b..e18ee787e6 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -412,7 +412,7 @@ struct FileHandle: Args: data: The data to write to the file. """ - self._write(data._as_ptr(), len(data)) + self._write(data.unsafe_ptr(), len(data)) @always_inline fn write(self, data: StringRef) raises: diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 672f0269bc..e57aa390d9 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -122,7 +122,7 @@ fn _try_write_int( # # TODO(#26444, Unicode support): Get an array of Character, not bytes. - var digit_chars_array = digit_chars.data() + var digit_chars_array = digit_chars.unsafe_ptr() # Prefix a '-' if the original int was negative and make positive. if value < 0: @@ -190,7 +190,7 @@ fn _try_write_int( # Re-add +1 byte since the loop ended so we didn't write another char. offset += 1 - var buf_ptr = buf.as_ptr() + offset + var buf_ptr = buf.unsafe_ptr() + offset # Calculate the length of the buffer we've filled. This is the number of # bytes from our final `buf_ptr` to the end of the buffer. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index d526541b62..d31e09aa31 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -333,7 +333,7 @@ struct Int( # Format the integer to the local byte array var len = _snprintf( - rebind[UnsafePointer[Int8]](buf.as_ptr()), + rebind[UnsafePointer[Int8]](buf.unsafe_ptr()), size, "%li", self.value, diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 795634d876..bd740f8133 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -57,11 +57,11 @@ struct _fdopen: @parameter if os_is_windows(): handle = external_call["_fdopen", UnsafePointer[NoneType]]( - _dup(stream_id), mode.data() + _dup(stream_id), mode.unsafe_ptr() ) else: handle = external_call["fdopen", UnsafePointer[NoneType]]( - _dup(stream_id), mode.data() + _dup(stream_id), mode.unsafe_ptr() ) self.handle = handle @@ -103,7 +103,7 @@ fn _printf[*types: AnyType](fmt: StringLiteral, *arguments: *types): @parameter if triple_is_nvidia_cuda(): _ = external_call["vprintf", Int32]( - fmt.data(), UnsafePointer.address_of(loaded_pack) + fmt.unsafe_ptr(), UnsafePointer.address_of(loaded_pack) ) else: with _fdopen(_fdopen.STDOUT) as fd: @@ -116,7 +116,7 @@ fn _printf[*types: AnyType](fmt: StringLiteral, *arguments: *types): `) -> !pop.scalar`, ], _type=Int32, - ](fd, fmt.data(), loaded_pack) + ](fd, fmt.unsafe_ptr(), loaded_pack) # ===----------------------------------------------------------------------=== # @@ -164,7 +164,7 @@ fn _snprintf[ `) -> !pop.scalar`, ], _type=Int32, - ](str, size, fmt.data(), loaded_pack) + ](str, size, fmt.unsafe_ptr(), loaded_pack) ) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 668bf2c239..ac950ce1af 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -65,7 +65,7 @@ fn ord(s: String) -> Int: # 2: 110aaaaa 10bbbbbb -> 00000000 00000000 00000aaa aabbbbbb a << 6 | b # 3: 1110aaaa 10bbbbbb 10cccccc -> 00000000 00000000 aaaabbbb bbcccccc a << 12 | b << 6 | c # 4: 11110aaa 10bbbbbb 10cccccc 10dddddd -> 00000000 000aaabb bbbbcccc ccdddddd a << 18 | b << 12 | c << 6 | d - var p = s._as_ptr().bitcast[DType.uint8]() + var p = s.unsafe_ptr().bitcast[DType.uint8]() var b1 = p.load() if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(len(s) == 1, "input string length must be 1") @@ -171,7 +171,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: var is_negative: Bool = False var start: Int = 0 var str_len = len(str_ref) - var buff = str_ref._as_ptr() + var buff = str_ref.unsafe_ptr() for pos in range(start, str_len): if isspace(buff[pos]): @@ -742,7 +742,7 @@ struct String( var buffer = Self._buffer_type() var adjusted_span_len = len(adjusted_span) buffer.resize(adjusted_span_len + 1, 0) - var ptr = self._as_ptr() + var ptr = self.unsafe_ptr() for i in range(adjusted_span_len): buffer[i] = ptr[adjusted_span[i]] buffer[adjusted_span_len] = 0 @@ -756,7 +756,7 @@ struct String( The string length. """ # Avoid returning -1 if the buffer is not initialized - if not self._as_ptr(): + if not self.unsafe_ptr(): return 0 # The negative 1 is to account for the terminator. @@ -775,10 +775,10 @@ struct String( if len(self) != len(other): return False - if int(self._as_ptr()) == int(other._as_ptr()): + if int(self.unsafe_ptr()) == int(other.unsafe_ptr()): return True - return memcmp(self._as_ptr(), other._as_ptr(), len(self)) == 0 + return memcmp(self.unsafe_ptr(), other.unsafe_ptr(), len(self)) == 0 @always_inline fn __ne__(self, other: String) -> Bool: @@ -813,12 +813,12 @@ struct String( buffer.resize(total_len + 1, 0) memcpy( DTypePointer(buffer.data), - self._as_ptr(), + self.unsafe_ptr(), self_len, ) memcpy( DTypePointer(buffer.data + self_len), - other._as_ptr(), + other.unsafe_ptr(), other_len + 1, # Also copy the terminator ) return Self(buffer^) @@ -853,8 +853,8 @@ struct String( self._buffer.resize(total_len + 1, 0) # Copy the data alongside the terminator. memcpy( - self._as_uint8_ptr() + self_len, - other._as_uint8_ptr(), + self.unsafe_uint8_ptr() + self_len, + other.unsafe_uint8_ptr(), other_len + 1, ) @@ -979,7 +979,7 @@ struct String( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self._as_ptr(), length: len(self)} + return StringRef {data: self.unsafe_ptr(), length: len(self)} fn _strref_keepalive(self): """ @@ -990,7 +990,7 @@ struct String( pass # TODO: Remove this method when #2317 is done - fn _as_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. Note that you should use `_as_uint8_ptr()` if you need to access the @@ -1003,7 +1003,7 @@ struct String( """ return rebind[DTypePointer[DType.int8]](self._buffer.data) - fn _as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + fn unsafe_uint8_ptr(self) -> DTypePointer[DType.uint8]: """Retrieves a pointer to the underlying memory. Returns: @@ -1040,7 +1040,7 @@ struct String( Returns: The pointer to the underlying memory. """ - var ptr = self._as_ptr() + var ptr = self.unsafe_ptr() self._buffer.data = UnsafePointer[Int8]() self._buffer.size = 0 self._buffer.capacity = 0 @@ -1169,9 +1169,9 @@ struct String( if occurrences == -1: return self - var self_start = self._as_ptr() - var self_ptr = self._as_ptr() - var new_ptr = new._as_ptr() + var self_start = self.unsafe_ptr() + var self_ptr = self.unsafe_ptr() + var new_ptr = new.unsafe_ptr() var self_len = len(self) var old_len = len(old) @@ -1261,8 +1261,8 @@ struct String( fn _interleave(self, val: String) -> String: var res = List[Int8]() - var val_ptr = val._as_ptr() - var self_ptr = self._as_ptr() + var val_ptr = val.unsafe_ptr() + var self_ptr = self.unsafe_ptr() res.reserve(len(val) * len(self) + 1) for i in range(len(self)): for j in range(len(val)): @@ -1301,7 +1301,7 @@ struct String( fn _toggle_ascii_case[check_case: fn (Int8) -> Bool](self) -> String: var copy: String = self - var char_ptr = copy._as_ptr() + var char_ptr = copy.unsafe_ptr() for i in range(len(self)): var char: Int8 = char_ptr[i] @@ -1419,7 +1419,7 @@ struct String( for i in range(n): memcpy( rebind[DTypePointer[DType.int8]](buf.data) + len_self * i, - self._as_ptr(), + self.unsafe_ptr(), len_self, ) return String(buf^) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c4fc0e428c..67c5b67105 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -69,7 +69,7 @@ struct StringLiteral( return __mlir_op.`pop.string.size`(self.value) @always_inline("nodebug") - fn data(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> DTypePointer[DType.int8]: """Get raw pointer to the underlying data. Returns: @@ -112,7 +112,7 @@ struct StringLiteral( if length != len(rhs): return False - return _memcmp(self.data(), rhs.data(), length) == 0 + return _memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), length) == 0 @always_inline("nodebug") fn __ne__(self, rhs: StringLiteral) -> Bool: @@ -134,7 +134,7 @@ struct StringLiteral( uses. Its intended usage is for data structures. See the `hash` builtin documentation for more details. """ - return hash(self.data(), len(self)) + return hash(self.unsafe_ptr(), len(self)) fn __str__(self) -> String: """Convert the string literal to a string. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 38b9b62b78..19a1a4fa44 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -671,3 +671,12 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if elem[] == value: count += 1 return count + + @always_inline + fn unsafe_ptr(self) -> UnsafePointer[T]: + """Retrieves a pointer to the underlying memory. + + Returns: + The UnsafePointer to the underlying memory. + """ + return self.data diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 5143a86d93..aba4fb02a3 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -116,3 +116,11 @@ struct Arc[T: Movable](CollectionElement): A Reference to the managed value. """ return self[]._inner[].payload + + fn as_ptr(self) -> UnsafePointer[T]: + """Retrieves a pointer to the underlying memory. + + Returns: + The UnsafePointer to the underlying memory. + """ + return UnsafePointer.address_of(self._inner[].payload) diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index d59f9f4876..15551b4218 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -110,7 +110,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -121,7 +121,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index 6b8a1f178a..4c39a5da60 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -108,7 +108,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -119,7 +119,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 242863b263..fcdc12ac87 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -115,7 +115,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["stat", Int32]( - path._as_ptr(), UnsafePointer.address_of(stat) + path.unsafe_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -126,7 +126,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["lstat", Int32]( - path._as_ptr(), UnsafePointer.address_of(stat) + path.unsafe_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index 821df8e39f..f50cea6bdf 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -47,7 +47,7 @@ fn setenv(name: String, value: String, overwrite: Bool = True) -> Bool: return False var status = external_call["setenv", Int32]( - name._as_ptr(), value._as_ptr(), Int32(1 if overwrite else 0) + name.unsafe_ptr(), value.unsafe_ptr(), Int32(1 if overwrite else 0) ) return status == 0 @@ -72,7 +72,9 @@ fn getenv(name: String, default: String = "") -> String: if not os_is_supported: return default - var ptr = external_call["getenv", DTypePointer[DType.uint8]](name._as_ptr()) + var ptr = external_call["getenv", DTypePointer[DType.uint8]]( + name.unsafe_ptr() + ) if not ptr: return default return String(StringRef(ptr)) diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index fb01e1f756..65b2f19ab4 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -111,7 +111,7 @@ struct _DirHandle: raise "the directory '" + path + "' does not exist" self._handle = external_call["opendir", UnsafePointer[NoneType]]( - path._as_ptr() + path.unsafe_ptr() ) if not self._handle: @@ -277,12 +277,12 @@ fn remove(path: String) raises: path: The path to the file. """ - var error = external_call["unlink", Int](path._as_ptr()) + var error = external_call["unlink", Int](path.unsafe_ptr()) if error != 0: # TODO get error message, the following code prints it # var error_str = String("Something went wrong") - # _ = external_call["perror", UnsafePointer[NoneType]](error_str._as_ptr()) + # _ = external_call["perror", UnsafePointer[NoneType]](error_str.unsafe_ptr()) # _ = error_str raise Error("Can not remove file: " + path) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 2f347b51bb..a84db2f4b5 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -496,7 +496,7 @@ struct CPython: DTypePointer[DType.int8], Int, DTypePointer[DType.int8] ) -> PyObjectPtr ](StringRef("PyUnicode_DecodeUTF8"))( - strref.data, strref.length, "strict".data() + strref.data, strref.length, "strict".unsafe_ptr() ) if self.logging_enabled: print( diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 5fc0f5eb94..599b72c29f 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -64,7 +64,7 @@ struct DLHandle(CollectionElement, Boolable): @parameter if not os_is_windows(): self.handle = external_call["dlopen", DTypePointer[DType.int8]]( - path._as_ptr(), flags + path.unsafe_ptr(), flags ) else: self.handle = DTypePointer[DType.int8]() @@ -104,7 +104,7 @@ struct DLHandle(CollectionElement, Boolable): A handle to the function. """ - return self._get_function[result_type](name._as_ptr()) + return self._get_function[result_type](name.unsafe_ptr()) @always_inline fn _get_function[ @@ -147,7 +147,7 @@ struct DLHandle(CollectionElement, Boolable): A handle to the function. """ - return self._get_function[result_type](func_name.data()) + return self._get_function[result_type](func_name.unsafe_ptr()) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index ff8c785db9..5f6b74a356 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -716,7 +716,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: var buf_len = Int(INITIAL_CAPACITY) var err = external_call["sysctlbyname", Int32]( - "kern.osproductversion".data(), + "kern.osproductversion".unsafe_ptr(), buf.data, UnsafePointer.address_of(buf_len), UnsafePointer[NoneType](), diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 63b72c21ee..ec45ef515e 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -239,7 +239,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): if self._is_small(): return self._storage.get[_FixedString[Self.SMALL_CAP]]()[].as_ptr() else: - return self._storage.get[String]()[]._as_ptr() + return self._storage.get[String]()[].unsafe_ptr() fn _strref_dangerous(self) -> StringRef: """ @@ -309,7 +309,7 @@ struct _FixedString[CAP: Int]( self.buffer = _ArrayMem[Int8, CAP]() self.size = len(literal) - memcpy(self.buffer.as_ptr(), literal.data(), len(literal)) + memcpy(self.buffer.as_ptr(), literal.unsafe_ptr(), len(literal)) # ===------------------------------------------------------------------=== # # Trait Interfaces diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index b4d275c6e6..0abef9e2e9 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -65,7 +65,7 @@ struct StringRef( Returns: Constructed `StringRef` object. """ - return StringRef(str.data(), len(str)) + return StringRef(str.unsafe_ptr(), len(str)) fn __str__(self) -> String: """Convert the string reference to a string. @@ -190,10 +190,10 @@ struct StringRef( # TODO: #2317 Drop support for this method when we have fully # transitionned to UInt8 as the main byte type. @always_inline - fn _as_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. - Prefer to use `_as_uint8_ptr()` instead. + Prefer to use `as_uint8_ptr()` instead. Returns: The DTypePointer to the underlying memory. @@ -201,7 +201,7 @@ struct StringRef( return self.data @always_inline - fn _as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + fn unsafe_uint8_ptr(self) -> DTypePointer[DType.uint8]: """Retrieves a pointer to the underlying memory. Returns: @@ -340,16 +340,16 @@ struct StringRef( var haystack_str = self._from_start(start) var loc = _memmem( - haystack_str._as_uint8_ptr(), + haystack_str.unsafe_uint8_ptr(), len(haystack_str), - substr._as_uint8_ptr(), + substr.unsafe_uint8_ptr(), len(substr), ) if not loc: return -1 - return int(loc) - int(self._as_uint8_ptr()) + return int(loc) - int(self.unsafe_uint8_ptr()) fn rfind(self, substr: StringRef, start: Int = 0) -> Int: """Finds the offset of the last occurrence of `substr` starting at @@ -373,16 +373,16 @@ struct StringRef( var haystack_str = self._from_start(start) var loc = _memrmem( - haystack_str._as_uint8_ptr(), + haystack_str.unsafe_uint8_ptr(), len(haystack_str), - substr._as_uint8_ptr(), + substr.unsafe_uint8_ptr(), len(substr), ) if not loc: return -1 - return int(loc) - int(self._as_uint8_ptr()) + return int(loc) - int(self.unsafe_uint8_ptr()) fn _from_start(self, start: Int) -> StringRef: """Gets the StringRef pointing to the substring after the specified slice start position. diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index de8ca14cfa..d896444fc6 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -29,19 +29,31 @@ def same_low_bits(i1: Int, i2: Int, bits: Int = 4) -> Int: def test_hash_byte_array(): # Test that values hash deterministically - assert_equal(hash("a".data(), 1), hash("a".data(), 1)) - assert_equal(hash("b".data(), 1), hash("b".data(), 1)) - assert_equal(hash("c".data(), 1), hash("c".data(), 1)) - assert_equal(hash("d".data(), 1), hash("d".data(), 1)) + assert_equal(hash("a".unsafe_ptr(), 1), hash("a".unsafe_ptr(), 1)) + assert_equal(hash("b".unsafe_ptr(), 1), hash("b".unsafe_ptr(), 1)) + assert_equal(hash("c".unsafe_ptr(), 1), hash("c".unsafe_ptr(), 1)) + assert_equal(hash("d".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1)) # Test that low bits are different var num_same = 0 - num_same += same_low_bits(hash("a".data(), 1), hash("b".data(), 1)) - num_same += same_low_bits(hash("a".data(), 1), hash("c".data(), 1)) - num_same += same_low_bits(hash("a".data(), 1), hash("d".data(), 1)) - num_same += same_low_bits(hash("b".data(), 1), hash("c".data(), 1)) - num_same += same_low_bits(hash("b".data(), 1), hash("d".data(), 1)) - num_same += same_low_bits(hash("c".data(), 1), hash("d".data(), 1)) + num_same += same_low_bits( + hash("a".unsafe_ptr(), 1), hash("b".unsafe_ptr(), 1) + ) + num_same += same_low_bits( + hash("a".unsafe_ptr(), 1), hash("c".unsafe_ptr(), 1) + ) + num_same += same_low_bits( + hash("a".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1) + ) + num_same += same_low_bits( + hash("b".unsafe_ptr(), 1), hash("c".unsafe_ptr(), 1) + ) + num_same += same_low_bits( + hash("b".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1) + ) + num_same += same_low_bits( + hash("c".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1) + ) assert_true(num_same < 2, "too little entropy in hash fn low bits") diff --git a/stdlib/test/collections/test_array.mojo b/stdlib/test/collections/test_array.mojo index 77b9bf85f5..d7afab9dcb 100644 --- a/stdlib/test/collections/test_array.mojo +++ b/stdlib/test/collections/test_array.mojo @@ -31,7 +31,7 @@ def test_array_mem(): # Test pointer operations # ================================== - var ptr = array.as_ptr() + var ptr = array.unsafe_ptr() assert_equal(ptr[0], 1) assert_equal(ptr[1], 1) assert_equal(ptr[2], 1) diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index 0c951b36a1..d7354ddbe9 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -100,14 +100,14 @@ def test_small_string_construction(): # ================================== var heap_s1 = String("hello") - var heap_s1_addr = int(heap_s1._as_ptr()) + var heap_s1_addr = int(heap_s1.unsafe_ptr()) var s3 = InlinedString(heap_s1^) # Test that a InlinedString constructed from a String uses the same # allocation as the original String (even if the String size is small # enough to fit inline). - assert_equal(int(s3.as_ptr()), heap_s1_addr) + assert_equal(int(s3.unsafe_ptr()), heap_s1_addr) def test_small_string_iadd(): From aa12254af5e6ed0f01941605b8284b4f7541be67 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Mon, 6 May 2024 15:50:56 -0700 Subject: [PATCH 0432/2019] [stdlib] Migrating ffi.mojo use cases from Pointer -> UnsafePointer Migrate ffi.mojo Pointer -> UnsafePointer. MODULAR_ORIG_COMMIT_REV_ID: e84083a696bf6619c0b67777c99ece9d74bac0c6 --- stdlib/src/sys/ffi.mojo | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 599b72c29f..cad2b6a76e 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements a foreign functions interface (FFI).""" -from memory import DTypePointer, Pointer +from memory import DTypePointer, LegacyPointer from utils import StringRef @@ -158,11 +158,13 @@ struct DLHandle(CollectionElement, Boolable): @always_inline fn _get_global[ name: StringLiteral, - init_fn: fn (Pointer[NoneType]) -> Pointer[NoneType], - destroy_fn: fn (Pointer[NoneType]) -> None, -](payload: Pointer[NoneType] = Pointer[NoneType]()) -> Pointer[NoneType]: + init_fn: fn (LegacyPointer[NoneType]) -> LegacyPointer[NoneType], + destroy_fn: fn (LegacyPointer[NoneType]) -> None, +]( + payload: LegacyPointer[NoneType] = LegacyPointer[NoneType]() +) -> LegacyPointer[NoneType]: return external_call[ - "KGEN_CompilerRT_GetGlobalOrCreate", Pointer[NoneType] + "KGEN_CompilerRT_GetGlobalOrCreate", LegacyPointer[NoneType] ](StringRef(name), payload, init_fn, destroy_fn) @@ -180,18 +182,18 @@ fn _get_global[ @always_inline -fn _get_global_or_null[name: StringLiteral]() -> Pointer[NoneType]: - return external_call["KGEN_CompilerRT_GetGlobalOrNull", Pointer[NoneType]]( - StringRef(name) - ) +fn _get_global_or_null[name: StringLiteral]() -> UnsafePointer[NoneType]: + return external_call[ + "KGEN_CompilerRT_GetGlobalOrNull", UnsafePointer[NoneType] + ](StringRef(name)) @always_inline fn _get_dylib[ name: StringLiteral, - init_fn: fn (Pointer[NoneType]) -> Pointer[NoneType], - destroy_fn: fn (Pointer[NoneType]) -> None, -](payload: Pointer[NoneType] = Pointer[NoneType]()) -> DLHandle: + init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], + destroy_fn: fn (UnsafePointer[NoneType]) -> None, +](payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]()) -> DLHandle: var ptr = _get_global[name, init_fn, destroy_fn](payload).bitcast[ DLHandle ]() @@ -202,10 +204,10 @@ fn _get_dylib[ fn _get_dylib_function[ name: StringLiteral, func_name: StringLiteral, - init_fn: fn (Pointer[NoneType]) -> Pointer[NoneType], - destroy_fn: fn (Pointer[NoneType]) -> None, + init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], + destroy_fn: fn (UnsafePointer[NoneType]) -> None, result_type: AnyRegType, -](payload: Pointer[NoneType] = Pointer[NoneType]()) -> result_type: +](payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]()) -> result_type: alias func_cache_name = name + "/" + func_name var func_ptr = _get_global_or_null[func_cache_name]() if func_ptr: From 32e62e6b305b23d5d42191471499d6805d7c6580 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Mon, 6 May 2024 18:46:24 -0600 Subject: [PATCH 0433/2019] [mojo-tooling] Add support for erroring out on invalid doc strings We currently emit warnings for invalid aspects of doc strings, but this makes it difficult to actually enforce that all doc strings are valid. The current strategy involves resorting to inspecting the stderr of a run of `mojo doc` to see if any warnings were emitted, which is not ideal. This PR restructures the processing of doc strings in two ways to make this easier: * A new `validate-doc-strings` flag is added, that will turn doc string related diagnostics into errors (instead of warnings). * The `warn-missing-doc-strings` flag is renamed to `diagnose-missing-doc-strings` to better reflect its purpose (and not attach too strongly to "warnings"). MODULAR_ORIG_COMMIT_REV_ID: 4f2f8c1413ff9659d1eb95ea317c46024e666837 --- docs/changelog.md | 6 ++++++ stdlib/docs/style-guide.md | 2 +- stdlib/scripts/check-docstrings.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c9c245172a..c007e543ff 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -100,6 +100,9 @@ what we publish. - `object` now implements all the bitwise operators. ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) +- A new `--validate-doc-strings` option has been added to `mojo` to emit errors + on invalid doc strings instead of warnings. + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no @@ -108,6 +111,9 @@ what we publish. - Many functions returning a pointer type have been unified to have a public API function of `unsafe_ptr()`. +- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to + `--diagnose-missing-doc-strings`. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 7f58ea863b..1e2aa8455d 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -69,7 +69,7 @@ formatted and consistent with the Mojo style guidelines. Note that you should not have any warnings. ```bash -> mojo doc --warn-missing-doc-strings -o /dev/null stdlib/src/ +> mojo doc --diagnose-missing-doc-strings --validate-doc-strings -o /dev/null stdlib/src/ ``` Note that this is also included in the pre-commit. So if you have `pre-commit` diff --git a/stdlib/scripts/check-docstrings.py b/stdlib/scripts/check-docstrings.py index 44eaf8e3d4..eb4b180125 100644 --- a/stdlib/scripts/check-docstrings.py +++ b/stdlib/scripts/check-docstrings.py @@ -24,7 +24,7 @@ def main(): command = [ "mojo", "doc", - "--warn-missing-doc-strings", + "--diagnose-missing-doc-strings", "-o", "/dev/null", "./stdlib/src", From 77ec367e3c53a45c7552fea0fc4985460f1d7650 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 6 May 2024 18:02:05 -0700 Subject: [PATCH 0434/2019] [mojo-stdlib] Replace VariadicPack.get_element with refitem Now that refitem can take parameters, we can directly index into the pack with `args[4]` instead of `args.get_element[4]()[]`. MODULAR_ORIG_COMMIT_REV_ID: c7b31961de749959349fc0cb004e1e51980c3b2e --- stdlib/src/builtin/builtin_list.mojo | 10 ++++------ stdlib/src/builtin/tuple.mojo | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index ef0a88313d..42b1f9a8f3 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -538,7 +538,7 @@ struct VariadicPack[ @parameter fn destroy_elt[i: Int](): # destroy the elements in reverse order. - destroy_pointee(UnsafePointer(self.get_element[len - i - 1]())) + destroy_pointee(UnsafePointer.address_of(self[len - i - 1])) unroll[destroy_elt, len]() @@ -569,10 +569,8 @@ struct VariadicPack[ """ return Self.__len__() - # TODO: This should be __getitem__ but Mojo doesn't know how to invoke that, - # we need this for tuple as well. @always_inline - fn get_element[ + fn __refitem__[ index: Int ](self) -> Reference[ element_types[index.value], @@ -615,7 +613,7 @@ struct VariadicPack[ @parameter fn unrolled[i: Int](): - func(self.get_element[i]()[]) + func(self[i]) unroll[unrolled, Self.__len__()]() @@ -634,6 +632,6 @@ struct VariadicPack[ @parameter fn unrolled[i: Int](): - func[i, element_types[i.value]](self.get_element[i]()[]) + func[i, element_types[i.value]](self[i]) unroll[unrolled, Self.__len__()]() diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 5dc9622222..f9a5a5c205 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -79,7 +79,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # pack. For now just keep everything simple and copy the element. initialize_pointee_copy( UnsafePointer(self[idx]), - storage.get_element[idx]()[], + storage[idx], ) unroll[initialize_elt, Self.__len__()]() From 4a15c7ea3169746208285f8aa3ef12e58675cd66 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Mon, 6 May 2024 19:24:02 -0600 Subject: [PATCH 0435/2019] [mojo-stdlib][mojo-tooling] Add a new decorator to hide decls from doc gen This PR adds a new decorator, `@doc_private`, that can be used to hide a decl from being generated in the output of `mojo doc`. It also removes the requirement that the decl has documentation (e.g. when used with --warn-missing-doc-strings). This gives a lot more flexibility in defining the public api, and is most useful to hide internal special functions (like init methods), that shouldn't necessarily be exposed to users. This does not impact the accessibility of documentation in IDE environments, or other situations that already display private or otherwise non-public documentation. MODULAR_ORIG_COMMIT_REV_ID: 96283b860a7b090e8f1a1c156705f5ec61ddd8a5 --- docs/changelog.md | 5 +++ stdlib/src/builtin/_documentation.mojo | 46 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 stdlib/src/builtin/_documentation.mojo diff --git a/docs/changelog.md b/docs/changelog.md index c007e543ff..27a21165e3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -103,6 +103,11 @@ what we publish. - A new `--validate-doc-strings` option has been added to `mojo` to emit errors on invalid doc strings instead of warnings. +- A new decorator, `@doc_private`, was added that can be used to hide a decl + from being generated in the output of `mojo doc`. It also removes the + requirement that the decl has documentation (e.g. when used with + --diagnose-missing-doc-strings). + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/builtin/_documentation.mojo b/stdlib/src/builtin/_documentation.mojo new file mode 100644 index 0000000000..cbe4c23663 --- /dev/null +++ b/stdlib/src/builtin/_documentation.mojo @@ -0,0 +1,46 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provides decorators and utilities for interacting with Mojo documentation +generation and validation. + +These are Mojo built-ins, so you don't need to import them. +""" + +# ===-------------------------------------------------------------------===# +# doc_private +# ===-------------------------------------------------------------------===# + + +fn doc_private(): + """Indicate that the decorated declaration is private from the viewpoint + of documentation generation. + + This decorator allows for hiding the documentation for a declaration during + generation. This is often used to hide `__init__`, and other special + methods, that are intended for internal consumption. + + For example: + + ```mojo + struct Foo: + @doc_private + fn __init__(inout self): + "This should not be called directly, prefer Foo.create instead." + return + + @staticmethod + fn create() -> Self: + return Self() + ``` + """ + return From 3e2c4024616f531be7b2fd1575a90761213d22ec Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 7 May 2024 10:23:28 -0500 Subject: [PATCH 0436/2019] [External] [stdlib] Add `bool` for `Boolable`. (#39454) [External] [stdlib] Add `bool` for `Boolable`. Co-authored-by: soraros Closes modularml/mojo#2479 MODULAR_ORIG_COMMIT_REV_ID: 1e10abd533c247467976af3bbbab370192e64796 --- stdlib/src/builtin/bool.mojo | 16 ++++++++++++++++ stdlib/test/builtin/test_bool.mojo | 1 + 2 files changed, 17 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 780dde1c80..235d05d635 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -288,3 +288,19 @@ fn bool(value: None) -> Bool: The bool representation of the object. """ return False + + +@always_inline +fn bool[T: Boolable](value: T) -> Bool: + """Get the bool representation of the object. + + Parameters: + T: The type of the object. + + Args: + value: The object to get the bool representation of. + + Returns: + The bool representation of the object. + """ + return value.__bool__() diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 3d6599e8ff..2436a866b2 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -41,6 +41,7 @@ fn takes_bool(cond: Bool) -> Bool: def test_convert_from_boolable(): assert_true(takes_bool(MyTrue())) + assert_true(bool(MyTrue())) def test_bool_to_string(): From c7f3a23675a63e8229784fbd0dcac45837a97487 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 7 May 2024 10:38:57 -0500 Subject: [PATCH 0437/2019] [External] [mojo-stdlib] Add `divmod(Int, Int)` to builtins (#39382) [External] [mojo-stdlib] Add `divmod(Int, Int)` to builtins The documentation for python's divmod: * https://docs.python.org/3/library/functions.html#divmod * https://docs.python.org/3/reference/datamodel.html#object.__divmod__ Later on, traits will allow us to generalize divmod to pretty much anything that has a `__divmod__` method, for example `datetime.timedelta`. We also add tests here as the function `_divmod` of `Int` was previously untested. I verified that the output correspond to what the python interpreter returns when calling `divmod()`. I don't know where to declare that a function doesn't need to be imported (available globally). Where can I find this? Is this declared in the compiler? [Divmod does not need to be imported in python]( https://docs.python.org/3/library/functions.html#built-in-functions) --------- Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2097 MODULAR_ORIG_COMMIT_REV_ID: 24fbad862a52ae75d239efe527d98c5da5c5a0ac --- docs/changelog.md | 5 +++-- stdlib/src/builtin/divmod.mojo | 31 +++++++++++++++++++++++++++++++ stdlib/src/builtin/int.mojo | 13 ++++++------- stdlib/test/builtin/test_int.mojo | 29 +++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 stdlib/src/builtin/divmod.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 27a21165e3..1025905a5d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -45,8 +45,9 @@ what we publish. return Self(round(self.re), round(self.im)) ``` -- The `abs, round, min, and max` functions have moved from `math` to `builtin`, - so you no longer need to do `from math import abs, round, min, max`. +- The `abs, round, min, max, and divmod` functions have moved from `math` to + `builtin`, so you no longer need to do + `from math import abs, round, min, max, divmod`. - Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in the `math` module by implementing the `__floor__()` and `__ceil__()` methods diff --git a/stdlib/src/builtin/divmod.mojo b/stdlib/src/builtin/divmod.mojo new file mode 100644 index 0000000000..66e28f7ca9 --- /dev/null +++ b/stdlib/src/builtin/divmod.mojo @@ -0,0 +1,31 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +fn divmod(numerator: Int, denominator: Int) -> Tuple[Int, Int]: + """Performs integer division and returns the quotient and the remainder. + + Currently supported only for integers. Support for more standard library + types like Int8, Int16... is planned. + + This method calls `a.__divmod__(b)`, thus, the actual implementation of + divmod should go in the `__divmod__` method of the struct of `a`. + + Args: + numerator: The dividend. + denominator: The divisor. + + Returns: + A `Tuple` containing the quotient and the remainder. + """ + return numerator.__divmod__(denominator) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index d31e09aa31..662ee0b331 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -658,25 +658,24 @@ struct Int( return mod @always_inline("nodebug") - fn _divmod(self, rhs: Int) -> StaticIntTuple[2]: + fn __divmod__(self, rhs: Int) -> Tuple[Int, Int]: """Computes both the quotient and remainder using integer division. Args: rhs: The value to divide on. Returns: - The quotient and remainder as a tuple `(self // rhs, self % rhs)`. + The quotient and remainder as a `Tuple(self // rhs, self % rhs)`. """ if rhs == 0: - # this should raise an exception. - return StaticIntTuple[2](0, 0) + return 0, 0 var div: Int = self._positive_div(rhs) if rhs > 0 and self > 0: - return StaticIntTuple[2](div, self._positive_rem(rhs)) + return div, self._positive_rem(rhs) var mod = self - div * rhs if ((rhs < 0) ^ (self < 0)) and mod: - return StaticIntTuple[2](div - 1, mod + rhs) - return StaticIntTuple[2](div, mod) + return div - 1, mod + rhs + return div, mod @always_inline("nodebug") fn __pow__(self, rhs: Int) -> Int: diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 1457d402a5..1b4220fdb0 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -87,6 +87,34 @@ def test_mod(): assert_equal(1, Int(-3) % Int(2)) +def test_divmod(): + var a: Int + var b: Int + a, b = divmod(7, 3) + assert_equal(a, 2) + assert_equal(b, 1) + + a, b = divmod(-7, 3) + assert_equal(a, -3) + assert_equal(b, 2) + + a, b = divmod(-7, -3) + assert_equal(a, 2) + assert_equal(b, -1) + + a, b = divmod(7, -3) + assert_equal(a, -3) + assert_equal(b, -2) + + a, b = divmod(0, 5) + assert_equal(a, 0) + assert_equal(b, 0) + + a, b = divmod(5, 0) + assert_equal(a, 0) + assert_equal(b, 0) + + def test_abs(): assert_equal(abs(Int(-5)), 5) assert_equal(abs(Int(2)), 2) @@ -121,6 +149,7 @@ def main(): test_round() test_floordiv() test_mod() + test_divmod() test_abs() test_string_conversion() test_int_representation() From 0a13a638a71dc31233314eeab00dc8ba7de49ee7 Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 7 May 2024 10:51:47 -0500 Subject: [PATCH 0438/2019] [External] [stdlib] Remove `Bool` constructor from `SIMD` (#39459) [External] [stdlib] Remove `Bool` constructor from `SIMD` Co-authored-by: soraros Closes modularml/mojo#2499 MODULAR_ORIG_COMMIT_REV_ID: 00c0712614c3e088336d8eda65b3540d7c14d0e2 --- stdlib/src/builtin/bool.mojo | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 235d05d635..e628461cb7 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -68,24 +68,6 @@ struct Bool( _type = __mlir_type.`!pop.scalar` ](value) - @always_inline("nodebug") - fn __init__[width: Int](value: SIMD[DType.bool, width]) -> Bool: - """Construct a Bool value given a SIMD value. - - If there is more than a single element in the SIMD value, then value is - reduced using the and operator. - - Parameters: - width: SIMD width. - - Args: - value: The initial SIMD value. - - Returns: - The constructed Bool value. - """ - return value.__bool__() - @always_inline("nodebug") fn __init__[boolable: Boolable](value: boolable) -> Bool: """Implicitly convert a Boolable value to a Bool. From 2783d9bece9b3f0fd683007315b2b4b5403ecbed Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 7 May 2024 10:53:56 -0500 Subject: [PATCH 0439/2019] [External] [stdlib] add in-place bitwise for `Bool` (#39461) [External] [stdlib] add in-place bitwise for `Bool` add in-place bitwise operations for `Bool` Co-authored-by: Helehex Closes modularml/mojo#2494 MODULAR_ORIG_COMMIT_REV_ID: 596a48d79d4e004ee35cac39449269d3349ab297 --- stdlib/src/builtin/bool.mojo | 146 +++++++++++++++++++---------- stdlib/test/builtin/test_bool.mojo | 49 +++++++++- 2 files changed, 144 insertions(+), 51 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index e628461cb7..70cb1c1e9f 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -18,6 +18,11 @@ These are Mojo built-ins, so you don't need to import them. from utils._visualizers import lldb_formatter_wrapping_type +# ===----------------------------------------------------------------------=== # +# Boolable +# ===----------------------------------------------------------------------=== # + + trait Boolable: """The `Boolable` trait describes a type that can be converted to a bool. @@ -43,6 +48,11 @@ trait Boolable: ... +# ===----------------------------------------------------------------------=== # +# Bool +# ===----------------------------------------------------------------------=== # + + @lldb_formatter_wrapping_type @value @register_passable("trivial") @@ -116,6 +126,19 @@ struct Bool( """ return "True" if self else "False" + @always_inline("nodebug") + fn __int__(self) -> Int: + """Convert this Bool to an integer. + + Returns: + 1 if the Bool is True, 0 otherwise. + """ + return Int( + __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( + self.value + ) + ) + @always_inline("nodebug") fn __eq__(self, rhs: Bool) -> Bool: """Compare this Bool to RHS. @@ -151,15 +174,32 @@ struct Bool( self.value, rhs.value ) + # ===-------------------------------------------------------------------===# + # Bitwise operations + # ===-------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __invert__(self) -> Bool: + """Inverts the Bool value. + + Returns: + True if the object is false and False otherwise. + """ + var true = __mlir_op.`kgen.param.constant`[ + _type = __mlir_type.`!pop.scalar`, + value = __mlir_attr.`#pop.simd : !pop.scalar`, + ]() + return __mlir_op.`pop.xor`(self.value, true) + @always_inline("nodebug") fn __and__(self, rhs: Bool) -> Bool: - """Compute `self & rhs`. + """Returns `self & rhs`. Bitwise and's the Bool value with the argument. This method gets invoked when a user uses the `and` infix operator. Args: - rhs: The rhs value of the and statement. + rhs: The right hand side of the `and` statement. Returns: `self & rhs`. @@ -167,96 +207,102 @@ struct Bool( return __mlir_op.`pop.and`(self.value, rhs.value) @always_inline("nodebug") - fn __or__(self, rhs: Bool) -> Bool: - """Compute `self | rhs`. + fn __iand__(inout self, rhs: Bool): + """Computes `self & rhs` and store the result in `self`. - Bitwise or's the Bool value with the argument. This method gets invoked - when a user uses the `or` infix operator. + Args: + rhs: The right hand side of the `and` statement. + """ + self = self & rhs + + @always_inline("nodebug") + fn __rand__(self, lhs: Bool) -> Bool: + """Returns `lhs & self`. Args: - rhs: The rhs value of the or statement. + lhs: The left hand side of the `and` statement. Returns: - `self | rhs`. + `lhs & self`. """ - return __mlir_op.`pop.or`(self.value, rhs.value) + return lhs & self @always_inline("nodebug") - fn __xor__(self, rhs: Bool) -> Bool: - """Compute `self ^ rhs`. + fn __or__(self, rhs: Bool) -> Bool: + """Returns `self | rhs`. - Bitwise Xor's the Bool value with the argument. This method gets invoked - when a user uses the `^` infix operator. + Bitwise or's the Bool value with the argument. This method gets invoked + when a user uses the `or` infix operator. Args: - rhs: The rhs value of the xor statement. + rhs: The right hand side of the `or` statement. Returns: - `self ^ rhs`. + `self | rhs`. """ - return __mlir_op.`pop.xor`(self.value, rhs.value) + return __mlir_op.`pop.or`(self.value, rhs.value) @always_inline("nodebug") - fn __invert__(self) -> Bool: - """Inverts the Bool value. + fn __ior__(inout self, rhs: Bool): + """Computes `self | rhs` and store the result in `self`. - Returns: - True if the object is false and False otherwise. + Args: + rhs: The right hand side of the `or` statement. """ - var true = __mlir_op.`kgen.param.constant`[ - _type = __mlir_type.`!pop.scalar`, - value = __mlir_attr.`#pop.simd : !pop.scalar`, - ]() - return __mlir_op.`pop.xor`(self.value, true) + self = self | rhs @always_inline("nodebug") - fn __rand__(self, value: Bool) -> Bool: - """Return `value & self`. + fn __ror__(self, lhs: Bool) -> Bool: + """Returns `lhs | self`. Args: - value: The other value. + lhs: The left hand side of the `or` statement. Returns: - `value & self`. + `lhs | self`. """ - return value & self + return lhs | self @always_inline("nodebug") - fn __ror__(self, value: Bool) -> Bool: - """Return `value | self`. + fn __xor__(self, rhs: Bool) -> Bool: + """Returns `self ^ rhs`. + + Bitwise Xor's the Bool value with the argument. This method gets invoked + when a user uses the `^` infix operator. Args: - value: The other value. + rhs: The right hand side of the `xor` statement. Returns: - `value | self`. + `self ^ rhs`. """ - return value | self + return __mlir_op.`pop.xor`(self.value, rhs.value) @always_inline("nodebug") - fn __rxor__(self, value: Bool) -> Bool: - """Return `value ^ self`. + fn __ixor__(inout self, rhs: Bool): + """Computes `self ^ rhs` and stores the result in `self`. Args: - value: The other value. - - Returns: - `value ^ self`. + rhs: The right hand side of the `xor` statement. """ - return value ^ self + self = self ^ rhs @always_inline("nodebug") - fn __int__(self) -> Int: - """Convert this Bool to an integer. + fn __rxor__(self, lhs: Bool) -> Bool: + """Returns `lhs ^ self`. + + Args: + lhs: The left hand side of the `xor` statement. Returns: - 1 if the Bool is True, 0 otherwise. + `lhs ^ self`. """ - return Int( - __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( - self.value - ) - ) + return lhs ^ self + + +# ===----------------------------------------------------------------------=== # +# bool +# ===----------------------------------------------------------------------=== # @always_inline diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 2436a866b2..0a7f9bd410 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import assert_equal, assert_true +from testing import assert_equal, assert_true, assert_false def test_bool_cast_to_int(): @@ -49,8 +49,55 @@ def test_bool_to_string(): assert_equal(str(False), "False") +def test_bitwise(): + var value: Bool + + # and + value = False + value &= False + assert_false(value) + value = False + value &= True + assert_false(value) + value = True + value &= False + assert_false(value) + value = True + value &= True + assert_true(value) + + # or + value = False + value |= False + assert_false(value) + value = False + value |= True + assert_true(value) + value = True + value |= False + assert_true(value) + value = True + value |= True + assert_true(value) + + # xor + value = False + value ^= False + assert_false(value) + value = False + value ^= True + assert_true(value) + value = True + value ^= False + assert_true(value) + value = True + value ^= True + assert_false(value) + + def main(): test_bool_cast_to_int() test_bool_none() test_convert_from_boolable() test_bool_to_string() + test_bitwise() From 974cad1ad21009a7e323d4eb6a9fbbc74f6b0b0c Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 7 May 2024 10:56:52 -0500 Subject: [PATCH 0440/2019] [External] [stdlib] Use `reversed(range(...))` in more places (#39453) [External] [stdlib] Use `reversed(range(...))` in more places Co-authored-by: soraros Closes modularml/mojo#2473 MODULAR_ORIG_COMMIT_REV_ID: 1f5d1bb1337cc6601c52ca387a2b75179c8c7a20 --- stdlib/src/builtin/builtin_list.mojo | 4 ++-- stdlib/src/utils/stringref.mojo | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 42b1f9a8f3..85ea8b4285 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -329,8 +329,8 @@ struct VariadicListMem[ # Otherwise this is a variadic of owned elements, destroy them. We # destroy in backwards order to match how arguments are normally torn # down when CheckLifetimes is left to its own devices. - for i in range(len(self), 0, -1): - destroy_pointee(UnsafePointer.address_of(self[i - 1])) + for i in reversed(range(len(self))): + destroy_pointee(UnsafePointer.address_of(self[i])) @always_inline fn __len__(self) -> Int: diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0abef9e2e9..8fa088afb5 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -547,7 +547,7 @@ fn _memrmem[ return DTypePointer[type]() if needle_len == 1: return _memrchr[type](haystack, needle[0], haystack_len) - for i in range(haystack_len - needle_len, -1, -1): + for i in reversed(range(haystack_len - needle_len + 1)): if haystack[i] != needle[0]: continue if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: From 0f8ed59c6304e57dde9ae8d01796039045a8d82f Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 7 May 2024 11:15:07 -0500 Subject: [PATCH 0441/2019] [External] [stdlib] Make the use of the unsafe constructor of List explicit (#39465) [External] [stdlib] Make the use of the unsafe constructor of List explicit This PR is a small piece of https://github.com/modularml/mojo/pull/2507 We want unsafe things to be explicit, as stated in [the vision guide](https://github.com/modularml/mojo/blob/nightly/stdlib/docs/vision.md#objectives-and-goals) Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2523 MODULAR_ORIG_COMMIT_REV_ID: 861433431c0fc7fd6ab1d905dfe09f3e3a5792c1 --- stdlib/src/builtin/file.mojo | 4 +++- stdlib/src/builtin/string.mojo | 12 ++++++++---- stdlib/src/collections/list.mojo | 10 +++++++--- stdlib/test/collections/test_list.mojo | 4 ++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index e18ee787e6..2b16d16f71 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -341,7 +341,9 @@ struct FileHandle: if err_msg: raise (err_msg^).consume_as_error() - var list = List[Int8](buf, size=int(size_copy), capacity=int(size_copy)) + var list = List[Int8]( + unsafe_pointer=buf, size=int(size_copy), capacity=int(size_copy) + ) return list diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index ac950ce1af..ed4896663e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -511,7 +511,9 @@ struct String( var length = len(impl) var capacity = impl.capacity self._buffer = List[Int8]( - impl.steal_data().bitcast[Int8](), size=length, capacity=capacity + unsafe_pointer=impl.steal_data().bitcast[Int8](), + size=length, + capacity=capacity, ) @always_inline @@ -574,7 +576,9 @@ struct String( """ # we don't know the capacity of ptr, but we'll assume it's the same or # larger than len - self = Self(Self._buffer_type(ptr, size=len, capacity=len)) + self._buffer = Self._buffer_type( + unsafe_pointer=ptr, size=len, capacity=len + ) @always_inline fn __init__(inout self, ptr: UnsafePointer[UInt8], len: Int): @@ -589,8 +593,8 @@ struct String( """ # we don't know the capacity of ptr, but we'll assume it's the same or # larger than len - self = Self( - Self._buffer_type(ptr.bitcast[Int8](), size=len, capacity=len) + self._buffer = Self._buffer_type( + unsafe_pointer=ptr.bitcast[Int8](), size=len, capacity=len ) @always_inline diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 19a1a4fa44..1b054a7f5a 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -128,16 +128,20 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.append(value[]) fn __init__( - inout self: Self, data: UnsafePointer[T], *, size: Int, capacity: Int + inout self: Self, + *, + unsafe_pointer: UnsafePointer[T], + size: Int, + capacity: Int, ): """Constructs a list from a pointer, its size, and its capacity. Args: - data: The pointer to the data. + unsafe_pointer: The pointer to the data. size: The number of elements in the list. capacity: The capacity of the list. """ - self.data = data + self.data = unsafe_pointer self.size = size self.capacity = capacity diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 4f9f9ac0cb..a65d070c44 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -645,7 +645,7 @@ def test_constructor_from_pointer(): new_pointer[2] = 2 # rest is not initialized - var some_list = List[Int8](new_pointer, size=3, capacity=5) + var some_list = List[Int8](unsafe_pointer=new_pointer, size=3, capacity=5) assert_equal(some_list[0], 0) assert_equal(some_list[1], 1) assert_equal(some_list[2], 2) @@ -660,7 +660,7 @@ def test_constructor_from_other_list_through_pointer(): var size = len(initial_list) var capacity = initial_list.capacity var some_list = List[Int8]( - initial_list.steal_data(), size=size, capacity=capacity + unsafe_pointer=initial_list.steal_data(), size=size, capacity=capacity ) assert_equal(some_list[0], 0) assert_equal(some_list[1], 1) From 23d0f0b294d0b078fab0b24b65b93387c271b35e Mon Sep 17 00:00:00 2001 From: Peyman Barazandeh Date: Tue, 7 May 2024 12:54:22 -0500 Subject: [PATCH 0442/2019] [External] [stdlib] Add unit tests for the SIMD __rfloordiv__() method and constraint check on the element types (#39451) [External] [stdlib] Add unit tests for the SIMD __rfloordiv__() method and constraint check on the element types The __rfloordiv__() method is already supported by Int and Object. This commit adds support for this method to the SIMD type. Co-authored-by: Peyman Barazandeh Closes modularml/mojo#2414 MODULAR_ORIG_COMMIT_REV_ID: d1050970b5e965aa2df06e9e1378763eaef61551 --- stdlib/src/builtin/simd.mojo | 1 + stdlib/test/builtin/test_simd.mojo | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a0d98a74b7..f613b0cd58 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -671,6 +671,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Returns: `floor(rhs / self)` value. """ + constrained[type.is_numeric(), "the type must be numeric"]() return rhs // self @always_inline("nodebug") diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 0f84102524..5e6501c10e 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -223,6 +223,18 @@ def test_floordiv(): assert_equal(Float32(99) // Float32(-2), -50) +def test_rfloordiv(): + alias I = SIMD[DType.int32, 4] + var i = I(2, 4, -2, -4) + assert_equal(i.__rfloordiv__(2), I(1, 0, -1, -1)) + assert_equal(i.__rfloordiv__(Int32(2)), I(1, 0, -1, -1)) + + alias F = SIMD[DType.float32, 4] + var f = F(3, -4, 1, 5) + assert_equal(f.__rfloordiv__(3), F(1, -1, 3, 0)) + assert_equal(f.__rfloordiv__(Float32(3)), F(1, -1, 3, 0)) + + def test_mod(): assert_equal(Int32(99) % Int32(1), 0) assert_equal(Int32(99) % Int32(3), 0) @@ -873,6 +885,7 @@ def main(): test_round() test_roundeven() test_floordiv() + test_rfloordiv() test_mod() test_rotate() test_shift() From fce6d47c64740d138004717ef3c6440e50fdb742 Mon Sep 17 00:00:00 2001 From: Matthew O Date: Tue, 7 May 2024 13:12:40 -0500 Subject: [PATCH 0443/2019] [External] [docs] Two helpful documentation additions (#39480) [External] [docs] Two helpful documentation additions Love what you folks are doing. --------- Co-authored-by: Matthew O Co-authored-by: Arthur Evans Closes modularml/mojo#2560 MODULAR_ORIG_COMMIT_REV_ID: 8cee20b76c15a0154b20cc2cf0cea6ab91b68706 --- docs/manual/decorators/index.md | 3 ++ docs/manual/index.md | 1 + docs/tools/testing.ipynb | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 docs/tools/testing.ipynb diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.md index 7aea536093..fc0dc6424f 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.md @@ -31,6 +31,9 @@ actually calling the higher-order function, you simply add the decorator (such as the `@value` decorator) above your code (such as a struct). The Mojo compiler then uses the decorator function to modify your code at compile time. +The creation of custom decorators is not yet supported. The available ones are +built directly into the compiler. + The following pages describe each built-in decorator with examples. :::{#docs} diff --git a/docs/manual/index.md b/docs/manual/index.md index 618eac3346..4194c34e9e 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -68,6 +68,7 @@ feedback](/mojo/community.html). - **Tools** - [Debugging](/mojo/tools/debugging.html) + - [Testing](/mojo/tools/testing.html) - **Project information** diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb new file mode 100644 index 0000000000..3de573b171 --- /dev/null +++ b/docs/tools/testing.ipynb @@ -0,0 +1,57 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "---\n", + "title: Testing\n", + "sidebar_position: 2\n", + "description: Testing Mojo programs.\n", + "css: /static/styles/page-navigation.css\n", + "website:\n", + " open-graph:\n", + " image: /static/images/mojo-social-card.png\n", + " twitter-card:\n", + " image: /static/images/mojo-social-card.png\n", + "---" + ], + "id": "e622ec78c00f1839" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Built-in test runner\n", + "\n", + "## File and folder conventions\n", + "\n", + "The Mojo CLI has a built-in test runner that can be used to test Mojo programs. `mojo test` will recursively search the `test` directory for any files with `test` in the filename. So in the following example:\n", + "```bash\n", + "ls ./test\n", + "test_users_controller.mojo\n", + "factories.mojo\n", + "./test/models:\n", + "user_model_tests.mojo\n", + "```\n", + "... the `mojo` CLI will search `test_users_controller.mojo` and `user_model_tests.mojo` for tests, but not `factories.mojo`. \n", + "\n", + "## Test functions\n", + "\n", + "Test functions must be named `test_*` and take no arguments. Use `def` notation rather than `fn` notation. If you use `fn` notation then, in the presence of any assertion functions, the compiler will complain unless you wrap the body in a `try/exception` block. So:\n", + "```mojo\n", + "def test_foo():\n", + " assert_equal(1, 1)\n", + "```\n", + "\n", + "Not:\n", + "```mojo\n", + "fn test_foo():\n", + " assert_equal(1, 1)\n", + "```\n", + "\n" + ], + "id": "1d92ef1d2dd77643" + } + ] +} From e2051954482dcaff09a0e1f0afa65a10c3fccd25 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 7 May 2024 12:51:46 -0700 Subject: [PATCH 0444/2019] [Docs] Fix code example. Fixes an incomplete code example and some missing text in the Types doc. MODULAR_ORIG_COMMIT_REV_ID: 14f28b592f0556636126c6ea2bb175754a622063 --- docs/manual/types.ipynb | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index fc2da0f793..79169dde45 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -131,7 +131,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -204,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -263,7 +263,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -331,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -379,7 +379,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -405,13 +405,13 @@ "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", "explicitly convert a value to string.\n", "\n", - "When concatenating values to a string using the `+` operator, `Stringable`\n", - "will implicitly convert `String`:" + "When concatenating values to a string using the `+` operator, `Stringable` types\n", + "implicitly convert to `String`:" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -464,17 +464,28 @@ "`StringLiteral` values to `String` using the built-in \n", "[`str()`](/mojo/stdlib/builtin/str/str) method. \n", "\n", - "For example, when concatenating strings with other values" + "For example, if you want to concatenate string literals to other types, you need \n", + "to first convert `StringLiteral` to `String` values. This is because many types\n", + "can be implicitly converted to `String`, but not to `StringLiteral`." ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Strings play nicely with others: True\n" + ] + } + ], "source": [ - "var a = 5\n", - "var b: String = \"String literals may not concatenate \"" + "# print(\"Strings play nicely with others: \" + True)\n", + "# Error: ... right hand side cannot be converted from Bool to StringLiteral\n", + "print(str(\"Strings play nicely with others: \") + True)" ] }, { @@ -924,8 +935,8 @@ " copied).\n", "\n", "- Memory-only types consist of any types that _don't_ fit the description of\n", - " register-passable types. In particular, these types usually use pointers or\n", - " references to manage heap-allocated memory. `String`, `List`, and `Dict` are\n", + " register-passable types. In particular, these types usually have pointers or \n", + " references to dynamically-allocated memory. `String`, `List`, and `Dict` are\n", " all examples of memory-only types.\n", "\n", "Our long-term goal is to make this distinction transparent to the user, and\n", From 23da6e00fc0f8c79f02ec31d05b3afa0c4d70d62 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 7 May 2024 13:13:51 -0700 Subject: [PATCH 0445/2019] Delete obsolete front matter This was used with our previous Quarto-based website. MODULAR_ORIG_COMMIT_REV_ID: 2dcbc114cf01bf088bcbb235320cac10c5319bb1 --- docs/changelog-released.md | 5 ----- docs/community.md | 5 ----- docs/faq.md | 5 ----- docs/manual/basics.ipynb | 15 +++++---------- docs/manual/decorators/always-inline.ipynb | 12 +++++------- docs/manual/decorators/index.md | 6 ------ docs/manual/decorators/nonmaterializable.ipynb | 12 +++++------- docs/manual/decorators/parameter.ipynb | 12 +++++------- docs/manual/decorators/register-passable.ipynb | 12 +++++------- docs/manual/decorators/staticmethod.ipynb | 12 +++++------- docs/manual/decorators/unroll.ipynb | 12 +++++------- docs/manual/decorators/value.ipynb | 12 +++++------- docs/manual/functions.ipynb | 13 +++++-------- docs/manual/index.md | 8 -------- docs/manual/lifecycle/death.ipynb | 12 +++++------- docs/manual/lifecycle/index.ipynb | 12 +++++------- docs/manual/lifecycle/life.ipynb | 12 +++++------- docs/manual/packages.md | 9 --------- docs/manual/parameters/index.ipynb | 12 +++++------- docs/manual/python/index.ipynb | 12 +++++------- docs/manual/python/types.ipynb | 12 +++++------- docs/manual/structs.ipynb | 13 +++++-------- docs/manual/traits.ipynb | 12 +++++------- docs/manual/values/index.ipynb | 13 +++++-------- docs/manual/values/ownership.ipynb | 13 +++++-------- docs/manual/values/value-semantics.ipynb | 13 +++++-------- docs/manual/variables.ipynb | 13 +++++-------- docs/notebooks/index.md | 5 ----- docs/roadmap.md | 5 ----- docs/tools/debugging.ipynb | 12 +++++------- docs/why-mojo.md | 5 ----- 31 files changed, 110 insertions(+), 216 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 76cbfe4bb5..075945df62 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -3,11 +3,6 @@ title: Mojo🔥 changelog sidebar_label: Changelog description: A history of significant Mojo changes. toc_max_heading_level: 2 -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- This is a running list of significant changes for the Mojo language and tools. diff --git a/docs/community.md b/docs/community.md index dbd0383f16..ba182694e7 100644 --- a/docs/community.md +++ b/docs/community.md @@ -3,11 +3,6 @@ title: Mojo🔥 community sidebar_label: Community description: Resources to share feedback, report issues, and chat. hide_table_of_contents: true -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- diff --git a/docs/faq.md b/docs/faq.md index c515ca8c7a..475c7500ce 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,11 +2,6 @@ title: Mojo🔥 FAQ sidebar_label: FAQ description: Answers to questions we expect about Mojo. -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- We tried to anticipate your questions about Mojo on this page. If this page diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index 8353564207..706b69ac37 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -2,21 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Introduction to Mojo\n", - "order: 1\n", "sidebar_position: 1\n", "description: Introduction to Mojo's basic language features.\n", - "aliases:\n", - " - /mojo/notebooks/HelloMojo.html\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/always-inline.ipynb b/docs/manual/decorators/always-inline.ipynb index fe5df87a81..d0648a2f0d 100644 --- a/docs/manual/decorators/always-inline.ipynb +++ b/docs/manual/decorators/always-inline.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@always_inline`'\n", "description: Copies the body of a function directly into the body of the calling function.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.md index fc0dc6424f..ba5b6373af 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.md @@ -4,12 +4,6 @@ sidebar_label: Decorators sidebar_position: 1 description: A reference of Mojo's built-in decorators hide_table_of_contents: true -css: /static/styles/page-navigation.css -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png listing: - id: docs contents: diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index 1078954e90..b5a657517b 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@nonmaterializable`'\n", "description: Declares that a type should exist only in the parameter domain.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/parameter.ipynb b/docs/manual/decorators/parameter.ipynb index b4e332c268..65aff01912 100644 --- a/docs/manual/decorators/parameter.ipynb +++ b/docs/manual/decorators/parameter.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@parameter`'\n", "description: Executes a function or if statement at compile time.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb index f4036a6118..682a6f9259 100644 --- a/docs/manual/decorators/register-passable.ipynb +++ b/docs/manual/decorators/register-passable.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@register_passable`'\n", "description: Declares that a type should be passed in machine registers.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index 6875521eeb..b8d46eae86 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@staticmethod`'\n", "description: Declares a struct method as static.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/unroll.ipynb b/docs/manual/decorators/unroll.ipynb index c7c6b2a1ab..e6f3d8138b 100644 --- a/docs/manual/decorators/unroll.ipynb +++ b/docs/manual/decorators/unroll.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@unroll`'\n", "description: Unrolls a loop at compile time.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/decorators/value.ipynb b/docs/manual/decorators/value.ipynb index 707b33f116..b6753b42e5 100644 --- a/docs/manual/decorators/value.ipynb +++ b/docs/manual/decorators/value.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: '`@value`'\n", "description: Generates boilerplate lifecycle methods for a struct.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index dd5eda877e..1597cea83b 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -2,19 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Functions\n", - "order: 2\n", "sidebar_position: 2\n", "description: Introduction to Mojo `fn` and `def` functions.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/index.md b/docs/manual/index.md index 4194c34e9e..effdd8ac84 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -2,14 +2,6 @@ title: "Mojo Manual" sidebar_label: Introduction description: A comprehensive guide to the Mojo programming language. -aliases: - - /mojo/programming-manual.html -css: /static/styles/page-navigation.css -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- Welcome to the Mojo Manual, a complete guide to the Mojo🔥 programming language! diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index c2d67ed01f..51299e0b29 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Death of a value\n", "sidebar_position: 3\n", "description: An explanation of when and how Mojo destroys values.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 661262d9f1..1c3226f9d0 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Intro to value lifecycle\n", "sidebar_position: 1\n", "description: An introduction to the value lifecycle.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 9421e6f3b3..a8d0d00b4c 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Life of a value\n", "sidebar_position: 2\n", "description: An explanation of when and how Mojo creates values.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/packages.md b/docs/manual/packages.md index c94aac8437..b1b4db3030 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -1,17 +1,8 @@ --- title: Modules and packages -order: 5 sidebar_position: 5 sidebar_label: Modules and packages description: Learn how to package Mojo code for distribution and importing. -css: /static/styles/page-navigation.css -aliases: - - /mojo/manual/get-started/packages.html -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- Mojo provides a packaging system that allows you to organize and compile code diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index b733e8c343..48073e73c9 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: \"Parameterization: compile-time metaprogramming\"\n", "description: An introduction to parameters and compile-time metaprogramming.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 3c8dfc0095..374bbbe44a 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Python integration\n", "sidebar_position: 1\n", "description: Using Python and Mojo together.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index f0058d913a..28842ac562 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Python types\n", "sidebar_position: 2\n", "description: Using Mojo types in Python, and Python types in Mojo.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---\n" ] }, diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index 18001717b5..ace17b025c 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -2,19 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Structs\n", - "order: 4\n", "sidebar_position: 4\n", "description: Introduction to Mojo structures (structs).\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 1758df3e34..03a38fd176 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -2,17 +2,15 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Traits\n", "description: Define shared behavior for types.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 5633ff522f..103a77717e 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -2,19 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Intro to value ownership\n", - "order: 1\n", "sidebar_position: 1\n", "description: Introduction to Mojo value ownership.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index c15e11987f..5201c34924 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -2,19 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Ownership and borrowing\n", - "order: 3\n", "sidebar_position: 3\n", "description: How Mojo shares references through function arguments.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index 1f08eceaaa..f405a34ab9 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -2,19 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Value semantics\n", - "order: 2\n", "sidebar_position: 2\n", "description: An explanation of Mojo's value-semantic defaults.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 9e1f5c633d..391204ebd6 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -2,19 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Variables\n", - "order: 3\n", "sidebar_position: 3\n", "description: Introduction to Mojo variables.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/notebooks/index.md b/docs/notebooks/index.md index c647c1c319..09e76b05fe 100644 --- a/docs/notebooks/index.md +++ b/docs/notebooks/index.md @@ -3,11 +3,6 @@ title: Mojo🔥 notebooks sidebar_label: Overview hide_table_of_contents: true description: All the Jupyter notebooks we've created for the Mojo Playground. -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png anchor-sections: false listing: - id: docs diff --git a/docs/roadmap.md b/docs/roadmap.md index 374bc97af3..925389dffd 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -2,11 +2,6 @@ title: Mojo🔥 roadmap & sharp edges sidebar_label: Roadmap & sharp edges description: A summary of our Mojo plans, including upcoming features and things we need to fix. -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- This document captures the broad plan about how we plan to implement things in diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 8d8b306ad8..9167270a84 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "raw", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "raw" + } + }, "source": [ "---\n", "title: Debugging\n", "sidebar_position: 1\n", "description: Debugging Mojo programs.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" ] }, diff --git a/docs/why-mojo.md b/docs/why-mojo.md index 508e2de247..7afe2bf093 100644 --- a/docs/why-mojo.md +++ b/docs/why-mojo.md @@ -2,11 +2,6 @@ title: Why Mojo🔥 sidebar_label: Why Mojo description: A backstory and rationale for why we created the Mojo language. -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png --- When we started Modular, we had no intention of building a new programming From 1679c0e61fe47355ecf966b2f00f406e181d47d6 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 7 May 2024 13:29:54 -0700 Subject: [PATCH 0446/2019] Unify the install procedure for Mojo and MAX packages Update the get started procedures and other relevant pages/links to create a single install guide for MAX/Mojo. MODULAR_ORIG_COMMIT_REV_ID: 3bfe0c40508f4686e0f697f5d8b451e6f0ddb840 --- .../hello-world.md => get-started.md} | 75 +++--- .../get-started/images/mojo-playground.png | Bin 265873 -> 0 bytes docs/manual/get-started/index.md | 236 ------------------ .../{get-started => }/images/mojo-vscode.png | Bin docs/manual/index.md | 4 +- 5 files changed, 48 insertions(+), 267 deletions(-) rename docs/manual/{get-started/hello-world.md => get-started.md} (61%) delete mode 100644 docs/manual/get-started/images/mojo-playground.png delete mode 100644 docs/manual/get-started/index.md rename docs/manual/{get-started => }/images/mojo-vscode.png (100%) diff --git a/docs/manual/get-started/hello-world.md b/docs/manual/get-started.md similarity index 61% rename from docs/manual/get-started/hello-world.md rename to docs/manual/get-started.md index 994dbe94b8..62aaeaadfc 100644 --- a/docs/manual/get-started/hello-world.md +++ b/docs/manual/get-started.md @@ -1,34 +1,31 @@ --- -title: Hello, world! -sidebar_position: 2 -description: Learn to run your first Mojo program. -css: /static/styles/page-navigation.css -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png +title: Get started with Mojo🔥 +sidebar_label: Get started +description: Install Mojo now and start developing --- -After you [install the MAX SDK](/engine/get-started) or -[Mojo SDK](/mojo/manual/get-started/) or you can use the -[Mojo CLI](/mojo/cli/) to build and run Mojo programs. So let's create the -classic starter program that prints "Hello, world!", in three different ways. +On this page, we'll show you how to create the classic "Hello world" starter +program with Mojo, in three different ways. If you'd rather read how to write +Mojo code beyond just printing text, see the [introduction to +Mojo](/mojo/manual/basics). -## Before you start +:::tip Updating? -Before you start, make sure the `MODULAR_HOME` and `PATH` environment variables -are set, as described in the install procedure, so you can run the `mojo` -command: +If you already installed Mojo, see the [update guide](/max/update). -```text -mojo --version -``` +::: + +## 1. Install Mojo -If you have other issues during install, check our [known -issues](/mojo/roadmap.html#mojo-sdk-known-issues). +Mojo is now bundled with MAX, which provides everything to compile, +run, debug, and package Mojo code ([read +why](/engine/faq#why-bundle-mojo-with-max)). -## 1. Run code in the REPL +To install Mojo, [see the MAX install guide](/max/install). + +## 2. Run code in the REPL + +Now that you've installed Mojo, let's write some code! First, let's use the Mojo [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), @@ -63,7 +60,7 @@ The REPL is primarily useful for short experiments because the code isn't saved. So when you want to write a real program, you need to write the code in a `.mojo` source file. -## 2. Run a Mojo file +## 3. Run a Mojo file Now let's write the code in a Mojo source file and run it with the [`mojo`](/mojo/cli/) command: @@ -93,11 +90,11 @@ If this didn't work for you, double-check your code looks exactly like the code in step 1, and make sure you correctly [installed Mojo](/mojo/manual/get-started/#install-mojo). -## 3. Build an executable binary +## 4. Build an executable binary Finally, let's build and run that same code as an executable: -1. Create an executable file with the [`build`](/mojo/cli/build.html) command: +1. Create an executable file with the [`build`](/mojo/cli/build) command: ```sh mojo build hello.mojo @@ -115,10 +112,19 @@ Finally, let's build and run that same code as an executable: This creates a statically compiled binary file, so it contains all the code and libraries it needs to run. +## 5. Install our VS Code extension (optional) + +To provide a first-class developer experience with features like code +completion, quick fixes, and hover help, we've created a [Mojo extension for +Visual Studio +Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + +![](./images/mojo-vscode.png) + ## Next steps -- If you're new to Mojo, we suggest you continue to the next section about - [language basics](/mojo/manual/basics.html). +- If you're new to Mojo, we suggest you learn the language basics in the + [introduction to Mojo](/mojo/manual/basics). - If you want to experiment with some code, clone [the Mojo repo](https://github.com/modularml/mojo/) to try our code examples: @@ -132,4 +138,15 @@ repo](https://github.com/modularml/mojo/) to try our code examples: that teach advanced Mojo features. - To see all the available Mojo APIs, check out the [Mojo standard library - reference](/mojo/lib.html). + reference](/mojo/lib). + +If you have issues during install, check our [known +issues](/mojo/roadmap#mojo-sdk-known-issues). + +:::note + +To help us improve Mojo, we collect some basic system information and +crash reports. [Learn +more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). + +::: diff --git a/docs/manual/get-started/images/mojo-playground.png b/docs/manual/get-started/images/mojo-playground.png deleted file mode 100644 index dd044f0edc8388e5beb38c55f31a286d54aa8753..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265873 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
+ + Or, click here to see the manual install commands. + +
+ + + + + ```sh + brew update && brew install modularml/packages/modular + ``` + + + + + ```sh + apt-get install -y apt-transport-https && + keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && + apt-get update && + apt-get install -y modular + ``` + + + + +
+
+ +2. Create a virtual environment: + + Because Mojo interoperates with Python, + it's important to define a predictable Python version and package library to + use. We suggest you do that with either venv or conda: + + + + + For most users, we recommend venv (it's included with Python): + + ```sh + python3 -m venv mojo-venv && source mojo-venv/bin/activate + ``` + + + + + Only if you already use conda as your preferred environment, we suggest you + use that: + + ```sh + conda create -n mojo python=3.10 -y && conda activate mojo + ``` + + + + +3. Install the Mojo SDK: + + ```sh + modular install mojo + ``` + +4. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: + + + + + If you're using Bash, run this command: + + ```sh + MOJO_PATH=$(modular config mojo.path) \ + && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ + && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> "$BASHRC" \ + && source "$BASHRC" + ``` + + + + + If you're using ZSH, run this command: + + ```sh + MOJO_PATH=$(modular config mojo.path) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ + && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> ~/.zshrc \ + && source ~/.zshrc + ``` + + + + + If you're using fish, run this command: + + ```sh + set MOJO_PATH (modular config mojo.path) \ + && set -Ux MODULAR_HOME $HOME/.modular \ + && fish_add_path $MOJO_PATH/bin + ``` + + + + + +{/*############################*/} +{/*### NIGHTLY BUILD SETUP ####*/} +{/*############################*/} + + +:::caution + +Nightly builds are not fully tested. They might include incomplete features, +performance regressions, and new bugs. When using code from the +[mojo](https://github.com/modularml/mojo) GitHub repo, be sure you checkout +the `nightly` branch, because the `main` branch might not be compatible with +nightly builds. + +::: + +1. Open a terminal and install the [`modular`](/cli/) command line tool with + this helper script (this is the same for stable and nightly builds): + + ```sh + curl -s https://get.modular.com | sh - + ``` + +
+ + Or, click here to see the manual install commands. + +
+ + + + + ```sh + brew update && brew install modularml/packages/modular + ``` + + + + + ```sh + apt-get install -y apt-transport-https && + keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && + apt-get update && + apt-get install -y modular + ``` + + + + +
+
+ +2. Create a virtual environment for nightly builds: + + Because Mojo interoperates with Python, + it's important to define a predictable Python version and package library to + use. We suggest you do that with either venv or conda: + + + + + For most users, we recommend venv (it's included with Python): + + ```sh + python3 -m venv mojo-nightly-venv && source mojo-nightly-venv/bin/activate + ``` + + + + + Only if you already use conda as your preferred environment, we suggest you + use that: + + ```sh + conda create -n mojo-nightly python=3.10 -y && conda activate mojo-nightly + ``` + + + + +3. Install the nightly Mojo SDK: + + ```sh + modular install nightly/mojo + ``` + +4. Set environment variables so you can access the nightly [`mojo`](/mojo/cli/) + CLI: + + + + + If you're using Bash, run this command: + + ```sh + MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ + && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ + && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> "$BASHRC" \ + && source "$BASHRC" + ``` + + + + + If you're using ZSH, run this command: + + ```sh + MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ + && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> ~/.zshrc \ + && source ~/.zshrc + ``` -To install Mojo, [see the MAX install guide](/max/install). + + + + If you're using fish, run this command: + + ```sh + set MOJO_NIGHTLY_PATH (modular config mojo-nightly.path) \ + && set -Ux MODULAR_HOME $HOME/.modular \ + && fish_add_path $MOJO_NIGHTLY_PATH/bin + ``` + + + + +
+ + +Now you're ready to go. ## 2. Run code in the REPL @@ -88,7 +380,7 @@ Now let's write the code in a Mojo source file and run it with the If this didn't work for you, double-check that your code looks exactly like the code in step 1, and make sure you correctly [installed -MAX](/max/install) (it includes Mojo). +mojo](/mojo/install) (it includes Mojo). ## 4. Build an executable binary @@ -156,3 +448,58 @@ crash reports. [Learn more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). ::: + +## Update Mojo + +To check your current Mojo version, use the `--version` option: + +```sh +mojo --version +``` + +And compare your version to the latest stable version in the [Mojo +changelog](/mojo/changelog). Or if you installed a nightly build, look for +release announcements in [this Discord +channel](https://discord.com/channels/1087530497313357884/1224434323193594059). + +If it's time to update, here's what to do: + +1. Make sure you have the latest `modular` CLI: + + + + + ```sh + brew update \ + && brew upgrade modular + ``` + + + + + ```sh + sudo apt update \ + && sudo apt install modular + ``` + + + + +2. Update the `mojo` package: + + + + + ```sh + modular update mojo + ``` + + + + + ```sh + modular update nightly/mojo + ``` + + + From 36dc7fd984024e9d9db9be4745cf93fbbdb25359 Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Fri, 17 May 2024 13:04:21 -0700 Subject: [PATCH 0610/2019] [stdlib] Add __call_location tests and clarify new behavior Adding some tests for `__call_location` now that inlining has been cleaned up. In addition, `__call_location` now returns the caller location even if used inside a function that was inlined into an always_inline_nodebug function. MODULAR_ORIG_COMMIT_REV_ID: 28dbf036a6d4265f55c6ed34bd3409b606f764b5 --- stdlib/src/builtin/_location.mojo | 9 ++++----- stdlib/test/builtin/test_location.mojo | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 32354be21d..6a9c0ab30f 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -66,11 +66,10 @@ fn __source_location() -> _SourceLocation: fn __call_location() -> _SourceLocation: """Returns the location where the enclosing function is called. - This should only be used in `@always_inline` and `@always_inline("nodebug")` - functions. When the enclosing function is `@always_inline`, the call - location will not be correct if inside a `@always_inline("nodebug")` - function. This is intended behavior since `@always_inline("nodebug")` is - meant to erase debug information, including locations. + This should only be used in `@always_inline` or `@always_inline("nodebug")` + functions so that it returns the source location of where the enclosing + function is called at (even if inside another `@always_inline("nodebug")` + function). This currently doesn't work when this or the enclosing function is called in a parameter expression. diff --git a/stdlib/test/builtin/test_location.mojo b/stdlib/test/builtin/test_location.mojo index 6e9080d325..47965c8ef7 100644 --- a/stdlib/test/builtin/test_location.mojo +++ b/stdlib/test/builtin/test_location.mojo @@ -182,13 +182,27 @@ fn get_four_call_locs_inlined() -> ( fn test_builtin_call_loc() raises: + var l = (148, 149, 156, 157) + var c = (25, 33, 25, 33) var loc_pair = get_call_locs() + check_source_loc(l[0], c[0], loc_pair[0]) + check_source_loc(l[1], c[1], loc_pair[1]) loc_pair = get_call_locs_inlined() + check_source_loc(l[2], c[2], loc_pair[0]) + check_source_loc(l[3], c[3], loc_pair[1]) var loc_quad = get_four_call_locs() + check_source_loc(l[0], c[0], loc_quad[0]) + check_source_loc(l[1], c[1], loc_quad[1]) + check_source_loc(l[2], c[2], loc_quad[2]) + check_source_loc(l[3], c[3], loc_quad[3]) loc_quad = get_four_call_locs_inlined() + check_source_loc(l[0], c[0], loc_quad[0]) + check_source_loc(l[1], c[1], loc_quad[1]) + check_source_loc(l[2], c[2], loc_quad[2]) + check_source_loc(l[3], c[3], loc_quad[3]) @always_inline From d82e03be49c40a3d41219bb612fe46981a9ed2ab Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 17 May 2024 15:18:20 -0500 Subject: [PATCH 0611/2019] [External] [stdlib] Add `bin()` builtin function (#40176) [External] [stdlib] Add `bin()` builtin function Fixes #2604 Implements the `bin()` builtin function from Python for converting integral values to a binary string representation. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2603 MODULAR_ORIG_COMMIT_REV_ID: 96515ed124867bcac5a49781d57a92fae6bc480b --- docs/changelog.md | 9 +++- stdlib/src/builtin/bin.mojo | 85 +++++++++++++++++++++++++++++++ stdlib/src/builtin/int.mojo | 2 +- stdlib/test/builtin/test_bin.mojo | 55 ++++++++++++++++++++ 4 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 stdlib/src/builtin/bin.mojo create mode 100644 stdlib/test/builtin/test_bin.mojo diff --git a/docs/changelog.md b/docs/changelog.md index d969eb3e6d..e79036e6f6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -269,7 +269,10 @@ what we publish. - Added the `Indexer` trait to denote types that implement the `__index__()` method which allow these types to be accepted in common `__getitem__` and `__setitem__` implementations, as well as allow a new builtin `index` function - to be called on them. For example: + to be called on them. + ([PR #2685](https://github.com/modularml/mojo/pull/2685) by + [@bgreni](https://github.com/bgreni)) + For example: ```mojo @value @@ -298,6 +301,10 @@ what we publish. the ability to execute a `Benchmark` and allows for benchmarking configuration via the `BenchmarkConfig` struct. +- Added the `bin()` builtin function to convert integral types into their binary + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) + by [@bgreni](https://github.com/bgreni)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/builtin/bin.mojo b/stdlib/src/builtin/bin.mojo new file mode 100644 index 0000000000..f15cd5c8e9 --- /dev/null +++ b/stdlib/src/builtin/bin.mojo @@ -0,0 +1,85 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the `bin()` function + +These are Mojo built-ins, so you don't need to import them. +""" + + +# Need this until we have constraints to stop the compiler from matching this +# directly to bin[type: DType](num: Scalar[type]). +@always_inline("nodebug") +fn bin(b: Scalar[DType.bool], /) -> String: + """Returns the binary representation of a scalar bool. + + Args: + b: A scalar bool value. + + Returns: + The binary string representation of b. + """ + return bin(index(b)) + + +fn bin[type: DType](num: Scalar[type], /) -> String: + """Return the binary string representation an integral value. + + ```mojo + print(bin(123)) + print(bin(-123)) + ``` + ```plaintext + '0b1111011' + '-0b1111011' + ``` + + Parameters: + type: The data type of the integral scalar. + + Args: + num: An integral scalar value. + + Returns: + The binary string representation of num. + """ + constrained[type.is_integral(), "Expected integral value"]() + alias BIN_PREFIX = "0b" + + if num == 0: + return BIN_PREFIX + "0" + + # TODD: pre-allocate string size when #2194 is resolved + var result = String() + var cpy = abs(num) + while cpy > 0: + result += str(cpy & 1) + cpy = cpy >> 1 + + result = BIN_PREFIX + result[::-1] + return "-" + result if num < 0 else result + + +@always_inline("nodebug") +fn bin[T: Indexer](num: T, /) -> String: + """Returns the binary representation of an indexer type. + + Parameters: + T: The Indexer type. + + Args: + num: An indexer value. + + Returns: + The binary string representation of num. + """ + return bin(Scalar[DType.index](index(num))) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 3f7bf92e41..841050dfe6 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -58,7 +58,7 @@ trait Indexer: @always_inline("nodebug") -fn index[T: Indexer](idx: T) -> Int: +fn index[T: Indexer](idx: T, /) -> Int: """Returns the value of `__index__` for the given value. Parameters: diff --git a/stdlib/test/builtin/test_bin.mojo b/stdlib/test/builtin/test_bin.mojo new file mode 100644 index 0000000000..c09b1d0bf9 --- /dev/null +++ b/stdlib/test/builtin/test_bin.mojo @@ -0,0 +1,55 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal + + +@value +struct Ind(Indexer): + fn __index__(self) -> Int: + return 1 + + +def test_bin_scalar(): + assert_equal(bin(Int8(2)), "0b10") + assert_equal(bin(Int32(123)), "0b1111011") + assert_equal(bin(Int32(-123)), "-0b1111011") + assert_equal(bin(Scalar[DType.bool](True)), "0b1") + assert_equal(bin(Scalar[DType.bool](False)), "0b0") + + +def test_bin_int(): + assert_equal(bin(0), "0b0") + assert_equal(bin(1), "0b1") + assert_equal(bin(-1), "-0b1") + assert_equal(bin(4), "0b100") + assert_equal(bin(Int(-4)), "-0b100") + assert_equal(bin(389703), "0b1011111001001000111") + assert_equal(bin(-10), "-0b1010") + + +def test_bin_bool(): + assert_equal(bin(True), "0b1") + assert_equal(bin(False), "0b0") + + +def test_indexer(): + assert_equal(bin(Ind()), "0b1") + + +def main(): + test_bin_scalar() + test_bin_int() + test_bin_bool() + test_indexer() From baf3a64aaca6bc59b3c4a8a566c1b248b300a153 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 17 May 2024 13:57:28 -0700 Subject: [PATCH 0612/2019] Delete overview page This page was originally meant to be a product overview of all docs, but we've learned that it mostly turned out to be a speed bump in the navigation UX. This path now redirects to the Mojo manual intro. MODULAR_ORIG_COMMIT_REV_ID: 243c3a502ec86d0a7d198df0e294c740410e2f7a --- docs/index.md | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 docs/index.md diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 0cc92c0946..0000000000 --- a/docs/index.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Mojo🔥 -sidebar_label: Overview -hide_table_of_contents: true -description: A programming language that bridges the gap between AI research - and production, unlocking speed and usability. -anchor-sections: false -listing: - - id: docs - contents: - - manual/get-started.md - - why-mojo.md - - manual/basics.ipynb - - lib.md - - notebooks/index.md - - changelog.md - - cli/index.md - - ../cli/index.md - - roadmap.md - - faq.md - - community.md - type: grid - grid-columns: 2 - sort: "false" - fields: [title, description] ---- - -Mojo is a new programming language that bridges the gap between research and -production by combining the best of Python syntax with systems programming and -metaprogramming. With Mojo, you can write portable code that's faster than C -and seamlessly inter-op with the Python ecosystem. - -The Mojo SDK is available for Linux and macOS! 🔥 - -:::{#docs} -::: From d7d1d7da36dcd02ace0ef498567af7e33c7e2a31 Mon Sep 17 00:00:00 2001 From: Chris <32541739+ChristopherLR@users.noreply.github.com> Date: Fri, 17 May 2024 16:15:22 -0500 Subject: [PATCH 0613/2019] [External] [mojo-stdlib] Add variadic initialiser, __iter__ and __contains__ to InlineList (#40189) [External] [mojo-stdlib] Add variadic initialiser, __iter__ and __contains__ to InlineList This PR adds some features to InlineList ( related issue #2658 ) *Variadic initialiser* ```mojo var x = InlineList[Int](1,2,3) ``` *iter* ```mojo var x = InlineList[Int](1,2,3) for i in x: print(i) ``` *contains* ```mojo var x = InlineList[Int](1,2,3) if 3 in x: print("ok") ``` Co-authored-by: Chris <32541739+ChristopherLR@users.noreply.github.com> Closes modularml/mojo#2703 MODULAR_ORIG_COMMIT_REV_ID: 6a9eb7b41905e62f8db0855159fd851b7ffb6174 --- docs/changelog.md | 3 + stdlib/src/collections/inline_list.mojo | 97 +++++++++++++++++++ stdlib/test/collections/test_inline_list.mojo | 52 ++++++++++ 3 files changed, 152 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e79036e6f6..368e74b1c1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -247,6 +247,9 @@ what we publish. - `List()` now supports `__contains__`. ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) +- `InlineList()` now supports `__contains__`, `__iter__`. + ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) + - `List` now has an `index` method that allows one to find the (first) location of an element in a `List` of `EqualityComparable` types. For example: diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 8519899d49..7bd9ab2765 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -20,10 +20,55 @@ from collections import InlineList """ from utils import InlineArray +from sys.intrinsics import _type_is_eq + # ===----------------------------------------------------------------------===# # InlineList # ===----------------------------------------------------------------------===# +@value +struct _InlineListIter[ + T: CollectionElement, + capacity: Int, + list_mutability: Bool, + list_lifetime: AnyLifetime[list_mutability].type, + forward: Bool = True, +]: + """Iterator for InlineList. + + Parameters: + T: The type of the elements in the list. + capacity: The maximum number of elements that the list can hold. + list_mutability: Whether the reference to the list is mutable. + list_lifetime: The lifetime of the List + forward: The iteration direction. `False` is backwards. + """ + + alias list_type = InlineList[T, capacity] + + var index: Int + var src: Reference[Self.list_type, list_mutability, list_lifetime] + + fn __iter__(self) -> Self: + return self + + fn __next__( + inout self, + ) -> Reference[T, list_mutability, list_lifetime]: + @parameter + if forward: + self.index += 1 + return self.src[].__refitem__(self.index - 1) + else: + self.index -= 1 + return self.src[].__refitem__(self.index) + + fn __len__(self) -> Int: + @parameter + if forward: + return len(self.src[]) - self.index + else: + return self.index # TODO: Provide a smarter default for the capacity. @@ -53,6 +98,19 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): ) self._size = 0 + # TODO: Avoid copying elements in once owned varargs + # allow transfers. + fn __init__(inout self, *values: ElementType): + """Constructs a list from the given values. + + Args: + values: The values to populate the list with. + """ + debug_assert(len(values) < capacity, "List is full.") + self = Self() + for value in values: + self.append(value[]) + @always_inline fn __len__(self) -> Int: """Returns the length of the list.""" @@ -98,3 +156,42 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): """Destroy all the elements in the list and free the memory.""" for i in range(self._size): destroy_pointee(UnsafePointer(self._array[i])) + + fn __iter__( + self: Reference[Self, _, _], + ) -> _InlineListIter[ElementType, capacity, self.is_mutable, self.lifetime]: + """Iterate over elements of the list, returning immutable references. + + Returns: + An iterator of immutable references to the list elements. + """ + return _InlineListIter(0, self) + + @always_inline + fn __contains__[ + C: ComparableCollectionElement + ](self: Self, value: C) -> Bool: + """Verify if a given value is present in the list. + + ```mojo + var x = InlineList[Int](1,2,3) + if 3 in x: print("x contains 3") + ``` + Parameters: + C: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + value: The value to find. + + Returns: + True if the value is contained in the list, False otherwise. + """ + + constrained[ + _type_is_eq[ElementType, C](), "value type is not self.ElementType" + ]() + for i in self: + if value == rebind[C](i[]): + return True + return False diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index af97f655e9..ba1308e9b9 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -90,7 +90,59 @@ def test_destructor(): assert_equal(destructor_counter[i], i) +def test_list_iter(): + var vs = InlineList[Int]() + vs.append(1) + vs.append(2) + vs.append(3) + + # Borrow immutably + fn sum(vs: InlineList[Int]) -> Int: + var sum = 0 + for v in vs: + sum += v[] + return sum + + assert_equal(6, sum(vs)) + + +def test_list_iter_mutable(): + var vs = InlineList[Int, 3](1, 2, 3) + + for v in vs: + v[] += 1 + + var sum = 0 + for v in vs: + sum += v[] + + assert_equal(9, sum) + + +def test_list_contains(): + var x = InlineList[Int](1, 2, 3) + assert_false(0 in x) + assert_true(x.__contains__(1)) + assert_false(x.__contains__(4)) + + +def test_list_variadic_constructor(): + var l = InlineList[Int](2, 4, 6) + assert_equal(3, len(l)) + assert_equal(2, l[0]) + assert_equal(4, l[1]) + assert_equal(6, l[2]) + + l.append(8) + assert_equal(4, len(l)) + assert_equal(8, l[3]) + + def main(): test_list() test_append_triggers_a_move() test_destructor() + test_list_iter() + test_list_iter_mutable() + test_list_contains() + test_list_variadic_constructor() From f0dcc756dab3e4da30809563b799261784340cf6 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 17 May 2024 16:26:02 -0500 Subject: [PATCH 0614/2019] [External] [stdlib] Add method `atof()` to `String` (#40180) [External] [stdlib] Add method `atof()` to `String` This PR adds a function that can convert a `String` to a `Float64`. Right now it is implemented just for Float64 but maybe we should add other precisions? This supports the following notations: ```python "-1236.233" "2.25" "2." "1.7E+3" # as well as the f/F postfix notation "-1236.233f" "2.25F" "2.f" "1.7E+3F" ``` ORIGINAL_AUTHOR=Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2649 Co-authored-by: Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> Closes modularml/mojo#2649 MODULAR_ORIG_COMMIT_REV_ID: b8d2c4ef38faa639e749957a0c1ba1a9c02a28cf --- docs/changelog.md | 3 + stdlib/src/builtin/string.mojo | 124 +++++++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 61 +++++++++++++ 3 files changed, 188 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 368e74b1c1..c7ed2204ac 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -308,6 +308,9 @@ what we publish. string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) by [@bgreni](https://github.com/bgreni)) +- Added `atof()` function which can convert a `String` to a `float64`. + ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 30eae027f7..723e09fe2c 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -372,6 +372,130 @@ fn atol(str: String, base: Int = 10) raises -> Int: return _atol(str._strref_dangerous(), base) +fn _atof_error(str_ref: StringRef) -> Error: + return Error("String is not convertible to float: '" + str(str_ref) + "'") + + +@always_inline +fn _atof(str_ref: StringRef) raises -> Float64: + """Parses the given string as a floating point and returns that value. + + For example, `atof("2.25")` returns `2.25`. If the given string cannot be parsed + as an float value, an error is raised. For example, `atof("hi")` raises an + error. + + Args: + str_ref: A string to be parsed as a floating point. + + Returns: + An float value that represents the string, or otherwise raises. + """ + if not str_ref: + raise Error(_atof_error(str_ref)) + + var result: Float64 = 0.0 + var exponent: Int = 0 + var sign: Int = 1 + + alias ord_0 = UInt8(ord("0")) + alias ord_9 = UInt8(ord("9")) + alias ord_dot = UInt8(ord(".")) + alias ord_plus = UInt8(ord("+")) + alias ord_minus = UInt8(ord("-")) + alias ord_f = UInt8(ord("f")) + alias ord_F = UInt8(ord("F")) + alias ord_e = UInt8(ord("e")) + alias ord_E = UInt8(ord("E")) + + var start: Int = 0 + var str_ref_strip = str_ref.strip() + var str_len = len(str_ref_strip) + var buff = str_ref_strip.unsafe_ptr() + + # check sign, inf, nan + if buff[start] == ord_plus: + start += 1 + elif buff[start] == ord_minus: + start += 1 + sign = -1 + if (str_len - start) >= 3: + if StringRef(buff + start, 3) == "nan": + return FloatLiteral.nan + if StringRef(buff + start, 3) == "inf": + return FloatLiteral.infinity * sign + # read before dot + for pos in range(start, str_len): + if ord_0 <= buff[pos] <= ord_9: + result = result * 10.0 + int(buff[pos] - ord_0) + start += 1 + else: + break + # if dot -> read after dot + if buff[start] == ord_dot: + start += 1 + for pos in range(start, str_len): + if ord_0 <= buff[pos] <= ord_9: + result = result * 10.0 + int(buff[pos] - ord_0) + exponent -= 1 + else: + break + start += 1 + # if e/E -> read scientific notation + if buff[start] == ord_e or buff[start] == ord_E: + start += 1 + var sign: Int = 1 + var shift: Int = 0 + var has_number: Bool = False + for pos in range(start, str_len): + if buff[start] == ord_plus: + pass + elif buff[pos] == ord_minus: + sign = -1 + elif ord_0 <= buff[start] <= ord_9: + has_number = True + shift = shift * 10 + int(buff[pos] - ord_0) + else: + break + start += 1 + exponent += sign * shift + if not has_number: + raise _atof_error(str_ref) + # check for f/F at the end + if buff[start] == ord_f or buff[start] == ord_F: + start += 1 + # check if string got fully parsed + if start != str_len: + raise _atof_error(str_ref) + # apply shift + # NOTE: Instead of `var result *= 10.0 ** exponent`, we calculate a positive + # integer factor as shift and multiply or divide by it based on the shift + # direction. This allows for better precision. + # TODO: investigate if there is a floating point arithmethic problem. + var shift: Int = 10 ** abs(exponent) + if exponent > 0: + result *= shift + if exponent < 0: + result /= shift + # apply sign + return result * sign + + +fn atof(str: String) raises -> Float64: + """Parses the given string as a floating point and returns that value. + + For example, `atof("2.25")` returns `2.25`. If the given string cannot be parsed + as an floating point value, an error is raised. For example, `atof("hi")` raises an + error. + + Args: + str: A string to be parsed as a floating point. + + Returns: + An floating point value that represents the string, or otherwise raises. + """ + return _atof(str._strref_dangerous()) + + # ===----------------------------------------------------------------------===# # isdigit # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 785c5d6243..54a2c841ad 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -412,6 +412,66 @@ fn test_atol_base_0() raises: _ = atol("0of_", base=0) +fn test_atof() raises: + assert_equal(375.0, atof(String("375.f"))) + assert_equal(1.0, atof(String("001."))) + assert_equal(+5.0, atof(String(" +005."))) + assert_equal(13.0, atof(String(" 013.f "))) + assert_equal(-89, atof(String("-89"))) + assert_equal(-0.3, atof(String(" -0.3"))) + assert_equal(-69e3, atof(String(" -69E+3 "))) + assert_equal(123.2e1, atof(String(" 123.2E1 "))) + assert_equal(23e3, atof(String(" 23E3 "))) + assert_equal(989343e-13, atof(String(" 989343E-13 "))) + assert_equal(1.123, atof(String(" 1.123f"))) + assert_equal(0.78, atof(String(" .78 "))) + assert_equal(121234.0, atof(String(" 121234. "))) + assert_equal(985031234.0, atof(String(" 985031234.F "))) + assert_equal(FloatLiteral.negative_zero, atof(String("-0"))) + assert_equal(FloatLiteral.nan, atof(String(" nan"))) + assert_equal(FloatLiteral.infinity, atof(String(" inf "))) + assert_equal(FloatLiteral.negative_infinity, atof(String("-inf "))) + + # Negative cases + with assert_raises(contains="String is not convertible to float: ''"): + _ = atof(String("")) + + with assert_raises( + contains="String is not convertible to float: ' 123 asd'" + ): + _ = atof(String(" 123 asd")) + + with assert_raises( + contains="String is not convertible to float: ' f.9123 '" + ): + _ = atof(String(" f.9123 ")) + + with assert_raises( + contains="String is not convertible to float: ' 989343E-1A3 '" + ): + _ = atof(String(" 989343E-1A3 ")) + + with assert_raises( + contains="String is not convertible to float: ' 124124124_2134124124 '" + ): + _ = atof(String(" 124124124_2134124124 ")) + + with assert_raises( + contains="String is not convertible to float: ' 123.2E '" + ): + _ = atof(String(" 123.2E ")) + + with assert_raises( + contains="String is not convertible to float: ' --958.23 '" + ): + _ = atof(String(" --958.23 ")) + + with assert_raises( + contains="String is not convertible to float: ' ++94. '" + ): + _ = atof(String(" ++94. ")) + + fn test_calc_initial_buffer_size_int32() raises: assert_equal(1, _calc_initial_buffer_size_int32(0)) assert_equal(1, _calc_initial_buffer_size_int32(9)) @@ -856,6 +916,7 @@ def main(): test_string_indexing() test_atol() test_atol_base_0() + test_atof() test_calc_initial_buffer_size_int32() test_calc_initial_buffer_size_int64() test_contains() From b6d9155f37a87131fe8b853b28022f514acff2fd Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 17 May 2024 16:35:23 -0700 Subject: [PATCH 0615/2019] [stdlib] Converting the underlying value type of the std.string class to use UInt8. Converting the standard String type to use `UInt8` as the underlying char type. As you would expect, this required several areas of code within the standard library to change as they implicitly depended on the existing underlying String character type. This change introduces a `String.value_type` type alias for dependent code to use. MODULAR_ORIG_COMMIT_REV_ID: 22e869eec5343471427c777cbedd1f79f594a7d6 --- stdlib/src/base64/base64.mojo | 17 ++- stdlib/src/builtin/file.mojo | 6 +- stdlib/src/builtin/hex.mojo | 2 +- stdlib/src/builtin/int.mojo | 4 +- stdlib/src/builtin/io.mojo | 6 +- stdlib/src/builtin/simd.mojo | 5 +- stdlib/src/builtin/string.mojo | 149 ++++++++----------------- stdlib/src/builtin/string_literal.mojo | 37 ++++-- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/sys/info.mojo | 2 +- stdlib/src/utils/inlined_string.mojo | 15 +-- stdlib/src/utils/stringref.mojo | 19 ---- stdlib/test/builtin/test_file.mojo | 12 +- stdlib/test/builtin/test_hash.mojo | 6 +- stdlib/test/builtin/test_string.mojo | 2 +- stdlib/test/collections/test_list.mojo | 26 ++--- 16 files changed, 126 insertions(+), 184 deletions(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 3bf20f904c..337d0666cc 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -70,10 +70,10 @@ fn b64encode(str: String) -> String: Base64 encoding of the input string. """ alias lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - var b64chars = lookup.unsafe_ptr() + var b64chars = lookup.as_uint8_ptr() var length = len(str) - var out = List[Int8](capacity=length + 1) + var out = String._buffer_type(capacity=length + 1) @parameter @always_inline @@ -125,7 +125,7 @@ fn b64decode(str: String) -> String: var n = len(str) debug_assert(n % 4 == 0, "Input length must be divisible by 4") - var p = List[Int8](capacity=n + 1) + var p = String._buffer_type(capacity=n + 1) # This algorithm is based on https://arxiv.org/abs/1704.00605 for i in range(0, n, 4): @@ -169,16 +169,15 @@ fn b16encode(str: String) -> String: Base16 encoding of the input string. """ alias lookup = "0123456789ABCDEF" - var b16chars = lookup.unsafe_ptr() + var b16chars = lookup.as_uint8_ptr() var length = len(str) - var out = List[Int8](capacity=length * 2 + 1) + var out = List[UInt8](capacity=length * 2 + 1) @parameter @always_inline - fn str_bytes(idx: Int) -> Int: - # TODO: Remove cast once transition to UInt8 string types is complete. - return int(str.unsafe_ptr().bitcast[UInt8]()[idx]) + fn str_bytes(idx: UInt8) -> UInt8: + return str._buffer[int(idx)] for i in range(length): var str_byte = str_bytes(i) @@ -226,7 +225,7 @@ fn b16decode(str: String) -> String: var n = len(str) debug_assert(n % 2 == 0, "Input length must be divisible by 2") - var p = List[Int8](capacity=int(n / 2) + 1) + var p = List[UInt8](capacity=int(n / 2) + 1) for i in range(0, n, 2): var hi = str[i] diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 65d395a6f0..8bbfe52ea3 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -282,7 +282,7 @@ struct FileHandle: raise (err_msg^).consume_as_error() return size_copy - fn read_bytes(self, size: Int64 = -1) raises -> List[Int8]: + fn read_bytes(self, size: Int64 = -1) raises -> List[UInt8]: """Reads data from a file and sets the file handle seek position. If size is left as default of -1, it will read to the end of the file. Setting size to a number larger than what's in the file will be handled @@ -337,7 +337,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() var buf = external_call[ - "KGEN_CompilerRT_IO_FileReadBytes", UnsafePointer[Int8] + "KGEN_CompilerRT_IO_FileReadBytes", UnsafePointer[UInt8] ]( self.handle, UnsafePointer.address_of(size_copy), @@ -347,7 +347,7 @@ struct FileHandle: if err_msg: raise (err_msg^).consume_as_error() - var list = List[Int8]( + var list = List[UInt8]( unsafe_pointer=buf, size=int(size_copy), capacity=int(size_copy) ) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 4e450d93a8..f5f771d7f2 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -197,7 +197,7 @@ fn _try_write_int( # bytes from our final `buf_ptr` to the end of the buffer. var len = CAPACITY - offset - var strref = StringRef(rebind[UnsafePointer[Int8]](buf_ptr), len) + var strref = StringRef(buf_ptr, len) fmt.write_str(strref) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 841050dfe6..a3bb456068 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -377,11 +377,11 @@ struct Int( # Stack allocate enough bytes to store any formatted 64-bit integer alias size: Int = 32 - var buf = InlineArray[Int8, size](unsafe_uninitialized=True) + var buf = InlineArray[UInt8, size](fill=0) # Format the integer to the local byte array var len = _snprintf( - rebind[UnsafePointer[Int8]](buf.unsafe_ptr()), + buf.unsafe_ptr(), size, "%li", self.value, diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 2d670545d9..3f7f151984 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -136,7 +136,7 @@ fn _printf[ fn _snprintf[ *types: AnyType ]( - str: UnsafePointer[Int8], + str: UnsafePointer[UInt8], size: Int, fmt: StringLiteral, *arguments: *types, @@ -179,7 +179,7 @@ fn _snprintf[ @no_inline fn _snprintf_scalar[ type: DType -](buffer: UnsafePointer[Int8], size: Int, x: Scalar[type],) -> Int: +](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type],) -> Int: alias format = _get_dtype_printf_format[type]() @parameter @@ -206,7 +206,7 @@ fn _snprintf_scalar[ @no_inline -fn _float_repr(buffer: UnsafePointer[Int8], size: Int, x: Float64) -> Int: +fn _float_repr(buffer: UnsafePointer[UInt8], size: Int, x: Float64) -> Int: # Using `%.17g` with decimal check is equivalent to CPython's fallback path # when its more complex dtoa library (forked from # https://github.com/dtolnay/dtoa) is not available. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d7ac0838de..4f6002f51d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -438,7 +438,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( The length of the SIMD vector. """ - return size + return self.size @always_inline("nodebug") fn __bool__(self) -> Bool: @@ -2799,7 +2799,8 @@ fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): # type. alias size: Int = _calc_format_buffer_size[dtype]() - var buf = InlineArray[Int8, size](unsafe_uninitialized=True) + var buf = InlineArray[UInt8, size](fill=0) + var buf_ptr = buf.unsafe_ptr() var wrote = _snprintf_scalar[dtype]( diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 723e09fe2c..b55554ccf7 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -59,7 +59,7 @@ fn ord(s: String) -> Int: var shift = int((6 * (num_bytes - 1))) var b1_mask = 0b11111111 >> (num_bytes + 1) var result = int(b1 & b1_mask) << shift - for i in range(1, num_bytes): + for _ in range(1, num_bytes): p += 1 shift -= 6 result |= int(p[] & 0b00111111) << shift @@ -113,7 +113,7 @@ fn chr(c: Int) -> String: shift -= 6 p.store(i, ((c >> shift) & 0b00111111) | 0b10000000) p.store(num_bytes, 0) - return String(p.bitcast[DType.int8](), num_bytes + 1) + return String(p.bitcast[DType.uint8](), num_bytes + 1) # ===----------------------------------------------------------------------===# @@ -122,7 +122,7 @@ fn chr(c: Int) -> String: @always_inline("nodebug") -fn _chr_ascii(c: Int8) -> String: +fn _chr_ascii(c: UInt8) -> String: """Returns a string based on the given ASCII code point. Args: @@ -135,7 +135,7 @@ fn _chr_ascii(c: Int8) -> String: @always_inline("nodebug") -fn _repr_ascii(c: Int8) -> String: +fn _repr_ascii(c: UInt8) -> String: """Returns a printable representation of the given ASCII code point. Args: @@ -501,7 +501,7 @@ fn atof(str: String) raises -> Float64: # ===----------------------------------------------------------------------===# -fn isdigit(c: Int8) -> Bool: +fn isdigit(c: UInt8) -> Bool: """Determines whether the given character is a digit [0-9]. Args: @@ -520,7 +520,7 @@ fn isdigit(c: Int8) -> Bool: # ===----------------------------------------------------------------------===# -fn isupper(c: Int8) -> Bool: +fn isupper(c: UInt8) -> Bool: """Determines whether the given character is an uppercase character. This currently only respects the default "C" locale, i.e. returns True only if the character specified is one of ABCDEFGHIJKLMNOPQRSTUVWXYZ. @@ -534,7 +534,7 @@ fn isupper(c: Int8) -> Bool: return _is_ascii_uppercase(c) -fn _is_ascii_uppercase(c: Int8) -> Bool: +fn _is_ascii_uppercase(c: UInt8) -> Bool: alias ord_a = ord("A") alias ord_z = ord("Z") return ord_a <= int(c) <= ord_z @@ -545,7 +545,7 @@ fn _is_ascii_uppercase(c: Int8) -> Bool: # ===----------------------------------------------------------------------===# -fn islower(c: Int8) -> Bool: +fn islower(c: UInt8) -> Bool: """Determines whether the given character is an lowercase character. This currently only respects the default "C" locale, i.e. returns True only if the character specified is one of abcdefghijklmnopqrstuvwxyz. @@ -559,7 +559,7 @@ fn islower(c: Int8) -> Bool: return _is_ascii_lowercase(c) -fn _is_ascii_lowercase(c: Int8) -> Bool: +fn _is_ascii_lowercase(c: UInt8) -> Bool: alias ord_a = ord("a") alias ord_z = ord("z") return ord_a <= int(c) <= ord_z @@ -570,8 +570,7 @@ fn _is_ascii_lowercase(c: Int8) -> Bool: # ===----------------------------------------------------------------------===# -# TODO(MSTDL-160): Make this take a Unicode codepoint type -fn isspace(c: Int) -> Bool: +fn isspace(c: UInt8) -> Bool: """Determines whether the given character is a whitespace character. This currently only respects the default "C" locale, i.e. returns True only if the character specified is one of @@ -596,7 +595,7 @@ fn isspace(c: Int) -> Bool: # ===----------------------------------------------------------------------===# -fn isprintable(c: Int8) -> Bool: +fn isprintable(c: UInt8) -> Bool: """Determines whether the given character is a printable character. Args: @@ -625,7 +624,7 @@ struct String( ): """Represents a mutable string.""" - alias _buffer_type = List[Int8] + alias _buffer_type = List[UInt8] var _buffer: Self._buffer_type """The underlying storage for the string.""" @@ -671,35 +670,6 @@ struct String( # Initializers # ===------------------------------------------------------------------===# - # TODO: Remove this method when #2317 is done - @always_inline - fn __init__(inout self, owned impl: Self._buffer_type): - """Construct a string from a buffer of bytes. - - The buffer must be terminated with a null byte: - - ```mojo - var buf = List[Int8]() - buf.append(ord('H')) - buf.append(ord('i')) - buf.append(0) - var hi = String(buf) - ``` - - Note that you should use the constructor from `List[UInt8]` instead - as we are now storing the bytes as UInt8. - - See https://github.com/modularml/mojo/issues/2317 for more information. - - Args: - impl: The buffer. - """ - debug_assert( - impl[-1] == 0, - "expected last element of String buffer to be null terminator", - ) - self._buffer = impl^ - @always_inline fn __init__(inout self, owned impl: List[UInt8]): """Construct a string from a buffer of bytes. @@ -724,8 +694,8 @@ struct String( # we store the length and capacity beforehand as `steal_data()` will invalidated `impl` var length = len(impl) var capacity = impl.capacity - self._buffer = List[Int8]( - unsafe_pointer=impl.steal_data().bitcast[Int8](), + self._buffer = List[UInt8]( + unsafe_pointer=impl.steal_data().bitcast[UInt8](), size=length, capacity=capacity, ) @@ -768,13 +738,12 @@ struct String( """ # Calculate length in bytes - var length = len(str_slice.as_bytes_slice()) + var length: Int = len(str_slice.as_bytes_slice()) var buffer = Self._buffer_type() buffer.resize(length + 1, 0) memcpy( dest=buffer.data, - # TODO: Remove cast after transition to UInt8 strings is complete. - src=str_slice.as_bytes_slice().unsafe_ptr().bitcast[Int8](), + src=str_slice.as_bytes_slice().unsafe_ptr(), count=length, ) buffer[length] = 0 @@ -801,29 +770,6 @@ struct String( self = str(value) - # TODO: Remove this method when #2317 is done - @always_inline - fn __init__(inout self, ptr: UnsafePointer[Int8], len: Int): - """Creates a string from the buffer. Note that the string now owns - the buffer. - - The buffer must be terminated with a null byte. - - Note that you should use the constructor from `UnsafePointer[UInt8]` instead - as we are now storing the bytes as UInt8. - - See https://github.com/modularml/mojo/issues/2317 for more information. - - Args: - ptr: The pointer to the buffer. - len: The length of the buffer, including the null terminator. - """ - # we don't know the capacity of ptr, but we'll assume it's the same or - # larger than len - self._buffer = Self._buffer_type( - unsafe_pointer=ptr, size=len, capacity=len - ) - @always_inline fn __init__(inout self, ptr: UnsafePointer[UInt8], len: Int): """Creates a string from the buffer. Note that the string now owns @@ -838,11 +784,11 @@ struct String( # we don't know the capacity of ptr, but we'll assume it's the same or # larger than len self._buffer = Self._buffer_type( - unsafe_pointer=ptr.bitcast[Int8](), size=len, capacity=len + unsafe_pointer=ptr.bitcast[UInt8](), size=len, capacity=len ) @always_inline - fn __init__(inout self, ptr: LegacyPointer[Int8], len: Int): + fn __init__(inout self, ptr: LegacyPointer[UInt8], len: Int): """Creates a string from the buffer. Note that the string now owns the buffer. @@ -853,11 +799,11 @@ struct String( len: The length of the buffer, including the null terminator. """ self._buffer = Self._buffer_type() - self._buffer.data = rebind[UnsafePointer[Int8]](ptr) + self._buffer.data = UnsafePointer(ptr.address) self._buffer.size = len @always_inline - fn __init__(inout self, ptr: DTypePointer[DType.int8], len: Int): + fn __init__(inout self, ptr: DTypePointer[DType.uint8], len: Int): """Creates a string from the buffer. Note that the string now owns the buffer. @@ -889,7 +835,7 @@ struct String( @staticmethod @always_inline - fn _from_bytes(owned buff: DTypePointer[DType.int8]) -> String: + fn _from_bytes(owned buff: DTypePointer[DType.uint8]) -> String: """Construct a string from a sequence of bytes. This does no validation that the given bytes are valid in any specific @@ -990,7 +936,7 @@ struct String( var buffer = Self._buffer_type() var adjusted_span_len = len(adjusted_span) buffer.resize(adjusted_span_len + 1, 0) - var ptr = self.unsafe_ptr() + var ptr = self.unsafe_uint8_ptr() for i in range(adjusted_span_len): buffer[i] = ptr[adjusted_span[i]] buffer[adjusted_span_len] = 0 @@ -1102,14 +1048,14 @@ struct String( var buffer = Self._buffer_type() buffer.resize(total_len + 1, 0) memcpy( - dest=buffer.data, - src=self.unsafe_ptr(), - count=self_len, + DTypePointer(buffer.data), + self.unsafe_uint8_ptr(), + self_len, ) memcpy( - dest=buffer.data + self_len, - src=other.unsafe_ptr(), - count=other_len + 1, # Also copy the terminator + DTypePointer(buffer.data + self_len), + other.unsafe_uint8_ptr(), + other_len + 1, # Also copy the terminator ) return Self(buffer^) @@ -1291,7 +1237,7 @@ struct String( Returns: The pointer to the underlying memory. """ - return self._buffer.data + return self._buffer.data.bitcast[Int8]() fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. @@ -1301,7 +1247,7 @@ struct String( """ return self._buffer.data.bitcast[UInt8]() - fn as_bytes(self) -> List[Int8]: + fn as_bytes(self) -> List[UInt8]: """Retrieves the underlying byte sequence encoding the characters in this string. @@ -1335,8 +1281,7 @@ struct String( """ return Span[UInt8, self.is_mutable, self.lifetime]( - # TODO: Remove cast after transition to UInt8 strings is complete. - unsafe_ptr=self[]._buffer.unsafe_ptr().bitcast[UInt8](), + unsafe_ptr=self[]._buffer.unsafe_ptr(), # Does NOT include the NUL terminator. len=self[]._byte_length(), ) @@ -1355,7 +1300,9 @@ struct String( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in String so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=bytes) + return StringSlice[self.is_mutable, self.lifetime]( + unsafe_from_utf8=bytes + ) fn _byte_length(self) -> Int: """Get the string length in bytes. @@ -1381,7 +1328,7 @@ struct String( The pointer to the underlying memory. """ var ptr = self.unsafe_ptr() - self._buffer.data = UnsafePointer[Int8]() + self._buffer.data = UnsafePointer[UInt8]() self._buffer.size = 0 self._buffer.capacity = 0 return ptr @@ -1509,15 +1456,15 @@ struct String( if occurrences == -1: return self - var self_start = self.unsafe_ptr() - var self_ptr = self.unsafe_ptr() - var new_ptr = new.unsafe_ptr() + var self_start = self.unsafe_uint8_ptr() + var self_ptr = self.unsafe_uint8_ptr() + var new_ptr = new.unsafe_uint8_ptr() var self_len = len(self) var old_len = len(old) var new_len = len(new) - var res = List[Int8]() + var res = List[UInt8]() res.reserve(self_len + (old_len - new_len) * occurrences + 1) for _ in range(occurrences): @@ -1603,9 +1550,9 @@ struct String( return hash(self._strref_dangerous()) fn _interleave(self, val: String) -> String: - var res = List[Int8]() - var val_ptr = val.unsafe_ptr() - var self_ptr = self.unsafe_ptr() + var res = List[UInt8]() + var val_ptr = val.unsafe_uint8_ptr() + var self_ptr = self.unsafe_uint8_ptr() res.reserve(len(val) * len(self) + 1) for i in range(len(self)): for j in range(len(val)): @@ -1641,13 +1588,13 @@ struct String( return self._toggle_ascii_case[_is_ascii_lowercase]() @always_inline - fn _toggle_ascii_case[check_case: fn (Int8) -> Bool](self) -> String: + fn _toggle_ascii_case[check_case: fn (UInt8) -> Bool](self) -> String: var copy: String = self - var char_ptr = copy.unsafe_ptr() + var char_ptr = copy.unsafe_uint8_ptr() for i in range(len(self)): - var char: Int8 = char_ptr[i] + var char: UInt8 = char_ptr[i] if check_case(char): var lower = _toggle_ascii_case(char) char_ptr[i] = lower @@ -1762,7 +1709,7 @@ struct String( for i in range(n): memcpy( dest=buf.data + len_self * i, - src=self.unsafe_ptr(), + src=self.unsafe_uint8_ptr(), count=len_self, ) return String(buf^) @@ -1777,7 +1724,7 @@ struct String( fn _vec_fmt[ *types: AnyType ]( - str: UnsafePointer[Int8], + str: UnsafePointer[UInt8], size: Int, fmt: StringLiteral, *arguments: *types, @@ -1785,7 +1732,7 @@ fn _vec_fmt[ return _snprintf(str, size, fmt, arguments) -fn _toggle_ascii_case(char: Int8) -> Int8: +fn _toggle_ascii_case(char: UInt8) -> UInt8: """Assuming char is a cased ASCII character, this function will return the opposite-cased letter """ diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 84983a9b3f..021b49e24b 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -94,6 +94,24 @@ struct StringLiteral( return UnsafePointer[Int8]._from_dtype_ptr(ptr) + @always_inline("nodebug") + fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: + """Get raw pointer to the underlying data. + + Returns: + The raw pointer to the data. + """ + return self.unsafe_ptr().bitcast[UInt8]() + + @always_inline("nodebug") + fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + """Get raw pointer to the underlying data. + + Returns: + The raw pointer to the data. + """ + return self.unsafe_ptr().bitcast[UInt8]() + @always_inline("nodebug") fn __bool__(self) -> Bool: """Convert the string to a bool value. @@ -215,7 +233,7 @@ struct StringLiteral( var data: DTypePointer[DType.uint8] = DTypePointer[DType.uint8]( uint8Ptr ) - memcpy(rebind[DTypePointer[DType.uint8]](buffer.data), data, length) + memcpy(DTypePointer(buffer.data), data, length) initialize_pointee_move(buffer.data + length, 0) string._buffer = buffer^ return string @@ -231,22 +249,26 @@ struct StringLiteral( return self.__str__().__repr__() @always_inline - fn as_string_slice(self) -> StringSlice[False, ImmStaticLifetime]: + fn as_string_slice( + self: Reference[Self, _, _] + ) -> StringSlice[False, ImmStaticLifetime]: """Returns a string slice of this static string literal. Returns: A string slice pointing to this static string literal. """ - var bytes = self.as_bytes_slice() + var bytes = self[].as_bytes_slice() # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=bytes) + return StringSlice[False, ImmStaticLifetime](unsafe_from_utf8=bytes) @always_inline - fn as_bytes_slice(self) -> Span[UInt8, False, ImmStaticLifetime]: + fn as_bytes_slice( + self: Reference[Self, _, _] + ) -> Span[UInt8, False, ImmStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -254,12 +276,11 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - # TODO: Remove cast after transition to UInt8 strings is complete. - var ptr = self.unsafe_ptr().bitcast[UInt8]() + var ptr = self[].unsafe_uint8_ptr() return Span[UInt8, False, ImmStaticLifetime]( unsafe_ptr=ptr, - len=self._byte_length(), + len=self[]._byte_length(), ) fn format_to(self, inout writer: Formatter): diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 1e27dd0629..470d4bb869 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -244,7 +244,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): with open(self, "r") as f: return f.read() - fn read_bytes(self) raises -> List[Int8]: + fn read_bytes(self) raises -> List[UInt8]: """Returns content of the file as bytes. Returns: diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 9a5ef63156..69fddd2d6f 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -712,7 +712,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: alias INITIAL_CAPACITY = 32 - var buf = List[Int8](capacity=INITIAL_CAPACITY) + var buf = List[UInt8](capacity=INITIAL_CAPACITY) var buf_len = Int(INITIAL_CAPACITY) var err = external_call["sysctlbyname", Int32]( diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index ed57fcabf6..8edf536e1f 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -231,9 +231,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): return res - # TODO: Remove this when we have transitioned to uint8 for bytes. - # See https://github.com/modularml/mojo/issues/2317 for details - fn unsafe_ptr(self) -> UnsafePointer[UInt8]: + fn as_uint8_ptr(self) -> UnsafePointer[UInt8]: """Returns a pointer to the bytes of string data. Returns: @@ -252,7 +250,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.unsafe_ptr(), length: len(self)} + return StringRef {data: self.as_uint8_ptr(), length: len(self)} fn _strref_keepalive(self): """ @@ -310,15 +308,10 @@ struct _FixedString[CAP: Int]( + ")" ) - self.buffer = InlineArray[UInt8, CAP](unsafe_uninitialized=True) + self.buffer = InlineArray[UInt8, CAP]() self.size = len(literal) - memcpy( - dest=self.buffer.unsafe_ptr(), - # TODO: Remove bitcast after string transition to UInt8 is complete. - src=literal.unsafe_ptr().bitcast[UInt8](), - count=len(literal), - ) + memcpy(self.buffer.unsafe_ptr(), literal.as_uint8_ptr(), len(literal)) # ===------------------------------------------------------------------=== # # Trait Interfaces diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 9a83f5b2b8..3e2b93170b 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -117,25 +117,6 @@ struct StringRef( return Self {data: unsafe_ptr, length: len} - # TODO: #2317 Drop support for this constructor when we have fully - # transitioned to UInt8 as the main byte type. - @always_inline - fn __init__(ptr: UnsafePointer[Int8]) -> StringRef: - """Construct a StringRef value given a null-terminated string. - - Note that you should use the constructor from `UnsafePointer[UInt8]` instead - as we are now storing the bytes as UInt8. - See https://github.com/modularml/mojo/issues/2317 for more information. - - Args: - ptr: UnsafePointer to the string. - - Returns: - Constructed `StringRef` object. - """ - - return DTypePointer[DType.int8](ptr) - @always_inline fn __init__(ptr: UnsafePointer[UInt8]) -> StringRef: """Construct a StringRef value given a null-terminated string. diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index c2bbe2cf67..572283bf86 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -178,14 +178,14 @@ def test_file_write_again(): @value @register_passable struct Word: - var first_letter: Int8 - var second_letter: Int8 - var third_letter: Int8 - var fourth_letter: Int8 - var fith_letter: Int8 + var first_letter: UInt8 + var second_letter: UInt8 + var third_letter: UInt8 + var fourth_letter: UInt8 + var fith_letter: UInt8 fn __str__(self) -> String: - var word = List[Int8](capacity=6) + var word = List[UInt8](capacity=6) word.append(self.first_letter) word.append(self.second_letter) word.append(self.third_letter) diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index 3653ef3292..e69604df9b 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -22,7 +22,7 @@ from builtin.hash import _hash_simd from testing import assert_equal, assert_not_equal, assert_true -def same_low_bits(i1: Int, i2: Int, bits: Int = 5) -> Int: +def same_low_bits(i1: Int, i2: Int, bits: Int = 5) -> UInt8: var mask = (1 << bits) - 1 return int(not (i1 ^ i2) & mask) @@ -35,7 +35,7 @@ def test_hash_byte_array(): assert_equal(hash("d".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1)) # Test that low bits are different - var num_same = 0 + var num_same: UInt8 = 0 num_same += same_low_bits( hash("a".unsafe_ptr(), 1), hash("b".unsafe_ptr(), 1) ) @@ -78,7 +78,7 @@ def _test_hash_int_simd[type: DType](bits: Int = 4, max_num_same: Int = 2): assert_equal(_hash_simd(d), _hash_simd(d)) # Test that low bits are different - var num_same = 0 + var num_same: UInt8 = 0 num_same += same_low_bits(_hash_simd(a), _hash_simd(b), bits) num_same += same_low_bits(_hash_simd(a), _hash_simd(c), bits) num_same += same_low_bits(_hash_simd(a), _hash_simd(d), bits) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 54a2c841ad..9599e23ee7 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -81,7 +81,7 @@ fn test_constructors() raises: assert_equal(3, len(s2)) # Construction from UnsafePointer - var ptr = UnsafePointer[Int8].alloc(4) + var ptr = UnsafePointer[UInt8].alloc(4) ptr[0] = ord("a") ptr[1] = ord("b") ptr[2] = ord("c") diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 873803f122..8361457791 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -386,47 +386,47 @@ def test_list_index(): assert_equal(test_list_a.index(30), 2) assert_equal(test_list_a.index(50), 4) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(60) + _ = test_list_a.index(60) # Tests With Start Parameter assert_equal(test_list_a.index(30, start=1), 2) assert_equal(test_list_a.index(30, start=-4), 2) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(30, start=3) + _ = test_list_a.index(30, start=3) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(30, start=5) + _ = test_list_a.index(30, start=5) # Tests With Start and End Parameters assert_equal(test_list_a.index(30, start=1, stop=3), 2) assert_equal(test_list_a.index(30, start=-4, stop=-2), 2) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(30, start=1, stop=2) + _ = test_list_a.index(30, start=1, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(30, start=3, stop=1) + _ = test_list_a.index(30, start=3, stop=1) # Tests With End Parameter Only assert_equal(test_list_a.index(30, stop=3), 2) assert_equal(test_list_a.index(30, stop=-2), 2) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(30, stop=1) + _ = test_list_a.index(30, stop=1) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(30, stop=2) + _ = test_list_a.index(30, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(60, stop=50) + _ = test_list_a.index(60, stop=50) # Edge Cases and Special Conditions assert_equal(test_list_a.index(10, start=-5, stop=-1), 0) assert_equal(test_list_a.index(10, start=0, stop=50), 0) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(50, start=-5, stop=-1) + _ = test_list_a.index(50, start=-5, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(50, start=0, stop=-1) + _ = test_list_a.index(50, start=0, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(10, start=-4, stop=-1) + _ = test_list_a.index(10, start=-4, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - test_list_a.index(10, start=5, stop=50) + _ = test_list_a.index(10, start=5, stop=50) with assert_raises(contains="ValueError: Given element is not in list"): - List[Int]().index(10) + _ = List[Int]().index(10) var test_list_b = List[Int](10, 20, 30, 20, 10) From 0e3715474266cdcb3106f3df71fbbb9ad76f629c Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Fri, 17 May 2024 16:37:39 -0700 Subject: [PATCH 0616/2019] [stdlib] Re-enable test_string::test_isspace This test can be re-enabled now that an upstream llvm fix went in. MODULAR_ORIG_COMMIT_REV_ID: b50d4620388f91661f5d4f0c2eeff9bdedb90afa --- stdlib/test/builtin/test_string.mojo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 9599e23ee7..af6661e38a 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -929,9 +929,7 @@ def main(): test_islower() test_lower() test_upper() - # TODO(37393): Re-enable once we debug why we are depending on some debug behavior - # on graviton. Showing an error in our O3 LLVM pipeline; could be a bug in LLVM. - # test_isspace() + test_isspace() test_ascii_aliases() test_rstrip() test_lstrip() From 500fbdf02ab3b8c7f4355d7a9096a33edf47e61f Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Fri, 17 May 2024 18:50:40 -0500 Subject: [PATCH 0617/2019] [External] [stdlib][NFC] Improve tests for utils.numerics (#40201) [External] [stdlib][NFC] Improve tests for utils.numerics ## Changes - [X] Alpha order on `test_simd.mojo` to easier overview - [X] Tests for `get_accum_type()`, `mex_or_inf()`, `min_finite()`, `min_or_neg_inf()`, `nextafter()` and `neg_inf()` ## Ref See https://github.com/modularml/mojo/issues/2680 Co-authored-by: Manuel Saelices Closes modularml/mojo#2727 MODULAR_ORIG_COMMIT_REV_ID: cc66c1163ae663010cac482518b57fbedb0aa636 --- stdlib/test/builtin/test_simd.mojo | 48 +++++++------- stdlib/test/utils/test_numerics.mojo | 97 +++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 26 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index bf04d29d75..ee9885609e 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1017,38 +1017,38 @@ def test_indexer(): def main(): + test_abs() + test_add() + test_add_with_overflow() + test_address() test_cast() - test_simd_variadic() + test_ceil() test_convert_simd_to_string() + test_deinterleave() + test_div() + test_extract() + test_floor() + test_floordiv() + test_iadd() + test_indexer() + test_insert() + test_interleave() test_issue_20421() - test_truthy() + test_join() test_len() - test_add() + test_limits() + test_min_max_clamp() + test_mod() + test_mul_with_overflow() test_radd() - test_iadd() - test_ceil() - test_floor() - test_trunc() - test_round() - test_roundeven() - test_div() - test_floordiv() test_rfloordiv() - test_mod() test_rmod() test_rotate() + test_round() + test_roundeven() test_shift() test_shuffle() - test_insert() - test_join() - test_interleave() - test_deinterleave() - test_address() - test_extract() - test_limits() - test_add_with_overflow() + test_simd_variadic() test_sub_with_overflow() - test_mul_with_overflow() - test_abs() - test_min_max_clamp() - test_indexer() + test_trunc() + test_truthy() diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index ce9d9fdf42..5034233b37 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -16,14 +16,19 @@ from sys.info import has_neon from testing import assert_equal, assert_true, assert_false, assert_almost_equal from utils.numerics import ( FPUtils, + get_accum_type, inf, isfinite, isinf, isnan, max_finite, + max_or_inf, + min_finite, + min_or_neg_inf, nan, - ulp, neg_inf, + nextafter, + ulp, ) @@ -55,6 +60,20 @@ def test_FPUtils(): assert_equal(FPU64.get_mantissa(FPU64.pack(True, 6, 12)), 12) +def test_get_accum_type(): + assert_equal(get_accum_type[DType.float32](), DType.float32) + assert_equal(get_accum_type[DType.float64](), DType.float64) + assert_equal(get_accum_type[DType.bfloat16](), DType.float32) + assert_equal(get_accum_type[DType.int8](), DType.int8) + assert_equal(get_accum_type[DType.int16](), DType.int16) + assert_equal(get_accum_type[DType.int32](), DType.int32) + assert_equal(get_accum_type[DType.int64](), DType.int64) + assert_equal(get_accum_type[DType.uint8](), DType.uint8) + assert_equal(get_accum_type[DType.uint16](), DType.uint16) + assert_equal(get_accum_type[DType.uint32](), DType.uint32) + assert_equal(get_accum_type[DType.uint64](), DType.uint64) + + def test_isfinite(): assert_true(isfinite(Float32(33))) @@ -115,6 +134,74 @@ def test_isnan(): assert_true(isnan(nan[DType.float64]())) +def test_max_finite(): + assert_almost_equal(max_finite[DType.float32](), 3.4028235e38) + assert_almost_equal(max_finite[DType.float64](), 1.7976931348623157e308) + + +def test_max_or_inf(): + assert_almost_equal(max_or_inf[DType.float32](), inf[DType.float32]()) + assert_almost_equal(max_or_inf[DType.float64](), inf[DType.float64]()) + + +def test_min_finite(): + assert_almost_equal(min_finite[DType.float32](), -3.4028235e38) + assert_almost_equal(min_finite[DType.float64](), -1.7976931348623157e308) + + +def test_min_or_neg_inf(): + assert_almost_equal( + min_or_neg_inf[DType.float32](), neg_inf[DType.float32]() + ) + assert_almost_equal( + min_or_neg_inf[DType.float64](), neg_inf[DType.float64]() + ) + + +def test_neg_inf(): + assert_false(isfinite(neg_inf[DType.float32]())) + assert_false(isfinite(neg_inf[DType.float64]())) + assert_true(isinf(neg_inf[DType.float32]())) + assert_true(isinf(neg_inf[DType.float64]())) + assert_false(isnan(neg_inf[DType.float32]())) + assert_false(isnan(neg_inf[DType.float64]())) + assert_equal(-inf[DType.float32](), neg_inf[DType.float32]()) + assert_equal(-inf[DType.float64](), neg_inf[DType.float64]()) + + +def test_nextafter(): + assert_true(isnan(nextafter(nan[DType.float32](), nan[DType.float32]()))) + assert_true(isinf(nextafter(inf[DType.float32](), inf[DType.float32]()))) + assert_true(isinf(nextafter(-inf[DType.float32](), -inf[DType.float32]()))) + assert_almost_equal(nextafter(Float64(0), Float64(0)), 0) + assert_almost_equal(nextafter(Float64(0), Float64(1)), 5e-324) + assert_almost_equal(nextafter(Float64(0), Float64(-1)), -5e-324) + assert_almost_equal(nextafter(Float64(1), Float64(0)), 0.99999999999999988) + assert_almost_equal( + nextafter(Float64(-1), Float64(0)), -0.99999999999999988 + ) + assert_almost_equal( + nextafter(SIMD[DType.float64, 2](0, 1), SIMD[DType.float64, 2](0, 1)), + SIMD[DType.float64, 2](0, 1), + ) + assert_almost_equal( + nextafter(SIMD[DType.float64, 2](0, 1), SIMD[DType.float64, 2](1, 1)), + SIMD[DType.float64, 2](5e-324, 1), + ) + assert_almost_equal( + nextafter(SIMD[DType.float64, 2](0, 1), SIMD[DType.float64, 2](-1, 1)), + SIMD[DType.float64, 2](-5e-324, 1), + ) + assert_almost_equal( + nextafter(SIMD[DType.float64, 2](1, 1), SIMD[DType.float64, 2](0, 0)), + SIMD[DType.float64, 2](0.99999999999999988, 0.99999999999999988), + ) + assert_almost_equal( + nextafter(SIMD[DType.float64, 2](-1, -1), SIMD[DType.float64, 2](0, 0)), + SIMD[DType.float64, 2](-0.99999999999999988, -0.99999999999999988), + ) + + def test_ulp(): assert_true(isnan(ulp(nan[DType.float32]()))) assert_true(isinf(ulp(inf[DType.float32]()))) @@ -127,8 +214,14 @@ def test_ulp(): def main(): test_FPUtils() + test_get_accum_type() test_isfinite() test_isinf() test_isnan() - # TODO: test nextafter + test_max_finite() + test_max_or_inf() + test_min_finite() + test_min_or_neg_inf() + test_neg_inf() + test_nextafter() test_ulp() From 14fb00a2167cce8f810393276a4e3acea7407254 Mon Sep 17 00:00:00 2001 From: Helehex Date: Fri, 17 May 2024 19:08:42 -0500 Subject: [PATCH 0618/2019] [External] [stdlib] Constrain `simd.bool()` to `size == 1` (#39338) [External] [stdlib] Constrain `simd.bool()` to `size == 1` Use explicit `reduce_or()`/`reduce_and()` instead See https://github.com/modularml/mojo/pull/2412. Co-authored-by: Helehex Closes modularml/mojo#2502 MODULAR_ORIG_COMMIT_REV_ID: b0e954912430469a9a60fb73ea46a4e7edf96964 --- docs/changelog.md | 6 ++++- stdlib/src/builtin/simd.mojo | 37 +++++++++++++++++------------- stdlib/src/testing/testing.mojo | 2 +- stdlib/src/utils/numerics.mojo | 14 +++++------ stdlib/test/builtin/test_simd.mojo | 14 +++-------- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c7ed2204ac..e2a76a61e9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -335,7 +335,11 @@ what we publish. - Various functions in the `algorithm` module are now moved to be builtin-functions. This includes `sort`, `swap`, and `partition`. `swap` and `partition` will likely shuffle around as we're reworking - our builtnin `sort` function and optimizing it. + our builtin `sort` function and optimizing it. + +- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, + explicitly use `SIMD.reduce_and()` or `SIMD.reduce_or()`. + ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) - `ListLiteral` and `Tuple` now only requires that element types be `Copyable`. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 4f6002f51d..ac2146a9f2 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -442,17 +442,22 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn __bool__(self) -> Bool: - """Converts the SIMD vector into a boolean scalar value. + """Converts the SIMD scalar into a boolean value. + + Constraints: + The size of the SIMD vector must be 1. Returns: - True if all the elements in the SIMD vector are non-zero and False - otherwise. + True if the SIMD scalar is non-zero and False otherwise. """ - - @parameter - if Self.element_type == DType.bool: - return self.reduce_and() - return (self != 0).reduce_and() + constrained[ + size == 1, + ( + "The truth value of a SIMD vector with more than one element is" + " ambiguous. Use `reduce_and()` or `reduce_or()`" + ), + ]() + return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value @staticmethod @always_inline("nodebug") @@ -707,7 +712,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ constrained[type.is_numeric(), "the type must be numeric"]() - if rhs == 0: + if (rhs == 0).reduce_and(): # this should raise an exception. return 0 @@ -719,7 +724,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( elif type.is_unsigned(): return div else: - if self > 0 and rhs > 0: + if ((self > 0) & (rhs > 0)).reduce_and(): return div var mod = self - div * rhs @@ -755,7 +760,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ constrained[type.is_numeric(), "the type must be numeric"]() - if rhs == 0: + if (rhs == 0).reduce_and(): # this should raise an exception. return 0 @@ -1568,7 +1573,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self << rhs`. """ constrained[type.is_integral(), "must be an integral type"]() - debug_assert(rhs >= 0, "unhandled negative value") + debug_assert((rhs >= 0).reduce_and(), "unhandled negative value") return __mlir_op.`pop.shl`(self.value, rhs.value) @always_inline("nodebug") @@ -1585,7 +1590,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self >> rhs`. """ constrained[type.is_integral(), "must be an integral type"]() - debug_assert(rhs >= 0, "unhandled negative value") + debug_assert((rhs >= 0).reduce_and(), "unhandled negative value") return __mlir_op.`pop.shr`(self.value, rhs.value) @always_inline("nodebug") @@ -2564,7 +2569,7 @@ fn _pow[ @parameter if rhs_type.is_floating_point() and lhs_type == rhs_type: var rhs_quotient = rhs.__floor__() - if rhs >= 0 and rhs_quotient == rhs: + if ((rhs >= 0) & (rhs_quotient == rhs)).reduce_and(): return _pow(lhs, rhs_quotient.cast[_integral_type_of[rhs_type]()]()) var result = SIMD[lhs_type, simd_width]() @@ -2587,9 +2592,9 @@ fn _pow[ return result elif rhs_type.is_integral(): # Common cases - if rhs == 2: + if (rhs == 2).reduce_and(): return lhs * lhs - if rhs == 3: + if (rhs == 3).reduce_and(): return lhs * lhs * lhs var result = SIMD[lhs_type, simd_width]() diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 65f18c8248..25c5c6f97b 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -235,7 +235,7 @@ fn assert_not_equal[ Raises: An Error with the provided message if assert fails and `None` otherwise. """ - if lhs == rhs: + if (lhs == rhs).reduce_and(): raise _assert_not_equal_error( str(lhs), str(rhs), msg, __call_location() ) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index de29497604..a750327e4b 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -629,13 +629,13 @@ fn isnan[ if not type.is_floating_point(): return False - @parameter - if type == DType.bfloat16: - alias int_dtype = _integral_type_of[type]() - var int_val = bitcast[int_dtype, simd_width](val) - return int_val & SIMD[int_dtype, simd_width](0x7FFF) > SIMD[ - int_dtype, simd_width - ](0x7F80) + # @parameter + # if type == DType.bfloat16: + # alias int_dtype = _integral_type_of[type]() + # var int_val = bitcast[int_dtype, simd_width](val) + # return int_val & SIMD[int_dtype, simd_width](0x7FFF) > SIMD[ + # int_dtype, simd_width + # ](0x7F80) alias signaling_nan_test: UInt32 = 0x0001 alias quiet_nan_test: UInt32 = 0x0002 diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index ee9885609e..5a79afa2c6 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -122,17 +122,9 @@ def test_truthy(): @parameter fn test_dtype[type: DType]() raises: - # # Scalars of 0-values are false-y, 1-values are truth-y - assert_false(Scalar[type](False).__bool__()) - assert_true(Scalar[type](True).__bool__()) - - # # SIMD vectors are truth-y if _all_ values are truth-y - assert_true(SIMD[type, 2](True, True).__bool__()) - - # # SIMD vectors are false-y if _any_ values are false-y - assert_false(SIMD[type, 2](False, True).__bool__()) - assert_false(SIMD[type, 2](True, False).__bool__()) - assert_false(SIMD[type, 2](False, False).__bool__()) + # Scalars of 0-values are false-y, 1-values are truth-y + assert_false(Scalar[type](False)) + assert_true(Scalar[type](True)) @parameter fn test_dtype_unrolled[i: Int]() raises: From 2e3958969b99553578532eecd6fb02101bf7b03d Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Fri, 17 May 2024 22:50:19 -0500 Subject: [PATCH 0619/2019] [External] [stdlib] Fix wrong slice check in span (#40212) [External] [stdlib] Fix wrong slice check in span Fix part of https://github.com/modularml/mojo/issues/2687 both the start and the end of span can be `len(original_string)`, that's not an issue. In the case where the start is equal to the lenght of th string, it just gives an empty span. This is similar to the slicing `my_string[:0]` which also gives an empty span. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2726 MODULAR_ORIG_COMMIT_REV_ID: 1e08d3d29352ae43b7e833d36f5a3acfa7ebe765 --- stdlib/src/utils/span.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 19a40296b0..5fe6be46d6 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -223,8 +223,8 @@ struct Span[ """ var adjusted_span = self._adjust_span(slice) debug_assert( - 0 <= adjusted_span.start < self._len - and 0 <= adjusted_span.end < self._len, + 0 <= adjusted_span.start <= self._len + and 0 <= adjusted_span.end <= self._len, "Slice must be within bounds.", ) var res = Self( From 8f263c137b2ec5e7e15a6b7fe954e6fb660df2b8 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 17 May 2024 23:00:00 -0500 Subject: [PATCH 0620/2019] [External] [stdlib] Fix list test warnings (#40208) [External] [stdlib] Fix list test warnings If you break any of the `List` tests you get flooded with warnings and it makes it annoying to find the message telling you what failed. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2723 MODULAR_ORIG_COMMIT_REV_ID: 341542be0e0903fbedffdbd8fda3de853b394d7c --- stdlib/test/collections/test_list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 8361457791..f96c239b0e 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -556,7 +556,7 @@ def test_2d_dynamic_list(): def test_list_explicit_copy(): var list = List[CopyCounter]() - list.append(CopyCounter()^) + list.append(CopyCounter()) var list_copy = List(list) assert_equal(0, list.__get_ref(0)[].copy_count) assert_equal(1, list_copy.__get_ref(0)[].copy_count) From 0ca42a93782dd9b3fa9448650e445f4ed82b508c Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 18 May 2024 08:41:18 -0500 Subject: [PATCH 0621/2019] [External] [tools] Add markdownlint to the pre-commit (#40213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [tools] Add markdownlint to the pre-commit Fix https://github.com/modularml/mojo/issues/2716 This pre-commit hook will also fix easy mistakes :) It also doesn't require any manual installation. It just works™ The CI will trigger an error if a markdown file does not conform to the rules of markdownlint Example of pre-commit failure in the CI: https://github.com/modularml/mojo/actions/runs/9132279705/job/25113289498?pr=2663 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2717 MODULAR_ORIG_COMMIT_REV_ID: 56e9f0d2e5498625762effab6e7c0d83b6c8b560 --- stdlib/docs/development.md | 2 +- stdlib/scripts/.markdownlint.yaml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 89b04f72b7..d303a4590d 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -142,7 +142,7 @@ standard library, test it, and raise a PR. First, follow everything in the [prerequisites](#prerequisites). -__IMPORTANT__ We'll be in the `mojo/stdlib` folder for this tutorial, check and +**IMPORTANT** We'll be in the `mojo/stdlib` folder for this tutorial, check and make sure you're in that location if anything goes wrong: `cd [path-to-repo]/stdlib` diff --git a/stdlib/scripts/.markdownlint.yaml b/stdlib/scripts/.markdownlint.yaml index de1b6b947e..166f2c4656 100644 --- a/stdlib/scripts/.markdownlint.yaml +++ b/stdlib/scripts/.markdownlint.yaml @@ -38,6 +38,11 @@ no-inline-html: false # MD045: Images don't require alt text no-alt-text: false +# MD050: Use **something** instead of __something__ for bold text +MD050: + # Strong style + style: "asterisk" + # MD051: Disable link checker because it false-flags custom anchors # https://github.com/DavidAnson/markdownlint/issues/570 link-fragments: false From 59e7e98163ea94fb30e91efaf366aa672a9177d9 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sun, 19 May 2024 01:24:23 -0700 Subject: [PATCH 0622/2019] [stdlib] Use Immutable and Mutable in {Imm, Mut}Lifetime and {Imm, Mut}StaticLifetime MODULAR_ORIG_COMMIT_REV_ID: 128d2ff9f190cf9525ca8951a8c372a1cbe2229a --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 10 ++++++---- stdlib/src/builtin/type_aliases.mojo | 8 ++++---- stdlib/src/memory/unsafe.mojo | 4 +++- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 804b826961..0be47d5a31 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -172,7 +172,7 @@ struct _VariadicListMemIter[ elt_type: AnyType, elt_is_mutable: Bool, elt_lifetime: AnyLifetime[elt_is_mutable].type, - list_lifetime: ImmLifetime, + list_lifetime: ImmutableLifetime, ]: """Iterator for VariadicListMem. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 021b49e24b..4a7725316f 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -251,7 +251,7 @@ struct StringLiteral( @always_inline fn as_string_slice( self: Reference[Self, _, _] - ) -> StringSlice[False, ImmStaticLifetime]: + ) -> StringSlice[False, ImmutableStaticLifetime]: """Returns a string slice of this static string literal. Returns: @@ -263,12 +263,14 @@ struct StringLiteral( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually # guaranteed to be valid. - return StringSlice[False, ImmStaticLifetime](unsafe_from_utf8=bytes) + return StringSlice[False, ImmutableStaticLifetime]( + unsafe_from_utf8=bytes + ) @always_inline fn as_bytes_slice( self: Reference[Self, _, _] - ) -> Span[UInt8, False, ImmStaticLifetime]: + ) -> Span[UInt8, False, ImmutableStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -278,7 +280,7 @@ struct StringLiteral( var ptr = self[].unsafe_uint8_ptr() - return Span[UInt8, False, ImmStaticLifetime]( + return Span[UInt8, False, ImmutableStaticLifetime]( unsafe_ptr=ptr, len=self[]._byte_length(), ) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 740b104a4a..cb0fb23d2e 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -21,16 +21,16 @@ alias AnyRegType = __mlir_type.`!kgen.type` alias NoneType = __mlir_type.`!kgen.none` """Represents the absence of a value.""" -alias ImmLifetime = __mlir_type.`!lit.lifetime<0>` +alias ImmutableLifetime = __mlir_type.`!lit.lifetime<0>` """Immutable lifetime reference type.""" -alias MutLifetime = __mlir_type.`!lit.lifetime<1>` +alias MutableLifetime = __mlir_type.`!lit.lifetime<1>` """Mutable lifetime reference type.""" -alias ImmStaticLifetime = __mlir_attr.`#lit.lifetime<0>: !lit.lifetime<0>` +alias ImmutableStaticLifetime = __mlir_attr.`#lit.lifetime<0>: !lit.lifetime<0>` """The immutable lifetime that lasts for the entire duration of program execution.""" -alias MutStaticLifetime = __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` +alias MutableStaticLifetime = __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` """The mutable lifetime that lasts for the entire duration of program execution.""" diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 04ff7d7efc..7d7fdfe0ed 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -179,7 +179,9 @@ struct LegacyPointer[ var address: Self._mlir_type """The pointed-to address.""" - alias _ref_type = Reference[type, True, MutStaticLifetime, address_space] + alias _ref_type = Reference[ + type, True, MutableStaticLifetime, address_space + ] @always_inline("nodebug") fn __init__() -> Self: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 877c2a4a60..7d2c3660a6 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -48,7 +48,7 @@ struct UnsafePointer[ # We're unsafe, so we can have unsafe things. References we make have # an immortal mutable lifetime, since we can't come up with a meaningful # lifetime for them anyway. - alias _ref_type = Reference[T, True, MutStaticLifetime, address_space] + alias _ref_type = Reference[T, True, MutableStaticLifetime, address_space] """The underlying pointer type.""" var address: Self._mlir_type diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 463c2119a2..e6b351f9a5 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -117,7 +117,7 @@ fn test_heap_string_from_string_slice() raises: var string_lit: StringLiteral = "Hello" var static_str: StringSlice[ - False, ImmStaticLifetime + False, ImmutableStaticLifetime ] = string_lit.as_string_slice() var heap_string = String(static_str) From 51bb050b2bfda4c730411c3469b99cb9011283f6 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 19 May 2024 08:57:29 -0700 Subject: [PATCH 0623/2019] [mojo-lang] Rework 'def' argument processing We previously defaulted 'def' arguments to 'owned' when no explicit arg convention is specified. This enabled them to be mutable in the body of the function like Python. However, this came at a serious cost: it made all 'def' arguments get copied in the caller side, which is extremely impractical for expensive-to-copy arguments and doesn't work for non-copyable types. This changes Mojo to use a computed lvalue to lazily synthesize the argument box only if it is mutated. This results in a lot less IR, doesn't copy the value unless there is a reason to and generally provides a better UX. This fixes MOCO-179. MODULAR_ORIG_COMMIT_REV_ID: 4a187f53ed160ac4f3db2d5f7f4b2e110a162cea --- stdlib/test/builtin/test_object.mojo | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 02cdc4ea30..bc35fa685f 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -180,13 +180,13 @@ def test_object_bitwise(): assert_true(15 ^ object(7) == 8) -def test_function(borrowed lhs, borrowed rhs) -> object: +def test_function(lhs, rhs) -> object: return lhs + rhs # These are all marked borrowed because 'object' doesn't support function # types with owned arguments. -def test_function_raises(borrowed a) -> object: +def test_function_raises(a) -> object: raise Error("Error from function type") @@ -205,22 +205,16 @@ def test_non_object_getattr(): a.foo(2) -# These are all marked borrowed because 'object' doesn't support function -# types with owned arguments. -def matrix_getitem(borrowed self, borrowed i) -> object: +def matrix_getitem(self, i) -> object: return self.value[i] -# These are all marked borrowed because 'object' doesn't support function -# types with owned arguments. -def matrix_setitem(borrowed self, borrowed i, borrowed value) -> object: +def matrix_setitem(self, i, value) -> object: self.value[i] = value return None -# These are all marked borrowed because 'object' doesn't support function -# types with owned arguments. -def matrix_append(borrowed self, borrowed value) -> object: +def matrix_append(self, value) -> object: var impl = self.value impl.append(value) return None From c68f2cbdef2702257e9e98cd7c567b4fb5eb808d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 19 May 2024 10:14:22 -0700 Subject: [PATCH 0624/2019] [mojo-docs] Changelog the def behavior change MODULAR_ORIG_COMMIT_REV_ID: 55af9f67cca1a4427eb9aec888818df0901b7f9b --- docs/changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e2a76a61e9..f1240e9a7e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,15 @@ what we publish. bar() # warning: use another function! ``` +- Mojo has changed how `def` arguments are processed. Previously, by default, + arguments to a `def` were treated as `owned` convention, which makes a copy of + the value, enabling that value to be mutable in the callee. This is "worked", + but was a major performance footgun, and required you to declare non-copyable + types as `borrowed` explicitly. Now Mojo takes a different approach: it takes + the arguments as `borrowed` (consistent with `fn`s) but will make a local copy + of the value **only if the argument is mutated** on the body of the function. + This improves consistency, performance, and ease of use. + - `int()` can now take a string and a specified base to parse an integer from a string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is specified, the string will be parsed as if it was an integer literal, with the From 28a95d8e555782fba9d39e8454b7356e2780f1f2 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 20 May 2024 01:08:31 -0700 Subject: [PATCH 0625/2019] [mojo-lang] Implement parameter inference inside parameter lists After lots of deck chair rearranging (as is the Mojo dev way), we can finally implement parameter inference from other parameters in any parameter list. This enables `inferred` parameters to work in struct parameter lists and in function binding (but not calling) contexts. The feature is now complete (modulo bugs...), but we still have work to do to unify the underlying representation of struct and function parameter lists. MODULAR_ORIG_COMMIT_REV_ID: fb60fe4f6f42f3c8f4608fbda0295fe7528d85dc --- docs/changelog.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index f1240e9a7e..4469473b30 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -54,7 +54,15 @@ what we publish. non-inferred parameter. `dt` is inferred from the parameter itself to `DType.int32`. - Note that this only works on function parameter lists at the moment. + This also works with structs. For example: + + ```mojo + struct ScalarContainer[inferred dt: DType, value: Scalar[dt]]: + pass + + fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred + pass + ``` - Mojo now supports adding a `@deprecated` decorator on structs, functions, traits, aliases, and global variables. The decorator marks the attached decl From 9a5e1ec4c22e9d46f3564aff07b14bf88d2c481a Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Mon, 20 May 2024 11:34:52 -0500 Subject: [PATCH 0626/2019] [External] [stdlib] Add List constructor from Span (#40246) [External] [stdlib] Add List constructor from Span Being able to convert a Span to an owned value is useful, and List makes the most sense for this type. We could also consider a `to_list()` method on Span instead if that is a better fit. Co-authored-by: Lukas Hermann Closes modularml/mojo#2754 MODULAR_ORIG_COMMIT_REV_ID: 5c4bf563fc8d7644b8c0cab316b5376988e25748 --- stdlib/src/collections/list.mojo | 11 +++++++++++ stdlib/test/collections/test_list.mojo | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b57989363e..ec6fcf1a4d 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -24,6 +24,7 @@ from memory import UnsafePointer, Reference from memory.unsafe_pointer import move_pointee, move_from_pointee from sys.intrinsics import _type_is_eq from .optional import Optional +from utils import Span # ===----------------------------------------------------------------------===# # List @@ -128,6 +129,16 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): for value in values: self.append(value[]) + fn __init__(inout self, span: Span[T]): + """Constructs a list from the a Span of values. + + Args: + span: The span of values to populate the list with. + """ + self = Self(capacity=len(span)) + for value in span: + self.append(value[]) + fn __init__( inout self: Self, *, diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index f96c239b0e..7e643a3c42 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -16,6 +16,7 @@ from collections import List from test_utils import CopyCounter, MoveCounter from testing import assert_equal, assert_false, assert_true, assert_raises +from utils import Span def test_mojo_issue_698(): @@ -759,6 +760,14 @@ def test_list_contains(): # assert_equal(List(0,1) in y,False) +def test_list_init_span(): + var l = List[String]("a", "bb", "cc", "def") + var sp = Span(l) + var l2 = List[String](sp) + for i in range(len(l)): + assert_equal(l[i], l2[i]) + + def main(): test_mojo_issue_698() test_list() From 710253d934e09a5aa122a17a1e8767e45f5e0f2f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 20 May 2024 10:04:14 -0700 Subject: [PATCH 0627/2019] [mojo-lang] Resolve function parameter overloads when forming references This PR teaches `getDirectSymbol` to attempt to resolve parameter overloads on the current function decl list if any parameters are present. This enables forming references to overloaded declarations if enough parameters are specified (without calling them). Also updates the changelog. MODULAR_ORIG_COMMIT_REV_ID: 02e3772fcd24d2d9965363c4d9b620cf462c79f1 --- docs/changelog.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4469473b30..312fa123e1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -60,10 +60,27 @@ what we publish. struct ScalarContainer[inferred dt: DType, value: Scalar[dt]]: pass - fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred + fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` pass ``` + This should make working with dependent parameters more ergonomic. + +- Mojo now allows functions overloaded on parameters to be resolved when forming + references to, but not calling, those functions. For example, the following + now works: + + ```mojo + fn overloaded_parameters[value: Int32](): + pass + + fn overloaded_parameters[value: Float32](): + pass + + fn form_reference(): + alias ref = overloaded_parameters[Int32()] # works! + ``` + - Mojo now supports adding a `@deprecated` decorator on structs, functions, traits, aliases, and global variables. The decorator marks the attached decl as deprecated and causes a warning to be emitted when the deprecated decl is From 68284ee2cfd32af172ca2941000b76c84960b276 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Mon, 20 May 2024 13:02:51 -0500 Subject: [PATCH 0628/2019] [External] [stdlib] Apply Indexer trait to stdlib containers (#40205) [External] [stdlib] Apply Indexer trait to stdlib containers The second half of #2384 I have intentionally omitted `static_tuple.mojo` to avoid conflicting with #2677 Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2722 MODULAR_ORIG_COMMIT_REV_ID: 365298938b36c3f4f9ad0067dfcb3856de369875 --- docs/changelog.md | 8 ++--- stdlib/src/builtin/builtin_list.mojo | 33 ++++++++++++------- stdlib/src/builtin/builtin_slice.mojo | 7 ++-- stdlib/src/builtin/range.mojo | 12 +++---- stdlib/src/builtin/simd.mojo | 31 +++++++++++++---- stdlib/src/builtin/string.mojo | 8 +++-- stdlib/src/collections/inline_list.mojo | 11 ++++--- stdlib/src/collections/list.mojo | 31 ++++++++++++----- stdlib/src/collections/vector.mojo | 26 ++++++++++----- stdlib/src/memory/unsafe.mojo | 24 +++++++++----- stdlib/src/memory/unsafe_pointer.mojo | 9 +++-- stdlib/src/utils/index.mojo | 18 +++++----- stdlib/src/utils/span.mojo | 20 +++++------ stdlib/src/utils/stringref.mojo | 7 ++-- stdlib/test/builtin/test_list.mojo | 2 ++ stdlib/test/builtin/test_range.mojo | 8 +++++ stdlib/test/builtin/test_simd.mojo | 8 +++++ stdlib/test/builtin/test_slice.mojo | 9 ++++- stdlib/test/builtin/test_string.mojo | 8 +++++ stdlib/test/builtin/test_stringref.mojo | 8 +++++ stdlib/test/collections/test_inline_list.mojo | 12 +++++++ stdlib/test/collections/test_list.mojo | 10 ++++++ stdlib/test/collections/test_vector.mojo | 11 +++++++ stdlib/test/memory/test_memory.mojo | 11 +++++++ stdlib/test/memory/test_unsafepointer.mojo | 11 +++++++ stdlib/test/utils/test_span.mojo | 9 +++++ 26 files changed, 266 insertions(+), 86 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 312fa123e1..d2887fd474 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -306,10 +306,8 @@ what we publish. - Added the `Indexer` trait to denote types that implement the `__index__()` method which allow these types to be accepted in common `__getitem__` and `__setitem__` implementations, as well as allow a new builtin `index` function - to be called on them. - ([PR #2685](https://github.com/modularml/mojo/pull/2685) by - [@bgreni](https://github.com/bgreni)) - For example: + to be called on them. Most stdlib containers are now able to be indexed by + any type that implements `Indexer`. For example: ```mojo @value @@ -329,6 +327,8 @@ what we publish. print(MyList()[AlwaysZero()]) # prints `1` ``` + ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + - `StringRef` now implements `strip()` which can be used to remove leading and trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) by [@fknfilewalker](https://github.com/fknfilewalker)) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 0be47d5a31..976feb9020 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -146,16 +146,19 @@ struct VariadicList[type: AnyRegType](Sized): return __mlir_op.`pop.variadic.size`(self.value) @always_inline - fn __getitem__(self, index: Int) -> type: + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> type: """Gets a single element on the variadic list. + Parameters: + IndexerType: The type of the indexer. + Args: - index: The index of the element to access on the list. + idx: The index of the element to access on the list. Returns: The element on the list corresponding to the given index. """ - return __mlir_op.`pop.variadic.get`(self.value, index.value) + return __mlir_op.`pop.variadic.get`(self.value, index(idx).value) @always_inline fn __iter__(self) -> Self.IterType: @@ -358,24 +361,29 @@ struct VariadicListMem[ # TODO: Fix for loops + _VariadicListIter to support a __nextref__ protocol # allowing us to get rid of this and make foreach iteration clean. @always_inline - fn __getitem__(self, index: Int) -> Self.reference_type: + fn __getitem__[ + IndexerType: Indexer + ](self, idx: IndexerType) -> Self.reference_type: """Gets a single element on the variadic list. + Parameters: + IndexerType: The type of the indexer. + Args: - index: The index of the element to access on the list. + idx: The index of the element to access on the list. Returns: A low-level pointer to the element on the list corresponding to the given index. """ return Self.reference_type( - __mlir_op.`pop.variadic.get`(self.value, index.value) + __mlir_op.`pop.variadic.get`(self.value, index(idx).value) ) @always_inline - fn __refitem__( - self, index: Int - ) -> Reference[ + fn __refitem__[ + IndexerType: Indexer + ](self, idx: IndexerType) -> Reference[ element_type, Bool {value: elt_is_mutable}, _lit_lifetime_union[ @@ -391,14 +399,17 @@ struct VariadicListMem[ ]: """Gets a single element on the variadic list. + Parameters: + IndexerType: The type of the indexer. + Args: - index: The index of the element to access on the list. + idx: The index of the element to access on the list. Returns: A low-level pointer to the element on the list corresponding to the given index. """ - return __mlir_op.`pop.variadic.get`(self.value, index.value) + return __mlir_op.`pop.variadic.get`(self.value, index(idx).value) fn __iter__( self, diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index ccb8ec01f2..c09d4c90ec 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -149,16 +149,19 @@ struct Slice(Sized, Stringable, EqualityComparable): return len(range(self.start, self.end, self.step)) @always_inline - fn __getitem__(self, idx: Int) -> Int: + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: """Get the slice index. + Parameters: + IndexerType: The type of the indexer. + Args: idx: The index. Returns: The slice index. """ - return self.start + idx * self.step + return self.start + index(idx) * self.step @always_inline("nodebug") fn _has_end(self) -> Bool: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index ff6bf16dc7..a1e98ddbba 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -85,8 +85,8 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): return self.curr @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> Int: - return idx + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + return index(idx) @always_inline("nodebug") fn __reversed__(self) -> _StridedRange: @@ -116,8 +116,8 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): return self.end - self.start if self.start < self.end else 0 @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> Int: - return self.start + idx + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + return self.start + index(idx) @always_inline("nodebug") fn __reversed__(self) -> _StridedRange: @@ -184,8 +184,8 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return _div_ceil_positive(abs(self.start - self.end), abs(self.step)) @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> Int: - return self.start + idx * self.step + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + return self.start + index(idx) * self.step @always_inline("nodebug") fn __reversed__(self) -> _StridedRange: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ac2146a9f2..9128fb3704 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1824,9 +1824,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> Scalar[type]: + fn __getitem__[ + IndexerType: Indexer + ](self, idx: IndexerType) -> Scalar[type]: """Gets an element from the vector. + Parameters: + IndexerType: The type of the indexer. + Args: idx: The element index. @@ -1835,32 +1840,44 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ return __mlir_op.`pop.simd.extractelement`[ _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ](self.value, idx.value) + ](self.value, index(idx).value) @always_inline("nodebug") - fn __setitem__(inout self, idx: Int, val: Scalar[type]): + fn __setitem__[ + IndexerType: Indexer + ](inout self, idx: IndexerType, val: Scalar[type]): """Sets an element in the vector. + Parameters: + IndexerType: The type of the indexer. + Args: idx: The index to set. val: The value to set. """ self.value = __mlir_op.`pop.simd.insertelement`( - self.value, val.value, idx.value + self.value, val.value, index(idx).value ) @always_inline("nodebug") - fn __setitem__( - inout self, idx: Int, val: __mlir_type[`!pop.scalar<`, type.value, `>`] + fn __setitem__[ + IndexerType: Indexer + ]( + inout self, + idx: IndexerType, + val: __mlir_type[`!pop.scalar<`, type.value, `>`], ): """Sets an element in the vector. + Parameters: + IndexerType: The type of the indexer. + Args: idx: The index to set. val: The value to set. """ self.value = __mlir_op.`pop.simd.insertelement`( - self.value, val, idx.value + self.value, val, index(idx).value ) fn __hash__(self) -> Int: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index b55554ccf7..a9c5ed0136 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -877,15 +877,19 @@ struct String( """ return len(self) > 0 - fn __getitem__(self, idx: Int) -> String: + fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> String: """Gets the character at the specified position. + Parameters: + IndexerType: The type of the indexer. + Args: - idx: The index value. + i: The index value. Returns: A new string containing the character at the specified position. """ + var idx = index(i) if idx < 0: return self.__getitem__(len(self) + idx) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 7bd9ab2765..281bbba3d5 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -129,19 +129,22 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): @always_inline fn __refitem__[ - IntableType: Intable, - ](self: Reference[Self, _, _], index: IntableType) -> Reference[ + IndexerType: Indexer, + ](self: Reference[Self, _, _], idx: IndexerType) -> Reference[ Self.ElementType, self.is_mutable, self.lifetime ]: """Get a `Reference` to the element at the given index. + Parameters: + IndexerType: The type of the indexer. + Args: - index: The index of the item. + idx: The index of the item. Returns: A reference to the item at the given index. """ - var i = int(index) + var i = index(idx) debug_assert( -self[]._size <= i < self[]._size, "Index must be within bounds." ) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index ec6fcf1a4d..ffe4a97b64 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -553,17 +553,25 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.capacity = 0 return ptr - fn __setitem__(inout self, i: Int, owned value: T): + fn __setitem__[ + IndexerType: Indexer + ](inout self, i: IndexerType, owned value: T): """Sets a list element at the given index. + Parameters: + IndexerType: The type of the indexer. + Args: i: The index of the element. value: The value to assign. """ - debug_assert(-self.size <= i < self.size, "index must be within bounds") + var normalized_idx = index(i) + debug_assert( + -self.size <= normalized_idx < self.size, + "index must be within bounds", + ) - var normalized_idx = i - if i < 0: + if normalized_idx < 0: normalized_idx += len(self) destroy_pointee(self.data + normalized_idx) @@ -613,21 +621,26 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return res^ @always_inline - fn __getitem__(self, i: Int) -> T: + fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> T: """Gets a copy of the list element at the given index. FIXME(lifetimes): This should return a reference, not a copy! + Parameters: + IndexerType: The type of the indexer. + Args: i: The index of the element. Returns: A copy of the element at the given index. """ - debug_assert(-self.size <= i < self.size, "index must be within bounds") - - var normalized_idx = i - if i < 0: + var normalized_idx = index(i) + debug_assert( + -self.size <= normalized_idx < self.size, + "index must be within bounds", + ) + if normalized_idx < 0: normalized_idx += len(self) return (self.data + normalized_idx)[] diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index edd2ec0dc6..ac239d1b2b 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -181,21 +181,25 @@ struct InlinedFixedVector[ return self.current_size @always_inline - fn __getitem__(self, i: Int) -> type: + fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> type: """Gets a vector element at the given index. + Parameters: + IndexerType: The type of the indexer. + Args: i: The index of the element. Returns: The element at the given index. """ + var normalized_idx = index(i) debug_assert( - -self.current_size <= i < self.current_size, + -self.current_size <= normalized_idx < self.current_size, "index must be within bounds", ) - var normalized_idx = i - if i < 0: + + if normalized_idx < 0: normalized_idx += len(self) if normalized_idx < Self.static_size: @@ -204,20 +208,24 @@ struct InlinedFixedVector[ return self.dynamic_data[normalized_idx - Self.static_size] @always_inline - fn __setitem__(inout self, i: Int, value: type): + fn __setitem__[ + IndexerType: Indexer + ](inout self, i: IndexerType, value: type): """Sets a vector element at the given index. + Parameters: + IndexerType: The type of the indexer. + Args: i: The index of the element. value: The value to assign. """ + var normalized_idx = index(i) debug_assert( - -self.current_size <= i < self.current_size, + -self.current_size <= normalized_idx < self.current_size, "index must be within bounds", ) - - var normalized_idx = i - if i < 0: + if normalized_idx < 0: normalized_idx += len(self) if normalized_idx < Self.static_size: diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 7d7fdfe0ed..f84adc5391 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -289,11 +289,13 @@ struct LegacyPointer[ ](self.address) @always_inline("nodebug") - fn __refitem__[T: Intable](self, offset: T) -> Self._ref_type: + fn __refitem__[ + IndexerType: Indexer + ](self, offset: IndexerType) -> Self._ref_type: """Enable subscript syntax `ref[idx]` to access the element. Parameters: - T: The Intable type of the offset. + IndexerType: The type of the indexer.. Args: offset: The offset to load from. @@ -301,7 +303,7 @@ struct LegacyPointer[ Returns: The MLIR reference for the Mojo compiler to use. """ - return (self + offset).__refitem__() + return (self + index(offset)).__refitem__() # ===------------------------------------------------------------------=== # # Load/Store @@ -714,12 +716,14 @@ struct DTypePointer[ return LegacyPointer.address_of(arg[]) @always_inline("nodebug") - fn __getitem__[T: Intable](self, offset: T) -> Scalar[type]: + fn __getitem__[ + IndexerType: Indexer + ](self, offset: IndexerType) -> Scalar[type]: """Loads a single element (SIMD of size 1) from the pointer at the specified index. Parameters: - T: The Intable type of the offset. + IndexerType: The type of the indexer. Args: offset: The offset to load from. @@ -727,20 +731,22 @@ struct DTypePointer[ Returns: The loaded value. """ - return self.load(offset) + return self.load(index(offset)) @always_inline("nodebug") - fn __setitem__[T: Intable](self, offset: T, val: Scalar[type]): + fn __setitem__[ + IndexerType: Indexer + ](self, offset: IndexerType, val: Scalar[type]): """Stores a single element value at the given offset. Parameters: - T: The Intable type of the offset. + IndexerType: The type of the indexer. Args: offset: The offset to store to. val: The value to store. """ - return self.store(offset, val) + return self.store(index(offset), val) # ===------------------------------------------------------------------=== # # Comparisons diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 7d2c3660a6..d25b3ef08b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -362,16 +362,21 @@ struct UnsafePointer[ ](self.address) @always_inline - fn __refitem__(self, offset: Int) -> Self._ref_type: + fn __refitem__[ + IndexerType: Indexer + ](self, offset: IndexerType) -> Self._ref_type: """Return a reference to the underlying data, offset by the offset index. + Parameters: + IndexerType: The type of the indexer. + Args: offset: The offset index. Returns: An offset reference. """ - return (self + offset).__refitem__() + return (self + index(offset)).__refitem__() # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index cc6c22f245..8a00319b9f 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -335,19 +335,19 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): return size @always_inline("nodebug") - fn __getitem__[intable: Intable](self, index: intable) -> Int: + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: """Gets an element from the tuple by index. Parameters: - intable: The intable type. + IndexerType: The type of the indexer. Args: - index: The element index. + idx: The element index. Returns: The tuple element value. """ - return self.data[index] + return self.data[index(idx)] @always_inline("nodebug") fn __setitem__[index: Int](inout self, val: Int): @@ -362,17 +362,19 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): self.data.__setitem__[index](val) @always_inline("nodebug") - fn __setitem__[intable: Intable](inout self, index: intable, val: Int): + fn __setitem__[ + IndexerType: Indexer + ](inout self, idx: IndexerType, val: Int): """Sets an element in the tuple at the given index. Parameters: - intable: The intable type. + IndexerType: The type of the indexer. Args: - index: The element index. + idx: The element index. val: The value to store. """ - self.data[index] = val + self.data[index(idx)] = val @always_inline("nodebug") fn as_tuple(self) -> StaticTuple[Int, size]: diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 5fe6be46d6..f80b6c9721 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -176,38 +176,38 @@ struct Span[ @always_inline fn __getitem__[ - IntableType: Intable - ](self, index: IntableType) -> Reference[T, is_mutable, lifetime]: + IndexerType: Indexer + ](self, idx: IndexerType) -> Reference[T, is_mutable, lifetime]: """Get a `Reference` to the element at the given index. Parameters: - IntableType: The inferred type of an intable argument. + IndexerType: The type of the indexer. Args: - index: The index of the item. + idx: The index of the item. Returns: A reference to the item at the given index. """ # note that self._refitem__ is already bounds checking - return self._refitem__(index) + return self._refitem__(index(idx)) @always_inline fn __setitem__[ - IntableType: Intable - ](inout self, index: IntableType, value: T): + IndexerType: Indexer + ](inout self, idx: IndexerType, value: T): """Get a `Reference` to the element at the given index. Parameters: - IntableType: The inferred type of an intable argument. + IndexerType: The type of the indexer. Args: - index: The index of the item. + idx: The index of the item. value: The value to set at the given index. """ # note that self._refitem__ is already bounds checking var ref = Reference[T, __mlir_attr.`1: i1`, __lifetime_of(self)]( - UnsafePointer(self._refitem__(index))[] + UnsafePointer(self._refitem__(index(idx)))[] ) ref[] = value diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 3e2b93170b..7e929d97e8 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -289,16 +289,19 @@ struct StringRef( return not (self < rhs) @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> StringRef: + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> StringRef: """Get the string value at the specified position. + Parameters: + IndexerType: The type of the indexer. + Args: idx: The index position. Returns: The character at the specified position. """ - return StringRef {data: self.data + idx, length: 1} + return StringRef {data: self.data + index(idx), length: 1} fn __hash__(self) -> Int: """Hash the underlying buffer using builtin hash. diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 5464bd1217..a3f028cfbc 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -25,6 +25,8 @@ fn test_variadic_list() raises: assert_equal(nums[0], 5) assert_equal(nums[1], 8) assert_equal(nums[2], 6) + assert_equal(nums[True], 8) + assert_equal(nums[Int32(0)], 5) assert_equal(len(nums), 3) diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 637d38eefd..77cd4c2355 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -115,7 +115,15 @@ def test_range_reversed(): test_sum_reversed(20, end, -3) +def test_indexing(): + var r = range(10) + assert_equal(r[True], 1) + assert_equal(r[Int8(4)], 4) + assert_equal(r[3], 3) + + def main(): test_range_len() test_range_getitem() test_range_reversed() + test_indexing() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 5a79afa2c6..9374592fb8 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1008,6 +1008,13 @@ def test_indexer(): assert_equal(0, Scalar[DType.bool](False).__index__()) +def test_indexing(): + var s = SIMD[DType.int32, 4](1, 2, 3, 4) + assert_equal(s[False], 1) + assert_equal(s[Int32(2)], 3) + assert_equal(s[3], 4) + + def main(): test_abs() test_add() @@ -1023,6 +1030,7 @@ def main(): test_floordiv() test_iadd() test_indexer() + test_indexing() test_insert() test_interleave() test_issue_20421() diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 672d14e799..06ddcbbfbb 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -82,9 +82,16 @@ def test_slice_stringable(): assert_equal(s[:-1], "0:-1:1") +def test_indexing(): + var s = slice(1, 10) + assert_equal(s[True], 2) + assert_equal(s[UInt64(0)], 1) + assert_equal(s[2], 3) + + def main(): test_none_end_folds() test_slicable() test_has_end() - test_slice_stringable() + test_indexing() diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index af6661e38a..2ad43811a6 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -899,6 +899,13 @@ def test_string_mul(): assert_equal(String("ab") * 5, "ababababab") +def test_indexing(): + a = String("abc") + assert_equal(a[False], "a") + assert_equal(a[Int16(1)], "b") + assert_equal(a[2], "c") + + def main(): test_constructors() test_copy() @@ -941,3 +948,4 @@ def main(): test_removesuffix() test_intable() test_string_mul() + test_indexing() diff --git a/stdlib/test/builtin/test_stringref.mojo b/stdlib/test/builtin/test_stringref.mojo index fc85265237..d75209e90c 100644 --- a/stdlib/test/builtin/test_stringref.mojo +++ b/stdlib/test/builtin/test_stringref.mojo @@ -75,7 +75,15 @@ def test_intable(): _ = int(StringRef("hi")) +def test_indexing(): + a = StringRef("abc") + assert_equal(a[False], "a") + assert_equal(a[Int16(1)], "b") + assert_equal(a[0], "a") + + def main(): test_strref_from_start() test_comparison_operators() test_intable() + test_indexing() diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index ba1308e9b9..9ec25036cd 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -138,6 +138,17 @@ def test_list_variadic_constructor(): assert_equal(8, l[3]) +def test_indexing(): + var list = InlineList[Int]() + + for i in range(5): + list.append(i) + + assert_equal(list[True], 1) + assert_equal(list[Int32(4)], 4) + assert_equal(list[0], 0) + + def main(): test_list() test_append_triggers_a_move() @@ -146,3 +157,4 @@ def main(): test_list_iter_mutable() test_list_contains() test_list_variadic_constructor() + test_indexing() diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 7e643a3c42..052b90024b 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -768,6 +768,15 @@ def test_list_init_span(): assert_equal(l[i], l2[i]) +def test_indexing(): + var l = List[Int](1, 2, 3) + assert_equal(l[Int8(1)], 2) + assert_equal(l[UInt64(2)], 3) + assert_equal(l[False], 1) + assert_equal(l[True], 2) + assert_equal(l[2], 3) + + def main(): test_mojo_issue_698() test_list() @@ -796,3 +805,4 @@ def main(): test_list_add() test_list_mult() test_list_contains() + test_indexing() diff --git a/stdlib/test/collections/test_vector.mojo b/stdlib/test/collections/test_vector.mojo index 9750141bd8..a715739965 100644 --- a/stdlib/test/collections/test_vector.mojo +++ b/stdlib/test/collections/test_vector.mojo @@ -109,6 +109,17 @@ def test_inlined_fixed_vector_with_default(): vector._del_old() +def test_indexing(): + var vector = InlinedFixedVector[Int](10) + for i in range(5): + vector.append(i) + assert_equal(1, vector[Int16(1)]) + assert_equal(1, vector[True]) + assert_equal(1, vector[Scalar[DType.bool](True)]) + assert_equal(2, vector[2]) + + def main(): test_inlined_fixed_vector() test_inlined_fixed_vector_with_default() + test_indexing() diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 55eedb1426..4cb7d4815c 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -508,6 +508,16 @@ def test_memcpy_unsafe_pointer(): assert_equal(list_a[9], 5) +def test_indexing(): + var ptr = DTypePointer[DType.float32].alloc(4) + for i in range(4): + ptr[i] = i + + assert_equal(ptr[True], 1) + assert_equal(ptr[Int32(2)], 2) + assert_equal(ptr[1], 1) + + def main(): test_memcpy() test_memcpy_dtype() @@ -526,3 +536,4 @@ def main(): test_dtypepointer_gather() test_dtypepointer_scatter() + test_indexing() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index b5ec147847..1d3813cdbd 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -158,6 +158,16 @@ def test_unsafepointer_address_space(): p2.free() +def test_indexing(): + var ptr = UnsafePointer[Int].alloc(4) + for i in range(4): + ptr[i] = i + + assert_equal(ptr[False], 0) + assert_equal(ptr[Int32(1)], 1) + assert_equal(ptr[3], 3) + + def main(): test_address_of() @@ -173,3 +183,4 @@ def main(): test_comparisons() test_unsafepointer_address_space() + test_indexing() diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index f0b180a865..bb41838959 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -110,8 +110,17 @@ def test_span_array_str(): assert_equal(l[-1], "i") +def test_indexing(): + var l = InlineArray[Int, 7](1, 2, 3, 4, 5, 6, 7) + var s = Span(l) + assert_equal(s[True][], 2) + assert_equal(s[Int32(0)][], 1) + assert_equal(s[3][], 4) + + def main(): test_span_list_int() test_span_list_str() test_span_array_int() test_span_array_str() + test_indexing() From 63efc66305d6955ef1f20c9b545fe48d98fa2c65 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Mon, 20 May 2024 13:07:24 -0500 Subject: [PATCH 0629/2019] [External] [stdlib] Tuple.__contains__ (#40202) [External] [stdlib] Tuple.__contains__ Hello, here is an implementation of ```Tuple.__contains__```, it makes it possible to do: ```mojo var x = Tuple(1,2,True) if 1 in x: print("x contains 1") ``` --------- Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2709 MODULAR_ORIG_COMMIT_REV_ID: ec8cf2aaa0912d06f87c2b2ad481a7aa99b4cc03 --- docs/changelog.md | 9 ++++ stdlib/src/builtin/tuple.mojo | 35 ++++++++++++++++ stdlib/test/builtin/test_tuple.mojo | 65 +++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 stdlib/test/builtin/test_tuple.mojo diff --git a/docs/changelog.md b/docs/changelog.md index d2887fd474..8ee85c71c9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -345,6 +345,15 @@ what we publish. - Added `atof()` function which can convert a `String` to a `float64`. ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) +- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) + by [@rd4com](https://github.com/rd4com)) For example: + + ```mojo + var x = Tuple(1, 2, True) + if 1 in x: + print("x contains 1") + ``` + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index a61ba80680..993f9bfefe 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -23,6 +23,8 @@ from memory.unsafe_pointer import ( move_pointee, ) +from sys.intrinsics import _type_is_eq + # ===----------------------------------------------------------------------===# # Tuple # ===----------------------------------------------------------------------===# @@ -178,3 +180,36 @@ struct Tuple[*element_types: Movable](Sized, Movable): The tuple element at the requested index. """ return rebind[T](self[i]) + + @always_inline("nodebug") + fn __contains__[ + RHS_T: ComparableCollectionElement + ](inout self, owned rhs: RHS_T) -> Bool: + """Verify if a given value is present in the tuple. + + ```mojo + var x = Tuple(1,2,True) + if 1 in x: print("x contains 1") + ``` + Args: + rhs: The value to find. + + Parameters: + RHS_T: The inferred type of RHS. Must implement the + trait `ComparableCollectionElement`. + + Returns: + True if the value is contained in the tuple, False otherwise. + """ + var result = False + + @parameter + fn SingleIteration[Index: Int](): + if _type_is_eq[RHS_T, element_types[Index.value]](): + var tmp = rhs + result |= self.get[Index, RHS_T]().__eq__(tmp) + _ = tmp + + unroll[SingleIteration, len(VariadicList(element_types))]() + _ = rhs^ + return result diff --git a/stdlib/test/builtin/test_tuple.mojo b/stdlib/test/builtin/test_tuple.mojo new file mode 100644 index 0000000000..c497f1fc50 --- /dev/null +++ b/stdlib/test/builtin/test_tuple.mojo @@ -0,0 +1,65 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_true, assert_false + + +def test_tuple_contains(): + var a = (123, True, "Mojo is awesome") + + assert_true("Mojo is awesome" in a) + assert_true(a.__contains__("Mojo is awesome")) + + assert_false("Hello world" in a) + assert_false(a.__contains__("Hello world")) + + assert_true(123 in a) + assert_true(a.__contains__(123)) + + assert_true(True in a) + assert_true(a.__contains__(True)) + + assert_false(False in a) + assert_false(a.__contains__(False)) + + assert_false(a.__contains__(1)) + assert_false(a.__contains__(0)) + assert_false(1 in a) + assert_false(0 in a) + + var b = (False, True) + assert_true(True in b) + assert_true(b.__contains__(True)) + assert_true(False in b) + assert_true(b.__contains__(False)) + assert_false(b.__contains__(1)) + assert_false(b.__contains__(0)) + + var c = (1, 0) + assert_false(c.__contains__(True)) + assert_false(c.__contains__(False)) + assert_false(True in c) + assert_false(False in c) + + var d = (123, True, String("Mojo is awesome")) + + assert_true(String("Mojo is awesome") in d) + assert_true(d.__contains__(String("Mojo is awesome"))) + + assert_false(String("Hello world") in d) + assert_false(d.__contains__(String("Hello world"))) + + +def main(): + test_tuple_contains() From 3986dc8f5b7345ff0a7ba87a383636ae14604992 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 20 May 2024 13:43:12 -0500 Subject: [PATCH 0630/2019] [External] [docs] Add a note about pull request sizes in the contributing guide (#40021) [External] [docs] Add a note about pull request sizes in the contributing guide We're seeing bigger and bigger PRs. It would be good for the overall DX if contributors were aware of the effect of huge PRs on the whole PR process. Reviewers will be able to reference this section when faced with a pull request that can be split into multiple ones. I will avoid discussions between the reviewer and the contributor about who is the laziest of the two (I've already witness this situation in another project haha) Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2663 MODULAR_ORIG_COMMIT_REV_ID: 8e4d8bc88530c05b370dcb86c6c5c5eafd215729 --- CONTRIBUTING.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72c71b2eee..94b402dfb1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -132,6 +132,44 @@ accepted. For example: - Changes if contributors are not responsive. - Adding an entire new module without going through the RFC/proposal process. +### About pull request sizes + +We ask that contributors make pull requests as small as possible. When +you are opening a pull request, check the number of lines modified in GitHub. +The smaller the better (but don't exclude the tests or docstrings). If your +pull request is over 100 lines, please try to split it into multiple pull +requests. If you make them independant, it's even better as no synchronization +will be needed for the merge. + +This guideline is here for the following reasons: + +- **Higher quality reviews**: It is much easier to spot a bug in a few lines +than in 1000 lines. +- **Faster overall review**: Reviewers, to approve a pull request, need to +understand every line and understand how it fits into your overall change. +They also need to go back and forth between files and functions to understand +the flow of the code. This is exponentially hard as there are more lines in the code. +- **Avoiding blocking changes that are valid**: In a huge pull request, it's +likely that some changes are valid and some need to be reworked/discussed. If all +the changes are in the same pull request, then the valid changes will be be blocked +until all discussions have been resolved. +- **Reducing the number of git conflicts**: Bigger pull request means slower reviews, +thus means that the pull request will be open longer and will have more git conflicts +to be resolved before being merged. +- **Parallel processing**: All programmers like to parallelize. Well, reviewers also +like to parallelize code reviews to merge your code faster. If you open two pull +requests that are independent, then two reviewers will be able to work on your +code. +- **Finding the time for a code review**: Doing a code review often requires +that the code is reviewed in one go, as it's hard to remember functions and code +logic from one review session to another. Thus a big pull request will require +the reviewer to allocate a big chunk of time to do the code review, which is not +always possible and might delay the review and merge of your pull request +for multiple days. + +Smaller pull requests means less work for the maintainers and faster reviews +and merges for the contributors. It's a win-win! + ### Proposals If you’re interested in making a significant change—one that doesn’t fall into From 38bf43a2c2b0846d061c3284f00bd09bdd608faf Mon Sep 17 00:00:00 2001 From: Peyman Barazandeh Date: Mon, 20 May 2024 13:57:52 -0500 Subject: [PATCH 0631/2019] [External] [stdlib][NFC] Add unit tests for SIMD sub, rsub and isub operations (#40272) [External] [stdlib][NFC] Add unit tests for SIMD sub, rsub and isub operations Add unit tests for the `__sub__()`, `__rsub__()`, and `__isub__()` methods. Co-authored-by: Peyman Barazandeh Closes modularml/mojo#2763 MODULAR_ORIG_COMMIT_REV_ID: 12d16aeffeed2b8f1736b97ef9b771ecdd3110c0 --- stdlib/test/builtin/test_simd.mojo | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 9374592fb8..aff83b003a 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -234,6 +234,66 @@ def test_iadd(): assert_equal(f1, F(0, 0, 0, 0, 0, 0, 0, 0)) +def test_sub(): + alias I = SIMD[DType.int32, 4] + var i = I(-2, -4, 0, 1) + assert_equal(i.__sub__(0), I(-2, -4, 0, 1)) + assert_equal(i.__sub__(Int32(0)), I(-2, -4, 0, 1)) + assert_equal(i.__sub__(2), I(-4, -6, -2, -1)) + assert_equal(i.__sub__(Int32(2)), I(-4, -6, -2, -1)) + + var i1 = I(1, -4, -3, 2) + var i2 = I(2, 5, 3, 1) + assert_equal(i1.__sub__(i2), I(-1, -9, -6, 1)) + + alias F = SIMD[DType.float32, 8] + var f1 = F(1, -1, 1, -1, 1, -1, 1, -1) + var f2 = F(-1, 1, -1, 1, -1, 1, -1, 1) + assert_equal(f1.__sub__(f2), F(2, -2, 2, -2, 2, -2, 2, -2)) + + +def test_rsub(): + alias I = SIMD[DType.int32, 4] + var i = I(-2, -4, 0, 1) + assert_equal(i.__rsub__(0), I(2, 4, 0, -1)) + assert_equal(i.__rsub__(Int32(0)), I(2, 4, 0, -1)) + assert_equal(i.__rsub__(2), I(4, 6, 2, 1)) + assert_equal(i.__rsub__(Int32(2)), I(4, 6, 2, 1)) + + var i1 = I(1, -4, -3, 2) + var i2 = I(2, 5, 3, 1) + assert_equal(i1.__rsub__(i2), I(1, 9, 6, -1)) + + alias F = SIMD[DType.float32, 8] + var f1 = F(1, -1, 1, -1, 1, -1, 1, -1) + var f2 = F(-1, 1, -1, 1, -1, 1, -1, 1) + assert_equal(f1.__rsub__(f2), F(-2, 2, -2, 2, -2, 2, -2, 2)) + + +def test_isub(): + alias I = SIMD[DType.int32, 4] + var i = I(-2, -4, 0, 1) + i.__isub__(0) + assert_equal(i, I(-2, -4, 0, 1)) + i.__isub__(Int32(0)) + assert_equal(i, I(-2, -4, 0, 1)) + i.__isub__(2) + assert_equal(i, I(-4, -6, -2, -1)) + i.__isub__(I(0, -2, 2, 3)) + assert_equal(i, I(-4, -4, -4, -4)) + + var i1 = I(1, -4, -3, 2) + var i2 = I(2, 5, 3, 1) + i1.__isub__(i2) + assert_equal(i1, I(-1, -9, -6, 1)) + + alias F = SIMD[DType.float32, 8] + var f1 = F(1, -1, 1, -1, 1, -1, 1, -1) + var f2 = F(-1, 1, -1, 1, -1, 1, -1, 1) + f1.__isub__(f2) + assert_equal(f1, F(2, -2, 2, -2, 2, -2, 2, -2)) + + def test_ceil(): assert_equal(Float32.__ceil__(Float32(1.5)), 2.0) assert_equal(Float32.__ceil__(Float32(-1.5)), -1.0) @@ -1034,6 +1094,7 @@ def main(): test_insert() test_interleave() test_issue_20421() + test_isub() test_join() test_len() test_limits() @@ -1046,9 +1107,11 @@ def main(): test_rotate() test_round() test_roundeven() + test_rsub() test_shift() test_shuffle() test_simd_variadic() + test_sub() test_sub_with_overflow() test_trunc() test_truthy() From a2a69f1ecba16dad10a283fdd281dad6126d3247 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 21 May 2024 02:58:22 +0800 Subject: [PATCH 0632/2019] This reimplements the unroll function in terms of the `@parameter unroll` and is a staging for eventual deletion of the function. MODULAR_ORIG_COMMIT_REV_ID: 805b60a551021f8f1bff6bf93e6422144db887ef --- stdlib/src/utils/loop.mojo | 43 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index 28fa732cc1..0be2f644dc 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -38,7 +38,10 @@ fn unroll[ argument, which is the loop index value. count: A number of repetitions. """ - _unroll_impl[func, 0, count]() + + @parameter + for i in range(count): + func[i]() @always_inline @@ -56,18 +59,6 @@ fn unroll[ _unroll_impl[func, 0, count]() -@always_inline -fn _unroll_impl[ - func: fn[idx: Int] () capturing -> None, - idx: Int, - count: Int, -](): - @parameter - if idx < count: - func[idx]() - _unroll_impl[func, idx + 1, count]() - - @always_inline fn _unroll_impl[ func: fn[idx: Int] () raises capturing -> None, @@ -100,17 +91,12 @@ fn unroll[ dim1: The second dimension size. """ - @always_inline @parameter - fn outer_func_wrapper[idx0: Int](): - @always_inline - @parameter - fn inner_func_wrapper[idx1: Int](): - func[idx0, idx1]() - - unroll[inner_func_wrapper, dim1]() + for i in range(dim0): - unroll[outer_func_wrapper, dim0]() + @parameter + for j in range(dim1): + func[i, j]() # ===----------------------------------------------------------------------===# @@ -135,11 +121,12 @@ fn unroll[ dim2: The second dimension size. """ - @always_inline @parameter - fn func_wrapper[idx0: Int, idx1: Int](): - alias _idx1 = idx1 // dim2 - alias _idx2 = idx1 % dim2 - func[idx0, _idx1, _idx2]() + for i in range(dim0): + + @parameter + for j in range(dim1): - unroll[func_wrapper, dim0, dim1 * dim2]() + @parameter + for k in range(dim2): + func[i, j, k]() From 7e56a542b6b0146bba9117cd34674574529c0d41 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 20 May 2024 14:14:42 -0500 Subject: [PATCH 0633/2019] [External] [tools] Allow to choose the test files when using `run-tests.sh` (#40225) [External] [tools] Allow to choose the test files when using `run-tests.sh` Also rectified a small mistake in the guide: unlike what was written previously, the contributors don't need to run `lit` after using the script `run-tests.sh`. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2737 MODULAR_ORIG_COMMIT_REV_ID: f0013a6d9c0903bed9392545555810607ec7d8cf --- stdlib/docs/development.md | 17 ++++++++++++++--- stdlib/scripts/run-tests.sh | 9 ++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index d303a4590d..e5426a4e9c 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -66,13 +66,24 @@ We provide a simple Bash script to build the standard library package and `test_utils` package that is used by the test suite. Just run `./stdlib/scripts/run-tests.sh` which will produce the necessary -`mojopkg` files inside your `build` directory, and then run `lit -sv -stdlib/test`. +`mojopkg` files inside your `build` directory, after this is done, the script will +run all the tests automatically. ```bash ./stdlib/scripts/run-tests.sh +``` + +If you wish to run the unit tests that are in a specific test file, you can do +so with -lit -sv stdlib/test +```bash +./stdlib/scripts/run-tests.sh ./stdlib/test/utils/test_span.mojo +``` + +You can do the same for a directory with + +```bash +./stdlib/scripts/run-tests.sh ./stdlib/test/utils ``` All the tests should pass on the `nightly` branch with the nightly Mojo diff --git a/stdlib/scripts/run-tests.sh b/stdlib/scripts/run-tests.sh index eab72cdc19..e228135c99 100755 --- a/stdlib/scripts/run-tests.sh +++ b/stdlib/scripts/run-tests.sh @@ -27,4 +27,11 @@ echo "Packaging up the test_utils." TEST_UTILS_PATH="${REPO_ROOT}/stdlib/test/test_utils" mojo package "${TEST_UTILS_PATH}" -o "${BUILD_DIR}/test_utils.mojopkg" -lit -sv "${REPO_ROOT}"/stdlib/test +TEST_PATH="${REPO_ROOT}/stdlib/test" +if [[ $# -gt 0 ]]; then + # If an argument is provided, use it as the specific test file or directory + TEST_PATH=$1 +fi + +# Run the tests +lit -sv "${TEST_PATH}" From 213f46177cf4bff0aec51a99dbde9d4c0a008137 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 20 May 2024 12:51:45 -0700 Subject: [PATCH 0634/2019] We believe this was fixed by a compiler change, re-enabling to see if we see any new flakes. MODULAR_ORIG_COMMIT_REV_ID: 9da4654746f2c7f89d70874ec08bd4e75c516cc4 --- stdlib/test/builtin/test_reversed.mojo | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index da9de000cf..1c08dc2ac0 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -10,8 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: disabled -# https://github.com/modularml/mojo/issues/2369 # RUN: %mojo %s from testing import assert_equal From 6ad072493a31d5b1865cb83a5e56fff789d8f37b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 20 May 2024 16:47:20 -0500 Subject: [PATCH 0635/2019] [stdlib] Support testing on `mojo:main` branch The current test configs cover the `nightly` release but not the stable release. This change updates the configs so that tests can be run with the latest `mojo` release. MODULAR_ORIG_COMMIT_REV_ID: 3491c0bdff8ff670d80aadfd99876fb3a7c13515 --- stdlib/test/lit.cfg.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index ca694eaae0..54dceae3fe 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -49,11 +49,13 @@ # to allow running the tests with LLVM sanitizers. config.substitutions.insert(0, ("%mojo", "mojo")) -# The `mojo` nightly compiler ships with its own `stdlib.mojopkg` -# For the open-source stdlib, we need to specify the paths to the -# just-built `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, -# without this, the `mojo` compiler would use its own `stdlib.mojopkg` -# it ships with which is not what we want. +# The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the +# open-source stdlib, we need to specify the paths to the just-built +# `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the +# `mojo` compiler would use its own `stdlib.mojopkg` it ships with which is not +# what we want. We override both the stable and nightly `mojo` import paths +# here to support both versions of the compiler. +os.environ["MODULAR_MOJO_IMPORT_PATH"] = str(build_root) os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = str(build_root) @@ -71,6 +73,7 @@ def has_not(): lit.llvm.llvm_config.with_system_environment( [ "MODULAR_HOME", + "MODULAR_MOJO_IMPORT_PATH", "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", ] ) From c34a99809759efa9d96f64229df0552d284bf5f2 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 20 May 2024 17:55:49 -0500 Subject: [PATCH 0636/2019] [External] [stdlib] Fix out of bounds access in `List.index()` (#40297) [External] [stdlib] Fix out of bounds access in `List.index()` Related to https://github.com/modularml/mojo/issues/2687 There were multiple bugs related to clipping there. Long story short, the behavior of `list.index()` in python is this one: given a `start` and `end`, python will look for the element in `my_list[start:end]` and report the result, (`start` is added to the result to give the index with respect to the original list). You can take a look at the description of the `index` method here: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists Since there is slicing semantics applied to `start` and `end`, we should do multiple things: 1) default to the start and the end of the list 2) normalize negative values by doing `+ len(my_list)` 3) clip both start and end between `0` and `len(my_list)` The last step wasn't done correctly. Especially for the `stop` argument where the clipping was applied only when negative values were found (this caused the out of bounds bug in the tests). Effectively ```mojo return end if end > 0 else min(end + size, size) ``` is equivalent to ```mojo return end if end > 0 else end + size ``` since the min is applied only when `end <= 0`. So `end + size <= size` This test can cause some flakyness in our CI: `test_list_a.index(10, start=5, stop=50)` for a list of size 6. The stop was positive, so it was never clipped, thus too many values (out of bounds) were tried, causing some to match, sometimes. Another bug was that because of this condition, `end = 0` means "check until the end of the list" while in python, with the slicing semantics, `end = 0` means "do nothing" (empty slice). ### TL;DR Multiple clipping bugs. Slicing semantics applied to `start` and `stop` now like in Python. No more flakyness in our CI. No more out of bounds access. Corresponding tests added. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2745 MODULAR_ORIG_COMMIT_REV_ID: 7bad2830dc41d96ae383fc4a8eac9ca3a69581de --- stdlib/src/collections/list.mojo | 32 +++++++++++++++++--------- stdlib/test/collections/test_list.mojo | 10 ++++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index ffe4a97b64..4606e0b139 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -519,18 +519,24 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): Raises: ValueError: If the value is not found in the list. """ - var size = self[].size - var normalized_start = max(size + start, 0) if start < 0 else start + var start_normalized = start - @parameter - fn normalized_stop() -> Int: - if stop is None: - return size - else: - var end = stop.value()[] - return end if end > 0 else min(end + size, size) - - for i in range(normalized_start, normalized_stop()): + var stop_normalized: Int + if stop is None: + # Default end + stop_normalized = len(self[]) + else: + stop_normalized = stop.value()[] + + if start_normalized < 0: + start_normalized += len(self[]) + if stop_normalized < 0: + stop_normalized += len(self[]) + + start_normalized = _clip(start_normalized, 0, len(self[])) + stop_normalized = _clip(stop_normalized, 0, len(self[])) + + for i in range(start_normalized, stop_normalized): if self[][i] == value: return i raise "ValueError: Given element is not in list" @@ -814,3 +820,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if i[] == rebind[T2](value): return True return False + + +fn _clip(value: Int, start: Int, end: Int) -> Int: + return max(start, min(value, end)) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 052b90024b..ae0e6851f1 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -392,6 +392,7 @@ def test_list_index(): # Tests With Start Parameter assert_equal(test_list_a.index(30, start=1), 2) assert_equal(test_list_a.index(30, start=-4), 2) + assert_equal(test_list_a.index(30, start=-1000), 2) with assert_raises(contains="ValueError: Given element is not in list"): _ = test_list_a.index(30, start=3) with assert_raises(contains="ValueError: Given element is not in list"): @@ -400,6 +401,7 @@ def test_list_index(): # Tests With Start and End Parameters assert_equal(test_list_a.index(30, start=1, stop=3), 2) assert_equal(test_list_a.index(30, start=-4, stop=-2), 2) + assert_equal(test_list_a.index(30, start=-1000, stop=1000), 2) with assert_raises(contains="ValueError: Given element is not in list"): _ = test_list_a.index(30, start=1, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): @@ -408,6 +410,7 @@ def test_list_index(): # Tests With End Parameter Only assert_equal(test_list_a.index(30, stop=3), 2) assert_equal(test_list_a.index(30, stop=-2), 2) + assert_equal(test_list_a.index(30, stop=1000), 2) with assert_raises(contains="ValueError: Given element is not in list"): _ = test_list_a.index(30, stop=1) with assert_raises(contains="ValueError: Given element is not in list"): @@ -429,6 +432,13 @@ def test_list_index(): with assert_raises(contains="ValueError: Given element is not in list"): _ = List[Int]().index(10) + # Test empty slice + with assert_raises(contains="ValueError: Given element is not in list"): + _ = test_list_a.index(10, start=1, stop=1) + # Test empty slice with 0 start and end + with assert_raises(contains="ValueError: Given element is not in list"): + _ = test_list_a.index(10, start=0, stop=0) + var test_list_b = List[Int](10, 20, 30, 20, 10) # Test finding the first occurrence of an item From da1bff82a54380b71938adc1dee8181a218f273c Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 20 May 2024 18:02:53 -0500 Subject: [PATCH 0637/2019] [External] [stdlib] Fix wrong debug_assert condition in `InlineList` (#40303) [External] [stdlib] Fix wrong debug_assert condition in `InlineList` If the InlineList is of capacity N, then it's possible to initialize it with N values. It's the same as a regular List. Related to https://github.com/modularml/mojo/issues/2687 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2744 MODULAR_ORIG_COMMIT_REV_ID: 8f5093eb1eb79591e9636f9a7a969037bab6dcd1 --- stdlib/src/collections/inline_list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 281bbba3d5..ada4f64126 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -106,7 +106,7 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): Args: values: The values to populate the list with. """ - debug_assert(len(values) < capacity, "List is full.") + debug_assert(len(values) <= capacity, "List is full.") self = Self() for value in values: self.append(value[]) From 8837d539e4cddb21879d70bd6dce75e08fbb4b12 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 20 May 2024 18:21:10 -0500 Subject: [PATCH 0638/2019] [External] [stdlib] Allow natural list.__repr__() syntax for List (#40304) [External] [stdlib] Allow natural list.__repr__() syntax for List No need for the `@staticmethod` and the `__type_of()` trick in `List.__repr__()` Co-authored-by: Manuel Saelices Closes modularml/mojo#2733 MODULAR_ORIG_COMMIT_REV_ID: 50d64ae606e89dde9f0b5db575f472cf89fbfca5 --- stdlib/src/collections/list.mojo | 8 ++------ stdlib/test/builtin/test_list.mojo | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 4606e0b139..b23f30d128 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -728,7 +728,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): result += "]" return result - @staticmethod fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. Note that since we can't condition methods on a trait yet, @@ -736,7 +735,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): ```mojo var my_list = List[Int](1, 2, 3) - print(__type_of(my_list).__repr__(my_list)) + print(my_list.__repr__(my_list)) ``` When the compiler supports conditional methods, then a simple `repr(my_list)` will @@ -744,9 +743,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): The elements' type must implement the `__repr__()` for this to work. - Args: - self: The list to represent as a string. - Parameters: U: The type of the elements in the list. Must implement the traits `Representable` and `CollectionElement`. @@ -754,7 +750,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): Returns: A string representation of the list. """ - return __type_of(self).__str__(self) + return self.__str__() fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index a3f028cfbc..5718d15483 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -35,9 +35,9 @@ fn test_variadic_list() raises: fn test_repr_list() raises: var l = List(1, 2, 3) - assert_equal(__type_of(l).__repr__(l), "[1, 2, 3]") + assert_equal(l.__repr__(), "[1, 2, 3]") var empty = List[Int]() - assert_equal(__type_of(empty).__repr__(empty), "[]") + assert_equal(empty.__repr__(), "[]") def main(): From dcdccdb6886ce18add249329adff716e4da612cc Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 20 May 2024 18:23:47 -0500 Subject: [PATCH 0639/2019] [External] [stdlib] Implement `os.path.getsize` (#40291) [External] [stdlib] Implement `os.path.getsize` Add `getsize` method for getting the size (in bytes) of a path. Fixes https://github.com/modularml/mojo/issues/1130 ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=https://github.com/modularml/mojo/pull/2626 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes https://github.com/modularml/mojo/pull/2626 MODULAR_ORIG_COMMIT_REV_ID: 443260f3be31a694c9e93fc11b64d0317646c4e0 --- docs/changelog.md | 3 ++ stdlib/src/os/path/__init__.mojo | 2 +- stdlib/src/os/path/path.mojo | 43 +++++++++++++++++++++++---- stdlib/test/os/path/test_getsize.mojo | 31 +++++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 stdlib/test/os/path/test_getsize.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 8ee85c71c9..b9f8a5f4e7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -354,6 +354,9 @@ what we publish. print("x contains 1") ``` +- Added `os.getsize` function, which gives the size in bytes of a path. + ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index 89d43e06ea..98b9c2411b 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -11,4 +11,4 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from .path import exists, isdir, isfile, islink, lexists +from .path import exists, isdir, isfile, islink, lexists, getsize diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 7d9e030d81..8f5010dcd1 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -29,6 +29,7 @@ from .._linux_x86 import _lstat as _lstat_linux_x86 from .._linux_x86 import _stat as _stat_linux_x86 from .._macos import _lstat as _lstat_macos from .._macos import _stat as _stat_macos +from ..fstat import stat # ===----------------------------------------------------------------------=== # @@ -91,7 +92,7 @@ fn isdir[pathlike: os.PathLike](path: pathlike) -> Bool: symbolic links, so both islink() and isdir() can be true for the same path. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + pathlike: The type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -131,7 +132,7 @@ fn isfile[pathlike: os.PathLike](path: pathlike) -> Bool: """Test whether a path is a regular file. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + pathlike: The type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -167,7 +168,7 @@ fn islink[pathlike: os.PathLike](path: pathlike) -> Bool: symbolic link. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + pathlike: The type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -204,7 +205,7 @@ fn exists[pathlike: os.PathLike](path: pathlike) -> Bool: """Return True if path exists. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + pathlike: The type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -241,7 +242,7 @@ fn lexists[pathlike: os.PathLike](path: pathlike) -> Bool: """Return True if path exists or is a broken symlink. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + pathlike: The type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -250,3 +251,35 @@ fn lexists[pathlike: os.PathLike](path: pathlike) -> Bool: Returns True if the path exists or is a broken symbolic link. """ return exists(path.__fspath__()) + + +# ===----------------------------------------------------------------------=== # +# getsize +# ===----------------------------------------------------------------------=== # + + +fn getsize(path: String) raises -> Int: + """Return the size, in bytes, of the specified path. + + Args: + path: The path to the file. + + Returns: + The size of the path in bytes. + """ + return stat(path).st_size + + +fn getsize[pathlike: os.PathLike](path: pathlike) raises -> Int: + """Return the size, in bytes, of the specified path. + + Parameters: + pathlike: The type conforming to the os.PathLike trait. + + Args: + path: The path to the file. + + Returns: + The size of the path in bytes. + """ + return getsize(path.__fspath__()) diff --git a/stdlib/test/os/path/test_getsize.mojo b/stdlib/test/os/path/test_getsize.mojo new file mode 100644 index 0000000000..d9b28961ef --- /dev/null +++ b/stdlib/test/os/path/test_getsize.mojo @@ -0,0 +1,31 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +import os +from os.path import getsize + +from testing import assert_equal, assert_false + + +fn main() raises: + # TODO: use `NamedTemporaryFile` once we implement it. + alias file_name = "test_file" + assert_false(os.path.exists(file_name), "File should not exist") + with open(file_name, "w"): + pass + assert_equal(getsize(file_name), 0) + with open(file_name, "w") as my_file: + my_file.write("test") + assert_equal(getsize(file_name), 4) + os.remove(file_name) From 8a2a5463c248f4b21252902e0403118d6f82944c Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Mon, 20 May 2024 18:36:02 -0500 Subject: [PATCH 0640/2019] [External] [stdlib] Add contains to `InlineArray` (#40305) [External] [stdlib] Add contains to `InlineArray` Add functionality for checking if a value is contained within an `InlineArray`. Co-authored-by: Lukas Hermann Closes modularml/mojo#2699 MODULAR_ORIG_COMMIT_REV_ID: faec0367fdc4b054c10cda0b7eb6ceded7e675e0 --- stdlib/src/utils/static_tuple.mojo | 34 ++++++++++++++++++++++++++++++ stdlib/test/utils/test_tuple.mojo | 7 ++++++ 2 files changed, 41 insertions(+) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index c588d1cd28..9660535d58 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -22,6 +22,7 @@ from utils import StaticTuple from memory import Pointer from utils import unroll +from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# # Utilities @@ -432,3 +433,36 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): An `UnsafePointer` to the underlying array. """ return UnsafePointer(self._array).bitcast[Self.ElementType]() + + @always_inline + fn __contains__[ + T: ComparableCollectionElement + ](self: Reference[InlineArray[T, size]], value: Self.ElementType) -> Bool: + """Verify if a given value is present in the array. + + ```mojo + from utils import InlineArray + var x = InlineArray[Int, 3](1,2,3) + if 3 in x: print("x contains 3") + ``` + + Parameters: + T: The type of the elements in the array. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + value: The value to find. + + Returns: + True if the value is contained in the array, False otherwise. + """ + constrained[ + _type_is_eq[T, Self.ElementType](), + "T must be equal to Self.ElementType", + ]() + + # TODO: use @parameter for soon once it stabilizes a bit + for i in range(size): + if self[][i] == rebind[T](value): + return True + return False diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 275528d507..d80a5902b4 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -189,6 +189,12 @@ def test_array_int_pointer(): _ = arr +def test_array_contains(): + var arr = InlineArray[String, 3]("hi", "hello", "hey") + assert_true(str("hi") in arr) + assert_true(not str("greetings") in arr) + + def main(): test_static_tuple() test_static_int_tuple() @@ -196,3 +202,4 @@ def main(): test_array_int() test_array_str() test_array_int_pointer() + test_array_contains() From ac1abafce5f66be055fee5176786f7003c80ed11 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 20 May 2024 19:15:16 -0500 Subject: [PATCH 0641/2019] [External] [stdlib] Fix invalid code point check (#40295) [External] [stdlib] Fix invalid code point check Related to https://github.com/modularml/mojo/issues/2687 See https://stackoverflow.com/questions/27415935/does-unicode-have-a-defined-maximum-number-of-code-points for the correct range. You can also use Python's `chr` function to make sure the range is correct Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2747 MODULAR_ORIG_COMMIT_REV_ID: e93765fdad9876ba4b7838194182bf72c5fb1c71 --- stdlib/src/builtin/string.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index a9c5ed0136..dd7ffea345 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -95,7 +95,9 @@ fn chr(c: Int) -> String: @always_inline fn _utf8_len(val: Int) -> Int: - debug_assert(val > 0x10FFFF, "Value is not a valid Unicode code point") + debug_assert( + 0 <= val <= 0x10FFFF, "Value is not a valid Unicode code point" + ) alias sizes = SIMD[DType.int32, 4]( 0, 0b1111_111, 0b1111_1111_111, 0b1111_1111_1111_1111 ) From 180ad862e60bc44f694c5114298f196ebd175fea Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 20 May 2024 19:24:46 -0500 Subject: [PATCH 0642/2019] [External] [stdlib] Add `Dict.fromkeys()` (#40292) [External] [stdlib] implement fromkeys in Dict Add `fromkeys()` method to `Dict` which returns a `Dict` with the specified keys and value. Fixes https://github.com/modularml/mojo/issues/2546 ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2622 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2622 MODULAR_ORIG_COMMIT_REV_ID: 4f43dc59a68509d8ae781215e0f0956e560fa38e --- docs/changelog.md | 4 +++ stdlib/src/collections/dict.mojo | 34 +++++++++++++++++++++++++ stdlib/test/collections/test_dict.mojo | 35 ++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b9f8a5f4e7..13f6bea6fb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -357,6 +357,10 @@ what we publish. - Added `os.getsize` function, which gives the size in bytes of a path. ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) +- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys + and value. + ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 7fe0fe7d7a..46d00001df 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -464,6 +464,40 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = existing._index.copy(existing._reserved) self._entries = existing._entries + @staticmethod + fn fromkeys(keys: List[K], value: V) -> Self: + """Create a new dictionary with keys from list and values set to value. + + Args: + keys: The keys to set. + value: The value to set. + + Returns: + The new dictionary. + """ + var dict = Dict[K, V]() + for key in keys: + dict[key[]] = value + return dict + + @staticmethod + fn fromkeys( + keys: List[K], value: Optional[V] = None + ) -> Dict[K, Optional[V]]: + """Create a new dictionary with keys from list and values set to value. + + Args: + keys: The keys to set. + value: The value to set. + + Returns: + The new dictionary. + """ + var dict = Dict[K, Optional[V]]() + for key in keys: + dict[key[]] = value + return dict + fn __copyinit__(inout self, existing: Self): """Copy an existing dictiontary. diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index bcd71b1699..86748a6c9b 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -25,6 +25,39 @@ def test_dict_construction(): _ = Dict[String, Int]() +def test_dict_fromkeys(): + alias keys = List[String]("a", "b") + var expected_dict = Dict[String, Int]() + expected_dict["a"] = 1 + expected_dict["b"] = 1 + var dict = Dict.fromkeys(keys, 1) + + assert_equal(len(dict), len(expected_dict)) + + for k_v in expected_dict.items(): + var k = k_v[].key + var v = k_v[].value + assert_true(k in dict) + assert_equal(dict[k], v) + + +def test_dict_fromkeys_optional(): + alias keys = List[String]("a", "b", "c") + var expected_dict = Dict[String, Optional[Int]]() + expected_dict["a"] = None + expected_dict["b"] = None + expected_dict["c"] = None + var dict = Dict[_, Int].fromkeys(keys) + + assert_equal(len(dict), len(expected_dict)) + + for k_v in expected_dict.items(): + var k = k_v[].key + var v = k_v[].value + assert_true(k in dict) + assert_false(v) + + def test_basic(): var dict = Dict[String, Int]() dict["a"] = 1 @@ -481,6 +514,8 @@ def test_find_get(): def main(): test_dict() + test_dict_fromkeys() + test_dict_fromkeys_optional() test_dict_string_representation_string_int() test_dict_string_representation_int_int() test_owned_kwargs_dict() From 518e1bce3ce8d151b59d55a7c9a7f0c8628716b5 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 20 May 2024 19:45:02 -0500 Subject: [PATCH 0643/2019] [External] [stdlib] Replace some uses of `StaticIntTuple` (#39743) [External] [stdlib] Replace some uses of `StaticIntTuple` Replace some uses of `StaticIntTuple` with `InlineArray`. Part of https://github.com/modularml/mojo/issues/2515. Note: - Some uses in `SIMD` couldn't be applied internally, so were backed off from the original https://github.com/modularml/mojo/pull/2525 ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2525 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2525 MODULAR_ORIG_COMMIT_REV_ID: 2788a23f4e6f565cdf745320da3fd9a372b2bc4e --- stdlib/src/builtin/string.mojo | 5 +---- stdlib/src/python/_cpython.mojo | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index dd7ffea345..45c84ce584 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1167,13 +1167,10 @@ struct String( UnsafePointer.address_of(self).bitcast[NoneType](), ) - fn join[rank: Int](self, elems: StaticIntTuple[rank]) -> String: + fn join(self, *elems: Int) -> String: """Joins the elements from the tuple using the current string as a delimiter. - Parameters: - rank: The size of the tuple. - Args: elems: The input tuple. diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 40114ce151..dd42a88c98 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -17,7 +17,7 @@ from sys.ffi import DLHandle from memory import DTypePointer, UnsafePointer -from utils import StringRef, StaticIntTuple +from utils import StringRef, InlineArray # https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/compile.h#L7 alias Py_single_input = 256 @@ -68,7 +68,7 @@ struct PythonVersion: fn __init__(version: StringRef) -> PythonVersion: var version_string = String(version) - var components = StaticIntTuple[3]() + var components = InlineArray[Int, 3](-1) var start = 0 var next_idx = 0 var i = 0 From 971080750065444940b5c894ea071bde810192e3 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 20 May 2024 17:59:03 -0700 Subject: [PATCH 0644/2019] [mojo] Improve implementation of `@parameter for` This PR improves the implementation of `@parameter for` to support early exits (return, break, continue, raise, etc.) and the `else` region. MODULAR_ORIG_COMMIT_REV_ID: c3ba6d88f2d4c999d989edf6942cafdc8d8ed449 --- docs/changelog.md | 6 +-- stdlib/src/builtin/_stubs.mojo | 78 ++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 13f6bea6fb..5a09b4a4e8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -32,9 +32,9 @@ what we publish. print("found 10!") ``` - Currently, `@parameter for` does not allow early exits in the body (`return`, - `break`, `continue`, etc.) and requires the sequence's `__iter__` method to - return a `_StridedRangeIterator`. These restrictions will be lifted soon. + Currently, `@parameter for` requires the sequence's `__iter__` method to + return a `_StridedRangeIterator`, meaning the induction variables must be + `Int`. The intention is to lift these restrictions in the future. - Mojo added support for the `inferred` passing kind on parameters. `inferred` parameters must appear at the beginning of the parameter list and cannot be diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index 758dcc8c16..089ef9b67a 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -48,40 +48,44 @@ trait _StridedIterable(_IntIter): ... -@value -struct _NextEval[T: _IntNext]: - var iter: T - var i: Int - - -fn _next_eval[T: _IntNext](iter: T) -> _NextEval[T]: - var copy = iter - var next = copy.__next__() - return _NextEval(copy, next) - - -@always_inline -fn _gen_next[ - inferred T: _IntIter, iter: T, f: fn[i: Int] () capturing -> None -](): - @parameter - if iter.__len__() == 0: - return - else: - alias next = _next_eval(iter) - f[next.i]() - _gen_next[next.iter, f]() - - -@always_inline -fn parameter_for[ - inferred T: _IntIterable, iter: T, f: fn[i: Int] () capturing -> None -](): - _gen_next[iter.__iter__(), f]() - - -@always_inline -fn parameter_for[ - inferred T: _StridedIterable, iter: T, f: fn[i: Int] () capturing -> None -](): - _gen_next[iter.__iter__(), f]() +struct _ParamForIterator[IteratorT: Copyable]: + var next_it: IteratorT + var value: Int + var stop: Bool + + fn __init__(inout self, next_it: IteratorT, value: Int, stop: Bool): + self.next_it = next_it + self.value = value + self.stop = stop + + +fn declval[T: AnyType]() -> T: + constrained[False, "should only be used inside __type_of"]() + while True: + pass + + +fn parameter_for_generator[ + T: _IntIterable, +](range: T) -> _ParamForIterator[__type_of(declval[T]().__iter__())]: + return _generator(range.__iter__()) + + +fn parameter_for_generator[ + T: _StridedIterable, +](range: T) -> _ParamForIterator[__type_of(declval[T]().__iter__())]: + return _generator(range.__iter__()) + + +fn _generator[ + IteratorT: _IntIter +](it: IteratorT) -> _ParamForIterator[IteratorT]: + if it.__len__() == 0: + return _ParamForIterator[IteratorT]( + __mlir_attr[`#kgen.unknown : !kgen.paramref<`, IteratorT, `>`], + 0, + True, + ) + var next_it = it + var value = next_it.__next__() + return _ParamForIterator(next_it, value, False) From 8912edb30bab407178ad313e7544a90528535dca Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 20 May 2024 21:21:20 -0700 Subject: [PATCH 0645/2019] [Docs] Add temporary bencher API docs. Add bencher API docs to stdlib docs as a temporary workaround until 24.4 launches. MODULAR_ORIG_COMMIT_REV_ID: 393ab6f5e68b25486f810d4abe70c42046a7da39 --- stdlib/docs/bencher/Bench.md | 174 +++++++++++++++++++++++++++ stdlib/docs/bencher/BenchConfig.md | 78 ++++++++++++ stdlib/docs/bencher/BenchId.md | 62 ++++++++++ stdlib/docs/bencher/Bencher.md | 117 ++++++++++++++++++ stdlib/docs/bencher/BenchmarkInfo.md | 51 ++++++++ stdlib/docs/bencher/Mode.md | 52 ++++++++ stdlib/docs/bencher/index.md | 39 ++++++ 7 files changed, 573 insertions(+) create mode 100644 stdlib/docs/bencher/Bench.md create mode 100644 stdlib/docs/bencher/BenchConfig.md create mode 100644 stdlib/docs/bencher/BenchId.md create mode 100644 stdlib/docs/bencher/Bencher.md create mode 100644 stdlib/docs/bencher/BenchmarkInfo.md create mode 100644 stdlib/docs/bencher/Mode.md create mode 100644 stdlib/docs/bencher/index.md diff --git a/stdlib/docs/bencher/Bench.md b/stdlib/docs/bencher/Bench.md new file mode 100644 index 0000000000..363b10e414 --- /dev/null +++ b/stdlib/docs/bencher/Bench.md @@ -0,0 +1,174 @@ +--- +title: Bench +version: 0.0.0 +slug: Bench +type: struct +namespace: benchmark.bencher +--- + +
+ +Defines the main Benchmark struct which executes a Benchmark and print result. + +## Fields + +- ​config (`BenchConfig`): Constructs a Benchmark object based on specific + configuration and mode. +- ​mode (`Mode`): Benchmark mode object representing benchmark or test + mode. +- ​info_vec (`List[BenchmarkInfo]`): A list containing the bencmark info. + +## Implemented traits + +`AnyType`, +`Copyable`, +`Movable` + +## Methods + +### `__init__` + +
+ +
+ +```mojo +__init__(inout self: Self, config: Optional[BenchConfig] = #kgen.none, mode: Mode = 0) +``` + +
+ +Constructs a Benchmark object based on specific configuration and mode. + +**Args:** + +- ​config (`Optional[BenchConfig]`): Benchmark configuration object to + control length and frequency of benchmarks. +- ​mode (`Mode`): Benchmark mode object representing benchmark or test + mode. + +
+ +### `bench_with_input` + +
+ +
+ +```mojo +bench_with_input[T: AnyType, bench_fn: fn(inout Bencher, $0) capturing -> None](inout self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) +``` + +
+ +Benchmarks an input function with input args of type AnyType. + +**Parameters:** + +- ​T (`AnyType`): Benchmark function input type. +- ​bench_fn (`fn(inout Bencher, $0) capturing -> None`): The function to + be benchmarked. + +**Args:** + +- ​bench_id (`BenchId`): The benchmark Id object used for identification. +- ​input (`T`): Represents the target function's input arguments. +- ​throughput_elems (`Optional[Int]`): Optional argument representing + algorithmic throughput. + +
+ +
+ +
+ +```mojo +bench_with_input[T: AnyRegType, bench_fn: fn(inout Bencher, $0) capturing -> None](inout self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) +``` + +
+ +Benchmarks an input function with input args of type AnyRegType. + +**Parameters:** + +- ​T (`AnyRegType`): Benchmark function input type. +- ​bench_fn (`fn(inout Bencher, $0) capturing -> None`): The function to + be benchmarked. + +**Args:** + +- ​bench_id (`BenchId`): The benchmark Id object used for identification. +- ​input (`T`): Represents the target function's input arguments. +- ​throughput_elems (`Optional[Int]`): Optional argument representing + algorithmic throughput. + +
+ +### `bench_function` + +
+ +
+ +```mojo +bench_function[bench_fn: fn(inout Bencher) capturing -> None](inout self: Self, bench_id: BenchId, throughput_elems: Optional[Int] = #kgen.none) +``` + +
+ +Benchmarks or Tests an input function. + +**Parameters:** + +- ​bench_fn (`fn(inout Bencher) capturing -> None`): The function to be + benchmarked. + +**Args:** + +- ​bench_id (`BenchId`): The benchmark Id object used for identification. +- ​throughput_elems (`Optional[Int]`): Optional argument representing + algorithmic throughput. + +
+ +
+ +
+ +```mojo +bench_function[bench_fn: fn(inout Bencher) raises capturing -> None](inout self: Self, bench_id: BenchId, throughput_elems: Optional[Int] = #kgen.none) +``` + +
+ +Benchmarks or Tests an input function. + +**Parameters:** + +- ​bench_fn (`fn(inout Bencher) raises capturing -> None`): The function + to be benchmarked. + +**Args:** + +- ​bench_id (`BenchId`): The benchmark Id object used for identification. +- ​throughput_elems (`Optional[Int]`): Optional argument representing + algorithmic throughput. + +
+ +### `dump_report` + +
+ +
+ +`dump_report(self: Self)` + +
+ +Prints out the report from a Benchmark execution. + +
+ +
diff --git a/stdlib/docs/bencher/BenchConfig.md b/stdlib/docs/bencher/BenchConfig.md new file mode 100644 index 0000000000..6c65296515 --- /dev/null +++ b/stdlib/docs/bencher/BenchConfig.md @@ -0,0 +1,78 @@ +--- +title: BenchConfig +version: 0.0.0 +slug: BenchConfig +type: struct +namespace: benchmark.bencher +--- + +
+ +Defines a benchmark configuration struct to control execution times and +frequency. + +## Fields + +- ​out_file (`Optional[Path]`): Output file to write results to. +- ​min_runtime_secs (`SIMD[float64, 1]`): Upper bound on benchmarking time + in secs. +- ​max_runtime_secs (`SIMD[float64, 1]`): Lower bound on benchmarking time + in secs. +- ​max_batch_size (`Int`): The maximum number of iterations to perform per + time measurement. +- ​max_iters (`Int`): Max number of iterations to run. +- ​warmup_iters (`Int`): Number of warmup iterations to run before + starting benchmarking. +- ​num_repetitions (`Int`): Number of times the benchmark has to be + repeated. +- ​flush_denormals (`Bool`): Whether or not the denormal values are + flushed. +- ​show_progress (`Bool`): Whether or not to show the progress of each + benchmark. +- ​tabular_view (`Bool`): Whether to print results in csv readable/tabular + format. + +## Implemented traits + +`AnyType`, +`CollectionElement`, +`Copyable`, +`Movable` + +## Methods + +### `__init__` + +
+ +
+ +```mojo +__init__(inout self: Self, out_file: Optional[Path] = #kgen.none, min_runtime_secs: SIMD[float64, 1] = #kgen.float_literal<1|10>, max_runtime_secs: SIMD[float64, 1] = 1, warmup_iters: Int = 2, max_batch_size: Int = 0, max_iters: Int = 1000000000, num_repetitions: Int = 1, flush_denormals: Bool = 1) +``` + +
+ +Constructs and initializes Benchmark config object with default and inputed values. + +**Args:** + +- ​out_file (`Optional[Path]`): Output file to write results to. +- ​min_runtime_secs (`SIMD[float64, 1]`): Upper bound on benchmarking time + in secs (default `0.1`). +- ​max_runtime_secs (`SIMD[float64, 1]`): Lower bound on benchmarking time + in secs (default `1`). +- ​warmup_iters (`Int`): Number of warmup iterations to run before + starting benchmarking (default 2). +- ​max_batch_size (`Int`): The maximum number of iterations to perform per + time measurement. +- ​max_iters (`Int`): Max number of iterations to run (default + `1_000_000_000`). +- ​num_repetitions (`Int`): Number of times the benchmark has to be + repeated. +- ​flush_denormals (`Bool`): Whether or not the denormal values are + flushed. + +
+ +
diff --git a/stdlib/docs/bencher/BenchId.md b/stdlib/docs/bencher/BenchId.md new file mode 100644 index 0000000000..e305f890fe --- /dev/null +++ b/stdlib/docs/bencher/BenchId.md @@ -0,0 +1,62 @@ +--- +title: BenchId +version: 0.0.0 +slug: BenchId +type: struct +namespace: benchmark.bencher +--- + +
+ +Defines a benchmark ID struct to identify and represent a particular benchmark +execution. + +## Fields + +- ​func_name (`String`): The target function name. +- ​input_id (`Optional[String]`): The target function input ID phrase. + +## Implemented traits + +`AnyType`, +`Copyable`, +`Movable` + +## Methods + +### `__init__` + +
+ +
+ +`__init__(inout self: Self, func_name: String, input_id: String)` + +
+ +Constructs a Benchmark Id object from input function name and Id phrase. + +**Args:** + +- ​func_name (`String`): The target function name. +- ​input_id (`String`): The target function input id phrase. + +
+ +
+ +
+ +`__init__(inout self: Self, func_name: String)` + +
+ +Constructs a Benchmark Id object from input function name. + +**Args:** + +- ​func_name (`String`): The target function name. + +
+ +
diff --git a/stdlib/docs/bencher/Bencher.md b/stdlib/docs/bencher/Bencher.md new file mode 100644 index 0000000000..4270a7a714 --- /dev/null +++ b/stdlib/docs/bencher/Bencher.md @@ -0,0 +1,117 @@ +--- +title: Bencher +version: 0.0.0 +slug: Bencher +type: struct +namespace: benchmark.bencher +--- + +
+ +Defines a Bencher struct which facilitates the timing of a target function. + +## Fields + +- ​num_iters (`Int`): Number of iterations to run the target function. +- ​elapsed (`Int`): The total time elpased when running the target + function. + +## Implemented traits + +`AnyType`, +`Copyable`, +`Movable` + +## Methods + +### `__init__` + +
+ +
+ +`__init__(inout self: Self, num_iters: Int)` + +
+ +Constructs a Bencher object to run and time a function. + +**Args:** + +- ​num_iters (`Int`): Number of times to run the target function. + +
+ +### `iter` + +
+ +
+ +`iter[iter_fn: fn() capturing -> None](inout self: Self)` + +
+ +Returns the total elapsed time by running a target function a particular number +of times. + +**Parameters:** + +- ​iter_fn (`fn() capturing -> None`): The target function to benchmark. + +
+ +
+ +
+ +`iter[iter_fn: fn() raises capturing -> None](inout self: Self)` + +
+ +Returns the total elapsed time by running a target function a particular number +of times. + +**Parameters:** + +- ​iter_fn (`fn() raises capturing -> None`): The target function to + benchmark. + +
+ +### `iter_custom` + +
+ +
+ +`iter_custom[iter_fn: fn(Int) capturing -> Int](inout self: Self)` + +
+ +Times a target function with custom number of iterations. + +**Parameters:** + +- ​iter_fn (`fn(Int) capturing -> Int`): The target function to benchmark. + +
+ +
+ +
+ +`iter_custom[iter_fn: fn(Int) raises capturing -> Int](inout self: Self)` + +
+ +Times a target function with custom number of iterations. + +**Parameters:** + +- ​iter_fn (`fn(Int) raises capturing -> Int`): The target function to + benchmark. + +
+ +
diff --git a/stdlib/docs/bencher/BenchmarkInfo.md b/stdlib/docs/bencher/BenchmarkInfo.md new file mode 100644 index 0000000000..88eafec487 --- /dev/null +++ b/stdlib/docs/bencher/BenchmarkInfo.md @@ -0,0 +1,51 @@ +--- +title: BenchmarkInfo +version: 0.0.0 +slug: BenchmarkInfo +type: struct +namespace: benchmark.bencher +--- + +
+ +Defines a Benchmark Info struct to record execution Statistics. + +## Fields + +- ​name (`String`): The name of the benchmark. +- ​result (`Report`): The output report after executing a benchmark. +- ​elems (`Optional[Int]`): Optional arg used to represent a specific + metric like throughput. + +## Implemented traits + +`AnyType`, +`CollectionElement`, +`Copyable`, +`Movable`, +`Stringable` + +## Methods + +### `__init__` + +
+ +
+ +`__init__(inout self: Self, name: String, result: Report, elems: Optional[Int])` + +
+ +Constructs a Benchmark Info object to return Benchmark report and Stats. + +**Args:** + +- ​name (`String`): The name of the benchmark. +- ​result (`Report`): The output report after executing a benchmark. +- ​elems (`Optional[Int]`): Optional arg used to represent a specific + metric like throughput. + +
+ +
diff --git a/stdlib/docs/bencher/Mode.md b/stdlib/docs/bencher/Mode.md new file mode 100644 index 0000000000..74efd79cc0 --- /dev/null +++ b/stdlib/docs/bencher/Mode.md @@ -0,0 +1,52 @@ +--- +title: Mode +version: 0.0.0 +slug: Mode +type: struct +namespace: benchmark.bencher +--- + +
+ +Defines a Benchmark Mode to distinguish between test runs and actual benchmarks. + +## Aliases + +- `Benchmark = 0`: +- `Test = 1`: + +## Fields + +- ​value (`Int`): Represents the mode type. + +## Implemented traits + +`AnyType`, +`Copyable`, +`Movable` + +## Methods + +### `__eq__` + +
+ +
+ +`__eq__(self: Self, other: Self) -> Bool` + +
+ +Check if its Benchmark mode or test mode. + +**Args:** + +- ​other (`Self`): The mode to be compared against. + +**Returns:** + +If its a test mode or benchmark mode. + +
+ +
diff --git a/stdlib/docs/bencher/index.md b/stdlib/docs/bencher/index.md new file mode 100644 index 0000000000..0372bc6dfb --- /dev/null +++ b/stdlib/docs/bencher/index.md @@ -0,0 +1,39 @@ +--- +title: bencher +version: 0.0.0 +type: module +namespace: benchmark +--- + +
+ +
+ +This is preview documentation for the `bencher` module, available in nightly +builds now. This documentation will move to +[docs.modular.com](https://docs.modular.com/mojo/stdlib/benchmark/) soon. + +You can import these APIs from the `benchmark` package. For example: + +```mojo +from benchmark import Bencher +``` + +
+ +## Structs + +- [​`BenchConfig`](./BenchConfig): Defines a benchmark configuration struct to + control execution times and frequency. +- [​`BenchId`](./BenchId): Defines a benchmark Id struct to identify and + represent a particular benchmark execution. +- [​`BenchmarkInfo`](./BenchmarkInfo): Defines a Benchmark Info struct to record + execution Statistics. +- [​`Mode`](./Mode): Defines a Benchmark Mode to distinguish between test runs + and actual benchmarks. +- [​`Bench`](./Bench): Defines the main Benchmark struct which executes a + Benchmark and print result. +- [​`Bencher`](./Bencher): Defines a Bencher struct which facilitates the timing + of a target function. + +
From 0dad844b5bb62730cbcf77bf3831373b56b0f7c6 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 21 May 2024 12:23:01 +0800 Subject: [PATCH 0646/2019] [Stdlib] Implement the raising unroll in terms of param for, NFC (#40319) MODULAR_ORIG_COMMIT_REV_ID: ef5eb91be3d47ce9061ceff4964bef1ce2b9535e --- stdlib/src/utils/loop.mojo | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index 0be2f644dc..79b3f149bd 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -56,19 +56,10 @@ fn unroll[ argument, which is the loop index value. count: A number of repetitions. """ - _unroll_impl[func, 0, count]() - -@always_inline -fn _unroll_impl[ - func: fn[idx: Int] () raises capturing -> None, - idx: Int, - count: Int, -]() raises: @parameter - if idx < count: - func[idx]() - _unroll_impl[func, idx + 1, count]() + for i in range(count): + func[i]() # ===----------------------------------------------------------------------===# From c51799d1d50b21fb9b5b978b8ecc0db6a72a7411 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 21 May 2024 13:42:34 -0400 Subject: [PATCH 0647/2019] [stdlib] Remove `tensor.random` This is the first step toward deprecating `tensor`. Equivalent static methods are added to the `Tensor` struct. MODULAR_ORIG_COMMIT_REV_ID: a80f581fafcdb0a220ef65364a8bd99cdda34caa --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5a09b4a4e8..596de85872 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -474,6 +474,9 @@ what we publish. - `math.limit.max_finite`: use `utils.numerics.max_finite` - `math.limit.min_finite`: use `utils.numerics.min_finite` +- The `tensor.random` module has been removed. The same functionality is now + accessible via the `Tensor.rand` and `Tensor.randn` static methods. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on From 06e07280c694a19db4db0c1c01df8def49630c0b Mon Sep 17 00:00:00 2001 From: Joshua Peterson Date: Tue, 21 May 2024 14:07:14 -0400 Subject: [PATCH 0648/2019] [docs] Add docs for the __copy_capture dectorator Add public documentation for the `__copy_capture` dectorator, that was introduced in v0.7.0. MODULAR_ORIG_COMMIT_REV_ID: a31bda2caabb447919d5bc53bbaa4625caee4cf1 --- docs/manual/decorators/copy-capture.ipynb | 64 +++++++++++++++++++++++ docs/manual/decorators/index.md | 1 + 2 files changed, 65 insertions(+) create mode 100644 docs/manual/decorators/copy-capture.ipynb diff --git a/docs/manual/decorators/copy-capture.ipynb b/docs/manual/decorators/copy-capture.ipynb new file mode 100644 index 0000000000..e5e59dfe87 --- /dev/null +++ b/docs/manual/decorators/copy-capture.ipynb @@ -0,0 +1,64 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: '`@__copy_capture`'\n", + "description: Captures register-passable typed values by copy.\n", + "---" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can add the `__copy_capture` decorator on a parametric closure to capture register-passable values by copy. This decorator causes a nested function to copy the value of the indicated variable into the closure object at the point of formation instead of capturing that variable by reference. This allows the closure to be passed as an escaping function, without lifetime concerns." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + " fn foo(x: Int):\n", + " var z = x\n", + "\n", + " @__copy_capture(z)\n", + " @parameter\n", + " fn formatter() -> Int:\n", + " return z\n", + " z = 2\n", + " print(formatter())\n", + "\n", + " fn main():\n", + " foo(5)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.md index 45936416a2..bfdd52e7c6 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.md @@ -8,6 +8,7 @@ listing: - id: docs contents: - always-inline.ipynb + - copy-capture.ipynb - nonmaterializable.ipynb - parameter.ipynb - register-passable.ipynb From b0559c228fba538777f405644a0b983a8a6e606d Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 21 May 2024 14:20:13 -0400 Subject: [PATCH 0649/2019] [stdlib] Added `FileHandle._get_raw_fd()` Currently there is no way of retrieving the underlying file descriptor of a `FileHandle.` The addition of this function allows redirecting output of `print` to a file. MODULAR_ORIG_COMMIT_REV_ID: 3d9509af076d7fa8f4964b34be632c78f1833719 --- stdlib/src/builtin/file.mojo | 7 ++++++ stdlib/test/builtin/test_file.mojo | 34 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 8bbfe52ea3..ff58eb0cac 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -463,6 +463,13 @@ struct FileHandle: """The function to call when entering the context.""" return self^ + fn _get_raw_fd(self) -> Int: + var i64_res = external_call[ + "KGEN_CompilerRT_IO_GetFD", + Int64, + ](self.handle) + return Int(i64_res.value) + fn open(path: String, mode: String) raises -> FileHandle: """Opens the file specified by path using the mode provided, returning a diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 572283bf86..4e471ae511 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -211,6 +211,39 @@ def test_file_read_to_dtype_pointer(): ) +def test_file_get_raw_fd(): + # since JIT and build give different file descriptors, we test by checking + # if we printed to the right file. + var f1 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_1", "rw") + var f2 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_2", "rw") + var f3 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_2", "rw") + + print( + "test from file 1", + file=f1._get_raw_fd(), + flush=True, + end="", + ) + _ = f1.seek(0) + assert_equal(f1.read(), "test from file 1") + assert_equal(f2.read(), "") + assert_equal(f3.read(), "") + + _ = f1.seek(0) + _ = f2.seek(0) + _ = f3.seek(0) + + print("test from file 2", file=f2._get_raw_fd(), flush=True, end="") + print("test from file 3", file=f3._get_raw_fd(), flush=True, end="") + + _ = f2.seek(0) + _ = f3.seek(0) + + assert_equal(f3.read(), "test from file 3") + assert_equal(f2.read(), "test from file 2") + assert_equal(f1.read(), "test from file 1") + + def main(): test_file_read() test_file_read_multi() @@ -223,3 +256,4 @@ def main(): test_file_write() test_file_write_again() test_file_read_to_dtype_pointer() + test_file_get_raw_fd() From df0ddc0906cfee4945ff4a05efdc33e8d54a0306 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 21 May 2024 14:22:56 -0400 Subject: [PATCH 0650/2019] [stdlib] Move some uses of `StringLiteral` to compile time `StringLiteral` will be made non-materializable, which will prevent its use at runtime. This patch paves the way towards this. MODULAR_ORIG_COMMIT_REV_ID: c9e0bb963a9b261732ccab2600b197b9a21f4c21 --- stdlib/src/builtin/io.mojo | 14 +++++----- stdlib/src/builtin/object.mojo | 5 +--- stdlib/src/builtin/simd.mojo | 26 +++++++++---------- stdlib/src/memory/arc.mojo | 1 + stdlib/test/builtin/test_sort_issue_1018.mojo | 8 +++--- stdlib/test/builtin/test_string.mojo | 16 ++++++------ 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 3f7f151984..f890c3f17f 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -97,8 +97,8 @@ fn _flush(file: Int = stdout): @no_inline fn _printf[ - *types: AnyType -](fmt: StringLiteral, *arguments: *types, file: Int = stdout): + fmt: StringLiteral, *types: AnyType +](*arguments: *types, file: Int = stdout): # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C snprintf call. Load # all the members of the pack. @@ -246,7 +246,7 @@ fn _put(x: Int, file: Int = stdout): x: The value to print. file: The output stream. """ - _printf(_get_dtype_printf_format[DType.index](), x, file=file) + _printf[_get_dtype_printf_format[DType.index]()](x, file=file) @no_inline @@ -265,12 +265,12 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): if type == DType.bool: _put("True") if x else _put("False") elif type.is_integral() or type == DType.address: - _printf(format, x) + _printf[format](x) elif type.is_floating_point(): @parameter if triple_is_nvidia_cuda(): - _printf(format, x.cast[DType.float64]()) + _printf[format](x.cast[DType.float64]()) else: _put(str(x)) else: @@ -332,14 +332,14 @@ fn _put(x: StringRef, file: Int = stdout): # The string can be printed, so that's fine. if str_len < MAX_STR_LEN: - _printf("%.*s", x.length, x.data, file=file) + _printf["%.*s"](x.length, x.data, file=file) return # The string is large, then we need to chunk it. var p = x.data while str_len: var ll = min(str_len, MAX_STR_LEN) - _printf("%.*s", ll, p, file=file) + _printf["%.*s"](ll, p, file=file) str_len -= ll p += ll diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 39116fc368..4e814fde23 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -17,17 +17,14 @@ These are Mojo built-ins, so you don't need to import them. from collections import Dict, List -from os import Atomic from sys.intrinsics import _type_is_eq -from memory import memcmp, memcpy, DTypePointer +from memory import memcmp, memcpy from memory import Arc from memory.unsafe_pointer import move_from_pointee from utils import StringRef, unroll, Variant -from .io import _printf, _put - # ===----------------------------------------------------------------------=== # # _ObjectImpl # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9128fb3704..ad358be38b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -608,23 +608,23 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if triple_is_nvidia_cuda(): - # FIXME(MSTDL-406): - # This prints "out of band" with the `Formatter` passed in, - # meaning this will only work if `Formatter` is an unbuffered - # wrapper around printf (which Formatter.stdout currently - # is by default). - # - # This is a workaround to permit debug formatting of - # floating-point values on GPU, where printing to stdout is - # the only way the Formatter framework is currently used. - var format = _get_dtype_printf_format[type]() @parameter if type.is_floating_point(): # get_dtype_printf_format hardcodes 17 digits of precision. - format = "%g" - - _printf(format, element) + _printf["%g"](element) + else: + # FIXME(MSTDL-406): + # This prints "out of band" with the `Formatter` passed + # in, meaning this will only work if `Formatter` is an + # unbuffered wrapper around printf (which Formatter.stdout + # currently is by default). + # + # This is a workaround to permit debug formatting of + # floating-point values on GPU, where printing to stdout + # is the only way the Formatter framework is currently + # used. + _printf[_get_dtype_printf_format[type]()](element) else: _format_scalar(writer, element) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 3fef8175cc..836bd4a0a6 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -23,6 +23,7 @@ print(3 == p.get()) ``` """ +from os.atomic import Atomic from memory import UnsafePointer, stack_allocation diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index 2fe1a9c014..116226ae69 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -16,7 +16,7 @@ from random import rand -fn sort_test[D: DType](size: Int, max: Int, name: StringLiteral) raises: +fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = Pointer[SIMD[D, 1]].alloc(size) rand[D](p, size) sort[D](p, size) @@ -37,9 +37,9 @@ fn sort_test[D: DType](size: Int, max: Int, name: StringLiteral) raises: fn main(): try: - sort_test[DType.int8](300, 3_000, "int8") - sort_test[DType.float32](3_000, 3_000, "float32") - sort_test[DType.float64](300_000, 3_000_000_000, "float64") + sort_test[DType.int8, "int8"](300, 3_000) + sort_test[DType.float32, "float32"](3_000, 3_000) + sort_test[DType.float64, "float64"](300_000, 3_000_000_000) # CHECK: Success print("Success") except e: diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 2ad43811a6..31b89a962d 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -829,22 +829,22 @@ fn test_strip() raises: fn test_hash() raises: - fn assert_hash_equals_literal_hash(s: StringLiteral) raises: + fn assert_hash_equals_literal_hash[s: StringLiteral]() raises: assert_equal(hash(s), hash(String(s))) - assert_hash_equals_literal_hash("a") - assert_hash_equals_literal_hash("b") - assert_hash_equals_literal_hash("c") - assert_hash_equals_literal_hash("d") - assert_hash_equals_literal_hash("this is a longer string") - assert_hash_equals_literal_hash( + assert_hash_equals_literal_hash["a"]() + assert_hash_equals_literal_hash["b"]() + assert_hash_equals_literal_hash["c"]() + assert_hash_equals_literal_hash["d"]() + assert_hash_equals_literal_hash["this is a longer string"]() + assert_hash_equals_literal_hash[ """ Blue: We have to take the amulet to the Banana King. Charlie: Oh, yes, The Banana King, of course. ABSOLUTELY NOT! Pink: He, he's counting on us, Charlie! (Pink starts floating) ah... Blue: If we don't give the amulet to the Banana King, the vortex will open and let out a thousand years of darkness. Pink: No! Darkness! (Pink is floating in the air)""" - ) + ]() fn test_startswith() raises: From d6770d0c234142e957596330c4409200b43cfc79 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 21 May 2024 13:41:50 -0500 Subject: [PATCH 0651/2019] [stdlib] Disable test_tuple on Mac Disabling `test_tuple` while we investigate Mac-specific failures. MODULAR_ORIG_COMMIT_REV_ID: 79ef45f61e1378781ea7613e541cc0c803886b71 --- stdlib/test/builtin/test_tuple.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_tuple.mojo b/stdlib/test/builtin/test_tuple.mojo index c497f1fc50..5c2f92a75e 100644 --- a/stdlib/test/builtin/test_tuple.mojo +++ b/stdlib/test/builtin/test_tuple.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from testing import assert_true, assert_false +from sys import os_is_macos def test_tuple_contains(): @@ -62,4 +63,6 @@ def test_tuple_contains(): def main(): - test_tuple_contains() + # FIXME(MSTDL-516) + if not os_is_macos(): + test_tuple_contains() From 29fa6755d1ffd6b5064eb6f14a863a4f91793edf Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Tue, 21 May 2024 13:48:14 -0500 Subject: [PATCH 0652/2019] [External] [stdlib][NFC] Tests for the abs() round() and divmod() math functions (#40350) [External] [stdlib][NFC] Tests for the abs() round() and divmod() math functions All of these functions call to the specific `__abs__`, `__round__` and `__divmod__` methods depending on the types involved. These are some smoke tests to make sure they do not accidentally break See ticket https://github.com/modularml/mojo/issues/2679 Co-authored-by: Manuel Saelices Closes modularml/mojo#2724 MODULAR_ORIG_COMMIT_REV_ID: 476d782383800dc7a6e39aaa8f0fca4c1a713844 --- stdlib/test/builtin/test_math.mojo | 46 +++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 9b88b5d299..3a839df5d4 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -15,6 +15,31 @@ from testing import assert_equal +def test_abs(): + assert_equal(0, abs(0)) + assert_equal(1, abs(1)) + assert_equal(1, abs(-1)) + + var lhs = SIMD[DType.int32, 4](1, -2, 3, -4) + var expected = SIMD[DType.int32, 4](1, 2, 3, 4) + assert_equal(expected, abs(lhs)) + + +def test_divmod(): + var t = divmod(0, 1) + assert_equal(0, t[0]) + assert_equal(0, t[1]) + t = divmod(1, 1) + assert_equal(1, t[0]) + assert_equal(0, t[1]) + t = divmod(1, 2) + assert_equal(0, t[0]) + assert_equal(1, t[1]) + t = divmod(4, 3) + assert_equal(1, t[0]) + assert_equal(1, t[1]) + + def test_min(): assert_equal(0, min(0, 1)) assert_equal(1, min(1, 42)) @@ -37,9 +62,22 @@ def test_max(): assert_equal(expected, rhs.max(lhs)) +def test_round(): + assert_equal(0, round(0.0)) + assert_equal(1, round(1.0)) + assert_equal(1, round(1.1)) + assert_equal(2, round(1.5)) + assert_equal(2, round(1.9)) + assert_equal(2, round(2.0)) + + var lhs = SIMD[DType.float32, 4](1.1, 1.5, 1.9, 2.0) + var expected = SIMD[DType.float32, 4](1.0, 2.0, 2.0, 2.0) + assert_equal(expected, round(lhs)) + + def main(): - test_min() + test_abs() + test_divmod() test_max() - # TODO: add tests for abs, divmod, round. These tests should be simple; they - # test the free functions, so it's not needed to cover all corner cases of - # the underlying implementations. + test_min() + test_round() From dc12e0135fe426db9493eda72827416c9bb3bede Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 21 May 2024 16:07:28 -0400 Subject: [PATCH 0653/2019] [stdlib] Re-enable accidentally commented code MODULAR_ORIG_COMMIT_REV_ID: acaae51ef0b454f0a9d9d67c85817363138d5efa --- stdlib/src/utils/numerics.mojo | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index a750327e4b..e153f0075a 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -629,13 +629,12 @@ fn isnan[ if not type.is_floating_point(): return False - # @parameter - # if type == DType.bfloat16: - # alias int_dtype = _integral_type_of[type]() - # var int_val = bitcast[int_dtype, simd_width](val) - # return int_val & SIMD[int_dtype, simd_width](0x7FFF) > SIMD[ - # int_dtype, simd_width - # ](0x7F80) + @parameter + if type == DType.bfloat16: + alias int_dtype = _integral_type_of[type]() + alias x7FFF = SIMD[int_dtype, simd_width](0x7FFF) + alias x7F80 = SIMD[int_dtype, simd_width](0x7F80) + return bitcast[int_dtype, simd_width](val) & x7FFF > x7F80 alias signaling_nan_test: UInt32 = 0x0001 alias quiet_nan_test: UInt32 = 0x0002 From 8863a81177122c55e80e5c4e875fa5e0005df0c2 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 21 May 2024 16:20:23 -0400 Subject: [PATCH 0654/2019] [stdlib] Remove more uses of runtime `StringLiteral` Moving towards making it non-materializable. When possible, we can explicitly cast to `StringRef`. MODULAR_ORIG_COMMIT_REV_ID: 3f8a0fd7cec82353d2dcf02866af536790b2a02e --- docs/manual/functions.ipynb | 2 +- stdlib/src/builtin/file.mojo | 8 -------- stdlib/src/builtin/string.mojo | 12 ------------ stdlib/test/builtin/test_file.mojo | 6 +++--- stdlib/test/os/path/test_getsize.mojo | 2 +- 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 4f50c6053c..68a63d222a 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -785,7 +785,7 @@ " print(\"MyString\")\n", "\n", "fn call_foo():\n", - " var string = \"Hello\"\n", + " alias string: StringLiteral = \"Hello\"\n", " # foo(string) # This call is ambiguous because two `foo` functions match it\n", " foo(MyString(string))" ] diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index ff58eb0cac..67570baef5 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -406,14 +406,6 @@ struct FileHandle: return pos - fn write(self, data: StringLiteral) raises: - """Write the data to the file. - - Args: - data: The data to write to the file. - """ - self.write(StringRef(data)) - fn write(self, data: String) raises: """Write the data to the file. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 45c84ce584..81a0862b77 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1723,18 +1723,6 @@ struct String( # ===----------------------------------------------------------------------===# -@always_inline -fn _vec_fmt[ - *types: AnyType -]( - str: UnsafePointer[UInt8], - size: Int, - fmt: StringLiteral, - *arguments: *types, -) -> Int: - return _snprintf(str, size, fmt, arguments) - - fn _toggle_ascii_case(char: UInt8) -> UInt8: """Assuming char is a cased ASCII character, this function will return the opposite-cased letter """ diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 4e471ae511..d8249bf31b 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -149,7 +149,7 @@ def test_file_open_nodir(): def test_file_write(): - var content = "The quick brown fox jumps over the lazy dog" + var content: String = "The quick brown fox jumps over the lazy dog" var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write" var f = open(TEMP_FILE, "w") f.write(content) @@ -161,8 +161,8 @@ def test_file_write(): def test_file_write_again(): - var unexpected_content = "foo bar baz" - var expected_content = "foo bar" + var unexpected_content: String = "foo bar baz" + var expected_content: String = "foo bar" var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write_again" with open(TEMP_FILE, "w") as f: f.write(unexpected_content) diff --git a/stdlib/test/os/path/test_getsize.mojo b/stdlib/test/os/path/test_getsize.mojo index d9b28961ef..c1fa61829f 100644 --- a/stdlib/test/os/path/test_getsize.mojo +++ b/stdlib/test/os/path/test_getsize.mojo @@ -26,6 +26,6 @@ fn main() raises: pass assert_equal(getsize(file_name), 0) with open(file_name, "w") as my_file: - my_file.write("test") + my_file.write(String("test")) assert_equal(getsize(file_name), 4) os.remove(file_name) From e04d0cc70c8bcd68f928b9d0c933137901b56850 Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Tue, 21 May 2024 17:07:38 -0500 Subject: [PATCH 0655/2019] [External] [docs] Add fix-ups to docs/changelog.md (#40372) [External] [docs] Add fix-ups to docs/changelog.md This PR adds a few typo/grammar fixes to `docs/changelog.md`: - Changed "the sequence must be a parameter value and the induction variables are also parameter values in that sequence" to "the sequence and the induction values in the sequence must be parameter values" - Changed "the `inferred` passing kind on parameters" to "the `inferred` parameter convention" (which is more consistent with the term "argument convention" already used by Mojo" - Changed "specified by the user. This allows users" to "specified by the user. This allows programmers" ("user" was referring to both writers and users of functions) - Changed "to `DType.int32`" to "to be `DType.int32`" - Changed "treated as `owned` convention" to "treated according to the `owned` convention" - Changed 'This is "worked"' to 'This "worked"' - Changed "mutated on the body of the function" to "mutated in the body of the function" - Changed "Added new `as_bytes_slice()` methods to `String` and `StringLiteral`" to "Added a new `as_bytes_slice()` method to `String` and `StringLiteral`" - Changed "functionalty" to "functionality" - Changed "method which allow" to "method which allows" - Changed "`ListLiteral` and `Tuple` now only requires" to "`ListLiteral` and `Tuple` now only require" Co-authored-by: Brian M Johnson Closes modularml/mojo#2774 MODULAR_ORIG_COMMIT_REV_ID: 47df4b6208aae531c322109a192c1c8a8fdc9b2b --- docs/changelog.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 596de85872..f665c2a1bb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -19,9 +19,8 @@ what we publish. ### ⭐️ New - Mojo has introduced `@parameter for`, a new feature for compile-time - programming. `@parameter for` defines a for loop where the sequence must be a - parameter value and the induction variables are also parameter values in that - sequence. For example: + programming. `@parameter for` defines a for loop where the sequence and the + induction values in the sequence must be parameter values. For example: ```mojo fn parameter_for[max: Int](): @@ -36,10 +35,10 @@ what we publish. return a `_StridedRangeIterator`, meaning the induction variables must be `Int`. The intention is to lift these restrictions in the future. -- Mojo added support for the `inferred` passing kind on parameters. `inferred` +- Mojo added support for the `inferred` parameter convention. `inferred` parameters must appear at the beginning of the parameter list and cannot be - explicitly specified by the user. This allows users to define functions with - dependent parameters to be called without the caller specifying all the + explicitly specified by the user. This allows programmers to define functions + with dependent parameters to be called without the caller specifying all the necessary parameters. For example: ```mojo @@ -51,7 +50,7 @@ what we publish. ``` In the above example, `Int32(42)` is passed directly into `value`, the first - non-inferred parameter. `dt` is inferred from the parameter itself to + non-inferred parameter. `dt` is inferred from the parameter itself to be `DType.int32`. This also works with structs. For example: @@ -104,12 +103,12 @@ what we publish. ``` - Mojo has changed how `def` arguments are processed. Previously, by default, - arguments to a `def` were treated as `owned` convention, which makes a copy of - the value, enabling that value to be mutable in the callee. This is "worked", - but was a major performance footgun, and required you to declare non-copyable - types as `borrowed` explicitly. Now Mojo takes a different approach: it takes - the arguments as `borrowed` (consistent with `fn`s) but will make a local copy - of the value **only if the argument is mutated** on the body of the function. + arguments to a `def` were treated treated according to the `owned` convention, + which makes a copy of the value, enabling that value to be mutable in the callee. + This "worked", but was a major performance footgun, and required you to declare + non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: + it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local + copy of the value **only if the argument is mutated** in the body of the function. This improves consistency, performance, and ease of use. - `int()` can now take a string and a specified base to parse an integer from a @@ -243,7 +242,7 @@ what we publish. - Added a new `Span` type for taking slices of contiguous collections. ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) -- Added new `as_bytes_slice()` methods to `String` and `StringLiteral`, which +- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which returns a `Span` of the bytes owned by the string. - Add new `ImmStaticLifetime` and `MutStaticLifetime` helpers @@ -256,7 +255,7 @@ what we publish. - Debugger users can now set breakpoints on function calls in O0 builds even if the call has been inlined by the compiler. -- The `os` module now provides functionalty for adding and removing directories +- The `os` module now provides functionality for adding and removing directories using `mkdir` and `rmdir`. ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) @@ -304,7 +303,7 @@ what we publish. ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - Added the `Indexer` trait to denote types that implement the `__index__()` - method which allow these types to be accepted in common `__getitem__` and + method which allows these types to be accepted in common `__getitem__` and `__setitem__` implementations, as well as allow a new builtin `index` function to be called on them. Most stdlib containers are now able to be indexed by any type that implements `Indexer`. For example: @@ -391,7 +390,7 @@ what we publish. explicitly use `SIMD.reduce_and()` or `SIMD.reduce_or()`. ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) -- `ListLiteral` and `Tuple` now only requires that element types be `Copyable`. +- `ListLiteral` and `Tuple` now only require that element types be `Copyable`. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - Continued transition to `UnsafePointer` and unsigned byte type for strings: From 2f8cf37b9af7017d96c4d81ca86d4908d297150a Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 21 May 2024 17:47:33 -0500 Subject: [PATCH 0656/2019] [External] [stdlib] Apply `any()`/`all()` for `SIMD` cases (#40313) [External] [stdlib] Apply `any()`/`all()` for `SIMD` cases Switch to using the builtin `any()`/`all()` functions instead of `SIMD.reduce_and()`/`SIMD.reduce_or()` where applicable. This simplifies the code *and* reads very nice. I tested the mandelbrot notebook example to ensure it still works. Co-authored-by: Helehex Closes modularml/mojo#2735 MODULAR_ORIG_COMMIT_REV_ID: d08203ce31e7d654c29bf09441845e2e1ed007dd --- examples/mandelbrot.mojo | 2 +- examples/notebooks/Mandelbrot.ipynb | 88 +++++------------------------ stdlib/src/builtin/simd.mojo | 16 +++--- stdlib/src/memory/memory.mojo | 4 +- stdlib/src/testing/testing.mojo | 10 ++-- 5 files changed, 30 insertions(+), 90 deletions(-) diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 90729625cb..b93961f579 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -57,7 +57,7 @@ fn mandelbrot_kernel_SIMD[ var t: SIMD[DType.bool, simd_width] = True for _ in range(MAX_ITERS): - if not t.reduce_or(): + if not any(t): break y2 = y * y y = x.fma(y + y, cy) diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 25190ce265..8f42d55a8b 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -69,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -92,7 +92,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -121,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -163,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -198,24 +198,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "def show_plot[type: DType](matrix: Matrix[type, height, width]):\n", " alias scale = 10\n", @@ -255,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -272,7 +257,7 @@ "\n", " var t: SIMD[DType.bool, simd_width] = True\n", " for _ in range(MAX_ITERS):\n", - " if not t.reduce_or():\n", + " if not any(t):\n", " break\n", " y2 = y * y\n", " y = x.fma(y + y, cy)\n", @@ -291,25 +276,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAowAAAKMCAYAAABhM+nWAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAAnYAAAJ2AHHoLmtAAEAAElEQVR4nOydd3gc13nuf2dmewEWWPRCACQAAgR7J9XFKokSJcdWc5MlO04sydZ1SbmJE9+Um+vYjmw5opJIop3YVrNVrC5RXRTFKvYCEgRRiN4WZfvOzP1jARAkAVaQaOf3PHpsAFvOLIGZd97zvd8nDMMwkEgkEolEIpFIhkAZ6QVIJBKJRCKRSEY3UjBKJBKJRCKRSM6IFIwSiUQikUgkkjMiBaNEIpFIJBKJ5IxIwSiRSCQSiUQiOSOmoX4w326/nOuQSM5K6bRpPPD1r0N5OZUvvoihaSO9JIlEIpFIxhV3Hz8+6PeHFIwSyWhBVRSmlpby7QcewPfii7QfPCjFokQikUgklxEpGCWjmmSvl8/deCMLp0+n6amn6DhwYKSXJJFIJBLJhEMKRsmoxeFw8OBDD5HW2Ej1+vUEW1tHekkSiUQikUxIpGCUjDqEEBQVFHDX176G99gxKl99VW5BSyQSiUQygkjBKBlVKKrKmltu4caFCwns2UP1u+9KsSiRSCQSyQgjBaNk1JCWnMzym25i5cyZHPjlL4kFAiBHnUskEolEMuJIwSgZFZSWlXHv3XfjbGig/PHHifn9I70kiUQikUgkvUjBKBlxSsvK+MGDD1L3u9/RWF4ut6AlEolEIhllSMEoGTEG9les+93vZMsciUQikUhGKVIwSkYE2V9RIpFIJJKxgxSMksuO7K8okUgkEsnYQgpGyWUl2evlwYcekv0VJRKJRCIZQ0jBKLksKKrKrOJibr3rLlIaGqRYlEgkEolkDCEFo+SSo6gqt991F6tmzaLhtdeoPHBAikWJRCKRSMYQUjBKLimKqnLz2rWsnjWL8scfJ9zWNtJLkkgkEolEcp5IwSi5ZJw0ueWRR+KTWyQSiUQikYw5pGCUXBJOm9wixaJEIpFIJGMWKRglw46c3CKRSCQSyfhCCkbJsFJaVsZ35OQWiUQikUjGFcpIL0AyfuhzFlvk5BaJRCKRSMYV0mGUXDQ2q5VV117L6rVrpbMokUgkEsk4RApGyUWR7PXy4He+Q1ZHB8fXrcPf0DDSS5JIJBKJRDLMSMEouWD6Z0I3NHD4D3+Q4RaJRCKRSMYpUjBKLgg5E1oikUgkkomDDL1IzpuBzmK1FIsSiUQikYx7pMMoOS+ksyiRSCQSycRDOoySc0IIgdPplM6iRCKRSCQTEOkwSs6KoqqsXrGCFUuXolZWUvnaa1IsSiQSiUQygZCCUXJGFFXl5rVruW3JEmqefx5fRQUYxkgvSyKRSCQSyWVECkbJkCiqyu133cXqWbM48MgjxAKBkV6SRCKRSCSSEUDWMEoGpc9ZXD1rFuWPPy7FokQikUgkExjpMEpOQzqLEolEIpFIBiIdRslJSGdRIpFIJBLJqUiHUdKPdBYlEolEIpEMhnQYJYB0FiUSiUQikQyNdBgl0lmUSCQSiURyRqRgnOBIZ1EiufQIkwlxAc9zpKWRNHkyxmXsfapHIjR89hlCXMiKQdc02atVIhmHSME4gZHOokRyEQiBOzv7rMLKlppK1rx5xDo7z+/lFQVPSQk9u3cT6+i4mJWeF50VFWRMm4a3tPS8nxvr6aFm0yZUl+uMn0s0ECDY1nYxy5RIJJcZYQxx6zrfbr/ca5FcRgZOcCl//HHC8uQtmcAM5QCeyeFLKCzEk5lJsKrqjK9t8Xjo+uwzoq2t572uWCBA5AKed1EoCvZzEMKDEfP7MTwekmfPhiGeb2gate+9h5GSgiMlZZAHGASam+k4dmzQNUgHUyK5tNx9/Pig35eCcQIinUXJhGQIR3AoB/BcHD7fjh3o0ehZ39qYQCJHqOqQYrEPU2Ii7pKSQQWhoWk079yJa+HC0wTlUA6mdCwlkuFDCkYJIJ1FyfjkVIdwMGdwKEfwTA7giDh8ElSHA+sg7uNgDuZQjuVQtZjSoZRIzowUjBLpLErGBfaUFMwDzk+nOoRncgaHcgQnkgM41hnMwRzMsew8eJBoWtpJtZiDOZSGYdBdVyf//SWSXqRgnOBIZ1EyVhCqeuLCLwTJU6diS0wEwJqURNq8eYSPHMHQdWBwh1A6g5LBajEHcyg79+2jsbGR5KlTgdOdSQMwYrHLvXqJZMSQgnECI51FyWhmoGOo2u1kLlqEGolgGAaWpCTsSUl0bd0Kug5C4D96lHBLy0mOkHQIJefKYA6lZ948FLMZON2ZDDY0UL1jBw6vF5COpGT8M5RglG11xjmyz6JkNNBfY3gWx9BktxNta6Nn/34wDEJAS2srmvy9lQwThqad9r2OLVtOfKEoOBwOQtu3AxBpbWXKwoU4MjKAuCOphUJxR1LXaS0vJ9zV1V9DK2skJeMV6TCOY6SzKBkpTA5HvyMzsMbwXBxDQ9fjP5NIRgNCxF3JAfQ5kuGWFtqamsi45hqEqp5WIynT25KxiNySnmDImkXJZUEIlN4tvj7nUCgK6UuWoHR3E+3oOK3GMCwdQ8k4wpKSgsnhAE6ukTR0/UR6Ozn5JCfSMIxBnU6JZDQgBeMEQjqLkktJf82hECRPn05iaiqqzXaScxjt7KS7vLzfKZQ1hpKJwsAayb70dqS19YQTKQTNO3fS1dGBxemUNZGSUYesYZwgyJpFyXAxMK3c19fQ4vH01xxiGKhWK53btqEHg7LWUCLh5BrJaFsb7Z98AoA7JYXwzp3xMFdPD4VXXonZ4ThRE1lcfNKEG1kLKRltSIdxHCGdRcnF0ld7ODCtjBAn+hr6fCfXHMqtNYnk/FEUhKL0f+mZNw+hKP0Tbiw2m6yFlIwYckt6nCNrFiXnS5+DqJjNZMyde1LtoREKnZRWln0NJZJLT9+Em3OphQTZH1JyaZCCcRwjhOCGVau4fdky6SxKzshgDqIrPx+tpYVgZeVJtYcyrSyRjBxnq4UMNTdTvWMH9uRkWQMpGVZkDeM4xuFwsGLpUmqef16KRclJCJMJ9SwOYmDrVoJ1dVIcSiSjiLPVQvb1h4y2tsoaSMllQQrGMU6y18uDDz2EWlmJr6JipJcjGQX0pZj7+h/ak5P7HcSeDRukgyiRjGEira1EAISgZ/NmAAp6ayAjjY0UXHPNSTWQgHQgJcOCFIxjGIfDwYMPPURaQwOVr70mTwgTkVP6ICbm5/enmM0JCXR99hndLS3SQZRIxhuG0V/D2DepxupwoFRVEfL7SU1PJ3n2bDr370cLhfAUFPTPyJZ9ICUXgqxhHKP0OYveY8eofvVV+cc/wTA5HDhSUk7rg9i9fTs9hw/3p5hl/0OJZGIysAbSM28e3RUV8RnZU6f294EUQsj0teQ0ZOhlHOFwOPjBX/913Fn8wx+kWJwADFqL2NODEQj090GUE1QkEsmQKAr27GwAwj09JC5cSOPmzRheL+i6rH2U9CMF4zhBOosTCCFwZ2djT0s7rRaxP82safJ3QCKRnB+9fSBNiYm4iopo2bXrtP6PwfZ2GaKcoMiU9DjAZrXy4He+E3cWpVgclwx0EhMKC/FkZqL5fLIWUSKRDB+9obdoWxsdbW2n1T46s7Op2ryZtIULaa+oINzVJctbJFIwjiVWXXstWR0dHJbb0OOLMziJtS+8gB6NypO1RCK5ZGiBAIGaGgCEz0dnVRXZU6Yg/H7CikLe8uXUbt4snccJjhSMY4TSsjJWr13L8XXrpFgcB0gnUSKRjEb6ri/+w4eBE30fpfMokYJxDFBaVsYPHnyQut/9Dn9Dw0gvR3KhSCdRIpGMMfpGgkrnUSJDL6Oc0rIyvvPAA7Q89RQdBw6M9HIk58mZnMSIdBIlEskYxZKSEm/d5fH0O4+e6dOl8zgOkCnpMchAZ1GKxTHEGZxE344d0kmUSCTjBqGqADimTEEIQVtTEynz51O7eTO6ohBqbx/hFUrOFykYxxhpycn84HvfI/Tmm1IsjgV6J6440tKYtHIlnowM6SRKJJIJR5/zqLvd+NvaMFJT+/s8ArIGfwwg2+qMIRRVZflNN+FsaKCxvHyklyM5AydNXElPJyEvj57du6l9/nnpJEokkgnHwJpHl8eDKy2Nll27KLjqKnpqa+lqbyfk88nz4hhECsZRhqKq3Lx2LStnzqT88cfl3dgoRKgqQlHwlpaSt3r1iYkrW7fS+cYb/SdMiUQyAghx0li8M2FNS8M1Zcp5i5dwUxM9R4+Copz5gbo+Yc/hhqad1OdRVFdj6ekhNSmJNlXtn22NEP0zsSWjGykYRxGKqnL7XXexetYsDjzyiEycjTLsKSnYkpPJXLQIu9OJzeOhfcMGug4elBNXJJLLTe+oO3GKaDOnpOCdPx+tq+uMTxeKgrOoiODu3Wg+37m/r6YRrasj/brrsKamDv04Xce3YwfBjg5MLtdJP4oFAkRaWs79Pcc4WiBAMBCIT5jp7qagd7Z1xrRpODweqnfsAJDXvFGOrGEcRcwpLeXb997L4ccfJywHwo8K+lLOk5YtI33ePGLHjxNtaaFn/37CLS1ydrNEcilQlP4wBQzuBDqmTMGZmUmkuvqkp5oSE/Fv307sHM6heiBA9AKEm+p0YkpJOcuL60R7enBdcQUmp7P/24am0fLOO5CaepLg1CMROnbsOOGMGsb4LmnpFfyRtjbU3Fyajx07kbLu7JQ34COIDL2McpK9Xr7zne9gfu89WvfuHenlTHhMDgfJJSUnUs5tbbS+/Tbhlpb4iUwGWCSSYcWSmorJ4Yh3GSgrw5GWhh4KDe0EGgY927djRKOnvdZoEVpCUQbdtlYTE3GUlp70s8D+/Wjp6binTwcgUl9P8/btWHuF6bi9QRUCoSj9KevWxkYUt5ueri6EEASleXLZkYJxFONwOPjBX/91fEa0HPs3Ypxam2hTFNree0+mnCWS4aLXORRC4C4pwZyYCIA5KYnEOXOIHj0KhoFiNuPfuhU9FAIu3AkcSwhFwZKT0+8wRltasM2fjy0zk2hzM02bNuGaPRuhqifVUI63G1iL10tM10lcuJDGzZvREhLorKmRtY6XESkYRynJXi8PPvQQ3mPHqH71VSkWRwIh8BQWkrV48cm1iQcOxE9Qo8CpkEjGKoM5h6rNhiUhgcD27Ri6HneSKiqINjfH/976tmMnMn3hHQDDwFZYiDk9HTQN344d2JcswWy307xxI0pvjeS4ubFVFISiYEpMxDAMounpstbxMiIF4yhEVRS+9sUvMichgcrf/16eIC8nQmCy2fonsHgnT6Z7xw5ZmyiRXCgD0sl9NYdmj2dI5zA22v7OziNdfRqXOQ3dV0Op9/RAUhKuuXMJ7NtHR0MD7tLS8eVADlLraElPj7uOyL6OlwIpGEch08rKeOgrX6F6/XqCshXLZcOemkr6ggVkL1xItKamfwKLFg6P7ROrRDIS9F7QLWlpeOfPR+/p6a851Ds7R7VzqDqdmHtrBE0pKbgXLsTo7Dyv1zAMA//27YQ7OlCdTgzDIHL8OMZlOpcMFLmu+fMRqjqoAxlpaxtdAv186a11tOfn01VXh2v+fNnX8RIhBeMoQ86IvryclHaePx+tpgbf5s30VFRIkSiRnAdCVRGq2l+D2JdW1n2+/nTyaKw5FKoa3+o0m3H3CivnggUofj96ZydqQgLhbdvQzjdkoevEenqwXXklqstFaM8euuvrsU+bRrSpiWBFRTzcommXTUSe6kBac3Jo/vRTXLNm0X34MNHOzvhaxui5T3U4sHi9hHt6EG43rT4fBsh09TAhBeMoIjs9nX/40Y+ol2LxkjNk2rmpSZ5YJJJzRHU6saakoNjtJC9ciMXlwuJ2x2sQNa0/rTxa0skDMaemYvJ6cS9ahBKLYcnLw2hsJFZVhd7RQfjgwRNrvtBQhRAwoA2QbcECUFX827djWboU1W7H9/HHGIpC9DKnfvvqIG2FhSAE3U1NJC5cSMunn45957G31tE+aRLtbW0yXT1MSME4SlBUlXu+9CVm6zpVr78+0ssZtyhms0w7SyQXSm+a2ZqWhru4GM+CBYieHoxwmFhzM6EDB4g2N49OodFbiygsFjwrVuCaMwejvh6toYHooUMAGNEolsLCIc8FhmEQ2b8frbMTLkIEC4cDNTUVo6cHPSGBSFsbRkYG6DrBioq4y3eZBba5d7bzeHMeh0pXj8RnPNaRgnEUMHCSy/6HH47XzEmGlT5HMX/5csyKItPOEsl5oDqdWFNTcZeV4czIwJ6bS3D3biJVVQQPHsQwjHjAY5QKCtXpxF5SgnvhQsxJSRhNTQQ3bCDW1ISakoJt/nzMWVmYs7LQdu9GH2LCi97cTLixEeuSJfg/+QQU5fy3qk9FUVCSkrBMnYr/s88QeXn4e5uOa37/xb32BTAuncdT0tVBpxN/Rwe6ohBqbx/p1Y0ZpGAcYYQQ3HzrrXxu6VLK5SSXYUWoKs70dJIKC0lfvLjfUew5dGjsnfAkksvJYE6i34/h9+PfupVYe/uoq0U8DSFQzGYcZWUkrliB2WQiuGEDWmsrsdpaUBQsJSUkfPnLqEKgV1ejd3SgNzQQ+fTT+I3kIFvRSmoqhq5jJCYSa21F93qJHjs2LO6bcDji/5uXR8/Ro6jZ2YSOHRtRN+w053HTJsxZWfirqoAxmEZWFMweD0ZCAv62NjSPRzqO54gUjCNM8eTJfPcb36DysceIjcDd5Likt39i9tKlJE+eTM+uXQSrqug+dEg6ihLJGTirk3iZ28RcDPbSUlJuvRVV1wm+8w7h/fuhd/qL4vXivvVWHDfcgKLrUF2N0dKCkpuL0d6K/9nnMOUXEPr4Y5Sh5kILgd7TgxGNEqirQ2Rno3d0DM/5RVEwT55M6PjxEXcc++hzHq0FBQSOH8e+aBHBmhqC7e1EfL4xt10tVBWTxyMdx/NgKMFouszrmJA4HA7u+trXCOzZIxuODgN9juKklSvxFhTQvWMHx//jPwiPdidEIhlB+tPN06aRtnLlCSfx00/pee210e8kDkAoCsJmw7NiBYnXXEPg2WeJHDiAMeD8KhwOku6/H1NCAqbMDERjPUb5AURhMRQU4E+28damjawqP4wtPR3TVVcN+l7a7t2Eenowz52LqayMWGMj/uZmsFguXjzpOtGKinhj89paEgsK6Dl6FEthYTxdPQL03SiEKiowO51QWYmpp4cEj4duRcE5ZQodO3aMmVpHQ9PiQSNFwQHYU1Lwt7XhKC4GXaejsnLUlliMNqTDeIlx2Gx8+6GHyGpulpNcLpZBHMXWd96R/RMlkjOhKLgKC0levLg/3dz1zjsE9+8fU05iH0JV8axaRcLixejV1QTffptYff3Jj3E48DzwAKKiAiUxkZjTQfvLT+HurMO79HpY+3l+/MgjZLz/CdcrVlISPQiX6/S5z04nSnEx6he+AFYLwXWPEopqaEePokydSnC4u1z0Oo6BQ4cIaBqm9PTLnqoejL6Z2K758wkeOULE5SLU0YGhKERGwfrOhz7H0VVURMuuXai5ubT0OrvS0IkjHcYRYlpZGZNtNg6//vqYOzGPFqSjKJGcJ4qCarORNG8ejilTcE+eTGDbNvyffIJvtKabz4YQWLKy8KxejTMnh56nnyZ65MigW8PWsjJMJhOhrVtx3nsvwYcfRq+uJiBiBGv/gPVYBVcFfWRmJ5FrcmF0RYjGYnDddSfG8QmBMm8eakICTJuGfryC1o/fJ0E3oaakoFdUYAqFEGVlw1bb2O84qiqeoiIiLS1EdR1hNo/o9aPPTezevDk+89owsHi9hNrasEyZ0r+VPhrbKp1Kn+PY0daG1eGAujpyCgpoPnYMV0EBndXV0nEcAukwXkJKy8r4wYMPcvw3v8HX285Bch5IR1EiOS8G1iZ6Fy4kVl1NuLIy3icxHB6zF0LV6cReVkbanXcS/vhjAm+9hTFElwlTdjbJ3/seoSeewGhrw2QYmIWA+nrMkQhBYjiWXYWIHkd3JCJsySh2L6RmQXoGmMzg64AjFZCUDIsWwTXXcPC9N2m4/x7qG3soVG1MS04mlpWFAcNf2wjxsX7RKLGkJEhKwn/06IWNLbxECFVF7a0N1DMysCYn07x9OxjG2LohOWWCjHQcZejlsjO1tJT/9e1vy0kuF8BgjmLntm3SUZRIBmHI2sQtWwj19fobw6gOBxkPPIDZZML/xz/291IcFCFIuOceTB0dhF9+GQBTaSn21FT0jz+ms6eHsN9PbmYKPXonrfOmkXLXl/jNfz7GDRrkLrsFdes2lGmzMSJRuPJqWHoFu3ft4Ltf+wrtNXXM0+BrLjdzMzJo6uxEt9lIzMgAjwf/kSPDU9s48Pi9XiKNjQQAS3HxiNU2DoVQFCw5OURbW1EnTaKzsrI/XT3WEslqb3pdzc2lobyccJ+rO4aOYTiQgvEyYjab+duHHsK6ezfNO3aM9HLGDtJRlEjOnXFWm3gqQlGwFRWRcued6Pv24X/ttf7081CYJk3C86UvEfzFLyAYjH9v6lRMNTWoqoru9yOEwORw0Fl1DKtN53mLQUNXJ6s+t5baI9u4MquUlFlLYcZcmL2AYOsxrltxE80tXSxWTaw1YJLdzoysLGpbW7FYrSQkJCAMA01REMXFhPpa0QyjQ2UuLIzXNkajGBbLsL3usNHr1PWlq9W8PDqrq8es4xiLRNDz8ydkqlrWMF4mFFXlxptvJtvt5vDBgyO9nDGDKytL1ihKJOeAUFWsGRmkrVyJu6Bg7NcmDoJQVZJuuYWEuXOJbNlC4K23zummUU1NBZ/vJGEZO3IEtbQUqqtRhIi3ybFaSb72OpRIkBv27aCKblqe/yPzS/NJmJJJ/eGNZM6aQUP5Ttb98H/R2d7FZCH4YVY2wY4OooCqKHhcLpo7O3E5naiKgslmw6ipwVlQQOjoUURhIdHKymG54e2rbbRHIgSiUYTNNrocPMPA0LT+dLVRW4s3P5/OykrsU6aMHcex9zhUsxmb39+fqrYVFEz4Po7SYRxGhBDcsGoVty9bxoFHHpmw9Q/ng8nhILmsjGl3303nRx9JR1EiGQJLaioWr5fkJUtw5+cT3LmTznfeGdO1iYMhVBXvHXfgLirC9/DDcZfuXC7QJhOeb3wDY9s2Yp99dvJrOhzYJk9GOXQIdB0xaRKWqVMRW7bA5FwCu7ZS29lBXqKNcoeVshuWs6W7g2/++i0cgRAzBKxQrdyeP5najg5cioLqcGAxmWjz+TgWCFCWmXniDRUFdfJkorW1UFBAqKoKNSUl3kj8YsWGYRBTVZSiIroPHGDUSpdBHMf2MVieNRH7OEqH8TLgcDhYsXQpNc8/L8XiWTh11nP9k0/SI4NBEsnJ9KadU5cvxzNnDlp9PbHGRlrefJNoc/NIr254OSUF3bluHcZ5NLAWZjOmpCQira2D/jxy4ADWaBRhsWC0thJtbsYyYwYc2IdSOoO88oPY83KYvWYN+ry5vP93f8NsLcRMK5g1WJTopLajg9ykJI7U15NqsSDMZhqDQTLsdgzDQPSFUnQdraIC1eHAqKnBvXgxRksLgUAAS1ERwW3b4o8zjPis6vP8nMw2G0ZVFeZgkIjFAn3J7tHEqY5jVRWWSARLaSn+viTyGLjRGaqP40R0HKXDOEz091tsaqLqlVcmzC/Q+SJnPUsk54Y1LQ3PggXxtHNNDZ0bNsRFoqaNK0exD0dZGRn33XfWFPRQ2K+5BsfkyYT+53+GPJdYZs7E0tWFUVODcvXVWEwmqKuDxhqEzQKTJ0N2OnWOKB9v/BDrkRoO1Xcy2eZmqiJI0C0UpKURCAapamsjPzMT3TCobm7G7naT2BuaOA2TCQwD86JFxMrL0TIzsUyfTnjbNqIdHSAE2gWU4BiaRjAUImI2j07ReAomrxfDMIi63WO+j2Of49jT3k6ws3OklzWsSIfxEtPfb/GNN6TwGQShqqSUlfU7inLWs0QyCAMcxaS5c9FqavA988y4SDsPiRA4pk0j/etfp2f9eiIXuG0pAK2qaujzr8mE6vViVFbGH6+q8TY1R49CWQnG8WOI+nqYO49sa4ib//Kf+fdv3UeiAte5nZitNhIjOmgaislEjstFXWcn2R4PSU4nzX4/brs9Xid5Kr2zqqObNsUdQqcTfdMm1O5urEuXEvrkE9TSUtTUVCL796N1dsadx7NcS4SqYrfZYDQ7jQOItbXFU9WAxesl2NpKWNdRRrjP5PlwquMYbWvDSE0FIQh3do6Z47gQpGAcBkrLyvjWN77B8d/8Bv0sKb6JiD01lUmrVpFeVETLyy/TIB1FieQ0TnUU29evJ9rYOK4vQACOadPIuEixCKB4PIienqEfEIsR2rLlRC1jH5oGR6vgqqWQ4ILZc+GDF3nvf/4TxWYn0wr1na3kp+Ug3HboCWIz2WnRdZIVhUgsRgRo9fvJ9nrP3ivRMNBqauL/Xwhib72FOTERU3o6eksLOuBcvRr/J58g3G601tYzpq2FqmK32yEUIgKjXjQauk6svR3R2YkpGsXt9aJ4PIQ6O8fWrGpdJ9rejtPpJCU1lfa2Nly5ufR0dhLy+cbl9U1uSV8kacnJ/OB73yP05puy3+JAhMBkszFp2TLS589Hq6mh6bXXRsWYK4lk1DCIo9jz6afj21HsY4Cz6L9IsaimppL0rW8RXr8eva5u6Ld0OFB0HfvkySiZmZh1HT76CAoKYPUKRNk0SE6GQBeR7jb+6u/+muiho/yl102yLRnHjNkYR8qhJ4yuWDjS1ES6xwOqSiwYRFgs8T6MF4GSmhr/t09ORs3Nxf/JJ5jnziVy6BCazzdkzaOhaQTHiNM4ELPXi6briIQEujs7MWDMbVND3DGN6TrC7abV58OAMRuMkVvSlwBFVVl+0004GxpoLC8f6eWMGoSqkrdyJdmLFhE9fpym9esJNzWNe6dEIjkfJqqj2MdwOYsQF4L6sWPoTU1nfJwRCKBeeSV0dGA0NKBXV6M4nfEfJidDVjaYVJhciN7VSktdHX+W6UHXwT6jBJpqEKtuwEhJRalrpOhQOdqefbTrOgluN1ogQDgcBqv1go9F76tl9PmIHT2Ko6gIfD50XceUl0fU5xt0osxYcxr7iPZuUxvd3STn5dG+bx9KQgJCUcZUyVKkd5tadHeTPWkS9fv3o7pcCFUdNyFYKRgvECEEa265hZUzZ1L++OMT5iR/JvontKxaRUpeHo3PPUdPRcXY2WKQSC41E7FG8VSGqWbxVM7184v19se1WK3oV16JcuRI/AeqCa27jef++BRW1Y6ud3GlTaMk0YJ36mzQgIWLYOUqRFYOod0b2fibJzEHdQrdiQiXC9VuxxoMEo5ELtpp7HMStV4zwuz1YnR2YvJ4CEE8KHOK4zjWahr76EtMh44exWG3Y54yhY7ycrTevplj5hqi6xi6TqCykkSHA2thYf+Mat+xYyO9uotGCsYLpKiggBsXLuTAL39J7DxaP4xXbF4vU9as6Z/QUvn736OHQiO9LIlk1DDRHcU+htNZvBD0zk5Mt98O27ZhVFVhdHUhkpIgEmbfX3+P1oMHWOox4yqdTOd184l4p8CNd8Lhz8CVCgkOsER5+dn1oAUpsieS4UlELy0ltm8fqsOBNRAgHI2iZGaiD9Hm53wx2tri4qmrC2d+PuHWVkx5efEG4QPqJseq09iPEMSOHMERjcKUKYTa2wn7fCO9qvNGEG+2npWfT/vhw6Bp2FJTx+w2NUjBeEHYrFbuuuceAnv2jBur+ULp66c4+bbb0I4dkxNaJJKBSEfxBJfIWYT4GEFxtrDJQFQVNA2tsxP12msR+/ZBRwdFuiApL4/k0hIci+bS3HiEjOvWgNcNH+6A2TfTE+vhpz/4IZ9u3kNaQOOf0s0oJoFy+DBGMIg+cyaiogJ18mRira3opaWg68SOHAFFOXP6WYiTBJ6Sno6puPjkx/e6bq4ZMwi88AKqYaAWFREdMGN6rDqNwIlJKxYLanc3seZmjPR0GGsteHqPI3D0KFZFYdLUqSf6N/b1oRxjSMF4Aay69lqyOjo4/O674zIJda4klZZSdPPN8X6Kr75K1759E84tkUiGQjqKJ3PJnEUhsJSVxdvknOf5WLhcCL+/v4G2IyObnJ4OxNUrEITIWHUbuE1gi8Cd30W3+nj8h99D23SAbD+kKNDR00VuYgL4/QTS0khsbyccCEB3N2arFSU1lei2bZiWL0dxOAh/+CG43XHxeMpxmMrKMGVmYoRCCEXBVFCAsXs3Rm+fP+H1oixahLDbCP72t1izsrDl5BD49FPQddSMDLReUTXWnca+NLXV5SIhNXXM1jZCvIRNq63F5fGgd3fTFQphTU8fc2lqKRjPk6mlpaxeu5bj69ZNzBP/gPRz7rJltDz7LN0HDoy5P2CJ5FIhVBVXaSk5t9+OVlU1cR3FPi6hswiAqmLOyCD6ySfnPTXFaG4m2tiIZdky0DVQzSjf/ivIyYaOJtjwJMycDHOvwsi6ni2bX2T7oUrsGqQCX8xKocjpRElLp6a1HVddPXg8WLKzMdxuoj4fxsGDqBYLoqICo7sba1oayoIFpzuiioJQFPRPPkH0lvPE3nwTWlrirqLDgfl730M1m8Gq0FF9mNTuEOH330c1m3FOm0aspYWYpsXFM2PcaezF0LSxX9vIgP6NQFZmJiIxkVYhxlSaWgrG88BsNnP3mjV0vvQS/oaGkV7OZefU9HPtww8TamgYU3dIEsklQ1FwFRaSfMUVuAsK6PjDHwgdOjQxbywHYMnKIuO++y5pzaIRDMKF1EwHAnD11ZCRASYBt98N06ZBMAAFJXDjN4EODNXM7t27eeDP/x5HfYgvueE6t6Aw2Y6y5DrILSDnk08QBtATRnR2Inp6sObnE2ttxUhNjQdUOjoQHR0Yx44NPgN6sHF5ioK6ejXqkiXx/pFTi9Fb6hHhY3xwpIWegMGyrGy0w4dRAXM4TNRqPVk0jmGnsZ9xUtsIoHd3I/z+MZemloLxHFFUlRtvvplst5vDvSm7iYQrK0umnyWSQRCqijUjg7SVK3EXFBDYtm18znq+AISq4lm9mvDHH1/SgItQ1cEF2FCPH7Ad3CesEAKyMjEaqqmtOQRajLSCbNb/9jmUpr182qzTVXWQFZkK1y0owLejiracbFLTvBjTZxJNScL/zLMoe8vp9vvJSUqGigpMKSkYPh+qx0NUURBTp6Jv3hw/f57tZttsRl2zBvOMGfDMM5CQAJ//POhTeS3zdV7dtoG7HS4sdjuRQIDqaJRslwsjFCJ2qmgc407jqbWN0eZmDCFQbLaxN895kDR1Q3k5sT7XeZQeixSM54AQgtUrVnDbkiUceOSRUX8XMJyYHA6Sy8qYdvfddH70kUw/SyQDsHi9ZNxyC+78fII7d9L4+99jhMMTd/t5AEJV8d5xB86cHHxPP33J3seUno558mRCb711To9X0tMxFRTEt3v78Puh4TjGlVcQVhS+873v4e8IMtWpEA5HuWOSlc3NQbwazMegrbmLSXMLSSqZiXb713jjD+v573WP4uqJ8nVhZoYtCbxeaG+H1laEEIjubqyLF8OhQ0Tz8uK9FN1uEAKjpuZkkeBwoJSWYlq1CkXT4NVX4Yor4NZbITODxj3bePvgYaZoBlfb7UQDAWqDQZItFsKGgdVmg1CIdkUh0WYDxo/T2F/baLejqirmwkLax/DQjL40tUfT0PPz8Xd0oCvKqNymloLxHHA4HKxYupSa55+fOGJRCMwOBzPvvx+7olD/5JP0DBynJZFMVIRAmM0kTJtGxtq16JWVtPzHf0hHcQBCVUlauxZ3URGd69ZhhMOX7L0sxcVou3efaHh9FkwlJRi7d8drA/toaYFIDOOT9/jt1k9pbehhmqKTqEGhVaB2Q34EUu2ghRSKNZXWtjYSk80886/f51+e3oArrPGnDlAsBnWJDkq8CfHXFgL6Rt5t3AiKgtkw0NPSEAsXYuzeTdTrRZkxI/54RUGZPx/V7YauTgiG4PbPQ3o6ghhGbQW7nvh/JB+r4gsOCx6nk1qfj4yEBPY2NpLu9ZLhdtMcDqOoKvQKRhgnTuMArDYbRlUV5lCIiMUypmZS99PnnJrN2Px+7Ckp9LS2EhyFM7alYDwLDpuNBx98EPXYMXwD2haMZ+ypqaQvWED2woUE9u7l2Ouvx2c/SyQSXCUlZK1di8kw6Hr1VQJ7946qk/pI0+csuouK8P3sZ/H6wkuIkpCA0dV1bmtzODDPm4exYcPpP+zupvm5F7Ae2s8/uEyYVRWb2Uye24muBUl1QZfJQpHHgidq4Mkv4cPdh9m59QCzFY3bUiwsTXDR1R0iWwsTqq7EVlgELe1w803w4UfgTuhPRyu6Dlu2IADrjTeC2dx7QApYLWAxg68NjtdCLIhQDWhrJJadTstnn/JXsydTYE9ChBSKlizi6Fsb6AiHSe89nJykJPRQ6LTJM31OoxEM0q4ouC9iKs1Io/X2QHbabFiTklA8HjorK8+vxdJooXc2tejsxBKN4vV6URITaTl2bNQcjxSMZ2FaWRmTbTYOv/HGqK0rGC6EqpJUUkLJ7bcTq66mRdYqSiRxBvRTTL32WnzPPUdw/37ZHeAUTnMWL7FYVFNTsc2eTXj9+nN7fGkpJpOJ2GBbmIfKSV+8kLtNKpWHD5LhceEzNFKTkiDmxJqXT2ljNV2BLoQnC2XWtVx9y924H/1LNv22nuVpbmwmO0IX1Pd0kWe2IiorIDUNtn2CkZkKCxaC6K2fFIBqApMJkpIBAwwgFoOWRsSGHZDshfx8yJ8EdTWw9naUziZSr1hBNDWN5mMf43HMQFhctCgxmoGS3sNRFAVhs6EGAtT4fGR7PP2HKlSVtkgEQ1EuaozhaEGoKg6zmdDRoyjRKIbNNuZS1H0YmoaiKCRYLPirqjDDqJkUIwXjGchOT+fPv/516n77W/RodKSXc0npn9QyZQpNzz+PX6Y7JRLg9H6KzQ8/TER2BziNy+0swrnPkAbAbMa2ahX622/DUOfztjYUxcSUzGzq2tvJtFnQQyG6krx4/D0gFDyTCmHOfPSMSWz65z/lD9sOc6XLTHphPsaVN2Hdu4vm9z/GLmIQ06AlXqogOjqgpjL+Pg4nTC2Fq6+FsumAhrFnG8Hywzj8PVA8A+77RnzLuKUJkryw5Gpwuflk7x4e+O/XKCXCl4tTyFoU5InfP4+5NcjUUz8fRaElGiXDbMYwjJOcqpykJGLBIMFIBONixxiOAqJtbahmMwlCQEHBmE5RQ3w2tdlkIn/KlFEzKUYKxiFQVJVVq1fj37QJ3ziu3ROqSkpZ2YlJLY89Jie1SCTIforni3PmTNzFxXQ++uhlEYsAakoKist1To81zZiBqmnEhjqfaxq0tIE3EdHTRW5yMugGNS0tuLr84E2EFC+kpENrK8xeyswEN/Ou3sdnL7yGePCHBCKtvP7UvzNNCxGYOh/H0UoonQlp6SfeR1FgzlywmKC1GYQOvgZCoXo2f/w+1//jz2ByEbTXwzP/ASXzYfGVREOdPP8P3+afXvoIpdtPWIE3dzcS2Pk8Fk2QYsRNy1PJTUpCC4U42t5OZnIySq9oVITAbLdDMEggGj2xJT7GOTVFLcb4cQ2cFDPStY1SMA5CXyr66unT2f/wwyO9nEuGUFWK7riDjKlTaXn5ZTmpRSLppT/9LPspnhMmr5ekW24h8Mor6JfLATGZsC9cSHTr1vg27hkQXi+2NWvQX3st3ntxKPrWnpcPlRWgCHK8qQi7DUqnQiwIvg5YexdGdxvHO318/MZHfPWWG4nZw/znf/0V7W1Rrpg6hXB7HbY5M1EyM8DjhbbemdJCh4/ehs4O+Mo3IcEDbTX8etMGMuxA0zGYPQ+sAhJCsP99uP4GXv+Ph3jzqdfI7YElVrDpgpKERA75I9yYkMgrDQ3kDnFYPYZBgqLQHQ73p6Yhfq2z9IrG8eI0DkxREwwSiMWwpKePrbGCp9A3KWakaxulYByEgalo7RKm+0aSgX0Vq9et6+9AL5FMWAZLPz/2mEw/nwXV4SDzgQcwKioI79lz2d5XmM2YkpKItLae+YGqim3tWpTKSrTdu095EYHIz4fq6vjXhhGvfWtqBW8KtLWiKCJea7hvHyS5IDsLqivo+sNjtPj9fPHOO+kpLOSF//ol4qCf73z5diI11Wz9aAfL5ixCaayFvTsgLRtS0+Ovnz8Z5i2CohLQoqDpXJNqI3vqDAJTp6N0NaNrUZ5sVJhW/jGWj9/j0ae3881ECAuo7IFpiYmUt3eyJjWT1zo7mZmUFB81NwiJdjudgQAiHMawWk8WGuPUaYR4ito5eTLB1lbCun5S/82xxmiobZSC8RTGeypa9lWUSAZHpp/PH5PXS+YDD6Dv24f/5Zcva8jAuXIlRlMTWm3tGR9nKinBkp+Ptm7daesTkyahZmXBs8+e+GZHB0zOx+juQAgFDB0CfkBA1mSwmyFzEolf+wHX5OTSfGQ7v/zO3Ww85OfL8yZRX7WJn75xlAcTzXR/tpGk6fPAkxgPvDickJ4Vf820dIhF4qGXRcspdplY99v/5pU1y1hzxQJCiSn8/HcvQzRKwsYHuNococUNERVadNjY3slMt4dXfT6KnU6uTEri3TPU7GUnJVHd3ExXKESi3X7y5zAOnUYAhECrqcEUjWKNRAhbLGNaNMIgtY2XUeRLwXgKJSUl4zMVLfsqSiSDothspK1cKdPP58lAZ/Fyi8WT0tFnOE8raWnY77gD7bnnMAZzioWA2tqTQzCaBr4uuOoq2PAaxHqPy2SCGTNBFVBZjihey9E9n/Av//R/qKjoZk2ayueuu4bO5s9YkuGmaM5M9IABwc54wMXrheLp4PWi7/kUEtwoFdsxsqcSbG7kyf9Zxy9+/Q6L7AbqwT1YQx18MU1hjtnEf9dEsAE726EiCkThqx4PtaEweXY7y71ejLNcrxQh8DidtAYCuG22/lrGgZ/FeHQaDU1DKAoOmw2jt1/jWBeNcKK20SMEoaQkEOKSB2KkYByA2Wxm7YoVtG3cOK5S0QNnQMu+ihJJL72zn7Pvugujrk6mn8+DkXQWAVSvF6Ox8czpaEXBsmIForoavbx88MfYbCc1tu5HCPj4I4xED6LtlC3vbZ+AN4Xq//wRP3zmLSzRCD/wQlGSFU8owGtHw3z1wTs5fnQ3ka2NeM05MH0WZORANITecpzPasrJsqmEjn1A46G9/OXD/0GgtYs0A+wxCNR2UGAXzJ+UwG/qOgnEIKCDzQBbDL6amERjKMxUp5PlXi+qEMTO8nsrhEA1mQatZRz4mHHpNBK/DjptNkQoRMzjQSjKmL8xFELgdDpJSU29LDOppWDsRQjBDWvWxGdFD3VyGYMoZjP5N99MzsyZcga0RAIgBLasrBOzn7dupfPtt+X28zmi2Gwj5iwCoKrYly5Fr6uLu4GDLlLBunYt1ilTiK1bN/jjVBV18WJEff3pP29uhrIyyE6Fjz845YkCikowbzjMfdYYHWaFJZ4E3AkOlMxpfGn+VWzVoHzDS0yNhAi7C7G6nPFaxfRMDFciidU72Prxc3z47h+Z5NeZ74RMHTa3Q6qIC8PjfoPndvpo02C+EMwRUBszuMbjpdBqpbmri+tzc1HPI/iQaLfT1tNDq89HYkbG4A86xWkUY3FW8xAIVcXpdGKeMoWO8nLGw1+8oWmnzaSOMnhi/mKRgrGXooICbly4kMOPPTZuxv8llZZSdPPNmIXg2L/925i/m5JILhbV6cQ9bRq5d9+N/8MP5eznC8CzahWiro7u554bkZtPU0YGlrw8Qq+/PriIEQLr6tXYZswg9rOfwVAtfhQFJSEBPv548NdJTz/5qisEJCTGH7ttE1kYZOROwjALVIsZEQtCSwtHDmzigw83kpLmJW/tSnzNXaRNnYVwOOgpf59dtT7u/+6PWG7uwmyDn1fB8mTYG4REDdJV0GOgWmzMFiHMDgcP5OXiCwR4pqaRmarKb1tbuSM7G9MpW6s2VcV2lnF/JpOJArOZUCyGzTS4BOhzGhVVRSkqovvAAca+XOxFCGJHjuCIROiKRtEtllEzSeVi6JtJnRiNEo1GsaSnx0NQwyj0pWAEbFYrd91zD4E9e8aFWFRtNvKWLyd32TJann2W7gMHpFiUTGwUBdXhoOBb38KiqrQ98QRBWcN7fgiBY9o0Eq+5hu6HHx6ZnQpVxbF6NdrOnehDpNeFw4F1yRK03/1uaLEIKGVlKDYbnEvTb4BpZeCwQ1MD2B2IK65D3fBHiIVhyjSwWAirdna/t4UrLQqLsm38x5atzHA4SACe/Y9fcHz7K7z86UFa2uG4C2wBSAJaO8GhQ7YKdlSWZKdT4Q9Spancm5OFTVXY2O5jls1BTyjEFVYrhU7nyccjBLPS02n2+0mw20+vUewlOzGRbTU1pCsKtjP1sBQCc9+s5nEyexron92smM24DYPuSARjHEy76Tsuk9lMVkoKIjGRmtbWeM/GYfpblYIRWHXttWR1dHD43XfHvO1u83qZ+cADiPp6ah9+mJCsyZJMcISqkrZqFd5Fiwjt3k2jrOG9IBzTppHx9a/Ts349sfr6y78AIXCuWoUtOxv/U08N/hCvF+f998Nnn2EcOXLml8vPh507oXce8VnJy4e9O+OJ6dQ0cLpAEXGxWF8DV63AgsZty1ejHNuFMKXwjQJBbPVXuXXZYqobfKQpGuEoLHKZWOCA8iB82QPlPTFQYHV6GsvTktnY2EpVd4Rv5eViVxWOdXZR1+nnlilFbGxoIGGQ5SlCEAGO+nxkeb1xR3SIzzHN4cAKp01/ORWj12iw2+0QChGB8SEae1EtFhKEoCscHjdOI4De3Q1dXaQ6HJgnT6bh4MFhed0JLxjTkpO54qqrOP7MM2O6humkiS2VlTQ899yYPh6J5KLpq1VctQp3bi4dclLLhdHrLKb3isXIYHOYL8cybDbsixcTeuopGKw/rs2G8/77USoq0F555cw3yqmpqLNnwxNPnNubp6XDzNnw1OOn/6z6KFy9HJLTECqYklPi9Yo33oU1J49XX3mOzLCPBJtGTwQyLYIvTUpiW3MX9+Yk8nFDOwUKFCd7WZXhxdANulrbuC09G7uqoOkGO1vbmeVJIhAOszsY5POZmUMuNfcU5/FUVCFIdrnY2djIPIfjnGoghapit9lgPDmNvYw7pxH63X+71YpRU4MtEiFoMl30hJgJLRgVVWX5TTfhamykaQw355UTWySSkzmtVvHZZ2W/0QtkoLM4UmIRevsuVlejDeEcWletQqmvRzuH2krh9SIaG89tO1ooMG8B1FfHt6MBrLb4fxAf9ZeaDu5ECHbF29HcfA/a1DKqqg+TkewmyQGdPbDcBUVmg67WVm7LyuRoT5hJZhOxaIwlLjsqgigGIQP6OiUaGDgCQUrSkgnHYsxXFLyn9FHsI83txqRp1Hd0kOv1nuGYBCWJifREIoOmpQd9iqpKp3GM0VeK5rHZcCQmXvSEmLHfjOgimFVczIqZM6kdwwlJxWymYO1aMoqLqV63js7du8fssUgkF42ioLpcFNx/P1nLl9P2xBN0vPKKFIsXghA4yspG3FkEsJSW4rj6aiJvvTWoc2gqK8N69dXob7119tpKmw3TF76AqK09Y8qa/DzAiLfdWbAItn4Sf7yqwvzF0Nyb0jaZIb8IsvOgsx2mL4KyBex/8VfoW17i6Xd38HGDxny3YFWGi8LkBJIUg66Odmo6gxS5naTYHHgccRF4rK2dDpO9/+uYblBliHgfRbud2V4vx4do0K0IgclqJUEIfGeo3/TY7QR1fcjXGYo+p9ESi2GfMgUxjkSjYjbjNplQIpGRXsqw0zchRlRVoUajFxxgmrAOY7LXy6133UXDa68RHqNj8WQKWiI5gaxVHF5Gi7OI2Yz71luJPPss+iC1k6ayMpz33Ye2fj1GXd1ZX05dvRq1oQHefHPobeviYpgyGV58BlashuPHoPJw/GeKCm43bPsw/vxrVoHVCv5OuOVLoCrgq6Psc99AiRzna4F/wvhU4UtzJ5ObPokIFt5/aQMF0RBXOxKYlZHK4XAdXcEQDrOZnmCIBRYT9t7G2QcaGvHanaS5XAghKO/pIWgY5CUnD7p0l83G7vp6Cs/iHJ5LWnowhKriys3F8PkI+/3xbdxx4siNV6cRTkyISTEMWiORC0pRT0jBqCoKn7vxRlKamqgcyRPhBSJUlaSSEmZ84xu0PPOMTEFLJjayVnF4GSU1iwCoKq41a1A0jfAg6zCVleHoE4vnsE5RUoLpqqvgTClvmw1uvx22fopI9MCs2fHaRd2Iu4srbwK7HXq6YNVtcPUKSHBDVh5EQxgtFRw7shVT+kwmTc3lufJuvrq0kEl/9k+Ut3bx2r/9A90RjQLTiS2+QCxGntmEAZgUhSyXHd0w4klnw2AKol+8KIpCrs124uenoAjBjLQ0WgOB4UlLD4Lea7I4TSb84TCG3Q5CjIu/uXFZ0ziAU1PU5+MST8gt6YzUVBZMm0b9O++Mue1bm9fLtC99ibI77qBh/Xp8O3ZIsSiZsKhOJ5758yn6/vcxtbfT+NOfEjx8eFxcuEaKPmfRPwrEovuOO3DMmEHg0Uf7E7t9KNnZOO+9F/0cxSJmM+bPfQ7x7LNwJifyhtXQVId4bwNce/2J2sW0dLjtTrhmGdQeg/u+A1MKwWqBJC9Eemg+vIl3P9vDpnfeIHtyHo1djVw1yUdk5gLqazfxw+8+xFM7akg0qdhM8Qu1qiiU5mRQ39GOYRhMy8qgNeynubtn0OVNy8ggEg7TPcSWsyIEitlMgqrSfaZSjFPS0heCarXislpxFhRgThgsuz02US0WEsxmRDh8wZ/NaEbv7karrSXJMEDTsA3hVp/KhHMYFVVl1erVhA8cINjaevYnjCJMDgczH3gA/dgxjj/2GOGWlpFekkQyMsi+isPPaHIWhcB5883YCwsJrFt3ej9FRcG6ejXGxo3nLBbVNWtQYjEY6vE2W1wsXnkl/McjcOMaKJwM//2fUFQCt38ZPEmAAd5kqDwIBcXg98GBWqLvPc1vdh2g+mgFa6+dQ6jrdf7uRy+x6ZNyylJb+fH9c7nCG2JqBLJtFgwhmORyIhQVxWQmJzkBJRZBV81Ud3eTaB9cgClCkORyYQdiuo46yFxkj91Ojd8fFzw226BbqxeSlh50PWYzpu5u9KYmIooybibDjGuncUCKOqGggJ7WVoK6ftYZ28IYQj7PHyKFNZYRQnDDqlXcvmwZ+x9+GG2w1gyjESHwFBZSfPfdxPbvp+mVV8acMyqRDBen1ir6+moVx/gFaqRxlJWNjppFwDx1Kp4vfpHgT36CcWqfREXBduedWAsLif3kJ4O32BmAKCnB/LnPxcXio4/CqTsyihKvWbz9dmiqg/c2IJZeCcWF8NYrMHseXLsSzCY4vB1aWkCoMHkq7N8O+cWw80N0m5X2+goSkpyon/sOW3d9Sn5qlL/9wV9xuFon2aYQjGloAfhfmU6WJLlIdicjCqdDdRUkp8CRQ8Si8EllLTPTs0lyOvms5jiEYU5OzknLbvf5aItGyU1JGfS4Y5pGdXMzdrebRIdj0Mf4gkGCgQCG3X7OaekhMQxi43AyjB6NjquJMKciVBUtGqUzFCJgMiEUhbuPHx/0sRPKYbRZraxYupSa558fM2JRqCoFN99M9pw5dG3bRuuGDVIsSiYmslbx0jCanEXik1oS7riD2JYtp21D98+ILiyMz4g+03lcVRElJVjuuw/xzDNxZ7Hv9RQlXo+Yng6rVsLkgnjN4nsb4Mab4eproKoCPndHXMi5XYQ3/B5zbiai4mNiuXNRW+pQ3AnxOX5X3oRSUEJKoBO0MIbeyOw5OXzzL3+Mamjk2MBpaJiAggSFVbnJmE0WsJjBaobFV0BHK+hFYHfjaWyh099Dgn1woQfx7eA8VUWPxWCQ0IqiKCQ5nbQGAriHqGX02O20n2229Lly6mQYqxVxkX3/RgPj2mkkPotaURQSbTaMcBhHaemQj51QgnHlNddgb2+ntqJipJdyTvT3Vywujqegg8GRGcclkYwC3NOmkf/1r8u+isOI6nRiLysj7a67RoVYVLxeku6/H2PfvtNb6Ax0Fs80I1oIRHEx6hVXoObnI558Mi4WFQXy8+P/WzYNcnIgMx327oafPo2IRuFPboc1t8QH83qT45NcXC5QVCwLr6Pzw0cgW+cP//3/uKGghOwZi+Gam0GLgN2J3loBrhT823cQDB6jZs9O6lvgwUILN04vRg9oKA1N+AV4UtNh8XLY/xloHrhqJRSXUPvLf6Yz5CdqxMhNiS8l0WI57TAT7XbQNGLBIDE4TTT21TImRqMITRtUVMafdmFp6cEYOBnGnJQESUn4jx4d8ynq8Zye7kNRFNJzcnCf4cZhwgjGqaWlrF67lrp168bE1pViNpN/883x/oqPPYZ2ruOrJJJxhmKzkbZyJanXXkvb+vUEx2Bng9GI6nCQcf/9mE0mup94guhI1oAKgXA4SLr/fqioIPzyy6fdHJtmz8ZaVETs0UcHF4uqisjIQF21CnXyZMSOHbBnD2RmQkZGvK9idhbUHQdFIDZ9AO3t0NEGGZmwfDVcdz0keiAagox02P4BRlICutOBkpqJI2suuifG8e43UboaYe4VYLOjt9dDrJXWo+9DYilpNz2IpesAN7/0e6LNMe5ZcRW22/+OJ//Pt8gPhkmasQjmLIk3+15zF7htaCE/+xpq+MmHn7BIN1hu1jne0UFBihetK0BnMBgXiaccs8luxwiF0EtKMCorT/rcPHY7htlMU2cnus12+vO5uLT0UAhVxWY2E6moQAUsRUUEx4hRMxTj3WkEiLa10f7JJ0P+fEIIRrPZzN1r1tD50kv4GxpGejlnRfZXlEgARcFVWEj2XXdh1NXR/PDDRMbA3+9YwOT1kvnAA+j79uF77TWIRkduMaqKc9Uq7IsXo+/cSfiVV04Ti8LrxbZmDfqrr8ZF3kk/HOAo5uUhGhpg7564OJw7C/bths4uqKuCF59BxKLxFjmGAYXFcNPNMCkXyvdDfQ16cw0dvga8qRaYOZmt1RtJf+cQ+bd8E8uMxWze+RYJDgue628CbypoYbp3vgOiC7d3O0akHkO7DnNCGp9bmYxRFcZ551+xa9t/0djTxez8bBKTHZCeAcVzwJsOe/7IwZqnePYPn7H1aJhiG+w0ovxJshmT1UKd0U2OMoSrpaqY8/KgvZ1wTw9GTg50dPQbI7qiENI0LLHY4LOjz2O29PmgtbWhms0kFxYSOHQIoWmY0tOJjtG+xzAxnMYzMSEE4/TCQrLcbo4M0wDuS0lyWZnsryiZ2AiBMJtJv+kmvHPmENi6lc4xPI1ptKE6HGQ+8ABGRQX+QZy8y7sYFdctt+CYMYPQU0/Fx/6dsgMkHI4TM6J37z7puf2OYkEBYvduaGqEqVNBjyKqj8F/PwGnjn01W2DhYpiUB5Mnw2eb4cPXYd7ieIucPVsRR/bQnKZzTGlj9+Z9TJqezvp/+lsKJ2UTix3ligX52K+4ATqPQM1mEqdfjSY0qt55jexFc8ASRpgcpC8vItS6lr9/5GE2fbKbhGAdBSlOFlx5XVxs5k3FMKJsrO/h1z/excHWMEvtglu9Diy6gmJXqWtrIyfNiwiEIRIGyyDuVm/HD0t2NobHQ6SpKb4FLQSqouBxuWju7MTldJ6Whh6utPRQRCsqMKkqnqIiIi0tRHV9TNc2TgSncSjGfUraYbPx0He+g2vLFlr37h3p5ZyR5LIyZn3jG9StX0/PwYNjYutcIhluXKWlZK1di8kwaH7sMfRgUAZbhomBzuJoEIvuO+7AXliIf4iaROH1xsXivn1ofes91VHcvRuqquDOO6G0GFobEcEgekcr3S+8QOKMOfFwSR/ZudBcD7VVsGMzRCJw/WqYPRfefB7mLoJDnxGKdvHAO29xo1Xn7S4dkw5/smgq068rJWnxUup8Am+ClbqmCIXz1qD4KzG0KEpaLrimo+mCF194mk9f/zEJbfu5IjWf4mgbKdNXYp69DDBhnnkte974G371xEasUQtXuywsifSAP0K7P0Z9Z4iyRA9JDk98gkxPEAwTZGb1i8STP7C42NN7eoiYTJAVf5xuGLT7fLTGYoPOmB7WtPRQKApGNEpsnNQ2juf09LVVVYN+f9w7jNPKysi32zk8inu0qTYbecuXk7tsWVwsyhotyQREqCqu0lLyv/51fM88Q3D/fumwDxNCVXHOnEnSLbeMCmfRlJ2NY/VqbNnZg/dZBLDZTjiLL78cF4rZ2SccxS1b4uP9Fi2Cr98LBQVQcxBajqGZTPzPm+8xact7LOtpg+lz4OhhQIChQ13NicktM+bAgiXw4m8gGgFdg5iGecYV3L9zB6mmKDPzPHhsGt7CfIIZRTyy7mnMRjuzFk+nudtL0dLbEM4kxNHfg+27gIlIJMSvf/VfrFiYTEL2HMzpGdTu2UbKvOVoDgXCBobWxBNvHef+u64jocOHV3ViPniYgO5m97ufkmd1kuz1xIWisEOCE2PSZGhtg6IiOHbsZNGl66BpKHY7lqIi9OZmYrEYismEyWYjPxYj0vv1QInjsdvpCgTifRut1ksjgHR90NrG0LFjY7Jv40CnUbNYhpyoM54Y1w5jdno6//CjH1H329/iG6WC0eb1MvOBBxD19bS89RahhoYx94cjkVwsFq+XjFtuwV1QQMfvf09QOuzDhlBVvHfcgbu4mMArrxDes2fExKJwOLCWlZFw113EPvqI8FtvDdkax7p2LdbUVLRf/xqSkjCtXRt3FHftgrfeitdd3n47FE+Bt99ArFiBf9cn4HFQO3U6r/3gG9yZmUt2ySxo74SmFsieFE9JQ1xozV8Ck6fAO6/AgV0wpQSuuh68KVB7GN79A6RmQHIidLfCV/+GPZveYoo4jnnll1EyMiAWweQQoE4F/CDiKdNINMqbbz7PTauvJVb917zy/HvceN23MeddyeO//geUSBpb97/IH1/p4W/mu1l+55d54vH1rJg2lf179lNXHeHb6SkUpaeBpoE7GZJSoLISI9kb/zdMy4HZs+PHoxuwdQu0dcST3aoKkQix6mpiva13gqEQR9vayMvIOE3gaLpOdXMzNrd70HDMcGMuLCR0/DgiL29M921s7+6mIRolKzFxpJcybAzlMI5bwSiE4J4vf5k5hkHV66+P9HIGxeRwMPcHP0CvrKThuefGbE2HRHKhCFUlYcYMMtauRa+spPOdd4ieWnMmuTB626p41qwhccYMOtetQz81MHIZ1yIcDjz334/JZCL80ktoZ7iJN5WV4bj3XvSf/xyjowPz97+PevQovPsuNDWB2Qxr1sCs6Ygn/zMeXMnNhilFBJxWXnnyX1ndcxhXl4L6+fvp2bMVtmzDdfPtJwI+uh5vm7PhFfC1gd0B3/w+CAP0CDz/BCy/DaoPxkXjomWQVwz+djAp7HjnMWZdn4Yp804QqYCToTbtwuEqqP0RZs8NdHYa3POFr7G8MJdHPzxKzK8zLcFMqg1qO6McCUC2Av873c3KpESE2RF3BL0p0FAPMQWKp0FVJWRlg1D6j8fo6oarroWkpHjaWxGwbTvRbdvR2jsIGQYhv58mXSfH4zlpjYZh0NHTQ2soRGZy8mVxzITDgaFpBDo6UKdNI1RVNeaug13hMCIWI2qxnHVSylhhwm1JFxUUsKi0lKOPPjrSSxmUPmdRTm6RTFSEqpJ15514iorofOUVAnv3yr+DYcReUkLKrbei6jq+n/0MY6i+hZeaU1LQgddfP2Mq21RWhvO++9DWr8cIheJice9eePllsNth/vx4s20tCo/8DPILYM5cMJvQWuvZ+I//xMq52bzT3MS00tUUzVvIG3/8FVmHd7HkhW6UK1bB8ap4ktjhBHcCpKTBzbdDLAw2O2z9AK66AbLzIWsSzF4KCcngK+fQrpfInLkUs2U7QvwIyOdsl1KLJY9Y3jp+9T+/5Yn/eoSm2hCi+ygrUqwkpKq0t/cQ9sd7gC+1wk0pbhZ4XXzW0EKSxc6UaTMQba1gscTrNGtq4uGeY1WQmhZ/E0VBJHugqR7mzIaiYsjMIhZtY+9TvyYPKw5vCi2GQZbZjHHKKDghBKrJRIKi0B0OX7paxgH09W105eZi+HyE/X7UrCw0n2/M1C0nWK0YJhOBUIjwOBKNgzEuBaOqKFx9xRUE9+5FG4XNfftnQldWSrEomXicMrGled06YmO41caoY8DklsAzzxA5cGBkxKKqYsrI6K9VHCoFPRBTWRmOPrFYVRUXi8eOxWsV586BFSvBZo6P71NVWHUjrL6xt7k20NPOgtR07CKJkkmzSVs8g3/+39/i+Rc/4Ctuncmd7WQe2RMPuiy5Fpzu+BsnJkH+FAgHwNcKV6yCvCLwd8a3sLtbiTQf4un1/0xL5S6uueoF0pY2oaQUgdA426VUCMELL7zM9777bSYlGvzg2nQmpRdxzfw5bH/1VV7Z3INFh7VuyDMrLE11Ue3rQVdUcpYsRen0weQiqDkG1cfA4YCW43DVlZCdE99eV5T4cSR6YEoRpKSimU38cude6kIh7rWYmBwMkuVygaZxtL2dTK/3JCcx0W6nMxBAiUTis7UvE3rv339CVhZ4PPh71xTz+cbE9VGoKg6bDSMUIjKOReO4FIwZqaksmDaNml//enTVQcmZ0BKJnNhyCRktk1sUrxf32rVY8vLQdu7E/9RTZ535fJKz2NSE+Qc/QI1E4q7i//2/kJ0Bu3Yguvxw1TUwcxYEusFph5Z6SM9ELSgm6Qc/geOHmPzmf1K5ZQOuzmr+68srKf9sD/6wH8I9cO3N8fY6EHcU5ywCdLCY4o5iaw1U7gLVApOKofYwza8/QeKBnSxfPJfkrCKshgWULuIO49mx2y08//QyOmsCTLUZ1GlX8eLhLbzxWS1eA1YmWyhLdJGbkEB9aydmAcVpSVBbGXcR/W1QMg2mzz7xoq6EeF1mLBJ3Snu6INgdHzfY0UT9vk859t6r/MWsYkJdUXYfPY7LYiUpKYlEVR10AkxOUhJ6KERLTw9up/PyJoC7usDvJzEvj2BrK5a8vDGTphaqitNmg1CIdiFwj8OWO+NOMAohWLlqFeEDBwgO1nZghJAzoSUTHTmx5dIyKia3mM1Yp0/HdfPNUFlJ6LHH0M+hJvUkZ7G8HNM996BOmwZ1tVBSjJg8GaPyM0Lvv4B1+W0omamQmABuK5HNv6b6SICiu74JPa1gios8+zd/RtrW/2FFuJ6nttWj1R7nrsIMKCqF/ZuhcDpkF0DRdLBZMVqriTUewVy4kJg3lXc2/ha1NsyKW50wZRY5D/ycnGPbITkdwxSAFC8HD2qYLIcpLp561mNcvXoN//APL/He22+TQDolsY+YWpQJ6BS6LVyT5yUhuxDdk0Ziazs5rS1UVtaQY7aCr7cRt8sN2z+O13CWzIDFV0KSB6NqLzQewtj3GUrqJJi7EDxeMlMhPU3weGsbndUdfE6YKXG5aAqHyUlMHHQCjKIodAM9gQDOS9CX8YwYBsRixI4exeb1jrk0tVBVfJEImqKAFIyjn9FYuyhnQksmNHJiyyVFKAq2oiJS7rxzxCa3CIcDS2kprpUrUXSdyGuvEdu9+5zOc0p2Ns57742LxQMH4q1zFi0CDHA5EUXFIAyibbv59eYN3BINkf3Nv4jXH274PbVNneRPLaS1fS+HNn/C3NmLiPksEAuTuvAWDh78hDc+2sIXvBa6hYa3ej8UzYH8ImisgeLpULGN4zU7qD26kUXmGt6sMvGT//srXv3q1RANADE00UON7qTAlUz44/XEZt1AZ8VePMW3oWkaqqqe8TgPHTrExo/KaauN4dfraNehsrmBSWaD1WkqCUVTYc3XURxuPJX7aHv6cQqSHJhycuIjBDu74gGXJC9csxLQIRxAD5hoePffSYm20NXhIvX6tXS0HOPA9gZe37SHlza3kaDDVQY4HQ46dZ1kRSGo64Q0jZRBBGGC3U40EqHB5yMnOfnCfikukoGTYkLHj+OeOnVMpKmzPB6ioRCBaDQu7McR40ow2qxW7rrnHgJ79oya2kWhqhSsXStnQksmJEJVSb/lFjmx5VLQl4K+6SYS5s4lsmULgbfeurw3o72OonPFClSTicjbb6MdOtQfZjgrioJ19WqMjRsxDhyITyZZvRphMUN+PqLdSsTXCF1NRFQX1REFW0oiZGSBHiG2+qtkbf5vdoRt/NN3v4dWW8dNSz8mNz+PpfOm89tNv+HhX+0g3wJfzHejTpoC6dkQCUDNYZi+MC7GDtSgfPA6drtBU1MNrq4mvn1dEc6Zi6FwDnRX8asNm1hKFVrJX2Nb9ffsePcx0lwWju3+C4qLXiaiOTGbzadt4UYiEfbu3cuPf/xdWuqOEAyD3WLilklw66xCRDBCdnIK3PgVmL4Yw6Sw981/ZVNFNbdOyiU10YYS7EHceCvEopDggaP7YdIUKCoDXcO87LtY0pwkVO6gy5vCt7/3EFXbttETjV/kiwUsVkAA2UlJbK6sJCslhbyUFMJ+P9U+30mpaSEEZquVSSYTmqbF60VHiGhFBSaHI17TGg7H09TV1fFU+Cg0XhQhsPZuTfujUcQ4Eo3jSjCuuvZasjo6OPzuu6PCtj7NWZRNiCUTCGtaGqkrV5JYWEjTT38qJ7YMMwNT0J0/+UlcpF2m895pjuI77xDet+/8nE1FwXbnnViys4k99VT8dYuLUYuK4NBB6t99nljdp/xi815KPHnkTBasuuPz6LPmoJt0YrXrefzv/p1ntrSTZQdLJEaGBaZ0NHH9g/+bX/3+af7w3NvkaPDlXJW0gkk4J+WBPwBXrACTGTJzoakK5iwna8ktpJsUFK2NJN8utIURqrZ/xOS6HVC2iC9/VaFhZznHK3eTVzyVWcvuRPg+JN/+VZTgf/LOp9lcf/0dmHtrIw8ePEhzczNPPfUUH334Pt0tFSwrtlF49bXcc2UuTnMySUtvpumZf8FhToRkLyQkEeqo4t/fquSuhYtImTmHitdfpvC2r6B+9BKkToKyuXDtjeBJorPmEIIYaVesZOOG3/HYP/0HwfCjfHiwngwVkgSYdFiZ4CZPAJF4+5ywpmFTVYSi0BKLDZqaTrTbMWIxwqEQ7YpyWVLTQ3FSmrqrC3XSJCIdHRiKMjpnUwuB1W6HYJCmYBCX3c7or8I8O+NGMKYlJ3PFVVdx/JlnRoWDcZqzKMWiZKIgBO5p08i54w60qiqa162TzvowIlQVe2kp6ffddyIFfbnOLxfrKPahKPHG3IWFxNatiwdiVBV1yZL4BBeHDePN9zhyYCc5hsEtha10VAjSb72Kmp3v84+/+wOpyj427OomFIRME6zIdlHkiJG/Yg0Wh4lbri0j4dCbzMgpZnJOPg6TFTKLMBasJly1ERF2Ym7UQLEgktIg1oUaa0ZYVezT2njyyVaWTMrDyMxFxCqwqu3kz/weOKYDGiYjBvYYWIpAFaxaCYi4m3T48GF+/vO/pLn5EFfMgdX//E28YgezUvNIzJqBr/kwBvmQks2RHDd7Pihn5TIgFkLVu7nhuhLmfemb/PLf/i9Tmhop3PYOZBXAtJmQlQMmlVg0zNbnn2DhylV8/PGHfP3b/0isqY4EAXOtsMQKGQYEolCqKpiEIMtlRwjB7IwMWvx+3HY7uR4PWjg8aGpamEw0h8MoqnpZU9NDobe1gRBYALPXO+pnU4eFIBAM4rLZxkRw52yMC8EohGDhokW4GhtpGgVNf6WzKJmoqE4n7mnTmPTFL9K6fj2hQ4dG5Yl8rGLyevHecguOggL8lysFLQSm3FzU1NSLcxT76HUWrYWFxAbOkFYUlIQE2LQJVEG2xUZSWjZF3R2kuZNJu+IKDrz3B77z9Pt0+GIUWyHFgCu9FpZ7LXidFiypOVgW3EDY6sLMDr7w3W+j5i+n67PXqflgA7mr74X0HLreegr33K/wxw9eYtKut5nz8IdE972F4d+PKNKwJjzAF790hMaqChq6D5OV8TnACrZTggymKwc9xClTpvCLXzwDGJhNAlUVgAaGDyPwCZ2RJIL7niGaGuWZN97nu1ffGheEgSbwPcOWzR/z06c+xR4J8d0sG8LlgMlTobkGokFoOY46cxHXffW7aG4H67/6eTK663lgfgYzzAJXIEbEH8GwJvLBkVrKkhNQrXbqGlowBYNEgIqODrJSUkAIug3jrKnpcDg8OoIchhFvQO/zYYpGcXq9kJRE2Ocbdf0b3TYbRYpCdyQyLmZOjwvBaLNaWbZkCa2vvz7iFyfpLEomKqrDQcH992NRVVoff5zgKB3HORY5aRZ0ZSVdjz2Gdqlvjvv6KK5ciSUzE3w+ohs2XJij2MepzuKA/pBKWRmKzQb19fE+g1ddjfL6S6QpZpSWboz33qFThIn0xChT4RozZNgtXJubTEswgIoZ73WfI9pay/u/+RXz1t6MMnUK7254iqd/v4m54eP8GRomk4m0tb/g+TdfZf1TL/IPt1yBsDmwFs0gzGxQOzhS5SCqzWHP7ibWrFkMuM/LIVJVFVV1nP4D4QLn7aRmHEPJuplH//PfWTw5EefiG8Ch0OirZ+/mD1g1twh3uJ6ZGCyfNwclIxfajsebjV91I9idiLRcTG1HURUL9/3JdAIzi7h+6VL0lkbMH76JyCilqSfKlS3tmF0WCEVJcDvo6OrGYrExcMibx27HMJuHTE0Lmw1rKEQoEkFYLBfyLz/8DJhNrXV1YR7Qv3G0bFMLQLVYcMO4mDk9LgTjymuuwd7eTm1FxYiuQzqLkgnJgBR0bN8+Gl97DSMWG+lVjRvMaWl4Vq7EVVh4WWZBq6mpqF4v9qVL+/soBp97Lu4mXsy/61DOYi8iKQk++wz8fnA64aMPsWk6wm4DqxUjFsDV1Mo/u1T8moZJwNQEF4kWBwnJXpTsSVA6l57DO7h+zjU0xyLsevFntHSZqDxawT33fxM1Iwfd6KQ+UospdIzv/+mXmXN9CSIhEay56JGN0N3E5NxSsGZQVPQgZrMZhrECTQiBI2kykUiE4oIklt97Ezb3lRjhXezaX01VsJp2JUJTj87ipaVY7nwILGb8r/83jgVXxQNB0WD8v5pyyo8c5nfPvcdf//k3eOnYAQKHdvCFL9yDSPCy/ef/F48epKi0FCUSI1Z5nLaGCjLNpzuFuqIMmZoWioLJbscWDI4ep7EXrXebmu5uEvPy6N67FyUxEaGqo6YURrVY0MNhGru6xvTM6TEvGPtqF+ueeWZEgy7SWZRMRGQK+tLRV6uYcvvtGNXVdD766KWbBW0yIcxmnCtXYps9G6OxEb2ujtDrr59TH8WzcgZnEYjPms7Ph+rq+NfNzaBpkJYMdjMEgyiKk1lXXoN/9za60cBiIc1uQeggCkohbzJ4M0m48yEI+hDVFUxNm0JJzmS84i9ZcMtdCO0whpEHpgg33nUVvvAkPvvoR8zKWYXZbsLuWgjeNFCzAMGlDAdbLBZu/sIPiUR2gzmNnshVPPvcT+ioDkOsmO/M10i+7laMwplUl28lZDWT3rgfS90hnDd8DWwOmLsa2+Ec/vcPp+LiCP/1vd/x+ZmTqDj4IRUt9ZRX17J6wQLE0qUYqTm0fvgu4UOHqe7oOG09qqIMmZru+zdSR6PTCCf3b3Q4cBYW0nP0KJbCwlHTv9FksTBJUeI1l2N0EsyYFoyjpXZROouSiYhMQV86TqpV/P3viZSXxwXUMNOXdrYvXIgpKQmjqYnw+vXoTU3x9xuOi+xZnEUAMWkSalYWPPvsiW9aLJCcgtHdgQgGYUohHK/BmZGNs6MVsrOgpxMiYag4AgXF0NNOrOMYm575F+Zf/wW6M8p45fm/ZdnK67Bn2EFMRzEXkpM0BwMDa/2nRJKTqT/wPHlXPQB4470OLyMWyywAPvjgdexOg3v/ZDo5y/+OP/zzX7NU78IkDALHPiDvplv5P9/9Sx66/XacJgGhLoIdx0ifUkJbTxZ33PsYQb/Gtt3HoM3HsrvWYHTrJBbNpvGaG9i6dRu/ePoPLA1FWBRl0Hq6M6Wm+34+Wp3GPgSgl5eTOHnyqOrf2Ddz2j+GJ8GMacE4GmoXpbMomXDIFPSlobevoqOs7NLWKvbWJlpKSrDNm9efdo60tqLV1g6vE3M2Z3HA4zh+/OQQjaZBuw+8iRg9XYjKo7BsOVQfjo/zS0yAQA9Y7bB0JYQjIFTe/f0TvPC7V0gPHebd5gTyp6XxzjY/35jrAWVK/8sLoNGfQWPAysLV3wKRRHwg9ciwZs0tXHPNLNz2dl579RMqqo8jUtJQQ81Mu/oLhDpf5O47F5LmbERzJxITGr9+7nfc9idf5b7v/iNHdu0kBRAx6PHr1H66DT3cinPuPL7259/i4JZPydEh1nuUk1yuQf+tz5SaBk5yGmNJSaAo8W3h0YSuj8r+jWN9EsyYFowjXbsonUXJREOmoC8dA/sqBl99ddhrFU+rTdy1C23DBsIHDlyayTDn4CwCcaFcVhZ3s04VMO3toAhYuBi2bYH09Lhg9PVuqebmw/HqeA/DxEQC299n44uvkxo0OFrpYOnipfgsCmWWbvDtgaQ8ECcue1OmTGHKlBdRFRvDWad4IQghSEjIZ+9eHzX7/8j9/+uLHM9ZzOQkH7Q1Y5n+VVr3f59dxmSc7/yKwpVfwW+bxG1f+jqHjtQidJhiAy0GzkAYY/shohbB3V97gNq2MB4gSzUxV4EMm50Ml4tDTU1Mz8o6dSFnTE1Dr9PocmFNSSGwdy+G2Tyizb2HYtD+je3tRHy+EVvTWJ4EM2YF40jXLkpnUTKhUJR4Cvpb35Ip6GFE9CZQPStWkHjNNQSefXZ4+yqazVinTcOcn39pahOHQMnOxrp6dbwp95mcRQBVRcnMhI0bT992NwyYMROUGGBA/XFobQJDjwcd2trjU18yc6G9Edp9fNEUI+eKWTj+7E8RJoNo4ZX4Q78kpNVg0UPEtBO1d/HJLIOkmUcQq9XGdX/yp1Rt/TFTjRshcQW4QwjRzsy538AcqyDBmU5HTy1/ePLnmMNh7ipJY3XpFILbtxIIa/g0eL8HKqMGWiTMXFXg1A3ucDvxGgaEodbvJzbEtbMvNR0JhQgDisl0upzWdbSjR7FYLBCJIIqLiR47NirH3g7s36g3NWFkZICiEPP5LvsNb98kGC0YpKq7m0y3+7K+/8UwJgXjSNcuSmdRMpEQqkraqlV4Fy0itHs3ja+/LlPQw4BQVTyrVpGweDF6dTXdDz9MrL5+GF74lL6JmkZs587hr00cAlNZGc5778XYuDE+wSUcPut6hc029OMaGqDuGFissHkLLFkCWz6Erk74yjfivRH9XZCciqOhkpLP3wPRIK0fbaLcaWHzT/+CK6+fwcaqz/jcl308/rtjZGd7SEhwcuedP8RsHvmG1AMpLi5B0wrBmEzeFBMIJ6hOYtFEdtXtweVayq6P3+S53/yU6xfYuG1aPo7Jd+NoPcybB/bydlMPyYRIBhKEiS+nONAUlQNdYa7OymTj8Xo8nN1PFSYTuslETUsLeRkZQ/YQFCYTtvR0jI4OYn4/IjsbvaNjxEMmp9Hbv9HicuFITSXY2oolLw//0aOXv6m2EHTEYqSbTBiGMWb6M45JwTiStYvSWZRMGIRAmM2kr1mDd+ZMOp55hlBFhQy2XCRCVbFkZJC4ejXOnBx6nn6a6JEjF3eBFSJem5iePrx9E88TU1kZjvvuQ1u/Pj4b+hxQyspQ7PZ4MnowDh+Glcvh2OH4cWbnxL9fMh1mzQN0+OgNmDEPlqwAbyq4EnBveYUN//ojlqRbqdzUyKKUNn738z8Q7ari+iVZeGd/t7dlzuhDVU28+94mCiavQe2VdmazmdxJZdx15+3UVe3DbMDXSlL4t/cauGbzIzxVYRBs6KEIKHE7mKUaqJi5JjeLl/eV87n0bALhMHv8Qb6YnkWjr5M0mw3dMIbsDShUlXy3GxGNxkNIQ2D01jA6srIgMRF/c3P88aPxXKHr8TS110ukogIVsBQVXfY0dXZCArFwmCOdnaR5PCNYPXvujEnBOFK1i9JZlEwkXCUlZK1di8kwaPzZz9Dl7/tFIRQFW1ERCUuXYs/PJ7prF76nn8Y4mwN3FtS0NGwLF2LOysKclYW2a9fw9E08D4TDEReLd911XmIRiLfT2bkz3n9xMBQlXrtYdQRKp8VrGg0D8ifHf6YocNPtYLVBKBCvuTOZsKTl8jf33os6YynBlFSUT3/Jguu+h0ibgtlhQ6heYPTV3fXxzW/+OSaTyuHDh5kyZQqHDx/mnXfewWM7zr23QmdoBj2uJPZ89hH1JvCH4QorpCO4Nz+Dt6sbWeC20xYMUo2J68xmQtEoJdEYyTY7yRl2yuvriZlMpA+xLWozmzFMJrRA4NxS0V1d0NmJzWZDFBcTqqoCuHyjK88Dra0N1WwmubBwxNLUAYjXiup6/Pd4lDPmBONI1S5KZ1EyURCqiqu0lPyvfx3fM88Q3L9fisWLoTf97LnpJhLmziWydSvdb755cenngX0T58zBqKkh+vHHhNrb0Vtahm/t54BwOHA88AAmkwntiScwzqe2NTUVdfZseOKJoR9jMkFuLmxTICsLtn4E3hRYeAWkZwJROLAJY/qV+D7+LSY9AdeMKxE5xZjnr0LXQnRv+keSvTYsnhRw2EBNveztc84Xs9nM4cOHKS//KYcPr+b//P2P6GivJcPp44Nj4LQZTG36BE2DfDMs9EACYI0JrIrCVR4nMR2Ot3Uwy2bH47CztyNC1wAvqycaJcNkwmDoLWohBKrdjur3Ux0Mnt6fcSC912STzYZRU4OzoIDgoUNofQ7maNumhsHT1FVVl2X3MsFqxd3baidkNqOOwuDQQMaUYByp2kXpLEomChavl4xbbsFdUEDbk08SPHhwVJ7kxwqq04m9pISklStRdZ3On/wk7rZc4Gc6aN/EJ59Eb2y8JH0az7wYgVpcjP2OO1D27SP22mvnnbYWXi+isRGamoZ+0JIl0FgPtTUQDkE0AnMWgBaF/duJJSi8v3cHGX4/jVWHuUqtJ1z+AbZ7fgxBH8273iA1Zw5q0UKwZ4NIvPw1axfIlClTaGm5gyd++SXuuwZe2t7J/t3QUA83X+nkzd2wxgNrPGbMOrSHdGalJuM0qVREwhiRuBD0CNB0nYN1DUxLz0TtdbOmZ2RQ1dZGfmbmGZtJn60/46n0OYpGeTmWSASjsJCozzc6W/BwSpra5yPs96NmZV2W2dRCVTGZzdR3dJA9WBujUcSYEowjUbsonUXJhKAvBf3AA+iVlbQ89hjREWyGP6YZ0E8xccUKzCYTwQ0biFxoLaGqxvsZTp+Oc8WKS9s38Vwxm7HedBPWefMwNm9Ge+ut869XU1XUpUsRx4+fWewqCtRWnzhOqw3mL46LPrOdI6/8ls2HavlCfiFX3XQLgb0vsOnlz7jy/f/Bc/ODCFcOwq5xaPsjFF15A6pyByPdQudcUVWVlJRMvv3tz2OPtVL52R956P6rqNi5i2c/2cINSXBDupsSjwdSJhFxefFUVUBXAEUoZLntVHf2IHqP12MYJ43+U0wmclwu6jo7yU1KOuNacj0e9HCYcCiEbrefm+jWdRSLBdHdjer1EmttRS0tBV0ncuTIqKtx1HvFbEJWFng8+JqbEVbrJa9tFKpKgctFJBaLtyi6ZO90cYwpwXi5axelsyiZCJyagva9+qrsrXiBnOooBt95B//+/RfW51AIzMXF2BcvxuR2ozqdRN55h/C+fZemb+I5opaWYr/tNtRYjNi//itcoGMqMjJQ8/Lg9deHfr7TCfPmwgdvxwVKQiJctyrev86bAh4Pxf/rp9zXWEu0/iN6Wt/kzx75iMVuleU5k8GVRPqMmXDgN3Q1pGLEVo/mssVBycubzL+/7MdpU/jff3MHLu9kjtTWsSCphc8vnEpx0TTE3BVQOAtHRxU1j/+c7PlXMc2VzL7H/5Pm7m7SrQmDvrbNbKZF18/NORQCxWrFJgShYBDNZju3EXeGgdHeDj4f5qQkTCkphHfswF5SQqiqCjUlhdhI3fgMRVcXdHXhsttRiooueW2jVVWxOBxUtrZicjpJGKVNvceMYByJ2sWUsjIypk6let06KRYl4w+Zgh42hKKgOBxkfOtbw+IomjIycKxcibWggNjWrcQ+/phIc/PIhgdsNqyrVmG9+mqMZ58lduBAXCxeCIqCuno1YteuM29Hl5aCxYQ4uB+mTYdJeWAxgdOFYTWjt+0nqrn584f+D1+c4aQjNco1s4u5Y/kybHmFEO1C045T17iXhcu+A6EXwfq1C1vzCGEymVi2/KvkZKfz9DO/xFr3Ah9sKuf7S0spuu4GxKxlaMlpRNr2Urn1RY51HCPjL37Jh+/+indafczTIf0Mr5+bnIweClHZ2kpWSgrKWUSjsFiwGsb5OY0Qn7DS1kb0k09QHQ6MmhrcixdjtLQQ6OkBIdA6Oy9526dzovf9zXb75attFAKvI94TVDtDcn0kGROCcSRqF21eL5Nvu42Wl18mOgprLiSSi0WmoIcHc1oaroULSVi4kNiuXfhef/2iHEXHFVfEJ7Hs3EnguecgFBrZC+jAWsX6evSf/xyjru7iXm/1akxZWfDUU0M/zmyGVSvhnbfin6cnCSrKIb8Aioo5tudZos//hpwvfI0jO7eTkJpHbqKXhd+YikgupfLw6+TZzCiTysle8wRE/wi2z134ukcIXdepqalg/ZP/wgfvf8S10xby39+/CUdLO8qsa9AcER574bfseftFZme56fb72PYvf8sTf3yPqzWdeedwle82DBJNpnNL616o0ziAvhsfbeNGMAyc+fmE29txrl6N/5NPEL2p7ZF2HgerbTSs1ktSAysAj9NJe3c3jV1dZCUmDvt7XCxjQjBe7tpFk8PBzAceQKuspGvfvkv+fhLJ5USmoC+egRNaXHPnYtTUELjQfoqDOIqXehLLua5LycjAumoV5oKCC69VPBWHA3XJEvjd7+JieIj3Zs0a0KKIw4fiieZJeWBSMWwWtm5+n6d/8Uv+5bt/wqMfV2KKRSmvbGdmrJ29gSoSZjdRZ7GS680nGluC1eoG89hyFvuwWCwsW3YH27bsYH5JD3/7/35M2771vPnxNm79ghvRU06R249pZhEtH7zLngaDnZ9swKFDqRVc56BtEu12sFiIBoNoMOg4wJO4GKdxIL1tn7SjR7GkpqJv2YI9LQ3z/PnE9uwhEAhgKSoiuG3bieeMgAPZV9voNJkIGEZ8TZeoNM5ksTBJUQhr2qhLTY8JwXg5axdtXi8zH3iA2P79NL3yiqzlkowrZAr64hnoKOrV1QSefJLYhaSUR6mjqKSmIrxeLL0zp41du9CefXZ41uX1Yr7/fpQdO+INuQdDVeGOO6B4CjzyMwgGYFJ+vKXOmy8S0yJs/de/Z5EeYNOTz/PU1gammnQKAx0sXXwvYuXnaezYRmlKG6+8+XtqGxpZuepbdHT4WLRo0ai7CJ8Le/bsofZ4Df/+8A+wpybx1OsNzFx7L5qyF1PudVRtOsAvnnqfv7jCwN8Owgq5Bsw2AwNacdpUE7ahjl9VMdvtiGAw/pRzEI0X6zQOpL8dlM9H5NgxMAzcixYRKy/HPm0alhkzMHp68H/00Yg5kKrVSnJhIYFDhxDRKMYZmplfKAlWKyEhODYKU9OjXjCmejwsveIK6p577pL/YvQ5i3plpRSLkvGFTEFfOEIgVBVhsYxfR9FkiiefV67EMmcONDZCXR3a669jDNe6HA7M99+PWlEBr7wy+OemqnDLLVA8BfFf6+JiEeIBl24fRKOoQvCFpARaujX8FT7+1KTjskOmzYWl4Tgf/dc/06nVcdylcaiimz///rX8+P99hbzMbhYs2Iqqjr6tvrNRXFzMz/7tEdwpWezY/hFFk3KIaZ2EzB4aD6/nZ/+2jhuTFVyuYhbkNLBsSjatO/dTG4ViVWGSy4lAMDM7HV9nDwkOx+BCRFUx2e0YfU7j2aa1DJfTOJAB193opk3xGyunE2PTJvSentMcSGtZGQBGJHKyEwnx37Fhvo5HKyowqSr2SIRoUhIoyrCXrY3W1PSoF4zJXi/m+noCl7gZrcnhYJZ0FiXjEJmCvnD6Us/uhQsx9/Y9vGBHEVC8Xtxr144OR1EI1NxcRGoq1kWLUDweRFMT+pNPYgz3zOleZ1HduxdefnlwETKYswjxWsYVK+HDt0CLoUSjZHiSSbOawGZhblqEepuLSQQQYUj2CyZlzKSz+jh/Nz2B9367hQxLMV+7+SpMpu3AsuE5pstISkoKhw+3k5RkxmLN4LlPrFitf6TxuVaaa8OYAzGuLs0lPVxP/dzF/OS5D8gPwbIEN9dOyuJobQOxnvjlvtnvZ1LKGd6sz2lMSsLweolVVFya9PS5YhhoNTUnvj7FgeyruYxVVPQ7kX3PC2/bRrSjA+F09j9da2m56PCYEKJ/JnX33r3xG65hdK5Ha2p6VAtGVVG4aulSRFfXJb/AJZeVYVcUjr32mryYSsYHMgV9YQzRRzHQ2nrhW2BmM9bp03HdfDNUVhJ67LHL6yj2bS8KgamsDMXjQS0owJyVBT4fxpYtGK2t6MO9xacoKLNno65ZE3cWhxKLOTmwehXkZJ3sLAKUzQQ9BkcOQWpa/HuqimJ2QDiIZcYc8gMdkJ4Nt3yZGXmFEGynQI8Q7Kjn5raDfG5uBmrS9QiKhu/YLiMHDx7k4X99iFtu+zN++Hc/pK76ACkJEOw0iMVAicELO2qJCmgKv0V3CArMUGhSEEJQ092Dx5FAosPOzMxURCSCYbEghnIDVRWTxYJRXo4hBMbUqRiHDw/tHg50GnUdUVyMdqlKyE51IAesoc+JBDAMA7W7G9vVV/cLRr2pCX9zM+Yrr0SoKoZhENm/P57Ojj/p3G8E+2ZSW60YwSARi2VYReNoTE2PasGYkZrKgmnTqPn1ry/pHXhyWRllX/widU88gXGZZq9KJJcamYI+f4a1jyInJrO4Vq5E0XUir71GbPfu4W1Y3OsUDpVuFV4v1kWLMLq7URITUZ1O9B07oLoa7RLOnBZpaagrVmCaMgVeew127Tr9uJ1OKCuDO++ATz+GP/wuPs2lj2Qv3HATvPsqBE6ZNa2q4EiB7MnQ1QrT54DVjGbR2fXZCxR0HsSUvwIt/1r2vfAos5a9haXkacA+7Md6qWltbcXXdICtb/wDeQn7qQxCnR9STJBlglQb5AhwCshRARtkICjLykAIQXFKElYBVpMJElzU1jWS6EgksVeQDPGmCIsFS3Ex+v79RHQ9Xkfa2jr443udRkdxMbEDB9BisbPPnh5OTnUie9cUfe21E1/rOo6iIhSzOf5lczM64PrCF0CIkxzJc3Uihapit9shFCLmdCJUFW2ouejnwWhMTY9awSiEYOWqVYQPHCA41C/oMJBcVsaMb3yDuvXr6TmfGagSyShFpqDPD6GqCFUdvsksEHdcHA4899+PqXcyi3bq66lqv2OjpKdjKi6+oBvjPqfQOPVi2beUhIS4g9jeDkCsufnC+yee04JUREkJ5jvuQKmuhnXr4NQaL7MZZs6E5cvifRX/+/F4GvqU1+HmW6GmAvbvHvy9UtOhoxVSUqCtifp9UXRfI9GdbxO8/Ues+39/yx1f+gsWfOnvwGgGrQFMky/JYV9KDMOgJ2zQEdMItsMPVxZSXDyLRHcyNR9swNXeweHmToTJRrEVXIEwKQaYhEAVAk+im86WTtwOF4oiSExwkagT/307m3N1+DCKqmIpKUFvbUUrLMQ4diwu/k/9fRUC48gRFFXFOqDGTx+p1nSGcdrNkFZejlZe3v+1JTUVY/PmkxxJo6en34lE007URg5xYyVUFbvTiVpYSPehQwznHuVoSk2PWsFYVFDAotJSjj766CV7D1dWFjO//nXq16+n5+DBS/Y+EsnlQqagzx2hKNiKinAvXozZ7cbsdF60owiAquJctQr74sXoO3cSGNCXUTgcKGlpYLNhWbwYoesIITAVFGDs3o3RtzV2Pgx0Coficuyc9Kar1aVLUfPzEc8+C+XlJ2/xOZ3xZtyrVsa3mT94B7F/7+BrLy6BvDz41aNDbxPW1YLJDN5UcCeTUTafWHIXn7zVgrO1hfkLrsDlqMEwihCeBYDrkhz6pSQWi/HBe29gUr101pbz7evns/pvnqSp5hAZ7iBbGj/iaM8MDr26kduTBYc6w6zMyaCpub1/yznBYUdLCFPf3s6k1BQ8CW6MSBgCQcjOhfb2M58nFAWlqgolKQlTRwfRvDz0jg4MVR3UcRSKgsntxpySQnjPHnSPJ74FPApvXPWB+Yg+R3KAE9mX0laTk/Fv3Qow6HEIQC8vxx4Oo0Wj6MPUr3E0paZHpWBUFYUrFy8muHcv2lB9ui4SoapMWrWKzo8+oufAgUvyHhLJZUOmoM8ZoapYMjJIXLUKR0EBka1biWzcSGAYJqmYsrNxrF6NLTub0FNPofWlqM1mTDNmYF2+HCUQgEgEGhowDhwAwyD25ptwtmDfAEdSpKejTJ3a7xCpixef/FhdR9+374QA7athHE7h2Lcesxl11SrU2bMRjY3x2dCvv35igouqQmZmXCjOnRN3FN95K+4onrrN3EdpGdxzHzz1BLQMmAQjxMkX4eQU+NyXYPM7kDkJJTWd1i2PcbgmwJ51vyDU1sicKav5dM8kduz+JUuv+DabNm3lz/7szzH3bkuOdt5++w3mzXfz9a9dx4e/ambxDd+ipv4Ij/3bX/CVr32RF+ob2bK5nBtM0B0zmJWSRLrbRYqicLy9gzxvMgoC1WrDGQngCwTxOOwIixUj1QOJvWMDhYCOjqHFuabFxaGiYBYC3es9s+Oo6xhHj2J2ODAVFRE5cCDuvAkxem9iDSP+n6KgHT2KdvRo/HgTE9FranBOnkzo6FHMV1xB5NAhNJ/v5M+rd3a20zDwh8PoNtuwLEuoKrkOBw09PWT2thQaCUalYFRUlWyXi85t2y7JL1bfjOiUvDwqf//7YX99ieRyIlPQ54Y5NRWT10vCkiXY8/OJ7tpF53PPYQxDSlk4HFjLyki46y5iH32E/6mnIBxGOByopaXYVq1C1TT0d95B37fvRHH92d7X4UD0OpLq4sWIXoGolJUhOjrAZoOuTjhUDmlpUFMTf83mZmKGgbjjjhOF+MePE928Of56AzDOdYtaCMSkSfEL/oD1iJwclMZGeOKJuEjsO660NPB6YelSyM+Fvbvhg7fjY/6GckMdzrhYvP3OuFg8POBmXlFg6jRQBOi9n1uSF9oaobUJtBhtz/wzr777Ch8ejJBp6uZH911PzYEP+fXrf+CeO1N5Yt3H/Pm3fozJNHZ6MV5//Qp27PCwccsRYulvU1v+C17eVczRVg/P/eJhdn4aJEmDKU5whkOUpKbgsJjZ3diDmxOi2G23UdfVTY4qwAAEiO4uDH8P5OVDUytMzod235kdR12HtjYUn++cHEchBOLQIazRKLGiIrTex+mXsNTsQujvP9rn/JvNJ91o6ZWVhLZvx33NNWjHj6PrOqa8PKI+H3pHx0mfl2q14hKCnlBoWJxGq6rSahikm0wYhjF0WOkSMyoF4/TCQibl5HDkjTeG/bWFqlKwdi0ZxcVUP/YY+iVyMCWSS44Q2LKySFu1CndurkxBD8JJE1nmzMGor0draKD7zTfRhsOBPaVWMfTEE2iHDoGiYJo7F+vy5ZhMJvS33yZ26NDQwkyIE8LObEZZtAihqijz5qEEg3HxWV8f3+KdNSsuFGeUQVcrWkstRqAL0746OHY8/hodHZi8Xvj00xPv0dSEWLIEkZ190veijY1w9dWIs9RHifx81KwsqK0Fmy2+noMH4wKxT6iaTPG1rV4Fs2dDcyPU1yI2vHqyU3jaiyvgcMCffivuQP72v+Kp6IFYbTB3Ebz0W9C1+Oc1fyk01oKhg68Ntu1npsgkK6mGNK8Te3sMZ+79PPRQMjmuNq7q2sqMGW1AmLESfnnppRf5yb/+NUTbeODBFB74RQ3mzloe/f4a/uHRAyRbYKVZ4DIMMOjv2ZeXkkxCVIv/+6gq6v9n77zDo7ivNfzOzPYirXbVe++AhADRezVg3FvcWxLj2DdO981NtZM4bokTt9jGJa64AwZTDBiD6UWAEAjUUO99++7cPwaZjikCXPZ9Hj2CrTOzq91vvt855xME4iPClGVojwvUWsCP4PND+QGwhSJ3t4MtWBHlAwYqx3XXbujoOLHoOSSUvtZx9PsRNBrUXV2obDa8h27nO1kt5IVAkpS/05wcpMRENHl50NCAUFurTBO49FJob4PuHuQdO/D6fOiHD4eGBrwNDegiI/E2NaEKDqa3qem4mZX97TTGBAXhdbnY39lJuMVCPw4uOm0EWT7xKzVEf3H+mAw6Hf9z//2YNm6kZdeu/n1wQSBx+nTiCwupeOIJfN/AeooAAU4Xc04OiXfeSe/nn9O5fHng5OcIJKMRdVgY+tzcrxJZHMuX4+3P+YLH1Cq6+moVRRHdddehTU9H3rkT/9fVRB5yDMWoKEUYxsUh1tUpIqytDYqLle0NC4OJEyEzDSwWhKQkaK/m+X/+jeC127j0smvRd7RDVS2YgmDf/uM7p49N75BlSE+HyMivd0FkGTZsOLwvfcdRFCE+Xmk8GT4cQoKhsR4+W4bQ1PD1xzs8AgqGwrBC2LkFViwGr0dxwY7kksvBFgLvvnxou3NgzrXw5jMwYIiyD52tVH/2MVHOdiRbMMLkqyAnj5qQODw1vyU28Q5kNqKOn4sgJp16f78B1NfXc9st12Fv3kxWRBzRYg9F+3uobu4mQisieX2kqwTuSYukpLIeHRL5MbGEmA7NHfT5oNcBKv0RI19k5TKfpIjGI192QUQWRRg2HNQaRfDXNsC0GcePjPHLsGkjtLaDyaRcr9FAQwOe4GDFdTObkVtajj5REkUICQFZxhMUdMEdxyOdRMlsRjIY8G/bhrx3L3JjI0JmJprf/Q6hswnnzo2waBG6O36C/Z238W7YhiYpGSE4GNeOHXi0WmX5WhQR0tNxnKC8ze9209NPNY1dLhc9Dgc6o7F/Z10ew/jKyhNe/o1zGDMzM0nU6yk9Dx3LaoOBmMJCGubPD4jFAN9aRJ2O8KlTCRs/ntZ58074IfV95NhuZ9Fuh97es09kOQUnrVUURbRz5qAbPx7KypAGDVKWZfV6peFDrQJJpdQwer1KDZ/Dqbh7X34JDofiUvQ5dqKofBFnZCijZ+qqEerrYOx45bGqt9Hd3cusG6/DXVGJLi0XMSsL+ZPF4HKAwaR8SfU5HyeqYSwpUX7O6AAcchJHjIDEREiIU5bGN69HaG2F6kPbj6Ds77FEREBaBgQFwcA8qKuC91+H8tLDy819SBKkZcLw0fDSE8oxScmE6++Ad1+Eni7IK4TFb0BbI3FJqdDbCcPGg0aDbLbQs++3uH1BbNmznOHjr6VoZyurV89n7tyfolb3f7xbf9Db28vjj/8Rl3Mn00cM5zc/vQVfbzdP/eVJlq7owir7MEuQpxdRiwKiKDAwOoJuey/BxkNJLpIERr0iEFHyovH5wKA/2mns0zHyIcdx/drDGxIWDpu/PH4DBUFZzbjhOrBYlHFIixdDdBRqiwW/TocQE4OnqQk5LQ25rEx5Hx5a0kYQUMNXjqM/K0uZbbh/v/Ke7a+ympM4idTWIn/xhXISeUgPCDk5aO66C2FvCW2vPote40EYXkCJ6GLJ9i+Y3mYnIywcr9eLJiYGuboaj06HymBArqhAstvxarVHufX96TQGabWYVSrqurvxabWYz0M04an4RglGlSQxPj+f1rVr8Z9Ll+IJ6MuItu/aRc8FyKQOEOB8oLHZSLr3XuTaWpqefBJ3ff3F3qSLzsm6nV3FxcqXUz/Xc2pycrDcccdRtYrAV86ifs4cRK0WJFHJSw4OUho+7D2KOGqroWfJq1DcjmnvPhgzQVmObagHc5AiiBITlS/NnGxlqHVqCqz7HMEWCldeDd3N0FCGK1jPxAwtttxQag+sxzziLny+Lna+WkWCz0NISq5SkyaeuJv1jDiRk9jdCTo9OO0I9i5F2AIwUvklqUDTN4tPVgSxKEB8Auwpgu4uePMFxZE89nUSRUhJh8JREJcA770MRjOMn678/90XoWwPTLkc6iqgch9ExUPuMNi9EfRGcPVQtncHLz+9mh1lfm68XETtWsbfn2rl/l/moFLdd27H5DxRXV3Nn//8O8xmFw5HMPVdLbz3yfNsr8vn4xUVZIiQrIbpYSbC9HpEQcCi0yBqNJi8PgSdTjn29t7DotFshZBQZXm5o/2wkPS6ITIaWk/y/mg+pnRDECE1HUaOQhhcANEx0FFH7ScfE+XvRohLU0bxmENg40Y0WVn49+7Fn5WFt6JCeQy7XTmhaGtD6OhAHRKCGBaGZ/NmVJMm4a+sxNvejmAygd+P70wGyh85l/TQNIKjnMSTJBkJMTFobr8dYd48GJxPSE4ewt7t+FxuPn36EdbXtRHmg5jmJoIvuxzvpk3K30NrK62trQTr9Wh1OgSnE19ICBzRFd6fNY2yKOLx+xG8XmX4+lk/0pnzjRKMarWaqMhIerZu7dfHlXS6rzKimxYt6t+huQECXAAESSJowAAi58zBV1ZG2/z53+vGlr7aRPOQIWiTkzGkpPRrt/MJn1OnwzhtGoaxY3HNm4f3SGe3z1kcNUpZKjIaQBtK49blaGpasI4ZCRXN4HPTI/t5duFbWPd0MjuzgIi2RthVgxwfDUOGKTMKQ6yK6xNsQaivVURWehpEx0JDDc6P/4WscyBPugK7twq3LYTSuHBCOnfy8voKGuvquV6txdLRhBAeCS2tkJYGFRWHHcdTvX9UqsMdyTk5yhJiYgLERENXB+zYghAdA4PywRqiJLOogPZWvrKrJAkGDgafRxGGpcVQVgrZg6Bsn3JZY73yI4igOrTEplbDkOGKKExMVpap9+6AvGEQnwQ7N8CaRdDaCKk5MGQ0vP6E8uUfEgam4EPbLkJwGJvefo24Jh9Xp2sRLbNQMYSH/9BJ6qDR+H0SHq8TrVZ70RoJjqW3t5eHH/4tKxa/waghKVwxIotX3l7Bjs+8NDvWE+SHKAOMiwkl3WqguKqe/MQEBsWGoxUlMFgRUlKhogyS0qGqTBk9pNdCT7si9OHQMWqF+GTl/ZGVo7wv9peeeAi8Wq0sVccnQHIy7N0JGjU4ethbspH6jR8RMWIW/u27kNwuxIomSEmF3TsRNTplLE9yMt4DB07oOPrXrkUyGBD278ff3Y163DhEoxFfURFOux1Vbu5pHb+j5pLq9co0gmOcxOMQRaTp0xHWrlXc9sF5iGUHYFAhQmYyw9uqmBwWQfeWvRimzoAh+ahysxBK9tL89jv4/TLo9QgqFRqTCTE1FcfevXiOSGjpL6dRFASsBgN13d2Y+yO7+wz4RgnGqePGYXY4qK+r69fHTZg8GaGujvrv+ZdsgG8n2vBwwqZOJTg1lc6FC7Hv2vW9fR8fV5tYWYm3ooLO997rl27nkyHabITMnYtQV4fjySfxH/kZ1VezmJaGf/16pOwM7AeLqaKE0uIexuXk4Fd5EeOjaOru4IFf/YzOra2MVQmEdNZBtRomz0BQa5Rl68JRIPqgeDu4usDeAbuaIdiiiCqdHtGWRNOudbSUPII/aQATb3yQYK+LyRtKmLe1mkxZZrfPxYCORnqamwiKTUDobYXcbBg46Ojas2Ox2RQHsacLgoOV5csd26C2Ej58WxEjV14Dg/LA64Q9GyAzi45PniVIG4mYnqvUUEZEK+JCr8e76k1oKUcleMDRBZ1tIHihvgLGT1YGcPcRHQdNdeC0w9IPYcBgRZiW7IDn3wK3E3QGGDgMZl0HH74IjbWKKBo1FTaugJyhish2ObhmQDoMzUbasQwhNIg6Uyefb/4nOsMePt22maqDa/n9Hz5Erb74TTAtLS089tgfKCnZzn3XBzFown04e8sIeXc5vXbI0sLsEIjU6RkUogNBJDvcilElIWsOJbgYg7FU7IW4FKg7CGMnK93nX66CIIviPA4eDOVlyglIYz3ERisnKds2w6TJSsnAscQcel2qK2Hdcrj5bmUpWqfFGhuP9wc388b6/yIWHWRQj0yqyYZBkpB1ouJua/WIe/eiSUnBf/Dg8Y7jod/ywYOKeF+0CD8gyDLGIzKjv5Zj55J+XQ2tKKK67jpU0dHw5ptHXU5EJGJTExkx8azr3sHImaNwNh1EEz4Tb0YOb6/5FHdjA/laPZJKhdloRBAE5H376O3ooMXjIdZq/eoh+8tpNOt0pIkirU4nfo0G7QUa6P2NEYzhViujxoyh9u23+/VD35qTQ9ykSVQ/+eT39ks2wLcUQcCcnU3stdfiq6yk6Zln8F6sxISLyIWsTTwOSUI7cKCSAX3gAI75849eoRAEtJdeijY1Fd9//oPqssvgzbfo8vWwacMq4gZlU1FdTF5GKrs6Kli0cDX16zbxREoIcaIKtQBkZ8HOzZA9EAqHQ3Q0Pkcz7Y0fUbz6ILY2O1mzb0SKioLWJohLQDP7RmLGTCams54tjdX85bI2Vq7YxN6iSsaKECKDRYJOAXq6uwhqaVbqqkxm2LAGuasbxo8/sWAMMsOm9QjtSjIMzU2HlzbTM+Hm26BsL4Tb8G9bgRxmYuO+DraWrKYgYTIjUlPw1laislmR3XZQe6lTFbOr9BMcezxMidlNsNGifFlqtVBbrvyo1DB4OLTUK8v3gwuVMToCULHv0GUjlS/y7HylhvP9/8DB/RARC6OmK7dNTIfYJFjyKsy4CZUoQ0sVZI+BuAGsfusFNmzuptBeSsrQ0cwYm0bpvl1IKhMZGVkXzWl0u918+eU6PvzgA3SeLtapnXy59jdUd4Hd7uOyMIFhITosoozX5aHb3ktcRBSiQQcOJ3LSQILtTiwqDcSnwMEypbP8YCk4epVYv2HjFNGokpALhsGnHyEUjoD1a5TXY/wkpZu9tvz4Ddz6JdRVQ2auIhbTM0GlwtNYwyfPP0lybhqfr6omXePD7ReI1ogY0pIR9u1DNqrB7gR0ynK10XiU48jo0cglJcjt7Ye7po+ot/UfmRndz4h5eahSU5VEIqdTcVJFUfkdG4+gVSE+8l9Gpqews6aEIbIGb3oG/5j3As/MX8RUYIhaTVdPD0aDAemQg6/SaIiXJHw+X7/XNAqAoFbT1t6OWZL6N8P6FHwjBKMgCAwrLMTU0EBjPw4b/irJ5eWXcQZqvQJ8i5CMRszZ2cT/4Ae0zJuHc+/e790JT9/cRHNh4QWpTTwOScJ87bXo09JwLVp0wgxoKT0dbUEB3r//XalldDggLoGI1cv4Qf5wmDgRccJkDrbs5t8PP05+VjqfXJmGO2kUgkeL1xgEe0tQR8bA5ddCUy1yYzX7FjzLuxtr0ZfuJtoukqURINgMMbnQUAYRsbga1qBt6WRAehzv7dhCQXYsWzaW09rrZ6ZFh94rE2wwEKJWK6N5NHqorgJAEET4dMHJ9/3Y5hirTYnpS0iA+a/AiLHQ04UwchKbN7zNgS/XckVuKGHDR7Jf9lOz8jVGGLw02P0ExcfxzjaJRz9q5F6bxIxgLcTGQ2cnTJl1+MsuJh6MJqXm0RwEkgD76yDYCgUjoL0RenuhvRl2fKGM0ZEkmHMrZAxSGl2QITwKFr8Cs26D8iLlNsGhMHAceB1MsAUzfsoQwvMvZ+GuWnr27KROfTvDRt+G05mAXm/spzfQ6dPc3Mzjj/2W4i0LuenGy5G9Xl7893+IELrJMkFipMTcvGh6O7vptvvIjA5DpdIgxiZDVzvkJCPKMpYhI8AQBNXlkJqpuIGiAIOGQUwCpOVAQho0bsS5/G80NXqIqixBk1mg1DfWVCgNIRNO0BUNytzL3EHgcQMySBIqnZ4f3PITpGg/7U3v8sKrfm6NDkWXnABWC8yZg9Dehbx+PVQ3gkanvI6gOI7p6dDejtfvR05MVIZhHzPX8LwgCAjp6UizZilZ530n4xERSvmFXgOCDNUVhMz9X+QQC2N7WpAiI/jwzf/w0RP/4Cqtipl6A/j8eGWZuvZ24mw2AIL1emSvF5fTSZsoEnyEOOxzGlvtdvxa7Vk5hKIgEG+xcKCjA90FSoD5RghGSZLItNlo3bq1374UA0kuAb6V9CW23HMPGkmi5YUXcHxfMs4FQXETNZrj5iaez9rEE6JWY5o1C31qKvann/4qh/koJAnNqFHImzYpS2p9H/qlpQheAZWkQygYTpdZywP3P8soO1wXo8eemsWmvZWMuv1/qXjzAdTlnSQNH4O/sRrBYASVirj8Cfzqstv44zWTmB6mRkxOgbhEfNZQ2LMUf0ws6ryp7Cyv48VHf8Vnq+1EaA8Q6oDxGoEh8bGonC5UPo/i4nFIzGoP1TzJfvCeRi1333zEH85VMp1ffhp6uuGSOQDUL/mQ4kUfcuP0fDSpvcAa1J4Ixt33IK8t/5ypI8fw9tq1LPjvy/z28olclxqDwePBX1GBKKlhtxK1RmgkJKUqgiQiGq9eT8OCR4iuqES85If4oi18ueJF/OuLGJo9AEPGIBgyRlmaTkiHng7Fcexug5r9cOmdUH9AWfK+7MegkuDAFgiJIGrgSIhKxtndQvunLzJnYg7u1maaShbib3fR5Mpl7NiJ6PX685rdK8syLpcLl8vFY489yiefLCTe1sozT/+HaNFLvhHGR2rJD4JkSxChOhGNEERShAGiUqFiL7i7lYaVgYVgVnKbKdqgjGBCgmmXQ0QMmIOhrRGCgkFvAJ8HaWA2S955i9lZo4iZMAXPwndQNTYjRMbCrk2HT8j6En0yB0LqCKWZKyIOub4Sb6cbyRKGJqsAT+dmBI3IXQMMzHzwn7BtLT5TJOKES6haOp+e8j1kacyIbg6/D32+rzr0VaGhyB0dSBYLHlFEyMjAv2HD+ZnTqFYjzZyJqqAAYcMG2LHj8L5OnwaNDRATA7t3wJQZEB4Jej1FG1ZR95/H+GztWn4yJo9RcakcXLGGzRUNDLZaCRYEOh0Ogg+NJRRUKppcLkRJUqYKHLMNLa2tWFWqs3YIJZXqgibAfCMEY4TNRlp2NgcP5TSeK4EklwDfRo5NbGlYvBj5QmQAX2QEUUQTG4s6LAzzsGGoQ0KQGxuxz5vXv3MTTxNNVhbmyy5D9PnoffxxRWidADEyEk1CAr7Fi4/ePkFQHDmjhdaSNfzqN6+iLa5nUGYkjgONOJsOMvHmucgmmTX77dz04x9zwK/G8e8Hybn8VsSh4zFbTWwr+ZyUuDCM9laQZBBl6j56hOreHhb88yEGRbv4tFRiqNqFwwR72mCwCZI1GgxqNZJGrYxOcbsU0Sgcyg6Ojjvk4HyNYDx2PuKyRcprMfNypfnE7yVy/KXcXDACMVjCX70SMcFKktlFV1AnMyb1sHBtEekdO3jsxvEMuetvyBt/R/PqKqr3lTIgNgnN5DmArLhfbQ0QFISvu43F7zxPbFouMUkhYLFTU7GQPS37yA9SozepwNEOUhRExyHLTuzVRRhT8mnzNIBGh9XvhiArXPOAsu+ObojLgo56yBkPUclo3Q7+99FshIbdVKxcgLSjiba89TR3Lecvf5rHzbf8lLZONenp6YSGhvbb+6uPtWu/4Mkn/0BVVR2dLVW0NrmwdMjMjlAzLhREtFyWZkOjC0aQtAi9vVjyxsDgqVBXBmHRgAix6UpneEg4qPzQsh3a7ZBaoLxePa3g7oFBoyDEAh0bIDiUspYo4qZfTtS4idSEJ/PfffuY29NCkEpUlq43fQH5I5QuaI0WcgdDeCS+yi2IUQZ2dFey7l+/5ors4URf/wua6irY15OBNkWgJjadF1/4JzcOHo6+tYzfvv4McV293KL3Eak1EeSUEbT6o+sSW1qUVJjubrTDh8PevV8lyJxwnuPZYDAgZmWhmjYN0euFRx453LEtSXDttRAXA3v3QGY6/OB2sFqhpwPHwd3ceveP8HV0Mz7WijUxjHsWLKCnyck1QJBOh8vlQuP1HpXGEhsSgt/ppLmn56saR1AcwvSwMFwuF+5Dw9XPlAudAHPRBaMgCEydNg1XSQmOfqrPCs3JITIjg6pnngkMMw7wzed7mNhybJezPjoauaMD1+bN2Fta8J7JGI1+pG9kjvvtt3Ht2XNSsYggoJ02DXnHDiVar4++rs+WFoiMwL6nhVElDURpBMKaO3A5VPhHj6PaG8T7/3yM9qjBLG3rwLJ1AcGyC0F2Kk0JLTWoa8tInWylYXEnyXodBFmJC4/C37CH1bs7WLbZy9REHavboKYLLguCTDXkWs1IXrfivplM0NMDFmXMB35ZaUg5tA90dsCxgznUapg8TWlqOXI+oixDZg5MmKo0pAgyYloOQksF3fueQpc1lTKXBsHVy941r7JpYxFzZv0JS2Y+4RLYqz5gu3cIr365nPQOJ9GjU4navRbiM5XGmoxBEGLFrzWQN3gG8fkZIHXjci0kTMxgwNhC1nz4Ga2fvMfUy29ESsyGyDh6t76CHBdLV8RyXnvyTWZ0thNy198QkjJxdpUj2x109vQgub2EJWWAJRT8XgS9GXV0JkQkMSFnEr6W/ez+4n0qyuu4dlI4e/d+yXPPv8DfHnmc9etFpk6djt/vR6PRnLHz6PP5cLvdyLLMwoUL2bJ5M2tWvUNLWwPBgMruZ3qqmtsHB1FgVeHThREcFo9GZ4HkQkXwtjdB3nRIGACxYcj7JeQOF4JR6c7F1w3OEhzjr2FL5w9pXbyVWenDUA2eAJEJEBKKo2QtNK+mJmYotS0ept45gd7KN9j6ro0QVweiXgA10NWiDEKPioSi9RCdADmDYNsaqNxIacVSqsp3Mfb6ewhNiMOnaQNdDDf+5GpkeySbixZgSR9Kb/5IfvfTB9i6r5c8AWpVbvQqDya9EcntVEYfHTkntK92ce1aJcNZlvGHhyPEx+NpaoKxY8HnU5zHvkzqU61K9s2iFATEAQOQJk9GUqlg2TLYu/erpXHUapg1C9JTEF5/Bebep7iKEshdTZQUb+eVv/+e3o4eIoCG2jZefmUpdh8M9UIUigC0mEw0dXYSZDR+1dAiiiLdQI/dfrjG8RCSSoVeEMDhwKnRKE7kGXIhE2AuumCMCw9nUHw81a+80i+Pp7PZSL78cpoXLMDzPWwQCPDtw5yd/VViS8M773ynT3JO1uXc9d57yB7PiQdLXyA0OTlY7rwT10svHT0y5wRI6emok5LwvfPO4QtlGf/GjYhdXcqZfngEsZKPm+PjIDEeGupoNxsxWyP5/c8fRKs2cPfVw2je9S6ehG50Q6Yhx5mo2/oxUYWXkZ45lCd++DZX5eQi5I/H6+lgZ0gBve5WgjQyLXb4stKJWYABGkjRwoDQEGKCzUq94oiJUFOl1ApmDVS6YgUJurogygZNLXD1tcc7GzFx0Fx/9HxEg1ERi3fMVcSnwQj2Vti1lB7UbN4iYy5ZiCsxH31cCmE6iNCHY/YW8X9PvYmvS42EjzU7XUhuNwNiJcxdDTD1GkXApQ6C6Hham7ZQ9c7jDLrtObwaDctWvEVJ8WKWL3mNB68qYPnmcobF6BBHTIHETGgvwTBgLBvKynj3zy8wc2AD5au0pFrDkGJS0BgHsG/vWj5duoaZY0YRljoQn6qD7S8+ROrom/AJPgRBwBpuQbIGM+jmv5Ptl6kq2Uj5h6/z49nDkTsa6Wkr48lHlqHWG5k5+yba2rrxHRIqWVlZxzmQzc3N7D1USpKWlsYbb/yXBR9/iFbsQeVtIyE2lnED4rBq47gsz0K8xYQUU4jGEotg9IMYD/4uqNwBuZPB1wXNX4DaDl0H6bK62SptJcG1kYTQyzlYE06SzQGpCXz66RLqO3aRlTKY7mmzqFq3igEGHZKjk16LHn/kZTTt/Ruhmiiq7JGs+ryKgYMyKG9MQnvJTcjNLThWrcCQNRjww4ACiE2EL5fCyCn4NSoan32QcaPTcIT6+NlzrzB7ooma4k1Mv+J37Ny+itdefI8JGTLPPvwOdyWGUFUqMNSgYXRMOD1OD3X2HuKCLZCUBG3tJ54TekRmNWVlSq2jSgUlJXhychAHDsS/cSO+vnmNx3JEgpKg0yHq9fDZZ7Br1+EOaqNRmZE6baoy/unF5+C2OyAxSTmJWf4OXoOGj+e/Tlunj9usEiM0AkXtHg64YKReR4TXiXjo3DZIrydIknA5nfh0uq/SWIL0ejxuN/UdHUd1TcOhpj61mprWVuLCws7KJbQDQZJEj8tFkFb7tbc/Wy66YAyNikLV1dUvg7oFSSJl1ix8FRV07d7dD1sXIMD54/uQ2CIcSirRhIejz8jAOGTIhe1yPp1tPHK+4mmIRSQJzYgRSu3ikeLe58PX3o40fjzCwoXKZaKElJ2viCujidDx0/Af2Ml1sQlEYGfHkg1UdnYyODMER14R2+PMVK/fzOzkLLSxMVw/JQbCx2PvaeW1eU9RvvZjlhzw0dboY4ARrrQJaPwyGp/AQKuZpJhIRI9HqWvrboPeDhg1HmzhEBWFv6UVeroRS0pg3Hgo2shxDuPWL6H2oOJGihLk5sG4SYpbaTCAxQoqEVmlpXLzK3hjJjDu3l/j8Mv8+YmHWfvkf5k/7/fExDfyxquvMLmggOJlG6luF3n+prGYnE6G+bswGY0gojS4BAUhO7pwNFdgHZDB0qUf45XdtOz/gsuv+Ruqzt/R5DPx4/GxWF1aSM6lu7OJP//t/9Br4fIb7mXqpFvBX06V5hPazRLq9lWYvL1IKgsJKQkIUgndjnA+WvoOa7e08OfZcbzx6mPMnDwc60ANdCSAWosaF6kFo3hgiA/EKYCG/FEC5WXLSUgdz6LF69i0aT4zZswBzMDRJzlut5unn/4bu3d/Snp6Cvv3j2f//sX89Ke3YQvykZEoER45SrmvXIfs8uARIvH7veyr/5SU+MlsWzWP9PQZNHolNAfWklB4M72qHl578UOm58ahyxMRJTPlkZfxxxc/ZExEOXf++VGQJCZMuJGHl31GeOEU7njiefT7N/B32U/4gNF8vmo5Vk03zf4UModMoq1Dzf5KmaxsiZFX34wwKJ33/7sa3549XGvQKmN3VBK0NsDwSWCxobKGUnjHb3E0lrLx8ceI7fFT2wHh0RK7tu4mafj1iPM+ISOrgJGJdWyotTDRXM4teVloOzspbWkjUmOk0+/H4uhEDrcePSe0z6U/4u8KOJxIJIqojUZYuxaxq0v5ezuJYBREUXErXS5oalJ+C4IyDD8rCwbnK9nlK5YilO5VZlHGxILZDD4XXHoLalcXv0kJxVu5gt0vuskaMILSV94jw+klHLCEhOBp70BAcRnRaNDIMmVtbUQdakYRBAG1VkuQz0en03lUAwwo2iXJbMbn9Spu5xkSpNXSZbfj83iQtdrzNsz7ogpGtVrNnClT6N6+vV9qtUIyM7GmpFDz7LPfu47SAN8iRBFTaiox11//nU1sOdJJ1EVFoY2JwVNUhHf5clwlJRemy/k0OOV8xZPeSUQym5G/+OI4sSuYTMd/eVVWKIktIQ5ITEYMD2VQeyP4PUw5WIIcEY4o91C7cw37yipZ26JlmqEJbc0iQmfdwJaKWK4YU0hlXRc2lR81cEWUxE+zrITrdGyv7WBgkBm1SoOYOQhsEcoAbbsdLrlKiYMbOg6CQ2h99c/wyQLCsgqgrFgRgdkDT7CTh5JagoKV4cdrV0BiKiQk4Vj5Hk5HDR1paSypaWfqEHjlv6+y8M3nkXUG/vqHx7Bp6hETcknIy+T6a4bQMjqfqnIHBZfdSvMnT2E0RMC2baDTgMcBLjv7i1fzwG8eJELr45JLsjGGhXHzL/7BZ5+XMuvGdFq21VDw899QtnEv+3dt4kDNPlIis7h57s/QBceiUsfw8fw7uGrGRKx6I1izEAxBxJtMvPT0iwwd9gNMoZO55prxXH1VNaIQR2puIqn5ScAECOn7OjwUaSgnAOqvlhaTM68HRGbMuJwZMy5Fo9FwaMDJUUdOrVbz618/BPwJURQBkRtvvAdNXwShIMNXC4cxeDRuVq9+G/Azduw0WltMhIQloAsbzptPv8i999xNWXkJ7733LqEaDUnD8hHDYwiPvIdfP/BjvK06rr95IHh9YK/n5Zc3k55lZP+uD2kq2kqmXkWozYDK0UzB+LHoXWVYUy/FZ9Bw/XVTGBrqY/+yV/iiWmZsmMAjS9w8Fa2BsFDobIKkbKUpKcQGfg9CUCiuqAj8aj+z/rIAwRoFgg80Gmpbm7nzR79iWIqNXkM1H39cRW+nxCP33k54dBIdpftQv/sRdb09xKs0+FtbEDs6kENCoLdVceLb2pWTiJaWw0vGR+L3Q1Vft79w+OTsRBgMylxRUATi8OHKeKbEBNi9E1YvQygpVnKzs3Lg1jsgNBR/Tw1bX/87+ZfcgiomCZKS2FduYIfkZt321ayxe5kBGOxOsiJsuAWRcI1G+TwQBLplmWBJQvD5vspvN+t0tPf0HFfjCKBTqdAZjbgdDhrtdkwGwxmLviiLhcqWFrrPo8t4UQVjdlIS0WYz+/ftO+fHCsnKYsBdd1E/bx6u5uZ+2LoAAfoZQUBQq4mYORNbfj72TZvoXLbsO3Nyc7J5ia516+hua8P3Dfu7FAwGQubOPfF8xVOgGjAAyWBQGnJOxLGvpyAokYCuXmUJTARBowKNCtW4GaDXgUogrCgPjzWJW0QVmq5aemUj7771JP5mF3Gyg6wYFQPNAi09fu4ZEEaSSY/D6aNgQBbtB8qwDhiuNLR0tcOM65RlxPIi8AlgsSKLPjrETjZ1tTG5oZqIhGToaIJt60697z1dMGwsDBkBpTsRdmzCPyCPZ195g54dpZQf+BsrtzsYGKrh57fMIrFgMBtX/J7xV9/CjbcGY+/9nCUl3eRFh4LQju2y61i/6FfYm9pIWfYGyVfeo7gymjB+nWMhZuo1hEyYhmAMRqUNZerUAfR2DSU6diubdhn47fP/4J9/Hskll6aCKQIM9fj80Qiilsuu+zNx+krE6EQQokDSo8bHHff9nri0NBB0aLUCkA3A7Cv/eKJ3xqFfx+T0CsrSveZr8nsFQUCnO90h4AIajY6pU2/96pLIKIiMuh+fz8eNd/2e8KQ0wlHzwANPolU5ERy1IGSwePECPP5G/vnCaxhDt4AuAaRoZs6MIiHhSv791O8pzHPzwDWzUKXlU1a+mK2NG5k88QbUIfGIfpHfPng/65avJNf/GaHBw3nqnS8I8vowagXwuxSRmFMAohf37oWoh8wAsYe9FZ+h2l9PXsEE2nvfomt/K4l517Bjxyb27lxPWJeBYA/EpaVwd14klut/BpXbKFvxEps6OplkCqLT3kOw0Qx+H0Jri9KRLwjIUWHKwPfPG2HcuMMlE2439NUuHktEhBJLeeQJnChCwWAlU72z8+j5ost7oaXp8BSAu+8BrQq2bUAeO57tn7zE6//9iAgZYi6/nM0tn/HrfxbjLmmgrVdmlhpSg4x0d9pRCQIxYWH4HQ7cbjfo9Vj0emS1GrfTiQsQVSqlxtFoZGt9PUOPqWU89MbBIQj02u2YziLBpS8BpsHhwKTVnpdaRkGWT7weNER//qfeXzZtGmN0OmrXrv36G58CUa1myE9/Ss+aNXRs3XrRl7gCBDgRpqwsoufMQSXLND37LH6H4zvR2HLR5yWeBX3Oorx7N64FC05bLAoGA4Z770X87DP827cff4OwMFQ+HypJgoEDFYNq/Vq476eQmqo0i2jUStRdwUiIjMXX00R1VTWJQ8eCrxcvfhZ+8gnzX3mOx+/K4aCqnK5lVYyaPZeWdR+hr6kjbOIPECJSwC/js4ZQ+drzpOQWKM7iJbdCdCJ01oHcRcWH84i/+hc4i95mzm9e4efZCUwxqJAcLhB0EJUA5QcUnWQ/wtERREhOh9nXQHMtrP8MCseBWqKotZ4//OMfRGg15IWpGZEdR/Klt6BPTed3zzzJ1EvzGT3xF7S3q9mzZxVDCvw4emIwlT1LizWB//vTy1w3Mh/PZ7uYdfWtMPEWZJ8bXL0I7jLkmI9Bcz2CbjZ9Lp7LZec3P7+d2cNsjL/h1whCGXR349On8enyzaSnDyM1NV1xwUSVsv3fYdxuN+BHo9YCHvrcUJ/Px9P/eowvVn/Myy+8SWVtExVL/sgl0+fgz7wctS4EkHB7PCxf/gpjhiSwd+cOPO7VvPbAMm5MMJORO4zw9DzIHQnpg6FxDz6jluruGsJMzVxx7V/4118fJiiolA8+38Ttd/8JQSPwi5/ewaUFI3jlmXdp7ZL554Ozqe3dTFtnNjn5o/jzgw9BlZu5BoksWzghglaptxWPEEeiIhDl1HSl8aZPwO/eqXSG5+UdfSB0esXBP1ip1OeC4ky2NEF7m+Ig9mmCvpVMg1GZApCVfXgKwJ6d8MP78S55l/c/mk9CmJUcs59io5f3qzoZMjmR7YvruDM0mIa9NRhlLR63xJAgG4JWp9QwSxL+zMyvEmwcbW2UtbaSEBmJKAg0dHdzoKWFQfHxJ5ybKMsyfo+HNpfrrOYzdrlcdDsceDWac3IZx1dWnvDyi+YwhlutjBwzho633z63BxIEEmfORC0IdO/ZExCLAb5xHFmr2DF/Po7iYnwXap7geaDPSdRnZaFNTLy48xLPlGOSW85ELAKI4eGIdjv+k9VINzfjz84+OlotdwDkD0b2OfBrzUiNNTDlMmVQsgCfLZxHokGFrBlH0bqHePr5UtYtXcmoaInu5kQaKiq45N7/xRMynK1v/ZmJkYkwdCZyXBauus0sWrOKqKrNJAerEGIHKF++HieYrJSU7+O1zev5/RVtNDYX8/BfH8LZsYUVbyxj2pzr8AeH4d+5HTEnF7G4CIYf4ejEJEBCstIl+8VSZXZeVDxo1MR3NPPnjHAiEmMJCw+BW/8MoaG4d73B0HAvOm0C+HYC+Wg0ZjRaEx2dFnS5U1k+/0tyEiLYsb2SPLMRknNAp0UQWvG51uNrVvPJ+hhy8rzEZ7tRq9VIkoTT6aa7p5MBhdchCO0gRkDwePB5SU/vIi0tDRBAOrUD+F3haKfz6H0eOnQoN9xwOSZbNJ+9+QFTJ8xCispG0hhBUH11/2nTbufRR//ORx89C91enp6Yz8Cr51LraKJ8WzFJZjOC10lnTz2fL1tM9rir0EZI/OXJuSSmDsWzfxn3zH0Rny6Fnp4uomIiaNHspMPloatH5tFHPiAoFFpdPrZu2cXn+91M1oikREfQ3esm2KRHdDlAZzw8H9SvnGAKpSVQWnJ4pwRRqS3csh4iIpWkGZsNUtOUeYkOO0iAzwtfrgRrBISFw6ixxzuP+YOViMmeLmUKQGUZXHcrbFiNqr2Na6+6FtobIDmd+JKN/GSshf0N1dw29yaKFn7Amh4PlwheBltsdLl7CRYE0BsQTSbE2lrE9HS8paWIubkkbt8ObjeyRoNFpyPXaqW2s5M4i+W411Q4tAp1tvMZ+2oZ/Z6++av9y0URjP2Z7GJJTSUmP5/KJ574Vn8JB/gOcrJaxW/pSc2JnETXtm0XbV7iGXMayS1fi06nLI2dYj+FiAjlSyk4GAxqmDgJubGStjV/pqaojUFTroW84YAMri5G5YTx+ccvYw8WmXXTS/idLnJM8JMcM6nx+aTO+BENQi+PPDiXazL8LKn0cXXjbhobtvPpJx9QOG0ajWMz6M4dTemX20kt+hzL5Ovp2reS3Vs/YebocGpb19MQ3ETlun/TSxRj77wO36gbKFn6v+xasoThGgNJw8ZBZAS0H+pWbaiET99WvliDrXDTvUrNo9dDiNdFyBW3gr0TJl0FMRF4e3Ywf3slg8bcSu7YaxDQExamIixsBgBRUT62b29md8l8/ufe6ax4/lNs2ihIzqKm240fCyV7ekm3FzFz5u8ornXw85/eyH0//R3p6QPZsGEtT/7rbkzGSw/VAfa9rCrS0oad2ev4HUaSJEaMmvjV/++5515UKgn8buhYDLYrAfB6vfzud7/j80+eQ3CLXD8mn7xfPowqMhpdxcfsf38JCZW5SJJIUI+d6Xf8HLXajWC8lILRapYt+4zx457Hp7bwxhsv89abL6Dp3ssOlZfUkBB+OCWRV5buoKLMT5evnqoKyNGqeCDRhtPnx6yRlNIMlQwJScps0L7axSOd7rBwxREUBMURjI2DwQWK8ajVQFM9WAxKrrn6UErQL/4XOlvA4VKiDlualZICey8IflizDPYVH/77v+xaiIqCBa8rKTeubujsgqzBRBeOA1sYzuqtPPfgL1lb2ku+DD69ml5k4i0m5URqQN5XJ4qiKKGZPRt1STGummrKKirRm80EGwzst9tPOT6nbz6j0+mkzeXCfIbCL9piweN00uhwYNLr+7UB5qIIxv5KdpF0OtJvuIGuzZvxnWxeWoAAF5rvSK3it9pJPBZJwnTppadObjmNx9AMHw719SdfZhcEhMREZX7jgFzwupTaKZMOMfcGkobp6FzxDuaOyxFUfnwqP0s+W8yrb29D9d5WUvAQGgQTo7WkGrVILjuu7nqefephBuWMY29XJ7PvfhBvtIftLz3OkMk/5Z23HmPq2GE8t2k1Vct28HBSKqjVCIZICrUSHc5etq1+jj379Vx75QgWLqqiuWwhO9tqmPfGMsI7vei1vYR43VjWL4PQGAiNUL6g80co4nfoWEjJgLpKJZZv6ARwdEF6Hh7aqDjwR5avrGPf7i6uvOXXCLhACDrq0IiiRHr6UG666dd8/tHV7CxzMOyWO5B76rDvW4ku927UGhOGgbejCc9G37aXe++9l/S0NACmTZt15q9ZANR9zTaS/iuxCLBo0QKaG7bw5huv4etuJTmpGmQHssZCdY/IKhfsX/cR44yJpA4cTmn5a2Sm3YcKPQgwddo0QJkxmZaWzpgxhUwt6GDz8m4umf0I7eVfcI9GzYaiJl7fUoHkg3uijFhUMkZELKYg2ts6EbUmLM5OiAqD2ARY2wijDzndwcEwYBDUVConaWoVWINBr8Mvd+DbOA9p8v10NDWw9qOXmTHhdtTZuRAdi7f0S9b89QlyuxyEj50GOzYpcYmle6CjXWl0GTIc4hOV4eSvPaMk4uTkwfsvwcRLwWJDjorjQG0F/3rib8T6tUSbINfvYXSkjaDoJEjOgNpaOLBHiWRUq2HKdBichz8hksrFH+FyOfFKEma9nniLhV67nQ6nE8tJsqQllYpWtxu/KJ5x17MoCLgEAbvDgUGnO75W8hy4KIKxv5JdEiZPRqiro2X58jN3CgIEOE+YMjO/qlVsfOyxb12t4rfeSTyWPmcxNfWUyS1fyym6o/sQ4uORoqPho4/AGowQGQ7RMQj+XnxBdbhictjes4fsra8TqlPz+r79vPX6J2hdXmpb4KoImBwmkJdiQ5OcD5Yk9i37N1Ehg7l+lo91JZcSka9i29bXiRj7f+QMzWDXbiu91fspXrKPp373Z4LDdFC2BI1PIm7E1URUrKVxfzn33Hcbby99kYrdxdSVBFFS9Sa76/0kqsE6IJIgqwXUIuQPgYYaCAlTBiuHhEJomLLPLjuMngHBIcoSodfN1g/fxRMUgaa3gyvGG9DrK0GYcMLjYzabWb5iHUVbtNx17c/JnnwVpeU1WGO6CY6MZM+eGMLiRgIimVk5Z/c6BTgtLrlkFpdccsmhGkg/4AFZhcvtZv4bC6hsMBKcmYZarMfvgMyUqUimmOMeR5IkRowYR35+IUsWxjJyWg3x2XHEJM3kyxgbr374KDFqiWtiDIwPDUbQ6Kitb0Zwaujx+4gVZWhrVYbIV5VBSjroDtWfOrrgrRehqeFQbvg1ynzEHRtpW7eYNbU7GRE+kH/++1GyLLEIsVZqDmwmWnIjiMXoVY34ckfCtOm0aGSsnd2Igh+uuUkZPdVUp+RnL3xTmc045zporFGSbvKGQWQsfq8dVdlC/nLjSL7YDTWvzmdqmIUgsw4SYhWXPS0Fyipg3ASlBCU8HMwmPt9+gMf3lnCJw89wnZ5upxOTVsummhpSjixbOQExFgsVzc10q9UEnURYngyzTkeY201DVxfRwcFndN9TcVEEY1ZCAs5zTHYJycoibtIkqp988lvn3AT4bvJtrVX8TjmJx3Kks/jMM2cvFgFVTs6pu6NBceWqq5UvH4MBUlJxW22UbS7i3YdepDckmnWbm3kjp5JnisrYvbWWbK2R7b2d5OohRi0QqjehDkuCWfdDeBxWycKPwyVkYyeTZw4BScXggn+C34jfv5XpMydT1iQQs60KXUYa+KroqlqMu8GHbcbf0aYFMyW/jXZNCc0Vu/mfXzzA22+/TFi5yB9TRLLNBtJDNHSVFWMZNgH2bVeaBoZPUpb9kjKhoxEcbTBgMG57Eauen8e0WZdD+hCG3zCXA8XLeeiNd/jtj67F5fSh0TQiCMeLC5/PR0N9PT+5/0+kxEYpkXfqIKwZDyCpTEyffvVZvz4BzoyjayAl5UeAlSsXMWp0KOML72bwuCksfuan3Bz3azSCQ1nOPQlqtZrMrNEs/fQdivf/mdjM63j51Vdw9/i5LUpPrFGLZNCATyZ4UD6W2hqsuYOgeOfhJKKOdthfovz0IQAqNUy+BBISFSdw0gyshRMZZZxJkLGZX/9oJlpbDBsPvktT12DG+2tYt+JD1lTV8as5Bazp7OXNN17lNr+LYZMuQdixFhLSlNhMUYAhIyEuGfKHQnWZIkoba/AZBD769H1a9+0kO7mETxe3Y1NDUrAWBg6GkRNh5ET8H78GYSGIkTawWSEiEmdHAy//69+M9UMM0N7TQ7fPR2ZEBP7TOOEWBQGb0YgEeGX5hE0yJ0MA1FotFr+fbrcb89d09p8uF1wwGnQ6hk6YQM/69Wf9GKJaTdrs2TS/8w7O79j8ugDfQr6ltYrfOSfxWPrLWex7uMRE/Nu2nTrPVqdTfgB6euhet4pnf/IqO0sP8MCUobQV5LBkbQkLV9WyaN0epg5IZOWWckbqoMCiZVaCBa3ZgJA5GmftDrShBmJ710LUA2Dyg2M/qEeCZAZJRpCjaZZNPPiby/nd7VeisSYhB41GZfZjyN1CZ/suCEvEEmdBbY8kOu0R/vbkQzi7Qnj0T78mvGYHolOiYdVnRFlFpW5REGHEJLCFKTMdXZ0QbIcgCao30VK/muhgD/WOWhzrGknOzsPv0vL4leOJTB/Ky48/zey7/4xK7SM8PO6oeXNlZWUkJkoMHnLtoaVSkfR02zm9LgH6lylTLkEUpuHxyLz40lNMvPIS1FEW8B0EWX3cnPcjWbh4LY/97RkSI4P50Y90bPislekaiA5SY7WEICRmINgisTQ1wKib4YvlSslGVibsK1EEYWeXUivbR4hNiaP0eeA/j0OIFdKyETUaOvav5Ld/fZQ8SWSPEwZZPVx6Syif7WxizJQZJFhD0Kel8svbbqWjqoPJERIDmmswJGWA2QimKOU5tDqIjoeuVsUx7G6FMTOoWvEBPfNfZsSwgVT2jGXZwYXMMckI6akwdjKkZuOXoMReR8TASYSqvcitNVSWfEnRhoVkVO0ie1AaG7cdYIikYrDRiMvrRStJaI/M0T4BgiAQYjLR2tlJg9NJzAmaZE6FSaOhrbcXwetF1mj6pZbxggvGcKuVULud+tLSs3uAQFd0gG8K37ZaRUFAVKu/m07iMahiYjBMn44uJuacnUUAMSwMTV4e8ksvnfxGkoQ0fDhCXZ0itj0eKNtLwf4yxqn8xNXW83HRBnwOH4uWbeYyo4B2Txn3R5tJ0cmEWIJp6bQTG58Gseng6cavD2Kdy0ehvQpRtQ2V8QYE+r5MBQRBYtnSHcyZPYDh1/4awWhDZjeCZTyCmAYqATztoJ7CoiWvUXZQRu1zMG36bMJm5CNs8rCmvoNOh5fLwmyKq+j1KM0EkpLS4xcFBHxgTqREtY2n3t3BP/7vTv7+z78R32gi+fdPkz7lctzrm2lT6bls0njWfDKfcZddDcRypMJIT08nPf3Jc3otApxf+moeJRXce++vD1/hboSeD8B8FYojeRifz8f777/PK6/8g3Ej/PTW+ln85kbiRC8JegFTUhqmlGyor4PcIZBdgF1o4sAnb5BqMmMoK4aMHKg8oCzrao9YgjWZlS79sr2KaBw/XfntcBOqS+B/Bo7CFG5jhl5NSPYgdFlDmF7QitDzCXUFo/m/556nrbGboSYYnJuFITYaWmuRC0aBWqWMYNIZlMkCrQ0QFac0enU0k2wNJvmunyDnDmPl4mVMihSYk5qM6t6Hkc1BOBtLwa4lODQYMTaU+//yKFpJx/ZVSwhz+Wjzw/vbSsnwwI2hIexvbSXcamVgeDjNdjtBev3XOocqrRaLz0f3GTbA9M1lrOvuxqjX90st4wUVjIIgkDtoEDqf76xrugJd0QG+KXxbahUFUUQTG4s6LIyQqVOR/P7vlpN4DJqcHCx33IF3zRp633xTiQM7RwSbDRoakE+1HC2KiEFB8MUX0NMDixZiUouMDwunS5RpqWxhokciwugjNzyEzBATvQ4HcRYDddYIIpMSwWVHHDsDvF40yemsee2X/OXtNVw9swSbpYs5d8ThYgJ6vfUr5y4lNZmJE36E1pCubKswHL1BBXIuljDl/dja2saaNcvw203cc0U0OVNmsmz1eiJCzTSXv0hKKHQ0lhO86ROElHyISYTwOHqrt7Nx0z8Ye8UjFO38B11d6Vx3zY3sKF5JTFAG14zJVRpkNCWs9+5HPqChcPosLsuT0QRlnvHw4QDfYNThoB4EVAHJX128Z88ePvjgA5YvfxGPq5U1a2ViNR2Mjw1m8oh0RhgFgm/7BbjsuKwiPY2NWOngwYf/hqNsP7/OGURSQio0VCjNJ72dsGYphIYffm6vF1KzYNxUZTaoCDTWYkvPwWb7McSnQXQSyA5cPhcv/vwmDu5v4YsKifp2D0PNMMcKZrkX0nJh8tXQdADX6tfQJQ2HsGzYvQmiU6C9EUzBYO+CYVNBEmnb9CaunQu4b3YuxsQJOPQCa7d+zj//+lu0di85aj/z658lyOPE4Id2r3Ku1uKFDB9MN5uJNpnYU19PhCDgAco7O4myWr/2b8Ss1dLe04Po9Z5xA0x/1zJeUMGo02qZNGIErYsXn5ULE+iKDnDREUUknY6wyZO/ubWKgqDUJWo0mIcMQZucjD46GrmjA9eKFbj37v3OOInH0icWXfPmfX0m9OnS1x1dW3vKIeRiTg6iTgeNjUoNYE4OxIQhNTUS4nMQEhtLgsNOtrMHlUFLV2cPCRFWxJx84uPiYetqxeHz++iV1VQ98VOiBuVhVQdTu3Evlz14NxV7/4Vb3EhFzXDS0nNJT89g1qwrjt0S5ZcgHP43cOONNxMXfRVxusXg9TFpwghefmc14667mY+KnmNYSycThlkRouIhyALt9bRXlqKpcrHzQDvP/Hs3Tz73K9Z9sR4V3Uy7/j4+31RMetVeUvRusgbOJTQ8D5fLg1rj+s4Pzv7eIQhA5lEXlZaW8o9//BKPZye5WQLJ+jSumw4GQUfBNfdR11RMx479WEIj8YdG0Vr0KFqDH9Tx/HCSCeeE+7Hlj+Wpfz/E0L11FM7JRSz6EvKHQeQRNbAqFVhsSlKS3qC8reOSlbznvDEgu0DwgMaI4Hufik4Hbq+Be2YORbd3I5MG5SJZEzHbYiFzIJiDEKo70Y2cgN/cDvIuhLA2Siua0FQ0kDTnFgiLUmIrVUZ0aZOZ+0Ai7e2LcdeV8LNf/YzVX2xGVmans88P4SKEAh0+GI+A3yczQmsgwu8n/VCedB8yypDu00EUBEKMRuq7ujCeKCHmVC8Z/VvLeEEFY2F2NrrWVqoPHDir+we6ogNcTARJInzaNGyFhXgPHvzG1Soe6SSahw1DHRKC3NCAt6KCrvfeQ/Z4DicdfAfR5ORgufNOXC+91H9iEU6rOxpQxuls3344/zYi4lDBvgTGUEhPQXOghDCVhN+kJzg8CrGzBYzBsHkNTLlCWa4zGDC4HWTc8Ft2rnyDCLeLH02biGPbWqplP81SFbYEIynJ4097F2w2GyNHTgNkkKfR01PC5pV/wLF/DU99mgDlLsZEaRA6q0GrBmcPJOUSPWwGYam5/PG5V7n1jl9iNnYxeepVqFRX8Pe//4N9X77JC9OfAG0+4ZFxAOj1asBwtkc7wLeIlJQUnnpqPl6vC1/7C+iNo5A6KqluEFHlJVIy7ymyB46ktHw/abYwItMLIcpH3YEVZFz3JNvX/5zf/voZgpr1RGdm0PXlUizpA6F4CzQcVJ7EYoMhoyAuEcKjlK79pn1gttCxbQVtyytJnH4dYqcToq2oNZk8dP8lYJvF9jWvo3UFEXLtA5T31NO+/AOSEhOhLQa6WnD1ltHU9g5dwYXUO3N56ZXXeejOWyErHyQ11K/B/fajGH74PuU9bfz7ySXkpaSQULuTWzOiKC5rIMgvk69TU9vrodYFuaLArVGRLKtuYFpwMIsbG8mKj0cGOs7yOAfpdHjcbho6Oy9qLeMFE4yCIJCUk0NvWdlZfcEGuqIDXCwESUIbGUn4tGmY4+Jof/ttnAcOXPzl51M5iZs3Y29pwVtd/Y0RtOeTr5zF/haLnEZ2NCCEhyPl58MLL5z4BpIEe0rA71ASIUZOhKpSZSBxdAKMngy5g5U5cx3NCEEWpKQsksu38ac/XIqpoJDPF7zCmMt+jNqaDIL6LB08AQQDpfsFPttQxZxxc7lsWhVv/6EYndkII2ZDci6Yrcj42H/gS1ROPzdfdQmZQ+Lw+WMQRQm/X6K3t4u7f/lLpLCJIPScxbYE+LYjSRKSZAAMYPoF4Gbltlbi2cqCxX7mfdKK3/0pj96fA/p3afcP5a1n3mLWFRPZX7WRPzzyOUkqgVv+93Y+f/VVCrtUWJoPwuAREBapFFKGhMGAoRAUDD47Tr+T2rplhKVO4L4XPuAq/24Sr/wB5V0rSRBvRVJnoR0SQUvpSxTt2YvZmsvgsAgkwYsrr5DFH/yDYYOnEHr1L6C9juC6NN75+ANMdU/xwO/+TUrtMnB2KXW8JjOOEZfx2N9+xsoVG7F09XJ3gZ9PzRLXDVazolvNlKwcujDwwYp1jFZBpCygFQSSAbUgkCjLqASB8vZ2WnU6LDodHU7nmXU9C4LiFF7kWsYLJhjjwsMZlJBA7apVZ3zfQFd0gIuFxmYj8tJLMScm4ti+nYZ33sHvdF7Ubfq+O4nHct6cRZTsaO3kyfhXrDh5d7QoIk2ZgnjwIDQ0nPg29t5Dcwx9EGGB2oPQ0w3BFohNhOyBoJdg9xIwRtHRUouzQyJi9o8R1CrK9i8lJjMOVbBBWXrj3JaWYmJi8Hjzaezayr7Oepw6P4bUHISELLCEgi2IL77czVvP/YHf/2QYNfX5+OxxLFnxAek5w0HQce21M8jOHnFIuAZ97XMG+K4jAFrGjJuBSprOe488Qnw43HjZA8jqMhxCIiGWbu6865c0d+r4119upiBaZEjeMK66/xnGiHZmp0VATgG01EBLHYyYrCxDO7qgsZgGQyVPPPRffv+7+3jv9WcpKykh/Vd34KnfTpTYjKjpQpY1bNzey0v/+YwrxuYzbfQwRIsRg9HJX/62iLuv/QHWoZPBGoXW14gmZAw/yTRQuVJF6tjJcKAZKj6lKy2FHbsa2bBxKfbdRRjsUNnqY8Pn+5kwcSyftXYwaKCWyNvvp/mFR5AEyDIbSDKYcfuONxN6HA7yVCp0ajU6r5dBVis9bjfBpzlj8ZtQy3jBBGNoVBSqri4l4/AMsWVloRbFQFd0gAvDoe7noOxsIufMwV9eTvNzz+E5xxjLc9qegJN4HIJOh3HaNAxjx54XsQggZWWhUqnwniw7GhDz8lClpMAzz5yyxhFBUPKZh46Eok0wfDyU7gJk2Lsdr7qUUudy1MafoG3+CE+pFk9iKurQMDocB1CrTAiqUBC0nHK2yenslyRxyazLMekcvP7QLxHbQG8QwRwCficHt6zir3+dz8MPPU1kag+RI67A5/eQnuFXkldEPZB+TtsQ4LuJWq0G4IorriQ2fAqrv9xGqMaKwXAFaCIR3Z388YH76Sjbw+irJvL0q58T47MzNlSFOtgAFbvAL8OEy6F4IwwoBJ0Gj1fm5Wf+TUrWTNTxeeQO/JSnfjGZzJnX4dZrkYR0cBeDNon09HTuufcx8gYNQ/K3gVqDs9LBL+4uIG3yLexZ9TC6jm5SCkYiCH60XoGMS6PBtQA5cxjF615h3i9eZN+WUgoSZFYfgIN1kKkFtwPCS7cQ7OxAGDQZT1Ianza0YFYL5EWEoBo4jNbyKtI6HbQCHaKILMtUAFGH3D2LXk9bTw9N7e0MiIo6reMqCgIWg4FtjY3kGQxH1UR+Hf1Vy3hBBKNKkhifn0/3nj3IZ+h86Gw2ki+/nLZFi75ZjQUBvrMc2f3ctWgR9l27LloZhGQ0os/MDDiJxyDabITMnYtQV4fjySfx19X1/5OoVGgLC/EvW6aMyDkRNhvSrFnwySdwOkEEogBffqEkQ9h7QaMFjQ68TravKafLMoDYQXtYVp5GsOkAO956mkvv+wttNWYmXHc/eGpAE8+5fnSHhoayefM+irY9S35mN/5eDVEDCsBoAFxo/N08/9fricubdSi3WUQSdaTnjD2n5w3w/SE9IxuAKVPzUAk9SpYzAmBg9uxEgoMe5Ec/eIjCSCP/c8VoBgwbS48o0bF5AxZjsNLcMmQchEZCbRmqcZfxwF+WohId1B/4iND0mzHkZ9PS+imhmXeCkIOSWCMRGioSGjrj0JZYARlj0NXETbuelvZ1PPXScn71yykgmAER1FZ8Yi6bdq+irr6G5x77kGxVN/tqoaoO9DLESpCsgdpWO+932tHoRLILI/HLdto7usmPDUe8+npIzsLa2UbDiy/z8ZYSZkZG0drbS2NvL+OTD3eXd7ndnPEpviiSERxMj9tN0BlmTPdHLeMFEYxqtZqoyEh6tm49o/sJkkTKrFn4KiroOsUZfoAA58w3pPtZEJXZd5rwcPQZGRiHDEGtUuFYvvx76yQei2AwEDJ3Lhw4gGP+/PPWACdotYgWC3JLy4lvIEmo5sxBKiuDHTuOvk4UITEBaiuPvry5CbJylJEh61bCzXcqtVKhseTeHMyGoj0YIgYzzNTIEw99wt8uy0dtaGbK5YNBbgZJB+j7Zf9mz57N5Elq/vzLJ7g5IwLd8KtAp8IbFMuBskUMzrjukIsR6HYOcPao1Trg8LKrx+Nh106RpYvf4uGbRjJuwmxCsoKg6SC49Hi3duNHgyj6wWNXUoYKxoFKRha78djXERM1ECEij5Z2EcJngKhBEaPSCbZAAARCQ5UoPq12LH/4+6uERxiV676qBRYpr6jhD7//X/xd3bTpYHyEitnp0dibWtjZ5Ka110unDwZIMMioJwEXC55+nL09Hn546RDkSy6nHRV//O2vqd5fwTC/jFUQaJFlglFqGs8Fi05Hl92O3+0+q4zpc61lvCCCsTA7G5PdTv0ZugDGiAisycnUPPdcoNElwHnjm9D9LBmNqMPC0OfmoouKQhsTg6eoCO/y5fSWlJzc4fqe0ecsyrt341qw4PyJRYMBw9y5CCUl+KurT3ybzEykxERlKfrY7YiPh5ho+PDt4+8YHqEMEh9cqKRYxCTg9bSzcdHnaONELMFVVDSt5tYrXIRPuwsOroH0IaBJprWtDWjFZjv3dBSHw8V9P/w7mdo2plz5oDIsuWsDkhzM8BG/QSWd+9J3gADHsmrlZ2iFEv79xEjqt2ylTVSx4vMa0pKj0FvamV+7j3ENDsZEJ6PKGQFJ2bg7y6k7OI+Hn97A4IEDuP1/XkMr9BBqSzzj5q+gIAsNDbF019SRnnb4/S1JEvHxaQzJFZidYKWjK4SbZt9Cs91L3MEtpNqtzHvxbcJkLyo/bG3qpfuDpeTH+nm018eXjfWIP5/Bazu9VLXZifQp43N8fj/FjY2kR0Qgfk26y+kQY7Gwt7ERjygSZjSe0X3PtZbxvAvGs+2OFiSJ+GnT6NmxA1dz83ncwgDfVy5297MgSWgiI79yEkW7HXp7ca1bR3dbG77A+/4wkoR24EBMs2fDgQPnVSzCEbWLixef8HNLyMlBc8cdCC+9pMxdPBZRhNoaBO8xQl8QIT4B6itBpYEQG3JQMPaOTrImZfDXx/5LWVsrZZ+/z+QrrSzb9i4TJ02mtDQcQSjmvff/y/Tp07HZJp3zPoqiyN133U1s7ZvohlwB4n6wXoIgOFCp+hybAAH6l1mzL2XW7KksWvgh3ZoKwgZNI8taidVq4Mc/nkn5rm4ik82MCbEosZReF7Q0o/28hHtu/gH5428DnRGEMM72PZqenk5fDa4syxQXF7Nw4UL279/Hzj2d3D9dS12Pi82NG2juTGRlZTnzvihC2+MlSgvbPcpC98GeLsRqGOKH6C1FvNnrw+qGVhmG+kGn12PS6WgDhgrCUVurEkVidDpkWT6jekSAXo8Hq+rM5VtfLWOwz0eXy3XGy9rnXTCeVXe0IJAwdSqhCQmUv/vu+du4AN9PDmU/W0eNuijdz30ZzkEjRqBPTMSzYwfe5ctxlZQoIijgph9NXyZ0WhquRYvwFhWd3zmsajW6adNOWrsoxMSguf12RSyeqNFGFCEnW6lX9B8jNlPTITkZVi6Em+6Chhr2bV3O//zrGQxqPy//5X94c/9aLL1uVr+p5cb7JP744A+JiEwhKGIGNkM3Q4YMPeddbGlp4dlnn6Fo82rmvfACot4MmtmAOpDOEuACoGPqtCuRxMuRJC1e0cTtt91Ccmgyv/rLRIZHWqnaWUSiXo+k1aHJm0rUwEKitC3KlABBT3+d0OzauZP/+ckPKD+wl9g4CaPHzc/+aufH44LZHxnMe2+8QGObhzgBElWg0+iI9zg56IFEkxmLy4HR7WV5rY9eLzT54O6gEHqcTsaazWxrbSXIYCD8yHxsICsign319fgk6bjrToUkiuSEh7OjqYms6OgzGs8DYNRo2NnYSOxZNL6cd8F4Nt3RKp2OmMJCGubPv+gjTAJ8d/jKUZw6FXNSEvbNm2n+9NPz3v0sSBKCJJ0ww7n700/xXazu628DkoTp0kvRp6Zif/pp5La28/t8goB25kwkrxfv3r3HXy+KSNOnI6xde2KxeGibiY1F+HI1+H1HXz5yFGzbAOYgJa92XxGazxbyv2o3UnIaprixeMvKWVAGcSY3r/57JQ11EmNGtbO3s5grb7uNtlY7YeFnP8ZGlmXWrFnD0qUf89or/yUzfzwBNzHAhUZzhGDZv38/U8Zncs+Ns5EMIWzafZCVG5/lB14n8QPHgmyBpi2QmgTS0fnk58qWLZu5dMYoFr5RTGutDw3Q1Qt6czp7Pn8XV4+XQWrI14BJreemxCje3lvJ1DArWqORxZV1JGgNDAm28EFNPXNCbKRpNKzt6sKq07GkvZ3LIiOPcxFloLq3lzSz+Yy3udfrxaRSnXnTDEotY47NRoPTiVmnO6MK5fMqGM+2Ozp+0iQ8NTX0nGUiTIAAx3LcPMV330V2uc7r8rMgiujS0jAPH47abEZtNH6nM5z7Gyk8HMOUKehTUuh9/HGl7u98P2d6OtqCArx///vxcxdFEdV116GKjoY33zz5g6SnQ3QkHCtu0zMhIQFWLgKDAdpboWgryZNnk/TFJ5AQj9C+nkTPF+yqAp+hgfsnzibnHw+jclYz1l7FO2/8iVt++SLNzRKCIBAaGnrG+7hr1y7eeOMZXn31TUaNnkBALAa42BQWFlJYOAhJXExnZxoP//UnXH/VQMoPdBHfXgVBNgjNUfqvxBD68z173fU3sPLDWu66JIY9W1oYFpeC52AFT77zGRq/jwI1XGqEOicMNaqpb2kl1mAk0mTk2YoGboiKxqZW82p1PWNDwphktfJSaSnDIyPp9vmI8noJOc1Zi6dLmNGI4POxv6WFzLCwM7qvAEgqFUGSRM8ZLkuf1/a3vu5o5xk4KPqwMCKGDKFl2bJA/F+Ac0MQEDQagvPySLr3XrReL83PPUf7woX4HY7+F4uCgKBSIRoMBI8dS9jNNxNx442o2tpwL1lC5zPP4Fi5Em9trTIOJyAWT4omJ4eQe+9Fo9Vif+aZCyIW0enQX3st8oYNJxSL0pw5qFJT4dln4WQrHxERcN218MF8aDnicy8rB269Az5+G1oalWYXk1nplg6PQtCbECor2f/Oh3z4eg13hsJNURqyJ45i8bIX8Bl0fLr3IMMnXYVDNPDT/7mFffs24na7z3g3tVoNDz/8V8aMGX/GtVMBApwPlMQYIwhXI4hmZk0fx5gxDxA2fiL/efdVPGo9dOwHkunvExyDwUBy/tV4VRHcdf+9jH/gJ7iCVcSqfMyyabk1Ske61YxGEkgKMqISYEKIiZrWNsZptSTq9bxaXU+SWs80m43ytja8hy4vam8nOCTkhB3JAhCi1WI+m1pEQaDJ6SRCqz0rlzFIq0WWZXwezxnd/7w6jGfaHd3X6OI7eBDXKWK4AgT4OiSjEVNmJhFTplyQeYonm5fY+d57yE5nQByeAUclt+zbd8FqOrXTpiHW1eFbuvTo16vPWUxNhccfP3niiyTB5MlQXYlQuu/w5X1i8c0XoXQPqNQwYSp8sRS6O2HVEggOgdQckg1anmly4OrtRFBB/fJ15ARDo7MFT9MHBKXcwJNP/Ay9bx/xEXtZtcrLtGlzzmg/MzIyz+LoBAhwYTCbg7jjR3/A4/Hwr6de5AdTL6Fh80fEDRgI9vfAfGe/P2ddXR2rG0y8/uSz1Fb50Nld/F+clhlRIUhRadiNNmK3biZIpcMWE0tTYwvNNfVEaINYW1tPrCwyJyyMTq+XT7q7mWw2o1Wp0DocpFosJzwxEw/VIu5saiInJgbpDDuoM8PC2NvYiFeSzrhbGiDKYqGypYXuM3AZz5tgPJvu6NCcHCLS0qh65pnAGJ0AZ84RCS3hU6eiliS6VqzAuXfveZmneGyXc2Be4rlxIZJbToYqJwft2LH4//GPo1c2jnQWn3nm1GLx0kshLRnhP88otYuSpCxD94nFfYf2J3sg+L1wYC84eiE9ByLClOfSaJF0RnSp2VBZQlDhRHpSq/jTb/6P+ORCbr/jYcbkD+DnP/sNoWF6IrpD+ceTjzB+wlRWrlzBvffej1qtxuVyIcsyBw7sQ6vVkZ4eEIkBvh0IgoAkqQCBO+75Jbt3bcNZ+h9uSEuEiKv7/fncbjdvvPFf3n37C8ZkxfK7yeEsWbmL8SMHo84aAYOnYPJ58IWH4fOqkdIGYF+8kJI9Faj9DnZ64caIaAA+q60lxmAg1WpFlmVkTu6HCkC310vnWawS9HG23dJweC5jg8OBSas9reXm8yYYz7Q7WmUwkDB9Os0LFuA5ncSEAAGO4DhHccUKWouLldrZfhZuJ+tyDsxLPHsuSHLLSVDl5GC84w588+Yh19YesVGn6SxGRCjOYloyPPW4slydlqk0uCQkHHYWAaw2mDpTqWN09CqXJacrWbnmIHB2QW4hqEWwRUJvN18+sZ5rRFhbu4Ekp5c7B2dwoGEVpeXNLH+/lKRsL0ve345bVUpZ2RCqq90888yj+L1uZszM5+677z2/BzBAgPOAJEmkpw+gdN9+Jtx0GU5nHdrO3Qi2Uf36PD6fD59jPy/8Lp05t/yX3SufYEhdG0G3/h/o1MjdzZSpXCw+uIfhsYOwWiz8ctVqgrwC6cFG4nvd2PQG9tvtlHs83BQejiQINNnt7FOpyDuFoNOpVGecKd3HuXZLn00t43kTjGfaHW3NzEQnitRfQFchwLecC+UoHpHlbJkyJdDl3J8cM1/xfCa3nAhVTg6GPrF45GfP6TiLkgSZmUrNYu1BhJf/A1YrTJoKSUlKN/TKRdB8qLzGYIDb50LVfthTpFwWGgE5ebBqARSOhQ9egKRMpbo8JgkkgakTpuLetQaDLZkfhIcRFBzBR48+h8/u46ZRIfxnrZtEyxJ69Bpee+x+Kmud3DX3VxgMJsaOn40kGc73YQwQ4Lwxa/YVOJ1OaFsLnUvBmgWCtd8eX6PR8JsHHyIpoYtli17msSdW8/Nxg5BDIyk6WMp7H/yX9h2fMTwtlxLvbt64bz4HG+1cJ0BJawdXRcfR6XSyoK6O6VFRhOiVJKbq9naydDos+pMnM51NpvSRnEu3NCi1jG29vdR3d188wXim3dGiWk3i5Mm0rlx5xlnTAb6fXChH8bjaxMbGQJdzP/FVF3Rq6oWZr3gMRzmLx4jFUzqLoqh0Qo8aBUkJ8MUqhKAgmHO54jYWF8FTb4HLqcxhFEVISYc510DpTli+UFmyFkTIGwqNNZCWAw1V4HFDwRjQ68HVC801EGRFM+M2sn0OME9DNlt4eK4BITodSS3wL48dMakAvykSvE2gj0Rr6BvjEWhqCfDtR6fTIUdNAu8g8OwG9UgQ+ke+lJaWgjqKutYYijbfz/0j25h2wzQ83jbefvmH1NVGMzU5m/kfF9Hu8NHrhDRAkiHBYCbeZGJ1UxNxajUpR4hDETidLJVujwezWn1W234u3dJ9qCSJBJUKl8+HVjpRtOIRtz2rZ/g6BAG5qwvnyTJYj8GWlYVaFOnZuzfwBRzgpPTNMzSfZ0cxUJt4/tHk5BB07bXIlZUXZr7iMZyxsyiKoNXCiBFKRnRqCuwuQmish0tmg98DtdWwYQ1s+AIQQKuHIcMhLgESk2Hbevh8qSIWRQly82HISLB3KQLxw5fgB/dBxkDoaoOqEohKgKZqSM0D/PidXaA3oh19gzJqpGc3kmwESxhoZJAyD0WlBfKfA3y3EAQB1GHg67+G2D179nD/fXdz79xkdu9JZUeVl1aDl44FT1BSFU5+Qg9mvGid+5iYrEcIzmPT6nUkyaDzQ6ooIssybW1tDIqOPuPGFYDEkBB67XY6nE4sZ7gsLQgCdXY753KaHR0URGlTkyJaL4ZgjLDZyCwo4OCuXV97277axbbly89LY0KA7wB9ySzDh6MxmdCYzefFUQzUJp5/BIMBbU4OQTfccMG7oPs4I2fRaFRcw5xsKCyEqnJwOqC4CGHqDKVZpb0FyvZCqBXqDHDFdUo9YlQs+DxQtBmefltxHPUGZRm6YASkpIFWA4n5sG8H3PWgch97O60NS+GgA1tmPoy7AmQn9NRTr/fgL1qEy5BN/IAQ1BEFCJWrQJMBKjegIyAWA3ynkXL75WG8Xi/z579DQ9Um/vvoBsKCrRRkZPPp0npKvqwhztxGiC2Pd77YSp0A2bOv5IN33yPIDyl6Hc5eF9mRkbQ6HFSpVIw+xiUUgKDTcA5NWi1F9fWknGFMXx8hej3hgnBaDuGJEAWBeIuFAx0d6Gy2U9ZCnhfBmJWQgLOkBMdpNK9Yc3ICtYsBTsiJkll6162jo6mp304uBFFE0OkCtYnnm0O1isYpU5BUKpwvvIDvREkq55nTdhY9HigogMmTlESW7i6E999SnMGrrlaEZFQkeOw4NryHzhKLEBQLV1xPj7OCLc++ybj8AoS2Zlj/OeQVKuN0BgwGj1MRj011MO1KaKuHjAFK53TvPrrNvfzqiX/w4wHjsQ4dh6A3Qus2ZJuJCH0ULy6CsUkNoEsCoQ2Sb7rgxzFAgG8rsizjcrn48MMPePmFx9Hjobxag+j0s7t4HU67TLQKfn5JHs9XOhicNYTFa7ZT/J/XcDthokpios1KSW89KkGgur2dnBPUKiZardjtdjocjlPWMYqCQG5YGM12O0F6/Rk3r8QGB7Px4EFsgkD4WYzXAaX5Jc5goLanh5hTJM/0u2A06HQMnTCBnvXrv/a2puhosm+4gbqXXgrULgb4Ck1YGBqbDeuIEectmUUyGlGHhaHPzSVo2DD8VVWB2sTzxLG1iq7duy+4YysYDIpYvP76r3cWExLgRz9UBNzqFQh7iiE5BQpHKNdtXgtX3wQuB3TWoRs6DF/PYppry/DExfHL3zzKLeoGDmxdRWpCMkJWLmhEEGU4WKoM7p44C3ILQPBS31mMrryckBlX075lN3/64CNGT5yLKjeEDfN+Q9ZP/oPFNpCS2gYE8SCXzBhAXGw2ghQP9F7Q4xggwDeR5ubm00o+am5u5osv1vDee88REhLMdVeYiWjxcudPn6B60yqKt1fxnw824/dD6Za95NrMrO8SUDt9ZACSWsXVsTEc6O7BrtUjCAIdwImCOk06HRurq0mNjDzlNgmCgAdosduJttnOeN9lWabL4+HM73kYrSRxwOHAd6GXpMOtVkLtdupLS099Q0EgfupUOtesUWoXA3y/EUUknY6wyZOx5Ofjq6vD29DQv1nPgoCoVmPIySF4yhREux16e7G/9Rae/fsDIrG/kSQ0mZkXtVYRFLFouPdeVCoVvhdfRD7y80YQkC69VBGL8+bBtKkwZgx8+I4yfNtshut+AMnJsHMLLN0Bk6ZDcDA+t4P64u2ExMvsLT7AkiI3+1/4jOJtxcwLgfstFlITEqFsNwwshOhEiIiG6BilwSU0DF/lNspXPU/mLf+HU6Vm3s4OJl9yJ8Xbl/LDBzeQF2HmYZ+L0uJt/PbJp3nt9ZcwGMYj9E14E86u0D1AgO8KbrebZcueYPTo0bz22m4uuWQWiYkpyEd8nu/btxev18MTT/yeLRs/JzsWUsLDWfxZI3+64xL22Gv5y2ufUKh1YNJAYw9sK2tja3Ebbh9MNBqIFfykilqSjXr2VB4kLzyGLqeTIoeDq07S4WzRaAg61MV8Kt8wWKcjJySE+s5OYiyWMz4GIRrNWSXGHElccDDdDgfdp5gL2a+CURAEcgcNQufzfa0TZElNxZaUROVjj/XnJgT4liEZjWjDwjDn5GAbNgzvwYO0v/yyIhJP4310Js+jz8wkZOpUJL8fx4oVuIqLla7cwJD4/kUQUKenYxg1Ck1iIu758y9KrSKCgJSejv7aaxF378b7ySfHOZtCejqqggJ48UW4807QSLD4Y4SoaBg5WnEUa6qgpAgSkuDSq5W6Q48LUWfAZNEy74OVXHft5Yzqfp3oslo6NTBZp2dEpAmhdBukDVIcxsZKWLsQEtJh6lXQWI04YBghOi+Gto+RdIPJzgmmrHQLPxhr4pbpP8OSO53F//oVacOv5w+/+Q2trXvo7ZWIiEi4sMcyQIBvKGq1mmuu+T1LP11MqO5z5DYnj7y4kZIaL+3tvYSGitRVFpOSHM+eomImFSQzboiOF57ZSYpeQ9e+ev7z0gosvS7eagKzDCPUSlNLhAzpZgNXh1lYWtXAqFTl705/6Mfp8ZDm9WI9wZKzJIrkRkZS0dZGgk53yhhOnVpNs99PiCTh9HrRnYH4O9d5jH2YNBraentxnmK1t18FoySKpIeE0LZ9+ymTWgRJImbkSLq3bsXncvXnJgT4FnBst7PQ24vc20vH22/jPHCg30Tiybqd3Xv3IgcarPofSUIVGYlh6lS0SUl4N23CuXgx/otRC6pWo505E21BAfKGDUrc37HvK4MB1bXXImzeDNOnQ24OQk0lXHk1ZGQil27DfbActVqDOHYihEeBtx3f7vfp2rEN1ZCrQG3l5jvuQ5Qr2FQt8XFxJ5OMcHlCEJJBA04fVJRAWJRSw3jlXco8xo5miEuh/PMlfLHyTVJ//wCL33uVBUvX8fg//05wsIBABiV7KyhvqmVmZjLq8Bxc7nJ0uvALfzwDBPiGIggCarWOmbMup7ctn00rX2Ns0FZ2VPWi6e1FJai4pTCY5ZXVmDq9XJ/j5a+LdlPcBJlB8PGnu2npcOMUIBqIlCDKYGCQSkatgtuSYqjpthOCgEo8WowJQJLJdMLVqb7B2LFGI3WdncR+jXMYHRzM5oMHiRAEdCbTGR2DHo+HDrf7rOcxglJLGaLXs6CxkWtPcpv+dRhFEaPRiNfhOOXtjBERWJOTqXnuuQs6dy3AxeWr2sRhw47qdnYUFyP7/f0WBymIIrq0NIJGjgx0O18IjnQUExLwbd+Off58JfHkAi/zCwYDUlYWumnTkLxevH//u9LtfOx22Gyo585F2rULqqrgjjsQgkzQfhBCLDT3drB+0z9Z++I6Hhgxlsh7/hcMOtx166nsXs7Oz3dw+cAJlHncNG9YRGtQAnv2ljDQKBOvQxmTM/U6Za5iTBqYQ5TntXeDrAXZC8gk5gzjtgEDqfWV8/67T/KXn/6E/bt3MnjEjUiSBlAz5pqJSJqDyASj02UQ6IIOEOB4BEGgrtVDWJofj/6HTC97mbaeSLYWlfH+vjacbpBd8Nfnyul2QaIKEnxuxG4YoFGRq4VuuxeNLPDjtDj21tYzDgiSJEpq68mOiEISRXxHaJZwsxnJ52NvYyO50dHHbZNOraZZlrGKIk6PB92puqYFgXCDAS1KXeKpHMkTYVarz33qqigyND7+pFf3q2DMTkoiPi6O/Z9+esrbhaSm0rNjB67m5v58+gDfNA4lpIgazXG1if3d7QyHHcXgadMwJCXh3rTp5N3OgvC1M6dOiCwHlrD7+CY5ioKg1CrOnYtKpcK/bBnevXtPnNBiMChi8cAB2LQJfnIv1B3EU9tAT/GXCEECTq8T+dP1XF2QR+SIkRCfCDWboBZc6gx0tq24LWbMRiML1qxm2fK38fvA5IS0pFBCr/0xJGSBRoPHEsYrT/yCSRHJJMfEgicUIuIhKATJbEbe8CoL1nxEeYWEyphFSnQ2Ph9IkkBWVha9vb9GFPYhiDEExGKAACcnPT0dr/e3rG6eR/qV96F2qMhP+xRH+R5W7qhBY5LwunwIEmTpwCYAPuXroNIJVhHSrTZ0kvJ31mcoWpAJFYXjBJkgCFT39p5yDmJ0cDD76+uxqdXIpxB1kiBgNZnY3tBAgcGAdAaCMVivZ0BICHVdXcQEn8648JM8jlZL6CkGgPerYLSZTHRu2YL3FCJAZTAQMXw4PcuX9+dTB/gmIYroY2LQhIdjGzIEjdWKv6npvNQmwonnJ3bOn498jMMlhYUhGAwIoogmJwd1ZCTy17jhRyHLuDZvxtPejnDE+AJfc/P3a4n7G+QoAojh4aiHDUNbWAjbtuFdvPjkTvKRzuKiRTBjOtRUIeztZM/OTYTFqtl3YDXDR6cz4/apdJsm0ubqwlq9FXx2NPEhhJSYyJiajDoijPL6Wt5fvRJtJ6To4abCWMbOvg5xyEQwGEGsRZY7iTJ1kVA4AE94KJQXozZkQN1+sEUhuESyTFk888vbWb15DxUH55M2eDzp6dPIycnBaAwChl7QYxogwLcVlUrNhIl34PE4WfXhr5h67/Xs/vgNEqyh5Kals+CjJYQ5nKhkcLv8yF5wOr1kipAeGYJNqwzPFoBgjQYAnaRCpzqxwWA1GDCdwkEURZE4q/W0ahkRBDKDg884W1qnUrHfbqfT7z8nwQjgOYWR12+CURAEEnJyoL39lLf7KjM60Bn93eJQl3NIQQGGlBSMUVH4Ozro3bIFe0sL7pqafhWJXzs/8ZCDqIqIQJOejhgUhC4vD39FBcgygiThWbdOETmniSzLSN3d6MaO/Uow+hsb6W1qQj16NEgSvoYG3AcOKM//XRrPIwgIOh26YcNQJyWhTU6+6I4iOh3aqVPRDB6McPAg/jfeQD5Vt/uRzuKCBYrDHBuLsHEdDMojt3AMq+Y/x46aLloadhGZ2kWpoZYJuSI9uhqoP0jM9J/SaoogbPAf+KK0jeefe57OWh9zC6xcO3ksIVkjIXUkGEWc0k52rP6IKn8keddcwZLiPbRvryXSHIV7yV+YecNtoElEmv4jpl6jwrfjfbp31ZCTHUSnuBvBEw1kAWfhhAcI8D1GklRIkompV/+BDz74B0tXVfLDG++lRhRwuN8lP8aGKjIVtzWOxuJi3FVV1Hc4sAaZ6e7oxmI2kxRmw9dtp9vpZGBMBJ2dPQTpj89ljwsJYX9dHWEazQkdxL5axkSzGcHjgUMi9ERY9Hq67HYElwtZqz2jZWn/Bfiu6TfBGBcezqCEBGpXrTrpbQKZ0d89jutyrqrCVV5O03vvIXs8Sl1iP7+R1eHhmIYNO+n8RCk8XBE20dGoo6PxFRUhd3XhmjcPf+OhWKmzXVoWBDyffHL4/34/hrQ0RLUafD5c1dWYJk5ENBjoXbMGwWxWojLtdnzfwhIMwWBQRHdODvphw5ArKvBXVmJ/992L7ygOGwZVVfhffBF/Q8OpX88jncUFC5Ta6ehoiIqAlhbYshExIpRx+mDGTb8U2WLEHxNN87Yt7D9oQdPVztAJM6lo3IIqwsJ/V3xB8bL3yQyJZe6PhjNk5p2oc5I5uO0ZhNJeBGMcO3Y+Q3lNA8NmjeCTz2pZ/sYH9HRrmJyZwO3pQRAajaunCQ58inbMdTRHDULetZbcKx7BJ1lQaSwExGKAAGdPWXk7NbVt5I28kuwJV3Pn3YMx+mFGxiCCZt6JP0zFqocexNbrZHikjRCDjhCtGtHlwKzVUtvVTaxKpMkLTb29xJ1gzKMoCMRZrVS2tpIYFYVwgnhAnVqNrFLhs9tp7unBbDSeVAzGhISwr74eryQRfgbNL6ebLHMu9JtgDI2KQtXVhf8UTQWBzOjvAKKoJLCEh2NOT8cydOh563I+kqMcxcGDkQ8ePHp+4iEHzDh1KrqhQ6G6Gs/69ThbW/E3N/df7aEswzEnO759+/Dt2weAZDAgHjiAv6cHfXg46iFDwO/HvnQpUlYWUlgY7uJifJ2dhx/vm1QTeaQzm5mJrqAAweGA7m7cb76J72LNq1SrUeXkICUmosnPVxzFN988taPYhyShmjMHqazssFgEyMiA3UXQWA9eL8LAQajDwiAhHsZPBUnkiht0yDotDq/May88SWHYUpLHJRHkbyZvYBDX3vU7ynYvQdLUQ8seYjOG8fy/lzA1JYa8kT9ktCUSXcwwlnx6OX/+YRLm0EtJi4mkaeUypQLfGAqxMiVlLXR0qBg55zoElQuVxsapJ7cFCBDg60hPTyct7WmKizfx2z/8jV1FnQzWGzDPvg1vehLbX/85O/ZXcVtKIhZHN6LXCWo9SCokn0D8nKuguIjgmgayIq3UdbQTZbFg1emOEmeiSkViUBBin4N4AjEoCAI9gkB3by9mg+GEt+mjx+sl7AznKiaEhNBtt9PlchF0ljGDX0e/CEaVJDE+P5/uPXtO6hwGMqO/xRyqSRQkCXNODsbISPRxcTiKinAsX46jpKRfu5yP5ThH8aWX8B7hKPU5ioZx4w47im43mvx85QH8frybNuFtbwdBUATkeUK22/FVVSn/6ejAXVGh7ENwMKqICPzNzfgB09VXgyAcXRMpy3irqy+8IBMEVHFxStNQTs5Rzqxv+XK8e/ZcnHmVgoAUF4cQFqZ0Pft8+LdtQz4dR/HIh8nMREpMVCL/jjyZkWXo7FL+bTKB0w4JiZCSAbW7wNVDe2w2axYt5u8PPUpGVi43P/knfv3w4xRv3Ma/HrmKFZvXkZxYgJgdjOAX6LCPQFB/RvxVD6IxSSA34vUHk5p9E9u7X+O2sbm4DT2UfVZCz+bFJF/+U/wtcbjq32HY+N8giieP5QoQIMCZ4/X6+OCDD2ltKuKhOy/FYE1AMyiTzTs/ZOgteejCYnnnL+9za0gQuTo/RETCqKkQFAK2cHyFA2l4+kVUB8qxqAx4fD6yYyLZU91AfmwCkigqtYsqFX67HbfbfVLRGKTXEyRJuJxOfDrdCd1I8dBcxcr2duIjIk57rqJJo2FTbS0JYWHfbMGIICB3deFsaTnpTb6qXQxkRn/zOaK7+ciaRPfBg4hqNb3r19PzySenLI495034OkcRjkoSkUwmVMnJSCV7kBcvhsRE/Pv3K6LC70fV3Y161Cg8a9fiS03FV15+/kc6HSFo5NZWPGvWAMp4IXnDhuNqIr1FRdjtdrQ5OciyfLQT+dUDnYUjKUnHfXj11Xbi96NKSkITHY2/ulqp7fziC5xtbedVWJ8UlQrUajSFhUhJSaijo6GjA/lUXc+nQMjJQXPHHQgvvQR95QgAogiJCVBbqfy/uQlWN8E99yKrJZwuH29uLubzv/+TL9Zu5mfDE/jBH39Pa28dO3YXE+z3Uf7FctLGTSYjKx+040GQ2Lt9L2mDR6A2hAE2EJMRZD9paZmkpT6OYPDyxttvEplqoXPnbpK9TvzhIxlkCUKQLP10EAMECNCHWq1m7txf4vW2su6zNxk59QpQCWRl38Af/vYQX65aS3aPB1MkkJgKA0aA3wNJ6RCXzNLX/0JyaixSazM76zvIU2vp9nqOX/4VBES9Ho3DcVLRKAoCaDRoZRmX04lfrz/uNn01jzFG4xknv5zvOsZ+EYwRNhuZBQUc3LXrhNcHahe/JZyou7mx8aiaRGT5vDmJfXydowgg2myY58xBk5iIv7gY9fXXIzY3wuJ3UKckILs9+GJj8W7ZojS5OBzw6aeog4NRt7Xh6O1FjolBbm+/4I7eUULsyJpIWcZcWAiiiL+p6bAT2XcWepIu7VMh6vXoCgsRjqglFSQJdXIyvqIi/B0dCFVVON59V+ksvhhL5Ec4idrCQkSLBaGhAbmyEt/8+cp2ncXnxlFi8dgT1fh4iImGD99W/m/vhTHjwe1mz6aPefof/6G8y47FJ/PfybkMv/EOVPpWvty2jYqSeq7Nj2bqDX9FnZALnnaaezoRhBCsViuCbRqCZEX56FchSTBixERAprR0G0MGT2LL58EM0i1E9LaxastmJk65lvNbfRQgwPcXm82GLFuZffUvUas0dPc4WLzsGfKSZOK6VXSthNBIG9z6c9DroK0aNICjjdzYID5urqSoqpY/h4dR2d5GRmQUaq+PurY24o7MrxZFRIMBjd2O2+NR6qSPNdIEAVGrRScIOB2OEzqN55L8cj7pl63IiI3FWVKCo7X1hNdbMzICtYvfQL5KXMnMRB0cfEG6m0+6LafjKMJX8/ZC5s6F8nK8q1ahv+IKWLIE2ttAq4JoE7LTxea336PALkJQMIwZgyBJyPX1+EtL0SUkIJvNOBobQau9eAPkj6mJ9Hz55Vf/7nMiD9/0+C7tr0PU6/HX1+Pbs+er4ygDzqVLL46D2Df/UhBQ5eQgWixHO4kbNyK3tOA/x6X5U4rFvu2oqUbwHlFzLYrQ2oJmeznXiyIH1TLjByQSM+cHMO4a3L3NLPvkYTQCVDc5aNu8mLKmtbz2Zg1jhwdTOO4GMjOmnXCZ6dCTkp5ewMcfH+RAzQKuumkOWJKYNjUVVOdnCSlAgAAKSiKM0uVsMBiorPTz8tMLiFI7uTfJiq5gBMQkgqMIb2I4RWv+y6BxP2Jlq4MXXlvFfSYdJXY7g4ONiB4PolZLrODH7/UiHino+pzGtDT8LS34UlORKyqU75i+zzRBQPgap/Fckl/OF/0iGINsNjwdHSe9Pjgxke4tWwK1i98AJKMRbWgool5/VOKKfcsW5MrK89rdfDIEScIybRpBw4ef1FFUNl7COG0a+uHD8W/fjmvJEox33on/jTfw7tmDZsIEvC09ULOK7dFhvFFaSUFmFpoh+YoY8PvxV1XhnzJFWQJetQp9XBx+mw3X7t2nLEK+GJxQ0B3bpX06XOzxPoccRCQJVU4OqqgoBK0WyWjEv3UrVFWdk5N43NN9nVg8GcHBoNWSHB+PdoeBkWYDotEMbW1Q/yX725p49aNS4iSYnRSMVRvD2nkvMDgonPET/opO3AnCZL5uuPaMGTMZODCHbmc5wQY9CLZz2+EAAQKcERs3bqS+Ygcb1rzC4r89xSU3/AhVWBDYD1C3bQtrm0qpr/gCU3Q4xUVf8u9JoWSHDKR29RZ2trcTq3ZgNYbg0qmoqW8kOfKYDGdRRKysRAwJQdXejichAX97O5jNyC0tSmnN1zmNZ5H8opUktCc9YT13zlkwhlksjBg9ms758094vT4sjPAhQ2icN+9cnyrAmXKoFhFBOLqruacH2eXC29R0XhJXTnvz+pJZ/p+98w5vqz7f933O0ZaHZMuS996OHdtJHGfvRSYzrBbKaIHQ0j1o+21/3QsotIRSZtlhQ0KADMjey85yPOK995JkrfP74ziTbAhJQPd15QLJks6QLL/n+bzv88yciTE6mr5TKYpHkCQC5s3DkJ2N88i0rk6HaDIhD/oAequr0WTmIIwswPrMH7kuWI1q+DAINcGmrRBqRdTrESsroa8PwWZDZbfj1Wjwut3ImZl4T74SvNw4xZT2ZcFJSyaizYYqNVWxORpUEOW6OgRRxDfof+lpaTnvnsSzcc7FYmgoBB03YBJmheyh8N4rSA01xI4cA4f2gWhENlnYtfcN/v74+0SLLh6Il7hp/nykcd9gjjUK2RTKQM8mgrMewO2Wz2SzdpS1q/7LnDmzQfisr5sfP34uLqmpqUyceR2ljV7W1w9wTVIa9kADb77yKNpDq8kfs4A58yfS2hnLvT9KZ9+Sv/BC8WH2N3UxQRbICrVgAhyiRHRIEGJoCIjSicvPXq9yWxRRCwI+qxUhNlYJrxg7FvngQeTubgRRPKXSeL7JL6IgkGOx0ORwEKjTnfOwzPnwuQtGg06Ht6wM+2nUENuIEXhrahg4vuHcz8XlpF5EX18fxpSUE6eaZRl8vi9luflkTpX13PXqq8gDA6d+giQRuHAh+uRk+h96CAbTWTQjRyI0NR1dvpRDQyE1nYOyh+i8fGySgJgejxCVgGwOQg4Opmf1WoJHFIJej6jWgCUMseYwPYdKCOjowB0Xh3fwStDX1vb1SnA5R8SwsBOWxIXQULQjRyL39iq3RRFVQgJyURFyd/eJCqLPd9GW/8+5WFSpoLAQtm0+VnyHhEJbE1QdhoIxoBWhqgxGTsC9fi2bPl1NrtvDDXF6pkYHIWYU4g0MoMLXiblxH6HmcHYXH6apeTNXXXX9GfeztPQQIbYULOHj+ILDtvz48XMOWCwWZs6ay/+e/ic3f/8XaMNsfPLJiwSGxDLnz+8iSB1s397Pmg+fwRxpZ/ErZSQEJ2CWBYZYTEgaNagk6HVCoBEiQmFPMQSGKC03/f3HNubzQXs7YlcXVFQow4adnXhkGeGqq/CsX48YEoKuoeGzSuN5JL8IgoBHEKjq7sZmNl+U8/a5v61Cw8MJMps5VfeiSqcjqqCA1tdfv+iDEl9bBn0RBUE4bS+ip70d++rVF3Wq+Vw4r6zn49Dm5KBPScH++ONHi8UjyFVVJwxzNNTV8vaDD7FA3UXU6KE0tJZR1lTJKCN8uL+LMQd3IgxJh/xcsITh1WpoWv0GpaUljLaGo01Lw6tWI8TG4mxpQRo7Fs/Bg/hOnla+HFW+kznFdPQ5MTilfCoEkwlNbi4cUWIBIShI6T3s6Dj6OM9HH8GX+Hk7r2VoQQB7H0Ln4P6KEhQUQkMt+LzHMsa9Pti2AXVqBnfm54LPib6/HSEiGravpKvyTcQhhRTt3cfUrEJo7SEiwkppaQmpqemn3bxGo2H27Hkg+ItFP34uFWq1mkmTppCamgLqYKbMehCA3t5WfvKD71G84xC3jg7iiXc6sOjjSY6Lo7+8GruzH69Wi0cXTF1/G9GSGuFwOXKgAdKToLQCUlOhouLYwOIRBhOqMJtR5edDmAVBp8O3dy9ekwltSQnOQcsdURTPO/lFlmUu5trY5/rGOpv/Ynh+Pu6aGvrKyz/PZvycguMTVgxWK5JOhyYo6JL2Ip4JVWgoofPmnTHr+VSIoaEEzJ3LwLJlJxQkgKKQnlSUhLd1cKPTRwQm1q7YxyZPEbFaiaBhaaQadVgsGoiNguwcPIKPR//0R5Y8/wb3ihIZ/b1Y1q7FJ0lQUYF+8EoQQHXDDUcLCbmuDufWrYhW6xd3gr5odDo0hYUIF7C8LsXEHJ1S/gy9vcjPPIN88orBJSygz7tn0WZTLHVWDqoAkqQsT29bC76TzlXeKISIcAwJSbB7HVR2g6iHlDGYI9XsaTrE7x7dR1ZBEK+/9jpR8TB12iyam01IkgqL5bPREKmpaZ//oP348fO5kCSJ1KzPZrQvXvwMWz7ZisfhpbZe5Nasfg7ta2ff6hoWaAXSw6309fQhObUkRdoQ+52g1SntX2UlyIlJUFMD06cq3zXHExwMBj3UVMPc2SC7EAK99O3ehKalC8nlwuFy0epwEBOq9DZfaPLLxeDzXeKeyX9REAhKTsbxZfjdfdU5yRdREEVMI0ceTVjp37oVn9OJp7X18hosEgREtRpDVhbmefOQDx+m9z//OauiePTpOp0yDV1ejqeo6DM/9+zfD6CkcwymuYj9DhKNJtxqmTFqLQnuPvqddqJ9WoKS41lefZiJDYfQ9nXx+LPP8MIT/2ESPsK8Xoo6OphitqAZGMBttUJrK3J7O+qwMITjp5VbWtCPGoUUGfnFnKeLgV4PjY3Ix01Hnyuyz/e5p5S/LC5owCUjA/YVQ1sLCCLMvAp0Wmg9qQiWJIiKgT0bIStPUQci4mHa9cjOHlpff5uNtRUsHBrG1s1/xKaOY+rk2zm4dxeNrRWkpmZQVFTO7Nlz8LhdaHU6UlMzvvBz4MePn8+H1+vF5XJRUnKQjz5cxrVzRtPd5CTIUU1GmsSaXV6iRcgI0NPvtBMYYMQkAmo1mNSQkgQHDoJPRjhcrnz/VpYq/45HEKC/D+6+F/DBuo8o7j5ARFAvu2q7Ge1xo/L5iNVq8Rw3fX0hyS8Xg8+1B2fyXzQlJxOamEjV229/nk18rdGEhaEyGFBbLCf6IlZWfikJK58HyWhEn56Oefp0JJ8Px7JlDBQXn9fFg3HGDISGBhyvv37K5/m6u1HdcAPCli3HlEZRBK0e9YADdfZQUmtKkXUaRHMkHfuqyBo6ksrYZN65aTJPFbWwQC0yRyPi6htUyAQBMSAArcWCp6gIj0ajFI4nKZnC+vVcLpdBgtUKhlMMT5ykLssXYcjkUnJBxaLRCPl5sGaFcjs5FfKHwb/+ongxgmLD5OiBxFQl9aG7AwKDlS/77AKwRuHa/Ql6TLQfaCUjOoTl/6rk3m+NJDT0IFu372ZIVCSfflrE5k1riAkppdPu4e67f3hxToQfP34uGK/Xy4svPsVrLz+Pq20fPU4vLsM3eX/VM6gcXiLXQwSQogWv10OgVoU5LEz5bg2LVuzcyg8qUZ+JacqqlChBdeWJG7LaYNgIKBgJnW1Qcxiyh5MeMo3/Nj3E1oqtmNwSJo2R+p4e9FotceHhR5NfiltayIqKQrqIU9Bn43MVjKfzXxQkicjCQnp37sR7ukEGPydyXEZzQFISapOJ4Lw83BUVqAIDv3RfxAtFGDQuDb/vPtQqFY6VK3GVlJz38IgmIwPD+PE4HnnkzEWmIHz254IAGh0cOoyQmY4YbITxEwn1OgiVPPz1tZcJb2hivlZmtlaF6AUfx5mh+HxQXo5Kp0N2OvGlpyOfrJRfyh5GSUIID0dMS4OgIKTcXIT6eggKUgoijxvcHvB6oL1D+WJrbsbd1HTCdN4lt9u5UHQ6pJkzUY0bd/7WORkZoFEhHNyvKIijx8CuLUosIChf6tm5sHMj3HQnvP0c5BRAYCCEx0BOIbTUoQ0yo5KSuSctleCc4eTYu4g1qHn4wecoK+7k1l8sYu/OTfzogd+TGGUgffh8RDH4opwOP378fD7i4uK45/Y42g9U4OpO473X/8d9CTqStFoCJS31VY30eSAnwkJIqAWSM6CyHIbmwtqVYA6F1AwoPwSRVmhqhows5fsElKXonFxoqIZ3X4XAILjlDggOwvHpcwRse4eFQVrShhZSd6iGiM5ujBoNDd3dRJtM9Hk8BFzpCuPp/BcFUURvNNK9aZN/OfosHN+LeHxGs6+7m87nnlNG8I+kq1zmf9yPT2jx7NlD1/LlymTseb+QmsAFC3AtWYKvoeH0j5NlPNu2oert/WwzsChCajp4XRCfpChEEWng7ucHk4bTGaun9YPtiPYBZB80+CBWqz/xdSQJdVwcdHQw0NeHHB2tXD1eqvdBEBBSU5HGjEGKi0PYswd6e2HjRhg7BtLSwNuLvPRl6io6CK+pRd3QClo9wCmn82RJ+mwSweVMaCjq++5DamyERx6B+vpzf65aDTOmw6qPlc9lRhbExcEny5TeRUmC8VOVXOkFN8GKtyArHybNhppSyC2Eyv0QnwadDUgxKWQ8eLUSIxZiQzYE8qPx34TQGGR7G7+ePI8Acw4C0sU7H378+PlcSJLExIkzcQ8U0jH0vzzx0lLuuf1W5l41Due2TbSpAqj+86PkGVSEWENg1FQIi4SRY8FsUZSG6Hj45CNISFRWKAacUFhwbOhFEGDDSujugoLRMGUWaNSKo4k2EHWAmcypQ+i6+Xss+e73yO3oIMRgIC4oCKfbjTUgANHr5VBrK5kn90WeRKBazcVyFL7ggvFM/ouhGRnoTCZaLvFU7mXH2XoRv4SM5ouBIEnoMzKw3HADcnX1qRNazgPtkCGIPh8DZ1OOvF48nZ2oJ0yAZcs++/OKCpg1E3p6YcRopRAYsKOJiGL/288juteTlxjLtrZ+4txdxJ7KQG+wmNJERSGbTLguRTLMoKIozZiBlJCAsHUrLF+uZCPbbHD/Iti9DSEpAVml4tWWBjqXLuW2sEjUGdmwt0QpGg8eBEBltcLmzQihoadPIrjcEEXE3FykOXOQysthyZLzew9EEWbPBq8bobREufK/biG89xq0NUNkNEyeCROnKhcX3a0weS4EBeBrLEdorVesLlJz4NAOMAZB2jAIMoHopblmKzhkQoZOA8nLc8ueZsyoaWSZcy87Q3g/fvyciCAIIBp58cMWkiKTmP3Nn+ASXRzYtYS1Rd3oZC+pZiNSeBRERCtPio6DpExISsK34nnorUMcPhLWrYLcfJAA73GCSXIKGIwwJE+x5HH0Q1AQ6iFjmX1/EK7oLN57/L8MDHg4LMsMCQ6mva+PULUa1Grq7faztkEF6/Vkm8009PQQFfzFr2hccMF4Ov9FlcFA3MyZdKxceXkNYFxKTpfRfAX0Ip6J4/0UDQkJ9L/xBq5Dhz5XFrFgMGCcNg3XqlXntIwtBAQgGo2n/kWSJMXeICMdOpvpP7QVQ2Yeh2oO0bjrPSZPGMUBWcPBQ2soENX0OJ2n3053N0J3NxqdDl96Op4vIxnmVIria6/BkelySYKpU6GhFiHMitzfRc/212kp3sGNQ5MICI1CqKtCNkhgdyjL9KIIg0NHYmfnZ5IILkfFUbBakaZNQ5WUBB98AHv2nH/BnpoKI4bBw39Rzt/cBVBfBQ11kFcAVy9UehWDgkGrgfZeCAuh29FG2+bn6OoXibOkYRF9kFkAEXEgdyP3FYMllbCcEWzdtoeiF//AtLt/wG23/xKVyuwvFv34uUJQq9V873t/RBKhs6uap/77Z/AGsH3dCob6AIMGjFoY6AVTGETEAB7kABf1MTa0I7KwjimkeuMyopuqkGrKoKEeLFbQ6mDYaEhKhehYkL2gHqBx2+ts7TFS/fTv2Ot0EdjnorRNZh6KEXdMSAhFTU0MiYkhRK9HLwhnzJbWqVSU2e10+3yXV8EoiOIp1QiDxYLY10fPoJrxdeRyymi+KAxOP5tmzyYoPx/Xtm30nIOf4rmgychAUqmUqL5zwefDW1SEIMsnel5JEsyZDXm50NtFz/PP0++17h0k4wABAABJREFUU99Uw59eeJGdu+oYkqCj9NBhfmQyEuhwEx4QgOh2geYUub6Dn3XRYECsqEB2OPDl5FwcZe5MiuLx5OZCegpCfR0MGw6N1eh6nWRFm6jTmfF2dhOZmoSg0uMrq0KsbwWd4VgRc3ISweWmOEoSQno66oULEaurYfFiOE1e/RkxGOCGG2DbZgSHHYbmQ2oyvPoc3H6PMiG9axNMmQ0eF3JnCYS04dy3mG0bGvnvumZS3XX8IL8Jku8CrxPUanzb3qM9ppmQiO/T3fMkWu1QRtwyh67y/xKc+SNE6dL3HPnx4+fcEAQBrVaHLMuUlDTz/HNbCBYaMAsyep0WQ0Ky0otojYTwWLBEgKsf3/rn6fau481Difx0YhndQR04XQ50BzuIm3Wd0t9oDoXMXGXp2uMCScLj6uO//3mS1ZsPMzkQynqgvR/m+CB8cJ963G46BudAooKD2VZTg00Uz5gt7buI39kX9I0mCAJDhg4lOCiI9uN3ThAIGTJEUYauMLXsi0ATFoYmNPSyyWi+WOjT07EsWIDk89H9978r7/cXcUxqNQHTp+NaseKcex+9tbU47XaMkycj5uXBypVKsbhwIWSlQ30t9HYSmDmMwIQEKqoPsW37PoQB2L+vjFstgaRrBVoGRHLMgeD2gtsFEZGnVtoGHfzVcXHwRStzZ1MUjyc0FObNgwAjxETCpjUIY8bTuceIUx1Hwpih7PvoXcIXXkvJe69TeXAPOZKeGEDQ6k8sro9LIrgsFMfjz0N8PMKSJXChynVoKCxaBAeLEVZ9DOYQmDMPfB745t1QVwkOLUycCcEmEAXYtwF7w5uoZn4L99Y/ItZ0MyMzFIteUNTH0HDQaBHSZxIa2MT2A+UU7TLw7XtvA1QEm0IR/AMufvxckbjdbtauWsrMTC+/+81iHv/rQyzUy2huuA9CrNgdh9CbRPp3v4kxaTS+Cb+k/YMIfvTnu3nkn3+mY08J48bfSGZeH97tK5HGzIQRYyDUiqdkO2x/BzErmn3qLPbXNWDUwGstECjDLAHGCqAVJXSShOO47zwZGPB6L2pW9Nm4oIJREkVSzWY6du8+YRn165bscrySaIiPJzgvD29DA56mpkua0XyxEHU6TDNmEDxhAvYlS3AdOPCFRucd6V30lpSc+5NkGdWQIQjR0YgOB5SUKIVUeipCbQ3YLODzIYyagNzTgqu9lHuHpeMuLaWmz8c0vYggy1i0El7RS9PAANHDCqCtHVJSlESTI5PYx3+mz6TMCcK5T1ELgqKk2WxnVxRBKfQMBvjed2FIFoKzH9bshtHjITqW8G8sYp4tgt6Dm8iQXWxv38zvP9pInMNDbJBAjE4E2acMBFVUnHhMl1pxPFdl9VwxGJRisaoMYflSZejlO4uUKK/gYKirwhdmg+x8xe9Mp8VXtx0MRozjHqQ3IIWtlX9iVryKUdfcBEnDQHRDcymYrQhhCTRt+B+LX/uUX/7wh3y8YiWTJ89ArR71xZ4XP378fKmo9Cq+sWgOAZn16EMsBErtYI2i1dGP3eWkrbuXdxb/mmumLCT/jr8ybuF1LF78J8o3Pk18mECwVc1DT6/l0ZG5SJIM/d1QtBFaKkEbQtfm7by+8SXSQ8LZ43SS09vEEBGG+0DlEcm12ujo70el1RI7qCZKgkCO1Uqr3U6QXn9RsqLPel4u9Ileux3PSTFtX5dkl1MqiTt3Hptq9nqv3OXmU3CkV9Fy440I9fX0PvIInjNNL1/INs6zd/GU9PUpS4/JiUrMW14eHNoPs+ZDQxVekw2LLpJxo5J4fO8hMgMNpJgC2NfSgU5U0+RxEWHQIvT3Ius10N8OQzIhZyhs2wrtncrgjM+nOPmfSpnLykK0WHBv2YJgtSL39382Ik8QEGJjlUGOrCzE6GjE8HCEoqLTK4qgDLgUjIDRo2HIEASdRumnuWoubH4etnnh9l/g7Wlg18GDPL74TeIHugno7iU3OJDsqDAGegcgKxNdQ42iwFZUKa99muzTkxVHAgOR29q+WD/HsDCE0FCk0aPPrqyeK8cri8uXKpF/0+eB2wmiCeKT8KamsOGxn5P06UtEPfBnmuo2MHBwM3GjF4K5iz/89vu882EXd+SFwYSbIMJN/4ZnIGQKAzVraavvRhV+Lb+4axz6sBTGD0lApfJPRPvxcyWjVqu59db7eOutp2js6ubgtj3It8wCvYpQSzKhphyi7J9SNnwkGbNvwYuPDVt3E9a/krysAnZ9tIGSfW/x0/F5aDWy8jejpRbi01ENLQRjIKa+FmbGvM+67bsIPLiEuUaJNL2BaEmLqA3C3eekvLOTccnJ9Pt8HGxuZkhEBG7gcHc3ESEhl6Q/+oIKRrVaTXRGBn2HDx+786uc7HLcdHPY1KmYvuJK4lFO7lXcuhX7xx9flPdXZbMhOBzKMMl57qOUkKD0MFosMDQboaoSps8ABEhLh+JNEBaJaNSRef0t/Pi+tQy4Za5OMHGwtZ1uuxOdQU1MSIgy9RpkRDhYChoNBATClnXIPb0wcaLyy1+8B+yhkJ2j7IPLBVu2KMqcwwHbtyOMGoUQHo73ww/xZWYiHGeFIMTHKykxdXVKL/DatdDRcXQY5cQTo1KUsZkzlAK4sQ7B0QeiDL0dILhx+uzsLd3L0MJZuGr38tbbL/D3x17G4fAhhQWg88JwtYis0VBnbyOy5ACEWpTl2JQEJft0/DglqaCz85jqeLLiaLUixMYqF0XH+zmCUtidy6rCkWZtQUDMylLORW4uQlMTQl3d51MUQWlHyM1V+lcry44VixlZMH4C9HQiR4bj7a9h9fJ3cBxeRdDoG6htrmTV0r8zYd7PwRbP2g0vU7ylnD/ePIqxc+9AHRnGE8//A3NwLTOyvbz498XMCDMSP+4JMObT3LUbvd6qJMf48ePnikUQBHQ6PSNGjKanowJjoIGg4ZMhLJqKllbobSYl5RZu/MFMUAXh9Ym4XGZaXaFUNjTy5/sX0FndSebtv8F+4EPk2moMsfEIQcEQZEZWSxzc9iE/+vWf6Grs4pdhIoUGPfFmG0yahRxoonzNRvqbmuh2OvmgvZ0F4UpXo4ySF32puKCCcWRmJgF2O43HqUyBUVGYIiKo/Solu5xqurml5SurJJ7MRetVPBlBQJOVpXgKnuf5lGJiUEdG4l26FOnee3CKAxxsLiPhwBrMt36Xxl2r8FWWUdPejnHTi7QGxVK3eSN3xgQSpgHBqGXAK3A0sb2vB3q7laVHl6ykf7S3IQgifPQ+MHgKCgpBPficQyWKEpmbq6icn7QrQxq1taiSk8FqhePV+OpqxRbG7T51oWW1KgbcoaFQWAjmYGhuhBefQRg3ARITYcMqnHtW09RVyl5nP9E5w/jbq0/x5PJKzPIAAwMykRJMwoNWL5AcbMTl8RAZFkJDUwdJWh2iKCjZpymp0NsOeCAxHrp6Bq+KWxTl8TjFkYqKE/0cFy4EQcC3dSvezk6EMzRjC6GhSIWF0NuLEByMqNfD7t3w9NNKkfh5+3ttNmVqPDUJPlqGUFykFIuR0XDbHbDkeZg6E+/SJax9898MmJ0kzpzJtvq1jE8yc8svXqO09F3qa328/NJ6fnnXPMbcdBdC05vgGsq3pkYjxn4PtaeEe36eQMteK9WHtmONkLBETQVJf+H77sePn8sGi8VCYOAkfvN/7xAVISAkDoGgIBIDIgEVCBJolCJOEmHy5JlMHDcab+OTLHl1NSmmYAiPQGVvoHTlm0Q0HcJ086+ROtT4Wj/kUMkfSRC7uHpEDLMnz0BtCsTZ4UQ393q8Ccl8sm4liQY9bb29JHo8mHW6S3tCBrmgglElSfRVVJzw5a63WvF2deG7EKPmy4njEles06djDA//6kw3nyMXu1fxZASdDn1BAa5XXjn/wQZJQq6rQwgOxltZyfsv/ov9Jfu4Z0w6r1Y1s3H3LhIbq5AlgeunTaDDXslNiUbGjZ+B2GcnLsBCx8pPiBvwKuqUz6sUjwY9xCdDS6uyT2rpaF+iALB5w3EHICpJIDs2g9GIPGEsZGWDSsJXW4P4/jLl9vEmrqNO0+cWHAy5Q6GmShlo2bYZob0N6utg9lyloHv8b4AMw4YhaE3kZmRhjw6l8vWVhHmdhEsCw0xgFURmRYawr7KBhv5e4jU6RJWK6JAgRI8L1FrweRFKBx0NQi3IvZ2KkmrvV4rUI8rjEYN+j+czfo74fIg9PUgTJ565YAwKUpTYjg7ljiMF6edBFBVfzCMKbF01wpOPQ+fgNLUowbSZsHW9ct5FAWn9avKnLKQjwIgrJpO8YTrkeBV0v8Snq9Zy7TXf5Uc/+gGt1R8gG2wISRNAjkEXcAeowoE0tNp4tNky3rLNoE9FpTqFh6cfP36uWFQqFVfNnseaJYfwBccBQUjSqX/PJUlCksyU2uey++ASFt43DXQSxc58qoL30t3eTGFvI1hseJ0hfLgOxg4fybULF+JNGYajZj9CRRlaXz8VT/+Zpm3baezzkk0/oiAcNeIWgBij8cs6BZ/hvAtGg07HiMmTcW3efPQ+QaUictgwenbtuqKHXbRWK6YRI44lruzZQ8sbb3xlppvPxpfRq3gqdAUFyJWVeMvKzu+JgoAqKwtBo0EcNgzJ5WJGXRvD4zN5Y1MJdQMVLDL66BVgqCWQIFMo3mAzOXf9mK76HZSvK6JuwE1PewMp5jBITFKSQ8KsSiFTV6UsN4eFKI7+nV2DSS8nXTCEhUFmFkRHg82G4HHDmIm4ty1l2xN/Z/TIsWDSK16IZ1uydPTAC08jtDQpt48Mz6SkH8s8drvhF79HNzSPOJUat8rHj+dnsLWii2F6WJQaTm1DE9lmEyrZh1mvJTnShtrpggCTUpS2toF7QCkaj3wbHa+k+uRjyqPog5sWQlMjbNqqKKD9/ScsoQuCgLB06dnfsy8qUtFoVBTFrEwYORJqKuF/TyE0NSpFPyjF4nULISoC3n1JSVgo2o4wZBjm7KEEJ8dT5jnIho9fotttYejkyXhccfzxDw9jDurjrgf+QV29SH19KAUFFiS1xNGTpRmGOdRNV5cbY2Q6CKYv5rj8+PFzWSBJEqIYzPgZV6MJsIFw9nKpo6OD9NwYVMPuAdUmctO7yYkvpGmND0ldCmtWoh53J48+cB/VzQL/3rGbDx58kPYusGoFnnmgl98vW0PDgJckIE+no/u4eOXzSXy5GJx3wRhmMhHa309TaenR+yS1Gn1ICL2trVdWUXVSb6I5Px9vTQ19V2jiygXzJfYqnmrb6oQEfFVV5//ZkSRUUVH4Nm9GzM+HgwcJFtT42ru4Sm8hIlimuLsLYUCg160hJC6D9CAvve8/zF/3lLN+awvqAZnbg6DGM8CQqgMwdgbYImDlcqVIC7Mp/X4dHYoJqyTBkJxj+xoUBHnDoLEWdmyEEAvc9h0ICkayRhCdncKhg0WkHNqL5PCCzqgoXScXnWdCb4DrboCdm5XM45xhkJKKrNUy0FvPc8/+h5A+Jyk6mGRSkW2QEQN0xFiCYcCDKTwEtShAYAAkJ8HhCqX/srhYKRp1hmNFluwDj7JvR5XHMCts34Tc0gxjRkF4OHy4HLKylKLtSA/n8f6OX/RnR6VSXt9mU/Kg8/PA5YTeHoTXX4Ly0hM/P4KoKLIpSfDsYiWqC5T9Kj0AXa249wms2P42KdHxFNxyN//v4X/yjWuG0LL7AAsX/oYVq7YzcaIbn+/IIMuJTearVq0gLk4LgvmLPVY/fvxcFqSnp4MnXLmQPgcyMjJIT38MHzpkYQY7yvv5ZPnz5MWZ+eATE/qeKsrWNjJzgolHH3ub4SoN1wUG8HRtO7098JfH3uRAUxejBiAX6AYCzeajE9GCIFDf388XdNl93lzQkvRAZ+cJjZfh+fl4W1txnE+u6yVGMhoJSE8/oTex49lncTc1XdEq6YXwpfUqngJVTAyayEgcb7xx3s8VbTZUERF4j5g5t7WBqMXscaDX6cHnpSA+CQw6RKMRecsGNjdUEx8skNfn4epbZuJY9ykt7U70KglUMjTXQmkxJCRAWDg4HdDTDiHBEBQA1+Upy50uF1RWKOqVx6nkCeeNgGmzwRJGd81BNm/fwTNFu5nSOUByajIYRYhLUIrPzq6T649TExKqWMEcKoZPPwZTCMyYC8U7qDmwiRffeI1rR0fTe98PEf/+ZxZmR6IzhZM/LAHRGg1NDYrZtT4YqqqUKeGx4+FAMQQaIMQGJgtUVinbs59iibitDWIMCFYbNNRAQw1yajJkDlGWhIv3QM4QyMkZ7OH8FAKDji3BH5kqP9fPlChCbOyxAvRIL6e9D+LjYF8xrFmBcHA/eH3Hit3jSU49psiefEw9nTBzAdKWD8irkRmel8JzK/9GZE83k0aksGJbNIv/8QQ//Mn1pFiiFaXyFEydOs0/Fe3Hz1cYi8UCWM7r8cXF9bz04nMMzZ3AN27+NkZdJ0/2VTM0SmnV/usDo+juqCQhRI9NpWXuiHRq67eQrdVT29KF1wOZKKu5GqeT5JAQJbrwMuC8C0ZBFE/c+StpOloQENRqgjIzsU6fjlqS6Fm16mvTm3gyRzKgbXfeif211y56r+Lp9sFXW3vORt3HniignTEDuagIubX1mEegJOGTddQ1NhMdZkJKyUCoq1AKmElTmaAVISGFKJ+DJ5Yto0cO4M6powjzghxkQUjJUixjwmxQtAvikiE8EhJTISqW/pJtFP/vYUboTagiYiAuHg7ugcoymHwVaDV4W6t596mf8PDTa0kAcqyBCJ4BGJIPXZ3Q3gBe6URl7zPHN+i3+J1FUF0GHy9V1M2rFkBTLVgsaGoaWbTgGoImTuLTj59gcpwO7eQF+ArmIkpePJKTfY/9m9wHfk/rp2/BoT2EZU4Fiw1MwVBbizchDcpLkTTJUFoGYyco2zme2DiIjFSsikDxSzQYlXMqSfi62xBq6xFEH+zYhBxlU5JnjlyVnzxVfjbi4yAqEuoGtxcUqPRydnbAyn5oO0ui0MmK7BFEEfCBSgO2CERBzZC5NyM2VyJuLeH7w1Ko3NNI7d5e/vTAfSSP9FK2/yXKOyzExmagVispEKIo4vV60Zwqe9yPHz9fW7xeL0888W+aqpaz4v2nmVyQSGSMl6Yt5fT1wXVpAQxLTeK2h99n8+5+HivUsGzHARZmWenxGFnT3EuED3qB0WYznzY3k3mpD+o4zqtgPFXCy5UyHX1EUbRNm4ZKlulZtYr2/fuRPZ4raxn9C0IVGkrovHlKBvSzz+I6cODL34nB6WhBks77PZBSU1EnJOBdsgRkGd/WrYg9PQiCgKhSkRgeieh2w5btoEFR/oaPhvBIPO5+/vHkU+z++EMe/8n3GHA5Kf/fP4kedwNGdz+EhUJsPOQX4q0tBbsDSSVAYBAlm5bTXVOJmJSiKI7L34CEFJi3UCkqRQGxt53cMC2/yI0lpt9Bpk7DAa+G7O5WpUcyOlqZRE5MU5Q9iwVqa5XlYIMRrDbIyISCkVC8A1YsU1JIrloAISHK8ne7l4iISLBZKHv9H2g1Fkbdcg0NSWN57bHfccO0MQSnePEZu/AYNayt281QezuWznqEwAC4+lY8nbX877GHGL5lC0PjkmBYLiQlQnvbiapcYxUse01Zak5KhdETIH84xMRj37GKHcteJj8xE2lVGfrAUITOVnjrtaNP/8xU+dmor4J3XlN6QY9wrr2PJyuyvsHPlcEIOfmwcQXkF0JTLWJEPKb+NmSjmTvnzqez6iClb3/KfE8fYXs30B2VSQ96dCqBX/3yp9x404189NFywsOTiIgIYc6ca8/xgPz48fN1oKKiguamIuT2BswekfwYHW+vqiZD9FLZBrnJoRiDo7lmdC4HijbyXpmL65PhvuJOWu0Q6wQ9MCksjP7+foINBqxnGCT8sjmvgvFUCS+X9XT0aRRFZ0nJV9M38RwQJAljTg7mefOQDx+m54knvpAM6AtCklBHRuJev/78pqMlCc2YMcjbth01d/Z2dipTukuXgsej9HxoNOAGeVgBQrAZOSgIR3sDzy9+hJ1LlvDrcUl8tHoZFXt3c22AD429GbRqmHE9Xp8DOdRKw7a30XbascXFgVZLitWIKwjkmEiIDFcsW7KHQ2QMNFVDTxvejlq62zqY8OOrOPTEpxAZi2bjdgjUQ1wcBAQp/o6xURAdNVig2SF7KOTlK715fT3w7qtQVQFDcmH6bKgph7degLQspbml8hC4elHvqSJuhIWXXn2PTe0f0l/fwczuYg5EGAjNHAbmELxaDetkB/EqGfU1t4Pk4+W/P8Zrb39MYLBEVkcTqu5IaDHC5vWQO/I4pVGAEaMhLhFGjlGGZrweXL2tbP/0PYJ0Mh07txBmCgW1SjGptUQpS7ntbZ+dKr9Y6E9SZI//TIVZYcABpfth5ARoqFYelz4EoXASqg1LCbvm24xz28HRhSY+hZKNb/HGlk30DBiYMO0GVr7/Avv3rcQ6KpaZt79/8Y/Hjx8/lw0ulwvgtCsLsizT1NREUVEzohPUCBRVVKFGZpsHCo1gFnX4BtqxV23nPpuKPl0gj25uQCNDmAcmAiZJYqLZzNLGRuJPXtG9xJz3kvTxCS+X83S0X1H8LIIkEbpwIYGpqdiXLmWguPiSthGobDbUkZE4j9isnCNieDiauDi8y5cffS+FgIBTW7pEREJ1LXKkjQN/uZ+fvr2JMI+T0VofjgNV2KIGmHH/g+z6+EWMASIRRhc792zlwK6PMIoGtu/czA+/+yNo2c7hnU62r30XOSCMgjt/RG1tBQNvPk1s2XY0c76hFEgZQ5GSMhidlsvyZS+Skmjk5bpWEgRImzANjAGweS2Mmw6HD4EuAFJTlEnryGiliCnZrxS7w0bCxKmg08Eny+DAoK/g8NHKNHVCKmTlEl91kKb6ZqKavUzyOhiWaiQ4IpUkTR+7XDHIuGmoqyIrSI3s64auJvD1MzPdRvKtBah31iNJPqg4ADUVilKqHVxONhiV25ExEGrB11wO7V7E+ExaVr5L8rjhrD64jckJabRtLiXA48UcFADhocpQjaCFi93nJ4hKz+J1NyjK4snFIoBWB67BAZ/sfNi8QlF4R05Slq0nXQO2KDQDfWAMhOpiwl1qJlpS8AWbSdB2M2RMAT+8To866wEkyZ8V7cfP14XW1lYefvg3xMWZGTv2JjIyMpAGL6i9Xi9bt27F4XDw859/n0hDDdYQKz+46WryG7fx6roK9lT10DIAh4rLyXzxZXr7PCzp8iE5G4nzQBzQ74NulYr5kZF09PfT3N/PxMTES3vgJ3FeBePJCS+X3XS0X1E8NYKAJjIS08yZGKOj6X78cXznWaRdDDTp6XiLivCdzzT6kd7FPXuQz0UZbWtTMqF37KB/3y5+H2KkutdN44CPuOgUrBPG8mZxKcs3VJG9r5Jar4qdjf8jTgWjTQKLZk1ge3ER/968gVlz5xFl0pKelolkdrL18b9jrmgg9poCCLEgRyfStesjREsCQRE2Js3K5pkNH6Jv7ycmxgY9TdDkhOGFEBMLeQWgUdHzxF8JDLYiSDFKv15BoVLgtNTD9rXQ2gyOwSViiw2ycuGtZ2DMVDi0B8LjCE/OJDyvHorWQrAZMkdAQhpjwsOQuzdwzTcn0rrkVeSoCJA78ZXvRDfrTl56aTr3JA2FwEC8NTWIuiCEjlaoLof0HBg6HHRa0OvwaET27f+EnEnX0Lz9X6hsWSx97Xk6Gyv52Z5KJvULLFDplN7LynIwaKF/AJKGKgNCF+PiRJSUaej8YUrP4qcff7ZYlCTFUqelQSl+1SqoKIGrvwmmEOTQdCXhp7kEd2s1kicKKW4IpsRcZrm6IaxFed8izaDOA1U04E908ePn64IgCKQkx2PrfYbGkgp8vh/gcol88slqyspKWb1qBdlJHgRvOzffYGVO7AhMGVaWL25n4qh0utt2UGn3caDbQ9+uJta1ybh7IBaZMEFkREAQW3p6uDUykmSjkYaeHmJkGfVlpC7CeRaMJye8XE7T0X5F8fQYMjMJv/NOBtavp+vVV5GP83W6VAgGA7phw/CuXHlezzuhd/FkmptP/V5XVsKQTIbPjKanpw22byXHGIy2sZV9yz9mY0UdSfhwt4BZdjNVAyYVTDEFIdXXsfWDteQlJzEqoJ2NSfmYhsYi9zYzK89CY95VtIsqbGoJWXbTsfFZEhY+SNmhR/nb794gM3sEG/auY7RRB4cEuOtBsEaBxUbL4R20bt1J195NpKgDsBZthIBQpcBx9H82+USUYNxUZeglIxciouCjVyBvtLLMbdTDwW1gCoecSZCUAi4723av5oUly/nOjbfj6CpD219OiT6AFx+4k7TMCWTeex9bX3gUT3UFo6bMQmpth9vvB60GqsqgowW+/3skvY4h19zP+qJ93PejV4jVu+nuddPtBvMAxEQY6HL0YAk0gi1SWWpPiIW+NmXCWaMH8Qv8ArTaYNJUJTv8X39RlELfKd5/WwREx8CGj+G6b8KmVTDzOphxDb7mHXg2v4I7fyGujnKe+c8PuaZgIYk3/1RRGrVWECwQ0goDHjBGApdH6oIfP36+HCwWC7fc+gDPPlxPwPLn2blpGyWVvXyys5sJuUaykqPQ9e9nQrAOoaGPdxs/Yt0LHxOpDuTjOjs7mn1YvDAgw8HaDiK9EA+ESyrmhkeyrq6O68PDSTEa8ckyhxwOHDrdZbUcDedZMIpAX3n5sWVAUbzk09GCJBGUne1XFE/B8VPQfZdqsOU0SGFhCHY7nvPZJ50O/cKFyFu2KL2LxyG3tOBualJi6waTSI4iCJCbi1h2AFNZGfmRESBJdNkd2Ppc/MJmpg+Z2v4+YoIM9Lo92IL1mAON2BvbWWjSkBJjwSuaOFBSTF6KgfrAcD5oCMYsV+M4uIf7QnWohYkk3fYQTmsUv/rpLWwodrCteC0jAwCzALZwiI6H+HTqDu/hV3/4JcX76gn3wZ8jvVgDA5TYv4pSiEmE6goldwqUqe0JMyEqCvZsgRHj4IMXlX5HQRy0oBHAEgnXLgKTiaY9j/Lnp4vZXlLPA9PiiUjs5cO3D3JD4QwCapZy15xMYpLG0mPfzW/eWcEDBpm6Q/uIm7sQ1r8PaflKv2TcfHD3gehiy5a1/PJHP8PXY6e9T/kCiQJGGUAccGCyWPGlZyC2DSbkdA1mU+sl5b/JmYoP5Ommw8+GICpL9FNnwNBcpRfx2cWntgMCpfiePBMOFkHOCOU85o+CYWOUfXC7UCVFUdxUxkdP/4qAdjWJ6cmAD7obaahaz4rSFm75xi9Qqw0Xts9+/Pi54vnww+Ws2fIaNyVImEvb6avtYUxwKL+YNYTHt7RTUQxd/YHYq2SWHbITOACV6k4qmyHAB6PV4JUhM9jM/o4u8s0hTAoMorm/H49WS6rBgAB4ZZmuzk6yIiKO+i9eLlyQDyOAymDANmoUfeepEH2RaK1WwqZPJzg5me6lS/2K4nFcFlPQZ0DU62Fg4LzeK+2MGYgNDXg//vizz7PbYfx4xeD55ILxCO3tSk9dnwMC9JgMegiNAbeDrrYmzBoNvS4HcUFBiD4PelFEHxWPOSsPYfg49u/dxremzKWtt4Xf/+MWGhpd/PKmMZhnX4VK1wI6EeKHsnXpg2za7iTDAPfmRjMlPoqgQAtMWQghYfgG+tizZR0V5Z0YPZCqA6NBo/gNehwwNB8a6mD8VKXAESVlGbqhGprqIG8krH4LqkvBFArDxim9kfggIQMi4uHgBvav6yLL1cpNc9MYNm8hK5csZt6Cm2nUlLG/BzbuWk7nu++weZ+POWovCQFqonx9sGud0usXFADtjZCVD/UVOIOM/P33v+Cm9FDywzMI7mqlo8uFvqOdVqfIxEgbDW3dqEr3Y9KowTKYkNPVBZ0dkJii+DFmpJ/Z9/F0WG0wbASMGAl1lfDKU0p84en6pyUJFiyEiAjYvx2+9V0wh4BWpSiyplDEwCm45dGs//sUgnwdzJ07F0bPoaNiL4erS/D01lNYcBUqVS2Qdu776sePn68UM2fOIjXqH6QmGHj/sScIr1iLL8TMT3+3AbvLi9oLDk0bn9SCzwVtPogTIVmABAkC1HrmBgdR43CQow9gQZgVl8fDJ729jA8MRKc+ZiOh4/JcxzjnglEQBOKyshA6OwEwWCyIfX30Hjp00XbudIg6Hdbp048ms7QsXozniHnz1xxBFBENBiLuv//ST0GfDklCN3IkvsYz/LE/CdFqRZOfj+/pp0+vaHu9SrEoiqd/jEoCQQ+BIaBXK8umXi8mWzQ+rRqproae/j4C1CLYByA6FHH4JMgewZARk/EIAi8/eA/d1f3E6URGB8tohsHe4gP0bn2aiJ1P8O4bRfx5UhgZMUnkf+NBHOUfsvvj7Qx390FzJUQnMyxOy63jkggs2ovNEESIQUOXz4Opt11R4VpqQKdW+gcTUhWlUaOC+CQo2a30KY6cAsPGQ1KGMiHc3QqGQGithqxxTBkylimd9WCJAXmACQttGBLTKC8p5ZO3nqascoCNpT5uTTJy1913YnU7kTd9AmoRckcrJuGWcEAGtRo1Hu67cS6546/H2bQJ7bY1ZKrD6HOqiC2pQNJqCbY76OnrIcgajqhTQ18nmAcHRLo6QKeBxkpIToDyChgzDkoOQncXp3Qyt9kgJU1J1MnJVYrmt1+Cw6WnXn4+7jPGzHlK3OOSZ+Eb34HoOLB30R+qY9fyJYy94VpkZxeyWoe7z0XOxGuQsqbgVBt4c90rJBn6yB59H6FpecCXH8Plx4+fywdRFKnt6KS2vZ2lWzp58Pvfpk7dxSu/KqdFhnwDdLpkQoAANSSYTFR1dJMVYqKhvYtrwywEq1Usb+3gVlskoiDQ5XAQ7HSSGBt7dDseWaZKEE57eXop3aLPuWCMsVoZGhdH/aefgiAQMmSIYvL8ZU5HiyIByclE3XQTcn391zaZ5XQIkoRpxgyCCgvx7NlD/9Kll6eZuiiiCgzEs379uSmMoohm2jSEmhp8TU2nfZhvyxbcWVmojUaorj7966k1ivJVeVCZRtYot8W+TkwBAQQZ9Ag+H3idIOkAmca6DezZU8/Oqlaeef1jZofCvek6ApML+Ov/NlG0ZifXF1Yx4ge/409BbnZv2kdMWjZiXBh/fHgzt4XqoK8FMkYgOrspdRhYu28/cwL0jJ4yBW19naKAWjKg6gDEJMCwUYrSl5EH2z6C0p0QHqcUT0FBEGgGa7hSLDq76a9YQ3dtC5Gz71AUNNmLb8AF3nbEsCyMIRbw2dlVtIapcyKILfIyPcbBHT/7G89vXc+Hr7zCDV6ZW2QvmK4BRy94THBoB+h0lOxbzh//+xbe/7zDjGgfv/rbo+BtoKiyn0Mr13JHXjaasZMIOViCEBEN3d3Q261YBMXEK/nVYWHQ3QPd7ZCUAH2DWdXXL/ysYbgoKqbhB4qgt+fsiuIRrDZFnU1IhP8+BBNmgMmEXLGXw60beW/ZR3znz6/Q1V1Ca+tOQkJMjBx9FcMn3kbZ4efx1Ji4/b7nEYVWNn6ylMSBN4jO/7c/L9qPn68xarWaUaO+wYp3n+HXjz6Ew+Vi+S+vJ0CCKWESyToNFZ0O7C5o96lIcPUTHBCASfaiMgQQGxjAmqYWYkU1ofpj7S0mTrxUPtDUdFr/RafbzS6vlwKj8aIf76k4L4Wxv7ISn9uNKEkEh4XRvX37l1OsDU4/22bPJjQvD/u2bXSvWOEvFI9DkCTM8+cTnJ1N36uv4i4ru2yX5rWZmUgGA65zVD5Vublok5PxPP74mYsFQUDMzYUNZ/H883mhrBxcHkAGjVZZNjUHQ18PouxTipfkXDAYce/dwf9Wv862vU20D8DdVgjTCXjtPip2F9GzZzc/nT6MnFu/hyfIhSe+lv7tErZRU0B08c35owk5fIDeyBQCQ63g6EatF9HpJEbNn4f66kW8+rf7GNnXT/KkucrwhkarxA3aIpAlLwdd/ex6/1UWLrgJdXis0sunlpTpXp8HX98h1mx+k+zwYaDygNcOwTbo2AINTWC4G7QC6LIZXjCDxY8/SeMekYf/8ijeIB/PPvoisT4XidESdtmDoaEM6g9DQwxMvQG8bjQ76vh5rIRWC+mTrsIl9fHaJ/vYtaOcwowhyD97mNpNb7J/61bmBRhQhdmUoi89Wznv2flKLKIpBDauAZ9HGUiyWGHnplO/V5+cQ7ILKNvR6mDSDMjOhcZqeHGxoiqOmgCdbXjK9rBtyaNce8ddGBo3Ydf2UNf8HvqgeymqFhmp05JbcB8wBAQBr1dPZNIoYlLuAvz9i378fJ0RBAGjMQifLorv/fjXeOxNqGodfC8umNFD8zGabYQ1trF2+SqGBujp9ILF7qTIBbdERiIj09fRybCwyDP2JgqyTLwgnHLgpcvpxOh0EhcZeTEP9bRcWA+jICDpdPgG/RgvNgHp6UTOn49Klmn+xz/wORxfuxi/M3HUXzElha6HHkL+kt6XC0UdH49n9+5ziiEUQkPRzZmDb9kypdg4G729yr+zIQqg1YPdARaTsiQqCEoKyPbBgrO2CsZNRUpKY/7b/2NEeAAp4Sa0ajBrfEgTr4a4FH5rjUI973u42nbz7kfV3HDLvUz+dif4uqjvTePpdUWMjUujsLceo6hGttfTb6/lgZ/cR5/WxEdL/sba9YcYMyKFzg3vY3b2w7w7IWsE3qYDVK37Djc+uIsfhHsRd3wAk28GWwyEhCn76fPQUbyHOIOGQ5YhqFf+GFX2bYQlxSPapkOYG4xpIGwDuqitHeDXP5yPurmN0KxEBlo/oCDRh6kTYsJN6EdOgP4OSB0Kk28AZy8cWE/KyIkkz/0GQmcdZIzEXbqam267nRHRj9O4x4svQMPGT5cjNVbikJwEqgUYOgyCQpSJYwBRBocDrr4ePngLcvIgMBC2b1XMtfvPsUA8wpFknLRMyB/sbXztKWhtVIrIW78DHy2BxDRUXi/X3/ZjHOGNVO78HvWaFCypbWxZ+xZyfyA1DfUkJU9DkpQvaklSkZJScO774sePn680FRUVlJWuYe+uLeQmJnDzVVcx+drraGkuRT9kApse+il7BiDE00u7R2R4hI2BmiZC9HoEBIaGmgkWFJPvC52ADuaUzTtfCudcMIaGhxMcGkoHEJKWht5sprWt7SLu2rFexbCJE+l6/XUc+/f7p5+P52R/xcWLL/tiUQoLQ5eby8Czz579wTodxkWLEMvL8RYVnf3xXi+eNWsQrFbEzs6zL10KgmJIHRyk2L6kpEN5iaLeyT5IGwITpyOWFZMRHk6G7IHJ86F2P3gGYMwCCLEijV8IajWavr1cOzkKwTsUKToal7uX//72+wS07SLplu9z6NOHsKbnIGgNZIT2sHSHm/ffeobS0j5enZ6EJTYWl0uGUdMgOgFcDvDK7NvRwT+vzyU/IR0ik/CmjqamcjdsXUXCpLkQkYglawJFa5dTvv63ZBVG09jyX8ICr1J6+MTJKC3UkwC46qoFOByF9Br/RXn9Yarqy6jokUnxSFgLxiGMuQqvz4tn52o0e1YiqDWgUiOMmIUgCqApBJ8TdeEC9pat50/vlfN/d9yLS4AdHW5mGaHb3ktgRg6oBAi1KKqipEYODISoWOqf+z1i+T5sagmprA0KxirL66s/hPQsZdjnbIiiEvfnckJ/D7z7ElSWKsq6KMGUOeB1Q/kBiE9BqCpFFRqKZ+kydkYayLoqhrXvNBNgcrB1XQnp6fEkJamR5Wn4Bi9IpZOXyf348fO1JTU1lZTUyUwYW8bffv8XVO2lfFqylJ7mbmqKGlj7STFxMshAgiijEUUSgwJBBkkSCA4MYF91A6OMAUiX2QT0uXBOBaNKkpiYl0fvgQPIHg/B8fH07thx8Yq3k3oVWx55BFdj42W7xHqpuBz9Fc+GFBqK3NSEr7n5rI89OhX9+uvn1ospyxAYiFBQAIPm8mdlcIiLmHjYsQXSM8FohLpq5b7yg8oStloLQTZIzISBHhgxFdRqGGijq7IKnzGAiqotaLVN5CTdAGIwgs9FeFQ04wuGkJYeizDkX4gtH0LEtUTbfsZYz99wl9qYmTqU8EU/Z9XjvyDF0U/IqImDaSxupKAwZmWPZIuji47Ckaza/g4jwuIJtxcpyl3pWpDdEBDChLv+j4mWWMTAYMIdrdQ4u4nyjUISTZ85bJ3OxqHWBfz6p1dTdbiBMFlmZJwG1dRb8aSPZfFjf8P+8bv86L541HEjwRyOz+egp74WU0ou1O7i0MG/sbksjad/HoHPXEPv4d8gDJTTH2mmw6UhWvIpQy4WC+SPwyuJVL36IyIPBbPPeQAG7MTt2ERGiAWKtiptABo1hIed4yW0T8mGLt0PXt8xqx5RUrK9E5Lg2YfgSGypIEDuOEwWCyO2rGbVi1UkGUIoLi1lVFIcEycNZ82K/QzNjWfLtvWkpqaSmjnu3D5Hfvz4+Vpg0OtZ/OS/aO/VQYKe/Z+0c8eNUTz9r1eZnxRM0a5jA7jWoEB0so+Gjg5iwywgimSGhiDJPi7E/N8JXEpJ6JwVRm9nJ862NvQWC9Zhw2h+7rmLskOCJGGbN8/fq3gmBAFDZia2u+667PwVz4gkoR89Gl99/VnVP1VWFtrx4/H985/nN7gjHvEkPMX9p0L2KY9v61B66Ybkwo71kJqlqF6r31UylAUgIx9CI2DWNyAsGp+9kwPPPQj2Klp1RvrVgcz65lT6+4t5+90KcnOH4ZZDSZ/7ApIqGmQ1CB3IkonD1XW8tqaa9g4df/jj73jnxZ/z3Mf7+E9BMOz+ENRzlGEWnRUpbgSxghF1w1ZiM28nZmgaPdJLBGvjcO7ag2AKR+4SICAcXVgs7u5Kij/8K/b4fKKSJp3ysAVBQKs18q1v3cBjjz6KuU8ga/wU5Oh0Dq3+H4k2PcO/t4gBtY+B3g4C4pOpWvEoJkMkJMdAeDi6/Zncddd3QNWDbEngqbd/S86YRJzdJopW7ya5rQ7D2LmQmY/H1cAH7/wPef9aom+9n/BAEy8s/RcznC4iJBFTfBwcOggeL2xec+7v92feZwmmz1OKxZcXg8MOKrXy/idngVqFsGsD8dd/m7uiEwEPic3tJOj7+ejp/7FpyxaG376G1JTvkpqScuH74cePn68kkyZPYf36Txg9egI/+9kKag9vYWgK7Gtws2OvnUlAXJARjU9EEgQkrY5owQteL+aAAGSNms62bkSN4rsYFxBwVAzzyTKHgYhT/A3zyjLFnZ0EmkyXzJ/xvHsY1Xo9A2VlDJxPnNs5IkgSkTfeiCklxd+reAYMmZmEX2nFIqAazIB2HpcBfSrEqCiMd9yB97nnkL+IFCGbDeLjYOVpPP+6u5Qp3aJtyu2IaJi5AN5+TlkaFgVFzSucqlje9LaAJCC4e0hf9BiiWoOvbCVi9izEgW0YvJu44fq5qNRJpKf/AUmtAgSl6AycgtvlZtmyZ8iLF5lyx72o1NX4Kou5J10gPTMFRs2BpDwY6Ae3E2nkAuI9LWw/GEBtSy1Bzf3YDckIvZVsanUwIgJGDE3GOdBAzbbXaD18iCHJ6ajyU5DEFiD2lIcdH5/AT364jgidkW/ddCvm+FDqNrxK+rjrCHKpGJD3cHD948TvXoEr8EY+XvsKN3/7DzgG9kOIBdvUfKSIZDw+kb//41Ge+88yJo+Saa+L5yfxEehHTYWcQnw+B6++81eWv7qFGIORdd/9M+sbHIQ63FwTE0yQSoDDB8HrAJdXSYS5kC9Ei01JwolPVJRFx+AKSJgNouKgvUnZzk3fhxGTwDtAX/mHyF11CDnXMz1hGNNvk1HLxQSH54DKev774MePn68069d/gtWyk9//+o9s37qbuZGwtmg2r733DsO0kBcIBp2KUI0B0eXApNeD1qdYuZkt+Dq76ENFdJ+ToMBAJK+XkuZmhkRG0tLbe9oMaVmW0TidJJlMlywB5pw00SMZ0sgyurAw1EFBX+zysCCgi4oi5rbbMCUn07J4Md7+fn+xeBKiTkfI/PmXZXLLWREEDNOn4929G9+ZpqNFEe3MmcgbNiB/UceXlgb7is4wTCEcs3SRVDB+OhRthoYaJRXE7YJbH4D0odBQBrs/RB4ohb0PIx5aAuUrUCUMRdS2QPBQhOA70eoykSQdarWaE9dXRTQaLd/97oNc9Y3H6fR8wmPPvMiW3iQyspIRkobAiFmgcsKGf4G7Dw/wzhO/441//wqv24DRGI5Ol46k6mfb3i6Sc5LpC6ylN7gV6v/E0JwYmjRR1O0tg4Z3FRX1JLxeL/97/jmK91Vx+7fuYkhUBy2Ch+iEdkTRCQPN9NtTeO3DWj6pqcPtiWH2/F+j1lnABYh5EDoVxCjee38TO7Y8x/cmG5g9/ZdMHJ9CIyKCWgCPE19/KyHOLfzp1z/gZ1ebmBer5o44E9+JCyLDrKFnoB+CgsEWBRpJ6d0UBKWX9FyQJEURvn0R6LXHlEVQlMWx05ViMTIOkrIgIR36G+mseoVtG99lx6fvgdeJSuulyd1F44Dl3Lftx4+frxXTp8+mqTGb7qYqtP0iK3e7ePfpdxhnhDFGSDMHEhIUQK/HjU8tKt9nKhUEGmCgF2nqVGLHj0ZIT0bweKjt68M3WE/JQOwZMqRlLt3AC5yjwngkQ7q5uZmkWbPo2bXrC10mDszMJP6uu+hfu5amJUvwnRT75kdJbom4/36E+np6H3kEz2Ce95WCOjUVbUIC9tdfP/2DRBHdjTeiiYrC88orX9zGg4Kgp+f0PxcEpWCxhiu2LGpJMc0eOx30ekVxdPZBax0EW2HibfgOr4KkKXS0vQPi1YRJ3dDXDeYFIEic7ddaFEXU6hi2Fw+hsfExZl17NS+8+iqWdB8JhiCoqYCrfwmOJsTuNlKGFjJ6wW1Ys/NAiEKWZYqL8vjNj5YQbImjvM5BSvoMnHPyUBkSiJYawZcGLf8BXw9IphO2X1FRgb2njOLiXQQEGBDtFdhEGXv/ZjRaOzGp43jsX49gDhfJmfoj9DYTB/sGiAwJQmUeApooEKIBUKs13Pfd2YQEGPlo+XYOvrOa264aAl214MpGEmTyA6w8++ofuGdSLlHT87ENuBHK9MgtjQTZeyEsFA4UQ5BZMeQOi1ZiBUUJOk4zXBdqBXMoDB8NMXGw/DU4fOjEdof4VIhNhMP7IW0oeFwQYoX+jQREJLC/70NGTJyHLFfi9mip3/UBBVd9E6TQs3+u/Pjx87XD4/FQVrqfex54mOJNn/Lp00+j6YUborVkxiYSYY2ip7KaAJcbQaMClQyZ2Uq7kyDCsEIIDETes5GOB/8fIZJIgMGI80iv9WnocDopU6nIUV1wQN/n5py2fHyGtLe3F3db2xejMAqCUizefTftzzyD40pSzL5EpCPJLeXl9J7rAMjlhCRhGDMGz7Ztn8mAPoooop0/X/FbXLxYiQ38IrDZIC8X/vfU6R+TlQ0GvTLN63UjqwQ8+WOQDm9D+PAlyJ2MkDhESVvpq0fu7MAeH4jUXYvdMx+VLhxMgSBHoPxKnds1oMfjYdWqzdhMPta/9gKzbXpiJ86DnjqIzAOrEVQeOlr+TU3xIaJS/4jP4+bjVR8zbdoM9IYwgmK+DwHhiGInomTDEGIFRCTilI3E/OKU205NTeWHP3vk2B3BFmRZRqvJQKPRgKABNNx2119JTsrl7fcWExJwGJzzwRt1wiHOmHEVP/nJK9RXv8quDTJP3TcffXYi3sYqRK8TIciKJvJGbhhbgzZ3IhYdPPvKY9xy+3d55R8/Y2Z7O6mh7TBuOvT1QlUF2KygVimqcOoppqYDgyFzqKIcNtXCumXQdtIgld4As2+AsmIYPRVZAmS3onyqInnznddYt83LLXddD6EiqgEHhXN/DaL/gtWPHz+nRqPRMP2q63nyiV+x7P0PSQXuiTEyYc41aGbehly5g8CPP0QY6EfwumBoIWQPU1Y7AoOV8ILedqr7tnCYDkJlNbvbHOSFhZ1xu/WdnaRptZh0ly408LxKVYPViik9nd41az73hiWjkcDMTGJvuYW2Z57Bcbr83685R5RF37599L///pVXLHIOvYuCgHbmTHTZ2Xgeekjx6btQdDrlHyi/oAUjoK4aoanx1I83GGHyFNixGQrHwt4dlDeXUVldRXpOHMED+9EfFNAkpOEJDGPNB88SFR3Kf95ai0VoYeT4AIaMmYuLSA5XqkhJ8Z2zFYskSRQWTmFcVjQbDbto6xQgfgT0VYAhEoRPaO3MprUzj2l3mVGHz6a0fCvx8SpUKonU1FRkWUYUBVIyRhw5mRd86gRBQKMNPnr73nsXoVIpx3L11X9DrRYRHPtBqAUyj26rpKSEPXsO0FIvc9/t1zPpGwtp2/BP7FX1xKcdgJAIQkdfQ6jRDL5+Dh38H1lRQ/E4d1C15yDGWDPExoKzB0xmWHALbF+nZFl7BiA+HgZO+kw4euDNp6GtSZmOPj4mUBQVZXH2DVBfAbHJdPa24ajejDlZQm+dT7fQxrbd63nq+ecwhUQDBtBw3OfzyrO88OPHz5dDfX09A201RPoGuGVMPrMWXI06MhUyh1Pu6kEOfBlzSABhsVmQMxQ6msEaCWaTssoRnUjAliCedvtIczoZJunQAvYzCHHdQOCXdYCn4dwURkkCQcCcmEhfURGuz+m/KBkMJCxahEaSaHvqKRwlJZ/r9b6qHK8sXqnF4rn0LgoGA9pRo/C+/PLnKxYlCamwEKGhQVmWVKkgOhph05pjlisnPZ6MLCWj2etRLF3qK1Dv3stEi5oBk5p9+zsp76ohY/cnvPTWMpq3rWFGopfScjCFCkRGhrL78DYSRoXTb7oPSAbOrWDUaDRMnHgdD/3jb7ja4E+/+yNS8wpImAnqCFwOMytXvceECQtRRXhAUJOaeqLNy8VsflarNcftq175H0P+CY8pLS3l8X//lKwEkd98//8YGePB1bSF0Dk34tY+ijcpn+1r3mDEiLGoXKFgikSNjfHXpPDPR39OZr6HrhYvUUGBMGo2qFXIGj0H3d30PPMUBcNGIm5dDUNGQMVB6OniaDF31EZHpSyDqNWQW6gMuMQmQvFm6O6A7BHQ1oSwfx+6nGH01XzAI8/XEBsZSVVNJ4Y2H/EJSYPHrL5kDeV+/Pi5/HG5XNTWrueGO29gg8fN3O8/iBQRyc4DH9LzygFi4sMInHUtnU89RHPJAAn1FRhnLFSCCZLS8Np7EAkhMHc4w8OsRGhc9Nb1sL+5mbjTqIxdDgf7BgaYa7u0mfZnLRgNOh0jJk/GtXkzksmE54hv3QWiCQ0l4f778ezbR9MHHyB7PJ/r9b6KCKKILiUFy403XtHKIpy9d1EIDcW4aBHs2oVcVvb5NiaKiEFBcCSj2maDCNupE2JCQmHuAoiLg2Vvwsy5sO5jSE4jXq/FqQ/h+b/8D1dbEw0c4KMlKzDJbm60CYT7BBIluH5oKpljbiW94AYEdxliWBqCeO5Gz16vlzfeeINlbz/Pupe+j9WqhuAhIOwBIlDrw7j++ttOMThz+ZCUlMRj/3oHUQSN0ITc1wDuLtqEYP63J4Tp7X9ElzQHKTUX5GqoWkJiRhrNzcsICHPx4iuQPSUNpl6HNyyGdevfIj4ikqf++3dymvoYHqBCTMuF/nYQfDBnodLXaO+DrWsgdxSER8GAEyJioK0BGqpgxWsQmwRX3wHVJZhlD3JiNg2vLOb3m2pIxoPDrGE3TnImXMcfX3mS9PRQrrvuJ6jVl27Jx48fP5c3arWaGTPu5sHv38nvv3UVAXmTaS9ezNCsaOQxI+gvfZ4d+z9iq1bCXFlNzHe+DwXjlF7G4F5K1r1IS00nnthgesUOtnV5mOqVcZ7hQtXp8RDv8WDWar+8Az0FZy0Yw0wmQvv7aS4rw3bTTVBdfcEbkwwGEu6/H29FBZ1Ll/r9FU+BIEmY580jKD8f19at2D/++IotFgWdjqCFC/Fs3XrK3kXBYDiW5LJ06efuixWzsxF1OmhuVtTDmTNg70nT0Wo1ZOXArNlQUw4vPKE0IXtcSspLcpryOFM4cyPDCcxMobxoK9oAPU39dpLNRgwBWn51cyERI8ZBUhqCdgBZFUy/3ceGjUuZOXP+Oe1vd3c3Kz94gof/dh8xk347+Nuo40hxKHCiync5IkkSknQkZzkRwazYQbTs30XO8PGEJ0YiCRKCaAMhGJJH0NdXwl/+uout65qIUYmkFI7EZw1n8etv8Pw/f8fvvjmNui4ncbJMbVcnCYd2K1Y7oTYoHsyctvcqvUAWC+xeB64B2O2Dxhrlc5SUpRSL7z4NlnCISVCWqhsi+M5V1xEcF0vvhqcJKFjALx56lVsn9DN22v8NFud+/Pjxc3rKysqYNG0WkdO+hSgFEjb8+4CBgwd3sOTVjfQdaOTT4n5+FhuMMSVN+Z7a+DxeKYEDdU7+/p/XyTPKbOuAhD44k24oA+2yTO9lsPJxTkvSA52dBERGYoqIoPbtty9oQ8cri/5i8dSckAn9978rWctXcLqNccYMhIYGXB9//JnjOKIsivv24f0iFFSDAWnqVFi9WlnWnjULoiPhzZcHf26EtAyYNh18Hli9DA7uhXnXK/F5/30IPMem1HRBwcQXjoeNH2COjKBHEomxWAjR+BBmfQMKpkNwKHQeYstHiynd9SL1Lonb7/8OMAOl8Ds9ra2tPPnkL7jne/dxzbU/RBTOfVjmSkClMpKaO5XolFjwqVDWjI0ArPn0MElp2Qy0dzNL5yJkyAjqKlajrvgdf/1WArt3byOg243NrCUqJFAZNnI5oL35RH/G7k6oLf9su0FSFlx7p1IsVh5QCkaNDkGvJ+rnTxAl+Wiwl7J7nZ456fn88mc2MoeORq0zcyHpC378+Pn64Ha7sdtruPWOhUjSkZ5v4+B/A4hPzKK1oZRX75tIojEMVWclSFNgzC2Uv/trug/uo68XtnZBsg/CgWCdjt7TDHr6fD4ONDeTarUinS6A4kvinIdeBFHEWV2N7yyj36fCryyeHUGSMM+fT2BKipIJ3X8ak+krBE1WFobx43E88shnisETlMUvaLldzMpCUqlg714wGGBUIcLrLynFRO4wZbBFrYI1H0NZibKEOXOeUiy+sBicg4kgR19QhOpS8MqIGfmYohOg9qByf2w6XrWDdz54iF3bq9m7ZSvXZgZy/Y3XYgoezbvvLEFSBTFjxuzP7OeRHjlBELjnnkVYLDmca8/jlURaWhowqNYe9x3n9Xrp7Gxm94Fm0m0JjItvoLe3DXNUFrHqVCZcey+52S+zQWVmepABdWe70osamwjVh5VoxtqKwUGX475HVCpQaWDMDMgfqxSLhw8oy9dRiRARrxSOsg8CPsVT9y4FObE0lG3i8J43yMp/x++96MePn7Oi0WiYNeu2U/4sIyOD8tI5jLy+lpSC73OwrJSNS97h9vHTsUuN/G99HVt2t3F3iJpMYyByWj6HP1hFoNlMbXMz7bJM90lKogwEyDLmK0VhFASBkCFDkLTa81a8/Mri2TlBWXzoIeTPM/hxGaDJysJ0110MPPMMvpP8Ir9wZREQsrJQ33QTPPWU4rm4aBGUHVT+/wc/URTFdasURdHjVoqIBQuPKYvOQZNnQVBsDxLSIDoeNtohNgVyRik+jY3lMONm0GrZ99t72VFUTqhW4AepZsZ96zts76zlLzcvYvJsiaG5U/j1L5eTkJTEvn0VpKZGYrEYuf76B1CrtVgsFsDyuY/9SqOiooKcmDqM48eQFaPBVf8aBrkPo87LjP9bRr+rjz17tjHr2yN59bkPuUkF2sJJipF6Rg709UBSOtRVKX6KAKZQyClU3rv2Rnj5YWitV97nGQuVgZmwSBC8oBbAOYSYvMmQWYVP00fs2L8jqk2X8rT48ePnK0JcQhpID7Gp8iUe+u2L/OKbt6Ia6OLhP/8KyRXMn3/wI/L7ypEzp/L00lfx+mQ2NjUxwmajpKWFVJsN8Tgl8XLwXzzCWfcgNDwck9VKgCTRvXXreRV8ok7nVxbPwmeUxSu8WJSsVoIWLmTgmWfwnOSreTGURSErC82ddyI88wyUlcHtt0NrA0JcPCQlwCcfKYqi06H0NUZEw+SZEBFxTFkEpbiYMlvJjY6Ng3efhY4WGFKgKFSBQTBqFkQlQVUxSUmZ/GTIMHRyP8YRMylvOsz7r63k27MmcbC+lC7nTrx1NdQ7E8lKncT02RMJDg752vfIpaam4oz9Bev3/JrZ19xGvWYTGksGcqAJSVML3bXkTL6ZD1f+l6KGFm4clQfxSRCTiKdsP4JGjeSTYdgYsEUr0+2GQNi7GbraoLlGUQpVGhg3W7HXaauHADU+owEhyITdbWLvjvfJzyxErZqMIBxZMvfjx4+fs+P1elm27H1mzZqt+NaiRPcVFxfzq19+j2H5obz/bjFP/TSFvW111Kx5CFOkkySPiba2rXzvnbW4TftYv7WY+TJE6HUkGQw0yDIhgnBCc9Ll4L94hDMWjCpJYmJeHr0HDqBLSMB3nsWMdfp05Pp6Ol5/3V8snoKvmrKIJGGYNg25qgrPoUMn/Ohi9CyKg8qi8MwzcOAAZGXB8GEIRgM4euH5JxQ9Py4BYhMgMgqsNjhYBO+/pCxLw7EM4imzFRPorlYlDnBIAdy0SFGufG5lqdtlh542Au76MwH7PoXk4WCNJcHr4XeT70JNP/mShKgxUnBHOz51OGpDBJKovrB85K8gWq2W79z/B9auXUtmxj9Y+eFiFtz8Y9R6PUHadl548l3aS9cTGSwgfOd30LqVJo+LN/+3ggQ7jLGZCEofjigJULRFURq9HqVdIC5dURuNgYpxd2st5IyG4vXUbPoX6mnxPLa0DofbSvnoesaOjyIgIHhQ8fXjx4+fs7N161a2Lf0/pk9L5EC5mvb2dlpaWvjj739La+1+sn0S91ylZulLVbgMqczMU2OMSOCJ/x4gRD6MW7Swa+NujB7oBG4ODESrVtN1im1dDv6LRzirwujt7MTV3U1gQgKO7dvP7VUHE1zCJk6k5ZFH/MXiKfiqKYuqqCgMM2eii4rCvnjxsXg2QfhilUVBAIMB9aJFSs/iU09BSYlSLH7vu4rJc1cL9A7AzDlgDsFdW4XK7UZQiUp0XF+PEicHEGSC3BEQEqbEAIZHQm8bFEyEyHgIMiGLMn3lmxH2b8M4YjbChIXQVg6x6cjBZpo+fRLbiJmoB2rB0486PAc0aiRtJhDAV2mY5YtAEATUai1Tp06nra2NSXPvQa23IiNQfCCR1av3YfQ6mDjsRlRBRtbvL+c/jy+jek8TPw2X6Fa5COprg/oSyB4F1qhjL67RKYrivvWQVQhRCbDtY4hPQS9H8cFjO5k080a2bd3IlMIxfLT0N8y97n6+ju0Bfvz4uTB8Ph8pman87Md30NoE44a4Wb6hmh9cNxSzPY39+93sK6pDJ2Vx17e+xT+fe4ne9u1UN3qZdtUEdpbXofFChgwZkkSi2UxlZyftOt0JSuLl4r94hHNaFA/Py8Pb2oqjvv6cXjQwM5OEu++m7ZlncDWeJmHja8xXTVnUZGVhuvNOPOvW0f/KK8di/UQR7cyZaEeNgl27FOucCy0WDQYEm00Zbhk5EnHnTli+HDQaKCiAu+4Egx7hUDHu2oOoKrYhTL+ZOmMQb/zpSRYNG4YmOQt6WsFqVaamg0xKFrHFBhHReOsPUP/BO8TGJ0KgWfH1k0TkrkZ6tjxG6+FWsscuQBI9YI2Ag8ux9zUTEJlCvduFx72UeNsDEDAE6OfyuS68fDm+l3PA6eTXDz6AytfDzblxjJw6ks5VP2fblmaunTWR2s53GRLkI8ZsQKwpUax2itae+oWn3KAUi68/BCNnggxh1/6YW5OHIsudaEQ14ZGF3PqdqajVl9bbzI8fP1cWsizz4jvbSO2tI3f4RKrL4KapGj7esp0pozN470AFHU0e3p5uZ+kTT1BRVEdyoJp2u5fn392I1uVhllaHxeFkvM2GShTpczjIVanQHde21OXxEH4Z+C8e4dyGXgDH4cNn/2MvCASkpxM/WCz6s6FPQhAQ1WpMc+Z8NZRFSUKTno7pzjsZePbZE3sWB7OhddnZeF9+WTHlPpeBKUFQeg2P3LTZEDMyEPPzEZ1OhN5eePllKC1VFMFFi0Crgs3rEZITobWJjqWvEhZtxWPYwwdPvIu5qw4hYgZdm5YTHJmEUDABmmsBL1SXQEw89LbzyYrXCTmwmVi9F8wjQSWBewAxOAyHw0p3qBkhNBDsOyBiDM4EAef+nRgLH+D1J37HrbfdhxCci/IbE3zq4/NzRgoL81n0nTlU7/09jS3lxOUu4vbhJl5/7pdcNy2CQ5uaSIyMgZZmwAeRiUr8nyAqU+5DCpWJaGsUvLtY6W9MHwGWCERTGJK3nXU73kEX5wVfHTL5IFz63iA/fvxcOaSmpjKiYBSOPW/y2ltriNZA5w6Q9LCkaDetzcra0ktry/Faw3B0OjjYIRPvg4ABDyN0BmZqdexAIMlgQJZlKoGI49qWvLJMcWcnQSYT4mXSziTI8qn/ig/X61FJEovmziU7MhKpuZn2DRvO/GIaDSk//CGONWvo27HjivYQvBjoMzKwLFiA5PPR/fjjV3SxKIaGEjh/Ppr4eFyvv35CsSharWimTUOblHT6bGiDAcFqPfE+QUDMykKMiFBUSlFEjI9HKCqCqirYv1+5aPF6ITRUKRYPFiOs/hhuuxOKt0FvF77GKoRv3UPL4a30ipsQc37GlhXvkFZ+mPzhhQhJQ0CrA0kNOSMBHxgC6F39LIYhI5DKt9PbUoPQOUDA7LshIhFfUwXgRnRUQHosGPpp63UToK3CVbcLbdxitHozCIbPHqufc0KWZTweN5Ik0la+DEuQiGh0IAtWXKKGf//qTrKlLKbPmoe9dBe0tWOoLoHcycqktDUG2hugqQr2b4aAYBg+HfImgMkEGqjr289bb73O3ff8h8rKWmrrKpk5c8ElPnI/fvxcSWzatIkf3r2QSMnOnVES1kAL/2/VQfpcoAaCZTAJgKhijAr22z1YtDq8DidqjYFbwiN4taGBqYGBpIWG0tTTw9uNjSxMTEQ3KJh4fD5WlZeTEh5OeEDAOe9bUWMjvbLMkM+xjD20ouKU959RYVSr1cRkZhIYGoq9ufmMGxAkCducOahkGcf+/f5i8XgEAUNmJra77sL+2mu4Dhy4cotFSUKbk0PA3Llw+DDOxYtPyIhWZWWhX7gQoboaz5NPgtsNej3iyJHHMnpFEXHYMESHA7q7j722ICCIImzYcGxZ+8MPoeWkpJbcXJgzGyrLEJYvBa0WgoOhtRlqqhATkyA4hNBZt1K5rJpVb75OVVU9k6+fQMmO/WTkjUKOSoDQCITKIrAlgDmUwJl3gm8r9pqNLF26gatu/ileRytU9yCljgJ5AF9nN+9/+DwzJ43FbL2HgYEWAlLvQhQj/UMtnxOlt1GZOLSmLgDA623H6Wxlx/a97Nzt4Z5HRuPu3sPew0upX1vL/Ow0pPoDEDIBmqtgzRsQnwWFsyA5F4wBEBSCq/JDXNHVVFYHUFnlRVKJCKKKadOmXbLj9ePHz5XLD76fTE7S1URUrubZ9fvx+iBHC71umKwRqHXDNfFRFLd2MBEvLo2WSp/EbeERfNrQQJTBQHJICLIs0ybLBAPq4/6GeGSZWkE44mZ7WXDGgnFkZibhJhOSLNO1c+fpHyiKWGfMIDQnh6aHHsJnt3/R+3lFY8jMJPyuu+h79llcV/AyvWS1Ypg2DX1yMgPLluEpKjrapiAYDKiysjDcdBO+Zcvwtbaimj0bQZYRoqMRGxqgpubYi61cqSiGJ19YyPKxgZmTyciAa65WfBVXLEMoLlLMmwsKoaUB6muUoi0qDnZuYtO7L7Cts5PhOancfvMYNhbvIydQwpuQwu7175CWN4FAdx2oYsHVDCodDR16/vnuIfLNFoKTE6hx1yMUVxAbnQaNe2kq3cRQt8T2PXqiUg5TfngP06d/4yKd8a83Xq+Xl156m4ce+ivjCi0svGEen+7+F4F1Pl79oJ6ROh/9Pe0ExaZCRzVU7oWskTBkFJTvhpKNUDAbilfT1N7DildLsOtNzIloQ9VRTF1dLykpKcd3QPjx48fPGfF6vXi9XqzJv2DVvo9w9G7lpQ9aiPGBWoRRAVrG2UI4WNNEoNdDR08/BVYbH9U2c29SCr1OJ1VuN7dYrUiCgMfnY39T02f8F0taWggyGAgzGs+wN18uZywYRaD/8GFkSTpjwotkMBA6ciSdr73mLxaP5zhl8YotFlUqBLUa4/Tp6PLykGtqsD/+OHJHh/LzwSlow6JFqAICENrbUU2ZglBfj1BXBwcPKgVgTc2Fqc6iqCiIM2fAuHHw1msIJQfA3n/iY+qqwScf3Wci47HpgpgaosYaYuajp14kKDqcXx9ysMj1Gwb0ArpRKchUINRX4sm8gwHJw7MvPYypq5urvnM/vvg0gr3vcbixBPv7vyV96t1Ezrodr9SD0CATn5hIYrL1lLvt54shNTWVP//5T9SVbCQ8ZoAQ33ziZ01Bpf8hFW9UIahFqCqG+lKlxaC7ET5+FoaMgyFjYcVTkD6K2BFTuGviVXR5amnevxlBcDB9+miUBSQ/fvz4OTfeeutN3nzyZyQlpvP2mtWoPR50A5BvELguIpQErYZDrT0kmoIQkElARhQE4pHRCALVnZ1k6XSY9XpAURI7gBHH+S/KskyVz0fsYCrY5cLntg6XjEYS7rsPZ1ERzvLyL2KfvjJcycqiYDCgychAX1CAymxGbm5Wkluamo4pgEemoEePRujrQwwMRIyLg2Xvw+pPlcf19V3YDhiNYLNBViaMHAk1lfD4wwgNJ03qG4yQlw/rVhy7nZMPu9aTHBGJ0NNB+e4ysjVmWkrbuMrlQL1vN1kjUln+5NMMvWUUA431lNctwedbzV0334jGO5XAsESaKt/Dmj+cLg1EDImnw+kgxNGGZCsgPs00uAR9+Vz9fdWQJIlRoybgcDj4f6teZfb8e7D3NrFt706e/aSHH101h4DxUyjfuoEEtwcpc7SiPofFQGcjvP9PsMVB5hiwRIJGTZA2iqCY6YiSAzDhtzzy48fP+aDVCNz8jVjee7uCmF4P8YFG0nKiuFbuxGl3U6M1M3TWeFQ9XbTVNmFuaOeQ3Yldq2fA42G72824wGMOGpuamggyGLAe16fY0tdHi93O+Pj4S3CEp+esBaPWYkFzOmVo0G9RI0k0LV+O/AUkd3wluBKVxcHYIZXNhiY9Hd2wYUgqFa4VK3C1teGtrT2mEEoSYng42pkz0cTEINjtiMOHIbQ2wo7NsH8PpCRASSmkp0FZ+emXmY8gisp0tCBAdjZMnQIuJ/T2KJnQ5aWnViitNuVxh/Yrf/tTM5Tp5qrDSEFmcPSROmYSGLXIu9fS6XKgHzEed0sp4+LiWbmyiKy8eXTUlrN2o50Aczl5Y26D4EAiwkaClMjYqybx9osPMH3CHLBOAgR/nfElotFouOPe3xEen8gLL7zMh289y53DEhmIC+K5De+z5f1t/GtiBtKBTyFzAnQ2QGsN5E2DjNFgDMbbtgV7aBVvvraJ+Qt+wvYdbcyYMedSH5ofP36uMAZc8Ke/1hHga+UfN08he9YtHK4tovHR/xIXm0z0zGuU3PoAE6a9n2KprmJXbQfTw6OpcDiQXS7izWYAOu12ih0OFoSHn6Ak+oAoWUZ1GamLcLaCURAwhIXR/dFHpzTf1kVGEnPzzbQ//TSyx3Ox9vGK40pRFqWwMASDAcliQV9QAP39qBMT8RYV4V25koEDB5ShleMQQkPRzZ+PJi4OubhYeZ3x46DiEEJfB6x+BVwayB6NXDgSPngf8oZCySEItZy4NG21KkqiIChKYmQk6LRg0MOaVQj794LXp/QpngpBhIxMxYjb51NsVSZNh3UfQ1sLeENgaKHi1aiSESQtIQFaiIhHf+3dyAGBTOxr5f/982H27NvBj26ZR1qKiZ7uSoLMNyCIKkBArbUybtZf8HgPgWQA4dJnen6dkCSJ1NRsnE4nn3zyPkMLEthbfZjOgBBMkkCipxepq05RETuqOVrQ6w3gdcG+VdS2tvJp8W4i40MwaUKYPLn7bJv148ePn8+we+c2HpgCC7/5bwRzIB3NB3nslTeYb9IyUu4F2QuZeRAZR2vnQZ7SCVhk0Az2KhaGh6Ma7FXs8niI8HgwH2fWLcsypQ4HDp3uslqOhrP1MEoSns5O3G1tn1V3RBHr9On0r12Lo6TkYu7jlcPlqCweF1guqNXoCgoQANFkQpebi6+yEjEgAPe2bcjt7Tg//hhfa+tnX0etRpWdjW7OHMTDh/H+97+IhYWoYmLgw4/AFoqvei/dvl7MlnDoqENQx8DkScgeL3KEDaG1RbHYyR4CQcGQOxRqqpTPliggbFmnTEe3tpzYo3g6dDoYMRLefklRMIfkKkuS5SUg+yApHQ6XQEYu4AO1VilO7XZ8waH0OZr5f7/5P6rL95GRMIRx029G9pYTEGBAFESOyIiCIKDVBYAvCX/m8KVDrVbz4x//mLrav1Nw838IMR3ipaceY9y0yQhGN77qIkRzBIgCGIKV6fdVT0DmJOLn3sO35vtA5wH3VkTVvEt9OH78+LnCKC0tJTupjBvmPweho9m9+5+o+59i2vwRvP70WqYOTURKSQWvE09HHTv6ethd1clYwD3Yq2gRRQTA6/Oxq7OTYLMZ6ST/xa6uLjLDwy8b/8UjnLZgNOh0jJw69bRLiQHJyQQmJND0xhsXbeeuNC61snhEMTx6e1A5lHt6AFDHxSE3NuKtqkLo62Pg2WfxHbFLOo1CLBgMSBkZ6GbMQPJ68X3wAd7iYqQZM1BnZ8NDD0FhIYQEIbT1ECwHwg13w57V4G6H4ZM4VN9A19MPk6cNRnP3/VBfD0YNvPA0QkuTsiGffHol8XRMnQF1lXC4FEJCYfps+GQZOAaLzV1bIC0TImOguVq5Ly4Vxs6m+bEf89rGrYQ4XIxOi+fqH9yHUeuE5Ks4WNpIfXspGRkZRzd1fCKJn0uDJEkMHTqBnJxC2tt7eW9tN7t3aci7Lp73PllDRmAkGQYd6IJAHwQ734WEfMi/Cnr2U+HQ0NSyiRHDb0XjH3bx48fPeZKUlERS4mtIkg4ZyMj4NttWl0D3fgziAJ6bvouz/gPqDOPZ+fxTvPjuFlQSSMCBpiZMAQFHexXbHQ6q3G4WWiwnKIky4AQuxziB0xaMYSYTIXY7p5qNFnU6om66Cfu2bchH/PK+xog6HaYZMwieMOHiFYuqE98qlc2GJjX1qK3N8YrhkV5SKSjoqHII4Nq48cRexDMxqChqp05FpVLhW7ECT0kJ2O1gNCKNGqUkrtjt4HLB3iKEvBEI3Ql4GspowI2tcAzPvfUsb723iTscfcRo9ISveA8pOAT2bFfSOAIDoaz0/IvFjCwYMxaefBh0erhjEVSXwYGiY48RBMjOB2nw3OkMMOcWiIwnzBDEbbFmhJBgjLNuQ0rIp791OW898ho7D9Vz7W03sH37FhobG7n66mtJTb2c3LC+vgiCgNst8vjjf2HLhpf4xrgwavs6eWdXDQUjBqjd2kHMpBtA9kBMFuRMh6ZDNNft4v13XkY0wYihV4E28lIfih8/fq4gZFlm//59aNRq0jMyEYDa2gbeW9XG7GnxxO/ZCxLU1HTw4N9/j6+rg45OD1oneGU4LMsMG5x69sky6zo7ydDrj05LH6Gqq4tOrZZg3eVXMp5xSdo3MEBwdjbdK1eecL91+nTk+nq6V6z42g+6qEJDibj/foT6enofeQRPQ8MX8rqCwYAUFgZ8VikUJOlor6Gvq0u572TF8AgX0FsqZWSgv/pqRVFctQrPvn3HehkNBtSLFilZzqWlyn1btsDIAsX+xuOjq6SK5uIttHX288kbS0m0e8iPMuPGw/79+8mJjQGTGWwhsGs7ZKRDZRVYLFBbqywnnw6DUSkWb7gRXn0WAgLhljuhtBhWLj2x8AyzQUqmMgTj7IXbfwwDdtizFpXTTsi3/wiRscgqkUN/XcjmDoEYm5qcGdfz9tsvY4lOJyUxhaREv7J4OaFWq/n5z/8A8oOo3S+weXcY42dVgt5IaH8/9LdDXztkT4XiDyBmKGHZ01hUOBXR04PKmHH2jfjx48fPcQwMDLD4Hw9w77dH0tr6A7xeWLbsfYLNafztkXf4RpKPdl8bT6+rINsAIVorDS2dtCMT4AMEgYzwcABqenup6e/ntsTEz2yn3+EgW6VCp7r8euXPnPQSEEDf7t242tqO3qe1WjHn59Px7LOnHIT5OiEZDETcfz9yeTm9r79+9qztU3EkO1kQ0GZmIpnNIIrohg9HsNvxdXV9RimU4fS9hheKIIBOh3b6dLTjxyMvWaLE/Z3kqylmZCCpVLB8+TGl8shksyjBpOlYVAKWa2+hv6cMw9sfMsMgkZyVSeOhMmKMg0vmHW2wZb1SADZWwqhR0N6q9C6mpMO2LScmpwgCZGXD5CnKYMyS5yExGXKHwa7NsPbjE4tFUYKJM8HrgahYEGTo7wa9EZoOw40/RE5IwyvLSPhIuPVPpBn1eINtCEYDgenTScoaBlIfCKFf3Hn287kRBAGdTg/oOVg1k1/88pv8+cE83l33CSHFzcx392Oc81PorANrEtiSEDzNvLVsKSNHxZEYOuFSH4IfP36uMGRZxivLPL74MZITd9DvsLL2g3dJSnbTVCET+6072L/2lyRnTifYvZviIh97XRDmExhiCqaptxe1IOD1+djZ2UmmyYRWFD+zjSrAdpn1Lh7hzCWsLOMeVLAAEEVMI0bgranB3dR0cffsMueIsujbt4/+998/v2JREFDFxCBIEpqsLNTh4QhaLSqjEc/OnSDLeFeuVAq2I0XZRZxCF61W1AUFaAsKoLoa3z//iVxf/9kHqtWoZsyAFStOnJ72+eDQIeRgHcKIYZRXHqDe7kNbsonH770OrRiEGDeEgPoKuv/zDObUNKiphhAL1A8qilvXKq81rBDKSpSp6ZzcY9sICobYONi3Wykyx01UFM0n/gIO+4nL7BYbjJuq/NPpwdkNOg1ozNDbBcMmQXQSdfu30FG5j6HjJ6IdKOdwfyitpcWMvP77JNnUgwWr35j7cqatrY2rr47CSTbB3jUYtT6IHwoDXWAIUdoQOmvwtB1kXuEodBGxQB8QeJZX9uPHj59jyLLMnoNtuOqdpFR9gj5YYEKuzKufCvx6+lDcXZv4w5Mt1HS9jtfhwjY4Y3erVkdJdzeFERGIokhrf/8pexfh8vVfPMJ5aZ6STkdoQQFdr732tVUXBVFEPE5ZPKdiUZIUk+tBBVEVH48mMhJfbS2CJOHeuBGcTtytrchfVlLOcYqiJj8foaYG3yuvIJeVnbbHUczKQvR44FRT8eERkJpI1d9+w3/37MDe2sZ9N82m39rO0uJuOvYWs7O4grF2O7dV70dMyVW2Y++HzJwTXys7F6JiFNucI+i1EGCE1HRFSWysUwZe8kae+NzAYMjKhYAAZVq2sQLqD0FSAmxbgjdiJOi0SEISKnME2UNHYm96nZ3r3iYu/2qGT5kLrnrQJ6BMSfunoi9nMjIySEr6Gw8/9AfqatSke1XE7t1MblQKQlgSDPQiG01IaWPZsrMWZ0kls2dnX+rd9uPHzxXGBx8sxeuq4rYF0wgf6OPJ9zZjlCDYIbNzezktK/sIkaG610U4ECTDBL2BVK2OHQgkGQzIZ+hdBGX1MPoy9F88wnkVjGFTp+KpqfnaJroIkoRpxgyCCgvx7NlD/9KlZywWpbD/z955h8dVnun7/k6Z3kfSqBfLVrHcewGDjU0zvUNCQsum7qZskl+STbJJNrubzSbZVNJISIMQICR001xwwb3bsiXLVu/SSJpezjm/P0buBQMGDJn7uriwZ0bnfHNmxnrn+Z73fXKR/X4ss2ejOJ3INltGQWxpIfbYYxmV7kzZyW8TJymKDzxwfILLaRDl5bBtG0ROGHkjSWAxIXq68acFHwlHsJcWYDtwiBcOyBAo4OXlqyhLakx2yQyEQ+TWbwRDgVkXneJdqEPXaFezzQ5jqqB2IhQWE122FdrbsMXjmfMe+7NCgM8L+7fDnIth62qwWjH6uxnZ/APEpR9kzd7l1KqXYT7wHIVVl4HLyg/+bx1LJl9B6YKLQGsA5RIyPWrn54c2y1FycnJIJl3MnVpKcmwX2kAOoYYdGLJADDVDoBrD4ia4+e8smL4E45g0hSxZsmQ5G/r6+njl5Ze57ebFqGYTv3pgDQEDinQokqG3PYzDAEmHGQIqDci32Lgvv4A/d3aywOnEoqo0j4yc1rsIme7o2Dv71N4Qpy0YhRDYSkqguRkAU24unqlTCT744D9eo4sQSKqK56qrcE+cSPjPfyZ1OiXu2OzlKVMwurvROzpIr15Nsrf3nVMQT+RNKIrHkZuLPGUKPPDAyfcpChQXQ08nztIKnE37MrMWL7uaG8vKadn1Ant2b+NTs6ahbFqPqifB7IRkCl5bDmbryV3SQsoMXv7wJzKexUQMIkMQ3EFy0zoS7QN4Zy/MTNQ/TGEpBArAboXHfgmTZ0MqhuErpPnhXhp3PUhnUicwZ4jugAdfRRVb1g5QUFzA9Gv/X6abOr4NFAfZYvG9g8lk4sYPfpvGxvX89DufwF08jradr1B2wW1gsiGlQvgvup0XVuxi0aLF7/Zys2TJ8h6jvr6ezZufY9vaMKneOHnpJFPtgqtzHKzpi5GIpNHS0GdAnc9L9+AQN/r8tMfjGMkkY7zeM3oXITN/cWcwiNPjeUvzFxVJoshkwuDc/xY7bcFotdux22z0btkCgMnvR+vsJNXbe46XcP5jrakh57rrkHWdoe9/HyN2wneAUU+inJt7fPby4a5lTTu7ouzt4nDm85w5b0hRPBbh9yO6u+HELmyAyy+Dni5obwVZApMFGg6g5W/mzyufpP/lF/jyfdewU5do0xUudJhxVYyDglLYsBomzoLd28E/6hcUAqrGZ7ab92yGrlb43H+CRSbqs/FKOkGxrDLfYYXcHAiONmX1NMOLj0A6BaVjoaQSGncg7AEmzFjA+LHj4bVHEVY7JRXj+cFXP8G+3ibu++evoMgOEDJYL3jLlzvLu8OBxh4+++l5dO/RiDZshJFDEKgCcwE0rWHRBVNRFPndXmaWLFneQ2iaxuYNayjz5/PAv38CORnk4X/5KpdPnERp7RQqfQ5++62fsS+e5AK3l45YgoneXIrsdh5oaDiS7HIm7yJkPJKmeJxKj+ctJbzU5Oayr6eHtCyTa7e/lad+EqctGD2BAGo8jpFOI2QZ39y5pN9gkfFeR8gy1tpaAvfeS/SRR0ju3Xt8sSjLKPn52C69FFNBAQwNkdq48eTs5XeLYzOfi4rQ3oiieMJx5HnzEO3tJ7/+dXVw4YXwsx/AuOrMhFIE+HIxrG4KnniES7xO9ENdRAc7mVBtwheRMOQ0QpXghg/C8CBctAQqxmWGbqdSUDoGOlsz/s8Pfgo6DmC0bCWZN4H02Aa6D72GEepHbDgIA/2gmjIq5WFl8vJb4cAuqJ6MeOlh5JoZyLkBGDcbvW4++x67n+tsvaworyM/sISf/eynGAZ89GOfQFVN5+oVyPIOcsniK/iv/1jBq6teYIn1EGNyZMwTbsRIRdmnVfHKg8v46EfHoKrZojFLlixnz5y5c/jQ7dfgzO3ir49uJ2qxUD5nDlz9UR7/04/ZkkhRa4AUi1FpsrPE7+fFgQHSFstZeRcPc65UwfZIhIpzXCzCGQrGGy+/HDkUwtB1zPn5OMvL6Vu27B9iO1pIEpZx43DNm4etooLIicO4hUCtqsI2fz6msjK0bduIPfpoptA5HzK1hUCuqsI0uj5j+3bSDz+cid17M4fLz0cuKzt+lA5ktqHvvQf++BtEZ0emYDyMw4Hsz2XRBQvpaG0i3BmnqH+IbmuS7Tn5XDtnNvKmNZinzIK5FzOwYxWDT/yWyngI6fp7oK0ROpvBm5PJ5oxFSafTvPLAt3lxT5hbpDixoT5svrxMUZlbDL2dGbVy5sXgdGU6o/dugI4muPDqTG71B76OaN/NvNs+ixTvIddn4+9//0/GVk/goktvzCpQ72FUVeW+j/4rpHdRHkqTEAZmsx1dtaJ2/x/33Xdr9vXNkiXLG0KWZeZdsAhN0/jtr57kP7/1C7508TSYvwQYoMI0xF90qDKgPZ7mXr+LtlCIPf393DN2LBZZfl3vIsBgPE6jojDpPJy/eJjTrswIh0mNFgfOqipi27e//7ejD3sVly7FNW0ayY0bGVm2DK23F4RAWCxYZs1CrajAPGYM6Y0biT/3HPr5dF1UFfPSpZinT8fYuBHtuecw3sr6hEC+7DLE9u3Hb0fLcmYr+rXViH3HFNPa0YJZAGJkmJLKcZCfT2DDCE5ZYtpl1yEXF8JgESmRIhkf4X9/93smNu5mzMRqWPsc9HdnVMO7vgAuD0g6SiiPmZPm0dX+EmNklYFYGEvKjjR/CexcB4uvR6+bDhYbUv1G2PRyplictxSqp2MYKbR0AmXyEpRoH4Y5TOu6B5k9cQ6Vi65Hk1znXdh7lrNHCIGqmlh69X2s+tsjHOjZy9RIF3JuHWMv/AyYijO2gyxZsmR5HQzDYOfOnSx79ilq6yYQicZpbDjIFUumsujeT/Dj5eupX7+SbTv2IMcMWoAb7XZsZjO/7e1lkteLVZJe17t4mI5gkGqzGc95mPBymNctZWWbDc/MmcROSHt5vyHb7VhravBeeimyrjP8v/+baVAxDOS8PCyzZmXSVg4dQm9uJvrYYxCPv/vbzqMcl/mcTpP+7nczQ7ff4vpEVRVyRQU88sjRG2UZbr0Vigvh8YeO3t7dlRnCbRuVwnU9M0C7pws6mxEXXEWhRSWejNO+9e9EylSGWnew5W8PUNjfxvhcK6KyGvwFMHcJTJ4LqgIHtkNkGLHgWsaNn8BHTcM4+iKkI2lE9ZSM9/Gj3wC3jwPL/0RyzRrq3GbE2Kmw6CaongqpBB0tO9j/+OeZdedPcFbPIyns7Ny/gTtuvAiT2Qyql2yzy3ub4eFhVq/fjKdAIZWU0QbrUTwloETAlI13zJIly9mRSCT40hc+zUXSBlTv9Xz9B8upzAkzoTaPPz7zSX7zyzjXujTaumGBgAJDcEF+Pmu6ughIEvNzcxFCMBCNntG7eBgBuN65p/emeN2C0ZyXh4hEiNXXv/GjC4EYTTE5epPAWluL4vGc/HjDINXTQ+zAgczIlBPRtHO2JS4kCWQZU14e1upq7DNmoCoKsZdeIrlvH0YyibBYMt3OU6ditLaSfPhhtDfjAXw7EQJhs2H75CdPznx+q8gy8vz5iA0bMsXx6G1ccw1UVSJ+dT8k4kcf39gAlyyGlgbw+jOxfYN9ma3wseNgTDXkFcLaJ8FSgXXXIWpumcnuZ3/F9BKVVNAMSz+QyZjW0+ir/4LQQJTUwOLbID6MnAqTqruAx//2J9jTzI0WG8q4CeDyktSS/Pn5p5jRtIvxd/0zIjcA8SFY9ReYsogdjz1IT1OIosadjKuuZdX6ncTM0zGV3QRoCHH+frPLcnZUVVUxbtwCli3bxqr9vfx6oYaiWEFPAkOA911eYZYsWd4LGIZBwBunKi/OEz9/lNIY3OXJJeG38tXfRtHTBk/1QQ3gMOBCr59D8Tir4nHuCwSQXycz+kTeC2a/0xaMksWCDDjr6jAikTdUqKm5ucg2G0pODs5ZszCGh4/cJ3u9qA4HiS1bTpphaGgaens7vkWLUEdzlA+jh8MMrV6NcDqPq9K1aJTUWUbkCUnCVFyMkCSsEyZgKSjAXFREascO0i+9RKS+HmEyYaqtPb7b+Te/ecNdxe8Ih7uf586FrVtJP/fc8QksbxFRU4NcXn7Uu3hYWayqhB9/P5OwcsJ6CASg4yDMmAPbXst4Os1mmHcJlFVBSQWW2ilUagn0kTb+9MQvWF0fo8em8+E5UxA2MxRVQvs2mlc9RFnVLOSr7oFoEFIRKB7Pl779E8w7D3HrBfNJTplLWpKwWO2kYxG2bdnHVK+GseUZcAXAXwgX3w4OD5fd+S/o8bsQKrz0609SH7Ry48c+xqGmR6kYc1tWXHyfcOmll1NSbGAafIT9219gctl8cKiA+91eWpYsWd4j7Nu3j737eug+4EPqHCTPgF+u68H2Wg8VSYNEGsplhQUK5MhWaux2ftbVxc35+Ue6k3tDIXojEW49g3cRIJ5KsVXTmPU2NKqcS05bMNpyc0nW12P1+4msX3/mZJdR75+1thZzeTmOqVPRDh5EdjhIbNqENpqBDJAGomdINFFsNkRTE+mmpuNuN8JhHHl5WKZPz6iDZArM4RdfRK2tRc17/Qg3c0UF1sJC0m1tSIpCYt06QsPDaH19KIEAtgULsEyfjqwoJF988fzpdj6RE7ufH3rozXU/nwGRl4d6662Iv/wl410sLs54FosLM8riicUigKpmElp2bspE+Q0eziAXYLaAxwcWK3oyiiELItYIXTvW8eWrSvEMDFIyawYNQwcwNXmoUBPEXW6YcQmp7kaU4mrQI+zcWY86soN/++yHKZxwEfGXfgD6RZCYh8ls4e6rL6Bw10rExbeCaoH8sVBaDTteRnH6oHY+yWSMMYM3M3fifP763M+55sY7s97F9xGSJLFx0yG6OrtwRMxMdlVArAkc2dSeLFmynB2rVq3gu9/5Imv+9HfmBRqoLi7jqfY4T67YQKEBdlniGr+XrYMj3ODzsWpwkHkWC1WjRZ9hGPQbBm5AfZ3fL0PxOPZ4nLLCwnfgmb15Tlsw6kNDRBsbsc+bhx6Pn+5hJ3n/Elu3Ev3tb0kfbpB4g13DRjRKuqXl1HcODREdHSR+GLPbjSkQOFJEnpGWFsLPPYfk9YKuI3m9uJYsgVQKU20tenMzWmMjib17M0qdJGW6g3X9vCkchd+P5dprz0n382mRJOQlS5BaWmD//szonHvvgddWZzyLidO8H2bNyXQqV9VCX1dmLA5kklpmzIdlDxF/7a8cHO6jo3Qiu4Yi/PM9t3Gwr4tE5wCphR/i0GPfYslFVrA7qZmWR+fYqfzxvz7P577wLWQ5yZoXv8QHP3AfBfOc6CP1RD0G7uh+YrE29tQfYlfzNioLchAeL9rExez+3TeoXP8XHBMXQs1cwl0N7F37FNNv+QySt4gP3vsZTGrt8TGEWd7TKIpCdfU0Xn7x18wrFhCuB//8d3tZWbJkeQ+gaRpNTU0sWrSYF154koHeA1zw3z+kPdjG1s99EZsBJYrCNV4vLw+McHtePvWRCC2axkeLi48exzDY091NVSCAdBb1iZvzf5Pr9F3ShoGjuhqTy0X6xC3fUUXRVleHe8mS471/b0eSyQk+yCPLUFXMEyac3UUWAhQF90c/imyxQDqN5HKhFBai79mDsWcPciKB7HJhmjPnuB/VduwgHo2iTJiQuSGZJLlhQ+aY71S0n6qiTJyI5aqrkA4eRPv5z99a9/NpEEVFyJdfjlJYCA8+CNdcnZmz+MffHN8NfSKSDGXlEI9C3WR49IFMgstFl8GCxRALZzpU/ZX4gyHkoWFm3PgBdqx9gofW7maM18Ht4x4kd9o4Nm74P2ZeegvGtBqefuTLWOO9SDleNrz4KJcu+SnjqsvACkmfzqFdv8YRTOMfOUi6byMf//jV6Bs3YrjdJEPNDOx4jqLpi3F4cjJr7D/I+EInhvEiLW1zeOqp1Vx+uZmqqqpzfi2zvDsIIairq+PWW/+JcdH/JjGwGXNudiB7lixZTo9hGMTjcX7ykx+B1kwskeaJx17k3//lXr7+u9/R3LCNQ00xKgzB3UVF7BwYZIHJQpnFwjNtbVxTWIhZPjqFIW0YDAIzhTjvC8Gz5YxNLyaPh+jmzWgnFIHHJp/EXn6ZyJ4959Q7B6Ndv7m5SFYrltmzEadIS1HLyjC6utBOUB2PRbLbkQIB5JISBCBVVKCUlUE0TPxXv2R4205cublou3adsigFEIaBffbsI404emMj1NWhTJxIeuNG0sEgwuFAfxui/47rftY09GefRdux44wZ1m8Kmw2prg71ttsQa9fCli1w773Q0wE/+0FmzuLpkGS46VYoK4HuDtCSMGEaTJ4BHYfgYD2kotDSgEWSsXzo8wTQGN78CuG9rXy+xEUoJx9/bIgVr21g9vX3IZcEoHAjd1sUjP6JdBzYQP7YsYwpbQY5DyzV7Nu5n5BrFjJJ/EXl+GsXIFQ7f97wEeZ7CnjhoX+necRGTc18orF+6DmIY+pVpCWFXS//jlTJKu758MVYHaXn9lpmedfJycnh6ms/zsO/6Sd6MMacmiSZfPAsWbJkOZmdO3dyzz130dFSzxeuqmPjhn185rrpPPfSYzzxzB6cOuQbcKXDyp6REB0JneuLi1jd1UW+3U7ZCTn1e7u7cdts5J1Ffv35niF9mNNnSQPWigoiDQ1HbztV8sm5KpCEyCSnBAKYamoyXsVoFBIJ9K4utL17TyoYk2vXnn6reFSRU2bPRorHEW1tEI+jWCyweRNMn8z2XZsI1zexOFiAMTJC0mRCUtVTfhvQ1607bq0mux3WrkUJhVAvuggRDhPr7ka+8ELQtKMK5Jsd5D26fvPixW9L9/OR4lgIpIkTkRcvRjabYdkyKC+DObNg02uIl144/ZcBSc4c64qlUFsDXe2w9AbYtg7y8uClv0LJGBg3Hh76PwgOwN1fgMJiEOB2OVlSOw2jagrp3FIUKc71U67EWpgDlgIQEzFNCBJr+k/K9GFExYcg/SIwDDjwegOMKfkeDocDoVqAFA2NIfwFcVQ5wsVX3Mya+DP46yby0P98mdtuvBUKK1GC+5h6wWW8uGMNZlsdspwtJN6PaJrOcy8sp/pjl77bS8mSJct5jKZpvLDsWS6rTtBoCbBl+Q7m2wQ7VzZQf7AHlwa5wGK7DclspjWc4O6CQlpisSNd0cf64HXD4JBhUC5Jr+uPP1cZ0u8Ep1cYZRlZCMKbN2ce6Pfjv+aaUyefvEUOzzlUCwtRCwvRduxAe+kl0oeLxDeQxXySIvfyy+i7d4OmIV9zDaxaBX4/Qw2badq/gYWOIlKqgqYotPT2Ul5Q8Pp+SMPAaM3484QQ8MwzGLqOtaoKoaro+/ZBXR2y3098wwakvDyMSAT99bq5hcgoobm5x60/vXt3pmgTAlFWdlol9KywWJDnzEEYBhgGwu1GstuhtRW8Xrj9NkgnYdcOhM8HVy6FVSsyySnH+vwsloxn0eWESVMgNAwzboBD9RALgdUCV94CehpW/h0UE1x2M8xeCJHhTOa00GDGEg5uX0Xb2l9y8Ue+QLx/LYnUCB6xGITCQLCHnW1XctGFH0QxFYD5I0euVXFxMUKI4z6QlZUalRXPMhTcx/pN7Yy5MIBR2IrJJrDZdOjbCyWzILyfhQsWoyjqm7+WWc57HC5wuAGyr3OWLFlOJp1O87OffZ8pU+vYblzDmKGH+doH7mXz1t189uF1KGmYIcF0m41Ss5nuSJJPFJegAI+0tXHVMV3Rh+kNheiJRLj4dbqj4dxlSL8TnHFLOtncjJFKIWQZ/zXXYNI0Rn7+80zyyTlAWCzYL7vsyJzD1OrVxAcHX7+wOhWShDJlyukVudxc5ClT4Jln4IrLYd1LXHTZdTQ9/hRKaASX6qDY4aBjeJgS7xuY1WYYR1REY98+jH37jiiQxsGDWOfORQoESC5bhj5+PNLhbm5dJ93YiFJ9dJiwXFGBWlgIoRDGihWk9+1DOBwZ1dIwEOXlyIWF0Nb2xq/PYSwWRE9Pppll/HgwmTIq4bXXgsMKj/4OgsOItubMc4uEoCAXps06vmB0OGCgD0xekETGv7hvK5glImof9Su2MWP6LJAUqBwP0+ZD9RRIxyEdQ9vyHJRORQZUJC5cdCXxl77N0OBGvAtuoF96BVWdjRAyk6fcimIu5ERLsCSd/OGSZRlkB8gVdHY9wdKld9HVs50L74jzyrq/ssDiQbVawWJDVWxvrfjOcl4jhGBcTQll44qAbD54lixZjkfTNH76k+/x2INf4/brJ7JpbT1f+qd7+fnGHfxtxX7MaaiSYKLVxnyrhddG4txdWIRFktjX30/abKbKZjtpV1IHSgzjdbujDyM4/xte4ExzGE0mxKhPzlpbi62i4twVi6NZzK5bb0V0dr7lOYdSXh6mJUswjx2L/swzRxW5Y09ptyNaW6G0FJoa8OYWYV+8kEcef4oCq4U8WdAXilFod2Do+tl1XZ+OwwpkXh5SWxu0tWEeOxZRV5fxQfb2oh88iDpxIsrcuRjt7Zk1Ggb6smVI06cj1dQg19YilZcjduyAkRFoaYG//OXN+UXt9sw2scUCs2bCmPKMCtjaAhddAFocsWMTkd2rISZh10TmscFBGApCfw/kjha7Xn9mzmJVNfR0wu4t4HRkUl7ECH/buIVLq4oIb1iGY/pCGDMeoiFIJ+DAdrTtT7Oubz+ltgBlqUOUlhTBmPlIY6ZRGtuHJAYwPLUgGUhSDpDzhp+u3+/nc5/7Jooik0zW0J5ej7MQlMgWUGaDrICUx3vjY5rlzWA2m7nhxm8wMLQPl//dn3CQJUuW8wdN03j++eeZNrGLX/bJmF7cTrnL4IP//GuioSSeNEyVoM5q446iAh5p62Kpw4lXVYmnUiwPhVjgdGJRj9+90A2D/bEYMYvlvFcM3yinLRhlu51kUxPWqioC995L5Le/PTfFoqpiX7oU27RppDdsIPnCC2++gUOWUWpqsN56K6KlhfTPfgaDg6d/vKLA7FmIxx+GW2/n4KYXsJDCL4GYPImS3bsxQkma+vspzMl5/Vb4w15AVUWaPfv4N4fbjTxlCqK5+ejzMwzo7CS1ahXSZZchL16M6O2FkhK0WBQZgbx0KWLvXtizJ6NcPv88vNnrLstQUAC1tTBtKiRimRE83R2IdSshHIKPfBzWLs/MTfQ6MU2ZQHJgkObXtlBQORGzz5/pep40LfPzoeFMgsvm1Zk5i6kk3PkxWPEULLkWNj7DTWUO/rzpVSYH+5lSPQGpeTeMmwpWGyTdUHMxZZKLklwb9d0bUDs7Ka+YRceu31M883ZQJiPJJW9Z/VNHP8iB/DEE8n4NRhsNy/+K2tXLmAlzAYlswfj+RQhBb28/gi4qKt/t1WTJkuV8QdM0Hn30EZoPrsHrreWmaTksrM7nlw9sxZVKUqBD0WixeHdxISvbOyk3WRnn8wEwGIvhjMcZU3pyw6RuGAwFg9QVFJz3nsQ3yunnMI6MkGxrw3/ttUcaXN4qptpanNddh6RpxI7Jan4zHJlHWF6O9uij6Pv3v75CWVIChw5k/uzzMbSrhcvGBQjLbqSWBtwzpxHbsJkc3YxktWYKrhObTGw2xKhSd9gLKIqLkTo7Mz7Aw4TD8MADmaHXJ/y86aabYPYsCA3B5Imk+w7y6qe/xvQpM3BpGrR3wfwLYdWrmcaSN0peHvj9MG8elJfArh2w8kVE/Z6jnlCvDz76Sdi/E5Yvgy//BxQVMiyP8Isf/4jS5hauTScxV00CvxfWvQz7dx19vdLpzNquvQ162mFcHZhUcOTQvqOJSw2NnIsuQNRNwnD6Sa39K6aeiVA5FXnqYkqrppHqa6Trhee48MOf5vlXfk4gr4BSazWCU49RerMIIUDOZ+fObn796At8+vYBiHrBM/ecnSPLeYxQstaDLFmyABnP4v0/+z4//uH3GFPgYtq4l/FM6ufG33VTmjYISOAVEDDb+FR5KS2xGC3xFHcXBJCEwDAM9sVimK1WTicpWXh/zmQ4vYfRMLDV1SHr+rkpFuvq8Nx7L8lHHiHxFrurhc2G/ZOfzMwjvP/+s5pHKAoKEONrIdgLd90LA73MvuE2DqzJoeORhxly5TGprwP7ZYuxNTWjObykGw4gLrook4cNmY7i6dORYhmlTnR2Qn19pgBrbT1z8StJYLPBxz4GZgX+/AeE1Qo1Nax64VnoaSG6fACHP4CSnw+b1mLk+aB/AMaNg0OHMkrl6dRYScpE8F1+GUyZAr3d0NmGeOkZ6DuhaJVluPo6aD2QKRYXXwmFRbB5Jb2H9jDNZyEuayRKx0BsGOq3QsM+MFmOZnyrJlh8FcxZAG1NYDHD3q2QW8jYK26G7gMweTbpsTV0PfV9cpo3YUxcgO52s+LRX3LR4qWoNRex6EPfRaePGfkNBKZOQ4i3T/Uzmcx89JNfp7P+V5RFWlA92WHO73dqa2uBYjjtP+1ZsmT5R+Kvf32M3/3uP8nPSTOzejbbXnuWyKAgJ2xQIMFFPi/dA0PcmOMnnkjwXGsnV+UXHsmC1g2D4cFBJhYWIp9mF/K9kAv9Zjhj04tr/nzCv/71Wy7uzHV1uO64g9RTT6H39x9t/BjlrOcXCoFcVYX11luRdu9Ge/ppRHExorwcIFM4nuI4oq4O9StfAbsVkY7Dr38It9+NXlJB77YVjJk9k7xAGcxaABdfir7zNbZ8+pPULrwO54ILM9u4++rBaocDBzLbxYebXZqbX18llWW4/HKYOwe2b0a8OJr5fNNt4HKz4LpbSWxdSWpE51BfmHGRCEQjiGAQw+uFyACUl8JgMNOt3N8PkUimeCstzfy/bjzMng2th+APDyB6u0/dXW6zQ20d1I6Hta/Axz+X+V26ZxvIJmrGT8QSP8DLWw2UQ9uheCxEdCgrg0mzISc/0+Tiy4Xxk6GlAdoaYM0yWHAlVNRAOAhl49BjPbz0+29hTuwhIQcpjnTQ3uqhsMBER/06ym0mKJuKJKIUuG4D2cXbuUVcU1PLunVB4m4LSs7st+08Wc4fcnLenAc2S5Ys7y/q6+tpb2/n/p/9nERPCA14ouFFXAmYrBowWix2RBNM9OdS5nLQPBKiPJmm0mI9chyDzMxE62nOMxiLsV9RmKKcsbx6T3L6phebDb27m+TevQirFcusWUd+lRvJJLFNm0b/cvqkE2Gz4f30p1EDAYzubswLFx5Vyg4fq6fn5PmFun5yoaOqmK+6CsvFFyOCQfRQCOVDHzraNdzTQ6q7GxYsQBz7QuXmotx0EyI/gHjxSajfCROmQHUdksPGjC//HyaTQN+3Bc2IwHAPD/zsB4QGR6iKDyH+/hDE0hjxOMxbAB4nzB9Ngtm5HcJ+qK6B9esztx07d/Gwh/CKK6CsGPHEI3CgERQVrrwarroGIz5C4+aX2BSJ09fexz+5vZnmkAsvBknKXHNJAo8XIxqD3Fx48mmYNQvGjoXxtdDbAx43or0N2ltgXFXmvj27YXiII0WY2Qwf/2coLMg0wUycAq++kCkir7weLFak+g30t/ThsEscjMbwDPUg1UyDqfMz80kKS8GXh3FwLzz6M4QnJ9PQctFVsPAacHkgMpTxi762hkk2F3YpiWXmpVg8grFuneVdA5SGtwAXgG6AogEREK+fB/5WqaqqYvnya0kLOPXEzSxZsmTJ8n4hmUzy2muv8bnPfIpyawf7GoYwx8CqQY5IcU+hl4MDQ0z1+iAWpdJs49JcP4YB2/sH8Xh9x3kRDw4OMmCx4LGeumRsCwapPcP972XOPFZn505sl16KddYsjEOHjiSqpJuasM2YgWniRBKbN5NoaUEPBo8r8uTcXLxf+ALm8nKMnTvRt23LjJ050dNntWJbtAhRUIC+cydMnIgeDmOMzh0EkAIBLNddh1pbi2hogN27keH4rmGbDdPixTBzBhg6xOOZgu2CBZnkkcbdEOqHsaVw6U2QGEIkhjBPm4um9fLymruo7p3DQP12LHu24xE68d3bMp3ETh/ClwvPPZlR10YxDDKzCAcGMp7EwgJ4bQNYrZmxM7Nnw/RpmVmEO7fB9OmZ/yZNyzw2PEzolRfoeuFxntzbxgQMDioSU2fNBo8VorHRUTY6hAYQsgSxYYzrr4LpM0m1HkQ9tAcxEoL6QdB0kMn819cLFhWuvTvTEWw2Q1UNOOwZ7+Rzj8G6FVBSAVffCh4/dDXDlPnM8HupaWsg9MgvIb8AUvFMtN/4KVA0Blwugn/9ASQkfHXToKQcxk4Cfw7J7cvBV4TJm4d0wxcp6mmE7oXgFOBzgUhz4ZUfRQltALUDjBQIG8iT39Ib+WzJycnB4cjDoOAdOV+WLFmyZHnn0TSNVatW8dRf/8CmLbtIjeymsQ3yY5k9B5cCdwS8dMcSzM/N5ZJcH0/s3s9F+X5kIUgZOolojJo833ENreF4nGmKclJ39GEkMrnQ70fO6GG0L1qEtmMHyYcfRmtsBLMZ05w5WK6+GlNlJXpzM3R2olRWktJ1JLebZGMjpvHjcdx0EyanE2PzZoxdu9BXrjw++URVkSZNQl68GCkaheFhmDABk6KgHTiAPmYMoqgIzCbkigqkoWHYvRtWLIfVazLH0LRMUThlClx6KSSi0N6C2LwB/Dmw+DLYuQka6mHCRNDi4PNANAhlVYABWhjseVRMqqTMcgHiqQeYlyfTrenYqmuhchz4/LBtS2agde0EOHgAhISorYO8AFyyBPp7MIaH4NorwOOD8gpIxhAdzbB/NzTuy1SYY6vB64bf/gjG1uC64cMsnDYD57/egT9uIJIKREdg5XOQMjIpKc1NkJcPCy+D8RMQJeUQ6WPPb77LRNWOkl8MweFME8pwEKrrYFwt3DIDejuhshryC0kFO2j6w/3UzLswc45rb4fZF2aOvfVVCJRAYQWS18fg5scI+bzkfewrdPz1AYry85EGOjNbzv3tDPfsgo4QvvwATJwHvhy0nmbaNjxJaXkNXHI3hHrQRBzKpiBLYXAXgMWOihusCzG0PkitQSRjwCVv37v8BK688sp37FxZsmTJkuWdI5lMsnv3bn5+/094ZfnTmNKDuNJ29JFMWotXFsxUDar8fiqtZgYGR1hSXsqhgUGGFCteW0YZPDQwyJBqPeJdPIwgo8mcS94rnsfTRwMqCuk1a9D27kVvb0fy+7F95jMouo5+6BDp73wHqaoK+z33IHQdQ5ZJBQeRbrwBxWZFamyE3XtBUZCHhmD2bKSCAtLbtiEKClBuvBHJ64GHH4KWtowvb/x4uGA+SkVZ5lWZNYfU6hfRvv45pCmzM2NZutrg2msgNAJtHZlO4KuWwmuvIla+nCkg/Tlw7z9Bbxf8+UH44H0wdwEkL4ToEIx0gkiCuwgESEiMu3wNpNMMvvYSfx1qYUpuHq48F4yrzGz5yjrcfXdmO3npUrBYobgYmvbDy0+BJCOmzYLJ06GknOjmlfT+7zcpLRuHVD0JFi4BuxMmTIamfbDwCpgwFbQYUkU1M+/7OHS2MLxhD8yeB5IK+cXQ0QbX3QIuByAyxavLTTLSSTcpJsQGYfzlUD4OYrGMsllWCa0HYdnjUDMJysaArjG0/CFc256DgCuzvT59AYQGSK54DLW0GlFZA92NYHdR+oF/h7lbeHXdS3Ru2chtDgv6ktsYam/CVzMJ71X30rfiefS6qYTDAzhjIaSicZR/4GtI4SZiT34d88X3sWfXn/G5/BRPvwEsXsCbUU1VGyiFRFK5YArhOOcfwSxZsmTJ8o/CYUXx979/kGXL/sYkVxT6DewGoIe52mchNxVnQVE+DR3dzHKYWdfWzfzCAhRJIhyLM9OkYB1VDsOxODPl0yuJ54rBeJxGRWHSe8DzeNoVFo2vxRLIQUypI9nSguTzoea5EV29iOICtOuvQpk6FQ0DZTiE5HJhjC1BEpBu2otpeAjy3aRVG8qMyRiSQESiqFNqELk5YFbRug4ixhQi5s3A6AtC3TgkVUL0BtHCMYy+duJmBXX+XHSLE9XvR554JylZQolFSVsvAI8LvfsQJpcFLl2IFiggpSpI4QFUE6SuvhpTvh869oMiZ+Lv3LWZokVIIEAgQFgJJUL8freFCy+9kvlVE0h4clDNKgT8UHcH2O3oDjv6UB+maIJ0VzOKVSY9awbKmHEIiwkt3A/dGppFoF92BdrkWSR1UF12UqkU5sEusJlIub2IwXYUswCbi4EJMzGSPehXXUvK70OxucHtJ12cDyYF0dKIcPnQgzqqSKIFuym99Aqk3GIor0Kz2kkrYOpog8EO0qqBMn8ByeIClN4WZFUhZ9ZionYvlNai5buhtxnZrJBSNFQpBb2HIBXLeBm9tSRsbsq3PML4j34SvWw8SZsFOuoxAl6SfjNGZRVaYRHa3vUw1I7Qw8hmFcPpziipIk5xoA7flKvB7ALcx403EUKAcAOm7NiTLFmyZMly1kQiEe6//6ekk0kADh48yKplT6FGBimzgDIId/ugNQoTTVBlh0NxG6ZUgmHZitdmw4NBjiSOuNlP7Hl+J2YrdASDVJvNeCzn/yCe0xaMWzY+hdtukE7sQbeBknbR9uxXKRxzGbpws7NrPVMKIrzw3C+YN+9SPKlSVv/9BcrHVPHS8hXcefud6KmNrN+QYJ5rCQ/98ZcsWngZbj8Y3XHCoR20twYpLpkOrTlEo2akld+lsrIEQ55HU7NBuul5XllVz8ULali17CUuWziLyppprFn9EvPmL2HNyhdJpRvo6ZK59da7UcwmmrasZtlLT1JRNpYli5eyctWLXKpdC3IJQjKTmyNRXjeLTXu7AJg9e3YmTg5YseIVulLttOXV8vnnf42W1rhk4SWYTApal4phJOjuWk1LR4Jbbr6HVSteYeHFU1n92gouVG9EUeppOuBCkiCFG0MPI297ilUrnmXBgiW8uOIl/umef0YIMysffR5Zkliy5GqKcwr59P/+gsjAFhyqxKQ6GwsvuoN0xMOa1csQchqbTSY3x0Jzc5RFi66n6WAXz77wDB/7yB2ogzs50HiQ7TuWce11n4RoJ+vWvsiFF1zGn77zZS5acBXl5RNJpdOs3LOZKwpL2b1yL6Sj1NZM5flVK1m0yIza14uqOFAGB5lYk+RbP3uaXZsOkuf8PbNm3MTKVU9w332fwbJ5K//3m/tZcuFC1j27Ek1PUC36EFI/iklgmA3CkXFYmoaR5ADVfXvQ7DM50LTxuOsN4HA4aGjopLLSd9ztWbJkyZIly+lYseIVtOReRCSJkoQaW5IpUxTMjhk4oiHyepvpGk4w2aqiJlMY8ThLvDmEEkmWeGxY1PND0ROA691exFkiDOPUM2Huv+MOzOvW0dDayiSvFwswHAwyYhgYQjChoIDhoSGC0SgRQAWiMrRJ0GSAS4eLPV4KgKZgkFYDugUkZEGtAXUYDEmwTgePkFnicjM8HGRINkgIiTqPC9lhZuNQLy+MGMy0WpgqoDUWp8/InMMpCeYD47w21oQi7E4LSkaHbk5xmDigG7QnUlw1ppCoHEb36FidUVqLp7CvoYtrP/RB5t76LSRJJp1OEw6HuW7xhZjb9xGLZ4yr002wPwkTFOjWYIwKa1JgaDBLhagBe9OCSgWqFIMggpkTa9iuhfnzljZmq5lrEwL8ElSboE2DsWYomDmNfZE99AeTbBhycah3GFWDfDlz7jwFzKOGic1RKJah0iRYMHU89R4Pv1+2FrM+ersKmgxrI1CrZFr/g7pgggVqzAapQD4lN3+Iz/7PD8nRk5Ra4KryQoI+Dw+s30upArdMLid3wTxeWfYw0ycV8fP1Bo1NneSZYEpJPjkeg67uMPctncFPNq1hz16de6bk8aedPcx0QIkLxk6sYn08yXOrmplWbOfer/wXZvlh9jZ6qJpcxLxLfoKi2IBM6PratWv51a++yq9+9RwWi+3tf8dnyZIlS5b3HYaWhpZ1EN7IcEsLbGphRPbh7Giha/MmfHYnatognBbYNR1UK1sPtjGpoJB8l4utre2QgGklxQBH/j61uPi482xrbydhGEw54fZj748BkwsLz2rdu7u6cFks+FyucxIlqOk6Lx88SEVeHnl2+5s6xuSmplPeftoSe/aECZT29jKg6/gPD6x0Omnp7yeUTjPZZGLQ7ebvsRjNhsEQkNBgkmpmfDxBBRAODiEBsgHTnE7GCEFNfj4ycKi7m0QsykSXkyqHg7Suo5nN7O3uRtisHAgOUxBRadcMytIwKRbHCViEwlVWGyVWK+VeL4qm0dTbiaFY+YDPjtdux2e1koqF8Y8M88kxpVhy84i6zHTletna08mGJ9fzkQl2pjT8mZd+b8ZeUMV//fA3dDbvp7mli0IVAgKcOuRpUKNCfQosGvQbCostNqok8KWi7NIM7ikPYJKgYzCIsFhIt7ZTPxRhHGDTYIHTSr5JRQK8kk4YjZCsUhzTUUac1Fbn8fjT+0gkocSkcJvPRoGRYuNQjKikUGKxcZU7RczQmZHvwZyOo/e0oeuQb8BsCXqSYDJb+VevSmE6yu6UwcUlAcyGTiQ0SEFJEc0eN6oOiy2QNsDc1UVfRzdLTDDWAgv8HratfpqlRRX0KcX0tqzma7UyT3UaHGztwR+EL1fYKBI6Q61gJAxW7erl3hyVSX6VMTNmoi24mr//x7eZ7DTx1Y9+hI0Nr/LYozv52Y++yaYDLzMUfIXOrjLMZjP19fV85CP38aUvFKKq2S3pLFmyZMny5hCyAmMWYGizkfNexT4njrbnEcLbC6j2LYHWNjr6RiiUJNbvO8j4gIWqXC9mAToGAnCbTEePd8Lf3y5qAwH2d3WhyTJ5Dsfbfr63wuk1WYuF1ngccyxGWyxGkdeLrqpYXC68isITXV0MJBJsBiokicV5eeQLgc9sZiSRQI7HscdiWGSZYrOZMTk5yJJ0ZJ5RTVERw7EYRirFjs5OYppGGqgHdkejTAH0VIqJCIoAkTIYY7dTqSjUFRZyWBiVVZWAL4+8dIqdvd14dUFzOExPJIqsaYR6+rEuWIRjyRXIe9bwx4ee5Ea3ifzJl/KHl5cxYcKfOVRVS8v+1WghDZ8MiTTYJPhsoZ9Su4WGvgH2J+Jc4POz0O3CN9pFFYzGqNDS9I+EeWEgSJcBfVqcWgMmCyiRIaGaucjv5WA4jG7AUBpckkSR1448eQYX+RcSu2AxFa/ciG7Euc9vpcpp4ZneFLVeLwu9Lrw2K/FUCj2d5mBwkEKTFRkDNChWwGII7ijOZ6zDjkVVGIrGqDR09o2E2TUQRAEsm/fRsvm/uMBIkqsJTBYL43N8OAcHGe9XGRA6mtXH5LF5+GbP4derXiaUgFc7dPpC8KmcTBN2jrcQfcxMhhMbsAuNxXaDOjPkWZ14Zl7GF3//VyJdQ/zbzFzKerfyp2fW85XrF+GwOJg9vZTfPvA/2JRBXllbyqF9axkbiDJ/+rfQNIl3akc6EomwcuUKli696p05YZYsWbJkeUcQshln/hIMPY1cZUZyNtPYKWFyrqJUdSOtXcHEwlw8qoIsHAwPjuB2OKnI8ZMeiRCMxvDarJm/D0cYisXe1pmKBtAWiTDO6XzbznGuOG3BqEUiFBUWMtDWhojH2d/VRVskAkCLEDhtNsarKrMsFsrdbpRjInKsqopht6Mnk8SGh9ElibWHDjEpL48kmQt0mJhh0KOqmDWNFiDP4eDrXi8+i+WIEVU3DLqCQey6js1kYvXBg0STSfwWCxPy84mlUkQMgx6h0NPVT49hYJBpfVciSaSHHsFyYDc/2LQNR1wj1+tizdMvMTa3kMmlM9i5bwOpIQ2rBte6waRBIAUuAa+F4rQkJG70+si1WkikUnQNpwDo0w3qO7oY0AxyrE5mSxLj8/NRR4vijmAQn0lmZ3cIq9BHIwYlPHYbHW39VDa3wAe/zt4DjVzl1hk04LXBEH/pDrFQBmcsztahoSPXKmxAi2awuSNEpQFfMKvkiRRdmkFTRzc9x4h0QR0GFCszza7M6MgUzATcXjcJmwoYeE0qpRVlKC4P/uQITJiOVFZGrHIiD3/le5SpkErKfLfGh0tPoaeSDHb3M/zb+5Gjycw2eBJ29KWIaFGGfvG/FHWOcEuJTCQuEUo4+MzHPovvmo/T3vwC//rZP5AKmfnobTX8ed8rXFOdxlU8GZ/Vyisv/5Urrrzjzb2L3yA/+ckPMYyD2YIxS5YsWd6nCEnBXbAEmz/Bzr3/x/yrfGxdn2DGh68ktnIf1pE4roZGXHkepGQMh9nMa8F2JuRlikOHxcxrLe1MyD15rM47oTyej5x+rI7VilBV3HY7hslEWpaPVMBVQK7dfsb9diEEstmMbrPRE4lQ6fPRG4vRNDTEsbbJGOD0ehnr81FHpmA88bgyUJqTg5ZO09TXR57djtXvx2UysaO7m8F4PHMcn49qv/+4/A4JEMkkiVXbuZQUOaWFFJpVrIsuIWCT2Owr5cn/ewRHGuYpsEiFDhnqE/Dj9gGKDJhlt7I6FMPXPYhPcEQlHUJifKCAXEk65brL/H7iqRTTVQtmkaY9Gac4xw8CFLMFuybT+59fYc2Bev7QmUTRIC8Nn8zzM8lmRZDx+SV0nbWd3dRrMNYQ3O31UWex4lJV1re3YjYM6nwehhMRivx+JCEQQuCxWY+MCADAgKFwiKFQmHyXieHBEJojgWxz4skrycy1rJ1J3GrFSBvYBRQosL4zSJ6eYlCDpBEhAcxWYEGunVzFoNjvxaYq6Bh8YnY57e2HKFp4Fc4P/ivYbAyj8qn/9yAd9UluXTiBsHksWnQLjoSVm5dcBkYXRQXT2b9/L9XV40/7njoX7Nu3jxeWPc3//Udl5oJk016yZMmS5X2JEAKT2cIVd36Z+NBGZtZWEmr/Ki//bge3XHQtutWC0nQIKsuQ9u5hYmEAjyHAMJCEYEJhgKHhMC6b7cjv/Qq/n1jk9MqjIkkUWywYhnFOPInnE6cvGFWVpKqilpWRbmp603vrTrsdp92OFI/jslgo9PtPeszhAuf1kBSFsfn5SKkUZk1DNpuZVFp6RLGUT3ccw0AknVxZVUHH/u24rWY8xWVohQVsfeox+iNwlUXhereVYDjE3hSsisJUPdO91DAUw0Lmz5VeLz6Hg5im4bXZsL/ONw2LqoKqMhyL4dAURCTGsAQumxVKy8mxSNx1/e3c/5HP4k8n+K/CPOpyMkXfYDTGtqERdvYHKbI6+LTPh89iPe56XVw5DoBwIkGuYiYcjmF32UmcajECXA4HLqcTxlTgigzReaCR4oEBuORyuOhSUGW+/rWvMRyJ4ZegxqRQbLPwan+Ei5xWvFKmzPKYTXgUHavdRNdImIKiANKcS8BmpWrmQsTcxdBzgIHQHu775q9Z9dpBFgYk7rqggh++upPLi8x84rab6OrcTIF/Btsa/sL8BTcDtbxdRZymaSxfvpzO9mbGVX39bTlHlvOPhoYGKisrs134WbL8A2PxzMIwDLYcvJKJNyrssMeRk2nyhY8CTyFSRzOe4QggEYuEwWQhAfRGIpQeE0fvMJtZ39JCdUFBJtXtBA57EtOyTOA9sM38RjhzX7kkEevvx+L3ow0MvKkTHC5sdIuFZDxOLBRCMZlwvwlPgDh8PJOJRCKBHInQl05T4vGceY6fEFBRgSivxG1S8TQ3QipNsmQMz728hjIZLlDhYBrWRGBDCi5EcIXfR/UxW+MCMAPbu7owyzIuYEdvL9LoVvSJHFvYua1WsFoZDocIRkKkXAnULVvwVJbTxquU6SkuUQRjvR4O9A/QFo2zIxKjVFK5Lr+IslMomADyqBXAbbViWCzIsRjB4RC9kQjjA37aDI1Cr3f0sSLzLUlIMBhEGuikNHe0gO/rg6EBNHsuUjSIF/inYi/N4TiNvcPcWVFCtfuELw2aRiwWotjjR4olMtc5UIzk8UF5HZGeg/z8f35AW30fLhnKTPDKmp3YWg+ypGYs1kgQUfVBlJIyrpk7gXgiwvPPP81ll12FJJ37CVh/+9sT/O53/0M6GUNY6s758bOcf9TX1/PDH36OH/3or8hytgs/S5Z/ZIQQFJaMoznVz8CuvzLN64b5t1IfDGLeupIykUbSDdrDwxS7Vdw2K5MKchHJJIbJdOR3sNdsxqWqp96jEoLWcJiq91mxCGcoGPVEgmRPD5LHQ/LAAeS3OO1cCIFmsdAfDFIqy8TicYQsv/kp6mYzLbEYhaqKlkgQMowzG1NNJsT4Otw7t4IvF1x+DFljIGXgTMOuBDhjMUZSMA/BV8eOxSbLJxVpumEw2+FAAO1DQ8QMgxKLhf2dnbSEQkceZ5FlpuTnn+TZjBgGW6MJ5OEQ80MxdgeH+EsshJzQkSV4cG8jKZOVebLCBwOF+K2244LPz4QQArfNhtNqpTQHOoaCeBWZzQcOEXDacTsdxDUts1UdHMrM4YnEwOWEglLobOW15n3sWb8GrwR/7hgiXzdYbAZzNEJQFsQ1DYEgz+VAkmWs/nyorISOQ2BxwJg6qJpI9+AAn/n81+nY3EeVGaZ6oM6mMD0a5sZZk1Eu+zBS9QzcusS2Lc/ha/8lv39mH1ddVMlAfyk5uZPPqZzf1dXF888/g5YQfPvj87AkWoBizn3IU5bzif7+foLBJo7/FGbJkuUflaqqairHjGF/4XgqxWYOqNXc+IHLuKggxMIhB7fW1FJpcyAFQyBUdJeDto5u3DY3bpsNWZKYkJ/Pju5uJpWWHhFt/hE4bcGYGhlBycsjvGYNIpnEIQSSqmbykN8kkhAU+3yk0mna+vspczrRR4uyN1McFHs8GLpO0+AgblnGUFV0STr9CxhPwJhxGJ2tiNxcdvzxFyiJJDf4/dSpMn/vHcRkwBV+3ymLxcPP4XABV+bzUebzoRsGaUWh9phvFBZZpi8S4UAwiDHahKMbBjHA4fUy0e9nazBIz55GAsD1gKqDz2xinttDRzhM+2CQqC1Bidd7tCv8LN6ch9dX4vURT6UocwvMAnY2d2KWJRyBHHZEwpT5vTh0gQhHkaw20uMm8tR/fY22oE6lJJhrlri4JJ9c+ejPx9Npyl1OFC1NZyTG+LGVSAcPINXWgc0JY2pIxEb4/Cc+xNrV25hthykWidvG5ZJnFZgnzkDc8CnSHj/hwV186Tu/pnXba9x1oUJR/iKGg23Ur/s8/sr/RDV7qKqqfiNvieMwDINUKkUsFuOuu+5kz55XSYVS6Ac7MbrnkHTWoFpy33c+kyxHMQwDDJ1swZglSxbICCuKakK1ejDKbuYPX/0md4xP8th2O1/63H1QkI/YugPWb4RwDBQLbpcDtw66riNJEqFUCqeicJox1vjMZlyK8r5zyZ+2YFR8Pix2O+F16zCsVvSKCpRQCH1w8C2fVFYUSgMBjFSK5t5ePHY7sqK8uW1qSaLA70doGj3DwxkFzeHAZbWerMx1dkLjQZg9HcZPZNdffk2uphNA5/n+MGUaTHA6mZ/7xooISYhTehUcVmum8ccw2B+PMxIMYgESQ0O8MjSEzWKh1m4/0n3uM5uZGAiwt78fVZaRhcAhSaw/eJDhY7rCR1KpN3ydYrrBGH8uEtAxEscpVNLDUVYM9FNoMVP23LN0HaznyZc3sMRh47+rSrAKgSxJDEVjxFJJKnNyjxxva0sX43P9dDQdxD15Kp6uDrjpbhjqYtOjP2XHhh1cYINbPDDWa6VjKMrgkMak+64iLkX46a+eYPffHsQk0nz7yulU5CZxzavgyc1u5lx3DcPJFZRU/hPw5j1o27Zt5fHHf8e2bQ0cOrSSIj8gQeX4JUie6by84jEWLbkPVTW/4euZ5fxH0zTWrl2Lx+FAlrJKcpYsWY5SVVVNQ0MDN93gYovnAzz8/65AKXdQv/G7DHtMuEwpxugxrGkDj93KYChMeDBKSU4OeU4niqbRFQxSkpNz3HGPVSAnlJS8rxTI03sYhQBdP/Ln5OAgye5uBlMpij2et3xiSQgMVcXidNIfjeKSJAbCYRRFocidyRyWz7Jok4QARUG3WDCl0/QOD5NOJFDMZpwWC7IkYfT2kuruxjRxIgwEMdpb6EqYGNZhef8wt/hyWBsMstTrPevznoim6xhA2jDY293NIcOgJxKhxDCIWSzUFRZybFqk22JhJB6n1u0GwKUo7OrtxaGqFNrtKGYzQlEozMmhEHCp6pGu8DeL22Rikt/PoXCYQ0NRUmkdj8dF28traFjxKrfLEh8JeGjq7EI3MuN5doZi1Gjp43I13WYTLtnE9t5eJu3ejVacT3LVM3xv+So279jNdEVnogUsBrQHo4xoMK8kj+Sqp/j77g30NvTzwSIzVZOmUHrnF0kpMnLJWG65qAj0Djym2xBCZu/evfzgB5/l3//95wQCxUiSRDqdxmzOFHm6rp9USIZCIX7zm1+zefMy2prWEgymifRoCBMsnehjysSJgIIQXYD+pq9llvObZDLJ888/zec+fgnqeRIDliVLlvOHqqoqUqlv4nQuo3LsJJ57/Je8/PQ+9u/q5kLVYFbEzBKvCfQEkiJT7FBA0xCyjGKx4IpEGI7FjhO7BDCSSjE0mnH9fuL0/4oaBoNr1iC73aQHBkgODdGZTFIw6hmUzW9dlRFC4LZacVoshBIJ+oeGqFBVNrW2kmez4XOMeu4slrPyOrqtVgzDwGG30xkM4tY0hsJh3HY7RKP4L70UgkHIz2doeITeYJBaofDFwiLWhMMUqeqRVJuzJZZKMRSLEQe2B4OYYzFaRr2E5ZLExWPGoI5uuZ/Ki3js2BsDmFBSAnB0/bp+RDEVQhzXFf5mONZ7abNaKTCZkA0Ds9lGWTLJRR4nu3tCiHQCIct4VJUPFBbis544i0rQHgxSbPXgECY6D3UQe/CPTIiGqFM0nGaFyQW5hJPRjJzvciKNqSXZ381Sk5mF4wMkA0UUz7mSA3tWsvHph7lwaj6Oqz9BY2SEabMSNDba+cMf/sDTT62g0P0hxtbMJ5Iq5uCh1Xzta78indZpbm4mEokgyzKapgHwne/8J5MqB5F1mRy3QsCjEEwmccpwVYWAzr1Qu5iFFyxGUd6aNzfL+Y3LDi6rBKTIhHRmyZIly1FU1UTVhKvYvn0H3/nRo0xyqqT64fm0xKWXz4eCIhiO4mlqxhgYhmgMyNQtQ6EQeSIzhufYxluLojDV7yecSJzVzqlZljG/B5TI0xaMhq5jqqlBHRxkZM0aAApdLjAMIokEqXg8o+CZzWfdlHE6JCFwWyy48/OJp9MEJAkzsKWrC7MkMcHnoyESQT/BL6BIErWBwCkLqAKvl5F4HCmVYnNHB7VeL4OvvorS0UFxdTWNI73s2bCJjxkKqwYHadE07iwsPKV8bBgG2jHnPqwg6obB5nQaZzyOidF5kh4PNZx6nuTrITjqUSzx+xmOxU6pmCpv8Y112Ht5mOFYDGsqhabrmCSFyS4/XcNBSnwuzC4HHDN78rjj+EePkdYokSSwqhQpsDscptLvJpJK47a58ZrNSBU10NmC5doPYRlsxyVLMHMR5Bag1m/nyo9OxFs6hv29++lp2McuSx7//C+/pb19I6ERnb07ernrTpUrb/lvvv3FOhT5SYaGFvLVr/4b08p2sKPFjcudpHxMDk279/L1e27gF4+vJ3ZghFkBWKNDvh3GF5ei5o2luzPIiy9uZfwEE253DlVVVW/pmmY5P5HQkQ0NGAGyXdJZsmQ5FRKrVq7g/33xYlJt26jz+pi19INMvnAO21/5E3XNfTQ0DFOdjCAsVqR4DEmxUpaTQzISoSUWo2R0GgmA12bjYDhM39DQ6xaMshBMysujLxo9tZXuPOKM+zQmv59kQ8PxNwpB2mymOx7Ho2kEw2G8djsui+WcNA9YFAWLw4FhGMy02RBC0DE8TOQU5tIii+W4BJpTcbjZZOXAAP39/Yy1WAjV17Ntx3a8adgtG4yPxbg2N5d4LEZXLHbSMQYMgz3d3bhG19B8WEEUgrkOB2XFxUic/TzJs+V0imlZTg7iHH4bcVut6BYLLoeDZCRC99AQlXl5yIaR6Q5zOfC4nKd37yoyYIV4AlvtVGZ43YT2bMMRjxIeDiGXj8XT2QrzFkNJJeTmgj8fysZBOkH53MUgyyBBnlunsbuD3/z6IYxUO6aUiQmFEt+9vYaffe9ZvrbIzKyxxSQG1/OD/3mEPdteoHmvwYcvkFmwqITlbQfQknH2bNxPsKOJlhCEI7CkUOb6cSp5/gCdLf3kl8vcdsl85DwfQi09Z9cyy/nD5s2b2bJ1J6nkAuDti/bKkiXLe59PfPJT6HqS4Gv347vrCjq2Pc/2/evRvD1s6jlEyaLZ7PzDk4j+ISYXFUMkxoguM5JK4ZMk4qnUcTuhsqIwxmQ66fYTEUKQAg4OD1Pg8515ROC7zBkLxlB9PcmeHqy5uaT6+o7cLglBkcdDKJFASqfpGhkhlUyims04TKZzUjiJYzyMJR5PZtbiCRiGQVqWGWO3n90xAZsksbmvjxFN51aHg2Ih2BqNsqK7G99oQXhihT8sBFWBAL7R26t5cwrim+HwdTisOOYIQWJ0/mSh240kSeekCyszn1FgWCwEVBWSSbDbM6MEdDCSCXTVhCSJU59PkaF2JqKiBPm1lXhqJ6If3I8cjeFpOQRjKqCoAsqqwOGCVAz6W9GsJp576acEOxNcf7Wdr37vJZav2M+nPnw9u+VDPPC1CTz3agurl+9koLGfq77xWcJJGSMVQWILwWEDYVH4+D0f58X9bfz6/vWMDBusf7WDayYX8NP97Ux0QcAEumHGmHc3Od5cjIJGhDyXH/7kSa6++uq3PWEmyztLfX09X/nKp7HJMRLxNOgHQZ76bi8rS5Ys5ymqagJM5F/0eWCEogvv4ZH//gz9bSu4c8lklu2PUh+OcpNsAl2nPRnDoakUeTw0dnURMJvhmMKw2ONhY3MzAb//dS11Bpy24/p84vRzGGMx4j09+BctIrl58ykf4zSbMcxm7DYb3cPDuDWNHV1dTMjJIT1aTL1ehOAbYSgeJ3FMh7AOHEgkCA4NcTaOSq/JxPjcXGr9fsyKgsdiYV9fHxU2GxU2G02Dg+TZ7fjt9iOewcOcq8LsrXBY2m4ZGsInSTR2dTEuL+8tz8g8lsPJNGmzGWncONz79kE6TTwUpj3ST2VhAHG6BgJdhzUroboGDu1DsjvwjBaiePKhqJxo1wEG+nsoGlOBYdb4/WOP8Z3v/5Rk1KBrQxUlpWV8dW6a5Sv/RuOuFPtf1tm8pQ97vof/+srXkXITdOzdz/d/8iR7NkeQdLBLsG39y4x0vkpFrkFLFMwDHex8FXwC9BQkkoKKwjG0r/kjv3l2OQsvmkfOfBP7djzAB+6YiWHUZsfrvE/QNI0NG1aT4+/DGQF/cjt0j4OibMGYJUuW10FIgIe9e7aycs3fcEWifP65NUiGgarpKE4FQYJilwOR0iGdYlwggBaLMRiN4rYdtb4E7HYs8L6JCTxtwTjS1UWe30+qry+jLh42dp6AILMHX+TxMJJIUGYy0R2L0Tw8TJHNhtA0OqPR4x5f5vXiOEWkngEE43E6hodPKs6GgT2JBMXpo926BhA3m6nOyzurgtGpKOzo62MklWKS10trNIrTbKbY7SaeTlOYk4MZGAiH0dJpemIxagOBjGJ6Fsd/pyj2eIinUuSaTKRGr1exz3du01FkmfTevZiSSTCbwWShWFYRkTiGw4pQTjGmpLEBLlkM7Qdg5oXQ0gDRCMgKTJuH8dJf6VjxOIOJCP75M1hr9lJSMZGxOR62Nw2w/lCc4oaXWJyn0xsWrJEM/ra6naW5Eh+4/mry/B52Nr7EKy8vZ+drYW4ogieT4BNpmp5cSU2hiStKC3iso514wiAYFnyk1MxkjyAvx42zoIK06uMTN3+YwMKb2b3uYW645lb6ezvIL9DJDvF+/zAwMMCmTb3MLgS7NoThXpyJCH0f/KOdJUuWt5+Vy19ithDYiubQ0LqZkZjGLU6VkqoxUFaFFByBxkOkogm6QhGMtIaqKJm4wNGRdF6Hg21dXcyw2884fUUAJWe5U/puctqC0ZSbizMnh96//528GTOIrVuHkU6f8WCu0c5pp8VCYNQA2tjfT/qYQtOlqoSiUTZ2dJzUxKIDTbJMpdnMiVMNBXBpbi6eE7qzBaduyDgdtYWFAISTSbqHhylXVTa2tZFnteJ3OHBaLDhsNvb39RGwWGh5i3Mi3y4OK4GDsRiSLKPHYowIce5Ms9EoOpCqqkIdGcEyMJCR2zUNwrFTF42SBIEAtDdBewuEQ5lva0KA0wkTZlG5bzOVcxch6iZzsd/Llq1/REoNclMR1Lc1U+gBS9pEcW4O2r5O/DJUmsAXGaRxy2rMw7u5YImZLVvglQ4ICLirAOYVBxhz891og8M8v+l+0OGTNR6qvFa8FgWrxwUTF+MdOwVMJnAkmXD9ndSEN2Dk3EBjY1O28eV9wq5dO3nmmQcoybERHEliOCfT3rwGX4UXuz3n9Q+QJUuWf3g+9sl/4c/OGC8+8d8sKPdzz6ypKHOvQSorRTRvhe4QREfo3tNEgdmK5LJCPE4ymcz8jhndXav1eF63WzrP4UDSNPb39TE+EHgHn+Ub47QFoxhV1Ux+P1p//xs66LEFS01u7kn3jyQSlJ3idoA6RTlSeL4dHF6by2zGlZdHQtPwC4ECx3kxxwcCGIbBSDx+ZE7kcDRK0WghfD5sUcPRjOq+cJhQJJLpprZYzhyTeLYIgR4Movv9SENDmWJRHm1wOVXRqKpQVJIJ4x6JwvyLYfkzsOhKyCtAHNqDWPohuOwWSIygpUMcWLsWX9LguV4oV8APPNKcZDDVybUusMlQ4fIS2raZkDOHwpvv5VdPvsCOzi70NBRaYSQBrV0jlPQN8vyaNYwkYZwL9rQH6eoJcum4XKxV0zDGz2brwTZir/6c2Qu8rB3MoTPWj8//M3bv7uDTn/nfUR9Llvcqmqbx1FN/JxyKkGMGvwrWRC921Y7Ndv5/g8+SJcv5gWHA3594kQP7LXzjwV+iVuwgtncQ05hZNDdvp3PfBjqGU5SIJAFJpT04QLHXjxSN0hKNUuL14rXZaI1GEYkExhkag4UQdEQinFmSe/d53Wm2sY4O0i4XgRkzCK1ff05O6jKb39ai8I1glmVy7XYMOOLFLJdlYonMHMJj50SKRIL9XV2E02nq8vKQFeXNZ2GfY5x2O3abjc5gkPJ0+q1ndQMYBoYso/f3I3m9cPiLw+GiMZLAGFOKGBi9fdYc6O2EjlYoKQe7A8ZUwZQZsP01mDwTxlRjhHpobd7Av377FxgtXbjNEndMKmV/QzNrg5Bvs3JnnopXBrdZEHBZEYWleKfMoXV4hB0bdmEkodoCnyixEI/HSSYFK1dtomnTPu7JgQFDYarHxhiHCeIGRlqw59lv8uXvvcTthWkc1muZNGWAlzbAUPPv+O4Pv4mijJApWc+HrwJZ3gxNTU0cOrQeRQvT2Bxh5hTIcblw+QW6nkCWz59dgixZspy/yLLM3R9YxOJLvoW9YBGJeCkD0UF++d+fJn5gFzeVpumVPNw4KcC6tfsYY/YQSiYZSaUoVFUMXUdIEoVeLy29vYzEYsf5G9+LnH4Oo2Fk/tN17JWVxBobEZKEob8/kzGO9WImNI32gQFK7Hb6dZ1ijwe3xYJhNpOWZXIVheZgkCK7nT5dp8jtRrzLiuOx3dTJdJrWvj6K7Xb6DOOtdVP396ONHYsSDGa2nA+//rIMNZMh1M9xgZltLaAbRx8zcz7s3QZVdVA7FVSJUP8hPv8vn2PDwTjzHHDtnDnsCPcS3AMTTXCFS2JmWcY6QDIBqRRCFzBxAYMJ8CsgW+AyN9RHIBKFXT0RpnVv5yuFHvqiEcbmupFkKM71Ixk6DA1gTnj50SQH42rG0eXM5a/PbiPWqfO1z/8rdpedF15cyaJF12RVxvcw6XSazo4IsWFwmKAqR8WuyMjAjt07qKubj6JkU1+yZMlyZkwmE9fd+e0jf5fkOr79/fsw6eswIlb2pBWuumsOT2/YSrcwiMbDzBQKxV4vRiLBwf5+CnNykCQJr91OfzSK8wyWMUWSKLJYzusGmdP+y7l90ybspaUgBMEtW3CVlmIyDNLnIEv6fEeSZUpyc+kKhY5LthFCkOdwAFAaCNA1PIxXlmno7qbE6yWcTr9j43bOuH5FoSw/n87h4XPSTW0cOkSqrAxVCBgYOHpHUxNEwxhWGXEqxdhmh/GTYLAHZl6IZrKw/Inv8ov/+zFdbXGmmqBaht2rN7IupGE3QGggp2F7cwcDI2Esiszkqko8F98IuQFM21/k8nkTefyJflqjGr5EHNVs5atVLsbmeonEExTbLMhGGt2QEJIMDg9ceD1VqgBpBG79EkV5JXzoBgtSrA3VnAYpwKL5cZRTNfNkeU+gaRovvfg0pYVtDDZEsaiQo6SIdjdjciykri6KLJ//aQpZsmQ5v+jr62P16tW0dazj3g/k8+MvdrFnB/StfooBZy6dKQ1LMs2IbEKNmUEI3IqC0HWEoiCpKq5UilA8flovY20gwP6uLjRZPlJnnG+ctmB8raGB+TNmIIRA1zTiwSDmvDzE8DDGaATb+xkhxCmTbdyWTBr04VmU8XQav6qys7cXh6Igadrx3dXvQvEoRtd/qm7qQq8XSZLeWGOMrme8jHl5R72Mo7djsoCmY4ytOqpgCsCfCzPnoxkpKJ2DZFIYXv5TPv7p72COpZlkhglmGErCoahGnSSYZgGrgFgoRp7qQVNMjM0P4FEt0N9DfKCDhx56lF2NncgpDZMZxjosLKoowmvOvJXdNivCZoV0EqomgNUCg+1QUAGhHrj+c+DNQ7JaUM1mhBaC8Hq0nK8gSaZRAVU7KZ86y/lPJBKhvb2d8vJ57BOPkmcxKM3xYvOPw6h/jFd6y1m0KD+rIGfJkuWsSSaT/PT//pOtLz2IrzjOgw/KpONQLMHYnEJceQUc3N3DWJcDn9OB2wDMFlBVeoeHMaxWPFYrrZHIGb2MBtAWiTDOeWLL7xvDINNA/HZw2oJRVVXCe/didbtJ9vUhORyYi4tJHzr0Ni3lPOWYZJtyWcYYDR4/jEVRQFGoKyoCOKm7OgW4zzIL++3g2G5qIUm09PbitduRVPXsG2MMA5xORGlpRlU8FkmCqhpobYXqakhGoLoO/vmLaP3trF/xBCXTLiTYuoOv/Nf3sSY0fBJcleNEisfYocGHc+1cUJyPIh39EIXjCXLNFryyxNBICFdTA7Htq5g0eS5PLH+APKDAAq5EnK37M2vy2yzUluQTSqbJK6tAGugFlxvmLIWcAsgvgbxiMJkZ7tjCQM/j5NfeTkNnOQeefJxAoIAdO3Ywffp05s69+Ny8AFneMb73v99h5/o/0XIoTEDWuaAIOnpDlOsyau1SFlVFswpylixZ3hCqqnLvRz9LTmQrXbsbKTEXELbtwJTWeWJXO0Jr53K7mUkeK25ZASTikTC6aiamaZhTKXSL5ay8jOciUzoYj9Mky9S9Ddab0x4xEY9Dfj5Oi4WBvj6SAwP0rluHr6KC+IED53wh5zOH1cSUphGNxRhIJin2eo9T6Q5nQB/bXd0XidAfjVLn9dIYjVLq8eAY3bp9p7us3VYrhsXCSCyW6fpOpWiNRM5acTT6+0n19mKqqoL6+uPvbGqCSxdDbh7YzXD9rRCLQDxOydhp5MlhPv/dH9LSqyEbcHeZn6k+B480dfOhsgBVrpM/PA6LGSxmBkMhhmNR7IeasM2Yy8q1W0kkYV6OhStKfFiPWbbLpLLrYCcei4k8a0fGMzlpJtRMgdJqjPggsY7VpCyC73/nO9xys50//fm77Nm4kUvmzeSZTQXsWf80t131B443ZmY5nzEMg9WrV7NixWOU5wr27UlTFACvAmNzPFjTEcTBFaxs95FMNbJ06dXv9pKzZMnyHkEIQXtHB958wRVX3IMYeZEDPzHzWlOMGU6IpgUXu6yEJBnP5InoO/fQHhmm2JWDx+Ggb3gYh93+ul7Gc5Up3TE8TOXb1Fh8xhLU7PXCyAgAWjSKtbKSaHs7qt2Odob85vctssygJGFIEulYDPWENJjDCCGOdFcX+v10DA8jyTKRaJSN7e24TSbq8vIIpdNYFOXcjMA5C8RoBrbTaiUUjyMSibNXHKNRuOACCAZPvk+SwG4HTYclV2Vi/8wW5DHjKB1qY/e2Ncx0wiXVfna3DpIr6fyyvo0LfS52trbTxukjkQwDdAwODkUpbmwi2BHhg3aYZcQ52N510uNVIVGVk8PIYBhPIg4DvZBfRHvjctYue5kf/+p3jBFpfKYkjV0Kt3wql283DPCdHy7DY4V/vnEGOXnQ19eLEDI5Odm5fec7u3bt4uc//wFf/cLH+I+vfJVSG8zKAxXo6hkikDZhy53AxbkJDHf29cySJcsbY/bs2VhM3+G+j15NgS+IFNa5c1wJl1y4CNIGssMH1ZOQYhGMHD+V+5sRA8OYQzHSFgtdwSAlOTln9DKeq0xpASfNsT5XvCHNMtLcjLe6GqOt7bTJL+93nGYzmM3EkkmIxTCNTnU/FYe/IRzOwR6Kx6kMBHAqCjt7exlOJpns8zEYDhNKpSj3enGYzRlt65gs7XONNFrQvlHF0aivJ63rKDk5R0fsQMbT2NCIcdMNpLsP8PKyhzFHo8y/+S6+ev9P2blmNSVYqMxzsT85SGtzkMmKQAuGqCsoIOeYrWjN0EnpBns7uxHHvL9kI01HUuMOq4kJXjsHgkOUeX24bcd/6CyyTCgSwaFKUL8bfVwtT/zXl9i66yWe3JVguh0W+GQWz5tG8fwLqT/YzIF1T9LeLfGBj93Ogouq0IwwjY2vUVU1kWTShekUqURZzg/q6+v53vc+TXmZg3VbVjM4kMIDKDqUue3kOhQSw0NEnvkWjoX/xMNPvsjiJXdRVlb+Lq88S5Ys7xVkWcZqd/PJT93BUz//CU7vJC740ueR/C5Y/ijSNXdgJIZIJ9sQB7vprpYRG5IUxEYQsoxL0xiOxV7Xy3i+72u9oYLR0HWGW1rwl5djNDb+QzS/nA7DZCKaSsEZlMYT8Yw2zABHPI/hZJLeYBCnqh5RIA1grNuNz+EgrmkIeFu6r0+nOPocDrwOx0mFsBEMYpSXYwwNZdZyuKCTJLhkMXp+Hqv/5eP0JcJM8KvoC5dS/+Ir9A/E0CXBvrYQ+ULmn3LsLCjKRxmNTzIw6B0J0Zs2qO/oJqiB3+qg8phC0mMy4TRJRAyN4ViSsW4fFTY7CbMCsoz1GI+o225D6DokEwRfeZm87k6qh+Bb+WCzWZlV6MZntrB3pJWf/uJ56hS4ZfEYplaM8NyDv2R6n5nKcTcSCpWwefM2Lr/8pnN63bOcO/r7+0kHD1BcPYm/LtuJ3dBYWAi5ZgiHopgsuVSWT4bpS9nU+gyFPkFxsffdXnaWLFneY1RUjOEn39/JRRN93PFvv8AaKODAlieQS/Io1nYiCvP40Y8+y9y4h5pxS2lxBGh78CmUYQ2v1Y71LLyMFkVhss9HOJk80mB7PnHGglFPpxmprz86f88wwDAYPnjwH9LLeBKqekRpVKzWI93JZ8Nhz6PbYmFiQQFwVIEUgBnY0tVFQtMosduRNI2OwzYAIfBZrRS53cdt5spvsiv7RMXRDAwMDyObTDgtliNrRdfRhoaQPR7E0NDRAwgBqQTyvr0sWrgYnBaYdxHxltW4JY2kLLg54GZT9xA3eWxcWlqEjoGuG/RGIqzqGWRwJIIXwfhAAbmyRJ7TcZLKORSN4dZSiJROiceDIcu0d/VQ7HOBSzlS4EpCZGZAjp2MX4X5fg8Fe3fgNDnJ87mRaiZC5wHML7fxhdlVWMbUUHjpTbQGI9ww5QZa2xsINvQykPxfimo/SkPDfqqqqt/wdc3y9qFpGqFQiKeffpLZcxYSSfSjxzQ0HTrDsCDfjNcqmFbkRWrZCtY0UyfMYb+jgKamTqqq3O/2U8iSJct7CMMwaGzRufoD/4KtbAIGJipqxqPPGk+872/8/OdbWfN0Cv8kwQO719O1dxtVEbhZNqEkkzSPjDDL4Tijl9FjtTIYDtMbDB6pC84nzlgwxgcGMAcCWC0WYm1twFEvY2jfPlRZftP77O8XDJOJSCrFYG8vPrs9o8y9SY5VIA3DYOYx3z729/UdiQ2SAKsQbGxtZSSZBDLdVZPy8kjBkSLyjSqThxVHTdfpCwZxp9MMhcN4HI6jJtxgkGRPDyaLBclmg0gkkx9dXQ2Ne8Bshds/Ah43it/M4gnPEdizl93BYcwGlNutDEZjbAuOMDwwSAsKk6w2rhk7DkXKKI6nW63HZkU3LLjtTqRYDMMClQWFSOkkRGNgO8Ee0HoIaicgOV1UXlGJtGkF+PIhtwjKqxk78xLoPQQOJ/gClBfaweOlonQMmjNAeTrBijXPc+ll953V9cvyzqBpGn/842/48Y++hctsUFtlon5tMy29cGMJXJwvKHRZkY008dAIDsUGeZNZvnYjPYMH+eDdn3u3n0KWLFneY8iyzKc+fi9Lli4FKfN7NSim8YNvfIWG/evYu6mBL19eSI+cx1xpmHRuAFthDuGmZkp1BbfiIBSP47HZzuhlDKVSOM+TBLkTOWPSS/e+fUydNYvYxo3H3RduagJNI3fcOLTW1n/orWkAVBXV4UAG9GQS6Rx43k70MJ4YSB5PpwlIEodvNUsSfdEoB4eHMUa3ik9UJhVJojYQOFJQnk6RlCSJEr+f4VgMUzpN7/BwJqPabM4ojmYzek0N0uERO9XV0NoMQoKbPkA6PsIfv/4Fdu/ci7PtAEFJsCdu4DDgQCjKk50DlMoKM/IKWWgy4bWdfdOPJESmEjZZEbqGGFsNTQ2ZRJhoDJyOo2k0QkDdFIQiI7oPgWqGoSFoPQif/V9w+aC4EuIh2PoYxsylRDWB4ijDbCtEFipXLJ101mvL8s6QTCYpLS3lq1/+Ikbbr9nWmaAgB4xwJnt8OGbQ1j2EIkuUOMyZWCBXLgsvu5uunufQ9Fqk8zhNIUuWLOcfJpOJa2/4MMlk8ojXUNN0+voGaWxsRIkbJJLF3PLP/4/ff+c/uOMj1yMbI7z4H4eIDoXJT4GqaXhstjN6Gcu9XiLRKEPx+HEi0vnAaQvGtt5eHtd1Zlx++SnvNwUCxAcGsHg8pI9N//gHRAAuiwUdCMfj2A0D+W3OyrYoCpYT1EyX1ZrprjqGY5XJIouF/V1dtEUixymScGol0m21YhgGDrudzmAQt6YxFA5TlpNDevdujGgU1WrNWBWiUVi4iEGblXU/+ALffuApSnSDMhksikFDAsYAm3pCVAvIUwx8QpBIpegaTmFR1TdUOCIJGDs+M//RZs/c5vFCwA/No1aJ3AAUl8Gh/Ud/rqwKbroPSDO07W+4dA2peh5UzSPZt4sffP9H3PqRf6Gq7mRVsaGhgcrKyuxQ73eR9vZ2/v3fv4pdbeLlV9vxO7uJ9CYYHIBqGxSbwCODRZKZXJ6Pw2WHYB+R3U/z1z2NVMwr5NVV3+D2O/8D1XR+/WOcJUuW85u+vj5efOYHXHjJP9HS0s4LL7zAY489zzc+ezND+xvJLS3k3z73z6j9bdwfi1JR5GJDKEptAorMNsY4HMRTmd93p/MyOsxmNra3U3mCSHQ+cEaFsba0FFmII4rVsSQHBjBVVmIc62XLgm42E0kkcAhxTpTGN0JGfTu+6DtWmTQMA02WGed0HqdIFttsGSUyGj3ijWRU4Tw2o3o4FiNHCBKRCH3pNIUOByIWQ0mnwONFs1l4+PN386MnVzNVgkUK7EpDZwIuRDBRSEzJzydHCCyyzNBwmMZgECFgal4uTSMjjCRTVOT4R72Tr6MAHWyC2hroOjQaBi5DXz/4cmCwH8bWwr6dEB7OvNMtNrj6g1BaycBfvk/3nmU47/1XIo1/wOqsQrG4uO+fvkNBVQ2allEpZVnGMAx27tzJz370BX78s78jW9/bAfLvVZLJJN/4xldJJh5nuC3F7ZdM5eHH23CkDYrN8KFaHwsKLQjAosq4VQG6Bs4AtPUz3web1m7mosXXsXzFC1x22bXv9lPKkiXLewghBNOmT+NH/3E348Y5+e0Dr+Cwmpg2Zzb/8/hzPPn0ZqZaYYJNcG3Ax3OtTRQUuDCaRijxemkYGKBQCCyqesTL2BuJnORldJtMOM/DzPszrsgAhhoaSITDRxtfjiHS3EwkGsVXWIgxMoKhv12BNO8hhEC3WN4xpfGNcGwWNhyvSO7v60Mn443c1NpKns2Gf9S7eFh5POy1aBkaymRU9/Yyzu/H2LUbyetg/Qu/5Y9PreYai4UZyThNCfBJCjc47FyYf7Qr+sj5bTZK/LkAhBMJRgYGcJtMaCNROoZDOJ12kKTTK4+6BoeaYe5c2LAKhoJQVg7hINgdMGEqvPYiuH3g8cM9X4TyamhvwrvkDryXf4CewdUY6VZaQp3kxvIpmH8j8ZSFX/7qB0yePJnCwjEEg4N8/vMfp9S0DyO5nn0thYwbNy6rNL6D9PX1kUqluPTSQpY9k8OFSy5hx65tXFBoIl82UJJgTsfY0xw84oH1281MKPIgoWC/8hOMsZoplRMo7nEUsvGM58uSJUuWE8nJyeEPvzuANb2LlnX5TLXE+eqnLmfNuuV0Ng9TqcAlNpid78GLzFD5Qtas+jO3jv58vt2OGdANA0kIkkB/JEKR33+0aVOSqMvLY2dvL3VFRcf9zny3OXMJaxjoJhOeuXMJvvjiSQWhoWmYCgoQbjf68PDbuc73HO+m0ni2HKtIHlYiD3sjzUDvyAjJZPKkbOzjMqpTKdrq6+k6dICO/j6+ZbfTo2l0axLzPF6mOF14TzMQ/Njzu61WphQXAzAci+FMp9jd0kmt30drOEKh34ckiZPnQ+bkwEBf5s+alvEoej2Z21UFmvbB9R+CGfPBaoNEFFxepNJxGLFhEu15wAW4CsyYVJX1G9t44rFv8fKK1Vx/3SxKKpw88odDRIeCuHLjdK37Hqn8z5JMlmF9hwau/6MTiUT43ve+xpzZ09i8OUlOnpui2lb+9Jtd3DixiFW7unFEEnjSMNYGqiwxvjQfj9VEZ3CI4lQarfMVtLLdSP4vEkvoLF+RYOnSrI8xS5Ysb4xLL78SbbaNFY//HWP7QR743XL6h4epM0OpDEYa/nwoSGi4kfXtqynSYLzVjMdkIqWqR5NfZBmP1coEn4/OkRGKR+c1CyCUTjM82tB6PvG6mqfJZst0Q5+G5NAQfX19uKxWzBbLP2YCzKk4j5XGM3HYG2kYBg6b7aRsbFlRcFutx2VUKyYTUyWFcZ4cGgcHKTOZWJAbIM/hOG4+4tmS8U5amGt3EE4ksCfidHT24HY58Licx082bWuDaASmz4ENazIqoyzDzXfCnk1w1e1wxU0QC4NkQMdBqFsAigypYVIDDew0crCNdPDQn3+N2TKOja9to6rIyw2XfZS/LFtDc9MLKEnBxRfXYam8m1/+6g/MmrCK6+78Jk1NTVRVVZ27FyDLcbS1tfH973+TNatW40g8yu4dRXzy0+P47teeJ9yb5rnVrTiEwWVlTmodmTdGqddOJBYjrcqUV09ERIZp/OPDWOdM5fngB2lryWHS+ACGcSlCZH2MWbJkOXuWLVvG3t0P8fFr7uSmxWPwtEV57ud/47V4ApeiYJLAk06jdXVxl91Cvkgwpzifve3dTCkuI5VIsK+nhwmFhVhUlT7DwCdJR7yNcP7OY3xdrVPX9TN3Qes6ksmEOnYsRjp9+sf9g6KbzUTSafTz8NvCmRCj28fjAwF8LhcWp5P+eJx4LIaRTqOP+lrdVitet5ugYTCQSFCTk0O+1Up7MEgqEmFHezu7OjvRdP24//TXSQk6fP7M8T24bW7cuszg0BBD0RiaPvrzhg51kzKWCcjYJmongcsN138Yrrkd9BRp2eChh37Jd7/2L7S++AC7lv2GB7//Jb742xX89dtfJ7RvG7OcMRZM0fnYlfkMDEs8+4ff8vwffouIwDibQblZ8MgP/g13xzNcsaActAFg79v3IvyDk0wmuf/+7+LQ/o5ItrJxbZKvf/7j7N8bpaM1gVcBn2xw+zg/N9cVMb6kIPO+Uc04bE5csoQ02ImQVKpu+yJOw07egYN0N+7gyivvRpLicIZYyixZsmQ5kWuuuYb7f/E7OqRihorb0Rd/mAOSikuGRX4HXZrERQEvtYpgSa4PkxCMJFMMJRIIoCsWo8BqPfI7sNDt5sDgIMPx+JFzeKxWYrpO+3nWI3JGhbG7v59Na9dSoyiY3W5Sp+mGNnSdwb17UeNx7BYLIuvtOsp7VGk8lmOzsYWu0zMyQlzTKPP7EaOFmtNux2mzIcfjOMxmHHY7XYkEMcOgxGJhf2cnLaHQkeNVe714R5NsjsVyikzrw/MhdV0nPBjFntDoGA5RGsjNdEt3d2UaX2z2jNro90NhCYgU8f5mUAzWPfsnfvzDX9M7rOM1HmB9Xze1Ikl/n8FHqtx0vrKWgqIKptdewZdWP09oYAeEUuR4BM44fGFuKTNv/DxTKmZgWFzsO9jIlAoXXu8c1q1bx+zZs7OexnOMqqp88pNf5LW1MzGbfsFVU+aypa2B+3/+Crdd6OW1tYPUeiwQj7J8+36aY4JKv5eSSJT63iATSvIQ0Tgesw1yy3BXTuaaxXdxhd2EyTMeTZNpamrMKsRZsmQ5a6qqaunr6+OPf/gmfkcPe/Z/kYV6mPFW0CWDawNu4gmNcosJu0llan4u4ZTGtLxcZENnfH4+rT09hI7pjg7Y7VjINKYetskoikKFqhJPp7GcJw0wZ1zF/vZ2Jl10EZbcXBzpNINr1576gaOVctJkwuz1YlPV0xaX/6gc9jSm4nEUs/m8kpnPlsMJKrrFkumWjkbpS6Uo8Xozb3Ih0CwW2gYHccsypW43ZT4fumGQVhRqnZlI9CNJNh0dxxWMkhBMy8nh4MgIw6nMwB+fzUaJNxPlJkkSJTk5DMdiFEsCIxIDhxXR2ACLFkFLAxSXwqLLYdcGCPehzb6A2Jqf8fJTT5OK6tSYoLmhhbGKQFfgUjOke4aZV+xl4gWL+eMTf2PXa/tJp2HZi+uotMPiCoVZBX7Sax/GlA8Pv7yFyspL2bptK1/+8jeYU9WOzfIAFpuHmprx7/TL8r5C0zSSyST79u3j2WefYs2aV+nqWM8nPlnAd3/5KP197UyU4ZEVg7iBzqE4rgSMs0KlSXBxWQ7D0TgTCq2EQhEcJgkjHkfTB3l178usXH2AW26ppM5dxrPPrmbs2PNvdEWWLFnOb/bt20fHgW0sWjKe559+gZVmsKuCC5wWbCkdS04R5aUK1pEIrYkkrvwivGYztHYiCRWvw4EVSOs6siThdTjY1tXFDLv9yPzlIrebja2tBCTppBF67xZnLBiFEATcbmw5OYientc9mKSqSB4P8aYm5PN0Uvm7hhCkzWa643HKZRlD096zSuzhYrd1aIh8VUVPJJBGlVMhSRT4/QhNIxmPZxJhFIXAaLF4GN0wmH3Ch0AAoXicvmAQY7SLzCFJbDh0iPzDKTqSdFSBTCYgHGNIlXG1NCNFY7D0+kziS/UU0nkBfv2zH7HygUcJJjT0NHgtMnNdJkQihkAwpyKArAqcN36Y1CXX8dL9v8dIQqUFKkzwwXG5zLtgDtLSjyC7c8BlY1zlFmTTbu666xeIZJCFn/kpLz39BW6+83+P+4aY5Y2haRq/+90DPPTQ79m+dRfTKmMYhsa3bvaTchSzuXEVeenMfPgxCowxQZlVYaLPhleBUo+dprYuwokU08aWZDLFzRZQ7ez6xmfoECq33r6Q2JCFZU/cQW/fAi6//LJ3+2lnyZLlPUZOTg6f/3gVbVtfY64NJvmd3D59IiXjp2F48hFDw4j9+6CwnGJvHsJqQ6xZjVFR8f/ZO+/wOOprDb8zs71pm3q3qnvvBjfAdEKvSYCQSrhJyE3PTUhCEpJQkhBK6C30XkzHDTfc5CpbkmX1ru19d2buHysbV7ABY0P2fR494NVqp2g1e+b7nfN9CB+sxy6Y8ASDDEYiFLvdIAgMt9sJxeN73EhUIC7L6L8wU9J7cThpLqos429uRkwmsZEuIDN8iCgIFNrtJGWZaCyG8Qu+fF9ot6OqKrFYDAOAVosgimklUqMhKQio0Sh98ThFTifiXm98UTjIxDOkXfD3MjGNJZMUCAJ6YENXFyMcDtpCIYpcLkSdHn8oiN/jx+bqhuJ80GhQ7U6i0RBv3fUHel6+F1GUCYVgjkHgqqp8zIkwjZEopVlmkgp4/FEsOhMLn76LbR1xxhjg5Cw4qdBKmduK5CgkVTaexx68C0vPYt6qa2DWlBhyKMLplbmcqL6BZtoFLFv+GJu3dDBsWA27Wto488yzjv4v4UtEIpHguWcehcAaXOYU+ojAr78+h+GzT+PbN/yLcBBsZsjWgAE4ucTF3HwbTnP6AquoKilBQ6lbixiLgdEIkgaaNjL+vG8wXqPSLCT47Y13YNMY+fNtP2b79u3odHpqa4cf24PPkCHDF4rH31QY2BTh5vNPYMLccxFGzySlSRJPrqfj1mcxRaDogl8jiEmaXv4ZunGzKXzvDaSJo+D9D5AkCVsqhT8axWEysXMoQ3rMUMEoCQJjcnLoj0Q+jOY9xhxW6Rru66NvwwYk08cbFguCgKrXE0gmkb9ggx6fG5JEQqslGo1+4WMVBUFANRjwx+M09PSg7DX4JEgSHlFElCSUaPQT+XQatFpybTZsVitThg1DNRrRajQQjeKPRLCaLRRn5yF09qNu2Ay7drLhrj9xwbTJPP/3eygLinh8MFkDJ+lUejo6aezxoqagZTDEGzsH0aJn/TNPsOGex/hetp5vuSRGGbUoqhZPREXOq+Sef9/KL274C+8+v4LrnDLdq8IUp+Dy009mTXgkr728kF//730E2x8hGniFlp0v0dDQgPwF//1+Huw+T5Ik8Z3vXsq/Hvo+o8uy+fE5U5l65S9o2LWLYfEwl9Vk4ZDAIcFXhuVwzrAcEskk3d4A3d4Avb5gOmRAlEBjhGgUwgGonQp55VA6llK/h+8ssLFg7imEoi3c8fefAIFjfQoyZMjwBWJwcJCCfJkfXHYSk374I7pKRFqj27nh7t8y5bT/5U9r6ojoRARnFnR/QL7ZRfaMat5Ro6hzprB57kiSySixRAI1mURRVYKJxD4Z0oIgkAQGIpFjd6D7IagHi3EBJhmNaCSJ7515Js62NtyVlYgtLUTa2g7rhVVVRaOq5AwfTqyp6TPd6S8NsowumfzCK42Q9m9ElrHIMjqLhQPuheJx9IqCZDJ9+iVbVcUfChEMh6lwOhH0+vR0dDKOx25gdWsjfoOeN70R8kWQEbggx04yFkTSaCl2OhCHUmQMkkQgEiaCikkHgk5kizeAlEqSbbUwrtCBx5bFH9bXkyMnyTEITCuwsGggSrlFZOTsGdy/aB3lpjCG4ZOpnG3j7gc3ceUVl/Pq6lZOO+3rFBVXUF5eie449eM8FsiyTCQS5p57/s32jW/zrWt/zXuLl+Hz+ent3cLFp05i89vvcNa0Qt6uH2R4bS2vPPw4gSY/Y40wLcdKWxwG/WHKdR9ewrIMOkYWuGn2hyh12bDrBJh5Bsw8E2omgwZ6GpeQUyrSMRgjGiulZuT09Dp3hgwZMhwGy5Yt49rvfoOVL9+LOa8AFB8pTTYLnz6fure9jLMXMe/bv+fFx25k1vTTaO3dQGsfTLL3sbYnxc4X1/A/xjyiHT4GYikqcnIIxeNEIhFUo3FP21UsmSQWidAjyxQP+TR+HBu7uwmqKqM+RbTg2J07D/r4Rye9qCp1mzYxTqulNDubaEvLYW9QEASMlZUEt29P+zgeB3LqcYckkQCIRjEajV/ootGg0YBGg6zXI1ZWou7YsW8ykF5PPJFAH4kgGY17pqs/EYKA1WLBajan+yQhXTQaTDgKyjjFZGbXznomFLgpc7sQgEgsTltKpjzLQk8wREVBLuLQ5FmW2YSgKCiJCB3AKafOhpZ6RFVBTERxGqz8fsooNjU2MbnAirZ0GMOrJyDKEdSKsfxWk0SqHEfKbGdlWw9/ucSJL9XHCEcUbfhOhOQU3n23gIqKuZksatLF4qOP3suOHdt54YVHGOkI4NvWQltzF9Gokf/73R/59a9/yfbFfpxykpeWbeDdhcvZ0p6gXAI7IHUGKTcITCrJQyeJ5GRZEUhfdzoGvVgFLTYEELXQ0w4GPeg2guqkV8jBbXIghg34Bj0MDHpwu93H+KxkyJDhi4Lb7ebRh27BVD4LhPT1XFJVpp/6ImbtjZx41i/5zwM/IZQ1BiXHQiQ1hbde/jc9xiZOmPA1Tv3+CBJdHpRVDURW7qDL66XA6WR1ezuVeXl7tmPQamkIhwmr6mEXjEeTj1QYAdx2O98+5RSKZJnY+vXI0eieqejDIpnEpKroM6kYh+ZLpDQKJhNiPI4uFtszCLMPn6XSSNrSSYzF0Gs0YDCAPQv6u/CrKfzRMBabAUlvwGo0IAkC0WQSZBmjkAKzMW3yvRtZhpISEGUYaAetPh0pOH0+0dVvgiBjNOrglEugeiJY7Wlb/2A/iklix8aFVJePRioZDsYyiNeDrpy+vnfxhpw07VzDqaf+DEn64lkrfVbIssxLLz7DHbf/jREj7DzyyFJuOF2mVc3Frgxw7veuZulGmbv/8RD2lEwyBE4NmIX0r+dEMyRVsIkCw4YE2zK7FVeWma5QlBFFeelEIIZypOUojJtBd6CfdxPbmTl5FG/2tDFi+h+59da7OOnkOUTDAX50/R/QZHquM2TI8CkYGBhAVXswm8tY8c4/WL58DQ3b6hhs6WT2uFrq1u7ga6eN49/vbefqygLsBdmsv2M5F7vcqEYjazo6qM7L22dIdEt3NzaDAafNdlifmUdTYfxYmcdsMGDSamldvx7ThAlHXtBotYQFgfiRFpr/TXyJehrVSAQ5lSKWSqHE4wc+Qa8nLknIkchnkj0uiCKq0Ug8lULNygKfHxQJm9VOYekwwiEZORihs7cfXySCXqvBaDCke9zC0XQVste+MXUO6IzpgjC/GBxuyHJinH0ORmc2lI1Kf99uoH7zy2xf9QoYTXgCSVwFIxHz3WAqB40VzJNBm0tO0SVUVp0yVCx+uCz939jjuHPnTkqsdZhTbXR/sARJSfHySpVpeg0/vvCrVBhTfLDkacJemXAI3BLU6GCYFiYbQRE1THJlcdXYauaPrGH+yBoqCgvo8kcpMBrp7O4lEAilNyZKIBlh8xpyBYnRnXqkWAkLpn2bRYseY/qEYp59+EFGu1oRxYO8VzNkyJDhCHC73XR1ycyfewK/+PkfqTS3Yw0kefCn32TaV79GmUlDfV0//U0hGkNO/v7sGiyqSlSWEVIpquz2PX6Muxmem0soEqH/OEjRO+wpaaPLhTE7m9An2Iig1RJJJiEWyyiNh0KSSOh08CWYnt5tVh5XFCyVlcj797DqdMTjcfTR6GejNAoCalERqt2OEAqB0YRQXYvU2kRJthtfJIpVTuLv9yPb4h8qjruLxj1KY9pnEkkDw8fBtjVwyoXQWg/DhkODAcbNRrFYWHPzj3l6yyaumjMBtXYQs9eFcczpYEwM/VXt/fuThoTMDx9raGhgx46H6e2dh8uVR2Vl1Zeqx1GWZZ5//jlaWlqQRJXvX/cjdDod1dXVBPJ+QcWIjQSbtmKR2jmlSOKCS65GnnIZi955jfeXPUCRToNTTZGrgck5Dur7fNRmO5mb8+FU9N6MKS5AUVU0kSh2CUjG0wqxKEHFGMS8PMa6csFejM/Vw0uPvIJFVPnXrbdQOedS3nzjTU474/zP/0RlyJDhC4WqqmzbtgWtVkd1dc0+35Nlmdv/+Xe8nRspcsLCFd10NvXy+jtrWd78Cj/6xR/w9K6gc1sbzg1rGBmBMVk27GYzdd3d1OTlUdfTw5S9/BhVoCMSoWo/a7pjwWEXjKlIhPb33sPyEYkvH4lWSziZhGgUvcGQ6Wk8GHv1NApaLYIkHTcO70eKIAhINTXE6+uRJOnAnsXPsqcRwOslCejLyqCpCTZthUQEdVgJdgZQVANWk4UujwdzIkJnYCgpZu+iUbPfkuSsUyEZg5rx4HTDWVdDTjFC2MPEBZczbsRodLPOAGcSQ0U56HVAAfDxhV9FRQU227d55pmbiAaC1I6eTG3tqV+aHsdly5bxj9t+yrTpp5CtX83gwCU07WzD7Xbz2muv0Ny6g2E2OKnShicaJ5ZTwB1/vobuHd3cd/XpGA0x/nP/e4y0GOgKxZlZkM2CIteeiyiANxwFwDFUQIqCgN1sSq9kJKKgxkGnB1cu5JaAWAJRH+sf3MgPRsIO1yRqNT288si11I4cA0oUxMwNbYYMGQ5NMpmkrW09xcXFLFvWR3V1NX6/n4qKCh599FE2bniXfLudEp2Gd5Z2M9MG85Ugl/3w+/RXj+NPd97H7xdMoXzyKQS3b6Tr+aWs6+wgqUJMlg9IPxMEgcqsLPRwzH1+P7YaUVWVnm3b0A0MoK+sxJqXd+jEl48hozQeBpJETKejY3CQcqsVg9n8hS2uk01NoCiYa2qgpWXfIRj4bJVGVU2bow8OosnJgb4+GDkWwoMgiIgoiJKwR3EskgSSwTA9qQTFWVlgdYJlL9uo7HwYNSGdVW0yQ2AQzPkQDyHklqFx5qEZPhU6FrFm8aMMyBZOvuBqNK6L4MAZ8QOQJAmnMw+t1kzA/wLDc9w8+uhNnHX2t1m+fDnXXvt9tFrdniXr47mITCQSpFIp3nrzDQqLinnrzdfZsnUVUrKLd157gv/9SgmJxj/ywEMN2O161q/zM6Kqiu9fM4mHfnc367fGCNe/x9kzVvBBtIBOkrz19nL6Y2BSYkzNdTG/wMnOvkFC0XTeqleGLf4YlxY62BkI4I8n0YgiI4rSDeOSzoCQiIEwtNQsiJDlhJlnMmfSXHw9F3JWSSU61wDzRlSTVTQZeAc4Fcj0MmbIkOHg6HQ6ysqmcMvvriOvuJ2Cq//MM8+/gyQV8PabDxAMxbnzrtvY9cb9bGzqp0wn4C6oIF5UwM9/8hMmjh/HoL0BqTyLZ55Yhhj1UqWwz83w3kiCgNNiYV13N5NNpkM+7/PgYwvG9r4+nkuluG7SJPR6/aevbvdSGuOCkM4n/nSv+KVDlCSKs7ORUykS0Shao/ELmx4i5eWRGhhA63CgHkyZHlIaDZKEZDDAp/CcUv1+hNNPh5Ur0w/s2gWjRoDFCu2te55nNxlBhU6vh3yDDtw54HRANJAuLKpGwrS5EB4ArYZ491ICAz4GmnZRc853ES1ZsHEJlE6C2rMZH+1CGXEmkrOIw7Q2BdJZySef/C18uzz88YanOOfiC/jn337HNd84nV3NzciKSmPjNqqrq/F4glRXVx9307z9/f3ccsv/sWr5UrSJZgaiOcwda+fMMy7kd796l6svmkPZhDI2tGynp2EdBpuRi6aVMP/KH/DQPXezvdNLb0Dl9zc8h0WnENR3UGgNYZQTpGQoNaezop+v24FPMjLZpEEkPSl9aWkBOlEgMDAIKhRkGWlq6yKUSDK8OA9R0mJMJNNrOpEAjJpKrwyD2xdSe9JZiJYU+LJxlJ45ZKtTzREsumTIkOG/EFmWaWz8gDHTZ7D6nZf406//gtkRZtP2NoaLQcpGjuLdVSu4877luJLgiaj85bE3CP7nLX4w3si9a9owu8uZUNHCI/UBCqPpNalhH7HN2FDiy7GuAw5LYawtLUWj0aAkEvibmkAUD1SLjgBBq6U3GiUSjVIlikhfot6tzwpBEECrJZpIQDSKzmj8QiqN8uAgYkUF+P3p/T/Y4JPBgFpTA83Nn3JjMqllyxBychC93vRj2W7UdU0H3pQIUOx0gtMFFhsEguC0gzsXde4CUhoN1C9itVjMY7f+imxBZN64CmoGFoFxHrJFA+vvQZz3C6QZ30cQs4gnQuj1KsIR3AJ1dHTxjzu6KbKlePvZpzl3ZB5KdAuvvfo4W9ebue6nf+Cll15i7donuPXWW7j55o1otTq+973vo9Vqj6oCqaoqyaFMb1EUSSaT1NfXs3Tp4j0KqCAInHnGGVSVm1n32u3EdnQxf8H3eGLhQkKDKeTWbdz53kJyFIUfzp/M/DMnEs2bxIP/+TnRwX7WtIlUCDI9AZkaPSQ94IvDgBeKJIiG42S57dQ6HThMJgzaDy9Zu6ehx5cUAaCgkhI1FNs1dPV4KHLZoLgU+rvBYiaVlUV8xZ24xk9mbcMu3l/6OJOnXkDzrq1cfPHX0Gp1gHLUzmeGDBm+HFRXV/PEY4/g376dHEnBOaGc/ITCBTPm0KETuOXeB/EEFYr1ICqgictM0ckoTTpOiUc4d2YRv3/4Xc7JtrJ4Z/qzyvcRbVl2g4FRTiedfv8xtdc5otvp7vXryRsxApPJRLS9/VNt2GI0YjEY0u7mkCkaD4Gq06WX8b/ASmOytRWppAQNoHo8Bz5BUYhv2YIcjaIzGBA+ad+mqoLVijBlSrr4TKVgxWqoKofG7Wmblf3ZXVjmu8DjAxR2vPkkra89iHvBeVz95xvITwT5eq2e6oCTxt46jD3NrOuUyXPmIay4kWzzAOvbRrJx0yp+85un0ekMh7W78Xicm266ga1bl9EsySTCcKJpkHve3srcyVX86sdX0zJYz1133UNVcRcbl/yMgS4jl1/1GzQaiVQqxZ133saECRMQRSMVFRUEAgGqqqo+2fkjrRgKgoDb7Wbz5k0888z9AOTkVPD8c08Q8nZx2+1/QKNJF1Rut5tJk0/m7n/fxogalf89fxTLWx5gxZuN6ARoWt/MV6v0jBlbS/6oUYTa+vnb4/ej8YQw60xcO6OUxauaKZEgVwCrBHGvj8mShkqDgMVkJuTxsc3n22c/DZKGMcW5xFXIMhkxarWICOTZrKhAhaEAMZWAnk7wDeIZMYolf7qUDZt38s1vvcHDbwf56te+Qd3CnzJynpt16waRpLGsWrmZqVOmMH3mvE98DjNkyPDlRZIkPB4VbbKHB+6+mj/e9Bpvvd7IeJvItJSXh7bvpLc7SbUGRmmgXIBaDWhUGAzGmF5diVQ0kh8vOJ8Lv/Zt8gQYBGbk5h5yufl48WM87E9mRVEQBAFXTQ3Rdes+9YYFSE/T6nQEEwlsQ/nDx0Ne4nHHXkqjxmjcY1D8hUGWSXq9SC4X+HwHV6cFgaRejxCLof00RaMoHqjENuxErapGaKg/8Pmqkn5+3wDMnUf4rZdJrnyPYZoEt99yF5NklSvKrOSb9QRDIotvfYxTJkxkzYY6Flz1M/70u4e54sRcFrW+yPnnnIQoKnti7j4OVVVJJGIYtCoTh+UihGSe3TDAOTVmLv3uL2nofZYnH2nGRDsO01jyjafz85+ejMYyEUVRefLJJ3ng/htpqJ+GxWjguz/4E7fd9hsqKqYSj8c59dTTeO+9RZx99tkHneZ74YVnaWluBEEgkVRRVZkN65fyrWuuoqh0HDff/Fe6217nrDPP5d67b2NKQS/X3XYzI6edxN59mrFYjE2bmmnfBePGNXP33VFMCjgMcFmxxKzJkzBe/keSegsmJcI5mue4/8FXcRtTLKxrwymIXF6Ry+Y+L3I4zji3nVAoRHsCSkgwoaAQ436/UoMk4Q+E6AuGGZ7vol2WKXY5YWjP0gq9HlIJKK2FTZtxrWqgTFFQFkW4et5Ixk430N8ymWR/gJdevpnQQCkjCvRUXTGVtNKYSX/JkCHDvqiqSm9vDzEhyKaEg/bOdsr0ICfhjx8MsLopxAgNlEhQaTZxZo6d5q5uUFWMKpjjSZKywpOPP0ZDT4wxcroQc4siykdYD4qCQKHReEwHXw7rU7lnYIANXV2UGgz0bdiALhT61MvSu9mdPT0QjeLxeKh2uxEzy0EHoOp0hJNJPH19OM1mHBbLsd6lw0dVQRQ/upeRdPZ0Qq9HjcfR5+bCJ5nG359wGGbPBoMIBysYAfw+uPBi2LgakygwYlg1Hwx4+Kqtn4JhNnRGLa3BCOPHzOBq91loSoq54au/YXn9WrZtDVGXU87Fs05nzpwrePw/D1NVXcv06XM/dtdWrVpFXd0mfniZgQLZxh0vNTHcAPOmTKWxbiW/uvllkmGVr5YYue7mqzHadKxe+XOqpv6EO+/cyQMP3EJVToCvDOshWHYhS5e8xgvPvM5p8zYQ8yeJdLyLs2wWFWUH9j3u3LmT9p1LmDd8G1vaennw1QQTJsisWtnK2WXryDrzTxTlJynPG0v90nc53dnKJRePxaG0s/SN/2PElN/R2NgCwMsvv0xvdyfzx9l4+kGRH119Irff8wbzHTKKxoI67TI6dXksf+QXbIvkc8899+FKJiiWoESEMwuyWNPei6oxclFNOU8291AsGbii1InLfOgmb5vZRLEburxe8o06lHiMgKLuidUCIKcABnpxZmUxc9J0ylq24yqcRrarmneXr+If/1yKE5lsm4UfXlbKiCtuoaE7RP/ADoYPH/6xv8MMGTL8d5FMJlm76j1OKrCx6oWlTCquZUSlRGBnE8t2dlIFjHIaiEfiXFyax1MtvRRrLVRqBGLJJO1tvTxxw82sDKeYicQZUtpLOMdioScYPOR2h+fmsqO7G1mSyDlGn/+HdQu9o6MDhg/H4HAQ8HqxT5/+6W1Q9kPR6ciy2YjGYl948+qjhlaL1mJBEgSUROJY780RIQ8Oorhce6aZD4UgSaijR6cNtT8ro3dZhi2bPyIvWNjjwyhMmo40bgbTC/OYPKyEwgnT0erNTMh2Im1YgSRpeGHlJnY89ld+99vbuXCEm+/NziXp6WXVc3+lfetjlBR5eO65p/YYc+9tzi3LMqlUimXLlnLNNV8jHongQWFpfSNyXCUYg2UrN/GfO+6isVPlmjKJH54zD21WOSmLBYvGi5gI8Nprf+F/vn8h3/v2aXywOUi5SeTtt1aixKIEm/r5xfev5Opvf5sfXP9/SDrXAUdcXV3NdT/+B7tCM3nm+QEuOOk02hpNTLbBrBFzmTBhGLPnmNm2q4fXl7VxyvjxaHKvAUMVY8dcwesLF3Ld9y8mOnAz3d0buf5HdgLeANfMm8iSZSuIJ2Q8UXBYnZhIoQxsZWtdHY/f828uLdBx6xlTOd2lxS7Cmt4AMwryuaQ8j6eaexlmNHHBsCJyLOaPnAiUBAFJFCh2OdEYzXQMBFBCkX3N2D0DkJUNqoIQCmGbdRYGs50lL77M9ofeIBJI0udXmJJnYtSYE1ixoZlvXfMtEuGmQ243Q4YM/73E43F6utvJGnkeF170NS679krOvfYs5owrZ26WwCgtuESoddh5sqWHcqORc8uKMBoM1CUFtngi6ENJKhWB/yssospkopwPVw0PtcqqAu3hMMcy/uSwFEZBEMjNygKvF53ZjBIOf+apLXpJAkkiriiosRiaIR9CfUZt3IMA2AwGFCAUi2FWVaSDxe8dp3xsL+MQcnMz0XAYvSSl4wU/rfy+alV6Wtps3mdaeg+CALasD//d1gydnaCXoG0X9qlzYf0SMNjwtnWge+N5mpQYt40rZuRJC+hQJE664ixaVzzCj//nQvQFkzjrLB9QyGOPPYBGo6G0tIrKykqeeuphxowZw+2330pR/gDnVIG4M8aSOhhvAVWGxzYMMCULfj5MwJ5bQVI28vYf/0T+GBe5E0po7tGyZXOUGVN6uPOmJfxxtpO7b7yRlCfJA5fUMueb19MRERlWaAVpACg86GlJJJLcee/LXDzFyDvP3Udbf4LZJWaycsZw99+/zguvBWlpjjLcpmP46d/gH08/x5kzCzGGu3jgzpuJeSLsavQwtbqdRSv9fP8kHdsTBVSpXhpEMMrQsbODSa2NbPO9j8fXzuK/XUv2iefjb1zCtuZdiL4+LivPwyYJPNjUw3CLmQX5riO3jhAEitzZCIoCkSiY9op97NgF1SMQJ83G3rgB1RvhpOxh2Ht0RBx1GA0a5k+eREqys/W1O7njd1eRXzKRgYGB424qPUOGDMeWFSuWcOvtl6PXn8Hil39PbXU173XuIm+8B+0mlWQImgIxBDXOBHc2J+e6eLmrnxZfhDNsVkoLi3iwvpFzc/Ip1Wp5IJnkhCFTboNGw3iXi1A8TtZBrAf1koT+MxbrjoTDbhRTVRUUhYjXS8vKlRRWVBBuaPjMd0gQRWJaLV1eL+UWCzqTCYQjmTv970DR6wnH41iGpqm/EL2fQ72Mmpyc9NT0oZRkRUHW64krCqbqatTGxk+3XUGAceNg1bKDf3/k6HSBMdD3YauFzpgekpkyB7Ld6Wp9+ARcNSMZs30dTqMG8wXXkKgZgbD2ObTaJNVn/oCEPokghNHpqggEgjz00IO4TLtwubXkF0xj1ZLXKLJOZFSthlZzFa3bNzPZAGdVpsu6F5vgJCdMsMCIaTMpr61GmHMJ55CCbCurnruCu5/9HwxCjJceeh5/FJZsTJIlKlx14ggmjx/G6jefxDLpPJ5+/V1O/0oZFuvBC8bXXnuFQW8LCZuRpJpASaqcMrIMu7uL4n43dPZRo4OLc2X8C++hefkmfvsmjMiBKQYjI668jNG5duzlF+PMf5I7XlxO87YHURJgS0FchnZ/iqaXXmZ5bwezyhw0uMfyzxv/RFv3Thoa+jjFZKA5GKV1wM8Et5MT8lyoqkrqMG9IBUHYU1yKwm6l2Lhf0ShA7TjYtQ28HoSv/QitkmSaHKUmEsYmhBFqpiFnWTjrGonskgt55rEbWbBgATDnCN5oGTJk+LIza9ZcFr33DrPnKGzvyuaBZ+5l/Pg5rHxLoQQgCVNE0Klwco6TpoFBWnwRri4uJEur4Y2+QdCZqDaZ8EWjZMVilJWUAOAwmdgZCtHn9TJmv4JREgTG5OTQH4lgMxqPyWf+YRWMqqpSt2kTYwQBXTRKzuTJCEcx11CSJApdLhKpFM0DA7hMJuxm81Hb3heSofi9wUiEgcFBqrOzkY73VBhVRbBakYqLSR0i3Hw3gigiVFeT2rYN8WBJMYfCYEh/HS4mM8ybD0vfSfs1FpfC4nC6yJw690N1M8sF42dB1E9RcQnCnHNJDrZwzzd+yfQCKDWG2KDLZ+F7i/n1jRfRPXA2v/nNDXS219Hoj3Lz1/NZ17GcbIuTzWt6mDZtBmveXsGYvGHIgQFi/gCP9oFLBYsCLlMWZf4OBN0EUrll3PrPvyDX/YdXN8W4YmQBW/V+pIjKGXkSP6y1kT11HtqZ5yKIKpMTITriSYaZdmJSG6ivTytte/fktbe3s3DhK8yaPhpMPZRnDZLwqry0shmrRUe/UyCchCIdhCMy7y7ZxlmF2eTjZeLwUszfuoGWqEKFpg1PdhH3/HADBVlw9Wlw68Np62uLAoWCytrtbUzSgi7p4p4bfsF555zOK6+/hyYOBilGc0DkvKJc9JLIkq4+goOeA4ZcDvoeQaA620GW1QKiiMM0dIHdv2jcneAT9MPJF6bfH8WjEGJ+nAyQtGn44L6/YpxWxshJF6DVmbjoq79Fqz3O/54yZMjwuXPXnbdD7yvs3LKQ1t46DKkEOzfcxUVjZe54Ckar4BBBp0A8mWSRJ8QZNisOnZaeQIC2ngGurKjCIEkQjWJn36iHQCJx0GVnQRBIAs1+P/lO5zGx2TusK6KsKGzzepk1fz7x99/H09REXBSxut0kBgaOyo6JgoCq1aIZKhQ9wSAanQ7bF2gJ9vNA0etxajTE43GMgnDcZ1DLAwOE+/sxVVUh79jx0c9takJOpdAnEmis1o8fspIkpGnTELq6Dq1e7s/wkaDVQP1mmDkH6jfBYF/6e0314HRCSRlcdh30tkFuAeLMM6FiNJqYn4svuYocMUTv9gb8djMXnHoGnrbl/P6vz2CLrGZyjZN5Y6ZQkaPh7rfWc34pTB+/gB5jDZecPYWGbX3k6mHpDokCUeYbhQYKdAKWVIJYOImpcjLheJQNzz/Oju4YJgF6PSrDtZBtE/hejZNCqxVmXEBi2AS0jQuRJl5KccJHgTCBVZuj/PrXV/Cj689ly5ZqjEYjs2fP4etf/xofrF7M5Qt0LG2V8Q0qJBKwvjtK9LkNtEShQIBL8nW4jQIVw0fidDuIRgKYqkbT/M7jtPZ3U/a9G/nPf/6FPRHjunNP5h/PL8JAislZBqZZYaUnRq6YjnaOrWngPLuN/peewxROYgfUFNh0Cm+19lImqkR1RsbmF3I4OVCCAHoBtrR2MdztpC0UpsDtRBSEocE5474JPlotjJqMOqyWyK7F6OPtUDqROx98jjOmnELl+BFgmASCgPYwrZEyZMjw34Msy7S0NJGvbsPX2Ud/q8pXf1jLX/5cT1GWBpcBtEPhUqIg4ItEcUajVBSXoAJ9sooDAd0nLPZUhlZ7jxGHfQttNhgwGwzEgXggQMU55xDfsIGjOXohADa9HllV6QkEKBFFYkNFUaa3Mc3u3s+ELEM0itFoPK6LRjUSQTtzZtpe53DQ60k6HGjdbtSPUSURBARVhfr6w+uxHT4SLroEHrsHdDoYPR6Wv7XXtg1w+XdAAqxWsNvT6pR9PPh6EfPLyRk5FbKyyW5dx4TAVtqbl7C+dypvv/YCxaY4373cxZKdAe5/bBM1RoVrTjodYXItd97wZ5q3BbAlU0TzXcwdXcbba3bSE4yxPQnz7Vr8hiixbau4+19/5rq5WfzjdYWwP065t5sJJih3WCg16KFyGuSWcutNv2SCuZsij4/ezf8kIGtZ1FGELlzHwE6VtVuz+Mttj3DTTTfR1baUUUUCPz5zHr98rpdc2wZaesEhQSAOkywaTsoxMyXbiCjKmEUVYdwCTOUjwF1EabCPUlcuKxb+kacfepsqUz76lkYGGlLkaUCOxVgYTU9Bl0tDd9Ai9HoDbO+G2WJ6cH24xUqlVmREWSkaQdhniflwUFCZbrYQisUxJ2J0dvVis5oRtFrsFgvY3dDRBJFQWiE26GlY+TpLN2/gmm9cwIb6AD0dnZjOyKHX18POTWv2JOrIsszq1asBmDp1asbMO0OG/3J27txJWbmBGbXnYRrcwSMPd/OH617HIiuEvXF6vVAhglGrYZzTRX80Rpleh4iArCjUd3YzIjcf6Rj2IX4ajmjNZe/p5VhfX1pdPFR6x2eIKAgUZGURl2V2ZXobD44kkdDpUKNRBhMJihyO47avMbF9Owqgy85G6e//+B8QReKbNqE9nLzpw30/1gyHK78Bj98HTdth7KS00rhj69A2JZhzKqQSqM4cdvU00PTOA4yy2MgrH4NQMQlh2HgUOUbje49ROf4ENOaZ5Dlt/PhHt0IsTp5dwtsJ697egCcEC7JEdjV18t66f3DN+OE8Lnv5YPk2htllXmhoJhsBtwoVetCoIom4Sud993J5mZOb6hLsHIjzTSdkC4AMUgqCoTjaWArvluWsXLaM19p6OPX1FZxxYjGTKiK83RRjoBvKBns467oFbN+2jDfeeBmTSaHUIfCXh9Yz15VizlXzad8ocOcz72EVFOZawalR8cYVSrMdcOLFqLWTUYxaFCWOJrec2K7FPPbYS1w2zcFLr3dx/7Mqcjztl64BqnUScwvzcGsEUopC04CH1nCMCZKAzWBkjMtBsc1Ky6CHbZ3dB/01ucwmipwOVFXdk+yyz1sDAVEUsJuMKCYDUiRKIBjCokuCXge7mgAFggEoKSdlUNEoYa4+VYLoThJJK7Mmxvnd31ZQUxtn8aIN/OMft2O3n8Ftt93Mm2/+m/JyKxMnrkKSMm0xGTL8NzNs2DCKCqfz+rJXuPZbP0T3xu8p3KqQUKArCKNFmGgQGJuXjS+WIBCLMTLPTZfPS77dzjC9nmGHEVLyEX4eFB/D9rzDLhgjsRhLFy9mgs1GwuOhdd06KqZMIbRqFWoqdTT3cQ/79zY6TSYkjSazTL0bScIjiqiiSOo4ToaR/X7Mp56KMqTefBzK4CCK3Y6mqgph+/ZDLk0LubmIZWXw+usf/YJaLZxzLrz4BDRsS/e4zT0Flr4JqSS4c+GEk9JfcpJkWwNP/OJaTMSYffFMBrcuxJVTjKAm8Pd34i6tQJTiNPQEePW1Otp2dBKIQn2PzNwd9XytQuChrSr1HoX3Fm/ijMuuwHziKKIf3I4sQ2Orj2IVfj2ykEqbAU84yo5AjDfbvFgFFW+oFyGocp4VHEPFIkCbJ0xYkZgw0Efd32/A3BVkgkPiNIeewY09bFubZIQmxLcvrSAS6iO08l4a4xX8+AfD+N1vtpPUm9HGfRiGn8DS1hQvvr2CYFzBKMFgREWfClOs10HNJMgpoTfSxsv/uY7CuIS++FzqGrfT3qkj4k0gx1RCSbCnYJgGvlmSQ6XbSUqWaQxEWNHeg6IxcEl+LsNcDtoDQTb2e/mgtQufxshkneaAi6QoCDiNEmsamonLCmMKcxF02n19Fvd+PgJ2kwmb0Zielg5HwWyEyuGwswHZaeOFP/8LyaanOzCI8WQd37n+b4yoMlNQkE9NdjGLA0lCvkFue+63PPrQLZTaHXzjqjtpbW2iunrsYb1fM2TI8OVDlmWefvoO6te+yuiSAf5yw095d1EL83Uw1S7hEiBHb0AnC4QSUSIJmQm5bnoicexaDUlZZkRhHl09HoYZCg4p6DhNJkyiSCyZxKDV7vO9HIsFUZbZ0d/PiNzcz+Ow9+GwC8Z+n4+uYcOYX1VF7+rVGJ1Okkepf/Gj2Lu3sScaJUuSsGo0qKJ43CpqnydWvR70eqKJBKlIBE8ySdExjBI6KLJMePlyjDk56aXpw+g3FCSJRFMTxoqKQ05NizU1CBs3Ql/foV9IlOC0M9O2/Du2pb0ZTz4j7WfT2QoLzoVR48BigWQM3n4WTeVIvn7uRTgtGvTlxWQXV4JGA+2rcYw4ldefW8Iztz9OqG0z584p444rsti2LsD6dpUlrTJ9MTADVTqodDopz8vnre0JuuvamZ0lYJJVsvUG1FiUR7q9ZMUiBGVwi2ASQImrTNAIWAQY63JgM+lQRAlJFMgxplW08qTEjyvsFJhUVIOOcTPmE7VkkaqdRrJnG0kJXMNHMnHXMh5esp0FM0fyvQvnYl39BLt2ruPXz/rwx8EqwBSrxJQcBx2DPkRZSBfVU+dB20b8LUWsbGiisecB2nygVyGsgXkmCMoCNaLK9GwXVW4XjQODLBoIIcbizCzIZ5jZSCiZ5MWWTjr9YcY6HNTmOXEMRfsdjGgySak9G4NGIhgIY9Yl072KLudBFUfYb1o6HIWm9E2BGvaTs72JNfoU9YNRJmz4DTefncXor0zk1uclnn25nbGlFkpz62lq3IImkeTEGifxzudJCSVApmDMkOG/FVVV0Wi06BzVPLGwCe/mFkYY4OwyF5NLS0n19qErrgWDlUQ4RrnWAEmZgs2bae4ZpEirR9TrybNb6PR5KXY4EYBSi2WfVbEiu53VLS3kulzk7XddFASBznCYz0eiO5AjWpLOsdv3FGXBzk7kWIzyiRPxHqZS9Fmxu7fRotcjKApdwSBJRcFpMmE1GDLL1KSTYVoCAfK1WuR4/PjyaxyaltZOmkRi167D+5FIBCorP3pq2maDj3DKRxThgouhqgLuvAWiEaiqhTknwQfL4Bs/AE8f7KiDaXPg4VtgxATE8TMpslqgtBJiHnC56At0UL/8PYQt6/nPvQ+xfLOH35xYyBxdM9pyC1p9EdkbdrGuIUJCgTFWSMlg8ftI7NjMvDw/w86tJLfLzxv1HrZ5Y7wrC4zUgk9rIJRKUW0yYRfSBdA0u5nuUJDKLDv+WJiQVkdhrhuiYbAXMnzeOBRBoXP5WxSPHo0w/SsYy4YT6liEWlpNbvZnQAcAAQAASURBVMloVKOWHk+Cpl2bKC+v5IIf3YlNTnJmlcpoCzTJcJ7LwMmFThoDMSQkDDoDUVWLUWcildIyONhCeXYppYk2NFaFIoOBbEGkrtvPguwsNnvClOg0vNjRR6s/yhkOKxXlJQRlmddbOmmLJhlrNHF2VRU66eNv8oxaLcas9EUzy2giEIvt26v4EYrjh9PScRg3Cqm/g0kWM4ovRLXTSnbNGLZEUuRExnPlpXn88H+uo69X4ZcX3oYUFrl64nCu+s0d6MT1aIquOaz3aYYMGb6cbNmymd///kbC4UHckQS/mF3JlJrhFMy/CNEQRfa10THgpLS6BlNnM6TA39UBvkEqknFEdza4cmhf/QH5ej0oCjlWKxpZptvrpXjI81UF/IkEn79++PEcsW+Esns5UFVxVld/5okvR4IIIIrIej1CKkVXMEh2IoFWr8ei0/3XK44FNhuo6nHr15jatOmI+l/lpiZkRcFUUwMtLfsuTWdnI40bB/fdd/AfFkUYOw5qquChu9ONduMnwbmXQl4BjJsEAS9k58KwKti1HcbNgFMvSucR105AlSAeaOOu+16nY8daPli3mjmmMPXtMNYEVcXVCOPPRgrXYTtxDK2Nv+f0KdX09Sd4ta4FRRG5KN9Kzwfryb3kRN4Kd9C4tR9fAGyigSqjge2Dfsa6HSwosOE0fVgIRZNJSnUGpEQK0WClaMx46BpKI6kZi1BQhqQmKcl1Q8AHvj7eePl91r73IBfNnYSj6HcsWv0y1/3wr3xtzhS2bdtMqTOPrME27BFo10BcgZZAjKeCXcgqXFSWx5a2fkr9UaTnH0DWBvjm6ZMpm11J+L53MVfPR7Lnk9yxDf1TLxLx+zCqGl7q8lCq1XJ1edp3LCorPNjcRZnOyJVFufsc15Gwp1fRONSrGAhh0SfTA0uHGkiRJKgaC4N9CL4AoruIafkixngI3MWMPeUr+GvN/O1nf8Pj1XHWtIlsW/k+X6k2c8El5yHm5ZFgAls3N/Dm668zcvRYzjzz7E+0/xkyZPjiotFomDRhLA2r3+emX/2cGXNmEfP8B9np5K6nbmD76l2cNXwOJaeeSVITJBXu45EXV3NZTQUpJYUmpiAKMsXOLNTSStjRhFAzAk1dHTZZxh+N7jHrdur12DQaVDiuBLAjKhh3Z0qXmEykIhEifX0kenrQm0zIkcjR2sePxarToep0mI1GegIB7IqCJxzOKI5w3Po1ptrbiUQiWKdOJblixWH/nJifvyeTeu+sacFsRmhpgd7eA38oNxcmTEhPOPe0w1cugiwHTJ0JkgCLXwBXPowcA631MNAOOXlQMxaMJqhfC4FBYsXDufirX6ezL8hfvncWg97RPLdoFcN0cG6uxMTSUu5Z3YixZTt1kXcYPXw+jUmBxxa/gCUOV5bYWN3tx5NUkP/wHP0JGT+w2AsnGmI0xOJ8tTQPoyQSTyaJJjV7lmqNWm2691JvwF5RAY31EA9BUcm+x6ozwHnXQsKPaeM6rr/ip+gnz+A/C5/l1r/dRZVk4KSp05C1W0hYtlOjF3hou8pADOwqJEUwmowscJixKyqi0YTebEWj6BEHVJzTp9H4/su8oZWw9/YyOuqjI+LBr9ey2hMHFf6nvIBKswkEWNzVz8Y+DyOcbk7J/gQJLgd7DwgCdvNevYqhKFG9BiTp4EvbbbuguAQkDUarFcJeiKiwbjUbbTpuv/4pJrvinH3JXP768Eom63XMdBnRrFvIjv4ufvf6Enr7zFx1biXjxpyYSYDJkOG/EK/XR+PWZv796AuMmjQNVAWNsgGxWOHU6jhXTv0ahlGnsWzjuzzx8DOMqYpTWe7kj0074IO1fLNiGLVqEuHKqxDa2lBHj4Lnn8cqCvgUhZyhgU1JFBmVl8fGnh5GFRcfVxPVR1Q57OjoYMzs2Ri9XoKRCN5duyg/4QSE1laix7BghHQVLg1NUwcTiYziuB/7+zUe855PVUVXVUVqx44jmrRXBgaQKyvR+nwH/tzBhmFEEU49FWw21CwrqqQgVlbCQE86yQUVRowhNdhDeMtrPPrQE8xySYy65EcIsSDqQARP/ytseG8T/ZKLrl4v8YSEOxkh0LgWiwBaFXoCMu+8/Abdvf2cU13ISXNO4u2GOu57ZSOjNTAIrO3yUSCBNyVQlZIpFmBNEoZLkC3DOFGloa0bURDI0uuw5LnZGAxR6nZiNRrSxZYkwa6dUF6TPrZsFxSUpe1jcguhrBZKqqFzB3P+716ItPLG6w9w001P09srY3TAk3fdx/zZJ2KfWMkf7mknHoNyAfK0IpeV5VNpNaLXaPBHoliR8Pb7MNl7cE2awo3Pv8tgxwB1OwYZmddL9Y9+yAvL3yLkh3Acrs82U20xk1IU3urxsN0X5bz8Qkptls/8/SaKAogSMgY6unspctogS8MB9+SKAj4v5GRD0JeOgQwHoHIkJf0x/mC3s67fw8pHF/PVglzOr7aRU1QIY2ejKhKG7ie5ZtZkioefyyOPvcZVV+Wgqq7jcqAsQ4YMnz0NDQ3cd98f+Mvf/8HoyXPTve8IGKp/CshUnFeAquSxfcdi/njDTwh1hGj6ABr7gCicYARjxAPf+gHMnE+qeQ2h+/6JsSQfTcMuSmuqUWJxEl4vgk5HIJnElziapoWfjCMqGPfOlN5NqL0dXSj0YaTacUBGcTyQvf0afYEACVmm1HVsP/Sia9ZgHDECrdmM3NZ22D8n79pFsrQULcBHZFIDUF0NY0aDxUT/tg0k33oMp9ON8bQrwGqDiJdU5yb++uiLPP7aEirFFCd9ZRpPvvcapZa3qPP18vRj76JLqfx8jI7xLpV1HSn+/O+3kBSFVCpdnowwGbEODvLbOSdgmDgVYexEal358OJWtidSFAgaTrKbMMlJYlo9PV4/g0mVMhnGaEBUQS9DtcNBls2CIIp4onGsaJGDEToDQUpysyEShmmzQSfCYG/6wjV1HpgtIKjQ35bu1Rg1K61ItvdQJWzhmyeOY9GyLQS8Ya5w51M+ZgQ3PPAU+REVrRbskobz85xYRIE3W7owxaIIpOtxBZV3utbjXFOPPhqh2qDlO1W51Pzy/7j77cV88P5aLFGFU20mTizKpykY5t1d7SgaI98rLcYoHd07ZFGjoSK3ADGVSA+5mIz7piAoClhsMH4KvPo0qAqMnQYBD26LGfXsy8kb7Oa0WABp+Fik9i1w1regegrVkX7umnEqGoOVd178N5ddeDnvvfMUF13yC7Ta46gvOEOGDEeNiooK/v3vF9HrjQdJWJFAmkBSTrBp83YuXWCjuD+fDVu9ZMcCTCizcZHLSPG0eQgTppDKzuHW+9ey4f21DEPL90qGUzDvBIS33yUaDpNIJNB/TKb0seKI1yZ9PT1oh5YCVVkm4PVSMX06vnffRT1OCkbIKI6HRJKQ9XrsgkD0OJiilpxO5ObmI/shRUH2etG4XAg+HygKgsuFYLPt+zyTCS69BBxZCCuXID78TxyFdhg/AaVzKVi1rHr5Nu5/YTVvb+klW4QcIyx6exXd8ipGVDp5druXeFjl69kik9yl3NfURCCh0C0rmIC5OoHzs60UWvWEUdGPmAQT5/GBN8Rv/3wvxFJIKlyeY2RGcT4PN3YgBoJU2bIYHPShUwVMiIzIzSNbEtLJJW09DHc7EUSVspxsArEYRZJAMhSmJ5miWKMBJQlaHVz4bXA6UbRaQgE/Sx+/kckuE9nfuINwOMW9t91KcbIdUVvIxt44M/Qg+cP4334esb+NM0t1vNCQwCHC+z0eiuUUDruD2hz7hzdWAowXRGw6LbW9PViNEm2qiV0vPkmo04cUU5iqgUuz7bzf0c1Sf4zzsvOptpgwSiIq6bSoI3pfiAebfz44AiCIAmj16UiZyJCdzv6vsG1TunisHQNiCjQ6GDERYeF/0JxxKbjcYDDCzLNAr0dN+olpQGe0IXleYMEV32EgoGHBKVei1cSATMGYIcN/A5IkIUmmj3yOTqfj3PN/zeYlOSx+9hYUXYo7zh+DY+xJEIySHDYBTW4h8TVPsvzJ+5mpgTnl5RR89WpSKISsErs8g9jMFgpyc2kJhej3+b64BWNKlnlu40aunj0b7YYNqKkUgiDQs2oVlqwsknv1lB1PZBTHfbEOTUwf8ynqVIrwBx9gnTYN+f3304Moh4OqokrSh72Mfj/StGmwalX6NUQxrSxecjEUFSK074Jn/oP77MtBk0LuaqF+6ULcHav51T8XYVFS6FQ40QAjdWBWIKHAXZs81KpQZYQSRSEYNTJcIxDRQ5ag4WyjwORcN9kWE4OBILU2E+LOBhIlpbz6zHOUBAcZboHWGPQEgtyzqZFal5til4tn2no50ZzFzPw8NKKwp0BS1A+TSyyJOEIkin1IMev0+sgfNw7iMahfB+dcBsjg7WJr/Vq+/8OfcKY7wkaNjlNy7+TBZ59l4aIWbpto5qmGrThUqNbAY00BituCVJgM3L8rhjEJWUoKM2kbH9njY5vXt+d0uwwGhhflEUgkcdiddHg85OMl8G4XTaEE2SpoZVjc1o3LaOEyexY2MR2L5SNKv6JS39mN/TDbDpwGAyMK8wgkk0B6ZSPHehhL2gIfFo37K42d7Wl1dsI06OuGnl1w2gXQ3wVX/BDGTE0rsnKInRvXUDb5RJbXLeKWW/7Eb397HRMmfh8EI+7DCbnOkCHDfyUtLS28tHQN/f4oV159HfaZs6l79248pjy67/9fLsp/ClP1TP726yv44K77Gf+d60jOms1vLz0BaUeAs40aTJJIIhQiqaoMM5sP6sd4rDhihdGVlUWuy8XuhcDo4CDG6mosOTl4j9OCEQ6tOLoTCfrjcardbsT/suSYvaeok7EYGr2eLMPnm6ErZWcj5eby8U6M+3JAL2MwmF6eliQ46yyYPBG2bkIw6eCFJ8HhhEkzINuN4OmmYOaZvP36XQjJFN4YXOwyckWhg3Xt3WyNwDARznTZKbcYsaPS4fXga27H45cJJ+BUC7QlVPJ7BnDlZ+ONxvAEwxSZduGc6GOMO0penpFJFaN54N0P8GtMnJNtpy0Q4pl+P5fl51NjOfCOVRSE9FCHyQhG4z6KWbHLCX4ftOyAS6+CuWeAxYo6uIue959hYDDCSz4Ybklw6y9vRhYhT4TVTWGECGQJAvkqFGhVmhMqKwMx5ooqSREMikC100mt0XjA34BNp2Vbaw+eWGzPY73+GAFFZaYKCcCmQk5KRQss7PdSm0ztMeL2ITIiNx/3YSr6Nq2Wbe0fbq/MakWTStEWCuMypZNfACTxIK+3u2hMJaCsEjpa0j6figIjxqSveM0NcNIZMGkmeHph/AzwdoDNzeYda2h47VbicS9LHr+TG35zM+MmDAPRwP75C6qqkkwm0R1GckOGDBm+/FRXVzNy1ALGXzKHquo5NK65kdcXvs3kvCpmnnA2Gr0AySiYLVSVWElOnsUd/7iZ59cG0MsqfjHJV0lgjicocLnY3NNzUD/GY8VnMy6rKPTX1R3zaenDZW/FscnjIVuvp+W/NTlGEEjp9fTEYpRJEtF4HEGSMHxOk9RqJELkzTfRZmWhHuENh7xrF8mRI9FFo+kHRBEuvhiqK+ChexG+/g0Y7EsXDCeeBCNGgQBieRXxdx6k7tWllGkESiwq0w3wWrcPXUzlglwHfTKEvT66/T66ge6UypL+OFkKnC6AGk5RJEJKkWju9eFNJnGaTDhCcQbv/CdKwMsZF36F6556DbsscGlxHk+39VKEyLUF+WSbP3p5Azi4YubzgasA7C7Y/D7YNPiyrPzrtZVUmcAJtAWhSgsGvYHR5hi7IjBGo+F/agrRyinW9Q6STKa4JM/B2n4fc8wmZubnHdIXUQXGl5SiqtAXDCKrKtvDUZKDXpzALsBkspKLgJqAy3MLcBr2XUY50km/8UWl+/x7W08PoirgNEisbdpFrtVMltUCoohjf5segbRVUl8/JBIHWu5IEgwfC4XlUDosPT2vjdG5/hVqJ05loT0bV88ufn737WDMJ6UY0B6kD3Pbtq20tW3jtNMuOqJjy5AhwxeHhoYGKioqDjtLftSosVTUVKGKEnHNDKZd5MGZgKITJ7Pk4Z/hLj6R1fWbqDrpEs45ay5NrQGusaqUilrKKkczXtSys66eHT09RJJJDMdRhv0RVwWqqtKzbRu6vZaXjqdp6cNlt+JY43KhAsF4nJ5oFJsk4QmH0UgSBTbbf0WfoygIFNrtxGWZjsFByq1WdGZzujfsKB+/3N+PNHw4Greb5PLlR/bDgoDkcqFu2JBWkEaPhpG1CCvfh298CyorobsDrvk+VA+H7lbYuZ7wsEp+e8fduFSZU10CO73wZH8UQYFJGoGefh9RnZGx+YXoUWnoH6QlHGO8MJSNvJeyZdBIBCNhis0SPjVF56CPbK3IWSWVvLNhEx0dUebo4PGWLobpTZyTm4MofHRP326VMX2MHKiYSRrYtJ5g8weYhuWw3ruRBTkiE0vt3FPn4+ISB1khH71Ad1ygUILLhhWyKxxlR/cAY50OSs0Sr/f5uaKw8KBKJ4CsqCio7Br0EIrG2KmoDPrDlKES0RsZn1eEEZggCORYLJ/pe2X/AnN0QQEAsWSS0qyhXs/WLoa7nPsmv+zeh8GBtFO6HAWLcd+isWYk1I5CDfXT2NmIZtcadCUldCQacaoViCmFaTOr0cgbkMWxJJMSqVQMnU63z4eGVqslJyeb7du3UVs74jM79gwZMhwf1NfX8/ebfsQ/7n4OyfjxGc719fXcdtuP+fWv/s6q1XU88vAzbNu6klPG5zGjsZ6b797Bn76ZRcXk8dz1p3/wnTEl6M48ixONEnqDE82cBWx/4F+AiLh+M2OdTgbDYWxG455r27GcFDnigrG9r4/nUimumzSJ/rVr9zx+PE5LHy57J8eE4nG6g0FKNRoa+voosdsJp1K4hwqoLzOiJFGcnU0qlWJXXx9OsxmHxXL0N6woxNetQzKZ0okuh0sqRWz1aoyzZqGsW4c4ahRCTS2MGwfRICx9BoZPg7APEjFw5xGSJnDXDf/DmpVbCCdgq0GlMwK1Gg1XOs3MKsxDIwgIgoA/GuM9T4DWuMgl2blUuBxoDjKMsTuBJOzxUJ5lYGXvAEJEZqmiUqjCmijMjcawSHEWDXoPciAfYpA0jCnMJQ6wd+/e3oqZzwuVw7E47UT1CqP8Pkb86nLu+tNNjDQKnJdj4I0Q+MJxrizPY0dbDx3+IPWBON8dXkVrJMYTLT1cnp8eTNmbaDKJLxIlCtQNeIhHovi0RiZLGioEkbMqqvacn71vpmLJJL7dSu8hEIAcq/VTFZYGrRaDVou6u9czHsccH0p+sZkRtNr0cj6AZnc8YBzKS9LT0f196Wzp7jYSMS+v//MHnH/5NynIcbO8u4OGx2/nzDPn4rPWsHbZqyD9i4cffo+ykjzmzv86w4ZVUFVVxerVq+nu7uLfd/yGH//0WhwOJ7m5eZ/4uDJkyHD80dXVRVfTZuTeVVA2/2OfPzAwwKZ1m3jz4XN47/0Uge5d5GpVdmwP0NoA87ItzBg1lVsee55ZRgF/QS4bVzxN1dzhvLhBxuULsH7RayRjAudgwNPbSxwocLlAEIglk6yXZaaYP754PRp8IoWxtrQUcS8F4Hielj4SRNKFo02vJy7LWLVa6gYGMEsSgiz/V/Q6CkNZ3VqLBQkY9PuPem9jorERY20t6hFY6+xGzMlBdLtRGhpQRw7Hs/51Yt4+bl7ZzOVCBwX9u9CMPQ136wZkg5UNL/yV597+AFMqne+sT8AkDZxvN3FSSSGQXoLdEQzzWksXpRotVxcX4tB92EOy/9RvSlFpHvSAqrK018OKcIJa7yA1wAhVIM9uZ6TBuE8H3KGWaA2ShM8fotHrpdRqQZNK0RWJMkJWEFUFUY7BqLFQPRzBMROT2Yh+/tk8cONVZOcUcW5NBbZomHEeP+PMEkZRIKWobOvzcH5JMbF4goUtXVyeX7BHWdytJDYNeljkC+GMRDGoYHc4qc1x4jAa92m6VlQVRVVpGhwkFIuhAB8kk2TFYtg/4ndVarEgyTLt4TACUO5yYdHr039PR1hECoKAJAhkGY2oBgNSNIo/EMKqS6LqtHuyttFIUDEWQgNQvwX0Wpg6Cypq0CZjXHTtTThdBno7VuIymHCfcD6PPvk8Ee8b5Bn8lEwfZPu2fq45cw7PPvNnvvc/Z3DzzfDeew8QDnWTK9nRyjJCqg3IFIwZMnxZSCQS/Oc/j9LXPYDQ/zYhdwnLl+9g7txT0Gq1B1yzgsEgjz76CGrIyx3/7uamM0ysdel5YlGM7jCcmwe/OH8Grslnc5nByh9/8DPaN7zPVePL+N8HN9E2qDDPtJk5J0ylpXEtL3SGOQOBvUcFfbEY5liM0qEVl8+bT9Sotn/iC/CFmJY+Enb7FlbnphMd9+91TAE2gyH9vC8ZAuljS6nq59PbqCjEWlqOfFoaUMNhEm+8gb6yEmXjZlY9dAeroiEMMpiydbSKNuKpRfg2LWVLQMGgUbmq2sbWZj8mjYFaOYaYBNdQ/RZNJtkRjPB85wCX5xdQaTIhicIe5Q04YOp3lyrgMpqpQCAPLdcV5pBKpVBUle3RKEGvjy349uyzy2BgVF56CtigPTAL2WYyUezKBtK9ewUm44cKmihib22Hhc8TO+sMuls9hDQCsYqT8HVsoCfZivO0q5F8j1LoDxO35dDXF6FYK1Nis7C4q49SjZZKkwlFVWkNBNkwpCR6tUbmWaxUFJQgwAGFnC8axZtMssHjIRqN4jEYmKDRIAInWK2UlZR89I2UqrK9t5eUqpKl0xENh6nr6GBMbi4JIGu/wvRwEQSBLJMJq9GIqMh4B/yEVDntWykKaaPzSAgsevjq1SAKhMItyFoT64Ih3vj3rVRlafj69yfwf3+9nTknVjPv2wX86f86aLi3k3HZuVQUTePnP1Rp7mpi44rlnH7aWfzlhr/zt98uYOKss2nq6MCRk0R7nDSnZ8iQ4dOxcuUKencuIsuk5f0NKtueupZzvvcVHnjges4++2fs3NkCwPDhw0mlUlxz9dcRA9uJJ2MUaqCmdgFr+lx0e+/jhCw4xSGR7colsOVpvv2Lu5ksQLVDw+Pr21CSCoUKjJbBtmEDb/TITEBgRG4u7V7vPtfVLI5dXOAn+vTfP/EFvjjT0kfK7iizvXsdu6NRPNEoI7OyaIpGKc7Kwjzk6/hlUh4/z95Gye1G7e8/omxpAKW/H2XECISRI5G0Wk4w2qnQGSnWqAzKMQJr1rPijbexakROy7fiHFNFU1kRO9uXc16eg/XtvZRKabUwLis8uKsLMRzjiqIiSgwGNnZ2oagqH8RTOCNRHBw49TtWEMi2WEgoCtt6eljq8dAXDlOqqkSMRkYXFLB3SWjTatnY04MvHmeC201zIICk1VLscHzYvzj02qMLClBVFc2QgmbRSrRtb6KguJCF13+HdQMhTijPpvCS73H/3fcw7PSJ9O3axq7OTr5z2hmEamfgafwj41ICMUVhU7+Xs/PyGYhEWDLgodMfZqzdcVAlEdIrCnFFYXlPDxsjEYpSKWxOJ+MdjoM+/+MYtdedsS8apcZgoD8cpj8cZrTTSb+iUDh0Ho4UURBA0iDqTBSJAmo43b8oKArojJBUoK2F5OLXWIoHfbGW3z+5kURM5Hd3fpsn348y2Anaxh6aJ1zO2Pw6rJY8GoIOAqvfIO+cE7nl76u5YkyIV999kGw9DNOq9LTu4vFHbuW3v78ftNkcX+mvGTJkOFLq6+u5+eb/paPHhzGm8twjT/KbH89n44bH6NzZwNIlk7j1z7+kssrID6//E/c/8i4NDcs4eWwuzY064qLCXx9bSl27h/k2GGsCTVzB8+5CDJtNjDJEOKvSxLj8GnxvddDc3s/52XZ2efy83plgnCwwE3CbTNgEgQ6/nxKH41ifFgRVPfgn9KSPMIvUSBLXnnUW9pYWgu3tex53Vlai6elBL4pfiGnpT4oCoKp0h0LIsoxLFGkJBhnpcn1plUdVVSGVwuP3H53eRkFAcjgwu1zIO3ce0Y/q5szBUFGBRpYRHnsMYkEwalHUBKpBi5qKwthRaE6Yx+CoiXzv+h9j2bGZiVoBt8FCpQRCSsCrCgxEFea6nGwOhNg04KVoSDm0GI2UOxx7SoG9l5S90SgbAgE2eL3km82UCwLD84Z6ITlw+VkFlKEl7WAsRrvXyzCLhYFwmGKnE1GjOWghpqgqwVgMJRFH1SgMRP20pKJYxozj0eZGnJEo3yrSs0GVmX7eeWSPH8lDz73OjtdWcIneyNK4SI6kQxuL0R5PMVZvYmZeHjpRPOgNwN7HlWc2c4LDgdNoRPoES8gfxe7l/S6/H6cofux5OGwScRBlBLMxrTRWVkPzVlSHg8GOHdSF/TyggtYPZxRbCZeVEBk9jmBXO9YSD0XVp7LymX/x9po4pw8vxlBu495ntlBhgTITnFRq4MJf3sSi5h3MGDca88RLQTiW9/8ZMmT4tMiyzMMP/5t77rmFC845nR1LP2DT2rXMzNMwvKaYE79ew71vN9KzpZ8cjcDP/7eAG1+xs2bpKr577qVMLs+mxKTn1/e9wuaVW5lrgVkOLZU1teRPnUbA202b1kjtvHk8+MLTbH9hMRVhkVpBoD6UQlQEZmfnUd/Tw5zych7v6WGq2Uyl00lLMMi67m5OHjbskG1NG7u7Caoqo4ZWRz8JYw/xGfyJ1xf3TnzZjbe5mbyaGujsPKJ84C8aIoAgUGi1AhCIxynS6eiOxQ6qPMKHSuUXlaPe26iqIAjEBwbQulxHZrEzNGil1tUhSBJojBCNIhp1CM4cGDcWnFnI+aXsXPgYJ2jDTCi1kyWDHFGQVWgKRdiakjjJZuPRti5KRS1fySuk9BDTv3srb3WRCIVaLWcUFBzy+XuzdxFpN5mwm0zEkkmydTpaBgcpslgOqrSJQz17GI2QiOPUaMgmQWygn59OmEJedxPGCy+nzNuGPOMMbn/jPbavXElYgMf9UbKSENMpVOlNXOm04TjITeGnOa5Pyu5zUexwHPQ8FDudn+yFdXpIxD9UGhsbIKkgpBRMBhuCP8b1Dge5WXFaZQWrrPL0S88wfN65TD/5RzRtepl3NsWREir129sIb4EZVhifJbCg3EzZvLPQTJrPybMKkeXhRGMqqhrllVdewWw2cOaZ53yGZylDhgyfB6qq4vP5CPm9uHK01G2ow6QqTDKqnPO1K3hw+SrG2aP8O6Cij8VhRYDsYAtfP11ixohu7n1pBadPn0Nr6y5KNFAkgSgZcBaVI05dgL16PHarkYCnh/cXXc8JepXhNhe5aJCbu1BklW1eL1ank0AshjkapbSwEFlV2eT1YrXbj5l7yycqGA+W+AKgKgr9ra1UTJlCaNWqPY9/2dnt22g1GMjNyqIzFEKWJAKRCGu6u7FptYx0u7/w09ZHu7dR9vnQlJai+v2HfcMhmExoJ01Cffddkl1d6KdNg/ffB4wQT6Ba7Qg6I8w8CdGoYdLpX2FSbT5iYxMJX4jw2ytZG5RZF0kx0Wbh7cEAl+cWUGk0HfKPUlFVlvT3s97jIc9s5sL8fFxG4xF7De6NQatF1Wopy8+nc0hpa+zqOrTSptODMw9Hjh2hYQsEfPDVa+GMc0n1b+XOp1/n7nsfwp1S8aUgS4Ya4IIsKwtcroMe2/6K4mdxXEfK/uehQKtFicUIquoni8gaKhoJDcUFavUg6kmU1zI+IeMw69gyGGGyKwtJFfjtRV9Hc/E36NJoef2ZJuSknm9NcdMVM7B5axOnuSSm5ZvROfOxzroI8LJpu4Y3Xn+BtvqF7OxNIIo2nnrqps/83GTIkOHooygKkpzk/t8t4JmnnuO3/3Mm993xMou7krx13Z+JJVIYRIWzrRpOnVCOVzOBr104E3nbn3lv604Wr21h5VstGGMK5VrQpiBP1JCsW4OxpBhFCDAQ7+TehSvxRVNslrWcNnEU+RXD8N7xOP2DEQyxGDUuF5DuWRRJF7K6WIwKu/2o290dik/8Kb9/4stujE4nyYGBT7lbX0wOpjwWu1yYNZqDTlsDe4YLvkjs39tYbDYzoCifPpNalkn6fGjsdggE0obbH7cvOTmIkQjKli1Is2al1UYY8t3TgT8C7hzILwCrFbW/heeDDhqXr0TtD2ELBtniBbcMTR4/lxeVUG06tGVBXzjMIo+HVlnm7M9YeUtnIouHVNoO6O0TJdi8FdWkRxD0MKyGlNnKfbf+h3/e9xxyEqwi2BVwAWc63AcUi8dCUfw49j4PqqLQPDBAlkYDWi2KJB3534tOD8kEanklQnsLiBL2xkbIsuMLBbEhoZU0qLKINpag9/U7uHvRKlauaea267/K2NEj+dXtD+KWwAqs6opwVmU2IX2SH//wL+jkzZhcKuPHaVGWw09+9UeWLx9kzpw4Op3umJ3HDBkyHBmyLCPLMieedDZbtwSYvqCMzQ1tkErhV8BAgskGqLZomVNswzJjAZEzz+eWW+7Dmswiur4ZXy+Y4golWsgTBFxAjzeCS6en/qmnqH/+MTSaOA6dyDWXX0RJ73ZyvvsbxKWPYz9lNqzaRu/O1nRULOyZkk6pKu2CQM2xOz2fLuklMjBwgOdisLOTVCRCYUkJkebmT7VzX3T2TozZf9p6R18fXZEIFTYbTpMJRJGsL1jCzG7fxu5gkHytllQ8TlhVP9UyteL1EgPMZWWH38vo9x9cjdTr4aQFYLLCHTfi1SRZtPwN3vXE2diTRJTTA9nDgdEC6BWoMBxcxYolk+yIRHi2v58ZBgPfLio6qn2qh6U4DgyA1Qm1FRAOEl2zgrdef5xqXx8OQSILmZFAQIWvuHIOKBaPB0Xx4xBEkQK3G0FR6PP7icoypW73hzcGh0t+2sdSTSQQBgfA4oTqCmxN9djMFuKREB29rVREQmz2D+LrC/Ajp0jryuX88G9PkC/HOc2hwy3BBKeNSMM2Vv34fxkjapk7xs4bwU7WvSNxQlkFxSUpVtVv5N93v8V3v3cTWu0X6+86Q4b/RmRZ5qGH7uO5Jx8i6KmnrMzFpOmVrF+1AX8SLCoUaqFWC2IiSXtUS63WzHs3/oS2Tet5cwvMNCo4EzBSA7N1ArOK8tCK6V72hj4voUiUUVlGtPNnU79xLatffJXSs4ax5v1X0JSOZktnkqWeN5kAjFJVdsRiRA0GBEFge28vNpOJ7I/xYPRz9LqoP3HB2O/z8dTKlVw+fDjBpqYPv6GqqIDX48HqdpP4L1Ub92f/aetBUaTUZEIE1vf3U2214t0rYUaFL8TUtSAIezKpm3w+bKKIPxKh6BNOuh5pL6PgciHYbAf/plYLNTXgzIIlryIkA4wpH8eY86ezaPkiXnxnLaM0kKNCQIbJ9gP3WVVVoorCg11dEItxcWEhlZ+TaerhKI6qICBu2YZXSRDf2cC4K7/Oy4IOSV7OeU47nV4/pzpcLBha3kgpCs0eD+2xGHXR6HGhKH4coiiCKKIajeQIAko0imBKe0ge9j4PDKQV6xwnGLUQjcK2ekQ1nr6x0BkosmsRY0km6Uyo+hDRkIp3dRPfNwiU2M2MclkJRiO4DQYSOUWcN/Nk2LaITTGVxjVBCsUU54+z0/vsfTR3djLgjZFMxpFlFb1ef9ye3wwZMkAgEODBB2+nxLydGaOzCWl03Pv3tyk0q6gymESBU/OySAZ8qIAnkuKVJ57l7e5e1rYp5CjgSIBThDJBwCWr1Ld273l9URCwG/Tkm2w0v7WEBVYTOo0KtvlYSkZyw09/xoUjcjGpCjIQVxTqfD4W5OUhCgKKolDCR1/zfLEYW+NxTsnOPirn6BMXjJFYjMLaWixu974FIxAPBCg96STiGzZ86h38MiKQHpeHtNw82mgknEjsSZj5oLOThCwz2u1mdxfocd/7KAjk2u0EEwnURIJUNEpUELAN3R0dCbt7GeWGBsShoaGDotGgnzoVdfXqg3s3nroADHpoboT5Z+KwmXBoRRr1Jm7+w7/4sVmLLpYkWwG7QUsqFkFR1T3F/d69ilUuF3OLitAcI/XtYIrjquZm4rLMmJwcmj0e7LVVrH/jLZ5+fxUTkwqDHj+VWj1T9Hpag8F9/BPHazTHpaL4UWQZjaCqxBMJenp6cFgs2IfaPw4LnQ6cbtSgFyEaTd+cDKuBmB+jZzB9g6G3Y68cxvyt61A1gFaDmozSHY2gV1QSVgem8uGYJs8GnQZGzGZk1Rj+MX0TYqADzciZuLs3Udbo54pLzubtp//M6i0fcMOfXkWn+wQ9mBkyZDjqtLe3c+WVX6W/txmtS09Oykxd/Q6SQZWBIJxughqtyrL+MLNtVgLBIHUtAyjqAINxOEkChxYqrVZciRgJo5mox4dx6KPPIEmMKchF1OnwxWOMtLnQuCwwcjSpr1zBH275M3U7+8jpG2R7COwaDesHB8kxmSixWA474SWeTFKUSmE/SquVn2pJWpVlenfsOGBAQZVl2letwu12I/h8qIfRi/bfyu5M670TZpyCgE4U6Y7FaA0EKDAavxC9j8LQcaDX0xuJEI5ESCYSaPV6rEOJHoeFLBPbuRNJELBUViLvd0OyN2owiOrZv5MWGDkSTj45/aG+bjkUl0DJMHDY4T9/42fDslhgNfHm9m4cgorfF0OSPyw6U4rCWx4PWyKRz7xX8ZOyv+JYIAgYJInBcJj+RILmjVvwA1cARgQEGexynLquLqKA5VP4Jx43CAKqTofJZkt7W8aHFMLDQZbB4wNXFmookPZn9HrTqqPfl/6+JCHsakYaPg7qN4AWMFgoKSjBB5QUFEJxGbhyoaQKLDYknR6hsDytgtodhEv8XDr3KhKSicFFF3HOySeRiLeRShVgMh1BgZshQ4ajTiKR4I47/sQpk5KMuO4m9L11TCiyseQvt9PYAzlaHZPNBhZ3+jnBZiClN9I6GGa8M4sBr4+gJHBZcT52VBoCITYloERJMCG/aI//rkEjEQyFMetkVFFFLKmCCRPBaqZ7zaPEN37A7OI8mlt6OEGBWg20Auc6HAiCgDcWw3SYCS9H8/b/UxWMK7ZtQz96NDNMJoKdnR9+Q1VRRJHw4CAWu/1LkfzyeaGXJLKH1EerwUBOVhbwxet9tJhMmIxGevx+7LKMNxSi1OU67KJLEASkmhri9fVIkoRwMBVMEBCHzs8+jBwJ13wDersRtAJ4B+GKbyC7s1n59D28vHApFdE+LM4s8tQY4fi+/Y+N4TDvtLeTMhj4ZlERxv16FRVVpS8Y5HgwjYrKMpJev6dHdm/s+xWGn7V/4rFCHEp2UVWVeDSKDhAO92/A40l7Mk6ZBivfTw8O7c7oliSIhGHGbNCLsKsBakdDMgzjp2HftAI6W2DkRCiphaJh0LaalM7GmwsfZNKIMXiKZ/H8c4v5ybXj2bJrNWd84+u4XN/H71mB05kARh/FM5MhQ4YjRavVcsMNt6LVCIRDG1EjFkLBd6gvqOTikTbKL/8xSx66F3/be/iDQToGw1xQWcZTrT0U6y1cX+QkIss82+OjCJErCgpwGdMJYXuTZTQRiMWwJOII0Ths3gA//CW5CQ+547ax/qk3qFBBBppTKUqMRpxGI6qq0hCNojMYjrlI9Km9UIaXlsKuXQc8HvN4MH0Jk18+T3ZPXcPH9z6GkklK7Pbjxvdxt3JaaLcTjMexCwLRSARPMklBVtZhvfGTTU2QSqFPJtFYLAcMWGlGjkQym0n19X34YHU1fOUcWLca4cxz0idx7Hiw2dj+7D/5xo9uwJmQObcqi42iBZNbodKaw8bNTagy7AiHeaKvj7Py8qgwGtEIAg2Dg4Si0T0tAc1AbzhMyXHsM5ql0zHa6WTrwADKEe6nRhQZnpu75723P3vSaI41goBiMJCIxdDD4SmNqgo1tbBjOwgiDO7ubXSBUQcDfUMT9qT/63LDttYPH6sYmf5KhCHSDRVuOtbdT06xidwRRUiGKN/+zvfRuWYxyX0iaYkSXLlzj8YZyJAhw6dEEAQMQ8OONvtUXnivjSXvNpHn7yTvhzfyr2WbeOetJQwTNajxFNNddlZ1dFNuMHF2QQ5v9w6yo3uACQ4nJ+ZmgwoDkQgdHu8BbWQCUO52Inf3I2aPIpWKcf+rb/G3B9+iJKmSUkmrkgYDJ+TnIwkCKUXB4/MxYqiX8Vjy6ZakVZW6TZsYIwjoDuabpyj019WhN5m+1MkvnxeH6n3sCgYxazTHre+jdeiDvCUQwCGK7OjpocThQNJoPt6/Ua8n5XCgd7sPmJoW7XaUdetg93tLEKC6CgQV4VvfQ474aXr9BTRZWWz3+Hj+vkdRwzKXGUHsDyEFQ/RIMsvagtQnVYwyVPX0cLLdjkUQeKu7e0/P37ihzGSAfEFgzrBhaI+HoukQCECH30/4ExS1RQYDO7q7aQuFDvieQZIYO5T9vPuVBSDHaj0mFzNhaBjmiJTGVatg1Agwm6G9NV1ozpgN65fv+7xUCjZvhBlzwe6CihGw4CIwWyAZgVSSnv7N3PVsPeedfwVywclEezZQXFwB6b056OZlWWbnzp1UV1d/2sPPkCHDp6C/v5/GxkamTp2KNHRT2LSzkfknBMgfn4MpT4NzcDGnTajkjHNO5dlf3YVbUNkUTfH1HCvLuvup90X5blUVelGkLRBk3YCHtliSsToj+4f5Zel0pPwRFg0OkN/Vx0tvvMXKcIKShMpEYLLDQUcsxjyLBfvQ6pAnFqNdo2HKZ+B1/Gn5VHsgKwrbvF5mzZ9P/P33DzDq/m9JfjkW7N/7CIf2feyORnEYDOlpZo5d72OBzUZclrFrtez0evf4N36s4iiKRDZvTnva7f9Hk0x++P+CgCrCpo3vMryikNtv/wfrX3wJAykC3YNka9MmqAMJeN8rEwPcEqyXFfpS8HW7HXM4zAqfj/xUiiyHg9F2Ow6D4QvZ81ficHyi/FFVVUlJEtUHGSgxSBJ94TA7fT52p4qWWCxIskxHOLzneU6TiSK7fU9RKYni0btxOVKlURBgzFhYtTT9b1mB95dCfjb4vB8+T5LglLOhpBTKKmHMFNBI6SVtgwElu5N1r23hxVfX8pUFl7Dmg6fIK5wAVH3k5t98cyHFxVFkuWLPh1SGDBk+XxKJBDfe8EuqKhSMRh1vvPEG8ViMRYvf5n2bzG9+fBLLNr7JFd8ZTvtLHpaseYfORJKFPV6mZmVh0OvZ2NfGefmFaEWBN/sH2dEzwFi7g3n5uQdN0gLwhMPUuPJo9ngwxOKUCQLnm0yMzcridb+fYqORyqHrtqqqLPd6qTYYsH9WqWqfgk9dspoNBoRQ6IDlQvgw+aWovBylqSkz/HKUOZTvowK4BIE1nZ3kGI04TSbiikLWMci81ksS7OXfuFtxrM7ORjrEHZQ8OIiq1aLGYugNhj1Fo9LdTbKtDclkSquMbjeDba1En32VJXEtD/7rPsoVhWIR+mSIxeAas5UJQ3ZFnkiEjfEUbqAE6Pf7kcxmvuJw4DAYvjQ9f0eKIAjkfsT0sc1opGDIpmc323t790z0i4BJFFnd0oI/kcCp1zMqL49AMolBo8ExpJJ/pvt8JEqjosAHq1EDQQRBBFUBqw2KSqF1J9iyIBpI9zdm56SHpUihhDoYaO0mZ9bpDLY8S/0dv+LpDRrOHFPFxCmTEbUGJPvYj93XefNO5p03H0YrLkPUFmSUxgwZjgEbN9aR6l3KtGITD93yE3Iq3Xzw/iJcJi9FWjcrFi4mp9jI+uxL+eurDzOqL0FQBpsqMCLbxd1NHYxwukmKIvdva0DRGvl2RRUGUTzo54YvGsWbTO7jVjHFauUChwNRq+XR7m4qTCbmOZ17BJS+UIj+SIQLysoO65hiQ19Hi09dMB7Sj3Ev+nbtorCignBDw6fdXIbDZG/fR2DP9LUIrO3rQyeKjLLbj1nm9W7/xrgs49TpiMViDCYSFA7lZO7/BydIEim9HmIx+uJxihwOUo2NaObPR2hshFgMado03CEf7l//iXe7OslTVb7ttNPk8WHRGrjU6cKi0dDh9eIDNgoCOtLFIkYjpzkclBwH09DHO6IgHNDfODI/f59/x5JJcl0ucgGbRsPGnh58iQTjXS52hkIEEwnKnE4sQ3fNn4kCebhKoyzDoBfmzIE3XoaUkvZpfL8Xps2CUWPhuYfh5LNgxjwIelA61vP0+mWM3/Im2e4sBG0V2QVX8qPZ06kqcaLJMiBq3Ye1mxqNhuKSct544zUUQeX662/+tEeeIUOGI2D79u3c8rdfMOvES/n9g/dx7dQQwex8XupKEo+Z+e39t/HIkw+TEhvpbPw3K9cnaNZAbhJmSCpL2roo05mZbDbxcGsnJ7nzqTaZMOwnwOydpLUxEqEolcK2n1tFVJa5u6ODQoNhn2JRBQZUFSugOYzPJEVV2ez3YznMGYFPwqcuGD/KjxEgFYmQM2UKwl7LVRk+f/SShN5kQgXGGo0IcFxkXu9WHD3xOIoosqu/H6fJhKTVHpAYI0gSHlFElCSUWAzRbEZpaUHeuRPtueciajQw80T6ttfx9O9+xomiynMDflwyzErG2N7VxXaNhpEGA7pUilqTiSWRCNONRqbn5qL7AvgRxpJJfLEju4e0H4MldYNWS97QNlVgVHExAKF4nD6vF6tWSyQSYXV7O3adjlF5eQSTyU/dC3nYSqPFkv7aTSQMs2ZDWQXEo3D5t2D0BEglwGRmR9tWYnXPU3TyMLpfvoG8+d/BecUvQNODotbh31iHY9L3OJyMhW3btvLD639FgUXmT7c/xYoVK/bpocqQIcPRRVVVptaU0Lz4Acryc/nbyxsRX9rJ/Hw7V/ztZlLiClpa6/j1hT+gTbuFp15oxd+nUiRCD6CNpbjKbWWxP0ipqGWUzXbAdWv/JK0L8vNxGo37rFx5k0ke6upi2H7KIqQzrbf19lKdk3NYXrmqqqKJxSiz2Y6/pJe9OZQf4248TU3EBAGby0UiMzF9TNnd+wgcUeb10VYerXo9ql5PUKulLxrFlkzij0QOUByzDAYwGIjH46SCQdpXr6bUbEbb2wsBP6tXreCu66+luTtInQKTVDjJ5aLGYEAAJmg06PV6Hu7oQAkGuaiwkGFHYYn0k6CSvkioQ//f4vMRjkb3fF8B1ssy5liMg5gJHRSbVssop5OGcPjDaWlBwGk0UpiVhcqQp+dR7DEUYM8FL8toZMxQb48vGqUyL2+PAmnRapFkmZ5odM+U9idSHo9EadyboalotbIG2WJBM9AJdjdqoB/7hsWcc8617HKU8PpTP+YH45og3sGy5b9izvyxOCb+CkQbsqzw6quvcNppp6Pbz3ReVVW2bt3KP/5xK2aDRMDvZ+nz/6Ri3EiSyXFI0vHxPsyQ4cuIqqps2rQRvV5PVVUVK0onM97cQiCosmGtipRUuaCqiP7Ni7j18SfweVX07W3URbu5YKzK8vehJAX9ssB3Sgvpj0RoDUS5qqDwgGJxRzjMy11dFH9EklZUlnmoq+sAZXE3SVXFBziP4LNX4OjFAsJnVDAe0o9xiLjfj6W4mFQ4nM6APUi/Y4Zjx8dlXodTKUa63YRSKfQazVHzfRQAm8GAVa8nmEigJBKHVhz1etp8PvK0WpKqStfy5WgiAf7wxlsYQxEmajScYDExLy8PvSgSS6XwRqPUBYPUtbdT4XQyu7DwmKec7K0YDqoq23p7sQ1dKLx6PaP3ms4GmGI2U1pQcNgXBUEQ6NxvWloEjILAB21tBBIJHHo9I3NyCKZSGDQa7Ido1v6s2b2dvRXI7b295BmN7OjuJpRMfmLl8WOVxnAYXl+ImmVPZ0sD2O1gtdHctJa+tiYqirS4NRaE/FryfvIo3pZFNC19jq+dVk3IXYqm/j36d2hpL52MOd9LY+MOZFnmscd+S3l5Kbm5Bfj9/rShvc3GihXLefDBP2Mx7aS9CTTxCGcvGE9T1MG/7/obP/jRbz/9Sc2QIcNBef/9ZfzhD5dx443/4K9/fZJnn72FyrwkDtWCpKg4RXh2+RbaF29Bm1Q5z65hMBxnV4PIwi0iWUmFpAInmowUabU83N3PGRYrjv1Wb3aEwzza3c1F+flUHCJJ66OUxd2s6uvDehjZ0Z8nn9mc9qH8GCGtQIb8fowOB0IwiJopGI9bDpZ5XTCkPAaSSUZnZe2TeQ2f/dT17sSYj1McC+12VFVlp8eDTRDY8dqbXBGNEgQKAYeqUt/djQysTaWwxGLoDAYW5OV97r2KqqoiD+Ws71YO91cM/YJAdU4Ojr3U1I+1HToMiu12iu32fR6LpVLkiiK5gFWjYVNfH/5EgrFOJ55QCI1GQ2FWVlrlO8rnaW8FcmR+/p4p7Zy9lEeNLKPR67EMmdce1ntub6VRFNNK4+5rT19f2uC9MBuWLU4Pt4wdD6Egmo5+qmur6F50C11VlciL7mPC5Tei9bYzY84JeFofYWDTEmZdey8XTjqdiLqL6//3d+TmtjB27DfYsX2At5+/inMv+il33P08p552Oe+99zqvvPIYpblw1eXXsHrpPbjFJB88+ldea4rwjV89CKjIskIymcxkT2fI8Bmhqip1dRu45eZr6e8e4IWn/sKSd7YyskTL9T+/gB0r2uiqW4w2AZ6YihOoNUCpkqLn+SVkVVSR6geDDG4BJjpd+GMxHJEoFQUl+2xrRzjMYz09XJSXR/UhCr2PUxYhvZy9ORrl7Nzc4+o68NksSX+cHyMQ8/kYEAQKS0qINDd/FpvNcJTZ2/dxt/K4d+b13lPXRyNx5nAUR0EQyHc6CcbjlEsSPq0WUyJBPJUiutdrTbdYKC0qQvocTad90SixVAoVaIhG8fl8xNhXOdxfMfy8FE+DRoNhrx6+kYWFAIQSCQZ8Psq1Wta0tZFjMuG0WEAQPjflcfeU9t7KY5fXi02W2djVxeicHESt9rD2RxBFVJOJ5LBhaAMB2LslJjf3w/Ubkxk620CvpaSsnLefvo+H3t7Ft8Z0UnrGApT+heiUGOb8b+KqSJGzPcWWtc+xubmehc/dR5h8aktHMdh/A1NLexiepeXhB37FSJcZl6WLurq1RCNQ4LTw5KMvEPDFmVIkMaN6Gid+52p0JaNJpXz8/bZ78Pla+e0Nf0er/Ygc9QwZMhwW8XicBx+8g+9eMpJ3Xw0T31DPLecNQzfrFL753cfQJ6JEwiq1EsgCFJmNVOlhjTeKuLmVTetamSyJjNGJZGuMlFgtLO7pQ6837vNZskdZzMuj6lDF4mEoi6qqsikYJFejwXkEVjoKEDjis3NkfCYF424/xhlz5pBcufIAP0YAhtQVz+BgppfxC8hulWn/zOu9E2eCkQj5Q0rWZ6lKHY7imGUwoOr1aHQ6DJEINlEkoih7lDKVoxeNJ6vqHk9CTyxGp9eLH9gSj1OWSiEAUYOBEXl5GPjslMPPkj09hgYDWXl5exRIPbChp4farCwCkQiFQ/5gR7PncTd7K4/FLle659FgYCASwZZM0hYOU+BwIIriR98ECALK4CBKdzeiyfThhHci8WHii9OVXpJ2ucBiZlx2MTfNm0zRiSfStfl1wt4EckUV9pyRLFv2LFo0/PzXj2KV2/CFNPzphrOoHwhy+z97qc1VuOveDr713asZMaqCHRuf4NR5k+lp2ELbpgE8UYkSA4yxCGiza5DtRhoaV3L//e+ydtHjPPTwz/B5+8iy56DVao8rhSFDhi8Ssixzxx3/wGaJsa55GKprJQ2bQ9zy0A5K3mrmlu99h9eXrmR150oMQI3FxGXD8nlyZw81LhduFDw9fs7Pc7Ki18sctxMVleCgh3E5H/YueqNRXu7q4qL8/EMqi6qq0hCJoMRizCk8sO9xzz6rKh6fj1FHmOzS6vfj0esPGBb9LBFU9SByIDDpCNWEbLud8QYDE6xWEh7PwTcmSbiKizGEwyh+f6aX8UuASnqcP5xIICeTxGKxPT2PR2vaWlXVPYpjKh7HaTKREgRsQ4WYoqoE43E6fD7KTSaafD7issyYnBySQ/v8SXv19p9SjgGbvF50sRgq0KjRUKPXYwUK7XYcQ6qrcLzE6R0hqqqiqCqhRAI5HicYiRBKpRiZk5NO6jkGhuaKqhKMxZDjcRLxOA6z+eMVR1WFRAKdLCMajeleao0GRo1ASIbhlNPAaoIJk8FoAu2QQXciiKo3093dyNo17zNrVBcvLV2Nf6CdkuLZdC99k2XbU/QGVGaXpljUoTJKJ/Lbn1xF87A5rH7/Va657hfc9n9XcsrUyUS3ryKgm8Lddz/CqTkw64qv8cTi5Rhd3by3JcLl06eTP66I4vJq+iIlzD/lMrTa4y8rPkOGLwLLli3j2adv44S8IPaaXazcMpvn7nwATRyqrBpOOOkEnnhzKTUJmRqNwNXDy3lqVy9lJjPzc508uLmB6Xn5WASBjbs6ObumBoB3t+5gTE4heTYbqqqyqK+PvmiUs4qLD7lKtCMc5vGeHi7IzaXcZDrk52JfJMKTPT2cn5d3RGbdG7u7+X/2zju+jfr+/8+7096S5b1jx87ekyQkBAh7b2hLaSktLW2BLjoopfClLT8KlA2lBQpllT3ChuyQkD2cxInjvYcka52kG78/ZKdJSCDDCaN+Ph59lNiS7nSW7t73+rzfr1evrjO6bzXwcBi7V6paPwMmc3QGg7QMGcLxQ4fSvmLFPh8z2Mv49WP3xBndbN6j53HvaWuxzzD7sLe5l+LYFovRHYsxyuvdlRzzKaVMFOmMxdgZCiHArl693mTygLe7rynlOOD0eCjzeBCAMQbDl8KRf6AQ+noY+xVcRZLINBioCwTIt9vp1DTy3W6Eo6A49iMKAm6rFd1ioTce30NxLPD5EPd1whYEMJlIJpOY4vG00giQ6UffUI8QjUBeDrjdEGxGzSwm1F3HTTffhtMu0VRXy/iSfOxiGVs+2kZrzELV+lVc+8NvMnftW9z1XANLdkgMNasUmGHdx2tZ9MI7nPLTH/Dvp+9gyYZOPln1NicfU8ITL3zAMBRGOe14l7zGz0eNZcXWFrLnnM03LzqDDqOfosqJSEYVxK9ewtAgg3zRqKpKKBTiL3/5I+XlAn97sZbRmRGWbXgaJQ4zXGBFoe39jzhBgCE2yBKgIRJHjMucOKSQ2q4edIOFcruVd+pb8HjTS8jaXhpbQtNYGwhwWl7efovFnliMN1pbOTcn5zNdOQ432eVIn4MHdF0s0+VC/pylZjkYpFuSKJ04kcB+CstBvprsq+exf9q6rqsrrQSS7ksciISZ/h5Hh9lMjte7R3LM7lnV/b16LquVXJ8PSPfqdQQC7FNe/wz2NaW8L6PxryOCIJDVdyyLsrNpDYXwShLVbW0U9h/vo6g4CoKA22bDabUSlmXEZBItHqdXEHBZrZ9WdHcvGmOxdNG4bAUMHwqFxTByNAgxVjQ2seTZ57nvvvuxyTK5BoGbLzsZps7hzrvv4pc3PcR5V/yQX82KkZOVwd86vEyf6OI3M2Zx7z2PsqApRSK6hinTJvPWQw9hyFZp3dqKOwEf1DYzRtf4RaUdwSRSNGkuwhX/xylinOa179JjlSiqHI4oZe5aOldVlRUrVlBRUYHff2Dm4IMM8r+Koig88MA9vPDUnYRDrXRv0+jqBFsr+FUY4YNzM41EYilQQVBAUMFhMFLX1snxRfkYRJFIXGay2YDZYMAmxxnm2nfBWNXWRq7dTvHuvq67oek668Nh8g0GSj9nVetgk112J8RXrGDcVFuL5HIxNSOD+P4KR13HU1qaNvketNj52rL3tHU4kaA1HqcnHmek270rYcZhMh32Um3/8z8rq3rX43abQB69VzrJIAeO2DelLisKGUbjl0JxxGqlMxIhHI2iJBIYLJZPL1PvXTTa7bB5C/rQUhI7V3Fv7Wq2fvAx04od3Hb6RLau2cEntR30rN/EwncWcsXcuYj6FiQ1gSGVz5YGEyXZJVz6syt44YVn6E6qlFogLMOKRZ8gOD2YRDMVBsgFKiwapVaRfLcPSdIQs0rRo21sCnSw6f2/cew3boJUI4lUJkZTWmH461//ysYNC7jr7nuADI78ZWGQQb6aqKrKfffdwROP/xGPImOTRZySiblDdGb4RMSkTlZhJUVmA4GanQSiKVAg3+HEKUnUtYUwJ2PoeroPUez7qokIu/57bzRdpxT2KxokNY11wSAn5eR8Zm//wSa77E5QltmcSDAvM/OgnrevfagNBtlfwOmAFoyBSIRhs2djaGjYcxpxL1rXrMFfVIRN10ntp99xkK8PAulhGYfZTLbbvSthJhyP0xONpkPaB2jKel9Z1SsbG8myWslwOHD1TVYPMjBYDAYwGL4UiiOA027HbrPREghQoijEZRlBkvbcj92Lxs7OtNL45rtsadyOKMc4FoXjxw/jxvU7qKnrwK/oNG5r4apsG76Tz+S79/0Dkiqyp4QlS19g6pwTePC5RWz54CNEXaczBYLBwBALLGgO4myBYyzptsgyq5UKp4mOnjCevDzM4+eiJMIsWHADU+Zcg8HsoGqHiZtvPpdf/erXbN5cxxP/fIjFL99OR1c9W7a0MHz4iEGlcZBB9sGKFSuQoy8SDzuYM2kcU8pyOPuUuRgMbsLRBuS6DnInnIRStxT3qtWUCTbw5iO0t9LU1Eae1IYYCiNoGgLgNpkQBIEKvxezwKfURTmVYmUqxay+EIx9saytjSybjaL9KJD9HGyyy+4kUikKFAXPYV5Du6JRXurp4Zz9/H7ARzXDHR1YPmdZWtc0ooEAVr8fIRRC3ztxYZCvJSKAIOxKmAknkwiK8qkp64Hoddw9qzpDEDAArb29pJJJjGYzTrP5KzmE8mXlsxTHwr7J6qNBf89lYUYGSUWhobOTEqcT3WDY80ahv2hMpTANHYoY6GGM2U40Fiea1Nn61losKZ05gkClUWBGho92OcGqP/8JYXst4wxQvPETRl7/e2q2vkFlQCcwZDR1DR9iE2CMDXbGwSvCiXk+pnnS/pE2SSAUjeA2WfEMHYtqSvHamg+IREewtSVCVsZzvPKeETm2iPfflWmobcKqB1n0/IMkshy8tULhjjv+BgwWjIMM0o+qqoTDYf7y59s4ZmQFjz92C9PGZYPUg27wsGJNHZFILaOz7ERyPPzxT88wMRXh/G/9EkPFaNi6Ht+4OjY82cM0nwtkmdIpU3Ht2I6oabhdDjbVtTDdvmfRF4zHccsyJUVF+9yvnliM9fE45+TkfK5QcSjJLv1oDMy6Q0DXP7MoHNCCUVFVXly/nu/Mno1x7dp92+sA6DqaKBLt7sbh8ZAatNj5n8RpMqGbTIy2WndNWQ90r6NZksi029EBu81GWyiER1UJRCJ4+vwjv05DKl80eyuOOUYjqiwT0fX0svFRRDQYKM7JQUilUGMxInv3NgoC5OWhtbcjBoMIgoU8gwW31w9GA75AkDK3ndZYGJMuoUcV5ohhCi1mSsvyyZt7IsKIoTjFkVQV57Ht/vvILRnL8WYZIZ5i7doaxrosCHKczQ0B/DYLwwqyMVvsuI0GcPmpX/IxobpWrr3uNJpb19HRZcGtLKW3SyS2fhXHmGKkXCZOmDIbi6WJs7/1e+qbo6hqO5IkDSqNg/zPo6oqb731ONurt3LZhcdx7kU/wmAwg6ADGpqq4/UmmTTpFgI9O/jPk3/DaHMz4ZvXIQ3xgs1Cb6Wfu/9wP98+4yIWvvwiMzO8eFw2yPRCUod4nOF+HxE5gcOyp4pXbDKxLz2wv3cx12DAdwDnvkNNdtF1nR2JBLLZfFhFo6LrrOjq4pjPOKcMuMLoc7nQ29v3ad69O3JPD5bSUrRweKB3YZCvEHtPWe/d69il6+QeZCzcZ20n3+MhnEggKgpr2tup7PcX3CuzepDDo19x7E/icUsSLqMRXZKOmrIr0NdXZDLt6m10SRKYTP/1YuzqQlEUSCSQLBZ8di9uIJBKMiTDh8EgUWg00xLsYaTHS1sywaRZc7H2tqObbCidG8kZNom4IcXYYRLXXjEXIQq/u+tNelIQjyXwZHip9Hlxm4xsrmvDYzGRafIgdndQMuM4LEKCO26+nZOOHYuYacCimrjrKjetWpR/3QM/GGpF9Y8mmjWaDauruOvu32PAyvd/8CPiSROnn37mUTmegwzyZaWiooLTTzsNhJzdfioAIpIElcOmp38iqZx58Q/wX74a2XE2z7/4Yz54u5W8HJ1VNc1sDL3H+NpaZo4ohykzYY4dJdCD/dGHadNTOJQUYMZnseAyGhENBsZkZNAUDFLcN1DZj6brBAMBRufmfq4vcTAeZ1M8zhmHkOyi6TqBYJDKrKzDOrc2hEKkjEaKPkNAGfCCsbGjgxcVhR9PmkTnqlWf+dhQfT29skxebm66cBwcgPmfZu9ex9ZIBI8oUt3RQZHHg2QwDMh0tbOvOB1nsxHZK0Fmdz/HQQ6f/iQeQdNo7+1FVlW8djsuq/WoFudOux2nzUZCljHrOqLZ/N+i0WBAAXRZxm21ogkCkZ4Y9oRKr67idNrxOT0E4wlKLTbEndsJRsM4srazVYsT8Ozk53/9N7beOMe0PUB0iJG3l4SZZRA4uSCHbd1BNgcCiIJAWYYHt9NOVXOAMcoymndUsWzNZmy+XFbEN2HOnsTUE65l4d9/xsJNMskeyJ+Wj6trHTWpDH7y01sYWxIm15uL0vA8in/qUTuGgwzyZUSSJCoqZh3QY/3+AtDzgRnce8edrHh3CUXOHIJrQ2RqNkbbRS6fNxNp7vlwwsXQ+DGPP/I4+VqSkyZNRIklCVRtZ3h+NlWNbYwvKGZbJEJc1z9VMOqkfXoPZP0qqCjkKMou396Dxdz3v8OhNZGgXJI+8xo74FdFXdcpz8uju6YmfUL+DKVR1zTM2dkIbjdCNDroyzgI8N9ex1ynk4Sq4jQa2REMUmizDdh0db/69Hl+jjDwWdn/a4iCAJKEZrFgUhQ6entxSBLSAEdJfhZC34S8arGQkGUsgoCwu9JoMKACQjyOwWql0O8nFI/jVFJsqm9heIYPXdTBZKGutQu3WUKsrcXY28ydK6vICSYoN8IHq1PsXJNiGlAu6FQ3tOLyeBnpy0AUwCzApsY2RmT60EUThrpGLnbaSSlB4uvCHHNDHu8suoWldTJdbXBpLmSJAvVJePTJ+/jrb4Yy/90YUkcPx06ZjlR5Oc899yx2mxkEiXnzTsZkGowUHGSQ/SIIgIkzzziTy88rYcOK50lt2MbMaVfgrPAT74qweuVWJsV70Lu3kgglmDx7KuqIYlrmryIaCeN1OAkmEkA69arQYkHT9T2uFbWBAN0H4Keo6jobAgFcfatcB0tK12kSBIYc9DP/SzCRoCqRYO7ntLgckeDaZVVVrHc4cOblfe5j5WCQrmAQ636aRgf538YsSdhMJvIzMmhWVZS+6eqGQIDevnSVw6XfzzHH62Vkfj6tqookCKxsbKSuq4tQPM5+ApEGOQjcFgsuu53czEwSioIqy4Ti8c9/4gAiiCKa1YqcSqElEnve0BoMKGYzqXgcVBW31Yrb4WR6SRkGkw27KrGjqY3OUIyEKtFa286OjzbyrbjCdBGKNUjEBS4S7Xzf6+KbublU2h2443HcZiNumxW308n0sjJ8Tg+BYISk0cn0gjwyEJjmtlN9+/1UP9lGeStcnmPixBwvSksrrXffwQXWIrZvsbD8rU1MdVnoaFK49aZf8tSD/8fwoa0sXfoYHR2tdHV1HdVjOsggR5Pq6mrUARiUrRw2gs74KDbVBphz7WM4jz2RJkHmwVdfIWPSRFqW38lOh43eSA2951/Fm5tr+OOWT2jUU5CUGe71EEkkGJGTQygWozMS2eP1I/E44w7AKULTdWKyTNkhOnhs6+zEYbXiP8jex350YEskgs9g+Nwp6wGLBtwdgyTxw9NPx1tbS7i5+XMfb/H5sAkCLoNhMGN6kM8lnEyi91mmZJrNGM3mAfFz3J2EqtIry5iBWDxOhsVChywzLDNzsNdxANB1nZa+3sYsl+uo9jZCenVDkmUsRuOeSiOAomBIJDBYrbDb8oyu63REIjgNBlqCPWS77TTEIkgiSCYDnfEwdquV0QXpnFgdnY7eCE6jgZZADwU+F1aXY9e2gokkrjHjEZt3QKQHnHaS8RixeBjVJGFzWLAaAKuFpC8bbcgw1q1YjCM3l5KCDDpqVnHX2na+e9J45jerZHmiBLwnMHbcOOaecCmCICINQAvHIIN8GdB1nYULF9C4824uvfxZJOnwh+jSN1db8funEg5HuO66WWxcsZXc/GF8Z7oTreJS5A3v8tyqDoKr13BtYRYzyiup/mQdrngKNSUxMjeX1fX1lGdnY7Vadw2erG1qIg6M/RzhbHtPDx+Ew1yQm3tIrVBrW1uJ6jqjDjESMKFp/Ku+nul+P/l9wRtz6ur2+dgjUjBKosiM4mLGCAKm3t7PHYAZzJge5GDpz7Bu7+3FI4qkNA2fzYbTYhlQW+P+7Wzv6iLbbEZNJgd7HQcITdcRNI1IOIysqhRnZCAcpP/YYaHriPE4ZoNhz55GAEVBSiQw7lU07r7vzcEAuWYTglWiOR4nFI9T6nbQFo+Ql+lDNBiwGo3opC92opJMR0rYrP/dltUOpSXQXAslpVC3BVxeGDIU1nwIJUOhfDRUjgMlBhYruHyAim53kzKYMJgM9FS9A8Eq3trWyXGzTYStF9GdKiYzM5fKvvzbQQb5KrNgwUfc/LtreOChBxk+ctae39fDpLm5mWuuuYKqjR9QadXIUOCOG79PqszKlqdfYGedjUu/eTUpi5Xu998ivmYH2zbuYK4jA5PVjqYo1HV3U5Kbuyua9EALxnXNzUSBcYcQJCErCs+0tjLOZqPU4zn4Nw5s7Ohgm6YxNzNz17VzfwXjEbnaqZpGVSDAMXPmkFq+fP/2On0MZkwPcrD0Tz3nud27/BxbwmEy+3wWB0px7N/OsMzMdGKNLA/2Og4Qu/c2+gUBVZYR+5ZljoqCexA9jXsXjaIgUOj1gabR0N2F0yRRlJODrGvkGUy0tPWQ43HQqKsU+nzp92M0QzIBsTg4HekbYwGorYHho6BmExRXQlZuulmocjKMmwolwyCnCIKt4C8AVFDaEFK9mAqOoWrDA1ROOQfJfBnfPNMKgo4cXcOS519FGzmXrCw/Xm/GkT+egwxyBIhGo9x22//x+muv8NCD9zN81CwGMu0omUxy442/Y/26D5g7Lo8Tx6k89mgrb7z4AY22BO5IE9aMcjZu+ZD3NjTRtH4tpycEmhM6zWKYYqMZ0WCgxOVCSKXQ+q49BlGkwGJB1/X9ns90XacOyD7E810gHsecSFB0iKllOtCk6+QeoPfxEbudF4A1H3+M0eU6oMcP9jIOcqg4TSacNht+r5c2RRnwHsd+BnsdjwxuiwXBbCYuitR1dBDcqxfoSHIwPY37RBQpyMjEa3dDJI5FELFZrZRl5xGMq+Rigr7meABMZvDnQ1Ffi3osCmOngsMJY6ZCZg443OnCtbgCcoohuwCUCLo3jLb+JtBlOlJddNosYFxN5fjjEb2lYM8EWw5YczFnnMI3v3MrhUU23O7gETt+gwxyJOns7OQPf7iJJ5/4K7dcM4fp0/0MdDSmqqoYDNs5Z5abK8a7+X//asOlQ1A20bO5mbWrwNzexp9f+Ih331nLBAT+EdPJsphxOW20BHswGQxYnU7aAgHCfX3Zw7OzCcdidHzG+awjEqEjFqPyECL9+v0XTYfhv9gcjVIfi1G+14T3/jhiBWNnMEiLzYanrOzAnqDr6EDL5s3ou2X+DjLIgbC74qiazQhGIy3hMD29vfQmEp+KdDocREFA7EuSEa1WMjweDGYzrb29dPX2srm9HVXTUAeV8oNCMBqxOJ2YRTFdvB21DfcpjaKIUFGx5+8MBlSzGeUzisa0UmoAgxUicdBBFAUKfT4MFjvE1bSy2I8kQWcX+PwgiP8tHkUJMnJgxIT0600/CfKHoPfsJBzZTHTDPWgTZ6P1LEAIboHeWhBnIBlnIAgOwEnf/D+CIGGx2CktPR5RPMBz8CCDfElQVZV4PM4dd/yORQteYaxXZUSWEyFZNeDbMplMXHPNH/j5DRfQm+zhxgumc96kAt5bWcW6Wo1yCZ5fGWFHdQS/Dm+3giUKLlGjsbeXHI+D5mAAAK/DgYf0KiuCQEMk8pmihQbk6/pBZ0dDerq6Oxik0u0+pNUtTdfZEApR5nRiPMBWoCPaMKQpCuvWrcPQ10j5ecg9PSQcDswVFUe3l2mQrxX7Uxw/r5f2UOhPknHa7WRmZFATj2Mzmdja3s7qpiaisoz8OS0Zg6QRBAG31YpusSAf5SlqQRQRhg1Dqar6dEvMgSiNkC4E3X4YNjJd/AFpLx0rxNT/Ko3BAHgyQNWgoBhy8mDdx+nexVGTILcIzr0SnC4wG6hVNHoDazDO/BXvrWyjxTOczIlXkDnsG8DBT0aqqsry5csHp6kH+VKiKAoP3n8XyxZ/wKL33sUVquGcCRWUD58C5tMHfHuSJBGJ2Jh77tv8/aM2YiaZdzrasYlQYYaADC4FZlpgiMnATwr8TLAaMaFjNhoJ6mq6l1nX8TidRFMpWnp6ALBIEpb9DJ7puk51PE78EKejA7JMkyThPsQ++p5EgkZFYajNdsAK5RGtyrY1NUFlJdaDyJIVJImO2lqsJSVHbscG+dqzt+LoslqJxeM09/YOqNq49/aGZWbicbkw2e0U+v3UBALEYzE2trURkmVUTRtctv48BAHNYqEmHEaOxz+3B3qgUHfsQNY0KC+HvW9YD0BpBNJF46Yt6LKcVg+hL7famlYaU6n0jUuoF6bNAaMR2puhtBJOOhcKSti66nm6X78PPdJNfd0Otv/7l2TY4cl/3M7qRYspKBwNUgGIpkNaiZk//w3uvvtqli17jWQygTaohA/yJSAcDvPWW69yzz138vFbt7D8rVtob24kQ4STSuzoTlC7nhjw7eq6TmtrK3qkg7panUeeWsPyTSnGu0QuKHByst/CqQ4YYYJ5bhuOVIJJdht+h4NipyO9kmA3QlIGXUcym3EJAhFZZmx2NuFodJ/XHFXXCQaDjDhE/8XGUIgysxnXIfjZ6sDKUIgSsxn3QTz/iI94tjQ04BcETJ9j4t2PEovhKC2lt7kZs82GGosd6V0c5GuOs8/IuC4cPiLJMXsjwK480MLMTFp6e9EliXAsxvrWVkb5/YNT1p/D7gkxCVnGbLEgHIVjJebmonR2YkgmP7293RJhjMXFsC+FLhpNF3FjJ0IsBI31fS8sgMUOxaUgh8DhSCuIFSOhchScch7YbBDdxLLNizhJ6qTpofcp+Ok/yD37ZzS0hynO8nPpty8AsQA49M/t8cefSCq1haHlq3j/zW7GTb2MvAPwzB1kkCNFZ2cnP7/uciZP6SUUzKVwTCUrNzSRpauMc4hk5g8jtH4dtmOPpzfQgMdTOGCDcdu2beMvt92EgQRnTq1EkTUyR5j59szxqJVT2PjAXylJNhJKqZRYTZgMBirsEqLZii2lgJJEyM9Hb2mGWBynyUwwHMasKCSArmiU/IyMfd7cWTiwJJi9kRWF9YrCuANcvd2b5miUhliMcwoKDup5R1Rh7J+WdowZg3AQF+ZQfT1SYSH6AJhzDjJIP7lOJ6LFgtPlYkcwSCwWO2KKYz+CIJDvdlPk86FZLBRnZtImy2zr7iYei9EUDKLp+hHdh68qoiAgSBKK2UxSltEV5YgfJ62ri0RPD4rXCxn7mCw2GNDGjPl8lTHTj97VuefPBQECAXB60v+9ZQMMHQF5BZDohWA7BFq46IyTuWurgc5JFyH5jZhGFVJ+8mmceJwfs8uPqh3eMbDZbIwefR6bNlqZUr4Zl6uHN15/jWQyOah+D3LU0HUdWZZRVZXt27czceQ2lr1Si0/tILWjnp7aCF4JXCII4V4sxWU8eOfvaNn5GqnUwPU4v/vO20wdbeLxe3/Cjbf+mN/94VqmjTIjzjiJ6ugW2rMyEW0mJvtdlHmsFHictCVkxLIKBKcDxo0BAwiz5sDcYxEdNjxOJ5FYDKfZzCifj5be3n1u+1C1/V3T0X0OHQeDputsOsjexX6OiA/j7mR6PIy3WJjgdJLsW9c/UCypFNkFBaQGzbwHGWA0Xae9L6u6Nx6nwu9HPEomx/1FT1s4jFcUqQ+FyLJa8dntIIqfGyX1v4iuqkfPr1EUMZaUYAyFEILBT6+MiCJCJILJYEDY33KO3Q4zpiEsXwi7L6ln+AEFrKa0FH3Vj8AQAmUJWzpEsstnE06u4uGntnPLrf+HZBbBnQDNhUohHy6YT1lZGUOGTDyst6iqKqlUgk+WzOfFl15i9DAvTq+PmcdfRV1dI1OnTh00/R7kiNHZ2cmGDRt4/MFb+O3vfo83dyTvv/sKXUtvItqU5MXlATwSnJABJ+QamXD5T9gai1I03EZP8WW8+vpbXHPNr3d5Hh4OyWQSUDAZ1iInjDx/9y8p8U8he/RElr/6f+itKU7PH4apthF3Yyu6vxC9bDhiMAguP4yblLbCKiqGTWvQ2pppfOplTHWNhAEkCYsgIFqte6S+dMRiPNvWxnk5OQd1ztd1nSXd3bTH48zNzT3o5WxF13m1ro5Rfj9Z+1Eoj6oP4+50BoO0DBnC3LIyOg62YKyoQI9G0/1Eg302gwwg4m5Z1W6jkZgsE0wmye3rJzmSM/r9X/A8l4uEqpIhCBiANe3tVLrdBCIRJIOBgkF/x10Ie/k1GqzWI+ekoGkowSAGjwchENjn7zWLhaQsYxZFMJs/fX7KyoKcnE8/t7sLho+EvMz0JHQ4RKpzB/et/JBgJMGlpzbSXTCS7/7wSu575nXyjUvY1iXw819dgCZ8g8LCMQwZcvhG3JIk8cYb73Lvvb+nobqFay6+EUNWFld//yqOP3E0Y8eOxmKxDRaNgww44XCYn//sOwiiQFvzCrprb+aRJ4bRXPMf/vyne3j3nw8iLFtGmTmtLlpzhyKWjKV8/FyqFt/M83/+Phd/M5PNmxczYsQMJOngyxhVVXnjjdc45ZTT+rLXTcAMtlWvZ0VNF1vaq1n+92cYLrYzbcxQHqxqYLSyE7Vd5tTMPGwuB4ydBGXD0AtKUde+h6T4CTlFnnrsP3h2bOVY3YLDZEEzmahubyc7I2OPgrE5EKDSbD5ogWDXdHRW1iFdG+pCIUIm00H1LvZzVEaRD3Zaup9QQwNxux3jITqYDzLI52GWJCwmEz2CQFIU036K0ehR3X7/lPW4wkJUi4W2VArjoL/jp+j3a5RFESUeP6IG/3ogQHzbNlRRTPcW7oUgiug2G6mKCtjXUF80Cm/NR3d7Pv27rOz0QIzLBbIM6zdS0Wrj+it+RbdxPBuWLaNqx7v856n7ePpfqzh9zkW8/bbKLTf/kOqtKxgoH7oTTjiRc845kRMmCVz6zd/w95/+lMtPHsWoUSfy46vPoGbbogHZziCD7M7CBR9x7LQx5Bmi7NwhE64toWKok4kVbp77zyfc+vePsWowww5jbEZMwR4aNi/nzmtOYs2ypwjVrMHQ3I3bWUsg0M3y5csPKltaVVX+/ve/849Hf46mpfb4XTAYpLujky0rl/DriSKbGxTuf6mKZ95bw1MfB8mym7Hm96mJmX4YOxnVYeTNp27hlWce4Rc//TFz6GT2hXPZkenglc4OZEUh227HAnucxwXgwFyq9+RwpqMTisLHsRjjbLZD6t8/Kh3325qaGDNrFtZAgPBBDLHomkakp4dUdzd2u32wp3GQI4bTbEY3mwn3WZ/0hMMYTKYBz6jeHwLpfkeX2YwrK2sP5bG1t5dkMrkryxrS8Zv/qwgmE4lEAvORTIbR9bRyeNxxCNu3ozc07GNHBLTubrS2NkSHY0+VsaMDRo6E/ExYvGC354jpi00qBpXD4YXHMI4YzWmnnMu7mz9h23tPcfpV59NiymBcqQ+5VyS2/XU+fG8dl11VhiuvkOrqair29os8BN588w3uv/cZzpvZi0HXKDYaGVNq5LG3v4dEgmjkdWAmyWT6ImfqGx4bZJCDIZlMUrV5ExaLhWHDR3Da6WeweLGHzur1XH2ClRdfW0pjWxPTykfwUfX9mBSNQjtEUrAxkKIx3MExLz7LJZYUT7emyEjayR7xU2KUc9ttv6a5+RMee2wpomhC13VMJtMeyriu66RSKRKJBB9++D7btm3nmcfv4MrLJmA0qCSTSURRJBaL8eabz3PxJaeSpy7m3Q/CTM2AsbkFiF2dDLOaGOdzI/gyoKAURk2BYBMJJc47LTIL3niCU7zwZoeV+LZ32NymM0EHgyBgczhY29rKJLsd6TDOVbqusywYpMxiOaTp6HpZJplMUpiVdUjbP2ojmgc7LQ2ArhMPhdAzM/FnZhLbufPI7uQg/9MIgMtsRtV12np78agqPdEoJV7vUTeS71cedcBus+3Kst7a3k40lWJkVhZRRSHTbj86MXpfNsxm4skknR0deO12vE7nwG9DENDq6tDC4fQx3vu8pevowSCpvDzMfj/s2LHn77OzPy0GFhZBXh68/TLYrWAwQvkIKCjiuMmzGH3CudS3fswrb97Ohx9Hme4W6Fxj4ddXXok58CbJChXBevAXin1RV7eD668tYsl8M5WF8NTqJtpu/n/4CkuZOu8Kho/Io7V1I7fd9iBTpoxk0qST6OrqYvjw4fj9/gHZh0G+vqiqysKFC3nl5cfZsu5D7n3oCWAEGzdu5L6//ZZLzlHoqT+XyzN3cMeOOpav3IBLUxlrheO9FnI0maHZGQzx2pAKimDUJH5uNSLk+dnStJh/v34DumShbVMTy9+7keff2IacMPHb391KRcUYtmzZQldXF93dXWzd+gnr1u2gue5DCgq8TBjvZphPoqv5G3y4dDRZ2TO55ZZbEYizfGkHRgIUWbO48dLTKJ12OqntixAXLEUcfizMPBUKhwJJkAw8+/9+xqrqbrJFWBeAib0drEqBrsA0rxczoAkCwz0eIokE7sOYD+mMRumIxznrEBLxFF1nRVcXo32+QzIKh6O0JH2o09K7EAR6ursx7WtqcZBBBhjxKPo3fh7783dc19HxP58sc8STYVSVVHc3utv9aV/GXTshgCiirF9/YK+ZmQXhYNpSx2bve76AnoiwcdGD3HHbjTR+8CQvvBBDiOqcUprBqSedTlVLL8kJV/LGe0/T0rLtsFsUqqurqdr8Ps//eysnnTwZh9+ICSh1WvjRhWcz45QR7Nixjr/8+Vu88fK/mOSu5u47b+eN524DNYiqqrz11pvEj5Kx+iBfHfqnn59//nn+9sdL6ah+nWPHDacwP5s///k2bvu/q7j66itQhHGs37qN+sIiphe5GeWzIWowzgxxTSDD6cJnNlHVFkD35sPEE6gbOpam+o0IEVixTsHTKfG90ybS2vQyjtAn/PTHlzJ06FC2bNnC7397FQ/dcRG//803oPMxdtYspEDpZmZRL4FN7Ywwa7Qs+phjxnfzt7tupW7LMrJSa8kyNuM3Z/HH//sjTSWlRCoquPH1d3mmvQdF7gBfJhSW8djTT/LL637MxpXr6U2mffin2SyUWC2MQ+AyQWCkw8Hm9nZcFgsxTaO+rye6P0P6YIWIbk3DAYdU8NWFQqSMRgoPY6jyqK1rHWy29O4kQiFEp5Noe/vA79ggg+wHp8mEYDJRl0wiANUdHWhfYFtEv7+j1WxmeF4eBVlZ+0yW6YhE/id6HvdOhjkSRaMeCJAIBhE+I0hA7+5G8XrRRo5MW+rsj+Ej4cKLYfnC9GTl2o/TE9SxKOFH76Lqjr/T8fEKXn6hhsuzLDw8zcNZM49BEgyMGurnwTvup8S8hqKsjWn54jBwu92YTAWcfuplvLqwh3cX1WLQYWdbhI8evJs1v/sGwRWf0N7UhZMkT97/Ej88byrfv/Yu/FlDqKmpQdO6efGFf5BKpaiurj6s/Rnkq4+qqlRXV7NmzRq+f9VFfPjhC1xx5TjmVhTzo8u/yc9u+DlLP/g9c4a3YLE9TtWWbUybNZFHn3yVs84eRdjqwCwamO5zMtYE+Q4Ha+rbMGgiiEaS1St54o9XE7GYKRk3mj/9tJTi/J08/NxK7IlTOf+a+8nJ7kFVw3z00Uds2baNxvpOck0yHTsijM+Ok+eDN17tYKQ1Rd2SxTzxd5k37lnBMM921CRcP85IbxcMc7tZMP9hSrLz+PPNv6ezpYuSilLIzoK8EpoWPcCyZ/7Ksy+/S+O2IPYUTDbCVFVGjCf5UX4OhQIkVBW575oRTiZx9g29HEqGtKJpLO/spMzvP+g2qcPtXeznqC1JH860tK6qdNbWYgRKysqI1dQcmZ0cZJB98EVNU38W/SeMYZmZ6IAiSfgMBtZ1dOAwGBBU9X+n57EvGUaWZSyAeAi9PftF10EQSPb0YMzMhM7OfT9OklB27MBUUQFbtnz691nZcP5F8PSj0NQAp7oh0AX+bDCZcc0+jZOTMmPzAyTD3VTOOQ6HEoILfgd6krrHf8aQksmMLrWT2PJI2pbH86P/pskcIKqqEg6H+eMfb+aTT94iy1pO9ScbKLIb8AoKoThUd6WId8KQ7asoVAx878K5zDztVFbGLKx87ylKy26hqKiIt+a/ii35AMveS7CuuomKirsO4QAP8lUnmUyiKAqPPPIQtTuXsr06zLDibopHTGTx2p3EW5tp+csdROobuGB2CVPn3MDCDW8wbFQFTz3+IrKqcsNjK2lrkrkqw0BAhZ5QglikFQBJgcb332b9mymccoTEGy/xyrpV9FRvgkqNcExkdPsGXn5iPvk/PJeF1c9z6y23MDw7xGmnnsybz80nN+XlmLPm8Yu7nqC5QyEzlYDSMn72/W+QO/dU7r79Qq45fyydSje9Heu54rJKdFMLa1Zv5o3Xl/DreXYaU0Fkjwul62W+99s72LApwjgrSCoca4EhokCp1YYtGMcsCLuuD/3/X+LzEYvFCMbj6EDBQWZI7wyFSJlMFB/Ckvbh9i72c1RjJvqnpYtsNpSDTHARBAFHaSk91dWYRfF/s29rkC8MsySBJNGTSKD0TVNn2Gx47Aef5TvQ7J4sMzwvDx3+53oeBUFIK41HoGjUQiEMp5wCH3+8/wfFYjBzZtqYe29ECY47AZrroKcLrr4eGndAZg6ceRE07ABBxz9tDhlqEsxGhHAHnHU9WI1Q/T4Tr32KiTYLgiiB+j0Ekwr0Ap4Dfh/btm3j9ddf575776Ygp52S/BF884pvcPmURSxc2cPj//4ARYEii4lTckwM8Vk4e/bZGOd9A8G4nmEb36D0tJv58MO3eezROxk1spK6NgsLl93MQ/c9wpa+QrmiooKampoBGcwZ5MtNZ2cnf/3rTSxZshipczszJxfRVF3P+ZMm0Lz+P7z5QQh7ROd7JT384hsXYrr4em668xYWvP4mJeUiXbVG/nz1PNau7GJ+0yd0JKDIIjClMPu/xZQOm1taKbbb8CkS4uYwjSs3MHL4EP71ah1X5Zoonnga1/ohoq3n7w8+QrE9zqlFdv797Du0t2kMdcWpfWI+iVaF09zw40lFDLv2doSKkSxes5aPt2Rx2aWF/OuuReSIGstffZ1wKsF69WOKEfD2OqkcXchmzxB+cfYvmWi1cNnMYiYaRYyYcKZ0JExYOjopMVlQXC4mlFoQ4ykm+P2EZRmHxcKKxkbK92W39TnIisLSSITJh6AQDkTvYj9HtWDc2tREcWUlWiRySM8P1taCqlJUWYna2Dg4NT3IUWd/09SHMrF2JPg85VFUVYxm867p769V8biX0ojRiCCKh68CqyqJJUswZ2WlfRn30yuqb9mCousYsrLSsYElxdDaCKefCUPL4Nkn4Hs/Sdv06CVQVAIvPQ5jJoOahOETEFa+B7nDYdx0yMwDUUX1uRE7ViBkFoGnFCwVfcpi+p1Fo1EefPA+zjzzTEpKygAQRRFFUTAYDCiKQlNTE3fc8Us+ev9dwkGZimEO/u+m8/hk2+ssXBbl5ZdWkK3CNBuMsEBrb5LCIRU4Z5xLwunAqFixz7mdn13zXWp21DBtTB4GIUr7ohp+fslQajsy+NWvv8v1F1cw/80KSLXw45/didFo/Hp9xv6H0XWdTZvWYzZbqKgYBqRv1E4//XTmzi2i0ivQ1WKi2PcC615fQVi1kNvXGSMkNKKKmTWb3uStV5Zy9viJjD7hOOY/8CCvfrCdLatrODvbQ4USJaDovF3bTqm0Z1uNWRdxWh1YDAIXWlVCXT3clJtBeWUpyqaVdEa3sTXVyoxMiVO+cy5vPfsGt07KJNjSyTM7uoglYZwFCiQwygp6Iso7//gRf31kGU45xi9+uBK/CQpE2NwcZ3a+h4kuncJpQ8k693sk80r5/W9+h03WmHviUIZ63AwdPZtkYy2Wpg6ElARjxoMqoJud0NIGXb3UfvwJXbLM8NzcQ+6Dr43FSCWTFOfmHvRzB6J3sZ+jqzBqGovr6hg6ezbGtWvRlYPvw7FkZhLp6sKUSg2Iy/sggxwse09TF4kicl+M3ZHIpj5U9qU8toVCuFX1a5tp3a80hqJR2ru7GZqVhXS4703XwelEKCqCz2iH0UMhhFNPheXLoagICvIhHoY5c2D1Crjxz+CwwicLoW47dHWkM6QLSqCtAeq3pc28iyrSUYHvPkxk2kRefvPfjJ15LmMqxoJB36NYBLjvvnvZtulhTjzOzD33NFBUlIvbPYp/PPoXJk0+mUCglZtu+gujRh1L7ZYP8BolvnX6qTR2dPGPRxbQ0JzEFAGfGdoTkBNOUmw3YonJtKx4mvvfX8yPfnkVKa2WSPcqvnlKBmLWHP5z7/9DCWmEooVEl/2bdetX8pa2CWuWhV/98kY+mv8wc0/7PkbjoB3P14ElSxayec1f+N419+z6md/vZ8aMU4CTSKVivL/+mxiHbMazWmVOXh61HTJbtjfSFoFf/+3fxFSdU40C3506hq6899jQGUbfFmaWGWLJEK1mM75YhEl5ORjF9GdcQCDL5UQQoKW7hxy7FbL9MGwUYjQAp12CLuqYV73DVBdkhhaxsG0d1/zhHoZnhql//p88OzTCX1Z0Ud8Rx65BddRE09t3c8MDaygVdCwiHGOCUXaJyW4TTpeTCXNmI+pJGDYWMrJQWl7l4yWr8Wga8dVbEU+bxj9XvsOWBVX86R9PYVw9nx6vCzotWDetgjnzMHyyFNNWG2Wyhpz6r+ejDBzouFh/7+I4v/+gLXkGqnexn6N+lfC5XOjt7QdurbMXck8PcU0jIyMDl8lEcjA2cJAviP5p6oSqUhsIUOpwYLLZ4Avsbdwf/cpjvsdDbyJBsclEWzxOdyzGKK+X7bEYRR7P10N57FMaM41GlH6vxsMsGrWuLuSODqwVFehbt+77QaqKsngxQlYWoqZBWyvCiBFpg+5zLgCrlZ6NS2h84k+MmncR0innotttKF3NGCJBBLcPJs8BuxOaq9FnXETt29fx/GsfM6rczssvLeS002djsn8fsJNKpRAEgfb2Ntoau3n5wf/DM8TJsJKrue6GPzFripP62ueYOs3H1i3reOXll/GbY3j8Om89/SpBXaW+VSHfBs0CWBCYZAW3CIIqQE8PvL2Ebx9zMnHBz6tP3kkymWLdZpnIpn/hyhFp69RY+vq7DMm38KuJPs6+/LsksoZjyi1h3lA3wm7JFulDpPLKKy/icLg57rjjBhXILyH9voUAW7du4fXXXycUDLJlzVs8/+I/kaShezw+/feTADPhSCa1azS+9d0fUjz6GKpvvhlNELCjI8sKPgHyJFh87z/4UAO9B8oMApMcZryCwE4FjApUN7btev0SlxMLGpLZQlGmH0qHghEIh2DGPJTSEUg2O9kjJhMP91DZNZcirY6XFqzk7x8v4rTsMLZJJ9G9+FmyJGhNwNaNtRRuruUHZjAK4DEbGel3kFRVXBYjLrcDsX47XHEDlI+Cziremf82E50q0Sgsb4zz0YMfkdTgBzOGs3Z9Fa3vf8TGZIAZdiPrq+Nckuflg9ZGRo/OZel7G5gC1AkCQ3WdDcEgzr4++M/jy9C72M9RLxgbOzp4UVH48aRJdK5adUivIRqNiG430bo6jF8TZWSQry6SJJGfkUFSUdj5Jept3B/9y+dOi4Ucr5eW3l50SSIci31tlEeLwQAGAylVRZdlzIdZNOqxGNL+ehR3PahPiZwyBVasAIsFTCKsmQ9mI0w8nuDSZ4l4PQhnnE98+T/YkkqxedFKLj7uMow5henp50/ehiknUbPqbR795xI8cRPDs0Yx0tGB0XgctbUNNDZ28u67L6OqZt5/8zHu//UM/vHUJ5xh1Ylt+TvNDTt4osrINWdkkmkp5KZfnMGcaSN48lmRi6b5caoO1iyswZhIv6XRZrh+RC5ZRoGOpEZdWyeyrJEUQOoN8rffXU9zR4ypBUUsrw7RUhek0gZX5gvMmTyK/Ct/j4xCympnS/XzRJuPZfz4K9nbiLKmpoampiUMHTqVm278Kb+98XasVhsrVqygoqJi0N/xS0BV1QbWrHqarVURInKct19/hqF5Lm7+81+xuCfv93lGo5HzzvstiVMrad+8lIf/8VMunjOGJzsjrNnRSq4E5+Z7aQjH2RiQsatwjsPAGJ+X+s4ugkYrx3uclGUUf+qGu6W7hwJRBVWFhloYPgpcDsJZmWx672lKZpzEhsWvkeHPZWymwL3vrGPj6hp6exKYR8/EUbCD4/OTPLQWRkow2ghZAtj6FimFZIra7jgzcj20xmO4ATy54LCybetKMrNyOfn6pwlvns1bm2Q+DsBcKxSYBXIdOXz3pj/iDcXoUKDOABcUeQmnzHyypZo7GoIMT2o42ttx2+347XZMbW2UeTyfe7MUVBQ+iESY+gX3LvZz1K8Guq5TnpdHd01N2oPoEJTGwanpQb5siIKAbjRi6CsUv2y9jftil+rYl1n9dVQeBUlCMZsRZBnTYRaNSt9Qx2dOS++ee59KwQvPU9O2ldxp45GmnkjhKefQaXiVzQ3V/POld7j01DM45rwrmf+fJ5laUk7OmKkwajoYTRhbmvnBeZeQN3ocPSYrOCcj1H3E725+napNWyjMCNPQmeAbl13OK+uq2dEUZ9ZPT+DeBUuIdOtIWgp7ROD9p1ZywwUnkjt1CG3bV9PepdIdDXP7t06ioyvG2oWLkROwsb2HaCyBW4QJxfl0R6Nsqq3Dvq0OnwwVJpHiTKiOQb4Vzi5wcmyhDccxpyFbGtiy4j6GlF7C1KnnYs88mX1FGFZUVLB+/Qxu+8PV5DuSaOGrePbVzbz99pPcddf9bNjQjMFgYPjwEV+5z9pXDV3XSSQSGAwGtL7PrKqqKIqCxWJi+bv/YsKxE5GiSW69YBbjJo0AIuwv0C6VSvGnP/0f7W1VqMlV/PgKjXuf3onU3YpdgBNzvSwPJii22CgxJPD5fBSaJN5sC3Febh4VDhtWoxFV1/ewBRNFIa0sqipE42AwwPCxMGYC0ZatFChB7vzjr1mxdCUXVhr5d28KY+ZofnfTTdz2+1/y5muLSWzq5MHm9P2YDPTokG8SGZOTA0LaRcJjNoEiU+T1IyVVcHioe+VpVmxexsnX/oJ7327g+XUpLDLMcVvIkyART/DRe4tJ9SgkdBgnwkyDQKGsEli4hMmuJE5Jw6BDBzrj+wZ2BT4/4FPVdRa1tZFpsVByCPHIA9m72M8XIh8sq6rCPHo0x9hshJubD+k1BqemB/my8VXpbdwfX1flUZAkUmbzYSuNBzItrUejqG+9haG8HFqaoaySEkUmmlnC/7v2Csb5Fa55eRsnj3mTb51xOWVzJnP/XQ/Qs3Its90CGGakl6TlCMXnXJ12yjXpODf8naA+ntfeW0qyZyGFZp0xdvjXjWP46/yN5Jsree2OUVz9i8fp7pX5Vp7AtIpsTrj4KqRjv4OYaqZu1VNUjD+FrnVb+M0dN9PUVc+EtY+T2mphQa2MEE9Q7HETDwapb2hG03XMOig6zMtwMzHLzurWNn5qEckpKSDX70SsHAtTT8JsiDDhpDuQMnzUtRl58qE7+clPfrvPY7RhwwZOPO00Apu28tzTT/LI3//BJd+7HovFzc9/fjFnnDQen+9mBEHaNW09qDwOPBs3buCRe3/G7OPOYtPmGjoDCTrr15NbGMBoHkKOX8S4ZhHfrrAw6viLqNr6KkV5x+PIOvZTr9XV1cUnn3zCW28+TZZN5vYLc/m/PzdT5K2nNQk/yjSSkYpRbDAxM8PJMz1hzvY5eGlHI+fl5DLG6yYYi9Mai7OuowebHEcALJKBMfnZJPq0JYso4I3EQVVQvE5W7zAwIa+Qq05T2LlhK6+tCTHbZeC6y2Zzy5238e6yGipE0BvBmoIpBrCbrcyxGil12enqjRBJpZhQWohoMIDZA2VlsGMTrF9F4Qmnc+klV7Gsdhvr3nyHMrtOjiSSmZLZGIYKEXbKCseJYDJaOc5kpNRho6qziyHWBAaDyHs2iMSgQhCozMpCB4IH8PfpicepT6U40+c7aMPsge5d7OcLO+vnWSxED7FY7GdwanqQLyNfpd7GffF1VB73Vhp1STr4jPADmZbu7EQbMQKysuCDj9AnjUUSJARsnHLh1fzpzzdjS2qckrQypnUNW+9dzgeLWvjbj67GMfMYkmvfRFrXie4uxjDpDEhFIFKFNuksXrzvOh58ZA2FTrAbRbIn/ADLkOFIwj84pqSLZxd8RCIlky3AFSUmhl5xHRxzFiE9wH8ev5szT5/Jsnvf5/YbL+d3j/6LEZHlfLilkx01UKqAS9cxSTIT8vKw9h0aqW/KPMtqQtcS+C1WCqbPwup2Qks1JBVY8G+02RNZ3dmCvC3EzvULGTPhajo6WsnMzNn12dB1nRUrPubFF5+nqMjN7Ak5rH/2Xv52zTepPH8q3Z0L6eqsJ5UcwaOP3E4kGkfSW6n45Z2An2g0yuLFH3LSSafvUsSkr8CN2BfN7lnKy5Ys4KRTzgDAZDTywx/O46GH7qF+dRupuMh1PxnHpOOSLN12Mde/uIQaTeeKITp6cD3PPPcs1107CsWnkEqlSKVSLFjwEccdN5f77/8LK1e8ht+R4tQZc3lpZTWhDli8BeZ4RIa4zWQZzXiDKRa39TDGYWVVbxzNaKXEZuHDhmY2hGIUKwoer49hLl+6YDRIhEIRqgMBBGB8TiY7whGCTz5F1zuvcffCTzgvT2eNxU1bQ4hpJhih6jQt/Zi6NZupFKFCgiwNzsv2MtJuxWuzYTUZEQRQRAPFJiNiTE5HdUoS1NZA2QgwGpGGTUBt2oytaiHTpw2hbrnKt6wyT65pxYpAkdvN1q4QPlWnwpQuqBrCUbSETigY5zlZZ12PwEnoTPF6MYsiOwIBAmYz7s9Q/jRdZ2kwSLnFgvsQVqlqB7h3sZ8vpGBUVJUX16/n4pEjcdfXox9GpNng1PQgX1a+ar2N++Projz2K42B3l5kVaU4IwPhYM4ZfT2K4uTJ6LW1+7XXEbKzIR6HiZPBLEJnkJQi8/ADT9FR1848G4w2mnAaMxhp7+U/v7uEpSuWEBEWMcSfR93HC3Flzmbk8GkgaKiJBI//4y/srK+loxc6u+FbU0v4/nQDz77wR2bmTuC8376PT1dwCzAzx4QzdxgkghB7lj/c+h/OHzWca37zJIXGDt59dQWxRUvJHmtm+06IRsBoAqMKDllmZ1PaMDnDamF4QQ6iwYBoNKKnBMpGliOmZHCXwjEnQ8U4CDTR9Na96BVF3HLrs/z6eyejxR7CZMwDTmPLlq10dXURDHazbt2znDwvA7PZz4fvb8crFYO9neCie3ijuZTurhgfL97Co7d8H5O/HDF3KiZj+vO3ePGH5GStZ+eOUj5e+R6TJ0+homIGqqoO9kDuRWdnJ1u3bsXv95NKJXn+6Udpaajlrv87d9djhg0fgSyX8IfbZBpeeYTlCwSef2A5/uQ8fnPPNSQCvZT7oDInjw5lJN8dNxOrw8Y999zJysWvYbM6CUXqaaiZzvXX/5WlS48B7RFi4XKe/+MCSgWBi04azvGTp+AYPgVsOt2/+iXt7XFG5WXzTl07F5SV8GhdGwWIXJaXh89qRewzve7HZbVR4E8HEETkBNGwTEVPiOptNVymarQ2gVsL8iMTOAUgpvLIu6vIVXSmiGDQoFgEeoJUBYIICFT4vbhdDswGA1aLJb3cHUuA3wqVI8HjhIrRIEmI449jSFYRO5Yu5OSfXMnDf74TTYcRgk5bT4gKp5uNvVEqNSMkAB0CMZUl9R3IBgM/92VQFwoxyuFAEASa43FGGQz7PVfqus6y7m4aNI1LsrMP+m/fLcu8FQhwfFbWgPUu9vOFnd01XWdBdTWXDR9OeMeOQ36dwanpQb7MfBV7G/fHwSiPTrP54BW8o4AgSWgWC35BQJVlDFbrQee5ahs2IOyv91oQ0jGC9fV7xgS+8S4X9HSgew0k0HAmdXYsW40x00ad3caS+eu44TgHC9wSk4cPJctXjbzlFcwjz2B57Ro+fGEtx06xoalgUqEoXM+bf3uQGrEY09QUTk3DpMIwO5ycbUdSdHTdypZVEZqqu/mwcx3NVY0cN8bB628soLY2wPadUCiAxwQlBoGpmT6GOay7VHCXyUhLew8FPheawQBmK+KwCWA2weRZkFMAVit4xlIcuZCV8+/nnDFjGHvCD2lZ/3c87slUb9/On/50LbKscP75J1NVVUcoKHLFBSKZtrG89vCLPPDHGqYMdbAq+BZTrSK/+faFVIWjTB1rQkVE1XQkCWbOPI4bfvkUvV3z+d45haxbZ+CF/3xIa1sTTmeE66+/E0gnjwCYTF9vO5/dexAVRcFoNCJJEslkkvv+ditrP3mFy8+ax9aeXD54+zG+ccbpOPKnoigpNE3HaDSybdt2Hn+smW9fMJlxboGXP3qFex/9mOxUmDwrnO2FSotEntiNNjODqk0beOWZuzh7nIk1O3Suuu5WZk4NUrNzGy+++DprPl5HIv42I4zw64tPYNx3/0jCYKJ19e+xus6mtkhFa9BY1xGgwu3m2fo2hlhsnJWd+SnLGFXT0fnv90zRdHZ294Cus6kzgCeWRBLAIkCBIOBVdXQddGCq2cy4bB+2vYpPSH/dzQJsqmthuN9HQzRKgT8DcehYiHRDSTmMGpu2xPLnIOQU4Csu57TRo/nxxReyens3lSLk6dCS1GlI9PLdgiIqbDY6olEW9PTQIIpM8noplCReCQQ4MScHr9VKMB5nUyLBGZ9RCMY1jfWhEHMzMzEepAimAytCIUaYTBQegWvMF1YwxmSZvIqKAQmzHpyaHuTLzFe9t3F/7E95jMRiBCIRPDYbiCKeAWy6Hgj6l4LkZBJLPI5ksRyw0qg2NiJHIthKStD3MWwnFBUh5eXBc8/B9OnpHwYD+M6/iHk1m9FjQajZjFhegX/UeAILXibxUSvn5/mIqaWYNtfS0tZIZPpwRowMUd+2nBt+fRvGTo03m8MUGqDCBe83qqTqVEziTjav2YlP05jlhGyzgVhPhIixE3nbVh5f9Ao165P85puFfOsbZ/L+ey/S01zPUAmyJHBLBk722JlRkINJEve4uOpAmTkPlCSNzW24fR48GdmQnQtOCa3pP7TW6uTPPBkysjj7ijsQJRnNtZXGrKkk5QT19Q3srAkyZayRV59+k3HjpjLl2Dn86bbvULshDDGNETkmTpZUJuTnUvzdS8gfVUynV2JL9XpkuQ6ncxIWi5U//vG3tDV9zC9OqcRsSvKXW3/LvHI/1950G4UVZyJJZj788EOefeYpjj9hJmeeeQnWPiuSzs5Otm/fztSpU79Sy9idnZ0IgoDf70dVVWr6PnNlZWVs3ryJB+78KaedfhlPPfk0v/3tb7F4ihgyZAhz5p7C1CEpTNHlVK9uJEc1csa8M2jf+AIvL26juDyH44//JVf/4Aq+c/Fx3PDH96he18iPh/tRoik6IjonuCEoQ6g7ijNp5L2nl2N2W8m3h9DzJjMzEeCE0b0s2dLGLb87njHDC3n69lN5+M5/MjK7nDHnX05twxp+dfezjLSEGDv9YW5dLlOUgLK4jEdIMMGXyUmZGXsUi4FYnEAqxbquHmyx+K4bmFpdIMNqpwwBC0ZyLGnLpixBoNRpZ3Ogk1AyhQA4FJnaeBsVGV7cTgeyqiIIAllOB6IgoOk60+0OInICRzKBEIsTrNqIy+dFzMoBuw28PiipgGQAwr08/dDt9La0MtbjwhDsJaRBrgYn5eRSYbOxLRrlqdZWjrFYuLK8HIMg8FpnJ/kGA6V9n0NZUShRFLz7KeZkReG5tjaKPR7yD2E1qjkapSEW45yCgoN+7oHwhVZXW+rrMTqdTPX5kA8yX3p3+qempVSKTEFAMhr3u1w0yCBfFF/13sb9sbfyGE4kEBWFNe3tVLrdBCIRJIOBgr7ff1mUR8FkIpFIYO73ajyQXszPy5YWBGhsTE9I//eHYDKmm+pDvTDndCgtxSBB5pnf4mSXI227M2EOWc1bUeVuws5sHn5lMf7MCCcW6pxx8hiM7W1EO6PEI0nm1yUJJsBv0ImldArtcE6+l2g0woawTiLQRmfVMzREdWYbILxwEzS3csdHzQgyDDXCUAGKDOAVdKqaW/d9jIASvw+nzYXHYE6fV/NLgDCa101CKuGFj97itKESm2rbGT/lVOY/vYL3123jRz8u56fXXMFFZ5Xi9cVY89FmfvmdGfz09z9h6+YgahzmeOBMP3i9WQy98BqUifNQE70sefEORuRqjDzlN8STfs4791zEyCJ++tsSPnrzE0LP9TI3w87vf/5rFtQFyS+Hl156jltvvZ4TJwxjx0aJDXk5dPUqzJlzPL+/8Xfk5a5n0qT3iURUHn74Qc4880wSiRQfffQ+P/jBjzD2eUYmk8k9isp+pTKZTO5qe1IUZdckr8lk+lQRqqrqLqXTaDSi9IVUGAwGUqkUkiRhNBp3+R1WV29DUVI0NTUxe/ZxPPzQ/Zxyyin868mHKS3Op6BoDNu2VbFt/XucMSufRPzH7KipY/TwIl577BbyjGaatr3Cjm4DufnTeeut+zFJhbh3tkB3ihnDynFn5/C9G+6mo2U1D95zHo899iCtzVW8/lYCLerivhuu5sTpU3jnkXtYXPsJLTI4AWNC5z9/vZ2dO3ei2uAbw3P4+1vLaK0XcJvuZ9GONib6nPzo4pm8vWoL3zrTwNCiY4lkO7nmiu9S3ahy4Sj4+90qsSgEFMg1gF0XOdHvQ+or4JKqxtKWNjZEYhQn+3oZs3y7zo1jBYGsvmXdvZFTKYbsJT8JpJXEDXUtyKpCidOJQVFoiETJsNko8HlxWixIVivBSJhQLIQrNx82rEGzGehtWY9YZcfktFKdeSxN3SlG5Xj43uSxPPyfJWTHVYYI6YJ1ezTKU21tXJCTQ4XdjqbrvNvdzU5Z5pLcXCRBQAe6dZ3wfs4xOlATi5FKJJiek3PQ1wRN19kQClHmdB60MnmgfKEFY084jOzx0KuqmA7RYqcfQRDQzGa0khIs0SipwyhABxnkSPJ16W3cH/3xieNsNiLJJG2hECVGIysbG8myWvHZ7V8e5dFsJp5M0tnRgddux+t0fu5TDihbeneys6GoGBa9C8edAjk5kJcPgg6llWA0gtMDkkTKpPP6kz8kVL2TVS0uOjvrOH+OxpIdm5hVns+wWWdhb9xM8s1ltMXNFNotrGkOkmW2sDqYINNowUmccFLHZ7LygwyIxuN4UiJNO2IUamCVIEMHqw6iwQqp/e+622RCi8SJ6irikApcSz9EMOjo+S7W9LzELXdt4OLZM/jQdAxFxROob32RUzxrmffL73LXc49y0oh2KvMUbvlrkClOnbefeZXGqiYyVLAYYboNQjEBj7+E1lgXdU9ezZTTf4C//GK8wyp5+bX5zH/zV2xev5QxQ0u47qcNuGIqf7toGlN++hPWfvAgQqCeddY4f3/4Udz2bt75aBl/+GkmNdXPUjhkHA/cv4J1nyzmjO/P4uUXX+CBB/8fI9z1cHyA636xlCsvn8KLL/6VYcPGsHx5DfPnv8j48aPo6koxa0Ym4yZcRmtrK++/9yInnHg6tbW1bNiwmLKySsDM6aefz9Chw3YdM1VVeeqpf/Daa0/h9/s46aQLePaZR8nwCkyfeRnPPvsYp502i+HDT+C9915ES6qUlmexes1qzKZeli17iKbqKq78xhQ+/GAh3z/RRPGMY3njtY/4xXdOZWurhXfff4GdO5up2fQKv7ggnyee7CZStYErr7ueex5+nwy7xJgSP7PPP5V//u1V3l68DuNVl3H6iELqc+aQZTiVWPsz/OexeSx+L8wpl97EilfupiPvdHpNEWQFVBHGOKC6vhVNlTjLY6VdMlPenuA0g485V0yl+KwrOKt1AzWBXpLdEt+cm48ldzYNLXn8/Ac38625JzDGqfC9+z7AkEorcsNEkHSYkZODQRTpicVZG+xlQ1eAfKudy7Lz8FmsBzRMJ6dSBOP7zkzRAV0yMKO0bNfPqtraEHUBn0Vi1Y5asp12PC4nTrsDl92BWNcEufnU1jzMzR8sZEaGmct/8n1uveOHrFm1GjGpMb9uCRVJlSGA1SBhFARWNzZyRk4OQ/vO4w3hMJu7u7l8yBBsfTcTiqaxvL2d8VlZSPso6GpiMV7r6eG0nJyDTnQB6EkkaFQUTvJ4jpgA8YUWjKqmsS0UYtacOaSWLz+kqMC9ifT0kOruxm63D05ND/KlZe/eRi2VAoPhS6O+HS4C6Zs4l9mMKyuLhKqSIQgYYJfy2BuLkd/nL7avE+hR21ejEYvTiRnQEgnEz+v96Z+Wzsj41LS0kJGB4NrNp04S4YSTYfN6CPdCxQgoLQOHDUwWEBSQe1EsGo89eD8t69/jxXfWcl5lLjedmcvsP9XQWgd/mJdB/rHn0JHYhlnootcsMMoMuRYJOcNBgSiwuTPCqNwCnt2RZJbXgTmVYFNSwKcI1HRFWZOIUqxApVFibm4WkWAQuwjsdZ9uEEVG5OUgigIiAsF4HKeSIrRqFa68TEL/fIB2l0hNrk7VjhjPdX/AtYEt1FdMwFC6lLVdFk4TM5HjMjFxBB7jMdz1vU6KsjOprW7mx6MCzN/UwjF2qDCDR9Lpqq1Bysxn8plXcs87m1Hq3qc3XklHyxaUaCt//nEF0075OT/60U/orokS2FFDwzN382bHRsaNnMvE8iymTBV59VWVpCwwwd1CjzWDlDCMJYuuZnSenRgKGxY9xc+v/zYLnvkLHWsX47HqrHzrA3KLqnjzxWF079iJJMrUxDcS7NUosWZiSUqsXb+Sk06eQ2WlAau1nGOPHcuwYSMBM/DpBI6Kigquu+46/H4f4OaSS47HxUusWvMyLlOA9Yv+zXvP/Rs53k0qodFbP46t69fz82/6WdEL542ewMOPLaVt5zaGFn0bOf4qmzeHuOXul5lUVkjRlBPo7FhHKJ7kvje2Mdro48JyG7rWwdy5PdTWjmbRCx8g2ntYti1AhQm+eeJsGseVMcnsozaUorl+LW8vGoVmiPPYn3/NqNg27qzaxqLl2/Dr4BXSAyMAk9w2xuX4wGqC0jIqz/8hlI9BsWoIai3Dj7uUJBIW9X1qtr3HewvvYLS7gqVdAv9ZtoEiQcUkphW/0SJokpUia3o6en0kRpFo5OycfIr3oyBCWkHTdZ2UrlPV1oam66xMpXDLMp59PF4UBCb4/dSHw/SmUpRmZDAyNxdREJBTKYrdAmYBQl0hVGcCyWLBU1AAcoKMtZu4weMkb+4J6LqR3p2bsCsqyRToioomQKXbyXCHi55QBKemkdW3UhRXVV4OBBjl8WDd7by2MxhEMZko2UdiS0CWeaejg1MyMyk6hBtpHVgZClFiNh/SVPWBIuj6vmW9SYcQQ3MoZHo8jLdYmOB0khwgVdDi9VKQmUls584Beb1BBjnSpOJxeqJRKvx+xK9Qj9XBopNu1o8kk2jJJNF4nGgqxcisLKKKQqbd/sVZ9Og6gixjMRg+t2gU/X4MfYMG9A/aGQwYv/99pBUrYPVqOH4uzDkWIcsPT/8Tzr4IRo4GJQGRBkjEQQTFl8GfHriV5x55B5uucZLfyK+/cw7GeWdx2z338cqry7lrohG9IAuLv537loPUpXDDqCyGTz0RNauMLW+/wbrla5B0AYPNzeZgnGMdZgrtVhSjiQ93NoHRwkyHFc1sZmVzO7pkYbLJ8Kk+8iKHnagq43LaEUxGPFYrGulpAjERRyVJt0FgiZygM5ng2LNPp+Inv0cVE/S0fYIaqWJ9zyjefeZGfvrLu/nn0w9yfImZoDnM6EQzTYta+ffCBFYVio2g6VBhMzAp28KKypG8vnItV51VRN5xxYRtdp5+PcSxlRrzF0TJVxI89/pmprng+Ezosdu47Ne/4sXWMH/8011oAZVoDKZnCFx7QgU7rG1EHCaeej5KsSHBRcN0PkpaaNwe44R8ibqozqgiHxGTlbwsP8PKcynONmIcfR54S8AUx+iehCZEMBrz0wdoryzvA0HTVNCTqCqoymr0xnWAhZqqWh57/DkirfWEowoRRaQ3rlNkF2mIgB2Vy2dX8NS6nXR2KEz2wvWXnsIdW6p4a3ELsUCKMXb45QljmP3Hn7F053IcLg+XXHwPQiRGpiTg13SmOeG80SWs8OQze94E1tnL2Lb2I3C6+dudT3PJ7FG8+846xBRkABPtBiaaIEMCn93OyEw3RpsRpp8A534HsrNRjRIPP/Rb3nr9ZU48bR6bP/yYG+54kGf++ShGRzMb3qzi97NS/OtDje9kZvLOlk7MuomoJjDRZGVZLEGBLnKcz0eG1bbfm+VgPN3TuLanB0s8Tp0g4LHbKQUcVislXu8+/xoCEJZl6gMBXEYjPklC1jQ8DgcuqzWdO6/raLpOS08PBQ4LgsuGoKngNKcz4M85F92ksePNe+h4ZT4tHQoGGbKsbqY7XXSnJNZurSOhwaj8fNxWK4+3tJBnszHX59v1nuRUiidbW5lstzPU693zs6HrzO/uJhaPc0JfQXuwNEWjvNHRwTkFBZgG4OZ7Tl3dPn/+hU+IdAaD1BcWMkrTDjn55VMIAi2bN+Mymwetdgb5SqCZTLgliZgsE0wmyTvAnNGvGrsrj7rZTEoU8RkMrOvowGEwIKgqHbLMsMzMo+/v2JdBLcsycVnGYDLh3s+Ns9bVhVpejjEY3PO8FQ6nM5hFEYaUwZAhqO2NCN+5GtFhB6sNFAm1o5uoN4+3f385cYuRF1/dgZbSyDHCiZl2LKdfTW+8gVfeWU1SgxerU/wsy0zk1If41vgQ/g//TOW8KxFmXsgnbz7A+1u2cYzLTk8oytqOEJeXF1HhsrOtN8qzO9s4Ly+PQquFZxvaENtDzMjJpbwvWeNT703XMcTi9PZGsJtSNET6pkgFAcxWJExkHXMMJ/f2YI12ok6ewb+eu5HYzl6c5m6mzMjijdeX0dgYZ+G/bmT98u2sXOhk3vCxWGefw2+W3IXeC6NNkCnAcBMU2ayEojrezXX8ZeI4hIkXY3Z8wu33fkjVli46aofTUt/BwvpOUgpYVMg12Jk+dDib4o08cf/jnO1SWWOApkYYYtCpalZY1S4j9YY41gx+I+ghM9/0GMid5cdl0ZGzysmYcQ4mXxbkDAObE6xGMCtgKAchCLiR8B3WR0sUJcCKKIHRNBMqZ6Ikmom1PsKf7/81wtKX2La+hqVr61hcm6LIohKIgNcILy6qJpaAbAm8wOr5b9PVoZMlg9UCIywgdrTz4Z234nA5eD2QRAvGMKqQZ9I5yQXFNiOFGVkMuegyHv14A5uq/smYYcU8/+SzeFWF4fEq2guMbNmRYqYNRpigRRGZneUlrClsau+h1OfCU1KB4nCiN3/CR2s/5v0n/01Pj8qKJ17m6h9dw2uP3kp4y04KC2XOHGHipZokF1xxOW3tDaxa+wHTNJlUChaaoMJs56y+7/neqJpGStdZ2tbG+liMAkXB5fNR6fUy8jN6GffGY7Olh++AUDyOOZWiMxRCSSQwWCy4rVYkQaDQ35ckE46hu+0IlaPRqqvZ/uufEHXCazu3MOHUb3FMRzWsWkfe6KkwcjKWuloyaxpoTGi7ehA1WWZOfv6u96UD2+NxtESC0ry8T33XPgoE2CnLnJ2dfUjn/KSm8W5vL+Uu1xHrXeznCy8YAWpaWtg4ejTH2O2HnPyyO3JPD7okoSUSZBcUkBq02hnkS45ZkkCS6EkkUEWRlCyTEAScFstXeiDmsxCAzL4l+eF5eekTa1cX2WYztZ2d+Gy2o+7vKAgCqsVCVyBAkSShK8p+k2HU2lpSxcUYIV0k7k5FBcycgdzdwrvP3cFMm4eM624Hp5vaTR/z0fylbHntUd7eGceQgrkO2JCCMzxWxk47nsD7j/Hionew6UlCybSPnO4upTiygWUbl1NrGc2EEy6kccPb/OeZfzPCDDPKh/DQws2MMuqUOaxs643y9M42LivMJdNk5LHaFoZb7ZxYUIihz5B7X4iCgMduw2W10ivLu6ZIsfVZEJWNgPXrsBk0OONC9IrJ5De2c8yvLueOe+7kjZsfojhLo9DsZtaxNmoDfjxdLsodOj/++UPEw+nip9Bq5SS3EaMgUOZ00BKLMnX0aKRhozA4mvjNHR/y7nvtjPEb+NVpc/nTSx9Qs76Dk50wxgI7e6Ks+GgN8oI1jEtoRDQYK8G8TBjps5Ib7+CCYg+iQUMRQTBIeDMzkDLzwWoGUYfjvwc5FWB1pFsETCkQ/aQjdkTg4H3wDhTJlMeEmb/BIElw3tmUF/wDy+wWLti4gE1bY3jX72BnUCfLkPYXFIFsm4XN3TIWGXJFOCnXSbkgU6AmaVvTSHV2CS8s2oZbgRMdMMwCBh0CsRSrl69CWLeO0R4DhbOmsjacItiaxJqCRK3OtqYUhSJkmK2UmI3MzvNhEgV6u7pxmyzoskrkuX8x//mHUDvq2eZVqWtRyRIEfnbSyYydOYVuh5snnruVh3Jy+N3qVjKNxcyjnZteXkRIhogOHg3OdDs5KSPjUwWSnEqxPRZjc2srPYDbbuf83Fx8fYXd4dxAuq1WNIsFh91OayBAsaIgyzJIEhajsc8GywrhOHpPEMHjorxmKw3dKU7DxXhrigWGdnJPPhfT7OP5uCHAM/ffjV/XKAOGaRpL2tuZkp29R4tNZyzGa93dnJWXh2G3n/f7LW6WZS7KzsZ8iMXeqq4uHILA+CPYu9jPl6JghIFJftkdQRSxlJWhhcMD9pqDDHKkcZrNYDbTHo8Ti8cZKopIX3M/Ofjv5PSwzEx00ktJbbHYF+LvKAoCBT5fetL1s5JhNA01EMCQkYEQDP735xYLXHop+DyweRllvRFMU8YR276K6oYafvaTH9DZ1MK3vGBQQVMgEoWbc+y4DCC5MlFHzeWUnAIeX3Y7+QaFkAzb3v+IrvYaXlxdxw2nzmLNc3fyyqJNnH/pFYyxabyxoYtGdRMTXQ6C0Rjza1u5rDCtLD64o5FSs/VTFiafeRxEAY/NmvZaTCVIhaO0KUkKpRo4di5k+mH2PIzxTuZ+40ruuOdeFv3r79x//Zk8/NzblOfrPPNAI1dd923+/fQLVL2zmLIU2C0wymHhFJ8VSVXQdJ2N7T2Imobw8QpyYnHeee0TtjQlcaYgM6Kx6l+vsXl9HaNN4BehMQblZoFKi5ERPhtLO0IUu2x4zGAwmxma6UYUtPTfAg1KhkLLVhh/Egw/BqJdkFuGZuwBNYpo9YBRBD0DhE+rrkcCQRAwGNKtD9U7u3ljRRunnzqJ2uh6CstGEqjfyXifGTWl8X69zKQ8H1oijtvnIaGEODnfg09U2dAD4ViQoKKzonYrIwWY6oKUDi2yQLlRYEyGh65oGEtSoCijDGX0Obz08vu0R6EMWN2QolAR+GVZDuZkAkVRaOr+701QMJGkPhAn2RbAZTYhp1LotfA9u8gxE0YxetockqqVh594E0EWuf+DLgJBqDDU8+yt9cgpcKrpqet5Xv8exaLWtyy8o6eHD8Nh9GSSaTk5+EXxgJXEA0UUBERJotDvJ5VKUd/RQYnbDeY+BwBJgmFjQZMRdtYiHTuPEreD7M2r0AU34S4HFdPN/PPO29i+fid1AYWwApOAmkAAxWxmiPW/XqaqrrM4EGCi2bzLWqef3f0WD7VYDCYSbJZl5vr9A2JR+Hl8KQrGgUx+2Z1QfT29skyGwYDxf+CiO8jXB4fVisNiIZxM4tB1YvCVNPs+FATAZbHgMJu/UH/Hz02G0XV0SULp7MSQTCL0788pJ8OQEgSDEesxJzBy2Cgi69/F6BBYdc9v0Vs7MSfgow7wqFAmwTmZGfhTMiGrB3s4gkMPsHD1SowJBa8EO2OQoWp4P27g8qwchg8Zzo3/eoNRs05GsNiZ+vN7MScSnGUR8VtFdrS1M1xXyDcaeLymkeFW+x7FYjyVIhjb93Tp7n516R8ARjNtgSi5FhPICWisB48b6raiOgVe+Mv3ePZfizkuL4+IXs+C2gTj65Pcc+2lrFvXzIh8L6H8Ovw9oCTBnZBZ0yMy2ZruobRgpMhtpyMW5pUPlhPUBS40Smy3qTh0jab1dZysgdecVsxOyPPi9zhx2220BoKcVphDaziMRTRQ5LIjSgbQVBANMPVk0OJpI+bK6ZBbBvaJ6Gqc5evm4/K3MzrvOyAYjlqxuDdlZWX88Ie30tS0EUFM8frK9cy74CyG5o1gxwdvcYm2FoNF55OQQqFRQXWY6YomEAwSF5flsakrzI62HsY4LGiyTBwDkzx2ZhTmYBAFIvEEbYEwbpuN9vpW1r79Oms/+JAMLd1HalLgFKNOTXMbAYOVyWYD4l51mttoxW0y4TKL1MoSp+U7ELIykC78DsKEGTRu+n+MFLu54syZPP3GYsoFyNehKQGnmCwEkjJzfFm7ikVd16kPh1nT00M8HqfHYuFYp5MhXu9nKuADhWg0UpKbi5SRAX4/9AeI1NbCqBFQOQwadyBkj8F2+Y/Rs7OZOGkmt/zip/y2SGdzqYWzTBLtLSFqdaiRZU7KzEyrlaSLxfldXTSqKpfvtRQdVBSeOQy/RUgvdW+JRPAZDHiO0rXhS1EwQl/yy7ZtXFpZSaS2dkBeU9c0NKORAFBSVkZsH0a7gwzyZUSAdE+dycT2UAi3JOE0GNBF8WvZ27gvPs/f8WhMWe+dDLO3X6PW1UVCVdETiXRoQCgEI4ZDdjbJaACVFI/Pn09q41L8C99npBX+Nm8oj7+7FVnRcZrNzDUKZKSStMSTZCR7iLw1nwZB4fYXViPLkNLgh24T0312gg4Xo4+ZSqS+nWsuuZJR4yt4//EbmOqQmZXr4oSZcxAlC/qOWpqXrWRbOIYYlTkhv5CUqrG+tW+6NKHgi8Xx7uM97+5XJwCl/gwcFjO5Hg8SoEdltPVrEAuL6FZ6eeRvD5PZVQ3orNtaT+ut9czW4Qy3wLKFKykdN5Snt4u8tCydLHOOVeC4vBzKXXaiqRRN3emc4PpQhI7eCDmiyKlFebQEesgQFIZ6bXQnEuR7vYhiehLVLEJvIIzXbKQww0c8laLYbMGalQ0GEbobweWGiSeAPx+6amHGeZA7BMwG6FoHJdPJ9btx+kcCNg52kGUgkSQJSbLR22skabuQK3/k4d+vvUZAXkNPbibBFQamWFWOK3BT2x6ChIxBlThn2FDeb+5hSzDBOWX5rGnrQfBkMDfThdtqoSsao6k7QFCFDXEIxoPENAjXvMcpKmwVYZwoUGizMsbnIRaPE4nLabVqH9PzxZl+QvE4k2wORDUJogVdlNi0fQFtgWFcfEaCax/4GCWuMUwAEwKneTxsiiWY5kkri8B/E1GiUUZ5vYz3evFarbuKrd3RdR31IGcbpAMoOAXSK5GIIsr69RgslrTCKAgwbhysXArDRoAnAyZOJ5qUuem3v2DBlg7mhq0cP3cEK/VaqlrAD5hEkSF95yOAHYEANbEYF+bm7jGIEtc0nmlrI8ts5pjDWEZOaBqbenuZfpTURfgSTEnvTmVBAeMVhSKjESUWG7DX9ZSWItbVYRbFL24Cc5BBDhENEDSNeDRKStMo8XoPOs7u68JnTVlLn5HPOhBoySSdweA+/Rp1VcWYSGAuKcE4aQL1GRZeXPwaCzfUEQkmuDlH5PWUwLfnjWZ5+2Yc1mls37idtroWxlhtTDYZ6I3FkFMKdhFWGiRqkypeAUZJMDXTR0RLkWu3kjlxAtz+CLic0FpFU3crjkV/Y5teTiqzlGGr/81fXtyBuVdFF42c5fUSVlQ2dAUo6EvKcFitlO5nuhTSfnWKpuExmXCZRNZ2d+ExmxmRn0MomURJJbEZ4LXeIMvlFG4DrJNhvATjjeASIajDMR4r7aLKnzqShFIwToJ5ZjCIAhqwVTMw1mbFs9sVr8hppyseJZRIMqIgh7ZwhBy7mbZohLxMH6LBgNloSE9tJ+NgltJLiqIEBSUQCUAyDpOOhbxiKBuZ9rwsGw+qjBpcxJbWFMNGF2FwzgAMfd+nL/47paoqsizzve+dSUftYv7821N4/MlmZhtSnD9jKpGuABveeJWPOzXOLMvHKWi8sq2Fb40ZSkNE5s3tLXy7LJ/elMLqth4aYinGOay4BSjM8GIzGtjeG2NpYxsYLRzvdeGzWtgQDBPuCVCPgbEWK559HIoih52uRJRIKpXOGNc1rDYjqfETWJGbTdLdy7V/eJp4OEWhBnNtNs7xuXmrO8QIi4N5GRk0hsOs6umhLpVipNXK9JycfV6X47v5K3brOpvb2nAdYNHoM5sZlZNDb58p+v4K0T1QFKRkEmNxcfqm77JLEFYth7knwKxZtMdD3PCr61mwcDWjBJhkgFyTyDMpDSUGFQYD38/NpdRmA12nIRLhtUCAWXtNRfcriwVWKzM8nkMu9BKKwn86O8kwm5nqdg/4J/dLOyW9OztaWhg/axbWQIDwABaMwdpaSKXwCAK2L4NZ8CCDHAQigCiims24gERfX91XPSHmUNjflHVNIECh3X5Eex339mvEaEToUzL6l6+Fjg7019+ksW4nboOGRYNiHbZ2CeQpCtLqTsYdcyp/+WQLjbWt/M7rISORIKTBBlVgmCoQV6EwqVEsiIzxeeiJR+jtlQmrKfI9mejjprHtg//wxDsL+MO8sTy8vIr6ddupr1/MhR6RFRlmlvSqZCbhZJPOkq7AAfnc7c7o3ZbQgvE4ozJNuIxGqhrbcBmNZHjsVHV2c4zTwVAhiWKAMUqUDBEEDdDS9iy1EYWV8RRjNFBEGCGkrSfddiuFPi+TDQZ8Dhu760EaOopooNBjoKWjhxyPg7Zogjy3j5b29L+7VJXCDB+YrOml5+IKqK9Oe11OmgM1GyGrIG2I7rRDKgrRdlAVwAXJGkg4wCn22eR8OZAkCYvFwo9//CvcqUqGDruKYetvZEpJHG1oPkJNgMKRlZirajElY3RgxCcJxOIy83e0cFpxHp8EY2xp6WKs38vcnGx8NiuqrlPd2c0LXRFEOcHsvDzKHTbq5SRP1bZQbDAyITuf44xGvLb9OAPoOkrYQKHRQEtbDzkeO42SibyicqxGmV/e8TqpUIpMDU4UwadrvNvYzni3lxFWKy82Ne1SFC/LysK7myjV38fYE49THwiwWlFwyjJuICQIVGRn4zvA77PLYGB9Wxs9iQRuk4nRPh/V0ShFXi8OiyXdx7j3axkMaMOGpYfX+gtToxGGjwQ5yvZFb1KzfA2TBPBpAiWyzjpZowABKzrzbDaG2Gyous6HPT1U9fQwwu2mbDfFcXdl8XCKRYB6WSaZSDApM/OoXgO+VAqjJIrMKC5mjCBg6u0dGIud3dAUhSy7HavZjDqABekggxxNtFSKYDj8tUuIORx0XaeltxdVVcmQJFKaduR6HXWdZDRKeyTC0D5lc9evVBWjLKOlUqyNRjEkk0RSScp9XrbLEWwOO4tNEitaupkOyAiMUHVyjAZG5+fjs+x53o0kEtT39DDc40KwSkR1DYek82ZvNz6nyLCb/8DP7n0EaUc173fCsSaIAc0JGA6canNyXMZn+9wdDGpff3lVWxt5NiudiQihRBIkCCQSZDmdnypId+9IF0m3GpS5HWzu6SKhaozJz97l97jHYSb9d20OBMi1mpCsRnSjade/DVZjWlksrYDOBjBIkFsEk2bBqo9g5skwZhp0LIUxw1Ga11MV9DF6/BQELQJ4wFz+pVTrdV1HVRM8+ujDrPzg9+jdEc6/5HvkWV/i30+FyatJcV6+j2BvDIfZhtVkYn1tC+W5ebxR08pVI8ux9g1qqbrOK42d1IVinOZ1UpaR7hHcFo7ydF0bl+XmUG6zIe3dtLi/fQN0Tac5GMBmlFgYDPBePEG7rBLTYB7phJUSBIZn+NkYDNKgKPtVFHtiMdaHw/QEAtQbDAyzWMi3WCjeragSD6KnUQe0vs+pADSFQqiKgk+S2B4MMjoriyTg2Vt5FEWESASTxYLw3SsQ8nLg5FMhHqbtk5d58Le3s7i6g0udHuKhEEmLBbfZTEsiwZUFBUiCwPyuLmricb6Rm4tlt/c5UMoigKLrPFNfzzCfj/IjdP7/SiiMqqaxqaeHsrw8zKI44EktksmEccgQ9IaGAX3dQQY5mghf44SYQ0UQhM/tdRwwX8c+v8ZMo5GULNOcSFDg9aYvapJEymJBUxTGejxs7epCR2BzT5Ao0BULkQWcj4ARMAIuUWSk10c4HKGpJ5BOWsnJAcBpsTAmPx80jYaeLpwmCcnj4HSrA2ncGJa09lLSWM3bPXCmIT11ndLAB5zm9e/X5253+hM0DoT+XtHReXnouo4SMVDkMlDV2cYQq4sMh52WWJwRuX1JMfvZdjyVotiTicUgEe6Nftrvkf+qyYU+X1o80FSEskoKa6vT/07GQUpB404oq0wXjWOmQU87uDNg2ASIR9ClHLT4Du5+ZQVKdxtDRzjQpROw2dwH9J6/CNJ9skb8fj9+TxHl2SkeuO8f9MYkMpI6lxVbKM6wYTCb2dTQxcjM9DFa197NWL93j2Lx7ZZu6nrjfKc0H6/RiA67FYu5VDpse2xb5783BjpQ291DJC5/ah81YElCwRSNMw2QEBDNVsa53VR1dVEHKG1tuHw+LnM6P6UoJjWNZW1trI/HyTUYGJ2byzSD4VM3Dgd97Nizp7mob0k4GI9TbrHQEY3SFY0yam/lUdcRLBaSqRTmxkb0Y2fRvnopj993N71Nm3itthuPBoHeXmZmZ1ObSFCTSHBpbi5BWWZxIECjqnJhTg7W3cIXBlJZ1IFloRApk4nCL2C19EulMEL6i5Lj9XJlaSnR/VS5h4slmcRjsQyaeg/yled/JSHmYNm711FJJI6Ir2NIltGSSXJMJqLw3wQJVSXS20s0ldojWWJf7J5Ioes6hXY7oViMhkiESq8Xr8OBrKq4LBZskkggEiIiQv55Z/FeooG/v/Y2kXaYK0CvBjlmKyc6XQzdLWmCvuPREQ7vMcsQh10JGp9XSmdYLHv0hu2u0OytPEYVGZfLTgJw26z7NAjvR9N0emUZLZnA6zQREsBls/IpTamsAtobwChBLJpWBotLIZ4eAmHqLBg9CUQBhk9ILylqKur2D3nr9ev45yKNvz/4Z/6zeBvfvvoXWCyln/OOv3g0TWPDuhW0r/49s4ZMQM8cSXj7Iuw7YjiPOZfOjnp2PP4ErrZm6lt70BUYX1BAntv1X2WxN84PhxRildLXu627FYsVDtseRzmeSrE9EmNLcyseXScABIxWJkufTgQC9uiF7YnHWR8Osz4YJMduZ5bXu0//xH5FcV0wSJbNxozdHnc00HQddH3/yqPFgtXtplmMcMu2OmqjSfxAUoNTLBbGezx83NGBYrFwTlYWjYkEr3V3M9FsZmpW1h4DLgOpLMJ/E13OKijAcgTrl/0pjF+6ghEg0+1mrMvFVKuV+BEw3dY1DZui4B4sGgf5ipNQVXRVxalpX+uEmMOh39cxspuvY1TTyHO7B+xY9UYiRGIxhni9CH29jb3xOCgKkVgMn8WCwWzGabF87kS3put0RiKg65iBze3tmCWJMRkZ1EYi9MTj+Bx2WnxuVjQ10KjqZKkwRhAYkZ1eXjQbjezs6SEiy7sKgh26Tkc0Sslup/w44PD5qDwAg3iX0ci29nZ6ZBm3ybRrf8Q+RbRfwdV1nd54nEA0TEc0yojsDIK6Sl6f0rPfpU8dgpEwoViEwhzfp+MZJSltddJSC/FYuvfQ7YasTNA1mHc2TDkWokE0SYRUAjGzAK32E2qbdxJ35jB0pA/RMgSjp4Ivw5DLgVC9bQtF/jgJ1cwvf3st8dbF/GrmeCrnfIc/3f83QkuqOV1Sicoaggrj8wvIdbuo7ujijfYIV+ymLO4IR3lyL2VRBxRNY0d3Dx8FI4ixBDOyc/D3fTc+tXS7G7quk9A0lra1sS4WI99oZJzPt89e2YSqppNb+hTFMV4vRQPss3goBONx0HXCuymPdeEwa2MxajUNI6CSnqO3CwJOYGhWFiQSrEgk0BIJZuTl7dNn8fGWFrLMZo7z+Q67WExqGk+1t1NkMjHhCJt0f6UKRoMk8Z05c/A3NCAPUL703uiahlVVB5NgBvlaEE4kUFMp8kwmzH0DMYPsidZ3qmsLh/GKIsFYjCKvd0Cmq/tPo6m9eht3ZdUGArgFgYSm4bbbkVX1gKY3+4cBBKApGCQQiyEKAkPsdqq6umhJpegAikj3F/WrNAGg22JhgsFAv+6sCwLDc3Iw7PXZONAEjf7eMB322J9Cu51kIoHHbidJOlHDYjTuOt7NwQBeg0RVsJtspx2Py5lWEPexzbT60zf9bO2bfu5HAKx2mDINPlkIigIFxVBeAc21cObF4Hei5BTw7iO/ZkzFZApO+RbYLUSiVWDMw+HMAkSQMviqFIybN2/k44/f5Nln36W17iPcupF3n7uZum1VLF7cxpyyUmILF7Nx1VYKJYnx+QV4HXY+qG3CpcDkogIAUprGPzZVMz0zlzHe9GRtv6K4rLkVzWBhrsNJWZ8P4ucRiMdZ29vL2kBgl6KYYbV+6oZI+//t3XdgXGeZ6P/vOWd618yoV/eexCl2eu9AAiEQyqUtLGy//PZuL3d7Z/eye7l0lh5CDyEhhCSQ5vTq3i3ZVpeml3PmlPf3x2gcWZFkx5at9n7+AcuT0ZmxNPPM8z5FCA7n8/w4naZeVbn8LGcUT9b4zGO6XD6u9rYmGAiwI5vFMAwyXi8Xh0IsiUbf8HzNdGYR4OmhIY46DtfV15/xMTrzooaxxnYcfv7SS9zS1UUok5mxQd7jyU0w0kJS2xBTMk3scpm0ZdEaicz2Zc0ptQClJRLBsG1ibveMdVcfm8s4SW2jpqq0JxJky2U8lsVLfX14NY0N8Th7ikUcIYgHArTFYhNH3x0jgNZY7NjcSd00aUkmaeGNNVs102WGTsXE79MZj9MZj+MIQb5cZrhQYLhYZEM8zrDjVGsPgfa6OLpp0hlV8CqQHclihw00n28s4/r6860qSvUbefxQLoNqvr6FQwD1DdDc+vpFpUbghWG47JpqALntGUzXJbx0NMPl793Ay7s+xze/uZWrrjyHq976USp2DM88W+KgaRrCyuDNbwHdzTktCn2HD/DpHz3Fh9/7m/z2P/47K9IjvD0S5py6MPlSEQfYmivz/vbXu933j6ZwNN+xY+iS7fC1nj7Uos5ljc0sDwRO+PMyWUbxLS0tk2YUhRBYQvBoKsWO0VHWx2JcmkzO2ROQ6s+eQkddHR11ddhjtb2WEOweGqLbcegZHmZNNMo5dXXEpihtmcmaxZqzvdFlKnMywwiQiEbZ6PNxYSRC5QxlGRVVRZGbYKQFpj+fp9HlIubxLIq1gqdjsu7qumCQyNiA7lM1ZW3j+IzhWCZDBZaFQuxLpchWKm/6e02cOzeRAjSEw2f8jbpWx9iXzdLiduN3u8kLQXTce8mxjGsqRdCtUBQ2kUhoioyjAkuXgJ6F1NgpUOcSuPQKuO/uaoAYCMIHf6u6GzoYBMVmeN8X+fHIcj54TZ4P/+ndtKDw+//wH3zxvhe58JKrue32D53R52GmCSHo7T3Kg1//MKvqInz3nsdZ6bfYm8rj+D08/FKFO6IaH2kOYZYdWv0Bhkom3UNpbli1CpeqUjZNvnqwl2sDYVbXJ0hVTL56uI813iA3xOMzmlGs2V8s8sjRo1g+H3e1tOCfB3OQddMko+vowNZ0Go+uc0RRiAQCdCgKq+rr8UzxOIQQHCkWeSCTmdHMogM8PDpKwTC4urHxrASM8yrDCDCazXIkEuEcx6ker83wiB14fRPMqGmSVBRcM/hpXJJmS3M4jBCCfKWCYxi4PJ5Fs1bwzZqsu7o/l8OsVHB7vaeccYz6fODzMTxW23js/sbVMNYyGVB9o2pMJGg8hccwfu7cZDpCITTb5mixurmlKx4nNNZheTIbMU5W7XG119UhHIeDIyNEXS5wu3HGunYVRUFTFNqT1Y0hYcskO5Il0qhhezzHNrkA1df9VApG+yASAnuSk6ZkY7V+sZCr1i52duBvv4mLlvv41k++yGjO5tImH9n+l9i0XuOWW2+ZoUd79iiKwqFD3Xzv4b00eDKs9QvWrbuQFfv3cOjgMGva3FwS9nM4V+K69lb6RnM4ZuXYka8A9uVLqCWDZe0d2I7gl0d66XL7j9vpPBVHCPaVStzX10f7NBnFGttx2F8uc/fAALc2NrLc78c3hxryaplDqD433ZkMxbEj6Jdtm6Cu4wHCsRjLYjFWAfXB4LTBriMEj6XT7EinWRWJcOEMBYsABzMZDuk6t56Fo+gTmbMBI8CBvj5eWrqUy2Ix9HT6jHwPRVGwPR5GKhVaksnqEfUZOAKXpLOp9nM9kMvRoaoIlwtlDr1oz0XhsWHgwUCAgWyWmG2TLhROK+MYDgYJBgL0ZzJExu7vDTupAZ/bTdMpfmAVwPr29mlvs3twEItq40qpVOK5I0eIeTysb2oib5oznoFUVJWWZBLFcRjKZinbNp3JJIx73FG/HyF8RENhjEKBo8URlrU0orjH3paEA+lMdbVfUwIO7T/+m6gaXHAJBPzgjcKhXQy8Moz14o/59p5dPLvP4MY1Se54/0dpueFjrPQlOXBoiJUrG2bscZ5Nmy5Yw5/89ifx+vbjfun7sOE8nGyAXM6h9PAjrO5YhXbx9bTt3k7fq68R8vgp6AYhn5ddvf1c1tRcnb9YKNKjm3ykufGE/+Zp0+TR3l4OmiY3NzezbJqMYu32v+zr45BpckdTE8sCgVmtFK1lDI/9mdczhwqQAdJeLxtc1S7wTcEgnS0tKHDSY7gcIXhwdJQDus67OzqqcyZn6PpzlsXjpRKbAgHCc+D1e04HjLbjcGh4mDVLlxLM5WZ8LmONAngaG1GiUcjlzsj3kKSzTVUUWqJRTMehqOuyi/okKFSbQFpjseMyjpVKhSFdZ/XYXMOTDR5rGbW2eJysrpNUFIxSiWHTpDUaPbYp5rSv+QRHiuuam4/9/0y5zPKmpmOZyZDbjWbbDJTLrGlsRJ2hfeXq2J5e4ffToCg45TJqIHBcQ9ax59Hjo01zoxR1RMiP4tJef3CqBkMjkEiOu3MNrr8Vlq4AowSH90A4SktDI3b0vXygcze/+YlltKw7F2/HCn7xsz/FyHlYdcWNwMrTfmxn25o1a1iz8rOEG5YizBcwLtBwKoNs/+UDBI0ukuvXE7zpwyhL12GuWkr4wE5eHE2TKFVY39JMBEFSVciUyvzscB9vbWo5bi7iRI4Q7B0d5YF8ntZAgA9M2MwymbJt87W+Plp8Pt6TTJ7w9qdrfBMWcKzWsJbwGZ8xrE3cLPN65rD2Uxg9jTFbluPweCbDAV3n7Q0NMzrqxhGCp4aGiHs8dM6RevQ5HTAKIcDrJZ5MYhw5cka/l57JcHhkhDoh8Hu9c77WQpJOlqKqpBQFW1UxdV12UZ+k8RnHfSMjNHq9HBoeJh4IoLnd1WPnN6F2+8OZDHWaxt6BAdprXdpnsRymNhh5fGZy9+AgTX4/PUND1I11O89U00zU769ux6lU8JRKqH7/cZlGqGZYcbvBtqFQPj5oTKdhSVd1R7SiVMfr3HYXbNwEu1+Dy6+HeAIaWyHRgKbCubeqUOwBlw/hN7lu3UbEknfiDnac9uOZDclkkr17U9gM89xzg3z1v7+Aqee4/ZYbWHv+ebz88j9xfaIZX10Df/9332Hnrn7Oq8Afd9TRm8uzW7i42O0mbZp0VSyW+aYPFu8dHmZ/schN4TDL4/ETdjSnTZOv9fWxNBDg2gnzP2daplxGtyxGhWDn4OCxHdOHa7WG4247PmNYM1MD/A+WSjzW34/p9XJXYyPeGR7R12MYHLYsbp7B4+3TNWebXmoCPh8tbjfvW7WKwqFDZ/4byp3T0kJmmngdR3ZRn4LaPMdSuUxEVbHglDfI6JaFY1kMZTK0BoPkHWfGMo6nqjY/MTduHl1eiOq8ypm4LiGgUsFj22/INB7HtsEqw/igMVEPl10K+3fCW26HTZdVM4vhCOLQdlA0lGQLNLUj8ocRwQCqF0jtg9YbQMmDCAL+eflhadeuXXz6079Hc/N5/PhHX2bFUpPV7evpH3axucnk7VevwaN08OSQlz//g7+gzYa7/ArL3S4yRRO/N8JVba385FAPMZefq+sbJv2ZdYTg56OjbC8U+EBLC3Un8YGhbNt8/uhRWn0+bpnBLuhaBtEclznMANsNgy7LIq8orGxooG7cZqAT1RrOBFsIDpXL/GB4mGtiMbr8frwzfFycMQy+NzjI5mSSjlmoP593TS81JV1HSybZe/QoHYEA1pneAe12k7Is0HWZaZQWHreb7rEuartSkV3Ub4ICRHw+wl5vtaGoUjnljKPP5QKXi47GRvqz2VnNONYoikI0ECDs99OaSNCXyxFXVfb199Mej5/+dSkKeDzVTKOmVTONxeIbb6dpgP/4TKOqwNNPwuqV0NIG+W4IRsnZaQpP/StmvpnO934SLB3R8xw5u8x+X5CAO8TS5B683g0oylzJ05yaDevLPPndT/Nr11rc/QBkt73IxoSb2976MUbNDP09j/O//uU5rmsI84H2JlZnMjzWM4IPiCkgEARKZVY3xKcMFu8dHmZ/qcSvt7Udt95uKmciszgxg5iBY5lDD/C2xkbqxoKoE5VizLSMZfHEwADdpsnNDQ10nYHEkgM8VyiQcLlom2PNinM+YATY19fH0lWrcAqFs/L9VJeLjGmCYRAMBs9Y7aQkzQbZRX16FEUhMnZcnXe7GSqXiZgm2VKpulP6TbxpqmP1krplkXC76U6naQ0GGZ7FjGNtHl1bLIZumiTcbg6lUnSFwziaBsrUO6JPSFHA78dZvRr1wIGpb1cLGiNx8LtheAjWrIPV6xG7t1E+8AjWLW/nc//9HZrZhzd1mM535sEfonLunWx97sf8+f/83/zW9UO0/uYdqK1f5N6fPEzA7+Ktb3vnqV37LBFCkM2mee4Zkz/6zfX0vpgllTvEqG7zwXPaabrwbVT6fsWX/uXb/Os7LmPzre+n7rnPo7xQotwD43+zFSYfVy6E4KHRUfaXSnygpeWkgsVazWKrz3dawWJt00x3JkNvufyGDGJcUc5K5nDaaxTi2HzFeq+X2+NxomfoNXMudUVPNC8CRsdxeLK7m9Z164j29JyRQd5v4Haj19WRrK+ndPDgmf9+knQWyS7q0zcx4ygqFaxymfLYvuo38wY3FzOOUK0tFG43nT4fVCocGhwkHgpRFwqd+tGu42Bt344ol3F3dsLIyOS383ggnkTk09XsYH09DPZDyE+Fev75r/+VjovfwpU3vJv49h+ja5A/8jK/+bufZPjgbsyKiV7YQDp3FZ/50oU8/4zO17/++6f+ZMwS0zRJpfbzB3/8t0QjGt/64V+yscOh71APh/Mqzzz6TfpLv8If9OC9/Db++mtf5VpPmWXnLOPll/o5XwiaT/A9evJ5doyM8LHlywmcpcyiEIKhQoERIXhmcBDL42G9yzWrGcTJOELw9Ogor2WzdMZiXHoGawrnWlf0RPMiYISxOUd79py9WkYARaFvxw4iXq/cOS0tOLKLembUMo54vQyWShTHz118k3Mcp8o4HttE4/Od9X8fhepjFB4P/nAYL9U5uZrXe6yB5s3fqYJzzjnVhhZVnXyUmW1DKgOJKKKQQ9mxvVrX6FFQVMFdvijJjhAvfOkf8CRydHbu4f4nvsd5Ygc/KzlckNR4zxUb+OI3PsuXvtnNp//snQyXrmP0wAGWLVt2Gs/I2ffMM3soDf0nfVk/a5evZ/2aUe75EuzZdZBk/0G2GSCExjM//BpPPbaThtYQP3i+h3DFIXiCuMNwHH6YTnNOXR3+k3ifO53MohACWwhSus6WdJrhUokwsLGhgS6//7RXdM4kAYyUSmzJZDjsOFxbX09rMHjGMv5zsSt6ojnf9DLeqrY2NloWHW73ma9lHCMcB79lEfP5ZNAoLVhyF/XMEFRf+AeyWWKKctqbYxwh6M1msS2LuKZh1O5vbHPMbHAch950mi6fD+F2o2jaqWVANQ2tsxN3Lgejo5PfRlEgmYANq1FeeBbedRdkhuHATnAJeuJJLCdPJeFGdET4yy/8imuvaOJH33uRiHD4s/NdHMAmFVrLx//+31HUAWj9H2hzMHszlVKpxObNF3HuOQqvPb6bzrjG4YEK14WhUYOnU+ARsNQHr+kquu4QUyAGrHK7uFiDmBbgnNYWHt2xh3MaWmkaF5A82ttLv+PwjtbWM9oNnS6X2ZrPk8pkOOJysdLnY1N9PS5FmROZxPF0y+JAqcRPUynO9Xq5MJnEfYav8ZCu89DQEDcnk2fsuPtkzduml/HOdi0jVEeSlF0uFF0nKoNGaYGSu6hnxlRzHGsZxzc7ikdVFNrH9kdndR2PZTE0NhdysDY3cYbGhJz0NY3txq5YFoeHh+kKh6slDW/2GhwHJ53GaWhAzWSqGcWJhIAN54BqAWMjdboPQCgKoQCdN74VsWolh579Cn1D8OF3v40/+eyPKI06fKQTli07l/rN76Z9aSOHKsN0Lb2Ih3/xM66//ibc7vnR8PWTn9zLUP9Btupu/vG372TPM8+ydHMLy40RenwdDD/wKB4HTBMuVB36NFgbCrDRJVCFm5gCjC0BUhTluAxZulzm1VKJdzU3nzBYNE4xs2jYNs8MDbGtXKbR5WJ9UxObXC5ic3ASSa1W8bsDA5iGwa1NTXSchevMGAYPDw1xyRwIFqczrwLGWallpBo0llwuhK7LTKO0sMku6hkzcXNMl6ZRNoxqRu4Ujt6iPh9CCEKBAHuGh2n0+egZGiIWDKK5XMftbD4bVJeLzqYmFNPELpUoKAoR/2Q7oacgBITDKB0dMF0DTH8/9B6CQKD6Z9uB7m646howihy891P854+f4gNXw7984yj5YZPNYbi5wUsskiAesTioGvzrX/wu739PK83NN2FZV8+LgHF4eJh9e3/Ik/f9Lm2rryQ3/CIXNA+TaF7DSF+OZ7f/kGYvXOR3ky+ZZEwI2Aq/vaKdZ470E7Vevy8FhZWJOrwKx1bjvZLL0ep2kziJn50tAwPUq+pJj84RQnC4UOC+TIakqnJbYyPxWSipOFkTaxUvaWo6YRA9I9+XudsVPdG8ChhhlmoZkZlGafGQXdQzZ3zG0bBtjo6O0h4MMuI41fmGb/INqbY5Zm1jY3Vuoq4zUioRUVVGCwVcLhdtZ6m7ulbbiMfDcKFAvlgkomnVZpWTfFxiZARzaAjPypWwa9fkN9q7F268Ho4chL6jMDJYvf8Xn4PRXl7e8ijmoQGe6INoAT7RoHLXijo6LtiEms8ihgfY2v0zHnncxW9ddz7epiFK6c/g8/0RijJ3j6aFEFiWxW/99idJJi8FsR+vr5EHXkjzky88xE2rG3nwEYt3xz1c0N5BOmfy1N4eVrgVyrpOsWSwoamRgUyOlpAfRYFoOMTW7j4uC4YASKVSnNvSMu2RsBCCvaUST+g6H2488TpBAQyXSmxJpzlcKrE2GuXik9hZPVtsIUiVy2etVnE8ATyVyXDIMLg1mZxzXdETzbuA8azPZRxHZhqlxUJ2Uc88VdNor6+nP5+nTlXZMzDAyvp6tFMs9FcUhajfT9jnI28YjGQyLHG7j3VXFyyLhlDorBxXh4NBwoEAhq7jFQLV6z25oLFUgssvrza/TEVVobERDu2FZ5+DSy6B5x6Hcy8AI0dHQeeTDSG0hnruasjQVBdAq0+ibL4Vzr0Sy7Eof/4oj/7kh3TFRhnseZhgNIBl7cXtXjNzT8IM27ZtK489dj+/93t/Xv2CsornXn2aJ5/+IRG9n32vdPPO2z/Anee24GpZQf9//i1d+SbqyxXqfF4ubqpjIJtjbUsT+w/3YeVdCCHwaiqKojBSKtHjcnH5CepP95ZK3D0wwB2NjSRrWd4p6KbJvnKZ+0ZHucDr5QNdXXhUdc7OM67NVewxTZb7fLy3sfGM1yqO11sssjOd5va2thldK3imzLuAEWanlrFGZhqlxUJ2Uc88RVFoiUQwbJu4x4Ou64xWKqe8MQaq/05Rn49oU1O1u9rjYevQECGXC822cXu9hMa6tc/UG7cyNrvR9vkwdB2fouC43Se1IUbs2oUlBK6GBhgaeuMNXC5ob4enHWhohNa26tf37ARhsXnNeli5CjID0HcQlqwADehcAyM7cPtivPfP/wn8QbD7aVraCu5LgYeAZVTHQc8ttm1z770/wu0+SKVSwT0W1Ol6metu+QiB4e+yLrCC4HW/xzc++w8M3PcQl6w6hx1HX+CmSgXF0hnIF2gNR0HTKJgmnR43aBqhxiRH0mlGSiXW+XzTdrqnSiXu7+/njqYmlk8TLNZq/749MICj69ze0sKSOdg4W2M5DgezWR4tFKj3+bjtDM5VnErFcfhFLsfySGTG1wqeKfMyYJytWsYamWmUFpOJu6gNRSHs883aCruFwKtpoGmkDANHVTk0PEwiGKzONzwNtXmO61pbAejPZAjbNq/197O+vh6Tai3kmZrrqKgqjt9PplhkcGSEFQ0NaCf4XiKbRbnrLnjmmclvcMklMNAHvUfhpluqW1+EgFwWPvjrcHg/vPIk1DfCR/8UYnFo6cQKunjtyce54Mrz6cvmIf9tWtr+FyhBqhHlTcDszLecjmVZfPaz/4dvfP2L/M0fXc399/8XXV3XkEql+Zu/+X00MqjkaA8Ps+yJG/jJD0fo8Dr8WH+FpZbJpqSKE/Gz/vzzUCIJSDZzfjwBA8Pk8jrdxTyODirVTuqp2ELwWD5Pq8s1bfBnC8GWkRFey2RYUlfHFc3NuObwe+KxHdAeD5sDAbpmaVfziyMjhBSFjbHYvHktnZcBI8xeLWONzDRKi0mti3qwXKZULrNCVWVDzAwIj9sYo1Gdb+g6hfmNE9Vq0tricTK6zjKvl+FSqZpVqqtjeKyGkrGayBmlKDheL/UuF7ZhVDObJzp27++HwcHJ/05V4UhP9X9bWuD5J6od1evPg1AIXn4WLrwSOjphzUaES8Px+nnl3i9z+OUHiGee58GjBhde+U5a2sJwLDyo/vxWKhVs28bj8cyJcTv3338vpvkrGuoVioeexeVs5f/96Cvs787Qu3eQoF/QGYaPvP0Slmxs5LLyE6T3ZdHKClYGYtEwypKlqG/5H5BohJ59KP2HcCoRnFQORwhAwaZaQzcZRwgeHh3loK7z3ik6qGsbWn6VTrOzXOamxkbaZ3kjy1SEEBiOw/PDwzxjGFydTJ6RHdAn65Cu87xh8LZEYs7XLY43bwPG2axlrJGZRmmxCfn9hHw+8pUKISEogWyIOU21jTGWEAzoOjHbJl0o0JlIzMibb218ScTvpyWROLZJ5oXDh2kIBEiEQjM+19HndoPbTcW2EbqOx+ebOmi0LMxnn0W55BLUJ54Aa1xrbzAIF5wPj/2i+mdDB8OAQBCuug6eegT0cjW72NwO5RyOOUDfy6+y8Z0f5/y3vAM8ZT7mSeEKXs7ExXjDw8P81V/9BZFgLx/9+N+zYsV5M/YcnIqBgQGeeuoR0qN+7OEBmipx7r2/QEs0ylXXlfhcr0BVFG49bz1XXXwrW/Y8xzNWHR99azuZ3RkKO4dYcc0NqBddD9e+neH0CMaWL1HfrjCc7ODQq9swDQuvqfEqcEU4POl1HM7n2TE6yoeWLp1y88vBUolf9fZieb18oKVlztbgje9+Tvj93JlMkpjFkT6jus69w8Ncm0gQn2evnfM2YITZrWWskZlGaTFRoJpB8njYl80S1TTCsiFmRtS2vOQNg5iiUC6VSJkmbWNzGGfi/oFjm2QaFQUvnNG5joqmYXq9KLqOe5qgUWloQGlqeuNfrFkDHhfKrh3gdkNrOwgHVq+r1jbu3gYeLyxZCc1tkB2AqMqQvoPsq4/iSYR48MFv8OH312PZHXzta1/kpptuprvnMNdddwOPPfYY25/+Bl/9t+UsWaZhmuaxesGzLZ/P8+sf+zD79jzB2mVlEnH4m2+N8onzQ1z3oT/mE3/4N9Q5YFoq13bFOfDYT7nnmd1sjLkZNAbwNLTQdzjKRedeBEtXYBlp/u/ffpzeLVv4yDIYGlEJti1h67YDrDYgqih0dXS84ToM2+bH6TTrY7FJN78IYH+pxHcHB7mpoYGlfv+cDBZns/t5Kg7wbDbLWo+HjnkWLMI8Dxhnu5axRmYapcVGURQaYjGUsYYY19jGj9k64llIwmNvJN25HM1uN5ZhUBTiTQ/9no7P5cIXCk0513Emax0VTaPi9U6baRTFIvaDD+KKxV7fLe12w003wiMPVadS3/xWGO6HUBjueA/c/UUIR+DDvwP1DZAaglKaF+7+FP/20A7+153D7C3t58q33MbNt32TS1Z/h6Jjck7rfn7++NN85Sv/xu5tL9E3UOFnz7WzyXcPxcqFNDatZPXq1WfteHrX2DihAwf2cc0mG1/O5I7mCP+8NcdyP9xw/Vvo6dlHQhQ4bILQbT5/zxOsjWps9jjcsW41/ouu4sXMFgb2eWDpKuhYwY5XtvC9B14gYcBOFZpiKvcPHcKwYRXV+sXJgqfavMVLk8lJPzjsL5W4Z3CQ28eCxbl2AC2E4EixyKuZzKx1P0/GAR5JpThi29zR0DCr13Kq5n1kU6tlDHZ2zup11DKNWV3HmaXAVZLOJpXqz73udrM3naai67N9SQtKSySC4vOxP5+nVC4jbHus/mzmKGNr2dY2NhKPRPCFwwyXy+wfHUUvlTiSTuM4zpS1bif9fcYyjaauI8YfOdcMD+M0NcH69dU/qyq85S1gmyh7d1e7ozeeDwO98IlPQs/+akbxQ78Nw72QHobBwzDST9uIzqfuvImL3/IR3vbRL9Hdt59SpoQY1Alb7fjdfg7sPcDe17ZwcUuZzrjK9a15jMEd2HaFhx76AC+//BR79+7Btm3syTbQzAAhBE888QQf/MC76d39aQqFEk++YnPFjbfSsLGDiAN6BbY+9Qzf/cKXee4QlAtwZVjhY10xPrSygXfedgeBmz/JK6VO/uAfjnLpW99OMRzhge/+f/z+7/8+CbtCyYJ7jsDXt1ts73MoC4XOurpJr2d/scgTus7ldXVvqKEVwL6xzOLtDQ0sm4PBYq2m8ieDg3i9Xm5rbOSShoZZDxYBDmYyHCyXuTWZxD0H6zxPxrzOMMLcqGWskZlGaTHSNI3WRALTsjDKZdlFPZMUhcaxTG5/LkfFtmestvGN3+r1uY7jax1naq7jiTKNSn3962N1Ojpgwzr47KdB1+Edd8J5G+G888jueIlwIYXa2QlH9sGyNVDMQbwB7Bht//wjiEQguxPHF+BHD/bzz7cF+covilyhDbDj29/gk5v9/LzOTWYoTqt6lMqLuxkKdXL/ax+jvn4J6z5o44rE+eY3v0xbWyvXXfeWGX3Oh4eH2bp1K3/zV39EZmQ3pYOdPPzsp7nrLoMvfu5Z/mWbQasGGQt+8PxhchbUK3B9VOO3lgTZlyqwdMNGArf9LjlzD9/49t9zyzmdPLb1WT7yv/+dYkHn0iA8oUPcgbdHfBwt6lzj99FUNIj7/RzJZI67JksIHjl6lFummLc4MbM4l+iWxaFSiWeGhzG9Xt7V0YH3LAyvP1k5y+LxUolNgQDheXwKM+8DRpgbtYw1sqZRWoxURQG3W3ZRnwGKUt2hbHu9Z6S2caKJtY4Jt/sNcx3Dp7jibbqaRmf79tfnMSaTkMugJOvhhpvgHe+CYAC7UsR6+JsQaYB1m6GtA5raIDtSTb81tEFuAFxFSvj4+tf+kJEdu/lR0c8rg5AQOh9N+Oi68k9xNzzJ83t2sHU3PH1Q4BG7+cO33cnq2+4Edzf3fL+ff/iH/81f/N4ydjQmcXuirFp18oO+a93XAHv27MblcmGaFj/96X1s2fIEK5p2kqgzMDIW93zzAT54bTtP3Odw5w0f4vuDX+ddMRWzUOHpQRNseFtE4d3Lm8kWMqyIxghoXtj1BOndP+aVXxQJVHYyVN6BboFhQZ8Nq1W41AUlR2FTLIFdKhGvi7+h69kRgkdTKSyfj+UTMofjaxZrmcW5whGC/ZkMWwoFzEqF85JJOv3+OTXX0HAcvj88TMLrpTMSme3LOS0LImCcK7WMNTLTKC1Wsov6zJlY22gbBtoZfm4nm+sYGevirgsGT6m7eqpMo8hmUW69FV54AS6+GAo5+Ou/r2YLK2UQJnufuh8rCP6OAErYgzmyH3p2E+laAZ0rIJqAnq1wpJu/+Py3+d59z/OR5Qr3v1jE48CmkMrqi2/ly8/3cd8XH0J1mWDCFWEvq3//MwzXBRkumhw8pPHTu/8OnzKCr7+B7c//AVfd+imefvppAJYtWwbAgQMHjgWFmqaxefNmNE1jeHiYf//3v2bfvtfIZi1U5QDNzRfywpYXqehpOpbYPPwQvGWTn7aEj1f26Nz38yPcetFyrrplGbtfXsfH/uBDPPS9+3nu7odY5oa1HkHP0V7qfF7WNIagdRU0rKQ19F6+aKQo7Rrlpy/lsS0NxYRVGoyUbBQb6lUXl0VCfCed44Z4hIxpUh73b1Lriv7g0qX4JmTAhkslvjc4yG1zLLOY1nW2FAocLJe5PBCgc4rxP7PJsCy+PzxMs9fL5mh03tcALoiAEWZ/LuNEMtMoLUayi/rMa4lEQAiKhoGp67i83hltiJnM+LmOWV3HY1kM5XKYlcopZRwnzTTaNtaTT6I0NqL6fShXXQHxGOae13AtWwd+L2FXnm8M6awp7+byjW/nc5/5J97TWk+kvR1MHewKdK1HHC3gFiZdQRcv9lnUCzg/CYdzDvfd/wSP7u+l1Rb4gE1NENQrpH/wXzwyfJTLr7+UgjZKe7CfQ4aDX8S46NI7uOfbv+Ser32Wj37Uxze/vJZQOMGjv/wlq5eorNywhptufSdwEaChKApvfetbgWvJ5dI0uP4PX/nidtxWhvdesYQfP3uQDyyFc7ra+NsHjmDosMKjcvO56/ne/Q+ye+9B/urTX+WpZ17DKcFbm32sbajjUN8ApunwSn+a8w2dl3ue5f57n+LmeIbGDQkuLYW5+sLLMLdsoS9rEYg18cTW3axUVPpSac71+In6fPxqcJBQvJppnK4r2haCJ9NpLvB650Rmcfw8xW26Tr2m8fbGxuoO8znokK5TMQwuqq+f98EigCLE5FXUF86BH443a1VbG+uzWTrc7jkzPFQ4Dn7LkplGadFxAMVx8FQqsov6DHCEoD+bpcvrRR17fn2nuJf6zRJCVL9/JkNEUTAc55QyjsK2cVcqeDs6YHQUkklcgOvDHwQKdIc9vPyLn3HbBz/OtnyOP/uN38BVLvN/3r2Je3YdJKop/M4f/Q1qayscegFGynDTr1M8/Cqlo8+z//nn6Bo9xNee6mVnWtDggpINIQU6PXBLaxDb7WLpOz+Eunwt9mg35vpzuO59v0Z5VMe24EPn13PDjcvIeHfwiX/Kc/0S8LgVOtadT6RlGe/7yMfR6i/G7faBojKx91gIh/RoH+mez6KNWtz/hf/glX0Oh0cFTUGVZ/sd1gbgkrjC7996ES/bo3zzVwd44RAEBVzqh+Vu6LcVwo5gpUfh4s5m0kaZX+aKqFi858bz+H7vEZxIBzdc2EJkXx8iuhZXooU9n/s8Md1NX66Iz3FzTksLD+3Zw5qWFpojEX7Z20uf43B7S8txQb8tBD8bGeFAucyHWlrwzHaX8YR5ihdFo9Sd5oD7M+mQrvPj4WGuTyRon2enLFd3d0/69QWTYQTYc/QoPcCHzjuPUG/vrB9Ng8w0SouXCjDWRd2XTrMkFMITCICizJli9PmsNrfRsG2Ojo6yJBzGFwzCWXgDVcY2xJxuxlHRNMSGDZBKVdf9jYxgL1+O6+7vYB3awZPFQZa3JNlhFhDnbaZZMzlQdnjw3me5Julh/ZUb2LPraZqPHuU7T7yMJ7SSdY4GrsN88wd7eP8Kjd0r1qJv6SWoQFKt/lw2u2BzS4LmSACtqRnXBdcjNlyCWSny2f/8FE2KhVankUrbXL5hPaVyhcG0iw4PXJvwsGLzZXTc+ZtEm9dCeBko3imfd0VRQQ0Qavtt/t/df8aVXWGsfI56IcibGlcnoaQ7FHTBd545yLCRZ0fv64Ft1oSXK7DODV0uha5QmIJu4nN5eV9CQ+/sRHv7H3BZeoAPfPKPiaT7OKjB2uE8F+Tq+VUhywY7iH/s/dAUgm5FYZ2iHOuK/vDY/M3x9qfTHCiVeHdz86wGi3NxnuKJHBobzn3dPAwWp7OgAkaAYDTKgf5+LozFqKRSs305wPE1jX6XC1VmWqRFpNZFXbEsDo6MkAgEiAWDs31ZC4aqabTX12NbFpVymbKiEPH5ztopS9TnOzbPcXyNY2cigXISgYZ98CDlYhGvpqF6vYhDhzBXrEBrWcK6bRlcR/KsWamwPdLIk6OwygFvBdatvhD/+/+Qz/313/LCc69wQx28UnmeR37yEnG/m49d1EXLNR/lt/75M9zgFbj8sD0LnS44vz5BwqXxUs8AGyJJlOwgr/zoU/zPv/scFwQVvvE7V/DaoMVffXYL373vCS5ucOOEwvzdpiQb3vM7+G/4KMqe/4b2NYAOyvQlAfF4nHK5TLpwgGeNPEsaIlx00aUs33gZX/r8l9mx9RBDZdAOj6A74BgQE3CeG5p8fm5LRliWrDsWJPWNpvAhGHJ5ab367Xzul1v4zOe/Qr5Y4VtPDXBOQLBknYXdEaA57mbJZdfw858+zaaCxZaBAaLBIHWBAD/ev3/SrmjdNHmiUODqUIjoWcpaTyZjWTwxMDCn5imeyPhNLvNxOPd0FtSRdM2q9nbe4vFANlv91DpH2LbNUC7H0kCAOvmGKS0yAsgbBlEg4naDyzVnj5Pmq3ypRLFUYkVd3ax1qWd1nYii4LJthk2T1mgU5QQjToTjoOk6PrcbxedDiUTQjhyhu5SnORLgSNDDV9OjvDJSZLMb6jS4fdVSHlV1Ht7bR0TABQEFTYNVdX6W33wr7VffiL28i9/+i7+n8uIT9GXg/KCPq4MKCa+LbKkMQmA1tEJA5Qe7uxE2/Ma5TeytC/OD146wpKBza3uYq9d1oF79TuhYBeEopJ4B3wgs2QxNHzmp5yWTyXDxxRvYtGqEP/3ER3ltxyBex833PvcdGk1QHIXLGqI83pclgmBVXYyhVJZ3drbRGQ1RsR129g0ce0/LlsokohFGmlv4wqs7yFccEPDuRJCrEj4ar7qax5LN/Nf/+QxChboMfAQX2xSFO5ubeaVcZn+xyHubmo4b0C6A7bkcTw8O8p4lS3DNQoBmOQ4Hs1keLRSo9/m4KBgkOg+CLwd4cHAQF7ApmZztyzllUx1JL8iA0aVpXLZ0KZcJgZ5Oz/blHMcRAmybOOD3eudMraUknU1muUyqWGRlMokqs+0zRgAIgWYYBF2uM95FPZ3eTIY6TWO0WGRFQwPaiTJVQqCWy3hdLlSvFyoVXJZFr65T0HX6KmUqHhd+u0J7LEbUq3H/8CirfaCEQ1xUH0KJhdA2nIfytvfBBZfz6CNf454//WOi0S7Mo0eoqxhEvAE2eQW50ut9wvFwENOpkLVMFBVe0CGswfUtQVY0RAnc+n644xNQTkOsDdxeUExQnwfPzZzMYd2nPvUvPP/M32EMlnh1t5tfu3YVz23bjTdv0urAjVEPBd2kPhJl20iRc9yQxse1TQleTefZOpSmLRBkmUsZe7oEQ7k8Aw7kHOhyQTgSZKhY4pquFhJXXMaPX36SmFHk317J0W7DMgeWBAJsiMW4d2CAD06yK3qoVOJLYyN0lpzlOOC4eYoeDxcFAnTFYvOiYaS2yeWgrnNHQ8O8Hc4NiyxgVBSF9miUW7q65kwt4xuYJjFFITCLS9AlabYYto2wbSKOQ8DnQ6iqzDbOJCFQDQPTss5KF/VUdMsC2yZo2wwZBm11ddPWcR+XafR4oFLBY9uogQBZXUdYJrlykVDYR95xiPp8eB0Tf1BD8Wpw+7vgI78LgSD5gV38vz/7EHes7aJo9PE3Xz9Iu27wtoSf/aZC3ChTN3YpAY+H9Y117M9maIgGKWkaAcekIeYDjwv/5itIFQeI3PF+Ht61h2uuv4kvff8Zfv3OJL6WD1drGE9g+/bXSCYe4Fdf/xSDz9q82gM7ekvYhsWvt3p5T0cd/YM5MmWLiuInhCCTLdFtQszl4YJkgs5oqFrqWSrx+GCKVK7IkrE9PALICugMh2kOe8iWsixb085XLZOfP3MEnwnn+wO8t6mJ7wwMsNbn47IJ6/+EENzb14dfUbhist3eZ4gQgrLj8N3+fkzDeH2e4jz5MOkAT2Yy7CqVuDWZnNfDuWGRNL3UCCEoCjHnahmP43aTsizQdZlplBYdr6aBpmE4Dpl8HtNx6KqrOysNG4uComB5vQzoOl2ahrDtWRltVJvjmNJ1VE3D0XXyMGU3taKqOH4/ermMVwhUr5dKpYKnVCLq9yN8PsLBEH2pFB0hX/X42hWCpV0QcENTCxx4ngHNzW/8+d/T98IBmvb18H+PWhgFWOqD4UyZ9UEfXS3NBDxuYgE/CgqqVeGCBg9qSxtOQxIUBXWwG1waFMvEMlm23ncv37r3FwQPPMdrex145+9jF3bz3LYiK1euJDnFMeSRI0f4j//4D3oOvsTKjgLBikXHqs386QeXMPL9e7iovRF322qi1y5j9MlfoTluvvbafoyixYpAgNuXdOBWVVKmyS+7ezlcNjnXH+C25Stwqa8/j7ppkimVOTo8Siwa5pdHy/yge4BiBW4IBPiNtjaemWZX9OFCgcOlEh/o6pq5H4ITGN/93BmLcUlT05ybpzid8ZnFtzc04J1H1/5mLciAEWA0m+VIJMI5k+0tnSNUl4uMaYJh4Pd6ZfeotOgoqort9RIBSuUyacuidZ5vQ5gral3Upm1T1nX8s5jJjfp84PMxXChQKJUIaRqK2z15baOiYPt8GI5DYOVKxN69VCoV1GKRQdOkta6O9mQSbBsKZUQkgJLOQOe54PZj/uKHvLr7WV7YcogmFX54yMJvwnJN4WINokC5aLCnNMD5zfVofi+OqqF4vCimAsvWoh7ZAwE/vPVDkB+FczajFlI0f/9f+eRVm+hafTn/+O7zcMcbeeDhbfz7v/9v/vVf/51nnlG58cab8YzVj27btpVyucyf/Mmf8OzTj7NhOfRWvHzsLVdy/kW3YG37HnUxDdfbPoKzYTOVnp2kdj9HOqXTny9T7/FzXUcrqqqwa3iEn40W6PL6+VBrA0Gvl539r9c0OsDzhkW8VKYO6C1UyApY7QiaAgE+3tJC2TB4rVzmHU1Nb/g5sIXg5XSaNdHoWemKno/dz5M5tiO6vn5BB4uwgANGgH29vXxDCO7q6sKbz8+pBphj3G5SpkkplSLp9xOfZIenJC1k4bE31+58nkaXC7tSkWsFZ5KmUQEyZ3gX9ckIB4OEg0FyxSKDo6NT1jYqqoqyciXWzp2omobi8dBTKhFXVfb19dEej6O6XPhcfsiVELE6lMZmuOQq+lub2PHMc1wXgaMFcEy42efiXR2tLAseX2qlCUF6NEPBH6CjMQnxTuhaAblRaO+E1k6gE2JJaF1Cw8f/hYamDhSfh9cev4fRA9/l5//9S95+cZzUkWf54X2PM3Dk+3z4Y1/CdhT+6R8/yf4920nEmtm8FCIW/MZlXXzmx89y+5EcO3Zv4+OdbgaWncu9X/9Hvv/j5xgctfA4sFmBO/1uIm4X9/YO050p8ZZImGTAz6vZPFtHDtPmD7JsLMRSgev9YZa0dKAAqXKZX+VydBkGH29rw6Oq/CSVotnlIj5Jydloucxhy+Kdp7Ev/GTNx+7nySyUHdEna0EHjI7j4ESjlHw+fMUiYmyF01yjuN0In48KYJkmrnEda5K0WDSHwwghyFcqOIaBy+ORawVnyrhd1Ga5jMfvn5Xj/1og4vh81LvdWLqOOmGndI29fz+2ZeGtVNACAdrr6tBNk3qPh+7RUdpCIYYdh+ZQiIHte2hD4cBIH//8kx/h6h+kzQc7LLjCDZdoUEyn2ZpO41JV1raM1edpGqovTNu5G+Ho/urgbSFAc8ON78LRgEAI1esDTUNZdh6iOES5NMS+Qp6E0sJwn8XvblL54cP3kO8p8c4/WoWqCiqmQ1dXHXURL8W0w0t7YFsaLt7rxspY3H3vK9y41E1myeX89Z/8JS+9tBO3I3BsqFNhiRc0BA8NjNKdLfOR9laGKhW+3tNLp+rm7U2tdE4R3O0pFrmvr492t5sPtrTg1zR2j45ysFzmvZOs0BNCsCWdZpXPR90ZrHet7X6udT/fFo/Pi+7nySykHdEna0E2vYynKApNdXV8bMkSilMUcs4VAtAqFRIuF26ZYZEWKUcIBnI5Orxe3HJDzMwzTQKOg/sU9kDPNGHbuAwD7xRBIwCVCl7bRvP7UVQVQbU5pjebJa6qjBQKtMbjKMLh4ZFBKljsd2xSDqwMhblQO77UpyMUZMQoUjBN1rQ1obrd+F0uMAqweg1ccxOsOQdWb2Dg+ftQIm1EzWHIDeFbdTGlWCs3X30hTSLN0SLctLye1tAII3k3v/OPf4W/Jc4vXhmiXInwD//0JZaGD6DqZVq0EKYTZs+QTrCUZnlY468+/DZ+sGsnS1piHHrlMJVigD17DhG0BT4BF/n97MgbfGLFCg7rOncfHuD9jU0s9wcmLS3QTZM9pRLfHx7m3U1NLPP70VSVsmny9d5eNodCrIzH3/DfDebz/GRggDu7us7Y79rE3c/zpft5MrkFtiN6okXV9DKeEALLtnl6cJDN8Tj6XGyAGaMAttvNqGnirlRwu90ywyItOqqi0BKNYtg2h+SGmJnndlOuVGAWM401iqZheb2g61N3UXs8GIaBt1zGFQ6jOA6Kqh6XcTycStEaCrFc86Li4bygm9FygZZQgPZ4HDHWSaypavU9Ie+i3e2ibyBFUyzEEWHTEgmj4kJZsQ7RtYxS+iCPPvIdOhtifP/pPtb50iQ7HufaG9czPJgj5nNICkj3prj9E7/ByvM38nKpkS/9/t9z14WvsH0oxIG9aeqWa1zaofCeG67gOz/dRjBlk1Qh7tiw7SXWuwwe7FZZcs07uCBaIfd3h2gGHBteNmFzXZyjZZ27jwzw/sZmVk5StlTrMv5qXx/oOu9pbWX52KxfIQQHSiVsw2BpW9sb/1tgRAjCgOsM/CzYQnCoXOahoaE5v/v5ZBzLLHo8CzJYnM6CDxgBUvk8eizGwOAgsTl+3KsoCpbbTW+hQJei4Ljdcp2gtCjJDTFnjvB4KJkmzMJmmIkUTSOlqse6qBWf740bYrxezLo63Mkk4sCBY1/2ud0It5uu5mZ6s1maQyGG8nlCqoeM4yLhuHhh30GylQpxn4+1rU3kTBOf203A72eZt4XeTJqYW+PgwCjLLqhDf/BH7Cn38qUHHyGb1WlOwIF+6Ghws3HlRXzuu0/SoJnkS3B9HD549XmELr2WF/e8xu/+7f/kwpiLaz9wJ6mhn6AheG2vxQXRJp56ZhvDR45yZKB64q1ocO8rA7StWs61d7ydz33zBxwqZtjUEWXwYIa9Fqx3u7i0PslXd+3lncnJg0VHCB4fHublVIoViQTXtLUdN2zbEoKnBgbY1NiINsl7ieM47BwcZGVDw6R/fzpqtYrdpsmVDQ10zOHdzydjoWcWT2RRBIy24/BkTw8H3G4+vnIlhUOHZvuSpqUqCs3hMLrjkJU7qKVFTFUUhNuNayxQdExTboiZKW43g2ObYUKaNquNRrUuasMw8Oo6rskyn6qKsXUrbq/3uONrBY7LODZ6PAykUqyOxugvG3TG6oHqdqGdRwbIGAYbG+o5mMvhcrlpi9dRsS3a3F7Uxx/HcXQOjI7SYDlEBCxLKXy8K0jrJZfg/fAf8q0/+18oNkTVakONb2iIu//qDwk5ORqdEhcEQ7z8+KsYQ37edW6F0rDJ890FhC14bRRaXPCexgANmsNw2eSFQZ1t//tfSFYUbr7tEj795Hb6C9AuFH6vPU5PKoVw+aYMFn8+Osr2UonbWlreUNPoCMGjqRSWz8dSv3/SDL0pBBkgPoO/UxNrFW+fx7WKNYshsygYWy4yhQVfwzjeqrY2NloWHW43Vqk025dzUoTj4LcsuYNakpAbYmbaXNoMUyMqFXyOgzZJplHYNm7DwDNNzeP4GscWt5uAx4My9rjssSUOBcOgJ5ViaSTEQLlAS30c1e3Cr6gIq0yRCoeKBRQVuuJhgiuWwbs+xMOvPc4nP/MdvIbNSg/cXOfmgoiCoSo0BlWeLCtcd+M1JD/xTxDyMvzcZ3jpy5/hhy8JCmXwOLDKo7A8HqM3r6PpZUZNSCoQ1+Cwo/GLvM0lCmxSFN6+ahVbj/bhGHDBhONkRwjuHR5mf6nEr7e14Z/k96E7l+OH/f2TbnSpebW/nz2Ow1uammYky+wIwYOjoxxYALWKNYshs+gAL2YyjORy/OsUy04WRYaxZl9fH0tXrcLOZmf7Uk6aoqoUNY1D2SxLAwG88khOWsQcj4eoplHSdTKVCi2xmMw2ngYFQFFwvF6KhkFIUVBnueFO8XgwTJPg8uXQ3Q3j3rwUTcP0elF0HfcUQeP4jKNwHCq6TrFcRvN6iY4lQqJ+P+e0tqKbJi0uD30DKdriEZxICFw+NN1iaTROXy4LpmBUF3gPv8oXvnQPRtHmXB/cEFRY5VFoCEZoqI+jNDfzjvOvIrB0Bf2PfAWjsIPv7O3mgRdgrUvlt9fHSK67jPQLz/JsX56WoJ+tuTJxBXwo+ITKslCY9aUsSxHEY/FjP9uTBSh7R0fZXyzygdbWSYNF23F4KZ1mXSyGf4oTKiEE3Y5Dh6KcdrAoqG6gqc1VnO+1ijWLIbPoAM9ks+zQdS6oq5vydosqYHQch5/v3k2fy8UtXV1zcwPMJDRNozEWo2jbeOVmGGkRq22ISRkGtqpi6jqGohD2+WRDzOlQFByfj4KuExRi1jONWlMT1vAwmmm+YUONomlUvF6EYeBtbITR0SnvR1FVLJ+P0VKJTk2rDvsed38+txvcbpb6WlAqFY70DhCNhIgFQwhbZ1lDA725PNF9B3ni5dc4vyJ4W72fNX6VJXVBSo5N05JV8Nb3wZVvIaAIUDViZhHbs5a6fZ/nH997AUu9dTS+4w+459tf58meEZY5gt0FHRQXF8aCXNbaxIFCme92D/K7S9vp6TnCar9/yg9DadPkgXyem8Jh6qaoyx8tl+k2Te6aZKNLzVChwFCpxJUzsNnlQKnED4aGONfrnbdzFSdaDJnFrGHwYqnEAcPgxkQCzzSxxaIKGKH6iUpvaqLiONUambk4zHsSqqLA2GYYq1ymYNu0hMOzfVmSNCvCXi94vQyWy5TKZVaoKorbLbONp2muZBrt0VGsaY6fFU1DbNgAqVT1NXyaf3dNVWlPJnEsC6uuDpfHAyMjx91GVRSEx0M0ECXq8iBWrUXZswulWKY9HgchWF8xOTcYwBEmQb+H3myJNU0JnIqFWsojsgMc2P8Sh597kCs+/k94Rp/l+v/xm/z47i/xVPdhBj71n2x75BesBFIWNCuwIRzgus5WUsUyP+/p5/3tLSwL+OmBKT8AOULwaG8vrYEAyycZkVO7zRPpNGv8fuqmKC+bqe5ow3F4bmiIZwyDm+vr6ZqlveUzbTFkFnt0nUdHRwlrGlfX1U0bLMIiDBgBDvT18dLSpVwWi6Gn07N9OW+O283+YpFGTcOsVOS8RmlRC/n9hHw+RnWdVDotaxtP1xzKNNaOn4WuTzqn0T54kHKxiFfTUMdWq07L40EkEog9e1Amed1UFIVoIAArV8LOHeB2QTAJK5fBgd20J+tRHAdhlsip0BpQ6RvJEi0bhLY8wsiPP8uP+gvcfvUGfnbPP+Hf/hi7rTyPvViAouCGtj38zu1tPPdynu6eLCtc1QHdjhC8ks7R6XazPBhgpFRit3Bx8RSZw5FSiW7T5P0NDVPuXD6cz3O4WORDS5dO+XTMRHd0xrL4zsAAMVXlzmSSxAIJFhd6ZtGwbXp0nZ9nMlwSi9Hs8ZzUh+2F9jycFNtxODA0xI7eXpiHafOmYBC8XkYti9FCgZxhzPYlSdKsUKi+0TseD9FIhJKuczSdnrbTTzoxx+ulaFk4lcqsXkdtTqMdj6MkEsf/peNge70YloWoVE58WuQ4WPv3YypKNSicyt69YAjEkuXg8cC+A9C1DFVRUDQN1R0g1tRO3bKVRINRYv4w2BpuLcEHr7gaV1sTX//adxGVCO9664e45qIOUiXY2gefei7D5/ZmscTrGURHCLKjKc5PxtEUhaOjac71+akLvJ4ZHP9mfiSdZr3PN2Xm0HYcXkmnWRuL4Z3m/e10uqOFEJRsm+8MDNDg9XJTY+OCCRYXembREIJ7R0Z4KpXi0liM1jcx6mihPRcnRQjB0WyWZ30+gh0ds305p6Q2r7HbNDEtC2eKriZJWgy8mobP4yGlKJhjtY05XUeGjaeolmk0TexZ/kCqeDwQj2P397/x71QVx+9HN00cwzhh0KioKmLNGpwdO45rpnmD5hYYGUV4PNDaAYcPQ2Cs4dDrhYuvQhWCWDAAy9biUjQSm68msO1ltt/zOH8Y1Lh0w4X0Zl3s3FfiHc0KfQUY7SmwygVt7mpCNOb1kCqVySmuY7WIKtDlfT0DmggEWBYJoZsmABlgukV0DlAul1l+gtmau4eGiAQC1L/JRkpHCLaMjvLV7m7a/X6uiccXTCCRsyy+NThIs9fLFQugu3uinG3zvaEhgh4P1zc10fImTxAW2vPxpjiOw8+3bmW+hlrH5jW63WR1vbo7e7YvSpJmUdjrJRYKMWjb9OXzOJWKzDaehjmRabRt9AMHMBQFbfnyN/69omD7fBiqijJd5nCM2LuXiuPgrF59XAPMcUZGoLEV1q6HgwegYxkwlhZ0HHjmCYjEQdWg5wAsWweH9hK5/CZuv+O9XLLxYoKXXMuyZa382iYPWQHtHnh7DK4PQqsLltYn6IoF0GybK2KvB21LkgkiXpV0qQxAW7yOHekRMuUymXKZ18pl2qbpZD2QTpM6wU7oU+2OdoTgV+k0L+s6V9XXc9kCCqoWcmbRAQ7kctw7PEzI7ea8cPiU6lYX0nPypg1ns2xXVQpLlrxxs8A8oqgqJZeLw/k83ek0U4zWlKRFI+T301BXx2ilwt6hIRzbnu1Lmp/mSKZRURS0VaswjxxBmWSAtaKqKKtXY+3ciTjRaYuioCxfjnKi06XGRlDU6o6+Q93QtqQaIDoOhCKwcROoSrWsKZ6AC66CzhXQ0ALLzyP3i/t58v/9G1sG6nliWGFlADYngywPeVjdGGdFIorhcTGUz3I0XwBRve6Qz8srIyPHMooCgWE7+DQN3TS5UFVJTHEcXTZNHsvnuSwUqnaAT6HWHb2qvn7652Cc2nzFHaUSdzU20hYMLpjJBAs5s+gAj2UyPFosssLr5aJI5JQf36JsehlPURQe27OH961aNec3wExHUVUMt5uQolA2DPxjReAL5Rdakt6MY/MF5dzGGTEXuqfNgwdxrV6NmOJ12t6/H9uy8FYqaIHAtJkzMTKCOTSEZ+VK2LXr5C5g/wFYthL27aoGicrY267LDc3tMNJX/ZpLg/OvIhIOc+UVb+NafQvW7u1cEXSRXHceXSPDeCwHbAG6oLMhQbOvRExTGBulTp3XS2zsWFpVFM5pbSSXK5KMxDgvkeBoJkPnJB3SmXKZsK6zZJKd0cceO2++O7qWWTyg67y9oWHa2sj5ZiFnFkd1nRfzeXpsm6vjcUKn2RC46APGkq6jJZPs2LOHDrd7Xs83DI+9kKdMk1IqRdLvJz7Jp3FJWizk3MYZMhe6px2H8s6duHQdX3s7YrL5i14vRqWCT9PQfD6YaqNXqQSXXw4nOyWjVITLroDCJN/zqptgpB/6j8AlV8Oqc0ABNVaPJ9XLf/zx5zlnXTutS5cRu/xdVHY8T2HXLkKda/D7o5BKEXjhaTBsKJfRWto5XwjUQhksG9WlobjdhNwmpm3TXShQFmLSgFEHPEx/dPhmu6OPbW7Rde5qbFxQweJC7YaudUE/lMmw1O3mpkQC9wzENgvl+Tkte44e5YF8nlJHx7w+mq5R3G6Ez0cFZBe1JCFrG2eK4/VSUlXck9USniXKunXTz170+RCrVlWHdE9D7NqF1d8PDQ0n941374K+AahvgLoEhMOwch1svhyefgTal8KS1aAKcCxMs8C//uWH+N6Dz1EcyNArfDw7Osi///R+LI8J686Ha25i8NCrONfdgnX5VVhN9SiJOtT6BuhoA1EByyYW8FNUBbahoygKHaHQGxp8bMfh1XSacF3dtFn0k+2OFsBwqcR9/f0ysziPjO+CviQWY2M8PiPBIsiA8ZhgNMqB3l5My5rtSzltCtU3SNlFLUnHm1jbWDIMDFnfePIUBc/KlZR27561WmmzpwcrEkGZqvHDcTC2b8cYHUVM83ouslmUyy+fvlt6vGwGrr8FwlG48GIo5OEjvwvP/wouvgZueif4/aABpX08/flP8M0fvkDIgki2wvDTT/KVv/xbrgxbRFcnYMUKBjMHONwQo/+SK/jnl1/mhX27QBPVYDERhysuh0QMfH5aNm6k4Bi0R6M0eTxkC4XjPvQIwDuD3dEHSiW+MjSEV1F47wJZ8wfV5+losbjgahYF0FsqnVYX9Iks+iPpmtFsloeFwGlr4+JAAH2erA2czrEuaschUy5TsixaIhFZwyUtWuNrG8Oaxv5MhiWhEJ5AQNb8nqTy/v0opom/UsHj9594YPZMs23MdBpXQwNks5NnEhXl9Z3TweDkAZRtYz35JEpDA2o6ffz9qCp0dUJv9/g7fb2r2rLgsmsAB9acAxsvA8sATUXoRZ7+7mf5y688jjDB44IX+oo0Dim8NxFizbqrqay6Fj23lf/5L1+nIxxkzX/+OUde20dDUxBcCrS2wvXnQrmMtXIl4tVtaF4P0aZGoo5GNpsnXywSDgaPe/4Vpt4QAyfXHS0YW/M3PLygNrdAtQHkqUyGnek0yyMRNi6QzKIlBM/ncmzPZukMhVgXDp+RxyUDxnFcmobhcpGz7eqKnAVyZKWoKsOKggPohoGlKIS9XvnmKC1atdpGXyJBxbI4ODJCIhAg9iZn0i1WwuOhZJpQLuP2+89u7bcQKOEwWns71oEDU95M0TTMujpcK1ag7N79xkyiEBAOo2zaBAcPHv93HR3Q2gI/vueNd9zQBJsvAzMPB1/FCXkY2f48DesvAI+HJ3/4Tf6/Tz9M2bBY6YJrYj4u8TgkowGIJfB0LuX3/vaPaRrV6U1bvOP8Vm7ZkOOq6EpCdhSrIYlr+Sq47Fp6hwb57J/8Dhtfe4lbz7uQ2FtvRLzwEpFcjkgsRkXXcXy+ky6lOlwo0HOC3dEHSiW+PzTELQswWHwkleKgrnN7WxteVV0Q74FHdJ0nh4cx3G5uaGrCcwYf10IIrmeM7TjsymQozuI6rDMl7PEQDQY5all05/NYpilruKRFT1UUFLcb11imJpXPkzMM+btxMtxuyoqCWS6f9Q/X9sgIxaeeQptm9R1Ug8bKzp0Iw5g8E6qqkx9JJ5OQy6BY5htvv2otZEfh0R/juIp844FvU+l+FZEf4sUXn+X/+9Q3uMFvkVTgziA0a7Ajb7LjaIZc7zAPfO4LFHcUaC5arBUQOtDHnhd1Hurpw25MINZtwNl8BZRHEFu+xYX9O7nylpsIvPt92De9lT1rlvCr7BClio5H01B1/aSef1sIXk6nWRON4pkkwBTA/rHM4kIMFp/MZDio69yaTOJbAMGiIwQ9us5PUik6o1Gurqs740GwDBgnSOdyfH3XLg4LgWsBdhgHfT7i0Sj9hsHukRFsWb8lLXIKEPF6sT0e+k2TfLnM4XR6wZwwnEnC46GkqlTK5bNa0yhKJZS2tinnMo6/ne04WCtWwCQ1j6JYxH7wQYjFXv+i2w033QjPP1s9eq5ZsRLWrIPlqyCfh6tupz+tcdU5V6NsvoyH7/4n/vMPP4CWG2aPATgw4rgIVGw6I1FUG1KjOcpHclzp0rgxmuSP4gFuamtj47ob+NBffpkXPH5eefCvSX33r7C8Xp498F0OiAK6L8Ng9jD/9f3v8v4v3s3WionlAcOuIAClXD5hLeZoucxhy2JtKDRpRriWWbw5mVxQwWLGMHh4dJRdpRJvb2ggvABqMXO2zcODgzw0MsLmaJSuQKB6anKGySPpCYQQKKpKsaEBO5ud7cuZcbXdu6bbjU/TyBkGUZ8PVFV+epAWNVVRaIlGyVcqRIBSuUzasmiNTLeITcLtplypQLmMJxQ6+SaS02QeOoT3mmtQ9u9HHD489Q0VBWt0FGVgAFc4fPz1DQ/jrF1bHdL92GPVr51zDjgWyt7dr9+uoRHuvAt2b4OuJRAIQPsSWuNRTNvkX//q49z39B7esqydA4f2s7MC13ugS4VDFUGHo3N+cwt+4BzA73ZRLJeIJ1txLV0O7/ufFF02//SF+7kmUOG24qscPPj7/NeDeY4OWij6U1xStPnO95/jHNXhgqCPjFXBKJl0RJO4ge7R0Skz40IItqTTrJpkA8z4msWFllk8pOs8PDREwuXi1mQS7zyv33eAQ7kcW0olQm43V0YiRM7iXFQZME7CcRx+vns3fS4Xt3R1UVkADTAT1Wq4So5DNp/HcRyWxGLzeg6lJM2E2jzT7nyeRpcLyzAoUc1CSpMTHg9mXR2B+vpp6wpnmtXTg5bPV4+bp8pwCoGTzWK2tOBOJhETrk9pbKweNQMEg3D9dfDYI9XZi1ANFq+7EYZ6ob0TnvwFbLoM8dV/RncG2VHWufehbfyvK9bywK69dHrAZYHqwNM5i40qRFSLg0eru7AVFFYm64jEIgSWrIRwHFSFr/33v+E1DHK2wpK8gtfXR89gnrUq3N7cxRFG+Zsr1nJtpUJxVAdXgGgsiepoZA2LiOOgTzFX8nChwOFSiQ9MUru4UGsWD+k6Px4e5rpkkjavd94nRLKGwYulEgd0nQ0+H+1nqLFlOjJgnIKZEs86AAAo1UlEQVQQAr2picoCGLMznfEbYvLlMgXbpiUcnu3LkqRZ1xwOI4RgXzZLRNPIlUo0j22KkR+rJqGq5Ldtw+f1opyNY7+xbmnflVdiPvDA8cfHU1xfZWQEdyIBkw39BlizBjwulB3bqh3RK1dXM4t9h6v3X9Grm11icWhYAktv4Huf+QxmRXDvlh1ENHimBBt9PlZoULFVrkrGGb/IT1HAq0BuKEPY3orYdDGv/uBb/OCBrdRZ8I66IC8MFHh0aw+XKoLVLhC9I4SyChecswyXT8VVyhA5/1K47EaEy43y9BZ27N1NvxCcN+EhGbbNfZkMayfULi7UzGLFcXhxZITnDYPrEgk65vkHPUcIjhgGj46OEta0GdnYcqpkwDiNfb29fEMI7urqwpvPL9iaplpGZX+xSKOmUalU0OGsprolaS5SFIWGWIyCYWCbJt2ym3pK5ugouFyIchm/339WgkYlGEQUCid1DO6k04iurur8xckyki4XXHwx/PIXsGQZXHIZdHbCT+4BFHjne+A7X4J3fxgO7EI5bxN+vZsL1MM8q0KlAgUBGxTYZOkYSpDf7mrHr1WDtHSpjG6aCAFlR6B5/Dy2rxfP/h/yiBCcawvaVXh6tAApnfcE/QxpkFM9GAUbdwmyW3ZQ3NBJcfUSKmtX87xpUtn+Ind/67/xjeisneRxPzM0RFJVuTiROO4EaaRU4gdDQwtqdE7OsvjB8DAhReFtiQTxeR4s5mybZ4aH6bEsNtbV0ezxzOpYPBkwTsNxHJxolJLPVw0YF7imYBAhBLvyeWKqSq5clnMbpUVPpXocLbxe8mNbk1L5PC6Ph9Asv4DPOZpGxeMBXcfv853xoNEeHqY4NERgxQrsPXumv7HjYGUyuGIxlFzujfMbPR5YugSlvg6CAXjlOfjmzyGXhd/5Q7Aq8P5frw7nrtsIrR1s/9o3+MwzfTQo0KYqXBGPcnQ4Q8mGdzXFcSmwd3iEIyWdrcUyq03rDceIZWC1LaiFr5oC1zbVUdagLRIm6vEQ1BwUvxuaGjDf8R7+6ytf4sCzX2HXwSO4DIeYAkEH6sfdb63reYuu8z8aGo77OXWEYEsmw7le74IIFh3gYCbD46USiQUwjHu2axWnIgPGE0jncnw9m+WORIKlgQDWVLtJFwhFUUiEwxRNE8c05dxGSRpzrJtaCAZyOWK2TapYpKuu7uwPr57LNI0KwFnINIpSCffll6O63ScOGAGRTmOoKsHNm3Gefvr4v7zkElixHHr2ws7t0NwKjc3VMTrtnWBb0NcNXi90roRIjOhFN/DbP3+QTUWVgaES3dksdYDq8ZGzbb6ycy/C5eNCzcX7G1uI+14/nM6Uy5RNkxEhGBkYIDqW8VQE9GaKxDxucppOn2mxtqke1angv/42RLGAfWgPA30mQROu9fpoKeuEfT6Cun7s/odLJb43OMjbGhpI+F//vrXd0Icdh/c2Np7W8z8XHJuvWC6zKRCgMxKZ18HiXKhVnIoMGE9goXdNT0ZVlOoxtcfDUV1H13XWqCqayyWzKdKiN1U3dXM4LH8/as5mptG2sfbsmb7xpUYIXCtX4ky8vaLA6tXVxpbRIzjlYVBN1HUXQHMzRKOw9SnoPwrL1mP5g9z92X/nvm9/ndtEkeQtNxH8xS8JhDr55YtbGSjqFIuDXNbYzPJAAJ/bXb1Ux8EUgi0DA7xWKrHCssipKqubm0mM/ewogBfIF4s4hqA1FKVvOEtLNIB48H6Goy5WezysTqh0Dxq8O1bHz41BLqur41eDg6yhOnPxyXSaC7xelk0IFn+VTh/bDe2e57uhR3WdZ7NZjtg2t9bXz+uRORUheHl0lF2GQWiWaxWnoogphmdd6PdP9uVFS1EUNqgqV4fDuObYP+KZJKgGzY5hUCiXWRWPoy2ixy9JJ9I/roSjIxZDc7nOyky0ecG28VgWofZ2nKkaTU6Xy4V/7VrcuRz2dON1ajQNz7p1eHI5xOHDaNdcg3vJEli+FF7egli7nEe2bqGtoZO1N9wCvbtg4w0wsAMe/xn2TR/jM1/6Av/2mS+zWrH5tc3r6Envp5w2eDmr4crbvCsQ5t2trbjGAjLdNNlXKrGjv58UEA0GuaKujrjfjwKoEwYuO0LgOA796TSdPh8un4++Qp6EqqDE3KSX1vHSttfo64UhS+GcZBLNMBhwHN7a0sKDIyMcKJf5UEvLsUaXWmbxgK5zV2Mj3nkeLB7Sde4dHmatx8PGRAL3PP2wJoC+Uolf5vN4gPWhENFZLnV539Gjk35dZhhPkhCC1yyLZDC4YHZNn4yJcxuzuk7Jsmgeq22cn7+ikjRzmsNhDNsm7HazP5OhPRBgRAg5vxFA09BWrkRkMmf023g2bEBMPGKeiqLgOvdc2LLluK/R043S3M72J37FLx/4EeetWk1u3yusvfwCIi4V0bWRPfu6+dXf/yb/994XcVs2cQ/86NkdbHLBiyYYps1qReWGRAKXqmI5DvtTKX6ZzyMqFS5uaiKpqjRMMTy7RlUUVE2jPZnEsSxsXcexbLwrV6Iu7yK+tIUBy8eOvueIKtDi9fKDbJYPNjTwy1SKA+Uy7x5bEwdvzCzO52BxfBf0tfO8C/oNO6Dn+HG6DBjfhNqu6YHBQWJjRwyLRW1u43Clgg2UUimSfj/xBbgNR5LerPG7qfsLBTm/cRy9uxujWCTocqHNxeciGq0GjI4DT2/B+9rz/FVrG67zL0S566NoEReEfZhOjFd6D3Dw8E42eWz6TfAqLi7xQtGwWG+D0Fx8rKWNsNvNtmyWp/r7sbxergyHWVpXh0tVyZRKDEzRROlzuaib+JrqcpFSVdwuF/T1gdtN4cYbeeDnj3PAhBuF4J7BQa6PRnm2WOSQrvOBlhZ8CyyzKIDeYpFf5HLzvgvasG2O6DrPp1JnZQf0TJEB45tgOw5P9vRwwO3m4ytXUjh0aLYv6awLezwIj4eCYVABRgsF3G43oQUwGFWSTpeqKHJ+4wTCthFeL0XDIKQoqGeg29Pu7UUMD7/5/7C+Hu288+DRR2HlUsDFyvf9Gux8Ga69DcIB7GiQ7a8d5KcPPMTzz+9mcD9ETLjQC++IBHk6U+K6pha2jqZow0Ob18tXjh4FXWdTYyNL/H5KlsXWvj6ywFZdZ6n1xm5pVVHYmEhwoFAgV6kAEA8EaIvFCPp84PNRNgxcoSCv/df/4dALr9IiIKcoXBmJ8HQ2S7PLxbubmo4LFhdCZtESgqezWXam0yyPRNg4T7ugBWA4Dj8ZGaFYqbA6GqXZ6503/y6yhvEUrGprY302S4fbvag3ozhCMFgoEFdVhNwUI0nHceDY/EZRqRAPBNBcrkWdcVR1/YxkGpVAgPDFF2M/9dSJB3i7XATe+17UsSNpz9VXozz9NFxzJUp7G+x4GS6+HFauorT7aR58/j/40jf3ks2bNLhtIgos84a5ur6B9P4echUPAZ+P7rzOeo+XHdksKxIJzvP72VEokEqn6XG5WOPzEQFaYzHqfL5JPzwUDIOedBpBdZzTslCIfakU2bEAEmBYUeh1HEaADp8PoesU3W6ubWxkic93rPZtoWQWe3SdJwYGMD0ebqmvxzsPMnGTyRoGu0slduTzNITDnBMK4Zqj75eyhnEG7Tl6lB7gQ+edR6i3F3GWdqfONbVsSr5SOW5TTFMoJLtFpUVv4vzGgXJ50WccnTOUadTq69FaWrBPfNPjKIkESihU/UMsBitXQUMSlixDWAbPDuT5zKe3clUIfloCocLyOo27PvA+9hwd4omd3URsB30wRZfXx1bD4LaWFnRV5Zv9/bS6XGxobuZil4vYSSRhon4/54y7nW6aNCYSjB9+sxKwHIdDmQyvAC11ddwSCh23I3ohZBbH1ypeGo/T4fPNy8cxcVPLuXV1NI01O803MmA8RcFolAO9vay1rGOdcIvV+E0xdYrC7pER2U0tSWNq8xtDXu9xG2MWZcZRUXB8Pgq6TlCI2alpVBTUaBRcLrSLL4Znn61mJQMBqBQ40rsDw9RpWdrGX/7lX+DS4ZANURMSGoiUzf6vfpPvFWx68jbnOTY+zUWnx8OlTU106zrfGRjgjqYmlvj9aKfx4dnndtM0rl5eN00OlMs8MzSE5fFwZSjEsrEPHzXzPbO4UGoVa93P23O5ObOp5XTJgPEUjWaz/EII0m43V0Sj87KeYqY1BYMYto3P7Zbd1JI0gcw4vu5MZRpPhmvdOrRgECuTQYnFYGQEVBUMgyPf+Ap3P/EA17Y2s3d5J8lSHo8KjgmdisLb6qK4sxlSWQOvCT4BtoBrAgGubmlhb6nE3QMD3NHYyPIZagi0hcARgoOZDE8UCjiGwYUNDXT5/fhcx7+Fz/fM4kKpVRzf/dwRDM6ZTS2nSwaMp0FVFHZqGhuXLCHa07Noj6bHm6ybOuH343a5FsQvjCSdLplx5IxkGp3BwZPaKa11deG8/DLq+vWofX1w+DB0dUGhQOvWrXxy+XrclQzdvT1cEXJIWeBVQLEE3ZkiG7xherJ5Ei4f5zs2+UCAy5qajgsWl81AsJgpl8lYFlvTaUq6Tsbr5eJQiCUtLZOeas3nzKJhWfToOs+NjGB6PNze1jYvaxXna/fzyZIB42lSFIVHduzgZrebwCIbtTOd8d3UR3WdOk0j5HKBqs7LT4ySNNNkxnHmMo0nu1NabWjAs3Ej4stfRl25shosClEdq6PrqCvW4u3eBf/jY3QsX0rjb9zO/pRByoB3JhJcFw3zXKFEQHf4aEMjd3d38/Z4nJJh8LP+fu5oajqlzKIQAltU90l3ZzL0lstsNwyaLItILMa6WIyYz/eGjGLNfM0s1nZAP1sqUalU2BCP0z4PaxUtIejJ53mxVKJomvOu+/lkyYDxNJV0nUPAD8pl7lqyBG8+f+L1VIuEAoS9XoIeDwjB4Xwex3FIBgJyN7UkjVnUGccZyjSe1E5pRcG9aRPK4cM4AwOwcmX166oK69YCDuzdC7ffCZdezaFDu3hsRLDPgMtcLq6Lhnk8W6A7X+a32trZ0t9PUzBIZyhEdz5Pi2XRNa7x5GTopkla19lbLpPJZNCBtNfLepeLtzU2Uuf1Vj84TFP3Nl8zixnD4LlCgUO6zqZAgPaGhjnbNTydI7rOk8PDGC4Xq30+WhOJefk4ToYMGGeAAjjRKCWfrxowSsdRFQUUBcPtBtuuvriaJh45v1GSjlnMGcfxmUbc7lNrDNA0sKfpk9Y0XK2tOE8+efztvF7YvBnle9+C5avgquvA7+fH997Hy1mTc1W4LhQkVSrRkyvxkZZWyobBq6US72puxhGCl9NponV10za4CMAZ2yW9e2gIx3F42bYJ6Doen4+1TU34gOg0mcSJ5ltmUVDtfn5pZIQduk7c5ZqXO6Brj+OVVIqXKhXOi0Zp8XoX/EpQGTDOkHQux9ezWe5IJFgaCGCVSrN9SXNO2ONBAAGfj55CgbhtM1IqyYyjJI0zXcbRAiI+38J7YxrLNI6WSoyMjrKyvh7tJIOmGntgAOPIEbRAADHJ669r1SpcLS1YtZ3W0SgUCnDzTXD4EPR0wx/9GaiC/BM/ZeSVZ1iHYKNb4aJokKeG0lyhuYm5XPwqlaLV7Sbh9zNaLtNtmtyVTE6aCRRCMFQoMCIEOwcHyQCRQIAOYFMwSGdLS3UV4JsMkudbZtEBnk+l2JbNEvP7uTaZJDYPEwbj5ykGvV6ujkaJLZL3LxkwzhAhBIqqUmxowM5mZ/ty5iwF0MbNb6xlHNeoKprLNa9HDkjSTJqYcewvl0mVy6yLRhkRguZweMH9vjheL3GXC8Mw8CsKypsIjCv79xO65hqU/fsRhw8f93dqQwP+u+7C/t73YHj49Q0v998PV10Jj/wM3vcBWLoMFAeijby7KYKRqaOuZPNiaoRniya/29aA4Ti8kk7zlpYWNFWlJ51mjc9H3YQ5i7YQjJbLbEmnGS6VCAMrGxqIKwr1weApLzkQwEipxJZMhsOOM+czi44QpAyD57JZjtg2lyeTtAQC8yrAGp9R3GUYhOb5PMVTJQPGGeQ4Dj/fvZteVeXqcBjXQssCzLBaxjHo89FvGBQyGbqiUTRNW3gZFEk6ReMzjo3RKP2FAjFVZe/QEB2xGJrLtWB+X2pTFiq2DeUyituNomknfURr9fSg5fPVJpZaLXmtdrGnB2esvlFJJFCGhqCjAyIhlI9+AlLD0NiEpQie+tWD7Dp4gOt8JTqWriT79A6uVSDu8/Nafz/NY7WLZdPkJcviktrwb6rJg8OFAi+n0xy2LFb5fNzZ1YVLUdBmILA7UCrxg6EhzvV6eW9jI+45GizW5iluzWY5Yll0eb3c0dCAe559yJmYUdwUixGd5/MUT5UMGGeYEILXLItkMMjFgQB6KjXblzSnKVQ7zU23G4+qsiubpdPvZ1QIuTFGksZRAcay84ZtE3a72Z/J0B4IsL9cpj0aJbRQ3sg0Dd3j4ejoKEvCYXzBYDUInI5tY6bTeC+9FOuhh15fEejz4d20Cefuu6u1i5qGdtllKOk03HYbSmszlAqIaJiBbc/zhS98nod+9jAXuGw2NLhZ2jyKyydYgRfNrKA4DkvHvuWBUgnbMOhqa0MAw6USW9JpDpdKrIlGeeeEDSynwxaCQ+UyPxge5ub6+jfdYHO21DKKz2ezHC6VWBYOc1MsRnSeNW9VhODl0dFFnVGcSAaMZ4BL0zBcLgYGB4nJUTsnpZZZ8LndHC6Vjm2MkRlHSXqjY78viQS9hQK2ppEvl0kVi9WjUVWdd2/QE6maRnt9PbZlUSmXKSsKEZ9v+qNcRUHfsgV3NIoYq1X0bN4M3d2IffsgEEBdvx7tLW+BUAiiEewjL/DML55kQDf5j3//NOlMibdokBTgLrgY2J8hXidQ0gbCKVEENKqjVJ4aGGBTYyOWbbOrUOC+0VEu8Hr5QFdXdf7eDAXvGcviiYEBuk2Tmxsa5mSwOFlG8R1tbbjn2RzC2oaWX+bzeGBRZxQnkgHjGWA7Dk/29LAHuDMSkfMZ3wRVUY5tjPG4XDLjKEnTUBWF1nAYgHylgmJZvDw8zMpwmPxYdzVwWuvpZpMy1jU9WCpRLJUIaRrKNF3U9vAw6rJliHS6dgdoS5YgurvB78f9e7+HtnEjNNSD38fTX/8M9/30ezzx9KsUCw7twJXAagG7DShnbBqEDyXkYiBg8Xg5x/OOxcWql0dSKUyvlyavl28ODODoOre3tLDkJHZGnyxHCPZnMjxaKFDv83F7PD6nPgjUavv2joxwVAh65nFG0RGCtGHwUj5Pd7lMZyjEukhk3jXlnEmKEJMPDbxwBn/oFysB1IOcz3gaHCEYGss4lg1DZhwlaRqC6u9MsVLBNk10XadoWaxLJud1raMAEAKrXGakUJi+i1pVUYpFAi0taHV1BK67DvHQQ7g+/nG0DevB7YZolOeeeISP/9qvYxdLNNjQCawHah/v64GL4nHqvR78XsF2JcevHykTs+AqQPj9rPR62ZPLsaSujitisUk3sJyqtK6zpVDgYLnM5YEAXXNsTV7GMNhVKLA9l6PO76dZUVgej8/bjOL2XI7DlkWLx8O6WGzePY6Z9L6jRyf9uswwnkFyPuPpkxlHSTp5tSkEte7qUVWlxeWa97WOClRH74x1Ueu6zmilQmttPuX4x+I4KK2tEI3iDA9T+u53Cf/5n6NtWI/tcmF53GRffJQf/v2fECiVSDrQBFyXSLDZ5zsWJCiAFxguFmmPNtJjOFTsMhowBHQKwU7D4KbGRtpPo+t5olqt4kNDQ9RrGm9vbCQyRwJ9WwgsIY6bo3jJPO16doQgMy6j2BEMcsUC2fl8psgM4xmmKAqKELzN52NpIDBjLyqLlcw4StKb5whBf6GAbdskNQ3TceZ1rWPeMLArFSzDIBEMUjeuSxmoNsiYJl7LIviOd+CN1zFQH+OHX/4vXjk0yDnRELe+8zL+8XM/ZVnZIujzcUc8/oYgWgjBsBD8YnCQinA4KqAZyLpcXOr3s7mhAc8MZRWFEBwpFnk1k6HbNLmyoYGOsU0vs62263nnyAhZIQj4/ZwficzrOYrD+TwDqkqzzCi+wVQZRhkwniU+4EPnnUeotxfhOLN9OfOeYdvYtk22WKTT76cylnEE5sQLrCTNVflKBWFZHEynWRkOYwqBS9NoiUSA+fP7I4C8rhMDTMvC5fUSnhhgGQaeWIzh3bt5zK7Q7cCVgSAXLVvGUwmDf3tiD202rFUUNk7xfbKKwoqGBgJC8MOhIcKKwmUzWKtoC0GqXObpTIbD5TIrIxFWB4OzHsjXup2PZrNst+1ju55jikJiHmYUJ85RXBoMEtQ0mVGchAwYZ1kiGmWjx8Nay5rROpfFbnzGcbBQoN7vp15ujpGkaY2vdezL5+n0+zmSy9Hg9xMPBOZV5tERgv5slpiiVDOnweBx3dS9mQxRIXALwVFdJ1Us0quqHELQFghyjqqydpr5gOlymR1j3b8zWas4PqPYY5os9/m4IJmc9UxX1jDIWRbbat3OHg8NXi9t4fC83JE8cY7i+lBIdj2fgAwY5wBHCC5yu7ksFJJDvWeYYdvkKxW8gGEYtPh8cle1JJ0kw7bJGwYemLeZx7xhICyLUrlMwudjSNdZXV9/7Lr7UiksxyFbLKK5XLTHYiSnqD0UQHcmQ2+5zDbDoNHlYkMsdtq1ikIIbCFI6/qcySg6QuAAh7NZ+g2DXYZB3LYJh8OsCATmzQeH8abKKC72OYonSwaMc4QjBBs9Hm7p6qIih3rPuFrmZKhQIK6qCMeRu6ol6SSdKPNoOA7RObzLunb9+0ZGaPR6sSuV6g5uRSHk9WLaNkczGZYEAuzPZMhVKpPeTwZIe72sHwss4z7faQfMaV1ne6HAaCbDUU1j2SxnFGuZxB3ZLEXDIOvxsFzTaI1EjmXg5ttrpmHbZAyDbsOQGcXTIAPGOWRVeztvcbkgl5vtS1nQaruqDcOg2etlxDBYmUjMyxdCSZoN4zOP+1MpPKrK+liMvrFu6+BY/ddcnPNYq3EslEqMlkqsr6vjaKmEqih01dWR1XUUQJ3ieDnq8530SsKJbMdBUA0Se7NZcsA2w6Be01gTjRJ1uYic5czddJnEDq+XqNc7Zz8InIglBD35PC+WSlCpoHk8LJEZxVMmA8Y5RFVV4kLwtmCQunmY7p9PahmHQ5kMCY8Ht2WR8Ptxu1yy2FmSTlLt90iBY93WCVVlTzZLxO1mXTJJcWwl6lx7g3bG3uL6cznSuk6r309J1+ktFlkejRIfG9ulKAr1p3jkrFsW2XIZgJQQ7BweJiAEBzSNZV4vIaAlGqVuFrqeF2ImscawbY7oOs+nUhguF6t9PlpDIVTmfgnFXCYDxjnGARqAd0Wj+N1uOdT7LBBAwTDQDYOYqqIpCs1j9VlzMUMiSXNZzjAwbJugy0VPKkVQ02gNBOgvl6nz+WiJRBAw5wISAYwUi0B1zuLe0VEM26YtEKAlGKSvVHpT9+cAr1oWfsMgDOQUhWXJJDFFwXeWM4n22PtI2jDoy2bJw4LKJMLrG1mO5vPstm2KpsnqaJTmef645hIZMM5BiqJw5fLlXCYEem2VlXTGOUJQNE1s08SpVChaFhsSCTnPUZJOUS1Q2Z9K4QhBl9/P4VyOim2zIZnEohqoeV2uOdVEUcuc1uwbGcE6hbFnQa+XjmgUqA78PpvZraxhYFgWGSHYOTKCXwh6NI1Oj4cg0LwAMonwxo0szR4PSbebllBoXnZvz2UyYJyDFEWhNRLhAsOQQ71ngQBS5TJ+TTs2z3FI12kLh2V3tSSdBsO2yRkGHlWlpOv05HIoisKGaBRTCAqmSUcsdqwGEuZeJnKucYSg9mY9MYPYbNsUFYUl8ThRRcGraWe9RvJMqHU770ul6BPi2EaWLr9flhSdQTJgnMuE4Ha/XwaNs8gRgsFiEdu2qXe5ZHe1JM0Qh+o4GYBipUJvPk/Q5TpWA+kIgVdV53QmcjbUjs4FYAA7sllUw0DApBlEWDilNRO7nUNeL0lFYancyHJWyIBxjvMIwS2BAMtCIZCbYGaV7K6WpDOvVgMJTJmJzJsmwHE1kePN1wBp4lE4VDt9946MHKtnPwoMlEo0Ug0YQ2M1iAosmAzieLUubtntPPtkwDgPNNbV8fGlSyl2d8/2pSx6U3VXO0BEFldL0oybLBMphEBRlGM1kbUAEiDm8bAumaRgWcfdjwJzplvbsCyyhnHc1wRwUNdJ5XKMP1TtA4J+P821LygKy+PxY0HxQv3AWqtN3JHLUahUyLrdstt5lsmAcR6oj0bpKpW4JBiUm2DmkFp3ddkwSOs6GyIRWesoSWdRrSZyvFp3dmbC8O1Wv/9Yt/aJTJW5PJFaDeF0oYwDbLNtPIZBaNzXBVDxelkeDjM+R6jAvNvRfKpsIbCEeENtYrPHQ8TjkR/IZ5kMGOcJRwjOc7m4OhyWQeMcUztCmljrmPD7URRlwR0RSdJcZ0/+9sX+VAr7BKU9U2UuT8Th9RrC0DS3E1S7p9vC4TcEgYqiLMoPmrW5iTtTKbJC4JW1iXOSDBjnkdrO6Sui0UX5ojJf1GodD+dyrAgGQQhcLhctoZA8RpGkeWCyzOXJWIg1hGdCrbQnM8ncxLCiEJe1iXPSVAHjqe09ks4oVVHYqWlsXLKEaE8PQjbBzElhjwcBrPb5KJkmA8UiHZrG7pERuqLR6uaLRXLEJEnzkVfTqA8EZvsyFqSsYbC7VGI4n2dAVWnyeOj0emlJJOTcxHlKBoxzlKIoPLJjBze73QTc7tm+HGkKCtVOzbDHQ9jjwbBtPC4XL46OEtA0sO1jXdYwf7s6JUmSplPrcj6SzzNoGOyqVAhpGkvr6liuaXJu4gIgj6TnMAHUA28LheTO6XmmVltV67IW4zbKyMyjJEkLRdYwyNs2O8d1ObdrGs3B4LENM9L8Io+k5yEFGAJ+Wihwp6rKTOM8UsskLq+rq26UUVUSmiYzj5IkzWsTM4m7KxVCtk0oGGRZMCi7nBcwmWGcB2SmceGQmUdJkuaj6TKJkQWwq1p6ncwwzmO1TOODhsHHV6+mcOjQbF+SdIpk5lGSpPlAZhKliWSGcR5Z1dbG+myWDrdb7pxeYGTmUZKkuUBmEiWZYVwA9hw9yiEhuMXvZ2kgIIPGBeTNZh4V5MosSZJOz/g5iX35PEWQmURpSjLDOB8Jwe0yaFwUJss8DpTLLAmHScoNM5IknQLDtskYBt2GcWxOYrPHgw9okpnERU9uellgPDLTuOgIIFUuIwAvHLdhpmhZtEciBOULvSRJE4zf3ewIwS7bhkoFzeNhSTBIUM5JlMaRAeNCJDONi1btKKlkmvQVCgQ1jXpN40ihwLp4nNpuIFn7KEmLU9YwqqsPhXjD7uaA211dYYosbZHeSAaMC5TMNEo1uUoFhEA3DI4WCjT5fHT6/bL2UZIWuFrpysRaxLhtU1IUOuTuZulNkAHjQiYEdyaTLA0EsEql2b4aaZY5QlD7pZ6q9tFwHKJeryxml6R5SgAjpRLZsQyiVwiOTFKLCHI8l/TmyC7pBUzVNIoNDdjZ7GxfijQHjM8gju+6bvD5sIDnRkZwKwobolH26zoqyAykJM1htRIUQXUuYtEw6AP6y2W8YxnEkKLQKWsRpTNIZhgXCEVR2KCqXB0O45JZI2kKtTceBRgsFslWKjR6vTIDKUlzTK0GUQDdhsFoPo8BZNxuOjQNBVgai6EpiswgSjNKZhgXOCEEr1oW7kKBy0IhGTRKk1J4/XiqORSiGU6YgRRCEPP5aAmFECC7sCVphk1XgwhQ8XhYUleHGwjLuYjSLJEB4wKiKgovmCZmPi8zjdJJU4DE2ImCANb4fChAT7FI1nFQgaAQPNffT8VxWD/WhS0An6bJOZCS9CYZtk3WMACOdTFPrEG8KBY7drwsS0WkuUAGjAuMqigy0yidsskykFB9gwsqCnWqyhFd52ihgAKsi0RIlUrHzYGs3Y98g5MWu8lqDx04NgcxABRlDaI0T8gaxgXKEYLzXC6ZaZRm3Pgu7IlzIA/kcgDH1UIqyHmQ0uJxMrWHtTmItd8JWYMozSWyhnGRkZlG6UwZnzkMezysiseB6hzIhro6gGO1kIbj0OzzgW0zqOsocFwmsnZ/8u1Smk9qNYfjM4cABWTtobRwyYBxAZM1jdLZNP4orVYLWbMrk8ESgpCmMVou8/zgIALwqOpxNZGA7M6W5pTavMPaz2et5tAvBAWOzxwKZO2htHDJgHGBk5lGaTaMr4WE6jzImvGZSM+4mkgBhDTtuO5smZGUzgZ7XGVWbecyY1+rzTuMj/39+JrDRmTmUFo8ZA3jIiFrGqW5anxN5Pj5kFANIGu1kVNlJGWNpPRm1GoMa8ZnDAEG4NjO5ZravMMaWXMoLWSyhnGRk5lGaa6aeGQ3vjsbps9IAsfVSNaMnxs5nnyjX5hq3cjjTcwUwvE1hrWfhPEZQ4AmkDuXJWkSMmBcRGRNozQfTRwzEvJ4SITDx32tViMJHDc3smBZr9+P282GROK4r4HMUM43EzOE47uR3eNuN1mmcGKNYY38ICFJJyYDxkVGZhql+W6yJoLxNZLw+tzI4LivBTSNF0dHyZrmcbedLENZM1WmskYGGqdmsozgeLWNJxOf3ckyhILju5FrZKZQkmaWDBgXIZlplBY6r6bhnaQOe0kyOentx2coa6bKVNZMlbGcaLFlMCdmACeaKiNY48CxjScT/wWnyhDKbmRJOvNkwLhIyUyjtBhNlRGcmKGsmSxTWTNVxnKi6TKYJ3KiDOeZMFnt38maLAM40VQZwfHkxhNJmntkwLiIyUyjJE1vqkxlzVQZy4kmy2CeyIkynGfKZLV/J2uqDOBEMiMoSfOPDBgXOZlplKRTd7I1jFNlME9kugznmSJr/yRJmowMGCWZaZSkOepEGU5JkqSzRZ3tC5DmhlqmcUuhgDVNwbokSZIkSYuPDBilY2qZxsfyeRk0SpIkSZJ0jAwYpePITKMkSZIkSRPJgFF6A5lplCRJkiRpPBkwSpOSmUZJkiRJkmpkwChNSWYaJUmSJEkCOVZHOoHxcxq7FIX2cFgO3JUkSZKkRUZmGKUTUhWFLZUK39Z1DpVKiFNYGSZJkiRJ0vwlA0bppPhUFZ+m8WC5zEEZNEqSJEnSoiIDRulNqSgKP5FBoyRJkiQtKjJglN48RZGZRkmSJElaRGTAKJ0SmWmUJEmSpMVDBozSqRuXaXSEQIaNkiRJkrQwyYBROi21TOOvUil6S6XZvhxJkiRJks4AGTBKp00oCk87Dr8slymZ5mxfjiRJkiRJM0wGjNJpU4CgqjIM/CCXI20Ys31JkiRJkiTNIBkwSjNGAYaAnxYKMtMoSZIkSQuIDBilGaXCsUxjyjDIVyqzfUmSJEmSJJ0mGTBKM66WafxWLsdT+TyWbc/2JUmSJEmSdBpkwCidESpgqio7hOAxGTRKkiRJ0rwmA0bpjFEAVVF41bLYUihg2TaOHPItSZIkSfOOa7YvQFr4VEXhBdMkm83SrChcGIuhKMpsX5YkSZIkSSdJZhils0JVFLbZNs/ZtlwnKEmSJEnzjAwYpbPGr6pyB7UkSZIkzUMyYJTOvrEd1PtKJY4WCrN9NZIkSZIknYAMGKVZUVEUvl0s8ohhyM0wkiRJkjTHyYBRmjVRTWMEuRlGkiRJkuY6GTBKs0puhpEkSZKkuU8GjNKsk5thJEmSJGlukwGjNCdM3AxTqlQwZOAoSZIkSXOCDBilOWP8ZpivZTJy9I4kSZIkzREyYJTmHFVRKGsaDxqGDBolSZIkaQ6QAaM0Jykg5zVKkiRJ0hwhA0ZpTpPzGiVJkiRp9smAUZrzavMa7ykWCS1bNtuXI0mSJEmLjiJkgZgkSZIkSZI0DZlhlCRJkiRJkqYlA0ZJkiRJkiRpWjJglCRJkiRJkqYlA0ZJkiRJkiRpWjJglCRJkiRJkqb1/wMqoNvYAPg1lwAAAABJRU5ErkJggg==" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Vectorized: 13.224016129032258 ms\n" - ] - } - ], + "outputs": [], "source": [ "fn run_mandelbrot(parallel: Bool) raises -> Float64:\n", " var matrix = Matrix[int_type, height, width]()\n", @@ -363,25 +332,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Parallelized: 2.1289554621848739 ms\n" - ] - } - ], + "outputs": [], "source": [ "parallelized = run_mandelbrot(parallel=True)\n", "print(\"Parallelized:\", parallelized, unit)" @@ -398,20 +351,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of physical cores: 12\n", - "Vectorized: 13.224016129032258 ms\n", - "Parallelized: 2.1289554621848739 ms\n", - "Parallel speedup: 6.2115043569116777\n" - ] - } - ], + "outputs": [], "source": [ "print(\"Number of physical cores:\", num_physical_cores())\n", "print(\"Vectorized:\", vectorized, \"ms\")\n", diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ad358be38b..732398a548 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -712,7 +712,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ constrained[type.is_numeric(), "the type must be numeric"]() - if (rhs == 0).reduce_and(): + if not any(rhs): # this should raise an exception. return 0 @@ -724,7 +724,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( elif type.is_unsigned(): return div else: - if ((self > 0) & (rhs > 0)).reduce_and(): + if all((self > 0) & (rhs > 0)): return div var mod = self - div * rhs @@ -760,7 +760,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ constrained[type.is_numeric(), "the type must be numeric"]() - if (rhs == 0).reduce_and(): + if not any(rhs): # this should raise an exception. return 0 @@ -1573,7 +1573,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self << rhs`. """ constrained[type.is_integral(), "must be an integral type"]() - debug_assert((rhs >= 0).reduce_and(), "unhandled negative value") + debug_assert(all(rhs >= 0), "unhandled negative value") return __mlir_op.`pop.shl`(self.value, rhs.value) @always_inline("nodebug") @@ -1590,7 +1590,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self >> rhs`. """ constrained[type.is_integral(), "must be an integral type"]() - debug_assert((rhs >= 0).reduce_and(), "unhandled negative value") + debug_assert(all(rhs >= 0), "unhandled negative value") return __mlir_op.`pop.shr`(self.value, rhs.value) @always_inline("nodebug") @@ -2586,7 +2586,7 @@ fn _pow[ @parameter if rhs_type.is_floating_point() and lhs_type == rhs_type: var rhs_quotient = rhs.__floor__() - if ((rhs >= 0) & (rhs_quotient == rhs)).reduce_and(): + if all((rhs >= 0) & (rhs_quotient == rhs)): return _pow(lhs, rhs_quotient.cast[_integral_type_of[rhs_type]()]()) var result = SIMD[lhs_type, simd_width]() @@ -2609,9 +2609,9 @@ fn _pow[ return result elif rhs_type.is_integral(): # Common cases - if (rhs == 2).reduce_and(): + if all(rhs == 2): return lhs * lhs - if (rhs == 3).reduce_and(): + if all(rhs == 3): return lhs * lhs * lhs var result = SIMD[lhs_type, simd_width]() diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index dbb3cd7db3..b83d8271a6 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -64,7 +64,7 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: var s1i = s1.load[width=simd_width](i) var s2i = s2.load[width=simd_width](i) var diff = s1i != s2i - if diff.reduce_or(): + if any(diff): var index = int( diff.select( iota, SIMD[DType.uint8, simd_width](255) @@ -79,7 +79,7 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: var s1i = s1.load[width=simd_width](last) var s2i = s2.load[width=simd_width](last) var diff = s1i != s2i - if diff.reduce_or(): + if any(diff): var index = int( diff.select(iota, SIMD[DType.uint8, simd_width](255)).reduce_min() ) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 25c5c6f97b..8d071ab383 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -46,7 +46,7 @@ fn _isclose( return a == b else: var both_nan = isnan(a) & isnan(b) - if equal_nan and both_nan.reduce_and(): + if equal_nan and all(both_nan): return True var res = (a == b) @@ -171,9 +171,7 @@ fn assert_equal[ Raises: An Error with the provided message if assert fails and `None` otherwise. """ - # `if lhs != rhs:` is not enough. `reduce_or()` must be used here, - # otherwise, if any of the elements are equal, the error is not triggered. - if (lhs != rhs).reduce_or(): + if any(lhs != rhs): raise _assert_equal_error(str(lhs), str(rhs), msg, __call_location()) @@ -235,7 +233,7 @@ fn assert_not_equal[ Raises: An Error with the provided message if assert fails and `None` otherwise. """ - if (lhs == rhs).reduce_and(): + if all(lhs == rhs): raise _assert_not_equal_error( str(lhs), str(rhs), msg, __call_location() ) @@ -289,7 +287,7 @@ fn assert_almost_equal[ lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan ) - if not almost_equal.reduce_and(): + if not all(almost_equal): var err = str(lhs) + " is not close to " + str(rhs) @parameter From 668ba952de17c132dc105748b3bfd7a2399a5c72 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 21 May 2024 15:52:30 -0700 Subject: [PATCH 0657/2019] [stdlib] Adding constraints on UnsafePointer to avoid pointer addition errors on zero sized structures. MODULAR_ORIG_COMMIT_REV_ID: 8af0851e36166adcb7fc439d2c4b47987779e618 --- stdlib/src/memory/unsafe_pointer.mojo | 11 ++++++++++- stdlib/test/memory/test_unsafepointer.mojo | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index d25b3ef08b..b5af5681f1 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -143,10 +143,19 @@ struct UnsafePointer[ Returns: The pointer to the newly allocated array. """ + alias sizeof_t = sizeof[T]() + alias alignof_t = alignof[T]() + + constrained[sizeof_t > 0, "size must be greater than zero"]() + constrained[alignof_t > 0, "alignment must be greater than zero"]() + constrained[ + sizeof_t % alignof_t == 0, "size must be a multiple of alignment" + ]() + return Self( address=int( _malloc[Int8, address_space=address_space]( - sizeof[T]() * count, alignment=alignof[T]() + sizeof_t * count, alignment=alignof_t ) ) ) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 1d3813cdbd..c22ed76b34 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -158,6 +158,19 @@ def test_unsafepointer_address_space(): p2.free() +# NOTE: Tests fails due to a `UnsafePointer` size +# and alignment constraint failing to be satisfied. +# +# def test_unsafepointer_zero_size(): +# alias T = SIMD[DType.int32, 0] +# +# var start_ptr = UnsafePointer[T].alloc(10) +# var dest_ptr = start_ptr + 5 +# +# assert_true(start_ptr < dest_ptr) +# assert_true(start_ptr != dest_ptr) + + def test_indexing(): var ptr = UnsafePointer[Int].alloc(4) for i in range(4): From 540f169b21b6133457120601ebad059ed4bf39f6 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 21 May 2024 18:07:09 -0500 Subject: [PATCH 0658/2019] [External] [stdlib] Fix incorrect number of variadic arguments in SIMD (#40375) [External] [stdlib] Fix incorrect number of variadic arguments in SIMD Fix part of https://github.com/modularml/mojo/issues/2687 by changing various SIMD tests that were neither passing `1` or `N` values in the construction of a `SIMD` type (i.e. ones that were passing `k` values where `1 < k < N`). Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2720 MODULAR_ORIG_COMMIT_REV_ID: 20baf2934b7d768523a412755e32ffbfb109c0fd --- stdlib/src/builtin/simd.mojo | 10 ++++++++-- stdlib/test/builtin/test_simd.mojo | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 732398a548..d4d86b01a7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -326,8 +326,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ] ](elems[0].value) return - - debug_assert(size == num_elements, "mismatch in the number of elements") + # TODO: Make this a compile-time check when possible. + debug_assert( + size == num_elements, + ( + "mismatch in the number of elements in the SIMD variadic" + " constructor" + ), + ) self = Self() @parameter diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index aff83b003a..41ede7a448 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -144,7 +144,7 @@ def test_len(): assert_equal(i1.__len__(), 1) alias I32 = SIMD[DType.int32, 4] - var i2 = I32(-1, 0, 1) + var i2 = I32(-1) assert_equal(4, i2.__len__()) var i3 = I32(-1, 0, 1, 3) assert_equal(4, i3.__len__()) @@ -154,7 +154,7 @@ def test_len(): assert_equal(1, i4.__len__()) alias UI64 = SIMD[DType.uint64, 16] - var i5 = UI64(10, 20, 30, 40) + var i5 = UI64(10) assert_equal(16, i5.__len__()) var i6 = UI64(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) assert_equal(16, i6.__len__()) From 83d24c6be39b831e64987262d34f7849f88d4f0d Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 21 May 2024 18:17:28 -0500 Subject: [PATCH 0659/2019] [External] [stdlib] Change `SIMD.reduce_and()`/`SIMD.reduce_or()` to bitwise operations (#40348) [External] [stdlib] Change `SIMD.reduce_and()`/`SIMD.reduce_or()` to bitwise operations changes the behavior of simd `reduce_and()`/`reduce_or()` to be bitwise operations. Theres a few things that are related to this: [builtin `any()`/`all()` functions](https://github.com/modularml/mojo/pull/2600) [constrain simd bool to size=1](https://github.com/modularml/mojo/pull/2502) Co-authored-by: Helehex Closes modularml/mojo#2671 MODULAR_ORIG_COMMIT_REV_ID: 1ab75b0817529420ea8dd4baf3014ea97dc162ad --- docs/changelog.md | 18 ++- stdlib/src/builtin/simd.mojo | 89 ++++++++--- stdlib/test/builtin/test_simd.mojo | 244 +++++++++++++++++++++++++++++ 3 files changed, 325 insertions(+), 26 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f665c2a1bb..f517c477f5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -166,9 +166,13 @@ what we publish. return Self(trunc(re), trunc(im)) ``` -- Add builtin `any()` and `all()` functions to check for truthy elements in a collection. - This also works to get the truthy value of a SIMD vector. - ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) +- You can now use the builtin `any()` and `all()` functions to check for truthy + elements in a collection. Because `SIMD.__bool__()` is now constrained to + `size=1`, You must explicity use these to get the truthy value of a SIMD + vector. This avoids common bugs around implicit conversion of `SIMD` to + `Bool`. + ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) + For example: ```mojo @@ -386,10 +390,14 @@ what we publish. `swap` and `partition` will likely shuffle around as we're reworking our builtin `sort` function and optimizing it. -- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, - explicitly use `SIMD.reduce_and()` or `SIMD.reduce_or()`. +- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, + explicitly use `any()` or `all()`. ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) +- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise + operations, and support integer types. + ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) + - `ListLiteral` and `Tuple` now only require that element types be `Copyable`. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d4d86b01a7..ff3373811a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -460,7 +460,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size == 1, ( "The truth value of a SIMD vector with more than one element is" - " ambiguous. Use `reduce_and()` or `reduce_or()`" + " ambiguous. Use the builtin `any()` or `all()` functions" + " instead." ), ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value @@ -2298,39 +2299,93 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return self.reduce[mul_reduce_body, size_out]() @always_inline - fn reduce_and(self) -> Bool: - """Reduces the boolean vector using the `and` operator. + fn reduce_and[size_out: Int = 1](self) -> SIMD[type, size_out]: + """Reduces the vector using the bitwise `&` operator. + + Parameters: + size_out: The width of the reduction. Constraints: - The element type of the vector must be boolean. + `size_out` must not exceed width of the vector. + The element type of the vector must be integer or boolean. Returns: - True if all element in the vector is True and False otherwise. + The reduced vector. """ + constrained[ + size_out <= size, "`size_out` must not exceed width of the vector." + ]() + constrained[ + type.is_integral() or type.is_bool(), + "The element type of the vector must be integer or boolean.", + ]() + + @parameter + if size_out > 1: + + @always_inline + @parameter + fn and_reduce_body[ + type: DType, width: Int + ](v1: SIMD[type, width], v2: SIMD[type, width]) -> SIMD[ + type, width + ]: + return v1 & v2 + + return self.reduce[and_reduce_body, size_out]() @parameter if size == 1: - return self.cast[DType.bool]()[0].value + return rebind[SIMD[type, size_out]](self) + return llvm_intrinsic[ - "llvm.vector.reduce.and", Scalar[DType.bool], has_side_effect=False + "llvm.vector.reduce.and", + SIMD[type, size_out], + has_side_effect=False, ](self) @always_inline - fn reduce_or(self) -> Bool: - """Reduces the boolean vector using the `or` operator. + fn reduce_or[size_out: Int = 1](self) -> SIMD[type, size_out]: + """Reduces the vector using the bitwise `|` operator. + + Parameters: + size_out: The width of the reduction. Constraints: - The element type of the vector must be boolean. + `size_out` must not exceed width of the vector. + The element type of the vector must be integer or boolean. Returns: - True if any element in the vector is True and False otherwise. + The reduced vector. """ + constrained[ + size_out <= size, "`size_out` must not exceed width of the vector." + ]() + constrained[ + type.is_integral() or type.is_bool(), + "The element type of the vector must be integer or boolean.", + ]() + + @parameter + if size_out > 1: + + @always_inline + @parameter + fn or_reduce_body[ + type: DType, width: Int + ](v1: SIMD[type, width], v2: SIMD[type, width]) -> SIMD[ + type, width + ]: + return v1 | v2 + + return self.reduce[or_reduce_body, size_out]() @parameter if size == 1: - return self.cast[DType.bool]()[0].value + return rebind[SIMD[type, size_out]](self) + return llvm_intrinsic[ - "llvm.vector.reduce.or", Scalar[DType.bool], has_side_effect=False + "llvm.vector.reduce.or", SIMD[type, size_out], has_side_effect=False ](self) @always_inline @@ -2340,10 +2395,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Returns: `True` if and only if **all** elements in this vector are non-zero. """ - - @parameter - if type == DType.bool: - return self.reduce_and() return self.cast[DType.bool]().reduce_and() @always_inline @@ -2354,10 +2405,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `True` if this vector contains **any** non-zero elements, `False` otherwise. """ - - @parameter - if type == DType.bool: - return self.reduce_or() return self.cast[DType.bool]().reduce_or() # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 41ede7a448..2cad8efe71 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1075,6 +1075,249 @@ def test_indexing(): assert_equal(s[3], 4) +def test_reduce(): + @parameter + def test_dtype[type: DType](): + alias X8 = SIMD[type, 8] + alias X4 = SIMD[type, 4] + alias X2 = SIMD[type, 2] + alias X1 = SIMD[type, 1] + var x8: X8 + var x4: X4 + var x2: X2 + var x1: X1 + + @parameter + if type.is_numeric(): + # reduce_add + x8 = X8(0, 1, 2, 3, 4, 5, 6, 7) + x4 = X4(4, 6, 8, 10) + x2 = X2(12, 16) + x1 = X1(int(28)) # TODO: fix MOCO-697 and use X1(28) instead + assert_equal(x8.reduce_add(), x1) + assert_equal(x4.reduce_add(), x1) + assert_equal(x2.reduce_add(), x1) + assert_equal(x1.reduce_add(), x1) + assert_equal(x8.reduce_add[2](), x2) + assert_equal(x4.reduce_add[2](), x2) + assert_equal(x2.reduce_add[2](), x2) + assert_equal(x8.reduce_add[4](), x4) + assert_equal(x4.reduce_add[4](), x4) + assert_equal(x8.reduce_add[8](), x8) + assert_equal(X2(6, 3).reduce_add(), 9) + + # reduce_mul + x8 = X8(0, 1, 2, 3, 4, 5, 6, 7) + x4 = X4(0, 5, 12, 21) + x2 = X2(0, 105) + x1 = X1(int(0)) # TODO: fix MOCO-697 and use X1(0) instead + assert_equal(x8.reduce_mul(), x1) + assert_equal(x4.reduce_mul(), x1) + assert_equal(x2.reduce_mul(), x1) + assert_equal(x1.reduce_mul(), x1) + assert_equal(x8.reduce_mul[2](), x2) + assert_equal(x4.reduce_mul[2](), x2) + assert_equal(x2.reduce_mul[2](), x2) + assert_equal(x8.reduce_mul[4](), x4) + assert_equal(x4.reduce_mul[4](), x4) + assert_equal(x8.reduce_mul[8](), x8) + assert_equal(X2(6, 3).reduce_mul(), 18) + + # reduce_min + x8 = X8(0, 1, 2, 3, 4, 5, 6, 7) + x4 = X4(0, 1, 2, 3) + x2 = X2(0, 1) + x1 = X1(int(0)) # TODO: fix MOCO-697 and use X1(0) instead + assert_equal(x8.reduce_min(), x1) + assert_equal(x4.reduce_min(), x1) + assert_equal(x2.reduce_min(), x1) + assert_equal(x1.reduce_min(), x1) + assert_equal(x8.reduce_min[2](), x2) + assert_equal(x4.reduce_min[2](), x2) + assert_equal(x2.reduce_min[2](), x2) + assert_equal(x8.reduce_min[4](), x4) + assert_equal(x4.reduce_min[4](), x4) + assert_equal(x8.reduce_min[8](), x8) + assert_equal(X2(6, 3).reduce_min(), 3) + + # reduce_max + x8 = X8(0, 1, 2, 3, 4, 5, 6, 7) + x4 = X4(4, 5, 6, 7) + x2 = X2(6, 7) + x1 = X1(int(7)) # TODO: fix MOCO-697 and use X1(7) instead + assert_equal(x8.reduce_max(), x1) + assert_equal(x4.reduce_max(), x1) + assert_equal(x2.reduce_max(), x1) + assert_equal(x1.reduce_max(), x1) + assert_equal(x8.reduce_max[2](), x2) + assert_equal(x4.reduce_max[2](), x2) + assert_equal(x2.reduce_max[2](), x2) + assert_equal(x8.reduce_max[4](), x4) + assert_equal(x4.reduce_max[4](), x4) + assert_equal(x8.reduce_max[8](), x8) + assert_equal(X2(6, 3).reduce_max(), 6) + + @parameter + if type.is_signed(): + # reduce_add + x8 = X8(0, -1, 2, -3, 4, -5, 6, -7) + x4 = X4(4, -6, 8, -10) + x2 = X2(12, -16) + x1 = X1(int(-4)) # TODO: fix MOCO-697 and use X1(-4) instead + assert_equal(x8.reduce_add(), x1) + assert_equal(x4.reduce_add(), x1) + assert_equal(x2.reduce_add(), x1) + assert_equal(x1.reduce_add(), x1) + assert_equal(x8.reduce_add[2](), x2) + assert_equal(x4.reduce_add[2](), x2) + assert_equal(x2.reduce_add[2](), x2) + assert_equal(x8.reduce_add[4](), x4) + assert_equal(x4.reduce_add[4](), x4) + assert_equal(x8.reduce_add[8](), x8) + assert_equal(X2(6, -3).reduce_add(), 3) + + # reduce_mul + x8 = X8(0, -1, 2, -3, 4, -5, 6, -7) + x4 = X4(0, 5, 12, 21) + x2 = X2(0, 105) + x1 = X1(int(0)) # TODO: fix MOCO-697 and use X1(0) instead + assert_equal(x8.reduce_mul(), x1) + assert_equal(x4.reduce_mul(), x1) + assert_equal(x2.reduce_mul(), x1) + assert_equal(x1.reduce_mul(), x1) + assert_equal(x8.reduce_mul[2](), x2) + assert_equal(x4.reduce_mul[2](), x2) + assert_equal(x2.reduce_mul[2](), x2) + assert_equal(x8.reduce_mul[4](), x4) + assert_equal(x4.reduce_mul[4](), x4) + assert_equal(x8.reduce_mul[8](), x8) + assert_equal(X2(6, -3).reduce_mul(), -18) + + # reduce_min + x8 = X8(0, -1, 2, -3, 4, -5, 6, -7) + x4 = X4(0, -5, 2, -7) + x2 = X2(0, -7) + x1 = X1(int(-7)) # TODO: fix MOCO-697 and use X1(-7) instead + assert_equal(x8.reduce_min(), x1) + assert_equal(x4.reduce_min(), x1) + assert_equal(x2.reduce_min(), x1) + assert_equal(x1.reduce_min(), x1) + assert_equal(x8.reduce_min[2](), x2) + assert_equal(x4.reduce_min[2](), x2) + assert_equal(x2.reduce_min[2](), x2) + assert_equal(x8.reduce_min[4](), x4) + assert_equal(x4.reduce_min[4](), x4) + assert_equal(x8.reduce_min[8](), x8) + assert_equal(X2(6, -3).reduce_min(), -3) + + # reduce_max + x8 = X8(0, -1, 2, -3, 4, -5, 6, -7) + x4 = X4(4, -1, 6, -3) + x2 = X2(6, -1) + x1 = X1(int(6)) # TODO: fix MOCO-697 and use X1(6) instead + assert_equal(x8.reduce_max(), x1) + assert_equal(x4.reduce_max(), x1) + assert_equal(x2.reduce_max(), x1) + assert_equal(x1.reduce_max(), x1) + assert_equal(x8.reduce_max[2](), x2) + assert_equal(x4.reduce_max[2](), x2) + assert_equal(x2.reduce_max[2](), x2) + assert_equal(x8.reduce_max[4](), x4) + assert_equal(x4.reduce_max[4](), x4) + assert_equal(x8.reduce_max[8](), x8) + assert_equal(X2(6, -3).reduce_max(), 6) + + @parameter + if type.is_bool(): + # reduce_and + x8 = X8(False, False, True, True, False, True, False, True) + x4 = X4(False, False, False, True) + x2 = X2(False, False) + x1 = X1(False) + assert_equal(x8.reduce_and(), x1) + assert_equal(x4.reduce_and(), x1) + assert_equal(x2.reduce_and(), x1) + assert_equal(x1.reduce_and(), x1) + assert_equal(x8.reduce_and[2](), x2) + assert_equal(x4.reduce_and[2](), x2) + assert_equal(x2.reduce_and[2](), x2) + assert_equal(x8.reduce_and[4](), x4) + assert_equal(x4.reduce_and[4](), x4) + assert_equal(x8.reduce_and[8](), x8) + assert_equal(X2(True, True).reduce_and(), True) + + # reduce_or + x8 = X8(False, False, True, True, False, True, False, True) + x4 = X4(False, True, True, True) + x2 = X2(True, True) + x1 = X1(True) + assert_equal(x8.reduce_or(), x1) + assert_equal(x4.reduce_or(), x1) + assert_equal(x2.reduce_or(), x1) + assert_equal(x1.reduce_or(), x1) + assert_equal(x8.reduce_or[2](), x2) + assert_equal(x4.reduce_or[2](), x2) + assert_equal(x2.reduce_or[2](), x2) + assert_equal(x8.reduce_or[4](), x4) + assert_equal(x4.reduce_or[4](), x4) + assert_equal(x8.reduce_or[8](), x8) + assert_equal(X2(False, False).reduce_or(), False) + + @parameter + if type.is_integral(): + # reduce_and + x8 = X8(0, 1, 2, 3, 4, 5, 6, 7) + x4 = X4(0, 1, 2, 3) + x2 = X2(0, 1) + x1 = X1(int(0)) # TODO: fix MOCO-697 and use X1(0) instead + assert_equal(x8.reduce_and(), x1) + assert_equal(x4.reduce_and(), x1) + assert_equal(x2.reduce_and(), x1) + assert_equal(x1.reduce_and(), x1) + assert_equal(x8.reduce_and[2](), x2) + assert_equal(x4.reduce_and[2](), x2) + assert_equal(x2.reduce_and[2](), x2) + assert_equal(x8.reduce_and[4](), x4) + assert_equal(x4.reduce_and[4](), x4) + assert_equal(x8.reduce_and[8](), x8) + assert_equal(X2(6, 3).reduce_and(), 2) + + # reduce_or + x8 = X8(0, 1, 2, 3, 4, 5, 6, 7) + x4 = X4(4, 5, 6, 7) + x2 = X2(6, 7) + x1 = X1(int(7)) # TODO: fix MOCO-697 and use X1(7) instead + assert_equal(x8.reduce_or(), x1) + assert_equal(x4.reduce_or(), x1) + assert_equal(x2.reduce_or(), x1) + assert_equal(x1.reduce_or(), x1) + assert_equal(x8.reduce_or[2](), x2) + assert_equal(x4.reduce_or[2](), x2) + assert_equal(x2.reduce_or[2](), x2) + assert_equal(x8.reduce_or[4](), x4) + assert_equal(x4.reduce_or[4](), x4) + assert_equal(x8.reduce_or[8](), x8) + assert_equal(X2(6, 3).reduce_or(), 7) + + test_dtype[DType.bool]() + test_dtype[DType.int8]() + test_dtype[DType.int16]() + test_dtype[DType.int32]() + test_dtype[DType.int64]() + test_dtype[DType.uint8]() + test_dtype[DType.uint16]() + test_dtype[DType.uint32]() + test_dtype[DType.uint64]() + test_dtype[DType.float16]() + test_dtype[DType.float32]() + test_dtype[DType.float64]() + test_dtype[DType.index]() + + @parameter + if not has_neon(): + test_dtype[DType.bfloat16]() + + def main(): test_abs() test_add() @@ -1115,3 +1358,4 @@ def main(): test_sub_with_overflow() test_trunc() test_truthy() + test_reduce() From f558f48ed129a5af89a6718b672a2c596af953a9 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 21 May 2024 18:42:15 -0500 Subject: [PATCH 0660/2019] [External] [stdlib] Add `__bool__()` for `InlineList` (#40381) [External] [stdlib] Add `__bool__(`) for `InlineList` This PR adds `__bool__()` for `InLineList`, which checks whether the list has any elements or not. ORIGINAL_AUTHOR=northstreet12 <166370617+northstreet12@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2769 Co-authored-by: northstreet12 <166370617+northstreet12@users.noreply.github.com> Closes modularml/mojo#2769 MODULAR_ORIG_COMMIT_REV_ID: 0803bf48d4097e2bd3f8f741c117877cb9c86e07 --- stdlib/src/collections/inline_list.mojo | 9 +++++++++ stdlib/test/collections/test_inline_list.mojo | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index ada4f64126..d38d3ecafd 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -198,3 +198,12 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): if value == rebind[C](i[]): return True return False + + @always_inline + fn __bool__(self) -> Bool: + """Checks whether the list has any elements or not. + + Returns: + `False` if the list is empty, `True` if there is at least one element. + """ + return len(self) > 0 diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index 9ec25036cd..70ef61edcf 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -138,6 +138,11 @@ def test_list_variadic_constructor(): assert_equal(8, l[3]) +def test_list_boolable(): + assert_true(InlineList[Int](1)) + assert_false(InlineList[Int]()) + + def test_indexing(): var list = InlineList[Int]() @@ -157,4 +162,5 @@ def main(): test_list_iter_mutable() test_list_contains() test_list_variadic_constructor() + test_list_boolable() test_indexing() From c85be51bd643327643b4b195aa9e256c7d020c7a Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 21 May 2024 18:49:37 -0500 Subject: [PATCH 0661/2019] [External] [stdlib] Implement `Dict.clear()` (#40322) [External] [stdlib] Implement `Dict.clear()` Implement `Dict.clear()` to remove all the elements from a `Dict`. ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2627 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2627 MODULAR_ORIG_COMMIT_REV_ID: 5eaab412c0d7b2a7cde82aecb29b746b7c2d769a --- docs/changelog.md | 3 +++ stdlib/src/collections/dict.mojo | 11 ++++++++++- stdlib/test/collections/test_dict.mojo | 13 +++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index f517c477f5..fd4de8a6d2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -364,6 +364,9 @@ what we publish. and value. ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) +- Added `clear` method to `Dict`. + ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 46d00001df..9b5c626912 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -431,6 +431,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( alias EMPTY = _EMPTY alias REMOVED = _REMOVED + alias _initial_reservation = 8 var size: Int """The number of elements currently stored in the dict.""" @@ -447,7 +448,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """Initialize an empty dictiontary.""" self.size = 0 self._n_entries = 0 - self._reserved = 8 + self._reserved = Self._initial_reservation self._index = _DictIndex(self._reserved) self._entries = Self._new_entries(self._reserved) @@ -522,6 +523,14 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = existing._index^ self._entries = existing._entries^ + fn clear(inout self): + """Remove all elements from the dictionary.""" + self.size = 0 + self._n_entries = 0 + self._reserved = Self._initial_reservation + self._index = _DictIndex(self._reserved) + self._entries = Self._new_entries(self._reserved) + fn __getitem__(self, key: K) raises -> V: """Retrieve a value out of the dictionary. diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 86748a6c9b..b51f0cb13b 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -512,6 +512,18 @@ def test_find_get(): assert_equal(some_dict.get("not_key", 0), 0) +fn test_clear() raises: + var some_dict = Dict[String, Int]() + some_dict["key"] = 1 + some_dict.clear() + assert_equal(len(some_dict), 0) + assert_false(some_dict.get("key")) + + some_dict = Dict[String, Int]() + some_dict.clear() + assert_equal(len(some_dict), 0) + + def main(): test_dict() test_dict_fromkeys() @@ -521,3 +533,4 @@ def main(): test_owned_kwargs_dict() test_bool_conversion() test_find_get() + test_clear() From 864f7cb19af9ca9a588c209b0ba6edd8172f051f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 21 May 2024 17:56:15 -0700 Subject: [PATCH 0662/2019] [mojo] Fix issues with self-referential structs This PR reworks the compiler implementation of nominal type lowering to fix crashes related to self-referential structs. In addition, it should correctly identity invalid self-referential structs and provide an error message. MODULAR_ORIG_COMMIT_REV_ID: 43b64a2c99d97550c38f833be9d7010d72db6abf --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index fd4de8a6d2..b5fd087a7b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -489,6 +489,8 @@ what we publish. ### 🛠️ Fixed +- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential + variant crashing the compiler. - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on simple trait definitions. - [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using From 99616c86e94a7c2880ffa3486b120751ba09546d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 22 May 2024 14:56:58 +0800 Subject: [PATCH 0663/2019] This replaces some (but not all) of the occurrences of unroll function with the more ergonomic parameter for. The migration to parameter for will happen piecemeal over the next few commits. MODULAR_ORIG_COMMIT_REV_ID: f1b082a56a7931fd4b0864f35c46c693f25dd24f --- stdlib/src/builtin/dtype.mojo | 29 ++++++++--------------------- stdlib/src/builtin/simd.mojo | 11 +++-------- stdlib/src/utils/static_tuple.mojo | 4 +--- 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 7df85f5d35..bd7ab0a552 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -581,23 +581,16 @@ struct DType(Stringable, Representable, KeyElement): dtypes: A list of DTypes on which to do dispatch. """ alias dtype_var = VariadicList[DType](dtypes) - var matched = False @parameter - @always_inline - fn _func[idx: Int](): + for idx in range(len(dtype_var)): alias dtype = dtype_var[idx] if self == dtype: - matched = True return func[dtype]() - unroll[_func, len(dtype_var)]() - - if not matched: - raise Error( - "dispatch_custom: dynamic_type does not match any dtype" - " parameters" - ) + raise Error( + "dispatch_custom: dynamic_type does not match any dtype parameters" + ) # ===----------------------------------------------------------------------===# # dispatch_arithmetic @@ -754,19 +747,13 @@ fn _get_runtime_dtype_size(type: DType) -> Int: DType.index, DType.address, ) - var size = -1 @parameter - @always_inline - fn func[idx: Int](): + for idx in range(len(type_list)): alias concrete_type = type_list[idx] if concrete_type == type: - size = sizeof[concrete_type]() - return - - unroll[func, len(type_list)]() + return sizeof[concrete_type]() - if size == -1: - abort("unable to get the dtype size of " + str(type)) + abort("unable to get the dtype size of " + str(type)) - return size + return -1 diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ff3373811a..456aed0d9f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1696,9 +1696,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ] ]() - @always_inline @parameter - fn fill[idx: Int](): + for idx in range(output_size): alias val = mask[idx] constrained[ 0 <= val < 2 * size, @@ -1709,7 +1708,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) __mlir_op.`pop.store`(val, ptr) - unroll[fill, output_size]() return array alias length = variadic_len[mask]() @@ -1745,14 +1743,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ @parameter - fn _check[i: Int](): + for i in range(output_size): constrained[ 0 <= mask[i] < 2 * size, "invalid index in the shuffle operation", ]() - unroll[_check, output_size]() - return __mlir_op.`pop.simd.shuffle`[ mask = mask.data.array, _type = __mlir_type[ @@ -2003,10 +1999,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var indices = StaticIntTuple[2 * size]() @parameter - fn _fill[i: Int](): + for i in range(2 * size): indices[i] = i - unroll[_fill, 2 * size]() return indices return self._shuffle_list[2 * size, build_indices()](other) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 9660535d58..60b570d073 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -89,12 +89,10 @@ fn _create_array[ _type = __mlir_type[`!pop.array<`, size.value, `, `, type, `>`] ]() - @always_inline @parameter - fn fill[idx: Int](): + for idx in range(size): _set_array_elem[idx, size, type](lst[idx], array) - unroll[fill, size]() return array From 14f81b5e9ae13a8dacbc5152d45c4fb1daa9bd7b Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 22 May 2024 09:49:35 -0400 Subject: [PATCH 0664/2019] [stdlib] Added `FileDescriptor` class. The `print` function currently takes either `1` or `2` as `stream_id`. We introduce a richer type `FileDescriptor` to allow printing to files. MODULAR_ORIG_COMMIT_REV_ID: 8041075de37557d109d01d524425398971a622de --- stdlib/src/builtin/file.mojo | 4 +- stdlib/src/builtin/file_descriptor.mojo | 52 +++++++++++++++++++++++++ stdlib/src/builtin/io.mojo | 38 ++++++++++++------ stdlib/src/sys/_io.mojo | 4 +- 4 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 stdlib/src/builtin/file_descriptor.mojo diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 67570baef5..0fc49ed4ca 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -81,7 +81,7 @@ struct FileHandle: Args: path: The file path. - mode: The mode to open the file in (the mode can be "r" or "w"). + mode: The mode to open the file in (the mode can be "r" or "w" or "rw"). """ self.__init__(path._strref_dangerous(), mode._strref_dangerous()) @@ -93,7 +93,7 @@ struct FileHandle: Args: path: The file path. - mode: The mode to open the file in (the mode can be "r" or "w"). + mode: The mode to open the file in (the mode can be "r" or "w" or "rw"). """ var err_msg = _OwnedStringRef() var handle = external_call[ diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo new file mode 100644 index 0000000000..2806e7072e --- /dev/null +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -0,0 +1,52 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Higher level abstraction for file stream. + +These are Mojo built-ins, so you don't need to import them. + +For example, here's how to print to a file + +```mojo +var f = open("my_file.txt", "r") +print("hello", file=f) +f.close() +``` + +""" + + +struct FileDescriptor: + """File descriptor of a file.""" + + var value: Int + """The underlying value of the file descriptor.""" + + fn __init__(inout self): + """Default constructor to stdout.""" + self.value = 1 + + fn __init__(inout self, x: Int): + """Constructs the file descriptor from an integer. + + Args: + x: The integer. + """ + self.value = x + + fn __init__(inout self, f: FileHandle): + """Constructs the file descriptor from a file handle. + + Args: + f: The file handle. + """ + self.value = f._get_raw_fd() diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index f890c3f17f..59c6ffef45 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -25,6 +25,7 @@ from sys import ( from builtin.dtype import _get_dtype_printf_format from builtin.builtin_list import _LITRefPackHelper +from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, unroll @@ -51,11 +52,11 @@ struct _fdopen: alias STDERR = 2 var handle: UnsafePointer[NoneType] - fn __init__(inout self, stream_id: Int): + fn __init__(inout self, stream_id: FileDescriptor): """Creates a file handle to the stdout/stderr stream. Args: - stream_id: The stream id (either `STDOUT` or `STDERR`) + stream_id: The stream id """ alias mode = "a" var handle: UnsafePointer[NoneType] @@ -63,11 +64,11 @@ struct _fdopen: @parameter if os_is_windows(): handle = external_call["_fdopen", UnsafePointer[NoneType]]( - _dup(stream_id), mode.unsafe_ptr() + _dup(stream_id.value), mode.unsafe_ptr() ) else: handle = external_call["fdopen", UnsafePointer[NoneType]]( - _dup(stream_id), mode.unsafe_ptr() + _dup(stream_id.value), mode.unsafe_ptr() ) self.handle = handle @@ -85,7 +86,7 @@ struct _fdopen: @no_inline -fn _flush(file: Int = stdout): +fn _flush(file: FileDescriptor = stdout): with _fdopen(file) as fd: _ = external_call["fflush", Int32](fd) @@ -98,7 +99,7 @@ fn _flush(file: Int = stdout): @no_inline fn _printf[ fmt: StringLiteral, *types: AnyType -](*arguments: *types, file: Int = stdout): +](*arguments: *types, file: FileDescriptor = stdout): # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C snprintf call. Load # all the members of the pack. @@ -239,7 +240,7 @@ fn _float_repr(buffer: UnsafePointer[UInt8], size: Int, x: Float64) -> Int: @no_inline -fn _put(x: Int, file: Int = stdout): +fn _put(x: Int, file: FileDescriptor = stdout): """Prints a scalar value. Args: @@ -307,13 +308,13 @@ fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): @no_inline -fn _put(x: String, file: Int = stdout): +fn _put(x: String, file: FileDescriptor = stdout): # 'x' is borrowed, so we know it will outlive the call to print. _put(x._strref_dangerous(), file=file) @no_inline -fn _put(x: StringRef, file: Int = stdout): +fn _put(x: StringRef, file: FileDescriptor = stdout): # Avoid printing "(null)" for an empty/default constructed `String` var str_len = len(x) @@ -345,12 +346,12 @@ fn _put(x: StringRef, file: Int = stdout): @no_inline -fn _put(x: StringLiteral, file: Int = stdout): +fn _put(x: StringLiteral, file: FileDescriptor = stdout): _put(StringRef(x), file=file) @no_inline -fn _put(x: DType, file: Int = stdout): +fn _put(x: DType, file: FileDescriptor = stdout): _put(str(x), file=file) @@ -367,7 +368,7 @@ fn print[ sep: StringLiteral = " ", end: StringLiteral = "\n", flush: Bool = False, - file: Int = stdout, + file: FileDescriptor = stdout, ): """Prints elements to the text stream. Each element is separated by `sep` and followed by `end`. @@ -383,6 +384,19 @@ fn print[ file: The output stream. """ + _print(values=values, sep=sep, end=end, flush=flush, file=file.value) + + +@no_inline +fn _print[ + *Ts: Stringable +]( + values: VariadicPack[_, _, Stringable, Ts], + sep: StringLiteral = " ", + end: StringLiteral = "\n", + flush: Bool = False, + file: Int = 1, +): @parameter fn print_with_separator[i: Int, T: Stringable](value: T): _put(str(value), file=file) diff --git a/stdlib/src/sys/_io.mojo b/stdlib/src/sys/_io.mojo index 105ef599c7..eaffb4ce08 100644 --- a/stdlib/src/sys/_io.mojo +++ b/stdlib/src/sys/_io.mojo @@ -11,5 +11,5 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # """IO constants and functions.""" -alias stdout: Int = 1 -alias stderr: Int = 2 +alias stdout: FileDescriptor = 1 +alias stderr: FileDescriptor = 2 From be76dd6ea45f114c619c2b2d7e19b2a5986f4a46 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 22 May 2024 10:55:42 -0400 Subject: [PATCH 0665/2019] [stdlib] Use consistent method order and comment headers for stdlib types MODULAR_ORIG_COMMIT_REV_ID: d6027952c0132873c42775871978fa5cf6a3e9d3 --- stdlib/src/builtin/string.mojo | 185 +++++++------ stdlib/src/collections/dict.mojo | 132 +++++---- stdlib/src/collections/list.mojo | 383 ++++++++++++++------------ stdlib/src/collections/optional.mojo | 134 +++++---- stdlib/src/collections/set.mojo | 79 +++--- stdlib/src/memory/unsafe_pointer.mojo | 134 ++++----- stdlib/src/utils/inlined_string.mojo | 126 +++++---- stdlib/src/utils/span.mojo | 69 ++--- stdlib/src/utils/static_tuple.mojo | 61 ++-- stdlib/src/utils/stringref.mojo | 163 ++++++----- stdlib/src/utils/variant.mojo | 63 +++-- 11 files changed, 846 insertions(+), 683 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 81a0862b77..2025ad8544 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -626,6 +626,7 @@ struct String( ): """Represents a mutable string.""" + # Fields alias _buffer_type = List[UInt8] var _buffer: Self._buffer_type """The underlying storage for the string.""" @@ -641,35 +642,8 @@ struct String( alias PUNCTUATION = String("""!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~""") alias PRINTABLE = String.DIGITS + String.ASCII_LETTERS + String.PUNCTUATION + String.WHITESPACE - @always_inline - fn __str__(self) -> String: - return self - - @always_inline - fn __repr__(self) -> String: - """Return a Mojo-compatible representation of the `String` instance. - - You don't need to call this method directly, use `repr(my_string)` instead. - - Returns: - A new representation of the string. - """ - alias ord_squote = ord("'") - var result = String() - var use_dquote = False - - for idx in range(len(self._buffer) - 1): - var char = self._buffer[idx] - result += _repr_ascii(char) - use_dquote = use_dquote or (char == ord_squote) - - if use_dquote: - return '"' + result + '"' - else: - return "'" + result + "'" - # ===------------------------------------------------------------------===# - # Initializers + # Life cycle methods # ===------------------------------------------------------------------===# @always_inline @@ -835,6 +809,37 @@ struct String( """ self._buffer = existing._buffer^ + # ===------------------------------------------------------------------===# + # Factory dunders + # ===------------------------------------------------------------------===# + + @staticmethod + fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + """ + Construct a string by concatenating a sequence of formattable arguments. + + Args: + args: A sequence of formattable arguments. + + Parameters: + Ts: The types of the arguments to format. Each type must be satisfy + `Formattable`. + + Returns: + A string formed by formatting the argument sequence. + """ + + var output = String() + var writer = output._unsafe_to_formatter() + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(writer) + + args.each[write_arg]() + + return output^ + @staticmethod @always_inline fn _from_bytes(owned buff: DTypePointer[DType.uint8]) -> String: @@ -870,15 +875,6 @@ struct String( # Operator dunders # ===------------------------------------------------------------------===# - @always_inline - fn __bool__(self) -> Bool: - """Checks if the string is not empty. - - Returns: - True if the string length is greater than zero, and False otherwise. - """ - return len(self) > 0 - fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> String: """Gets the character at the specified position. @@ -901,26 +897,6 @@ struct String( buf.append(0) return String(buf^) - @always_inline - fn _adjust_span(self, span: Slice) -> Slice: - """Adjusts the span based on the string length.""" - var adjusted_span = span - - if adjusted_span.start < 0: - adjusted_span.start = len(self) + adjusted_span.start - - if not adjusted_span._has_end(): - adjusted_span.end = len(self) - elif adjusted_span.end < 0: - adjusted_span.end = len(self) + adjusted_span.end - - if span.step < 0: - var tmp = adjusted_span.end - adjusted_span.end = adjusted_span.start - 1 - adjusted_span.start = tmp - 1 - - return adjusted_span - @always_inline fn __getitem__(self, span: Slice) -> String: """Gets the sequence of characters at the specified positions. @@ -948,20 +924,6 @@ struct String( buffer[adjusted_span_len] = 0 return Self(buffer^) - @always_inline - fn __len__(self) -> Int: - """Returns the string length. - - Returns: - The string length. - """ - # Avoid returning -1 if the buffer is not initialized - if not self.unsafe_ptr(): - return 0 - - # The negative 1 is to account for the terminator. - return len(self._buffer) - 1 - @always_inline fn __eq__(self, other: String) -> Bool: """Compares two Strings if they have the same values. @@ -1101,35 +1063,82 @@ struct String( ) # ===------------------------------------------------------------------=== # - # Methods + # Trait implementations # ===------------------------------------------------------------------=== # - @staticmethod - fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + @always_inline + fn __bool__(self) -> Bool: + """Checks if the string is not empty. + + Returns: + True if the string length is greater than zero, and False otherwise. """ - Construct a string by concatenating a sequence of formattable arguments. + return len(self) > 0 - Args: - args: A sequence of formattable arguments. + @always_inline + fn __len__(self) -> Int: + """Returns the string length. - Parameters: - Ts: The types of the arguments to format. Each type must be satisfy - `Formattable`. + Returns: + The string length. + """ + # Avoid returning -1 if the buffer is not initialized + if not self.unsafe_ptr(): + return 0 + + # The negative 1 is to account for the terminator. + return len(self._buffer) - 1 + + @always_inline + fn __str__(self) -> String: + return self + + @always_inline + fn __repr__(self) -> String: + """Return a Mojo-compatible representation of the `String` instance. + + You don't need to call this method directly, use `repr(my_string)` instead. Returns: - A string formed by formatting the argument sequence. + A new representation of the string. """ + alias ord_squote = ord("'") + var result = String() + var use_dquote = False - var output = String() - var writer = output._unsafe_to_formatter() + for idx in range(len(self._buffer) - 1): + var char = self._buffer[idx] + result += _repr_ascii(char) + use_dquote = use_dquote or (char == ord_squote) - @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(writer) + if use_dquote: + return '"' + result + '"' + else: + return "'" + result + "'" - args.each[write_arg]() + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # - return output^ + @always_inline + fn _adjust_span(self, span: Slice) -> Slice: + """Adjusts the span based on the string length.""" + var adjusted_span = span + + if adjusted_span.start < 0: + adjusted_span.start = len(self) + adjusted_span.start + + if not adjusted_span._has_end(): + adjusted_span.end = len(self) + elif adjusted_span.end < 0: + adjusted_span.end = len(self) + adjusted_span.end + + if span.step < 0: + var tmp = adjusted_span.end + adjusted_span.end = adjusted_span.start - 1 + adjusted_span.start = tmp - 1 + + return adjusted_span fn format_to(self, inout writer: Formatter): """ diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 9b5c626912..24ab0b7dd7 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -429,6 +429,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( # don't churn and compact on repeated insert/delete, and instead amortize # compaction cost to O(1) amortized cost. + # Fields alias EMPTY = _EMPTY alias REMOVED = _REMOVED alias _initial_reservation = 8 @@ -443,6 +444,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( var _index: _DictIndex var _entries: List[Optional[DictEntry[K, V]]] + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline fn __init__(inout self): """Initialize an empty dictiontary.""" @@ -523,13 +528,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = existing._index^ self._entries = existing._entries^ - fn clear(inout self): - """Remove all elements from the dictionary.""" - self.size = 0 - self._n_entries = 0 - self._reserved = Self._initial_reservation - self._index = _DictIndex(self._reserved) - self._entries = Self._new_entries(self._reserved) + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# fn __getitem__(self, key: K) raises -> V: """Retrieve a value out of the dictionary. @@ -582,6 +583,53 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return self.find(key).__bool__() + fn __iter__( + self: Reference[Self, _, _], + ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: + """Iterate over the dict's keys as immutable references. + + Returns: + An iterator of immutable references to the dictionary keys. + """ + return _DictKeyIter(_DictEntryIter(0, 0, self)) + + fn __reversed__( + self: Reference[Self, _, _] + ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime, False]: + """Iterate backwards over the dict keys, returning immutable references. + + Returns: + A reversed iterator of immutable references to the dict keys. + """ + return _DictKeyIter( + _DictEntryIter[forward=False](self[]._reserved - 1, 0, self) + ) + + fn __or__(self, other: Self) -> Self: + """Merge self with other and return the result as a new dict. + + Args: + other: The dictionary to merge with. + + Returns: + The result of the merge. + """ + var result = Dict(self) + result.update(other) + return result^ + + fn __ior__(inout self, other: Self): + """Merge self with other in place. + + Args: + other: The dictionary to merge with. + """ + self.update(other) + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + fn __len__(self) -> Int: """The number of elements currently stored in the dictionary.""" return self.size @@ -639,6 +687,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( result += "}" return result + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + fn _minimum_size_of_string_representation(self) -> Int: # we do a rough estimation of the minimum number of chars that we'll see # in the string representation, we assume that str(key) and str(value) @@ -745,16 +797,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( return default.value()[] raise "KeyError" - fn __iter__( - self: Reference[Self, _, _], - ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: - """Iterate over the dict's keys as immutable references. - - Returns: - An iterator of immutable references to the dictionary keys. - """ - return _DictKeyIter(_DictEntryIter(0, 0, self)) - fn keys( self: Reference[Self, _, _] ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: @@ -803,26 +845,13 @@ struct Dict[K: KeyElement, V: CollectionElement]( for entry in other.items(): self[entry[].key] = entry[].value - fn __or__(self, other: Self) -> Self: - """Merge self with other and return the result as a new dict. - - Args: - other: The dictionary to merge with. - - Returns: - The result of the merge. - """ - var result = Dict(self) - result.update(other) - return result^ - - fn __ior__(inout self, other: Self): - """Merge self with other in place. - - Args: - other: The dictionary to merge with. - """ - self.update(other) + fn clear(inout self): + """Remove all elements from the dictionary.""" + self.size = 0 + self._n_entries = 0 + self._reserved = Self._initial_reservation + self._index = _DictIndex(self._reserved) + self._entries = Self._new_entries(self._reserved) @staticmethod @always_inline @@ -929,18 +958,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._n_entries = self.size - fn __reversed__( - self: Reference[Self, _, _] - ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime, False]: - """Iterate backwards over the dict keys, returning immutable references. - - Returns: - A reversed iterator of immutable references to the dict keys. - """ - return _DictKeyIter( - _DictEntryIter[forward=False](self[]._reserved - 1, 0, self) - ) - struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """Container used to pass owned variadic keyword arguments to functions. @@ -953,10 +970,15 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): V: The value type of the dictionary. Currently must be CollectionElement. """ + # Fields alias key_type = String var _dict: Dict[Self.key_type, V] + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): """Initialize an empty keyword dictionary.""" self._dict = Dict[Self.key_type, V]() @@ -977,6 +999,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ self._dict = existing._dict^ + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __getitem__(self, key: Self.key_type) raises -> V: """Retrieve a value out of the keyword dictionary. @@ -1002,6 +1028,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ self._dict[key] = value + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __contains__(self, key: Self.key_type) -> Bool: """Check if a given key is in the keyword dictionary or not. @@ -1020,6 +1050,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """The number of elements currently stored in the keyword dictionary.""" return len(self._dict) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn find(self, key: Self.key_type) -> Optional[V]: """Find a value in the keyword dictionary by key. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b23f30d128..e2270e622a 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -84,6 +84,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): T: The type of the elements. """ + # Fields var data: UnsafePointer[T] """The underlying storage for the list.""" var size: Int @@ -91,6 +92,10 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var capacity: Int """The amount of elements that can fit in the list without resizing it.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): """Constructs an empty list.""" self.data = UnsafePointer[T]() @@ -185,6 +190,134 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if self.data: self.data.free() + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + + fn __setitem__[ + IndexerType: Indexer + ](inout self, i: IndexerType, owned value: T): + """Sets a list element at the given index. + + Parameters: + IndexerType: The type of the indexer. + + Args: + i: The index of the element. + value: The value to assign. + """ + var normalized_idx = index(i) + debug_assert( + -self.size <= normalized_idx < self.size, + "index must be within bounds", + ) + + if normalized_idx < 0: + normalized_idx += len(self) + + destroy_pointee(self.data + normalized_idx) + initialize_pointee_move(self.data + normalized_idx, value^) + + @always_inline + fn __contains__[ + T2: ComparableCollectionElement + ](self: List[T2], value: T) -> Bool: + """Verify if a given value is present in the list. + + ```mojo + var x = List[Int](1,2,3) + if 3 in x: print("x contains 3") + ``` + Parameters: + T2: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + value: The value to find. + + Returns: + True if the value is contained in the list, False otherwise. + """ + + constrained[_type_is_eq[T, T2](), "value type is not self.T"]() + for i in self: + if i[] == rebind[T2](value): + return True + return False + + @always_inline("nodebug") + fn __mul__(self, x: Int) -> Self: + """Multiplies the list by x and returns a new list. + + Args: + x: The multiplier number. + + Returns: + The new list. + """ + # avoid the copy since it would be cleared immediately anyways + if x == 0: + return Self() + var result = List(self) + result.__mul(x) + return result^ + + @always_inline("nodebug") + fn __imul__(inout self, x: Int): + """Multiplies the list by x in place. + + Args: + x: The multiplier number. + """ + self.__mul(x) + + @always_inline("nodebug") + fn __add__(self, owned other: Self) -> Self: + """Concatenates self with other and returns the result as a new list. + + Args: + other: List whose elements will be combined with the elements of self. + + Returns: + The newly created list. + """ + var result = List(self) + result.extend(other^) + return result^ + + @always_inline("nodebug") + fn __iadd__(inout self, owned other: Self): + """Appends the elements of other into self. + + Args: + other: List whose elements will be appended to self. + """ + self.extend(other^) + + fn __iter__( + self: Reference[Self, _, _], + ) -> _ListIter[T, self.is_mutable, self.lifetime]: + """Iterate over elements of the list, returning immutable references. + + Returns: + An iterator of immutable references to the list elements. + """ + return _ListIter(0, self) + + fn __reversed__( + self: Reference[Self, _, _] + ) -> _ListIter[T, self.is_mutable, self.lifetime, False]: + """Iterate backwards over the list, returning immutable references. + + Returns: + A reversed iterator of immutable references to the list elements. + """ + return _ListIter[forward=False](len(self[]), self) + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + fn __len__(self) -> Int: """Gets the number of elements in the list. @@ -201,6 +334,73 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ return len(self) > 0 + fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: + """Returns a string representation of a `List`. + + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + var my_list = List[Int](1, 2, 3) + print(my_list.__str__()) + ``` + + When the compiler supports conditional methods, then a simple `str(my_list)` will + be enough. + + The elements' type must implement the `__repr__()` for this to work. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Representable` and `CollectionElement`. + + Returns: + A string representation of the list. + """ + # we do a rough estimation of the number of chars that we'll see + # in the final string, we assume that str(x) will be at least one char. + var minimum_capacity = ( + 2 # '[' and ']' + + len(self) * 3 # str(x) and ", " + - 2 # remove the last ", " + ) + var result = String(List[UInt8](capacity=minimum_capacity)) + result += "[" + for i in range(len(self)): + result += repr(self[i]) + if i < len(self) - 1: + result += ", " + result += "]" + return result + + fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: + """Returns a string representation of a `List`. + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + var my_list = List[Int](1, 2, 3) + print(my_list.__repr__(my_list)) + ``` + + When the compiler supports conditional methods, then a simple `repr(my_list)` will + be enough. + + The elements' type must implement the `__repr__()` for this to work. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Representable` and `CollectionElement`. + + Returns: + A string representation of the list. + """ + return self.__str__() + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline fn _realloc(inout self, new_capacity: Int): var new_data = UnsafePointer[T].alloc(new_capacity) @@ -275,55 +475,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): for i in range(x - 1): self.extend(orig) - @always_inline("nodebug") - fn __mul__(self, x: Int) -> Self: - """Multiplies the list by x and returns a new list. - - Args: - x: The multiplier number. - - Returns: - The new list. - """ - # avoid the copy since it would be cleared immediately anyways - if x == 0: - return Self() - var result = List(self) - result.__mul(x) - return result^ - - @always_inline("nodebug") - fn __imul__(inout self, x: Int): - """Multiplies the list by x in place. - - Args: - x: The multiplier number. - """ - self.__mul(x) - - @always_inline("nodebug") - fn __add__(self, owned other: Self) -> Self: - """Concatenates self with other and returns the result as a new list. - - Args: - other: List whose elements will be combined with the elements of self. - - Returns: - The newly created list. - """ - var result = List(self) - result.extend(other^) - return result^ - - @always_inline("nodebug") - fn __iadd__(inout self, owned other: Self): - """Appends the elements of other into self. - - Args: - other: List whose elements will be appended to self. - """ - self.extend(other^) - @always_inline fn extend(inout self, owned other: List[T]): """Extends this list by consuming the elements of `other`. @@ -559,30 +710,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.capacity = 0 return ptr - fn __setitem__[ - IndexerType: Indexer - ](inout self, i: IndexerType, owned value: T): - """Sets a list element at the given index. - - Parameters: - IndexerType: The type of the indexer. - - Args: - i: The index of the element. - value: The value to assign. - """ - var normalized_idx = index(i) - debug_assert( - -self.size <= normalized_idx < self.size, - "index must be within bounds", - ) - - if normalized_idx < 0: - normalized_idx += len(self) - - destroy_pointee(self.data + normalized_idx) - initialize_pointee_move(self.data + normalized_idx, value^) - @always_inline fn _adjust_span(self, span: Slice) -> Slice: """Adjusts the span based on the list length.""" @@ -669,89 +796,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return (self[].data + normalized_idx)[] - fn __iter__( - self: Reference[Self, _, _], - ) -> _ListIter[T, self.is_mutable, self.lifetime]: - """Iterate over elements of the list, returning immutable references. - - Returns: - An iterator of immutable references to the list elements. - """ - return _ListIter(0, self) - - fn __reversed__( - self: Reference[Self, _, _] - ) -> _ListIter[T, self.is_mutable, self.lifetime, False]: - """Iterate backwards over the list, returning immutable references. - - Returns: - A reversed iterator of immutable references to the list elements. - """ - return _ListIter[forward=False](len(self[]), self) - - fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: - """Returns a string representation of a `List`. - - Note that since we can't condition methods on a trait yet, - the way to call this method is a bit special. Here is an example below: - - ```mojo - var my_list = List[Int](1, 2, 3) - print(my_list.__str__()) - ``` - - When the compiler supports conditional methods, then a simple `str(my_list)` will - be enough. - - The elements' type must implement the `__repr__()` for this to work. - - Parameters: - U: The type of the elements in the list. Must implement the - traits `Representable` and `CollectionElement`. - - Returns: - A string representation of the list. - """ - # we do a rough estimation of the number of chars that we'll see - # in the final string, we assume that str(x) will be at least one char. - var minimum_capacity = ( - 2 # '[' and ']' - + len(self) * 3 # str(x) and ", " - - 2 # remove the last ", " - ) - var result = String(List[UInt8](capacity=minimum_capacity)) - result += "[" - for i in range(len(self)): - result += repr(self[i]) - if i < len(self) - 1: - result += ", " - result += "]" - return result - - fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: - """Returns a string representation of a `List`. - Note that since we can't condition methods on a trait yet, - the way to call this method is a bit special. Here is an example below: - - ```mojo - var my_list = List[Int](1, 2, 3) - print(my_list.__repr__(my_list)) - ``` - - When the compiler supports conditional methods, then a simple `repr(my_list)` will - be enough. - - The elements' type must implement the `__repr__()` for this to work. - - Parameters: - U: The type of the elements in the list. Must implement the - traits `Representable` and `CollectionElement`. - - Returns: - A string representation of the list. - """ - return self.__str__() - fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. Note that since we can't condition methods on a trait yet, @@ -790,33 +834,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ return self.data - @always_inline - fn __contains__[ - T2: ComparableCollectionElement - ](self: List[T2], value: T) -> Bool: - """Verify if a given value is present in the list. - - ```mojo - var x = List[Int](1,2,3) - if 3 in x: print("x contains 3") - ``` - Parameters: - T2: The type of the elements in the list. Must implement the - traits `EqualityComparable` and `CollectionElement`. - - Args: - value: The value to find. - - Returns: - True if the value is contained in the list, False otherwise. - """ - - constrained[_type_is_eq[T, T2](), "value type is not self.T"]() - for i in self: - if i[] == rebind[T2](value): - return True - return False - fn _clip(value: Int, start: Int, end: Int) -> Int: return max(start, min(value, end)) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 479a7282c9..266d86cc19 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -74,11 +74,16 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): T: The type of value stored in the Optional. """ + # Fields # _NoneType comes first so its index is 0. # This means that Optionals that are 0-initialized will be None. alias _type = Variant[_NoneType, T] var _value: Self._type + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): """Construct an empty Optional.""" self._value = Self._type(_NoneType()) @@ -99,6 +104,60 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): """ self = Self() + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + + fn __is__(self, other: NoneType) -> Bool: + """Return `True` if the Optional has no value. + + It allows you to use the following syntax: `if my_optional is None:` + + Args: + other: The value to compare to (None). + + Returns: + True if the Optional has no value and False otherwise. + """ + return not self.__bool__() + + fn __isnot__(self, other: NoneType) -> Bool: + """Return `True` if the Optional has a value. + + It allows you to use the following syntax: `if my_optional is not None:`. + + Args: + other: The value to compare to (None). + + Returns: + True if the Optional has a value and False otherwise. + """ + return self.__bool__() + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + fn __bool__(self) -> Bool: + """Return true if the Optional has a value. + + Returns: + True if the optional has a value and False otherwise. + """ + return not self._value.isa[_NoneType]() + + fn __invert__(self) -> Bool: + """Return False if the optional has a value. + + Returns: + False if the optional has a value and True otherwise. + """ + return not self + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline fn value( self: Reference[Self, _, _] @@ -195,48 +254,6 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): return self._value[T] return default - fn __is__(self, other: NoneType) -> Bool: - """Return `True` if the Optional has no value. - - It allows you to use the following syntax: `if my_optional is None:` - - Args: - other: The value to compare to (None). - - Returns: - True if the Optional has no value and False otherwise. - """ - return not self.__bool__() - - fn __isnot__(self, other: NoneType) -> Bool: - """Return `True` if the Optional has a value. - - It allows you to use the following syntax: `if my_optional is not None:`. - - Args: - other: The value to compare to (None). - - Returns: - True if the Optional has a value and False otherwise. - """ - return self.__bool__() - - fn __bool__(self) -> Bool: - """Return true if the Optional has a value. - - Returns: - True if the optional has a value and False otherwise. - """ - return not self._value.isa[_NoneType]() - - fn __invert__(self) -> Bool: - """Return False if the optional has a value. - - Returns: - False if the optional has a value and True otherwise. - """ - return not self - # ===----------------------------------------------------------------------===# # OptionalReg @@ -254,9 +271,14 @@ struct OptionalReg[T: AnyRegType](Boolable): T: The type of value stored in the Optional. """ + # Fields alias _mlir_type = __mlir_type[`!kgen.variant<`, T, `, i1>`] var _value: Self._mlir_type + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): """Create an optional with a value of None.""" self = Self(None) @@ -281,14 +303,9 @@ struct OptionalReg[T: AnyRegType](Boolable): _type = Self._mlir_type, index = Int(1).value ](__mlir_attr.false) - @always_inline - fn value(self) -> T: - """Get the optional value. - - Returns: - The contained value. - """ - return __mlir_op.`kgen.variant.take`[index = Int(0).value](self._value) + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# fn __is__(self, other: NoneType) -> Bool: """Return `True` if the Optional has no value. @@ -316,6 +333,10 @@ struct OptionalReg[T: AnyRegType](Boolable): """ return self.__bool__() + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + fn __bool__(self) -> Bool: """Return true if the optional has a value. @@ -323,3 +344,16 @@ struct OptionalReg[T: AnyRegType](Boolable): True if the optional has a value and False otherwise. """ return __mlir_op.`kgen.variant.is`[index = Int(0).value](self._value) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + @always_inline + fn value(self) -> T: + """Get the optional value. + + Returns: + The contained value. + """ + return __mlir_op.`kgen.variant.take`[index = Int(0).value](self._value) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index ab629c121a..3ddb0f9bd2 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -41,8 +41,13 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): T: The element type of the set. Must implement KeyElement. """ + # Fields var _data: Dict[T, NoneType] + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self, *ts: T): """Construct a set from initial elements. @@ -81,6 +86,10 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ self._data = other._data^ + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + fn __contains__(self, t: T) -> Bool: """Whether or not the set contains an element. @@ -92,22 +101,6 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return t in self._data - fn __bool__(self) -> Bool: - """Whether the set is non-empty or not. - - Returns: - True if the set is non-empty, False if it is empty. - """ - return len(self).__bool__() - - fn __len__(self) -> Int: - """The size of the set. - - Returns: - The number of elements in the set. - """ - return len(self._data) - fn __eq__(self, other: Self) -> Bool: """Set equality. @@ -135,21 +128,6 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return not (self == other) - fn __hash__(self) -> Int: - """A hash value of the elements in the set. - - The hash value is order independent, so s1 == s2 -> hash(s1) == hash(s2). - - Returns: - A hash value of the set suitable for non-cryptographic purposes. - """ - var hash_value = 0 - # Hash combination needs to be commutative so iteration order - # doesn't impact the hash value. - for e in self: - hash_value ^= hash(e[]) - return hash_value - fn __and__(self, other: Self) -> Self: """The set intersection operator. @@ -283,6 +261,45 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ self.symmetric_difference_update(other) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn __bool__(self) -> Bool: + """Whether the set is non-empty or not. + + Returns: + True if the set is non-empty, False if it is empty. + """ + return len(self).__bool__() + + fn __len__(self) -> Int: + """The size of the set. + + Returns: + The number of elements in the set. + """ + return len(self._data) + + fn __hash__(self) -> Int: + """A hash value of the elements in the set. + + The hash value is order independent, so s1 == s2 -> hash(s1) == hash(s2). + + Returns: + A hash value of the set suitable for non-cryptographic purposes. + """ + var hash_value = 0 + # Hash combination needs to be commutative so iteration order + # doesn't impact the hash value. + for e in self: + hash_value ^= hash(e[]) + return hash_value + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + fn __iter__( self: Reference[Self, _, _], ) -> _DictKeyIter[T, NoneType, self.is_mutable, self.lifetime]: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b5af5681f1..b66bfece50 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -39,6 +39,7 @@ struct UnsafePointer[ address_space: The address space associated with the UnsafePointer allocated memory. """ + # Fields alias _mlir_type = __mlir_type[ `!kgen.pointer<`, T, `,`, address_space._value.value, `>` ] @@ -55,7 +56,7 @@ struct UnsafePointer[ """The underlying pointer.""" # ===-------------------------------------------------------------------===# - # Initializers + # Life cycle methods # ===-------------------------------------------------------------------===# @always_inline @@ -174,72 +175,35 @@ struct UnsafePointer[ return Self(arg) # ===-------------------------------------------------------------------===# - # Methods + # Operator dunders # ===-------------------------------------------------------------------===# @always_inline - fn free(self): - """Free the memory referenced by the pointer.""" - Pointer[Int8, address_space=address_space](address=int(self)).free() - - @always_inline("nodebug") - fn bitcast[ - new_type: AnyType = T, - /, - address_space: AddressSpace = Self.address_space, - ](self) -> UnsafePointer[new_type, address_space]: - """Bitcasts a UnsafePointer to a different type. - - Parameters: - new_type: The target type. - address_space: The address space of the result. + fn __refitem__( + self, + ) -> Self._ref_type: + """Return a reference to the underlying data, offset by the offset index. Returns: - A new UnsafePointer object with the specified type and the same address, - as the original UnsafePointer. + A reference to the value. """ - return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, address_space]._mlir_type, + return __mlir_op.`lit.ref.from_pointer`[ + _type = Self._ref_type._mlir_type ](self.address) @always_inline - fn offset(self, offset: Int) -> Self: - """Return a pointer at an offset from the current one. + fn __refitem__[ + IndexerType: Indexer + ](self, offset: IndexerType) -> Self._ref_type: + """Return a reference to the underlying data, offset by the offset index. Args: offset: The offset index. Returns: - An offset pointer. - """ - return Self(address=int(self) + offset * sizeof[T]()) - - @always_inline - fn __int__(self) -> Int: - """Returns the pointer address as an integer. - - Returns: - The address of the pointer as an Int. - """ - return __mlir_op.`pop.pointer_to_index`[ - _type = __mlir_type.`!pop.scalar` - ](self.address) - - fn __str__(self) -> String: - return hex(self) - - # ===-------------------------------------------------------------------===# - # Operator dunders - # ===-------------------------------------------------------------------===# - - @always_inline - fn __bool__(self) -> Bool: - """Return true if the pointer is non-null. - - Returns: - Whether the pointer is null. + An offset reference. """ - return int(self) != 0 + return (self + index(offset)).__refitem__() @always_inline fn __add__(self, offset: Int) -> Self: @@ -357,35 +321,73 @@ struct UnsafePointer[ """ return int(self) >= int(rhs) + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @always_inline - fn __refitem__( - self, - ) -> Self._ref_type: - """Return a reference to the underlying data, offset by the offset index. + fn __bool__(self) -> Bool: + """Return true if the pointer is non-null. Returns: - A reference to the value. + Whether the pointer is null. """ - return __mlir_op.`lit.ref.from_pointer`[ - _type = Self._ref_type._mlir_type + return int(self) != 0 + + @always_inline + fn __int__(self) -> Int: + """Returns the pointer address as an integer. + + Returns: + The address of the pointer as an Int. + """ + return __mlir_op.`pop.pointer_to_index`[ + _type = __mlir_type.`!pop.scalar` ](self.address) + fn __str__(self) -> String: + return hex(self) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline - fn __refitem__[ - IndexerType: Indexer - ](self, offset: IndexerType) -> Self._ref_type: - """Return a reference to the underlying data, offset by the offset index. + fn free(self): + """Free the memory referenced by the pointer.""" + Pointer[Int8, address_space=address_space](address=int(self)).free() + + @always_inline("nodebug") + fn bitcast[ + new_type: AnyType = T, + /, + address_space: AddressSpace = Self.address_space, + ](self) -> UnsafePointer[new_type, address_space]: + """Bitcasts a UnsafePointer to a different type. Parameters: - IndexerType: The type of the indexer. + new_type: The target type. + address_space: The address space of the result. + + Returns: + A new UnsafePointer object with the specified type and the same address, + as the original UnsafePointer. + """ + return __mlir_op.`pop.pointer.bitcast`[ + _type = UnsafePointer[new_type, address_space]._mlir_type, + ](self.address) + + @always_inline + fn offset(self, offset: Int) -> Self: + """Return a pointer at an offset from the current one. Args: offset: The offset index. Returns: - An offset reference. + An offset pointer. """ - return (self + index(offset)).__refitem__() + return Self(address=int(self) + offset * sizeof[T]()) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 8edf536e1f..fb814d8829 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -44,12 +44,13 @@ struct InlinedString(Sized, Stringable, CollectionElement): layout of this string, even if the given string would fit within the small-string capacity of this type.""" + # Fields alias Layout = Variant[String, _FixedString[Self.SMALL_CAP]] var _storage: Self.Layout # ===------------------------------------------------------------------===# - # Constructors + # Life cycle methods # ===------------------------------------------------------------------===# fn __init__(inout self): @@ -91,25 +92,9 @@ struct InlinedString(Sized, Stringable, CollectionElement): self._storage = Self.Layout(heap_string^) # ===------------------------------------------------------------------=== # - # Trait Interfaces + # Operator dunders # ===------------------------------------------------------------------=== # - fn __len__(self) -> Int: - if self._is_small(): - return len(self._storage[_FixedString[Self.SMALL_CAP]]) - else: - debug_assert( - self._storage.isa[String](), - "expected non-small string variant to be String", - ) - return len(self._storage[String]) - - fn __str__(self) -> String: - if self._is_small(): - return str(self._storage[_FixedString[Self.SMALL_CAP]]) - else: - return self._storage[String] - fn __iadd__(inout self, literal: StringLiteral): """Appends another string to this string. @@ -220,6 +205,26 @@ struct InlinedString(Sized, Stringable, CollectionElement): string += other._strref_dangerous() return string + # ===------------------------------------------------------------------=== # + # Trait implementations + # ===------------------------------------------------------------------=== # + + fn __len__(self) -> Int: + if self._is_small(): + return len(self._storage[_FixedString[Self.SMALL_CAP]]) + else: + debug_assert( + self._storage.isa[String](), + "expected non-small string variant to be String", + ) + return len(self._storage[String]) + + fn __str__(self) -> String: + if self._is_small(): + return str(self._storage[_FixedString[Self.SMALL_CAP]]) + else: + return self._storage[String] + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # @@ -278,13 +283,14 @@ struct _FixedString[CAP: Int]( CAP: The fixed-size count of bytes of string storage capacity available. """ + # Fields var buffer: InlineArray[UInt8, CAP] """The underlying storage for the fixed string.""" var size: Int """The number of elements in the vector.""" # ===------------------------------------------------------------------===# - # Constructors + # Life cycle methods # ===------------------------------------------------------------------===# fn __init__(inout self): @@ -314,15 +320,39 @@ struct _FixedString[CAP: Int]( memcpy(self.buffer.unsafe_ptr(), literal.as_uint8_ptr(), len(literal)) # ===------------------------------------------------------------------=== # - # Trait Interfaces + # Factor methods # ===------------------------------------------------------------------=== # - @always_inline - fn __str__(self) -> String: - return String(self._strref_dangerous()) + @staticmethod + fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + """ + Construct a string by concatenating a sequence of formattable arguments. - fn __len__(self) -> Int: - return self.size + Args: + args: A sequence of formattable arguments. + + Parameters: + Ts: The types of the arguments to format. Each type must be satisfy + `Formattable`. + + Returns: + A string formed by formatting the argument sequence. + """ + + var output = Self() + var writer = output._unsafe_to_formatter() + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(writer) + + args.each[write_arg]() + + return output^ + + # ===------------------------------------------------------------------=== # + # Operator dunders + # ===------------------------------------------------------------------=== # fn __iadd__(inout self, literal: StringLiteral) raises: """Appends another string to this string. @@ -351,6 +381,21 @@ struct _FixedString[CAP: Int]( if err: raise err.value()[] + # ===------------------------------------------------------------------=== # + # Trait implementations + # ===------------------------------------------------------------------=== # + + @always_inline + fn __str__(self) -> String: + return String(self._strref_dangerous()) + + fn __len__(self) -> Int: + return self.size + + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # + fn _iadd_non_raising(inout self, strref: StringRef) -> Optional[Error]: var total_len = len(self) + len(strref) @@ -402,37 +447,6 @@ struct _FixedString[CAP: Int]( UnsafePointer.address_of(self).bitcast[NoneType](), ) - # ===------------------------------------------------------------------=== # - # Methods - # ===------------------------------------------------------------------=== # - - @staticmethod - fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: - """ - Construct a string by concatenating a sequence of formattable arguments. - - Args: - args: A sequence of formattable arguments. - - Parameters: - Ts: The types of the arguments to format. Each type must be satisfy - `Formattable`. - - Returns: - A string formed by formatting the argument sequence. - """ - - var output = Self() - var writer = output._unsafe_to_formatter() - - @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(writer) - - args.each[write_arg]() - - return output^ - fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index f80b6c9721..7024579114 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -81,11 +81,12 @@ struct Span[ lifetime: The lifetime of the Span. """ + # Field var _data: UnsafePointer[T] var _len: Int # ===------------------------------------------------------------------===# - # Initializers + # Life cycle methods # ===------------------------------------------------------------------===# @always_inline @@ -124,19 +125,6 @@ struct Span[ self._data = UnsafePointer(array).bitcast[T]() self._len = size - # ===------------------------------------------------------------------===# - # Trait impls - # ===------------------------------------------------------------------===# - - @always_inline - fn __len__(self) -> Int: - """Returns the length of the span. This is a known constant value. - - Returns: - The size of the span. - """ - return self._len - # ===------------------------------------------------------------------===# # Operator dunders # ===------------------------------------------------------------------===# @@ -154,26 +142,6 @@ struct Span[ offset += len(self) return (self._data + offset)[] - @always_inline - fn _adjust_span(self, span: Slice) -> Slice: - """Adjusts the span based on the list length.""" - var adjusted_span = span - - if adjusted_span.start < 0: - adjusted_span.start = len(self) + adjusted_span.start - - if not adjusted_span._has_end(): - adjusted_span.end = len(self) - elif adjusted_span.end < 0: - adjusted_span.end = len(self) + adjusted_span.end - - if span.step < 0: - var tmp = adjusted_span.end - adjusted_span.end = adjusted_span.start - 1 - adjusted_span.start = tmp - 1 - - return adjusted_span - @always_inline fn __getitem__[ IndexerType: Indexer @@ -243,10 +211,43 @@ struct Span[ """ return _SpanIter(0, self) + # ===------------------------------------------------------------------===# + # Trait implementations + # ===------------------------------------------------------------------===# + + @always_inline + fn __len__(self) -> Int: + """Returns the length of the span. This is a known constant value. + + Returns: + The size of the span. + """ + return self._len + # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# + @always_inline + fn _adjust_span(self, span: Slice) -> Slice: + """Adjusts the span based on the list length.""" + var adjusted_span = span + + if adjusted_span.start < 0: + adjusted_span.start = len(self) + adjusted_span.start + + if not adjusted_span._has_end(): + adjusted_span.end = len(self) + elif adjusted_span.end < 0: + adjusted_span.end = len(self) + adjusted_span.end + + if span.step < 0: + var tmp = adjusted_span.end + adjusted_span.end = adjusted_span.start - 1 + adjusted_span.start = tmp - 1 + + return adjusted_span + fn unsafe_ptr(self) -> UnsafePointer[T]: """ Gets a pointer to the first element of this slice. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 60b570d073..9ab4141222 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -255,6 +255,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): size: The size of the array. """ + # Fields alias type = __mlir_type[ `!pop.array<`, size.value, `, `, Self.ElementType, `>` ] @@ -262,7 +263,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): """The underlying storage for the array.""" # ===------------------------------------------------------------------===# - # Initializers + # Life cycle methods # ===------------------------------------------------------------------===# @always_inline @@ -335,33 +336,6 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): UnsafePointer[Self.ElementType](ref), elems[i] ) - # ===------------------------------------------------------------------=== # - # Trait Interfaces - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn __len__(self) -> Int: - """Returns the length of the array. This is a known constant value. - - Returns: - The size of the array. - """ - return size - - @always_inline("nodebug") - fn _get_reference_unsafe( - self: Reference[Self, _, _], index: Int - ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: - """Get a reference to an element of self without checking index bounds. - - Users should opt for `__refitem__` instead of this method. - """ - var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(self[]._array).address, - index.value, - ) - return UnsafePointer(ptr)[] - # ===------------------------------------------------------------------===# # Operator dunders # ===------------------------------------------------------------------===# @@ -417,6 +391,37 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): return self[]._get_reference_unsafe(normalized_idx) + # ===------------------------------------------------------------------=== # + # Trait implementations + # ===------------------------------------------------------------------=== # + + @always_inline("nodebug") + fn __len__(self) -> Int: + """Returns the length of the array. This is a known constant value. + + Returns: + The size of the array. + """ + return size + + # ===------------------------------------------------------------------===# + # Methods + # ===------------------------------------------------------------------===# + + @always_inline("nodebug") + fn _get_reference_unsafe( + self: Reference[Self, _, _], index: Int + ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: + """Get a reference to an element of self without checking index bounds. + + Users should opt for `__refitem__` instead of this method. + """ + var ptr = __mlir_op.`pop.array.gep`( + UnsafePointer.address_of(self[]._array).address, + index.value, + ) + return UnsafePointer(ptr)[] + @always_inline fn unsafe_ptr(self) -> UnsafePointer[Self.ElementType]: """Get an `UnsafePointer` to the underlying array. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 7e929d97e8..ec50253d9b 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -50,11 +50,16 @@ struct StringRef( and a length, which need not be null terminated. """ + # Fields var data: UnsafePointer[UInt8] """A pointer to the beginning of the string data being referenced.""" var length: Int """The length of the string being referenced.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline fn __init__(str: StringLiteral) -> StringRef: """Construct a StringRef value given a constant string. @@ -67,14 +72,6 @@ struct StringRef( """ return StringRef(str.unsafe_ptr(), len(str)) - fn __str__(self) -> String: - """Convert the string reference to a string. - - Returns: - A new string. - """ - return self - # TODO: #2317 Drop support for this constructor when we have fully # transitioned to UInt8 as the main byte type. @always_inline @@ -170,34 +167,24 @@ struct StringRef( return StringRef(ptr.bitcast[DType.int8](), len) - @always_inline - fn unsafe_ptr(self) -> UnsafePointer[UInt8]: - """Retrieves a pointer to the underlying memory. - - Prefer to use `as_uint8_ptr()` instead. + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# - Returns: - The pointer to the underlying memory. - """ - return self.data - - @always_inline - fn __bool__(self) -> Bool: - """Checks if the string is empty or not. + @always_inline("nodebug") + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> StringRef: + """Get the string value at the specified position. - Returns: - Returns True if the string is not empty and False otherwise. - """ - return len(self) != 0 + Parameters: + IndexerType: The type of the indexer. - @always_inline - fn __len__(self) -> Int: - """Returns the length of the string. + Args: + idx: The index position. Returns: - The length of the string. + The character at the specified position. """ - return self.length + return StringRef {data: self.data + index(idx), length: 1} @always_inline fn __eq__(self, rhs: StringRef) -> Bool: @@ -211,16 +198,16 @@ struct StringRef( """ return not (self != rhs) - # Use a local memcmp rather than memory.memcpy to avoid indirect recursions. - @always_inline("nodebug") - fn _memcmp(self, other: StringRef, count: Int) -> Int: - for i in range(count): - var s1i = self.data[i] - var s2i = other.data[i] - if s1i == s2i: - continue - return 1 if s1i > s2i else -1 - return 0 + fn __contains__(self, substr: StringRef) -> Bool: + """Returns True if the substring is contained within the current string. + + Args: + substr: The substring to check. + + Returns: + True if the string contains the substring. + """ + return self.find(substr) != -1 @always_inline fn __ne__(self, rhs: StringRef) -> Bool: @@ -288,20 +275,18 @@ struct StringRef( """ return not (self < rhs) - @always_inline("nodebug") - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> StringRef: - """Get the string value at the specified position. - - Parameters: - IndexerType: The type of the indexer. + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# - Args: - idx: The index position. + @always_inline + fn __bool__(self) -> Bool: + """Checks if the string is empty or not. Returns: - The character at the specified position. + Returns True if the string is not empty and False otherwise. """ - return StringRef {data: self.data + index(idx), length: 1} + return len(self) != 0 fn __hash__(self) -> Int: """Hash the underlying buffer using builtin hash. @@ -313,6 +298,61 @@ struct StringRef( """ return hash(self.data, self.length) + fn __int__(self) raises -> Int: + """Parses the given string as a base-10 integer and returns that value. + + For example, `int("19")` returns `19`. If the given string cannot be parsed + as an integer value, an error is raised. For example, `int("hi")` raises an + error. + + Returns: + An integer value that represents the string, or otherwise raises. + """ + return _atol(self) + + @always_inline + fn __len__(self) -> Int: + """Returns the length of the string. + + Returns: + The length of the string. + """ + return self.length + + fn __str__(self) -> String: + """Convert the string reference to a string. + + Returns: + A new string. + """ + return self + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + # Use a local memcmp rather than memory.memcpy to avoid indirect recursions. + @always_inline("nodebug") + fn _memcmp(self, other: StringRef, count: Int) -> Int: + for i in range(count): + var s1i = self.data[i] + var s2i = other.data[i] + if s1i == s2i: + continue + return 1 if s1i > s2i else -1 + return 0 + + @always_inline + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: + """Retrieves a pointer to the underlying memory. + + Prefer to use `as_uint8_ptr()` instead. + + Returns: + The pointer to the underlying memory. + """ + return self.data + fn count(self, substr: StringRef) -> Int: """Return the number of non-overlapping occurrences of substring `substr` in the string. @@ -342,17 +382,6 @@ struct StringRef( return res - fn __contains__(self, substr: StringRef) -> Bool: - """Returns True if the substring is contained within the current string. - - Args: - substr: The substring to check. - - Returns: - True if the string contains the substring. - """ - return self.find(substr) != -1 - fn find(self, substr: StringRef, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at `start`. If not found, returns -1. @@ -477,18 +506,6 @@ struct StringRef( end -= 1 return StringRef(ptr + start, end - start) - fn __int__(self) raises -> Int: - """Parses the given string as a base-10 integer and returns that value. - - For example, `int("19")` returns `19`. If the given string cannot be parsed - as an integer value, an error is raised. For example, `int("hi")` raises an - error. - - Returns: - An integer value that represents the string, or otherwise raises. - """ - return _atol(self) - # ===----------------------------------------------------------------------===# # Utilities diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 8cc2513dda..3adcf36ed7 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -139,12 +139,17 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Ts: The elements of the variadic. """ + # Fields alias _sentinel: Int = -1 alias _mlir_type = __mlir_type[ `!kgen.variant<[rebind(:`, __type_of(Ts), ` `, Ts, `)]>` ] var _impl: Self._mlir_type + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__[T: CollectionElement](inout self, owned value: T): """Create a variant with one of the types. @@ -201,6 +206,39 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """Destroy the variant.""" self._call_correct_deleter() + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + + fn __refitem__[ + T: CollectionElement + ](self: Reference[Self, _, _]) -> Reference[ + T, self.is_mutable, self.lifetime + ]: + """Get the value out of the variant as a type-checked type. + + This explicitly check that your value is of that type! + If you haven't verified the type correctness at runtime, the program + will abort! + + For now this has the limitations that it + - requires the variant value to be mutable + + Parameters: + T: The type of the value to get out. + + Returns: + The internal data represented as a `Reference[T]`. + """ + if not self[].isa[T](): + abort("get: wrong variant type") + + return self[].unsafe_get[T]() + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + fn _get_ptr[T: CollectionElement](self) -> UnsafePointer[T]: constrained[ Self._check[T]() != Self._sentinel, "not a union element type" @@ -372,31 +410,6 @@ struct Variant[*Ts: CollectionElement](CollectionElement): debug_assert(self[].isa[T](), "get: wrong variant type") return self[]._get_ptr[T]()[] - fn __refitem__[ - T: CollectionElement - ](self: Reference[Self, _, _]) -> Reference[ - T, self.is_mutable, self.lifetime - ]: - """Get the value out of the variant as a type-checked type. - - This explicitly check that your value is of that type! - If you haven't verified the type correctness at runtime, the program - will abort! - - For now this has the limitations that it - - requires the variant value to be mutable - - Parameters: - T: The type of the value to get out. - - Returns: - The internal data represented as a `Reference[T]`. - """ - if not self[].isa[T](): - abort("get: wrong variant type") - - return self[].unsafe_get[T]() - @staticmethod fn _check[T: CollectionElement]() -> Int8: return _UnionTypeIndex[T, Ts].compute() From c6f36614208498ee7ad887daed4fbb77624f8ded Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 22 May 2024 11:17:56 -0400 Subject: [PATCH 0666/2019] [stdlib] Updated style-guide for 40196. MODULAR_ORIG_COMMIT_REV_ID: a0d5a5780a7c263071f03d42bfd708a8bb286fb9 --- stdlib/docs/style-guide.md | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 55419b73f4..3f14fd3129 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -119,28 +119,52 @@ defined on structs. struct MyStruct(Sized, Stringable): """Description goes here.""" + # Fields var field: Int - # ===------------------------------------------------------------------===# - # Constructors - # ===------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# - fn __init__(self): - ... + fn __init__(...) + fn __moveinit__(...) + fn __copyinit__(...) - # ===------------------------------------------------------------------=== # - # Trait Interfaces - # ===------------------------------------------------------------------=== # + fn __del__(...) - fn __len__(self) -> Int: - ... + # ===-------------------------------------------------------------------===# + # Factory methods + # ===-------------------------------------------------------------------===# - fn __str__(self) -> String: - ... + @staticmethod + fn foo(...) -> Self[...] + + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + + # Basically anything that "backs" special syntax: [..], *, +, /, //, etc... - # ===------------------------------------------------------------------=== # + fn __getitem__ + fn __getitem__ + fn __refitem__ + + fn __add__ + fn __iadd__ + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + fn __bool__ + fn __len__ + fn __str__ + + # ===-------------------------------------------------------------------===# # Methods - # ===------------------------------------------------------------------=== # + # ===-------------------------------------------------------------------===# + + fn unsafe_ptr(..) # e.g. ``` ## Code conventions From 34e33951e655e65b1325756fc812b0fda0da7064 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 22 May 2024 09:23:47 -0700 Subject: [PATCH 0667/2019] Revert self-referential fix temporarily This is causing other compiler crashes currently MODULAR_ORIG_COMMIT_REV_ID: 35de2e983d1c017b67cbbd1825369d350feb2939 --- docs/changelog.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b5fd087a7b..fd4de8a6d2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -489,8 +489,6 @@ what we publish. ### 🛠️ Fixed -- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential - variant crashing the compiler. - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on simple trait definitions. - [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using From 7c57d6dd90c5e13d2dbf93f826a7cd64241b07c2 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 22 May 2024 11:32:46 -0500 Subject: [PATCH 0668/2019] [External] [stdlib] Fix Tuple.__contains__ (#40417) [External] [stdlib] Fix `Tuple.__contains__` Various follow-ups on https://github.com/modularml/mojo/pull/2709. This also ensures `Tuple.__contains__` works in the parameter domain. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2782 MODULAR_ORIG_COMMIT_REV_ID: 88a3d728bbda276c34ee485d3ee7a57c147ad403 --- stdlib/src/builtin/tuple.mojo | 50 ++++++++++++++++++++--------- stdlib/test/builtin/test_tuple.mojo | 46 ++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 993f9bfefe..45c150a092 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -182,34 +182,54 @@ struct Tuple[*element_types: Movable](Sized, Movable): return rebind[T](self[i]) @always_inline("nodebug") - fn __contains__[ - RHS_T: ComparableCollectionElement - ](inout self, owned rhs: RHS_T) -> Bool: + fn __contains__[T: ComparableCollectionElement](self, value: T) -> Bool: """Verify if a given value is present in the tuple. ```mojo var x = Tuple(1,2,True) if 1 in x: print("x contains 1") ``` + Args: - rhs: The value to find. + value: The value to find. Parameters: - RHS_T: The inferred type of RHS. Must implement the + T: The type of the value argument. Must implement the trait `ComparableCollectionElement`. Returns: True if the value is contained in the tuple, False otherwise. """ - var result = False @parameter - fn SingleIteration[Index: Int](): - if _type_is_eq[RHS_T, element_types[Index.value]](): - var tmp = rhs - result |= self.get[Index, RHS_T]().__eq__(tmp) - _ = tmp - - unroll[SingleIteration, len(VariadicList(element_types))]() - _ = rhs^ - return result + fn T_in_ts() -> Bool: + @parameter + for i in range(len(VariadicList(element_types))): + + @parameter + if _type_is_eq[element_types[i], T](): + return True + return False + + @parameter + if not T_in_ts(): + return False + + @parameter + for i in range(len(VariadicList(element_types))): + + @parameter + if _type_is_eq[T, element_types[i]](): + var tmp_ref = self.__refitem__[i]() + var tmp = rebind[ + Reference[ + T, + tmp_ref.is_mutable, + tmp_ref.lifetime, + tmp_ref.address_space, + ] + ](tmp_ref) + if tmp[].__eq__(value): + return True + + return False diff --git a/stdlib/test/builtin/test_tuple.mojo b/stdlib/test/builtin/test_tuple.mojo index 5c2f92a75e..1412b8ea72 100644 --- a/stdlib/test/builtin/test_tuple.mojo +++ b/stdlib/test/builtin/test_tuple.mojo @@ -56,11 +56,57 @@ def test_tuple_contains(): var d = (123, True, String("Mojo is awesome")) assert_true(String("Mojo is awesome") in d) + assert_false("Mojo is awesome" in d) assert_true(d.__contains__(String("Mojo is awesome"))) assert_false(String("Hello world") in d) assert_false(d.__contains__(String("Hello world"))) + alias a_alias = (123, True, "Mojo is awesome") + + assert_true("Mojo is awesome" in a_alias) + assert_true(a_alias.__contains__("Mojo is awesome")) + + assert_false("Hello world" in a_alias) + assert_false(a_alias.__contains__("Hello world")) + + assert_true(123 in a_alias) + assert_true(a_alias.__contains__(123)) + + assert_true(True in a_alias) + assert_true(a_alias.__contains__(True)) + + assert_false(False in a_alias) + assert_false(a_alias.__contains__(False)) + + assert_false(a_alias.__contains__(1)) + assert_false(a_alias.__contains__(0)) + assert_false(1 in a_alias) + assert_false(0 in a_alias) + + alias b_alias = (False, True) + assert_true(True in b_alias) + assert_true(b_alias.__contains__(True)) + assert_true(False in b_alias) + assert_true(b_alias.__contains__(False)) + assert_false(b_alias.__contains__(1)) + assert_false(b_alias.__contains__(0)) + + alias c_alias = (1, 0) + assert_false(c_alias.__contains__(True)) + assert_false(c_alias.__contains__(False)) + assert_false(True in c_alias) + assert_false(False in c_alias) + + alias d_alias = (123, True, String("Mojo is awesome")) + + assert_true(String("Mojo is awesome") in d_alias) + assert_false("Mojo is awesome" in d_alias) + assert_true(d_alias.__contains__(String("Mojo is awesome"))) + + assert_false(String("Hello world") in d_alias) + assert_false(d_alias.__contains__(String("Hello world"))) + def main(): # FIXME(MSTDL-516) From 901c9446b3cd1ac9895cbad641c0125c5ebd71f5 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 22 May 2024 13:10:31 -0400 Subject: [PATCH 0669/2019] [stdlib] Move more uses of `StringLiteral` to compile time This will help with making in non-materializable. MODULAR_ORIG_COMMIT_REV_ID: 019ea1e527650c7b969e72263cb3f417e34d7816 --- stdlib/src/builtin/io.mojo | 16 ++++++++-------- stdlib/src/builtin/simd.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 59c6ffef45..29fb8a1c1a 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -180,7 +180,7 @@ fn _snprintf[ @no_inline fn _snprintf_scalar[ type: DType -](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type],) -> Int: +](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type]) -> Int: alias format = _get_dtype_printf_format[type]() @parameter @@ -264,7 +264,7 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): @parameter if type == DType.bool: - _put("True") if x else _put("False") + _put["True"]() if x else _put["False"]() elif type.is_integral() or type == DType.address: _printf[format](x) elif type.is_floating_point(): @@ -295,14 +295,14 @@ fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): if simd_width == 1: _put_simd_scalar(x[0]) elif type.is_integral(): - _put("[") + _put["["]() @parameter for i in range(simd_width): _put_simd_scalar(x[i]) if i != simd_width - 1: - _put(", ") - _put("]") + _put[", "]() + _put["]"]() else: _put(str(x)) @@ -346,7 +346,7 @@ fn _put(x: StringRef, file: FileDescriptor = stdout): @no_inline -fn _put(x: StringLiteral, file: FileDescriptor = stdout): +fn _put[x: StringLiteral](file: FileDescriptor = stdout): _put(StringRef(x), file=file) @@ -403,11 +403,11 @@ fn _print[ @parameter if i < values.__len__() - 1: - _put(sep, file=file) + _put(StringRef(sep), file=file) values.each_idx[print_with_separator]() - _put(end, file=file) + _put(StringRef(end), file=file) if flush: _flush(file=file) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 456aed0d9f..ff5cfea520 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -42,7 +42,7 @@ from utils._visualizers import lldb_formatter_wrapping_type from utils import InlineArray from .dtype import _integral_type_of, _get_dtype_printf_format -from .io import _snprintf_scalar, _snprintf, _printf, _print_fmt +from .io import _snprintf_scalar, _printf, _print_fmt from .string import _calc_initial_buffer_size, _calc_format_buffer_size # ===------------------------------------------------------------------------===# diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index e6b351f9a5..3f977f365b 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -18,8 +18,8 @@ from utils import Span fn test_string_literal_byte_slice() raises: - var string: StringLiteral = "Hello" - var slice = string.as_bytes_slice() + alias string: StringLiteral = "Hello" + alias slice = string.as_bytes_slice() assert_equal(len(slice), 5) assert_equal(slice[0][], ord("H")) @@ -114,13 +114,13 @@ fn test_string_byte_slice() raises: fn test_heap_string_from_string_slice() raises: - var string_lit: StringLiteral = "Hello" + alias string_lit: StringLiteral = "Hello" - var static_str: StringSlice[ + alias static_str: StringSlice[ False, ImmutableStaticLifetime ] = string_lit.as_string_slice() - var heap_string = String(static_str) + alias heap_string = String(static_str) assert_equal(heap_string, "Hello") From 0b6e645fd47259e7bd0fd4aee07ac2cce8d874da Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 22 May 2024 11:27:15 -0600 Subject: [PATCH 0670/2019] [stdlib] Add additional comp-time tuple contains test As a follow-up on https://github.com/modularml/mojo/pull/2782, add an explicit test for calling `Tuple.contains` in an alias statement. MODULAR_ORIG_COMMIT_REV_ID: 9e65df61388f063c0f4477705aae92f5806089b9 --- stdlib/test/builtin/test_tuple.mojo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/test/builtin/test_tuple.mojo b/stdlib/test/builtin/test_tuple.mojo index 1412b8ea72..ba9c0aaa82 100644 --- a/stdlib/test/builtin/test_tuple.mojo +++ b/stdlib/test/builtin/test_tuple.mojo @@ -99,6 +99,9 @@ def test_tuple_contains(): assert_false(False in c_alias) alias d_alias = (123, True, String("Mojo is awesome")) + # Ensure `contains` itself works in comp-time domain + alias ok = 123 in d_alias + assert_true(ok) assert_true(String("Mojo is awesome") in d_alias) assert_false("Mojo is awesome" in d_alias) From 840217dc5b74c08689dfe5b93e8a581a7570f5ff Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 22 May 2024 11:05:50 -0700 Subject: [PATCH 0671/2019] Revert "[mojo-lang] Fix mojo-build-server breakage" (#40432) Reverts modularml/modular#40419 MODULAR_ORIG_COMMIT_REV_ID: 980ceab597ad172306a5dab64cc8fc8a0e3fb873 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index fd4de8a6d2..b5fd087a7b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -489,6 +489,8 @@ what we publish. ### 🛠️ Fixed +- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential + variant crashing the compiler. - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on simple trait definitions. - [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using From 4939be24b5884b8b469a612e82ce2ca3aed0a178 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 22 May 2024 13:12:51 -0500 Subject: [PATCH 0672/2019] [External] [stdlib] Fix: Add null terminator to the buffer inside `__str__` method (#40421) [External] [stdlib] Fix: Add null terminator to the buffer inside `__str__` method String really has a hard time working without this null terminator and I feel this PR is a step towards fixing https://github.com/modularml/mojo/issues/2687 without requirering controversial changes like the ones in https://github.com/modularml/mojo/pull/2691. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2787 MODULAR_ORIG_COMMIT_REV_ID: 95f438ebbb6e076fe6ffb79debf7cd4fd70248de --- stdlib/src/collections/dict.mojo | 4 +++- stdlib/src/collections/list.mojo | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 24ab0b7dd7..9d348ecc04 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -675,7 +675,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( A string representation of the Dict. """ var minimum_capacity = self._minimum_size_of_string_representation() - var result = String(List[UInt8](capacity=minimum_capacity)) + var string_buffer = List[UInt8](capacity=minimum_capacity) + string_buffer.append(0) # Null terminator + var result = String(string_buffer^) result += "{" var i = 0 diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index e2270e622a..d7f83545a2 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -364,7 +364,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): + len(self) * 3 # str(x) and ", " - 2 # remove the last ", " ) - var result = String(List[UInt8](capacity=minimum_capacity)) + var string_buffer = List[UInt8](capacity=minimum_capacity) + string_buffer.append(0) # Null terminator + var result = String(string_buffer^) result += "[" for i in range(len(self)): result += repr(self[i]) From ee228ab8e3260809cf02b46fc803d00595c27775 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 22 May 2024 13:25:48 -0500 Subject: [PATCH 0673/2019] [External] [stdlib] Add `startswith()` and `endswith()` to `StringRef` (#40383) [External] [stdlib] Add `startswith()` and `endswith()` to `StringRef` Also delegates `String` functions to `StringRef` ORIGINAL_AUTHOR=Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2710 Co-authored-by: Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> Closes modularml/mojo#2710 MODULAR_ORIG_COMMIT_REV_ID: cb7fd59c23fb5c655a1c0a1d6b5f7601306f6953 --- docs/changelog.md | 3 +++ stdlib/src/builtin/string.mojo | 22 ++++++++++++------- stdlib/src/utils/stringref.mojo | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b5fd087a7b..61aa65e239 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -367,6 +367,9 @@ what we publish. - Added `clear` method to `Dict`. ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) +- `StringRef` now implements `startswith()` and `endswith()`. + ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 2025ad8544..999f6893b5 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1626,8 +1626,13 @@ struct String( True if the self[start:end] is prefixed by the input prefix. """ if end == -1: - return self.find(prefix, start) == start - return self[start:end].startswith(prefix) + return StringRef( + self.unsafe_ptr() + start, len(self) - start + ).startswith(prefix._strref_dangerous()) + + return StringRef(self.unsafe_ptr() + start, end - start).startswith( + prefix._strref_dangerous() + ) fn endswith(self, suffix: String, start: Int = 0, end: Int = -1) -> Bool: """Checks if the string end with the specified suffix between start @@ -1642,8 +1647,13 @@ struct String( True if the self[start:end] is suffixed by the input suffix. """ if end == -1: - return self._endswith_impl(suffix, start) - return self[start:end]._endswith_impl(suffix) + return StringRef( + self.unsafe_ptr() + start, len(self) - start + ).endswith(suffix._strref_dangerous()) + + return StringRef(self.unsafe_ptr() + start, end - start).endswith( + suffix._strref_dangerous() + ) fn removeprefix(self, prefix: String, /) -> String: """If the string starts with the prefix string, return `string[len(prefix):]`. @@ -1687,10 +1697,6 @@ struct String( return self[: -len(suffix)] return self - @always_inline - fn _endswith_impl(self, suffix: String, start: Int = 0) -> Bool: - return self.rfind(suffix, start) + len(suffix) == len(self) - fn __int__(self) raises -> Int: """Parses the given string as a base-10 integer and returns that value. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index ec50253d9b..10fa4d4abf 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -506,6 +506,45 @@ struct StringRef( end -= 1 return StringRef(ptr + start, end - start) + fn startswith( + self, prefix: StringRef, start: Int = 0, end: Int = -1 + ) -> Bool: + """Checks if the StringRef starts with the specified prefix between start + and end positions. Returns True if found and False otherwise. + + Args: + prefix: The prefix to check. + start: The start offset from which to check. + end: The end offset from which to check. + + Returns: + True if the self[start:end] is prefixed by the input prefix. + """ + if end == -1: + return self.find(prefix, start) == start + return StringRef(self.unsafe_ptr() + start, end - start).startswith( + prefix + ) + + fn endswith(self, suffix: StringRef, start: Int = 0, end: Int = -1) -> Bool: + """Checks if the StringRef end with the specified suffix between start + and end positions. Returns True if found and False otherwise. + + Args: + suffix: The suffix to check. + start: The start offset from which to check. + end: The end offset from which to check. + + Returns: + True if the self[start:end] is suffixed by the input suffix. + """ + + if end == -1: + return self.rfind(suffix, start) + len(suffix) == len(self) + return StringRef(self.unsafe_ptr() + start, end - start).endswith( + suffix + ) + # ===----------------------------------------------------------------------===# # Utilities From b6bec1f5c2f07a26ee15048917551d8a0fd76132 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 22 May 2024 14:57:36 -0400 Subject: [PATCH 0674/2019] [stdlib] Remove `Indexer` trait from `SIMD` This removes `__index__` from `SIMD` since without conditional conformance, this will cause overload resolution problems when `Indexer` types can implicitly convert to `Int` (because constraints don't prevent float simd vectors from being convertible to `Int` via `__index__`). MODULAR_ORIG_COMMIT_REV_ID: e345e000aac62d53fb93406ed7cbee37d6a140fd --- docs/changelog.md | 3 +++ stdlib/src/base64/base64.mojo | 4 ++-- stdlib/src/builtin/bin.mojo | 2 +- stdlib/src/builtin/simd.mojo | 17 ----------------- stdlib/test/builtin/test_list.mojo | 1 - stdlib/test/builtin/test_range.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 10 +--------- stdlib/test/builtin/test_slice.mojo | 2 +- stdlib/test/builtin/test_string.mojo | 2 +- stdlib/test/builtin/test_stringref.mojo | 2 +- stdlib/test/collections/test_inline_list.mojo | 2 +- stdlib/test/collections/test_list.mojo | 3 +-- stdlib/test/collections/test_vector.mojo | 3 +-- stdlib/test/memory/test_memory.mojo | 2 +- stdlib/test/memory/test_unsafepointer.mojo | 2 +- stdlib/test/utils/test_span.mojo | 2 +- 16 files changed, 17 insertions(+), 42 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 61aa65e239..e74a0c6b1d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -490,6 +490,9 @@ what we publish. - The `tensor.random` module has been removed. The same functionality is now accessible via the `Tensor.rand` and `Tensor.randn` static methods. +- The builtin `SIMD` struct no longer conforms to `Indexer`; users must + explicitly cast `Scalar` values using `int`. + ### 🛠️ Fixed - [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 337d0666cc..a2ae6fe601 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -183,8 +183,8 @@ fn b16encode(str: String) -> String: var str_byte = str_bytes(i) var hi = str_byte >> 4 var lo = str_byte & 0b1111 - out.append(b16chars[hi]) - out.append(b16chars[lo]) + out.append(b16chars[int(hi)]) + out.append(b16chars[int(lo)]) out.append(0) diff --git a/stdlib/src/builtin/bin.mojo b/stdlib/src/builtin/bin.mojo index f15cd5c8e9..2c05dc1751 100644 --- a/stdlib/src/builtin/bin.mojo +++ b/stdlib/src/builtin/bin.mojo @@ -28,7 +28,7 @@ fn bin(b: Scalar[DType.bool], /) -> String: Returns: The binary string representation of b. """ - return bin(index(b)) + return bin(index(int(b))) fn bin[type: DType](num: Scalar[type], /) -> String: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ff5cfea520..d0224255fb 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -139,7 +139,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Sized, Stringable, Truncable, - Indexer, ): """Represents a small vector that is backed by a hardware vector element. @@ -180,22 +179,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( _simd_construction_checks[type, size]() self = _unchecked_zero[type, size]() - @always_inline("nodebug") - fn __index__(self) -> Int: - """Returns the value as an int if it is an integral value. - - Constraints: - Must be a scalar integral value. - - Returns: - The value as an integer. - """ - constrained[ - type.is_integral() or type.is_bool(), - "expected integral or bool type", - ]() - return self.__int__() - @always_inline("nodebug") fn __init__(inout self, value: SIMD[DType.float64, 1]): """Initializes the SIMD vector with a float. diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 5718d15483..52e7a8801d 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -26,7 +26,6 @@ fn test_variadic_list() raises: assert_equal(nums[1], 8) assert_equal(nums[2], 6) assert_equal(nums[True], 8) - assert_equal(nums[Int32(0)], 5) assert_equal(len(nums), 3) diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 77cd4c2355..2b2dfaae93 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -118,7 +118,7 @@ def test_range_reversed(): def test_indexing(): var r = range(10) assert_equal(r[True], 1) - assert_equal(r[Int8(4)], 4) + assert_equal(r[int(4)], 4) assert_equal(r[3], 3) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 2cad8efe71..7d09811add 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1061,17 +1061,10 @@ def test_min_max_clamp(): assert_equal(i.clamp(-7, 4), I(-7, -5, 4, 4)) -def test_indexer(): - assert_equal(5, Int8(5).__index__()) - assert_equal(56, UInt32(56).__index__()) - assert_equal(1, Scalar[DType.bool](True).__index__()) - assert_equal(0, Scalar[DType.bool](False).__index__()) - - def test_indexing(): var s = SIMD[DType.int32, 4](1, 2, 3, 4) assert_equal(s[False], 1) - assert_equal(s[Int32(2)], 3) + assert_equal(s[int(2)], 3) assert_equal(s[3], 4) @@ -1332,7 +1325,6 @@ def main(): test_floor() test_floordiv() test_iadd() - test_indexer() test_indexing() test_insert() test_interleave() diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 06ddcbbfbb..6aabc2fdbd 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -85,7 +85,7 @@ def test_slice_stringable(): def test_indexing(): var s = slice(1, 10) assert_equal(s[True], 2) - assert_equal(s[UInt64(0)], 1) + assert_equal(s[int(0)], 1) assert_equal(s[2], 3) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 31b89a962d..6cba68983d 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -902,7 +902,7 @@ def test_string_mul(): def test_indexing(): a = String("abc") assert_equal(a[False], "a") - assert_equal(a[Int16(1)], "b") + assert_equal(a[int(1)], "b") assert_equal(a[2], "c") diff --git a/stdlib/test/builtin/test_stringref.mojo b/stdlib/test/builtin/test_stringref.mojo index d75209e90c..c8859069ee 100644 --- a/stdlib/test/builtin/test_stringref.mojo +++ b/stdlib/test/builtin/test_stringref.mojo @@ -78,7 +78,7 @@ def test_intable(): def test_indexing(): a = StringRef("abc") assert_equal(a[False], "a") - assert_equal(a[Int16(1)], "b") + assert_equal(a[int(1)], "b") assert_equal(a[0], "a") diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index 70ef61edcf..8bb0dbae36 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -150,7 +150,7 @@ def test_indexing(): list.append(i) assert_equal(list[True], 1) - assert_equal(list[Int32(4)], 4) + assert_equal(list[int(4)], 4) assert_equal(list[0], 0) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index ae0e6851f1..3bff4f4981 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -780,8 +780,7 @@ def test_list_init_span(): def test_indexing(): var l = List[Int](1, 2, 3) - assert_equal(l[Int8(1)], 2) - assert_equal(l[UInt64(2)], 3) + assert_equal(l[int(1)], 2) assert_equal(l[False], 1) assert_equal(l[True], 2) assert_equal(l[2], 3) diff --git a/stdlib/test/collections/test_vector.mojo b/stdlib/test/collections/test_vector.mojo index a715739965..347d90ca41 100644 --- a/stdlib/test/collections/test_vector.mojo +++ b/stdlib/test/collections/test_vector.mojo @@ -113,9 +113,8 @@ def test_indexing(): var vector = InlinedFixedVector[Int](10) for i in range(5): vector.append(i) - assert_equal(1, vector[Int16(1)]) + assert_equal(0, vector[int(0)]) assert_equal(1, vector[True]) - assert_equal(1, vector[Scalar[DType.bool](True)]) assert_equal(2, vector[2]) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 4cb7d4815c..8944f15e13 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -514,7 +514,7 @@ def test_indexing(): ptr[i] = i assert_equal(ptr[True], 1) - assert_equal(ptr[Int32(2)], 2) + assert_equal(ptr[int(2)], 2) assert_equal(ptr[1], 1) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index c22ed76b34..82df00a3f5 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -177,7 +177,7 @@ def test_indexing(): ptr[i] = i assert_equal(ptr[False], 0) - assert_equal(ptr[Int32(1)], 1) + assert_equal(ptr[int(1)], 1) assert_equal(ptr[3], 3) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index bb41838959..d264fc3bfa 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -114,7 +114,7 @@ def test_indexing(): var l = InlineArray[Int, 7](1, 2, 3, 4, 5, 6, 7) var s = Span(l) assert_equal(s[True][], 2) - assert_equal(s[Int32(0)][], 1) + assert_equal(s[int(0)][], 1) assert_equal(s[3][], 4) From 176c14e0334600aa1416f59c5280beb58dc8db3f Mon Sep 17 00:00:00 2001 From: Wah Loon Keng Date: Wed, 22 May 2024 14:01:32 -0500 Subject: [PATCH 0675/2019] [External] [Docs] correct output, fix indent in Heterogeneous variadic arguments (#40435) [External] [Docs] correct output, fix indent in Heterogeneous variadic arguments The current [Heterogeneous variadic arguments](https://docs.modular.com/mojo/manual/functions#heterogeneous-variadic-arguments) (see screenshot) - last example has wrong output (`Bob0` should be `Bob`), and indentation of the example code is also off (had to edit after copying into Mojo playground) Screenshot 2024-05-12 at 9 31 51 AM Co-authored-by: Wah Loon Keng Closes modularml/mojo#2741 MODULAR_ORIG_COMMIT_REV_ID: c06231809a24351548c4967f66faf65d9204e859 --- docs/manual/functions.ipynb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 68a63d222a..2f97a69439 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -513,23 +513,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "Bob0\n" + "Bob\n" ] } ], "source": [ - " fn print_string(s: String):\n", + "fn print_string(s: String):\n", " print(s, end=\"\")\n", "\n", - " fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):\n", - " print_string(str(first))\n", + "fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):\n", + " print_string(str(first))\n", "\n", - " @parameter\n", - " fn print_elt[T: Stringable](a: T):\n", - " print_string(\" \")\n", - " print_string(a)\n", - " rest.each[print_elt]()\n", - " print_many(\"Bob\")" + " @parameter\n", + " fn print_elt[T: Stringable](a: T):\n", + " print_string(\" \")\n", + " print_string(a)\n", + " rest.each[print_elt]()\n", + "print_many(\"Bob\")" ] }, { From dd4a674195a4f0de35d741931fb81d38f70dce7d Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 22 May 2024 15:04:05 -0400 Subject: [PATCH 0676/2019] [stdlib] Enabled `test_tuple`. The test is fixed by https://github.com/modularml/mojo/pull/2782. MODULAR_ORIG_COMMIT_REV_ID: aac3c3c850d5f784ff622adb0b0f524d22c8e464 --- stdlib/test/builtin/test_tuple.mojo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/test/builtin/test_tuple.mojo b/stdlib/test/builtin/test_tuple.mojo index ba9c0aaa82..a15234b7f3 100644 --- a/stdlib/test/builtin/test_tuple.mojo +++ b/stdlib/test/builtin/test_tuple.mojo @@ -112,6 +112,4 @@ def test_tuple_contains(): def main(): - # FIXME(MSTDL-516) - if not os_is_macos(): - test_tuple_contains() + test_tuple_contains() From 3b26533bf3d541e97d6b40bf3a77b2dd5d9ad665 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 22 May 2024 16:12:14 -0400 Subject: [PATCH 0677/2019] [stdlib] Make `Indexer` implicitly convertible to `Int` Allowing types conforming to `Indexer` to be used by common `__getitem__` et al. implementations. MODULAR_ORIG_COMMIT_REV_ID: 33d2b176a61c9fbb48c43b79d02a41f4fa207326 --- docs/changelog.md | 19 +++++++++++++++++++ stdlib/src/builtin/int.mojo | 12 ++++++++++++ stdlib/src/os/atomic.mojo | 9 --------- stdlib/src/python/object.mojo | 8 +++++++- stdlib/src/sys/intrinsics.mojo | 21 ++++++++++++--------- 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e74a0c6b1d..12732b12f3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -332,6 +332,25 @@ what we publish. ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + Types conforming to the `Indexer` trait are implicitly convertible to Int. + This means you can write generic APIs that take `Int` instead of making them + take a generic type that conforms to `Indexer`, e.g. + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + @value + struct Incrementer: + fn __getitem__(self, idx: Int) -> Int: + return idx + 1 + + var a = Incrementer() + print(a[AlwaysZero()]) # works and prints 1 + ``` + - `StringRef` now implements `strip()` which can be used to remove leading and trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) by [@fknfilewalker](https://github.com/fknfilewalker)) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index a3bb456068..1f84eef7c6 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -339,6 +339,18 @@ struct Int( """ self = value.__int__() + @always_inline("nodebug") + fn __init__[IndexerTy: Indexer](inout self, value: IndexerTy): + """Construct Int from the given Indexer value. + + Parameters: + IndexerTy: A type conforming to Indexer. + + Args: + value: The init value. + """ + self = value.__index__() + @always_inline("nodebug") fn __int__(self) -> Int: """Gets the integral value (this is an identity function for Int). diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 2123c09f89..c3d8dddfae 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -48,15 +48,6 @@ struct Atomic[type: DType]: """ self.value = value - @always_inline - fn __init__(inout self, value: Int): - """Constructs a new atomic value. - - Args: - value: Initial value represented as `mlir.index` type. - """ - self.value = value - @always_inline fn load(inout self) -> Scalar[type]: """Loads the current value from the atomic. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 658a7912b5..0d4ce29e09 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,7 +101,13 @@ struct _PyIter(Sized): @register_passable struct PythonObject( - Intable, Stringable, SizedRaising, Boolable, CollectionElement, KeyElement + Boolable, + CollectionElement, + Indexer, + Intable, + KeyElement, + SizedRaising, + Stringable, ): """A Python object.""" diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 5184bbbcd2..327e87a93a 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1352,12 +1352,13 @@ fn strided_load[ if simd_width == 1: return addr.load() if mask else Scalar[type]() + alias IndexTy = SIMD[DType.index, simd_width] var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", - SIMD[DType.index, simd_width], - has_side_effect=False, + "llvm.experimental.stepvector", IndexTy, has_side_effect=False ]() - var offset = (int(addr) + stride * iota * sizeof[type]()) + var offset = IndexTy(int(addr)) + IndexTy(stride) * iota * IndexTy( + sizeof[type]() + ) var passthrough = SIMD[type, simd_width]() return gather[type, simd_width]( offset.cast[DType.address](), mask, passthrough @@ -1433,12 +1434,14 @@ fn strided_store[ addr.store(value[0]) return + alias IndexTy = SIMD[DType.index, simd_width] var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", - SIMD[DType.index, simd_width], - has_side_effect=False, + "llvm.experimental.stepvector", IndexTy, has_side_effect=False ]() - var offset = int(addr) + stride * iota * sizeof[type]() + var offset = IndexTy(int(addr)) + IndexTy(stride) * iota * IndexTy( + sizeof[type]() + ) + scatter[type, simd_width](value, offset.cast[DType.address](), mask) @@ -1471,7 +1474,7 @@ fn strided_store[ addr.store(value[0]) return - return strided_store[type, simd_width](value, addr, stride, True) + strided_store[type, simd_width](value, addr, stride, True) # ===-------------------------------------------------------------------===# From 892949fa11b5e1e6cb1f4f835e576d766128cb31 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 22 May 2024 16:53:02 -0400 Subject: [PATCH 0678/2019] [stdlib] Rename `InlinedString` to `InlineString` To be consistent with `InlineArray` and `InlineList`. MODULAR_ORIG_COMMIT_REV_ID: 485fd2ccbe341dc914b1a805b4d85b1a28db0e43 --- stdlib/src/utils/__init__.mojo | 2 +- ...inlined_string.mojo => inline_string.mojo} | 10 +++--- stdlib/test/utils/test_format.mojo | 2 +- stdlib/test/utils/test_inlined_string.mojo | 32 +++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) rename stdlib/src/utils/{inlined_string.mojo => inline_string.mojo} (98%) diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 6bbddb24eb..459696c36f 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -17,7 +17,7 @@ from .index import ( Index, product, ) -from .inlined_string import InlinedString +from .inline_string import InlineString from .loop import unroll from .span import Span from .static_tuple import StaticTuple, InlineArray diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inline_string.mojo similarity index 98% rename from stdlib/src/utils/inlined_string.mojo rename to stdlib/src/utils/inline_string.mojo index fb814d8829..be9d618783 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -26,12 +26,12 @@ from utils._format import ToFormatter # ===----------------------------------------------------------------------===# -# InlinedString +# InlineString # ===----------------------------------------------------------------------===# @value -struct InlinedString(Sized, Stringable, CollectionElement): +struct InlineString(Sized, Stringable, CollectionElement): """A string that performs small-string optimization to avoid heap allocations for short strings. """ @@ -59,7 +59,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): self._storage = Self.Layout(fixed^) fn __init__(inout self, literal: StringLiteral): - """Constructs a InlinedString value given a string literal. + """Constructs a InlineString value given a string literal. Args: literal: The input constant string. @@ -129,7 +129,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): self._storage[_FixedString[Self.SMALL_CAP]] += strref except e: abort( - "unreachable: InlinedString append to FixedString failed: " + "unreachable: InlineString append to FixedString failed: " + str(e), ) else: @@ -191,7 +191,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): string += other._strref_dangerous() return string - fn __add__(self, other: InlinedString) -> Self: + fn __add__(self, other: InlineString) -> Self: """Construct a string by appending another string at the end of this string. Args: diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 4f614b378f..dcf84fda6e 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -13,7 +13,7 @@ # RUN: %mojo -debug-level full %s from utils._format import Formattable, write_to, Formatter -from utils.inlined_string import _FixedString +from utils.inline_string import _FixedString from testing import assert_equal diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index d7354ddbe9..6d707b30ef 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -15,8 +15,8 @@ from testing import assert_equal, assert_true -from utils.inlined_string import _FixedString -from utils import InlinedString +from utils.inline_string import _FixedString +from utils import InlineString def main(): @@ -86,8 +86,8 @@ def test_small_string_construction(): # Test construction from StringLiteral # ================================== - var s1 = InlinedString("hello world") - var s2 = InlinedString("the quick brown fox jumped over the lazy dog") + var s1 = InlineString("hello world") + var s2 = InlineString("the quick brown fox jumped over the lazy dog") assert_true(s1._is_small()) assert_true(not s2._is_small()) @@ -102,9 +102,9 @@ def test_small_string_construction(): var heap_s1 = String("hello") var heap_s1_addr = int(heap_s1.unsafe_ptr()) - var s3 = InlinedString(heap_s1^) + var s3 = InlineString(heap_s1^) - # Test that a InlinedString constructed from a String uses the same + # Test that a InlineString constructed from a String uses the same # allocation as the original String (even if the String size is small # enough to fit inline). assert_equal(int(s3.unsafe_ptr()), heap_s1_addr) @@ -112,10 +112,10 @@ def test_small_string_construction(): def test_small_string_iadd(): # ================================== - # Test appending StringLiteral to InlinedString + # Test appending StringLiteral to InlineString # ================================== - var s1 = InlinedString("") + var s1 = InlineString("") assert_equal(len(s1), 0) assert_true(s1._is_small()) @@ -150,10 +150,10 @@ def test_small_string_iadd(): assert_equal(str(s1), "Hello world, how's it going? The End.") # ================================== - # Test appending String to InlinedString + # Test appending String to InlineString # ================================== - var s2 = InlinedString("") + var s2 = InlineString("") s2 += String("Hello, World!") assert_equal(str(s2), "Hello, World!") @@ -162,28 +162,28 @@ def test_small_string_iadd(): def test_small_string_add(): # - # Test InlinedString + StringLiteral + # Test InlineString + StringLiteral # - var s1: InlinedString = InlinedString("hello") + " world" + var s1: InlineString = InlineString("hello") + " world" assert_equal(str(s1), "hello world") assert_equal(len(s1), "11") # - # Test InlinedString + InlinedString + # Test InlineString + InlineString # - var s2: InlinedString = InlinedString("hello") + InlinedString(" world") + var s2: InlineString = InlineString("hello") + InlineString(" world") assert_equal(str(s2), "hello world") assert_equal(len(s2), "11") # - # Test InlinedString + String + # Test InlineString + String # - var s3: InlinedString = InlinedString("hello") + String(" world") + var s3: InlineString = InlineString("hello") + String(" world") assert_equal(str(s3), "hello world") assert_equal(len(s3), "11") From 10ae49b674047b3e7e29bb6fedb575ce7da7a6ea Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 22 May 2024 18:17:49 -0400 Subject: [PATCH 0679/2019] [stdlib] Add changelog for `InlinedString` rename MODULAR_ORIG_COMMIT_REV_ID: a6f4831c7f291598ef5751cec1198c780c3a4ff3 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 12732b12f3..ec8fd25ce1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -461,6 +461,9 @@ what we publish. `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the `math` module. +- `InlinedString` has been renamed to `InlineString` to be consistent with other + types. + ### ❌ Removed - The `@unroll` decorator has been deprecated and removed. The decorator was From 7d4f517603d4d78bac9bb24c24892d199ac53655 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 22 May 2024 16:46:05 -0600 Subject: [PATCH 0680/2019] [stdlib] Move `test_stringref` to `test/utils` Move `test_stringref.mojo` from `builtin` to `utils` test to mirror its source structure since `StringRef` is in the `utils` directory, not `builtin`. MODULAR_ORIG_COMMIT_REV_ID: 00aaee5d5124c66f2aaa0fc145b02688279e54af --- stdlib/test/{builtin => utils}/test_stringref.mojo | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename stdlib/test/{builtin => utils}/test_stringref.mojo (100%) diff --git a/stdlib/test/builtin/test_stringref.mojo b/stdlib/test/utils/test_stringref.mojo similarity index 100% rename from stdlib/test/builtin/test_stringref.mojo rename to stdlib/test/utils/test_stringref.mojo From 33b8c6b80e95d0b4f681f6ba6760983f970c30a2 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 22 May 2024 17:44:18 -0700 Subject: [PATCH 0681/2019] [mojo-lang] Change the syntax of inferred parameters This PR changes the syntax of inferred parameters. Previously, we used a keyword to specify that a parameter is inferred. However, this had several issues: 1. It takes up a keyword and the syntax space for conventions, but it specified a passing kind, not a convention. 2. Python `/` and `*` syntax have the benefit that they provide additional structural guarantees about where the parameters can be in the parameter list. This breaks from established Python syntax. We chose to switch this to use a new marker kind, `//`. Where all parameters to the left of this marker are inferred. This has a kind of symmetry with existing syntax: ```mojo fn foo[a: Int, //, b: Int, /, c: Int, *d: Int, *, e: Int, **f: Int](): pass ``` MODULAR_ORIG_COMMIT_REV_ID: d070cf7c836dff997c5de9326b365e8cf74a380a --- docs/changelog.md | 11 ++++++----- stdlib/src/memory/memory.mojo | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ec8fd25ce1..f624259d45 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -35,14 +35,15 @@ what we publish. return a `_StridedRangeIterator`, meaning the induction variables must be `Int`. The intention is to lift these restrictions in the future. -- Mojo added support for the `inferred` parameter convention. `inferred` - parameters must appear at the beginning of the parameter list and cannot be - explicitly specified by the user. This allows programmers to define functions +- Mojo added support for the inferred parameters. Inferred parameters must + appear at the beginning of the parameter list and cannot be explicitly + specified by the user. They are declared to the left of a `//` marker, much + like positional-only parameters. This allows programmers to define functions with dependent parameters to be called without the caller specifying all the necessary parameters. For example: ```mojo - fn parameter_simd[inferred dt: DType, value: Scalar[dt]](): + fn parameter_simd[dt: DType, //, value: Scalar[dt]](): print(value) fn call_it(): @@ -56,7 +57,7 @@ what we publish. This also works with structs. For example: ```mojo - struct ScalarContainer[inferred dt: DType, value: Scalar[dt]]: + struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: pass fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index b83d8271a6..0ef2a8680b 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -311,7 +311,7 @@ fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): @always_inline fn memcpy[ - inferred dtype: DType + dtype: DType, // ](*, dest: UnsafePointer[Scalar[dtype]], src: __type_of(dest), count: Int): """Copies a memory area. From 17774568bcbed63008c0b39f7ffd37b4f18c7874 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 22 May 2024 18:41:51 -0700 Subject: [PATCH 0682/2019] Fix docsite build, with a period This is a bug with our website, but this is also a workaround to fix it. The widget that builds the stdlib module index page currently requires this sentence to end in a period. MODULAR_ORIG_COMMIT_REV_ID: be9c32611dc7f55bdd8fd495b5f1b3e923140c9a --- stdlib/src/builtin/bin.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/bin.mojo b/stdlib/src/builtin/bin.mojo index 2c05dc1751..8520ced2ce 100644 --- a/stdlib/src/builtin/bin.mojo +++ b/stdlib/src/builtin/bin.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements the `bin()` function +"""Implements the `bin()` function. These are Mojo built-ins, so you don't need to import them. """ From b73b9ba21cb478a0ef2d67d4a88aa96493562e03 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 22 May 2024 22:09:38 -0400 Subject: [PATCH 0683/2019] [stdlib] Remove most uses of `Indexer` Since `Indexer` types are now implicitly convertible to `Int`, we should prefer to just declare methods taking `Int` where possible. MODULAR_ORIG_COMMIT_REV_ID: e2ecdd3b3001fbf12724d06f1cda77c2814cdcb6 --- stdlib/src/builtin/bin.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 27 ++++++++----------------- stdlib/src/builtin/builtin_slice.mojo | 5 +---- stdlib/src/builtin/int.mojo | 2 ++ stdlib/src/builtin/range.mojo | 6 +++--- stdlib/src/builtin/simd.mojo | 25 ++++------------------- stdlib/src/builtin/string.mojo | 8 ++------ stdlib/src/collections/inline_list.mojo | 20 +++++++----------- stdlib/src/collections/list.mojo | 20 ++++++------------ stdlib/src/collections/vector.mojo | 20 ++++++------------ stdlib/src/memory/unsafe.mojo | 27 ++++++------------------- stdlib/src/memory/unsafe_pointer.mojo | 14 +++++-------- stdlib/src/utils/index.mojo | 16 ++++----------- stdlib/src/utils/span.mojo | 24 +++++++--------------- stdlib/src/utils/stringref.mojo | 7 ++----- 15 files changed, 64 insertions(+), 159 deletions(-) diff --git a/stdlib/src/builtin/bin.mojo b/stdlib/src/builtin/bin.mojo index 8520ced2ce..1cf6fb447a 100644 --- a/stdlib/src/builtin/bin.mojo +++ b/stdlib/src/builtin/bin.mojo @@ -28,7 +28,7 @@ fn bin(b: Scalar[DType.bool], /) -> String: Returns: The binary string representation of b. """ - return bin(index(int(b))) + return bin(int(b)) fn bin[type: DType](num: Scalar[type], /) -> String: diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 976feb9020..d25572f336 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -146,19 +146,16 @@ struct VariadicList[type: AnyRegType](Sized): return __mlir_op.`pop.variadic.size`(self.value) @always_inline - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> type: + fn __getitem__(self, idx: Int) -> type: """Gets a single element on the variadic list. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index of the element to access on the list. Returns: The element on the list corresponding to the given index. """ - return __mlir_op.`pop.variadic.get`(self.value, index(idx).value) + return __mlir_op.`pop.variadic.get`(self.value, idx.value) @always_inline fn __iter__(self) -> Self.IterType: @@ -361,14 +358,9 @@ struct VariadicListMem[ # TODO: Fix for loops + _VariadicListIter to support a __nextref__ protocol # allowing us to get rid of this and make foreach iteration clean. @always_inline - fn __getitem__[ - IndexerType: Indexer - ](self, idx: IndexerType) -> Self.reference_type: + fn __getitem__(self, idx: Int) -> Self.reference_type: """Gets a single element on the variadic list. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index of the element to access on the list. @@ -377,13 +369,13 @@ struct VariadicListMem[ given index. """ return Self.reference_type( - __mlir_op.`pop.variadic.get`(self.value, index(idx).value) + __mlir_op.`pop.variadic.get`(self.value, idx.value) ) @always_inline - fn __refitem__[ - IndexerType: Indexer - ](self, idx: IndexerType) -> Reference[ + fn __refitem__( + self, idx: Int + ) -> Reference[ element_type, Bool {value: elt_is_mutable}, _lit_lifetime_union[ @@ -399,9 +391,6 @@ struct VariadicListMem[ ]: """Gets a single element on the variadic list. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index of the element to access on the list. @@ -409,7 +398,7 @@ struct VariadicListMem[ A low-level pointer to the element on the list corresponding to the given index. """ - return __mlir_op.`pop.variadic.get`(self.value, index(idx).value) + return __mlir_op.`pop.variadic.get`(self.value, idx.value) fn __iter__( self, diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index c09d4c90ec..4b49cb5bb0 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -149,12 +149,9 @@ struct Slice(Sized, Stringable, EqualityComparable): return len(range(self.start, self.end, self.step)) @always_inline - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + fn __getitem__(self, idx: Int) -> Int: """Get the slice index. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 1f84eef7c6..b35f8fd366 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -41,6 +41,8 @@ trait Indexer: that are coercible to `Int` (e.g. floating point values that have `__int__` method). In contrast to `Intable`, types conforming to `Indexer` must be convertible to `Int` in a lossless way. + + Note that types conforming to `Indexer` are implicitly convertible to `Int`. """ fn __index__(self) -> Int: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index a1e98ddbba..2756d944d7 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -85,7 +85,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): return self.curr @always_inline("nodebug") - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + fn __getitem__(self, idx: Int) -> Int: return index(idx) @always_inline("nodebug") @@ -116,7 +116,7 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): return self.end - self.start if self.start < self.end else 0 @always_inline("nodebug") - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + fn __getitem__(self, idx: Int) -> Int: return self.start + index(idx) @always_inline("nodebug") @@ -184,7 +184,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return _div_ceil_positive(abs(self.start - self.end), abs(self.step)) @always_inline("nodebug") - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + fn __getitem__(self, idx: Int) -> Int: return self.start + index(idx) * self.step @always_inline("nodebug") diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d0224255fb..1373735f75 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1810,14 +1810,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__[ - IndexerType: Indexer - ](self, idx: IndexerType) -> Scalar[type]: + fn __getitem__(self, idx: Int) -> Scalar[type]: """Gets an element from the vector. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The element index. @@ -1829,14 +1824,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ](self.value, index(idx).value) @always_inline("nodebug") - fn __setitem__[ - IndexerType: Indexer - ](inout self, idx: IndexerType, val: Scalar[type]): + fn __setitem__(inout self, idx: Int, val: Scalar[type]): """Sets an element in the vector. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index to set. val: The value to set. @@ -1846,18 +1836,11 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn __setitem__[ - IndexerType: Indexer - ]( - inout self, - idx: IndexerType, - val: __mlir_type[`!pop.scalar<`, type.value, `>`], + fn __setitem__( + inout self, idx: Int, val: __mlir_type[`!pop.scalar<`, type.value, `>`] ): """Sets an element in the vector. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index to set. val: The value to set. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 999f6893b5..53ef72970a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -875,19 +875,15 @@ struct String( # Operator dunders # ===------------------------------------------------------------------===# - fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> String: + fn __getitem__(self, idx: Int) -> String: """Gets the character at the specified position. - Parameters: - IndexerType: The type of the indexer. - Args: - i: The index value. + idx: The index value. Returns: A new string containing the character at the specified position. """ - var idx = index(i) if idx < 0: return self.__getitem__(len(self) + idx) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index d38d3ecafd..e53b9f0f06 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -128,31 +128,25 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): self._size += 1 @always_inline - fn __refitem__[ - IndexerType: Indexer, - ](self: Reference[Self, _, _], idx: IndexerType) -> Reference[ - Self.ElementType, self.is_mutable, self.lifetime - ]: + fn __refitem__( + self: Reference[Self, _, _], owned idx: Int + ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: """Get a `Reference` to the element at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index of the item. Returns: A reference to the item at the given index. """ - var i = index(idx) debug_assert( - -self[]._size <= i < self[]._size, "Index must be within bounds." + -self[]._size <= idx < self[]._size, "Index must be within bounds." ) - if i < 0: - i += len(self[]) + if idx < 0: + idx += len(self[]) - return self[]._array[i] + return self[]._array[idx] @always_inline fn __del__(owned self): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index d7f83545a2..ec30519f65 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -194,19 +194,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # Operator dunders # ===-------------------------------------------------------------------===# - fn __setitem__[ - IndexerType: Indexer - ](inout self, i: IndexerType, owned value: T): + fn __setitem__(inout self, idx: Int, owned value: T): """Sets a list element at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: - i: The index of the element. + idx: The index of the element. value: The value to assign. """ - var normalized_idx = index(i) + var normalized_idx = idx debug_assert( -self.size <= normalized_idx < self.size, "index must be within bounds", @@ -756,21 +751,18 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return res^ @always_inline - fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> T: + fn __getitem__(self, idx: Int) -> T: """Gets a copy of the list element at the given index. FIXME(lifetimes): This should return a reference, not a copy! - Parameters: - IndexerType: The type of the indexer. - Args: - i: The index of the element. + idx: The index of the element. Returns: A copy of the element at the given index. """ - var normalized_idx = index(i) + var normalized_idx = idx debug_assert( -self.size <= normalized_idx < self.size, "index must be within bounds", diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index ac239d1b2b..50215b09c6 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -181,19 +181,16 @@ struct InlinedFixedVector[ return self.current_size @always_inline - fn __getitem__[IndexerType: Indexer](self, i: IndexerType) -> type: + fn __getitem__(self, idx: Int) -> type: """Gets a vector element at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: - i: The index of the element. + idx: The index of the element. Returns: The element at the given index. """ - var normalized_idx = index(i) + var normalized_idx = idx debug_assert( -self.current_size <= normalized_idx < self.current_size, "index must be within bounds", @@ -208,19 +205,14 @@ struct InlinedFixedVector[ return self.dynamic_data[normalized_idx - Self.static_size] @always_inline - fn __setitem__[ - IndexerType: Indexer - ](inout self, i: IndexerType, value: type): + fn __setitem__(inout self, idx: Int, value: type): """Sets a vector element at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: - i: The index of the element. + idx: The index of the element. value: The value to assign. """ - var normalized_idx = index(i) + var normalized_idx = idx debug_assert( -self.current_size <= normalized_idx < self.current_size, "index must be within bounds", diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index f84adc5391..a2aa78b162 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -289,21 +289,16 @@ struct LegacyPointer[ ](self.address) @always_inline("nodebug") - fn __refitem__[ - IndexerType: Indexer - ](self, offset: IndexerType) -> Self._ref_type: + fn __refitem__(self, offset: Int) -> Self._ref_type: """Enable subscript syntax `ref[idx]` to access the element. - Parameters: - IndexerType: The type of the indexer.. - Args: offset: The offset to load from. Returns: The MLIR reference for the Mojo compiler to use. """ - return (self + index(offset)).__refitem__() + return (self + offset).__refitem__() # ===------------------------------------------------------------------=== # # Load/Store @@ -716,37 +711,27 @@ struct DTypePointer[ return LegacyPointer.address_of(arg[]) @always_inline("nodebug") - fn __getitem__[ - IndexerType: Indexer - ](self, offset: IndexerType) -> Scalar[type]: + fn __getitem__(self, offset: Int) -> Scalar[type]: """Loads a single element (SIMD of size 1) from the pointer at the specified index. - Parameters: - IndexerType: The type of the indexer. - Args: offset: The offset to load from. Returns: The loaded value. """ - return self.load(index(offset)) + return self.load(offset) @always_inline("nodebug") - fn __setitem__[ - IndexerType: Indexer - ](self, offset: IndexerType, val: Scalar[type]): + fn __setitem__(self, offset: Int, val: Scalar[type]): """Stores a single element value at the given offset. - Parameters: - IndexerType: The type of the indexer. - Args: offset: The offset to store to. val: The value to store. """ - return self.store(index(offset), val) + return self.store(offset, val) # ===------------------------------------------------------------------=== # # Comparisons diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b66bfece50..7eee5540f2 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -179,10 +179,8 @@ struct UnsafePointer[ # ===-------------------------------------------------------------------===# @always_inline - fn __refitem__( - self, - ) -> Self._ref_type: - """Return a reference to the underlying data, offset by the offset index. + fn __refitem__(self) -> Self._ref_type: + """Return a reference to the underlying data. Returns: A reference to the value. @@ -192,10 +190,8 @@ struct UnsafePointer[ ](self.address) @always_inline - fn __refitem__[ - IndexerType: Indexer - ](self, offset: IndexerType) -> Self._ref_type: - """Return a reference to the underlying data, offset by the offset index. + fn __refitem__(self, offset: Int) -> Self._ref_type: + """Return a reference to the underlying data, offset by the given index. Args: offset: The offset index. @@ -203,7 +199,7 @@ struct UnsafePointer[ Returns: An offset reference. """ - return (self + index(offset)).__refitem__() + return (self + offset).__refitem__() @always_inline fn __add__(self, offset: Int) -> Self: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 8a00319b9f..e82b5b80ff 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -335,19 +335,16 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): return size @always_inline("nodebug") - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> Int: + fn __getitem__(self, idx: Int) -> Int: """Gets an element from the tuple by index. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The element index. Returns: The tuple element value. """ - return self.data[index(idx)] + return self.data[idx] @always_inline("nodebug") fn __setitem__[index: Int](inout self, val: Int): @@ -362,19 +359,14 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): self.data.__setitem__[index](val) @always_inline("nodebug") - fn __setitem__[ - IndexerType: Indexer - ](inout self, idx: IndexerType, val: Int): + fn __setitem__(inout self, idx: Int, val: Int): """Sets an element in the tuple at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The element index. val: The value to store. """ - self.data[index(idx)] = val + self.data[idx] = val @always_inline("nodebug") fn as_tuple(self) -> StaticTuple[Int, size]: diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 7024579114..085c262cb5 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -132,25 +132,20 @@ struct Span[ @always_inline fn _refitem__[ intable: Intable - ](self, index: intable) -> Reference[T, is_mutable, lifetime]: + ](self, idx: intable) -> Reference[T, is_mutable, lifetime]: debug_assert( - -self._len <= int(index) < self._len, "index must be within bounds" + -self._len <= int(idx) < self._len, "index must be within bounds" ) - var offset = int(index) + var offset = int(idx) if offset < 0: offset += len(self) return (self._data + offset)[] @always_inline - fn __getitem__[ - IndexerType: Indexer - ](self, idx: IndexerType) -> Reference[T, is_mutable, lifetime]: + fn __getitem__(self, idx: Int) -> Reference[T, is_mutable, lifetime]: """Get a `Reference` to the element at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index of the item. @@ -158,24 +153,19 @@ struct Span[ A reference to the item at the given index. """ # note that self._refitem__ is already bounds checking - return self._refitem__(index(idx)) + return self._refitem__(idx) @always_inline - fn __setitem__[ - IndexerType: Indexer - ](inout self, idx: IndexerType, value: T): + fn __setitem__(inout self, idx: Int, value: T): """Get a `Reference` to the element at the given index. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index of the item. value: The value to set at the given index. """ # note that self._refitem__ is already bounds checking var ref = Reference[T, __mlir_attr.`1: i1`, __lifetime_of(self)]( - UnsafePointer(self._refitem__(index(idx)))[] + UnsafePointer(self._refitem__(idx))[] ) ref[] = value diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 10fa4d4abf..79d0c053e3 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -172,19 +172,16 @@ struct StringRef( # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> StringRef: + fn __getitem__(self, idx: Int) -> StringRef: """Get the string value at the specified position. - Parameters: - IndexerType: The type of the indexer. - Args: idx: The index position. Returns: The character at the specified position. """ - return StringRef {data: self.data + index(idx), length: 1} + return StringRef {data: self.data + idx, length: 1} @always_inline fn __eq__(self, rhs: StringRef) -> Bool: From 85391d78fe30004f227d83ca4b9d35fad0135e0e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 24 May 2024 07:31:56 -0700 Subject: [PATCH 0684/2019] [mojo-docs] Changelogify some improvements to def functions. MODULAR_ORIG_COMMIT_REV_ID: 63944fc745674eb3b9280093d4999023cd3420b2 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index f624259d45..9a147be7d4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -210,6 +210,11 @@ what we publish. - The Mojo Language Server now reports a warning when a local variable is unused. +- Implicit variable definitions in a `def` are more flexible: you can now + implicitly declare variables as the result of a tuple return, using + `a,b,c = foo()`, and can now shadow global immutable symbols using + `slice = foo()` without getting a compiler error. + - The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that allow users to opt into the `math.ceildiv` function. From ed5654ad31047912532d671f42129cea8ef01273 Mon Sep 17 00:00:00 2001 From: Lily Brown Date: Fri, 24 May 2024 15:20:56 -0700 Subject: [PATCH 0685/2019] [LSP] Support local variable renaming Support local variable renaming in the Mojo Language Server. MODULAR_ORIG_COMMIT_REV_ID: b2e00fc9ff318bfe03ecf939c4e0f070309b5519 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9a147be7d4..6a7b4c55fc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -395,6 +395,8 @@ what we publish. - `StringRef` now implements `startswith()` and `endswith()`. ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) +- The Mojo Language Server now supports renaming local variables. + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously From 12b9d473154b923d5c9a9bcf3512bf851ccff40d Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Fri, 24 May 2024 22:35:33 -0500 Subject: [PATCH 0686/2019] [External] [stdlib] Add method `unsafe_get` to `List` (#40553) [External] [stdlib] Add method `unsafe_get` to `List` See https://github.com/modularml/mojo/pull/2677#discussion_r1605618673 for the background about this method. We are currently missing methods to access elements in collections without any bounds checks or wraparound, for maximum performance. I suggest that we introduce `unsafe_get` and `unsafe_set` to our List-like collections. This is equivalent to * https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_unchecked * https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_unchecked_mut in Rust. This should prove useful to makers of high performance libraries like Max :p We can then make `__getitem__` and `__setitem__` as safe as we want without impacting the power users. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2800 MODULAR_ORIG_COMMIT_REV_ID: 5321c191d83262f240c5d8d5ac77afccaa00a7ba --- docs/changelog.md | 5 ++++ stdlib/src/collections/list.mojo | 38 +++++++++++++++++++++++++- stdlib/test/collections/test_list.mojo | 22 +++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6a7b4c55fc..0c492ca7f6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -385,6 +385,11 @@ what we publish. - Added `os.getsize` function, which gives the size in bytes of a path. ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) +- `List` now has a method `unsafe_get` to get the reference to an + element without bounds check or wraparound for negative indices. + Note that this method is unsafe. Use with caution. + ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + - Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys and value. ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index ec30519f65..2a890ceaab 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -788,7 +788,43 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if i < 0: normalized_idx += self[].size - return (self[].data + normalized_idx)[] + return self[].unsafe_get(normalized_idx) + + @always_inline + fn unsafe_get[ + IndexerType: Indexer, + ](self: Reference[Self, _, _], idx: IndexerType) -> Reference[ + Self.T, self.is_mutable, self.lifetime + ]: + """Get a reference to an element of self without checking index bounds. + + Users should consider using `__getitem__` instead of this method as it is unsafe. + If an index is out of bounds, this method will not abort, it will be considered + undefined behavior. + + Note that there is no wraparound for negative indices, caution is advised. + Using negative indices is considered undefined behavior. + Never use `my_list.unsafe_get(-1)` to get the last element of the list. It will not work. + Instead, do `my_list.unsafe_get(len(my_list) - 1)`. + + Parameters: + IndexerType: The type of the argument used as index. + + Args: + idx: The index of the element to get. + + Returns: + A reference to the element at the given index. + """ + var idx_as_int = index(idx) + debug_assert( + 0 <= idx_as_int < len(self[]), + ( + "The index provided must be within the range [0, len(List) -1]" + " when using List.unsafe_get()" + ), + ) + return (self[].data + idx_as_int)[] fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 3bff4f4981..39ba9b2aad 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -59,6 +59,27 @@ def test_list(): assert_equal(7, list[-1]) +def test_list_unsafe_get(): + var list = List[Int]() + + for i in range(5): + list.append(i) + + assert_equal(5, len(list)) + assert_equal(0, list.unsafe_get(0)[]) + assert_equal(1, list.unsafe_get(1)[]) + assert_equal(2, list.unsafe_get(2)[]) + assert_equal(3, list.unsafe_get(3)[]) + assert_equal(4, list.unsafe_get(4)[]) + + list[2] = -2 + assert_equal(-2, list.unsafe_get(2)[]) + + list.clear() + list.append(2) + assert_equal(2, list.unsafe_get(0)[]) + + def test_list_clear(): var list = List[Int](1, 2, 3) assert_equal(len(list), 3) @@ -789,6 +810,7 @@ def test_indexing(): def main(): test_mojo_issue_698() test_list() + test_list_unsafe_get() test_list_clear() test_list_to_bool_conversion() test_list_pop() From 0f1a16c657852aecdb3f5a340a8a3e5f9aae3f8d Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Fri, 24 May 2024 22:36:11 -0500 Subject: [PATCH 0687/2019] [External] [stdlib] Cleanup `String` constructors to reuse null terminator checks (#40488) [External] [stdlib] Cleanup `String` constructors to reuse null terminator checks We would like to reuse as much as possible the main constructor (from List[UInt8]) which has more checks. And it will also simplify the SSO work as we centralize the entrypoints of the String creation. A few simple cleanups too: * Do not add the null terminator if the list storage was already initialized to 0 * Remove unnecessary bitcasts now that we always work with UInt8 All the diffs here are independant from one another but I didn't have the courage to do 5 PRs Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2789 MODULAR_ORIG_COMMIT_REV_ID: 242f2a63531e461ab4039747ba57b30c96d42483 --- stdlib/src/builtin/string.mojo | 42 ++++++++++++++-------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 53ef72970a..751a8de219 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -667,14 +667,7 @@ struct String( impl[-1] == 0, "expected last element of String buffer to be null terminator", ) - # we store the length and capacity beforehand as `steal_data()` will invalidated `impl` - var length = len(impl) - var capacity = impl.capacity - self._buffer = List[UInt8]( - unsafe_pointer=impl.steal_data().bitcast[UInt8](), - size=length, - capacity=capacity, - ) + self._buffer = impl^ @always_inline fn __init__(inout self): @@ -690,17 +683,10 @@ struct String( """ var length = len(str) var buffer = Self._buffer_type() + # +1 for null terminator, initialized to 0 buffer.resize(length + 1, 0) - memcpy( - # TODO(modularml/mojo#2317): - # Remove this bitcast after transition to UInt8 for string data - # is complete. - dest=buffer.data.bitcast[UInt8](), - src=str.data, - count=length, - ) - buffer[length] = 0 - self._buffer = buffer^ + memcpy(dest=buffer.data, src=str.data, count=length) + self = Self(buffer^) @always_inline fn __init__(inout self, str_slice: StringSlice): @@ -716,14 +702,14 @@ struct String( # Calculate length in bytes var length: Int = len(str_slice.as_bytes_slice()) var buffer = Self._buffer_type() + # +1 for null terminator, initialized to 0 buffer.resize(length + 1, 0) memcpy( dest=buffer.data, src=str_slice.as_bytes_slice().unsafe_ptr(), count=length, ) - buffer[length] = 0 - self._buffer = buffer^ + self = Self(buffer^) @always_inline fn __init__(inout self, literal: StringLiteral): @@ -759,8 +745,10 @@ struct String( """ # we don't know the capacity of ptr, but we'll assume it's the same or # larger than len - self._buffer = Self._buffer_type( - unsafe_pointer=ptr.bitcast[UInt8](), size=len, capacity=len + self = Self( + Self._buffer_type( + unsafe_pointer=ptr.bitcast[UInt8](), size=len, capacity=len + ) ) @always_inline @@ -774,9 +762,13 @@ struct String( ptr: The pointer to the buffer. len: The length of the buffer, including the null terminator. """ - self._buffer = Self._buffer_type() - self._buffer.data = UnsafePointer(ptr.address) - self._buffer.size = len + self = Self( + Self._buffer_type( + unsafe_pointer=UnsafePointer(ptr.address), + size=len, + capacity=len, + ) + ) @always_inline fn __init__(inout self, ptr: DTypePointer[DType.uint8], len: Int): From 9cf9d33e1dd6a2c6749280d10e805c2113d86e16 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Fri, 24 May 2024 22:41:02 -0500 Subject: [PATCH 0688/2019] [External] [stdlib] Introduce list sorting for comparable elements (#40122) [External] [stdlib] Introduce list sorting for comparable elements Add a `sort` function for list of `ComparableCollectionElement`s. In the future, we can consolidate the pre-existing sort related functions as we make things more general to apply for various cases. Co-authored-by: Maxim Zaks Closes modularml/mojo#2609 MODULAR_ORIG_COMMIT_REV_ID: d998432a73e2ad553c518c0c93237efc5766bc9b --- docs/changelog.md | 4 + stdlib/src/builtin/bool.mojo | 63 ++- stdlib/src/builtin/sort.mojo | 88 +++++ stdlib/src/builtin/string.mojo | 1 + stdlib/src/builtin/string_literal.mojo | 1 + stdlib/src/builtin/value.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 8 +- stdlib/src/utils/index.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/builtin/test_bool.mojo | 33 ++ stdlib/test/builtin/test_sort.mojo | 519 +++++++++++++------------ 11 files changed, 475 insertions(+), 248 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0c492ca7f6..ed4b6d8330 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,10 @@ what we publish. ### ⭐️ New +- Add a `sort` function for list of `ComparableCollectionElement`s. + [PR #2609](https://github.com/modularml/mojo/pull/2609) by + [@mzaks](https://github.com/mzaks) + - Mojo has introduced `@parameter for`, a new feature for compile-time programming. `@parameter for` defines a for loop where the sequence and the induction values in the sequence must be parameter values. For example: diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index bcc45bb128..9147c7a1d9 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -60,9 +60,8 @@ trait Boolable: @register_passable("trivial") struct Bool( Stringable, - CollectionElement, + ComparableCollectionElement, Boolable, - EqualityComparable, Intable, Indexer, ): @@ -200,6 +199,66 @@ struct Bool( self._as_scalar_bool(), rhs._as_scalar_bool() ) + @always_inline("nodebug") + fn __lt__(self, rhs: Self) -> Bool: + """Compare this Bool to RHS using less-than comparison. + + Args: + rhs: The rhs of the operation. + + Returns: + True if self is False and rhs is True. + """ + + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) + + @always_inline("nodebug") + fn __le__(self, rhs: Self) -> Bool: + """Compare this Bool to RHS using less-than-or-equal comparison. + + Args: + rhs: The rhs of the operation. + + Returns: + True if self is False and rhs is True or False. + """ + + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) + + @always_inline("nodebug") + fn __gt__(self, rhs: Self) -> Bool: + """Compare this Bool to RHS using greater-than comparison. + + Args: + rhs: The rhs of the operation. + + Returns: + True if self is True and rhs is False. + """ + + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) + + @always_inline("nodebug") + fn __ge__(self, rhs: Self) -> Bool: + """Compare this Bool to RHS using greater-than-or-equal comparison. + + Args: + rhs: The rhs of the operation. + + Returns: + True if self is True and rhs is True or False. + """ + + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) + # ===-------------------------------------------------------------------===# # Bitwise operations # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 1c2aebf96e..ce949df362 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -395,3 +395,91 @@ fn _small_sort[ _sort_partial_3[type, cmp_fn](array, 0, 2, 3) _sort_partial_3[type, cmp_fn](array, 1, 2, 3) return + + +# ===----------------------------------------------------------------------=== # +# Comparable elements list sorting +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn insertion_sort[type: ComparableCollectionElement](inout list: List[type]): + """Sort list of the order comparable elements in-place with insertion sort algorithm. + + Parameters: + type: The order comparable collection element type. + + Args: + list: The list of the order comparable elements which will be sorted in-place. + """ + for i in range(1, len(list)): + var key = list[i] + var j = i - 1 + while j >= 0 and key < list[j]: + list[j + 1] = list[j] + j -= 1 + list[j + 1] = key + + +fn _quick_sort[ + type: ComparableCollectionElement +](inout list: List[type], low: Int, high: Int): + """Sort section of the list, between low and high, with quick sort algorithm in-place. + + Parameters: + type: The order comparable collection element type. + + Args: + list: The list of the order comparable elements which will be sorted in-place. + low: Int value identifying the lowest index of the list section to be sorted. + high: Int value identifying the highest index of the list section to be sorted. + """ + + @always_inline + @parameter + fn _partition(low: Int, high: Int) -> Int: + var pivot = list[high] + var i = low - 1 + for j in range(low, high): + if list[j] <= pivot: + i += 1 + list[j], list[i] = list[i], list[j] + list[i + 1], list[high] = list[high], list[i + 1] + return i + 1 + + if low < high: + var pi = _partition(low, high) + _quick_sort(list, low, pi - 1) + _quick_sort(list, pi + 1, high) + + +@always_inline +fn quick_sort[type: ComparableCollectionElement](inout list: List[type]): + """Sort list of the order comparable elements in-place with quick sort algorithm. + + Parameters: + type: The order comparable collection element type. + + Args: + list: The list of the order comparable elements which will be sorted in-place. + """ + _quick_sort(list, 0, len(list) - 1) + + +fn sort[ + type: ComparableCollectionElement, slist_ub: Int = 64 +](inout list: List[type]): + """Sort list of the order comparable elements in-place. This function picks the best algorithm based on the list length. + + Parameters: + type: The order comparable collection element type. + slist_ub: The upper bound for a list size which is considered small. + + Args: + list: The list of the scalars which will be sorted in-place. + """ + var count = len(list) + if count <= slist_ub: + insertion_sort(list) # small lists are best sorted with insertion sort + else: + quick_sort(list) # others are best sorted with quick sort diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 751a8de219..6141ecf44d 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -620,6 +620,7 @@ struct String( Representable, IntableRaising, KeyElement, + Comparable, Boolable, Formattable, ToFormatter, diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 4a7725316f..8b3428af69 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -38,6 +38,7 @@ struct StringLiteral( KeyElement, Boolable, Formattable, + Comparable, ): """This type represents a string literal. diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index c03b2e7865..45145fc359 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -157,7 +157,7 @@ trait StringableCollectionElement(CollectionElement, Stringable): pass -trait ComparableCollectionElement(CollectionElement, EqualityComparable): +trait ComparableCollectionElement(CollectionElement, Comparable): """ This trait is a temporary solution to enable comparison of collection elements as utilized in the `index` and `count` methods of diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 7eee5540f2..41a2633b7d 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -31,7 +31,13 @@ from memory.memory import _free, _malloc @register_passable("trivial") struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC -](Boolable, CollectionElement, Stringable, Intable, EqualityComparable): +]( + Boolable, + CollectionElement, + Stringable, + Intable, + Comparable, +): """This is a pointer type that can point to any generic value that is movable. Parameters: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index e82b5b80ff..1b28607cc2 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -180,7 +180,7 @@ fn _bool_tuple_reduce[ @value @register_passable("trivial") -struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): +struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): """A base struct that implements size agnostic index functions. Parameters: diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 79d0c053e3..ee9776bb1b 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -43,7 +43,7 @@ struct StringRef( Stringable, Hashable, Boolable, - EqualityComparable, + Comparable, ): """ Represent a constant reference to a string, i.e. a sequence of characters diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 17a4347afc..5458834ab5 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -105,6 +105,38 @@ def test_indexer(): assert_equal(0, Bool.__index__(False)) +def test_comparisons(): + assert_true(False == False) + assert_true(True == True) + assert_false(False == True) + assert_false(True == False) + + assert_true(False != True) + assert_true(True != False) + assert_false(False != False) + assert_false(True != True) + + assert_true(True > False) + assert_false(False > True) + assert_false(False > False) + assert_false(True > True) + + assert_true(True >= False) + assert_false(False >= True) + assert_true(False >= False) + assert_true(True >= True) + + assert_false(True < False) + assert_true(False < True) + assert_false(False < False) + assert_false(True < True) + + assert_false(True <= False) + assert_true(False <= True) + assert_true(False <= False) + assert_true(True <= True) + + def main(): test_bool_cast_to_int() test_bool_none() @@ -113,3 +145,4 @@ def main(): test_bitwise() test_neg() test_indexer() + test_comparisons() diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index c46e1303e4..5bc828a00a 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -10,17 +10,66 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +# RUN: %mojo -D CURRENT_DIR=%S %s -# RUN: %mojo %s | FileCheck %s -from collections import List -from random import random_si64, seed +from pathlib import Path +from sys import os_is_windows, env_get_string + +alias CURRENT_DIR = env_get_string["CURRENT_DIR"]() +from testing import assert_true, assert_equal, assert_false +from random import random_si64, random_ui64, random_float64, seed from builtin.sort import _quicksort, _small_sort -# CHECK-LABEL: test_sort_small_3 -fn test_sort_small_3(): - print("== test_sort_small_3") +fn random_numbers[ + dtype: DType +](size: Int, max: Int = 3000) -> List[Scalar[dtype]]: + var result = List[Scalar[dtype]](size) + for _ in range(size): + + @parameter + if ( + dtype == DType.int8 + or dtype == DType.int16 + or dtype == DType.int32 + or dtype == DType.int64 + ): + result.append(random_si64(0, max).cast[dtype]()) + elif ( + dtype == DType.float16 + or dtype == DType.float32 + or dtype == DType.float64 + ): + result.append(random_float64(0, max).cast[dtype]()) + else: + result.append(random_ui64(0, max).cast[dtype]()) + return result + + +# fn assert_sorted[dtype: DType](inout list: List[Scalar[dtype]]) raises: +# sort[dtype](list) +# for i in range(1, len(list)): +# assert_true( +# list[i] >= list[i - 1], str(list[i - 1]) + " > " + str(list[i]) +# ) + + +fn assert_sorted_string(inout list: List[String]) raises: + for i in range(1, len(list)): + assert_true( + list[i] >= list[i - 1], str(list[i - 1]) + " > " + str(list[i]) + ) + + +fn assert_sorted[ + type: ComparableCollectionElement +](inout list: List[type]) raises: + for i in range(1, len(list)): + assert_true(list[i] >= list[i - 1], "error at index: " + str(i)) + + +fn test_sort_small_3() raises: alias length = 3 var list = List[Int]() @@ -36,16 +85,12 @@ fn test_sort_small_3(): var ptr = rebind[Pointer[Int]](list.data) _small_sort[length, Int, _less_than_equal](ptr) - # CHECK: 1 - # CHECK: 2 - # CHECK: 9 + var expected = List[Int](1, 2, 9) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort_small_5 -fn test_sort_small_5(): - print("== test_sort_small_5") +fn test_sort_small_5() raises: alias length = 5 var list = List[Int]() @@ -63,28 +108,18 @@ fn test_sort_small_5(): var ptr = rebind[Pointer[Int]](list.data) _small_sort[length, Int, _less_than_equal](ptr) - # CHECK: 1 - # CHECK: 2 - # CHECK: 3 - # CHECK: 4 - # CHECK: 9 + var expected = List[Int](1, 2, 3, 4, 9) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort0 fn test_sort0(): - print("== test_sort0") - var list = List[Int]() sort(list) -# CHECK-LABEL: test_sort2 -fn test_sort2(): - print("== test_sort2") - +fn test_sort2() raises: alias length = 2 var list = List[Int]() @@ -93,26 +128,21 @@ fn test_sort2(): sort(list) - # CHECK: -1 - # CHECK: 0 + var expected = List[Int](-1, 0) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) list[0] = 2 list[1] = -2 sort(list) - # CHECK: -2 - # CHECK: 2 + expected = List[Int](-2, 2) for i in range(length): - print(list[i]) - + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort3 -fn test_sort3(): - print("== test_sort3") +fn test_sort3() raises: alias length = 3 var list = List[Int]() @@ -122,11 +152,9 @@ fn test_sort3(): sort(list) - # CHECK: -1 - # CHECK: 0 - # CHECK: 1 + var expected = List[Int](-1, 0, 1) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) list[0] = 2 list[1] = -2 @@ -134,22 +162,17 @@ fn test_sort3(): sort(list) - # CHECK: -2 - # CHECK: 0 - # CHECK: 2 + expected = List[Int](-2, 0, 2) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) -# CHECK-LABEL test_sort3_dupe_elements -fn test_sort3_dupe_elements(): - print("== test_sort3_dupe_elements") - +fn test_sort3_dupe_elements() raises: alias length = 3 fn test[ cmp_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, - ](): + ]() raises: var list = List[Int](capacity=3) list.append(5) list.append(3) @@ -158,11 +181,9 @@ fn test_sort3_dupe_elements(): var ptr = rebind[Pointer[Int]](list.data) _quicksort[Int, cmp_fn](ptr, len(list)) - # CHECK: 3 - # CHECK: 3 - # CHECK: 5 + var expected = List[Int](3, 3, 5) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) @parameter fn _lt[type: AnyRegType](lhs: type, rhs: type) -> Bool: @@ -176,10 +197,7 @@ fn test_sort3_dupe_elements(): test[_leq]() -# CHECK-LABEL: test_sort4 -fn test_sort4(): - print("== test_sort4") - +fn test_sort4() raises: alias length = 4 var list = List[Int]() @@ -190,12 +208,9 @@ fn test_sort4(): sort(list) - # CHECK: -1 - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 + var expected = List[Int](-1, 0, 1, 2) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) list[0] = 2 list[1] = -2 @@ -204,18 +219,12 @@ fn test_sort4(): sort(list) - # CHECK: -4 - # CHECK: -2 - # CHECK: 0 - # CHECK: 2 + expected = List[Int](-4, -2, 0, 2) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort5 -fn test_sort5(): - print("== test_sort5") - +fn test_sort5() raises: alias length = 5 var list = List[Int]() @@ -224,13 +233,9 @@ fn test_sort5(): sort(list) - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 - # CHECK: 3 - # CHECK: 4 + var expected = List[Int](0, 1, 2, 3, 4) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) list[0] = 2 list[1] = -2 @@ -240,19 +245,12 @@ fn test_sort5(): sort(list) - # CHECK: -4 - # CHECK: -2 - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 + expected = List[Int](-4, -2, 0, 1, 2) for i in range(length): - print(list[i]) - + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort_reverse -fn test_sort_reverse(): - print("== test_sort_reverse") +fn test_sort_reverse() raises: alias length = 5 var list = List[Int](capacity=length) @@ -261,19 +259,12 @@ fn test_sort_reverse(): sort(list) - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 - # CHECK: 3 - # CHECK: 4 + var expected = List[Int](0, 1, 2, 3, 4) for i in range(length): - print(list[i]) + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort_semi_random -fn test_sort_semi_random(): - print("== test_sort_semi_random") - +fn test_sort_semi_random() raises: alias length = 8 var list = List[Int](capacity=length) @@ -285,22 +276,12 @@ fn test_sort_semi_random(): sort(list) - # CHECK: 7 - # CHECK: 5 - # CHECK: 3 - # CHECK: 1 - # CHECK: 0 - # CHECK: 2 - # CHECK: 4 - # CHECK: 6 + var expected = List[Int](-7, -5, -3, -1, 0, 2, 4, 6) for i in range(length): - print(list[i]) - + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort9 -fn test_sort9(): - print("== test_sort9") +fn test_sort9() raises: alias length = 9 var list = List[Int](capacity=length) @@ -309,23 +290,12 @@ fn test_sort9(): sort(list) - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 - # CHECK: 3 - # CHECK: 4 - # CHECK: 5 - # CHECK: 6 - # CHECK: 7 - # CHECK: 8 + var expected = List[Int](0, 1, 2, 3, 4, 5, 6, 7, 8) for i in range(length): - print(list[i]) - + assert_equal(expected[i], list[i]) -# CHECK-LABEL: test_sort103 -fn test_sort103(): - print("== test_sort103") +fn test_sort103() raises: alias length = 103 var list = List[Int](capacity=length) @@ -334,16 +304,11 @@ fn test_sort103(): sort(list) - # CHECK-NOT: unsorted for i in range(1, length): - if list[i - 1] > list[i]: - print("error: unsorted") + assert_false(list[i - 1] > list[i]) -# CHECK-LABEL: test_sort_any_103 -fn test_sort_any_103(): - print("== test_sort_any_103") - +fn test_sort_any_103() raises: alias length = 103 var list = List[Float32](capacity=length) @@ -352,15 +317,11 @@ fn test_sort_any_103(): sort[DType.float32](list) - # CHECK-NOT: unsorted for i in range(1, length): - if list[i - 1] > list[i]: - print("error: unsorted") - + assert_false(list[i - 1] > list[i]) -fn test_quick_sort_repeated_val(): - print("== test_quick_sort_repeated_val") +fn test_quick_sort_repeated_val() raises: alias length = 36 var list = List[Float32](capacity=length) @@ -377,98 +338,96 @@ fn test_quick_sort_repeated_val(): var ptr = rebind[Pointer[Float32]](list.data) _quicksort[Float32, _greater_than](ptr, len(list)) - # CHECK: 9.0 - # CHECK: 9.0 - # CHECK: 9.0 - # CHECK: 9.0 - # CHECK: 8.0 - # CHECK: 8.0 - # CHECK: 8.0 - # CHECK: 8.0 - # CHECK: 7.0 - # CHECK: 7.0 - # CHECK: 7.0 - # CHECK: 7.0 - # CHECK: 6.0 - # CHECK: 6.0 - # CHECK: 6.0 - # CHECK: 6.0 - # CHECK: 5.0 - # CHECK: 5.0 - # CHECK: 5.0 - # CHECK: 5.0 - # CHECK: 4.0 - # CHECK: 4.0 - # CHECK: 4.0 - # CHECK: 4.0 - # CHECK: 3.0 - # CHECK: 3.0 - # CHECK: 3.0 - # CHECK: 3.0 - # CHECK: 2.0 - # CHECK: 2.0 - # CHECK: 2.0 - # CHECK: 2.0 - # CHECK: 1.0 - # CHECK: 1.0 - # CHECK: 1.0 - # CHECK: 1.0 + var expected = List[Float32]( + 9.0, + 9.0, + 9.0, + 9.0, + 8.0, + 8.0, + 8.0, + 8.0, + 7.0, + 7.0, + 7.0, + 7.0, + 6.0, + 6.0, + 6.0, + 6.0, + 5.0, + 5.0, + 5.0, + 5.0, + 4.0, + 4.0, + 4.0, + 4.0, + 3.0, + 3.0, + 3.0, + 3.0, + 2.0, + 2.0, + 2.0, + 2.0, + 1.0, + 1.0, + 1.0, + 1.0, + ) for i in range(0, length): - print(list[i]) + assert_equal(expected[i], list[i]) @parameter fn _less_than[type: AnyRegType](lhs: type, rhs: type) -> Bool: return rebind[Float32](lhs) < rebind[Float32](rhs) - # CHECK: 1.0 - # CHECK: 1.0 - # CHECK: 1.0 - # CHECK: 1.0 - # CHECK: 2.0 - # CHECK: 2.0 - # CHECK: 2.0 - # CHECK: 2.0 - # CHECK: 3.0 - # CHECK: 3.0 - # CHECK: 3.0 - # CHECK: 3.0 - # CHECK: 4.0 - # CHECK: 4.0 - # CHECK: 4.0 - # CHECK: 4.0 - # CHECK: 5.0 - # CHECK: 5.0 - # CHECK: 5.0 - # CHECK: 5.0 - # CHECK: 6.0 - # CHECK: 6.0 - # CHECK: 6.0 - # CHECK: 6.0 - # CHECK: 7.0 - # CHECK: 7.0 - # CHECK: 7.0 - # CHECK: 7.0 - # CHECK: 8.0 - # CHECK: 8.0 - # CHECK: 8.0 - # CHECK: 8.0 - # CHECK: 9.0 - # CHECK: 9.0 - # CHECK: 9.0 - # CHECK: 9.0 + expected = List[Float32]( + 1.0, + 1.0, + 1.0, + 1.0, + 2.0, + 2.0, + 2.0, + 2.0, + 3.0, + 3.0, + 3.0, + 3.0, + 4.0, + 4.0, + 4.0, + 4.0, + 5.0, + 5.0, + 5.0, + 5.0, + 6.0, + 6.0, + 6.0, + 6.0, + 7.0, + 7.0, + 7.0, + 7.0, + 8.0, + 8.0, + 8.0, + 8.0, + 9.0, + 9.0, + 9.0, + 9.0, + ) var sptr = rebind[Pointer[Float32]](list.data) _quicksort[Float32, _less_than](sptr, len(list)) for i in range(0, length): - print(list[i]) - + assert_equal(expected[i], list[i]) -fn test_partition_top_k(length: Int, k: Int): - print("== test_partition_top_k_", end="") - print(length, end="") - print("_", end="") - print(k, end="") - print("") +fn test_partition_top_k(length: Int, k: Int) raises: var list = List[Float32](capacity=length) for i in range(0, length): @@ -483,12 +442,10 @@ fn test_partition_top_k(length: Int, k: Int): for i in range(0, k): if list[i] < length - k: - print("error: incorrect top-k element", list[i]) + assert_true(False) -# CHECK-LABEL: test_sort_stress -fn test_sort_stress(): - print("== test_sort_stress") +fn test_sort_stress() raises: var lens = VariadicList[Int](3, 100, 117, 223, 500, 1000, 1500, 2000, 3000) var random_seed = 0 seed(random_seed) @@ -498,19 +455,16 @@ fn test_sort_stress(): fn test[ cmp_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, check_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, - ](length: Int): + ](length: Int) raises: var list = List[Int](capacity=length) - for i in range(length): + for _ in range(length): list.append(int(random_si64(-length, length))) var ptr = rebind[Pointer[Int]](list.data) _quicksort[Int, cmp_fn](ptr, len(list)) - # CHECK-NOT: error for i in range(length - 1): - if not check_fn[Int](list[i], list[i + 1]): - print("error: unsorted, seed is", random_seed) - return + assert_true(check_fn[Int](list[i], list[i + 1])) @parameter @always_inline @@ -545,10 +499,7 @@ struct MyStruct: var val: Int -# CHECK-LABEL: test_sort_custom -fn test_sort_custom(): - print("== test_sort_custom") - +fn test_sort_custom() raises: alias length = 103 var list = List[MyStruct](capacity=length) @@ -561,13 +512,97 @@ fn test_sort_custom(): sort[MyStruct, compare_fn](list) - # CHECK-NOT: unsorted for i in range(1, length): - if list[i - 1].val > list[i].val: - print("error: unsorted") + assert_false(list[i - 1].val > list[i].val) + +def test_sort_string_small_list(): + var list = random_numbers[DType.int32](10) + var string_list = List[String]() + for n in list: + string_list.append(str(int(n[]))) + sort(string_list) + assert_sorted_string(string_list) -fn main(): + +def test_sort_string_big_list(): + var list = random_numbers[DType.int32](1000) + var string_list = List[String]() + for n in list: + string_list.append(str(int(n[]))) + sort(string_list) + assert_sorted_string(string_list) + + +def test_sort_strings(): + var text = (Path(CURRENT_DIR) / "test_file_dummy_input.txt").read_text() + var strings = text.split(" ") + sort(strings) + assert_sorted_string(strings) + + +@value +struct Person(ComparableCollectionElement): + var name: String + var age: Int + + fn __lt__(self, other: Self) -> Bool: + if self.age < other.age: + return True + if self.age == other.age: + return self.name < other.name + return False + + fn __le__(self, other: Self) -> Bool: + return not (other < self) + + fn __gt__(self, other: Self) -> Bool: + return other < self + + fn __ge__(self, other: Self) -> Bool: + return not (self < other) + + fn __eq__(self, other: Self) -> Bool: + return self.age == other.age and self.name == other.name + + fn __ne__(self, other: Self) -> Bool: + return self.age != other.age or self.name != other.name + + +def test_sort_comparamble_elements_list(): + var list = List[Person]() + + @parameter + fn gen_list(count: Int): + list = List[Person]() + var ages = random_numbers[DType.uint8](count) + var names = List[String]("Maxim", "Max", "Alex", "Bob", "Joe") + for age in ages: + var name = names[int(age[]) % len(names)] + list.append(Person(name, int(age[]))) + + gen_list(10) + sort(list) + assert_sorted(list) + + gen_list(100) + sort(list) + assert_sorted(list) + + gen_list(1000) + sort(list) + assert_sorted(list) + + +fn test_sort_empty_comparamble_elements_list() raises: + var person_list = List[Person]() + sort(person_list) + insertion_sort(person_list) + quick_sort(person_list) + assert_true(len(person_list) == 0) + + +def main(): test_sort_small_3() test_sort_small_5() test_sort0() @@ -587,12 +622,12 @@ fn main(): test_sort_custom() - # CHECK-LABEL: test_partition_top_k_7_5 - # CHECK-NOT: incorrect top-k test_partition_top_k(7, 5) - # CHECK-LABEL: test_partition_top_k_11_2 - # CHECK-NOT: incorrect top-k test_partition_top_k(11, 2) - # CHECK-LABEL: test_partition_top_k_4_1 - # CHECK-NOT: incorrect top-k test_partition_top_k(4, 1) + + test_sort_string_small_list() + test_sort_string_big_list() + test_sort_strings() + test_sort_comparamble_elements_list() + test_sort_empty_comparamble_elements_list() From e680390877a2235deb09a920ff33505108c68eee Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 24 May 2024 23:15:48 -0500 Subject: [PATCH 0689/2019] [External] [Examples] Fix plt figure dimensions in RayTracing example notebook (#40617) [External] [Examples] Fix plt figure dimensions in RayTracing example notebook The figsize in plt.figure should be in (width, height). It was inverted, making the fig look like this: ![before](https://github.com/modularml/mojo/assets/102399121/dc609d7c-5bb6-4b3a-99ad-8fc3a8e01e69) After swapping the dimension: ![after](https://github.com/modularml/mojo/assets/102399121/f6cc0835-a77e-4c46-a13b-e33f083fc60f) ORIGINAL_AUTHOR=SynodicMonth <102399121+SynodicMonth@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2798 Co-authored-by: SynodicMonth <102399121+SynodicMonth@users.noreply.github.com> Closes modularml/mojo#2798 MODULAR_ORIG_COMMIT_REV_ID: 13e6021c175538a385a47a027e9375ed93ee1690 --- examples/notebooks/RayTracing.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index de32b13a20..3bb4ea98f0 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -311,7 +311,7 @@ " plt = Python.import_module(\"matplotlib.pyplot\")\n", " colors = Python.import_module(\"matplotlib.colors\")\n", " dpi = 32\n", - " fig = plt.figure(1, [image.height // 10, image.width // 10], dpi)\n", + " fig = plt.figure(1, [image.width // 10, image.height // 10], dpi)\n", "\n", " plt.imshow(image.to_numpy_image())\n", " plt.axis(\"off\")\n", From e05abff934010c5932029ba41a68236aed89e919 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Sat, 25 May 2024 12:51:38 -0500 Subject: [PATCH 0690/2019] [External] [docs] Fix spelling error in `types.ipynb` (#40625) [External] [docs] Fix spelling error in `types.ipynb` ORIGINAL_AUTHOR=Trent Fellbootman <78004741+Trent-Fellbootman@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2818 Co-authored-by: Trent Fellbootman <78004741+Trent-Fellbootman@users.noreply.github.com> Closes modularml/mojo#2818 MODULAR_ORIG_COMMIT_REV_ID: c0d099a3c0640d90394e26d4be28126d155028a5 --- docs/manual/types.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 6ceffe596b..1a454a11b4 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -530,7 +530,7 @@ "Many types have a boolean representation. Any type that implements the \n", "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait has a boolean \n", "representation. As a general principle, collections evaluate as True if they \n", - "contain any elements, False if the are empty; strings evaluate as True if they\n", + "contain any elements, False if they are empty; strings evaluate as True if they\n", "have a non-zero length." ] }, From 22e83e408ef5c6452cc7f2e4c48324f8540fd650 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 25 May 2024 16:04:20 -0700 Subject: [PATCH 0691/2019] [mojo-lang] Implement support for returning references. This introduces the `fn x() -> ref [lifetime] type:` syntax for returning references. This integrates nicely (and locally) with function call and return processing. I'm very happy with how simple this is! MODULAR_ORIG_COMMIT_REV_ID: 348baea7a3a7ae264a878db4f027ac8bcc811765 --- stdlib/src/builtin/tuple.mojo | 26 +++++++++++++------------- stdlib/src/utils/span.mojo | 4 ++-- stdlib/src/utils/static_tuple.mojo | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 45c150a092..885220c040 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -150,11 +150,19 @@ struct Tuple[*element_types: Movable](Sized, Movable): return Self.__len__() @always_inline("nodebug") - fn __refitem__[ + fn __getitem__[ idx: Int - ](self: Reference[Self, _, _]) -> Reference[ - element_types[idx.value], self.is_mutable, self.lifetime + ](self: Reference[Self, _, _]) -> ref [self.lifetime] element_types[ + idx.value ]: + """Get a reference to an element in the tuple. + + Parameters: + idx: The element to return. + + Returns: + A referece to the specified element. + """ # Return a reference to an element at the specified index, propagating # mutability of self. var storage_kgen_ptr = UnsafePointer.address_of(self[].storage).address @@ -220,16 +228,8 @@ struct Tuple[*element_types: Movable](Sized, Movable): @parameter if _type_is_eq[T, element_types[i]](): - var tmp_ref = self.__refitem__[i]() - var tmp = rebind[ - Reference[ - T, - tmp_ref.is_mutable, - tmp_ref.lifetime, - tmp_ref.address_space, - ] - ](tmp_ref) - if tmp[].__eq__(value): + var elt_ptr = UnsafePointer.address_of(self[i]).bitcast[T]() + if elt_ptr[].__eq__(value): return True return False diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 085c262cb5..20af861891 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -164,10 +164,10 @@ struct Span[ value: The value to set at the given index. """ # note that self._refitem__ is already bounds checking - var ref = Reference[T, __mlir_attr.`1: i1`, __lifetime_of(self)]( + var r = Reference[T, __mlir_attr.`1: i1`, __lifetime_of(self)]( UnsafePointer(self._refitem__(idx))[] ) - ref[] = value + r[] = value @always_inline fn __getitem__(self, slice: Slice) -> Self: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 9ab4141222..28e37baae9 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -331,9 +331,9 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): @parameter for i in range(size): - var ref = self._get_reference_unsafe(i) + var eltref = self._get_reference_unsafe(i) initialize_pointee_move( - UnsafePointer[Self.ElementType](ref), elems[i] + UnsafePointer[Self.ElementType](eltref), elems[i] ) # ===------------------------------------------------------------------===# From 0c06c37069e7eb96edb3e025381a8f1d6a25266e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 25 May 2024 21:19:52 -0700 Subject: [PATCH 0692/2019] [mojo-stdlib] Move most things off refitem to getitem. Now that we can return a reference from a function, we can eliminate refitem. This moves everything except pointer and reference types, which need support for address spaces in the returned reference. This also adds support for refresult in calls in parameter contexts. MODULAR_ORIG_COMMIT_REV_ID: 9c328b12d498308a9a81c678603b909885fd273a --- docs/changelog.md | 14 +++++++++ stdlib/src/builtin/builtin_list.mojo | 42 ++++++------------------- stdlib/src/builtin/string.mojo | 2 +- stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/inline_list.mojo | 8 ++--- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/arc.mojo | 6 ++-- stdlib/src/utils/static_tuple.mojo | 20 ++++++------ stdlib/src/utils/variant.mojo | 8 ++--- 9 files changed, 45 insertions(+), 59 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ed4b6d8330..7265674c07 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -22,6 +22,20 @@ what we publish. [PR #2609](https://github.com/modularml/mojo/pull/2609) by [@mzaks](https://github.com/mzaks) +- Mojo functions can return an auto-dereferenced refeference to storage with a + new `ref` keyword in the result type specifier. For example: + + ```mojo + struct Pair: + var first: Int + var second: Int + fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + return self.first + fn show_mutation(): + var somePair = ... + get_first_ref(somePair) = 1 + ``` + - Mojo has introduced `@parameter for`, a new feature for compile-time programming. `@parameter for` defines a for loop where the sequence and the induction values in the sequence must be parameter values. For example: diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index d25572f336..0135d962f8 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -194,7 +194,8 @@ struct _VariadicListMemIter[ self.index += 1 # TODO: Need to make this return a dereferenced reference, not a # reference that must be deref'd by the user. - return self.src[].__getitem__(self.index - 1) + # NOTE: Using UnsafePointer here to get lifetimes to match. + return UnsafePointer.address_of(self.src[][self.index - 1])[] fn __len__(self) -> Int: return len(self.src[]) - self.index @@ -355,29 +356,10 @@ struct VariadicListMem[ """ return __mlir_op.`pop.variadic.size`(self.value) - # TODO: Fix for loops + _VariadicListIter to support a __nextref__ protocol - # allowing us to get rid of this and make foreach iteration clean. @always_inline - fn __getitem__(self, idx: Int) -> Self.reference_type: - """Gets a single element on the variadic list. - - Args: - idx: The index of the element to access on the list. - - Returns: - A low-level pointer to the element on the list corresponding to the - given index. - """ - return Self.reference_type( - __mlir_op.`pop.variadic.get`(self.value, idx.value) - ) - - @always_inline - fn __refitem__( + fn __getitem__( self, idx: Int - ) -> Reference[ - element_type, - Bool {value: elt_is_mutable}, + ) -> ref [ _lit_lifetime_union[ Bool {value: elt_is_mutable}, lifetime, @@ -387,8 +369,8 @@ struct VariadicListMem[ _lit_mut_cast[ False, __lifetime_of(self), Bool {value: elt_is_mutable} ].result, - ].result, - ]: + ].result + ] element_type: """Gets a single element on the variadic list. Args: @@ -398,7 +380,7 @@ struct VariadicListMem[ A low-level pointer to the element on the list corresponding to the given index. """ - return __mlir_op.`pop.variadic.get`(self.value, idx.value) + return Reference(__mlir_op.`pop.variadic.get`(self.value, idx.value))[] fn __iter__( self, @@ -590,13 +572,9 @@ struct VariadicPack[ return Self.__len__() @always_inline - fn __refitem__[ + fn __getitem__[ index: Int - ](self) -> Reference[ - element_types[index.value], - Bool {value: Self.elt_is_mutable}, - Self.lifetime, - ]: + ](self) -> ref [Self.lifetime] element_types[index.value]: """Return a reference to an element of the pack. Parameters: @@ -618,7 +596,7 @@ struct VariadicPack[ Bool {value: Self.elt_is_mutable}, Self.lifetime, ] - return rebind[result_ref._mlir_type](ref_elt) + return Reference(rebind[result_ref._mlir_type](ref_elt))[] @always_inline fn each[func: fn[T: element_trait] (T) capturing -> None](self): diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 6141ecf44d..d8d713f050 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -236,7 +236,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: alias ord_0 = ord("0") # FIXME: - # Change this to `alias` after fixing support for __refitem__ of alias. + # Change this to `alias` after fixing support for __getitem__ of alias. var ord_letter_min = (ord("a"), ord("A")) alias ord_underscore = ord("_") diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 9d348ecc04..e3977e1371 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -546,7 +546,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return self._find_ref(key)[] - # TODO(MSTDL-452): rename to __refitem__ + # TODO(MSTDL-452): rename to __getitem__ returning a reference fn __get_ref( self: Reference[Self, _, _], key: K ) raises -> Reference[V, self.is_mutable, self.lifetime]: diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index e53b9f0f06..b1ccfcc860 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -58,10 +58,10 @@ struct _InlineListIter[ @parameter if forward: self.index += 1 - return self.src[].__refitem__(self.index - 1) + return self.src[][self.index - 1] else: self.index -= 1 - return self.src[].__refitem__(self.index) + return self.src[][self.index] fn __len__(self) -> Int: @parameter @@ -128,9 +128,9 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): self._size += 1 @always_inline - fn __refitem__( + fn __getitem__( self: Reference[Self, _, _], owned idx: Int - ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: + ) -> ref [self.lifetime] Self.ElementType: """Get a `Reference` to the element at the given index. Args: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 2a890ceaab..e4eaa7d8f9 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -772,7 +772,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return (self.data + normalized_idx)[] - # TODO(30737): Replace __getitem__ with this as __refitem__, but lots of places use it + # TODO(30737): Replace __getitem__ with this, but lots of places use it fn __get_ref( self: Reference[Self, _, _], i: Int ) -> Reference[T, self.is_mutable, self.lifetime]: diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 836bd4a0a6..ffa3354a71 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -101,9 +101,7 @@ struct Arc[T: Movable](CollectionElement): # FIXME: This isn't right - the element should be mutable regardless # of whether the 'self' type is mutable. - fn __refitem__( - self: Reference[Self, _, _] - ) -> Reference[T, self.is_mutable, self.lifetime]: + fn __getitem__(self: Reference[Self, _, _]) -> ref [self.lifetime] T: """Returns a Reference to the managed value. Returns: @@ -117,4 +115,4 @@ struct Arc[T: Movable](CollectionElement): Returns: The UnsafePointer to the underlying memory. """ - return UnsafePointer.address_of(self._inner[].payload) + return UnsafePointer.address_of(self._inner[].payload)[] diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 28e37baae9..91a31c203e 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -341,11 +341,11 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __refitem__[ + fn __getitem__[ IntableType: Intable, - ](self: Reference[Self, _, _], index: IntableType) -> Reference[ - Self.ElementType, self.is_mutable, self.lifetime - ]: + ](self: Reference[Self, _, _], index: IntableType) -> ref [ + self.lifetime + ] Self.ElementType: """Get a `Reference` to the element at the given index. Parameters: @@ -362,15 +362,13 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): if normalized_idx < 0: normalized_idx += size - return self[]._get_reference_unsafe(normalized_idx) + return self[]._get_reference_unsafe(normalized_idx)[] @always_inline("nodebug") - fn __refitem__[ + fn __getitem__[ IntableType: Intable, index: IntableType, - ](self: Reference[Self, _, _]) -> Reference[ - Self.ElementType, self.is_mutable, self.lifetime - ]: + ](self: Reference[Self, _, _]) -> ref [self.lifetime] Self.ElementType: """Get a `Reference` to the element at the given index. Parameters: @@ -389,7 +387,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): if i < 0: normalized_idx += size - return self[]._get_reference_unsafe(normalized_idx) + return self[]._get_reference_unsafe(normalized_idx)[] # ===------------------------------------------------------------------=== # # Trait implementations @@ -414,7 +412,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: """Get a reference to an element of self without checking index bounds. - Users should opt for `__refitem__` instead of this method. + Users should opt for `__getitem__` instead of this method. """ var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(self[]._array).address, diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 3adcf36ed7..b1dcc7cd57 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -210,11 +210,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): # Operator dunders # ===-------------------------------------------------------------------===# - fn __refitem__[ + fn __getitem__[ T: CollectionElement - ](self: Reference[Self, _, _]) -> Reference[ - T, self.is_mutable, self.lifetime - ]: + ](self: Reference[Self, _, _]) -> ref [self.lifetime] T: """Get the value out of the variant as a type-checked type. This explicitly check that your value is of that type! @@ -233,7 +231,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): if not self[].isa[T](): abort("get: wrong variant type") - return self[].unsafe_get[T]() + return self[].unsafe_get[T]()[] # ===-------------------------------------------------------------------===# # Methods From 4dd021243e7ddc4ba3ee6894fa30f57bd81e66c7 Mon Sep 17 00:00:00 2001 From: northstreet12 Date: Sun, 26 May 2024 00:17:19 -0500 Subject: [PATCH 0693/2019] [External] [stdlib] Fix atol() when explicitly setting base 2, 8, and 16 (#40634) [External] [stdlib] Fix atol() when explicitly setting base 2, 8, and 16 Fix `atol()` when explicitly setting base 2, 8, and 16. ``` atol(" 0xff ", 16) atol(" 0o12 ", 8) ``` Co-authored-by: northstreet12 Closes modularml/mojo#2650 MODULAR_ORIG_COMMIT_REV_ID: 1c35383e9a4fe74b60e60be82c9149df611a4ae5 --- stdlib/src/builtin/string.mojo | 14 ++++++++++++++ stdlib/test/builtin/test_string.mojo | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index d8d713f050..6f8abac2c1 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -234,6 +234,20 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: start = pos break + if str_ref[start] == "0" and start + 1 < str_len: + if base == 2 and ( + str_ref[start + 1] == "b" or str_ref[start + 1] == "B" + ): + start += 2 + elif base == 8 and ( + str_ref[start + 1] == "o" or str_ref[start + 1] == "O" + ): + start += 2 + elif base == 16 and ( + str_ref[start + 1] == "x" or str_ref[start + 1] == "X" + ): + start += 2 + alias ord_0 = ord("0") # FIXME: # Change this to `alias` after fixing support for __getitem__ of alias. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 6cba68983d..11b9631b22 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -306,7 +306,14 @@ fn test_atol() raises: assert_equal(10, atol("A", 16)) assert_equal(15, atol("f ", 16)) assert_equal(255, atol(" FF", 16)) + assert_equal(255, atol(" 0xff ", 16)) + assert_equal(255, atol(" 0Xff ", 16)) assert_equal(18, atol("10010", 2)) + assert_equal(18, atol("0b10010", 2)) + assert_equal(18, atol("0B10010", 2)) + assert_equal(10, atol("12", 8)) + assert_equal(10, atol("0o12", 8)) + assert_equal(10, atol("0O12", 8)) assert_equal(35, atol("Z", 36)) # Negative cases From 39763847206fcdf1cf70b6681d1dd006c0fcb100 Mon Sep 17 00:00:00 2001 From: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Date: Sun, 26 May 2024 07:25:45 -0500 Subject: [PATCH 0694/2019] [External] [stdlib] implement `bit_ceil` and `bit_floor` (#40487) [External] [stdlib] implement `bit_ceil` and `bit_floor` fix #2682 Co-authored-by: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Closes modularml/mojo#2736 MODULAR_ORIG_COMMIT_REV_ID: 3599b7ce4aba5d369ffb7e0fc09d1571dfb53f1e --- stdlib/src/bit/__init__.mojo | 2 + stdlib/src/bit/bit.mojo | 128 +++++++++++++++++++++++++++++++++- stdlib/test/bit/test_bit.mojo | 72 ++++++++++++++++++- 3 files changed, 199 insertions(+), 3 deletions(-) diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index 98c17fdf64..5b4ef1ddce 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -22,4 +22,6 @@ from .bit import ( bit_width, rotate_bits_left, rotate_bits_right, + bit_ceil, + bit_floor, ) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index adc685e517..31e26378f8 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -253,11 +253,26 @@ fn bit_not[ # ===----------------------------------------------------------------------===# +@always_inline +fn bit_width(val: Int) -> Int: + """Computes the minimum number of bits required to represent the integer. + + Args: + val: The input value. + + Returns: + The number of bits required to represent the integer. + """ + alias bitwidth = bitwidthof[Int]() + + return bitwidth - countl_zero(~val if val < 0 else val) + + @always_inline fn bit_width[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Computes the minimum number of digits required to represent the integer. + """Computes the minimum number of bits required to represent the integer. Parameters: type: `dtype` used for the computation. @@ -271,7 +286,7 @@ fn bit_width[ Returns: A SIMD value where the element at position `i` equals to the number of - digits required to represent the integer at position `i` of the input + bits required to represent the integer at position `i` of the input value. """ @@ -289,6 +304,115 @@ fn bit_width[ return bitwidth - leading_zero +# ===----------------------------------------------------------------------===# +# bit_ceil +# ===----------------------------------------------------------------------===# +# reference: https://en.cppreference.com/w/cpp/numeric/bit_ceil + + +@always_inline("nodebug") +fn bit_ceil(val: Int) -> Int: + """Computes the smallest power of 2 that is greater than or equal to the + input value. Any integral value less than or equal to 1 be ceiled to 1. + + Args: + val: The input value. + + Returns: + The smallest power of 2 that is greater than or equal to the input value. + """ + if val <= 1: + return 1 + + if val & (val - 1) == 0: + return val + + return 1 << bit_width(val - 1) + + +@always_inline("nodebug") +fn bit_ceil[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the smallest power of 2 that is greater than or equal to the + input value for each element of a SIMD vector. Any integral value less than + or equal to 1 will be ceiled to 1. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` is the smallest power of 2 + that is greater than or equal to the integer at position `i` of the input + value. + """ + constrained[type.is_integral(), "must be integral"]() + + alias ones = SIMD[type, simd_width].splat(1) + + return (val > 1).select(1 << bit_width(val - ones), ones) + + +# ===----------------------------------------------------------------------===# +# bit_floor +# ===----------------------------------------------------------------------===# +# reference: https://en.cppreference.com/w/cpp/numeric/bit_floor + + +@always_inline("nodebug") +fn bit_floor(val: Int) -> Int: + """Computes the largest power of 2 that is less than or equal to the input + value. Any integral value less than or equal to 0 will be floored to 0. + + Args: + val: The input value. + + Returns: + The largest power of 2 that is less than or equal to the input value. + """ + if val <= 0: + return 0 + + return 1 << (bit_width(val) - 1) + + +@always_inline("nodebug") +fn bit_floor[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the largest power of 2 that is less than or equal to the input + value for each element of a SIMD vector. Any integral value less than or + equal to 0 will be floored to 0. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` is the largest power of 2 + that is less than or equal to the integer at position `i` of the input + value. + """ + constrained[type.is_integral(), "must be integral and unsigned"]() + + alias zeros = SIMD[type, simd_width].splat(0) + + return (val > 0).select(1 << (bit_width(val) - 1), zeros) + + # ===----------------------------------------------------------------------===# # rotate_bits_left # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 106a73efcf..1378d6a81e 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -12,11 +12,75 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from bit import rotate_bits_left, rotate_bits_right +from bit import ( + rotate_bits_left, + rotate_bits_right, + bit_width, + bit_ceil, + bit_floor, +) from testing import assert_equal +def test_bit_width(): + assert_equal(bit_width(-2), 1) + assert_equal(bit_width(-1), 0) + assert_equal(bit_width(1), 1) + assert_equal(bit_width(2), 2) + assert_equal(bit_width(4), 3) + assert_equal(bit_width(5), 3) + + +def test_bit_width_simd(): + alias simd_width = 4 + alias type = DType.int8 + + alias var1 = SIMD[type, simd_width](-2, -1, 3, 4) + assert_equal(bit_width(var1), SIMD[type, simd_width](1, 0, 2, 3)) + + alias var2 = SIMD[type, simd_width](1, 2, 3, 4) + assert_equal(bit_width(var2), SIMD[type, simd_width](1, 2, 2, 3)) + + +def test_bit_ceil(): + assert_equal(bit_ceil(-2), 1) + assert_equal(bit_ceil(1), 1) + assert_equal(bit_ceil(2), 2) + assert_equal(bit_ceil(4), 4) + assert_equal(bit_ceil(5), 8) + + +def test_bit_ceil_simd(): + alias simd_width = 4 + alias type = DType.int8 + + alias var1 = SIMD[type, simd_width](-2, -1, 3, 4) + assert_equal(bit_ceil(var1), SIMD[type, simd_width](1, 1, 4, 4)) + + alias var2 = SIMD[type, simd_width](1, 2, 3, 4) + assert_equal(bit_ceil(var2), SIMD[type, simd_width](1, 2, 4, 4)) + + +def test_bit_floor(): + assert_equal(bit_floor(-2), 0) + assert_equal(bit_floor(1), 1) + assert_equal(bit_floor(2), 2) + assert_equal(bit_floor(4), 4) + assert_equal(bit_floor(5), 4) + + +def test_bit_floor_simd(): + alias simd_width = 4 + alias type = DType.int8 + + alias var1 = SIMD[type, simd_width](-1, -2, 3, 4) + assert_equal(bit_floor(var1), SIMD[type, simd_width](0, 0, 2, 4)) + + alias var2 = SIMD[type, simd_width](4, 5, 6, 7) + assert_equal(bit_floor(var2), SIMD[type, simd_width](4, 4, 4, 4)) + + def test_rotate_bits_int(): assert_equal(rotate_bits_left[0](104), 104) assert_equal(rotate_bits_left[2](104), 416) @@ -66,3 +130,9 @@ def test_rotate_bits_simd(): def main(): test_rotate_bits_int() test_rotate_bits_simd() + test_bit_ceil() + test_bit_ceil_simd() + test_bit_floor() + test_bit_floor_simd() + test_bit_width() + test_bit_width_simd() From 990e9bdaa7840b0dc00abc28657d94a752005ece Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Sun, 26 May 2024 10:58:21 -0500 Subject: [PATCH 0695/2019] [External] [stdlib] Change Tuple.__contains__ to T:EqualityComparable instead of T:ComparableCollectionElement (#40640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Change Tuple.__contains__ to T:EqualityComparable instead of T:ComparableCollectionElement Hello, `ComparableCollectionElement` requires many methods unused by `Tuple.__contains__`: *1️⃣`__eq__`, 2️⃣`__ne__`, 3️⃣`__gt__`, 4️⃣`__ge__`, 5️⃣`__lt__`, 6️⃣`__le__`, `7️⃣__copyinit__`, `8️⃣__moveinit__`*.   `Tuple.__contains__` only uses 1️⃣`__eq__` and `EqualityComparable` requires 1️⃣`__eq__`, 2️⃣`__ne__`.   That way, users can easily create types ready for `Tuple.__contains__` if needed, for example: ```mojo @value struct MyStruct(EqualityComparable): alias A = MyStruct(True) alias B = MyStruct(False) var value:Bool fn __eq__(self,other:Self)->Bool: return self.value==other.value # ✅1️⃣ fn __ne__(self,other:Self)->Bool: return not self==other # ✅2️⃣ fn SomeTuple()-> (MyStruct, Int): return (MyStruct.A,1) fn main(): if MyStruct.A in SomeTuple(): print("A") ``` Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2833 MODULAR_ORIG_COMMIT_REV_ID: c6afc9b454e0db1ee3935205d5d94648563ed2d2 --- stdlib/src/builtin/tuple.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 885220c040..5873290944 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -190,7 +190,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): return rebind[T](self[i]) @always_inline("nodebug") - fn __contains__[T: ComparableCollectionElement](self, value: T) -> Bool: + fn __contains__[T: EqualityComparable](self, value: T) -> Bool: """Verify if a given value is present in the tuple. ```mojo @@ -203,7 +203,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): Parameters: T: The type of the value argument. Must implement the - trait `ComparableCollectionElement`. + trait `EqualityComparable`. Returns: True if the value is contained in the tuple, False otherwise. From 3a2f357c6a6d4384c49290a4f09ddf49f7a19bc5 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Sun, 26 May 2024 11:54:14 -0500 Subject: [PATCH 0696/2019] [External] [stdlib] Add the new `Hasher` trait with some unit tests (#40624) [External] [stdlib] Add the new `Hasher` trait with some unit tests This was done in cooperation with @mzaks. We want to introduce the new hasher progressively to make it easier to review and flush out the compiler bugs. New names will be used to avoid conflicts during the transition. The source PR is this one: https://github.com/modularml/mojo/pull/2619 Co-authored-by: Gabriel de Marmiesse Co-authored-by: Maxim Zaks Closes https://github.com/modularml/mojo/pull/2823 MODULAR_ORIG_COMMIT_REV_ID: 67371ec3b0c3eb886896c2a030567d465d187efa --- stdlib/src/builtin/_hasher.mojo | 40 +++++++++++++ stdlib/test/builtin/test_hasher.mojo | 86 ++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 stdlib/src/builtin/_hasher.mojo create mode 100644 stdlib/test/builtin/test_hasher.mojo diff --git a/stdlib/src/builtin/_hasher.mojo b/stdlib/src/builtin/_hasher.mojo new file mode 100644 index 0000000000..ac9c9e2fd9 --- /dev/null +++ b/stdlib/src/builtin/_hasher.mojo @@ -0,0 +1,40 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +trait _HashableWithHasher: + fn __hash__[H: _Hasher](self, inout hasher: H): + ... + + +trait _Hasher: + fn __init__(inout self): + ... + + fn _update_with_simd(inout self, value: SIMD[_, _]): + ... + + fn update[T: _HashableWithHasher](inout self, value: T): + ... + + fn finish(owned self) -> UInt64: + ... + + +fn _hash_with_hasher[ + HasherType: _Hasher, HashableType: _HashableWithHasher +](hashable: HashableType) -> UInt64: + var hasher = HasherType() + hasher.update(hashable) + var value = hasher^.finish() + return value diff --git a/stdlib/test/builtin/test_hasher.mojo b/stdlib/test/builtin/test_hasher.mojo new file mode 100644 index 0000000000..60335f2c50 --- /dev/null +++ b/stdlib/test/builtin/test_hasher.mojo @@ -0,0 +1,86 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + + +from builtin._hasher import _HashableWithHasher, _Hasher, _hash_with_hasher +from testing import assert_equal + + +struct DummyHasher(_Hasher): + var _dummy_value: UInt64 + + fn __init__(inout self): + self._dummy_value = 0 + + fn _update_with_simd(inout self, value: SIMD[_, _]): + self._dummy_value += value.cast[DType.uint64]().reduce_add() + + fn update[T: _HashableWithHasher](inout self, value: T): + value.__hash__(self) + + fn finish(owned self) -> UInt64: + return self._dummy_value + + +@value +struct SomeHashableStruct(_HashableWithHasher): + var _value: Int64 + + fn __hash__[H: _Hasher](self, inout hasher: H): + hasher._update_with_simd(self._value) + + +def test_hasher(): + var hasher = DummyHasher() + var hashable = SomeHashableStruct(42) + hasher.update(hashable) + assert_equal(hasher^.finish(), 42) + + +def test_hash_with_hasher(): + var hashable = SomeHashableStruct(10) + assert_equal(_hash_with_hasher[DummyHasher](hashable), 10) + + +@value +struct ComplexeHashableStruct(_HashableWithHasher): + var _value1: SomeHashableStruct + var _value2: SomeHashableStruct + + fn __hash__[H: _Hasher](self, inout hasher: H): + hasher.update(self._value1) + hasher.update(self._value2) + + +def test_complex_hasher(): + var hasher = DummyHasher() + var hashable = ComplexeHashableStruct( + SomeHashableStruct(42), SomeHashableStruct(10) + ) + hasher.update(hashable) + assert_equal(hasher^.finish(), 52) + + +def test_complexe_hash_with_hasher(): + var hashable = ComplexeHashableStruct( + SomeHashableStruct(42), SomeHashableStruct(10) + ) + assert_equal(_hash_with_hasher[DummyHasher](hashable), 52) + + +def main(): + test_hasher() + test_hash_with_hasher() + test_complex_hasher() + test_complexe_hash_with_hasher() From 0258a20e354440bc4b02aafa43143aac51801f17 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 26 May 2024 17:26:16 -0400 Subject: [PATCH 0697/2019] [stdlib] Add `tempfile` module This will contain utilities to work with temporary files and directories. MODULAR_ORIG_COMMIT_REV_ID: 3fcf2f8d22fda0b574c11e7accb23981fa30e958 --- docs/changelog.md | 3 +++ stdlib/src/tempfile/__init__.mojo | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 stdlib/src/tempfile/__init__.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 7265674c07..cf896ba491 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -420,6 +420,9 @@ what we publish. - The Mojo Language Server now supports renaming local variables. +- Added a new `tempfile` module. Similarly to Python, this will contain + utilities for creating and working with temporary files and directories. + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/tempfile/__init__.mojo b/stdlib/src/tempfile/__init__.mojo new file mode 100644 index 0000000000..1df906803a --- /dev/null +++ b/stdlib/src/tempfile/__init__.mojo @@ -0,0 +1,13 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the tempfile package.""" From c594986f637af02c81ac7fd61b3309d771ee6e60 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Sun, 26 May 2024 16:39:44 -0600 Subject: [PATCH 0698/2019] [stdlib] Fix docstring script name in style guide The script got renamed and is not a Python script: fix the spelling to avoid contributors. Xref https://github.com/modularml/mojo/pull/2843 too. MODULAR_ORIG_COMMIT_REV_ID: 62ca3b310a018856fdec86aa4c391c86d896f3cf --- stdlib/docs/style-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 3f14fd3129..5c3f7ac129 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -283,7 +283,7 @@ Every public function and public struct (including data fields) in the standard library must have docstrings (code comments that describe the API behavior). Mojo includes tooling to ensure that public functions include docstrings. -You can run `./stdlib/scripts/check-doc-strings.sh` to validate +You can run `./stdlib/scripts/check-docstrings.py` to validate docstrings. If the command exits with a `0` exit code, the docstrings are compliant; otherwise, an error will be shown. This is also enforced by the LSP with warnings for anything that doesn’t conform, you can generate docstrings From 3bf42e71784ecfbd14087dcc1cad36b6b030e3e8 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 26 May 2024 16:16:45 -0700 Subject: [PATCH 0699/2019] [mojo-lang][mojo-stdlib] Remove all uses of refitem. This patch pulls together a few related changes: 1) Introduce support for address spaces to ref results. 2) Introduces support for raising functions with ref results. 3) Switch Reference/*Pointer to use getitem instead of refitem. 4) Remove usage of automatically_dereference/__mlir_ref__. This doesn't remove support for automatically_dereference or refitem, that will be a subsequent patch. MODULAR_ORIG_COMMIT_REV_ID: b42733d7740ff06acff4cb4fc2198bf8f2ee06d6 --- stdlib/src/memory/reference.mojo | 23 +++++++++++------------ stdlib/src/memory/unsafe.mojo | 26 ++++++++++++++++---------- stdlib/src/memory/unsafe_pointer.mojo | 18 ++++++++++++------ stdlib/test/utils/test_tuple.mojo | 2 +- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index cd02236411..6f2bffe78b 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -154,6 +154,15 @@ struct AddressSpace(EqualityComparable): """ return self._value + @always_inline("nodebug") + fn __mlir_index__(self) -> __mlir_type.index: + """Convert to index. + + Returns: + The corresponding __mlir_type.index value. + """ + return self._value.value + @always_inline("nodebug") fn __eq__(self, other: Self) -> Bool: """True if the two address spaces are equal and False otherwise. @@ -185,7 +194,6 @@ struct AddressSpace(EqualityComparable): @value -@automatically_dereference @register_passable("trivial") struct Reference[ type: AnyType, @@ -233,19 +241,10 @@ struct Reference[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __refitem__(self) -> Self: + fn __getitem__(self) -> ref [lifetime, address_space._value.value] type: """Enable subscript syntax `ref[]` to access the element. Returns: The MLIR reference for the Mojo compiler to use. """ - return self - - @always_inline("nodebug") - fn __mlir_ref__(self) -> Self._mlir_type: - """Enable the Mojo compiler to see into `Reference`. - - Returns: - The MLIR reference for the Mojo compiler to use. - """ - return self.value + return __get_litref_as_mvalue(self.value) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index a2aa78b162..21d89edd1d 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -278,27 +278,33 @@ struct LegacyPointer[ ) @always_inline("nodebug") - fn __refitem__(self) -> Self._ref_type: - """Enable subscript syntax `ref[]` to access the element. + fn __getitem__( + self, + ) -> ref [MutableStaticLifetime, address_space._value.value] type: + """Enable subscript syntax `ptr[]` to access the element. Returns: - The MLIR reference for the Mojo compiler to use. + The reference for the Mojo compiler to use. """ - return __mlir_op.`lit.ref.from_pointer`[ - _type = Self._ref_type._mlir_type - ](self.address) + return __get_litref_as_mvalue( + __mlir_op.`lit.ref.from_pointer`[_type = Self._ref_type._mlir_type]( + self.address + ) + ) @always_inline("nodebug") - fn __refitem__(self, offset: Int) -> Self._ref_type: - """Enable subscript syntax `ref[idx]` to access the element. + fn __getitem__( + self, offset: Int + ) -> ref [MutableStaticLifetime, address_space._value.value] type: + """Enable subscript syntax `ptr[idx]` to access the element. Args: offset: The offset to load from. Returns: - The MLIR reference for the Mojo compiler to use. + The reference for the Mojo compiler to use. """ - return (self + offset).__refitem__() + return (self + offset)[] # ===------------------------------------------------------------------=== # # Load/Store diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 41a2633b7d..fca93be869 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -185,18 +185,24 @@ struct UnsafePointer[ # ===-------------------------------------------------------------------===# @always_inline - fn __refitem__(self) -> Self._ref_type: + fn __getitem__( + self, + ) -> ref [MutableStaticLifetime, address_space._value.value] T: """Return a reference to the underlying data. Returns: A reference to the value. """ - return __mlir_op.`lit.ref.from_pointer`[ - _type = Self._ref_type._mlir_type - ](self.address) + return __get_litref_as_mvalue( + __mlir_op.`lit.ref.from_pointer`[_type = Self._ref_type._mlir_type]( + self.address + ) + ) @always_inline - fn __refitem__(self, offset: Int) -> Self._ref_type: + fn __getitem__( + self, offset: Int + ) -> ref [MutableStaticLifetime, address_space._value.value] T: """Return a reference to the underlying data, offset by the given index. Args: @@ -205,7 +211,7 @@ struct UnsafePointer[ Returns: An offset reference. """ - return (self + offset).__refitem__() + return (self + offset)[] @always_inline fn __add__(self, offset: Int) -> Self: diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index d80a5902b4..75e98ac8ce 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -131,7 +131,7 @@ def test_array_str(): assert_equal(arr[1], "hello") assert_equal(arr[2], "hey") - # Test mutating an array through its __refitem__ + # Test mutating an array through its __getitem__ arr[0] = "howdy" arr[1] = "morning" arr[2] = "wazzup" From 136d8895d116e5bb1abb6997620bc17029d6dc12 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 26 May 2024 17:41:11 -0700 Subject: [PATCH 0700/2019] [mojo-lang] Remove a bunch of old reference stuff This removes the `isAutoDereference` bit of LIT::StructDeclOp, the `__mlir_ref__` support, the `@automatically_dereference` decorator, and the `__refitem__` method support. This also updates the changelog to reflect the removal of refitem. MODULAR_ORIG_COMMIT_REV_ID: ba864d8c988c193477bd93d362404154a8ee4642 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index cf896ba491..9beb519b11 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -36,6 +36,11 @@ what we publish. get_first_ref(somePair) = 1 ``` + This approach provides a general way to return an "automatically dereferenced" + reference of a given type. Notably, this eliminates the need for + `__refitem__` to exist. `__refitem__` has thus been removed and replaced with + `__getitem__` that returns a reference. + - Mojo has introduced `@parameter for`, a new feature for compile-time programming. `@parameter for` defines a for loop where the sequence and the induction values in the sequence must be parameter values. For example: From f591d4295094ab1a1dc54afa2532a3f55b947a10 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sun, 26 May 2024 23:38:37 -0700 Subject: [PATCH 0701/2019] [stdlib] Don't mark `SIMD.__abs__` as `nodebug` inline Functions marked as `@always_inline("nodebug")` are meant to be small, zero-cost abstractions. The implementation of `SIMD.__abs__` is neither. It isn't small (contains branches) and it isn't zero-cost, because the verifier for `pop.neg` triggers if the function gets inlined and pre-specialized. MODULAR_ORIG_COMMIT_REV_ID: 3b749024aba0af0656a625880e6178d4ba0fcec0 --- stdlib/src/builtin/simd.mojo | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 1373735f75..22cb94f1ef 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -977,7 +977,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var v = bitcast[integral_type, size](self) return v.cast[dest_type]() - @always_inline("nodebug") + @always_inline fn __abs__(self) -> Self: """Defines the absolute value operation. @@ -988,14 +988,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type.is_unsigned() or type.is_bool(): return self - - @parameter - if type.is_floating_point(): + elif type.is_floating_point(): alias integral_type = FPUtils[type].integral_type var m = self._float_to_bits[integral_type]() return (m & (FPUtils[type].sign_mask() - 1))._bits_to_float[type]() - - return (self < 0).select(-self, self) + else: + return (self < 0).select(-self, self) fn _floor_ceil_trunc_impl[intrinsic: StringLiteral](self) -> Self: constrained[ From e36965c195b0edbbb74067520ee2a57dfc894e14 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 05:47:33 -0400 Subject: [PATCH 0702/2019] [stdlib] Remove rarely used overload of `math.pow` Users can easily write this if they want to (recursively using multiplication and `@parameter if`). MODULAR_ORIG_COMMIT_REV_ID: b1ab22b205fc090fb18cefc2a7084dd28e339edd --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9beb519b11..442eec429c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -554,6 +554,9 @@ what we publish. - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. +- The overload of `math.pow` taking an integer parameter exponent has been + removed. + ### 🛠️ Fixed - [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential From fa7836c84c96a4110d0613fcc586b253c3e85f83 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 05:58:28 -0400 Subject: [PATCH 0703/2019] [stdlib][NFC] Remove unnecessary `_div_ceil_positive` function And add a TODO to another one to remove it later in favor of `math.ceildiv`. MODULAR_ORIG_COMMIT_REV_ID: 8f7f21f9afcff528363b69eedaed34a932b0db63 --- stdlib/src/builtin/hash.mojo | 9 --------- stdlib/src/builtin/range.mojo | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 143b333662..8381df9e17 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -34,15 +34,6 @@ from memory import memcpy, memset_zero, stack_allocation # TODO remove this import onece InlineArray is moved to collections from utils import InlineArray -# ===----------------------------------------------------------------------=== # -# Utilities -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: - return (numerator + denominator - 1)._positive_div(denominator) - # ===----------------------------------------------------------------------=== # # Implementation diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 2756d944d7..bd72e13aaa 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -26,6 +26,7 @@ from builtin._stubs import _IntIterable, _StridedIterable # ===----------------------------------------------------------------------=== # +# TODO: use math.ceildiv when open sourced. @always_inline fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: """Divides an integer by another integer, and round up to the nearest From 97d0075e2d781a51095c734b4ca52d958788a308 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 06:17:32 -0400 Subject: [PATCH 0704/2019] [stdlib] Remove `Slice.__len__` The semantics of this method are ambiguous since the length of a slice always depends on the object being sliced. An equalivalent unsafe method is added for compatibility. MODULAR_ORIG_COMMIT_REV_ID: ad053736563f0aecc09f3f9a3b546c9d000c66ec --- docs/changelog.md | 7 +++++++ stdlib/src/builtin/builtin_slice.mojo | 8 +++++--- stdlib/src/builtin/string.mojo | 7 ++----- stdlib/src/collections/list.mojo | 6 +++--- stdlib/src/utils/span.mojo | 8 ++++---- stdlib/test/utils/test_string_slice.mojo | 16 ++++++++-------- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 442eec429c..e90c39c754 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -503,6 +503,13 @@ what we publish. - `InlinedString` has been renamed to `InlineString` to be consistent with other types. +- The `Slice.__len__` function has been removed and `Slice` no longer conforms + to the `Sized` trait. This clarifies the ambiguity of the semantics: the + length of a slice always depends on the length of the object being sliced. + Users that need the existing functionality can use the `Slice.unsafe_indices` + method. This makes it explicit that this implementation does not check if the + slice bounds are concrete or within any given object's length. + ### ❌ Removed - The `@unroll` decorator has been deprecated and removed. The decorator was diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 4b49cb5bb0..b872933dbe 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -40,7 +40,7 @@ fn _default_or[T: AnyRegType](value: T, default: Int) -> Int: @register_passable("trivial") -struct Slice(Sized, Stringable, EqualityComparable): +struct Slice(Stringable, EqualityComparable): """Represents a slice expression. Objects of this type are generated when slice syntax is used within square @@ -138,10 +138,12 @@ struct Slice(Sized, Stringable, EqualityComparable): """ return not (self == other) - @always_inline("nodebug") - fn __len__(self) -> Int: + @always_inline + fn unsafe_indices(self) -> Int: """Return the length of the slice. + Only use this function if start/end is guaranteed to be not None. + Returns: The length of the slice. """ diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 6f8abac2c1..6d489e87e9 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -912,14 +912,11 @@ struct String( """ var adjusted_span = self._adjust_span(span) + var adjusted_span_len = adjusted_span.unsafe_indices() if adjusted_span.step == 1: - return StringRef( - self._buffer.data + span.start, - len(adjusted_span), - ) + return StringRef(self._buffer.data + span.start, adjusted_span_len) var buffer = Self._buffer_type() - var adjusted_span_len = len(adjusted_span) buffer.resize(adjusted_span_len + 1, 0) var ptr = self.unsafe_uint8_ptr() for i in range(adjusted_span_len): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index e4eaa7d8f9..1d1cc7359b 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -739,13 +739,13 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ var adjusted_span = self._adjust_span(span) - var adjusted_span_len = len(adjusted_span) + var adjusted_span_len = adjusted_span.unsafe_indices() if not adjusted_span_len: return Self() - var res = Self(capacity=len(adjusted_span)) - for i in range(len(adjusted_span)): + var res = Self(capacity=adjusted_span_len) + for i in range(adjusted_span_len): res.append(self[adjusted_span[i]]) return res^ diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 20af861891..02fd399ffb 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -170,16 +170,16 @@ struct Span[ r[] = value @always_inline - fn __getitem__(self, slice: Slice) -> Self: + fn __getitem__(self, slc: Slice) -> Self: """Get a new span from a slice of the current span. Args: - slice: The slice specifying the range of the new subslice. + slc: The slice specifying the range of the new subslice. Returns: A new span that points to the same data as the current span. """ - var adjusted_span = self._adjust_span(slice) + var adjusted_span = self._adjust_span(slc) debug_assert( 0 <= adjusted_span.start <= self._len and 0 <= adjusted_span.end <= self._len, @@ -187,7 +187,7 @@ struct Span[ ) var res = Self( unsafe_ptr=(self._data + adjusted_span.start), - len=len(adjusted_span), + len=adjusted_span.unsafe_indices(), ) return res diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 3f977f365b..a2e616f0bf 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -19,14 +19,14 @@ from utils import Span fn test_string_literal_byte_slice() raises: alias string: StringLiteral = "Hello" - alias slice = string.as_bytes_slice() - - assert_equal(len(slice), 5) - assert_equal(slice[0][], ord("H")) - assert_equal(slice[1][], ord("e")) - assert_equal(slice[2][], ord("l")) - assert_equal(slice[3][], ord("l")) - assert_equal(slice[4][], ord("o")) + alias slc = string.as_bytes_slice() + + assert_equal(len(slc), 5) + assert_equal(slc[0][], ord("H")) + assert_equal(slc[1][], ord("e")) + assert_equal(slc[2][], ord("l")) + assert_equal(slc[3][], ord("l")) + assert_equal(slc[4][], ord("o")) fn test_string_byte_slice() raises: From bac113948c625f8f140f2cfa4dbe69d77d521611 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 08:17:39 -0400 Subject: [PATCH 0705/2019] [stdlib][docs][NFC] Rename `pow` in some examples To make sure they don't conflict with `pow` when it becomes a builtin MODULAR_ORIG_COMMIT_REV_ID: dea3d2c8ae680b3609ca15f2b09fc0b80b9768ae --- docs/manual/functions.ipynb | 8 ++++---- examples/notebooks/HelloMojo.ipynb | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 2f97a69439..ee7a1cadd3 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -222,12 +222,12 @@ "metadata": {}, "outputs": [], "source": [ - "fn pow(base: Int, exp: Int = 2) -> Int:\n", + "fn my_pow(base: Int, exp: Int = 2) -> Int:\n", " return base ** exp\n", "\n", "fn use_defaults():\n", " # Uses the default value for `exp`\n", - " var z = pow(3)\n", + " var z = my_pow(3)\n", " print(z)" ] }, @@ -260,12 +260,12 @@ "metadata": {}, "outputs": [], "source": [ - "fn pow(base: Int, exp: Int = 2) -> Int:\n", + "fn my_pow(base: Int, exp: Int = 2) -> Int:\n", " return base ** exp\n", "\n", "fn use_keywords():\n", " # Uses keyword argument names (with order reversed)\n", - " var z = pow(exp=3, base=2)\n", + " var z = my_pow(exp=3, base=2)\n", " print(z)" ] }, diff --git a/examples/notebooks/HelloMojo.ipynb b/examples/notebooks/HelloMojo.ipynb index 0159f99c22..43c01875cf 100644 --- a/examples/notebooks/HelloMojo.ipynb +++ b/examples/notebooks/HelloMojo.ipynb @@ -301,15 +301,15 @@ } ], "source": [ - "fn pow(base: Int, exp: Int = 2) -> Int:\n", + "fn my_pow(base: Int, exp: Int = 2) -> Int:\n", " return base ** exp\n", "\n", "# Uses default value for `exp`\n", - "z = pow(3)\n", + "z = my_pow(3)\n", "print(z)\n", "\n", "# Uses keyword argument names (with order reversed)\n", - "z = pow(exp=3, base=2)\n", + "z = my_pow(exp=3, base=2)\n", "print(z)" ] }, From b74d39ff3d77b131984c00fa52a7ff4ddaebe7fb Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 09:15:54 -0400 Subject: [PATCH 0706/2019] [stdlib] Simplify `pow` implementations This includes changing the argument names to be pythonic. Also cleanup docstrings. MODULAR_ORIG_COMMIT_REV_ID: 49ce4704a41c1e5ab512c8af2cda43003b987374 --- stdlib/src/builtin/int.mojo | 12 ++-- stdlib/src/builtin/math.mojo | 35 ++++++----- stdlib/src/builtin/object.mojo | 6 +- stdlib/src/builtin/simd.mojo | 107 +++++++++++++-------------------- stdlib/src/python/object.mojo | 6 +- 5 files changed, 72 insertions(+), 94 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b35f8fd366..5a19f2b141 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -749,24 +749,24 @@ struct Int( return div, mod @always_inline("nodebug") - fn __pow__(self, rhs: Int) -> Int: - """Return pow(self, rhs). + fn __pow__(self, exp: Int) -> Int: + """Return the value raised to the power of the given exponent. Computes the power of an integer using the Russian Peasant Method. Args: - rhs: The RHS value. + exp: The RHS value. Returns: - The value of `pow(self, rhs)`. + The value of `self` raised to the power of `exp`. """ - if rhs < 0: + if exp < 0: # Not defined for Integers, this should raise an # exception. return 0 var res: Int = 1 var x = self - var n = rhs + var n = exp while n > 0: if n & 1 != 0: res *= x diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 9b13abd80e..70adcdc1bc 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -127,11 +127,11 @@ fn max(x: Int, y: Int) -> Int: """Gets the maximum of two integers. Args: - x: Integer input to max. - y: Integer input to max. + x: Integer input to max. + y: Integer input to max. Returns: - Maximum of x and y. + Maximum of x and y. """ return __mlir_op.`index.maxs`(x.value, y.value) @@ -148,15 +148,15 @@ fn max[ corresponding elements in x and y. Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. Args: - x: First SIMD vector. - y: Second SIMD vector. + x: First SIMD vector. + y: Second SIMD vector. Returns: - A SIMD vector containing the elementwise maximum of x and y. + A SIMD vector containing the elementwise maximum of x and y. """ return x.max(y) @@ -171,11 +171,11 @@ fn min(x: Int, y: Int) -> Int: """Gets the minimum of two integers. Args: - x: Integer input to max. - y: Integer input to max. + x: Integer input to max. + y: Integer input to max. Returns: - Minimum of x and y. + Minimum of x and y. """ return __mlir_op.`index.mins`(x.value, y.value) @@ -192,15 +192,15 @@ fn min[ corresponding elements in x and y. Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. Args: - x: First SIMD vector. - y: Second SIMD vector. + x: First SIMD vector. + y: Second SIMD vector. Returns: - A SIMD vector containing the elementwise minimum of x and y. + A SIMD vector containing the elementwise minimum of x and y. """ return x.min(y) @@ -212,8 +212,7 @@ fn min[ trait Roundable: """ - The `Roundable` trait describes a type that defines an rounded value - operation. + The `Roundable` trait describes a type that defines a rounding operation. Types that conform to `Roundable` will work with the builtin `round` function. The round operation always returns the same type as the input. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 4e814fde23..0bb3d6a0a6 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1288,17 +1288,17 @@ struct object(IntableRaising, Boolable, Stringable): ) @always_inline - fn __pow__(self, rhs: object) raises -> object: + fn __pow__(self, exp: object) raises -> object: """Exponentiation operator. Valid only for arithmetic types. Args: - rhs: Right hand value. + exp: Exponent value. Returns: The left hand value raised to the power of the right hand value. """ return Self._arithmetic_binary_op[Float64.__pow__, Int64.__pow__]( - self, rhs + self, exp ) @always_inline diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 22cb94f1ef..aab64e590b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -784,33 +784,33 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return value % self @always_inline("nodebug") - fn __pow__(self, rhs: Int) -> Self: + fn __pow__(self, exp: Int) -> Self: """Computes the vector raised to the power of the input integer value. Args: - rhs: The exponential value. + exp: The exponent value. Returns: A SIMD vector where each element is raised to the power of the - specified exponential value. + specified exponent value. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return _pow(self, rhs) + return _pow[type, size, DType.index](self, exp) # TODO(#22771): remove this overload. @always_inline("nodebug") - fn __pow__(self, rhs: Self) -> Self: + fn __pow__(self, exp: Self) -> Self: """Computes the vector raised elementwise to the right hand side power. Args: - rhs: The exponential value. + exp: The exponent value. Returns: A SIMD vector where each element is raised to the power of the - specified exponential value. + specified exponent value. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return _pow(self, rhs) + return _pow(self, exp) @always_inline("nodebug") fn __lt__(self, rhs: Self) -> Self._Mask: @@ -2547,61 +2547,40 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ](zero_simd, self, Int32(-shift)) -# ===-------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _pow -# ===-------------------------------------------------------------------===# - - -fn _pow[ - type: DType, simd_width: Int -](arg0: SIMD[type, simd_width], arg1: Int) -> SIMD[type, simd_width]: - """Computes the `pow` of the inputs. - - Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. - - Args: - arg0: The first input argument. - arg1: The second input argument. - - Returns: - The `pow` of the inputs. - """ - return _pow[type, DType.index, simd_width](arg0, arg1) +# ===----------------------------------------------------------------------=== # @always_inline fn _pow[ - lhs_type: DType, rhs_type: DType, simd_width: Int -](lhs: SIMD[lhs_type, simd_width], rhs: SIMD[rhs_type, simd_width]) -> SIMD[ - lhs_type, simd_width -]: - """Computes elementwise power of a type raised to another type. - - An element of the result SIMD vector will be the result of raising the - corresponding element of lhs to the corresponding element of rhs. + BaseTy: DType, simd_width: Int, ExpTy: DType +](base: SIMD[BaseTy, simd_width], exp: SIMD[ExpTy, simd_width]) -> __type_of( + base +): + """Computes the power of the elements of a SIMD vector raised to the + corresponding elements of another SIMD vector. Parameters: - lhs_type: The `dtype` of the lhs SIMD vector. - rhs_type: The `dtype` of the rhs SIMD vector. - simd_width: The width of the input and output SIMD vectors. + BaseTy: The `dtype` of the `base` SIMD vector. + simd_width: The width of the input and output SIMD vectors. + ExpTy: The `dtype` of the `exp` SIMD vector. Args: - lhs: Base of the power operation. - rhs: Exponent of the power operation. + base: Base of the power operation. + exp: Exponent of the power operation. Returns: - A SIMD vector containing elementwise lhs raised to the power of rhs. + A vector containing elementwise `base` raised to the power of `exp`. """ @parameter - if rhs_type.is_floating_point() and lhs_type == rhs_type: - var rhs_quotient = rhs.__floor__() - if all((rhs >= 0) & (rhs_quotient == rhs)): - return _pow(lhs, rhs_quotient.cast[_integral_type_of[rhs_type]()]()) + if ExpTy.is_floating_point() and BaseTy == ExpTy: + var rhs_quotient = exp.__floor__() + if all((exp >= 0) & (rhs_quotient == exp)): + return _pow(base, rhs_quotient.cast[_integral_type_of[ExpTy]()]()) - var result = SIMD[lhs_type, simd_width]() + var result = __type_of(base)() @parameter if triple_is_nvidia_cuda(): @@ -2615,37 +2594,37 @@ fn _pow[ @parameter for i in range(simd_width): result[i] = llvm_intrinsic[ - "llvm.pow", Scalar[lhs_type], has_side_effect=False - ](lhs[i], rhs[i]) + "llvm.pow", Scalar[BaseTy], has_side_effect=False + ](base[i], exp[i]) return result - elif rhs_type.is_integral(): + elif ExpTy.is_integral(): # Common cases - if all(rhs == 2): - return lhs * lhs - if all(rhs == 3): - return lhs * lhs * lhs + if all(exp == 2): + return base * base + if all(exp == 3): + return base * base * base - var result = SIMD[lhs_type, simd_width]() + var result = __type_of(base)() @parameter for i in range(simd_width): - result[i] = _powi(lhs[i], rhs[i].cast[DType.int32]()) + result[i] = _powi(base[i], exp[i].cast[DType.int32]()) return result else: - # Unsupported. - return SIMD[lhs_type, simd_width]() + constrained[False, "unsupported type combination"]() + return __type_of(base)() @always_inline -fn _powi[type: DType](lhs: Scalar[type], rhs: Int32) -> __type_of(lhs): - if type.is_integral() and rhs < 0: +fn _powi[type: DType](base: Scalar[type], exp: Int32) -> __type_of(base): + if type.is_integral() and exp < 0: # Not defined for Integers, this should raise an # exception. debug_assert(False, "exponent < 0 is undefined for integers") return 0 - var a = lhs - var b = abs(rhs) if type.is_floating_point() else rhs + var a = base + var b = abs(exp) if type.is_floating_point() else exp var res: Scalar[type] = 1 while b > 0: if b & 1: @@ -2655,7 +2634,7 @@ fn _powi[type: DType](lhs: Scalar[type], rhs: Int32) -> __type_of(lhs): @parameter if type.is_floating_point(): - if rhs < 0: + if exp < 0: return 1 / res return res diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 0d4ce29e09..12114b8145 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -928,16 +928,16 @@ struct PythonObject( """ return self._call_single_arg_inplace_method("__lshift__", rhs) - fn __pow__(self, rhs: PythonObject) raises -> PythonObject: + fn __pow__(self, exp: PythonObject) raises -> PythonObject: """Raises this object to the power of the given value. Args: - rhs: The exponent. + exp: The exponent. Returns: The result of raising this by the given exponent. """ - return self._call_single_arg_method("__pow__", rhs) + return self._call_single_arg_method("__pow__", exp) fn __rpow__(self, lhs: PythonObject) raises -> PythonObject: """Reverse power of. From 216c340c0cfa9aa0731c68251f83cbffd802a7e3 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 27 May 2024 10:32:45 -0500 Subject: [PATCH 0707/2019] [External] [stdlib] Add the `normalize_index` function (#40280) [External] [stdlib] Add the `normalize_index` function This PR is some kind of mix between https://github.com/modularml/mojo/pull/2386 and https://github.com/modularml/mojo/pull/2384 which have issues (aborting) or have too many conflicts because the PR is too big. This PR solves part of https://github.com/modularml/mojo/issues/2251 and https://github.com/modularml/mojo/issues/2337 We try here to give the ground work for indexing correctly. This function added can then be used wherever we work with sequences. Two things I noticed during development: 1) The `debug_assert` does not run in unit tests. Is there any way to enable it? We currently have out-of-bounds bugs in our test suite. 2) The null terminator is causing pain, again, again, and again. Do we have any plans to make it optional when working with String? I opened https://github.com/modularml/mojo/issues/2678 to discuss this. To avoid to fix those issues in this PR, I used the `normalize_index` on the `__refitem__` of `InlineArray` which doesn't have widespread use yet and isn't impacted by out-of-bounds bugs. My recommendation would be to merge this PR then to rebase https://github.com/modularml/mojo/pull/2386 and https://github.com/modularml/mojo/pull/2384 on it. We should also afterwards fix the out of bounds bugs that can be triggered in the test suite by enabling debug_assert. The diff might seem big, but no worries, it's mostly the licenses and docstrings :) Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2677 MODULAR_ORIG_COMMIT_REV_ID: 66e7121a6a333c16284eb33a89eb85c034c296c3 --- .../src/collections/_index_normalization.mojo | 78 +++++++++++++++++++ stdlib/src/collections/list.mojo | 28 +++---- stdlib/src/utils/static_tuple.mojo | 46 ++++++----- .../collections/test_index_normalization.mojo | 64 +++++++++++++++ stdlib/test/utils/test_tuple.mojo | 19 +++++ 5 files changed, 200 insertions(+), 35 deletions(-) create mode 100644 stdlib/src/collections/_index_normalization.mojo create mode 100644 stdlib/test/collections/test_index_normalization.mojo diff --git a/stdlib/src/collections/_index_normalization.mojo b/stdlib/src/collections/_index_normalization.mojo new file mode 100644 index 0000000000..b64f48908c --- /dev/null +++ b/stdlib/src/collections/_index_normalization.mojo @@ -0,0 +1,78 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""The utilities provided in this module help normalize the access +to data elements in arrays.""" + +from sys import triple_is_nvidia_cuda + + +fn get_out_of_bounds_error_message[ + container_name: StringLiteral +](i: Int, container_length: Int) -> String: + if container_length == 0: + return ( + "The " + + container_name + + " has a length of 0. Thus it's not possible to access its values" + " with an index but the index value " + + str(i) + + " was used. Aborting now to avoid an out-of-bounds access." + ) + else: + return ( + "The " + + container_name + + " has a length of " + + str(container_length) + + ". Thus the index provided should be between " + + str(-container_length) + + " (inclusive) and " + + str(container_length) + + " (exclusive) but the index value " + + str(i) + + " was used. Aborting now to avoid an out-of-bounds access." + ) + + +@always_inline +fn normalize_index[ + ContainerType: Sized, //, container_name: StringLiteral +](idx: Int, container: ContainerType) -> Int: + """Normalize the given index value to a valid index value for the given container length. + + If the provided value is negative, the `index + container_length` is returned. + + Parameters: + ContainerType: The type of the container. Must have a `__len__` method. + container_name: The name of the container. Used for the error message. + + Args: + idx: The index value to normalize. + container: The container to normalize the index for. + + Returns: + The normalized index value. + """ + var container_length = len(container) + if not (-container_length <= idx < container_length): + + @parameter + if triple_is_nvidia_cuda(): + abort() + else: + abort( + get_out_of_bounds_error_message[container_name]( + idx, container_length + ) + ) + return idx + int(idx < 0) * container_length diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1d1cc7359b..87fa3483b8 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -791,40 +791,34 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return self[].unsafe_get(normalized_idx) @always_inline - fn unsafe_get[ - IndexerType: Indexer, - ](self: Reference[Self, _, _], idx: IndexerType) -> Reference[ - Self.T, self.is_mutable, self.lifetime - ]: + fn unsafe_get( + self: Reference[Self, _, _], idx: Int + ) -> Reference[Self.T, self.is_mutable, self.lifetime]: """Get a reference to an element of self without checking index bounds. - Users should consider using `__getitem__` instead of this method as it is unsafe. - If an index is out of bounds, this method will not abort, it will be considered - undefined behavior. + Users should consider using `__getitem__` instead of this method as it + is unsafe. If an index is out of bounds, this method will not abort, it + will be considered undefined behavior. - Note that there is no wraparound for negative indices, caution is advised. - Using negative indices is considered undefined behavior. - Never use `my_list.unsafe_get(-1)` to get the last element of the list. It will not work. + Note that there is no wraparound for negative indices, caution is + advised. Using negative indices is considered undefined behavior. Never + use `my_list.unsafe_get(-1)` to get the last element of the list. Instead, do `my_list.unsafe_get(len(my_list) - 1)`. - Parameters: - IndexerType: The type of the argument used as index. - Args: idx: The index of the element to get. Returns: A reference to the element at the given index. """ - var idx_as_int = index(idx) debug_assert( - 0 <= idx_as_int < len(self[]), + 0 <= idx < len(self[]), ( "The index provided must be within the range [0, len(List) -1]" " when using List.unsafe_get()" ), ) - return (self[].data + idx_as_int)[] + return (self[].data + idx)[] fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 91a31c203e..358a9f3506 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -18,7 +18,7 @@ You can import these APIs from the `utils` package. For example: from utils import StaticTuple ``` """ - +from collections._index_normalization import normalize_index from memory import Pointer from utils import unroll @@ -341,28 +341,20 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__[ - IntableType: Intable, - ](self: Reference[Self, _, _], index: IntableType) -> ref [ - self.lifetime - ] Self.ElementType: + fn __getitem__( + self: Reference[Self, _, _], idx: Int + ) -> ref [self.lifetime] Self.ElementType: """Get a `Reference` to the element at the given index. - Parameters: - IntableType: The inferred type of an intable argument. - Args: - index: The index of the item. + idx: The index of the item. Returns: A reference to the item at the given index. """ - debug_assert(-size <= int(index) < size, "Index must be within bounds.") - var normalized_idx = int(index) - if normalized_idx < 0: - normalized_idx += size + var normalized_index = normalize_index["InlineArray"](idx, self[]) - return self[]._get_reference_unsafe(normalized_idx)[] + return self[]._get_reference_unsafe(normalized_index)[] @always_inline("nodebug") fn __getitem__[ @@ -408,15 +400,33 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): @always_inline("nodebug") fn _get_reference_unsafe( - self: Reference[Self, _, _], index: Int + self: Reference[Self, _, _], idx: Int ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: """Get a reference to an element of self without checking index bounds. - Users should opt for `__getitem__` instead of this method. + Users should opt for `__getitem__` instead of this method as it is + unsafe. + + Note that there is no wraparound for negative indices. Using negative + indices is considered undefined behavior. + + Args: + idx: The index of the element to get. + + Returns: + A reference to the element at the given index. """ + var idx_as_int = index(idx) + debug_assert( + 0 <= idx_as_int < size, + ( + "Index must be within bounds when using" + " `InlineArray.unsafe_get()`." + ), + ) var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(self[]._array).address, - index.value, + idx_as_int.value, ) return UnsafePointer(ptr)[] diff --git a/stdlib/test/collections/test_index_normalization.mojo b/stdlib/test/collections/test_index_normalization.mojo new file mode 100644 index 0000000000..d207cb96de --- /dev/null +++ b/stdlib/test/collections/test_index_normalization.mojo @@ -0,0 +1,64 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from collections._index_normalization import ( + get_out_of_bounds_error_message, + normalize_index, +) +from testing import assert_equal + + +def test_out_of_bounds_message(): + assert_equal( + get_out_of_bounds_error_message["List"](5, 2), + ( + "The List has a length of 2. Thus the index provided should be" + " between -2 (inclusive) and 2 (exclusive) but the index value 5" + " was used. Aborting now to avoid an out-of-bounds access." + ), + ) + + assert_equal( + get_out_of_bounds_error_message["List"](0, 0), + ( + "The List has a length of 0. Thus it's not possible to access its" + " values with an index but the index value 0 was used. Aborting now" + " to avoid an out-of-bounds access." + ), + ) + assert_equal( + get_out_of_bounds_error_message["InlineArray"](8, 0), + ( + "The InlineArray has a length of 0. Thus it's not possible to" + " access its values with an index but the index value 8 was used." + " Aborting now to avoid an out-of-bounds access." + ), + ) + + +def test_normalize_index(): + container = List[Int](1, 1, 1, 1) + assert_equal(normalize_index[""](-4, container), 0) + assert_equal(normalize_index[""](-3, container), 1) + assert_equal(normalize_index[""](-2, container), 2) + assert_equal(normalize_index[""](-1, container), 3) + assert_equal(normalize_index[""](0, container), 0) + assert_equal(normalize_index[""](1, container), 1) + assert_equal(normalize_index[""](2, container), 2) + assert_equal(normalize_index[""](3, container), 3) + + +def main(): + test_out_of_bounds_message() + test_normalize_index() diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 75e98ac8ce..3242b97df9 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -76,6 +76,24 @@ def test_tuple_literal(): assert_equal(len(()), 0) +def test_array_get_reference_unsafe(): + # Negative indexing is undefined behavior with _get_reference_unsafe + # so there are not test cases for it. + var arr = InlineArray[Int, 3](0, 0, 0) + + assert_equal(arr._get_reference_unsafe(0)[], 0) + assert_equal(arr._get_reference_unsafe(1)[], 0) + assert_equal(arr._get_reference_unsafe(2)[], 0) + + arr[0] = 1 + arr[1] = 2 + arr[2] = 3 + + assert_equal(arr._get_reference_unsafe(0)[], 1) + assert_equal(arr._get_reference_unsafe(1)[], 2) + assert_equal(arr._get_reference_unsafe(2)[], 3) + + def test_array_int(): var arr = InlineArray[Int, 3](0, 0, 0) @@ -199,6 +217,7 @@ def main(): test_static_tuple() test_static_int_tuple() test_tuple_literal() + test_array_get_reference_unsafe() test_array_int() test_array_str() test_array_int_pointer() From e7f662d0bb30b2140d56d7fc42c72130d28ec8a7 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 11:37:29 -0400 Subject: [PATCH 0708/2019] [stdlib] Move `pow` from `math` to builtins This brings us in line with Python where this is also a builtin. Notably, `IntLiteral` and `FloatLiteral` does not implement this yet, and tests will need improvement, but this change opens up the possibility of the community contributing to these. MODULAR_ORIG_COMMIT_REV_ID: ddde9c362eb779281207230b278dae177a7ca489 --- docs/changelog.md | 12 +++--- examples/notebooks/RayTracing.ipynb | 3 -- stdlib/src/builtin/int.mojo | 7 ++-- stdlib/src/builtin/int_literal.mojo | 2 + stdlib/src/builtin/math.mojo | 60 +++++++++++++++++++++++++++++ stdlib/src/builtin/simd.mojo | 1 + stdlib/test/builtin/test_math.mojo | 10 +++++ stdlib/test/builtin/test_simd.mojo | 25 +++++++++++- 8 files changed, 108 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e90c39c754..92d9107a91 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -162,9 +162,10 @@ what we publish. return Self(round(self.re), round(self.im)) ``` -- The `abs`, `round`, `min`, `max`, and `divmod` functions have moved from - `math` to `builtin`, so you no longer need to do - `from math import abs, round, min, max, divmod`. +- User defined types can now also opt in to use the `pow` function by + implementing the `__pow__` method (and thus conforming to the new `Powable` + trait). As before, these types will also benefit from being able to use the + `**` operator. - Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` functions in the `math` module by implementing the `__floor__()`, @@ -434,8 +435,9 @@ what we publish. removed `let` declarations but still provided an error message to users. Now, it is completely gone from the grammar. Long live `var`! -- The `abs` and `round` functions have moved from `math` to `builtin`, so you no - longer need to do `from math import abs, round`. +- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved + from `math` to `builtin`, so you no longer need to do + `from math import abs, round, min, max, divmod, pow`. - Many functions returning a pointer type have been unified to have a public API function of `unsafe_ptr()`. diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 3bb4ea98f0..852ca89d58 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -805,9 +805,6 @@ } ], "source": [ - "from math import pow\n", - "\n", - "\n", "fn reflect(I: Vec3f, N: Vec3f) -> Vec3f:\n", " return I - N * (I @ N) * 2.0\n", "\n", diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 5a19f2b141..cb2fe0a980 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -248,12 +248,13 @@ struct Int( Comparable, Floorable, Formattable, + Indexer, Intable, KeyElement, + Powable, Roundable, Stringable, Truncable, - Indexer, ): """This type represents an integer value.""" @@ -749,13 +750,13 @@ struct Int( return div, mod @always_inline("nodebug") - fn __pow__(self, exp: Int) -> Int: + fn __pow__(self, exp: Self) -> Self: """Return the value raised to the power of the given exponent. Computes the power of an integer using the Russian Peasant Method. Args: - exp: The RHS value. + exp: The exponent value. Returns: The value of `self` raised to the power of `exp`. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index d8691e0446..6bcbf99b8a 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -326,6 +326,8 @@ struct IntLiteral( ](self.value, rhs.value) ) + # TODO: implement __pow__ + @always_inline("nodebug") fn __floordiv__(self, rhs: Self) -> Self: """Return `self // rhs`. diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 70adcdc1bc..2acb9aa605 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -205,6 +205,66 @@ fn min[ return x.min(y) +# ===----------------------------------------------------------------------=== # +# pow +# ===----------------------------------------------------------------------=== # + + +trait Powable: + """ + The `Powable` trait describes a type that defines a power operation (i.e. + exponentiation) with the same base and exponent types. + + Types that conform to `Powable` will work with the builtin `pow` function, + which will return the same type as the inputs. + + TODO: add example + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __pow__(self, exp: Self) -> Self: + """Return the value raised to the power of the given exponent. + + Args: + exp: The exponent value. + + Returns: + The value of `self` raised to the power of `exp`. + """ + ... + + +fn pow[T: Powable](base: T, exp: T) -> T: + """Computes the `base` raised to the power of the `exp`. + + Parameters: + T: A type conforming to the `Powable` trait. + + Args: + base: The base of the power operation. + exp: The exponent of the power operation. + + Returns: + The `base` raised to the power of the `exp`. + """ + return base.__pow__(exp) + + +fn pow(base: SIMD, exp: Int) -> __type_of(base): + """Computes elementwise value of a SIMD vector raised to the power of the + given integer. + + Args: + base: The first input argument. + exp: The second input argument. + + Returns: + The `base` elementwise raised raised to the power of `exp`. + """ + return base.__pow__(exp) + + # ===----------------------------------------------------------------------=== # # round # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index aab64e590b..a1096329a3 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -135,6 +135,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Floorable, Hashable, Intable, + Powable, Roundable, Sized, Stringable, diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 3a839df5d4..178b4df545 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -75,9 +75,19 @@ def test_round(): assert_equal(expected, round(lhs)) +def test_pow(): + alias F = SIMD[DType.float32, 4] + var base = F(0.0, 1.0, 2.0, 3.0) + assert_equal(pow(base, 2.0), F(0.0, 1.0, 4.0, 9.0)) + assert_equal(pow(base, int(2)), F(0.0, 1.0, 4.0, 9.0)) + alias I = SIMD[DType.int32, 4] + assert_equal(pow(I(0, 1, 2, 3), int(2)), I(0, 1, 4, 9)) + + def main(): test_abs() test_divmod() test_max() test_min() test_round() + test_pow() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 7d09811add..ec85031f1e 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1311,6 +1311,28 @@ def test_reduce(): test_dtype[DType.bfloat16]() +def test_pow(): + alias inf = FloatLiteral.infinity + alias F = SIMD[DType.float32, 4] + + var simd_val = F(0, 1, 2, 3) + + assert_equal(simd_val.__pow__(2.0), F(0.0, 1.0, 4.0, 9.0)) + assert_equal(simd_val.__pow__(2), F(0.0, 1.0, 4.0, 9.0)) + assert_equal(simd_val.__pow__(3), F(0.0, 1.0, 8.0, 27.0)) + assert_equal(simd_val.__pow__(-1), F(inf, 1.0, 0.5, 0.3333333432674408)) + + # TODO: enable when math.isclose is open sourced + # assert_equal(simd_val.__pow__(0.5), F(0.0, 1.0, 1.41421, 1.73205)) + # assert_equal(simd_val.__pow__(2, -0.5), F(0.70710, 0.57735, 0.5, 0.44721)) + + alias I = SIMD[DType.int32, 4] + var simd_val_int = I(0, 1, 2, 3) + + # TODO: extend/improve these tests + assert_equal(simd_val_int.__pow__(2), I(0, 1, 4, 9)) + + def main(): test_abs() test_add() @@ -1336,7 +1358,9 @@ def main(): test_min_max_clamp() test_mod() test_mul_with_overflow() + test_pow() test_radd() + test_reduce() test_rfloordiv() test_rmod() test_rotate() @@ -1350,4 +1374,3 @@ def main(): test_sub_with_overflow() test_trunc() test_truthy() - test_reduce() From 681285dd41c6b7d14de8e9f0cda28234d8b94a07 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 27 May 2024 12:19:37 -0500 Subject: [PATCH 0709/2019] [External] [stdlib] implement os.path.join (#40539) [External] [stdlib] implement os.path.join Implementation of `os.path.join` ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2792 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2792 MODULAR_ORIG_COMMIT_REV_ID: c69ceacfe4a63e67e39bc1fdf9bc7540096298e3 --- docs/changelog.md | 3 ++ stdlib/src/os/__init__.mojo | 1 + stdlib/src/os/os.mojo | 3 ++ stdlib/src/os/path/__init__.mojo | 2 +- stdlib/src/os/path/path.mojo | 57 ++++++++++++++++++++++++++++++ stdlib/test/os/path/test_join.mojo | 53 +++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/os/path/test_join.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 92d9107a91..f8b6f5174d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -421,6 +421,9 @@ what we publish. - Added `clear` method to `Dict`. ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) +- Added `os.path.join` function. + ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) + - `StringRef` now implements `startswith()` and `endswith()`. ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index 771d2e22fb..d0efc581f2 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -16,6 +16,7 @@ from .atomic import Atomic from .env import setenv, getenv from .fstat import lstat, stat, stat_result from .os import ( + sep, abort, listdir, remove, diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index bdaa870354..8abd84fef0 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -32,6 +32,9 @@ from utils import StringRef, InlineArray from .path import isdir from .pathlike import PathLike +# TODO move this to a more accurate location once nt/posix like modules are in stdlib +alias sep = "\\" if os_is_windows() else "/" + # ===----------------------------------------------------------------------=== # # SEEK Constants diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index 98b9c2411b..827e9ed64c 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -11,4 +11,4 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from .path import exists, isdir, isfile, islink, lexists, getsize +from .path import exists, isdir, isfile, islink, lexists, getsize, join diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 8f5010dcd1..00caaf18de 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -22,6 +22,7 @@ from os.path import isdir from stat import S_ISDIR, S_ISLNK, S_ISREG from sys import has_neon, os_is_linux, os_is_macos, os_is_windows +from ..os import sep from .. import PathLike from .._linux_aarch64 import _lstat as _lstat_linux_arm from .._linux_aarch64 import _stat as _stat_linux_arm @@ -283,3 +284,59 @@ fn getsize[pathlike: os.PathLike](path: pathlike) raises -> Int: The size of the path in bytes. """ return getsize(path.__fspath__()) + + +# ===----------------------------------------------------------------------=== # +# join +# ===----------------------------------------------------------------------=== # + + +fn join(path: String, *paths: String) -> String: + """Join two or more pathname components, inserting '/' as needed. + If any component is an absolute path, all previous path components + will be discarded. An empty last part will result in a path that + ends with a separator. + + Args: + path: The path to join. + paths: The paths to join. + + Returns: + The joined path. + """ + var joined_path = path + + for cur_path in paths: + if cur_path[].startswith(sep): + joined_path = cur_path[] + elif not joined_path or path.endswith(sep): + joined_path += cur_path[] + else: + joined_path += sep + cur_path[] + + return joined_path + + +# TODO uncomment this when unpacking is supported +# fn join[pathlike: os.PathLike](path: pathlike, *paths: pathlike) -> String: +# """Join two or more pathname components, inserting '/' as needed. +# If any component is an absolute path, all previous path components +# will be discarded. An empty last part will result in a path that +# ends with a separator. + +# Parameters: +# pathlike: The type conforming to the os.PathLike trait. + +# Args: +# path: The path to join. +# paths: The paths to join. + +# Returns: +# The joined path. +# """ +# var paths_str= List[String]() + +# for cur_path in paths: +# paths_str.append(cur_path[].__fspath__()) + +# return join(path.__fspath__(), *paths_str) diff --git a/stdlib/test/os/path/test_join.mojo b/stdlib/test/os/path/test_join.mojo new file mode 100644 index 0000000000..a61ffd9da2 --- /dev/null +++ b/stdlib/test/os/path/test_join.mojo @@ -0,0 +1,53 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + + +import os +from os.path import join +from pathlib import Path + +from testing import assert_equal + + +fn main() raises: + # TODO uncomment lines using Path when unpacking is supported + assert_equal("path/to/file", join("path", "to", "file")) + # assert_equal("path/to/file", join(Path("path"), Path("to"), Path("file"))) + assert_equal("path/to/file", join("path", "to/file")) + # assert_equal("path/to/file", join(Path("path"), Path("to/file"))) + assert_equal("path/to/file", join("path/to", "file")) + # assert_equal("path/to/file", join(Path("path/to"), Path("file"))) + assert_equal("path/to/file", join("path/", "to/", "file")) + + assert_equal("path/", join("path", "")) + # assert_equal("path/", join(Path("path"), Path(""))) + assert_equal("path", join("path")) + # assert_equal("path", join(Path("path"))) + assert_equal("", join("")) + assert_equal("path", join("", "path")) + + assert_equal("/path/to/file", join("ignored", "/path/to", "file")) + # assert_equal("/path/to/file", join(Path("ignored"), Path("/path/to/file"))) + assert_equal( + "/absolute/path", + join("ignored", "/ignored/absolute/path", "/absolute", "path"), + ) + # assert_equal( + # "/path/to/file", + # join( + # Path("ignored"), + # Path("/path/to/file/but/ignored/again"), + # Path("/path/to/file"), + # ), + # ) From 0a89f4c0420ee8df69952efa25361f7e6ebe31ef Mon Sep 17 00:00:00 2001 From: Avinag <116362503+Av1nag@users.noreply.github.com> Date: Mon, 27 May 2024 13:09:09 -0500 Subject: [PATCH 0710/2019] [External] [docker] ```AUTH_KEY``` arguments removed in ```examples/docker``` folder (#40616) [External] [docker] ```AUTH_KEY``` arguments removed in ```examples/docker``` folder As of my knowledge, in the latest update there's no longer required any ```AUTH_KEY``` for installing. But the Docker file haven't updated with respect to the latest release. By address this issue, I've modified the all the three files in ```examples/docker```. Now can be build docker image without any hassle. Co-authored-by: Avinag <116362503+Av1nag@users.noreply.github.com> Closes modularml/mojo#2802 MODULAR_ORIG_COMMIT_REV_ID: c752915a7df2dfda91900b777df9e5c3994790fc --- examples/docker/Dockerfile.mojosdk | 9 ++------- examples/docker/build-image.sh | 17 +---------------- examples/docker/docker-compose.yml | 2 -- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/examples/docker/Dockerfile.mojosdk b/examples/docker/Dockerfile.mojosdk index b25ce5d5fc..a65c198986 100644 --- a/examples/docker/Dockerfile.mojosdk +++ b/examples/docker/Dockerfile.mojosdk @@ -17,7 +17,7 @@ # Example command line: # Use no-cache to force docker to rebuild layers of the image by downloading the SDK from the repos -# ./build-image.sh --auth-key +# ./build-image.sh --mojo-version # FROM ubuntu:20.04 @@ -50,12 +50,7 @@ RUN pip install \ matplotlib \ ipywidgets -# A random default token -ARG AUTH_KEY=5ca1ab1e -ENV AUTH_KEY=$AUTH_KEY - -RUN curl https://get.modular.com | sh - && \ - modular auth $AUTH_KEY +RUN curl -s https://get.modular.com | sh - RUN modular install mojo ARG MODULAR_HOME="/root/.modular" diff --git a/examples/docker/build-image.sh b/examples/docker/build-image.sh index 6ef332f830..a494731645 100644 --- a/examples/docker/build-image.sh +++ b/examples/docker/build-image.sh @@ -15,21 +15,15 @@ set -e # Usage # ========== -# ./build-image.sh --auth-key +# ./build-image.sh --mojo-version # # CLI option handling code -DEFAULT_KEY=5ca1ab1e -user_key=${user_key:=${DEFAULT_KEY}} mojo_ver=${mojo_ver:=0.3} container_engine=${container_engine:=docker} extra_cap=${extra_cap:=} while [ $# -gt 0 ]; do case "$1" in - --auth-key) - user_key="$2" - shift - ;; --use-podman) container_engine=podman extra_cap="--cap-add SYS_PTRACE" @@ -45,18 +39,9 @@ while [ $# -gt 0 ]; do shift $(( $# > 0 ? 1 : 0 )) done -check_options() { - if [ "${user_key}" = "${DEFAULT_KEY}" ]; then - echo "# No auth token specified; use --auth-key to specify your token" - exit 1 - fi -} - build_image() { - check_options echo "# Building image with ${container_engine}..." ${container_engine} build --no-cache ${extra_cap} \ - --build-arg AUTH_KEY=${user_key} \ --pull -t modular/mojo-v${mojo_ver}-`date '+%Y%d%m-%H%M'` \ --file Dockerfile.mojosdk . } diff --git a/examples/docker/docker-compose.yml b/examples/docker/docker-compose.yml index a4db38552a..7c26a916b5 100644 --- a/examples/docker/docker-compose.yml +++ b/examples/docker/docker-compose.yml @@ -8,8 +8,6 @@ services: build : context: . dockerfile: Dockerfile.mojosdk - args: - - AUTH_KEY=1Auth_token no_cache : true ports: - 8888:8888 From 29b19dafe5b3185cf218bd05c4e09a1edf13d489 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 27 May 2024 11:10:32 -0700 Subject: [PATCH 0711/2019] Change arguments from `StringLiteral` to `String`. MODULAR_ORIG_COMMIT_REV_ID: 933b2a50845c0ae5ab92b7aeb22ba2d55f65c27b --- examples/reduce.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/reduce.mojo b/examples/reduce.mojo index ff8240ed25..493097523a 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -50,11 +50,11 @@ fn stdlib_reduce_sum[size: Int](array: Buffer[type, size]) -> scalar: return my_sum -def pretty_print(name: StringLiteral, elements: Int, time: Float64): +def pretty_print(name: String, elements: Int, time: Float64): py = Python.import_module("builtins") py.print( py.str("{:<16} {:>11,} {:>8.2f}ms").format( - String(name) + " elements:", elements, time + name + " elements:", elements, time ) ) @@ -62,7 +62,7 @@ def pretty_print(name: StringLiteral, elements: Int, time: Float64): fn bench[ func: fn[size: Int] (buffer: Buffer[type, size]) -> scalar, size: Int, - name: StringLiteral, + name: String, ](buffer: Buffer[type, size]) raises: @parameter fn runner(): From f13729b63115d66019f27f24968e786d8e4fc8d0 Mon Sep 17 00:00:00 2001 From: northstreet12 Date: Mon, 27 May 2024 13:40:53 -0500 Subject: [PATCH 0712/2019] [External] [stdlib] Add count() method for InlineList (#40683) [External] [stdlib] Add `count()` method for `InlineList` Add `count` method for `InlineList` to count the number of occurrences the value occurs in the `InlineList`. *count* ```mojo var my_list = InlineList[Int](1, 2, 3) print(my_list.count(1)) ``` Co-authored-by: northstreet12 Closes modularml/mojo#2768 MODULAR_ORIG_COMMIT_REV_ID: 3024406c384b6d838df35b1243a02ac0efffe2be --- stdlib/src/collections/inline_list.mojo | 28 +++++++++++++++++++ stdlib/test/collections/test_inline_list.mojo | 11 ++++++++ 2 files changed, 39 insertions(+) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index b1ccfcc860..d8a843d911 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -193,6 +193,34 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): return True return False + @always_inline + fn count[C: ComparableCollectionElement](self: Self, value: C) -> Int: + """Counts the number of occurrences of a value in the list. + + ```mojo + var my_list = InlineList[Int](1, 2, 3) + print(my_list.count(1)) + ``` + Parameters: + C: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + value: The value to count. + + Returns: + The number of occurrences of the value in the list. + """ + constrained[ + _type_is_eq[ElementType, C](), "value type is not self.ElementType" + ]() + + var count = 0 + for elem in self: + if value == rebind[C](elem[]): + count += 1 + return count + @always_inline fn __bool__(self) -> Bool: """Checks whether the list has any elements or not. diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index 8bb0dbae36..2179daa1a7 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -138,6 +138,16 @@ def test_list_variadic_constructor(): assert_equal(8, l[3]) +def test_list_count(): + var list = InlineList[Int](1, 2, 3, 2, 5, 6, 7, 8, 9, 10) + assert_equal(1, list.count(1)) + assert_equal(2, list.count(2)) + assert_equal(0, list.count(4)) + + var list2 = InlineList[Int]() + assert_equal(0, list2.count(1)) + + def test_list_boolable(): assert_true(InlineList[Int](1)) assert_false(InlineList[Int]()) @@ -162,5 +172,6 @@ def main(): test_list_iter_mutable() test_list_contains() test_list_variadic_constructor() + test_list_count() test_list_boolable() test_indexing() From 434d8fd17183881eb03ed01831b80b78d99429cd Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 27 May 2024 12:19:07 -0700 Subject: [PATCH 0713/2019] [mojo-stdlib] Simplify span, switching to getitem references. This adopts references and removes some redundancy. MODULAR_ORIG_COMMIT_REV_ID: d5acb59ac81d5033d6a3db8c3f1cf07014524aeb --- stdlib/src/utils/span.mojo | 48 +++++--------- stdlib/test/utils/test_span.mojo | 80 ++++++++++++------------ stdlib/test/utils/test_string_slice.mojo | 42 ++++++------- 3 files changed, 75 insertions(+), 95 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 02fd399ffb..07d7cfaa91 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -53,10 +53,10 @@ struct _SpanIter[ @parameter if forward: self.index += 1 - return self.src._refitem__(self.index - 1) + return self.src[self.index - 1] else: self.index -= 1 - return self.src._refitem__(self.index) + return self.src[self.index] @always_inline fn __len__(self) -> Int: @@ -130,44 +130,24 @@ struct Span[ # ===------------------------------------------------------------------===# @always_inline - fn _refitem__[ - intable: Intable - ](self, idx: intable) -> Reference[T, is_mutable, lifetime]: - debug_assert( - -self._len <= int(idx) < self._len, "index must be within bounds" - ) - - var offset = int(idx) - if offset < 0: - offset += len(self) - return (self._data + offset)[] - - @always_inline - fn __getitem__(self, idx: Int) -> Reference[T, is_mutable, lifetime]: - """Get a `Reference` to the element at the given index. + fn __getitem__(self, idx: Int) -> ref [lifetime] T: + """Get a reference to an element in the span. Args: - idx: The index of the item. + idx: The index of the value to return. Returns: - A reference to the item at the given index. - """ - # note that self._refitem__ is already bounds checking - return self._refitem__(idx) - - @always_inline - fn __setitem__(inout self, idx: Int, value: T): - """Get a `Reference` to the element at the given index. - - Args: - idx: The index of the item. - value: The value to set at the given index. + An element reference. """ - # note that self._refitem__ is already bounds checking - var r = Reference[T, __mlir_attr.`1: i1`, __lifetime_of(self)]( - UnsafePointer(self._refitem__(idx))[] + # TODO: Simplify this with a UInt type. + debug_assert( + -self._len <= int(idx) < self._len, "index must be within bounds" ) - r[] = value + + var offset = idx + if offset < 0: + offset += len(self) + return self._data[offset] @always_inline fn __getitem__(self, slc: Slice) -> Self: diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index d264fc3bfa..470a07ae4d 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -22,22 +22,22 @@ def test_span_list_int(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i][]) + assert_equal(l[i], s[i]) # subslice var s2 = s[2:] - assert_equal(s2[0][], l[2]) - assert_equal(s2[1][], l[3]) - assert_equal(s2[2][], l[4]) - assert_equal(s2[3][], l[5]) - assert_equal(s[-1][], l[-1]) + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) + assert_equal(s[-1], l[-1]) # Test mutation - s[0][] = 9 - assert_equal(s[0][], 9) + s[0] = 9 + assert_equal(s[0], 9) assert_equal(l[0], 9) - s[-1][] = 0 - assert_equal(s[-1][], 0) + s[-1] = 0 + assert_equal(s[-1], 0) assert_equal(l[-1], 0) @@ -46,21 +46,21 @@ def test_span_list_str(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i][]) + assert_equal(l[i], s[i]) # subslice var s2 = s[2:] - assert_equal(s2[0][], l[2]) - assert_equal(s2[1][], l[3]) - assert_equal(s2[2][], l[4]) - assert_equal(s2[3][], l[5]) + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) # Test mutation - s[0][] = "h" - assert_equal(s[0][], "h") + s[0] = "h" + assert_equal(s[0], "h") assert_equal(l[0], "h") - s[-1][] = "i" - assert_equal(s[-1][], "i") + s[-1] = "i" + assert_equal(s[-1], "i") assert_equal(l[-1], "i") @@ -69,21 +69,21 @@ def test_span_array_int(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i][]) + assert_equal(l[i], s[i]) # subslice var s2 = s[2:] - assert_equal(s2[0][], l[2]) - assert_equal(s2[1][], l[3]) - assert_equal(s2[2][], l[4]) - assert_equal(s2[3][], l[5]) + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) # Test mutation - s[0][] = 9 - assert_equal(s[0][], 9) + s[0] = 9 + assert_equal(s[0], 9) assert_equal(l[0], 9) - s[-1][] = 0 - assert_equal(s[-1][], 0) + s[-1] = 0 + assert_equal(s[-1], 0) assert_equal(l[-1], 0) @@ -92,30 +92,30 @@ def test_span_array_str(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i][]) + assert_equal(l[i], s[i]) # subslice var s2 = s[2:] - assert_equal(s2[0][], l[2]) - assert_equal(s2[1][], l[3]) - assert_equal(s2[2][], l[4]) - assert_equal(s2[3][], l[5]) + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) # Test mutation - s[0][] = "h" - assert_equal(s[0][], "h") + s[0] = "h" + assert_equal(s[0], "h") assert_equal(l[0], "h") - s[-1][] = "i" - assert_equal(s[-1][], "i") + s[-1] = "i" + assert_equal(s[-1], "i") assert_equal(l[-1], "i") def test_indexing(): var l = InlineArray[Int, 7](1, 2, 3, 4, 5, 6, 7) var s = Span(l) - assert_equal(s[True][], 2) - assert_equal(s[int(0)][], 1) - assert_equal(s[3][], 4) + assert_equal(s[True], 2) + assert_equal(s[int(0)], 1) + assert_equal(s[3], 4) def main(): diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index a2e616f0bf..bcabb5504c 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -22,11 +22,11 @@ fn test_string_literal_byte_slice() raises: alias slc = string.as_bytes_slice() assert_equal(len(slc), 5) - assert_equal(slc[0][], ord("H")) - assert_equal(slc[1][], ord("e")) - assert_equal(slc[2][], ord("l")) - assert_equal(slc[3][], ord("l")) - assert_equal(slc[4][], ord("o")) + assert_equal(slc[0], ord("H")) + assert_equal(slc[1], ord("e")) + assert_equal(slc[2], ord("l")) + assert_equal(slc[3], ord("l")) + assert_equal(slc[4], ord("o")) fn test_string_byte_slice() raises: @@ -34,11 +34,11 @@ fn test_string_byte_slice() raises: var str_slice = string.as_bytes_slice() assert_equal(len(str_slice), 5) - assert_equal(str_slice[0][], ord("H")) - assert_equal(str_slice[1][], ord("e")) - assert_equal(str_slice[2][], ord("l")) - assert_equal(str_slice[3][], ord("l")) - assert_equal(str_slice[4][], ord("o")) + assert_equal(str_slice[0], ord("H")) + assert_equal(str_slice[1], ord("e")) + assert_equal(str_slice[2], ord("l")) + assert_equal(str_slice[3], ord("l")) + assert_equal(str_slice[4], ord("o")) # ---------------------------------- # Test subslicing @@ -47,32 +47,32 @@ fn test_string_byte_slice() raises: # Slice the whole thing var sub1 = str_slice[:5] assert_equal(len(sub1), 5) - assert_equal(sub1[0][], ord("H")) - assert_equal(sub1[1][], ord("e")) - assert_equal(sub1[2][], ord("l")) - assert_equal(sub1[3][], ord("l")) - assert_equal(sub1[4][], ord("o")) + assert_equal(sub1[0], ord("H")) + assert_equal(sub1[1], ord("e")) + assert_equal(sub1[2], ord("l")) + assert_equal(sub1[3], ord("l")) + assert_equal(sub1[4], ord("o")) # Slice the end var sub2 = str_slice[2:5] assert_equal(len(sub2), 3) - assert_equal(sub2[0][], ord("l")) - assert_equal(sub2[1][], ord("l")) - assert_equal(sub2[2][], ord("o")) + assert_equal(sub2[0], ord("l")) + assert_equal(sub2[1], ord("l")) + assert_equal(sub2[2], ord("o")) # Slice the first element var sub3 = str_slice[0:1] assert_equal(len(sub3), 1) - assert_equal(sub3[0][], ord("H")) + assert_equal(sub3[0], ord("H")) # # Test mutation through slice # - sub1[0][] = ord("J") + sub1[0] = ord("J") assert_equal(string, "Jello") - sub2[2][] = ord("y") + sub2[2] = ord("y") assert_equal(string, "Jelly") # ---------------------------------- From ac4af1980d09d72742daa48404c9b4d0460c0b08 Mon Sep 17 00:00:00 2001 From: Jay Zhan Date: Mon, 27 May 2024 15:28:52 -0500 Subject: [PATCH 0714/2019] [External] [stdlib] Support `Dict.popitem()` (#40688) [External] [stdlib] Support `Dict.popitem()` Implement `Dict.popitem()` which removes and returns the last item in the `Dict`. Fixes https://github.com/modularml/mojo/issues/2355 --------- Co-authored-by: Jay Zhan Closes modularml/mojo#2701 MODULAR_ORIG_COMMIT_REV_ID: c93387c953e05a447e6467270c54093b85e743b0 --- docs/changelog.md | 4 ++++ stdlib/src/collections/dict.mojo | 28 ++++++++++++++++++++++++++ stdlib/test/collections/test_dict.mojo | 16 +++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index f8b6f5174d..8ec09bb276 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,10 @@ what we publish. ### ⭐️ New +- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. +([PR #2701](https://github.com/modularml/mojo/pull/2701) +by [@jayzhan211](https://github.com/jayzhan211)) + - Add a `sort` function for list of `ComparableCollectionElement`s. [PR #2609](https://github.com/modularml/mojo/pull/2609) by [@mzaks](https://github.com/mzaks) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e3977e1371..f5ac1528c9 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -799,6 +799,34 @@ struct Dict[K: KeyElement, V: CollectionElement]( return default.value()[] raise "KeyError" + fn popitem(inout self) raises -> DictEntry[K, V]: + """Remove and return a (key, value) pair from the dictionary. Pairs are returned in LIFO order. + popitem() is useful to destructively iterate over a dictionary, as often used in set algorithms. + If the dictionary is empty, calling popitem() raises a KeyError. + + Args: None + + Returns: + Last dictionary item + + Raises: + "KeyError" if the dictionary is empty. + """ + + var key = Optional[K](None) + var val = Optional[V](None) + + for item in reversed(self.items()): + key = Optional(item[].key) + val = Optional(item[].value) + break + + if key: + _ = self.pop(key.value()[]) + return DictEntry[K, V](key.value()[], val.value()[]) + + raise "KeyError: popitem(): dictionary is empty" + fn keys( self: Reference[Self, _, _] ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index b51f0cb13b..0788e4add1 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -452,6 +452,7 @@ def test_dict(): test["test_dict_update_empty_new", test_dict_update_empty_new]() test["test_mojo_issue_1729", test_mojo_issue_1729]() test["test dict or", test_dict_or]() + test["test dict popteim", test_dict_popitem]() def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): @@ -512,6 +513,21 @@ def test_find_get(): assert_equal(some_dict.get("not_key", 0), 0) +def test_dict_popitem(): + var dict = Dict[String, Int]() + dict["a"] = 1 + dict["b"] = 2 + + var item = dict.popitem() + assert_equal(item.key, "b") + assert_equal(item.value, 2) + item = dict.popitem() + assert_equal(item.key, "a") + assert_equal(item.value, 1) + with assert_raises(contains="KeyError"): + _ = dict.popitem() + + fn test_clear() raises: var some_dict = Dict[String, Int]() some_dict["key"] = 1 From 2f5c4d62a96c018dd7840671d314bc46d6a42224 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Mon, 27 May 2024 15:30:34 -0500 Subject: [PATCH 0715/2019] [External] [stdlib] Add `__repr__` in `SIMD` (#40687) [External] [stdlib] Add `__repr__` in `SIMD` Implements `__repr__` for `SIMD` --------- Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2728 MODULAR_ORIG_COMMIT_REV_ID: 53688198a78f892e2d4799bc8bec3f7974a538fb --- docs/changelog.md | 3 ++ stdlib/src/builtin/dtype.mojo | 16 +++++++ stdlib/src/builtin/io.mojo | 13 +++--- stdlib/src/builtin/simd.mojo | 68 +++++++++++++++++++++++++----- stdlib/test/builtin/test_simd.mojo | 33 ++++++++++++++- 5 files changed, 117 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 8ec09bb276..f82286992a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -435,6 +435,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added a new `tempfile` module. Similarly to Python, this will contain utilities for creating and working with temporary files and directories. + +- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. +([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) ### 🦋 Changed diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index bd7ab0a552..8503bdc8ed 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -641,6 +641,22 @@ fn _integral_type_of[type: DType]() -> DType: return type.invalid +fn _scientific_notation_digits[type: DType]() -> StringLiteral: + """Get the number of digits as a StringLiteral for the scientific notation + representation of a float. + """ + constrained[type.is_floating_point(), "expected floating point type"]() + + @parameter + if type == DType.bfloat16 or type == DType.float16: + return "4" + elif type == DType.float32 or type == DType.tensor_float32: + return "8" + else: + constrained[type == DType.float64, "unknown floating point type"]() + return "16" + + # ===-------------------------------------------------------------------===# # _uint_type_of_width # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 29fb8a1c1a..425990c8f9 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -179,7 +179,8 @@ fn _snprintf[ @no_inline fn _snprintf_scalar[ - type: DType + type: DType, + float_format: StringLiteral = "%.17g", ](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type]) -> Int: alias format = _get_dtype_printf_format[type]() @@ -195,9 +196,9 @@ fn _snprintf_scalar[ type == DType.float16 or type == DType.bfloat16 or type == DType.float32 ): # We need to cast the value to float64 to print it. - return _float_repr(buffer, size, x.cast[DType.float64]()) + return _float_repr[float_format](buffer, size, x.cast[DType.float64]()) elif type == DType.float64: - return _float_repr(buffer, size, rebind[Float64](x)) + return _float_repr[float_format](buffer, size, rebind[Float64](x)) return 0 @@ -207,11 +208,13 @@ fn _snprintf_scalar[ @no_inline -fn _float_repr(buffer: UnsafePointer[UInt8], size: Int, x: Float64) -> Int: +fn _float_repr[ + format: StringLiteral = "%.17g" +](buffer: UnsafePointer[UInt8], size: Int, x: Float64) -> Int: # Using `%.17g` with decimal check is equivalent to CPython's fallback path # when its more complex dtoa library (forked from # https://github.com/dtolnay/dtoa) is not available. - var n = _snprintf(buffer, size, "%.17g", x.value) + var n = _snprintf(buffer, size, format, x.value) # If the buffer isn't big enough to add anything, then just return. if n + 2 >= size: return n diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a1096329a3..141a048a1b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -40,9 +40,12 @@ from utils.numerics import ( ) from utils._visualizers import lldb_formatter_wrapping_type from utils import InlineArray - -from .dtype import _integral_type_of, _get_dtype_printf_format -from .io import _snprintf_scalar, _printf, _print_fmt +from .dtype import ( + _integral_type_of, + _get_dtype_printf_format, + _scientific_notation_digits, +) +from .io import _snprintf_scalar, _snprintf, _printf, _print_fmt from .string import _calc_initial_buffer_size, _calc_format_buffer_size # ===------------------------------------------------------------------------===# @@ -140,6 +143,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Sized, Stringable, Truncable, + Representable, ): """Represents a small vector that is backed by a hardware vector element. @@ -577,10 +581,48 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return String.format_sequence(self) + @always_inline + fn __repr__(self) -> String: + """Get the representation of the SIMD value eg. "SIMD[DType.int8, 2](1, 2)". + Returns: + The representation of the SIMD value. + + """ + + var output = String() + var writer = output._unsafe_to_formatter() + self.format_to[use_scientific_notation=True](writer) + + var values = output.as_string_slice() + + @parameter + if size > 1: + # TODO: Fix when slice indexing is implemented on StringSlice + values = StringSlice(output.as_bytes_slice()[1:-1]) + return ( + "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" + ) + + @always_inline fn format_to(self, inout writer: Formatter): """ Formats this SIMD value to the provided formatter. + Args: + writer: The formatter to write to. + """ + self.format_to[use_scientific_notation=False](writer) + + # This overload is required to keep SIMD compliant with the Formattable trait, + # and the call to `String.format_sequence(self)` in SIMD.__str__ will fail to compile + fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): + """ + Formats this SIMD value to the provided formatter. + + Parameters: + use_scientific_notation: Whether floats should use scientific notation. + This parameter does not apply to integer types. + Args: writer: The formatter to write to. """ @@ -617,7 +659,15 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # used. _printf[_get_dtype_printf_format[type]()](element) else: - _format_scalar(writer, element) + + @parameter + if use_scientific_notation and type.is_floating_point(): + alias float_format = "%." + _scientific_notation_digits[ + type + ]() + "e" + _format_scalar[type, float_format](writer, element) + else: + _format_scalar(writer, element) # Print a closing `]`. @parameter @@ -2808,7 +2858,9 @@ fn _simd_apply[ # ===----------------------------------------------------------------------=== # -fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): +fn _format_scalar[ + dtype: DType, float_format: StringLiteral = "%.17g" +](inout writer: Formatter, value: Scalar[dtype]): # Stack allocate enough bytes to store any formatted Scalar value of any # type. alias size: Int = _calc_format_buffer_size[dtype]() @@ -2817,11 +2869,7 @@ fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): var buf_ptr = buf.unsafe_ptr() - var wrote = _snprintf_scalar[dtype]( - buf_ptr, - size, - value, - ) + var wrote = _snprintf_scalar[dtype, float_format](buf_ptr, size, value) var strref = StringRef(buf_ptr, wrote) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index ec85031f1e..344a54d994 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from sys import has_neon -from utils.numerics import isfinite, isinf, isnan +from utils.numerics import isfinite, isinf, isnan, nan from testing import assert_equal, assert_not_equal, assert_true, assert_false @@ -90,6 +90,36 @@ def test_convert_simd_to_string(): ) +def test_simd_repr(): + assert_equal( + SIMD[DType.int32, 4](1, 2, 3, 4).__repr__(), + "SIMD[DType.int32, 4](1, 2, 3, 4)", + ) + assert_equal( + SIMD[DType.int32, 4](-1, 2, -3, 4).__repr__(), + "SIMD[DType.int32, 4](-1, 2, -3, 4)", + ) + assert_equal( + SIMD[DType.bool, 2](True, False).__repr__(), + "SIMD[DType.bool, 2](True, False)", + ) + assert_equal(Int32(4).__repr__(), "SIMD[DType.int32, 1](4)") + assert_equal( + Float64(235234523.3452).__repr__(), + "SIMD[DType.float64, 1](2.3523452334520000e+08)", + ) + assert_equal( + Float32(2897239).__repr__(), "SIMD[DType.float32, 1](2.89723900e+06)" + ) + assert_equal(Float16(324).__repr__(), "SIMD[DType.float16, 1](3.2400e+02)") + assert_equal( + SIMD[DType.float32, 4]( + Float32.MAX, Float32.MIN, -0.0, nan[DType.float32]() + ).__repr__(), + "SIMD[DType.float32, 4](inf, -inf, -0.00000000e+00, nan)", + ) + + def test_issue_20421(): var a = DTypePointer[DType.uint8]().alloc(16 * 64, alignment=64) for i in range(16 * 64): @@ -1341,6 +1371,7 @@ def main(): test_cast() test_ceil() test_convert_simd_to_string() + test_simd_repr() test_deinterleave() test_div() test_extract() From b3f6075d8b7aadf51d432cc82c2236f6f56eadb8 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 27 May 2024 15:46:17 -0500 Subject: [PATCH 0716/2019] [External] [stdlib] Add `String.isspace()` conformant with Python (#40686) [External] [stdlib] Add `String.isspace()` conformant with Python For better compatibility with python ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2793 --------- Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#2793 MODULAR_ORIG_COMMIT_REV_ID: 0d9e6257f53ac8d291b4a8f17df405512fa4d973 --- docs/changelog.md | 5 +- stdlib/src/builtin/string.mojo | 90 +++++++++++++++++++++++----- stdlib/src/utils/stringref.mojo | 7 ++- stdlib/test/builtin/test_string.mojo | 77 +++++++++++++++++++----- 4 files changed, 145 insertions(+), 34 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f82286992a..ffcddda1c4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -491,7 +491,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has the same behavior. -- Changed `isspace(..)` to take an `Int`. +- Added `String.isspace()` method conformant with Python's universal separators. + +- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), + use `String.isspace()` instead. - Added `UnsafePointer.offset()` method. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 6d489e87e9..6913b0e1d5 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -222,7 +222,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: var buff = str_ref.unsafe_ptr() for pos in range(start, str_len): - if isspace(int(buff[pos])): + if _isspace(buff[pos]): continue if str_ref[pos] == "-": @@ -297,13 +297,13 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: elif ord_letter_min[1] <= ord_current <= ord_letter_max[1]: result += ord_current - ord_letter_min[1] + 10 found_valid_chars_after_start = True - elif isspace(ord_current): + elif _isspace(ord_current): has_space_after_number = True start = pos + 1 break else: raise Error(_atol_error(base, str_ref)) - if pos + 1 < str_len and not isspace(int(buff[pos + 1])): + if pos + 1 < str_len and not _isspace(buff[pos + 1]): var nextresult = result * real_base if nextresult < result: raise Error( @@ -317,7 +317,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: if has_space_after_number: for pos in range(start, str_len): - if not isspace(int(buff[pos])): + if not _isspace(buff[pos]): raise Error(_atol_error(base, str_ref)) if is_negative: result = -result @@ -582,28 +582,38 @@ fn _is_ascii_lowercase(c: UInt8) -> Bool: # ===----------------------------------------------------------------------===# -# isspace +# _isspace # ===----------------------------------------------------------------------===# -fn isspace(c: UInt8) -> Bool: +fn _get_spaces_table() -> InlineArray[UInt8, 256]: + var table = InlineArray[UInt8, 256](0) + table[ord(" ")] = 1 + table[ord("\t")] = 1 + table[ord("\n")] = 1 + table[ord("\r")] = 1 + table[ord("\f")] = 1 + table[ord("\v")] = 1 + return table + + +alias _SPACES_TABLE = _get_spaces_table() + + +fn _isspace(c: UInt8) -> Bool: """Determines whether the given character is a whitespace character. - This currently only respects the default "C" locale, i.e. returns - True only if the character specified is one of - " \n\t\r\f\v". + This only respects the default "C" locale, i.e. returns True only + if the character specified is one of " \\t\\n\\r\\f\\v". + For full Python support use `String.isspace()`. Args: c: The character to check. Returns: - True if the character is one of the whitespace characters listed above, otherwise False. + True if the character is one of the whitespace characters + listed above, otherwise False. """ - - alias ord_space = ord(" ") - alias ord_tab = ord("\t") - alias ord_carriage_return = ord("\r") - - return c == ord_space or ord_tab <= int(c) <= ord_carriage_return + return _SPACES_TABLE[int(c)] # ===----------------------------------------------------------------------===# @@ -1417,6 +1427,54 @@ struct String( substr._strref_dangerous(), start=start ) + fn isspace(self) -> Bool: + """Determines whether the given String is a python + whitespace String. This corresponds to Python's + [universal separators]( + https://docs.python.org/3/library/stdtypes.html#str.splitlines) + `" \\t\\n\\r\\f\\v\\x1c\\x1e\\x85\\u2028\\u2029"`. + + Returns: + True if the String is one of the whitespace characters + listed above, otherwise False.""" + # TODO add line and paragraph separator as stringliteral + # once unicode escape secuences are accepted + # 0 is to build a String with null terminator + alias information_sep_four = List[UInt8](0x5C, 0x78, 0x31, 0x63, 0) + """TODO: \\x1c""" + alias information_sep_two = List[UInt8](0x5C, 0x78, 0x31, 0x65, 0) + """TODO: \\x1e""" + alias next_line = List[UInt8](0x78, 0x38, 0x35, 0) + """TODO: \\x85""" + alias unicode_line_sep = List[UInt8]( + 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x38, 0 + ) + """TODO: \\u2028""" + alias unicode_paragraph_sep = List[UInt8]( + 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x39, 0 + ) + """TODO: \\u2029""" + + @always_inline + fn compare(item1: List[UInt8], item2: List[UInt8], amnt: Int) -> Bool: + var ptr1 = DTypePointer(item1.unsafe_ptr()) + var ptr2 = DTypePointer(item2.unsafe_ptr()) + return memcmp(ptr1, ptr2, amnt) == 0 + + if len(self) == 1: + return _isspace(self._buffer.unsafe_get(0)[]) + elif len(self) == 3: + return compare(self._buffer, next_line, 3) + elif len(self) == 4: + return compare(self._buffer, information_sep_four, 4) or compare( + self._buffer, information_sep_two, 4 + ) + elif len(self) == 7: + return compare(self._buffer, unicode_line_sep, 7) or compare( + self._buffer, unicode_paragraph_sep, 7 + ) + return False + fn split(self, delimiter: String) raises -> List[String]: """Split the string by a delimiter. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index ee9776bb1b..abbe8d9944 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -15,7 +15,7 @@ from bit import countr_zero from builtin.dtype import _uint_type_of_width -from builtin.string import _atol +from builtin.string import _atol, _isspace from memory import DTypePointer, UnsafePointer, memcmp @@ -488,6 +488,7 @@ struct StringRef( fn strip(self) -> StringRef: """Gets a StringRef with leading and trailing whitespaces removed. + This only takes C spaces into account: " \\t\\n\\r\\f\\v". For example, `" mojo "` returns `"mojo"`. @@ -497,9 +498,9 @@ struct StringRef( var start: Int = 0 var end: Int = len(self) var ptr = self.unsafe_ptr() - while start < end and isspace(int(ptr[start])): + while start < end and _isspace(ptr[start]): start += 1 - while end > start and isspace(int(ptr[end - 1])): + while end > start and _isspace(ptr[end - 1]): end -= 1 return StringRef(ptr + start, end - start) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 11b9631b22..8dbb579c96 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -15,6 +15,7 @@ from builtin.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, + _isspace, ) from testing import ( assert_equal, @@ -691,22 +692,70 @@ fn test_upper() raises: fn test_isspace() raises: # checking true cases - assert_true(isspace(ord(" "))) - assert_true(isspace(ord("\n"))) - assert_true(isspace(ord("\t"))) - assert_true(isspace(ord("\r"))) - assert_true(isspace(ord("\v"))) - assert_true(isspace(ord("\f"))) + assert_true(_isspace(ord(" "))) + assert_true(_isspace(ord("\n"))) + assert_true(_isspace(ord("\t"))) + assert_true(_isspace(ord("\r"))) + assert_true(_isspace(ord("\v"))) + assert_true(_isspace(ord("\f"))) # Checking false cases - assert_false(isspace(ord("a"))) - assert_false(isspace(ord("u"))) - assert_false(isspace(ord("s"))) - assert_false(isspace(ord("t"))) - assert_false(isspace(ord("i"))) - assert_false(isspace(ord("n"))) - assert_false(isspace(ord("z"))) - assert_false(isspace(ord("."))) + assert_false(_isspace(ord("a"))) + assert_false(_isspace(ord("u"))) + assert_false(_isspace(ord("s"))) + assert_false(_isspace(ord("t"))) + assert_false(_isspace(ord("i"))) + assert_false(_isspace(ord("n"))) + assert_false(_isspace(ord("z"))) + assert_false(_isspace(ord("."))) + + # test all utf8 and unicode separators + # 0 is to build a String with null terminator + alias information_sep_four = List[UInt8](0x5C, 0x78, 0x31, 0x63, 0) + """TODO: \\x1c""" + alias information_sep_two = List[UInt8](0x5C, 0x78, 0x31, 0x65, 0) + """TODO: \\x1e""" + alias next_line = List[UInt8](0x78, 0x38, 0x35, 0) + """TODO: \\x85""" + alias unicode_line_sep = List[UInt8]( + 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x38, 0 + ) + """TODO: \\u2028""" + alias unicode_paragraph_sep = List[UInt8]( + 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x39, 0 + ) + """TODO: \\u2029""" + # TODO add line and paragraph separator as stringliteral once unicode + # escape secuences are accepted + var univ_sep_var = List[String]( + String(" "), + String("\t"), + String("\n"), + String("\r"), + String("\v"), + String("\f"), + String(next_line), + String(information_sep_four), + String(information_sep_two), + String(unicode_line_sep), + String(unicode_paragraph_sep), + ) + + for b in List[UInt8](0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x38, 0): + var val = String(List[UInt8](b[], 0)) + if not (val in univ_sep_var): + assert_false(val.isspace()) + + for b in List[UInt8](0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x39, 0): + var val = String(List[UInt8](b[], 0)) + if not (val in univ_sep_var): + assert_false(val.isspace()) + + for i in univ_sep_var: + assert_true(i[].isspace()) + + for i in List[String]("not", "space", "", "s", "a", "c"): + assert_false(i[].isspace()) fn test_ascii_aliases() raises: From 1540f77596e211e25a5332a8d0a4b54eaa21ea7f Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 27 May 2024 16:07:04 -0500 Subject: [PATCH 0717/2019] [External] [stdlib] Implement `tempfile.{mkdtemp,gettempdir}` (#40647) [External] [stdlib] Implement `tempfile.{mkdtemp,gettempdir}` A new `tempfile` module is added with these two functions. Currently limited to MacOS and Linux. ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2742 --------- Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2742 MODULAR_ORIG_COMMIT_REV_ID: 943428af5d911d2f82d723f54b4029480d68ec5c --- docs/changelog.md | 6 +- stdlib/src/tempfile/__init__.mojo | 2 + stdlib/src/tempfile/tempfile.mojo | 166 ++++++++++++++++++++++++ stdlib/test/tempfile/test_tempfile.mojo | 166 ++++++++++++++++++++++++ 4 files changed, 337 insertions(+), 3 deletions(-) create mode 100644 stdlib/src/tempfile/tempfile.mojo create mode 100644 stdlib/test/tempfile/test_tempfile.mojo diff --git a/docs/changelog.md b/docs/changelog.md index ffcddda1c4..9eeae06472 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -433,9 +433,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The Mojo Language Server now supports renaming local variables. -- Added a new `tempfile` module. Similarly to Python, this will contain - utilities for creating and working with temporary files and directories. - +- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. + ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) + - Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. ([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) diff --git a/stdlib/src/tempfile/__init__.mojo b/stdlib/src/tempfile/__init__.mojo index 1df906803a..0365c38c2c 100644 --- a/stdlib/src/tempfile/__init__.mojo +++ b/stdlib/src/tempfile/__init__.mojo @@ -11,3 +11,5 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # """Implements the tempfile package.""" + +from .tempfile import gettempdir, mkdtemp diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo new file mode 100644 index 0000000000..4025b9ab3b --- /dev/null +++ b/stdlib/src/tempfile/tempfile.mojo @@ -0,0 +1,166 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements tempfile methods. + +You can import a method from the `tempfile` package. For example: + +```mojo +from tempfile import gettempdir +``` +""" + +from collections import Optional +import os +import sys +from pathlib import Path + + +alias TMP_MAX = 10_000 + + +fn _get_random_name(size: Int = 8) -> String: + alias characters = String("abcdefghijklmnopqrstuvwxyz0123456789_") + var name_list = List[UInt8](capacity=size + 1) + for _ in range(size): + var rand_index = int(random.random_ui64(0, len(characters) - 1)) + name_list.append(ord(characters[rand_index])) + name_list.append(0) + return String(name_list^) + + +fn _candidate_tempdir_list() -> List[String]: + """Generate a list of candidate temporary directories which + _get_default_tempdir will try.""" + + constrained[not sys.os_is_windows(), "windows not supported yet"]() + + var dirlist = List[String]() + var possible_env_vars = List("TMPDIR", "TEMP", "TMP") + var dirname: String + + # First, try the environment. + for env_var in possible_env_vars: + if dirname := os.getenv(env_var[]): + dirlist.append(dirname^) + + # Failing that, try OS-specific locations. + dirlist.extend(List(String("/tmp"), String("/var/tmp"), String("/usr/tmp"))) + + # As a last resort, the current directory if possible, + # os.path.getcwd() could raise + try: + dirlist.append(Path()) + except: + pass + + return dirlist + + +fn _get_default_tempdir() raises -> String: + """Calculate the default directory to use for temporary files. + + We determine whether or not a candidate temp dir is usable by + trying to create and write to a file in that directory. If this + is successful, the test file is deleted. To prevent denial of + service, the name of the test file must be randomized.""" + + var dirlist = _candidate_tempdir_list() + + for dir_name in dirlist: + if not os.path.isdir(dir_name[]): + continue + if _try_to_create_file(dir_name[]): + return dir_name[] + + raise Error("No usable temporary directory found") + + +fn _try_to_create_file(dir: String) -> Bool: + for _ in range(TMP_MAX): + var name = _get_random_name() + # TODO use os.join when it exists + var filename = Path(dir) / name + + # prevent overwriting existing file + if os.path.exists(filename): + continue + + # verify that we have writing access in the target directory + try: + with FileHandle(filename, "w"): + pass + os.remove(filename) + return True + except: + if os.path.exists(filename): + try: + os.remove(filename) + except: + pass + return False + + return False + + +fn gettempdir() -> Optional[String]: + """Return the default directory to use for temporary files. + + Returns: + The name of the default temporary directory. + """ + # TODO In python _get_default_tempdir is called exactly once so that the default + # tmp dir is the same throughout the program execution/ + # Since there is no global scope in mojo yet, this is not possible for now. + try: + return _get_default_tempdir() + except: + return None + + +fn mkdtemp( + suffix: String = "", prefix: String = "tmp", dir: Optional[String] = None +) raises -> String: + """Create a temporary directory. + Caller is responsible for deleting the directory when done with it. + + Args: + suffix: Suffix to use for the directory name. + prefix: Prefix to use for the directory name. + dir: Directory in which the directory will be created. + + Returns: + The name of the created directory. + + Raises: + If the directory can not be created. + """ + var final_dir: Path + if not dir: + final_dir = Path(_get_default_tempdir()) + else: + final_dir = Path(dir.value()[]) + + for _ in range(TMP_MAX): + var dir_name = final_dir / (prefix + _get_random_name() + suffix) + if os.path.exists(dir_name): + continue + try: + os.mkdir(dir_name, mode=0o700) + # TODO for now this name could be relative, + # python implementation expands the path, + # but several functions are not yet implemented in mojo + # i.e. abspath, normpath + return dir_name + except: + continue + raise Error("Failed to create temporary file") diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo new file mode 100644 index 0000000000..ee9713e41d --- /dev/null +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -0,0 +1,166 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +import os +from os.path import exists +from pathlib import Path +from testing import assert_true, assert_false, assert_equal +from tempfile import gettempdir, mkdtemp + + +fn test_mkdtemp() raises: + var dir_name: String + + dir_name = mkdtemp() + assert_true(exists(dir_name), "Failed to create temporary directory") + os.rmdir(dir_name) + assert_false(exists(dir_name), "Failed to delete temporary directory") + + dir_name = mkdtemp(prefix="my_prefix", suffix="my_suffix") + assert_true(exists(dir_name), "Failed to create temporary directory") + var name = dir_name.split("/")[-1] + assert_true(name.startswith("my_prefix")) + assert_true(name.endswith("my_suffix")) + + os.rmdir(dir_name) + assert_false(exists(dir_name), "Failed to delete temporary directory") + + dir_name = mkdtemp(dir=Path().__fspath__()) + assert_true(exists(dir_name), "Failed to create temporary directory") + assert_true( + exists(Path() / dir_name.split("/")[-1]), + "Expected directory to be created in cwd", + ) + os.rmdir(dir_name) + assert_false(exists(dir_name), "Failed to delete temporary directory") + + +struct TempEnvWithCleanup: + var vars_to_set: Dict[String, String] + var _vars_back: Dict[String, String] + var clean_up_function: fn () raises -> None + """Function called after the context manager exits if an error occurs.""" + + fn __init__( + inout self, + vars_to_set: Dict[String, String], + clean_up_function: fn () raises -> None, + ): + self.vars_to_set = vars_to_set + self._vars_back = Dict[String, String]() + self.clean_up_function = clean_up_function + + fn __enter__(inout self) raises: + for key_value in self.vars_to_set.items(): + var key = key_value[].key + var value = key_value[].value + self._vars_back[key] = os.getenv(key) + _ = os.setenv(key, value, overwrite=True) + + fn __exit__(inout self): + for key_value in self.vars_to_set.items(): + var key = key_value[].key + var value = key_value[].value + _ = os.setenv(key, value, overwrite=True) + + fn __exit__(inout self, error: Error) raises -> Bool: + self.__exit__() + self.clean_up_function() + return False + + +fn _clean_up_gettempdir_test() raises: + var dir_without_writing_access = Path() / "dir_without_writing_access" + if exists(dir_without_writing_access): + os.rmdir(dir_without_writing_access) + var dir_with_writing_access = Path() / "dir_with_writing_access" + if exists(dir_with_writing_access): + os.rmdir(dir_with_writing_access) + + +fn _set_up_gettempdir_test( + dir_with_writing_access: Path, dir_without_writing_access: Path +) raises: + os.mkdir(dir_with_writing_access, mode=0o700) + try: + os.mkdir(dir_without_writing_access, mode=0o100) + except: + os.rmdir(dir_with_writing_access) + raise Error("Failed to setup test") + + +fn test_gettempdir() raises: + var non_existing_dir = Path() / "non_existing_dir" + assert_false( + exists(non_existing_dir), + "Unexpected dir" + String(non_existing_dir), + ) + var dir_without_writing_access = Path() / "dir_without_writing_access" + var dir_with_writing_access = Path() / "dir_with_writing_access" + _set_up_gettempdir_test(dir_with_writing_access, dir_without_writing_access) + + var tmpdir_result: Optional[String] + var vars_to_set = Dict[String, String]() + + # test TMPDIR is used first + vars_to_set["TMPDIR"] = dir_with_writing_access + with TempEnvWithCleanup( + vars_to_set, + _clean_up_gettempdir_test, + ): + tmpdir_result = gettempdir() + assert_true(tmpdir_result, "Failed to get temporary directory") + assert_equal( + tmpdir_result.value()[], + dir_with_writing_access, + "expected to get:" + String(dir_with_writing_access), + ) + + # test gettempdir falls back to TEMP + vars_to_set["TMPDIR"] = non_existing_dir + vars_to_set["TEMP"] = dir_with_writing_access + with TempEnvWithCleanup( + vars_to_set, + _clean_up_gettempdir_test, + ): + tmpdir_result = gettempdir() + assert_true(tmpdir_result, "Failed to get temporary directory") + assert_equal( + tmpdir_result.value()[], + dir_with_writing_access, + "expected to get:" + String(dir_with_writing_access), + ) + + # test gettempdir falls back to TMP + vars_to_set["TMPDIR"] = non_existing_dir + vars_to_set["TEMP"] = non_existing_dir + vars_to_set["TMP"] = dir_with_writing_access + with TempEnvWithCleanup( + vars_to_set, + _clean_up_gettempdir_test, + ): + tmpdir_result = gettempdir() + assert_true(tmpdir_result, "Failed to get temporary directory") + assert_equal( + tmpdir_result.value()[], + dir_with_writing_access, + "expected to get:" + String(dir_with_writing_access), + ) + + _clean_up_gettempdir_test() + + +fn main() raises: + test_mkdtemp() + test_gettempdir() From 54ee9c05e3df21e0077be5dc97a35012d28b2502 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 27 May 2024 17:54:42 -0400 Subject: [PATCH 0718/2019] [stdlib][NFC] Wholesale style and docstring improvements in `string.mojo` MODULAR_ORIG_COMMIT_REV_ID: 987f77b5e724e6b57c0208b28401392d3b9de0e2 --- stdlib/src/builtin/simd.mojo | 148 +++++++++++++------------ stdlib/src/builtin/string.mojo | 196 ++++++++++++++++----------------- 2 files changed, 173 insertions(+), 171 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 141a048a1b..a4a9569c77 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -45,12 +45,12 @@ from .dtype import ( _get_dtype_printf_format, _scientific_notation_digits, ) -from .io import _snprintf_scalar, _snprintf, _printf, _print_fmt +from .io import _snprintf_scalar, _printf, _print_fmt from .string import _calc_initial_buffer_size, _calc_format_buffer_size -# ===------------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # Type Aliases -# ===------------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # alias Scalar = SIMD[size=1] """Represents a scalar dtype.""" @@ -81,9 +81,9 @@ alias Float32 = Scalar[DType.float32] alias Float64 = Scalar[DType.float64] """Represents a 64-bit floating point value.""" -# ===------------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # Utilities -# ===------------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -122,9 +122,9 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: } -# ===------------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # SIMD -# ===------------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @lldb_formatter_wrapping_type @@ -147,7 +147,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ): """Represents a small vector that is backed by a hardware vector element. - SIMD allows a single instruction to be executed across the multiple data elements of the vector. + SIMD allows a single instruction to be executed across the multiple data + elements of the vector. Constraints: The size of the SIMD vector to be positive and a power of 2. @@ -583,10 +584,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline fn __repr__(self) -> String: - """Get the representation of the SIMD value eg. "SIMD[DType.int8, 2](1, 2)". + """Get the representation of the SIMD value e.g. "SIMD[DType.int8, 2](1, 2)". + Returns: The representation of the SIMD value. - """ var output = String() @@ -613,8 +614,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ self.format_to[use_scientific_notation=False](writer) - # This overload is required to keep SIMD compliant with the Formattable trait, - # and the call to `String.format_sequence(self)` in SIMD.__str__ will fail to compile + # This overload is required to keep SIMD compliant with the Formattable + # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will + # fail to compile. fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): """ Formats this SIMD value to the provided formatter. @@ -977,9 +979,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( self.value, rhs.value ) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Unary operations. - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __pos__(self) -> Self: @@ -1140,9 +1142,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # In place operations. - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __iadd__(inout self, rhs: Self): @@ -1235,9 +1237,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() self = self.__pow__(rhs) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Checked operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline fn add_with_overflow(self, rhs: Self) -> (Self, Self._Mask): @@ -1247,8 +1249,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rhs: The rhs value. Returns: - A tuple with the results of the operation and a mask for overflows. The first is a new vector whose element at position `i` is computed as - `self[i] + rhs[i]`. The second item is a vector of booleans where a `1` at position `i` represents `self[i] + rhs[i]` overflowed. + A tuple with the results of the operation and a mask for overflows. + The first is a new vector whose element at position `i` is computed + as `self[i] + rhs[i]`. The second item is a vector of booleans where + a `1` at position `i` represents `self[i] + rhs[i]` overflowed. """ constrained[type.is_integral()]() @@ -1278,8 +1282,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rhs: The rhs value. Returns: - A tuple with the results of the operation and a mask for overflows. The first is a new vector whose element at position `i` is computed as - `self[i] - rhs[i]`. The second item is a vector of booleans where a `1` at position `i` represents `self[i] - rhs[i]` overflowed. + A tuple with the results of the operation and a mask for overflows. + The first is a new vector whose element at position `i` is computed + as `self[i] - rhs[i]`. The second item is a vector of booleans where + a `1` at position `i` represents `self[i] - rhs[i]` overflowed. """ constrained[type.is_integral()]() @@ -1309,8 +1315,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rhs: The rhs value. Returns: - A tuple with the results of the operation and a mask for overflows. The first is a new vector whose element at position `i` is computed as - `self[i] * rhs[i]`. The second item is a vector of booleans where a `1` at position `i` represents `self[i] * rhs[i]` overflowed. + A tuple with the results of the operation and a mask for overflows. + The first is a new vector whose element at position `i` is computed + as `self[i] * rhs[i]`. The second item is a vector of booleans where + a `1` at position `i` represents `self[i] * rhs[i]` overflowed. """ constrained[type.is_integral()]() @@ -1332,9 +1340,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ](self, rhs) return (result[0], result[1]) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Reversed operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __radd__(self, value: Self) -> Self: @@ -1407,9 +1415,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( self.value, multiplier.value, accumulator.value ) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Bitwise operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __and__(self, rhs: Self) -> Self: @@ -1594,9 +1602,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( else: return self ^ -1 - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Shift operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __lshift__(self, rhs: Self) -> Self: @@ -1690,17 +1698,17 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[type.is_integral(), "must be an integral type"]() return value >> self - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Shuffle operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn _shuffle_list[ *mask: Int, output_size: Int = size ](self, other: Self) -> SIMD[type, output_size]: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). The mask values - must be within `2*len(self)`. + the `other` value using the specified mask (permutation). The mask + values must be within `2 * len(self)`. Parameters: mask: The permutation to use in the shuffle. @@ -1710,8 +1718,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( other: The other vector to shuffle with. Returns: - A new vector with the same length as the mask where the value at position `i` is - `(self+other)[permutation[i]]`. + A new vector with the same length as the mask where the value at + position `i` is `(self + other)[permutation[i]]`. """ @parameter @@ -1759,8 +1767,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( output_size: Int, mask: StaticIntTuple[output_size] ](self, other: Self) -> SIMD[type, output_size]: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). The mask values - must be within `2*len(self)`. + the `other` value using the specified mask (permutation). The mask + values must be within `2 * len(self)`. Parameters: output_size: The size of the output vector. @@ -1770,8 +1778,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( other: The other vector to shuffle with. Returns: - A new vector with the same length as the mask where the value at position `i` is - `(self+other)[permutation[i]]`. + A new vector with the same length as the mask where the value at + position `i` is `(self + other)[permutation[i]]`. """ @parameter @@ -1791,23 +1799,23 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn shuffle[*mask: Int](self) -> Self: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). The mask values - must be within `2*len(self)`. + the `other` value using the specified mask (permutation). The mask + values must be within `2 * len(self)`. Parameters: mask: The permutation to use in the shuffle. Returns: - A new vector with the same length as the mask where the value at position `i` is - `(self)[permutation[i]]`. + A new vector with the same length as the mask where the value at + position `i` is `(self)[permutation[i]]`. """ return self._shuffle_list[mask](self) @always_inline("nodebug") fn shuffle[*mask: Int](self, other: Self) -> Self: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). The mask values - must be within `2*len(self)`. + the `other` value using the specified mask (permutation). The mask + values must be within `2 * len(self)`. Parameters: mask: The permutation to use in the shuffle. @@ -1816,31 +1824,31 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( other: The other vector to shuffle with. Returns: - A new vector with the same length as the mask where the value at position `i` is - `(self+other)[permutation[i]]`. + A new vector with the same length as the mask where the value at + position `i` is `(self + other)[permutation[i]]`. """ return self._shuffle_list[mask](other) @always_inline("nodebug") fn shuffle[mask: StaticIntTuple[size]](self) -> Self: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). The mask values - must be within `2*len(self)`. + the `other` value using the specified mask (permutation). The mask + values must be within `2 * len(self)`. Parameters: mask: The permutation to use in the shuffle. Returns: - A new vector with the same length as the mask where the value at position `i` is - `(self)[permutation[i]]`. + A new vector with the same length as the mask where the value at + position `i` is `(self)[permutation[i]]`. """ return self._shuffle_list[size, mask](self) @always_inline("nodebug") fn shuffle[mask: StaticIntTuple[size]](self, other: Self) -> Self: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). The mask values - must be within `2*len(self)`. + the `other` value using the specified mask (permutation). The mask + values must be within `2 * len(self)`. Parameters: mask: The permutation to use in the shuffle. @@ -1849,14 +1857,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( other: The other vector to shuffle with. Returns: - A new vector with the same length as the mask where the value at position `i` is - `(self+other)[permutation[i]]`. + A new vector with the same length as the mask where the value at + position `i` is `(self + other)[permutation[i]]`. """ return self._shuffle_list[size, mask](other) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Indexing operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Scalar[type]: @@ -2078,9 +2086,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rebind[Self._SIMDHalfType](res[1]), ) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Binary operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn min(self, other: Self) -> Self: @@ -2110,9 +2118,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() return __mlir_op.`pop.max`(self.value, other.value) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Reduce operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline fn reduce[ @@ -2417,9 +2425,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ return self.cast[DType.bool]().reduce_or() - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # select - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # TODO (7748): always_inline required to WAR LLVM codegen bug @always_inline("nodebug") @@ -2430,8 +2438,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( true_case: SIMD[result_type, size], false_case: SIMD[result_type, size], ) -> SIMD[result_type, size]: - """Selects the values of the `true_case` or the `false_case` based on the - current boolean values of the SIMD vector. + """Selects the values of the `true_case` or the `false_case` based on + the current boolean values of the SIMD vector. Parameters: result_type: The element type of the input and output SIMD vectors. @@ -2454,9 +2462,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( false_case.value, ) - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Rotation operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline fn rotate_left[shift: Int](self) -> Self: @@ -2516,9 +2524,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return self return self.rotate_left[-shift]() - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Shift operations - # ===-------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline fn shift_left[shift: Int](self) -> Self: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 6913b0e1d5..6d5235dd0f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -24,11 +24,9 @@ from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy from utils import StringRef, StaticIntTuple, Span, StringSlice from utils._format import Formattable, Formatter, ToFormatter -from .io import _snprintf - -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # ord -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn ord(s: String) -> Int: @@ -66,17 +64,17 @@ fn ord(s: String) -> Int: return result -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # chr -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn chr(c: Int) -> String: """Returns a string based on the given Unicode code point. - Returns the string representing a character whose code point is the integer `c`. - For example, `chr(97)` returns the string `"a"`. This is the inverse of the `ord()` - function. + Returns the string representing a character whose code point is the integer + `c`. For example, `chr(97)` returns the string `"a"`. This is the inverse of + the `ord()` function. Args: c: An integer that represents a code point. @@ -118,9 +116,9 @@ fn chr(c: Int) -> String: return String(p.bitcast[DType.uint8](), num_bytes + 1) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # ascii -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -183,28 +181,16 @@ fn ascii(value: String) -> String: return value.__repr__() -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # strtol -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: - """Parses the given string as an integer in the given base and returns that value. - - For example, `atol("19")` returns `19`. If the given string cannot be parsed - as an integer value, an error is raised. For example, `atol("hi")` raises an - error. - - If base is 0 the the string is parsed as an Integer literal, - see: https://docs.python.org/3/reference/lexical_analysis.html#integers - - Args: - str_ref: A string to be parsed as an integer in the given base. - base: Base used for conversion, value must be between 2 and 36, or 0. + """Implementation of `atol` for StringRef inputs. - Returns: - An integer value that represents the string, or otherwise raises. + Please see its docstring for details. """ if (base != 0) and (base < 2 or base > 36): raise Error("Base must be >= 2 and <= 36, or 0.") @@ -369,14 +355,14 @@ fn _identify_base(str_ref: StringRef, start: Int) -> Tuple[Int, Int]: fn atol(str: String, base: Int = 10) raises -> Int: - """Parses the given string as an integer in the given base and returns that value. + """Parses and returns the given string as an integer in the given base. - For example, `atol("19")` returns `19`. If the given string cannot be parsed - as an integer value, an error is raised. For example, `atol("hi")` raises an - error. + For example, `atol("19")` returns `19`. If base is 0 the the string is + parsed as an Integer literal, see: https://docs.python.org/3/reference/lexical_analysis.html#integers. - If base is 0 the the string is parsed as an Integer literal, - see: https://docs.python.org/3/reference/lexical_analysis.html#integers + Raises: + If the given string cannot be parsed as an integer value. For example in + `atol("hi")`. Args: str: A string to be parsed as an integer in the given base. @@ -394,17 +380,9 @@ fn _atof_error(str_ref: StringRef) -> Error: @always_inline fn _atof(str_ref: StringRef) raises -> Float64: - """Parses the given string as a floating point and returns that value. - - For example, `atof("2.25")` returns `2.25`. If the given string cannot be parsed - as an float value, an error is raised. For example, `atof("hi")` raises an - error. - - Args: - str_ref: A string to be parsed as a floating point. + """Implementation of `atof` for StringRef inputs. - Returns: - An float value that represents the string, or otherwise raises. + Please see its docstring for details. """ if not str_ref: raise Error(_atof_error(str_ref)) @@ -499,9 +477,11 @@ fn _atof(str_ref: StringRef) raises -> Float64: fn atof(str: String) raises -> Float64: """Parses the given string as a floating point and returns that value. - For example, `atof("2.25")` returns `2.25`. If the given string cannot be parsed - as an floating point value, an error is raised. For example, `atof("hi")` raises an - error. + For example, `atof("2.25")` returns `2.25`. + + Raises: + If the given string cannot be parsed as an floating point value, for + example in `atof("hi")`. Args: str: A string to be parsed as a floating point. @@ -512,9 +492,9 @@ fn atof(str: String) raises -> Float64: return _atof(str._strref_dangerous()) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # isdigit -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn isdigit(c: UInt8) -> Bool: @@ -531,15 +511,16 @@ fn isdigit(c: UInt8) -> Bool: return ord_0 <= int(c) <= ord_9 -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # isupper -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn isupper(c: UInt8) -> Bool: """Determines whether the given character is an uppercase character. - This currently only respects the default "C" locale, i.e. returns - True only if the character specified is one of ABCDEFGHIJKLMNOPQRSTUVWXYZ. + + This currently only respects the default "C" locale, i.e. returns True iff + the character specified is one of "ABCDEFGHIJKLMNOPQRSTUVWXYZ". Args: c: The character to check. @@ -556,15 +537,16 @@ fn _is_ascii_uppercase(c: UInt8) -> Bool: return ord_a <= int(c) <= ord_z -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # islower -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn islower(c: UInt8) -> Bool: """Determines whether the given character is an lowercase character. - This currently only respects the default "C" locale, i.e. returns - True only if the character specified is one of abcdefghijklmnopqrstuvwxyz. + + This currently only respects the default "C" locale, i.e. returns True iff + the character specified is one of "abcdefghijklmnopqrstuvwxyz". Args: c: The character to check. @@ -581,9 +563,9 @@ fn _is_ascii_lowercase(c: UInt8) -> Bool: return ord_a <= int(c) <= ord_z -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _isspace -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn _get_spaces_table() -> InlineArray[UInt8, 256]: @@ -602,23 +584,23 @@ alias _SPACES_TABLE = _get_spaces_table() fn _isspace(c: UInt8) -> Bool: """Determines whether the given character is a whitespace character. - This only respects the default "C" locale, i.e. returns True only - if the character specified is one of " \\t\\n\\r\\f\\v". - For full Python support use `String.isspace()`. + + This only respects the default "C" locale, i.e. returns True only if the + character specified is one of " \\t\\n\\r\\f\\v". For semantics similar + to Python, use `String.isspace()`. Args: c: The character to check. Returns: - True if the character is one of the whitespace characters - listed above, otherwise False. + True iff the character is one of the whitespace characters listed above. """ return _SPACES_TABLE[int(c)] -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # isprintable -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn isprintable(c: UInt8) -> Bool: @@ -635,9 +617,11 @@ fn isprintable(c: UInt8) -> Bool: return ord_space <= int(c) <= ord_tilde -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # String -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # + + struct String( Sized, Stringable, @@ -665,11 +649,16 @@ struct String( alias HEX_DIGITS = String.DIGITS + String("abcdef") + String("ABCDEF") alias OCT_DIGITS = String("01234567") alias PUNCTUATION = String("""!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~""") - alias PRINTABLE = String.DIGITS + String.ASCII_LETTERS + String.PUNCTUATION + String.WHITESPACE + alias PRINTABLE = ( + String.DIGITS + + String.ASCII_LETTERS + + String.PUNCTUATION + + String.WHITESPACE + ) - # ===------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Life cycle methods - # ===------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @always_inline fn __init__(inout self, owned impl: List[UInt8]): @@ -826,9 +815,9 @@ struct String( """ self._buffer = existing._buffer^ - # ===------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Factory dunders - # ===------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # @staticmethod fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: @@ -840,7 +829,7 @@ struct String( Parameters: Ts: The types of the arguments to format. Each type must be satisfy - `Formattable`. + `Formattable`. Returns: A string formed by formatting the argument sequence. @@ -888,9 +877,9 @@ struct String( return String(buff^) - # ===------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # # Operator dunders - # ===------------------------------------------------------------------===# + # ===------------------------------------------------------------------=== # fn __getitem__(self, idx: Int) -> String: """Gets the character at the specified position. @@ -978,7 +967,7 @@ struct String( rhs: The other String to compare against. Returns: - True if this String is less than or equal to the RHS String and False otherwise. + True iff this String is less than or equal to the RHS String. """ return not (rhs < self) @@ -990,7 +979,7 @@ struct String( rhs: The other String to compare against. Returns: - True if this String is strictly greater than the RHS String and False otherwise. + True iff this String is strictly greater than the RHS String. """ return rhs < self @@ -1002,7 +991,7 @@ struct String( rhs: The other String to compare against. Returns: - True if this String is greater than or equal to the RHS String and False otherwise. + True iff this String is greater than or equal to the RHS String. """ return not (self < rhs) @@ -1107,8 +1096,6 @@ struct String( fn __repr__(self) -> String: """Return a Mojo-compatible representation of the `String` instance. - You don't need to call this method directly, use `repr(my_string)` instead. - Returns: A new representation of the string. """ @@ -1495,15 +1482,16 @@ struct String( var current_offset = 0 while True: var loc = self.find(delimiter, current_offset) - # delimiter not found, so add the search slice from where we're currently at + # The delimiter was not found, so add the search slice from where + # we're currently at. if loc == -1: output.append(self[current_offset:]) break - # We found a delimiter, so add the preceding string slice + # We found a delimiter, so add the preceding string slice. output.append(self[current_offset:loc]) - # Advance our search offset past the delimiter + # Advance our search offset past the delimiter. current_offset = loc + len(delimiter) return output @@ -1513,11 +1501,11 @@ struct String( if replaced by `new`. Args: - old: The substring to replace. - new: The substring to replace with. + old: The substring to replace. + new: The substring to replace with. Returns: - The string where all occurrences of `old` are replaced with `new`. + The string where all occurrences of `old` are replaced with `new`. """ if not old: return self._interleave(new) @@ -1566,13 +1554,14 @@ struct String( return String(res^) fn strip(self, chars: String = String.WHITESPACE) -> String: - """Return a copy of the string with leading and trailing characters removed. + """Return a copy of the string with leading and trailing characters + removed. Args: chars: A set of characters to be removed. Defaults to whitespace. Returns: - A copy of the string with no leading or trailing characters. + A copy of the string with no leading or trailing characters. """ return self.lstrip(chars).rstrip(chars) @@ -1584,7 +1573,7 @@ struct String( chars: A set of characters to be removed. Defaults to whitespace. Returns: - A copy of the string with no trailing characters. + A copy of the string with no trailing characters. """ var r_idx = len(self) @@ -1600,7 +1589,7 @@ struct String( chars: A set of characters to be removed. Defaults to whitespace. Returns: - A copy of the string with no leading characters. + A copy of the string with no leading characters. """ var l_idx = 0 @@ -1714,8 +1703,9 @@ struct String( ) fn removeprefix(self, prefix: String, /) -> String: - """If the string starts with the prefix string, return `string[len(prefix):]`. - Otherwise, return a copy of the original string. + """Returns a new string with the prefix removed if it was present. + + For example: ```mojo print(String('TestHook').removeprefix('Test')) @@ -1725,18 +1715,20 @@ struct String( ``` Args: - prefix: The prefix to remove from the string. + prefix: The prefix to remove from the string. Returns: - A new string with the prefix removed if it was present. + `string[len(prefix):]` if the string starts with the prefix string, + or a copy of the original string otherwise. """ if self.startswith(prefix): return self[len(prefix) :] return self fn removesuffix(self, suffix: String, /) -> String: - """If the string ends with the suffix string, return `string[:-len(suffix)]`. - Otherwise, return a copy of the original string. + """Returns a new string with the suffix removed if it was present. + + For example: ```mojo print(String('TestHook').removesuffix('Hook')) @@ -1746,10 +1738,11 @@ struct String( ``` Args: - suffix: The suffix to remove from the string. + suffix: The suffix to remove from the string. Returns: - A new string with the suffix removed if it was present. + `string[:-len(suffix)]` if the string ends with the suffix string, + or a copy of the original string otherwise. """ if self.endswith(suffix): return self[: -len(suffix)] @@ -1791,13 +1784,14 @@ struct String( return String(buf^) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # Utilities -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn _toggle_ascii_case(char: UInt8) -> UInt8: - """Assuming char is a cased ASCII character, this function will return the opposite-cased letter + """Assuming char is a cased ASCII character, this function will return the + opposite-cased letter. """ # ASCII defines A-Z and a-z as differing only in their 6th bit, From 19fd583100cd0c973dfb1e6127de224fbd49e46f Mon Sep 17 00:00:00 2001 From: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Date: Mon, 27 May 2024 17:52:43 -0500 Subject: [PATCH 0719/2019] [External] [stdlib] implement `has_single_bit` (#40693) [External] [stdlib] Implement `has_single_bit` Implement `has_single_bit` for checking if a value is an integral power of `2`. Fixes https://github.com/modularml/mojo/issues/2682. Co-authored-by: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Closes modularml/mojo#2859 MODULAR_ORIG_COMMIT_REV_ID: 15d31fa55f239879d4c4049557e308b85e0eb4f7 --- stdlib/src/bit/__init__.mojo | 1 + stdlib/src/bit/bit.mojo | 50 ++++++++++++++++++++++++++++++++--- stdlib/test/bit/test_bit.mojo | 31 ++++++++++++++++++++++ 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index 5b4ef1ddce..f448810c60 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -22,6 +22,7 @@ from .bit import ( bit_width, rotate_bits_left, rotate_bits_right, + has_single_bit, bit_ceil, bit_floor, ) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 31e26378f8..25e28a4302 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -246,8 +246,6 @@ fn bit_not[ return __mlir_op.`pop.xor`(val.value, neg_one.value) -# TODO: implement bit_ceil, bit_floor, has_single_bit, et al - # ===----------------------------------------------------------------------===# # bit_width # ===----------------------------------------------------------------------===# @@ -304,6 +302,50 @@ fn bit_width[ return bitwidth - leading_zero +# ===----------------------------------------------------------------------===# +# has_single_bit +# ===----------------------------------------------------------------------===# +# reference: https://en.cppreference.com/w/cpp/numeric/has_single_bit + + +@always_inline +fn has_single_bit(val: Int) -> Bool: + """Checks if the input value is a power of 2. + + Args: + val: The input value. + + Returns: + True if the input value is a power of 2, False otherwise. + """ + return val > 0 and not (val & (val - 1)) + + +@always_inline +fn has_single_bit[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[DType.bool, simd_width]: + """Checks if the input value is a power of 2 for each element of a SIMD vector. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` is True if the integer at + position `i` of the input value is a power of 2, False otherwise. + """ + constrained[type.is_integral(), "must be integral"]() + + return (val > 0) & ((val & (val - 1)) == 0) + + # ===----------------------------------------------------------------------===# # bit_ceil # ===----------------------------------------------------------------------===# @@ -313,7 +355,7 @@ fn bit_width[ @always_inline("nodebug") fn bit_ceil(val: Int) -> Int: """Computes the smallest power of 2 that is greater than or equal to the - input value. Any integral value less than or equal to 1 be ceiled to 1. + input value. Any integral value less than or equal to 1 will be ceiled to 1. Args: val: The input value. @@ -324,7 +366,7 @@ fn bit_ceil(val: Int) -> Int: if val <= 1: return 1 - if val & (val - 1) == 0: + if has_single_bit(val): return val return 1 << bit_width(val - 1) diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 1378d6a81e..0a4f87166b 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -18,11 +18,40 @@ from bit import ( bit_width, bit_ceil, bit_floor, + has_single_bit, ) from testing import assert_equal +def test_has_single_bit(): + assert_equal(has_single_bit(-1), False) + assert_equal(has_single_bit(0), False) + assert_equal(has_single_bit(1), True) + assert_equal(has_single_bit(2), True) + assert_equal(has_single_bit(3), False) + assert_equal(has_single_bit(4), True) + assert_equal(has_single_bit(5), False) + + +def test_has_single_bit_simd(): + alias simd_width = 4 + alias type = DType.int8 + alias return_type = DType.bool + + alias var1 = SIMD[type, simd_width](-1, 0, 1, 2) + assert_equal( + has_single_bit(var1), + SIMD[return_type, simd_width](False, False, True, True), + ) + + alias var2 = SIMD[type, simd_width](3, 4, 5, 8) + assert_equal( + has_single_bit(var2), + SIMD[return_type, simd_width](False, True, False, True), + ) + + def test_bit_width(): assert_equal(bit_width(-2), 1) assert_equal(bit_width(-1), 0) @@ -136,3 +165,5 @@ def main(): test_bit_floor_simd() test_bit_width() test_bit_width_simd() + test_has_single_bit() + test_has_single_bit_simd() From 6ae6b58dc7e1c9d356f808a3b7c0c2505d7c551f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 05:35:41 -0400 Subject: [PATCH 0720/2019] [stdlib] Move `fmt` argument of `io._snprintf` to parameter Since it's a StringLiteral, and those will soon be forbidden in runtime arguments. MODULAR_ORIG_COMMIT_REV_ID: 65f9c07180cd29cf9cd810ee5e5819d32e540bcd --- stdlib/src/builtin/int.mojo | 7 +------ stdlib/src/builtin/io.mojo | 26 +++++++++++--------------- stdlib/src/utils/index.mojo | 15 ++++++--------- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index cb2fe0a980..13b5edeacb 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -395,12 +395,7 @@ struct Int( var buf = InlineArray[UInt8, size](fill=0) # Format the integer to the local byte array - var len = _snprintf( - buf.unsafe_ptr(), - size, - "%li", - self.value, - ) + var len = _snprintf["%li"](buf.unsafe_ptr(), size, self.value) # Create a StringRef that does NOT include the NUL terminator written # to the buffer. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 425990c8f9..0d38dc2194 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -135,19 +135,17 @@ fn _printf[ @no_inline fn _snprintf[ - *types: AnyType -]( - str: UnsafePointer[UInt8], - size: Int, - fmt: StringLiteral, - *arguments: *types, -) -> Int: + fmt: StringLiteral, *types: AnyType +](str: UnsafePointer[UInt8], size: Int, *arguments: *types) -> Int: """Writes a format string into an output pointer. + Parameters: + fmt: A format string. + types: The types of arguments interpolated into the format string. + Args: str: A pointer into which the format string is written. size: At most, `size - 1` bytes are written into the output string. - fmt: A format string. arguments: Arguments interpolated into the format string. Returns: @@ -182,16 +180,14 @@ fn _snprintf_scalar[ type: DType, float_format: StringLiteral = "%.17g", ](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type]) -> Int: - alias format = _get_dtype_printf_format[type]() - @parameter if type == DType.bool: if x: - return _snprintf(buffer, size, "True") + return _snprintf["True"](buffer, size) else: - return _snprintf(buffer, size, "False") + return _snprintf["False"](buffer, size) elif type.is_integral() or type == DType.address: - return _snprintf(buffer, size, format, x) + return _snprintf[_get_dtype_printf_format[type]()](buffer, size, x) elif ( type == DType.float16 or type == DType.bfloat16 or type == DType.float32 ): @@ -209,12 +205,12 @@ fn _snprintf_scalar[ @no_inline fn _float_repr[ - format: StringLiteral = "%.17g" + fmt: StringLiteral = "%.17g" ](buffer: UnsafePointer[UInt8], size: Int, x: Float64) -> Int: # Using `%.17g` with decimal check is equivalent to CPython's fallback path # when its more complex dtoa library (forked from # https://github.com/dtolnay/dtoa) is not available. - var n = _snprintf(buffer, size, format, x.value) + var n = _snprintf[fmt](buffer, size, x.value) # If the buffer isn't big enough to add anything, then just return. if n + 2 >= size: return n diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 1b28607cc2..a40ea49d94 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -646,22 +646,19 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): buf.reserve(initial_buffer_size) # Print an opening `(`. - buf.size += _snprintf(buf.data, 2, "(") + buf.size += _snprintf["("](buf.data, 2) for i in range(size): # Print separators between each element. if i != 0: - buf.size += _snprintf(buf.data + buf.size, 3, ", ") - buf.size += _snprintf( - buf.data + buf.size, - _calc_initial_buffer_size(self[i]), - _get_dtype_printf_format[DType.index](), - self[i], + buf.size += _snprintf[", "](buf.data + buf.size, 3) + buf.size += _snprintf[_get_dtype_printf_format[DType.index]()]( + buf.data + buf.size, _calc_initial_buffer_size(self[i]), self[i] ) # Single element tuples should be printed with a trailing comma. if size == 1: - buf.size += _snprintf(buf.data + buf.size, 2, ",") + buf.size += _snprintf[","](buf.data + buf.size, 2) # Print a closing `)`. - buf.size += _snprintf(buf.data + buf.size, 2, ")") + buf.size += _snprintf[")"](buf.data + buf.size, 2) buf.size += 1 # for the null terminator. return buf^ From 3ffc92d12269e416eb2cdb7a9154cc4d58ae46e8 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 10:43:41 -0400 Subject: [PATCH 0721/2019] [stdlib] Do not use `StringLiteral` in `print` signature Since using it in runtime arguments will soon be prohibited. Since `print` currently already allocates, this patch just uses `Stringable` for `end` and `sep`. MODULAR_ORIG_COMMIT_REV_ID: 1a963fdee20062bbd689c367f1e93c0cfae4d4c2 --- stdlib/src/builtin/io.mojo | 85 +++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 0d38dc2194..56535b481f 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -362,10 +362,73 @@ fn _put(x: DType, file: FileDescriptor = stdout): @no_inline fn print[ *Ts: Stringable +](*values: *Ts, flush: Bool = False, file: FileDescriptor = stdout): + """Prints elements to the text stream. Each element is separated by a + whitespace and followed by a newline character. + + Parameters: + Ts: The elements types. + + Args: + values: The elements to print. + flush: If set to true, then the stream is forcibly flushed. + file: The output stream. + """ + _print(values, sep=" ", end="\n", flush=flush, file=file) + + +@no_inline +fn print[ + *Ts: Stringable, EndTy: Stringable ]( *values: *Ts, - sep: StringLiteral = " ", - end: StringLiteral = "\n", + end: EndTy, + flush: Bool = False, + file: FileDescriptor = stdout, +): + """Prints elements to the text stream. Each element is separated by a + whitespace and followed by `end`. + + Parameters: + Ts: The elements types. + EndTy: The type of end argument. + + Args: + values: The elements to print. + end: The String to write after printing the elements. + flush: If set to true, then the stream is forcibly flushed. + file: The output stream. + """ + _print(values, sep=" ", end=str(end), flush=flush, file=file) + + +@no_inline +fn print[ + SepTy: Stringable, *Ts: Stringable +](*values: *Ts, sep: SepTy, flush: Bool = False, file: FileDescriptor = stdout): + """Prints elements to the text stream. Each element is separated by `sep` + and followed by a newline character. + + Parameters: + SepTy: The type of separator. + Ts: The elements types. + + Args: + values: The elements to print. + sep: The separator used between elements. + flush: If set to true, then the stream is forcibly flushed. + file: The output stream. + """ + _print(values, sep=str(sep), end="\n", flush=flush, file=file) + + +@no_inline +fn print[ + SepTy: Stringable, EndTy: Stringable, *Ts: Stringable +]( + *values: *Ts, + sep: SepTy, + end: EndTy, flush: Bool = False, file: FileDescriptor = stdout, ): @@ -373,6 +436,8 @@ fn print[ and followed by `end`. Parameters: + SepTy: The type of separator. + EndTy: The type of end argument. Ts: The elements types. Args: @@ -382,8 +447,7 @@ fn print[ flush: If set to true, then the stream is forcibly flushed. file: The output stream. """ - - _print(values=values, sep=sep, end=end, flush=flush, file=file.value) + _print(values, sep=str(sep), end=str(end), flush=flush, file=file) @no_inline @@ -391,10 +455,11 @@ fn _print[ *Ts: Stringable ]( values: VariadicPack[_, _, Stringable, Ts], - sep: StringLiteral = " ", - end: StringLiteral = "\n", - flush: Bool = False, - file: Int = 1, + *, + sep: String, + end: String, + flush: Bool, + file: FileDescriptor, ): @parameter fn print_with_separator[i: Int, T: Stringable](value: T): @@ -402,11 +467,11 @@ fn _print[ @parameter if i < values.__len__() - 1: - _put(StringRef(sep), file=file) + _put(sep, file=file) values.each_idx[print_with_separator]() - _put(StringRef(end), file=file) + _put(end, file=file) if flush: _flush(file=file) From 0ac380e500b4b9df7134c8f9c79e0c6797fe784f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 12:09:36 -0400 Subject: [PATCH 0722/2019] [stdlib] Remove `math.identity` The implementation is trivial, but it only supported SIMD. Eventually we might provide this in a different location, but it certainly doesn't belong to `math`. MODULAR_ORIG_COMMIT_REV_ID: 5de8e10717662ded4d3effc29fc2c463acc717ca --- docs/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9eeae06472..1a32b01db0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -546,7 +546,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `add`, `sub`, `mul`, `div`, and `mod` functions have been removed from the `math` module. Instead, users should rely on the `+`, `-`, `*`, `/`, and `%` - operators, respectively. + operators, respectively. The `math.identity` function is also removed; users + can implement it trivially. - The `math.roundeven` function has been removed from the `math` module. The new `SIMD.roundeven` method now provides the identical functionality. From 51daf776a89a6b50cb8dcff3665e221dd5ef3bb3 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 12:38:46 -0400 Subject: [PATCH 0723/2019] [stdlib] Remove a bunch of logical functions from `math` These are trivial to implement if someone needs a callback, but were untested and only worked for SIMD. MODULAR_ORIG_COMMIT_REV_ID: 0052da6b5dd7bf723b9abd614d6432485cab3b1c --- docs/changelog.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1a32b01db0..53469d64eb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -544,10 +544,12 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `math.round_half_down` and `math.round_half_up` functions are removed. These can be trivially implemented using the `ceil` and `floor` functions. -- The `add`, `sub`, `mul`, `div`, and `mod` functions have been removed from the - `math` module. Instead, users should rely on the `+`, `-`, `*`, `/`, and `%` - operators, respectively. The `math.identity` function is also removed; users - can implement it trivially. +- The `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, + `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and + `logical_not`, functions have been removed from the `math` module. Instead, + users should rely directly on the `+`, `-`, `*`, `/`, `%`, `>`, `>=`, `<`, + `<=`, `==`, `!=`, `&`, `^`, and `~` operators, respectively. The + `math.identity` function is also removed; users can implement it trivially. - The `math.roundeven` function has been removed from the `math` module. The new `SIMD.roundeven` method now provides the identical functionality. From cef737e027d73736eef1697167e591da6753bc08 Mon Sep 17 00:00:00 2001 From: Yihua Lou <1237500+yihualou@users.noreply.github.com> Date: Tue, 28 May 2024 10:17:37 -0700 Subject: [PATCH 0724/2019] [stdlib] Add PyGILState_{Ensure, Release} methods Required to associate non-Python created threads when using the CPython API. See https://docs.python.org/3/c-api/init.html for more details. MODULAR_ORIG_COMMIT_REV_ID: 0be98e7b5371c7b492f12aee19ac18e13087be67 --- stdlib/src/python/_cpython.mojo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index dd42a88c98..9b1234316e 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -245,6 +245,12 @@ struct CPython: self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) self._dec_total_rc() + fn PyGILState_Ensure(inout self) -> Int64: + return self.lib.get_function[fn () -> Int64]("PyGILState_Ensure")() + + fn PyGILState_Release(inout self, state: Int64): + self.lib.get_function[fn (Int64) -> None]("PyGILState_Release")(state) + # This function assumes a specific way PyObjectPtr is implemented, namely # that the refcount has offset 0 in that structure. That generally doesn't # have to always be the case - but often it is and it's convenient for From 3c101c513f65afa6192c414651f1beecd5e0383d Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 13:59:07 -0400 Subject: [PATCH 0725/2019] [stdlib] Remove `math.reciprocal` and `math.select` These can be trivially implemented, but only worked on SIMD. MODULAR_ORIG_COMMIT_REV_ID: 0130d1d372e33e6c037909e9ce030aca105394fd --- docs/changelog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 53469d64eb..6bf96bdedc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -548,8 +548,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and `logical_not`, functions have been removed from the `math` module. Instead, users should rely directly on the `+`, `-`, `*`, `/`, `%`, `>`, `>=`, `<`, - `<=`, `==`, `!=`, `&`, `^`, and `~` operators, respectively. The - `math.identity` function is also removed; users can implement it trivially. + `<=`, `==`, `!=`, `&`, `^`, and `~` operators, respectively. The `identity` + `reciprocal` functions are also removed; users can implement these trivially. + The `select` function is removed in favor of using `SIMD.select` directly. - The `math.roundeven` function has been removed from the `math` module. The new `SIMD.roundeven` method now provides the identical functionality. From 881700fdee6f8efc37270f4d0f4f7b031a9cfe3b Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 28 May 2024 11:48:57 -0700 Subject: [PATCH 0726/2019] [mojo] Fix some invalid `AnyType -> AnyRegType` bindings (NFC) Binding a generic Mojo type to `AnyRegType` is invalid, because the latter assumes that the type is register passable (and trivial -- it is incorrectly named). The compiler allow this for legacy reasons but we intend to make it an error so the code is semantically correct and safety is enforced. This PR starts transitioning some violations of this: - `get_sizeof` and `get_alignof` are changed to take an `AnyType` - some invalid rebinds of generic types were fixed MODULAR_ORIG_COMMIT_REV_ID: a0b9e17e8786c1ea418bf18091bb4e1b5f062583 --- stdlib/src/builtin/builtin_list.mojo | 6 ++++-- stdlib/src/sys/info.mojo | 22 ++++++++++++++++++---- stdlib/src/utils/index.mojo | 12 +++++++++--- stdlib/src/utils/variant.mojo | 4 ++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 0135d962f8..1847c30826 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -63,7 +63,7 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): return len(self.storage) @always_inline("nodebug") - fn get[i: Int, T: Movable](self) -> T: + fn get[i: Int, T: Movable](self) -> ref [__lifetime_of(self)] T: """Get a list element at the given index. Parameters: @@ -73,7 +73,9 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): Returns: The element at the given index. """ - return rebind[T](self.storage[i]) + return rebind[Reference[T, False, __lifetime_of(self)]]( + Reference(self.storage[i]) + )[] # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 69fddd2d6f..239970ff7e 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -510,7 +510,7 @@ fn simdbytewidth[ @always_inline("nodebug") fn sizeof[ - type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() + type: AnyType, target: __mlir_type.`!kgen.target` = _current_target() ]() -> IntLiteral: """Returns the size of (in bytes) of the type. @@ -521,9 +521,16 @@ fn sizeof[ Returns: The size of the type in bytes. """ + alias mlir_type = __mlir_attr[ + `#kgen.param.expr> : `, + AnyType, + `> : !kgen.type`, + ] return __mlir_attr[ `#kgen.param.expr : !kgen.type,`, target, `> : !kgen.int_literal`, @@ -556,7 +563,7 @@ fn sizeof[ @always_inline("nodebug") fn alignof[ - type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() + type: AnyType, target: __mlir_type.`!kgen.target` = _current_target() ]() -> IntLiteral: """Returns the align of (in bytes) of the type. @@ -567,9 +574,16 @@ fn alignof[ Returns: The alignment of the type in bytes. """ + alias mlir_type = __mlir_attr[ + `#kgen.param.expr> : `, + AnyType, + `> : !kgen.type`, + ] return __mlir_attr[ `#kgen.param.expr : !kgen.type,`, target, `> : !kgen.int_literal`, diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index a40ea49d94..041dbbc0b9 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -224,7 +224,9 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): @parameter fn fill[idx: Int](): - tup[idx] = rebind[Int](elems[idx]) + tup[idx] = rebind[Reference[Int, False, __lifetime_of(elems)]]( + Reference(elems[idx]) + )[] unroll[fill, 2]() @@ -249,7 +251,9 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): @parameter fn fill[idx: Int](): - tup[idx] = rebind[Int](elems[idx]) + tup[idx] = rebind[Reference[Int, False, __lifetime_of(elems)]]( + Reference(elems[idx]) + )[] unroll[fill, 3]() @@ -274,7 +278,9 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): @parameter fn fill[idx: Int](): - tup[idx] = rebind[Int](elems[idx]) + tup[idx] = rebind[Reference[Int, False, __lifetime_of(elems)]]( + Reference(elems[idx]) + )[] unroll[fill, 4]() diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index b1dcc7cd57..5a0eb89432 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -39,7 +39,7 @@ print(to_string(who_knows)) """ from sys import alignof, sizeof -from sys.intrinsics import _mlirtype_is_eq +from sys.intrinsics import _type_is_eq from memory import UnsafePointer from memory.unsafe_pointer import ( @@ -90,7 +90,7 @@ struct _UnionTypeIndex[T: CollectionElement, *Ts: CollectionElement]: alias q = Ts[i] @parameter - if _mlirtype_is_eq[q, T](): + if _type_is_eq[q, T](): result = i unroll[each, len(VariadicList(Ts))]() From 407b3788b8fb4d12b21708e3f9abdf58a9e580bb Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 14:53:50 -0400 Subject: [PATCH 0727/2019] [stdlib] Remove `math.is_{even,odd}` Untested and unused, only working for `SIMD` and `Int`. MODULAR_ORIG_COMMIT_REV_ID: acf534fb3c285eb55eca16f48d48192150829408 --- docs/changelog.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6bf96bdedc..0882e770ed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -544,13 +544,16 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `math.round_half_down` and `math.round_half_up` functions are removed. These can be trivially implemented using the `ceil` and `floor` functions. -- The `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, - `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and - `logical_not`, functions have been removed from the `math` module. Instead, - users should rely directly on the `+`, `-`, `*`, `/`, `%`, `>`, `>=`, `<`, - `<=`, `==`, `!=`, `&`, `^`, and `~` operators, respectively. The `identity` - `reciprocal` functions are also removed; users can implement these trivially. - The `select` function is removed in favor of using `SIMD.select` directly. +- The following functions have been removed from the math module: + - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, + `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and + `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, + `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, + respectively. + - `identity` and `reciprocal`; users can implement these trivially. + - `select`; in favor of using `SIMD.select` directly. + - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` + with `1`. - The `math.roundeven` function has been removed from the `math` module. The new `SIMD.roundeven` method now provides the identical functionality. From c0fd4412cf1a9cae776bb4a7e7441cef33760c0a Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 28 May 2024 12:57:27 -0700 Subject: [PATCH 0728/2019] Revert "[External] [stdlib] Support `Dict.popitem()` (#40688)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces nondeterminism into the testsuite. `test_dict.mojo` nondeterministically fails with ``` [M] ➜ modular git:(1853f9d3e9) mojo /Users/jeff/Documents/modular/******/test/stdlib/collections/test_dict.mojo Test test_basic ...PASS Test test_multiple_resizes ...PASS Test test_big_dict ...PASS Test test_compact ...PASS Test test_compact_with_elements ...PASS Test test_pop_default ...PASS Test test_key_error ...PASS Test test_iter ...PASS Test test_iter_keys ...PASS Test test_iter_values ...PASS Test test_iter_values_mut ...PASS Test test_iter_items ...PASS Test test_dict_copy ...PASS Test test_dict_copy_add_new_item ...PASS Test test_dict_copy_delete_original ...PASS Test test_dict_copy_calls_copy_constructor ...PASS Test test_dict_update_nominal ...PASS Test test_dict_update_empty_origin ...PASS Test test_dict_update_empty_new ...PASS Test test_mojo_issue_1729 ...PASS Test test dict or ...PASS Test test dict popteim ...get: wrong variant type Please submit a bug report to https://github.com/modularml/mojo/issues and include the crash backtrace along with all the relevant source codes. Stack dump: 0. Program arguments: mojo /Users/jeff/Documents/modular/******/test/stdlib/collections/test_dict.mojo #0 0x00000001043a10b0 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x1000c90b0) #1 0x000000010439f210 llvm::sys::RunSignalHandlers() (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x1000c7210) #2 0x00000001043a1750 SignalHandler(int) (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x1000c9750) #3 0x00000001ab1b2a24 (/usr/lib/system/libsystem_platform.dylib+0x18042ea24) #4 0xffff8002a81b8510 #5 0x00000001047c1608 M::KGEN::ExecutionEngine::runProgram(llvm::StringRef, llvm::StringRef, llvm::function_ref) (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x1004e9608) #6 0x00000001042f8270 executeMain(mlir::ModuleOp, mlir::SymbolTable const&, M::KGEN::ExecutionEngine*, M::LLCL::Runtime&, llvm::ArrayRef) (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x100020270) #7 0x00000001042f7cb8 run(M::State const&) (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x10001fcb8) #8 0x00000001042df774 main (/Users/jeff/Documents/modular/.derived/build-relwithdebinfo/bin/mojo+0x100007774) #9 0x00000001aae2bf28 [1] 44318 trace trap mojo ``` MODULAR_ORIG_COMMIT_REV_ID: ee1c665669902106df680fe6c6d2599897665ff5 --- docs/changelog.md | 4 ---- stdlib/src/collections/dict.mojo | 28 -------------------------- stdlib/test/collections/test_dict.mojo | 16 --------------- 3 files changed, 48 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0882e770ed..d9b2cb8497 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,10 +18,6 @@ what we publish. ### ⭐️ New -- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. -([PR #2701](https://github.com/modularml/mojo/pull/2701) -by [@jayzhan211](https://github.com/jayzhan211)) - - Add a `sort` function for list of `ComparableCollectionElement`s. [PR #2609](https://github.com/modularml/mojo/pull/2609) by [@mzaks](https://github.com/mzaks) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index f5ac1528c9..e3977e1371 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -799,34 +799,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( return default.value()[] raise "KeyError" - fn popitem(inout self) raises -> DictEntry[K, V]: - """Remove and return a (key, value) pair from the dictionary. Pairs are returned in LIFO order. - popitem() is useful to destructively iterate over a dictionary, as often used in set algorithms. - If the dictionary is empty, calling popitem() raises a KeyError. - - Args: None - - Returns: - Last dictionary item - - Raises: - "KeyError" if the dictionary is empty. - """ - - var key = Optional[K](None) - var val = Optional[V](None) - - for item in reversed(self.items()): - key = Optional(item[].key) - val = Optional(item[].value) - break - - if key: - _ = self.pop(key.value()[]) - return DictEntry[K, V](key.value()[], val.value()[]) - - raise "KeyError: popitem(): dictionary is empty" - fn keys( self: Reference[Self, _, _] ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 0788e4add1..b51f0cb13b 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -452,7 +452,6 @@ def test_dict(): test["test_dict_update_empty_new", test_dict_update_empty_new]() test["test_mojo_issue_1729", test_mojo_issue_1729]() test["test dict or", test_dict_or]() - test["test dict popteim", test_dict_popitem]() def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): @@ -513,21 +512,6 @@ def test_find_get(): assert_equal(some_dict.get("not_key", 0), 0) -def test_dict_popitem(): - var dict = Dict[String, Int]() - dict["a"] = 1 - dict["b"] = 2 - - var item = dict.popitem() - assert_equal(item.key, "b") - assert_equal(item.value, 2) - item = dict.popitem() - assert_equal(item.key, "a") - assert_equal(item.value, 1) - with assert_raises(contains="KeyError"): - _ = dict.popitem() - - fn test_clear() raises: var some_dict = Dict[String, Int]() some_dict["key"] = 1 From 0db62fdb031c72d2a02ac478a98f3e74f6230f1f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 16:02:23 -0400 Subject: [PATCH 0729/2019] [stdlib] Rename `bit.byte_reverse` to `bit.byte_swap` This is the more familiar name for most developers, so we should stick to that. MODULAR_ORIG_COMMIT_REV_ID: e098460c01d9343e33f0a9fce0ced2564d09a5f4 --- docs/changelog.md | 2 +- stdlib/src/bit/__init__.mojo | 2 +- stdlib/src/bit/bit.mojo | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d9b2cb8497..a0bf75c762 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -500,7 +500,7 @@ what we publish. - `cttz` -> `countr_zero` - `bit_length` -> `bit_width` - `ctpop` -> `pop_count` - - `bswap` -> `byte_reverse` + - `bswap` -> `byte_swap` - `bitreverse` -> `bit_reverse` - The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index f448810c60..4273d9db83 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -16,7 +16,7 @@ from .bit import ( countl_zero, countr_zero, bit_reverse, - byte_reverse, + byte_swap, pop_count, bit_not, bit_width, diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 25e28a4302..37a79685cf 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -142,12 +142,12 @@ fn bit_reverse[ # ===----------------------------------------------------------------------===# -# byte_reverse +# byte_swap # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn byte_reverse[ +fn byte_swap[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Byte-swaps a value. From 23e1fadd553dda60cc3e72c6927065e9d06672eb Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 28 May 2024 13:10:51 -0700 Subject: [PATCH 0730/2019] [mojo] Remove more invalid rebinds of generic types (NFC) This PR removes more invalid rebinds of generic types to AnyRegType. MODULAR_ORIG_COMMIT_REV_ID: 01fb1cd8d4f5f3095dc989dd606e4912b4e9304c --- stdlib/src/builtin/tuple.mojo | 6 ++++-- stdlib/src/collections/inline_list.mojo | 7 +++++-- stdlib/src/collections/list.mojo | 4 ++-- stdlib/src/utils/static_tuple.mojo | 11 +++++++---- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 5873290944..04962e37e2 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -177,7 +177,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[i: Int, T: Movable](self) -> T: + fn get[i: Int, T: Movable](self) -> ref [__lifetime_of(self)] T: """Get a tuple element and rebind to the specified type. Parameters: @@ -187,7 +187,9 @@ struct Tuple[*element_types: Movable](Sized, Movable): Returns: The tuple element at the requested index. """ - return rebind[T](self[i]) + return rebind[Reference[T, False, __lifetime_of(self)]]( + Reference(self[i]) + )[] @always_inline("nodebug") fn __contains__[T: EqualityComparable](self, value: T) -> Bool: diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index d8a843d911..9e7df8caa0 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -189,7 +189,7 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): _type_is_eq[ElementType, C](), "value type is not self.ElementType" ]() for i in self: - if value == rebind[C](i[]): + if value == rebind[Reference[C, False, __lifetime_of(self)]](i)[]: return True return False @@ -217,7 +217,10 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): var count = 0 for elem in self: - if value == rebind[C](elem[]): + if ( + value + == rebind[Reference[C, False, __lifetime_of(self)]](elem)[] + ): count += 1 return count diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 87fa3483b8..993116a11e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -216,7 +216,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): @always_inline fn __contains__[ T2: ComparableCollectionElement - ](self: List[T2], value: T) -> Bool: + ](self: List[T], value: T2) -> Bool: """Verify if a given value is present in the list. ```mojo @@ -236,7 +236,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): constrained[_type_is_eq[T, T2](), "value type is not self.T"]() for i in self: - if i[] == rebind[T2](value): + if rebind[Reference[T2, False, __lifetime_of(self)]](i)[] == value: return True return False diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 358a9f3506..aa3218bd7c 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -446,9 +446,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): return UnsafePointer(self._array).bitcast[Self.ElementType]() @always_inline - fn __contains__[ - T: ComparableCollectionElement - ](self: Reference[InlineArray[T, size]], value: Self.ElementType) -> Bool: + fn __contains__[T: ComparableCollectionElement](self, value: T) -> Bool: """Verify if a given value is present in the array. ```mojo @@ -474,6 +472,11 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): # TODO: use @parameter for soon once it stabilizes a bit for i in range(size): - if self[][i] == rebind[T](value): + if ( + rebind[Reference[T, False, __lifetime_of(self)]]( + Reference(self[i]) + )[] + == value + ): return True return False From 8fc24497f1fc359b47ff1bb5e2c01f2a11e1964c Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 28 May 2024 15:22:54 -0500 Subject: [PATCH 0731/2019] [External] [stdlib] Make `String.split()` default to whitespace & fix behavior to be pythonic (#40714) [External] [stdlib] Make `String.split()` default to whitespace & fix behavior to be pythonic Closes #2686 `String.split()` now defaults to whitespace and has pythonic behavior in that it removes all adjacent whitespaces by default. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2711 --------- Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#2711 MODULAR_ORIG_COMMIT_REV_ID: b6a05e97f8de09a2c77b272b6cd8b96da3c5c782 --- docs/changelog.md | 3 + stdlib/src/builtin/string.mojo | 168 +++++++++++++++++++++++---- stdlib/test/builtin/test_string.mojo | 55 +++++++-- 3 files changed, 193 insertions(+), 33 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a0bf75c762..47f1734382 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -492,6 +492,9 @@ what we publish. - Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), use `String.isspace()` instead. +- `String.split()` now defaults to whitespace and has pythonic behavior in that + it removes all adjacent whitespaces by default. + - Added `UnsafePointer.offset()` method. - The `math.bit` module has been moved to a new top-level `bit` module. The diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 6d5235dd0f..cebc608e2a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -641,7 +641,6 @@ struct String( """The underlying storage for the string.""" """ Useful string aliases. """ - alias WHITESPACE = String(" \n\t\r\f\v") alias ASCII_LOWERCASE = String("abcdefghijklmnopqrstuvwxyz") alias ASCII_UPPERCASE = String("ABCDEFGHIJKLMNOPQRSTUVWXYZ") alias ASCII_LETTERS = String.ASCII_LOWERCASE + String.ASCII_UPPERCASE @@ -653,7 +652,7 @@ struct String( String.DIGITS + String.ASCII_LETTERS + String.PUNCTUATION - + String.WHITESPACE + + " \t\n\r\v\f" # single byte utf8 whitespaces ) # ===------------------------------------------------------------------=== # @@ -1462,37 +1461,125 @@ struct String( ) return False - fn split(self, delimiter: String) raises -> List[String]: - """Split the string by a delimiter. + fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]: + """Split the string by a separator. Args: - delimiter: The string to split on. + sep: The string to split on. + maxsplit: The maximum amount of items to split from String. + Defaults to unlimited. Returns: - A List of Strings containing the input split by the delimiter. + A List of Strings containing the input split by the separator. - Raises: - Error if an empty delimiter is specified. + Examples: + + ```mojo + # Splitting a space + _ = String("hello world").split(" ") # ["hello", "world"] + # Splitting adjacent separators + _ = String("hello,,world").split(",") # ["hello", "", "world"] + # Splitting with maxsplit + _ = String("1,2,3").split(",", 1) # ['1', '2,3'] + ``` + . + """ + var output = List[String]() + + var str_iter_len = len(self) - 1 + var lhs = 0 + var rhs = 0 + var items = 0 + var sep_len = len(sep) + if sep_len == 0: + raise Error("ValueError: empty separator") + + while lhs <= str_iter_len: + rhs = self.find(sep, lhs) + if rhs == -1: + output.append(self[lhs:]) + break + + if maxsplit > -1: + if items == maxsplit: + output.append(self[lhs:]) + break + items += 1 + + output.append(self[lhs:rhs]) + lhs = rhs + sep_len + + if self.endswith(sep): + output.append("") + return output + + fn split(self, *, maxsplit: Int = -1) -> List[String]: + """Split the string by every Whitespace separator. + + Currently only uses C style separators. + + Args: + maxsplit: The maximum amount of items to split from String. Defaults + to unlimited. + + Returns: + A List of Strings containing the input split by the separator. + + Examples: + + ```mojo + # Splitting an empty string or filled with whitespaces + _ = String(" ").split() # [] + _ = String("").split() # [] + + # Splitting a string with leading, trailing, and middle whitespaces + _ = String(" hello world ").split() # ["hello", "world"] + ``` + . """ - if not delimiter: - raise Error("empty delimiter not allowed to be passed to split.") + # TODO: implement and document splitting adjacent universal newlines: + # _ = String( + # "hello \\t\\n\\r\\f\\v\\x1c\\x1e\\x85\\u2028\\u2029world" + # ).split() # ["hello", "world"] var output = List[String]() - var current_offset = 0 - while True: - var loc = self.find(delimiter, current_offset) - # The delimiter was not found, so add the search slice from where - # we're currently at. - if loc == -1: - output.append(self[current_offset:]) + var str_iter_len = len(self) - 1 + var lhs = 0 + var rhs = 0 + var items = 0 + # FIXME: this should iterate and build unicode strings + # and use self.isspace() + while lhs <= str_iter_len: + # Python adds all "whitespace chars" as one separator + # if no separator was specified + while lhs <= str_iter_len: + if not _isspace(self._buffer.unsafe_get(lhs)[]): + break + lhs += 1 + # if it went until the end of the String, then + # it should be sliced up until the original + # start of the whitespace which was already appended + if lhs - 1 == str_iter_len: + break + elif lhs == str_iter_len: + # if the last char is not whitespace + output.append(self[str_iter_len]) break + rhs = lhs + 1 + while rhs <= str_iter_len: + if _isspace(self._buffer.unsafe_get(rhs)[]): + break + rhs += 1 - # We found a delimiter, so add the preceding string slice. - output.append(self[current_offset:loc]) + if maxsplit > -1: + if items == maxsplit: + output.append(self[lhs:]) + break + items += 1 - # Advance our search offset past the delimiter. - current_offset = loc + len(delimiter) + output.append(self[lhs:rhs]) + lhs = rhs return output @@ -1553,7 +1640,7 @@ struct String( res.append(0) return String(res^) - fn strip(self, chars: String = String.WHITESPACE) -> String: + fn strip(self, chars: String) -> String: """Return a copy of the string with leading and trailing characters removed. @@ -1566,7 +1653,16 @@ struct String( return self.lstrip(chars).rstrip(chars) - fn rstrip(self, chars: String = String.WHITESPACE) -> String: + fn strip(self) -> String: + """Return a copy of the string with leading and trailing whitespaces + removed. + + Returns: + A copy of the string with no leading or trailing whitespaces. + """ + return self.lstrip().rstrip() + + fn rstrip(self, chars: String) -> String: """Return a copy of the string with trailing characters removed. Args: @@ -1582,7 +1678,19 @@ struct String( return self[:r_idx] - fn lstrip(self, chars: String = String.WHITESPACE) -> String: + fn rstrip(self) -> String: + """Return a copy of the string with trailing whitespaces removed. + + Returns: + A copy of the string with no trailing whitespaces. + """ + # TODO: should use self.__iter__ and self.isspace() + var r_idx = len(self) + while r_idx > 0 and _isspace(self._buffer.unsafe_get(r_idx - 1)[]): + r_idx -= 1 + return self[:r_idx] + + fn lstrip(self, chars: String) -> String: """Return a copy of the string with leading characters removed. Args: @@ -1598,6 +1706,18 @@ struct String( return self[l_idx:] + fn lstrip(self) -> String: + """Return a copy of the string with leading whitespaces removed. + + Returns: + A copy of the string with no leading whitespaces. + """ + # TODO: should use self.__iter__ and self.isspace() + var l_idx = 0 + while l_idx < len(self) and _isspace(self._buffer.unsafe_get(l_idx)[]): + l_idx += 1 + return self[l_idx:] + fn __hash__(self) -> Int: """Hash the underlying buffer using builtin hash. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 8dbb579c96..cefc9b67b8 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -602,11 +602,52 @@ fn test_rfind() raises: fn test_split() raises: - # Reject empty delimiters - with assert_raises( - contains="empty delimiter not allowed to be passed to split." - ): - _ = String("hello").split("") + # empty separators default to whitespace + var d = String("hello world").split() + assert_true(len(d) == 2) + assert_true(d[0] == "hello", d[1] == "world") + d = String("hello \t\n\n\v\fworld").split("\n") + assert_true(len(d) == 3) + assert_true(d[0] == "hello \t" and d[1] == "" and d[2] == "\v\fworld") + + # Should add all whitespace-like chars as one + alias utf8_spaces = String(" \t\n\r\v\f") + var s = utf8_spaces + "hello" + utf8_spaces + "world" + utf8_spaces + d = s.split() + assert_true(len(d) == 2) + assert_true(d[0] == "hello" and d[1] == "world") + + # should split into empty strings between separators + d = String("1,,,3").split(",") + assert_true(len(d) == 4) + assert_true(d[0] == "1" and d[1] == "" and d[2] == "" and d[3] == "3") + d = String(",,,").split(",") + assert_true(len(d) == 4) + assert_true(d[0] == "" and d[1] == "" and d[2] == "" and d[3] == "") + d = String(" a b ").split(" ") + assert_true(len(d) == 4) + assert_true(d[0] == "" and d[1] == "a" and d[2] == "b" and d[3] == "") + d = String("abababaaba").split("aba") + assert_true(len(d) == 4) + assert_true(d[0] == "" and d[1] == "b" and d[2] == "" and d[3] == "") + + # should split into maxsplit + 1 items + d = String("1,2,3").split(",", 0) + assert_true(len(d) == 1) + assert_true(d[0] == "1,2,3") + d = String("1,2,3").split(",", 1) + assert_true(len(d) == 2) + assert_true(d[0] == "1" and d[1] == "2,3") + + assert_true(len(String("").split()) == 0) + assert_true(len(String(" ").split()) == 0) + assert_true(len(String("").split(" ")) == 1) + assert_true(len(String(" ").split(" ")) == 2) + assert_true(len(String(" ").split(" ")) == 3) + assert_true(len(String(" ").split(" ")) == 4) + + with assert_raises(): + _ = String("").split("") # Split in middle var d1 = String("n") @@ -759,10 +800,6 @@ fn test_isspace() raises: fn test_ascii_aliases() raises: - var whitespaces = String(" \n\t\r\f\v") - for i in range(len(whitespaces)): - assert_true(whitespaces[i] in String.WHITESPACE) - assert_true(String("a") in String.ASCII_LOWERCASE) assert_true(String("b") in String.ASCII_LOWERCASE) assert_true(String("y") in String.ASCII_LOWERCASE) From 22ea67cd7a5393f44af9de2630febb9951e6e965 Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 28 May 2024 15:27:43 -0500 Subject: [PATCH 0732/2019] [External] [Docs] Fix minor typos (#40739) [External] [Docs] Fix minor typos Co-authored-by: Arun Closes modularml/mojo#2694 MODULAR_ORIG_COMMIT_REV_ID: 9f2ce4bf18779e550a9820cc89d86caec3ff47b8 --- docs/roadmap.md | 4 ++-- proposals/byte-as-uint8.md | 2 +- proposals/mojo-and-dynamism.md | 4 ++-- stdlib/src/builtin/object.mojo | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 51e982c8ae..f19c0d9abf 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -608,9 +608,9 @@ zero. In other words, most standard library types are considered "unsafe". ```mojo var l = List[Int](capacity=0) -print(l[1]) # could crash or print garbage values (undefined behaviour) +print(l[1]) # could crash or print garbage values (undefined behavior) -print(1//0) # does not raise and could print anything (undefined behaviour) +print(1//0) # does not raise and could print anything (undefined behavior) ``` This is clearly unacceptable given the strong memory safety goals of Mojo. We diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md index fea7496d72..7ff764a60e 100644 --- a/proposals/byte-as-uint8.md +++ b/proposals/byte-as-uint8.md @@ -1,4 +1,4 @@ -# Standardise the representation of byte sequence as a sequence of unsigned 8 bit integers +# Standardize the representation of byte sequence as a sequence of unsigned 8 bit integers At this point in time, a sequence of bytes is often represented as a sequence of signed 8 bit integers in Mojo standard library. Most noticeable example is the diff --git a/proposals/mojo-and-dynamism.md b/proposals/mojo-and-dynamism.md index 845cc993b6..f1c48bc5e1 100644 --- a/proposals/mojo-and-dynamism.md +++ b/proposals/mojo-and-dynamism.md @@ -125,7 +125,7 @@ compatibility with Python. It allows, for example, omitting type annotations, implicit variable declarations, implicit raises, etc. But the Mojo `def` is not the same as a Python `def`. A commonly reported issue is that Mojo scoping rules differ from Python's. In Python, local variables are scoped at the function -level, but Python also supports behaviour like: +level, but Python also supports behavior like: ```python def foo(k): @@ -229,7 +229,7 @@ from Mojo itself, it comes from Mojo's first class interoperability with CPython. This in effect will be Mojo's escape hatch for compatibility purposes and is what gives Mojo access to all of Python's vast ecosystem. Below that, Mojo will provide an emulation of Python's hash-table dynamism that is a -faithful but not quite identical replication of Python behaviour (no GIL, for +faithful but not quite identical replication of Python behavior (no GIL, for example!). Building this out will be a huge undertaking, and is something Mojo should do over time. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 0bb3d6a0a6..303c8076f2 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -66,9 +66,9 @@ struct _ImmutableString: struct _RefCountedList: - """Python objects have the behaviour that bool, int, float, and str are + """Python objects have the behavior that bool, int, float, and str are passed by value but lists and dictionaries are passed by reference. In order - to model this behaviour, lists and dictionaries are implemented as + to model this behavior, lists and dictionaries are implemented as ref-counted data types. """ @@ -509,7 +509,7 @@ struct _ObjectImpl(CollectionElement, Stringable): fn coerce_arithmetic_type(inout lhs: _ObjectImpl, inout rhs: _ObjectImpl): """Coerces two values of arithmetic type to the appropriate lowest-common denominator type for performing arithmetic operations. - Bools are always converted to integers, to match Python's behaviour. + Bools are always converted to integers, to match Python's behavior. """ if lhs.is_bool(): lhs = lhs.convert_bool_to_int() From 2b357bb1143a32b81c2f6b54fe6a10ee68202621 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 28 May 2024 16:41:20 -0400 Subject: [PATCH 0733/2019] [stdlib] Remove `math.align_down_residual` Doesn't pull its weight, untested, only works for `SIMD`. MODULAR_ORIG_COMMIT_REV_ID: 2a81f3b0c9392ecf80a9b2c2d952b727fbada00c --- docs/changelog.md | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 47f1734382..9683178883 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -537,13 +537,10 @@ what we publish. - The method `object.print()` has been removed. Since now, `object` has the `Stringable` trait, you can use `print(my_object)` instead. -- The `math.clamp` function has been removed in favor of a new `SIMD.clamp` - method. - -- The `math.round_half_down` and `math.round_half_up` functions are removed. - These can be trivially implemented using the `ceil` and `floor` functions. - - The following functions have been removed from the math module: + - `clamp`; use the new `SIMD.clamp` method instead. + - `round_half_down` and `round_half_up`; these can be trivially implemented + using the `ceil` and `floor` functions. - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, @@ -553,22 +550,19 @@ what we publish. - `select`; in favor of using `SIMD.select` directly. - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` with `1`. - -- The `math.roundeven` function has been removed from the `math` module. The new - `SIMD.roundeven` method now provides the identical functionality. - -- The `math.div_ceil` function has been removed in favor of the `math.ceildiv` - function. + - `roundeven`; the new `SIMD.roundeven` method now provides the identical + functionality. + - `div_ceil`; use the new `ceildiv` function. + - `rotate_left` and `rotate_right`; the same functionality is available in the + builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the + `bit.rotate_bits_{left,right}` methods for `Int`. + - an overload of `math.pow` taking an integer parameter exponent. + - `align_down_residual`; it can be trivially implemented using `align_down`. - The `math.bit.select` and `math.bit.bit_and` functions have been removed. The same functionality is available in the builtin `SIMD.select` and `SIMD.__and__` methods, respectively. -- The `math.rotate_left` and `math.rotate_right` functions have been removed. - The same functionality is available in the builtin `SIMD.rotate_{left,right}` - methods for `SIMD` types, and the `bit.rotate_bits_{left,right}` methods for - `Int`. - - The `math.limit` module has been removed. The same functionality is available as follows: - `math.limit.inf`: use `utils.numerics.max_or_inf` @@ -582,9 +576,6 @@ what we publish. - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. -- The overload of `math.pow` taking an integer parameter exponent has been - removed. - ### 🛠️ Fixed - [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential From 2a961bbacf624cfed4f6ac1bdd60f140c083f6d4 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 28 May 2024 15:00:46 -0700 Subject: [PATCH 0734/2019] [mojo-stdlib] Fix some binding of non-trivial types to AnyRegType (NFC) Most of this involved changing `Pointer` to `UnsafePointer`, or adding `UnsafePointer` overloads of certain methods. MODULAR_ORIG_COMMIT_REV_ID: 98572ead41fa60cd04670ce709f376e6f29fe3f2 --- stdlib/src/memory/memory.mojo | 49 ++++++++++++++++++++++------- stdlib/src/sys/ffi.mojo | 4 +-- stdlib/src/sys/intrinsics.mojo | 2 +- stdlib/test/memory/test_memory.mojo | 3 +- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 0ef2a8680b..67546b807b 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -234,19 +234,16 @@ fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): @always_inline -fn memcpy(dest: LegacyPointer, src: __type_of(dest), count: Int): +fn memcpy( + dest_data: LegacyPointer[Int8, *_], src_data: __type_of(dest_data), n: Int +): """Copies a memory area. Args: - dest: The destination pointer. - src: The source pointer. - count: The number of elements to copy. + dest_data: The destination pointer. + src_data: The source pointer. + n: The number of bytes to copy. """ - var n = count * sizeof[dest.type]() - - var dest_data = dest.bitcast[Int8]() - var src_data = src.bitcast[Int8]() - if n < 5: if n == 0: return @@ -285,8 +282,12 @@ fn memcpy(dest: LegacyPointer, src: __type_of(dest), count: Int): # ) # return - var dest_dtype_ptr = DTypePointer[DType.int8, dest.address_space](dest_data) - var src_dtype_ptr = DTypePointer[DType.int8, src.address_space](src_data) + var dest_dtype_ptr = DTypePointer[DType.int8, dest_data.address_space]( + dest_data + ) + var src_dtype_ptr = DTypePointer[DType.int8, src_data.address_space]( + src_data + ) # Copy in 32-byte chunks. alias chunk_size = 32 @@ -297,6 +298,32 @@ fn memcpy(dest: LegacyPointer, src: __type_of(dest), count: Int): dest_dtype_ptr.store(i, src_dtype_ptr.load[width=1](i)) +@always_inline +fn memcpy(dest: LegacyPointer, src: __type_of(dest), count: Int): + """Copies a memory area. + + Args: + dest: The destination pointer. + src: The source pointer. + count: The number of elements to copy. + """ + var n = count * sizeof[dest.type]() + memcpy(dest.bitcast[Int8](), src.bitcast[Int8](), n) + + +@always_inline +fn memcpy(dest: UnsafePointer, src: __type_of(dest), count: Int): + """Copies a memory area. + + Args: + dest: The destination pointer. + src: The source pointer. + count: The number of elements to copy. + """ + var n = count * sizeof[dest.type]() + memcpy(dest.bitcast[Int8]().address, src.bitcast[Int8]().address, n) + + @always_inline fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): """Copies a memory area. diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index cad2b6a76e..6846812c38 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -40,11 +40,11 @@ alias DEFAULT_RTLD = RTLD.NOW | RTLD.GLOBAL @value -@register_passable +@register_passable("trivial") struct DLHandle(CollectionElement, Boolable): """Represents a dynamically linked library that can be loaded and unloaded. - The library is loaded on initialization and unloaded on deletion of the object. + The library is loaded on initialization and unloaded by `close`. """ var handle: DTypePointer[DType.int8] diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 327e87a93a..b67c7ee4ad 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1533,7 +1533,7 @@ fn _type_is_eq[t1: AnyType, t2: AnyType]() -> Bool: # ===----------------------------------------------------------------------=== # -@register_passable +@register_passable("trivial") struct _RegisterPackType[*a: AnyRegType]: var storage: __mlir_type[`!kgen.pack<`, a, `>`] diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 8944f15e13..7d4e44c7de 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -30,6 +30,7 @@ alias int8_pop = __mlir_type.`!pop.scalar` @value +@register_passable("trivial") struct Pair: var lo: Int var hi: Int @@ -333,7 +334,7 @@ def test_pointer_refitem(): def test_pointer_refitem_string(): alias payload = "$Modular!Mojo!HelloWorld^" - var ptr = Pointer[String].alloc(1) + var ptr = UnsafePointer[String].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = String() ptr[] = payload assert_equal(ptr[], payload) From c52a2bd28a7f0a465aea1ca8bbc3cc856ba75bdc Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 28 May 2024 17:39:36 -0700 Subject: [PATCH 0735/2019] [mojo] Rename `AnyRegType` -> `AnyTrivialRegType` (NFC) This PR renames `AnyRegTYpe` to `AnyTrivialRegType` and forbids binding non-trivial register-passable types to `AnyTrivialRegType`. This closes a major safety hole: binding a non-trivial type to `AnyRegType` was not safe: copy constructors and destructors don't get called properly. MODULAR_ORIG_COMMIT_REV_ID: a69207d541e45e8a5a4bb083c825ce9adea1ac5f --- docs/changelog-released.md | 6 +- docs/changelog.md | 7 +- docs/manual/parameters/index.ipynb | 12 +- docs/manual/types.ipynb | 8 +- examples/notebooks/programming-manual.ipynb | 14 +- stdlib/docs/bencher/Bench.md | 6 +- stdlib/docs/faq.md | 6 +- stdlib/docs/roadmap.md | 2 +- stdlib/src/builtin/_closure.mojo | 4 +- stdlib/src/builtin/_stubs.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 4 +- stdlib/src/builtin/builtin_slice.mojo | 6 +- stdlib/src/builtin/coroutine.mojo | 8 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/rebind.mojo | 4 +- stdlib/src/builtin/sort.mojo | 20 +- stdlib/src/builtin/type_aliases.mojo | 2 +- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/collections/vector.mojo | 7 +- stdlib/src/memory/memory.mojo | 14 +- stdlib/src/memory/unsafe.mojo | 6 +- stdlib/src/sys/_assembly.mojo | 132 ++++----- stdlib/src/sys/ffi.mojo | 290 ++++++++++---------- stdlib/src/sys/info.mojo | 6 +- stdlib/src/sys/intrinsics.mojo | 136 ++++----- stdlib/src/utils/static_tuple.mojo | 6 +- stdlib/test/builtin/test_sort.mojo | 28 +- 27 files changed, 380 insertions(+), 360 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index faa318fa04..51563ca4bd 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -547,10 +547,10 @@ modular update mojo #### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of - `AnyRegType`, please upgrade your code to `AnyType` in examples like this: + `AnyTrivialRegType`, please upgrade your code to `AnyType` in examples like this: ```mojo - fn your_function[*Types: AnyRegType](*args: *Ts): ... + fn your_function[*Types: AnyTrivialRegType](*args: *Ts): ... ``` This move gives you access to a nicer API and has the benefit of being memory @@ -2361,7 +2361,7 @@ the previous "read to EOF" behavior when size is negative. `Bool` for whether the assertion succeeded or not. - Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no - longer (implicitly) assumed to be register-passable. A new `AnyRegType` type + longer (implicitly) assumed to be register-passable. A new `AnyTrivialRegType` type is used to represent generic types that are register passable. - Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) diff --git a/docs/changelog.md b/docs/changelog.md index 9683178883..85a1d7fdd6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -211,7 +211,7 @@ what we publish. - Add an `InlinedArray` type that works on memory-only types. Compare with the existing `StaticTuple` type, which is conceptually an array - type, but only worked on `AnyRegType`. + type, but only worked on `AnyTrivialRegType`. ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) - Base64 decoding support has been added. @@ -437,6 +437,11 @@ what we publish. ### 🦋 Changed +- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids + binding non-trivial register-passable types to `AnyTrivialRegType`. This + closes a major safety hole in the language. Please use `AnyType` for generic + code going forward. + - The `let` keyword has been completely removed from the language. We previously removed `let` declarations but still provided an error message to users. Now, it is completely gone from the grammar. Long live `var`! diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 816a0bb4d1..16fced1a28 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -129,7 +129,7 @@ "metadata": {}, "outputs": [], "source": [ - "struct GenericArray[T: AnyRegType]:\n", + "struct GenericArray[T: AnyTrivialRegType]:\n", " var data: Pointer[T]\n", " var size: Int\n", "\n", @@ -155,7 +155,7 @@ "source": [ "This struct has a single parameter, `T`, which is a placeholder for the data\n", "type you want to store in the array, sometimes called a _type parameter_. `T` is\n", - "typed as [`AnyRegType`](/mojo/stdlib/builtin/type_aliases.html), which is a \n", + "typed as [`AnyTrivialRegType`](/mojo/stdlib/builtin/type_aliases.html), which is a \n", "_metatype_ representing any \n", "[register-passable type](/mojo/manual/decorators/register-passable.html). This\n", "means our `GenericArray` can hold fixed-size data types like integers and \n", @@ -182,7 +182,7 @@ "constructor, and the return type of the `__getitem__()` method.\n", "\n", "At the moment, this code only works with register-passable types, which is why\n", - "the type parameter `T` is limited to `AnyRegType`. There's also an\n", + "the type parameter `T` is limited to `AnyTrivialRegType`. There's also an\n", "[`AnyType`](/mojo/stdlib/builtin/type_aliases.html) metatype, which includes \n", "**all** Mojo types.\n", "\n", @@ -218,7 +218,7 @@ "signature:\n", "\n", "```mojo\n", - "struct GenericArray[T: AnyRegType]:\n", + "struct GenericArray[T: AnyTrivialRegType]:\n", " ...\n", "\n", " @staticmethod\n", @@ -807,7 +807,7 @@ "and functions to be defined. \n", "\n", "For example, we can create a simplified `Array` that supports arbitrary types of\n", - "elements (via the `AnyRegType` parameter):" + "elements (via the `AnyTrivialRegType` parameter):" ] }, { @@ -824,7 +824,7 @@ } ], "source": [ - "struct Array[T: AnyRegType]:\n", + "struct Array[T: AnyTrivialRegType]:\n", " var data: Pointer[T]\n", " var size: Int\n", "\n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 1a454a11b4..1aac8f6ccb 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -961,21 +961,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## `AnyType` and `AnyRegType`\n", + "## `AnyType` and `AnyTrivialRegType`\n", "\n", "Two other things you'll see in Mojo APIs are references to `AnyType` and\n", - "`AnyRegType`. These are effectively _metatypes_, that is, types of types.\n", + "`AnyTrivialRegType`. These are effectively _metatypes_, that is, types of types.\n", "\n", "- `AnyType` represents any Mojo type. Mojo treats `AnyType` as a special kind of\n", " trait, and you'll find more discussion of it on the\n", " [Traits page](/mojo/manual/traits#the-anytype-trait).\n", - "- `AnyRegType` is a metatype representing any Mojo type that's marked \n", + "- `AnyTrivialRegType` is a metatype representing any Mojo type that's marked \n", " register passable.\n", "\n", "You'll see them in signatures like this:\n", "\n", "```mojo\n", - "fn any_type_function[ValueType: AnyRegType](value: ValueType):\n", + "fn any_type_function[ValueType: AnyTrivialRegType](value: ValueType):\n", " ...\n", "```\n", "\n", diff --git a/examples/notebooks/programming-manual.ipynb b/examples/notebooks/programming-manual.ipynb index cd076d51e5..faa2db8a61 100644 --- a/examples/notebooks/programming-manual.ipynb +++ b/examples/notebooks/programming-manual.ipynb @@ -2096,7 +2096,7 @@ "and functions to be defined. \n", "\n", "For example, we can create a simplified `Array` that supports arbitrary types of\n", - "the elements (via the `AnyRegType` parameter):" + "the elements (via the `AnyTrivialRegType` parameter):" ] }, { @@ -2113,7 +2113,7 @@ } ], "source": [ - "struct Array[T: AnyRegType]:\n", + "struct Array[T: AnyTrivialRegType]:\n", " var data: Pointer[T]\n", " var size: Int\n", "\n", @@ -2170,7 +2170,7 @@ "types such as for a tuple:\n", "\n", "```mojo\n", - "struct Tuple[*Ts: AnyRegType]:\n", + "struct Tuple[*Ts: AnyTrivialRegType]:\n", " var _storage : *Ts\n", "```\n", "\n", @@ -2179,8 +2179,8 @@ "better way to handle this):\n", "\n", "```mojo\n", - "struct Array[T: AnyRegType]:\n", - " fn __getitem__[IndexType: AnyRegType](self, idx: IndexType)\n", + "struct Array[T: AnyTrivialRegType]:\n", + " fn __getitem__[IndexType: AnyTrivialRegType](self, idx: IndexType)\n", " -> (ArraySlice[T] if issubclass(IndexType, Range) else T):\n", " ...\n", "```" @@ -2241,7 +2241,7 @@ "Like `var` and `let`, aliases obey scope, and you can use local aliases within\n", "functions as you'd expect.\n", "\n", - "By the way, both `None` and `AnyRegType` are defined as [type\n", + "By the way, both `None` and `AnyTrivialRegType` are defined as [type\n", "aliases](https://docs.modular.com/mojo/MojoBuiltin/TypeAliases.html)." ] }, @@ -3244,7 +3244,7 @@ "metadata": {}, "outputs": [], "source": [ - "struct Array[Type: AnyRegType]:\n", + "struct Array[Type: AnyTrivialRegType]:\n", " var data: Pointer[Type]\n", " var size: Int\n", "\n", diff --git a/stdlib/docs/bencher/Bench.md b/stdlib/docs/bencher/Bench.md index 363b10e414..1e71a29c53 100644 --- a/stdlib/docs/bencher/Bench.md +++ b/stdlib/docs/bencher/Bench.md @@ -83,16 +83,16 @@ Benchmarks an input function with input args of type AnyType.
```mojo -bench_with_input[T: AnyRegType, bench_fn: fn(inout Bencher, $0) capturing -> None](inout self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) +bench_with_input[T: AnyTrivialRegType, bench_fn: fn(inout Bencher, $0) capturing -> None](inout self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) ```
-Benchmarks an input function with input args of type AnyRegType. +Benchmarks an input function with input args of type AnyTrivialRegType. **Parameters:** -- ​T (`AnyRegType`): Benchmark function input type. +- ​T (`AnyTrivialRegType`): Benchmark function input type. - ​bench_fn (`fn(inout Bencher, $0) capturing -> None`): The function to be benchmarked. diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index be9780fa05..02009c62db 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -22,13 +22,13 @@ your issues resolved. ## Standard library code -### 1. Why do we have both `AnyRegType` and `AnyType`? +### 1. Why do we have both `AnyTrivialRegType` and `AnyType`? -This is largely a historical thing as the library only worked on `AnyRegType` +This is largely a historical thing as the library only worked on `AnyTrivialRegType` when it was first written. As we introduced the notion of memory-only types and traits, `AnyType` was born. Over time, we expect to rewrite nearly the entire library to have everything work on `AnyType` and be generalized to -not just work on `AnyRegType`. Several things need to happen in tandem with +not just work on `AnyTrivialRegType`. Several things need to happen in tandem with the compiler team to make this possible. ### 2. Are the MLIR dialects private? diff --git a/stdlib/docs/roadmap.md b/stdlib/docs/roadmap.md index 7e05f86f0d..d94535089f 100644 --- a/stdlib/docs/roadmap.md +++ b/stdlib/docs/roadmap.md @@ -20,7 +20,7 @@ mission](https://docs.modular.com/mojo/why-mojo). ### Core library improvements -- Remove `AnyRegType` in the standard library in favor of `AnyType`. +- Remove `AnyTrivialRegType` in the standard library in favor of `AnyType`. - Unify `Pointer` and `AnyPointer`. diff --git a/stdlib/src/builtin/_closure.mojo b/stdlib/src/builtin/_closure.mojo index cb2d35c45b..26898877cc 100644 --- a/stdlib/src/builtin/_closure.mojo +++ b/stdlib/src/builtin/_closure.mojo @@ -13,7 +13,9 @@ @register_passable -struct __ParameterClosureCaptureList[fn_type: AnyRegType, fn_ref: fn_type]: +struct __ParameterClosureCaptureList[ + fn_type: AnyTrivialRegType, fn_ref: fn_type +]: var value: __mlir_type.`!kgen.pointer` # Parameter closure invariant requires this function be marked 'capturing'. diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index 089ef9b67a..2006128733 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -19,7 +19,7 @@ from builtin.range import _StridedRangeIterator @register_passable("trivial") -struct __MLIRType[T: AnyRegType](Movable, Copyable): +struct __MLIRType[T: AnyTrivialRegType](Movable, Copyable): var value: T diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 1847c30826..3111547ea6 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -84,7 +84,7 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): @value -struct _VariadicListIter[type: AnyRegType]: +struct _VariadicListIter[type: AnyTrivialRegType]: """Const Iterator for VariadicList. Parameters: @@ -103,7 +103,7 @@ struct _VariadicListIter[type: AnyRegType]: @register_passable("trivial") -struct VariadicList[type: AnyRegType](Sized): +struct VariadicList[type: AnyTrivialRegType](Sized): """A utility class to access variadic function arguments. Provides a "list" view of the function argument so that the size of the argument list and each individual argument can be accessed. diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index b872933dbe..1d2136582a 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -26,7 +26,7 @@ fn _int_max_value() -> Int: @always_inline("nodebug") -fn _default_or[T: AnyRegType](value: T, default: Int) -> Int: +fn _default_or[T: AnyTrivialRegType](value: T, default: Int) -> Int: # TODO: Handle `__index__` for other types when we have traits! @parameter if _mlirtype_is_eq[T, Int](): @@ -76,7 +76,7 @@ struct Slice(Stringable, EqualityComparable): @always_inline("nodebug") fn __init__[ - T0: AnyRegType, T1: AnyRegType, T2: AnyRegType + T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType ](inout self, start: T0, end: T1, step: T2): """Construct slice given the start, end and step values. @@ -197,7 +197,7 @@ fn slice(start: Int, end: Int) -> Slice: # TODO(30496): Modernize the slice type @always_inline("nodebug") fn slice[ - T0: AnyRegType, T1: AnyRegType, T2: AnyRegType + T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType ](start: T0, end: T1, step: T2) -> Slice: """Construct a Slice given the start, end and step values. diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 3163cc0fc7..5262b3ae7c 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -81,7 +81,7 @@ fn _coro_resume_noop_callback(handle: AnyCoroutine, null: AnyCoroutine): @register_passable -struct Coroutine[type: AnyRegType]: +struct Coroutine[type: AnyTrivialRegType]: """Represents a coroutine. Coroutines can pause execution saving the state of the program (including @@ -96,7 +96,7 @@ struct Coroutine[type: AnyRegType]: var _handle: AnyCoroutine @always_inline - fn _get_ctx[ctx_type: AnyRegType](self) -> UnsafePointer[ctx_type]: + fn _get_ctx[ctx_type: AnyTrivialRegType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. Parameters: @@ -178,7 +178,7 @@ struct Coroutine[type: AnyRegType]: @register_passable -struct RaisingCoroutine[type: AnyRegType]: +struct RaisingCoroutine[type: AnyTrivialRegType]: """Represents a coroutine that can raise. Coroutines can pause execution saving the state of the program (including @@ -208,7 +208,7 @@ struct RaisingCoroutine[type: AnyRegType]: return __mlir_op.`kgen.variant.take`[index = Int(1).value](variant) @always_inline - fn _get_ctx[ctx_type: AnyRegType](self) -> UnsafePointer[ctx_type]: + fn _get_ctx[ctx_type: AnyTrivialRegType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. Parameters: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 303c8076f2..6420ad9d1c 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -198,7 +198,7 @@ struct _Function: """The function pointer.""" @always_inline - fn __init__[FnT: AnyRegType](inout self, value: FnT): + fn __init__[FnT: AnyTrivialRegType](inout self, value: FnT): # FIXME: No "pointer bitcast" for signature function pointers. var f = UnsafePointer[Int16]() UnsafePointer.address_of(f).bitcast[FnT]()[] = value diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index 20e9e6fac9..9a06bec09c 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -18,8 +18,8 @@ These are Mojo built-ins, so you don't need to import them. @always_inline("nodebug") fn rebind[ - dest_type: AnyRegType, - src_type: AnyRegType, + dest_type: AnyTrivialRegType, + src_type: AnyTrivialRegType, ](val: src_type) -> dest_type: """Statically assert that a parameter input type `src_type` resolves to the same type as a parameter result type `dest_type` after function diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index ce949df362..c6f85ceffd 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -24,11 +24,11 @@ from sys import bitwidthof # sort # ===----------------------------------------------------------------------===# -alias _cmp_fn_type = fn[type: AnyRegType] (type, type) capturing -> Bool +alias _cmp_fn_type = fn[type: AnyTrivialRegType] (type, type) capturing -> Bool fn _insertion_sort[ - type: AnyRegType, cmp_fn: _cmp_fn_type + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], start: Int, end: Int): """Sort the array[start:end] slice""" @@ -67,7 +67,7 @@ fn _insertion_sort[ @always_inline fn _partition[ - type: AnyRegType, cmp_fn: _cmp_fn_type + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], start: Int, end: Int) -> Int: if start == end: return end @@ -132,7 +132,7 @@ fn _estimate_initial_height(size: Int) -> Int: fn _quicksort[ - type: AnyRegType, cmp_fn: _cmp_fn_type + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], size: Int): if size == 0: return @@ -211,7 +211,7 @@ fn _quicksort[ # partition # ===----------------------------------------------------------------------===# fn partition[ - type: AnyRegType, cmp_fn: _cmp_fn_type + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](buff: Pointer[type], k: Int, size: Int): """Partition the input vector inplace such that first k elements are the largest (or smallest if cmp_fn is <= operator) elements. @@ -258,7 +258,7 @@ fn sort(inout buff: Pointer[Int], len: Int): """ @parameter - fn _less_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _less_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) <= rebind[Int](rhs) _quicksort[Int, _less_than_equal](buff, len) @@ -277,7 +277,7 @@ fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """ @parameter - fn _less_than_equal[ty: AnyRegType](lhs: ty, rhs: ty) -> Bool: + fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) _quicksort[Scalar[type], _less_than_equal](buff, len) @@ -335,7 +335,7 @@ fn sort[ @always_inline fn _sort2[ - type: AnyRegType, cmp_fn: _cmp_fn_type + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], offset0: Int, offset1: Int): var a = array[offset0] var b = array[offset1] @@ -346,7 +346,7 @@ fn _sort2[ @always_inline fn _sort_partial_3[ - type: AnyRegType, cmp_fn: _cmp_fn_type + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], offset0: Int, offset1: Int, offset2: Int): var a = array[offset0] var b = array[offset1] @@ -364,7 +364,7 @@ fn _sort_partial_3[ @always_inline fn _small_sort[ - n: Int, type: AnyRegType, cmp_fn: _cmp_fn_type + n: Int, type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type]): @parameter if n == 2: diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index cb0fb23d2e..effc721d13 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -alias AnyRegType = __mlir_type.`!kgen.type` +alias AnyTrivialRegType = __mlir_type.`!kgen.type` """Represents any register passable Mojo data type.""" alias NoneType = __mlir_type.`!kgen.none` diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 266d86cc19..921d7f8c5b 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -261,7 +261,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): @register_passable("trivial") -struct OptionalReg[T: AnyRegType](Boolable): +struct OptionalReg[T: AnyTrivialRegType](Boolable): """A register-passable optional type. This struct optionally contains a value. It only works with trivial register diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 50215b09c6..827d61c0d8 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -29,7 +29,7 @@ from utils import InlineArray @value struct _VecIter[ - type: AnyRegType, + type: AnyTrivialRegType, vec_type: AnyType, deref: fn (UnsafePointer[vec_type], Int) -> type, ](Sized): @@ -53,7 +53,7 @@ struct _VecIter[ @always_inline -fn _calculate_fixed_vector_default_size[type: AnyRegType]() -> Int: +fn _calculate_fixed_vector_default_size[type: AnyTrivialRegType]() -> Int: alias prefered_bytecount = 64 alias sizeof_type = sizeof[type]() @@ -69,7 +69,8 @@ fn _calculate_fixed_vector_default_size[type: AnyRegType]() -> Int: struct InlinedFixedVector[ - type: AnyRegType, size: Int = _calculate_fixed_vector_default_size[type]() + type: AnyTrivialRegType, + size: Int = _calculate_fixed_vector_default_size[type](), ](Sized): """A dynamically-allocated vector with small-vector optimization and a fixed maximum capacity. diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 67546b807b..0ce2fbdce2 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -124,7 +124,7 @@ fn memcmp(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: @always_inline fn memcmp[ - type: AnyRegType, address_space: AddressSpace + type: AnyTrivialRegType, address_space: AddressSpace ]( s1: LegacyPointer[type, address_space], s2: LegacyPointer[type, address_space], @@ -391,7 +391,7 @@ fn memset[ @always_inline fn memset[ - type: AnyRegType, address_space: AddressSpace + type: AnyTrivialRegType, address_space: AddressSpace ](ptr: UnsafePointer[type, address_space], value: UInt8, count: Int): """Fills memory with the given value. @@ -409,7 +409,7 @@ fn memset[ @always_inline fn memset[ - type: AnyRegType, address_space: AddressSpace + type: AnyTrivialRegType, address_space: AddressSpace ](ptr: LegacyPointer[type, address_space], value: UInt8, count: Int): """Fills memory with the given value. @@ -449,7 +449,7 @@ fn memset_zero[ @always_inline fn memset_zero[ - type: AnyRegType, address_space: AddressSpace + type: AnyTrivialRegType, address_space: AddressSpace ](ptr: UnsafePointer[type, address_space], count: Int): """Fills memory with zeros. @@ -466,7 +466,7 @@ fn memset_zero[ @always_inline fn memset_zero[ - type: AnyRegType, address_space: AddressSpace + type: AnyTrivialRegType, address_space: AddressSpace ](ptr: LegacyPointer[type, address_space], count: Int): """Fills memory with zeros. @@ -515,7 +515,7 @@ fn stack_allocation[ @always_inline fn stack_allocation[ count: Int, - type: AnyRegType, + type: AnyTrivialRegType, /, alignment: Int = 1, address_space: AddressSpace = AddressSpace.GENERIC, @@ -557,7 +557,7 @@ fn stack_allocation[ @always_inline fn _malloc[ - type: AnyRegType, + type: AnyTrivialRegType, /, *, address_space: AddressSpace = AddressSpace.GENERIC, diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 21d89edd1d..bc0348e777 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -162,7 +162,7 @@ alias Pointer = LegacyPointer @value @register_passable("trivial") struct LegacyPointer[ - type: AnyRegType, address_space: AddressSpace = AddressSpace.GENERIC + type: AnyTrivialRegType, address_space: AddressSpace = AddressSpace.GENERIC ](Boolable, CollectionElement, Intable, Stringable, EqualityComparable): """Defines a LegacyPointer struct that contains the address of a register passable type. @@ -272,7 +272,7 @@ struct LegacyPointer[ Returns: A LegacyPointer struct which contains the address of the argument. """ - # Work around AnyRegType vs AnyType. + # Work around AnyTrivialRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( UnsafePointer(arg).address ) @@ -453,7 +453,7 @@ struct LegacyPointer[ @always_inline("nodebug") fn bitcast[ - new_type: AnyRegType = type, + new_type: AnyTrivialRegType = type, /, address_space: AddressSpace = Self.address_space, ](self) -> LegacyPointer[new_type, address_space]: diff --git a/stdlib/src/sys/_assembly.mojo b/stdlib/src/sys/_assembly.mojo index 1d5d934c09..aaa9b3808e 100644 --- a/stdlib/src/sys/_assembly.mojo +++ b/stdlib/src/sys/_assembly.mojo @@ -22,7 +22,7 @@ from sys.intrinsics import _mlirtype_is_eq @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r", @@ -74,8 +74,8 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r", @@ -127,9 +127,9 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r", @@ -181,10 +181,10 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r", @@ -236,11 +236,11 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r", @@ -294,12 +294,12 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, - arg4_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r", @@ -357,13 +357,13 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, - arg4_type: AnyRegType, - arg5_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, + arg5_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r", @@ -422,14 +422,14 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, - arg4_type: AnyRegType, - arg5_type: AnyRegType, - arg6_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, + arg5_type: AnyTrivialRegType, + arg6_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r", @@ -489,15 +489,15 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, - arg4_type: AnyRegType, - arg5_type: AnyRegType, - arg6_type: AnyRegType, - arg7_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, + arg5_type: AnyTrivialRegType, + arg6_type: AnyTrivialRegType, + arg7_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r,r", @@ -558,16 +558,16 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, - arg4_type: AnyRegType, - arg5_type: AnyRegType, - arg6_type: AnyRegType, - arg7_type: AnyRegType, - arg8_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, + arg5_type: AnyTrivialRegType, + arg6_type: AnyTrivialRegType, + arg7_type: AnyTrivialRegType, + arg8_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r,r,r", @@ -629,17 +629,17 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ asm: StringLiteral, - result_type: AnyRegType, - arg0_type: AnyRegType, - arg1_type: AnyRegType, - arg2_type: AnyRegType, - arg3_type: AnyRegType, - arg4_type: AnyRegType, - arg5_type: AnyRegType, - arg6_type: AnyRegType, - arg7_type: AnyRegType, - arg8_type: AnyRegType, - arg9_type: AnyRegType, + result_type: AnyTrivialRegType, + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, + arg5_type: AnyTrivialRegType, + arg6_type: AnyTrivialRegType, + arg7_type: AnyTrivialRegType, + arg8_type: AnyTrivialRegType, + arg9_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r,r,r,r", diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 6846812c38..35e19de177 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -90,7 +90,9 @@ struct DLHandle(CollectionElement, Boolable): # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline - fn get_function[result_type: AnyRegType](self, name: String) -> result_type: + fn get_function[ + result_type: AnyTrivialRegType + ](self, name: String) -> result_type: """Returns a handle to the function with the given name in the dynamic library. @@ -108,7 +110,7 @@ struct DLHandle(CollectionElement, Boolable): @always_inline fn _get_function[ - result_type: AnyRegType + result_type: AnyTrivialRegType ](self, name: DTypePointer[DType.int8]) -> result_type: """Returns a handle to the function with the given name in the dynamic library. @@ -134,7 +136,7 @@ struct DLHandle(CollectionElement, Boolable): @always_inline fn _get_function[ - func_name: StringLiteral, result_type: AnyRegType + func_name: StringLiteral, result_type: AnyTrivialRegType ](self) -> result_type: """Returns a handle to the function with the given name in the dynamic library. @@ -206,7 +208,7 @@ fn _get_dylib_function[ func_name: StringLiteral, init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], destroy_fn: fn (UnsafePointer[NoneType]) -> None, - result_type: AnyRegType, + result_type: AnyTrivialRegType, ](payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]()) -> result_type: alias func_cache_name = name + "/" + func_name var func_ptr = _get_global_or_null[func_cache_name]() @@ -229,7 +231,7 @@ fn _get_dylib_function[ @always_inline("nodebug") -fn external_call[callee: StringLiteral, type: AnyRegType]() -> type: +fn external_call[callee: StringLiteral, type: AnyTrivialRegType]() -> type: """Calls an external function. Parameters: @@ -250,7 +252,7 @@ fn external_call[callee: StringLiteral, type: AnyRegType]() -> type: @always_inline("nodebug") fn external_call[ - callee: StringLiteral, type: AnyRegType, T0: AnyRegType + callee: StringLiteral, type: AnyTrivialRegType, T0: AnyTrivialRegType ](arg0: T0) -> type: """Calls an external function. @@ -278,7 +280,10 @@ fn external_call[ @always_inline("nodebug") fn external_call[ - callee: StringLiteral, type: AnyRegType, T0: AnyRegType, T1: AnyRegType + callee: StringLiteral, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, ](arg0: T0, arg1: T1) -> type: """Calls an external function. @@ -311,10 +316,10 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, ](arg0: T0, arg1: T1, arg2: T2) -> type: """Calls an external function. @@ -349,11 +354,11 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: """Calls an external function. @@ -390,12 +395,12 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: """Calls an external function. @@ -434,13 +439,13 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: """Calls an external function. @@ -481,14 +486,14 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: """Calls an external function. @@ -531,15 +536,15 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -593,16 +598,16 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -659,17 +664,17 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -729,18 +734,18 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, - T10: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, + T10: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -803,19 +808,19 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, - T10: AnyRegType, - T11: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, + T10: AnyTrivialRegType, + T11: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -903,20 +908,20 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, - T10: AnyRegType, - T11: AnyRegType, - T12: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, + T10: AnyTrivialRegType, + T11: AnyTrivialRegType, + T12: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -1009,21 +1014,21 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, - T10: AnyRegType, - T11: AnyRegType, - T12: AnyRegType, - T13: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, + T10: AnyTrivialRegType, + T11: AnyTrivialRegType, + T12: AnyTrivialRegType, + T13: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -1121,22 +1126,22 @@ fn external_call[ @always_inline("nodebug") fn external_call[ callee: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, - T10: AnyRegType, - T11: AnyRegType, - T12: AnyRegType, - T13: AnyRegType, - T14: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, + T10: AnyTrivialRegType, + T11: AnyTrivialRegType, + T12: AnyTrivialRegType, + T13: AnyTrivialRegType, + T14: AnyTrivialRegType, ]( arg0: T0, arg1: T1, @@ -1242,7 +1247,9 @@ fn external_call[ @always_inline("nodebug") -fn _external_call_const[callee: StringLiteral, type: AnyRegType]() -> type: +fn _external_call_const[ + callee: StringLiteral, type: AnyTrivialRegType +]() -> type: """Mark the external function call as having no observable effects to the program state. This allows the compiler to optimize away successive calls to the same function. @@ -1269,7 +1276,7 @@ fn _external_call_const[callee: StringLiteral, type: AnyRegType]() -> type: @always_inline("nodebug") fn _external_call_const[ - callee: StringLiteral, type: AnyRegType, T0: AnyRegType + callee: StringLiteral, type: AnyTrivialRegType, T0: AnyTrivialRegType ](arg0: T0) -> type: """Mark the external function call as having no observable effects to the program state. This allows the compiler to optimize away successive calls @@ -1301,7 +1308,10 @@ fn _external_call_const[ @always_inline("nodebug") fn _external_call_const[ - callee: StringLiteral, type: AnyRegType, T0: AnyRegType, T1: AnyRegType + callee: StringLiteral, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, ](arg0: T0, arg1: T1) -> type: """Mark the external function call as having no observable effects to the program state. This allows the compiler to optimize away successive calls diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 239970ff7e..c19f1bac4e 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -616,7 +616,8 @@ fn alignof[ @always_inline("nodebug") fn bitwidthof[ - type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() + type: AnyTrivialRegType, + target: __mlir_type.`!kgen.target` = _current_target(), ]() -> IntLiteral: """Returns the size of (in bits) of the type. @@ -651,7 +652,8 @@ fn bitwidthof[ @always_inline("nodebug") fn simdwidthof[ - type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() + type: AnyTrivialRegType, + target: __mlir_type.`!kgen.target` = _current_target(), ]() -> IntLiteral: """Returns the vector size of the type on the host system. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index b67c7ee4ad..c2f65a6e3e 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -32,7 +32,7 @@ from memory import AddressSpace, DTypePointer @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, type: AnyRegType, has_side_effect: Bool = True + intrin: StringLiteral, type: AnyTrivialRegType, has_side_effect: Bool = True ]() -> type: """Calls an LLVM intrinsic with no arguments. @@ -82,8 +82,8 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0) -> type: """Calls an LLVM intrinsic with one argument. @@ -137,9 +137,9 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1) -> type: """Calls an LLVM intrinsic with two arguments. @@ -195,10 +195,10 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2) -> type: """Calls an LLVM intrinsic with three arguments. @@ -259,11 +259,11 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: """Calls an LLVM intrinsic with four arguments. @@ -327,12 +327,12 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: """Calls an LLVM intrinsic with five arguments. @@ -394,13 +394,13 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: """Calls an LLVM intrinsic with six arguments. @@ -465,14 +465,14 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: """Calls an LLVM intrinsic with seven arguments. @@ -538,15 +538,15 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, has_side_effect: Bool = True, ]( arg0: T0, @@ -622,16 +622,16 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, has_side_effect: Bool = True, ]( arg0: T0, @@ -711,17 +711,17 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, - type: AnyRegType, - T0: AnyRegType, - T1: AnyRegType, - T2: AnyRegType, - T3: AnyRegType, - T4: AnyRegType, - T5: AnyRegType, - T6: AnyRegType, - T7: AnyRegType, - T8: AnyRegType, - T9: AnyRegType, + type: AnyTrivialRegType, + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, + T2: AnyTrivialRegType, + T3: AnyTrivialRegType, + T4: AnyTrivialRegType, + T5: AnyTrivialRegType, + T6: AnyTrivialRegType, + T7: AnyTrivialRegType, + T8: AnyTrivialRegType, + T9: AnyTrivialRegType, has_side_effect: Bool = True, ]( arg0: T0, @@ -1482,7 +1482,7 @@ fn strided_store[ # ===-------------------------------------------------------------------===# -fn _mlirtype_is_eq[t1: AnyRegType, t2: AnyRegType]() -> Bool: +fn _mlirtype_is_eq[t1: AnyTrivialRegType, t2: AnyTrivialRegType]() -> Bool: """Compares the two type for equality. Parameters: @@ -1534,7 +1534,7 @@ fn _type_is_eq[t1: AnyType, t2: AnyType]() -> Bool: @register_passable("trivial") -struct _RegisterPackType[*a: AnyRegType]: +struct _RegisterPackType[*a: AnyTrivialRegType]: var storage: __mlir_type[`!kgen.pack<`, a, `>`] @always_inline("nodebug") diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index aa3218bd7c..7d938a712a 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -33,7 +33,7 @@ from sys.intrinsics import _type_is_eq fn _set_array_elem[ index: Int, size: Int, - type: AnyRegType, + type: AnyTrivialRegType, ]( val: type, array: Reference[ @@ -61,7 +61,7 @@ fn _set_array_elem[ @always_inline fn _create_array[ - size: Int, type: AnyRegType + size: Int, type: AnyTrivialRegType ](lst: VariadicList[type]) -> __mlir_type[ `!pop.array<`, size.value, `, `, type, `>` ]: @@ -114,7 +114,7 @@ fn _static_tuple_construction_checks[size: Int](): @value @register_passable("trivial") -struct StaticTuple[element_type: AnyRegType, size: Int](Sized): +struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): """A statically sized tuple type which contains elements of homogeneous types. Parameters: diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 5bc828a00a..5bc140b792 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -79,7 +79,7 @@ fn test_sort_small_3() raises: list.append(2) @parameter - fn _less_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _less_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) <= rebind[Int](rhs) var ptr = rebind[Pointer[Int]](list.data) @@ -102,7 +102,7 @@ fn test_sort_small_5() raises: list.append(4) @parameter - fn _less_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _less_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) <= rebind[Int](rhs) var ptr = rebind[Pointer[Int]](list.data) @@ -171,7 +171,7 @@ fn test_sort3_dupe_elements() raises: alias length = 3 fn test[ - cmp_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, + cmp_fn: fn[type: AnyTrivialRegType] (type, type) capturing -> Bool, ]() raises: var list = List[Int](capacity=3) list.append(5) @@ -186,11 +186,11 @@ fn test_sort3_dupe_elements() raises: assert_equal(expected[i], list[i]) @parameter - fn _lt[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _lt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) < rebind[Int](rhs) @parameter - fn _leq[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _leq[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) <= rebind[Int](rhs) test[_lt]() @@ -332,7 +332,7 @@ fn test_quick_sort_repeated_val() raises: list.append(i + 1) @parameter - fn _greater_than[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _greater_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Float32](lhs) > rebind[Float32](rhs) var ptr = rebind[Pointer[Float32]](list.data) @@ -380,7 +380,7 @@ fn test_quick_sort_repeated_val() raises: assert_equal(expected[i], list[i]) @parameter - fn _less_than[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Float32](lhs) < rebind[Float32](rhs) expected = List[Float32]( @@ -434,7 +434,7 @@ fn test_partition_top_k(length: Int, k: Int) raises: list.append(i) @parameter - fn _great_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _great_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Float32](lhs) >= rebind[Float32](rhs) var ptr = rebind[Pointer[Float32]](list.data) @@ -453,8 +453,8 @@ fn test_sort_stress() raises: @__copy_capture(random_seed) @parameter fn test[ - cmp_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, - check_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, + cmp_fn: fn[type: AnyTrivialRegType] (type, type) capturing -> Bool, + check_fn: fn[type: AnyTrivialRegType] (type, type) capturing -> Bool, ](length: Int) raises: var list = List[Int](capacity=length) for _ in range(length): @@ -468,22 +468,22 @@ fn test_sort_stress() raises: @parameter @always_inline - fn _gt[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _gt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) > rebind[Int](rhs) @parameter @always_inline - fn _geq[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _geq[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) >= rebind[Int](rhs) @parameter @always_inline - fn _lt[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _lt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) < rebind[Int](rhs) @parameter @always_inline - fn _leq[type: AnyRegType](lhs: type, rhs: type) -> Bool: + fn _leq[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) <= rebind[Int](rhs) for i in range(len(lens)): From 9d6c9d48e7345014b2e6747e2e8dbbf249e008fc Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 28 May 2024 20:46:46 -0400 Subject: [PATCH 0736/2019] [stdlib] docs: Changelog use of Bool in Reference.is_mutable MODULAR_ORIG_COMMIT_REV_ID: bd0f503f1e2e6286d97c63430f7be8686968b8e8 --- docs/changelog.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 85a1d7fdd6..bb1f27ba6b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -247,6 +247,26 @@ what we publish. processing. Previously it required the use of an internal MLIR type to achieve this. +- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, + not a low-level `__mlir_type.i1` value. + + This improves the ergonomics of spelling out a + `Reference` type explicitly. For example, to define a struct holding a + `Reference`, you can now write: + + ```mojo + struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: + var data: Reference[Int32, is_mutable, lifetime] + ``` + + Or to specify a field that is always immutable, `False` can be specified + as the mutability: + + ```mojo + struct Foo[lifetime: AnyLifetime[False].type]: + var data: Reference[Int32, False, lifetime] + ``` + - `object` now implements all the bitwise operators. ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) From 7063fdbc7964cbecbe776d07e6d52dbeef0fe599 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 28 May 2024 18:15:04 -0700 Subject: [PATCH 0737/2019] [mojo-docs] Fix formatting in changelog due to rename (NFC) (#40778) MODULAR_ORIG_COMMIT_REV_ID: 8d67b86b72155091c99aeb8c7db0685b5ae5502c --- docs/changelog-released.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 51563ca4bd..faa318fa04 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -547,10 +547,10 @@ modular update mojo #### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of - `AnyTrivialRegType`, please upgrade your code to `AnyType` in examples like this: + `AnyRegType`, please upgrade your code to `AnyType` in examples like this: ```mojo - fn your_function[*Types: AnyTrivialRegType](*args: *Ts): ... + fn your_function[*Types: AnyRegType](*args: *Ts): ... ``` This move gives you access to a nicer API and has the benefit of being memory @@ -2361,7 +2361,7 @@ the previous "read to EOF" behavior when size is negative. `Bool` for whether the assertion succeeded or not. - Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no - longer (implicitly) assumed to be register-passable. A new `AnyTrivialRegType` type + longer (implicitly) assumed to be register-passable. A new `AnyRegType` type is used to represent generic types that are register passable. - Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) From 555c32417983ecf1d3b89dd42f702f3cd0381d56 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 28 May 2024 21:51:21 -0400 Subject: [PATCH 0738/2019] [stdlib] polish: Switch inline_string.mojo to use `StringSlice` instead of `StringRef` This improves the safety and maintainability of this code by moving to an efficient but lifetime-checked alternative. * Change inlined_string.mojo to use safe `StringSlice` instead of dangerous `StringRef`. - Change `InlineString.__iadd__` to take `StringSlice` (was `StringRef`) - Remove `InlineString._strref_dangerous()` - Add `InlineString.{as_string_slice, as_bytes_slice}` - Do the above for `_FixedString` as well - Rename `InlineString.as_uint8_ptr` to `unsafe_ptr` * Add `Formatter.write_str(StringSlice)` * Add `StringSlice(unsafe_from_utf8_strref: StringRef)` initializer * Add `StringSlice.unsafe_ptr()` MODULAR_ORIG_COMMIT_REV_ID: 9a68947ceeef3e559203b613c96f8b20ac00add6 --- stdlib/src/builtin/string_literal.mojo | 14 +-- stdlib/src/utils/_format.mojo | 14 +++ stdlib/src/utils/inline_string.mojo | 149 ++++++++++++++++--------- stdlib/src/utils/string_slice.mojo | 64 +++++++++++ 4 files changed, 178 insertions(+), 63 deletions(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 8b3428af69..49467a1dfd 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -250,16 +250,14 @@ struct StringLiteral( return self.__str__().__repr__() @always_inline - fn as_string_slice( - self: Reference[Self, _, _] - ) -> StringSlice[False, ImmutableStaticLifetime]: + fn as_string_slice(self) -> StringSlice[False, ImmutableStaticLifetime]: """Returns a string slice of this static string literal. Returns: A string slice pointing to this static string literal. """ - var bytes = self[].as_bytes_slice() + var bytes = self.as_bytes_slice() # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually @@ -269,9 +267,7 @@ struct StringLiteral( ) @always_inline - fn as_bytes_slice( - self: Reference[Self, _, _] - ) -> Span[UInt8, False, ImmutableStaticLifetime]: + fn as_bytes_slice(self) -> Span[UInt8, False, ImmutableStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -279,11 +275,11 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - var ptr = self[].unsafe_uint8_ptr() + var ptr = self.unsafe_uint8_ptr() return Span[UInt8, False, ImmutableStaticLifetime]( unsafe_ptr=ptr, - len=self[]._byte_length(), + len=self._byte_length(), ) fn format_to(self, inout writer: Formatter): diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index f7a4b5411c..d562ffcea3 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -99,6 +99,20 @@ struct Formatter: """ self._write_func(self._write_func_arg, strref) + @always_inline + fn write_str(inout self, str_slice: StringSlice[False, _]): + """ + Write a string slice to this formatter. + + Args: + str_slice: The string slice to write to this formatter. Must NOT be + null terminated. + """ + + var strref: StringRef = str_slice._strref_dangerous() + + self.write_str(strref) + # ===------------------------------------------------------------------=== # # Factory methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index be9d618783..15d0a1ec4e 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -21,7 +21,7 @@ from memory import memcpy, LegacyPointer, UnsafePointer from collections import Optional -from utils import InlineArray, Variant +from utils import InlineArray, Variant, StringSlice from utils._format import ToFormatter @@ -109,24 +109,24 @@ struct InlineString(Sized, Stringable, CollectionElement): Args: string: The string to append. """ - self.__iadd__(string._strref_dangerous()) + self.__iadd__(string.as_string_slice()) - fn __iadd__(inout self, strref: StringRef): + fn __iadd__(inout self, str_slice: StringSlice[False]): """Appends another string to this string. Args: - strref: The string to append. + str_slice: The string to append. """ - var total_len = len(self) + len(strref) + var total_len = len(self) + str_slice._byte_length() # NOTE: Not guaranteed that we're in the small layout even if our # length is shorter than the small capacity. if not self._is_small(): - self._storage[String] += strref + self._storage[String] += str_slice elif total_len < Self.SMALL_CAP: try: - self._storage[_FixedString[Self.SMALL_CAP]] += strref + self._storage[_FixedString[Self.SMALL_CAP]] += str_slice except e: abort( "unreachable: InlineString append to FixedString failed: " @@ -150,8 +150,8 @@ struct InlineString(Sized, Stringable, CollectionElement): # Copy the bytes from the additional string. memcpy( dest=buffer.unsafe_ptr() + len(self), - src=strref.unsafe_ptr(), - count=len(strref), + src=str_slice.unsafe_ptr(), + count=str_slice._byte_length(), ) # Record that we've initialized `total_len` count of elements @@ -188,7 +188,7 @@ struct InlineString(Sized, Stringable, CollectionElement): """ var string = self - string += other._strref_dangerous() + string += other.as_string_slice() return string fn __add__(self, other: InlineString) -> Self: @@ -202,7 +202,7 @@ struct InlineString(Sized, Stringable, CollectionElement): """ var string = self - string += other._strref_dangerous() + string += other.as_string_slice() return string # ===------------------------------------------------------------------=== # @@ -236,7 +236,7 @@ struct InlineString(Sized, Stringable, CollectionElement): return res - fn as_uint8_ptr(self) -> UnsafePointer[UInt8]: + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Returns a pointer to the bytes of string data. Returns: @@ -248,22 +248,39 @@ struct InlineString(Sized, Stringable, CollectionElement): else: return self._storage[String].unsafe_uint8_ptr() - fn _strref_dangerous(self) -> StringRef: - """ - Returns an inner pointer to the string as a StringRef. - This functionality is extremely dangerous because Mojo eagerly releases - strings. Using this requires the use of the _strref_keepalive() method - to keep the underlying string alive long enough. + @always_inline + fn as_string_slice( + self: Reference[Self, _, _] + ) -> StringSlice[self.is_mutable, self.lifetime]: + """Returns a string slice of the data owned by this inline string. + + Returns: + A string slice pointing to the data owned by this inline string. """ - return StringRef {data: self.as_uint8_ptr(), length: len(self)} - fn _strref_keepalive(self): + # FIXME(MSTDL-160): + # Enforce UTF-8 encoding in _FixedString so this is actually + # guaranteed to be valid. + return StringSlice(unsafe_from_utf8=self[].as_bytes_slice()) + + @always_inline + fn as_bytes_slice( + self: Reference[Self, _, _] + ) -> Span[UInt8, self.is_mutable, self.lifetime]: """ - A noop that keeps `self` alive through the call. This - can be carefully used with `_strref_dangerous()` to wield inner pointers - without the string getting deallocated early. + Returns a contiguous slice of the bytes owned by this string. + + This does not include the trailing null terminator. + + Returns: + A contiguous slice pointing to the bytes owned by this string. """ - pass + + return Span[UInt8, self.is_mutable, self.lifetime]( + unsafe_ptr=self[].unsafe_ptr(), + # Does NOT include the NUL terminator. + len=len(self[]), + ) # ===----------------------------------------------------------------------===# @@ -360,7 +377,7 @@ struct _FixedString[CAP: Int]( Args: literal: The string to append. """ - self.__iadd__(StringRef(literal)) + self.__iadd__(literal.as_string_slice()) fn __iadd__(inout self, string: String) raises: """Appends another string to this string. @@ -368,16 +385,16 @@ struct _FixedString[CAP: Int]( Args: string: The string to append. """ - self.__iadd__(string._strref_dangerous()) + self.__iadd__(string.as_string_slice()) @always_inline - fn __iadd__(inout self, strref: StringRef) raises: + fn __iadd__(inout self, str_slice: StringSlice[False]) raises: """Appends another string to this string. Args: - strref: The string to append. + str_slice: The string to append. """ - var err = self._iadd_non_raising(strref) + var err = self._iadd_non_raising(str_slice) if err: raise err.value()[] @@ -387,7 +404,7 @@ struct _FixedString[CAP: Int]( @always_inline fn __str__(self) -> String: - return String(self._strref_dangerous()) + return String(self.as_string_slice()) fn __len__(self) -> Int: return self.size @@ -396,15 +413,18 @@ struct _FixedString[CAP: Int]( # Methods # ===------------------------------------------------------------------=== # - fn _iadd_non_raising(inout self, strref: StringRef) -> Optional[Error]: - var total_len = len(self) + len(strref) + fn _iadd_non_raising( + inout self, + str_slice: StringSlice[False], + ) -> Optional[Error]: + var total_len = len(self) + str_slice._byte_length() - # Ensure there is sufficient capacity to append `strref` + # Ensure there is sufficient capacity to append `str_slice` if total_len > CAP: return Optional( Error( "Insufficient capacity to append len=" - + str(len(strref)) + + str(str_slice._byte_length()) + " string to len=" + str(len(self)) + " FixedString with capacity=" @@ -412,11 +432,11 @@ struct _FixedString[CAP: Int]( ) ) - # Append the bytes from `strref` at the end of the current string + # Append the bytes from `str_slice` at the end of the current string memcpy( - DTypePointer(self.buffer.unsafe_ptr().bitcast[UInt8]() + len(self)), - strref.data, - len(strref), + dest=self.buffer.unsafe_ptr() + len(self), + src=str_slice.unsafe_ptr(), + count=str_slice._byte_length(), ) self.size = total_len @@ -424,20 +444,24 @@ struct _FixedString[CAP: Int]( return None fn format_to(self, inout writer: Formatter): - writer.write_str(self._strref_dangerous()) + writer.write_str(self.as_string_slice()) fn _unsafe_to_formatter(inout self) -> Formatter: fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() + var str_slice = StringSlice[False, ImmutableStaticLifetime]( + unsafe_from_utf8_strref=strref + ) + # FIXME(#37990): - # Use `ptr[] += strref` and remove _iadd_non_raising after + # Use `ptr[] += str_slice` and remove _iadd_non_raising after # "failed to fold operation lit.try" is fixed. # try: - # ptr[] += strref + # ptr[] += str_slice # except e: # abort("error formatting to FixedString: " + str(e)) - var err = ptr[]._iadd_non_raising(strref) + var err = ptr[]._iadd_non_raising(str_slice) if err: abort("error formatting to FixedString: " + str(err.value()[])) @@ -455,19 +479,36 @@ struct _FixedString[CAP: Int]( """ return self.buffer.unsafe_ptr() - fn _strref_dangerous(self) -> StringRef: - """ - Returns an inner pointer to the string as a StringRef. - This functionality is extremely dangerous because Mojo eagerly releases - strings. Using this requires the use of the _strref_keepalive() method - to keep the underlying string alive long enough. + @always_inline + fn as_string_slice( + self: Reference[Self, _, _] + ) -> StringSlice[self.is_mutable, self.lifetime]: + """Returns a string slice of the data owned by this fixed string. + + Returns: + A string slice pointing to the data owned by this fixed string. """ - return StringRef {data: self.unsafe_ptr(), length: len(self)} - fn _strref_keepalive(self): + # FIXME(MSTDL-160): + # Enforce UTF-8 encoding in _FixedString so this is actually + # guaranteed to be valid. + return StringSlice(unsafe_from_utf8=self[].as_bytes_slice()) + + @always_inline + fn as_bytes_slice( + self: Reference[Self, _, _] + ) -> Span[UInt8, self.is_mutable, self.lifetime]: """ - A noop that keeps `self` alive through the call. This - can be carefully used with `_strref_dangerous()` to wield inner pointers - without the string getting deallocated early. + Returns a contiguous slice of the bytes owned by this string. + + This does not include the trailing null terminator. + + Returns: + A contiguous slice pointing to the bytes owned by this string. """ - pass + + return Span[UInt8, self.is_mutable, self.lifetime]( + unsafe_ptr=self[].unsafe_ptr(), + # Does NOT include the NUL terminator. + len=self[].size, + ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index e37d9fefd9..17dfcb1da7 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -60,6 +60,28 @@ struct StringSlice[ self._slice = unsafe_from_utf8^ + fn __init__(inout self, *, unsafe_from_utf8_strref: StringRef): + """ + Construct a new StringSlice from a StringRef pointing to UTF-8 encoded + bytes. + + Safety: + - `unsafe_from_utf8_strref` MUST point to data that is valid for + `lifetime`. + - `unsafe_from_utf8_strref` MUST be valid UTF-8 encoded data. + + Args: + unsafe_from_utf8_strref: A StringRef of bytes encoded in UTF-8. + """ + var strref = unsafe_from_utf8_strref + + var byte_slice = Span[UInt8, is_mutable, lifetime]( + unsafe_ptr=strref.unsafe_ptr(), + len=len(strref), + ) + + self = Self(unsafe_from_utf8=byte_slice) + # ===------------------------------------------------------------------===# # Trait implementations # ===------------------------------------------------------------------===# @@ -80,3 +102,45 @@ struct StringSlice[ A slice containing the underlying sequence of encoded bytes. """ return self._slice + + @always_inline + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: + """ + Gets a pointer to the first element of this string slice. + + Returns: + A pointer pointing at the first element of this string slice. + """ + + return self._slice.unsafe_ptr() + + @always_inline + fn _byte_length(self) -> Int: + """ + Get the length of this string slice in bytes. + + Returns: + The length of this string slice in bytes. + """ + + return len(self.as_bytes_slice()) + + fn _strref_dangerous(self) -> StringRef: + """ + Returns an inner pointer to the string as a StringRef. + + Safety: + This functionality is extremely dangerous because Mojo eagerly + releases strings. Using this requires the use of the + _strref_keepalive() method to keep the underlying string alive long + enough. + """ + return StringRef(self.unsafe_ptr(), self._byte_length()) + + fn _strref_keepalive(self): + """ + A no-op that keeps `self` alive through the call. This + can be carefully used with `_strref_dangerous()` to wield inner pointers + without the string getting deallocated early. + """ + pass From f1799b167c11ee5ee26ca34e3b72195a1a5a22c9 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 28 May 2024 22:48:27 -0400 Subject: [PATCH 0739/2019] [stdlib] polish: Replace `StringRef` with `StringSlice` in `write_str()` * Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. MODULAR_ORIG_COMMIT_REV_ID: 9d1025279137719e064227a069725595124c4a28 --- docs/changelog.md | 12 +++++++++- stdlib/src/builtin/hex.mojo | 20 ++++++++++++++--- stdlib/src/builtin/int.mojo | 21 ++--------------- stdlib/src/builtin/simd.mojo | 26 ++++++++++++--------- stdlib/src/builtin/string.mojo | 5 +---- stdlib/src/builtin/string_literal.mojo | 5 +---- stdlib/src/utils/_format.mojo | 18 ++++++++++----- stdlib/src/utils/string_slice.mojo | 31 +++++++++++++++++++++++++- 8 files changed, 90 insertions(+), 48 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bb1f27ba6b..a4c643aa02 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -296,10 +296,20 @@ what we publish. - Added a new `Span` type for taking slices of contiguous collections. ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) +- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type + in standard library code. + + `StringSlice` is a non-owning reference to encoded string data. Unlike + `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it + points to. + + - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. + - Changed `Formatter.write_str()` to take a safe `StringSlice`. + - Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which returns a `Span` of the bytes owned by the string. -- Add new `ImmStaticLifetime` and `MutStaticLifetime` helpers +- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers - Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index f5f771d7f2..4015032967 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -134,7 +134,15 @@ fn _try_write_int( fmt.write_str(prefix) if value == 0: - var zero = StringRef(digit_chars_array, 1) + # TODO: Replace with safe digit_chars[:1] syntax. + # SAFETY: + # This static lifetime is valid as long as we're using a + # `StringLiteral` for `digit_chars`. + var zero = StringSlice[False, ImmutableStaticLifetime]( + # TODO: Remove cast after transition to UInt8 strings is complete. + unsafe_from_utf8_ptr=digit_chars_array.bitcast[UInt8](), + len=1, + ) fmt.write_str(zero) return @@ -197,8 +205,14 @@ fn _try_write_int( # bytes from our final `buf_ptr` to the end of the buffer. var len = CAPACITY - offset - var strref = StringRef(buf_ptr, len) + # SAFETY: + # Create a slice to only those bytes in `buf` that have been initialized. + var str_slice = StringSlice[False, __lifetime_of(buf)]( + # TODO: Remove cast after transition to UInt8 strings is complete. + unsafe_from_utf8_ptr=buf_ptr.bitcast[UInt8](), + len=len, + ) - fmt.write_str(strref) + fmt.write_str(str_slice) return None diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 13b5edeacb..e503825cdd 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -22,6 +22,7 @@ from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf from builtin.hex import _try_write_int +from builtin.simd import _format_scalar from utils._visualizers import lldb_formatter_wrapping_type from utils._format import Formattable, Formatter @@ -389,25 +390,7 @@ struct Int( + str(err.value()[]) ) else: - # Stack allocate enough bytes to store any formatted 64-bit integer - alias size: Int = 32 - - var buf = InlineArray[UInt8, size](fill=0) - - # Format the integer to the local byte array - var len = _snprintf["%li"](buf.unsafe_ptr(), size, self.value) - - # Create a StringRef that does NOT include the NUL terminator written - # to the buffer. - # - # Write the formatted integer to the formatter. - # - # SAFETY: - # `buf` is kept alive long enough for the use of this StringRef. - writer.write_str(StringRef(buf.unsafe_ptr(), len)) - - # Keep buf alive until we've finished with the StringRef - _ = buf^ + _format_scalar(writer, Int64(self)) fn __repr__(self) -> String: """Get the integer as a string. Returns the same `String` as `__str__`. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a4a9569c77..395b5c2964 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -39,7 +39,8 @@ from utils.numerics import ( min_or_neg_inf as _min_or_neg_inf, ) from utils._visualizers import lldb_formatter_wrapping_type -from utils import InlineArray +from utils import InlineArray, StringSlice + from .dtype import ( _integral_type_of, _get_dtype_printf_format, @@ -599,7 +600,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if size > 1: # TODO: Fix when slice indexing is implemented on StringSlice - values = StringSlice(output.as_bytes_slice()[1:-1]) + values = StringSlice(unsafe_from_utf8=output.as_bytes_slice()[1:-1]) return ( "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" ) @@ -2867,7 +2868,8 @@ fn _simd_apply[ fn _format_scalar[ - dtype: DType, float_format: StringLiteral = "%.17g" + dtype: DType, + float_format: StringLiteral = "%.17g", ](inout writer: Formatter, value: Scalar[dtype]): # Stack allocate enough bytes to store any formatted Scalar value of any # type. @@ -2875,12 +2877,16 @@ fn _format_scalar[ var buf = InlineArray[UInt8, size](fill=0) - var buf_ptr = buf.unsafe_ptr() - - var wrote = _snprintf_scalar[dtype, float_format](buf_ptr, size, value) - - var strref = StringRef(buf_ptr, wrote) + var wrote = _snprintf_scalar[dtype, float_format]( + buf.unsafe_ptr(), + size, + value, + ) - writer.write_str(strref) + # SAFETY: + # Create a slice to only those bytes in `buf` that have been initialized. + var str_slice = StringSlice[False, __lifetime_of(buf)]( + unsafe_from_utf8_ptr=buf.unsafe_ptr(), len=wrote + ) - _ = buf^ # Keep alive + writer.write_str(str_slice) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index cebc608e2a..30b5ad37d3 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1144,10 +1144,7 @@ struct String( writer: The formatter to write to. """ - # SAFETY: - # Safe because `self` is borrowed, so its lifetime - # extends beyond this function. - writer.write_str(self._strref_dangerous()) + writer.write_str(self.as_string_slice()) fn _unsafe_to_formatter(inout self) -> Formatter: """ diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 49467a1dfd..c6b242df00 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -290,10 +290,7 @@ struct StringLiteral( writer: The formatter to write to. """ - # SAFETY: - # Safe because `self` is borrowed, so the lifetime of this - # StringRef extends beyond this function. - writer.write_str(StringRef(self)) + writer.write_str(self.as_string_slice()) fn __contains__(self, substr: StringLiteral) -> Bool: """Returns True if the substring is contained within the current string. diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index d562ffcea3..8368ce4718 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -88,16 +88,19 @@ struct Formatter: # Methods # ===------------------------------------------------------------------=== # + # TODO(cleanup): + # Remove this overload by defining a working + # `StringSlice.__init__(StringLiteral)` implicit conversion. @always_inline - fn write_str(inout self, strref: StringRef): + fn write_str(inout self, literal: StringLiteral): """ - Write a string to this formatter. + Write a string literal to this formatter. Args: - strref: The string to write to this formatter. Must NOT be null - terminated. + literal: The string literal to write. """ - self._write_func(self._write_func_arg, strref) + + self.write_str(literal.as_string_slice()) @always_inline fn write_str(inout self, str_slice: StringSlice[False, _]): @@ -109,9 +112,12 @@ struct Formatter: null terminated. """ + # SAFETY: + # Safe because `str_slice` is a `borrowed` arg, and so alive at least + # as long as this call. var strref: StringRef = str_slice._strref_dangerous() - self.write_str(strref) + self._write_func(self._write_func_arg, strref) # ===------------------------------------------------------------------=== # # Factory methods diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 17dfcb1da7..d01e865157 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -46,7 +46,7 @@ struct StringSlice[ @always_inline fn __init__( - inout self, owned unsafe_from_utf8: Span[UInt8, is_mutable, lifetime] + inout self, *, owned unsafe_from_utf8: Span[UInt8, is_mutable, lifetime] ): """ Construct a new StringSlice from a sequence of UTF-8 encoded bytes. @@ -82,6 +82,35 @@ struct StringSlice[ self = Self(unsafe_from_utf8=byte_slice) + @always_inline + fn __init__( + inout self, + *, + unsafe_from_utf8_ptr: UnsafePointer[UInt8], + len: Int, + ): + """ + Construct a StringSlice from a pointer to a sequence of UTF-8 encoded + bytes and a length. + + Safety: + - `unsafe_from_utf8_ptr` MUST point to at least `len` bytes of valid + UTF-8 encoded data. + - `unsafe_from_utf8_ptr` must point to data that is live for the + duration of `lifetime`. + + Args: + unsafe_from_utf8_ptr: A pointer to a sequence of bytes encoded in + UTF-8. + len: The number of bytes of encoded data. + """ + var byte_slice = Span[UInt8, is_mutable, lifetime]( + unsafe_ptr=unsafe_from_utf8_ptr, + len=len, + ) + + self._slice = byte_slice + # ===------------------------------------------------------------------===# # Trait implementations # ===------------------------------------------------------------------===# From e36289ed30c2d04d33f7fc7a9f33324e4cdf1699 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 28 May 2024 22:49:51 -0400 Subject: [PATCH 0740/2019] [stdlib] comment: Fix a few header comment typos#40529 MODULAR_ORIG_COMMIT_REV_ID: 56c1ef2ecdbee724fc43d06385e9e1902dcb55af --- stdlib/src/collections/set.mojo | 6 +++--- stdlib/src/utils/inline_string.mojo | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 3ddb0f9bd2..58b6837874 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -45,7 +45,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): var _data: Dict[T, NoneType] # ===-------------------------------------------------------------------===# - # Methods + # Life cycle methods # ===-------------------------------------------------------------------===# fn __init__(inout self, *ts: T): @@ -87,7 +87,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): self._data = other._data^ # ===-------------------------------------------------------------------===# - # Methods + # Operator dunders # ===-------------------------------------------------------------------===# fn __contains__(self, t: T) -> Bool: @@ -262,7 +262,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): self.symmetric_difference_update(other) # ===-------------------------------------------------------------------===# - # Methods + # Trait implementations # ===-------------------------------------------------------------------===# fn __bool__(self) -> Bool: diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 15d0a1ec4e..1fe6ed877e 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -337,7 +337,7 @@ struct _FixedString[CAP: Int]( memcpy(self.buffer.unsafe_ptr(), literal.as_uint8_ptr(), len(literal)) # ===------------------------------------------------------------------=== # - # Factor methods + # Factory methods # ===------------------------------------------------------------------=== # @staticmethod From be01044f01ee987a6482e77e88181fe77f68fe19 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 28 May 2024 21:41:15 -0700 Subject: [PATCH 0741/2019] [mojo-stdlib] Add a few methods to StringRef This adds a default constructor and take_front/take_back + drop_front/drop_back methods to follow the LLVM StringRef type. This whole type needs to be reconsidered, and these should use slice syntax, but this was expedient. MODULAR_ORIG_COMMIT_REV_ID: 5b3a3afa86ef3f38f0c2a66b754bff9b067502f9 --- stdlib/src/utils/stringref.mojo | 90 ++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index abbe8d9944..505a712d40 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -61,7 +61,16 @@ struct StringRef( # ===-------------------------------------------------------------------===# @always_inline - fn __init__(str: StringLiteral) -> StringRef: + fn __init__() -> Self: + """Construct a StringRef value with length zero. + + Returns: + Constructed `StringRef` object. + """ + return StringRef(UnsafePointer[UInt8](), 0) + + @always_inline + fn __init__(str: StringLiteral) -> Self: """Construct a StringRef value given a constant string. Args: @@ -75,7 +84,7 @@ struct StringRef( # TODO: #2317 Drop support for this constructor when we have fully # transitioned to UInt8 as the main byte type. @always_inline - fn __init__(ptr: DTypePointer[DType.int8], len: Int) -> StringRef: + fn __init__(ptr: DTypePointer[DType.int8], len: Int) -> Self: """Construct a StringRef value given a (potentially non-0 terminated string). @@ -97,7 +106,7 @@ struct StringRef( return Self {data: unsafe_ptr.bitcast[UInt8](), length: len} @always_inline - fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> StringRef: + fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> Self: """Construct a StringRef value given a (potentially non-0 terminated string). @@ -115,7 +124,7 @@ struct StringRef( return Self {data: unsafe_ptr, length: len} @always_inline - fn __init__(ptr: UnsafePointer[UInt8]) -> StringRef: + fn __init__(ptr: UnsafePointer[UInt8]) -> Self: """Construct a StringRef value given a null-terminated string. Args: @@ -130,7 +139,7 @@ struct StringRef( # TODO: #2317 Drop support for this constructor when we have fully # transitioned to UInt8 as the main byte type. @always_inline - fn __init__(ptr: DTypePointer[DType.int8]) -> StringRef: + fn __init__(ptr: DTypePointer[DType.int8]) -> Self: """Construct a StringRef value given a null-terminated string. Note that you should use the constructor from `DTypePointer[DType.uint8]` instead @@ -151,7 +160,7 @@ struct StringRef( return StringRef(ptr, len) @always_inline - fn __init__(ptr: DTypePointer[DType.uint8]) -> StringRef: + fn __init__(ptr: DTypePointer[DType.uint8]) -> Self: """Construct a StringRef value given a null-terminated string. Args: @@ -167,6 +176,75 @@ struct StringRef( return StringRef(ptr.bitcast[DType.int8](), len) + # ===-------------------------------------------------------------------===# + # Helper methods for slicing + # ===-------------------------------------------------------------------===# + # TODO: Move to slice syntax like str_ref[:42] + + fn take_front(self, num_bytes: Int = 1) -> Self: + """Return a StringRef equal to 'self' but with only the first + `num_bytes` elements remaining. If `num_bytes` is greater than the + length of the string, the entire string is returned. + + Args: + num_bytes: The number of bytes to include. + + Returns: + A new slice that starts with those bytes. + """ + debug_assert(num_bytes >= 0, "num_bytes must be non-negative") + if num_bytes >= self.length: + return self + return Self(self.data, num_bytes) + + fn take_back(self, num_bytes: Int = 1) -> Self: + """Return a StringRef equal to 'self' but with only the last + `num_bytes` elements remaining. If `num_bytes` is greater than the + length of the string, the entire string is returned. + + Args: + num_bytes: The number of bytes to include. + + Returns: + A new slice that ends with those bytes. + """ + debug_assert(num_bytes >= 0, "num_bytes must be non-negative") + if num_bytes >= self.length: + return self + return Self(self.data + (self.length - num_bytes), num_bytes) + + fn drop_front(self, num_bytes: Int = 1) -> Self: + """Return a StringRef equal to 'self' but with the first + `num_bytes` elements skipped. If `num_bytes` is greater than the + length of the string, an empty StringRef is returned. + + Args: + num_bytes: The number of bytes to drop. + + Returns: + A new slice with those bytes skipped. + """ + debug_assert(num_bytes >= 0, "num_bytes must be non-negative") + if num_bytes >= self.length: + return StringRef() + return Self(self.data + num_bytes, self.length - num_bytes) + + fn drop_back(self, num_bytes: Int = 1) -> Self: + """Return a StringRef equal to 'self' but with the last `num_bytes` + elements skipped. If `num_bytes` is greater than the + length of the string, the entire string is returned. + + Args: + num_bytes: The number of bytes to include. + + Returns: + A new slice ends earlier than those bytes. + """ + debug_assert(num_bytes >= 0, "num_bytes must be non-negative") + if num_bytes >= self.length: + return StringRef() + return Self(self.data, self.length - num_bytes) + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# From b60ba25675aa2752a4082d8e9b72c46136120f92 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 29 May 2024 00:10:32 -0700 Subject: [PATCH 0742/2019] [mojo-lang] Forbid non-trivial reg-passable borrows in async functions Async function calls are no longer allowed to borrow non-trivial register-passable types. Because async functions capture their arguments but register-passable types don't have lifetimes (yet), Mojo is not able to correctly track the reference, making this unsafe. To cover this safety gap, Mojo has temporarily disallowed binding non-trivial register-passable types to borrowed arguments in async functions. MODULAR_ORIG_COMMIT_REV_ID: 640d7193844ec7e9103e5f9089ccb147328acecb --- docs/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index a4c643aa02..c781f111be 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -467,6 +467,13 @@ what we publish. ### 🦋 Changed +- Async function calls are no longer allowed to borrow non-trivial + register-passable types. Because async functions capture their arguments but + register-passable types don't have lifetimes (yet), Mojo is not able to + correctly track the reference, making this unsafe. To cover this safety gap, + Mojo has temporarily disallowed binding non-trivial register-passable types + to borrowed arguments in async functions. + - `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids binding non-trivial register-passable types to `AnyTrivialRegType`. This closes a major safety hole in the language. Please use `AnyType` for generic From 4229adcb2ad5803729f6460f6943cf466f7463d4 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Wed, 29 May 2024 06:27:05 -0500 Subject: [PATCH 0743/2019] Internal commit MODULAR_ORIG_COMMIT_REV_ID: 6861a4247f22123d868b210837f78a65521d0670 --- docs/changelog.md | 5 +++ stdlib/src/builtin/_math.mojo | 72 ++++++++++++++++++++++++++++++ stdlib/test/builtin/test_math.mojo | 21 +++++++++ 3 files changed, 98 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c781f111be..07441e0461 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -566,6 +566,11 @@ what we publish. method. This makes it explicit that this implementation does not check if the slice bounds are concrete or within any given object's length. +- `math.gcd` now works on negative inputs, and like Python's implementation, + accepts a variadic list of integers. New overloads for a `List` or `Span`of + integers are also added. + ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) + ### ❌ Removed - The `@unroll` decorator has been deprecated and removed. The decorator was diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 8250806ce7..eea01382fc 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -195,3 +195,75 @@ trait Truncable: # associated types. fn __trunc__(self) -> Self: ... + + +# ===----------------------------------------------------------------------=== # +# gcd +# ===----------------------------------------------------------------------=== # + + +fn gcd(owned m: Int, owned n: Int, /) -> Int: + """Compute the greatest common divisor of two integers. + + Args: + m: The first integer. + n: The second integrer. + + Returns: + The greatest common divisor of the two integers. + """ + while n: + m, n = n, m % n + return abs(m) + + +fn gcd(s: Span[Int], /) -> Int: + """Computes the greatest common divisor of a span of integers. + + Args: + s: A span containing a collection of integers. + + Returns: + The greatest common divisor of all the integers in the span. + """ + if len(s) == 0: + return 0 + var result = s[0] + for item in s[1:]: + result = gcd(item[], result) + if result == 1: + return result + return result + + +@always_inline +fn gcd(l: List[Int], /) -> Int: + """Computes the greatest common divisor of a list of integers. + + Args: + l: A list containing a collection of integers. + + Returns: + The greatest common divisor of all the integers in the list. + """ + return gcd(Span(l)) + + +fn gcd(*values: Int) -> Int: + """Computes the greatest common divisor of a variadic number of integers. + + Args: + values: A variadic list of integers. + + Returns: + The greatest common divisor of the given integers. + """ + # TODO: Deduplicate when we can create a Span from VariadicList + if len(values) == 0: + return 0 + var result = values[0] + for i in range(1, len(values)): + result = gcd(values[i], result) + if result == 1: + return result + return result diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 178b4df545..0cc838f27e 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -84,6 +84,26 @@ def test_pow(): assert_equal(pow(I(0, 1, 2, 3), int(2)), I(0, 1, 4, 9)) +def test_gcd(): + var l = List(2, 4, 6, 8, 16) + var il = InlineArray[Int, 5](4, 16, 2, 8, 6) + assert_equal(gcd(Span(il)), 2) + assert_equal(gcd(2, 4, 6, 8, 16), 2) + assert_equal(gcd(l), 2) + assert_equal(gcd(88, 24), 8) + assert_equal(gcd(0, 0), 0) + assert_equal(gcd(1, 0), 1) + assert_equal(gcd(-2, 4), 2) + assert_equal(gcd(-2, -4), 2) + assert_equal(gcd(24826148, 45296490), 526) + assert_equal(gcd(0, 9), 9) + assert_equal(gcd(4, 4), 4) + assert_equal(gcd(8), 8) + assert_equal(gcd(), 0) + assert_equal(gcd(List[Int]()), 0) + assert_equal(gcd(List(16)), 16) + + def main(): test_abs() test_divmod() @@ -91,3 +111,4 @@ def main(): test_min() test_round() test_pow() + test_gcd() From b6ac3f51f9c737edde345e0a89ea634c51b1e44a Mon Sep 17 00:00:00 2001 From: Nick Smith Date: Wed, 29 May 2024 08:34:45 -0500 Subject: [PATCH 0744/2019] [External] Add proposal for ref convention (#40780) [External] Add proposal for ref convention Moving https://github.com/modularml/mojo/pull/2863 to the nightly branch. Co-authored-by: Nick Smith Closes modularml/mojo#2872 MODULAR_ORIG_COMMIT_REV_ID: 015f97ecf28fcdf4b50660713c8ea12bb41e27cd --- proposals/ref-convention.md | 630 ++++++++++++++++++++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 proposals/ref-convention.md diff --git a/proposals/ref-convention.md b/proposals/ref-convention.md new file mode 100644 index 0000000000..38399090fe --- /dev/null +++ b/proposals/ref-convention.md @@ -0,0 +1,630 @@ +# Mojo references, an alternate take + +Nick Smith + Chris Lattner; May 28, 2024 + +**TL;DR**: This white paper proposes several changes that rethink Mojo’s general +UI around references. It does not change the formal semantic model, but has a +profound impact on explainability and auto-dereferencing. + +## Motivation + +Mojo’s safe references have evolved and iterated a lot. From the initial +compiler re-plumbing that made memory-only types possible, to threading +lifetimes through everything with the introduction of `!lit.ref` to the +development of a “user-space” Reference type, to the recent discussions about +[adding automatic dereference to Reference](https://github.com/modularml/mojo/discussions/2594), +we’ve been iteratively improving the model with a goal of ending up with +something powerful and explainable. + +Along the way, we’ve had a number of challenges to address: + +1. How to make references work, how they dovetail with ASAP destruction + insertion, with exclusivity checking, and how lifetimes and mutability are + represented: These topics are **NOT** touched on in this paper. + +2. The introduction of parametric mutability, which is important to representing + things like a “refitem” whose result is a reference with the same mutability + as ‘self’. We currently support this, but require a weird “define self as + Reference type instead of Self type” approach which is inconsistent with all + the other argument conventions in Mojo. + +3. We need “automatically dereferenced” references for ergonomics, and require + an explainable and predictable model. The recently proposed “automatic + dereference” model follows C++ precedent by building this in as a new form of + LValue to RValue conversion, but this makes the language significantly more + complicated by breaking an invariant: the type loaded from an LValue (an + RValue) has different type than the type stored into it. + +4. We have persistent confusion about the word “reference”: Python considers all + “PythonObject”s to be “object references”, which is completely different than + what the `Reference` type provides. Furthermore, there are also “reference + semantic” types which are more similar to the Python notion and less similar + to `Reference`. It would be awesome to clarify this. + +5. We still need to + [reconsider which keywords](https://github.com/modularml/mojo/blob/main/proposals/lifetimes-keyword-renaming.md) + to use for argument conventions. The `inout` keyword, for example, is + problematic because it works with types that are not movable or copyable. The + callee doesn’t actually move things in and out, it takes a mutable reference. + It is the caller that does the inout magic when needed, so it seems + inappropriate to put this on the callee declaration. + +We believe that this proposed model is also significantly simpler than the +previous approaches. + +## Proposal #1: Introduce a new `ref` argument convention + +The existing `inout` and `borrowed` conventions are argument conventions that +are syntax sugar for an underlying MLIR type `!lit.ref` (with a bit of +additional semantics on top). These references are always “auto dereferenced” in +the body of the function. Let’s introduce a new `ref` convention that allows +specifying an expected lifetime and that is auto-dereferenced in the body like +`inout` is. For the moment, we'll use the syntax `ref []`. Here's a +basic example: + +```mojo +fn take_int_ref(a: Int, ref [_] b: Int) -> Int: + return a+b # b, not b[] +``` + +Alternative syntaxes are proposed later in this document. + +Given this feature, we can remove the ability to define `self` as a `Reference` +and just use this new argument convention. Instead of: + +```mojo +fn __refitem__(self: Reference[Self, _, _], index: Int) -> Reference[ + Self.ElementType, self.is_mutable, self.lifetime + ]: +``` + +You would now use an argument convention: + +```mojo +fn __refitem__(ref [_] self, index: Int) -> Reference[ + # This is a bit yuck, but is simplified further below. + Self.ElementType, __lifetime_of(self).is_mutable, __lifetime_of(self) + ]: + +# Alternatively, name the Lifetime: +fn __refitem__[life: Lifetime](ref [life] self, index: Int) -> Reference[ + # This is a bit yuck, see below. + Self.ElementType, life.is_mutable, life + ]: +``` + +### Supporting address spaces + +`!lit.ref` currently has three parameters: a type, a lifetime, and an address +space. The address space is a relatively niche but important feature used for +GPUs and other accelerators. We specify the address space as follows: + +```mojo +fn foo(...) -> ref [lifetime, addr_space] T: ... +``` + +This parameter would be optional, just as it is currently optional for +`Reference`. + +In the future, the lifetime and address space could potentially be combined into +one value. + +### How do we explain this? + +If we implement this, we would advise Mojo users to use `ref` with an explicit +lifetime in a few situations: + +1. If you want to bind an argument to something of parametric mutability. + +2. When you want to tie the lifetime of one argument to the lifetime of another + argument. + +3. When you want an argument that is guaranteed to be passed in memory: this can + be important and useful for generic arguments that need an identity, + irrespective of whether the concrete type is register passable. + +This also aligns with the C++ notion of “passing by reference”. + +## Proposal #2: Allow the use of `ref` in result positions + +The other feature we need is the ability to _return_ an "automatically +dereferenced" reference. For the moment, we'll use the same `ref []` +syntax for this. This feature can replace the use of `Reference` in the previous +example: + +```mojo +fn __refitem__(ref [_] self, index: Int) + -> ref [__lifetime_of(self)] Self.ElementType: + +# Hopefully someday we'll have a Lifetime type: +fn __refitem__(ref [_] self, index: Int) + -> ref [Lifetime(self)] Self.ElementType: +``` + +This is much more clear. As with the `ref` argument convention, the `ref` result +convention automatically dereferences itself... but this happens on the caller +side. + +This means that auto-deref behavior happens in exactly one place in the +compiler: in the result of function calls whose result convention is a `ref` +result convention. This makes it far less invasive than the previously proposed +auto-deref behavior and is much more predictable for users. + +Note that this gives the developer control over auto-deref, because both of +these are valid: + +```mojo +fn yes_auto_deref(...) -> ref [lifetime] Int: ... +fn no_auto_deref(...) -> Reference[Int, lifetime]: ... +``` + +We would expect `ref` to be the default choice. It's the most versatile: if a +function call returns a `ref` but you want a `Reference`, you can just wrap the +call in the `Reference` constructor. The purpose of `Reference` is to allow +references to be _stored_ in data structures. Furthermore, it supports nesting +such as `Reference[Reference[Reference[Int], ...], ...], ...]` without implicit +promotions interfering. + +## Proposal #3: Remove `__refitem__` + +Now that general functions can return an auto-dereferenced reference, we can get +rid of the `__refitem__` special case and just use `__getitem__`. This +simplifies the language and improves consistency with Python. Our example above +becomes: + +```mojo +fn __getitem__(ref [_] self, index: Int) + -> ref [__lifetime_of(self)] Self.ElementType: +``` + +Note: Neither `inout` nor `borrowed` are allowed in a result position. The +result requires a lifetime so that the caller knows what it refers to, and how +long it is valid for. + +## Proposal #4: Rename `Reference` to `Pointer` (or `SafePointer` ) + +As of Mojo 24.3, our status is: + +1. The `Reference` type doesn’t have support for automatic dereferencing. + +2. The old `Pointer` type got renamed to `LegacyPointer`, and we aim to replace + it entirely with `UnsafePointer`. + +3. The `UnsafePointer` API has been cleaned up and improved, and interacts well + with `Reference`. + +Because there is no automatic dereference, the existing `Reference` type +currently behaves like a pointer, not a reference. You need to explicitly +dereference it with `ptr[]` just like an unsafe pointer… but it adds safety +through lifetime management and semantic checking. + +Let’s just rename it to `Pointer`: The consequence of this is that explicit +dereference syntax is a feature, not a bug. Furthermore, this entire feature +area becomes more explainable and separate from the existing Python reference +types. This allows a consistent story about how Mojo supports both `Pointer` +(for use when building data structures) and `UnsafePointer` (for interacting +with C code). + +## Summary + +We believe that the proposed `ref` conventions will lead to a simpler and more +consistent UX, and a more predictable and simpler automatic dereferencing +feature than previously proposed. Furthermore, we believe that renaming +`Reference` to `Pointer` will clarify terminology and allow library developers +to continue working with explicit safe pointers when building data structures +and other libraries. + +## Discussion + Future direction + +We believe that the proposed model would simplify a lot of things, but there are +some consequences worth mentioning: + +### Argument/Result conventions are not first class types + +The proposal above is consistent with Mojo’s current use of argument +conventions, but is inconsistent with Rust references. Notably, it is NOT +possible to define a local “ref”, and it isn’t possible to define a “ref to a +ref”: + +```mojo +fn weird_tests[life1: Lifetime, life2: Lifetime]( + ref [life1] arg1: Int, ## ok + + # error: 'ref' is an argument convention, not a type. + ref [life1] arg2: ref [life2] Int, + + # This is ok + ref [life1] arg3: Pointer[Int, life2]): + + # error: 'ref' is an argument convention, not a type. + var local1: ref [life1] Int + var local2: Pointer[Int, life1] # ok! +``` + +While this is _different_ than Rust, we feel this is actually a good thing - it +clarifies where automatic dereference happens (on arguments and results of ‘ref’ +functions). It is also clear that `typeof(arg3) == Pointer[Int, life2]` and that +`typeof(arg3[]) == Int` through composition. + +### Multiple results wouldn’t get automatic dereference + +One downside of this proposal (compared to the C++-style autoderef proposal) is +that it wouldn’t be possible to write a function that returns multiple +auto-dereferenced values in a tuple: + +```mojo +# Simple tuple result type is ok of course: +fn g1(...) -> (Int, Int): ... + +# Returning safe pointers is fine: each element needs explicit derefs +fn g2(...) -> (Pointer[Int, life1], Pointer[Int, life2]) + +# error: 'ref' is an argument convention, not a type. +fn g3(...) -> (ref [life1] Int, ref [life2] Int) +``` + +This is unfortunate, and really doesn’t fit with this model, but it isn’t a +common occurrence. + +#### Aside: Mojo may need native support for multiple results anyway + +If this were important to solve for, we could consider extending Mojo to +natively support multiple return values directly. The compiler internally +already supports this, but it is not exposed to Mojo source code. One example of +something that could be supported with native multiple return values are +“unpacking” for non-movable results, e.g.: + +```mojo +fn do_stuff() -> result: NonMovable: + var a : NonMovable + a, result = f() +``` + +… this isn’t possible to support with “multiple results are returned as tuples”. +This doesn’t seem like a priority to solve in the short term, but is a plausible +long term path. + +### What would this simplify in the Mojo compiler? + +This generally makes the type checker simpler, predictable, and more composable, +because it doesn’t cause reference decay to affect literally everything in the +expression type checker. Concretely, we can revert a lot of the stuff Chris has +been working on lately: + +- No need for the new `@automatically_dereference` decorator. +- IRValue::getRValueType is no longer context sensitive. +- Removes the support for allowing ‘self’ to be declared as Reference. +- Removes the `!lit.ref` special case hack in call argument parsing and + parameter inference. + +It also eliminates the need for all the parameter inference improvements that +went in, but they are also not harmful, so they’ll stay in. + +### Reference patterns are still needed + +We will need to build out Mojo's support for +[patterns](https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-patterns) +and the +[`match`](https://docs.python.org/3/tutorial/controlflow.html#match-statements) +statement, which are currently completely missing. As part of this, we'll need +to investigate adding a `ref` pattern. Such a thing would allow a foreach loop +that mutates the elements from within the loop: + +```mojo + for (ref elt) in mutableList: + elt = 42 +``` + +Mojo needs patterns across the language in general: they should work in `match` +statements, `var` declarations, and a few other places. Adding `ref` patterns +would allow having an autodereferenced `var` reference. + +## Syntax alternatives + +We've relegated syntax discussions to this appendix. Let the bikeshedding +commence! 👏 + +We have proposed two new conventions: an argument convention and a result +convention. For both of these conventions, we have used the keyword `ref`. This +terminology has precedent in C++, C#, and many other languages. However, Mojo +aims to be a superset of Python, and in Python, a "reference" is understood to +be a pointer to a heap-allocated, garbage-collected instance of a class. The +argument conventions that we've proposed are entirely unrelated to that feature. +Therefore, it seems wise to consider other syntaxes for the conventions. We +encountered a similar issue with the `Reference` type, and this motivated our +proposal to rename `Reference` to `Pointer`. + +Our use of square brackets in the `ref []` syntax is also worth +interrogating. This syntax is motivated by the fact that the lifetime is being +provided as a parameter to a `!lit.ref` type. However, this type is an +implementation detail — Mojo users are not supposed to know about this. In this +light, the square brackets are quite "mysterious". There is no function being +invoked, and (apparently) no type being instantiated. This usage of square +brackets has no precedent. + +### Syntaxes for the argument convention + +Here are some alternative syntaxes that avoid both of the issues mentioned +above: + +```mojo +# We could repurpose Python's `from` keyword: +fn foo(x from : Int): + +# Or its `in` keyword: +fn foo(x in : Int): + +# Or introduce a new `at` keyword: +fn foo(x at : Int): +``` + +All of these keywords are suggestive of a lifetime being a "location" associated +with a variable. This is a helpful way to think about Mojo's `Lifetime` type — +one of the major purposes of this type (which is still in-development) is to +keep track of where a variable is located (e.g. on the stack), to ensure that +the variable is not used after it is freed. For this reason, "lifetimes" will +likely be renamed at some point, perhaps to something like "regions" or +"origins". In this light, the syntax `x from : Int` makes a lot of +sense. + +That said, it's too early to settle on a keyword for this. If `Lifetime` ends up +being called something weird like `AccessProtocol`, none of these keywords would +make sense! The main thing to take away from this discussion is that +"bracketless" syntaxes are an option. + +### Syntaxes for the result convention + +We can consider a similar syntax for the result convention. However, results are +usually not named, so a naïve translation of the above syntax doesn't look very +clean: + +```mojo +fn foo() -> from : Int: +``` + +Using the `ref` keyword here would avoid this problem: + +```mojo +fn foo() -> ref from : Int: +``` + +But given that we're trying to avoid the term "reference", we should consider +other options. A noun would make the most sense, because we'd like to be able to +say "this function returns a ...". But what word could we use here? + +Traditionally, a function returns a **value**. For example, `len` returns a +value of type `Int`. Our new result convention works differently. It returns a +"reference" to a variable, but this reference is not a value — it can't be +stored in a collection, for example. (At least, not without wrapping it in a +`Pointer`.) + +A reasonable path forward would be say that some functions return **values**, +and other functions return **variables**. This deftly dodges the word +"reference", while still providing intuition about how the result can be used. + +A variable _holds_ a value. Thus, it should be clear that if a function returns +a variable, you can read its value, and if the variable is mutable, you can +overwrite its value. These are exactly the affordances provided by the new +result convention. + +Given all of this, the following syntax might be reasonable: + +```mojo +fn foo() -> var from : Int: + +# The use of `var` here is independent of the `from` syntax. +# We could combine it with the square bracket syntax instead: +fn foo() -> var [] Int: +``` + +The expression `foo()` behaves just like a variable does. If it is mutable, you +can reassign it, or mutate it: + +```mojo +foo() = 0 +foo() += 1 +``` + +The biggest downside of using the `var` keyword for the new result convention is +that as of today, `var` is exclusively used to declare _new_ variables, whereas +in the above syntax, `var` is being used to declare that a function returns a +reference to an _existing_ variable. That said, it's unlikely that users will +misinterpret `-> var from ` as declaring a new variable, given the +context in which it appears. + +As mentioned, `var` is the only noun that seems suitable. However, we could +consider a verb or an adjective: + +```mojo +fn foo() -> select from : Int: + +fn foo() -> select [] Int: + +fn foo() -> shared from : Int: + +fn foo() -> shared [] Int: +``` + +Unfortunately, if `->` is read as "returns", then a verb doesn't make +grammatical sense. An adjective _might_ make sense, but it's not clear what a +good adjective would be. The word `shared` is problematic, because it has +connotations concerning synchronization etc. + +Finally, using the `from`/`in`/`at` keywords in the result convention leads to +an issue with the `:` character. We are using it to specify both the type of the +returned variable, and to mark the end of the function signature: + +```mojo +fn foo() -> var from : Int: +``` + +This is clunky, and may complicate parsing. We can fix this by adding +parentheses: + +```mojo +fn foo() -> (var from : Int): +``` + +Alternatively, if (for some reason) the `Lifetime`/`Origin` type ends up being +parameterized by the type of the variables that it contains, we could drop the +type annotation entirely: + +```mojo +fn foo() -> var from : + +fn foo() -> var at : +``` + +Another option would be to revert to the square bracket syntax, since this +allows us to omit the first colon: + +```mojo +fn foo() -> var [] Int: +``` + +In summary: there are a lot of alternatives worth considering! + +### Keyword alternatives for `inout`, `borrowed`, and `owned` + +Along with contemplating syntaxes for the _new_ conventions, it makes sense to +revisit the syntax of Mojo's _existing_ conventions. The conventions are all +related, and we need a consistent way to talk about them. + +Let’s look at a few examples in 24.3 Mojo: + +```mojo +## Today in mojo: +struct MyInt: + # Note that inout is a lie here, the input is uninitialized. + fn __init__(inout self, value: Int): ... + + # borrowed is the default (e.g. on 'rhs') so not usually written. + fn __add__(borrowed self, rhs: MyInt) -> MyInt: ... + + # This function actually just takes a mutable reference, the + # caller may do some copy in/out depending on its situation though. + fn __iadd__(inout self, rhs: MyInt): ... + + # An owned argument must be initialized when the function is called, + # and it will be deinitialized by the time the function returns. + # (Unless the caller provides a copy.) + fn __del__(owned self): ... +``` + +A common question a programmer has in their mind when browsing unfamiliar code +is: "what does this function do?" A well-designed syntax for arguments and +results will help make it clear what a function does to them. The current +keywords don't achieve this goal: + +1. Using `inout` for the `self` argument of a constructor call is semantically + misleading. `inout` is meant to be used to declare that an _existing_ value + is mutated, but `self` does not have a value at the beginning of the function + call. The role of a constructor is to _initialize_ `self`, so it makes sense + to introduce a new keyword (such as `init`) for this purpose. Note: this + convention is also the convention associated with a standard return type, + e.g. `-> T`. If we had a syntax for naming the result, we could write this as + `-> (init result: T)`. +2. More generally, the keyword `inout` is misleading no matter where it is used. + It suggests that a value will be copied (or moved) into the function, and + then copied back out again. In actuality, an `inout` value is often (but not + always) passed by address. The true purpose of this keyword is to declare + that the argument will be **mutated**, so it makes sense to rename this + keyword to `mut`, or `mutate`. The former is more concise and is consistent + with Rust, so it might be preferable. +3. The keyword `owned` isn't very informative. The purpose of this convention is + to allow a function to "use up" the value of a variable. The variable still + belongs to its original owner, the callee just _deinitializes_ it. There's a + notable caveat: if the caller doesn't use the `^` sigil, they will provide a + _copy_ of their variable, and therefore they will not witness this + deinitialization. But on the callee's side, the variable must always end up + deinitialized, for example by calling its destructor, or its move + constructor. To emphasise this fact, it would make sense to rename `owned` to + `consume`. + +Notice that all of these words are verbs, or abbreviations thereof: + +- `init` (i.e. "initialize") +- `mut` (i.e. "mutate") +- `consume` + +Verbs are nice, because they communicate what a function _does_ to an argument. +In comparison, adjectives such as `owned` are not as clear. + +We also have `borrowed`, which we can rename to `borrow`, thus ensuring that all +of our conventions are verbs. We could consider renaming this to `read` instead +(as a +[soft keyword](https://docs.python.org/3/reference/lexical_analysis.html#soft-keywords)). +But we might not actually need a keyword at all, since this is now the default +convention for both `def` and `fn`. + +In summary, a function will either: + +- `init` an argument +- `borrow` or `read` an argument +- `mut` or `mutate` an argument +- `consume` an argument + +These are just suggestions. Various synonyms of these words might also work +well. Regardless, we should postpone any final decisions until argument +conventions and lifetimes have fully settled. + +If we repaint the earlier example with the new keywords, we get: + +```mojo +## Proposed +struct MyInt: + fn __init__(init self, value: Int): ... + + fn __add__(borrow self, rhs: MyInt) -> MyInt: ... + + fn __iadd__(mut self, rhs: MyInt): ... + + fn __del__(consume self): ... +``` + +On top of this, we would have `ref`, which would be used whenever you need to +associate an argument or a result with an explicit lifetime. + +Note that argument conventions are not types - they are modifiers to the +behavior of arguments that are local to the function declaration syntax. This +means that all of these keywords can be soft keywords, if we want. + +### Terminology for the `^` sigil + +Along with renaming `owned` to `consume`, it would make sense to simultaneously +rename the `^` sigil, which is currently known as the "transfer operator". + +The current name is problematic for two reasons: + +1. In everyday English, "transfer" and "move" are synonyms. However, + "transferring" and "moving" are completely different concepts in Mojo. + Appending the `^` sigil to an expression does not imply that its move + constructor will be invoked. In fact, it's possible (and common) to transfer + a value whose type is **immovable**. +2. The `^` sigil isn't actually an operator. Or rather, it doesn't correspond to + any _particular_ operation. Instead, it is merely used to indicate that a + value should be consumed, i.e. passed to a function using the `consume` + convention. In the case of `y = x^`, `x` is (typically) passed to + `__moveinit__`, which consumes it, and in the case of a call such as + `foo(x^)`, `x` is being passed to `foo`, which consumes it. Hence, `^` is not + really an operator, it's a way of indicating and/or influencing what + operation gets invoked. The chosen operation (if any) is determined by the + surrounding context. + +So we have problems with both the words "transfer" and "operator". To resolve +this, we suggest that the `^` sigil be referred to as the "consumption sigil". + +- The word "consumption" establishes that each use of `^` is associated with a + function call that uses the `consume` convention. As a fun bonus: the `^` + symbol is usually pronounced "carrot", and carrots 🥕 are consumable. Whatever + name we end up using, we should make sure that it matches the associated + keyword. +- The word "sigil" has precedent in a few languages. It's usually used to refer + to a character that influences how an identifier or expression is interpreted. + In the case of Mojo, we can just quote the dictionary definition: + > _sigil_: an inscribed symbol considered to have magical power + +That's exactly what `^` is. If you inscribe an expression with `^`, its value +will be "magically" consumed. That's the hand-wavey definition. To provide a +formal definition, you would need to talk about move constructors, copy +constructors, temporary variables, etc. From 8cb7793fbfee69572f32c08c78aff481c828321a Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 29 May 2024 09:05:31 -0500 Subject: [PATCH 0745/2019] [External] [stdlib] Add method `_update_with_bytes()` to `_Hasher` (#40797) [External] [stdlib] Add method `_update_with_bytes()` to `_Hasher` This is a follow-up on this comment: https://github.com/modularml/mojo/pull/2823#discussion_r1615226363 With this, the actual Hasher trait is complete and we can start implementing some hashers here and there, and adding the corresponding `__hash__` methods. See https://github.com/modularml/mojo/blob/nightly/proposals/improved-hash-module.md for more information on the subject Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2865 MODULAR_ORIG_COMMIT_REV_ID: 2cb9ecc4ac57598d75a8aeb49c61fb913c138d3a --- stdlib/src/builtin/_hasher.mojo | 5 ++++ stdlib/test/builtin/test_hasher.mojo | 34 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/stdlib/src/builtin/_hasher.mojo b/stdlib/src/builtin/_hasher.mojo index ac9c9e2fd9..4c0ea632a0 100644 --- a/stdlib/src/builtin/_hasher.mojo +++ b/stdlib/src/builtin/_hasher.mojo @@ -21,6 +21,11 @@ trait _Hasher: fn __init__(inout self): ... + fn _update_with_bytes( + inout self, data: DTypePointer[DType.uint8], length: Int + ): + ... + fn _update_with_simd(inout self, value: SIMD[_, _]): ... diff --git a/stdlib/test/builtin/test_hasher.mojo b/stdlib/test/builtin/test_hasher.mojo index 60335f2c50..900e89339c 100644 --- a/stdlib/test/builtin/test_hasher.mojo +++ b/stdlib/test/builtin/test_hasher.mojo @@ -23,6 +23,12 @@ struct DummyHasher(_Hasher): fn __init__(inout self): self._dummy_value = 0 + fn _update_with_bytes( + inout self, data: DTypePointer[DType.uint8], length: Int + ): + for i in range(length): + self._dummy_value += data[i].cast[DType.uint64]() + fn _update_with_simd(inout self, value: SIMD[_, _]): self._dummy_value += value.cast[DType.uint64]().reduce_add() @@ -79,8 +85,36 @@ def test_complexe_hash_with_hasher(): assert_equal(_hash_with_hasher[DummyHasher](hashable), 52) +@value +struct ComplexHashableStructWithList(_HashableWithHasher): + var _value1: SomeHashableStruct + var _value2: SomeHashableStruct + var _value3: List[UInt8] + + fn __hash__[H: _Hasher](self, inout hasher: H): + hasher.update(self._value1) + hasher.update(self._value2) + # This is okay because self is passed as borrowed so the pointer will + # be valid until at least the end of the function + hasher._update_with_bytes( + data=DTypePointer(self._value3.unsafe_ptr()), + length=len(self._value3), + ) + _ = self._value3 + + +def test_update_with_bytes(): + var hasher = DummyHasher() + var hashable = ComplexHashableStructWithList( + SomeHashableStruct(42), SomeHashableStruct(10), List[UInt8](1, 2, 3) + ) + hasher.update(hashable) + assert_equal(hasher^.finish(), 58) + + def main(): test_hasher() test_hash_with_hasher() test_complex_hasher() test_complexe_hash_with_hasher() + test_update_with_bytes() From 40e2b50a32f70925ba04f12188697245740d012c Mon Sep 17 00:00:00 2001 From: Ethan Wu Date: Wed, 29 May 2024 09:06:49 -0500 Subject: [PATCH 0746/2019] [External] [stdlib] Add `String.splitlines` (#40796) [External] [stdlib] Add `String.splitlines` `String` now has a `splitlines()` method, which allows splitting strings at line boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) Co-authored-by: Ethan Wu Closes modularml/mojo#2810 MODULAR_ORIG_COMMIT_REV_ID: 23abce1e515706a9f7de5ceb69145e545ceebcfa --- docs/changelog.md | 5 ++ stdlib/src/builtin/string.mojo | 67 ++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 83 ++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 07441e0461..848c996511 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -330,6 +330,11 @@ what we publish. whitespace, ASCII lower/uppercase, and so on. ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) +- `String` now has a `splitlines()` method, which allows splitting strings at line + boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) + and provides an option to retain or remove the line break characters. + ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) + - `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 30b5ad37d3..5e3ea9562e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -598,6 +598,34 @@ fn _isspace(c: UInt8) -> Bool: return _SPACES_TABLE[int(c)] +# ===----------------------------------------------------------------------=== # +# _isnewline +# ===----------------------------------------------------------------------=== # + + +fn _get_newlines_table() -> InlineArray[UInt8, 128]: + var table = InlineArray[UInt8, 128](0) + table[ord("\n")] = 1 + table[ord("\r")] = 1 + table[ord("\f")] = 1 + table[ord("\v")] = 1 + table[ord("\x1c")] = 1 + table[ord("\x1d")] = 1 + table[ord("\x1e")] = 1 + return table + + +alias _NEWLINES_TABLE = _get_newlines_table() + + +fn _isnewline(c: String) -> Bool: + # TODO: add \u2028 and \u2029 when they are properly parsed + # FIXME: \x85 is parsed but not encoded in utf-8 + if len(c._buffer) == 2: + return c == "\x85" or _NEWLINES_TABLE[ord(c)] + return False + + # ===----------------------------------------------------------------------=== # # isprintable # ===----------------------------------------------------------------------=== # @@ -1580,6 +1608,45 @@ struct String( return output + fn splitlines(self, keepends: Bool = False) -> List[String]: + """Split the string at line boundaries. + + Args: + keepends: If True, line breaks are kept in the resulting strings. + + Returns: + A List of Strings containing the input split by line boundaries. + """ + var output = List[String]() + var length = len(self) + var current_offset = 0 + + while current_offset < length: + var loc = -1 + var eol_length = 1 + + for i in range(current_offset, length): + var char = self[i] + var next_char = self[i + 1] if i + 1 < length else "" + + if _isnewline(char): + loc = i + if char == "\r" and next_char == "\n": + eol_length = 2 + break + else: + output.append(self[current_offset:]) + break + + if keepends: + output.append(self[current_offset : loc + eol_length]) + else: + output.append(self[current_offset:loc]) + + current_offset = loc + eol_length + + return output + fn replace(self, old: String, new: String) -> String: """Return a copy of the string with all occurrences of substring `old` if replaced by `new`. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index cefc9b67b8..79024b29fd 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -683,6 +683,88 @@ fn test_split() raises: assert_equal(res4[1], "o") +fn test_splitlines() raises: + # Test with no line breaks + var in1 = String("hello world") + var res1 = in1.splitlines() + assert_equal(len(res1), 1) + assert_equal(res1[0], "hello world") + + # Test with \n line break + var in2 = String("hello\nworld") + var res2 = in2.splitlines() + assert_equal(len(res2), 2) + assert_equal(res2[0], "hello") + assert_equal(res2[1], "world") + + # Test with \r\n line break + var in3 = String("hello\r\nworld") + var res3 = in3.splitlines() + assert_equal(len(res3), 2) + assert_equal(res3[0], "hello") + assert_equal(res3[1], "world") + + # Test with \r line break + var in4 = String("hello\rworld") + var res4 = in4.splitlines() + assert_equal(len(res4), 2) + assert_equal(res4[0], "hello") + assert_equal(res4[1], "world") + + # Test with multiple different line breaks + var in5 = String("hello\nworld\r\nmojo\rlanguage") + var res5 = in5.splitlines() + assert_equal(len(res5), 4) + assert_equal(res5[0], "hello") + assert_equal(res5[1], "world") + assert_equal(res5[2], "mojo") + assert_equal(res5[3], "language") + + # Test with keepends=True + var res6 = in5.splitlines(keepends=True) + assert_equal(len(res6), 4) + assert_equal(res6[0], "hello\n") + assert_equal(res6[1], "world\r\n") + assert_equal(res6[2], "mojo\r") + assert_equal(res6[3], "language") + + # Test with an empty string + var in7 = String("") + var res7 = in7.splitlines() + assert_equal(len(res7), 0) + + # test \v \f \x1c \x1d + var in8 = String("hello\vworld\fmojo\x1clanguage\x1d") + var res8 = in8.splitlines() + assert_equal(len(res8), 4) + assert_equal(res8[0], "hello") + assert_equal(res8[1], "world") + assert_equal(res8[2], "mojo") + assert_equal(res8[3], "language") + + # test \x1e \x85 + var in9 = String("hello\x1eworld\x85mojo") + var res9 = in9.splitlines() + assert_equal(len(res9), 3) + assert_equal(res9[0], "hello") + assert_equal(res9[1], "world") + assert_equal(res9[2], "mojo") + + # test with keepends=True + var res10 = in8.splitlines(keepends=True) + assert_equal(len(res10), 4) + assert_equal(res10[0], "hello\v") + assert_equal(res10[1], "world\f") + assert_equal(res10[2], "mojo\x1c") + assert_equal(res10[3], "language\x1d") + + var res11 = in9.splitlines(keepends=True) + assert_equal(len(res11), 3) + assert_equal(res11[0], "hello\x1e") + assert_equal(res11[1], "world\x85") + assert_equal(res11[2], "mojo") + + fn test_isupper() raises: assert_true(isupper(ord("A"))) assert_true(isupper(ord("B"))) @@ -1025,6 +1107,7 @@ def main(): test_replace() test_rfind() test_split() + test_splitlines() test_isupper() test_islower() test_lower() From a8fee611ba338ec3fea2ab67c9dec41507ed9128 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 29 May 2024 11:17:03 -0400 Subject: [PATCH 0747/2019] [stdlib] Remove `all_true`, `any_true`, and `none_true` from `math` Because they are trivial now that we have `SIMD.reduce_{and,or}`. MODULAR_ORIG_COMMIT_REV_ID: d7045377d8a8158dbb57edb227b2adddbac86f72 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 848c996511..c3c955087c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -610,6 +610,8 @@ what we publish. `bit.rotate_bits_{left,right}` methods for `Int`. - an overload of `math.pow` taking an integer parameter exponent. - `align_down_residual`; it can be trivially implemented using `align_down`. + - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and + `SIMD.reduce_or` directly. - The `math.bit.select` and `math.bit.bit_and` functions have been removed. The same functionality is available in the builtin `SIMD.select` and From f53632f33b45c0f35516740ce5e2133302d926f0 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 29 May 2024 11:54:28 -0400 Subject: [PATCH 0748/2019] [stdlib] Remove `SIMD._reduce_{all,any}` Only used in one place and trivial. Also, fix some docstrings. MODULAR_ORIG_COMMIT_REV_ID: 9b1a815f8538f02bd731c34d0ee4c980c0fccb50 --- stdlib/src/builtin/bool.mojo | 24 +++++++++++++----------- stdlib/src/builtin/simd.mojo | 19 ------------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 9147c7a1d9..6527fb462d 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -441,7 +441,7 @@ fn bool[T: Boolable](value: T) -> Bool: fn any[T: BoolableCollectionElement](list: List[T]) -> Bool: - """Checks if **any** elements in the list are truthy. + """Checks if **any** element in the list is truthy. Parameters: T: The type of elements to check. @@ -450,7 +450,7 @@ fn any[T: BoolableCollectionElement](list: List[T]) -> Bool: list: The list to check. Returns: - Returns `True` if **any** elements in the list are truthy, `False` otherwise. + `True` if **any** element in the list is truthy, `False` otherwise. """ for item in list: if item[]: @@ -459,7 +459,7 @@ fn any[T: BoolableCollectionElement](list: List[T]) -> Bool: fn any[T: BoolableKeyElement](set: Set[T]) -> Bool: - """Checks if **any** elements in the set are truthy. + """Checks if **any** element in the set is truthy. Parameters: T: The type of elements to check. @@ -468,7 +468,7 @@ fn any[T: BoolableKeyElement](set: Set[T]) -> Bool: set: The set to check. Returns: - Returns `True` if **any** elements in the set are truthy, `False` otherwise. + `True` if **any** element in the set is truthy, `False` otherwise. """ for item in set: if item[]: @@ -477,15 +477,16 @@ fn any[T: BoolableKeyElement](set: Set[T]) -> Bool: fn any(value: SIMD) -> Bool: - """Checks if **any** elements in the simd vector are truthy. + """Checks if **any** element in the simd vector is truthy. Args: value: The simd vector to check. Returns: - Returns `True` if **any** elements in the simd vector are truthy, `False` otherwise. + `True` if **any** element in the simd vector is truthy, `False` + otherwise. """ - return value._reduce_any() + return value.cast[DType.bool]().reduce_or() # ===----------------------------------------------------------------------=== # @@ -506,7 +507,7 @@ fn all[T: BoolableCollectionElement](list: List[T]) -> Bool: list: The list to check. Returns: - Returns `True` if **all** elements in the list are truthy, `False` otherwise. + `True` if **all** elements in the list are truthy, `False` otherwise. """ for item in list: if not item[]: @@ -524,7 +525,7 @@ fn all[T: BoolableKeyElement](set: Set[T]) -> Bool: set: The set to check. Returns: - Returns `True` if **all** elements in the set are truthy, `False` otherwise. + `True` if **all** elements in the set are truthy, `False` otherwise. """ for item in set: if not item[]: @@ -539,6 +540,7 @@ fn all(value: SIMD) -> Bool: value: The simd vector to check. Returns: - Returns `True` if **all** elements in the simd vector are truthy, `False` otherwise. + `True` if **all** elements in the simd vector are truthy, `False` + otherwise. """ - return value._reduce_all() + return value.cast[DType.bool]().reduce_and() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 395b5c2964..9a2cd402d9 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2407,25 +2407,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "llvm.vector.reduce.or", SIMD[type, size_out], has_side_effect=False ](self) - @always_inline - fn _reduce_all(self) -> Bool: - """Returns whether **all** elements in this vector are non-zero. - - Returns: - `True` if and only if **all** elements in this vector are non-zero. - """ - return self.cast[DType.bool]().reduce_and() - - @always_inline - fn _reduce_any(self) -> Bool: - """Returns whether this vector contains **any** non-zero elements. - - Returns: - `True` if this vector contains **any** non-zero elements, `False` - otherwise. - """ - return self.cast[DType.bool]().reduce_or() - # ===------------------------------------------------------------------=== # # select # ===------------------------------------------------------------------=== # From db1b4e7a0dd333e3a47df4f88a0d201fb055dd4c Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 29 May 2024 12:34:11 -0400 Subject: [PATCH 0749/2019] [stdlib] Remove the `Stringable` constructor from `String` We should explicitly use `str(...)` instead of replying on implicit conversion. MODULAR_ORIG_COMMIT_REV_ID: d66cffb4d293b08164467fd863f5ec930e6b8871 --- docs/changelog.md | 3 +++ stdlib/src/builtin/string.mojo | 14 +------------ stdlib/src/tempfile/tempfile.mojo | 6 +++--- stdlib/test/bit/test_bit.mojo | 4 ++-- stdlib/test/builtin/test_string.mojo | 3 ++- stdlib/test/tempfile/test_tempfile.mojo | 26 ++++++++++++------------- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c3c955087c..7748c800b3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -571,6 +571,9 @@ what we publish. method. This makes it explicit that this implementation does not check if the slice bounds are concrete or within any given object's length. +- Implicit conversion to `String` is now removed for builtin classes/types. + One should use `str(...)` explicitly to convert to `String`. + - `math.gcd` now works on negative inputs, and like Python's implementation, accepts a variadic list of integers. New overloads for a `List` or `Span`of integers are also added. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5e3ea9562e..b2e2487432 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -385,7 +385,7 @@ fn _atof(str_ref: StringRef) raises -> Float64: Please see its docstring for details. """ if not str_ref: - raise Error(_atof_error(str_ref)) + raise _atof_error(str_ref) var result: Float64 = 0.0 var exponent: Int = 0 @@ -761,18 +761,6 @@ struct String( """ self = literal.__str__() - fn __init__[stringable: Stringable](inout self, value: stringable): - """Creates a string from a value that conforms to Stringable trait. - - Parameters: - stringable: The Stringable type. - - Args: - value: The value that conforms to Stringable. - """ - - self = str(value) - @always_inline fn __init__(inout self, ptr: UnsafePointer[UInt8], len: Int): """Creates a string from the buffer. Note that the string now owns diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 4025b9ab3b..55225f9271 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -59,7 +59,7 @@ fn _candidate_tempdir_list() -> List[String]: # As a last resort, the current directory if possible, # os.path.getcwd() could raise try: - dirlist.append(Path()) + dirlist.append(str(Path())) except: pass @@ -97,7 +97,7 @@ fn _try_to_create_file(dir: String) -> Bool: # verify that we have writing access in the target directory try: - with FileHandle(filename, "w"): + with FileHandle(str(filename), "w"): pass os.remove(filename) return True @@ -160,7 +160,7 @@ fn mkdtemp( # python implementation expands the path, # but several functions are not yet implemented in mojo # i.e. abspath, normpath - return dir_name + return str(dir_name) except: continue raise Error("Failed to create temporary file") diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 0a4f87166b..a0c783e659 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -42,13 +42,13 @@ def test_has_single_bit_simd(): alias var1 = SIMD[type, simd_width](-1, 0, 1, 2) assert_equal( has_single_bit(var1), - SIMD[return_type, simd_width](False, False, True, True), + SIMD[DType.bool, simd_width](False, False, True, True), ) alias var2 = SIMD[type, simd_width](3, 4, 5, 8) assert_equal( has_single_bit(var2), - SIMD[return_type, simd_width](False, True, False, True), + SIMD[DType.bool, simd_width](False, True, False, True), ) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 79024b29fd..a166c51cf1 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -605,7 +605,8 @@ fn test_split() raises: # empty separators default to whitespace var d = String("hello world").split() assert_true(len(d) == 2) - assert_true(d[0] == "hello", d[1] == "world") + assert_true(d[0] == "hello") + assert_true(d[1] == "world") d = String("hello \t\n\n\v\fworld").split("\n") assert_true(len(d) == 3) assert_true(d[0] == "hello \t" and d[1] == "" and d[2] == "\v\fworld") diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index ee9713e41d..34079e3583 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -104,7 +104,7 @@ fn test_gettempdir() raises: var non_existing_dir = Path() / "non_existing_dir" assert_false( exists(non_existing_dir), - "Unexpected dir" + String(non_existing_dir), + "Unexpected dir" + str(non_existing_dir), ) var dir_without_writing_access = Path() / "dir_without_writing_access" var dir_with_writing_access = Path() / "dir_with_writing_access" @@ -114,7 +114,7 @@ fn test_gettempdir() raises: var vars_to_set = Dict[String, String]() # test TMPDIR is used first - vars_to_set["TMPDIR"] = dir_with_writing_access + vars_to_set["TMPDIR"] = str(dir_with_writing_access) with TempEnvWithCleanup( vars_to_set, _clean_up_gettempdir_test, @@ -123,13 +123,13 @@ fn test_gettempdir() raises: assert_true(tmpdir_result, "Failed to get temporary directory") assert_equal( tmpdir_result.value()[], - dir_with_writing_access, - "expected to get:" + String(dir_with_writing_access), + str(dir_with_writing_access), + "expected to get:" + str(dir_with_writing_access), ) # test gettempdir falls back to TEMP - vars_to_set["TMPDIR"] = non_existing_dir - vars_to_set["TEMP"] = dir_with_writing_access + vars_to_set["TMPDIR"] = str(non_existing_dir) + vars_to_set["TEMP"] = str(dir_with_writing_access) with TempEnvWithCleanup( vars_to_set, _clean_up_gettempdir_test, @@ -138,14 +138,14 @@ fn test_gettempdir() raises: assert_true(tmpdir_result, "Failed to get temporary directory") assert_equal( tmpdir_result.value()[], - dir_with_writing_access, - "expected to get:" + String(dir_with_writing_access), + str(dir_with_writing_access), + "expected to get:" + str(dir_with_writing_access), ) # test gettempdir falls back to TMP - vars_to_set["TMPDIR"] = non_existing_dir - vars_to_set["TEMP"] = non_existing_dir - vars_to_set["TMP"] = dir_with_writing_access + vars_to_set["TMPDIR"] = str(non_existing_dir) + vars_to_set["TEMP"] = str(non_existing_dir) + vars_to_set["TMP"] = str(dir_with_writing_access) with TempEnvWithCleanup( vars_to_set, _clean_up_gettempdir_test, @@ -154,8 +154,8 @@ fn test_gettempdir() raises: assert_true(tmpdir_result, "Failed to get temporary directory") assert_equal( tmpdir_result.value()[], - dir_with_writing_access, - "expected to get:" + String(dir_with_writing_access), + str(dir_with_writing_access), + "expected to get:" + str(dir_with_writing_access), ) _clean_up_gettempdir_test() From 21963b550873a81f0e70ade472be1d923d7047db Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 29 May 2024 09:42:27 -0700 Subject: [PATCH 0750/2019] [stdlib] Adding String.find unit tests Adding unit tests for String.find. MODULAR_ORIG_COMMIT_REV_ID: 0e2ed25a11729fccf7e0cda2e2add0c9da5000c1 --- stdlib/test/utils/test_stringref.mojo | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/stdlib/test/utils/test_stringref.mojo b/stdlib/test/utils/test_stringref.mojo index c8859069ee..deedbd78c7 100644 --- a/stdlib/test/utils/test_stringref.mojo +++ b/stdlib/test/utils/test_stringref.mojo @@ -82,8 +82,35 @@ def test_indexing(): assert_equal(a[0], "a") +def test_find(): + haystack = StringRef("abcdefg") + haystack_with_special_chars = StringRef("abcdefg@#$") + haystack_repeated_chars = StringRef("aaaaaaaaaaaaaaaaaaaaaaaa") + + assert_equal(haystack.find("a"), 0) + assert_equal(haystack.find("ab"), 0) + assert_equal(haystack.find("abc"), 0) + assert_equal(haystack.find("bcd"), 1) + assert_equal(haystack.find("de"), 3) + assert_equal(haystack.find("fg"), 5) + assert_equal(haystack.find("g"), 6) + assert_equal(haystack.find("z"), -1) + assert_equal(haystack.find("zzz"), -1) + + assert_equal(haystack.find("@#$"), -1) + assert_equal(haystack_with_special_chars.find("@#$"), 7) + + assert_equal(haystack_repeated_chars.find("aaa"), 0) + assert_equal(haystack_repeated_chars.find("AAa"), -1) + + assert_equal(haystack.find("hijklmnopqrstuvwxyz"), -1) + + assert_equal(StringRef("").find("abc"), -1) + + def main(): test_strref_from_start() test_comparison_operators() test_intable() test_indexing() + test_find() From 55dc5157898f9edbd229e381e75eccf578620f50 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 29 May 2024 13:28:30 -0400 Subject: [PATCH 0751/2019] [stdlib] Remove `math.reduce_bit_count` in favor of new `SIMD.reduce_bit_count` Since that's where the other similar reduction functions are for SIMD. MODULAR_ORIG_COMMIT_REV_ID: 53ff3ccc496c6ca847471b8e6600f651bd0b96a0 --- docs/changelog.md | 1 + stdlib/src/builtin/simd.mojo | 22 +++++++++++++++++++++- stdlib/test/builtin/test_simd.mojo | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7748c800b3..b3e7dfa6fb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -615,6 +615,7 @@ what we publish. - `align_down_residual`; it can be trivially implemented using `align_down`. - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and `SIMD.reduce_or` directly. + - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. - The `math.bit.select` and `math.bit.bit_and` functions have been removed. The same functionality is available in the builtin `SIMD.select` and diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9a2cd402d9..aef73379de 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ - +from bit import pop_count from sys import ( llvm_intrinsic, has_neon, @@ -2407,6 +2407,26 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "llvm.vector.reduce.or", SIMD[type, size_out], has_side_effect=False ](self) + @always_inline + fn reduce_bit_count(self) -> Int: + """Returns the total number of bits set in the SIMD vector. + + Constraints: + Must be either an integral or a boolean type. + + Returns: + Count of set bits across all elements of the vector. + """ + + @parameter + if type.is_bool(): + return int(self.cast[DType.uint8]().reduce_add()) + else: + constrained[ + type.is_integral(), "Expected either integral or bool type" + ]() + return int(pop_count(self).reduce_add()) + # ===------------------------------------------------------------------=== # # select # ===------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 344a54d994..b4aa7afded 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1341,6 +1341,23 @@ def test_reduce(): test_dtype[DType.bfloat16]() +def test_reduce_bit_count(): + var int_0xFFFF = Int32(0xFFFF) + assert_equal(int_0xFFFF.reduce_bit_count(), 16) + + var int_iota8 = SIMD[DType.int32, 8](0, 1, 2, 3, 4, 5, 6, 7) + assert_equal(int_iota8.reduce_bit_count(), 12) + + var bool_true = Scalar[DType.bool].splat(True) + assert_equal(bool_true.reduce_bit_count(), 1) + + var bool_false = Scalar[DType.bool].splat(False) + assert_equal(bool_false.reduce_bit_count(), 0) + + var bool_true16 = SIMD[DType.bool, 16].splat(True) + assert_equal(bool_true16.reduce_bit_count(), 16) + + def test_pow(): alias inf = FloatLiteral.infinity alias F = SIMD[DType.float32, 4] @@ -1392,6 +1409,7 @@ def main(): test_pow() test_radd() test_reduce() + test_reduce_bit_count() test_rfloordiv() test_rmod() test_rotate() From e8901ad5002a02904e9eff9157b7dea7c148e462 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 29 May 2024 13:43:49 -0700 Subject: [PATCH 0752/2019] [stdlib] alternative SIMD optimized find algorithm The debug performance has improved 3x with this change. Reference implementation available [Internal link] in C++. MODULAR_ORIG_COMMIT_REV_ID: 85e2cb14637e7df5c67f6dd18aab445fa21916f6 --- stdlib/src/utils/stringref.mojo | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 505a712d40..efb997d5da 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -668,25 +668,40 @@ fn _memmem[ return _memchr[type](haystack, needle[0], haystack_len) alias bool_mask_width = simdwidthof[DType.bool]() - var first_needle = SIMD[type, bool_mask_width](needle[0]) var vectorized_end = _align_down( haystack_len - needle_len + 1, bool_mask_width ) + + var first_needle = SIMD[type, bool_mask_width](needle[0]) + var last_needle = SIMD[type, bool_mask_width](needle[needle_len - 1]) + for i in range(0, vectorized_end, bool_mask_width): - var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle + var first_block = haystack.load[width=bool_mask_width](i) + var last_block = haystack.load[width=bool_mask_width]( + i + needle_len - 1 + ) + + var eq_first = first_needle == first_block + var eq_last = last_needle == last_block + + var bool_mask = eq_first & eq_last var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) + while mask: var offset = i + countr_zero(mask) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) + # remaining partial block compare using byte-by-byte + # for i in range(vectorized_end, haystack_len - needle_len + 1): if haystack[i] != needle[0]: continue if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: return haystack + i + return DTypePointer[type]() From 75a4b7ea944ed1802e5f76250d27646741184f36 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 29 May 2024 15:53:43 -0500 Subject: [PATCH 0753/2019] [External] [stdlib] Fix `_isspace` use at compile time in licence checker (#40837) [External] [stdlib] Fix `_isspace` use at compile time in licence checker Co-authored-by: Laszlo Kindrat Closes modularml/mojo#2883 MODULAR_ORIG_COMMIT_REV_ID: dc651937f29b565d831fd4c8f5fc7fc7bc19c6b6 --- stdlib/scripts/check-licenses.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/scripts/check-licenses.mojo b/stdlib/scripts/check-licenses.mojo index cc0d5126e4..0b60a0f0dd 100644 --- a/stdlib/scripts/check-licenses.mojo +++ b/stdlib/scripts/check-licenses.mojo @@ -16,7 +16,7 @@ from pathlib import Path # We can't check much more than this at the moment, because the license year # changes and the language is not mature enough to do regex yet. -alias LICENSE = String( +var LICENSE = String( """ # ===----------------------------------------------------------------------=== # # Copyright (c) From 62df108dd6d2598626e55e8c40c587144f72e9b4 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 29 May 2024 17:15:21 -0400 Subject: [PATCH 0754/2019] [stdlib] Don't use LUT for `string._isspace` Because global LUT-based function cannot be used at compile time. Also, this happens to compile magically to something very efficient. MODULAR_ORIG_COMMIT_REV_ID: ab11ffe7a50e73d32ecea78dbfcaa89fbd66cae7 --- stdlib/src/builtin/string.mojo | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index b2e2487432..d56a8ffc7a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -568,20 +568,6 @@ fn _is_ascii_lowercase(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # -fn _get_spaces_table() -> InlineArray[UInt8, 256]: - var table = InlineArray[UInt8, 256](0) - table[ord(" ")] = 1 - table[ord("\t")] = 1 - table[ord("\n")] = 1 - table[ord("\r")] = 1 - table[ord("\f")] = 1 - table[ord("\v")] = 1 - return table - - -alias _SPACES_TABLE = _get_spaces_table() - - fn _isspace(c: UInt8) -> Bool: """Determines whether the given character is a whitespace character. @@ -595,7 +581,23 @@ fn _isspace(c: UInt8) -> Bool: Returns: True iff the character is one of the whitespace characters listed above. """ - return _SPACES_TABLE[int(c)] + + alias ` ` = UInt8(ord(" ")) + alias `\t` = UInt8(ord("\t")) + alias `\n` = UInt8(ord("\n")) + alias `\r` = UInt8(ord("\r")) + alias `\f` = UInt8(ord("\f")) + alias `\v` = UInt8(ord("\v")) + + # This compiles to something very clever that's even faster than a LUT. + return ( + c == ` ` + or c == `\t` + or c == `\n` + or c == `\r` + or c == `\f` + or c == `\v` + ) # ===----------------------------------------------------------------------=== # From 4fe7ede354add86f63230cfe8e7ac634981c8f58 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 29 May 2024 17:16:29 -0400 Subject: [PATCH 0755/2019] [stdlib] Inline some hidden methods in `utils.numerics` Users should just call equivalent methods in `FPUtils`. Also clean up a bunch of things in `FPUtils`. MODULAR_ORIG_COMMIT_REV_ID: 6027e30c656939d8cc00eae691048ee4f73e736b --- stdlib/src/utils/numerics.mojo | 165 +++++++-------------------------- 1 file changed, 34 insertions(+), 131 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index e153f0075a..83743693c8 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -27,97 +27,6 @@ from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply from memory import UnsafePointer, bitcast -# ===----------------------------------------------------------------------=== # -# _digits -# ===----------------------------------------------------------------------=== # - - -@always_inline("nodebug") -fn _digits[type: DType]() -> IntLiteral: - """Returns the number of digits in base-radix that can be represented by - the type without change. - - For integer types, this is the number of bits not counting the sign bit and - the padding bits (if any). For floating-point types, this is the digits of - the mantissa (for IEC 559/IEEE 754 implementations, this is the number of - digits stored for the mantissa plus one, because the mantissa has an - implicit leading 1 and binary point). - - Parameters: - type: The type to get the digits for. - - Returns: - The number of digits that can be represented by the type without change. - """ - alias mlir_type = __mlir_type[`!pop.scalar<`, type.value, `>`] - - @parameter - if type == DType.bool: - return 1 - elif type.is_integral(): - - @parameter - if type.is_signed(): - return bitwidthof[mlir_type]() - 1 - else: - return bitwidthof[mlir_type]() - elif type == DType.float16: - return 11 - elif type == DType.bfloat16: - return 8 - elif type == DType.float32: - return 24 - elif type == DType.float64: - return 53 - else: - constrained[False, "unsupported DType"]() - return -1 - - -# ===----------------------------------------------------------------------=== # -# _fp_bitcast_to_integer -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _fp_bitcast_to_integer[type: DType](value: Scalar[type]) -> Int: - """Bitcasts the floating-point value to an integer. - - Parameters: - type: The floating-point type. - - Args: - value: The value to bitcast. - - Returns: - An integer representation of the floating-point value. - """ - alias integer_type = _integral_type_of[type]() - return int(bitcast[integer_type, 1](value)) - - -# ===----------------------------------------------------------------------=== # -# _fp_bitcast_from_integer -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _fp_bitcast_from_integer[type: DType](value: Int) -> Scalar[type]: - """Bitcasts the integer value to a floating-point value. - - Parameters: - type: The floating-point type. - - Args: - value: The value to bitcast. - - Returns: - A float-point representation of the integer value. - """ - alias integer_type = _integral_type_of[type]() - var int_val = SIMD[integer_type, 1](value) - return bitcast[type, 1](int_val) - # ===----------------------------------------------------------------------=== # # FPUtils @@ -149,7 +58,17 @@ struct FPUtils[type: DType]: type.is_floating_point(), "dtype must be a floating point type", ]() - return _digits[type]() - 1 + + @parameter + if type == DType.float16: + return 10 + elif type == DType.bfloat16: + return 7 + elif type == DType.float32: + return 23 + else: + constrained[type == DType.float64, "unsupported DType"]() + return 52 @staticmethod @always_inline("nodebug") @@ -170,7 +89,7 @@ struct FPUtils[type: DType]: elif type == DType.float32 or type == DType.bfloat16: return 128 else: - debug_assert(type == DType.float64, "must be float64") + constrained[type == DType.float64, "unsupported DType"]() return 1024 @staticmethod @@ -192,7 +111,7 @@ struct FPUtils[type: DType]: elif type == DType.float32 or type == DType.bfloat16: return 8 else: - debug_assert(type == DType.float64, "must be float64") + constrained[type == DType.float64, "unsupported DType"]() return 11 @staticmethod @@ -203,10 +122,6 @@ struct FPUtils[type: DType]: Returns: The mantissa mask. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() return (1 << Self.mantissa_width()) - 1 @staticmethod @@ -217,56 +132,42 @@ struct FPUtils[type: DType]: Returns: The exponent bias. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() return Self.max_exponent() - 1 @staticmethod @always_inline fn sign_mask() -> Int: - """Returns the sign mask of a floating point type. It is computed by - `1 << (exponent_width + mantissa_mask)`. + """Returns the sign mask of a floating point type. + + It is computed by `1 << (exponent_width + mantissa_width)`. Returns: The sign mask. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() - alias shift = int(Self.exponent_width() + Self.mantissa_width()) - return 1 << shift + return 1 << int(Self.exponent_width() + Self.mantissa_width()) @staticmethod @always_inline fn exponent_mask() -> Int: - """Returns the exponent mask of a floating point type. It is computed by - `~(sign_mask | mantissa_mask)`. + """Returns the exponent mask of a floating point type. + + It is computed by `~(sign_mask | mantissa_mask)`. Returns: The exponent mask. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() return ~(Self.sign_mask() | Self.mantissa_mask()) @staticmethod @always_inline fn exponent_mantissa_mask() -> Int: - """Returns the exponent and mantissa mask of a floating point type. It is - computed by `exponent_mask + mantissa_mask`. + """Returns the exponent and mantissa mask of a floating point type. + + It is computed by `exponent_mask + mantissa_mask`. Returns: The exponent and mantissa mask. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() return Self.exponent_mask() + Self.mantissa_mask() @staticmethod @@ -283,10 +184,6 @@ struct FPUtils[type: DType]: Returns: The quiet NaN mask. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() alias mantissa_width_val = Self.mantissa_width() return (1 << Self.exponent_width() - 1) << mantissa_width_val + ( 1 << (mantissa_width_val - 1) @@ -303,7 +200,11 @@ struct FPUtils[type: DType]: Returns: An integer representation of the floating-point value. """ - return _fp_bitcast_to_integer[type](value) + constrained[ + type.is_floating_point(), + "dtype must be a floating point type", + ]() + return int(bitcast[Self.integral_type, 1](value)) @staticmethod @always_inline @@ -316,13 +217,16 @@ struct FPUtils[type: DType]: Returns: An floating-point representation of the Int. """ - return _fp_bitcast_from_integer[type](value) + constrained[ + type.is_floating_point(), + "dtype must be a floating point type", + ]() + return bitcast[type, 1](SIMD[Self.integral_type, 1](value)) @staticmethod @always_inline fn get_sign(value: Scalar[type]) -> Bool: - """Returns the sign of the floating point value. True if the sign is set - and False otherwise. + """Returns the sign of the floating point value. Args: value: The floating-point type. @@ -330,7 +234,6 @@ struct FPUtils[type: DType]: Returns: Returns True if the sign is set and False otherwise. """ - return (Self.bitcast_to_integer(value) & Self.sign_mask()) != 0 @staticmethod From 13da26179b0387f2f6469429773e8f8da42225c4 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 29 May 2024 16:18:25 -0500 Subject: [PATCH 0756/2019] [stdlib] feature: Add new ExplicitlyCopyable trait This is a small first step towards removing the `Copyable` bounds from the standard library collection types. MODULAR_ORIG_COMMIT_REV_ID: 1aaf5dc9c891fb56ee81af7bdf8a5352e97cdbf1 --- stdlib/src/builtin/value.mojo | 51 +++++++++++++++++++++++++++++ stdlib/src/utils/variant.mojo | 32 ++++++++++++++---- stdlib/test/utils/test_variant.mojo | 29 +++++++++++----- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 45145fc359..03137b5a64 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -99,6 +99,57 @@ trait Copyable: ... +trait ExplicitlyCopyable: + """The ExplicitlyCopyable trait denotes a type whose value can be copied + explicitly. + + Unlike `Copyable`, which denotes types that are _implicitly_ copyable, an + explicitly copyable type can only be copied when the explicit copy + initializer is called intentionally by the programmer. + + An explicit copy initializer is just a normal `__init__` method that takes + a `borrowed` argument of `Self`. + + Example implementing the `ExplicitlyCopyable` trait on `Foo` which requires + the `__init__(.., Self)` method: + + ```mojo + struct Foo(ExplicitlyCopyable): + var s: String + + fn __init__(inout self, s: String): + self.s = s + + fn __init__(inout self, copy: Self): + print("explicitly copying value") + self.s = copy.s + ``` + + You can now copy objects inside a generic function: + + ```mojo + fn copy_return[T: ExplicitlyCopyable](foo: T) -> T: + var copy = T(foo) + return copy + + var foo = Foo("test") + var res = copy_return(foo) + ``` + + ```plaintext + explicitly copying value + ``` + """ + + fn __init__(inout self, other: Self): + """Construct a deep copy of the provided value. + + Args: + other: The value to copy. + """ + ... + + trait Defaultable: """The `Defaultable` trait describes a type with a default constructor. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 5a0eb89432..a32f290028 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -97,8 +97,10 @@ struct _UnionTypeIndex[T: CollectionElement, *Ts: CollectionElement]: return result -@value -struct Variant[*Ts: CollectionElement](CollectionElement): +struct Variant[*Ts: CollectionElement]( + CollectionElement, + ExplicitlyCopyable, +): """A runtime-variant type. Data for this type is stored internally. Currently, its size is the @@ -150,6 +152,14 @@ struct Variant[*Ts: CollectionElement](CollectionElement): # Life cycle methods # ===-------------------------------------------------------------------===# + fn __init__(inout self, *, unsafe_uninitialized: ()): + """Unsafely create an uninitialized Variant. + + Args: + unsafe_uninitialized: Marker argument indicating this initializer is unsafe. + """ + self._impl = __mlir_attr[`#kgen.unknown : `, Self._mlir_type] + fn __init__[T: CollectionElement](inout self, owned value: T): """Create a variant with one of the types. @@ -164,13 +174,13 @@ struct Variant[*Ts: CollectionElement](CollectionElement): self._get_state()[] = Self._check[T]() initialize_pointee_move(self._get_ptr[T](), value^) - fn __copyinit__(inout self, other: Self): - """Creates a deep copy of an existing variant. + fn __init__(inout self, other: Self): + """Explicitly creates a deep copy of an existing variant. Args: - other: The variant to copy from. + other: The value to copy from. """ - self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] + self = Self(unsafe_uninitialized=()) self._get_state()[] = other._get_state()[] @parameter @@ -184,6 +194,16 @@ struct Variant[*Ts: CollectionElement](CollectionElement): unroll[each, len(VariadicList(Ts))]() + fn __copyinit__(inout self, other: Self): + """Creates a deep copy of an existing variant. + + Args: + other: The variant to copy from. + """ + + # Delegate to explicit copy initializer. + self = Self(other=other) + fn __moveinit__(inout self, owned other: Self): """Move initializer for the variant. diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index cf78f1edfc..db834cffcb 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -103,10 +103,23 @@ def test_basic(): def test_copy(): var v1 = TestVariant(TestCounter()) var v2 = v1 - assert_true( - v2[TestCounter].copied > v1[TestCounter].copied, - "didn't call copyinit", - ) + # didn't call copyinit + assert_equal(v1[TestCounter].copied, 0) + assert_equal(v2[TestCounter].copied, 1) + # test that we didn't call the other copyinit too! + assert_no_poison() + + +def test_explicit_copy(): + var v1 = TestVariant(TestCounter()) + + # Perform explicit copy + var v2 = TestVariant(other=v1) + + # Test copy counts + assert_equal(v1[TestCounter].copied, 0) + assert_equal(v2[TestCounter].copied, 1) + # test that we didn't call the other copyinit too! assert_no_poison() @@ -114,10 +127,9 @@ def test_copy(): def test_move(): var v1 = TestVariant(TestCounter()) var v2 = v1 - assert_true( - v2[TestCounter].moved > v1[TestCounter].moved, - "didn't call moveinit", - ) + # didn't call moveinit + assert_equal(v1[TestCounter].moved, 1) + assert_equal(v2[TestCounter].moved, 2) # test that we didn't call the other moveinit too! assert_no_poison() @@ -198,6 +210,7 @@ def main(): test_basic() test_get_returns_mutable_reference() test_copy() + test_explicit_copy() test_move() test_del() test_take_doesnt_call_deleter() From 4082421baeb0c451590922a874169baed4aee7c5 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 29 May 2024 18:23:30 -0700 Subject: [PATCH 0757/2019] [mojo-lang] Place the union of argument lifetimes on coroutines This PR implements the lifetime piece of the async frontend work. This gives `Coroutine` an extra lifetime parameter. When calling an async function, the union of all argument lifetimes and all argument lifetime accesses (the same ones used by check lifetimes) is placed on the coroutine. This means that they are kept alive as long as the coroutine is used. MODULAR_ORIG_COMMIT_REV_ID: 4ef0a38f42784e69dad73ce19c3d9bd82b0d4483 --- docs/changelog.md | 6 ++++++ stdlib/src/builtin/coroutine.mojo | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b3e7dfa6fb..92731edd8d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -472,6 +472,12 @@ what we publish. ### 🦋 Changed +- `Coroutine` now requires a lifetime parameter. This parameter is set + automatically by the parser when calling an async function. It contains the + lifetimes of all the arguments and any lifetime accesses by the arguments. + This ensures that argument captures by async functions keep the arguments + alive as long as the coroutine is alive. + - Async function calls are no longer allowed to borrow non-trivial register-passable types. Because async functions capture their arguments but register-passable types don't have lifetimes (yet), Mojo is not able to diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 5262b3ae7c..acb2edfd6e 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -81,7 +81,11 @@ fn _coro_resume_noop_callback(handle: AnyCoroutine, null: AnyCoroutine): @register_passable -struct Coroutine[type: AnyTrivialRegType]: +struct Coroutine[ + is_mut: Bool, //, + type: AnyTrivialRegType, + lifetime: AnyLifetime[is_mut].type, +]: """Represents a coroutine. Coroutines can pause execution saving the state of the program (including @@ -90,7 +94,9 @@ struct Coroutine[type: AnyTrivialRegType]: left off, with the saved state restored. Parameters: + is_mut: Whether the lifetime is mutable. type: Type of value returned upon completion of the coroutine. + lifetime: The lifetime of the coroutine's captures. """ var _handle: AnyCoroutine @@ -123,7 +129,7 @@ struct Coroutine[type: AnyTrivialRegType]: return __mlir_op.`co.get_results`[_type=type](self._handle) @always_inline - fn __init__(handle: AnyCoroutine) -> Coroutine[type]: + fn __init__(handle: AnyCoroutine) -> Self: """Construct a coroutine object from a handle. Args: @@ -178,7 +184,11 @@ struct Coroutine[type: AnyTrivialRegType]: @register_passable -struct RaisingCoroutine[type: AnyTrivialRegType]: +struct RaisingCoroutine[ + is_mut: Bool, //, + type: AnyTrivialRegType, + lifetime: AnyLifetime[is_mut].type, +]: """Represents a coroutine that can raise. Coroutines can pause execution saving the state of the program (including @@ -187,7 +197,9 @@ struct RaisingCoroutine[type: AnyTrivialRegType]: left off, with the saved state restored. Parameters: + is_mut: Whether the lifetime is mutable. type: Type of value returned upon completion of the coroutine. + lifetime: The lifetime of the coroutine's captures. """ alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] From 722e8db630a59945d793de49a839ab36472849b4 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 29 May 2024 20:34:10 -0600 Subject: [PATCH 0758/2019] [stdlib] Rename `has_single_bit` to `is_power_of_two` The C++ function identifier isn't great. Let's stick with the identifier `is_power_of_two` instead. MODULAR_ORIG_COMMIT_REV_ID: 97a458ac7d0337480f5d189eadc75284ef8fa233 --- stdlib/src/bit/__init__.mojo | 2 +- stdlib/src/bit/bit.mojo | 8 ++++---- stdlib/test/bit/test_bit.mojo | 28 ++++++++++++++-------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index 4273d9db83..f249493069 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -22,7 +22,7 @@ from .bit import ( bit_width, rotate_bits_left, rotate_bits_right, - has_single_bit, + is_power_of_two, bit_ceil, bit_floor, ) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 37a79685cf..cc5e8a6e27 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -303,13 +303,13 @@ fn bit_width[ # ===----------------------------------------------------------------------===# -# has_single_bit +# is_power_of_two # ===----------------------------------------------------------------------===# # reference: https://en.cppreference.com/w/cpp/numeric/has_single_bit @always_inline -fn has_single_bit(val: Int) -> Bool: +fn is_power_of_two(val: Int) -> Bool: """Checks if the input value is a power of 2. Args: @@ -322,7 +322,7 @@ fn has_single_bit(val: Int) -> Bool: @always_inline -fn has_single_bit[ +fn is_power_of_two[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[DType.bool, simd_width]: """Checks if the input value is a power of 2 for each element of a SIMD vector. @@ -366,7 +366,7 @@ fn bit_ceil(val: Int) -> Int: if val <= 1: return 1 - if has_single_bit(val): + if is_power_of_two(val): return val return 1 << bit_width(val - 1) diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index a0c783e659..72b95563f7 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -18,36 +18,36 @@ from bit import ( bit_width, bit_ceil, bit_floor, - has_single_bit, + is_power_of_two, ) from testing import assert_equal -def test_has_single_bit(): - assert_equal(has_single_bit(-1), False) - assert_equal(has_single_bit(0), False) - assert_equal(has_single_bit(1), True) - assert_equal(has_single_bit(2), True) - assert_equal(has_single_bit(3), False) - assert_equal(has_single_bit(4), True) - assert_equal(has_single_bit(5), False) +def test_is_power_of_two(): + assert_equal(is_power_of_two(-1), False) + assert_equal(is_power_of_two(0), False) + assert_equal(is_power_of_two(1), True) + assert_equal(is_power_of_two(2), True) + assert_equal(is_power_of_two(3), False) + assert_equal(is_power_of_two(4), True) + assert_equal(is_power_of_two(5), False) -def test_has_single_bit_simd(): +def test_is_power_of_two_simd(): alias simd_width = 4 alias type = DType.int8 alias return_type = DType.bool alias var1 = SIMD[type, simd_width](-1, 0, 1, 2) assert_equal( - has_single_bit(var1), + is_power_of_two(var1), SIMD[DType.bool, simd_width](False, False, True, True), ) alias var2 = SIMD[type, simd_width](3, 4, 5, 8) assert_equal( - has_single_bit(var2), + is_power_of_two(var2), SIMD[DType.bool, simd_width](False, True, False, True), ) @@ -165,5 +165,5 @@ def main(): test_bit_floor_simd() test_bit_width() test_bit_width_simd() - test_has_single_bit() - test_has_single_bit_simd() + test_is_power_of_two() + test_is_power_of_two_simd() From b19cc63587e562d4543bb657692b8ce8ee22b4cd Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 29 May 2024 21:22:13 -0600 Subject: [PATCH 0759/2019] [stdlib] Remove private `is_power_of_2` function Use the `is_power_of_two` function from `bit` module instead. MODULAR_ORIG_COMMIT_REV_ID: 8ab6185f3205d643bbe4a39482749b59c189dd6b --- stdlib/src/memory/unsafe.mojo | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index bc0348e777..6ed8e7e696 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -30,28 +30,11 @@ from sys import ( from sys.intrinsics import PrefetchOptions, _mlirtype_is_eq from sys.intrinsics import prefetch as _prefetch from sys.intrinsics import gather, scatter, strided_load, strided_store +from bit import is_power_of_two from .memory import _free, _malloc from .reference import AddressSpace -# ===----------------------------------------------------------------------===# -# Utilities -# ===----------------------------------------------------------------------===# - - -@always_inline -fn _is_power_of_2(val: Int) -> Bool: - """Checks whether an integer is a power of two. - - Args: - val: The integer to check. - - Returns: - True if val is a power of two, otherwise False. - """ - return (val & (val - 1) == 0) & (val != 0) - - # ===----------------------------------------------------------------------===# # bitcast # ===----------------------------------------------------------------------===# @@ -1110,7 +1093,7 @@ struct DTypePointer[ "offset type must be an integral type", ]() constrained[ - _is_power_of_2(alignment), + is_power_of_two(alignment), "alignment must be a power of two integer value", ]() @@ -1188,7 +1171,7 @@ struct DTypePointer[ "offset type must be an integral type", ]() constrained[ - _is_power_of_2(alignment), + is_power_of_two(alignment), "alignment must be a power of two integer value", ]() @@ -1216,7 +1199,7 @@ struct DTypePointer[ otherwise. """ constrained[ - _is_power_of_2(alignment), "alignment must be a power of 2." + is_power_of_two(alignment), "alignment must be a power of 2." ]() return int(self) % alignment == 0 From 4c97235f91a7459c1447be0e8929fb0eded45bcf Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 29 May 2024 21:50:57 -0600 Subject: [PATCH 0760/2019] [stdlib] Use `!= 0` instead of `> 0` in `is_power_of_two` Instead of comparing the value to `> 0`, use `!= 0` in `bit.is_power_of_two` which is a little bit easier to reason about. MODULAR_ORIG_COMMIT_REV_ID: 0c465b6ad612d02d83bdcda8d9b1b4aa93e280ea --- stdlib/src/bit/bit.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index cc5e8a6e27..c7bfd8ee45 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -318,7 +318,7 @@ fn is_power_of_two(val: Int) -> Bool: Returns: True if the input value is a power of 2, False otherwise. """ - return val > 0 and not (val & (val - 1)) + return (val != 0) & (val & (val - 1) == 0) @always_inline @@ -343,7 +343,7 @@ fn is_power_of_two[ """ constrained[type.is_integral(), "must be integral"]() - return (val > 0) & ((val & (val - 1)) == 0) + return (val != 0) & (val & (val - 1) == 0) # ===----------------------------------------------------------------------===# From b8ed0d2646e9d807de3874446821a6f628d092e8 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 29 May 2024 22:14:13 -0700 Subject: [PATCH 0761/2019] [mojo-stdlib] Replace pointer.get_null() methods with default ctor All our pointers can be default constructed to get a null pointer, so we don't need a redundant static `get_null()` method. Remove the later in favor of the former. While here, rename `DTypePointer._mlir_type` to `_pointer_type` (since it isn't an mlir type) MODULAR_ORIG_COMMIT_REV_ID: 9becd057602e1d77e29d1d81577295e8d7949d1a --- stdlib/src/memory/unsafe.mojo | 34 +++++------------------- stdlib/src/memory/unsafe_pointer.mojo | 16 +++-------- stdlib/src/python/_cpython.mojo | 17 +++++------- stdlib/src/python/object.mojo | 8 +++--- stdlib/src/sys/ffi.mojo | 2 +- stdlib/src/time/time.mojo | 2 +- stdlib/test/sys/test_windows_target.mojo | 4 +-- 7 files changed, 24 insertions(+), 59 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 6ed8e7e696..dc592858f3 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -173,7 +173,7 @@ struct LegacyPointer[ Returns: Constructed LegacyPointer object. """ - return Self.get_null() + return __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] @always_inline("nodebug") fn __init__(address: Self._mlir_type) -> Self: @@ -216,16 +216,6 @@ struct LegacyPointer[ Scalar[DType.index](address).value ) - @staticmethod - @always_inline("nodebug") - fn get_null() -> Self: - """Constructs a LegacyPointer representing nullptr. - - Returns: - Constructed nullptr LegacyPointer object. - """ - return __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] - fn __str__(self) -> String: """Format this pointer as a hexadecimal string. @@ -242,7 +232,7 @@ struct LegacyPointer[ Returns: Returns False if the LegacyPointer is null and True otherwise. """ - return self != Self.get_null() + return self != Self() @staticmethod @always_inline("nodebug") @@ -589,15 +579,15 @@ struct DTypePointer[ """ alias element_type = Scalar[type] - alias _mlir_type = Pointer[Scalar[type], address_space] - var address: Self._mlir_type + alias _pointer_type = Pointer[Scalar[type], address_space] + var address: Self._pointer_type """The pointed-to address.""" @always_inline("nodebug") fn __init__(inout self): """Constructs a null `DTypePointer` from the given type.""" - self.address = Self._mlir_type() + self.address = Self._pointer_type() @always_inline("nodebug") fn __init__( @@ -645,7 +635,7 @@ struct DTypePointer[ value: The input pointer index. """ var address = __mlir_op.`pop.index_to_pointer`[ - _type = Self._mlir_type._mlir_type + _type = Self._pointer_type._mlir_type ](value.cast[DType.index]().value) self.address = address @@ -656,17 +646,7 @@ struct DTypePointer[ Args: address: The input address. """ - self.address = Self._mlir_type(address=address) - - @staticmethod - @always_inline("nodebug") - fn get_null() -> Self: - """Constructs a `DTypePointer` representing *nullptr*. - - Returns: - Constructed *nullptr* `DTypePointer` object. - """ - return Self._mlir_type() + self.address = Self._pointer_type(address=address) fn __str__(self) -> String: """Format this pointer as a hexadecimal string. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index fca93be869..6b65a62d56 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -72,7 +72,9 @@ struct UnsafePointer[ Returns: A null pointer. """ - return Self.get_null() + return Self { + address: __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] + } @always_inline fn __init__(value: Self._mlir_type) -> Self: @@ -127,18 +129,6 @@ struct UnsafePointer[ # DTypePointer? return UnsafePointer[Scalar[dtype]](address=int(ptr)) - @staticmethod - @always_inline("nodebug") - fn get_null() -> Self: - """Constructs a UnsafePointer representing nullptr. - - Returns: - Constructed nullptr UnsafePointer object. - """ - return Self { - address: __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] - } - @staticmethod @always_inline fn alloc(count: Int) -> Self: diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 9b1234316e..ebd34194f8 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -40,10 +40,9 @@ struct PyKeyValuePair: struct PyObjectPtr: var value: DTypePointer[DType.int8] - @staticmethod - fn null_ptr() -> PyObjectPtr: - var null_pointer = DTypePointer[DType.int8].get_null() - return PyObjectPtr {value: null_pointer} + @always_inline("nodebug") + fn __init__(inout self): + self.value = DTypePointer[DType.int8]() fn is_null(self) -> Bool: return int(self.value) == 0 @@ -169,12 +168,10 @@ struct CPython: " suitable libpython, please set `MOJO_PYTHON_LIBRARY`" ) - var null_pointer = DTypePointer[DType.int8].get_null() - self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) - self.none_value = PyObjectPtr(null_pointer) - self.dict_type = PyObjectPtr(null_pointer) + self.none_value = PyObjectPtr() + self.dict_type = PyObjectPtr() self.logging_enabled = logging_enabled self.version = PythonVersion(_py_get_version(self.lib)) @@ -780,8 +777,8 @@ struct CPython: fn PyDict_Next( inout self, dictionary: PyObjectPtr, p: Int ) -> PyKeyValuePair: - var key = DTypePointer[DType.int8].get_null() - var value = DTypePointer[DType.int8].get_null() + var key = DTypePointer[DType.int8]() + var value = DTypePointer[DType.int8]() var v = p var position = UnsafePointer[Int].address_of(v) var value_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 12114b8145..d54e4f7c2b 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -58,16 +58,16 @@ struct _PyIter(Sized): var maybeNextItem = cpython.PyIter_Next(self.iterator.py_object) if maybeNextItem.is_null(): self.isDone = True - self.preparedNextItem = PyObjectPtr.null_ptr() + self.preparedNextItem = PyObjectPtr() else: self.preparedNextItem = maybeNextItem self.isDone = False fn __init__(inout self): """Initialize an empty iterator.""" - self.iterator = PyObjectPtr.null_ptr() + self.iterator = PyObjectPtr() self.isDone = True - self.preparedNextItem = PyObjectPtr.null_ptr() + self.preparedNextItem = PyObjectPtr() fn __next__(inout self: _PyIter) -> PythonObject: """Return the next item and update to point to subsequent item. @@ -345,7 +345,7 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() if not self.py_object.is_null(): cpython.Py_DecRef(self.py_object) - self.py_object = PyObjectPtr.null_ptr() + self.py_object = PyObjectPtr() fn __getattr__(self, name: StringLiteral) raises -> PythonObject: """Return the value of the object attribute with the given name. diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 35e19de177..6ffb5ca9b0 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -78,7 +78,7 @@ struct DLHandle(CollectionElement, Boolable): @parameter if not os_is_windows(): _ = external_call["dlclose", Int](self.handle) - self.handle = DTypePointer[DType.int8].get_null() + self.handle = DTypePointer[DType.int8]() fn __bool__(self) -> Bool: """Checks if the handle is valid. diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index e80fce7c87..bb65fb68e4 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -251,7 +251,7 @@ fn sleep(sec: Float64): int((sec - total_secs) * NANOSECONDS_IN_SECOND), ) var req = UnsafePointer[_CTimeSpec].address_of(tv_spec) - var rem = UnsafePointer[_CTimeSpec].get_null() + var rem = UnsafePointer[_CTimeSpec]() _ = external_call["nanosleep", Int32](req, rem) diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index 3cc60f5d10..f78fb8393d 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -44,9 +44,7 @@ def test_last_error(): # GetProcessId takes the handle to a process and returns its id. If the # handle is null this will fail and returns an invalid handle error (error # code 6). - var succeeded = external_call["GetProcessId", Int]( - UnsafePointer[Int].get_null() - ) + var succeeded = external_call["GetProcessId", Int](UnsafePointer[Int]()) assert_equal(succeeded, 0) From 38ccbbc6d7d603d1727eb3f488fa8dc2e20afc3f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 29 May 2024 23:19:38 -0600 Subject: [PATCH 0762/2019] [stdlib] Remove `is_power_of_2` from `math` Remove `is_power_of_2` from `math` module in favor of `is_power_of_two` from the `bit` module. MODULAR_ORIG_COMMIT_REV_ID: 5fab62d7a9eb9cde5de84e6a71cac68616bb92ef --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 92731edd8d..5f24b437c2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -498,6 +498,9 @@ what we publish. from `math` to `builtin`, so you no longer need to do `from math import abs, round, min, max, divmod, pow`. + - The `is_power_of_2` function in the `math` module is now called + `is_power_of_two` and located in the `bit` module. + - Many functions returning a pointer type have been unified to have a public API function of `unsafe_ptr()`. From 53b8f285f2e05fc7ceca50b621943d2506dfb118 Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Wed, 29 May 2024 22:32:57 -0700 Subject: [PATCH 0763/2019] [stdlib] Collapse the two versions of KGEN TypeConstantAttrs into one Collapse `kgen.parameterizedtype.constant` and `kgen.concretetype.constant` attrs into one: `kgen.type`. MODULAR_ORIG_COMMIT_REV_ID: a0980a52ec5e0038885dccc8f123f288d846ea92 --- stdlib/src/sys/info.mojo | 14 +++++++------- stdlib/src/sys/intrinsics.mojo | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index c19f1bac4e..3369eddf80 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -522,14 +522,14 @@ fn sizeof[ The size of the type in bytes. """ alias mlir_type = __mlir_attr[ - `#kgen.param.expr> : `, AnyType, `> : !kgen.type`, ] return __mlir_attr[ - `#kgen.param.expr : !kgen.type,`, target, @@ -551,7 +551,7 @@ fn sizeof[ The size of the dtype in bytes. """ return __mlir_attr[ - `#kgen.param.expr`, @@ -575,15 +575,15 @@ fn alignof[ The alignment of the type in bytes. """ alias mlir_type = __mlir_attr[ - `#kgen.param.expr> : `, AnyType, `> : !kgen.type`, ] return __mlir_attr[ - `#kgen.param.expr : !kgen.type,`, target, `> : !kgen.int_literal`, @@ -604,7 +604,7 @@ fn alignof[ The alignment of the dtype in bytes. """ return __mlir_attr[ - `#kgen.param.expr`, diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index c2f65a6e3e..b8c97a9dd1 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1494,11 +1494,11 @@ fn _mlirtype_is_eq[t1: AnyTrivialRegType, t2: AnyTrivialRegType]() -> Bool: """ return __mlir_attr[ `#kgen.param.expr : !kgen.type`, `,`, - `#kgen.parameterizedtype.constant<`, + `#kgen.type<`, t2, `> : !kgen.type`, `> : i1`, @@ -1517,11 +1517,11 @@ fn _type_is_eq[t1: AnyType, t2: AnyType]() -> Bool: """ return __mlir_attr[ `#kgen.param.expr : !kgen.type`, `,`, - `#kgen.parameterizedtype.constant<`, + `#kgen.type<`, +t2, `> : !kgen.type`, `> : i1`, From 2320f63e367868976537bf1f3608f4ad3cd7eb17 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 29 May 2024 22:46:54 -0700 Subject: [PATCH 0764/2019] [mojo-docs] Add `get_null()` removal to changelog. MODULAR_ORIG_COMMIT_REV_ID: 9b1d1a36d11905eb90d622ca2f454efb0c98591d --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5f24b437c2..4ec848970d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -313,6 +313,9 @@ what we publish. - Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. +- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), + please use the default constructor instead: `UnsafePointer[T]()`. + - `Dict` now implements `get(key)` and `get(key, default)` functions. ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) From 28a68f5d7ec998f4dc23659a312fbf70eb67a7cf Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 30 May 2024 06:46:16 -0400 Subject: [PATCH 0765/2019] [stdlib] Deduplicate constraints in `utils.numerics.FPUtils` MODULAR_ORIG_COMMIT_REV_ID: 35c22b3ca4f32deb251c39c74f097e754ba28746 --- stdlib/src/builtin/simd.mojo | 10 ++++----- stdlib/src/utils/numerics.mojo | 37 ++++++++++++---------------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index aef73379de..b90a8e7973 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2718,7 +2718,7 @@ fn _bfloat16_to_f32_scalar( # BF16 support on neon systems is not supported. return _unchecked_zero[DType.float32, 1]() - var bfloat_bits = FPUtils.bitcast_to_integer(val) + var bfloat_bits = FPUtils[DType.bfloat16].bitcast_to_integer(val) return FPUtils[DType.float32].bitcast_from_integer( bfloat_bits << _fp32_bf16_mantissa_diff ) @@ -2755,11 +2755,11 @@ fn _f32_to_bfloat16_scalar( return _unchecked_zero[DType.bfloat16, 1]() if _isnan(val): - return -_nan[DType.bfloat16]() if FPUtils.get_sign(val) else _nan[ - DType.bfloat16 - ]() + return -_nan[DType.bfloat16]() if FPUtils[DType.float32].get_sign( + val + ) else _nan[DType.bfloat16]() - var float_bits = FPUtils.bitcast_to_integer(val) + var float_bits = FPUtils[DType.float32].bitcast_to_integer(val) var lsb = (float_bits >> _fp32_bf16_mantissa_diff) & 1 var rounding_bias = 0x7FFF + lsb diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 83743693c8..b6d254e0e5 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -33,7 +33,15 @@ from memory import UnsafePointer, bitcast # ===----------------------------------------------------------------------=== # -struct FPUtils[type: DType]: +fn _constrain_fp_type[type: DType](): + constrained[ + type.is_floating_point(), "dtype must be a floating point type" + ]() + + +struct FPUtils[ + type: DType, *, _constraint: NoneType = _constrain_fp_type[type]() +]: """Collection of utility functions for working with FP values. Constraints: @@ -41,6 +49,7 @@ struct FPUtils[type: DType]: Parameters: type: The concrete FP dtype (FP32/FP64/etc). + _constraint: Implements the constraint. Do not pass explicitly. """ alias integral_type = _integral_type_of[type]() @@ -54,10 +63,6 @@ struct FPUtils[type: DType]: Returns: The mantissa width. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() @parameter if type == DType.float16: @@ -67,7 +72,7 @@ struct FPUtils[type: DType]: elif type == DType.float32: return 23 else: - constrained[type == DType.float64, "unsupported DType"]() + constrained[type == DType.float64, "unsupported float type"]() return 52 @staticmethod @@ -78,10 +83,6 @@ struct FPUtils[type: DType]: Returns: The max exponent. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() @parameter if type == DType.float16: @@ -89,7 +90,7 @@ struct FPUtils[type: DType]: elif type == DType.float32 or type == DType.bfloat16: return 128 else: - constrained[type == DType.float64, "unsupported DType"]() + constrained[type == DType.float64, "unsupported float type"]() return 1024 @staticmethod @@ -100,10 +101,6 @@ struct FPUtils[type: DType]: Returns: The exponent width. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() @parameter if type == DType.float16: @@ -111,7 +108,7 @@ struct FPUtils[type: DType]: elif type == DType.float32 or type == DType.bfloat16: return 8 else: - constrained[type == DType.float64, "unsupported DType"]() + constrained[type == DType.float64, "unsupported float type"]() return 11 @staticmethod @@ -200,10 +197,6 @@ struct FPUtils[type: DType]: Returns: An integer representation of the floating-point value. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() return int(bitcast[Self.integral_type, 1](value)) @staticmethod @@ -217,10 +210,6 @@ struct FPUtils[type: DType]: Returns: An floating-point representation of the Int. """ - constrained[ - type.is_floating_point(), - "dtype must be a floating point type", - ]() return bitcast[type, 1](SIMD[Self.integral_type, 1](value)) @staticmethod From 29f9fc1d2e5b63118713842e66a22f36e388c488 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 30 May 2024 07:59:21 -0400 Subject: [PATCH 0766/2019] [stdlib] Don't use LUT in `string._isnewline` Because the it cannot be used in a parameter context. MODULAR_ORIG_COMMIT_REV_ID: 65734c3532c85a321b8b439ff17f233ff13202f8 --- stdlib/src/builtin/string.mojo | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index d56a8ffc7a..8c7e7cab9c 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -582,6 +582,7 @@ fn _isspace(c: UInt8) -> Bool: True iff the character is one of the whitespace characters listed above. """ + # NOTE: a global LUT doesn't work at compile time so we can't use it here. alias ` ` = UInt8(ord(" ")) alias `\t` = UInt8(ord("\t")) alias `\n` = UInt8(ord("\n")) @@ -605,27 +606,34 @@ fn _isspace(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # -fn _get_newlines_table() -> InlineArray[UInt8, 128]: - var table = InlineArray[UInt8, 128](0) - table[ord("\n")] = 1 - table[ord("\r")] = 1 - table[ord("\f")] = 1 - table[ord("\v")] = 1 - table[ord("\x1c")] = 1 - table[ord("\x1d")] = 1 - table[ord("\x1e")] = 1 - return table - - -alias _NEWLINES_TABLE = _get_newlines_table() - +fn _isnewline(s: String) -> Bool: + if len(s._buffer) != 2: + return False -fn _isnewline(c: String) -> Bool: # TODO: add \u2028 and \u2029 when they are properly parsed # FIXME: \x85 is parsed but not encoded in utf-8 - if len(c._buffer) == 2: - return c == "\x85" or _NEWLINES_TABLE[ord(c)] - return False + if s == "\x85": + return True + + # NOTE: a global LUT doesn't work at compile time so we can't use it here. + alias `\n` = UInt8(ord("\n")) + alias `\r` = UInt8(ord("\r")) + alias `\f` = UInt8(ord("\f")) + alias `\v` = UInt8(ord("\v")) + alias `\x1c` = UInt8(ord("\x1c")) + alias `\x1d` = UInt8(ord("\x1d")) + alias `\x1e` = UInt8(ord("\x1e")) + + var c = UInt8(ord(s)) + return ( + c == `\n` + or c == `\r` + or c == `\f` + or c == `\v` + or c == `\x1c` + or c == `\x1d` + or c == `\x1e` + ) # ===----------------------------------------------------------------------=== # From 2624935d75e4c2a356228f47e4e87ac0b3426f70 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 30 May 2024 11:33:46 -0400 Subject: [PATCH 0767/2019] [stdlib] Rename `math.tgamma` to `math.gamma` Because that's how it's called in Python. MODULAR_ORIG_COMMIT_REV_ID: e44072b5738ca477ca398c9749bc68809c6450f9 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 4ec848970d..658883e399 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -568,6 +568,9 @@ what we publish. - The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been moved to the `bit` module. +- The `math.tgamma` function has been renamed to `math.gamma` to conform with + Python's naming. + - The implementation of the following functions have been moved from the `math` module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the From ce0925bd82b032f86b85986568f75cc50d0d9687 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 30 May 2024 10:51:09 -0500 Subject: [PATCH 0768/2019] [External] [stdlib] Roundable trait with ndigits param in the round operation and Round to nearest even logic (#40794) [External] [stdlib] Roundable trait with ndigits param in the round operation and Round to nearest even logic * Add `Roundable.__round__(ndigits)` method * Implement the method in all the types that adhere to `Roundable` * Round to the next even number when the diff is exactly 0.5, recommended way in [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754), which also Python adheres to. * Tests --------- Co-authored-by: Manuel Saelices Closes modularml/mojo#2752 MODULAR_ORIG_COMMIT_REV_ID: f3349ff968cdb519bd505427a6aea8afe1ae1bb0 --- stdlib/src/builtin/float_literal.mojo | 55 ++++++++++++++------- stdlib/src/builtin/int.mojo | 12 +++++ stdlib/src/builtin/int_literal.mojo | 40 +++++++++++++++ stdlib/src/builtin/math.mojo | 20 ++++++++ stdlib/src/builtin/simd.mojo | 12 +++++ stdlib/test/builtin/test_float_literal.mojo | 18 +++++-- stdlib/test/builtin/test_int.mojo | 4 ++ stdlib/test/builtin/test_int_literal.mojo | 17 +++++++ stdlib/test/builtin/test_math.mojo | 8 ++- 9 files changed, 163 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index db8e2876f9..603e0a976a 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -250,18 +250,24 @@ struct FloatLiteral( if not self._is_normal(): return self + alias one = __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` + alias neg_one = __mlir_attr.`#kgen.int_literal<-1> : !kgen.int_literal` var truncated: IntLiteral = self.__int_literal__() - var result: Self - if abs(self) - abs(truncated) <= 0.5: - result = Self(truncated) - elif self > 0: - result = Self(truncated + 1) + var abs_diff = abs(self - truncated) + var plus_one = one if self > 0 else neg_one + if abs_diff == 0.5: + # Round to the nearest even number. + if truncated % 2 == 0: + return Self(truncated) + else: + return Self(truncated + plus_one) + elif abs_diff > 0.5: + return Self(truncated + plus_one) else: - result = Self(truncated - 1) - return result + return Self(truncated) @always_inline("nodebug") - fn __round__(self, ndigits: IntLiteral) -> Self: + fn __round__(self, ndigits: Int) -> Self: """Return the rounded value of the FloatLiteral. Args: @@ -275,24 +281,39 @@ struct FloatLiteral( return self alias one = __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` + alias neg_one = __mlir_attr.`#kgen.int_literal<-1> : !kgen.int_literal` alias ten = __mlir_attr.`#kgen.int_literal<10> : !kgen.int_literal` var multiplier = one + var target: Self = self # TODO: Use IntLiteral.__pow__() when it's implemented. - for _ in range(ndigits): + for _ in range(abs(ndigits)): multiplier = __mlir_op.`kgen.int_literal.binop`[ oper = __mlir_attr.`#kgen` ](multiplier, ten) - var target: Self = self * Self(multiplier) - var truncated: Self = target.__int_literal__() + if ndigits > 0: + target *= Self(multiplier) + elif ndigits < 0: + target /= Self(multiplier) + else: + return self.__round__() + var truncated: IntLiteral = target.__int_literal__() var result: Self - if abs(target) - abs(truncated) <= 0.5: - result = truncated - elif self > 0: - result = truncated + 1 + var abs_diff = abs(target - truncated) + var plus_one = one if self > 0 else neg_one + if abs_diff == 0.5: + # Round to the nearest even number. + if truncated % 2 == 0: + result = Self(truncated) + else: + result = Self(truncated + plus_one) + elif abs_diff <= 0.5: + result = Self(truncated) else: - result = truncated - 1 - if ndigits > 0: + result = Self(truncated + plus_one) + if ndigits >= 0: result /= Self(multiplier) + elif ndigits < 0: + result *= Self(multiplier) return result # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index e503825cdd..c2a4932d7d 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -573,6 +573,18 @@ struct Int( """ return self + @always_inline("nodebug") + fn __round__(self, ndigits: Int) -> Self: + """Return the rounded value of the Int value, which is itself. + Args: + ndigits: The number of digits to round to. + Returns: + The Int value itself if ndigits >= 0 else the rounded value. + """ + if ndigits >= 0: + return self + return self - (self % 10 ** -(ndigits)) + @always_inline("nodebug") fn __trunc__(self) -> Self: """Return the truncated Int value, which is itself. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 6bcbf99b8a..8f9f72f1b7 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -269,6 +269,46 @@ struct IntLiteral( """ return self + @always_inline("nodebug") + fn __divmod__(self, rhs: Self) -> Tuple[Self, Self]: + """Return the quotient and remainder of the division of self by rhs. + + Args: + rhs: The value to divide on. + + Returns: + The quotient and remainder of the division. + """ + var quotient: Self = self.__floordiv__(rhs) + var remainder: Self = self - (quotient * rhs) + return quotient, remainder + + @always_inline("nodebug") + fn __round__(self, ndigits: Int) -> Self: + """Return the rounded value of the IntLiteral value, which is itself. + + Args: + ndigits: The number of digits to round to. + + Returns: + The IntLiteral value itself if ndigits >= 0 else the rounded value. + """ + if ndigits >= 0: + return self + alias one = __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` + alias ten = __mlir_attr.`#kgen.int_literal<10> : !kgen.int_literal` + var multiplier = one + # TODO: Use IntLiteral.__pow__() when it's implemented. + for _ in range(-ndigits): + multiplier = __mlir_op.`kgen.int_literal.binop`[ + oper = __mlir_attr.`#kgen` + ](multiplier, ten) + alias Pair = Tuple[Self, Self] + var mod: IntLiteral = self % Self(multiplier) + if mod * 2 >= multiplier: + mod -= multiplier + return self - mod + @always_inline("nodebug") fn __invert__(self) -> Self: """Return ~self. diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 2acb9aa605..b88eac3e22 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -294,6 +294,9 @@ trait Roundable: fn __round__(self) -> Self: ... + fn __round__(self, ndigits: Int) -> Self: + ... + @always_inline fn round[T: Roundable](value: T) -> T: @@ -309,3 +312,20 @@ fn round[T: Roundable](value: T) -> T: The rounded value of the object. """ return value.__round__() + + +@always_inline +fn round[T: Roundable](value: T, ndigits: Int) -> T: + """Get the rounded value of the given object. + + Parameters: + T: The type conforming to Roundable. + + Args: + value: The object to get the rounded value of. + ndigits: The number of digits to round to. + + Returns: + The rounded value of the object. + """ + return value.__round__(ndigits) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b90a8e7973..0457bd3a74 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1143,6 +1143,18 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) + @always_inline("nodebug") + fn __round__(self, ndigits: Int) -> Self: + """Performs elementwise rounding on the elements of a SIMD vector. + This rounding goes to the nearest integer with ties away from zero. + Args: + ndigits: The number of digits to round to. + Returns: + The elementwise rounded value of this SIMD vector. + """ + # TODO: see how can we implement this. + return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) + # ===------------------------------------------------------------------=== # # In place operations. # ===------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index e4d5b352f2..e56272735e 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -69,9 +69,9 @@ def test_trunc(): def test_round(): - assert_equal(FloatLiteral.__round__(1.5), 1.0) + assert_equal(FloatLiteral.__round__(1.5), 2.0) assert_equal(FloatLiteral.__round__(1.6), 2.0) - assert_equal(FloatLiteral.__round__(-1.5), -1.0) + assert_equal(FloatLiteral.__round__(-1.5), -2.0) assert_equal(FloatLiteral.__round__(-3.6), -4.0) assert_equal(FloatLiteral.__round__(3.0), 3.0) assert_equal(FloatLiteral.__round__(0.0), 0.0) @@ -81,10 +81,7 @@ def test_round(): assert_equal(FloatLiteral.__round__(inf), inf) assert_equal(FloatLiteral.__round__(neg_inf), neg_inf) - assert_equal(FloatLiteral.__round__(1.5, 0), 1.0) - assert_equal(FloatLiteral.__round__(2.5, 0), 2.0) assert_equal(FloatLiteral.__round__(1.6, 0), 2.0) - assert_equal(FloatLiteral.__round__(-2.5, 0), -2.0) assert_equal(FloatLiteral.__round__(1.5, 1), 1.5) assert_equal(FloatLiteral.__round__(1.123, 1), 1.1) @@ -95,6 +92,17 @@ def test_round(): assert_equal(FloatLiteral.__round__(-1.198, 2), -1.2) assert_equal(FloatLiteral.__round__(-1.123, 2), -1.12) + # Test rounding to nearest even number + assert_equal(FloatLiteral.__round__(1.5, 0), 2.0) + assert_equal(FloatLiteral.__round__(2.5, 0), 2.0) + assert_equal(FloatLiteral.__round__(-2.5, 0), -2.0) + assert_equal(FloatLiteral.__round__(-1.5, 0), -2.0) + + # Negative ndigits + assert_equal(FloatLiteral.__round__(123.456, -1), 120.0) + assert_equal(FloatLiteral.__round__(123.456, -2), 100.0) + assert_equal(FloatLiteral.__round__(123.456, -3), 0.0) + fn round10(x: Float64) -> Float64: # TODO: implement __div__ on FloatLiteral? diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 07e9ac5c25..ae3439d265 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -65,6 +65,10 @@ def test_round(): assert_equal(Int.__round__(Int(5)), 5) assert_equal(Int.__round__(Int(0)), 0) assert_equal(Int.__round__(Int(-5)), -5) + assert_equal(Int.__round__(5, 1), 5) + assert_equal(Int.__round__(0, 1), 0) + assert_equal(Int.__round__(-5, 1), -5) + assert_equal(Int.__round__(100, -2), 100) def test_trunc(): diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 19a459eb11..ea7c22da68 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -38,6 +38,10 @@ def test_round(): assert_equal(IntLiteral.__round__(5), 5) assert_equal(IntLiteral.__round__(0), 0) assert_equal(IntLiteral.__round__(-5), -5) + assert_equal(IntLiteral.__round__(5, 1), 5) + assert_equal(IntLiteral.__round__(0, 1), 0) + assert_equal(IntLiteral.__round__(-5, 1), -5) + assert_equal(IntLiteral.__round__(100, -2), 100) def test_trunc(): @@ -83,6 +87,18 @@ def test_indexer(): assert_equal(88, IntLiteral.__index__(88)) +def test_divmod(): + t = IntLiteral.__divmod__(2, 2) + assert_equal(t[0], 1) + assert_equal(t[1], 0) + t = IntLiteral.__divmod__(2, 3) + assert_equal(t[0], 0) + assert_equal(t[1], 2) + t = IntLiteral.__divmod__(99, -2) + assert_equal(t[0], -50) + assert_equal(t[1], -1) + + def main(): test_int() test_ceil() @@ -91,6 +107,7 @@ def main(): test_trunc() test_floordiv() test_mod() + test_divmod() test_bit_width() test_abs() test_indexer() diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 0cc838f27e..13c9cecb03 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -66,9 +66,15 @@ def test_round(): assert_equal(0, round(0.0)) assert_equal(1, round(1.0)) assert_equal(1, round(1.1)) + assert_equal(1, round(1.4)) assert_equal(2, round(1.5)) - assert_equal(2, round(1.9)) assert_equal(2, round(2.0)) + assert_equal(1, round(1.4, 0)) + # FIXME: round(2.5) is 2.0 (roundeven) but it's using roundhalfup + # Fix when the math libray is open sourced + # assert_equal(2, round(2.5)) + # assert_equal(1.5, round(1.5, 1)) + # assert_equal(1.61, round(1.613, 2)) var lhs = SIMD[DType.float32, 4](1.1, 1.5, 1.9, 2.0) var expected = SIMD[DType.float32, 4](1.0, 2.0, 2.0, 2.0) From db806466fcccd66b4a6f305726da689d019f00f9 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 30 May 2024 12:29:15 -0400 Subject: [PATCH 0769/2019] [stdlib] Remove `math.{rint,nearbyint}` Because they are unused, untested, and it's not clear if they provide the right API given that we could just overload `round`. MODULAR_ORIG_COMMIT_REV_ID: 49b03fce24844c3a2492af1d5c72e30064617a45 --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 658883e399..f027264e81 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -631,6 +631,7 @@ what we publish. - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and `SIMD.reduce_or` directly. - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. + - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. - The `math.bit.select` and `math.bit.bit_and` functions have been removed. The same functionality is available in the builtin `SIMD.select` and From bc31d1e797ef4d6043f52dde8def8a2dbcb6531e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 30 May 2024 13:18:12 -0400 Subject: [PATCH 0770/2019] [stdlib] Remove `math.polynomial.EvaluationMethod` and Estrin's method This method was limited to degree 10 or less, poorly tested, underutilized, and its performance unclear. MODULAR_ORIG_COMMIT_REV_ID: fcf5c29d5026e9c5d2c0b4691a014a5fc9a01890 --- docs/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index f027264e81..903b7bf9e4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -633,6 +633,13 @@ what we publish. - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. +- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's + method is no longer available. This method was limited to degree 10 or less, + underutilized, and its performance unclear. In the future, this might be + reintroduced with an improved implementation if needed, when better + performance benchmarking infrastructure is available. The default behavior of + `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). + - The `math.bit.select` and `math.bit.bit_and` functions have been removed. The same functionality is available in the builtin `SIMD.select` and `SIMD.__and__` methods, respectively. From 4325246bbc6e3ee32ef04a0de08d8e9a8212d589 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 30 May 2024 10:28:13 -0700 Subject: [PATCH 0771/2019] [Docs] Add missing changelog item for InlineList. We missed adding a changelog item when InlineList was added. MODULAR_ORIG_COMMIT_REV_ID: 3481d21551d0fd60f9143fb3fb30dcb483991244 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 903b7bf9e4..76790e39c7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -352,6 +352,11 @@ what we publish. - `List()` now supports `__contains__`. ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) +- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) + type, a stack-allocated list with a static maximum size. + ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + - `InlineList()` now supports `__contains__`, `__iter__`. ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) From 16fc20f85138108ef717644578f9b910f27fe4f1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 30 May 2024 12:34:41 -0500 Subject: [PATCH 0772/2019] [stdlib] docs: Fix mental typo in changelog MODULAR_ORIG_COMMIT_REV_ID: 41db2ee4d934b76ea88aa6e6b83a2d20f657f2fe --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 76790e39c7..aa9c23f8a5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -534,7 +534,7 @@ what we publish. operations, and support integer types. ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) -- `ListLiteral` and `Tuple` now only require that element types be `Copyable`. +- `ListLiteral` and `Tuple` now only require that element types be `Movable`. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - Continued transition to `UnsafePointer` and unsigned byte type for strings: From 8b4fdd43b90296a45780d6750ac329cb542f1c7f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 30 May 2024 11:35:29 -0600 Subject: [PATCH 0773/2019] [stdlib] Use `List` over `VariadicList` in `test_sort.mojo` There's no need to use `VariadicList` here. Use the simple `List` type instead. MODULAR_ORIG_COMMIT_REV_ID: 98169fb0c13de3d28092f31ad8d0364cab246f5d --- stdlib/test/builtin/test_sort.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 5bc140b792..2af26b8dce 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -446,7 +446,7 @@ fn test_partition_top_k(length: Int, k: Int) raises: fn test_sort_stress() raises: - var lens = VariadicList[Int](3, 100, 117, 223, 500, 1000, 1500, 2000, 3000) + var lens = List[Int](3, 100, 117, 223, 500, 1000, 1500, 2000, 3000) var random_seed = 0 seed(random_seed) From 279188bc786cdc6ced90f93b60da5a240c8a0e03 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 30 May 2024 15:28:00 -0500 Subject: [PATCH 0774/2019] [stdlib] docs: Update style guide value param guidance to match current practice (`snake_case`) MODULAR_ORIG_COMMIT_REV_ID: 5dc9b96ae4611c47426a8b13074ac93bdef2d53f --- stdlib/docs/style-guide.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 5c3f7ac129..6140f2342a 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -208,9 +208,9 @@ The following table shows our preferred use of different case styles. | `alias` type | `alias Int8 = Scalar[DType.int8]` | `PascalCase` | `alias` value global / local scope | `alias CHUNK_SIZE = 32` / `alias chunk_size = 32` | `SCREAMING_SNAKE_CASE` / `snake_case` | `struct` type parameter | `struct List[ElementType: Movable]` | `PascalCase` -| `struct` value parameter | `struct Array[ElementType: Movable, Length: Int]` | `PascalCase` +| `struct` value parameter | `struct Array[ElementType: Movable, length: Int]` | `snake_case` | `fn` type parameter | `fn do_it[Action: Actionable](action: Action)` | `PascalCase` -| `fn` value parameter | `fn repeat[Count: Int]()` | `PascalCase` +| `fn` value parameter | `fn repeat[count: Int]()` | `snake_case` Although these are our style conventions, not all code currently adheres to it. When preparing a new change, it is important to adhere to the style and naming @@ -231,8 +231,8 @@ struct LinkedList[ElementType: Movable] # 🟢 Preferred #### ℹ️ Order type parameters ahead of value parameters ```mojo -struct Array[LENGTH: Int, ElementType: Movable] # 🔴 Avoid -struct Array[ElementType: Movable, Length: Int] # 🟢 Preferred +struct Array[length: Int, ElementType: Movable] # 🔴 Avoid +struct Array[ElementType: Movable, length: Int] # 🟢 Preferred ``` ### Container lifecycle semantics From 971f921b994be4c3780bff85ddd45f462411ea47 Mon Sep 17 00:00:00 2001 From: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Date: Thu, 30 May 2024 16:48:09 -0500 Subject: [PATCH 0775/2019] [External] [stdlib] More tests for the `bit` module (#40973) [External] [stdlib] More tests for the `bit` module As mentioned in #2862 this PR is used to implement test cases for all the functions that has not been tested originally. Co-authored-by: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Closes modularml/mojo#2877 MODULAR_ORIG_COMMIT_REV_ID: 3ebcc372378e079c4b49990cbb9af041398a8a66 --- stdlib/src/bit/bit.mojo | 17 +- stdlib/test/bit/test_bit.mojo | 337 +++++++++++++++++++++++++++++++--- 2 files changed, 327 insertions(+), 27 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index c7bfd8ee45..573dae7069 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -113,13 +113,14 @@ fn countr_zero[ # ===----------------------------------------------------------------------===# # bit_reverse # ===----------------------------------------------------------------------===# +# TODO: implement bit_reverse for Int type @always_inline("nodebug") fn bit_reverse[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Element-wise reverses the bitpattern of an integral value. + """Element-wise reverses the bitpattern of a SIMD vector of integer values. Parameters: type: `dtype` used for the computation. @@ -144,13 +145,14 @@ fn bit_reverse[ # ===----------------------------------------------------------------------===# # byte_swap # ===----------------------------------------------------------------------===# +# TODO: implement byte_swap for Int type @always_inline("nodebug") fn byte_swap[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Byte-swaps a value. + """Byte-swaps a SIMD vector of integer values with an even number of bytes. Byte swap an integer value or vector of integer values with an even number of bytes (positive multiple of 16 bits). This is equivalent to `llvm.bswap` @@ -188,13 +190,14 @@ fn byte_swap[ # ===----------------------------------------------------------------------===# # pop_count # ===----------------------------------------------------------------------===# +# TODO: implement pop_count for Int type @always_inline("nodebug") fn pop_count[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Counts the number of bits set in a value. + """Counts the number of bits set in a SIMD vector of integer values. Parameters: type: `dtype` used for the computation. @@ -219,13 +222,14 @@ fn pop_count[ # ===----------------------------------------------------------------------===# # bit_not # ===----------------------------------------------------------------------===# +# TODO: implement bit_not for Int type @always_inline("nodebug") fn bit_not[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Performs a bitwise NOT operation on an integral. + """Performs a bitwise NOT operation on an SIMD vector of integer values. Parameters: type: `dtype` used for the computation. @@ -270,7 +274,8 @@ fn bit_width(val: Int) -> Int: fn bit_width[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Computes the minimum number of bits required to represent the integer. + """Computes the minimum number of bits required to represent the SIMD vector + of integer values. Parameters: type: `dtype` used for the computation. @@ -298,7 +303,7 @@ fn bit_width[ else: var leading_zero_pos = countl_zero(val) var leading_zero_neg = countl_zero(bit_not(val)) - var leading_zero = (val > 0).select(leading_zero_pos, leading_zero_neg) + var leading_zero = (val < 0).select(leading_zero_neg, leading_zero_pos) return bitwidth - leading_zero diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 72b95563f7..46c2d9bb28 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -19,12 +19,218 @@ from bit import ( bit_ceil, bit_floor, is_power_of_two, + countl_zero, + countr_zero, + bit_reverse, + byte_swap, + pop_count, + bit_not, ) from testing import assert_equal +def test_countl_zero(): + assert_equal(countl_zero(-(2**59)), 0) + assert_equal(countl_zero(-(2**20)), 0) + assert_equal(countl_zero(-1), 0) + assert_equal(countl_zero(-1), 0) + assert_equal(countl_zero(0), 64) + assert_equal(countl_zero(1), 63) + assert_equal(countl_zero(2), 62) + assert_equal(countl_zero(3), 62) + assert_equal(countl_zero(4), 61) + assert_equal(countl_zero(2**20), 43) + assert_equal(countl_zero(2**59), 4) + + +def test_countl_zero_simd(): + alias simd_width = 4 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-(2**6), 0, -1, 2**6) + assert_equal(countl_zero(var1), SIMD[int8_t, simd_width](0, 8, 0, 1)) + + alias var3 = SIMD[int16_t, simd_width](-(2**14), 0, -1, 2**14) + assert_equal(countl_zero(var3), SIMD[int16_t, simd_width](0, 16, 0, 1)) + + alias var5 = SIMD[int32_t, simd_width](-(2**30), 0, -1, 2**30) + assert_equal(countl_zero(var5), SIMD[int32_t, simd_width](0, 32, 0, 1)) + + # TODO: use this line after #2882 is fixed + # alias var7 = SIMD[int64_t, simd_width](-(2**62), 0, -1, 2**62) + alias var7 = SIMD[int64_t, simd_width]( + -4611686018427387904, 0, -1, 4611686018427387904 + ) + assert_equal(countl_zero(var7), SIMD[int64_t, simd_width](0, 64, 0, 1)) + + +def test_countr_zero(): + assert_equal(countr_zero(-(2**59)), 59) + assert_equal(countr_zero(-(2**20)), 20) + assert_equal(countr_zero(-1), 0) + assert_equal(countr_zero(0), 64) + assert_equal(countr_zero(1), 0) + assert_equal(countr_zero(2), 1) + assert_equal(countr_zero(3), 0) + assert_equal(countr_zero(4), 2) + assert_equal(countr_zero(2**20), 20) + assert_equal(countr_zero(2**59), 59) + + +def test_countr_zero_simd(): + alias simd_width = 4 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-(2**6), 0, -1, 2**6) + assert_equal(countr_zero(var1), SIMD[int8_t, simd_width](6, 8, 0, 6)) + + alias var3 = SIMD[int16_t, simd_width](-(2**14), 0, -1, 2**14) + assert_equal(countr_zero(var3), SIMD[int16_t, simd_width](14, 16, 0, 14)) + + alias var5 = SIMD[int32_t, simd_width](-(2**30), 0, -1, 2**30) + assert_equal(countr_zero(var5), SIMD[int32_t, simd_width](30, 32, 0, 30)) + + # TODO: use this line after #2882 is fixed + # alias var7 = SIMD[int64_t, simd_width](-(2**62), 0, -1, 2**62) + alias var7 = SIMD[int64_t, simd_width]( + -4611686018427387904, 0, -1, 4611686018427387904 + ) + assert_equal(countr_zero(var7), SIMD[int64_t, simd_width](62, 64, 0, 62)) + + +def test_bit_reverse_simd(): + alias simd_width = 4 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-1, 0, 1, 2) + assert_equal(bit_reverse(var1), SIMD[int8_t, simd_width](-1, 0, -128, 64)) + + alias var2 = SIMD[int16_t, simd_width](-1, 0, 1, 2) + assert_equal( + bit_reverse(var2), SIMD[int16_t, simd_width](-1, 0, -32768, 16384) + ) + + alias var3 = SIMD[int32_t, simd_width](-1, 0, 1, 2) + assert_equal( + bit_reverse(var3), + SIMD[int32_t, simd_width](-1, 0, -2147483648, 1073741824), + ) + + alias var4 = SIMD[int64_t, simd_width](-1, 0, 1, 2) + assert_equal( + bit_reverse(var4), + SIMD[int64_t, simd_width]( + -1, 0, -9223372036854775808, 4611686018427387904 + ), + ) + + +def test_byte_swap_simd(): + alias simd_width = 4 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var2 = SIMD[int16_t, simd_width](-0x0123, 0x0000, 0x0102, 0x0201) + assert_equal( + byte_swap(var2), + SIMD[int16_t, simd_width](0xDDFE, 0x0000, 0x0201, 0x0102), + ) + + alias var3 = SIMD[int32_t, simd_width]( + -0x01234567, 0x01234567, 0x56789ABC, 0x89ABCDEF + ) + assert_equal( + byte_swap(var3), + SIMD[int32_t, simd_width]( + 0x99BADCFE, 0x67452301, 0xBC9A7856, 0xEFCDAB89 + ), + ) + + alias var4 = SIMD[int64_t, simd_width]( + -0x0123456789ABCDEF, + 0x0123456789ABCDEF, + 0x56789ABCDEF01234, + 0x23456789ABCDEF01, + ) + assert_equal( + byte_swap(var4), + SIMD[int64_t, simd_width]( + 0x1132547698BADCFE, + 0xEFCDAB8967452301, + 0x3412F0DEBC9A7856, + 0x01EFCDAB89674523, + ), + ) + + +def test_pop_count_simd(): + alias simd_width = 4 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-114, 0, 100, 2**6) + assert_equal(pop_count(var1), SIMD[int8_t, simd_width](4, 0, 3, 1)) + + alias var2 = SIMD[int16_t, simd_width](-11444, 0, 3000, 2**13) + assert_equal(pop_count(var2), SIMD[int16_t, simd_width](8, 0, 7, 1)) + + alias var3 = SIMD[int32_t, simd_width](-111444, 0, 30000, 2**29) + assert_equal(pop_count(var3), SIMD[int32_t, simd_width](22, 0, 7, 1)) + + # TODO: use this line after #2882 is fixed + # alias var4 = SIMD[int64_t, simd_width](-111444444, 0, 3000000, 2**59) + alias var4 = SIMD[int64_t, simd_width]( + -111444444, 0, 3000000, 576460752303423488 + ) + assert_equal(pop_count(var4), SIMD[int64_t, simd_width](51, 0, 10, 1)) + + +def test_bit_not_simd(): + alias simd_width = 4 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-114, 0, 100, 2**6) + assert_equal(bit_not(var1), SIMD[int8_t, simd_width](113, -1, -101, -65)) + + alias var2 = SIMD[int16_t, simd_width](-11444, 0, 3000, 2**13) + assert_equal( + bit_not(var2), SIMD[int16_t, simd_width](11443, -1, -3001, -8193) + ) + + alias var3 = SIMD[int32_t, simd_width](-111444, 0, 30000, 2**29) + assert_equal( + bit_not(var3), SIMD[int32_t, simd_width](111443, -1, -30001, -536870913) + ) + + # TODO: use this line after #2882 is fixed + # alias var4 = SIMD[int64_t, simd_width](-111444444, 0, 3000000, 2**59) + alias var4 = SIMD[int64_t, simd_width]( + -111444444, 0, 3000000, 576460752303423488 + ) + assert_equal( + bit_not(var4), + SIMD[int64_t, simd_width](111444443, -1, -3000001, -(2**59) - 1), + ) + + def test_is_power_of_two(): + assert_equal(is_power_of_two(-(2**59)), False) assert_equal(is_power_of_two(-1), False) assert_equal(is_power_of_two(0), False) assert_equal(is_power_of_two(1), True) @@ -32,82 +238,163 @@ def test_is_power_of_two(): assert_equal(is_power_of_two(3), False) assert_equal(is_power_of_two(4), True) assert_equal(is_power_of_two(5), False) + assert_equal(is_power_of_two(2**59), True) def test_is_power_of_two_simd(): alias simd_width = 4 - alias type = DType.int8 - alias return_type = DType.bool + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 - alias var1 = SIMD[type, simd_width](-1, 0, 1, 2) + alias var1 = SIMD[int8_t, simd_width](-114, 0, 100, 2**6) assert_equal( is_power_of_two(var1), - SIMD[DType.bool, simd_width](False, False, True, True), + SIMD[DType.bool, simd_width](False, False, False, True), ) - alias var2 = SIMD[type, simd_width](3, 4, 5, 8) + alias var2 = SIMD[int16_t, simd_width](-11444, 0, 3000, 2**13) assert_equal( is_power_of_two(var2), - SIMD[DType.bool, simd_width](False, True, False, True), + SIMD[DType.bool, simd_width](False, False, False, True), + ) + + alias var3 = SIMD[int32_t, simd_width](-111444, 0, 30000, 2**29) + assert_equal( + is_power_of_two(var3), + SIMD[DType.bool, simd_width](False, False, False, True), + ) + + # TODO: use this line after #2882 is fixed + # alias var4 = SIMD[int64_t, simd_width](-111444444, 0, 3000000, 2**59) + alias var4 = SIMD[int64_t, simd_width]( + -111444444, 0, 3000000, 576460752303423488 + ) + assert_equal( + is_power_of_two(var4), + SIMD[DType.bool, simd_width](False, False, False, True), ) def test_bit_width(): + assert_equal(bit_width(-(2**59)), 59) assert_equal(bit_width(-2), 1) assert_equal(bit_width(-1), 0) + assert_equal(bit_width(0), 0) assert_equal(bit_width(1), 1) assert_equal(bit_width(2), 2) assert_equal(bit_width(4), 3) assert_equal(bit_width(5), 3) + assert_equal(bit_width(2**59), 60) def test_bit_width_simd(): alias simd_width = 4 - alias type = DType.int8 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-114, 0, 100, 2**6) + assert_equal(bit_width(var1), SIMD[int8_t, simd_width](7, 0, 7, 7)) + + alias var2 = SIMD[int16_t, simd_width](-11444, 0, 3000, 2**13) + assert_equal(bit_width(var2), SIMD[int16_t, simd_width](14, 0, 12, 14)) - alias var1 = SIMD[type, simd_width](-2, -1, 3, 4) - assert_equal(bit_width(var1), SIMD[type, simd_width](1, 0, 2, 3)) + alias var3 = SIMD[int32_t, simd_width](-111444, 0, 30000, 2**29) + assert_equal(bit_width(var3), SIMD[int32_t, simd_width](17, 0, 15, 30)) - alias var2 = SIMD[type, simd_width](1, 2, 3, 4) - assert_equal(bit_width(var2), SIMD[type, simd_width](1, 2, 2, 3)) + # TODO: use this line after #2882 is fixed + # alias var4 = SIMD[int64_t, simd_width](-111444444, 0, 3000000, 2**59) + alias var4 = SIMD[int64_t, simd_width]( + -111444444, 0, 3000000, 576460752303423488 + ) + assert_equal(bit_width(var4), SIMD[int64_t, simd_width](27, 0, 22, 60)) def test_bit_ceil(): + assert_equal(bit_ceil(-(2**59)), 1) assert_equal(bit_ceil(-2), 1) assert_equal(bit_ceil(1), 1) assert_equal(bit_ceil(2), 2) assert_equal(bit_ceil(4), 4) assert_equal(bit_ceil(5), 8) + assert_equal(bit_ceil(2**59 - 3), 2**59) def test_bit_ceil_simd(): alias simd_width = 4 - alias type = DType.int8 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-114, 0, 2**7 - 3, 2**6) + assert_equal(bit_ceil(var1), SIMD[int8_t, simd_width](1, 1, 2**7, 2**6)) + + alias var2 = SIMD[int16_t, simd_width](-11444, 0, 2**12 - 3, 2**13) + assert_equal( + bit_ceil(var2), SIMD[int16_t, simd_width](1, 1, 2**12, 2**13) + ) - alias var1 = SIMD[type, simd_width](-2, -1, 3, 4) - assert_equal(bit_ceil(var1), SIMD[type, simd_width](1, 1, 4, 4)) + alias var3 = SIMD[int32_t, simd_width](-111444, 0, 2**14 - 3, 2**29) + assert_equal( + bit_ceil(var3), SIMD[int32_t, simd_width](1, 1, 2**14, 2**29) + ) - alias var2 = SIMD[type, simd_width](1, 2, 3, 4) - assert_equal(bit_ceil(var2), SIMD[type, simd_width](1, 2, 4, 4)) + # TODO: use this line after #2882 is fixed + # alias var4 = SIMD[int64_t, simd_width](-111444444, 1, 2**22-3, 2**59) + alias var4 = SIMD[int64_t, simd_width]( + -111444444, 1, 2**22 - 3, 576460752303423488 + ) + assert_equal( + bit_ceil(var4), + SIMD[int64_t, simd_width](1, 1, 2**22, 2**59), + ) def test_bit_floor(): + assert_equal(bit_floor(-(2**59)), 0) assert_equal(bit_floor(-2), 0) assert_equal(bit_floor(1), 1) assert_equal(bit_floor(2), 2) assert_equal(bit_floor(4), 4) assert_equal(bit_floor(5), 4) + assert_equal(bit_floor(2**59), 2**59) def test_bit_floor_simd(): alias simd_width = 4 - alias type = DType.int8 + alias int8_t = DType.int8 + alias int16_t = DType.int16 + alias int32_t = DType.int32 + alias int64_t = DType.int64 + + alias var1 = SIMD[int8_t, simd_width](-114, 0, 2**5 + 3, 2**6) + assert_equal( + bit_floor(var1), SIMD[int8_t, simd_width](0, 0, 2**5, 2**6) + ) + + alias var2 = SIMD[int16_t, simd_width](-11444, 0, 2**12 + 3, 2**13) + assert_equal( + bit_floor(var2), SIMD[int16_t, simd_width](0, 0, 2**12, 2**13) + ) - alias var1 = SIMD[type, simd_width](-1, -2, 3, 4) - assert_equal(bit_floor(var1), SIMD[type, simd_width](0, 0, 2, 4)) + alias var3 = SIMD[int32_t, simd_width](-111444, 0, 2**14 + 3, 2**29) + assert_equal( + bit_floor(var3), SIMD[int32_t, simd_width](0, 0, 2**14, 2**29) + ) - alias var2 = SIMD[type, simd_width](4, 5, 6, 7) - assert_equal(bit_floor(var2), SIMD[type, simd_width](4, 4, 4, 4)) + # TODO: use this line after #2882 is fixed + # alias var4 = SIMD[int64_t, simd_width](-111444444, 1, 2**22+3, 2**59) + alias var4 = SIMD[int64_t, simd_width]( + -111444444, 1, 2**22 + 3, 576460752303423488 + ) + assert_equal( + bit_floor(var4), + SIMD[int64_t, simd_width](0, 1, 2**22, 2**59), + ) def test_rotate_bits_int(): @@ -167,3 +454,11 @@ def main(): test_bit_width_simd() test_is_power_of_two() test_is_power_of_two_simd() + test_countl_zero() + test_countl_zero_simd() + test_countr_zero() + test_countr_zero_simd() + test_bit_reverse_simd() + test_byte_swap_simd() + test_pop_count_simd() + test_bit_not_simd() From 6fd70182052cbba744ba410e238fc68fe86269e7 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 30 May 2024 18:31:40 -0700 Subject: [PATCH 0776/2019] [Docs] Assorted parameter doc updates - Adds a new section on "infer-only" parameters using the `//` sigil. - Adds a new section on parameter inference including struct parameter inference. - Adds a new section clarifying the difference between the terms "generic" and "parameterized." MODULAR_ORIG_COMMIT_REV_ID: 3455f903637af7cce450ff980ec1a520bf04565d --- docs/manual/parameters/index.ipynb | 442 +++++++++++++++++++++++------ 1 file changed, 349 insertions(+), 93 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 16fced1a28..088ee3ced8 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -56,16 +56,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "(The [`@unroll`](/mojo/manual/decorators/unroll.html) directive shown here \n", - "unrolls loops at compile time. The directive only works if the loop limits are\n", - "compile-time constants.)\n", + "The [`@parameter`](/mojo/manual/decorators/parameter.html) directive shown here \n", + "causes the `for` loop to be evaluated at compile time. The directive only works\n", + "if the loop limits are compile-time constants. Since `count` is a parameter,\n", + "`range(count)` can be calculated at compile time.\n", "\n", - "Calling a parameterized function, you provide values for the parameters, just like function arguments: " + "Calling a parameterized function, you provide values for the parameters, just \n", + "like function arguments: " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -111,6 +113,68 @@ "compilation fails." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameters and generics\n", + "\n", + "\"Generics\" refers to functions that can act on multiple types of values, or \n", + "containers that can hold multiple types of values. For example, \n", + "[`List`](/mojo/stdlib/collections/list/List), can hold\n", + "different types of values, so you can have a list of `Int` values, or\n", + "a list of `String` values).\n", + "\n", + "In Mojo, generics use parameters to specify types. For example, `List`\n", + "takes a type parameter, so a vector of integers is written `List[Int]`.\n", + "So all generics use parameters, but **not** everything that uses parameters is a\n", + "generic. \n", + "\n", + "For example, the `repeat[]()` function in the previous section includes \n", + "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n", + "but not generic. A generic function or struct is parameterized on _type_. For\n", + "example, we could rewrite `repeat[]()` to take any type of argument that\n", + "conforms to the [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) trait: " + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "3\n", + "3\n" + ] + } + ], + "source": [ + "fn repeat[MsgType: Stringable, count: Int](msg: MsgType):\n", + " @parameter\n", + " for i in range(count):\n", + " print(msg)\n", + "\n", + "repeat[3](3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This updated function takes any `Stringable` type, so you can pass it an `Int`,\n", + "`String`, or `Bool` value.\n", + "\n", + "Mojo's support for generics is still early. You can write generic functions like\n", + "this using traits and parameters. You can also write generic collections like\n", + "`List` and `Dict`. If you're interested in learning how these types work, you\n", + "can find the source code for the standard library collection types \n", + "[on GitHub](https://github.com/modularml/mojo/blob/nightly/stdlib/src/collections/)." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -119,30 +183,34 @@ "## Parameterized structs\n", "\n", "You can also add parameters to structs. You can use parameterized structs to\n", - "build generic containers. For example, a generic array type might include\n", - "code like this:" + "build generic containers. For example, a generic array type might include code\n", + "like this:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "struct GenericArray[T: AnyTrivialRegType]:\n", - " var data: Pointer[T]\n", + "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n", + "\n", + "struct GenericArray[ElementType: CollectionElement]:\n", + " var data: UnsafePointer[ElementType]\n", " var size: Int\n", "\n", - " fn __init__(inout self, *elements: T):\n", + " fn __init__(inout self, *elements: ElementType):\n", " self.size = len(elements)\n", - " self.data = Pointer[T].alloc(self.size)\n", + " self.data = UnsafePointer[ElementType].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data[i] = elements[i]\n", + " initialize_pointee_move(self.data.offset(i), elements[i])\n", "\n", " fn __del__(owned self):\n", + " for i in range(self.size):\n", + " destroy_pointee(self.data.offset(i))\n", " self.data.free()\n", "\n", - " fn __getitem__(self, i: Int) raises -> T:\n", + " fn __getitem__(self, i: Int) raises -> ref [__lifetime_of(self)] ElementType:\n", " if (i < self.size):\n", " return self.data[i]\n", " else:\n", @@ -153,22 +221,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This struct has a single parameter, `T`, which is a placeholder for the data\n", - "type you want to store in the array, sometimes called a _type parameter_. `T` is\n", - "typed as [`AnyTrivialRegType`](/mojo/stdlib/builtin/type_aliases.html), which is a \n", - "_metatype_ representing any \n", - "[register-passable type](/mojo/manual/decorators/register-passable.html). This\n", - "means our `GenericArray` can hold fixed-size data types like integers and \n", - "floating-point numbers that can be passed in a machine register, but not \n", - "dynamically allocated data like strings or vectors.\n", - "\n", - ":::note\n", - "\n", - "Using a capital `T` as the parameter doesn't have any special significance to\n", - "the compiler, but it's a convention in a number of languages to use a short name\n", - "for a type parameter, frequently defaulting to `T` (for \"type\"). \n", - "\n", - ":::\n", + "This struct has a single parameter, `ElementType`, which is a placeholder for\n", + "the data type you want to store in the array, sometimes called a _type\n", + "parameter_. `ElementType` is typed as\n", + "[`CollectionElement`](/mojo/stdlib/builtin/type_aliases.html), which is a\n", + "[trait](/mojo/manual/traits) representing any type that can be copied and moved.\n", "\n", "As with parameterized functions, you need to pass in parameter values when you\n", "use a parameterized struct. In this case, when you create an instance of \n", @@ -177,35 +234,30 @@ "passing in this case is a _type_. That's OK: a Mojo type is a valid compile-time\n", "value.)\n", "\n", - "You'll see that `T` is used throughout the struct where you'd usually see a \n", + "You'll see that `ElementType` is used throughout the struct where you'd usually see a \n", "type name. For example, as the formal type for the `elements` in the \n", "constructor, and the return type of the `__getitem__()` method.\n", "\n", - "At the moment, this code only works with register-passable types, which is why\n", - "the type parameter `T` is limited to `AnyTrivialRegType`. There's also an\n", - "[`AnyType`](/mojo/stdlib/builtin/type_aliases.html) metatype, which includes \n", - "**all** Mojo types.\n", - "\n", "Here's an example of using `GenericArray`:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "1 2 3 4 " + "1 2 3 4 " ] } ], "source": [ "var array = GenericArray[Int](1, 2, 3, 4)\n", "for i in range(array.size):\n", - " print(array[i], sep=\" \", end=\"\")" + " print(array[i], end=\" \")" ] }, { @@ -218,16 +270,16 @@ "signature:\n", "\n", "```mojo\n", - "struct GenericArray[T: AnyTrivialRegType]:\n", + "struct GenericArray[ElementType: CollectionElement]:\n", " ...\n", "\n", " @staticmethod\n", - " fn splat(count: Int, value: T) -> Self:\n", + " fn splat(count: Int, value: ElementType) -> Self:\n", " # Create a new array with count instances of the given value\n", "```\n", "\n", - "Here, `Self` is equivalent to writing `GenericArray[T]`. That is, you can call\n", - "the `splat()` method like this:\n", + "Here, `Self` is equivalent to writing `GenericArray[ElementType]`. That is, you\n", + "can call the `splat()` method like this:\n", "\n", "```mojo\n", "GenericArray[Float64].splat(8, 0)\n", @@ -289,14 +341,14 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "1 4 9 16 " + "1 4 9 16 " ] } ], @@ -304,7 +356,7 @@ "var vector = SIMD[DType.int16, 4](1, 2, 3, 4)\n", "vector = vector * vector\n", "for i in range(4):\n", - " print(vector[i], sep=\" \", end=\"\")" + " print(vector[i], end=\" \")" ] }, { @@ -349,7 +401,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -425,7 +477,7 @@ "source": [ "## Using parameterized types and functions\n", "\n", - "You can instantiate parametric types and functions by passing values to the\n", + "You can use parametric types and functions by passing values to the\n", "parameters in square brackets. For example, for the `SIMD` type above, `type`\n", "specifies the data type and `size` specifies the length of the SIMD vector (it\n", "must be a power of 2):" @@ -433,7 +485,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -470,19 +522,20 @@ "Note that the `cast()` method also needs a parameter to specify the type you\n", "want from the cast (the method definition above expects a `target` parametric\n", "value). Thus, just as the `SIMD` struct is a generic type definition, the\n", - "`cast()` method is a generic method definition that gets instantiated at\n", - "compile-time instead of runtime, based on the parameter value.\n", + "`cast()` method is a generic method definition. At compile time, the compiler\n", + "creates a concrete version of the `cast()` method with the target parameter\n", + "bound to `DType.float32`.\n", "\n", - "The code above shows the use of concrete types (that is, it\n", - "instantiates `SIMD` using known type values), but the major power of parameters\n", - "comes from the ability to define parametric algorithms and types (code that\n", - "uses the parameter values). For example, here's how to define a parametric\n", - "algorithm with `SIMD` that is type- and width-agnostic:" + "The code above shows the use of concrete types (that is, the parameters are all\n", + "bound to known values). But the major power of parameters comes from the\n", + "ability to define parametric algorithms and types (code that uses the parameter\n", + "values). For example, here's how to define a parametric algorithm with `SIMD`\n", + "that is type- and width-agnostic:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -513,14 +566,126 @@ "parameters are resolved at compile-time before they are needed by the runtime\n", "program (but compile-time parameter expressions cannot use runtime values).\n", "\n", - "The Mojo compiler is also smart about type inference with parameters. Note\n", - "that the above function is able to call the parametric\n", - "[`sqrt[]()`](/mojo/stdlib/math/math/sqrt) function\n", - "without specifying the parameters—the compiler infers its parameters based on\n", - "the parametric `x` value passed into it, as if you\n", - "wrote `sqrt[dt, width](x)` explicitly. Also note that `rsqrt()` chose to\n", - "name its second parameter `width` even though the `SIMD` type names it\n", - "`size`, and there is no problem." + "### Parameter inference\n", + "\n", + "The Mojo compiler can often _infer_ parameter values, so you don't always have\n", + "to specify them. For example, you can call the `rsqrt()` function defined above\n", + "without any parameters:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.174072265625, 0.174072265625, 0.174072265625, 0.174072265625]\n" + ] + } + ], + "source": [ + "var v = SIMD[DType.float16, 4](33)\n", + "print(rsqrt(v))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "The compiler infers its parameters based on the parametric `v`\n", + "value passed into it, as if you wrote `rsqrt[DType.float16, 4](v)` explicitly.\n", + "\n", + "Mojo can also infer the values of struct parameters from the arguments passed to \n", + "a constructor or static method.\n", + "\n", + "For example, consider the following struct:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "@value\n", + "struct One[Type: StringableCollectionElement]:\n", + " var value: Type\n", + "\n", + " fn __init__(inout self, value: Type):\n", + " self.value = value\n", + "\n", + "def use_one():\n", + " s1 = One(123)\n", + " s2 = One(\"Hello\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that you can create an instance of `One` without specifying the `Type`\n", + "parameter—Mojo can infer it from the `value` argument.\n", + "\n", + "You can also infer parameters from a parameterized type passed to a constructor\n", + "or static method:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "infer me\n", + "🔥 1 2\n" + ] + } + ], + "source": [ + "struct Two[Type: StringableCollectionElement]:\n", + " var val1: Type\n", + " var val2: Type\n", + "\n", + " fn __init__(inout self, one: One[Type], another: One[Type]):\n", + " self.val1 = one.value\n", + " self.val2 = another.value\n", + " print(self.val1, self.val2)\n", + "\n", + " @staticmethod\n", + " fn fire(thing1: One[Type], thing2: One[Type]):\n", + " print(\"🔥\", thing1.value, thing2.value)\n", + "\n", + "def use_two():\n", + " s3 = Two(One(\"infer\"), One(\"me\"))\n", + " Two.fire(One(1), One(2))\n", + "\n", + "use_two()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Two` takes a `Type` parameter, and its constructor takes values of type\n", + "`One[Type]`. When constructing an instance of `Two`, you don't need to specify\n", + "the `Type` parameter, since it can be inferred from the arguments.\n", + "\n", + "Similarly, the static `fire()` method takes values of type `One[Type]`, so Mojo\n", + "can infer the `Type` value at compile time.\n", + "\n", + ":::note\n", + "\n", + "If you're familiar with C++, you may recognize this as similar to Class Template\n", + "Argument Deduction (CTAD).\n", + "\n", + ":::" ] }, { @@ -544,7 +709,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -601,7 +766,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -628,6 +793,96 @@ ":::" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Infer-only parameters\n", + "\n", + "Sometimes you need to declare functions where parameters depend on other\n", + "parameters. Because the signature is processed left to right, a parameter can\n", + "only _depend_ on a parameter earlier in the parameter list. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Value: 2.2000000000000002\n", + "Value is floating-point: True\n" + ] + } + ], + "source": [ + "fn dependent_type[dtype: DType, value: Scalar[dtype]]():\n", + " print(\"Value: \", value)\n", + " print(\"Value is floating-point: \", dtype.is_floating_point())\n", + "\n", + "dependent_type[DType.float64, Float64(2.2)]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can't reverse the position of the `dtype` and `value` parameters, because\n", + "`value` depends on `dtype`. However, because `dtype` is a required parameter,\n", + "you can't leave it out of the parameter list and let Mojo infer it from `value`:\n", + "\n", + "```mojo\n", + "dependent_type[Float64(2.2)]() # Error!\n", + "```\n", + "\n", + "Infer-only parameters are a special class of parameters that are **always** \n", + "inferred from context. Infer-only parameters are placed at the **beginning** of\n", + "the parameter list, set off from other parameters by the `//` sigil`:\n", + "\n", + "```mojo\n", + "fn example[type: CollectionElement, //, list: List[type]]()\n", + "```\n", + "\n", + "Transforming `dtype` into an infer-only parameter solves this problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Value: 2.2000000000000002\n", + "Value is floating-point: True\n" + ] + } + ], + "source": [ + "fn dependent_type[dtype: DType, //, value: Scalar[dtype]]():\n", + " print(\"Value: \", value)\n", + " print(\"Value is floating-point: \", dtype.is_floating_point())\n", + "\n", + "dependent_type[Float64(2.2)]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because infer-only parameters are declared at the beginning of the parameter\n", + "list, other parameters can depend on them, and the compiler will always attempt\n", + "to infer the infer-only values from bound parameters or arguments.\n", + "\n", + "If the compiler can't infer the value of an infer-only parameter, compilation\n", + "fails." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -640,7 +895,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -665,7 +920,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -706,7 +961,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -729,7 +984,7 @@ " return result\n", "\n", "var a = SIMD[DType.float32, 2](1, 2)\n", - "var x = concat[DType.float32, 2, 2](a, a)\n", + "var x = concat(a, a)\n", "\n", "print('result type:', x.element_type, 'length:', len(x))" ] @@ -751,7 +1006,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -797,7 +1052,8 @@ "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter.html) decorator to create a parametric if condition, which is an `if` statement that\n", "runs at compile-time. It requires that its condition be a valid parameter\n", "expression, and ensures that only the live branch of the `if` statement is\n", - "compiled into the program.\n", + "compiled into the program. (This is similar to use of the `@parameter` decorator\n", + "with a `for` loop shown earlier.)\n", "\n", "## Mojo types are just parameter expressions\n", "\n", @@ -812,7 +1068,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -861,7 +1117,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -921,14 +1177,14 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "alias Float16 = SIMD[DType.float16, 1]\n", "alias UInt8 = SIMD[DType.uint8, 1]\n", "\n", - "var x : Float16 # Float16 works like a \"typedef\"" + "var x: Float16 = 0 # Float16 works like a \"typedef\"" ] }, { @@ -1075,7 +1331,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1111,7 +1367,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1142,7 +1398,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1159,7 +1415,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1180,7 +1436,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1227,7 +1483,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1248,7 +1504,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1270,7 +1526,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1288,7 +1544,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1328,7 +1584,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1348,7 +1604,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1369,7 +1625,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1386,7 +1642,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1415,7 +1671,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ From ee3f97dafaa01ae3dce70dc5b1b7e28fe7acd951 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 30 May 2024 19:14:58 -0700 Subject: [PATCH 0777/2019] [mojo-lang] Fix MOCO-757: Transfer ^ of borrowed arg leads to double free (#40892) Mojo used to support transfering out of `let` declarations, which were immutable but owned. We don't have to support this anymore, so there is no reason to support transfering from immutable refs. MODULAR_ORIG_COMMIT_REV_ID: ee2358b31fb300ab0843b53f54eea065b55e0fda --- stdlib/test/test_utils/types.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 5b241de79c..6d9da396d9 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -22,7 +22,7 @@ struct MoveOnly[T: Movable](Movable): var data: T """Test data payload.""" - fn __init__(inout self, i: T): + fn __init__(inout self, owned i: T): """Construct a MoveOnly providing the payload data. Args: From d12eb390308e4e1fae10d1c6fd678675ae386f85 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 30 May 2024 21:19:41 -0700 Subject: [PATCH 0778/2019] [KGEN] Rename ArgConvention::ByRef to ArgConvention::InOut, NFC. (#41013) This aligns the terminology with the modern keyword, we haven't called it 'byref' for a long time. We may eventually rename this again but for now align these. MODULAR_ORIG_COMMIT_REV_ID: eccdfc1e3620f1483b2435391610a530d6318037 --- stdlib/src/builtin/builtin_list.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 3111547ea6..77904f7e2a 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -280,9 +280,9 @@ struct VariadicListMem[ # Provide support for variadics of *inout* arguments. The reference will # automatically be inferred to be mutable, and the !kgen.variadic will have - # convention=byref. + # convention=inout. alias _inout_variadic_type = __mlir_type[ - `!kgen.variadic<`, Self._mlir_ref_type, `, byref>` + `!kgen.variadic<`, Self._mlir_ref_type, `, inout>` ] @always_inline From 0d9df971a269eb1d41e20fc6975400df124f38c9 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 30 May 2024 23:41:51 -0500 Subject: [PATCH 0779/2019] [External] [stdlib] Fix UB in `reversed(Dict.values())` and `reversed(Dict.items())` (#40974) [External] [stdlib] Fix UB in `reversed(Dict.values())` and `reversed(Dict.items())` Finally found the culprit in the flakyness that plagued us since a few week in the `test_reversed.mojo`. ### The actual bug: When iterating over a list in reverse order, we should start at `len(my_list) - 1` not at `len(my_list)`. That triggered an out of bounds access and thus was undefined behavior. ### The effect on our CI As you know, we have been seeing flakyness lately. It was documented a number of times and always related to `reverse`: * https://github.com/modularml/mojo/issues/2866#issuecomment-2135965412 * https://github.com/modularml/mojo/issues/2369 ### Why was it passing sometimes? This is because there were `Optional[...]` in the List. Thus if the flag of the `Optional` says that no element is present, it's just skipped (the dict doesn't have an entry at this index). So the list of the Dict would often look like this: `["a", "b", "c", "d"] None` but the last `None` is actually memory that we don't have access to. Sometimes it's then skipped in the iteration making the tests pass. Sometimes it would cause segfaults because the test dict worked with strings. Sometimes we would get `wrong variant type` since we don't know what happens to the memory between None check and access. ### Why wasn't it found earlier? First of all, our Dict implementation is too complexe for what it does and thus is very good at hiding bugs. Well we did have `debug_assert` before getting the element of the `List`, but this `debug_assert` looked like this in the dict iterator: ```mojo @parameter if forward: debug_assert( self.index < self.src[]._reserved, "dict iter bounds" ) else: debug_assert(self.index >= 0, "dict iter bounds") ``` So one bound was checked when reading in one direction and the other bound was checked in the other direction. A better `debug_assert` would have been ```mojo debug_assert(0 <= self.index < self.src[]._reserved, "dict iter bounds") ``` When I worked on my PR https://github.com/modularml/mojo/pull/2718 the condition `self.index < self.src[]._reserved` didn't trigger anything since it was in the wrong branch, it was never executed. Also before, `__get_ref` didn't have any bounds checks, even when assertions were enabled. A recent commit https://github.com/modularml/mojo/commit/8d0870e1b9e23fc9cf8b30882bb225a421f0fb9b adds `unsafe_get()` in List and make `__get_ref` use it. It also adds `debug_assert` to `unsafe_get()`, which means that now `__get_ref` has bounds checks if run with assertions enabled. This allowed me to catch the out of bounds access when updating https://github.com/modularml/mojo/pull/2718 making the fail deterministic and debuggable. Since we have this, the `debug_assert` in `dict.mojo` isn't necessary anymore. ### Consequences on ongoing work: * This fix have been also added to https://github.com/modularml/mojo/pull/2718 * The PR https://github.com/modularml/mojo/pull/2701 that we did with @jayzhan211 was actually correct. It was just using `reverse(Dict.items())` which was buggy at the time. After the fix is merged, we can re-revert this PR. * https://github.com/modularml/mojo/pull/2794 is not necessary anymore since the implementation by @jayzhan211 was correct. * The real cause of https://github.com/modularml/mojo/issues/2866 was found, the issue has already been closed though. * https://github.com/modularml/mojo/issues/2369 can be closed for good. * https://github.com/modularml/mojo/pull/2832 can be closed for good. ### Closing thoughts * We really need to run the unit tests with assertions enabled and add assertions whenever necessary * The dict implementation is a bit too complicated. For example, `self._reserved` is the length of the internal list. There is no need to store the length of the list twice. Let's drop this variable and use `len(self._entries)` instead. I guess this is a relic of the time when `List` wasn't completely flushed out. If had done so, it would have been ovious that we can't do `my_list.__get_ref(len(my_list))` * Iterating manually over a list like this is bug-prone. The implementation we have especially is, since ```mojo @parameter if forward: self.index += 1 else: self.index -= 1 ``` is done twice in the code, it should only be done once. While there is no bug, code duplication and complexity hides bugs. * We should iterate over the list with a list iterator, not with a custom-made iterator. This will remove a lot of code in the `dict.mojo`. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2896 MODULAR_ORIG_COMMIT_REV_ID: b65009dc51f1e3027f91b5b61a5b7003cb022b87 --- stdlib/src/builtin/reversed.mojo | 2 +- stdlib/src/collections/dict.mojo | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index c449cc5479..b8b07ae708 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -193,5 +193,5 @@ fn reversed[ """ var src = Reference(value)[].src return _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( - src[]._reserved, 0, src + src[]._reserved - 1, 0, src ) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e3977e1371..ff35d8babe 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -85,15 +85,6 @@ struct _DictEntryIter[ @always_inline fn __next__(inout self) -> Self.ref_type: while True: - - @parameter - if forward: - debug_assert( - self.index < self.src[]._reserved, "dict iter bounds" - ) - else: - debug_assert(self.index >= 0, "dict iter bounds") - var opt_entry_ref = self.src[]._entries.__get_ref(self.index) if opt_entry_ref[]: @@ -187,7 +178,7 @@ struct _DictValueIter[ var src = self.iter.src return _DictValueIter( _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( - src[]._reserved, 0, src + src[]._reserved - 1, 0, src ) ) From 6ecabb72acd5d6dd18a7924f98aec53ca8ef3373 Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Thu, 30 May 2024 23:10:26 -0700 Subject: [PATCH 0780/2019] [stdlib] Support dynamic module creation This capability allows Mojo code to create inline modules with complex behavior for Python interop. There is no need to rely on discovery of these source files elsewhere on the system. While this still depends on CPython library support, it eliminates at least one interaction with the host system. MODULAR_ORIG_COMMIT_REV_ID: 96c2100388bd6682589373569fca4385b6589e79 --- stdlib/src/python/_cpython.mojo | 30 ++++++++++++ stdlib/src/python/python.mojo | 52 +++++++++++++++++---- stdlib/test/python/test_python_interop.mojo | 26 +++++++++++ 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index ebd34194f8..0bc041869e 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -361,6 +361,36 @@ struct CPython: self._inc_total_rc() return result + fn PyEval_EvalCode( + inout self, + co: PyObjectPtr, + globals: PyObjectPtr, + locals: PyObjectPtr, + ) -> PyObjectPtr: + var result = PyObjectPtr( + self.lib.get_function[ + fn ( + PyObjectPtr, PyObjectPtr, PyObjectPtr + ) -> DTypePointer[DType.int8] + ]("PyEval_EvalCode")(co, globals, locals) + ) + self._inc_total_rc() + return result + + fn Py_CompileString( + inout self, + strref: StringRef, + filename: StringRef, + compile_mode: Int, + ) -> PyObjectPtr: + var r = self.lib.get_function[ + fn ( + DTypePointer[DType.uint8], DTypePointer[DType.uint8], Int32 + ) -> PyObjectPtr + ]("Py_CompileString")(strref.data, filename.data, Int32(compile_mode)) + self._inc_total_rc() + return r + fn PyObject_GetAttrString( inout self, obj: PyObjectPtr, diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index e634a4ee43..557dc09dd3 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -26,7 +26,7 @@ from memory import UnsafePointer from utils import StringRef -from ._cpython import CPython, Py_eval_input +from ._cpython import CPython, Py_eval_input, Py_file_input from .object import PythonObject @@ -93,28 +93,62 @@ struct Python: return cpython.PyRun_SimpleString(code) @staticmethod - fn evaluate(expr: StringRef) raises -> PythonObject: + fn evaluate( + expr: StringRef, file: Bool = False, name: StringRef = "__main__" + ) raises -> PythonObject: """Executes the given Python code. Args: expr: The Python expression to evaluate. + file: Evaluate as a file and return the module. + name: The name of the module (most relevant if `file` is True). Returns: `PythonObject` containing the result of the evaluation. """ var cpython = _get_global_python_itf().cpython() - var module = PythonObject(cpython.PyImport_AddModule("__main__")) + var module = PythonObject(cpython.PyImport_AddModule(name)) # PyImport_AddModule returns a borrowed reference - IncRef it to keep it alive. cpython.Py_IncRef(module.py_object) var dict_obj = PythonObject(cpython.PyModule_GetDict(module.py_object)) # PyModule_GetDict returns a borrowed reference - IncRef it to keep it alive. cpython.Py_IncRef(dict_obj.py_object) - var result = cpython.PyRun_String( - expr, dict_obj.py_object, dict_obj.py_object, Py_eval_input - ) - # We no longer need module and dictionary, release them. - Python.throw_python_exception_if_error_state(cpython) - return PythonObject(result) + if file: + # We compile the code as provided and execute in the module + # context. Note that this may be an existing module if the provided + # module name is not unique. The name here is used only for this + # code object, not the module itself. + # + # The Py_file_input is the code passed to the parsed to indicate + # the initial state: this is essentially whether it is expecting + # to compile an expression, a file or statements (e.g. repl). + var code = PythonObject( + cpython.Py_CompileString(expr, "", Py_file_input) + ) + # For this evaluation, we pass the dictionary both as the globals + # and the locals. This is because the globals is defined as the + # dictionary for the module scope, and locals is defined as the + # dictionary for the *current* scope. Since we are executing at + # the module scope for this eval, they should be the same object. + var result = PythonObject( + cpython.PyEval_EvalCode( + code.py_object, dict_obj.py_object, dict_obj.py_object + ) + ) + Python.throw_python_exception_if_error_state(cpython) + _ = code^ + _ = result^ + return module + else: + # We use the result of evaluating the expression directly, and allow + # all the globals/locals to be discarded. See above re: why the same + # dictionary is being used here for both globals and locals. + var result = cpython.PyRun_String( + expr, dict_obj.py_object, dict_obj.py_object, Py_eval_input + ) + # We no longer need module and dictionary, release them. + Python.throw_python_exception_if_error_state(cpython) + return PythonObject(result) @staticmethod fn add_to_path(dir_path: String) raises: diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index 1ff266319d..a2801da121 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -43,6 +43,25 @@ fn test_local_import(inout python: Python) -> String: return str(e) +fn test_dynamic_import(inout python: Python, times: Int = 1) -> String: + alias INLINE_MODULE = """ +called_already = False +def hello(name): + global called_already + if not called_already: + called_already = True + return f"Hello {name}!" + return "Again?" +""" + try: + var mod = Python.evaluate(INLINE_MODULE, file=True) + for _ in range(times - 1): + mod.hello("world") + return str(mod.hello("world")) + except e: + return str(e) + + fn test_call(inout python: Python) -> String: try: Python.add_to_path(TEST_DIR) @@ -65,6 +84,13 @@ def main(): var python = Python() assert_equal(test_local_import(python), "orange") + # Test twice to ensure that the module state is fresh. + assert_equal(test_dynamic_import(python), "Hello world!") + assert_equal(test_dynamic_import(python), "Hello world!") + + # Test with two calls to ensure that the state is persistent. + assert_equal(test_dynamic_import(python, times=2), "Again?") + assert_equal( test_call(python), ( From a8fc02ac941ec9a6ec35821f9ad446f06f5da224 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 31 May 2024 11:14:52 -0500 Subject: [PATCH 0781/2019] [External] [stdlib] Consolidate bin and hex internal implementation (#40690) [External] [stdlib] Consolidate bin and hex internal implementation Fixes #2730 - Utilize `_format_int` for for both `bin` and `hex` - Support `SIMD` and `Indexer` types for `hex` - Move implementations into `format_int` module --------- Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Co-authored-by: Rob Parolin Closes modularml/mojo#2790 MODULAR_ORIG_COMMIT_REV_ID: 0d5c716cb884468c8d3d49ac585115c39f131880 --- .../src/builtin/{hex.mojo => format_int.mojo} | 166 ++++++++++++++++-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/test/builtin/test_bin.mojo | 55 ------ .../{test_hex.mojo => test_format_int.mojo} | 64 +++++-- 7 files changed, 204 insertions(+), 89 deletions(-) rename stdlib/src/builtin/{hex.mojo => format_int.mojo} (60%) delete mode 100644 stdlib/test/builtin/test_bin.mojo rename stdlib/test/builtin/{test_hex.mojo => test_format_int.mojo} (54%) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/format_int.mojo similarity index 60% rename from stdlib/src/builtin/hex.mojo rename to stdlib/src/builtin/format_int.mojo index 4015032967..65545b0731 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -11,19 +11,92 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Provides the `hex` function. +"""Provides the `hex` and `bin` functions. These are Mojo built-ins, so you don't need to import them. """ -from collections import List, Optional +from collections import Optional from utils import InlineArray alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" +# ===----------------------------------------------------------------------===# +# bin +# ===----------------------------------------------------------------------===# + + @always_inline -fn hex[T: Intable](value: T, prefix: StringLiteral = "0x") -> String: +fn bin[ + type: DType +](num: Scalar[type], prefix: StringLiteral = "0b", /) -> String: + """Return the binary string representation an integral value. + + ```mojo + print(bin(123)) + print(bin(-123)) + ``` + ```plaintext + '0b1111011' + '-0b1111011' + ``` + + Parameters: + type: The data type of the integral scalar. + + Args: + num: An integral scalar value. + prefix: The prefix of the formatted int. + + Returns: + The binary string representation of num. + """ + return _try_format_int(num, 2, prefix=prefix) + + +# Need this until we have constraints to stop the compiler from matching this +# directly to bin[type: DType](num: Scalar[type]). +@always_inline("nodebug") +fn bin(b: Scalar[DType.bool], prefix: StringLiteral = "0b", /) -> String: + """Returns the binary representation of a scalar bool. + + Args: + b: A scalar bool value. + prefix: The prefix of the formatted int. + + Returns: + The binary string representation of b. + """ + return bin(b.cast[DType.int8]()) + + +@always_inline("nodebug") +fn bin[T: Indexer](num: T, prefix: StringLiteral = "0b", /) -> String: + """Returns the binary representation of an indexer type. + + Parameters: + T: The Indexer type. + + Args: + num: An indexer value. + prefix: The prefix of the formatted int. + + Returns: + The binary string representation of num. + """ + return bin(Scalar[DType.index](index(num))) + + +# ===----------------------------------------------------------------------===# +# hex +# ===----------------------------------------------------------------------===# + + +@always_inline +fn hex[ + type: DType +](value: Scalar[type], prefix: StringLiteral = "0x", /) -> String: """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. @@ -32,7 +105,7 @@ fn hex[T: Intable](value: T, prefix: StringLiteral = "0x") -> String: subsequent digits are hex. Parameters: - T: The intable type to represent in hexadecimal. + type: The type of the Scalar to represent in hexadecimal. Args: value: The integer value to format. @@ -41,9 +114,60 @@ fn hex[T: Intable](value: T, prefix: StringLiteral = "0x") -> String: Returns: A string containing the hex representation of the given integer. """ + return _try_format_int(value, 16, prefix=prefix) + + +@always_inline +fn hex[T: Indexer](value: T, prefix: StringLiteral = "0x", /) -> String: + """Returns the hex string representation of the given integer. + The hexadecimal representation is a base-16 encoding of the integer value. + + The returned string will be prefixed with "0x" to indicate that the + subsequent digits are hex. + + Parameters: + T: The indexer type to represent in hexadecimal. + + Args: + value: The integer value to format. + prefix: The prefix of the formatted int. + + Returns: + A string containing the hex representation of the given integer. + """ + return hex[DType.index](index(value), prefix) + + +@always_inline +fn hex(value: Scalar[DType.bool], prefix: StringLiteral = "0x", /) -> String: + """Returns the hex string representation of the given scalar bool. + + The hexadecimal representation is a base-16 encoding of the bool. + + The returned string will be prefixed with "0x" to indicate that the + subsequent digits are hex. + + Args: + value: The bool value to format. + prefix: The prefix of the formatted int. + + Returns: + A string containing the hex representation of the given bool. + """ + return hex(value.cast[DType.int8]()) + + +# ===----------------------------------------------------------------------===# +# Integer formatting utilities +# ===----------------------------------------------------------------------===# + + +fn _try_format_int[ + type: DType +](value: Scalar[type], radix: Int = 10, prefix: StringLiteral = "",) -> String: try: - return _format_int(int(value), 16, prefix=prefix) + return _format_int(value, radix, prefix=prefix) except e: # This should not be reachable as _format_int only throws if we pass # incompatible radix and custom digit chars, which we aren't doing @@ -53,13 +177,10 @@ fn hex[T: Intable](value: T, prefix: StringLiteral = "0x") -> String: ) -# ===----------------------------------------------------------------------===# -# Integer formatting utilities -# ===----------------------------------------------------------------------===# - - -fn _format_int( - value: Int64, +fn _format_int[ + type: DType +]( + value: Scalar[type], radix: Int = 10, digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, prefix: StringLiteral = "", @@ -73,9 +194,11 @@ fn _format_int( @always_inline -fn _write_int( +fn _write_int[ + type: DType +]( inout fmt: Formatter, - value: Int64, + value: Scalar[type], radix: Int = 10, digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, prefix: StringLiteral = "", @@ -86,9 +209,11 @@ fn _write_int( @always_inline -fn _try_write_int( +fn _try_write_int[ + type: DType +]( inout fmt: Formatter, - value: Int64, + value: Scalar[type], radix: Int = 10, digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, prefix: StringLiteral = "", @@ -103,6 +228,8 @@ fn _try_write_int( # Check that the radix and available digit characters are valid # + constrained[type.is_integral(), "Expected integral"]() + if radix < 2: return Error("Unable to format integer to string with radix < 2") @@ -151,6 +278,7 @@ fn _try_write_int( # # Stack allocate enough bytes to store any formatted 64-bit integer + # TODO: use a dynamic size when #2194 is resolved alias CAPACITY: Int = 64 var buf = InlineArray[Int8, CAPACITY](unsafe_uninitialized=True) @@ -167,7 +295,7 @@ fn _try_write_int( var remaining_int = value @parameter - fn process_digits[get_digit_value: fn () capturing -> Int64](): + fn process_digits[get_digit_value: fn () capturing -> Scalar[type]](): while remaining_int: var digit_value = get_digit_value() @@ -184,14 +312,14 @@ fn _try_write_int( if remaining_int >= 0: @parameter - fn pos_digit_value() -> Int64: + fn pos_digit_value() -> Scalar[type]: return remaining_int % radix process_digits[pos_digit_value]() else: @parameter - fn neg_digit_value() -> Int64: + fn neg_digit_value() -> Scalar[type]: return abs(remaining_int % -radix) process_digits[neg_digit_value]() diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index c2a4932d7d..bfa1b088ad 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -21,7 +21,7 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf -from builtin.hex import _try_write_int +from builtin.format_int import _try_write_int from builtin.simd import _format_scalar from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 6420ad9d1c..2e2edc6c9c 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -555,7 +555,7 @@ struct _ObjectImpl(CollectionElement, Stringable): + "'" ) if self.is_func(): - return "Function at address " + hex(self.get_as_func().value) + return "Function at address " + hex(int(self.get_as_func().value)) if self.is_list(): var res = String("[") for j in range(self.get_list_length()): diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index dc592858f3..3927be6cdb 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -223,7 +223,7 @@ struct LegacyPointer[ A String containing the hexadecimal representation of the memory location destination of this pointer. """ - return hex(self) + return hex(int(self)) @always_inline("nodebug") fn __bool__(self) -> Bool: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 6b65a62d56..e94778e672 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -344,7 +344,7 @@ struct UnsafePointer[ ](self.address) fn __str__(self) -> String: - return hex(self) + return hex(int(self)) # ===-------------------------------------------------------------------===# # Methods diff --git a/stdlib/test/builtin/test_bin.mojo b/stdlib/test/builtin/test_bin.mojo deleted file mode 100644 index c09b1d0bf9..0000000000 --- a/stdlib/test/builtin/test_bin.mojo +++ /dev/null @@ -1,55 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s - -from testing import assert_equal - - -@value -struct Ind(Indexer): - fn __index__(self) -> Int: - return 1 - - -def test_bin_scalar(): - assert_equal(bin(Int8(2)), "0b10") - assert_equal(bin(Int32(123)), "0b1111011") - assert_equal(bin(Int32(-123)), "-0b1111011") - assert_equal(bin(Scalar[DType.bool](True)), "0b1") - assert_equal(bin(Scalar[DType.bool](False)), "0b0") - - -def test_bin_int(): - assert_equal(bin(0), "0b0") - assert_equal(bin(1), "0b1") - assert_equal(bin(-1), "-0b1") - assert_equal(bin(4), "0b100") - assert_equal(bin(Int(-4)), "-0b100") - assert_equal(bin(389703), "0b1011111001001000111") - assert_equal(bin(-10), "-0b1010") - - -def test_bin_bool(): - assert_equal(bin(True), "0b1") - assert_equal(bin(False), "0b0") - - -def test_indexer(): - assert_equal(bin(Ind()), "0b1") - - -def main(): - test_bin_scalar() - test_bin_int() - test_bin_bool() - test_indexer() diff --git a/stdlib/test/builtin/test_hex.mojo b/stdlib/test/builtin/test_format_int.mojo similarity index 54% rename from stdlib/test/builtin/test_hex.mojo rename to stdlib/test/builtin/test_format_int.mojo index 938e8e7e1c..88f73faca8 100644 --- a/stdlib/test/builtin/test_hex.mojo +++ b/stdlib/test/builtin/test_format_int.mojo @@ -12,19 +12,19 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from builtin.hex import _format_int +from builtin.format_int import _format_int from testing import assert_equal fn test_format_int() raises: - assert_equal(_format_int(123), "123") - assert_equal(_format_int(4, 2), "100") - assert_equal(_format_int(255, 2), "11111111") - assert_equal(_format_int(254, 2), "11111110") - assert_equal(_format_int(255, 36), "73") + assert_equal(_format_int[DType.index](123), "123") + assert_equal(_format_int[DType.index](4, 2), "100") + assert_equal(_format_int[DType.index](255, 2), "11111111") + assert_equal(_format_int[DType.index](254, 2), "11111110") + assert_equal(_format_int[DType.index](255, 36), "73") - assert_equal(_format_int(-123, 10), "-123") - assert_equal(_format_int(-999_999_999, 10), "-999999999") + assert_equal(_format_int[DType.index](-123, 10), "-123") + assert_equal(_format_int[DType.index](-999_999_999, 10), "-999999999") # # Max and min i64 values in base 10 @@ -61,10 +61,7 @@ fn test_hex() raises: # Max and min i64 values in base 16 assert_equal(hex(Int64.MAX_FINITE), "0x7fffffffffffffff") - # # Negative values - # - assert_equal(hex(-0), "0x0") assert_equal(hex(-1), "-0x1") assert_equal(hex(-10), "-0xa") @@ -72,7 +69,52 @@ fn test_hex() raises: assert_equal(hex(Int64.MIN_FINITE), "-0x8000000000000000") + # SIMD values + assert_equal(hex(Int32(45)), "0x2d") + assert_equal(hex(Int8(2)), "0x2") + assert_equal(hex(Int8(-2)), "-0x2") + assert_equal(hex(Scalar[DType.bool](True)), "0x1") + assert_equal(hex(False), "0x0") + + +@value +struct Ind(Indexer): + fn __index__(self) -> Int: + return 1 + + +def test_bin_scalar(): + assert_equal(bin(Int8(2)), "0b10") + assert_equal(bin(Int32(123)), "0b1111011") + assert_equal(bin(Int32(-123)), "-0b1111011") + assert_equal(bin(Scalar[DType.bool](True)), "0b1") + assert_equal(bin(Scalar[DType.bool](False)), "0b0") + + +def test_bin_int(): + assert_equal(bin(0), "0b0") + assert_equal(bin(1), "0b1") + assert_equal(bin(-1), "-0b1") + assert_equal(bin(4), "0b100") + assert_equal(bin(Int(-4)), "-0b100") + assert_equal(bin(389703), "0b1011111001001000111") + assert_equal(bin(-10), "-0b1010") + + +def test_bin_bool(): + assert_equal(bin(True), "0b1") + assert_equal(bin(False), "0b0") + + +def test_indexer(): + assert_equal(bin(Ind()), "0b1") + assert_equal(hex(Ind()), "0x1") + def main(): test_format_int() test_hex() + test_bin_scalar() + test_bin_int() + test_bin_bool() + test_indexer() From c4bca9385dad024fa2bee6831bff393f41cafd2f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 31 May 2024 09:55:36 -0700 Subject: [PATCH 0782/2019] [mojo-stdlib] Add an "empty()" method to StringRef for convenience. MODULAR_ORIG_COMMIT_REV_ID: 0148ca35a8303a2037aaf158697388b8f7809c23 --- stdlib/src/utils/stringref.mojo | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index efb997d5da..790c77dddd 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -428,6 +428,15 @@ struct StringRef( """ return self.data + @always_inline + fn empty(self) -> Bool: + """Returns True if the StringRef has length = 0. + + Returns: + Whether the stringref is empty. + """ + return self.length == 0 + fn count(self, substr: StringRef) -> Int: """Return the number of non-overlapping occurrences of substring `substr` in the string. From 40361c3bd72129e5229fa505d905199940caea17 Mon Sep 17 00:00:00 2001 From: "Ehsan M. Kermani" <6980212+ehsanmok@users.noreply.github.com> Date: Fri, 31 May 2024 10:56:03 -0600 Subject: [PATCH 0783/2019] [stdlib] Construct `String` from `PythonObject`. This makes working with `PythonObject` more user friendly due to removing `Stringable` from `String` constructor. MODULAR_ORIG_COMMIT_REV_ID: b75eed03c4f73adc7321e5770a24d795551ff4fb --- stdlib/src/builtin/string.mojo | 9 +++++++++ stdlib/test/builtin/test_string.mojo | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 8c7e7cab9c..42273cd7cc 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -822,6 +822,15 @@ struct String( """ self = String(ptr.address, len) + @always_inline + fn __init__(inout self, obj: PythonObject): + """Creates a string from a python object. + + Args: + obj: A python object. + """ + self = str(obj) + @always_inline fn __copyinit__(inout self, existing: Self): """Creates a deep copy of an existing string. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index a166c51cf1..1f7d1ac965 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -26,6 +26,7 @@ from testing import ( ) from utils import StringRef +from python import Python @value @@ -90,6 +91,11 @@ fn test_constructors() raises: var s3 = String(ptr, 4) assert_equal(s3, "abc") + # Construction from PythonObject + var py = Python.evaluate("1 + 1") + var s4 = String(py) + assert_equal(s4, "2") + fn test_copy() raises: var s0 = String("find") From ad4083421a276d988781dbfd772d76ed560cc67d Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 4 Jun 2024 09:58:13 -0700 Subject: [PATCH 0784/2019] [Docs] Add page on unsafe pointers. Covers UnsafePointer and DTypePointer. MODULAR_ORIG_COMMIT_REV_ID: f70d4f5f61c57bebc8bbe64cc56cf40603b128a0 --- docs/manual/images/pointer-diagram-dark.png | Bin 0 -> 21558 bytes docs/manual/images/pointer-diagram.png | Bin 0 -> 20998 bytes docs/manual/images/pointer-lifecycle-dark.png | Bin 0 -> 82779 bytes docs/manual/images/pointer-lifecycle.png | Bin 0 -> 81374 bytes docs/manual/images/pointer-offset-dark.png | Bin 0 -> 15226 bytes docs/manual/images/pointer-offset.png | Bin 0 -> 15051 bytes .../images/strided-load-storage-dark.png | Bin 0 -> 13876 bytes docs/manual/images/strided-load-storage.png | Bin 0 -> 13569 bytes docs/manual/pointers.ipynb | 756 ++++++++++++++++++ 9 files changed, 756 insertions(+) create mode 100644 docs/manual/images/pointer-diagram-dark.png create mode 100644 docs/manual/images/pointer-diagram.png create mode 100644 docs/manual/images/pointer-lifecycle-dark.png create mode 100644 docs/manual/images/pointer-lifecycle.png create mode 100644 docs/manual/images/pointer-offset-dark.png create mode 100644 docs/manual/images/pointer-offset.png create mode 100644 docs/manual/images/strided-load-storage-dark.png create mode 100644 docs/manual/images/strided-load-storage.png create mode 100644 docs/manual/pointers.ipynb diff --git a/docs/manual/images/pointer-diagram-dark.png b/docs/manual/images/pointer-diagram-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fe55eb78ff1833f9d96cf011e1d6bd7273dcd9ff GIT binary patch literal 21558 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 WKtyzfdJPfqM@vl)_Ce)Q$o~VYQC747 literal 0 HcmV?d00001 diff --git a/docs/manual/images/strided-load-storage-dark.png b/docs/manual/images/strided-load-storage-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e72ad2034cad6dadd638b286b8c9996c988aeb8f GIT binary patch literal 13876 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 AqyPW_ literal 0 HcmV?d00001 diff --git a/docs/manual/images/strided-load-storage.png b/docs/manual/images/strided-load-storage.png new file mode 100644 index 0000000000000000000000000000000000000000..9aea48055d1a26b071ef6cd67b86bbfa3f31d081 GIT binary patch literal 13569 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 literal 0 HcmV?d00001 diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb new file mode 100644 index 0000000000..07c2d6e575 --- /dev/null +++ b/docs/manual/pointers.ipynb @@ -0,0 +1,756 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Unsafe pointers\n", + "description: Using unsafe pointers to access dynamically-allocated memory.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type \n", + "creates an indirect reference to a location in memory.\n", + "You can use an `UnsafePointer` to dynamically allocate and free memory, or to\n", + "point to memory allocated by some other piece of code. You can use these\n", + "pointers to write code that interacts with low-level interfaces, to interface\n", + "with other programming languages, or to build certain kinds of data structures.\n", + "But as the name suggests, they're inherently _unsafe_. For example, when using\n", + "unsafe pointers, you're responsible for ensuring that memory gets allocated and\n", + "freed correctly.\n", + "\n", + ":::note \n", + "\n", + "In addition to unsafe pointers, Mojo supports a safe \n", + "[`Reference`](/mojo/stdlib/memory/reference/Reference) type. See\n", + "[`UnsafePointer` and `Reference`](#unsafepointer-and-reference) for a brief\n", + "comparison of the types.\n", + "\n", + ":::\n", + "\n", + "## What is a pointer?\n", + "\n", + "An `UnsafePointer` is a type that holds an address to memory. You can store\n", + "and retrieve values in that memory. The `UnsafePointer` type is _generic_—it can \n", + "point to any type of value, and the value type is specified as a parameter. The\n", + "value pointed to by a pointer is sometimes called a _pointee_." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, initialize_pointee_move\n", + "\n", + "# Allocate memory to hold a value\n", + "var ptr = UnsafePointer[Int].alloc(1)\n", + "# Initialize the allocated memory\n", + "initialize_pointee_copy(ptr, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "\n", + " ![](./images/pointer-diagram.png#light)\n", + " ![](./images/pointer-diagram-dark.png#dark)\n", + "\n", + "
Figure 1. Pointer and pointee
\n", + "
\n", + "\n", + "\n", + "Accessing the memory—to retrieve or update a value—is called \n", + "_dereferencing_ the pointer. You can dereference a pointer by following the\n", + "variable name with an empty pair of square brackets:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "110\n" + ] + } + ], + "source": [ + "# Update an initialized value\n", + "ptr[] += 10\n", + "# Access an initialized value\n", + "print(ptr[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also allocate memory to hold multiple values to build array-like\n", + "structures. For details, see \n", + "[Storing multiple values](#storing-multiple-values)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Lifecycle of a pointer\n", + "\n", + "At any given time, a pointer can be in one of several states:\n", + "\n", + "- Uninitialized. Just like any variable, a variable of type `UnsafePointer` can\n", + " be declared but uninitialized.\n", + "\n", + " \n", + " ```mojo\n", + " var ptr: UnsafePointer[Int]\n", + " ```\n", + "\n", + "- Null. A null pointer has an address of 0, indicating an invalid pointer.\n", + "\n", + " ```mojo\n", + " ptr = UnsafePointer[Int]()\n", + " ```\n", + "\n", + "- Pointing to allocated, uninitialized memory. The `alloc()` static method\n", + " returns a pointer to a newly-allocated block of memory with space for the \n", + " specified number of elements of the pointee's type.\n", + "\n", + " ```mojo\n", + " ptr = UnsafePointer[Int].alloc(1)\n", + " ```\n", + " Trying to dereference a pointer to uninitialized memory results in undefined \n", + " behavior. \n", + "\n", + "- Pointing to initialized memory. You can initialize an allocated, uninitialized\n", + " pointer by moving or copying an existing value into the memory. Or you can use\n", + " the `address_of()` static method to get a pointer to an existing value. \n", + "\n", + " ```mojo\n", + " initialize_pointee_copy(ptr, value)\n", + " # or\n", + " initalize_pointee_move(ptr, value^)\n", + " # or \n", + " ptr = UnsafePointer[Int].address_of(value)\n", + " ```\n", + " \n", + " Once the value is initialized, you can read or mutate it using the dereference\n", + " syntax: \n", + "\n", + " ```mojo\n", + " oldValue = ptr[]\n", + " ptr[] = newValue\n", + " ```\n", + "\n", + "- Dangling. When you free the pointer's allocated memory, you're left with a \n", + " _dangling pointer_. The address still points to its previous location, but the\n", + " memory is no longer allocated to this pointer. Trying to dereference the\n", + " pointer, or calling any method that would access the memory location results\n", + " in undefined behavior.\n", + "\n", + " ```mojo\n", + " ptr.free()\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "The following diagram shows the lifecycle of an `UnsafePointer`:\n", + "\n", + "
\n", + "\n", + " ![](./images/pointer-lifecycle.png#light)\n", + " ![](./images/pointer-lifecycle-dark.png#dark)\n", + "\n", + "
Figure 2. Lifecycle of an UnsafePointer
\n", + "
\n", + "\n", + "### Allocating memory\n", + "\n", + "Use the static `alloc()` method to allocate memory. The method returns a new\n", + "pointer pointing to the requested memory. You can allocate space for one or \n", + "more values of the pointee's type.\n", + "\n", + "```mojo\n", + "ptr = UnsafePointer[Int].alloc(10) # Allocate space for 10 Int values\n", + "```\n", + "\n", + "The allocated space is _uninitialized_—like a variable that's been declared but\n", + "not initialized.\n", + "\n", + "### Initializing the pointee\n", + "\n", + "The `unsafe_pointer` module includes a number of free functions for working with\n", + "the `UnsafePointer` type. To initialize allocated memory, you can use the \n", + "[`initialize_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy)\n", + "or [`initialize_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move)\n", + "functions:\n", + "\n", + "```mojo\n", + "initialize_pointee_copy(ptr, 5)\n", + "```\n", + "\n", + "To move a value into the pointer's memory location, use\n", + "`initialize_pointee_move()`:\n", + "\n", + "```mojo\n", + "initialize_pointee_move(str_ptr, my_string^)\n", + "```\n", + "\n", + "Note that to move the value, you usually need to add the transfer operator\n", + "(`^`), unless the value is a [trivial\n", + "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", + "`Int`) or a newly-constructed, \"owned\" value:\n", + "\n", + "```mojo\n", + "initialize_pointee_move(str_ptr, str(\"Owned string\"))\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternately, you can get a pointer to an existing value using the static \n", + "`address_of()` method. This is useful for getting a pointer to a value on the \n", + "stack, for example." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "var counter: Int = 5\n", + "ptr = UnsafePointer[Int].address_of(counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that when calling `address_of()`, you don't need to allocate memory ahead\n", + "of time, since you're pointing to an existing value." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "#### Initializing from an address\n", + "\n", + "When exchanging data with other programming languages, you may need to construct\n", + "an `UnsafePointer` from an address. For example, if you're working with a\n", + "pointer allocated by a C or C++ library, or a Python object that implements the\n", + "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n", + "you can construct an `UnsafePointer` to access the data from the Mojo side.\n", + "\n", + "You can construct an `UnsafePointer` from an integer address using the `address`\n", + "keyword argument. For example, the following code creates a NumPy array and then\n", + "accesses the data using a Mojo pointer:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, 3, 4, 5, 6, 7, 8, 9, " + ] + } + ], + "source": [ + "from python import Python\n", + "from memory.unsafe_pointer import UnsafePointer\n", + "\n", + "def share_array():\n", + " np = Python.import_module(\"numpy\")\n", + " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", + " addr = int(arr.__array_interface__[\"data\"][0])\n", + " ptr = UnsafePointer[Int64](address=addr)\n", + " for i in range(9):\n", + " print(ptr[i], end=\", \")\n", + "\n", + "share_array()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When dealing with memory allocated elsewhere, you need to be aware of who's\n", + "responsible for freeing the memory. Freeing memory allocated elsewhere \n", + "can result in undefined behavior.\n", + "\n", + "You also need to be aware of the format of the data stored in memory, including\n", + "data types and byte order. For more information, see \n", + "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Dereferencing pointers\n", + "\n", + "Use the `[]` dereference operator to access the value stored at a pointer (the\n", + " \"pointee\").\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "110\n" + ] + } + ], + "source": [ + "# Read from pointee\n", + "print(ptr[])\n", + "# mutate pointee\n", + "ptr[] = 0\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "If you've allocated space for multiple values, you can use subscript syntax to\n", + "access the values, as if they were an array, like `ptr[3]`. The empty subscript\n", + "`[]` has the same meaning as `[0]`.\n", + "\n", + ":::caution\n", + "\n", + "The dereference operator assumes that the memory being dereferenced is \n", + "initialized. Dereferencing uninitialized memory results in undefined behavior.\n", + "\n", + ":::\n", + "\n", + "You cannot safely use the dereference operator on uninitialized memory, even to\n", + "_initialize_ a pointee. This is because assigning to a dereferenced pointer\n", + "calls lifecycle methods on the existing pointee (such as the destructor, move\n", + "constructor or copy constructor).\n", + "\n", + "```mojo\n", + "str_ptr = UnsafePointer[String].alloc(1)\n", + "# str_ptr[] = \"Testing\" # Undefined behavior!\n", + "initialize_pointee_move(str_ptr, \"Testing\")\n", + "str_ptr[] += \" pointers\" # Works now\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Destroying or removing values\n", + "\n", + "The \n", + "[`move_from_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee)\n", + "function moves the pointee from the memory location pointed to by `ptr`. This is\n", + "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n", + "the memory location uninitialized.\n", + "\n", + "The [`destroy_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", + "function calls the destructor on the pointee, and leaves the memory location\n", + "pointed to by `ptr` uninitialized. \n", + "\n", + "Both `move_from_pointee()` and `destroy_pointee()` require that the pointer is \n", + "non-null, and the memory location contains a valid, initialized value of the \n", + "pointee's type; otherwise the function results in undefined behavior.\n", + "\n", + "The [`move_pointee(src, dst)`](/mojo/stdlib/memory/unsafe_pointer/move_pointee)\n", + "function moves the pointee from one pointer location to another. Both pointers\n", + "must be non-null. The source location must contain a valid, initialized value of \n", + "the pointee's type, and is left uninitialized after the call. The destination \n", + "location is assumed to be uninitialized—if it contains a valid value, that\n", + "value's destructor is not run. The value from the source location is moved to\n", + "the destination location as a consuming move. This function also has undefined\n", + "behavior if any of its prerequisites is not met." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Freeing memory\n", + "\n", + "Calling [`free()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#free) on a \n", + "pointer frees the memory allocated by the pointer. It doesn't call the \n", + "destructors on any values stored in the memory—you need to do that explicitly\n", + "(for example, using\n", + "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) or\n", + "one of the other functions described in \n", + "[Destroying or removing values](#destroying-or-removing-values)).\n", + "\n", + "Disposing of a pointer without freeing the associated memory can result in a\n", + "memory leak—where your program keeps taking more and more memory, because not\n", + "all allocated memory is being freed.\n", + "\n", + "On the other hand, if you have multiple copies of a pointer accessing the same\n", + "memory, you need to make sure you only call `free()` on one of them. Freeing the\n", + "same memory twice is also an error.\n", + "\n", + "After freeing a pointer's memory, you're left with a dangling pointer—its\n", + "address still points to the freed memory. Any attempt to access the memory,\n", + "like dereferencing the pointer results in undefined behavior.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Storing multiple values\n", + "\n", + "As mentioned in [Allocating memory](#allocating-memory), you can use an \n", + "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n", + "in as single, contiguous block. Pointers support arithmetic: adding an integer\n", + "to a pointer returns a new pointer offset by the specified number of values from\n", + "the original pointer:\n", + "\n", + "```mojo\n", + "third_ptr = first_ptr + 2\n", + "```\n", + "\n", + "Pointers also support subtraction, as well as in-place addition and subtraction:\n", + "\n", + "```mojo\n", + "# Advance the pointer one element:\n", + "ptr += 1\n", + "```\n", + "\n", + "
\n", + "\n", + " ![](./images/pointer-offset.png#light)\n", + " ![](./images/pointer-offset-dark.png#dark)\n", + "\n", + "
Figure 3. Pointer arithmetic
\n", + "
\n", + "\n", + "For example, the following example allocates memory to store 6 `Float64`\n", + "values, and initializes them all to zero." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "float_ptr = UnsafePointer[Float64].alloc(6)\n", + "for offset in range(6):\n", + " initialize_pointee_copy(float_ptr+offset, 0.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once the values are initialized, you can access them using subscript syntax:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0, 0.0, 3.0, 0.0, 0.0, 0.0, " + ] + } + ], + "source": [ + "float_ptr[2] = 3.0\n", + "for offset in range(6):\n", + " print(float_ptr[offset], end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Converting data: bitcasting and byte order\n", + "\n", + "Bitcasting a pointer returns a new pointer that has the same memory location,\n", + "but a new data type. This can be useful if you need to access different types of\n", + "data from a single area of memory. This can happen when you're reading binary\n", + "files, like image files, or receiving data over the network.\n", + "\n", + "The following sample processes a format that consists of chunks of data,\n", + "where each chunk contains a variable number of 32-bit integers.\n", + "Each chunk begins with an 8-bit integer that identifies the number of values\n", + "in the chunk." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def read_chunks(owned ptr: UnsafePointer[UInt8]) -> List[List[UInt32]]:\n", + " chunks = List[List[UInt32]]()\n", + " # A chunk size of 0 indicates the end of the data\n", + " chunk_size = int(ptr[])\n", + " while (chunk_size > 0):\n", + " # Skip the 1 byte chunk_size and get a pointer to the first\n", + " # UInt32 in the chunk\n", + " ui32_ptr = (ptr + 1).bitcast[UInt32]()\n", + " chunk = List[UInt32](capacity=chunk_size)\n", + " for i in range(chunk_size):\n", + " chunk.append(ui32_ptr[i])\n", + " chunks.append(chunk)\n", + " # Move our pointer to the next byte after the current chunk\n", + " ptr += (1 + 4 * chunk_size)\n", + " # Read the size of the next chunk\n", + " chunk_size = int(ptr[])\n", + " return chunks" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When dealing with data read in from a file or from the network, you may also\n", + "need to deal with byte order. Most systems use little-endian byte order (also\n", + "called least-signficicant byte, or LSB) where the least-significant byte in a\n", + "multibyte value comes first. For example, the number 1001 can be represented in\n", + "hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a\n", + "16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit \n", + "integer, it would be represented as E9 03 00 00. \n", + "\n", + "Big-endian or most-significant byte (MSB) ordering is the opposite: in the \n", + "32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and\n", + "when transmitting data over the network. You can use the \n", + "[`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap) function to swap the byte\n", + "order of a SIMD value from big-endian to little-endian or the reverse. For\n", + "example, if the method above was reading big-endian data, you'd just need to\n", + "change a single line:\n", + "\n", + "```mojo\n", + "chunk.append(byte_swap(ui32_ptr[i]))\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `DTypePointer`: handling numeric data\n", + "\n", + "The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", + "pointer that supports some additional methods for loading and storing numeric\n", + "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n", + "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n", + "[SIMD and DType](/mojo/manual/types#simd-and-dtype).\n", + "\n", + "`DTypePointer` has a similar API to `UnsafePointer`:\n", + "\n", + "- You can [`alloc()`](/mojo/stdlib/memory/unsafe/DTypePointer#alloc) and\n", + " [`free()`](/mojo/stdlib/memory/unsafe/DTypePointer#free) memory, or use \n", + " [`address_of()`](/mojo/stdlib/memory/unsafe/DTypePointer#address_of) to point\n", + " to an existing value.\n", + "- The pointer supports pointer arithmetic to access adjacent memory locations.\n", + "- You can dereference a `DTypePointer` using subscript notation.\n", + "- You can construct a `DTypePointer` from an `Int` address.\n", + "\n", + "\n", + "You can also construct a `DTypePointer` from an `UnsafePointer` of a scalar\n", + "type like `Int64` or `Float32`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from memory import DTypePointer, UnsafePointer\n", + "\n", + "uptr = UnsafePointer[Float64].alloc(10)\n", + "dptr = DTypePointer(uptr)\n", + "# Or:\n", + "dptr = DTypePointer[DType.float64].alloc(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unlike `UnsafePointer`, `DTypePointer` doesn't have special methods to\n", + "initialize values, destroy them, or move them out. Because all of the values\n", + "that `DTypePointer` works with are trivial types, `DTypePointer` doesn't need to\n", + "destroy values before overwriting them or freeing memory. Instead, you can use\n", + "subscript notation (like `UnsafePointer`) or use the\n", + "[`load()`](/mojo/stdlib/memory/unsafe/DTypePointer#load) and \n", + "[`store()`](/mojo/stdlib/memory/unsafe/DTypePointer#store) methods to access \n", + "values.\n", + "\n", + "What `DTypePointer` adds is various methods of loading and storing SIMD values\n", + "to memory. In particular: strided load/store and gather/scatter.\n", + "\n", + "Strided load loads values from memory into a SIMD vector using an offset (the\n", + "\"stride\") between successive memory addresses. This can be useful for\n", + "extracting rows or columns from tabular data, or for extracting individual\n", + "values from structured data. For example, consider the data for an RGB image,\n", + "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n", + "you want to access just the red values, you can use a strided load or store.\n", + "\n", + "
\n", + "\n", + " ![](./images/strided-load-storage.png#light)\n", + " ![](./images/strided-load-storage-dark.png#dark)\n", + "\n", + "
Figure 4. Strided load
\n", + "
\n", + "\n", + "The following function uses the \n", + "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_load)\n", + "and \n", + "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n", + "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", + "that this function only handles images with where the number of pixels is evenly\n", + "divisible by eight.)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def invert_red_channel(ptr: DTypePointer[DType.uint8], pixel_count: Int):\n", + " # number of values loaded or stored at a time\n", + " alias simd_width = 8\n", + " # bytes per pixel, which is also the stride size\n", + " bpp = 3\n", + " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", + " red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n", + " # Invert values and store them in their original locations\n", + " ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note Future of `DTypePointer`\n", + "\n", + "The `DTypePointer` type exists for historical reasons, but it no longer really\n", + "needs to be a separate type. `UnsafePointer` can handle most things that\n", + "`DTypePointer` does except for a few features related to reading and writing\n", + "`SIMD` values. At some point in the future, these features will probably be\n", + "integrated into the `SIMD` type, so you can use them with `UnsafePointer`.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Safety\n", + "\n", + "Unsafe pointers are unsafe for several reasons:\n", + "\n", + "- Memory management is up to the user. You need to manually allocate\n", + " and free memory, and be aware of when other APIs are allocating or freeing\n", + " memory for you.\n", + "\n", + "- `UnsafePointer` and `DTypePointer` values are _nullable_—that is, the pointer\n", + " is not guaranteed to point to anything. And even when a pointer points to\n", + " allocated memory, that memory may not be _initialized_.\n", + "\n", + "- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`.\n", + " When you use an `UnsafePointer`, managing memory and knowing when to destroy\n", + " objects is your responsibility. (Since `DTypePointer` only works with trivial\n", + " types, this is not typically an issue for `DTypePointer`.)\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `UnsafePointer` and `Reference`\n", + "\n", + "The [`Reference`](/mojo/stdlib/memory/reference/Reference) type is essentially a \n", + "safe pointer type. Like a pointer, you can derferences a `Reference` using the \n", + "dereference operator, `[]`. However, the `Reference` type has several\n", + "differences from `UnsafePointer` which make it safer:\n", + "\n", + "- A `Reference` is _non-nullable_. A reference always points to something.\n", + "- You can't allocate or free memory using a `Reference`—only point to an\n", + " existing value.\n", + "- A `Reference` only refers to a single value. You can't do pointer arithmetic\n", + " with a `Reference`.\n", + "- A `Reference` has an associated _lifetime_, which connects it back to an\n", + " original, owned value. The lifetime ensures that the value won't be destroyed\n", + " while the reference exists.\n", + "\n", + "The `Reference` type shouldn't be confused with the immutable and mutable\n", + "references used with the `borrowed` and `inout` argument conventions. Those\n", + "references do not require explicit dereferencing, unlike a `Reference` or \n", + "`UnsafePointer`." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 7b21ffc4a040dc3d65eeff7baa1eea49f499e3f7 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 4 Jun 2024 10:04:58 -0700 Subject: [PATCH 0785/2019] Python can now initialize using the Python on top of PATH, without referring to the python_lib path in modular.cfg set on install. This will allow you to activate a virtual environment like conda, install packages, and access them from Mojo without setting environment variables. MODULAR_ORIG_COMMIT_REV_ID: e908fdc86a52e5cccb529bc2704c24f686f8c940 --- stdlib/src/python/_cpython.mojo | 102 ++++++++++++-------------------- stdlib/src/python/python.mojo | 5 +- stdlib/src/sys/ffi.mojo | 22 +++++++ 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 0bc041869e..be87e8e478 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from os import getenv -from sys import external_call +from sys import external_call, exit from sys.ffi import DLHandle from memory import DTypePointer, UnsafePointer @@ -86,32 +86,6 @@ struct PythonVersion: return PythonVersion(components[0], components[1], components[2]) -fn _py_initialize(inout cpython: CPython): - # Configure the Python interpreter to search for libraries installed in - # "site-package" directories. If we don't do this, and a `virtualenv` - # is activated when invoking `mojo`, then the Python interpreter will - # use the system's `site-package` directories instead of the - # `virtualenv` ones. - # - # This function does not overwrite any existing `PYTHONPATH`, in case - # the user wants to specify this environment variable themselves. - # - # Finally, another approach is to use `Py_SetPath()`, but that requires - # setting explicitly every library search path, as well as needing to - # specify a `PYTHONHOME`. This is much more complicated and restrictive - # to be considered a better solution. - var error_message: StringRef = external_call[ - "KGEN_CompilerRT_Python_SetPythonPath", - DTypePointer[DType.int8], - ]() - if len(error_message) != 0: - # Print the error message, but keep going in order to allow - # `Py_Initialize` to fail. - print("Mojo/Python interoperability error: ", error_message) - - cpython.lib.get_function[fn () -> None]("Py_Initialize")() - - fn _py_get_version(lib: DLHandle) -> StringRef: var version_string = lib.get_function[fn () -> DTypePointer[DType.int8]]( "Py_GetVersion" @@ -119,32 +93,6 @@ fn _py_get_version(lib: DLHandle) -> StringRef: return StringRef(version_string) -fn _py_initialize(lib: DLHandle): - # Configure the Python interpreter to search for libraries installed in - # "site-package" directories. If we don't do this, and a `virtualenv` - # is activated when invoking `mojo`, then the Python interpreter will - # use the system's `site-package` directories instead of the - # `virtualenv` ones. - # - # This function does not overwrite any existing `PYTHONPATH`, in case - # the user wants to specify this environment variable themselves. - # - # Finally, another approach is to use `Py_SetPath()`, but that requires - # setting explicitly every library search path, as well as needing to - # specify a `PYTHONHOME`. This is much more complicated and restrictive - # to be considered a better solution. - var error_message: StringRef = external_call[ - "KGEN_CompilerRT_Python_SetPythonPath", - DTypePointer[DType.int8], - ]() - if len(error_message) != 0: - # Print the error message, but keep going in order to allow - # `Py_Initialize` to fail. - print("Mojo/Python interoperability error: ", error_message) - - lib.get_function[fn () -> None]("Py_Initialize")() - - fn _py_finalize(lib: DLHandle): lib.get_function[fn () -> None]("Py_Finalize")() @@ -156,28 +104,42 @@ struct CPython: var logging_enabled: Bool var version: PythonVersion var total_ref_count: UnsafePointer[Int] + var init_error: StringRef fn __init__(inout self: CPython): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") + print("MOJO_PYTHON:", getenv("MOJO_PYTHON")) + print("MOJO_PYTHON_LIBRARY:", getenv("MOJO_PYTHON_LIBRARY")) + + # TODO(MOCO-772) Allow raises to propagate through function pointers + # and make this initialization a raising function. + self.init_error = external_call[ + "KGEN_CompilerRT_Python_SetPythonPath", + DTypePointer[DType.int8], + ]() + var python_lib = getenv("MOJO_PYTHON_LIBRARY") - if python_lib == "": - abort( - "Mojo/Python interoperability error: Unable to locate a" - " suitable libpython, please set `MOJO_PYTHON_LIBRARY`" - ) + + if logging_enabled: + print("PYTHONEXECUTABLE:", getenv("PYTHONEXECUTABLE")) + print("libpython selected:", python_lib) self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) self.none_value = PyObjectPtr() self.dict_type = PyObjectPtr() self.logging_enabled = logging_enabled - self.version = PythonVersion(_py_get_version(self.lib)) - - _py_initialize(self.lib) - _ = self.Py_None() - _ = self.PyDict_Type() + if not self.init_error: + if not self.lib.check_symbol("Py_Initialize"): + self.init_error = "compatible Python library not found" + self.lib.get_function[fn () -> None]("Py_Initialize")() + self.version = PythonVersion(_py_get_version(self.lib)) + _ = self.Py_None() + _ = self.PyDict_Type() + else: + self.version = PythonVersion(0, 0, 0) @staticmethod fn destroy(inout existing: CPython): @@ -194,6 +156,19 @@ struct CPython: existing.lib.close() existing.total_ref_count.free() + fn check_init_error(self) raises: + """Used for entry points that initialize Python on first use, will + raise an error if one occured when initializing the global CPython. + """ + if self.init_error: + var error: String = self.init_error + error += "\nMOJO_PYTHON: " + getenv("MOJO_PYTHON") + error += "\nMOJO_PYTHON_LIBRARY: " + getenv("MOJO_PYTHON_LIBRARY") + error += "\nPYTHONEXECUTABLE: " + getenv("PYTHONEXECUTABLE") + error += "\n\nMojo/Python interop error, troubleshooting docs at:" + error += "\n https://modul.ar/fix-python\n" + raise error + fn Py_None(inout self) -> PyObjectPtr: """Get a None value, of type NoneType.""" if self.none_value.is_null(): @@ -217,6 +192,7 @@ struct CPython: self.logging_enabled = existing.logging_enabled self.version = existing.version self.total_ref_count = existing.total_ref_count + self.init_error = existing.init_error fn _inc_total_rc(inout self): var v = move_from_pointee(self.total_ref_count) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 557dc09dd3..762f38f6fe 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -21,9 +21,8 @@ from python import Python from sys import external_call, sizeof from sys.ffi import _get_global - +from os.env import getenv from memory import UnsafePointer - from utils import StringRef from ._cpython import CPython, Py_eval_input, Py_file_input @@ -199,6 +198,8 @@ struct Python: The Python module. """ var cpython = _get_global_python_itf().cpython() + # Throw error if it occured during initialization + cpython.check_init_error() var module_maybe = cpython.PyImport_ImportModule(module) Python.throw_python_exception_if_error_state(cpython) return PythonObject(module_maybe) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 6ffb5ca9b0..3da6bd0a62 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -69,6 +69,28 @@ struct DLHandle(CollectionElement, Boolable): else: self.handle = DTypePointer[DType.int8]() + fn check_symbol(self, name: String) -> Bool: + """Check that the symbol exists in the dynamic library. + + Args: + name: The symbol to check. + + Returns: + `True` if the symbol exists. + """ + constrained[ + not os_is_windows(), + "Checking dynamic library symbol is not supported on Windows", + ]() + + var opaque_function_ptr = external_call[ + "dlsym", DTypePointer[DType.int8] + ](self.handle.address, name.unsafe_ptr()) + if opaque_function_ptr: + return True + + return False + # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline fn close(inout self): From 7d3fc400fc25f0f02b59675805f1afb02e1f5968 Mon Sep 17 00:00:00 2001 From: Yihua Lou <1237500+yihualou@users.noreply.github.com> Date: Wed, 5 Jun 2024 04:49:53 -0700 Subject: [PATCH 0786/2019] Internal commit MODULAR_ORIG_COMMIT_REV_ID: ea711f042818e6515f12b278d04d38fd68c8c6de --- stdlib/src/python/_cpython.mojo | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index be87e8e478..6417193efe 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -218,11 +218,17 @@ struct CPython: self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) self._dec_total_rc() - fn PyGILState_Ensure(inout self) -> Int64: - return self.lib.get_function[fn () -> Int64]("PyGILState_Ensure")() + fn PyGILState_Ensure(inout self) -> Bool: + return self.lib.get_function[fn () -> Bool]("PyGILState_Ensure")() - fn PyGILState_Release(inout self, state: Int64): - self.lib.get_function[fn (Int64) -> None]("PyGILState_Release")(state) + fn PyGILState_Release(inout self, state: Bool): + self.lib.get_function[fn (Bool) -> None]("PyGILState_Release")(state) + + fn PyEval_SaveThread(inout self) -> Int64: + return self.lib.get_function[fn () -> Int64]("PyEval_SaveThread")() + + fn PyEval_RestoreThread(inout self, state: Int64): + self.lib.get_function[fn (Int64) -> None]("PyEval_RestoreThread")(state) # This function assumes a specific way PyObjectPtr is implemented, namely # that the refcount has offset 0 in that structure. That generally doesn't From d44d0d26175a1369d46f056f9c14ff041cc0f526 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 5 Jun 2024 14:26:47 -0700 Subject: [PATCH 0787/2019] [cherrypick][docs] Cherrypick the changelogs for 24.4 (#41426) All the changelog edits thus far. MODULAR_ORIG_COMMIT_REV_ID: 5a72140a3d45a12146e71121c6896c349d9dda52 --- docs/changelog-released.md | 664 +++++++++++++++++++++++++++++++++++++ docs/changelog.md | 658 +----------------------------------- 2 files changed, 673 insertions(+), 649 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index faa318fa04..783135293e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,6 +25,670 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## UNRELEASED + +### 🔥 Legendary + +### ⭐️ New + +- Add a `sort` function for list of `ComparableCollectionElement`s. + [PR #2609](https://github.com/modularml/mojo/pull/2609) by + [@mzaks](https://github.com/mzaks) + +- Mojo functions can return an auto-dereferenced refeference to storage with a + new `ref` keyword in the result type specifier. For example: + + ```mojo + struct Pair: + var first: Int + var second: Int + fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + return self.first + fn show_mutation(): + var somePair = ... + get_first_ref(somePair) = 1 + ``` + + This approach provides a general way to return an "automatically dereferenced" + reference of a given type. Notably, this eliminates the need for + `__refitem__` to exist. `__refitem__` has thus been removed and replaced with + `__getitem__` that returns a reference. + +- Mojo has introduced `@parameter for`, a new feature for compile-time + programming. `@parameter for` defines a for loop where the sequence and the + induction values in the sequence must be parameter values. For example: + + ```mojo + fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") + ``` + + Currently, `@parameter for` requires the sequence's `__iter__` method to + return a `_StridedRangeIterator`, meaning the induction variables must be + `Int`. The intention is to lift these restrictions in the future. + +- Mojo added support for the inferred parameters. Inferred parameters must + appear at the beginning of the parameter list and cannot be explicitly + specified by the user. They are declared to the left of a `//` marker, much + like positional-only parameters. This allows programmers to define functions + with dependent parameters to be called without the caller specifying all the + necessary parameters. For example: + + ```mojo + fn parameter_simd[dt: DType, //, value: Scalar[dt]](): + print(value) + + fn call_it(): + parameter_simd[Int32(42)]() + ``` + + In the above example, `Int32(42)` is passed directly into `value`, the first + non-inferred parameter. `dt` is inferred from the parameter itself to be + `DType.int32`. + + This also works with structs. For example: + + ```mojo + struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: + pass + + fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` + pass + ``` + + This should make working with dependent parameters more ergonomic. + +- Mojo now allows functions overloaded on parameters to be resolved when forming + references to, but not calling, those functions. For example, the following + now works: + + ```mojo + fn overloaded_parameters[value: Int32](): + pass + + fn overloaded_parameters[value: Float32](): + pass + + fn form_reference(): + alias ref = overloaded_parameters[Int32()] # works! + ``` + +- Mojo now supports adding a `@deprecated` decorator on structs, functions, + traits, aliases, and global variables. The decorator marks the attached decl + as deprecated and causes a warning to be emitted when the deprecated decl is + referenced in user code. The decorator requires a deprecation message to be + specified as a string literal. + + ```mojo + @deprecated("Foo is deprecated, use Bar instead") + struct Foo: + pass + + fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead + pass + + @deprecated("use another function!") + fn bar(): + pass + + fn techdebt(): + bar() # warning: use another function! + ``` + +- Mojo has changed how `def` arguments are processed. Previously, by default, + arguments to a `def` were treated treated according to the `owned` convention, + which makes a copy of the value, enabling that value to be mutable in the callee. + This "worked", but was a major performance footgun, and required you to declare + non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: + it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local + copy of the value **only if the argument is mutated** in the body of the function. + This improves consistency, performance, and ease of use. + +- `int()` can now take a string and a specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with the + base determined by whether the string contains the prefix `"0x"`, `"0o"`, or + `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2274](https://github.com/modularml/mojo/issues/2274)) + +- Mojo now supports types to opt in to use the `abs` and `round` functions by + implementing the `__abs__` and `__round__` methods (i.e. by conforming to the + new `Absable` and `Roundable` traits), respectively, e.g.: + + ```mojo + from math import sqrt + + @value + struct Complex(Absable, Roundable): + var re: Float64 + var im: Float64 + + fn __abs__(self) -> Self: + return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) + + fn __round__(self) -> Self: + return Self(round(self.re), round(self.im)) + ``` + +- User defined types can now also opt in to use the `pow` function by + implementing the `__pow__` method (and thus conforming to the new `Powable` + trait). As before, these types will also benefit from being able to use the + `**` operator. + +- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` + functions in the `math` module by implementing the `__floor__()`, + `__ceil__()`, and `__trunc__()` methods (and so conforming to the new + `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). + For example: + + ```mojo + from math import Ceilable, Floorable, Truncable, ceil, floor, trunc + + @value + struct Complex(Ceilable, Floorable, Truncable): + var re: Float64 + var im: Float64 + + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) + + fn __floor__(self) -> Self: + return Self(floor(re), floor(im)) + + fn __trunc__(self) -> Self: + return Self(trunc(re), trunc(im)) + ``` + +- You can now use the builtin `any()` and `all()` functions to check for truthy + elements in a collection. Because `SIMD.__bool__()` is now constrained to + `size=1`, You must explicity use these to get the truthy value of a SIMD + vector. This avoids common bugs around implicit conversion of `SIMD` to + `Bool`. + ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) + + For example: + + ```mojo + fn truthy_simd(): + var vec = SIMD[DType.int32, 4](0, 1, 2, 3) + if any(vec): + print("any elements are truthy") + if all(vec): + print("all elements are truthy") + ``` + +- Add an `InlinedArray` type that works on memory-only types. + Compare with the existing `StaticTuple` type, which is conceptually an array + type, but only worked on `AnyTrivialRegType`. + ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) + +- Base64 decoding support has been added. + ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) + +- Add Base16 encoding and decoding support. + ([PR #2584](https://github.com/modularml/mojo/pull/2584) + by [@kernhanda](https://github.com/kernhanda)) + +- Add `repr()` function and `Representable` trait. + ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Add `SIMD.shuffle()` with `StaticIntTuple` mask. + ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) + +- Invoking `mojo package my-package -o my-dir` on the command line, where + `my-package` is a Mojo package source directory, and `my-dir` is an existing + directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. + Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. + +- The Mojo Language Server now reports a warning when a local variable is unused. + +- Implicit variable definitions in a `def` are more flexible: you can now + implicitly declare variables as the result of a tuple return, using + `a,b,c = foo()`, and can now shadow global immutable symbols using + `slice = foo()` without getting a compiler error. + +- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that + allow users to opt into the `math.ceildiv` function. + +- Mojo now allows methods to declare `self` as a `Reference` directly, which + can be useful for advanced cases of parametric mutabilty and custom lifetime + processing. Previously it required the use of an internal MLIR type to + achieve this. + +- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, + not a low-level `__mlir_type.i1` value. + + This improves the ergonomics of spelling out a + `Reference` type explicitly. For example, to define a struct holding a + `Reference`, you can now write: + + ```mojo + struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: + var data: Reference[Int32, is_mutable, lifetime] + ``` + + Or to specify a field that is always immutable, `False` can be specified + as the mutability: + + ```mojo + struct Foo[lifetime: AnyLifetime[False].type]: + var data: Reference[Int32, False, lifetime] + ``` + +- `object` now implements all the bitwise operators. + ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) + +- A new `--validate-doc-strings` option has been added to `mojo` to emit errors + on invalid doc strings instead of warnings. + +- Several `mojo` subcommands now support a `--diagnostic-format` option that + changes the format with which errors, warnings, and other diagnostics are + printed. By specifying `--diagnostic-format json` on the command line, errors + and other diagnostics will be output in a structured + [JSON Lines](https://jsonlines.org) format that is easier for machines to + parse. + + The full list of subcommands that support `--diagnostic-format` is as follows: + `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. + Further, the `mojo test --json` option has been subsumed into this new option; + for the same behavior, run `mojo test --diagnostic-format json`. + + Note that the format of the JSON output may change; we don't currently + guarantee its stability across releases of Mojo. + +- A new decorator, `@doc_private`, was added that can be used to hide a decl + from being generated in the output of `mojo doc`. It also removes the + requirement that the decl has documentation (e.g. when used with + --diagnose-missing-doc-strings). + +- Added a new `Span` type for taking slices of contiguous collections. + ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) + +- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type + in standard library code. + + `StringSlice` is a non-owning reference to encoded string data. Unlike + `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it + points to. + + - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. + - Changed `Formatter.write_str()` to take a safe `StringSlice`. + +- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which + returns a `Span` of the bytes owned by the string. + +- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers + +- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. + +- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), + please use the default constructor instead: `UnsafePointer[T]()`. + +- `Dict` now implements `get(key)` and `get(key, default)` functions. + ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) + +- Debugger users can now set breakpoints on function calls in O0 builds even if + the call has been inlined by the compiler. + +- The `os` module now provides functionality for adding and removing directories + using `mkdir` and `rmdir`. + ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) + +- `Dict.__get_ref(key)`, allowing to get references to dictionary values. + +- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters + other than whitespace. In addition, there are now several useful aliases for + whitespace, ASCII lower/uppercase, and so on. + ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) + +- `String` now has a `splitlines()` method, which allows splitting strings at line + boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) + and provides an option to retain or remove the line break characters. + ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) + +- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. + ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. + ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) + +- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. + Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` + is not possible yet. + ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `List()` now supports `__contains__`. + ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) + +- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) + type, a stack-allocated list with a static maximum size. + ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `InlineList()` now supports `__contains__`, `__iter__`. + ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) + +- `List` now has an `index` method that allows one to find the (first) location + of an element in a `List` of `EqualityComparable` types. For example: + + ```mojo + var my_list = List[Int](2, 3, 5, 7, 3) + print(my_list.index(3)) # prints 1 + ``` + +- `List` can now be converted to a `String` with a simplified syntax: + + ```mojo + var my_list = List[Int](2, 3) + print(my_list.__str__()) # prints [2, 3] + ``` + + Note that `List` doesn't conform to the `Stringable` trait yet so you cannot + use `str(my_list)` yet. + ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added the `Indexer` trait to denote types that implement the `__index__()` + method which allows these types to be accepted in common `__getitem__` and + `__setitem__` implementations, as well as allow a new builtin `index` function + to be called on them. Most stdlib containers are now able to be indexed by + any type that implements `Indexer`. For example: + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + struct MyList: + var data: List[Int] + + fn __init__(inout self): + self.data = List[Int](1, 2, 3, 4) + + fn __getitem__[T: Indexer](self, idx: T) -> T: + return self.data[index(idx)] + + print(MyList()[AlwaysZero()]) # prints `1` + ``` + + ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + + Types conforming to the `Indexer` trait are implicitly convertible to Int. + This means you can write generic APIs that take `Int` instead of making them + take a generic type that conforms to `Indexer`, e.g. + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + @value + struct Incrementer: + fn __getitem__(self, idx: Int) -> Int: + return idx + 1 + + var a = Incrementer() + print(a[AlwaysZero()]) # works and prints 1 + ``` + +- `StringRef` now implements `strip()` which can be used to remove leading and + trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) + by [@fknfilewalker](https://github.com/fknfilewalker)) + +- The `bencher` module as part of the `benchmark` package is now public + and documented. This module provides types such as `Bencher` which provides + the ability to execute a `Benchmark` and allows for benchmarking configuration + via the `BenchmarkConfig` struct. + +- Added the `bin()` builtin function to convert integral types into their binary + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) + by [@bgreni](https://github.com/bgreni)) + +- Added `atof()` function which can convert a `String` to a `float64`. + ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) + +- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) + by [@rd4com](https://github.com/rd4com)) For example: + + ```mojo + var x = Tuple(1, 2, True) + if 1 in x: + print("x contains 1") + ``` + +- Added `os.getsize` function, which gives the size in bytes of a path. + ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) + +- `List` now has a method `unsafe_get` to get the reference to an + element without bounds check or wraparound for negative indices. + Note that this method is unsafe. Use with caution. + ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys + and value. + ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `clear` method to `Dict`. + ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `os.path.join` function. + ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) + +- `StringRef` now implements `startswith()` and `endswith()`. + ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) + +- The Mojo Language Server now supports renaming local variables. + +- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. + ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. +([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) + +### 🦋 Changed + +- `Coroutine` now requires a lifetime parameter. This parameter is set + automatically by the parser when calling an async function. It contains the + lifetimes of all the arguments and any lifetime accesses by the arguments. + This ensures that argument captures by async functions keep the arguments + alive as long as the coroutine is alive. + +- Async function calls are no longer allowed to borrow non-trivial + register-passable types. Because async functions capture their arguments but + register-passable types don't have lifetimes (yet), Mojo is not able to + correctly track the reference, making this unsafe. To cover this safety gap, + Mojo has temporarily disallowed binding non-trivial register-passable types + to borrowed arguments in async functions. + +- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids + binding non-trivial register-passable types to `AnyTrivialRegType`. This + closes a major safety hole in the language. Please use `AnyType` for generic + code going forward. + +- The `let` keyword has been completely removed from the language. We previously + removed `let` declarations but still provided an error message to users. Now, + it is completely gone from the grammar. Long live `var`! + +- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved + from `math` to `builtin`, so you no longer need to do + `from math import abs, round, min, max, divmod, pow`. + + - The `is_power_of_2` function in the `math` module is now called + `is_power_of_two` and located in the `bit` module. + +- Many functions returning a pointer type have been unified to have a public + API function of `unsafe_ptr()`. + +- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to + `--diagnose-missing-doc-strings`. + +- The `take` function in `Variant` and `Optional` has been renamed to + `unsafe_take`. + +- The `get` function in `Variant` has been replaced by `__refitem__`. That is, + `v.get[T]()` should be replaced with `v[T]`. + +- Various functions in the `algorithm` module are now moved to be + builtin-functions. This includes `sort`, `swap`, and `partition`. + `swap` and `partition` will likely shuffle around as we're reworking + our builtin `sort` function and optimizing it. + +- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, + explicitly use `any()` or `all()`. + ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) + +- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise + operations, and support integer types. + ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) + +- `ListLiteral` and `Tuple` now only require that element types be `Movable`. + Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. + +- Continued transition to `UnsafePointer` and unsigned byte type for strings: + - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) + - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was + `DTypePointer`) + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was + `DTypePointer`). + - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now + returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) + - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was + `DTypePointer[DType.int8]`). + - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has + the same behavior. + +- Added `String.isspace()` method conformant with Python's universal separators. + +- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), + use `String.isspace()` instead. + +- `String.split()` now defaults to whitespace and has pythonic behavior in that + it removes all adjacent whitespaces by default. + +- Added `UnsafePointer.offset()` method. + +- The `math.bit` module has been moved to a new top-level `bit` module. The + following functions in this module have been renamed: + - `ctlz` -> `countl_zero` + - `cttz` -> `countr_zero` + - `bit_length` -> `bit_width` + - `ctpop` -> `pop_count` + - `bswap` -> `byte_swap` + - `bitreverse` -> `bit_reverse` + +- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been + moved to the `bit` module. + +- The `math.tgamma` function has been renamed to `math.gamma` to conform with + Python's naming. + +- The implementation of the following functions have been moved from the `math` + module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, + `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the + `math` module. + +- `InlinedString` has been renamed to `InlineString` to be consistent with other + types. + +- The `Slice.__len__` function has been removed and `Slice` no longer conforms + to the `Sized` trait. This clarifies the ambiguity of the semantics: the + length of a slice always depends on the length of the object being sliced. + Users that need the existing functionality can use the `Slice.unsafe_indices` + method. This makes it explicit that this implementation does not check if the + slice bounds are concrete or within any given object's length. + +- Implicit conversion to `String` is now removed for builtin classes/types. + One should use `str(...)` explicitly to convert to `String`. + +- `math.gcd` now works on negative inputs, and like Python's implementation, + accepts a variadic list of integers. New overloads for a `List` or `Span`of + integers are also added. + ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) + +### ❌ Removed + +- The `@unroll` decorator has been deprecated and removed. The decorator was + supposed to guarantee that a decorated loop would be unrolled, or else the + compiler would error. In practice, this guarantee was eroded over time, as + a compiler-based approach cannot be as robust as the Mojo parameter system. + In addition, the `@unroll` decorator did not make the loop induction variables + parameter values, limiting its usefulness. Please see `@parameter for` for a + replacement! + +- The method `object.print()` has been removed. Since now, `object` has the + `Stringable` trait, you can use `print(my_object)` instead. + +- The following functions have been removed from the math module: + - `clamp`; use the new `SIMD.clamp` method instead. + - `round_half_down` and `round_half_up`; these can be trivially implemented + using the `ceil` and `floor` functions. + - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, + `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and + `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, + `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, + respectively. + - `identity` and `reciprocal`; users can implement these trivially. + - `select`; in favor of using `SIMD.select` directly. + - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` + with `1`. + - `roundeven`; the new `SIMD.roundeven` method now provides the identical + functionality. + - `div_ceil`; use the new `ceildiv` function. + - `rotate_left` and `rotate_right`; the same functionality is available in the + builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the + `bit.rotate_bits_{left,right}` methods for `Int`. + - an overload of `math.pow` taking an integer parameter exponent. + - `align_down_residual`; it can be trivially implemented using `align_down`. + - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and + `SIMD.reduce_or` directly. + - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. + - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. + +- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's + method is no longer available. This method was limited to degree 10 or less, + underutilized, and its performance unclear. In the future, this might be + reintroduced with an improved implementation if needed, when better + performance benchmarking infrastructure is available. The default behavior of + `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). + +- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The + same functionality is available in the builtin `SIMD.select` and + `SIMD.__and__` methods, respectively. + +- The `math.limit` module has been removed. The same functionality is available + as follows: + - `math.limit.inf`: use `utils.numerics.max_or_inf` + - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` + - `math.limit.max_finite`: use `utils.numerics.max_finite` + - `math.limit.min_finite`: use `utils.numerics.min_finite` + +- The `tensor.random` module has been removed. The same functionality is now + accessible via the `Tensor.rand` and `Tensor.randn` static methods. + +- The builtin `SIMD` struct no longer conforms to `Indexer`; users must + explicitly cast `Scalar` values using `int`. + +### 🛠️ Fixed + +- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential + variant crashing the compiler. +- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on + simple trait definitions. +- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using + `//` on `FloatLiteral` in alias expression. +- Made several improvements to dictionary performance. Dicts with integer keys + are most heavily affected, but large dicts and dicts with large values + will also see large improvements. +- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` + to include calling location. + ## v24.3 (2024-05-02) ### ✨ Highlights diff --git a/docs/changelog.md b/docs/changelog.md index aa9c23f8a5..98426ad668 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,664 +14,24 @@ what we publish. ## UNRELEASED -### 🔥 Legendary - ### ⭐️ New -- Add a `sort` function for list of `ComparableCollectionElement`s. - [PR #2609](https://github.com/modularml/mojo/pull/2609) by - [@mzaks](https://github.com/mzaks) - -- Mojo functions can return an auto-dereferenced refeference to storage with a - new `ref` keyword in the result type specifier. For example: - - ```mojo - struct Pair: - var first: Int - var second: Int - fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: - return self.first - fn show_mutation(): - var somePair = ... - get_first_ref(somePair) = 1 - ``` - - This approach provides a general way to return an "automatically dereferenced" - reference of a given type. Notably, this eliminates the need for - `__refitem__` to exist. `__refitem__` has thus been removed and replaced with - `__getitem__` that returns a reference. - -- Mojo has introduced `@parameter for`, a new feature for compile-time - programming. `@parameter for` defines a for loop where the sequence and the - induction values in the sequence must be parameter values. For example: - - ```mojo - fn parameter_for[max: Int](): - @parameter - for i in range(max) - @parameter - if i == 10: - print("found 10!") - ``` - - Currently, `@parameter for` requires the sequence's `__iter__` method to - return a `_StridedRangeIterator`, meaning the induction variables must be - `Int`. The intention is to lift these restrictions in the future. - -- Mojo added support for the inferred parameters. Inferred parameters must - appear at the beginning of the parameter list and cannot be explicitly - specified by the user. They are declared to the left of a `//` marker, much - like positional-only parameters. This allows programmers to define functions - with dependent parameters to be called without the caller specifying all the - necessary parameters. For example: - - ```mojo - fn parameter_simd[dt: DType, //, value: Scalar[dt]](): - print(value) - - fn call_it(): - parameter_simd[Int32(42)]() - ``` - - In the above example, `Int32(42)` is passed directly into `value`, the first - non-inferred parameter. `dt` is inferred from the parameter itself to be - `DType.int32`. - - This also works with structs. For example: - - ```mojo - struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: - pass - - fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` - pass - ``` - - This should make working with dependent parameters more ergonomic. - -- Mojo now allows functions overloaded on parameters to be resolved when forming - references to, but not calling, those functions. For example, the following - now works: - - ```mojo - fn overloaded_parameters[value: Int32](): - pass - - fn overloaded_parameters[value: Float32](): - pass - - fn form_reference(): - alias ref = overloaded_parameters[Int32()] # works! - ``` - -- Mojo now supports adding a `@deprecated` decorator on structs, functions, - traits, aliases, and global variables. The decorator marks the attached decl - as deprecated and causes a warning to be emitted when the deprecated decl is - referenced in user code. The decorator requires a deprecation message to be - specified as a string literal. - - ```mojo - @deprecated("Foo is deprecated, use Bar instead") - struct Foo: - pass - - fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead - pass - - @deprecated("use another function!") - fn bar(): - pass - - fn techdebt(): - bar() # warning: use another function! - ``` - -- Mojo has changed how `def` arguments are processed. Previously, by default, - arguments to a `def` were treated treated according to the `owned` convention, - which makes a copy of the value, enabling that value to be mutable in the callee. - This "worked", but was a major performance footgun, and required you to declare - non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: - it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local - copy of the value **only if the argument is mutated** in the body of the function. - This improves consistency, performance, and ease of use. - -- `int()` can now take a string and a specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is - specified, the string will be parsed as if it was an integer literal, with the - base determined by whether the string contains the prefix `"0x"`, `"0o"`, or - `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by - [@artemiogr97](https://github.com/artemiogr97), fixes - [#2274](https://github.com/modularml/mojo/issues/2274)) - -- Mojo now supports types to opt in to use the `abs` and `round` functions by - implementing the `__abs__` and `__round__` methods (i.e. by conforming to the - new `Absable` and `Roundable` traits), respectively, e.g.: - - ```mojo - from math import sqrt - - @value - struct Complex(Absable, Roundable): - var re: Float64 - var im: Float64 - - fn __abs__(self) -> Self: - return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) - - fn __round__(self) -> Self: - return Self(round(self.re), round(self.im)) - ``` - -- User defined types can now also opt in to use the `pow` function by - implementing the `__pow__` method (and thus conforming to the new `Powable` - trait). As before, these types will also benefit from being able to use the - `**` operator. - -- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` - functions in the `math` module by implementing the `__floor__()`, - `__ceil__()`, and `__trunc__()` methods (and so conforming to the new - `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). - For example: - - ```mojo - from math import Ceilable, Floorable, Truncable, ceil, floor, trunc - - @value - struct Complex(Ceilable, Floorable, Truncable): - var re: Float64 - var im: Float64 - - fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) - - fn __floor__(self) -> Self: - return Self(floor(re), floor(im)) - - fn __trunc__(self) -> Self: - return Self(trunc(re), trunc(im)) - ``` - -- You can now use the builtin `any()` and `all()` functions to check for truthy - elements in a collection. Because `SIMD.__bool__()` is now constrained to - `size=1`, You must explicity use these to get the truthy value of a SIMD - vector. This avoids common bugs around implicit conversion of `SIMD` to - `Bool`. - ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) - - For example: - - ```mojo - fn truthy_simd(): - var vec = SIMD[DType.int32, 4](0, 1, 2, 3) - if any(vec): - print("any elements are truthy") - if all(vec): - print("all elements are truthy") - ``` - -- Add an `InlinedArray` type that works on memory-only types. - Compare with the existing `StaticTuple` type, which is conceptually an array - type, but only worked on `AnyTrivialRegType`. - ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) - -- Base64 decoding support has been added. - ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) - -- Add Base16 encoding and decoding support. - ([PR #2584](https://github.com/modularml/mojo/pull/2584) - by [@kernhanda](https://github.com/kernhanda)) - -- Add `repr()` function and `Representable` trait. - ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Add `SIMD.shuffle()` with `StaticIntTuple` mask. - ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) - -- Invoking `mojo package my-package -o my-dir` on the command line, where - `my-package` is a Mojo package source directory, and `my-dir` is an existing - directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. - Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. - -- The Mojo Language Server now reports a warning when a local variable is unused. - -- Implicit variable definitions in a `def` are more flexible: you can now - implicitly declare variables as the result of a tuple return, using - `a,b,c = foo()`, and can now shadow global immutable symbols using - `slice = foo()` without getting a compiler error. - -- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that - allow users to opt into the `math.ceildiv` function. - -- Mojo now allows methods to declare `self` as a `Reference` directly, which - can be useful for advanced cases of parametric mutabilty and custom lifetime - processing. Previously it required the use of an internal MLIR type to - achieve this. - -- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, - not a low-level `__mlir_type.i1` value. - - This improves the ergonomics of spelling out a - `Reference` type explicitly. For example, to define a struct holding a - `Reference`, you can now write: - - ```mojo - struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: - var data: Reference[Int32, is_mutable, lifetime] - ``` - - Or to specify a field that is always immutable, `False` can be specified - as the mutability: - - ```mojo - struct Foo[lifetime: AnyLifetime[False].type]: - var data: Reference[Int32, False, lifetime] - ``` - -- `object` now implements all the bitwise operators. - ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) - -- A new `--validate-doc-strings` option has been added to `mojo` to emit errors - on invalid doc strings instead of warnings. - -- Several `mojo` subcommands now support a `--diagnostic-format` option that - changes the format with which errors, warnings, and other diagnostics are - printed. By specifying `--diagnostic-format json` on the command line, errors - and other diagnostics will be output in a structured - [JSON Lines](https://jsonlines.org) format that is easier for machines to - parse. - - The full list of subcommands that support `--diagnostic-format` is as follows: - `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. - Further, the `mojo test --json` option has been subsumed into this new option; - for the same behavior, run `mojo test --diagnostic-format json`. - - Note that the format of the JSON output may change; we don't currently - guarantee its stability across releases of Mojo. - -- A new decorator, `@doc_private`, was added that can be used to hide a decl - from being generated in the output of `mojo doc`. It also removes the - requirement that the decl has documentation (e.g. when used with - --diagnose-missing-doc-strings). - -- Added a new `Span` type for taking slices of contiguous collections. - ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) - -- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type - in standard library code. - - `StringSlice` is a non-owning reference to encoded string data. Unlike - `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it - points to. - - - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. - - Changed `Formatter.write_str()` to take a safe `StringSlice`. - -- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which - returns a `Span` of the bytes owned by the string. - -- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers - -- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. - -- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), - please use the default constructor instead: `UnsafePointer[T]()`. - -- `Dict` now implements `get(key)` and `get(key, default)` functions. - ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) - -- Debugger users can now set breakpoints on function calls in O0 builds even if - the call has been inlined by the compiler. - -- The `os` module now provides functionality for adding and removing directories - using `mkdir` and `rmdir`. - ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) +- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. +([PR #2701](https://github.com/modularml/mojo/pull/2701) +by [@jayzhan211](https://github.com/jayzhan211)) -- `Dict.__get_ref(key)`, allowing to get references to dictionary values. +- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` + for convenient interoperability with C APIs. -- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters - other than whitespace. In addition, there are now several useful aliases for - whitespace, ASCII lower/uppercase, and so on. - ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) - -- `String` now has a `splitlines()` method, which allows splitting strings at line - boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) - and provides an option to retain or remove the line break characters. - ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) - -- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. - ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. - ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) - -- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. - Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` - is not possible yet. - ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `List()` now supports `__contains__`. - ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) - -- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) - type, a stack-allocated list with a static maximum size. - ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `InlineList()` now supports `__contains__`, `__iter__`. - ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) - -- `List` now has an `index` method that allows one to find the (first) location - of an element in a `List` of `EqualityComparable` types. For example: - - ```mojo - var my_list = List[Int](2, 3, 5, 7, 3) - print(my_list.index(3)) # prints 1 - ``` - -- `List` can now be converted to a `String` with a simplified syntax: - - ```mojo - var my_list = List[Int](2, 3) - print(my_list.__str__()) # prints [2, 3] - ``` - - Note that `List` doesn't conform to the `Stringable` trait yet so you cannot - use `str(my_list)` yet. - ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Added the `Indexer` trait to denote types that implement the `__index__()` - method which allows these types to be accepted in common `__getitem__` and - `__setitem__` implementations, as well as allow a new builtin `index` function - to be called on them. Most stdlib containers are now able to be indexed by - any type that implements `Indexer`. For example: - - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 - - struct MyList: - var data: List[Int] - - fn __init__(inout self): - self.data = List[Int](1, 2, 3, 4) - - fn __getitem__[T: Indexer](self, idx: T) -> T: - return self.data[index(idx)] - - print(MyList()[AlwaysZero()]) # prints `1` - ``` - - ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) - - Types conforming to the `Indexer` trait are implicitly convertible to Int. - This means you can write generic APIs that take `Int` instead of making them - take a generic type that conforms to `Indexer`, e.g. - - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 - - @value - struct Incrementer: - fn __getitem__(self, idx: Int) -> Int: - return idx + 1 - - var a = Incrementer() - print(a[AlwaysZero()]) # works and prints 1 - ``` - -- `StringRef` now implements `strip()` which can be used to remove leading and - trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) - by [@fknfilewalker](https://github.com/fknfilewalker)) - -- The `bencher` module as part of the `benchmark` package is now public - and documented. This module provides types such as `Bencher` which provides - the ability to execute a `Benchmark` and allows for benchmarking configuration - via the `BenchmarkConfig` struct. - -- Added the `bin()` builtin function to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) - by [@bgreni](https://github.com/bgreni)) - -- Added `atof()` function which can convert a `String` to a `float64`. - ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) - -- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) - by [@rd4com](https://github.com/rd4com)) For example: - - ```mojo - var x = Tuple(1, 2, True) - if 1 in x: - print("x contains 1") - ``` - -- Added `os.getsize` function, which gives the size in bytes of a path. - ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) - -- `List` now has a method `unsafe_get` to get the reference to an - element without bounds check or wraparound for negative indices. - Note that this method is unsafe. Use with caution. - ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys - and value. - ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `clear` method to `Dict`. - ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `os.path.join` function. - ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) - -- `StringRef` now implements `startswith()` and `endswith()`. - ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) - -- The Mojo Language Server now supports renaming local variables. - -- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. - ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. -([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) +- Added `C_char` type alias in `sys.ffi`. ### 🦋 Changed -- `Coroutine` now requires a lifetime parameter. This parameter is set - automatically by the parser when calling an async function. It contains the - lifetimes of all the arguments and any lifetime accesses by the arguments. - This ensures that argument captures by async functions keep the arguments - alive as long as the coroutine is alive. - -- Async function calls are no longer allowed to borrow non-trivial - register-passable types. Because async functions capture their arguments but - register-passable types don't have lifetimes (yet), Mojo is not able to - correctly track the reference, making this unsafe. To cover this safety gap, - Mojo has temporarily disallowed binding non-trivial register-passable types - to borrowed arguments in async functions. - -- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids - binding non-trivial register-passable types to `AnyTrivialRegType`. This - closes a major safety hole in the language. Please use `AnyType` for generic - code going forward. - -- The `let` keyword has been completely removed from the language. We previously - removed `let` declarations but still provided an error message to users. Now, - it is completely gone from the grammar. Long live `var`! - -- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved - from `math` to `builtin`, so you no longer need to do - `from math import abs, round, min, max, divmod, pow`. - - - The `is_power_of_2` function in the `math` module is now called - `is_power_of_two` and located in the `bit` module. - -- Many functions returning a pointer type have been unified to have a public - API function of `unsafe_ptr()`. - -- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to - `--diagnose-missing-doc-strings`. - -- The `take` function in `Variant` and `Optional` has been renamed to - `unsafe_take`. - -- The `get` function in `Variant` has been replaced by `__refitem__`. That is, - `v.get[T]()` should be replaced with `v[T]`. - -- Various functions in the `algorithm` module are now moved to be - builtin-functions. This includes `sort`, `swap`, and `partition`. - `swap` and `partition` will likely shuffle around as we're reworking - our builtin `sort` function and optimizing it. - -- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, - explicitly use `any()` or `all()`. - ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) - -- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise - operations, and support integer types. - ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) - -- `ListLiteral` and `Tuple` now only require that element types be `Movable`. - Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - - Continued transition to `UnsafePointer` and unsigned byte type for strings: - - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) - - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was - `DTypePointer`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was - `DTypePointer`). - - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now - returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). - - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) - - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was - `DTypePointer[DType.int8]`). - - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has - the same behavior. - -- Added `String.isspace()` method conformant with Python's universal separators. - -- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), - use `String.isspace()` instead. - -- `String.split()` now defaults to whitespace and has pythonic behavior in that - it removes all adjacent whitespaces by default. - -- Added `UnsafePointer.offset()` method. - -- The `math.bit` module has been moved to a new top-level `bit` module. The - following functions in this module have been renamed: - - `ctlz` -> `countl_zero` - - `cttz` -> `countr_zero` - - `bit_length` -> `bit_width` - - `ctpop` -> `pop_count` - - `bswap` -> `byte_swap` - - `bitreverse` -> `bit_reverse` - -- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been - moved to the `bit` module. - -- The `math.tgamma` function has been renamed to `math.gamma` to conform with - Python's naming. - -- The implementation of the following functions have been moved from the `math` - module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, - `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the - `math` module. - -- `InlinedString` has been renamed to `InlineString` to be consistent with other - types. - -- The `Slice.__len__` function has been removed and `Slice` no longer conforms - to the `Sized` trait. This clarifies the ambiguity of the semantics: the - length of a slice always depends on the length of the object being sliced. - Users that need the existing functionality can use the `Slice.unsafe_indices` - method. This makes it explicit that this implementation does not check if the - slice bounds are concrete or within any given object's length. - -- Implicit conversion to `String` is now removed for builtin classes/types. - One should use `str(...)` explicitly to convert to `String`. - -- `math.gcd` now works on negative inputs, and like Python's implementation, - accepts a variadic list of integers. New overloads for a `List` or `Span`of - integers are also added. - ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) + - Rename `String._as_ptr()` to `String.unsafe_ptr()` + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `DTypePointer[DType.int8]`) ### ❌ Removed -- The `@unroll` decorator has been deprecated and removed. The decorator was - supposed to guarantee that a decorated loop would be unrolled, or else the - compiler would error. In practice, this guarantee was eroded over time, as - a compiler-based approach cannot be as robust as the Mojo parameter system. - In addition, the `@unroll` decorator did not make the loop induction variables - parameter values, limiting its usefulness. Please see `@parameter for` for a - replacement! - -- The method `object.print()` has been removed. Since now, `object` has the - `Stringable` trait, you can use `print(my_object)` instead. - -- The following functions have been removed from the math module: - - `clamp`; use the new `SIMD.clamp` method instead. - - `round_half_down` and `round_half_up`; these can be trivially implemented - using the `ceil` and `floor` functions. - - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, - `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and - `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, - `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, - respectively. - - `identity` and `reciprocal`; users can implement these trivially. - - `select`; in favor of using `SIMD.select` directly. - - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` - with `1`. - - `roundeven`; the new `SIMD.roundeven` method now provides the identical - functionality. - - `div_ceil`; use the new `ceildiv` function. - - `rotate_left` and `rotate_right`; the same functionality is available in the - builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the - `bit.rotate_bits_{left,right}` methods for `Int`. - - an overload of `math.pow` taking an integer parameter exponent. - - `align_down_residual`; it can be trivially implemented using `align_down`. - - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and - `SIMD.reduce_or` directly. - - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. - - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. - -- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's - method is no longer available. This method was limited to degree 10 or less, - underutilized, and its performance unclear. In the future, this might be - reintroduced with an improved implementation if needed, when better - performance benchmarking infrastructure is available. The default behavior of - `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). - -- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The - same functionality is available in the builtin `SIMD.select` and - `SIMD.__and__` methods, respectively. - -- The `math.limit` module has been removed. The same functionality is available - as follows: - - `math.limit.inf`: use `utils.numerics.max_or_inf` - - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` - - `math.limit.max_finite`: use `utils.numerics.max_finite` - - `math.limit.min_finite`: use `utils.numerics.min_finite` - -- The `tensor.random` module has been removed. The same functionality is now - accessible via the `Tensor.rand` and `Tensor.randn` static methods. - -- The builtin `SIMD` struct no longer conforms to `Indexer`; users must - explicitly cast `Scalar` values using `int`. - ### 🛠️ Fixed - -- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential - variant crashing the compiler. -- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on - simple trait definitions. -- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using - `//` on `FloatLiteral` in alias expression. -- Made several improvements to dictionary performance. Dicts with integer keys - are most heavily affected, but large dicts and dicts with large values - will also see large improvements. -- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` - to include calling location. From aaa5a5479a984b7054329a44c67dc1e25aef84e5 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 5 Jun 2024 18:38:23 -0700 Subject: [PATCH 0788/2019] [Docs] Document default arg convention, clean up decorators - Updates doc on the `def` function default argument convention. This touches several docs. - Remove the reference page for `@unroll` and add `@parameter for` to the `@parameter` page. - Update the reference page for `@always-inline("nodebug")` to describe intended use. MODULAR_ORIG_COMMIT_REV_ID: 6805152f0150eaf4c551209b0a8c3e8b1c398455 --- docs/faq.md | 22 +-- docs/manual/decorators/always-inline.ipynb | 8 +- docs/manual/decorators/index.md | 1 - docs/manual/decorators/parameter.ipynb | 51 +++++- docs/manual/decorators/unroll.ipynb | 164 ----------------- docs/manual/functions.ipynb | 197 ++++++++++++--------- docs/manual/index.md | 51 +++--- docs/manual/values/ownership.ipynb | 151 +++++++++------- docs/manual/values/value-semantics.ipynb | 48 ++--- docs/roadmap.md | 6 +- 10 files changed, 320 insertions(+), 379 deletions(-) delete mode 100644 docs/manual/decorators/unroll.ipynb diff --git a/docs/faq.md b/docs/faq.md index 59e986cde3..a6d385cbc0 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -6,7 +6,7 @@ description: Answers to questions we expect about Mojo. We tried to anticipate your questions about Mojo on this page. If this page doesn't answer all your questions, also check out our [Mojo community -channels](/mojo/community.html). +channels](/mojo/community). ## Motivation @@ -20,7 +20,7 @@ to unify AI software and we can’t do that without a unified language that can scale across the AI infrastructure stack. That said, we don’t plan to stop at AI—the north star is for Mojo to support the whole gamut of general-purpose programming over time. For a longer answer, read [Why -Mojo](/mojo/why-mojo.html). +Mojo](/mojo/why-mojo). ### Why is it called Mojo? @@ -43,7 +43,7 @@ it’s missing. We are guided more by pragmatism than novelty, but Mojo’s use [MLIR](https://mlir.llvm.org/) allows it to scale to new exotic hardware types and domains in a way that other languages haven’t demonstrated (for an example of Mojo talking directly to MLIR, see our [low-level IR in Mojo -notebook](/mojo/notebooks/BoolMLIR.html)). It also +notebook](/mojo/notebooks/BoolMLIR)). It also includes autotuning, and has caching and distributed compilation built into its core. We also believe Mojo has a good chance of unifying hybrid packages in the broader Python community. @@ -84,7 +84,7 @@ Codon and PyPy aim to improve performance compared to CPython, but Mojo’s goal are much deeper than this. Our objective isn’t just to create “a faster Python,” but to enable a whole new layer of systems programming that includes direct access to accelerated hardware, as outlined in [Why -Mojo](/mojo/why-mojo.html). Our technical implementation +Mojo](/mojo/why-mojo). Our technical implementation approach is also very different, for example, we are not relying on heroic compiler and JIT technologies to “devirtualize” Python. @@ -123,7 +123,7 @@ you to try Mojo and if you find it useful, then that's great too. The best place to start is the [Mojo Manual](/mojo/manual). And if you want to see what features are coming in the future, take a look at [the -roadmap](/mojo/roadmap.html). +roadmap](/mojo/roadmap). ### What does it mean that Mojo is designed for MLIR? @@ -138,7 +138,7 @@ ground up with MLIR design principles. This means that Mojo not only offers high-performance compilation for heterogeneous hardware, but it also provides direct programming support for the MLIR intermediate representations. For a simple example of Mojo talking directly to MLIR, see our [low-level IR in Mojo -notebook](/mojo/notebooks/BoolMLIR.html). +notebook](/mojo/notebooks/BoolMLIR). ### Is Mojo only for AI or can it be used for other stuff? @@ -165,7 +165,7 @@ language that will support more architectures over time and includes a debugger, a full tool suite, etc. For more about embedded domain-specific languages (EDSLs) like Triton, read the “Embedded DSLs in Python” section of [Why -Mojo](/mojo/why-mojo.html#embedded-dsls-in-python). +Mojo](/mojo/why-mojo#embedded-dsls-in-python). ### How does Mojo help with PyTorch and TensorFlow acceleration? @@ -200,7 +200,7 @@ and build migration tools as the language matures. Yes, we want to enable developers to port code from languages other than Python to Mojo as well. We expect that due to Mojo’s similarity to the C/C++ type systems, migrating code from C/C++ should work well and it’s in [our -roadmap](/mojo/roadmap.html#cc-interop). +roadmap](/mojo/roadmap#cc-interop). ### How does Mojo support hardware lowering? @@ -379,7 +379,7 @@ more detail such as the time spend compiling. Mojo is still in early development and not at a 1.0 version yet. It’s still missing many foundational features, but please take a look at our -[roadmap](/mojo/roadmap.html) to understand where things are headed. As such, +[roadmap](/mojo/roadmap) to understand where things are headed. As such, the language is evolving rapidly and source stability is not guaranteed. ### How often will you be releasing new versions of Mojo? @@ -424,9 +424,9 @@ Clang, Swift, MLIR, etc.). ### Where can I ask more questions or share feedback? If you have questions about upcoming features or have suggestions -for the language, be sure you first read the [Mojo roadmap](roadmap.html), which +for the language, be sure you first read the [Mojo roadmap](/mojo/roadmap), which provides important information about our current priorities and links to our GitHub channels where you can report issues and discuss new features. To get in touch with the Mojo team and developer community, use the resources -on our [Mojo community page](/mojo/community.html). +on our [Mojo community page](/mojo/community). diff --git a/docs/manual/decorators/always-inline.ipynb b/docs/manual/decorators/always-inline.ipynb index d0648a2f0d..f272691b9b 100644 --- a/docs/manual/decorators/always-inline.ipynb +++ b/docs/manual/decorators/always-inline.ipynb @@ -72,8 +72,12 @@ "\n", "You can also use the decorator with the `\"nodebug\"` argument, which has the\n", "same effect to inline the function, but without debug information. This means\n", - "you can't step into the function when debugging, but it reduces the debug build\n", - "binary size." + "that you can't step into the function when debugging.\n", + "\n", + "This decorator is intended to be used on the lowest-level functions in a\n", + "library, which may wrap primitive functions, MLIR operations, or inline\n", + "assembly. Marking these functions as \"nodebug\" prevents users from accidentally\n", + "stepping into low-level non-Mojo code when debugging." ] } ], diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.md index bfdd52e7c6..4f7f11ee43 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.md @@ -13,7 +13,6 @@ listing: - parameter.ipynb - register-passable.ipynb - staticmethod.ipynb - - unroll.ipynb - value.ipynb type: grid page-size: 99 diff --git a/docs/manual/decorators/parameter.ipynb b/docs/manual/decorators/parameter.ipynb index 65aff01912..c74b25e22f 100644 --- a/docs/manual/decorators/parameter.ipynb +++ b/docs/manual/decorators/parameter.ipynb @@ -51,6 +51,55 @@ " print(\"this will be eliminated at compile time\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parametric for statement\n", + "\n", + "You can add the `@parameter` decorator to an `for` loop to create a loop that's\n", + "evaluated at compile time. The loop sequence and induction values must be\n", + "a valid parameter expressions (that is, an expressions that evaluate at compile\n", + "time).\n", + "\n", + "This has the effect of \"unrolling\" the loop.\n", + "\n", + " ```mojo\n", + " fn parameter_for[max: Int]():\n", + " @parameter\n", + " for i in range(max)\n", + " @parameter\n", + " if i == 10:\n", + " print(\"found 10!\")\n", + " ```\n", + "\n", + " Currently, `@parameter for` requires the sequence's `__iter__` method to\n", + " return a `_StridedRangeIterator`, meaning the induction variables must be\n", + " `Int`. The intention is to lift these restrictions in the future.\n", + "\n", + "### Compared to `unroll()`\n", + "\n", + "The Mojo standard library also includes a function called\n", + "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", + "given function that you want to call repeatedly, but has some important\n", + "differences when compared to the parametric `for` statement:\n", + "\n", + "- The `@parameter` decorator operates on `for` loop expressions. The \n", + " `unroll()` function is a higher-order function that takes a parametric closure\n", + " (see below) and executes it a specified number of times.\n", + "\n", + "- The parametric `for` statement is more versatile, since you can do anything \n", + " you can do in a `for` statement: including using arbitrary sequences, \n", + " early-exiting from the loop, skipping iterations with `continue` and so on.\n", + " \n", + " By contrast, `unroll()` simply takes a function and a count, and executes\n", + " the function the specified number of times.\n", + "\n", + "Both `unroll()` and `@parameter for` unroll at the beginning of compilation, \n", + "which might explode the size of the program that still needs to be compiled,\n", + "depending on the amount of code that's unrolled." + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -66,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/docs/manual/decorators/unroll.ipynb b/docs/manual/decorators/unroll.ipynb deleted file mode 100644 index d0300178ac..0000000000 --- a/docs/manual/decorators/unroll.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@parameter`'\n", - "description: Unrolls a loop at compile time.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@parameter` decorator on any loop (such as `for` and `while`) to\n", - "make the Mojo compiler [unroll the\n", - "loop](https://en.wikipedia.org/wiki/Loop_unrolling), either fully or with a\n", - "given unroll factor.\n", - "\n", - "For example, the compiler will unroll all 10 iterations of the following loop\n", - "into 10 consecutive calls to `print()` (removing the `for` loop entirely):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@parameter\n", - "for i in range(10):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The decorator also accepts an \"unroll factor\" argument, which specifies how\n", - "many iterations to unroll at once. For example, the unroll above is equivalent\n", - "to `@parameter(10)` because it unrolls all iterations of the loop. So if you pass\n", - "a number smaller than the loop bounds, the compiler creates multiple unrolls.\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Unroll every 2 iterations, leaving a loop with 5 iterations.\n", - "# @parameter(2)\n", - "for i in range (10):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is equivalent to this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(0, 10, 2):\n", - " print(i)\n", - " print(i+1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, the compiler can unroll a loop only when the following statements are\n", - "true:\n", - "\n", - "- The loop's lower bound, upper bound, and induction step size are compile-time\n", - "constants (they do not vary at runtime). For example, in the above code\n", - "`range(0, 10, 2)`, `0` is the lower bound, `10` is the upper bound, and `2`\n", - "is the induction step size—these could instead be defined with variable names,\n", - "but the values cannot vary at runtime.\n", - "\n", - "- Likewise, there are no early exits in the loop that make the loop count\n", - "variable at runtime." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Compared to `unroll()`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Mojo standard library also includes a function called\n", - "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", - "given function that you want to call repeatedly, but has some important\n", - "differences when compared to the `@parameter` decorator:\n", - "\n", - "- The `@parameter` decorator operates on loop expressions only, not on functions\n", - " like the `unroll()` function does.\n", - "\n", - "- The `@parameter` decorator determines how to unroll the loop based on the\n", - " induction variable (`i`), the value of which _is not_ known when compilation\n", - " begins. Whereas, the `unroll()` function calls upon your looping function\n", - " (`func`) with the `Int` loop index parameter that _is_ known at compile time.\n", - "\n", - " This means two things:\n", - "\n", - " - Within a loop using the `@parameter` decorator, the `i` induction variable is \n", - " still a runtime variable, so you _cannot_ use it as a parameter value (such\n", - " as for `SIMD[Int8, i]`). Whereas, within the `func` callback used with the\n", - " `unroll()` function, the `Int` loop index is known at compile time, so you\n", - " _can_ use it as a parameter value.\n", - "\n", - " - The `unroll()` function unrolls at the beginning of compilation, which\n", - " might explode the program size that still needs to be compiled, depending\n", - " on the amount of code that's unrolled. Whereas, the `@parameter` decorator\n", - " performs unrolling later in the compilation, after the compiler is able to\n", - " evaluate the induction variable (`i`), which avoids early explosion of the\n", - " program size that still needs compilation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index ee7a1cadd3..64e4e2e604 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -34,25 +34,89 @@ "\n", ":::note\n", "\n", - "Functions declared inside a [`struct`](/mojo/manual/structs.html) are called\n", + "Functions declared inside a [`struct`](/mojo/manual/structs) are called\n", "\"methods,\" but they have all the same qualities as \"functions\" described here.\n", "\n", ":::" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `fn` functions\n", + "\n", + "The `fn` function has somewhat stricter rules than the `def` function.\n", + "\n", + "Here's an example of an `fn` function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn greet(name: String) -> String:\n", + " var greeting = \"Hello, \" + name + \"!\"\n", + " return greeting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As far as a function caller is concerned, `def` and `fn` functions are\n", + "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", + "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", + "function is more strict on the inside.\n", + "\n", + "Here's everything to know about `fn`:\n", + "\n", + "- Arguments must specify a type (except for the\n", + " `self` argument in [struct methods](/mojo/manual/structs#methods)).\n", + "\n", + "- Return values must specify a type, unless the function doesn't return a value.\n", + " \n", + " If you don't specify a return type, it defaults to `None` (meaning no return\n", + " value).\n", + "\n", + "- By default, arguments are received as an immutable reference (values are\n", + " read-only, using the `borrowed` [argument\n", + " convention](/mojo/manual/values/ownership#argument-conventions)).\n", + " \n", + " This prevents accidental mutations, and permits the use of non-copyable types\n", + " as arguments.\n", + " \n", + " If you want a local copy, you can simply assign the value to a local\n", + " variable. Or, you can get a mutable reference to the value by declaring the\n", + " `inout` [argument\n", + " convention](/mojo/manual/values/ownership#argument-conventions)).\n", + "\n", + "- [Variables](/mojo/manual/variables) must be declared using the `var`\n", + " keyword.\n", + "\n", + "- If the function raises an exception, it must be explicitly declared with the\n", + " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", + "\n", + "By enforcing these type checks, using the `fn` function helps avoid a variety\n", + "of runtime errors." + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `def` functions\n", "\n", - "The `def` function provides the same dynamism and flexibility as a Python\n", + "Compared to an `fn` function, a `def` function has fewer restrictions.\n", + "The `def` function works more like a Python\n", "`def` function. For example, this function works the same in Python and Mojo:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -65,14 +129,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In Mojo, you also have the option to specify the argument type and the return\n", - "type. You can also declare variables with `var`, with or without explicit\n", - "typing." + "In a Mojo `def` function, you have the option to specify the argument type and\n", + "the return type. You can also declare variables with `var`, with or without\n", + "explicit typing. So you can write a `def` function that looks almost exactly\n", + "like the `fn` function shown earlier:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -92,23 +157,26 @@ "\n", "- Arguments don't require a declared type.\n", "\n", - " Undeclared arguments are actually passed as an `object`, which allows the\n", + " Undeclared arguments are actually passed as an\n", + " [`object`](/mojo/stdlib/builtin/object/object), which allows the\n", " function to receive any type (Mojo infers the type at runtime).\n", "\n", - "- Return types don't need to be declared and also default to `object`.\n", + "- Return types don't need to be declared, and also default to `object`. (If a \n", + " `def` function doesn't declare a return type of `None`, it's considered to\n", + " return an `object` by default.)\n", "\n", - "- Arguments are mutable (usually passed by value, using the `owned` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", + "- Arguments are mutable. Arguments default to using the using the `borrowed` \n", + " [argument convention](/mojo/manual/values/ownership#argument-conventions))\n", + " like an `fn` function, with a special addition: if the function mutates the\n", + " argument, it makes a mutable copy. \n", "\n", " If an argument is an `object` type, it's received as a reference, following\n", " [object reference\n", - " semantics](/mojo/manual/values/value-semantics.html#python-style-reference-semantics).\n", + " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", " \n", - " If an argument is any other declared type, it's received as a value (using\n", - " the `owned` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", + " If an argument is any other declared type, it's received as a value.\n", "\n", - "- [Variables](/mojo/manual/variables.html) don't need to be declared using \n", + "- [Variables](/mojo/manual/variables) don't need to be declared using \n", " `var`." ] }, @@ -131,86 +199,43 @@ "\n", "For compatibility with Python, `object` values are passed using [object\n", "reference\n", - "semantics](/mojo/manual/values/value-semantics.html#python-style-reference-semantics).\n", + "semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", "As such, the `object` type is not compatible with the [argument\n", - "conventions](/mojo/manual/values/ownership.html#argument-conventions) that\n", + "conventions](/mojo/manual/values/ownership#argument-conventions) that\n", "enforce value semantics. So, be careful if using `object` values alongside other\n", "strongly-typed values—their behavior might be inconsistent because `object` is \n", "the only type in the standard library that does not conform to [full value\n", - "semantics](/mojo/manual/values/value-semantics.html#full-value-semantics)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `fn` functions\n", + "semantics](/mojo/manual/values/value-semantics#full-value-semantics).\n", "\n", - "The `fn` function provides strict type checking and additional memory safety.\n", - "It basically forces you to write the optional things in `def`, and it ensures\n", - "that you don't accidentally mutate received arguments. For example, here's the\n", - "same function from above using `fn`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn greet(name: String) -> String:\n", - " var greeting = \"Hello, \" + name + \"!\"\n", - " return greeting" + ":::note TODO\n", + "\n", + "The `object` type is still a work in progress. It doesn't support all of the\n", + "possible underlying types, for example.\n", + "\n", + ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As far as a function caller is concerned, `def` and `fn` functions are\n", - "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", - "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", - "function is more strict on the inside.\n", + "## Function arguments\n", "\n", - "Here's everything to know about `fn`:\n", + "As noted in the previous sections, there are a few differences between how `def`\n", + "and `fn` functions treat arguments. But most of the time they are the same.\n", "\n", - "- Arguments must specify a type (except for the\n", - " `self` argument in [struct methods](/mojo/manual/structs.html)).\n", + "As noted, there are some differences in _argument conventions_. \n", + "Argument conventions are discussed in much more detail in the page on\n", + "[Ownership](/mojo/manual/values/ownership#argument-conventions).\n", "\n", - "- Return values must specify a type, unless the function doesn't return a value.\n", - " \n", - " If you don't specify a return type, it defaults to `None` (meaning no return\n", - " value).\n", + "The other difference is that `def` functions don't need to specify an argument's\n", + "type. If no type is specified, the argument is passed as an \n", + "[`object`](/mojo/stdlib/builtin/object/object).\n", "\n", - "- By default, arguments are received as an immutable reference (values are\n", - " read-only, using the `borrowed` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", - " \n", - " This prevents accidental mutations, and permits the use of non-copyable types\n", - " as arguments.\n", - " \n", - " If you want a local copy, you can simply assign the value to a local\n", - " variable. Or, you can get a mutable reference to the value by declaring the\n", - " `inout` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", - "\n", - "- [Variables](/mojo/manual/variables.html) must be declared using the `var`\n", - " keyword.\n", + "The remaining rules for arguments described in this section apply to both `def`\n", + "and `fn` functions.\n", "\n", - "- If the function raises an exception, it must be explicitly declared with the\n", - " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", - "\n", - "By enforcing these type checks, using the `fn` function helps avoid a variety\n", - "of runtime errors. It also improves performance compared to the dynamic typing\n", - "in a `def` function, because there's no overhead processing required to figure\n", - "out what data types to use at runtime—the types are fixed at compile time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional arguments\n", + "### Optional arguments\n", "\n", "An optional argument is one that includes a default value, such as the `exp`\n", "argument here:" @@ -247,7 +272,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Keyword arguments\n", + "### Keyword arguments\n", "\n", "You can also use keyword arguments when calling a function. Keyword arguments\n", "are specified using the format argument_name =\n", @@ -273,7 +298,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Variadic arguments\n", + "### Variadic arguments\n", "\n", "Variadic arguments let a function accept a variable number of arguments. To\n", "define a function that takes a variadic argument, use the variadic argument\n", @@ -326,7 +351,7 @@ ":::\n", "\n", "\n", - "### Homogeneous variadic arguments\n", + "#### Homogeneous variadic arguments\n", "\n", "When defining a homogeneous variadic argument, use \n", "*argument_name: argument_type:" @@ -416,7 +441,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Heterogeneous variadic arguments\n", + "#### Heterogeneous variadic arguments\n", "\n", "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", @@ -545,7 +570,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Variadic keyword arguments\n", + "#### Variadic keyword arguments\n", "\n", "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", "keyword arguments allow the user to pass an arbitrary number of keyword\n", @@ -611,7 +636,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Positional-only and keyword-only arguments\n", + "### Positional-only and keyword-only arguments\n", "\n", "When defining a function, you can restrict some arguments so that they can only\n", "be passed as positional arguments, or they can only be passed as keyword \n", diff --git a/docs/manual/index.md b/docs/manual/index.md index 460756b70f..4d28a625c4 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -13,12 +13,12 @@ ideal for heterogeneous hardware, from CPUs and GPUs, to various AI ASICs). We also designed Mojo as a superset of Python because we love Python and its community, but we couldn't realistically enhance Python to do all the things we wanted. For a longer discussion on this topic, read [Why -Mojo](/mojo/why-mojo.html). +Mojo](/mojo/why-mojo). Beware that Mojo is still a very young language, so there's a lot that hasn't been built yet. Likewise, there's a lot of documentation that hasn't been written yet. But we're excited to share Mojo with you and [get your -feedback](/mojo/community.html). +feedback](/mojo/community). ## Contents @@ -29,42 +29,47 @@ feedback](/mojo/community.html). - **Language basics** - - [Introduction to Mojo](/mojo/manual/basics.html) - - [Functions](/mojo/manual/functions.html) - - [Variables](/mojo/manual/variables.html) - - [Structs](/mojo/manual/structs.html) - - [Modules and packages](/mojo/manual/packages.html) + - [Introduction to Mojo](/mojo/manual/basics) + - [Functions](/mojo/manual/functions) + - [Variables](/mojo/manual/variables) + - [Types](/mojo/manual/types) + - [Structs](/mojo/manual/structs) + - [Modules and packages](/mojo/manual/packages) - **Value ownership** - - [Intro to value ownership](/mojo/manual/values/index.html) - - [Value semantics](/mojo/manual/values/value-semantics.html) - - [Ownership and borrowing](/mojo/manual/values/ownership.html) + - [Intro to value ownership](/mojo/manual/values/index) + - [Value semantics](/mojo/manual/values/value-semantics) + - [Ownership and borrowing](/mojo/manual/values/ownership) - **Value lifecycle** - - [Intro to value lifecycle](/mojo/manual/lifecycle/index.html) - - [Life of a value](/mojo/manual/lifecycle/life.html) - - [Death of a value](/mojo/manual/lifecycle/death.html) + - [Intro to value lifecycle](/mojo/manual/lifecycle/index) + - [Life of a value](/mojo/manual/lifecycle/life) + - [Death of a value](/mojo/manual/lifecycle/death) - **Traits and parameters** - - [Traits](/mojo/manual/traits.html) - - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index.html) + - [Traits](/mojo/manual/traits) + - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index) + +- **Pointers** + + - [Unsafe pointers](/mojo/manual/pointers) - **Python** - - [Python integration](/mojo/manual/python/index.html) - - [Python types](/mojo/manual/python/types.html) + - [Python integration](/mojo/manual/python/index) + - [Python types](/mojo/manual/python/types) - **Tools** - - [Debugging](/mojo/tools/debugging.html) - - [Testing](/mojo/tools/testing.html) + - [Debugging](/mojo/tools/debugging) + - [Testing](/mojo/tools/testing) - **Project information** - - [Roadmap and sharp edges](/mojo/roadmap.html) - - [Changelog](/mojo/changelog.html) - - [FAQ](/mojo/faq.html) - - [Community](/mojo/community.html) + - [Roadmap and sharp edges](/mojo/roadmap) + - [Changelog](/mojo/changelog) + - [FAQ](/mojo/faq) + - [Community](/mojo/community) diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 20674db040..a92d507b7a 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -64,7 +64,7 @@ "\n", "- `borrowed`: The function receives an **immutable reference**. This means the\n", " function can read the original value (it is *not* a copy), but it cannot\n", - " mutate (modify) it.\n", + " mutate (modify) it. `def` functions treat this differently, as described below.\n", " \n", "- `inout`: The function receives a **mutable reference**. This means the\n", " function can read and mutate the original value (it is *not* a copy).\n", @@ -100,16 +100,38 @@ "metadata": {}, "source": [ "You've probably already seen some function arguments that don't declare a\n", - "convention. That's because every argument has a default convention, depending\n", - "on whether the function is declared with `fn` or `def`:\n", + "convention. by default, all arguments are `borrowed`. But `def` and `fn` \n", + "functions treat `borrowed` arguments somewhat differently:\n", "\n", - "- All values passed into a Mojo [`def`\n", - " function](/mojo/manual/functions.html#def-functions) are `owned`,\n", - " by default. \n", "\n", - "- All values passed into a Mojo [`fn`\n", - " function](/mojo/manual/functions.html#fn-functions) are `borrowed`,\n", - " by default.\n", + "- In an [`fn` function](/mojo/manual/functions.html#fn-functions), the function\n", + " always receives an immutable reference. If you want a mutable copy, you can\n", + " assign it to a local variable:\n", + "\n", + " ```mojo\n", + " var my_copy = borrowed_arg\n", + " ```\n", + "\n", + "- In a [`def` function](/mojo/manual/functions.html#def-functions), if the \n", + " function mutates the value, the function receives a mutable copy of the \n", + " argument. Otherwise, it receives an immutable reference. This allows you to\n", + " treat arguments as mutable, but avoid the overhead of making extra copies when\n", + " they're not needed.\n", + "\n", + "The difference between `borrowed` and `owned` in a `def` function may be a\n", + "little subtle: \n", + "\n", + "- In a `def` function, a `borrowed` argument is received as an immutable\n", + " reference, unless it's mutated in the body of the function. This eliminates\n", + " unneeded copies, but maintains the Python expectation that arguments are \n", + " mutable.\n", + "\n", + "- The `borrowed` argument always gets an immutable reference or a local copy.\n", + " You can't transfer a value into a `borrowed` argument.\n", + "\n", + "- The `owned` argument always gets a uniquely owned value, which may have been\n", + " copied or transferred from the callee. Using `owned` arguments without the \n", + " transfer operator (`^`) usually results in values being copied.\n", "\n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." @@ -126,9 +148,11 @@ "- Every value has only one owner at a time.\n", "- When the lifetime of the owner ends, Mojo destroys the value.\n", "\n", - "The \"borrow checker\" is a process in the Mojo compiler that ensures there is\n", - "only one owner for each value at any time. It also enforces some other\n", - "memory-safety rules:\n", + "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", + "that only one mutable reference to a value can exist at a time. **This is not\n", + "currently enforced.**\n", + "\n", + "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Immutable arguments (`borrowed`)\n", + "## Borrowed arguments (`borrowed`)\n", + "\n", + "The `borrowed` convention is the default for all arguments.\n", "\n", - "If you'd like your function to receive an **immutable reference**, add the\n", - "`borrowed` keyword in front of the argument name.\n", + "In `fn` functions, a `borrowed` argument is received as an immutable reference.\n", "\n", - "The `borrowed` convention is the default for all arguments in an `fn` function,\n", - "but you can still specify it to be explicit. It also works on `def` functions,\n", - "which otherwise receive arguments by value, which might not be desirable, such\n", - "as when the type is expensive to copy (or not copyable at all) and you just\n", - "need to read it. For example:" + "In `def` functions, you can treat a `borrowed` argument as mutable or immutable.\n", + "If you mutate the argument in the body of the function, you get a mutable copy\n", + "of the original value. If you don't mutate the argument, you get an immutable\n", + "reference, as in an `fn` function.\n", + "\n", + "For example:" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -177,7 +205,7 @@ "source": [ "from tensor import Tensor, TensorShape\n", "\n", - "def print_shape(borrowed tensor: Tensor[DType.float32]):\n", + "def print_shape(tensor: Tensor[DType.float32]):\n", " shape = tensor.shape()\n", " print(str(shape))\n", "\n", @@ -189,9 +217,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In general, passing an immutable reference is much more efficient when handling\n", - "large or expensive-to-copy values, because the copy constructor and destructor\n", - "are not invoked for a borrow.\n", + "Here the `tensor` argument is borrowed and not mutated, so the `print_shape()`\n", + "function gets an immutable reference to the original `Tensor`, and doesn't do \n", + "any copying. In general, passing an immutable reference is much more efficient\n", + "when handling large or expensive-to-copy values, because the copy constructor\n", + "and destructor are not invoked for a borrow.\n", "\n", "### Compared to C++ and Rust\n", "\n", @@ -200,10 +230,8 @@ "mutability in the callee. However, the borrowed convention differs from\n", "`const&` in C++ in two important ways:\n", "\n", - "- The Mojo compiler implements a borrow checker (similar to Rust) that prevents\n", - "code from dynamically forming mutable references to a value when there are\n", - "immutable references outstanding, and it prevents multiple mutable references\n", - "to the same value.\n", + "- The Mojo compiler implements a lifetime checker that ensures that values are\n", + "not destroyed when there are outstanding references to those values.\n", "\n", "- Small values like `Int`, `Float`, and `SIMD` are passed directly in machine\n", "registers instead of through an extra indirection (this is because they are\n", @@ -213,7 +241,8 @@ "when compared to languages like C++ and Rust, and moves this optimization from\n", "every call site to a declaration on the type definition.\n", "\n", - "Similar to Rust, Mojo's borrow checker enforces the exclusivity of invariants.\n", + "In the future, Mojo's lifetime checker will enforces the exclusivity of\n", + "mutable references, similar to Rust.\n", "The major difference between Rust and Mojo is that Mojo does not require a\n", "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", "passing small values, and Rust defaults to moving values instead of passing\n", @@ -331,19 +360,30 @@ "lifetime of that variable.\n", "\n", "Technically, the `owned` keyword does not guarantee that the received value is\n", - "a mutable reference to _the original value_—it guarantees only that the function\n", - "gets unique ownership of this particular value (enforcing [value\n", + "_the original value_—it guarantees only that the function\n", + "gets unique ownership of a value (enforcing [value\n", "semantics](/mojo/manual/values/value-semantics.html)). This happens in one of\n", - "two ways:\n", + "three ways:\n", "\n", "- The caller passes the argument with the `^` transfer operator, which ends the\n", - "lifetime of that variable (the variable becomes invalid) and ownership is\n", + "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", "transferred into the function without making a copy of any heap-allocated data.\n", "\n", "- The caller **does not** use the `^` transfer operator, in which case, the\n", "value is copied into the function argument and the original variable remains\n", - "valid (unless it is not used again, in which case the compiler destroys the\n", - "variable anyway because its lifetime naturally ends there).\n", + "valid. (If the original value is not used again, the compiler may optimize away\n", + "the copy and transfer the value).\n", + "\n", + "- The caller passes in a newly-created \"owned\" value, such as a value returned\n", + "from a function. In this case, no variable owns the value and it can be\n", + "transferred directly to the callee. For example:\n", + "\n", + " ```mojo\n", + " def take(owned s: String):\n", + " pass\n", + "\n", + " take(str(\"A brand-new String!\"))\n", + " ```\n", "\n", "Regardless, when the function declares an argument as `owned`, it can be certain\n", "that it has unique mutable access to that value. \n", @@ -395,7 +435,7 @@ " print(message) # ERROR: The `message` variable is uninitialized\n", "```\n", "\n", - "This is a critical feature of Mojo's borrow checker, because it ensures that no\n", + "This is a critical feature of Mojo's lifetime checker, because it ensures that no\n", "two variables can have ownership of the same value. To fix the error, you must\n", "not use the `message` variable after you end its lifetime with the `^` transfer\n", "operator. So here is the corrected code:" @@ -478,10 +518,10 @@ " [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn`\n", " requires all types be explicitly declared).\n", "\n", - "- A `def` argument without a convention keyword (`borrowed`, `inout`, or\n", - " `owned`) defaults to `owned` (it receives a copy with a mutable variable).\n", + "- A `def` function can treat a `borrowed` argument as mutable (in which case it\n", + " receives a mutable copy). An `fn` function must make this copy explicitly.\n", "\n", - "For example, these two functions have the exact same behavior:" + "For example, these two functions have the exact same behavior." ] }, { @@ -490,29 +530,11 @@ "metadata": {}, "outputs": [], "source": [ - "def example(borrowed a: Int, inout b: Int, c):\n", + "def def_example(a: Int, inout b: Int, owned c):\n", " pass\n", "\n", - "fn example(a: Int, inout b: Int, owned c: object):\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or, instead of specifying `owned` for the `c` argument, you can get the same\n", - "effect by manually making a copy when you need it:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn example(a: Int, inout b: Int, c_in: object):\n", - " var c = c_in\n", + "fn fn_example(a_in: Int, inout b: Int, owned c: object):\n", + " var a = a_in\n", " pass" ] }, @@ -521,8 +543,9 @@ "metadata": {}, "source": [ "This shadow copy typically adds no overhead, because references for small types\n", - "like `object` are cheap to copy. The expensive part is adjusting the reference\n", - "count, but that's also eliminated by a compiler optimization." + "like `object` are cheap to copy. However, copying large types that allocate heap\n", + "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", + "copying large numbers of strings.)" ] } ], diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index f405a34ab9..5ece6cbdb8 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -130,43 +130,40 @@ "\n", "In Mojo, the default behavior for all function arguments is to use value\n", "semantics. If the function wants to modify the value of an incoming argument,\n", - "then it must explicitly declare so, which avoids accidental mutations.\n", + "then it must explicitly declare so, which avoids accidental mutations of the\n", + "original value.\n", "\n", - "For starters, all Mojo types passed to a `def` function are passed by value,\n", - "which maintains the expected mutability behavior from Python. Except—contrary\n", - "to Python—the function has true ownership of the value, usually because it's a\n", - "copy.\n", + "All Mojo types passed to a `def` function can be treated as mutable,\n", + "which maintains the expected mutability behavior from Python. But by default, it\n", + "is mutating a uniquely-owned value, not the original value.\n", "\n", - "For example, even though the Mojo [`Tensor`](/mojo/stdlib/tensor/tensor.html)\n", - "type allocates values on the heap, when you pass an instance to a `def`\n", - "function, it creates a unique copy of all values. Thus, if we modify the\n", + "For example, when you pass an instance of a `SIMD` vector to a `def`\n", + "function it creates a unique copy of all values. Thus, if we modify the\n", "argument in the function, the original value is unchanged:" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Tensor([[1, 3]], dtype=uint8, shape=2)\n", - "Tensor([[1, 2]], dtype=uint8, shape=2)\n" + "[9, 2, 3, 4]\n", + "[1, 2, 3, 4]\n" ] } ], "source": [ - "def update_tensor(t: Tensor[DType.uint8]):\n", - " t[1] = 3\n", + "def update_simd(t: SIMD[DType.int32, 4]):\n", + " t[0] = 9\n", " print(t)\n", "\n", - "t = Tensor[DType.uint8](2)\n", - "t[0] = 1\n", - "t[1] = 2\n", - "update_tensor(t)\n", - "print(t)" + "v = SIMD[DType.int32, 4](1, 2, 3, 4)\n", + "update_simd(v)\n", + "print(v)" ] }, { @@ -189,10 +186,13 @@ "metadata": {}, "source": [ "The arguments above are mutable because a [`def`\n", - "function](/mojo/manual/functions.html#def-functions) gets ownership for\n", - "its arguments by default (usually as a copy). Whereas, `fn` functions instead\n", - "receive arguments as immutable references, by default. This is a memory\n", - "optimization to avoid making unnecessary copies.\n", + "function](/mojo/manual/functions.html#def-functions) has special treatment for\n", + "the default\n", + "[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions).\n", + "\n", + "Whereas, `fn` functions always receive `borrowed` arguments as immutable\n", + "references. This is a memory optimization to avoid making\n", + "unnecessary copies.\n", "\n", "For example, let's create another function with the `fn` declaration. In this\n", "case, the `y` argument is immutable by default, so if the function wants to\n", @@ -301,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -337,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "metadata": {}, "outputs": [ { diff --git a/docs/roadmap.md b/docs/roadmap.md index f19c0d9abf..7b84532a6e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -18,7 +18,7 @@ the long-term), so we want to fully build-out the core Mojo language features before we work on other dependent features and enhancements. Currently, that means we are focused on the core system programming features -that are essential to [Mojo's mission](why-mojo.html), and as outlined in the +that are essential to [Mojo's mission](/mojo/why-mojo), and as outlined in the following sections of this roadmap. In the near-term, we will **not** prioritize "general goodness" work such as: @@ -210,7 +210,7 @@ to use right now. ## Traits support As of v0.6.0 Mojo has basic support for -[traits](/mojo/manual/traits.html#built-in-traits). Traits allow you +[traits](/mojo/manual/traits). Traits allow you to specify a set of requirements for types to implement. Types can implement those requirements to *conform to* the trait. Traits allow you to write generic functions and generic containers, which can work with any type that @@ -221,7 +221,7 @@ Currently, the only kind of requirements supported by traits are required method signatures. The trait can't provide a default implementation for its required methods, so each conforming type must implement all of the required methods. -A number of [built-in traits](/mojo/manual/traits.html#built-in-traits) are +A number of [built-in traits](/mojo/manual/traits#built-in-traits) are already implemented in the standard library. We plan to expand traits support in future releases. Planned features include: From 52ba4e20b6a1dd6901b0cc30e960400a232d25ac Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 5 Jun 2024 18:58:05 -0700 Subject: [PATCH 0789/2019] Updating Docs to show how the new behavior of linking to libpython at runtime works. Also adds a script to find a suitable libpython on the user's machine and prints a command to resolve. Removes workarounds and notes for the previous behaviour. MODULAR_ORIG_COMMIT_REV_ID: b825d4d2834f4ae1244a936effd82b2de8f67578 --- docs/changelog-released.md | 8 ++ docs/manual/python/index.ipynb | 158 +++++++++++++++++++++++---------- docs/roadmap.md | 21 ----- 3 files changed, 120 insertions(+), 67 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 783135293e..1e3785e5d1 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -515,6 +515,14 @@ modular update mojo removed `let` declarations but still provided an error message to users. Now, it is completely gone from the grammar. Long live `var`! +- Mojo will now link to a Python dynamic library based on the Python on top of + your search path: `PATH`. This enables you to activate a virtual environment + like `conda` and have access to Python modules installed in that environment + without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a + `libpython` dynamic library on installation and put the path in + `.modular/modular.cfg`, which could result in version conflicts if you + activated a virtual environment of a different Python version. + - The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved from `math` to `builtin`, so you no longer need to do `from math import abs, round, min, max, divmod, pow`. diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 374bbbe44a..94d6ab5f2a 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -261,86 +261,152 @@ "\n", "## Python environment\n", "\n", - "The Mojo SDK depends on an existing installed version of Python that includes\n", - "a shared library version of the Python interpreter. When you install the Mojo\n", - "SDK, it tries to locate a compatible version of the Python interpreter and set\n", - "up Python's `sys.path` to load matching modules. In most cases this just works\n", - "and you don't have to do any further configuration of your Python environment. \n", + "The Mojo SDK depends on an existing Python dynamic library. At runtime, Mojo\n", + "uses the first Python in the search path (`PATH`), to find an associated dynamic\n", + "Python library of the same version. This will also add any modules from the\n", + "activated virtual environment. \n", "\n", - "If you run into problems after installing Mojo, see the following sections.\n", + "### Resolving issues\n", "\n", - "### Installation issues\n", + "Finding libpython may fail if the Python interpreter on top of `PATH` does not\n", + "have an associated dynamic library. Some Python distributions don't include the\n", + "shared library, and others only have a static library which isn't supported by\n", + "Mojo yet.\n", "\n", - "When the installer runs, it tries to locate the CPython shared library using the \n", - "[find_libpython](https://pypi.org/project/find-libpython/) module.\n", - "\n", - "This may fail if one of the following is true:\n", + "You can find a compatible Python on your system by running this Python script:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "python" + } + }, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "\n", + "FIND_LIBPYTHON = \"\"\"\n", + "import os\n", + "import sys\n", + "from pathlib import Path\n", + "from sysconfig import get_config_var\n", + "ext = \"dll\" if os.name == \"nt\" else \"dylib\" if sys.platform == \"darwin\" else \"so\"\n", + "binary = f\"libpython{get_config_var('py_version_short')}.{ext}\"\n", + "for folder in [Path(get_config_var(p)) for p in [\"LIBPL\", \"LIBDIR\"]]:\n", + " libpython_path = folder / binary\n", + " if libpython_path.is_file():\n", + " print(libpython_path.resolve())\n", + " exit(0)\n", + "exit(1)\n", + "\"\"\"\n", + "FIND_PYTHON_VER = \"import sysconfig; print(sysconfig.get_python_version())\"\n", + "\n", + "exe_names = [\"python3\", \"python\"] + [f\"python3.{i}\" for i in range(8, 13)]\n", + "seen = []\n", + "executables = []\n", + "\n", + "print(\"Mojo will attempt to use the first python executable from the top:\\n\")\n", + "print(\"vers | compat | path\")\n", + "for path in os.environ[\"PATH\"].split(\":\"):\n", + " for exe in exe_names:\n", + " full_path = os.path.join(path, exe)\n", + " if os.path.exists(full_path):\n", + " pyver = subprocess.check_output([full_path, \"-c\", FIND_PYTHON_VER], text=True).strip()\n", + " res = subprocess.run([full_path, \"-c\", FIND_LIBPYTHON], text=True, capture_output=True)\n", + " libpython = res.stdout.strip()\n", + " if res.returncode != 0:\n", + " print(f\"{pyver:<7} no {full_path}\")\n", + " elif libpython not in seen:\n", + " print(f\"{pyver:<7} yes {full_path}\")\n", + " seen.append(libpython)\n", + " executables.append(full_path)\n", + "\n", + "if not executables:\n", + " print(\"no compatible Python environments found\")\n", + "else:\n", + " print(\"\\ncreate and activate a virtual environment to use a different Python version:\")\n", + " print(f\" {executables[-1]} -m venv .venv\")\n", + " print(\" source .venv/bin/activate\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Which will produce output like:\n", "\n", - "- There is no version of Python installed, or the installed version isn't\n", - " supported by the Mojo SDK.\n", + "```output\n", + "Mojo will attempt to use the first python executable from the top:\n", "\n", - "- The installer can't find a shared library version of the CPython interpreter \n", - " (for example, `.so` or `.dylib` file). Some Python distributions don't include\n", - " shared libraries, which prevents Mojo from embedding the interpreter.\n", + "vers | compat | path\n", + "3.11 yes /opt/homebrew/opt/python@3.11/libexec/bin/python3\n", + "3.12 yes /opt/homebrew/bin/python3\n", + "3.9 yes /usr/bin/python3\n", "\n", - "If one of these things is the case, you'll need to install a compatible version\n", - "of Python that includes shared libraries. Try following the instructions in \n", - "[Set up a Python environment with Conda](#set-up-a-python-environment-with-conda)\n", - "to install a virtual environment. \n", + "create and activate a virtual environment to use a different Python version:\n", + " /usr/bin/python3 -m venv .venv\n", + " source .venv/bin/activate\n", + "```\n", "\n", + "If you have no compatible environment, you can install a compatible version of\n", + "Python that includes shared libraries. Try following the instructions in [Set up\n", + "a Python environment with Conda](#set-up-a-python-environment-with-conda) to\n", + "install a virtual environment. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "### Set up a Python environment with Conda\n", "\n", - "Using a Python virtual environment like \n", - "[Conda](https://docs.conda.io/en/latest/) is one way to avoid problems with \n", - "your Python installation. This provides a consistent Python environment with a\n", - "known version of Python and all of the Python packages you want to use with\n", - "Mojo.\n", + "Using a Python virtual environment such as \n", + "[Conda](https://docs.conda.io/en/latest/) is one way to get a version of Python\n", + "that will work reliably with Mojo. It comes with the required dynamic library,\n", + "and ensures there are no conflicts with system dependencies. \n", "\n", "To set up a virtual environment with Conda:\n", "\n", "1. Install Conda by following the \n", " [Quick command-line install instructions](https://docs.conda.io/projects/miniconda/en/latest/#quick-command-line-install).\n", "\n", - " Make sure to initialize Conda for the shell or shells you use, for example:\n", - "\n", - " ```bash\n", - " ~/miniconda3/bin/conda init zsh\n", - " ```\n", - "\n", - " Or:\n", + "2. Initialize Conda for all the shells on your path:\n", "\n", " ```bash\n", " ~/miniconda3/bin/conda init --all\n", " ```\n", "\n", - "2. Restart your shell. \n", - "\n", - "3. Run the following command to configure Mojo to use the Python shared library\n", - " from your Conda environment:\n", + " Or just one at a time:\n", "\n", " ```bash\n", - " export MOJO_PYTHON_LIBRARY=\"$(find $CONDA_PREFIX/lib -iname 'libpython*.[s,d]*' | sort -r | head -n 1)\"\n", - " echo \"export MOJO_PYTHON_LIBRARY=$MOJO_PYTHON_LIBRARY\" >> ~/.zshrc\n", + " ~/miniconda3/bin/conda init zsh\n", " ```\n", "\n", - " **Note:** If you're using a shell other than zsh, you'll need to adjust these\n", - " commands. For example, if you're using bash, replace `.zshrc` with the\n", - " shell configuration file you use, such as `.bashrc` or `.bash_profile`.\n", + "2. Restart your shell. \n", "\n", - "4. Try running the Mojo REPL:\n", + "3. Install your desired version of Python and activate the environment:\n", "\n", - " ```bash\n", - " mojo\n", - " ```\n", + " ```bash\n", + " conda create -n 3.10 python=3.10\n", + " conda activate 3.10\n", + " ```\n", "\n", "After setting up the Conda virtual environment, you can install any Python \n", - "packages you want to use with Mojo using the `conda install` command. For\n", + "packages you want to use with Mojo, with `conda install` or `pip install`. For\n", "example:\n", "\n", "```bash\n", "conda install numpy\n", + "pip install pillow\n", "```\n", "\n", + "Now whenever you `conda activate 3.10`, Mojo will be able to find any modules\n", + "you installed into that environment.\n", + "\n", "For more information on using Conda with Mojo, see \n", "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on\n", "the Modular Blog." diff --git a/docs/roadmap.md b/docs/roadmap.md index 7b84532a6e..31529aae5e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -94,27 +94,6 @@ systems. Here are some of the notable issues that we plan to fix: processors and Apple Silicon macOS. Support for more Linux distributions (including Debian and RHEL) and Windows is in progress. -- Python interoperability might fail when running a compiled Mojo program, with - the message - `Unable to locate a suitable libpython, please set MOJO_PYTHON_LIBRARY`. This - is because we currently do not embed the Python version into the Mojo binary. - For details and the workaround, see [issue - #551](https://github.com/modularml/mojo/issues/551). - -- Mojo programs that import NumPy might fail with the following error: - - ```plaintext - Importing the numpy C-extensions failed. This error can happen for - many reasons, often due to issues with your setup or how NumPy was - installed. - ``` - - This may occur because the version of NumPy doesn't match the Python - interpreter Mojo is using. As a workaround, follow the instructions in - [issue #1085](https://github.com/modularml/mojo/issues/1085#issuecomment-1771403719) - to install a Python virtual environment using Conda. This can solve many - issues with Python interoperability. - - Modular CLI install might fail and require `modular clean` before you re-install. From 56ef6c32414f2c143e4dd91dda08415519b6c2ec Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 5 Jun 2024 19:07:34 -0700 Subject: [PATCH 0790/2019] [Docs] Mojo 24.4 release changelog updates. It's a changelog. For Mojo. For 24.4. MODULAR_ORIG_COMMIT_REV_ID: ba2fdcf736b8ea14e15fa761e12ebcda5e4b74d0 --- docs/changelog-released.md | 1107 ++++++++++++++++++++---------------- 1 file changed, 616 insertions(+), 491 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 1e3785e5d1..3c0f4189a3 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,53 +25,87 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` -## UNRELEASED +## v24.4 (2024-06-06) -### 🔥 Legendary +### ✨ Highlights -### ⭐️ New +Big themes for this release: -- Add a `sort` function for list of `ComparableCollectionElement`s. - [PR #2609](https://github.com/modularml/mojo/pull/2609) by - [@mzaks](https://github.com/mzaks) +- Improvements to the performance and ease-of-use for `def` functions. -- Mojo functions can return an auto-dereferenced refeference to storage with a - new `ref` keyword in the result type specifier. For example: +- Continued unification of standard library APIs around the `UnsafePointer` + type. + +- Many quality-of-life improvements for the standard library collection types. + +- Significant performance improvements when inserting into a `Dict`. Performance + on this metric is still not where we'd like it to be, but it is much improved. + +- A new `@parameter for` mechanism for expressing compile-time loops, which + replaces the earlier (and less reliable) `@unroll` decorator. + +- New Mojo Manual pages on [Control flow](/mojo/manual/control-flow), + [Testing](/mojo/tools/testing) and using + [unsafe pointers](/mojo/manual/pointers). + +### Language changes + +- Mojo has changed how `def` function arguments are processed. Previously, by + default, arguments to a `def` were treated treated according to the `owned` + convention, which makes a copy of the value, enabling that value to be mutable + in the callee. + + This could lead to a major performance issues because of the proliferation of + unnecessary copies. It also required you to declare non-copyable types as + `borrowed` explicitly. Now Mojo takes a different approach: `def` functions + take arguments as `borrowed` by default (consistent with `fn` functions) but + will make a local copy of the value **only if the argument is mutated** in the + body of the function. + + This improves consistency, performance, and ease of use. + +- Implicit variable definitions in a `def` function are more flexible: you can + now implicitly declare variables as the result of a tuple return, using + `a,b,c = foo()`. For example: ```mojo - struct Pair: - var first: Int - var second: Int - fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: - return self.first - fn show_mutation(): - var somePair = ... - get_first_ref(somePair) = 1 - ``` + def return_two(i: Int) -> (Int, Int): + return i, i+1 - This approach provides a general way to return an "automatically dereferenced" - reference of a given type. Notably, this eliminates the need for - `__refitem__` to exist. `__refitem__` has thus been removed and replaced with - `__getitem__` that returns a reference. + a, b = return_two(5) + ``` -- Mojo has introduced `@parameter for`, a new feature for compile-time - programming. `@parameter for` defines a for loop where the sequence and the - induction values in the sequence must be parameter values. For example: + Implicit variable declarations can also now shadow global immutable symbols + (such as module names and built-ins) without getting a compiler error. + For example: ```mojo - fn parameter_for[max: Int](): - @parameter - for i in range(max) - @parameter - if i == 10: - print("found 10!") + slice = foo() ``` - Currently, `@parameter for` requires the sequence's `__iter__` method to - return a `_StridedRangeIterator`, meaning the induction variables must be - `Int`. The intention is to lift these restrictions in the future. +- Mojo functions can return an auto-dereferenced refeference to storage with a + new `ref` keyword in the result type specifier. For example: + + ```mojo + @value + struct Pair: + var first: Int + var second: Int + + fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + return self.first -- Mojo added support for the inferred parameters. Inferred parameters must + fn show_mutation(): + var somePair = Pair(5, 6) + somePair.get_first_ref() = 1 + ``` + + This approach provides a general way to return an "automatically dereferenced" + reference of a given type. Notably, this eliminates the need for + `__refitem__()` to exist. `__refitem__()` has thus been removed and replaced + with `__getitem__()` that returns a reference. + +- Mojo added support for _infer-only parameters_. Infer-only parameters must appear at the beginning of the parameter list and cannot be explicitly specified by the user. They are declared to the left of a `//` marker, much like positional-only parameters. This allows programmers to define functions @@ -87,8 +121,8 @@ modular update mojo ``` In the above example, `Int32(42)` is passed directly into `value`, the first - non-inferred parameter. `dt` is inferred from the parameter itself to be - `DType.int32`. + parameter that isn't infer-only. `dt` is inferred from the parameter itself + to be `DType.int32`. This also works with structs. For example: @@ -100,7 +134,9 @@ modular update mojo pass ``` - This should make working with dependent parameters more ergonomic. + This should make working with dependent parameters more ergonomic. See + [Infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) in the + Mojo Manual. - Mojo now allows functions overloaded on parameters to be resolved when forming references to, but not calling, those functions. For example, the following @@ -118,10 +154,10 @@ modular update mojo ``` - Mojo now supports adding a `@deprecated` decorator on structs, functions, - traits, aliases, and global variables. The decorator marks the attached decl - as deprecated and causes a warning to be emitted when the deprecated decl is - referenced in user code. The decorator requires a deprecation message to be - specified as a string literal. + traits, aliases, and global variables. The decorator marks the attached + declaration as deprecated and causes a warning to be emitted when the + deprecated declaration is referenced in user code. The decorator requires a + deprecation message, specified as a string literal. ```mojo @deprecated("Foo is deprecated, use Bar instead") @@ -139,488 +175,555 @@ modular update mojo bar() # warning: use another function! ``` -- Mojo has changed how `def` arguments are processed. Previously, by default, - arguments to a `def` were treated treated according to the `owned` convention, - which makes a copy of the value, enabling that value to be mutable in the callee. - This "worked", but was a major performance footgun, and required you to declare - non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: - it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local - copy of the value **only if the argument is mutated** in the body of the function. - This improves consistency, performance, and ease of use. - -- `int()` can now take a string and a specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is - specified, the string will be parsed as if it was an integer literal, with the - base determined by whether the string contains the prefix `"0x"`, `"0o"`, or - `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by - [@artemiogr97](https://github.com/artemiogr97), fixes - [#2274](https://github.com/modularml/mojo/issues/2274)) - -- Mojo now supports types to opt in to use the `abs` and `round` functions by - implementing the `__abs__` and `__round__` methods (i.e. by conforming to the - new `Absable` and `Roundable` traits), respectively, e.g.: +- Mojo has introduced + [`@parameter for`](/mojo/manual/decorators/parameter#parametric-for-statement), + a new feature for compile-time programming. `@parameter for` defines a for + loop where the sequence and the induction values in the sequence must be + parameter values. For example: ```mojo - from math import sqrt - - @value - struct Complex(Absable, Roundable): - var re: Float64 - var im: Float64 - - fn __abs__(self) -> Self: - return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) - - fn __round__(self) -> Self: - return Self(round(self.re), round(self.im)) - ``` - -- User defined types can now also opt in to use the `pow` function by - implementing the `__pow__` method (and thus conforming to the new `Powable` - trait). As before, these types will also benefit from being able to use the - `**` operator. - -- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` - functions in the `math` module by implementing the `__floor__()`, - `__ceil__()`, and `__trunc__()` methods (and so conforming to the new - `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). - For example: - - ```mojo - from math import Ceilable, Floorable, Truncable, ceil, floor, trunc - - @value - struct Complex(Ceilable, Floorable, Truncable): - var re: Float64 - var im: Float64 - - fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) - - fn __floor__(self) -> Self: - return Self(floor(re), floor(im)) - - fn __trunc__(self) -> Self: - return Self(trunc(re), trunc(im)) - ``` - -- You can now use the builtin `any()` and `all()` functions to check for truthy - elements in a collection. Because `SIMD.__bool__()` is now constrained to - `size=1`, You must explicity use these to get the truthy value of a SIMD - vector. This avoids common bugs around implicit conversion of `SIMD` to - `Bool`. - ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) - - For example: - - ```mojo - fn truthy_simd(): - var vec = SIMD[DType.int32, 4](0, 1, 2, 3) - if any(vec): - print("any elements are truthy") - if all(vec): - print("all elements are truthy") + fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") ``` -- Add an `InlinedArray` type that works on memory-only types. - Compare with the existing `StaticTuple` type, which is conceptually an array - type, but only worked on `AnyTrivialRegType`. - ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) - -- Base64 decoding support has been added. - ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) - -- Add Base16 encoding and decoding support. - ([PR #2584](https://github.com/modularml/mojo/pull/2584) - by [@kernhanda](https://github.com/kernhanda)) - -- Add `repr()` function and `Representable` trait. - ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Add `SIMD.shuffle()` with `StaticIntTuple` mask. - ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) - -- Invoking `mojo package my-package -o my-dir` on the command line, where - `my-package` is a Mojo package source directory, and `my-dir` is an existing - directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. - Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. - -- The Mojo Language Server now reports a warning when a local variable is unused. - -- Implicit variable definitions in a `def` are more flexible: you can now - implicitly declare variables as the result of a tuple return, using - `a,b,c = foo()`, and can now shadow global immutable symbols using - `slice = foo()` without getting a compiler error. - -- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that - allow users to opt into the `math.ceildiv` function. - -- Mojo now allows methods to declare `self` as a `Reference` directly, which - can be useful for advanced cases of parametric mutabilty and custom lifetime - processing. Previously it required the use of an internal MLIR type to - achieve this. + Currently, `@parameter for` requires the sequence's `__iter__()` method to + return a `_StridedRangeIterator`, meaning the induction variables must be + `Int`. The intention is to lift these restrictions in the future. - The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, not a low-level `__mlir_type.i1` value. This improves the ergonomics of spelling out a - `Reference` type explicitly. For example, to define a struct holding a - `Reference`, you can now write: - - ```mojo - struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: - var data: Reference[Int32, is_mutable, lifetime] - ``` - - Or to specify a field that is always immutable, `False` can be specified - as the mutability: + `Reference` type explicitly. - ```mojo - struct Foo[lifetime: AnyLifetime[False].type]: - var data: Reference[Int32, False, lifetime] - ``` +- Mojo will now link to a Python dynamic library based on the Python on top of + your search path: `PATH`. This enables you to activate a virtual environment + like `conda` and have access to Python modules installed in that environment + without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a + `libpython` dynamic library on installation and put the path in + `.modular/modular.cfg`, which could result in version conflicts if you + activated a virtual environment of a different Python version. -- `object` now implements all the bitwise operators. - ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) +- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids + binding non-trivial register-passable types to `AnyTrivialRegType`. This + closes a major safety hole in the language. Please use `AnyType` for generic + code going forward. -- A new `--validate-doc-strings` option has been added to `mojo` to emit errors - on invalid doc strings instead of warnings. +- The `let` keyword has been completely removed from the language. We previously + removed `let` declarations but still provided an error message to users. Now, + it is completely gone from the grammar. -- Several `mojo` subcommands now support a `--diagnostic-format` option that - changes the format with which errors, warnings, and other diagnostics are - printed. By specifying `--diagnostic-format json` on the command line, errors - and other diagnostics will be output in a structured - [JSON Lines](https://jsonlines.org) format that is easier for machines to - parse. +### Standard library changes - The full list of subcommands that support `--diagnostic-format` is as follows: - `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. - Further, the `mojo test --json` option has been subsumed into this new option; - for the same behavior, run `mojo test --diagnostic-format json`. +- New traits and related features: - Note that the format of the JSON output may change; we don't currently - guarantee its stability across releases of Mojo. + - Added built-in [`repr()`](mojo/stdlib/builtin/repr/repr) function and + [`Representable`](/mojo/stdlib/builtin/repr/Representable) trait. + ([PR #2361](https://github.com/modularml/mojo/pull/2361)) -- A new decorator, `@doc_private`, was added that can be used to hide a decl - from being generated in the output of `mojo doc`. It also removes the - requirement that the decl has documentation (e.g. when used with - --diagnose-missing-doc-strings). + - Added the [`Indexer`](/mojo/stdlib/builtin/int/Indexer) trait to denote + types that implement the `__index__()` method which allows these types to be + accepted in common `__getitem__()` and `__setitem__()` implementations, as + well as allow a new built-in + [`index()`](/mojo/stdlib/builtin/int/index-function) + function to be called on them. Most standard library containers can now be + indexed by any type that implements `Indexer`. For example: -- Added a new `Span` type for taking slices of contiguous collections. - ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 -- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type - in standard library code. + struct MyList: + var data: List[Int] - `StringSlice` is a non-owning reference to encoded string data. Unlike - `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it - points to. + fn __init__(inout self): + self.data = List[Int](1, 2, 3, 4) - - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. - - Changed `Formatter.write_str()` to take a safe `StringSlice`. + fn __getitem__[T: Indexer](self, idx: T) -> Int: + return self.data[index(idx)] -- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which - returns a `Span` of the bytes owned by the string. + print(MyList()[AlwaysZero()]) # prints `1` + ``` -- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers + Types conforming to the `Indexer` trait are implicitly convertible to Int. + This means you can write generic APIs that take `Int` instead of making them + take a generic type that conforms to `Indexer`. For example: -- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 -- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), - please use the default constructor instead: `UnsafePointer[T]()`. + @value + struct Incrementer: + fn __getitem__(self, idx: Int) -> Int: + return idx + 1 -- `Dict` now implements `get(key)` and `get(key, default)` functions. - ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) + var a = Incrementer() + print(a[AlwaysZero()]) # works and prints 1 + ``` -- Debugger users can now set breakpoints on function calls in O0 builds even if - the call has been inlined by the compiler. + ([PR #2685](https://github.com/modularml/mojo/pull/2685)) -- The `os` module now provides functionality for adding and removing directories - using `mkdir` and `rmdir`. - ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) + - Added traits allowing user-defined types to be supported by various + built-in and math functions. -- `Dict.__get_ref(key)`, allowing to get references to dictionary values. + | Function | Trait | Required method | + |------------------|------------------|-----------------| + | [`abs()`](/mojo/stdlib/builtin/math/abs) | [`Absable`](/mojo/stdlib/builtin/math/Absable) | `__abs__()` | + | [`pow()`](/mojo/stdlib/builtin/math/pow) | [`Powable`](/mojo/stdlib/builtin/math/Powable) | `__pow__()` | + | [`round()`](/mojo/stdlib/builtin/math/round) | [`Roundable`](/mojo/stdlib/builtin/math/Roundable) | `__round__()` | + | [`math.ceil`](/mojo/stdlib/math/math/ceil) | `math.Ceilable` | `__ceil__()` | + | [`math.ceildiv`](/mojo/stdlib/math/math/ceildiv) | `math.CeilDivable`
`math.CeilDivableRaising` | `__ceildiv__()` | + | [`math.floor`](/mojo/stdlib/math/math/floor) | `math.Floorable` | `__floor__()` | + | [`math.trunc`](/mojo/stdlib/math/math/trunc) | `Truncable` | `__trunc__()` | -- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters - other than whitespace. In addition, there are now several useful aliases for - whitespace, ASCII lower/uppercase, and so on. - ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) + Notes: -- `String` now has a `splitlines()` method, which allows splitting strings at line - boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) - and provides an option to retain or remove the line break characters. - ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) + - Conforming to the `Powable` trait also means that the type can be used + with the power operator (`**`). -- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. - ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + - For `ceildiv()`, structs can conform to either the `CeilDivable` trait + or `CeilDivableRaising` trait. -- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. - ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) + - Due to ongoing refactoring, the traits `Ceilable`, `CeilDivable`, + `Floorable`, and `Truncable` do not appear in the API reference. They + should be imported from the `math` module, except for `Truncable` which + is (temporarily) available as a built-in trait and does not need to be + imported. -- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. - Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` - is not possible yet. - ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + Example: -- `List()` now supports `__contains__`. - ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) + ```mojo + from math import sqrt -- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) - type, a stack-allocated list with a static maximum size. - ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + @value + struct Complex2(Absable, Roundable): + var re: Float64 + var im: Float64 -- `InlineList()` now supports `__contains__`, `__iter__`. - ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) + fn __abs__(self) -> Self: + return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) -- `List` now has an `index` method that allows one to find the (first) location - of an element in a `List` of `EqualityComparable` types. For example: + fn __round__(self) -> Self: + return Self(round(self.re, 0), round(self.im, 0)) - ```mojo - var my_list = List[Int](2, 3, 5, 7, 3) - print(my_list.index(3)) # prints 1 - ``` + fn __round__(self, ndigits: Int) -> Self: + return Self(round(self.re, ndigits), round(self.im, ndigits)) -- `List` can now be converted to a `String` with a simplified syntax: + ``` - ```mojo - var my_list = List[Int](2, 3) - print(my_list.__str__()) # prints [2, 3] - ``` +- Benchmarking: + + - The [`bencher`](/mojo/stdlib/benchmark/bencher/) module as part of the + `benchmark` package is now public and documented. This module provides + types such as `Bencher` which provides the ability to execute a `Benchmark` + and allows for benchmarking configuration via the `BenchmarkConfig` struct. + +- [`String`](/mojo/stdlib/builtin/string/String) and friends: + + - **Breaking.** Implicit conversion to `String` is now removed for builtin + classes/types. Use [`str()`](/mojo/stdlib/builtin/str/str) explicitly to + convert to `String`. + + - Added [`String.isspace()`](/mojo/stdlib/builtin/string/String#isspace) + method conformant with Python's universal separators. This replaces the + `isspace()` free function from the `string` module. (If you need the old + function, it is temporarily available as `_isspace()`. It now takes a + `UInt8` but is otherwise unchanged.) + + - [`String.split()`](/mojo/stdlib/builtin/string/String#split) now defaults to + whitespace and has Pythonic behavior in that it removes all adjacent + whitespace by default. + + - [`String.strip()`](/mojo/stdlib/builtin/string/String#strip), + [`lstrip()`](/mojo/stdlib/builtin/string/String#lstrip) and + [`rstrip()`](/mojo/stdlib/builtin/string/String#rstrip) can now remove + custom characters other than whitespace. In addition, there are now several + useful aliases for whitespace, ASCII lower/uppercase, and so on. + ([PR #2555](https://github.com/modularml/mojo/pull/2555)) + + - `String` now has a + [`splitlines()`](/mojo/stdlib/builtin/string/String#splitlines) method, + which allows splitting strings at line boundaries. This method supports + [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) + and provides an option to retain or remove the line break characters. + ([PR #2810](https://github.com/modularml/mojo/pull/2810)) + + - `InlinedString` has been renamed to + [`InlineString`](/mojo/stdlib/utils/inline_string/InlineString) to be + consistent with other types. + + - [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) now implements + [`strip()`](/mojo/stdlib/utils/stringref/StringRef#strip), which can be used + to remove leading and trailing whitespace. + ([PR #2683](https://github.com/modularml/mojo/pull/2683)) + + - `StringRef` now implements + [`startswith()`](/mojo/stdlib/utils/stringref/StringRef#startswith) and + [`endswith()`](/mojo/stdlib/utils/stringref/StringRef#endswith). + ([PR #2710](https://github.com/modularml/mojo/pull/2710)) + + - Added a new [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) + type, to replace uses of the unsafe `StringRef` type in standard library + code. + + `StringSlice` is a non-owning reference to encoded string data. Unlike + `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it + points to. + + - Added new + [`as_string_slice()`](/mojo/stdlib/builtin/string/String#as_string_slice) + methods to `String` and `StringLiteral`. + - Added `StringSlice` initializer from an `UnsafePointer` and a length in + bytes. + + - Added a new + [`as_bytes_slice()`](/mojo/stdlib/builtin/string/String#as_bytes_slice) + method to `String` and `StringLiteral`, which + returns a `Span` of the bytes owned by the string. + + - Continued transition to + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and + unsigned byte type for strings: + - Renamed `String._as_ptr()` to + [`String.unsafe_ptr()`](/mojo/stdlib/builtin/string/String#unsafe_ptr), + and changed return type to `UnsafePointer` (was `DTypePointer`). + - Renamed `StringLiteral.data()` to + [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr), + and changed return type to `UnsafePointer` (was `DTypePointer`). + - `InlineString.as_ptr()` has been renamed to + [`unsafe_ptr()`](/mojo/stdlib/utils/inline_string/InlineString#unsafe_ptr) + and now returns an `UnsafePointer[UInt8]` (was + `DTypePointer[DType.int8]`). + - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) and + [`StringRef.unsafe_ptr()`](/mojo/stdlib/utils/stringref/StringRef#unsafe_ptr) + now returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + +- Other built-ins: + + - The `Slice.__len__()` function has been removed and + [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) no longer conforms + to the `Sized` trait. This clarifies the ambiguity of the semantics: the + length of a slice always depends on the length of the object being sliced. + Users that need the existing functionality can use the + [`Slice.unsafe_indices()`](/mojo/stdlib/builtin/builtin_slice/Slice#unsafe_indices) + method. This makes it explicit that this implementation does not check if + the slice bounds are concrete or within any given object's length. + + - Added a built-in [`sort()`](/mojo/stdlib/builtin/sort/sort) function for + lists of elements that conform to the + [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement) + trait.([PR #2609](https://github.com/modularml/mojo/pull/2609)) + + - [`int()`](/mojo/stdlib/builtin/int/int-function) can now take a string and a + specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with + the base determined by whether the string contains the prefix `"0x"`, + `"0o"`, or `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273), + fixes [#2274](https://github.com/modularml/mojo/issues/2274)) + + - Added the [`bin()`](/mojo/stdlib/builtin/format_int/bin) built-in function + to convert integral types into their binary + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603)) + + - Added the [`atof()`](/mojo/stdlib/builtin/string/atof) built-in function, + which can convert a `String` to a `float64`. + ([PR #2649](https://github.com/modularml/mojo/pull/2649)) + + - You can now use the built-in [`any()`](/mojo/stdlib/builtin/bool/any) and + [`all()`](/mojo/stdlib/builtin/bool/all) functions to check for truthy + elements in a collection. Because `SIMD.__bool__()` is now constrained to + `size=1`, You must explicitly use these to get the truthy value of a SIMD + vector with more than one element. This avoids common bugs around implicit + conversion of `SIMD` to `Bool`. + ([PR #2600](https://github.com/modularml/mojo/pull/2600)) + + For example: - Note that `List` doesn't conform to the `Stringable` trait yet so you cannot - use `str(my_list)` yet. - ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + ```mojo + fn truthy_simd(): + var vec = SIMD[DType.int32, 4](0, 1, 2, 3) + if any(vec): + print("any elements are truthy") + if all(vec): + print("all elements are truthy") + ``` -- Added the `Indexer` trait to denote types that implement the `__index__()` - method which allows these types to be accepted in common `__getitem__` and - `__setitem__` implementations, as well as allow a new builtin `index` function - to be called on them. Most stdlib containers are now able to be indexed by - any type that implements `Indexer`. For example: + - [`object`](/mojo/stdlib/builtin/object/) now implements all the bitwise + operators. + ([PR #2324](https://github.com/modularml/mojo/pull/2324)) - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 + - [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now supports `__contains__()`. + ([PR #2709](https://github.com/modularml/mojo/pull/2709)) For example: - struct MyList: - var data: List[Int] + ```mojo + var x = Tuple(1, 2, True) + if 1 in x: + print("x contains 1") + ``` - fn __init__(inout self): - self.data = List[Int](1, 2, 3, 4) + - [`ListLiteral`](/mojo/stdlib/builtin/builtin_list/ListLiteral) and `Tuple` + now only require that element types be `Movable`. Consequently, + `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - fn __getitem__[T: Indexer](self, idx: T) -> T: - return self.data[index(idx)] + - Added new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers. - print(MyList()[AlwaysZero()]) # prints `1` - ``` +- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and + others: - ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + - Added new [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload for + `UnsafePointer[Scalar[_]]` pointers. - Types conforming to the `Indexer` trait are implicitly convertible to Int. - This means you can write generic APIs that take `Int` instead of making them - take a generic type that conforms to `Indexer`, e.g. + - Removed the `get_null()` method from `UnsafePointer` and other pointer + types. Please use the default constructor instead: `UnsafePointer[T]()`. - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 + - Many functions returning a pointer type have been unified to have a public + API function of `unsafe_ptr()`. - @value - struct Incrementer: - fn __getitem__(self, idx: Int) -> Int: - return idx + 1 +- Collections: - var a = Incrementer() - print(a[AlwaysZero()]) # works and prints 1 - ``` + - [`List`](/mojo/stdlib/collections/list/List) now has an + [`index()`](/mojo/stdlib/collections/list/List#index) method that allows you + to find the (first) location of an element in a `List` of + `EqualityComparable` types. For example: -- `StringRef` now implements `strip()` which can be used to remove leading and - trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) - by [@fknfilewalker](https://github.com/fknfilewalker)) + ```mojo + var my_list = List[Int](2, 3, 5, 7, 3) + print(my_list.index(3)) # prints 1 + ``` -- The `bencher` module as part of the `benchmark` package is now public - and documented. This module provides types such as `Bencher` which provides - the ability to execute a `Benchmark` and allows for benchmarking configuration - via the `BenchmarkConfig` struct. + - `List` can now be converted to a `String` with a simplified syntax: -- Added the `bin()` builtin function to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) - by [@bgreni](https://github.com/bgreni)) + ```mojo + var my_list = List[Int](2, 3) + print(my_list.__str__()) # prints [2, 3] + ``` -- Added `atof()` function which can convert a `String` to a `float64`. - ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) + Note that `List` doesn't conform to the `Stringable` trait yet so you cannot + use `str(my_list)` yet. + ([PR #2673](https://github.com/modularml/mojo/pull/2673)) -- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) - by [@rd4com](https://github.com/rd4com)) For example: + - `List` has a simplified syntax to call the + [`count()`](/mojo/stdlib/collections/list/List#count) method: + `my_list.count(x)`. + ([PR #2675](https://github.com/modularml/mojo/pull/2675)) - ```mojo - var x = Tuple(1, 2, True) - if 1 in x: - print("x contains 1") - ``` + - `List()` now supports `__contains__()`, so you can now use lists with the + `in` operator: -- Added `os.getsize` function, which gives the size in bytes of a path. - ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) + ```mojo + if x in my_list: + ``` -- `List` now has a method `unsafe_get` to get the reference to an - element without bounds check or wraparound for negative indices. - Note that this method is unsafe. Use with caution. - ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + ([PR #2667](https://github.com/modularml/mojo/pull/2667)) -- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys - and value. - ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) + - `List` now has an + [`unsafe_get()`](/mojo/stdlib/collections/list/List#unsafe_get) to get the + reference to an element without bounds check or wraparound for negative + indices. Note that this method is unsafe. Use with caution. + [PR #2800](https://github.com/modularml/mojo/pull/2800)) -- Added `clear` method to `Dict`. - ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) + - Added a [`fromkeys()`](/mojo/stdlib/collections/dict/Dict#fromkeys) method + to `Dict` to return a `Dict` with the specified keys and values. + ([PR 2622](https://github.com/modularml/mojo/pull/2622)) -- Added `os.path.join` function. - ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) + - Added a [`clear()`](/mojo/stdlib/collections/dict/Dict#clear) method to + `Dict`. ([PR 2627](https://github.com/modularml/mojo/pull/2627)) -- `StringRef` now implements `startswith()` and `endswith()`. - ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) + - `Dict` now supports [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) + for its `items()` and `values()` iterators. + ([PR #2340](https://github.com/modularml/mojo/pull/2340)) -- The Mojo Language Server now supports renaming local variables. + - `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. + Note that `Dict` does not conform to the `Stringable` trait so + `str(my_dict)` is not possible yet. + ([PR #2674](https://github.com/modularml/mojo/pull/2674)) -- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. - ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) + - `Dict` now implements [`get(key)`](/mojo/stdlib/collections/dict/Dict#get) + and `get(key, default)` functions. + ([PR #2519](https://github.com/modularml/mojo/pull/2519)) + + - Added a temporary `__get_ref(key)` method to `Dict`, allowing you to get a + `Reference` to a dictionary value. + + - Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) + type, a stack-allocated list with a static maximum size. + ([PR 2587#](https://github.com/modularml/mojo/pull/2587)) + ([PR #2703](https://github.com/modularml/mojo/pull/2703)) + + - Added a new [`Span`](/mojo/stdlib/utils/span/Span) type for taking slices of + contiguous collections. + ([PR #2595](https://github.com/modularml/mojo/pull/2595)) + +- [`os`](/mojo/stdlib/os/os/) module: + + - The `os` module now provides functionality for adding and removing + directories using [`mkdir()`](/mojo/stdlib/os/os/mkdir) and + [`rmdir()`](/mojo/stdlib/os/os/rmdir). + ([PR #2430](https://github.com/modularml/mojo/pull/2430)) + + - Added the [`os.path.getsize()`](/mojo/stdlib/os/path/path/getsize) function, + which gives the size in bytes of the file identified by the path. + ([PR 2626](https://github.com/modularml/mojo/pull/2626)) + + - Added [`os.path.join()`](/mojo/stdlib/os/path/path/join) function. + ([PR 2792](https://github.com/modularml/mojo/pull/2792)) + + - Added a new [`tempfile`](/mojo/stdlib/tempfile/tempfile/) module, with + `gettempdir()` and `mkdtemp()` functions. + ([PR 2742](https://github.com/modularml/mojo/pull/2742)) + +- [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type: + + - Added [`SIMD.shuffle()`](/mojo/stdlib/builtin/simd/SIMD#shuffle) with + `StaticIntTuple` mask. + ([PR #2315](https://github.com/modularml/mojo/pull/2315)) + + - [`SIMD.__bool__()`](/mojo/stdlib/builtin/simd/SIMD#bool) is constrained + such that it only works when `size` is `1`. For SIMD vectors with more than + one element, use [`any()`](/mojo/stdlib/builtin/bool/any) or + [`all()`](/mojo/stdlib/builtin/bool/all). + ([PR #2502](https://github.com/modularml/mojo/pull/2502)) + + - The [`SIMD.reduce_or()`](/mojo/stdlib/builtin/simd/SIMD#reduce_or) and + [`SIMD.reduce_and()`](/mojo/stdlib/builtin/simd/SIMD#reduce_and) methods are + now bitwise operations, and support integer types. + ([PR #2671](https://github.com/modularml/mojo/pull/2671)) + + - Added [`SIMD.__repr__()`](/mojo/stdlib/builtin/simd/SIMD#__repr__) to get + the verbose string representation of `SIMD` types. + ([PR #2728](https://github.com/modularml/mojo/pull/2728)) + +- [`math`](/mojo/stdlib/math/math/) package: + + - The `math.bit` module has been moved to a new top-level + [`bit`](/mojo/stdlib/bit/bit/) module. The following functions in this + module have been renamed: + - `ctlz` -> `countl_zero` + - `cttz` -> `countr_zero` + - `bit_length` -> `bit_width` + - `ctpop` -> `pop_count` + - `bswap` -> `byte_swap` + - `bitreverse` -> `bit_reverse` + + - The `math.rotate_bits_left()` and `math.rotate_bits_right()` functions have + been moved to the `bit` module. + + - The `is_power_of_2()` function in the `math` module is now called + `is_power_of_two()` and located in the `bit` module. + + - The `abs()`, `round()`, `min()`, `max()`, `pow()`, and `divmod()` functions + have moved from `math` to `builtin`, so you no longer need to import these + functions. + + - The `math.tgamma()` function has been renamed to + [`math.gamma()`](/mojo/stdlib/math/math/gamma) to conform with Python's + naming. + + - The implementation of the following functions have been moved from the + `math` module to the new [`utils.numerics`](/mojo/stdlib/utils/numerics/) + module: `isfinite()`, `isinf()`, `isnan()`, `nan()`, `nextafter()`, and + `ulp()`. The functions continue to be exposed in the `math` module. + + - [`math.gcd()`](/mojo/stdlib/math/math/gcd) now works on negative inputs, and + like Python's implementation, accepts a variadic list of integers. New + overloads for a `List` or `Span`of integers are also added. + ([PR #2777](https://github.com/modularml/mojo/pull/2777)) + +- Async and coroutines: + + - [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) now requires a + lifetime parameter. This parameter is set automatically by the parser when + calling an async function. It contains the lifetimes of all the arguments + and any lifetime accesses by the arguments. This ensures that argument + captures by async functions keep the arguments alive as long as the + coroutine is alive. + + - Async function calls are no longer allowed to borrow non-trivial + register-passable types. Because async functions capture their arguments but + register-passable types don't have lifetimes (yet), Mojo is not able to + correctly track the reference, making this unsafe. To cover this safety gap, + Mojo has temporarily disallowed binding non-trivial register-passable types + to borrowed arguments in async functions. + +- Miscellaneous: + + - Added an [`InlineArray`](/mojo/stdlib/utils/static_tuple/InlineArray) type + that works on memory-only types. Compare with the existing + [`StaticTuple`](/mojo/stdlib/utils/static_tuple/StaticTuple) type, which is + conceptually an array type, but only works on `AnyTrivialRegType`. + ([PR #2294](https://github.com/modularml/mojo/pull/2294)) + + - The [`base64`](/mojo/stdlib/base64/) package now includes encoding and + decoding support for both the Base64 and Base16 encoding schemes. + ([PR #2364](https://github.com/modularml/mojo/pull/2364)) + ([PR #2584](https://github.com/modularml/mojo/pull/2584)) + + - The `take()` function in [`Variant`](/mojo/stdlib/utils/variant/Variant) and + [`Optional`](/mojo/stdlib/collections/optional/Optional) has been renamed to + `unsafe_take()`. + + - The `get()` function in `Variant` has been replaced by `__getitem__()`. That + is, `v.get[T]()` should be replaced with `v[T]`. + + - Various functions in the `algorithm` module are now built-in functions. This + includes `sort()`, `swap()`, and `partition()`. `swap()` and `partition()` + will likely shuffle around as we're reworking our built-in `sort()` function + and optimizing it. -- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. -([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) +### Tooling changes -### 🦋 Changed +- Invoking `mojo package my-package -o my-dir` on the command line, where + `my-package` is a Mojo package source directory, and `my-dir` is an existing + directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. + Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. -- `Coroutine` now requires a lifetime parameter. This parameter is set - automatically by the parser when calling an async function. It contains the - lifetimes of all the arguments and any lifetime accesses by the arguments. - This ensures that argument captures by async functions keep the arguments - alive as long as the coroutine is alive. +- The Mojo Language Server now reports a warning when a local variable is + unused. -- Async function calls are no longer allowed to borrow non-trivial - register-passable types. Because async functions capture their arguments but - register-passable types don't have lifetimes (yet), Mojo is not able to - correctly track the reference, making this unsafe. To cover this safety gap, - Mojo has temporarily disallowed binding non-trivial register-passable types - to borrowed arguments in async functions. +- Several `mojo` subcommands now support a `--diagnostic-format` option that + changes the format with which errors, warnings, and other diagnostics are + printed. By specifying `--diagnostic-format json` on the command line, errors + and other diagnostics will be output in a structured + [JSON Lines](https://jsonlines.org) format that is easier for machines to + parse. -- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids - binding non-trivial register-passable types to `AnyTrivialRegType`. This - closes a major safety hole in the language. Please use `AnyType` for generic - code going forward. + The full list of subcommands that support `--diagnostic-format` is as follows: + `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. + Further, the `mojo test --json` option has been subsumed into this new option; + for the same behavior, run `mojo test --diagnostic-format json`. -- The `let` keyword has been completely removed from the language. We previously - removed `let` declarations but still provided an error message to users. Now, - it is completely gone from the grammar. Long live `var`! + Note that the format of the JSON output may change; we don't currently + guarantee its stability across releases of Mojo. -- Mojo will now link to a Python dynamic library based on the Python on top of - your search path: `PATH`. This enables you to activate a virtual environment - like `conda` and have access to Python modules installed in that environment - without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a - `libpython` dynamic library on installation and put the path in - `.modular/modular.cfg`, which could result in version conflicts if you - activated a virtual environment of a different Python version. +- A new `--validate-doc-strings` option has been added to `mojo` to emit errors + on invalid doc strings instead of warnings. -- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved - from `math` to `builtin`, so you no longer need to do - `from math import abs, round, min, max, divmod, pow`. +- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to + `--diagnose-missing-doc-strings`. - - The `is_power_of_2` function in the `math` module is now called - `is_power_of_two` and located in the `bit` module. +- A new decorator, `@doc_private`, was added that can be used to hide a + declaration from being generated in the output of `mojo doc`. It also removes + the requirement that the declaration has documentation (for example, when used + with `--diagnose-missing-doc-strings`). -- Many functions returning a pointer type have been unified to have a public - API function of `unsafe_ptr()`. +- Debugger users can now set breakpoints on function calls in O0 builds even if + the call has been inlined by the compiler. -- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to - `--diagnose-missing-doc-strings`. +- The Mojo Language Server now supports renaming local variables. -- The `take` function in `Variant` and `Optional` has been renamed to - `unsafe_take`. - -- The `get` function in `Variant` has been replaced by `__refitem__`. That is, - `v.get[T]()` should be replaced with `v[T]`. - -- Various functions in the `algorithm` module are now moved to be - builtin-functions. This includes `sort`, `swap`, and `partition`. - `swap` and `partition` will likely shuffle around as we're reworking - our builtin `sort` function and optimizing it. - -- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, - explicitly use `any()` or `all()`. - ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) - -- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise - operations, and support integer types. - ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) - -- `ListLiteral` and `Tuple` now only require that element types be `Movable`. - Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - -- Continued transition to `UnsafePointer` and unsigned byte type for strings: - - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) - - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was - `DTypePointer`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was - `DTypePointer`). - - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now - returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). - - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) - - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was - `DTypePointer[DType.int8]`). - - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has - the same behavior. - -- Added `String.isspace()` method conformant with Python's universal separators. - -- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), - use `String.isspace()` instead. - -- `String.split()` now defaults to whitespace and has pythonic behavior in that - it removes all adjacent whitespaces by default. - -- Added `UnsafePointer.offset()` method. - -- The `math.bit` module has been moved to a new top-level `bit` module. The - following functions in this module have been renamed: - - `ctlz` -> `countl_zero` - - `cttz` -> `countr_zero` - - `bit_length` -> `bit_width` - - `ctpop` -> `pop_count` - - `bswap` -> `byte_swap` - - `bitreverse` -> `bit_reverse` - -- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been - moved to the `bit` module. - -- The `math.tgamma` function has been renamed to `math.gamma` to conform with - Python's naming. - -- The implementation of the following functions have been moved from the `math` - module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, - `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the - `math` module. - -- `InlinedString` has been renamed to `InlineString` to be consistent with other - types. - -- The `Slice.__len__` function has been removed and `Slice` no longer conforms - to the `Sized` trait. This clarifies the ambiguity of the semantics: the - length of a slice always depends on the length of the object being sliced. - Users that need the existing functionality can use the `Slice.unsafe_indices` - method. This makes it explicit that this implementation does not check if the - slice bounds are concrete or within any given object's length. - -- Implicit conversion to `String` is now removed for builtin classes/types. - One should use `str(...)` explicitly to convert to `String`. - -- `math.gcd` now works on negative inputs, and like Python's implementation, - accepts a variadic list of integers. New overloads for a `List` or `Span`of - integers are also added. - ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) +### Other changes -### ❌ Removed +#### ❌ Removed - The `@unroll` decorator has been deprecated and removed. The decorator was supposed to guarantee that a decorated loop would be unrolled, or else the @@ -630,60 +733,62 @@ modular update mojo parameter values, limiting its usefulness. Please see `@parameter for` for a replacement! -- The method `object.print()` has been removed. Since now, `object` has the - `Stringable` trait, you can use `print(my_object)` instead. +- The method `object.print()` has been removed. Since `object` now conforms to + the `Stringable` trait, you can use `print(my_object)` instead. - The following functions have been removed from the math module: - - `clamp`; use the new `SIMD.clamp` method instead. - - `round_half_down` and `round_half_up`; these can be trivially implemented - using the `ceil` and `floor` functions. - - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, - `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and - `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, - `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, - respectively. - - `identity` and `reciprocal`; users can implement these trivially. - - `select`; in favor of using `SIMD.select` directly. - - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` - with `1`. - - `roundeven`; the new `SIMD.roundeven` method now provides the identical + - `clamp()`; use the new `SIMD.clamp()` method instead. + - `round_half_down()` and `round_half_up()`; these can be trivially implemented + using the `ceil()` and `floor()` functions. + - `add()`, `sub()`, `mul()`, `div()`, `mod()`, `greater()`, `greater_equal()`, + `less()`, `less_equal()`, `equal()`, `not_equal()`, `logical_and()`, + `logical_xor()`, and `logical_not()`; Instead, users should rely directly on + the corresponding operators (`+`, `-`, `*`, `/`, `%`, `>`, `>=`, `<`, `<=`, + `==`, `!=`, `&`, `^`, and `~`). + - `identity()` and `reciprocal()`; users can implement these trivially. + - `select()`; removed in favor of using `SIMD.select()` directly. + - `is_even()` and `is_odd()`; these can be trivially implemented using bitwise + `&` with `1`. + - `roundeven()`; the new `SIMD.roundeven()` method now provides the identical functionality. - - `div_ceil`; use the new `ceildiv` function. - - `rotate_left` and `rotate_right`; the same functionality is available in the - builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the - `bit.rotate_bits_{left,right}` methods for `Int`. - - an overload of `math.pow` taking an integer parameter exponent. - - `align_down_residual`; it can be trivially implemented using `align_down`. - - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and - `SIMD.reduce_or` directly. - - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. - - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. + - `div_ceil()`; use the new `ceildiv()` function. + - `rotate_left()` and `rotate_right()`; the same functionality is available in + the builtin `SIMD.rotate_{left,right}()` methods for `SIMD` types, and the + `bit.rotate_bits_{left,right})()` methods for `Int`. + - An overload of `math.pow()` taking an integer parameter exponent. + - `align_down_residual()`; it can be trivially implemented using + `align_down()`. + - `all_true()`, `any_true()`, and `none_true()`; use `SIMD.reduce_and()` and + `SIMD.reduce_or()` directly. + - `reduce_bit_count()`; use the new `SIMD.reduce_bit_count()` directly. + - `rint()` and `nearbyint()`; use `round()` or `SIMD.roundeven()` as + appropriate. - The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's method is no longer available. This method was limited to degree 10 or less, underutilized, and its performance unclear. In the future, this might be reintroduced with an improved implementation if needed, when better performance benchmarking infrastructure is available. The default behavior of - `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). + `math.polynomial.polynomial_evaluate()` is unchanged (Horner's method). -- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The - same functionality is available in the builtin `SIMD.select` and - `SIMD.__and__` methods, respectively. +- The `math.bit.select()` and `math.bit.bit_and()` functions have been removed. + The same functionality is available in the builtin `SIMD.select` and + `SIMD.__and__()` methods, respectively. - The `math.limit` module has been removed. The same functionality is available as follows: - - `math.limit.inf`: use `utils.numerics.max_or_inf` - - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` - - `math.limit.max_finite`: use `utils.numerics.max_finite` - - `math.limit.min_finite`: use `utils.numerics.min_finite` + - `math.limit.inf()`: use `utils.numerics.max_or_inf()` + - `math.limit.neginf()`: use `utils.numerics.min_or_neg_inf()` + - `math.limit.max_finite()`: use `utils.numerics.max_finite()` + - `math.limit.min_finite()`: use `utils.numerics.min_finite()` - The `tensor.random` module has been removed. The same functionality is now - accessible via the `Tensor.rand` and `Tensor.randn` static methods. + accessible via the `Tensor.rand()` and `Tensor.randn()` static methods. - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. -### 🛠️ Fixed +#### 🛠️ Fixed - [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential variant crashing the compiler. @@ -697,6 +802,26 @@ modular update mojo - [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` to include calling location. +### Special thanks + +Special thanks to our community contributors: + +[@rd4com](https://github.com/rd4com), +[@toiletsandpaper](https://github.com/toiletsandpaper), +[@helehex](https://github.com/helehex), [@rd4com](https://github.com/rd4com/), +[@artemiogr97](https://github.com/artemiogr97), +[@mikowals](https://github.com/mikowals), +[@kernhanda](https://github.com/kernhanda), [@lsh](https://github.com/lsh), +[@LJ-9801](https://github.com/LJ-9801), +[@YichengDWu](https://github.com/YichengDWu), +[@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), +[@fknfilewalker](https://github.com/fknfilewalker), +[@jayzhan211](https://github.com/jayzhan211), +[@martinvuyk](https://github.com/martinvuyk), +[@ChristopherLR](https://github.com/ChristopherLR), +[@mzaks](https://github.com/mzaks), [@bgreni](https://github.com/bgreni), +[@Brian-M-J](https://github.com/Brian-M-J) + ## v24.3 (2024-05-02) ### ✨ Highlights @@ -831,7 +956,7 @@ modular update mojo This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that `__call_location()` only works in `@always_inline` or `@always_inline("nodebug")` functions. It gives incorrect results if placed in - an `@always_inline` function that's called *from* an + an `@always_inline` function that's called _from_ an `@always_inline("nodebug")` function. This feature is still evolving and for the time being you need to explicitly @@ -2267,7 +2392,7 @@ experience without dedicated sugar. specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are *not enabled* + before the program exits. By default, assertions are _not enabled_ in the standard library right now for performance reasons. - The Mojo Language Server now implements the References request. IDEs use @@ -2377,7 +2502,7 @@ experience without dedicated sugar. array slices. While this is a major step forward for the lifetimes system in Mojo, it is - still *very* early and awkward to use. Notably, there is no syntactic sugar + still _very_ early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now. @@ -2700,8 +2825,8 @@ experience without dedicated sugar. - Traits have arrived! - You can now define a *trait*, which consists of a required set of method - prototypes. A struct can *conform to* the trait by implementing these methods. + You can now define a _trait_, which consists of a required set of method + prototypes. A struct can _conform to_ the trait by implementing these methods. This lets you write generic functions that work on any structs that conform to a given trait. @@ -2821,7 +2946,7 @@ experience without dedicated sugar. ### ⭐️ New - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include *everything* about Mojo yet, but it includes a lot, + It doesn't include _everything_ about Mojo yet, but it includes a lot, and more than the original [programming manual](/mojo/programming-manual.html) (now deprecated). @@ -2844,7 +2969,7 @@ experience without dedicated sugar. ``` In the first signature for `eat()`, the `b` parameter isn't bound, so it's - *implicitly* added as an input parameter on the function. + _implicitly_ added as an input parameter on the function. In the second signature for `eat()`, the author has explicitly defined an input parameter (`_b`), which is bound to the second parameter on the argument @@ -3089,8 +3214,8 @@ the previous "read to EOF" behavior when size is negative. - There is an issue affecting Jupyter notebooks that use autotuning and traits. This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the *Matrix multiplication in - Mojo* notebook. + outside of the notebooks. This issue affects the _Matrix multiplication in + Mojo_ notebook. ## v0.5.0 (2023-11-2) @@ -3105,7 +3230,7 @@ the previous "read to EOF" behavior when size is negative. function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. -- Mojo now supports compile-time *keyword parameters*, in addition to existing +- Mojo now supports compile-time _keyword parameters_, in addition to existing support for [keyword arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For example: @@ -3444,7 +3569,7 @@ the previous "read to EOF" behavior when size is negative. return self^ ``` - Here Mojo *cannot* invoke a noop `__exit__` method because the context + Here Mojo _cannot_ invoke a noop `__exit__` method because the context manager is consumed by the `__enter__` method. This can be used for types (like file descriptors) that are traditionally used with `with` statements, even though Mojo's guaranteed early destruction doesn't require that. @@ -4711,7 +4836,7 @@ busy this week. is currently unsupported). These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to *not* capture values + types, with the caveat that closures are known to _not_ capture values correctly. Be very careful with interesting types in the vicinity of a closure! From ba015500029d37d9d3ffa76161efe63cdf171a28 Mon Sep 17 00:00:00 2001 From: KenJones-Modular <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:27:32 -0700 Subject: [PATCH 0791/2019] New documentation for the Mojo testing framework MODULAR_ORIG_COMMIT_REV_ID: a889cd8468637c72abfb76f6e30bde3741faf422 --- docs/tools/testing.ipynb | 750 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 726 insertions(+), 24 deletions(-) diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb index 5a4b78043f..2db4aad051 100644 --- a/docs/tools/testing.ipynb +++ b/docs/tools/testing.ipynb @@ -15,56 +15,758 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The Mojo CLI has a built-in test runner that can be used to test Mojo programs. \n", - "The `mojo test` command recursively searches the `test` directory for any files\n", - "with `test` in the filename. For example, given the following directory\n", - "structure:\n", + "Mojo includes a framework for developing and executing unit tests. The framework\n", + "also supports testing code examples in the\n", + "[documentation strings](/mojo/manual/basics#code-comments)\n", + "(also known as *docstrings*) of your API references. The Mojo testing framework\n", + "consists of a set of assertions defined as part of the\n", + "[Mojo standard library](/mojo/lib) and the\n", + "[`mojo test`](/mojo/cli/test) command line tool.\n", + "\n", + "## Get started\n", + "\n", + "Let’s start with a simple example of writing and running Mojo tests.\n", + "\n", + "### 1. Write tests\n", + "\n", + "For your first example of using the Mojo testing framework, create a file named\n", + "`test_quickstart.mojo` containing the following code:\n", + "\n", + "```mojo\n", + "# Content of test_quickstart.mojo\n", + "from testing import assert_equal\n", + "\n", + "def inc(n: Int) -> Int:\n", + " return n + 1\n", + "\n", + "def test_inc_zero():\n", + " # This test contains an intentional logical error to show an example of\n", + " # what a test failure looks like at runtime.\n", + " assert_equal(inc(0), 0)\n", + "\n", + "def test_inc_one():\n", + " assert_equal(inc(1), 2)\n", + "```\n", + "\n", + "In this file, the `inc()` function is the test *target*. The functions whose\n", + "names begin with `test_` are the tests. Usually the target should be in a\n", + "separate source file from its tests, but you can define them in the same file\n", + "for this simple example.\n", + "\n", + "A test function *fails* if it raises an error when executed, otherwise it\n", + "*passes*. The two tests in this example use the `assert_equal()` function,\n", + "which raises an error if the two values provided are not equal.\n", + "\n", + ":::note\n", + "\n", + "The implementation of `test_inc_zero()` contains an intentional logical error\n", + "so that you can see an example of a failed test when you execute it in the\n", + "next step of this tutorial. \n", + "\n", + ":::\n", + "\n", + "### 2. Execute tests\n", + "\n", + "Then in the directory containing the file, execute the following command in your\n", + "shell:\n", "\n", "```bash\n", - "test/\n", - " test_users_controller.mojo\n", - " factories.mojo\n", - " models/\n", - " user_model_tests.mojo\n", + "mojo test test_quickstart.mojo\n", + "```\n", + "\n", + "You should see output similar to this (note that this example elides the full\n", + "filesystem paths from the output shown):\n", + "\n", "```\n", + "Testing Time: 1.193s\n", + "\n", + "Total Discovered Tests: 2\n", + "\n", + "Passed : 1 (50.00%)\n", + "Failed : 1 (50.00%)\n", + "Skipped: 0 (0.00%)\n", + "\n", + "******************** Failure: 'ROOT_DIR/test_quickstart.mojo::test_inc_zero()' ********************\n", "\n", - "The `mojo` CLI will search `test_users_controller.mojo` and \n", - "`user_model_tests.mojo` for tests, but not `factories.mojo`. \n", + "Unhandled exception caught during execution\n", + "\n", + "Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\n", + " left: 1\n", + " right: 0\n", + "\n", + "********************\n", + "```\n", "\n", - "## Test functions\n", + "The output starts with a summary of the number of tests discovered, passed,\n", + "failed, and skipped. Following that, each failed test is reported along with\n", + "its error message.\n", "\n", - "Test functions must be named `test_*` and take no arguments. Use `def` notation\n", - "rather than `fn` notation. By definition, every test function should be capable\n", - "of raising an error. So:" + "### Next steps\n", + "\n", + "- [The `testing` module](#the-testing-module) describes the assertion\n", + "functions available to help implement tests.\n", + "- [Writing unit tests](#writing-unit-tests) shows how to write unit tests and\n", + "organize them into test files.\n", + "- [The `mojo test` command](#the-mojo-test-command) describes how to execute\n", + "and collect lists of tests.\n", + "- [Writing API documentation tests](#writing-api-documentation-tests)\n", + "discusses how to use the Mojo testing framework to test code examples in your\n", + "API documentation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## The `testing` module\n", + "\n", + "The Mojo standard library includes a [`testing`](/mojo/stdlib/testing/testing/)\n", + "module that defines several assertion functions for implementing tests. Each\n", + "assertion returns `None` if its condition is met or raises an error if it isn’t.\n", + "\n", + "- [`assert_true()`](/mojo/stdlib/testing/testing/assert_true):\n", + "Asserts that the input value is `True`.\n", + "- [`assert_false()`](/mojo/stdlib/testing/testing/assert_false):\n", + "Asserts that the input value is `False`.\n", + "- [`assert_equal()`](/mojo/stdlib/testing/testing/assert_equal):\n", + "Asserts that the input values are equal.\n", + "- [`assert_not_equal()`](/mojo/stdlib/testing/testing/assert_not_equal):\n", + "Asserts that the input values are not equal.\n", + "- [`assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal):\n", + "Asserts that the input values are equal up to a tolerance.\n", + "\n", + "The boolean assertions report a basic error message when they fail." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: At Expression [1] wrapper:14:16: AssertionError: condition was unexpectedly False\n" + ] + } + ], "source": [ - "from testing import assert_equal\n", + "from testing import *\n", + "assert_true(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each function also accepts an optional `msg` keyword argument for providing a\n", + "custom message to include if the assertion fails." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: At Expression [2] wrapper:14:16: AssertionError: paradoxes are not allowed\n" + ] + } + ], + "source": [ + "assert_true(False, msg=\"paradoxes are not allowed\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For comparing floating point values you should use `assert_almost_equal()`,\n", + "which allows you to specify either an absolute or relative tolerance." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: At Expression [3] wrapper:15:24: AssertionError: 3.3333333333333335 is not close to 3.3300000000000001 with a diff of 0.0033333333333334103 (close but no cigar)\n" + ] + } + ], + "source": [ + "result = 10 / 3\n", + "assert_almost_equal(result, 3.33, atol=0.001, msg=\"close but no cigar\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The testing module also defines a context manager,\n", + "[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises),\n", + "to assert that a given code block correctly raises an expected error." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test passes because the error is raised\n", + "Test fails because the error isn't raised\n", + "Error: AssertionError: Didn't raise at Expression [4] wrapper:18:23\n" + ] + } + ], + "source": [ + "def inc(n: Int) -> Int:\n", + " if n == Int.MAX:\n", + " raise Error(\"inc overflow\")\n", + " return n + 1\n", + "\n", + "print(\"Test passes because the error is raised\")\n", + "with assert_raises():\n", + " _ = inc(Int.MAX)\n", + "\n", + "print(\"Test fails because the error isn't raised\")\n", + "with assert_raises():\n", + " _ = inc(Int.MIN)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note\n", + "\n", + "The example above assigns the return value from `inc()` to a\n", + "[*discard pattern*](/mojo/manual/lifecycle/death#explicit-lifetimes).\n", + "Without it, the Mojo compiler detects that the return value is unused and\n", + "optimizes the code to eliminate the function call.\n", + "\n", + ":::\n", + "\n", + "You can also provide an optional `contains` argument to `assert_raises()` to\n", + "indicate that the test passes only if the error message contains the substring\n", + "specified. Other errors are propagated, failing the test." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test passes because the error contains the substring\n", + "Test fails because the error doesnt contain the substring\n", + "Error: invalid value\n" + ] + } + ], + "source": [ + "print(\"Test passes because the error contains the substring\")\n", + "with assert_raises(contains=\"required\"):\n", + " raise Error(\"missing required argument\")\n", + "\n", + "print(\"Test fails because the error doesnt contain the substring\")\n", + "with assert_raises(contains=\"required\"):\n", + " raise Error(\"invalid value\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Writing unit tests\n", + "\n", + "A Mojo unit test is simply a function that fulfills all of these requirements:\n", + "\n", + "- Has a name that starts with `test_`.\n", + "- Accepts no arguments.\n", + "- Returns either `None` or a value of type `object`.\n", + "- Raises an error to indicate test failure.\n", + "- Is defined at the module scope, not as a Mojo struct method.\n", + "\n", + "You can use either `def` or `fn` to define a test function. Because a test\n", + "function always raises an error to indicate failure, any test function defined\n", + "using `fn` must include the `raises` declaration.\n", + "\n", + "Generally, you should use the assertion utilities from the Mojo standard library\n", + "[`testing`](/mojo/stdlib/testing/testing/) module to implement your tests.\n", + "You can include multiple related assertions in the same test function. However,\n", + "if an assertion raises an error during execution then the test function returns\n", + "immediately, skipping any subsequent assertions.\n", + "\n", + "You must define your Mojo unit tests in Mojo source files named with a `test`\n", + "prefix or suffix. You can organize your test files within a directory hierarchy,\n", + "but the test files must not be part of a Mojo package (that is, the test\n", + "directories should not contain `__init__.mojo` files).\n", + "\n", + "Here is an example of a test file containing three tests for functions defined\n", + "in a source module named `my_target_module` (which is not shown here).\n", + "\n", + "```mojo\n", + "# File: test_my_target_module.mojo\n", + "\n", + "from my_target_module import convert_input, validate_input\n", + "from testing import assert_equal, assert_false, assert_raises, assert_true\n", "\n", + "def test_validate_input():\n", + "\tassert_true(validate_input(\"good\"), msg=\"'good' should be valid input\")\n", + "\tassert_false(validate_input(\"bad\"), msg=\"'bad' should be invalid input\")\n", "\n", - "def test_foo():\n", - " assert_equal(1, 1)\n" + "def test_convert_input():\n", + "\tassert_equal(convert_input(\"input1\"), \"output1\")\n", + "\tassert_equal(convert_input(\"input2\"), \"output2\")\n", + "\n", + "def test_convert_input_error():\n", + "\twith assert_raises():\n", + "\t\t_ = convert_input(\"garbage\")\n", + "```\n", + "\n", + "The unique identity of a unit test consists of the path of the test file and the\n", + "name of the test function, separated by `::`. So the test IDs from the example\n", + "above are:\n", + "\n", + "- `test_my_target_module.mojo::test_validate_input()`\n", + "- `test_my_target_module.mojo::test_convert_input()`\n", + "- `test_my_target_module.mojo::test_convert_error()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "## The `mojo test` command\n", + "\n", + "The `mojo` command line interface includes the [`mojo test`](/mojo/cli/test)\n", + "command for running tests or collecting a list of tests.\n", + "\n", + "### Running tests\n", + "\n", + "By default, the `mojo test` command runs the tests that you specify using one of\n", + "the following:\n", + "\n", + "- A single test ID with either an absolute or relative file path, to run only\n", + "that test.\n", + "- A single absolute or relative file path, to run all tests in that file.\n", + "- A single absolute or relative directory path, to recurse through that\n", + "directory hierarchy and run all tests found.\n", + "\n", + "If needed, you can optionally use the `-I` option one or more times to append\n", + "additional paths to the list of directories searched to import Mojo modules and\n", + "packages. For example, consider a project with the following directory\n", + "structure:\n", + "\n", + "```\n", + ".\n", + "├── src\n", + "│   ├── example.mojo\n", + "│   └── my_math\n", + "│   ├── __init__.mojo\n", + "│   └── utils.mojo\n", + "└── test\n", + " └── my_math\n", + " ├── test_dec.mojo\n", + " └── test_inc.mojo\n", + "```\n", + "\n", + "From the project root directory, you could execute all of the tests in the\n", + "`test` directory like this:\n", + "\n", + "```\n", + "$ mojo test -I src test\n", + "Testing Time: 3.433s\n", + "\n", + "Total Discovered Tests: 4\n", + "\n", + "Passed : 4 (100.00%)\n", + "Failed : 0 (0.00%)\n", + "Skipped: 0 (0.00%)\n", + "```\n", + "\n", + "You could run the tests contained in only the `test_dec.mojo` file like this:\n", + "\n", + "```\n", + "$ mojo test -I src test/my_math/test_dec.mojo\n", + "Testing Time: 1.175s\n", + "\n", + "Total Discovered Tests: 2\n", + "\n", + "Passed : 2 (100.00%)\n", + "Failed : 0 (0.00%)\n", + "Skipped: 0 (0.00%)\n", + "```\n", + "\n", + "And you could run a single test from a file by providing its fully qualified\n", + "ID like this:\n", + "\n", + "```\n", + "$ mojo test -I src 'test/my_math/test_dec.mojo::test_dec_valid()'\n", + "Testing Time: 0.66s\n", + "\n", + "Total Discovered Tests: 1\n", + "\n", + "Passed : 1 (100.00%)\n", + "Failed : 0 (0.00%)\n", + "Skipped: 0 (0.00%)\n", + "```\n", + "\n", + "### Collecting a list of tests\n", + "\n", + "By including the `--collect-only` or `--co` option, you can use `mojo test` to\n", + "discover and print a list of tests. \n", + "\n", + "As an example, consider the project structure shown in the\n", + "[Running tests](#running-tests) section. The following command produces a list\n", + "of all of the tests defined in the `test` directory hierarchy.\n", + "\n", + "```bash\n", + "mojo test --co test\n", + "```\n", + "\n", + "The output shows the hierarchy of directories, test files, and individual tests\n", + "(note that this example elides the full filesystem paths from the output shown):\n", + "\n", + "```\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "```\n", + "\n", + "### Producing JSON formatted output\n", + "\n", + "By default `mojo test` produces concise, human-readable output. Alternatively\n", + "you can produce JSON formatted output more suitable for input to other tools by\n", + "including the `--diagnostic-format json` option.\n", + "\n", + "For example, you could run the tests in the `test_quickstart.mojo` file shown\n", + "in the [Get started](#get-started) section with JSON formatted output using this\n", + "command:\n", + "\n", + "```bash\n", + "mojo test --diagnostic-format json test_quickstart.mojo\n", + "```\n", + "\n", + "The output shows the detailed results for each individual test and summary\n", + "results (note that this example elides the full filesystem paths from the\n", + "output shown):\n", + "\n", + "```\n", + "{\n", + " \"children\": [\n", + " {\n", + " \"duration_ms\": 60,\n", + " \"error\": \"Unhandled exception caught during execution\",\n", + " \"kind\": \"executionError\",\n", + " \"stdErr\": \"\",\n", + " \"stdOut\": \"Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\\r\\n left: 1\\r\\n right: 0\\r\\n\",\n", + " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_zero()\"\n", + " },\n", + " {\n", + " \"duration_ms\": 51,\n", + " \"error\": \"\",\n", + " \"kind\": \"success\",\n", + " \"stdErr\": \"\",\n", + " \"stdOut\": \"\",\n", + " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_one()\"\n", + " }\n", + " ],\n", + " \"duration_ms\": 1171,\n", + " \"error\": \"\",\n", + " \"kind\": \"executionError\",\n", + " \"stdErr\": \"\",\n", + " \"stdOut\": \"\",\n", + " \"testID\": \"ROOT_DIR/test_quickstart.mojo\"\n", + "}\n", + "```\n", + "\n", + "You can also produce JSON output for test collection as well. As an example,\n", + "consider the project structure shown in the [Running tests](#running-tests)\n", + "section. The following command collects a list in JSON format of all of the\n", + "tests defined in the `test` directory hierarchy:\n", + "\n", + "```bash\n", + "mojo test --diagnostic-format json --co test\n", + "```\n", "\n", - "Not:\n", + "The output would appear as follows (note that this example elides the full\n", + "filesystem paths from the output shown):\n", + "\n", + "```\n", + "{\n", + " \"children\": [\n", + " {\n", + " \"children\": [\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_valid()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 5,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 5\n", + " }\n", + " },\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_min()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 9,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 9\n", + " }\n", + " }\n", + " ],\n", + " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo\"\n", + " },\n", + " {\n", + " \"children\": [\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_valid()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 5,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 5\n", + " }\n", + " },\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_max()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 9,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 9\n", + " }\n", + " }\n", + " ],\n", + " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo\"\n", + " }\n", + " ],\n", + " \"id\": \"ROOT_DIR/test/my_math\"\n", + "}\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Writing API documentation tests\n", + "\n", + "The Mojo testing framework also supports testing code examples that you include\n", + "in [docstrings](/mojo/manual/basics#code-comments). This helps to ensure that\n", + "the code examples in your API documentation are correct and up to date.\n", + "\n", + "### Identifying executable code\n", + "\n", + "The Mojo testing framework requires you to explicitly identify the code blocks\n", + "that you want it to execute.\n", + "\n", + "In a Mojo docstring, a fenced code block delimited by standard triple-backquotes\n", + "is a *display-only* code block. It appears in the API documentation, but\n", + "`mojo test` does not identify it as a test or attempt to execute any of the code\n", + "in the block.\n", + "\n", + "~~~\n", + "\"\"\" Non-executable code block example.\n", + "\n", + "The generated API documentation includes all lines of the following code block,\n", + "but `mojo test` does not execute any of the code in it.\n", + "\n", + "```\n", + "# mojo test does NOT execute any of this code block\n", + "a = 1\n", + "print(a)\n", + "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + "In contrast, a fenced code block that starts with the line ```mojo\n", + "not only appears in the API documentation, but `mojo test` treats it as an\n", + "executable test. The test fails if the code raises any error, otherwise it\n", + "passes.\n", + "\n", + "~~~\n", + "\"\"\" Executable code block example.\n", + "\n", + "The generated API documentation includes all lines of the following code block\n", + "*and* `mojo test` executes it as a test.\n", + "\n", + "```mojo\n", + "from testing import assert_equals\n", + "\n", + "b = 2\n", + "assert_equals(b, 2)\n", + "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + "Sometimes you might want to execute a line of code as part of the test but *not*\n", + "display that line in the API documentation. To achieve this, prefix the line of\n", + "code with `%#`. For example, you could use this technique to omit `import`\n", + "statements and assertion functions from the documentation.\n", + "\n", + "~~~\n", + "\"\"\" Executable code block example with some code lines omitted from output.\n", + "\n", + "The generated API documentation includes only the lines of code that do *not*\n", + "start with `%#`. However, `mojo test` executes *all* lines of code.\n", "\n", "```mojo\n", - "fn test_foo() raises:\n", - " assert_equal(1, 1)\n", + "%# from testing import assert_equal\n", + "c = 3\n", + "print(c)\n", + "%# assert_equal(c, 3)\n", "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + "### Documentation test suites and scoping\n", + "\n", + "The Mojo testing framework treats each docstring as a separate *test suite*.\n", + "In other words, a single test suite could correspond to the docstring for an\n", + "individual package, module, function, struct, struct method, etc.\n", + "\n", + "Each executable code block within a given docstring is a single test of the same\n", + "test suite. The `mojo test` command executes the tests of a test suite\n", + "sequentially in the order that they appear within the docstring. If a test\n", + "within a particular test suite fails, then all subsequent tests within the same\n", + "test suite are skipped.\n", + "\n", + "All tests within the test suite execute in the same scope, and test execution\n", + "within that scope is stateful. This means, for example, that a variable created\n", + "within one test is then accessible to subsequent tests in the same test suite.\n", + "\n", + "~~~\n", + "\"\"\" Stateful example.\n", + "\n", + "Assign 1 to the variable `a`:\n", + "\n", + "```mojo\n", + "%# from testing import assert_equal\n", + "a = 1\n", + "%# assert_equal(a, 1)\n", + "```\n", + "\n", + "Then increment the value of `a` by 1:\n", + "\n", + "```mojo\n", + "a += 1\n", + "%# assert_equal(a, 2)\n", + "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + ":::note\n", + "\n", + "Test suite scopes do *not* nest. In other words, the test suite scope of a\n", + "module is completely independent of the test suite scope of a function or struct\n", + "defined within that module. For example, this means that if a module’s test\n", + "suite creates a variable, that variable is *not* accessible to a function’s test\n", + "suite within the same module.\n", + "\n", + ":::\n", + "\n", + "### Documentation test identifiers\n", + "\n", + "The format of a documentation test identifier is `@::`.\n", + "This is best explained by an example. Consider the project structure shown in\n", + "the [Running tests](#running-tests) section. The source files in the `src`\n", + "directory might contain docstrings for the `my_math` package, the `utils.mojo`\n", + "module, and the individual functions within that module. You could collect the\n", + "full list of tests by executing:\n", + "\n", + "```\n", + "mojo test --co src\n", + "```\n", + "\n", + "The output shows the hierarchy of directories, test files, and individual tests\n", + "(note that this example elides the full filesystem paths from the output shown):\n", + "\n", + "```\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "```\n", + "\n", + "Several different test suites appear in this result:\n", + "\n", + "| Test suite scope | File | Test suite name |\n", + "|------------------|------|-----------------|\n", + "| Package | `src/my_math/__init__.mojo` | `__doc__` |\n", + "| Module | `src/my_math/utils.mojo` | `__doc__` |\n", + "| Function | `src/my_math/utils.mojo` | `inc(stdlib\\3A\\3Abuiltin\\3A\\3Aint\\3A\\3AInt).__doc__` |\n", "\n", - "The [testing module](/mojo/stdlib/testing/testing) defines a set of test-related\n", - "utilities. " + "Then within a specific test suite, tests are numbered sequentially in the order\n", + "they appear in the docstring, starting with 0." ] } ], From 9365a00ecd63ae17ac4a705dfc0e64108a9c10c0 Mon Sep 17 00:00:00 2001 From: KenJones-Modular <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:36:14 -0700 Subject: [PATCH 0792/2019] New Mojo control flow page MODULAR_ORIG_COMMIT_REV_ID: 73247a4d84f315ebabeb84ec233d3d1e7fe2d983 --- docs/manual/control-flow.ipynb | 887 +++++++++++++++++++++++++++++++++ docs/manual/index.md | 1 + 2 files changed, 888 insertions(+) create mode 100644 docs/manual/control-flow.ipynb diff --git a/docs/manual/control-flow.ipynb b/docs/manual/control-flow.ipynb new file mode 100644 index 0000000000..8ee4dab294 --- /dev/null +++ b/docs/manual/control-flow.ipynb @@ -0,0 +1,887 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Control flow\n", + "description: Mojo control flow statements.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mojo includes several traditional control flow structures for conditional and\n", + "repeated execution of code blocks.\n", + "\n", + "## The `if` statement\n", + "\n", + "Mojo supports the `if` statement for conditional code execution. With it you can\n", + "conditionally execute an indented code block if a given\n", + "[boolean](/mojo/manual/types#booleans) expression evaluates to `True`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "It is warm.\n", + "The temperature is 77.0 Fahrenheit.\n" + ] + } + ], + "source": [ + "temp_celsius = 25\n", + "if temp_celsius > 20:\n", + " print(\"It is warm.\")\n", + " print(\"The temperature is\", temp_celsius * 9 / 5 + 32, \"Fahrenheit.\" )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can write the entire `if` statement as a single line if all you need to\n", + "execute conditionally is a single, short statement." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "It is warm.\n" + ] + } + ], + "source": [ + "temp_celsius = 22\n", + "if temp_celsius < 15: print(\"It is cool.\") # Skipped because condition is False\n", + "if temp_celsius > 20: print(\"It is warm.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Optionally, an `if` statement can include any number of additional `elif`\n", + "clauses, each specifying a boolean condition and associated code block to\n", + "execute if `True`. The conditions are tested in the order given. When a\n", + "condition evaluates to `True`, the associated code block is executed and no\n", + "further conditions are tested.\n", + "\n", + "Additionally, an `if` statement can include an optional `else` clause providing\n", + "a code block to execute if all conditions evaluate to `False`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "It is warm.\n" + ] + } + ], + "source": [ + "temp_celsius = 25\n", + "if temp_celsius <= 0:\n", + " print(\"It is freezing.\")\n", + "elif temp_celsius < 20:\n", + " print(\"It is cool.\")\n", + "elif temp_celsius < 30:\n", + " print(\"It is warm.\")\n", + "else:\n", + " print(\"It is hot.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note TODO\n", + "\n", + "Mojo currently does not support the equivalent of a Python `match` or C `switch`\n", + "statement for pattern matching and conditional execution.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Short-circuit evaluation\n", + "\n", + "Mojo follows [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation)\n", + "semantics for boolean operators. If the first argument to an `or` operator\n", + "evaluates to `True`, the second argument is not evaluated.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Short-circuit \"or\" evaluation\n", + "Executing true_func\n", + "True result\n" + ] + } + ], + "source": [ + "def true_func() -> Bool:\n", + " print(\"Executing true_func\")\n", + " return True\n", + "\n", + "def false_func() -> Bool:\n", + " print(\"Executing false_func\")\n", + " return False\n", + "\n", + "print('Short-circuit \"or\" evaluation')\n", + "if true_func() or false_func():\n", + " print(\"True result\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the first argument to an `and` operator evaluates to `False`, the second\n", + "argument is not evaluated." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Short-circuit \"and\" evaluation\n", + "Executing false_func\n" + ] + } + ], + "source": [ + "print('Short-circuit \"and\" evaluation')\n", + "if false_func() and true_func():\n", + " print(\"True result\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conditional expressions\n", + "\n", + "Mojo also supports conditional expressions (or what is sometimes called a\n", + "[_ternary conditional operator_](https://en.wikipedia.org/wiki/Ternary_conditional_operator))\n", + "using the syntaxtrue_result if boolean_expression else false_result, just as\n", + "in Python. This is most often used as a concise way to assign one of two\n", + "different values to a variable, based on a boolean condition." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The forecast for today is cool\n" + ] + } + ], + "source": [ + "temp_celsius = 15\n", + "forecast = \"warm\" if temp_celsius > 20 else \"cool\"\n", + "print(\"The forecast for today is\", forecast)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The alternative, written as a multi-line `if` statement, is more verbose." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The forecast for today is cool\n" + ] + } + ], + "source": [ + "if temp_celsius > 20:\n", + " forecast = \"warm\"\n", + "else:\n", + " forecast = \"cool\"\n", + "print(\"The forecast for today is\", forecast)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `while` statement\n", + "\n", + "The `while` loop repeatedly executes a code block while a given boolean\n", + "expression evaluates to `True`. For example, the following loop prints values\n", + "from the Fibonacci series that are less than 50." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 1, 2, 3, 5, 8, 13, 21, 34" + ] + } + ], + "source": [ + "fib_prev = 0\n", + "fib_curr = 1\n", + "\n", + "print(fib_prev, end=\"\")\n", + "while fib_curr < 50:\n", + " print(\",\", fib_curr, end=\"\")\n", + " fib_prev, fib_curr = fib_curr, fib_prev + fib_curr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `continue` statement skips execution of the rest of the code block and\n", + "resumes with the loop test expression." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, 4, 5, " + ] + } + ], + "source": [ + "n = 0\n", + "while n < 5:\n", + " n += 1\n", + " if n == 3:\n", + " continue\n", + " print(n, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `break` statement terminates execution of the loop." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, " + ] + } + ], + "source": [ + "n = 0\n", + "while n < 5:\n", + " n += 1\n", + " if n == 3:\n", + " break\n", + " print(n, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Optionally, a `while` loop can include an `else` clause. The body of the `else`\n", + "clause executes when the loop's boolean condition evaluates to `False`, even if\n", + "it occurs the first time tested." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loop completed\n" + ] + } + ], + "source": [ + "n = 5\n", + "\n", + "while n < 4:\n", + " print(n)\n", + " n += 1\n", + "else:\n", + " print(\"Loop completed\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note\n", + "\n", + "The `else` clause does _not_ execute if a `break` or `return` statement\n", + "exits the `while` loop.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n" + ] + } + ], + "source": [ + "n = 0\n", + "while n < 5:\n", + " n += 1\n", + " if n == 3:\n", + " break\n", + " print(n)\n", + "else:\n", + " print(\"Executing else clause\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `for` statement\n", + "\n", + "The `for` loop iterates over a sequence, executing a code block for each\n", + "element in the sequence.\n", + "The Mojo `for` loop can iterate over any type that implements an `__iter__()`\n", + "method that returns a type that defines `__next__()` and `__len__()` methods.\n", + "\n", + "### Iterating over Mojo collections\n", + "\n", + "All of the collection types in the [`collections`](/mojo/stdlib/collections)\n", + "module support `for` loop iteration. See the\n", + "[Collection types](/mojo/manual/types#collection-types) documentation for more\n", + "information on Mojo collection types.\n", + "\n", + ":::caution TODO\n", + "\n", + "Iterating over Mojo native collections currently assigns the loop index variable\n", + "a [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", + "item itself. You can access the item using the dereference operator, `[]`, as\n", + "shown in the examples below. This may change in a future version of Mojo.\n", + "\n", + ":::\n", + "\n", + "The following shows an example of iterating over a Mojo\n", + "[`List`](/mojo/stdlib/collections/list/List)." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "California\n", + "Hawaii\n", + "Oregon\n" + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "states = List[String](\"California\", \"Hawaii\", \"Oregon\")\n", + "for state in states:\n", + " print(state[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The same technique works for iterating over a Mojo\n", + "[`Set`](/mojo/stdlib/collections/set/Set)." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42\n", + "0\n" + ] + } + ], + "source": [ + "from collections import Set\n", + "\n", + "values = Set[Int](42, 0)\n", + "for item in values:\n", + " print(item[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two techniques for iterating over a Mojo\n", + "[`Dict`](/mojo/stdlib/collections/dict/Dict). The first is to iterate directly\n", + "using the `Dict`, which produces a sequence of the dictionary's keys." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sacramento, California\n", + "Honolulu, Hawaii\n", + "Salem, Oregon\n" + ] + } + ], + "source": [ + "from collections import Dict\n", + "\n", + "capitals = Dict[String, String]()\n", + "capitals[\"California\"] = \"Sacramento\"\n", + "capitals[\"Hawaii\"] = \"Honolulu\"\n", + "capitals[\"Oregon\"] = \"Salem\"\n", + "\n", + "for state in capitals:\n", + " print(capitals[state[]] + \", \" + state[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second approach to iterating over a Mojo `Dict` is to invoke its\n", + "[`items()`](/mojo/stdlib/collections/dict/Dict#items) method, which produces a\n", + "sequence of [`DictEntry`](/mojo/stdlib/collections/dict/DictEntry) objects.\n", + "Within the loop body, you can then access the `key` and `value` fields of the\n", + "entry." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sacramento, California\n", + "Honolulu, Hawaii\n", + "Salem, Oregon\n" + ] + } + ], + "source": [ + "for item in capitals.items():\n", + " print(item[].value + \", \" + item[].key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another type of iterable provided by the Mojo standard library is a _range_,\n", + "which is a sequence of integers generated by the\n", + "[`range()`](/mojo/stdlib/builtin/range/range) function. It differs from the\n", + "collection types shown above in that it's implemented as a\n", + "[generator](https://en.wikipedia.org/wiki/Generator_(computer_programming)),\n", + "producing each value as needed rather than materializing the entire sequence\n", + "in memory. Additionally, each value assigned to the loop index variable is\n", + "simply the `Int` value rather than a `Reference` to the value, so you should\n", + "not use the dereference operator on it within the loop. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, 3, 4, " + ] + } + ], + "source": [ + "for i in range(5):\n", + " print(i, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `for` loop control statements\n", + "\n", + "A `continue` statement skips execution of the rest of the code block and\n", + "resumes the loop with the next element of the collection." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, 4, " + ] + } + ], + "source": [ + "for i in range(5):\n", + " if i == 3:\n", + " continue\n", + " print(i, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `break` statement terminates execution of the loop." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, " + ] + } + ], + "source": [ + "for i in range(5):\n", + " if i == 3:\n", + " break\n", + " print(i, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Optionally, a `for` loop can include an `else` clause. The body of the `else`\n", + "clause executes after iterating over all of the elements in a collection." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, 3, 4, \n", + "Finished executing 'for' loop\n" + ] + } + ], + "source": [ + "for i in range(5):\n", + " print(i, end=\", \")\n", + "else:\n", + " print(\"\\nFinished executing 'for' loop\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `else` clause executes even if the collection is empty." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finished executing 'for' loop\n" + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "empty = List[Int]()\n", + "for i in empty:\n", + " print(i[])\n", + "else:\n", + " print(\"Finished executing 'for' loop\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note\n", + "\n", + "The `else` clause does _not_ execute if a `break` or `return` statement\n", + "terminates the `for` loop.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found a dog\n" + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "animals = List[String](\"cat\", \"aardvark\", \"hippopotamus\", \"dog\")\n", + "for animal in animals:\n", + " if animal[] == \"dog\":\n", + " print(\"Found a dog\")\n", + " break\n", + "else:\n", + " print(\"No dog found\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating over Python collections\n", + "\n", + "The Mojo `for` loop supports iterating over Python collection types. Each item\n", + "retrieved by the loop is a\n", + "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", + "the Python object. Refer to the [Python types](/mojo/manual/python/types)\n", + "documentation for more information on manipulating Python objects from Mojo.\n", + "\n", + "The following is a simple example of iterating over a mixed-type Python list." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42\n", + "cat\n", + "3.14159\n" + ] + } + ], + "source": [ + "from python import Python\n", + "\n", + "# Create a mixed-type Python list\n", + "py_list = Python.evaluate(\"[42, 'cat', 3.14159]\")\n", + "for py_obj in py_list: # Each element is of type \"PythonObject\"\n", + " print(py_obj)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note TODO\n", + "\n", + "Iterating over a Mojo collection currently assigns the loop index variable a\n", + "`Reference` to each element, which then requires you to use the dereference\n", + "operator within the loop body. In contrast, iterating over a Python collection\n", + "assigns a `PythonObject` wrapper for the element, which does _not_ require you\n", + "to use the dereference operator.\n", + "\n", + ":::\n", + "\n", + "\n", + "There are two techniques for iterating over a Python dictionary. The first is to\n", + "iterate directly using the dictionary, which produces a sequence of its keys." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a 1\n", + "b 2.71828\n", + "c sushi\n" + ] + } + ], + "source": [ + "from python import Python\n", + "\n", + "# Create a mixed-type Python dictionary\n", + "py_dict = Python.evaluate(\"{'a': 1, 'b': 2.71828, 'c': 'sushi'}\")\n", + "for py_key in py_dict: # Each element is of type \"PythonObject\"\n", + " print(py_key, py_dict[py_key])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second approach to iterating over a Python dictionary is to invoke its\n", + "`items()` method, which produces a sequence of 2-tuple objects.\n", + "Within the loop body, you can then access the key and value by index." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a 1\n", + "b 2.71828\n", + "c sushi\n" + ] + } + ], + "source": [ + "for py_tuple in py_dict.items(): # Each element is of type \"PythonObject\"\n", + " print(py_tuple[0], py_tuple[1])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/manual/index.md b/docs/manual/index.md index 4d28a625c4..f0390218c4 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -33,6 +33,7 @@ feedback](/mojo/community). - [Functions](/mojo/manual/functions) - [Variables](/mojo/manual/variables) - [Types](/mojo/manual/types) + - [Control flow](/mojo/manual/control-flow) - [Structs](/mojo/manual/structs) - [Modules and packages](/mojo/manual/packages) From 8bd1dbdf26c70c634768bfd4c014537f6fdb0fb2 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 6 Jun 2024 17:38:30 -0700 Subject: [PATCH 0793/2019] Update the changelog release date. MODULAR_ORIG_COMMIT_REV_ID: 6a27aed03a27a3bbf7a7745ccaf1b39728589097 --- docs/changelog-released.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 3c0f4189a3..55660efbf8 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,7 +25,7 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` -## v24.4 (2024-06-06) +## v24.4 (2024-06-07) ### ✨ Highlights From 17662e30fd306f3d307bd93eb1614a6dea36892c Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 28 Aug 2024 10:58:40 -0700 Subject: [PATCH 0794/2019] [docs] Updates for Mojo README (#3425) Updates to fix links and clarify the license Signed-off-by: Jack Clayton --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ebeb9c8c7e..d4058593c0 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ or the standalone Mojo SDK: - [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started/hello-world). +program](https://docs.modular.com/mojo/manual/get-started#2-run-code-in-the-repl). ### Latest Nightly @@ -57,7 +57,7 @@ view of how the development of Mojo is progressing. Use at your own risk and be patient! To get nightly builds, see the same instructions to [install the Mojo -SDK](https://docs.modular.com/mojo/manual/get-started/#install-mojo), but use +SDK](https://docs.modular.com/mojo/manual/get-started/#1-install-mojo), but use the command shown there to install `nightly/mojo`. When you clone this repo, be sure you switch to the `nightly` branch, because @@ -88,8 +88,10 @@ For more general questions or to chat with other Mojo developers, check out our ## License -This repository is licensed under the Apache License v2.0 with LLVM Exceptions -(see the LLVM [License](https://llvm.org/LICENSE.txt)). +This repository and its contributions are licensed under the Apache License v2.0 +with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). +MAX and Mojo usage and distribution are licensed under the +[MAX & Mojo Community License](https://www.modular.com/legal/max-mojo-license). ## Thanks to our contributors From 827768e1ddb92cbfd29ee48c951900cd1d47ff5f Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 6 Jun 2024 11:01:58 -0500 Subject: [PATCH 0795/2019] / MODULAR_ORIG_COMMIT_REV_ID: 014d3fdf96ffef8916d396d34eb508140bcca766 --- README.md | 10 +- stdlib/benchmarks/lit.cfg.py | 35 ++++ stdlib/benchmarks/utils/bench_memmem.mojo | 224 ++++++++++++++++++++++ 3 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 stdlib/benchmarks/lit.cfg.py create mode 100644 stdlib/benchmarks/utils/bench_memmem.mojo diff --git a/README.md b/README.md index d4058593c0..ebeb9c8c7e 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ or the standalone Mojo SDK: - [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started#2-run-code-in-the-repl). +program](https://docs.modular.com/mojo/manual/get-started/hello-world). ### Latest Nightly @@ -57,7 +57,7 @@ view of how the development of Mojo is progressing. Use at your own risk and be patient! To get nightly builds, see the same instructions to [install the Mojo -SDK](https://docs.modular.com/mojo/manual/get-started/#1-install-mojo), but use +SDK](https://docs.modular.com/mojo/manual/get-started/#install-mojo), but use the command shown there to install `nightly/mojo`. When you clone this repo, be sure you switch to the `nightly` branch, because @@ -88,10 +88,8 @@ For more general questions or to chat with other Mojo developers, check out our ## License -This repository and its contributions are licensed under the Apache License v2.0 -with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). -MAX and Mojo usage and distribution are licensed under the -[MAX & Mojo Community License](https://www.modular.com/legal/max-mojo-license). +This repository is licensed under the Apache License v2.0 with LLVM Exceptions +(see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Thanks to our contributors diff --git a/stdlib/benchmarks/lit.cfg.py b/stdlib/benchmarks/lit.cfg.py new file mode 100644 index 0000000000..8282c244b6 --- /dev/null +++ b/stdlib/benchmarks/lit.cfg.py @@ -0,0 +1,35 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +import os +from pathlib import Path + +import lit.formats +import lit.llvm + +config.test_format = lit.formats.ShTest(True) + +# name: The name of this test suite. +config.name = "Mojo Standard Library Benchmarks" + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [".mojo"] + +# test_source_root: The root path where tests are located. +config.test_source_root = Path(__file__).parent.resolve() + +# test_exec_root: The root path where tests should be run. +config.test_exec_root = os.path.join( + config.modular_obj_root, "open-source", "mojo", "stdlib", "benchmarks" +) +config.substitutions.insert(0, ("%mojo", "mojo")) diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo new file mode 100644 index 0000000000..2945bb07ab --- /dev/null +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -0,0 +1,224 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo %s + +from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from utils.stringref import _memmem, _memchr, _align_down + +from bit import countr_zero +from builtin.dtype import _uint_type_of_width + +# ===----------------------------------------------------------------------===# +# Benchmark Data +# ===----------------------------------------------------------------------===# +var haystack = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sed dictum est, et finibus ipsum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam tincidunt vel lacus vitae pulvinar. Donec ac ligula elementum, mollis purus a, lacinia quam. Maecenas vulputate mauris quis sem euismod sollicitudin. Proin accumsan nulla vel nisl congue varius. Morbi a erat dui. Aliquam maximus interdum orci, vitae pretium lorem bibendum non. Vestibulum eu lacus ullamcorper, egestas dui vel, pharetra ipsum. Pellentesque sagittis, urna a tincidunt sodales, leo sem placerat eros, vitae molestie felis diam at dolor. + +Donec viverra sem sit amet facilisis laoreet. Morbi semper convallis nisi, vitae congue velit tincidunt vel. Fusce ultrices, libero vel venenatis placerat, justo tellus porttitor massa, at volutpat tortor nunc id dui. Morbi eu ex quis odio porttitor ultricies vel eget massa. Aenean quis luctus nulla. Fusce sit amet leo at quam hendrerit mattis. Morbi sed quam nisl. Quisque purus enim, iaculis sed laoreet vel, pellentesque ut orci. Vivamus risus orci, varius eu pharetra quis, tincidunt non enim. Suspendisse bibendum lacus ex, quis blandit lectus malesuada a. Maecenas iaculis porta lacus, sit amet tristique ante scelerisque non. Proin auctor elit in lacus dictum egestas. Pellentesque tincidunt justo sed vehicula blandit. Pellentesque vehicula facilisis tellus in viverra. + +Curabitur vel fermentum risus. Etiam ornare dolor in eros faucibus, sit amet sagittis orci blandit. Curabitur pulvinar pretium fermentum. Duis sit amet placerat ipsum. Sed dui lorem, gravida quis lacinia vel, maximus ut augue. Aenean a mauris ornare, fermentum orci vel, euismod dolor. Nullam et libero eget mi pellentesque congue. Donec venenatis sapien sit amet sem fringilla sagittis. Pellentesque in placerat mi. Curabitur congue fermentum rhoncus. Duis blandit mauris nec diam bibendum faucibus. Praesent consequat, purus sed viverra interdum, ante enim scelerisque augue, nec pharetra tellus sem nec eros. Etiam quis est tellus. + +Donec lorem eros, hendrerit vel aliquet a, venenatis et lacus. Nulla quis nibh egestas dolor convallis cursus sit amet eu quam. Pellentesque sit amet pharetra nibh. Vestibulum aliquam tempor ex, nec tincidunt felis blandit tincidunt. Sed eu purus ac neque sagittis iaculis. In et sagittis erat, id gravida diam. Etiam pharetra enim tortor, id mollis odio faucibus quis. Etiam bibendum est pharetra neque convallis sollicitudin. Praesent consectetur lobortis nibh, vel interdum augue mattis eu. In tortor augue, venenatis vel semper vel, faucibus a est. Mauris efficitur aliquet sodales. Donec bibendum tempor elit, non pellentesque ligula pharetra vel. Praesent est sapien, sagittis ac lectus at, aliquam dictum neque. Ut efficitur commodo sapien et luctus. Nulla tincidunt justo nec vestibulum finibus. Maecenas eget aliquam ante, eu porta nisl. + +Aliquam ac massa mi. Mauris gravida, nisl ac volutpat egestas, leo massa tincidunt libero, eu aliquet ante mi sed justo. Quisque mattis convallis mauris, a laoreet magna aliquet at. Maecenas vel libero vehicula, interdum mauris consequat, imperdiet enim. Cras et nisi nec erat varius condimentum. Morbi mollis metus eu condimentum aliquam. Proin commodo elit a diam interdum, sit amet auctor purus malesuada. Pellentesque laoreet ante et ex sodales, eu euismod enim venenatis. Vivamus lobortis eu velit vel tincidunt. Quisque elementum dapibus odio, ultrices vulputate massa finibus ac. Morbi at condimentum orci, non efficitur tellus. Aenean id quam dignissim, dapibus justo finibus, finibus lorem. Morbi eu tortor eget elit faucibus fermentum eget a eros. Interdum et malesuada fames ac ante ipsum primis in faucibus. Morbi maximus venenatis massa, ut auctor ex ullamcorper nec. + +Suspendisse malesuada nisi augue, eget tincidunt nibh sagittis nec. Aliquam semper, sem id rhoncus varius, leo eros sodales dolor, sollicitudin vestibulum nisl sem in orci. Suspendisse quis neque rhoncus, placerat nisl id, vulputate nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent facilisis venenatis ante sit amet dignissim. Sed tellus nisi, sagittis nec ullamcorper vitae, placerat condimentum urna. Curabitur eget iaculis purus. Donec in congue odio, eu vestibulum massa. Sed maximus vulputate augue, nec pulvinar sem elementum sed. Aliquam dignissim risus tortor, at semper libero rhoncus sed. Aliquam eget lorem pellentesque, vehicula velit ut, vehicula lectus. + +Sed arcu nunc, aliquam vel finibus vitae, mattis laoreet ante. Aenean sit amet nunc vehicula, faucibus tortor id, scelerisque dui. Morbi convallis elit sed leo fringilla, quis bibendum purus sollicitudin. Nulla vitae tincidunt tellus. Vivamus velit metus, maximus et lorem eget, elementum bibendum sem. Suspendisse eleifend pulvinar hendrerit. Maecenas tempus non urna aliquam lacinia. Vivamus sodales varius nibh eu maximus. Ut quis gravida ligula. Sed at molestie dui. Maecenas porttitor, quam et auctor lacinia, nisl enim dapibus sem, eget posuere orci odio sit amet velit. Nunc nisl turpis, ultrices ut pulvinar vel, iaculis nec erat. Vivamus bibendum, lorem id bibendum aliquam, erat augue egestas erat, a tristique massa tellus in magna. Curabitur porttitor pharetra dolor non maximus. Etiam eu felis porta, congue enim non, facilisis nisi. + +Ut quam libero, consequat eget viverra nec, tristique vitae purus. Duis in dapibus ligula, at volutpat nibh. Maecenas varius diam et nunc cursus lacinia. Sed quis aliquet ligula. In viverra eros non leo accumsan accumsan. In pharetra, purus quis dictum ullamcorper, felis augue porta augue, id gravida ligula sem quis risus. Ut tempor efficitur ante non accumsan. Sed mattis quam at ante mattis, in ultricies mi venenatis. Donec eget lorem ac enim tincidunt pulvinar vel quis lacus. Quisque consequat suscipit mauris. Ut commodo id orci at consequat. + +Vestibulum sem erat, fermentum et nibh non, imperdiet viverra mi. Donec lectus ipsum, laoreet at erat in, molestie rutrum orci. Mauris pretium neque ac tempus ullamcorper. Aliquam in massa interdum, aliquam turpis non, scelerisque erat. Curabitur commodo ligula ultricies justo ullamcorper, et sodales augue euismod. Sed a vehicula sem. Donec malesuada lorem ante, at dapibus magna tincidunt sit amet. Duis vel est quis libero semper mattis. Nam molestie tellus nec pharetra tristique. + +Morbi egestas elit sapien, eget auctor libero sollicitudin ut. Aenean id dui tempor, pretium sapien id, fermentum orci. Aenean semper dolor orci, vel ultrices nisl lobortis vel. Duis at sem quis arcu vulputate auctor. Nulla quis pharetra elit, quis feugiat magna. Integer scelerisque tortor eros, vel convallis est congue vel. Vivamus id feugiat massa, non volutpat nulla. Maecenas sit amet facilisis lorem. + +Nullam eu vulputate libero. Aenean rhoncus quis erat commodo commodo. Aenean scelerisque tortor quis nunc maximus, pulvinar cursus quam consequat. Quisque et maximus nisl. Nunc convallis eu purus vitae tempus. Aenean pulvinar feugiat libero, vel convallis diam blandit in. Vestibulum faucibus urna nibh, vel ornare nisl commodo at. Nullam interdum in sem non facilisis. Donec volutpat mi lectus, a malesuada enim eleifend eu. Proin tincidunt consequat diam, sed pharetra turpis placerat sed. Duis id est in ligula fermentum mattis. + +Donec leo leo, finibus id imperdiet et, porta eget orci. Fusce malesuada turpis ac est egestas, vel euismod lectus accumsan. Phasellus a mauris hendrerit, ullamcorper diam nec, cursus nisi. Duis maximus, justo sit amet interdum faucibus, nisi lectus eleifend risus, commodo varius libero lorem vitae nibh. Suspendisse potenti. Nunc ornare nisi quis diam imperdiet rhoncus. Suspendisse suscipit nibh eget augue vestibulum, eget lacinia erat suscipit. Quisque sed enim feugiat diam accumsan dictum nec euismod elit. Duis leo arcu, placerat eget facilisis et, molestie at metus. Cras id tellus maximus, pharetra massa et, commodo ligula. Nam ac dui interdum, blandit tortor in, ullamcorper orci. Praesent rhoncus orci nisl, sed volutpat risus fringilla sit amet. Morbi a diam tortor. + +Integer sed arcu pellentesque, euismod arcu a, ullamcorper diam. Praesent feugiat diam auctor risus egestas scelerisque. Quisque lacinia bibendum sem, id pretium justo convallis quis. Quisque vel massa vulputate, lobortis ligula ut, consequat nunc. Sed eget nisl eget lacus luctus egestas. In ornare pellentesque quam, ut sodales erat placerat eget. Nullam dignissim facilisis augue, vitae imperdiet tellus maximus eget. Duis et sodales leo. Vivamus vel urna at odio elementum bibendum sed eu ex. Donec imperdiet gravida mollis. + +Aenean rutrum faucibus purus quis porttitor. Fusce scelerisque ullamcorper laoreet. Donec porttitor pulvinar dolor eget facilisis. Nunc libero urna, molestie sit amet dui in, posuere tempus massa. Nulla porta sed ligula vel congue. Praesent sed luctus tellus. Donec sagittis vel leo in commodo. Donec hendrerit risus vel condimentum ullamcorper. + +Curabitur vel venenatis dui. Proin ante ante, condimentum sit amet quam non, aliquam porttitor metus. Donec cursus dignissim mollis. Morbi dictum porta libero, eu finibus nulla commodo et. Cras orci enim, eleifend id lacus ac, pharetra faucibus turpis. Duis ultrices luctus mi. Quisque metus erat, finibus ac fringilla et, ullamcorper et quam. Sed at leo sed mauris auctor maximus in faucibus nunc. Aenean nisi felis, commodo non feugiat at, varius sit amet justo. Vestibulum lobortis nisi arcu, quis facilisis ligula lacinia eget. Fusce ac commodo nisi, id euismod dui. + +Cras nec dictum est. Nulla urna erat, bibendum sed elementum convallis, pellentesque id neque. In in dignissim dui. Cras pellentesque sed mauris sit amet venenatis. Maecenas eu ullamcorper velit, non rutrum nibh. Mauris bibendum varius erat, vitae tristique augue euismod at. Nunc ultricies nunc tortor, viverra maximus diam dapibus quis. Nullam malesuada tellus nec orci posuere, id varius dolor posuere. + +Ut sit amet mauris sit amet nulla sollicitudin maximus. Nunc semper at diam in ullamcorper. Etiam ultrices tincidunt congue. Nullam nec quam sed sem maximus finibus. Praesent felis urna, ultrices in gravida vel, eleifend vel ligula. Sed nisi mi, iaculis eu dignissim ut, laoreet et nulla. Aenean scelerisque tempus massa. Morbi eget lorem leo. Integer mi velit, ornare vel vestibulum non, sodales vitae diam. Vestibulum viverra porta nulla non bibendum. Vivamus sed tellus et libero sodales ultricies. + +Donec gravida posuere nunc sed pretium. Donec tempor scelerisque nulla, eu aliquet nulla sodales in. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean vulputate turpis odio, non porttitor magna tempor non. Proin libero turpis, congue eu dolor vitae, congue sollicitudin nulla. Nulla eleifend sagittis ex, ut euismod tellus egestas ut. Nulla feugiat diam sed lacus accumsan accumsan. Sed arcu nibh, dapibus sit amet pulvinar ut, pulvinar at dolor. Vivamus elementum nisl sit amet fringilla ornare. Morbi dapibus diam at felis eleifend, vitae interdum nunc euismod. Duis vehicula nibh et mauris aliquam tempus. Sed volutpat ultrices scelerisque. Integer feugiat, mi sit amet ornare interdum, eros neque dapibus libero, ac euismod massa erat ut quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac pellentesque felis. Vestibulum imperdiet sem at dapibus egestas. + +Curabitur tempus, eros eget accumsan pellentesque, elit risus rhoncus sapien, non vestibulum nisi purus in tellus. In commodo elementum odio, nec viverra felis placerat a. Mauris cursus neque eget purus mattis consequat. Aliquam erat volutpat. Proin vel urna quam. Integer accumsan lobortis tempor. Aliquam sed leo metus. Duis nisi nisl, viverra vitae lobortis at, euismod at sem. Nunc quis nisi quam. Curabitur consectetur porttitor sem, sed condimentum nunc hendrerit ac. Fusce ultrices at dui at luctus. Pellentesque non suscipit lectus. Nunc sodales nec urna et mollis. + +Aenean sed interdum justo. Ut mauris ex, interdum id accumsan vel, consequat sed risus. Pellentesque vel pulvinar lectus. Mauris ac lectus velit. Curabitur porta, felis sed euismod tempus, neque odio interdum massa, sed commodo sapien urna vel ex. Fusce lobortis sem nec lorem egestas, ut congue velit efficitur. In sodales commodo interdum. In tincidunt urna nunc, eget rhoncus elit maximus non. + +Duis mauris orci, varius ut nisl eget, pharetra rhoncus felis. Proin erat augue, congue vel augue eget, aliquet aliquam urna. Curabitur feugiat accumsan nunc, ac imperdiet ipsum viverra vel. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ut imperdiet risus. Nam consectetur dictum placerat. Nullam sed lacus leo. Phasellus sit amet leo a mi mollis mattis. Duis ut quam elit. Aliquam semper hendrerit dolor eu sollicitudin. Fusce efficitur lorem nec hendrerit mattis. Phasellus est orci, auctor non felis quis, interdum dictum augue. Duis vitae accumsan purus. + +Nullam a ligula blandit, rhoncus arcu et, iaculis velit. Praesent sed blandit mi. Sed commodo sem quis felis porta, a consequat enim mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur tempus dui dolor, quis tempor risus commodo eu. Mauris dignissim, massa id elementum porta, risus ligula egestas neque, tempus rhoncus turpis risus eget lorem. Sed sit amet semper nulla. Phasellus ullamcorper, velit ac faucibus maximus, ligula sapien ultrices felis, sit amet malesuada justo metus a dui. Sed et dapibus sapien, id laoreet enim. Suspendisse et justo eget nisl mollis pellentesque vel id metus. Duis lobortis risus non turpis semper, ac ultricies nisl rutrum. + +Donec porttitor a dolor ut commodo. Donec a odio id ante ultricies vehicula. Vivamus luctus quis est et maximus. Aliquam turpis ligula, tristique ut elit ac, vestibulum auctor diam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam commodo blandit nisi non iaculis. Praesent tempor dictum enim, in feugiat nunc fermentum ac. Quisque eros velit, imperdiet et nisl vel, mollis viverra nisl. + +Etiam porttitor consectetur erat et vulputate. Nam libero lacus, gravida quis dictum eget, venenatis quis mi. Aenean in arcu justo. Aenean sollicitudin, leo id mollis fermentum, metus tortor mollis leo, eu tincidunt justo odio et justo. Ut eget convallis elit. Nullam quis molestie lectus. Nam ullamcorper semper fringilla. Mauris sed finibus tellus, ac aliquam tellus. Maecenas feugiat ut justo non ullamcorper. Suspendisse vehicula ipsum enim, ut blandit dui malesuada ut. Nullam euismod nec urna vitae accumsan. Sed placerat mi laoreet dignissim posuere. Sed in diam diam. + +Praesent vel ligula lacinia, faucibus magna eget, posuere est. Proin arcu massa, scelerisque vitae maximus a, ultricies sodales turpis. Nunc suscipit tellus in enim hendrerit, in efficitur metus porttitor. Phasellus sagittis, turpis eu feugiat ornare, augue augue pellentesque urna, id tempus sem magna at urna. Quisque tincidunt faucibus consequat. Ut venenatis nisl sed rutrum mattis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi non orci ut nibh facilisis vehicula. Nulla non metus eget turpis laoreet feugiat. Nam lacus lectus, tristique quis commodo in, hendrerit ut sem. Praesent pretium imperdiet rutrum. + +Pellentesque vel ultrices metus. Maecenas leo purus, pretium quis eros vel, efficitur dignissim lorem. In hac habitasse platea dictumst. Pellentesque odio ligula, malesuada ac tincidunt maximus, commodo sed elit. Sed porttitor suscipit lectus sed pellentesque. Phasellus a commodo lacus. Donec ac commodo odio. Aenean faucibus ipsum sed nulla tempus luctus vel non dolor. Phasellus ornare elit lectus, eu sodales turpis molestie non. + +Sed tempus vitae purus id molestie. Aliquam erat volutpat. In convallis sapien ut nunc elementum accumsan. Maecenas quam tortor, sodales vel venenatis nec, posuere et ex. Nam urna libero, pharetra ut pretium tempus, auctor vitae arcu. In luctus non augue vitae elementum. Sed efficitur congue convallis. Proin elementum nec orci nec malesuada. Nulla porttitor quis urna nec dapibus. Cras malesuada nisl eget libero porttitor, eu dignissim neque tempus. Vestibulum porttitor sodales dolor, quis rhoncus felis dapibus non. Maecenas egestas, nisi ac imperdiet bibendum, lectus tortor sagittis elit, vitae semper libero mi sit amet nunc. Nulla ac tortor at enim pellentesque scelerisque. Nunc dapibus interdum est ac molestie. Nullam condimentum iaculis urna, eget ultrices magna aliquet sed. + +Duis posuere tortor eu libero suscipit, sed commodo mauris fermentum. Curabitur laoreet fermentum lacus. Maecenas venenatis eleifend quam, eu gravida tellus tincidunt quis. Donec ultricies, urna efficitur fringilla feugiat, dolor nisi dictum nulla, eu maximus mi nunc vel sem. Vestibulum est nibh, suscipit ut risus vitae, dapibus mattis augue. Ut mollis urna id eros elementum, sed pulvinar mauris ultricies. Quisque a enim purus. Etiam non dapibus neque. Vivamus urna lacus, aliquet eu sem sed, dictum bibendum enim. Sed cursus enim et sem dictum placerat. Mauris et nunc commodo, vulputate odio eget, pharetra ex. Sed vitae hendrerit leo, at bibendum turpis. Pellentesque tincidunt ullamcorper est, eget elementum est maximus et. + +Curabitur quis vehicula dolor, at semper sem. Vestibulum vel velit vitae turpis venenatis luctus. Maecenas et urna condimentum, venenatis lacus ut, fringilla libero. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Duis sollicitudin turpis vulputate viverra porta. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Curabitur aliquet convallis nisl in molestie. Integer tempus diam a felis auctor, a ullamcorper eros dictum. Donec porta justo felis, non feugiat purus faucibus ut. Duis at ipsum mollis, vulputate augue id, efficitur nunc. Nullam mattis, nunc et maximus dictum, nibh lacus porttitor erat, et molestie massa lectus quis sapien. Maecenas egestas lorem ut condimentum tristique. + +Maecenas eu accumsan neque. Cras vitae consequat ante, vel sagittis nibh. In rutrum odio nibh, ut bibendum lorem finibus eu. Proin vehicula erat in luctus iaculis. Praesent pulvinar tincidunt viverra. In et dignissim elit. Curabitur placerat tortor in vulputate consequat. Vestibulum a feugiat neque, condimentum elementum urna. Maecenas tincidunt turpis a ante aliquam, quis aliquet libero varius. Proin efficitur lorem mauris, in vehicula ex iaculis id. Curabitur rhoncus pulvinar varius. Morbi accumsan rutrum tellus. Vestibulum accumsan urna quis lacus interdum consectetur. Nulla facilisi. + +Duis rutrum enim eu lectus imperdiet elementum id in justo. Praesent id elit et ex pharetra commodo. Phasellus a ipsum feugiat, interdum tellus sit amet, ultrices dolor. Maecenas rhoncus lectus at quam elementum pharetra. Aenean in vestibulum lorem. Mauris vel lorem leo. Ut arcu massa, pellentesque dignissim orci dictum, sodales tristique lacus. Ut eget finibus tellus. Integer rhoncus dignissim felis quis fringilla. Nullam urna odio, porttitor non nisl vel, eleifend vulputate nulla. + +Ut sit amet ex sit amet tellus interdum ullamcorper. Fusce lacinia tellus et maximus lobortis. Donec eget elementum eros. Morbi commodo erat sed ligula aliquam laoreet. Cras bibendum pharetra arcu vel sollicitudin. Maecenas sodales orci eu nisl hendrerit, eget interdum lectus fringilla. Fusce tempus est nec turpis hendrerit accumsan. Suspendisse lacus risus, efficitur in turpis non, varius aliquam massa. Quisque vitae velit vel ipsum pulvinar dapibus vitae eu purus. Phasellus convallis nec magna eu sagittis. + +Praesent non congue ex, nec interdum ante. Quisque elementum arcu id nulla placerat congue. In feugiat metus at tortor accumsan pharetra. Fusce tortor nunc, dictum vitae scelerisque vel, dictum sit amet arcu. Sed suscipit purus a ultricies vehicula. Vivamus ipsum sem, vehicula eget laoreet nec, suscipit ac mi. Mauris non nulla sit amet ligula faucibus ultrices vel eget urna. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aliquam fringilla sed mi in euismod. Quisque odio justo, interdum vel urna eget, sodales vehicula quam. Vivamus feugiat varius elit. Pellentesque in mattis arcu. Suspendisse pharetra libero ex, ac ultrices felis volutpat id. Duis eu placerat mi. Proin dolor dolor, convallis eu lacinia a, lobortis eget velit. Maecenas ut varius nulla. + +Cras ullamcorper augue id aliquet commodo. Vestibulum leo erat, porttitor tristique urna vel, consequat sodales elit. Donec finibus eros arcu, non auctor tortor laoreet ut. Duis a pulvinar nisi. Aliquam a urna fringilla enim vehicula blandit vel vitae metus. Maecenas vel aliquam ante. Ut condimentum elementum lacinia. Integer mattis felis non ipsum aliquam, ut porttitor libero vehicula. Maecenas consectetur, massa in venenatis dapibus, dui mi feugiat mauris, sit amet tincidunt lorem erat nec ante. Praesent facilisis neque commodo erat dignissim commodo. Maecenas blandit venenatis tempus. Cras imperdiet elementum urna molestie aliquam. Aenean efficitur lectus risus, quis tristique odio tristique ut. Donec quis sem quis quam maximus molestie. Duis scelerisque posuere malesuada. Maecenas gravida erat nec massa auctor, sit amet aliquet massa gravida. + +Nam erat ligula, feugiat id imperdiet fringilla, fermentum non ligula. Praesent non elementum ex. Donec nec consectetur mauris, pulvinar tincidunt lacus. Nam varius, enim ut posuere sagittis, nisl massa condimentum arcu, vitae mattis justo ligula vel ipsum. Cras bibendum placerat orci ut sollicitudin. Aliquam rutrum et elit eu rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Duis tempus eros in egestas placerat. Fusce ullamcorper varius augue, sit amet interdum nunc finibus id. + +Suspendisse potenti. Sed a arcu a justo eleifend ullamcorper. Duis id lobortis nisi. Cras tempor sem nibh. Nunc sapien erat, rhoncus nec cursus in, blandit facilisis erat. Suspendisse vitae enim tellus. Aliquam bibendum sed urna sed facilisis. Proin efficitur quam sem, et tempor lacus molestie faucibus. Nullam et lectus ex. Curabitur sollicitudin convallis pretium. Nulla enim nisi, ullamcorper sit amet leo sit amet, pellentesque vestibulum eros. Nulla finibus ipsum justo, at porta est feugiat quis. Mauris vestibulum tellus sed tincidunt gravida. Nam ornare diam non libero aliquet venenatis. Maecenas non faucibus nulla. Proin sed molestie nibh. + +In hac habitasse platea dictumst. Phasellus ut massa varius enim accumsan vehicula. Donec sit amet dignissim sapien, quis sagittis augue. Suspendisse rhoncus arcu massa, ut dictum dolor venenatis eu. Donec egestas est sem, nec suscipit tortor fringilla et. Sed ullamcorper tellus arcu, ac ultrices mauris vestibulum eget. Duis ac tellus id odio semper rhoncus. Quisque aliquet maximus mattis. Proin imperdiet et augue sed egestas. Sed rutrum nisl et odio semper tincidunt. Fusce accumsan ante et est bibendum tincidunt. Ut pellentesque felis in lacus sagittis, at maximus nunc efficitur. + +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc tempor rutrum tempus. Vivamus maximus non erat vel tristique. Proin est sapien, sollicitudin non interdum non, viverra non magna. Proin quis aliquet eros. Sed fermentum dolor eget facilisis euismod. Aliquam pulvinar, nisl ac imperdiet lacinia, velit diam imperdiet erat, quis vulputate tortor est eget sem. In a vulputate arcu, sed aliquam mi. Suspendisse at pharetra quam, non fringilla massa. Aliquam at tortor euismod, facilisis libero id, mattis eros. Donec pretium nulla et ante vulputate volutpat. Fusce turpis est, bibendum vitae tincidunt sed, vestibulum nec ipsum. Maecenas at dolor dignissim, varius orci a, egestas leo. Mauris blandit non neque ut convallis. Sed id elit nunc. Vestibulum maximus at arcu ut ullamcorper. + +Nullam neque lorem, efficitur vitae molestie dignissim, sagittis sit amet felis. Aliquam a tortor quis velit gravida pulvinar. Sed interdum tristique pulvinar. Nulla in commodo odio, sed fermentum elit. Phasellus massa lacus, tristique vel finibus congue, tincidunt nec leo. Nunc rhoncus ex metus, eu ultrices augue feugiat vitae. Phasellus libero nisi, blandit at aliquet vitae, eleifend sit amet augue. Sed ac venenatis urna, ac pretium velit. Sed quis ultrices sapien, quis molestie elit. Sed euismod quam eget magna tristique, a lobortis massa blandit. Quisque lectus arcu, ornare at lorem non, venenatis mattis velit. Aenean congue magna vel ex finibus, vel porta magna condimentum. Maecenas cursus faucibus aliquet. Ut finibus velit erat, quis ultrices nunc sagittis id. Vestibulum auctor tristique venenatis. Aenean pharetra erat at ex dignissim gravida. + +Nunc molestie massa nec vulputate imperdiet. Ut quis magna sapien. Pellentesque sit amet ex neque. Cras eget mattis lorem. Nam a fermentum ipsum, vitae facilisis velit. Aenean iaculis pharetra elit aliquam aliquam. Etiam ut aliquet nunc, eget suscipit risus. + +Sed porttitor maximus neque vel consectetur. Aenean vel magna et purus posuere ultricies. Maecenas tempor varius neque. Aenean dictum urna at lorem tempor lacinia. Quisque eget congue elit, mattis consequat est. Maecenas sed odio gravida, facilisis odio at, vestibulum dui. Cras id ultrices nisl. Curabitur et accumsan ligula, et vestibulum urna. Curabitur a velit ac dolor suscipit congue. Etiam est nunc, porta efficitur nibh vitae, tristique pretium ipsum. Cras lobortis, nibh vel hendrerit gravida, sapien orci malesuada lectus, vitae iaculis sapien ex quis tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Fusce libero urna, pulvinar non ultrices at, euismod id tortor. + +Vivamus et dignissim nisi. Vestibulum eleifend sagittis finibus. Praesent vestibulum quis justo vitae cursus. Curabitur sagittis auctor aliquet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam bibendum nisi lectus, ac euismod dui aliquet vitae. Praesent dignissim nunc malesuada dui suscipit, at sollicitudin massa tempor. Curabitur eget justo in sapien dictum hendrerit. Sed sagittis cursus tellus. Nunc eget tristique quam, eu luctus quam. Nunc nec semper magna. Maecenas vel consequat ante. Quisque fermentum lacus ac consectetur fermentum. Phasellus blandit in orci sit amet interdum. Vestibulum accumsan libero purus, non tincidunt erat rhoncus ut. Phasellus condimentum massa non lacus porta, ut efficitur lectus congue. + +In dui neque, placerat eu gravida et, aliquet sed ex. Phasellus a diam iaculis, sagittis lacus in, consequat felis. Nullam finibus euismod tellus, et semper felis hendrerit ac. Morbi posuere viverra accumsan. Nam ac ullamcorper leo. Vivamus placerat ultrices commodo. Donec vitae eleifend lorem, in pretium sem. Maecenas turpis sem, gravida ac tortor ac, sollicitudin gravida lacus. Integer condimentum est ante, ut consequat odio scelerisque eget. + +Nulla id varius ex. Integer efficitur condimentum blandit. Nulla facilisi. Ut non urna eu nisi mattis pulvinar. Etiam tincidunt orci sit amet lectus imperdiet, et aliquam est pharetra. Nulla convallis orci vitae feugiat dignissim. Ut faucibus, diam sed mattis aliquet, lacus mi fringilla arcu, nec accumsan metus urna nec neque. + +Maecenas congue, dolor eu accumsan faucibus, ligula arcu suscipit ligula, quis pretium turpis ligula id leo. Curabitur accumsan dolor a condimentum elementum. Nunc eleifend ex enim, in tristique velit tincidunt vitae. Sed purus libero, rhoncus id elit at, scelerisque faucibus elit. Vestibulum sed dolor quam. Aliquam scelerisque enim risus, eu placerat lectus aliquam in. Nunc sapien diam, mattis quis mollis non, hendrerit id magna. + +Morbi suscipit bibendum urna, semper bibendum quam blandit fermentum. Maecenas molestie magna fringilla nisi varius, et scelerisque libero faucibus. Nunc luctus odio orci, convallis vulputate leo euismod non. Aliquam vulputate viverra libero vel dignissim. Curabitur id rhoncus nisl. Nullam tincidunt, velit sit amet dignissim finibus, est nulla gravida libero, non rhoncus leo enim ac purus. Sed nec tellus velit. Aenean nec risus quam. Suspendisse feugiat felis quis elit laoreet placerat. Nullam tortor tortor, condimentum a varius rutrum, euismod porttitor ligula. Aliquam erat volutpat. Nullam hendrerit finibus euismod. Aliquam erat volutpat. Nulla a elementum libero, non varius ex. + +Curabitur aliquet ligula ac molestie rhoncus. Praesent tempor, eros et pharetra facilisis, tellus elit lacinia justo, et porttitor augue justo eu enim. Fusce accumsan, urna nec faucibus interdum, dui augue feugiat nunc, eget pharetra mauris erat id est. Sed accumsan est sit amet nisl aliquam condimentum. Duis maximus est nec sem aliquam, eget hendrerit augue semper. Praesent a tristique sapien. Suspendisse egestas nunc eu justo dapibus consequat. Proin lectus magna, iaculis luctus scelerisque et, mattis non dui. Fusce tristique maximus mauris commodo tristique. Sed aliquet, erat eget sollicitudin iaculis, ante quam suscipit purus, at rhoncus mauris dolor sed augue. + +Duis tempus eget augue id luctus. Quisque dapibus libero sed maximus cursus. Pellentesque posuere vitae quam in ultrices. Duis tortor purus, condimentum nec lorem sit amet, eleifend varius quam. Nulla risus diam, rhoncus nec enim ac, bibendum cursus nisl. Suspendisse elit nisl, aliquet vitae purus at, tincidunt consectetur nunc. Integer congue justo erat, ac ultricies urna ultricies ut. Donec eu est eu nulla luctus bibendum. Duis a sapien et ligula auctor molestie. Maecenas non quam sed elit volutpat imperdiet. In fringilla semper velit, id aliquam nisi maximus ut. Proin ornare convallis sagittis. Sed massa urna, commodo quis congue non, finibus et est. Donec tristique diam sit amet risus vehicula, viverra dapibus nunc auctor. Praesent non lorem orci. + +Integer ullamcorper iaculis scelerisque. Nullam non lacus egestas, blandit quam nec, lobortis mi. Sed quis mauris gravida, rhoncus dolor id, venenatis tellus. Pellentesque iaculis consectetur velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tempor molestie metus, eu auctor elit ornare ac. Sed sed lectus vel libero convallis scelerisque eget lacinia eros. Pellentesque non eros at libero dapibus varius vel vitae arcu. Curabitur commodo volutpat tortor. + +Curabitur ultricies eget risus tristique iaculis. Nunc consequat diam nisl, in fermentum nibh bibendum sit amet. Quisque sit amet risus vitae libero accumsan eleifend. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam volutpat finibus tristique. Nullam quis feugiat dui. Phasellus sit amet quam efficitur, consectetur mauris vitae, egestas neque. Integer pellentesque ligula posuere congue consectetur. Vestibulum vel enim ex. Duis eget sapien in tortor placerat cursus. Vivamus rhoncus sapien dui, non tempor est pharetra quis. + +Proin ut leo vel tortor consequat mattis. Sed dictum nisi a nibh mattis gravida. Fusce in metus euismod, gravida quam sed, mattis ipsum. Morbi fermentum aliquam rutrum. Quisque commodo ac metus sed consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam iaculis ut leo id commodo. In enim dolor, dictum eget facilisis et, iaculis ac tellus. Nunc quis leo in lectus efficitur tincidunt. Vivamus pulvinar pretium erat, in malesuada tellus pulvinar non. Nulla maximus diam neque, a mattis diam pharetra vitae. Vestibulum sit amet felis non justo malesuada porttitor. + +Vivamus nec nibh nibh. Nulla vel nulla magna. Duis tempor, dolor in lacinia gravida, dolor dolor efficitur quam, ac feugiat lacus tellus non risus. Suspendisse in turpis enim. Donec sollicitudin elit et dui gravida, at lacinia massa porttitor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pharetra sagittis leo, et viverra nibh finibus in. Vivamus cursus odio sed enim blandit maximus. Suspendisse laoreet tempus mattis. Nam vel purus in sem pellentesque convallis. + +Nam scelerisque eget metus nec pretium. Donec interdum lectus quam, non lobortis nisi laoreet eu. Integer euismod sed leo a luctus. Aliquam sodales sit amet dolor sed dapibus. Nulla venenatis dignissim lorem. Cras sed velit et velit ullamcorper lobortis. Fusce mattis sagittis vulputate. Ut sollicitudin in erat et ullamcorper. Sed condimentum vehicula nulla quis tristique. Nullam vestibulum orci sit amet iaculis scelerisque. Phasellus tortor lacus, sollicitudin ac est viverra, iaculis laoreet elit. Pellentesque eu diam venenatis, vulputate augue ut, auctor mi. + +Fusce vel dolor nec nibh fringilla placerat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Suspendisse in rhoncus lorem. Etiam fringilla lacus rhoncus felis vulputate venenatis quis quis est. Vivamus accumsan fringilla ipsum vitae tristique. Vivamus at sapien dolor. Duis commodo neque id orci tincidunt cursus. + +Fusce sit amet suscipit justo. Nam placerat eu orci nec lacinia. Etiam sollicitudin pharetra volutpat. Nullam pulvinar, nibh ac fermentum maximus, enim nulla accumsan est, vitae tempus erat neque ut risus. Vestibulum pharetra suscipit massa nec vestibulum. Donec lobortis tortor sed pellentesque finibus. Quisque vitae lacus turpis. Aenean dignissim ante a lorem sollicitudin mattis. Maecenas vel mi fringilla, bibendum orci eu, egestas velit. Phasellus volutpat fringilla ex, consequat scelerisque dolor euismod eu. Cras eget mi enim. Aliquam pretium, magna pellentesque varius dignissim, arcu mauris lacinia dui, et viverra ex urna sit amet quam. + +Curabitur auctor volutpat diam vitae vehicula. Vivamus est arcu, efficitur nec interdum et, sagittis quis sem. Nam sodales vitae velit id pharetra. Mauris malesuada est quis nisi mattis, in facilisis lacus tempor. Integer cursus, risus sed molestie sollicitudin, nisi purus mattis justo, eget egestas tellus nisi mollis elit. Aenean sollicitudin justo luctus.""" + +var needle = "school" # a word intentionally not in the test data + + +# ===----------------------------------------------------------------------===# +# Baseline `_memmem` implementation +# ===----------------------------------------------------------------------===# +@always_inline +fn _memmem_baseline[ + type: DType +]( + haystack: DTypePointer[type], + haystack_len: Int, + needle: DTypePointer[type], + needle_len: Int, +) -> DTypePointer[type]: + if not needle_len: + return haystack + if needle_len > haystack_len: + return DTypePointer[type]() + if needle_len == 1: + return _memchr[type](haystack, needle[0], haystack_len) + + alias bool_mask_width = simdwidthof[DType.bool]() + var first_needle = SIMD[type, bool_mask_width](needle[0]) + var vectorized_end = _align_down( + haystack_len - needle_len + 1, bool_mask_width + ) + for i in range(0, vectorized_end, bool_mask_width): + var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle + var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) + while mask: + var offset = i + countr_zero(mask) + if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: + return haystack + offset + mask = mask & (mask - 1) + + for i in range(vectorized_end, haystack_len - needle_len + 1): + if haystack[i] != needle[0]: + continue + + if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: + return haystack + i + return DTypePointer[type]() + + +# ===----------------------------------------------------------------------===# +# Benchmarks +# ===----------------------------------------------------------------------===# +@parameter +fn bench_find_baseline(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + _ = _memmem_baseline( + haystack.as_uint8_ptr(), + len(haystack), + needle.as_uint8_ptr(), + len(needle), + ) + + b.iter[call_fn]() + + +@parameter +fn bench_find_optimized(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + _ = _memmem( + haystack.as_uint8_ptr(), + len(haystack), + needle.as_uint8_ptr(), + len(needle), + ) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + var m = Bench(BenchConfig(num_repetitions=5, warmup_iters=100)) + m.bench_function[bench_find_baseline](BenchId("find_baseline")) + m.bench_function[bench_find_optimized](BenchId("find_optimized")) + m.dump_report() From de49605c2f69a28073a11c8598a826b640aba696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Brunk?= Date: Sat, 8 Jun 2024 10:05:10 -0700 Subject: [PATCH 0796/2019] - Description and fix for new `//` inferred-only syntax - Fix for new explicit String conversions - Fix for partially bound types with `SIMD` changes MODULAR_ORIG_COMMIT_REV_ID: ba3f14ea83de0f480bddbeaef87c57153332b110 --- docs/manual/parameters/index.ipynb | 135 ++++++++++++++++++----------- 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 088ee3ced8..2b38241046 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -139,16 +139,15 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "3\n", - "3\n", - "3\n" + "42\n", + "42\n" ] } ], @@ -158,7 +157,8 @@ " for i in range(count):\n", " print(msg)\n", "\n", - "repeat[3](3)" + "# Must use keyword parameter for `count`\n", + "repeat[count=2](42)" ] }, { @@ -167,6 +167,40 @@ "source": [ "This updated function takes any `Stringable` type, so you can pass it an `Int`,\n", "`String`, or `Bool` value.\n", + "\n", + "You can't pass the `count` as a positional keyword without also specifying `MsgType`.\n", + "You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now\n", + "you can pass the following parameter `count` positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42\n", + "42\n" + ] + } + ], + "source": [ + "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", + " @parameter\n", + " for i in range(count):\n", + " print(msg)\n", + "\n", + "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", + "repeat[2](42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "Mojo's support for generics is still early. You can write generic functions like\n", "this using traits and parameters. You can also write generic collections like\n", @@ -189,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -243,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -341,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -401,7 +435,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -485,7 +519,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -535,7 +569,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -575,7 +609,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -607,7 +641,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -636,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -709,7 +743,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -741,7 +775,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -766,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -806,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -851,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -895,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -920,7 +954,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -961,7 +995,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -1006,7 +1040,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -1068,7 +1102,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1117,7 +1151,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -1177,7 +1211,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -1213,12 +1247,13 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "alias Bytes = SIMD[DType.uint8, _]\n", - "var b = Bytes[8]()" + "alias StringKeyDict = Dict[String, _]\n", + "var b = StringKeyDict[UInt8]()\n", + "b[\"answer\"] = 42" ] }, { @@ -1234,7 +1269,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -1331,7 +1366,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -1367,7 +1402,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -1398,7 +1433,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -1415,7 +1450,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -1436,7 +1471,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -1483,7 +1518,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -1491,7 +1526,7 @@ "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", " fn __str__(self) -> String:\n", " var values = StaticIntTuple[3](sugar, cream, chocolate)\n", - " return str(\"Fudge\") + values" + " return str(\"Fudge\") + str(values)" ] }, { @@ -1504,7 +1539,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -1526,7 +1561,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -1544,7 +1579,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -1584,7 +1619,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -1604,12 +1639,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(str(\"Devoured \") + str(f))" + " print(str(\"Devoured \") + str(f))" ] }, { @@ -1625,7 +1660,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -1642,7 +1677,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -1671,7 +1706,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -1705,9 +1740,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Mojo", + "display_name": "Mojo Source", "language": "mojo", - "name": "mojo-jupyter-kernel" + "name": "mojo-source-kernel" }, "language_info": { "codemirror_mode": { From 2904f9d6f68d60771271a1b39d3ff0906ee95150 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Sat, 8 Jun 2024 17:34:33 -0700 Subject: [PATCH 0797/2019] [Docs] Fix some broken links. Fixed an outdated sample & fixed an omission in the Mojo changelog reported on Discord. And brought over two fixes from public repo PRs. MODULAR_ORIG_COMMIT_REV_ID: 2f7dfb2c5a7d377c5192a90480439bbac468cd5a --- docs/changelog-released.md | 32 +++++++++------ docs/manual/functions.ipynb | 2 +- docs/manual/get-started.md | 6 +-- docs/manual/index.md | 2 +- docs/manual/lifecycle/death.ipynb | 67 +++++++------------------------ docs/manual/python/types.ipynb | 2 +- 6 files changed, 39 insertions(+), 72 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 55660efbf8..813273cc73 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -51,11 +51,11 @@ Big themes for this release: ### Language changes - Mojo has changed how `def` function arguments are processed. Previously, by - default, arguments to a `def` were treated treated according to the `owned` + default, arguments to a `def` were treated according to the `owned` convention, which makes a copy of the value, enabling that value to be mutable in the callee. - This could lead to a major performance issues because of the proliferation of + This could lead to major performance issues because of the proliferation of unnecessary copies. It also required you to declare non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: `def` functions take arguments as `borrowed` by default (consistent with `fn` functions) but @@ -221,7 +221,7 @@ Big themes for this release: - New traits and related features: - - Added built-in [`repr()`](mojo/stdlib/builtin/repr/repr) function and + - Added built-in [`repr()`](/mojo/stdlib/builtin/repr/repr) function and [`Representable`](/mojo/stdlib/builtin/repr/Representable) trait. ([PR #2361](https://github.com/modularml/mojo/pull/2361)) @@ -489,6 +489,9 @@ Big themes for this release: - Many functions returning a pointer type have been unified to have a public API function of `unsafe_ptr()`. + - The `Tensor.data()` method has been renamed to `unsafe_ptr()`. The return + type is still a `DTypePointer[T]`. + - Collections: - [`List`](/mojo/stdlib/collections/list/List) now has an @@ -783,7 +786,9 @@ Big themes for this release: - `math.limit.min_finite()`: use `utils.numerics.min_finite()` - The `tensor.random` module has been removed. The same functionality is now - accessible via the `Tensor.rand()` and `Tensor.randn()` static methods. + accessible via the [`Tensor.rand()`](/mojo/stdlib/tensor/tensor/Tensor#rand) + and [`Tensor.randn()`](/mojo/stdlib/tensor/tensor/Tensor#randn) static + methods. - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. @@ -1611,7 +1616,7 @@ fixed in a future release. type, a register-passable alternative to [`Optional`](/mojo/stdlib/collections/optional/Optional). -- The [`ulp()`](/mojo/stdlib/math/math/ulp) function has been added to the +- The [`ulp()`](/mojo/stdlib/utils/numerics/ulp) function has been added to the `math` module. This allows you to get the units of least precision (or units of last place) of a floating point value. @@ -1700,10 +1705,11 @@ fixed in a future release. from buffer import parallel_memcpy ``` - - The [`rand()`](/mojo/stdlib/tensor/random/rand) and - [`randn()`](/mojo/stdlib/tensor/random/randn) functions from the `random` - package that return a `Tensor` have moved to the `tensor` package. Note that - the overloads that write to a `DTypePointer` remain in the `random` package. + - The [`rand()`](/mojo/stdlib/tensor/tensor/Tensor#rand) and + [`randn()`](/mojo/stdlib/tensor/tensor/Tensor#randn) functions from the + `random` package that return a `Tensor` have moved to the `tensor` package. + Note that the overloads that write to a `DTypePointer` remain in the + `random` package. If you happen to be using both versions in the same source file, you can import them both using the `import as` syntax: @@ -1721,9 +1727,9 @@ fixed in a future release. from os import abort ``` - - The [`isinf()`](/mojo/stdlib/math/math/isinf) and - [`isfinite()`](/mojo/stdlib/math/math/isfinite) methods have been moved from - `math.limits` to the `math` module. + - The [`isinf()`](/mojo/stdlib/utils/numerics/isfinite) and + [`isfinite()`](/mojo/stdlib/utils/numerics/isfinite) methods have been moved + from `math.limits` to the `math` module. ```mojo from math import ininf, isfinite @@ -2061,7 +2067,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `UnusualSlice` constructor. - The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/reference/reference) instead of having to + [`Reference`](/mojo/stdlib/memory/reference/Reference) instead of having to return an MLIR internal reference type. - Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 64e4e2e604..5cd00427b9 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -404,7 +404,7 @@ "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", - "[`Reference`](/mojo/stdlib/memory/unsafe/reference) for each value instead\n", + "[`Reference`](/mojo/stdlib/memory/reference/Reference) for each value instead\n", "of the value itself. You must add an empty subscript operator `[]` to\n", "dereference the reference and retrieve the value:\n" ] diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index 15fb479331..36e41a6bc9 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -378,9 +378,9 @@ Now let's write the code in a Mojo source file and run it with the Hello, world! ``` -If this didn't work for you, double-check that your code looks exactly like the code -in step 1, and make sure you correctly [installed -mojo](/mojo/install) (it includes Mojo). +If this didn't work for you, double-check that your code looks exactly like the +code in step 1, and make sure you correctly installed either [MAX](/max/install) +(which includes Mojo) or [Mojo](#1-install-mojo). ## 4. Build an executable binary diff --git a/docs/manual/index.md b/docs/manual/index.md index f0390218c4..139148920e 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -24,7 +24,7 @@ feedback](/mojo/community). - **Get started** - - [Why Mojo](/mojo/manual/why-mojo) + - [Why Mojo](/mojo/why-mojo) - [Get started with Mojo](/mojo/manual/get-started) - **Language basics** diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 4df4001373..d13e357940 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -200,7 +200,8 @@ "dynamically allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", - "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" + "memory allocated by its\n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer):" ] }, { @@ -209,17 +210,21 @@ "metadata": {}, "outputs": [], "source": [ + "from memory.unsafe_pointer import UnsafePointer\n", + "\n", "struct HeapArray:\n", - " var data: Pointer[Int]\n", + " var data: UnsafePointer[Int]\n", " var size: Int\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", - " self.data = Pointer[Int].alloc(self.size)\n", + " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data.store(i, val)\n", + " (self.data + i).init_pointee_copy(val)\n", "\n", " fn __del__(owned self):\n", + " for i in range(self.size):\n", + " (self.data + 1).destroy_pointee()\n", " self.data.free()" ] }, @@ -231,40 +236,9 @@ "when a pointer is destroyed, Mojo doesn't call the destructors on those values.\n", "\n", "So in the `HeapArray` example above, calling `free()` on the pointer releases\n", - "the memory, but doesn't call the destructors on the stored `Int` values. That's\n", - "OK in this case, because trivial types like `Int` have no-op destructors.\n", - "However, in the general case—if you're storing values that _might_ have\n", - "functional destructors—it's not enough to call `free()` on the pointer. You\n", - "should also ensure that Mojo completely destroys all the allocated types\n", - "through their destructors.\n", - "\n", - "The following example updates the previous `__del__()` method to ensure\n", - "destructors are called on array elements. It does this by assigning each of the\n", - "stored values in turn to the `_` \"discard\" pattern. Assigning a value to the\n", - "`_` discard variable tells the compiler that this is the last use of that\n", - "value, so it can be destroyed immediately." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "struct HeapArray:\n", - " var data: Pointer[Int]\n", - " var size: Int\n", - "\n", - " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " _ = __get_address_as_owned_value((self.data + i).address)\n", - " self.data.free()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "the memory, but doesn't call the destructors on the stored values. To invoke\n", + "the destructors, use the `destroy_pointee()` method provided by the \n", + "`UnsafePointer` type.\n", "\n", ":::note\n", "\n", @@ -274,20 +248,7 @@ "destroys a value, however, it passes in the original value as `self`, not a\n", "copy.\n", "\n", - ":::\n", - "\n", - "For instances of the \n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type, use\n", - "the [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", - "function instead of the discard pattern. For example, if the previous example\n", - "used `UnsafePointer`, the `__del__()` method would look like this:\n", - "\n", - "```mojo\n", - "fn __del__(owned self):\n", - " for i in range(self.size):\n", - " destroy_pointee(self.data + i)\n", - " self.data.free()\n", - "```" + ":::" ] }, { @@ -303,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index 24533e268a..f3c74ebe72 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -215,7 +215,7 @@ "using the built-in \n", "[`int()`](/mojo/stdlib/builtin/int/int-function),\n", "[`str()`](/mojo/stdlib/builtin/str/str),\n", - "and [`bool()`](/mojo/stdlib/builtin/bool/bool) functions, and print Python \n", + "and [`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python \n", "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", From 5a7869cf017710fcded96440aa4ee285fb70537e Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 10 Jun 2024 18:33:37 -0700 Subject: [PATCH 0798/2019] [Docs] Fix more links. Does what it says on the tin. MODULAR_ORIG_COMMIT_REV_ID: 8268efe61ba7b16c78211589c99512e5ac3f3417 --- docs/changelog-released.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 813273cc73..b9b75b85d7 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -813,7 +813,7 @@ Special thanks to our community contributors: [@rd4com](https://github.com/rd4com), [@toiletsandpaper](https://github.com/toiletsandpaper), -[@helehex](https://github.com/helehex), [@rd4com](https://github.com/rd4com/), +[@helehex](https://github.com/helehex), [@artemiogr97](https://github.com/artemiogr97), [@mikowals](https://github.com/mikowals), [@kernhanda](https://github.com/kernhanda), [@lsh](https://github.com/lsh), @@ -1787,8 +1787,8 @@ fixed in a future release. - The [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload that worked on [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) types has been removed in favor - of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer) and - [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): + of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/LegacyPointer) + and [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): ```mojo # Doesn't work @@ -2073,8 +2073,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) method, for moving a value from one pointer memory location to another. -- Added built-in [`hex()`](/mojo/stdlib/builtin/hex/hex) function, which can be - used to format any value whose type implements the +- Added built-in [`hex()`](/mojo/stdlib/builtin/format_int/hex) function, which + can be used to format any value whose type implements the [`Intable`](/mojo/stdlib/builtin/int/Intable) trait as a hexadecimal string. - [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now implements @@ -3098,7 +3098,7 @@ the previous "read to EOF" behavior when size is negative. - Subscripting added to [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer): + [`Pointer`](/mojo/stdlib/memory/unsafe/LegacyPointer): ```mojo let p = DTypePointer[DType.float16].alloc(4) From 26c6476c99fb9b9b3f5abef7be5fa677acfe1a40 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sun, 16 Jun 2024 08:02:41 -0700 Subject: [PATCH 0799/2019] Adds a community contribution that was missed from the changelog. MODULAR_ORIG_COMMIT_REV_ID: 4c70c89e17196554aab8d864c51ecc235c7a90c8 --- docs/changelog-released.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index b9b75b85d7..ae9121d157 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -683,6 +683,11 @@ Big themes for this release: will likely shuffle around as we're reworking our built-in `sort()` function and optimizing it. +- `infinity` and `NaN` are now correctly handled in + [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) + and an `inf` function has been added to `utils/numerics.mojo`. + ([PR #2375](https://github.com/modularml/mojo/pull/2375)) + ### Tooling changes - Invoking `mojo package my-package -o my-dir` on the command line, where @@ -825,7 +830,8 @@ Special thanks to our community contributors: [@martinvuyk](https://github.com/martinvuyk), [@ChristopherLR](https://github.com/ChristopherLR), [@mzaks](https://github.com/mzaks), [@bgreni](https://github.com/bgreni), -[@Brian-M-J](https://github.com/Brian-M-J) +[@Brian-M-J](https://github.com/Brian-M-J), +[@leandrolcampos](https://github.com/leandrolcampos) ## v24.3 (2024-05-02) From c6294abb43295b22c919930185d8bb50057db24c Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 18 Jun 2024 15:41:44 -0700 Subject: [PATCH 0800/2019] [Docs] Fix code and typos in "Death of a value." Example was using new APIs that didn't make it into 24.4. MODULAR_ORIG_COMMIT_REV_ID: 0f6b84a575c484e552313ae66ac6c4350824e125 --- docs/manual/lifecycle/death.ipynb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index d13e357940..b67b73661d 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -206,11 +206,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n", "\n", "struct HeapArray:\n", " var data: UnsafePointer[Int]\n", @@ -220,11 +220,11 @@ " self.size = size\n", " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(val)\n", + " initialize_pointee_copy(self.data + i, val)\n", "\n", " fn __del__(owned self):\n", " for i in range(self.size):\n", - " (self.data + 1).destroy_pointee()\n", + " destroy_pointee(self.data + i)\n", " self.data.free()" ] }, @@ -237,14 +237,14 @@ "\n", "So in the `HeapArray` example above, calling `free()` on the pointer releases\n", "the memory, but doesn't call the destructors on the stored values. To invoke\n", - "the destructors, use the `destroy_pointee()` method provided by the \n", - "`UnsafePointer` type.\n", + "the destructors, use the `destroy_pointee()` function from the \n", + "`unsafe_pointer` module.\n", "\n", ":::note\n", "\n", "You can't just call the destructor explicitly. Because `__del__()`\n", "takes `self` as an `owned` value, and owned arguments are copied by default,\n", - "`foo._del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", + "`foo.__del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", "destroys a value, however, it passes in the original value as `self`, not a\n", "copy.\n", "\n", From 6e7885fbe89384c15c90571f7c4f999d90e46a0f Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 2 Jul 2024 15:27:07 -0700 Subject: [PATCH 0801/2019] [Docs] Fix links and typos (cherry-picks) MODULAR_ORIG_COMMIT_REV_ID: 4e9ecf09cee9841786b7da9524451ebe6f0c21ac --- docs/changelog-released.md | 52 ++--- docs/faq.md | 8 +- docs/manual/basics.ipynb | 16 +- .../manual/decorators/register-passable.ipynb | 20 +- docs/manual/decorators/staticmethod.ipynb | 2 +- docs/manual/decorators/value.ipynb | 2 +- docs/manual/functions.ipynb | 6 +- docs/manual/index.md | 8 +- docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/lifecycle/index.ipynb | 14 +- docs/manual/lifecycle/life.ipynb | 42 ++-- docs/manual/packages.md | 8 +- docs/manual/parameters/index.ipynb | 19 +- docs/manual/pointers.ipynb | 6 +- docs/manual/python/index.ipynb | 2 +- docs/manual/structs.ipynb | 14 +- docs/manual/traits.ipynb | 23 +- docs/manual/types.ipynb | 51 ++-- docs/manual/values/index.ipynb | 2 +- docs/manual/values/ownership.ipynb | 18 +- docs/manual/values/value-semantics.ipynb | 4 +- docs/manual/variables.ipynb | 39 +--- docs/upgrade-guide.md | 219 ------------------ 23 files changed, 161 insertions(+), 416 deletions(-) delete mode 100644 docs/upgrade-guide.md diff --git a/docs/changelog-released.md b/docs/changelog-released.md index ae9121d157..8f9a51dee7 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -2358,7 +2358,7 @@ experience without dedicated sugar. ### ⭐️ New - A new Mojo-native dictionary type, - [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. + [`Dict`](/mojo/stdlib/collections/dict) for storing key-value pairs. `Dict` stores values that conform to the [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. Keys need to conform to the new @@ -2843,7 +2843,7 @@ experience without dedicated sugar. a given trait. The following section gives a brief overview of traits—see the - [Mojo Manual](/mojo/manual/traits.html) and this + [Mojo Manual](/mojo/manual/traits) and this [traits blog post](https://modul.ar/traits-blog) for more details! Traits are declared with the `trait` keyword. The bodies of traits should @@ -2906,7 +2906,7 @@ experience without dedicated sugar. the_parents(x) ``` - For more information, see the [Traits page](/mojo/manual/traits.html) + For more information, see the [Traits page](/mojo/manual/traits) in the Mojo Manual. - A fundamental `Destructable` trait has been added to the language. This is a @@ -2959,8 +2959,8 @@ experience without dedicated sugar. - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. It doesn't include _everything_ about Mojo yet, but it includes a lot, - and more than the original [programming - manual](/mojo/programming-manual.html) (now deprecated). + and more than the original programming + manual (now deprecated). Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love @@ -3091,7 +3091,7 @@ the previous "read to EOF" behavior when size is negative. let data = binary_path.read_bytes() ``` -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` +- [`Tensor`](/mojo/stdlib/tensor/tensor) has new `save()` and `load()` methods to save and load to file. These methods preserve shape and datatype information. For example: @@ -3165,15 +3165,15 @@ the previous "read to EOF" behavior when size is negative. print(len(nums)) ``` -- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) +- The assert functions in the [`testing`](/mojo/stdlib/testing/testing) package now raise an `Error` when the assertion fails instead of returning a `Bool` for whether the assertion succeeded or not. -- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no +- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases) type are no longer (implicitly) assumed to be register-passable. A new `AnyRegType` type is used to represent generic types that are register passable. -- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) +- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark) report is now an argument instead of a parameter: ```mojo @@ -3269,8 +3269,8 @@ the previous "read to EOF" behavior when size is negative. KwParamStruct[msg="hello", a=42]() # prints 'hello 42' ``` - For more detail, see the [programming - manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). + For more detail, see the [Mojo + Manual](/mojo/manual/parameters/#optional-parameters-and-keyword-parameters). For the time being, the following notable limitations apply: @@ -3348,7 +3348,7 @@ the previous "read to EOF" behavior when size is negative. pass ``` -- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been +- The [`benchmark`](/mojo/stdlib/benchmark/benchmark) module has been simplified and improved so you can now run: ```mojo @@ -3415,14 +3415,14 @@ the previous "read to EOF" behavior when size is negative. CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' ``` -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and +- [`Tensor`](/mojo/stdlib/tensor/tensor) has new `fromfile()` and `tofile()` methods to save and load as bytes from a file. - The built-in `print()` function now works on the - [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. + [`Tensor`](/mojo/stdlib/tensor/tensor) type. -- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and - [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors +- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape) and + [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape) now have constructors that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list/List) and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. @@ -3556,7 +3556,7 @@ the previous "read to EOF" behavior when size is negative. print(DefaultParams["meow"]().message) # prints 'meow' ``` -- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O +- The new [`file`](/mojo/stdlib/builtin/file) module adds basic file I/O support. You can now write: ```mojo @@ -3856,7 +3856,7 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco value from one location to another. For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). + [move constructors](/mojo/manual/lifecycle/life#move-constructors). - The Error type in Mojo has changed. Instead of extracting the error message using `error.value` you will now extract the error message using @@ -3911,23 +3911,23 @@ All earlier releases were considered version 0.1. ### ⭐️ New - A new `clobber_memory` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. + [`benchmark`](/mojo/stdlib/benchmark/benchmark) module. The clobber memory function tells the system to flush all memory operations at the specified program point. This allows you to benchmark operations without the compiler reordering memory operations. - A new `keep` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` + [`benchmark`](/mojo/stdlib/benchmark/benchmark) module. The `keep` function tries to tell the compiler not to optimize the variable away if not used. This allows you to avoid compiler's dead code elimination mechanism, with a low footprint side effect. - New `shift_right` and `shift_left` functions have been added to the - [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in + [`simd`](/mojo/stdlib/builtin/simd) module. They shift the elements in a SIMD vector right/left, filling elements with zeros as needed. - A new `cumsum` function has been added to the - [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes + [`reduction`](/mojo/stdlib/algorithm/reduction) module that computes the cumulative sum (also known as scan) of input elements. - Mojo Jupyter kernel now supports code completion. @@ -4235,7 +4235,7 @@ All earlier releases were considered version 0.1. dependencies between global variables, and their destructors are called in the reverse order. -- The [Mojo programming manual](/mojo/programming-manual.html) is now written +- The Mojo programming manual is now written as a Jupyter notebook, and available in its entirety in the Mojo Playground (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of the same material, but it was not up-to-date.) @@ -4359,7 +4359,7 @@ possible to write the following: ``` For details on the overload resolution logic, see the Mojo Manual section on - [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). + [parameters](/mojo/manual/parameters/#overloading-on-parameters). - A new `cost_of()` function has been added to `Autotune`. This meta-function must be invoked at compile time, and it returns the number of MLIR operations @@ -4491,7 +4491,7 @@ only in declared parameter names, e.g. the following now works correctly: ``` When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) + [`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and) value (this is common in initializers for example), it is allowed to do whatever it wants with the value and destroy it when it is finished. In order to support this, @@ -4649,7 +4649,7 @@ only in declared parameter names, e.g. the following now works correctly: optimized Matmul implementation is 3x faster. - Renamed the [`^` postfix -operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) +operator](/mojo/manual/values/ownership#transfer-arguments-owned-and) from "consume" to "transfer." #### 🛠️ Fixed diff --git a/docs/faq.md b/docs/faq.md index a6d385cbc0..49ca3bea84 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -44,7 +44,7 @@ it’s missing. We are guided more by pragmatism than novelty, but Mojo’s use and domains in a way that other languages haven’t demonstrated (for an example of Mojo talking directly to MLIR, see our [low-level IR in Mojo notebook](/mojo/notebooks/BoolMLIR)). It also -includes autotuning, and has caching and distributed compilation built into its +has caching and distributed compilation built into its core. We also believe Mojo has a good chance of unifying hybrid packages in the broader Python community. @@ -210,12 +210,6 @@ means that Mojo is easily extensible to any hardware backend. For more information, read about our vision for [pluggable hardware](https://www.modular.com/hardware). -### How does Mojo autotuning work? - -For details about what autotuning capabilities we support so far, check out -the Mojo Manual section on [metaprogramming](/mojo/manual/parameters/). -But stay tuned for more details! - ### Who writes the software to add more hardware support for Mojo? Mojo provides all the language functionality necessary for anyone to extend diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index 59e6caffae..f7730cf07e 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -46,7 +46,7 @@ ":::note\n", "\n", "Mojo is a young language and there are many [features still\n", - "missing](/mojo/roadmap.html). As such, Mojo is currently **not** meant for\n", + "missing](/mojo/roadmap). As such, Mojo is currently **not** meant for\n", "beginners. Even this basics section assumes some programming experience.\n", "However, throughout the Mojo Manual, we try not to assume experience with any\n", "particular language.\n", @@ -147,7 +147,7 @@ "metadata": {}, "source": [ "For more details, see the page about\n", - "[functions](/mojo/manual/functions.html)." + "[functions](/mojo/manual/functions)." ] }, { @@ -237,7 +237,7 @@ "(only the argument and return types must be declared in `fn` functions).\n", "\n", "For more details, see the page about\n", - "[variables](/mojo/manual/variables.html)." + "[variables](/mojo/manual/variables)." ] }, { @@ -303,7 +303,7 @@ "metadata": {}, "source": [ "For more details, see the page about\n", - "[structs](/mojo/manual/structs.html)." + "[structs](/mojo/manual/structs)." ] }, { @@ -398,7 +398,7 @@ "`SomeTrait`. Thus, `fun_with_traits()` is known as a \"generic function\" because\n", "it accepts a _generalized_ type instead of a specific type.\n", "\n", - "For more details, see the page about [traits](/mojo/manual/traits.html)." + "For more details, see the page about [traits](/mojo/manual/traits)." ] }, { @@ -629,7 +629,7 @@ "source": [ "Documenting your code with these kinds of comments (known as \"docstrings\")\n", "is a topic we've yet to fully specify, but you can generate an API reference\n", - "from docstrings using the [`mojo doc` command](/mojo/cli/doc.html)." + "from docstrings using the [`mojo doc` command](/mojo/cli/doc)." ] }, { @@ -700,7 +700,7 @@ "\n", "If you're in the mood to read more, continue through each page of this\n", "Mojo Manual using the buttons at the bottom of each page—the next page from\n", - "here is [Functions](/mojo/manual/functions.html).\n", + "here is [Functions](/mojo/manual/functions).\n", "\n", "Otherwise, here are some other resources to check out:\n", "\n", @@ -716,7 +716,7 @@ " that teach advanced Mojo features.\n", "\n", "- To see all the available Mojo APIs, check out the [Mojo standard library\n", - " reference](/mojo/lib.html)." + " reference](/mojo/lib)." ] } ], diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb index 682a6f9259..9627f7f5dd 100644 --- a/docs/manual/decorators/register-passable.ipynb +++ b/docs/manual/decorators/register-passable.ipynb @@ -85,19 +85,6 @@ "This behavior is what we expect from `Pair`, with or without the decorator." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note Updated syntax\n", - "\n", - "Register passable types now accept standard lifecycle methods, as shown above. \n", - "Prior to Mojo 24.1, register-passable types required a non-standard constructor that returned a value, with syntax like this: `return Self{a: one, b: two}`. \n", - "The older syntax is still supported but will be removed in a future release. For more information about this change, see the [Mojo 24.1 release notes](/mojo/changelog#v241-2024-02-29).\n", - "\n", - ":::" - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -115,7 +102,7 @@ "instead of being passed by-pointer.\n", "\n", "1. `@register_passable` types cannot have a [`__moveinit__()`\n", - "constructor](/mojo/manual/lifecycle/life.html#move-constructors), because\n", + "constructor](/mojo/manual/lifecycle/life#move-constructors), because\n", "values passed in a register cannot be passed by reference.\n" ] }, @@ -154,7 +141,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This is similar to the [`@value`](/mojo/manual/decorators/value.html) decorator,\n", + "This is similar to the [`@value`](/mojo/manual/decorators/value) decorator,\n", "except when using `@register_passable(\"trivial\")` the only lifecycle method\n", "you're allowed to define is the `__init__()` constructor (but you don't have\n", "to)—you _cannot_ define any copy or move constructors or a destructor.\n", @@ -183,8 +170,7 @@ "This decorator is due for reconsideration. Lack of custom\n", "copy/move/destroy logic and \"passability in a register\" are orthogonal concerns\n", "and should be split. This former logic should be subsumed into a more general\n", - "[`@value(\"trivial\")`](/mojo/manual/decorators/value.html) decorator, which is\n", - "orthogonal from `@register_passable`.\n", + "decorator, which is orthogonal to `@register_passable`.\n", "\n", ":::" ] diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index b8d46eae86..f6e798b97a 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -60,7 +60,7 @@ "access instance data.\n", "\n", "For more information see the documentation on\n", - "[static methods](/mojo/manual/structs.html#static-methods)." + "[static methods](/mojo/manual/structs#static-methods)." ] } ], diff --git a/docs/manual/decorators/value.ipynb b/docs/manual/decorators/value.ipynb index b6753b42e5..807bd962cb 100644 --- a/docs/manual/decorators/value.ipynb +++ b/docs/manual/decorators/value.ipynb @@ -86,7 +86,7 @@ "metadata": {}, "source": [ "For more information about these lifecycle methods, read\n", - "[Life of a value](/mojo/manual/lifecycle/life.html)." + "[Life of a value](/mojo/manual/lifecycle/life)." ] } ], diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 5cd00427b9..063d40f661 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -261,7 +261,7 @@ "metadata": {}, "source": [ "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout).\n", + "[`inout`](/mojo/manual/values/ownership#mutable-arguments-inout).\n", "\n", "Any optional arguments must appear after any required arguments. [Keyword-only\n", "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", @@ -599,7 +599,7 @@ "\n", " - Variadic keyword arguments are always implicitly treated as if they\n", " were declared with the `owned` [argument \n", - " convention](/mojo/manual/values/ownership.html#argument-conventions), and\n", + " convention](/mojo/manual/values/ownership#argument-conventions), and\n", " can't be declared otherwise:\n", "\n", " ```mojo\n", @@ -787,7 +787,7 @@ "ambiguity by explicitly casting your value to a supported argument type. For\n", "example, in the following code, we want to call the overloaded `foo()`\n", "function, but both implementations accept an argument that supports [implicit\n", - "conversion](/mojo/manual/variables.html#implicit-type-conversion) from\n", + "conversion](/mojo/manual/variables#implicit-type-conversion) from\n", "`StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a\n", "compiler error. We can fix it by casting the value to the type we really want:" ] diff --git a/docs/manual/index.md b/docs/manual/index.md index 139148920e..7f8256e80c 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -39,20 +39,20 @@ feedback](/mojo/community). - **Value ownership** - - [Intro to value ownership](/mojo/manual/values/index) + - [Intro to value ownership](/mojo/manual/values/) - [Value semantics](/mojo/manual/values/value-semantics) - [Ownership and borrowing](/mojo/manual/values/ownership) - **Value lifecycle** - - [Intro to value lifecycle](/mojo/manual/lifecycle/index) + - [Intro to value lifecycle](/mojo/manual/lifecycle/) - [Life of a value](/mojo/manual/lifecycle/life) - [Death of a value](/mojo/manual/lifecycle/death) - **Traits and parameters** - [Traits](/mojo/manual/traits) - - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index) + - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/) - **Pointers** @@ -60,7 +60,7 @@ feedback](/mojo/community). - **Python** - - [Python integration](/mojo/manual/python/index) + - [Python integration](/mojo/manual/python/) - [Python types](/mojo/manual/python/types) - **Tools** diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index b67b73661d..b9496583cb 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -418,7 +418,7 @@ "dismantle the `existing`/`self` value that's `owned`. That is, `__moveinit__()`\n", "implicitly destroys sub-elements of `existing` in order to transfer ownership\n", "to a new instance (read more about the [move\n", - "constructor](/mojo/manual/lifecycle/life.html#move-constructor)),\n", + "constructor](/mojo/manual/lifecycle/life#move-constructor)),\n", "while `__del__()` implements the deletion logic for its `self`. As such, they\n", "both need to own and transform elements of the `owned` value, and they\n", "definitely don’t want the original `owned` value's destructor to also run—that\n", diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 1c3226f9d0..39f645eca4 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -21,7 +21,7 @@ "source": [ "So far, we've explained how Mojo allows you to build high-performance code that\n", "is memory safe _without_ manually managing memory, using Mojo's [ownership\n", - "model](/mojo/manual/values/ownership.html). However, Mojo is designed for\n", + "model](/mojo/manual/values/ownership). However, Mojo is designed for\n", "[systems programming](https://en.wikipedia.org/wiki/Systems_programming), which\n", "often requires manual memory management for custom data types. So, Mojo lets\n", "you do that as you see fit. To be clear, Mojo has no reference counter and no\n", @@ -31,9 +31,9 @@ "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", - "[structs](/mojo/manual/structs.html). You can actually write your own\n", + "[structs](/mojo/manual/structs). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", - "[MLIR dialects](/mojo/notebooks/BoolMLIR.html).\n", + "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", "\n", "What's great about the Mojo language is that it provides you these low-level\n", "tools for systems programming, but within a framework that helps you build\n", @@ -43,7 +43,7 @@ "semantics](/mojo/manual/values/value-semantics), the programmer instantiating\n", "your type/object doesn't need to think about memory management at all, and the\n", "behavior will be safe and predictable, thanks to [value\n", - "ownership](/mojo/manual/values/ownership.html).\n", + "ownership](/mojo/manual/values/ownership).\n", "\n", "In summary, it's the responsibility of the type author to manage the memory and\n", "resources for each value type, by implementing specific lifecycle methods, such\n", @@ -65,7 +65,7 @@ "First, let's clarify some terminology:\n", "\n", "- The \"lifecycle\" of a value is defined by various [dunder\n", - "methods](/mojo/manual/structs.html#special-methods) in a struct.\n", + "methods](/mojo/manual/structs#special-methods) in a struct.\n", "Each lifecycle event is handled by a different method,\n", "such as the constructor (`__init__()`), the destructor (`__del__()`), the copy\n", "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", @@ -86,8 +86,8 @@ "As you might imagine, keeping track of a value's lifetime can be difficult if a\n", "value is shared across functions many times during the life of a program.\n", "However, Mojo makes this predictable partly through its [value\n", - "semantics](/mojo/manual/values/value-semantics.html) and [value\n", - "ownership](/mojo/manual/values/ownership.html) (both prerequisite readings for\n", + "semantics](/mojo/manual/values/value-semantics) and [value\n", + "ownership](/mojo/manual/values/ownership) (both prerequisite readings for\n", "the following sections). The final piece of the puzzle for lifetime management\n", "is the value lifecycle: every value (defined in a struct) needs to implement\n", "key lifecycle methods that define how a value is created and destroyed." diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index a8d0d00b4c..0a0a599315 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -23,7 +23,7 @@ "up until the value is last used, at which point Mojo destroys it. This page\n", "describes how every value in Mojo is created, copied, and moved. (The next\n", "page describes [how values are\n", - "destroyed](/mojo/manual/lifecycle/death.html).)\n", + "destroyed](/mojo/manual/lifecycle/death).)\n", "\n", "All data types in Mojo—including basic types in the standard library such as\n", "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", @@ -31,7 +31,7 @@ "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", - "[struct](/mojo/manual/structs.html). This means the creation and\n", + "[struct](/mojo/manual/structs). This means the creation and\n", "destruction of any piece of data follows the same lifecycle rules, and you can\n", "define your own data types that work exactly the same way.\n", "\n", @@ -130,7 +130,7 @@ "metadata": {}, "source": [ "An instance of `MyPet` can also be\n", - "[borrowed](/mojo/manual/values/ownership.html#immutable-arguments-borrowed)\n", + "[borrowed](/mojo/manual/values/ownership#immutable-arguments-borrowed)\n", "and destroyed, but it currently can't be copied or moved.\n", "\n", "We believe this is a good default starting point, because there are no built-in\n", @@ -143,9 +143,9 @@ "Mojo does not require a destructor to destroy an object. As long as\n", "all fields in the struct are destructible (every type in the standard library\n", "is destructible, except for\n", - "[pointers](/mojo/stdlib/memory/unsafe.html)), then Mojo knows how to destroy\n", + "[pointers](/mojo/stdlib/memory/unsafe)), then Mojo knows how to destroy\n", "the type when its lifetime ends. We'll discuss that more in [Death of a\n", - "value](/mojo/manual/lifecycle/death.html).\n", + "value](/mojo/manual/lifecycle/death).\n", "\n", ":::" ] @@ -157,14 +157,14 @@ "### Overloading the constructor\n", "\n", "Like any other function/method, you can\n", - "[overload](/mojo/manual/functions.html#overloaded-functions) the\n", + "[overload](/mojo/manual/functions#overloaded-functions) the\n", "`__init__()` constructor to initialize the object with different arguments. For\n", "example, you might want a default constructor that sets some default values and\n", "takes no arguments, and then additional constructors that accept more arguments.\n", "\n", "Just be aware that, in order to modify any fields, each constructor must\n", "declare the `self` argument with the [`inout`\n", - "convention](/mojo/manual/values/ownership.html#mutable-arguments-inout). If you\n", + "convention](/mojo/manual/values/ownership#mutable-arguments-inout). If you\n", "want to call one constructor from another, you simply call upon that\n", "constructor as you would externally (you don't need to pass `self`).\n", "\n", @@ -384,7 +384,7 @@ "\n", "Also, notice that the `existing` argument in `__copyinit__()` is immutable\n", "because the default [argument\n", - "convention](/mojo/manual/values/ownership.html#argument-conventions) in an `fn`\n", + "convention](/mojo/manual/values/ownership#argument-conventions) in an `fn`\n", "function is `borrowed`—this is a good thing because this function should not\n", "modify the contents of the value being copied.\n", "\n", @@ -414,7 +414,7 @@ "source": [ "What makes Mojo's copy behavior different, compared to other languages, is that\n", "`__copyinit__()` is designed to perform a deep copy of all fields in the type\n", - "(as per [value semantics](/mojo/manual/values/value-semantics.html)). That is,\n", + "(as per [value semantics](/mojo/manual/values/value-semantics)). That is,\n", "it copies heap-allocated values, rather than just copying the pointer.\n", "\n", "However, the Mojo compiler doesn't enforce this, so it's the type author's\n", @@ -512,7 +512,7 @@ "In `HeapArray`, we must use the `__del__()` destructor to free the\n", "heap-allocated data when the `HeapArray` lifetime ends, but Mojo automatically\n", "destroys all other fields when their respective lifetimes end. We'll discuss\n", - "this destructor more in [Death of a value](/mojo/manual/lifecycle/death.html).\n", + "this destructor more in [Death of a value](/mojo/manual/lifecycle/death).\n", "\n", ":::" ] @@ -524,7 +524,7 @@ "If your type doesn't use any pointers for heap-allocated data, then writing the\n", "constructor and copy constructor is all boilerplate code that you shouldn't\n", "have to write. For most structs that don't manage memory explicitly, you can \n", - "just add the [`@value` decorator](/mojo/manual/decorators/value.html) to your\n", + "just add the [`@value` decorator](/mojo/manual/decorators/value) to your\n", "struct definition and Mojo will synthesize the `__init__()`, `__copyinit__()`,\n", "and `__moveinit__()` methods.\n", "\n", @@ -532,7 +532,7 @@ "\n", "Mojo also calls upon the copy constructor when a value is passed to a\n", "function that takes the argument as\n", - "[`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)\n", + "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", "operator `^`), then Mojo instead invokes the move constructor.\n", @@ -547,7 +547,7 @@ "## Move constructor\n", "\n", "Although copying values provides predictable behavior that matches Mojo's\n", - "[value semantics](/mojo/manual/values/value-semantics.html), copying some data\n", + "[value semantics](/mojo/manual/values/value-semantics), copying some data\n", "types can be a significant hit on performance. If you're familiar with\n", "reference semantics, then the solution here might seem clear: instead of making\n", "a copy when passing a value, share the value as a reference. And if the\n", @@ -558,7 +558,7 @@ "\n", "To support moving a value, implement the `__moveinit__()` method. The \n", "`__moveinit__()` method performs a consuming move: it [transfers\n", - "ownership](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)\n", + "ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", "of a value from one variable to another when the original variable's lifetime\n", "ends (also called a \"destructive move\").\n", "\n", @@ -569,7 +569,7 @@ "the move constructors are only part of the implementation for how Mojo\n", "transfers ownership of a value. You can learn more in the section about\n", "[ownership\n", - "transfer](/mojo/manual/values/ownership.html#transfer-arguments-owned-and).\n", + "transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and).\n", "\n", ":::" ] @@ -640,7 +640,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)).\n", + "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", @@ -693,7 +693,7 @@ "integers, floats, and booleans, is very cheap. Yet, if you allow your type to\n", "be copied, then there's generally no reason to disallow moves, so you can\n", "synthesize both constructors by adding the [`@value`\n", - "decorator](/mojo/manual/decorators/value.html).\n", + "decorator](/mojo/manual/decorators/value).\n", "\n", ":::" ] @@ -705,13 +705,13 @@ "## Simple value types {#value-decorator}\n", "\n", "Because copy and move constructors are opt-in, Mojo provides great control for\n", - "exotic usecases (such as for atomic values that should never be copied or\n", + "exotic use cases (such as for atomic values that should never be copied or\n", "moved), but most structs are simple aggregations of other types that should be\n", "easily copied and moved, and we don't want to write a lot of boilerplate\n", "constructors for those simple value types.\n", "\n", "To solve this, Mojo provides the [`@value`\n", - "decorator](/mojo/manual/decorators/value.html), which synthesizes the\n", + "decorator](/mojo/manual/decorators/value), which synthesizes the\n", "boilerplate code for the `__init__()`, `__copyinit__()`, and `__moveinit__()`\n", "methods.\n", "\n", @@ -825,7 +825,7 @@ "Also notice that the `MyPet` struct above doesn't include the `__del__()`\n", "destructor (the `@value` decorator does not synthesize this), because Mojo\n", "doesn't need it to destroy fields, as discussed in [Death of a\n", - "value](/mojo/manual/lifecycle/death.html)\n", + "value](/mojo/manual/lifecycle/death)\n", "\n", ":::" ] @@ -871,7 +871,7 @@ "it is safe to ignore for general application-level code.\n", "\n", "For more information, see the [`@register_passable`\n", - "documentation](/mojo/manual/decorators/register-passable.html).\n", + "documentation](/mojo/manual/decorators/register-passable).\n", "\n", ":::note TODO\n", "\n", diff --git a/docs/manual/packages.md b/docs/manual/packages.md index aec79c6583..f9a146f37e 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -9,7 +9,7 @@ Mojo provides a packaging system that allows you to organize and compile code libraries into importable files. This page introduces the necessary concepts about how to organize your code into modules and packages (which is a lot like Python), and shows you how to create a packaged binary with the [`mojo -package`](/mojo/cli/package.html) command. +package`](/mojo/cli/package) command. ## Mojo modules @@ -93,7 +93,7 @@ from a compiled `.mojopkg`/`.📦` file. It makes no real difference to Mojo which way you import a package. When importing from source files, the directory name works as the package name, whereas when importing from a compiled package, the filename is the package name (which you specify with the [`mojo -package`](/mojo/cli/package.html) command—it can differ from the directory +package`](/mojo/cli/package) command—it can differ from the directory name). For example, consider a project with these files: @@ -200,7 +200,7 @@ from mypackage import MyPair This feature explains why some members in the Mojo standard library can be imported from their package name, while others required the `.` notation. For example, the -[`functional`](/mojo/stdlib/algorithm/functional.html) module resides in the +[`functional`](/mojo/stdlib/algorithm/functional/) module resides in the `algorithm` package, so you can import members of that module (such as the `map()` function) like this: @@ -227,6 +227,6 @@ from algorithm import map Which modules in the standard library are imported to the package scope varies, and is subject to change. Refer to the [documentation for each -module](/mojo/lib.html) to see how you can import its members. +module](/mojo/lib) to see how you can import its members. ::: diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 2b38241046..14df3b8a5e 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -56,7 +56,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The [`@parameter`](/mojo/manual/decorators/parameter.html) directive shown here \n", + "The [`@parameter`](/mojo/manual/decorators/parameter) directive shown here \n", "causes the `for` loop to be evaluated at compile time. The directive only works\n", "if the loop limits are compile-time constants. Since `count` is a parameter,\n", "`range(count)` can be calculated at compile time.\n", @@ -134,7 +134,7 @@ "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n", "but not generic. A generic function or struct is parameterized on _type_. For\n", "example, we could rewrite `repeat[]()` to take any type of argument that\n", - "conforms to the [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) trait: " + "conforms to the [`Stringable`](/mojo/stdlib/builtin/str#stringable) trait: " ] }, { @@ -258,7 +258,7 @@ "This struct has a single parameter, `ElementType`, which is a placeholder for\n", "the data type you want to store in the array, sometimes called a _type\n", "parameter_. `ElementType` is typed as\n", - "[`CollectionElement`](/mojo/stdlib/builtin/type_aliases.html), which is a\n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement), which is a\n", "[trait](/mojo/manual/traits) representing any type that can be copied and moved.\n", "\n", "As with parameterized functions, you need to pass in parameter values when you\n", @@ -1083,7 +1083,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter.html) decorator to create a parametric if condition, which is an `if` statement that\n", + "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that\n", "runs at compile-time. It requires that its condition be a valid parameter\n", "expression, and ensures that only the live branch of the `if` statement is\n", "compiled into the program. (This is similar to use of the `@parameter` decorator\n", @@ -1260,9 +1260,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here, `Bytes` is a type alias for a `SIMD` vector of bytes. The underscore `_`\n", - "in the parameter list indicates that the second parameter, `width`, is unbound.\n", - "You specify the `width` parameter later, when you use `Bytes`.\n", + "Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The\n", + "underscore `_` in the parameter list indicates that the second parameter,\n", + "`V` (the value type), is unbound.\n", + "You specify the `V` parameter later, when you use `StringKeyDict`.\n", "\n", "For example, given the following type:" ] @@ -1740,9 +1741,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Mojo Source", + "display_name": "Mojo", "language": "mojo", - "name": "mojo-source-kernel" + "name": "mojo-jupyter-kernel" }, "language_info": { "codemirror_mode": { diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 07c2d6e575..a893a9a84c 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -438,7 +438,7 @@ "\n", "As mentioned in [Allocating memory](#allocating-memory), you can use an \n", "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n", - "in as single, contiguous block. Pointers support arithmetic: adding an integer\n", + "as a single, contiguous block. Pointers support arithmetic: adding an integer\n", "to a pointer returns a new pointer offset by the specified number of values from\n", "the original pointer:\n", "\n", @@ -576,7 +576,7 @@ "source": [ "## `DTypePointer`: handling numeric data\n", "\n", - "The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", + "A [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", "pointer that supports some additional methods for loading and storing numeric\n", "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n", "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n", @@ -647,7 +647,7 @@ "and \n", "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n", "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", - "that this function only handles images with where the number of pixels is evenly\n", + "that this function only handles images where the number of pixels is evenly\n", "divisible by eight.)" ] }, diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 94d6ab5f2a..6e414cc178 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -101,7 +101,7 @@ " the function signature. You'll also see this when calling Python functions\n", " that may raise exceptions. (Raising exceptions is much more common in Python\n", " code than in the Mojo standard library, which \n", - " [limits their use for performance reasons](/mojo/roadmap.html#the-standard-library-has-limited-exceptions-use).)\n", + " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", "\n", ":::note\n", "\n", diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index 2067504437..ba46521ccd 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -34,8 +34,8 @@ "example, all the data types in Mojo's standard library (such as `Int`,\n", "`Bool`, `String`, and `Tuple`) are defined as structs.\n", "\n", - "If you understand how [functions](/mojo/manual/functions.html) and\n", - "[variables](/mojo/manual/variables.html) work in Mojo, you probably\n", + "If you understand how [functions](/mojo/manual/functions) and\n", + "[variables](/mojo/manual/variables) work in Mojo, you probably\n", "noticed that Mojo is designed to provide dynamic programming features in a\n", "`def` function while enforcing stronger code safety in `fn` functions. When it\n", "comes to structs, Mojo leans toward the safe side: You can still choose whether\n", @@ -92,7 +92,7 @@ "source": [ "Notice that the first argument in the `__init__()` method is `inout self`. For\n", "now, ignore `inout` (it's an [argument\n", - "convention](/mojo/manual/values/ownership.html#argument-conventions) that\n", + "convention](/mojo/manual/values/ownership#argument-conventions) that\n", "declares `self` as a mutable reference); all you need to know right now is that\n", "`self` must be the first argument. It references the current struct instance\n", "(it allows code in the method to refer to \"itself\"). *When you call the\n", @@ -283,7 +283,7 @@ "while being safe and easy to use.\n", "\n", "- Mojo structs do not support inheritance (\"sub-classing\"), but a struct can\n", - " implement [traits](/mojo/manual/traits.html).\n", + " implement [traits](/mojo/manual/traits).\n", "\n", "- Python classes support class attributes—values that are shared by all\n", " instances of the class, equivalent to class variables or static data members\n", @@ -337,7 +337,7 @@ "\n", "Mojo supports a long list of special methods; far too many to discuss here, but\n", "they generally match all of [Python's special\n", - "methods](https://docs.python.org/3/reference/datamodel.html#special-method-names)\n", + "methods](https://docs.python.org/3/reference/datamodel#special-method-names)\n", "and they usually accomplish one of two types of tasks:\n", "\n", "- Operator overloading: A lot of special methods are designed to overload\n", @@ -367,7 +367,7 @@ "source": [ "### `@value` decorator\n", "\n", - "When you add the [`@value` decorator](/mojo/manual/decorators/value.html) to a\n", + "When you add the [`@value` decorator](/mojo/manual/decorators/value) to a\n", "struct, Mojo will synthesize the essential lifecycle methods so your object\n", "provides full value semantics. Specifically, it generates the `__init__()`,\n", "`__copyinit__()`, and `__moveinit__()` methods, which allow you to construct,\n", @@ -460,7 +460,7 @@ "`__init__()`, this code also introduces `owned`, which is another argument\n", "convention that ensures the argument has unique ownership of the value.\n", "For more detail, see the section about [value\n", - "ownership](/mojo/manual/values/ownership.html).\n" + "ownership](/mojo/manual/values/ownership).\n" ] } ], diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 03a38fd176..2085c49e29 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -68,7 +68,7 @@ "you're passing it, only the fact that they implement the `quack()` method.\n", "\n", "In a statically-typed environment, this approach doesn't work:\n", - "[`fn` functions](/mojo/manual/functions.html#fn-functions) require you to\n", + "[`fn` functions](/mojo/manual/functions#fn-functions) require you to\n", "specify the type of each argument. If you wanted to write this example in Mojo \n", "_without_ traits, you'd need to write a function overload for each input type.\n", "All of the examples from here on are in Mojo, so we'll just call the function\n", @@ -208,7 +208,7 @@ "metadata": {}, "source": [ "This syntax may look a little unfamiliar if you haven't dealt with Mojo\n", - "[parameters](/mojo/manual/parameters/index.html) before. What this signature\n", + "[parameters](/mojo/manual/parameters/) before. What this signature\n", "means is that `maybe_a_duck` is an argument of type `T`, where `T` is a type\n", "that must conform to the `Quackable` trait. TODO: This syntax is a little \n", "verbose, and we hope to make it more ergonomic in a future release.\n", @@ -378,7 +378,7 @@ "`MassProducible` type has a default (no-argument) constructor and can be moved.\n", "It uses the built-in [`Movable`](/mojo/stdlib/builtin/value/Movable) trait,\n", "which requires the type to have a [move \n", - "constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", + "constructor](/mojo/manual/lifecycle/life#move-constructor).\n", "\n", "The `factory[]()` function returns a newly-constructed instance of a \n", "`MassProducible` type." @@ -415,7 +415,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable.html#register_passabletrivial) \n", + "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable#register_passabletrivial) \n", "types have restrictions on their lifecycle methods: they can't define copy or\n", "move constructors, because they don't require any custom logic.\n", "\n", @@ -441,14 +441,21 @@ "by a number of standard library types, and you can also implement these on your\n", "own types:\n", "\n", + " - [`Absable`](/mojo/stdlib/builtin/math/Absable)\n", " - [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType)\n", " - [`Boolable`](/mojo/stdlib/builtin/bool/Boolable)\n", + " - [`BoolableCollectionElement`](/mojo/stdlib/builtin/value/BoolableCollectionElement)\n", + " - [`BoolableKeyElement`](/mojo/stdlib/builtin/value/BoolableKeyElement)\n", " - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", + " - [`Powable`](/mojo/stdlib/builtin/math/Powable)\n", + " - [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", + " - [`RepresentableCollectionElement`](/mojo/stdlib/builtin/value/RepresentableCollectionElement)\n", + " - [`RepresentableKeyElement`](/mojo/stdlib/collections/dict/RepresentableKeyElement)\n", " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", "\n", @@ -554,7 +561,7 @@ "When building a generic container type, one challenge is knowing how to dispose\n", "of the contained items when the container is destroyed. Any type that \n", "dynamically allocates memory needs to supply a \n", - "[destructor](/mojo/manual/lifecycle/death.html#destructor) (`__del__()` method)\n", + "[destructor](/mojo/manual/lifecycle/death#destructor) (`__del__()` method)\n", "that must be called to free the allocated memory. But not all types have a \n", "destructor, and your Mojo code has no way to determine which is which.\n", "\n", @@ -582,7 +589,7 @@ "to be able to identify the types at compile time. For example, if the container\n", "needs to copy a value, the compiler needs to verify that the type can be copied.\n", "\n", - "The [`List`](/mojo/stdlib/collections/list.html) type is an example of a\n", + "The [`List`](/mojo/stdlib/collections/list) type is an example of a\n", "generic container. A single `List` can only hold a single type of data.\n", "For example, you can create a list of integer values like this:" ] @@ -616,8 +623,8 @@ "container. For example, `List` requires elements that can be moved and\n", "copied. To store a struct in a `List`, the struct needs to conform to\n", "the `CollectionElement` trait, which requires a \n", - "[copy constructor](/mojo/manual/lifecycle/life.html#copy-constructor) and a \n", - "[move constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", + "[copy constructor](/mojo/manual/lifecycle/life#copy-constructor) and a \n", + "[move constructor](/mojo/manual/lifecycle/life#move-constructor).\n", "\n", "Building generic containers is an advanced topic. For an introduction, see the\n", "section on \n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 1aac8f6ccb..db01ab1928 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -131,7 +131,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -204,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -248,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -265,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -333,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -381,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -404,16 +404,13 @@ "source": [ "Most standard library types conform to the \n", "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", - "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", - "explicitly convert a value to a string.\n", - "\n", - "When concatenating values to a string using the `+` operator, `Stringable` types\n", - "implicitly convert to `String`:" + "a type that can be converted to a string. Use `str(value)` to\n", + "explicitly convert a value to a string:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -425,7 +422,7 @@ } ], "source": [ - "var s = str(\"Items in list: \") + 5\n", + "var s = str(\"Items in list: \") + str(5)\n", "print(s)" ] }, @@ -473,7 +470,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -487,7 +484,7 @@ "source": [ "# print(\"Strings play nicely with others: \" + True)\n", "# Error: ... right hand side cannot be converted from Bool to StringLiteral\n", - "print(str(\"Strings play nicely with others: \") + True)" + "print(str(\"Strings play nicely with others: \") + str(True))" ] }, { @@ -505,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -560,7 +557,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -579,7 +576,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -623,7 +620,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -685,7 +682,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -714,7 +711,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -760,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -801,7 +798,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -840,7 +837,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -866,7 +863,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -881,7 +878,7 @@ "var opt: Optional[String] = str(\"Testing\")\n", "if opt:\n", " var value_ref = opt.value()\n", - " print(value_ref[])" + " print(value_ref)" ] }, { @@ -895,7 +892,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 103a77717e..7fbd408e8b 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -102,7 +102,7 @@ "\n", "But before we explain the rules and syntax for Mojo's value ownership model,\n", "you first need to understand [value\n", - "semantics](/mojo/manual/values/value-semantics.html)." + "semantics](/mojo/manual/values/value-semantics)." ] } ], diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index a92d507b7a..ecf19d7390 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -29,7 +29,7 @@ "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", "each value at a time, while still allowing you to share references with other\n", "functions. When the lifetime of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death.html).\n", + "value](/mojo/manual/lifecycle/death).\n", "\n", "On this page, we'll explain the rules that govern this ownership model and how\n", "to specify different argument conventions that define how values are shared into\n", @@ -49,7 +49,7 @@ "performance, and safety of the language.\n", "\n", "In Mojo, we want to provide full [value\n", - "semantics](/mojo/manual/values/value-semantics.html) by default, which provides\n", + "semantics](/mojo/manual/values/value-semantics) by default, which provides\n", "consistent and predictable behavior. But as a systems programming language, we\n", "also need to offer full control over memory optimizations, which generally\n", "requires reference semantics. The trick is to introduce reference semantics in\n", @@ -104,7 +104,7 @@ "functions treat `borrowed` arguments somewhat differently:\n", "\n", "\n", - "- In an [`fn` function](/mojo/manual/functions.html#fn-functions), the function\n", + "- In an [`fn` function](/mojo/manual/functions#fn-functions), the function\n", " always receives an immutable reference. If you want a mutable copy, you can\n", " assign it to a local variable:\n", "\n", @@ -112,7 +112,7 @@ " var my_copy = borrowed_arg\n", " ```\n", "\n", - "- In a [`def` function](/mojo/manual/functions.html#def-functions), if the \n", + "- In a [`def` function](/mojo/manual/functions#def-functions), if the \n", " function mutates the value, the function receives a mutable copy of the \n", " argument. Otherwise, it receives an immutable reference. This allows you to\n", " treat arguments as mutable, but avoid the overhead of making extra copies when\n", @@ -166,7 +166,7 @@ "references are and aren't valid (an invalid reference is one whose lifetime has\n", "ended, perhaps because the value ownership was transferred). Importantly, this\n", "logic allows Mojo to immediately [destroy\n", - "values](/mojo/manual/lifecycle/death.html) when their lifetime ends.\n", + "values](/mojo/manual/lifecycle/death) when their lifetime ends.\n", "\n", "-->" ] @@ -340,7 +340,7 @@ ":::note\n", "\n", "You cannot define [default\n", - "values](/mojo/manual/functions.html#optional-arguments) for `inout`\n", + "values](/mojo/manual/functions#optional-arguments) for `inout`\n", "arguments.\n", "\n", ":::" @@ -362,7 +362,7 @@ "Technically, the `owned` keyword does not guarantee that the received value is\n", "_the original value_—it guarantees only that the function\n", "gets unique ownership of a value (enforcing [value\n", - "semantics](/mojo/manual/values/value-semantics.html)). This happens in one of\n", + "semantics](/mojo/manual/values/value-semantics)). This happens in one of\n", "three ways:\n", "\n", "- The caller passes the argument with the `^` transfer operator, which ends the\n", @@ -489,7 +489,7 @@ "making a copy:\n", "\n", "- If a type implements the [move\n", - " constructor](/mojo/manual/lifecycle/life.html#consuming-move-constructor),\n", + " constructor](/mojo/manual/lifecycle/life#consuming-move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", " transferred into a function as an `owned` argument, _and_ the original value's\n", " lifetime ends at the same point (with or without use of the `^` transfer\n", @@ -509,7 +509,7 @@ "## Comparing `def` and `fn` argument conventions\n", "\n", "As mentioned in the section about\n", - "[functions](/mojo/manual/functions.html), `def` and `fn` functions\n", + "[functions](/mojo/manual/functions), `def` and `fn` functions\n", "are interchangeable, as far as a caller is concerned, and they can both\n", "accomplish the same things. It's only the inside that differs, and Mojo's `def`\n", "function is essentially just sugaring for the `fn` function:\n", diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index 5ece6cbdb8..903cabd474 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -27,7 +27,7 @@ "provides tight controls for reference semantics that avoid memory errors.\n", "\n", "The controls over reference semantics are provided by the [value ownership\n", - "model](/mojo/manual/values/ownership.html), but before we get into the syntax\n", + "model](/mojo/manual/values/ownership), but before we get into the syntax\n", "and rules for that, it's important that you understand the principles of value\n", "semantics. Generally, it means that each variable has unique access to a value,\n", "and any code outside the scope of that variable cannot modify its value." @@ -186,7 +186,7 @@ "metadata": {}, "source": [ "The arguments above are mutable because a [`def`\n", - "function](/mojo/manual/functions.html#def-functions) has special treatment for\n", + "function](/mojo/manual/functions#def-functions) has special treatment for\n", "the default\n", "[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions).\n", "\n", diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 7115d3a5f5..7cac974ebd 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -23,12 +23,12 @@ "A variable is a name that holds a value or object. All variables in Mojo are \n", "mutable—their value can be changed. (If you want to define a constant value that\n", "can't change at runtime, see the \n", - "[`alias` keyword](/mojo/manual/parameters/index.html#alias-named-parameter-expressions).)\n", + "[`alias` keyword](/mojo/manual/parameters/#alias-named-parameter-expressions).)\n", "\n", "Mojo has two kinds of variables:\n", "\n", "- Declared variables are created with the `var` keyword, and may include\n", - " [type annotations](#type-annotation).\n", + " [type annotations](#type-annotations).\n", "\n", " ```mojo\n", " var a = 5\n", @@ -80,16 +80,7 @@ "source": [ "In this example, the `temperature` variable is explicitly typed as `Float64`,\n", "but assigned an integer value, so the value is implicitly converted to a \n", - "`Float64`. \n", - "\n", - ":::note\n", - "\n", - "Mojo formerly supported the `let` keyword for declaring immutable variables.\n", - "This has been removed to simplify the language, and for other reasons\n", - "[discussed\n", - "elsewhere](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md).\n", - "\n", - ":::" + "`Float64`. " ] }, { @@ -142,24 +133,12 @@ "source": [ "## Declared variables\n", "\n", - "You can declare a variable with the `var` keyword. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ + "You can declare a variable with the `var` keyword. For example:\n", + "\n", + "```mojo\n", "var name = str(\"Sam\")\n", - "var user_id: Int" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ + "var user_id: Int\n", + "```\n", "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", "declared values are typed—either explicitly with a \n", @@ -373,7 +352,7 @@ "is lossless.\n", "\n", "Implicit conversion follows the logic of [overloaded\n", - "functions](/mojo/manual/functions.html#overloaded-functions). If the destination\n", + "functions](/mojo/manual/functions#overloaded-functions). If the destination\n", "type has a single-argument constructor that takes an argument of the source\n", "type, it can be invoked for implicit conversion. \n", "\n", diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md deleted file mode 100644 index 613652d45c..0000000000 --- a/docs/upgrade-guide.md +++ /dev/null @@ -1,219 +0,0 @@ ---- -title: Mojo🔥 upgrade guide -sidebar_label: Upgrade guide -description: Shows how to upgrade your Mojo programs from one release to the next. ---- - -The following update steps are ordered from most to least common. If you have -compiler warnings or errors, you can go down the list and try recompiling your -program after each item. - -## Upgrading Mojo 0.7 to 24.1 - -### Rename `let` to `var` - -There is now a compiler warning to change the `let` keyword to `var`. But `let` -is a common word that you might mistakenly overwrite a comment or doc string. VS -Code has a nice `find and replace` feature to visualize the changes before -making them. - -First click the search icon and select the icon for Match Whole Word `[ab]`, -then click the arrow to expand the replace bar. Put `let` in the first bar and -`var` in the second, then press enter. You can change one file at a time by -pressing the `Replace All` button next to each file: - -![let to var](./images/let-to-var.png) - -### `vectorize()` and `unroll()` signature change - -The `vectorize()` function now has the ability to add an `unroll_factor` -parameter to unroll the loop. This will often be used as a keyword parameter for -clarity like this: `vectorize[fn, simd_width, unroll_factor=2](size)`. The `fn` -parameter was moved to the front of the signature to accommodate this. - -You can update old code using the find and replace regex syntax to swap the -parameters: - -```re -vectorize\[(.*?), (.*?)\] -vectorize[$2, $1] -``` - -In VS Code back in search, you can press the `.*` icon and paste regex to fix -the signatures: - -![vectorize fix](./images/vectorize-fix.png) - -Similarly, `unroll` has changed to match the vectorize signature, which can be -updated in the same way with the following regex: - -```re -unroll\[(.*?), (.*?)\] -unroll[$2, $1] -``` - -### `vectorize_unroll()` function removed - -You must change `vectorize_unroll[width, unroll_factor, fn](size)` to -`vectorize[fn, width, unroll_factor](size)`. The regex for this is: - -```re -vectorize_unroll\[(.*?), (.*?), (.*?)\] -vectorize[$3, $1, $2] -``` - -### `DynamicVector` constructor `capacity` now keyword-only - -The [`DynamicVector`](/mojo/stdlib/collections/list/List) struct had -a constructor that took a single integer value for the vector's capacity. This -had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. -This was not intended to support implicit conversion, so `capacity` is now a -keyword-only argument to avoid this. To update your code you can use this regex: - -```re -(DynamicVector\[.*Int\]\()(\w.*\)) -$1capacity=$2 -``` - -Which in VS Code looks like this: - -![DynamicVector capacity](./images/dynamic-vector-capacity.png) - -### `NDBuffer` signature change - -The shape of an -[`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) can -now default to being unknown, so the parameter list has been rearranged to -accommodate this: - -For example this: - -```mojo -var buf = NDBuffer[rank, shape, type, address_space]() -``` - -Becomes this: - -```mojo -var buf = NDBuffer[type, rank, shape, address_space]() -``` - -And the `shape` and `address_space` parameters are now optional: - -```mojo -var buf = NDBuffer[type, rank]() -``` - -The regex to fix this is: - -```re -NDBuffer\[(.*?), (.*?), (.*?), -NDBuffer[$3, $1, $2] -``` - -### Dereference `Variant` with `[]` - -Previously, using [`Variant`](/mojo/stdlib/utils/variant/Variant) -was unsafe with heap allocated objects, it now -returns a reference. If you had code that looks like this: - -```mojo -from utils import Variant - -fn foo(variant: Variant[String, Int]): - if variant.isa[String](): - var s = variant.get[String]() - print(s) - -fn main(): - foo(String("foo")) -``` - -You now need to dereference to get access to the pointed to value with `[]`: - -```mojo - var s = variant.get[String]() - print(s[]) -``` - -Note that dereferencing with `[]` is temporary, Mojo will add auto-dereferencing -in the future so this is invisible to the user. - -### String method changes - -Methods on `String` were changed to match Python naming conventions: - -```mojo -var s = String("Foo") -var lower = s.tolower() -var upper = s.toupper() -``` - -Has become: - -```mojo -var s = String("Foo") -var lower = s.lower() -var upper = s.upper() -``` - -The regex to fix this is: - -```re -\.tolower\(\) -lower() -``` - -And: - -```re -\.toupper\(\) -upper() -``` - -### Implicit parameters for arguments - -Previously you could explicitly specify the implicit parameters of an -[automatically parameterized function](/mojo/manual/parameters/#automatic-parameterization-of-functions) -by passing them in the parameter list: - -```mojo -fn foo(x: SIMD): - print(x) - -fn main(): - foo[DType.int32, 1](SIMD[DType.int32, 1](4)) -``` - -This was not intended to be possible and is now a compiler error. These implicit -parameters are always inferred from the function arguments. For example: - -```mojo -fn foo(x: SIMD): - print(x) - -fn main(): - var v =SIMD[DType.int32, 2](4, 2) - foo(v) -``` - -There is no regex to accommodate all the variations of this, so it needs to be -done manually. - -### Using a trait inside another trait - -This no longer compiles: - -```mojo -trait Foo: - fn foo[T: Bar](self): ... - -trait Bar: ... - -struct MyStruct(Foo): - fn foo[T: Bar](self): ... -``` - -You must use concrete types inside trait methods for now, this was an unintended -regression as our codebase wasn't using this feature, it will be fixed in a -future release. From ab3a374dafece804897908085cb082d2d0d1df10 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 17 Jul 2024 17:28:51 -0700 Subject: [PATCH 0802/2019] [Docs] Debugging updates. - Adds brief sections on the Variables and Call Stack sections of the Run & Debug view. - Adds instructions for starting a debug session from the command line (including attaching to a running process). - Adds a "Tips & tricks" section that mentions the `breakpoint()` built-in and using the `param_env` module to turn debug code on/off using the `-D` command-line option. MODULAR_ORIG_COMMIT_REV_ID: 505b02f7d02d0369e643c4f366818d77f21f8d66 --- docs/tools/debugging.ipynb | 283 ++++++++++++++++-- .../images/debugger-call-stack-nested1.png | Bin 0 -> 105875 bytes docs/tools/images/debugger-variables.png | Bin 0 -> 71527 bytes 3 files changed, 252 insertions(+), 31 deletions(-) create mode 100644 docs/tools/images/debugger-call-stack-nested1.png create mode 100644 docs/tools/images/debugger-variables.png diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 216f5cb11e..acfca3ea23 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -31,8 +31,9 @@ "\n", "The Mojo SDK includes the [LLDB debugger](https://lldb.llvm.org/) and a Mojo\n", "LLDB plugin. Together these provide the low-level debugging interface for the\n", - "Mojo extension. You can also use `mojo debug` to start a command-line debugging\n", - "session using LLDB.\n", + "Mojo extension. You can also use the `mojo debug` command to start a \n", + "command-line debugging session using LLDB or to launch a Mojo debugging session\n", + "in VS Code.\n", "\n", "## Start debugging\n", "\n", @@ -103,8 +104,13 @@ "menu to select debug configurations. It also has areas to display current\n", "variables, watch expressions, the current call stack, and breakpoints.\n", "\n", + "
\n", + "\n", "![](images/run-and-debug-view.png)\n", "\n", + "
Figure 1. Run and Debug view
\n", + "
\n", + "\n", "To open **Run and Debug** view, click the **Run and Debug** icon in the\n", "**Activity Bar** (on the left side of the VS Code window) or press\n", "Control+Shift+D \n", @@ -115,14 +121,24 @@ "If you haven't created any launch configurations in the current project,\n", "VS Code shows the **Run start view**.\n", "\n", + "
\n", + "\n", "![](images/run-start-view.png)\n", "\n", + "
Figure 2. Run start view
\n", + "
\n", + "\n", "If you've already launched a debug session or created a `launch.json` file to\n", "define launch configurations, you'll see the **Launch configurations** menu,\n", "which lets you choose configurations and start debug sessions:\n", "\n", + "
\n", + "\n", "![](images/launch-configuration-menu.png)\n", "\n", + "
Figure 3. Launch configurations menu
\n", + "
\n", + "\n", "### Other ways to start a debug session\n", "\n", "There are a number of other ways to start a debug session.\n", @@ -139,7 +155,7 @@ "same debug configurations described in [Quick run or\n", "debug](#quick-run-or-debug).\n", "\n", - "#### Launching from the File Explorer\n", + "#### Launch from the File Explorer\n", "\n", "To launch a debug session from the the **File Explorer** view:\n", "\n", @@ -163,28 +179,79 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Edit launch configurations\n", + "## Starting the debugger from the command line\n", "\n", - "To edit launch configurations:\n", + "Use the `mojo debug` command to start a debug session from the command line. You\n", + "can choose from two debugging interfaces:\n", "\n", - "1. If the **Run and Debug** view isn't already open, click the **Run and\n", - "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", - "or press Control+Shift+D (Command+Shift+D on macOS).\n", + "- With the `--rpc` flag, `mojo debug` starts an RPC debug session in an external\n", + " editor with an active RPC debug server. If you have VS Code running, this\n", + " starts a debug session in VS Code. \n", "\n", - " ![](images/run-and-debug-icon.png)\n", - " \n", - "1. Create or open the `launch.json` file:\n", - " 1. If you see the **Run start view**, click **create a launch.json file**.\n", - " 1. If you already have launch configurations set up, click the gear icon\n", - " next to the **Launch configurations** menu.\n", - " ![](images/launch-configuration-menu.png)\n", - "1. Select **Mojo** from the list of debuggers.\n", + "- Without the `--rpc` flag, `mojo debug` starts a command-line [LLDB \n", + " debugger](https://lldb.llvm.org/) session.\n", "\n", - "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", - "some common debug actions. Click **Add configuration** to add a new\n", - "configuration template. \n", + "You can choose to build and debug a Mojo file, run and debug a compiled binary,\n", + "or to attach the debugger to a running process.\n", + "\n", + ":::note Environment variables\n", + "\n", + "When you debug a program from the command line using `--rpc`, the program runs\n", + "with the environment variables set in the terminal. When launching from inside\n", + "VS Code, the environment is defined by the VS Code [launch \n", + "configuration](#launch-configurations).\n", + "\n", + ":::\n", + "\n", + "For a full list of command-line options, see the [`mojo debug` reference\n", + "page](/mojo/cli/debug).\n", + "\n", + "### Start a debug session from the command line\n", + "\n", + "With VS Code open, run the following command (either from VS Code's integrated\n", + "terminal or an external shell):\n", + "\n", + "```bash\n", + "mojo debug --rpc myproject.mojo\n", + "```\n", + "\n", + "Or to debug a compiled binary:\n", "\n", - "### Mojo launch configurations\n", + "```bash\n", + "mojo debug --rpc myproject\n", + "```\n", + "\n", + "\n", + "For best results, build with the `-O0 -g` command-line options when you build a\n", + "binary that you intend to debug—this produces a binary with full debug info.\n", + "(When you call `mojo debug` on a Mojo source file, it includes debug\n", + "information by default.) See the [`mojo build` reference page](/mojo/cli/build)\n", + "for details on compilation options.\n", + "\n", + "### Attach the debugger to a running process from the command line\n", + "\n", + "You can also attach the debugger to a running process by specifying either the\n", + "process ID or process name on the command line:\n", + "\n", + "```bash\n", + "mojo debug --rpc --pid \n", + "```\n", + "\n", + "Or:\n", + "\n", + "```bash\n", + "mojo debug --rpc --process-name \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Launch configurations\n", + "\n", + "VS Code _launch configurations_ let you define setup information for debugging\n", + "your applications.\n", "\n", "The Mojo debugger provides the following launch configuration templates:\n", "\n", @@ -220,22 +287,59 @@ "- `description`. A longer description of the configuration, not shown in the UI.\n", "- `env`. Environment variables to be set before running the program.\n", "- `mojoFile`. Path to a Mojo file to launch and debug.\n", - "- `program`. Path to a compiled binary to launch and debug.\n", + "- `pid`. Process ID of the running process to attach to.\n", + "- `program`. Path to a compiled binary to launch and debug, or the \n", + " program to attach to.\n", "- `runInTerminal`. True to run the program with a dedicated terminal, which\n", " allows the program to receive standard input from the terminal. False to run\n", " the program with its output directed to the **Debug Console**.\n", "\n", - "Mojo `launch` configurations must include either the `mojoFile` or `program`\n", - "attribute.\n", + "If configuration is a `launch` request, the configuration must include either\n", + "the `mojoFile` or `program` attribute.\n", + "\n", + "For `attach` requests, the configuration must include either the `pid` or \n", + "`program` attribute.\n", "\n", "VS Code performs variable substitution on the launch configurations. You can\n", "use `${workspaceFolder}` to substitute the path to the current workspace, and\n", "`${file}` to represent the file in the active editor tab. For a complete list\n", - "of variables, see the [Variables\n", + "of variables, see the VS Code [Variables\n", "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", "\n", - "Note that for launch configurations that launch a Mojo file, there is currently\n", - "no way to specify compilation options.\n" + "For more information, see the VS Code documentation for [Launch \n", + "configurations]](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", + "\n", + ":::note Compilation options\n", + "\n", + "Mojo launch configurations don't allow you to specify compilation options. If\n", + "you need to specify compilation options, you can build the binary using [`mojo\n", + "build`](/mojo/cli/build), then use a launch configuration with the `program`\n", + "option to launch the compiled binary. Or if you [start the debugger from the\n", + "command line](#starting-the-debugger-from-the-command-line), you can pass\n", + "compilation options to the `mojo debug` command.\n", + "\n", + ":::\n", + "\n", + "### Edit launch configurations\n", + "\n", + "To edit launch configurations:\n", + "\n", + "1. If the **Run and Debug** view isn't already open, click the **Run and\n", + "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", + "or press Control+Shift+D (Command+Shift+D on macOS).\n", + "\n", + " ![](images/run-and-debug-icon.png)\n", + " \n", + "1. Create or open the `launch.json` file:\n", + " 1. If you see the **Run start view**, click **create a launch.json file**.\n", + " 1. If you already have launch configurations set up, click the gear icon\n", + " next to the **Launch configurations** menu.\n", + " ![](images/launch-configuration-menu.png)\n", + "1. Select **Mojo** from the list of debuggers.\n", + "\n", + "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", + "some common debug actions. Click **Add configuration** to add a new\n", + "configuration template. " ] }, { @@ -302,16 +406,22 @@ "- **Log Message**. Add a logpoint (supported)\n", "- **Wait for Breakpoint**. Add a triggered breakpoint (supported).\n", "\n", - "#### Hit counts\n", + "#### Set a hit count breakpoint\n", "\n", "A hit count breakpoint is a breakpoint that only breaks execution after the\n", "debugger hits it a specified number of times.\n", "\n", - "To set a hit count for a breakpoint:\n", + "To add a hit count breakpoint:\n", + "\n", + "1. Right click in the left gutter of the editor where you want to place the \n", + " breakpoint, and select **Add Conditional Breakpoint.**\n", + "2. Select **Hit Count** from the menu and enter the desired hit count.\n", "\n", - "- Right click on the breakpoint in the left gutter of the editor and select\n", + "To change an existing breakpoint to a hit count breakpoint:\n", + "\n", + "1. Right click on the breakpoint in the left gutter of the editor and select\n", " **Edit breakpoint**.\n", - "- Select **Hit Count** from the menu and enter the desired hit count.\n", + "2. Select **Hit Count** from the menu and enter the desired hit count.\n", "\n", "You can also edit a breakpoint from the **Breakpoints** section of the **Run and\n", "Debug** view:\n", @@ -326,7 +436,51 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Using the Debug Console\n", + "### View local variables\n", + "\n", + "When a program is paused in the debugger, the editor shows local variable values\n", + "inline. You can also find them in the **Variables** section of the **Run and\n", + "Debug** view.\n", + "\n", + "
\n", + "\n", + "![VS Code window showing a program paused in the debugger, with the variables sections of the Run and Debug view visible. The edit shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2.](images/debugger-variables.png)\n", + "\n", + "
Figure 4. Local variable values displayed in the debugger
\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View the call stack\n", + "\n", + "When a program is paused in the debugger, the **Run and Debug** view shows the\n", + "current call stack. (You may see multiple call stacks, one for each active\n", + "thread in the program.)\n", + "\n", + "
\n", + "\n", + "![VS Code window showing a program paused in the debugger, with the call stack and variables sections of the Run and Debug view visible. The call stack shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2; the parent function nested1 is selected in the call stack, and editor highlights the current line in nested1 (the call to nested2()).](images/debugger-call-stack-nested1.png)\n", + "\n", + "
Figure 5. Call stack in Run and Debug view
\n", + "
\n", + "\n", + "The **Call Stack** section of the Run and Debug view shows a stack frame for\n", + "each function call in the current call stack. Clicking on the name of the\n", + "function highlights the current line in that function. For example, in Figure\n", + "5, the program is paused at a breakpoint in `nested2()`, but the parent\n", + "function, `nested1()` is selected in the call stack. The editor highlights the\n", + "current line in `nested1()` (that is, the call to `nested2()`) and shows the\n", + "current local variable values for `nested1()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use the Debug Console\n", "\n", "The **Debug Console** gives you a command-line interface to the debugger. The \n", "**Debug Console** processes LLDB commands and Mojo expressions.\n", @@ -348,6 +502,73 @@ "\n", ":::" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tips and tricks\n", + "\n", + " There are several features in the standard library that aren't directly related\n", + " to the debugger, but which can help you debug your programs. These include:\n", + "\n", + " * Programmatic breakpoints.\n", + " * Setting parameters from the Mojo command line.\n", + "\n", + " ### Set a programmatic breakpoint\n", + "\n", + " To break at a specific point in your code, you can use the built-in\n", + " [`breakpoint()`](/mojo/stdlib/builtin/breakpoint/breakpoint) function:\n", + "\n", + " ```mojo\n", + "if some_value.is_valid():\n", + " do_the_right_thing()\n", + "else:\n", + " # We should never get here!\n", + " breakpoint()\n", + "```\n", + "\n", + "If you have VS Code open and run this code in debug mode (either using VS Code\n", + "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", + "triggers the debugger.\n", + "\n", + ":::note Assertions\n", + "\n", + "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", + "ways to specify assertions. Assertions also trigger an error, so can open the \n", + "debugger in the same way that a `breakpoint()` call will.\n", + "\n", + ":::\n", + "\n", + "### Set parameters from the Mojo command line\n", + "\n", + "You can use the [`param_env`](/mojo/stdlib/sys/param_env/) module to retrieve\n", + "parameter values specified on the Mojo command line. Among other things, this\n", + "is an easy way to switch debugging logic on and off. For example:\n", + "\n", + "```mojo\n", + "from param_env import is_defined\n", + "\n", + "def some_function_with_issues():\n", + " # ...\n", + " @parameter\n", + " if is_defined[\"DEBUG_ME\"]():\n", + " breakpoint()\n", + "```\n", + "\n", + "To activate this code, use the [`-D` command-line\n", + "option](/mojo/cli/debug#compilation-options) to define `DEBUG_ME`:\n", + "\n", + "```bash\n", + "mojo debug -D DEBUG_ME main.mojo\n", + "```\n", + "\n", + "The `is_defined()` function returns a compile-time true or false value based on\n", + "whether the specified name is defined. Since the `breakpoint()` call is inside a\n", + "[parametric `if` statement](/mojo/manual/decorators/parameter#parametric-if-statement),\n", + "it is only included in the compiled code when the `DEBUG_ME` name is defined on\n", + "the command line." + ] } ], "metadata": { diff --git a/docs/tools/images/debugger-call-stack-nested1.png b/docs/tools/images/debugger-call-stack-nested1.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf33e7dfdeacf7e791c8a2d76fe2e00287b387c GIT binary patch literal 105875 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
e37 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 Date: Wed, 17 Jul 2024 17:29:22 -0700 Subject: [PATCH 0803/2019] [Docs] Fix typo in types.ipynb MODULAR_ORIG_COMMIT_REV_ID: 59634c567e1afb34a1f44cd93133bb81bbcf721d --- docs/manual/types.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index db01ab1928..fe9e1adfcc 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -658,6 +658,7 @@ " ```mojo\n", " # Doesn't work!\n", " var list: List[Int] = [2, 3, 5]\n", + " ```\n", "\n", " But you can use variadic arguments to achieve the same thing:\n", "\n", From 1baa7a8cf2a49c37504f2d891e310b661dc8a045 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 22 Jul 2024 21:41:59 -0700 Subject: [PATCH 0804/2019] Update docs for migration to Docusaurus 3.4 MODULAR_ORIG_COMMIT_REV_ID: ed9b343eb43c6eaa941191f2fabbc519f3c51f61 --- docs/{community.md => community.mdx} | 0 docs/{lib.md => lib.mdx} | 4 ++-- docs/manual/decorators/{index.md => index.mdx} | 2 +- docs/manual/{get-started.md => get-started.mdx} | 0 docs/notebooks/{index.md => index.mdx} | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) rename docs/{community.md => community.mdx} (100%) rename docs/{lib.md => lib.mdx} (84%) rename docs/manual/decorators/{index.md => index.mdx} (98%) rename docs/manual/{get-started.md => get-started.mdx} (100%) rename docs/notebooks/{index.md => index.mdx} (94%) diff --git a/docs/community.md b/docs/community.mdx similarity index 100% rename from docs/community.md rename to docs/community.mdx diff --git a/docs/lib.md b/docs/lib.mdx similarity index 84% rename from docs/lib.md rename to docs/lib.mdx index 93ff45569b..bbbab3104a 100644 --- a/docs/lib.md +++ b/docs/lib.mdx @@ -5,12 +5,12 @@ hide_table_of_contents: true description: A list of all modules in the Mojo standard library. listing: - id: stdlib - contents: "stdlib/*/*/index.md" + contents: 'stdlib/*/*/index.md' type: grid page-size: 99 --- These are all the modules in the Mojo standard library. -:::{#stdlib} +:::🔥#stdlib ::: diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.mdx similarity index 98% rename from docs/manual/decorators/index.md rename to docs/manual/decorators/index.mdx index 4f7f11ee43..f7c7d1a6af 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.mdx @@ -34,5 +34,5 @@ built directly into the compiler. The following pages describe each built-in decorator with examples. -:::{#docs} +:::🔥#docs ::: diff --git a/docs/manual/get-started.md b/docs/manual/get-started.mdx similarity index 100% rename from docs/manual/get-started.md rename to docs/manual/get-started.mdx diff --git a/docs/notebooks/index.md b/docs/notebooks/index.mdx similarity index 94% rename from docs/notebooks/index.md rename to docs/notebooks/index.mdx index 09e76b05fe..636b8e4a25 100644 --- a/docs/notebooks/index.md +++ b/docs/notebooks/index.mdx @@ -13,7 +13,7 @@ listing: - RayTracing.ipynb type: grid grid-columns: 2 - sort: "false" + sort: 'false' --- The following pages are rendered from the Jupyter notebooks that are [available @@ -21,5 +21,5 @@ on GitHub](https://github.com/modularml/mojo/tree/main/examples/notebooks).


-:::{#docs} +:::🔥#docs ::: From 0a295bd3bbfd113847057ea58914f017ad456d51 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 25 Jul 2024 14:52:25 -0700 Subject: [PATCH 0805/2019] update Python min version to 3.9 MODULAR_ORIG_COMMIT_REV_ID: 29883bbf0ea46a14a02ae91099fed4bcc4d27390 --- docs/manual/get-started.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 36e41a6bc9..3a2d5f1c15 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -25,7 +25,7 @@ If you already installed Mojo, see [how to update](#update-mojo). - Apple silicon (M1 or M2 processor) - macOS Ventura (12) or later -- Python 3.8 - 3.11 +- Python 3.9 - 3.11 - Xcode or Xcode Command Line Tools - [Homebrew](https://brew.sh) @@ -37,7 +37,7 @@ If you already installed Mojo, see [how to update](#update-mojo). newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) or AWS Graviton2/3 CPU - Minimum 8 GiB RAM -- Python 3.8 - 3.11 +- Python 3.9 - 3.11 - g++ or clang++ C++ compiler From ba68fdcd05a282a9bef7bc33f31bbd244a6899c0 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 31 Jul 2024 12:12:05 -0700 Subject: [PATCH 0806/2019] Unify the MAX/Mojo install procedure. Installing MAX does not require auth anymore, and it always includes the latest Mojo. The standalone Mojo package will soon be unavailable and Mojo will be available only throu the MAX package, which will simplify the install/update procedure and provide a unified package that's free for everybody to use. MODULAR_ORIG_COMMIT_REV_ID: e7d38aa717aa4fd424dffcc91b33bb6dc89f9c77 --- docs/faq.md | 28 +-- docs/manual/get-started.mdx | 365 ++---------------------------------- 2 files changed, 22 insertions(+), 371 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 49ca3bea84..a05a863fe6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -270,7 +270,11 @@ dashboard](https://www.modular.com/max/performance). ### How can I get access to the SDK? -You can [get the Mojo SDK here](https://developer.modular.com/download)! +Mojo is included with the MAX SDK, which you can [download and use for +free](/max/install). + +Read more about [why Mojo is bundled with +MAX](/max/faq#why-bundle-mojo-with-max). ### Is the Mojo Playground still available? @@ -303,14 +307,6 @@ and does not require login. Please read the [Mojo SDK License Terms](https://www.modular.com/legal/mojo). -### What does the Mojo SDK ship with? - -The Mojo SDK includes the Mojo standard library and `mojo` command-line tool, -which provides a REPL similar to the `python` command, along with `build`, -`run`, `package`, `doc` and `format` commands. We've also published a [Mojo -language extension for VS -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - ### What operating systems are supported? Currently, we support Ubuntu Linux 20.04/22.04 (64-bit x86) and macOS (Apple @@ -383,20 +379,6 @@ Please join the [Mojo Discord channel](http://discord.gg/modular) for notifications and [sign up for our newsletter](https://www.modular.com/newsletter) for more coarse-grain updates. -## Mojo Playground {#mojo-playground} - -### What sort of computer is backing each instance in the Mojo Playground? - -The Mojo Playground runs on a fleet of [AWS EC2 -C6i](https://aws.amazon.com/ec2/instance-types/c6i/) (c6i.8xlarge) instances -that is divided among active users. Due to the shared nature of the system, the -number of vCPU cores provided to your session may vary. We guarantee 1 vCPU -core per session, but that may increase when the total number of active users is -low. - -Each user also has a dedicated volume in which you can save your own files that -persist across sessions. - ## Open Source ### Will Mojo be open-sourced? diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 3a2d5f1c15..265ea29bf5 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -7,313 +7,18 @@ description: Install Mojo now and start developing import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -On this page, we'll show you how to install Mojo and create the classic "Hello +On this page, we'll show you how to create the classic "Hello world" starter program with Mojo, in three different ways. If you'd rather read how to write Mojo code beyond just printing text, see the [introduction to Mojo](/mojo/manual/basics). -:::tip Updating? - -If you already installed Mojo, see [how to update](#update-mojo). - -::: - -## Requirements - - - - -- Apple silicon (M1 or M2 processor) -- macOS Ventura (12) or later -- Python 3.9 - 3.11 -- Xcode or Xcode Command Line Tools -- [Homebrew](https://brew.sh) - - - - -- Ubuntu 20.04/22.04 LTS -- x86-64 CPU (with [SSE4.2 or - newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) - or AWS Graviton2/3 CPU -- Minimum 8 GiB RAM -- Python 3.9 - 3.11 -- g++ or clang++ C++ compiler - - - - -Windows support is still in development. - -In the meantime, you can use Mojo on Windows [with -WSL](https://learn.microsoft.com/en-us/windows/wsl/install), using a compatible -version of Ubuntu (see our requirements for Linux). - - - - ## 1. Install Mojo -If you already [installed MAX](/max/install), you can [skip to the next -section](#2-run-code-in-the-repl) because MAX includes Mojo. - -The Mojo SDK is available as either a stable build or a nightly build. -We strive to release stable builds once a month and release nightly builds as -often as possible (not necessarily every day). - - -{/*############################*/} -{/*#### STABLE BUILD SETUP ####*/} -{/*############################*/} - - -1. Open a terminal and install the [`modular`](/cli/) command line tool with - this helper script: - - ```sh - curl -s https://get.modular.com | sh - - ``` - -
- - Or, click here to see the manual install commands. - -
- - - - - ```sh - brew update && brew install modularml/packages/modular - ``` - - - - - ```sh - apt-get install -y apt-transport-https && - keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && - apt-get update && - apt-get install -y modular - ``` - - - - -
-
- -2. Create a virtual environment: - - Because Mojo interoperates with Python, - it's important to define a predictable Python version and package library to - use. We suggest you do that with either venv or conda: - - - - - For most users, we recommend venv (it's included with Python): - - ```sh - python3 -m venv mojo-venv && source mojo-venv/bin/activate - ``` - - - - - Only if you already use conda as your preferred environment, we suggest you - use that: - - ```sh - conda create -n mojo python=3.10 -y && conda activate mojo - ``` - - - - -3. Install the Mojo SDK: - - ```sh - modular install mojo - ``` - -4. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: - - - - - If you're using Bash, run this command: - - ```sh - MOJO_PATH=$(modular config mojo.path) \ - && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ - && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> "$BASHRC" \ - && source "$BASHRC" - ``` - - - - - If you're using ZSH, run this command: - - ```sh - MOJO_PATH=$(modular config mojo.path) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ - && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> ~/.zshrc \ - && source ~/.zshrc - ``` - - - - - If you're using fish, run this command: - - ```sh - set MOJO_PATH (modular config mojo.path) \ - && set -Ux MODULAR_HOME $HOME/.modular \ - && fish_add_path $MOJO_PATH/bin - ``` - - - - -
-{/*############################*/} -{/*### NIGHTLY BUILD SETUP ####*/} -{/*############################*/} - - -:::caution - -Nightly builds are not fully tested. They might include incomplete features, -performance regressions, and new bugs. When using code from the -[mojo](https://github.com/modularml/mojo) GitHub repo, be sure you checkout -the `nightly` branch, because the `main` branch might not be compatible with -nightly builds. - -::: - -1. Open a terminal and install the [`modular`](/cli/) command line tool with - this helper script (this is the same for stable and nightly builds): - - ```sh - curl -s https://get.modular.com | sh - - ``` - -
- - Or, click here to see the manual install commands. - -
- - - - - ```sh - brew update && brew install modularml/packages/modular - ``` - - - - - ```sh - apt-get install -y apt-transport-https && - keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && - apt-get update && - apt-get install -y modular - ``` - - - - -
-
- -2. Create a virtual environment for nightly builds: - - Because Mojo interoperates with Python, - it's important to define a predictable Python version and package library to - use. We suggest you do that with either venv or conda: - - - - - For most users, we recommend venv (it's included with Python): - - ```sh - python3 -m venv mojo-nightly-venv && source mojo-nightly-venv/bin/activate - ``` - - - - - Only if you already use conda as your preferred environment, we suggest you - use that: - - ```sh - conda create -n mojo-nightly python=3.10 -y && conda activate mojo-nightly - ``` - - - - -3. Install the nightly Mojo SDK: - - ```sh - modular install nightly/mojo - ``` - -4. Set environment variables so you can access the nightly [`mojo`](/mojo/cli/) - CLI: - - - - - If you're using Bash, run this command: - - ```sh - MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ - && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ - && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> "$BASHRC" \ - && source "$BASHRC" - ``` +Mojo is now bundled with MAX, which provides everything you need to compile, +run, debug, and package Mojo code. (Read [why we bundled Mojo with +MAX](/max/faq#why-bundle-mojo-with-max).) - - - - If you're using ZSH, run this command: - - ```sh - MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ - && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> ~/.zshrc \ - && source ~/.zshrc - ``` - - - - - If you're using fish, run this command: - - ```sh - set MOJO_NIGHTLY_PATH (modular config mojo-nightly.path) \ - && set -Ux MODULAR_HOME $HOME/.modular \ - && fish_add_path $MOJO_NIGHTLY_PATH/bin - ``` - - - - -
-
- -Now you're ready to go. +Follow the guide to [install MAX & Mojo](/max/install), and then return here. ## 2. Run code in the REPL @@ -451,55 +156,19 @@ more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). ## Update Mojo -To check your current Mojo version, use the `--version` option: +Because Mojo is now a part of MAX, you soon won't be able to update the +standalone `mojo` package, and you must instead install/update the `max` +package. (Read [why we bundled Mojo with +MAX](/max/faq#why-bundle-mojo-with-max).) -```sh -mojo --version -``` - -And compare your version to the latest stable version in the [Mojo -changelog](/mojo/changelog). Or if you installed a nightly build, look for -release announcements in [this Discord -channel](https://discord.com/channels/1087530497313357884/1224434323193594059). - -If it's time to update, here's what to do: +If you already installed Mojo on its own, you'll need to install MAX to get all +future Mojo updates. -1. Make sure you have the latest `modular` CLI: - - - - - ```sh - brew update \ - && brew upgrade modular - ``` +Before you install `max`, you should uninstall Mojo to avoid conflicting +toolchain versions between the `mojo` and `max` packages: - - - - ```sh - sudo apt update \ - && sudo apt install modular - ``` - - - - -2. Update the `mojo` package: - - - - - ```sh - modular update mojo - ``` - - - - - ```sh - modular update nightly/mojo - ``` +```sh +modular uninstall mojo +``` - - +Then follow the guide to [install MAX & Mojo](/max/install). \ No newline at end of file From 9c13af9ce8b2119f401fd39a0a66033b69e71ad8 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 3 Sep 2024 10:14:29 -0700 Subject: [PATCH 0807/2019] Update docs to use Magic MODULAR_ORIG_COMMIT_REV_ID: 908398932b05cbced9d38e8c053c46b7ddd138ca --- docs/manual/get-started.mdx | 257 +++++++++++++++++++-------- docs/manual/python/index.ipynb | 308 +++++++++------------------------ docs/manual/python/types.ipynb | 12 +- 3 files changed, 276 insertions(+), 301 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 265ea29bf5..08dcbf6503 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -6,47 +6,106 @@ description: Install Mojo now and start developing import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import MaxInstall from '@site/src/components/MaxInstall'; -On this page, we'll show you how to create the classic "Hello -world" starter program with Mojo, in three different ways. If you'd rather read -how to write Mojo code beyond just printing text, see the [introduction to -Mojo](/mojo/manual/basics). +On this page, we'll show you how to create the classic "Hello world" starter +program with Mojo. If you'd rather read how to write Mojo code, see the +[introduction to Mojo](/mojo/manual/basics). -## 1. Install Mojo +By installing Mojo, you understand and agree to our [software +license](https://www.modular.com/legal/max). -Mojo is now bundled with MAX, which provides everything you need to compile, -run, debug, and package Mojo code. (Read [why we bundled Mojo with -MAX](/max/faq#why-bundle-mojo-with-max).) +## 1. Create a new project -Follow the guide to [install MAX & Mojo](/max/install), and then return here. +To create a new Mojo project, we'll use [Magic](/magic)—a virtual environment +manager and package manager based on conda. -## 2. Run code in the REPL +1. Install Magic on macOS or Ubuntu Linux with this command: + + + + Then run the `source` command printed in your terminal. + +2. Create a Mojo project called "hello-world": + + ```sh + magic init hello-world --format mojoproject + ``` + + This creates a directory named `hello-world` and installs the Mojo project + dependencies—the only dependency for a Mojo project is the `max` package + (Mojo is [bundled with MAX](/max/faq#why-bundle-mojo-with-max)). + +3. Start a shell in the project virtual environment: + + ```sh + cd hello-world && magic shell + ``` + +That's it! The `magic shell` command activates the virtual environment so you +can now start using Mojo. For example, you can check your Mojo version like +this: + +```sh +mojo --version +``` + + +
+ Click here to install the Mojo nightly build. +
+ +To install the latest nightly build, specify the `max-nightly` channel when you +initialize your project: + +```sh +magic init hello-world-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly +``` + +When you include the `-c` (`--channel`) option, you must specify all channels. +Be sure to specify the `conda-forge` channel (or another conda channel) where +Magic should look for all packages other than MAX/Mojo. + +Then start a shell in the new environment: + +```sh +cd hello-world-nightly && magic shell +``` -Now that you've installed Mojo, let's write some code! +The nightly version of Mojo installed in this project is fully independent, so +it will not interfere with other projects that use the latest stable build. + +
+
+ + +## 2. Run code in the REPL First, let's use the Mojo [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), which allows you to write and run Mojo code in a command prompt: -1. To start a REPL session, type `mojo` in your terminal and press - Enter. +1. To start a REPL session, type `mojo` and press Enter. -2. Then type `print("Hello, world!")` and press Enter twice +2. Type `print("Hello, world!")` and press Enter twice (a blank line is required to indicate the end of an expression). -That's it! For example: + The result looks like this: -```text -$ mojo -Welcome to Mojo! 🔥 + ```text + $ mojo + Welcome to Mojo! 🔥 -Expressions are delimited by a blank line. -Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. + Expressions are delimited by a blank line. + Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. -1> print("Hello, world!") -2. -Hello, world! -``` + 1> print("Hello world") + Hello world + ``` + +3. To exit REPL, type `:quit` and press Enter, or press +Ctrl + D. You can write as much code as you want in the REPL. You can press Enter to start a new line and continue writing code, and when you @@ -77,16 +136,10 @@ Now let's write the code in a Mojo source file and run it with the mojo hello.mojo ``` - It should immediately print the message: - - ```text + ```output Hello, world! ``` -If this didn't work for you, double-check that your code looks exactly like the -code in step 1, and make sure you correctly installed either [MAX](/max/install) -(which includes Mojo) or [Mojo](#1-install-mojo). - ## 4. Build an executable binary Finally, let's build and run that same code as an executable: @@ -106,10 +159,60 @@ Finally, let's build and run that same code as an executable: ./hello ``` -This creates a statically compiled binary file, so it contains all the code and -libraries it needs to run. + ```output + Hello, world! + ``` + +The [`build`](/mojo/cli/build) command creates a statically compiled binary +file, so it contains all the code and libraries it needs to run. + +You can now deactivate the virtual environment by just typing `exit`: + +```sh +exit +``` + +Now let's try running an existing code example. + +## 5. Run an example from GitHub -## 5. Install our VS Code extension (optional) +Our Mojo code examples in GitHub include a Magic configuration file so you can +simply clone the repo and run the code with `magic`. For example: + +1. Clone the Mojo repo: + + ```sh + git clone https://github.com/modularml/mojo.git + ``` + + Only if you installed the nightly build, also checkout the nightly branch: + + ```sh + git checkout nightly + ``` + +2. Navigate to the examples: + + ```sh + cd mojo/examples + ``` + +3. Run some code: + + ```sh + magic run mojo hello_interop.mojo + ``` + + ```output + Hello Mojo 🔥! + 9 + 6 + 3 + Hello from Python! + I can even print a numpy array: [1 2 3] + ``` + +## 6. Install our VS Code extension (optional) To provide a first-class developer experience with features like code completion, quick fixes, and hover help, we've created a [Mojo extension for @@ -118,27 +221,64 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco ![](./images/mojo-vscode.png) -## Next steps -- If you're new to Mojo, we suggest you learn the language basics in the - [introduction to Mojo](/mojo/manual/basics). +## Update Mojo + +To update the Mojo version in your project, use `magic add` +and specify the `max` package version. + +For example, if you want to always use the latest version of Mojo, you can use +the `*` wildcard as the version and then simply run `magic update` (you must +run `magic add` within the project path): + +```sh +cd hello-world +``` + +```sh +magic add "max=*" +``` + +```sh +magic update +``` + +:::note + +Although the wildcard option allows `magic update` to always install the latest +version, it also updates the `magic.lock` file in your project with the +explicit version you've installed. This ensures that anybody else who +initializes the project also gets the same package version (until you run +`magic update` again). + +::: + +To be more specific with your package version, you can use any of the [Python +package version +specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#id5). +For example: + +```sh +magic add "max~=24.4" +``` + +```sh +magic add "max>=24.4,<24.5" +``` -- If you want to experiment with some code, clone [the Mojo -repo](https://github.com/modularml/mojo/) to try our code examples: - ```sh - git clone https://github.com/modularml/mojo.git - ``` +## Next steps - If you installed the nightly build, also checkout the nightly branch: +- If you're new to Mojo, we suggest you learn the language basics in the + [introduction to Mojo](/mojo/manual/basics). - ```sh - git checkout nightly - ``` +- To learn more about the `magic` tool, read [Get started with Magic](/magic/). - In addition to several `.mojo` examples, the repo includes [Jupyter - notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) - that teach advanced Mojo features. +- Explore more code examples in the [the Mojo +repo](https://github.com/modularml/mojo/). In addition to several `.mojo` +examples, the repo includes [Jupyter +notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) +that teach advanced Mojo features. - To see all the available Mojo APIs, check out the [Mojo standard library reference](/mojo/lib). @@ -153,22 +293,3 @@ crash reports. [Learn more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). ::: - -## Update Mojo - -Because Mojo is now a part of MAX, you soon won't be able to update the -standalone `mojo` package, and you must instead install/update the `max` -package. (Read [why we bundled Mojo with -MAX](/max/faq#why-bundle-mojo-with-max).) - -If you already installed Mojo on its own, you'll need to install MAX to get all -future Mojo updates. - -Before you install `max`, you should uninstall Mojo to avoid conflicting -toolchain versions between the `mojo` and `max` packages: - -```sh -modular uninstall mojo -``` - -Then follow the guide to [install MAX & Mojo](/max/install). \ No newline at end of file diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 6e414cc178..e9e3caa2b1 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -20,59 +20,74 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Our long-term goal is to make Mojo a *superset of Python* (that is, to make Mojo \n", - "compatible with existing Python programs). Python programmers should be able to\n", - "use Mojo immediately, and be able to access the huge ecosystem of Python \n", - "packages that are available today. \n", - "\n", - "However, Mojo is still in early development and many Python features are not yet\n", + "Mojo is still in early development and many Python features are not yet\n", "implemented. You can't currently write everything in Mojo that you can write in\n", "Python. And Mojo doesn't have its own ecosystem of packages yet.\n", "\n", "To help bridge this gap, Mojo lets you import Python modules, call Python \n", - "functions and interact with Python objects from Mojo code. It runs Python code\n", - "using a standard Python interpreter (CPython), so your existing Python code\n", + "functions, and interact with Python objects from Mojo code. The Python code\n", + "runs in a standard Python interpreter (CPython), so your existing Python code\n", "doesn't need to change.\n", "\n", + "## Create a Python environment\n", + "\n", + "To successfully integrate Python code with your Mojo project, your environment\n", + "must have a compatible Python runtime installed along with any additional\n", + "Python packages that you want to use. Currently, you can create a compatible\n", + "environment in a couple of ways:\n", + "\n", + "- We recommend that you use [Magic](/magic), our package manager and\n", + " virtual environment manager for MAX and Mojo projects. To use Magic to create\n", + " and manage the virtual environment for your Mojo/Python project, first\n", + " follow the instructions in [Install Magic](/magic/#install-magic).\n", + " Then you can create a new Mojo project like this:\n", + "\n", + " ```sh\n", + " magic init my-mojo-project --format mojoproject\n", + " ```\n", + "\n", + " After creating the project, you can enter the project and install any\n", + " dependencies, for example [NumPy](https://numpy.org/):\n", + "\n", + " ```sh\n", + " cd my-mojo-project\n", + " ```\n", + "\n", + " ```sh\n", + " magic add \"numpy>=2.0\"\n", + " ```\n", + "\n", + "- Alternatively, you can also add MAX and Mojo to a\n", + " [conda](https://docs.conda.io/projects/conda/en/latest/index.html) project.\n", + " To do so, follow the steps in [Add MAX/Mojo to a conda project](/magic/conda).\n", + "\n", + "- It's also possible to convert an existing conda project to Magic as documented\n", + " in [Migrate a conda project to Magic](/magic/#migrate-a-conda-project-to-magic).\n", + "\n", "## Import a Python module\n", "\n", "To import a Python module in Mojo, just call \n", "[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) \n", - "with the module name:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ + "with the module name. The following shows an example of importing the standard\n", + "Python [NumPy](https://numpy.org/) package:\n", + "\n", + "```mojo\n", "from python import Python\n", "\n", - "fn use_array() raises:\n", + "def main():\n", " # This is equivalent to Python's `import numpy as np`\n", - " var np = Python.import_module(\"numpy\")\n", + " np = Python.import_module(\"numpy\")\n", "\n", " # Now use numpy as if writing in Python\n", - " var array = np.array([1, 2, 3])\n", - " print(array)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1 2 3]\n" - ] - } - ], - "source": [ - "use_array()" + " array = np.array([1, 2, 3])\n", + " print(array)\n", + "```\n", + "\n", + "Running this program produces the following output:\n", + "\n", + "```\n", + "[1 2 3]\n", + "```" ] }, { @@ -80,13 +95,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Yes, this imports Python NumPy, and you can import *any other Python module*\n", - "that you have installed.\n", + "Assuming that you have the NumPy package installed in your\n", + "[environment](#create-a-python-environment), this imports NumPy and you can use any\n", + "of its features.\n", "\n", "A few things to note:\n", "\n", + "- The `import_module()` method returns a reference to the module in the form of\n", + " a [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject)\n", + " wrapper. You must store the reference in a variable and then use it as shown\n", + " in the example above to access functions, classes, and other objects defined\n", + " by the module. See [Mojo wrapper objects](/mojo/manual/python/types#mojo-wrapper-objects)\n", + " for more information about the `PythonObject` type.\n", + "\n", "- Currently, you cannot import individual members (such as a single Python class\n", - " or function)—you must import the whole Python module and then access members\n", + " or function). You must import the whole Python module and then access members\n", " through the module name.\n", "\n", "- Mojo doesn't yet support top-level code, so the `import_module()` call must\n", @@ -108,7 +131,7 @@ "Mojo loads the Python interpreter and Python modules at runtime, so\n", "wherever you run a Mojo program, it must be able to access a compatible Python\n", "interpreter, and to locate any imported Python modules. For more information,\n", - "see [Python environment](#python-environment).\n", + "see [Create a Python environment](#create-a-python-environment).\n", "\n", ":::" ] @@ -139,17 +162,17 @@ "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", - "fn main() raises:\n", + "def main():\n", " Python.add_to_path(\"path/to/module\")\n", - " var mypython = Python.import_module(\"mypython\")\n", + " mypython = Python.import_module(\"mypython\")\n", "\n", - " var values = mypython.gen_random_values(2, 3)\n", + " values = mypython.gen_random_values(2, 3)\n", " print(values)\n", "```\n", "\n", "Both absolute and relative paths work with \n", - "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example, you\n", - "can import from the local directory like this:\n", + "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example,\n", + "you can import from the local directory like this:\n", "\n", "```mojo\n", "Python.add_to_path(\".\")\n", @@ -184,17 +207,10 @@ "application drive the event loop and poll for updates. The following example\n", "uses Tkinter, but the basic approach can be applied to other packages.\n", "\n", - "First we create a Python module that defines a Tkinter interface, with a window\n", - "and single button:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%%python\n", + "First you create a Python module that defines a Tkinter interface, with a window\n", + "and single button:\n", + "\n", + "```{.python filename=\"myapp.py\"}\n", "import tkinter as tk\n", "\n", "class App:\n", @@ -218,37 +234,33 @@ " self.create_button(\"Hello Mojo!\")\n", "\n", " def update(self):\n", - " self._root.update()" + " self._root.update()\n", + "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can call this module from Mojo like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ + "You can call this module from Mojo like this:\n", + "\n", + "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", - "fn button_clicked():\n", + "def button_clicked():\n", " print(\"Hi from a Mojo🔥 fn!\")\n", "\n", "def main():\n", " Python.add_to_path(\".\")\n", - " var app = Python.import_module(\"myapp\").App()\n", + " app = Python.import_module(\"myapp\").App()\n", " app.create(\"800x600\")\n", "\n", " while True:\n", " app.update()\n", " if app.clicked:\n", " button_clicked()\n", - " app.clicked = False" + " app.clicked = False\n", + "```" ] }, { @@ -257,159 +269,7 @@ "source": [ "Instead of the Python module calling the Tkinter `mainloop()` method, the Mojo \n", "code calls the `update()` method in a loop and checks the `clicked` attribute \n", - "after each update.\n", - "\n", - "## Python environment\n", - "\n", - "The Mojo SDK depends on an existing Python dynamic library. At runtime, Mojo\n", - "uses the first Python in the search path (`PATH`), to find an associated dynamic\n", - "Python library of the same version. This will also add any modules from the\n", - "activated virtual environment. \n", - "\n", - "### Resolving issues\n", - "\n", - "Finding libpython may fail if the Python interpreter on top of `PATH` does not\n", - "have an associated dynamic library. Some Python distributions don't include the\n", - "shared library, and others only have a static library which isn't supported by\n", - "Mojo yet.\n", - "\n", - "You can find a compatible Python on your system by running this Python script:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "python" - } - }, - "outputs": [], - "source": [ - "import os\n", - "import subprocess\n", - "\n", - "FIND_LIBPYTHON = \"\"\"\n", - "import os\n", - "import sys\n", - "from pathlib import Path\n", - "from sysconfig import get_config_var\n", - "ext = \"dll\" if os.name == \"nt\" else \"dylib\" if sys.platform == \"darwin\" else \"so\"\n", - "binary = f\"libpython{get_config_var('py_version_short')}.{ext}\"\n", - "for folder in [Path(get_config_var(p)) for p in [\"LIBPL\", \"LIBDIR\"]]:\n", - " libpython_path = folder / binary\n", - " if libpython_path.is_file():\n", - " print(libpython_path.resolve())\n", - " exit(0)\n", - "exit(1)\n", - "\"\"\"\n", - "FIND_PYTHON_VER = \"import sysconfig; print(sysconfig.get_python_version())\"\n", - "\n", - "exe_names = [\"python3\", \"python\"] + [f\"python3.{i}\" for i in range(8, 13)]\n", - "seen = []\n", - "executables = []\n", - "\n", - "print(\"Mojo will attempt to use the first python executable from the top:\\n\")\n", - "print(\"vers | compat | path\")\n", - "for path in os.environ[\"PATH\"].split(\":\"):\n", - " for exe in exe_names:\n", - " full_path = os.path.join(path, exe)\n", - " if os.path.exists(full_path):\n", - " pyver = subprocess.check_output([full_path, \"-c\", FIND_PYTHON_VER], text=True).strip()\n", - " res = subprocess.run([full_path, \"-c\", FIND_LIBPYTHON], text=True, capture_output=True)\n", - " libpython = res.stdout.strip()\n", - " if res.returncode != 0:\n", - " print(f\"{pyver:<7} no {full_path}\")\n", - " elif libpython not in seen:\n", - " print(f\"{pyver:<7} yes {full_path}\")\n", - " seen.append(libpython)\n", - " executables.append(full_path)\n", - "\n", - "if not executables:\n", - " print(\"no compatible Python environments found\")\n", - "else:\n", - " print(\"\\ncreate and activate a virtual environment to use a different Python version:\")\n", - " print(f\" {executables[-1]} -m venv .venv\")\n", - " print(\" source .venv/bin/activate\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Which will produce output like:\n", - "\n", - "```output\n", - "Mojo will attempt to use the first python executable from the top:\n", - "\n", - "vers | compat | path\n", - "3.11 yes /opt/homebrew/opt/python@3.11/libexec/bin/python3\n", - "3.12 yes /opt/homebrew/bin/python3\n", - "3.9 yes /usr/bin/python3\n", - "\n", - "create and activate a virtual environment to use a different Python version:\n", - " /usr/bin/python3 -m venv .venv\n", - " source .venv/bin/activate\n", - "```\n", - "\n", - "If you have no compatible environment, you can install a compatible version of\n", - "Python that includes shared libraries. Try following the instructions in [Set up\n", - "a Python environment with Conda](#set-up-a-python-environment-with-conda) to\n", - "install a virtual environment. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Set up a Python environment with Conda\n", - "\n", - "Using a Python virtual environment such as \n", - "[Conda](https://docs.conda.io/en/latest/) is one way to get a version of Python\n", - "that will work reliably with Mojo. It comes with the required dynamic library,\n", - "and ensures there are no conflicts with system dependencies. \n", - "\n", - "To set up a virtual environment with Conda:\n", - "\n", - "1. Install Conda by following the \n", - " [Quick command-line install instructions](https://docs.conda.io/projects/miniconda/en/latest/#quick-command-line-install).\n", - "\n", - "2. Initialize Conda for all the shells on your path:\n", - "\n", - " ```bash\n", - " ~/miniconda3/bin/conda init --all\n", - " ```\n", - "\n", - " Or just one at a time:\n", - "\n", - " ```bash\n", - " ~/miniconda3/bin/conda init zsh\n", - " ```\n", - "\n", - "2. Restart your shell. \n", - "\n", - "3. Install your desired version of Python and activate the environment:\n", - "\n", - " ```bash\n", - " conda create -n 3.10 python=3.10\n", - " conda activate 3.10\n", - " ```\n", - "\n", - "After setting up the Conda virtual environment, you can install any Python \n", - "packages you want to use with Mojo, with `conda install` or `pip install`. For\n", - "example:\n", - "\n", - "```bash\n", - "conda install numpy\n", - "pip install pillow\n", - "```\n", - "\n", - "Now whenever you `conda activate 3.10`, Mojo will be able to find any modules\n", - "you installed into that environment.\n", - "\n", - "For more information on using Conda with Mojo, see \n", - "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on\n", - "the Modular Blog." + "after each update.\n" ] } ], diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index f3c74ebe72..c7c4c4e558 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -128,7 +128,7 @@ "### Mojo wrapper objects\n", "\n", "When you use Python objects in your Mojo code, Mojo adds the \n", - "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", + "[`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) wrapper around\n", "the Python object. This object exposes a number of common double underscore\n", "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n", "through to the underlying Python object. " @@ -219,7 +219,7 @@ "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", - "[`to_float64()`](/mojo/stdlib/python/object/PythonObject#to_float64) for \n", + "[`to_float64()`](/mojo/stdlib/python/python_object/PythonObject#to_float64) for \n", "converting to a Mojo floating point value.\n", "\n", "```mojo\n", @@ -267,13 +267,7 @@ "metadata": {}, "source": [ "One TODO item here: The `Python.is_type()` method is misleadingly named, since \n", - "it doesn't compare _types_, but object identity.\n", - "\n", - "## Further reading\n", - "\n", - "For more information, see \n", - "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on \n", - "the Modular Blog." + "it doesn't compare _types_, but object identity.\n" ] } ], From 9f3c50eee5344687720db49c5be5842253472883 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 3 Sep 2024 14:02:28 -0700 Subject: [PATCH 0808/2019] Fix magic add command MODULAR_ORIG_COMMIT_REV_ID: 05f026985e3342b22f2402b1ef243683dc9ce517 --- docs/manual/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 08dcbf6503..363fef9d10 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -236,7 +236,7 @@ cd hello-world ``` ```sh -magic add "max=*" +magic add max ``` ```sh From b5391bae169c63d36e75f2ffa33c6dc6d8242f99 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 4 Sep 2024 15:27:19 -0700 Subject: [PATCH 0809/2019] Update READMEs to use Magic/conda MODULAR_ORIG_COMMIT_REV_ID: 0d8849e0ed397ce50b96c6673d8a57c7fdc6b357 --- CONTRIBUTING.md | 32 +++++++++++------------ README.md | 41 +++++++++++++++++++---------- examples/README.md | 29 +++++++++++---------- examples/notebooks/README.md | 50 +++++++++++++++++++++--------------- examples/notebooks/pixi.toml | 13 ++++++++++ 5 files changed, 100 insertions(+), 65 deletions(-) create mode 100644 examples/notebooks/pixi.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94b402dfb1..df52cb9705 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -259,30 +259,30 @@ git rebase upstream/nightly #### Getting the nightly Mojo compiler Now that you're on the nightly branch, you need to install the latest nightly -Mojo compiler: +build. -```bash -curl https://get.modular.com | sh - - -modular auth +If you're using [`magic`](https://docs.modular.com/magic), create a new +project environment with the `max-nightly` channel like this: -modular install nightly/mojo +```bash +magic init mojo-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly ``` -If you already have an older `nightly/mojo` compiler, replace -`modular install nightly/mojo` with `modular update nightly/mojo`. +If you're [using conda](https://docs.modular.com/magic/conda), add the +`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +file. For example: -Then, follow the instructions from the `modular` tool in adding the `mojo` -compiler to your `PATH` such as: +```yaml +[project] +name = "Mojo nightly example" +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] -```bash -echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc -echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc +[dependencies] +max = "*" ``` -If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. - #### Mojo nightly vscode extension Install the [Mojo nightly VS Code diff --git a/README.md b/README.md index ebeb9c8c7e..7a56bf66e6 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,8 @@ To learn more about Mojo, see the ### Latest Released -To install the last released build of Mojo, you can install the MAX SDK -or the standalone Mojo SDK: - -- [Get the MAX SDK](https://docs.modular.com/engine/get-started) -- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) - -Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started/hello-world). +To install the last released build of Mojo, follow the guide to +[Get started with Mojo](https://docs.modular.com/mojo/manual/get-started). ### Latest Nightly @@ -56,13 +50,32 @@ The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk and be patient! -To get nightly builds, see the same instructions to [install the Mojo -SDK](https://docs.modular.com/mojo/manual/get-started/#install-mojo), but use -the command shown there to install `nightly/mojo`. +To get nightly builds, see the same instructions to [Get started with +Mojo](https://docs.modular.com/mojo/manual/get-started), but when you create +your project, instead use the following `magic init` command to set the +conda package channel to `max-nightly`: + +```bash +magic init hello-world-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly +``` + +Or, if you're [using conda](https://docs.modular.com/magic/conda), add the +`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +file. For example: + +```yaml +[project] +name = "Mojo nightly example" +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] + +[dependencies] +max = "*" +``` -When you clone this repo, be sure you switch to the `nightly` branch, because -the `main` branch is for stable releases and might not be compatible with -nightly builds: +And when you clone this repo, switch to the `nightly` branch because the `main` +branch might not be compatible with nightly builds: ```bash git clone https://github.com/modularml/mojo.git diff --git a/examples/README.md b/examples/README.md index 4b1b9d147c..f338fad698 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,11 +1,11 @@ # Mojo code examples -A collection of sample programs and Mojo notebooks written in the +A collection of sample programs and Mojo notebooks written in the [Mojo](https://docs.modular.com/mojo/programming-manual.html) programming language. ## Getting Started -Access a Mojo programming environment available from the +Access a Mojo programming environment available from the Mojo product [page](https://www.modular.com/mojo). Git clone the repository of Mojo samples using the command below: @@ -16,23 +16,24 @@ git clone https://github.com/modularml/mojo.git ## Running -Use the following sample command-line to run the programs: +If you're using [`magic`](https://docs.modular.com/magic), navigate into +the examples directory and use `magic run`. For example: ```bash -mojo matmul.mojo +magic run mojo matmul.mojo ``` -You can run the Mojo notebooks using [JupyterLab or Visual Studio +You can run the Mojo notebooks using [JupyterLab or Visual Studio Code](notebooks/README.md) with the Mojo extension available on the Marketplace. ### Mojo SDK Container -The repo also contains a Dockerfile that can be used to create a -Mojo SDK container for developing and running Mojo programs. Use the -container in conjunction with the Visual Studio Code devcontainers +The repo also contains a Dockerfile that can be used to create a +Mojo SDK container for developing and running Mojo programs. Use the +container in conjunction with the Visual Studio Code devcontainers extension to develop directly inside the container. -The Dockerfile also sets up a `conda` environment and by default, +The Dockerfile also sets up a `conda` environment and by default, starts a `jupyter` server (which you can access via the browser). To build a Mojo container, either use @@ -55,11 +56,11 @@ The script also supports building with `podman` instead of `docker`: ./build-image.sh --auth-key \ --use-podman \ --mojo-version 0.3 - + ``` -You can then run with either `docker` or `podman`. In the example below, -we map the ports, bind mount the current directory and open a shell into +You can then run with either `docker` or `podman`. In the example below, +we map the ports, bind mount the current directory and open a shell into the container: ```bash @@ -85,8 +86,8 @@ podman run \ ## License -The Mojo examples and notebooks in this repository are licensed -under the Apache License v2.0 with LLVM Exceptions +The Mojo examples and notebooks in this repository are licensed +under the Apache License v2.0 with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Contributing diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 7c6eecf342..4ee3854595 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -29,9 +29,10 @@ notebooks. Especially if you're developing with Mojo on a remote system, using VS Code is ideal because it allows you to edit and interact with notebooks on the remote machine where you've installed Mojo. -All you need is the Mojo SDK and the Jupyter VS Code extension: +All you need is Mojo and the Jupyter VS Code extension: -1. Install the [Mojo SDK](https://developer.modular.com/download). +1. [Create a new Mojo +project](https://docs.modular.com/mojo/manual/get-started#1-create-a-new-project). 2. Install [Visual Studio Code](https://code.visualstudio.com/) and the [Jupyter @@ -56,33 +57,40 @@ instructions don't support remote access to the JupyterLab). For more details about using JupyterLab, see the complete [JupyterLab installation guide](https://jupyterlab.readthedocs.io/en/latest/getting_started/installation.html). -**Note:** You must run this setup on the same machine where you've installed -the [Mojo SDK](https://developer.modular.com/download). However, syntax -highlighting for Mojo code is not currently enabled in JupyterLab (coming soon). +### 1. Launch JupyterLab -1. Install JupyterLab: +You can use either Magic or conda. - ```sh - python3 -m pip install jupyterlab - ``` +#### Using Magic -2. Make sure the user-level `bin` is in your `$PATH`: +If you have [`magic`](https://docs.modular.com/magic) you can run the following +command to launch JupyterLab from this directory: - ```sh - export PATH="$HOME/.local/bin:$PATH" - ``` +```sh +magic run jupyter lab +``` -3. Launch JupyterLab: +After a moment, it will open a browser window with JupterLab running. - ```sh - jupyter lab - ``` +#### Using conda -4. When you open any of the `.ipynb` notebooks from this repository, JupyterLab - should automatically select the Mojo kernel (which was installed with the - Mojo SDK). +Create a Conda environment, activate that enviroment, and install JupyterLab. - Now run some Mojo code! +``` sh +# Create a Conda environment if you don't have one +conda create -n mojo-repo +# Activate the environment +conda env update -n mojo-repo -f environment.yml --prune +# run JupyterLab +conda run -n mojo-repo jupyter lab +``` + +After a moment, it will open a browser window with JupterLab running. + +### 2. Run the .ipynb notebooks + +The left nav bar should show all the notebooks in this directory. +Open any `.ipynb` file and start running the code. ## Notes and tips diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml new file mode 100644 index 0000000000..5b2ca73adb --- /dev/null +++ b/examples/notebooks/pixi.toml @@ -0,0 +1,13 @@ +[project] +name = "Mojo notebooks" +version = "1.0.0" +description = "Environment for running JupyterLab" +authors = ["Modular "] +channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] + +[dependencies] +python = ">=3.8,<3.12" +max = "*" +pip = ">=24.0,<25" +jupyterlab = ">=4.2.5,<5" From fded76ae5dd79007816ce3c61355fdb1b86267a9 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 31 May 2024 14:40:16 -0600 Subject: [PATCH 0810/2019] [stdlib] Support `Dict.popitem()` Implement `Dict.popitem()` which removes and returns the last item in the `Dict`. Fixes https://github.com/modularml/mojo/issues/2355. Reapply after it got reverted now that the UB in `reversed()` for `Dict` has been fixed. MODULAR_ORIG_COMMIT_REV_ID: d8bf29ed0aeb6d59a9a0a3af650ce141993faa83 --- CONTRIBUTING.md | 32 +- README.md | 41 +- docs/changelog-released.md | 913 +----------------- docs/changelog.md | 656 ++++++++++++- docs/{community.mdx => community.md} | 0 docs/faq.md | 58 +- docs/{lib.mdx => lib.md} | 4 +- docs/manual/basics.ipynb | 16 +- docs/manual/control-flow.ipynb | 887 ----------------- docs/manual/decorators/always-inline.ipynb | 8 +- .../manual/decorators/{index.mdx => index.md} | 3 +- docs/manual/decorators/parameter.ipynb | 51 +- .../manual/decorators/register-passable.ipynb | 20 +- docs/manual/decorators/staticmethod.ipynb | 2 +- docs/manual/decorators/unroll.ipynb | 164 ++++ docs/manual/decorators/value.ipynb | 2 +- docs/manual/functions.ipynb | 205 ++-- docs/manual/get-started.md | 505 ++++++++++ docs/manual/get-started.mdx | 295 ------ docs/manual/images/pointer-diagram-dark.png | Bin 21558 -> 0 bytes docs/manual/images/pointer-diagram.png | Bin 20998 -> 0 bytes docs/manual/images/pointer-lifecycle-dark.png | Bin 82779 -> 0 bytes docs/manual/images/pointer-lifecycle.png | Bin 81374 -> 0 bytes docs/manual/images/pointer-offset-dark.png | Bin 15226 -> 0 bytes docs/manual/images/pointer-offset.png | Bin 15051 -> 0 bytes .../images/strided-load-storage-dark.png | Bin 13876 -> 0 bytes docs/manual/images/strided-load-storage.png | Bin 13569 -> 0 bytes docs/manual/index.md | 54 +- docs/manual/lifecycle/death.ipynb | 73 +- docs/manual/lifecycle/index.ipynb | 14 +- docs/manual/lifecycle/life.ipynb | 42 +- docs/manual/packages.md | 8 +- docs/manual/parameters/index.ipynb | 146 ++- docs/manual/pointers.ipynb | 756 --------------- docs/manual/python/index.ipynb | 244 +++-- docs/manual/python/types.ipynb | 14 +- docs/manual/structs.ipynb | 14 +- docs/manual/traits.ipynb | 23 +- docs/manual/types.ipynb | 52 +- docs/manual/values/index.ipynb | 2 +- docs/manual/values/ownership.ipynb | 163 ++-- docs/manual/values/value-semantics.ipynb | 50 +- docs/manual/variables.ipynb | 39 +- docs/notebooks/{index.mdx => index.md} | 4 +- docs/roadmap.md | 27 +- docs/tools/debugging.ipynb | 283 +----- .../images/debugger-call-stack-nested1.png | Bin 105875 -> 0 bytes docs/tools/images/debugger-variables.png | Bin 71527 -> 0 bytes docs/tools/testing.ipynb | 750 +------------- docs/upgrade-guide.md | 219 +++++ examples/README.md | 29 +- examples/notebooks/README.md | 50 +- examples/notebooks/pixi.toml | 13 - stdlib/src/collections/dict.mojo | 28 + stdlib/src/python/_cpython.mojo | 116 ++- stdlib/src/python/python.mojo | 5 +- stdlib/src/sys/ffi.mojo | 22 - stdlib/test/collections/test_dict.mojo | 16 + 58 files changed, 2518 insertions(+), 4600 deletions(-) rename docs/{community.mdx => community.md} (100%) rename docs/{lib.mdx => lib.md} (84%) delete mode 100644 docs/manual/control-flow.ipynb rename docs/manual/decorators/{index.mdx => index.md} (97%) create mode 100644 docs/manual/decorators/unroll.ipynb create mode 100644 docs/manual/get-started.md delete mode 100644 docs/manual/get-started.mdx delete mode 100644 docs/manual/images/pointer-diagram-dark.png delete mode 100644 docs/manual/images/pointer-diagram.png delete mode 100644 docs/manual/images/pointer-lifecycle-dark.png delete mode 100644 docs/manual/images/pointer-lifecycle.png delete mode 100644 docs/manual/images/pointer-offset-dark.png delete mode 100644 docs/manual/images/pointer-offset.png delete mode 100644 docs/manual/images/strided-load-storage-dark.png delete mode 100644 docs/manual/images/strided-load-storage.png delete mode 100644 docs/manual/pointers.ipynb rename docs/notebooks/{index.mdx => index.md} (94%) delete mode 100644 docs/tools/images/debugger-call-stack-nested1.png delete mode 100644 docs/tools/images/debugger-variables.png create mode 100644 docs/upgrade-guide.md delete mode 100644 examples/notebooks/pixi.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df52cb9705..94b402dfb1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -259,30 +259,30 @@ git rebase upstream/nightly #### Getting the nightly Mojo compiler Now that you're on the nightly branch, you need to install the latest nightly -build. - -If you're using [`magic`](https://docs.modular.com/magic), create a new -project environment with the `max-nightly` channel like this: +Mojo compiler: ```bash -magic init mojo-nightly --format mojoproject \ - -c conda-forge -c https://conda.modular.com/max-nightly +curl https://get.modular.com | sh - + +modular auth + +modular install nightly/mojo ``` -If you're [using conda](https://docs.modular.com/magic/conda), add the -`https://conda.modular.com/max-nightly` channel to your `environment.yaml` -file. For example: +If you already have an older `nightly/mojo` compiler, replace +`modular install nightly/mojo` with `modular update nightly/mojo`. -```yaml -[project] -name = "Mojo nightly example" -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] -platforms = ["osx-arm64", "linux-aarch64", "linux-64"] +Then, follow the instructions from the `modular` tool in adding the `mojo` +compiler to your `PATH` such as: -[dependencies] -max = "*" +```bash +echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc +echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc ``` +If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. + #### Mojo nightly vscode extension Install the [Mojo nightly VS Code diff --git a/README.md b/README.md index 7a56bf66e6..ebeb9c8c7e 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,14 @@ To learn more about Mojo, see the ### Latest Released -To install the last released build of Mojo, follow the guide to -[Get started with Mojo](https://docs.modular.com/mojo/manual/get-started). +To install the last released build of Mojo, you can install the MAX SDK +or the standalone Mojo SDK: + +- [Get the MAX SDK](https://docs.modular.com/engine/get-started) +- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) + +Then follow the docs to [write your first Mojo +program](https://docs.modular.com/mojo/manual/get-started/hello-world). ### Latest Nightly @@ -50,32 +56,13 @@ The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk and be patient! -To get nightly builds, see the same instructions to [Get started with -Mojo](https://docs.modular.com/mojo/manual/get-started), but when you create -your project, instead use the following `magic init` command to set the -conda package channel to `max-nightly`: - -```bash -magic init hello-world-nightly --format mojoproject \ - -c conda-forge -c https://conda.modular.com/max-nightly -``` - -Or, if you're [using conda](https://docs.modular.com/magic/conda), add the -`https://conda.modular.com/max-nightly` channel to your `environment.yaml` -file. For example: - -```yaml -[project] -name = "Mojo nightly example" -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] -platforms = ["osx-arm64", "linux-aarch64", "linux-64"] - -[dependencies] -max = "*" -``` +To get nightly builds, see the same instructions to [install the Mojo +SDK](https://docs.modular.com/mojo/manual/get-started/#install-mojo), but use +the command shown there to install `nightly/mojo`. -And when you clone this repo, switch to the `nightly` branch because the `main` -branch might not be compatible with nightly builds: +When you clone this repo, be sure you switch to the `nightly` branch, because +the `main` branch is for stable releases and might not be compatible with +nightly builds: ```bash git clone https://github.com/modularml/mojo.git diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 8f9a51dee7..faa318fa04 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,814 +25,6 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` -## v24.4 (2024-06-07) - -### ✨ Highlights - -Big themes for this release: - -- Improvements to the performance and ease-of-use for `def` functions. - -- Continued unification of standard library APIs around the `UnsafePointer` - type. - -- Many quality-of-life improvements for the standard library collection types. - -- Significant performance improvements when inserting into a `Dict`. Performance - on this metric is still not where we'd like it to be, but it is much improved. - -- A new `@parameter for` mechanism for expressing compile-time loops, which - replaces the earlier (and less reliable) `@unroll` decorator. - -- New Mojo Manual pages on [Control flow](/mojo/manual/control-flow), - [Testing](/mojo/tools/testing) and using - [unsafe pointers](/mojo/manual/pointers). - -### Language changes - -- Mojo has changed how `def` function arguments are processed. Previously, by - default, arguments to a `def` were treated according to the `owned` - convention, which makes a copy of the value, enabling that value to be mutable - in the callee. - - This could lead to major performance issues because of the proliferation of - unnecessary copies. It also required you to declare non-copyable types as - `borrowed` explicitly. Now Mojo takes a different approach: `def` functions - take arguments as `borrowed` by default (consistent with `fn` functions) but - will make a local copy of the value **only if the argument is mutated** in the - body of the function. - - This improves consistency, performance, and ease of use. - -- Implicit variable definitions in a `def` function are more flexible: you can - now implicitly declare variables as the result of a tuple return, using - `a,b,c = foo()`. For example: - - ```mojo - def return_two(i: Int) -> (Int, Int): - return i, i+1 - - a, b = return_two(5) - ``` - - Implicit variable declarations can also now shadow global immutable symbols - (such as module names and built-ins) without getting a compiler error. - For example: - - ```mojo - slice = foo() - ``` - -- Mojo functions can return an auto-dereferenced refeference to storage with a - new `ref` keyword in the result type specifier. For example: - - ```mojo - @value - struct Pair: - var first: Int - var second: Int - - fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: - return self.first - - fn show_mutation(): - var somePair = Pair(5, 6) - somePair.get_first_ref() = 1 - ``` - - This approach provides a general way to return an "automatically dereferenced" - reference of a given type. Notably, this eliminates the need for - `__refitem__()` to exist. `__refitem__()` has thus been removed and replaced - with `__getitem__()` that returns a reference. - -- Mojo added support for _infer-only parameters_. Infer-only parameters must - appear at the beginning of the parameter list and cannot be explicitly - specified by the user. They are declared to the left of a `//` marker, much - like positional-only parameters. This allows programmers to define functions - with dependent parameters to be called without the caller specifying all the - necessary parameters. For example: - - ```mojo - fn parameter_simd[dt: DType, //, value: Scalar[dt]](): - print(value) - - fn call_it(): - parameter_simd[Int32(42)]() - ``` - - In the above example, `Int32(42)` is passed directly into `value`, the first - parameter that isn't infer-only. `dt` is inferred from the parameter itself - to be `DType.int32`. - - This also works with structs. For example: - - ```mojo - struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: - pass - - fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` - pass - ``` - - This should make working with dependent parameters more ergonomic. See - [Infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) in the - Mojo Manual. - -- Mojo now allows functions overloaded on parameters to be resolved when forming - references to, but not calling, those functions. For example, the following - now works: - - ```mojo - fn overloaded_parameters[value: Int32](): - pass - - fn overloaded_parameters[value: Float32](): - pass - - fn form_reference(): - alias ref = overloaded_parameters[Int32()] # works! - ``` - -- Mojo now supports adding a `@deprecated` decorator on structs, functions, - traits, aliases, and global variables. The decorator marks the attached - declaration as deprecated and causes a warning to be emitted when the - deprecated declaration is referenced in user code. The decorator requires a - deprecation message, specified as a string literal. - - ```mojo - @deprecated("Foo is deprecated, use Bar instead") - struct Foo: - pass - - fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead - pass - - @deprecated("use another function!") - fn bar(): - pass - - fn techdebt(): - bar() # warning: use another function! - ``` - -- Mojo has introduced - [`@parameter for`](/mojo/manual/decorators/parameter#parametric-for-statement), - a new feature for compile-time programming. `@parameter for` defines a for - loop where the sequence and the induction values in the sequence must be - parameter values. For example: - - ```mojo - fn parameter_for[max: Int](): - @parameter - for i in range(max) - @parameter - if i == 10: - print("found 10!") - ``` - - Currently, `@parameter for` requires the sequence's `__iter__()` method to - return a `_StridedRangeIterator`, meaning the induction variables must be - `Int`. The intention is to lift these restrictions in the future. - -- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, - not a low-level `__mlir_type.i1` value. - - This improves the ergonomics of spelling out a - `Reference` type explicitly. - -- Mojo will now link to a Python dynamic library based on the Python on top of - your search path: `PATH`. This enables you to activate a virtual environment - like `conda` and have access to Python modules installed in that environment - without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a - `libpython` dynamic library on installation and put the path in - `.modular/modular.cfg`, which could result in version conflicts if you - activated a virtual environment of a different Python version. - -- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids - binding non-trivial register-passable types to `AnyTrivialRegType`. This - closes a major safety hole in the language. Please use `AnyType` for generic - code going forward. - -- The `let` keyword has been completely removed from the language. We previously - removed `let` declarations but still provided an error message to users. Now, - it is completely gone from the grammar. - -### Standard library changes - -- New traits and related features: - - - Added built-in [`repr()`](/mojo/stdlib/builtin/repr/repr) function and - [`Representable`](/mojo/stdlib/builtin/repr/Representable) trait. - ([PR #2361](https://github.com/modularml/mojo/pull/2361)) - - - Added the [`Indexer`](/mojo/stdlib/builtin/int/Indexer) trait to denote - types that implement the `__index__()` method which allows these types to be - accepted in common `__getitem__()` and `__setitem__()` implementations, as - well as allow a new built-in - [`index()`](/mojo/stdlib/builtin/int/index-function) - function to be called on them. Most standard library containers can now be - indexed by any type that implements `Indexer`. For example: - - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 - - struct MyList: - var data: List[Int] - - fn __init__(inout self): - self.data = List[Int](1, 2, 3, 4) - - fn __getitem__[T: Indexer](self, idx: T) -> Int: - return self.data[index(idx)] - - print(MyList()[AlwaysZero()]) # prints `1` - ``` - - Types conforming to the `Indexer` trait are implicitly convertible to Int. - This means you can write generic APIs that take `Int` instead of making them - take a generic type that conforms to `Indexer`. For example: - - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 - - @value - struct Incrementer: - fn __getitem__(self, idx: Int) -> Int: - return idx + 1 - - var a = Incrementer() - print(a[AlwaysZero()]) # works and prints 1 - ``` - - ([PR #2685](https://github.com/modularml/mojo/pull/2685)) - - - Added traits allowing user-defined types to be supported by various - built-in and math functions. - - | Function | Trait | Required method | - |------------------|------------------|-----------------| - | [`abs()`](/mojo/stdlib/builtin/math/abs) | [`Absable`](/mojo/stdlib/builtin/math/Absable) | `__abs__()` | - | [`pow()`](/mojo/stdlib/builtin/math/pow) | [`Powable`](/mojo/stdlib/builtin/math/Powable) | `__pow__()` | - | [`round()`](/mojo/stdlib/builtin/math/round) | [`Roundable`](/mojo/stdlib/builtin/math/Roundable) | `__round__()` | - | [`math.ceil`](/mojo/stdlib/math/math/ceil) | `math.Ceilable` | `__ceil__()` | - | [`math.ceildiv`](/mojo/stdlib/math/math/ceildiv) | `math.CeilDivable`
`math.CeilDivableRaising` | `__ceildiv__()` | - | [`math.floor`](/mojo/stdlib/math/math/floor) | `math.Floorable` | `__floor__()` | - | [`math.trunc`](/mojo/stdlib/math/math/trunc) | `Truncable` | `__trunc__()` | - - Notes: - - - Conforming to the `Powable` trait also means that the type can be used - with the power operator (`**`). - - - For `ceildiv()`, structs can conform to either the `CeilDivable` trait - or `CeilDivableRaising` trait. - - - Due to ongoing refactoring, the traits `Ceilable`, `CeilDivable`, - `Floorable`, and `Truncable` do not appear in the API reference. They - should be imported from the `math` module, except for `Truncable` which - is (temporarily) available as a built-in trait and does not need to be - imported. - - Example: - - ```mojo - from math import sqrt - - @value - struct Complex2(Absable, Roundable): - var re: Float64 - var im: Float64 - - fn __abs__(self) -> Self: - return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) - - fn __round__(self) -> Self: - return Self(round(self.re, 0), round(self.im, 0)) - - fn __round__(self, ndigits: Int) -> Self: - return Self(round(self.re, ndigits), round(self.im, ndigits)) - - ``` - -- Benchmarking: - - - The [`bencher`](/mojo/stdlib/benchmark/bencher/) module as part of the - `benchmark` package is now public and documented. This module provides - types such as `Bencher` which provides the ability to execute a `Benchmark` - and allows for benchmarking configuration via the `BenchmarkConfig` struct. - -- [`String`](/mojo/stdlib/builtin/string/String) and friends: - - - **Breaking.** Implicit conversion to `String` is now removed for builtin - classes/types. Use [`str()`](/mojo/stdlib/builtin/str/str) explicitly to - convert to `String`. - - - Added [`String.isspace()`](/mojo/stdlib/builtin/string/String#isspace) - method conformant with Python's universal separators. This replaces the - `isspace()` free function from the `string` module. (If you need the old - function, it is temporarily available as `_isspace()`. It now takes a - `UInt8` but is otherwise unchanged.) - - - [`String.split()`](/mojo/stdlib/builtin/string/String#split) now defaults to - whitespace and has Pythonic behavior in that it removes all adjacent - whitespace by default. - - - [`String.strip()`](/mojo/stdlib/builtin/string/String#strip), - [`lstrip()`](/mojo/stdlib/builtin/string/String#lstrip) and - [`rstrip()`](/mojo/stdlib/builtin/string/String#rstrip) can now remove - custom characters other than whitespace. In addition, there are now several - useful aliases for whitespace, ASCII lower/uppercase, and so on. - ([PR #2555](https://github.com/modularml/mojo/pull/2555)) - - - `String` now has a - [`splitlines()`](/mojo/stdlib/builtin/string/String#splitlines) method, - which allows splitting strings at line boundaries. This method supports - [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) - and provides an option to retain or remove the line break characters. - ([PR #2810](https://github.com/modularml/mojo/pull/2810)) - - - `InlinedString` has been renamed to - [`InlineString`](/mojo/stdlib/utils/inline_string/InlineString) to be - consistent with other types. - - - [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) now implements - [`strip()`](/mojo/stdlib/utils/stringref/StringRef#strip), which can be used - to remove leading and trailing whitespace. - ([PR #2683](https://github.com/modularml/mojo/pull/2683)) - - - `StringRef` now implements - [`startswith()`](/mojo/stdlib/utils/stringref/StringRef#startswith) and - [`endswith()`](/mojo/stdlib/utils/stringref/StringRef#endswith). - ([PR #2710](https://github.com/modularml/mojo/pull/2710)) - - - Added a new [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) - type, to replace uses of the unsafe `StringRef` type in standard library - code. - - `StringSlice` is a non-owning reference to encoded string data. Unlike - `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it - points to. - - - Added new - [`as_string_slice()`](/mojo/stdlib/builtin/string/String#as_string_slice) - methods to `String` and `StringLiteral`. - - Added `StringSlice` initializer from an `UnsafePointer` and a length in - bytes. - - - Added a new - [`as_bytes_slice()`](/mojo/stdlib/builtin/string/String#as_bytes_slice) - method to `String` and `StringLiteral`, which - returns a `Span` of the bytes owned by the string. - - - Continued transition to - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and - unsigned byte type for strings: - - Renamed `String._as_ptr()` to - [`String.unsafe_ptr()`](/mojo/stdlib/builtin/string/String#unsafe_ptr), - and changed return type to `UnsafePointer` (was `DTypePointer`). - - Renamed `StringLiteral.data()` to - [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr), - and changed return type to `UnsafePointer` (was `DTypePointer`). - - `InlineString.as_ptr()` has been renamed to - [`unsafe_ptr()`](/mojo/stdlib/utils/inline_string/InlineString#unsafe_ptr) - and now returns an `UnsafePointer[UInt8]` (was - `DTypePointer[DType.int8]`). - - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) and - [`StringRef.unsafe_ptr()`](/mojo/stdlib/utils/stringref/StringRef#unsafe_ptr) - now returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). - -- Other built-ins: - - - The `Slice.__len__()` function has been removed and - [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) no longer conforms - to the `Sized` trait. This clarifies the ambiguity of the semantics: the - length of a slice always depends on the length of the object being sliced. - Users that need the existing functionality can use the - [`Slice.unsafe_indices()`](/mojo/stdlib/builtin/builtin_slice/Slice#unsafe_indices) - method. This makes it explicit that this implementation does not check if - the slice bounds are concrete or within any given object's length. - - - Added a built-in [`sort()`](/mojo/stdlib/builtin/sort/sort) function for - lists of elements that conform to the - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement) - trait.([PR #2609](https://github.com/modularml/mojo/pull/2609)) - - - [`int()`](/mojo/stdlib/builtin/int/int-function) can now take a string and a - specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is - specified, the string will be parsed as if it was an integer literal, with - the base determined by whether the string contains the prefix `"0x"`, - `"0o"`, or `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273), - fixes [#2274](https://github.com/modularml/mojo/issues/2274)) - - - Added the [`bin()`](/mojo/stdlib/builtin/format_int/bin) built-in function - to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603)) - - - Added the [`atof()`](/mojo/stdlib/builtin/string/atof) built-in function, - which can convert a `String` to a `float64`. - ([PR #2649](https://github.com/modularml/mojo/pull/2649)) - - - You can now use the built-in [`any()`](/mojo/stdlib/builtin/bool/any) and - [`all()`](/mojo/stdlib/builtin/bool/all) functions to check for truthy - elements in a collection. Because `SIMD.__bool__()` is now constrained to - `size=1`, You must explicitly use these to get the truthy value of a SIMD - vector with more than one element. This avoids common bugs around implicit - conversion of `SIMD` to `Bool`. - ([PR #2600](https://github.com/modularml/mojo/pull/2600)) - - For example: - - ```mojo - fn truthy_simd(): - var vec = SIMD[DType.int32, 4](0, 1, 2, 3) - if any(vec): - print("any elements are truthy") - if all(vec): - print("all elements are truthy") - ``` - - - [`object`](/mojo/stdlib/builtin/object/) now implements all the bitwise - operators. - ([PR #2324](https://github.com/modularml/mojo/pull/2324)) - - - [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now supports `__contains__()`. - ([PR #2709](https://github.com/modularml/mojo/pull/2709)) For example: - - ```mojo - var x = Tuple(1, 2, True) - if 1 in x: - print("x contains 1") - ``` - - - [`ListLiteral`](/mojo/stdlib/builtin/builtin_list/ListLiteral) and `Tuple` - now only require that element types be `Movable`. Consequently, - `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - - - Added new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers. - -- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and - others: - - - Added new [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload for - `UnsafePointer[Scalar[_]]` pointers. - - - Removed the `get_null()` method from `UnsafePointer` and other pointer - types. Please use the default constructor instead: `UnsafePointer[T]()`. - - - Many functions returning a pointer type have been unified to have a public - API function of `unsafe_ptr()`. - - - The `Tensor.data()` method has been renamed to `unsafe_ptr()`. The return - type is still a `DTypePointer[T]`. - -- Collections: - - - [`List`](/mojo/stdlib/collections/list/List) now has an - [`index()`](/mojo/stdlib/collections/list/List#index) method that allows you - to find the (first) location of an element in a `List` of - `EqualityComparable` types. For example: - - ```mojo - var my_list = List[Int](2, 3, 5, 7, 3) - print(my_list.index(3)) # prints 1 - ``` - - - `List` can now be converted to a `String` with a simplified syntax: - - ```mojo - var my_list = List[Int](2, 3) - print(my_list.__str__()) # prints [2, 3] - ``` - - Note that `List` doesn't conform to the `Stringable` trait yet so you cannot - use `str(my_list)` yet. - ([PR #2673](https://github.com/modularml/mojo/pull/2673)) - - - `List` has a simplified syntax to call the - [`count()`](/mojo/stdlib/collections/list/List#count) method: - `my_list.count(x)`. - ([PR #2675](https://github.com/modularml/mojo/pull/2675)) - - - `List()` now supports `__contains__()`, so you can now use lists with the - `in` operator: - - ```mojo - if x in my_list: - ``` - - ([PR #2667](https://github.com/modularml/mojo/pull/2667)) - - - `List` now has an - [`unsafe_get()`](/mojo/stdlib/collections/list/List#unsafe_get) to get the - reference to an element without bounds check or wraparound for negative - indices. Note that this method is unsafe. Use with caution. - [PR #2800](https://github.com/modularml/mojo/pull/2800)) - - - Added a [`fromkeys()`](/mojo/stdlib/collections/dict/Dict#fromkeys) method - to `Dict` to return a `Dict` with the specified keys and values. - ([PR 2622](https://github.com/modularml/mojo/pull/2622)) - - - Added a [`clear()`](/mojo/stdlib/collections/dict/Dict#clear) method to - `Dict`. ([PR 2627](https://github.com/modularml/mojo/pull/2627)) - - - `Dict` now supports [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) - for its `items()` and `values()` iterators. - ([PR #2340](https://github.com/modularml/mojo/pull/2340)) - - - `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. - Note that `Dict` does not conform to the `Stringable` trait so - `str(my_dict)` is not possible yet. - ([PR #2674](https://github.com/modularml/mojo/pull/2674)) - - - `Dict` now implements [`get(key)`](/mojo/stdlib/collections/dict/Dict#get) - and `get(key, default)` functions. - ([PR #2519](https://github.com/modularml/mojo/pull/2519)) - - - Added a temporary `__get_ref(key)` method to `Dict`, allowing you to get a - `Reference` to a dictionary value. - - - Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) - type, a stack-allocated list with a static maximum size. - ([PR 2587#](https://github.com/modularml/mojo/pull/2587)) - ([PR #2703](https://github.com/modularml/mojo/pull/2703)) - - - Added a new [`Span`](/mojo/stdlib/utils/span/Span) type for taking slices of - contiguous collections. - ([PR #2595](https://github.com/modularml/mojo/pull/2595)) - -- [`os`](/mojo/stdlib/os/os/) module: - - - The `os` module now provides functionality for adding and removing - directories using [`mkdir()`](/mojo/stdlib/os/os/mkdir) and - [`rmdir()`](/mojo/stdlib/os/os/rmdir). - ([PR #2430](https://github.com/modularml/mojo/pull/2430)) - - - Added the [`os.path.getsize()`](/mojo/stdlib/os/path/path/getsize) function, - which gives the size in bytes of the file identified by the path. - ([PR 2626](https://github.com/modularml/mojo/pull/2626)) - - - Added [`os.path.join()`](/mojo/stdlib/os/path/path/join) function. - ([PR 2792](https://github.com/modularml/mojo/pull/2792)) - - - Added a new [`tempfile`](/mojo/stdlib/tempfile/tempfile/) module, with - `gettempdir()` and `mkdtemp()` functions. - ([PR 2742](https://github.com/modularml/mojo/pull/2742)) - -- [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type: - - - Added [`SIMD.shuffle()`](/mojo/stdlib/builtin/simd/SIMD#shuffle) with - `StaticIntTuple` mask. - ([PR #2315](https://github.com/modularml/mojo/pull/2315)) - - - [`SIMD.__bool__()`](/mojo/stdlib/builtin/simd/SIMD#bool) is constrained - such that it only works when `size` is `1`. For SIMD vectors with more than - one element, use [`any()`](/mojo/stdlib/builtin/bool/any) or - [`all()`](/mojo/stdlib/builtin/bool/all). - ([PR #2502](https://github.com/modularml/mojo/pull/2502)) - - - The [`SIMD.reduce_or()`](/mojo/stdlib/builtin/simd/SIMD#reduce_or) and - [`SIMD.reduce_and()`](/mojo/stdlib/builtin/simd/SIMD#reduce_and) methods are - now bitwise operations, and support integer types. - ([PR #2671](https://github.com/modularml/mojo/pull/2671)) - - - Added [`SIMD.__repr__()`](/mojo/stdlib/builtin/simd/SIMD#__repr__) to get - the verbose string representation of `SIMD` types. - ([PR #2728](https://github.com/modularml/mojo/pull/2728)) - -- [`math`](/mojo/stdlib/math/math/) package: - - - The `math.bit` module has been moved to a new top-level - [`bit`](/mojo/stdlib/bit/bit/) module. The following functions in this - module have been renamed: - - `ctlz` -> `countl_zero` - - `cttz` -> `countr_zero` - - `bit_length` -> `bit_width` - - `ctpop` -> `pop_count` - - `bswap` -> `byte_swap` - - `bitreverse` -> `bit_reverse` - - - The `math.rotate_bits_left()` and `math.rotate_bits_right()` functions have - been moved to the `bit` module. - - - The `is_power_of_2()` function in the `math` module is now called - `is_power_of_two()` and located in the `bit` module. - - - The `abs()`, `round()`, `min()`, `max()`, `pow()`, and `divmod()` functions - have moved from `math` to `builtin`, so you no longer need to import these - functions. - - - The `math.tgamma()` function has been renamed to - [`math.gamma()`](/mojo/stdlib/math/math/gamma) to conform with Python's - naming. - - - The implementation of the following functions have been moved from the - `math` module to the new [`utils.numerics`](/mojo/stdlib/utils/numerics/) - module: `isfinite()`, `isinf()`, `isnan()`, `nan()`, `nextafter()`, and - `ulp()`. The functions continue to be exposed in the `math` module. - - - [`math.gcd()`](/mojo/stdlib/math/math/gcd) now works on negative inputs, and - like Python's implementation, accepts a variadic list of integers. New - overloads for a `List` or `Span`of integers are also added. - ([PR #2777](https://github.com/modularml/mojo/pull/2777)) - -- Async and coroutines: - - - [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) now requires a - lifetime parameter. This parameter is set automatically by the parser when - calling an async function. It contains the lifetimes of all the arguments - and any lifetime accesses by the arguments. This ensures that argument - captures by async functions keep the arguments alive as long as the - coroutine is alive. - - - Async function calls are no longer allowed to borrow non-trivial - register-passable types. Because async functions capture their arguments but - register-passable types don't have lifetimes (yet), Mojo is not able to - correctly track the reference, making this unsafe. To cover this safety gap, - Mojo has temporarily disallowed binding non-trivial register-passable types - to borrowed arguments in async functions. - -- Miscellaneous: - - - Added an [`InlineArray`](/mojo/stdlib/utils/static_tuple/InlineArray) type - that works on memory-only types. Compare with the existing - [`StaticTuple`](/mojo/stdlib/utils/static_tuple/StaticTuple) type, which is - conceptually an array type, but only works on `AnyTrivialRegType`. - ([PR #2294](https://github.com/modularml/mojo/pull/2294)) - - - The [`base64`](/mojo/stdlib/base64/) package now includes encoding and - decoding support for both the Base64 and Base16 encoding schemes. - ([PR #2364](https://github.com/modularml/mojo/pull/2364)) - ([PR #2584](https://github.com/modularml/mojo/pull/2584)) - - - The `take()` function in [`Variant`](/mojo/stdlib/utils/variant/Variant) and - [`Optional`](/mojo/stdlib/collections/optional/Optional) has been renamed to - `unsafe_take()`. - - - The `get()` function in `Variant` has been replaced by `__getitem__()`. That - is, `v.get[T]()` should be replaced with `v[T]`. - - - Various functions in the `algorithm` module are now built-in functions. This - includes `sort()`, `swap()`, and `partition()`. `swap()` and `partition()` - will likely shuffle around as we're reworking our built-in `sort()` function - and optimizing it. - -- `infinity` and `NaN` are now correctly handled in - [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) - and an `inf` function has been added to `utils/numerics.mojo`. - ([PR #2375](https://github.com/modularml/mojo/pull/2375)) - -### Tooling changes - -- Invoking `mojo package my-package -o my-dir` on the command line, where - `my-package` is a Mojo package source directory, and `my-dir` is an existing - directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. - Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. - -- The Mojo Language Server now reports a warning when a local variable is - unused. - -- Several `mojo` subcommands now support a `--diagnostic-format` option that - changes the format with which errors, warnings, and other diagnostics are - printed. By specifying `--diagnostic-format json` on the command line, errors - and other diagnostics will be output in a structured - [JSON Lines](https://jsonlines.org) format that is easier for machines to - parse. - - The full list of subcommands that support `--diagnostic-format` is as follows: - `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. - Further, the `mojo test --json` option has been subsumed into this new option; - for the same behavior, run `mojo test --diagnostic-format json`. - - Note that the format of the JSON output may change; we don't currently - guarantee its stability across releases of Mojo. - -- A new `--validate-doc-strings` option has been added to `mojo` to emit errors - on invalid doc strings instead of warnings. - -- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to - `--diagnose-missing-doc-strings`. - -- A new decorator, `@doc_private`, was added that can be used to hide a - declaration from being generated in the output of `mojo doc`. It also removes - the requirement that the declaration has documentation (for example, when used - with `--diagnose-missing-doc-strings`). - -- Debugger users can now set breakpoints on function calls in O0 builds even if - the call has been inlined by the compiler. - -- The Mojo Language Server now supports renaming local variables. - -### Other changes - -#### ❌ Removed - -- The `@unroll` decorator has been deprecated and removed. The decorator was - supposed to guarantee that a decorated loop would be unrolled, or else the - compiler would error. In practice, this guarantee was eroded over time, as - a compiler-based approach cannot be as robust as the Mojo parameter system. - In addition, the `@unroll` decorator did not make the loop induction variables - parameter values, limiting its usefulness. Please see `@parameter for` for a - replacement! - -- The method `object.print()` has been removed. Since `object` now conforms to - the `Stringable` trait, you can use `print(my_object)` instead. - -- The following functions have been removed from the math module: - - `clamp()`; use the new `SIMD.clamp()` method instead. - - `round_half_down()` and `round_half_up()`; these can be trivially implemented - using the `ceil()` and `floor()` functions. - - `add()`, `sub()`, `mul()`, `div()`, `mod()`, `greater()`, `greater_equal()`, - `less()`, `less_equal()`, `equal()`, `not_equal()`, `logical_and()`, - `logical_xor()`, and `logical_not()`; Instead, users should rely directly on - the corresponding operators (`+`, `-`, `*`, `/`, `%`, `>`, `>=`, `<`, `<=`, - `==`, `!=`, `&`, `^`, and `~`). - - `identity()` and `reciprocal()`; users can implement these trivially. - - `select()`; removed in favor of using `SIMD.select()` directly. - - `is_even()` and `is_odd()`; these can be trivially implemented using bitwise - `&` with `1`. - - `roundeven()`; the new `SIMD.roundeven()` method now provides the identical - functionality. - - `div_ceil()`; use the new `ceildiv()` function. - - `rotate_left()` and `rotate_right()`; the same functionality is available in - the builtin `SIMD.rotate_{left,right}()` methods for `SIMD` types, and the - `bit.rotate_bits_{left,right})()` methods for `Int`. - - An overload of `math.pow()` taking an integer parameter exponent. - - `align_down_residual()`; it can be trivially implemented using - `align_down()`. - - `all_true()`, `any_true()`, and `none_true()`; use `SIMD.reduce_and()` and - `SIMD.reduce_or()` directly. - - `reduce_bit_count()`; use the new `SIMD.reduce_bit_count()` directly. - - `rint()` and `nearbyint()`; use `round()` or `SIMD.roundeven()` as - appropriate. - -- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's - method is no longer available. This method was limited to degree 10 or less, - underutilized, and its performance unclear. In the future, this might be - reintroduced with an improved implementation if needed, when better - performance benchmarking infrastructure is available. The default behavior of - `math.polynomial.polynomial_evaluate()` is unchanged (Horner's method). - -- The `math.bit.select()` and `math.bit.bit_and()` functions have been removed. - The same functionality is available in the builtin `SIMD.select` and - `SIMD.__and__()` methods, respectively. - -- The `math.limit` module has been removed. The same functionality is available - as follows: - - `math.limit.inf()`: use `utils.numerics.max_or_inf()` - - `math.limit.neginf()`: use `utils.numerics.min_or_neg_inf()` - - `math.limit.max_finite()`: use `utils.numerics.max_finite()` - - `math.limit.min_finite()`: use `utils.numerics.min_finite()` - -- The `tensor.random` module has been removed. The same functionality is now - accessible via the [`Tensor.rand()`](/mojo/stdlib/tensor/tensor/Tensor#rand) - and [`Tensor.randn()`](/mojo/stdlib/tensor/tensor/Tensor#randn) static - methods. - -- The builtin `SIMD` struct no longer conforms to `Indexer`; users must - explicitly cast `Scalar` values using `int`. - -#### 🛠️ Fixed - -- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential - variant crashing the compiler. -- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on - simple trait definitions. -- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using - `//` on `FloatLiteral` in alias expression. -- Made several improvements to dictionary performance. Dicts with integer keys - are most heavily affected, but large dicts and dicts with large values - will also see large improvements. -- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` - to include calling location. - -### Special thanks - -Special thanks to our community contributors: - -[@rd4com](https://github.com/rd4com), -[@toiletsandpaper](https://github.com/toiletsandpaper), -[@helehex](https://github.com/helehex), -[@artemiogr97](https://github.com/artemiogr97), -[@mikowals](https://github.com/mikowals), -[@kernhanda](https://github.com/kernhanda), [@lsh](https://github.com/lsh), -[@LJ-9801](https://github.com/LJ-9801), -[@YichengDWu](https://github.com/YichengDWu), -[@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), -[@fknfilewalker](https://github.com/fknfilewalker), -[@jayzhan211](https://github.com/jayzhan211), -[@martinvuyk](https://github.com/martinvuyk), -[@ChristopherLR](https://github.com/ChristopherLR), -[@mzaks](https://github.com/mzaks), [@bgreni](https://github.com/bgreni), -[@Brian-M-J](https://github.com/Brian-M-J), -[@leandrolcampos](https://github.com/leandrolcampos) - ## v24.3 (2024-05-02) ### ✨ Highlights @@ -967,7 +159,7 @@ Special thanks to our community contributors: This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that `__call_location()` only works in `@always_inline` or `@always_inline("nodebug")` functions. It gives incorrect results if placed in - an `@always_inline` function that's called _from_ an + an `@always_inline` function that's called *from* an `@always_inline("nodebug")` function. This feature is still evolving and for the time being you need to explicitly @@ -1622,7 +814,7 @@ fixed in a future release. type, a register-passable alternative to [`Optional`](/mojo/stdlib/collections/optional/Optional). -- The [`ulp()`](/mojo/stdlib/utils/numerics/ulp) function has been added to the +- The [`ulp()`](/mojo/stdlib/math/math/ulp) function has been added to the `math` module. This allows you to get the units of least precision (or units of last place) of a floating point value. @@ -1711,11 +903,10 @@ fixed in a future release. from buffer import parallel_memcpy ``` - - The [`rand()`](/mojo/stdlib/tensor/tensor/Tensor#rand) and - [`randn()`](/mojo/stdlib/tensor/tensor/Tensor#randn) functions from the - `random` package that return a `Tensor` have moved to the `tensor` package. - Note that the overloads that write to a `DTypePointer` remain in the - `random` package. + - The [`rand()`](/mojo/stdlib/tensor/random/rand) and + [`randn()`](/mojo/stdlib/tensor/random/randn) functions from the `random` + package that return a `Tensor` have moved to the `tensor` package. Note that + the overloads that write to a `DTypePointer` remain in the `random` package. If you happen to be using both versions in the same source file, you can import them both using the `import as` syntax: @@ -1733,9 +924,9 @@ fixed in a future release. from os import abort ``` - - The [`isinf()`](/mojo/stdlib/utils/numerics/isfinite) and - [`isfinite()`](/mojo/stdlib/utils/numerics/isfinite) methods have been moved - from `math.limits` to the `math` module. + - The [`isinf()`](/mojo/stdlib/math/math/isinf) and + [`isfinite()`](/mojo/stdlib/math/math/isfinite) methods have been moved from + `math.limits` to the `math` module. ```mojo from math import ininf, isfinite @@ -1793,8 +984,8 @@ fixed in a future release. - The [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload that worked on [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) types has been removed in favor - of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/LegacyPointer) - and [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): + of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer) and + [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): ```mojo # Doesn't work @@ -2073,14 +1264,14 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `UnusualSlice` constructor. - The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/reference/Reference) instead of having to + [`Reference`](/mojo/stdlib/memory/reference/reference) instead of having to return an MLIR internal reference type. - Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) method, for moving a value from one pointer memory location to another. -- Added built-in [`hex()`](/mojo/stdlib/builtin/format_int/hex) function, which - can be used to format any value whose type implements the +- Added built-in [`hex()`](/mojo/stdlib/builtin/hex/hex) function, which can be + used to format any value whose type implements the [`Intable`](/mojo/stdlib/builtin/int/Intable) trait as a hexadecimal string. - [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now implements @@ -2358,7 +1549,7 @@ experience without dedicated sugar. ### ⭐️ New - A new Mojo-native dictionary type, - [`Dict`](/mojo/stdlib/collections/dict) for storing key-value pairs. + [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. `Dict` stores values that conform to the [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. Keys need to conform to the new @@ -2404,7 +1595,7 @@ experience without dedicated sugar. specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are _not enabled_ + before the program exits. By default, assertions are *not enabled* in the standard library right now for performance reasons. - The Mojo Language Server now implements the References request. IDEs use @@ -2514,7 +1705,7 @@ experience without dedicated sugar. array slices. While this is a major step forward for the lifetimes system in Mojo, it is - still _very_ early and awkward to use. Notably, there is no syntactic sugar + still *very* early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now. @@ -2837,13 +2028,13 @@ experience without dedicated sugar. - Traits have arrived! - You can now define a _trait_, which consists of a required set of method - prototypes. A struct can _conform to_ the trait by implementing these methods. + You can now define a *trait*, which consists of a required set of method + prototypes. A struct can *conform to* the trait by implementing these methods. This lets you write generic functions that work on any structs that conform to a given trait. The following section gives a brief overview of traits—see the - [Mojo Manual](/mojo/manual/traits) and this + [Mojo Manual](/mojo/manual/traits.html) and this [traits blog post](https://modul.ar/traits-blog) for more details! Traits are declared with the `trait` keyword. The bodies of traits should @@ -2906,7 +2097,7 @@ experience without dedicated sugar. the_parents(x) ``` - For more information, see the [Traits page](/mojo/manual/traits) + For more information, see the [Traits page](/mojo/manual/traits.html) in the Mojo Manual. - A fundamental `Destructable` trait has been added to the language. This is a @@ -2958,9 +2149,9 @@ experience without dedicated sugar. ### ⭐️ New - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include _everything_ about Mojo yet, but it includes a lot, - and more than the original programming - manual (now deprecated). + It doesn't include *everything* about Mojo yet, but it includes a lot, + and more than the original [programming + manual](/mojo/programming-manual.html) (now deprecated). Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love @@ -2981,7 +2172,7 @@ experience without dedicated sugar. ``` In the first signature for `eat()`, the `b` parameter isn't bound, so it's - _implicitly_ added as an input parameter on the function. + *implicitly* added as an input parameter on the function. In the second signature for `eat()`, the author has explicitly defined an input parameter (`_b`), which is bound to the second parameter on the argument @@ -3091,7 +2282,7 @@ the previous "read to EOF" behavior when size is negative. let data = binary_path.read_bytes() ``` -- [`Tensor`](/mojo/stdlib/tensor/tensor) has new `save()` and `load()` +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` methods to save and load to file. These methods preserve shape and datatype information. For example: @@ -3104,7 +2295,7 @@ the previous "read to EOF" behavior when size is negative. - Subscripting added to [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe/LegacyPointer): + [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer): ```mojo let p = DTypePointer[DType.float16].alloc(4) @@ -3165,15 +2356,15 @@ the previous "read to EOF" behavior when size is negative. print(len(nums)) ``` -- The assert functions in the [`testing`](/mojo/stdlib/testing/testing) +- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) package now raise an `Error` when the assertion fails instead of returning a `Bool` for whether the assertion succeeded or not. -- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases) type are no +- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no longer (implicitly) assumed to be register-passable. A new `AnyRegType` type is used to represent generic types that are register passable. -- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark) +- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) report is now an argument instead of a parameter: ```mojo @@ -3226,8 +2417,8 @@ the previous "read to EOF" behavior when size is negative. - There is an issue affecting Jupyter notebooks that use autotuning and traits. This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the _Matrix multiplication in - Mojo_ notebook. + outside of the notebooks. This issue affects the *Matrix multiplication in + Mojo* notebook. ## v0.5.0 (2023-11-2) @@ -3242,7 +2433,7 @@ the previous "read to EOF" behavior when size is negative. function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. -- Mojo now supports compile-time _keyword parameters_, in addition to existing +- Mojo now supports compile-time *keyword parameters*, in addition to existing support for [keyword arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For example: @@ -3269,8 +2460,8 @@ the previous "read to EOF" behavior when size is negative. KwParamStruct[msg="hello", a=42]() # prints 'hello 42' ``` - For more detail, see the [Mojo - Manual](/mojo/manual/parameters/#optional-parameters-and-keyword-parameters). + For more detail, see the [programming + manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). For the time being, the following notable limitations apply: @@ -3348,7 +2539,7 @@ the previous "read to EOF" behavior when size is negative. pass ``` -- The [`benchmark`](/mojo/stdlib/benchmark/benchmark) module has been +- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been simplified and improved so you can now run: ```mojo @@ -3415,14 +2606,14 @@ the previous "read to EOF" behavior when size is negative. CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' ``` -- [`Tensor`](/mojo/stdlib/tensor/tensor) has new `fromfile()` and +- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and `tofile()` methods to save and load as bytes from a file. - The built-in `print()` function now works on the - [`Tensor`](/mojo/stdlib/tensor/tensor) type. + [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. -- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape) and - [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape) now have constructors +- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and + [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list/List) and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. @@ -3556,7 +2747,7 @@ the previous "read to EOF" behavior when size is negative. print(DefaultParams["meow"]().message) # prints 'meow' ``` -- The new [`file`](/mojo/stdlib/builtin/file) module adds basic file I/O +- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O support. You can now write: ```mojo @@ -3581,7 +2772,7 @@ the previous "read to EOF" behavior when size is negative. return self^ ``` - Here Mojo _cannot_ invoke a noop `__exit__` method because the context + Here Mojo *cannot* invoke a noop `__exit__` method because the context manager is consumed by the `__enter__` method. This can be used for types (like file descriptors) that are traditionally used with `with` statements, even though Mojo's guaranteed early destruction doesn't require that. @@ -3856,7 +3047,7 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco value from one location to another. For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life#move-constructors). + [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). - The Error type in Mojo has changed. Instead of extracting the error message using `error.value` you will now extract the error message using @@ -3911,23 +3102,23 @@ All earlier releases were considered version 0.1. ### ⭐️ New - A new `clobber_memory` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark) module. + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The clobber memory function tells the system to flush all memory operations at the specified program point. This allows you to benchmark operations without the compiler reordering memory operations. - A new `keep` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark) module. The `keep` + [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` function tries to tell the compiler not to optimize the variable away if not used. This allows you to avoid compiler's dead code elimination mechanism, with a low footprint side effect. - New `shift_right` and `shift_left` functions have been added to the - [`simd`](/mojo/stdlib/builtin/simd) module. They shift the elements in + [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in a SIMD vector right/left, filling elements with zeros as needed. - A new `cumsum` function has been added to the - [`reduction`](/mojo/stdlib/algorithm/reduction) module that computes + [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes the cumulative sum (also known as scan) of input elements. - Mojo Jupyter kernel now supports code completion. @@ -4235,7 +3426,7 @@ All earlier releases were considered version 0.1. dependencies between global variables, and their destructors are called in the reverse order. -- The Mojo programming manual is now written +- The [Mojo programming manual](/mojo/programming-manual.html) is now written as a Jupyter notebook, and available in its entirety in the Mojo Playground (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of the same material, but it was not up-to-date.) @@ -4359,7 +3550,7 @@ possible to write the following: ``` For details on the overload resolution logic, see the Mojo Manual section on - [parameters](/mojo/manual/parameters/#overloading-on-parameters). + [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). - A new `cost_of()` function has been added to `Autotune`. This meta-function must be invoked at compile time, and it returns the number of MLIR operations @@ -4491,7 +3682,7 @@ only in declared parameter names, e.g. the following now works correctly: ``` When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and) + [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) value (this is common in initializers for example), it is allowed to do whatever it wants with the value and destroy it when it is finished. In order to support this, @@ -4649,7 +3840,7 @@ only in declared parameter names, e.g. the following now works correctly: optimized Matmul implementation is 3x faster. - Renamed the [`^` postfix -operator](/mojo/manual/values/ownership#transfer-arguments-owned-and) +operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) from "consume" to "transfer." #### 🛠️ Fixed @@ -4848,7 +4039,7 @@ busy this week. is currently unsupported). These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to _not_ capture values + types, with the caveat that closures are known to *not* capture values correctly. Be very careful with interesting types in the vicinity of a closure! diff --git a/docs/changelog.md b/docs/changelog.md index 98426ad668..5ecddc0d69 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,24 +14,668 @@ what we publish. ## UNRELEASED +### 🔥 Legendary + ### ⭐️ New - `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. ([PR #2701](https://github.com/modularml/mojo/pull/2701) by [@jayzhan211](https://github.com/jayzhan211)) -- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` - for convenient interoperability with C APIs. +- Add a `sort` function for list of `ComparableCollectionElement`s. + [PR #2609](https://github.com/modularml/mojo/pull/2609) by + [@mzaks](https://github.com/mzaks) + +- Mojo functions can return an auto-dereferenced refeference to storage with a + new `ref` keyword in the result type specifier. For example: + + ```mojo + struct Pair: + var first: Int + var second: Int + fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + return self.first + fn show_mutation(): + var somePair = ... + get_first_ref(somePair) = 1 + ``` + + This approach provides a general way to return an "automatically dereferenced" + reference of a given type. Notably, this eliminates the need for + `__refitem__` to exist. `__refitem__` has thus been removed and replaced with + `__getitem__` that returns a reference. + +- Mojo has introduced `@parameter for`, a new feature for compile-time + programming. `@parameter for` defines a for loop where the sequence and the + induction values in the sequence must be parameter values. For example: + + ```mojo + fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") + ``` + + Currently, `@parameter for` requires the sequence's `__iter__` method to + return a `_StridedRangeIterator`, meaning the induction variables must be + `Int`. The intention is to lift these restrictions in the future. + +- Mojo added support for the inferred parameters. Inferred parameters must + appear at the beginning of the parameter list and cannot be explicitly + specified by the user. They are declared to the left of a `//` marker, much + like positional-only parameters. This allows programmers to define functions + with dependent parameters to be called without the caller specifying all the + necessary parameters. For example: + + ```mojo + fn parameter_simd[dt: DType, //, value: Scalar[dt]](): + print(value) + + fn call_it(): + parameter_simd[Int32(42)]() + ``` + + In the above example, `Int32(42)` is passed directly into `value`, the first + non-inferred parameter. `dt` is inferred from the parameter itself to be + `DType.int32`. + + This also works with structs. For example: + + ```mojo + struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: + pass + + fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` + pass + ``` + + This should make working with dependent parameters more ergonomic. + +- Mojo now allows functions overloaded on parameters to be resolved when forming + references to, but not calling, those functions. For example, the following + now works: + + ```mojo + fn overloaded_parameters[value: Int32](): + pass + + fn overloaded_parameters[value: Float32](): + pass + + fn form_reference(): + alias ref = overloaded_parameters[Int32()] # works! + ``` + +- Mojo now supports adding a `@deprecated` decorator on structs, functions, + traits, aliases, and global variables. The decorator marks the attached decl + as deprecated and causes a warning to be emitted when the deprecated decl is + referenced in user code. The decorator requires a deprecation message to be + specified as a string literal. + + ```mojo + @deprecated("Foo is deprecated, use Bar instead") + struct Foo: + pass + + fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead + pass + + @deprecated("use another function!") + fn bar(): + pass + + fn techdebt(): + bar() # warning: use another function! + ``` + +- Mojo has changed how `def` arguments are processed. Previously, by default, + arguments to a `def` were treated treated according to the `owned` convention, + which makes a copy of the value, enabling that value to be mutable in the callee. + This "worked", but was a major performance footgun, and required you to declare + non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: + it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local + copy of the value **only if the argument is mutated** in the body of the function. + This improves consistency, performance, and ease of use. + +- `int()` can now take a string and a specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with the + base determined by whether the string contains the prefix `"0x"`, `"0o"`, or + `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2274](https://github.com/modularml/mojo/issues/2274)) + +- Mojo now supports types to opt in to use the `abs` and `round` functions by + implementing the `__abs__` and `__round__` methods (i.e. by conforming to the + new `Absable` and `Roundable` traits), respectively, e.g.: + + ```mojo + from math import sqrt + + @value + struct Complex(Absable, Roundable): + var re: Float64 + var im: Float64 + + fn __abs__(self) -> Self: + return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) + + fn __round__(self) -> Self: + return Self(round(self.re), round(self.im)) + ``` + +- User defined types can now also opt in to use the `pow` function by + implementing the `__pow__` method (and thus conforming to the new `Powable` + trait). As before, these types will also benefit from being able to use the + `**` operator. + +- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` + functions in the `math` module by implementing the `__floor__()`, + `__ceil__()`, and `__trunc__()` methods (and so conforming to the new + `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). + For example: + + ```mojo + from math import Ceilable, Floorable, Truncable, ceil, floor, trunc + + @value + struct Complex(Ceilable, Floorable, Truncable): + var re: Float64 + var im: Float64 + + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) + + fn __floor__(self) -> Self: + return Self(floor(re), floor(im)) + + fn __trunc__(self) -> Self: + return Self(trunc(re), trunc(im)) + ``` + +- You can now use the builtin `any()` and `all()` functions to check for truthy + elements in a collection. Because `SIMD.__bool__()` is now constrained to + `size=1`, You must explicity use these to get the truthy value of a SIMD + vector. This avoids common bugs around implicit conversion of `SIMD` to + `Bool`. + ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) + + For example: + + ```mojo + fn truthy_simd(): + var vec = SIMD[DType.int32, 4](0, 1, 2, 3) + if any(vec): + print("any elements are truthy") + if all(vec): + print("all elements are truthy") + ``` + +- Add an `InlinedArray` type that works on memory-only types. + Compare with the existing `StaticTuple` type, which is conceptually an array + type, but only worked on `AnyTrivialRegType`. + ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) + +- Base64 decoding support has been added. + ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) + +- Add Base16 encoding and decoding support. + ([PR #2584](https://github.com/modularml/mojo/pull/2584) + by [@kernhanda](https://github.com/kernhanda)) + +- Add `repr()` function and `Representable` trait. + ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Add `SIMD.shuffle()` with `StaticIntTuple` mask. + ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) + +- Invoking `mojo package my-package -o my-dir` on the command line, where + `my-package` is a Mojo package source directory, and `my-dir` is an existing + directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. + Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. + +- The Mojo Language Server now reports a warning when a local variable is unused. + +- Implicit variable definitions in a `def` are more flexible: you can now + implicitly declare variables as the result of a tuple return, using + `a,b,c = foo()`, and can now shadow global immutable symbols using + `slice = foo()` without getting a compiler error. + +- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that + allow users to opt into the `math.ceildiv` function. + +- Mojo now allows methods to declare `self` as a `Reference` directly, which + can be useful for advanced cases of parametric mutabilty and custom lifetime + processing. Previously it required the use of an internal MLIR type to + achieve this. + +- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, + not a low-level `__mlir_type.i1` value. + + This improves the ergonomics of spelling out a + `Reference` type explicitly. For example, to define a struct holding a + `Reference`, you can now write: + + ```mojo + struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: + var data: Reference[Int32, is_mutable, lifetime] + ``` + + Or to specify a field that is always immutable, `False` can be specified + as the mutability: + + ```mojo + struct Foo[lifetime: AnyLifetime[False].type]: + var data: Reference[Int32, False, lifetime] + ``` + +- `object` now implements all the bitwise operators. + ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) + +- A new `--validate-doc-strings` option has been added to `mojo` to emit errors + on invalid doc strings instead of warnings. + +- Several `mojo` subcommands now support a `--diagnostic-format` option that + changes the format with which errors, warnings, and other diagnostics are + printed. By specifying `--diagnostic-format json` on the command line, errors + and other diagnostics will be output in a structured + [JSON Lines](https://jsonlines.org) format that is easier for machines to + parse. + + The full list of subcommands that support `--diagnostic-format` is as follows: + `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. + Further, the `mojo test --json` option has been subsumed into this new option; + for the same behavior, run `mojo test --diagnostic-format json`. + + Note that the format of the JSON output may change; we don't currently + guarantee its stability across releases of Mojo. + +- A new decorator, `@doc_private`, was added that can be used to hide a decl + from being generated in the output of `mojo doc`. It also removes the + requirement that the decl has documentation (e.g. when used with + --diagnose-missing-doc-strings). + +- Added a new `Span` type for taking slices of contiguous collections. + ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) + +- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type + in standard library code. + + `StringSlice` is a non-owning reference to encoded string data. Unlike + `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it + points to. + + - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. + - Changed `Formatter.write_str()` to take a safe `StringSlice`. + +- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which + returns a `Span` of the bytes owned by the string. + +- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers + +- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. + +- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), + please use the default constructor instead: `UnsafePointer[T]()`. + +- `Dict` now implements `get(key)` and `get(key, default)` functions. + ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) + +- Debugger users can now set breakpoints on function calls in O0 builds even if + the call has been inlined by the compiler. + +- The `os` module now provides functionality for adding and removing directories + using `mkdir` and `rmdir`. + ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) + +- `Dict.__get_ref(key)`, allowing to get references to dictionary values. -- Added `C_char` type alias in `sys.ffi`. +- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters + other than whitespace. In addition, there are now several useful aliases for + whitespace, ASCII lower/uppercase, and so on. + ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) + +- `String` now has a `splitlines()` method, which allows splitting strings at line + boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) + and provides an option to retain or remove the line break characters. + ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) + +- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. + ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. + ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) + +- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. + Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` + is not possible yet. + ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `List()` now supports `__contains__`. + ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) + +- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) + type, a stack-allocated list with a static maximum size. + ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `InlineList()` now supports `__contains__`, `__iter__`. + ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) + +- `List` now has an `index` method that allows one to find the (first) location + of an element in a `List` of `EqualityComparable` types. For example: + + ```mojo + var my_list = List[Int](2, 3, 5, 7, 3) + print(my_list.index(3)) # prints 1 + ``` + +- `List` can now be converted to a `String` with a simplified syntax: + + ```mojo + var my_list = List[Int](2, 3) + print(my_list.__str__()) # prints [2, 3] + ``` + + Note that `List` doesn't conform to the `Stringable` trait yet so you cannot + use `str(my_list)` yet. + ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added the `Indexer` trait to denote types that implement the `__index__()` + method which allows these types to be accepted in common `__getitem__` and + `__setitem__` implementations, as well as allow a new builtin `index` function + to be called on them. Most stdlib containers are now able to be indexed by + any type that implements `Indexer`. For example: + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + struct MyList: + var data: List[Int] + + fn __init__(inout self): + self.data = List[Int](1, 2, 3, 4) + + fn __getitem__[T: Indexer](self, idx: T) -> T: + return self.data[index(idx)] + + print(MyList()[AlwaysZero()]) # prints `1` + ``` + + ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + + Types conforming to the `Indexer` trait are implicitly convertible to Int. + This means you can write generic APIs that take `Int` instead of making them + take a generic type that conforms to `Indexer`, e.g. + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + @value + struct Incrementer: + fn __getitem__(self, idx: Int) -> Int: + return idx + 1 + + var a = Incrementer() + print(a[AlwaysZero()]) # works and prints 1 + ``` + +- `StringRef` now implements `strip()` which can be used to remove leading and + trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) + by [@fknfilewalker](https://github.com/fknfilewalker)) + +- The `bencher` module as part of the `benchmark` package is now public + and documented. This module provides types such as `Bencher` which provides + the ability to execute a `Benchmark` and allows for benchmarking configuration + via the `BenchmarkConfig` struct. + +- Added the `bin()` builtin function to convert integral types into their binary + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) + by [@bgreni](https://github.com/bgreni)) + +- Added `atof()` function which can convert a `String` to a `float64`. + ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) + +- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) + by [@rd4com](https://github.com/rd4com)) For example: + + ```mojo + var x = Tuple(1, 2, True) + if 1 in x: + print("x contains 1") + ``` + +- Added `os.getsize` function, which gives the size in bytes of a path. + ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) + +- `List` now has a method `unsafe_get` to get the reference to an + element without bounds check or wraparound for negative indices. + Note that this method is unsafe. Use with caution. + ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys + and value. + ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `clear` method to `Dict`. + ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `os.path.join` function. + ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) + +- `StringRef` now implements `startswith()` and `endswith()`. + ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) + +- The Mojo Language Server now supports renaming local variables. + +- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. + ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. +([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) ### 🦋 Changed +- `Coroutine` now requires a lifetime parameter. This parameter is set + automatically by the parser when calling an async function. It contains the + lifetimes of all the arguments and any lifetime accesses by the arguments. + This ensures that argument captures by async functions keep the arguments + alive as long as the coroutine is alive. + +- Async function calls are no longer allowed to borrow non-trivial + register-passable types. Because async functions capture their arguments but + register-passable types don't have lifetimes (yet), Mojo is not able to + correctly track the reference, making this unsafe. To cover this safety gap, + Mojo has temporarily disallowed binding non-trivial register-passable types + to borrowed arguments in async functions. + +- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids + binding non-trivial register-passable types to `AnyTrivialRegType`. This + closes a major safety hole in the language. Please use `AnyType` for generic + code going forward. + +- The `let` keyword has been completely removed from the language. We previously + removed `let` declarations but still provided an error message to users. Now, + it is completely gone from the grammar. Long live `var`! + +- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved + from `math` to `builtin`, so you no longer need to do + `from math import abs, round, min, max, divmod, pow`. + + - The `is_power_of_2` function in the `math` module is now called + `is_power_of_two` and located in the `bit` module. + +- Many functions returning a pointer type have been unified to have a public + API function of `unsafe_ptr()`. + +- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to + `--diagnose-missing-doc-strings`. + +- The `take` function in `Variant` and `Optional` has been renamed to + `unsafe_take`. + +- The `get` function in `Variant` has been replaced by `__refitem__`. That is, + `v.get[T]()` should be replaced with `v[T]`. + +- Various functions in the `algorithm` module are now moved to be + builtin-functions. This includes `sort`, `swap`, and `partition`. + `swap` and `partition` will likely shuffle around as we're reworking + our builtin `sort` function and optimizing it. + +- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, + explicitly use `any()` or `all()`. + ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) + +- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise + operations, and support integer types. + ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) + +- `ListLiteral` and `Tuple` now only require that element types be `Movable`. + Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. + - Continued transition to `UnsafePointer` and unsigned byte type for strings: - - Rename `String._as_ptr()` to `String.unsafe_ptr()` - - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `DTypePointer[DType.int8]`) + - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) + - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was + `DTypePointer`) + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was + `DTypePointer`). + - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now + returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) + - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was + `DTypePointer[DType.int8]`). + - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has + the same behavior. + +- Added `String.isspace()` method conformant with Python's universal separators. + +- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), + use `String.isspace()` instead. + +- `String.split()` now defaults to whitespace and has pythonic behavior in that + it removes all adjacent whitespaces by default. + +- Added `UnsafePointer.offset()` method. + +- The `math.bit` module has been moved to a new top-level `bit` module. The + following functions in this module have been renamed: + - `ctlz` -> `countl_zero` + - `cttz` -> `countr_zero` + - `bit_length` -> `bit_width` + - `ctpop` -> `pop_count` + - `bswap` -> `byte_swap` + - `bitreverse` -> `bit_reverse` + +- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been + moved to the `bit` module. + +- The `math.tgamma` function has been renamed to `math.gamma` to conform with + Python's naming. + +- The implementation of the following functions have been moved from the `math` + module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, + `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the + `math` module. + +- `InlinedString` has been renamed to `InlineString` to be consistent with other + types. + +- The `Slice.__len__` function has been removed and `Slice` no longer conforms + to the `Sized` trait. This clarifies the ambiguity of the semantics: the + length of a slice always depends on the length of the object being sliced. + Users that need the existing functionality can use the `Slice.unsafe_indices` + method. This makes it explicit that this implementation does not check if the + slice bounds are concrete or within any given object's length. + +- Implicit conversion to `String` is now removed for builtin classes/types. + One should use `str(...)` explicitly to convert to `String`. + +- `math.gcd` now works on negative inputs, and like Python's implementation, + accepts a variadic list of integers. New overloads for a `List` or `Span`of + integers are also added. + ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) ### ❌ Removed +- The `@unroll` decorator has been deprecated and removed. The decorator was + supposed to guarantee that a decorated loop would be unrolled, or else the + compiler would error. In practice, this guarantee was eroded over time, as + a compiler-based approach cannot be as robust as the Mojo parameter system. + In addition, the `@unroll` decorator did not make the loop induction variables + parameter values, limiting its usefulness. Please see `@parameter for` for a + replacement! + +- The method `object.print()` has been removed. Since now, `object` has the + `Stringable` trait, you can use `print(my_object)` instead. + +- The following functions have been removed from the math module: + - `clamp`; use the new `SIMD.clamp` method instead. + - `round_half_down` and `round_half_up`; these can be trivially implemented + using the `ceil` and `floor` functions. + - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, + `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and + `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, + `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, + respectively. + - `identity` and `reciprocal`; users can implement these trivially. + - `select`; in favor of using `SIMD.select` directly. + - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` + with `1`. + - `roundeven`; the new `SIMD.roundeven` method now provides the identical + functionality. + - `div_ceil`; use the new `ceildiv` function. + - `rotate_left` and `rotate_right`; the same functionality is available in the + builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the + `bit.rotate_bits_{left,right}` methods for `Int`. + - an overload of `math.pow` taking an integer parameter exponent. + - `align_down_residual`; it can be trivially implemented using `align_down`. + - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and + `SIMD.reduce_or` directly. + - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. + - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. + +- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's + method is no longer available. This method was limited to degree 10 or less, + underutilized, and its performance unclear. In the future, this might be + reintroduced with an improved implementation if needed, when better + performance benchmarking infrastructure is available. The default behavior of + `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). + +- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The + same functionality is available in the builtin `SIMD.select` and + `SIMD.__and__` methods, respectively. + +- The `math.limit` module has been removed. The same functionality is available + as follows: + - `math.limit.inf`: use `utils.numerics.max_or_inf` + - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` + - `math.limit.max_finite`: use `utils.numerics.max_finite` + - `math.limit.min_finite`: use `utils.numerics.min_finite` + +- The `tensor.random` module has been removed. The same functionality is now + accessible via the `Tensor.rand` and `Tensor.randn` static methods. + +- The builtin `SIMD` struct no longer conforms to `Indexer`; users must + explicitly cast `Scalar` values using `int`. + ### 🛠️ Fixed + +- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential + variant crashing the compiler. +- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on + simple trait definitions. +- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using + `//` on `FloatLiteral` in alias expression. +- Made several improvements to dictionary performance. Dicts with integer keys + are most heavily affected, but large dicts and dicts with large values + will also see large improvements. +- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` + to include calling location. diff --git a/docs/community.mdx b/docs/community.md similarity index 100% rename from docs/community.mdx rename to docs/community.md diff --git a/docs/faq.md b/docs/faq.md index a05a863fe6..59e986cde3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -6,7 +6,7 @@ description: Answers to questions we expect about Mojo. We tried to anticipate your questions about Mojo on this page. If this page doesn't answer all your questions, also check out our [Mojo community -channels](/mojo/community). +channels](/mojo/community.html). ## Motivation @@ -20,7 +20,7 @@ to unify AI software and we can’t do that without a unified language that can scale across the AI infrastructure stack. That said, we don’t plan to stop at AI—the north star is for Mojo to support the whole gamut of general-purpose programming over time. For a longer answer, read [Why -Mojo](/mojo/why-mojo). +Mojo](/mojo/why-mojo.html). ### Why is it called Mojo? @@ -43,8 +43,8 @@ it’s missing. We are guided more by pragmatism than novelty, but Mojo’s use [MLIR](https://mlir.llvm.org/) allows it to scale to new exotic hardware types and domains in a way that other languages haven’t demonstrated (for an example of Mojo talking directly to MLIR, see our [low-level IR in Mojo -notebook](/mojo/notebooks/BoolMLIR)). It also -has caching and distributed compilation built into its +notebook](/mojo/notebooks/BoolMLIR.html)). It also +includes autotuning, and has caching and distributed compilation built into its core. We also believe Mojo has a good chance of unifying hybrid packages in the broader Python community. @@ -84,7 +84,7 @@ Codon and PyPy aim to improve performance compared to CPython, but Mojo’s goal are much deeper than this. Our objective isn’t just to create “a faster Python,” but to enable a whole new layer of systems programming that includes direct access to accelerated hardware, as outlined in [Why -Mojo](/mojo/why-mojo). Our technical implementation +Mojo](/mojo/why-mojo.html). Our technical implementation approach is also very different, for example, we are not relying on heroic compiler and JIT technologies to “devirtualize” Python. @@ -123,7 +123,7 @@ you to try Mojo and if you find it useful, then that's great too. The best place to start is the [Mojo Manual](/mojo/manual). And if you want to see what features are coming in the future, take a look at [the -roadmap](/mojo/roadmap). +roadmap](/mojo/roadmap.html). ### What does it mean that Mojo is designed for MLIR? @@ -138,7 +138,7 @@ ground up with MLIR design principles. This means that Mojo not only offers high-performance compilation for heterogeneous hardware, but it also provides direct programming support for the MLIR intermediate representations. For a simple example of Mojo talking directly to MLIR, see our [low-level IR in Mojo -notebook](/mojo/notebooks/BoolMLIR). +notebook](/mojo/notebooks/BoolMLIR.html). ### Is Mojo only for AI or can it be used for other stuff? @@ -165,7 +165,7 @@ language that will support more architectures over time and includes a debugger, a full tool suite, etc. For more about embedded domain-specific languages (EDSLs) like Triton, read the “Embedded DSLs in Python” section of [Why -Mojo](/mojo/why-mojo#embedded-dsls-in-python). +Mojo](/mojo/why-mojo.html#embedded-dsls-in-python). ### How does Mojo help with PyTorch and TensorFlow acceleration? @@ -200,7 +200,7 @@ and build migration tools as the language matures. Yes, we want to enable developers to port code from languages other than Python to Mojo as well. We expect that due to Mojo’s similarity to the C/C++ type systems, migrating code from C/C++ should work well and it’s in [our -roadmap](/mojo/roadmap#cc-interop). +roadmap](/mojo/roadmap.html#cc-interop). ### How does Mojo support hardware lowering? @@ -210,6 +210,12 @@ means that Mojo is easily extensible to any hardware backend. For more information, read about our vision for [pluggable hardware](https://www.modular.com/hardware). +### How does Mojo autotuning work? + +For details about what autotuning capabilities we support so far, check out +the Mojo Manual section on [metaprogramming](/mojo/manual/parameters/). +But stay tuned for more details! + ### Who writes the software to add more hardware support for Mojo? Mojo provides all the language functionality necessary for anyone to extend @@ -270,11 +276,7 @@ dashboard](https://www.modular.com/max/performance). ### How can I get access to the SDK? -Mojo is included with the MAX SDK, which you can [download and use for -free](/max/install). - -Read more about [why Mojo is bundled with -MAX](/max/faq#why-bundle-mojo-with-max). +You can [get the Mojo SDK here](https://developer.modular.com/download)! ### Is the Mojo Playground still available? @@ -307,6 +309,14 @@ and does not require login. Please read the [Mojo SDK License Terms](https://www.modular.com/legal/mojo). +### What does the Mojo SDK ship with? + +The Mojo SDK includes the Mojo standard library and `mojo` command-line tool, +which provides a REPL similar to the `python` command, along with `build`, +`run`, `package`, `doc` and `format` commands. We've also published a [Mojo +language extension for VS +Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + ### What operating systems are supported? Currently, we support Ubuntu Linux 20.04/22.04 (64-bit x86) and macOS (Apple @@ -369,7 +379,7 @@ more detail such as the time spend compiling. Mojo is still in early development and not at a 1.0 version yet. It’s still missing many foundational features, but please take a look at our -[roadmap](/mojo/roadmap) to understand where things are headed. As such, +[roadmap](/mojo/roadmap.html) to understand where things are headed. As such, the language is evolving rapidly and source stability is not guaranteed. ### How often will you be releasing new versions of Mojo? @@ -379,6 +389,20 @@ Please join the [Mojo Discord channel](http://discord.gg/modular) for notifications and [sign up for our newsletter](https://www.modular.com/newsletter) for more coarse-grain updates. +## Mojo Playground {#mojo-playground} + +### What sort of computer is backing each instance in the Mojo Playground? + +The Mojo Playground runs on a fleet of [AWS EC2 +C6i](https://aws.amazon.com/ec2/instance-types/c6i/) (c6i.8xlarge) instances +that is divided among active users. Due to the shared nature of the system, the +number of vCPU cores provided to your session may vary. We guarantee 1 vCPU +core per session, but that may increase when the total number of active users is +low. + +Each user also has a dedicated volume in which you can save your own files that +persist across sessions. + ## Open Source ### Will Mojo be open-sourced? @@ -400,9 +424,9 @@ Clang, Swift, MLIR, etc.). ### Where can I ask more questions or share feedback? If you have questions about upcoming features or have suggestions -for the language, be sure you first read the [Mojo roadmap](/mojo/roadmap), which +for the language, be sure you first read the [Mojo roadmap](roadmap.html), which provides important information about our current priorities and links to our GitHub channels where you can report issues and discuss new features. To get in touch with the Mojo team and developer community, use the resources -on our [Mojo community page](/mojo/community). +on our [Mojo community page](/mojo/community.html). diff --git a/docs/lib.mdx b/docs/lib.md similarity index 84% rename from docs/lib.mdx rename to docs/lib.md index bbbab3104a..93ff45569b 100644 --- a/docs/lib.mdx +++ b/docs/lib.md @@ -5,12 +5,12 @@ hide_table_of_contents: true description: A list of all modules in the Mojo standard library. listing: - id: stdlib - contents: 'stdlib/*/*/index.md' + contents: "stdlib/*/*/index.md" type: grid page-size: 99 --- These are all the modules in the Mojo standard library. -:::🔥#stdlib +:::{#stdlib} ::: diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index f7730cf07e..59e6caffae 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -46,7 +46,7 @@ ":::note\n", "\n", "Mojo is a young language and there are many [features still\n", - "missing](/mojo/roadmap). As such, Mojo is currently **not** meant for\n", + "missing](/mojo/roadmap.html). As such, Mojo is currently **not** meant for\n", "beginners. Even this basics section assumes some programming experience.\n", "However, throughout the Mojo Manual, we try not to assume experience with any\n", "particular language.\n", @@ -147,7 +147,7 @@ "metadata": {}, "source": [ "For more details, see the page about\n", - "[functions](/mojo/manual/functions)." + "[functions](/mojo/manual/functions.html)." ] }, { @@ -237,7 +237,7 @@ "(only the argument and return types must be declared in `fn` functions).\n", "\n", "For more details, see the page about\n", - "[variables](/mojo/manual/variables)." + "[variables](/mojo/manual/variables.html)." ] }, { @@ -303,7 +303,7 @@ "metadata": {}, "source": [ "For more details, see the page about\n", - "[structs](/mojo/manual/structs)." + "[structs](/mojo/manual/structs.html)." ] }, { @@ -398,7 +398,7 @@ "`SomeTrait`. Thus, `fun_with_traits()` is known as a \"generic function\" because\n", "it accepts a _generalized_ type instead of a specific type.\n", "\n", - "For more details, see the page about [traits](/mojo/manual/traits)." + "For more details, see the page about [traits](/mojo/manual/traits.html)." ] }, { @@ -629,7 +629,7 @@ "source": [ "Documenting your code with these kinds of comments (known as \"docstrings\")\n", "is a topic we've yet to fully specify, but you can generate an API reference\n", - "from docstrings using the [`mojo doc` command](/mojo/cli/doc)." + "from docstrings using the [`mojo doc` command](/mojo/cli/doc.html)." ] }, { @@ -700,7 +700,7 @@ "\n", "If you're in the mood to read more, continue through each page of this\n", "Mojo Manual using the buttons at the bottom of each page—the next page from\n", - "here is [Functions](/mojo/manual/functions).\n", + "here is [Functions](/mojo/manual/functions.html).\n", "\n", "Otherwise, here are some other resources to check out:\n", "\n", @@ -716,7 +716,7 @@ " that teach advanced Mojo features.\n", "\n", "- To see all the available Mojo APIs, check out the [Mojo standard library\n", - " reference](/mojo/lib)." + " reference](/mojo/lib.html)." ] } ], diff --git a/docs/manual/control-flow.ipynb b/docs/manual/control-flow.ipynb deleted file mode 100644 index 8ee4dab294..0000000000 --- a/docs/manual/control-flow.ipynb +++ /dev/null @@ -1,887 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Control flow\n", - "description: Mojo control flow statements.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo includes several traditional control flow structures for conditional and\n", - "repeated execution of code blocks.\n", - "\n", - "## The `if` statement\n", - "\n", - "Mojo supports the `if` statement for conditional code execution. With it you can\n", - "conditionally execute an indented code block if a given\n", - "[boolean](/mojo/manual/types#booleans) expression evaluates to `True`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "It is warm.\n", - "The temperature is 77.0 Fahrenheit.\n" - ] - } - ], - "source": [ - "temp_celsius = 25\n", - "if temp_celsius > 20:\n", - " print(\"It is warm.\")\n", - " print(\"The temperature is\", temp_celsius * 9 / 5 + 32, \"Fahrenheit.\" )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can write the entire `if` statement as a single line if all you need to\n", - "execute conditionally is a single, short statement." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "It is warm.\n" - ] - } - ], - "source": [ - "temp_celsius = 22\n", - "if temp_celsius < 15: print(\"It is cool.\") # Skipped because condition is False\n", - "if temp_celsius > 20: print(\"It is warm.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, an `if` statement can include any number of additional `elif`\n", - "clauses, each specifying a boolean condition and associated code block to\n", - "execute if `True`. The conditions are tested in the order given. When a\n", - "condition evaluates to `True`, the associated code block is executed and no\n", - "further conditions are tested.\n", - "\n", - "Additionally, an `if` statement can include an optional `else` clause providing\n", - "a code block to execute if all conditions evaluate to `False`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "It is warm.\n" - ] - } - ], - "source": [ - "temp_celsius = 25\n", - "if temp_celsius <= 0:\n", - " print(\"It is freezing.\")\n", - "elif temp_celsius < 20:\n", - " print(\"It is cool.\")\n", - "elif temp_celsius < 30:\n", - " print(\"It is warm.\")\n", - "else:\n", - " print(\"It is hot.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note TODO\n", - "\n", - "Mojo currently does not support the equivalent of a Python `match` or C `switch`\n", - "statement for pattern matching and conditional execution.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Short-circuit evaluation\n", - "\n", - "Mojo follows [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation)\n", - "semantics for boolean operators. If the first argument to an `or` operator\n", - "evaluates to `True`, the second argument is not evaluated.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Short-circuit \"or\" evaluation\n", - "Executing true_func\n", - "True result\n" - ] - } - ], - "source": [ - "def true_func() -> Bool:\n", - " print(\"Executing true_func\")\n", - " return True\n", - "\n", - "def false_func() -> Bool:\n", - " print(\"Executing false_func\")\n", - " return False\n", - "\n", - "print('Short-circuit \"or\" evaluation')\n", - "if true_func() or false_func():\n", - " print(\"True result\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If the first argument to an `and` operator evaluates to `False`, the second\n", - "argument is not evaluated." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Short-circuit \"and\" evaluation\n", - "Executing false_func\n" - ] - } - ], - "source": [ - "print('Short-circuit \"and\" evaluation')\n", - "if false_func() and true_func():\n", - " print(\"True result\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conditional expressions\n", - "\n", - "Mojo also supports conditional expressions (or what is sometimes called a\n", - "[_ternary conditional operator_](https://en.wikipedia.org/wiki/Ternary_conditional_operator))\n", - "using the syntaxtrue_result if boolean_expression else false_result, just as\n", - "in Python. This is most often used as a concise way to assign one of two\n", - "different values to a variable, based on a boolean condition." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The forecast for today is cool\n" - ] - } - ], - "source": [ - "temp_celsius = 15\n", - "forecast = \"warm\" if temp_celsius > 20 else \"cool\"\n", - "print(\"The forecast for today is\", forecast)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The alternative, written as a multi-line `if` statement, is more verbose." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The forecast for today is cool\n" - ] - } - ], - "source": [ - "if temp_celsius > 20:\n", - " forecast = \"warm\"\n", - "else:\n", - " forecast = \"cool\"\n", - "print(\"The forecast for today is\", forecast)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `while` statement\n", - "\n", - "The `while` loop repeatedly executes a code block while a given boolean\n", - "expression evaluates to `True`. For example, the following loop prints values\n", - "from the Fibonacci series that are less than 50." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 1, 2, 3, 5, 8, 13, 21, 34" - ] - } - ], - "source": [ - "fib_prev = 0\n", - "fib_curr = 1\n", - "\n", - "print(fib_prev, end=\"\")\n", - "while fib_curr < 50:\n", - " print(\",\", fib_curr, end=\"\")\n", - " fib_prev, fib_curr = fib_curr, fib_prev + fib_curr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `continue` statement skips execution of the rest of the code block and\n", - "resumes with the loop test expression." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, 4, 5, " - ] - } - ], - "source": [ - "n = 0\n", - "while n < 5:\n", - " n += 1\n", - " if n == 3:\n", - " continue\n", - " print(n, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `break` statement terminates execution of the loop." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, " - ] - } - ], - "source": [ - "n = 0\n", - "while n < 5:\n", - " n += 1\n", - " if n == 3:\n", - " break\n", - " print(n, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, a `while` loop can include an `else` clause. The body of the `else`\n", - "clause executes when the loop's boolean condition evaluates to `False`, even if\n", - "it occurs the first time tested." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loop completed\n" - ] - } - ], - "source": [ - "n = 5\n", - "\n", - "while n < 4:\n", - " print(n)\n", - " n += 1\n", - "else:\n", - " print(\"Loop completed\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "The `else` clause does _not_ execute if a `break` or `return` statement\n", - "exits the `while` loop.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n" - ] - } - ], - "source": [ - "n = 0\n", - "while n < 5:\n", - " n += 1\n", - " if n == 3:\n", - " break\n", - " print(n)\n", - "else:\n", - " print(\"Executing else clause\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `for` statement\n", - "\n", - "The `for` loop iterates over a sequence, executing a code block for each\n", - "element in the sequence.\n", - "The Mojo `for` loop can iterate over any type that implements an `__iter__()`\n", - "method that returns a type that defines `__next__()` and `__len__()` methods.\n", - "\n", - "### Iterating over Mojo collections\n", - "\n", - "All of the collection types in the [`collections`](/mojo/stdlib/collections)\n", - "module support `for` loop iteration. See the\n", - "[Collection types](/mojo/manual/types#collection-types) documentation for more\n", - "information on Mojo collection types.\n", - "\n", - ":::caution TODO\n", - "\n", - "Iterating over Mojo native collections currently assigns the loop index variable\n", - "a [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", - "item itself. You can access the item using the dereference operator, `[]`, as\n", - "shown in the examples below. This may change in a future version of Mojo.\n", - "\n", - ":::\n", - "\n", - "The following shows an example of iterating over a Mojo\n", - "[`List`](/mojo/stdlib/collections/list/List)." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "California\n", - "Hawaii\n", - "Oregon\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "states = List[String](\"California\", \"Hawaii\", \"Oregon\")\n", - "for state in states:\n", - " print(state[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The same technique works for iterating over a Mojo\n", - "[`Set`](/mojo/stdlib/collections/set/Set)." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "0\n" - ] - } - ], - "source": [ - "from collections import Set\n", - "\n", - "values = Set[Int](42, 0)\n", - "for item in values:\n", - " print(item[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are two techniques for iterating over a Mojo\n", - "[`Dict`](/mojo/stdlib/collections/dict/Dict). The first is to iterate directly\n", - "using the `Dict`, which produces a sequence of the dictionary's keys." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sacramento, California\n", - "Honolulu, Hawaii\n", - "Salem, Oregon\n" - ] - } - ], - "source": [ - "from collections import Dict\n", - "\n", - "capitals = Dict[String, String]()\n", - "capitals[\"California\"] = \"Sacramento\"\n", - "capitals[\"Hawaii\"] = \"Honolulu\"\n", - "capitals[\"Oregon\"] = \"Salem\"\n", - "\n", - "for state in capitals:\n", - " print(capitals[state[]] + \", \" + state[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The second approach to iterating over a Mojo `Dict` is to invoke its\n", - "[`items()`](/mojo/stdlib/collections/dict/Dict#items) method, which produces a\n", - "sequence of [`DictEntry`](/mojo/stdlib/collections/dict/DictEntry) objects.\n", - "Within the loop body, you can then access the `key` and `value` fields of the\n", - "entry." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sacramento, California\n", - "Honolulu, Hawaii\n", - "Salem, Oregon\n" - ] - } - ], - "source": [ - "for item in capitals.items():\n", - " print(item[].value + \", \" + item[].key)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another type of iterable provided by the Mojo standard library is a _range_,\n", - "which is a sequence of integers generated by the\n", - "[`range()`](/mojo/stdlib/builtin/range/range) function. It differs from the\n", - "collection types shown above in that it's implemented as a\n", - "[generator](https://en.wikipedia.org/wiki/Generator_(computer_programming)),\n", - "producing each value as needed rather than materializing the entire sequence\n", - "in memory. Additionally, each value assigned to the loop index variable is\n", - "simply the `Int` value rather than a `Reference` to the value, so you should\n", - "not use the dereference operator on it within the loop. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, 3, 4, " - ] - } - ], - "source": [ - "for i in range(5):\n", - " print(i, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `for` loop control statements\n", - "\n", - "A `continue` statement skips execution of the rest of the code block and\n", - "resumes the loop with the next element of the collection." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, 4, " - ] - } - ], - "source": [ - "for i in range(5):\n", - " if i == 3:\n", - " continue\n", - " print(i, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `break` statement terminates execution of the loop." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, " - ] - } - ], - "source": [ - "for i in range(5):\n", - " if i == 3:\n", - " break\n", - " print(i, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, a `for` loop can include an `else` clause. The body of the `else`\n", - "clause executes after iterating over all of the elements in a collection." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, 3, 4, \n", - "Finished executing 'for' loop\n" - ] - } - ], - "source": [ - "for i in range(5):\n", - " print(i, end=\", \")\n", - "else:\n", - " print(\"\\nFinished executing 'for' loop\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `else` clause executes even if the collection is empty." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finished executing 'for' loop\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "empty = List[Int]()\n", - "for i in empty:\n", - " print(i[])\n", - "else:\n", - " print(\"Finished executing 'for' loop\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "The `else` clause does _not_ execute if a `break` or `return` statement\n", - "terminates the `for` loop.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found a dog\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "animals = List[String](\"cat\", \"aardvark\", \"hippopotamus\", \"dog\")\n", - "for animal in animals:\n", - " if animal[] == \"dog\":\n", - " print(\"Found a dog\")\n", - " break\n", - "else:\n", - " print(\"No dog found\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Iterating over Python collections\n", - "\n", - "The Mojo `for` loop supports iterating over Python collection types. Each item\n", - "retrieved by the loop is a\n", - "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", - "the Python object. Refer to the [Python types](/mojo/manual/python/types)\n", - "documentation for more information on manipulating Python objects from Mojo.\n", - "\n", - "The following is a simple example of iterating over a mixed-type Python list." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "cat\n", - "3.14159\n" - ] - } - ], - "source": [ - "from python import Python\n", - "\n", - "# Create a mixed-type Python list\n", - "py_list = Python.evaluate(\"[42, 'cat', 3.14159]\")\n", - "for py_obj in py_list: # Each element is of type \"PythonObject\"\n", - " print(py_obj)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note TODO\n", - "\n", - "Iterating over a Mojo collection currently assigns the loop index variable a\n", - "`Reference` to each element, which then requires you to use the dereference\n", - "operator within the loop body. In contrast, iterating over a Python collection\n", - "assigns a `PythonObject` wrapper for the element, which does _not_ require you\n", - "to use the dereference operator.\n", - "\n", - ":::\n", - "\n", - "\n", - "There are two techniques for iterating over a Python dictionary. The first is to\n", - "iterate directly using the dictionary, which produces a sequence of its keys." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "a 1\n", - "b 2.71828\n", - "c sushi\n" - ] - } - ], - "source": [ - "from python import Python\n", - "\n", - "# Create a mixed-type Python dictionary\n", - "py_dict = Python.evaluate(\"{'a': 1, 'b': 2.71828, 'c': 'sushi'}\")\n", - "for py_key in py_dict: # Each element is of type \"PythonObject\"\n", - " print(py_key, py_dict[py_key])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The second approach to iterating over a Python dictionary is to invoke its\n", - "`items()` method, which produces a sequence of 2-tuple objects.\n", - "Within the loop body, you can then access the key and value by index." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "a 1\n", - "b 2.71828\n", - "c sushi\n" - ] - } - ], - "source": [ - "for py_tuple in py_dict.items(): # Each element is of type \"PythonObject\"\n", - " print(py_tuple[0], py_tuple[1])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/always-inline.ipynb b/docs/manual/decorators/always-inline.ipynb index f272691b9b..d0648a2f0d 100644 --- a/docs/manual/decorators/always-inline.ipynb +++ b/docs/manual/decorators/always-inline.ipynb @@ -72,12 +72,8 @@ "\n", "You can also use the decorator with the `\"nodebug\"` argument, which has the\n", "same effect to inline the function, but without debug information. This means\n", - "that you can't step into the function when debugging.\n", - "\n", - "This decorator is intended to be used on the lowest-level functions in a\n", - "library, which may wrap primitive functions, MLIR operations, or inline\n", - "assembly. Marking these functions as \"nodebug\" prevents users from accidentally\n", - "stepping into low-level non-Mojo code when debugging." + "you can't step into the function when debugging, but it reduces the debug build\n", + "binary size." ] } ], diff --git a/docs/manual/decorators/index.mdx b/docs/manual/decorators/index.md similarity index 97% rename from docs/manual/decorators/index.mdx rename to docs/manual/decorators/index.md index f7c7d1a6af..bfdd52e7c6 100644 --- a/docs/manual/decorators/index.mdx +++ b/docs/manual/decorators/index.md @@ -13,6 +13,7 @@ listing: - parameter.ipynb - register-passable.ipynb - staticmethod.ipynb + - unroll.ipynb - value.ipynb type: grid page-size: 99 @@ -34,5 +35,5 @@ built directly into the compiler. The following pages describe each built-in decorator with examples. -:::🔥#docs +:::{#docs} ::: diff --git a/docs/manual/decorators/parameter.ipynb b/docs/manual/decorators/parameter.ipynb index c74b25e22f..65aff01912 100644 --- a/docs/manual/decorators/parameter.ipynb +++ b/docs/manual/decorators/parameter.ipynb @@ -51,55 +51,6 @@ " print(\"this will be eliminated at compile time\")" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parametric for statement\n", - "\n", - "You can add the `@parameter` decorator to an `for` loop to create a loop that's\n", - "evaluated at compile time. The loop sequence and induction values must be\n", - "a valid parameter expressions (that is, an expressions that evaluate at compile\n", - "time).\n", - "\n", - "This has the effect of \"unrolling\" the loop.\n", - "\n", - " ```mojo\n", - " fn parameter_for[max: Int]():\n", - " @parameter\n", - " for i in range(max)\n", - " @parameter\n", - " if i == 10:\n", - " print(\"found 10!\")\n", - " ```\n", - "\n", - " Currently, `@parameter for` requires the sequence's `__iter__` method to\n", - " return a `_StridedRangeIterator`, meaning the induction variables must be\n", - " `Int`. The intention is to lift these restrictions in the future.\n", - "\n", - "### Compared to `unroll()`\n", - "\n", - "The Mojo standard library also includes a function called\n", - "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", - "given function that you want to call repeatedly, but has some important\n", - "differences when compared to the parametric `for` statement:\n", - "\n", - "- The `@parameter` decorator operates on `for` loop expressions. The \n", - " `unroll()` function is a higher-order function that takes a parametric closure\n", - " (see below) and executes it a specified number of times.\n", - "\n", - "- The parametric `for` statement is more versatile, since you can do anything \n", - " you can do in a `for` statement: including using arbitrary sequences, \n", - " early-exiting from the loop, skipping iterations with `continue` and so on.\n", - " \n", - " By contrast, `unroll()` simply takes a function and a count, and executes\n", - " the function the specified number of times.\n", - "\n", - "Both `unroll()` and `@parameter for` unroll at the beginning of compilation, \n", - "which might explode the size of the program that still needs to be compiled,\n", - "depending on the amount of code that's unrolled." - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -115,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [ { diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb index 9627f7f5dd..682a6f9259 100644 --- a/docs/manual/decorators/register-passable.ipynb +++ b/docs/manual/decorators/register-passable.ipynb @@ -85,6 +85,19 @@ "This behavior is what we expect from `Pair`, with or without the decorator." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note Updated syntax\n", + "\n", + "Register passable types now accept standard lifecycle methods, as shown above. \n", + "Prior to Mojo 24.1, register-passable types required a non-standard constructor that returned a value, with syntax like this: `return Self{a: one, b: two}`. \n", + "The older syntax is still supported but will be removed in a future release. For more information about this change, see the [Mojo 24.1 release notes](/mojo/changelog#v241-2024-02-29).\n", + "\n", + ":::" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -102,7 +115,7 @@ "instead of being passed by-pointer.\n", "\n", "1. `@register_passable` types cannot have a [`__moveinit__()`\n", - "constructor](/mojo/manual/lifecycle/life#move-constructors), because\n", + "constructor](/mojo/manual/lifecycle/life.html#move-constructors), because\n", "values passed in a register cannot be passed by reference.\n" ] }, @@ -141,7 +154,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This is similar to the [`@value`](/mojo/manual/decorators/value) decorator,\n", + "This is similar to the [`@value`](/mojo/manual/decorators/value.html) decorator,\n", "except when using `@register_passable(\"trivial\")` the only lifecycle method\n", "you're allowed to define is the `__init__()` constructor (but you don't have\n", "to)—you _cannot_ define any copy or move constructors or a destructor.\n", @@ -170,7 +183,8 @@ "This decorator is due for reconsideration. Lack of custom\n", "copy/move/destroy logic and \"passability in a register\" are orthogonal concerns\n", "and should be split. This former logic should be subsumed into a more general\n", - "decorator, which is orthogonal to `@register_passable`.\n", + "[`@value(\"trivial\")`](/mojo/manual/decorators/value.html) decorator, which is\n", + "orthogonal from `@register_passable`.\n", "\n", ":::" ] diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index f6e798b97a..b8d46eae86 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -60,7 +60,7 @@ "access instance data.\n", "\n", "For more information see the documentation on\n", - "[static methods](/mojo/manual/structs#static-methods)." + "[static methods](/mojo/manual/structs.html#static-methods)." ] } ], diff --git a/docs/manual/decorators/unroll.ipynb b/docs/manual/decorators/unroll.ipynb new file mode 100644 index 0000000000..d0300178ac --- /dev/null +++ b/docs/manual/decorators/unroll.ipynb @@ -0,0 +1,164 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: '`@parameter`'\n", + "description: Unrolls a loop at compile time.\n", + "---" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can add the `@parameter` decorator on any loop (such as `for` and `while`) to\n", + "make the Mojo compiler [unroll the\n", + "loop](https://en.wikipedia.org/wiki/Loop_unrolling), either fully or with a\n", + "given unroll factor.\n", + "\n", + "For example, the compiler will unroll all 10 iterations of the following loop\n", + "into 10 consecutive calls to `print()` (removing the `for` loop entirely):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@parameter\n", + "for i in range(10):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The decorator also accepts an \"unroll factor\" argument, which specifies how\n", + "many iterations to unroll at once. For example, the unroll above is equivalent\n", + "to `@parameter(10)` because it unrolls all iterations of the loop. So if you pass\n", + "a number smaller than the loop bounds, the compiler creates multiple unrolls.\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Unroll every 2 iterations, leaving a loop with 5 iterations.\n", + "# @parameter(2)\n", + "for i in range (10):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The result is equivalent to this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(0, 10, 2):\n", + " print(i)\n", + " print(i+1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, the compiler can unroll a loop only when the following statements are\n", + "true:\n", + "\n", + "- The loop's lower bound, upper bound, and induction step size are compile-time\n", + "constants (they do not vary at runtime). For example, in the above code\n", + "`range(0, 10, 2)`, `0` is the lower bound, `10` is the upper bound, and `2`\n", + "is the induction step size—these could instead be defined with variable names,\n", + "but the values cannot vary at runtime.\n", + "\n", + "- Likewise, there are no early exits in the loop that make the loop count\n", + "variable at runtime." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compared to `unroll()`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Mojo standard library also includes a function called\n", + "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", + "given function that you want to call repeatedly, but has some important\n", + "differences when compared to the `@parameter` decorator:\n", + "\n", + "- The `@parameter` decorator operates on loop expressions only, not on functions\n", + " like the `unroll()` function does.\n", + "\n", + "- The `@parameter` decorator determines how to unroll the loop based on the\n", + " induction variable (`i`), the value of which _is not_ known when compilation\n", + " begins. Whereas, the `unroll()` function calls upon your looping function\n", + " (`func`) with the `Int` loop index parameter that _is_ known at compile time.\n", + "\n", + " This means two things:\n", + "\n", + " - Within a loop using the `@parameter` decorator, the `i` induction variable is \n", + " still a runtime variable, so you _cannot_ use it as a parameter value (such\n", + " as for `SIMD[Int8, i]`). Whereas, within the `func` callback used with the\n", + " `unroll()` function, the `Int` loop index is known at compile time, so you\n", + " _can_ use it as a parameter value.\n", + "\n", + " - The `unroll()` function unrolls at the beginning of compilation, which\n", + " might explode the program size that still needs to be compiled, depending\n", + " on the amount of code that's unrolled. Whereas, the `@parameter` decorator\n", + " performs unrolling later in the compilation, after the compiler is able to\n", + " evaluate the induction variable (`i`), which avoids early explosion of the\n", + " program size that still needs compilation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/manual/decorators/value.ipynb b/docs/manual/decorators/value.ipynb index 807bd962cb..b6753b42e5 100644 --- a/docs/manual/decorators/value.ipynb +++ b/docs/manual/decorators/value.ipynb @@ -86,7 +86,7 @@ "metadata": {}, "source": [ "For more information about these lifecycle methods, read\n", - "[Life of a value](/mojo/manual/lifecycle/life)." + "[Life of a value](/mojo/manual/lifecycle/life.html)." ] } ], diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 063d40f661..ee7a1cadd3 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -34,89 +34,25 @@ "\n", ":::note\n", "\n", - "Functions declared inside a [`struct`](/mojo/manual/structs) are called\n", + "Functions declared inside a [`struct`](/mojo/manual/structs.html) are called\n", "\"methods,\" but they have all the same qualities as \"functions\" described here.\n", "\n", ":::" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `fn` functions\n", - "\n", - "The `fn` function has somewhat stricter rules than the `def` function.\n", - "\n", - "Here's an example of an `fn` function:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn greet(name: String) -> String:\n", - " var greeting = \"Hello, \" + name + \"!\"\n", - " return greeting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As far as a function caller is concerned, `def` and `fn` functions are\n", - "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", - "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", - "function is more strict on the inside.\n", - "\n", - "Here's everything to know about `fn`:\n", - "\n", - "- Arguments must specify a type (except for the\n", - " `self` argument in [struct methods](/mojo/manual/structs#methods)).\n", - "\n", - "- Return values must specify a type, unless the function doesn't return a value.\n", - " \n", - " If you don't specify a return type, it defaults to `None` (meaning no return\n", - " value).\n", - "\n", - "- By default, arguments are received as an immutable reference (values are\n", - " read-only, using the `borrowed` [argument\n", - " convention](/mojo/manual/values/ownership#argument-conventions)).\n", - " \n", - " This prevents accidental mutations, and permits the use of non-copyable types\n", - " as arguments.\n", - " \n", - " If you want a local copy, you can simply assign the value to a local\n", - " variable. Or, you can get a mutable reference to the value by declaring the\n", - " `inout` [argument\n", - " convention](/mojo/manual/values/ownership#argument-conventions)).\n", - "\n", - "- [Variables](/mojo/manual/variables) must be declared using the `var`\n", - " keyword.\n", - "\n", - "- If the function raises an exception, it must be explicitly declared with the\n", - " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", - "\n", - "By enforcing these type checks, using the `fn` function helps avoid a variety\n", - "of runtime errors." - ] - }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `def` functions\n", "\n", - "Compared to an `fn` function, a `def` function has fewer restrictions.\n", - "The `def` function works more like a Python\n", + "The `def` function provides the same dynamism and flexibility as a Python\n", "`def` function. For example, this function works the same in Python and Mojo:" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -129,15 +65,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In a Mojo `def` function, you have the option to specify the argument type and\n", - "the return type. You can also declare variables with `var`, with or without\n", - "explicit typing. So you can write a `def` function that looks almost exactly\n", - "like the `fn` function shown earlier:" + "In Mojo, you also have the option to specify the argument type and the return\n", + "type. You can also declare variables with `var`, with or without explicit\n", + "typing." ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -157,26 +92,23 @@ "\n", "- Arguments don't require a declared type.\n", "\n", - " Undeclared arguments are actually passed as an\n", - " [`object`](/mojo/stdlib/builtin/object/object), which allows the\n", + " Undeclared arguments are actually passed as an `object`, which allows the\n", " function to receive any type (Mojo infers the type at runtime).\n", "\n", - "- Return types don't need to be declared, and also default to `object`. (If a \n", - " `def` function doesn't declare a return type of `None`, it's considered to\n", - " return an `object` by default.)\n", + "- Return types don't need to be declared and also default to `object`.\n", "\n", - "- Arguments are mutable. Arguments default to using the using the `borrowed` \n", - " [argument convention](/mojo/manual/values/ownership#argument-conventions))\n", - " like an `fn` function, with a special addition: if the function mutates the\n", - " argument, it makes a mutable copy. \n", + "- Arguments are mutable (usually passed by value, using the `owned` [argument\n", + " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", "\n", " If an argument is an `object` type, it's received as a reference, following\n", " [object reference\n", - " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", + " semantics](/mojo/manual/values/value-semantics.html#python-style-reference-semantics).\n", " \n", - " If an argument is any other declared type, it's received as a value.\n", + " If an argument is any other declared type, it's received as a value (using\n", + " the `owned` [argument\n", + " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", "\n", - "- [Variables](/mojo/manual/variables) don't need to be declared using \n", + "- [Variables](/mojo/manual/variables.html) don't need to be declared using \n", " `var`." ] }, @@ -199,43 +131,86 @@ "\n", "For compatibility with Python, `object` values are passed using [object\n", "reference\n", - "semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", + "semantics](/mojo/manual/values/value-semantics.html#python-style-reference-semantics).\n", "As such, the `object` type is not compatible with the [argument\n", - "conventions](/mojo/manual/values/ownership#argument-conventions) that\n", + "conventions](/mojo/manual/values/ownership.html#argument-conventions) that\n", "enforce value semantics. So, be careful if using `object` values alongside other\n", "strongly-typed values—their behavior might be inconsistent because `object` is \n", "the only type in the standard library that does not conform to [full value\n", - "semantics](/mojo/manual/values/value-semantics#full-value-semantics).\n", - "\n", - ":::note TODO\n", - "\n", - "The `object` type is still a work in progress. It doesn't support all of the\n", - "possible underlying types, for example.\n", + "semantics](/mojo/manual/values/value-semantics.html#full-value-semantics)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `fn` functions\n", "\n", - ":::" + "The `fn` function provides strict type checking and additional memory safety.\n", + "It basically forces you to write the optional things in `def`, and it ensures\n", + "that you don't accidentally mutate received arguments. For example, here's the\n", + "same function from above using `fn`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn greet(name: String) -> String:\n", + " var greeting = \"Hello, \" + name + \"!\"\n", + " return greeting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Function arguments\n", + "As far as a function caller is concerned, `def` and `fn` functions are\n", + "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", + "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", + "function is more strict on the inside.\n", "\n", - "As noted in the previous sections, there are a few differences between how `def`\n", - "and `fn` functions treat arguments. But most of the time they are the same.\n", + "Here's everything to know about `fn`:\n", "\n", - "As noted, there are some differences in _argument conventions_. \n", - "Argument conventions are discussed in much more detail in the page on\n", - "[Ownership](/mojo/manual/values/ownership#argument-conventions).\n", + "- Arguments must specify a type (except for the\n", + " `self` argument in [struct methods](/mojo/manual/structs.html)).\n", "\n", - "The other difference is that `def` functions don't need to specify an argument's\n", - "type. If no type is specified, the argument is passed as an \n", - "[`object`](/mojo/stdlib/builtin/object/object).\n", + "- Return values must specify a type, unless the function doesn't return a value.\n", + " \n", + " If you don't specify a return type, it defaults to `None` (meaning no return\n", + " value).\n", "\n", - "The remaining rules for arguments described in this section apply to both `def`\n", - "and `fn` functions.\n", + "- By default, arguments are received as an immutable reference (values are\n", + " read-only, using the `borrowed` [argument\n", + " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", + " \n", + " This prevents accidental mutations, and permits the use of non-copyable types\n", + " as arguments.\n", + " \n", + " If you want a local copy, you can simply assign the value to a local\n", + " variable. Or, you can get a mutable reference to the value by declaring the\n", + " `inout` [argument\n", + " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", + "\n", + "- [Variables](/mojo/manual/variables.html) must be declared using the `var`\n", + " keyword.\n", "\n", - "### Optional arguments\n", + "- If the function raises an exception, it must be explicitly declared with the\n", + " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", + "\n", + "By enforcing these type checks, using the `fn` function helps avoid a variety\n", + "of runtime errors. It also improves performance compared to the dynamic typing\n", + "in a `def` function, because there's no overhead processing required to figure\n", + "out what data types to use at runtime—the types are fixed at compile time." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Optional arguments\n", "\n", "An optional argument is one that includes a default value, such as the `exp`\n", "argument here:" @@ -261,7 +236,7 @@ "metadata": {}, "source": [ "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership#mutable-arguments-inout).\n", + "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout).\n", "\n", "Any optional arguments must appear after any required arguments. [Keyword-only\n", "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", @@ -272,7 +247,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Keyword arguments\n", + "## Keyword arguments\n", "\n", "You can also use keyword arguments when calling a function. Keyword arguments\n", "are specified using the format argument_name =\n", @@ -298,7 +273,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Variadic arguments\n", + "## Variadic arguments\n", "\n", "Variadic arguments let a function accept a variable number of arguments. To\n", "define a function that takes a variadic argument, use the variadic argument\n", @@ -351,7 +326,7 @@ ":::\n", "\n", "\n", - "#### Homogeneous variadic arguments\n", + "### Homogeneous variadic arguments\n", "\n", "When defining a homogeneous variadic argument, use \n", "*argument_name: argument_type:" @@ -404,7 +379,7 @@ "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", - "[`Reference`](/mojo/stdlib/memory/reference/Reference) for each value instead\n", + "[`Reference`](/mojo/stdlib/memory/unsafe/reference) for each value instead\n", "of the value itself. You must add an empty subscript operator `[]` to\n", "dereference the reference and retrieve the value:\n" ] @@ -441,7 +416,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Heterogeneous variadic arguments\n", + "### Heterogeneous variadic arguments\n", "\n", "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", @@ -570,7 +545,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Variadic keyword arguments\n", + "### Variadic keyword arguments\n", "\n", "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", "keyword arguments allow the user to pass an arbitrary number of keyword\n", @@ -599,7 +574,7 @@ "\n", " - Variadic keyword arguments are always implicitly treated as if they\n", " were declared with the `owned` [argument \n", - " convention](/mojo/manual/values/ownership#argument-conventions), and\n", + " convention](/mojo/manual/values/ownership.html#argument-conventions), and\n", " can't be declared otherwise:\n", "\n", " ```mojo\n", @@ -636,7 +611,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Positional-only and keyword-only arguments\n", + "## Positional-only and keyword-only arguments\n", "\n", "When defining a function, you can restrict some arguments so that they can only\n", "be passed as positional arguments, or they can only be passed as keyword \n", @@ -787,7 +762,7 @@ "ambiguity by explicitly casting your value to a supported argument type. For\n", "example, in the following code, we want to call the overloaded `foo()`\n", "function, but both implementations accept an argument that supports [implicit\n", - "conversion](/mojo/manual/variables#implicit-type-conversion) from\n", + "conversion](/mojo/manual/variables.html#implicit-type-conversion) from\n", "`StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a\n", "compiler error. We can fix it by casting the value to the type we really want:" ] diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md new file mode 100644 index 0000000000..15fb479331 --- /dev/null +++ b/docs/manual/get-started.md @@ -0,0 +1,505 @@ +--- +title: Get started with Mojo🔥 +sidebar_label: Get started +description: Install Mojo now and start developing +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +On this page, we'll show you how to install Mojo and create the classic "Hello +world" starter program with Mojo, in three different ways. If you'd rather read +how to write Mojo code beyond just printing text, see the [introduction to +Mojo](/mojo/manual/basics). + +:::tip Updating? + +If you already installed Mojo, see [how to update](#update-mojo). + +::: + +## Requirements + + + + +- Apple silicon (M1 or M2 processor) +- macOS Ventura (12) or later +- Python 3.8 - 3.11 +- Xcode or Xcode Command Line Tools +- [Homebrew](https://brew.sh) + + + + +- Ubuntu 20.04/22.04 LTS +- x86-64 CPU (with [SSE4.2 or + newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) + or AWS Graviton2/3 CPU +- Minimum 8 GiB RAM +- Python 3.8 - 3.11 +- g++ or clang++ C++ compiler + + + + +Windows support is still in development. + +In the meantime, you can use Mojo on Windows [with +WSL](https://learn.microsoft.com/en-us/windows/wsl/install), using a compatible +version of Ubuntu (see our requirements for Linux). + + + + +## 1. Install Mojo + +If you already [installed MAX](/max/install), you can [skip to the next +section](#2-run-code-in-the-repl) because MAX includes Mojo. + +The Mojo SDK is available as either a stable build or a nightly build. +We strive to release stable builds once a month and release nightly builds as +often as possible (not necessarily every day). + + +{/*############################*/} +{/*#### STABLE BUILD SETUP ####*/} +{/*############################*/} + + +1. Open a terminal and install the [`modular`](/cli/) command line tool with + this helper script: + + ```sh + curl -s https://get.modular.com | sh - + ``` + +
+ + Or, click here to see the manual install commands. + +
+ + + + + ```sh + brew update && brew install modularml/packages/modular + ``` + + + + + ```sh + apt-get install -y apt-transport-https && + keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && + apt-get update && + apt-get install -y modular + ``` + + + + +
+
+ +2. Create a virtual environment: + + Because Mojo interoperates with Python, + it's important to define a predictable Python version and package library to + use. We suggest you do that with either venv or conda: + + + + + For most users, we recommend venv (it's included with Python): + + ```sh + python3 -m venv mojo-venv && source mojo-venv/bin/activate + ``` + + + + + Only if you already use conda as your preferred environment, we suggest you + use that: + + ```sh + conda create -n mojo python=3.10 -y && conda activate mojo + ``` + + + + +3. Install the Mojo SDK: + + ```sh + modular install mojo + ``` + +4. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: + + + + + If you're using Bash, run this command: + + ```sh + MOJO_PATH=$(modular config mojo.path) \ + && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ + && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> "$BASHRC" \ + && source "$BASHRC" + ``` + + + + + If you're using ZSH, run this command: + + ```sh + MOJO_PATH=$(modular config mojo.path) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ + && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> ~/.zshrc \ + && source ~/.zshrc + ``` + + + + + If you're using fish, run this command: + + ```sh + set MOJO_PATH (modular config mojo.path) \ + && set -Ux MODULAR_HOME $HOME/.modular \ + && fish_add_path $MOJO_PATH/bin + ``` + + + + +
+{/*############################*/} +{/*### NIGHTLY BUILD SETUP ####*/} +{/*############################*/} + + +:::caution + +Nightly builds are not fully tested. They might include incomplete features, +performance regressions, and new bugs. When using code from the +[mojo](https://github.com/modularml/mojo) GitHub repo, be sure you checkout +the `nightly` branch, because the `main` branch might not be compatible with +nightly builds. + +::: + +1. Open a terminal and install the [`modular`](/cli/) command line tool with + this helper script (this is the same for stable and nightly builds): + + ```sh + curl -s https://get.modular.com | sh - + ``` + +
+ + Or, click here to see the manual install commands. + +
+ + + + + ```sh + brew update && brew install modularml/packages/modular + ``` + + + + + ```sh + apt-get install -y apt-transport-https && + keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && + curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && + apt-get update && + apt-get install -y modular + ``` + + + + +
+
+ +2. Create a virtual environment for nightly builds: + + Because Mojo interoperates with Python, + it's important to define a predictable Python version and package library to + use. We suggest you do that with either venv or conda: + + + + + For most users, we recommend venv (it's included with Python): + + ```sh + python3 -m venv mojo-nightly-venv && source mojo-nightly-venv/bin/activate + ``` + + + + + Only if you already use conda as your preferred environment, we suggest you + use that: + + ```sh + conda create -n mojo-nightly python=3.10 -y && conda activate mojo-nightly + ``` + + + + +3. Install the nightly Mojo SDK: + + ```sh + modular install nightly/mojo + ``` + +4. Set environment variables so you can access the nightly [`mojo`](/mojo/cli/) + CLI: + + + + + If you're using Bash, run this command: + + ```sh + MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ + && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ + && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> "$BASHRC" \ + && source "$BASHRC" + ``` + + + + + If you're using ZSH, run this command: + + ```sh + MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ + && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ + && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> ~/.zshrc \ + && source ~/.zshrc + ``` + + + + + If you're using fish, run this command: + + ```sh + set MOJO_NIGHTLY_PATH (modular config mojo-nightly.path) \ + && set -Ux MODULAR_HOME $HOME/.modular \ + && fish_add_path $MOJO_NIGHTLY_PATH/bin + ``` + + + + +
+
+ +Now you're ready to go. + +## 2. Run code in the REPL + +Now that you've installed Mojo, let's write some code! + +First, let's use the Mojo +[REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), +which allows you to write and run Mojo code in a command prompt: + +1. To start a REPL session, type `mojo` in your terminal and press + Enter. + +2. Then type `print("Hello, world!")` and press Enter twice +(a blank line is required to indicate the end of an expression). + +That's it! For example: + +```text +$ mojo +Welcome to Mojo! 🔥 + +Expressions are delimited by a blank line. +Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. + +1> print("Hello, world!") +2. +Hello, world! +``` + +You can write as much code as you want in the REPL. You can press +Enter to start a new line and continue writing code, and when you +want Mojo to evaluate the code, press Enter twice. If there's +something to print, Mojo prints it and then returns the prompt to you. + +The REPL is primarily useful for short experiments because the code isn't +saved. So when you want to write a real program, you need to write the code in +a `.mojo` source file. + +## 3. Run a Mojo file + +Now let's write the code in a Mojo source file and run it with the +[`mojo`](/mojo/cli/) command: + +1. Create a file named `hello.mojo` (or `hello.🔥`) and add the following code: + + ```mojo + fn main(): + print("Hello, world!") + ``` + + That's all you need. Save the file and return to your terminal. + +2. Now run it with the `mojo` command: + + ```sh + mojo hello.mojo + ``` + + It should immediately print the message: + + ```text + Hello, world! + ``` + +If this didn't work for you, double-check that your code looks exactly like the code +in step 1, and make sure you correctly [installed +mojo](/mojo/install) (it includes Mojo). + +## 4. Build an executable binary + +Finally, let's build and run that same code as an executable: + +1. Create an executable file with the [`build`](/mojo/cli/build) command: + + ```sh + mojo build hello.mojo + ``` + + The executable file uses the same name as the `.mojo` file, but + you can change that with the `-o` option. + +2. Then run the executable: + + ```sh + ./hello + ``` + +This creates a statically compiled binary file, so it contains all the code and +libraries it needs to run. + +## 5. Install our VS Code extension (optional) + +To provide a first-class developer experience with features like code +completion, quick fixes, and hover help, we've created a [Mojo extension for +Visual Studio +Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). + +![](./images/mojo-vscode.png) + +## Next steps + +- If you're new to Mojo, we suggest you learn the language basics in the + [introduction to Mojo](/mojo/manual/basics). + +- If you want to experiment with some code, clone [the Mojo +repo](https://github.com/modularml/mojo/) to try our code examples: + + ```sh + git clone https://github.com/modularml/mojo.git + ``` + + If you installed the nightly build, also checkout the nightly branch: + + ```sh + git checkout nightly + ``` + + In addition to several `.mojo` examples, the repo includes [Jupyter + notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) + that teach advanced Mojo features. + +- To see all the available Mojo APIs, check out the [Mojo standard library + reference](/mojo/lib). + +If you have issues during install, check our [known +issues](/mojo/roadmap#mojo-sdk-known-issues). + +:::note + +To help us improve Mojo, we collect some basic system information and +crash reports. [Learn +more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). + +::: + +## Update Mojo + +To check your current Mojo version, use the `--version` option: + +```sh +mojo --version +``` + +And compare your version to the latest stable version in the [Mojo +changelog](/mojo/changelog). Or if you installed a nightly build, look for +release announcements in [this Discord +channel](https://discord.com/channels/1087530497313357884/1224434323193594059). + +If it's time to update, here's what to do: + +1. Make sure you have the latest `modular` CLI: + + + + + ```sh + brew update \ + && brew upgrade modular + ``` + + + + + ```sh + sudo apt update \ + && sudo apt install modular + ``` + + + + +2. Update the `mojo` package: + + + + + ```sh + modular update mojo + ``` + + + + + ```sh + modular update nightly/mojo + ``` + + + diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx deleted file mode 100644 index 363fef9d10..0000000000 --- a/docs/manual/get-started.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Get started with Mojo🔥 -sidebar_label: Get started -description: Install Mojo now and start developing ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import MaxInstall from '@site/src/components/MaxInstall'; - -On this page, we'll show you how to create the classic "Hello world" starter -program with Mojo. If you'd rather read how to write Mojo code, see the -[introduction to Mojo](/mojo/manual/basics). - -By installing Mojo, you understand and agree to our [software -license](https://www.modular.com/legal/max). - -## 1. Create a new project - -To create a new Mojo project, we'll use [Magic](/magic)—a virtual environment -manager and package manager based on conda. - -1. Install Magic on macOS or Ubuntu Linux with this command: - - - - Then run the `source` command printed in your terminal. - -2. Create a Mojo project called "hello-world": - - ```sh - magic init hello-world --format mojoproject - ``` - - This creates a directory named `hello-world` and installs the Mojo project - dependencies—the only dependency for a Mojo project is the `max` package - (Mojo is [bundled with MAX](/max/faq#why-bundle-mojo-with-max)). - -3. Start a shell in the project virtual environment: - - ```sh - cd hello-world && magic shell - ``` - -That's it! The `magic shell` command activates the virtual environment so you -can now start using Mojo. For example, you can check your Mojo version like -this: - -```sh -mojo --version -``` - - -
- Click here to install the Mojo nightly build. -
- -To install the latest nightly build, specify the `max-nightly` channel when you -initialize your project: - -```sh -magic init hello-world-nightly --format mojoproject \ - -c conda-forge -c https://conda.modular.com/max-nightly -``` - -When you include the `-c` (`--channel`) option, you must specify all channels. -Be sure to specify the `conda-forge` channel (or another conda channel) where -Magic should look for all packages other than MAX/Mojo. - -Then start a shell in the new environment: - -```sh -cd hello-world-nightly && magic shell -``` - -The nightly version of Mojo installed in this project is fully independent, so -it will not interfere with other projects that use the latest stable build. - -
-
- - -## 2. Run code in the REPL - -First, let's use the Mojo -[REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), -which allows you to write and run Mojo code in a command prompt: - -1. To start a REPL session, type `mojo` and press Enter. - -2. Type `print("Hello, world!")` and press Enter twice -(a blank line is required to indicate the end of an expression). - - The result looks like this: - - ```text - $ mojo - Welcome to Mojo! 🔥 - - Expressions are delimited by a blank line. - Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. - - 1> print("Hello world") - Hello world - ``` - -3. To exit REPL, type `:quit` and press Enter, or press -Ctrl + D. - -You can write as much code as you want in the REPL. You can press -Enter to start a new line and continue writing code, and when you -want Mojo to evaluate the code, press Enter twice. If there's -something to print, Mojo prints it and then returns the prompt to you. - -The REPL is primarily useful for short experiments because the code isn't -saved. So when you want to write a real program, you need to write the code in -a `.mojo` source file. - -## 3. Run a Mojo file - -Now let's write the code in a Mojo source file and run it with the -[`mojo`](/mojo/cli/) command: - -1. Create a file named `hello.mojo` (or `hello.🔥`) and add the following code: - - ```mojo - fn main(): - print("Hello, world!") - ``` - - That's all you need. Save the file and return to your terminal. - -2. Now run it with the `mojo` command: - - ```sh - mojo hello.mojo - ``` - - ```output - Hello, world! - ``` - -## 4. Build an executable binary - -Finally, let's build and run that same code as an executable: - -1. Create an executable file with the [`build`](/mojo/cli/build) command: - - ```sh - mojo build hello.mojo - ``` - - The executable file uses the same name as the `.mojo` file, but - you can change that with the `-o` option. - -2. Then run the executable: - - ```sh - ./hello - ``` - - ```output - Hello, world! - ``` - -The [`build`](/mojo/cli/build) command creates a statically compiled binary -file, so it contains all the code and libraries it needs to run. - -You can now deactivate the virtual environment by just typing `exit`: - -```sh -exit -``` - -Now let's try running an existing code example. - -## 5. Run an example from GitHub - -Our Mojo code examples in GitHub include a Magic configuration file so you can -simply clone the repo and run the code with `magic`. For example: - -1. Clone the Mojo repo: - - ```sh - git clone https://github.com/modularml/mojo.git - ``` - - Only if you installed the nightly build, also checkout the nightly branch: - - ```sh - git checkout nightly - ``` - -2. Navigate to the examples: - - ```sh - cd mojo/examples - ``` - -3. Run some code: - - ```sh - magic run mojo hello_interop.mojo - ``` - - ```output - Hello Mojo 🔥! - 9 - 6 - 3 - Hello from Python! - I can even print a numpy array: [1 2 3] - ``` - -## 6. Install our VS Code extension (optional) - -To provide a first-class developer experience with features like code -completion, quick fixes, and hover help, we've created a [Mojo extension for -Visual Studio -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - -![](./images/mojo-vscode.png) - - -## Update Mojo - -To update the Mojo version in your project, use `magic add` -and specify the `max` package version. - -For example, if you want to always use the latest version of Mojo, you can use -the `*` wildcard as the version and then simply run `magic update` (you must -run `magic add` within the project path): - -```sh -cd hello-world -``` - -```sh -magic add max -``` - -```sh -magic update -``` - -:::note - -Although the wildcard option allows `magic update` to always install the latest -version, it also updates the `magic.lock` file in your project with the -explicit version you've installed. This ensures that anybody else who -initializes the project also gets the same package version (until you run -`magic update` again). - -::: - -To be more specific with your package version, you can use any of the [Python -package version -specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#id5). -For example: - -```sh -magic add "max~=24.4" -``` - -```sh -magic add "max>=24.4,<24.5" -``` - - -## Next steps - -- If you're new to Mojo, we suggest you learn the language basics in the - [introduction to Mojo](/mojo/manual/basics). - -- To learn more about the `magic` tool, read [Get started with Magic](/magic/). - -- Explore more code examples in the [the Mojo -repo](https://github.com/modularml/mojo/). In addition to several `.mojo` -examples, the repo includes [Jupyter -notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) -that teach advanced Mojo features. - -- To see all the available Mojo APIs, check out the [Mojo standard library - reference](/mojo/lib). - -If you have issues during install, check our [known -issues](/mojo/roadmap#mojo-sdk-known-issues). - -:::note - -To help us improve Mojo, we collect some basic system information and -crash reports. [Learn -more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). - -::: diff --git a/docs/manual/images/pointer-diagram-dark.png b/docs/manual/images/pointer-diagram-dark.png deleted file mode 100644 index fe55eb78ff1833f9d96cf011e1d6bd7273dcd9ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21558 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 WKtyzfdJPfqM@vl)_Ce)Q$o~VYQC747 diff --git a/docs/manual/images/strided-load-storage-dark.png b/docs/manual/images/strided-load-storage-dark.png deleted file mode 100644 index e72ad2034cad6dadd638b286b8c9996c988aeb8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13876 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 AqyPW_ diff --git a/docs/manual/images/strided-load-storage.png b/docs/manual/images/strided-load-storage.png deleted file mode 100644 index 9aea48055d1a26b071ef6cd67b86bbfa3f31d081..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13569 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 diff --git a/docs/manual/index.md b/docs/manual/index.md index 7f8256e80c..460756b70f 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -13,64 +13,58 @@ ideal for heterogeneous hardware, from CPUs and GPUs, to various AI ASICs). We also designed Mojo as a superset of Python because we love Python and its community, but we couldn't realistically enhance Python to do all the things we wanted. For a longer discussion on this topic, read [Why -Mojo](/mojo/why-mojo). +Mojo](/mojo/why-mojo.html). Beware that Mojo is still a very young language, so there's a lot that hasn't been built yet. Likewise, there's a lot of documentation that hasn't been written yet. But we're excited to share Mojo with you and [get your -feedback](/mojo/community). +feedback](/mojo/community.html). ## Contents - **Get started** - - [Why Mojo](/mojo/why-mojo) + - [Why Mojo](/mojo/manual/why-mojo) - [Get started with Mojo](/mojo/manual/get-started) - **Language basics** - - [Introduction to Mojo](/mojo/manual/basics) - - [Functions](/mojo/manual/functions) - - [Variables](/mojo/manual/variables) - - [Types](/mojo/manual/types) - - [Control flow](/mojo/manual/control-flow) - - [Structs](/mojo/manual/structs) - - [Modules and packages](/mojo/manual/packages) + - [Introduction to Mojo](/mojo/manual/basics.html) + - [Functions](/mojo/manual/functions.html) + - [Variables](/mojo/manual/variables.html) + - [Structs](/mojo/manual/structs.html) + - [Modules and packages](/mojo/manual/packages.html) - **Value ownership** - - [Intro to value ownership](/mojo/manual/values/) - - [Value semantics](/mojo/manual/values/value-semantics) - - [Ownership and borrowing](/mojo/manual/values/ownership) + - [Intro to value ownership](/mojo/manual/values/index.html) + - [Value semantics](/mojo/manual/values/value-semantics.html) + - [Ownership and borrowing](/mojo/manual/values/ownership.html) - **Value lifecycle** - - [Intro to value lifecycle](/mojo/manual/lifecycle/) - - [Life of a value](/mojo/manual/lifecycle/life) - - [Death of a value](/mojo/manual/lifecycle/death) + - [Intro to value lifecycle](/mojo/manual/lifecycle/index.html) + - [Life of a value](/mojo/manual/lifecycle/life.html) + - [Death of a value](/mojo/manual/lifecycle/death.html) - **Traits and parameters** - - [Traits](/mojo/manual/traits) - - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/) - -- **Pointers** - - - [Unsafe pointers](/mojo/manual/pointers) + - [Traits](/mojo/manual/traits.html) + - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index.html) - **Python** - - [Python integration](/mojo/manual/python/) - - [Python types](/mojo/manual/python/types) + - [Python integration](/mojo/manual/python/index.html) + - [Python types](/mojo/manual/python/types.html) - **Tools** - - [Debugging](/mojo/tools/debugging) - - [Testing](/mojo/tools/testing) + - [Debugging](/mojo/tools/debugging.html) + - [Testing](/mojo/tools/testing.html) - **Project information** - - [Roadmap and sharp edges](/mojo/roadmap) - - [Changelog](/mojo/changelog) - - [FAQ](/mojo/faq) - - [Community](/mojo/community) + - [Roadmap and sharp edges](/mojo/roadmap.html) + - [Changelog](/mojo/changelog.html) + - [FAQ](/mojo/faq.html) + - [Community](/mojo/community.html) diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index b9496583cb..4df4001373 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -200,31 +200,26 @@ "dynamically allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", - "memory allocated by its\n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer):" + "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n", - "\n", "struct HeapArray:\n", - " var data: UnsafePointer[Int]\n", + " var data: Pointer[Int]\n", " var size: Int\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", - " self.data = UnsafePointer[Int].alloc(self.size)\n", + " self.data = Pointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " initialize_pointee_copy(self.data + i, val)\n", + " self.data.store(i, val)\n", "\n", " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " destroy_pointee(self.data + i)\n", " self.data.free()" ] }, @@ -236,19 +231,63 @@ "when a pointer is destroyed, Mojo doesn't call the destructors on those values.\n", "\n", "So in the `HeapArray` example above, calling `free()` on the pointer releases\n", - "the memory, but doesn't call the destructors on the stored values. To invoke\n", - "the destructors, use the `destroy_pointee()` function from the \n", - "`unsafe_pointer` module.\n", + "the memory, but doesn't call the destructors on the stored `Int` values. That's\n", + "OK in this case, because trivial types like `Int` have no-op destructors.\n", + "However, in the general case—if you're storing values that _might_ have\n", + "functional destructors—it's not enough to call `free()` on the pointer. You\n", + "should also ensure that Mojo completely destroys all the allocated types\n", + "through their destructors.\n", + "\n", + "The following example updates the previous `__del__()` method to ensure\n", + "destructors are called on array elements. It does this by assigning each of the\n", + "stored values in turn to the `_` \"discard\" pattern. Assigning a value to the\n", + "`_` discard variable tells the compiler that this is the last use of that\n", + "value, so it can be destroyed immediately." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "struct HeapArray:\n", + " var data: Pointer[Int]\n", + " var size: Int\n", + "\n", + " fn __del__(owned self):\n", + " for i in range(self.size):\n", + " _ = __get_address_as_owned_value((self.data + i).address)\n", + " self.data.free()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", ":::note\n", "\n", "You can't just call the destructor explicitly. Because `__del__()`\n", "takes `self` as an `owned` value, and owned arguments are copied by default,\n", - "`foo.__del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", + "`foo._del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", "destroys a value, however, it passes in the original value as `self`, not a\n", "copy.\n", "\n", - ":::" + ":::\n", + "\n", + "For instances of the \n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type, use\n", + "the [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", + "function instead of the discard pattern. For example, if the previous example\n", + "used `UnsafePointer`, the `__del__()` method would look like this:\n", + "\n", + "```mojo\n", + "fn __del__(owned self):\n", + " for i in range(self.size):\n", + " destroy_pointee(self.data + i)\n", + " self.data.free()\n", + "```" ] }, { @@ -264,7 +303,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -418,7 +457,7 @@ "dismantle the `existing`/`self` value that's `owned`. That is, `__moveinit__()`\n", "implicitly destroys sub-elements of `existing` in order to transfer ownership\n", "to a new instance (read more about the [move\n", - "constructor](/mojo/manual/lifecycle/life#move-constructor)),\n", + "constructor](/mojo/manual/lifecycle/life.html#move-constructor)),\n", "while `__del__()` implements the deletion logic for its `self`. As such, they\n", "both need to own and transform elements of the `owned` value, and they\n", "definitely don’t want the original `owned` value's destructor to also run—that\n", diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 39f645eca4..1c3226f9d0 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -21,7 +21,7 @@ "source": [ "So far, we've explained how Mojo allows you to build high-performance code that\n", "is memory safe _without_ manually managing memory, using Mojo's [ownership\n", - "model](/mojo/manual/values/ownership). However, Mojo is designed for\n", + "model](/mojo/manual/values/ownership.html). However, Mojo is designed for\n", "[systems programming](https://en.wikipedia.org/wiki/Systems_programming), which\n", "often requires manual memory management for custom data types. So, Mojo lets\n", "you do that as you see fit. To be clear, Mojo has no reference counter and no\n", @@ -31,9 +31,9 @@ "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", - "[structs](/mojo/manual/structs). You can actually write your own\n", + "[structs](/mojo/manual/structs.html). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", - "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", + "[MLIR dialects](/mojo/notebooks/BoolMLIR.html).\n", "\n", "What's great about the Mojo language is that it provides you these low-level\n", "tools for systems programming, but within a framework that helps you build\n", @@ -43,7 +43,7 @@ "semantics](/mojo/manual/values/value-semantics), the programmer instantiating\n", "your type/object doesn't need to think about memory management at all, and the\n", "behavior will be safe and predictable, thanks to [value\n", - "ownership](/mojo/manual/values/ownership).\n", + "ownership](/mojo/manual/values/ownership.html).\n", "\n", "In summary, it's the responsibility of the type author to manage the memory and\n", "resources for each value type, by implementing specific lifecycle methods, such\n", @@ -65,7 +65,7 @@ "First, let's clarify some terminology:\n", "\n", "- The \"lifecycle\" of a value is defined by various [dunder\n", - "methods](/mojo/manual/structs#special-methods) in a struct.\n", + "methods](/mojo/manual/structs.html#special-methods) in a struct.\n", "Each lifecycle event is handled by a different method,\n", "such as the constructor (`__init__()`), the destructor (`__del__()`), the copy\n", "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", @@ -86,8 +86,8 @@ "As you might imagine, keeping track of a value's lifetime can be difficult if a\n", "value is shared across functions many times during the life of a program.\n", "However, Mojo makes this predictable partly through its [value\n", - "semantics](/mojo/manual/values/value-semantics) and [value\n", - "ownership](/mojo/manual/values/ownership) (both prerequisite readings for\n", + "semantics](/mojo/manual/values/value-semantics.html) and [value\n", + "ownership](/mojo/manual/values/ownership.html) (both prerequisite readings for\n", "the following sections). The final piece of the puzzle for lifetime management\n", "is the value lifecycle: every value (defined in a struct) needs to implement\n", "key lifecycle methods that define how a value is created and destroyed." diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 0a0a599315..a8d0d00b4c 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -23,7 +23,7 @@ "up until the value is last used, at which point Mojo destroys it. This page\n", "describes how every value in Mojo is created, copied, and moved. (The next\n", "page describes [how values are\n", - "destroyed](/mojo/manual/lifecycle/death).)\n", + "destroyed](/mojo/manual/lifecycle/death.html).)\n", "\n", "All data types in Mojo—including basic types in the standard library such as\n", "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", @@ -31,7 +31,7 @@ "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", - "[struct](/mojo/manual/structs). This means the creation and\n", + "[struct](/mojo/manual/structs.html). This means the creation and\n", "destruction of any piece of data follows the same lifecycle rules, and you can\n", "define your own data types that work exactly the same way.\n", "\n", @@ -130,7 +130,7 @@ "metadata": {}, "source": [ "An instance of `MyPet` can also be\n", - "[borrowed](/mojo/manual/values/ownership#immutable-arguments-borrowed)\n", + "[borrowed](/mojo/manual/values/ownership.html#immutable-arguments-borrowed)\n", "and destroyed, but it currently can't be copied or moved.\n", "\n", "We believe this is a good default starting point, because there are no built-in\n", @@ -143,9 +143,9 @@ "Mojo does not require a destructor to destroy an object. As long as\n", "all fields in the struct are destructible (every type in the standard library\n", "is destructible, except for\n", - "[pointers](/mojo/stdlib/memory/unsafe)), then Mojo knows how to destroy\n", + "[pointers](/mojo/stdlib/memory/unsafe.html)), then Mojo knows how to destroy\n", "the type when its lifetime ends. We'll discuss that more in [Death of a\n", - "value](/mojo/manual/lifecycle/death).\n", + "value](/mojo/manual/lifecycle/death.html).\n", "\n", ":::" ] @@ -157,14 +157,14 @@ "### Overloading the constructor\n", "\n", "Like any other function/method, you can\n", - "[overload](/mojo/manual/functions#overloaded-functions) the\n", + "[overload](/mojo/manual/functions.html#overloaded-functions) the\n", "`__init__()` constructor to initialize the object with different arguments. For\n", "example, you might want a default constructor that sets some default values and\n", "takes no arguments, and then additional constructors that accept more arguments.\n", "\n", "Just be aware that, in order to modify any fields, each constructor must\n", "declare the `self` argument with the [`inout`\n", - "convention](/mojo/manual/values/ownership#mutable-arguments-inout). If you\n", + "convention](/mojo/manual/values/ownership.html#mutable-arguments-inout). If you\n", "want to call one constructor from another, you simply call upon that\n", "constructor as you would externally (you don't need to pass `self`).\n", "\n", @@ -384,7 +384,7 @@ "\n", "Also, notice that the `existing` argument in `__copyinit__()` is immutable\n", "because the default [argument\n", - "convention](/mojo/manual/values/ownership#argument-conventions) in an `fn`\n", + "convention](/mojo/manual/values/ownership.html#argument-conventions) in an `fn`\n", "function is `borrowed`—this is a good thing because this function should not\n", "modify the contents of the value being copied.\n", "\n", @@ -414,7 +414,7 @@ "source": [ "What makes Mojo's copy behavior different, compared to other languages, is that\n", "`__copyinit__()` is designed to perform a deep copy of all fields in the type\n", - "(as per [value semantics](/mojo/manual/values/value-semantics)). That is,\n", + "(as per [value semantics](/mojo/manual/values/value-semantics.html)). That is,\n", "it copies heap-allocated values, rather than just copying the pointer.\n", "\n", "However, the Mojo compiler doesn't enforce this, so it's the type author's\n", @@ -512,7 +512,7 @@ "In `HeapArray`, we must use the `__del__()` destructor to free the\n", "heap-allocated data when the `HeapArray` lifetime ends, but Mojo automatically\n", "destroys all other fields when their respective lifetimes end. We'll discuss\n", - "this destructor more in [Death of a value](/mojo/manual/lifecycle/death).\n", + "this destructor more in [Death of a value](/mojo/manual/lifecycle/death.html).\n", "\n", ":::" ] @@ -524,7 +524,7 @@ "If your type doesn't use any pointers for heap-allocated data, then writing the\n", "constructor and copy constructor is all boilerplate code that you shouldn't\n", "have to write. For most structs that don't manage memory explicitly, you can \n", - "just add the [`@value` decorator](/mojo/manual/decorators/value) to your\n", + "just add the [`@value` decorator](/mojo/manual/decorators/value.html) to your\n", "struct definition and Mojo will synthesize the `__init__()`, `__copyinit__()`,\n", "and `__moveinit__()` methods.\n", "\n", @@ -532,7 +532,7 @@ "\n", "Mojo also calls upon the copy constructor when a value is passed to a\n", "function that takes the argument as\n", - "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", + "[`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", "operator `^`), then Mojo instead invokes the move constructor.\n", @@ -547,7 +547,7 @@ "## Move constructor\n", "\n", "Although copying values provides predictable behavior that matches Mojo's\n", - "[value semantics](/mojo/manual/values/value-semantics), copying some data\n", + "[value semantics](/mojo/manual/values/value-semantics.html), copying some data\n", "types can be a significant hit on performance. If you're familiar with\n", "reference semantics, then the solution here might seem clear: instead of making\n", "a copy when passing a value, share the value as a reference. And if the\n", @@ -558,7 +558,7 @@ "\n", "To support moving a value, implement the `__moveinit__()` method. The \n", "`__moveinit__()` method performs a consuming move: it [transfers\n", - "ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", + "ownership](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)\n", "of a value from one variable to another when the original variable's lifetime\n", "ends (also called a \"destructive move\").\n", "\n", @@ -569,7 +569,7 @@ "the move constructors are only part of the implementation for how Mojo\n", "transfers ownership of a value. You can learn more in the section about\n", "[ownership\n", - "transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and).\n", + "transfer](/mojo/manual/values/ownership.html#transfer-arguments-owned-and).\n", "\n", ":::" ] @@ -640,7 +640,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and)).\n", + "operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", @@ -693,7 +693,7 @@ "integers, floats, and booleans, is very cheap. Yet, if you allow your type to\n", "be copied, then there's generally no reason to disallow moves, so you can\n", "synthesize both constructors by adding the [`@value`\n", - "decorator](/mojo/manual/decorators/value).\n", + "decorator](/mojo/manual/decorators/value.html).\n", "\n", ":::" ] @@ -705,13 +705,13 @@ "## Simple value types {#value-decorator}\n", "\n", "Because copy and move constructors are opt-in, Mojo provides great control for\n", - "exotic use cases (such as for atomic values that should never be copied or\n", + "exotic usecases (such as for atomic values that should never be copied or\n", "moved), but most structs are simple aggregations of other types that should be\n", "easily copied and moved, and we don't want to write a lot of boilerplate\n", "constructors for those simple value types.\n", "\n", "To solve this, Mojo provides the [`@value`\n", - "decorator](/mojo/manual/decorators/value), which synthesizes the\n", + "decorator](/mojo/manual/decorators/value.html), which synthesizes the\n", "boilerplate code for the `__init__()`, `__copyinit__()`, and `__moveinit__()`\n", "methods.\n", "\n", @@ -825,7 +825,7 @@ "Also notice that the `MyPet` struct above doesn't include the `__del__()`\n", "destructor (the `@value` decorator does not synthesize this), because Mojo\n", "doesn't need it to destroy fields, as discussed in [Death of a\n", - "value](/mojo/manual/lifecycle/death)\n", + "value](/mojo/manual/lifecycle/death.html)\n", "\n", ":::" ] @@ -871,7 +871,7 @@ "it is safe to ignore for general application-level code.\n", "\n", "For more information, see the [`@register_passable`\n", - "documentation](/mojo/manual/decorators/register-passable).\n", + "documentation](/mojo/manual/decorators/register-passable.html).\n", "\n", ":::note TODO\n", "\n", diff --git a/docs/manual/packages.md b/docs/manual/packages.md index f9a146f37e..aec79c6583 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -9,7 +9,7 @@ Mojo provides a packaging system that allows you to organize and compile code libraries into importable files. This page introduces the necessary concepts about how to organize your code into modules and packages (which is a lot like Python), and shows you how to create a packaged binary with the [`mojo -package`](/mojo/cli/package) command. +package`](/mojo/cli/package.html) command. ## Mojo modules @@ -93,7 +93,7 @@ from a compiled `.mojopkg`/`.📦` file. It makes no real difference to Mojo which way you import a package. When importing from source files, the directory name works as the package name, whereas when importing from a compiled package, the filename is the package name (which you specify with the [`mojo -package`](/mojo/cli/package) command—it can differ from the directory +package`](/mojo/cli/package.html) command—it can differ from the directory name). For example, consider a project with these files: @@ -200,7 +200,7 @@ from mypackage import MyPair This feature explains why some members in the Mojo standard library can be imported from their package name, while others required the `.` notation. For example, the -[`functional`](/mojo/stdlib/algorithm/functional/) module resides in the +[`functional`](/mojo/stdlib/algorithm/functional.html) module resides in the `algorithm` package, so you can import members of that module (such as the `map()` function) like this: @@ -227,6 +227,6 @@ from algorithm import map Which modules in the standard library are imported to the package scope varies, and is subject to change. Refer to the [documentation for each -module](/mojo/lib) to see how you can import its members. +module](/mojo/lib.html) to see how you can import its members. ::: diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 14df3b8a5e..088ee3ced8 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -56,7 +56,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The [`@parameter`](/mojo/manual/decorators/parameter) directive shown here \n", + "The [`@parameter`](/mojo/manual/decorators/parameter.html) directive shown here \n", "causes the `for` loop to be evaluated at compile time. The directive only works\n", "if the loop limits are compile-time constants. Since `count` is a parameter,\n", "`range(count)` can be calculated at compile time.\n", @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -134,20 +134,21 @@ "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n", "but not generic. A generic function or struct is parameterized on _type_. For\n", "example, we could rewrite `repeat[]()` to take any type of argument that\n", - "conforms to the [`Stringable`](/mojo/stdlib/builtin/str#stringable) trait: " + "conforms to the [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) trait: " ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "42\n", - "42\n" + "3\n", + "3\n", + "3\n" ] } ], @@ -157,8 +158,7 @@ " for i in range(count):\n", " print(msg)\n", "\n", - "# Must use keyword parameter for `count`\n", - "repeat[count=2](42)" + "repeat[3](3)" ] }, { @@ -167,40 +167,6 @@ "source": [ "This updated function takes any `Stringable` type, so you can pass it an `Int`,\n", "`String`, or `Bool` value.\n", - "\n", - "You can't pass the `count` as a positional keyword without also specifying `MsgType`.\n", - "You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now\n", - "you can pass the following parameter `count` positionally:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "42\n" - ] - } - ], - "source": [ - "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", - " @parameter\n", - " for i in range(count):\n", - " print(msg)\n", - "\n", - "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", - "repeat[2](42)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ "\n", "Mojo's support for generics is still early. You can write generic functions like\n", "this using traits and parameters. You can also write generic collections like\n", @@ -223,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -258,7 +224,7 @@ "This struct has a single parameter, `ElementType`, which is a placeholder for\n", "the data type you want to store in the array, sometimes called a _type\n", "parameter_. `ElementType` is typed as\n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement), which is a\n", + "[`CollectionElement`](/mojo/stdlib/builtin/type_aliases.html), which is a\n", "[trait](/mojo/manual/traits) representing any type that can be copied and moved.\n", "\n", "As with parameterized functions, you need to pass in parameter values when you\n", @@ -277,7 +243,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -375,7 +341,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -435,7 +401,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -519,7 +485,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -569,7 +535,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -609,7 +575,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -641,7 +607,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -670,7 +636,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -743,7 +709,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -775,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -800,7 +766,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -840,7 +806,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -885,7 +851,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -929,7 +895,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -954,7 +920,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -995,7 +961,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -1040,7 +1006,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -1083,7 +1049,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that\n", + "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter.html) decorator to create a parametric if condition, which is an `if` statement that\n", "runs at compile-time. It requires that its condition be a valid parameter\n", "expression, and ensures that only the live branch of the `if` statement is\n", "compiled into the program. (This is similar to use of the `@parameter` decorator\n", @@ -1102,7 +1068,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -1151,7 +1117,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -1211,7 +1177,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -1247,30 +1213,28 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "alias StringKeyDict = Dict[String, _]\n", - "var b = StringKeyDict[UInt8]()\n", - "b[\"answer\"] = 42" + "alias Bytes = SIMD[DType.uint8, _]\n", + "var b = Bytes[8]()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The\n", - "underscore `_` in the parameter list indicates that the second parameter,\n", - "`V` (the value type), is unbound.\n", - "You specify the `V` parameter later, when you use `StringKeyDict`.\n", + "Here, `Bytes` is a type alias for a `SIMD` vector of bytes. The underscore `_`\n", + "in the parameter list indicates that the second parameter, `width`, is unbound.\n", + "You specify the `width` parameter later, when you use `Bytes`.\n", "\n", "For example, given the following type:" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1367,7 +1331,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1403,7 +1367,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1434,7 +1398,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1451,7 +1415,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1472,7 +1436,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1519,7 +1483,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1527,7 +1491,7 @@ "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", " fn __str__(self) -> String:\n", " var values = StaticIntTuple[3](sugar, cream, chocolate)\n", - " return str(\"Fudge\") + str(values)" + " return str(\"Fudge\") + values" ] }, { @@ -1540,7 +1504,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1562,7 +1526,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1580,7 +1544,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1620,7 +1584,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1640,12 +1604,12 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(str(\"Devoured \") + str(f))" + " print(str(\"Devoured \") + str(f))" ] }, { @@ -1661,7 +1625,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1678,7 +1642,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1707,7 +1671,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb deleted file mode 100644 index a893a9a84c..0000000000 --- a/docs/manual/pointers.ipynb +++ /dev/null @@ -1,756 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Unsafe pointers\n", - "description: Using unsafe pointers to access dynamically-allocated memory.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type \n", - "creates an indirect reference to a location in memory.\n", - "You can use an `UnsafePointer` to dynamically allocate and free memory, or to\n", - "point to memory allocated by some other piece of code. You can use these\n", - "pointers to write code that interacts with low-level interfaces, to interface\n", - "with other programming languages, or to build certain kinds of data structures.\n", - "But as the name suggests, they're inherently _unsafe_. For example, when using\n", - "unsafe pointers, you're responsible for ensuring that memory gets allocated and\n", - "freed correctly.\n", - "\n", - ":::note \n", - "\n", - "In addition to unsafe pointers, Mojo supports a safe \n", - "[`Reference`](/mojo/stdlib/memory/reference/Reference) type. See\n", - "[`UnsafePointer` and `Reference`](#unsafepointer-and-reference) for a brief\n", - "comparison of the types.\n", - "\n", - ":::\n", - "\n", - "## What is a pointer?\n", - "\n", - "An `UnsafePointer` is a type that holds an address to memory. You can store\n", - "and retrieve values in that memory. The `UnsafePointer` type is _generic_—it can \n", - "point to any type of value, and the value type is specified as a parameter. The\n", - "value pointed to by a pointer is sometimes called a _pointee_." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, initialize_pointee_move\n", - "\n", - "# Allocate memory to hold a value\n", - "var ptr = UnsafePointer[Int].alloc(1)\n", - "# Initialize the allocated memory\n", - "initialize_pointee_copy(ptr, 100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - "\n", - " ![](./images/pointer-diagram.png#light)\n", - " ![](./images/pointer-diagram-dark.png#dark)\n", - "\n", - "
Figure 1. Pointer and pointee
\n", - "
\n", - "\n", - "\n", - "Accessing the memory—to retrieve or update a value—is called \n", - "_dereferencing_ the pointer. You can dereference a pointer by following the\n", - "variable name with an empty pair of square brackets:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "110\n" - ] - } - ], - "source": [ - "# Update an initialized value\n", - "ptr[] += 10\n", - "# Access an initialized value\n", - "print(ptr[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also allocate memory to hold multiple values to build array-like\n", - "structures. For details, see \n", - "[Storing multiple values](#storing-multiple-values)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Lifecycle of a pointer\n", - "\n", - "At any given time, a pointer can be in one of several states:\n", - "\n", - "- Uninitialized. Just like any variable, a variable of type `UnsafePointer` can\n", - " be declared but uninitialized.\n", - "\n", - " \n", - " ```mojo\n", - " var ptr: UnsafePointer[Int]\n", - " ```\n", - "\n", - "- Null. A null pointer has an address of 0, indicating an invalid pointer.\n", - "\n", - " ```mojo\n", - " ptr = UnsafePointer[Int]()\n", - " ```\n", - "\n", - "- Pointing to allocated, uninitialized memory. The `alloc()` static method\n", - " returns a pointer to a newly-allocated block of memory with space for the \n", - " specified number of elements of the pointee's type.\n", - "\n", - " ```mojo\n", - " ptr = UnsafePointer[Int].alloc(1)\n", - " ```\n", - " Trying to dereference a pointer to uninitialized memory results in undefined \n", - " behavior. \n", - "\n", - "- Pointing to initialized memory. You can initialize an allocated, uninitialized\n", - " pointer by moving or copying an existing value into the memory. Or you can use\n", - " the `address_of()` static method to get a pointer to an existing value. \n", - "\n", - " ```mojo\n", - " initialize_pointee_copy(ptr, value)\n", - " # or\n", - " initalize_pointee_move(ptr, value^)\n", - " # or \n", - " ptr = UnsafePointer[Int].address_of(value)\n", - " ```\n", - " \n", - " Once the value is initialized, you can read or mutate it using the dereference\n", - " syntax: \n", - "\n", - " ```mojo\n", - " oldValue = ptr[]\n", - " ptr[] = newValue\n", - " ```\n", - "\n", - "- Dangling. When you free the pointer's allocated memory, you're left with a \n", - " _dangling pointer_. The address still points to its previous location, but the\n", - " memory is no longer allocated to this pointer. Trying to dereference the\n", - " pointer, or calling any method that would access the memory location results\n", - " in undefined behavior.\n", - "\n", - " ```mojo\n", - " ptr.free()\n", - " ```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "The following diagram shows the lifecycle of an `UnsafePointer`:\n", - "\n", - "
\n", - "\n", - " ![](./images/pointer-lifecycle.png#light)\n", - " ![](./images/pointer-lifecycle-dark.png#dark)\n", - "\n", - "
Figure 2. Lifecycle of an UnsafePointer
\n", - "
\n", - "\n", - "### Allocating memory\n", - "\n", - "Use the static `alloc()` method to allocate memory. The method returns a new\n", - "pointer pointing to the requested memory. You can allocate space for one or \n", - "more values of the pointee's type.\n", - "\n", - "```mojo\n", - "ptr = UnsafePointer[Int].alloc(10) # Allocate space for 10 Int values\n", - "```\n", - "\n", - "The allocated space is _uninitialized_—like a variable that's been declared but\n", - "not initialized.\n", - "\n", - "### Initializing the pointee\n", - "\n", - "The `unsafe_pointer` module includes a number of free functions for working with\n", - "the `UnsafePointer` type. To initialize allocated memory, you can use the \n", - "[`initialize_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy)\n", - "or [`initialize_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move)\n", - "functions:\n", - "\n", - "```mojo\n", - "initialize_pointee_copy(ptr, 5)\n", - "```\n", - "\n", - "To move a value into the pointer's memory location, use\n", - "`initialize_pointee_move()`:\n", - "\n", - "```mojo\n", - "initialize_pointee_move(str_ptr, my_string^)\n", - "```\n", - "\n", - "Note that to move the value, you usually need to add the transfer operator\n", - "(`^`), unless the value is a [trivial\n", - "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", - "`Int`) or a newly-constructed, \"owned\" value:\n", - "\n", - "```mojo\n", - "initialize_pointee_move(str_ptr, str(\"Owned string\"))\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternately, you can get a pointer to an existing value using the static \n", - "`address_of()` method. This is useful for getting a pointer to a value on the \n", - "stack, for example." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "var counter: Int = 5\n", - "ptr = UnsafePointer[Int].address_of(counter)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that when calling `address_of()`, you don't need to allocate memory ahead\n", - "of time, since you're pointing to an existing value." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "#### Initializing from an address\n", - "\n", - "When exchanging data with other programming languages, you may need to construct\n", - "an `UnsafePointer` from an address. For example, if you're working with a\n", - "pointer allocated by a C or C++ library, or a Python object that implements the\n", - "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n", - "you can construct an `UnsafePointer` to access the data from the Mojo side.\n", - "\n", - "You can construct an `UnsafePointer` from an integer address using the `address`\n", - "keyword argument. For example, the following code creates a NumPy array and then\n", - "accesses the data using a Mojo pointer:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, 3, 4, 5, 6, 7, 8, 9, " - ] - } - ], - "source": [ - "from python import Python\n", - "from memory.unsafe_pointer import UnsafePointer\n", - "\n", - "def share_array():\n", - " np = Python.import_module(\"numpy\")\n", - " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - " addr = int(arr.__array_interface__[\"data\"][0])\n", - " ptr = UnsafePointer[Int64](address=addr)\n", - " for i in range(9):\n", - " print(ptr[i], end=\", \")\n", - "\n", - "share_array()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When dealing with memory allocated elsewhere, you need to be aware of who's\n", - "responsible for freeing the memory. Freeing memory allocated elsewhere \n", - "can result in undefined behavior.\n", - "\n", - "You also need to be aware of the format of the data stored in memory, including\n", - "data types and byte order. For more information, see \n", - "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Dereferencing pointers\n", - "\n", - "Use the `[]` dereference operator to access the value stored at a pointer (the\n", - " \"pointee\").\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "110\n" - ] - } - ], - "source": [ - "# Read from pointee\n", - "print(ptr[])\n", - "# mutate pointee\n", - "ptr[] = 0\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "If you've allocated space for multiple values, you can use subscript syntax to\n", - "access the values, as if they were an array, like `ptr[3]`. The empty subscript\n", - "`[]` has the same meaning as `[0]`.\n", - "\n", - ":::caution\n", - "\n", - "The dereference operator assumes that the memory being dereferenced is \n", - "initialized. Dereferencing uninitialized memory results in undefined behavior.\n", - "\n", - ":::\n", - "\n", - "You cannot safely use the dereference operator on uninitialized memory, even to\n", - "_initialize_ a pointee. This is because assigning to a dereferenced pointer\n", - "calls lifecycle methods on the existing pointee (such as the destructor, move\n", - "constructor or copy constructor).\n", - "\n", - "```mojo\n", - "str_ptr = UnsafePointer[String].alloc(1)\n", - "# str_ptr[] = \"Testing\" # Undefined behavior!\n", - "initialize_pointee_move(str_ptr, \"Testing\")\n", - "str_ptr[] += \" pointers\" # Works now\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Destroying or removing values\n", - "\n", - "The \n", - "[`move_from_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee)\n", - "function moves the pointee from the memory location pointed to by `ptr`. This is\n", - "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n", - "the memory location uninitialized.\n", - "\n", - "The [`destroy_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", - "function calls the destructor on the pointee, and leaves the memory location\n", - "pointed to by `ptr` uninitialized. \n", - "\n", - "Both `move_from_pointee()` and `destroy_pointee()` require that the pointer is \n", - "non-null, and the memory location contains a valid, initialized value of the \n", - "pointee's type; otherwise the function results in undefined behavior.\n", - "\n", - "The [`move_pointee(src, dst)`](/mojo/stdlib/memory/unsafe_pointer/move_pointee)\n", - "function moves the pointee from one pointer location to another. Both pointers\n", - "must be non-null. The source location must contain a valid, initialized value of \n", - "the pointee's type, and is left uninitialized after the call. The destination \n", - "location is assumed to be uninitialized—if it contains a valid value, that\n", - "value's destructor is not run. The value from the source location is moved to\n", - "the destination location as a consuming move. This function also has undefined\n", - "behavior if any of its prerequisites is not met." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Freeing memory\n", - "\n", - "Calling [`free()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#free) on a \n", - "pointer frees the memory allocated by the pointer. It doesn't call the \n", - "destructors on any values stored in the memory—you need to do that explicitly\n", - "(for example, using\n", - "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) or\n", - "one of the other functions described in \n", - "[Destroying or removing values](#destroying-or-removing-values)).\n", - "\n", - "Disposing of a pointer without freeing the associated memory can result in a\n", - "memory leak—where your program keeps taking more and more memory, because not\n", - "all allocated memory is being freed.\n", - "\n", - "On the other hand, if you have multiple copies of a pointer accessing the same\n", - "memory, you need to make sure you only call `free()` on one of them. Freeing the\n", - "same memory twice is also an error.\n", - "\n", - "After freeing a pointer's memory, you're left with a dangling pointer—its\n", - "address still points to the freed memory. Any attempt to access the memory,\n", - "like dereferencing the pointer results in undefined behavior.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Storing multiple values\n", - "\n", - "As mentioned in [Allocating memory](#allocating-memory), you can use an \n", - "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n", - "as a single, contiguous block. Pointers support arithmetic: adding an integer\n", - "to a pointer returns a new pointer offset by the specified number of values from\n", - "the original pointer:\n", - "\n", - "```mojo\n", - "third_ptr = first_ptr + 2\n", - "```\n", - "\n", - "Pointers also support subtraction, as well as in-place addition and subtraction:\n", - "\n", - "```mojo\n", - "# Advance the pointer one element:\n", - "ptr += 1\n", - "```\n", - "\n", - "
\n", - "\n", - " ![](./images/pointer-offset.png#light)\n", - " ![](./images/pointer-offset-dark.png#dark)\n", - "\n", - "
Figure 3. Pointer arithmetic
\n", - "
\n", - "\n", - "For example, the following example allocates memory to store 6 `Float64`\n", - "values, and initializes them all to zero." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "float_ptr = UnsafePointer[Float64].alloc(6)\n", - "for offset in range(6):\n", - " initialize_pointee_copy(float_ptr+offset, 0.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once the values are initialized, you can access them using subscript syntax:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0, 0.0, 3.0, 0.0, 0.0, 0.0, " - ] - } - ], - "source": [ - "float_ptr[2] = 3.0\n", - "for offset in range(6):\n", - " print(float_ptr[offset], end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Converting data: bitcasting and byte order\n", - "\n", - "Bitcasting a pointer returns a new pointer that has the same memory location,\n", - "but a new data type. This can be useful if you need to access different types of\n", - "data from a single area of memory. This can happen when you're reading binary\n", - "files, like image files, or receiving data over the network.\n", - "\n", - "The following sample processes a format that consists of chunks of data,\n", - "where each chunk contains a variable number of 32-bit integers.\n", - "Each chunk begins with an 8-bit integer that identifies the number of values\n", - "in the chunk." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "def read_chunks(owned ptr: UnsafePointer[UInt8]) -> List[List[UInt32]]:\n", - " chunks = List[List[UInt32]]()\n", - " # A chunk size of 0 indicates the end of the data\n", - " chunk_size = int(ptr[])\n", - " while (chunk_size > 0):\n", - " # Skip the 1 byte chunk_size and get a pointer to the first\n", - " # UInt32 in the chunk\n", - " ui32_ptr = (ptr + 1).bitcast[UInt32]()\n", - " chunk = List[UInt32](capacity=chunk_size)\n", - " for i in range(chunk_size):\n", - " chunk.append(ui32_ptr[i])\n", - " chunks.append(chunk)\n", - " # Move our pointer to the next byte after the current chunk\n", - " ptr += (1 + 4 * chunk_size)\n", - " # Read the size of the next chunk\n", - " chunk_size = int(ptr[])\n", - " return chunks" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When dealing with data read in from a file or from the network, you may also\n", - "need to deal with byte order. Most systems use little-endian byte order (also\n", - "called least-signficicant byte, or LSB) where the least-significant byte in a\n", - "multibyte value comes first. For example, the number 1001 can be represented in\n", - "hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a\n", - "16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit \n", - "integer, it would be represented as E9 03 00 00. \n", - "\n", - "Big-endian or most-significant byte (MSB) ordering is the opposite: in the \n", - "32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and\n", - "when transmitting data over the network. You can use the \n", - "[`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap) function to swap the byte\n", - "order of a SIMD value from big-endian to little-endian or the reverse. For\n", - "example, if the method above was reading big-endian data, you'd just need to\n", - "change a single line:\n", - "\n", - "```mojo\n", - "chunk.append(byte_swap(ui32_ptr[i]))\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `DTypePointer`: handling numeric data\n", - "\n", - "A [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", - "pointer that supports some additional methods for loading and storing numeric\n", - "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n", - "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n", - "[SIMD and DType](/mojo/manual/types#simd-and-dtype).\n", - "\n", - "`DTypePointer` has a similar API to `UnsafePointer`:\n", - "\n", - "- You can [`alloc()`](/mojo/stdlib/memory/unsafe/DTypePointer#alloc) and\n", - " [`free()`](/mojo/stdlib/memory/unsafe/DTypePointer#free) memory, or use \n", - " [`address_of()`](/mojo/stdlib/memory/unsafe/DTypePointer#address_of) to point\n", - " to an existing value.\n", - "- The pointer supports pointer arithmetic to access adjacent memory locations.\n", - "- You can dereference a `DTypePointer` using subscript notation.\n", - "- You can construct a `DTypePointer` from an `Int` address.\n", - "\n", - "\n", - "You can also construct a `DTypePointer` from an `UnsafePointer` of a scalar\n", - "type like `Int64` or `Float32`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import DTypePointer, UnsafePointer\n", - "\n", - "uptr = UnsafePointer[Float64].alloc(10)\n", - "dptr = DTypePointer(uptr)\n", - "# Or:\n", - "dptr = DTypePointer[DType.float64].alloc(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Unlike `UnsafePointer`, `DTypePointer` doesn't have special methods to\n", - "initialize values, destroy them, or move them out. Because all of the values\n", - "that `DTypePointer` works with are trivial types, `DTypePointer` doesn't need to\n", - "destroy values before overwriting them or freeing memory. Instead, you can use\n", - "subscript notation (like `UnsafePointer`) or use the\n", - "[`load()`](/mojo/stdlib/memory/unsafe/DTypePointer#load) and \n", - "[`store()`](/mojo/stdlib/memory/unsafe/DTypePointer#store) methods to access \n", - "values.\n", - "\n", - "What `DTypePointer` adds is various methods of loading and storing SIMD values\n", - "to memory. In particular: strided load/store and gather/scatter.\n", - "\n", - "Strided load loads values from memory into a SIMD vector using an offset (the\n", - "\"stride\") between successive memory addresses. This can be useful for\n", - "extracting rows or columns from tabular data, or for extracting individual\n", - "values from structured data. For example, consider the data for an RGB image,\n", - "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n", - "you want to access just the red values, you can use a strided load or store.\n", - "\n", - "
\n", - "\n", - " ![](./images/strided-load-storage.png#light)\n", - " ![](./images/strided-load-storage-dark.png#dark)\n", - "\n", - "
Figure 4. Strided load
\n", - "
\n", - "\n", - "The following function uses the \n", - "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_load)\n", - "and \n", - "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n", - "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", - "that this function only handles images where the number of pixels is evenly\n", - "divisible by eight.)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def invert_red_channel(ptr: DTypePointer[DType.uint8], pixel_count: Int):\n", - " # number of values loaded or stored at a time\n", - " alias simd_width = 8\n", - " # bytes per pixel, which is also the stride size\n", - " bpp = 3\n", - " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", - " red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n", - " # Invert values and store them in their original locations\n", - " ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note Future of `DTypePointer`\n", - "\n", - "The `DTypePointer` type exists for historical reasons, but it no longer really\n", - "needs to be a separate type. `UnsafePointer` can handle most things that\n", - "`DTypePointer` does except for a few features related to reading and writing\n", - "`SIMD` values. At some point in the future, these features will probably be\n", - "integrated into the `SIMD` type, so you can use them with `UnsafePointer`.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Safety\n", - "\n", - "Unsafe pointers are unsafe for several reasons:\n", - "\n", - "- Memory management is up to the user. You need to manually allocate\n", - " and free memory, and be aware of when other APIs are allocating or freeing\n", - " memory for you.\n", - "\n", - "- `UnsafePointer` and `DTypePointer` values are _nullable_—that is, the pointer\n", - " is not guaranteed to point to anything. And even when a pointer points to\n", - " allocated memory, that memory may not be _initialized_.\n", - "\n", - "- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`.\n", - " When you use an `UnsafePointer`, managing memory and knowing when to destroy\n", - " objects is your responsibility. (Since `DTypePointer` only works with trivial\n", - " types, this is not typically an issue for `DTypePointer`.)\n", - "\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `UnsafePointer` and `Reference`\n", - "\n", - "The [`Reference`](/mojo/stdlib/memory/reference/Reference) type is essentially a \n", - "safe pointer type. Like a pointer, you can derferences a `Reference` using the \n", - "dereference operator, `[]`. However, the `Reference` type has several\n", - "differences from `UnsafePointer` which make it safer:\n", - "\n", - "- A `Reference` is _non-nullable_. A reference always points to something.\n", - "- You can't allocate or free memory using a `Reference`—only point to an\n", - " existing value.\n", - "- A `Reference` only refers to a single value. You can't do pointer arithmetic\n", - " with a `Reference`.\n", - "- A `Reference` has an associated _lifetime_, which connects it back to an\n", - " original, owned value. The lifetime ensures that the value won't be destroyed\n", - " while the reference exists.\n", - "\n", - "The `Reference` type shouldn't be confused with the immutable and mutable\n", - "references used with the `borrowed` and `inout` argument conventions. Those\n", - "references do not require explicit dereferencing, unlike a `Reference` or \n", - "`UnsafePointer`." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index e9e3caa2b1..374bbbe44a 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -20,74 +20,59 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Mojo is still in early development and many Python features are not yet\n", + "Our long-term goal is to make Mojo a *superset of Python* (that is, to make Mojo \n", + "compatible with existing Python programs). Python programmers should be able to\n", + "use Mojo immediately, and be able to access the huge ecosystem of Python \n", + "packages that are available today. \n", + "\n", + "However, Mojo is still in early development and many Python features are not yet\n", "implemented. You can't currently write everything in Mojo that you can write in\n", "Python. And Mojo doesn't have its own ecosystem of packages yet.\n", "\n", "To help bridge this gap, Mojo lets you import Python modules, call Python \n", - "functions, and interact with Python objects from Mojo code. The Python code\n", - "runs in a standard Python interpreter (CPython), so your existing Python code\n", + "functions and interact with Python objects from Mojo code. It runs Python code\n", + "using a standard Python interpreter (CPython), so your existing Python code\n", "doesn't need to change.\n", "\n", - "## Create a Python environment\n", - "\n", - "To successfully integrate Python code with your Mojo project, your environment\n", - "must have a compatible Python runtime installed along with any additional\n", - "Python packages that you want to use. Currently, you can create a compatible\n", - "environment in a couple of ways:\n", - "\n", - "- We recommend that you use [Magic](/magic), our package manager and\n", - " virtual environment manager for MAX and Mojo projects. To use Magic to create\n", - " and manage the virtual environment for your Mojo/Python project, first\n", - " follow the instructions in [Install Magic](/magic/#install-magic).\n", - " Then you can create a new Mojo project like this:\n", - "\n", - " ```sh\n", - " magic init my-mojo-project --format mojoproject\n", - " ```\n", - "\n", - " After creating the project, you can enter the project and install any\n", - " dependencies, for example [NumPy](https://numpy.org/):\n", - "\n", - " ```sh\n", - " cd my-mojo-project\n", - " ```\n", - "\n", - " ```sh\n", - " magic add \"numpy>=2.0\"\n", - " ```\n", - "\n", - "- Alternatively, you can also add MAX and Mojo to a\n", - " [conda](https://docs.conda.io/projects/conda/en/latest/index.html) project.\n", - " To do so, follow the steps in [Add MAX/Mojo to a conda project](/magic/conda).\n", - "\n", - "- It's also possible to convert an existing conda project to Magic as documented\n", - " in [Migrate a conda project to Magic](/magic/#migrate-a-conda-project-to-magic).\n", - "\n", "## Import a Python module\n", "\n", "To import a Python module in Mojo, just call \n", "[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) \n", - "with the module name. The following shows an example of importing the standard\n", - "Python [NumPy](https://numpy.org/) package:\n", - "\n", - "```mojo\n", + "with the module name:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ "from python import Python\n", "\n", - "def main():\n", + "fn use_array() raises:\n", " # This is equivalent to Python's `import numpy as np`\n", - " np = Python.import_module(\"numpy\")\n", + " var np = Python.import_module(\"numpy\")\n", "\n", " # Now use numpy as if writing in Python\n", - " array = np.array([1, 2, 3])\n", - " print(array)\n", - "```\n", - "\n", - "Running this program produces the following output:\n", - "\n", - "```\n", - "[1 2 3]\n", - "```" + " var array = np.array([1, 2, 3])\n", + " print(array)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1 2 3]\n" + ] + } + ], + "source": [ + "use_array()" ] }, { @@ -95,21 +80,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Assuming that you have the NumPy package installed in your\n", - "[environment](#create-a-python-environment), this imports NumPy and you can use any\n", - "of its features.\n", + "Yes, this imports Python NumPy, and you can import *any other Python module*\n", + "that you have installed.\n", "\n", "A few things to note:\n", "\n", - "- The `import_module()` method returns a reference to the module in the form of\n", - " a [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject)\n", - " wrapper. You must store the reference in a variable and then use it as shown\n", - " in the example above to access functions, classes, and other objects defined\n", - " by the module. See [Mojo wrapper objects](/mojo/manual/python/types#mojo-wrapper-objects)\n", - " for more information about the `PythonObject` type.\n", - "\n", "- Currently, you cannot import individual members (such as a single Python class\n", - " or function). You must import the whole Python module and then access members\n", + " or function)—you must import the whole Python module and then access members\n", " through the module name.\n", "\n", "- Mojo doesn't yet support top-level code, so the `import_module()` call must\n", @@ -124,14 +101,14 @@ " the function signature. You'll also see this when calling Python functions\n", " that may raise exceptions. (Raising exceptions is much more common in Python\n", " code than in the Mojo standard library, which \n", - " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", + " [limits their use for performance reasons](/mojo/roadmap.html#the-standard-library-has-limited-exceptions-use).)\n", "\n", ":::note\n", "\n", "Mojo loads the Python interpreter and Python modules at runtime, so\n", "wherever you run a Mojo program, it must be able to access a compatible Python\n", "interpreter, and to locate any imported Python modules. For more information,\n", - "see [Create a Python environment](#create-a-python-environment).\n", + "see [Python environment](#python-environment).\n", "\n", ":::" ] @@ -162,17 +139,17 @@ "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", - "def main():\n", + "fn main() raises:\n", " Python.add_to_path(\"path/to/module\")\n", - " mypython = Python.import_module(\"mypython\")\n", + " var mypython = Python.import_module(\"mypython\")\n", "\n", - " values = mypython.gen_random_values(2, 3)\n", + " var values = mypython.gen_random_values(2, 3)\n", " print(values)\n", "```\n", "\n", "Both absolute and relative paths work with \n", - "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example,\n", - "you can import from the local directory like this:\n", + "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example, you\n", + "can import from the local directory like this:\n", "\n", "```mojo\n", "Python.add_to_path(\".\")\n", @@ -207,10 +184,17 @@ "application drive the event loop and poll for updates. The following example\n", "uses Tkinter, but the basic approach can be applied to other packages.\n", "\n", - "First you create a Python module that defines a Tkinter interface, with a window\n", - "and single button:\n", - "\n", - "```{.python filename=\"myapp.py\"}\n", + "First we create a Python module that defines a Tkinter interface, with a window\n", + "and single button:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%%python\n", "import tkinter as tk\n", "\n", "class App:\n", @@ -234,33 +218,37 @@ " self.create_button(\"Hello Mojo!\")\n", "\n", " def update(self):\n", - " self._root.update()\n", - "```" + " self._root.update()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "You can call this module from Mojo like this:\n", - "\n", - "```{.mojo filename=\"main.mojo\"}\n", + "We can call this module from Mojo like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ "from python import Python\n", "\n", - "def button_clicked():\n", + "fn button_clicked():\n", " print(\"Hi from a Mojo🔥 fn!\")\n", "\n", "def main():\n", " Python.add_to_path(\".\")\n", - " app = Python.import_module(\"myapp\").App()\n", + " var app = Python.import_module(\"myapp\").App()\n", " app.create(\"800x600\")\n", "\n", " while True:\n", " app.update()\n", " if app.clicked:\n", " button_clicked()\n", - " app.clicked = False\n", - "```" + " app.clicked = False" ] }, { @@ -269,7 +257,93 @@ "source": [ "Instead of the Python module calling the Tkinter `mainloop()` method, the Mojo \n", "code calls the `update()` method in a loop and checks the `clicked` attribute \n", - "after each update.\n" + "after each update.\n", + "\n", + "## Python environment\n", + "\n", + "The Mojo SDK depends on an existing installed version of Python that includes\n", + "a shared library version of the Python interpreter. When you install the Mojo\n", + "SDK, it tries to locate a compatible version of the Python interpreter and set\n", + "up Python's `sys.path` to load matching modules. In most cases this just works\n", + "and you don't have to do any further configuration of your Python environment. \n", + "\n", + "If you run into problems after installing Mojo, see the following sections.\n", + "\n", + "### Installation issues\n", + "\n", + "When the installer runs, it tries to locate the CPython shared library using the \n", + "[find_libpython](https://pypi.org/project/find-libpython/) module.\n", + "\n", + "This may fail if one of the following is true:\n", + "\n", + "- There is no version of Python installed, or the installed version isn't\n", + " supported by the Mojo SDK.\n", + "\n", + "- The installer can't find a shared library version of the CPython interpreter \n", + " (for example, `.so` or `.dylib` file). Some Python distributions don't include\n", + " shared libraries, which prevents Mojo from embedding the interpreter.\n", + "\n", + "If one of these things is the case, you'll need to install a compatible version\n", + "of Python that includes shared libraries. Try following the instructions in \n", + "[Set up a Python environment with Conda](#set-up-a-python-environment-with-conda)\n", + "to install a virtual environment. \n", + "\n", + "### Set up a Python environment with Conda\n", + "\n", + "Using a Python virtual environment like \n", + "[Conda](https://docs.conda.io/en/latest/) is one way to avoid problems with \n", + "your Python installation. This provides a consistent Python environment with a\n", + "known version of Python and all of the Python packages you want to use with\n", + "Mojo.\n", + "\n", + "To set up a virtual environment with Conda:\n", + "\n", + "1. Install Conda by following the \n", + " [Quick command-line install instructions](https://docs.conda.io/projects/miniconda/en/latest/#quick-command-line-install).\n", + "\n", + " Make sure to initialize Conda for the shell or shells you use, for example:\n", + "\n", + " ```bash\n", + " ~/miniconda3/bin/conda init zsh\n", + " ```\n", + "\n", + " Or:\n", + "\n", + " ```bash\n", + " ~/miniconda3/bin/conda init --all\n", + " ```\n", + "\n", + "2. Restart your shell. \n", + "\n", + "3. Run the following command to configure Mojo to use the Python shared library\n", + " from your Conda environment:\n", + "\n", + " ```bash\n", + " export MOJO_PYTHON_LIBRARY=\"$(find $CONDA_PREFIX/lib -iname 'libpython*.[s,d]*' | sort -r | head -n 1)\"\n", + " echo \"export MOJO_PYTHON_LIBRARY=$MOJO_PYTHON_LIBRARY\" >> ~/.zshrc\n", + " ```\n", + "\n", + " **Note:** If you're using a shell other than zsh, you'll need to adjust these\n", + " commands. For example, if you're using bash, replace `.zshrc` with the\n", + " shell configuration file you use, such as `.bashrc` or `.bash_profile`.\n", + "\n", + "4. Try running the Mojo REPL:\n", + "\n", + " ```bash\n", + " mojo\n", + " ```\n", + "\n", + "After setting up the Conda virtual environment, you can install any Python \n", + "packages you want to use with Mojo using the `conda install` command. For\n", + "example:\n", + "\n", + "```bash\n", + "conda install numpy\n", + "```\n", + "\n", + "For more information on using Conda with Mojo, see \n", + "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on\n", + "the Modular Blog." ] } ], diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index c7c4c4e558..24533e268a 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -128,7 +128,7 @@ "### Mojo wrapper objects\n", "\n", "When you use Python objects in your Mojo code, Mojo adds the \n", - "[`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) wrapper around\n", + "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", "the Python object. This object exposes a number of common double underscore\n", "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n", "through to the underlying Python object. " @@ -215,11 +215,11 @@ "using the built-in \n", "[`int()`](/mojo/stdlib/builtin/int/int-function),\n", "[`str()`](/mojo/stdlib/builtin/str/str),\n", - "and [`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python \n", + "and [`bool()`](/mojo/stdlib/builtin/bool/bool) functions, and print Python \n", "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", - "[`to_float64()`](/mojo/stdlib/python/python_object/PythonObject#to_float64) for \n", + "[`to_float64()`](/mojo/stdlib/python/object/PythonObject#to_float64) for \n", "converting to a Mojo floating point value.\n", "\n", "```mojo\n", @@ -267,7 +267,13 @@ "metadata": {}, "source": [ "One TODO item here: The `Python.is_type()` method is misleadingly named, since \n", - "it doesn't compare _types_, but object identity.\n" + "it doesn't compare _types_, but object identity.\n", + "\n", + "## Further reading\n", + "\n", + "For more information, see \n", + "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on \n", + "the Modular Blog." ] } ], diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index ba46521ccd..2067504437 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -34,8 +34,8 @@ "example, all the data types in Mojo's standard library (such as `Int`,\n", "`Bool`, `String`, and `Tuple`) are defined as structs.\n", "\n", - "If you understand how [functions](/mojo/manual/functions) and\n", - "[variables](/mojo/manual/variables) work in Mojo, you probably\n", + "If you understand how [functions](/mojo/manual/functions.html) and\n", + "[variables](/mojo/manual/variables.html) work in Mojo, you probably\n", "noticed that Mojo is designed to provide dynamic programming features in a\n", "`def` function while enforcing stronger code safety in `fn` functions. When it\n", "comes to structs, Mojo leans toward the safe side: You can still choose whether\n", @@ -92,7 +92,7 @@ "source": [ "Notice that the first argument in the `__init__()` method is `inout self`. For\n", "now, ignore `inout` (it's an [argument\n", - "convention](/mojo/manual/values/ownership#argument-conventions) that\n", + "convention](/mojo/manual/values/ownership.html#argument-conventions) that\n", "declares `self` as a mutable reference); all you need to know right now is that\n", "`self` must be the first argument. It references the current struct instance\n", "(it allows code in the method to refer to \"itself\"). *When you call the\n", @@ -283,7 +283,7 @@ "while being safe and easy to use.\n", "\n", "- Mojo structs do not support inheritance (\"sub-classing\"), but a struct can\n", - " implement [traits](/mojo/manual/traits).\n", + " implement [traits](/mojo/manual/traits.html).\n", "\n", "- Python classes support class attributes—values that are shared by all\n", " instances of the class, equivalent to class variables or static data members\n", @@ -337,7 +337,7 @@ "\n", "Mojo supports a long list of special methods; far too many to discuss here, but\n", "they generally match all of [Python's special\n", - "methods](https://docs.python.org/3/reference/datamodel#special-method-names)\n", + "methods](https://docs.python.org/3/reference/datamodel.html#special-method-names)\n", "and they usually accomplish one of two types of tasks:\n", "\n", "- Operator overloading: A lot of special methods are designed to overload\n", @@ -367,7 +367,7 @@ "source": [ "### `@value` decorator\n", "\n", - "When you add the [`@value` decorator](/mojo/manual/decorators/value) to a\n", + "When you add the [`@value` decorator](/mojo/manual/decorators/value.html) to a\n", "struct, Mojo will synthesize the essential lifecycle methods so your object\n", "provides full value semantics. Specifically, it generates the `__init__()`,\n", "`__copyinit__()`, and `__moveinit__()` methods, which allow you to construct,\n", @@ -460,7 +460,7 @@ "`__init__()`, this code also introduces `owned`, which is another argument\n", "convention that ensures the argument has unique ownership of the value.\n", "For more detail, see the section about [value\n", - "ownership](/mojo/manual/values/ownership).\n" + "ownership](/mojo/manual/values/ownership.html).\n" ] } ], diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 2085c49e29..03a38fd176 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -68,7 +68,7 @@ "you're passing it, only the fact that they implement the `quack()` method.\n", "\n", "In a statically-typed environment, this approach doesn't work:\n", - "[`fn` functions](/mojo/manual/functions#fn-functions) require you to\n", + "[`fn` functions](/mojo/manual/functions.html#fn-functions) require you to\n", "specify the type of each argument. If you wanted to write this example in Mojo \n", "_without_ traits, you'd need to write a function overload for each input type.\n", "All of the examples from here on are in Mojo, so we'll just call the function\n", @@ -208,7 +208,7 @@ "metadata": {}, "source": [ "This syntax may look a little unfamiliar if you haven't dealt with Mojo\n", - "[parameters](/mojo/manual/parameters/) before. What this signature\n", + "[parameters](/mojo/manual/parameters/index.html) before. What this signature\n", "means is that `maybe_a_duck` is an argument of type `T`, where `T` is a type\n", "that must conform to the `Quackable` trait. TODO: This syntax is a little \n", "verbose, and we hope to make it more ergonomic in a future release.\n", @@ -378,7 +378,7 @@ "`MassProducible` type has a default (no-argument) constructor and can be moved.\n", "It uses the built-in [`Movable`](/mojo/stdlib/builtin/value/Movable) trait,\n", "which requires the type to have a [move \n", - "constructor](/mojo/manual/lifecycle/life#move-constructor).\n", + "constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", "\n", "The `factory[]()` function returns a newly-constructed instance of a \n", "`MassProducible` type." @@ -415,7 +415,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable#register_passabletrivial) \n", + "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable.html#register_passabletrivial) \n", "types have restrictions on their lifecycle methods: they can't define copy or\n", "move constructors, because they don't require any custom logic.\n", "\n", @@ -441,21 +441,14 @@ "by a number of standard library types, and you can also implement these on your\n", "own types:\n", "\n", - " - [`Absable`](/mojo/stdlib/builtin/math/Absable)\n", " - [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType)\n", " - [`Boolable`](/mojo/stdlib/builtin/bool/Boolable)\n", - " - [`BoolableCollectionElement`](/mojo/stdlib/builtin/value/BoolableCollectionElement)\n", - " - [`BoolableKeyElement`](/mojo/stdlib/builtin/value/BoolableKeyElement)\n", " - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", - " - [`Powable`](/mojo/stdlib/builtin/math/Powable)\n", - " - [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", - " - [`RepresentableCollectionElement`](/mojo/stdlib/builtin/value/RepresentableCollectionElement)\n", - " - [`RepresentableKeyElement`](/mojo/stdlib/collections/dict/RepresentableKeyElement)\n", " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", "\n", @@ -561,7 +554,7 @@ "When building a generic container type, one challenge is knowing how to dispose\n", "of the contained items when the container is destroyed. Any type that \n", "dynamically allocates memory needs to supply a \n", - "[destructor](/mojo/manual/lifecycle/death#destructor) (`__del__()` method)\n", + "[destructor](/mojo/manual/lifecycle/death.html#destructor) (`__del__()` method)\n", "that must be called to free the allocated memory. But not all types have a \n", "destructor, and your Mojo code has no way to determine which is which.\n", "\n", @@ -589,7 +582,7 @@ "to be able to identify the types at compile time. For example, if the container\n", "needs to copy a value, the compiler needs to verify that the type can be copied.\n", "\n", - "The [`List`](/mojo/stdlib/collections/list) type is an example of a\n", + "The [`List`](/mojo/stdlib/collections/list.html) type is an example of a\n", "generic container. A single `List` can only hold a single type of data.\n", "For example, you can create a list of integer values like this:" ] @@ -623,8 +616,8 @@ "container. For example, `List` requires elements that can be moved and\n", "copied. To store a struct in a `List`, the struct needs to conform to\n", "the `CollectionElement` trait, which requires a \n", - "[copy constructor](/mojo/manual/lifecycle/life#copy-constructor) and a \n", - "[move constructor](/mojo/manual/lifecycle/life#move-constructor).\n", + "[copy constructor](/mojo/manual/lifecycle/life.html#copy-constructor) and a \n", + "[move constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", "\n", "Building generic containers is an advanced topic. For an introduction, see the\n", "section on \n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index fe9e1adfcc..1aac8f6ccb 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -131,7 +131,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -204,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -248,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -265,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -333,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -381,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -404,13 +404,16 @@ "source": [ "Most standard library types conform to the \n", "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", - "a type that can be converted to a string. Use `str(value)` to\n", - "explicitly convert a value to a string:" + "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", + "explicitly convert a value to a string.\n", + "\n", + "When concatenating values to a string using the `+` operator, `Stringable` types\n", + "implicitly convert to `String`:" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -422,7 +425,7 @@ } ], "source": [ - "var s = str(\"Items in list: \") + str(5)\n", + "var s = str(\"Items in list: \") + 5\n", "print(s)" ] }, @@ -470,7 +473,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -484,7 +487,7 @@ "source": [ "# print(\"Strings play nicely with others: \" + True)\n", "# Error: ... right hand side cannot be converted from Bool to StringLiteral\n", - "print(str(\"Strings play nicely with others: \") + str(True))" + "print(str(\"Strings play nicely with others: \") + True)" ] }, { @@ -502,7 +505,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -557,7 +560,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +579,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -620,7 +623,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -658,7 +661,6 @@ " ```mojo\n", " # Doesn't work!\n", " var list: List[Int] = [2, 3, 5]\n", - " ```\n", "\n", " But you can use variadic arguments to achieve the same thing:\n", "\n", @@ -683,7 +685,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -712,7 +714,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -758,7 +760,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -799,7 +801,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -838,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -864,7 +866,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -879,7 +881,7 @@ "var opt: Optional[String] = str(\"Testing\")\n", "if opt:\n", " var value_ref = opt.value()\n", - " print(value_ref)" + " print(value_ref[])" ] }, { @@ -893,7 +895,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [ { diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 7fbd408e8b..103a77717e 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -102,7 +102,7 @@ "\n", "But before we explain the rules and syntax for Mojo's value ownership model,\n", "you first need to understand [value\n", - "semantics](/mojo/manual/values/value-semantics)." + "semantics](/mojo/manual/values/value-semantics.html)." ] } ], diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index ecf19d7390..20674db040 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -29,7 +29,7 @@ "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", "each value at a time, while still allowing you to share references with other\n", "functions. When the lifetime of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death).\n", + "value](/mojo/manual/lifecycle/death.html).\n", "\n", "On this page, we'll explain the rules that govern this ownership model and how\n", "to specify different argument conventions that define how values are shared into\n", @@ -49,7 +49,7 @@ "performance, and safety of the language.\n", "\n", "In Mojo, we want to provide full [value\n", - "semantics](/mojo/manual/values/value-semantics) by default, which provides\n", + "semantics](/mojo/manual/values/value-semantics.html) by default, which provides\n", "consistent and predictable behavior. But as a systems programming language, we\n", "also need to offer full control over memory optimizations, which generally\n", "requires reference semantics. The trick is to introduce reference semantics in\n", @@ -64,7 +64,7 @@ "\n", "- `borrowed`: The function receives an **immutable reference**. This means the\n", " function can read the original value (it is *not* a copy), but it cannot\n", - " mutate (modify) it. `def` functions treat this differently, as described below.\n", + " mutate (modify) it.\n", " \n", "- `inout`: The function receives a **mutable reference**. This means the\n", " function can read and mutate the original value (it is *not* a copy).\n", @@ -100,38 +100,16 @@ "metadata": {}, "source": [ "You've probably already seen some function arguments that don't declare a\n", - "convention. by default, all arguments are `borrowed`. But `def` and `fn` \n", - "functions treat `borrowed` arguments somewhat differently:\n", + "convention. That's because every argument has a default convention, depending\n", + "on whether the function is declared with `fn` or `def`:\n", "\n", + "- All values passed into a Mojo [`def`\n", + " function](/mojo/manual/functions.html#def-functions) are `owned`,\n", + " by default. \n", "\n", - "- In an [`fn` function](/mojo/manual/functions#fn-functions), the function\n", - " always receives an immutable reference. If you want a mutable copy, you can\n", - " assign it to a local variable:\n", - "\n", - " ```mojo\n", - " var my_copy = borrowed_arg\n", - " ```\n", - "\n", - "- In a [`def` function](/mojo/manual/functions#def-functions), if the \n", - " function mutates the value, the function receives a mutable copy of the \n", - " argument. Otherwise, it receives an immutable reference. This allows you to\n", - " treat arguments as mutable, but avoid the overhead of making extra copies when\n", - " they're not needed.\n", - "\n", - "The difference between `borrowed` and `owned` in a `def` function may be a\n", - "little subtle: \n", - "\n", - "- In a `def` function, a `borrowed` argument is received as an immutable\n", - " reference, unless it's mutated in the body of the function. This eliminates\n", - " unneeded copies, but maintains the Python expectation that arguments are \n", - " mutable.\n", - "\n", - "- The `borrowed` argument always gets an immutable reference or a local copy.\n", - " You can't transfer a value into a `borrowed` argument.\n", - "\n", - "- The `owned` argument always gets a uniquely owned value, which may have been\n", - " copied or transferred from the callee. Using `owned` arguments without the \n", - " transfer operator (`^`) usually results in values being copied.\n", + "- All values passed into a Mojo [`fn`\n", + " function](/mojo/manual/functions.html#fn-functions) are `borrowed`,\n", + " by default.\n", "\n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." @@ -148,11 +126,9 @@ "- Every value has only one owner at a time.\n", "- When the lifetime of the owner ends, Mojo destroys the value.\n", "\n", - "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", - "that only one mutable reference to a value can exist at a time. **This is not\n", - "currently enforced.**\n", - "\n", - "" + "values](/mojo/manual/lifecycle/death.html) when their lifetime ends." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Borrowed arguments (`borrowed`)\n", - "\n", - "The `borrowed` convention is the default for all arguments.\n", + "## Immutable arguments (`borrowed`)\n", "\n", - "In `fn` functions, a `borrowed` argument is received as an immutable reference.\n", + "If you'd like your function to receive an **immutable reference**, add the\n", + "`borrowed` keyword in front of the argument name.\n", "\n", - "In `def` functions, you can treat a `borrowed` argument as mutable or immutable.\n", - "If you mutate the argument in the body of the function, you get a mutable copy\n", - "of the original value. If you don't mutate the argument, you get an immutable\n", - "reference, as in an `fn` function.\n", - "\n", - "For example:" + "The `borrowed` convention is the default for all arguments in an `fn` function,\n", + "but you can still specify it to be explicit. It also works on `def` functions,\n", + "which otherwise receive arguments by value, which might not be desirable, such\n", + "as when the type is expensive to copy (or not copyable at all) and you just\n", + "need to read it. For example:" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -205,7 +177,7 @@ "source": [ "from tensor import Tensor, TensorShape\n", "\n", - "def print_shape(tensor: Tensor[DType.float32]):\n", + "def print_shape(borrowed tensor: Tensor[DType.float32]):\n", " shape = tensor.shape()\n", " print(str(shape))\n", "\n", @@ -217,11 +189,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here the `tensor` argument is borrowed and not mutated, so the `print_shape()`\n", - "function gets an immutable reference to the original `Tensor`, and doesn't do \n", - "any copying. In general, passing an immutable reference is much more efficient\n", - "when handling large or expensive-to-copy values, because the copy constructor\n", - "and destructor are not invoked for a borrow.\n", + "In general, passing an immutable reference is much more efficient when handling\n", + "large or expensive-to-copy values, because the copy constructor and destructor\n", + "are not invoked for a borrow.\n", "\n", "### Compared to C++ and Rust\n", "\n", @@ -230,8 +200,10 @@ "mutability in the callee. However, the borrowed convention differs from\n", "`const&` in C++ in two important ways:\n", "\n", - "- The Mojo compiler implements a lifetime checker that ensures that values are\n", - "not destroyed when there are outstanding references to those values.\n", + "- The Mojo compiler implements a borrow checker (similar to Rust) that prevents\n", + "code from dynamically forming mutable references to a value when there are\n", + "immutable references outstanding, and it prevents multiple mutable references\n", + "to the same value.\n", "\n", "- Small values like `Int`, `Float`, and `SIMD` are passed directly in machine\n", "registers instead of through an extra indirection (this is because they are\n", @@ -241,8 +213,7 @@ "when compared to languages like C++ and Rust, and moves this optimization from\n", "every call site to a declaration on the type definition.\n", "\n", - "In the future, Mojo's lifetime checker will enforces the exclusivity of\n", - "mutable references, similar to Rust.\n", + "Similar to Rust, Mojo's borrow checker enforces the exclusivity of invariants.\n", "The major difference between Rust and Mojo is that Mojo does not require a\n", "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", "passing small values, and Rust defaults to moving values instead of passing\n", @@ -340,7 +311,7 @@ ":::note\n", "\n", "You cannot define [default\n", - "values](/mojo/manual/functions#optional-arguments) for `inout`\n", + "values](/mojo/manual/functions.html#optional-arguments) for `inout`\n", "arguments.\n", "\n", ":::" @@ -360,30 +331,19 @@ "lifetime of that variable.\n", "\n", "Technically, the `owned` keyword does not guarantee that the received value is\n", - "_the original value_—it guarantees only that the function\n", - "gets unique ownership of a value (enforcing [value\n", - "semantics](/mojo/manual/values/value-semantics)). This happens in one of\n", - "three ways:\n", + "a mutable reference to _the original value_—it guarantees only that the function\n", + "gets unique ownership of this particular value (enforcing [value\n", + "semantics](/mojo/manual/values/value-semantics.html)). This happens in one of\n", + "two ways:\n", "\n", "- The caller passes the argument with the `^` transfer operator, which ends the\n", - "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", + "lifetime of that variable (the variable becomes invalid) and ownership is\n", "transferred into the function without making a copy of any heap-allocated data.\n", "\n", "- The caller **does not** use the `^` transfer operator, in which case, the\n", "value is copied into the function argument and the original variable remains\n", - "valid. (If the original value is not used again, the compiler may optimize away\n", - "the copy and transfer the value).\n", - "\n", - "- The caller passes in a newly-created \"owned\" value, such as a value returned\n", - "from a function. In this case, no variable owns the value and it can be\n", - "transferred directly to the callee. For example:\n", - "\n", - " ```mojo\n", - " def take(owned s: String):\n", - " pass\n", - "\n", - " take(str(\"A brand-new String!\"))\n", - " ```\n", + "valid (unless it is not used again, in which case the compiler destroys the\n", + "variable anyway because its lifetime naturally ends there).\n", "\n", "Regardless, when the function declares an argument as `owned`, it can be certain\n", "that it has unique mutable access to that value. \n", @@ -435,7 +395,7 @@ " print(message) # ERROR: The `message` variable is uninitialized\n", "```\n", "\n", - "This is a critical feature of Mojo's lifetime checker, because it ensures that no\n", + "This is a critical feature of Mojo's borrow checker, because it ensures that no\n", "two variables can have ownership of the same value. To fix the error, you must\n", "not use the `message` variable after you end its lifetime with the `^` transfer\n", "operator. So here is the corrected code:" @@ -489,7 +449,7 @@ "making a copy:\n", "\n", "- If a type implements the [move\n", - " constructor](/mojo/manual/lifecycle/life#consuming-move-constructor),\n", + " constructor](/mojo/manual/lifecycle/life.html#consuming-move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", " transferred into a function as an `owned` argument, _and_ the original value's\n", " lifetime ends at the same point (with or without use of the `^` transfer\n", @@ -509,7 +469,7 @@ "## Comparing `def` and `fn` argument conventions\n", "\n", "As mentioned in the section about\n", - "[functions](/mojo/manual/functions), `def` and `fn` functions\n", + "[functions](/mojo/manual/functions.html), `def` and `fn` functions\n", "are interchangeable, as far as a caller is concerned, and they can both\n", "accomplish the same things. It's only the inside that differs, and Mojo's `def`\n", "function is essentially just sugaring for the `fn` function:\n", @@ -518,10 +478,10 @@ " [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn`\n", " requires all types be explicitly declared).\n", "\n", - "- A `def` function can treat a `borrowed` argument as mutable (in which case it\n", - " receives a mutable copy). An `fn` function must make this copy explicitly.\n", + "- A `def` argument without a convention keyword (`borrowed`, `inout`, or\n", + " `owned`) defaults to `owned` (it receives a copy with a mutable variable).\n", "\n", - "For example, these two functions have the exact same behavior." + "For example, these two functions have the exact same behavior:" ] }, { @@ -530,11 +490,29 @@ "metadata": {}, "outputs": [], "source": [ - "def def_example(a: Int, inout b: Int, owned c):\n", + "def example(borrowed a: Int, inout b: Int, c):\n", " pass\n", "\n", - "fn fn_example(a_in: Int, inout b: Int, owned c: object):\n", - " var a = a_in\n", + "fn example(a: Int, inout b: Int, owned c: object):\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or, instead of specifying `owned` for the `c` argument, you can get the same\n", + "effect by manually making a copy when you need it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn example(a: Int, inout b: Int, c_in: object):\n", + " var c = c_in\n", " pass" ] }, @@ -543,9 +521,8 @@ "metadata": {}, "source": [ "This shadow copy typically adds no overhead, because references for small types\n", - "like `object` are cheap to copy. However, copying large types that allocate heap\n", - "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", - "copying large numbers of strings.)" + "like `object` are cheap to copy. The expensive part is adjusting the reference\n", + "count, but that's also eliminated by a compiler optimization." ] } ], diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index 903cabd474..f405a34ab9 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -27,7 +27,7 @@ "provides tight controls for reference semantics that avoid memory errors.\n", "\n", "The controls over reference semantics are provided by the [value ownership\n", - "model](/mojo/manual/values/ownership), but before we get into the syntax\n", + "model](/mojo/manual/values/ownership.html), but before we get into the syntax\n", "and rules for that, it's important that you understand the principles of value\n", "semantics. Generally, it means that each variable has unique access to a value,\n", "and any code outside the scope of that variable cannot modify its value." @@ -130,40 +130,43 @@ "\n", "In Mojo, the default behavior for all function arguments is to use value\n", "semantics. If the function wants to modify the value of an incoming argument,\n", - "then it must explicitly declare so, which avoids accidental mutations of the\n", - "original value.\n", + "then it must explicitly declare so, which avoids accidental mutations.\n", "\n", - "All Mojo types passed to a `def` function can be treated as mutable,\n", - "which maintains the expected mutability behavior from Python. But by default, it\n", - "is mutating a uniquely-owned value, not the original value.\n", + "For starters, all Mojo types passed to a `def` function are passed by value,\n", + "which maintains the expected mutability behavior from Python. Except—contrary\n", + "to Python—the function has true ownership of the value, usually because it's a\n", + "copy.\n", "\n", - "For example, when you pass an instance of a `SIMD` vector to a `def`\n", - "function it creates a unique copy of all values. Thus, if we modify the\n", + "For example, even though the Mojo [`Tensor`](/mojo/stdlib/tensor/tensor.html)\n", + "type allocates values on the heap, when you pass an instance to a `def`\n", + "function, it creates a unique copy of all values. Thus, if we modify the\n", "argument in the function, the original value is unchanged:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[9, 2, 3, 4]\n", - "[1, 2, 3, 4]\n" + "Tensor([[1, 3]], dtype=uint8, shape=2)\n", + "Tensor([[1, 2]], dtype=uint8, shape=2)\n" ] } ], "source": [ - "def update_simd(t: SIMD[DType.int32, 4]):\n", - " t[0] = 9\n", + "def update_tensor(t: Tensor[DType.uint8]):\n", + " t[1] = 3\n", " print(t)\n", "\n", - "v = SIMD[DType.int32, 4](1, 2, 3, 4)\n", - "update_simd(v)\n", - "print(v)" + "t = Tensor[DType.uint8](2)\n", + "t[0] = 1\n", + "t[1] = 2\n", + "update_tensor(t)\n", + "print(t)" ] }, { @@ -186,13 +189,10 @@ "metadata": {}, "source": [ "The arguments above are mutable because a [`def`\n", - "function](/mojo/manual/functions#def-functions) has special treatment for\n", - "the default\n", - "[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions).\n", - "\n", - "Whereas, `fn` functions always receive `borrowed` arguments as immutable\n", - "references. This is a memory optimization to avoid making\n", - "unnecessary copies.\n", + "function](/mojo/manual/functions.html#def-functions) gets ownership for\n", + "its arguments by default (usually as a copy). Whereas, `fn` functions instead\n", + "receive arguments as immutable references, by default. This is a memory\n", + "optimization to avoid making unnecessary copies.\n", "\n", "For example, let's create another function with the `fn` declaration. In this\n", "case, the `y` argument is immutable by default, so if the function wants to\n", @@ -301,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -337,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 16, "metadata": {}, "outputs": [ { diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 7cac974ebd..7115d3a5f5 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -23,12 +23,12 @@ "A variable is a name that holds a value or object. All variables in Mojo are \n", "mutable—their value can be changed. (If you want to define a constant value that\n", "can't change at runtime, see the \n", - "[`alias` keyword](/mojo/manual/parameters/#alias-named-parameter-expressions).)\n", + "[`alias` keyword](/mojo/manual/parameters/index.html#alias-named-parameter-expressions).)\n", "\n", "Mojo has two kinds of variables:\n", "\n", "- Declared variables are created with the `var` keyword, and may include\n", - " [type annotations](#type-annotations).\n", + " [type annotations](#type-annotation).\n", "\n", " ```mojo\n", " var a = 5\n", @@ -80,7 +80,16 @@ "source": [ "In this example, the `temperature` variable is explicitly typed as `Float64`,\n", "but assigned an integer value, so the value is implicitly converted to a \n", - "`Float64`. " + "`Float64`. \n", + "\n", + ":::note\n", + "\n", + "Mojo formerly supported the `let` keyword for declaring immutable variables.\n", + "This has been removed to simplify the language, and for other reasons\n", + "[discussed\n", + "elsewhere](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md).\n", + "\n", + ":::" ] }, { @@ -133,12 +142,24 @@ "source": [ "## Declared variables\n", "\n", - "You can declare a variable with the `var` keyword. For example:\n", - "\n", - "```mojo\n", + "You can declare a variable with the `var` keyword. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ "var name = str(\"Sam\")\n", - "var user_id: Int\n", - "```\n", + "var user_id: Int" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", "declared values are typed—either explicitly with a \n", @@ -352,7 +373,7 @@ "is lossless.\n", "\n", "Implicit conversion follows the logic of [overloaded\n", - "functions](/mojo/manual/functions#overloaded-functions). If the destination\n", + "functions](/mojo/manual/functions.html#overloaded-functions). If the destination\n", "type has a single-argument constructor that takes an argument of the source\n", "type, it can be invoked for implicit conversion. \n", "\n", diff --git a/docs/notebooks/index.mdx b/docs/notebooks/index.md similarity index 94% rename from docs/notebooks/index.mdx rename to docs/notebooks/index.md index 636b8e4a25..09e76b05fe 100644 --- a/docs/notebooks/index.mdx +++ b/docs/notebooks/index.md @@ -13,7 +13,7 @@ listing: - RayTracing.ipynb type: grid grid-columns: 2 - sort: 'false' + sort: "false" --- The following pages are rendered from the Jupyter notebooks that are [available @@ -21,5 +21,5 @@ on GitHub](https://github.com/modularml/mojo/tree/main/examples/notebooks).


-:::🔥#docs +:::{#docs} ::: diff --git a/docs/roadmap.md b/docs/roadmap.md index 31529aae5e..f19c0d9abf 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -18,7 +18,7 @@ the long-term), so we want to fully build-out the core Mojo language features before we work on other dependent features and enhancements. Currently, that means we are focused on the core system programming features -that are essential to [Mojo's mission](/mojo/why-mojo), and as outlined in the +that are essential to [Mojo's mission](why-mojo.html), and as outlined in the following sections of this roadmap. In the near-term, we will **not** prioritize "general goodness" work such as: @@ -94,6 +94,27 @@ systems. Here are some of the notable issues that we plan to fix: processors and Apple Silicon macOS. Support for more Linux distributions (including Debian and RHEL) and Windows is in progress. +- Python interoperability might fail when running a compiled Mojo program, with + the message + `Unable to locate a suitable libpython, please set MOJO_PYTHON_LIBRARY`. This + is because we currently do not embed the Python version into the Mojo binary. + For details and the workaround, see [issue + #551](https://github.com/modularml/mojo/issues/551). + +- Mojo programs that import NumPy might fail with the following error: + + ```plaintext + Importing the numpy C-extensions failed. This error can happen for + many reasons, often due to issues with your setup or how NumPy was + installed. + ``` + + This may occur because the version of NumPy doesn't match the Python + interpreter Mojo is using. As a workaround, follow the instructions in + [issue #1085](https://github.com/modularml/mojo/issues/1085#issuecomment-1771403719) + to install a Python virtual environment using Conda. This can solve many + issues with Python interoperability. + - Modular CLI install might fail and require `modular clean` before you re-install. @@ -189,7 +210,7 @@ to use right now. ## Traits support As of v0.6.0 Mojo has basic support for -[traits](/mojo/manual/traits). Traits allow you +[traits](/mojo/manual/traits.html#built-in-traits). Traits allow you to specify a set of requirements for types to implement. Types can implement those requirements to *conform to* the trait. Traits allow you to write generic functions and generic containers, which can work with any type that @@ -200,7 +221,7 @@ Currently, the only kind of requirements supported by traits are required method signatures. The trait can't provide a default implementation for its required methods, so each conforming type must implement all of the required methods. -A number of [built-in traits](/mojo/manual/traits#built-in-traits) are +A number of [built-in traits](/mojo/manual/traits.html#built-in-traits) are already implemented in the standard library. We plan to expand traits support in future releases. Planned features include: diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index acfca3ea23..216f5cb11e 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -31,9 +31,8 @@ "\n", "The Mojo SDK includes the [LLDB debugger](https://lldb.llvm.org/) and a Mojo\n", "LLDB plugin. Together these provide the low-level debugging interface for the\n", - "Mojo extension. You can also use the `mojo debug` command to start a \n", - "command-line debugging session using LLDB or to launch a Mojo debugging session\n", - "in VS Code.\n", + "Mojo extension. You can also use `mojo debug` to start a command-line debugging\n", + "session using LLDB.\n", "\n", "## Start debugging\n", "\n", @@ -104,13 +103,8 @@ "menu to select debug configurations. It also has areas to display current\n", "variables, watch expressions, the current call stack, and breakpoints.\n", "\n", - "
\n", - "\n", "![](images/run-and-debug-view.png)\n", "\n", - "
Figure 1. Run and Debug view
\n", - "
\n", - "\n", "To open **Run and Debug** view, click the **Run and Debug** icon in the\n", "**Activity Bar** (on the left side of the VS Code window) or press\n", "Control+Shift+D \n", @@ -121,24 +115,14 @@ "If you haven't created any launch configurations in the current project,\n", "VS Code shows the **Run start view**.\n", "\n", - "
\n", - "\n", "![](images/run-start-view.png)\n", "\n", - "
Figure 2. Run start view
\n", - "
\n", - "\n", "If you've already launched a debug session or created a `launch.json` file to\n", "define launch configurations, you'll see the **Launch configurations** menu,\n", "which lets you choose configurations and start debug sessions:\n", "\n", - "
\n", - "\n", "![](images/launch-configuration-menu.png)\n", "\n", - "
Figure 3. Launch configurations menu
\n", - "
\n", - "\n", "### Other ways to start a debug session\n", "\n", "There are a number of other ways to start a debug session.\n", @@ -155,7 +139,7 @@ "same debug configurations described in [Quick run or\n", "debug](#quick-run-or-debug).\n", "\n", - "#### Launch from the File Explorer\n", + "#### Launching from the File Explorer\n", "\n", "To launch a debug session from the the **File Explorer** view:\n", "\n", @@ -179,79 +163,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Starting the debugger from the command line\n", - "\n", - "Use the `mojo debug` command to start a debug session from the command line. You\n", - "can choose from two debugging interfaces:\n", - "\n", - "- With the `--rpc` flag, `mojo debug` starts an RPC debug session in an external\n", - " editor with an active RPC debug server. If you have VS Code running, this\n", - " starts a debug session in VS Code. \n", - "\n", - "- Without the `--rpc` flag, `mojo debug` starts a command-line [LLDB \n", - " debugger](https://lldb.llvm.org/) session.\n", - "\n", - "You can choose to build and debug a Mojo file, run and debug a compiled binary,\n", - "or to attach the debugger to a running process.\n", - "\n", - ":::note Environment variables\n", - "\n", - "When you debug a program from the command line using `--rpc`, the program runs\n", - "with the environment variables set in the terminal. When launching from inside\n", - "VS Code, the environment is defined by the VS Code [launch \n", - "configuration](#launch-configurations).\n", - "\n", - ":::\n", - "\n", - "For a full list of command-line options, see the [`mojo debug` reference\n", - "page](/mojo/cli/debug).\n", + "## Edit launch configurations\n", "\n", - "### Start a debug session from the command line\n", - "\n", - "With VS Code open, run the following command (either from VS Code's integrated\n", - "terminal or an external shell):\n", - "\n", - "```bash\n", - "mojo debug --rpc myproject.mojo\n", - "```\n", - "\n", - "Or to debug a compiled binary:\n", - "\n", - "```bash\n", - "mojo debug --rpc myproject\n", - "```\n", - "\n", - "\n", - "For best results, build with the `-O0 -g` command-line options when you build a\n", - "binary that you intend to debug—this produces a binary with full debug info.\n", - "(When you call `mojo debug` on a Mojo source file, it includes debug\n", - "information by default.) See the [`mojo build` reference page](/mojo/cli/build)\n", - "for details on compilation options.\n", - "\n", - "### Attach the debugger to a running process from the command line\n", - "\n", - "You can also attach the debugger to a running process by specifying either the\n", - "process ID or process name on the command line:\n", + "To edit launch configurations:\n", "\n", - "```bash\n", - "mojo debug --rpc --pid \n", - "```\n", + "1. If the **Run and Debug** view isn't already open, click the **Run and\n", + "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", + "or press Control+Shift+D (Command+Shift+D on macOS).\n", "\n", - "Or:\n", + " ![](images/run-and-debug-icon.png)\n", + " \n", + "1. Create or open the `launch.json` file:\n", + " 1. If you see the **Run start view**, click **create a launch.json file**.\n", + " 1. If you already have launch configurations set up, click the gear icon\n", + " next to the **Launch configurations** menu.\n", + " ![](images/launch-configuration-menu.png)\n", + "1. Select **Mojo** from the list of debuggers.\n", "\n", - "```bash\n", - "mojo debug --rpc --process-name \n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Launch configurations\n", + "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", + "some common debug actions. Click **Add configuration** to add a new\n", + "configuration template. \n", "\n", - "VS Code _launch configurations_ let you define setup information for debugging\n", - "your applications.\n", + "### Mojo launch configurations\n", "\n", "The Mojo debugger provides the following launch configuration templates:\n", "\n", @@ -287,59 +220,22 @@ "- `description`. A longer description of the configuration, not shown in the UI.\n", "- `env`. Environment variables to be set before running the program.\n", "- `mojoFile`. Path to a Mojo file to launch and debug.\n", - "- `pid`. Process ID of the running process to attach to.\n", - "- `program`. Path to a compiled binary to launch and debug, or the \n", - " program to attach to.\n", + "- `program`. Path to a compiled binary to launch and debug.\n", "- `runInTerminal`. True to run the program with a dedicated terminal, which\n", " allows the program to receive standard input from the terminal. False to run\n", " the program with its output directed to the **Debug Console**.\n", "\n", - "If configuration is a `launch` request, the configuration must include either\n", - "the `mojoFile` or `program` attribute.\n", - "\n", - "For `attach` requests, the configuration must include either the `pid` or \n", - "`program` attribute.\n", + "Mojo `launch` configurations must include either the `mojoFile` or `program`\n", + "attribute.\n", "\n", "VS Code performs variable substitution on the launch configurations. You can\n", "use `${workspaceFolder}` to substitute the path to the current workspace, and\n", "`${file}` to represent the file in the active editor tab. For a complete list\n", - "of variables, see the VS Code [Variables\n", + "of variables, see the [Variables\n", "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", "\n", - "For more information, see the VS Code documentation for [Launch \n", - "configurations]](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", - "\n", - ":::note Compilation options\n", - "\n", - "Mojo launch configurations don't allow you to specify compilation options. If\n", - "you need to specify compilation options, you can build the binary using [`mojo\n", - "build`](/mojo/cli/build), then use a launch configuration with the `program`\n", - "option to launch the compiled binary. Or if you [start the debugger from the\n", - "command line](#starting-the-debugger-from-the-command-line), you can pass\n", - "compilation options to the `mojo debug` command.\n", - "\n", - ":::\n", - "\n", - "### Edit launch configurations\n", - "\n", - "To edit launch configurations:\n", - "\n", - "1. If the **Run and Debug** view isn't already open, click the **Run and\n", - "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", - "or press Control+Shift+D (Command+Shift+D on macOS).\n", - "\n", - " ![](images/run-and-debug-icon.png)\n", - " \n", - "1. Create or open the `launch.json` file:\n", - " 1. If you see the **Run start view**, click **create a launch.json file**.\n", - " 1. If you already have launch configurations set up, click the gear icon\n", - " next to the **Launch configurations** menu.\n", - " ![](images/launch-configuration-menu.png)\n", - "1. Select **Mojo** from the list of debuggers.\n", - "\n", - "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", - "some common debug actions. Click **Add configuration** to add a new\n", - "configuration template. " + "Note that for launch configurations that launch a Mojo file, there is currently\n", + "no way to specify compilation options.\n" ] }, { @@ -406,22 +302,16 @@ "- **Log Message**. Add a logpoint (supported)\n", "- **Wait for Breakpoint**. Add a triggered breakpoint (supported).\n", "\n", - "#### Set a hit count breakpoint\n", + "#### Hit counts\n", "\n", "A hit count breakpoint is a breakpoint that only breaks execution after the\n", "debugger hits it a specified number of times.\n", "\n", - "To add a hit count breakpoint:\n", - "\n", - "1. Right click in the left gutter of the editor where you want to place the \n", - " breakpoint, and select **Add Conditional Breakpoint.**\n", - "2. Select **Hit Count** from the menu and enter the desired hit count.\n", + "To set a hit count for a breakpoint:\n", "\n", - "To change an existing breakpoint to a hit count breakpoint:\n", - "\n", - "1. Right click on the breakpoint in the left gutter of the editor and select\n", + "- Right click on the breakpoint in the left gutter of the editor and select\n", " **Edit breakpoint**.\n", - "2. Select **Hit Count** from the menu and enter the desired hit count.\n", + "- Select **Hit Count** from the menu and enter the desired hit count.\n", "\n", "You can also edit a breakpoint from the **Breakpoints** section of the **Run and\n", "Debug** view:\n", @@ -436,51 +326,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### View local variables\n", - "\n", - "When a program is paused in the debugger, the editor shows local variable values\n", - "inline. You can also find them in the **Variables** section of the **Run and\n", - "Debug** view.\n", - "\n", - "
\n", - "\n", - "![VS Code window showing a program paused in the debugger, with the variables sections of the Run and Debug view visible. The edit shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2.](images/debugger-variables.png)\n", - "\n", - "
Figure 4. Local variable values displayed in the debugger
\n", - "
\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View the call stack\n", - "\n", - "When a program is paused in the debugger, the **Run and Debug** view shows the\n", - "current call stack. (You may see multiple call stacks, one for each active\n", - "thread in the program.)\n", - "\n", - "
\n", - "\n", - "![VS Code window showing a program paused in the debugger, with the call stack and variables sections of the Run and Debug view visible. The call stack shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2; the parent function nested1 is selected in the call stack, and editor highlights the current line in nested1 (the call to nested2()).](images/debugger-call-stack-nested1.png)\n", - "\n", - "
Figure 5. Call stack in Run and Debug view
\n", - "
\n", - "\n", - "The **Call Stack** section of the Run and Debug view shows a stack frame for\n", - "each function call in the current call stack. Clicking on the name of the\n", - "function highlights the current line in that function. For example, in Figure\n", - "5, the program is paused at a breakpoint in `nested2()`, but the parent\n", - "function, `nested1()` is selected in the call stack. The editor highlights the\n", - "current line in `nested1()` (that is, the call to `nested2()`) and shows the\n", - "current local variable values for `nested1()`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Use the Debug Console\n", + "### Using the Debug Console\n", "\n", "The **Debug Console** gives you a command-line interface to the debugger. The \n", "**Debug Console** processes LLDB commands and Mojo expressions.\n", @@ -502,73 +348,6 @@ "\n", ":::" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Tips and tricks\n", - "\n", - " There are several features in the standard library that aren't directly related\n", - " to the debugger, but which can help you debug your programs. These include:\n", - "\n", - " * Programmatic breakpoints.\n", - " * Setting parameters from the Mojo command line.\n", - "\n", - " ### Set a programmatic breakpoint\n", - "\n", - " To break at a specific point in your code, you can use the built-in\n", - " [`breakpoint()`](/mojo/stdlib/builtin/breakpoint/breakpoint) function:\n", - "\n", - " ```mojo\n", - "if some_value.is_valid():\n", - " do_the_right_thing()\n", - "else:\n", - " # We should never get here!\n", - " breakpoint()\n", - "```\n", - "\n", - "If you have VS Code open and run this code in debug mode (either using VS Code\n", - "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", - "triggers the debugger.\n", - "\n", - ":::note Assertions\n", - "\n", - "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", - "ways to specify assertions. Assertions also trigger an error, so can open the \n", - "debugger in the same way that a `breakpoint()` call will.\n", - "\n", - ":::\n", - "\n", - "### Set parameters from the Mojo command line\n", - "\n", - "You can use the [`param_env`](/mojo/stdlib/sys/param_env/) module to retrieve\n", - "parameter values specified on the Mojo command line. Among other things, this\n", - "is an easy way to switch debugging logic on and off. For example:\n", - "\n", - "```mojo\n", - "from param_env import is_defined\n", - "\n", - "def some_function_with_issues():\n", - " # ...\n", - " @parameter\n", - " if is_defined[\"DEBUG_ME\"]():\n", - " breakpoint()\n", - "```\n", - "\n", - "To activate this code, use the [`-D` command-line\n", - "option](/mojo/cli/debug#compilation-options) to define `DEBUG_ME`:\n", - "\n", - "```bash\n", - "mojo debug -D DEBUG_ME main.mojo\n", - "```\n", - "\n", - "The `is_defined()` function returns a compile-time true or false value based on\n", - "whether the specified name is defined. Since the `breakpoint()` call is inside a\n", - "[parametric `if` statement](/mojo/manual/decorators/parameter#parametric-if-statement),\n", - "it is only included in the compiled code when the `DEBUG_ME` name is defined on\n", - "the command line." - ] } ], "metadata": { diff --git a/docs/tools/images/debugger-call-stack-nested1.png b/docs/tools/images/debugger-call-stack-nested1.png deleted file mode 100644 index aaf33e7dfdeacf7e791c8a2d76fe2e00287b387c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 105875 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
e37 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 Int:\n", - " return n + 1\n", - "\n", - "def test_inc_zero():\n", - " # This test contains an intentional logical error to show an example of\n", - " # what a test failure looks like at runtime.\n", - " assert_equal(inc(0), 0)\n", - "\n", - "def test_inc_one():\n", - " assert_equal(inc(1), 2)\n", - "```\n", - "\n", - "In this file, the `inc()` function is the test *target*. The functions whose\n", - "names begin with `test_` are the tests. Usually the target should be in a\n", - "separate source file from its tests, but you can define them in the same file\n", - "for this simple example.\n", - "\n", - "A test function *fails* if it raises an error when executed, otherwise it\n", - "*passes*. The two tests in this example use the `assert_equal()` function,\n", - "which raises an error if the two values provided are not equal.\n", - "\n", - ":::note\n", - "\n", - "The implementation of `test_inc_zero()` contains an intentional logical error\n", - "so that you can see an example of a failed test when you execute it in the\n", - "next step of this tutorial. \n", - "\n", - ":::\n", - "\n", - "### 2. Execute tests\n", - "\n", - "Then in the directory containing the file, execute the following command in your\n", - "shell:\n", + "The Mojo CLI has a built-in test runner that can be used to test Mojo programs. \n", + "The `mojo test` command recursively searches the `test` directory for any files\n", + "with `test` in the filename. For example, given the following directory\n", + "structure:\n", "\n", "```bash\n", - "mojo test test_quickstart.mojo\n", - "```\n", - "\n", - "You should see output similar to this (note that this example elides the full\n", - "filesystem paths from the output shown):\n", - "\n", + "test/\n", + " test_users_controller.mojo\n", + " factories.mojo\n", + " models/\n", + " user_model_tests.mojo\n", "```\n", - "Testing Time: 1.193s\n", - "\n", - "Total Discovered Tests: 2\n", - "\n", - "Passed : 1 (50.00%)\n", - "Failed : 1 (50.00%)\n", - "Skipped: 0 (0.00%)\n", - "\n", - "******************** Failure: 'ROOT_DIR/test_quickstart.mojo::test_inc_zero()' ********************\n", "\n", - "Unhandled exception caught during execution\n", - "\n", - "Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\n", - " left: 1\n", - " right: 0\n", - "\n", - "********************\n", - "```\n", + "The `mojo` CLI will search `test_users_controller.mojo` and \n", + "`user_model_tests.mojo` for tests, but not `factories.mojo`. \n", "\n", - "The output starts with a summary of the number of tests discovered, passed,\n", - "failed, and skipped. Following that, each failed test is reported along with\n", - "its error message.\n", + "## Test functions\n", "\n", - "### Next steps\n", - "\n", - "- [The `testing` module](#the-testing-module) describes the assertion\n", - "functions available to help implement tests.\n", - "- [Writing unit tests](#writing-unit-tests) shows how to write unit tests and\n", - "organize them into test files.\n", - "- [The `mojo test` command](#the-mojo-test-command) describes how to execute\n", - "and collect lists of tests.\n", - "- [Writing API documentation tests](#writing-api-documentation-tests)\n", - "discusses how to use the Mojo testing framework to test code examples in your\n", - "API documentation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## The `testing` module\n", - "\n", - "The Mojo standard library includes a [`testing`](/mojo/stdlib/testing/testing/)\n", - "module that defines several assertion functions for implementing tests. Each\n", - "assertion returns `None` if its condition is met or raises an error if it isn’t.\n", - "\n", - "- [`assert_true()`](/mojo/stdlib/testing/testing/assert_true):\n", - "Asserts that the input value is `True`.\n", - "- [`assert_false()`](/mojo/stdlib/testing/testing/assert_false):\n", - "Asserts that the input value is `False`.\n", - "- [`assert_equal()`](/mojo/stdlib/testing/testing/assert_equal):\n", - "Asserts that the input values are equal.\n", - "- [`assert_not_equal()`](/mojo/stdlib/testing/testing/assert_not_equal):\n", - "Asserts that the input values are not equal.\n", - "- [`assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal):\n", - "Asserts that the input values are equal up to a tolerance.\n", - "\n", - "The boolean assertions report a basic error message when they fail." + "Test functions must be named `test_*` and take no arguments. Use `def` notation\n", + "rather than `fn` notation. By definition, every test function should be capable\n", + "of raising an error. So:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: At Expression [1] wrapper:14:16: AssertionError: condition was unexpectedly False\n" - ] - } - ], - "source": [ - "from testing import *\n", - "assert_true(False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each function also accepts an optional `msg` keyword argument for providing a\n", - "custom message to include if the assertion fails." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: At Expression [2] wrapper:14:16: AssertionError: paradoxes are not allowed\n" - ] - } - ], - "source": [ - "assert_true(False, msg=\"paradoxes are not allowed\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For comparing floating point values you should use `assert_almost_equal()`,\n", - "which allows you to specify either an absolute or relative tolerance." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: At Expression [3] wrapper:15:24: AssertionError: 3.3333333333333335 is not close to 3.3300000000000001 with a diff of 0.0033333333333334103 (close but no cigar)\n" - ] - } - ], - "source": [ - "result = 10 / 3\n", - "assert_almost_equal(result, 3.33, atol=0.001, msg=\"close but no cigar\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The testing module also defines a context manager,\n", - "[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises),\n", - "to assert that a given code block correctly raises an expected error." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test passes because the error is raised\n", - "Test fails because the error isn't raised\n", - "Error: AssertionError: Didn't raise at Expression [4] wrapper:18:23\n" - ] - } - ], - "source": [ - "def inc(n: Int) -> Int:\n", - " if n == Int.MAX:\n", - " raise Error(\"inc overflow\")\n", - " return n + 1\n", - "\n", - "print(\"Test passes because the error is raised\")\n", - "with assert_raises():\n", - " _ = inc(Int.MAX)\n", - "\n", - "print(\"Test fails because the error isn't raised\")\n", - "with assert_raises():\n", - " _ = inc(Int.MIN)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "The example above assigns the return value from `inc()` to a\n", - "[*discard pattern*](/mojo/manual/lifecycle/death#explicit-lifetimes).\n", - "Without it, the Mojo compiler detects that the return value is unused and\n", - "optimizes the code to eliminate the function call.\n", - "\n", - ":::\n", - "\n", - "You can also provide an optional `contains` argument to `assert_raises()` to\n", - "indicate that the test passes only if the error message contains the substring\n", - "specified. Other errors are propagated, failing the test." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test passes because the error contains the substring\n", - "Test fails because the error doesnt contain the substring\n", - "Error: invalid value\n" - ] - } - ], - "source": [ - "print(\"Test passes because the error contains the substring\")\n", - "with assert_raises(contains=\"required\"):\n", - " raise Error(\"missing required argument\")\n", - "\n", - "print(\"Test fails because the error doesnt contain the substring\")\n", - "with assert_raises(contains=\"required\"):\n", - " raise Error(\"invalid value\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Writing unit tests\n", - "\n", - "A Mojo unit test is simply a function that fulfills all of these requirements:\n", - "\n", - "- Has a name that starts with `test_`.\n", - "- Accepts no arguments.\n", - "- Returns either `None` or a value of type `object`.\n", - "- Raises an error to indicate test failure.\n", - "- Is defined at the module scope, not as a Mojo struct method.\n", - "\n", - "You can use either `def` or `fn` to define a test function. Because a test\n", - "function always raises an error to indicate failure, any test function defined\n", - "using `fn` must include the `raises` declaration.\n", - "\n", - "Generally, you should use the assertion utilities from the Mojo standard library\n", - "[`testing`](/mojo/stdlib/testing/testing/) module to implement your tests.\n", - "You can include multiple related assertions in the same test function. However,\n", - "if an assertion raises an error during execution then the test function returns\n", - "immediately, skipping any subsequent assertions.\n", - "\n", - "You must define your Mojo unit tests in Mojo source files named with a `test`\n", - "prefix or suffix. You can organize your test files within a directory hierarchy,\n", - "but the test files must not be part of a Mojo package (that is, the test\n", - "directories should not contain `__init__.mojo` files).\n", - "\n", - "Here is an example of a test file containing three tests for functions defined\n", - "in a source module named `my_target_module` (which is not shown here).\n", - "\n", - "```mojo\n", - "# File: test_my_target_module.mojo\n", - "\n", - "from my_target_module import convert_input, validate_input\n", - "from testing import assert_equal, assert_false, assert_raises, assert_true\n", - "\n", - "def test_validate_input():\n", - "\tassert_true(validate_input(\"good\"), msg=\"'good' should be valid input\")\n", - "\tassert_false(validate_input(\"bad\"), msg=\"'bad' should be invalid input\")\n", - "\n", - "def test_convert_input():\n", - "\tassert_equal(convert_input(\"input1\"), \"output1\")\n", - "\tassert_equal(convert_input(\"input2\"), \"output2\")\n", - "\n", - "def test_convert_input_error():\n", - "\twith assert_raises():\n", - "\t\t_ = convert_input(\"garbage\")\n", - "```\n", - "\n", - "The unique identity of a unit test consists of the path of the test file and the\n", - "name of the test function, separated by `::`. So the test IDs from the example\n", - "above are:\n", - "\n", - "- `test_my_target_module.mojo::test_validate_input()`\n", - "- `test_my_target_module.mojo::test_convert_input()`\n", - "- `test_my_target_module.mojo::test_convert_error()`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, + "outputs": [], "source": [ - "## The `mojo test` command\n", - "\n", - "The `mojo` command line interface includes the [`mojo test`](/mojo/cli/test)\n", - "command for running tests or collecting a list of tests.\n", - "\n", - "### Running tests\n", - "\n", - "By default, the `mojo test` command runs the tests that you specify using one of\n", - "the following:\n", - "\n", - "- A single test ID with either an absolute or relative file path, to run only\n", - "that test.\n", - "- A single absolute or relative file path, to run all tests in that file.\n", - "- A single absolute or relative directory path, to recurse through that\n", - "directory hierarchy and run all tests found.\n", - "\n", - "If needed, you can optionally use the `-I` option one or more times to append\n", - "additional paths to the list of directories searched to import Mojo modules and\n", - "packages. For example, consider a project with the following directory\n", - "structure:\n", - "\n", - "```\n", - ".\n", - "├── src\n", - "│   ├── example.mojo\n", - "│   └── my_math\n", - "│   ├── __init__.mojo\n", - "│   └── utils.mojo\n", - "└── test\n", - " └── my_math\n", - " ├── test_dec.mojo\n", - " └── test_inc.mojo\n", - "```\n", - "\n", - "From the project root directory, you could execute all of the tests in the\n", - "`test` directory like this:\n", - "\n", - "```\n", - "$ mojo test -I src test\n", - "Testing Time: 3.433s\n", - "\n", - "Total Discovered Tests: 4\n", - "\n", - "Passed : 4 (100.00%)\n", - "Failed : 0 (0.00%)\n", - "Skipped: 0 (0.00%)\n", - "```\n", - "\n", - "You could run the tests contained in only the `test_dec.mojo` file like this:\n", - "\n", - "```\n", - "$ mojo test -I src test/my_math/test_dec.mojo\n", - "Testing Time: 1.175s\n", - "\n", - "Total Discovered Tests: 2\n", - "\n", - "Passed : 2 (100.00%)\n", - "Failed : 0 (0.00%)\n", - "Skipped: 0 (0.00%)\n", - "```\n", - "\n", - "And you could run a single test from a file by providing its fully qualified\n", - "ID like this:\n", - "\n", - "```\n", - "$ mojo test -I src 'test/my_math/test_dec.mojo::test_dec_valid()'\n", - "Testing Time: 0.66s\n", - "\n", - "Total Discovered Tests: 1\n", - "\n", - "Passed : 1 (100.00%)\n", - "Failed : 0 (0.00%)\n", - "Skipped: 0 (0.00%)\n", - "```\n", - "\n", - "### Collecting a list of tests\n", - "\n", - "By including the `--collect-only` or `--co` option, you can use `mojo test` to\n", - "discover and print a list of tests. \n", - "\n", - "As an example, consider the project structure shown in the\n", - "[Running tests](#running-tests) section. The following command produces a list\n", - "of all of the tests defined in the `test` directory hierarchy.\n", - "\n", - "```bash\n", - "mojo test --co test\n", - "```\n", - "\n", - "The output shows the hierarchy of directories, test files, and individual tests\n", - "(note that this example elides the full filesystem paths from the output shown):\n", - "\n", - "```\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "```\n", - "\n", - "### Producing JSON formatted output\n", - "\n", - "By default `mojo test` produces concise, human-readable output. Alternatively\n", - "you can produce JSON formatted output more suitable for input to other tools by\n", - "including the `--diagnostic-format json` option.\n", - "\n", - "For example, you could run the tests in the `test_quickstart.mojo` file shown\n", - "in the [Get started](#get-started) section with JSON formatted output using this\n", - "command:\n", - "\n", - "```bash\n", - "mojo test --diagnostic-format json test_quickstart.mojo\n", - "```\n", - "\n", - "The output shows the detailed results for each individual test and summary\n", - "results (note that this example elides the full filesystem paths from the\n", - "output shown):\n", - "\n", - "```\n", - "{\n", - " \"children\": [\n", - " {\n", - " \"duration_ms\": 60,\n", - " \"error\": \"Unhandled exception caught during execution\",\n", - " \"kind\": \"executionError\",\n", - " \"stdErr\": \"\",\n", - " \"stdOut\": \"Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\\r\\n left: 1\\r\\n right: 0\\r\\n\",\n", - " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_zero()\"\n", - " },\n", - " {\n", - " \"duration_ms\": 51,\n", - " \"error\": \"\",\n", - " \"kind\": \"success\",\n", - " \"stdErr\": \"\",\n", - " \"stdOut\": \"\",\n", - " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_one()\"\n", - " }\n", - " ],\n", - " \"duration_ms\": 1171,\n", - " \"error\": \"\",\n", - " \"kind\": \"executionError\",\n", - " \"stdErr\": \"\",\n", - " \"stdOut\": \"\",\n", - " \"testID\": \"ROOT_DIR/test_quickstart.mojo\"\n", - "}\n", - "```\n", - "\n", - "You can also produce JSON output for test collection as well. As an example,\n", - "consider the project structure shown in the [Running tests](#running-tests)\n", - "section. The following command collects a list in JSON format of all of the\n", - "tests defined in the `test` directory hierarchy:\n", - "\n", - "```bash\n", - "mojo test --diagnostic-format json --co test\n", - "```\n", + "from testing import assert_equal\n", "\n", - "The output would appear as follows (note that this example elides the full\n", - "filesystem paths from the output shown):\n", "\n", - "```\n", - "{\n", - " \"children\": [\n", - " {\n", - " \"children\": [\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_valid()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 5,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 5\n", - " }\n", - " },\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_min()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 9,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 9\n", - " }\n", - " }\n", - " ],\n", - " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo\"\n", - " },\n", - " {\n", - " \"children\": [\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_valid()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 5,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 5\n", - " }\n", - " },\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_max()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 9,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 9\n", - " }\n", - " }\n", - " ],\n", - " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo\"\n", - " }\n", - " ],\n", - " \"id\": \"ROOT_DIR/test/my_math\"\n", - "}\n", - "```" + "def test_foo():\n", + " assert_equal(1, 1)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Writing API documentation tests\n", - "\n", - "The Mojo testing framework also supports testing code examples that you include\n", - "in [docstrings](/mojo/manual/basics#code-comments). This helps to ensure that\n", - "the code examples in your API documentation are correct and up to date.\n", - "\n", - "### Identifying executable code\n", - "\n", - "The Mojo testing framework requires you to explicitly identify the code blocks\n", - "that you want it to execute.\n", - "\n", - "In a Mojo docstring, a fenced code block delimited by standard triple-backquotes\n", - "is a *display-only* code block. It appears in the API documentation, but\n", - "`mojo test` does not identify it as a test or attempt to execute any of the code\n", - "in the block.\n", - "\n", - "~~~\n", - "\"\"\" Non-executable code block example.\n", - "\n", - "The generated API documentation includes all lines of the following code block,\n", - "but `mojo test` does not execute any of the code in it.\n", - "\n", - "```\n", - "# mojo test does NOT execute any of this code block\n", - "a = 1\n", - "print(a)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - "In contrast, a fenced code block that starts with the line ```mojo\n", - "not only appears in the API documentation, but `mojo test` treats it as an\n", - "executable test. The test fails if the code raises any error, otherwise it\n", - "passes.\n", - "\n", - "~~~\n", - "\"\"\" Executable code block example.\n", - "\n", - "The generated API documentation includes all lines of the following code block\n", - "*and* `mojo test` executes it as a test.\n", - "\n", - "```mojo\n", - "from testing import assert_equals\n", - "\n", - "b = 2\n", - "assert_equals(b, 2)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - "Sometimes you might want to execute a line of code as part of the test but *not*\n", - "display that line in the API documentation. To achieve this, prefix the line of\n", - "code with `%#`. For example, you could use this technique to omit `import`\n", - "statements and assertion functions from the documentation.\n", - "\n", - "~~~\n", - "\"\"\" Executable code block example with some code lines omitted from output.\n", "\n", - "The generated API documentation includes only the lines of code that do *not*\n", - "start with `%#`. However, `mojo test` executes *all* lines of code.\n", + "Not:\n", "\n", "```mojo\n", - "%# from testing import assert_equal\n", - "c = 3\n", - "print(c)\n", - "%# assert_equal(c, 3)\n", + "fn test_foo() raises:\n", + " assert_equal(1, 1)\n", "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - "### Documentation test suites and scoping\n", - "\n", - "The Mojo testing framework treats each docstring as a separate *test suite*.\n", - "In other words, a single test suite could correspond to the docstring for an\n", - "individual package, module, function, struct, struct method, etc.\n", - "\n", - "Each executable code block within a given docstring is a single test of the same\n", - "test suite. The `mojo test` command executes the tests of a test suite\n", - "sequentially in the order that they appear within the docstring. If a test\n", - "within a particular test suite fails, then all subsequent tests within the same\n", - "test suite are skipped.\n", - "\n", - "All tests within the test suite execute in the same scope, and test execution\n", - "within that scope is stateful. This means, for example, that a variable created\n", - "within one test is then accessible to subsequent tests in the same test suite.\n", - "\n", - "~~~\n", - "\"\"\" Stateful example.\n", - "\n", - "Assign 1 to the variable `a`:\n", - "\n", - "```mojo\n", - "%# from testing import assert_equal\n", - "a = 1\n", - "%# assert_equal(a, 1)\n", - "```\n", - "\n", - "Then increment the value of `a` by 1:\n", - "\n", - "```mojo\n", - "a += 1\n", - "%# assert_equal(a, 2)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - ":::note\n", - "\n", - "Test suite scopes do *not* nest. In other words, the test suite scope of a\n", - "module is completely independent of the test suite scope of a function or struct\n", - "defined within that module. For example, this means that if a module’s test\n", - "suite creates a variable, that variable is *not* accessible to a function’s test\n", - "suite within the same module.\n", - "\n", - ":::\n", - "\n", - "### Documentation test identifiers\n", - "\n", - "The format of a documentation test identifier is `@::`.\n", - "This is best explained by an example. Consider the project structure shown in\n", - "the [Running tests](#running-tests) section. The source files in the `src`\n", - "directory might contain docstrings for the `my_math` package, the `utils.mojo`\n", - "module, and the individual functions within that module. You could collect the\n", - "full list of tests by executing:\n", - "\n", - "```\n", - "mojo test --co src\n", - "```\n", - "\n", - "The output shows the hierarchy of directories, test files, and individual tests\n", - "(note that this example elides the full filesystem paths from the output shown):\n", - "\n", - "```\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "```\n", - "\n", - "Several different test suites appear in this result:\n", - "\n", - "| Test suite scope | File | Test suite name |\n", - "|------------------|------|-----------------|\n", - "| Package | `src/my_math/__init__.mojo` | `__doc__` |\n", - "| Module | `src/my_math/utils.mojo` | `__doc__` |\n", - "| Function | `src/my_math/utils.mojo` | `inc(stdlib\\3A\\3Abuiltin\\3A\\3Aint\\3A\\3AInt).__doc__` |\n", "\n", - "Then within a specific test suite, tests are numbered sequentially in the order\n", - "they appear in the docstring, starting with 0." + "The [testing module](/mojo/stdlib/testing/testing) defines a set of test-related\n", + "utilities. " ] } ], diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md new file mode 100644 index 0000000000..613652d45c --- /dev/null +++ b/docs/upgrade-guide.md @@ -0,0 +1,219 @@ +--- +title: Mojo🔥 upgrade guide +sidebar_label: Upgrade guide +description: Shows how to upgrade your Mojo programs from one release to the next. +--- + +The following update steps are ordered from most to least common. If you have +compiler warnings or errors, you can go down the list and try recompiling your +program after each item. + +## Upgrading Mojo 0.7 to 24.1 + +### Rename `let` to `var` + +There is now a compiler warning to change the `let` keyword to `var`. But `let` +is a common word that you might mistakenly overwrite a comment or doc string. VS +Code has a nice `find and replace` feature to visualize the changes before +making them. + +First click the search icon and select the icon for Match Whole Word `[ab]`, +then click the arrow to expand the replace bar. Put `let` in the first bar and +`var` in the second, then press enter. You can change one file at a time by +pressing the `Replace All` button next to each file: + +![let to var](./images/let-to-var.png) + +### `vectorize()` and `unroll()` signature change + +The `vectorize()` function now has the ability to add an `unroll_factor` +parameter to unroll the loop. This will often be used as a keyword parameter for +clarity like this: `vectorize[fn, simd_width, unroll_factor=2](size)`. The `fn` +parameter was moved to the front of the signature to accommodate this. + +You can update old code using the find and replace regex syntax to swap the +parameters: + +```re +vectorize\[(.*?), (.*?)\] +vectorize[$2, $1] +``` + +In VS Code back in search, you can press the `.*` icon and paste regex to fix +the signatures: + +![vectorize fix](./images/vectorize-fix.png) + +Similarly, `unroll` has changed to match the vectorize signature, which can be +updated in the same way with the following regex: + +```re +unroll\[(.*?), (.*?)\] +unroll[$2, $1] +``` + +### `vectorize_unroll()` function removed + +You must change `vectorize_unroll[width, unroll_factor, fn](size)` to +`vectorize[fn, width, unroll_factor](size)`. The regex for this is: + +```re +vectorize_unroll\[(.*?), (.*?), (.*?)\] +vectorize[$3, $1, $2] +``` + +### `DynamicVector` constructor `capacity` now keyword-only + +The [`DynamicVector`](/mojo/stdlib/collections/list/List) struct had +a constructor that took a single integer value for the vector's capacity. This +had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. +This was not intended to support implicit conversion, so `capacity` is now a +keyword-only argument to avoid this. To update your code you can use this regex: + +```re +(DynamicVector\[.*Int\]\()(\w.*\)) +$1capacity=$2 +``` + +Which in VS Code looks like this: + +![DynamicVector capacity](./images/dynamic-vector-capacity.png) + +### `NDBuffer` signature change + +The shape of an +[`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) can +now default to being unknown, so the parameter list has been rearranged to +accommodate this: + +For example this: + +```mojo +var buf = NDBuffer[rank, shape, type, address_space]() +``` + +Becomes this: + +```mojo +var buf = NDBuffer[type, rank, shape, address_space]() +``` + +And the `shape` and `address_space` parameters are now optional: + +```mojo +var buf = NDBuffer[type, rank]() +``` + +The regex to fix this is: + +```re +NDBuffer\[(.*?), (.*?), (.*?), +NDBuffer[$3, $1, $2] +``` + +### Dereference `Variant` with `[]` + +Previously, using [`Variant`](/mojo/stdlib/utils/variant/Variant) +was unsafe with heap allocated objects, it now +returns a reference. If you had code that looks like this: + +```mojo +from utils import Variant + +fn foo(variant: Variant[String, Int]): + if variant.isa[String](): + var s = variant.get[String]() + print(s) + +fn main(): + foo(String("foo")) +``` + +You now need to dereference to get access to the pointed to value with `[]`: + +```mojo + var s = variant.get[String]() + print(s[]) +``` + +Note that dereferencing with `[]` is temporary, Mojo will add auto-dereferencing +in the future so this is invisible to the user. + +### String method changes + +Methods on `String` were changed to match Python naming conventions: + +```mojo +var s = String("Foo") +var lower = s.tolower() +var upper = s.toupper() +``` + +Has become: + +```mojo +var s = String("Foo") +var lower = s.lower() +var upper = s.upper() +``` + +The regex to fix this is: + +```re +\.tolower\(\) +lower() +``` + +And: + +```re +\.toupper\(\) +upper() +``` + +### Implicit parameters for arguments + +Previously you could explicitly specify the implicit parameters of an +[automatically parameterized function](/mojo/manual/parameters/#automatic-parameterization-of-functions) +by passing them in the parameter list: + +```mojo +fn foo(x: SIMD): + print(x) + +fn main(): + foo[DType.int32, 1](SIMD[DType.int32, 1](4)) +``` + +This was not intended to be possible and is now a compiler error. These implicit +parameters are always inferred from the function arguments. For example: + +```mojo +fn foo(x: SIMD): + print(x) + +fn main(): + var v =SIMD[DType.int32, 2](4, 2) + foo(v) +``` + +There is no regex to accommodate all the variations of this, so it needs to be +done manually. + +### Using a trait inside another trait + +This no longer compiles: + +```mojo +trait Foo: + fn foo[T: Bar](self): ... + +trait Bar: ... + +struct MyStruct(Foo): + fn foo[T: Bar](self): ... +``` + +You must use concrete types inside trait methods for now, this was an unintended +regression as our codebase wasn't using this feature, it will be fixed in a +future release. diff --git a/examples/README.md b/examples/README.md index f338fad698..4b1b9d147c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,11 +1,11 @@ # Mojo code examples -A collection of sample programs and Mojo notebooks written in the +A collection of sample programs and Mojo notebooks written in the [Mojo](https://docs.modular.com/mojo/programming-manual.html) programming language. ## Getting Started -Access a Mojo programming environment available from the +Access a Mojo programming environment available from the Mojo product [page](https://www.modular.com/mojo). Git clone the repository of Mojo samples using the command below: @@ -16,24 +16,23 @@ git clone https://github.com/modularml/mojo.git ## Running -If you're using [`magic`](https://docs.modular.com/magic), navigate into -the examples directory and use `magic run`. For example: +Use the following sample command-line to run the programs: ```bash -magic run mojo matmul.mojo +mojo matmul.mojo ``` -You can run the Mojo notebooks using [JupyterLab or Visual Studio +You can run the Mojo notebooks using [JupyterLab or Visual Studio Code](notebooks/README.md) with the Mojo extension available on the Marketplace. ### Mojo SDK Container -The repo also contains a Dockerfile that can be used to create a -Mojo SDK container for developing and running Mojo programs. Use the -container in conjunction with the Visual Studio Code devcontainers +The repo also contains a Dockerfile that can be used to create a +Mojo SDK container for developing and running Mojo programs. Use the +container in conjunction with the Visual Studio Code devcontainers extension to develop directly inside the container. -The Dockerfile also sets up a `conda` environment and by default, +The Dockerfile also sets up a `conda` environment and by default, starts a `jupyter` server (which you can access via the browser). To build a Mojo container, either use @@ -56,11 +55,11 @@ The script also supports building with `podman` instead of `docker`: ./build-image.sh --auth-key \ --use-podman \ --mojo-version 0.3 - + ``` -You can then run with either `docker` or `podman`. In the example below, -we map the ports, bind mount the current directory and open a shell into +You can then run with either `docker` or `podman`. In the example below, +we map the ports, bind mount the current directory and open a shell into the container: ```bash @@ -86,8 +85,8 @@ podman run \ ## License -The Mojo examples and notebooks in this repository are licensed -under the Apache License v2.0 with LLVM Exceptions +The Mojo examples and notebooks in this repository are licensed +under the Apache License v2.0 with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Contributing diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 4ee3854595..7c6eecf342 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -29,10 +29,9 @@ notebooks. Especially if you're developing with Mojo on a remote system, using VS Code is ideal because it allows you to edit and interact with notebooks on the remote machine where you've installed Mojo. -All you need is Mojo and the Jupyter VS Code extension: +All you need is the Mojo SDK and the Jupyter VS Code extension: -1. [Create a new Mojo -project](https://docs.modular.com/mojo/manual/get-started#1-create-a-new-project). +1. Install the [Mojo SDK](https://developer.modular.com/download). 2. Install [Visual Studio Code](https://code.visualstudio.com/) and the [Jupyter @@ -57,40 +56,33 @@ instructions don't support remote access to the JupyterLab). For more details about using JupyterLab, see the complete [JupyterLab installation guide](https://jupyterlab.readthedocs.io/en/latest/getting_started/installation.html). -### 1. Launch JupyterLab +**Note:** You must run this setup on the same machine where you've installed +the [Mojo SDK](https://developer.modular.com/download). However, syntax +highlighting for Mojo code is not currently enabled in JupyterLab (coming soon). -You can use either Magic or conda. +1. Install JupyterLab: -#### Using Magic + ```sh + python3 -m pip install jupyterlab + ``` -If you have [`magic`](https://docs.modular.com/magic) you can run the following -command to launch JupyterLab from this directory: +2. Make sure the user-level `bin` is in your `$PATH`: -```sh -magic run jupyter lab -``` + ```sh + export PATH="$HOME/.local/bin:$PATH" + ``` -After a moment, it will open a browser window with JupterLab running. +3. Launch JupyterLab: -#### Using conda + ```sh + jupyter lab + ``` -Create a Conda environment, activate that enviroment, and install JupyterLab. +4. When you open any of the `.ipynb` notebooks from this repository, JupyterLab + should automatically select the Mojo kernel (which was installed with the + Mojo SDK). -``` sh -# Create a Conda environment if you don't have one -conda create -n mojo-repo -# Activate the environment -conda env update -n mojo-repo -f environment.yml --prune -# run JupyterLab -conda run -n mojo-repo jupyter lab -``` - -After a moment, it will open a browser window with JupterLab running. - -### 2. Run the .ipynb notebooks - -The left nav bar should show all the notebooks in this directory. -Open any `.ipynb` file and start running the code. + Now run some Mojo code! ## Notes and tips diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml deleted file mode 100644 index 5b2ca73adb..0000000000 --- a/examples/notebooks/pixi.toml +++ /dev/null @@ -1,13 +0,0 @@ -[project] -name = "Mojo notebooks" -version = "1.0.0" -description = "Environment for running JupyterLab" -authors = ["Modular "] -channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] -platforms = ["osx-arm64", "linux-aarch64", "linux-64"] - -[dependencies] -python = ">=3.8,<3.12" -max = "*" -pip = ">=24.0,<25" -jupyterlab = ">=4.2.5,<5" diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index ff35d8babe..0c46e407dd 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -790,6 +790,34 @@ struct Dict[K: KeyElement, V: CollectionElement]( return default.value()[] raise "KeyError" + fn popitem(inout self) raises -> DictEntry[K, V]: + """Remove and return a (key, value) pair from the dictionary. Pairs are returned in LIFO order. + popitem() is useful to destructively iterate over a dictionary, as often used in set algorithms. + If the dictionary is empty, calling popitem() raises a KeyError. + + Args: None + + Returns: + Last dictionary item + + Raises: + "KeyError" if the dictionary is empty. + """ + + var key = Optional[K](None) + var val = Optional[V](None) + + for item in reversed(self.items()): + key = Optional(item[].key) + val = Optional(item[].value) + break + + if key: + _ = self.pop(key.value()[]) + return DictEntry[K, V](key.value()[], val.value()[]) + + raise "KeyError: popitem(): dictionary is empty" + fn keys( self: Reference[Self, _, _] ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 6417193efe..0bc041869e 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from os import getenv -from sys import external_call, exit +from sys import external_call from sys.ffi import DLHandle from memory import DTypePointer, UnsafePointer @@ -86,6 +86,32 @@ struct PythonVersion: return PythonVersion(components[0], components[1], components[2]) +fn _py_initialize(inout cpython: CPython): + # Configure the Python interpreter to search for libraries installed in + # "site-package" directories. If we don't do this, and a `virtualenv` + # is activated when invoking `mojo`, then the Python interpreter will + # use the system's `site-package` directories instead of the + # `virtualenv` ones. + # + # This function does not overwrite any existing `PYTHONPATH`, in case + # the user wants to specify this environment variable themselves. + # + # Finally, another approach is to use `Py_SetPath()`, but that requires + # setting explicitly every library search path, as well as needing to + # specify a `PYTHONHOME`. This is much more complicated and restrictive + # to be considered a better solution. + var error_message: StringRef = external_call[ + "KGEN_CompilerRT_Python_SetPythonPath", + DTypePointer[DType.int8], + ]() + if len(error_message) != 0: + # Print the error message, but keep going in order to allow + # `Py_Initialize` to fail. + print("Mojo/Python interoperability error: ", error_message) + + cpython.lib.get_function[fn () -> None]("Py_Initialize")() + + fn _py_get_version(lib: DLHandle) -> StringRef: var version_string = lib.get_function[fn () -> DTypePointer[DType.int8]]( "Py_GetVersion" @@ -93,6 +119,32 @@ fn _py_get_version(lib: DLHandle) -> StringRef: return StringRef(version_string) +fn _py_initialize(lib: DLHandle): + # Configure the Python interpreter to search for libraries installed in + # "site-package" directories. If we don't do this, and a `virtualenv` + # is activated when invoking `mojo`, then the Python interpreter will + # use the system's `site-package` directories instead of the + # `virtualenv` ones. + # + # This function does not overwrite any existing `PYTHONPATH`, in case + # the user wants to specify this environment variable themselves. + # + # Finally, another approach is to use `Py_SetPath()`, but that requires + # setting explicitly every library search path, as well as needing to + # specify a `PYTHONHOME`. This is much more complicated and restrictive + # to be considered a better solution. + var error_message: StringRef = external_call[ + "KGEN_CompilerRT_Python_SetPythonPath", + DTypePointer[DType.int8], + ]() + if len(error_message) != 0: + # Print the error message, but keep going in order to allow + # `Py_Initialize` to fail. + print("Mojo/Python interoperability error: ", error_message) + + lib.get_function[fn () -> None]("Py_Initialize")() + + fn _py_finalize(lib: DLHandle): lib.get_function[fn () -> None]("Py_Finalize")() @@ -104,42 +156,28 @@ struct CPython: var logging_enabled: Bool var version: PythonVersion var total_ref_count: UnsafePointer[Int] - var init_error: StringRef fn __init__(inout self: CPython): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") - print("MOJO_PYTHON:", getenv("MOJO_PYTHON")) - print("MOJO_PYTHON_LIBRARY:", getenv("MOJO_PYTHON_LIBRARY")) - - # TODO(MOCO-772) Allow raises to propagate through function pointers - # and make this initialization a raising function. - self.init_error = external_call[ - "KGEN_CompilerRT_Python_SetPythonPath", - DTypePointer[DType.int8], - ]() - var python_lib = getenv("MOJO_PYTHON_LIBRARY") - - if logging_enabled: - print("PYTHONEXECUTABLE:", getenv("PYTHONEXECUTABLE")) - print("libpython selected:", python_lib) + if python_lib == "": + abort( + "Mojo/Python interoperability error: Unable to locate a" + " suitable libpython, please set `MOJO_PYTHON_LIBRARY`" + ) self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) self.none_value = PyObjectPtr() self.dict_type = PyObjectPtr() self.logging_enabled = logging_enabled - if not self.init_error: - if not self.lib.check_symbol("Py_Initialize"): - self.init_error = "compatible Python library not found" - self.lib.get_function[fn () -> None]("Py_Initialize")() - self.version = PythonVersion(_py_get_version(self.lib)) - _ = self.Py_None() - _ = self.PyDict_Type() - else: - self.version = PythonVersion(0, 0, 0) + self.version = PythonVersion(_py_get_version(self.lib)) + + _py_initialize(self.lib) + _ = self.Py_None() + _ = self.PyDict_Type() @staticmethod fn destroy(inout existing: CPython): @@ -156,19 +194,6 @@ struct CPython: existing.lib.close() existing.total_ref_count.free() - fn check_init_error(self) raises: - """Used for entry points that initialize Python on first use, will - raise an error if one occured when initializing the global CPython. - """ - if self.init_error: - var error: String = self.init_error - error += "\nMOJO_PYTHON: " + getenv("MOJO_PYTHON") - error += "\nMOJO_PYTHON_LIBRARY: " + getenv("MOJO_PYTHON_LIBRARY") - error += "\nPYTHONEXECUTABLE: " + getenv("PYTHONEXECUTABLE") - error += "\n\nMojo/Python interop error, troubleshooting docs at:" - error += "\n https://modul.ar/fix-python\n" - raise error - fn Py_None(inout self) -> PyObjectPtr: """Get a None value, of type NoneType.""" if self.none_value.is_null(): @@ -192,7 +217,6 @@ struct CPython: self.logging_enabled = existing.logging_enabled self.version = existing.version self.total_ref_count = existing.total_ref_count - self.init_error = existing.init_error fn _inc_total_rc(inout self): var v = move_from_pointee(self.total_ref_count) @@ -218,17 +242,11 @@ struct CPython: self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) self._dec_total_rc() - fn PyGILState_Ensure(inout self) -> Bool: - return self.lib.get_function[fn () -> Bool]("PyGILState_Ensure")() - - fn PyGILState_Release(inout self, state: Bool): - self.lib.get_function[fn (Bool) -> None]("PyGILState_Release")(state) - - fn PyEval_SaveThread(inout self) -> Int64: - return self.lib.get_function[fn () -> Int64]("PyEval_SaveThread")() + fn PyGILState_Ensure(inout self) -> Int64: + return self.lib.get_function[fn () -> Int64]("PyGILState_Ensure")() - fn PyEval_RestoreThread(inout self, state: Int64): - self.lib.get_function[fn (Int64) -> None]("PyEval_RestoreThread")(state) + fn PyGILState_Release(inout self, state: Int64): + self.lib.get_function[fn (Int64) -> None]("PyGILState_Release")(state) # This function assumes a specific way PyObjectPtr is implemented, namely # that the refcount has offset 0 in that structure. That generally doesn't diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 762f38f6fe..557dc09dd3 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -21,8 +21,9 @@ from python import Python from sys import external_call, sizeof from sys.ffi import _get_global -from os.env import getenv + from memory import UnsafePointer + from utils import StringRef from ._cpython import CPython, Py_eval_input, Py_file_input @@ -198,8 +199,6 @@ struct Python: The Python module. """ var cpython = _get_global_python_itf().cpython() - # Throw error if it occured during initialization - cpython.check_init_error() var module_maybe = cpython.PyImport_ImportModule(module) Python.throw_python_exception_if_error_state(cpython) return PythonObject(module_maybe) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 3da6bd0a62..6ffb5ca9b0 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -69,28 +69,6 @@ struct DLHandle(CollectionElement, Boolable): else: self.handle = DTypePointer[DType.int8]() - fn check_symbol(self, name: String) -> Bool: - """Check that the symbol exists in the dynamic library. - - Args: - name: The symbol to check. - - Returns: - `True` if the symbol exists. - """ - constrained[ - not os_is_windows(), - "Checking dynamic library symbol is not supported on Windows", - ]() - - var opaque_function_ptr = external_call[ - "dlsym", DTypePointer[DType.int8] - ](self.handle.address, name.unsafe_ptr()) - if opaque_function_ptr: - return True - - return False - # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline fn close(inout self): diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index b51f0cb13b..491de7fa8d 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -452,6 +452,7 @@ def test_dict(): test["test_dict_update_empty_new", test_dict_update_empty_new]() test["test_mojo_issue_1729", test_mojo_issue_1729]() test["test dict or", test_dict_or]() + test["test dict popitem", test_dict_popitem]() def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): @@ -512,6 +513,21 @@ def test_find_get(): assert_equal(some_dict.get("not_key", 0), 0) +def test_dict_popitem(): + var dict = Dict[String, Int]() + dict["a"] = 1 + dict["b"] = 2 + + var item = dict.popitem() + assert_equal(item.key, "b") + assert_equal(item.value, 2) + item = dict.popitem() + assert_equal(item.key, "a") + assert_equal(item.value, 1) + with assert_raises(contains="KeyError"): + _ = dict.popitem() + + fn test_clear() raises: var some_dict = Dict[String, Int]() some_dict["key"] = 1 From 46e61dbfb5759459d838692a6ce5c423d61faf8f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 31 May 2024 14:03:12 -0700 Subject: [PATCH 0811/2019] [mojo-lang] Pipe `LifetimeSetType` through Coroutine and its users This PR changes Coroutine to take a LifetimeSet, rather than a singular lifetime with a singular mutability. This allows the compiler to track fine-grained mutability on the lifetime effects of accessing the coroutine. MODULAR_ORIG_COMMIT_REV_ID: 00cc495985fa12179dd1d7ba8697239a54d738d2 --- docs/changelog.md | 2 +- stdlib/src/builtin/coroutine.mojo | 18 ++++-------------- stdlib/src/builtin/type_aliases.mojo | 3 +++ 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5ecddc0d69..7e7ec475c1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -484,7 +484,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### 🦋 Changed -- `Coroutine` now requires a lifetime parameter. This parameter is set +- `Coroutine` now requires a lifetime set parameter. This parameter is set automatically by the parser when calling an async function. It contains the lifetimes of all the arguments and any lifetime accesses by the arguments. This ensures that argument captures by async functions keep the arguments diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index acb2edfd6e..e3d92fa03e 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -81,11 +81,7 @@ fn _coro_resume_noop_callback(handle: AnyCoroutine, null: AnyCoroutine): @register_passable -struct Coroutine[ - is_mut: Bool, //, - type: AnyTrivialRegType, - lifetime: AnyLifetime[is_mut].type, -]: +struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: """Represents a coroutine. Coroutines can pause execution saving the state of the program (including @@ -94,9 +90,8 @@ struct Coroutine[ left off, with the saved state restored. Parameters: - is_mut: Whether the lifetime is mutable. type: Type of value returned upon completion of the coroutine. - lifetime: The lifetime of the coroutine's captures. + lifetimes: The lifetime of the coroutine's captures. """ var _handle: AnyCoroutine @@ -184,11 +179,7 @@ struct Coroutine[ @register_passable -struct RaisingCoroutine[ - is_mut: Bool, //, - type: AnyTrivialRegType, - lifetime: AnyLifetime[is_mut].type, -]: +struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: """Represents a coroutine that can raise. Coroutines can pause execution saving the state of the program (including @@ -197,9 +188,8 @@ struct RaisingCoroutine[ left off, with the saved state restored. Parameters: - is_mut: Whether the lifetime is mutable. type: Type of value returned upon completion of the coroutine. - lifetime: The lifetime of the coroutine's captures. + lifetimes: The lifetime set of the coroutine's captures. """ alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index effc721d13..cd8720e507 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -33,6 +33,9 @@ alias ImmutableStaticLifetime = __mlir_attr.`#lit.lifetime<0>: !lit.lifetime<0>` alias MutableStaticLifetime = __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` """The mutable lifetime that lasts for the entire duration of program execution.""" +alias LifetimeSet = __mlir_type.`!lit.lifetime.set` +"""A set of lifetime parameters.""" + # Helper to build !lit.lifetime type. # TODO: Should be a parametric alias. From 5a2d57f4dff261191b225744e5eb4f79f216f48a Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Fri, 31 May 2024 17:08:10 -0500 Subject: [PATCH 0812/2019] [External] [stdlib] Enable assertions in unit tests (#41057) [External] [stdlib] Enable assertions in unit tests Change the default mode of running the standard library unit tests to run with `-D MOJO_ENABLE_ASSERTIONS`, i.e. assertions enabled. Not all of the tests work with assertions, so appropriate issues are filed to track that work along with a new substitution, `%bare-mojo` for running without assertions enabled. Related to https://github.com/modularml/mojo/issues/2687 Closes https://github.com/modularml/mojo/pull/2718 MODULAR_ORIG_COMMIT_REV_ID: ddc68f1fd7ae786abe135f254264157d264caa93 --- stdlib/docs/development.md | 2 ++ stdlib/test/bit/test_bit.mojo | 2 +- .../builtin/test_debug_assert_mojo_enable_assertions.mojo | 2 +- stdlib/test/builtin/test_debug_assert_warning.mojo | 2 +- stdlib/test/builtin/test_string.mojo | 4 +++- stdlib/test/lit.cfg.py | 3 ++- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index e5426a4e9c..b99f384e27 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -73,6 +73,8 @@ run all the tests automatically. ./stdlib/scripts/run-tests.sh ``` +Note that tests are run with `-D MOJO_ENABLE_ASSERTIONS`. + If you wish to run the unit tests that are in a specific test file, you can do so with diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 46c2d9bb28..e127ce6d8c 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %bare-mojo %s from bit import ( rotate_bits_left, diff --git a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo index d7bf282794..cc6b7eb8bb 100644 --- a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo +++ b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %mojo -D MOJO_ENABLE_ASSERTIONS -debug-level full %s 2>&1 | FileCheck %s +# RUN: not --crash %mojo -debug-level full %s 2>&1 | FileCheck %s # CHECK-LABEL: test_fail diff --git a/stdlib/test/builtin/test_debug_assert_warning.mojo b/stdlib/test/builtin/test_debug_assert_warning.mojo index 7928909e61..32d9b92310 100644 --- a/stdlib/test/builtin/test_debug_assert_warning.mojo +++ b/stdlib/test/builtin/test_debug_assert_warning.mojo @@ -14,7 +14,7 @@ # This file only tests the debug_assert function # # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D ASSERT_WARNING -debug-level full %s | FileCheck %s -check-prefix=CHECK-WARN +# RUN: %bare-mojo -D ASSERT_WARNING -debug-level full %s | FileCheck %s -check-prefix=CHECK-WARN # CHECK-WARN: test_ok diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 1f7d1ac965..114960d1e6 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -10,8 +10,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %bare-mojo %s +# TODO: Replace %bare-mojo with %mojo +# when https://github.com/modularml/mojo/issues/2751 is fixed. from builtin.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 54dceae3fe..0e0f56680b 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -47,7 +47,8 @@ # Substitute %mojo for just `mojo` itself # since we're not supporting `--sanitize` initially # to allow running the tests with LLVM sanitizers. -config.substitutions.insert(0, ("%mojo", "mojo")) +config.substitutions.insert(0, ("%mojo", "mojo -D MOJO_ENABLE_ASSERTIONS")) +config.substitutions.insert(1, ("%bare-mojo", "mojo")) # The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the # open-source stdlib, we need to specify the paths to the just-built From 215e5d359533e4361aeb00c754eb43467bf58232 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 31 May 2024 18:03:24 -0500 Subject: [PATCH 0813/2019] [stdlib] feature: Change `String.unsafe_ptr()` to return `UnsafePointer[UInt8]` * Add `alias C_char = Int8` in sys.ffi * Change `String.unsafe_ptr()` to return `UnsafePointer[UInt8]` (was `Int8`) * Remove `String.unsafe_uint8_ptr()` * Add `String.unsafe_cstr_ptr() -> UnsafePointer[C_char]` for convenience * Change `String._steal_ptr()` to return `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`) * Change `FileHandle._write` from taking `DTypePointer[int8]` to `UnsafePointer[UInt8]` * Change `DLHandle._get_function` to take `UnsafePointer[C_char]` (was `DTypePointer[DType.int8]`) MODULAR_ORIG_COMMIT_REV_ID: 6d49b224eeb2699aae52f659e197aab7f6b6c2eb --- docs/changelog.md | 11 +++++-- stdlib/src/builtin/file.mojo | 5 ++-- stdlib/src/builtin/string.mojo | 41 +++++++++++++-------------- stdlib/src/memory/unsafe_pointer.mojo | 8 ++++-- stdlib/src/sys/ffi.mojo | 11 +++++-- stdlib/src/utils/inline_string.mojo | 2 +- 6 files changed, 43 insertions(+), 35 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7e7ec475c1..11a89d0470 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -542,9 +542,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - Continued transition to `UnsafePointer` and unsigned byte type for strings: - - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) - - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was - `DTypePointer`) + - Rename `String._as_ptr()` to `String.unsafe_ptr()` + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `DTypePointer[DType.int8]`) - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`). - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now @@ -555,6 +555,11 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has the same behavior. +- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` + for convenient interoperability with C APIs. + +- Added `C_char` type alias in `sys.ffi`. + - Added `String.isspace()` method conformant with Python's universal separators. - Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 0fc49ed4ca..50962765f5 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -421,13 +421,12 @@ struct FileHandle: Args: data: The data to write to the file. """ - # TODO: Remove cast when transition to UInt8 strings is complete. - self._write(data.unsafe_ptr().bitcast[Int8](), len(data)) + self._write(data.unsafe_ptr(), len(data)) @always_inline fn _write[ address_space: AddressSpace - ](self, ptr: DTypePointer[DType.int8, address_space], len: Int) raises: + ](self, ptr: UnsafePointer[UInt8, address_space], len: Int) raises: """Write the data to the file. Params: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 42273cd7cc..7eb3525f13 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -18,6 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from bit import countl_zero from collections import List, KeyElement from sys import llvm_intrinsic, bitwidthof +from sys.ffi import C_char from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy @@ -951,7 +952,7 @@ struct String( var buffer = Self._buffer_type() buffer.resize(adjusted_span_len + 1, 0) - var ptr = self.unsafe_uint8_ptr() + var ptr = self.unsafe_ptr() for i in range(adjusted_span_len): buffer[i] = ptr[adjusted_span[i]] buffer[adjusted_span_len] = 0 @@ -1050,12 +1051,12 @@ struct String( buffer.resize(total_len + 1, 0) memcpy( DTypePointer(buffer.data), - self.unsafe_uint8_ptr(), + self.unsafe_ptr(), self_len, ) memcpy( DTypePointer(buffer.data + self_len), - other.unsafe_uint8_ptr(), + other.unsafe_ptr(), other_len + 1, # Also copy the terminator ) return Self(buffer^) @@ -1265,27 +1266,23 @@ struct String( """ pass - # TODO: Remove this method when #2317 is done - fn unsafe_ptr(self) -> UnsafePointer[Int8]: + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. - Note that you should use `unsafe_uint8_ptr()` if you need to access the - pointer as we are now storing the bytes as UInt8. - - See https://github.com/modularml/mojo/issues/2317 for more information. - Returns: The pointer to the underlying memory. """ - return self._buffer.data.bitcast[Int8]() + return self._buffer.data - fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: - """Retrieves a pointer to the underlying memory. + fn unsafe_cstr_ptr(self) -> UnsafePointer[C_char]: + """Retrieves a C-string-compatible pointer to the underlying memory. + + The returned pointer is guaranteed to be null, or NUL terminated. Returns: The pointer to the underlying memory. """ - return self._buffer.data.bitcast[UInt8]() + return self.unsafe_ptr().bitcast[C_char]() fn as_bytes(self) -> List[UInt8]: """Retrieves the underlying byte sequence encoding the characters in @@ -1360,7 +1357,7 @@ struct String( else: return buffer_len - fn _steal_ptr(inout self) -> DTypePointer[DType.int8]: + fn _steal_ptr(inout self) -> UnsafePointer[UInt8]: """Transfer ownership of pointer to the underlying memory. The caller is responsible for freeing up the memory. @@ -1672,9 +1669,9 @@ struct String( if occurrences == -1: return self - var self_start = self.unsafe_uint8_ptr() - var self_ptr = self.unsafe_uint8_ptr() - var new_ptr = new.unsafe_uint8_ptr() + var self_start = self.unsafe_ptr() + var self_ptr = self.unsafe_ptr() + var new_ptr = new.unsafe_ptr() var self_len = len(self) var old_len = len(old) @@ -1801,8 +1798,8 @@ struct String( fn _interleave(self, val: String) -> String: var res = List[UInt8]() - var val_ptr = val.unsafe_uint8_ptr() - var self_ptr = self.unsafe_uint8_ptr() + var val_ptr = val.unsafe_ptr() + var self_ptr = self.unsafe_ptr() res.reserve(len(val) * len(self) + 1) for i in range(len(self)): for j in range(len(val)): @@ -1841,7 +1838,7 @@ struct String( fn _toggle_ascii_case[check_case: fn (UInt8) -> Bool](self) -> String: var copy: String = self - var char_ptr = copy.unsafe_uint8_ptr() + var char_ptr = copy.unsafe_ptr() for i in range(len(self)): var char: UInt8 = char_ptr[i] @@ -1969,7 +1966,7 @@ struct String( for i in range(n): memcpy( dest=buf.data + len_self * i, - src=self.unsafe_uint8_ptr(), + src=self.unsafe_ptr(), count=len_self, ) return String(buf^) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index e94778e672..d14a06a4db 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -122,12 +122,14 @@ struct UnsafePointer[ @staticmethod fn _from_dtype_ptr[ - dtype: DType - ](ptr: DTypePointer[dtype]) -> UnsafePointer[Scalar[dtype]]: + dtype: DType, + ](ptr: DTypePointer[dtype, address_space]) -> UnsafePointer[ + Scalar[dtype], address_space + ]: # TODO: # Is there a better way to create an UnsafePointer from a # DTypePointer? - return UnsafePointer[Scalar[dtype]](address=int(ptr)) + return UnsafePointer[Scalar[dtype], address_space](address=int(ptr)) @staticmethod @always_inline diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 6ffb5ca9b0..8624be87c0 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -19,6 +19,9 @@ from utils import StringRef from .info import os_is_linux, os_is_windows from .intrinsics import _mlirtype_is_eq +alias C_char = Int8 +"""C `char` type.""" + struct RTLD: """Enumeration of the RTLD flags used during dynamic library loading.""" @@ -106,12 +109,12 @@ struct DLHandle(CollectionElement, Boolable): A handle to the function. """ - return self._get_function[result_type](name.unsafe_ptr()) + return self._get_function[result_type](name.unsafe_cstr_ptr()) @always_inline fn _get_function[ result_type: AnyTrivialRegType - ](self, name: DTypePointer[DType.int8]) -> result_type: + ](self, name: UnsafePointer[C_char]) -> result_type: """Returns a handle to the function with the given name in the dynamic library. @@ -149,7 +152,9 @@ struct DLHandle(CollectionElement, Boolable): A handle to the function. """ - return self._get_function[result_type](func_name.unsafe_ptr()) + return self._get_function[result_type]( + func_name.unsafe_ptr().bitcast[C_char]() + ) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 1fe6ed877e..2c59fe2aef 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -246,7 +246,7 @@ struct InlineString(Sized, Stringable, CollectionElement): if self._is_small(): return self._storage[_FixedString[Self.SMALL_CAP]].unsafe_ptr() else: - return self._storage[String].unsafe_uint8_ptr() + return self._storage[String].unsafe_ptr() @always_inline fn as_string_slice( From 669fadaf969a0b3edfbad3ce2f3026ec24ab3cdf Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Fri, 31 May 2024 18:08:41 -0500 Subject: [PATCH 0814/2019] [External] [docs] Add section 'Dev Container' to the Mojo stdlib docs (#41065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [docs] Add section 'Dev Container' to the Mojo stdlib docs Test online with GitHub Codespaces: https://codespaces.new/benz0li/mojo-dev-container?hide_repo_select=true&ref=main Parent image: `glcr.b-data.ch/mojo/base:nightly` 👉 This image is being rebuilt [at the start of every 4th hour](https://cron.help/every-4-hours). Co-authored-by: Olivier Benz Closes modularml/mojo#2664 MODULAR_ORIG_COMMIT_REV_ID: 96e7a1d0b243761a48bd4af3bd1f5f7441abcc85 --- stdlib/docs/development.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index b99f384e27..c88d5cde82 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -17,6 +17,22 @@ And if you're using VS Code: - [Install the nightly VS Code extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly) +### Dev Container + +There is an externally maintained +[Mojo Dev Container](https://github.com/benz0li/mojo-dev-container) with all +prerequisites installed. + +The Dev Container also includes the unit test dependencies `lit` and `FileCheck` +(from LLVM) and has `pre-commit` already set up. + +See [Mojo Dev Container > Usage](https://github.com/benz0li/mojo-dev-container#usage) +on how to use with [Github Codespaces](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository) +or [VS Code](https://code.visualstudio.com/docs/devcontainers/containers). + +If there is a problem with the Dev Container, please open an issue +[here](https://github.com/benz0li/mojo-dev-container/issues). + ## Building the standard library To build the standard library, you can run the From 28ffb3d0d032b8bf4d97ae71b1aa963bb28ec083 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 31 May 2024 16:29:15 -0700 Subject: [PATCH 0815/2019] Move notes for Mojo changelog as per 24.4 branch This still needs copy editing. This is merely to clear the slate for the changelog in main. MODULAR_ORIG_COMMIT_REV_ID: b03122aff8d852d461b1bf746779857eb5291d42 --- docs/changelog-released.md | 673 +++++++++++++++++++++++++++++++++++++ docs/changelog.md | 663 ------------------------------------ 2 files changed, 673 insertions(+), 663 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index faa318fa04..0ea8251ef1 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,6 +25,679 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## UNRELEASED + +### 🔥 Legendary + +### ⭐️ New + +- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. +([PR #2701](https://github.com/modularml/mojo/pull/2701) +by [@jayzhan211](https://github.com/jayzhan211)) + +- Add a `sort` function for list of `ComparableCollectionElement`s. + [PR #2609](https://github.com/modularml/mojo/pull/2609) by + [@mzaks](https://github.com/mzaks) + +- Mojo functions can return an auto-dereferenced refeference to storage with a + new `ref` keyword in the result type specifier. For example: + + ```mojo + struct Pair: + var first: Int + var second: Int + fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + return self.first + fn show_mutation(): + var somePair = ... + get_first_ref(somePair) = 1 + ``` + + This approach provides a general way to return an "automatically dereferenced" + reference of a given type. Notably, this eliminates the need for + `__refitem__` to exist. `__refitem__` has thus been removed and replaced with + `__getitem__` that returns a reference. + +- Mojo has introduced `@parameter for`, a new feature for compile-time + programming. `@parameter for` defines a for loop where the sequence and the + induction values in the sequence must be parameter values. For example: + + ```mojo + fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") + ``` + + Currently, `@parameter for` requires the sequence's `__iter__` method to + return a `_StridedRangeIterator`, meaning the induction variables must be + `Int`. The intention is to lift these restrictions in the future. + +- Mojo added support for the inferred parameters. Inferred parameters must + appear at the beginning of the parameter list and cannot be explicitly + specified by the user. They are declared to the left of a `//` marker, much + like positional-only parameters. This allows programmers to define functions + with dependent parameters to be called without the caller specifying all the + necessary parameters. For example: + + ```mojo + fn parameter_simd[dt: DType, //, value: Scalar[dt]](): + print(value) + + fn call_it(): + parameter_simd[Int32(42)]() + ``` + + In the above example, `Int32(42)` is passed directly into `value`, the first + non-inferred parameter. `dt` is inferred from the parameter itself to be + `DType.int32`. + + This also works with structs. For example: + + ```mojo + struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: + pass + + fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` + pass + ``` + + This should make working with dependent parameters more ergonomic. + +- Mojo now allows functions overloaded on parameters to be resolved when forming + references to, but not calling, those functions. For example, the following + now works: + + ```mojo + fn overloaded_parameters[value: Int32](): + pass + + fn overloaded_parameters[value: Float32](): + pass + + fn form_reference(): + alias ref = overloaded_parameters[Int32()] # works! + ``` + +- Mojo now supports adding a `@deprecated` decorator on structs, functions, + traits, aliases, and global variables. The decorator marks the attached decl + as deprecated and causes a warning to be emitted when the deprecated decl is + referenced in user code. The decorator requires a deprecation message to be + specified as a string literal. + + ```mojo + @deprecated("Foo is deprecated, use Bar instead") + struct Foo: + pass + + fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead + pass + + @deprecated("use another function!") + fn bar(): + pass + + fn techdebt(): + bar() # warning: use another function! + ``` + +- Mojo has changed how `def` arguments are processed. Previously, by default, + arguments to a `def` were treated treated according to the `owned` convention, + which makes a copy of the value, enabling that value to be mutable in the callee. + This "worked", but was a major performance footgun, and required you to declare + non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: + it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local + copy of the value **only if the argument is mutated** in the body of the function. + This improves consistency, performance, and ease of use. + +- `int()` can now take a string and a specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with the + base determined by whether the string contains the prefix `"0x"`, `"0o"`, or + `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by + [@artemiogr97](https://github.com/artemiogr97), fixes + [#2274](https://github.com/modularml/mojo/issues/2274)) + +- Mojo now supports types to opt in to use the `abs` and `round` functions by + implementing the `__abs__` and `__round__` methods (i.e. by conforming to the + new `Absable` and `Roundable` traits), respectively, e.g.: + + ```mojo + from math import sqrt + + @value + struct Complex(Absable, Roundable): + var re: Float64 + var im: Float64 + + fn __abs__(self) -> Self: + return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) + + fn __round__(self) -> Self: + return Self(round(self.re), round(self.im)) + ``` + +- User defined types can now also opt in to use the `pow` function by + implementing the `__pow__` method (and thus conforming to the new `Powable` + trait). As before, these types will also benefit from being able to use the + `**` operator. + +- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` + functions in the `math` module by implementing the `__floor__()`, + `__ceil__()`, and `__trunc__()` methods (and so conforming to the new + `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). + For example: + + ```mojo + from math import Ceilable, Floorable, Truncable, ceil, floor, trunc + + @value + struct Complex(Ceilable, Floorable, Truncable): + var re: Float64 + var im: Float64 + + fn __ceil__(self) -> Self: + return Self(ceil(re), ceil(im)) + + fn __floor__(self) -> Self: + return Self(floor(re), floor(im)) + + fn __trunc__(self) -> Self: + return Self(trunc(re), trunc(im)) + ``` + +- You can now use the builtin `any()` and `all()` functions to check for truthy + elements in a collection. Because `SIMD.__bool__()` is now constrained to + `size=1`, You must explicity use these to get the truthy value of a SIMD + vector. This avoids common bugs around implicit conversion of `SIMD` to + `Bool`. + ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) + + For example: + + ```mojo + fn truthy_simd(): + var vec = SIMD[DType.int32, 4](0, 1, 2, 3) + if any(vec): + print("any elements are truthy") + if all(vec): + print("all elements are truthy") + ``` + +- Add an `InlinedArray` type that works on memory-only types. + Compare with the existing `StaticTuple` type, which is conceptually an array + type, but only worked on `AnyTrivialRegType`. + ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) + +- Base64 decoding support has been added. + ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) + +- Add Base16 encoding and decoding support. + ([PR #2584](https://github.com/modularml/mojo/pull/2584) + by [@kernhanda](https://github.com/kernhanda)) + +- Add `repr()` function and `Representable` trait. + ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Add `SIMD.shuffle()` with `StaticIntTuple` mask. + ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) + +- Invoking `mojo package my-package -o my-dir` on the command line, where + `my-package` is a Mojo package source directory, and `my-dir` is an existing + directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. + Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. + +- The Mojo Language Server now reports a warning when a local variable is unused. + +- Implicit variable definitions in a `def` are more flexible: you can now + implicitly declare variables as the result of a tuple return, using + `a,b,c = foo()`, and can now shadow global immutable symbols using + `slice = foo()` without getting a compiler error. + +- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that + allow users to opt into the `math.ceildiv` function. + +- Mojo now allows methods to declare `self` as a `Reference` directly, which + can be useful for advanced cases of parametric mutabilty and custom lifetime + processing. Previously it required the use of an internal MLIR type to + achieve this. + +- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, + not a low-level `__mlir_type.i1` value. + + This improves the ergonomics of spelling out a + `Reference` type explicitly. For example, to define a struct holding a + `Reference`, you can now write: + + ```mojo + struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: + var data: Reference[Int32, is_mutable, lifetime] + ``` + + Or to specify a field that is always immutable, `False` can be specified + as the mutability: + + ```mojo + struct Foo[lifetime: AnyLifetime[False].type]: + var data: Reference[Int32, False, lifetime] + ``` + +- `object` now implements all the bitwise operators. + ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) + +- A new `--validate-doc-strings` option has been added to `mojo` to emit errors + on invalid doc strings instead of warnings. + +- Several `mojo` subcommands now support a `--diagnostic-format` option that + changes the format with which errors, warnings, and other diagnostics are + printed. By specifying `--diagnostic-format json` on the command line, errors + and other diagnostics will be output in a structured + [JSON Lines](https://jsonlines.org) format that is easier for machines to + parse. + + The full list of subcommands that support `--diagnostic-format` is as follows: + `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. + Further, the `mojo test --json` option has been subsumed into this new option; + for the same behavior, run `mojo test --diagnostic-format json`. + + Note that the format of the JSON output may change; we don't currently + guarantee its stability across releases of Mojo. + +- A new decorator, `@doc_private`, was added that can be used to hide a decl + from being generated in the output of `mojo doc`. It also removes the + requirement that the decl has documentation (e.g. when used with + --diagnose-missing-doc-strings). + +- Added a new `Span` type for taking slices of contiguous collections. + ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) + +- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type + in standard library code. + + `StringSlice` is a non-owning reference to encoded string data. Unlike + `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it + points to. + + - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. + - Changed `Formatter.write_str()` to take a safe `StringSlice`. + +- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which + returns a `Span` of the bytes owned by the string. + +- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers + +- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. + +- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), + please use the default constructor instead: `UnsafePointer[T]()`. + +- `Dict` now implements `get(key)` and `get(key, default)` functions. + ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) + +- Debugger users can now set breakpoints on function calls in O0 builds even if + the call has been inlined by the compiler. + +- The `os` module now provides functionality for adding and removing directories + using `mkdir` and `rmdir`. + ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) + +- `Dict.__get_ref(key)`, allowing to get references to dictionary values. + +- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters + other than whitespace. In addition, there are now several useful aliases for + whitespace, ASCII lower/uppercase, and so on. + ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) + +- `String` now has a `splitlines()` method, which allows splitting strings at line + boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) + and provides an option to retain or remove the line break characters. + ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) + +- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. + ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. + ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) + +- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. + Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` + is not possible yet. + ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `List()` now supports `__contains__`. + ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) + +- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) + type, a stack-allocated list with a static maximum size. + ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by + [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- `InlineList()` now supports `__contains__`, `__iter__`. + ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) + +- `List` now has an `index` method that allows one to find the (first) location + of an element in a `List` of `EqualityComparable` types. For example: + + ```mojo + var my_list = List[Int](2, 3, 5, 7, 3) + print(my_list.index(3)) # prints 1 + ``` + +- `List` can now be converted to a `String` with a simplified syntax: + + ```mojo + var my_list = List[Int](2, 3) + print(my_list.__str__()) # prints [2, 3] + ``` + + Note that `List` doesn't conform to the `Stringable` trait yet so you cannot + use `str(my_list)` yet. + ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added the `Indexer` trait to denote types that implement the `__index__()` + method which allows these types to be accepted in common `__getitem__` and + `__setitem__` implementations, as well as allow a new builtin `index` function + to be called on them. Most stdlib containers are now able to be indexed by + any type that implements `Indexer`. For example: + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + struct MyList: + var data: List[Int] + + fn __init__(inout self): + self.data = List[Int](1, 2, 3, 4) + + fn __getitem__[T: Indexer](self, idx: T) -> T: + return self.data[index(idx)] + + print(MyList()[AlwaysZero()]) # prints `1` + ``` + + ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + + Types conforming to the `Indexer` trait are implicitly convertible to Int. + This means you can write generic APIs that take `Int` instead of making them + take a generic type that conforms to `Indexer`, e.g. + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + @value + struct Incrementer: + fn __getitem__(self, idx: Int) -> Int: + return idx + 1 + + var a = Incrementer() + print(a[AlwaysZero()]) # works and prints 1 + ``` + +- `StringRef` now implements `strip()` which can be used to remove leading and + trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) + by [@fknfilewalker](https://github.com/fknfilewalker)) + +- The `bencher` module as part of the `benchmark` package is now public + and documented. This module provides types such as `Bencher` which provides + the ability to execute a `Benchmark` and allows for benchmarking configuration + via the `BenchmarkConfig` struct. + +- Added the `bin()` builtin function to convert integral types into their binary + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) + by [@bgreni](https://github.com/bgreni)) + +- Added `atof()` function which can convert a `String` to a `float64`. + ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) + +- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) + by [@rd4com](https://github.com/rd4com)) For example: + + ```mojo + var x = Tuple(1, 2, True) + if 1 in x: + print("x contains 1") + ``` + +- Added `os.getsize` function, which gives the size in bytes of a path. + ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) + +- `List` now has a method `unsafe_get` to get the reference to an + element without bounds check or wraparound for negative indices. + Note that this method is unsafe. Use with caution. + ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + +- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys + and value. + ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `clear` method to `Dict`. + ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `os.path.join` function. + ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) + +- `StringRef` now implements `startswith()` and `endswith()`. + ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) + +- The Mojo Language Server now supports renaming local variables. + +- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. + ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. +([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) + +### 🦋 Changed + +- `Coroutine` now requires a lifetime set parameter. This parameter is set + automatically by the parser when calling an async function. It contains the + lifetimes of all the arguments and any lifetime accesses by the arguments. + This ensures that argument captures by async functions keep the arguments + alive as long as the coroutine is alive. + +- Async function calls are no longer allowed to borrow non-trivial + register-passable types. Because async functions capture their arguments but + register-passable types don't have lifetimes (yet), Mojo is not able to + correctly track the reference, making this unsafe. To cover this safety gap, + Mojo has temporarily disallowed binding non-trivial register-passable types + to borrowed arguments in async functions. + +- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids + binding non-trivial register-passable types to `AnyTrivialRegType`. This + closes a major safety hole in the language. Please use `AnyType` for generic + code going forward. + +- The `let` keyword has been completely removed from the language. We previously + removed `let` declarations but still provided an error message to users. Now, + it is completely gone from the grammar. Long live `var`! + +- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved + from `math` to `builtin`, so you no longer need to do + `from math import abs, round, min, max, divmod, pow`. + + - The `is_power_of_2` function in the `math` module is now called + `is_power_of_two` and located in the `bit` module. + +- Many functions returning a pointer type have been unified to have a public + API function of `unsafe_ptr()`. + +- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to + `--diagnose-missing-doc-strings`. + +- The `take` function in `Variant` and `Optional` has been renamed to + `unsafe_take`. + +- The `get` function in `Variant` has been replaced by `__refitem__`. That is, + `v.get[T]()` should be replaced with `v[T]`. + +- Various functions in the `algorithm` module are now moved to be + builtin-functions. This includes `sort`, `swap`, and `partition`. + `swap` and `partition` will likely shuffle around as we're reworking + our builtin `sort` function and optimizing it. + +- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, + explicitly use `any()` or `all()`. + ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) + +- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise + operations, and support integer types. + ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) + +- `ListLiteral` and `Tuple` now only require that element types be `Movable`. + Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. + +- Continued transition to `UnsafePointer` and unsigned byte type for strings: + - Rename `String._as_ptr()` to `String.unsafe_ptr()` + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `DTypePointer[DType.int8]`) + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was + `DTypePointer`). + - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now + returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) + - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was + `DTypePointer[DType.int8]`). + - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has + the same behavior. + +- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` + for convenient interoperability with C APIs. + +- Added `C_char` type alias in `sys.ffi`. + +- Added `String.isspace()` method conformant with Python's universal separators. + +- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), + use `String.isspace()` instead. + +- `String.split()` now defaults to whitespace and has pythonic behavior in that + it removes all adjacent whitespaces by default. + +- Added `UnsafePointer.offset()` method. + +- The `math.bit` module has been moved to a new top-level `bit` module. The + following functions in this module have been renamed: + - `ctlz` -> `countl_zero` + - `cttz` -> `countr_zero` + - `bit_length` -> `bit_width` + - `ctpop` -> `pop_count` + - `bswap` -> `byte_swap` + - `bitreverse` -> `bit_reverse` + +- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been + moved to the `bit` module. + +- The `math.tgamma` function has been renamed to `math.gamma` to conform with + Python's naming. + +- The implementation of the following functions have been moved from the `math` + module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, + `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the + `math` module. + +- `InlinedString` has been renamed to `InlineString` to be consistent with other + types. + +- The `Slice.__len__` function has been removed and `Slice` no longer conforms + to the `Sized` trait. This clarifies the ambiguity of the semantics: the + length of a slice always depends on the length of the object being sliced. + Users that need the existing functionality can use the `Slice.unsafe_indices` + method. This makes it explicit that this implementation does not check if the + slice bounds are concrete or within any given object's length. + +- Implicit conversion to `String` is now removed for builtin classes/types. + One should use `str(...)` explicitly to convert to `String`. + +- `math.gcd` now works on negative inputs, and like Python's implementation, + accepts a variadic list of integers. New overloads for a `List` or `Span`of + integers are also added. + ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) + +### ❌ Removed + +- The `@unroll` decorator has been deprecated and removed. The decorator was + supposed to guarantee that a decorated loop would be unrolled, or else the + compiler would error. In practice, this guarantee was eroded over time, as + a compiler-based approach cannot be as robust as the Mojo parameter system. + In addition, the `@unroll` decorator did not make the loop induction variables + parameter values, limiting its usefulness. Please see `@parameter for` for a + replacement! + +- The method `object.print()` has been removed. Since now, `object` has the + `Stringable` trait, you can use `print(my_object)` instead. + +- The following functions have been removed from the math module: + - `clamp`; use the new `SIMD.clamp` method instead. + - `round_half_down` and `round_half_up`; these can be trivially implemented + using the `ceil` and `floor` functions. + - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, + `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and + `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, + `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, + respectively. + - `identity` and `reciprocal`; users can implement these trivially. + - `select`; in favor of using `SIMD.select` directly. + - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` + with `1`. + - `roundeven`; the new `SIMD.roundeven` method now provides the identical + functionality. + - `div_ceil`; use the new `ceildiv` function. + - `rotate_left` and `rotate_right`; the same functionality is available in the + builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the + `bit.rotate_bits_{left,right}` methods for `Int`. + - an overload of `math.pow` taking an integer parameter exponent. + - `align_down_residual`; it can be trivially implemented using `align_down`. + - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and + `SIMD.reduce_or` directly. + - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. + - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. + +- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's + method is no longer available. This method was limited to degree 10 or less, + underutilized, and its performance unclear. In the future, this might be + reintroduced with an improved implementation if needed, when better + performance benchmarking infrastructure is available. The default behavior of + `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). + +- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The + same functionality is available in the builtin `SIMD.select` and + `SIMD.__and__` methods, respectively. + +- The `math.limit` module has been removed. The same functionality is available + as follows: + - `math.limit.inf`: use `utils.numerics.max_or_inf` + - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` + - `math.limit.max_finite`: use `utils.numerics.max_finite` + - `math.limit.min_finite`: use `utils.numerics.min_finite` + +- The `tensor.random` module has been removed. The same functionality is now + accessible via the `Tensor.rand` and `Tensor.randn` static methods. + +- The builtin `SIMD` struct no longer conforms to `Indexer`; users must + explicitly cast `Scalar` values using `int`. + +### 🛠️ Fixed + +- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential + variant crashing the compiler. +- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on + simple trait definitions. +- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using + `//` on `FloatLiteral` in alias expression. +- Made several improvements to dictionary performance. Dicts with integer keys + are most heavily affected, but large dicts and dicts with large values + will also see large improvements. +- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` + to include calling location. + ## v24.3 (2024-05-02) ### ✨ Highlights diff --git a/docs/changelog.md b/docs/changelog.md index 11a89d0470..e0d605860b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,673 +14,10 @@ what we publish. ## UNRELEASED -### 🔥 Legendary - ### ⭐️ New -- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. -([PR #2701](https://github.com/modularml/mojo/pull/2701) -by [@jayzhan211](https://github.com/jayzhan211)) - -- Add a `sort` function for list of `ComparableCollectionElement`s. - [PR #2609](https://github.com/modularml/mojo/pull/2609) by - [@mzaks](https://github.com/mzaks) - -- Mojo functions can return an auto-dereferenced refeference to storage with a - new `ref` keyword in the result type specifier. For example: - - ```mojo - struct Pair: - var first: Int - var second: Int - fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: - return self.first - fn show_mutation(): - var somePair = ... - get_first_ref(somePair) = 1 - ``` - - This approach provides a general way to return an "automatically dereferenced" - reference of a given type. Notably, this eliminates the need for - `__refitem__` to exist. `__refitem__` has thus been removed and replaced with - `__getitem__` that returns a reference. - -- Mojo has introduced `@parameter for`, a new feature for compile-time - programming. `@parameter for` defines a for loop where the sequence and the - induction values in the sequence must be parameter values. For example: - - ```mojo - fn parameter_for[max: Int](): - @parameter - for i in range(max) - @parameter - if i == 10: - print("found 10!") - ``` - - Currently, `@parameter for` requires the sequence's `__iter__` method to - return a `_StridedRangeIterator`, meaning the induction variables must be - `Int`. The intention is to lift these restrictions in the future. - -- Mojo added support for the inferred parameters. Inferred parameters must - appear at the beginning of the parameter list and cannot be explicitly - specified by the user. They are declared to the left of a `//` marker, much - like positional-only parameters. This allows programmers to define functions - with dependent parameters to be called without the caller specifying all the - necessary parameters. For example: - - ```mojo - fn parameter_simd[dt: DType, //, value: Scalar[dt]](): - print(value) - - fn call_it(): - parameter_simd[Int32(42)]() - ``` - - In the above example, `Int32(42)` is passed directly into `value`, the first - non-inferred parameter. `dt` is inferred from the parameter itself to be - `DType.int32`. - - This also works with structs. For example: - - ```mojo - struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: - pass - - fn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` - pass - ``` - - This should make working with dependent parameters more ergonomic. - -- Mojo now allows functions overloaded on parameters to be resolved when forming - references to, but not calling, those functions. For example, the following - now works: - - ```mojo - fn overloaded_parameters[value: Int32](): - pass - - fn overloaded_parameters[value: Float32](): - pass - - fn form_reference(): - alias ref = overloaded_parameters[Int32()] # works! - ``` - -- Mojo now supports adding a `@deprecated` decorator on structs, functions, - traits, aliases, and global variables. The decorator marks the attached decl - as deprecated and causes a warning to be emitted when the deprecated decl is - referenced in user code. The decorator requires a deprecation message to be - specified as a string literal. - - ```mojo - @deprecated("Foo is deprecated, use Bar instead") - struct Foo: - pass - - fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead - pass - - @deprecated("use another function!") - fn bar(): - pass - - fn techdebt(): - bar() # warning: use another function! - ``` - -- Mojo has changed how `def` arguments are processed. Previously, by default, - arguments to a `def` were treated treated according to the `owned` convention, - which makes a copy of the value, enabling that value to be mutable in the callee. - This "worked", but was a major performance footgun, and required you to declare - non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: - it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local - copy of the value **only if the argument is mutated** in the body of the function. - This improves consistency, performance, and ease of use. - -- `int()` can now take a string and a specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is - specified, the string will be parsed as if it was an integer literal, with the - base determined by whether the string contains the prefix `"0x"`, `"0o"`, or - `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by - [@artemiogr97](https://github.com/artemiogr97), fixes - [#2274](https://github.com/modularml/mojo/issues/2274)) - -- Mojo now supports types to opt in to use the `abs` and `round` functions by - implementing the `__abs__` and `__round__` methods (i.e. by conforming to the - new `Absable` and `Roundable` traits), respectively, e.g.: - - ```mojo - from math import sqrt - - @value - struct Complex(Absable, Roundable): - var re: Float64 - var im: Float64 - - fn __abs__(self) -> Self: - return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) - - fn __round__(self) -> Self: - return Self(round(self.re), round(self.im)) - ``` - -- User defined types can now also opt in to use the `pow` function by - implementing the `__pow__` method (and thus conforming to the new `Powable` - trait). As before, these types will also benefit from being able to use the - `**` operator. - -- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` - functions in the `math` module by implementing the `__floor__()`, - `__ceil__()`, and `__trunc__()` methods (and so conforming to the new - `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). - For example: - - ```mojo - from math import Ceilable, Floorable, Truncable, ceil, floor, trunc - - @value - struct Complex(Ceilable, Floorable, Truncable): - var re: Float64 - var im: Float64 - - fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) - - fn __floor__(self) -> Self: - return Self(floor(re), floor(im)) - - fn __trunc__(self) -> Self: - return Self(trunc(re), trunc(im)) - ``` - -- You can now use the builtin `any()` and `all()` functions to check for truthy - elements in a collection. Because `SIMD.__bool__()` is now constrained to - `size=1`, You must explicity use these to get the truthy value of a SIMD - vector. This avoids common bugs around implicit conversion of `SIMD` to - `Bool`. - ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) - - For example: - - ```mojo - fn truthy_simd(): - var vec = SIMD[DType.int32, 4](0, 1, 2, 3) - if any(vec): - print("any elements are truthy") - if all(vec): - print("all elements are truthy") - ``` - -- Add an `InlinedArray` type that works on memory-only types. - Compare with the existing `StaticTuple` type, which is conceptually an array - type, but only worked on `AnyTrivialRegType`. - ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) - -- Base64 decoding support has been added. - ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) - -- Add Base16 encoding and decoding support. - ([PR #2584](https://github.com/modularml/mojo/pull/2584) - by [@kernhanda](https://github.com/kernhanda)) - -- Add `repr()` function and `Representable` trait. - ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Add `SIMD.shuffle()` with `StaticIntTuple` mask. - ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) - -- Invoking `mojo package my-package -o my-dir` on the command line, where - `my-package` is a Mojo package source directory, and `my-dir` is an existing - directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. - Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. - -- The Mojo Language Server now reports a warning when a local variable is unused. - -- Implicit variable definitions in a `def` are more flexible: you can now - implicitly declare variables as the result of a tuple return, using - `a,b,c = foo()`, and can now shadow global immutable symbols using - `slice = foo()` without getting a compiler error. - -- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that - allow users to opt into the `math.ceildiv` function. - -- Mojo now allows methods to declare `self` as a `Reference` directly, which - can be useful for advanced cases of parametric mutabilty and custom lifetime - processing. Previously it required the use of an internal MLIR type to - achieve this. - -- The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, - not a low-level `__mlir_type.i1` value. - - This improves the ergonomics of spelling out a - `Reference` type explicitly. For example, to define a struct holding a - `Reference`, you can now write: - - ```mojo - struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: - var data: Reference[Int32, is_mutable, lifetime] - ``` - - Or to specify a field that is always immutable, `False` can be specified - as the mutability: - - ```mojo - struct Foo[lifetime: AnyLifetime[False].type]: - var data: Reference[Int32, False, lifetime] - ``` - -- `object` now implements all the bitwise operators. - ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) - -- A new `--validate-doc-strings` option has been added to `mojo` to emit errors - on invalid doc strings instead of warnings. - -- Several `mojo` subcommands now support a `--diagnostic-format` option that - changes the format with which errors, warnings, and other diagnostics are - printed. By specifying `--diagnostic-format json` on the command line, errors - and other diagnostics will be output in a structured - [JSON Lines](https://jsonlines.org) format that is easier for machines to - parse. - - The full list of subcommands that support `--diagnostic-format` is as follows: - `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. - Further, the `mojo test --json` option has been subsumed into this new option; - for the same behavior, run `mojo test --diagnostic-format json`. - - Note that the format of the JSON output may change; we don't currently - guarantee its stability across releases of Mojo. - -- A new decorator, `@doc_private`, was added that can be used to hide a decl - from being generated in the output of `mojo doc`. It also removes the - requirement that the decl has documentation (e.g. when used with - --diagnose-missing-doc-strings). - -- Added a new `Span` type for taking slices of contiguous collections. - ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) - -- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type - in standard library code. - - `StringSlice` is a non-owning reference to encoded string data. Unlike - `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it - points to. - - - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. - - Changed `Formatter.write_str()` to take a safe `StringSlice`. - -- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which - returns a `Span` of the bytes owned by the string. - -- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers - -- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. - -- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), - please use the default constructor instead: `UnsafePointer[T]()`. - -- `Dict` now implements `get(key)` and `get(key, default)` functions. - ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) - -- Debugger users can now set breakpoints on function calls in O0 builds even if - the call has been inlined by the compiler. - -- The `os` module now provides functionality for adding and removing directories - using `mkdir` and `rmdir`. - ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) - -- `Dict.__get_ref(key)`, allowing to get references to dictionary values. - -- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters - other than whitespace. In addition, there are now several useful aliases for - whitespace, ASCII lower/uppercase, and so on. - ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) - -- `String` now has a `splitlines()` method, which allows splitting strings at line - boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) - and provides an option to retain or remove the line break characters. - ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) - -- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. - ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. - ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) - -- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. - Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` - is not possible yet. - ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `List()` now supports `__contains__`. - ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) - -- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) - type, a stack-allocated list with a static maximum size. - ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- `InlineList()` now supports `__contains__`, `__iter__`. - ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) - -- `List` now has an `index` method that allows one to find the (first) location - of an element in a `List` of `EqualityComparable` types. For example: - - ```mojo - var my_list = List[Int](2, 3, 5, 7, 3) - print(my_list.index(3)) # prints 1 - ``` - -- `List` can now be converted to a `String` with a simplified syntax: - - ```mojo - var my_list = List[Int](2, 3) - print(my_list.__str__()) # prints [2, 3] - ``` - - Note that `List` doesn't conform to the `Stringable` trait yet so you cannot - use `str(my_list)` yet. - ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Added the `Indexer` trait to denote types that implement the `__index__()` - method which allows these types to be accepted in common `__getitem__` and - `__setitem__` implementations, as well as allow a new builtin `index` function - to be called on them. Most stdlib containers are now able to be indexed by - any type that implements `Indexer`. For example: - - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 - - struct MyList: - var data: List[Int] - - fn __init__(inout self): - self.data = List[Int](1, 2, 3, 4) - - fn __getitem__[T: Indexer](self, idx: T) -> T: - return self.data[index(idx)] - - print(MyList()[AlwaysZero()]) # prints `1` - ``` - - ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) - - Types conforming to the `Indexer` trait are implicitly convertible to Int. - This means you can write generic APIs that take `Int` instead of making them - take a generic type that conforms to `Indexer`, e.g. - - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 - - @value - struct Incrementer: - fn __getitem__(self, idx: Int) -> Int: - return idx + 1 - - var a = Incrementer() - print(a[AlwaysZero()]) # works and prints 1 - ``` - -- `StringRef` now implements `strip()` which can be used to remove leading and - trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) - by [@fknfilewalker](https://github.com/fknfilewalker)) - -- The `bencher` module as part of the `benchmark` package is now public - and documented. This module provides types such as `Bencher` which provides - the ability to execute a `Benchmark` and allows for benchmarking configuration - via the `BenchmarkConfig` struct. - -- Added the `bin()` builtin function to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) - by [@bgreni](https://github.com/bgreni)) - -- Added `atof()` function which can convert a `String` to a `float64`. - ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) - -- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) - by [@rd4com](https://github.com/rd4com)) For example: - - ```mojo - var x = Tuple(1, 2, True) - if 1 in x: - print("x contains 1") - ``` - -- Added `os.getsize` function, which gives the size in bytes of a path. - ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) - -- `List` now has a method `unsafe_get` to get the reference to an - element without bounds check or wraparound for negative indices. - Note that this method is unsafe. Use with caution. - ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys - and value. - ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `clear` method to `Dict`. - ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `os.path.join` function. - ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) - -- `StringRef` now implements `startswith()` and `endswith()`. - ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) - -- The Mojo Language Server now supports renaming local variables. - -- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. - ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. -([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) - ### 🦋 Changed -- `Coroutine` now requires a lifetime set parameter. This parameter is set - automatically by the parser when calling an async function. It contains the - lifetimes of all the arguments and any lifetime accesses by the arguments. - This ensures that argument captures by async functions keep the arguments - alive as long as the coroutine is alive. - -- Async function calls are no longer allowed to borrow non-trivial - register-passable types. Because async functions capture their arguments but - register-passable types don't have lifetimes (yet), Mojo is not able to - correctly track the reference, making this unsafe. To cover this safety gap, - Mojo has temporarily disallowed binding non-trivial register-passable types - to borrowed arguments in async functions. - -- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids - binding non-trivial register-passable types to `AnyTrivialRegType`. This - closes a major safety hole in the language. Please use `AnyType` for generic - code going forward. - -- The `let` keyword has been completely removed from the language. We previously - removed `let` declarations but still provided an error message to users. Now, - it is completely gone from the grammar. Long live `var`! - -- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved - from `math` to `builtin`, so you no longer need to do - `from math import abs, round, min, max, divmod, pow`. - - - The `is_power_of_2` function in the `math` module is now called - `is_power_of_two` and located in the `bit` module. - -- Many functions returning a pointer type have been unified to have a public - API function of `unsafe_ptr()`. - -- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to - `--diagnose-missing-doc-strings`. - -- The `take` function in `Variant` and `Optional` has been renamed to - `unsafe_take`. - -- The `get` function in `Variant` has been replaced by `__refitem__`. That is, - `v.get[T]()` should be replaced with `v[T]`. - -- Various functions in the `algorithm` module are now moved to be - builtin-functions. This includes `sort`, `swap`, and `partition`. - `swap` and `partition` will likely shuffle around as we're reworking - our builtin `sort` function and optimizing it. - -- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, - explicitly use `any()` or `all()`. - ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) - -- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise - operations, and support integer types. - ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) - -- `ListLiteral` and `Tuple` now only require that element types be `Movable`. - Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - -- Continued transition to `UnsafePointer` and unsigned byte type for strings: - - Rename `String._as_ptr()` to `String.unsafe_ptr()` - - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `DTypePointer[DType.int8]`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was - `DTypePointer`). - - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now - returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). - - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) - - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was - `DTypePointer[DType.int8]`). - - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has - the same behavior. - -- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` - for convenient interoperability with C APIs. - -- Added `C_char` type alias in `sys.ffi`. - -- Added `String.isspace()` method conformant with Python's universal separators. - -- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), - use `String.isspace()` instead. - -- `String.split()` now defaults to whitespace and has pythonic behavior in that - it removes all adjacent whitespaces by default. - -- Added `UnsafePointer.offset()` method. - -- The `math.bit` module has been moved to a new top-level `bit` module. The - following functions in this module have been renamed: - - `ctlz` -> `countl_zero` - - `cttz` -> `countr_zero` - - `bit_length` -> `bit_width` - - `ctpop` -> `pop_count` - - `bswap` -> `byte_swap` - - `bitreverse` -> `bit_reverse` - -- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been - moved to the `bit` module. - -- The `math.tgamma` function has been renamed to `math.gamma` to conform with - Python's naming. - -- The implementation of the following functions have been moved from the `math` - module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, - `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the - `math` module. - -- `InlinedString` has been renamed to `InlineString` to be consistent with other - types. - -- The `Slice.__len__` function has been removed and `Slice` no longer conforms - to the `Sized` trait. This clarifies the ambiguity of the semantics: the - length of a slice always depends on the length of the object being sliced. - Users that need the existing functionality can use the `Slice.unsafe_indices` - method. This makes it explicit that this implementation does not check if the - slice bounds are concrete or within any given object's length. - -- Implicit conversion to `String` is now removed for builtin classes/types. - One should use `str(...)` explicitly to convert to `String`. - -- `math.gcd` now works on negative inputs, and like Python's implementation, - accepts a variadic list of integers. New overloads for a `List` or `Span`of - integers are also added. - ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) - ### ❌ Removed -- The `@unroll` decorator has been deprecated and removed. The decorator was - supposed to guarantee that a decorated loop would be unrolled, or else the - compiler would error. In practice, this guarantee was eroded over time, as - a compiler-based approach cannot be as robust as the Mojo parameter system. - In addition, the `@unroll` decorator did not make the loop induction variables - parameter values, limiting its usefulness. Please see `@parameter for` for a - replacement! - -- The method `object.print()` has been removed. Since now, `object` has the - `Stringable` trait, you can use `print(my_object)` instead. - -- The following functions have been removed from the math module: - - `clamp`; use the new `SIMD.clamp` method instead. - - `round_half_down` and `round_half_up`; these can be trivially implemented - using the `ceil` and `floor` functions. - - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, - `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and - `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, - `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, - respectively. - - `identity` and `reciprocal`; users can implement these trivially. - - `select`; in favor of using `SIMD.select` directly. - - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` - with `1`. - - `roundeven`; the new `SIMD.roundeven` method now provides the identical - functionality. - - `div_ceil`; use the new `ceildiv` function. - - `rotate_left` and `rotate_right`; the same functionality is available in the - builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the - `bit.rotate_bits_{left,right}` methods for `Int`. - - an overload of `math.pow` taking an integer parameter exponent. - - `align_down_residual`; it can be trivially implemented using `align_down`. - - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and - `SIMD.reduce_or` directly. - - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. - - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. - -- The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's - method is no longer available. This method was limited to degree 10 or less, - underutilized, and its performance unclear. In the future, this might be - reintroduced with an improved implementation if needed, when better - performance benchmarking infrastructure is available. The default behavior of - `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). - -- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The - same functionality is available in the builtin `SIMD.select` and - `SIMD.__and__` methods, respectively. - -- The `math.limit` module has been removed. The same functionality is available - as follows: - - `math.limit.inf`: use `utils.numerics.max_or_inf` - - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` - - `math.limit.max_finite`: use `utils.numerics.max_finite` - - `math.limit.min_finite`: use `utils.numerics.min_finite` - -- The `tensor.random` module has been removed. The same functionality is now - accessible via the `Tensor.rand` and `Tensor.randn` static methods. - -- The builtin `SIMD` struct no longer conforms to `Indexer`; users must - explicitly cast `Scalar` values using `int`. - ### 🛠️ Fixed - -- [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential - variant crashing the compiler. -- [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on - simple trait definitions. -- [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using - `//` on `FloatLiteral` in alias expression. -- Made several improvements to dictionary performance. Dicts with integer keys - are most heavily affected, but large dicts and dicts with large values - will also see large improvements. -- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` - to include calling location. From c061b74414ab9ee229e5a6b237c378f89e40635f Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 31 May 2024 17:05:22 -0700 Subject: [PATCH 0816/2019] Fix mojo released changelog Revert changes that didn't make it into the cut MODULAR_ORIG_COMMIT_REV_ID: a0ff478449eef49528560cb0e032ba4117e3a151 --- docs/changelog-released.md | 17 ++++------------- docs/changelog.md | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 0ea8251ef1..783135293e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -31,10 +31,6 @@ modular update mojo ### ⭐️ New -- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. -([PR #2701](https://github.com/modularml/mojo/pull/2701) -by [@jayzhan211](https://github.com/jayzhan211)) - - Add a `sort` function for list of `ComparableCollectionElement`s. [PR #2609](https://github.com/modularml/mojo/pull/2609) by [@mzaks](https://github.com/mzaks) @@ -497,7 +493,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### 🦋 Changed -- `Coroutine` now requires a lifetime set parameter. This parameter is set +- `Coroutine` now requires a lifetime parameter. This parameter is set automatically by the parser when calling an async function. It contains the lifetimes of all the arguments and any lifetime accesses by the arguments. This ensures that argument captures by async functions keep the arguments @@ -555,9 +551,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - Continued transition to `UnsafePointer` and unsigned byte type for strings: - - Rename `String._as_ptr()` to `String.unsafe_ptr()` - - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `DTypePointer[DType.int8]`) + - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) + - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was + `DTypePointer`) - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`). - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now @@ -568,11 +564,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has the same behavior. -- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` - for convenient interoperability with C APIs. - -- Added `C_char` type alias in `sys.ffi`. - - Added `String.isspace()` method conformant with Python's universal separators. - Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), diff --git a/docs/changelog.md b/docs/changelog.md index e0d605860b..98426ad668 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,8 +16,22 @@ what we publish. ### ⭐️ New +- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. +([PR #2701](https://github.com/modularml/mojo/pull/2701) +by [@jayzhan211](https://github.com/jayzhan211)) + +- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` + for convenient interoperability with C APIs. + +- Added `C_char` type alias in `sys.ffi`. + ### 🦋 Changed +- Continued transition to `UnsafePointer` and unsigned byte type for strings: + - Rename `String._as_ptr()` to `String.unsafe_ptr()` + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `DTypePointer[DType.int8]`) + ### ❌ Removed ### 🛠️ Fixed From 574ade1d216ac2ac5e30713754cb2f0a52d01074 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 31 May 2024 19:33:19 -0500 Subject: [PATCH 0817/2019] [stdlib] docs: Fix tangled changelog for String APIs These APIs underwent a lot of churn this release cycle :sweat_smile: MODULAR_ORIG_COMMIT_REV_ID: 5b7b44d5c21cc14e2a77850da3ece865e584248a --- docs/changelog-released.md | 10 +++++----- docs/changelog.md | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 783135293e..57bba4f705 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -551,11 +551,11 @@ modular update mojo Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - Continued transition to `UnsafePointer` and unsigned byte type for strings: - - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) - - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was - `DTypePointer`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was - `DTypePointer`). + - Rename `String._as_ptr()` to `String.unsafe_ptr()`, and change return type + to `UnsafePointer` (was `DTypePointer`). + - Rename `StringLiteral.data()` to `StringLiteral.unsafe_ptr()`, and change + return type to `UnsafePointer` (was `DTypePointer`). + - Added `unsafe_uint8_ptr()` methods to `String` and `StringLiteral`. - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) diff --git a/docs/changelog.md b/docs/changelog.md index 98426ad668..abc43ab06b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -28,10 +28,12 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### 🦋 Changed - Continued transition to `UnsafePointer` and unsigned byte type for strings: - - Rename `String._as_ptr()` to `String.unsafe_ptr()` - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `DTypePointer[DType.int8]`) + (was `UnsafePointer[Int8]`) ### ❌ Removed +- Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the + same thing. + ### 🛠️ Fixed From e183e0a90558ba483059ddd03b3e64985f10d07a Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 31 May 2024 19:35:40 -0500 Subject: [PATCH 0818/2019] [stdlib] docs: Changelog `as_string_slice()` added in 24.4 MODULAR_ORIG_COMMIT_REV_ID: 3cf1823181458c7b8a9b9aca1228937062fce0a8 --- docs/changelog-released.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 57bba4f705..03419b687f 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -316,6 +316,7 @@ modular update mojo `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it points to. + - Add new `as_string_slice()` method to `String` and `StringLiteral`. - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. - Changed `Formatter.write_str()` to take a safe `StringSlice`. From 33e49fcc9ffe52a9a117e62c2fdc04942005d16b Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 31 May 2024 20:28:37 -0500 Subject: [PATCH 0819/2019] [stdlib] feature: Require `ExplicitlyCopyable` elements in `InlineArray` and `InlineList` This is an initial step towards making the standard library collection types and their element types _not_ `Copyable`, which can lead to unintended and expensive copies. * Add `trait CollectionElementNew(ExplicitlyCopyable, Movable)` as a temporary alternative to `CollectionElement`. Once most of the standard library has been converted to use `CollectionElementNew`, the current `CollectionElement` will be removed and `CollectionElementNew` will become the new `CollectionElement`. * Make `ExplicitlyCopyable.__init__` require named argument to avoid implicit conversion ambiguities. * Change the following collections to require `CollectionElementNew` (and consequently `ExplicitlyCopyable`) instead of `CollectionElement`: - `InlineArray` - `InlineList` * Add explicit copy initializers to: - `Int` - `SIMD` - `String` - `InlineArray` - `Variant` - `LayoutTensor` * Add `UnsafePointer.initialize_pointee_explicit_copy()` using conditional conformance trick. * Change `Span.__init__(InlineArray)` to use conditional conformance trick for `CollectionElementNew` requirement. * Revert to using `StaticTuple` in `InlinedFixedVector` - `AnyTrivialRegType`'s do not automatically conform to `ExplicitlyCopyable` MODULAR_ORIG_COMMIT_REV_ID: 37e15fea916e222a33ac5e3a3968253f20d9cf14 --- docs/changelog.md | 6 +++ stdlib/src/builtin/int.mojo | 12 +++++ stdlib/src/builtin/simd.mojo | 10 ++++ stdlib/src/builtin/string.mojo | 8 +++ stdlib/src/builtin/value.mojo | 17 +++++- stdlib/src/collections/inline_list.mojo | 10 ++-- stdlib/src/collections/vector.mojo | 6 +-- stdlib/src/memory/unsafe_pointer.mojo | 35 ++++++++++++- stdlib/src/utils/span.mojo | 13 ++++- stdlib/src/utils/static_tuple.mojo | 52 ++++++++++++++++--- stdlib/src/utils/variant.mojo | 2 +- stdlib/test/builtin/test_math.mojo | 2 +- stdlib/test/collections/test_inline_list.mojo | 11 +++- stdlib/test/memory/test_unsafepointer.mojo | 16 +++++- stdlib/test/test_utils/__init__.mojo | 2 +- stdlib/test/test_utils/types.mojo | 31 ++++++++++- stdlib/test/utils/test_span.mojo | 8 +-- 17 files changed, 210 insertions(+), 31 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index abc43ab06b..cf5bb5b267 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,12 @@ what we publish. ### ⭐️ New +- Added new `ExplicitlyCopyable` trait, to mark types that can be copied + explicitly, but which might not be implicitly copyable. + + This supports work to transition the standard library collection types away + from implicit copyability, which can lead to unintended expensive copies. + - `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. ([PR #2701](https://github.com/modularml/mojo/pull/2701) by [@jayzhan211](https://github.com/jayzhan211)) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index bfa1b088ad..a8044c3bc9 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -268,11 +268,23 @@ struct Int( alias MIN = int(Scalar[DType.index].MIN) """Returns the minimum value of type.""" + # ===------------------------------------------------------------------=== # + # Life cycle methods + # ===------------------------------------------------------------------=== # + @always_inline("nodebug") fn __init__(inout self): """Default constructor that produces zero.""" self.value = __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + self = other + @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.index): """Construct Int from the given index value. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 0457bd3a74..24f63e6f95 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -136,6 +136,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Ceilable, CeilDivable, CollectionElement, + CollectionElementNew, Floorable, Hashable, Intable, @@ -206,6 +207,15 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ](casted) self.value = vec + @always_inline("nodebug") + fn __init__(inout self, *, other: SIMD[type, size]): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + self.__copyinit__(other) + @always_inline("nodebug") fn __init__(inout self, value: Int): """Initializes the SIMD vector with an integer. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 7eb3525f13..a4593ae7d9 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -726,6 +726,14 @@ struct String( """Construct an uninitialized string.""" self._buffer = Self._buffer_type() + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + self.__copyinit__(other) + @always_inline fn __init__(inout self, str: StringRef): """Construct a string from a StringRef object. diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 03137b5a64..f230694289 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -141,8 +141,12 @@ trait ExplicitlyCopyable: ``` """ - fn __init__(inout self, other: Self): - """Construct a deep copy of the provided value. + # Note: + # `other` is a required named argument for the time being to minimize + # implicit conversion overload ambiguity errors, particularly + # with SIMD and Int. + fn __init__(inout self, *, other: Self): + """Explicitly construct a deep copy of the provided value. Args: other: The value to copy. @@ -196,6 +200,15 @@ trait CollectionElement(Copyable, Movable): pass +trait CollectionElementNew(ExplicitlyCopyable, Movable): + """A temporary explicitly-copyable alternative to `CollectionElement`. + + This trait will eventually replace `CollectionElement`. + """ + + pass + + trait StringableCollectionElement(CollectionElement, Stringable): """The StringableCollectionElement trait denotes a trait composition of the `CollectionElement` and `Stringable` traits. diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 9e7df8caa0..0f004c370a 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -28,7 +28,7 @@ from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# @value struct _InlineListIter[ - T: CollectionElement, + T: CollectionElementNew, capacity: Int, list_mutability: Bool, list_lifetime: AnyLifetime[list_mutability].type, @@ -72,7 +72,7 @@ struct _InlineListIter[ # TODO: Provide a smarter default for the capacity. -struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): +struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """A list allocated on the stack with a maximum size known at compile time. It is backed by an `InlineArray` and an `Int` to represent the size. @@ -109,7 +109,7 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): debug_assert(len(values) <= capacity, "List is full.") self = Self() for value in values: - self.append(value[]) + self.append(ElementType(other=value[])) @always_inline fn __len__(self) -> Int: @@ -176,7 +176,7 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): ``` Parameters: C: The type of the elements in the list. Must implement the - traits `EqualityComparable` and `CollectionElement`. + traits `EqualityComparable` and `CollectionElementNew`. Args: value: The value to find. @@ -203,7 +203,7 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): ``` Parameters: C: The type of the elements in the list. Must implement the - traits `EqualityComparable` and `CollectionElement`. + traits `EqualityComparable` and `CollectionElementNew`. Args: value: The value to count. diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 827d61c0d8..59277ccddd 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -20,7 +20,7 @@ from collections.vector import InlinedFixedVector """ from memory import UnsafePointer, Reference -from utils import InlineArray +from utils import StaticTuple # ===----------------------------------------------------------------------===# # _VecIter @@ -97,7 +97,7 @@ struct InlinedFixedVector[ """ alias static_size: Int = size - alias static_data_type = InlineArray[type, size] + alias static_data_type = StaticTuple[type, size] var static_data: Self.static_data_type """The underlying static storage, used for small vectors.""" var dynamic_data: UnsafePointer[type] @@ -116,7 +116,7 @@ struct InlinedFixedVector[ Args: capacity: The requested maximum capacity of the vector. """ - self.static_data = Self.static_data_type(unsafe_uninitialized=True) + self.static_data = Self.static_data_type() # Undef initialization self.dynamic_data = UnsafePointer[type]() if capacity > Self.static_size: self.dynamic_data = UnsafePointer[type].alloc(capacity - size) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index d14a06a4db..93a6c10ee2 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -20,7 +20,7 @@ from memory import UnsafePointer """ from sys import alignof, sizeof -from sys.intrinsics import _mlirtype_is_eq +from sys.intrinsics import _mlirtype_is_eq, _type_is_eq from memory.memory import _free, _malloc @@ -352,6 +352,39 @@ struct UnsafePointer[ # Methods # ===-------------------------------------------------------------------===# + @always_inline + fn initialize_pointee_explicit_copy[ + T2: ExplicitlyCopyable + ](inout self: UnsafePointer[T, address_space], value: T2): + """Emplace a copy of `value` into this pointer location. + + The pointer memory location is assumed to contain uninitialized data, + and consequently the current contents of this pointer are not destructed + before writing `value`. Similarly, ownership of `value` is logically + transferred into the pointer location. + + When compared to `initialize_pointee_move`, this avoids an extra move on + the callee side when the value must be copied. + + Parameters: + T2: The type the pointer points to, which must be + `ExplicitlyCopyable`. + + Args: + value: The value to emplace. + """ + + constrained[ + address_space == AddressSpace.GENERIC, + "can not initialize pointer in non-GENERIC address space", + ]() + + constrained[_type_is_eq[T, T2](), "pointee type is not self.T"]() + + var ptr = self.bitcast[T2, address_space = AddressSpace.GENERIC]() + + __get_address_as_uninit_lvalue(ptr.address) = T2(other=value) + @always_inline fn free(self): """Free the memory referenced by the pointer.""" diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 07d7cfaa91..c07abbde4e 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -22,6 +22,8 @@ from utils import Span from . import InlineArray +from sys.intrinsics import _type_is_eq + @value struct _SpanIter[ @@ -112,16 +114,23 @@ struct Span[ @always_inline fn __init__[ - size: Int - ](inout self, array: Reference[InlineArray[T, size], is_mutable, lifetime]): + T2: CollectionElementNew, size: Int + ]( + inout self, + array: Reference[InlineArray[T2, size], is_mutable, lifetime], + ): """Construct a Span from an InlineArray. Parameters: + T2: The type of the elements in the span. size: The size of the InlineArray. Args: array: The array to which the span refers. """ + + constrained[_type_is_eq[T, T2](), "array element is not Span.T"]() + self._data = UnsafePointer(array).bitcast[T]() self._len = size diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 7d938a712a..29d0dff6dd 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -247,7 +247,10 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): @value -struct InlineArray[ElementType: CollectionElement, size: Int](Sized): +struct InlineArray[ + ElementType: CollectionElementNew, + size: Int, +](Sized, Movable, Copyable, ExplicitlyCopyable): """A fixed-size sequence of size homogeneous elements where size is a constant expression. Parameters: @@ -301,6 +304,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): unsafe_uninitialized: A boolean to indicate if the array should be initialized. Always set to `True` (it's not actually used inside the constructor). """ + _static_tuple_construction_checks[size]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() @always_inline @@ -315,27 +319,61 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): @parameter for i in range(size): - var ptr = self._get_reference_unsafe(i) - initialize_pointee_copy(UnsafePointer[Self.ElementType](ptr), fill) + var ptr = UnsafePointer(self._get_reference_unsafe(i)) + ptr.initialize_pointee_explicit_copy(fill) @always_inline - fn __init__(inout self, *elems: Self.ElementType): + fn __init__(inout self, owned *elems: Self.ElementType): """Constructs an array given a set of arguments. Args: elems: The element types. """ - debug_assert(len(elems) == size, "Elements must be of length size") + + self = Self(storage=elems^) + + @always_inline("nodebug") + fn __init__( + inout self, + *, + owned storage: VariadicListMem[Self.ElementType, _, _], + ): + """Construct an array from a low-level internal representation. + + Args: + storage: The variadic list storage to construct from. + """ + + debug_assert(len(storage) == size, "Elements must be of length size") _static_tuple_construction_checks[size]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + # Move each element into the array storage. @parameter for i in range(size): var eltref = self._get_reference_unsafe(i) - initialize_pointee_move( - UnsafePointer[Self.ElementType](eltref), elems[i] + move_pointee( + dst=UnsafePointer[Self.ElementType](eltref), + src=UnsafePointer(storage[i]), ) + # Mark the elements as already destroyed. + storage._is_owned = False + + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + + self = Self(unsafe_uninitialized=True) + + for idx in range(size): + var ptr = self.unsafe_ptr() + idx + + ptr.initialize_pointee_explicit_copy(other[idx]) + # ===------------------------------------------------------------------===# # Operator dunders # ===------------------------------------------------------------------===# diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index a32f290028..76e1409443 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -174,7 +174,7 @@ struct Variant[*Ts: CollectionElement]( self._get_state()[] = Self._check[T]() initialize_pointee_move(self._get_ptr[T](), value^) - fn __init__(inout self, other: Self): + fn __init__(inout self, *, other: Self): """Explicitly creates a deep copy of an existing variant. Args: diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 13c9cecb03..ee20cb489d 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -93,7 +93,7 @@ def test_pow(): def test_gcd(): var l = List(2, 4, 6, 8, 16) var il = InlineArray[Int, 5](4, 16, 2, 8, 6) - assert_equal(gcd(Span(il)), 2) + assert_equal(gcd(Span[Int](il)), 2) assert_equal(gcd(2, 4, 6, 8, 16), 2) assert_equal(gcd(l), 2) assert_equal(gcd(88, 24), 8) diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index 2179daa1a7..08560d8274 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -58,10 +58,19 @@ def test_append_triggers_a_move(): @value -struct ValueToCountDestructor(CollectionElement): +struct ValueToCountDestructor(CollectionElementNew): var value: Int var destructor_counter: UnsafePointer[List[Int]] + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + self.value = other.value + self.destructor_counter = other.destructor_counter + fn __del__(owned self): self.destructor_counter[].append(self.value) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 82df00a3f5..2c6e597c34 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -14,7 +14,7 @@ from memory import UnsafePointer from memory.unsafe_pointer import move_from_pointee, move_pointee -from test_utils import MoveCounter +from test_utils import MoveCounter, ExplicitCopyOnly from testing import assert_equal, assert_not_equal, assert_true @@ -81,6 +81,19 @@ def test_unsafepointer_move_pointee_move_count(): assert_equal(2, ptr_2[].move_count) +def test_unsafepointer_initialize_pointee_explicit_copy(): + var ptr = UnsafePointer[ExplicitCopyOnly].alloc(1) + + var orig = ExplicitCopyOnly(5) + assert_equal(orig.copy_count, 0) + + # Test initialize pointee from `ExplicitlyCopyable` type + ptr.initialize_pointee_explicit_copy(orig) + + assert_equal(ptr[].value, 5) + assert_equal(ptr[].copy_count, 1) + + def test_refitem(): var ptr = UnsafePointer[Int].alloc(1) ptr[0] = 0 @@ -189,6 +202,7 @@ def main(): test_unsafepointer_of_move_only_type() test_unsafepointer_move_pointee_move_count() + test_unsafepointer_initialize_pointee_explicit_copy() test_bitcast() test_unsafepointer_string() diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index ca2d56a1d5..e9d4cc2c76 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # from .test_utils import libm_call -from .types import CopyCounter, MoveCounter, MoveOnly +from .types import CopyCounter, MoveCounter, MoveOnly, ExplicitCopyOnly diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 6d9da396d9..168e8944c0 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -39,6 +39,19 @@ struct MoveOnly[T: Movable](Movable): self.data = other.data^ +struct ExplicitCopyOnly(ExplicitlyCopyable): + var value: Int + var copy_count: Int + + fn __init__(inout self, value: Int): + self.value = value + self.copy_count = 0 + + fn __init__(inout self, *, other: Self): + self.value = other.value + self.copy_count = other.copy_count + 1 + + struct CopyCounter(CollectionElement): """Counts the number of copies performed on a value.""" @@ -54,7 +67,10 @@ struct CopyCounter(CollectionElement): self.copy_count = existing.copy_count + 1 -struct MoveCounter[T: CollectionElement](CollectionElement): +struct MoveCounter[T: CollectionElementNew]( + CollectionElement, + CollectionElementNew, +): """Counts the number of moves performed on a value.""" var value: T @@ -66,6 +82,17 @@ struct MoveCounter[T: CollectionElement](CollectionElement): self.value = value^ self.move_count = 0 + # TODO: This type should not be ExplicitlyCopyable, but has to be to satisfy + # CollectionElementNew at the moment. + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + self.value = T(other=other.value) + self.move_count = other.move_count + fn __moveinit__(inout self, owned existing: Self): self.value = existing.value^ self.move_count = existing.move_count + 1 @@ -74,5 +101,5 @@ struct MoveCounter[T: CollectionElement](CollectionElement): # CollectionElement at the moment. fn __copyinit__(inout self, existing: Self): # print("ERROR: _MoveCounter copy constructor called unexpectedly!") - self.value = existing.value + self.value = T(other=existing.value) self.move_count = existing.move_count diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 470a07ae4d..28e034b626 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -19,7 +19,7 @@ from testing import assert_equal def test_span_list_int(): var l = List[Int](1, 2, 3, 4, 5, 6, 7) - var s = Span(l) + var s = Span(list=l) assert_equal(len(s), len(l)) for i in range(len(s)): assert_equal(l[i], s[i]) @@ -66,7 +66,7 @@ def test_span_list_str(): def test_span_array_int(): var l = InlineArray[Int, 7](1, 2, 3, 4, 5, 6, 7) - var s = Span(l) + var s = Span[Int](array=l) assert_equal(len(s), len(l)) for i in range(len(s)): assert_equal(l[i], s[i]) @@ -89,7 +89,7 @@ def test_span_array_int(): def test_span_array_str(): var l = InlineArray[String, 7]("a", "b", "c", "d", "e", "f", "g") - var s = Span(l) + var s = Span[String](array=l) assert_equal(len(s), len(l)) for i in range(len(s)): assert_equal(l[i], s[i]) @@ -112,7 +112,7 @@ def test_span_array_str(): def test_indexing(): var l = InlineArray[Int, 7](1, 2, 3, 4, 5, 6, 7) - var s = Span(l) + var s = Span[Int](array=l) assert_equal(s[True], 2) assert_equal(s[int(0)], 1) assert_equal(s[3], 4) From 67514d21225a335d2ada6e0d59ab8e386b36048e Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sat, 1 Jun 2024 03:35:23 -0700 Subject: [PATCH 0820/2019] [stdlib] Removing `UnsafePointer.offset(offset:Int)` MODULAR_ORIG_COMMIT_REV_ID: 57f87920f34d1785ba4e2ef2679a9302c72508cb --- docs/changelog.md | 2 ++ stdlib/src/memory/unsafe_pointer.mojo | 14 +------------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cf5bb5b267..d1abc6a6b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -42,4 +42,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. +- Removed `UnsafePointer.offset(offset:Int)`. + ### 🛠️ Fixed diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 93a6c10ee2..c133630115 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -215,7 +215,7 @@ struct UnsafePointer[ Returns: An offset pointer. """ - return self.offset(offset) + return Self(address=int(self) + offset * sizeof[T]()) @always_inline fn __sub__(self, offset: Int) -> Self: @@ -410,18 +410,6 @@ struct UnsafePointer[ _type = UnsafePointer[new_type, address_space]._mlir_type, ](self.address) - @always_inline - fn offset(self, offset: Int) -> Self: - """Return a pointer at an offset from the current one. - - Args: - offset: The offset index. - - Returns: - An offset pointer. - """ - return Self(address=int(self) + offset * sizeof[T]()) - # ===----------------------------------------------------------------------=== # # UnsafePointer extensions From 91af07c87f33012c658d6f8f8c1b6b69b36ea572 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 1 Jun 2024 17:09:16 -0700 Subject: [PATCH 0821/2019] [mojo-lang] improve 'ref' arguments and adopt in stdlib. This improves the implementation of 'ref' arguments and adopts in a few places in the standard library. There is more work to do, but this is a step forward. MODULAR_ORIG_COMMIT_REV_ID: 59888e1b2fcd41b2847144ad7789f830884160fb --- stdlib/src/builtin/builtin_list.mojo | 4 ++- stdlib/src/builtin/reversed.mojo | 40 +++++++++++++--------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 77904f7e2a..4a8c27b6f8 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -382,7 +382,9 @@ struct VariadicListMem[ A low-level pointer to the element on the list corresponding to the given index. """ - return Reference(__mlir_op.`pop.variadic.get`(self.value, idx.value))[] + return __get_litref_as_mvalue( + __mlir_op.`pop.variadic.get`(self.value, idx.value) + ) fn __iter__( self, diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index b8b07ae708..b8f10e5e2b 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -77,18 +77,18 @@ fn reversed[T: ReversibleRange](value: T) -> _StridedRange: fn reversed[ - T: CollectionElement -]( - value: Reference[List[T], _, _], -) -> _ListIter[ - T, value.is_mutable, value.lifetime, False -]: + T: CollectionElement, //, + mutability: Bool, + self_life: AnyLifetime[mutability].type, +](ref [self_life]value: List[T]) -> _ListIter[T, mutability, self_life, False]: """Get a reversed iterator of the input list. **Note**: iterators are currently non-raising. Parameters: T: The type of the elements in the list. + mutability: Mutability of the collection. + self_life: Lifetime of the collection. Args: value: The list to get the reversed iterator of. @@ -96,16 +96,18 @@ fn reversed[ Returns: The reversed iterator of the list. """ - return value[].__reversed__() + return value.__reversed__() fn reversed[ K: KeyElement, V: CollectionElement, + mutability: Bool, + self_life: AnyLifetime[mutability].type, ]( - value: Reference[Dict[K, V], _, _], + ref [self_life]value: Dict[K, V], ) -> _DictKeyIter[ - K, V, value.is_mutable, value.lifetime, False + K, V, mutability, self_life, False ]: """Get a reversed iterator of the input dict. @@ -114,6 +116,8 @@ fn reversed[ Parameters: K: The type of the keys in the dict. V: The type of the values in the dict. + mutability: Mutability of the collection. + self_life: Lifetime of the collection. Args: value: The dict to get the reversed iterator of. @@ -121,7 +125,7 @@ fn reversed[ Returns: The reversed iterator of the dict keys. """ - return value[].__reversed__() + return value.__reversed__() fn reversed[ @@ -132,11 +136,7 @@ fn reversed[ dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, ]( - value: Reference[ - _DictValueIter[K, V, dict_mutability, dict_lifetime], - mutability, - self_life, - ]._mlir_type, + ref [self_life]value: _DictValueIter[K, V, dict_mutability, dict_lifetime] ) -> _DictValueIter[K, V, dict_mutability, dict_lifetime, False]: """Get a reversed iterator of the input dict values. @@ -156,7 +156,7 @@ fn reversed[ Returns: The reversed iterator of the dict values. """ - return Reference(value)[].__reversed__[mutability, self_life]() + return value.__reversed__[mutability, self_life]() fn reversed[ @@ -167,11 +167,7 @@ fn reversed[ dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, ]( - value: Reference[ - _DictEntryIter[K, V, dict_mutability, dict_lifetime], - mutability, - self_life, - ]._mlir_type, + ref [self_life]value: _DictEntryIter[K, V, dict_mutability, dict_lifetime] ) -> _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]: """Get a reversed iterator of the input dict items. @@ -191,7 +187,7 @@ fn reversed[ Returns: The reversed iterator of the dict items. """ - var src = Reference(value)[].src + var src = value.src return _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( src[]._reserved - 1, 0, src ) From e5bffbedc6c868482200be149df0b3bce0dc3ca3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 1 Jun 2024 17:50:52 -0700 Subject: [PATCH 0822/2019] [mojo-stdlib] Use inferred params to simplify a bunch of types. This simplifies some iterator types that a future patch won't want to specify the mutability for. MODULAR_ORIG_COMMIT_REV_ID: 2ccfbc94ada69d5db6b3d79848573cd7ff47edd4 --- stdlib/src/builtin/reversed.mojo | 22 +++++++---------- stdlib/src/collections/dict.mojo | 42 +++++++++++++++----------------- stdlib/src/collections/list.mojo | 8 +++--- stdlib/src/collections/set.mojo | 2 +- 4 files changed, 33 insertions(+), 41 deletions(-) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index b8f10e5e2b..22708459f5 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -80,7 +80,7 @@ fn reversed[ T: CollectionElement, //, mutability: Bool, self_life: AnyLifetime[mutability].type, -](ref [self_life]value: List[T]) -> _ListIter[T, mutability, self_life, False]: +](ref [self_life]value: List[T]) -> _ListIter[T, self_life, False]: """Get a reversed iterator of the input list. **Note**: iterators are currently non-raising. @@ -104,11 +104,7 @@ fn reversed[ V: CollectionElement, mutability: Bool, self_life: AnyLifetime[mutability].type, -]( - ref [self_life]value: Dict[K, V], -) -> _DictKeyIter[ - K, V, mutability, self_life, False -]: +](ref [self_life]value: Dict[K, V],) -> _DictKeyIter[K, V, self_life, False]: """Get a reversed iterator of the input dict. **Note**: iterators are currently non-raising. @@ -135,9 +131,9 @@ fn reversed[ V: CollectionElement, dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, -]( - ref [self_life]value: _DictValueIter[K, V, dict_mutability, dict_lifetime] -) -> _DictValueIter[K, V, dict_mutability, dict_lifetime, False]: +](ref [self_life]value: _DictValueIter[K, V, dict_lifetime]) -> _DictValueIter[ + K, V, dict_lifetime, False +]: """Get a reversed iterator of the input dict values. **Note**: iterators are currently non-raising. @@ -166,9 +162,9 @@ fn reversed[ V: CollectionElement, dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, -]( - ref [self_life]value: _DictEntryIter[K, V, dict_mutability, dict_lifetime] -) -> _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]: +](ref [self_life]value: _DictEntryIter[K, V, dict_lifetime]) -> _DictEntryIter[ + K, V, dict_lifetime, False +]: """Get a reversed iterator of the input dict items. **Note**: iterators are currently non-raising. @@ -188,6 +184,6 @@ fn reversed[ The reversed iterator of the dict items. """ var src = value.src - return _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( + return _DictEntryIter[K, V, dict_lifetime, False]( src[]._reserved - 1, 0, src ) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 0c46e407dd..dd8825f07c 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -54,18 +54,18 @@ trait RepresentableKeyElement(KeyElement, Representable): @value struct _DictEntryIter[ + dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable DictEntry references. Parameters: + dict_mutability: Whether the reference to the dictionary is mutable. K: The key type of the elements in the dictionary. V: The value type of the elements in the dictionary. - dict_mutability: Whether the reference to the dictionary is mutable. dict_lifetime: The lifetime of the List forward: The iteration direction. `False` is backwards. """ @@ -109,18 +109,18 @@ struct _DictEntryIter[ @value struct _DictKeyIter[ + dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable Dict key references. Parameters: + dict_mutability: Whether the reference to the vector is mutable. K: The key type of the elements in the dictionary. V: The value type of the elements in the dictionary. - dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List forward: The iteration direction. `False` is backwards. """ @@ -130,9 +130,7 @@ struct _DictKeyIter[ ] alias ref_type = Reference[K, False, Self.imm_dict_lifetime] - alias dict_entry_iter = _DictEntryIter[ - K, V, dict_mutability, dict_lifetime, forward - ] + alias dict_entry_iter = _DictEntryIter[K, V, dict_lifetime, forward] var iter: Self.dict_entry_iter @@ -148,9 +146,9 @@ struct _DictKeyIter[ @value struct _DictValueIter[ + dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, forward: Bool = True, ]: @@ -158,26 +156,26 @@ struct _DictValueIter[ is mutable. Parameters: + dict_mutability: Whether the reference to the vector is mutable. K: The key type of the elements in the dictionary. V: The value type of the elements in the dictionary. - dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List forward: The iteration direction. `False` is backwards. """ alias ref_type = Reference[V, dict_mutability, dict_lifetime] - var iter: _DictEntryIter[K, V, dict_mutability, dict_lifetime, forward] + var iter: _DictEntryIter[K, V, dict_lifetime, forward] fn __iter__(self) -> Self: return self fn __reversed__[ mutability: Bool, self_life: AnyLifetime[mutability].type - ](self) -> _DictValueIter[K, V, dict_mutability, dict_lifetime, False]: + ](self) -> _DictValueIter[K, V, dict_lifetime, False]: var src = self.iter.src return _DictValueIter( - _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( + _DictEntryIter[K, V, dict_lifetime, False]( src[]._reserved - 1, 0, src ) ) @@ -576,7 +574,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn __iter__( self: Reference[Self, _, _], - ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: + ) -> _DictKeyIter[K, V, self.lifetime]: """Iterate over the dict's keys as immutable references. Returns: @@ -586,7 +584,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn __reversed__( self: Reference[Self, _, _] - ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime, False]: + ) -> _DictKeyIter[K, V, self.lifetime, False]: """Iterate backwards over the dict keys, returning immutable references. Returns: @@ -818,9 +816,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( raise "KeyError: popitem(): dictionary is empty" - fn keys( - self: Reference[Self, _, _] - ) -> _DictKeyIter[K, V, self.is_mutable, self.lifetime]: + fn keys(self: Reference[Self, _, _]) -> _DictKeyIter[K, V, self.lifetime]: """Iterate over the dict's keys as immutable references. Returns: @@ -830,7 +826,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn values( self: Reference[Self, _, _] - ) -> _DictValueIter[K, V, self.is_mutable, self.lifetime]: + ) -> _DictValueIter[K, V, self.lifetime]: """Iterate over the dict's values as references. Returns: @@ -840,7 +836,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn items( self: Reference[Self, _, _] - ) -> _DictEntryIter[K, V, self.is_mutable, self.lifetime]: + ) -> _DictEntryIter[K, V, self.lifetime]: """Iterate over the dict's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1111,7 +1107,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn __iter__( self: Reference[Self, _, _] - ) -> _DictKeyIter[Self.key_type, V, self.is_mutable, self.lifetime]: + ) -> _DictKeyIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1123,7 +1119,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn keys( self: Reference[Self, _, _], - ) -> _DictKeyIter[Self.key_type, V, self.is_mutable, self.lifetime]: + ) -> _DictKeyIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1135,7 +1131,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn values( self: Reference[Self, _, _], - ) -> _DictValueIter[Self.key_type, V, self.is_mutable, self.lifetime]: + ) -> _DictValueIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dict's values as references. Returns: @@ -1147,7 +1143,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn items( self: Reference[Self, _, _] - ) -> _DictEntryIter[Self.key_type, V, self.is_mutable, self.lifetime]: + ) -> _DictEntryIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dictionary's entries as immutable references. These can't yet be unpacked like Python dict items, but you can diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 993116a11e..9b9c7aee8c 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -33,16 +33,16 @@ from utils import Span @value struct _ListIter[ + list_mutability: Bool, //, T: CollectionElement, - list_mutability: Bool, list_lifetime: AnyLifetime[list_mutability].type, forward: Bool = True, ]: """Iterator for List. Parameters: - T: The type of the elements in the list. list_mutability: Whether the reference to the list is mutable. + T: The type of the elements in the list. list_lifetime: The lifetime of the List forward: The iteration direction. `False` is backwards. """ @@ -291,7 +291,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn __iter__( self: Reference[Self, _, _], - ) -> _ListIter[T, self.is_mutable, self.lifetime]: + ) -> _ListIter[T, self.lifetime]: """Iterate over elements of the list, returning immutable references. Returns: @@ -301,7 +301,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn __reversed__( self: Reference[Self, _, _] - ) -> _ListIter[T, self.is_mutable, self.lifetime, False]: + ) -> _ListIter[T, self.lifetime, False]: """Iterate backwards over the list, returning immutable references. Returns: diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 58b6837874..2dc79010cd 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -302,7 +302,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): fn __iter__( self: Reference[Self, _, _], - ) -> _DictKeyIter[T, NoneType, self.is_mutable, self.lifetime]: + ) -> _DictKeyIter[T, NoneType, self.lifetime]: """Iterate over elements of the set, returning immutable references. Returns: From 1d0d7aeda6ea20a9c278b5ca9fff9db79573d38e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 1 Jun 2024 18:53:05 -0700 Subject: [PATCH 0823/2019] [mojo-stdlib] Move the 'Reference. is_mutable' parameter to being inferred This massively simplifies all users of the `Reference` type by allowing the `is_mutable` parameter to be inferred, using Mojo's fancy new "inferred only" parameter kind. MODULAR_ORIG_COMMIT_REV_ID: 1a1fce14e0584b5ae8dd29d2d2cb87b916ad50d8 --- docs/changelog.md | 3 ++ stdlib/src/builtin/builtin_list.mojo | 9 ++---- stdlib/src/builtin/string.mojo | 4 +-- stdlib/src/builtin/tuple.mojo | 8 ++--- stdlib/src/collections/dict.mojo | 42 ++++++++++++------------- stdlib/src/collections/inline_list.mojo | 21 ++++++------- stdlib/src/collections/list.mojo | 20 ++++++------ stdlib/src/collections/optional.mojo | 8 ++--- stdlib/src/collections/set.mojo | 2 +- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/memory/reference.mojo | 4 +-- stdlib/src/memory/unsafe.mojo | 8 ++--- stdlib/src/memory/unsafe_pointer.mojo | 6 ++-- stdlib/src/utils/index.mojo | 6 ++-- stdlib/src/utils/inline_string.mojo | 8 ++--- stdlib/src/utils/span.mojo | 9 ++---- stdlib/src/utils/static_tuple.mojo | 13 +++----- stdlib/src/utils/variant.mojo | 10 ++---- 18 files changed, 79 insertions(+), 104 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d1abc6a6b5..28b8fe4b67 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,9 @@ what we publish. ### ⭐️ New +- The `Reference` type (and many iterators) now use "inferred" parameters to + represent the mutability of their lifetime, simplifying the interface. + - Added new `ExplicitlyCopyable` trait, to mark types that can be copied explicitly, but which might not be implicitly copyable. diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 4a8c27b6f8..584f348fea 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -73,7 +73,7 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): Returns: The element at the given index. """ - return rebind[Reference[T, False, __lifetime_of(self)]]( + return rebind[Reference[T, __lifetime_of(self)]]( Reference(self.storage[i]) )[] @@ -190,7 +190,7 @@ struct _VariadicListMemIter[ ] var index: Int - var src: Reference[Self.variadic_list_type, False, list_lifetime] + var src: Reference[Self.variadic_list_type, list_lifetime] fn __next__(inout self) -> Self.variadic_list_type.reference_type: self.index += 1 @@ -251,9 +251,7 @@ struct VariadicListMem[ lifetime: The reference lifetime of the underlying elements. """ - alias reference_type = Reference[ - element_type, Bool {value: elt_is_mutable}, lifetime - ] + alias reference_type = Reference[element_type, lifetime] alias _mlir_ref_type = Self.reference_type._mlir_type alias _mlir_type = __mlir_type[ `!kgen.variadic<`, Self._mlir_ref_type, `, borrow_in_mem>` @@ -597,7 +595,6 @@ struct VariadicPack[ # element_types[index] expression is erased to AnyType for Reference. alias result_ref = Reference[ element_types[index.value], - Bool {value: Self.elt_is_mutable}, Self.lifetime, ] return Reference(rebind[result_ref._mlir_type](ref_elt))[] diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index a4593ae7d9..49a557dd06 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1314,7 +1314,7 @@ struct String( @always_inline fn as_bytes_slice( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> Span[UInt8, self.is_mutable, self.lifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -1333,7 +1333,7 @@ struct String( @always_inline fn as_string_slice( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> StringSlice[self.is_mutable, self.lifetime]: """Returns a string slice of the data owned by this string. diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 04962e37e2..ed941cbb74 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -152,9 +152,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): @always_inline("nodebug") fn __getitem__[ idx: Int - ](self: Reference[Self, _, _]) -> ref [self.lifetime] element_types[ - idx.value - ]: + ](self: Reference[Self, _]) -> ref [self.lifetime] element_types[idx.value]: """Get a reference to an element in the tuple. Parameters: @@ -187,9 +185,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): Returns: The tuple element at the requested index. """ - return rebind[Reference[T, False, __lifetime_of(self)]]( - Reference(self[i]) - )[] + return rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] @always_inline("nodebug") fn __contains__[T: EqualityComparable](self, value: T) -> Bool: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index dd8825f07c..1626eae1e6 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -73,11 +73,11 @@ struct _DictEntryIter[ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[DictEntry[K, V], False, Self.imm_dict_lifetime] + alias ref_type = Reference[DictEntry[K, V], Self.imm_dict_lifetime] var index: Int var seen: Int - var src: Reference[Dict[K, V], dict_mutability, dict_lifetime] + var src: Reference[Dict[K, V], dict_lifetime] fn __iter__(self) -> Self: return self @@ -95,7 +95,9 @@ struct _DictEntryIter[ self.index -= 1 self.seen += 1 - return opt_entry_ref[].value()[] + # Use UnsafePointer to cast from dict_mutability to mutable. + # TODO: This seems wrong. + return UnsafePointer(opt_entry_ref[].value())[] @parameter if forward: @@ -128,7 +130,7 @@ struct _DictKeyIter[ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[K, False, Self.imm_dict_lifetime] + alias ref_type = Reference[K, Self.imm_dict_lifetime] alias dict_entry_iter = _DictEntryIter[K, V, dict_lifetime, forward] @@ -163,7 +165,7 @@ struct _DictValueIter[ forward: The iteration direction. `False` is backwards. """ - alias ref_type = Reference[V, dict_mutability, dict_lifetime] + alias ref_type = Reference[V, dict_lifetime] var iter: _DictEntryIter[K, V, dict_lifetime, forward] @@ -537,8 +539,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MSTDL-452): rename to __getitem__ returning a reference fn __get_ref( - self: Reference[Self, _, _], key: K - ) raises -> Reference[V, self.is_mutable, self.lifetime]: + self: Reference[Self, _], key: K + ) raises -> Reference[V, self.lifetime]: """Retrieve a value out of the dictionary. Args: @@ -573,7 +575,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( return self.find(key).__bool__() fn __iter__( - self: Reference[Self, _, _], + self: Reference[Self, _], ) -> _DictKeyIter[K, V, self.lifetime]: """Iterate over the dict's keys as immutable references. @@ -583,7 +585,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( return _DictKeyIter(_DictEntryIter(0, 0, self)) fn __reversed__( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> _DictKeyIter[K, V, self.lifetime, False]: """Iterate backwards over the dict keys, returning immutable references. @@ -709,8 +711,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MOCO-604): Return Optional[Reference] instead of raising fn _find_ref( - self: Reference[Self, _, _], key: K - ) raises -> Reference[V, self.is_mutable, self.lifetime]: + self: Reference[Self, _], key: K + ) raises -> Reference[V, self.lifetime]: """Find a value in the dictionary by key. Args: @@ -816,7 +818,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( raise "KeyError: popitem(): dictionary is empty" - fn keys(self: Reference[Self, _, _]) -> _DictKeyIter[K, V, self.lifetime]: + fn keys(self: Reference[Self, _]) -> _DictKeyIter[K, V, self.lifetime]: """Iterate over the dict's keys as immutable references. Returns: @@ -824,9 +826,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return Self.__iter__(self) - fn values( - self: Reference[Self, _, _] - ) -> _DictValueIter[K, V, self.lifetime]: + fn values(self: Reference[Self, _]) -> _DictValueIter[K, V, self.lifetime]: """Iterate over the dict's values as references. Returns: @@ -834,9 +834,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return _DictValueIter(_DictEntryIter(0, 0, self)) - fn items( - self: Reference[Self, _, _] - ) -> _DictEntryIter[K, V, self.lifetime]: + fn items(self: Reference[Self, _]) -> _DictEntryIter[K, V, self.lifetime]: """Iterate over the dict's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1106,7 +1104,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return self._dict.pop(key, default^) fn __iter__( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> _DictKeyIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dict's keys as immutable references. @@ -1118,7 +1116,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return _DictKeyIter(_DictEntryIter(0, 0, self[]._dict)) fn keys( - self: Reference[Self, _, _], + self: Reference[Self, _], ) -> _DictKeyIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dict's keys as immutable references. @@ -1130,7 +1128,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return Self.__iter__(self) fn values( - self: Reference[Self, _, _], + self: Reference[Self, _], ) -> _DictValueIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dict's values as references. @@ -1142,7 +1140,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return _DictValueIter(_DictEntryIter(0, 0, self[]._dict)) fn items( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> _DictEntryIter[Self.key_type, V, self.lifetime]: """Iterate over the keyword dictionary's entries as immutable references. diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 0f004c370a..45f374c8de 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -28,18 +28,18 @@ from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# @value struct _InlineListIter[ + list_mutability: Bool, //, T: CollectionElementNew, capacity: Int, - list_mutability: Bool, list_lifetime: AnyLifetime[list_mutability].type, forward: Bool = True, ]: """Iterator for InlineList. Parameters: + list_mutability: Whether the reference to the list is mutable. T: The type of the elements in the list. capacity: The maximum number of elements that the list can hold. - list_mutability: Whether the reference to the list is mutable. list_lifetime: The lifetime of the List forward: The iteration direction. `False` is backwards. """ @@ -47,14 +47,14 @@ struct _InlineListIter[ alias list_type = InlineList[T, capacity] var index: Int - var src: Reference[Self.list_type, list_mutability, list_lifetime] + var src: Reference[Self.list_type, list_lifetime] fn __iter__(self) -> Self: return self fn __next__( inout self, - ) -> Reference[T, list_mutability, list_lifetime]: + ) -> Reference[T, list_lifetime]: @parameter if forward: self.index += 1 @@ -129,7 +129,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __getitem__( - self: Reference[Self, _, _], owned idx: Int + self: Reference[Self, _], owned idx: Int ) -> ref [self.lifetime] Self.ElementType: """Get a `Reference` to the element at the given index. @@ -155,8 +155,8 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): destroy_pointee(UnsafePointer(self._array[i])) fn __iter__( - self: Reference[Self, _, _], - ) -> _InlineListIter[ElementType, capacity, self.is_mutable, self.lifetime]: + self: Reference[Self, _], + ) -> _InlineListIter[ElementType, capacity, self.lifetime]: """Iterate over elements of the list, returning immutable references. Returns: @@ -189,7 +189,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): _type_is_eq[ElementType, C](), "value type is not self.ElementType" ]() for i in self: - if value == rebind[Reference[C, False, __lifetime_of(self)]](i)[]: + if value == rebind[Reference[C, __lifetime_of(self)]](i)[]: return True return False @@ -217,10 +217,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): var count = 0 for elem in self: - if ( - value - == rebind[Reference[C, False, __lifetime_of(self)]](elem)[] - ): + if value == rebind[Reference[C, __lifetime_of(self)]](elem)[]: count += 1 return count diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9b9c7aee8c..ee3d8d2a85 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -50,14 +50,14 @@ struct _ListIter[ alias list_type = List[T] var index: Int - var src: Reference[Self.list_type, list_mutability, list_lifetime] + var src: Reference[Self.list_type, list_lifetime] fn __iter__(self) -> Self: return self fn __next__( inout self, - ) -> Reference[T, list_mutability, list_lifetime]: + ) -> Reference[T, list_lifetime]: @parameter if forward: self.index += 1 @@ -236,7 +236,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): constrained[_type_is_eq[T, T2](), "value type is not self.T"]() for i in self: - if rebind[Reference[T2, False, __lifetime_of(self)]](i)[] == value: + if rebind[Reference[T2, __lifetime_of(self)]](i)[] == value: return True return False @@ -290,7 +290,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.extend(other^) fn __iter__( - self: Reference[Self, _, _], + self: Reference[Self, _], ) -> _ListIter[T, self.lifetime]: """Iterate over elements of the list, returning immutable references. @@ -300,7 +300,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return _ListIter(0, self) fn __reversed__( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> _ListIter[T, self.lifetime, False]: """Iterate backwards over the list, returning immutable references. @@ -636,7 +636,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn index[ C: ComparableCollectionElement ]( - self: Reference[List[C]], + self: Reference[List[C], _], value: C, start: Int = 0, stop: Optional[Int] = None, @@ -774,8 +774,8 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # TODO(30737): Replace __getitem__ with this, but lots of places use it fn __get_ref( - self: Reference[Self, _, _], i: Int - ) -> Reference[T, self.is_mutable, self.lifetime]: + self: Reference[Self, _], i: Int + ) -> Reference[T, self.lifetime]: """Gets a reference to the list element at the given index. Args: @@ -792,8 +792,8 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): @always_inline fn unsafe_get( - self: Reference[Self, _, _], idx: Int - ) -> Reference[Self.T, self.is_mutable, self.lifetime]: + self: Reference[Self, _], idx: Int + ) -> Reference[Self.T, self.lifetime]: """Get a reference to an element of self without checking index bounds. Users should consider using `__getitem__` instead of this method as it diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 921d7f8c5b..52b947d0c6 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -159,9 +159,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): # ===-------------------------------------------------------------------===# @always_inline - fn value( - self: Reference[Self, _, _] - ) -> Reference[T, self.is_mutable, self.lifetime]: + fn value(self: Reference[Self, _]) -> Reference[T, self.lifetime]: """Retrieve a reference to the value of the Optional. This check to see if the optional contains a value. @@ -178,9 +176,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): return self[].unsafe_value() @always_inline - fn unsafe_value( - self: Reference[Self, _, _] - ) -> Reference[T, self.is_mutable, self.lifetime]: + fn unsafe_value(self: Reference[Self, _]) -> Reference[T, self.lifetime]: """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 2dc79010cd..41ed888472 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -301,7 +301,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # ===-------------------------------------------------------------------===# fn __iter__( - self: Reference[Self, _, _], + self: Reference[Self, _], ) -> _DictKeyIter[T, NoneType, self.lifetime]: """Iterate over elements of the set, returning immutable references. diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index ffa3354a71..38c6d18184 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -101,7 +101,7 @@ struct Arc[T: Movable](CollectionElement): # FIXME: This isn't right - the element should be mutable regardless # of whether the 'self' type is mutable. - fn __getitem__(self: Reference[Self, _, _]) -> ref [self.lifetime] T: + fn __getitem__(self: Reference[Self, _]) -> ref [self.lifetime] T: """Returns a Reference to the managed value. Returns: diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 6f2bffe78b..0c0e46acfe 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -196,16 +196,16 @@ struct AddressSpace(EqualityComparable): @value @register_passable("trivial") struct Reference[ + is_mutable: Bool, //, type: AnyType, - is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, ]: """Defines a non-nullable safe reference. Parameters: - type: Type of the underlying data. is_mutable: Whether the referenced data may be mutated through this. + type: Type of the underlying data. lifetime: The lifetime of the reference. address_space: The address space of the referenced data. """ diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 3927be6cdb..a6a9bea022 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -162,9 +162,7 @@ struct LegacyPointer[ var address: Self._mlir_type """The pointed-to address.""" - alias _ref_type = Reference[ - type, True, MutableStaticLifetime, address_space - ] + alias _ref_type = Reference[type, MutableStaticLifetime, address_space] @always_inline("nodebug") fn __init__() -> Self: @@ -236,7 +234,7 @@ struct LegacyPointer[ @staticmethod @always_inline("nodebug") - fn address_of(arg: Reference[type, _, _, address_space]) -> Self: + fn address_of(arg: Reference[type, _, address_space]) -> Self: """Gets the address of the argument. Args: @@ -668,7 +666,7 @@ struct DTypePointer[ @staticmethod @always_inline("nodebug") - fn address_of(arg: Reference[Scalar[type], _, _, address_space]) -> Self: + fn address_of(arg: Reference[Scalar[type], _, address_space]) -> Self: """Gets the address of the argument. Args: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index c133630115..4d067ddc14 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -55,7 +55,7 @@ struct UnsafePointer[ # We're unsafe, so we can have unsafe things. References we make have # an immortal mutable lifetime, since we can't come up with a meaningful # lifetime for them anyway. - alias _ref_type = Reference[T, True, MutableStaticLifetime, address_space] + alias _ref_type = Reference[T, MutableStaticLifetime, address_space] """The underlying pointer type.""" var address: Self._mlir_type @@ -89,7 +89,7 @@ struct UnsafePointer[ return Self {address: value} @always_inline - fn __init__(value: Reference[T, _, _, address_space]) -> Self: + fn __init__(value: Reference[T, _, address_space]) -> Self: """Create an unsafe UnsafePointer from a safe Reference. Args: @@ -161,7 +161,7 @@ struct UnsafePointer[ @staticmethod @always_inline("nodebug") - fn address_of(arg: Reference[T, _, _, address_space]) -> Self: + fn address_of(arg: Reference[T, _, address_space]) -> Self: """Gets the address of the argument. Args: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 041dbbc0b9..364592e7a8 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -224,7 +224,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): @parameter fn fill[idx: Int](): - tup[idx] = rebind[Reference[Int, False, __lifetime_of(elems)]]( + tup[idx] = rebind[Reference[Int, __lifetime_of(elems)]]( Reference(elems[idx]) )[] @@ -251,7 +251,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): @parameter fn fill[idx: Int](): - tup[idx] = rebind[Reference[Int, False, __lifetime_of(elems)]]( + tup[idx] = rebind[Reference[Int, __lifetime_of(elems)]]( Reference(elems[idx]) )[] @@ -278,7 +278,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): @parameter fn fill[idx: Int](): - tup[idx] = rebind[Reference[Int, False, __lifetime_of(elems)]]( + tup[idx] = rebind[Reference[Int, __lifetime_of(elems)]]( Reference(elems[idx]) )[] diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 2c59fe2aef..ca140d8aa4 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -250,7 +250,7 @@ struct InlineString(Sized, Stringable, CollectionElement): @always_inline fn as_string_slice( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> StringSlice[self.is_mutable, self.lifetime]: """Returns a string slice of the data owned by this inline string. @@ -265,7 +265,7 @@ struct InlineString(Sized, Stringable, CollectionElement): @always_inline fn as_bytes_slice( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> Span[UInt8, self.is_mutable, self.lifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -481,7 +481,7 @@ struct _FixedString[CAP: Int]( @always_inline fn as_string_slice( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> StringSlice[self.is_mutable, self.lifetime]: """Returns a string slice of the data owned by this fixed string. @@ -496,7 +496,7 @@ struct _FixedString[CAP: Int]( @always_inline fn as_bytes_slice( - self: Reference[Self, _, _] + self: Reference[Self, _] ) -> Span[UInt8, self.is_mutable, self.lifetime]: """ Returns a contiguous slice of the bytes owned by this string. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index c07abbde4e..0abe1fe69f 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -51,7 +51,7 @@ struct _SpanIter[ @always_inline fn __next__( inout self, - ) -> Reference[T, is_mutable, lifetime]: + ) -> Reference[T, lifetime]: @parameter if forward: self.index += 1 @@ -103,7 +103,7 @@ struct Span[ self._len = len @always_inline - fn __init__(inout self, list: Reference[List[T], is_mutable, lifetime]): + fn __init__(inout self, list: Reference[List[T], lifetime]): """Construct a Span from a List. Args: @@ -115,10 +115,7 @@ struct Span[ @always_inline fn __init__[ T2: CollectionElementNew, size: Int - ]( - inout self, - array: Reference[InlineArray[T2, size], is_mutable, lifetime], - ): + ](inout self, array: Reference[InlineArray[T2, size], lifetime],): """Construct a Span from an InlineArray. Parameters: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 29d0dff6dd..971d66bbed 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -38,7 +38,6 @@ fn _set_array_elem[ val: type, array: Reference[ __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], - __mlir_attr.`1 : i1`, _, ], ): @@ -380,7 +379,7 @@ struct InlineArray[ @always_inline("nodebug") fn __getitem__( - self: Reference[Self, _, _], idx: Int + self: Reference[Self, _], idx: Int ) -> ref [self.lifetime] Self.ElementType: """Get a `Reference` to the element at the given index. @@ -398,7 +397,7 @@ struct InlineArray[ fn __getitem__[ IntableType: Intable, index: IntableType, - ](self: Reference[Self, _, _]) -> ref [self.lifetime] Self.ElementType: + ](self: Reference[Self, _]) -> ref [self.lifetime] Self.ElementType: """Get a `Reference` to the element at the given index. Parameters: @@ -438,8 +437,8 @@ struct InlineArray[ @always_inline("nodebug") fn _get_reference_unsafe( - self: Reference[Self, _, _], idx: Int - ) -> Reference[Self.ElementType, self.is_mutable, self.lifetime]: + self: Reference[Self, _], idx: Int + ) -> Reference[Self.ElementType, self.lifetime]: """Get a reference to an element of self without checking index bounds. Users should opt for `__getitem__` instead of this method as it is @@ -511,9 +510,7 @@ struct InlineArray[ # TODO: use @parameter for soon once it stabilizes a bit for i in range(size): if ( - rebind[Reference[T, False, __lifetime_of(self)]]( - Reference(self[i]) - )[] + rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] == value ): return True diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 76e1409443..6597e1c00b 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -232,7 +232,7 @@ struct Variant[*Ts: CollectionElement]( fn __getitem__[ T: CollectionElement - ](self: Reference[Self, _, _]) -> ref [self.lifetime] T: + ](self: Reference[Self, _]) -> ref [self.lifetime] T: """Get the value out of the variant as a type-checked type. This explicitly check that your value is of that type! @@ -263,9 +263,7 @@ struct Variant[*Ts: CollectionElement]( ]() return UnsafePointer.address_of(self._impl).bitcast[T]() - fn _get_state( - self: Reference[Self, _, _] - ) -> Reference[Int8, self.is_mutable, self.lifetime]: + fn _get_state(self: Reference[Self, _]) -> Reference[Int8, self.lifetime]: var int8_self = UnsafePointer(self).bitcast[Int8]() return (int8_self + _UnionSize[Ts].compute())[] @@ -406,9 +404,7 @@ struct Variant[*Ts: CollectionElement]( fn unsafe_get[ T: CollectionElement - ](self: Reference[Self, _, _]) -> Reference[ - T, self.is_mutable, self.lifetime - ]: + ](self: Reference[Self, _]) -> Reference[T, self.lifetime]: """Get the value out of the variant as a type-checked type. This doesn't explicitly check that your value is of that type! From c72f2dedc9e4885ca29f3a4f2f803f9f4273a020 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 1 Jun 2024 21:48:06 -0700 Subject: [PATCH 0824/2019] [mojo-stdlib] Modernize a bunch of reference taking stuff This moves a bunch of slice types to take their "isMutable" bit as inferred, using `ref [_]` to tidy up a bunch of APIs, and fixes some mutability bugs in dictionary iteration. MODULAR_ORIG_COMMIT_REV_ID: 77003579b8773361d39259b10ce996c2e198361c --- stdlib/src/builtin/builtin_list.mojo | 32 +++++++------------ stdlib/src/builtin/format_int.mojo | 4 +-- stdlib/src/builtin/reversed.mojo | 28 ++++------------- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string.mojo | 20 +++++------- stdlib/src/builtin/string_literal.mojo | 10 +++--- stdlib/src/builtin/tuple.mojo | 4 +-- stdlib/src/collections/dict.mojo | 18 +++-------- stdlib/src/collections/inline_list.mojo | 14 ++++----- stdlib/src/collections/list.mojo | 24 +++++++------- stdlib/src/utils/_format.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 40 ++++++++++-------------- stdlib/src/utils/span.mojo | 12 +++---- stdlib/src/utils/string_slice.mojo | 14 ++++----- stdlib/test/utils/test_string_slice.mojo | 2 +- 15 files changed, 86 insertions(+), 140 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 584f348fea..0210909b1f 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -73,9 +73,8 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): Returns: The element at the given index. """ - return rebind[Reference[T, __lifetime_of(self)]]( - Reference(self.storage[i]) - )[] + # FIXME: Rebinding to a different lifetime. + return UnsafePointer.address_of(self.storage[i]).bitcast[T]()[] # ===----------------------------------------------------------------------===# @@ -171,16 +170,16 @@ struct VariadicList[type: AnyTrivialRegType](Sized): @value struct _VariadicListMemIter[ + elt_is_mutable: Bool, //, elt_type: AnyType, - elt_is_mutable: Bool, elt_lifetime: AnyLifetime[elt_is_mutable].type, list_lifetime: ImmutableLifetime, ]: """Iterator for VariadicListMem. Parameters: - elt_type: The type of the elements in the list. elt_is_mutable: Whether the elements in the list are mutable. + elt_type: The type of the elements in the list. elt_lifetime: The lifetime of the elements. list_lifetime: The lifetime of the VariadicListMem. """ @@ -222,7 +221,7 @@ struct _lit_lifetime_union[ struct _lit_mut_cast[ - is_mutable: Bool, + is_mutable: Bool, //, operand: AnyLifetime[is_mutable].type, result_mutable: Bool, ]: @@ -367,7 +366,7 @@ struct VariadicListMem[ # since that is what we want to use in the ultimate reference and # the union overall doesn't matter. _lit_mut_cast[ - False, __lifetime_of(self), Bool {value: elt_is_mutable} + __lifetime_of(self), Bool {value: elt_is_mutable} ].result, ].result ] element_type: @@ -386,12 +385,7 @@ struct VariadicListMem[ fn __iter__( self, - ) -> _VariadicListMemIter[ - element_type, - Bool {value: elt_is_mutable}, - lifetime, - __lifetime_of(self), - ]: + ) -> _VariadicListMemIter[element_type, lifetime, __lifetime_of(self),]: """Iterate over the list. Returns: @@ -399,7 +393,6 @@ struct VariadicListMem[ """ return _VariadicListMemIter[ element_type, - Bool {value: elt_is_mutable}, lifetime, __lifetime_of(self), ](0, self) @@ -586,18 +579,17 @@ struct VariadicPack[ A reference to the element. The Reference's mutability follows the mutability of the pack argument convention. """ - var ref_elt = __mlir_op.`lit.ref.pack.extract`[index = index.value]( + var litref_elt = __mlir_op.`lit.ref.pack.extract`[index = index.value]( self._value ) # Rebind the !lit.ref to agree on the element type. This is needed # because we're getting a low level rebind to AnyType when the # element_types[index] expression is erased to AnyType for Reference. - alias result_ref = Reference[ - element_types[index.value], - Self.lifetime, - ] - return Reference(rebind[result_ref._mlir_type](ref_elt))[] + var ref_elt = UnsafePointer.address_of( + __get_litref_as_mvalue(litref_elt) + ) + return ref_elt.bitcast[element_types[index.value]]()[] @always_inline fn each[func: fn[T: element_trait] (T) capturing -> None](self): diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 65545b0731..5f8e9027a4 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -265,7 +265,7 @@ fn _try_write_int[ # SAFETY: # This static lifetime is valid as long as we're using a # `StringLiteral` for `digit_chars`. - var zero = StringSlice[False, ImmutableStaticLifetime]( + var zero = StringSlice[ImmutableStaticLifetime]( # TODO: Remove cast after transition to UInt8 strings is complete. unsafe_from_utf8_ptr=digit_chars_array.bitcast[UInt8](), len=1, @@ -335,7 +335,7 @@ fn _try_write_int[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. - var str_slice = StringSlice[False, __lifetime_of(buf)]( + var str_slice = StringSlice[__lifetime_of(buf)]( # TODO: Remove cast after transition to UInt8 strings is complete. unsafe_from_utf8_ptr=buf_ptr.bitcast[UInt8](), len=len, diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 22708459f5..37e579741a 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -77,18 +77,14 @@ fn reversed[T: ReversibleRange](value: T) -> _StridedRange: fn reversed[ - T: CollectionElement, //, - mutability: Bool, - self_life: AnyLifetime[mutability].type, -](ref [self_life]value: List[T]) -> _ListIter[T, self_life, False]: + T: CollectionElement +](ref [_]value: List[T]) -> _ListIter[T, __lifetime_of(value), False]: """Get a reversed iterator of the input list. **Note**: iterators are currently non-raising. Parameters: T: The type of the elements in the list. - mutability: Mutability of the collection. - self_life: Lifetime of the collection. Args: value: The list to get the reversed iterator of. @@ -102,9 +98,7 @@ fn reversed[ fn reversed[ K: KeyElement, V: CollectionElement, - mutability: Bool, - self_life: AnyLifetime[mutability].type, -](ref [self_life]value: Dict[K, V],) -> _DictKeyIter[K, V, self_life, False]: +](ref [_]value: Dict[K, V],) -> _DictKeyIter[K, V, __lifetime_of(value), False]: """Get a reversed iterator of the input dict. **Note**: iterators are currently non-raising. @@ -112,8 +106,6 @@ fn reversed[ Parameters: K: The type of the keys in the dict. V: The type of the values in the dict. - mutability: Mutability of the collection. - self_life: Lifetime of the collection. Args: value: The dict to get the reversed iterator of. @@ -125,13 +117,11 @@ fn reversed[ fn reversed[ - mutability: Bool, - self_life: AnyLifetime[mutability].type, K: KeyElement, V: CollectionElement, dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, -](ref [self_life]value: _DictValueIter[K, V, dict_lifetime]) -> _DictValueIter[ +](ref [_]value: _DictValueIter[K, V, dict_lifetime]) -> _DictValueIter[ K, V, dict_lifetime, False ]: """Get a reversed iterator of the input dict values. @@ -139,8 +129,6 @@ fn reversed[ **Note**: iterators are currently non-raising. Parameters: - mutability: Whether the reference to the dict is mutable. - self_life: The lifetime of the dict. K: The type of the keys in the dict. V: The type of the values in the dict. dict_mutability: Whether the reference to the dict values is mutable. @@ -152,17 +140,15 @@ fn reversed[ Returns: The reversed iterator of the dict values. """ - return value.__reversed__[mutability, self_life]() + return value.__reversed__() fn reversed[ - mutability: Bool, - self_life: AnyLifetime[mutability].type, K: KeyElement, V: CollectionElement, dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, -](ref [self_life]value: _DictEntryIter[K, V, dict_lifetime]) -> _DictEntryIter[ +](ref [_]value: _DictEntryIter[K, V, dict_lifetime]) -> _DictEntryIter[ K, V, dict_lifetime, False ]: """Get a reversed iterator of the input dict items. @@ -170,8 +156,6 @@ fn reversed[ **Note**: iterators are currently non-raising. Parameters: - mutability: Whether the reference to the dict is mutable. - self_life: The lifetime of the dict. K: The type of the keys in the dict. V: The type of the values in the dict. dict_mutability: Whether the reference to the dict items is mutable. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 24f63e6f95..028eb97c01 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2908,7 +2908,7 @@ fn _format_scalar[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. - var str_slice = StringSlice[False, __lifetime_of(buf)]( + var str_slice = StringSlice[__lifetime_of(buf)]( unsafe_from_utf8_ptr=buf.unsafe_ptr(), len=wrote ) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 49a557dd06..44d277869f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1313,9 +1313,7 @@ struct String( return copy @always_inline - fn as_bytes_slice( - self: Reference[Self, _] - ) -> Span[UInt8, self.is_mutable, self.lifetime]: + fn as_bytes_slice(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -1325,28 +1323,24 @@ struct String( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, self.is_mutable, self.lifetime]( - unsafe_ptr=self[]._buffer.unsafe_ptr(), + return Span[UInt8, __lifetime_of(self)]( + unsafe_ptr=self._buffer.unsafe_ptr(), # Does NOT include the NUL terminator. - len=self[]._byte_length(), + len=self._byte_length(), ) @always_inline - fn as_string_slice( - self: Reference[Self, _] - ) -> StringSlice[self.is_mutable, self.lifetime]: + fn as_string_slice(ref [_]self: Self) -> StringSlice[__lifetime_of(self)]: """Returns a string slice of the data owned by this string. Returns: A string slice pointing to the data owned by this string. """ - var bytes = self[].as_bytes_slice() - # FIXME(MSTDL-160): # Enforce UTF-8 encoding in String so this is actually # guaranteed to be valid. - return StringSlice[self.is_mutable, self.lifetime]( - unsafe_from_utf8=bytes + return StringSlice[__lifetime_of(self)]( + unsafe_from_utf8=self.as_bytes_slice() ) fn _byte_length(self) -> Int: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c6b242df00..732fd40a04 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -250,7 +250,7 @@ struct StringLiteral( return self.__str__().__repr__() @always_inline - fn as_string_slice(self) -> StringSlice[False, ImmutableStaticLifetime]: + fn as_string_slice(self) -> StringSlice[ImmutableStaticLifetime]: """Returns a string slice of this static string literal. Returns: @@ -262,12 +262,10 @@ struct StringLiteral( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually # guaranteed to be valid. - return StringSlice[False, ImmutableStaticLifetime]( - unsafe_from_utf8=bytes - ) + return StringSlice[ImmutableStaticLifetime](unsafe_from_utf8=bytes) @always_inline - fn as_bytes_slice(self) -> Span[UInt8, False, ImmutableStaticLifetime]: + fn as_bytes_slice(self) -> Span[UInt8, ImmutableStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -277,7 +275,7 @@ struct StringLiteral( var ptr = self.unsafe_uint8_ptr() - return Span[UInt8, False, ImmutableStaticLifetime]( + return Span[UInt8, ImmutableStaticLifetime]( unsafe_ptr=ptr, len=self._byte_length(), ) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index ed941cbb74..3a60c6522f 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -152,7 +152,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): @always_inline("nodebug") fn __getitem__[ idx: Int - ](self: Reference[Self, _]) -> ref [self.lifetime] element_types[idx.value]: + ](ref [_]self: Self) -> ref [__lifetime_of(self)] element_types[idx.value]: """Get a reference to an element in the tuple. Parameters: @@ -163,7 +163,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): """ # Return a reference to an element at the specified index, propagating # mutability of self. - var storage_kgen_ptr = UnsafePointer.address_of(self[].storage).address + var storage_kgen_ptr = UnsafePointer.address_of(self.storage).address # KGenPointer to the element. var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 1626eae1e6..ec0748f914 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -73,7 +73,6 @@ struct _DictEntryIter[ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[DictEntry[K, V], Self.imm_dict_lifetime] var index: Int var seen: Int @@ -83,7 +82,7 @@ struct _DictEntryIter[ return self @always_inline - fn __next__(inout self) -> Self.ref_type: + fn __next__(inout self) -> Reference[DictEntry[K, V], Self.dict_lifetime]: while True: var opt_entry_ref = self.src[]._entries.__get_ref(self.index) if opt_entry_ref[]: @@ -95,9 +94,7 @@ struct _DictEntryIter[ self.index -= 1 self.seen += 1 - # Use UnsafePointer to cast from dict_mutability to mutable. - # TODO: This seems wrong. - return UnsafePointer(opt_entry_ref[].value())[] + return opt_entry_ref[].value() @parameter if forward: @@ -127,11 +124,6 @@ struct _DictKeyIter[ forward: The iteration direction. `False` is backwards. """ - alias imm_dict_lifetime = __mlir_attr[ - `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` - ] - alias ref_type = Reference[K, Self.imm_dict_lifetime] - alias dict_entry_iter = _DictEntryIter[K, V, dict_lifetime, forward] var iter: Self.dict_entry_iter @@ -139,7 +131,7 @@ struct _DictKeyIter[ fn __iter__(self) -> Self: return self - fn __next__(inout self) -> Self.ref_type: + fn __next__(inout self) -> Reference[K, dict_lifetime]: return self.iter.__next__()[].key fn __len__(self) -> Int: @@ -172,9 +164,7 @@ struct _DictValueIter[ fn __iter__(self) -> Self: return self - fn __reversed__[ - mutability: Bool, self_life: AnyLifetime[mutability].type - ](self) -> _DictValueIter[K, V, dict_lifetime, False]: + fn __reversed__(self) -> _DictValueIter[K, V, dict_lifetime, False]: var src = self.iter.src return _DictValueIter( _DictEntryIter[K, V, dict_lifetime, False]( diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 45f374c8de..f1a17bb1a9 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -129,8 +129,8 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __getitem__( - self: Reference[Self, _], owned idx: Int - ) -> ref [self.lifetime] Self.ElementType: + ref [_]self: Self, owned idx: Int + ) -> ref [__lifetime_of(self)] Self.ElementType: """Get a `Reference` to the element at the given index. Args: @@ -140,13 +140,13 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): A reference to the item at the given index. """ debug_assert( - -self[]._size <= idx < self[]._size, "Index must be within bounds." + -self._size <= idx < self._size, "Index must be within bounds." ) if idx < 0: - idx += len(self[]) + idx += len(self) - return self[]._array[idx] + return self._array[idx] @always_inline fn __del__(owned self): @@ -155,8 +155,8 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): destroy_pointee(UnsafePointer(self._array[i])) fn __iter__( - self: Reference[Self, _], - ) -> _InlineListIter[ElementType, capacity, self.lifetime]: + ref [_]self: Self, + ) -> _InlineListIter[ElementType, capacity, __lifetime_of(self)]: """Iterate over elements of the list, returning immutable references. Returns: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index ee3d8d2a85..7b345677e5 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -289,9 +289,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ self.extend(other^) - fn __iter__( - self: Reference[Self, _], - ) -> _ListIter[T, self.lifetime]: + fn __iter__(ref [_]self: Self) -> _ListIter[T, __lifetime_of(self)]: """Iterate over elements of the list, returning immutable references. Returns: @@ -300,14 +298,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return _ListIter(0, self) fn __reversed__( - self: Reference[Self, _] - ) -> _ListIter[T, self.lifetime, False]: + ref [_]self: Self, + ) -> _ListIter[T, __lifetime_of(self), False]: """Iterate backwards over the list, returning immutable references. Returns: A reversed iterator of immutable references to the list elements. """ - return _ListIter[forward=False](len(self[]), self) + return _ListIter[forward=False](len(self), self) # ===-------------------------------------------------------------------===# # Trait implementations @@ -636,7 +634,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn index[ C: ComparableCollectionElement ]( - self: Reference[List[C], _], + ref [_]self: List[C], value: C, start: Int = 0, stop: Optional[Int] = None, @@ -672,20 +670,20 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var stop_normalized: Int if stop is None: # Default end - stop_normalized = len(self[]) + stop_normalized = len(self) else: stop_normalized = stop.value()[] if start_normalized < 0: - start_normalized += len(self[]) + start_normalized += len(self) if stop_normalized < 0: - stop_normalized += len(self[]) + stop_normalized += len(self) - start_normalized = _clip(start_normalized, 0, len(self[])) - stop_normalized = _clip(stop_normalized, 0, len(self[])) + start_normalized = _clip(start_normalized, 0, len(self)) + stop_normalized = _clip(stop_normalized, 0, len(self)) for i in range(start_normalized, stop_normalized): - if self[][i] == value: + if self[i] == value: return i raise "ValueError: Given element is not in list" diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index 8368ce4718..cf1486ff7a 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -103,7 +103,7 @@ struct Formatter: self.write_str(literal.as_string_slice()) @always_inline - fn write_str(inout self, str_slice: StringSlice[False, _]): + fn write_str(inout self, str_slice: StringSlice[_]): """ Write a string slice to this formatter. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index ca140d8aa4..c7a3ec8b6c 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -111,7 +111,7 @@ struct InlineString(Sized, Stringable, CollectionElement): """ self.__iadd__(string.as_string_slice()) - fn __iadd__(inout self, str_slice: StringSlice[False]): + fn __iadd__(inout self, str_slice: StringSlice[_]): """Appends another string to this string. Args: @@ -249,9 +249,7 @@ struct InlineString(Sized, Stringable, CollectionElement): return self._storage[String].unsafe_ptr() @always_inline - fn as_string_slice( - self: Reference[Self, _] - ) -> StringSlice[self.is_mutable, self.lifetime]: + fn as_string_slice(ref [_]self: Self) -> StringSlice[__lifetime_of(self)]: """Returns a string slice of the data owned by this inline string. Returns: @@ -261,12 +259,10 @@ struct InlineString(Sized, Stringable, CollectionElement): # FIXME(MSTDL-160): # Enforce UTF-8 encoding in _FixedString so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self[].as_bytes_slice()) + return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) @always_inline - fn as_bytes_slice( - self: Reference[Self, _] - ) -> Span[UInt8, self.is_mutable, self.lifetime]: + fn as_bytes_slice(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -276,10 +272,10 @@ struct InlineString(Sized, Stringable, CollectionElement): A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, self.is_mutable, self.lifetime]( - unsafe_ptr=self[].unsafe_ptr(), + return Span[UInt8, __lifetime_of(self)]( + unsafe_ptr=self.unsafe_ptr(), # Does NOT include the NUL terminator. - len=len(self[]), + len=len(self), ) @@ -388,7 +384,7 @@ struct _FixedString[CAP: Int]( self.__iadd__(string.as_string_slice()) @always_inline - fn __iadd__(inout self, str_slice: StringSlice[False]) raises: + fn __iadd__(inout self, str_slice: StringSlice[_]) raises: """Appends another string to this string. Args: @@ -415,7 +411,7 @@ struct _FixedString[CAP: Int]( fn _iadd_non_raising( inout self, - str_slice: StringSlice[False], + str_slice: StringSlice[_], ) -> Optional[Error]: var total_len = len(self) + str_slice._byte_length() @@ -450,7 +446,7 @@ struct _FixedString[CAP: Int]( fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() - var str_slice = StringSlice[False, ImmutableStaticLifetime]( + var str_slice = StringSlice[ImmutableStaticLifetime]( unsafe_from_utf8_strref=strref ) @@ -480,9 +476,7 @@ struct _FixedString[CAP: Int]( return self.buffer.unsafe_ptr() @always_inline - fn as_string_slice( - self: Reference[Self, _] - ) -> StringSlice[self.is_mutable, self.lifetime]: + fn as_string_slice(ref [_]self: Self) -> StringSlice[__lifetime_of(self)]: """Returns a string slice of the data owned by this fixed string. Returns: @@ -492,12 +486,10 @@ struct _FixedString[CAP: Int]( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in _FixedString so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self[].as_bytes_slice()) + return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) @always_inline - fn as_bytes_slice( - self: Reference[Self, _] - ) -> Span[UInt8, self.is_mutable, self.lifetime]: + fn as_bytes_slice(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -507,8 +499,8 @@ struct _FixedString[CAP: Int]( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, self.is_mutable, self.lifetime]( - unsafe_ptr=self[].unsafe_ptr(), + return Span[UInt8, __lifetime_of(self)]( + unsafe_ptr=self.unsafe_ptr(), # Does NOT include the NUL terminator. - len=self[].size, + len=self.size, ) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 0abe1fe69f..22e84c1daa 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -27,22 +27,22 @@ from sys.intrinsics import _type_is_eq @value struct _SpanIter[ + is_mutable: Bool, //, T: CollectionElement, - is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type, forward: Bool = True, ]: """Iterator for Span. Parameters: - T: The type of the elements in the span. is_mutable: Whether the reference to the span is mutable. + T: The type of the elements in the span. lifetime: The lifetime of the Span. forward: The iteration direction. `False` is backwards. """ var index: Int - var src: Span[T, is_mutable, lifetime] + var src: Span[T, lifetime] @always_inline fn __iter__(self) -> Self: @@ -71,15 +71,15 @@ struct _SpanIter[ @value struct Span[ + is_mutable: Bool, //, T: CollectionElement, - is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type, ]: """A non owning view of contiguous data. Parameters: - T: The type of the elements in the span. is_mutable: Whether the span is mutable. + T: The type of the elements in the span. lifetime: The lifetime of the Span. """ @@ -179,7 +179,7 @@ struct Span[ return res @always_inline - fn __iter__(self) -> _SpanIter[T, is_mutable, lifetime]: + fn __iter__(self) -> _SpanIter[T, lifetime]: """Get an iterator over the elements of the span. Returns: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index d01e865157..63f0bd0a05 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -24,7 +24,7 @@ from utils import Span struct StringSlice[ - is_mutable: Bool, + is_mutable: Bool, //, lifetime: AnyLifetime[is_mutable].type, ](Stringable): """ @@ -38,16 +38,14 @@ struct StringSlice[ lifetime: The lifetime of the underlying string data. """ - var _slice: Span[UInt8, is_mutable, lifetime] + var _slice: Span[UInt8, lifetime] # ===------------------------------------------------------------------===# # Initializers # ===------------------------------------------------------------------===# @always_inline - fn __init__( - inout self, *, owned unsafe_from_utf8: Span[UInt8, is_mutable, lifetime] - ): + fn __init__(inout self, *, owned unsafe_from_utf8: Span[UInt8, lifetime]): """ Construct a new StringSlice from a sequence of UTF-8 encoded bytes. @@ -75,7 +73,7 @@ struct StringSlice[ """ var strref = unsafe_from_utf8_strref - var byte_slice = Span[UInt8, is_mutable, lifetime]( + var byte_slice = Span[UInt8, lifetime]( unsafe_ptr=strref.unsafe_ptr(), len=len(strref), ) @@ -104,7 +102,7 @@ struct StringSlice[ UTF-8. len: The number of bytes of encoded data. """ - var byte_slice = Span[UInt8, is_mutable, lifetime]( + var byte_slice = Span[UInt8, lifetime]( unsafe_ptr=unsafe_from_utf8_ptr, len=len, ) @@ -123,7 +121,7 @@ struct StringSlice[ # ===------------------------------------------------------------------===# @always_inline - fn as_bytes_slice(self) -> Span[UInt8, is_mutable, lifetime]: + fn as_bytes_slice(self) -> Span[UInt8, lifetime]: """ Get the sequence of encoded bytes as a slice of the underlying string. diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index bcabb5504c..79445c0a10 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -117,7 +117,7 @@ fn test_heap_string_from_string_slice() raises: alias string_lit: StringLiteral = "Hello" alias static_str: StringSlice[ - False, ImmutableStaticLifetime + ImmutableStaticLifetime ] = string_lit.as_string_slice() alias heap_string = String(static_str) From 3f033163a7099e74783de26a2f5a94f0552dc211 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 2 Jun 2024 00:02:01 -0700 Subject: [PATCH 0825/2019] [mojo-stdlib] Keep moving things to ref conventions. This moves some low level methods in Variant and Optional over to taking and returning a ref. This shakes out some other bugs and problems along the way. MODULAR_ORIG_COMMIT_REV_ID: 06a51ce396c4ced744da8a1ec4d279fb2f1b77fe --- stdlib/src/collections/optional.mojo | 6 +++--- stdlib/src/utils/variant.mojo | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 52b947d0c6..9a376ad406 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -176,7 +176,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): return self[].unsafe_value() @always_inline - fn unsafe_value(self: Reference[Self, _]) -> Reference[T, self.lifetime]: + fn unsafe_value(ref [_]self: Self) -> ref [__lifetime_of(self)] T: """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. @@ -187,8 +187,8 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): Returns: A reference to the contained data of the option as a Reference[T]. """ - debug_assert(self[].__bool__(), ".value() on empty Optional") - return self[]._value[T] + debug_assert(self.__bool__(), ".value() on empty Optional") + return self._value[T] @always_inline fn _value_copy(self) -> T: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 6597e1c00b..c5c351a4a8 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -232,7 +232,7 @@ struct Variant[*Ts: CollectionElement]( fn __getitem__[ T: CollectionElement - ](self: Reference[Self, _]) -> ref [self.lifetime] T: + ](ref [_]self: Self) -> ref [__lifetime_of(self)] T: """Get the value out of the variant as a type-checked type. This explicitly check that your value is of that type! @@ -248,10 +248,10 @@ struct Variant[*Ts: CollectionElement]( Returns: The internal data represented as a `Reference[T]`. """ - if not self[].isa[T](): + if not self.isa[T](): abort("get: wrong variant type") - return self[].unsafe_get[T]()[] + return self.unsafe_get[T]()[] # ===-------------------------------------------------------------------===# # Methods @@ -404,7 +404,7 @@ struct Variant[*Ts: CollectionElement]( fn unsafe_get[ T: CollectionElement - ](self: Reference[Self, _]) -> Reference[T, self.lifetime]: + ](ref [_]self: Self) -> Reference[T, __lifetime_of(self)]: """Get the value out of the variant as a type-checked type. This doesn't explicitly check that your value is of that type! @@ -421,8 +421,8 @@ struct Variant[*Ts: CollectionElement]( Returns: The internal data represented as a `Reference[T]`. """ - debug_assert(self[].isa[T](), "get: wrong variant type") - return self[]._get_ptr[T]()[] + debug_assert(self.isa[T](), "get: wrong variant type") + return self._get_ptr[T]()[] @staticmethod fn _check[T: CollectionElement]() -> Int8: From 4f5aa6ef3501e797c99959d576ac4fba0b7c6d56 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Sun, 2 Jun 2024 05:12:45 -0500 Subject: [PATCH 0826/2019] [External] [stdlib] Implement pythonic lcm function (#41107) [External] [stdlib] Implement pythonic lcm function Similar changes as #2777 but for the `lcm` function. Suggested followup https://github.com/modularml/mojo/pull/2777#pullrequestreview-2082274419 --------- Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2884 MODULAR_ORIG_COMMIT_REV_ID: 7bbc25a74f1f0a0d8412383460a706d6236a7c57 --- stdlib/src/builtin/_math.mojo | 71 ++++++++++++++++++++++++++++++ stdlib/test/builtin/test_math.mojo | 20 +++++++++ 2 files changed, 91 insertions(+) diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index eea01382fc..89717218ad 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -267,3 +267,74 @@ fn gcd(*values: Int) -> Int: if result == 1: return result return result + + +# ===----------------------------------------------------------------------=== # +# lcm +# ===----------------------------------------------------------------------=== # + + +fn lcm(owned m: Int, owned n: Int, /) -> Int: + """Computes the least common multiple of two integers. + + Args: + m: The first integer. + n: The second integer. + + Returns: + The least common multiple of the two integers. + """ + var d: Int + if d := gcd(m, n): + return abs((m // d) * n if m > n else (n // d) * m) + return 0 + + +fn lcm(s: Span[Int], /) -> Int: + """Computes the least common multiple of a span of integers. + + Args: + s: A span of integers. + + Returns: + The least common multiple of the span. + """ + if len(s) == 0: + return 1 + + var result = s[0] + for item in s[1:]: + result = lcm(result, item[]) + return result + + +@always_inline +fn lcm(l: List[Int], /) -> Int: + """Computes the least common multiple of a list of integers. + + Args: + l: A list of integers. + + Returns: + The least common multiple of the list. + """ + return lcm(Span(l)) + + +fn lcm(*values: Int) -> Int: + """Computes the least common multiple of a variadic list of integers. + + Args: + values: A variadic list of integers. + + Returns: + The least common multiple of the list. + """ + # TODO: Deduplicate when we can create a Span from VariadicList + if len(values) == 0: + return 1 + + var result = values[0] + for i in range(1, len(values)): + result = lcm(result, values[i]) + return result diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index ee20cb489d..4dd0684b6b 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -110,6 +110,25 @@ def test_gcd(): assert_equal(gcd(List(16)), 16) +def test_lcm(): + assert_equal(lcm(-2, 4), 4) + assert_equal(lcm(2345, 23452), 54994940) + var l = List(4, 6, 7, 3) + assert_equal(lcm(Span(l)), 84) + assert_equal(lcm(l), 84) + assert_equal(lcm(4, 6, 7, 3), 84) + assert_equal(lcm(), 1) + assert_equal(lcm(List(3)), 3) + assert_equal(lcm(List[Int]()), 1) + assert_equal(lcm(0, 4), 0) + assert_equal(lcm(5, 33), 165) + assert_equal(lcm(-34, -56, -32), 3808) + var il = InlineArray[Int, 5](4, 16, 2, 8, 6) + assert_equal(lcm(Span[Int](il)), 48) + assert_equal(lcm(345, 623, 364, 84, 93), 346475220) + assert_equal(lcm(0, 0), 0) + + def main(): test_abs() test_divmod() @@ -118,3 +137,4 @@ def main(): test_round() test_pow() test_gcd() + test_lcm() From 35f94efcdf790d06b702fc203191ece9a8ecd453 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Sun, 2 Jun 2024 05:51:40 -0500 Subject: [PATCH 0827/2019] [External] [stdlib] Temporary directory (#41108) [External] [stdlib] Temporary directory Implementation of `TemporaryDirectory`, a wrapper around `mkdtemp` that can be used as a context nanager. ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2743 --------- Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2743 MODULAR_ORIG_COMMIT_REV_ID: 6ed15f5aab3ba505d62ca4689da53f7906efe18a --- docs/changelog.md | 3 + stdlib/src/tempfile/__init__.mojo | 2 +- stdlib/src/tempfile/tempfile.mojo | 85 +++++++++++++++++++++++-- stdlib/test/tempfile/test_tempfile.mojo | 23 ++++++- 4 files changed, 104 insertions(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 28b8fe4b67..579a3a147c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -34,6 +34,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added `C_char` type alias in `sys.ffi`. +- Added `TemporaryDirectory` in module `tempfile`. + ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) + ### 🦋 Changed - Continued transition to `UnsafePointer` and unsigned byte type for strings: diff --git a/stdlib/src/tempfile/__init__.mojo b/stdlib/src/tempfile/__init__.mojo index 0365c38c2c..c018869cf1 100644 --- a/stdlib/src/tempfile/__init__.mojo +++ b/stdlib/src/tempfile/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the tempfile package.""" -from .tempfile import gettempdir, mkdtemp +from .tempfile import gettempdir, mkdtemp, TemporaryDirectory diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 55225f9271..87b0b440fa 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -144,11 +144,7 @@ fn mkdtemp( Raises: If the directory can not be created. """ - var final_dir: Path - if not dir: - final_dir = Path(_get_default_tempdir()) - else: - final_dir = Path(dir.value()[]) + var final_dir = Path(dir.value()[]) if dir else Path(_get_default_tempdir()) for _ in range(TMP_MAX): var dir_name = final_dir / (prefix + _get_random_name() + suffix) @@ -164,3 +160,82 @@ fn mkdtemp( except: continue raise Error("Failed to create temporary file") + + +# TODO use shutil.rmtree (or equivalent) when it exists +fn _rmtree(path: String, ignore_errors: Bool = False) raises: + """Removes the specified directory and all its contents. + + If the path is a symbolic link, an error is raised. If ignore_errors is + True, errors resulting from failed removals will be ignored. Absolute and + relative paths are allowed, relative paths are resolved from cwd. + + Args: + path: The path to the directory. + ignore_errors: Whether to ignore errors. + """ + if os.path.islink(path): + raise Error("`path`can not be a symbolic link: " + path) + + for file_or_dir in os.listdir(path): + var curr_path = os.path.join(path, file_or_dir[]) + if os.path.isfile(curr_path): + try: + os.remove(curr_path) + except e: + if not ignore_errors: + raise e + continue + if os.path.isdir(curr_path): + try: + _rmtree(curr_path, ignore_errors) + except e: + if ignore_errors: + continue + raise e + try: + os.rmdir(path) + except e: + if not ignore_errors: + raise e + + +struct TemporaryDirectory: + """A temporary directory.""" + + var name: String + """The name of the temporary directory.""" + var _ignore_cleanup_errors: Bool + """Whether to ignore cleanup errors.""" + + fn __init__( + inout self, + suffix: String = "", + prefix: String = "tmp", + dir: Optional[String] = None, + ignore_cleanup_errors: Bool = False, + ) raises: + """Create a temporary directory. Can be used as a context manager. + + Args: + suffix: Suffix to use for the directory name. + prefix: Prefix to use for the directory name. + dir: Directory in which the directory will be created. + ignore_cleanup_errors: Whether to ignore cleanup errors. + """ + self._ignore_cleanup_errors = ignore_cleanup_errors + + self.name = mkdtemp(suffix, prefix, dir) + + fn __enter__(self) -> String: + return self.name + + fn __exit__(self) raises: + _rmtree(self.name, ignore_errors=self._ignore_cleanup_errors) + + fn __exit__(self, err: Error) -> Bool: + try: + self.__exit__() + return True + except: + return False diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index 34079e3583..f776ff5973 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -16,7 +16,7 @@ import os from os.path import exists from pathlib import Path from testing import assert_true, assert_false, assert_equal -from tempfile import gettempdir, mkdtemp +from tempfile import gettempdir, mkdtemp, TemporaryDirectory fn test_mkdtemp() raises: @@ -29,7 +29,7 @@ fn test_mkdtemp() raises: dir_name = mkdtemp(prefix="my_prefix", suffix="my_suffix") assert_true(exists(dir_name), "Failed to create temporary directory") - var name = dir_name.split("/")[-1] + var name = dir_name.split(os.sep)[-1] assert_true(name.startswith("my_prefix")) assert_true(name.endswith("my_suffix")) @@ -39,7 +39,7 @@ fn test_mkdtemp() raises: dir_name = mkdtemp(dir=Path().__fspath__()) assert_true(exists(dir_name), "Failed to create temporary directory") assert_true( - exists(Path() / dir_name.split("/")[-1]), + exists(Path() / dir_name.split(os.sep)[-1]), "Expected directory to be created in cwd", ) os.rmdir(dir_name) @@ -161,6 +161,23 @@ fn test_gettempdir() raises: _clean_up_gettempdir_test() +fn test_temporary_directory() raises -> None: + var tmp_dir: String = "" + with TemporaryDirectory(suffix="my_suffix", prefix="my_prefix") as tmp_dir: + assert_true(exists(tmp_dir), "Failed to create temp dir " + tmp_dir) + assert_true(tmp_dir.endswith("my_suffix")) + assert_true(tmp_dir.split(os.sep)[-1].startswith("my_prefix")) + assert_false(exists(tmp_dir), "Failed to delete temp dir " + tmp_dir) + + with TemporaryDirectory() as tmp_dir: + assert_true(exists(tmp_dir), "Failed to create temp dir " + tmp_dir) + _ = open(Path(tmp_dir) / "test_file", "w") + os.mkdir(Path(tmp_dir) / "test_dir") + _ = open(Path(tmp_dir) / "test_dir" / "test_file2", "w") + assert_false(exists(tmp_dir), "Failed to delete temp dir " + tmp_dir) + + fn main() raises: test_mkdtemp() test_gettempdir() + test_temporary_directory() From 0772135c9774e7c50a4f4f83a2ddc1df12277660 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Sun, 2 Jun 2024 06:30:04 -0500 Subject: [PATCH 0828/2019] [External] [stdlib] Add SliceNew (#41109) [External] [stdlib] Add SliceNew Subset of changes from #2495 Introduce `SliceNew` with corrected behaviour when using negative step values. Currently internal code relies on the old incorrect `Slice` behaviour, so we must repair this feature incrementally. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2894 MODULAR_ORIG_COMMIT_REV_ID: d730dbb36e81bbfd615e20446aea6ffb32efeaed --- docs/changelog.md | 4 + stdlib/src/builtin/builtin_slice.mojo | 227 ++++++++++++++++++++++++++ stdlib/test/builtin/test_slice.mojo | 58 +++++++ 3 files changed, 289 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 579a3a147c..4dc9c65080 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -37,6 +37,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added `TemporaryDirectory` in module `tempfile`. ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) +- Added temporary `SliceNew` type with corrected behaviour from `Slice` to facilitate + an incremental internal migration due to reliance on the old, incorrect behaviour. + ([PR #2894](https://github.com/modularml/mojo/pull/2894) by [@bgreni](https://github.com/bgreni)) + ### 🦋 Changed - Continued transition to `UnsafePointer` and unsigned byte type for strings: diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 1d2136582a..a90ca98461 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from sys.intrinsics import _mlirtype_is_eq +from collections import OptionalReg @always_inline("nodebug") @@ -215,3 +216,229 @@ fn slice[ The constructed slice. """ return Slice(start, end, step) + + +# ===-------------------------------------------------------------------===# +# SliceNew +# ===-------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: + if x and y: + return x.value() == y.value() + return not x and not y + + +@register_passable("trivial") +struct SliceNew(Stringable, EqualityComparable): + """Represents a new slice implementation that corrects incorrect behaviour in the existing implementation. + """ + + var start: OptionalReg[Int] + """The starting index of the slice.""" + var end: OptionalReg[Int] + """The end index of the slice.""" + var step: Int + """The step increment value of the slice.""" + + @always_inline("nodebug") + fn __init__(inout self, start: Int, end: Int): + """Construct slice given the start and end values. + + Args: + start: The start value. + end: The end value. + """ + self.start = start + self.end = end + self.step = 1 + + @always_inline("nodebug") + fn __init__( + inout self, + start: OptionalReg[Int], + end: OptionalReg[Int], + step: OptionalReg[Int], + ): + """Construct slice given the start, end and step values. + + Args: + start: The start value. + end: The end value. + step: The step value. + """ + self.start = start + self.end = end + + if step and step.value() == 0: + abort("Cannot build slice with a step of 0") + + self.step = step.value() if step else 1 + + fn __str__(self) -> String: + """Gets the string representation of the span. + + Returns: + The string representation of the span. + """ + var res = str(self.start.value()) if self.start else "" + res += ":" + if self.end: + res += str(self.end.value()) if self.end else "" + res += ":" + res += str(self.step) + return res + + @always_inline("nodebug") + fn __eq__(self, other: Self) -> Bool: + """Compare this slice to the other. + + Args: + other: The slice to compare to. + + Returns: + True if start, end, and step values of this slice match the + corresponding values of the other slice and False otherwise. + """ + return ( + _compare_optional(self.start, other.start) + and _compare_optional(self.end, other.end) + and self.step == other.step + ) + + @always_inline("nodebug") + fn __ne__(self, other: Self) -> Bool: + """Compare this slice to the other. + + Args: + other: The slice to compare to. + + Returns: + False if start, end, and step values of this slice match the + corresponding values of the other slice and True otherwise. + """ + return not (self == other) + + @always_inline + fn unsafe_indices(self) -> Int: + """Return the length of the slice. + + Only use this function if start/end is guaranteed to be not None. + + Returns: + The length of the slice. + """ + + return len(range(self.start.value(), self.end.value(), self.step)) + + @always_inline + fn __getitem__(self, idx: Int) -> Int: + """Get the slice index. + + Args: + idx: The index. + + Returns: + The slice index. + """ + return self.start.value() + idx * self.step + + fn indices(self, src_len: Int) -> (Int, Int, Int): + """Returns a tuple of 3 intergers representing the start, end, and step + of the slice if applied to a container of the given length. + + Uses the target container length to normalize negative, out of bounds, or None indices. + + Negative indices are wrapped using the length of the container. + ```mojo + s = slice_new(0, -1, 1) + s.indices(5) # returns (0, 4, 1) + ``` + + None indices are defaulted to the start or the end of the container based + on whether `step` is positive or negative. + ```mojo + s = slice_new(None, None, 1) + s.indices(5) # returns (0, 5, 1) + ``` + + Out of bounds indices are clamped using the size of the container. + ```mojo + s = slice_new(20) + s.indices(5) # returns (0, 5, 1) + ``` + + Args: + src_len: The length of the target container. + + Returns: + A tuple containing three integers for start, end, and step. + """ + var start = self.start + var end = self.end + var positive_step = self.step > 0 + + if not start: + start = 0 if positive_step else src_len - 1 + elif start.value() < 0: + start = start.value() + src_len + if start.value() < 0: + start = 0 if positive_step else -1 + elif start.value() >= src_len: + start = src_len if positive_step else src_len - 1 + + if not end: + end = src_len if positive_step else -1 + elif end.value() < 0: + end = end.value() + src_len + if end.value() < 0: + end = 0 if positive_step else -1 + elif end.value() >= src_len: + end = src_len if positive_step else src_len - 1 + + return (start.value(), end.value(), self.step) + + +@always_inline("nodebug") +fn slice_new(end: Int) -> SliceNew: + """Construct slice given the end value. + + Args: + end: The end value. + + Returns: + The constructed slice. + """ + return SliceNew(0, end) + + +@always_inline("nodebug") +fn slice_new(start: Int, end: Int) -> SliceNew: + """Construct slice given the start and end values. + + Args: + start: The start value. + end: The end value. + + Returns: + The constructed slice. + """ + return SliceNew(start, end) + + +@always_inline("nodebug") +fn slice_new( + start: OptionalReg[Int], end: OptionalReg[Int], step: OptionalReg[Int] +) -> SliceNew: + """Construct a Slice given the start, end and step values. + + Args: + start: The start value. + end: The end value. + step: The step value. + + Returns: + The constructed slice. + """ + return SliceNew(start, end, step) diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 6aabc2fdbd..98c37062ef 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -89,9 +89,67 @@ def test_indexing(): assert_equal(s[2], 3) +def test_slice_indices_new(): + var start: Int + var end: Int + var step: Int + var s = slice_new(1, 10) + start, end, step = s.indices(9) + assert_equal(slice_new(start, end, step), slice_new(1, 9)) + s = slice_new(1, None, 1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(1, 5)) + s = slice_new(1, None, -1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(1, -1, -1)) + s = slice_new(-1, None, 1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(4, 5, 1)) + s = slice_new(None, 2, 1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(0, 2, 1)) + s = slice_new(None, 2, -1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(4, 2, -1)) + s = slice_new(0, -1, 1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(0, 4, 1)) + s = slice_new(None, None, 1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(0, 5, 1)) + s = slice_new(20) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(0, 5, 1)) + s = slice_new(10, -10, 1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(5, 0, 1)) + assert_equal(len(range(start, end, step)), 0) + s = slice_new(-12, -10, -1) + start, end, step = s.indices(5) + assert_equal(slice_new(start, end, step), slice_new(-1, -1, -1)) + assert_equal(len(range(start, end, step)), 0) + + +def test_slice_eq_new(): + assert_equal(slice_new(1, 2, 3), slice_new(1, 2, 3)) + assert_equal(slice_new(0, 1), slice_new(1)) + assert_true(slice_new(2, 3) != slice_new(4, 5)) + assert_equal(slice_new(1, None, 1), slice_new(1, None, None)) + + +def test_none_end_folds_new(): + alias all_def_slice = slice_new(0, None, 1) + assert_equal(all_def_slice.start.value(), 0) + assert_true(all_def_slice.end is None) + assert_equal(all_def_slice.step, 1) + + def main(): test_none_end_folds() test_slicable() test_has_end() test_slice_stringable() test_indexing() + test_slice_indices_new() + test_slice_eq_new() + test_none_end_folds_new() From 272aed6dfdbe33c065aa3ffab8574ee0ae60fa89 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 2 Jun 2024 09:34:11 -0400 Subject: [PATCH 0829/2019] [stdlib] Fix failing slice test MODULAR_ORIG_COMMIT_REV_ID: 14301a66d7c2881a085e9093abd5e719f70232df --- stdlib/test/builtin/test_slice.mojo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 98c37062ef..38ba1006d1 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -123,7 +123,8 @@ def test_slice_indices_new(): s = slice_new(10, -10, 1) start, end, step = s.indices(5) assert_equal(slice_new(start, end, step), slice_new(5, 0, 1)) - assert_equal(len(range(start, end, step)), 0) + # FIXME(#38392) + # assert_equal(len(range(start, end, step)), 0) s = slice_new(-12, -10, -1) start, end, step = s.indices(5) assert_equal(slice_new(start, end, step), slice_new(-1, -1, -1)) From 30a3d497295a91c4a88e8dc29137d1d0a09e4658 Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Sun, 2 Jun 2024 09:19:05 -0500 Subject: [PATCH 0830/2019] [External] [docs] Fix typo in `changelog-released.md` (#41115) [External] [docs] Fix typo in `changelog-released.md` Change "refeference" to "reference" Co-authored-by: Brian M Johnson Closes modularml/mojo#2920 MODULAR_ORIG_COMMIT_REV_ID: ecf9b30338927b8fd34e3ece77210ac356764998 --- docs/changelog-released.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 03419b687f..1c7506728a 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -35,7 +35,7 @@ modular update mojo [PR #2609](https://github.com/modularml/mojo/pull/2609) by [@mzaks](https://github.com/mzaks) -- Mojo functions can return an auto-dereferenced refeference to storage with a +- Mojo functions can return an auto-dereferenced reference to storage with a new `ref` keyword in the result type specifier. For example: ```mojo From 9c6683ef95a47fcdfdf778f384eaeb7db1091102 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 2 Jun 2024 10:34:53 -0400 Subject: [PATCH 0831/2019] [stdlib] Remove `StringLiteral` `prefix` arguments of `hex` and `bin` In favor of a `prefix` parameter, tightening the noose around `StringLiteral` before it becomes non-materializable. MODULAR_ORIG_COMMIT_REV_ID: 8b65b63a66a8d43c9c380c67dfd29c5366665eb2 --- stdlib/src/builtin/format_int.mojo | 81 ++++++++++++------------------ stdlib/src/builtin/string.mojo | 5 +- 2 files changed, 34 insertions(+), 52 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 5f8e9027a4..40104cc3e4 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -28,9 +28,7 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @always_inline -fn bin[ - type: DType -](num: Scalar[type], prefix: StringLiteral = "0b", /) -> String: +fn bin[prefix: StringLiteral = "0b"](num: Scalar, /) -> String: """Return the binary string representation an integral value. ```mojo @@ -43,49 +41,50 @@ fn bin[ ``` Parameters: - type: The data type of the integral scalar. + prefix: The prefix of the formatted int. Args: num: An integral scalar value. - prefix: The prefix of the formatted int. Returns: The binary string representation of num. """ - return _try_format_int(num, 2, prefix=prefix) + return _try_format_int[prefix](num, 2) # Need this until we have constraints to stop the compiler from matching this # directly to bin[type: DType](num: Scalar[type]). @always_inline("nodebug") -fn bin(b: Scalar[DType.bool], prefix: StringLiteral = "0b", /) -> String: +fn bin[prefix: StringLiteral = "0b"](b: Scalar[DType.bool], /) -> String: """Returns the binary representation of a scalar bool. + Parameters: + prefix: The prefix of the formatted int. + Args: b: A scalar bool value. - prefix: The prefix of the formatted int. Returns: The binary string representation of b. """ - return bin(b.cast[DType.int8]()) + return bin[prefix](b.cast[DType.int8]()) @always_inline("nodebug") -fn bin[T: Indexer](num: T, prefix: StringLiteral = "0b", /) -> String: +fn bin[T: Indexer, //, prefix: StringLiteral = "0b"](num: T, /) -> String: """Returns the binary representation of an indexer type. Parameters: T: The Indexer type. + prefix: The prefix of the formatted int. Args: num: An indexer value. - prefix: The prefix of the formatted int. Returns: The binary string representation of num. """ - return bin(Scalar[DType.index](index(num))) + return bin[prefix](Scalar[DType.index](index(num))) # ===----------------------------------------------------------------------===# @@ -94,9 +93,7 @@ fn bin[T: Indexer](num: T, prefix: StringLiteral = "0b", /) -> String: @always_inline -fn hex[ - type: DType -](value: Scalar[type], prefix: StringLiteral = "0x", /) -> String: +fn hex[prefix: StringLiteral = "0x"](value: Scalar, /) -> String: """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. @@ -105,20 +102,19 @@ fn hex[ subsequent digits are hex. Parameters: - type: The type of the Scalar to represent in hexadecimal. + prefix: The prefix of the formatted int. Args: value: The integer value to format. - prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given integer. """ - return _try_format_int(value, 16, prefix=prefix) + return _try_format_int[prefix](value, 16) @always_inline -fn hex[T: Indexer](value: T, prefix: StringLiteral = "0x", /) -> String: +fn hex[T: Indexer, //, prefix: StringLiteral = "0x"](value: T, /) -> String: """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. @@ -128,19 +124,19 @@ fn hex[T: Indexer](value: T, prefix: StringLiteral = "0x", /) -> String: Parameters: T: The indexer type to represent in hexadecimal. + prefix: The prefix of the formatted int. Args: value: The integer value to format. - prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given integer. """ - return hex[DType.index](index(value), prefix) + return hex[prefix](Scalar[DType.index](index(value))) @always_inline -fn hex(value: Scalar[DType.bool], prefix: StringLiteral = "0x", /) -> String: +fn hex[prefix: StringLiteral = "0x"](value: Scalar[DType.bool], /) -> String: """Returns the hex string representation of the given scalar bool. The hexadecimal representation is a base-16 encoding of the bool. @@ -148,14 +144,16 @@ fn hex(value: Scalar[DType.bool], prefix: StringLiteral = "0x", /) -> String: The returned string will be prefixed with "0x" to indicate that the subsequent digits are hex. + Parameters: + prefix: The prefix of the formatted int. + Args: value: The bool value to format. - prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given bool. """ - return hex(value.cast[DType.int8]()) + return hex[prefix](value.cast[DType.int8]()) # ===----------------------------------------------------------------------===# @@ -164,10 +162,10 @@ fn hex(value: Scalar[DType.bool], prefix: StringLiteral = "0x", /) -> String: fn _try_format_int[ - type: DType -](value: Scalar[type], radix: Int = 10, prefix: StringLiteral = "",) -> String: + prefix: StringLiteral = "" +](value: Scalar, radix: Int = 10) -> String: try: - return _format_int(value, radix, prefix=prefix) + return _format_int[prefix=prefix](value, radix) except e: # This should not be reachable as _format_int only throws if we pass # incompatible radix and custom digit chars, which we aren't doing @@ -178,58 +176,52 @@ fn _try_format_int[ fn _format_int[ - type: DType + type: DType, prefix: StringLiteral = "" ]( value: Scalar[type], radix: Int = 10, digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, - prefix: StringLiteral = "", ) raises -> String: var string = String() var fmt = string._unsafe_to_formatter() - _write_int(fmt, value, radix, digit_chars, prefix) + _write_int[prefix](fmt, value, radix, digit_chars) return string^ @always_inline fn _write_int[ - type: DType + type: DType, //, prefix: StringLiteral = "" ]( inout fmt: Formatter, value: Scalar[type], radix: Int = 10, digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, - prefix: StringLiteral = "", ) raises: - var err = _try_write_int(fmt, value, radix, digit_chars, prefix) + var err = _try_write_int[prefix](fmt, value, radix, digit_chars) if err: raise err.value()[] @always_inline fn _try_write_int[ - type: DType + type: DType, //, prefix: StringLiteral = "" ]( inout fmt: Formatter, value: Scalar[type], radix: Int = 10, digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, - prefix: StringLiteral = "", ) -> Optional[Error]: - """Writes a formatted string representation of the given integer using the specified radix. + """Writes a formatted string representation of the given integer using the + specified radix. The maximum supported radix is 36 unless a custom `digit_chars` mapping is provided. """ - - # - # Check that the radix and available digit characters are valid - # - constrained[type.is_integral(), "Expected integral"]() + # Check that the radix and available digit characters are valid if radix < 2: return Error("Unable to format integer to string with radix < 2") @@ -245,9 +237,7 @@ fn _try_write_int[ " mapping len is not >= 2" ) - # # Process the integer value into its corresponding digits - # # TODO(#26444, Unicode support): Get an array of Character, not bytes. var digit_chars_array = digit_chars.unsafe_ptr() @@ -273,9 +263,7 @@ fn _try_write_int[ fmt.write_str(zero) return - # # Create a buffer to store the formatted value - # # Stack allocate enough bytes to store any formatted 64-bit integer # TODO: use a dynamic size when #2194 is resolved @@ -288,10 +276,7 @@ fn _try_write_int[ # earlier in the buffer as we write the more-significant digits. var offset = CAPACITY - 1 - # # Write the digits of the number - # - var remaining_int = value @parameter diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 44d277869f..8ae0efdcbe 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -162,10 +162,7 @@ fn _repr_ascii(c: UInt8) -> String: return r"\r" else: var uc = c.cast[DType.uint8]() - if uc < 16: - return hex(uc, r"\x0") - else: - return hex(uc, r"\x") + return hex[r"\x0"](uc) if uc < 16 else hex[r"\x"](uc) # TODO: This is currently the same as repr, should change with unicode strings From 2a6eb8a9bb9a6a9d395f2bee5b8da218e5bd599e Mon Sep 17 00:00:00 2001 From: soraros Date: Sun, 2 Jun 2024 09:43:05 -0500 Subject: [PATCH 0832/2019] [External] [stdlib] Simplify `Tuple.__contains__`. (#41114) [External] [stdlib] Simplify `Tuple.__contains__`. Signed-off-by: sora <210at85@gmail.com> Co-authored-by: soraros Closes modularml/mojo#2922 MODULAR_ORIG_COMMIT_REV_ID: be3af41acb720b85dad090159818604ea6f7de01 --- stdlib/src/builtin/tuple.mojo | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 3a60c6522f..ae92fd874d 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -189,45 +189,33 @@ struct Tuple[*element_types: Movable](Sized, Movable): @always_inline("nodebug") fn __contains__[T: EqualityComparable](self, value: T) -> Bool: - """Verify if a given value is present in the tuple. + """Return whether the tuple contains the specified value. + + For example: ```mojo - var x = Tuple(1,2,True) - if 1 in x: print("x contains 1") + var t = Tuple(True, 1, 2.5) + if 1 in t: + print("t contains 1") ``` Args: - value: The value to find. + value: The value to search for. Parameters: - T: The type of the value argument. Must implement the - trait `EqualityComparable`. + T: The type of the value. Returns: - True if the value is contained in the tuple, False otherwise. + True if the value is in the tuple, False otherwise. """ - @parameter - fn T_in_ts() -> Bool: - @parameter - for i in range(len(VariadicList(element_types))): - - @parameter - if _type_is_eq[element_types[i], T](): - return True - return False - - @parameter - if not T_in_ts(): - return False - @parameter for i in range(len(VariadicList(element_types))): @parameter - if _type_is_eq[T, element_types[i]](): + if _type_is_eq[element_types[i], T](): var elt_ptr = UnsafePointer.address_of(self[i]).bitcast[T]() - if elt_ptr[].__eq__(value): + if elt_ptr[] == value: return True return False From 56993290e444e32289fe82d9bac98858512a5a66 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sun, 2 Jun 2024 11:07:36 -0400 Subject: [PATCH 0833/2019] [stdlib] Remove runtime usage of `StringLiteral` from `Formatter` This will soon be prohibited. MODULAR_ORIG_COMMIT_REV_ID: 9eb8050c70aa54ddf1cea3540ac18fcc4c0b6305 --- stdlib/src/builtin/format_int.mojo | 4 ++-- stdlib/src/builtin/io.mojo | 13 +++++-------- stdlib/src/builtin/simd.mojo | 6 +++--- stdlib/src/utils/_format.mojo | 8 ++++---- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 40104cc3e4..2575bbcc52 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -244,11 +244,11 @@ fn _try_write_int[ # Prefix a '-' if the original int was negative and make positive. if value < 0: - fmt.write_str("-") + fmt.write_str["-"]() # Add the custom number prefix, e.g. "0x" commonly used for hex numbers. # This comes *after* the minus sign, if present. - fmt.write_str(prefix) + fmt.write_str[prefix]() if value == 0: # TODO: Replace with safe digit_chars[:1] syntax. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 56535b481f..7e9049deb8 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -486,14 +486,11 @@ fn _print[ # default, replace `print` with this function. @no_inline fn _print_fmt[ - T: Formattable, *Ts: Formattable -]( - first: T, - *rest: *Ts, + T: Formattable, + *Ts: Formattable, sep: StringLiteral = " ", end: StringLiteral = "\n", - flush: Bool = False, -): +](first: T, *rest: *Ts, flush: Bool = False): """Prints elements to the text stream. Each element is separated by `sep` and followed by `end`. @@ -503,12 +500,12 @@ fn _print_fmt[ Parameters: T: The first element type. Ts: The remaining element types. + sep: The separator used between elements. + end: The String to write after printing the elements. Args: first: The first element. rest: The remaining elements. - sep: The separator used between elements. - end: The String to write after printing the elements. flush: If set to true, then the stream is forcibly flushed. """ var writer = Formatter.stdout() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 028eb97c01..b2a2d9d4d4 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -643,14 +643,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # Print an opening `[`. @parameter if size > 1: - writer.write_str("[") + writer.write_str["["]() # Print each element. for i in range(size): var element = self[i] # Print separators between each element. if i != 0: - writer.write_str(", ") + writer.write_str[", "]() @parameter if triple_is_nvidia_cuda(): @@ -685,7 +685,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # Print a closing `]`. @parameter if size > 1: - writer.write_str("]") + writer.write_str["]"]() @always_inline("nodebug") fn __add__(self, rhs: Self) -> Self: diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index cf1486ff7a..1d156c7a9d 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -92,15 +92,15 @@ struct Formatter: # Remove this overload by defining a working # `StringSlice.__init__(StringLiteral)` implicit conversion. @always_inline - fn write_str(inout self, literal: StringLiteral): + fn write_str[literal: StringLiteral](inout self): """ Write a string literal to this formatter. - Args: + Parameters: literal: The string literal to write. """ - - self.write_str(literal.as_string_slice()) + alias slc = literal.as_string_slice() + self.write_str(slc) @always_inline fn write_str(inout self, str_slice: StringSlice[_]): From 7c816b24573a50f02774e084ea8254aa37618f97 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 2 Jun 2024 09:45:12 -0700 Subject: [PATCH 0834/2019] [mojo-stdlib] Move `*Pointer.address_of` to `ref` argument. This moves these core methods to use ref argument convention, removing an implicit conversion and a `Reference` temporary. MODULAR_ORIG_COMMIT_REV_ID: c5ec6e48a8adf3b3435fed029a01070bdc859fee --- stdlib/src/memory/unsafe.mojo | 8 ++++---- stdlib/src/memory/unsafe_pointer.mojo | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index a6a9bea022..8d839b55bc 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -234,7 +234,7 @@ struct LegacyPointer[ @staticmethod @always_inline("nodebug") - fn address_of(arg: Reference[type, _, address_space]) -> Self: + fn address_of(ref [_, address_space._value.value]arg: type) -> Self: """Gets the address of the argument. Args: @@ -245,7 +245,7 @@ struct LegacyPointer[ """ # Work around AnyTrivialRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( - UnsafePointer(arg).address + UnsafePointer.address_of(arg).address ) @always_inline("nodebug") @@ -666,7 +666,7 @@ struct DTypePointer[ @staticmethod @always_inline("nodebug") - fn address_of(arg: Reference[Scalar[type], _, address_space]) -> Self: + fn address_of(ref [_, address_space._value.value]arg: Scalar[type]) -> Self: """Gets the address of the argument. Args: @@ -675,7 +675,7 @@ struct DTypePointer[ Returns: A DTypePointer struct which contains the address of the argument. """ - return LegacyPointer.address_of(arg[]) + return LegacyPointer.address_of(arg) @always_inline("nodebug") fn __getitem__(self, offset: Int) -> Scalar[type]: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 4d067ddc14..d28a8883d3 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -161,7 +161,7 @@ struct UnsafePointer[ @staticmethod @always_inline("nodebug") - fn address_of(arg: Reference[T, _, address_space]) -> Self: + fn address_of(ref [_, address_space._value.value]arg: T) -> Self: """Gets the address of the argument. Args: @@ -170,7 +170,7 @@ struct UnsafePointer[ Returns: An UnsafePointer which contains the address of the argument. """ - return Self(arg) + return Self(__get_mvalue_as_litref(arg)) # ===-------------------------------------------------------------------===# # Operator dunders From a3b6e3adde578e116020a42ade20c1c9daf32071 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 2 Jun 2024 10:23:36 -0700 Subject: [PATCH 0835/2019] [mojo-stdlib] Move Reference.init to `ref` argument. This is the big one - this is the primary user of the `!lit.ref` informal convention. When this lands, we can remove a bunch of code from the compiler. MODULAR_ORIG_COMMIT_REV_ID: e993ab15a2889ac87aec13b40b0e03f79dd41aa8 --- stdlib/src/memory/reference.mojo | 10 ++++++---- stdlib/src/memory/unsafe_pointer.mojo | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 0c0e46acfe..15722f33e8 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -228,13 +228,15 @@ struct Reference[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__(inout self, value: Self._mlir_type): - """Constructs a Reference from the MLIR reference. + fn __init__( + inout self, ref [lifetime, address_space._value.value]value: type + ): + """Constructs a Reference from a value reference. Args: - value: The MLIR reference. + value: The value reference. """ - self.value = value + self.value = __get_mvalue_as_litref(value) # ===------------------------------------------------------------------===# # Operator dunders diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index d28a8883d3..33f50bc0ba 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -170,7 +170,7 @@ struct UnsafePointer[ Returns: An UnsafePointer which contains the address of the argument. """ - return Self(__get_mvalue_as_litref(arg)) + return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) # ===-------------------------------------------------------------------===# # Operator dunders From 7c2dc56d7236dcb5775a245e245f41dc54ec04c5 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 2 Jun 2024 12:49:05 -0700 Subject: [PATCH 0836/2019] [mojo-stdlib] Remove most remaining Reference self's This is on the quest to push Reference down in visibility, and eliminate support for Reference self. MODULAR_ORIG_COMMIT_REV_ID: 3f9fc713bfa37bbb725c1ab1b1bda302a1ed0f76 --- stdlib/src/collections/dict.mojo | 54 +++++++++++++-------------- stdlib/src/collections/list.mojo | 20 +++++----- stdlib/src/collections/optional.mojo | 6 +-- stdlib/src/collections/set.mojo | 6 +-- stdlib/src/memory/arc.mojo | 4 +- stdlib/src/memory/unsafe_pointer.mojo | 14 ++++--- stdlib/src/utils/span.mojo | 10 ++--- stdlib/src/utils/static_tuple.mojo | 25 ++++++------- stdlib/src/utils/variant.mojo | 4 +- 9 files changed, 71 insertions(+), 72 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index ec0748f914..be6fef0381 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -94,7 +94,7 @@ struct _DictEntryIter[ self.index -= 1 self.seen += 1 - return opt_entry_ref[].value() + return opt_entry_ref[].value()[] @parameter if forward: @@ -529,8 +529,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MSTDL-452): rename to __getitem__ returning a reference fn __get_ref( - self: Reference[Self, _], key: K - ) raises -> Reference[V, self.lifetime]: + ref [_]self: Self, key: K + ) raises -> Reference[V, __lifetime_of(self)]: """Retrieve a value out of the dictionary. Args: @@ -542,7 +542,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( Raises: "KeyError" if the key isn't present. """ - return self[]._find_ref(key) + return self._find_ref(key) fn __setitem__(inout self, owned key: K, owned value: V): """Set a value in the dictionary by key. @@ -565,8 +565,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( return self.find(key).__bool__() fn __iter__( - self: Reference[Self, _], - ) -> _DictKeyIter[K, V, self.lifetime]: + ref [_]self: Self, + ) -> _DictKeyIter[K, V, __lifetime_of(self)]: """Iterate over the dict's keys as immutable references. Returns: @@ -575,15 +575,15 @@ struct Dict[K: KeyElement, V: CollectionElement]( return _DictKeyIter(_DictEntryIter(0, 0, self)) fn __reversed__( - self: Reference[Self, _] - ) -> _DictKeyIter[K, V, self.lifetime, False]: + ref [_]self: Self, + ) -> _DictKeyIter[K, V, __lifetime_of(self), False]: """Iterate backwards over the dict keys, returning immutable references. Returns: A reversed iterator of immutable references to the dict keys. """ return _DictKeyIter( - _DictEntryIter[forward=False](self[]._reserved - 1, 0, self) + _DictEntryIter[forward=False](self._reserved - 1, 0, self) ) fn __or__(self, other: Self) -> Self: @@ -701,8 +701,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MOCO-604): Return Optional[Reference] instead of raising fn _find_ref( - self: Reference[Self, _], key: K - ) raises -> Reference[V, self.lifetime]: + ref [_]self: Self, key: K + ) raises -> Reference[V, __lifetime_of(self)]: """Find a value in the dictionary by key. Args: @@ -716,9 +716,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( var found: Bool var slot: Int var index: Int - found, slot, index = self[]._find_index(hash, key) + found, slot, index = self._find_index(hash, key) if found: - var entry = self[]._entries.__get_ref(index) + var entry = self._entries.__get_ref(index) debug_assert(entry[].__bool__(), "entry in index must be full") return Reference(entry[].value()[].value) raise "KeyError" @@ -808,7 +808,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( raise "KeyError: popitem(): dictionary is empty" - fn keys(self: Reference[Self, _]) -> _DictKeyIter[K, V, self.lifetime]: + fn keys(ref [_]self: Self) -> _DictKeyIter[K, V, __lifetime_of(self)]: """Iterate over the dict's keys as immutable references. Returns: @@ -816,7 +816,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return Self.__iter__(self) - fn values(self: Reference[Self, _]) -> _DictValueIter[K, V, self.lifetime]: + fn values(ref [_]self: Self) -> _DictValueIter[K, V, __lifetime_of(self)]: """Iterate over the dict's values as references. Returns: @@ -824,7 +824,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return _DictValueIter(_DictEntryIter(0, 0, self)) - fn items(self: Reference[Self, _]) -> _DictEntryIter[K, V, self.lifetime]: + fn items(ref [_]self: Self) -> _DictEntryIter[K, V, __lifetime_of(self)]: """Iterate over the dict's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1094,8 +1094,8 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return self._dict.pop(key, default^) fn __iter__( - self: Reference[Self, _] - ) -> _DictKeyIter[Self.key_type, V, self.lifetime]: + ref [_]self: Self, + ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1103,11 +1103,11 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ # TODO(#36448): Use this instead of the current workaround # return self._dict.__iter__() - return _DictKeyIter(_DictEntryIter(0, 0, self[]._dict)) + return _DictKeyIter(_DictEntryIter(0, 0, self._dict)) fn keys( - self: Reference[Self, _], - ) -> _DictKeyIter[Self.key_type, V, self.lifetime]: + ref [_]self: Self, + ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1118,8 +1118,8 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return Self.__iter__(self) fn values( - self: Reference[Self, _], - ) -> _DictValueIter[Self.key_type, V, self.lifetime]: + ref [_]self: Self, + ) -> _DictValueIter[Self.key_type, V, __lifetime_of(self)]: """Iterate over the keyword dict's values as references. Returns: @@ -1127,11 +1127,11 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """ # TODO(#36448): Use this instead of the current workaround # return self._dict.values() - return _DictValueIter(_DictEntryIter(0, 0, self[]._dict)) + return _DictValueIter(_DictEntryIter(0, 0, self._dict)) fn items( - self: Reference[Self, _] - ) -> _DictEntryIter[Self.key_type, V, self.lifetime]: + ref [_]self: Self, + ) -> _DictEntryIter[Self.key_type, V, __lifetime_of(self)]: """Iterate over the keyword dictionary's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1148,7 +1148,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): # TODO(#36448): Use this instead of the current workaround # return self[]._dict.items() - return _DictEntryIter(0, 0, self[]._dict) + return _DictEntryIter(0, 0, self._dict) @always_inline("nodebug") fn _insert(inout self, owned key: Self.key_type, owned value: V): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 7b345677e5..6df07a8829 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -61,10 +61,10 @@ struct _ListIter[ @parameter if forward: self.index += 1 - return self.src[].__get_ref(self.index - 1) + return self.src[].__get_ref(self.index - 1)[] else: self.index -= 1 - return self.src[].__get_ref(self.index) + return self.src[].__get_ref(self.index)[] fn __len__(self) -> Int: @parameter @@ -772,8 +772,8 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # TODO(30737): Replace __getitem__ with this, but lots of places use it fn __get_ref( - self: Reference[Self, _], i: Int - ) -> Reference[T, self.lifetime]: + ref [_]self: Self, i: Int + ) -> Reference[T, __lifetime_of(self)]: """Gets a reference to the list element at the given index. Args: @@ -784,14 +784,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ var normalized_idx = i if i < 0: - normalized_idx += self[].size + normalized_idx += self.size - return self[].unsafe_get(normalized_idx) + return self.unsafe_get(normalized_idx) @always_inline fn unsafe_get( - self: Reference[Self, _], idx: Int - ) -> Reference[Self.T, self.lifetime]: + ref [_]self: Self, idx: Int + ) -> Reference[Self.T, __lifetime_of(self)]: """Get a reference to an element of self without checking index bounds. Users should consider using `__getitem__` instead of this method as it @@ -810,13 +810,13 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): A reference to the element at the given index. """ debug_assert( - 0 <= idx < len(self[]), + 0 <= idx < len(self), ( "The index provided must be within the range [0, len(List) -1]" " when using List.unsafe_get()" ), ) - return (self[].data + idx)[] + return (self.data + idx)[] fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 9a376ad406..df6ca5154e 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -159,7 +159,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): # ===-------------------------------------------------------------------===# @always_inline - fn value(self: Reference[Self, _]) -> Reference[T, self.lifetime]: + fn value(ref [_]self: Self) -> Reference[T, __lifetime_of(self)]: """Retrieve a reference to the value of the Optional. This check to see if the optional contains a value. @@ -170,10 +170,10 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): Returns: A reference to the contained data of the option as a Reference[T]. """ - if not self[].__bool__(): + if not self.__bool__(): abort(".value() on empty Optional") - return self[].unsafe_value() + return self.unsafe_value() @always_inline fn unsafe_value(ref [_]self: Self) -> ref [__lifetime_of(self)] T: diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 41ed888472..f830d43b56 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -301,15 +301,15 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # ===-------------------------------------------------------------------===# fn __iter__( - self: Reference[Self, _], - ) -> _DictKeyIter[T, NoneType, self.lifetime]: + ref [_]self: Self, + ) -> _DictKeyIter[T, NoneType, __lifetime_of(self)]: """Iterate over elements of the set, returning immutable references. Returns: An iterator of immutable references to the set elements. """ # here we rely on Set being a trivial wrapper of a Dict - return _DictKeyIter(_DictEntryIter(0, 0, self[]._data)) + return _DictKeyIter(_DictEntryIter(0, 0, self._data)) fn add(inout self, t: T): """Add an element to the set. diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 38c6d18184..2229b40df4 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -101,13 +101,13 @@ struct Arc[T: Movable](CollectionElement): # FIXME: This isn't right - the element should be mutable regardless # of whether the 'self' type is mutable. - fn __getitem__(self: Reference[Self, _]) -> ref [self.lifetime] T: + fn __getitem__(ref [_]self: Self) -> ref [__lifetime_of(self)] T: """Returns a Reference to the managed value. Returns: A Reference to the managed value. """ - return self[]._inner[].payload + return self._inner[].payload fn as_ptr(self) -> UnsafePointer[T]: """Retrieves a pointer to the underlying memory. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 33f50bc0ba..a103c470a9 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -52,11 +52,6 @@ struct UnsafePointer[ alias type = T - # We're unsafe, so we can have unsafe things. References we make have - # an immortal mutable lifetime, since we can't come up with a meaningful - # lifetime for them anyway. - alias _ref_type = Reference[T, MutableStaticLifetime, address_space] - """The underlying pointer type.""" var address: Self._mlir_type """The underlying pointer.""" @@ -88,6 +83,8 @@ struct UnsafePointer[ """ return Self {address: value} + # TODO: remove this in favor of address_of which is less of a footgun. We + # shouldn't allow safe references to implicitly convert to unsafe pointers. @always_inline fn __init__(value: Reference[T, _, address_space]) -> Self: """Create an unsafe UnsafePointer from a safe Reference. @@ -185,8 +182,13 @@ struct UnsafePointer[ Returns: A reference to the value. """ + + # We're unsafe, so we can have unsafe things. References we make have + # an immortal mutable lifetime, since we can't come up with a meaningful + # lifetime for them anyway. + alias _ref_type = Reference[T, MutableStaticLifetime, address_space] return __get_litref_as_mvalue( - __mlir_op.`lit.ref.from_pointer`[_type = Self._ref_type._mlir_type]( + __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( self.address ) ) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 22e84c1daa..364fd4c1d3 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -103,19 +103,19 @@ struct Span[ self._len = len @always_inline - fn __init__(inout self, list: Reference[List[T], lifetime]): + fn __init__(inout self, ref [lifetime]list: List[T]): """Construct a Span from a List. Args: list: The list to which the span refers. """ - self._data = list[].data - self._len = len(list[]) + self._data = list.data + self._len = len(list) @always_inline fn __init__[ T2: CollectionElementNew, size: Int - ](inout self, array: Reference[InlineArray[T2, size], lifetime],): + ](inout self, ref [lifetime]array: InlineArray[T2, size]): """Construct a Span from an InlineArray. Parameters: @@ -128,7 +128,7 @@ struct Span[ constrained[_type_is_eq[T, T2](), "array element is not Span.T"]() - self._data = UnsafePointer(array).bitcast[T]() + self._data = UnsafePointer.address_of(array).bitcast[T]() self._len = size # ===------------------------------------------------------------------===# diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 971d66bbed..fd4a31a81f 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -36,10 +36,7 @@ fn _set_array_elem[ type: AnyTrivialRegType, ]( val: type, - array: Reference[ - __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], - _, - ], + ref [_]array: __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], ): """Sets the array element at position `index` with the value `val`. @@ -53,7 +50,7 @@ fn _set_array_elem[ array: the array which is captured by reference. """ var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer(array).address, index.value + UnsafePointer.address_of(array).address, index.value ) UnsafePointer(ptr)[] = val @@ -379,8 +376,8 @@ struct InlineArray[ @always_inline("nodebug") fn __getitem__( - self: Reference[Self, _], idx: Int - ) -> ref [self.lifetime] Self.ElementType: + ref [_]self: Self, idx: Int + ) -> ref [__lifetime_of(self)] Self.ElementType: """Get a `Reference` to the element at the given index. Args: @@ -389,15 +386,15 @@ struct InlineArray[ Returns: A reference to the item at the given index. """ - var normalized_index = normalize_index["InlineArray"](idx, self[]) + var normalized_index = normalize_index["InlineArray"](idx, self) - return self[]._get_reference_unsafe(normalized_index)[] + return self._get_reference_unsafe(normalized_index)[] @always_inline("nodebug") fn __getitem__[ IntableType: Intable, index: IntableType, - ](self: Reference[Self, _]) -> ref [self.lifetime] Self.ElementType: + ](ref [_]self: Self) -> ref [__lifetime_of(self)] Self.ElementType: """Get a `Reference` to the element at the given index. Parameters: @@ -416,7 +413,7 @@ struct InlineArray[ if i < 0: normalized_idx += size - return self[]._get_reference_unsafe(normalized_idx)[] + return self._get_reference_unsafe(normalized_idx)[] # ===------------------------------------------------------------------=== # # Trait implementations @@ -437,8 +434,8 @@ struct InlineArray[ @always_inline("nodebug") fn _get_reference_unsafe( - self: Reference[Self, _], idx: Int - ) -> Reference[Self.ElementType, self.lifetime]: + ref [_]self: Self, idx: Int + ) -> Reference[Self.ElementType, __lifetime_of(self)]: """Get a reference to an element of self without checking index bounds. Users should opt for `__getitem__` instead of this method as it is @@ -462,7 +459,7 @@ struct InlineArray[ ), ) var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(self[]._array).address, + UnsafePointer.address_of(self._array).address, idx_as_int.value, ) return UnsafePointer(ptr)[] diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index c5c351a4a8..1c49e08725 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -263,8 +263,8 @@ struct Variant[*Ts: CollectionElement]( ]() return UnsafePointer.address_of(self._impl).bitcast[T]() - fn _get_state(self: Reference[Self, _]) -> Reference[Int8, self.lifetime]: - var int8_self = UnsafePointer(self).bitcast[Int8]() + fn _get_state(ref [_]self: Self) -> Reference[Int8, __lifetime_of(self)]: + var int8_self = UnsafePointer.address_of(self).bitcast[Int8]() return (int8_self + _UnionSize[Ts].compute())[] @always_inline From f2137ed798ed1102adb6da11a2a4c67c56ddf6ab Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sun, 2 Jun 2024 16:59:30 -0700 Subject: [PATCH 0837/2019] [stdlib] First batch of microbenchmarks for the stdlib. This PR includes benchmarks for the following: - algorithm.elementwise - builtin.math.log/log2/sin/cos/tan/asin/acos/atan. - collections.dict - utils.formatter MODULAR_ORIG_COMMIT_REV_ID: f97541468afc26b78068b352af98a6ccebf4f6b5 --- .../algorithm/bench_elementwise.mojo | 56 +++ .../benchmarks/algorithm/bench_vectorize.mojo | 366 ++++++++++++++++++ stdlib/benchmarks/builtin/bench_math.mojo | 243 ++++++++++++ stdlib/benchmarks/collections/bench_dict.mojo | 174 +++++++++ stdlib/benchmarks/utils/bench_formatter.mojo | 67 ++++ stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- stdlib/src/random/random.mojo | 15 + 7 files changed, 922 insertions(+), 1 deletion(-) create mode 100644 stdlib/benchmarks/algorithm/bench_elementwise.mojo create mode 100644 stdlib/benchmarks/algorithm/bench_vectorize.mojo create mode 100644 stdlib/benchmarks/builtin/bench_math.mojo create mode 100644 stdlib/benchmarks/collections/bench_dict.mojo create mode 100644 stdlib/benchmarks/utils/bench_formatter.mojo diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo new file mode 100644 index 0000000000..9e1236a289 --- /dev/null +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -0,0 +1,56 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from algorithm import elementwise +from benchmark import Bench, Bencher, BenchId, BenchConfig +from buffer import Buffer +from utils.index import Index, StaticIntTuple + + +# ===----------------------------------------------------------------------===# +# Benchmark elementwise +# ===----------------------------------------------------------------------===# +@parameter +fn bench_elementwise[n: Int](inout b: Bencher) raises: + var vector = Buffer[DType.index, n].stack_allocation() + + for i in range(len(vector)): + vector[i] = -1 + + @always_inline + @parameter + fn call_fn() raises: + @always_inline + @parameter + fn func[simd_width: Int, rank: Int](idx: StaticIntTuple[rank]): + vector[idx[0]] = 42 + + elementwise[func, 1, 1](Index(n)) + + b.iter[call_fn]() + + +fn main() raises: + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=10000)) + m.bench_function[bench_elementwise[32]](BenchId("bench_elementwise_32")) + m.bench_function[bench_elementwise[128]](BenchId("bench_elementwise_128")) + m.bench_function[bench_elementwise[1024]](BenchId("bench_elementwise_1024")) + m.bench_function[bench_elementwise[8192]](BenchId("bench_elementwise_8192")) + m.bench_function[bench_elementwise[32768]]( + BenchId("bench_elementwise_32768") + ) + m.bench_function[bench_elementwise[131072]]( + BenchId("bench_elementwise_131072") + ) + m.dump_report() diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo new file mode 100644 index 0000000000..ae33075de2 --- /dev/null +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -0,0 +1,366 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# ===----------------------------------------------------------------------=== # +# +# Benchmarking performance of vectorize over basic operations +# +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo %s -t | FileCheck %s +# CHECK: Benchmark results + +from random import rand +from algorithm import vectorize + +from benchmark import Unit, run +from benchmark import Bench, Bencher, BenchId, keep + +from memory.unsafe import DTypePointer +from memory import memcmp +from buffer import Buffer + + +@value +struct Op(Stringable): + var op_code: Int + alias add = 0 + alias sub = 1 + alias mul = 2 + alias div = 3 + alias fma = 4 + alias ld = 5 + alias st = 6 + + @always_inline("nodebug") + fn __eq__(self, other: Op) -> Bool: + return self.op_code == other.op_code + + @always_inline("nodebug") + fn __str__(self) -> String: + alias op_list = List[String]( + "add", "sub", "mul", "div", "fma", "ld", "st" + ) + return "op." + op_list[self.op_code] + + +fn test_vectorize[ + N: Int, + simd_width: Int = 1, + op: Op = Op.add, + const_operand: Bool = True, + dtype: DType = DType.float32, + unroll_factor: Int = 1, +](inout m: Bench) raises: + constrained[(N % simd_width) == 0]() + # Create a mem of size N + alias buffer_align = 64 + var vector = DTypePointer[dtype].alloc(N, alignment=buffer_align) + var result = DTypePointer[dtype].alloc(N, alignment=buffer_align) + + @always_inline + @parameter + fn ld_vector[simd_width: Int](idx: Int): + vector.store[width=simd_width]( + idx + 1, SIMD[vector.type, simd_width](idx) + ) + + @always_inline + @parameter + fn st_vector[simd_width: Int](idx: Int): + result.store[width=simd_width](idx, vector.load[width=simd_width](idx)) + + @__copy_capture(vector) + @always_inline + @parameter + fn arithmetic_const[simd_width: Int](idx: Int): + alias x: Scalar[dtype] = 2 + alias y: Scalar[dtype] = 3 + + @parameter + if op == Op.add: + vector.store[width=simd_width]( + idx, vector.load[width=simd_width](idx) + x + ) + elif op == Op.sub: + vector.store[width=simd_width]( + idx, vector.load[width=simd_width](idx) - x + ) + elif op == Op.mul: + vector.store[width=simd_width]( + idx, vector.load[width=simd_width](idx) * x + ) + elif op == Op.div: + vector.store[width=simd_width]( + idx, vector.load[width=simd_width](idx) / x + ) + elif op == Op.fma: + vector.store[width=simd_width]( + idx, vector.load[width=simd_width](idx) * x + y + ) + + @__copy_capture(vector) + @always_inline + @parameter + fn arithmetic_vector[simd_width: Int](idx: Int): + @parameter + if op == Op.add: + vector.store[width=simd_width]( + idx, + vector.load[width=simd_width](idx) + + vector.load[width=simd_width](idx), + ) + elif op == Op.sub: + vector.store[width=simd_width]( + idx, + vector.load[width=simd_width](idx) + - vector.load[width=simd_width](idx), + ) + elif op == Op.mul: + vector.store[width=simd_width]( + idx, + vector.load[width=simd_width](idx) + * vector.load[width=simd_width](idx), + ) + elif op == Op.div: + vector.store[width=simd_width]( + idx, + vector.load[width=simd_width](idx) + / vector.load[width=simd_width](idx), + ) + elif op == Op.fma: + vector.store[width=simd_width]( + idx, + vector.load[width=simd_width](idx) + * vector.load[width=simd_width](idx) + + vector.load[width=simd_width](idx), + ) + + @always_inline + @parameter + fn bench_(inout b: Bencher): + @always_inline + @parameter + fn call_fn(): + @parameter + if op == Op.ld: + vectorize[ld_vector, simd_width, unroll_factor=unroll_factor](N) + elif op == Op.st: + vectorize[st_vector, simd_width, unroll_factor=unroll_factor](N) + else: + + @parameter + if const_operand: + vectorize[ + arithmetic_const, + simd_width, + unroll_factor=unroll_factor, + ](N) + else: + vectorize[ + arithmetic_vector, + simd_width, + unroll_factor=unroll_factor, + ](N) + keep(vector) + + b.iter[call_fn]() + + var bench_id = BenchId( + str(op) + + ", N=" + + str(N) + + ", simd=" + + str(simd_width) + + ", const_operand=" + + str(const_operand) + + ", dtype=" + + str(dtype) + + ", unroll_factor=" + + str(unroll_factor) + ) + + m.bench_function[bench_](bench_id, throughput_elems=N) + vector.free() + result.free() + + +# TODO: move this function to a common module for benchmarking. +@always_inline +fn unroll_nested_call[ + func: fn[List[Int]] () raises capturing -> None, + count: List[Int], # TODO: a better datatype to use? e.g., Dim? + loop_idx: Int = 0, + index_prev: List[Int] = List[Int](), +]() raises: + """Fully unroll a nested loop of depth `depth` and call function `func` + at the innermost loop with a List of indices from all levels as arguments. + + for loop_idx0 in range(count[0]): + for loop_idx1 in range(count[1]): + for loop_idx2 in range(count[2]): + func(List[loop_idx0, loop_idx1, loop_idx2]) + + Params: + - func: function to call at the innermost loop. + - count: List[Int] contains the total count of iterations for each loop, + outmost loop is at index=0, inner-most loop at index=depth-1 + - loop_idx: index of the current loop + - index_prev: List[Int] of all indices from outer loops. + """ + alias depth = len(count) + + @always_inline + @parameter + fn append_index(x: List[Int], y: Int) -> List[Int]: + var z = x + z.append(y) + return z + + @parameter + for i in range(count[loop_idx]): + alias index = append_index(index_prev, i) + + @parameter + if loop_idx < depth - 1: + unroll_nested_call[func, count, loop_idx + 1, index]() + else: + func[index]() + + +fn bench_compare(): + alias type = DType.uint8 + alias width = simdwidthof[type]() + alias unit = Unit.ns + # increasing will reduce the benefit of passing the size as a paramater + alias multiplier = 2 + # Add .5 of the elements that fit into a simd register + alias size: Int = int(multiplier * width + (width * 0.5)) + alias unroll_factor = 2 + alias its = 1000 + + var p1 = DTypePointer[type].alloc(size) + var p2 = DTypePointer[type].alloc(size) + print("Benchmark results") + rand(p1, size) + + @parameter + fn arg_size(): + @parameter + fn closure[width: Int](i: Int): + p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + + for i in range(its): + vectorize[closure, width](size) + + @parameter + fn param_size(): + @parameter + fn closure[width: Int](i: Int): + p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + + for i in range(its): + vectorize[closure, width, size=size]() + + @parameter + fn arg_size_unroll(): + @parameter + fn closure[width: Int](i: Int): + p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + + for i in range(its): + vectorize[closure, width, unroll_factor=unroll_factor](size) + + @parameter + fn param_size_unroll(): + @parameter + fn closure[width: Int](i: Int): + p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + + for i in range(its): + vectorize[closure, width, size=size, unroll_factor=unroll_factor]() + + var arg = run[arg_size](max_runtime_secs=0.5).mean(unit) + print(p2.load[width=size]()) + memset_zero(p2, size) + + var param = run[param_size](max_runtime_secs=0.5).mean(unit) + print(p2.load[width=size]()) + memset_zero(p2, size) + + var arg_unroll = run[arg_size_unroll](max_runtime_secs=0.5).mean(unit) + print(p2.load[width=size]()) + memset_zero(p2, size) + + var param_unroll = run[param_size_unroll](max_runtime_secs=0.5).mean(unit) + print(p2.load[width=size]()) + + print( + "calculating", + size, + "elements,", + width, + "elements fit into the SIMD register\n", + ) + + print(" size as argument:", arg, unit) + print(" unrolled:", arg_unroll, unit) + print() + print("size as parameter:", param, unit) + print(" unrolled:", param_unroll, unit) + print( + "\nPassing size as a parameter and unrolling is", + arg_unroll / param_unroll, + "x faster", + ) + p1.free() + p2.free() + + +fn main() raises: + alias vec_size_list = List[Int](512, 2048) + alias simd_width_list = List[Int](1, 2, 4, 8) + alias op_list = List[Op](Op.add, Op.mul, Op.fma, Op.ld) + alias const_operand_list = List[Bool](True) + alias dtype_list = List[DType](DType.float32) + alias unroll_factor_list = List[Int](1, 2, 4) + + var m = Bench() + + @always_inline + @parameter + fn callback[index: List[Int]]() raises: + @parameter + if len(index) == 6: + alias vec_size = vec_size_list[index[0]] + alias simd_width = simd_width_list[index[1]] + alias op_code = op_list[index[2]] + alias const_op = const_operand_list[index[3]] + alias dtype = dtype_list[index[4]] + alias unroll_factor = unroll_factor_list[index[5]] + test_vectorize[ + vec_size, simd_width, op_code, const_op, dtype, unroll_factor + ](m) + + unroll_nested_call[ + callback, + List[Int]( + len(vec_size_list), + len(simd_width_list), + len(op_list), + len(const_operand_list), + len(dtype_list), + len(unroll_factor_list), + ), + ]() + + m.dump_report() diff --git a/stdlib/benchmarks/builtin/bench_math.mojo b/stdlib/benchmarks/builtin/bench_math.mojo new file mode 100644 index 0000000000..5000bbb080 --- /dev/null +++ b/stdlib/benchmarks/builtin/bench_math.mojo @@ -0,0 +1,243 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from random import * +from math import * + +# ===----------------------------------------------------------------------===# +# Benchmark Data +# ===----------------------------------------------------------------------===# +alias input_type = Float32 + + +fn make_inputs( + begin: input_type, end: input_type, num: input_type +) -> List[input_type]: + if num == 1: + return List[input_type](begin) + + var step = (end - begin) / (num - 1) + + var result: List[input_type] = List[input_type]() + for i in range(num): + result.append(begin + step * i) + return result + + +var inputs = make_inputs(0, 10_000, 1_000_000) + + +# ===----------------------------------------------------------------------===# +# Benchmark sin +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_sin(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = sin(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark cos +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_cos(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = cos(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark tan +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_tan(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = tan(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark asin +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_asin(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = asin(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark acos +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_acos(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = acos(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark atan +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_atan(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = atan(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark log +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_log(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = log(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark log2 +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_log2(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = log2(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark sqrt +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_sqrt(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = sqrt(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark exp2 +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_exp2(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = exp2(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark exp +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_exp(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = exp(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark erf +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_erf(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = erf(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark fma +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math_fma(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = fma(input[], input[], input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + seed() + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100000)) + m.bench_function[bench_math_sin](BenchId("bench_math_sin")) + m.bench_function[bench_math_cos](BenchId("bench_math_cos")) + m.bench_function[bench_math_tan](BenchId("bench_math_tan")) + m.bench_function[bench_math_asin](BenchId("bench_math_asin")) + m.bench_function[bench_math_acos](BenchId("bench_math_acos")) + m.bench_function[bench_math_atan](BenchId("bench_math_atan")) + m.bench_function[bench_math_log](BenchId("bench_math_log")) + m.bench_function[bench_math_log2](BenchId("bench_math_log2")) + m.bench_function[bench_math_sqrt](BenchId("bench_math_sqrt")) + m.bench_function[bench_math_exp2](BenchId("bench_math_exp2")) + m.bench_function[bench_math_exp](BenchId("bench_math_exp")) + m.bench_function[bench_math_erf](BenchId("bench_math_erf")) + m.bench_function[bench_math_fma](BenchId("bench_math_fma")) + m.dump_report() diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo new file mode 100644 index 0000000000..01b228c54b --- /dev/null +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -0,0 +1,174 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from stdlib.collections import Dict +from random import * + + +# ===----------------------------------------------------------------------===# +# Benchmark Data +# ===----------------------------------------------------------------------===# +fn make_dict(n: Int) -> Dict[Int, Int]: + var dict = Dict[Int, Int]() + for i in range(0, n): + dict[i] = random.random_si64(0, n).value + return dict + + +alias small_n = 10_000 +alias large_n = 500_000 +alias insert_n = small_n // 2 +alias partial_n = small_n // 4 + +var small = make_dict(small_n) +var large = make_dict(large_n) + + +# ===----------------------------------------------------------------------===# +# Benchmark Dict Ctor +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_ctor(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var _d: Dict[Int, Int] = Dict[Int, Int]() + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Dict Small Insert +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_small_insert(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for key in range(small_n, small_n + insert_n): + small[key] = random.random_si64(0, small_n).value + + b.iter[call_fn]() + keep(small) + + +# ===----------------------------------------------------------------------===# +# Benchmark Dict Large Insert +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_large_insert(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for key in range(large_n, large_n + insert_n): + large[key] = random.random_si64(0, large_n).value + + b.iter[call_fn]() + keep(large) + + +# ===----------------------------------------------------------------------===# +# Benchmark Dict Small Lookup +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_small_lookup(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for key in range(0, partial_n): + _ = small[key] + + b.iter[call_fn]() + keep(small) + + +# ===----------------------------------------------------------------------===# +# Benchmark Dict Large Lookup +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_large_lookup(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for key in range(0, partial_n): + _ = large[key] + + b.iter[call_fn]() + keep(large) + + +# NOTE: The delete element benchmarks are commented out because they fail to compile. +# +# ===----------------------------------------------------------------------===# +# Benchmark Dict Small Lookup +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_small_del(inout b: Bencher) raises: + pass + # var small_copy = small + # + # @always_inline + # @parameter + # fn call_fn() raises: + # for key in range(0, partial_n): + # # del small_copy[key] + # _ = small_copy.pop(key) + # + # b.iter[call_fn]() + # keep(small_copy) + + +# ===----------------------------------------------------------------------===# +# Benchmark Dict Large Lookup +# ===----------------------------------------------------------------------===# +@parameter +fn bench_dict_large_del(inout b: Bencher) raises: + pass + + +# var large_copy = large +# @always_inline +# @parameter +# fn call_fn() raises: +# for key in range(0, partial_n): +# # del large_copy[key] +# _ = large_copy.pop(key) +# +# b.iter[call_fn]() +# keep(large_copy) + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + seed() + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100)) + m.bench_function[bench_dict_ctor](BenchId("bench_dict_ctor")) + m.bench_function[bench_dict_small_insert]( + BenchId("bench_dict_small_insert") + ) + m.bench_function[bench_dict_large_insert]( + BenchId("bench_dict_large_insert") + ) + m.bench_function[bench_dict_small_lookup]( + BenchId("bench_dict_small_lookup") + ) + m.bench_function[bench_dict_large_lookup]( + BenchId("bench_dict_large_lookup") + ) + m.bench_function[bench_dict_small_del](BenchId("bench_dict_small_del")) + m.bench_function[bench_dict_large_del](BenchId("bench_dict_large_del")) + m.dump_report() diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo new file mode 100644 index 0000000000..477d0ffd07 --- /dev/null +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -0,0 +1,67 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo %s + +from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from utils.stringref import _memmem, _memchr, _align_down + +from bit import countr_zero +from builtin.dtype import _uint_type_of_width + +# ===----------------------------------------------------------------------===# +# Benchmark Data +# ===----------------------------------------------------------------------===# + + +# ===----------------------------------------------------------------------===# +# Benchmarks +# ===----------------------------------------------------------------------===# +@parameter +fn bench_formatter_int[n: Int](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var s1 = String() + var s1_fmt = Formatter(s1) + Int(n).format_to(s1_fmt) + + b.iter[call_fn]() + + +@parameter +fn bench_formatter_simd[n: Int](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var s1 = String() + var s1_fmt = Formatter(s1) + SIMD[DType.int32](n).format_to(s1_fmt) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=10000)) + m.bench_function[bench_formatter_int[42]](BenchId("bench_formatter_int_42")) + m.bench_function[bench_formatter_int[2**64]]( + BenchId("bench_formatter_int_2**64") + ) + m.bench_function[bench_formatter_simd[42]](BenchId("bench_formatter_simd")) + m.bench_function[bench_formatter_simd[2**16]]( + BenchId("bench_formatter_simd_2**16") + ) + m.dump_report() diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 2945bb07ab..6e2d57c55e 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -218,7 +218,7 @@ fn bench_find_optimized(inout b: Bencher) raises: # Benchmark Main # ===----------------------------------------------------------------------===# def main(): - var m = Bench(BenchConfig(num_repetitions=5, warmup_iters=100)) + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=10000)) m.bench_function[bench_find_baseline](BenchId("find_baseline")) m.bench_function[bench_find_optimized](BenchId("find_optimized")) m.dump_report() diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 5687d2e8db..f4fb7bb44e 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -93,6 +93,21 @@ fn random_ui64(min: UInt64, max: UInt64) -> UInt64: ) +fn random_si32(min: Int, max: Int) -> Int: + """Returns a random `Int` number from the given range. + + Args: + min: The minimum number in the range. + max: The maximum number in the range. + + Returns: + A random number from the specified range. + """ + return external_call["KGEN_CompilerRT_RandomSInt64", Int]( + _get_random_state(), min, max + ) + + fn randint[ type: DType ](ptr: DTypePointer[type], size: Int, low: Int, high: Int): From f118f8fd4939bbfb1082423b8105d0fca4565b0c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 2 Jun 2024 17:14:12 -0700 Subject: [PATCH 0838/2019] [mojo-stdlib] Reduce UnsafePointer impl conversion from Reference I'd like to remove this implicit conversion eventually. MODULAR_ORIG_COMMIT_REV_ID: 10f7835d3adbbc33686c8421c69f742236f08fa2 --- stdlib/src/builtin/tuple.mojo | 10 +++++----- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/sys/ffi.mojo | 8 +++++--- stdlib/src/utils/static_tuple.mojo | 8 ++++---- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index ae92fd874d..adaa4e0127 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -80,8 +80,8 @@ struct Tuple[*element_types: Movable](Sized, Movable): @parameter fn initialize_elt[idx: Int](): move_pointee( - dst=UnsafePointer(self[idx]), - src=UnsafePointer(storage[idx]), + dst=UnsafePointer.address_of(self[idx]), + src=UnsafePointer.address_of(storage[idx]), ) # Move each element into the tuple storage. @@ -97,7 +97,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): # trivial and won't do anything. @parameter fn destroy_elt[idx: Int](): - destroy_pointee(UnsafePointer(self[idx])) + destroy_pointee(UnsafePointer.address_of(self[idx])) unroll[destroy_elt, Self.__len__()]() @@ -115,9 +115,9 @@ struct Tuple[*element_types: Movable](Sized, Movable): @parameter fn initialize_elt[idx: Int](): - var existing_elt_ptr = UnsafePointer(existing[idx]).address move_pointee( - src=UnsafePointer(existing[idx]), dst=UnsafePointer(self[idx]) + src=UnsafePointer.address_of(existing[idx]), + dst=UnsafePointer.address_of(self[idx]), ) unroll[initialize_elt, Self.__len__()]() diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index f1a17bb1a9..d1c3df5775 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -152,7 +152,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __del__(owned self): """Destroy all the elements in the list and free the memory.""" for i in range(self._size): - destroy_pointee(UnsafePointer(self._array[i])) + destroy_pointee(UnsafePointer.address_of(self._array[i])) fn __iter__( ref [_]self: Self, diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 59277ccddd..a8637dd9cf 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -243,5 +243,5 @@ struct InlinedFixedVector[ An iterator to the start of the vector. """ return Self._iterator( - 0, self.current_size, UnsafePointer(Reference(self)) + 0, self.current_size, UnsafePointer.address_of(self) ) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 2229b40df4..f59ac8027d 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -115,4 +115,4 @@ struct Arc[T: Movable](CollectionElement): Returns: The UnsafePointer to the underlying memory. """ - return UnsafePointer.address_of(self._inner[].payload)[] + return UnsafePointer.address_of(self._inner[].payload) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 8624be87c0..0790dcaabd 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -133,7 +133,9 @@ struct DLHandle(CollectionElement, Boolable): var opaque_function_ptr = external_call[ "dlsym", DTypePointer[DType.int8] ](self.handle.address, name) - return UnsafePointer(opaque_function_ptr).bitcast[result_type]()[] + return UnsafePointer.address_of(opaque_function_ptr).bitcast[ + result_type + ]()[] else: return abort[result_type]("get_function isn't supported on windows") @@ -218,13 +220,13 @@ fn _get_dylib_function[ alias func_cache_name = name + "/" + func_name var func_ptr = _get_global_or_null[func_cache_name]() if func_ptr: - return UnsafePointer(func_ptr).bitcast[result_type]()[] + return UnsafePointer.address_of(func_ptr).bitcast[result_type]()[] var dylib = _get_dylib[name, init_fn, destroy_fn](payload) var new_func = dylib._get_function[func_name, result_type]() external_call["KGEN_CompilerRT_InsertGlobal", NoneType]( StringRef(func_cache_name), - UnsafePointer(new_func).bitcast[Pointer[NoneType]]()[], + UnsafePointer.address_of(new_func).bitcast[Pointer[NoneType]]()[], ) return new_func diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index fd4a31a81f..b5587a85e8 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -315,7 +315,7 @@ struct InlineArray[ @parameter for i in range(size): - var ptr = UnsafePointer(self._get_reference_unsafe(i)) + var ptr = UnsafePointer.address_of(self._get_reference_unsafe(i)[]) ptr.initialize_pointee_explicit_copy(fill) @always_inline @@ -349,8 +349,8 @@ struct InlineArray[ for i in range(size): var eltref = self._get_reference_unsafe(i) move_pointee( - dst=UnsafePointer[Self.ElementType](eltref), - src=UnsafePointer(storage[i]), + dst=UnsafePointer[Self.ElementType].address_of(eltref[]), + src=UnsafePointer.address_of(storage[i]), ) # Mark the elements as already destroyed. @@ -477,7 +477,7 @@ struct InlineArray[ Returns: An `UnsafePointer` to the underlying array. """ - return UnsafePointer(self._array).bitcast[Self.ElementType]() + return UnsafePointer.address_of(self._array).bitcast[Self.ElementType]() @always_inline fn __contains__[T: ComparableCollectionElement](self, value: T) -> Bool: From 3da650c6c91d74112391f8ca56b7eb05b9f450cb Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 2 Jun 2024 18:41:40 -0700 Subject: [PATCH 0839/2019] [mojo-stdlib] Move global UnsafePointer functions to be methods. This extends the compiler to allow the type of `self` to be `Self` with any parameters, allowing more flexible conditional conformance. This allows us to move the global functions in UnsafePointer to being methods on it instead, which is a much nicer API. Specifically, this moves: 1) `destroy_pointee(p)` => `p.destroy_pointee()` 2) `move_from_pointee(p)` => `p.take_pointee()` 3) `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` 4) `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` 5) `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` This uses the word "init" instead of "initialize" since that is the term of art used in Python. MODULAR_ORIG_COMMIT_REV_ID: ba930ea1ef25c4f010bd27742e3afee047377764 --- docs/changelog.md | 9 + stdlib/src/builtin/builtin_list.mojo | 5 +- stdlib/src/builtin/object.mojo | 6 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 18 +- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/list.mojo | 37 ++-- stdlib/src/memory/arc.mojo | 4 +- stdlib/src/memory/unsafe_pointer.mojo | 192 +++++++++------------ stdlib/src/os/os.mojo | 7 +- stdlib/src/python/_cpython.mojo | 14 +- stdlib/src/utils/static_tuple.mojo | 5 +- stdlib/src/utils/variant.mojo | 18 +- stdlib/test/memory/test_arc.mojo | 3 +- stdlib/test/memory/test_unsafepointer.mojo | 12 +- stdlib/test/utils/test_variant.mojo | 12 +- 16 files changed, 157 insertions(+), 189 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4dc9c65080..02a877b0a4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -47,6 +47,15 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) +- The global functions for working with `UnsafePointer` have transitioned to + being methods through the use of conditional conformances: + + - `destroy_pointee(p)` => `p.destroy_pointee()` + - `move_from_pointee(p)` => `p.take_pointee()` + - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` + - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` + - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` + ### ❌ Removed - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 0210909b1f..d7eb137c5f 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -16,7 +16,6 @@ These are Mojo built-ins, so you don't need to import them. """ from memory import Reference, UnsafePointer, LegacyPointer -from memory.unsafe_pointer import destroy_pointee # ===----------------------------------------------------------------------===# # ListLiteral @@ -344,7 +343,7 @@ struct VariadicListMem[ # destroy in backwards order to match how arguments are normally torn # down when CheckLifetimes is left to its own devices. for i in reversed(range(len(self))): - destroy_pointee(UnsafePointer.address_of(self[i])) + UnsafePointer.address_of(self[i]).destroy_pointee() @always_inline fn __len__(self) -> Int: @@ -535,7 +534,7 @@ struct VariadicPack[ @parameter fn destroy_elt[i: Int](): # destroy the elements in reverse order. - destroy_pointee(UnsafePointer.address_of(self[len - i - 1])) + UnsafePointer.address_of(self[len - i - 1]).destroy_pointee() unroll[destroy_elt, len]() diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 2e2edc6c9c..66e3a8642d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -21,7 +21,6 @@ from sys.intrinsics import _type_is_eq from memory import memcmp, memcpy from memory import Arc -from memory.unsafe_pointer import move_from_pointee from utils import StringRef, unroll, Variant @@ -1735,9 +1734,8 @@ struct object(IntableRaising, Boolable, Stringable): var index = Self._convert_index_to_int(i) if self._value.is_str(): var impl = _ImmutableString(UnsafePointer[Int8].alloc(1), 1) - initialize_pointee_copy( - impl.data, - move_from_pointee(self._value.get_as_string().data + index), + impl.data.init_pointee_copy( + (self._value.get_as_string().data + index).take_pointee(), ) return _ObjectImpl(impl) return self._value.get_list_element(i._value.get_as_int().value) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 732fd40a04..11bdefcaae 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -235,7 +235,7 @@ struct StringLiteral( uint8Ptr ) memcpy(DTypePointer(buffer.data), data, length) - initialize_pointee_move(buffer.data + length, 0) + (buffer.data + length).init_pointee_move(0) string._buffer = buffer^ return string diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index adaa4e0127..ed47f0825b 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -17,12 +17,6 @@ These are Mojo built-ins, so you don't need to import them. from utils._visualizers import lldb_formatter_wrapping_type -from memory.unsafe_pointer import ( - initialize_pointee_move, - initialize_pointee_copy, - move_pointee, -) - from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# @@ -79,9 +73,8 @@ struct Tuple[*element_types: Movable](Sized, Movable): @parameter fn initialize_elt[idx: Int](): - move_pointee( - dst=UnsafePointer.address_of(self[idx]), - src=UnsafePointer.address_of(storage[idx]), + UnsafePointer.address_of(storage[idx]).move_pointee_into( + UnsafePointer.address_of(self[idx]) ) # Move each element into the tuple storage. @@ -97,7 +90,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): # trivial and won't do anything. @parameter fn destroy_elt[idx: Int](): - destroy_pointee(UnsafePointer.address_of(self[idx])) + UnsafePointer.address_of(self[idx]).destroy_pointee() unroll[destroy_elt, Self.__len__()]() @@ -115,9 +108,8 @@ struct Tuple[*element_types: Movable](Sized, Movable): @parameter fn initialize_elt[idx: Int](): - move_pointee( - src=UnsafePointer.address_of(existing[idx]), - dst=UnsafePointer.address_of(self[idx]), + UnsafePointer.address_of(existing[idx]).move_pointee_into( + UnsafePointer.address_of(self[idx]) ) unroll[initialize_elt, Self.__len__()]() diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index d1c3df5775..d0cfbcf2a9 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -152,7 +152,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __del__(owned self): """Destroy all the elements in the list and free the memory.""" for i in range(self._size): - destroy_pointee(UnsafePointer.address_of(self._array[i])) + UnsafePointer.address_of(self._array[i]).destroy_pointee() fn __iter__( ref [_]self: Self, diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 6df07a8829..ceba186bad 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -21,7 +21,6 @@ from collections import List from memory import UnsafePointer, Reference -from memory.unsafe_pointer import move_pointee, move_from_pointee from sys.intrinsics import _type_is_eq from .optional import Optional from utils import Span @@ -186,7 +185,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn __del__(owned self): """Destroy all elements in the list and free its memory.""" for i in range(self.size): - destroy_pointee(self.data + i) + (self.data + i).destroy_pointee() if self.data: self.data.free() @@ -210,8 +209,8 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if normalized_idx < 0: normalized_idx += len(self) - destroy_pointee(self.data + normalized_idx) - initialize_pointee_move(self.data + normalized_idx, value^) + (self.data + normalized_idx).destroy_pointee() + (self.data + normalized_idx).init_pointee_move(value^) @always_inline fn __contains__[ @@ -401,7 +400,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var new_data = UnsafePointer[T].alloc(new_capacity) for i in range(self.size): - move_pointee(src=self.data + i, dst=new_data + i) + (self.data + i).move_pointee_into(new_data + i) if self.data: self.data.free() @@ -417,7 +416,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ if self.size >= self.capacity: self._realloc(max(1, self.capacity * 2)) - initialize_pointee_move(self.data + self.size, value^) + (self.data + self.size).init_pointee_move(value^) self.size += 1 @always_inline @@ -443,9 +442,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var earlier_ptr = self.data + earlier_idx var later_ptr = self.data + later_idx - var tmp = move_from_pointee(earlier_ptr) - move_pointee(src=later_ptr, dst=earlier_ptr) - initialize_pointee_move(later_ptr, tmp^) + var tmp = earlier_ptr.take_pointee() + later_ptr.move_pointee_into(earlier_ptr) + later_ptr.init_pointee_move(tmp^) earlier_idx -= 1 later_idx -= 1 @@ -503,7 +502,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # `other` list into this list using a single `T.__moveinit()__` # call, without moving into an intermediate temporary value # (avoiding an extra redundant move constructor call). - move_pointee(src=src_ptr, dst=dest_ptr) + src_ptr.move_pointee_into(dest_ptr) dest_ptr = dest_ptr + 1 @@ -527,9 +526,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if i < 0: normalized_idx += len(self) - var ret_val = move_from_pointee(self.data + normalized_idx) + var ret_val = (self.data + normalized_idx).take_pointee() for j in range(normalized_idx + 1, self.size): - move_pointee(src=self.data + j, dst=self.data + j - 1) + (self.data + j).move_pointee_into(self.data + j - 1) self.size -= 1 if self.size * 4 < self.capacity: if self.capacity > 1: @@ -567,9 +566,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): else: self.reserve(new_size) for i in range(new_size, self.size): - destroy_pointee(self.data + i) + (self.data + i).destroy_pointee() for i in range(self.size, new_size): - initialize_pointee_copy(self.data + i, value) + (self.data + i).init_pointee_copy(value) self.size = new_size @always_inline @@ -590,7 +589,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): ), ) for i in range(new_size, self.size): - destroy_pointee(self.data + i) + (self.data + i).destroy_pointee() self.size = new_size self.reserve(new_size) @@ -623,9 +622,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var earlier_ptr = self.data + earlier_idx var later_ptr = self.data + later_idx - var tmp = move_from_pointee(earlier_ptr) - move_pointee(src=later_ptr, dst=earlier_ptr) - initialize_pointee_move(later_ptr, tmp^) + var tmp = earlier_ptr.take_pointee() + later_ptr.move_pointee_into(earlier_ptr) + later_ptr.init_pointee_move(tmp^) earlier_idx += 1 later_idx -= 1 @@ -690,7 +689,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn clear(inout self): """Clears the elements in the list.""" for i in range(self.size): - destroy_pointee(self.data + i) + (self.data + i).destroy_pointee() self.size = 0 fn steal_data(inout self) -> UnsafePointer[T]: diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index f59ac8027d..cfd5ce3830 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -73,7 +73,7 @@ struct Arc[T: Movable](CollectionElement): value: The value to manage. """ self._inner = UnsafePointer[Self._inner_type].alloc(1) - # Cannot use initialize_pointee_move as _ArcInner isn't movable. + # Cannot use init_pointee_move as _ArcInner isn't movable. __get_address_as_uninit_lvalue(self._inner.address) = Self._inner_type( value^ ) @@ -96,7 +96,7 @@ struct Arc[T: Movable](CollectionElement): references, delete the object and free its memory.""" if self._inner[].drop_ref(): # Call inner destructor, then free the memory. - destroy_pointee(self._inner) + (self._inner).destroy_pointee() self._inner.free() # FIXME: This isn't right - the element should be mutable regardless diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index a103c470a9..420e132c06 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -365,7 +365,7 @@ struct UnsafePointer[ before writing `value`. Similarly, ownership of `value` is logically transferred into the pointer location. - When compared to `initialize_pointee_move`, this avoids an extra move on + When compared to `init_pointee_move`, this avoids an extra move on the callee side when the value must be copied. Parameters: @@ -412,128 +412,108 @@ struct UnsafePointer[ _type = UnsafePointer[new_type, address_space]._mlir_type, ](self.address) + @always_inline + fn destroy_pointee(self: UnsafePointer[_, AddressSpace.GENERIC]): + """Destroy the pointed-to value. -# ===----------------------------------------------------------------------=== # -# UnsafePointer extensions -# ===----------------------------------------------------------------------=== # - -# TODO: These should be methods when we have conditional conformance. None of -# these can work with pointers in generic address spaces, because they need to -# invoke methods like del or moveinit or copyinit, which take borrowed arguments -# in the corresponding traits. - - -@always_inline -fn destroy_pointee(ptr: UnsafePointer[_]): - """Destroy the pointed-to value. - - The pointer must not be null, and the pointer memory location is assumed - to contain a valid initialized instance of `T`. This is equivalent to - `_ = move_from_pointee(ptr)` but doesn't require `Movable` and is more - efficient becase it doesn't invoke `__moveinit__`. - - Args: - ptr: The pointer whose pointee this destroys. - """ - _ = __get_address_as_owned_value(ptr.address) - - -@always_inline -fn move_from_pointee[T: Movable](ptr: UnsafePointer[T]) -> T: - """Move the value at the pointer out. - - The pointer must not be null, and the pointer memory location is assumed - to contain a valid initialized instance of `T`. - - This performs a _consuming_ move, ending the lifetime of the value stored - in this pointer memory location. Subsequent reads of this pointer are - not valid. If a new valid value is stored using `initialize_pointee_move()`, then - reading from this pointer becomes valid again. - - Parameters: - T: The type the pointer points to, which must be `Movable`. - - Args: - ptr: The pointer whose pointee this moves from. - - Returns: - The value at the pointer. - """ - return __get_address_as_owned_value(ptr.address) + The pointer must not be null, and the pointer memory location is assumed + to contain a valid initialized instance of `T`. This is equivalent to + `_ = self.take_pointee()` but doesn't require `Movable` and is + more efficient becase it doesn't invoke `__moveinit__`. + """ + _ = __get_address_as_owned_value(self.address) -@always_inline -fn initialize_pointee_move[T: Movable](ptr: UnsafePointer[T], owned value: T): - """Emplace a new value into the pointer location, moving from `value`. + @always_inline + fn take_pointee[T: Movable](self: UnsafePointer[T]) -> T: + """Move the value at the pointer out, leaving it uninitialized. - The pointer memory location is assumed to contain uninitialized data, - and consequently the current contents of this pointer are not destructed - before writing `value`. Similarly, ownership of `value` is logically - transferred into the pointer location. + The pointer must not be null, and the pointer memory location is assumed + to contain a valid initialized instance of `T`. - When compared to `initialize_pointee_copy`, this avoids an extra copy on - the caller side when the value is an `owned` rvalue. + This performs a _consuming_ move, ending the lifetime of the value stored + in this pointer memory location. Subsequent reads of this pointer are + not valid. If a new valid value is stored using `init_pointee_move()`, then + reading from this pointer becomes valid again. - Parameters: - T: The type the pointer points to, which must be `Movable`. + Parameters: + T: The type the pointer points to, which must be `Movable`. - Args: - ptr: The pointer to initialize through. - value: The value to emplace. - """ - __get_address_as_uninit_lvalue(ptr.address) = value^ + Returns: + The value at the pointer. + """ + return __get_address_as_owned_value(self.address) + # TODO: Allow overloading on more specific traits + @always_inline + fn init_pointee_move[T: Movable](self: UnsafePointer[T], owned value: T): + """Emplace a new value into the pointer location, moving from `value`. -@always_inline -fn initialize_pointee_copy[T: Copyable](ptr: UnsafePointer[T], value: T): - """Emplace a copy of `value` into the pointer location. + The pointer memory location is assumed to contain uninitialized data, + and consequently the current contents of this pointer are not destructed + before writing `value`. Similarly, ownership of `value` is logically + transferred into the pointer location. - The pointer memory location is assumed to contain uninitialized data, - and consequently the current contents of this pointer are not destructed - before writing `value`. Similarly, ownership of `value` is logically - transferred into the pointer location. + When compared to `init_pointee_copy`, this avoids an extra copy on + the caller side when the value is an `owned` rvalue. - When compared to `initialize_pointee_move`, this avoids an extra move on - the callee side when the value must be copied. + Parameters: + T: The type the pointer points to, which must be `Movable`. - Parameters: - T: The type the pointer points to, which must be `Copyable`. + Args: + value: The value to emplace. + """ + __get_address_as_uninit_lvalue(self.address) = value^ - Args: - ptr: The pointer to initialize through. - value: The value to emplace. - """ - __get_address_as_uninit_lvalue(ptr.address) = value + @always_inline + fn init_pointee_copy[T: Copyable](self: UnsafePointer[T], value: T): + """Emplace a copy of `value` into the pointer location. + The pointer memory location is assumed to contain uninitialized data, + and consequently the current contents of this pointer are not destructed + before writing `value`. Similarly, ownership of `value` is logically + transferred into the pointer location. -@always_inline -fn move_pointee[T: Movable](*, src: UnsafePointer[T], dst: UnsafePointer[T]): - """Moves the value `src` points to into the memory location pointed to by - `dest`. + When compared to `init_pointee_move`, this avoids an extra move on + the callee side when the value must be copied. - This performs a consuming move (using `__moveinit__()`) out of the - memory location pointed to by `src`. Subsequent reads of this - pointer are not valid unless and until a new, valid value has been - moved into this pointer's memory location using `initialize_pointee_move()`. + Parameters: + T: The type the pointer points to, which must be `Copyable`. - This transfers the value out of `self` and into `dest` using at most one - `__moveinit__()` call. + Args: + value: The value to emplace. + """ + __get_address_as_uninit_lvalue(self.address) = value - Safety: - * `src` must not be null - * `src` must contain a valid, initialized instance of `T` - * `dst` must not be null - * The contents of `dst` should be uninitialized. If `dst` was - previously written with a valid value, that value will be be - overwritten and its destructor will NOT be run. + @always_inline + fn move_pointee_into[ + T: Movable + ](self: UnsafePointer[T], dst: UnsafePointer[T]): + """Moves the value `self` points to into the memory location pointed to by + `dst`. + + This performs a consuming move (using `__moveinit__()`) out of the + memory location pointed to by `self`. Subsequent reads of this + pointer are not valid unless and until a new, valid value has been + moved into this pointer's memory location using `init_pointee_move()`. + + This transfers the value out of `self` and into `dest` using at most one + `__moveinit__()` call. + + Safety: + * `self` must be non-null + * `self` must contain a valid, initialized instance of `T` + * `dst` must not be null + * The contents of `dst` should be uninitialized. If `dst` was + previously written with a valid value, that value will be be + overwritten and its destructor will NOT be run. - Parameters: - T: The type the pointer points to, which must be `Movable`. + Parameters: + T: The type the pointer points to, which must be `Movable`. - Args: - src: Source pointer that the value will be moved from. - dst: Destination pointer that the value will be moved into. - """ - __get_address_as_uninit_lvalue(dst.address) = __get_address_as_owned_value( - src.address - ) + Args: + dst: Destination pointer that the value will be moved into. + """ + __get_address_as_uninit_lvalue( + dst.address + ) = __get_address_as_owned_value(self.address) diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 8abd84fef0..dd78b31912 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -25,7 +25,6 @@ from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda from memory import ( DTypePointer, ) -from memory.unsafe_pointer import move_from_pointee from utils import StringRef, InlineArray @@ -88,7 +87,7 @@ struct _dirent_macos: fn _strnlen(ptr: UnsafePointer[Int8], max: Int) -> Int: var len = 0 - while len < max and move_from_pointee(ptr + len): + while len < max and (ptr + len)[0]: len += 1 return len @@ -149,7 +148,7 @@ struct _DirHandle: ) if not ep: break - var name = move_from_pointee(ep).name + var name = ep.take_pointee().name var name_ptr = UnsafePointer.address_of(name).bitcast[Int8]() var name_str = StringRef( name_ptr, _strnlen(name_ptr, _dirent_linux.MAX_NAME_SIZE) @@ -174,7 +173,7 @@ struct _DirHandle: ) if not ep: break - var name = move_from_pointee(ep).name + var name = ep.take_pointee().name var name_ptr = UnsafePointer.address_of(name).bitcast[Int8]() var name_str = StringRef( name_ptr, _strnlen(name_ptr, _dirent_macos.MAX_NAME_SIZE) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 0bc041869e..1421a17811 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -184,12 +184,12 @@ struct CPython: existing.Py_DecRef(existing.none_value) if existing.logging_enabled: print("CPython destroy") - var remaining_refs = move_from_pointee(existing.total_ref_count) + var remaining_refs = existing.total_ref_count.take_pointee() print("Number of remaining refs:", remaining_refs) # Technically not necessary since we're working with register # passable types, by it's good practice to re-initialize the # pointer after a consuming move. - initialize_pointee_move(existing.total_ref_count, remaining_refs) + existing.total_ref_count.init_pointee_move(remaining_refs) _py_finalize(existing.lib) existing.lib.close() existing.total_ref_count.free() @@ -219,12 +219,12 @@ struct CPython: self.total_ref_count = existing.total_ref_count fn _inc_total_rc(inout self): - var v = move_from_pointee(self.total_ref_count) - initialize_pointee_move(self.total_ref_count, v + 1) + var v = self.total_ref_count.take_pointee() + self.total_ref_count.init_pointee_move(v + 1) fn _dec_total_rc(inout self): - var v = move_from_pointee(self.total_ref_count) - initialize_pointee_move(self.total_ref_count, v - 1) + var v = self.total_ref_count.take_pointee() + self.total_ref_count.init_pointee_move(v - 1) fn Py_IncRef(inout self, ptr: PyObjectPtr): if self.logging_enabled: @@ -847,6 +847,6 @@ struct CPython: return PyKeyValuePair { key: key, value: value, - position: move_from_pointee(position), + position: position.take_pointee(), success: result == 1, } diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index b5587a85e8..db5e8e2e29 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -348,9 +348,8 @@ struct InlineArray[ @parameter for i in range(size): var eltref = self._get_reference_unsafe(i) - move_pointee( - dst=UnsafePointer[Self.ElementType].address_of(eltref[]), - src=UnsafePointer.address_of(storage[i]), + UnsafePointer.address_of(storage[i]).move_pointee_into( + UnsafePointer[Self.ElementType].address_of(eltref[]) ) # Mark the elements as already destroyed. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 1c49e08725..25053ee165 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -42,11 +42,6 @@ from sys import alignof, sizeof from sys.intrinsics import _type_is_eq from memory import UnsafePointer -from memory.unsafe_pointer import ( - initialize_pointee_move, - move_from_pointee, - move_pointee, -) from utils import unroll # ===----------------------------------------------------------------------=== # @@ -172,7 +167,7 @@ struct Variant[*Ts: CollectionElement]( """ self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] self._get_state()[] = Self._check[T]() - initialize_pointee_move(self._get_ptr[T](), value^) + self._get_ptr[T]().init_pointee_move(value^) fn __init__(inout self, *, other: Self): """Explicitly creates a deep copy of an existing variant. @@ -187,8 +182,9 @@ struct Variant[*Ts: CollectionElement]( fn each[i: Int](): if self._get_state()[] == i: alias T = Ts[i] - initialize_pointee_move( - UnsafePointer.address_of(self._impl).bitcast[T](), + UnsafePointer.address_of(self._impl).bitcast[ + T + ]().init_pointee_move( UnsafePointer.address_of(other._impl).bitcast[T]()[], ) @@ -218,7 +214,7 @@ struct Variant[*Ts: CollectionElement]( if self._get_state()[] == i: alias T = Ts[i] # Calls the correct __moveinit__ - move_pointee(src=other._get_ptr[T](), dst=self._get_ptr[T]()) + other._get_ptr[T]().move_pointee_into(self._get_ptr[T]()) unroll[each, len(VariadicList(Ts))]() @@ -273,7 +269,7 @@ struct Variant[*Ts: CollectionElement]( fn each[i: Int](): if self._get_state()[] == i: alias q = Ts[i] - destroy_pointee(self._get_ptr[q]().address) + self._get_ptr[q]().destroy_pointee() unroll[each, len(VariadicList(Ts))]() @@ -318,7 +314,7 @@ struct Variant[*Ts: CollectionElement]( debug_assert(self.isa[T](), "taking wrong type") # don't call the variant's deleter later self._get_state()[] = Self._sentinel - return move_from_pointee(self._get_ptr[T]()) + return self._get_ptr[T]().take_pointee() @always_inline fn replace[ diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 7cffd9e45e..57fe268ee0 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -15,7 +15,6 @@ from collections import List from memory import Arc -from memory.unsafe_pointer import initialize_pointee_move from testing import assert_equal, assert_false, assert_true @@ -31,7 +30,7 @@ struct ObservableDel(CollectionElement): var target: UnsafePointer[Bool] fn __del__(owned self): - initialize_pointee_move(self.target, True) + self.target.init_pointee_move(True) def test_deleter_not_called_until_no_references(): diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 2c6e597c34..6e40142120 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -13,7 +13,6 @@ # RUN: %mojo %s from memory import UnsafePointer -from memory.unsafe_pointer import move_from_pointee, move_pointee from test_utils import MoveCounter, ExplicitCopyOnly from testing import assert_equal, assert_not_equal, assert_true @@ -40,16 +39,16 @@ struct MoveOnlyType(Movable): def test_unsafepointer_of_move_only_type(): var actions_ptr = UnsafePointer[List[String]].alloc(1) - initialize_pointee_move(actions_ptr, List[String]()) + actions_ptr.init_pointee_move(List[String]()) var ptr = UnsafePointer[MoveOnlyType].alloc(1) - initialize_pointee_move(ptr, MoveOnlyType(42, actions_ptr)) + ptr.init_pointee_move(MoveOnlyType(42, actions_ptr)) assert_equal(len(actions_ptr[0]), 2) assert_equal(actions_ptr[0][0], "__init__") assert_equal(actions_ptr[0][1], "__moveinit__", msg="emplace_value") assert_equal(ptr[0].value, 42) - var value = move_from_pointee(ptr) + var value = ptr.take_pointee() assert_equal(len(actions_ptr[0]), 3) assert_equal(actions_ptr[0][2], "__moveinit__") assert_equal(value.value, 42) @@ -66,7 +65,7 @@ def test_unsafepointer_move_pointee_move_count(): var value = MoveCounter(5) assert_equal(0, value.move_count) - initialize_pointee_move(ptr, value^) + ptr.init_pointee_move(value^) # ----- # Test that `UnsafePointer.move_pointee` performs exactly one move. @@ -75,8 +74,7 @@ def test_unsafepointer_move_pointee_move_count(): assert_equal(1, ptr[].move_count) var ptr_2 = UnsafePointer[MoveCounter[Int]].alloc(1) - - move_pointee(src=ptr, dst=ptr_2) + ptr.move_pointee_into(ptr_2) assert_equal(2, ptr_2[].move_count) diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index db834cffcb..76b6c64906 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -44,14 +44,14 @@ fn _poison_ptr() -> UnsafePointer[Bool]: fn assert_no_poison() raises: - assert_false(move_from_pointee(_poison_ptr())) + assert_false(_poison_ptr().take_pointee()) fn _initialize_poison( payload: UnsafePointer[NoneType], ) -> UnsafePointer[NoneType]: var poison = UnsafePointer[Bool].alloc(1) - initialize_pointee_move(poison, False) + poison.init_pointee_move(False) return poison.bitcast[NoneType]() @@ -64,13 +64,13 @@ struct Poison(CollectionElement): pass fn __copyinit__(inout self, other: Self): - initialize_pointee_move(_poison_ptr(), True) + _poison_ptr().init_pointee_move(True) fn __moveinit__(inout self, owned other: Self): - initialize_pointee_move(_poison_ptr(), True) + _poison_ptr().init_pointee_move(True) fn __del__(owned self): - initialize_pointee_move(_poison_ptr(), True) + _poison_ptr().init_pointee_move(True) alias TestVariant = Variant[TestCounter, Poison] @@ -139,7 +139,7 @@ struct ObservableDel(CollectionElement): var target: UnsafePointer[Bool] fn __del__(owned self): - initialize_pointee_move(self.target, True) + self.target.init_pointee_move(True) def test_del(): From 72a130e71e11afcf0c53feaebdf0313cd72a52e3 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Sun, 2 Jun 2024 20:46:00 -0500 Subject: [PATCH 0840/2019] [External] [stdlib] use `ref[_]` in Optional.value() return type (#40932) [External] [stdlib] use `ref[_]` in Optional.value() return type Since auto-deref is possible with the new `ref[_]` return annotation, this PR wires it up for `Optional.value()`. Co-authored-by: Lukas Hermann Closes modularml/mojo#2875 MODULAR_ORIG_COMMIT_REV_ID: 8bacdcb4205ab78ef54616e037e87b67167e551a --- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/collections/dict.mojo | 17 +++++++---------- stdlib/src/collections/list.mojo | 2 +- stdlib/src/collections/optional.mojo | 10 +++++----- stdlib/src/tempfile/tempfile.mojo | 2 +- stdlib/src/testing/testing.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 4 ++-- stdlib/test/collections/test_dict.mojo | 4 ++-- stdlib/test/collections/test_optional.mojo | 6 +++--- stdlib/test/tempfile/test_tempfile.mojo | 6 +++--- 12 files changed, 28 insertions(+), 31 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 2575bbcc52..793f5bf3fe 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -201,7 +201,7 @@ fn _write_int[ ) raises: var err = _try_write_int[prefix](fmt, value, radix, digit_chars) if err: - raise err.value()[] + raise err.value() @always_inline diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index a8044c3bc9..479fec5ab3 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -399,7 +399,7 @@ struct Int( if err: abort( "unreachable: unexpected write int failure condition: " - + str(err.value()[]) + + str(err.value()) ) else: _format_scalar(writer, Int64(self)) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 66e3a8642d..e932062fdd 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -131,7 +131,7 @@ struct _RefCountedAttrsDict: fn get(self, key: StringLiteral) raises -> _ObjectImpl: var iter = self.impl[].find(key) if iter: - return iter.value()[] + return iter.value() raise Error( "AttributeError: Object does not have an attribute of name '" + key diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index be6fef0381..a7311b2324 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -94,7 +94,7 @@ struct _DictEntryIter[ self.index -= 1 self.seen += 1 - return opt_entry_ref[].value()[] + return opt_entry_ref[].value() @parameter if forward: @@ -720,7 +720,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( if found: var entry = self._entries.__get_ref(index) debug_assert(entry[].__bool__(), "entry in index must be full") - return Reference(entry[].value()[].value) + return Reference(entry[].value().value) raise "KeyError" fn get(self, key: K) -> Optional[V]: @@ -777,7 +777,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( self.size -= 1 return entry_value.value^ elif default: - return default.value()[] + return default.value() raise "KeyError" fn popitem(inout self) raises -> DictEntry[K, V]: @@ -803,8 +803,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( break if key: - _ = self.pop(key.value()[]) - return DictEntry[K, V](key.value()[], val.value()[]) + _ = self.pop(key.value()) + return DictEntry[K, V](key.value(), val.value()) raise "KeyError: popitem(): dictionary is empty" @@ -915,10 +915,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( else: var entry = self._entries.__get_ref(index) debug_assert(entry[].__bool__(), "entry in index must be full") - if ( - hash == entry[].value()[].hash - and key == entry[].value()[].key - ): + if hash == entry[].value().hash and key == entry[].value().key: return (True, slot, index) self._next_index_slot(slot, perturb) @@ -954,7 +951,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( debug_assert(right < self._reserved, "Invalid dict state") var entry = self._entries.__get_ref(right) debug_assert(entry[].__bool__(), "Logic error") - var slot = self._find_empty_index(entry[].value()[].hash) + var slot = self._find_empty_index(entry[].value().hash) self._set_index(slot, left) if left != right: self._entries[left] = entry[].unsafe_take() diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index ceba186bad..4779213eb6 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -671,7 +671,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # Default end stop_normalized = len(self) else: - stop_normalized = stop.value()[] + stop_normalized = stop.value() if start_normalized < 0: start_normalized += len(self) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index df6ca5154e..9af65e4836 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -21,9 +21,9 @@ from collections import Optional var a = Optional(1) var b = Optional[Int](None) if a: - print(a.value()[]) # prints 1 + print(a.value()) # prints 1 if b: # bool(b) is False, so no print - print(b.value()[]) + print(b.value()) var c = a.or_else(2) var d = b.or_else(2) print(c) # prints 1 @@ -61,9 +61,9 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): var a = Optional(1) var b = Optional[Int](None) if a: - print(a.value()[]) # prints 1 + print(a.value()) # prints 1 if b: # bool(b) is False, so no print - print(b.value()[]) + print(b.value()) var c = a.or_else(2) var d = b.or_else(2) print(c) # prints 1 @@ -159,7 +159,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): # ===-------------------------------------------------------------------===# @always_inline - fn value(ref [_]self: Self) -> Reference[T, __lifetime_of(self)]: + fn value(ref [_]self: Self) -> ref [__lifetime_of(self)] T: """Retrieve a reference to the value of the Optional. This check to see if the optional contains a value. diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 87b0b440fa..0808ac4f5f 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -144,7 +144,7 @@ fn mkdtemp( Raises: If the directory can not be created. """ - var final_dir = Path(dir.value()[]) if dir else Path(_get_default_tempdir()) + var final_dir = Path(dir.value()) if dir else Path(_get_default_tempdir()) for _ in range(TMP_MAX): var dir_name = final_dir / (prefix + _get_random_name() + suffix) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 8d071ab383..770018c287 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -399,5 +399,5 @@ struct assert_raises: Error: If the error raised doesn't match the expected error to raise. """ if self.message_contains: - return self.message_contains.value()[] in str(error) + return self.message_contains.value() in str(error) return True diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index c7a3ec8b6c..23991dcb61 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -392,7 +392,7 @@ struct _FixedString[CAP: Int]( """ var err = self._iadd_non_raising(str_slice) if err: - raise err.value()[] + raise err.value() # ===------------------------------------------------------------------=== # # Trait implementations @@ -459,7 +459,7 @@ struct _FixedString[CAP: Int]( # abort("error formatting to FixedString: " + str(e)) var err = ptr[]._iadd_non_raising(str_slice) if err: - abort("error formatting to FixedString: " + str(err.value()[])) + abort("error formatting to FixedString: " + str(err.value())) return Formatter( write_to_string, diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 491de7fa8d..47cb9cc8c7 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -507,8 +507,8 @@ def test_owned_kwargs_dict(): def test_find_get(): var some_dict = Dict[String, Int]() some_dict["key"] = 1 - assert_equal(some_dict.find("key").value()[], 1) - assert_equal(some_dict.get("key").value()[], 1) + assert_equal(some_dict.find("key").value(), 1) + assert_equal(some_dict.get("key").value(), 1) assert_equal(some_dict.find("not_key").or_else(0), 0) assert_equal(some_dict.get("not_key", 0), 0) diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 37c78eb3b8..848cc3b851 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -37,7 +37,7 @@ def test_basic(): assert_true(b or True) assert_false(b or False) - assert_equal(1, a.value()[]) + assert_equal(1, a.value()) # Test invert operator assert_false(~a) @@ -56,8 +56,8 @@ def test_basic(): # We may want to come back and add an immutable test once # there are the language features to do so. var a2 = Optional(1) - a2.value()[] = 2 - assert_equal(a2.value()[], 2) + a2.value() = 2 + assert_equal(a2.value(), 2) def test_optional_reg_basic(): diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index f776ff5973..00d8006f56 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -122,7 +122,7 @@ fn test_gettempdir() raises: tmpdir_result = gettempdir() assert_true(tmpdir_result, "Failed to get temporary directory") assert_equal( - tmpdir_result.value()[], + tmpdir_result.value(), str(dir_with_writing_access), "expected to get:" + str(dir_with_writing_access), ) @@ -137,7 +137,7 @@ fn test_gettempdir() raises: tmpdir_result = gettempdir() assert_true(tmpdir_result, "Failed to get temporary directory") assert_equal( - tmpdir_result.value()[], + tmpdir_result.value(), str(dir_with_writing_access), "expected to get:" + str(dir_with_writing_access), ) @@ -153,7 +153,7 @@ fn test_gettempdir() raises: tmpdir_result = gettempdir() assert_true(tmpdir_result, "Failed to get temporary directory") assert_equal( - tmpdir_result.value()[], + tmpdir_result.value(), str(dir_with_writing_access), "expected to get:" + str(dir_with_writing_access), ) From 2a6827370be29574096781658e90930c94a97410 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Sun, 2 Jun 2024 21:29:15 -0600 Subject: [PATCH 0841/2019] [stdlib] Add comp-time tests for `String.split()` Add various compile-time tests to show `String.split()` works in different settings. Previously, these tests failed prior to not using the global lookup table. MODULAR_ORIG_COMMIT_REV_ID: e0bb8c31cbf76a5df6dd203156b735fad7054864 --- stdlib/test/builtin/test_string.mojo | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 114960d1e6..68dc165eca 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -989,28 +989,45 @@ fn test_strip() raises: # with default strip chars var empty_string = String("") assert_true(empty_string.strip() == "") + alias comp_empty_string_stripped = String("").strip() + assert_true(comp_empty_string_stripped == "") var space_string = String(" \t\n\r\v\f ") assert_true(space_string.strip() == "") + alias comp_space_string_stripped = String(" \t\n\r\v\f ").strip() + assert_true(comp_space_string_stripped == "") var str0 = String(" n ") assert_true(str0.strip() == "n") + alias comp_str0_stripped = String(" n ").strip() + assert_true(comp_str0_stripped == "n") var str1 = String("string") assert_true(str1.strip() == "string") + alias comp_str1_stripped = String("string").strip() + assert_true(comp_str1_stripped == "string") var str2 = String(" \t\n\t\v\fsomething \t\n\t\v\f") + alias comp_str2_stripped = String(" \t\n\t\v\fsomething \t\n\t\v\f").strip() assert_true(str2.strip() == "something") + assert_true(comp_str2_stripped == "something") # with custom strip chars var str3 = String("mississippi") assert_true(str3.strip("mips") == "") assert_true(str3.strip("mip") == "ssiss") + alias comp_str3_stripped = String("mississippi").strip("mips") + assert_true(comp_str3_stripped == "") var str4 = String(" \n mississippimississippi \n ") assert_true(str4.strip(" ") == "\n mississippimississippi \n") assert_true(str4.strip("\nmip ") == "ssissippimississ") + alias comp_str4_stripped = String(" \n mississippimississippi \n ").strip( + " " + ) + assert_true(comp_str4_stripped == "\n mississippimississippi \n") + fn test_hash() raises: fn assert_hash_equals_literal_hash[s: StringLiteral]() raises: From c6824f7509263e6c71bd9a3f9fdbece723a38121 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 3 Jun 2024 04:20:46 -0500 Subject: [PATCH 0842/2019] [External] [stdlib] `memcmp` refactor for string comparisons (#41118) [External] [stdlib] `memcmp` refactor for string comparisons This is a follow-up to #2740. Refactored the `memcmp` implementation to give `StringRef` a `constrained`-less version for vectorized string comparisons that avoid an internal recursion bug. ORIGINAL_AUTHOR=Simon Hellsten <56205346+siitron@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2926 Co-authored-by: Simon Hellsten <56205346+siitron@users.noreply.github.com> Closes modularml/mojo#2926 MODULAR_ORIG_COMMIT_REV_ID: 53e819bfd8ec6f6d85610677131edefa4207087f --- stdlib/src/memory/memory.mojo | 20 ++++++++++++-------- stdlib/src/utils/stringref.mojo | 20 +++++++------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 0ce2fbdce2..7422b66c7a 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -42,8 +42,9 @@ fn _align_down(value: Int, alignment: Int) -> Int: @always_inline -fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: - constrained[s1.type.is_integral(), "the input dtype must be integral"]() +fn _memcmp_impl_unconstrained( + s1: DTypePointer, s2: __type_of(s1), count: Int +) -> Int: alias simd_width = simdwidthof[s1.type]() if count < simd_width: for i in range(count): @@ -59,8 +60,9 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: has_side_effect=False, ]() - var vector_end_simd = _align_down(count, simd_width) - for i in range(0, vector_end_simd, simd_width): + var last = count - simd_width + + for i in range(0, last, simd_width): var s1i = s1.load[width=simd_width](i) var s2i = s2.load[width=simd_width](i) var diff = s1i != s2i @@ -72,10 +74,6 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: ) return -1 if s1i[index] < s2i[index] else 1 - var last = count - simd_width - if last <= 0: - return 0 - var s1i = s1.load[width=simd_width](last) var s2i = s2.load[width=simd_width](last) var diff = s1i != s2i @@ -87,6 +85,12 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: return 0 +@always_inline +fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: + constrained[s1.type.is_integral(), "the input dtype must be integral"]() + return _memcmp_impl_unconstrained(s1, s2, count) + + @always_inline fn memcmp(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: """Compares two buffers. Both strings are assumed to be of the same length. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 790c77dddd..f2f13b9576 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -17,6 +17,7 @@ from bit import countr_zero from builtin.dtype import _uint_type_of_width from builtin.string import _atol, _isspace from memory import DTypePointer, UnsafePointer, memcmp +from memory.memory import _memcmp_impl_unconstrained # ===----------------------------------------------------------------------=== # @@ -294,7 +295,9 @@ struct StringRef( Returns: True if the strings do not match and False otherwise. """ - return len(self) != len(rhs) or self._memcmp(rhs, len(self)) + return len(self) != len(rhs) or _memcmp_impl_unconstrained( + self.data, rhs.data, len(self) + ) @always_inline fn __lt__(self, rhs: StringRef) -> Bool: @@ -309,7 +312,9 @@ struct StringRef( """ var len1 = len(self) var len2 = len(rhs) - return self._memcmp(rhs, min(len1, len2)) < int(len1 < len2) + return int(len1 < len2) > _memcmp_impl_unconstrained( + self.data, rhs.data, min(len1, len2) + ) @always_inline fn __le__(self, rhs: StringRef) -> Bool: @@ -406,17 +411,6 @@ struct StringRef( # Methods # ===-------------------------------------------------------------------===# - # Use a local memcmp rather than memory.memcpy to avoid indirect recursions. - @always_inline("nodebug") - fn _memcmp(self, other: StringRef, count: Int) -> Int: - for i in range(count): - var s1i = self.data[i] - var s2i = other.data[i] - if s1i == s2i: - continue - return 1 if s1i > s2i else -1 - return 0 - @always_inline fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. From a6950de508b8e999863ac75d456ebce7795993e8 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 06:20:39 -0400 Subject: [PATCH 0843/2019] [stdlib][NFC] Improve a few style nits in `sys` MODULAR_ORIG_COMMIT_REV_ID: 3bc7b195172907dc32f0ae4dac015023eb65295f --- stdlib/src/sys/_build.mojo | 41 ++++++++++++++++------------------- stdlib/src/sys/param_env.mojo | 13 +++-------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/stdlib/src/sys/_build.mojo b/stdlib/src/sys/_build.mojo index f79da40cc2..d3f4632b1f 100644 --- a/stdlib/src/sys/_build.mojo +++ b/stdlib/src/sys/_build.mojo @@ -16,13 +16,13 @@ from .param_env import env_get_string, is_defined @always_inline("nodebug") -fn _build_info_type() -> StringLiteral: +fn _build_type() -> StringLiteral: constrained[is_defined["BUILD_TYPE"](), "the build type must be defined"]() return env_get_string["BUILD_TYPE"]() @always_inline("nodebug") -fn _kernels_build_info_type() -> StringLiteral: +fn _kernels_build_type() -> StringLiteral: constrained[ is_defined["KERNELS_BUILD_TYPE"](), "the kernels build type must be defined", @@ -42,11 +42,10 @@ fn is_kernels_debug_build() -> Bool: @parameter if is_defined["DEBUG"](): return True - - @parameter - if not is_defined["KERNELS_BUILD_TYPE"](): + elif is_defined["KERNELS_BUILD_TYPE"](): + return _kernels_build_type() == "debug" + else: return False - return _kernels_build_info_type() == "debug" @always_inline("nodebug") @@ -61,11 +60,10 @@ fn is_debug_build() -> Bool: @parameter if is_defined["DEBUG"](): return True - - @parameter - if not is_defined["BUILD_TYPE"](): + elif is_defined["BUILD_TYPE"](): + return _build_type() == "debug" + else: return False - return _build_info_type() == "debug" @always_inline("nodebug") @@ -80,15 +78,15 @@ fn is_release_build() -> Bool: @parameter if is_defined["DEBUG"](): return False - - @parameter - if not is_defined["BUILD_TYPE"](): + elif is_defined["BUILD_TYPE"](): + alias build_type: StringLiteral = _build_type() + return ( + build_type == "release" + or build_type == "relwithdebinfo" + or build_type == "minsizerel" + ) + else: return True - return ( - _build_info_type() == "release" - or _build_info_type() == "relwithdebinfo" - or _build_info_type() == "minsizerel" - ) @always_inline("nodebug") @@ -103,8 +101,7 @@ fn is_relwithdebinfo_build() -> Bool: @parameter if is_defined["DEBUG"](): return True - - @parameter - if not is_defined["BUILD_TYPE"](): + elif is_defined["BUILD_TYPE"](): + return _build_type() == "relwithdebinfo" + else: return False - return _build_info_type() == "relwithdebinfo" diff --git a/stdlib/src/sys/param_env.mojo b/stdlib/src/sys/param_env.mojo index 8e5a970481..c9b43c01be 100644 --- a/stdlib/src/sys/param_env.mojo +++ b/stdlib/src/sys/param_env.mojo @@ -50,10 +50,7 @@ fn is_defined[name: StringLiteral]() -> Bool: Returns: True if the name is defined. """ - alias result = __mlir_attr[ - `#kgen.param.expr : i1` - ] - return result + return __mlir_attr[`#kgen.param.expr : i1`] fn env_get_int[name: StringLiteral]() -> Int: @@ -66,10 +63,7 @@ fn env_get_int[name: StringLiteral]() -> Int: Returns: An integer parameter value. """ - alias result = __mlir_attr[ - `#kgen.param.expr : index` - ] - return result + return __mlir_attr[`#kgen.param.expr : index`] fn env_get_int[name: StringLiteral, default: Int]() -> Int: @@ -101,10 +95,9 @@ fn env_get_string[name: StringLiteral]() -> StringLiteral: Returns: A string parameter value. """ - alias result = __mlir_attr[ + return __mlir_attr[ `#kgen.param.expr : !kgen.string` ] - return result fn env_get_string[ From b30261851017b4131ffc54d1ef087635658ff4b1 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 06:55:34 -0400 Subject: [PATCH 0844/2019] [stdlib] Remove `StringLiteral` constructors from `Path` Because these manually materialize a `String` anyway, and soon will be prohibited. MODULAR_ORIG_COMMIT_REV_ID: a6c28c9c07c02eb8605f0f8009317cd4bc70132f --- stdlib/src/pathlib/path.mojo | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 470d4bb869..85aec2f0aa 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -54,14 +54,6 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """Initializes a path with the current directory.""" self = cwd() - fn __init__(inout self, path: StringLiteral): - """Initializes a path with the provided path. - - Args: - path: The file system path. - """ - self.path = path - fn __init__(inout self, path: String): """Initializes a path with the provided path. @@ -98,17 +90,6 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """ return self.__truediv__(suffix.path) - fn __truediv__(self, suffix: StringLiteral) -> Self: - """Joins two paths using the system-defined path separator. - - Args: - suffix: The suffix to append to the path. - - Returns: - A new path with the suffix appended to the current path. - """ - return self.__truediv__(String(suffix)) - fn __truediv__(self, suffix: String) -> Self: """Joins two paths using the system-defined path separator. From db817cd6d3a1462ae6ea582233846e6e27d6ff02 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 3 Jun 2024 08:11:50 -0500 Subject: [PATCH 0845/2019] [External] [stdlib] Add `Bool.__repr__()` (#41136) [External] [stdlib] Add `Bool.__repr__()` Related to: * https://github.com/modularml/mojo/issues/2458 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2935 MODULAR_ORIG_COMMIT_REV_ID: a09a3f4ee7bab9943c0926e6d28029d53f8c9546 --- stdlib/src/builtin/bool.mojo | 13 +++++++++++++ stdlib/test/builtin/test_bool.mojo | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 6527fb462d..7363044c98 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -64,6 +64,7 @@ struct Bool( Boolable, Intable, Indexer, + Representable, ): """The primitive Bool scalar value used in Mojo.""" @@ -141,11 +142,23 @@ struct Bool( fn __str__(self) -> String: """Get the bool as a string. + Returns `"True"` or `"False"`. + Returns: A string representation. """ return "True" if self else "False" + fn __repr__(self) -> String: + """Get the bool as a string. + + Returns `"True"` or `"False"`. + + Returns: + A string representation. + """ + return str(self) + @always_inline("nodebug") fn __int__(self) -> Int: """Convert this Bool to an integer. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 5458834ab5..7a24b56a1c 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -49,6 +49,11 @@ def test_bool_to_string(): assert_equal(str(False), "False") +def test_bool_representation(): + assert_equal(repr(True), "True") + assert_equal(repr(False), "False") + + def test_bitwise(): var value: Bool @@ -142,6 +147,7 @@ def main(): test_bool_none() test_convert_from_boolable() test_bool_to_string() + test_bool_representation() test_bitwise() test_neg() test_indexer() From c684ebc80ae3832b1c22eee4a274734319f5c9ef Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 10:40:26 -0400 Subject: [PATCH 0846/2019] [stdlib] Add some bf16 tests MODULAR_ORIG_COMMIT_REV_ID: bdf7d70cb8e9807e242a2b07fdb84d000a3e527c --- stdlib/test/builtin/test_simd.mojo | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index b4aa7afded..4bd191288a 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -329,6 +329,10 @@ def test_ceil(): assert_equal(Float32.__ceil__(Float32(-1.5)), -1.0) assert_equal(Float32.__ceil__(Float32(3.0)), 3.0) + @parameter + if not has_neon(): + assert_equal(BFloat16.__ceil__(BFloat16(2.5)), 3.0) + alias F = SIMD[DType.float32, 4] assert_equal( F.__ceil__(F(0.0, 1.4, -42.5, -12.6)), F(0.0, 2.0, -42.0, -12.0) @@ -352,6 +356,10 @@ def test_floor(): assert_equal(Float32.__floor__(Float32(-1.5)), -2.0) assert_equal(Float32.__floor__(Float32(3.0)), 3.0) + @parameter + if not has_neon(): + assert_equal(BFloat16.__floor__(BFloat16(2.5)), 2.0) + alias F = SIMD[DType.float32, 4] assert_equal( F.__floor__(F(0.0, 1.6, -42.5, -12.4)), F(0.0, 1.0, -43.0, -13.0) From 77df303beb9ed5a6d5fea31a16f17a6068409f5e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 3 Jun 2024 11:18:16 -0400 Subject: [PATCH 0847/2019] [mojo] Implement raises and in-memory results on async functions This PR switches the representation of results in async functions to always be `byref_result`. Consequently, it also implements support for throwing async functions. At the same time, alter the relevant stdlib components: Coroutine and RaisingCoroutine now accept `AnyType` as the result type of the async function and change the implementations accordingly: each passes the litref for their result into the coroutine to be populated by the async function and RaisingCoroutine passes the litref for its function error slot to the coroutine as well. MODULAR_ORIG_COMMIT_REV_ID: 6e0fb2bf4b20c6780a5493e4bdd2ba8cf060dafa --- docs/changelog.md | 5 ++ stdlib/src/builtin/coroutine.mojo | 83 +++++++++++++++++++------------ 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 02a877b0a4..6f97462a7b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,11 @@ what we publish. ### ⭐️ New +- `async` functions now support memory-only results (like `String`, `List`, + etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have + been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means + the result types of `async` functions do not need to be `Movable`. + - The `Reference` type (and many iterators) now use "inferred" parameters to represent the mutability of their lifetime, simplifying the interface. diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index e3d92fa03e..7a0b96148f 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -81,7 +81,7 @@ fn _coro_resume_noop_callback(handle: AnyCoroutine, null: AnyCoroutine): @register_passable -struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: +struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: """Represents a coroutine. Coroutines can pause execution saving the state of the program (including @@ -97,7 +97,7 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: var _handle: AnyCoroutine @always_inline - fn _get_ctx[ctx_type: AnyTrivialRegType](self) -> UnsafePointer[ctx_type]: + fn _get_ctx[ctx_type: AnyType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. Parameters: @@ -115,13 +115,12 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: ](self._handle) @always_inline - fn get(self) -> type: - """Get the value of the fulfilled coroutine promise. - - Returns: - The value of the fulfilled promise. - """ - return __mlir_op.`co.get_results`[_type=type](self._handle) + fn _set_result_slot(self, slot: UnsafePointer[type]): + __mlir_op.`co.set_byref_error_result`( + self._handle, + __mlir_attr.`#interp.pointer<0> : !kgen.pointer`, + slot.address, + ) @always_inline fn __init__(handle: AnyCoroutine) -> Self: @@ -141,6 +140,7 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: __mlir_op.`co.destroy`(self._handle) @always_inline + @__named_result(out) fn __await__(self) -> type: """Suspends the current coroutine until the coroutine is complete. @@ -148,6 +148,11 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: The coroutine promise. """ + # Black magic! Internal implementation detail! + # Don't you dare copy this code! 😤 + __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) + self._set_result_slot(UnsafePointer.address_of(out)) + @always_inline @parameter fn await_body(parent_hdl: AnyCoroutine): @@ -159,9 +164,9 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: __mlir_op.`co.resume`(self._handle) _suspend_async[await_body]() - return self.get() # Never call this method. + @__named_result(out) fn _deprecated_direct_resume(self) -> type: LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( _CoroutineContext { @@ -169,8 +174,13 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: _parent_hdl: self._handle, } ) + __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) + __mlir_op.`co.set_byref_error_result`( + self._handle, + __mlir_attr.`#interp.pointer<0> : !kgen.pointer`, + UnsafePointer.address_of(out).address, + ) __mlir_op.`co.resume`(self._handle) - return self.get() # ===----------------------------------------------------------------------=== # @@ -179,7 +189,7 @@ struct Coroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: @register_passable -struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: +struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: """Represents a coroutine that can raise. Coroutines can pause execution saving the state of the program (including @@ -192,25 +202,10 @@ struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: lifetimes: The lifetime set of the coroutine's captures. """ - alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] var _handle: AnyCoroutine @always_inline - fn get(self) raises -> type: - """Get the value of the fulfilled coroutine promise. - - Returns: - The value of the fulfilled promise. - """ - var variant = __mlir_op.`co.get_results`[_type = Self._var_type]( - self._handle - ) - if __mlir_op.`kgen.variant.is`[index = Int(0).value](variant): - raise __mlir_op.`kgen.variant.take`[index = Int(0).value](variant) - return __mlir_op.`kgen.variant.take`[index = Int(1).value](variant) - - @always_inline - fn _get_ctx[ctx_type: AnyTrivialRegType](self) -> UnsafePointer[ctx_type]: + fn _get_ctx[ctx_type: AnyType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. Parameters: @@ -228,13 +223,24 @@ struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: ](self._handle) @always_inline - fn __init__(inout self, handle: AnyCoroutine): + fn _set_result_slot( + self, slot: UnsafePointer[type], err: UnsafePointer[Error] + ): + __mlir_op.`co.set_byref_error_result`( + self._handle, err.address, slot.address + ) + + @always_inline + fn __init__(handle: AnyCoroutine) -> Self: """Construct a coroutine object from a handle. Args: handle: The init handle. + + Returns: + An owning coroutine. """ - self = Self {_handle: handle} + return Self {_handle: handle} @always_inline fn __del__(owned self): @@ -242,6 +248,7 @@ struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: __mlir_op.`co.destroy`(self._handle) @always_inline + @__named_result(out) fn __await__(self) raises -> type: """Suspends the current coroutine until the coroutine is complete. @@ -249,6 +256,15 @@ struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: The coroutine promise. """ + # Black magic! Internal implementation detail! + # Don't you dare copy this code! 😤 + self._set_result_slot( + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), + __mlir_op.`lit.ref.to_pointer`( + __get_mvalue_as_litref(__get_nearest_error_slot()) + ), + ) + @always_inline @parameter fn await_body(parent_hdl: AnyCoroutine): @@ -260,4 +276,9 @@ struct RaisingCoroutine[type: AnyTrivialRegType, lifetimes: LifetimeSet]: __mlir_op.`co.resume`(self._handle) _suspend_async[await_body]() - return self.get() + if __mlir_op.`co.get_results`[_type = __mlir_type.i1](self._handle): + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(__get_nearest_error_slot()) + ) + __mlir_op.`lit.raise`() + __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) From 048c5a10979dac871278c3908bd792913f066211 Mon Sep 17 00:00:00 2001 From: Davood Mohajerani Date: Mon, 3 Jun 2024 12:19:46 -0400 Subject: [PATCH 0848/2019] =?UTF-8?q?[******]=20Remove=C2=A0`Bencher`'s?= =?UTF-8?q?=C2=A0=20`throughput=5Felems`=C2=A0and=20replace=20test=20usage?= =?UTF-8?q?s=20with=C2=A0`ThroughputMeasure`=20(#41009)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Changes - Functions `bench_function` and `bench_with_input` now only accept `measures=List[ThroughputMeasure]`. - Also, added variadic overloads to accept `*measures=ThroughputMeasure`. - Updated all the existing tests and benchmarks. - Closes KERN-494 MODULAR_ORIG_COMMIT_REV_ID: b27000fda7605ecae1450cd50bb8f96e33d8c9e1 --- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index ae33075de2..c03ec790ed 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -23,7 +23,14 @@ from random import rand from algorithm import vectorize from benchmark import Unit, run -from benchmark import Bench, Bencher, BenchId, keep +from benchmark import ( + Bench, + Bencher, + BenchId, + BenchMetric, + ThroughputMeasure, + keep, +) from memory.unsafe import DTypePointer from memory import memcmp @@ -189,7 +196,9 @@ fn test_vectorize[ + str(unroll_factor) ) - m.bench_function[bench_](bench_id, throughput_elems=N) + m.bench_function[bench_]( + bench_id, ThroughputMeasure(BenchMetric.elements, N) + ) vector.free() result.free() From 6cec7e0c3db79975cc4f05e4d51e60b54f3f097a Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:20:17 -0400 Subject: [PATCH 0849/2019] [stdlib] Move `load` into `SIMD`. Instead of `ptr.load[width=4](offset)` we should do: `SIMD[width=4].load(ptr, offset)`. In particular, `ptr.load(i)` should be changed to `Scalar.load(ptr, i)` since the default width of original `load` is 1 but for `SIMD` it is the size of the type. MODULAR_ORIG_COMMIT_REV_ID: 9000f0fc338689997a7ff35febcfe8d48d0daa32 --- docs/changelog.md | 6 + examples/mandelbrot.mojo | 2 +- examples/notebooks/Mandelbrot.ipynb | 4 +- examples/notebooks/Matmul.ipynb | 4 +- .../benchmarks/algorithm/bench_vectorize.mojo | 97 ++++++----- stdlib/benchmarks/utils/bench_memmem.mojo | 4 +- stdlib/src/builtin/hash.mojo | 4 +- stdlib/src/builtin/simd.mojo | 153 ++++++++++++++++++ stdlib/src/builtin/string.mojo | 6 +- stdlib/src/collections/dict.mojo | 16 +- stdlib/src/memory/memory.mojo | 20 ++- stdlib/src/memory/unsafe.mojo | 125 +------------- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/sys/intrinsics.mojo | 20 +-- stdlib/src/utils/_serialize.mojo | 4 +- stdlib/src/utils/stringref.mojo | 14 +- stdlib/test/builtin/test_file.mojo | 5 +- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/memory/test_memory.mojo | 36 ++--- 19 files changed, 298 insertions(+), 226 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6f97462a7b..baa53630e5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -61,6 +61,12 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` +- `DTypePointer.load/store/prefetch` has been now moved to `SIMD`. Instead of + using `ptr.load[width=4](offset)` one should use `SIMD[size=4].load(ptr, offset)`. + Note the default load width before was 1, but the default size of `SIMD` is + the size of the SIMD type. + The default store size is the size of the `SIMD` value to be stored. + ### ❌ Removed - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index b93961f579..a5aa1c366e 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -41,7 +41,7 @@ struct Matrix[type: DType, rows: Int, cols: Int]: self.data = DTypePointer[type].alloc(rows * cols) fn store[nelts: Int](self, row: Int, col: Int, val: SIMD[type, nelts]): - self.data.store[width=nelts](row * cols + col, val) + SIMD[size=nelts].store(self.data, row * cols + col, val) fn mandelbrot_kernel_SIMD[ diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 8f42d55a8b..7f291a96fb 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -104,10 +104,10 @@ " self.data = DTypePointer[type].alloc(rows * cols)\n", "\n", " fn __getitem__(self, row: Int, col: Int) -> Scalar[type]:\n", - " return self.data.load(row * cols + col)\n", + " return Scalar.load(self.data, row * cols + col)\n", "\n", " fn store[width: Int = 1](self, row: Int, col: Int, val: SIMD[type, width]):\n", - " self.data.store[width=width](row * cols + col, val)" + " SIMD[size=width].store(self.data, row * cols + col, val)" ] }, { diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index eff5e1f81d..7e2d149531 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -437,10 +437,10 @@ " self.store[1](y, x, val)\n", "\n", " fn load[nelts: Int](self, y: Int, x: Int) -> SIMD[type, nelts]:\n", - " return self.data.load[width=nelts](y * self.cols + x)\n", + " return SIMD[size=nelts].load(self.data, y * self.cols + x)\n", "\n", " fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]):\n", - " return self.data.store[width=nelts](y * self.cols + x, val)" + " return SIMD[size=nelts].store(self.data, y * self.cols + x, val)" ] }, { diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index c03ec790ed..389bb2abc3 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -77,14 +77,16 @@ fn test_vectorize[ @always_inline @parameter fn ld_vector[simd_width: Int](idx: Int): - vector.store[width=simd_width]( - idx + 1, SIMD[vector.type, simd_width](idx) + SIMD[size=simd_width].store( + vector, idx + 1, SIMD[vector.type, simd_width](idx) ) @always_inline @parameter fn st_vector[simd_width: Int](idx: Int): - result.store[width=simd_width](idx, vector.load[width=simd_width](idx)) + SIMD[size=simd_width].store( + result, idx, SIMD[size=simd_width].load(vector, idx) + ) @__copy_capture(vector) @always_inline @@ -95,24 +97,24 @@ fn test_vectorize[ @parameter if op == Op.add: - vector.store[width=simd_width]( - idx, vector.load[width=simd_width](idx) + x + SIMD[size=simd_width].store( + vector, idx, SIMD[size=simd_width].load(vector, idx) + x ) elif op == Op.sub: - vector.store[width=simd_width]( - idx, vector.load[width=simd_width](idx) - x + SIMD[size=simd_width].store( + vector, idx, SIMD[size=simd_width].load(vector, idx) - x ) elif op == Op.mul: - vector.store[width=simd_width]( - idx, vector.load[width=simd_width](idx) * x + SIMD[size=simd_width].store( + vector, idx, SIMD[size=simd_width].load(vector, idx) * x ) elif op == Op.div: - vector.store[width=simd_width]( - idx, vector.load[width=simd_width](idx) / x + SIMD[size=simd_width].store( + vector, idx, SIMD[size=simd_width].load(vector, idx) / x ) elif op == Op.fma: - vector.store[width=simd_width]( - idx, vector.load[width=simd_width](idx) * x + y + SIMD[size=simd_width].store( + vector, idx, SIMD[size=simd_width].load(vector, idx) * x + y ) @__copy_capture(vector) @@ -121,35 +123,40 @@ fn test_vectorize[ fn arithmetic_vector[simd_width: Int](idx: Int): @parameter if op == Op.add: - vector.store[width=simd_width]( + SIMD[size=simd_width].store( + vector, idx, - vector.load[width=simd_width](idx) - + vector.load[width=simd_width](idx), + SIMD[size=simd_width].load(vector, idx) + + SIMD[size=simd_width].load(vector, idx), ) elif op == Op.sub: - vector.store[width=simd_width]( + SIMD[size=simd_width].store( + vector, idx, - vector.load[width=simd_width](idx) - - vector.load[width=simd_width](idx), + SIMD[size=simd_width].load(vector, idx) + - SIMD[size=simd_width].load(vector, idx), ) elif op == Op.mul: - vector.store[width=simd_width]( + SIMD[size=simd_width].store( + vector, idx, - vector.load[width=simd_width](idx) - * vector.load[width=simd_width](idx), + SIMD[size=simd_width].load(vector, idx) + * SIMD[size=simd_width].load(vector, idx), ) elif op == Op.div: - vector.store[width=simd_width]( + SIMD[size=simd_width].store( + vector, idx, - vector.load[width=simd_width](idx) - / vector.load[width=simd_width](idx), + SIMD[size=simd_width].load(vector, idx) + / SIMD[size=simd_width].load(vector, idx), ) elif op == Op.fma: - vector.store[width=simd_width]( + SIMD[size=simd_width].store( + vector, idx, - vector.load[width=simd_width](idx) - * vector.load[width=simd_width](idx) - + vector.load[width=simd_width](idx), + SIMD[size=simd_width].load(vector, idx) + * SIMD[size=simd_width].load(vector, idx) + + SIMD[size=simd_width].load(vector, idx), ) @always_inline @@ -266,7 +273,11 @@ fn bench_compare(): fn arg_size(): @parameter fn closure[width: Int](i: Int): - p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + SIMD.store( + p2, + i, + SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + ) for i in range(its): vectorize[closure, width](size) @@ -275,7 +286,11 @@ fn bench_compare(): fn param_size(): @parameter fn closure[width: Int](i: Int): - p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + SIMD.store( + p2, + i, + SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + ) for i in range(its): vectorize[closure, width, size=size]() @@ -284,7 +299,11 @@ fn bench_compare(): fn arg_size_unroll(): @parameter fn closure[width: Int](i: Int): - p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + SIMD.store( + p2, + i, + SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + ) for i in range(its): vectorize[closure, width, unroll_factor=unroll_factor](size) @@ -293,25 +312,29 @@ fn bench_compare(): fn param_size_unroll(): @parameter fn closure[width: Int](i: Int): - p2.store(i, p1.load[width=width](i) + p2.load[width=width](i)) + SIMD.store( + p2, + i, + SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + ) for i in range(its): vectorize[closure, width, size=size, unroll_factor=unroll_factor]() var arg = run[arg_size](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=size]()) + print(SIMD[size=size].load(p2)) memset_zero(p2, size) var param = run[param_size](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=size]()) + print(SIMD[size=size].load(p2)) memset_zero(p2, size) var arg_unroll = run[arg_size_unroll](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=size]()) + print(SIMD[size=size].load(p2)) memset_zero(p2, size) var param_unroll = run[param_size_unroll](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=size]()) + print(SIMD[size=size].load(p2)) print( "calculating", diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 6e2d57c55e..c92a03bafa 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -164,7 +164,9 @@ fn _memmem_baseline[ haystack_len - needle_len + 1, bool_mask_width ) for i in range(0, vectorized_end, bool_mask_width): - var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle + var bool_mask = SIMD[size=bool_mask_width].load( + haystack, i + ) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: var offset = i + countr_zero(mask) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 8381df9e17..1bdc901451 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -273,7 +273,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: # 2. Compute the hash, but strided across the SIMD vector width. var hash_data = _HASH_INIT[type, simd_width]() for i in range(k): - var update = simd_data.load[width=simd_width](i * simd_width) + var update = SIMD[size=simd_width].load(simd_data, i * simd_width) hash_data = _HASH_UPDATE(hash_data, update) # 3. Copy the tail data (smaller than the SIMD register) into @@ -285,7 +285,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: ) memcpy(ptr, bytes + k * stride, r) memset_zero(ptr + r, stride - r) # set the rest to 0 - var last_value = ptr.bitcast[type]().load[width=simd_width]() + var last_value = SIMD[size=simd_width].load(ptr.bitcast[type]()) hash_data = _HASH_UPDATE(hash_data, last_value) # Now finally, hash the final SIMD vector state. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b2a2d9d4d4..03a7d21b90 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -23,6 +23,8 @@ from sys import ( triple_is_nvidia_cuda, simdwidthof, _RegisterPackType, + PrefetchOptions, + prefetch, ) from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @@ -436,6 +438,157 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) ) + alias _default_alignment = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1 + + @staticmethod + @always_inline + fn prefetch[ + params: PrefetchOptions, + *, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space]): + # Prefetch at the underlying address. + """Prefetches memory at the underlying address. + + Parameters: + params: Prefetch options (see `PrefetchOptions` for details). + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to prefetch from. + """ + prefetch[params](ptr) + + @staticmethod + @always_inline("nodebug") + fn load[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space]) -> Self: + """Loads the value the Pointer object points to. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + + Returns: + The loaded value. + """ + return Self.load[alignment=alignment, address_space=address_space]( + ptr, 0 + ) + + @staticmethod + @always_inline("nodebug") + fn load[ + T: Intable, + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: T) -> Self: + """Loads the value the Pointer object points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + T: The Intable type of the offset. + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + offset: The offset to load from. + + Returns: + The loaded value. + """ + + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: + # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types + # regardless of the alignment that is passed. This causes issues if + # this method is called on an unaligned pointer. + # TODO #37823 We can make this smarter when we add an `aligned` + # trait to the pointer class. + var v = SIMD[type, size]() + + # intentionally don't unroll, otherwise the compiler vectorizes + for i in range(size): + v[i] = ptr.address.offset(int(offset) + i).load[ + alignment=alignment + ]() + return v + + return ( + ptr.address.offset(offset) + .bitcast[SIMD[type, size]]() + .load[alignment=alignment]() + ) + + @staticmethod + @always_inline("nodebug") + fn store[ + T: Intable, + /, + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: T, val: Self): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + T: The Intable type of the offset. + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + offset: The offset to store to. + val: The value to store. + """ + Self.store[alignment=alignment, address_space=address_space]( + ptr.offset(offset), val + ) + + @staticmethod + @always_inline("nodebug") + fn store[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], val: Self): + """Stores a single element value. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + val: The value to store. + """ + constrained[size > 0, "width must be a positive integer value"]() + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() + ptr.address.bitcast[SIMD[type, size]]().store[alignment=alignment](val) + @always_inline("nodebug") fn __len__(self) -> Int: """Gets the length of the SIMD vector. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 8ae0efdcbe..8b80d454bb 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -109,11 +109,11 @@ fn chr(c: Int) -> String: var shift = 6 * (num_bytes - 1) var mask = UInt8(0xFF) >> (num_bytes + 1) var num_bytes_marker = UInt8(0xFF) << (8 - num_bytes) - p.store(((c >> shift) & mask) | num_bytes_marker) + Scalar.store(p, ((c >> shift) & mask) | num_bytes_marker) for i in range(1, num_bytes): shift -= 6 - p.store(i, ((c >> shift) & 0b00111111) | 0b10000000) - p.store(num_bytes, 0) + Scalar.store(p, i, ((c >> shift) & 0b00111111) | 0b10000000) + Scalar.store(p, num_bytes, 0) return String(p.bitcast[DType.uint8](), num_bytes + 1) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index a7311b2324..5db51cfe9e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -277,30 +277,30 @@ struct _DictIndex: fn get_index(self, reserved: Int, slot: Int) -> Int: if reserved <= 128: var data = self.data.bitcast[DType.int8]() - return int(data.load(slot % reserved)) + return int(Scalar.load(data, slot % reserved)) elif reserved <= 2**16 - 2: var data = self.data.bitcast[DType.int16]() - return int(data.load(slot % reserved)) + return int(Scalar.load(data, slot % reserved)) elif reserved <= 2**32 - 2: var data = self.data.bitcast[DType.int32]() - return int(data.load(slot % reserved)) + return int(Scalar.load(data, slot % reserved)) else: var data = self.data.bitcast[DType.int64]() - return int(data.load(slot % reserved)) + return int(Scalar.load(data, slot % reserved)) fn set_index(inout self, reserved: Int, slot: Int, value: Int): if reserved <= 128: var data = self.data.bitcast[DType.int8]() - return data.store(slot % reserved, value) + return Scalar.store(data, slot % reserved, value) elif reserved <= 2**16 - 2: var data = self.data.bitcast[DType.int16]() - return data.store(slot % reserved, value) + return Scalar.store(data, slot % reserved, value) elif reserved <= 2**32 - 2: var data = self.data.bitcast[DType.int32]() - return data.store(slot % reserved, value) + return Scalar.store(data, slot % reserved, value) else: var data = self.data.bitcast[DType.int64]() - return data.store(slot % reserved, value) + return Scalar.store(data, slot % reserved, value) fn __del__(owned self): self.data.free() diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 7422b66c7a..a4f6726e36 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -63,8 +63,8 @@ fn _memcmp_impl_unconstrained( var last = count - simd_width for i in range(0, last, simd_width): - var s1i = s1.load[width=simd_width](i) - var s2i = s2.load[width=simd_width](i) + var s1i = SIMD[size=simd_width].load(s1, i) + var s2i = SIMD[size=simd_width].load(s2, i) var diff = s1i != s2i if any(diff): var index = int( @@ -74,8 +74,8 @@ fn _memcmp_impl_unconstrained( ) return -1 if s1i[index] < s2i[index] else 1 - var s1i = s1.load[width=simd_width](last) - var s2i = s2.load[width=simd_width](last) + var s1i = SIMD[size=simd_width].load(s1, last) + var s2i = SIMD[size=simd_width].load(s2, last) var diff = s1i != s2i if any(diff): var index = int( @@ -218,9 +218,11 @@ fn memcpy[count: Int](dest: LegacyPointer, src: __type_of(dest)): alias chunk_size = 32 alias vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - dest_dtype_ptr.store(i, src_dtype_ptr.load[width=chunk_size](i)) + SIMD.store( + dest_dtype_ptr, i, SIMD[size=chunk_size].load(src_dtype_ptr, i) + ) for i in range(vector_end, n): - dest_dtype_ptr.store(i, src_dtype_ptr.load[width=1](i)) + Scalar.store(dest_dtype_ptr, i, Scalar.load(src_dtype_ptr, i)) @always_inline @@ -297,9 +299,11 @@ fn memcpy( alias chunk_size = 32 var vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - dest_dtype_ptr.store(i, src_dtype_ptr.load[width=chunk_size](i)) + SIMD.store( + dest_dtype_ptr, i, SIMD[size=chunk_size].load(src_dtype_ptr, i) + ) for i in range(vector_end, n): - dest_dtype_ptr.store(i, src_dtype_ptr.load[width=1](i)) + Scalar.store(dest_dtype_ptr, i, Scalar.load(src_dtype_ptr, i)) @always_inline diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 8d839b55bc..c6de8d3136 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -27,8 +27,6 @@ from sys import ( sizeof, triple_is_nvidia_cuda, ) -from sys.intrinsics import PrefetchOptions, _mlirtype_is_eq -from sys.intrinsics import prefetch as _prefetch from sys.intrinsics import gather, scatter, strided_load, strided_store from bit import is_power_of_two @@ -688,7 +686,7 @@ struct DTypePointer[ Returns: The loaded value. """ - return self.load(offset) + return Scalar.load(self, offset) @always_inline("nodebug") fn __setitem__(self, offset: Int, val: Scalar[type]): @@ -698,7 +696,7 @@ struct DTypePointer[ offset: The offset to store to. val: The value to store. """ - return self.store(offset, val) + return Scalar.store(self, offset, val) # ===------------------------------------------------------------------=== # # Comparisons @@ -806,125 +804,6 @@ struct DTypePointer[ Scalar[type] ]() if triple_is_nvidia_cuda() else 1 - @always_inline - fn prefetch[params: PrefetchOptions](self): - # Prefetch at the underlying address. - """Prefetches memory at the underlying address. - - Parameters: - params: Prefetch options (see `PrefetchOptions` for details). - """ - _prefetch[params](self) - - @always_inline("nodebug") - fn load[ - *, width: Int = 1, alignment: Int = Self._default_alignment - ](self) -> SIMD[type, width]: - """Loads the value the Pointer object points to. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - width: The SIMD width. - alignment: The minimal alignment of the address. - - Returns: - The loaded value. - """ - return self.load[width=width, alignment=alignment](0) - - @always_inline("nodebug") - fn load[ - T: Intable, *, width: Int = 1, alignment: Int = Self._default_alignment - ](self, offset: T) -> SIMD[type, width]: - """Loads the value the Pointer object points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - T: The Intable type of the offset. - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The offset to load from. - - Returns: - The loaded value. - """ - - @parameter - if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: - # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types - # regardless of the alignment that is passed. This causes issues if - # this method is called on an unaligned pointer. - # TODO #37823 We can make this smarter when we add an `aligned` - # trait to the pointer class. - var v = SIMD[type, width]() - - # intentionally don't unroll, otherwise the compiler vectorizes - for i in range(width): - v[i] = self.address.offset(int(offset) + i).load[ - alignment=alignment - ]() - return v - - return ( - self.address.offset(offset) - .bitcast[SIMD[type, width]]() - .load[alignment=alignment]() - ) - - @always_inline("nodebug") - fn store[ - T: Intable, - /, - *, - width: Int = 1, - alignment: Int = Self._default_alignment, - ](self, offset: T, val: SIMD[type, width]): - """Stores a single element value at the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - T: The Intable type of the offset. - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The offset to store to. - val: The value to store. - """ - self.offset(offset).store[width=width, alignment=alignment](val) - - @always_inline("nodebug") - fn store[ - *, width: Int = 1, alignment: Int = Self._default_alignment - ](self, val: SIMD[type, width]): - """Stores a single element value. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - val: The value to store. - """ - constrained[width > 0, "width must be a positive integer value"]() - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - self.address.bitcast[SIMD[type, width]]().store[alignment=alignment]( - val - ) - @always_inline("nodebug") fn simd_nt_store[ width: Int, T: Intable diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 1421a17811..c59524e1dd 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -256,7 +256,7 @@ struct CPython: fn _Py_REFCNT(inout self, ptr: PyObjectPtr) -> Int: if ptr._get_ptr_as_int() == 0: return -1 - return int(ptr.value.load()) + return int(Scalar.load(ptr.value)) fn PyDict_New(inout self) -> PyObjectPtr: var r = self.lib.get_function[fn () -> PyObjectPtr]("PyDict_New")() diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index b8c97a9dd1..e501a0cca2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -858,7 +858,9 @@ fn gather[ @parameter if size == 1: - return DTypePointer[type](base[0]).load() if mask else passthrough[0] + return Scalar.load( + DTypePointer[type](base[0]) + ) if mask else passthrough[0] return llvm_intrinsic[ "llvm.masked.gather", __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], @@ -935,7 +937,7 @@ fn scatter[ if size == 1: if mask: var ptr = DTypePointer[type](base[0]) - ptr.store(value[0]) + Scalar.store(ptr, value[0]) return llvm_intrinsic["llvm.masked.scatter", NoneType]( value, @@ -1223,7 +1225,7 @@ fn masked_load[ @parameter if size == 1: - return addr.load() if mask else passthrough[0] + return Scalar.load(addr) if mask else passthrough[0] return llvm_intrinsic["llvm.masked.load", SIMD[addr.type, size]]( addr.bitcast[DType.invalid.value]().address, @@ -1264,7 +1266,7 @@ fn masked_store[ @parameter if size == 1: if mask: - addr.store(value[0]) + Scalar.store(addr, value[0]) return llvm_intrinsic["llvm.masked.store", NoneType]( @@ -1305,7 +1307,7 @@ fn compressed_store[ @parameter if size == 1: if mask: - addr.store(value[0]) + Scalar.store(addr, value[0]) return llvm_intrinsic["llvm.masked.compressstore", NoneType]( @@ -1350,7 +1352,7 @@ fn strided_load[ @parameter if simd_width == 1: - return addr.load() if mask else Scalar[type]() + return Scalar.load(addr) if mask else Scalar[type]() alias IndexTy = SIMD[DType.index, simd_width] var iota = llvm_intrinsic[ @@ -1391,7 +1393,7 @@ fn strided_load[ @parameter if simd_width == 1: - return addr.load() + return Scalar.load(addr) return strided_load[type, simd_width](addr, stride, True) @@ -1431,7 +1433,7 @@ fn strided_store[ @parameter if simd_width == 1: if mask: - addr.store(value[0]) + Scalar.store(addr, value[0]) return alias IndexTy = SIMD[DType.index, simd_width] @@ -1471,7 +1473,7 @@ fn strided_store[ @parameter if simd_width == 1: - addr.store(value[0]) + Scalar.store(addr, value[0]) return strided_store[type, simd_width](value, addr, stride, True) diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index ac632a6e29..b1528e3063 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -47,10 +47,10 @@ fn _serialize_elements_complete[ ](ptr: DTypePointer, len: Int): if len == 0: return - serialize_fn(ptr.load()) + serialize_fn(Scalar.load(ptr)) for i in range(1, len): serialize_fn(", ") - serialize_fn(ptr.load(i)) + serialize_fn(Scalar.load(ptr, i)) fn _serialize_elements[ diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index f2f13b9576..3191d2af98 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -155,7 +155,7 @@ struct StringRef( """ var len = 0 - while ptr.load(len): + while Scalar.load(ptr, len): len += 1 return StringRef(ptr, len) @@ -172,7 +172,7 @@ struct StringRef( """ var len = 0 - while ptr.load(len): + while Scalar.load(ptr, len): len += 1 return StringRef(ptr.bitcast[DType.int8](), len) @@ -643,7 +643,9 @@ fn _memchr[ var vectorized_end = _align_down(len, bool_mask_width) for i in range(0, vectorized_end, bool_mask_width): - var bool_mask = source.load[width=bool_mask_width](i) == first_needle + var bool_mask = SIMD[size=bool_mask_width].load( + source, i + ) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) if mask: return source + i + countr_zero(mask) @@ -679,9 +681,9 @@ fn _memmem[ var last_needle = SIMD[type, bool_mask_width](needle[needle_len - 1]) for i in range(0, vectorized_end, bool_mask_width): - var first_block = haystack.load[width=bool_mask_width](i) - var last_block = haystack.load[width=bool_mask_width]( - i + needle_len - 1 + var first_block = SIMD[size=bool_mask_width].load(haystack, i) + var last_block = SIMD[size=bool_mask_width].load( + haystack, i + needle_len - 1 ) var eq_first = first_needle == first_block diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index d8249bf31b..b681e47ce7 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -201,13 +201,14 @@ def test_file_read_to_dtype_pointer(): var ptr = DTypePointer[DType.int8].alloc(8) var data = f.read(ptr, 8) assert_equal( - str(ptr.load[width=8](0)), "[76, 111, 114, 101, 109, 32, 105, 112]" + str(SIMD[size=8].load(ptr, 0)), "[76, 111, 114, 101, 109, 32, 105, 112]" ) var ptr2 = DTypePointer[DType.int8].alloc(8) var data2 = f.read(ptr2, 8) assert_equal( - str(ptr2.load[width=8](0)), "[115, 117, 109, 32, 100, 111, 108, 111]" + str(SIMD[size=8].load(ptr2, 0)), + "[115, 117, 109, 32, 100, 111, 108, 111]", ) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 4bd191288a..1c5ff42780 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -124,7 +124,7 @@ def test_issue_20421(): var a = DTypePointer[DType.uint8]().alloc(16 * 64, alignment=64) for i in range(16 * 64): a[i] = i & 255 - var av16 = a.offset(128 + 64 + 4).bitcast[DType.int32]().load[width=4]() + var av16 = SIMD[size=4].load(a.offset(128 + 64 + 4).bitcast[DType.int32]()) assert_equal( av16, SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448), diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 7d4e44c7de..1193fc8904 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -133,8 +133,8 @@ def test_memcmp(): def test_memcmp_overflow(): var p1 = DTypePointer[DType.int8].alloc(1) var p2 = DTypePointer[DType.int8].alloc(1) - p1.store(-120) - p2.store(120) + Scalar.store(p1, -120) + Scalar.store(p2, 120) var c = memcmp(p1, p2, 1) assert_equal(c, -1, "-120 is smaller than 120") @@ -150,10 +150,10 @@ def test_memcmp_simd(): var p2 = DTypePointer[DType.int8].alloc(length) memset_zero(p1, length) memset_zero(p2, length) - p1.store(120) - p1.store(1, 100) - p2.store(120) - p2.store(1, 90) + Scalar.store(p1, 120) + Scalar.store(p1, 1, 100) + Scalar.store(p2, 120) + Scalar.store(p2, 1, 90) var c = memcmp(p1, p2, length) assert_equal(c, 1, "[120, 100, 0, ...] is bigger than [120, 90, 0, ...]") @@ -164,10 +164,10 @@ def test_memcmp_simd(): memset_zero(p1, length) memset_zero(p2, length) - p1.store(length - 2, 120) - p1.store(length - 1, 100) - p2.store(length - 2, 120) - p2.store(length - 1, 90) + Scalar.store(p1, length - 2, 120) + Scalar.store(p1, length - 1, 100) + Scalar.store(p2, length - 2, 120) + Scalar.store(p2, length - 1, 90) c = memcmp(p1, p2, length) assert_equal(c, 1, "[..., 0, 120, 100] is bigger than [..., 0, 120, 90]") @@ -296,13 +296,13 @@ def test_memset(): var buf0 = DTypePointer[DType.int32].alloc(2) memset(buf0, 1, 2) - assert_equal(buf0.load(0), 16843009) + assert_equal(Scalar.load(buf0, 0), 16843009) memset(buf0, -1, 2) - assert_equal(buf0.load(0), -1) + assert_equal(Scalar.load(buf0, 0), -1) var buf1 = DTypePointer[DType.int8].alloc(2) memset(buf1, 5, 2) - assert_equal(buf1.load(0), 5) + assert_equal(Scalar.load(buf1, 0), 5) def test_pointer_string(): @@ -354,7 +354,7 @@ def test_pointer_refitem_pair(): def test_dtypepointer_gather(): var ptr = DTypePointer[DType.float32].alloc(4) - ptr.store(0, SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0)) + SIMD.store(ptr, 0, SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0)) @parameter def _test_gather[ @@ -399,7 +399,7 @@ def test_dtypepointer_gather(): def test_dtypepointer_scatter(): var ptr = DTypePointer[DType.float32].alloc(4) - ptr.store(0, SIMD[ptr.type, 4](0.0)) + SIMD.store(ptr, 0, SIMD[ptr.type, 4](0.0)) @parameter def _test_scatter[ @@ -410,7 +410,7 @@ def test_dtypepointer_scatter(): desired: SIMD[ptr.type, 4], ): ptr.scatter(offset, val) - var actual = ptr.load[width=4](0) + var actual = SIMD[size=4].load(ptr, 0) assert_almost_equal( actual, desired, msg="_test_scatter", atol=0.0, rtol=0.0 ) @@ -425,7 +425,7 @@ def test_dtypepointer_scatter(): desired: SIMD[ptr.type, 4], ): ptr.scatter(offset, val, mask) - var actual = ptr.load[width=4](0) + var actual = SIMD[size=4].load(ptr, 0) assert_almost_equal( actual, desired, msg="_test_masked_scatter", atol=0.0, rtol=0.0 ) @@ -442,7 +442,7 @@ def test_dtypepointer_scatter(): SIMD[ptr.type, 4](3.0, 2.0, 1.0, 0.0), ) - ptr.store(0, SIMD[ptr.type, 4](0.0)) + SIMD.store(ptr, 0, SIMD[ptr.type, 4](0.0)) _test_masked_scatter[1]( Int16(2), 2.0, False, SIMD[ptr.type, 4](0.0, 0.0, 0.0, 0.0) From 43f1245fe5b2ef0de4362e3fa670a1ba1e3530d2 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 3 Jun 2024 12:50:44 -0400 Subject: [PATCH 0850/2019] [mojo-lang] Error on unsupported `async` function forms This PR ensures the parser correctly errors when hitting a case where async functions are not yet supported. We're still working out the details of these cases: ref results, constructors, and indirect calls. MODULAR_ORIG_COMMIT_REV_ID: 6a6a8ad560bc0ba8d86cd9ff621f98f351497ff1 --- docs/changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index baa53630e5..9a8a5b9c5b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -21,6 +21,16 @@ what we publish. been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means the result types of `async` functions do not need to be `Movable`. + ```mojo + async fn raise_or_string(c: Bool) raises -> String: + if c: + raise "whoops!" + return "hello world!" + ``` + + Note that `async` functions do not yet support indirect calls, `ref` results, + and constructors. + - The `Reference` type (and many iterators) now use "inferred" parameters to represent the mutability of their lifetime, simplifying the interface. From 53153e9d4ff67098e589cd3b0ee6de8a90b663bf Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:21:52 -0400 Subject: [PATCH 0851/2019] [stdlib] Use consistent method order and comment headers for stdlib. Round 2 of [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 1a5072d3d932b517c2a5264a64a51b21c829c840 --- stdlib/src/builtin/int.mojo | 327 ++-- stdlib/src/builtin/int_literal.mojo | 275 ++-- stdlib/src/builtin/simd.mojo | 2013 +++++++++++------------ stdlib/src/builtin/string_literal.mojo | 195 ++- stdlib/src/collections/inline_list.mojo | 61 +- stdlib/src/memory/unsafe.mojo | 221 +-- 6 files changed, 1579 insertions(+), 1513 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 479fec5ab3..6a831f0eef 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -259,6 +259,7 @@ struct Int( ): """This type represents an integer value.""" + # Fields var value: __mlir_type.index """The underlying storage for the integer value.""" @@ -367,59 +368,9 @@ struct Int( """ self = value.__index__() - @always_inline("nodebug") - fn __int__(self) -> Int: - """Gets the integral value (this is an identity function for Int). - - Returns: - The value as an integer. - """ - return self - - fn __str__(self) -> String: - """Get the integer as a string. - - Returns: - A string representation. - """ - - return String.format_sequence(self) - - fn format_to(self, inout writer: Formatter): - """ - Formats this integer to the provided formatter. - - Args: - writer: The formatter to write to. - """ - - @parameter - if triple_is_nvidia_cuda(): - var err = _try_write_int(writer, Int64(self)) - if err: - abort( - "unreachable: unexpected write int failure condition: " - + str(err.value()) - ) - else: - _format_scalar(writer, Int64(self)) - - fn __repr__(self) -> String: - """Get the integer as a string. Returns the same `String` as `__str__`. - - Returns: - A string representation. - """ - return str(self) - - @always_inline("nodebug") - fn __mlir_index__(self) -> __mlir_type.index: - """Convert to index. - - Returns: - The corresponding __mlir_type.index value. - """ - return self.value + # ===------------------------------------------------------------------=== # + # Operator dunders + # ===------------------------------------------------------------------=== # @always_inline("nodebug") fn __lt__(self, rhs: Int) -> Bool: @@ -507,27 +458,6 @@ struct Int( pred = __mlir_attr.`#index` ](self.value, rhs.value) - @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Convert this Int to Bool. - - Returns: - False Bool value if the value is equal to 0 and True otherwise. - """ - return self != 0 - - @always_inline("nodebug") - fn __index__(self) -> Int: - """Return self converted to an integer, if self is suitable for use as - an index into a list. - - For Int type this is simply the value. - - Returns: - The corresponding Int value. - """ - return self - @always_inline("nodebug") fn __pos__(self) -> Int: """Return +self. @@ -549,63 +479,6 @@ struct Int( __mlir_op.`index.constant`[value = __mlir_attr.`-1:index`](), ) - @always_inline("nodebug") - fn __abs__(self) -> Self: - """Return the absolute value of the Int value. - - Returns: - The absolute value. - """ - return -self if self < 0 else self - - @always_inline("nodebug") - fn __ceil__(self) -> Self: - """Return the ceiling of the Int value, which is itself. - - Returns: - The Int value itself. - """ - return self - - @always_inline("nodebug") - fn __floor__(self) -> Self: - """Return the floor of the Int value, which is itself. - - Returns: - The Int value itself. - """ - return self - - @always_inline("nodebug") - fn __round__(self) -> Self: - """Return the rounded value of the Int value, which is itself. - - Returns: - The Int value itself. - """ - return self - - @always_inline("nodebug") - fn __round__(self, ndigits: Int) -> Self: - """Return the rounded value of the Int value, which is itself. - Args: - ndigits: The number of digits to round to. - Returns: - The Int value itself if ndigits >= 0 else the rounded value. - """ - if ndigits >= 0: - return self - return self - (self % 10 ** -(ndigits)) - - @always_inline("nodebug") - fn __trunc__(self) -> Self: - """Return the truncated Int value, which is itself. - - Returns: - The Int value itself. - """ - return self - @always_inline("nodebug") fn __invert__(self) -> Int: """Return ~self. @@ -662,32 +535,6 @@ struct Int( """ return Float64(self) / Float64(rhs) - @always_inline("nodebug") - fn _positive_div(self, rhs: Int) -> Int: - """Return the division of `self` and `rhs` assuming that the arguments - are both positive. - - Args: - rhs: The value to divide on. - - Returns: - The integer division of `self` and `rhs` . - """ - return __mlir_op.`index.divs`(self.value, rhs.value) - - @always_inline("nodebug") - fn _positive_rem(self, rhs: Int) -> Int: - """Return the modulus of `self` and `rhs` assuming that the arguments - are both positive. - - Args: - rhs: The value to divide on. - - Returns: - The integer modulus of `self` and `rhs` . - """ - return __mlir_op.`index.rems`(self.value, rhs.value) - @always_inline("nodebug") fn __floordiv__(self, rhs: Int) -> Int: """Return the division of `self` and `rhs` rounded down to the nearest @@ -1092,6 +939,114 @@ struct Int( """ return value ^ self + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __bool__(self) -> Bool: + """Convert this Int to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self != 0 + + @always_inline("nodebug") + fn __index__(self) -> Int: + """Return self converted to an integer, if self is suitable for use as + an index into a list. + + For Int type this is simply the value. + + Returns: + The corresponding Int value. + """ + return self + + @always_inline("nodebug") + fn __int__(self) -> Int: + """Gets the integral value (this is an identity function for Int). + + Returns: + The value as an integer. + """ + return self + + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Return the absolute value of the Int value. + + Returns: + The absolute value. + """ + return -self if self < 0 else self + + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Return the ceiling of the Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Return the floor of the Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + + @always_inline("nodebug") + fn __round__(self) -> Self: + """Return the rounded value of the Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + + @always_inline("nodebug") + fn __round__(self, ndigits: Int) -> Self: + """Return the rounded value of the Int value, which is itself. + Args: + ndigits: The number of digits to round to. + Returns: + The Int value itself if ndigits >= 0 else the rounded value. + """ + if ndigits >= 0: + return self + return self - (self % 10 ** -(ndigits)) + + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Return the truncated Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + + fn __str__(self) -> String: + """Get the integer as a string. + + Returns: + A string representation. + """ + + return String.format_sequence(self) + + fn __repr__(self) -> String: + """Get the integer as a string. Returns the same `String` as `__str__`. + + Returns: + A string representation. + """ + return str(self) + fn __hash__(self) -> Int: """Hash the int using builtin hash. @@ -1102,3 +1057,61 @@ struct Int( """ # TODO(MOCO-636): switch to DType.index return _hash_simd(Scalar[DType.int64](self)) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn format_to(self, inout writer: Formatter): + """ + Formats this integer to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + @parameter + if triple_is_nvidia_cuda(): + var err = _try_write_int(writer, Int64(self)) + if err: + abort( + "unreachable: unexpected write int failure condition: " + + str(err.value()) + ) + else: + _format_scalar(writer, Int64(self)) + + @always_inline("nodebug") + fn __mlir_index__(self) -> __mlir_type.index: + """Convert to index. + + Returns: + The corresponding __mlir_type.index value. + """ + return self.value + + @always_inline("nodebug") + fn _positive_div(self, rhs: Int) -> Int: + """Return the division of `self` and `rhs` assuming that the arguments + are both positive. + + Args: + rhs: The value to divide on. + + Returns: + The integer division of `self` and `rhs` . + """ + return __mlir_op.`index.divs`(self.value, rhs.value) + + @always_inline("nodebug") + fn _positive_rem(self, rhs: Int) -> Int: + """Return the modulus of `self` and `rhs` assuming that the arguments + are both positive. + + Args: + rhs: The value to divide on. + + Returns: + The integer modulus of `self` and `rhs` . + """ + return __mlir_op.`index.rems`(self.value, rhs.value) diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 8f9f72f1b7..d6f11097fd 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -38,6 +38,7 @@ struct IntLiteral( precision integer types. """ + # Fields alias _mlir_type = __mlir_type.`!kgen.int_literal` var value: Self._mlir_type @@ -47,6 +48,10 @@ struct IntLiteral( __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` ) + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self): """Default constructor.""" @@ -61,43 +66,9 @@ struct IntLiteral( """ self.value = value - @always_inline("nodebug") - fn __int__(self) -> Int: - """Convert from IntLiteral to Int. - - Returns: - The value as an integer. - """ - return Int(self.__as_mlir_index()) - - @always_inline("nodebug") - fn _bit_width(self) -> IntLiteral: - """Get the (signed) bit width of the IntLiteral. - - Returns: - The bit width. - """ - return __mlir_op.`kgen.int_literal.bit_width`(self.value) - - @always_inline - fn __str__(self) -> String: - """Convert from IntLiteral to String. - - Returns: - The value as a string. - """ - return str(Int(self)) - - @always_inline("nodebug") - fn __as_mlir_index(self) -> __mlir_type.index: - """Convert from IntLiteral to index. - - Returns: - The corresponding __mlir_type.index value. - """ - return __mlir_op.`kgen.int_literal.convert`[_type = __mlir_type.index]( - self.value - ) + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __lt__(self, rhs: Self) -> Bool: @@ -185,25 +156,6 @@ struct IntLiteral( pred = __mlir_attr.`#kgen` ](self.value, rhs.value) - @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Convert this IntLiteral to Bool. - - Returns: - False Bool value if the value is equal to 0 and True otherwise. - """ - return self != Self() - - @always_inline("nodebug") - fn __index__(self) -> Int: - """Return self converted to an integer, if self is suitable for use as - an index into a list. - - Returns: - The corresponding Int value. - """ - return self.__int__() - @always_inline("nodebug") fn __pos__(self) -> Self: """Return +self. @@ -222,53 +174,6 @@ struct IntLiteral( """ return Self() - self - @always_inline("nodebug") - fn __abs__(self) -> Self: - """Return the absolute value of the IntLiteral value. - - Returns: - The absolute value. - """ - if self >= 0: - return self - return -self - - @always_inline("nodebug") - fn __ceil__(self) -> Self: - """Return the ceiling of the IntLiteral value, which is itself. - - Returns: - The IntLiteral value itself. - """ - return self - - @always_inline("nodebug") - fn __floor__(self) -> Self: - """Return the floor of the IntLiteral value, which is itself. - - Returns: - The IntLiteral value itself. - """ - return self - - @always_inline("nodebug") - fn __round__(self) -> Self: - """Return the rounded value of the IntLiteral value, which is itself. - - Returns: - The IntLiteral value itself. - """ - return self - - @always_inline("nodebug") - fn __trunc__(self) -> Self: - """Return the truncated of the IntLiteral value, which is itself. - - Returns: - The IntLiteral value itself. - """ - return self - @always_inline("nodebug") fn __divmod__(self, rhs: Self) -> Tuple[Self, Self]: """Return the quotient and remainder of the division of self by rhs. @@ -283,32 +188,6 @@ struct IntLiteral( var remainder: Self = self - (quotient * rhs) return quotient, remainder - @always_inline("nodebug") - fn __round__(self, ndigits: Int) -> Self: - """Return the rounded value of the IntLiteral value, which is itself. - - Args: - ndigits: The number of digits to round to. - - Returns: - The IntLiteral value itself if ndigits >= 0 else the rounded value. - """ - if ndigits >= 0: - return self - alias one = __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` - alias ten = __mlir_attr.`#kgen.int_literal<10> : !kgen.int_literal` - var multiplier = one - # TODO: Use IntLiteral.__pow__() when it's implemented. - for _ in range(-ndigits): - multiplier = __mlir_op.`kgen.int_literal.binop`[ - oper = __mlir_attr.`#kgen` - ](multiplier, ten) - alias Pair = Tuple[Self, Self] - var mod: IntLiteral = self % Self(multiplier) - if mod * 2 >= multiplier: - mod -= multiplier - return self - mod - @always_inline("nodebug") fn __invert__(self) -> Self: """Return ~self. @@ -688,3 +567,141 @@ struct IntLiteral( `value ^ self`. """ return value ^ self + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __bool__(self) -> Bool: + """Convert this IntLiteral to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self != Self() + + @always_inline("nodebug") + fn __index__(self) -> Int: + """Return self converted to an integer, if self is suitable for use as + an index into a list. + + Returns: + The corresponding Int value. + """ + return self.__int__() + + @always_inline("nodebug") + fn __int__(self) -> Int: + """Convert from IntLiteral to Int. + + Returns: + The value as an integer. + """ + return Int(self.__as_mlir_index()) + + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Return the absolute value of the IntLiteral value. + + Returns: + The absolute value. + """ + if self >= 0: + return self + return -self + + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Return the ceiling of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Return the floor of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + + @always_inline("nodebug") + fn __round__(self) -> Self: + """Return the rounded value of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Return the truncated of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + + @always_inline("nodebug") + fn __round__(self, ndigits: Int) -> Self: + """Return the rounded value of the IntLiteral value, which is itself. + + Args: + ndigits: The number of digits to round to. + + Returns: + The IntLiteral value itself if ndigits >= 0 else the rounded value. + """ + if ndigits >= 0: + return self + alias one = __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` + alias ten = __mlir_attr.`#kgen.int_literal<10> : !kgen.int_literal` + var multiplier = one + # TODO: Use IntLiteral.__pow__() when it's implemented. + for _ in range(-ndigits): + multiplier = __mlir_op.`kgen.int_literal.binop`[ + oper = __mlir_attr.`#kgen` + ](multiplier, ten) + alias Pair = Tuple[Self, Self] + var mod: IntLiteral = self % Self(multiplier) + if mod * 2 >= multiplier: + mod -= multiplier + return self - mod + + @always_inline + fn __str__(self) -> String: + """Convert from IntLiteral to String. + + Returns: + The value as a string. + """ + return str(Int(self)) + + # ===----------------------------------------------------------------------===# + # Methods + # ===----------------------------------------------------------------------===# + + @always_inline("nodebug") + fn _bit_width(self) -> IntLiteral: + """Get the (signed) bit width of the IntLiteral. + + Returns: + The bit width. + """ + return __mlir_op.`kgen.int_literal.bit_width`(self.value) + + @always_inline("nodebug") + fn __as_mlir_index(self) -> __mlir_type.index: + """Convert from IntLiteral to index. + + Returns: + The corresponding __mlir_type.index value. + """ + return __mlir_op.`kgen.int_literal.convert`[_type = __mlir_type.index]( + self.value + ) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 03a7d21b90..2b396c4708 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -162,6 +162,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size: The size of the SIMD vector. """ + # Fields alias _Mask = SIMD[DType.bool, size] alias element_type = type @@ -180,6 +181,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( alias MIN_FINITE = Self(_min_finite[type]()) """Returns the minimum (lowest) finite value of SIMD value.""" + alias _default_alignment = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1 + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self): """Default initializer of the SIMD vector. @@ -438,486 +447,152 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) ) - alias _default_alignment = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1 - - @staticmethod - @always_inline - fn prefetch[ - params: PrefetchOptions, - *, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space]): - # Prefetch at the underlying address. - """Prefetches memory at the underlying address. - - Parameters: - params: Prefetch options (see `PrefetchOptions` for details). - address_space: The address space the pointer is in. - - Args: - ptr: The pointer to prefetch from. - """ - prefetch[params](ptr) + # ===-------------------------------------------------------------------===# + # Factory methods + # ===-------------------------------------------------------------------===# @staticmethod @always_inline("nodebug") - fn load[ - *, - alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space]) -> Self: - """Loads the value the Pointer object points to. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. + fn splat(x: Scalar[type]) -> Self: + """Splats (broadcasts) the element onto the vector. Args: - ptr: The pointer to load from. + x: The input scalar value. Returns: - The loaded value. + A new SIMD vector whose elements are the same as the input value. """ - return Self.load[alignment=alignment, address_space=address_space]( - ptr, 0 - ) - - @staticmethod - @always_inline("nodebug") - fn load[ - T: Intable, - *, - alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: T) -> Self: - """Loads the value the Pointer object points to with the given offset. + _simd_construction_checks[type, size]() + return Self { + value: __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `, `, type.value, `>` + ] + ](x.value) + } - Constraints: - The width and alignment must be positive integer values. + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# - Parameters: - T: The Intable type of the offset. - alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. + @always_inline("nodebug") + fn __getitem__(self, idx: Int) -> Scalar[type]: + """Gets an element from the vector. Args: - ptr: The pointer to load from. - offset: The offset to load from. + idx: The element index. Returns: - The loaded value. + The value at position `idx`. """ + return __mlir_op.`pop.simd.extractelement`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ](self.value, index(idx).value) - @parameter - if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: - # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types - # regardless of the alignment that is passed. This causes issues if - # this method is called on an unaligned pointer. - # TODO #37823 We can make this smarter when we add an `aligned` - # trait to the pointer class. - var v = SIMD[type, size]() - - # intentionally don't unroll, otherwise the compiler vectorizes - for i in range(size): - v[i] = ptr.address.offset(int(offset) + i).load[ - alignment=alignment - ]() - return v + @always_inline("nodebug") + fn __setitem__(inout self, idx: Int, val: Scalar[type]): + """Sets an element in the vector. - return ( - ptr.address.offset(offset) - .bitcast[SIMD[type, size]]() - .load[alignment=alignment]() + Args: + idx: The index to set. + val: The value to set. + """ + self.value = __mlir_op.`pop.simd.insertelement`( + self.value, val.value, index(idx).value ) - @staticmethod @always_inline("nodebug") - fn store[ - T: Intable, - /, - *, - alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: T, val: Self): - """Stores a single element value at the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - T: The Intable type of the offset. - alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. + fn __setitem__( + inout self, idx: Int, val: __mlir_type[`!pop.scalar<`, type.value, `>`] + ): + """Sets an element in the vector. Args: - ptr: The pointer to store to. - offset: The offset to store to. - val: The value to store. + idx: The index to set. + val: The value to set. """ - Self.store[alignment=alignment, address_space=address_space]( - ptr.offset(offset), val + self.value = __mlir_op.`pop.simd.insertelement`( + self.value, val, index(idx).value ) - @staticmethod @always_inline("nodebug") - fn store[ - *, - alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], val: Self): - """Stores a single element value. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. + fn __add__(self, rhs: Self) -> Self: + """Computes `self + rhs`. Args: - ptr: The pointer to store to. - val: The value to store. + rhs: The rhs value. + + Returns: + A new vector whose element at position `i` is computed as + `self[i] + rhs[i]`. """ - constrained[size > 0, "width must be a positive integer value"]() - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - ptr.address.bitcast[SIMD[type, size]]().store[alignment=alignment](val) + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.add`(self.value, rhs.value) @always_inline("nodebug") - fn __len__(self) -> Int: - """Gets the length of the SIMD vector. + fn __sub__(self, rhs: Self) -> Self: + """Computes `self - rhs`. + + Args: + rhs: The rhs value. Returns: - The length of the SIMD vector. + A new vector whose element at position `i` is computed as + `self[i] - rhs[i]`. """ - - return self.size + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.sub`(self.value, rhs.value) @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Converts the SIMD scalar into a boolean value. + fn __mul__(self, rhs: Self) -> Self: + """Computes `self * rhs`. - Constraints: - The size of the SIMD vector must be 1. + Args: + rhs: The rhs value. Returns: - True if the SIMD scalar is non-zero and False otherwise. + A new vector whose element at position `i` is computed as + `self[i] * rhs[i]`. """ - constrained[ - size == 1, - ( - "The truth value of a SIMD vector with more than one element is" - " ambiguous. Use the builtin `any()` or `all()` functions" - " instead." - ), - ]() - return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value - @staticmethod + @parameter + if type == DType.bool: + return (rebind[Self._Mask](self) & rebind[Self._Mask](rhs)).cast[ + type + ]() + + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.mul`(self.value, rhs.value) + @always_inline("nodebug") - fn splat(x: Scalar[type]) -> Self: - """Splats (broadcasts) the element onto the vector. + fn __truediv__(self, rhs: Self) -> Self: + """Computes `self / rhs`. Args: - x: The input scalar value. + rhs: The rhs value. Returns: - A new SIMD vector whose elements are the same as the input value. + A new vector whose element at position `i` is computed as + `self[i] / rhs[i]`. """ - _simd_construction_checks[type, size]() - return Self { - value: __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[ - `!pop.simd<`, size.value, `, `, type.value, `>` - ] - ](x.value) - } + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.div`(self.value, rhs.value) @always_inline("nodebug") - fn cast[target: DType](self) -> SIMD[target, size]: - """Casts the elements of the SIMD vector to the target element type. + fn __floordiv__(self, rhs: Self) -> Self: + """Returns the division of self and rhs rounded down to the nearest + integer. - Parameters: - target: The target DType. + Constraints: + The element type of the SIMD vector must be numeric. + + Args: + rhs: The value to divide with. Returns: - A new SIMD vector whose elements have been casted to the target - element type. + `floor(self / rhs)` value. """ - - @parameter - if type == target: - return rebind[SIMD[target, size]](self) - - @parameter - if has_neon() and (type == DType.bfloat16 or target == DType.bfloat16): - # BF16 support on neon systems is not supported. - return _unchecked_zero[target, size]() - - @parameter - if type == DType.bool: - return self.select(SIMD[target, size](1), SIMD[target, size](0)) - elif target == DType.bool: - return rebind[SIMD[target, size]](self != 0) - elif type == DType.bfloat16: - var cast_result = _bfloat16_to_f32( - rebind[SIMD[DType.bfloat16, size]](self) - ).cast[target]() - return rebind[SIMD[target, size]](cast_result) - elif target == DType.bfloat16: - return rebind[SIMD[target, size]]( - _f32_to_bfloat16(self.cast[DType.float32]()) - ) - elif target == DType.address: - var index_val = __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.simd<`, size.value, `, index>`] - ](self.value) - var tmp = SIMD[DType.address, size]( - __mlir_op.`pop.index_to_pointer`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, address >`, - ] - ](index_val) - ) - return rebind[SIMD[target, size]](tmp) - elif (type == DType.address) and target.is_integral(): - var index_tmp = SIMD[DType.index, size]( - __mlir_op.`pop.pointer_to_index`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - DType.index.value, - `>`, - ] - ]( - rebind[ - __mlir_type[ - `!pop.simd<`, - size.value, - `, address >`, - ] - ](self.value) - ) - ) - return index_tmp.cast[target]() - else: - return __mlir_op.`pop.cast`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - target.value, - `>`, - ] - ](self.value) - - @always_inline("nodebug") - fn __int__(self) -> Int: - """Casts to the value to an Int. If there is a fractional component, - then the fractional part is truncated. - - Constraints: - The size of the SIMD vector must be 1. - - Returns: - The value as an integer. - """ - constrained[size == 1, "expected a scalar type"]() - return __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( - rebind[Scalar[type]](self).value - ) - - @always_inline - fn __str__(self) -> String: - """Get the SIMD as a string. - - Returns: - A string representation. - """ - - return String.format_sequence(self) - - @always_inline - fn __repr__(self) -> String: - """Get the representation of the SIMD value e.g. "SIMD[DType.int8, 2](1, 2)". - - Returns: - The representation of the SIMD value. - """ - - var output = String() - var writer = output._unsafe_to_formatter() - self.format_to[use_scientific_notation=True](writer) - - var values = output.as_string_slice() - - @parameter - if size > 1: - # TODO: Fix when slice indexing is implemented on StringSlice - values = StringSlice(unsafe_from_utf8=output.as_bytes_slice()[1:-1]) - return ( - "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" - ) - - @always_inline - fn format_to(self, inout writer: Formatter): - """ - Formats this SIMD value to the provided formatter. - - Args: - writer: The formatter to write to. - """ - self.format_to[use_scientific_notation=False](writer) - - # This overload is required to keep SIMD compliant with the Formattable - # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will - # fail to compile. - fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): - """ - Formats this SIMD value to the provided formatter. - - Parameters: - use_scientific_notation: Whether floats should use scientific notation. - This parameter does not apply to integer types. - - Args: - writer: The formatter to write to. - """ - - # Print an opening `[`. - @parameter - if size > 1: - writer.write_str["["]() - - # Print each element. - for i in range(size): - var element = self[i] - # Print separators between each element. - if i != 0: - writer.write_str[", "]() - - @parameter - if triple_is_nvidia_cuda(): - - @parameter - if type.is_floating_point(): - # get_dtype_printf_format hardcodes 17 digits of precision. - _printf["%g"](element) - else: - # FIXME(MSTDL-406): - # This prints "out of band" with the `Formatter` passed - # in, meaning this will only work if `Formatter` is an - # unbuffered wrapper around printf (which Formatter.stdout - # currently is by default). - # - # This is a workaround to permit debug formatting of - # floating-point values on GPU, where printing to stdout - # is the only way the Formatter framework is currently - # used. - _printf[_get_dtype_printf_format[type]()](element) - else: - - @parameter - if use_scientific_notation and type.is_floating_point(): - alias float_format = "%." + _scientific_notation_digits[ - type - ]() + "e" - _format_scalar[type, float_format](writer, element) - else: - _format_scalar(writer, element) - - # Print a closing `]`. - @parameter - if size > 1: - writer.write_str["]"]() - - @always_inline("nodebug") - fn __add__(self, rhs: Self) -> Self: - """Computes `self + rhs`. - - Args: - rhs: The rhs value. - - Returns: - A new vector whose element at position `i` is computed as - `self[i] + rhs[i]`. - """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.add`(self.value, rhs.value) - - @always_inline("nodebug") - fn __sub__(self, rhs: Self) -> Self: - """Computes `self - rhs`. - - Args: - rhs: The rhs value. - - Returns: - A new vector whose element at position `i` is computed as - `self[i] - rhs[i]`. - """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.sub`(self.value, rhs.value) - - @always_inline("nodebug") - fn __mul__(self, rhs: Self) -> Self: - """Computes `self * rhs`. - - Args: - rhs: The rhs value. - - Returns: - A new vector whose element at position `i` is computed as - `self[i] * rhs[i]`. - """ - - @parameter - if type == DType.bool: - return (rebind[Self._Mask](self) & rebind[Self._Mask](rhs)).cast[ - type - ]() - - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.mul`(self.value, rhs.value) - - @always_inline("nodebug") - fn __truediv__(self, rhs: Self) -> Self: - """Computes `self / rhs`. - - Args: - rhs: The rhs value. - - Returns: - A new vector whose element at position `i` is computed as - `self[i] / rhs[i]`. - """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.div`(self.value, rhs.value) - - @always_inline("nodebug") - fn __floordiv__(self, rhs: Self) -> Self: - """Returns the division of self and rhs rounded down to the nearest - integer. - - Constraints: - The element type of the SIMD vector must be numeric. - - Args: - rhs: The value to divide with. - - Returns: - `floor(self / rhs)` value. - """ - constrained[type.is_numeric(), "the type must be numeric"]() + constrained[type.is_numeric(), "the type must be numeric"]() if not any(rhs): # this should raise an exception. @@ -938,23 +613,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var mask = ((rhs < 0) ^ (self < 0)) & (mod != 0) return div - mask.cast[type]() - @always_inline("nodebug") - fn __rfloordiv__(self, rhs: Self) -> Self: - """Returns the division of rhs and self rounded down to the nearest - integer. - - Constraints: - The element type of the SIMD vector must be numeric. - - Args: - rhs: The value to divide by self. - - Returns: - `floor(rhs / self)` value. - """ - constrained[type.is_numeric(), "the type must be numeric"]() - return rhs // self - @always_inline("nodebug") fn __mod__(self, rhs: Self) -> Self: """Returns the remainder of self divided by rhs. @@ -987,19 +645,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var mask = ((rhs < 0) ^ (self < 0)) & (mod != 0) return mod + mask.select(rhs, Self(0)) - @always_inline("nodebug") - fn __rmod__(self, value: Self) -> Self: - """Returns `value mod self`. - - Args: - value: The other value. - - Returns: - `value mod self`. - """ - constrained[type.is_numeric(), "the type must be numeric"]() - return value % self - @always_inline("nodebug") fn __pow__(self, exp: Int) -> Self: """Computes the vector raised to the power of the input integer value. @@ -1143,740 +788,999 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( self.value, rhs.value ) + @always_inline("nodebug") + fn __pos__(self) -> Self: + """Defines the unary `+` operation. + + Returns: + This SIMD vector. + """ + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return self + + @always_inline("nodebug") + fn __neg__(self) -> Self: + """Defines the unary `-` operation. + + Returns: + The negation of this SIMD vector. + """ + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.neg`(self.value) + + @always_inline("nodebug") + fn __and__(self, rhs: Self) -> Self: + """Returns `self & rhs`. + + Constraints: + The element type of the SIMD vector must be bool or integral. + + Args: + rhs: The RHS value. + + Returns: + `self & rhs`. + """ + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + return __mlir_op.`pop.and`(self.value, rhs.value) + + @always_inline("nodebug") + fn __xor__(self, rhs: Self) -> Self: + """Returns `self ^ rhs`. + + Constraints: + The element type of the SIMD vector must be bool or integral. + + Args: + rhs: The RHS value. + + Returns: + `self ^ rhs`. + """ + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + return __mlir_op.`pop.xor`(self.value, rhs.value) + + @always_inline("nodebug") + fn __or__(self, rhs: Self) -> Self: + """Returns `self | rhs`. + + Constraints: + The element type of the SIMD vector must be bool or integral. + + Args: + rhs: The RHS value. + + Returns: + `self | rhs`. + """ + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + return __mlir_op.`pop.or`(self.value, rhs.value) + + @always_inline("nodebug") + fn __lshift__(self, rhs: Self) -> Self: + """Returns `self << rhs`. + + Constraints: + The element type of the SIMD vector must be integral. + + Args: + rhs: The RHS value. + + Returns: + `self << rhs`. + """ + constrained[type.is_integral(), "must be an integral type"]() + debug_assert(all(rhs >= 0), "unhandled negative value") + return __mlir_op.`pop.shl`(self.value, rhs.value) + + @always_inline("nodebug") + fn __rshift__(self, rhs: Self) -> Self: + """Returns `self >> rhs`. + + Constraints: + The element type of the SIMD vector must be integral. + + Args: + rhs: The RHS value. + + Returns: + `self >> rhs`. + """ + constrained[type.is_integral(), "must be an integral type"]() + debug_assert(all(rhs >= 0), "unhandled negative value") + return __mlir_op.`pop.shr`(self.value, rhs.value) + + @always_inline("nodebug") + fn __invert__(self) -> Self: + """Returns `~self`. + + Constraints: + The element type of the SIMD vector must be boolean or integral. + + Returns: + The `~self` value. + """ + constrained[ + type.is_bool() or type.is_integral(), + "must be an bool or integral type", + ]() + + @parameter + if type.is_bool(): + return self.select(Self(False), Self(True)) + else: + return self ^ -1 + # ===------------------------------------------------------------------=== # - # Unary operations. + # In place operations. # ===------------------------------------------------------------------=== # @always_inline("nodebug") - fn __pos__(self) -> Self: - """Defines the unary `+` operation. + fn __iadd__(inout self, rhs: Self): + """Performs in-place addition. + + The vector is mutated where each element at position `i` is computed as + `self[i] + rhs[i]`. + + Args: + rhs: The rhs of the addition operation. + """ + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + self = self + rhs + + @always_inline("nodebug") + fn __isub__(inout self, rhs: Self): + """Performs in-place subtraction. + + The vector is mutated where each element at position `i` is computed as + `self[i] - rhs[i]`. + + Args: + rhs: The rhs of the operation. + """ + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + self = self - rhs + + @always_inline("nodebug") + fn __imul__(inout self, rhs: Self): + """Performs in-place multiplication. + + The vector is mutated where each element at position `i` is computed as + `self[i] * rhs[i]`. - Returns: - This SIMD vector. + Args: + rhs: The rhs of the operation. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return self + self = self * rhs @always_inline("nodebug") - fn __neg__(self) -> Self: - """Defines the unary `-` operation. + fn __itruediv__(inout self, rhs: Self): + """In-place true divide operator. - Returns: - The negation of this SIMD vector. + The vector is mutated where each element at position `i` is computed as + `self[i] / rhs[i]`. + + Args: + rhs: The rhs of the operation. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.neg`(self.value) + self = self / rhs - @always_inline - fn _bits_to_float[dest_type: DType](self) -> SIMD[dest_type, size]: - """Bitcasts the integer value to a floating-point value. + @always_inline("nodebug") + fn __ifloordiv__(inout self, rhs: Self): + """In-place flood div operator. - Parameters: - dest_type: DType to bitcast the input SIMD vector to. + The vector is mutated where each element at position `i` is computed as + `self[i] // rhs[i]`. - Returns: - A floating-point representation of the integer value. + Args: + rhs: The rhs of the operation. """ - alias integral_type = FPUtils[type].integral_type - return bitcast[dest_type, size](self.cast[integral_type]()) + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + self = self // rhs - @always_inline - fn _float_to_bits[dest_type: DType](self) -> SIMD[dest_type, size]: - """Bitcasts the floating-point value to an integer value. + @always_inline("nodebug") + fn __imod__(inout self, rhs: Self): + """In-place mod operator. - Parameters: - dest_type: DType to bitcast the input SIMD vector to. + The vector is mutated where each element at position `i` is computed as + `self[i] % rhs[i]`. - Returns: - An integer representation of the floating-point value. + Args: + rhs: The rhs of the operation. """ - alias integral_type = FPUtils[type].integral_type - var v = bitcast[integral_type, size](self) - return v.cast[dest_type]() + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + self = self.__mod__(rhs) - @always_inline - fn __abs__(self) -> Self: - """Defines the absolute value operation. + @always_inline("nodebug") + fn __ipow__(inout self, rhs: Int): + """In-place pow operator. - Returns: - The absolute value of this SIMD vector. + The vector is mutated where each element at position `i` is computed as + `pow(self[i], rhs)`. + + Args: + rhs: The rhs of the operation. """ + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + self = self.__pow__(rhs) - @parameter - if type.is_unsigned() or type.is_bool(): - return self - elif type.is_floating_point(): - alias integral_type = FPUtils[type].integral_type - var m = self._float_to_bits[integral_type]() - return (m & (FPUtils[type].sign_mask() - 1))._bits_to_float[type]() - else: - return (self < 0).select(-self, self) + @always_inline("nodebug") + fn __iand__(inout self, rhs: Self): + """Computes `self & rhs` and save the result in `self`. - fn _floor_ceil_trunc_impl[intrinsic: StringLiteral](self) -> Self: + Constraints: + The element type of the SIMD vector must be bool or integral. + + Args: + rhs: The RHS value. + """ constrained[ - intrinsic == "llvm.floor" - or intrinsic == "llvm.ceil" - or intrinsic == "llvm.trunc", - "unsupported intrinsic", + type.is_integral() or type.is_bool(), + "must be an integral or bool type", ]() + self = self & rhs - @parameter - if type.is_bool() or type.is_integral(): - return self + @always_inline("nodebug") + fn __ixor__(inout self, rhs: Self): + """Computes `self ^ rhs` and save the result in `self`. - @parameter - if has_neon() and type == DType.bfloat16: - return ( - self.cast[DType.float32]() - ._floor_ceil_trunc_impl[intrinsic]() - .cast[type]() - ) + Constraints: + The element type of the SIMD vector must be bool or integral. - return llvm_intrinsic[intrinsic, Self, has_side_effect=False](self) + Args: + rhs: The RHS value. + """ + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + self = self ^ rhs @always_inline("nodebug") - fn __floor__(self) -> Self: - """Performs elementwise floor on the elements of a SIMD vector. + fn __ior__(inout self, rhs: Self): + """Computes `self | rhs` and save the result in `self`. - Returns: - The elementwise floor of this SIMD vector. + Constraints: + The element type of the SIMD vector must be bool or integral. + + Args: + rhs: The RHS value. """ - return self._floor_ceil_trunc_impl["llvm.floor"]() + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + self = self | rhs @always_inline("nodebug") - fn __ceil__(self) -> Self: - """Performs elementwise ceiling on the elements of a SIMD vector. + fn __ilshift__(inout self, rhs: Self): + """Computes `self << rhs` and save the result in `self`. - Returns: - The elementwise ceiling of this SIMD vector. + Constraints: + The element type of the SIMD vector must be integral. + + Args: + rhs: The RHS value. """ - return self._floor_ceil_trunc_impl["llvm.ceil"]() + constrained[type.is_integral(), "must be an integral type"]() + self = self << rhs @always_inline("nodebug") - fn __trunc__(self) -> Self: - """Performs elementwise truncation on the elements of a SIMD vector. + fn __irshift__(inout self, rhs: Self): + """Computes `self >> rhs` and save the result in `self`. - Returns: - The elementwise truncated values of this SIMD vector. - """ + Constraints: + The element type of the SIMD vector must be integral. - return self._floor_ceil_trunc_impl["llvm.trunc"]() + Args: + rhs: The RHS value. + """ + constrained[type.is_integral(), "must be an integral type"]() + self = self >> rhs - fn clamp(self, lower_bound: Self, upper_bound: Self) -> Self: - """Clamps the values in a SIMD vector to be in a certain range. + # ===------------------------------------------------------------------=== # + # Reversed operations + # ===------------------------------------------------------------------=== # - Clamp cuts values in the input SIMD vector off at the upper bound and - lower bound values. For example, SIMD vector `[0, 1, 2, 3]` clamped to - a lower bound of 1 and an upper bound of 2 would return `[1, 1, 2, 2]`. + @always_inline("nodebug") + fn __radd__(self, value: Self) -> Self: + """Returns `value + self`. Args: - lower_bound: Minimum of the range to clamp to. - upper_bound: Maximum of the range to clamp to. + value: The other value. Returns: - A new SIMD vector containing x clamped to be within lower_bound and - upper_bound. + `value + self`. """ - - return self.min(upper_bound).max(lower_bound) + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return value + self @always_inline("nodebug") - fn roundeven(self) -> Self: - """Performs elementwise banker's rounding on the elements of a SIMD - vector. + fn __rsub__(self, value: Self) -> Self: + """Returns `value - self`. - This rounding goes to the nearest integer with ties toward the nearest - even integer. + Args: + value: The other value. Returns: - The elementwise banker's rounding of this SIMD vector. + `value - self`. """ - return llvm_intrinsic["llvm.roundeven", Self, has_side_effect=False]( - self - ) + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return value - self @always_inline("nodebug") - fn __round__(self) -> Self: - """Performs elementwise rounding on the elements of a SIMD vector. + fn __rmul__(self, value: Self) -> Self: + """Returns `value * self`. - This rounding goes to the nearest integer with ties away from zero. + Args: + value: The other value. Returns: - The elementwise rounded value of this SIMD vector. + `value * self`. """ - return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return value * self @always_inline("nodebug") - fn __round__(self, ndigits: Int) -> Self: - """Performs elementwise rounding on the elements of a SIMD vector. - This rounding goes to the nearest integer with ties away from zero. + fn __rfloordiv__(self, rhs: Self) -> Self: + """Returns the division of rhs and self rounded down to the nearest + integer. + + Constraints: + The element type of the SIMD vector must be numeric. + Args: - ndigits: The number of digits to round to. + rhs: The value to divide by self. + Returns: - The elementwise rounded value of this SIMD vector. + `floor(rhs / self)` value. """ - # TODO: see how can we implement this. - return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) - - # ===------------------------------------------------------------------=== # - # In place operations. - # ===------------------------------------------------------------------=== # + constrained[type.is_numeric(), "the type must be numeric"]() + return rhs // self @always_inline("nodebug") - fn __iadd__(inout self, rhs: Self): - """Performs in-place addition. - - The vector is mutated where each element at position `i` is computed as - `self[i] + rhs[i]`. + fn __rtruediv__(self, value: Self) -> Self: + """Returns `value / self`. Args: - rhs: The rhs of the addition operation. + value: The other value. + + Returns: + `value / self`. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self + rhs - - @always_inline("nodebug") - fn __isub__(inout self, rhs: Self): - """Performs in-place subtraction. + return value / self - The vector is mutated where each element at position `i` is computed as - `self[i] - rhs[i]`. + @always_inline("nodebug") + fn __rmod__(self, value: Self) -> Self: + """Returns `value mod self`. Args: - rhs: The rhs of the operation. + value: The other value. + + Returns: + `value mod self`. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self - rhs + constrained[type.is_numeric(), "the type must be numeric"]() + return value % self @always_inline("nodebug") - fn __imul__(inout self, rhs: Self): - """Performs in-place multiplication. + fn __rand__(self, value: Self) -> Self: + """Returns `value & self`. - The vector is mutated where each element at position `i` is computed as - `self[i] * rhs[i]`. + Constraints: + The element type of the SIMD vector must be bool or integral. Args: - rhs: The rhs of the operation. + value: The other value. + + Returns: + `value & self`. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self * rhs + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + return value & self @always_inline("nodebug") - fn __itruediv__(inout self, rhs: Self): - """In-place true divide operator. + fn __rxor__(self, value: Self) -> Self: + """Returns `value ^ self`. - The vector is mutated where each element at position `i` is computed as - `self[i] / rhs[i]`. + Constraints: + The element type of the SIMD vector must be bool or integral. Args: - rhs: The rhs of the operation. + value: The other value. + + Returns: + `value ^ self`. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self / rhs + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + return value ^ self @always_inline("nodebug") - fn __ifloordiv__(inout self, rhs: Self): - """In-place flood div operator. + fn __ror__(self, value: Self) -> Self: + """Returns `value | self`. - The vector is mutated where each element at position `i` is computed as - `self[i] // rhs[i]`. + Constraints: + The element type of the SIMD vector must be bool or integral. Args: - rhs: The rhs of the operation. + value: The other value. + + Returns: + `value | self`. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self // rhs + constrained[ + type.is_integral() or type.is_bool(), + "must be an integral or bool type", + ]() + return value | self @always_inline("nodebug") - fn __imod__(inout self, rhs: Self): - """In-place mod operator. + fn __rlshift__(self, value: Self) -> Self: + """Returns `value << self`. - The vector is mutated where each element at position `i` is computed as - `self[i] % rhs[i]`. + Constraints: + The element type of the SIMD vector must be integral. Args: - rhs: The rhs of the operation. + value: The other value. + + Returns: + `value << self`. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self.__mod__(rhs) + constrained[type.is_integral(), "must be an integral type"]() + return value << self @always_inline("nodebug") - fn __ipow__(inout self, rhs: Int): - """In-place pow operator. + fn __rrshift__(self, value: Self) -> Self: + """Returns `value >> self`. - The vector is mutated where each element at position `i` is computed as - `pow(self[i], rhs)`. + Constraints: + The element type of the SIMD vector must be integral. Args: - rhs: The rhs of the operation. + value: The other value. + + Returns: + `value >> self`. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - self = self.__pow__(rhs) + constrained[type.is_integral(), "must be an integral type"]() + return value >> self # ===------------------------------------------------------------------=== # - # Checked operations + # Trait implementations # ===------------------------------------------------------------------=== # - @always_inline - fn add_with_overflow(self, rhs: Self) -> (Self, Self._Mask): - """Computes `self + rhs` and a mask of which indices overflowed. - - Args: - rhs: The rhs value. + @always_inline("nodebug") + fn __len__(self) -> Int: + """Gets the length of the SIMD vector. Returns: - A tuple with the results of the operation and a mask for overflows. - The first is a new vector whose element at position `i` is computed - as `self[i] + rhs[i]`. The second item is a vector of booleans where - a `1` at position `i` represents `self[i] + rhs[i]` overflowed. + The length of the SIMD vector. """ - constrained[type.is_integral()]() - @parameter - if type.is_signed(): - var result = llvm_intrinsic[ - "llvm.sadd.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - else: - var result = llvm_intrinsic[ - "llvm.uadd.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) + return self.size - @always_inline - fn sub_with_overflow(self, rhs: Self) -> (Self, Self._Mask): - """Computes `self - rhs` and a mask of which indices overflowed. + @always_inline("nodebug") + fn __bool__(self) -> Bool: + """Converts the SIMD scalar into a boolean value. - Args: - rhs: The rhs value. + Constraints: + The size of the SIMD vector must be 1. Returns: - A tuple with the results of the operation and a mask for overflows. - The first is a new vector whose element at position `i` is computed - as `self[i] - rhs[i]`. The second item is a vector of booleans where - a `1` at position `i` represents `self[i] - rhs[i]` overflowed. + True if the SIMD scalar is non-zero and False otherwise. """ - constrained[type.is_integral()]() - - @parameter - if type.is_signed(): - var result = llvm_intrinsic[ - "llvm.ssub.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - else: - var result = llvm_intrinsic[ - "llvm.usub.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) + constrained[ + size == 1, + ( + "The truth value of a SIMD vector with more than one element is" + " ambiguous. Use the builtin `any()` or `all()` functions" + " instead." + ), + ]() + return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value - @always_inline - fn mul_with_overflow(self, rhs: Self) -> (Self, Self._Mask): - """Computes `self * rhs` and a mask of which indices overflowed. + @always_inline("nodebug") + fn __int__(self) -> Int: + """Casts to the value to an Int. If there is a fractional component, + then the fractional part is truncated. - Args: - rhs: The rhs value. + Constraints: + The size of the SIMD vector must be 1. Returns: - A tuple with the results of the operation and a mask for overflows. - The first is a new vector whose element at position `i` is computed - as `self[i] * rhs[i]`. The second item is a vector of booleans where - a `1` at position `i` represents `self[i] * rhs[i]` overflowed. + The value as an integer. """ - constrained[type.is_integral()]() + constrained[size == 1, "expected a scalar type"]() + return __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( + rebind[Scalar[type]](self).value + ) - @parameter - if type.is_signed(): - var result = llvm_intrinsic[ - "llvm.smul.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - else: - var result = llvm_intrinsic[ - "llvm.umul.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) + @always_inline + fn __str__(self) -> String: + """Get the SIMD as a string. - # ===------------------------------------------------------------------=== # - # Reversed operations - # ===------------------------------------------------------------------=== # + Returns: + A string representation. + """ - @always_inline("nodebug") - fn __radd__(self, value: Self) -> Self: - """Returns `value + self`. + return String.format_sequence(self) - Args: - value: The other value. + @always_inline + fn __repr__(self) -> String: + """Get the representation of the SIMD value e.g. "SIMD[DType.int8, 2](1, 2)". Returns: - `value + self`. + The representation of the SIMD value. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return value + self + + var output = String() + var writer = output._unsafe_to_formatter() + self.format_to[use_scientific_notation=True](writer) + + var values = output.as_string_slice() + + @parameter + if size > 1: + # TODO: Fix when slice indexing is implemented on StringSlice + values = StringSlice(unsafe_from_utf8=output.as_bytes_slice()[1:-1]) + return ( + "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" + ) @always_inline("nodebug") - fn __rsub__(self, value: Self) -> Self: - """Returns `value - self`. - - Args: - value: The other value. + fn __floor__(self) -> Self: + """Performs elementwise floor on the elements of a SIMD vector. Returns: - `value - self`. + The elementwise floor of this SIMD vector. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return value - self + return self._floor_ceil_trunc_impl["llvm.floor"]() @always_inline("nodebug") - fn __rmul__(self, value: Self) -> Self: - """Returns `value * self`. - - Args: - value: The other value. + fn __ceil__(self) -> Self: + """Performs elementwise ceiling on the elements of a SIMD vector. Returns: - `value * self`. + The elementwise ceiling of this SIMD vector. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return value * self + return self._floor_ceil_trunc_impl["llvm.ceil"]() @always_inline("nodebug") - fn __rtruediv__(self, value: Self) -> Self: - """Returns `value / self`. - - Args: - value: The other value. + fn __trunc__(self) -> Self: + """Performs elementwise truncation on the elements of a SIMD vector. Returns: - `value / self`. + The elementwise truncated values of this SIMD vector. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return value / self - # TODO: Move to global function. - @always_inline("nodebug") - fn fma(self, multiplier: Self, accumulator: Self) -> Self: - """Performs a fused multiply-add operation, i.e. - `self*multiplier + accumulator`. + return self._floor_ceil_trunc_impl["llvm.trunc"]() - Args: - multiplier: The value to multiply. - accumulator: The value to accumulate. + @always_inline + fn __abs__(self) -> Self: + """Defines the absolute value operation. Returns: - A new vector whose element at position `i` is computed as - `self[i]*multiplier[i] + accumulator[i]`. + The absolute value of this SIMD vector. """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.fma`( - self.value, multiplier.value, accumulator.value - ) - # ===------------------------------------------------------------------=== # - # Bitwise operations - # ===------------------------------------------------------------------=== # + @parameter + if type.is_unsigned() or type.is_bool(): + return self + elif type.is_floating_point(): + alias integral_type = FPUtils[type].integral_type + var m = self._float_to_bits[integral_type]() + return (m & (FPUtils[type].sign_mask() - 1))._bits_to_float[type]() + else: + return (self < 0).select(-self, self) @always_inline("nodebug") - fn __and__(self, rhs: Self) -> Self: - """Returns `self & rhs`. - - Constraints: - The element type of the SIMD vector must be bool or integral. + fn __round__(self) -> Self: + """Performs elementwise rounding on the elements of a SIMD vector. - Args: - rhs: The RHS value. + This rounding goes to the nearest integer with ties away from zero. Returns: - `self & rhs`. + The elementwise rounded value of this SIMD vector. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - return __mlir_op.`pop.and`(self.value, rhs.value) + return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) @always_inline("nodebug") - fn __iand__(inout self, rhs: Self): - """Computes `self & rhs` and save the result in `self`. - - Constraints: - The element type of the SIMD vector must be bool or integral. - + fn __round__(self, ndigits: Int) -> Self: + """Performs elementwise rounding on the elements of a SIMD vector. + This rounding goes to the nearest integer with ties away from zero. Args: - rhs: The RHS value. + ndigits: The number of digits to round to. + Returns: + The elementwise rounded value of this SIMD vector. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - self = self & rhs - - @always_inline("nodebug") - fn __rand__(self, value: Self) -> Self: - """Returns `value & self`. - - Constraints: - The element type of the SIMD vector must be bool or integral. + # TODO: see how can we implement this. + return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) - Args: - value: The other value. + fn __hash__(self) -> Int: + """Hash the value using builtin hash. Returns: - `value & self`. + A 64-bit hash value. This value is _not_ suitable for cryptographic + uses. Its intended usage is for data structures. See the `hash` + builtin documentation for more details. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - return value & self + return _hash_simd(self) - @always_inline("nodebug") - fn __xor__(self, rhs: Self) -> Self: - """Returns `self ^ rhs`. + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # - Constraints: - The element type of the SIMD vector must be bool or integral. + @always_inline("nodebug") + fn cast[target: DType](self) -> SIMD[target, size]: + """Casts the elements of the SIMD vector to the target element type. - Args: - rhs: The RHS value. + Parameters: + target: The target DType. Returns: - `self ^ rhs`. + A new SIMD vector whose elements have been casted to the target + element type. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - return __mlir_op.`pop.xor`(self.value, rhs.value) - - @always_inline("nodebug") - fn __ixor__(inout self, rhs: Self): - """Computes `self ^ rhs` and save the result in `self`. - Constraints: - The element type of the SIMD vector must be bool or integral. + @parameter + if type == target: + return rebind[SIMD[target, size]](self) - Args: - rhs: The RHS value. - """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - self = self ^ rhs + @parameter + if has_neon() and (type == DType.bfloat16 or target == DType.bfloat16): + # BF16 support on neon systems is not supported. + return _unchecked_zero[target, size]() - @always_inline("nodebug") - fn __rxor__(self, value: Self) -> Self: - """Returns `value ^ self`. + @parameter + if type == DType.bool: + return self.select(SIMD[target, size](1), SIMD[target, size](0)) + elif target == DType.bool: + return rebind[SIMD[target, size]](self != 0) + elif type == DType.bfloat16: + var cast_result = _bfloat16_to_f32( + rebind[SIMD[DType.bfloat16, size]](self) + ).cast[target]() + return rebind[SIMD[target, size]](cast_result) + elif target == DType.bfloat16: + return rebind[SIMD[target, size]]( + _f32_to_bfloat16(self.cast[DType.float32]()) + ) + elif target == DType.address: + var index_val = __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.simd<`, size.value, `, index>`] + ](self.value) + var tmp = SIMD[DType.address, size]( + __mlir_op.`pop.index_to_pointer`[ + _type = __mlir_type[ + `!pop.simd<`, + size.value, + `, address >`, + ] + ](index_val) + ) + return rebind[SIMD[target, size]](tmp) + elif (type == DType.address) and target.is_integral(): + var index_tmp = SIMD[DType.index, size]( + __mlir_op.`pop.pointer_to_index`[ + _type = __mlir_type[ + `!pop.simd<`, + size.value, + `, `, + DType.index.value, + `>`, + ] + ]( + rebind[ + __mlir_type[ + `!pop.simd<`, + size.value, + `, address >`, + ] + ](self.value) + ) + ) + return index_tmp.cast[target]() + else: + return __mlir_op.`pop.cast`[ + _type = __mlir_type[ + `!pop.simd<`, + size.value, + `, `, + target.value, + `>`, + ] + ](self.value) - Constraints: - The element type of the SIMD vector must be bool or integral. + @always_inline + fn format_to(self, inout writer: Formatter): + """ + Formats this SIMD value to the provided formatter. Args: - value: The other value. - - Returns: - `value ^ self`. + writer: The formatter to write to. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - return value ^ self + self.format_to[use_scientific_notation=False](writer) - @always_inline("nodebug") - fn __or__(self, rhs: Self) -> Self: - """Returns `self | rhs`. + # This overload is required to keep SIMD compliant with the Formattable + # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will + # fail to compile. + fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): + """ + Formats this SIMD value to the provided formatter. - Constraints: - The element type of the SIMD vector must be bool or integral. + Parameters: + use_scientific_notation: Whether floats should use scientific notation. + This parameter does not apply to integer types. Args: - rhs: The RHS value. - - Returns: - `self | rhs`. + writer: The formatter to write to. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - return __mlir_op.`pop.or`(self.value, rhs.value) - @always_inline("nodebug") - fn __ior__(inout self, rhs: Self): - """Computes `self | rhs` and save the result in `self`. + # Print an opening `[`. + @parameter + if size > 1: + writer.write_str["["]() - Constraints: - The element type of the SIMD vector must be bool or integral. + # Print each element. + for i in range(size): + var element = self[i] + # Print separators between each element. + if i != 0: + writer.write_str[", "]() - Args: - rhs: The RHS value. - """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - self = self | rhs + @parameter + if triple_is_nvidia_cuda(): - @always_inline("nodebug") - fn __ror__(self, value: Self) -> Self: - """Returns `value | self`. + @parameter + if type.is_floating_point(): + # get_dtype_printf_format hardcodes 17 digits of precision. + _printf["%g"](element) + else: + # FIXME(MSTDL-406): + # This prints "out of band" with the `Formatter` passed + # in, meaning this will only work if `Formatter` is an + # unbuffered wrapper around printf (which Formatter.stdout + # currently is by default). + # + # This is a workaround to permit debug formatting of + # floating-point values on GPU, where printing to stdout + # is the only way the Formatter framework is currently + # used. + _printf[_get_dtype_printf_format[type]()](element) + else: - Constraints: - The element type of the SIMD vector must be bool or integral. + @parameter + if use_scientific_notation and type.is_floating_point(): + alias float_format = "%." + _scientific_notation_digits[ + type + ]() + "e" + _format_scalar[type, float_format](writer, element) + else: + _format_scalar(writer, element) - Args: - value: The other value. + # Print a closing `]`. + @parameter + if size > 1: + writer.write_str["]"]() + + @always_inline + fn _bits_to_float[dest_type: DType](self) -> SIMD[dest_type, size]: + """Bitcasts the integer value to a floating-point value. + + Parameters: + dest_type: DType to bitcast the input SIMD vector to. Returns: - `value | self`. + A floating-point representation of the integer value. """ - constrained[ - type.is_integral() or type.is_bool(), - "must be an integral or bool type", - ]() - return value | self + alias integral_type = FPUtils[type].integral_type + return bitcast[dest_type, size](self.cast[integral_type]()) - @always_inline("nodebug") - fn __invert__(self) -> Self: - """Returns `~self`. + @always_inline + fn _float_to_bits[dest_type: DType](self) -> SIMD[dest_type, size]: + """Bitcasts the floating-point value to an integer value. - Constraints: - The element type of the SIMD vector must be boolean or integral. + Parameters: + dest_type: DType to bitcast the input SIMD vector to. Returns: - The `~self` value. + An integer representation of the floating-point value. """ + alias integral_type = FPUtils[type].integral_type + var v = bitcast[integral_type, size](self) + return v.cast[dest_type]() + + fn _floor_ceil_trunc_impl[intrinsic: StringLiteral](self) -> Self: constrained[ - type.is_bool() or type.is_integral(), - "must be an bool or integral type", + intrinsic == "llvm.floor" + or intrinsic == "llvm.ceil" + or intrinsic == "llvm.trunc", + "unsupported intrinsic", ]() @parameter - if type.is_bool(): - return self.select(Self(False), Self(True)) - else: - return self ^ -1 + if type.is_bool() or type.is_integral(): + return self - # ===------------------------------------------------------------------=== # - # Shift operations - # ===------------------------------------------------------------------=== # + @parameter + if has_neon() and type == DType.bfloat16: + return ( + self.cast[DType.float32]() + ._floor_ceil_trunc_impl[intrinsic]() + .cast[type]() + ) - @always_inline("nodebug") - fn __lshift__(self, rhs: Self) -> Self: - """Returns `self << rhs`. + return llvm_intrinsic[intrinsic, Self, has_side_effect=False](self) - Constraints: - The element type of the SIMD vector must be integral. + fn clamp(self, lower_bound: Self, upper_bound: Self) -> Self: + """Clamps the values in a SIMD vector to be in a certain range. + + Clamp cuts values in the input SIMD vector off at the upper bound and + lower bound values. For example, SIMD vector `[0, 1, 2, 3]` clamped to + a lower bound of 1 and an upper bound of 2 would return `[1, 1, 2, 2]`. Args: - rhs: The RHS value. + lower_bound: Minimum of the range to clamp to. + upper_bound: Maximum of the range to clamp to. Returns: - `self << rhs`. + A new SIMD vector containing x clamped to be within lower_bound and + upper_bound. """ - constrained[type.is_integral(), "must be an integral type"]() - debug_assert(all(rhs >= 0), "unhandled negative value") - return __mlir_op.`pop.shl`(self.value, rhs.value) - @always_inline("nodebug") - fn __rshift__(self, rhs: Self) -> Self: - """Returns `self >> rhs`. + return self.min(upper_bound).max(lower_bound) - Constraints: - The element type of the SIMD vector must be integral. + @always_inline("nodebug") + fn roundeven(self) -> Self: + """Performs elementwise banker's rounding on the elements of a SIMD + vector. - Args: - rhs: The RHS value. + This rounding goes to the nearest integer with ties toward the nearest + even integer. Returns: - `self >> rhs`. + The elementwise banker's rounding of this SIMD vector. """ - constrained[type.is_integral(), "must be an integral type"]() - debug_assert(all(rhs >= 0), "unhandled negative value") - return __mlir_op.`pop.shr`(self.value, rhs.value) - - @always_inline("nodebug") - fn __ilshift__(inout self, rhs: Self): - """Computes `self << rhs` and save the result in `self`. + return llvm_intrinsic["llvm.roundeven", Self, has_side_effect=False]( + self + ) - Constraints: - The element type of the SIMD vector must be integral. + @always_inline + fn add_with_overflow(self, rhs: Self) -> (Self, Self._Mask): + """Computes `self + rhs` and a mask of which indices overflowed. Args: - rhs: The RHS value. + rhs: The rhs value. + + Returns: + A tuple with the results of the operation and a mask for overflows. + The first is a new vector whose element at position `i` is computed + as `self[i] + rhs[i]`. The second item is a vector of booleans where + a `1` at position `i` represents `self[i] + rhs[i]` overflowed. """ - constrained[type.is_integral(), "must be an integral type"]() - self = self << rhs + constrained[type.is_integral()]() - @always_inline("nodebug") - fn __irshift__(inout self, rhs: Self): - """Computes `self >> rhs` and save the result in `self`. + @parameter + if type.is_signed(): + var result = llvm_intrinsic[ + "llvm.sadd.with.overflow", + _RegisterPackType[Self, Self._Mask], + Self, + Self, + ](self, rhs) + return (result[0], result[1]) + else: + var result = llvm_intrinsic[ + "llvm.uadd.with.overflow", + _RegisterPackType[Self, Self._Mask], + Self, + Self, + ](self, rhs) + return (result[0], result[1]) - Constraints: - The element type of the SIMD vector must be integral. + @always_inline + fn sub_with_overflow(self, rhs: Self) -> (Self, Self._Mask): + """Computes `self - rhs` and a mask of which indices overflowed. Args: - rhs: The RHS value. + rhs: The rhs value. + + Returns: + A tuple with the results of the operation and a mask for overflows. + The first is a new vector whose element at position `i` is computed + as `self[i] - rhs[i]`. The second item is a vector of booleans where + a `1` at position `i` represents `self[i] - rhs[i]` overflowed. """ - constrained[type.is_integral(), "must be an integral type"]() - self = self >> rhs + constrained[type.is_integral()]() - @always_inline("nodebug") - fn __rlshift__(self, value: Self) -> Self: - """Returns `value << self`. + @parameter + if type.is_signed(): + var result = llvm_intrinsic[ + "llvm.ssub.with.overflow", + _RegisterPackType[Self, Self._Mask], + Self, + Self, + ](self, rhs) + return (result[0], result[1]) + else: + var result = llvm_intrinsic[ + "llvm.usub.with.overflow", + _RegisterPackType[Self, Self._Mask], + Self, + Self, + ](self, rhs) + return (result[0], result[1]) - Constraints: - The element type of the SIMD vector must be integral. + @always_inline + fn mul_with_overflow(self, rhs: Self) -> (Self, Self._Mask): + """Computes `self * rhs` and a mask of which indices overflowed. Args: - value: The other value. + rhs: The rhs value. Returns: - `value << self`. + A tuple with the results of the operation and a mask for overflows. + The first is a new vector whose element at position `i` is computed + as `self[i] * rhs[i]`. The second item is a vector of booleans where + a `1` at position `i` represents `self[i] * rhs[i]` overflowed. """ - constrained[type.is_integral(), "must be an integral type"]() - return value << self + constrained[type.is_integral()]() - @always_inline("nodebug") - fn __rrshift__(self, value: Self) -> Self: - """Returns `value >> self`. + @parameter + if type.is_signed(): + var result = llvm_intrinsic[ + "llvm.smul.with.overflow", + _RegisterPackType[Self, Self._Mask], + Self, + Self, + ](self, rhs) + return (result[0], result[1]) + else: + var result = llvm_intrinsic[ + "llvm.umul.with.overflow", + _RegisterPackType[Self, Self._Mask], + Self, + Self, + ](self, rhs) + return (result[0], result[1]) - Constraints: - The element type of the SIMD vector must be integral. + # TODO: Move to global function. + @always_inline("nodebug") + fn fma(self, multiplier: Self, accumulator: Self) -> Self: + """Performs a fused multiply-add operation, i.e. + `self*multiplier + accumulator`. Args: - value: The other value. + multiplier: The value to multiply. + accumulator: The value to accumulate. Returns: - `value >> self`. + A new vector whose element at position `i` is computed as + `self[i]*multiplier[i] + accumulator[i]`. """ - constrained[type.is_integral(), "must be an integral type"]() - return value >> self - - # ===------------------------------------------------------------------=== # - # Shuffle operations - # ===------------------------------------------------------------------=== # + constrained[type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.fma`( + self.value, multiplier.value, accumulator.value + ) @always_inline("nodebug") fn _shuffle_list[ @@ -2038,60 +1942,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ return self._shuffle_list[size, mask](other) - # ===------------------------------------------------------------------=== # - # Indexing operations - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> Scalar[type]: - """Gets an element from the vector. - - Args: - idx: The element index. - - Returns: - The value at position `idx`. - """ - return __mlir_op.`pop.simd.extractelement`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ](self.value, index(idx).value) - - @always_inline("nodebug") - fn __setitem__(inout self, idx: Int, val: Scalar[type]): - """Sets an element in the vector. - - Args: - idx: The index to set. - val: The value to set. - """ - self.value = __mlir_op.`pop.simd.insertelement`( - self.value, val.value, index(idx).value - ) - - @always_inline("nodebug") - fn __setitem__( - inout self, idx: Int, val: __mlir_type[`!pop.scalar<`, type.value, `>`] - ): - """Sets an element in the vector. - - Args: - idx: The index to set. - val: The value to set. - """ - self.value = __mlir_op.`pop.simd.insertelement`( - self.value, val, index(idx).value - ) - - fn __hash__(self) -> Int: - """Hash the value using builtin hash. - - Returns: - A 64-bit hash value. This value is _not_ suitable for cryptographic - uses. Its intended usage is for data structures. See the `hash` - builtin documentation for more details. - """ - return _hash_simd(self) - @always_inline("nodebug") fn slice[ output_width: Int, /, *, offset: Int = 0 @@ -2262,10 +2112,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rebind[Self._SIMDHalfType](res[1]), ) - # ===------------------------------------------------------------------=== # - # Binary operations - # ===------------------------------------------------------------------=== # - @always_inline("nodebug") fn min(self, other: Self) -> Self: """Computes the elementwise minimum between the two vectors. @@ -2782,6 +2628,153 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "llvm.vector.splice", Self, has_side_effect=False ](zero_simd, self, Int32(-shift)) + @staticmethod + @always_inline + fn prefetch[ + params: PrefetchOptions, + *, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space]): + # Prefetch at the underlying address. + """Prefetches memory at the underlying address. + + Parameters: + params: Prefetch options (see `PrefetchOptions` for details). + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to prefetch from. + """ + prefetch[params](ptr) + + @staticmethod + @always_inline("nodebug") + fn load[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space]) -> Self: + """Loads the value the Pointer object points to. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + + Returns: + The loaded value. + """ + return Self.load[alignment=alignment, address_space=address_space]( + ptr, 0 + ) + + @staticmethod + @always_inline("nodebug") + fn load[ + T: Intable, + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: T) -> Self: + """Loads the value the Pointer object points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + T: The Intable type of the offset. + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + offset: The offset to load from. + + Returns: + The loaded value. + """ + + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: + # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types + # regardless of the alignment that is passed. This causes issues if + # this method is called on an unaligned pointer. + # TODO #37823 We can make this smarter when we add an `aligned` + # trait to the pointer class. + var v = SIMD[type, size]() + + # intentionally don't unroll, otherwise the compiler vectorizes + for i in range(size): + v[i] = ptr.address.offset(int(offset) + i).load[ + alignment=alignment + ]() + return v + + return ( + ptr.address.offset(offset) + .bitcast[SIMD[type, size]]() + .load[alignment=alignment]() + ) + + @staticmethod + @always_inline("nodebug") + fn store[ + T: Intable, + /, + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: T, val: Self): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + T: The Intable type of the offset. + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + offset: The offset to store to. + val: The value to store. + """ + Self.store[alignment=alignment, address_space=address_space]( + ptr.offset(offset), val + ) + + @staticmethod + @always_inline("nodebug") + fn store[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], val: Self): + """Stores a single element value. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + val: The value to store. + """ + constrained[size > 0, "width must be a positive integer value"]() + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() + ptr.address.bitcast[SIMD[type, size]]().store[alignment=alignment](val) + # ===----------------------------------------------------------------------=== # # _pow diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 11bdefcaae..4ac2a1082a 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -47,11 +47,16 @@ struct StringLiteral( and this does not include the null terminator. """ + # Fields alias type = __mlir_type.`!kgen.string` var value: Self.type """The underlying storage for the string literal.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self, value: Self.type): """Create a string literal from a builtin string type. @@ -61,66 +66,9 @@ struct StringLiteral( """ self.value = value - @always_inline("nodebug") - fn __len__(self) -> Int: - """Get the string length. - - Returns: - The length of this StringLiteral. - """ - # TODO(MSTDL-160): - # Properly count Unicode codepoints instead of returning this length - # in bytes. - return self._byte_length() - - @always_inline - fn _byte_length(self) -> Int: - """Get the string length in bytes. - - Returns: - The length of this StringLiteral in bytes. - """ - return __mlir_op.`pop.string.size`(self.value) - - @always_inline("nodebug") - fn unsafe_ptr(self) -> UnsafePointer[Int8]: - """Get raw pointer to the underlying data. - - Returns: - The raw pointer to the data. - """ - var ptr = DTypePointer[DType.int8]( - __mlir_op.`pop.string.address`(self.value) - ) - - return UnsafePointer[Int8]._from_dtype_ptr(ptr) - - @always_inline("nodebug") - fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: - """Get raw pointer to the underlying data. - - Returns: - The raw pointer to the data. - """ - return self.unsafe_ptr().bitcast[UInt8]() - - @always_inline("nodebug") - fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: - """Get raw pointer to the underlying data. - - Returns: - The raw pointer to the data. - """ - return self.unsafe_ptr().bitcast[UInt8]() - - @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Convert the string to a bool value. - - Returns: - True if the string is not empty. - """ - return len(self) != 0 + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __add__(self, rhs: StringLiteral) -> StringLiteral: @@ -206,15 +154,53 @@ struct StringLiteral( """ return not (self < rhs) - fn __hash__(self) -> Int: - """Hash the underlying buffer using builtin hash. + fn __contains__(self, substr: StringLiteral) -> Bool: + """Returns True if the substring is contained within the current string. + + Args: + substr: The substring to check. Returns: - A 64-bit hash value. This value is _not_ suitable for cryptographic - uses. Its intended usage is for data structures. See the `hash` - builtin documentation for more details. + True if the string contains the substring. """ - return hash(self.unsafe_ptr(), len(self)) + return substr in StringRef(self) + + # ===-------------------------------------------------------------------===# + # Trait impelemntations + # ===-------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __len__(self) -> Int: + """Get the string length. + + Returns: + The length of this StringLiteral. + """ + # TODO(MSTDL-160): + # Properly count Unicode codepoints instead of returning this length + # in bytes. + return self._byte_length() + + @always_inline("nodebug") + fn __bool__(self) -> Bool: + """Convert the string to a bool value. + + Returns: + True if the string is not empty. + """ + return len(self) != 0 + + fn __int__(self) raises -> Int: + """Parses the given string as a base-10 integer and returns that value. + + For example, `int("19")` returns `19`. If the given string cannot be parsed + as an integer value, an error is raised. For example, `int("hi")` raises an + error. + + Returns: + An integer value that represents the string, or otherwise raises. + """ + return _atol(self) fn __str__(self) -> String: """Convert the string literal to a string. @@ -249,6 +235,60 @@ struct StringLiteral( """ return self.__str__().__repr__() + fn __hash__(self) -> Int: + """Hash the underlying buffer using builtin hash. + + Returns: + A 64-bit hash value. This value is _not_ suitable for cryptographic + uses. Its intended usage is for data structures. See the `hash` + builtin documentation for more details. + """ + return hash(self.unsafe_ptr(), len(self)) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + @always_inline + fn _byte_length(self) -> Int: + """Get the string length in bytes. + + Returns: + The length of this StringLiteral in bytes. + """ + return __mlir_op.`pop.string.size`(self.value) + + @always_inline("nodebug") + fn unsafe_ptr(self) -> UnsafePointer[Int8]: + """Get raw pointer to the underlying data. + + Returns: + The raw pointer to the data. + """ + var ptr = DTypePointer[DType.int8]( + __mlir_op.`pop.string.address`(self.value) + ) + + return UnsafePointer[Int8]._from_dtype_ptr(ptr) + + @always_inline("nodebug") + fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: + """Get raw pointer to the underlying data. + + Returns: + The raw pointer to the data. + """ + return self.unsafe_ptr().bitcast[UInt8]() + + @always_inline("nodebug") + fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + """Get raw pointer to the underlying data. + + Returns: + The raw pointer to the data. + """ + return self.unsafe_ptr().bitcast[UInt8]() + @always_inline fn as_string_slice(self) -> StringSlice[ImmutableStaticLifetime]: """Returns a string slice of this static string literal. @@ -290,17 +330,6 @@ struct StringLiteral( writer.write_str(self.as_string_slice()) - fn __contains__(self, substr: StringLiteral) -> Bool: - """Returns True if the substring is contained within the current string. - - Args: - substr: The substring to check. - - Returns: - True if the string contains the substring. - """ - return substr in StringRef(self) - fn find(self, substr: StringLiteral, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at `start`. If not found, returns -1. @@ -326,15 +355,3 @@ struct StringLiteral( The offset of `substr` relative to the beginning of the string. """ return StringRef(self).rfind(substr, start=start) - - fn __int__(self) raises -> Int: - """Parses the given string as a base-10 integer and returns that value. - - For example, `int("19")` returns `19`. If the given string cannot be parsed - as an integer value, an error is raised. For example, `int("hi")` raises an - error. - - Returns: - An integer value that represents the string, or otherwise raises. - """ - return _atol(self) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index d0cfbcf2a9..f1e303d63f 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -87,9 +87,14 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): capacity: The maximum number of elements that the list can hold. """ + # Fields var _array: InlineArray[ElementType, capacity] var _size: Int + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline fn __init__(inout self): """This constructor creates an empty InlineList.""" @@ -112,20 +117,14 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): self.append(ElementType(other=value[])) @always_inline - fn __len__(self) -> Int: - """Returns the length of the list.""" - return self._size - - @always_inline - fn append(inout self, owned value: ElementType): - """Appends a value to the list. + fn __del__(owned self): + """Destroy all the elements in the list and free the memory.""" + for i in range(self._size): + UnsafePointer.address_of(self._array[i]).destroy_pointee() - Args: - value: The value to append. - """ - debug_assert(self._size < capacity, "List is full.") - self._array[self._size] = value^ - self._size += 1 + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# @always_inline fn __getitem__( @@ -148,11 +147,23 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): return self._array[idx] + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @always_inline - fn __del__(owned self): - """Destroy all the elements in the list and free the memory.""" - for i in range(self._size): - UnsafePointer.address_of(self._array[i]).destroy_pointee() + fn __len__(self) -> Int: + """Returns the length of the list.""" + return self._size + + @always_inline + fn __bool__(self) -> Bool: + """Checks whether the list has any elements or not. + + Returns: + `False` if the list is empty, `True` if there is at least one element. + """ + return len(self) > 0 fn __iter__( ref [_]self: Self, @@ -193,6 +204,10 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): return True return False + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline fn count[C: ComparableCollectionElement](self: Self, value: C) -> Int: """Counts the number of occurrences of a value in the list. @@ -222,10 +237,12 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): return count @always_inline - fn __bool__(self) -> Bool: - """Checks whether the list has any elements or not. + fn append(inout self, owned value: ElementType): + """Appends a value to the list. - Returns: - `False` if the list is empty, `True` if there is at least one element. + Args: + value: The value to append. """ - return len(self) > 0 + debug_assert(self._size < capacity, "List is full.") + self._array[self._size] = value^ + self._size += 1 diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index c6de8d3136..6fa008f2bf 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -574,11 +574,16 @@ struct DTypePointer[ address_space: The address space the pointer is in. """ + # Fields alias element_type = Scalar[type] alias _pointer_type = Pointer[Scalar[type], address_space] var address: Self._pointer_type """The pointed-to address.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self): """Constructs a null `DTypePointer` from the given type.""" @@ -644,23 +649,9 @@ struct DTypePointer[ """ self.address = Self._pointer_type(address=address) - fn __str__(self) -> String: - """Format this pointer as a hexadecimal string. - - Returns: - A String containing the hexadecimal representation of the memory location - destination of this pointer. - """ - return str(self.address) - - @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Checks if the DTypePointer is *null*. - - Returns: - Returns False if the DTypePointer is *null* and True otherwise. - """ - return self.address.__bool__() + # ===------------------------------------------------------------------=== # + # Factory methods + # ===------------------------------------------------------------------=== # @staticmethod @always_inline("nodebug") @@ -675,6 +666,28 @@ struct DTypePointer[ """ return LegacyPointer.address_of(arg) + @staticmethod + @always_inline + fn alloc(count: Int, /, *, alignment: Int = alignof[type]()) -> Self: + """Heap-allocates a number of element of the specified type using + the specified alignment. + + Args: + count: The number of elements to allocate (note that this is not + the bytecount). + alignment: The alignment used for the allocation. + + Returns: + A new `DTypePointer` object which has been allocated on the heap. + """ + return _malloc[Self.element_type, address_space=address_space]( + count * sizeof[type](), alignment=alignment + ) + + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __getitem__(self, offset: Int) -> Scalar[type]: """Loads a single element (SIMD of size 1) from the pointer at the @@ -698,10 +711,6 @@ struct DTypePointer[ """ return Scalar.store(self, offset, val) - # ===------------------------------------------------------------------=== # - # Comparisons - # ===------------------------------------------------------------------=== # - @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: """Returns True if the two pointers are equal. @@ -739,26 +748,97 @@ struct DTypePointer[ return self.address < rhs.address # ===------------------------------------------------------------------=== # - # Allocate/Free + # Pointer arithmetic # ===------------------------------------------------------------------=== # - @staticmethod - @always_inline - fn alloc(count: Int, /, *, alignment: Int = alignof[type]()) -> Self: - """Heap-allocates a number of element of the specified type using - the specified alignment. + @always_inline("nodebug") + fn __add__[T: Intable](self, rhs: T) -> Self: + """Returns a new pointer shifted by the specified offset. + + Parameters: + T: The Intable type of the offset. Args: - count: The number of elements to allocate (note that this is not - the bytecount). - alignment: The alignment used for the allocation. + rhs: The offset. Returns: - A new `DTypePointer` object which has been allocated on the heap. + The new DTypePointer shifted by the offset. """ - return _malloc[Self.element_type, address_space=address_space]( - count * sizeof[type](), alignment=alignment - ) + return self.offset(rhs) + + @always_inline("nodebug") + fn __sub__[T: Intable](self, rhs: T) -> Self: + """Returns a new pointer shifted back by the specified offset. + + Parameters: + T: The Intable type of the offset. + + Args: + rhs: The offset. + + Returns: + The new DTypePointer shifted by the offset. + """ + return self.offset(-int(rhs)) + + @always_inline("nodebug") + fn __iadd__[T: Intable](inout self, rhs: T): + """Shifts the current pointer by the specified offset. + + Parameters: + T: The Intable type of the offset. + + Args: + rhs: The offset. + """ + self = self + rhs + + @always_inline("nodebug") + fn __isub__[T: Intable](inout self, rhs: T): + """Shifts back the current pointer by the specified offset. + + Parameters: + T: The Intable type of the offset. + + Args: + rhs: The offset. + """ + self = self - rhs + + # ===------------------------------------------------------------------=== # + # Trait implementations + # ===------------------------------------------------------------------=== # + + @always_inline("nodebug") + fn __int__(self) -> Int: + """Returns the pointer address as an integer. + + Returns: + The address of the pointer as an Int. + """ + return int(self.address) + + fn __str__(self) -> String: + """Format this pointer as a hexadecimal string. + + Returns: + A String containing the hexadecimal representation of the memory location + destination of this pointer. + """ + return str(self.address) + + @always_inline("nodebug") + fn __bool__(self) -> Bool: + """Checks if the DTypePointer is *null*. + + Returns: + Returns False if the DTypePointer is *null* and True otherwise. + """ + return self.address.__bool__() + + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # @always_inline fn free(self): @@ -796,10 +876,6 @@ struct DTypePointer[ """ return self.address - # ===------------------------------------------------------------------=== # - # Load/Store - # ===------------------------------------------------------------------=== # - alias _default_alignment = alignof[ Scalar[type] ]() if triple_is_nvidia_cuda() else 1 @@ -874,7 +950,7 @@ struct DTypePointer[ self.address.bitcast[SIMD[type, width]]().nt_store(val) # ===------------------------------------------------------------------=== # - # Gather/Scatter + # Gather / Scatter # ===------------------------------------------------------------------=== # @always_inline("nodebug") @@ -1035,15 +1111,6 @@ struct DTypePointer[ var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) scatter(val, base.cast[DType.address](), mask, alignment) - @always_inline("nodebug") - fn __int__(self) -> Int: - """Returns the pointer address as an integer. - - Returns: - The address of the pointer as an Int. - """ - return int(self.address) - @always_inline fn is_aligned[alignment: Int](self) -> Bool: """Checks if the pointer is aligned. @@ -1060,10 +1127,6 @@ struct DTypePointer[ ]() return int(self) % alignment == 0 - # ===------------------------------------------------------------------=== # - # Pointer Arithmetic - # ===------------------------------------------------------------------=== # - @always_inline("nodebug") fn offset[T: Intable](self, idx: T) -> Self: """Returns a new pointer shifted by the specified offset. @@ -1078,57 +1141,3 @@ struct DTypePointer[ The new constructed DTypePointer. """ return self.address.offset(idx) - - @always_inline("nodebug") - fn __add__[T: Intable](self, rhs: T) -> Self: - """Returns a new pointer shifted by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - - Returns: - The new DTypePointer shifted by the offset. - """ - return self.offset(rhs) - - @always_inline("nodebug") - fn __sub__[T: Intable](self, rhs: T) -> Self: - """Returns a new pointer shifted back by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - - Returns: - The new DTypePointer shifted by the offset. - """ - return self.offset(-int(rhs)) - - @always_inline("nodebug") - fn __iadd__[T: Intable](inout self, rhs: T): - """Shifts the current pointer by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - """ - self = self + rhs - - @always_inline("nodebug") - fn __isub__[T: Intable](inout self, rhs: T): - """Shifts back the current pointer by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - """ - self = self - rhs From b96e1d42ecc8d776d3e3d396ee294c5387cbce37 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 14:29:24 -0400 Subject: [PATCH 0852/2019] [stdlib] Add some tests for `utils.numerics` To improve coverage. MODULAR_ORIG_COMMIT_REV_ID: d94a74585f2bcc986892aa50fa6ae355b2908bb8 --- stdlib/test/utils/test_numerics.mojo | 56 ++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index 5034233b37..a12fe84291 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -134,20 +134,70 @@ def test_isnan(): assert_true(isnan(nan[DType.float64]())) +fn overflow_int[type: DType]() -> Bool: + constrained[type.is_integral(), "comparison only valid on integral types"]() + return max_finite[type]() + 1 < max_finite[type]() + + +fn overflow_fp[type: DType]() -> Bool: + constrained[ + type.is_floating_point(), + "comparison only valid on floating point types", + ]() + return max_finite[type]() + 1 == max_finite[type]() + + def test_max_finite(): assert_almost_equal(max_finite[DType.float32](), 3.4028235e38) assert_almost_equal(max_finite[DType.float64](), 1.7976931348623157e308) + assert_true(overflow_int[DType.int8]()) + assert_true(overflow_int[DType.uint8]()) + assert_true(overflow_int[DType.int16]()) + assert_true(overflow_int[DType.uint16]()) + assert_true(overflow_int[DType.int32]()) + assert_true(overflow_int[DType.uint32]()) + assert_true(overflow_int[DType.int64]()) + assert_true(overflow_int[DType.uint64]()) -def test_max_or_inf(): - assert_almost_equal(max_or_inf[DType.float32](), inf[DType.float32]()) - assert_almost_equal(max_or_inf[DType.float64](), inf[DType.float64]()) + assert_true(overflow_fp[DType.float32]()) + assert_true(overflow_fp[DType.float64]()) + + +fn underflow_int[type: DType]() -> Bool: + constrained[type.is_integral(), "comparison only valid on integral types"]() + return min_finite[type]() - 1 > min_finite[type]() + + +fn underflow_fp[type: DType]() -> Bool: + constrained[ + type.is_floating_point(), + "comparison only valid on floating point types", + ]() + return min_finite[type]() - 1 == min_finite[type]() def test_min_finite(): assert_almost_equal(min_finite[DType.float32](), -3.4028235e38) assert_almost_equal(min_finite[DType.float64](), -1.7976931348623157e308) + assert_true(underflow_int[DType.int8]()) + assert_true(underflow_int[DType.uint8]()) + assert_true(underflow_int[DType.int16]()) + assert_true(underflow_int[DType.uint16]()) + assert_true(underflow_int[DType.int32]()) + assert_true(underflow_int[DType.uint32]()) + assert_true(underflow_int[DType.int64]()) + assert_true(underflow_int[DType.uint64]()) + + assert_true(underflow_fp[DType.float32]()) + assert_true(underflow_fp[DType.float64]()) + + +def test_max_or_inf(): + assert_almost_equal(max_or_inf[DType.float32](), inf[DType.float32]()) + assert_almost_equal(max_or_inf[DType.float64](), inf[DType.float64]()) + def test_min_or_neg_inf(): assert_almost_equal( From 14071cf0f0f38d30861b20284b433e07f932fd0a Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 3 Jun 2024 12:19:19 -0700 Subject: [PATCH 0853/2019] [Docs] Add page on unsafe pointers. Covers UnsafePointer and DTypePointer. MODULAR_ORIG_COMMIT_REV_ID: f5ea0fc20b011e4fd48eff5e98c308054a76d538 --- docs/manual/images/pointer-diagram-dark.png | Bin 0 -> 21558 bytes docs/manual/images/pointer-diagram.png | Bin 0 -> 20998 bytes docs/manual/images/pointer-lifecycle-dark.png | Bin 0 -> 82779 bytes docs/manual/images/pointer-lifecycle.png | Bin 0 -> 81374 bytes docs/manual/images/pointer-offset-dark.png | Bin 0 -> 15226 bytes docs/manual/images/pointer-offset.png | Bin 0 -> 15051 bytes .../images/strided-load-storage-dark.png | Bin 0 -> 13876 bytes docs/manual/images/strided-load-storage.png | Bin 0 -> 13569 bytes docs/manual/pointers.ipynb | 756 ++++++++++++++++++ 9 files changed, 756 insertions(+) create mode 100644 docs/manual/images/pointer-diagram-dark.png create mode 100644 docs/manual/images/pointer-diagram.png create mode 100644 docs/manual/images/pointer-lifecycle-dark.png create mode 100644 docs/manual/images/pointer-lifecycle.png create mode 100644 docs/manual/images/pointer-offset-dark.png create mode 100644 docs/manual/images/pointer-offset.png create mode 100644 docs/manual/images/strided-load-storage-dark.png create mode 100644 docs/manual/images/strided-load-storage.png create mode 100644 docs/manual/pointers.ipynb diff --git a/docs/manual/images/pointer-diagram-dark.png b/docs/manual/images/pointer-diagram-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fe55eb78ff1833f9d96cf011e1d6bd7273dcd9ff GIT binary patch literal 21558 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 WKtyzfdJPfqM@vl)_Ce)Q$o~VYQC747 literal 0 HcmV?d00001 diff --git a/docs/manual/images/strided-load-storage-dark.png b/docs/manual/images/strided-load-storage-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e72ad2034cad6dadd638b286b8c9996c988aeb8f GIT binary patch literal 13876 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 AqyPW_ literal 0 HcmV?d00001 diff --git a/docs/manual/images/strided-load-storage.png b/docs/manual/images/strided-load-storage.png new file mode 100644 index 0000000000000000000000000000000000000000..9aea48055d1a26b071ef6cd67b86bbfa3f31d081 GIT binary patch literal 13569 zcmd6OhdWzw{H_ti4rOSHrM6OvQnYH;-m6CK z?VkR|y}$bx-1|Ivl04^p*ZGdmc;ENL>+7nK5-|{AVPTPKsH+%aVd21m|NdZn;QOA< z?nmGU+t*MHhE+Yxw1tJmhNYpRWE^O{8%CJRshZ`bV(NEx{4jz@E=n}rK(2dnGf-YF zl%IWZcRQG^VmY{)cAmC6aMwGqQkL2*%|tdo5YtkA9f%9hF#qVZHtPLH-z2;>fDJZ235-lhLT5rV(x3UDf)T-b|}Qj**TdQu}H3O3jV9OaCY~r#Clew zdPhFO@KOWTuMunki}R93`$ldAeU7^#t7K)x?wy(hLtiR^FR(VQ-+sD3;o86>gFYgJ z^?T$Vd3-ua<>y%o{s>tn@7Q0+HEwnEiWNQ$g7*AGuY~p=wdI!xTlY>85JF$x_gfy6 z8@a074cR`q)PcoafuYvBSMr<5WBKM){z82|w`j;A&WfV~H`4OE)LroQ+fG`NXe`Rg zf<=DMrQr5(1!xbMhOPK{$7i0TpH0;v+{MR6usDIx1p-wz_jyq=#8*1pDAE`UxmGaL z4fw1n{IU7yl+^0bhsMhhuIv#()%FoXt(C#kK(~QHR4?fq&Nz{?{EYJbQVIj7TqnUd z)3CUgoJbOrkxw@)YvsTNCMbw*pTmS+b8DXvYI7*NAGTPUbb&TBb4JZUYeRI+dkRb| znvH8C@f({m$2vB+4`wLaR;dyW0bvXz4Zwe43um9nb0&eV;Lsj&Tfwx^$)G%f zdGpZc2*quXKt)km?qZgU1khJ6u&*`v+Vf|pnfQ=R?H_;K(t*07hn##r?6lNyBboI0 z@M^vjI}#||M@BH)pX+PpLc3Q(5`dIjlYW}t>Nd?ibGi@!nz zySBHpTN(|!I-R2?coBE=yFKJ=H#lYI&!0bGQEcZ`?q}G!M|TNFXpzYwV2Ts$CO}%% z1p&m+2b+O&^AANukG?T_Hre^C^d{~6-WBhc2(0hul2ULO6Q-`sJBG=p8(ykId$c#y zcj#c?$~g;Qq_|7MY70i9yR-p#|%M$krsq>_DPTt~|!{^mp8|#|M z{2zZO6x3LRZ#$1H@|%O;wsvv%u%F1yMmPqn;RoE zotNX`v(46c4eZn>Z>|0+oCHnZNNvc_IVi9rBMi6BW=ofiEGOj>4KL5KM=u%mIVm=7 z^%isQWYQc^H@?Wx1liQ-wxA<}qobpR=y!(7F5xux-R}*pD5HSwizz}a&+Jdn?T9G@D%+j+ILun;&vWU zS4eJTiuB%=bXN_3q#JCHc-nvdZK3o~F`TLIA{DYM8spjAc^3AJ@kM$I^5g2G_GNji z$#yzf<;VHe#c9JT6qRup+;&$pXLHoga$y zlx&cO+)#5#lGwA8i41{7G9U+>a)m$BA2&REr;4H>eTJIOgizXV(lz&Bofl!}#{B6s z3`xD?2Op0UpEY!-L>3x;97VwL@yaavM%y>y6=0&lX8zfA!j? zJ~FJCT;N7xeF2U5LwjnZWyUu8&!X>21!SxuA+YKXY2m_P#Qp5iR+R5;Vbf-{&FAoj z6-gdx^pQEec6+|z<-L(3Pt=UR!8R`bxa&!XQ|ISxh2rDqBS*utEq*(zZv*C~yo9TX zr8c}WV|H0>^BSZVDb5pUj3{r*<~&&|JXM8z3zyH>ziCvNhP+#mw0&lLotw&~I-3a* zWV<9LCSKq~Vr7Zt3jnL3oeKAw!W5X688701E8lJ;FRf>NeNk+L6h0L~rYK}qz0?Q) z)#~$!NWoQ7FbDfELvRn68fSK|U{acV-$}3U3faK6ucyKQ+dn#wuqX~#2s>Mmc8qN5 zE_wDDg0sw9!-c$VT2YPFp|`Z&8V;*6mFe=y{qd0#Df!rgy9hi1mGF(VIW^l+NgIo6p4kve?Fa?n>*X)yP=!Y@XJf2yusW5$No#`TlyYTHC0<)#9GUq0~qRU{6e|du({e7h12@*0Db;;C2v0{kM;pkV4Y+( zgWnI;8Ujvreo8vcrYlCV5<9v0Ue80-0XH@WyLmz6dDi|_ve<&mrh1Gp{(MPx+sPrm zNxm?C8S@u5mnhpvkuZK)jppR#-teb+^Bc((}18~l_9Isul_Pdu?50D zP8&y#D>pvrAwmhWDZdoBBC}@gywyt^WdkEP)(k`f*-Lj?-VyMo=)=HtOb_31|C}rH zi~`(xYyipd<*;RBWb};LLRt((oD3B#RK4RYJ@-hjdt_A0BK?l6)>?0K?Z(IIY~iU5 z+quNCB;dvQ+uaU!C=tbNX?0A;c-3=vS%*DD(A$Btn~}b^YR#tk6Hw58#_IM;$9~kT zh1G4`)vfH9nVU7miyV1Us;EaYG9mm(y8h{*+XMN4CR^9cDjmbAlps0T* z?*?^TzQ35OOkx%Iy>v@5rFQTB7rBrL+P5h)-itH5cLO%9GR^U4;&CU4 zliFZH`JHca_OZdCP~2qd;^V>Js9C_6J@nFnfX?DyOSt*_`vcJyb`U3ipVa}DU7!Qy zWAV?1_4Ur#O4AEasl``Jfnw6`BpKDT#~h?wBe1x$%Z@u1D$%3}p(EQZMl#JU`n-)W z<#DsFe*CU-kzN|dtliwB16Wj=mmg<6@<*~rZ1s-y2i9oZC5w1#{9d{#5)AW!r3T-P z_-1)?vy|j~Vw(yx#+M0rwt*^?SZi^a?HjK@$+1{9(GUI}wX?5;skN|^9|so3*Z z2W8`~cOQK%Kk9;oC*8$ceI9M^-+(j1QEg5`*?e+U_zol_D>5$-cu4bCzCX;;Mm-VV z02lnUsRz4HC-=zD2jhrpuU!d>7KSn9x9Hy8S9g15nw=(jqf=4cbEQS0HlhQDpM~8e za{o+F!c-dlfI}+7Mt7RN=g|8lrU>IVUKFBp{hlohOPN6_eV{ch)mJ#0E}Jg9$gsAF zvoOrJSLB=g=gLoxO)L-UN)<%9QiGH`U*nO}!J3vk%Bc2n9~^kf=St?i9v=7FDJtW| z3_QIq>?NpuNI=dan{G&y$bi=S0?I!m#8ny>x|FRqzc0@F&{BpSiTacLiMlr&RAF7S zU;(CBU)Y{y`dv;Sc4p|GU5Mw{_WeNf>mmNz%IVVvJ*fe@C`MI?#9!`VDb6#C(@ixh z$dCKTre2@iip28HPaZSkK8u~$l6g_2uP<_Eq)waEnL|ny(3%)+IpJr+_z=M+X5z=e z^n9b})K)bl<|2w?Lur3Cm%Nzus)6TYj4~Lne{52QR{uD#p39`z^6h|BY*XQ=ERcDi z42gn?miJp!-cv=La4wudPiwA9{e+N`M%L1s@lhRQv4pgADj2bS=d&sEUmpf3lUy_D zIR=2zk!KalBQOtd9JKz`^$iV z0_y2+{C--0dt*yx2NK!2LGeEZ9^0TqU$V^5O7u5;3PI{gg2;os4J8q)lL%#O`|`yv z2$rbgQQru%kEidG*tLS0`-2yJw3W(pJ93spw#%EQimhI9BJkpmq_KDB1SUrnA9FJ? zrQ4E*h*9GirzxgKwrlA{$%u-sxcT6DuQ45(MQ45ED|QaaBapZq`^6=J3E5xm3QpX= zI-q|e&Z#Dbt7yayha8k4UIl1tYv=kO4N2^03X|F@cFS5xca=}>2pLUQ%QUw=aA};e z0a9acM&h(*en1jFUdo!0(HBYnCO7xGxovu7%=OXv3$oQsBHUuWVxH;Ha^(RA~KeMfC)zxkWOSp+4mUoi2= zvU@hp?Cs69jz7kUlL0L%8;(>H3^qm2v~N9|$ut;L>X7uy{WbEdP!?VyNNp0J^sMvx z`dZ$qumAh1w=%hX+~NecC1_I~CG6LX2Q$$1YA{!bmehaK`c@|33wMQ9;V3)&GRYa* z6W_6aXIUZ~Bo$~|{5^m$gX#5U&m?&?gjF{5{6Mcb9HBEKw;WnX@*LYx)5~3qp|PtK zYVf>b95HLp-@GZ6>wA8!x8R&ojTlg!r6XfFlf0Fu>_S+Wt_QT&U$bl7W{m$dRye>X zH2#Uyu+}ikSHZ#qv2st;eCYlx_x^PUR<+=c=HPdpV)@*V)#Kx1g9E75mN3HWlb^z0 z=*n({yKMJS+rcMvFaR6yp;s&mZhqATrKXJGQG(Bn$?25iIHEd`en|qq6iJ|N zwe&NP2_PhxI=k_#>$0Pvm`x~7v*y_O!-LdjT)E}kV4`dwJK@D8Yzz_811-|BaIBlm zFMcxOVeYfLIep(2e0!f-p<61}BFsvtaHoaGP_sMvg%(ceE9vU!dh+e(NSueHh)1HL zADx1O8PIu3;cOm#Hj&sdV_+a2B@ju@2}*=KcZptjQIys(?~}VfsC^5%Y!5!!J}sTx zTH>vo^V~P;J6NpRdO9PYWk}RlgVFswC`B-`*!h8^MN4Kq_U{*IQ6m@GL0NGJ6!y~S z3DFp-p9%z|=OUi7u*DSA<%Ivh=rLe!Q)9qWZ;BO9|1#29@qpft0GI-DAws*OI#Ks`$*=5Z*i7?mX-~ zq@gprf>6+Viic5`YP!F{3qLuKokKlbL^ceOiDb;u$A1ml9i-_f^2RLrAM8dA3_u=0 za}P^KEl}|>E0pJSLQE&6YOfq6C&HSJ^`Bq#Ire&ohu_Xquo#22WADbORA*lj>?&h( zd_{Uj7~5n(1hbwni}S7bs(6^@%}=Xj%%CP4*iO8 zaU)HkU!GB4;GIWA&j}WqEl+Eme*hQ)-sr2%d^JQh>RRsm3R=d~$<>ueeg{t@KU3Bl zNw}rAzP>IifW2$fKV}lNaIcry>-UKo7o4rLMshihqXUeZR_{Lz9yx-Ym4%8(Wi#NF zvOE>Y>>XiZZ7Ppqi?A@W1u3~KbO5``;(oxxdh41IX|(9HYlC?#IqQ5T=oOtm85=&r zkRRw~@e>){w-5jBvM)A&yeheF zt`3oB;vnm^zPp7obt&3w^h*?9dWZ2OKbQqat7HzMa{kcvNGe??%G30Sh<@$vBk1`EfcjGUAmmmBbw` zmM&(?tFOV4uzLdQY>RlsBO&`Itzvxp=etfIEw*nkZ+D!mwn};EV{5O?b^}bg6BQK| zS@H3Ya+8WvbIM!J=&#fDE=EH>xig#wF;hK7fUDR;vBz$IP3iQpx3|YfoddRdzKN0S ze7;0mMtIJ(flNF{XG#T*XG2bmzg=xd(S7&{yjh1gFF0~kDI`1HQS|-a;gtB|L8m@k zx_e#i$FzZG5dYwpWVWV*A-m;4&>TgHaHzYeGU!mdP6UZT$OEgZ(0Q+tvN}6T&2e ztB8|8@q3};CFj4LJeH( zF_Z3s1dtn+_4X0Y=2r$?45nTV=NZXv?Uz)4Fs~P`C)w`77ejyWJIm>%_nM@t7`8}j zo`r$3x3jEQ!D|ob_f9T^l2CGYzQ)Hag`@nJByzQ^nv* zm~~J-yB?7E5X3~m&@DTxS$l=d`)NO7V;z#*G7#>RV*A84x(`XiX7>sXem9zc$7+gJ z#FB{RrEy_0gT%#pjBtcTs$&Edzp&RrI%P#`7rO`KAc}L*g|EbL)Ms}oR1uDm z)WNj>SwOLvnpnOP1tNB2;{fTKP2(nk25|&9;s3LXy-2L>2ltZ1Ac`ZU%;iAS-d{wW ziBmA@f}j-JuLyDhjxHE|KLo(e{cSnA>N*LN^6}K+oHRJW8Bf1A(-3P`Rzf3kl<&og zK{|~|{Y%;Zl_AXdS4JGCNdQF(6}M%J01>OQlY$O5r^!r!?j@mJby$RnJ&&;nP2It@ z*uWB4@lv_~#RmWLenIO0b)OT?i%b6>cb@{okfaa`&rr0heMO;2&qyy_IfaWo%T)O6 zeIyp8pp5Y=+vz1FXpoXe&?zmlQ9vkn*Oz^&YTG3`pUo1I6OO~u*2kbAJX6N{3*}KXXfXK6@zlwEsdA-& z(nq;)3;|9%IQ=l!{)bG$Fb4dlW$)S7}fZu3pWj3>cb-9r4YC^aPBN*^zFWEM}l6QJSzLH)y=9zs^bVSF}uQl~s<`>kx z2@J~!HKg1`vPNMrAuWDW5l%u>geOTfWF{KoZLuNtFrm&$7Jq zp<-QxZP{H>CX^4DYMB;V;^XwRvT6gBk*k5%@tltgppw6(ic) zGD=%dz*alzm;v_N3%Xb*lFS!is?#-x@PsMi`;SZ9u+qttoB5Yf>qR5&FQ`57i#7-d zC$O4LP;-JY@vKL+i-SLi=MsyP2^=d)KFZ_?AE)wdk`9aquD>VoQnh+CE6Co(5Mf5% zi|z{T{zjj{lGk5^Dj4g5VfmQ}o@1VqgV#g&hr6i)X+>{5`*5DSaLTNwulzuum29FsI`A-wIl-X z597N??dh~pqebW4n0L&s${C~2nYtdioW~slM}^~ih}gDhwNL*wk6q(01~pUP)MNO& zg_UgD_$Nzi0$V;PU)|KS8eU+Ur0M%Ev2yFhsN*-Qd=D?x`#Z|Qzh&3nJ-ltY}1tI^mn<(8Z2+j^ITHsM?*oMQHD?F;vlOJ51Awh$gI|7`$P$I%c1l)Dxcrdak^wd6NUM%i zIKDm}5er>mh`oNC$MF38hVKF{getA=^t2iJH~b!WAxZP^`_5c8np+irBb-gYWo{=X zq0;T%s?aO?$|nFlKjD#k{@iae7NW~mUSdM zJ)Er%V}q;&qE;&eci!`^VE&$FJWDFwH$bnheX1ocN~7=PMvvY~Zl`72Zn3%#zb+i- z`F&mbA`Nm6`tHZ4MSF}c8l0K#mo`@BlW?8-V#mM zxyys|gODT|A_$qg;!%jR(MRr4M3ysri)8Z;-GS{Qf#_$OHIN9b_kCSFZVg}iv_TVBeg z=vtE0{6e}erPW6D!|~*Ens4CkL&xg$y2{t=Rpepvh2pWQ42d0N-)l)mUBo}}SBR)u zjI?|6adq)8ud3`0CwiaO2m|tfntxh=dW?>j#w*%J4b98l;iHZslOxIoEulf_pWnh> z`L`v^Zi^V#KU&a=Ib(wT}^amKuj# zUI%-QcPoOwDn>r3#jX5lO~U3Y3_aOCdbiRoJEZz~Nc~B#!Ptps@V!U&sXGqNRvksh zMii656txm3B$So8&*n>24OARw+@Q1uOQ+C8JMWNJZCs8QEd!>%UtV)iIR?nvJ&PxNu2F#z*Kv{#g-VU(#N zU$0c?Q$#AMrSbzQEp&0B@vRu{T?#9tI zK(pWB^^4TnApJgNB@w5%nlMNSS=%e53P^(h~`z3+0xV$v>jfi|CcfP0`#dcN8c6MLM$Lbo;Vd zJ($uQieuX(GxYbyA_4)MBZH^T@i0ojE@zp44!_yn+S+;)T>JIwSJmN*{8XAgLBfB4 zbhR=6nMdOcRfTEQ<=pUFnSFIvS(gRdthaC5)rt+XjBna^s#|;J+e5s#koB(_qAX{h zKq|NY_OZScYF}_k;#slsXfUntc){fSc12Q#F{yX{{W`Y?g1TXyuecEa*?r~9gjIs( zQ@Im%CTkS@gM)wnyvqG5Ig0KoA9rQsEzaRBZk5^~g{i0Xw7u8C{0C?_@6OOCzgt~bIU5%1Uu%ZWb$>a>J*SvmHDF`-g zw|-d)S$>U7p(6cy6{5M72n=at8O`PKk8SZw8&!LAa}RcnGQ5-@zWChBtZWJbp!QP= zC5Yu@(Ps%F=rDlFT4gMB(ON=i;lYy!tZL^ES1@3@h&#wT*|wWwFNWfx6={-UwxZL%<> z;P?q32ocf@X3_}3&^augVA~gZ5qWVUbKkwHnsT7$=rT0&zFA#e{pJ(D{D#ofRb03_ z7hNPzDX4`|Q61XAhzdM68q#fgS3d=3Q|5pgh%en)=#kz{-u z4lRUVif{P6;~?OkN$Xs=NZ<ffv3TAnqmbVo&pZ;UvKh!gu zKMI7XU`%~J!_UMRVpmWc%7_z<;fo5pb|X!_7fb}}9Gr;&e;o(pv9rw1O!SnMSH8mm zSY|boJIN%7MR(1ukDNgZ>p?6j>96wZ<>08=&s@khEB^zg;fvTc^U{f(6JI1msLHhB z!|v~f#AMkx7U4{dUJF;2%6}q^rBV-j`(-FB?jsl~ly>x2a^xtS&Y9^6MP;Rjy`^@q z0W0AI7^G!lhq{r2f@u1}hc8;1sNQgQZU4c!1LQ9Fn+_0ApIDAg+>>p2%+v}4327#m zlv(Qh&uoxZ`=sC85nu8C7L%&qH_cqBZ|wb6N4EEOJ_7h=QChgk@H8PHy>QO``(6VB zh%>eT;ZFapT%PYOZc)v+$IPdnoal)e0QuKv;H zy;;eCU`5dVzn;*P&_j<4Dsu?rU z{yWuAHMf$VW)_O`D3gm0HB{SUUXW*O+h&XVJcB&f$0h^@{*~^E7S^9n1*=}nRgl{Y zEm6mmqiP@WW*T0K`MscO{M^3ntcp?mO9Wkn6M0t1)hbu+;r7;!v(!9usgQzXE+A*i*meHzZ=)~EL zS!aC@^J){=G+SKw0ryI2GH5W>mI7AIgS6zO#rbocrv{dKa(ipKaHc>Rfb)wTo+IvI zUu8NNb&IF;;{@qG+f>^Dpiw#jSj4T8cu58C`qdHZeYQsqL|hIm(JXRLA@w zG_6U^Y%s6-xXeK6@}sp@OJlt!vJF79I+K*9YUO9o!r*k?>1W(qCtGNb@bK`Vjz4WG70(e@h zN3M4!?P7$0_xh{?{<}TA7Qe@>tRQY227$Fro`uDIX1e(H#x_iQd&PmT*qaEajHmPC z3C_aV(o@cf*N5i&nJwVUw+T8Zy#6=*99;}pZ5c-n3Q!QbNU_V)pwHlKgvyi#SL&k3 zeR3M;`!96FUhlT}NC3&tE3>y<4wUN|e3V@x$*yRH+w+FR#Kg~$QR{~NXm-j z=hUY=BqK*-yJhgBH|$+F#rJ~gz))J>VE`oB{wGx$`|<@A=-1H?(&Aq@lIAIZ^emY4 z7wKyDg4{H+W7xo*4tTu|)1gVyd1C3+>n>Qctk0Y#olr`U{coz=aKnxk2|^xj?uoh3 zqaoX;kAp=x;ru$do7iBw*rI1|J`ix#N@>5gZLYqu{Lzl(-8$zFxNhd)Rl3A|By%r6!o-HSv^` zj&i~;eUi#bH6LTru;brKJ6M9W2FxTn+W}p{%KCYvxRGhg+>CYFD8Qnf>BT?=@&1{v zM`@!66IKvVt)Vk2-qsIy7TR00LX8NlgX;mVA(=H=@bax*T6--Dq{YLl@qSo6s4A94 z@Q9{>W-$-gJ6!J_%26OU6@=r{MO_sGV0Dfq1)E&dz(Kd!kKDrB{%IoB-GOKe8c}G= zQfWq@1YwACZD`>F9a~qoTRs!xl7th$+0EYCa*^bgOf?LS-w3Kn63wV5 zq+#+M+wp7iBe13j9L~0}gtXFsqiOE3*p#b!g)gN4=cF-IaP1ol}olia0=NNl&|uiBYkc z&B)v-Gy;RX+4YyX4(z#c%ipe}v(ViVn6!VDqAoiT1)#j4fC|t97Drr^YUc01HWx^u>GB6&2`m(e zNlPJguBYgGa>0&QrNBXL|GuW6S16FyGrj2t>4An zVDDAR!qY~poUA3+4AOGC?{nxq9#GmVx0(6mk#oMndIy%OwSG1q%8mSk5F^O(rQ7Y=2#|(AM@S1 zwR&bQ5TtbQt|^#k9>9O?C+hf9@w!I)a29yn8)ww?HNDnyyYCmlFKUcuT&OBZHaPqc z>ErNG(-wo32)zhFLBYzm+1V{O>p5Qdg>*Z-<(Z*eyBFVFb%AJng+VPfe;}SA>e_Sz zAKJ52gqGgMLP~K8Pt+5dQij!gaOfZ$XJR=AvOe_`>VQq)J3)DudwZ$;4d$et-}TN0 zvhbyM{t+m+1Z1-=f2$)^0rU&W+lupqsbA--|(`|KRkS(986hRner(_k46~oFQ-v*kFxTqw{|)8u`O5hQ;}Uj zjgH^PX`zHj=gcN-Fd5ST{-t1Xjsb}}GD5p4tKI=ax#u~uo?`15T|XBBAd^dJj%9_v zpGO<;$-&ok<+LK>j;iB|8umW;F4yNc&XulY7qTp+y|qnue4D1)F(85lniw5*c4UW_ z0O_>3p4;9pS6V_-^M3RXPkI{Fv{e4YC4D1vTiiM%hSp%~G_{z0??usSDai`&c5t#e zxvC|{yAK1lT7|vBh$qZ$>4ub*>k7H7$|%($Z6yF_l=52 zUP4nr!nQAfQ#{e7NKhxzNsR^x@T4{Md5X`<`PF^3`G;gnZMiOZ@^Y;K60v^(xk=Nz z`KCN!Z>?N3#}H<6Z+1;|#x1E%H^9SiTEkKOoEhI|QegJO*&Mhw8 zVaNAwa*rNtnItkwds1e|l9gnpB06Yv3`osqRa1rJGP*3uRYf-2C*mL2s9FfDhiZh7+HnRLMtUjw%iTL5}XL1pQDzqaC_3n!ggiwodUuicglo0v}h(UK7 z@MB(eDeY_R?2CtuwNIpt9v!Dd)?YqyZ~e4d=I++MDef~B#A0?~NF})P3=}>zI1)*0Zy~A*wC<@x|=-Sf5$Dq}s=VRXa~>rvP+wT@s%;Jtt-X5D^{OPsKhF zP<_G^MdC+4p#};P`eQDXsPi2b%l#KUN+tNq8v%pBEOGDhiVyL=lJ5iAnX%>;-*W=3 zcM9UIGWt!ya%_p3Sal4s49dAaDv&JwCrNIC>!JPOB&yfi-DSPFM>xBVc8eJ!dVJ#mjp zFk)PJ%PGqHu93vC|45R|0zrqTjI3kW$?c&}JkG_6pO>Fd5)2DkT62TrmXd!KJd9pZ z_fREmmp?3xQk%Y);lfm=p@u_RLx(8FjFJ>IS7>7m&+A0|&1n7XmU%Q2dWKb1*P0!VGF{IQaj(ssq6e0obi+hiI<{{`alrfL8H literal 0 HcmV?d00001 diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb new file mode 100644 index 0000000000..07c2d6e575 --- /dev/null +++ b/docs/manual/pointers.ipynb @@ -0,0 +1,756 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Unsafe pointers\n", + "description: Using unsafe pointers to access dynamically-allocated memory.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type \n", + "creates an indirect reference to a location in memory.\n", + "You can use an `UnsafePointer` to dynamically allocate and free memory, or to\n", + "point to memory allocated by some other piece of code. You can use these\n", + "pointers to write code that interacts with low-level interfaces, to interface\n", + "with other programming languages, or to build certain kinds of data structures.\n", + "But as the name suggests, they're inherently _unsafe_. For example, when using\n", + "unsafe pointers, you're responsible for ensuring that memory gets allocated and\n", + "freed correctly.\n", + "\n", + ":::note \n", + "\n", + "In addition to unsafe pointers, Mojo supports a safe \n", + "[`Reference`](/mojo/stdlib/memory/reference/Reference) type. See\n", + "[`UnsafePointer` and `Reference`](#unsafepointer-and-reference) for a brief\n", + "comparison of the types.\n", + "\n", + ":::\n", + "\n", + "## What is a pointer?\n", + "\n", + "An `UnsafePointer` is a type that holds an address to memory. You can store\n", + "and retrieve values in that memory. The `UnsafePointer` type is _generic_—it can \n", + "point to any type of value, and the value type is specified as a parameter. The\n", + "value pointed to by a pointer is sometimes called a _pointee_." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, initialize_pointee_move\n", + "\n", + "# Allocate memory to hold a value\n", + "var ptr = UnsafePointer[Int].alloc(1)\n", + "# Initialize the allocated memory\n", + "initialize_pointee_copy(ptr, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + "\n", + " ![](./images/pointer-diagram.png#light)\n", + " ![](./images/pointer-diagram-dark.png#dark)\n", + "\n", + "
Figure 1. Pointer and pointee
\n", + "
\n", + "\n", + "\n", + "Accessing the memory—to retrieve or update a value—is called \n", + "_dereferencing_ the pointer. You can dereference a pointer by following the\n", + "variable name with an empty pair of square brackets:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "110\n" + ] + } + ], + "source": [ + "# Update an initialized value\n", + "ptr[] += 10\n", + "# Access an initialized value\n", + "print(ptr[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also allocate memory to hold multiple values to build array-like\n", + "structures. For details, see \n", + "[Storing multiple values](#storing-multiple-values)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Lifecycle of a pointer\n", + "\n", + "At any given time, a pointer can be in one of several states:\n", + "\n", + "- Uninitialized. Just like any variable, a variable of type `UnsafePointer` can\n", + " be declared but uninitialized.\n", + "\n", + " \n", + " ```mojo\n", + " var ptr: UnsafePointer[Int]\n", + " ```\n", + "\n", + "- Null. A null pointer has an address of 0, indicating an invalid pointer.\n", + "\n", + " ```mojo\n", + " ptr = UnsafePointer[Int]()\n", + " ```\n", + "\n", + "- Pointing to allocated, uninitialized memory. The `alloc()` static method\n", + " returns a pointer to a newly-allocated block of memory with space for the \n", + " specified number of elements of the pointee's type.\n", + "\n", + " ```mojo\n", + " ptr = UnsafePointer[Int].alloc(1)\n", + " ```\n", + " Trying to dereference a pointer to uninitialized memory results in undefined \n", + " behavior. \n", + "\n", + "- Pointing to initialized memory. You can initialize an allocated, uninitialized\n", + " pointer by moving or copying an existing value into the memory. Or you can use\n", + " the `address_of()` static method to get a pointer to an existing value. \n", + "\n", + " ```mojo\n", + " initialize_pointee_copy(ptr, value)\n", + " # or\n", + " initalize_pointee_move(ptr, value^)\n", + " # or \n", + " ptr = UnsafePointer[Int].address_of(value)\n", + " ```\n", + " \n", + " Once the value is initialized, you can read or mutate it using the dereference\n", + " syntax: \n", + "\n", + " ```mojo\n", + " oldValue = ptr[]\n", + " ptr[] = newValue\n", + " ```\n", + "\n", + "- Dangling. When you free the pointer's allocated memory, you're left with a \n", + " _dangling pointer_. The address still points to its previous location, but the\n", + " memory is no longer allocated to this pointer. Trying to dereference the\n", + " pointer, or calling any method that would access the memory location results\n", + " in undefined behavior.\n", + "\n", + " ```mojo\n", + " ptr.free()\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "The following diagram shows the lifecycle of an `UnsafePointer`:\n", + "\n", + "
\n", + "\n", + " ![](./images/pointer-lifecycle.png#light)\n", + " ![](./images/pointer-lifecycle-dark.png#dark)\n", + "\n", + "
Figure 2. Lifecycle of an UnsafePointer
\n", + "
\n", + "\n", + "### Allocating memory\n", + "\n", + "Use the static `alloc()` method to allocate memory. The method returns a new\n", + "pointer pointing to the requested memory. You can allocate space for one or \n", + "more values of the pointee's type.\n", + "\n", + "```mojo\n", + "ptr = UnsafePointer[Int].alloc(10) # Allocate space for 10 Int values\n", + "```\n", + "\n", + "The allocated space is _uninitialized_—like a variable that's been declared but\n", + "not initialized.\n", + "\n", + "### Initializing the pointee\n", + "\n", + "The `unsafe_pointer` module includes a number of free functions for working with\n", + "the `UnsafePointer` type. To initialize allocated memory, you can use the \n", + "[`initialize_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy)\n", + "or [`initialize_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move)\n", + "functions:\n", + "\n", + "```mojo\n", + "initialize_pointee_copy(ptr, 5)\n", + "```\n", + "\n", + "To move a value into the pointer's memory location, use\n", + "`initialize_pointee_move()`:\n", + "\n", + "```mojo\n", + "initialize_pointee_move(str_ptr, my_string^)\n", + "```\n", + "\n", + "Note that to move the value, you usually need to add the transfer operator\n", + "(`^`), unless the value is a [trivial\n", + "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", + "`Int`) or a newly-constructed, \"owned\" value:\n", + "\n", + "```mojo\n", + "initialize_pointee_move(str_ptr, str(\"Owned string\"))\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternately, you can get a pointer to an existing value using the static \n", + "`address_of()` method. This is useful for getting a pointer to a value on the \n", + "stack, for example." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "var counter: Int = 5\n", + "ptr = UnsafePointer[Int].address_of(counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that when calling `address_of()`, you don't need to allocate memory ahead\n", + "of time, since you're pointing to an existing value." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "#### Initializing from an address\n", + "\n", + "When exchanging data with other programming languages, you may need to construct\n", + "an `UnsafePointer` from an address. For example, if you're working with a\n", + "pointer allocated by a C or C++ library, or a Python object that implements the\n", + "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n", + "you can construct an `UnsafePointer` to access the data from the Mojo side.\n", + "\n", + "You can construct an `UnsafePointer` from an integer address using the `address`\n", + "keyword argument. For example, the following code creates a NumPy array and then\n", + "accesses the data using a Mojo pointer:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, 3, 4, 5, 6, 7, 8, 9, " + ] + } + ], + "source": [ + "from python import Python\n", + "from memory.unsafe_pointer import UnsafePointer\n", + "\n", + "def share_array():\n", + " np = Python.import_module(\"numpy\")\n", + " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", + " addr = int(arr.__array_interface__[\"data\"][0])\n", + " ptr = UnsafePointer[Int64](address=addr)\n", + " for i in range(9):\n", + " print(ptr[i], end=\", \")\n", + "\n", + "share_array()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When dealing with memory allocated elsewhere, you need to be aware of who's\n", + "responsible for freeing the memory. Freeing memory allocated elsewhere \n", + "can result in undefined behavior.\n", + "\n", + "You also need to be aware of the format of the data stored in memory, including\n", + "data types and byte order. For more information, see \n", + "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Dereferencing pointers\n", + "\n", + "Use the `[]` dereference operator to access the value stored at a pointer (the\n", + " \"pointee\").\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "110\n" + ] + } + ], + "source": [ + "# Read from pointee\n", + "print(ptr[])\n", + "# mutate pointee\n", + "ptr[] = 0\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "If you've allocated space for multiple values, you can use subscript syntax to\n", + "access the values, as if they were an array, like `ptr[3]`. The empty subscript\n", + "`[]` has the same meaning as `[0]`.\n", + "\n", + ":::caution\n", + "\n", + "The dereference operator assumes that the memory being dereferenced is \n", + "initialized. Dereferencing uninitialized memory results in undefined behavior.\n", + "\n", + ":::\n", + "\n", + "You cannot safely use the dereference operator on uninitialized memory, even to\n", + "_initialize_ a pointee. This is because assigning to a dereferenced pointer\n", + "calls lifecycle methods on the existing pointee (such as the destructor, move\n", + "constructor or copy constructor).\n", + "\n", + "```mojo\n", + "str_ptr = UnsafePointer[String].alloc(1)\n", + "# str_ptr[] = \"Testing\" # Undefined behavior!\n", + "initialize_pointee_move(str_ptr, \"Testing\")\n", + "str_ptr[] += \" pointers\" # Works now\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Destroying or removing values\n", + "\n", + "The \n", + "[`move_from_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee)\n", + "function moves the pointee from the memory location pointed to by `ptr`. This is\n", + "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n", + "the memory location uninitialized.\n", + "\n", + "The [`destroy_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", + "function calls the destructor on the pointee, and leaves the memory location\n", + "pointed to by `ptr` uninitialized. \n", + "\n", + "Both `move_from_pointee()` and `destroy_pointee()` require that the pointer is \n", + "non-null, and the memory location contains a valid, initialized value of the \n", + "pointee's type; otherwise the function results in undefined behavior.\n", + "\n", + "The [`move_pointee(src, dst)`](/mojo/stdlib/memory/unsafe_pointer/move_pointee)\n", + "function moves the pointee from one pointer location to another. Both pointers\n", + "must be non-null. The source location must contain a valid, initialized value of \n", + "the pointee's type, and is left uninitialized after the call. The destination \n", + "location is assumed to be uninitialized—if it contains a valid value, that\n", + "value's destructor is not run. The value from the source location is moved to\n", + "the destination location as a consuming move. This function also has undefined\n", + "behavior if any of its prerequisites is not met." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Freeing memory\n", + "\n", + "Calling [`free()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#free) on a \n", + "pointer frees the memory allocated by the pointer. It doesn't call the \n", + "destructors on any values stored in the memory—you need to do that explicitly\n", + "(for example, using\n", + "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) or\n", + "one of the other functions described in \n", + "[Destroying or removing values](#destroying-or-removing-values)).\n", + "\n", + "Disposing of a pointer without freeing the associated memory can result in a\n", + "memory leak—where your program keeps taking more and more memory, because not\n", + "all allocated memory is being freed.\n", + "\n", + "On the other hand, if you have multiple copies of a pointer accessing the same\n", + "memory, you need to make sure you only call `free()` on one of them. Freeing the\n", + "same memory twice is also an error.\n", + "\n", + "After freeing a pointer's memory, you're left with a dangling pointer—its\n", + "address still points to the freed memory. Any attempt to access the memory,\n", + "like dereferencing the pointer results in undefined behavior.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Storing multiple values\n", + "\n", + "As mentioned in [Allocating memory](#allocating-memory), you can use an \n", + "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n", + "in as single, contiguous block. Pointers support arithmetic: adding an integer\n", + "to a pointer returns a new pointer offset by the specified number of values from\n", + "the original pointer:\n", + "\n", + "```mojo\n", + "third_ptr = first_ptr + 2\n", + "```\n", + "\n", + "Pointers also support subtraction, as well as in-place addition and subtraction:\n", + "\n", + "```mojo\n", + "# Advance the pointer one element:\n", + "ptr += 1\n", + "```\n", + "\n", + "
\n", + "\n", + " ![](./images/pointer-offset.png#light)\n", + " ![](./images/pointer-offset-dark.png#dark)\n", + "\n", + "
Figure 3. Pointer arithmetic
\n", + "
\n", + "\n", + "For example, the following example allocates memory to store 6 `Float64`\n", + "values, and initializes them all to zero." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "float_ptr = UnsafePointer[Float64].alloc(6)\n", + "for offset in range(6):\n", + " initialize_pointee_copy(float_ptr+offset, 0.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once the values are initialized, you can access them using subscript syntax:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0, 0.0, 3.0, 0.0, 0.0, 0.0, " + ] + } + ], + "source": [ + "float_ptr[2] = 3.0\n", + "for offset in range(6):\n", + " print(float_ptr[offset], end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Converting data: bitcasting and byte order\n", + "\n", + "Bitcasting a pointer returns a new pointer that has the same memory location,\n", + "but a new data type. This can be useful if you need to access different types of\n", + "data from a single area of memory. This can happen when you're reading binary\n", + "files, like image files, or receiving data over the network.\n", + "\n", + "The following sample processes a format that consists of chunks of data,\n", + "where each chunk contains a variable number of 32-bit integers.\n", + "Each chunk begins with an 8-bit integer that identifies the number of values\n", + "in the chunk." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def read_chunks(owned ptr: UnsafePointer[UInt8]) -> List[List[UInt32]]:\n", + " chunks = List[List[UInt32]]()\n", + " # A chunk size of 0 indicates the end of the data\n", + " chunk_size = int(ptr[])\n", + " while (chunk_size > 0):\n", + " # Skip the 1 byte chunk_size and get a pointer to the first\n", + " # UInt32 in the chunk\n", + " ui32_ptr = (ptr + 1).bitcast[UInt32]()\n", + " chunk = List[UInt32](capacity=chunk_size)\n", + " for i in range(chunk_size):\n", + " chunk.append(ui32_ptr[i])\n", + " chunks.append(chunk)\n", + " # Move our pointer to the next byte after the current chunk\n", + " ptr += (1 + 4 * chunk_size)\n", + " # Read the size of the next chunk\n", + " chunk_size = int(ptr[])\n", + " return chunks" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When dealing with data read in from a file or from the network, you may also\n", + "need to deal with byte order. Most systems use little-endian byte order (also\n", + "called least-signficicant byte, or LSB) where the least-significant byte in a\n", + "multibyte value comes first. For example, the number 1001 can be represented in\n", + "hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a\n", + "16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit \n", + "integer, it would be represented as E9 03 00 00. \n", + "\n", + "Big-endian or most-significant byte (MSB) ordering is the opposite: in the \n", + "32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and\n", + "when transmitting data over the network. You can use the \n", + "[`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap) function to swap the byte\n", + "order of a SIMD value from big-endian to little-endian or the reverse. For\n", + "example, if the method above was reading big-endian data, you'd just need to\n", + "change a single line:\n", + "\n", + "```mojo\n", + "chunk.append(byte_swap(ui32_ptr[i]))\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `DTypePointer`: handling numeric data\n", + "\n", + "The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", + "pointer that supports some additional methods for loading and storing numeric\n", + "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n", + "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n", + "[SIMD and DType](/mojo/manual/types#simd-and-dtype).\n", + "\n", + "`DTypePointer` has a similar API to `UnsafePointer`:\n", + "\n", + "- You can [`alloc()`](/mojo/stdlib/memory/unsafe/DTypePointer#alloc) and\n", + " [`free()`](/mojo/stdlib/memory/unsafe/DTypePointer#free) memory, or use \n", + " [`address_of()`](/mojo/stdlib/memory/unsafe/DTypePointer#address_of) to point\n", + " to an existing value.\n", + "- The pointer supports pointer arithmetic to access adjacent memory locations.\n", + "- You can dereference a `DTypePointer` using subscript notation.\n", + "- You can construct a `DTypePointer` from an `Int` address.\n", + "\n", + "\n", + "You can also construct a `DTypePointer` from an `UnsafePointer` of a scalar\n", + "type like `Int64` or `Float32`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from memory import DTypePointer, UnsafePointer\n", + "\n", + "uptr = UnsafePointer[Float64].alloc(10)\n", + "dptr = DTypePointer(uptr)\n", + "# Or:\n", + "dptr = DTypePointer[DType.float64].alloc(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unlike `UnsafePointer`, `DTypePointer` doesn't have special methods to\n", + "initialize values, destroy them, or move them out. Because all of the values\n", + "that `DTypePointer` works with are trivial types, `DTypePointer` doesn't need to\n", + "destroy values before overwriting them or freeing memory. Instead, you can use\n", + "subscript notation (like `UnsafePointer`) or use the\n", + "[`load()`](/mojo/stdlib/memory/unsafe/DTypePointer#load) and \n", + "[`store()`](/mojo/stdlib/memory/unsafe/DTypePointer#store) methods to access \n", + "values.\n", + "\n", + "What `DTypePointer` adds is various methods of loading and storing SIMD values\n", + "to memory. In particular: strided load/store and gather/scatter.\n", + "\n", + "Strided load loads values from memory into a SIMD vector using an offset (the\n", + "\"stride\") between successive memory addresses. This can be useful for\n", + "extracting rows or columns from tabular data, or for extracting individual\n", + "values from structured data. For example, consider the data for an RGB image,\n", + "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n", + "you want to access just the red values, you can use a strided load or store.\n", + "\n", + "
\n", + "\n", + " ![](./images/strided-load-storage.png#light)\n", + " ![](./images/strided-load-storage-dark.png#dark)\n", + "\n", + "
Figure 4. Strided load
\n", + "
\n", + "\n", + "The following function uses the \n", + "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_load)\n", + "and \n", + "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n", + "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", + "that this function only handles images with where the number of pixels is evenly\n", + "divisible by eight.)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def invert_red_channel(ptr: DTypePointer[DType.uint8], pixel_count: Int):\n", + " # number of values loaded or stored at a time\n", + " alias simd_width = 8\n", + " # bytes per pixel, which is also the stride size\n", + " bpp = 3\n", + " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", + " red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n", + " # Invert values and store them in their original locations\n", + " ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note Future of `DTypePointer`\n", + "\n", + "The `DTypePointer` type exists for historical reasons, but it no longer really\n", + "needs to be a separate type. `UnsafePointer` can handle most things that\n", + "`DTypePointer` does except for a few features related to reading and writing\n", + "`SIMD` values. At some point in the future, these features will probably be\n", + "integrated into the `SIMD` type, so you can use them with `UnsafePointer`.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Safety\n", + "\n", + "Unsafe pointers are unsafe for several reasons:\n", + "\n", + "- Memory management is up to the user. You need to manually allocate\n", + " and free memory, and be aware of when other APIs are allocating or freeing\n", + " memory for you.\n", + "\n", + "- `UnsafePointer` and `DTypePointer` values are _nullable_—that is, the pointer\n", + " is not guaranteed to point to anything. And even when a pointer points to\n", + " allocated memory, that memory may not be _initialized_.\n", + "\n", + "- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`.\n", + " When you use an `UnsafePointer`, managing memory and knowing when to destroy\n", + " objects is your responsibility. (Since `DTypePointer` only works with trivial\n", + " types, this is not typically an issue for `DTypePointer`.)\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `UnsafePointer` and `Reference`\n", + "\n", + "The [`Reference`](/mojo/stdlib/memory/reference/Reference) type is essentially a \n", + "safe pointer type. Like a pointer, you can derferences a `Reference` using the \n", + "dereference operator, `[]`. However, the `Reference` type has several\n", + "differences from `UnsafePointer` which make it safer:\n", + "\n", + "- A `Reference` is _non-nullable_. A reference always points to something.\n", + "- You can't allocate or free memory using a `Reference`—only point to an\n", + " existing value.\n", + "- A `Reference` only refers to a single value. You can't do pointer arithmetic\n", + " with a `Reference`.\n", + "- A `Reference` has an associated _lifetime_, which connects it back to an\n", + " original, owned value. The lifetime ensures that the value won't be destroyed\n", + " while the reference exists.\n", + "\n", + "The `Reference` type shouldn't be confused with the immutable and mutable\n", + "references used with the `borrowed` and `inout` argument conventions. Those\n", + "references do not require explicit dereferencing, unlike a `Reference` or \n", + "`UnsafePointer`." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From a34ce2f2401c782de1ad52fddacfd68b656f4985 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 15:29:43 -0400 Subject: [PATCH 0854/2019] [stdlib] Add a test for `SIMD.fma` A specific test about behavior in a parameter `for`. MODULAR_ORIG_COMMIT_REV_ID: 2a764131c33e20e242a89b00888166bc061c0afd --- stdlib/test/builtin/test_simd.mojo | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 1c5ff42780..6442b58a30 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -14,6 +14,7 @@ from sys import has_neon from utils.numerics import isfinite, isinf, isnan, nan +from utils.static_tuple import StaticTuple from testing import assert_equal, assert_not_equal, assert_true, assert_false @@ -132,6 +133,57 @@ def test_issue_20421(): a.free() +def test_issue_30237(): + alias dtype = DType.float32 + alias simd_width = 1 + alias coefficients_len = 7 + alias coefficients = StaticTuple[SIMD[dtype, simd_width], coefficients_len]( + 4.89352455891786e-03, + 6.37261928875436e-04, + 1.48572235717979e-05, + 5.12229709037114e-08, + -8.60467152213735e-11, + 2.00018790482477e-13, + -2.76076847742355e-16, + ) + + @parameter + @always_inline + fn eval1(x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: + var c_last = coefficients[coefficients_len - 1] + var c_second_from_last = coefficients[coefficients_len - 2] + + var result = x.fma(c_last, c_second_from_last) + + @parameter + for idx in range(coefficients_len - 2): + var c = coefficients[coefficients_len - 3 - idx] + result = x.fma(result, c) + + return result + + @parameter + @always_inline + fn eval2(x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: + var c_last = coefficients[coefficients_len - 1] + var c_second_from_last = coefficients[coefficients_len - 2] + + var result = x.fma(c_last, c_second_from_last) + + for idx in range(coefficients_len - 2): + var c = coefficients[coefficients_len - 3 - idx] + result = x.fma(result, c) + + return result + + var x = 6.0 + var x2 = x * x + var result1 = eval1(x2) + var result2 = eval2(x2) + + assert_equal(result1, result2) + + def test_truthy(): alias dtypes = ( DType.bool, @@ -1407,6 +1459,7 @@ def main(): test_insert() test_interleave() test_issue_20421() + test_issue_30237() test_isub() test_join() test_len() From b82a345da7684e3d58aaef530f83a982b29800f2 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 3 Jun 2024 15:03:44 -0500 Subject: [PATCH 0855/2019] [stdlib] feature: Change `StringLiteral.unsafe_ptr()` to return `UnsafePointer[UInt8]` Previously returned `UnsafePointer[Int8]`. * Removed `StringLiteral.unsafe_uint8_ptr()` * Removed `StringLiteral.as_uint8_ptr()` * Added `StringLiteral.unsafe_cstr_ptr()`, returning an `UnsafePointer[C_char]`. This can be used to interop with C functions, and is guaranteed to be NUL terminated. * Replace StringLiteral.unsafe_ptr() with unsafe_cstr_ptr() in calls to C functions via FFI. * Remove `.bitcast[UInt8]()` calls that are no longer necessary. MODULAR_ORIG_COMMIT_REV_ID: 291facf79b5ba484a025c67116342235b2604d1d --- docs/changelog.md | 9 ++++-- stdlib/benchmarks/utils/bench_memmem.mojo | 8 +++--- stdlib/src/base64/base64.mojo | 4 +-- stdlib/src/builtin/error.mojo | 3 +- stdlib/src/builtin/format_int.mojo | 5 ++-- stdlib/src/builtin/io.mojo | 10 +++---- stdlib/src/builtin/string_literal.mojo | 22 +++++++++------ stdlib/src/python/_cpython.mojo | 6 ++-- stdlib/src/sys/ffi.mojo | 4 +-- stdlib/src/sys/info.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 -- stdlib/test/builtin/test_string_literal.mojo | 29 ++++++++++++++++++++ 13 files changed, 71 insertions(+), 35 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9a8a5b9c5b..17b39788df 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -44,8 +44,9 @@ what we publish. ([PR #2701](https://github.com/modularml/mojo/pull/2701) by [@jayzhan211](https://github.com/jayzhan211)) -- Added `String.unsafe_cstr_ptr(self)` that returns an `UnsafePointer[C_char]` - for convenient interoperability with C APIs. +- Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that + returns an `UnsafePointer[C_char]` for convenient interoperability with C + APIs. - Added `C_char` type alias in `sys.ffi`. @@ -61,6 +62,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Continued transition to `UnsafePointer` and unsigned byte type for strings: - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `UnsafePointer[Int8]`) - The global functions for working with `UnsafePointer` have transitioned to being methods through the use of conditional conformances: @@ -82,6 +85,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. +- Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. + - Removed `UnsafePointer.offset(offset:Int)`. ### 🛠️ Fixed diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index c92a03bafa..aa7667beb0 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -192,9 +192,9 @@ fn bench_find_baseline(inout b: Bencher) raises: @parameter fn call_fn(): _ = _memmem_baseline( - haystack.as_uint8_ptr(), + haystack.unsafe_ptr(), len(haystack), - needle.as_uint8_ptr(), + needle.unsafe_ptr(), len(needle), ) @@ -207,9 +207,9 @@ fn bench_find_optimized(inout b: Bencher) raises: @parameter fn call_fn(): _ = _memmem( - haystack.as_uint8_ptr(), + haystack.unsafe_ptr(), len(haystack), - needle.as_uint8_ptr(), + needle.unsafe_ptr(), len(needle), ) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index a2ae6fe601..e959930cf3 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -70,7 +70,7 @@ fn b64encode(str: String) -> String: Base64 encoding of the input string. """ alias lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - var b64chars = lookup.as_uint8_ptr() + var b64chars = lookup.unsafe_ptr() var length = len(str) var out = String._buffer_type(capacity=length + 1) @@ -169,7 +169,7 @@ fn b16encode(str: String) -> String: Base16 encoding of the input string. """ alias lookup = "0123456789ABCDEF" - var b16chars = lookup.as_uint8_ptr() + var b16chars = lookup.unsafe_ptr() var length = len(str) var out = List[UInt8](capacity=length * 2 + 1) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 2342a5115d..71bfc5b7a4 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -60,8 +60,7 @@ struct Error(Stringable, Boolable): The constructed Error object. """ return Error { - # TODO: Remove cast once string UInt8 transition is complete. - data: value.unsafe_ptr().bitcast[UInt8](), + data: value.unsafe_ptr(), loaded_length: len(value), } diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 793f5bf3fe..28bcf94b18 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -256,8 +256,7 @@ fn _try_write_int[ # This static lifetime is valid as long as we're using a # `StringLiteral` for `digit_chars`. var zero = StringSlice[ImmutableStaticLifetime]( - # TODO: Remove cast after transition to UInt8 strings is complete. - unsafe_from_utf8_ptr=digit_chars_array.bitcast[UInt8](), + unsafe_from_utf8_ptr=digit_chars_array, len=1, ) fmt.write_str(zero) @@ -269,7 +268,7 @@ fn _try_write_int[ # TODO: use a dynamic size when #2194 is resolved alias CAPACITY: Int = 64 - var buf = InlineArray[Int8, CAPACITY](unsafe_uninitialized=True) + var buf = InlineArray[UInt8, CAPACITY](unsafe_uninitialized=True) # Start the buf pointer at the end. We will write the least-significant # digits later in the buffer, and then decrement the pointer to move diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 7e9049deb8..410c55bc42 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -64,11 +64,11 @@ struct _fdopen: @parameter if os_is_windows(): handle = external_call["_fdopen", UnsafePointer[NoneType]]( - _dup(stream_id.value), mode.unsafe_ptr() + _dup(stream_id.value), mode.unsafe_cstr_ptr() ) else: handle = external_call["fdopen", UnsafePointer[NoneType]]( - _dup(stream_id.value), mode.unsafe_ptr() + _dup(stream_id.value), mode.unsafe_cstr_ptr() ) self.handle = handle @@ -112,7 +112,7 @@ fn _printf[ @parameter if triple_is_nvidia_cuda(): _ = external_call["vprintf", Int32]( - fmt.unsafe_ptr(), UnsafePointer.address_of(loaded_pack) + fmt.unsafe_cstr_ptr(), UnsafePointer.address_of(loaded_pack) ) else: with _fdopen(file) as fd: @@ -125,7 +125,7 @@ fn _printf[ `) -> !pop.scalar`, ], _type=Int32, - ](fd, fmt.unsafe_ptr(), loaded_pack) + ](fd, fmt.unsafe_cstr_ptr(), loaded_pack) # ===----------------------------------------------------------------------=== # @@ -171,7 +171,7 @@ fn _snprintf[ `) -> !pop.scalar`, ], _type=Int32, - ](str, size, fmt.unsafe_ptr(), loaded_pack) + ](str, size, fmt.unsafe_cstr_ptr(), loaded_pack) ) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 4ac2a1082a..dc7c731fc1 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -21,6 +21,8 @@ from utils import StringRef from utils._visualizers import lldb_formatter_wrapping_type from utils._format import Formattable, Formatter +from sys.ffi import C_char + from .string import _atol # ===----------------------------------------------------------------------===# @@ -259,7 +261,7 @@ struct StringLiteral( return __mlir_op.`pop.string.size`(self.value) @always_inline("nodebug") - fn unsafe_ptr(self) -> UnsafePointer[Int8]: + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Get raw pointer to the underlying data. Returns: @@ -269,16 +271,20 @@ struct StringLiteral( __mlir_op.`pop.string.address`(self.value) ) - return UnsafePointer[Int8]._from_dtype_ptr(ptr) + # TODO(MSTDL-555): + # Remove bitcast after changing pop.string.address + # return type. + return UnsafePointer[Int8]._from_dtype_ptr(ptr).bitcast[UInt8]() - @always_inline("nodebug") - fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: - """Get raw pointer to the underlying data. + fn unsafe_cstr_ptr(self) -> UnsafePointer[C_char]: + """Retrieves a C-string-compatible pointer to the underlying memory. + + The returned pointer is guaranteed to be NUL terminated, and not null. Returns: - The raw pointer to the data. + The pointer to the underlying memory. """ - return self.unsafe_ptr().bitcast[UInt8]() + return self.unsafe_ptr().bitcast[C_char]() @always_inline("nodebug") fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: @@ -313,7 +319,7 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - var ptr = self.unsafe_uint8_ptr() + var ptr = self.unsafe_ptr() return Span[UInt8, ImmutableStaticLifetime]( unsafe_ptr=ptr, diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index c59524e1dd..a281a12f8f 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -526,10 +526,12 @@ struct CPython: fn PyString_FromStringAndSize(inout self, strref: StringRef) -> PyObjectPtr: var r = self.lib.get_function[ fn ( - DTypePointer[DType.uint8], Int, DTypePointer[DType.int8] + DTypePointer[DType.uint8], + Int, + UnsafePointer[C_char], ) -> PyObjectPtr ](StringRef("PyUnicode_DecodeUTF8"))( - strref.data, strref.length, "strict".unsafe_ptr() + strref.data, strref.length, "strict".unsafe_cstr_ptr() ) if self.logging_enabled: print( diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 0790dcaabd..4e5c643ee0 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -154,9 +154,7 @@ struct DLHandle(CollectionElement, Boolable): A handle to the function. """ - return self._get_function[result_type]( - func_name.unsafe_ptr().bitcast[C_char]() - ) + return self._get_function[result_type](func_name.unsafe_cstr_ptr()) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 3369eddf80..556d1a3b0b 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -732,7 +732,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: var buf_len = Int(INITIAL_CAPACITY) var err = external_call["sysctlbyname", Int32]( - "kern.osproductversion".unsafe_ptr(), + "kern.osproductversion".unsafe_cstr_ptr(), buf.data, UnsafePointer.address_of(buf_len), UnsafePointer[NoneType](), diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 23991dcb61..bad52eed95 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -330,7 +330,7 @@ struct _FixedString[CAP: Int]( self.buffer = InlineArray[UInt8, CAP]() self.size = len(literal) - memcpy(self.buffer.unsafe_ptr(), literal.as_uint8_ptr(), len(literal)) + memcpy(self.buffer.unsafe_ptr(), literal.unsafe_ptr(), len(literal)) # ===------------------------------------------------------------------=== # # Factory methods diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 3191d2af98..f38a038815 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -415,8 +415,6 @@ struct StringRef( fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. - Prefer to use `as_uint8_ptr()` instead. - Returns: The pointer to the underlying memory. """ diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index e4501043d4..ea99a2f9bf 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -12,6 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from sys.ffi import C_char + from testing import ( assert_equal, assert_not_equal, @@ -122,6 +124,32 @@ def test_intable(): _ = int("hi") +def test_layout(): + # + # Test empty StringLiteral contents + # + + var empty = "".unsafe_ptr() + + # An empty string literal is stored as just the NUL terminator. + assert_true(int(empty) != 0) + # TODO(MSTDL-596): This seems to hang? + # assert_equal(empty[0], 0) + + # + # Test non-empty StringLiteral C string + # + + var ptr: UnsafePointer[C_char] = "hello".unsafe_cstr_ptr() + + assert_equal(ptr[0], ord("h")) + assert_equal(ptr[1], ord("e")) + assert_equal(ptr[2], ord("l")) + assert_equal(ptr[3], ord("l")) + assert_equal(ptr[4], ord("o")) + assert_equal(ptr[5], 0) # Verify NUL terminated + + fn test_repr() raises: # Usual cases assert_equal(StringLiteral.__repr__("hello"), "'hello'") @@ -150,4 +178,5 @@ def main(): test_comparison_operators() test_hash() test_intable() + test_layout() test_repr() From af93ea0b3a04fb45908703ee44c8b3ddc209dd62 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 16:04:12 -0400 Subject: [PATCH 0856/2019] [stdlib] Add some more tests for `SIMD` MODULAR_ORIG_COMMIT_REV_ID: ccdbbc9e99ea4f46032add8babfe03841e904a45 --- stdlib/test/builtin/test_simd.mojo | 31 +++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 6442b58a30..345a504f51 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -121,6 +121,28 @@ def test_simd_repr(): ) +def test_issue_1625(): + var size = 16 + alias simd_width = 8 + var ptr = DTypePointer[DType.int64].alloc(size) + for i in range(size): + ptr[i] = i + + var x = SIMD[size = 2 * simd_width].load(ptr, 0) + var evens_and_odds = x.deinterleave() + + # FIXME (40568) should directly use the SIMD assert_equal + assert_equal( + str(evens_and_odds[0]), + str(SIMD[DType.int64, 8](0, 2, 4, 6, 8, 10, 12, 14)), + ) + assert_equal( + str(evens_and_odds[1]), + str(SIMD[DType.int64, 8](1, 3, 5, 7, 9, 11, 13, 15)), + ) + ptr.free() + + def test_issue_20421(): var a = DTypePointer[DType.uint8]().alloc(16 * 64, alignment=64) for i in range(16 * 64): @@ -828,8 +850,13 @@ def test_insert(): def test_join(): - vec = SIMD[DType.int32, 4](100, 101, 102, 103) + alias I2 = SIMD[DType.int32, 2] + assert_equal(Int32(3).join(Int32(4)), I2(3, 4)) + + alias I4 = SIMD[DType.int32, 4] + assert_equal(I2(5, 6).join(I2(9, 10)), I4(5, 6, 9, 10)) + vec = I4(100, 101, 102, 103) assert_equal( vec.join(vec), SIMD[DType.int32, 8](100, 101, 102, 103, 100, 101, 102, 103), @@ -1458,6 +1485,7 @@ def main(): test_indexing() test_insert() test_interleave() + test_issue_1625() test_issue_20421() test_issue_30237() test_isub() @@ -1484,3 +1512,4 @@ def main(): test_sub_with_overflow() test_trunc() test_truthy() + # TODO: add tests for __and__, __or__, anc comparison operators From c6c13e4b48f315a31e88bf04d24a265a3b7bbacd Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 3 Jun 2024 13:09:22 -0700 Subject: [PATCH 0857/2019] [mojo-docs] Document conditional conformance in changelog Give a simple example as well. MODULAR_ORIG_COMMIT_REV_ID: 6ccb9666f6b682f3760ec56c7b5fde9a3f255965 --- docs/changelog.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 17b39788df..c005001769 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,31 @@ what we publish. ### ⭐️ New +- Now supports "conditional conformances" where some methods on a struct have + additional trait requirements that the struct itself doesn't. This is + expressed through an explicitly declared `self` type: + + ```mojo + struct GenericThing[Type: AnyType]: # Works with anything + # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' + fn normal_method(self): ... + + # Just redeclare the requirements with more specific types: + fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): + var tmp = val^ # Ok to move 'val' since it is Movable + ... + fn usage_example(): + var a = GenericThing[Int]() + a.normal_method() # Ok, Int conforms to AnyType + a.needs_move(42) # Ok, Int is movable + + var b = GenericThing[NonMovable]() + b.normal_method() # Ok, NonMovable conforms to AnyType + + # error: argument type 'NonMovable' does not conform to trait 'Movable' + b.needs_move(NonMovable()) + ``` + - `async` functions now support memory-only results (like `String`, `List`, etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means From 706d9353e2d2b5d66cdc161ee499b46d0140cab9 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 3 Jun 2024 16:44:21 -0400 Subject: [PATCH 0858/2019] [stdlib][NFC] Add TODO about bf16 support on neon Including the ticket number for easier tracking. MODULAR_ORIG_COMMIT_REV_ID: c3606052789f6235b93b544e4b95c59de8604970 --- stdlib/src/builtin/simd.mojo | 54 +++++++++++++------------- stdlib/test/builtin/test_bfloat16.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 6 ++- stdlib/test/utils/test_numerics.mojo | 3 ++ 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2b396c4708..931c0b39b7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -721,15 +721,17 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self[i] == rhs[i]`. """ - @parameter # Because of #30525, we roll our own implementation for eq. + # TODO(KERN-228): support BF16 on neon systems. + # As a workaround, we roll our own implementation + @parameter if has_neon() and type == DType.bfloat16: var int_self = bitcast[_integral_type_of[type](), size](self) var int_rhs = bitcast[_integral_type_of[type](), size](rhs) return int_self == int_rhs - - return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - self.value, rhs.value - ) + else: + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self.value, rhs.value + ) @always_inline("nodebug") fn __ne__(self, rhs: Self) -> Self._Mask: @@ -744,15 +746,17 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self[i] != rhs[i]`. """ - @parameter # Because of #30525, we roll our own implementation for ne. + # TODO(KERN-228): support BF16 on neon systems. + # As a workaround, we roll our own implementation. + @parameter if has_neon() and type == DType.bfloat16: var int_self = bitcast[_integral_type_of[type](), size](self) var int_rhs = bitcast[_integral_type_of[type](), size](rhs) return int_self != int_rhs - - return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - self.value, rhs.value - ) + else: + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self.value, rhs.value + ) @always_inline("nodebug") fn __gt__(self, rhs: Self) -> Self._Mask: @@ -1444,14 +1448,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type == target: return rebind[SIMD[target, size]](self) - - @parameter - if has_neon() and (type == DType.bfloat16 or target == DType.bfloat16): - # BF16 support on neon systems is not supported. + elif has_neon() and ( + type == DType.bfloat16 or target == DType.bfloat16 + ): + # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[target, size]() - - @parameter - if type == DType.bool: + elif type == DType.bool: return self.select(SIMD[target, size](1), SIMD[target, size](0)) elif target == DType.bool: return rebind[SIMD[target, size]](self != 0) @@ -1620,16 +1622,16 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type.is_bool() or type.is_integral(): return self - - @parameter - if has_neon() and type == DType.bfloat16: + elif has_neon() and type == DType.bfloat16: + # TODO(KERN-228): support BF16 on neon systems. + # As a workaround, we cast to float32. return ( self.cast[DType.float32]() ._floor_ceil_trunc_impl[intrinsic]() .cast[type]() ) - - return llvm_intrinsic[intrinsic, Self, has_side_effect=False](self) + else: + return llvm_intrinsic[intrinsic, Self, has_side_effect=False](self) fn clamp(self, lower_bound: Self, upper_bound: Self) -> Self: """Clamps the values in a SIMD vector to be in a certain range. @@ -2883,7 +2885,7 @@ fn _bfloat16_to_f32_scalar( ) -> Scalar[DType.float32]: @parameter if has_neon(): - # BF16 support on neon systems is not supported. + # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[DType.float32, 1]() var bfloat_bits = FPUtils[DType.bfloat16].bitcast_to_integer(val) @@ -2898,7 +2900,7 @@ fn _bfloat16_to_f32[ ](val: SIMD[DType.bfloat16, size]) -> SIMD[DType.float32, size]: @parameter if has_neon(): - # BF16 support on neon systems is not supported. + # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[DType.float32, size]() @always_inline @@ -2919,7 +2921,7 @@ fn _f32_to_bfloat16_scalar( ) -> Scalar[DType.bfloat16]: @parameter if has_neon(): - # BF16 support on neon systems is not supported. + # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[DType.bfloat16, 1]() if _isnan(val): @@ -2944,7 +2946,7 @@ fn _f32_to_bfloat16[ ](val: SIMD[DType.float32, size]) -> SIMD[DType.bfloat16, size]: @parameter if has_neon(): - # BF16 support on neon systems is not supported. + # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[DType.bfloat16, size]() @always_inline diff --git a/stdlib/test/builtin/test_bfloat16.mojo b/stdlib/test/builtin/test_bfloat16.mojo index e7a99eefab..f6cdaed6c6 100644 --- a/stdlib/test/builtin/test_bfloat16.mojo +++ b/stdlib/test/builtin/test_bfloat16.mojo @@ -86,7 +86,7 @@ def check_float64_values(): def main(): check_float64_values() - # TODO re-enable this test when we sort out BF16 support for graviton3 #30525 + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): test_methods() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 345a504f51..87ea9d28cc 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -237,9 +237,9 @@ def test_truthy(): unroll[test_dtype_unrolled, dtypes.__len__()]() + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): - # TODO bfloat16 is not supported on neon #30525 test_dtype[DType.bfloat16]() @@ -263,6 +263,7 @@ def test_len(): var i6 = UI64(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) assert_equal(16, i6.__len__()) + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): alias BF16 = SIMD[DType.bfloat16, 2] @@ -403,6 +404,7 @@ def test_ceil(): assert_equal(Float32.__ceil__(Float32(-1.5)), -1.0) assert_equal(Float32.__ceil__(Float32(3.0)), 3.0) + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): assert_equal(BFloat16.__ceil__(BFloat16(2.5)), 3.0) @@ -430,6 +432,7 @@ def test_floor(): assert_equal(Float32.__floor__(Float32(-1.5)), -2.0) assert_equal(Float32.__floor__(Float32(3.0)), 3.0) + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): assert_equal(BFloat16.__floor__(BFloat16(2.5)), 2.0) @@ -1423,6 +1426,7 @@ def test_reduce(): test_dtype[DType.float64]() test_dtype[DType.index]() + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): test_dtype[DType.bfloat16]() diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index a12fe84291..bc4758376a 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -77,6 +77,7 @@ def test_get_accum_type(): def test_isfinite(): assert_true(isfinite(Float32(33))) + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): assert_false(isfinite(inf[DType.bfloat16]())) @@ -97,6 +98,7 @@ def test_isfinite(): def test_isinf(): assert_false(isinf(Float32(33))) + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): assert_true(isinf(inf[DType.bfloat16]())) @@ -117,6 +119,7 @@ def test_isinf(): def test_isnan(): assert_false(isnan(Float32(33))) + # TODO(KERN-228): support BF16 on neon systems. @parameter if not has_neon(): assert_false(isnan(inf[DType.bfloat16]())) From 82ea5cc15c51d35a7833854c1d8162d0a3ba5d43 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 3 Jun 2024 14:33:30 -0700 Subject: [PATCH 0859/2019] [mojo-stdlib] Remove the Reference->UnsafePointer implicit conversion. It is really unfortunate that any API that takes an UnsafePointer can be passed a Reference - Reference is supposed to be explicitly dereferenced and a safe type, we don't want that. Instead let's require the use of `UnsafePointer.address_of(ref[])` to make the unsafety explicit and reduce places where passing around a litref can create an UnsafePointer. MODULAR_ORIG_COMMIT_REV_ID: ac034e879dfa1d996fc279818d7c0ac8fe8207e4 --- docs/changelog.md | 5 ++++ stdlib/src/memory/unsafe_pointer.mojo | 40 +++++++++------------------ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c005001769..67ed0f61c4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -107,6 +107,11 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### ❌ Removed +- It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + `UnsafePointer.address_of(someRef[])` which makes the code explicit that the + `UnsafePointer` gets the address of what the reference points to. + - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 420e132c06..7ae3ec9dab 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -83,20 +83,6 @@ struct UnsafePointer[ """ return Self {address: value} - # TODO: remove this in favor of address_of which is less of a footgun. We - # shouldn't allow safe references to implicitly convert to unsafe pointers. - @always_inline - fn __init__(value: Reference[T, _, address_space]) -> Self: - """Create an unsafe UnsafePointer from a safe Reference. - - Args: - value: The input reference to construct with. - - Returns: - The pointer. - """ - return Self {address: __mlir_op.`lit.ref.to_pointer`(value.value)} - @always_inline fn __init__(*, address: Int) -> Self: """Create an unsafe UnsafePointer from an address in an integer. @@ -117,6 +103,19 @@ struct UnsafePointer[ # Factory methods # ===-------------------------------------------------------------------===# + @staticmethod + @always_inline("nodebug") + fn address_of(ref [_, address_space._value.value]arg: T) -> Self: + """Gets the address of the argument. + + Args: + arg: The value to get the address of. + + Returns: + An UnsafePointer which contains the address of the argument. + """ + return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) + @staticmethod fn _from_dtype_ptr[ dtype: DType, @@ -156,19 +155,6 @@ struct UnsafePointer[ ) ) - @staticmethod - @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: T) -> Self: - """Gets the address of the argument. - - Args: - arg: The value to get the address of. - - Returns: - An UnsafePointer which contains the address of the argument. - """ - return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) - # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# From e9c6783776e020ab18d27bbfdbba8d4c80f0d9f0 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 3 Jun 2024 16:25:24 -0600 Subject: [PATCH 0860/2019] Revert "[mojo-stdlib] Remove the Reference->UnsafePointer implicit conversion." This broke the Main build due to some internal CI mishaps. Reverting for now. MODULAR_ORIG_COMMIT_REV_ID: a8b43dde20a6099d34e4c6ee72a117f74e8efbc7 --- docs/changelog.md | 5 ---- stdlib/src/memory/unsafe_pointer.mojo | 40 ++++++++++++++++++--------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 67ed0f61c4..c005001769 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -107,11 +107,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### ❌ Removed -- It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the - `UnsafePointer.address_of(someRef[])` which makes the code explicit that the - `UnsafePointer` gets the address of what the reference points to. - - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 7ae3ec9dab..420e132c06 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -83,6 +83,20 @@ struct UnsafePointer[ """ return Self {address: value} + # TODO: remove this in favor of address_of which is less of a footgun. We + # shouldn't allow safe references to implicitly convert to unsafe pointers. + @always_inline + fn __init__(value: Reference[T, _, address_space]) -> Self: + """Create an unsafe UnsafePointer from a safe Reference. + + Args: + value: The input reference to construct with. + + Returns: + The pointer. + """ + return Self {address: __mlir_op.`lit.ref.to_pointer`(value.value)} + @always_inline fn __init__(*, address: Int) -> Self: """Create an unsafe UnsafePointer from an address in an integer. @@ -103,19 +117,6 @@ struct UnsafePointer[ # Factory methods # ===-------------------------------------------------------------------===# - @staticmethod - @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: T) -> Self: - """Gets the address of the argument. - - Args: - arg: The value to get the address of. - - Returns: - An UnsafePointer which contains the address of the argument. - """ - return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) - @staticmethod fn _from_dtype_ptr[ dtype: DType, @@ -155,6 +156,19 @@ struct UnsafePointer[ ) ) + @staticmethod + @always_inline("nodebug") + fn address_of(ref [_, address_space._value.value]arg: T) -> Self: + """Gets the address of the argument. + + Args: + arg: The value to get the address of. + + Returns: + An UnsafePointer which contains the address of the argument. + """ + return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# From c55774b54000126a7c738080ffcbae9d626258b6 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 3 Jun 2024 16:09:14 -0700 Subject: [PATCH 0861/2019] Python can now initialize using the Python on top of PATH, without referring to the `python_lib` path in `modular.cfg` set on install. The setting has not been removed yet, but will be deprecated in future PR's. This will allow you to activate a virtual environment like conda, install packages, and access them from Mojo without setting envioronment variables. You can activate this functionality now by removing the `python_lib` line from `~/.modular/modular.cfg`. MODULAR_ORIG_COMMIT_REV_ID: 7b7efb613564c5beb747232a7bfd7f061d9239ce --- stdlib/src/python/_cpython.mojo | 77 ++++++++------------------------- 1 file changed, 17 insertions(+), 60 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index a281a12f8f..2c13a39674 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from os import getenv -from sys import external_call +from sys import external_call, exit from sys.ffi import DLHandle from memory import DTypePointer, UnsafePointer @@ -86,32 +86,6 @@ struct PythonVersion: return PythonVersion(components[0], components[1], components[2]) -fn _py_initialize(inout cpython: CPython): - # Configure the Python interpreter to search for libraries installed in - # "site-package" directories. If we don't do this, and a `virtualenv` - # is activated when invoking `mojo`, then the Python interpreter will - # use the system's `site-package` directories instead of the - # `virtualenv` ones. - # - # This function does not overwrite any existing `PYTHONPATH`, in case - # the user wants to specify this environment variable themselves. - # - # Finally, another approach is to use `Py_SetPath()`, but that requires - # setting explicitly every library search path, as well as needing to - # specify a `PYTHONHOME`. This is much more complicated and restrictive - # to be considered a better solution. - var error_message: StringRef = external_call[ - "KGEN_CompilerRT_Python_SetPythonPath", - DTypePointer[DType.int8], - ]() - if len(error_message) != 0: - # Print the error message, but keep going in order to allow - # `Py_Initialize` to fail. - print("Mojo/Python interoperability error: ", error_message) - - cpython.lib.get_function[fn () -> None]("Py_Initialize")() - - fn _py_get_version(lib: DLHandle) -> StringRef: var version_string = lib.get_function[fn () -> DTypePointer[DType.int8]]( "Py_GetVersion" @@ -119,32 +93,6 @@ fn _py_get_version(lib: DLHandle) -> StringRef: return StringRef(version_string) -fn _py_initialize(lib: DLHandle): - # Configure the Python interpreter to search for libraries installed in - # "site-package" directories. If we don't do this, and a `virtualenv` - # is activated when invoking `mojo`, then the Python interpreter will - # use the system's `site-package` directories instead of the - # `virtualenv` ones. - # - # This function does not overwrite any existing `PYTHONPATH`, in case - # the user wants to specify this environment variable themselves. - # - # Finally, another approach is to use `Py_SetPath()`, but that requires - # setting explicitly every library search path, as well as needing to - # specify a `PYTHONHOME`. This is much more complicated and restrictive - # to be considered a better solution. - var error_message: StringRef = external_call[ - "KGEN_CompilerRT_Python_SetPythonPath", - DTypePointer[DType.int8], - ]() - if len(error_message) != 0: - # Print the error message, but keep going in order to allow - # `Py_Initialize` to fail. - print("Mojo/Python interoperability error: ", error_message) - - lib.get_function[fn () -> None]("Py_Initialize")() - - fn _py_finalize(lib: DLHandle): lib.get_function[fn () -> None]("Py_Finalize")() @@ -161,21 +109,30 @@ struct CPython: var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") + + var error_message: StringRef = external_call[ + "KGEN_CompilerRT_Python_SetPythonPath", + DTypePointer[DType.int8], + ]() + if error_message: + print(error_message) + print("Mojo/Python interop error, troubleshooting docs at:") + print(" https://modul.ar/fix-python") + # TODO: bubble up error to Mojo in next PR on stack + exit(1) + var python_lib = getenv("MOJO_PYTHON_LIBRARY") - if python_lib == "": - abort( - "Mojo/Python interoperability error: Unable to locate a" - " suitable libpython, please set `MOJO_PYTHON_LIBRARY`" - ) + if not python_lib: + abort("unable to get MOJO_PYTHON_LIBRARY environment variable") self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) self.none_value = PyObjectPtr() self.dict_type = PyObjectPtr() self.logging_enabled = logging_enabled - self.version = PythonVersion(_py_get_version(self.lib)) - _py_initialize(self.lib) + self.lib.get_function[fn () -> None]("Py_Initialize")() + self.version = PythonVersion(_py_get_version(self.lib)) _ = self.Py_None() _ = self.PyDict_Type() From f05516fa577928ef0cd88954a8ecc27093de46ef Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 3 Jun 2024 16:47:12 -0700 Subject: [PATCH 0862/2019] Adds a method to DLHandle to check if a symbol name exists, this allows checking if the dynamic library CPython attempts to load has the correct Python symbols. Also propagates up any error to the `import_module` entry point, which other methods like `add_to_path` and `evaluate` also run through. MODULAR_ORIG_COMMIT_REV_ID: f9855603ce219e88b6af2b2dbadef0d183bd3d7b --- stdlib/src/python/_cpython.mojo | 41 ++++++++++++++++++++++----------- stdlib/src/python/python.mojo | 5 ++-- stdlib/src/sys/ffi.mojo | 22 ++++++++++++++++++ 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 2c13a39674..116c000ea3 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -104,37 +104,36 @@ struct CPython: var logging_enabled: Bool var version: PythonVersion var total_ref_count: UnsafePointer[Int] + var init_error: StringRef fn __init__(inout self: CPython): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") - var error_message: StringRef = external_call[ + # TODO(MOCO-772) Allow raises to propagate through function pointers + # and make this initialization a raising function. + self.init_error = external_call[ "KGEN_CompilerRT_Python_SetPythonPath", DTypePointer[DType.int8], ]() - if error_message: - print(error_message) - print("Mojo/Python interop error, troubleshooting docs at:") - print(" https://modul.ar/fix-python") - # TODO: bubble up error to Mojo in next PR on stack - exit(1) var python_lib = getenv("MOJO_PYTHON_LIBRARY") - if not python_lib: - abort("unable to get MOJO_PYTHON_LIBRARY environment variable") self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) self.none_value = PyObjectPtr() self.dict_type = PyObjectPtr() self.logging_enabled = logging_enabled - - self.lib.get_function[fn () -> None]("Py_Initialize")() - self.version = PythonVersion(_py_get_version(self.lib)) - _ = self.Py_None() - _ = self.PyDict_Type() + if not self.init_error: + if not self.lib.check_symbol("Py_Initialize"): + self.init_error = "compatible Python library not found" + self.lib.get_function[fn () -> None]("Py_Initialize")() + self.version = PythonVersion(_py_get_version(self.lib)) + _ = self.Py_None() + _ = self.PyDict_Type() + else: + self.version = PythonVersion(0, 0, 0) @staticmethod fn destroy(inout existing: CPython): @@ -151,6 +150,19 @@ struct CPython: existing.lib.close() existing.total_ref_count.free() + fn check_init_error(self) raises: + """Used for entry points that initialize Python on first use, will + raise an error if one occured when initializing the global CPython. + """ + if self.init_error: + var error: String = self.init_error + error += "\nMOJO_PYTHON: " + getenv("MOJO_PYTHON") + error += "\nMOJO_PYTHON_LIBRARY: " + getenv("MOJO_PYTHON_LIBRARY") + error += "\nPYTHONEXECUTABLE: " + getenv("PYTHONEXECUTABLE") + error += "\n\nMojo/Python interop error, troubleshooting docs at:" + error += "\n https://modul.ar/fix-python\n" + raise error + fn Py_None(inout self) -> PyObjectPtr: """Get a None value, of type NoneType.""" if self.none_value.is_null(): @@ -174,6 +186,7 @@ struct CPython: self.logging_enabled = existing.logging_enabled self.version = existing.version self.total_ref_count = existing.total_ref_count + self.init_error = existing.init_error fn _inc_total_rc(inout self): var v = self.total_ref_count.take_pointee() diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 557dc09dd3..762f38f6fe 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -21,9 +21,8 @@ from python import Python from sys import external_call, sizeof from sys.ffi import _get_global - +from os.env import getenv from memory import UnsafePointer - from utils import StringRef from ._cpython import CPython, Py_eval_input, Py_file_input @@ -199,6 +198,8 @@ struct Python: The Python module. """ var cpython = _get_global_python_itf().cpython() + # Throw error if it occured during initialization + cpython.check_init_error() var module_maybe = cpython.PyImport_ImportModule(module) Python.throw_python_exception_if_error_state(cpython) return PythonObject(module_maybe) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 4e5c643ee0..e665cfacee 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -72,6 +72,28 @@ struct DLHandle(CollectionElement, Boolable): else: self.handle = DTypePointer[DType.int8]() + fn check_symbol(self, name: String) -> Bool: + """Check that the symbol exists in the dynamic library. + + Args: + name: The symbol to check. + + Returns: + `True` if the symbol exists. + """ + constrained[ + not os_is_windows(), + "Checking dynamic library symbol is not supported on Windows", + ]() + + var opaque_function_ptr = external_call[ + "dlsym", DTypePointer[DType.int8] + ](self.handle.address, name.unsafe_ptr()) + if opaque_function_ptr: + return True + + return False + # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline fn close(inout self): From dd16c18dd83a801f94fa097f052c1fbb000d04a1 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 3 Jun 2024 17:23:31 -0700 Subject: [PATCH 0863/2019] ] This removes all reference to `python_lib`, and setting it during install with the `find_libpython` script, enabling Mojo to dynamically find libpython at runtime. This incures not additional overhead. [ MODULAR_ORIG_COMMIT_REV_ID: dcbbd028ed4ccf400b3f90e618b659af38a278fd --- stdlib/src/python/_cpython.mojo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 116c000ea3..7604965006 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -110,6 +110,8 @@ struct CPython: var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") + print("MOJO_PYTHON:", getenv("MOJO_PYTHON")) + print("MOJO_PYTHON_LIBRARY:", getenv("MOJO_PYTHON_LIBRARY")) # TODO(MOCO-772) Allow raises to propagate through function pointers # and make this initialization a raising function. @@ -120,6 +122,10 @@ struct CPython: var python_lib = getenv("MOJO_PYTHON_LIBRARY") + if logging_enabled: + print("PYTHONEXECUTABLE:", getenv("PYTHONEXECUTABLE")) + print("libpython selected:", python_lib) + self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) self.none_value = PyObjectPtr() From e5dc1707b170be68b967456ff2eb27ad6821643e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 3 Jun 2024 19:05:07 -0700 Subject: [PATCH 0864/2019] [mojo-stdlib] Remove the Reference->UnsafePointer implicit conversion. It is really unfortunate that any API that takes an UnsafePointer can be passed a Reference - Reference is supposed to be explicitly dereferenced and a safe type, we don't want that. Instead let's require the use of UnsafePointer.address_of(ref[]) to make the unsafety explicit and reduce places where passing around a litref can create an UnsafePointer. We should re-evaluate this when/if `Reference` gets renamed to `Pointer`. If it were named that, then this would be way less confusing, but should still be explicit somehow. MODULAR_ORIG_COMMIT_REV_ID: 76930776424988abcf4dd5065c83db4e7262bfb0 --- docs/changelog.md | 5 +++ stdlib/src/memory/unsafe_pointer.mojo | 40 ++++++------------- stdlib/test/collections/test_inline_list.mojo | 4 +- 3 files changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c005001769..67ed0f61c4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -107,6 +107,11 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### ❌ Removed +- It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + `UnsafePointer.address_of(someRef[])` which makes the code explicit that the + `UnsafePointer` gets the address of what the reference points to. + - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 420e132c06..7ae3ec9dab 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -83,20 +83,6 @@ struct UnsafePointer[ """ return Self {address: value} - # TODO: remove this in favor of address_of which is less of a footgun. We - # shouldn't allow safe references to implicitly convert to unsafe pointers. - @always_inline - fn __init__(value: Reference[T, _, address_space]) -> Self: - """Create an unsafe UnsafePointer from a safe Reference. - - Args: - value: The input reference to construct with. - - Returns: - The pointer. - """ - return Self {address: __mlir_op.`lit.ref.to_pointer`(value.value)} - @always_inline fn __init__(*, address: Int) -> Self: """Create an unsafe UnsafePointer from an address in an integer. @@ -117,6 +103,19 @@ struct UnsafePointer[ # Factory methods # ===-------------------------------------------------------------------===# + @staticmethod + @always_inline("nodebug") + fn address_of(ref [_, address_space._value.value]arg: T) -> Self: + """Gets the address of the argument. + + Args: + arg: The value to get the address of. + + Returns: + An UnsafePointer which contains the address of the argument. + """ + return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) + @staticmethod fn _from_dtype_ptr[ dtype: DType, @@ -156,19 +155,6 @@ struct UnsafePointer[ ) ) - @staticmethod - @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: T) -> Self: - """Gets the address of the argument. - - Args: - arg: The value to get the address of. - - Returns: - An UnsafePointer which contains the address of the argument. - """ - return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) - # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index 08560d8274..26315f2bad 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -83,7 +83,9 @@ def test_destructor(): for index in range(capacity): inline_list.append( - ValueToCountDestructor(index, UnsafePointer(destructor_counter)) + ValueToCountDestructor( + index, UnsafePointer.address_of(destructor_counter) + ) ) # Private api use here: From 3ff4ce849f3e65907a5925660012fc0b63a4f571 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 3 Jun 2024 22:11:03 -0400 Subject: [PATCH 0865/2019] [mojo] Remove one argument from coroutine callback Internal implementation details are shifting and this isn't needed. MODULAR_ORIG_COMMIT_REV_ID: 7f22520d4e50e53c1fc249e952a693276c0a5756 --- stdlib/src/builtin/coroutine.mojo | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 7a0b96148f..c5a9f52909 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -50,16 +50,13 @@ struct _CoroutineContext: and contain the resume function and a payload pointer.""" # Passed the coroutine being completed and its context's payload. - alias _resume_fn_type = fn (AnyCoroutine, AnyCoroutine) -> None + alias _resume_fn_type = fn (AnyCoroutine) -> None var _resume_fn: Self._resume_fn_type var _parent_hdl: AnyCoroutine -fn _coro_resume_callback( - handle: AnyCoroutine, - parent: AnyCoroutine, -): +fn _coro_resume_callback(parent: AnyCoroutine): """Resume the parent Coroutine.""" _coro_resume_fn(parent) @@ -70,7 +67,7 @@ fn _coro_resume_fn(handle: AnyCoroutine): __mlir_op.`co.resume`(handle) -fn _coro_resume_noop_callback(handle: AnyCoroutine, null: AnyCoroutine): +fn _coro_resume_noop_callback(null: AnyCoroutine): """Return immediately since nothing to resume.""" return From 6fbfef9f045857686fb86a3c9973f9a26ba9429a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 3 Jun 2024 19:27:38 -0700 Subject: [PATCH 0866/2019] [mojo-stdlib] Upgrade DTypePointer to use ref-returning getitem. This upgrades DTypePointer to use a `ref` returning getitem, which is more efficient than get/set pair and allows clients who want to build on top of ref returns. While here, also enable `ptr[]` instead of requiring `ptr[0]` for consistency with other pointers. Of course, it would be better to remove DTypePointer entirely, but until we can, let's make it better. MODULAR_ORIG_COMMIT_REV_ID: 6ea93d733c7acf304cda312cb125df07bda7fa11 --- stdlib/src/memory/unsafe.mojo | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 6fa008f2bf..bf49384fb7 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -689,27 +689,18 @@ struct DTypePointer[ # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__(self, offset: Int) -> Scalar[type]: - """Loads a single element (SIMD of size 1) from the pointer at the - specified index. + fn __getitem__( + self, offset: Int = 0 + ) -> ref [MutableStaticLifetime, address_space._value.value] Scalar[type]: + """Enable subscript syntax `ptr[]` to access the element. Args: offset: The offset to load from. Returns: - The loaded value. - """ - return Scalar.load(self, offset) - - @always_inline("nodebug") - fn __setitem__(self, offset: Int, val: Scalar[type]): - """Stores a single element value at the given offset. - - Args: - offset: The offset to store to. - val: The value to store. + The reference for the Mojo compiler to use. """ - return Scalar.store(self, offset, val) + return self.address[offset] @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: From 86adc117c370151f9548ad74d1a6ffa1004161cc Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 3 Jun 2024 23:50:57 -0400 Subject: [PATCH 0867/2019] [mojo] Optimize coroutine callback by removing 1 indirect call Now that we've removed the unused argument from the coroutine completion callback, we can directly embed the resume function as the callback closure. This is enabled by changing `co.resume` to return the resume function rather than call it. This removes 1 indirect call when a coroutine completes and resumes its parent. MODULAR_ORIG_COMMIT_REV_ID: e7e35455830fddd96af118c7d108f35bf73b56fe --- stdlib/src/builtin/coroutine.mojo | 36 ++++++++++--------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index c5a9f52909..325bfc7415 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -56,15 +56,16 @@ struct _CoroutineContext: var _parent_hdl: AnyCoroutine -fn _coro_resume_callback(parent: AnyCoroutine): - """Resume the parent Coroutine.""" - _coro_resume_fn(parent) +@always_inline +fn _coro_get_resume_fn(handle: AnyCoroutine) -> fn (AnyCoroutine) -> None: + """This function is a generic coroutine resume function.""" + return __mlir_op.`co.resume`[_type= fn (AnyCoroutine) -> None](handle) @always_inline fn _coro_resume_fn(handle: AnyCoroutine): """This function is a generic coroutine resume function.""" - __mlir_op.`co.resume`(handle) + _coro_get_resume_fn(handle)(handle) fn _coro_resume_noop_callback(null: AnyCoroutine): @@ -155,30 +156,14 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: fn await_body(parent_hdl: AnyCoroutine): LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( _CoroutineContext { - _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl + _resume_fn: _coro_get_resume_fn(parent_hdl), + _parent_hdl: parent_hdl, } ) - __mlir_op.`co.resume`(self._handle) + _coro_resume_fn(self._handle) _suspend_async[await_body]() - # Never call this method. - @__named_result(out) - fn _deprecated_direct_resume(self) -> type: - LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( - _CoroutineContext { - _resume_fn: _coro_resume_noop_callback, - _parent_hdl: self._handle, - } - ) - __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) - __mlir_op.`co.set_byref_error_result`( - self._handle, - __mlir_attr.`#interp.pointer<0> : !kgen.pointer`, - UnsafePointer.address_of(out).address, - ) - __mlir_op.`co.resume`(self._handle) - # ===----------------------------------------------------------------------=== # # RaisingCoroutine @@ -267,10 +252,11 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: fn await_body(parent_hdl: AnyCoroutine): LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( _CoroutineContext { - _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl + _resume_fn: _coro_get_resume_fn(parent_hdl), + _parent_hdl: parent_hdl, } ) - __mlir_op.`co.resume`(self._handle) + _coro_resume_fn(self._handle) _suspend_async[await_body]() if __mlir_op.`co.get_results`[_type = __mlir_type.i1](self._handle): From ceae1a2b703a014131c55747b93e97f2e7bc78e9 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 4 Jun 2024 01:02:06 -0400 Subject: [PATCH 0868/2019] [mojo] Alter coroutine implementation MODULAR_ORIG_COMMIT_REV_ID: e93175c9d3d5a1202b7c82f67821c76cfe8c579a --- stdlib/src/builtin/coroutine.mojo | 40 +++++++------------------------ 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 325bfc7415..5b49761c37 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -148,21 +148,12 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: # Black magic! Internal implementation detail! # Don't you dare copy this code! 😤 + __mlir_op.`co.await`[_type=NoneType]( + self._handle, + __mlir_attr.`#interp.pointer<0> : !kgen.pointer`, + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), + ) __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) - self._set_result_slot(UnsafePointer.address_of(out)) - - @always_inline - @parameter - fn await_body(parent_hdl: AnyCoroutine): - LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( - _CoroutineContext { - _resume_fn: _coro_get_resume_fn(parent_hdl), - _parent_hdl: parent_hdl, - } - ) - _coro_resume_fn(self._handle) - - _suspend_async[await_body]() # ===----------------------------------------------------------------------=== # @@ -240,26 +231,13 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: # Black magic! Internal implementation detail! # Don't you dare copy this code! 😤 - self._set_result_slot( - __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), + if __mlir_op.`co.await`[_type = __mlir_type.i1]( + self._handle, __mlir_op.`lit.ref.to_pointer`( __get_mvalue_as_litref(__get_nearest_error_slot()) ), - ) - - @always_inline - @parameter - fn await_body(parent_hdl: AnyCoroutine): - LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( - _CoroutineContext { - _resume_fn: _coro_get_resume_fn(parent_hdl), - _parent_hdl: parent_hdl, - } - ) - _coro_resume_fn(self._handle) - - _suspend_async[await_body]() - if __mlir_op.`co.get_results`[_type = __mlir_type.i1](self._handle): + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), + ): __mlir_op.`lit.ownership.mark_initialized`( __get_mvalue_as_litref(__get_nearest_error_slot()) ) From f05042223a637e4f8314ab3c73f4e50b0ccfb351 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 4 Jun 2024 02:44:09 -0500 Subject: [PATCH 0869/2019] [External] [stdlib] Add `String.__iter__()` and fix `String.isspace()` (#41248) [External] [stdlib] Add `String.__iter__()` and fix `String.isspace()` Add an iterator for String that takes multi byte utf8 encoding into account (other encodings remain as `TODO`). Used a comparison in the utf8 bit range to know if a byte is a 1 byte, continuation byte, a 2 byte start, a 3 byte start, or a 4 byte start. The algorithm is made to be faster for languages with mostly ASCII characters, because the amount of continuation bytes in a string are counted in the Iterator constructor. That way when all of the characters with continuation bytes have been iterated over (the continuation_bytes variable becomes 0), the `_utf8_byte_type` function does not need to be called and a Reference of length 1 is directly returned on every subsequent iteration. The iterator returns a StringSlice of the corresponding byte length (without null terminator length). ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2868 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#2868 MODULAR_ORIG_COMMIT_REV_ID: 7ca8fce3da1218c13672c9c242a0fc0e89709a77 --- docs/manual/traits.ipynb | 17 +++ stdlib/src/builtin/string.mojo | 171 +++++++++++++++++++---- stdlib/src/utils/string_slice.mojo | 9 ++ stdlib/test/builtin/test_string.mojo | 125 ++++++++++++++--- stdlib/test/utils/test_string_slice.mojo | 22 ++- 5 files changed, 289 insertions(+), 55 deletions(-) diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 03a38fd176..dd47c7ef1c 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -441,16 +441,33 @@ "by a number of standard library types, and you can also implement these on your\n", "own types:\n", "\n", + " - [`Absable`](/mojo/stdlib/builtin/anytype/Absable)\n", " - [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType)\n", " - [`Boolable`](/mojo/stdlib/builtin/bool/Boolable)\n", + " - [`BoolableCollectionElement`](/mojo/stdlib/builtin/value/BoolableCollectionElement)\n", + " - [`BoolableKeyElement`](/mojo/stdlib/collections/dict/BoolableKeyElement)\n", " - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement)\n", + " - [`Comparable`](/mojo/stdlib/builtin/comparable/Comparable)\n", + " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", + " - [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable)\n", + " - [`Formattable`](/mojo/stdlib/utils/_format/Formattable)\n", + " - [`Hashable`](/mojo/stdlib/builtin/hash/Hashable)\n", + " - [`Indexer`](/mojo/stdlib/builtin/int/Indexer)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", + " - [`Powable`](/mojo/stdlib/builtin/math/Powable)\n", + " - [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", + " - [`RepresentableCollectionElement`](/mojo/stdlib/collections/dict/RepresentableCollectionElement)\n", + " - [`RepresentableKeyElement`](/mojo/stdlib/collections/dict/RepresentableKeyElement)\n", " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", + " - [`StringableCollectionElement`](/mojo/stdlib/builtin/value/StringableCollectionElement)\n", + " - [`Roundable`](/mojo/stdlib/builtin/math/Roundable)\n", + " - [`ToFormatter`](/mojo/stdlib/utils/_format/ToFormatter)\n", + " - [`Truncable`](/mojo/stdlib/builtin/_math/Truncable)\n", "\n", "The API reference docs linked above include usage examples for each trait. The\n", "following sections discuss a few of these traits.\n", diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 8b80d454bb..5972fe35ae 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -587,6 +587,9 @@ fn _isspace(c: UInt8) -> Bool: alias `\r` = UInt8(ord("\r")) alias `\f` = UInt8(ord("\f")) alias `\v` = UInt8(ord("\v")) + alias `\x1c` = UInt8(ord("\x1c")) + alias `\x1d` = UInt8(ord("\x1d")) + alias `\x1e` = UInt8(ord("\x1e")) # This compiles to something very clever that's even faster than a LUT. return ( @@ -596,6 +599,9 @@ fn _isspace(c: UInt8) -> Bool: or c == `\r` or c == `\f` or c == `\v` + or c == `\x1c` + or c == `\x1d` + or c == `\x1e` ) @@ -658,6 +664,90 @@ fn isprintable(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # +fn _utf8_byte_type(b: UInt8) -> UInt8: + """UTF-8 byte type. + + Returns: + The byte type: + 0 -> ASCII byte. + 1 -> continuation byte. + 2 -> start of 2 byte long sequence. + 3 -> start of 3 byte long sequence. + 4 -> start of 4 byte long sequence. + """ + return countl_zero(~(b & 0b1111_0000)) + + +@value +struct _StringIter[ + is_mutable: Bool, //, + lifetime: AnyLifetime[is_mutable].type, + forward: Bool = True, +]: + """Iterator for String. + + Parameters: + is_mutable: Whether the slice is mutable. + lifetime: The lifetime of the underlying string data. + forward: The iteration direction. `False` is backwards. + """ + + var index: Int + var continuation_bytes: Int + var ptr: UnsafePointer[UInt8] + var length: Int + + fn __init__( + inout self, *, unsafe_pointer: UnsafePointer[UInt8], length: Int + ): + self.index = 0 if forward else length + self.ptr = unsafe_pointer + self.length = length + self.continuation_bytes = 0 + for i in range(length): + if _utf8_byte_type(int(unsafe_pointer[i])) == 1: + self.continuation_bytes += 1 + + fn __iter__(self) -> Self: + return self + + fn __next__(inout self) -> StringSlice[lifetime]: + @parameter + if forward: + var byte_len = 1 + if self.continuation_bytes > 0: + var byte_type = _utf8_byte_type(int(self.ptr[self.index])) + if byte_type != 0: + byte_len = int(byte_type) + self.continuation_bytes -= byte_len - 1 + self.index += byte_len + return StringSlice[lifetime]( + unsafe_from_utf8_ptr=self.ptr + (self.index - byte_len), + len=byte_len, + ) + else: + var byte_len = 1 + if self.continuation_bytes > 0: + var byte_type = _utf8_byte_type(int(self.ptr[self.index - 1])) + if byte_type != 0: + while byte_type == 1: + byte_len += 1 + var b = int(self.ptr[self.index - byte_len]) + byte_type = _utf8_byte_type(b) + self.continuation_bytes -= byte_len - 1 + self.index -= byte_len + return StringSlice[lifetime]( + unsafe_from_utf8_ptr=self.ptr + self.index, len=byte_len + ) + + fn __len__(self) -> Int: + @parameter + if forward: + return self.length - self.index - self.continuation_bytes + else: + return self.index - self.continuation_bytes + + struct String( Sized, Stringable, @@ -1101,6 +1191,26 @@ struct String( count=other_len + 1, ) + fn __iter__(ref [_]self) -> _StringIter[__lifetime_of(self)]: + """Iterate over elements of the string, returning immutable references. + + Returns: + An iterator of references to the string elements. + """ + return _StringIter[__lifetime_of(self)]( + unsafe_pointer=self.unsafe_ptr(), length=len(self) + ) + + fn __reversed__(ref [_]self) -> _StringIter[__lifetime_of(self), False]: + """Iterate backwards over the string, returning immutable references. + + Returns: + A reversed iterator of references to the string elements. + """ + return _StringIter[__lifetime_of(self), forward=False]( + unsafe_pointer=self.unsafe_ptr(), length=len(self) + ) + # ===------------------------------------------------------------------=== # # Trait implementations # ===------------------------------------------------------------------=== # @@ -1116,10 +1226,10 @@ struct String( @always_inline fn __len__(self) -> Int: - """Returns the string length. + """Returns the string byte length. Returns: - The string length. + The string byte length. """ # Avoid returning -1 if the buffer is not initialized if not self.unsafe_ptr(): @@ -1450,44 +1560,43 @@ struct String( Returns: True if the String is one of the whitespace characters - listed above, otherwise False.""" + listed above, otherwise False. + """ # TODO add line and paragraph separator as stringliteral # once unicode escape secuences are accepted - # 0 is to build a String with null terminator - alias information_sep_four = List[UInt8](0x5C, 0x78, 0x31, 0x63, 0) - """TODO: \\x1c""" - alias information_sep_two = List[UInt8](0x5C, 0x78, 0x31, 0x65, 0) - """TODO: \\x1e""" - alias next_line = List[UInt8](0x78, 0x38, 0x35, 0) + var next_line = List[UInt8](0xC2, 0x85) """TODO: \\x85""" - alias unicode_line_sep = List[UInt8]( - 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x38, 0 - ) + var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8) """TODO: \\u2028""" - alias unicode_paragraph_sep = List[UInt8]( - 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x39, 0 - ) + var unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9) """TODO: \\u2029""" @always_inline - fn compare(item1: List[UInt8], item2: List[UInt8], amnt: Int) -> Bool: - var ptr1 = DTypePointer(item1.unsafe_ptr()) - var ptr2 = DTypePointer(item2.unsafe_ptr()) + fn _compare( + item1: UnsafePointer[UInt8], item2: UnsafePointer[UInt8], amnt: Int + ) -> Bool: + var ptr1 = DTypePointer(item1) + var ptr2 = DTypePointer(item2) return memcmp(ptr1, ptr2, amnt) == 0 - if len(self) == 1: - return _isspace(self._buffer.unsafe_get(0)[]) - elif len(self) == 3: - return compare(self._buffer, next_line, 3) - elif len(self) == 4: - return compare(self._buffer, information_sep_four, 4) or compare( - self._buffer, information_sep_two, 4 - ) - elif len(self) == 7: - return compare(self._buffer, unicode_line_sep, 7) or compare( - self._buffer, unicode_paragraph_sep, 7 - ) - return False + if len(self) == 0: + return False + + for s in self: + var no_null_len = len(s) + var ptr = s.unsafe_ptr() + if no_null_len == 1 and not _isspace(ptr[0]): + return False + elif no_null_len == 2 and not _compare( + ptr, next_line.unsafe_ptr(), 2 + ): + return False + elif no_null_len == 3 and not ( + _compare(ptr, unicode_line_sep.unsafe_ptr(), 3) + or _compare(ptr, unicode_paragraph_sep.unsafe_ptr(), 3) + ): + return False + return True fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]: """Split the string by a separator. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 63f0bd0a05..6922066129 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -116,6 +116,15 @@ struct StringSlice[ fn __str__(self) -> String: return String(str_slice=self) + fn __len__(self) -> Int: + """Returns the StringSlice byte length. + + Returns: + The StringSlice byte length. + """ + + return self._byte_length() + # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 68dc165eca..91302ea5f0 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -843,19 +843,11 @@ fn test_isspace() raises: # test all utf8 and unicode separators # 0 is to build a String with null terminator - alias information_sep_four = List[UInt8](0x5C, 0x78, 0x31, 0x63, 0) - """TODO: \\x1c""" - alias information_sep_two = List[UInt8](0x5C, 0x78, 0x31, 0x65, 0) - """TODO: \\x1e""" - alias next_line = List[UInt8](0x78, 0x38, 0x35, 0) + alias next_line = List[UInt8](0xC2, 0x85, 0) """TODO: \\x85""" - alias unicode_line_sep = List[UInt8]( - 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x38, 0 - ) + alias unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8, 0) """TODO: \\u2028""" - alias unicode_paragraph_sep = List[UInt8]( - 0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x39, 0 - ) + alias unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9, 0) """TODO: \\u2029""" # TODO add line and paragraph separator as stringliteral once unicode # escape secuences are accepted @@ -866,29 +858,28 @@ fn test_isspace() raises: String("\r"), String("\v"), String("\f"), + String("\x1c"), + String("\x1d"), + String("\x1e"), String(next_line), - String(information_sep_four), - String(information_sep_two), String(unicode_line_sep), String(unicode_paragraph_sep), ) - for b in List[UInt8](0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x38, 0): - var val = String(List[UInt8](b[], 0)) - if not (val in univ_sep_var): - assert_false(val.isspace()) - - for b in List[UInt8](0x20, 0x5C, 0x75, 0x32, 0x30, 0x32, 0x39, 0): - var val = String(List[UInt8](b[], 0)) - if not (val in univ_sep_var): - assert_false(val.isspace()) - for i in univ_sep_var: assert_true(i[].isspace()) for i in List[String]("not", "space", "", "s", "a", "c"): assert_false(i[].isspace()) + for i in range(len(univ_sep_var)): + var sep = String("") + for j in range(len(univ_sep_var)): + sep += univ_sep_var[i] + sep += univ_sep_var[j] + assert_true(sep.isspace()) + _ = sep + fn test_ascii_aliases() raises: assert_true(String("a") in String.ASCII_LOWERCASE) @@ -1107,6 +1098,93 @@ def test_indexing(): assert_equal(a[2], "c") +def test_string_iter(): + var vs = String("123") + + # Borrow immutably + fn conc(vs: String) -> String: + var c = String("") + for v in vs: + c += v + return c + + assert_equal(123, atol(conc(vs))) + + concat = String("") + for v in vs.__reversed__(): + concat += v + assert_equal(321, atol(concat)) + + # TODO: UnsafePointer does not have a store or __setitem__ method + # for v in vs: + # v.unsafe_ptr().store(0, "1") + + # # Borrow immutably + # for v in vs: + # concat += v + + # assert_equal(111, atol(concat)) + + var idx = -1 + vs = String("mojo🔥") + for item in vs: + idx += 1 + if idx == 0: + assert_equal("m", item) + elif idx == 1: + assert_equal("o", item) + elif idx == 2: + assert_equal("j", item) + elif idx == 3: + assert_equal("o", item) + elif idx == 4: + assert_equal("🔥", item) + assert_equal(4, idx) + + var items = List[String]( + "mojo🔥", + "السلام عليكم", + "Dobrý den", + "Hello", + "שָׁלוֹם", + "नमस्ते", + "こんにちは", + "안녕하세요", + "你好", + "Olá", + "Здравствуйте", + ) + var rev = List[String]( + "🔥ojom", + "مكيلع مالسلا", + "ned ýrboD", + "olleH", + "םֹולָׁש", + "ेत्समन", + "はちにんこ", + "요세하녕안", + "好你", + "álO", + "етйувтсвардЗ", + ) + var utf8_sequence_lengths = List(5, 12, 9, 5, 7, 6, 5, 5, 2, 3, 12) + for item_idx in range(len(items)): + var item = items[item_idx] + var utf8_sequence_len = 0 + var byte_idx = 0 + for v in item: + var byte_len = len(v) + assert_equal(item[byte_idx : byte_idx + byte_len], v) + byte_idx += byte_len + utf8_sequence_len += 1 + assert_equal(utf8_sequence_len, utf8_sequence_lengths[item_idx]) + var concat = String("") + for v in item.__reversed__(): + concat += v + assert_equal(rev[item_idx], concat) + item_idx += 1 + + def main(): test_constructors() test_copy() @@ -1151,3 +1229,4 @@ def main(): test_intable() test_string_mul() test_indexing() + test_string_iter() diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 79445c0a10..41057780b0 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -125,8 +125,28 @@ fn test_heap_string_from_string_slice() raises: assert_equal(heap_string, "Hello") +fn test_slice_len() raises: + alias str1: StringLiteral = "12345" + alias str2: StringLiteral = "1234" + alias str3: StringLiteral = "123" + alias str4: StringLiteral = "12" + alias str5: StringLiteral = "1" + + alias slice1: StringSlice[ImmutableStaticLifetime] = str1.as_string_slice() + alias slice2: StringSlice[ImmutableStaticLifetime] = str2.as_string_slice() + alias slice3: StringSlice[ImmutableStaticLifetime] = str3.as_string_slice() + alias slice4: StringSlice[ImmutableStaticLifetime] = str4.as_string_slice() + alias slice5: StringSlice[ImmutableStaticLifetime] = str5.as_string_slice() + + assert_equal(5, len(slice1)) + assert_equal(4, len(slice2)) + assert_equal(3, len(slice3)) + assert_equal(2, len(slice4)) + assert_equal(1, len(slice5)) + + fn main() raises: test_string_literal_byte_slice() test_string_byte_slice() - test_heap_string_from_string_slice() + test_slice_len() From ec87776a5637c54a849889c03f720e00b3070d7a Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Tue, 4 Jun 2024 02:46:13 -0500 Subject: [PATCH 0870/2019] [External] [stdlib] Add basics of a `collections.Counter` (#41249) [External] [stdlib] Add basics of a `collections.Counter` Implement `collections.Counter` class. Not fully implemented YET. There is a much more complete PR version here: https://github.com/modularml/mojo/pull/2910, but I was asked to split the PR into smaller ones, so this is the first one. Co-authored-by: Manuel Saelices Closes modularml/mojo#2937 MODULAR_ORIG_COMMIT_REV_ID: 2ad3ba701df4450465c7032d8cef97fb5f183291 --- stdlib/src/collections/__init__.mojo | 1 + stdlib/src/collections/counter.mojo | 78 +++++++++++++++++++++++ stdlib/test/collections/test_counter.mojo | 47 ++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 stdlib/src/collections/counter.mojo create mode 100644 stdlib/test/collections/test_counter.mojo diff --git a/stdlib/src/collections/__init__.mojo b/stdlib/src/collections/__init__.mojo index ce76accb8c..c1b1617316 100644 --- a/stdlib/src/collections/__init__.mojo +++ b/stdlib/src/collections/__init__.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the collections package.""" +from .counter import Counter from .dict import Dict, KeyElement from .inline_list import InlineList from .list import List diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo new file mode 100644 index 0000000000..b584abbbc0 --- /dev/null +++ b/stdlib/src/collections/counter.mojo @@ -0,0 +1,78 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from collections.dict import Dict + + +struct Counter[V: KeyElement]( + # Sized, + # CollectionElement, + # Boolable +): + """A container for counting hashable items. + + The value type must be specified statically, unlike a Python + Counter, which can accept arbitrary value types. + + The value type must implement the `KeyElement` trait, as its values are + stored in the dictionary as keys. + + Usage: + + ```mojo + from collections import Counter + var c = Counter[String](List("a", "a", "a", "b", "b", "c", "d", "c", "c")) + print(c["a"]) # prints 3 + print(c["b"]) # prints 2 + ``` + Parameters: + V: The value type to be counted. Currently must be KeyElement. + """ + + var _data: Dict[V, Int] + + def __init__(inout self): + """Create a new, empty Counter object.""" + self._data = Dict[V, Int]() + + # TODO: Change List to Iterable when it is supported in Mojo + def __init__(inout self, items: List[V]): + """Create a from an input iterable. + + Args: + items: A list of items to count. + """ + self._data = Dict[V, Int]() + for item_ref in items: + var item = item_ref[] + self._data[item] = self._data.get(item, 0) + 1 + + def __getitem__(self, key: V) -> Int: + """Get the count of a key. + + Args: + key: The key to get the count of. + + Returns: + The count of the key. + """ + return self._data.get(key, 0) + + fn __setitem__(inout self, value: V, count: Int): + """Set a value in the keyword Counter by key. + + Args: + value: The value to associate with the specified count. + count: The count to store in the Counter. + """ + self._data[value] = count diff --git a/stdlib/test/collections/test_counter.mojo b/stdlib/test/collections/test_counter.mojo new file mode 100644 index 0000000000..e2afa7cb54 --- /dev/null +++ b/stdlib/test/collections/test_counter.mojo @@ -0,0 +1,47 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from collections.counter import Counter + +from testing import assert_equal, assert_false, assert_raises, assert_true + + +def test_counter_construction(): + _ = Counter[Int]() + _ = Counter[Int](List[Int]()) + _ = Counter[String](List[String]()) + + +def test_counter_getitem(): + c = Counter[Int](List[Int](1, 2, 2, 3, 3, 3, 4)) + assert_equal(c[1], 1) + assert_equal(c[2], 2) + assert_equal(c[3], 3) + assert_equal(c[4], 1) + assert_equal(c[5], 0) + + +def test_counter_setitem(): + c = Counter[Int]() + c[1] = 1 + c[2] = 2 + assert_equal(c[1], 1) + assert_equal(c[2], 2) + assert_equal(c[3], 0) + + +def main(): + test_counter_construction() + test_counter_getitem() + test_counter_setitem() From 923e02567b59505040b859f119c5808dfefc531a Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 4 Jun 2024 01:07:37 -0700 Subject: [PATCH 0871/2019] [stdlib] stdlib benchmark cleanup * Moving the math builtins to the separate math benchmarks directory. * Collapsing all the math benchmarks into a single timed function. MODULAR_ORIG_COMMIT_REV_ID: bf05c8b2e89b59e0879a58ce3256325d82151e36 --- .../algorithm/bench_elementwise.mojo | 3 + stdlib/benchmarks/builtin/bench_math.mojo | 243 ------------------ stdlib/benchmarks/collections/bench_dict.mojo | 43 ---- stdlib/benchmarks/math/bench_math.mojo | 96 +++++++ stdlib/src/random/random.mojo | 15 -- 5 files changed, 99 insertions(+), 301 deletions(-) delete mode 100644 stdlib/benchmarks/builtin/bench_math.mojo create mode 100644 stdlib/benchmarks/math/bench_math.mojo diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 9e1236a289..4b9aa3c1ed 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -37,6 +37,9 @@ fn bench_elementwise[n: Int](inout b: Bencher) raises: vector[idx[0]] = 42 elementwise[func, 1, 1](Index(n)) + elementwise[func=func, simd_width = simdwidthof[DType.index](), rank=1]( + Index(n) + ) b.iter[call_fn]() diff --git a/stdlib/benchmarks/builtin/bench_math.mojo b/stdlib/benchmarks/builtin/bench_math.mojo deleted file mode 100644 index 5000bbb080..0000000000 --- a/stdlib/benchmarks/builtin/bench_math.mojo +++ /dev/null @@ -1,243 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s - -from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run -from random import * -from math import * - -# ===----------------------------------------------------------------------===# -# Benchmark Data -# ===----------------------------------------------------------------------===# -alias input_type = Float32 - - -fn make_inputs( - begin: input_type, end: input_type, num: input_type -) -> List[input_type]: - if num == 1: - return List[input_type](begin) - - var step = (end - begin) / (num - 1) - - var result: List[input_type] = List[input_type]() - for i in range(num): - result.append(begin + step * i) - return result - - -var inputs = make_inputs(0, 10_000, 1_000_000) - - -# ===----------------------------------------------------------------------===# -# Benchmark sin -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_sin(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = sin(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark cos -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_cos(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = cos(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark tan -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_tan(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = tan(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark asin -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_asin(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = asin(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark acos -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_acos(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = acos(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark atan -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_atan(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = atan(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark log -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_log(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = log(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark log2 -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_log2(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = log2(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark sqrt -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_sqrt(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = sqrt(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark exp2 -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_exp2(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = exp2(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark exp -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_exp(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = exp(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark erf -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_erf(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = erf(input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark fma -# ===----------------------------------------------------------------------===# -@parameter -fn bench_math_fma(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for input in inputs: - _ = fma(input[], input[], input[]) - - b.iter[call_fn]() - - -# ===----------------------------------------------------------------------===# -# Benchmark Main -# ===----------------------------------------------------------------------===# -def main(): - seed() - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100000)) - m.bench_function[bench_math_sin](BenchId("bench_math_sin")) - m.bench_function[bench_math_cos](BenchId("bench_math_cos")) - m.bench_function[bench_math_tan](BenchId("bench_math_tan")) - m.bench_function[bench_math_asin](BenchId("bench_math_asin")) - m.bench_function[bench_math_acos](BenchId("bench_math_acos")) - m.bench_function[bench_math_atan](BenchId("bench_math_atan")) - m.bench_function[bench_math_log](BenchId("bench_math_log")) - m.bench_function[bench_math_log2](BenchId("bench_math_log2")) - m.bench_function[bench_math_sqrt](BenchId("bench_math_sqrt")) - m.bench_function[bench_math_exp2](BenchId("bench_math_exp2")) - m.bench_function[bench_math_exp](BenchId("bench_math_exp")) - m.bench_function[bench_math_erf](BenchId("bench_math_erf")) - m.bench_function[bench_math_fma](BenchId("bench_math_fma")) - m.dump_report() diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index 01b228c54b..ecad30eec2 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -109,47 +109,6 @@ fn bench_dict_large_lookup(inout b: Bencher) raises: keep(large) -# NOTE: The delete element benchmarks are commented out because they fail to compile. -# -# ===----------------------------------------------------------------------===# -# Benchmark Dict Small Lookup -# ===----------------------------------------------------------------------===# -@parameter -fn bench_dict_small_del(inout b: Bencher) raises: - pass - # var small_copy = small - # - # @always_inline - # @parameter - # fn call_fn() raises: - # for key in range(0, partial_n): - # # del small_copy[key] - # _ = small_copy.pop(key) - # - # b.iter[call_fn]() - # keep(small_copy) - - -# ===----------------------------------------------------------------------===# -# Benchmark Dict Large Lookup -# ===----------------------------------------------------------------------===# -@parameter -fn bench_dict_large_del(inout b: Bencher) raises: - pass - - -# var large_copy = large -# @always_inline -# @parameter -# fn call_fn() raises: -# for key in range(0, partial_n): -# # del large_copy[key] -# _ = large_copy.pop(key) -# -# b.iter[call_fn]() -# keep(large_copy) - - # ===----------------------------------------------------------------------===# # Benchmark Main # ===----------------------------------------------------------------------===# @@ -169,6 +128,4 @@ def main(): m.bench_function[bench_dict_large_lookup]( BenchId("bench_dict_large_lookup") ) - m.bench_function[bench_dict_small_del](BenchId("bench_dict_small_del")) - m.bench_function[bench_dict_large_del](BenchId("bench_dict_large_del")) m.dump_report() diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo new file mode 100644 index 0000000000..b48af60067 --- /dev/null +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -0,0 +1,96 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s -t + +from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from random import * +from math import * + +# ===----------------------------------------------------------------------===# +# Benchmark Data +# ===----------------------------------------------------------------------===# +alias input_type = Float32 + + +fn make_inputs( + begin: input_type, end: input_type, num: input_type +) -> List[input_type]: + if num == 1: + return List[input_type](begin) + + var step = (end - begin) / (num - 1) + + var result: List[input_type] = List[input_type]() + for i in range(num): + result.append(begin + step * i) + return result + + +var inputs = make_inputs(0, 10_000, 1_000_000) + +# ===----------------------------------------------------------------------===# +# Benchmark math_func +# ===----------------------------------------------------------------------===# + + +@parameter +fn bench_math[ + math_f1p: fn[type: DType, size: Int] (SIMD[type, size]) -> SIMD[type, size] +](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = math_f1p(input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark fma +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math3[ + math_f3p: fn[type: DType, size: Int] ( + SIMD[type, size], SIMD[type, size], SIMD[type, size] + ) -> SIMD[type, size] +](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for input in inputs: + _ = math_f3p(input[], input[], input[]) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + seed() + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100000)) + m.bench_function[bench_math[sin]](BenchId("bench_math_sin")) + m.bench_function[bench_math[cos]](BenchId("bench_math_cos")) + m.bench_function[bench_math[tan]](BenchId("bench_math_tan")) + m.bench_function[bench_math[asin]](BenchId("bench_math_asin")) + m.bench_function[bench_math[acos]](BenchId("bench_math_acos")) + m.bench_function[bench_math[atan]](BenchId("bench_math_atan")) + m.bench_function[bench_math[log]](BenchId("bench_math_log")) + m.bench_function[bench_math[log2]](BenchId("bench_math_log2")) + m.bench_function[bench_math[sqrt]](BenchId("bench_math_sqrt")) + m.bench_function[bench_math[exp2]](BenchId("bench_math_exp2")) + m.bench_function[bench_math[exp]](BenchId("bench_math_exp")) + m.bench_function[bench_math[erf]](BenchId("bench_math_erf")) + m.bench_function[bench_math3[fma]](BenchId("bench_math_fma")) + m.dump_report() diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index f4fb7bb44e..5687d2e8db 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -93,21 +93,6 @@ fn random_ui64(min: UInt64, max: UInt64) -> UInt64: ) -fn random_si32(min: Int, max: Int) -> Int: - """Returns a random `Int` number from the given range. - - Args: - min: The minimum number in the range. - max: The maximum number in the range. - - Returns: - A random number from the specified range. - """ - return external_call["KGEN_CompilerRT_RandomSInt64", Int]( - _get_random_state(), min, max - ) - - fn randint[ type: DType ](ptr: DTypePointer[type], size: Int, low: Int, high: Int): From 78263a8e898deee3cd671416d2d4ed80985d55c8 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 4 Jun 2024 02:36:15 -0700 Subject: [PATCH 0872/2019] [Stdlib] Simplify some calls to the llvm intrinsic functions, NFC We do not need to get the underlying MLIR value of the constants being passed in, instead we can pass the values directly. MODULAR_ORIG_COMMIT_REV_ID: b7396b03f559d0239f0c357568c7e5f57f97ec36 --- stdlib/src/utils/numerics.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index b6d254e0e5..a45f868390 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -532,7 +532,7 @@ fn isnan[ alias quiet_nan_test: UInt32 = 0x0002 return llvm_intrinsic[ "llvm.is.fpclass", SIMD[DType.bool, simd_width], has_side_effect=False - ](val.value, (signaling_nan_test | quiet_nan_test).value) + ](val.value, (signaling_nan_test | quiet_nan_test)) # ===----------------------------------------------------------------------=== # @@ -802,7 +802,7 @@ fn isinf[ alias negative_infinity_test: UInt32 = 0x0004 alias positive_infinity_test: UInt32 = 0x0200 return llvm_intrinsic["llvm.is.fpclass", SIMD[DType.bool, simd_width]]( - val.value, (negative_infinity_test | positive_infinity_test).value + val.value, (negative_infinity_test | positive_infinity_test) ) @@ -835,7 +835,7 @@ fn isfinite[ return True return llvm_intrinsic["llvm.is.fpclass", SIMD[DType.bool, simd_width]]( - val.value, UInt32(0x1F8).value + val.value, UInt32(0x1F8) ) From 08858b7e07314bd3f3a5cd299b0055ae8f10074f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 4 Jun 2024 06:56:55 -0400 Subject: [PATCH 0873/2019] [stdlib] Add missing `sys.intrinsics` tests A couple of these tests were missed in a previous migration, this patch adds them. MODULAR_ORIG_COMMIT_REV_ID: f85828230d24b25a747b52b0a2efd7a484164255 --- stdlib/test/sys/test_intrinsics.mojo | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 27248afc7d..827336a844 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -23,6 +23,72 @@ from sys import ( from testing import assert_equal from memory import DTypePointer +alias F32x4 = SIMD[DType.float32, 4] +alias F32x8 = SIMD[DType.float32, 8] +alias iota_4 = F32x4(0.0, 1.0, 2.0, 3.0) +alias iota_8 = F32x8(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0) + + +def test_compressed_store(): + var vector = DTypePointer[DType.float32]().alloc(5) + memset_zero(vector, 5) + + compressed_store(iota_4, vector, iota_4 >= 2) + assert_equal(SIMD[size=4].load(vector, 0), F32x4(2.0, 3.0, 0.0, 0.0)) + + # Just clear the buffer. + SIMD[size=4].store(vector, 0, 0) + + var val = F32x4(0.0, 1.0, 3.0, 0.0) + compressed_store(val, vector, val != 0) + assert_equal(SIMD[size=4].load(vector, 0), F32x4(1.0, 3.0, 0.0, 0.0)) + vector.free() + + +def test_masked_load(): + var vector = DTypePointer[DType.float32]().alloc(5) + for i in range(5): + vector[i] = 1 + + assert_equal( + masked_load[4](vector, iota_4 < 5, 0), F32x4(1.0, 1.0, 1.0, 1.0) + ) + + assert_equal( + masked_load[8](vector, iota_8 < 5, 0), + F32x8(1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0), + ) + + assert_equal( + masked_load[8]( + vector, iota_8 < 5, F32x8(43, 321, 12, 312, 323, 15, 9, 3) + ), + F32x8(1.0, 1.0, 1.0, 1.0, 1.0, 15.0, 9.0, 3.0), + ) + + assert_equal( + masked_load[8]( + vector, iota_8 < 2, F32x8(43, 321, 12, 312, 323, 15, 9, 3) + ), + F32x8(1.0, 1.0, 12.0, 312.0, 323.0, 15.0, 9.0, 3.0), + ) + vector.free() + + +def test_masked_store(): + var vector = DTypePointer[DType.float32]().alloc(5) + memset_zero(vector, 5) + + masked_store[4](iota_4, vector, iota_4 < 5) + assert_equal(SIMD[size=4].load(vector, 0), F32x4(0.0, 1.0, 2.0, 3.0)) + + masked_store[8](iota_8, vector, iota_8 < 5) + assert_equal( + masked_load[8](vector, iota_8 < 5, 33), + F32x8(0.0, 1.0, 2.0, 3.0, 4.0, 33.0, 33.0, 33.0), + ) + vector.free() + fn test_strided_load() raises: alias size = 16 @@ -56,5 +122,8 @@ fn test_strided_store() raises: def main(): + test_compressed_store() + test_masked_load() + test_masked_store() test_strided_load() test_strided_store() From 7e4cfee7e935a925168091c6961e0c07b8da92c9 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 4 Jun 2024 04:05:01 -0700 Subject: [PATCH 0874/2019] [******] Resort all the imports, NFC MODULAR_ORIG_COMMIT_REV_ID: 9d9dcfe1d8129a8a55a87461169376779b5c72e7 --- stdlib/test/base64/test_base64.mojo | 3 ++- stdlib/test/bit/test_bit.mojo | 15 +++++++-------- stdlib/test/builtin/test_any_all.mojo | 3 ++- stdlib/test/builtin/test_bfloat16.mojo | 3 ++- stdlib/test/builtin/test_bool.mojo | 2 +- stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/builtin/test_float_literal.mojo | 4 ++-- stdlib/test/builtin/test_hasher.mojo | 2 +- stdlib/test/builtin/test_location.mojo | 4 ++-- stdlib/test/builtin/test_object.mojo | 3 +-- stdlib/test/builtin/test_print.mojo | 5 +++-- stdlib/test/builtin/test_range.mojo | 2 -- stdlib/test/builtin/test_simd.mojo | 5 +++-- stdlib/test/builtin/test_sort.mojo | 6 +++--- stdlib/test/builtin/test_string.mojo | 2 +- stdlib/test/builtin/test_string_literal.mojo | 4 ++-- stdlib/test/builtin/test_tuple.mojo | 3 ++- stdlib/test/collections/test_dict.mojo | 1 - .../collections/test_index_normalization.mojo | 1 + stdlib/test/collections/test_inline_list.mojo | 3 ++- stdlib/test/collections/test_list.mojo | 3 ++- stdlib/test/collections/test_optional.mojo | 2 +- stdlib/test/collections/test_set.mojo | 2 +- stdlib/test/memory/test_memory.mojo | 4 ++-- stdlib/test/memory/test_unsafepointer.mojo | 2 +- stdlib/test/os/path/test_exists.mojo | 4 ++-- stdlib/test/os/path/test_isdir.mojo | 4 ++-- stdlib/test/os/path/test_isfile.mojo | 4 ++-- stdlib/test/os/path/test_islink.mojo | 2 +- stdlib/test/os/test_atomic.mojo | 3 ++- stdlib/test/os/test_mkdir_and_rmdir.mojo | 4 ++-- stdlib/test/os/test_remove.mojo | 2 +- stdlib/test/os/test_stat.mojo | 2 +- stdlib/test/pathlib/test_pathlib.mojo | 6 +++--- stdlib/test/python/test_ownership.mojo | 3 +-- .../test/python/test_python_error_handling.mojo | 1 - stdlib/test/python/test_python_info.mojo | 3 +-- stdlib/test/python/test_python_interop.mojo | 3 +-- stdlib/test/python/test_python_object.mojo | 4 ++-- .../python/test_python_object_len_raises.mojo | 2 +- stdlib/test/python/test_python_to_mojo.mojo | 1 - stdlib/test/random/test_random.mojo | 3 ++- stdlib/test/sys/test_aarch64_target.mojo | 3 ++- stdlib/test/sys/test_build_info_debug.mojo | 3 ++- stdlib/test/sys/test_dlhandle.mojo | 1 + stdlib/test/sys/test_intrinsics.mojo | 2 +- stdlib/test/sys/test_linux_target.mojo | 1 + stdlib/test/sys/test_macos_target.mojo | 3 ++- stdlib/test/sys/test_paramenv.mojo | 2 +- stdlib/test/sys/test_targetinfo.mojo | 1 + stdlib/test/sys/test_windows_target.mojo | 2 +- stdlib/test/tempfile/test_tempfile.mojo | 5 +++-- stdlib/test/test_utils/__init__.mojo | 2 +- stdlib/test/testing/test_assert_raises.mojo | 2 +- stdlib/test/testing/test_assertion.mojo | 9 +++++---- stdlib/test/time/test_time.mojo | 1 + stdlib/test/utils/issue_13632.mojo | 1 + stdlib/test/utils/test_format.mojo | 5 +++-- stdlib/test/utils/test_format_to_stdout.mojo | 4 ++-- stdlib/test/utils/test_inlined_string.mojo | 2 +- stdlib/test/utils/test_numerics.mojo | 4 +++- stdlib/test/utils/test_span.mojo | 4 +++- stdlib/test/utils/test_stringref.mojo | 2 +- stdlib/test/utils/test_tuple.mojo | 2 +- stdlib/test/utils/test_unroll.mojo | 3 ++- 65 files changed, 109 insertions(+), 92 deletions(-) diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 4f7006abd0..0e9abc7f12 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -12,7 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from base64 import b64encode, b64decode, b16encode, b16decode +from base64 import b16decode, b16encode, b64decode, b64encode + from testing import assert_equal diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index e127ce6d8c..95f8bb9f63 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -13,20 +13,19 @@ # RUN: %bare-mojo %s from bit import ( - rotate_bits_left, - rotate_bits_right, - bit_width, bit_ceil, bit_floor, - is_power_of_two, - countl_zero, - countr_zero, + bit_not, bit_reverse, + bit_width, byte_swap, + countl_zero, + countr_zero, + is_power_of_two, pop_count, - bit_not, + rotate_bits_left, + rotate_bits_right, ) - from testing import assert_equal diff --git a/stdlib/test/builtin/test_any_all.mojo b/stdlib/test/builtin/test_any_all.mojo index a65ccfcc51..4fbf993fe7 100644 --- a/stdlib/test/builtin/test_any_all.mojo +++ b/stdlib/test/builtin/test_any_all.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_true, assert_false from collections.set import Set +from testing import assert_false, assert_true + def test_list_any(): # List[Int] diff --git a/stdlib/test/builtin/test_bfloat16.mojo b/stdlib/test/builtin/test_bfloat16.mojo index f6cdaed6c6..399c2deb27 100644 --- a/stdlib/test/builtin/test_bfloat16.mojo +++ b/stdlib/test/builtin/test_bfloat16.mojo @@ -14,7 +14,8 @@ from random import randn_float64 from sys import has_neon -from testing import assert_equal, assert_almost_equal + +from testing import assert_almost_equal, assert_equal def test_methods(): diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 7a24b56a1c..c97ee2a827 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal, assert_false, assert_true def test_bool_cast_to_int(): diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index b681e47ce7..c2b07226ff 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -14,7 +14,7 @@ from pathlib import Path -from sys import os_is_windows, env_get_string +from sys import env_get_string, os_is_windows from testing import assert_equal, assert_true diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index e56272735e..4a8373a8d8 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -13,11 +13,11 @@ # RUN: %mojo %s from testing import ( + assert_almost_equal, assert_equal, + assert_false, assert_not_equal, - assert_almost_equal, assert_true, - assert_false, ) alias nan = FloatLiteral.nan diff --git a/stdlib/test/builtin/test_hasher.mojo b/stdlib/test/builtin/test_hasher.mojo index 900e89339c..ed92b76fe3 100644 --- a/stdlib/test/builtin/test_hasher.mojo +++ b/stdlib/test/builtin/test_hasher.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s -from builtin._hasher import _HashableWithHasher, _Hasher, _hash_with_hasher +from builtin._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher from testing import assert_equal diff --git a/stdlib/test/builtin/test_location.mojo b/stdlib/test/builtin/test_location.mojo index 47965c8ef7..c70610663d 100644 --- a/stdlib/test/builtin/test_location.mojo +++ b/stdlib/test/builtin/test_location.mojo @@ -14,9 +14,9 @@ # RUN: %mojo -debug-level full %s from builtin._location import ( - _SourceLocation, - __source_location, __call_location, + __source_location, + _SourceLocation, ) from testing import assert_equal, assert_true diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index bc35fa685f..a02e50948b 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -14,8 +14,7 @@ from random import random_float64 - -from testing import assert_equal, assert_false, assert_true, assert_raises +from testing import assert_equal, assert_false, assert_raises, assert_true def test_object_ctors(): diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 1a23fbfc45..080355a76b 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -13,10 +13,11 @@ # RUN: %mojo %s | FileCheck %s +import sys + from memory import DTypePointer -from utils import StringRef, StaticIntTuple -import sys +from utils import StaticIntTuple, StringRef # CHECK-LABEL: test_print diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 2b2dfaae93..4c941ca076 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -14,8 +14,6 @@ from testing import assert_equal -from testing import assert_equal - def test_range_len(): # Usual cases diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 87ea9d28cc..8b0e54a62a 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -13,11 +13,12 @@ # RUN: %mojo %s from sys import has_neon + +from testing import assert_equal, assert_false, assert_not_equal, assert_true + from utils.numerics import isfinite, isinf, isnan, nan from utils.static_tuple import StaticTuple -from testing import assert_equal, assert_not_equal, assert_true, assert_false - def test_cast(): assert_equal( diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 2af26b8dce..2dfa3fee86 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -13,13 +13,13 @@ # RUN: %mojo -D CURRENT_DIR=%S %s from pathlib import Path -from sys import os_is_windows, env_get_string +from sys import env_get_string, os_is_windows alias CURRENT_DIR = env_get_string["CURRENT_DIR"]() -from testing import assert_true, assert_equal, assert_false -from random import random_si64, random_ui64, random_float64, seed +from random import random_float64, random_si64, random_ui64, seed from builtin.sort import _quicksort, _small_sort +from testing import assert_equal, assert_false, assert_true fn random_numbers[ diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 91302ea5f0..68e9303d4d 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -19,6 +19,7 @@ from builtin.string import ( _calc_initial_buffer_size_int64, _isspace, ) +from python import Python from testing import ( assert_equal, assert_false, @@ -28,7 +29,6 @@ from testing import ( ) from utils import StringRef -from python import Python @value diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index ea99a2f9bf..a856a254b8 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -16,10 +16,10 @@ from sys.ffi import C_char from testing import ( assert_equal, - assert_not_equal, - assert_true, assert_false, + assert_not_equal, assert_raises, + assert_true, ) diff --git a/stdlib/test/builtin/test_tuple.mojo b/stdlib/test/builtin/test_tuple.mojo index a15234b7f3..d1f6205727 100644 --- a/stdlib/test/builtin/test_tuple.mojo +++ b/stdlib/test/builtin/test_tuple.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_true, assert_false from sys import os_is_macos +from testing import assert_false, assert_true + def test_tuple_contains(): var a = (123, True, "Mojo is awesome") diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 47cb9cc8c7..a8f38a81f2 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -15,7 +15,6 @@ from collections import Dict, KeyElement, Optional from collections.dict import OwnedKwargsDict - from test_utils import CopyCounter from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_index_normalization.mojo b/stdlib/test/collections/test_index_normalization.mojo index d207cb96de..fe74b90622 100644 --- a/stdlib/test/collections/test_index_normalization.mojo +++ b/stdlib/test/collections/test_index_normalization.mojo @@ -16,6 +16,7 @@ from collections._index_normalization import ( get_out_of_bounds_error_message, normalize_index, ) + from testing import assert_equal diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index 26315f2bad..ca0f2e831a 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -13,8 +13,9 @@ # RUN: %mojo %s from collections import InlineList, Set -from testing import assert_equal, assert_false, assert_true, assert_raises + from test_utils import MoveCounter +from testing import assert_equal, assert_false, assert_raises, assert_true def test_list(): diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 39ba9b2aad..fc3a1c13c4 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -15,7 +15,8 @@ from collections import List from test_utils import CopyCounter, MoveCounter -from testing import assert_equal, assert_false, assert_true, assert_raises +from testing import assert_equal, assert_false, assert_raises, assert_true + from utils import Span diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 848cc3b851..5936959b3c 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -14,7 +14,7 @@ from collections import Optional, OptionalReg -from testing import assert_true, assert_false, assert_equal +from testing import assert_equal, assert_false, assert_true def test_basic(): diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 9c22bce2c3..18a30c4aff 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -14,7 +14,7 @@ from collections import Set -from testing import assert_raises, assert_true, assert_false +from testing import assert_false, assert_raises, assert_true fn assert_equal[T: EqualityComparable](lhs: T, rhs: T) raises: diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 1193fc8904..8440786e1f 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -14,8 +14,7 @@ from sys import sizeof -from memory import memcmp, memcpy, memset, memset_zero, DTypePointer, Pointer -from utils.numerics import nan +from memory import DTypePointer, Pointer, memcmp, memcpy, memset, memset_zero from testing import ( assert_almost_equal, assert_equal, @@ -24,6 +23,7 @@ from testing import ( ) from utils import Index +from utils.numerics import nan alias void = __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` alias int8_pop = __mlir_type.`!pop.scalar` diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 6e40142120..471c3cad42 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from memory import UnsafePointer -from test_utils import MoveCounter, ExplicitCopyOnly +from test_utils import ExplicitCopyOnly, MoveCounter from testing import assert_equal, assert_not_equal, assert_true diff --git a/stdlib/test/os/path/test_exists.mojo b/stdlib/test/os/path/test_exists.mojo index 171d9b8059..bfd5b8cb86 100644 --- a/stdlib/test/os/path/test_exists.mojo +++ b/stdlib/test/os/path/test_exists.mojo @@ -15,9 +15,9 @@ from os.path import exists, lexists from pathlib import Path, cwd -from builtin._location import __source_location -from testing import assert_true, assert_false +from builtin._location import __source_location +from testing import assert_false, assert_true def main(): diff --git a/stdlib/test/os/path/test_isdir.mojo b/stdlib/test/os/path/test_isdir.mojo index 140937430a..9ebc00c5dd 100644 --- a/stdlib/test/os/path/test_isdir.mojo +++ b/stdlib/test/os/path/test_isdir.mojo @@ -14,9 +14,9 @@ from os.path import isdir from pathlib import Path, cwd -from builtin._location import __source_location -from testing import assert_true, assert_false +from builtin._location import __source_location +from testing import assert_false, assert_true def main(): diff --git a/stdlib/test/os/path/test_isfile.mojo b/stdlib/test/os/path/test_isfile.mojo index 1a57c3fd2e..042ebae624 100644 --- a/stdlib/test/os/path/test_isfile.mojo +++ b/stdlib/test/os/path/test_isfile.mojo @@ -14,9 +14,9 @@ from os.path import isfile from pathlib import Path -from builtin._location import __source_location -from testing import assert_true, assert_false +from builtin._location import __source_location +from testing import assert_false, assert_true def main(): diff --git a/stdlib/test/os/path/test_islink.mojo b/stdlib/test/os/path/test_islink.mojo index 3c47735361..c4489b02b9 100644 --- a/stdlib/test/os/path/test_islink.mojo +++ b/stdlib/test/os/path/test_islink.mojo @@ -20,7 +20,7 @@ from os.path import isdir, islink from pathlib import Path from sys import env_get_string -from testing import assert_true, assert_false +from testing import assert_false, assert_true alias TEMP_DIR = env_get_string["TEMP_DIR"]() diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 16c69abfdf..ab536d9e41 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -13,7 +13,8 @@ # RUN: %mojo %s from os import Atomic -from testing import assert_equal, assert_true, assert_false + +from testing import assert_equal, assert_false, assert_true fn test_atomic() raises: diff --git a/stdlib/test/os/test_mkdir_and_rmdir.mojo b/stdlib/test/os/test_mkdir_and_rmdir.mojo index ff39af36a1..caeb7e63c4 100644 --- a/stdlib/test/os/test_mkdir_and_rmdir.mojo +++ b/stdlib/test/os/test_mkdir_and_rmdir.mojo @@ -12,11 +12,11 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from os import mkdir, rmdir, remove +from os import mkdir, remove, rmdir from os.path import exists from pathlib import Path -from testing import assert_true, assert_false, assert_raises +from testing import assert_false, assert_raises, assert_true fn create_dir_and_test_delete_string[ diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index 11cc19e8cd..5a280938b9 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -16,7 +16,7 @@ from os import remove, unlink from os.path import exists from pathlib import Path -from testing import assert_true, assert_false, assert_raises +from testing import assert_false, assert_raises, assert_true fn create_file_and_test_delete_string[ diff --git a/stdlib/test/os/test_stat.mojo b/stdlib/test/os/test_stat.mojo index bfe7169fcc..c6be572d9c 100644 --- a/stdlib/test/os/test_stat.mojo +++ b/stdlib/test/os/test_stat.mojo @@ -14,8 +14,8 @@ from os import stat from stat import S_ISREG -from builtin._location import __source_location +from builtin._location import __source_location from testing import assert_not_equal, assert_true diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 11e1812bc1..f0e58ce613 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -13,11 +13,11 @@ # REQUIRES: !windows # RUN: %mojo -D TEMP_FILE=%t %s -from pathlib import cwd, Path, DIR_SEPARATOR +from pathlib import DIR_SEPARATOR, Path, cwd from sys import env_get_string -from builtin._location import __source_location -from testing import assert_true, assert_false, assert_equal, assert_not_equal +from builtin._location import __source_location +from testing import assert_equal, assert_false, assert_not_equal, assert_true alias TEMP_FILE = env_get_string["TEMP_FILE"]() diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index 396d84e51e..2df908f4e2 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -15,8 +15,7 @@ from sys import env_get_string -from python import PythonObject, Python - +from python import Python, PythonObject from testing import assert_equal alias TEST_DIR = env_get_string["TEST_DIR"]() diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index a945000726..733ebb7973 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -16,7 +16,6 @@ from sys import env_get_string from python import Python, PythonObject - from testing import assert_equal, assert_raises alias TEST_DIR = env_get_string["TEST_DIR"]() diff --git a/stdlib/test/python/test_python_info.mojo b/stdlib/test/python/test_python_info.mojo index 8d182f0fca..1728a10c24 100644 --- a/stdlib/test/python/test_python_info.mojo +++ b/stdlib/test/python/test_python_info.mojo @@ -14,9 +14,8 @@ # RUN: %mojo %s -from python._cpython import PythonVersion from python import Python - +from python._cpython import PythonVersion from testing import assert_equal diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index a2801da121..35afe0f423 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -15,8 +15,7 @@ from sys import env_get_string -from python.python import Python, _get_global_python_itf, PythonObject - +from python.python import Python, PythonObject, _get_global_python_itf from testing import assert_equal alias TEST_DIR = env_get_string["TEST_DIR"]() diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 7a1c6fd42d..8280797478 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -13,8 +13,8 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s -from python import PythonObject, Python -from testing import assert_false, assert_raises, assert_true, assert_equal +from python import Python, PythonObject +from testing import assert_equal, assert_false, assert_raises, assert_true from utils import StringRef diff --git a/stdlib/test/python/test_python_object_len_raises.mojo b/stdlib/test/python/test_python_object_len_raises.mojo index 3b2356ebd6..62742b4d59 100644 --- a/stdlib/test/python/test_python_object_len_raises.mojo +++ b/stdlib/test/python/test_python_object_len_raises.mojo @@ -13,7 +13,7 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s -from python import PythonObject, Python +from python import Python, PythonObject from testing import assert_raises diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 97d0210435..0fbf502b7a 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -14,7 +14,6 @@ # RUN: %mojo %s from python import Python, PythonObject - from testing import assert_equal, assert_false, assert_true diff --git a/stdlib/test/random/test_random.mojo b/stdlib/test/random/test_random.mojo index 1238277111..f1f1f6ac73 100644 --- a/stdlib/test/random/test_random.mojo +++ b/stdlib/test/random/test_random.mojo @@ -13,7 +13,8 @@ # RUN: %mojo %s from random import randn_float64, random_float64, random_si64, random_ui64, seed -from testing import assert_true, assert_equal + +from testing import assert_equal, assert_true def test_random(): diff --git a/stdlib/test/sys/test_aarch64_target.mojo b/stdlib/test/sys/test_aarch64_target.mojo index b31b98e4ff..a678d977d9 100644 --- a/stdlib/test/sys/test_aarch64_target.mojo +++ b/stdlib/test/sys/test_aarch64_target.mojo @@ -17,7 +17,8 @@ # RUN: %mojo %s from sys import alignof, has_avx512f, has_neon, simdbitwidth -from testing import assert_false, assert_true, assert_equal + +from testing import assert_equal, assert_false, assert_true def test_arch_query(): diff --git a/stdlib/test/sys/test_build_info_debug.mojo b/stdlib/test/sys/test_build_info_debug.mojo index 27ef38de21..575d1deb73 100644 --- a/stdlib/test/sys/test_build_info_debug.mojo +++ b/stdlib/test/sys/test_build_info_debug.mojo @@ -14,7 +14,8 @@ # RUN: %mojo %s from sys._build import is_debug_build, is_release_build -from testing import assert_true, assert_false + +from testing import assert_false, assert_true fn test_is_debug(): diff --git a/stdlib/test/sys/test_dlhandle.mojo b/stdlib/test/sys/test_dlhandle.mojo index 2aadf441ae..ba983710bb 100644 --- a/stdlib/test/sys/test_dlhandle.mojo +++ b/stdlib/test/sys/test_dlhandle.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from sys.ffi import DLHandle + from testing import assert_false diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 827336a844..81507ec85d 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -20,8 +20,8 @@ from sys import ( strided_store, ) -from testing import assert_equal from memory import DTypePointer +from testing import assert_equal alias F32x4 = SIMD[DType.float32, 4] alias F32x8 = SIMD[DType.float32, 8] diff --git a/stdlib/test/sys/test_linux_target.mojo b/stdlib/test/sys/test_linux_target.mojo index b84943137c..a118683ca3 100644 --- a/stdlib/test/sys/test_linux_target.mojo +++ b/stdlib/test/sys/test_linux_target.mojo @@ -18,6 +18,7 @@ # RUN: %mojo %s from sys import os_is_linux, os_is_macos + from testing import assert_false, assert_true diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 931b8997be..1337bb92ad 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -26,7 +26,8 @@ from sys import ( os_is_windows, ) from sys.info import _macos_version -from testing import assert_true, assert_false + +from testing import assert_false, assert_true fn test_os_query() raises: diff --git a/stdlib/test/sys/test_paramenv.mojo b/stdlib/test/sys/test_paramenv.mojo index 37ae87cc90..840db7c3b9 100644 --- a/stdlib/test/sys/test_paramenv.mojo +++ b/stdlib/test/sys/test_paramenv.mojo @@ -14,7 +14,7 @@ from sys import env_get_int, env_get_string, is_defined -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal, assert_false, assert_true def test_is_defined(): diff --git a/stdlib/test/sys/test_targetinfo.mojo b/stdlib/test/sys/test_targetinfo.mojo index f73c741121..69bcfe14c9 100644 --- a/stdlib/test/sys/test_targetinfo.mojo +++ b/stdlib/test/sys/test_targetinfo.mojo @@ -19,6 +19,7 @@ from sys.info import ( num_physical_cores, sizeof, ) + from testing import assert_equal, assert_true diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index f78fb8393d..99ad6f1b8d 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -25,7 +25,7 @@ from os._windows import ( ) from sys import external_call, os_is_linux, os_is_macos, os_is_windows -from testing import assert_false, assert_true, assert_equal +from testing import assert_equal, assert_false, assert_true def test_os_query(): diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index 00d8006f56..8cbe6c015b 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -15,8 +15,9 @@ import os from os.path import exists from pathlib import Path -from testing import assert_true, assert_false, assert_equal -from tempfile import gettempdir, mkdtemp, TemporaryDirectory +from tempfile import TemporaryDirectory, gettempdir, mkdtemp + +from testing import assert_equal, assert_false, assert_true fn test_mkdtemp() raises: diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index e9d4cc2c76..97d6bf4ebb 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # from .test_utils import libm_call -from .types import CopyCounter, MoveCounter, MoveOnly, ExplicitCopyOnly +from .types import CopyCounter, ExplicitCopyOnly, MoveCounter, MoveOnly diff --git a/stdlib/test/testing/test_assert_raises.mojo b/stdlib/test/testing/test_assert_raises.mojo index 5c8c20ab28..4dda27ca87 100644 --- a/stdlib/test/testing/test_assert_raises.mojo +++ b/stdlib/test/testing/test_assert_raises.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_raises, assert_equal, assert_true +from testing import assert_equal, assert_raises, assert_true fn test_assert_raises_catches_error() raises: diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 0fdc41aa4f..0d1d59cf94 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -20,6 +20,7 @@ from testing import ( assert_raises, assert_true, ) + from utils.numerics import inf, nan @@ -62,22 +63,22 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:63:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:64:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:68:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:69:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:73:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:74:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:78:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:79:25: AssertionError:" in str(e)) def test_assert_almost_equal(): diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index f4080bd442..4945c20665 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -14,6 +14,7 @@ from sys import os_is_windows from time import now, sleep, time_function + from testing import assert_true diff --git a/stdlib/test/utils/issue_13632.mojo b/stdlib/test/utils/issue_13632.mojo index 7d9c460d99..d9d0655d19 100644 --- a/stdlib/test/utils/issue_13632.mojo +++ b/stdlib/test/utils/issue_13632.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections import List + from testing import assert_equal diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index dcf84fda6e..5d45382a8d 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -12,10 +12,11 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils._format import Formattable, write_to, Formatter -from utils.inline_string import _FixedString from testing import assert_equal +from utils._format import Formattable, Formatter, write_to +from utils.inline_string import _FixedString + fn main() raises: test_formatter_of_string() diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index bc0adecb59..11f9bb09cf 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,10 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils._format import Formatter, write_to, Formattable - from builtin.io import _print_fmt +from utils._format import Formattable, Formatter, write_to + fn main() raises: test_write_to_stdout() diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index 6d707b30ef..fd1a41b609 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -15,8 +15,8 @@ from testing import assert_equal, assert_true -from utils.inline_string import _FixedString from utils import InlineString +from utils.inline_string import _FixedString def main(): diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index bc4758376a..0640d226a9 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -13,7 +13,9 @@ # RUN: %mojo %s from sys.info import has_neon -from testing import assert_equal, assert_true, assert_false, assert_almost_equal + +from testing import assert_almost_equal, assert_equal, assert_false, assert_true + from utils.numerics import ( FPUtils, get_accum_type, diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 28e034b626..388d5aa83b 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -12,10 +12,12 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils import InlineArray, Span from collections.list import List + from testing import assert_equal +from utils import InlineArray, Span + def test_span_list_int(): var l = List[Int](1, 2, 3, 4, 5, 6, 7) diff --git a/stdlib/test/utils/test_stringref.mojo b/stdlib/test/utils/test_stringref.mojo index deedbd78c7..3dbca7cca5 100644 --- a/stdlib/test/utils/test_stringref.mojo +++ b/stdlib/test/utils/test_stringref.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_true, assert_false, assert_raises +from testing import assert_equal, assert_false, assert_raises, assert_true from utils import StringRef diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 3242b97df9..b35a6ae4f2 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -14,7 +14,7 @@ from testing import assert_equal, assert_false, assert_true -from utils import StaticTuple, StaticIntTuple, InlineArray +from utils import InlineArray, StaticIntTuple, StaticTuple def test_static_tuple(): diff --git a/stdlib/test/utils/test_unroll.mojo b/stdlib/test/utils/test_unroll.mojo index 382635effd..c28837635c 100644 --- a/stdlib/test/utils/test_unroll.mojo +++ b/stdlib/test/utils/test_unroll.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils import StaticIntTuple, unroll from testing import assert_equal, assert_raises +from utils import StaticIntTuple, unroll + def test_unroll(): var indexes_seen = List[Int]() From 133437c59c7e5e0999943b631e9bac20e82e94ea Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 4 Jun 2024 06:26:35 -0500 Subject: [PATCH 0875/2019] [External] [stdlib] Simplify iterator logic in `dict.mojo` (#41265) [External] [stdlib] Simplify iterator logic in `dict.mojo` There is no need to increment or decrement the index in both branches of the if. We just need to do it once, before the if statement, since `self.index` is not used afterwards. It also makes it easier to understand the purpose of the if statement. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2934 MODULAR_ORIG_COMMIT_REV_ID: 425f8f41b2d9d7971c2971891a718d8cc0e6d7aa --- stdlib/src/collections/dict.mojo | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 5db51cfe9e..3647ebed34 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -85,16 +85,6 @@ struct _DictEntryIter[ fn __next__(inout self) -> Reference[DictEntry[K, V], Self.dict_lifetime]: while True: var opt_entry_ref = self.src[]._entries.__get_ref(self.index) - if opt_entry_ref[]: - - @parameter - if forward: - self.index += 1 - else: - self.index -= 1 - - self.seen += 1 - return opt_entry_ref[].value() @parameter if forward: @@ -102,6 +92,10 @@ struct _DictEntryIter[ else: self.index -= 1 + if opt_entry_ref[]: + self.seen += 1 + return opt_entry_ref[].value() + fn __len__(self) -> Int: return len(self.src[]) - self.seen From 11099abe8ec00ba43ba9135e6343b4600c960f76 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 4 Jun 2024 10:51:26 -0400 Subject: [PATCH 0876/2019] [stdlib] Avoid using various `lit`-specific substitution in tests Several tests use various lit substitution syntax that will only work with the lit test runner.Since we want these tests to work with other test runners (such as the one with mojo test), we need to rework these parts. Thankfully, a lot of them should be able to be achieved with various `Path` functions that we have in the `pathlib` module now in the standard library. This PR changed command line argument `-D CURRENT_DIR=%S` and `env_get_string["CURRENT_DIR"]()` to using `dir_of_current_file()`, which avoids `lit` substitution. MODULAR_ORIG_COMMIT_REV_ID: d0a080a7947752fe837973df53b1ba9361cb22a4 --- stdlib/src/pathlib/__init__.mojo | 1 + stdlib/src/pathlib/path.mojo | 13 +++++++++ stdlib/test/builtin/test_file.mojo | 29 ++++++++++--------- stdlib/test/builtin/test_sort.mojo | 13 +++++---- stdlib/test/python/test_ownership.mojo | 10 +++---- .../python/test_python_error_handling.mojo | 10 +++---- stdlib/test/python/test_python_interop.mojo | 10 +++---- 7 files changed, 49 insertions(+), 37 deletions(-) diff --git a/stdlib/src/pathlib/__init__.mojo b/stdlib/src/pathlib/__init__.mojo index 377c43b403..89c2319de5 100644 --- a/stdlib/src/pathlib/__init__.mojo +++ b/stdlib/src/pathlib/__init__.mojo @@ -15,5 +15,6 @@ from .path import ( DIR_SEPARATOR, cwd, + _dir_of_current_file, Path, ) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 85aec2f0aa..2ae4819fa5 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -16,6 +16,7 @@ import os from os import PathLike, listdir, stat_result from sys import os_is_windows +from builtin._location import __call_location from memory import stack_allocation @@ -44,6 +45,18 @@ fn cwd() raises -> Path: return String(StringRef(buf)) +@always_inline +fn _dir_of_current_file() raises -> Path: + """Gets the directory the file is at. + + Returns: + The directory the file calling is at. + """ + var file_name = __call_location().file_name + var i = str(file_name).rfind(DIR_SEPARATOR) + return Path(str(file_name)[0:i]) + + struct Path(Stringable, CollectionElement, PathLike, KeyElement): """The Path object.""" diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index c2b07226ff..a8a8760356 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -10,21 +10,20 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D CURRENT_DIR=%S -D TEMP_FILE_DIR=%T -debug-level full %s +# RUN: %mojo -D TEMP_FILE_DIR=%T -debug-level full %s -from pathlib import Path -from sys import env_get_string, os_is_windows +from pathlib import Path, _dir_of_current_file +from sys import os_is_windows, env_get_string from testing import assert_equal, assert_true -alias CURRENT_DIR = env_get_string["CURRENT_DIR"]() alias TEMP_FILE_DIR = env_get_string["TEMP_FILE_DIR"]() def test_file_read(): var f = open( - Path(CURRENT_DIR) / "test_file_dummy_input.txt", + _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) assert_true( @@ -37,7 +36,7 @@ def test_file_read(): def test_file_read_multi(): var f = open( - (Path(CURRENT_DIR) / "test_file_dummy_input.txt"), + _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) @@ -50,7 +49,7 @@ def test_file_read_multi(): def test_file_read_bytes_multi(): var f = open( - Path(CURRENT_DIR) / "test_file_dummy_input.txt", + _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) @@ -80,7 +79,7 @@ def test_file_read_bytes_multi(): def test_file_read_path(): - var file_path = Path(CURRENT_DIR) / "test_file_dummy_input.txt" + var file_path = _dir_of_current_file() / "test_file_dummy_input.txt" var f = open(file_path, "r") assert_true( @@ -92,7 +91,7 @@ def test_file_read_path(): def test_file_path_direct_read(): - var file_path = Path(CURRENT_DIR) / "test_file_dummy_input.txt" + var file_path = _dir_of_current_file() / "test_file_dummy_input.txt" assert_true( file_path.read_text().startswith( "Lorem ipsum dolor sit amet, consectetur adipiscing elit." @@ -102,7 +101,7 @@ def test_file_path_direct_read(): def test_file_read_context(): with open( - Path(CURRENT_DIR) / "test_file_dummy_input.txt", + _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) as f: assert_true( @@ -116,7 +115,7 @@ def test_file_seek(): import os with open( - Path(CURRENT_DIR) / "test_file_dummy_input.txt", + _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) as f: var pos = f.seek(6) @@ -196,7 +195,7 @@ struct Word: def test_file_read_to_dtype_pointer(): - var f = open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") + var f = open(_dir_of_current_file() / "test_file_dummy_input.txt", "r") var ptr = DTypePointer[DType.int8].alloc(8) var data = f.read(ptr, 8) @@ -217,7 +216,7 @@ def test_file_get_raw_fd(): # if we printed to the right file. var f1 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_1", "rw") var f2 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_2", "rw") - var f3 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_2", "rw") + var f3 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_3", "rw") print( "test from file 1", @@ -244,6 +243,10 @@ def test_file_get_raw_fd(): assert_equal(f2.read(), "test from file 2") assert_equal(f1.read(), "test from file 1") + f1.close() + f2.close() + f3.close() + def main(): test_file_read() diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 2dfa3fee86..8a4898528b 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -10,13 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D CURRENT_DIR=%S %s +# RUN: %mojo %s -from pathlib import Path -from sys import env_get_string, os_is_windows +from pathlib import Path, _dir_of_current_file +from sys import os_is_windows, env_get_string -alias CURRENT_DIR = env_get_string["CURRENT_DIR"]() -from random import random_float64, random_si64, random_ui64, seed +from random import random_si64, random_ui64, random_float64, seed from builtin.sort import _quicksort, _small_sort from testing import assert_equal, assert_false, assert_true @@ -535,7 +534,9 @@ def test_sort_string_big_list(): def test_sort_strings(): - var text = (Path(CURRENT_DIR) / "test_file_dummy_input.txt").read_text() + var text = ( + _dir_of_current_file() / "test_file_dummy_input.txt" + ).read_text() var strings = text.split(" ") sort(strings) assert_sorted_string(strings) diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index 2df908f4e2..708798449c 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -11,18 +11,16 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo -D TEST_DIR=%S %s +# RUN: %mojo %s -from sys import env_get_string +from pathlib import _dir_of_current_file from python import Python, PythonObject from testing import assert_equal -alias TEST_DIR = env_get_string["TEST_DIR"]() - fn test_import(inout python: Python) raises: - Python.add_to_path(TEST_DIR) + Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") var py_string = my_module.my_function("Hello") var str = String(python.__str__(py_string)) @@ -58,7 +56,7 @@ fn test_getitem_ownership(inout python: Python) raises: fn test_getattr_ownership(inout python: Python) raises: - Python.add_to_path(TEST_DIR) + Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") var obj = my_module.Foo(4) var py_string = str(obj.bar) diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index 733ebb7973..7427273b77 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -11,15 +11,13 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo -D TEST_DIR=%S %s +# RUN: %mojo %s -from sys import env_get_string +from pathlib import _dir_of_current_file from python import Python, PythonObject from testing import assert_equal, assert_raises -alias TEST_DIR = env_get_string["TEST_DIR"]() - fn test_python_exception_import() raises: try: @@ -30,7 +28,7 @@ fn test_python_exception_import() raises: fn test_python_exception_getattr() raises: try: - Python.add_to_path(TEST_DIR) + Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") if my_module: var person = my_module.Person() @@ -51,7 +49,7 @@ fn test_python_exception_call() raises: with assert_raises( contains="Can't instantiate abstract class AbstractPerson" ): - Python.add_to_path(TEST_DIR) + Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") if my_module: var person = my_module.AbstractPerson() diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index 35afe0f423..089217de6d 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -11,15 +11,13 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo -D TEST_DIR=%S %s +# RUN: %mojo %s -from sys import env_get_string +from pathlib import _dir_of_current_file from python.python import Python, PythonObject, _get_global_python_itf from testing import assert_equal -alias TEST_DIR = env_get_string["TEST_DIR"]() - fn test_execute_python_string(inout python: Python) -> String: try: @@ -31,7 +29,7 @@ fn test_execute_python_string(inout python: Python) -> String: fn test_local_import(inout python: Python) -> String: try: - Python.add_to_path(TEST_DIR) + Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") if my_module: var foo = my_module.Foo("apple") @@ -63,7 +61,7 @@ def hello(name): fn test_call(inout python: Python) -> String: try: - Python.add_to_path(TEST_DIR) + Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") return str( my_module.eat_it_all( From 4e6270b0f3cccecf3bd445c4277b831830457f2d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 4 Jun 2024 08:08:58 -0700 Subject: [PATCH 0877/2019] [******] Remove non-temporal store from the pointer types This functionality was no longer used, so we can remove its support from the pointer types. MODULAR_ORIG_COMMIT_REV_ID: edfed96727053aec75934f8887c37ed4bf02295a --- docs/changelog-released.md | 2 ++ stdlib/src/memory/unsafe.mojo | 50 ----------------------------------- 2 files changed, 2 insertions(+), 50 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 1c7506728a..858ec28393 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -676,6 +676,8 @@ modular update mojo - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. +- The pointer types no longer have the ability to perform a non-temporal store. + ### 🛠️ Fixed - [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index bf49384fb7..338c128b16 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -361,23 +361,6 @@ struct LegacyPointer[ ]() __mlir_op.`pop.store`[alignment = alignment.value](value, self.address) - @always_inline("nodebug") - fn nt_store(self, value: type): - """Stores a value using non-temporal store. - - The address must be properly aligned, 64B for avx512, 32B for avx2, and - 16B for avx. - - Args: - value: The value to store. - """ - # Store a simd value into the pointer. The address must be properly - # aligned, 64B for avx512, 32B for avx2, and 16B for avx. - __mlir_op.`pop.store`[ - alignment = int(8 * simdwidthof[type]()).value, - nonTemporal = __mlir_attr.unit, - ](value, self.address) - @always_inline("nodebug") fn __int__(self) -> Int: """Returns the pointer address as an integer. @@ -871,22 +854,6 @@ struct DTypePointer[ Scalar[type] ]() if triple_is_nvidia_cuda() else 1 - @always_inline("nodebug") - fn simd_nt_store[ - width: Int, T: Intable - ](self, offset: T, val: SIMD[type, width]): - """Stores a SIMD vector using non-temporal store. - - Parameters: - width: The SIMD width. - T: The Intable type of the offset. - - Args: - offset: The offset to store to. - val: The SIMD value to store. - """ - self.offset(offset).simd_nt_store[width](val) - @always_inline("nodebug") fn simd_strided_load[ width: Int, T: Intable @@ -923,23 +890,6 @@ struct DTypePointer[ """ strided_store(val, self, int(stride), True) - @always_inline("nodebug") - fn simd_nt_store[width: Int](self, val: SIMD[type, width]): - """Stores a SIMD vector using non-temporal store. - - The address must be properly aligned, 64B for avx512, 32B for avx2, and - 16B for avx. - - Parameters: - width: The SIMD width. - - Args: - val: The SIMD value to store. - """ - # Store a simd value into the pointer. The address must be properly - # aligned, 64B for avx512, 32B for avx2, and 16B for avx. - self.address.bitcast[SIMD[type, width]]().nt_store(val) - # ===------------------------------------------------------------------=== # # Gather / Scatter # ===------------------------------------------------------------------=== # From ef531bf1e63797da79d43bdc6a52469ffd34e1be Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 4 Jun 2024 09:20:41 -0600 Subject: [PATCH 0878/2019] [stdlib] Remove `SIMD.setitem` overload for `pop.scalar` This `setitem` overload is unused: remove it. MODULAR_ORIG_COMMIT_REV_ID: bb396b46213cfe922e688cb7d26a56633a43a694 --- stdlib/src/builtin/simd.mojo | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 931c0b39b7..874c31fe46 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -501,20 +501,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( self.value, val.value, index(idx).value ) - @always_inline("nodebug") - fn __setitem__( - inout self, idx: Int, val: __mlir_type[`!pop.scalar<`, type.value, `>`] - ): - """Sets an element in the vector. - - Args: - idx: The index to set. - val: The value to set. - """ - self.value = __mlir_op.`pop.simd.insertelement`( - self.value, val, index(idx).value - ) - @always_inline("nodebug") fn __add__(self, rhs: Self) -> Self: """Computes `self + rhs`. From c8accd0d0615d07bcc1dfce82f93e1768579ad6c Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 4 Jun 2024 09:24:45 -0700 Subject: [PATCH 0879/2019] [stdlib] benchmarks configured to be compile verify only (#41285) The stdlib benchmarks are configured to be compile verify only. MODULAR_ORIG_COMMIT_REV_ID: cb5f28a23c5d3572ca02e7461927e4be656c0470 --- stdlib/benchmarks/algorithm/bench_elementwise.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 2 +- stdlib/benchmarks/utils/bench_formatter.mojo | 2 +- stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 4b9aa3c1ed..9ee388308c 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s -t from algorithm import elementwise from benchmark import Bench, Bencher, BenchId, BenchConfig diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index ecad30eec2..05e728210f 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from stdlib.collections import Dict diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 477d0ffd07..cf549a1b40 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from utils.stringref import _memmem, _memchr, _align_down diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index aa7667beb0..fd2c310460 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from utils.stringref import _memmem, _memchr, _align_down From dfec89d82bd3517bfdbfd12a229727646a207a7a Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 4 Jun 2024 10:04:19 -0700 Subject: [PATCH 0880/2019] [stdlib] removing mojo-no-debug from stdlib benchmarks (#41287) Removing `mojo-no-debug` from the stdlib benchmarks as its not available in open source CI. MODULAR_ORIG_COMMIT_REV_ID: c98e39bcf422ce4bd46a746a0a9b8f0c5b9ca047 --- stdlib/benchmarks/algorithm/bench_elementwise.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 2 +- stdlib/benchmarks/utils/bench_formatter.mojo | 2 +- stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 9ee388308c..bc8ba5696b 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s -t +# RUN: %mojo %s -t from algorithm import elementwise from benchmark import Bench, Bencher, BenchId, BenchConfig diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index 05e728210f..f35faf0c72 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s -t +# RUN: %mojo %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from stdlib.collections import Dict diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index cf549a1b40..ffe6e1dec9 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s -t +# RUN: %mojo %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from utils.stringref import _memmem, _memchr, _align_down diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index fd2c310460..220c43771a 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s -t +# RUN: %mojo %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from utils.stringref import _memmem, _memchr, _align_down From f2799a6731161ad205b048e1f8ab86ec51dd294b Mon Sep 17 00:00:00 2001 From: GeauxEric Date: Tue, 4 Jun 2024 12:40:35 -0500 Subject: [PATCH 0881/2019] [External] [stdlib] Add safe, non-allocating `FileHandle.write()` (#41125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Add safe, non-allocating `FileHandle.write()` Currently, the `FileHandle` type used to read and write data to an opened file descriptor currently provides three `write(..)` method overloads: - `FileHandle.write(String)` — requires allocating a `String` - `FileHandle.write(StringRef)` — use of the unsafe and easy-to-misuse `StringRef` type - `FileHandle.write(UnsafePointer[UInt8], Int)` — requires use of `UnsafePointer` This means that all currently existing ways to write to a file are either non-optimal or requires dipping down into low-level unsafe code. Add *another write overload* — this time, for a `Span` to safely write a borrowed sequence of bytes. Fixes https://github.com/modularml/mojo/issues/2909 Signed-off-by: Yun Ding Co-authored-by: GeauxEric Closes modularml/mojo#2913 MODULAR_ORIG_COMMIT_REV_ID: c83c86ea369f78d9bbff1bc0567af54fef3c2d65 --- stdlib/src/builtin/file.mojo | 8 ++ stdlib/test/builtin/test_file.mojo | 136 ++++++++++++++--------------- 2 files changed, 76 insertions(+), 68 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 50962765f5..e89c168478 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -414,6 +414,14 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) + fn write(self, data: Span[UInt8, _]) raises: + """Write a borrowed sequence of data to the file. + + Args: + data: The data to write to the file. + """ + self._write(data.unsafe_ptr(), len(data)) + @always_inline fn write(self, data: StringRef) raises: """Write the data to the file. diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index a8a8760356..3ec0efcb1c 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -22,72 +22,64 @@ alias TEMP_FILE_DIR = env_get_string["TEMP_FILE_DIR"]() def test_file_read(): - var f = open( - _dir_of_current_file() / "test_file_dummy_input.txt", - "r", - ) - assert_true( - f.read().startswith( - "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + var path = _dir_of_current_file() / "test_file_dummy_input.txt" + with open(path, "r") as f: + assert_true( + f.read().startswith( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ) ) - ) - f.close() def test_file_read_multi(): - var f = open( + with open( _dir_of_current_file() / "test_file_dummy_input.txt", "r", - ) - - assert_equal(f.read(12), "Lorem ipsum ") - assert_equal(f.read(6), "dolor ") - assert_true(f.read().startswith("sit amet, consectetur adipiscing elit.")) - - f.close() + ) as f: + assert_equal(f.read(12), "Lorem ipsum ") + assert_equal(f.read(6), "dolor ") + assert_true( + f.read().startswith("sit amet, consectetur adipiscing elit.") + ) def test_file_read_bytes_multi(): - var f = open( + with open( _dir_of_current_file() / "test_file_dummy_input.txt", "r", - ) - - var bytes1 = f.read_bytes(12) - assert_equal(len(bytes1), 12, "12 bytes") - # we add the null terminator - bytes1.append(0) - var string1 = String(bytes1) - assert_equal(len(string1), 12, "12 chars") - assert_equal(string1, String("Lorem ipsum ")) - - var bytes2 = f.read_bytes(6) - assert_equal(len(bytes2), 6, "6 bytes") - # we add the null terminator - bytes2.append(0) - var string2 = String(bytes2) - assert_equal(len(string2), 6, "6 chars") - assert_equal(string2, "dolor ") + ) as f: + var bytes1 = f.read_bytes(12) + assert_equal(len(bytes1), 12, "12 bytes") + # we add the null terminator + bytes1.append(0) + var string1 = String(bytes1) + assert_equal(len(string1), 12, "12 chars") + assert_equal(string1, String("Lorem ipsum ")) - # Read where N is greater than the number of bytes in the file. - var s: String = f.read(1e9) + var bytes2 = f.read_bytes(6) + assert_equal(len(bytes2), 6, "6 bytes") + # we add the null terminator + bytes2.append(0) + var string2 = String(bytes2) + assert_equal(len(string2), 6, "6 chars") + assert_equal(string2, "dolor ") - assert_equal(len(s), 936) - assert_true(s.startswith("sit amet, consectetur adipiscing elit.")) + # Read where N is greater than the number of bytes in the file. + var s: String = f.read(1e9) - f.close() + assert_equal(len(s), 936) + assert_true(s.startswith("sit amet, consectetur adipiscing elit.")) def test_file_read_path(): var file_path = _dir_of_current_file() / "test_file_dummy_input.txt" - var f = open(file_path, "r") - assert_true( - f.read().startswith( - "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + with open(file_path, "r") as f: + assert_true( + f.read().startswith( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ) ) - ) - f.close() def test_file_path_direct_read(): @@ -150,13 +142,21 @@ def test_file_open_nodir(): def test_file_write(): var content: String = "The quick brown fox jumps over the lazy dog" var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write" - var f = open(TEMP_FILE, "w") - f.write(content) - f.close() + with open(TEMP_FILE, "w") as f: + f.write(content) + + with open(TEMP_FILE, "r") as read_file: + assert_equal(read_file.read(), content) + + +def test_file_write_span(): + var content: String = "The quick brown fox jumps over the lazy dog" + var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write_span" + with open(TEMP_FILE, "w") as f: + f.write(content.as_bytes_slice()) - var read_file = open(TEMP_FILE, "r") - assert_equal(read_file.read(), content) - read_file.close() + with open(TEMP_FILE, "r") as read_file: + assert_equal(read_file.read(), content) def test_file_write_again(): @@ -169,9 +169,8 @@ def test_file_write_again(): with open(TEMP_FILE, "w") as f: f.write(expected_content) - var read_file = open(TEMP_FILE, "r") - assert_equal(read_file.read(), expected_content) - read_file.close() + with open(TEMP_FILE, "r") as read_file: + assert_equal(read_file.read(), expected_content) @value @@ -195,20 +194,20 @@ struct Word: def test_file_read_to_dtype_pointer(): - var f = open(_dir_of_current_file() / "test_file_dummy_input.txt", "r") - - var ptr = DTypePointer[DType.int8].alloc(8) - var data = f.read(ptr, 8) - assert_equal( - str(SIMD[size=8].load(ptr, 0)), "[76, 111, 114, 101, 109, 32, 105, 112]" - ) + with open(_dir_of_current_file() / "test_file_dummy_input.txt", "r") as f: + var ptr = DTypePointer[DType.int8].alloc(8) + var data = f.read(ptr, 8) + assert_equal( + str(SIMD[size=8].load(ptr, 0)), + "[76, 111, 114, 101, 109, 32, 105, 112]", + ) - var ptr2 = DTypePointer[DType.int8].alloc(8) - var data2 = f.read(ptr2, 8) - assert_equal( - str(SIMD[size=8].load(ptr2, 0)), - "[115, 117, 109, 32, 100, 111, 108, 111]", - ) + var ptr2 = DTypePointer[DType.int8].alloc(8) + var data2 = f.read(ptr2, 8) + assert_equal( + str(SIMD[size=8].load(ptr2, 0)), + "[115, 117, 109, 32, 100, 111, 108, 111]", + ) def test_file_get_raw_fd(): @@ -258,6 +257,7 @@ def main(): test_file_seek() test_file_open_nodir() test_file_write() + test_file_write_span() test_file_write_again() test_file_read_to_dtype_pointer() test_file_get_raw_fd() From 2562080a51850f972a207e500d91240847f86f72 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 4 Jun 2024 10:48:48 -0700 Subject: [PATCH 0882/2019] [Stdlib] Simplify the implementation of _strnlen, NFC We do not need the offset and deref, instead we can just index directly into the pointer. MODULAR_ORIG_COMMIT_REV_ID: bc6a725de3a08772208753087cca1a0181221ee1 --- stdlib/src/os/os.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index dd78b31912..b9fe3ee544 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -86,10 +86,10 @@ struct _dirent_macos: fn _strnlen(ptr: UnsafePointer[Int8], max: Int) -> Int: - var len = 0 - while len < max and (ptr + len)[0]: - len += 1 - return len + var offset = 0 + while offset < max and ptr[offset]: + offset += 1 + return offset struct _DirHandle: From a9a2872d12450c7d799a7184b3e2fd6337b0770a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 4 Jun 2024 10:49:12 -0700 Subject: [PATCH 0883/2019] [Stdlib] Init SIMD with undef rather than 0 during variadic construction Rather than initialize the SIMD with 0 and then overwrite, just initialize the SIMD value with undef and then populate. This is a micro-optimization. MODULAR_ORIG_COMMIT_REV_ID: ecd5ce1205d77587ab4abab466347b91b97c47b3 --- stdlib/src/builtin/simd.mojo | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 874c31fe46..6618804d39 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -324,7 +324,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constructed. """ _simd_construction_checks[type, size]() - var num_elements: Int = len(elems) + + var num_elements = len(elems) if num_elements == 1: # Construct by broadcasting a scalar. self.value = __mlir_op.`pop.simd.splat`[ @@ -345,7 +346,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( " constructor" ), ) - self = Self() + + self = __mlir_op.`kgen.undef`[ + _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] + ]() @parameter for i in range(size): From 317d3159e54e10ee1e4f1481a0cfd347160996d3 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 4 Jun 2024 14:24:34 -0400 Subject: [PATCH 0884/2019] [mojo] Alter coroutine implementation MODULAR_ORIG_COMMIT_REV_ID: bcca60e01ff6dd44bb7407a3e3d4bf99b16dd15b --- stdlib/src/builtin/coroutine.mojo | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 5b49761c37..57a5740cf0 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -116,7 +116,6 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: fn _set_result_slot(self, slot: UnsafePointer[type]): __mlir_op.`co.set_byref_error_result`( self._handle, - __mlir_attr.`#interp.pointer<0> : !kgen.pointer`, slot.address, ) @@ -150,7 +149,6 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: # Don't you dare copy this code! 😤 __mlir_op.`co.await`[_type=NoneType]( self._handle, - __mlir_attr.`#interp.pointer<0> : !kgen.pointer`, __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), ) __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) @@ -200,7 +198,7 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: self, slot: UnsafePointer[type], err: UnsafePointer[Error] ): __mlir_op.`co.set_byref_error_result`( - self._handle, err.address, slot.address + self._handle, slot.address, err.address ) @always_inline @@ -233,10 +231,10 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: # Don't you dare copy this code! 😤 if __mlir_op.`co.await`[_type = __mlir_type.i1]( self._handle, + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), __mlir_op.`lit.ref.to_pointer`( __get_mvalue_as_litref(__get_nearest_error_slot()) ), - __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), ): __mlir_op.`lit.ownership.mark_initialized`( __get_mvalue_as_litref(__get_nearest_error_slot()) From 9db6a1fd803a8b8296369c4ee0b97244bcfb8e1f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 4 Jun 2024 14:53:54 -0600 Subject: [PATCH 0885/2019] [stdlib] Reorganize open source stdlib tests This is an internal reorganization of where the open source Mojo standard library tests live. Additionally, it updates the `stdlib/test/lit.cfg.py` to be shared for both internal and external use. The different execution modes are guarded by use of an environment variable that is set in internal builds. MODULAR_ORIG_COMMIT_REV_ID: 1e930100921a4bca23845cc660c55c4bdf3eedf6 --- stdlib/test/lit.cfg.py | 104 ++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 0e0f56680b..e50d82cc77 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -14,11 +14,14 @@ import os import platform import shutil + from pathlib import Path import lit.formats import lit.llvm +# Configuration file for the 'lit' test runner. + config.test_format = lit.formats.ShTest(True) # name: The name of this test suite. @@ -27,38 +30,6 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes = [".mojo"] -# test_utils does not contain tests, just source code -# that we run `mojo package` on to be used by other tests -config.excludes = ["test_utils"] - -# test_source_root: The root path where tests are located. -config.test_source_root = Path(__file__).parent.resolve() - -# The `run-tests.sh` script creates the build directory for you. -build_root = Path(__file__).parent.parent.parent / "build" - -# The tests are executed inside this build directory to avoid -# polluting the source tree. -config.test_exec_root = build_root / "stdlib" - -# This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRE: darwin`. -config.available_features.add(platform.system().lower()) - -# Substitute %mojo for just `mojo` itself -# since we're not supporting `--sanitize` initially -# to allow running the tests with LLVM sanitizers. -config.substitutions.insert(0, ("%mojo", "mojo -D MOJO_ENABLE_ASSERTIONS")) -config.substitutions.insert(1, ("%bare-mojo", "mojo")) - -# The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the -# open-source stdlib, we need to specify the paths to the just-built -# `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the -# `mojo` compiler would use its own `stdlib.mojopkg` it ships with which is not -# what we want. We override both the stable and nightly `mojo` import paths -# here to support both versions of the compiler. -os.environ["MODULAR_MOJO_IMPORT_PATH"] = str(build_root) -os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = str(build_root) - # Check if the `not` binary from LLVM is available. def has_not(): @@ -68,13 +39,62 @@ def has_not(): if has_not(): config.available_features.add("has_not") -# Pass through several environment variables -# to the underlying subprocesses that run the tests. -lit.llvm.initialize(lit_config, config) -lit.llvm.llvm_config.with_system_environment( - [ - "MODULAR_HOME", - "MODULAR_MOJO_IMPORT_PATH", - "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", - ] -) +# Insert at 0th position to intentionally override +# %mojo from the global utils/build/llvm-lit/lit.common.cfg.py +# (only matters internally). +# In the future, we can do other fancy things like with sanitizers +# and build type. +config.substitutions.insert(0, ("%mojo", "mojo -D MOJO_ENABLE_ASSERTIONS")) + +# Mojo without assertions. Only use this for known tests that do not work +# with assertions enabled. +config.substitutions.insert(1, ("%bare-mojo", "mojo")) + +# This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRE: darwin`. +config.available_features.add(platform.system().lower()) + +# Internal testing configuration. This environment variable +# is set by the internal `start-modular.sh` script. +if "_START_MODULAR_INCLUDED" in os.environ: + # test_source_root: The root path where tests are located. + config.test_source_root = os.path.dirname(__file__) + + # test_exec_root: The root path where tests should be run. + config.test_exec_root = os.path.join( + config.modular_obj_root, "mojo-stdlib", "test" + ) +# External, public Mojo testing configuration +else: + # test_utils does not contain tests, just source code + # that we run `mojo package` on to be used by other tests + config.excludes = ["test_utils"] + + # test_source_root: The root path where tests are located. + config.test_source_root = Path(__file__).parent.resolve() + + # The `run-tests.sh` script creates the build directory for you. + build_root = Path(__file__).parent.parent.parent / "build" + + # The tests are executed inside this build directory to avoid + # polluting the source tree. + config.test_exec_root = build_root / "stdlib" + + # The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the + # open-source stdlib, we need to specify the paths to the just-built + # `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the + # `mojo` compiler would use its own `stdlib.mojopkg` it ships with which is not + # what we want. We override both the stable and nightly `mojo` import paths + # here to support both versions of the compiler. + os.environ["MODULAR_MOJO_IMPORT_PATH"] = str(build_root) + os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = str(build_root) + + # Pass through several environment variables + # to the underlying subprocesses that run the tests. + lit.llvm.initialize(lit_config, config) + lit.llvm.llvm_config.with_system_environment( + [ + "MODULAR_HOME", + "MODULAR_MOJO_IMPORT_PATH", + "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + ] + ) From bd5ccb03294762877d2e81257cc133a846646659 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 5 Jun 2024 00:00:27 -0400 Subject: [PATCH 0886/2019] [mojo] Make `Coroutine.__await__` consuming This strengthens the invariant that coroutines can only be awaited once, by forcing await to consume the coroutine. There is no other reason to hold a reference to the coroutine after it has been awaited. MODULAR_ORIG_COMMIT_REV_ID: 4dc4f8221b36752837dc51f01fa8e808c3557612 --- docs/changelog.md | 3 +++ stdlib/src/builtin/coroutine.mojo | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 67ed0f61c4..777c9630bb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -84,6 +84,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### 🦋 Changed +- `await` on a coroutine now consumes it. This strengthens the invariant that + coroutines can only be awaited once. + - Continued transition to `UnsafePointer` and unsigned byte type for strings: - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 57a5740cf0..421ff0025e 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -138,7 +138,7 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: @always_inline @__named_result(out) - fn __await__(self) -> type: + fn __await__(owned self) -> type: """Suspends the current coroutine until the coroutine is complete. Returns: @@ -152,6 +152,7 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), ) __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) + _ = self^ # ===----------------------------------------------------------------------=== # @@ -220,7 +221,7 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: @always_inline @__named_result(out) - fn __await__(self) raises -> type: + fn __await__(owned self) raises -> type: """Suspends the current coroutine until the coroutine is complete. Returns: @@ -239,5 +240,7 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: __mlir_op.`lit.ownership.mark_initialized`( __get_mvalue_as_litref(__get_nearest_error_slot()) ) + _ = self^ __mlir_op.`lit.raise`() __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) + _ = self^ From 5cf8bd5f6ea06b6587546e4363eccc21d1fb194f Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 5 Jun 2024 08:33:29 -0500 Subject: [PATCH 0887/2019] [External] [stdlib] Add `String.format` method (#41365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Add `String.format` method Partial " fix for https://github.com/modularml/mojo/issues/2761 The current PR provides one `format` method overload, it replaces occurrences of `{i}` by `args[i]`, ```mojo print( String("{1} {0} {1}").format( "Mojo", "🔥" ) ) # 🔥 Mojo 🔥 ``` It also support automatic indexing (`{}`) ```mojo print( String("Hello {} {}").format("Beautiful", "world") ) ``` ```mojo print( String("{} {}").format(True, 1.125) ) ``` We took great care to make sure it follows python behaviours. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2771 MODULAR_ORIG_COMMIT_REV_ID: aebbc30e0b8e35e93dc8ede0bce4d9bad0fc41a5 --- docs/changelog.md | 19 +++ stdlib/src/builtin/string.mojo | 234 +++++++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 116 +++++++++++++ 3 files changed, 369 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 777c9630bb..b5f1010503 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -82,6 +82,25 @@ by [@jayzhan211](https://github.com/jayzhan211)) an incremental internal migration due to reliance on the old, incorrect behaviour. ([PR #2894](https://github.com/modularml/mojo/pull/2894) by [@bgreni](https://github.com/bgreni)) +- Added `String.format` method. + ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) + + Support automatic and manual indexing of `*args`. + + Examples: + + ```mojo + print( + String("{1} Welcome to {0} {1}").format("mojo", "🔥") + ) + # 🔥 Wecome to mojo 🔥 + ``` + + ```mojo + print(String("{} {} {}").format(True, 1.125, 2)) + #True 1.125 2 + ``` + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5972fe35ae..5cb6fc53da 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2079,6 +2079,76 @@ struct String( ) return String(buf^) + @always_inline + fn format[*Ts: Stringable](self, *args: *Ts) raises -> String: + """Format a template with *args. + + Example of manual indexing: + + ```mojo + print( + String("{0} {1} {0}").format( + "Mojo", 1.125 + ) + ) #Mojo 1.125 Mojo + ``` + + Example of automatic indexing: + + ```mojo + var x = String("{} {}").format( + True, "hello world" + ) + print(x) #True hello world + ``` + + Args: + args: The substitution values. + + Parameters: + Ts: The types of the substitution values. + Are required to implement `Stringable`. + + Returns: + The template with the given values substituted. + + """ + alias num_pos_args = len(VariadicList(Ts)) + var entries = _FormatCurlyEntry.create_entries(self, num_pos_args) + + var res: String = "" + var pos_in_self = 0 + + var current_automatic_arg_index = 0 + for e in entries: + debug_assert(pos_in_self < len(self), "pos_in_self >= len(self)") + res += self[pos_in_self : e[].first_curly] + + if e[].is_escaped_brace(): + res += "}" if e[].field[Bool] else "{" + + if e[].is_manual_indexing(): + + @parameter + for i in range(num_pos_args): + if i == e[].field[Int]: + res += str(args[i]) + + if e[].is_automatic_indexing(): + + @parameter + for i in range(num_pos_args): + if i == current_automatic_arg_index: + res += str(args[i]) + current_automatic_arg_index += 1 + + pos_in_self = e[].last_curly + 1 + + if pos_in_self < len(self): + res += self[pos_in_self : len(self)] + + return res^ + # ===----------------------------------------------------------------------=== # # Utilities @@ -2209,3 +2279,167 @@ fn _calc_format_buffer_size[type: DType]() -> Int: return 64 + 1 else: return 128 + 1 # Add 1 for the terminator + + +# ===----------------------------------------------------------------------===# +# Format method structures +# ===----------------------------------------------------------------------===# + + +@value +struct _FormatCurlyEntry: + """ + Internally used by the `format()` method. + + Specifically to structure fields. + + Does not contain any substitution values. + + """ + + var first_curly: Int + """The index of an opening brace around a substitution field.""" + + var last_curly: Int + """The index of an closing brace around a substitution field.""" + + var field: Variant[ + String, # kwargs indexing (`{field_name}`) + Int, # args manual indexing (`{3}`) + NoneType, # args automatic indexing (`{}`) + Bool, # for escaped curlies ('{{') + ] + """Store the substitution field.""" + + fn is_escaped_brace(ref [_]self) -> Bool: + return self.field.isa[Bool]() + + fn is_kwargs_field(ref [_]self) -> Bool: + return self.field.isa[String]() + + fn is_automatic_indexing(ref [_]self) -> Bool: + return self.field.isa[NoneType]() + + fn is_manual_indexing(ref [_]self) -> Bool: + return self.field.isa[Int]() + + @staticmethod + fn create_entries( + format_src: String, len_pos_args: Int + ) raises -> List[Self]: + """Used internally by the `format()` method. + + Args: + format_src: The "format" part provided by the user. + len_pos_args: The len of *args + + Returns: + A `List` of structured field entries. + + Purpose of the `Variant` `Self.field`: + + - `Int` for manual indexing + (value field contains `0`) + + - `NoneType` for automatic indexing + (value field contains `None`) + + - `String` for **kwargs indexing + (value field contains `foo`) + + - `Bool` for escaped curlies + (value field contains False for `{` or True for '}') + """ + var manual_indexing_count = 0 + var automatic_indexing_count = 0 + var raised_manual_index = Optional[Int](None) + var raised_automatic_index = Optional[Int](None) + var raised_kwarg_field = Optional[String](None) + + var entries = List[Self]() + var start = Optional[Int](None) + var skip_next = False + for i in range(len(format_src)): + if skip_next: + skip_next = False + continue + if format_src[i] == "{": + if start: + # already one there. + if i - start.value() == 1: + # python escapes double curlies + var curren_entry = Self( + first_curly=start.value(), last_curly=i, field=False + ) + entries.append(curren_entry^) + start = None + continue + raise ( + "there is a single curly { left unclosed or unescaped" + ) + else: + start = i + continue + if format_src[i] == "}": + if start: + var start_value = start.value() + var current_entry = Self( + first_curly=start_value, last_curly=i, field=None + ) + if i - start_value != 1: + var field = format_src[start_value + 1 : i] + try: + # field is a number for manual indexing: + var number = int(field) + current_entry.field = number + if number >= len_pos_args or number < 0: + raised_manual_index = number + break + manual_indexing_count += 1 + except e: + debug_assert( + "not convertible to integer" in str(e), + "Not the expected error from atol", + ) + # field is an keyword for **kwargs: + current_entry.field = field + raised_kwarg_field = field + break + else: + # automatic indexing + # current_entry.field is already None + if automatic_indexing_count >= len_pos_args: + raised_automatic_index = automatic_indexing_count + break + automatic_indexing_count += 1 + entries.append(current_entry^) + start = None + else: + # python escapes double curlies + if (i + 1) < len(format_src): + if format_src[i + 1] == "}": + var curren_entry = Self( + first_curly=i, last_curly=i + 1, field=True + ) + entries.append(curren_entry^) + skip_next = True + continue + # if it is not an escaped one, it is an error + raise ( + "there is a single curly } left unclosed or unescaped" + ) + + if raised_automatic_index: + raise "Automatic indexing require more args in *args" + if raised_kwarg_field: + raise "Index " + raised_kwarg_field.value() + " not in kwargs" + if manual_indexing_count and automatic_indexing_count: + raise "Cannot both use manual and automatic indexing" + if raised_manual_index: + raise ( + "Index " + str(raised_manual_index.value()) + " not in *args" + ) + if start: + raise "there is a single curly { left unclosed or unescaped" + + return entries^ diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 68e9303d4d..937773e66c 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -1185,6 +1185,121 @@ def test_string_iter(): item_idx += 1 +def test_format_args(): + with assert_raises(contains="Index -1 not in *args"): + _ = String("{-1} {0}").format("First") + + with assert_raises(contains="Index 1 not in *args"): + _ = String("A {0} B {1}").format("First") + + with assert_raises(contains="Index 1 not in *args"): + _ = String("A {1} B {0}").format("First") + + with assert_raises(contains="Index 1 not in *args"): + _ = String("A {1} B {0}").format() + + with assert_raises( + contains="Automatic indexing require more args in *args" + ): + _ = String("A {} B {}").format("First") + + with assert_raises( + contains="Cannot both use manual and automatic indexing" + ): + _ = String("A {} B {1}").format("First", "Second") + + with assert_raises(contains="Index first not in kwargs"): + _ = String("A {first} B {second}").format(1, 2) + + assert_equal( + String(" {} , {} {} !").format( + "Hello", + "Beautiful", + "World", + ), + " Hello , Beautiful World !", + ) + + with assert_raises( + contains="there is a single curly { left unclosed or unescaped" + ): + _ = String("{ {}").format(1) + + with assert_raises( + contains="there is a single curly { left unclosed or unescaped" + ): + _ = String("{ {0}").format(1) + + with assert_raises( + contains="there is a single curly { left unclosed or unescaped" + ): + _ = String("{}{").format(1) + + with assert_raises( + contains="there is a single curly } left unclosed or unescaped" + ): + _ = String("{}}").format(1) + + with assert_raises( + contains="there is a single curly { left unclosed or unescaped" + ): + _ = String("{} {").format(1) + + with assert_raises( + contains="there is a single curly { left unclosed or unescaped" + ): + _ = String("{").format(1) + + with assert_raises( + contains="there is a single curly } left unclosed or unescaped" + ): + _ = String("}").format(1) + + assert_equal(String("}}").format(), "}") + assert_equal(String("{{").format(), "{") + + assert_equal(String("{{}}{}{{}}").format("foo"), "{}foo{}") + + assert_equal(String("{{ {0}").format("foo"), "{ foo") + assert_equal(String("{{{0}").format("foo"), "{foo") + assert_equal(String("{{0}}").format("foo"), "{0}") + assert_equal(String("{{}}").format("foo"), "{}") + assert_equal(String("{{0}}").format("foo"), "{0}") + assert_equal(String("{{{0}}}").format("foo"), "{foo}") + + var vinput = "{} {}" + var output = String(vinput).format("123", 456) + assert_equal(len(output), 7) + + vinput = "{1}{0}" + output = String(vinput).format("123", 456) + assert_equal(len(output), 6) + assert_equal(output, "456123") + + vinput = "123" + output = String(vinput).format() + assert_equal(len(output), 3) + + vinput = "" + output = String(vinput).format() + assert_equal(len(output), 0) + + assert_equal( + String("{0} {1} ❤️‍🔥 {1} {0}").format( + "🔥", + "Mojo", + ), + "🔥 Mojo ❤️‍🔥 Mojo 🔥", + ) + + assert_equal(String("{0} {1}").format(True, 1.125), "True 1.125") + + assert_equal(String("{0} {1}").format("{1}", "Mojo"), "{1} Mojo") + assert_equal( + String("{0} {1} {0} {1}").format("{1}", "Mojo"), "{1} Mojo {1} Mojo" + ) + + def main(): test_constructors() test_copy() @@ -1230,3 +1345,4 @@ def main(): test_string_mul() test_indexing() test_string_iter() + test_format_args() From eee6a7f7ee80582915d71c803eea5f00a0aa7ad3 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:02:08 -0500 Subject: [PATCH 0888/2019] [External] [stdlib] Fix `Error.__repr__` (#41366) [External] [stdlib] Fix `Error.__repr__` The current `Error.__repr__` implementation doesn't follow to the convention, so fix it to disambiguate it from a simple string. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2954 MODULAR_ORIG_COMMIT_REV_ID: e20b53b808fe8f8a2eebc1e05ad11d7751f7b649 --- stdlib/src/builtin/error.mojo | 4 ++-- stdlib/test/builtin/test_error.mojo | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 71bfc5b7a4..a563791900 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -26,7 +26,7 @@ from memory import memcmp, memcpy, UnsafePointer @register_passable -struct Error(Stringable, Boolable): +struct Error(Stringable, Boolable, Representable): """This type represents an Error.""" var data: UnsafePointer[UInt8] @@ -150,7 +150,7 @@ struct Error(Stringable, Boolable): Returns: A printable representation of the error message. """ - return str(self) + return "Error(" + repr(self._message()) + ")" @always_inline fn _message(self) -> String: diff --git a/stdlib/test/builtin/test_error.mojo b/stdlib/test/builtin/test_error.mojo index 8b5d8e92e0..87dd096cd4 100644 --- a/stdlib/test/builtin/test_error.mojo +++ b/stdlib/test/builtin/test_error.mojo @@ -32,6 +32,7 @@ def test_from_and_to_string(): assert_equal(str(error), "FOO") assert_equal(str(Error("bad")), "bad") + assert_equal(repr(Error("err")), "Error('err')") def main(): From a37a99357db30cd8350f3501f07fa34d72b73bf1 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 5 Jun 2024 07:30:45 -0700 Subject: [PATCH 0889/2019] Adds os.path.dirname using the same implementation as Python. MODULAR_ORIG_COMMIT_REV_ID: 4fa93b6ef9474505bcdd467b7502128762bbd3fe --- stdlib/src/os/path/__init__.mojo | 2 +- stdlib/src/os/path/path.mojo | 119 +++++++++++++++++--------- stdlib/test/os/path/test_dirname.mojo | 78 +++++++++++++++++ 3 files changed, 157 insertions(+), 42 deletions(-) create mode 100644 stdlib/test/os/path/test_dirname.mojo diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index 827e9ed64c..56b12d786e 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -11,4 +11,4 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from .path import exists, isdir, isfile, islink, lexists, getsize, join +from .path import exists, isdir, isfile, islink, lexists, getsize, join, dirname diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 00caaf18de..4784b7e02a 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -72,11 +72,11 @@ fn isdir(path: String) -> Bool: symbolic links, so both islink() and isdir() can be true for the same path. Args: - path: The path to the directory. + path: The path to the directory. Returns: - True if the path is a directory or a link to a directory and - False otherwise. + True if the path is a directory or a link to a directory and + False otherwise. """ _constrain_unix() try: @@ -88,19 +88,19 @@ fn isdir(path: String) -> Bool: return False -fn isdir[pathlike: os.PathLike](path: pathlike) -> Bool: +fn isdir[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path is an existing directory. This follows symbolic links, so both islink() and isdir() can be true for the same path. Parameters: - pathlike: The type conforming to the os.PathLike trait. + PathLike: The type conforming to the os.PathLike trait. Args: - path: The path to the directory. + path: The path to the directory. Returns: - True if the path is a directory or a link to a directory and - False otherwise. + True if the path is a directory or a link to a directory and + False otherwise. """ return isdir(path.__fspath__()) @@ -114,10 +114,10 @@ fn isfile(path: String) -> Bool: """Test whether a path is a regular file. Args: - path: The path to the directory. + path: The path to the directory. Returns: - Returns True if the path is a regular file. + Returns True if the path is a regular file. """ _constrain_unix() try: @@ -129,17 +129,17 @@ fn isfile(path: String) -> Bool: return False -fn isfile[pathlike: os.PathLike](path: pathlike) -> Bool: +fn isfile[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Test whether a path is a regular file. Parameters: - pathlike: The type conforming to the os.PathLike trait. + PathLike: The type conforming to the os.PathLike trait. Args: - path: The path to the directory. + path: The path to the directory. Returns: - Returns True if the path is a regular file. + Returns True if the path is a regular file. """ return isfile(path.__fspath__()) @@ -152,10 +152,10 @@ fn islink(path: String) -> Bool: symbolic link. Args: - path: The path to the directory. + path: The path to the directory. Returns: - True if the path is a link to a directory and False otherwise. + True if the path is a link to a directory and False otherwise. """ _constrain_unix() try: @@ -164,22 +164,59 @@ fn islink(path: String) -> Bool: return False -fn islink[pathlike: os.PathLike](path: pathlike) -> Bool: +fn islink[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path refers to an existing directory entry that is a symbolic link. Parameters: - pathlike: The type conforming to the os.PathLike trait. + PathLike: The type conforming to the os.PathLike trait. Args: - path: The path to the directory. + path: The path to the directory. Returns: - True if the path is a link to a directory and False otherwise. + True if the path is a link to a directory and False otherwise. """ return islink(path.__fspath__()) +# ===----------------------------------------------------------------------=== # +# dirname +# ===----------------------------------------------------------------------=== # + + +fn dirname(path: String) -> String: + """Returns the directory component of a pathname. + + Args: + path: The path to a file. + + Returns: + The directory component of a pathname. + """ + alias sep = str(os.sep) + var i = path.rfind(sep) + 1 + var head = path[:i] + if head and head != sep * len(head): + return head.rstrip(sep) + return head + + +fn dirname[PathLike: os.PathLike, //](path: PathLike) -> String: + """Returns the directory component of a pathname. + + Parameters: + PathLike: The type conforming to the os.PathLike trait. + + Args: + path: The path to a file. + + Returns: + The directory component of a pathname. + """ + return dirname(path.__fspath__()) + + # ===----------------------------------------------------------------------=== # # exists # ===----------------------------------------------------------------------=== # @@ -189,10 +226,10 @@ fn exists(path: String) -> Bool: """Return True if path exists. Args: - path: The path to the directory. + path: The path to the directory. Returns: - Returns True if the path exists and is not a broken symbolic link. + Returns True if the path exists and is not a broken symbolic link. """ _constrain_unix() try: @@ -202,17 +239,17 @@ fn exists(path: String) -> Bool: return False -fn exists[pathlike: os.PathLike](path: pathlike) -> Bool: +fn exists[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path exists. Parameters: - pathlike: The type conforming to the os.PathLike trait. + PathLike: The type conforming to the os.PathLike trait. Args: - path: The path to the directory. + path: The path to the directory. Returns: - Returns True if the path exists and is not a broken symbolic link. + Returns True if the path exists and is not a broken symbolic link. """ return exists(path.__fspath__()) @@ -226,10 +263,10 @@ fn lexists(path: String) -> Bool: """Return True if path exists or is a broken symlink. Args: - path: The path to the directory. + path: The path to the directory. Returns: - Returns True if the path exists or is a broken symbolic link. + Returns True if the path exists or is a broken symbolic link. """ _constrain_unix() try: @@ -239,17 +276,17 @@ fn lexists(path: String) -> Bool: return False -fn lexists[pathlike: os.PathLike](path: pathlike) -> Bool: +fn lexists[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path exists or is a broken symlink. Parameters: - pathlike: The type conforming to the os.PathLike trait. + PathLike: The type conforming to the os.PathLike trait. Args: - path: The path to the directory. + path: The path to the directory. Returns: - Returns True if the path exists or is a broken symbolic link. + Returns True if the path exists or is a broken symbolic link. """ return exists(path.__fspath__()) @@ -263,25 +300,25 @@ fn getsize(path: String) raises -> Int: """Return the size, in bytes, of the specified path. Args: - path: The path to the file. + path: The path to the file. Returns: - The size of the path in bytes. + The size of the path in bytes. """ return stat(path).st_size -fn getsize[pathlike: os.PathLike](path: pathlike) raises -> Int: +fn getsize[PathLike: os.PathLike, //](path: PathLike) raises -> Int: """Return the size, in bytes, of the specified path. Parameters: - pathlike: The type conforming to the os.PathLike trait. + PathLike: The type conforming to the os.PathLike trait. Args: - path: The path to the file. + path: The path to the file. Returns: - The size of the path in bytes. + The size of the path in bytes. """ return getsize(path.__fspath__()) @@ -298,11 +335,11 @@ fn join(path: String, *paths: String) -> String: ends with a separator. Args: - path: The path to join. - paths: The paths to join. + path: The path to join. + paths: The paths to join. Returns: - The joined path. + The joined path. """ var joined_path = path diff --git a/stdlib/test/os/path/test_dirname.mojo b/stdlib/test/os/path/test_dirname.mojo new file mode 100644 index 0000000000..97580f295e --- /dev/null +++ b/stdlib/test/os/path/test_dirname.mojo @@ -0,0 +1,78 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: !windows +# RUN: %mojo %s + + +from os.path import dirname +from testing import assert_equal + + +fn main() raises: + # Root directories + assert_equal("/", dirname("/")) + + # Empty strings + assert_equal("", dirname("")) + + # Current directory (matching behavior of python, doesn't resolve `..` etc.) + assert_equal("", dirname(".")) + + # Parent directory + assert_equal("", dirname("..")) + + # Absolute paths + assert_equal("/", dirname("/file")) + assert_equal("/dir", dirname("/dir/file")) + assert_equal("/dir/subdir", dirname("/dir/subdir/file")) + + # Relative paths + assert_equal("dir", dirname("dir/file")) + assert_equal("dir/subdir", dirname("dir/subdir/file")) + assert_equal("", dirname("file")) + + # Trailing slashes + assert_equal("/path/to", dirname("/path/to/")) + assert_equal("/path/to/dir", dirname("/path/to/dir/")) + + # Multiple slashes + assert_equal("/path/to", dirname("/path/to//file")) + assert_equal("/path", dirname("/path//to")) + + # Paths with spaces + assert_equal("/path to", dirname("/path to/file")) + assert_equal("/path to/dir", dirname("/path to/dir/file")) + + # Paths with special characters + assert_equal("/path-to", dirname("/path-to/file")) + assert_equal("/path_to/dir", dirname("/path_to/dir/file")) + + # Paths with dots + assert_equal("/path/./to", dirname("/path/./to/file")) + assert_equal("/path/../to", dirname("/path/../to/file")) + + # Paths with double dots + assert_equal("/path/..", dirname("/path/../file")) + assert_equal("/path/to/..", dirname("/path/to/../file")) + + # Root and relative mixed + assert_equal("/dir/.", dirname("/dir/./file")) + assert_equal("/dir/subdir/..", dirname("/dir/subdir/../file")) + + # Edge cases + assert_equal("/.", dirname("/./file")) + assert_equal("/..", dirname("/../file")) + + # Unix hidden files + assert_equal("/path/to", dirname("/path/to/.hiddenfile")) + assert_equal("/path/to/dir", dirname("/path/to/dir/.hiddenfile")) From e17ad79161cf3890da07b7b5a6bb9ed5ff73912e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 5 Jun 2024 11:12:56 -0400 Subject: [PATCH 0890/2019] [mojo] Continue tightening `Coroutine.__await__` invariants (NFC) MODULAR_ORIG_COMMIT_REV_ID: 267d4f9f0b010d4bd3aa1a16393e068ac28f629b --- stdlib/src/builtin/coroutine.mojo | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 421ff0025e..75c6801d65 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -147,12 +147,13 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: # Black magic! Internal implementation detail! # Don't you dare copy this code! 😤 + var handle = self._handle + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) __mlir_op.`co.await`[_type=NoneType]( - self._handle, + handle, __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), ) __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) - _ = self^ # ===----------------------------------------------------------------------=== # @@ -230,8 +231,10 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: # Black magic! Internal implementation detail! # Don't you dare copy this code! 😤 + var handle = self._handle + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) if __mlir_op.`co.await`[_type = __mlir_type.i1]( - self._handle, + handle, __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), __mlir_op.`lit.ref.to_pointer`( __get_mvalue_as_litref(__get_nearest_error_slot()) @@ -240,7 +243,5 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: __mlir_op.`lit.ownership.mark_initialized`( __get_mvalue_as_litref(__get_nearest_error_slot()) ) - _ = self^ __mlir_op.`lit.raise`() __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) - _ = self^ From b45915853393eb597cf63c9ec64767e624b2a892 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 5 Jun 2024 08:58:54 -0700 Subject: [PATCH 0891/2019] Adds a new environment variable `MOJO_PYTHON` which you can point to any python executable on your system. Thi will force Mojo to use a libpython of the same version. If you point it to a virtual environment executable, Mojo will also have access to the associated modules without having to activate the environment. MODULAR_ORIG_COMMIT_REV_ID: d5bb8540d6cc6c48a686f06f2a6fd9332333e0ec --- stdlib/src/python/_cpython.mojo | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 7604965006..05e3a82927 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -162,9 +162,15 @@ struct CPython: """ if self.init_error: var error: String = self.init_error - error += "\nMOJO_PYTHON: " + getenv("MOJO_PYTHON") - error += "\nMOJO_PYTHON_LIBRARY: " + getenv("MOJO_PYTHON_LIBRARY") - error += "\nPYTHONEXECUTABLE: " + getenv("PYTHONEXECUTABLE") + var mojo_python = getenv("MOJO_PYTHON") + var python_lib = getenv("MOJO_PYTHON_LIBRARY") + var python_exe = getenv("PYTHONEXECUTABLE") + if mojo_python: + error += "\nMOJO_PYTHON: " + mojo_python + if python_lib: + error += "\nMOJO_PYTHON_LIBRARY: " + python_lib + if python_exe: + error += "\npython executable: " + python_exe error += "\n\nMojo/Python interop error, troubleshooting docs at:" error += "\n https://modul.ar/fix-python\n" raise error From fef547feab291848e38ffc55ee63d027c8b96be0 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 5 Jun 2024 08:59:57 -0700 Subject: [PATCH 0892/2019] This makes Mojo behave like Python, allowing you to access python modules from the same directory as the target file. MODULAR_ORIG_COMMIT_REV_ID: 689a194957985f84fa29fc836caaa26386410c8c --- stdlib/src/python/_cpython.mojo | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 05e3a82927..aed83aa135 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -11,8 +11,11 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from os import getenv -from sys import external_call, exit +from os import getenv, setenv +from pathlib import Path +from os.path import dirname +from sys import external_call +from sys.arg import argv from sys.ffi import DLHandle from memory import DTypePointer, UnsafePointer @@ -113,6 +116,20 @@ struct CPython: print("MOJO_PYTHON:", getenv("MOJO_PYTHON")) print("MOJO_PYTHON_LIBRARY:", getenv("MOJO_PYTHON_LIBRARY")) + # Add directory of target file to top of sys.path to find python modules + var file_dir = dirname(argv()[0]) + if Path(file_dir).is_dir() or file_dir == "": + var python_path = getenv("PYTHONPATH") + # A leading `:` will put the current dir at the top of sys.path. + # If we're doing `mojo run main.mojo` or `./main`, the returned + # `dirname` will be an empty string. + if file_dir == "" and not python_path: + file_dir = ":" + if python_path: + _ = setenv("PYTHONPATH", file_dir + ":" + python_path) + else: + _ = setenv("PYTHONPATH", file_dir) + # TODO(MOCO-772) Allow raises to propagate through function pointers # and make this initialization a raising function. self.init_error = external_call[ From 1cb26ddea7d1b910f173131139cb141e7c9c23c2 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Wed, 5 Jun 2024 11:23:59 -0500 Subject: [PATCH 0893/2019] [External] [stdlib] Cleanup sort module (#41378) [External] [stdlib] Cleanup sort module Just a few renaming and removal of the Comparable elements list sorting section as it can be delegated to other sort function Co-authored-by: Maxim Zaks Closes modularml/mojo#2957 MODULAR_ORIG_COMMIT_REV_ID: f357a8e1943578fbf330b11e29fb6259f8e60b0a --- stdlib/src/builtin/sort.mojo | 158 +++++++++-------------------- stdlib/test/builtin/test_sort.mojo | 6 +- 2 files changed, 48 insertions(+), 116 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index c6f85ceffd..c9ed9e03fc 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -27,6 +27,7 @@ from sys import bitwidthof alias _cmp_fn_type = fn[type: AnyTrivialRegType] (type, type) capturing -> Bool +@always_inline fn _insertion_sort[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], start: Int, end: Int): @@ -46,6 +47,7 @@ fn _insertion_sort[ array[j] = value +@always_inline fn _insertion_sort[ type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool ](array: UnsafePointer[type], start: Int, end: Int): @@ -125,12 +127,14 @@ fn _partition[ return right +@always_inline fn _estimate_initial_height(size: Int) -> Int: # Compute the log2 of the size rounded upward. var log2 = int((bitwidthof[DType.index]() - 1) ^ countl_zero(size | 1)) return max(2, log2) +@always_inline fn _quicksort[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], size: Int): @@ -177,6 +181,7 @@ fn _quicksort[ stack.append(pivot) +@always_inline fn _quicksort[ type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool ](array: UnsafePointer[type], size: Int): @@ -213,12 +218,12 @@ fn _quicksort[ fn partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](buff: Pointer[type], k: Int, size: Int): - """Partition the input vector inplace such that first k elements are the + """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is <= operator) elements. The ordering of the first k elements is undefined. Parameters: - type: DType of the underlying data. + type: Trivial reg type of the underlying data. cmp_fn: Comparison functor of type, type) capturing -> Bool type. Args: @@ -249,8 +254,8 @@ fn partition[ fn sort(inout buff: Pointer[Int], len: Int): - """Sort the vector inplace. - The function doesn't return anything, the vector is updated inplace. + """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Args: buff: Input buffer. @@ -265,8 +270,8 @@ fn sort(inout buff: Pointer[Int], len: Int): fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): - """Sort the vector inplace. - The function doesn't return anything, the vector is updated inplace. + """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Parameters: type: DType of the underlying data. @@ -283,49 +288,66 @@ fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): _quicksort[Scalar[type], _less_than_equal](buff, len) -fn sort(inout v: List[Int]): - """Sort the vector inplace. - The function doesn't return anything, the vector is updated inplace. +fn sort(inout list: List[Int]): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. Args: - v: Input integer vector to sort. + list: Input integer list to sort. """ # Downcast any pointer to register-passable pointer. - var ptr = rebind[Pointer[Int]](v.data) - sort(ptr, len(v)) + var ptr = rebind[Pointer[Int]](list.data) + sort(ptr, len(list)) -fn sort[type: DType](inout v: List[Scalar[type]]): - """Sort the vector inplace. - The function doesn't return anything, the vector is updated inplace. +fn sort[type: DType](inout list: List[Scalar[type]]): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. Parameters: type: DType of the underlying data. Args: - v: Input vector to sort. + list: Input vector to sort. """ - var ptr = rebind[Pointer[Scalar[type]]](v.data) - sort[type](ptr, len(v)) + var ptr = rebind[Pointer[Scalar[type]]](list.data) + sort[type](ptr, len(list)) fn sort[ type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool, -](inout v: List[type]): - """Sort the vector inplace. - The function doesn't return anything, the vector is updated inplace. +](inout list: List[type]): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. Parameters: - type: DType of the underlying data. + type: CollectionElement type of the underlying data. cmp_fn: The comparison function. Args: - v: Input vector to sort. + list: Input list to sort. + """ + + _quicksort[type, cmp_fn](list.data, len(list)) + + +fn sort[type: ComparableCollectionElement](inout list: List[type]): + """Sort list of the order comparable elements in-place. + + Parameters: + type: The order comparable collection element type. + + Args: + list: The list of the scalars which will be sorted in-place. """ - _quicksort[type, cmp_fn](v.data, len(v)) + @parameter + fn _less_than_equal(a: type, b: type) -> Bool: + return a <= b + + _quicksort[type, _less_than_equal](list.data, len(list)) # ===----------------------------------------------------------------------===# @@ -395,91 +417,3 @@ fn _small_sort[ _sort_partial_3[type, cmp_fn](array, 0, 2, 3) _sort_partial_3[type, cmp_fn](array, 1, 2, 3) return - - -# ===----------------------------------------------------------------------=== # -# Comparable elements list sorting -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn insertion_sort[type: ComparableCollectionElement](inout list: List[type]): - """Sort list of the order comparable elements in-place with insertion sort algorithm. - - Parameters: - type: The order comparable collection element type. - - Args: - list: The list of the order comparable elements which will be sorted in-place. - """ - for i in range(1, len(list)): - var key = list[i] - var j = i - 1 - while j >= 0 and key < list[j]: - list[j + 1] = list[j] - j -= 1 - list[j + 1] = key - - -fn _quick_sort[ - type: ComparableCollectionElement -](inout list: List[type], low: Int, high: Int): - """Sort section of the list, between low and high, with quick sort algorithm in-place. - - Parameters: - type: The order comparable collection element type. - - Args: - list: The list of the order comparable elements which will be sorted in-place. - low: Int value identifying the lowest index of the list section to be sorted. - high: Int value identifying the highest index of the list section to be sorted. - """ - - @always_inline - @parameter - fn _partition(low: Int, high: Int) -> Int: - var pivot = list[high] - var i = low - 1 - for j in range(low, high): - if list[j] <= pivot: - i += 1 - list[j], list[i] = list[i], list[j] - list[i + 1], list[high] = list[high], list[i + 1] - return i + 1 - - if low < high: - var pi = _partition(low, high) - _quick_sort(list, low, pi - 1) - _quick_sort(list, pi + 1, high) - - -@always_inline -fn quick_sort[type: ComparableCollectionElement](inout list: List[type]): - """Sort list of the order comparable elements in-place with quick sort algorithm. - - Parameters: - type: The order comparable collection element type. - - Args: - list: The list of the order comparable elements which will be sorted in-place. - """ - _quick_sort(list, 0, len(list) - 1) - - -fn sort[ - type: ComparableCollectionElement, slist_ub: Int = 64 -](inout list: List[type]): - """Sort list of the order comparable elements in-place. This function picks the best algorithm based on the list length. - - Parameters: - type: The order comparable collection element type. - slist_ub: The upper bound for a list size which is considered small. - - Args: - list: The list of the scalars which will be sorted in-place. - """ - var count = len(list) - if count <= slist_ub: - insertion_sort(list) # small lists are best sorted with insertion sort - else: - quick_sort(list) # others are best sorted with quick sort diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 8a4898528b..2e9fe2d710 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -595,11 +595,9 @@ def test_sort_comparamble_elements_list(): assert_sorted(list) -fn test_sort_empty_comparamble_elements_list() raises: +fn test_sort_empty_comparable_elements_list() raises: var person_list = List[Person]() sort(person_list) - insertion_sort(person_list) - quick_sort(person_list) assert_true(len(person_list) == 0) @@ -631,4 +629,4 @@ def main(): test_sort_string_big_list() test_sort_strings() test_sort_comparamble_elements_list() - test_sort_empty_comparamble_elements_list() + test_sort_empty_comparable_elements_list() From a3a4c37c057ed2fb85c0488f5e231ab1c61df67c Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:15:06 -0500 Subject: [PATCH 0894/2019] [External] [stdlib] Add oct function (#41314) [External] [stdlib] Add oct function Add the `oct` builtin function to get the octal representation of integers. Follow up from #2730 --------- Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Co-authored-by: Connor Gray Closes modularml/mojo#2914 MODULAR_ORIG_COMMIT_REV_ID: 79696c73dd4e9ea617fe3ea970e95fb37b6035f7 --- stdlib/src/builtin/bin.mojo | 85 ------------------------ stdlib/src/builtin/format_int.mojo | 69 +++++++++++++++++++ stdlib/test/builtin/test_format_int.mojo | 44 ++++++++++++ 3 files changed, 113 insertions(+), 85 deletions(-) delete mode 100644 stdlib/src/builtin/bin.mojo diff --git a/stdlib/src/builtin/bin.mojo b/stdlib/src/builtin/bin.mojo deleted file mode 100644 index 1cf6fb447a..0000000000 --- a/stdlib/src/builtin/bin.mojo +++ /dev/null @@ -1,85 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -"""Implements the `bin()` function. - -These are Mojo built-ins, so you don't need to import them. -""" - - -# Need this until we have constraints to stop the compiler from matching this -# directly to bin[type: DType](num: Scalar[type]). -@always_inline("nodebug") -fn bin(b: Scalar[DType.bool], /) -> String: - """Returns the binary representation of a scalar bool. - - Args: - b: A scalar bool value. - - Returns: - The binary string representation of b. - """ - return bin(int(b)) - - -fn bin[type: DType](num: Scalar[type], /) -> String: - """Return the binary string representation an integral value. - - ```mojo - print(bin(123)) - print(bin(-123)) - ``` - ```plaintext - '0b1111011' - '-0b1111011' - ``` - - Parameters: - type: The data type of the integral scalar. - - Args: - num: An integral scalar value. - - Returns: - The binary string representation of num. - """ - constrained[type.is_integral(), "Expected integral value"]() - alias BIN_PREFIX = "0b" - - if num == 0: - return BIN_PREFIX + "0" - - # TODD: pre-allocate string size when #2194 is resolved - var result = String() - var cpy = abs(num) - while cpy > 0: - result += str(cpy & 1) - cpy = cpy >> 1 - - result = BIN_PREFIX + result[::-1] - return "-" + result if num < 0 else result - - -@always_inline("nodebug") -fn bin[T: Indexer](num: T, /) -> String: - """Returns the binary representation of an indexer type. - - Parameters: - T: The Indexer type. - - Args: - num: An indexer value. - - Returns: - The binary string representation of num. - """ - return bin(Scalar[DType.index](index(num))) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 28bcf94b18..b9e99cf423 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -156,6 +156,75 @@ fn hex[prefix: StringLiteral = "0x"](value: Scalar[DType.bool], /) -> String: return hex[prefix](value.cast[DType.int8]()) +# ===----------------------------------------------------------------------===# +# oct +# ===----------------------------------------------------------------------===# + + +@always_inline +fn oct[prefix: StringLiteral = "0o"](value: Scalar, /) -> String: + """Returns the octal string representation of the given integer. + + The octal representation is a base-8 encoding of the integer value. + + The returned string will be prefixed with "0o" to indicate that the + subsequent digits are octal. + + Parameters: + prefix: The prefix of the formatted int. + + Args: + value: The integer value to format. + + Returns: + A string containing the octal representation of the given integer. + """ + return _try_format_int[prefix](value, 8) + + +@always_inline +fn oct[T: Indexer, //, prefix: StringLiteral = "0o"](value: T, /) -> String: + """Returns the octal string representation of the given integer. + + The octal representation is a base-8 encoding of the integer value. + + The returned string will be prefixed with "0o" to indicate that the + subsequent digits are octal. + + Parameters: + T: The indexer type to represent in octal. + prefix: The prefix of the formatted int. + + Args: + value: The integer value to format. + + Returns: + A string containing the octal representation of the given integer. + """ + return oct[prefix](Scalar[DType.index](index(value))) + + +@always_inline +fn oct[prefix: StringLiteral = "0o"](value: Scalar[DType.bool], /) -> String: + """Returns the octal string representation of the given scalar bool. + + The octal representation is a base-8 encoding of the bool. + + The returned string will be prefixed with "0o" to indicate that the + subsequent digits are octal. + + Parameters: + prefix: The prefix of the formatted int. + + Args: + value: The bool value to format. + + Returns: + A string containing the octal representation of the given bool. + """ + return oct[prefix](value.cast[DType.int8]()) + + # ===----------------------------------------------------------------------===# # Integer formatting utilities # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_format_int.mojo b/stdlib/test/builtin/test_format_int.mojo index 88f73faca8..228be3da42 100644 --- a/stdlib/test/builtin/test_format_int.mojo +++ b/stdlib/test/builtin/test_format_int.mojo @@ -106,9 +106,50 @@ def test_bin_bool(): assert_equal(bin(False), "0b0") +def test_oct_scalar(): + assert_equal(oct(Int32(234)), "0o352") + assert_equal(oct(Int32(-23)), "-0o27") + assert_equal(oct(Int32(0)), "0o0") + assert_equal(oct(Scalar[DType.bool](True)), "0o1") + assert_equal(oct(Scalar[DType.bool](False)), "0o0") + + +def test_oct_int(): + assert_equal(oct(768), "0o1400") + assert_equal(oct(-12), "-0o14") + assert_equal(oct(23623564), "0o132073614") + assert_equal(oct(0), "0o0") + assert_equal(oct(1), "0o1") + assert_equal(oct(Int(7658)), "0o16752") + + +def test_oct_bool(): + assert_equal(oct(True), "0o1") + assert_equal(oct(False), "0o0") + + def test_indexer(): assert_equal(bin(Ind()), "0b1") assert_equal(hex(Ind()), "0x1") + assert_equal(oct(Ind()), "0o1") + + +def test_different_prefix(): + assert_equal(bin["binary"](Int8(1)), "binary1") + assert_equal(hex["hexidecimal"](Int8(1)), "hexidecimal1") + assert_equal(oct["octal"](Int8(1)), "octal1") + + assert_equal(bin["binary"](0), "binary0") + assert_equal(hex["hexidecimal"](0), "hexidecimal0") + assert_equal(oct["octal"](0), "octal0") + + assert_equal(bin["I'mAnIndexer!"](Ind()), "I'mAnIndexer!1") + assert_equal(hex["I'mAnIndexer!"](Ind()), "I'mAnIndexer!1") + assert_equal(oct["I'mAnIndexer!"](Ind()), "I'mAnIndexer!1") + + assert_equal(bin["test"](Scalar[DType.bool](True)), "test1") + assert_equal(hex["test"](Scalar[DType.bool](True)), "test1") + assert_equal(oct["test"](Scalar[DType.bool](True)), "test1") def main(): @@ -118,3 +159,6 @@ def main(): test_bin_int() test_bin_bool() test_indexer() + test_oct_scalar() + test_oct_bool() + test_oct_int() From fcacaf1af7e1af1c644d9197fb2f1ae4de99f84c Mon Sep 17 00:00:00 2001 From: Yihua Lou <1237500+yihualou@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:52:40 -0700 Subject: [PATCH 0895/2019] Internal commit MODULAR_ORIG_COMMIT_REV_ID: 2f8a201b831b96924eb02e4c2b316f106f2ab8e6 --- stdlib/src/python/_cpython.mojo | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index aed83aa135..7289868c99 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -241,11 +241,17 @@ struct CPython: self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) self._dec_total_rc() - fn PyGILState_Ensure(inout self) -> Int64: - return self.lib.get_function[fn () -> Int64]("PyGILState_Ensure")() + fn PyGILState_Ensure(inout self) -> Bool: + return self.lib.get_function[fn () -> Bool]("PyGILState_Ensure")() - fn PyGILState_Release(inout self, state: Int64): - self.lib.get_function[fn (Int64) -> None]("PyGILState_Release")(state) + fn PyGILState_Release(inout self, state: Bool): + self.lib.get_function[fn (Bool) -> None]("PyGILState_Release")(state) + + fn PyEval_SaveThread(inout self) -> Int64: + return self.lib.get_function[fn () -> Int64]("PyEval_SaveThread")() + + fn PyEval_RestoreThread(inout self, state: Int64): + self.lib.get_function[fn (Int64) -> None]("PyEval_RestoreThread")(state) # This function assumes a specific way PyObjectPtr is implemented, namely # that the refcount has offset 0 in that structure. That generally doesn't From 4874d9266b661539e0729cc2954d81db5df1c894 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:07:40 -0500 Subject: [PATCH 0896/2019] [External] [stdlib] Fix possible bug in List.resize with default value T (#41395) [External] [stdlib] Fix possible bug in List.resize with default value T There might be a bug in: `List.resize(inout self, new_size: Int, value: T)` It calls `destroy_pointee` on uninitialized values (`self.reserve(new_size)`), it is used with trivial type `Int` (no-op destructor) in stdlib so no bug yet? Can someone have a look internally? Not sure if it a bug or not, but it seem important to check! Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2915 MODULAR_ORIG_COMMIT_REV_ID: 85082cd9cbf18d99f0d09748b33b6fdeed94c7ab --- stdlib/src/collections/list.mojo | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 4779213eb6..2f975cc991 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -565,8 +565,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.resize(new_size) else: self.reserve(new_size) - for i in range(new_size, self.size): - (self.data + i).destroy_pointee() for i in range(self.size, new_size): (self.data + i).init_pointee_copy(value) self.size = new_size From 411571dcae4784e6d6472aab705621dff1708109 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 5 Jun 2024 14:08:13 -0500 Subject: [PATCH 0897/2019] [External] [stdlib] Remove attribute `self._reserved` from `Dict` (#41393) [External] [stdlib] Remove attribute `self._reserved` from `Dict` ### Rationale: Our Dict implementation is too complicated for what it does. We have to keep `self._reserved` updated so that it's always equal to `len(self._entries)`. That's 1) taking memory (64 bits) for nothing 2) is a potential source of bugs as `self._reserved` could become out of sync with `len(self._entries)` in future code changes. We could use `len(self._entries)` everywhere, but from an algorithmic point of view, I guess it's also easier to read `self._reserved`. Since we don't have `@property` available in Mojo yet, I settled with ```mojo fn _reserved(self) -> Int: return len(self._entries) ``` ### Using all the capacity available in the List I also added another optimization here, to make the dict compatible in the future with small buffer optimization: If we ask `self._entries` to allocate for N elements, but the List allocator tell us that now the capacity of the list is M, then we use M as the size of the list (and also the capacity). Concretely, let's say that we have small buffer optimization and that we ask for a list with a capacity of 8, it may be possible that the list has a small buffer of size 16, thus giving us a list of capacity 16 (the minimum) when we asked 8. Then we can use the 16 slots available. This is also good in case we later on allow Dict to grab another structure's pointer/list, as we'll use all the memory available. If the second optimization is not a good idea, I can remove it. There might be some requirement (always be a power of 2 for example) that I'm not aware of and in this case we shouldn't let `List` decide of the capacity of our Dict. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2905 MODULAR_ORIG_COMMIT_REV_ID: 066be7c692f8effeba508574f753d2acbbb358b0 --- stdlib/src/builtin/reversed.mojo | 2 +- stdlib/src/collections/dict.mojo | 63 +++++++++++++++++--------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 37e579741a..f8cb7996df 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -169,5 +169,5 @@ fn reversed[ """ var src = value.src return _DictEntryIter[K, V, dict_lifetime, False]( - src[]._reserved - 1, 0, src + src[]._reserved() - 1, 0, src ) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 3647ebed34..875e03eb1e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -162,7 +162,7 @@ struct _DictValueIter[ var src = self.iter.src return _DictValueIter( _DictEntryIter[K, V, dict_lifetime, False]( - src[]._reserved - 1, 0, src + src[]._reserved() - 1, 0, src ) ) @@ -413,10 +413,11 @@ struct Dict[K: KeyElement, V: CollectionElement]( """The number of elements currently stored in the dict.""" var _n_entries: Int """The number of entries currently allocated.""" - var _reserved: Int - """The current reserved size of the dictionary.""" var _index: _DictIndex + + # We use everything available in the list. Which means that + # len(self._entries) == self._entries.capacity == self._reserved() var _entries: List[Optional[DictEntry[K, V]]] # ===-------------------------------------------------------------------===# @@ -428,9 +429,14 @@ struct Dict[K: KeyElement, V: CollectionElement]( """Initialize an empty dictiontary.""" self.size = 0 self._n_entries = 0 - self._reserved = Self._initial_reservation - self._index = _DictIndex(self._reserved) - self._entries = Self._new_entries(self._reserved) + self._entries = Self._new_entries(Self._initial_reservation) + self._index = _DictIndex(len(self._entries)) + + # TODO: add @property when Mojo supports it to make + # it possible to do `self._reserved`. + @always_inline + fn _reserved(self) -> Int: + return len(self._entries) @always_inline fn __init__(inout self, existing: Self): @@ -441,8 +447,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ self.size = existing.size self._n_entries = existing._n_entries - self._reserved = existing._reserved - self._index = existing._index.copy(existing._reserved) + self._index = existing._index.copy(existing._reserved()) self._entries = existing._entries @staticmethod @@ -487,8 +492,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ self.size = existing.size self._n_entries = existing._n_entries - self._reserved = existing._reserved - self._index = existing._index.copy(existing._reserved) + self._index = existing._index.copy(existing._reserved()) self._entries = existing._entries fn __moveinit__(inout self, owned existing: Self): @@ -499,7 +503,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ self.size = existing.size self._n_entries = existing._n_entries - self._reserved = existing._reserved self._index = existing._index^ self._entries = existing._entries^ @@ -577,7 +580,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( A reversed iterator of immutable references to the dict keys. """ return _DictKeyIter( - _DictEntryIter[forward=False](self._reserved - 1, 0, self) + _DictEntryIter[forward=False](self._reserved() - 1, 0, self) ) fn __or__(self, other: Self) -> Self: @@ -848,15 +851,15 @@ struct Dict[K: KeyElement, V: CollectionElement]( """Remove all elements from the dictionary.""" self.size = 0 self._n_entries = 0 - self._reserved = Self._initial_reservation - self._index = _DictIndex(self._reserved) - self._entries = Self._new_entries(self._reserved) + self._entries = Self._new_entries(Self._initial_reservation) + self._index = _DictIndex(self._reserved()) @staticmethod @always_inline - fn _new_entries(reserved: Int) -> List[Optional[DictEntry[K, V]]]: - var entries = List[Optional[DictEntry[K, V]]](capacity=reserved) - for i in range(reserved): + fn _new_entries(reserve_at_least: Int) -> List[Optional[DictEntry[K, V]]]: + var entries = List[Optional[DictEntry[K, V]]](capacity=reserve_at_least) + # We have memory available, we'll use everything. + for i in range(entries.capacity): entries.append(None) return entries @@ -877,18 +880,18 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._n_entries += 1 fn _get_index(self, slot: Int) -> Int: - return self._index.get_index(self._reserved, slot) + return self._index.get_index(self._reserved(), slot) fn _set_index(inout self, slot: Int, index: Int): - return self._index.set_index(self._reserved, slot, index) + return self._index.set_index(self._reserved(), slot, index) fn _next_index_slot(self, inout slot: Int, inout perturb: UInt64): alias PERTURB_SHIFT = 5 perturb >>= PERTURB_SHIFT - slot = ((5 * slot) + int(perturb + 1)) % self._reserved + slot = ((5 * slot) + int(perturb + 1)) % self._reserved() fn _find_empty_index(self, hash: Int) -> Int: - var slot = hash % self._reserved + var slot = hash % self._reserved() var perturb = bitcast[DType.uint64](Int64(hash)) while True: var index = self._get_index(slot) @@ -898,7 +901,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _find_index(self, hash: Int, key: K) -> (Bool, Int, Int): # Return (found, slot, index) - var slot = hash % self._reserved + var slot = hash % self._reserved() var perturb = bitcast[DType.uint64](Int64(hash)) while True: var index = self._get_index(slot) @@ -914,22 +917,22 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._next_index_slot(slot, perturb) fn _over_load_factor(self) -> Bool: - return 3 * self.size > 2 * self._reserved + return 3 * self.size > 2 * self._reserved() fn _over_compact_factor(self) -> Bool: - return 4 * self._n_entries > 3 * self._reserved + return 4 * self._n_entries > 3 * self._reserved() fn _maybe_resize(inout self): if not self._over_load_factor(): if self._over_compact_factor(): self._compact() return - self._reserved *= 2 + var _reserved = self._reserved() * 2 self.size = 0 self._n_entries = 0 - self._index = _DictIndex(self._reserved) var old_entries = self._entries^ - self._entries = self._new_entries(self._reserved) + self._entries = self._new_entries(_reserved) + self._index = _DictIndex(self._reserved()) for i in range(len(old_entries)): var entry = old_entries.__get_ref(i) @@ -937,12 +940,12 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._insert(entry[].unsafe_take()) fn _compact(inout self): - self._index = _DictIndex(self._reserved) + self._index = _DictIndex(self._reserved()) var right = 0 for left in range(self.size): while not self._entries.__get_ref(right)[]: right += 1 - debug_assert(right < self._reserved, "Invalid dict state") + debug_assert(right < self._reserved(), "Invalid dict state") var entry = self._entries.__get_ref(right) debug_assert(entry[].__bool__(), "Logic error") var slot = self._find_empty_index(entry[].value().hash) From 8964a9f3363c2492ec21736d9557c406910eb7af Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 5 Jun 2024 12:19:19 -0700 Subject: [PATCH 0898/2019] [Docs] Document default arg convention, clean up decorators - Updates doc on the `def` function default argument convention. This touches several docs. - Remove the reference page for `@unroll` and add `@parameter for` to the `@parameter` page. - Update the reference page for `@always-inline("nodebug")` to describe intended use. MODULAR_ORIG_COMMIT_REV_ID: bdffa21b3c61049af74e75100df688a561c1a429 --- docs/faq.md | 22 +-- docs/manual/decorators/always-inline.ipynb | 8 +- docs/manual/decorators/index.md | 1 - docs/manual/decorators/parameter.ipynb | 51 +++++- docs/manual/decorators/unroll.ipynb | 164 ----------------- docs/manual/functions.ipynb | 197 ++++++++++++--------- docs/manual/index.md | 51 +++--- docs/manual/values/ownership.ipynb | 151 +++++++++------- docs/manual/values/value-semantics.ipynb | 48 ++--- docs/roadmap.md | 6 +- 10 files changed, 320 insertions(+), 379 deletions(-) delete mode 100644 docs/manual/decorators/unroll.ipynb diff --git a/docs/faq.md b/docs/faq.md index 59e986cde3..a6d385cbc0 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -6,7 +6,7 @@ description: Answers to questions we expect about Mojo. We tried to anticipate your questions about Mojo on this page. If this page doesn't answer all your questions, also check out our [Mojo community -channels](/mojo/community.html). +channels](/mojo/community). ## Motivation @@ -20,7 +20,7 @@ to unify AI software and we can’t do that without a unified language that can scale across the AI infrastructure stack. That said, we don’t plan to stop at AI—the north star is for Mojo to support the whole gamut of general-purpose programming over time. For a longer answer, read [Why -Mojo](/mojo/why-mojo.html). +Mojo](/mojo/why-mojo). ### Why is it called Mojo? @@ -43,7 +43,7 @@ it’s missing. We are guided more by pragmatism than novelty, but Mojo’s use [MLIR](https://mlir.llvm.org/) allows it to scale to new exotic hardware types and domains in a way that other languages haven’t demonstrated (for an example of Mojo talking directly to MLIR, see our [low-level IR in Mojo -notebook](/mojo/notebooks/BoolMLIR.html)). It also +notebook](/mojo/notebooks/BoolMLIR)). It also includes autotuning, and has caching and distributed compilation built into its core. We also believe Mojo has a good chance of unifying hybrid packages in the broader Python community. @@ -84,7 +84,7 @@ Codon and PyPy aim to improve performance compared to CPython, but Mojo’s goal are much deeper than this. Our objective isn’t just to create “a faster Python,” but to enable a whole new layer of systems programming that includes direct access to accelerated hardware, as outlined in [Why -Mojo](/mojo/why-mojo.html). Our technical implementation +Mojo](/mojo/why-mojo). Our technical implementation approach is also very different, for example, we are not relying on heroic compiler and JIT technologies to “devirtualize” Python. @@ -123,7 +123,7 @@ you to try Mojo and if you find it useful, then that's great too. The best place to start is the [Mojo Manual](/mojo/manual). And if you want to see what features are coming in the future, take a look at [the -roadmap](/mojo/roadmap.html). +roadmap](/mojo/roadmap). ### What does it mean that Mojo is designed for MLIR? @@ -138,7 +138,7 @@ ground up with MLIR design principles. This means that Mojo not only offers high-performance compilation for heterogeneous hardware, but it also provides direct programming support for the MLIR intermediate representations. For a simple example of Mojo talking directly to MLIR, see our [low-level IR in Mojo -notebook](/mojo/notebooks/BoolMLIR.html). +notebook](/mojo/notebooks/BoolMLIR). ### Is Mojo only for AI or can it be used for other stuff? @@ -165,7 +165,7 @@ language that will support more architectures over time and includes a debugger, a full tool suite, etc. For more about embedded domain-specific languages (EDSLs) like Triton, read the “Embedded DSLs in Python” section of [Why -Mojo](/mojo/why-mojo.html#embedded-dsls-in-python). +Mojo](/mojo/why-mojo#embedded-dsls-in-python). ### How does Mojo help with PyTorch and TensorFlow acceleration? @@ -200,7 +200,7 @@ and build migration tools as the language matures. Yes, we want to enable developers to port code from languages other than Python to Mojo as well. We expect that due to Mojo’s similarity to the C/C++ type systems, migrating code from C/C++ should work well and it’s in [our -roadmap](/mojo/roadmap.html#cc-interop). +roadmap](/mojo/roadmap#cc-interop). ### How does Mojo support hardware lowering? @@ -379,7 +379,7 @@ more detail such as the time spend compiling. Mojo is still in early development and not at a 1.0 version yet. It’s still missing many foundational features, but please take a look at our -[roadmap](/mojo/roadmap.html) to understand where things are headed. As such, +[roadmap](/mojo/roadmap) to understand where things are headed. As such, the language is evolving rapidly and source stability is not guaranteed. ### How often will you be releasing new versions of Mojo? @@ -424,9 +424,9 @@ Clang, Swift, MLIR, etc.). ### Where can I ask more questions or share feedback? If you have questions about upcoming features or have suggestions -for the language, be sure you first read the [Mojo roadmap](roadmap.html), which +for the language, be sure you first read the [Mojo roadmap](/mojo/roadmap), which provides important information about our current priorities and links to our GitHub channels where you can report issues and discuss new features. To get in touch with the Mojo team and developer community, use the resources -on our [Mojo community page](/mojo/community.html). +on our [Mojo community page](/mojo/community). diff --git a/docs/manual/decorators/always-inline.ipynb b/docs/manual/decorators/always-inline.ipynb index d0648a2f0d..f272691b9b 100644 --- a/docs/manual/decorators/always-inline.ipynb +++ b/docs/manual/decorators/always-inline.ipynb @@ -72,8 +72,12 @@ "\n", "You can also use the decorator with the `\"nodebug\"` argument, which has the\n", "same effect to inline the function, but without debug information. This means\n", - "you can't step into the function when debugging, but it reduces the debug build\n", - "binary size." + "that you can't step into the function when debugging.\n", + "\n", + "This decorator is intended to be used on the lowest-level functions in a\n", + "library, which may wrap primitive functions, MLIR operations, or inline\n", + "assembly. Marking these functions as \"nodebug\" prevents users from accidentally\n", + "stepping into low-level non-Mojo code when debugging." ] } ], diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.md index bfdd52e7c6..4f7f11ee43 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.md @@ -13,7 +13,6 @@ listing: - parameter.ipynb - register-passable.ipynb - staticmethod.ipynb - - unroll.ipynb - value.ipynb type: grid page-size: 99 diff --git a/docs/manual/decorators/parameter.ipynb b/docs/manual/decorators/parameter.ipynb index 65aff01912..c74b25e22f 100644 --- a/docs/manual/decorators/parameter.ipynb +++ b/docs/manual/decorators/parameter.ipynb @@ -51,6 +51,55 @@ " print(\"this will be eliminated at compile time\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parametric for statement\n", + "\n", + "You can add the `@parameter` decorator to an `for` loop to create a loop that's\n", + "evaluated at compile time. The loop sequence and induction values must be\n", + "a valid parameter expressions (that is, an expressions that evaluate at compile\n", + "time).\n", + "\n", + "This has the effect of \"unrolling\" the loop.\n", + "\n", + " ```mojo\n", + " fn parameter_for[max: Int]():\n", + " @parameter\n", + " for i in range(max)\n", + " @parameter\n", + " if i == 10:\n", + " print(\"found 10!\")\n", + " ```\n", + "\n", + " Currently, `@parameter for` requires the sequence's `__iter__` method to\n", + " return a `_StridedRangeIterator`, meaning the induction variables must be\n", + " `Int`. The intention is to lift these restrictions in the future.\n", + "\n", + "### Compared to `unroll()`\n", + "\n", + "The Mojo standard library also includes a function called\n", + "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", + "given function that you want to call repeatedly, but has some important\n", + "differences when compared to the parametric `for` statement:\n", + "\n", + "- The `@parameter` decorator operates on `for` loop expressions. The \n", + " `unroll()` function is a higher-order function that takes a parametric closure\n", + " (see below) and executes it a specified number of times.\n", + "\n", + "- The parametric `for` statement is more versatile, since you can do anything \n", + " you can do in a `for` statement: including using arbitrary sequences, \n", + " early-exiting from the loop, skipping iterations with `continue` and so on.\n", + " \n", + " By contrast, `unroll()` simply takes a function and a count, and executes\n", + " the function the specified number of times.\n", + "\n", + "Both `unroll()` and `@parameter for` unroll at the beginning of compilation, \n", + "which might explode the size of the program that still needs to be compiled,\n", + "depending on the amount of code that's unrolled." + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -66,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/docs/manual/decorators/unroll.ipynb b/docs/manual/decorators/unroll.ipynb deleted file mode 100644 index d0300178ac..0000000000 --- a/docs/manual/decorators/unroll.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@parameter`'\n", - "description: Unrolls a loop at compile time.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@parameter` decorator on any loop (such as `for` and `while`) to\n", - "make the Mojo compiler [unroll the\n", - "loop](https://en.wikipedia.org/wiki/Loop_unrolling), either fully or with a\n", - "given unroll factor.\n", - "\n", - "For example, the compiler will unroll all 10 iterations of the following loop\n", - "into 10 consecutive calls to `print()` (removing the `for` loop entirely):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@parameter\n", - "for i in range(10):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The decorator also accepts an \"unroll factor\" argument, which specifies how\n", - "many iterations to unroll at once. For example, the unroll above is equivalent\n", - "to `@parameter(10)` because it unrolls all iterations of the loop. So if you pass\n", - "a number smaller than the loop bounds, the compiler creates multiple unrolls.\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Unroll every 2 iterations, leaving a loop with 5 iterations.\n", - "# @parameter(2)\n", - "for i in range (10):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is equivalent to this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(0, 10, 2):\n", - " print(i)\n", - " print(i+1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, the compiler can unroll a loop only when the following statements are\n", - "true:\n", - "\n", - "- The loop's lower bound, upper bound, and induction step size are compile-time\n", - "constants (they do not vary at runtime). For example, in the above code\n", - "`range(0, 10, 2)`, `0` is the lower bound, `10` is the upper bound, and `2`\n", - "is the induction step size—these could instead be defined with variable names,\n", - "but the values cannot vary at runtime.\n", - "\n", - "- Likewise, there are no early exits in the loop that make the loop count\n", - "variable at runtime." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Compared to `unroll()`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Mojo standard library also includes a function called\n", - "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", - "given function that you want to call repeatedly, but has some important\n", - "differences when compared to the `@parameter` decorator:\n", - "\n", - "- The `@parameter` decorator operates on loop expressions only, not on functions\n", - " like the `unroll()` function does.\n", - "\n", - "- The `@parameter` decorator determines how to unroll the loop based on the\n", - " induction variable (`i`), the value of which _is not_ known when compilation\n", - " begins. Whereas, the `unroll()` function calls upon your looping function\n", - " (`func`) with the `Int` loop index parameter that _is_ known at compile time.\n", - "\n", - " This means two things:\n", - "\n", - " - Within a loop using the `@parameter` decorator, the `i` induction variable is \n", - " still a runtime variable, so you _cannot_ use it as a parameter value (such\n", - " as for `SIMD[Int8, i]`). Whereas, within the `func` callback used with the\n", - " `unroll()` function, the `Int` loop index is known at compile time, so you\n", - " _can_ use it as a parameter value.\n", - "\n", - " - The `unroll()` function unrolls at the beginning of compilation, which\n", - " might explode the program size that still needs to be compiled, depending\n", - " on the amount of code that's unrolled. Whereas, the `@parameter` decorator\n", - " performs unrolling later in the compilation, after the compiler is able to\n", - " evaluate the induction variable (`i`), which avoids early explosion of the\n", - " program size that still needs compilation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index ee7a1cadd3..64e4e2e604 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -34,25 +34,89 @@ "\n", ":::note\n", "\n", - "Functions declared inside a [`struct`](/mojo/manual/structs.html) are called\n", + "Functions declared inside a [`struct`](/mojo/manual/structs) are called\n", "\"methods,\" but they have all the same qualities as \"functions\" described here.\n", "\n", ":::" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `fn` functions\n", + "\n", + "The `fn` function has somewhat stricter rules than the `def` function.\n", + "\n", + "Here's an example of an `fn` function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn greet(name: String) -> String:\n", + " var greeting = \"Hello, \" + name + \"!\"\n", + " return greeting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As far as a function caller is concerned, `def` and `fn` functions are\n", + "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", + "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", + "function is more strict on the inside.\n", + "\n", + "Here's everything to know about `fn`:\n", + "\n", + "- Arguments must specify a type (except for the\n", + " `self` argument in [struct methods](/mojo/manual/structs#methods)).\n", + "\n", + "- Return values must specify a type, unless the function doesn't return a value.\n", + " \n", + " If you don't specify a return type, it defaults to `None` (meaning no return\n", + " value).\n", + "\n", + "- By default, arguments are received as an immutable reference (values are\n", + " read-only, using the `borrowed` [argument\n", + " convention](/mojo/manual/values/ownership#argument-conventions)).\n", + " \n", + " This prevents accidental mutations, and permits the use of non-copyable types\n", + " as arguments.\n", + " \n", + " If you want a local copy, you can simply assign the value to a local\n", + " variable. Or, you can get a mutable reference to the value by declaring the\n", + " `inout` [argument\n", + " convention](/mojo/manual/values/ownership#argument-conventions)).\n", + "\n", + "- [Variables](/mojo/manual/variables) must be declared using the `var`\n", + " keyword.\n", + "\n", + "- If the function raises an exception, it must be explicitly declared with the\n", + " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", + "\n", + "By enforcing these type checks, using the `fn` function helps avoid a variety\n", + "of runtime errors." + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `def` functions\n", "\n", - "The `def` function provides the same dynamism and flexibility as a Python\n", + "Compared to an `fn` function, a `def` function has fewer restrictions.\n", + "The `def` function works more like a Python\n", "`def` function. For example, this function works the same in Python and Mojo:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -65,14 +129,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In Mojo, you also have the option to specify the argument type and the return\n", - "type. You can also declare variables with `var`, with or without explicit\n", - "typing." + "In a Mojo `def` function, you have the option to specify the argument type and\n", + "the return type. You can also declare variables with `var`, with or without\n", + "explicit typing. So you can write a `def` function that looks almost exactly\n", + "like the `fn` function shown earlier:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -92,23 +157,26 @@ "\n", "- Arguments don't require a declared type.\n", "\n", - " Undeclared arguments are actually passed as an `object`, which allows the\n", + " Undeclared arguments are actually passed as an\n", + " [`object`](/mojo/stdlib/builtin/object/object), which allows the\n", " function to receive any type (Mojo infers the type at runtime).\n", "\n", - "- Return types don't need to be declared and also default to `object`.\n", + "- Return types don't need to be declared, and also default to `object`. (If a \n", + " `def` function doesn't declare a return type of `None`, it's considered to\n", + " return an `object` by default.)\n", "\n", - "- Arguments are mutable (usually passed by value, using the `owned` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", + "- Arguments are mutable. Arguments default to using the using the `borrowed` \n", + " [argument convention](/mojo/manual/values/ownership#argument-conventions))\n", + " like an `fn` function, with a special addition: if the function mutates the\n", + " argument, it makes a mutable copy. \n", "\n", " If an argument is an `object` type, it's received as a reference, following\n", " [object reference\n", - " semantics](/mojo/manual/values/value-semantics.html#python-style-reference-semantics).\n", + " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", " \n", - " If an argument is any other declared type, it's received as a value (using\n", - " the `owned` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", + " If an argument is any other declared type, it's received as a value.\n", "\n", - "- [Variables](/mojo/manual/variables.html) don't need to be declared using \n", + "- [Variables](/mojo/manual/variables) don't need to be declared using \n", " `var`." ] }, @@ -131,86 +199,43 @@ "\n", "For compatibility with Python, `object` values are passed using [object\n", "reference\n", - "semantics](/mojo/manual/values/value-semantics.html#python-style-reference-semantics).\n", + "semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", "As such, the `object` type is not compatible with the [argument\n", - "conventions](/mojo/manual/values/ownership.html#argument-conventions) that\n", + "conventions](/mojo/manual/values/ownership#argument-conventions) that\n", "enforce value semantics. So, be careful if using `object` values alongside other\n", "strongly-typed values—their behavior might be inconsistent because `object` is \n", "the only type in the standard library that does not conform to [full value\n", - "semantics](/mojo/manual/values/value-semantics.html#full-value-semantics)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `fn` functions\n", + "semantics](/mojo/manual/values/value-semantics#full-value-semantics).\n", "\n", - "The `fn` function provides strict type checking and additional memory safety.\n", - "It basically forces you to write the optional things in `def`, and it ensures\n", - "that you don't accidentally mutate received arguments. For example, here's the\n", - "same function from above using `fn`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn greet(name: String) -> String:\n", - " var greeting = \"Hello, \" + name + \"!\"\n", - " return greeting" + ":::note TODO\n", + "\n", + "The `object` type is still a work in progress. It doesn't support all of the\n", + "possible underlying types, for example.\n", + "\n", + ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As far as a function caller is concerned, `def` and `fn` functions are\n", - "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", - "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", - "function is more strict on the inside.\n", + "## Function arguments\n", "\n", - "Here's everything to know about `fn`:\n", + "As noted in the previous sections, there are a few differences between how `def`\n", + "and `fn` functions treat arguments. But most of the time they are the same.\n", "\n", - "- Arguments must specify a type (except for the\n", - " `self` argument in [struct methods](/mojo/manual/structs.html)).\n", + "As noted, there are some differences in _argument conventions_. \n", + "Argument conventions are discussed in much more detail in the page on\n", + "[Ownership](/mojo/manual/values/ownership#argument-conventions).\n", "\n", - "- Return values must specify a type, unless the function doesn't return a value.\n", - " \n", - " If you don't specify a return type, it defaults to `None` (meaning no return\n", - " value).\n", + "The other difference is that `def` functions don't need to specify an argument's\n", + "type. If no type is specified, the argument is passed as an \n", + "[`object`](/mojo/stdlib/builtin/object/object).\n", "\n", - "- By default, arguments are received as an immutable reference (values are\n", - " read-only, using the `borrowed` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", - " \n", - " This prevents accidental mutations, and permits the use of non-copyable types\n", - " as arguments.\n", - " \n", - " If you want a local copy, you can simply assign the value to a local\n", - " variable. Or, you can get a mutable reference to the value by declaring the\n", - " `inout` [argument\n", - " convention](/mojo/manual/values/ownership.html#argument-conventions)).\n", - "\n", - "- [Variables](/mojo/manual/variables.html) must be declared using the `var`\n", - " keyword.\n", + "The remaining rules for arguments described in this section apply to both `def`\n", + "and `fn` functions.\n", "\n", - "- If the function raises an exception, it must be explicitly declared with the\n", - " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", - "\n", - "By enforcing these type checks, using the `fn` function helps avoid a variety\n", - "of runtime errors. It also improves performance compared to the dynamic typing\n", - "in a `def` function, because there's no overhead processing required to figure\n", - "out what data types to use at runtime—the types are fixed at compile time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional arguments\n", + "### Optional arguments\n", "\n", "An optional argument is one that includes a default value, such as the `exp`\n", "argument here:" @@ -247,7 +272,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Keyword arguments\n", + "### Keyword arguments\n", "\n", "You can also use keyword arguments when calling a function. Keyword arguments\n", "are specified using the format argument_name =\n", @@ -273,7 +298,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Variadic arguments\n", + "### Variadic arguments\n", "\n", "Variadic arguments let a function accept a variable number of arguments. To\n", "define a function that takes a variadic argument, use the variadic argument\n", @@ -326,7 +351,7 @@ ":::\n", "\n", "\n", - "### Homogeneous variadic arguments\n", + "#### Homogeneous variadic arguments\n", "\n", "When defining a homogeneous variadic argument, use \n", "*argument_name: argument_type:" @@ -416,7 +441,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Heterogeneous variadic arguments\n", + "#### Heterogeneous variadic arguments\n", "\n", "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", @@ -545,7 +570,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Variadic keyword arguments\n", + "#### Variadic keyword arguments\n", "\n", "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", "keyword arguments allow the user to pass an arbitrary number of keyword\n", @@ -611,7 +636,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Positional-only and keyword-only arguments\n", + "### Positional-only and keyword-only arguments\n", "\n", "When defining a function, you can restrict some arguments so that they can only\n", "be passed as positional arguments, or they can only be passed as keyword \n", diff --git a/docs/manual/index.md b/docs/manual/index.md index 460756b70f..4d28a625c4 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -13,12 +13,12 @@ ideal for heterogeneous hardware, from CPUs and GPUs, to various AI ASICs). We also designed Mojo as a superset of Python because we love Python and its community, but we couldn't realistically enhance Python to do all the things we wanted. For a longer discussion on this topic, read [Why -Mojo](/mojo/why-mojo.html). +Mojo](/mojo/why-mojo). Beware that Mojo is still a very young language, so there's a lot that hasn't been built yet. Likewise, there's a lot of documentation that hasn't been written yet. But we're excited to share Mojo with you and [get your -feedback](/mojo/community.html). +feedback](/mojo/community). ## Contents @@ -29,42 +29,47 @@ feedback](/mojo/community.html). - **Language basics** - - [Introduction to Mojo](/mojo/manual/basics.html) - - [Functions](/mojo/manual/functions.html) - - [Variables](/mojo/manual/variables.html) - - [Structs](/mojo/manual/structs.html) - - [Modules and packages](/mojo/manual/packages.html) + - [Introduction to Mojo](/mojo/manual/basics) + - [Functions](/mojo/manual/functions) + - [Variables](/mojo/manual/variables) + - [Types](/mojo/manual/types) + - [Structs](/mojo/manual/structs) + - [Modules and packages](/mojo/manual/packages) - **Value ownership** - - [Intro to value ownership](/mojo/manual/values/index.html) - - [Value semantics](/mojo/manual/values/value-semantics.html) - - [Ownership and borrowing](/mojo/manual/values/ownership.html) + - [Intro to value ownership](/mojo/manual/values/index) + - [Value semantics](/mojo/manual/values/value-semantics) + - [Ownership and borrowing](/mojo/manual/values/ownership) - **Value lifecycle** - - [Intro to value lifecycle](/mojo/manual/lifecycle/index.html) - - [Life of a value](/mojo/manual/lifecycle/life.html) - - [Death of a value](/mojo/manual/lifecycle/death.html) + - [Intro to value lifecycle](/mojo/manual/lifecycle/index) + - [Life of a value](/mojo/manual/lifecycle/life) + - [Death of a value](/mojo/manual/lifecycle/death) - **Traits and parameters** - - [Traits](/mojo/manual/traits.html) - - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index.html) + - [Traits](/mojo/manual/traits) + - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index) + +- **Pointers** + + - [Unsafe pointers](/mojo/manual/pointers) - **Python** - - [Python integration](/mojo/manual/python/index.html) - - [Python types](/mojo/manual/python/types.html) + - [Python integration](/mojo/manual/python/index) + - [Python types](/mojo/manual/python/types) - **Tools** - - [Debugging](/mojo/tools/debugging.html) - - [Testing](/mojo/tools/testing.html) + - [Debugging](/mojo/tools/debugging) + - [Testing](/mojo/tools/testing) - **Project information** - - [Roadmap and sharp edges](/mojo/roadmap.html) - - [Changelog](/mojo/changelog.html) - - [FAQ](/mojo/faq.html) - - [Community](/mojo/community.html) + - [Roadmap and sharp edges](/mojo/roadmap) + - [Changelog](/mojo/changelog) + - [FAQ](/mojo/faq) + - [Community](/mojo/community) diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 20674db040..a92d507b7a 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -64,7 +64,7 @@ "\n", "- `borrowed`: The function receives an **immutable reference**. This means the\n", " function can read the original value (it is *not* a copy), but it cannot\n", - " mutate (modify) it.\n", + " mutate (modify) it. `def` functions treat this differently, as described below.\n", " \n", "- `inout`: The function receives a **mutable reference**. This means the\n", " function can read and mutate the original value (it is *not* a copy).\n", @@ -100,16 +100,38 @@ "metadata": {}, "source": [ "You've probably already seen some function arguments that don't declare a\n", - "convention. That's because every argument has a default convention, depending\n", - "on whether the function is declared with `fn` or `def`:\n", + "convention. by default, all arguments are `borrowed`. But `def` and `fn` \n", + "functions treat `borrowed` arguments somewhat differently:\n", "\n", - "- All values passed into a Mojo [`def`\n", - " function](/mojo/manual/functions.html#def-functions) are `owned`,\n", - " by default. \n", "\n", - "- All values passed into a Mojo [`fn`\n", - " function](/mojo/manual/functions.html#fn-functions) are `borrowed`,\n", - " by default.\n", + "- In an [`fn` function](/mojo/manual/functions.html#fn-functions), the function\n", + " always receives an immutable reference. If you want a mutable copy, you can\n", + " assign it to a local variable:\n", + "\n", + " ```mojo\n", + " var my_copy = borrowed_arg\n", + " ```\n", + "\n", + "- In a [`def` function](/mojo/manual/functions.html#def-functions), if the \n", + " function mutates the value, the function receives a mutable copy of the \n", + " argument. Otherwise, it receives an immutable reference. This allows you to\n", + " treat arguments as mutable, but avoid the overhead of making extra copies when\n", + " they're not needed.\n", + "\n", + "The difference between `borrowed` and `owned` in a `def` function may be a\n", + "little subtle: \n", + "\n", + "- In a `def` function, a `borrowed` argument is received as an immutable\n", + " reference, unless it's mutated in the body of the function. This eliminates\n", + " unneeded copies, but maintains the Python expectation that arguments are \n", + " mutable.\n", + "\n", + "- The `borrowed` argument always gets an immutable reference or a local copy.\n", + " You can't transfer a value into a `borrowed` argument.\n", + "\n", + "- The `owned` argument always gets a uniquely owned value, which may have been\n", + " copied or transferred from the callee. Using `owned` arguments without the \n", + " transfer operator (`^`) usually results in values being copied.\n", "\n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." @@ -126,9 +148,11 @@ "- Every value has only one owner at a time.\n", "- When the lifetime of the owner ends, Mojo destroys the value.\n", "\n", - "The \"borrow checker\" is a process in the Mojo compiler that ensures there is\n", - "only one owner for each value at any time. It also enforces some other\n", - "memory-safety rules:\n", + "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", + "that only one mutable reference to a value can exist at a time. **This is not\n", + "currently enforced.**\n", + "\n", + "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Immutable arguments (`borrowed`)\n", + "## Borrowed arguments (`borrowed`)\n", + "\n", + "The `borrowed` convention is the default for all arguments.\n", "\n", - "If you'd like your function to receive an **immutable reference**, add the\n", - "`borrowed` keyword in front of the argument name.\n", + "In `fn` functions, a `borrowed` argument is received as an immutable reference.\n", "\n", - "The `borrowed` convention is the default for all arguments in an `fn` function,\n", - "but you can still specify it to be explicit. It also works on `def` functions,\n", - "which otherwise receive arguments by value, which might not be desirable, such\n", - "as when the type is expensive to copy (or not copyable at all) and you just\n", - "need to read it. For example:" + "In `def` functions, you can treat a `borrowed` argument as mutable or immutable.\n", + "If you mutate the argument in the body of the function, you get a mutable copy\n", + "of the original value. If you don't mutate the argument, you get an immutable\n", + "reference, as in an `fn` function.\n", + "\n", + "For example:" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -177,7 +205,7 @@ "source": [ "from tensor import Tensor, TensorShape\n", "\n", - "def print_shape(borrowed tensor: Tensor[DType.float32]):\n", + "def print_shape(tensor: Tensor[DType.float32]):\n", " shape = tensor.shape()\n", " print(str(shape))\n", "\n", @@ -189,9 +217,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In general, passing an immutable reference is much more efficient when handling\n", - "large or expensive-to-copy values, because the copy constructor and destructor\n", - "are not invoked for a borrow.\n", + "Here the `tensor` argument is borrowed and not mutated, so the `print_shape()`\n", + "function gets an immutable reference to the original `Tensor`, and doesn't do \n", + "any copying. In general, passing an immutable reference is much more efficient\n", + "when handling large or expensive-to-copy values, because the copy constructor\n", + "and destructor are not invoked for a borrow.\n", "\n", "### Compared to C++ and Rust\n", "\n", @@ -200,10 +230,8 @@ "mutability in the callee. However, the borrowed convention differs from\n", "`const&` in C++ in two important ways:\n", "\n", - "- The Mojo compiler implements a borrow checker (similar to Rust) that prevents\n", - "code from dynamically forming mutable references to a value when there are\n", - "immutable references outstanding, and it prevents multiple mutable references\n", - "to the same value.\n", + "- The Mojo compiler implements a lifetime checker that ensures that values are\n", + "not destroyed when there are outstanding references to those values.\n", "\n", "- Small values like `Int`, `Float`, and `SIMD` are passed directly in machine\n", "registers instead of through an extra indirection (this is because they are\n", @@ -213,7 +241,8 @@ "when compared to languages like C++ and Rust, and moves this optimization from\n", "every call site to a declaration on the type definition.\n", "\n", - "Similar to Rust, Mojo's borrow checker enforces the exclusivity of invariants.\n", + "In the future, Mojo's lifetime checker will enforces the exclusivity of\n", + "mutable references, similar to Rust.\n", "The major difference between Rust and Mojo is that Mojo does not require a\n", "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", "passing small values, and Rust defaults to moving values instead of passing\n", @@ -331,19 +360,30 @@ "lifetime of that variable.\n", "\n", "Technically, the `owned` keyword does not guarantee that the received value is\n", - "a mutable reference to _the original value_—it guarantees only that the function\n", - "gets unique ownership of this particular value (enforcing [value\n", + "_the original value_—it guarantees only that the function\n", + "gets unique ownership of a value (enforcing [value\n", "semantics](/mojo/manual/values/value-semantics.html)). This happens in one of\n", - "two ways:\n", + "three ways:\n", "\n", "- The caller passes the argument with the `^` transfer operator, which ends the\n", - "lifetime of that variable (the variable becomes invalid) and ownership is\n", + "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", "transferred into the function without making a copy of any heap-allocated data.\n", "\n", "- The caller **does not** use the `^` transfer operator, in which case, the\n", "value is copied into the function argument and the original variable remains\n", - "valid (unless it is not used again, in which case the compiler destroys the\n", - "variable anyway because its lifetime naturally ends there).\n", + "valid. (If the original value is not used again, the compiler may optimize away\n", + "the copy and transfer the value).\n", + "\n", + "- The caller passes in a newly-created \"owned\" value, such as a value returned\n", + "from a function. In this case, no variable owns the value and it can be\n", + "transferred directly to the callee. For example:\n", + "\n", + " ```mojo\n", + " def take(owned s: String):\n", + " pass\n", + "\n", + " take(str(\"A brand-new String!\"))\n", + " ```\n", "\n", "Regardless, when the function declares an argument as `owned`, it can be certain\n", "that it has unique mutable access to that value. \n", @@ -395,7 +435,7 @@ " print(message) # ERROR: The `message` variable is uninitialized\n", "```\n", "\n", - "This is a critical feature of Mojo's borrow checker, because it ensures that no\n", + "This is a critical feature of Mojo's lifetime checker, because it ensures that no\n", "two variables can have ownership of the same value. To fix the error, you must\n", "not use the `message` variable after you end its lifetime with the `^` transfer\n", "operator. So here is the corrected code:" @@ -478,10 +518,10 @@ " [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn`\n", " requires all types be explicitly declared).\n", "\n", - "- A `def` argument without a convention keyword (`borrowed`, `inout`, or\n", - " `owned`) defaults to `owned` (it receives a copy with a mutable variable).\n", + "- A `def` function can treat a `borrowed` argument as mutable (in which case it\n", + " receives a mutable copy). An `fn` function must make this copy explicitly.\n", "\n", - "For example, these two functions have the exact same behavior:" + "For example, these two functions have the exact same behavior." ] }, { @@ -490,29 +530,11 @@ "metadata": {}, "outputs": [], "source": [ - "def example(borrowed a: Int, inout b: Int, c):\n", + "def def_example(a: Int, inout b: Int, owned c):\n", " pass\n", "\n", - "fn example(a: Int, inout b: Int, owned c: object):\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or, instead of specifying `owned` for the `c` argument, you can get the same\n", - "effect by manually making a copy when you need it:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn example(a: Int, inout b: Int, c_in: object):\n", - " var c = c_in\n", + "fn fn_example(a_in: Int, inout b: Int, owned c: object):\n", + " var a = a_in\n", " pass" ] }, @@ -521,8 +543,9 @@ "metadata": {}, "source": [ "This shadow copy typically adds no overhead, because references for small types\n", - "like `object` are cheap to copy. The expensive part is adjusting the reference\n", - "count, but that's also eliminated by a compiler optimization." + "like `object` are cheap to copy. However, copying large types that allocate heap\n", + "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", + "copying large numbers of strings.)" ] } ], diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index f405a34ab9..5ece6cbdb8 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -130,43 +130,40 @@ "\n", "In Mojo, the default behavior for all function arguments is to use value\n", "semantics. If the function wants to modify the value of an incoming argument,\n", - "then it must explicitly declare so, which avoids accidental mutations.\n", + "then it must explicitly declare so, which avoids accidental mutations of the\n", + "original value.\n", "\n", - "For starters, all Mojo types passed to a `def` function are passed by value,\n", - "which maintains the expected mutability behavior from Python. Except—contrary\n", - "to Python—the function has true ownership of the value, usually because it's a\n", - "copy.\n", + "All Mojo types passed to a `def` function can be treated as mutable,\n", + "which maintains the expected mutability behavior from Python. But by default, it\n", + "is mutating a uniquely-owned value, not the original value.\n", "\n", - "For example, even though the Mojo [`Tensor`](/mojo/stdlib/tensor/tensor.html)\n", - "type allocates values on the heap, when you pass an instance to a `def`\n", - "function, it creates a unique copy of all values. Thus, if we modify the\n", + "For example, when you pass an instance of a `SIMD` vector to a `def`\n", + "function it creates a unique copy of all values. Thus, if we modify the\n", "argument in the function, the original value is unchanged:" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Tensor([[1, 3]], dtype=uint8, shape=2)\n", - "Tensor([[1, 2]], dtype=uint8, shape=2)\n" + "[9, 2, 3, 4]\n", + "[1, 2, 3, 4]\n" ] } ], "source": [ - "def update_tensor(t: Tensor[DType.uint8]):\n", - " t[1] = 3\n", + "def update_simd(t: SIMD[DType.int32, 4]):\n", + " t[0] = 9\n", " print(t)\n", "\n", - "t = Tensor[DType.uint8](2)\n", - "t[0] = 1\n", - "t[1] = 2\n", - "update_tensor(t)\n", - "print(t)" + "v = SIMD[DType.int32, 4](1, 2, 3, 4)\n", + "update_simd(v)\n", + "print(v)" ] }, { @@ -189,10 +186,13 @@ "metadata": {}, "source": [ "The arguments above are mutable because a [`def`\n", - "function](/mojo/manual/functions.html#def-functions) gets ownership for\n", - "its arguments by default (usually as a copy). Whereas, `fn` functions instead\n", - "receive arguments as immutable references, by default. This is a memory\n", - "optimization to avoid making unnecessary copies.\n", + "function](/mojo/manual/functions.html#def-functions) has special treatment for\n", + "the default\n", + "[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions).\n", + "\n", + "Whereas, `fn` functions always receive `borrowed` arguments as immutable\n", + "references. This is a memory optimization to avoid making\n", + "unnecessary copies.\n", "\n", "For example, let's create another function with the `fn` declaration. In this\n", "case, the `y` argument is immutable by default, so if the function wants to\n", @@ -301,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -337,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "metadata": {}, "outputs": [ { diff --git a/docs/roadmap.md b/docs/roadmap.md index f19c0d9abf..7b84532a6e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -18,7 +18,7 @@ the long-term), so we want to fully build-out the core Mojo language features before we work on other dependent features and enhancements. Currently, that means we are focused on the core system programming features -that are essential to [Mojo's mission](why-mojo.html), and as outlined in the +that are essential to [Mojo's mission](/mojo/why-mojo), and as outlined in the following sections of this roadmap. In the near-term, we will **not** prioritize "general goodness" work such as: @@ -210,7 +210,7 @@ to use right now. ## Traits support As of v0.6.0 Mojo has basic support for -[traits](/mojo/manual/traits.html#built-in-traits). Traits allow you +[traits](/mojo/manual/traits). Traits allow you to specify a set of requirements for types to implement. Types can implement those requirements to *conform to* the trait. Traits allow you to write generic functions and generic containers, which can work with any type that @@ -221,7 +221,7 @@ Currently, the only kind of requirements supported by traits are required method signatures. The trait can't provide a default implementation for its required methods, so each conforming type must implement all of the required methods. -A number of [built-in traits](/mojo/manual/traits.html#built-in-traits) are +A number of [built-in traits](/mojo/manual/traits#built-in-traits) are already implemented in the standard library. We plan to expand traits support in future releases. Planned features include: From 107c679878a535735e727e6008bfc4a86036a42e Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 5 Jun 2024 14:19:52 -0500 Subject: [PATCH 0899/2019] [External] [stdlib] Delay string creation in debug_assert for better perf (#41400) [External] [stdlib] Delay string creation in debug_assert for better perf See https://github.com/modularml/mojo/pull/2945#discussion_r1625636497 for background. By delaying the conversion to string and the concatenation after the `if` statement, we make sure that there is no additional instruction when not using `-D MOJO_ENABLE_ASSERTIONS`. Futhermore, in platforms where using `.alloc` is problematic, it can help. @laszlokindrat since you were participating in the conversation. --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: Laszlo Kindrat Closes modularml/mojo#2959 MODULAR_ORIG_COMMIT_REV_ID: e9761b10631c363410f217e7bcaed89cb59be3a0 --- stdlib/src/builtin/debug_assert.mojo | 29 +++++++++++++------ stdlib/src/collections/list.mojo | 10 ++++--- ...t_debug_assert_mojo_enable_assertions.mojo | 4 +-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index ec101b76e2..269e4430c4 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -23,7 +23,9 @@ from builtin._location import __call_location, _SourceLocation @always_inline -fn debug_assert[stringable: Stringable](cond: Bool, msg: stringable): +fn debug_assert[ + *stringable: Stringable +](cond: Bool, *message_parts: *stringable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -34,11 +36,12 @@ fn debug_assert[stringable: Stringable](cond: Bool, msg: stringable): for enabling assertions in the library. Parameters: - stringable: The type of the message. + stringable: The type of the message parts. Args: cond: The bool value to assert. - msg: The message to display on failure. + message_parts: The message parts to convert to `String` and concatenate + before displaying it on failure. """ # Print an error and fail. @@ -52,13 +55,21 @@ fn debug_assert[stringable: Stringable](cond: Bool, msg: stringable): @parameter if err or warn: if not cond: - _debug_assert_msg[err](msg, __call_location()) + var full_message: String = "" + + @parameter + fn add_to_full_message[ + i: Int, StringableType: Stringable + ](msg: StringableType): + full_message += str(msg) + + message_parts.each_idx[add_to_full_message]() + + _debug_assert_msg[err](full_message, __call_location()) @no_inline -fn _debug_assert_msg[ - err: Bool, stringable: Stringable -](msg: stringable, loc: _SourceLocation): +fn _debug_assert_msg[err: Bool](msg: String, loc: _SourceLocation): """Aborts with (or prints) the given message and location. Note that it's important that this function doesn't get inlined; otherwise, @@ -79,6 +90,6 @@ fn _debug_assert_msg[ @parameter if err: - abort(loc.prefix("Assert Error: " + str(msg))) + abort(loc.prefix("Assert Error: " + msg)) else: - print(loc.prefix("Assert Warning:"), str(msg)) + print(loc.prefix("Assert Warning:"), msg) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 2f975cc991..2ce1099829 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -808,10 +808,12 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ debug_assert( 0 <= idx < len(self), - ( - "The index provided must be within the range [0, len(List) -1]" - " when using List.unsafe_get()" - ), + "The index provided must be within the range [0,", + len(self), + "[", + " when using List.unsafe_get(). But you provided: ", + idx, + ".", ) return (self.data + idx)[] diff --git a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo index cc6b7eb8bb..112daa76f8 100644 --- a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo +++ b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo @@ -21,7 +21,7 @@ # CHECK-LABEL: test_fail fn main(): print("== test_fail") - # CHECK: Assert Error: fail - debug_assert(False, "fail") + # CHECK: Assert Error: fail,1,some string + debug_assert(False, "fail,", 1, String(",some string")) # CHECK-NOT: is never reached print("is never reached") From cba501f40c0412cc64899123d4373e022c57e32c Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 5 Jun 2024 14:45:51 -0500 Subject: [PATCH 0900/2019] [External] [stdlib] Add struct `UnsafeMaybeUninitialized` (#41185) [External] [stdlib] Add struct `UnsafeMaybeUninitialized` This struct is private at the moment and for internal use only. Once we are sure that it's ready for public use, we'll call it `MaybeUninitialized`. Heavily borrowed from https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html This can allow us to add a destructor to `InlineArray` without impacting `InlineList` and SBO. We can make `InlineList` use a `InlineArray[MaybeUninitialized[T]]`. @ConnorGray for review since you have a good idea of what to do with it. See https://github.com/modularml/mojo/issues/2869#issuecomment-2137962617 for background. Three things to note: 1) I didn't measure the performance impact of this struct compared to working directly with uninitialized memory. Even if there is a performance penalty right now, do we really care at this point since we don't have benchmarks yet? Even if the implementation could be faster, this PR gives us unit tests and a struct with method signatures to iterate on. 2) This struct is private, I didn't want to put too much pressure on this PR to have something perfect on the first try. Furthermore, we don't have good docstrings yet, those can be added later on. 3) I use ``` `__mlir_type[`!pop.array<`, _size.value, `, `, Self.ElementType, `>`]` ``` but this may not be the best MLIR type to do this. I'm not very experienced with MLIR, so if there is something more appropriate that we can use, let me know. --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: Connor Gray Closes modularml/mojo#2888 MODULAR_ORIG_COMMIT_REV_ID: ef173db08ff7b602ab7dc66aa4009acd69c64df8 --- stdlib/src/memory/maybe_uninitialized.mojo | 166 ++++++++++++++++++ stdlib/src/memory/unsafe.mojo | 2 + .../test/memory/test_maybe_uninitialized.mojo | 107 +++++++++++ 3 files changed, 275 insertions(+) create mode 100644 stdlib/src/memory/maybe_uninitialized.mojo create mode 100644 stdlib/test/memory/test_maybe_uninitialized.mojo diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo new file mode 100644 index 0000000000..baf3ae6af3 --- /dev/null +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -0,0 +1,166 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( + CollectionElement +): + """A memory location that may or may not be initialized. + + Note that the destructor is a no-op. If the memory was initialized, the caller + is responsible for calling `assume_initialized_destroy` before the memory is + deallocated. + + Every method in this struct is unsafe and the caller must know at all + times if the memory is initialized or not. Calling a method + that assumes the memory is initialized when it is not will result in + undefined behavior. + + Parameters: + ElementType: The type of the element to store. + """ + + alias type = __mlir_type[`!pop.array<1, `, Self.ElementType, `>`] + var _array: Self.type + + @always_inline + fn __init__(inout self): + """The memory is now considered uninitialized.""" + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @always_inline + fn __init__(inout self, owned value: Self.ElementType): + """The memory is now considered initialized. + + Args: + value: The value to initialize the memory with. + """ + self = Self() + self.write(value^) + + @always_inline + fn __copyinit__(inout self, other: Self): + """Copy another object. + + This method should never be called as implicit copy should not + be done on memory that may be uninitialized. + + Calling this method will actally cause the program to abort. + + If you wish to perform a copy, you should manually call the method + `copy_from` instead. + + Args: + other: The object to copy. + """ + abort("You should never call __copyinit__ on MaybeUninitialized") + self = Self() + + @always_inline + fn copy_from(inout self, other: Self): + """Copy another object. + + This function assumes that the current memory is uninitialized + and the other object is initialized memory. + + Args: + other: The object to copy. + """ + self.unsafe_ptr().init_pointee_copy(other.assume_initialized()) + + @always_inline + fn __moveinit__(inout self, owned other: Self): + """Move another object. + + This method should never be called as implicit moves should not + be done on memory that may be uninitialized. + + Calling this method will actally cause the program to abort. + + If you wish to perform a move, you should manually call the method + `move_from` instead. + + Args: + other: The object to move. + """ + abort("You should never call __moveinit__ on MaybeUninitialized") + self = Self() + + @always_inline + fn move_from(inout self, inout other: Self): + """Move another object. + + This function assumes that the current memory is uninitialized + and the other object is initialized memory. + + After the function is called, the other object is considered uninitialized. + + Args: + other: The object to move. + """ + other.unsafe_ptr().move_pointee_into(self.unsafe_ptr()) + + @always_inline + fn write(inout self, owned value: Self.ElementType): + """Write a value into an uninitialized memory location. + + Calling this method assumes that the memory is uninitialized. + + Args: + value: The value to write. + """ + self.unsafe_ptr().init_pointee_move(value^) + + @always_inline + fn assume_initialized( + ref [_]self: Self, + ) -> ref [__lifetime_of(self)] Self.ElementType: + """Returns a reference to the internal value. + + Calling this method assumes that the memory is initialized. + + Returns: + A reference to the internal value. + """ + return self.unsafe_ptr()[] + + @always_inline + fn unsafe_ptr(self) -> UnsafePointer[Self.ElementType]: + """Get a pointer to the underlying element. + + Note that this method does not assumes that the memory is initialized + or not. It can always be called. + + Returns: + A pointer to the underlying element. + """ + return UnsafePointer.address_of(self._array).bitcast[Self.ElementType]() + + @always_inline + fn assume_initialized_destroy(inout self): + """Runs the destructor of the internal value. + + Calling this method assumes that the memory is initialized. + + """ + self.unsafe_ptr().destroy_pointee() + + @always_inline + fn __del__(owned self): + """This is a no-op. + + Calling this method assumes that the memory is uninitialized. + If the memory was initialized, the caller should + use `assume_initialized_destroy` before. + """ + pass diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 338c128b16..94b4d3feac 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -32,6 +32,8 @@ from bit import is_power_of_two from .memory import _free, _malloc from .reference import AddressSpace +from .maybe_uninitialized import UnsafeMaybeUninitialized + # ===----------------------------------------------------------------------===# # bitcast diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo new file mode 100644 index 0000000000..f6f1bfcfad --- /dev/null +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -0,0 +1,107 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from memory.unsafe import UnsafeMaybeUninitialized +from testing import assert_equal +from test_utils import MoveCounter, CopyCounter + + +@value +struct ValueToCountDestructor(CollectionElement): + var value: Int + var destructor_counter: UnsafePointer[List[Int]] + + fn __del__(owned self): + self.destructor_counter[].append(self.value) + + +def test_maybe_uninitialized(): + # Every time an Int is destroyed, it's going to be reccorded here. + var destructor_counter = List[Int]() + + var a = UnsafeMaybeUninitialized[ValueToCountDestructor]() + a.write( + ValueToCountDestructor(42, UnsafePointer.address_of(destructor_counter)) + ) + + assert_equal(a.assume_initialized().value, 42) + assert_equal(len(destructor_counter), 0) + + assert_equal(a.unsafe_ptr()[].value, 42) + assert_equal(len(destructor_counter), 0) + + a.assume_initialized_destroy() + assert_equal(len(destructor_counter), 1) + assert_equal(destructor_counter[0], 42) + _ = a + + # Last use of a, but the destructor should not have run + # since we asssume uninitialized memory + assert_equal(len(destructor_counter), 1) + + +@value +struct ImpossibleToDestroy(CollectionElement): + var value: Int + + fn __del__(owned self): + abort("We should never call the destructor of ImpossibleToDestroy") + + +def test_write_does_not_trigger_destructor(): + var a = UnsafeMaybeUninitialized[ImpossibleToDestroy]() + a.write(ImpossibleToDestroy(42)) + + # Using the initializer should not trigger the destructor too. + var b = UnsafeMaybeUninitialized[ImpossibleToDestroy]( + ImpossibleToDestroy(42) + ) + + # The destructor of a and b have already run at this point, and it shouldn't have + # caused a crash since we assume uninitialized memory. + + +def test_maybe_uninitialized_move(): + var a = UnsafeMaybeUninitialized[MoveCounter[Int]](MoveCounter(10)) + assert_equal(a.assume_initialized().move_count, 1) + + var b = UnsafeMaybeUninitialized[MoveCounter[Int]]() + # b is uninitialized here. + b.move_from(a) + # a is uninitialized now. + assert_equal(b.assume_initialized().move_count, 2) + b.assume_initialized_destroy() + + +def test_maybe_uninitialized_copy(): + var a = UnsafeMaybeUninitialized[CopyCounter]() + a.write(CopyCounter()) + assert_equal(a.assume_initialized().copy_count, 0) + + var b = UnsafeMaybeUninitialized[CopyCounter]() + assert_equal(a.assume_initialized().copy_count, 0) + + # b is uninitialized here. + b.copy_from(a) + a.assume_initialized_destroy() + + assert_equal(b.assume_initialized().copy_count, 1) + b.assume_initialized_destroy() + + +def main(): + test_maybe_uninitialized() + test_write_does_not_trigger_destructor() + test_maybe_uninitialized_move() + test_maybe_uninitialized_copy() From d17a76ff2a7e0bd90ede52e1776f12e435cc2a57 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Wed, 5 Jun 2024 17:05:21 -0500 Subject: [PATCH 0901/2019] [External] [stdlib] Implement an `CollectionElementNew` constructor for `Bool` (#41409) [External] [stdlib] Implement an `CollectionElementNew` constructor for `Bool` As title, implements `CollectionElementNew` for `Bool`. --------- Co-authored-by: Lukas Hermann Co-authored-by: Connor Gray Closes modularml/mojo#2955 MODULAR_ORIG_COMMIT_REV_ID: f412566907b05a6bd09b478d817ac74f84b697ea --- stdlib/src/builtin/bool.mojo | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 7363044c98..58e1c5d717 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -64,6 +64,7 @@ struct Bool( Boolable, Intable, Indexer, + CollectionElementNew, Representable, ): """The primitive Bool scalar value used in Mojo.""" @@ -71,6 +72,18 @@ struct Bool( var value: __mlir_type.i1 """The underlying storage of the boolean value.""" + @always_inline("nodebug") + fn __init__(*, other: Self) -> Bool: + """Explicitly construct a deep copy of the provided value. + + Args: + other: The value to copy. + + Returns: + The constructed Bool value. + """ + return Self {value: other.value} + @always_inline("nodebug") fn __init__(value: __mlir_type.i1) -> Bool: """Construct a Bool value given a __mlir_type.i1 value. From af87b6e2b61416fe81cd5fba499fbdf5f4255456 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 5 Jun 2024 16:58:58 -0600 Subject: [PATCH 0902/2019] [stdlib] Add ability to run benchmarks externally As part of getting ready to open source the micro benchmarks for the Mojo standard library, do a few things: - Add a `run-benchmarks.sh` script, similar to `run-tests.sh`. The former will run all of the benchmarks where the latter runs all of the unit tests. Note that you'll typically want to run just an individual benchmark at a time though when doing perf comparisons. - Update the `stdlib/test/lit.cfg.py` to put the test execution results in a nested subdirectory "test" so we can discern test execution for unit tests and benchmarks. Note: - In the near future, we'll ship the micro benchmarks themselves. These scripts (such as `run-benchmark.sh`) won't do anything yet for external contributors until we ship the benchmark files themselves. Stay tuned! MODULAR_ORIG_COMMIT_REV_ID: 6a5c7aac3924f7608e508f3cf60a2d37d377d8ac --- stdlib/benchmarks/lit.cfg.py | 59 ++++++++++++++++++++++++++++---- stdlib/scripts/run-benchmarks.sh | 35 +++++++++++++++++++ stdlib/test/lit.cfg.py | 2 +- 3 files changed, 88 insertions(+), 8 deletions(-) create mode 100755 stdlib/scripts/run-benchmarks.sh diff --git a/stdlib/benchmarks/lit.cfg.py b/stdlib/benchmarks/lit.cfg.py index 8282c244b6..d7f1b7045c 100644 --- a/stdlib/benchmarks/lit.cfg.py +++ b/stdlib/benchmarks/lit.cfg.py @@ -25,11 +25,56 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes = [".mojo"] -# test_source_root: The root path where tests are located. -config.test_source_root = Path(__file__).parent.resolve() - -# test_exec_root: The root path where tests should be run. -config.test_exec_root = os.path.join( - config.modular_obj_root, "open-source", "mojo", "stdlib", "benchmarks" -) config.substitutions.insert(0, ("%mojo", "mojo")) + +# Internal testing configuration. This environment variable +# is set by the internal `start-modular.sh` script. +if "_START_MODULAR_INCLUDED" in os.environ: + # test_source_root: The root path where tests are located. + config.test_source_root = Path(__file__).parent.resolve() + + # test_exec_root: The root path where tests should be run. + config.test_exec_root = os.path.join( + config.modular_obj_root, "open-source", "mojo", "stdlib", "benchmarks" + ) +else: + # This is important since `benchmark` is closed source + # still right now and is always used by the benchmarks. + pre_built_packages_path = os.environ.get( + "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + Path(os.environ["MODULAR_HOME"]) + / "pkg" + / "packages.modular.com_nightly_mojo" + / "lib" + / "mojo", + ) + + # test_source_root: The root path where tests are located. + config.test_source_root = Path(__file__).parent.resolve() + + # The `run-tests.sh` script creates the build directory for you. + build_root = Path(__file__).parent.parent.parent / "build" + + # The tests are executed inside this build directory to avoid + # polluting the source tree. + config.test_exec_root = build_root / "stdlib" / "benchmarks" + + # Add both the open source, locally built `stdlib.mojopkg` + # along with the closed source, pre-built packages shipped + # with the Mojo SDK to the appropriate environment variables. + # These environment variables are interpreted by the mojo parser + # when resolving imports. + os.environ[ + "MODULAR_MOJO_NIGHTLY_IMPORT_PATH" + ] = f"{build_root},{pre_built_packages_path}" + + # Pass through several environment variables + # to the underlying subprocesses that run the tests. + lit.llvm.initialize(lit_config, config) + lit.llvm.llvm_config.with_system_environment( + [ + "MODULAR_HOME", + "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + "PATH", + ] + ) diff --git a/stdlib/scripts/run-benchmarks.sh b/stdlib/scripts/run-benchmarks.sh new file mode 100755 index 0000000000..b6525af2d6 --- /dev/null +++ b/stdlib/scripts/run-benchmarks.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +##===----------------------------------------------------------------------===## +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +##===----------------------------------------------------------------------===## + +set -euo pipefail + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +REPO_ROOT="${SCRIPT_DIR}"/../.. +BUILD_DIR="${REPO_ROOT}"/build + +mkdir -p "${BUILD_DIR}" + +source "${SCRIPT_DIR}"/build-stdlib.sh + +TEST_UTILS_PATH="${REPO_ROOT}/stdlib/test/test_utils" +mojo package "${TEST_UTILS_PATH}" -o "${BUILD_DIR}/test_utils.mojopkg" + +BENCHMARK_PATH="${REPO_ROOT}/stdlib/benchmarks" +if [[ $# -gt 0 ]]; then + # If an argument is provided, use it as the specific file or directory. + BENCHMARK_PATH=$1 +fi + +# Run the benchmarks +lit -sv "${BENCHMARK_PATH}" diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index e50d82cc77..a1af3fc316 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -77,7 +77,7 @@ def has_not(): # The tests are executed inside this build directory to avoid # polluting the source tree. - config.test_exec_root = build_root / "stdlib" + config.test_exec_root = build_root / "stdlib" / "test" # The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the # open-source stdlib, we need to specify the paths to the just-built From a9b3216b5a64092832c7414acc2604ddad278bda Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 5 Jun 2024 16:15:51 -0700 Subject: [PATCH 0903/2019] [mojo-stdlib] Fix Arc.getitem to return a mutable reference. This type should yield a mutable reference even when self is immutable. We don't currently have scoped lifetimes, so this is a bit of an ugly hack, but this will get the client side right at least. MODULAR_ORIG_COMMIT_REV_ID: c52441416e433da65c8f2184823ee7b033815433 --- stdlib/src/memory/arc.mojo | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index cfd5ce3830..f199484c98 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -25,6 +25,7 @@ print(3 == p.get()) from os.atomic import Atomic from memory import UnsafePointer, stack_allocation +from builtin.builtin_list import _lit_mut_cast struct _ArcInner[T: Movable]: @@ -99,10 +100,22 @@ struct Arc[T: Movable](CollectionElement): (self._inner).destroy_pointee() self._inner.free() - # FIXME: This isn't right - the element should be mutable regardless - # of whether the 'self' type is mutable. - fn __getitem__(ref [_]self: Self) -> ref [__lifetime_of(self)] T: - """Returns a Reference to the managed value. + # FIXME: The lifetime returned for this is currently self lifetime, which + # keeps the Arc object alive as long as there are references into it. That + # said, this isn't really the right modeling, we need hierarchical lifetimes + # to model the mutability and invalidation of the returned reference + # correctly. + fn __getitem__[ + self_life: ImmutableLifetime + ]( + ref [self_life]self: Self, + ) -> ref [ + _lit_mut_cast[self_life, result_mutable=True].result + ] T: + """Returns a mutable Reference to the managed value. + + Parameters: + self_life: The lifetime of self. Returns: A Reference to the managed value. From 7c175a6cf422bb8022153710fbc7f7725577b327 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 5 Jun 2024 18:37:55 -0500 Subject: [PATCH 0904/2019] [External] [CI] Run all unit tests with assertions enabled and disabled (#41436) [External] [CI] Run all unit tests with assertions enabled and disabled Follow-up on https://github.com/modularml/mojo/pull/2718#discussion_r1609049240 Note: - Since Copybara isn't managing the `.github` directory, split the non-CI changes from the `.github` directory changes for adding the CI for running the unit tests with and without assertions. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2947 MODULAR_ORIG_COMMIT_REV_ID: b30481fc61f237b994095c68515ab8e0cec417b4 --- stdlib/docs/development.md | 4 ++++ .../builtin/test_debug_assert_mojo_enable_assertions.mojo | 2 +- stdlib/test/lit.cfg.py | 7 ++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index c88d5cde82..8afdb6ff91 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -130,6 +130,10 @@ Reminder that if you’re choosing to invoke `lit` directly and not use the system right now to ensure these dependencies are up-to-date before running the tests. +This is rarely useful, but if you wish to run the unit tests with assertions +disabled, you can set the environment +variable `MOJO_ENABLE_ASSERTIONS_IN_TESTS=0`. + If you run into any issues when running the tests, [please file an issue](https://github.com/modularml/mojo/issues) and we’ll take a look. diff --git a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo index 112daa76f8..206e119938 100644 --- a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo +++ b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %mojo -debug-level full %s 2>&1 | FileCheck %s +# RUN: not --crash %bare-mojo -D MOJO_ENABLE_ASSERTIONS -debug-level full %s 2>&1 | FileCheck %s # CHECK-LABEL: test_fail diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index a1af3fc316..ce41cab3c9 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -44,7 +44,12 @@ def has_not(): # (only matters internally). # In the future, we can do other fancy things like with sanitizers # and build type. -config.substitutions.insert(0, ("%mojo", "mojo -D MOJO_ENABLE_ASSERTIONS")) +if bool(int(os.environ.get("MOJO_ENABLE_ASSERTIONS_IN_TESTS", 1))): + base_mojo_command = "mojo -D MOJO_ENABLE_ASSERTIONS" +else: + print("Running tests with assertions disabled.") + base_mojo_command = "mojo" +config.substitutions.insert(0, ("%mojo", base_mojo_command)) # Mojo without assertions. Only use this for known tests that do not work # with assertions enabled. From 0286e9ea17c96829560e29280e66ddaacc1a6079 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 5 Jun 2024 17:13:05 -0700 Subject: [PATCH 0905/2019] Updating Docs to show how the new behavior of linking to libpython at runtime works. Also adds a script to find a suitable libpython on the user's machine and printing a command to resolve. Removes workarounds and notes for the previous behaviour. MODULAR_ORIG_COMMIT_REV_ID: 078a157d54db22ad8e9cea772770ac7451405292 --- docs/changelog-released.md | 8 ++ docs/manual/python/index.ipynb | 158 +++++++++++++++++++++++---------- docs/roadmap.md | 21 ----- 3 files changed, 120 insertions(+), 67 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 858ec28393..edfb3054cb 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -516,6 +516,14 @@ modular update mojo removed `let` declarations but still provided an error message to users. Now, it is completely gone from the grammar. Long live `var`! +- Mojo will now link to a Python dynamic library based on the Python on top of + your search path: `PATH`. This enables you to activate a virtual environment + like `conda` and have access to Python modules installed in that environment + without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a + `libpython` dynamic library on installation and put the path in + `.modular/modular.cfg`, which could result in version conflicts if you + activated a virtual environment of a different Python version. + - The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved from `math` to `builtin`, so you no longer need to do `from math import abs, round, min, max, divmod, pow`. diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 374bbbe44a..94d6ab5f2a 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -261,86 +261,152 @@ "\n", "## Python environment\n", "\n", - "The Mojo SDK depends on an existing installed version of Python that includes\n", - "a shared library version of the Python interpreter. When you install the Mojo\n", - "SDK, it tries to locate a compatible version of the Python interpreter and set\n", - "up Python's `sys.path` to load matching modules. In most cases this just works\n", - "and you don't have to do any further configuration of your Python environment. \n", + "The Mojo SDK depends on an existing Python dynamic library. At runtime, Mojo\n", + "uses the first Python in the search path (`PATH`), to find an associated dynamic\n", + "Python library of the same version. This will also add any modules from the\n", + "activated virtual environment. \n", "\n", - "If you run into problems after installing Mojo, see the following sections.\n", + "### Resolving issues\n", "\n", - "### Installation issues\n", + "Finding libpython may fail if the Python interpreter on top of `PATH` does not\n", + "have an associated dynamic library. Some Python distributions don't include the\n", + "shared library, and others only have a static library which isn't supported by\n", + "Mojo yet.\n", "\n", - "When the installer runs, it tries to locate the CPython shared library using the \n", - "[find_libpython](https://pypi.org/project/find-libpython/) module.\n", - "\n", - "This may fail if one of the following is true:\n", + "You can find a compatible Python on your system by running this Python script:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "python" + } + }, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "\n", + "FIND_LIBPYTHON = \"\"\"\n", + "import os\n", + "import sys\n", + "from pathlib import Path\n", + "from sysconfig import get_config_var\n", + "ext = \"dll\" if os.name == \"nt\" else \"dylib\" if sys.platform == \"darwin\" else \"so\"\n", + "binary = f\"libpython{get_config_var('py_version_short')}.{ext}\"\n", + "for folder in [Path(get_config_var(p)) for p in [\"LIBPL\", \"LIBDIR\"]]:\n", + " libpython_path = folder / binary\n", + " if libpython_path.is_file():\n", + " print(libpython_path.resolve())\n", + " exit(0)\n", + "exit(1)\n", + "\"\"\"\n", + "FIND_PYTHON_VER = \"import sysconfig; print(sysconfig.get_python_version())\"\n", + "\n", + "exe_names = [\"python3\", \"python\"] + [f\"python3.{i}\" for i in range(8, 13)]\n", + "seen = []\n", + "executables = []\n", + "\n", + "print(\"Mojo will attempt to use the first python executable from the top:\\n\")\n", + "print(\"vers | compat | path\")\n", + "for path in os.environ[\"PATH\"].split(\":\"):\n", + " for exe in exe_names:\n", + " full_path = os.path.join(path, exe)\n", + " if os.path.exists(full_path):\n", + " pyver = subprocess.check_output([full_path, \"-c\", FIND_PYTHON_VER], text=True).strip()\n", + " res = subprocess.run([full_path, \"-c\", FIND_LIBPYTHON], text=True, capture_output=True)\n", + " libpython = res.stdout.strip()\n", + " if res.returncode != 0:\n", + " print(f\"{pyver:<7} no {full_path}\")\n", + " elif libpython not in seen:\n", + " print(f\"{pyver:<7} yes {full_path}\")\n", + " seen.append(libpython)\n", + " executables.append(full_path)\n", + "\n", + "if not executables:\n", + " print(\"no compatible Python environments found\")\n", + "else:\n", + " print(\"\\ncreate and activate a virtual environment to use a different Python version:\")\n", + " print(f\" {executables[-1]} -m venv .venv\")\n", + " print(\" source .venv/bin/activate\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Which will produce output like:\n", "\n", - "- There is no version of Python installed, or the installed version isn't\n", - " supported by the Mojo SDK.\n", + "```output\n", + "Mojo will attempt to use the first python executable from the top:\n", "\n", - "- The installer can't find a shared library version of the CPython interpreter \n", - " (for example, `.so` or `.dylib` file). Some Python distributions don't include\n", - " shared libraries, which prevents Mojo from embedding the interpreter.\n", + "vers | compat | path\n", + "3.11 yes /opt/homebrew/opt/python@3.11/libexec/bin/python3\n", + "3.12 yes /opt/homebrew/bin/python3\n", + "3.9 yes /usr/bin/python3\n", "\n", - "If one of these things is the case, you'll need to install a compatible version\n", - "of Python that includes shared libraries. Try following the instructions in \n", - "[Set up a Python environment with Conda](#set-up-a-python-environment-with-conda)\n", - "to install a virtual environment. \n", + "create and activate a virtual environment to use a different Python version:\n", + " /usr/bin/python3 -m venv .venv\n", + " source .venv/bin/activate\n", + "```\n", "\n", + "If you have no compatible environment, you can install a compatible version of\n", + "Python that includes shared libraries. Try following the instructions in [Set up\n", + "a Python environment with Conda](#set-up-a-python-environment-with-conda) to\n", + "install a virtual environment. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "### Set up a Python environment with Conda\n", "\n", - "Using a Python virtual environment like \n", - "[Conda](https://docs.conda.io/en/latest/) is one way to avoid problems with \n", - "your Python installation. This provides a consistent Python environment with a\n", - "known version of Python and all of the Python packages you want to use with\n", - "Mojo.\n", + "Using a Python virtual environment such as \n", + "[Conda](https://docs.conda.io/en/latest/) is one way to get a version of Python\n", + "that will work reliably with Mojo. It comes with the required dynamic library,\n", + "and ensures there are no conflicts with system dependencies. \n", "\n", "To set up a virtual environment with Conda:\n", "\n", "1. Install Conda by following the \n", " [Quick command-line install instructions](https://docs.conda.io/projects/miniconda/en/latest/#quick-command-line-install).\n", "\n", - " Make sure to initialize Conda for the shell or shells you use, for example:\n", - "\n", - " ```bash\n", - " ~/miniconda3/bin/conda init zsh\n", - " ```\n", - "\n", - " Or:\n", + "2. Initialize Conda for all the shells on your path:\n", "\n", " ```bash\n", " ~/miniconda3/bin/conda init --all\n", " ```\n", "\n", - "2. Restart your shell. \n", - "\n", - "3. Run the following command to configure Mojo to use the Python shared library\n", - " from your Conda environment:\n", + " Or just one at a time:\n", "\n", " ```bash\n", - " export MOJO_PYTHON_LIBRARY=\"$(find $CONDA_PREFIX/lib -iname 'libpython*.[s,d]*' | sort -r | head -n 1)\"\n", - " echo \"export MOJO_PYTHON_LIBRARY=$MOJO_PYTHON_LIBRARY\" >> ~/.zshrc\n", + " ~/miniconda3/bin/conda init zsh\n", " ```\n", "\n", - " **Note:** If you're using a shell other than zsh, you'll need to adjust these\n", - " commands. For example, if you're using bash, replace `.zshrc` with the\n", - " shell configuration file you use, such as `.bashrc` or `.bash_profile`.\n", + "2. Restart your shell. \n", "\n", - "4. Try running the Mojo REPL:\n", + "3. Install your desired version of Python and activate the environment:\n", "\n", - " ```bash\n", - " mojo\n", - " ```\n", + " ```bash\n", + " conda create -n 3.10 python=3.10\n", + " conda activate 3.10\n", + " ```\n", "\n", "After setting up the Conda virtual environment, you can install any Python \n", - "packages you want to use with Mojo using the `conda install` command. For\n", + "packages you want to use with Mojo, with `conda install` or `pip install`. For\n", "example:\n", "\n", "```bash\n", "conda install numpy\n", + "pip install pillow\n", "```\n", "\n", + "Now whenever you `conda activate 3.10`, Mojo will be able to find any modules\n", + "you installed into that environment.\n", + "\n", "For more information on using Conda with Mojo, see \n", "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on\n", "the Modular Blog." diff --git a/docs/roadmap.md b/docs/roadmap.md index 7b84532a6e..31529aae5e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -94,27 +94,6 @@ systems. Here are some of the notable issues that we plan to fix: processors and Apple Silicon macOS. Support for more Linux distributions (including Debian and RHEL) and Windows is in progress. -- Python interoperability might fail when running a compiled Mojo program, with - the message - `Unable to locate a suitable libpython, please set MOJO_PYTHON_LIBRARY`. This - is because we currently do not embed the Python version into the Mojo binary. - For details and the workaround, see [issue - #551](https://github.com/modularml/mojo/issues/551). - -- Mojo programs that import NumPy might fail with the following error: - - ```plaintext - Importing the numpy C-extensions failed. This error can happen for - many reasons, often due to issues with your setup or how NumPy was - installed. - ``` - - This may occur because the version of NumPy doesn't match the Python - interpreter Mojo is using. As a workaround, follow the instructions in - [issue #1085](https://github.com/modularml/mojo/issues/1085#issuecomment-1771403719) - to install a Python virtual environment using Conda. This can solve many - issues with Python interoperability. - - Modular CLI install might fail and require `modular clean` before you re-install. From 901fe990d70a7a1033eaa594168b27862f3c9a1e Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Wed, 5 Jun 2024 19:31:10 -0500 Subject: [PATCH 0906/2019] [External] [stdlib] Remove SliceNew.__getitem__ (#41441) [External] [stdlib] Remove SliceNew.__getitem__ Fixes https://github.com/modularml/mojo/issues/2953 slice in python does not implement `__getitem__` and with the introduction of `indices()` it will also no longer be used when we've migrated to `SliceNew` in the stdlib containers. The current implementation is also incorrect when using negative indices anyways. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2963 MODULAR_ORIG_COMMIT_REV_ID: 946d821005c7a2dc49d40cb189bf0b8c180fc250 --- stdlib/src/builtin/builtin_slice.mojo | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index a90ca98461..7739d98066 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -332,18 +332,6 @@ struct SliceNew(Stringable, EqualityComparable): return len(range(self.start.value(), self.end.value(), self.step)) - @always_inline - fn __getitem__(self, idx: Int) -> Int: - """Get the slice index. - - Args: - idx: The index. - - Returns: - The slice index. - """ - return self.start.value() + idx * self.step - fn indices(self, src_len: Int) -> (Int, Int, Int): """Returns a tuple of 3 intergers representing the start, end, and step of the slice if applied to a container of the given length. From cee459b4ffa43f790060a264160b0f8741c722a7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 5 Jun 2024 18:42:34 -0600 Subject: [PATCH 0907/2019] [stdlib] Add `README` for stdlib benchmarks (#41390) Add a `README.md` targeted at external contributors on where we're at with the microbenchmarking for the Mojo standard library. MODULAR_ORIG_COMMIT_REV_ID: 5c86d6e193acbed5240eb22a08825ef5c5c3ff29 --- stdlib/benchmarks/README.md | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 stdlib/benchmarks/README.md diff --git a/stdlib/benchmarks/README.md b/stdlib/benchmarks/README.md new file mode 100644 index 0000000000..dbae23362f --- /dev/null +++ b/stdlib/benchmarks/README.md @@ -0,0 +1,58 @@ +# Mojo standard library benchmarks + +This document covers the benchmarks provided for the Mojo +standard library. + +## Layout + +There is 1-1 correspondence between the directory structure of +the benchmarks and their source counterpart. For example, +consider `collections/bench_dict.mojo` and its source counterpart: +`collections/dict.mojo`. This organization makes it easy to stay +organized. + +Benchmark files should be prefixed with `bench_` in the filename. +This is helpful for consistency, but also is recognized by tooling +internally. + +## How to run the benchmarks + +If you want to just compile and run all of the benchmarks as-is, +there is a script provided [here](../../stdlib/scripts/run-benchmarks.sh). +This script builds the open source `stdlib.mojopkg` and then executes +all of the benchmarks. Note that, by default, the executor (`llvm-lit`) +will run all of these benchmarks in parallel. That is not terribly useful +in practice for doing performance comparisons. When doing A-B comparisons, +I recommend just rebuilding the `stdlib.mojopkg` if needed using +the [build-stdlib script](../../stdlib/scripts/build-stdlib.sh) and then +just running `mojo` directly for the individual benchmark file. For example, +`mojo stdlib/benchmarks/collections/bench_dict.mojo` as run from the root +of the repo. As part of the benchmark configs, there is a mechanism +for specifying the number of repetitions to run the benchmark, warmup iterations, +and other things. + +## How to write effective benchmarks + +All of the benchmarks use the `benchmark` module. `Bench` objects are built +on top of the `benchmark` module. You can also use `BenchConfig` to configure +`Bench`. For the most part, you can copy-paste from existing +benchmarks to get started. + +Note that the `benchmark` package isn't open source yet and we do not currently +have a mechanism for generating nightly API docs for closed source packages. +So, we manually provide relatively up-to-date docs for these [here](../../docs/bencher/). +In the future, we hope to open source the `benchmark` package and and also generate +nightly API docs. This is definitely a rough edge, but bear with us! We eagerly +wanted to get these benchmarks out to the public even though we fully understand +the experience is not perfect right now. + +## Benchmarks in CI + +Currently, there is no short-term plans for adding these benchmarks with regression +detection and such in the public Mojo CI. We're working hard to improve the processes +for this internally first before we commit to doing this in the external repo. + +## Other reading + +Check out our [blog post](https://www.modular.com/blog/how-to-be-confident-in-your-performance-benchmarking) +for more info on writing benchmarks. From ac7016d94d6ee04977a205b23bac7ad5795b97f0 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 5 Jun 2024 18:38:09 -0700 Subject: [PATCH 0908/2019] [Docs] Mojo 24.4 release changelog updates. It's a changelog. For Mojo. For 24.4. MODULAR_ORIG_COMMIT_REV_ID: 2904183a16dd2b5fd6fe02c993ed8441b0274da0 --- docs/changelog-released.md | 1110 ++++++++++++++++++++---------------- 1 file changed, 616 insertions(+), 494 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index edfb3054cb..d89301e596 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,53 +25,87 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` -## UNRELEASED +## v24.4 (2024-06-06) -### 🔥 Legendary +### ✨ Highlights -### ⭐️ New +Big themes for this release: -- Add a `sort` function for list of `ComparableCollectionElement`s. - [PR #2609](https://github.com/modularml/mojo/pull/2609) by - [@mzaks](https://github.com/mzaks) +- Improvements to the performance and ease-of-use for `def` functions. -- Mojo functions can return an auto-dereferenced reference to storage with a - new `ref` keyword in the result type specifier. For example: +- Continued unification of standard library APIs around the `UnsafePointer` + type. + +- Many quality-of-life improvements for the standard library collection types. + +- Significant performance improvements when inserting into a `Dict`. Performance + on this metric is still not where we'd like it to be, but it is much improved. + +- A new `@parameter for` mechanism for expressing compile-time loops, which + replaces the earlier (and less reliable) `@unroll` decorator. + +- New Mojo Manual pages on [Control flow](/mojo/manual/control-flow), + [Testing](/mojo/tools/testing) and using + [unsafe pointers](/mojo/manual/pointers). + +### Language changes + +- Mojo has changed how `def` function arguments are processed. Previously, by + default, arguments to a `def` were treated treated according to the `owned` + convention, which makes a copy of the value, enabling that value to be mutable + in the callee. + + This could lead to a major performance issues because of the proliferation of + unnecessary copies. It also required you to declare non-copyable types as + `borrowed` explicitly. Now Mojo takes a different approach: `def` functions + take arguments as `borrowed` by default (consistent with `fn` functions) but + will make a local copy of the value **only if the argument is mutated** in the + body of the function. + + This improves consistency, performance, and ease of use. + +- Implicit variable definitions in a `def` function are more flexible: you can + now implicitly declare variables as the result of a tuple return, using + `a,b,c = foo()`. For example: ```mojo - struct Pair: - var first: Int - var second: Int - fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: - return self.first - fn show_mutation(): - var somePair = ... - get_first_ref(somePair) = 1 - ``` + def return_two(i: Int) -> (Int, Int): + return i, i+1 - This approach provides a general way to return an "automatically dereferenced" - reference of a given type. Notably, this eliminates the need for - `__refitem__` to exist. `__refitem__` has thus been removed and replaced with - `__getitem__` that returns a reference. + a, b = return_two(5) + ``` -- Mojo has introduced `@parameter for`, a new feature for compile-time - programming. `@parameter for` defines a for loop where the sequence and the - induction values in the sequence must be parameter values. For example: + Implicit variable declarations can also now shadow global immutable symbols + (such as module names and built-ins) without getting a compiler error. + For example: ```mojo - fn parameter_for[max: Int](): - @parameter - for i in range(max) - @parameter - if i == 10: - print("found 10!") + slice = foo() ``` - Currently, `@parameter for` requires the sequence's `__iter__` method to - return a `_StridedRangeIterator`, meaning the induction variables must be - `Int`. The intention is to lift these restrictions in the future. +- Mojo functions can return an auto-dereferenced reference to storage with a + new `ref` keyword in the result type specifier. For example: + + ```mojo + @value + struct Pair: + var first: Int + var second: Int + + fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + return self.first -- Mojo added support for the inferred parameters. Inferred parameters must + fn show_mutation(): + var somePair = Pair(5, 6) + somePair.get_first_ref() = 1 + ``` + + This approach provides a general way to return an "automatically dereferenced" + reference of a given type. Notably, this eliminates the need for + `__refitem__()` to exist. `__refitem__()` has thus been removed and replaced + with `__getitem__()` that returns a reference. + +- Mojo added support for _infer-only parameters_. Infer-only parameters must appear at the beginning of the parameter list and cannot be explicitly specified by the user. They are declared to the left of a `//` marker, much like positional-only parameters. This allows programmers to define functions @@ -87,8 +121,8 @@ modular update mojo ``` In the above example, `Int32(42)` is passed directly into `value`, the first - non-inferred parameter. `dt` is inferred from the parameter itself to be - `DType.int32`. + parameter that isn't infer-only. `dt` is inferred from the parameter itself + to be `DType.int32`. This also works with structs. For example: @@ -100,7 +134,9 @@ modular update mojo pass ``` - This should make working with dependent parameters more ergonomic. + This should make working with dependent parameters more ergonomic. See + [Infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) in the + Mojo Manual. - Mojo now allows functions overloaded on parameters to be resolved when forming references to, but not calling, those functions. For example, the following @@ -118,10 +154,10 @@ modular update mojo ``` - Mojo now supports adding a `@deprecated` decorator on structs, functions, - traits, aliases, and global variables. The decorator marks the attached decl - as deprecated and causes a warning to be emitted when the deprecated decl is - referenced in user code. The decorator requires a deprecation message to be - specified as a string literal. + traits, aliases, and global variables. The decorator marks the attached + declaration as deprecated and causes a warning to be emitted when the + deprecated declaration is referenced in user code. The decorator requires a + deprecation message, specified as a string literal. ```mojo @deprecated("Foo is deprecated, use Bar instead") @@ -139,489 +175,555 @@ modular update mojo bar() # warning: use another function! ``` -- Mojo has changed how `def` arguments are processed. Previously, by default, - arguments to a `def` were treated treated according to the `owned` convention, - which makes a copy of the value, enabling that value to be mutable in the callee. - This "worked", but was a major performance footgun, and required you to declare - non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: - it takes the arguments as `borrowed` (consistent with `fn`s) but will make a local - copy of the value **only if the argument is mutated** in the body of the function. - This improves consistency, performance, and ease of use. - -- `int()` can now take a string and a specified base to parse an integer from a - string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is - specified, the string will be parsed as if it was an integer literal, with the - base determined by whether the string contains the prefix `"0x"`, `"0o"`, or - `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273) by - [@artemiogr97](https://github.com/artemiogr97), fixes - [#2274](https://github.com/modularml/mojo/issues/2274)) - -- Mojo now supports types to opt in to use the `abs` and `round` functions by - implementing the `__abs__` and `__round__` methods (i.e. by conforming to the - new `Absable` and `Roundable` traits), respectively, e.g.: +- Mojo has introduced + [`@parameter for`](/mojo/manual/decorators/parameter#parametric-for-statement), + a new feature for compile-time programming. `@parameter for` defines a for + loop where the sequence and the induction values in the sequence must be + parameter values. For example: ```mojo - from math import sqrt - - @value - struct Complex(Absable, Roundable): - var re: Float64 - var im: Float64 - - fn __abs__(self) -> Self: - return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) - - fn __round__(self) -> Self: - return Self(round(self.re), round(self.im)) - ``` - -- User defined types can now also opt in to use the `pow` function by - implementing the `__pow__` method (and thus conforming to the new `Powable` - trait). As before, these types will also benefit from being able to use the - `**` operator. - -- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` - functions in the `math` module by implementing the `__floor__()`, - `__ceil__()`, and `__trunc__()` methods (and so conforming to the new - `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). - For example: - - ```mojo - from math import Ceilable, Floorable, Truncable, ceil, floor, trunc - - @value - struct Complex(Ceilable, Floorable, Truncable): - var re: Float64 - var im: Float64 - - fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) - - fn __floor__(self) -> Self: - return Self(floor(re), floor(im)) - - fn __trunc__(self) -> Self: - return Self(trunc(re), trunc(im)) - ``` - -- You can now use the builtin `any()` and `all()` functions to check for truthy - elements in a collection. Because `SIMD.__bool__()` is now constrained to - `size=1`, You must explicity use these to get the truthy value of a SIMD - vector. This avoids common bugs around implicit conversion of `SIMD` to - `Bool`. - ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) - - For example: - - ```mojo - fn truthy_simd(): - var vec = SIMD[DType.int32, 4](0, 1, 2, 3) - if any(vec): - print("any elements are truthy") - if all(vec): - print("all elements are truthy") + fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") ``` -- Add an `InlinedArray` type that works on memory-only types. - Compare with the existing `StaticTuple` type, which is conceptually an array - type, but only worked on `AnyTrivialRegType`. - ([PR #2294](https://github.com/modularml/mojo/pull/2294) by [@lsh](https://github.com/lsh)) - -- Base64 decoding support has been added. - ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) - -- Add Base16 encoding and decoding support. - ([PR #2584](https://github.com/modularml/mojo/pull/2584) - by [@kernhanda](https://github.com/kernhanda)) - -- Add `repr()` function and `Representable` trait. - ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - -- Add `SIMD.shuffle()` with `StaticIntTuple` mask. - ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) - -- Invoking `mojo package my-package -o my-dir` on the command line, where - `my-package` is a Mojo package source directory, and `my-dir` is an existing - directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. - Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. - -- The Mojo Language Server now reports a warning when a local variable is unused. - -- Implicit variable definitions in a `def` are more flexible: you can now - implicitly declare variables as the result of a tuple return, using - `a,b,c = foo()`, and can now shadow global immutable symbols using - `slice = foo()` without getting a compiler error. - -- The `math` module now has `CeilDivable` and `CeilDivableRaising` traits that - allow users to opt into the `math.ceildiv` function. - -- Mojo now allows methods to declare `self` as a `Reference` directly, which - can be useful for advanced cases of parametric mutabilty and custom lifetime - processing. Previously it required the use of an internal MLIR type to - achieve this. + Currently, `@parameter for` requires the sequence's `__iter__()` method to + return a `_StridedRangeIterator`, meaning the induction variables must be + `Int`. The intention is to lift these restrictions in the future. - The `is_mutable` parameter of `Reference` and `AnyLifetime` is now a `Bool`, not a low-level `__mlir_type.i1` value. This improves the ergonomics of spelling out a - `Reference` type explicitly. For example, to define a struct holding a - `Reference`, you can now write: - - ```mojo - struct Foo[is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type]: - var data: Reference[Int32, is_mutable, lifetime] - ``` - - Or to specify a field that is always immutable, `False` can be specified - as the mutability: + `Reference` type explicitly. - ```mojo - struct Foo[lifetime: AnyLifetime[False].type]: - var data: Reference[Int32, False, lifetime] - ``` +- Mojo will now link to a Python dynamic library based on the Python on top of + your search path: `PATH`. This enables you to activate a virtual environment + like `conda` and have access to Python modules installed in that environment + without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a + `libpython` dynamic library on installation and put the path in + `.modular/modular.cfg`, which could result in version conflicts if you + activated a virtual environment of a different Python version. -- `object` now implements all the bitwise operators. - ([PR #2324](https://github.com/modularml/mojo/pull/2324) by [@LJ-9801](https://github.com/LJ-9801)) +- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids + binding non-trivial register-passable types to `AnyTrivialRegType`. This + closes a major safety hole in the language. Please use `AnyType` for generic + code going forward. -- A new `--validate-doc-strings` option has been added to `mojo` to emit errors - on invalid doc strings instead of warnings. +- The `let` keyword has been completely removed from the language. We previously + removed `let` declarations but still provided an error message to users. Now, + it is completely gone from the grammar. -- Several `mojo` subcommands now support a `--diagnostic-format` option that - changes the format with which errors, warnings, and other diagnostics are - printed. By specifying `--diagnostic-format json` on the command line, errors - and other diagnostics will be output in a structured - [JSON Lines](https://jsonlines.org) format that is easier for machines to - parse. +### Standard library changes - The full list of subcommands that support `--diagnostic-format` is as follows: - `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. - Further, the `mojo test --json` option has been subsumed into this new option; - for the same behavior, run `mojo test --diagnostic-format json`. +- New traits and related features: - Note that the format of the JSON output may change; we don't currently - guarantee its stability across releases of Mojo. + - Added built-in [`repr()`](mojo/stdlib/builtin/repr/repr) function and + [`Representable`](/mojo/stdlib/builtin/repr/Representable) trait. + ([PR #2361](https://github.com/modularml/mojo/pull/2361)) -- A new decorator, `@doc_private`, was added that can be used to hide a decl - from being generated in the output of `mojo doc`. It also removes the - requirement that the decl has documentation (e.g. when used with - --diagnose-missing-doc-strings). + - Added the [`Indexer`](/mojo/stdlib/builtin/int/Indexer) trait to denote + types that implement the `__index__()` method which allows these types to be + accepted in common `__getitem__()` and `__setitem__()` implementations, as + well as allow a new built-in + [`index()`](/mojo/stdlib/builtin/int/index-function) + function to be called on them. Most standard library containers can now be + indexed by any type that implements `Indexer`. For example: -- Added a new `Span` type for taking slices of contiguous collections. - ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 -- Added a new `StringSlice` type, to replace uses of the unsafe `StringRef` type - in standard library code. + struct MyList: + var data: List[Int] - `StringSlice` is a non-owning reference to encoded string data. Unlike - `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it - points to. + fn __init__(inout self): + self.data = List[Int](1, 2, 3, 4) - - Add new `as_string_slice()` method to `String` and `StringLiteral`. - - Add `StringSlice` intializer from an `UnsafePointer` and a length in bytes. - - Changed `Formatter.write_str()` to take a safe `StringSlice`. + fn __getitem__[T: Indexer](self, idx: T) -> Int: + return self.data[index(idx)] -- Added a new `as_bytes_slice()` method to `String` and `StringLiteral`, which - returns a `Span` of the bytes owned by the string. + print(MyList()[AlwaysZero()]) # prints `1` + ``` -- Add new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers + Types conforming to the `Indexer` trait are implicitly convertible to Int. + This means you can write generic APIs that take `Int` instead of making them + take a generic type that conforms to `Indexer`. For example: -- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 -- Removed the `UnsafePointer[T].get_null()` method (and from other pointers), - please use the default constructor instead: `UnsafePointer[T]()`. + @value + struct Incrementer: + fn __getitem__(self, idx: Int) -> Int: + return idx + 1 -- `Dict` now implements `get(key)` and `get(key, default)` functions. - ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) + var a = Incrementer() + print(a[AlwaysZero()]) # works and prints 1 + ``` -- Debugger users can now set breakpoints on function calls in O0 builds even if - the call has been inlined by the compiler. + ([PR #2685](https://github.com/modularml/mojo/pull/2685)) -- The `os` module now provides functionality for adding and removing directories - using `mkdir` and `rmdir`. - ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) + - Added traits allowing user-defined types to be supported by various + built-in and math functions. -- `Dict.__get_ref(key)`, allowing to get references to dictionary values. + | Function | Trait | Required method | + |------------------|------------------|-----------------| + | [`abs()`](/mojo/stdlib/builtin/math/abs) | [`Absable`](/mojo/stdlib/builtin/math/Absable) | `__abs__()` | + | [`pow()`](/mojo/stdlib/builtin/math/pow) | [`Powable`](/mojo/stdlib/builtin/math/Powable) | `__pow__()` | + | [`round()`](/mojo/stdlib/builtin/math/round) | [`Roundable`](/mojo/stdlib/builtin/math/Roundable) | `__round__()` | + | [`math.ceil`](/mojo/stdlib/math/math/ceil) | `math.Ceilable` | `__ceil__()` | + | [`math.ceildiv`](/mojo/stdlib/math/math/ceildiv) | `math.CeilDivable`
`math.CeilDivableRaising` | `__ceildiv__()` | + | [`math.floor`](/mojo/stdlib/math/math/floor) | `math.Floorable` | `__floor__()` | + | [`math.trunc`](/mojo/stdlib/math/math/trunc) | `Truncable` | `__trunc__()` | -- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters - other than whitespace. In addition, there are now several useful aliases for - whitespace, ASCII lower/uppercase, and so on. - ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) + Notes: -- `String` now has a `splitlines()` method, which allows splitting strings at line - boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) - and provides an option to retain or remove the line break characters. - ([PR #2810](https://github.com/modularml/mojo/pull/2810)) by [@YichengDWu](https://github.com/YichengDWu) + - Conforming to the `Powable` trait also means that the type can be used + with the power operator (`**`). -- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. - ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + - For `ceildiv()`, structs can conform to either the `CeilDivable` trait + or `CeilDivableRaising` trait. -- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. - ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) + - Due to ongoing refactoring, the traits `Ceilable`, `CeilDivable`, + `Floorable`, and `Truncable` do not appear in the API reference. They + should be imported from the `math` module, except for `Truncable` which + is (temporarily) available as a built-in trait and does not need to be + imported. -- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. - Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` - is not possible yet. - ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + Example: -- `List()` now supports `__contains__`. - ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) + ```mojo + from math import sqrt -- Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) - type, a stack-allocated list with a static maximum size. - ([PR 2587#](https://github.com/modularml/mojo/pull/2587) by - [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + @value + struct Complex2(Absable, Roundable): + var re: Float64 + var im: Float64 -- `InlineList()` now supports `__contains__`, `__iter__`. - ([PR #2703](https://github.com/modularml/mojo/pull/2703) by [@ChristopherLR](https://github.com/ChristopherLR)) + fn __abs__(self) -> Self: + return Self(sqrt(self.re * self.re + self.im * self.im), 0.0) -- `List` now has an `index` method that allows one to find the (first) location - of an element in a `List` of `EqualityComparable` types. For example: + fn __round__(self) -> Self: + return Self(round(self.re, 0), round(self.im, 0)) - ```mojo - var my_list = List[Int](2, 3, 5, 7, 3) - print(my_list.index(3)) # prints 1 - ``` + fn __round__(self, ndigits: Int) -> Self: + return Self(round(self.re, ndigits), round(self.im, ndigits)) -- `List` can now be converted to a `String` with a simplified syntax: + ``` - ```mojo - var my_list = List[Int](2, 3) - print(my_list.__str__()) # prints [2, 3] - ``` +- Benchmarking: + + - The [`bencher`](/mojo/stdlib/benchmark/bencher/) module as part of the + `benchmark` package is now public and documented. This module provides + types such as `Bencher` which provides the ability to execute a `Benchmark` + and allows for benchmarking configuration via the `BenchmarkConfig` struct. + +- [`String`](/mojo/stdlib/builtin/string/String) and friends: + + - **Breaking.** Implicit conversion to `String` is now removed for builtin + classes/types. Use [`str()`](/mojo/stdlib/builtin/str/str) explicitly to + convert to `String`. + + - Added [`String.isspace()`](/mojo/stdlib/builtin/string/String#isspace) + method conformant with Python's universal separators. This replaces the + `isspace()` free function from the `string` module. (If you need the old + function, it is temporarily available as `_isspace()`. It now takes a + `UInt8` but is otherwise unchanged.) + + - [`String.split()`](/mojo/stdlib/builtin/string/String#split) now defaults to + whitespace and has Pythonic behavior in that it removes all adjacent + whitespace by default. + + - [`String.strip()`](/mojo/stdlib/builtin/string/String#strip), + [`lstrip()`](/mojo/stdlib/builtin/string/String#lstrip) and + [`rstrip()`](/mojo/stdlib/builtin/string/String#rstrip) can now remove + custom characters other than whitespace. In addition, there are now several + useful aliases for whitespace, ASCII lower/uppercase, and so on. + ([PR #2555](https://github.com/modularml/mojo/pull/2555)) + + - `String` now has a + [`splitlines()`](/mojo/stdlib/builtin/string/String#splitlines) method, + which allows splitting strings at line boundaries. This method supports + [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) + and provides an option to retain or remove the line break characters. + ([PR #2810](https://github.com/modularml/mojo/pull/2810)) + + - `InlinedString` has been renamed to + [`InlineString`](/mojo/stdlib/utils/inline_string/InlineString) to be + consistent with other types. + + - [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) now implements + [`strip()`](/mojo/stdlib/utils/stringref/StringRef#strip), which can be used + to remove leading and trailing whitespace. + ([PR #2683](https://github.com/modularml/mojo/pull/2683)) + + - `StringRef` now implements + [`startswith()`](/mojo/stdlib/utils/stringref/StringRef#startswith) and + [`endswith()`](/mojo/stdlib/utils/stringref/StringRef#endswith). + ([PR #2710](https://github.com/modularml/mojo/pull/2710)) + + - Added a new [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) + type, to replace uses of the unsafe `StringRef` type in standard library + code. + + `StringSlice` is a non-owning reference to encoded string data. Unlike + `StringRef`, a `StringSlice` is safely tied to the lifetime of the data it + points to. + + - Added new + [`as_string_slice()`](/mojo/stdlib/builtin/string/String#as_string_slice) + methods to `String` and `StringLiteral`. + - Added `StringSlice` initializer from an `UnsafePointer` and a length in + bytes. + + - Added a new + [`as_bytes_slice()`](/mojo/stdlib/builtin/string/String#as_bytes_slice) + method to `String` and `StringLiteral`, which + returns a `Span` of the bytes owned by the string. + + - Continued transition to + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and + unsigned byte type for strings: + - Renamed `String._as_ptr()` to + [`String.unsafe_ptr()`](/mojo/stdlib/builtin/string/String#unsafe_ptr), + and changed return type to `UnsafePointer` (was `DTypePointer`). + - Renamed `StringLiteral.data()` to + [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr), + and changed return type to `UnsafePointer` (was `DTypePointer`). + - `InlineString.as_ptr()` has been renamed to + [`unsafe_ptr()`](/mojo/stdlib/utils/inline_string/InlineString#unsafe_ptr) + and now returns an `UnsafePointer[UInt8]` (was + `DTypePointer[DType.int8]`). + - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) and + [`StringRef.unsafe_ptr()`](/mojo/stdlib/utils/stringref/StringRef#unsafe_ptr) + now returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + +- Other built-ins: + + - The `Slice.__len__()` function has been removed and + [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) no longer conforms + to the `Sized` trait. This clarifies the ambiguity of the semantics: the + length of a slice always depends on the length of the object being sliced. + Users that need the existing functionality can use the + [`Slice.unsafe_indices()`](/mojo/stdlib/builtin/builtin_slice/Slice#unsafe_indices) + method. This makes it explicit that this implementation does not check if + the slice bounds are concrete or within any given object's length. + + - Added a built-in [`sort()`](/mojo/stdlib/builtin/sort/sort) function for + lists of elements that conform to the + [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement) + trait.([PR #2609](https://github.com/modularml/mojo/pull/2609)) + + - [`int()`](/mojo/stdlib/builtin/int/int-function) can now take a string and a + specified base to parse an integer from a + string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is + specified, the string will be parsed as if it was an integer literal, with + the base determined by whether the string contains the prefix `"0x"`, + `"0o"`, or `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273), + fixes [#2274](https://github.com/modularml/mojo/issues/2274)) + + - Added the [`bin()`](/mojo/stdlib/builtin/format_int/bin) built-in function + to convert integral types into their binary + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603)) + + - Added the [`atof()`](/mojo/stdlib/builtin/string/atof) built-in function, + which can convert a `String` to a `float64`. + ([PR #2649](https://github.com/modularml/mojo/pull/2649)) + + - You can now use the built-in [`any()`](/mojo/stdlib/builtin/bool/any) and + [`all()`](/mojo/stdlib/builtin/bool/all) functions to check for truthy + elements in a collection. Because `SIMD.__bool__()` is now constrained to + `size=1`, You must explicitly use these to get the truthy value of a SIMD + vector with more than one element. This avoids common bugs around implicit + conversion of `SIMD` to `Bool`. + ([PR #2600](https://github.com/modularml/mojo/pull/2600)) + + For example: - Note that `List` doesn't conform to the `Stringable` trait yet so you cannot - use `str(my_list)` yet. - ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + ```mojo + fn truthy_simd(): + var vec = SIMD[DType.int32, 4](0, 1, 2, 3) + if any(vec): + print("any elements are truthy") + if all(vec): + print("all elements are truthy") + ``` -- Added the `Indexer` trait to denote types that implement the `__index__()` - method which allows these types to be accepted in common `__getitem__` and - `__setitem__` implementations, as well as allow a new builtin `index` function - to be called on them. Most stdlib containers are now able to be indexed by - any type that implements `Indexer`. For example: + - [`object`](/mojo/stdlib/builtin/object/) now implements all the bitwise + operators. + ([PR #2324](https://github.com/modularml/mojo/pull/2324)) - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 + - [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) now supports `__contains__()`. + ([PR #2709](https://github.com/modularml/mojo/pull/2709)) For example: - struct MyList: - var data: List[Int] + ```mojo + var x = Tuple(1, 2, True) + if 1 in x: + print("x contains 1") + ``` - fn __init__(inout self): - self.data = List[Int](1, 2, 3, 4) + - [`ListLiteral`](/mojo/stdlib/builtin/builtin_list/ListLiteral) and `Tuple` + now only require that element types be `Movable`. Consequently, + `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - fn __getitem__[T: Indexer](self, idx: T) -> T: - return self.data[index(idx)] + - Added new `ImmutableStaticLifetime` and `MutableStaticLifetime` helpers. - print(MyList()[AlwaysZero()]) # prints `1` - ``` +- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and + others: - ([PR #2685](https://github.com/modularml/mojo/pull/2685) by [@bgreni](https://github.com/bgreni)) + - Added new [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload for + `UnsafePointer[Scalar[_]]` pointers. - Types conforming to the `Indexer` trait are implicitly convertible to Int. - This means you can write generic APIs that take `Int` instead of making them - take a generic type that conforms to `Indexer`, e.g. + - Removed the `get_null()` method from `UnsafePointer` and other pointer + types. Please use the default constructor instead: `UnsafePointer[T]()`. - ```mojo - @value - struct AlwaysZero(Indexer): - fn __index__(self) -> Int: - return 0 + - Many functions returning a pointer type have been unified to have a public + API function of `unsafe_ptr()`. - @value - struct Incrementer: - fn __getitem__(self, idx: Int) -> Int: - return idx + 1 +- Collections: - var a = Incrementer() - print(a[AlwaysZero()]) # works and prints 1 - ``` + - [`List`](/mojo/stdlib/collections/list/List) now has an + [`index()`](/mojo/stdlib/collections/list/List#index) method that allows you + to find the (first) location of an element in a `List` of + `EqualityComparable` types. For example: -- `StringRef` now implements `strip()` which can be used to remove leading and - trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) - by [@fknfilewalker](https://github.com/fknfilewalker)) + ```mojo + var my_list = List[Int](2, 3, 5, 7, 3) + print(my_list.index(3)) # prints 1 + ``` -- The `bencher` module as part of the `benchmark` package is now public - and documented. This module provides types such as `Bencher` which provides - the ability to execute a `Benchmark` and allows for benchmarking configuration - via the `BenchmarkConfig` struct. + - `List` can now be converted to a `String` with a simplified syntax: -- Added the `bin()` builtin function to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603) - by [@bgreni](https://github.com/bgreni)) + ```mojo + var my_list = List[Int](2, 3) + print(my_list.__str__()) # prints [2, 3] + ``` -- Added `atof()` function which can convert a `String` to a `float64`. - ([PR #2649](https://github.com/modularml/mojo/pull/2649) by [@fknfilewalker](https://github.com/fknfilewalker)) + Note that `List` doesn't conform to the `Stringable` trait yet so you cannot + use `str(my_list)` yet. + ([PR #2673](https://github.com/modularml/mojo/pull/2673)) -- `Tuple()` now supports `__contains__`. ([PR #2709](https://github.com/modularml/mojo/pull/2709) - by [@rd4com](https://github.com/rd4com)) For example: + - `List` has a simplified syntax to call the + [`count()`](/mojo/stdlib/collections/list/List#count) method: + `my_list.count(x)`. + ([PR #2675](https://github.com/modularml/mojo/pull/2675)) - ```mojo - var x = Tuple(1, 2, True) - if 1 in x: - print("x contains 1") - ``` + - `List()` now supports `__contains__()`, so you can now use lists with the + `in` operator: -- Added `os.getsize` function, which gives the size in bytes of a path. - ([PR 2626](https://github.com/modularml/mojo/pull/2626) by [@artemiogr97](https://github.com/artemiogr97)) + ```mojo + if x in my_list: + ``` -- `List` now has a method `unsafe_get` to get the reference to an - element without bounds check or wraparound for negative indices. - Note that this method is unsafe. Use with caution. - ([PR #2800](https://github.com/modularml/mojo/pull/2800) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + ([PR #2667](https://github.com/modularml/mojo/pull/2667)) -- Added `fromkeys` method to `Dict` to return a `Dict` with the specified keys - and value. - ([PR 2622](https://github.com/modularml/mojo/pull/2622) by [@artemiogr97](https://github.com/artemiogr97)) + - `List` now has an + [`unsafe_get()`](/mojo/stdlib/collections/list/List#unsafe_get) to get the + reference to an element without bounds check or wraparound for negative + indices. Note that this method is unsafe. Use with caution. + [PR #2800](https://github.com/modularml/mojo/pull/2800)) -- Added `clear` method to `Dict`. - ([PR 2627](https://github.com/modularml/mojo/pull/2627) by [@artemiogr97](https://github.com/artemiogr97)) + - Added a [`fromkeys()`](/mojo/stdlib/collections/dict/Dict#fromkeys) method + to `Dict` to return a `Dict` with the specified keys and values. + ([PR 2622](https://github.com/modularml/mojo/pull/2622)) -- Added `os.path.join` function. - ([PR 2792](https://github.com/modularml/mojo/pull/2792)) by [@artemiogr97](https://github.com/artemiogr97)) + - Added a [`clear()`](/mojo/stdlib/collections/dict/Dict#clear) method to + `Dict`. ([PR 2627](https://github.com/modularml/mojo/pull/2627)) -- `StringRef` now implements `startswith()` and `endswith()`. - ([PR #2710](https://github.com/modularml/mojo/pull/2710) by [@fknfilewalker](https://github.com/fknfilewalker)) + - `Dict` now supports [`reversed()`](/mojo/stdlib/builtin/reversed/reversed) + for its `items()` and `values()` iterators. + ([PR #2340](https://github.com/modularml/mojo/pull/2340)) -- The Mojo Language Server now supports renaming local variables. + - `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. + Note that `Dict` does not conform to the `Stringable` trait so + `str(my_dict)` is not possible yet. + ([PR #2674](https://github.com/modularml/mojo/pull/2674)) -- Added a new `tempfile` module, with `gettempdir` and `mkdtemp` functions. - ([PR 2742](https://github.com/modularml/mojo/pull/2742) by [@artemiogr97](https://github.com/artemiogr97)) + - `Dict` now implements [`get(key)`](/mojo/stdlib/collections/dict/Dict#get) + and `get(key, default)` functions. + ([PR #2519](https://github.com/modularml/mojo/pull/2519)) + + - Added a temporary `__get_ref(key)` method to `Dict`, allowing you to get a + `Reference` to a dictionary value. + + - Added a new [`InlineList`](/mojo/stdlib/collections/inline_list/InlineList) + type, a stack-allocated list with a static maximum size. + ([PR 2587#](https://github.com/modularml/mojo/pull/2587)) + ([PR #2703](https://github.com/modularml/mojo/pull/2703)) + + - Added a new [`Span`](/mojo/stdlib/utils/span/Span) type for taking slices of + contiguous collections. + ([PR #2595](https://github.com/modularml/mojo/pull/2595)) + +- [`os`](/mojo/stdlib/os/os/) module: + + - The `os` module now provides functionality for adding and removing + directories using [`mkdir()`](/mojo/stdlib/os/os/mkdir) and + [`rmdir()`](/mojo/stdlib/os/os/rmdir). + ([PR #2430](https://github.com/modularml/mojo/pull/2430)) + + - Added the [`os.path.getsize()`](/mojo/stdlib/os/path/path/getsize) function, + which gives the size in bytes of the file identified by the path. + ([PR 2626](https://github.com/modularml/mojo/pull/2626)) + + - Added [`os.path.join()`](/mojo/stdlib/os/path/path/join) function. + ([PR 2792](https://github.com/modularml/mojo/pull/2792)) + + - Added a new [`tempfile`](/mojo/stdlib/tempfile/tempfile/) module, with + `gettempdir()` and `mkdtemp()` functions. + ([PR 2742](https://github.com/modularml/mojo/pull/2742)) + +- [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type: + + - Added [`SIMD.shuffle()`](/mojo/stdlib/builtin/simd/SIMD#shuffle) with + `StaticIntTuple` mask. + ([PR #2315](https://github.com/modularml/mojo/pull/2315)) + + - [`SIMD.__bool__()`](/mojo/stdlib/builtin/simd/SIMD#bool) is constrained + such that it only works when `size` is `1`. For SIMD vectors with more than + one element, use [`any()`](/mojo/stdlib/builtin/bool/any) or + [`all()`](/mojo/stdlib/builtin/bool/all). + ([PR #2502](https://github.com/modularml/mojo/pull/2502)) + + - The [`SIMD.reduce_or()`](/mojo/stdlib/builtin/simd/SIMD#reduce_or) and + [`SIMD.reduce_and()`](/mojo/stdlib/builtin/simd/SIMD#reduce_and) methods are + now bitwise operations, and support integer types. + ([PR #2671](https://github.com/modularml/mojo/pull/2671)) + + - Added [`SIMD.__repr__()`](/mojo/stdlib/builtin/simd/SIMD#__repr__) to get + the verbose string representation of `SIMD` types. + ([PR #2728](https://github.com/modularml/mojo/pull/2728)) + +- [`math`](/mojo/stdlib/math/math/) package: + + - The `math.bit` module has been moved to a new top-level + [`bit`](/mojo/stdlib/bit/bit/) module. The following functions in this + module have been renamed: + - `ctlz` -> `countl_zero` + - `cttz` -> `countr_zero` + - `bit_length` -> `bit_width` + - `ctpop` -> `pop_count` + - `bswap` -> `byte_swap` + - `bitreverse` -> `bit_reverse` + + - The `math.rotate_bits_left()` and `math.rotate_bits_right()` functions have + been moved to the `bit` module. + + - The `is_power_of_2()` function in the `math` module is now called + `is_power_of_two()` and located in the `bit` module. + + - The `abs()`, `round()`, `min()`, `max()`, `pow()`, and `divmod()` functions + have moved from `math` to `builtin`, so you no longer need to import these + functions. + + - The `math.tgamma()` function has been renamed to + [`math.gamma()`](/mojo/stdlib/math/math/gamma) to conform with Python's + naming. + + - The implementation of the following functions have been moved from the + `math` module to the new [`utils.numerics`](/mojo/stdlib/utils/numerics/) + module: `isfinite()`, `isinf()`, `isnan()`, `nan()`, `nextafter()`, and + `ulp()`. The functions continue to be exposed in the `math` module. + + - [`math.gcd()`](/mojo/stdlib/math/math/gcd) now works on negative inputs, and + like Python's implementation, accepts a variadic list of integers. New + overloads for a `List` or `Span`of integers are also added. + ([PR #2777](https://github.com/modularml/mojo/pull/2777)) + +- Async and coroutines: + + - [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) now requires a + lifetime parameter. This parameter is set automatically by the parser when + calling an async function. It contains the lifetimes of all the arguments + and any lifetime accesses by the arguments. This ensures that argument + captures by async functions keep the arguments alive as long as the + coroutine is alive. + + - Async function calls are no longer allowed to borrow non-trivial + register-passable types. Because async functions capture their arguments but + register-passable types don't have lifetimes (yet), Mojo is not able to + correctly track the reference, making this unsafe. To cover this safety gap, + Mojo has temporarily disallowed binding non-trivial register-passable types + to borrowed arguments in async functions. + +- Miscellaneous: + + - Added an [`InlineArray`](/mojo/stdlib/utils/static_tuple/InlineArray) type + that works on memory-only types. Compare with the existing + [`StaticTuple`](/mojo/stdlib/utils/static_tuple/StaticTuple) type, which is + conceptually an array type, but only works on `AnyTrivialRegType`. + ([PR #2294](https://github.com/modularml/mojo/pull/2294)) + + - The [`base64`](/mojo/stdlib/base64/) package now includes encoding and + decoding support for both the Base64 and Base16 encoding schemes. + ([PR #2364](https://github.com/modularml/mojo/pull/2364)) + ([PR #2584](https://github.com/modularml/mojo/pull/2584)) + + - The `take()` function in [`Variant`](/mojo/stdlib/utils/variant/Variant) and + [`Optional`](/mojo/stdlib/collections/optional/Optional) has been renamed to + `unsafe_take()`. + + - The `get()` function in `Variant` has been replaced by `__getitem__()`. That + is, `v.get[T]()` should be replaced with `v[T]`. + + - Various functions in the `algorithm` module are now built-in functions. This + includes `sort()`, `swap()`, and `partition()`. `swap()` and `partition()` + will likely shuffle around as we're reworking our built-in `sort()` function + and optimizing it. -- Added `SIMD.__repr__` to get the verbose string representation of `SIMD` types. -([PR #2728](https://github.com/modularml/mojo/pull/2728) by [@bgreni](https://github.com/bgreni)) +### Tooling changes -### 🦋 Changed +- Invoking `mojo package my-package -o my-dir` on the command line, where + `my-package` is a Mojo package source directory, and `my-dir` is an existing + directory, now outputs a Mojo package to `my-dir/my-package.mojopkg`. + Previously, this had to be spelled out, as in `-o my-dir/my-package.mojopkg`. -- `Coroutine` now requires a lifetime parameter. This parameter is set - automatically by the parser when calling an async function. It contains the - lifetimes of all the arguments and any lifetime accesses by the arguments. - This ensures that argument captures by async functions keep the arguments - alive as long as the coroutine is alive. +- The Mojo Language Server now reports a warning when a local variable is + unused. -- Async function calls are no longer allowed to borrow non-trivial - register-passable types. Because async functions capture their arguments but - register-passable types don't have lifetimes (yet), Mojo is not able to - correctly track the reference, making this unsafe. To cover this safety gap, - Mojo has temporarily disallowed binding non-trivial register-passable types - to borrowed arguments in async functions. +- Several `mojo` subcommands now support a `--diagnostic-format` option that + changes the format with which errors, warnings, and other diagnostics are + printed. By specifying `--diagnostic-format json` on the command line, errors + and other diagnostics will be output in a structured + [JSON Lines](https://jsonlines.org) format that is easier for machines to + parse. -- `AnyRegType` has been renamed to `AnyTrivialRegType` and Mojo now forbids - binding non-trivial register-passable types to `AnyTrivialRegType`. This - closes a major safety hole in the language. Please use `AnyType` for generic - code going forward. + The full list of subcommands that support `--diagnostic-format` is as follows: + `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. + Further, the `mojo test --json` option has been subsumed into this new option; + for the same behavior, run `mojo test --diagnostic-format json`. -- The `let` keyword has been completely removed from the language. We previously - removed `let` declarations but still provided an error message to users. Now, - it is completely gone from the grammar. Long live `var`! + Note that the format of the JSON output may change; we don't currently + guarantee its stability across releases of Mojo. -- Mojo will now link to a Python dynamic library based on the Python on top of - your search path: `PATH`. This enables you to activate a virtual environment - like `conda` and have access to Python modules installed in that environment - without setting `MOJO_PYTHON_LIBRARY`. Previously Mojo would find a - `libpython` dynamic library on installation and put the path in - `.modular/modular.cfg`, which could result in version conflicts if you - activated a virtual environment of a different Python version. +- A new `--validate-doc-strings` option has been added to `mojo` to emit errors + on invalid doc strings instead of warnings. -- The `abs`, `round`, `min`, `max`, `pow`, and `divmod` functions have moved - from `math` to `builtin`, so you no longer need to do - `from math import abs, round, min, max, divmod, pow`. +- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to + `--diagnose-missing-doc-strings`. - - The `is_power_of_2` function in the `math` module is now called - `is_power_of_two` and located in the `bit` module. +- A new decorator, `@doc_private`, was added that can be used to hide a + declaration from being generated in the output of `mojo doc`. It also removes + the requirement that the declaration has documentation (for example, when used + with `--diagnose-missing-doc-strings`). -- Many functions returning a pointer type have been unified to have a public - API function of `unsafe_ptr()`. +- Debugger users can now set breakpoints on function calls in O0 builds even if + the call has been inlined by the compiler. -- The `--warn-missing-doc-strings` flag for `mojo` has been renamed to - `--diagnose-missing-doc-strings`. +- The Mojo Language Server now supports renaming local variables. -- The `take` function in `Variant` and `Optional` has been renamed to - `unsafe_take`. - -- The `get` function in `Variant` has been replaced by `__refitem__`. That is, - `v.get[T]()` should be replaced with `v[T]`. - -- Various functions in the `algorithm` module are now moved to be - builtin-functions. This includes `sort`, `swap`, and `partition`. - `swap` and `partition` will likely shuffle around as we're reworking - our builtin `sort` function and optimizing it. - -- `SIMD.bool()` is constrained only for when the `size` is `1` now. Instead, - explicitly use `any()` or `all()`. - ([PR #2502](https://github.com/modularml/mojo/pull/2502) by [@helehex](https://github.com/helehex)) - -- The `SIMD.reduce_or()` and `SIMD.reduce_and()` methods are now bitwise - operations, and support integer types. - ([PR #2671](https://github.com/modularml/mojo/pull/2671) by [@helehex](https://github.com/helehex)) - -- `ListLiteral` and `Tuple` now only require that element types be `Movable`. - Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - -- Continued transition to `UnsafePointer` and unsigned byte type for strings: - - Rename `String._as_ptr()` to `String.unsafe_ptr()`, and change return type - to `UnsafePointer` (was `DTypePointer`). - - Rename `StringLiteral.data()` to `StringLiteral.unsafe_ptr()`, and change - return type to `UnsafePointer` (was `DTypePointer`). - - Added `unsafe_uint8_ptr()` methods to `String` and `StringLiteral`. - - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now - returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). - - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) - - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was - `DTypePointer[DType.int8]`). - - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has - the same behavior. - -- Added `String.isspace()` method conformant with Python's universal separators. - -- Changed `isspace(..)` to take a `UInt8` and was made private (`_isspace(..)`), - use `String.isspace()` instead. - -- `String.split()` now defaults to whitespace and has pythonic behavior in that - it removes all adjacent whitespaces by default. - -- Added `UnsafePointer.offset()` method. - -- The `math.bit` module has been moved to a new top-level `bit` module. The - following functions in this module have been renamed: - - `ctlz` -> `countl_zero` - - `cttz` -> `countr_zero` - - `bit_length` -> `bit_width` - - `ctpop` -> `pop_count` - - `bswap` -> `byte_swap` - - `bitreverse` -> `bit_reverse` - -- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been - moved to the `bit` module. - -- The `math.tgamma` function has been renamed to `math.gamma` to conform with - Python's naming. - -- The implementation of the following functions have been moved from the `math` - module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, - `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the - `math` module. - -- `InlinedString` has been renamed to `InlineString` to be consistent with other - types. - -- The `Slice.__len__` function has been removed and `Slice` no longer conforms - to the `Sized` trait. This clarifies the ambiguity of the semantics: the - length of a slice always depends on the length of the object being sliced. - Users that need the existing functionality can use the `Slice.unsafe_indices` - method. This makes it explicit that this implementation does not check if the - slice bounds are concrete or within any given object's length. - -- Implicit conversion to `String` is now removed for builtin classes/types. - One should use `str(...)` explicitly to convert to `String`. - -- `math.gcd` now works on negative inputs, and like Python's implementation, - accepts a variadic list of integers. New overloads for a `List` or `Span`of - integers are also added. - ([PR #2777](https://github.com/modularml/mojo/pull/2777) by [@bgreni](https://github.com/bgreni)) +### Other changes -### ❌ Removed +#### ❌ Removed - The `@unroll` decorator has been deprecated and removed. The decorator was supposed to guarantee that a decorated loop would be unrolled, or else the @@ -631,62 +733,62 @@ modular update mojo parameter values, limiting its usefulness. Please see `@parameter for` for a replacement! -- The method `object.print()` has been removed. Since now, `object` has the - `Stringable` trait, you can use `print(my_object)` instead. +- The method `object.print()` has been removed. Since `object` now conforms to + the `Stringable` trait, you can use `print(my_object)` instead. - The following functions have been removed from the math module: - - `clamp`; use the new `SIMD.clamp` method instead. - - `round_half_down` and `round_half_up`; these can be trivially implemented - using the `ceil` and `floor` functions. - - `add`, `sub`, `mul`, `div`, `mod`, `greater`, `greater_equal`, `less`, - `less_equal`, `equal`, `not_equal`, `logical_and`, `logical_xor`, and - `logical_not`; Instead, users should rely directly on the `+`, `-`, `*`, - `/`, `%`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `^`, and `~` operators, - respectively. - - `identity` and `reciprocal`; users can implement these trivially. - - `select`; in favor of using `SIMD.select` directly. - - `is_even` and `is_odd`; these can be trivially implemented using bitwise `&` - with `1`. - - `roundeven`; the new `SIMD.roundeven` method now provides the identical + - `clamp()`; use the new `SIMD.clamp()` method instead. + - `round_half_down()` and `round_half_up()`; these can be trivially implemented + using the `ceil()` and `floor()` functions. + - `add()`, `sub()`, `mul()`, `div()`, `mod()`, `greater()`, `greater_equal()`, + `less()`, `less_equal()`, `equal()`, `not_equal()`, `logical_and()`, + `logical_xor()`, and `logical_not()`; Instead, users should rely directly on + the corresponding operators (`+`, `-`, `*`, `/`, `%`, `>`, `>=`, `<`, `<=`, + `==`, `!=`, `&`, `^`, and `~`). + - `identity()` and `reciprocal()`; users can implement these trivially. + - `select()`; removed in favor of using `SIMD.select()` directly. + - `is_even()` and `is_odd()`; these can be trivially implemented using bitwise + `&` with `1`. + - `roundeven()`; the new `SIMD.roundeven()` method now provides the identical functionality. - - `div_ceil`; use the new `ceildiv` function. - - `rotate_left` and `rotate_right`; the same functionality is available in the - builtin `SIMD.rotate_{left,right}` methods for `SIMD` types, and the - `bit.rotate_bits_{left,right}` methods for `Int`. - - an overload of `math.pow` taking an integer parameter exponent. - - `align_down_residual`; it can be trivially implemented using `align_down`. - - `all_true`, `any_true`, and `none_true`; use `SIMD.reduce_and` and - `SIMD.reduce_or` directly. - - `reduce_bit_count`; use the new `SIMD.reduce_bit_count` directly. - - `rint` and `nearbyint`; use `round` or `SIMD.roundeven` as appropriate. + - `div_ceil()`; use the new `ceildiv()` function. + - `rotate_left()` and `rotate_right()`; the same functionality is available in + the builtin `SIMD.rotate_{left,right}()` methods for `SIMD` types, and the + `bit.rotate_bits_{left,right})()` methods for `Int`. + - An overload of `math.pow()` taking an integer parameter exponent. + - `align_down_residual()`; it can be trivially implemented using + `align_down()`. + - `all_true()`, `any_true()`, and `none_true()`; use `SIMD.reduce_and()` and + `SIMD.reduce_or()` directly. + - `reduce_bit_count()`; use the new `SIMD.reduce_bit_count()` directly. + - `rint()` and `nearbyint()`; use `round()` or `SIMD.roundeven()` as + appropriate. - The `EvaluationMethod` has been removed from `math.polynomial` and Estrin's method is no longer available. This method was limited to degree 10 or less, underutilized, and its performance unclear. In the future, this might be reintroduced with an improved implementation if needed, when better performance benchmarking infrastructure is available. The default behavior of - `math.polynomial.polynomial_evaluate` is unchanged (Horner's method). + `math.polynomial.polynomial_evaluate()` is unchanged (Horner's method). -- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The - same functionality is available in the builtin `SIMD.select` and - `SIMD.__and__` methods, respectively. +- The `math.bit.select()` and `math.bit.bit_and()` functions have been removed. + The same functionality is available in the builtin `SIMD.select` and + `SIMD.__and__()` methods, respectively. - The `math.limit` module has been removed. The same functionality is available as follows: - - `math.limit.inf`: use `utils.numerics.max_or_inf` - - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` - - `math.limit.max_finite`: use `utils.numerics.max_finite` - - `math.limit.min_finite`: use `utils.numerics.min_finite` + - `math.limit.inf()`: use `utils.numerics.max_or_inf()` + - `math.limit.neginf()`: use `utils.numerics.min_or_neg_inf()` + - `math.limit.max_finite()`: use `utils.numerics.max_finite()` + - `math.limit.min_finite()`: use `utils.numerics.min_finite()` - The `tensor.random` module has been removed. The same functionality is now - accessible via the `Tensor.rand` and `Tensor.randn` static methods. + accessible via the `Tensor.rand()` and `Tensor.randn()` static methods. - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. -- The pointer types no longer have the ability to perform a non-temporal store. - -### 🛠️ Fixed +#### 🛠️ Fixed - [#1837](https://github.com/modularml/mojo/issues/1837) Fix self-referential variant crashing the compiler. @@ -700,6 +802,26 @@ modular update mojo - [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` to include calling location. +### Special thanks + +Special thanks to our community contributors: + +[@rd4com](https://github.com/rd4com), +[@toiletsandpaper](https://github.com/toiletsandpaper), +[@helehex](https://github.com/helehex), [@rd4com](https://github.com/rd4com/), +[@artemiogr97](https://github.com/artemiogr97), +[@mikowals](https://github.com/mikowals), +[@kernhanda](https://github.com/kernhanda), [@lsh](https://github.com/lsh), +[@LJ-9801](https://github.com/LJ-9801), +[@YichengDWu](https://github.com/YichengDWu), +[@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), +[@fknfilewalker](https://github.com/fknfilewalker), +[@jayzhan211](https://github.com/jayzhan211), +[@martinvuyk](https://github.com/martinvuyk), +[@ChristopherLR](https://github.com/ChristopherLR), +[@mzaks](https://github.com/mzaks), [@bgreni](https://github.com/bgreni), +[@Brian-M-J](https://github.com/Brian-M-J) + ## v24.3 (2024-05-02) ### ✨ Highlights @@ -834,7 +956,7 @@ modular update mojo This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that `__call_location()` only works in `@always_inline` or `@always_inline("nodebug")` functions. It gives incorrect results if placed in - an `@always_inline` function that's called *from* an + an `@always_inline` function that's called _from_ an `@always_inline("nodebug")` function. This feature is still evolving and for the time being you need to explicitly @@ -2270,7 +2392,7 @@ experience without dedicated sugar. specifying `-D MOJO_ENABLE_ASSERTIONS` when invoking `mojo` to compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace - before the program exits. By default, assertions are *not enabled* + before the program exits. By default, assertions are _not enabled_ in the standard library right now for performance reasons. - The Mojo Language Server now implements the References request. IDEs use @@ -2380,7 +2502,7 @@ experience without dedicated sugar. array slices. While this is a major step forward for the lifetimes system in Mojo, it is - still *very* early and awkward to use. Notably, there is no syntactic sugar + still _very_ early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now. @@ -2703,8 +2825,8 @@ experience without dedicated sugar. - Traits have arrived! - You can now define a *trait*, which consists of a required set of method - prototypes. A struct can *conform to* the trait by implementing these methods. + You can now define a _trait_, which consists of a required set of method + prototypes. A struct can _conform to_ the trait by implementing these methods. This lets you write generic functions that work on any structs that conform to a given trait. @@ -2824,7 +2946,7 @@ experience without dedicated sugar. ### ⭐️ New - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. - It doesn't include *everything* about Mojo yet, but it includes a lot, + It doesn't include _everything_ about Mojo yet, but it includes a lot, and more than the original [programming manual](/mojo/programming-manual.html) (now deprecated). @@ -2847,7 +2969,7 @@ experience without dedicated sugar. ``` In the first signature for `eat()`, the `b` parameter isn't bound, so it's - *implicitly* added as an input parameter on the function. + _implicitly_ added as an input parameter on the function. In the second signature for `eat()`, the author has explicitly defined an input parameter (`_b`), which is bound to the second parameter on the argument @@ -3092,8 +3214,8 @@ the previous "read to EOF" behavior when size is negative. - There is an issue affecting Jupyter notebooks that use autotuning and traits. This issue only manifests on macOS, and the same code runs without issue - outside of the notebooks. This issue affects the *Matrix multiplication in - Mojo* notebook. + outside of the notebooks. This issue affects the _Matrix multiplication in + Mojo_ notebook. ## v0.5.0 (2023-11-2) @@ -3108,7 +3230,7 @@ the previous "read to EOF" behavior when size is negative. function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. -- Mojo now supports compile-time *keyword parameters*, in addition to existing +- Mojo now supports compile-time _keyword parameters_, in addition to existing support for [keyword arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For example: @@ -3447,7 +3569,7 @@ the previous "read to EOF" behavior when size is negative. return self^ ``` - Here Mojo *cannot* invoke a noop `__exit__` method because the context + Here Mojo _cannot_ invoke a noop `__exit__` method because the context manager is consumed by the `__enter__` method. This can be used for types (like file descriptors) that are traditionally used with `with` statements, even though Mojo's guaranteed early destruction doesn't require that. @@ -4714,7 +4836,7 @@ busy this week. is currently unsupported). These should be generally reliable for both memory-only and register-passable - types, with the caveat that closures are known to *not* capture values + types, with the caveat that closures are known to _not_ capture values correctly. Be very careful with interesting types in the vicinity of a closure! From a716aead75b073488629a4c9ac2758f90c0ef7c7 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 5 Jun 2024 21:16:04 -0500 Subject: [PATCH 0909/2019] [stdlib] cleanup: Change StringRef C str initializers to take `UnsafePointer[C_char]` * Change StringRef constructors taking `DTypePointer[int8]` to take `UnsafePointer[C_char]`. * Change _ImmutableString.data to `UnsafePointer[UInt8]` (was an `Int8`) * Change _dirent_{linux, macos}.name field InlineArray element type to `C_char` (was `Int8`) MODULAR_ORIG_COMMIT_REV_ID: 29f93192984c51f4fb9f19e798d5248f559552c6 --- docs/changelog.md | 4 ++++ stdlib/src/builtin/object.mojo | 21 ++++++++++----------- stdlib/src/os/os.mojo | 11 ++++++----- stdlib/src/pathlib/path.mojo | 10 +++++++--- stdlib/src/python/_cpython.mojo | 6 +++--- stdlib/src/utils/stringref.mojo | 15 ++++++--------- 6 files changed, 36 insertions(+), 31 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b5f1010503..269ed3e310 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -112,6 +112,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) +- The `StringRef` constructors from `DTypePointer.int8` have been changed to + take a `UnsafePointer[C_char]`, reflecting their use for compatibility with + C APIs. + - The global functions for working with `UnsafePointer` have transitioned to being methods through the use of conditional conformances: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index e932062fdd..0e473831ef 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -43,15 +43,15 @@ struct _ImmutableString: pointer and integer pair. Memory will be dynamically allocated. """ - var data: UnsafePointer[Int8] + var data: UnsafePointer[UInt8] """The pointer to the beginning of the string contents. It is not null-terminated.""" var length: Int """The length of the string.""" @always_inline - fn __init__(inout self, data: UnsafePointer[Int8], length: Int): - self.data = data.address + fn __init__(inout self, data: UnsafePointer[UInt8], length: Int): + self.data = data self.length = length @always_inline @@ -331,7 +331,7 @@ struct _ObjectImpl(CollectionElement, Stringable): if self.is_str(): var str = self.get_as_string() var impl = _ImmutableString( - UnsafePointer[Int8].alloc(str.length), str.length + UnsafePointer[UInt8].alloc(str.length), str.length ) memcpy( dest=impl.data, @@ -743,13 +743,12 @@ struct object(IntableRaising, Boolable, Stringable): value: The string value. """ var impl = _ImmutableString( - UnsafePointer[Int8].alloc(value.length), value.length + UnsafePointer[UInt8].alloc(value.length), value.length ) memcpy( - impl.data, - # TODO: Remove bitcast once transition to UInt8 strings is complete. - value.unsafe_ptr().bitcast[Int8](), - value.length, + dest=impl.data, + src=value.unsafe_ptr(), + count=value.length, ) self._value = impl @@ -1239,7 +1238,7 @@ struct object(IntableRaising, Boolable, Stringable): var rhsStr = rhs._value.get_as_string() var length = lhsStr.length + rhsStr.length var impl = _ImmutableString( - UnsafePointer[Int8].alloc(length), length + UnsafePointer[UInt8].alloc(length), length ) memcpy(impl.data, lhsStr.data, lhsStr.length) memcpy(impl.data + lhsStr.length, rhsStr.data, rhsStr.length) @@ -1733,7 +1732,7 @@ struct object(IntableRaising, Boolable, Stringable): raise Error("TypeError: can only index into lists and strings") var index = Self._convert_index_to_int(i) if self._value.is_str(): - var impl = _ImmutableString(UnsafePointer[Int8].alloc(1), 1) + var impl = _ImmutableString(UnsafePointer[UInt8].alloc(1), 1) impl.data.init_pointee_copy( (self._value.get_as_string().data + index).take_pointee(), ) diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index b9fe3ee544..eb59f5e737 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -21,6 +21,7 @@ from os import listdir from collections import List from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda +from sys.ffi import C_char from memory import ( DTypePointer, @@ -64,7 +65,7 @@ struct _dirent_linux: """Length of the record.""" var d_type: Int8 """Type of file.""" - var name: InlineArray[Int8, Self.MAX_NAME_SIZE] + var name: InlineArray[C_char, Self.MAX_NAME_SIZE] """Name of entry.""" @@ -81,11 +82,11 @@ struct _dirent_macos: """Length of the name.""" var d_type: Int8 """Type of file.""" - var name: InlineArray[Int8, Self.MAX_NAME_SIZE] + var name: InlineArray[C_char, Self.MAX_NAME_SIZE] """Name of entry.""" -fn _strnlen(ptr: UnsafePointer[Int8], max: Int) -> Int: +fn _strnlen(ptr: UnsafePointer[C_char], max: Int) -> Int: var offset = 0 while offset < max and ptr[offset]: offset += 1 @@ -149,7 +150,7 @@ struct _DirHandle: if not ep: break var name = ep.take_pointee().name - var name_ptr = UnsafePointer.address_of(name).bitcast[Int8]() + var name_ptr = name.unsafe_ptr() var name_str = StringRef( name_ptr, _strnlen(name_ptr, _dirent_linux.MAX_NAME_SIZE) ) @@ -174,7 +175,7 @@ struct _DirHandle: if not ep: break var name = ep.take_pointee().name - var name_ptr = UnsafePointer.address_of(name).bitcast[Int8]() + var name_ptr = name.unsafe_ptr() var name_str = StringRef( name_ptr, _strnlen(name_ptr, _dirent_macos.MAX_NAME_SIZE) ) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 2ae4819fa5..dcd512eca8 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -22,6 +22,8 @@ from memory import stack_allocation from utils import StringRef +from sys.ffi import C_char + alias DIR_SEPARATOR = "\\" if os_is_windows() else "/" @@ -32,14 +34,16 @@ fn cwd() raises -> Path: The current directory. """ alias MAX_CWD_BUFFER_SIZE = 1024 - var buf = stack_allocation[MAX_CWD_BUFFER_SIZE, DType.int8]() + var buf0 = stack_allocation[MAX_CWD_BUFFER_SIZE, C_char.type]() + + var buf = UnsafePointer[C_char]._from_dtype_ptr(buf0) - var res = external_call["getcwd", DTypePointer[DType.int8]]( + var res = external_call["getcwd", UnsafePointer[C_char]]( buf, MAX_CWD_BUFFER_SIZE ) # If we get a nullptr, then we raise an error. - if res == DTypePointer[DType.int8](): + if res == UnsafePointer[C_char](): raise Error("unable to query the current directory") return String(StringRef(buf)) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 7289868c99..f122f9c14c 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -90,7 +90,7 @@ struct PythonVersion: fn _py_get_version(lib: DLHandle) -> StringRef: - var version_string = lib.get_function[fn () -> DTypePointer[DType.int8]]( + var version_string = lib.get_function[fn () -> UnsafePointer[C_char]]( "Py_GetVersion" )() return StringRef(version_string) @@ -134,7 +134,7 @@ struct CPython: # and make this initialization a raising function. self.init_error = external_call[ "KGEN_CompilerRT_Python_SetPythonPath", - DTypePointer[DType.int8], + UnsafePointer[C_char], ]() var python_lib = getenv("MOJO_PYTHON_LIBRARY") @@ -680,7 +680,7 @@ struct CPython: fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: var result = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[Int]) -> DTypePointer[DType.int8] + fn (PyObjectPtr, UnsafePointer[Int]) -> UnsafePointer[C_char] ]("PyUnicode_AsUTF8AndSize")(py_object, UnsafePointer[Int]()) return StringRef(result) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index f38a038815..3584ea2d2a 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -82,10 +82,8 @@ struct StringRef( """ return StringRef(str.unsafe_ptr(), len(str)) - # TODO: #2317 Drop support for this constructor when we have fully - # transitioned to UInt8 as the main byte type. @always_inline - fn __init__(ptr: DTypePointer[DType.int8], len: Int) -> Self: + fn __init__(ptr: UnsafePointer[C_char], len: Int) -> Self: """Construct a StringRef value given a (potentially non-0 terminated string). @@ -102,9 +100,8 @@ struct StringRef( Returns: Constructed `StringRef` object. """ - var unsafe_ptr = UnsafePointer[Int8]._from_dtype_ptr(ptr) - return Self {data: unsafe_ptr.bitcast[UInt8](), length: len} + return Self {data: ptr.bitcast[UInt8](), length: len} @always_inline fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> Self: @@ -137,10 +134,8 @@ struct StringRef( return DTypePointer[DType.uint8](ptr) - # TODO: #2317 Drop support for this constructor when we have fully - # transitioned to UInt8 as the main byte type. @always_inline - fn __init__(ptr: DTypePointer[DType.int8]) -> Self: + fn __init__(ptr: UnsafePointer[C_char]) -> Self: """Construct a StringRef value given a null-terminated string. Note that you should use the constructor from `DTypePointer[DType.uint8]` instead @@ -175,7 +170,9 @@ struct StringRef( while Scalar.load(ptr, len): len += 1 - return StringRef(ptr.bitcast[DType.int8](), len) + var ptr1 = UnsafePointer[C_char]._from_dtype_ptr(ptr) + + return StringRef(ptr1, len) # ===-------------------------------------------------------------------===# # Helper methods for slicing From d083f7666eb492b014cebec3c4ec490385791ef1 Mon Sep 17 00:00:00 2001 From: KenJones-Modular <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 5 Jun 2024 19:42:45 -0700 Subject: [PATCH 0910/2019] [docs] New documentation for the Mojo testing framework (#41339) MODULAR_ORIG_COMMIT_REV_ID: ee9b0b841f2e6c85dbc03aad39a37379473a7e71 --- docs/tools/testing.ipynb | 750 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 726 insertions(+), 24 deletions(-) diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb index 5a4b78043f..2db4aad051 100644 --- a/docs/tools/testing.ipynb +++ b/docs/tools/testing.ipynb @@ -15,56 +15,758 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The Mojo CLI has a built-in test runner that can be used to test Mojo programs. \n", - "The `mojo test` command recursively searches the `test` directory for any files\n", - "with `test` in the filename. For example, given the following directory\n", - "structure:\n", + "Mojo includes a framework for developing and executing unit tests. The framework\n", + "also supports testing code examples in the\n", + "[documentation strings](/mojo/manual/basics#code-comments)\n", + "(also known as *docstrings*) of your API references. The Mojo testing framework\n", + "consists of a set of assertions defined as part of the\n", + "[Mojo standard library](/mojo/lib) and the\n", + "[`mojo test`](/mojo/cli/test) command line tool.\n", + "\n", + "## Get started\n", + "\n", + "Let’s start with a simple example of writing and running Mojo tests.\n", + "\n", + "### 1. Write tests\n", + "\n", + "For your first example of using the Mojo testing framework, create a file named\n", + "`test_quickstart.mojo` containing the following code:\n", + "\n", + "```mojo\n", + "# Content of test_quickstart.mojo\n", + "from testing import assert_equal\n", + "\n", + "def inc(n: Int) -> Int:\n", + " return n + 1\n", + "\n", + "def test_inc_zero():\n", + " # This test contains an intentional logical error to show an example of\n", + " # what a test failure looks like at runtime.\n", + " assert_equal(inc(0), 0)\n", + "\n", + "def test_inc_one():\n", + " assert_equal(inc(1), 2)\n", + "```\n", + "\n", + "In this file, the `inc()` function is the test *target*. The functions whose\n", + "names begin with `test_` are the tests. Usually the target should be in a\n", + "separate source file from its tests, but you can define them in the same file\n", + "for this simple example.\n", + "\n", + "A test function *fails* if it raises an error when executed, otherwise it\n", + "*passes*. The two tests in this example use the `assert_equal()` function,\n", + "which raises an error if the two values provided are not equal.\n", + "\n", + ":::note\n", + "\n", + "The implementation of `test_inc_zero()` contains an intentional logical error\n", + "so that you can see an example of a failed test when you execute it in the\n", + "next step of this tutorial. \n", + "\n", + ":::\n", + "\n", + "### 2. Execute tests\n", + "\n", + "Then in the directory containing the file, execute the following command in your\n", + "shell:\n", "\n", "```bash\n", - "test/\n", - " test_users_controller.mojo\n", - " factories.mojo\n", - " models/\n", - " user_model_tests.mojo\n", + "mojo test test_quickstart.mojo\n", + "```\n", + "\n", + "You should see output similar to this (note that this example elides the full\n", + "filesystem paths from the output shown):\n", + "\n", "```\n", + "Testing Time: 1.193s\n", + "\n", + "Total Discovered Tests: 2\n", + "\n", + "Passed : 1 (50.00%)\n", + "Failed : 1 (50.00%)\n", + "Skipped: 0 (0.00%)\n", + "\n", + "******************** Failure: 'ROOT_DIR/test_quickstart.mojo::test_inc_zero()' ********************\n", "\n", - "The `mojo` CLI will search `test_users_controller.mojo` and \n", - "`user_model_tests.mojo` for tests, but not `factories.mojo`. \n", + "Unhandled exception caught during execution\n", + "\n", + "Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\n", + " left: 1\n", + " right: 0\n", + "\n", + "********************\n", + "```\n", "\n", - "## Test functions\n", + "The output starts with a summary of the number of tests discovered, passed,\n", + "failed, and skipped. Following that, each failed test is reported along with\n", + "its error message.\n", "\n", - "Test functions must be named `test_*` and take no arguments. Use `def` notation\n", - "rather than `fn` notation. By definition, every test function should be capable\n", - "of raising an error. So:" + "### Next steps\n", + "\n", + "- [The `testing` module](#the-testing-module) describes the assertion\n", + "functions available to help implement tests.\n", + "- [Writing unit tests](#writing-unit-tests) shows how to write unit tests and\n", + "organize them into test files.\n", + "- [The `mojo test` command](#the-mojo-test-command) describes how to execute\n", + "and collect lists of tests.\n", + "- [Writing API documentation tests](#writing-api-documentation-tests)\n", + "discusses how to use the Mojo testing framework to test code examples in your\n", + "API documentation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## The `testing` module\n", + "\n", + "The Mojo standard library includes a [`testing`](/mojo/stdlib/testing/testing/)\n", + "module that defines several assertion functions for implementing tests. Each\n", + "assertion returns `None` if its condition is met or raises an error if it isn’t.\n", + "\n", + "- [`assert_true()`](/mojo/stdlib/testing/testing/assert_true):\n", + "Asserts that the input value is `True`.\n", + "- [`assert_false()`](/mojo/stdlib/testing/testing/assert_false):\n", + "Asserts that the input value is `False`.\n", + "- [`assert_equal()`](/mojo/stdlib/testing/testing/assert_equal):\n", + "Asserts that the input values are equal.\n", + "- [`assert_not_equal()`](/mojo/stdlib/testing/testing/assert_not_equal):\n", + "Asserts that the input values are not equal.\n", + "- [`assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal):\n", + "Asserts that the input values are equal up to a tolerance.\n", + "\n", + "The boolean assertions report a basic error message when they fail." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: At Expression [1] wrapper:14:16: AssertionError: condition was unexpectedly False\n" + ] + } + ], "source": [ - "from testing import assert_equal\n", + "from testing import *\n", + "assert_true(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each function also accepts an optional `msg` keyword argument for providing a\n", + "custom message to include if the assertion fails." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: At Expression [2] wrapper:14:16: AssertionError: paradoxes are not allowed\n" + ] + } + ], + "source": [ + "assert_true(False, msg=\"paradoxes are not allowed\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For comparing floating point values you should use `assert_almost_equal()`,\n", + "which allows you to specify either an absolute or relative tolerance." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: At Expression [3] wrapper:15:24: AssertionError: 3.3333333333333335 is not close to 3.3300000000000001 with a diff of 0.0033333333333334103 (close but no cigar)\n" + ] + } + ], + "source": [ + "result = 10 / 3\n", + "assert_almost_equal(result, 3.33, atol=0.001, msg=\"close but no cigar\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The testing module also defines a context manager,\n", + "[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises),\n", + "to assert that a given code block correctly raises an expected error." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test passes because the error is raised\n", + "Test fails because the error isn't raised\n", + "Error: AssertionError: Didn't raise at Expression [4] wrapper:18:23\n" + ] + } + ], + "source": [ + "def inc(n: Int) -> Int:\n", + " if n == Int.MAX:\n", + " raise Error(\"inc overflow\")\n", + " return n + 1\n", + "\n", + "print(\"Test passes because the error is raised\")\n", + "with assert_raises():\n", + " _ = inc(Int.MAX)\n", + "\n", + "print(\"Test fails because the error isn't raised\")\n", + "with assert_raises():\n", + " _ = inc(Int.MIN)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note\n", + "\n", + "The example above assigns the return value from `inc()` to a\n", + "[*discard pattern*](/mojo/manual/lifecycle/death#explicit-lifetimes).\n", + "Without it, the Mojo compiler detects that the return value is unused and\n", + "optimizes the code to eliminate the function call.\n", + "\n", + ":::\n", + "\n", + "You can also provide an optional `contains` argument to `assert_raises()` to\n", + "indicate that the test passes only if the error message contains the substring\n", + "specified. Other errors are propagated, failing the test." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unhandled exception caught during execution" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test passes because the error contains the substring\n", + "Test fails because the error doesnt contain the substring\n", + "Error: invalid value\n" + ] + } + ], + "source": [ + "print(\"Test passes because the error contains the substring\")\n", + "with assert_raises(contains=\"required\"):\n", + " raise Error(\"missing required argument\")\n", + "\n", + "print(\"Test fails because the error doesnt contain the substring\")\n", + "with assert_raises(contains=\"required\"):\n", + " raise Error(\"invalid value\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Writing unit tests\n", + "\n", + "A Mojo unit test is simply a function that fulfills all of these requirements:\n", + "\n", + "- Has a name that starts with `test_`.\n", + "- Accepts no arguments.\n", + "- Returns either `None` or a value of type `object`.\n", + "- Raises an error to indicate test failure.\n", + "- Is defined at the module scope, not as a Mojo struct method.\n", + "\n", + "You can use either `def` or `fn` to define a test function. Because a test\n", + "function always raises an error to indicate failure, any test function defined\n", + "using `fn` must include the `raises` declaration.\n", + "\n", + "Generally, you should use the assertion utilities from the Mojo standard library\n", + "[`testing`](/mojo/stdlib/testing/testing/) module to implement your tests.\n", + "You can include multiple related assertions in the same test function. However,\n", + "if an assertion raises an error during execution then the test function returns\n", + "immediately, skipping any subsequent assertions.\n", + "\n", + "You must define your Mojo unit tests in Mojo source files named with a `test`\n", + "prefix or suffix. You can organize your test files within a directory hierarchy,\n", + "but the test files must not be part of a Mojo package (that is, the test\n", + "directories should not contain `__init__.mojo` files).\n", + "\n", + "Here is an example of a test file containing three tests for functions defined\n", + "in a source module named `my_target_module` (which is not shown here).\n", + "\n", + "```mojo\n", + "# File: test_my_target_module.mojo\n", + "\n", + "from my_target_module import convert_input, validate_input\n", + "from testing import assert_equal, assert_false, assert_raises, assert_true\n", "\n", + "def test_validate_input():\n", + "\tassert_true(validate_input(\"good\"), msg=\"'good' should be valid input\")\n", + "\tassert_false(validate_input(\"bad\"), msg=\"'bad' should be invalid input\")\n", "\n", - "def test_foo():\n", - " assert_equal(1, 1)\n" + "def test_convert_input():\n", + "\tassert_equal(convert_input(\"input1\"), \"output1\")\n", + "\tassert_equal(convert_input(\"input2\"), \"output2\")\n", + "\n", + "def test_convert_input_error():\n", + "\twith assert_raises():\n", + "\t\t_ = convert_input(\"garbage\")\n", + "```\n", + "\n", + "The unique identity of a unit test consists of the path of the test file and the\n", + "name of the test function, separated by `::`. So the test IDs from the example\n", + "above are:\n", + "\n", + "- `test_my_target_module.mojo::test_validate_input()`\n", + "- `test_my_target_module.mojo::test_convert_input()`\n", + "- `test_my_target_module.mojo::test_convert_error()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "## The `mojo test` command\n", + "\n", + "The `mojo` command line interface includes the [`mojo test`](/mojo/cli/test)\n", + "command for running tests or collecting a list of tests.\n", + "\n", + "### Running tests\n", + "\n", + "By default, the `mojo test` command runs the tests that you specify using one of\n", + "the following:\n", + "\n", + "- A single test ID with either an absolute or relative file path, to run only\n", + "that test.\n", + "- A single absolute or relative file path, to run all tests in that file.\n", + "- A single absolute or relative directory path, to recurse through that\n", + "directory hierarchy and run all tests found.\n", + "\n", + "If needed, you can optionally use the `-I` option one or more times to append\n", + "additional paths to the list of directories searched to import Mojo modules and\n", + "packages. For example, consider a project with the following directory\n", + "structure:\n", + "\n", + "```\n", + ".\n", + "├── src\n", + "│   ├── example.mojo\n", + "│   └── my_math\n", + "│   ├── __init__.mojo\n", + "│   └── utils.mojo\n", + "└── test\n", + " └── my_math\n", + " ├── test_dec.mojo\n", + " └── test_inc.mojo\n", + "```\n", + "\n", + "From the project root directory, you could execute all of the tests in the\n", + "`test` directory like this:\n", + "\n", + "```\n", + "$ mojo test -I src test\n", + "Testing Time: 3.433s\n", + "\n", + "Total Discovered Tests: 4\n", + "\n", + "Passed : 4 (100.00%)\n", + "Failed : 0 (0.00%)\n", + "Skipped: 0 (0.00%)\n", + "```\n", + "\n", + "You could run the tests contained in only the `test_dec.mojo` file like this:\n", + "\n", + "```\n", + "$ mojo test -I src test/my_math/test_dec.mojo\n", + "Testing Time: 1.175s\n", + "\n", + "Total Discovered Tests: 2\n", + "\n", + "Passed : 2 (100.00%)\n", + "Failed : 0 (0.00%)\n", + "Skipped: 0 (0.00%)\n", + "```\n", + "\n", + "And you could run a single test from a file by providing its fully qualified\n", + "ID like this:\n", + "\n", + "```\n", + "$ mojo test -I src 'test/my_math/test_dec.mojo::test_dec_valid()'\n", + "Testing Time: 0.66s\n", + "\n", + "Total Discovered Tests: 1\n", + "\n", + "Passed : 1 (100.00%)\n", + "Failed : 0 (0.00%)\n", + "Skipped: 0 (0.00%)\n", + "```\n", + "\n", + "### Collecting a list of tests\n", + "\n", + "By including the `--collect-only` or `--co` option, you can use `mojo test` to\n", + "discover and print a list of tests. \n", + "\n", + "As an example, consider the project structure shown in the\n", + "[Running tests](#running-tests) section. The following command produces a list\n", + "of all of the tests defined in the `test` directory hierarchy.\n", + "\n", + "```bash\n", + "mojo test --co test\n", + "```\n", + "\n", + "The output shows the hierarchy of directories, test files, and individual tests\n", + "(note that this example elides the full filesystem paths from the output shown):\n", + "\n", + "```\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "```\n", + "\n", + "### Producing JSON formatted output\n", + "\n", + "By default `mojo test` produces concise, human-readable output. Alternatively\n", + "you can produce JSON formatted output more suitable for input to other tools by\n", + "including the `--diagnostic-format json` option.\n", + "\n", + "For example, you could run the tests in the `test_quickstart.mojo` file shown\n", + "in the [Get started](#get-started) section with JSON formatted output using this\n", + "command:\n", + "\n", + "```bash\n", + "mojo test --diagnostic-format json test_quickstart.mojo\n", + "```\n", + "\n", + "The output shows the detailed results for each individual test and summary\n", + "results (note that this example elides the full filesystem paths from the\n", + "output shown):\n", + "\n", + "```\n", + "{\n", + " \"children\": [\n", + " {\n", + " \"duration_ms\": 60,\n", + " \"error\": \"Unhandled exception caught during execution\",\n", + " \"kind\": \"executionError\",\n", + " \"stdErr\": \"\",\n", + " \"stdOut\": \"Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\\r\\n left: 1\\r\\n right: 0\\r\\n\",\n", + " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_zero()\"\n", + " },\n", + " {\n", + " \"duration_ms\": 51,\n", + " \"error\": \"\",\n", + " \"kind\": \"success\",\n", + " \"stdErr\": \"\",\n", + " \"stdOut\": \"\",\n", + " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_one()\"\n", + " }\n", + " ],\n", + " \"duration_ms\": 1171,\n", + " \"error\": \"\",\n", + " \"kind\": \"executionError\",\n", + " \"stdErr\": \"\",\n", + " \"stdOut\": \"\",\n", + " \"testID\": \"ROOT_DIR/test_quickstart.mojo\"\n", + "}\n", + "```\n", + "\n", + "You can also produce JSON output for test collection as well. As an example,\n", + "consider the project structure shown in the [Running tests](#running-tests)\n", + "section. The following command collects a list in JSON format of all of the\n", + "tests defined in the `test` directory hierarchy:\n", + "\n", + "```bash\n", + "mojo test --diagnostic-format json --co test\n", + "```\n", "\n", - "Not:\n", + "The output would appear as follows (note that this example elides the full\n", + "filesystem paths from the output shown):\n", + "\n", + "```\n", + "{\n", + " \"children\": [\n", + " {\n", + " \"children\": [\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_valid()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 5,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 5\n", + " }\n", + " },\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_min()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 9,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 9\n", + " }\n", + " }\n", + " ],\n", + " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo\"\n", + " },\n", + " {\n", + " \"children\": [\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_valid()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 5,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 5\n", + " }\n", + " },\n", + " {\n", + " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_max()\",\n", + " \"location\": {\n", + " \"endColumn\": 5,\n", + " \"endLine\": 9,\n", + " \"startColumn\": 5,\n", + " \"startLine\": 9\n", + " }\n", + " }\n", + " ],\n", + " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo\"\n", + " }\n", + " ],\n", + " \"id\": \"ROOT_DIR/test/my_math\"\n", + "}\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Writing API documentation tests\n", + "\n", + "The Mojo testing framework also supports testing code examples that you include\n", + "in [docstrings](/mojo/manual/basics#code-comments). This helps to ensure that\n", + "the code examples in your API documentation are correct and up to date.\n", + "\n", + "### Identifying executable code\n", + "\n", + "The Mojo testing framework requires you to explicitly identify the code blocks\n", + "that you want it to execute.\n", + "\n", + "In a Mojo docstring, a fenced code block delimited by standard triple-backquotes\n", + "is a *display-only* code block. It appears in the API documentation, but\n", + "`mojo test` does not identify it as a test or attempt to execute any of the code\n", + "in the block.\n", + "\n", + "~~~\n", + "\"\"\" Non-executable code block example.\n", + "\n", + "The generated API documentation includes all lines of the following code block,\n", + "but `mojo test` does not execute any of the code in it.\n", + "\n", + "```\n", + "# mojo test does NOT execute any of this code block\n", + "a = 1\n", + "print(a)\n", + "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + "In contrast, a fenced code block that starts with the line ```mojo\n", + "not only appears in the API documentation, but `mojo test` treats it as an\n", + "executable test. The test fails if the code raises any error, otherwise it\n", + "passes.\n", + "\n", + "~~~\n", + "\"\"\" Executable code block example.\n", + "\n", + "The generated API documentation includes all lines of the following code block\n", + "*and* `mojo test` executes it as a test.\n", + "\n", + "```mojo\n", + "from testing import assert_equals\n", + "\n", + "b = 2\n", + "assert_equals(b, 2)\n", + "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + "Sometimes you might want to execute a line of code as part of the test but *not*\n", + "display that line in the API documentation. To achieve this, prefix the line of\n", + "code with `%#`. For example, you could use this technique to omit `import`\n", + "statements and assertion functions from the documentation.\n", + "\n", + "~~~\n", + "\"\"\" Executable code block example with some code lines omitted from output.\n", + "\n", + "The generated API documentation includes only the lines of code that do *not*\n", + "start with `%#`. However, `mojo test` executes *all* lines of code.\n", "\n", "```mojo\n", - "fn test_foo() raises:\n", - " assert_equal(1, 1)\n", + "%# from testing import assert_equal\n", + "c = 3\n", + "print(c)\n", + "%# assert_equal(c, 3)\n", "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + "### Documentation test suites and scoping\n", + "\n", + "The Mojo testing framework treats each docstring as a separate *test suite*.\n", + "In other words, a single test suite could correspond to the docstring for an\n", + "individual package, module, function, struct, struct method, etc.\n", + "\n", + "Each executable code block within a given docstring is a single test of the same\n", + "test suite. The `mojo test` command executes the tests of a test suite\n", + "sequentially in the order that they appear within the docstring. If a test\n", + "within a particular test suite fails, then all subsequent tests within the same\n", + "test suite are skipped.\n", + "\n", + "All tests within the test suite execute in the same scope, and test execution\n", + "within that scope is stateful. This means, for example, that a variable created\n", + "within one test is then accessible to subsequent tests in the same test suite.\n", + "\n", + "~~~\n", + "\"\"\" Stateful example.\n", + "\n", + "Assign 1 to the variable `a`:\n", + "\n", + "```mojo\n", + "%# from testing import assert_equal\n", + "a = 1\n", + "%# assert_equal(a, 1)\n", + "```\n", + "\n", + "Then increment the value of `a` by 1:\n", + "\n", + "```mojo\n", + "a += 1\n", + "%# assert_equal(a, 2)\n", + "```\n", + "\"\"\"\n", + "~~~\n", + "\n", + ":::note\n", + "\n", + "Test suite scopes do *not* nest. In other words, the test suite scope of a\n", + "module is completely independent of the test suite scope of a function or struct\n", + "defined within that module. For example, this means that if a module’s test\n", + "suite creates a variable, that variable is *not* accessible to a function’s test\n", + "suite within the same module.\n", + "\n", + ":::\n", + "\n", + "### Documentation test identifiers\n", + "\n", + "The format of a documentation test identifier is `@::`.\n", + "This is best explained by an example. Consider the project structure shown in\n", + "the [Running tests](#running-tests) section. The source files in the `src`\n", + "directory might contain docstrings for the `my_math` package, the `utils.mojo`\n", + "module, and the individual functions within that module. You could collect the\n", + "full list of tests by executing:\n", + "\n", + "```\n", + "mojo test --co src\n", + "```\n", + "\n", + "The output shows the hierarchy of directories, test files, and individual tests\n", + "(note that this example elides the full filesystem paths from the output shown):\n", + "\n", + "```\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "```\n", + "\n", + "Several different test suites appear in this result:\n", + "\n", + "| Test suite scope | File | Test suite name |\n", + "|------------------|------|-----------------|\n", + "| Package | `src/my_math/__init__.mojo` | `__doc__` |\n", + "| Module | `src/my_math/utils.mojo` | `__doc__` |\n", + "| Function | `src/my_math/utils.mojo` | `inc(stdlib\\3A\\3Abuiltin\\3A\\3Aint\\3A\\3AInt).__doc__` |\n", "\n", - "The [testing module](/mojo/stdlib/testing/testing) defines a set of test-related\n", - "utilities. " + "Then within a specific test suite, tests are numbered sequentially in the order\n", + "they appear in the docstring, starting with 0." ] } ], From 5145303ba1038c869e47420a1aa3db7e9610c023 Mon Sep 17 00:00:00 2001 From: KenJones-Modular <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 5 Jun 2024 19:45:18 -0700 Subject: [PATCH 0911/2019] [docs] New Mojo control flow page (#39845) MODULAR_ORIG_COMMIT_REV_ID: ef41e2ef31de18bfa0acb2530d9e09a62c9caff4 --- docs/manual/control-flow.ipynb | 887 +++++++++++++++++++++++++++++++++ docs/manual/index.md | 1 + 2 files changed, 888 insertions(+) create mode 100644 docs/manual/control-flow.ipynb diff --git a/docs/manual/control-flow.ipynb b/docs/manual/control-flow.ipynb new file mode 100644 index 0000000000..8ee4dab294 --- /dev/null +++ b/docs/manual/control-flow.ipynb @@ -0,0 +1,887 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Control flow\n", + "description: Mojo control flow statements.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mojo includes several traditional control flow structures for conditional and\n", + "repeated execution of code blocks.\n", + "\n", + "## The `if` statement\n", + "\n", + "Mojo supports the `if` statement for conditional code execution. With it you can\n", + "conditionally execute an indented code block if a given\n", + "[boolean](/mojo/manual/types#booleans) expression evaluates to `True`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "It is warm.\n", + "The temperature is 77.0 Fahrenheit.\n" + ] + } + ], + "source": [ + "temp_celsius = 25\n", + "if temp_celsius > 20:\n", + " print(\"It is warm.\")\n", + " print(\"The temperature is\", temp_celsius * 9 / 5 + 32, \"Fahrenheit.\" )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can write the entire `if` statement as a single line if all you need to\n", + "execute conditionally is a single, short statement." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "It is warm.\n" + ] + } + ], + "source": [ + "temp_celsius = 22\n", + "if temp_celsius < 15: print(\"It is cool.\") # Skipped because condition is False\n", + "if temp_celsius > 20: print(\"It is warm.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Optionally, an `if` statement can include any number of additional `elif`\n", + "clauses, each specifying a boolean condition and associated code block to\n", + "execute if `True`. The conditions are tested in the order given. When a\n", + "condition evaluates to `True`, the associated code block is executed and no\n", + "further conditions are tested.\n", + "\n", + "Additionally, an `if` statement can include an optional `else` clause providing\n", + "a code block to execute if all conditions evaluate to `False`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "It is warm.\n" + ] + } + ], + "source": [ + "temp_celsius = 25\n", + "if temp_celsius <= 0:\n", + " print(\"It is freezing.\")\n", + "elif temp_celsius < 20:\n", + " print(\"It is cool.\")\n", + "elif temp_celsius < 30:\n", + " print(\"It is warm.\")\n", + "else:\n", + " print(\"It is hot.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note TODO\n", + "\n", + "Mojo currently does not support the equivalent of a Python `match` or C `switch`\n", + "statement for pattern matching and conditional execution.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Short-circuit evaluation\n", + "\n", + "Mojo follows [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation)\n", + "semantics for boolean operators. If the first argument to an `or` operator\n", + "evaluates to `True`, the second argument is not evaluated.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Short-circuit \"or\" evaluation\n", + "Executing true_func\n", + "True result\n" + ] + } + ], + "source": [ + "def true_func() -> Bool:\n", + " print(\"Executing true_func\")\n", + " return True\n", + "\n", + "def false_func() -> Bool:\n", + " print(\"Executing false_func\")\n", + " return False\n", + "\n", + "print('Short-circuit \"or\" evaluation')\n", + "if true_func() or false_func():\n", + " print(\"True result\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the first argument to an `and` operator evaluates to `False`, the second\n", + "argument is not evaluated." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Short-circuit \"and\" evaluation\n", + "Executing false_func\n" + ] + } + ], + "source": [ + "print('Short-circuit \"and\" evaluation')\n", + "if false_func() and true_func():\n", + " print(\"True result\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conditional expressions\n", + "\n", + "Mojo also supports conditional expressions (or what is sometimes called a\n", + "[_ternary conditional operator_](https://en.wikipedia.org/wiki/Ternary_conditional_operator))\n", + "using the syntaxtrue_result if boolean_expression else false_result, just as\n", + "in Python. This is most often used as a concise way to assign one of two\n", + "different values to a variable, based on a boolean condition." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The forecast for today is cool\n" + ] + } + ], + "source": [ + "temp_celsius = 15\n", + "forecast = \"warm\" if temp_celsius > 20 else \"cool\"\n", + "print(\"The forecast for today is\", forecast)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The alternative, written as a multi-line `if` statement, is more verbose." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The forecast for today is cool\n" + ] + } + ], + "source": [ + "if temp_celsius > 20:\n", + " forecast = \"warm\"\n", + "else:\n", + " forecast = \"cool\"\n", + "print(\"The forecast for today is\", forecast)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `while` statement\n", + "\n", + "The `while` loop repeatedly executes a code block while a given boolean\n", + "expression evaluates to `True`. For example, the following loop prints values\n", + "from the Fibonacci series that are less than 50." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 1, 2, 3, 5, 8, 13, 21, 34" + ] + } + ], + "source": [ + "fib_prev = 0\n", + "fib_curr = 1\n", + "\n", + "print(fib_prev, end=\"\")\n", + "while fib_curr < 50:\n", + " print(\",\", fib_curr, end=\"\")\n", + " fib_prev, fib_curr = fib_curr, fib_prev + fib_curr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `continue` statement skips execution of the rest of the code block and\n", + "resumes with the loop test expression." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, 4, 5, " + ] + } + ], + "source": [ + "n = 0\n", + "while n < 5:\n", + " n += 1\n", + " if n == 3:\n", + " continue\n", + " print(n, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `break` statement terminates execution of the loop." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, " + ] + } + ], + "source": [ + "n = 0\n", + "while n < 5:\n", + " n += 1\n", + " if n == 3:\n", + " break\n", + " print(n, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Optionally, a `while` loop can include an `else` clause. The body of the `else`\n", + "clause executes when the loop's boolean condition evaluates to `False`, even if\n", + "it occurs the first time tested." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loop completed\n" + ] + } + ], + "source": [ + "n = 5\n", + "\n", + "while n < 4:\n", + " print(n)\n", + " n += 1\n", + "else:\n", + " print(\"Loop completed\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note\n", + "\n", + "The `else` clause does _not_ execute if a `break` or `return` statement\n", + "exits the `while` loop.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n" + ] + } + ], + "source": [ + "n = 0\n", + "while n < 5:\n", + " n += 1\n", + " if n == 3:\n", + " break\n", + " print(n)\n", + "else:\n", + " print(\"Executing else clause\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `for` statement\n", + "\n", + "The `for` loop iterates over a sequence, executing a code block for each\n", + "element in the sequence.\n", + "The Mojo `for` loop can iterate over any type that implements an `__iter__()`\n", + "method that returns a type that defines `__next__()` and `__len__()` methods.\n", + "\n", + "### Iterating over Mojo collections\n", + "\n", + "All of the collection types in the [`collections`](/mojo/stdlib/collections)\n", + "module support `for` loop iteration. See the\n", + "[Collection types](/mojo/manual/types#collection-types) documentation for more\n", + "information on Mojo collection types.\n", + "\n", + ":::caution TODO\n", + "\n", + "Iterating over Mojo native collections currently assigns the loop index variable\n", + "a [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", + "item itself. You can access the item using the dereference operator, `[]`, as\n", + "shown in the examples below. This may change in a future version of Mojo.\n", + "\n", + ":::\n", + "\n", + "The following shows an example of iterating over a Mojo\n", + "[`List`](/mojo/stdlib/collections/list/List)." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "California\n", + "Hawaii\n", + "Oregon\n" + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "states = List[String](\"California\", \"Hawaii\", \"Oregon\")\n", + "for state in states:\n", + " print(state[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The same technique works for iterating over a Mojo\n", + "[`Set`](/mojo/stdlib/collections/set/Set)." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42\n", + "0\n" + ] + } + ], + "source": [ + "from collections import Set\n", + "\n", + "values = Set[Int](42, 0)\n", + "for item in values:\n", + " print(item[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two techniques for iterating over a Mojo\n", + "[`Dict`](/mojo/stdlib/collections/dict/Dict). The first is to iterate directly\n", + "using the `Dict`, which produces a sequence of the dictionary's keys." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sacramento, California\n", + "Honolulu, Hawaii\n", + "Salem, Oregon\n" + ] + } + ], + "source": [ + "from collections import Dict\n", + "\n", + "capitals = Dict[String, String]()\n", + "capitals[\"California\"] = \"Sacramento\"\n", + "capitals[\"Hawaii\"] = \"Honolulu\"\n", + "capitals[\"Oregon\"] = \"Salem\"\n", + "\n", + "for state in capitals:\n", + " print(capitals[state[]] + \", \" + state[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second approach to iterating over a Mojo `Dict` is to invoke its\n", + "[`items()`](/mojo/stdlib/collections/dict/Dict#items) method, which produces a\n", + "sequence of [`DictEntry`](/mojo/stdlib/collections/dict/DictEntry) objects.\n", + "Within the loop body, you can then access the `key` and `value` fields of the\n", + "entry." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sacramento, California\n", + "Honolulu, Hawaii\n", + "Salem, Oregon\n" + ] + } + ], + "source": [ + "for item in capitals.items():\n", + " print(item[].value + \", \" + item[].key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another type of iterable provided by the Mojo standard library is a _range_,\n", + "which is a sequence of integers generated by the\n", + "[`range()`](/mojo/stdlib/builtin/range/range) function. It differs from the\n", + "collection types shown above in that it's implemented as a\n", + "[generator](https://en.wikipedia.org/wiki/Generator_(computer_programming)),\n", + "producing each value as needed rather than materializing the entire sequence\n", + "in memory. Additionally, each value assigned to the loop index variable is\n", + "simply the `Int` value rather than a `Reference` to the value, so you should\n", + "not use the dereference operator on it within the loop. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, 3, 4, " + ] + } + ], + "source": [ + "for i in range(5):\n", + " print(i, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `for` loop control statements\n", + "\n", + "A `continue` statement skips execution of the rest of the code block and\n", + "resumes the loop with the next element of the collection." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, 4, " + ] + } + ], + "source": [ + "for i in range(5):\n", + " if i == 3:\n", + " continue\n", + " print(i, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `break` statement terminates execution of the loop." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, " + ] + } + ], + "source": [ + "for i in range(5):\n", + " if i == 3:\n", + " break\n", + " print(i, end=\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Optionally, a `for` loop can include an `else` clause. The body of the `else`\n", + "clause executes after iterating over all of the elements in a collection." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0, 1, 2, 3, 4, \n", + "Finished executing 'for' loop\n" + ] + } + ], + "source": [ + "for i in range(5):\n", + " print(i, end=\", \")\n", + "else:\n", + " print(\"\\nFinished executing 'for' loop\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `else` clause executes even if the collection is empty." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finished executing 'for' loop\n" + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "empty = List[Int]()\n", + "for i in empty:\n", + " print(i[])\n", + "else:\n", + " print(\"Finished executing 'for' loop\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note\n", + "\n", + "The `else` clause does _not_ execute if a `break` or `return` statement\n", + "terminates the `for` loop.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found a dog\n" + ] + } + ], + "source": [ + "from collections import List\n", + "\n", + "animals = List[String](\"cat\", \"aardvark\", \"hippopotamus\", \"dog\")\n", + "for animal in animals:\n", + " if animal[] == \"dog\":\n", + " print(\"Found a dog\")\n", + " break\n", + "else:\n", + " print(\"No dog found\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating over Python collections\n", + "\n", + "The Mojo `for` loop supports iterating over Python collection types. Each item\n", + "retrieved by the loop is a\n", + "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", + "the Python object. Refer to the [Python types](/mojo/manual/python/types)\n", + "documentation for more information on manipulating Python objects from Mojo.\n", + "\n", + "The following is a simple example of iterating over a mixed-type Python list." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42\n", + "cat\n", + "3.14159\n" + ] + } + ], + "source": [ + "from python import Python\n", + "\n", + "# Create a mixed-type Python list\n", + "py_list = Python.evaluate(\"[42, 'cat', 3.14159]\")\n", + "for py_obj in py_list: # Each element is of type \"PythonObject\"\n", + " print(py_obj)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note TODO\n", + "\n", + "Iterating over a Mojo collection currently assigns the loop index variable a\n", + "`Reference` to each element, which then requires you to use the dereference\n", + "operator within the loop body. In contrast, iterating over a Python collection\n", + "assigns a `PythonObject` wrapper for the element, which does _not_ require you\n", + "to use the dereference operator.\n", + "\n", + ":::\n", + "\n", + "\n", + "There are two techniques for iterating over a Python dictionary. The first is to\n", + "iterate directly using the dictionary, which produces a sequence of its keys." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a 1\n", + "b 2.71828\n", + "c sushi\n" + ] + } + ], + "source": [ + "from python import Python\n", + "\n", + "# Create a mixed-type Python dictionary\n", + "py_dict = Python.evaluate(\"{'a': 1, 'b': 2.71828, 'c': 'sushi'}\")\n", + "for py_key in py_dict: # Each element is of type \"PythonObject\"\n", + " print(py_key, py_dict[py_key])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second approach to iterating over a Python dictionary is to invoke its\n", + "`items()` method, which produces a sequence of 2-tuple objects.\n", + "Within the loop body, you can then access the key and value by index." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a 1\n", + "b 2.71828\n", + "c sushi\n" + ] + } + ], + "source": [ + "for py_tuple in py_dict.items(): # Each element is of type \"PythonObject\"\n", + " print(py_tuple[0], py_tuple[1])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/manual/index.md b/docs/manual/index.md index 4d28a625c4..f0390218c4 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -33,6 +33,7 @@ feedback](/mojo/community). - [Functions](/mojo/manual/functions) - [Variables](/mojo/manual/variables) - [Types](/mojo/manual/types) + - [Control flow](/mojo/manual/control-flow) - [Structs](/mojo/manual/structs) - [Modules and packages](/mojo/manual/packages) From 94aa8f12ad6bcecf9fe66d08a9b842daf3585215 Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Thu, 6 Jun 2024 17:20:39 -0500 Subject: [PATCH 0912/2019] [External] [docs] Fix docstring in `swap.mojo` (#41487) [External] [docs] Fix docstring in `swap.mojo` The code says that `T` is constrained to Movable types, so the docstring should also reflect this. It only uses the `^` operator in its body. Co-authored-by: Brian M Johnson Closes modularml/mojo#2969 MODULAR_ORIG_COMMIT_REV_ID: d8c80105b34292fb8253781e92b3abfaae2db661 --- stdlib/src/builtin/swap.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/swap.mojo b/stdlib/src/builtin/swap.mojo index 3c3fcc6e22..fed7502fd1 100644 --- a/stdlib/src/builtin/swap.mojo +++ b/stdlib/src/builtin/swap.mojo @@ -21,7 +21,7 @@ fn swap[T: Movable](inout lhs: T, inout rhs: T): """Swaps the two given arguments. Parameters: - T: Constrained to Copyable types. + T: Constrained to Movable types. Args: lhs: Argument value swapped with rhs. From 064bc93fe23476bf441c38de7d2a22c75905ad20 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 6 Jun 2024 17:32:31 -0700 Subject: [PATCH 0913/2019] Update the changelog release date. MODULAR_ORIG_COMMIT_REV_ID: cd8a0ab9efdafb704e864f4b29761787fe55407a --- docs/changelog-released.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index d89301e596..f74caa86d4 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -25,7 +25,7 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` -## v24.4 (2024-06-06) +## v24.4 (2024-06-07) ### ✨ Highlights From f4f147cf2533640d27dabb7006a8d4427dbb4f56 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 7 Jun 2024 08:33:42 -0700 Subject: [PATCH 0914/2019] Fixes for recent SIMD/DTypePointer changes. Adds test back in but only runs one benchmark in CI to avoid excess running time. Also clean up formatting, get padding back in line. MODULAR_ORIG_COMMIT_REV_ID: b73a8570731fbba503381d2e65fa84bfbe5f5467 --- examples/matmul.mojo | 83 ++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 47f04f8b06..b37ccf617a 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -10,6 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +# RUN: %mojo %s | FileCheck %s # This sample demonstrates how various systems optimizations can be applied to a # naive matmul implementation in Mojo to gain significant performance speedups @@ -22,6 +23,7 @@ from algorithm import parallelize, vectorize from sys import info from memory import memset_zero from python import Python +from os.env import getenv alias M = 512 # rows of A and C alias N = 4096 # cols of B and C @@ -61,31 +63,28 @@ struct Matrix[rows: Int, cols: Int]: self.store[1](y, x, val) fn load[nelts: Int](self, y: Int, x: Int) -> SIMD[type, nelts]: - return self.data.load[width=nelts](y * self.cols + x) + return SIMD[size=nelts].load(self.data, y * self.cols + x) fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]): - return self.data.store[width=nelts](y * self.cols + x, val) + SIMD[size=nelts].store(self.data, y * self.cols + x, val) def run_matmul_python() -> Float64: - Python.add_to_path(".") var pymatmul: PythonObject = Python.import_module("pymatmul") var py = Python.import_module("builtins") var gflops = pymatmul.benchmark_matmul_python(128, 128, 128).to_float64() - py.print(py.str("{:<13}{:>8.3f} GFLOPS").format("Python:", gflops)) + py.print(py.str("{:<18}{:>8.3f} GFLOPS").format("Python:", gflops)) return gflops -def run_matmul_numpy() -> Float64: +def run_matmul_numpy(): var pymatmul: PythonObject = Python.import_module("pymatmul") var py = Python.import_module("builtins") var gflops = pymatmul.benchmark_matmul_numpy(M, N, K).to_float64() - py.print(py.str("{:<13}{:>8.3f} GFLOPS").format("Numpy:", gflops)) - - return gflops + py.print(py.str("{:<18}{:>8.3f} GFLOPS").format("Numpy:", gflops)) fn matmul_naive(inout C: Matrix, A: Matrix, B: Matrix): @@ -226,7 +225,7 @@ fn bench[ var py = Python.import_module("builtins") _ = py.print( - py.str("{:<13}{:>8.3f} GFLOPS {:>9.2f}x Python").format( + py.str("{:<18}{:>8.3f} GFLOPS {:>9.2f}x Python").format( name, gflops, speedup ) ) @@ -248,7 +247,7 @@ fn test_matrix_equal[ return True -fn test_all() raises: +def test_all(): var A = Matrix[M, K].rand() var B = Matrix[K, N].rand() var C = Matrix[M, N]() @@ -263,59 +262,35 @@ fn test_all() raises: raise Error("Tiled output does not match naive implementation") if not test_matrix_equal[matmul_unrolled[0]](C, A, B): raise Error("Unroll output does not match naive implementation") - if not test_matrix_equal[matmul_unrolled[1]]( - C, - A, - B, - ): - raise Error( - "Unroll with workers as physical cores output does not match naive" - " implementation" - ) - if not test_matrix_equal[matmul_unrolled[2]]( - C, - A, - B, - ): - raise Error( - "Unroll with workers as logical cores output does not match naive" - " implementation" - ) - if not test_matrix_equal[matmul_unrolled[3]]( - C, - A, - B, - ): - raise Error( - "Unroll with workers as performance cores output does not match" - " naive implementation" - ) + if not test_matrix_equal[matmul_unrolled[1]](C, A, B): + raise Error("Unroll/physical cores does not match naive implementation") + if not test_matrix_equal[matmul_unrolled[2]](C, A, B): + raise Error("Unroll/logical cores does not match naive implementation") + if not test_matrix_equal[matmul_unrolled[3]](C, A, B): + raise Error("Unroll/perf cores does not match naive implementation") A.data.free() B.data.free() C.data.free() -fn main() raises: +def main(): constrained[N % tile_n == 0, "N must be a multiple of tile_n"]() constrained[K % tile_k == 0, "K must be a multiple of tile_k"]() test_all() print("CPU Results\n") var python_gflops = run_matmul_python() - var numpy_gflops = run_matmul_numpy() - - bench[matmul_naive, "Naive:"](python_gflops) - bench[matmul_vectorized, "Vectorized: "](python_gflops) - bench[matmul_parallelized, "Parallelized:"](python_gflops) - bench[matmul_tiled, "Tiled:"](python_gflops) - bench[matmul_unrolled[0], "Unrolled:"](python_gflops) - bench[matmul_unrolled[1], "Unrolled w/ workers == physical cores:"]( - python_gflops - ) - bench[matmul_unrolled[2], "Unrolled w/ workers == logical cores:"]( - python_gflops - ) - bench[matmul_unrolled[3], "Unrolled w/ workers == performance cores:"]( - python_gflops - ) + run_matmul_numpy() + + # Don't run all these benchmarks in CI, too resource intensive + if not getenv("CI"): + bench[matmul_naive, "Naive:"](python_gflops) + bench[matmul_vectorized, "Vectorized: "](python_gflops) + bench[matmul_parallelized, "Parallelized:"](python_gflops) + bench[matmul_tiled, "Tiled:"](python_gflops) + bench[matmul_unrolled[0], "Unrolled:"](python_gflops) + bench[matmul_unrolled[1], "Physical Cores:"](python_gflops) + bench[matmul_unrolled[2], "Logical Cores:"](python_gflops) + # CHECK: Performance Cores + bench[matmul_unrolled[3], "Performance Cores:"](python_gflops) From 5ed605003d9b558f0b6ca1a732fdb345a38444f0 Mon Sep 17 00:00:00 2001 From: Samay Kapadia Date: Fri, 7 Jun 2024 14:12:50 -0500 Subject: [PATCH 0915/2019] [External] [docs] fix typo for 24.2 in changelog-released.md (#41592) [External] [docs] fix typo for 24.2 in changelog-released.md Opening a new PR on top of nightly instead of main, previous PR: https://github.com/modularml/mojo/pull/2980 Closes modularml/mojo#2982 MODULAR_ORIG_COMMIT_REV_ID: 74fc96f5af40fcc9c4790ed2ab350b651bb7c71c --- docs/changelog-released.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index f74caa86d4..64923bc9fc 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -51,7 +51,7 @@ Big themes for this release: ### Language changes - Mojo has changed how `def` function arguments are processed. Previously, by - default, arguments to a `def` were treated treated according to the `owned` + default, arguments to a `def` were treated according to the `owned` convention, which makes a copy of the value, enabling that value to be mutable in the callee. From 7433f63f3f5d40c76312e427c90fb8a0bec4053b Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Fri, 7 Jun 2024 18:27:25 -0500 Subject: [PATCH 0916/2019] [External] [docs] Fix typo in `changelog-released.md` (#41588) [External] [docs] Fix typo in `changelog-released.md` Change "a major performance issues" to "major performance issues" Co-authored-by: Brian M Johnson Closes modularml/mojo#2978 MODULAR_ORIG_COMMIT_REV_ID: 0ee459018e9aafdd88a400c75afcc782ed45f543 --- docs/changelog-released.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 64923bc9fc..9acaf6c1e6 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -55,7 +55,7 @@ Big themes for this release: convention, which makes a copy of the value, enabling that value to be mutable in the callee. - This could lead to a major performance issues because of the proliferation of + This could lead to major performance issues because of the proliferation of unnecessary copies. It also required you to declare non-copyable types as `borrowed` explicitly. Now Mojo takes a different approach: `def` functions take arguments as `borrowed` by default (consistent with `fn` functions) but From 6a8cefcbcf7d8d47430b053e8e7bd5b0c72ba4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Brunk?= Date: Sat, 8 Jun 2024 11:24:56 -0500 Subject: [PATCH 0917/2019] [External] [docs] Fix parameter docs and add inferred-only description (#41638) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [docs] Fix parameter docs and add inferred-only description - Description and fix for new `//` inferred-only syntax - Fix for new explicit String conversions - Fix for partially bound types with `SIMD` changes Co-authored-by: Sören Brunk Closes modularml/mojo#2991 MODULAR_ORIG_COMMIT_REV_ID: 5a3a50a992ec166c96101a4823dc88d221a2d505 --- docs/manual/parameters/index.ipynb | 135 ++++++++++++++++++----------- 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 088ee3ced8..2b38241046 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -139,16 +139,15 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "3\n", - "3\n", - "3\n" + "42\n", + "42\n" ] } ], @@ -158,7 +157,8 @@ " for i in range(count):\n", " print(msg)\n", "\n", - "repeat[3](3)" + "# Must use keyword parameter for `count`\n", + "repeat[count=2](42)" ] }, { @@ -167,6 +167,40 @@ "source": [ "This updated function takes any `Stringable` type, so you can pass it an `Int`,\n", "`String`, or `Bool` value.\n", + "\n", + "You can't pass the `count` as a positional keyword without also specifying `MsgType`.\n", + "You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now\n", + "you can pass the following parameter `count` positionally:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "42\n", + "42\n" + ] + } + ], + "source": [ + "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", + " @parameter\n", + " for i in range(count):\n", + " print(msg)\n", + "\n", + "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", + "repeat[2](42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "Mojo's support for generics is still early. You can write generic functions like\n", "this using traits and parameters. You can also write generic collections like\n", @@ -189,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -243,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -341,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -401,7 +435,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -485,7 +519,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -535,7 +569,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -575,7 +609,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -607,7 +641,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -636,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -709,7 +743,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -741,7 +775,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -766,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -806,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -851,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -895,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -920,7 +954,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -961,7 +995,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -1006,7 +1040,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -1068,7 +1102,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1117,7 +1151,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -1177,7 +1211,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -1213,12 +1247,13 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "alias Bytes = SIMD[DType.uint8, _]\n", - "var b = Bytes[8]()" + "alias StringKeyDict = Dict[String, _]\n", + "var b = StringKeyDict[UInt8]()\n", + "b[\"answer\"] = 42" ] }, { @@ -1234,7 +1269,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -1331,7 +1366,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -1367,7 +1402,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -1398,7 +1433,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -1415,7 +1450,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -1436,7 +1471,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -1483,7 +1518,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -1491,7 +1526,7 @@ "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", " fn __str__(self) -> String:\n", " var values = StaticIntTuple[3](sugar, cream, chocolate)\n", - " return str(\"Fudge\") + values" + " return str(\"Fudge\") + str(values)" ] }, { @@ -1504,7 +1539,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -1526,7 +1561,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -1544,7 +1579,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -1584,7 +1619,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -1604,12 +1639,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(str(\"Devoured \") + str(f))" + " print(str(\"Devoured \") + str(f))" ] }, { @@ -1625,7 +1660,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -1642,7 +1677,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -1671,7 +1706,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -1705,9 +1740,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Mojo", + "display_name": "Mojo Source", "language": "mojo", - "name": "mojo-jupyter-kernel" + "name": "mojo-source-kernel" }, "language_info": { "codemirror_mode": { From bc2ba69730185b2c9b5fd8092efa8bda24c99b78 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Sat, 8 Jun 2024 16:07:47 -0700 Subject: [PATCH 0918/2019] [Docs] Fix some broken links. Also fixed an outdated sample & fixed an omission in the Mojo changelog reported on Discord. MODULAR_ORIG_COMMIT_REV_ID: 9f85b1b52a973283e3b6110557c8581212779241 --- docs/changelog-released.md | 28 ++++++++----- docs/manual/functions.ipynb | 2 +- docs/manual/get-started.md | 6 +-- docs/manual/index.md | 2 +- docs/manual/lifecycle/death.ipynb | 67 +++++++------------------------ docs/manual/python/types.ipynb | 2 +- 6 files changed, 37 insertions(+), 70 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 9acaf6c1e6..44fd4c9fc7 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -221,7 +221,7 @@ Big themes for this release: - New traits and related features: - - Added built-in [`repr()`](mojo/stdlib/builtin/repr/repr) function and + - Added built-in [`repr()`](/mojo/stdlib/builtin/repr/repr) function and [`Representable`](/mojo/stdlib/builtin/repr/Representable) trait. ([PR #2361](https://github.com/modularml/mojo/pull/2361)) @@ -489,6 +489,9 @@ Big themes for this release: - Many functions returning a pointer type have been unified to have a public API function of `unsafe_ptr()`. + - The `Tensor.data()` method has been renamed to `unsafe_ptr()`. The return + type is still a `DTypePointer[T]`. + - Collections: - [`List`](/mojo/stdlib/collections/list/List) now has an @@ -783,7 +786,9 @@ Big themes for this release: - `math.limit.min_finite()`: use `utils.numerics.min_finite()` - The `tensor.random` module has been removed. The same functionality is now - accessible via the `Tensor.rand()` and `Tensor.randn()` static methods. + accessible via the [`Tensor.rand()`](/mojo/stdlib/tensor/tensor/Tensor#rand) + and [`Tensor.randn()`](/mojo/stdlib/tensor/tensor/Tensor#randn) static + methods. - The builtin `SIMD` struct no longer conforms to `Indexer`; users must explicitly cast `Scalar` values using `int`. @@ -1611,7 +1616,7 @@ fixed in a future release. type, a register-passable alternative to [`Optional`](/mojo/stdlib/collections/optional/Optional). -- The [`ulp()`](/mojo/stdlib/math/math/ulp) function has been added to the +- The [`ulp()`](/mojo/stdlib/utils/numerics/ulp) function has been added to the `math` module. This allows you to get the units of least precision (or units of last place) of a floating point value. @@ -1700,10 +1705,11 @@ fixed in a future release. from buffer import parallel_memcpy ``` - - The [`rand()`](/mojo/stdlib/tensor/random/rand) and - [`randn()`](/mojo/stdlib/tensor/random/randn) functions from the `random` - package that return a `Tensor` have moved to the `tensor` package. Note that - the overloads that write to a `DTypePointer` remain in the `random` package. + - The [`rand()`](/mojo/stdlib/tensor/tensor/Tensor#rand) and + [`randn()`](/mojo/stdlib/tensor/tensor/Tensor#randn) functions from the + `random` package that return a `Tensor` have moved to the `tensor` package. + Note that the overloads that write to a `DTypePointer` remain in the + `random` package. If you happen to be using both versions in the same source file, you can import them both using the `import as` syntax: @@ -1721,9 +1727,9 @@ fixed in a future release. from os import abort ``` - - The [`isinf()`](/mojo/stdlib/math/math/isinf) and - [`isfinite()`](/mojo/stdlib/math/math/isfinite) methods have been moved from - `math.limits` to the `math` module. + - The [`isinf()`](/mojo/stdlib/utils/numerics/isfinite) and + [`isfinite()`](/mojo/stdlib/utils/numerics/isfinite) methods have been moved + from `math.limits` to the `math` module. ```mojo from math import ininf, isfinite @@ -2061,7 +2067,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `UnusualSlice` constructor. - The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/reference/reference) instead of having to + [`Reference`](/mojo/stdlib/memory/reference/Reference) instead of having to return an MLIR internal reference type. - Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 64e4e2e604..5cd00427b9 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -404,7 +404,7 @@ "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", - "[`Reference`](/mojo/stdlib/memory/unsafe/reference) for each value instead\n", + "[`Reference`](/mojo/stdlib/memory/reference/Reference) for each value instead\n", "of the value itself. You must add an empty subscript operator `[]` to\n", "dereference the reference and retrieve the value:\n" ] diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index 15fb479331..36e41a6bc9 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -378,9 +378,9 @@ Now let's write the code in a Mojo source file and run it with the Hello, world! ``` -If this didn't work for you, double-check that your code looks exactly like the code -in step 1, and make sure you correctly [installed -mojo](/mojo/install) (it includes Mojo). +If this didn't work for you, double-check that your code looks exactly like the +code in step 1, and make sure you correctly installed either [MAX](/max/install) +(which includes Mojo) or [Mojo](#1-install-mojo). ## 4. Build an executable binary diff --git a/docs/manual/index.md b/docs/manual/index.md index f0390218c4..139148920e 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -24,7 +24,7 @@ feedback](/mojo/community). - **Get started** - - [Why Mojo](/mojo/manual/why-mojo) + - [Why Mojo](/mojo/why-mojo) - [Get started with Mojo](/mojo/manual/get-started) - **Language basics** diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 4df4001373..d13e357940 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -200,7 +200,8 @@ "dynamically allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", - "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" + "memory allocated by its\n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer):" ] }, { @@ -209,17 +210,21 @@ "metadata": {}, "outputs": [], "source": [ + "from memory.unsafe_pointer import UnsafePointer\n", + "\n", "struct HeapArray:\n", - " var data: Pointer[Int]\n", + " var data: UnsafePointer[Int]\n", " var size: Int\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", - " self.data = Pointer[Int].alloc(self.size)\n", + " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data.store(i, val)\n", + " (self.data + i).init_pointee_copy(val)\n", "\n", " fn __del__(owned self):\n", + " for i in range(self.size):\n", + " (self.data + 1).destroy_pointee()\n", " self.data.free()" ] }, @@ -231,40 +236,9 @@ "when a pointer is destroyed, Mojo doesn't call the destructors on those values.\n", "\n", "So in the `HeapArray` example above, calling `free()` on the pointer releases\n", - "the memory, but doesn't call the destructors on the stored `Int` values. That's\n", - "OK in this case, because trivial types like `Int` have no-op destructors.\n", - "However, in the general case—if you're storing values that _might_ have\n", - "functional destructors—it's not enough to call `free()` on the pointer. You\n", - "should also ensure that Mojo completely destroys all the allocated types\n", - "through their destructors.\n", - "\n", - "The following example updates the previous `__del__()` method to ensure\n", - "destructors are called on array elements. It does this by assigning each of the\n", - "stored values in turn to the `_` \"discard\" pattern. Assigning a value to the\n", - "`_` discard variable tells the compiler that this is the last use of that\n", - "value, so it can be destroyed immediately." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "struct HeapArray:\n", - " var data: Pointer[Int]\n", - " var size: Int\n", - "\n", - " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " _ = __get_address_as_owned_value((self.data + i).address)\n", - " self.data.free()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "the memory, but doesn't call the destructors on the stored values. To invoke\n", + "the destructors, use the `destroy_pointee()` method provided by the \n", + "`UnsafePointer` type.\n", "\n", ":::note\n", "\n", @@ -274,20 +248,7 @@ "destroys a value, however, it passes in the original value as `self`, not a\n", "copy.\n", "\n", - ":::\n", - "\n", - "For instances of the \n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type, use\n", - "the [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", - "function instead of the discard pattern. For example, if the previous example\n", - "used `UnsafePointer`, the `__del__()` method would look like this:\n", - "\n", - "```mojo\n", - "fn __del__(owned self):\n", - " for i in range(self.size):\n", - " destroy_pointee(self.data + i)\n", - " self.data.free()\n", - "```" + ":::" ] }, { @@ -303,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index 24533e268a..f3c74ebe72 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -215,7 +215,7 @@ "using the built-in \n", "[`int()`](/mojo/stdlib/builtin/int/int-function),\n", "[`str()`](/mojo/stdlib/builtin/str/str),\n", - "and [`bool()`](/mojo/stdlib/builtin/bool/bool) functions, and print Python \n", + "and [`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python \n", "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", From e251ef42a8f6560f2d478d1f129774c7c83cfe5b Mon Sep 17 00:00:00 2001 From: Andrew Luo Date: Sat, 8 Jun 2024 22:24:11 -0700 Subject: [PATCH 0919/2019] [stdlib] Move `Scalar` From Omitting to Unbinding Parameter Style As noted by the [public docs](https://docs.modular.com/mojo/manual/parameters/#:~:text=Parameter%20inference%E2%80%8B&text=Mojo%20can%20also%20infer%20the,a%20constructor%20or%20static%20method.&text=Note%20that%20you%20can%20create,it%20from%20the%20value%20argument.) The omitting style is to be deprecated. Semantically the two styles are the same in this case (the only difference is in omitting style, default parameters are immediately init.). MODULAR_ORIG_COMMIT_REV_ID: 9e988d8240b3b9472a9eaa99b9f4faa4e1509466 --- stdlib/src/builtin/simd.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6618804d39..3c0bbe3884 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -55,7 +55,7 @@ from .string import _calc_initial_buffer_size, _calc_format_buffer_size # Type Aliases # ===----------------------------------------------------------------------=== # -alias Scalar = SIMD[size=1] +alias Scalar = SIMD[_, size=1] """Represents a scalar dtype.""" alias Int8 = Scalar[DType.int8] From c94836688b1dd26fca6bee8c5f1b8db97cb910ac Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:21:49 -0500 Subject: [PATCH 0920/2019] [External] [stdlib] Implement `NamedTemporaryFile` in `tempfile` module (#41582) [External] [stdlib] Implement `NamedTemporaryFile` in `tempfile` module Implementation of `NamedTemporaryFile` a wrapper around a `FileHandle` that can be used as a context manager that is deleted after closing it if needed. ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2762 MODULAR_ORIG_COMMIT_REV_ID: 84f0675e5d24afecb64a776cddc0a98823c2961a --- docs/changelog.md | 3 + stdlib/src/tempfile/__init__.mojo | 7 +- stdlib/src/tempfile/tempfile.mojo | 130 ++++++++++++++++++++++++ stdlib/test/tempfile/test_tempfile.mojo | 70 +++++++++++-- 4 files changed, 200 insertions(+), 10 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 269ed3e310..606891ca46 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added `TemporaryDirectory` in module `tempfile`. ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) +- Added `NamedTemporaryFile` in module `tempfile`. + ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) + - Added temporary `SliceNew` type with corrected behaviour from `Slice` to facilitate an incremental internal migration due to reliance on the old, incorrect behaviour. ([PR #2894](https://github.com/modularml/mojo/pull/2894) by [@bgreni](https://github.com/bgreni)) diff --git a/stdlib/src/tempfile/__init__.mojo b/stdlib/src/tempfile/__init__.mojo index c018869cf1..5e62fe3b17 100644 --- a/stdlib/src/tempfile/__init__.mojo +++ b/stdlib/src/tempfile/__init__.mojo @@ -12,4 +12,9 @@ # ===----------------------------------------------------------------------=== # """Implements the tempfile package.""" -from .tempfile import gettempdir, mkdtemp, TemporaryDirectory +from .tempfile import ( + gettempdir, + mkdtemp, + TemporaryDirectory, + NamedTemporaryFile, +) diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 0808ac4f5f..24bbf7f81c 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -239,3 +239,133 @@ struct TemporaryDirectory: return True except: return False + + +struct NamedTemporaryFile: + """A handle to a temporary file.""" + + var _file_handle: FileHandle + """The underlying file handle.""" + var _delete: Bool + """Whether the file is deleted on close.""" + var name: String + """Name of the file.""" + + fn __init__( + inout self, + mode: String = "w", + suffix: String = "", + prefix: String = "tmp", + dir: Optional[String] = None, + delete: Bool = True, + ) raises: + """Create a named temporary file. Can be used as a context manager. + This is a wrapper around a `FileHandle`, + os.remove is called in close method if `delete` is True. + + Args: + mode: The mode to open the file in (the mode can be "r" or "w"). + suffix: Suffix to use for the file name. + prefix: Prefix to use for the file name. + dir: Directory in which the file will be created. + delete: Whether the file is deleted on close. + """ + + var final_dir = Path(dir.value()) if dir else Path( + _get_default_tempdir() + ) + + self._delete = delete + self.name = "" + + for _ in range(TMP_MAX): + var potential_name = final_dir / ( + prefix + _get_random_name() + suffix + ) + if not os.path.exists(potential_name): + self.name = potential_name.__fspath__() + break + try: + # TODO for now this name could be relative, + # python implementation expands the path, + # but several functions are not yet implemented in mojo + # i.e. abspath, normpath + self._file_handle = FileHandle(self.name, mode=mode) + return + except: + raise Error("Failed to create temporary file") + + @always_inline + fn __del__(owned self): + """Closes the file handle.""" + try: + self.close() + except: + pass + + fn close(inout self) raises: + """Closes the file handle.""" + self._file_handle.close() + if self._delete: + os.remove(self.name) + + fn __moveinit__(inout self, owned existing: Self): + """Moves constructor for the file handle. + + Args: + existing: The existing file handle. + """ + self._file_handle = existing._file_handle^ + self._delete = existing._delete + self.name = existing.name^ + + @always_inline + fn read(self, size: Int64 = -1) raises -> String: + """Reads the data from the file. + + Args: + size: Requested number of bytes to read. + + Returns: + The contents of the file. + """ + return self._file_handle.read(size) + + fn read_bytes(self, size: Int64 = -1) raises -> List[UInt8]: + """Read from file buffer until we have `size` characters or we hit EOF. + If `size` is negative or omitted, read until EOF. + + Args: + size: Requested number of bytes to read. + + Returns: + The contents of the file. + """ + return self._file_handle.read_bytes(size) + + fn seek(self, offset: UInt64) raises -> UInt64: + """Seeks to the given offset in the file. + + Args: + offset: The byte offset to seek to from the start of the file. + + Raises: + An error if this file handle is invalid, or if file seek returned a + failure. + + Returns: + The resulting byte offset from the start of the file. + """ + return self._file_handle.seek(offset) + + fn write(self, data: String) raises: + """Write the data to the file. + + Args: + data: The data to write to the file. + """ + self._file_handle.write(data) + + fn __enter__(owned self) -> Self: + """The function to call when entering the context.""" + return self^ diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index 8cbe6c015b..f4e48cdc65 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -15,12 +15,12 @@ import os from os.path import exists from pathlib import Path -from tempfile import TemporaryDirectory, gettempdir, mkdtemp +from tempfile import NamedTemporaryFile, TemporaryDirectory, gettempdir, mkdtemp from testing import assert_equal, assert_false, assert_true -fn test_mkdtemp() raises: +def test_mkdtemp(): var dir_name: String dir_name = mkdtemp() @@ -62,7 +62,7 @@ struct TempEnvWithCleanup: self._vars_back = Dict[String, String]() self.clean_up_function = clean_up_function - fn __enter__(inout self) raises: + def __enter__(inout self): for key_value in self.vars_to_set.items(): var key = key_value[].key var value = key_value[].value @@ -75,7 +75,7 @@ struct TempEnvWithCleanup: var value = key_value[].value _ = os.setenv(key, value, overwrite=True) - fn __exit__(inout self, error: Error) raises -> Bool: + def __exit__(inout self, error: Error) -> Bool: self.__exit__() self.clean_up_function() return False @@ -90,9 +90,9 @@ fn _clean_up_gettempdir_test() raises: os.rmdir(dir_with_writing_access) -fn _set_up_gettempdir_test( +def _set_up_gettempdir_test( dir_with_writing_access: Path, dir_without_writing_access: Path -) raises: +): os.mkdir(dir_with_writing_access, mode=0o700) try: os.mkdir(dir_without_writing_access, mode=0o100) @@ -101,7 +101,7 @@ fn _set_up_gettempdir_test( raise Error("Failed to setup test") -fn test_gettempdir() raises: +def test_gettempdir(): var non_existing_dir = Path() / "non_existing_dir" assert_false( exists(non_existing_dir), @@ -162,7 +162,7 @@ fn test_gettempdir() raises: _clean_up_gettempdir_test() -fn test_temporary_directory() raises -> None: +def test_temporary_directory() -> None: var tmp_dir: String = "" with TemporaryDirectory(suffix="my_suffix", prefix="my_prefix") as tmp_dir: assert_true(exists(tmp_dir), "Failed to create temp dir " + tmp_dir) @@ -178,7 +178,59 @@ fn test_temporary_directory() raises -> None: assert_false(exists(tmp_dir), "Failed to delete temp dir " + tmp_dir) -fn main() raises: +def test_named_temporary_file_deletion(): + var tmp_file: NamedTemporaryFile + var file_path: String + + with NamedTemporaryFile( + prefix="my_prefix", suffix="my_suffix", dir=Path().__fspath__() + ) as my_tmp_file: + file_path = my_tmp_file.name + var file_name = file_path.split(os.sep)[-1] + assert_true(exists(file_path), "Failed to create file " + file_path) + assert_true(file_name.startswith("my_prefix")) + assert_true(file_name.endswith("my_suffix")) + # TODO use os.path.split when it exists + assert_equal(file_path[: -len(file_name) - 1], Path().__fspath__()) + assert_false(exists(file_path), "Failed to delete file " + file_path) + + with NamedTemporaryFile(delete=False) as my_tmp_file: + file_path = my_tmp_file.name + assert_true(exists(file_path), "Failed to create file " + file_path) + assert_true(exists(file_path), "File " + file_path + " should still exist") + os.remove(file_path) + + tmp_file = NamedTemporaryFile() + file_path = tmp_file.name + assert_true(exists(file_path), "Failed to create file " + file_path) + tmp_file.close() + assert_false(exists(file_path), "Failed to delete file " + file_path) + + tmp_file = NamedTemporaryFile(delete=False) + file_path = tmp_file.name + assert_true(exists(file_path), "Failed to create file " + file_path) + tmp_file.close() + assert_true(exists(file_path), "File " + file_path + " should still exist") + os.remove(file_path) + + +def test_named_temporary_file_write(): + var file_name: String + var contents: String + + with NamedTemporaryFile(delete=False) as my_tmp_file: + file_name = my_tmp_file.name + my_tmp_file.write("hello world") + + with open(file_name, "r") as my_file: + contents = my_file.read() + assert_equal("hello world", contents) + os.remove(file_name) + + +def main(): test_mkdtemp() test_gettempdir() test_temporary_directory() + test_named_temporary_file_write() + test_named_temporary_file_deletion() From 4886ddd842a1c65f2b9711aae89c1eabc0aa725d Mon Sep 17 00:00:00 2001 From: Scott Brenner Date: Mon, 10 Jun 2024 12:41:27 -0500 Subject: [PATCH 0921/2019] [External] [docs] Update "Contributing" section in examples/README.md (#41664) [External] [docs] Update "Contributing" section in examples/README.md Proposing updating this section as it conflicts with the contents under https://github.com/modularml/mojo/tree/nightly?tab=readme-ov-file#contributing, inspired by contents in https://github.com/modularml/mojo/blob/nightly/stdlib/README.md#contributing-to-the-mojo-standard-library. Co-authored-by: Scott Brenner Closes modularml/mojo#2997 MODULAR_ORIG_COMMIT_REV_ID: 1f159ffa655d49ddee95f637ad812bc6c205082a --- examples/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/README.md b/examples/README.md index 4b1b9d147c..f5d82b7152 100644 --- a/examples/README.md +++ b/examples/README.md @@ -91,6 +91,10 @@ under the Apache License v2.0 with LLVM Exceptions ## Contributing -Thanks for your interest in contributing to this repository! -We are not accepting pull requests at this time, but are actively -working on a process to accept contributions. Please stay tuned. +As a contributor, your efforts and expertise are invaluable in driving the +evolution of the Mojo programming language. The [Mojo contributor +guide](../CONTRIBUTING.md) provides all the information necessary to make +meaningful contributions—from understanding the submission process to +adhering to best practices: + +- [Mojo contributor guide](../CONTRIBUTING.md) From 084185031399367663e325adcafc952da9b0aee6 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 10 Jun 2024 12:42:05 -0500 Subject: [PATCH 0922/2019] [External] [stdlib] Add `List.unsafe_set()` (#41635) [External] [stdlib] Add `List.unsafe_set()` This is the counterpart of the function `unsafe_get` already present on `List` and is, like `unsafe_get`, inspired by rust's `get_unchecked` : https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_unchecked Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2966 MODULAR_ORIG_COMMIT_REV_ID: 5fc72d5249f24d164fef8b49e7606be82ccfdceb --- stdlib/src/collections/list.mojo | 30 ++++++++++++++++++++++++-- stdlib/test/collections/test_list.mojo | 21 ++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 2ce1099829..639d43ac99 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -209,8 +209,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if normalized_idx < 0: normalized_idx += len(self) - (self.data + normalized_idx).destroy_pointee() - (self.data + normalized_idx).init_pointee_move(value^) + self.unsafe_set(normalized_idx, value^) @always_inline fn __contains__[ @@ -817,6 +816,33 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): ) return (self.data + idx)[] + @always_inline + fn unsafe_set(self, idx: Int, owned value: T): + """Write a value to a given location without checking index bounds. + + Users should consider using `my_list[idx] = value` instead of this method as it + is unsafe. If an index is out of bounds, this method will not abort, it + will be considered undefined behavior. + + Note that there is no wraparound for negative indices, caution is + advised. Using negative indices is considered undefined behavior. Never + use `my_list.unsafe_set(-1, value)` to set the last element of the list. + Instead, do `my_list.unsafe_set(len(my_list) - 1, value)`. + + Args: + idx: The index of the element to set. + value: The value to set. + """ + debug_assert( + 0 <= idx < len(self), + ( + "The index provided must be within the range [0, len(List) -1]" + " when using List.unsafe_set()" + ), + ) + (self.data + idx).destroy_pointee() + (self.data + idx).init_pointee_move(value^) + fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. Note that since we can't condition methods on a trait yet, diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index fc3a1c13c4..42e90ee602 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -81,6 +81,26 @@ def test_list_unsafe_get(): assert_equal(2, list.unsafe_get(0)[]) +def test_list_unsafe_set(): + var list = List[Int]() + + for i in range(5): + list.append(i) + + assert_equal(5, len(list)) + list.unsafe_set(0, 0) + list.unsafe_set(1, 10) + list.unsafe_set(2, 20) + list.unsafe_set(3, 30) + list.unsafe_set(4, 40) + + assert_equal(list[0], 0) + assert_equal(list[1], 10) + assert_equal(list[2], 20) + assert_equal(list[3], 30) + assert_equal(list[4], 40) + + def test_list_clear(): var list = List[Int](1, 2, 3) assert_equal(len(list), 3) @@ -812,6 +832,7 @@ def main(): test_mojo_issue_698() test_list() test_list_unsafe_get() + test_list_unsafe_set() test_list_clear() test_list_to_bool_conversion() test_list_pop() From 2d54a7686abbef59ce9d45fa79080c588e1be596 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Mon, 10 Jun 2024 12:42:40 -0500 Subject: [PATCH 0923/2019] [External] [stdlib] Implement `CollectionElementNew` for `Span` (#41633) [External] [stdlib] Implement `CollectionElementNew` for `Span` Similar to https://github.com/modularml/mojo/pull/2955, add the `ExplicitCopy` constructor to `Span`. Co-authored-by: Lukas Hermann Closes modularml/mojo#2976 MODULAR_ORIG_COMMIT_REV_ID: fc805ea848de2b85f8d0073d3e3b7e27cb733a3c --- stdlib/src/utils/span.mojo | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 364fd4c1d3..68a82c0e64 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -74,7 +74,7 @@ struct Span[ is_mutable: Bool, //, T: CollectionElement, lifetime: AnyLifetime[is_mutable].type, -]: +](CollectionElementNew): """A non owning view of contiguous data. Parameters: @@ -102,6 +102,16 @@ struct Span[ self._data = unsafe_ptr self._len = len + @always_inline + fn __init__(inout self, *, other: Self): + """Explicitly construct a deep copy of the provided Span. + + Args: + other: The Span to copy. + """ + self._data = other._data + self._len = other._len + @always_inline fn __init__(inout self, ref [lifetime]list: List[T]): """Construct a Span from a List. From 03cb85055ae7f03632c4dd69140c39587c52f7dc Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:44:51 -0500 Subject: [PATCH 0924/2019] [External] [stdlib] Removed SIMD.splat (#41632) [External] [stdlib] Removed some uses of `SIMD.splat` Remove uses of `SIMD.splat` in the open source standard library. Related to https://github.com/modularml/mojo/issues/2942 ORIGINAL_AUTHOR=Devesh Rahatekar <79015420+devesh-2002@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2990 Co-authored-by: Devesh Rahatekar <79015420+devesh-2002@users.noreply.github.com> Closes modularml/mojo#2990 MODULAR_ORIG_COMMIT_REV_ID: 87810a77b979aa16a330212681df3836a68dd48e --- examples/notebooks/programming-manual.ipynb | 2 +- stdlib/src/bit/bit.mojo | 6 +++--- stdlib/test/builtin/test_simd.mojo | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/notebooks/programming-manual.ipynb b/examples/notebooks/programming-manual.ipynb index faa2db8a61..ace149a6ba 100644 --- a/examples/notebooks/programming-manual.ipynb +++ b/examples/notebooks/programming-manual.ipynb @@ -1704,7 +1704,7 @@ "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n", "\n", "# Make a big vector containing 1.0 in float16 format.\n", - "var big_vec = SIMD[DType.float16, 32].splat(1.0)\n", + "var big_vec = SIMD[DType.float16, 32](1.0)\n", "\n", "# Do some math and convert the elements to float32.\n", "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n", diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 573dae7069..5c9a05b633 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -246,7 +246,7 @@ fn bit_not[ NOT of the integer value at position `i` of the input value. """ constrained[type.is_integral(), "must be integral"]() - var neg_one = SIMD[type, simd_width].splat(-1) + var neg_one = SIMD[type, simd_width](-1) return __mlir_op.`pop.xor`(val.value, neg_one.value) @@ -402,7 +402,7 @@ fn bit_ceil[ """ constrained[type.is_integral(), "must be integral"]() - alias ones = SIMD[type, simd_width].splat(1) + alias ones = SIMD[type, simd_width](1) return (val > 1).select(1 << bit_width(val - ones), ones) @@ -455,7 +455,7 @@ fn bit_floor[ """ constrained[type.is_integral(), "must be integral and unsigned"]() - alias zeros = SIMD[type, simd_width].splat(0) + alias zeros = SIMD[type, simd_width](0) return (val > 0).select(1 << (bit_width(val) - 1), zeros) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8b0e54a62a..d846c4d72b 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1440,13 +1440,13 @@ def test_reduce_bit_count(): var int_iota8 = SIMD[DType.int32, 8](0, 1, 2, 3, 4, 5, 6, 7) assert_equal(int_iota8.reduce_bit_count(), 12) - var bool_true = Scalar[DType.bool].splat(True) + var bool_true = Scalar[DType.bool](True) assert_equal(bool_true.reduce_bit_count(), 1) - var bool_false = Scalar[DType.bool].splat(False) + var bool_false = Scalar[DType.bool](False) assert_equal(bool_false.reduce_bit_count(), 0) - var bool_true16 = SIMD[DType.bool, 16].splat(True) + var bool_true16 = SIMD[DType.bool, 16](True) assert_equal(bool_true16.reduce_bit_count(), 16) From 1e5e8c8e92378ee70daca6b1680d1f67ace5c555 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 10 Jun 2024 14:29:15 -0600 Subject: [PATCH 0925/2019] [stdlib] Remove `SIMD.splat` Now that all of the uses of `SIMD.splat` have been moved over to using the constructor for `SIMD`, remove `SIMD.splat` method itself. MODULAR_ORIG_COMMIT_REV_ID: 20fb93ad26b39c781bd83734f7c4951adcc05987 --- docs/changelog.md | 2 ++ stdlib/src/builtin/simd.mojo | 24 ------------------------ 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 606891ca46..918eb60b65 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -148,4 +148,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. + ### 🛠️ Fixed diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 3c0bbe3884..32dcae4e94 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -451,30 +451,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) ) - # ===-------------------------------------------------------------------===# - # Factory methods - # ===-------------------------------------------------------------------===# - - @staticmethod - @always_inline("nodebug") - fn splat(x: Scalar[type]) -> Self: - """Splats (broadcasts) the element onto the vector. - - Args: - x: The input scalar value. - - Returns: - A new SIMD vector whose elements are the same as the input value. - """ - _simd_construction_checks[type, size]() - return Self { - value: __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[ - `!pop.simd<`, size.value, `, `, type.value, `>` - ] - ](x.value) - } - # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# From c5d9832c3c61f6505075d39818295dd5801426eb Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 10 Jun 2024 15:37:30 -0500 Subject: [PATCH 0926/2019] [External] [stdlib] Add a `UInt` struct with a few methods (#41662) [External] [stdlib] Add a `UInt` struct with a few methods Fix part of * https://github.com/modularml/mojo/issues/2919 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2932 MODULAR_ORIG_COMMIT_REV_ID: c69af24872d2234e12384633310bb2a64ddf7875 --- stdlib/src/builtin/simd.mojo | 23 ++++++-- stdlib/src/builtin/uint.mojo | 86 ++++++++++++++++++++++++++++++ stdlib/test/builtin/test_uint.mojo | 33 ++++++++++++ 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 stdlib/src/builtin/uint.mojo create mode 100644 stdlib/test/builtin/test_uint.mojo diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 32dcae4e94..cc95c2e0f4 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -227,21 +227,38 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ self.__copyinit__(other) + @always_inline("nodebug") + fn __init__(inout self, value: UInt): + """Initializes the SIMD vector with an unsigned integer. + + The unsigned integer value is splatted across all the elements of the SIMD + vector. + + Args: + value: The input value. + """ + self = Self(value.value) + @always_inline("nodebug") fn __init__(inout self, value: Int): - """Initializes the SIMD vector with an integer. + """Initializes the SIMD vector with a signed integer. - The integer value is splatted across all the elements of the SIMD + The signed integer value is splatted across all the elements of the SIMD vector. Args: value: The input value. """ + self = Self(value.value) + + @doc_private + @always_inline("nodebug") + fn __init__(inout self, value: __mlir_type.index): _simd_construction_checks[type, size]() var t0 = __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type.`!pop.scalar` - ](value.value) + ](value) var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] ](t0) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo new file mode 100644 index 0000000000..ad894f98b4 --- /dev/null +++ b/stdlib/src/builtin/uint.mojo @@ -0,0 +1,86 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the UInt class. + +These are Mojo built-ins, so you don't need to import them. +""" + + +@lldb_formatter_wrapping_type +@value +@register_passable("trivial") +struct UInt: + """This type represents an unsigned integer. + + An unsigned integer is represents a positive integral number. + + The size of this unsigned integer is platform-dependent. + + If you wish to use a fixed size unsigned integer, consider using + `UInt8`, `UInt16`, `UInt32`, or `UInt64`. + """ + + var value: __mlir_type.index + """The underlying storage for the integer value. + + Note that it is the same type as the `Int.value` field. + MLIR doesn't differentiate between signed and unsigned integers + when it comes to storing them with the index dialect. + The difference is in the operations that are performed on them, + which have signed and unsigned variants. + """ + + @always_inline("nodebug") + fn __init__(inout self): + """Default constructor that produces zero.""" + self.value = __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() + + @always_inline("nodebug") + fn __init__(inout self, value: __mlir_type.index): + """Construct Int from the given index value. + + Args: + value: The init value. + """ + self.value = value + + @always_inline("nodebug") + fn __init__(inout self, value: IntLiteral): + """Construct Int from the given IntLiteral value. + + Args: + value: The init value. + """ + # TODO: Find a way to convert directly without using UInt64. + # This is because the existing + # __mlir_op.`kgen.int_literal.convert` + # in IntLiteral.__as_mlir_index() + # assumes that the index represents an signed integer. + # We need a variant for unsigned integers. + # Change when https://github.com/modularml/mojo/issues/2933 is fixed + self.value = int(UInt64(value)).value + + @always_inline("nodebug") + fn __str__(self) -> String: + """Convert this UInt to a string. + + A small example. + ```mojo + var x = UInt(50) + var x_as_string = str(x) # x_as_string = "50" + ``` + + Returns: + The string representation of this Int. + """ + return str(UInt64(self)) diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo new file mode 100644 index 0000000000..d3180c285c --- /dev/null +++ b/stdlib/test/builtin/test_uint.mojo @@ -0,0 +1,33 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal + + +def test_simple_uint(): + assert_equal(str(UInt(32)), "32") + + assert_equal(str(UInt(0)), "0") + assert_equal(str(UInt()), "0") + + # (2 ** 64) - 1 + # TODO: raise an error in the future when + # https://github.com/modularml/mojo/issues/2933 is fixed + assert_equal(str(UInt(-1)), "18446744073709551615") + + assert_equal(str(UInt(18446744073709551615)), "18446744073709551615") + + +def main(): + test_simple_uint() From f8ecb0c587fc5992c44855e93d2dc2c108cb8aaf Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:40:41 -0400 Subject: [PATCH 0927/2019] [stdlib] Remove `lit` substitution for `TEMP_FILE_DIR` in tests. We can now access the default temporary directory from stdlib, which allows us to avoid using `lit` substitutions. This PR change the command line argument `-D TEMP_FILE_DIR=%T` and `env_get_string["TEMP_FILE_DIR"]` to using `gettempfile()`. MODULAR_ORIG_COMMIT_REV_ID: 97a0c8ed5c1ec3214c5cb99eefa24fecfdce5519 --- stdlib/src/tempfile/tempfile.mojo | 25 +++++++++++++------------ stdlib/test/builtin/test_file.mojo | 19 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 24bbf7f81c..8b8dff6a36 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -254,6 +254,7 @@ struct NamedTemporaryFile: fn __init__( inout self, mode: String = "w", + name: Optional[String] = None, suffix: String = "", prefix: String = "tmp", dir: Optional[String] = None, @@ -265,26 +266,26 @@ struct NamedTemporaryFile: Args: mode: The mode to open the file in (the mode can be "r" or "w"). - suffix: Suffix to use for the file name. - prefix: Prefix to use for the file name. + name: The name of the temp file; if it is unspecified, then random name will be provided. + suffix: Suffix to use for the file name if name is not provided. + prefix: Prefix to use for the file name if name is not provided. dir: Directory in which the file will be created. delete: Whether the file is deleted on close. """ - var final_dir = Path(dir.value()) if dir else Path( - _get_default_tempdir() - ) + var final_dir = dir.value() if dir else _get_default_tempdir() self._delete = delete self.name = "" - for _ in range(TMP_MAX): - var potential_name = final_dir / ( - prefix + _get_random_name() + suffix - ) - if not os.path.exists(potential_name): - self.name = potential_name.__fspath__() - break + if name: + self.name = name.value() + else: + for _ in range(TMP_MAX): + var potential_name = final_dir + os.sep + prefix + _get_random_name() + suffix + if not os.path.exists(potential_name): + self.name = potential_name + break try: # TODO for now this name could be relative, # python implementation expands the path, diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 3ec0efcb1c..4ce10ad262 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -10,16 +10,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D TEMP_FILE_DIR=%T -debug-level full %s +# RUN: %mojo -debug-level full %s from pathlib import Path, _dir_of_current_file -from sys import os_is_windows, env_get_string +from tempfile import gettempdir +from sys import os_is_windows from testing import assert_equal, assert_true -alias TEMP_FILE_DIR = env_get_string["TEMP_FILE_DIR"]() - def test_file_read(): var path = _dir_of_current_file() / "test_file_dummy_input.txt" @@ -141,7 +140,7 @@ def test_file_open_nodir(): def test_file_write(): var content: String = "The quick brown fox jumps over the lazy dog" - var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write" + var TEMP_FILE = Path(gettempdir().value()) / "test_file_write" with open(TEMP_FILE, "w") as f: f.write(content) @@ -151,7 +150,7 @@ def test_file_write(): def test_file_write_span(): var content: String = "The quick brown fox jumps over the lazy dog" - var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write_span" + var TEMP_FILE = Path(gettempdir().value()) / "test_file_write_span" with open(TEMP_FILE, "w") as f: f.write(content.as_bytes_slice()) @@ -162,7 +161,7 @@ def test_file_write_span(): def test_file_write_again(): var unexpected_content: String = "foo bar baz" var expected_content: String = "foo bar" - var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write_again" + var TEMP_FILE = Path(gettempdir().value()) / "test_file_write_again" with open(TEMP_FILE, "w") as f: f.write(unexpected_content) @@ -213,9 +212,9 @@ def test_file_read_to_dtype_pointer(): def test_file_get_raw_fd(): # since JIT and build give different file descriptors, we test by checking # if we printed to the right file. - var f1 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_1", "rw") - var f2 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_2", "rw") - var f3 = open(Path(TEMP_FILE_DIR) / "test_file_dummy_3", "rw") + var f1 = open(Path(gettempdir().value()) / "test_file_dummy_1", "rw") + var f2 = open(Path(gettempdir().value()) / "test_file_dummy_2", "rw") + var f3 = open(Path(gettempdir().value()) / "test_file_dummy_3", "rw") print( "test from file 1", From 13dc171b7c99f456e3e0718b52d0ebf92965eeb0 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 10 Jun 2024 19:35:56 -0500 Subject: [PATCH 0928/2019] [External] [stdlib] Fix use-after-free in `hash.mojo` (#41697) [External] [stdlib] Fix use-after-free in `hash.mojo` The last use of the `InlineArray` named `remaining` is at line 284. But we use a pointer pointing to it until line 288. We're very lucky this didn't break so far, but we should ensure the array lives long enough anyway. I'm pretty sure we were relying on some undefined behavior here. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2970 MODULAR_ORIG_COMMIT_REV_ID: f36814458195542be29c1b9ffaa8858d3f118b77 --- stdlib/src/builtin/hash.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 1bdc901451..cc517102a1 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -287,6 +287,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: memset_zero(ptr + r, stride - r) # set the rest to 0 var last_value = SIMD[size=simd_width].load(ptr.bitcast[type]()) hash_data = _HASH_UPDATE(hash_data, last_value) + _ = remaining # We make sure the array lives long enough. # Now finally, hash the final SIMD vector state. return _hash_simd(hash_data) From d197065e6d910cca57d19c0b7fbe149e777627d9 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 10 Jun 2024 17:45:13 -0700 Subject: [PATCH 0929/2019] [Docs] Fix more links. Does what it says on the tin. MODULAR_ORIG_COMMIT_REV_ID: 1455ee9d95c5adadcf40a2552ae5adf8fc7779d2 --- docs/changelog-released.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 44fd4c9fc7..82446c3348 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -813,7 +813,7 @@ Special thanks to our community contributors: [@rd4com](https://github.com/rd4com), [@toiletsandpaper](https://github.com/toiletsandpaper), -[@helehex](https://github.com/helehex), [@rd4com](https://github.com/rd4com/), +[@helehex](https://github.com/helehex), [@artemiogr97](https://github.com/artemiogr97), [@mikowals](https://github.com/mikowals), [@kernhanda](https://github.com/kernhanda), [@lsh](https://github.com/lsh), @@ -1787,8 +1787,8 @@ fixed in a future release. - The [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload that worked on [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) types has been removed in favor - of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer) and - [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): + of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/LegacyPointer) + and [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer): ```mojo # Doesn't work @@ -2073,8 +2073,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) method, for moving a value from one pointer memory location to another. -- Added built-in [`hex()`](/mojo/stdlib/builtin/hex/hex) function, which can be - used to format any value whose type implements the +- Added built-in [`hex()`](/mojo/stdlib/builtin/format_int/hex) function, which + can be used to format any value whose type implements the [`Intable`](/mojo/stdlib/builtin/int/Intable) trait as a hexadecimal string. - [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now implements @@ -3098,7 +3098,7 @@ the previous "read to EOF" behavior when size is negative. - Subscripting added to [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer): + [`Pointer`](/mojo/stdlib/memory/unsafe/LegacyPointer): ```mojo let p = DTypePointer[DType.float16].alloc(4) From 4c33d2370996a28c8809197c72526869c351bfb0 Mon Sep 17 00:00:00 2001 From: Scott Brenner Date: Mon, 10 Jun 2024 20:17:27 -0500 Subject: [PATCH 0930/2019] [External] [docs] Correct "write your first Mojo program" hyperlink (#41701) [External] [docs] Correct "write your first Mojo program" hyperlink Page currently hyperlinked here no longer exists, think this is the new location? Co-authored-by: Scott Brenner Closes modularml/mojo#2995 MODULAR_ORIG_COMMIT_REV_ID: 970f5b2910e32d8ee325be1c8f94a8e4cd027f75 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebeb9c8c7e..c6c802ddc6 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ or the standalone Mojo SDK: - [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started/hello-world). +program](https://docs.modular.com/mojo/manual/get-started#2-run-code-in-the-repl). ### Latest Nightly From 14cb19cdc8ce405dbb8f24b6eda287447b28f70e Mon Sep 17 00:00:00 2001 From: Scott Brenner Date: Mon, 10 Jun 2024 20:18:08 -0500 Subject: [PATCH 0931/2019] [External] [docs] Correct "install the Mojo SDK" anchor (#41702) [External] [docs] Correct "install the Mojo SDK" anchor Similar to https://github.com/modularml/mojo/pull/2995, correcting the anchor here. Co-authored-by: Scott Brenner Closes modularml/mojo#2996 MODULAR_ORIG_COMMIT_REV_ID: e234893d024e7808bf5a4f1040be0face686eb76 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6c802ddc6..561ed6a259 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ view of how the development of Mojo is progressing. Use at your own risk and be patient! To get nightly builds, see the same instructions to [install the Mojo -SDK](https://docs.modular.com/mojo/manual/get-started/#install-mojo), but use +SDK](https://docs.modular.com/mojo/manual/get-started/#1-install-mojo), but use the command shown there to install `nightly/mojo`. When you clone this repo, be sure you switch to the `nightly` branch, because From 416e29d2fad8cfa5d6849c23acaa2b53bccb84a5 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Mon, 10 Jun 2024 21:38:19 -0500 Subject: [PATCH 0932/2019] [External] [stdlib] Refactor slice to use Optionals and fix negative step slices (#39458) [External] [stdlib] Refactor slice to use Optionals and fix negative step slices - Use `Optional` for `Slice` `start` and `end` - Fix negative step slice indexing - Fix incorrect String unit tests - Fix `_StridedRange` length with mismatched start and end indices Fixes #2046 Fixes #1944 Fixes #2142 Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2495 MODULAR_ORIG_COMMIT_REV_ID: cc3eba63bc981953391cfd7e57de1f3fbe2beaa6 --- docs/changelog.md | 12 ++- stdlib/src/builtin/builtin_slice.mojo | 137 +++++++++++++++---------- stdlib/src/builtin/range.mojo | 7 +- stdlib/src/builtin/string.mojo | 45 +++----- stdlib/src/collections/list.mojo | 35 ++----- stdlib/src/utils/span.mojo | 33 ++---- stdlib/test/builtin/test_range.mojo | 10 +- stdlib/test/builtin/test_slice.mojo | 67 ++++++++++-- stdlib/test/builtin/test_string.mojo | 12 ++- stdlib/test/collections/test_list.mojo | 39 +++++++ stdlib/test/utils/test_span.mojo | 24 +++++ 11 files changed, 262 insertions(+), 159 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 918eb60b65..0d10bac85c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -89,7 +89,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) Support automatic and manual indexing of `*args`. - + Examples: ```mojo @@ -134,6 +134,16 @@ by [@jayzhan211](https://github.com/jayzhan211)) the size of the SIMD type. The default store size is the size of the `SIMD` value to be stored. +- `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements + a constructor which accepts optional values. `Slice._has_end()` has also been removed + since a Slice with no end is now represented by an empty `Slice.end` option. + ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) + + ```mojo + var s = Slice(1, None, 2) + print(s.start.value()) # must retrieve the value from the optional + ``` + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 7739d98066..4699035970 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -20,24 +20,10 @@ from collections import OptionalReg @always_inline("nodebug") -fn _int_max_value() -> Int: - # FIXME: The `slice` type should have the concept of `None` indices, but the - # effect of a `None` end index is the same as a very large end index. - return int(Int32.MAX) - - -@always_inline("nodebug") -fn _default_or[T: AnyTrivialRegType](value: T, default: Int) -> Int: - # TODO: Handle `__index__` for other types when we have traits! - @parameter - if _mlirtype_is_eq[T, Int](): - return __mlir_op.`kgen.rebind`[_type=Int](value) - else: - __mlir_op.`kgen.param.assert`[ - cond = (_mlirtype_is_eq[T, NoneType]()).__mlir_i1__(), - message = "expected Int or NoneType".value, - ]() - return default +fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: + if x and y: + return x.value() == y.value() + return not x and not y @register_passable("trivial") @@ -56,9 +42,9 @@ struct Slice(Stringable, EqualityComparable): ``` """ - var start: Int + var start: OptionalReg[Int] """The starting index of the slice.""" - var end: Int + var end: OptionalReg[Int] """The end index of the slice.""" var step: Int """The step increment value of the slice.""" @@ -76,24 +62,22 @@ struct Slice(Stringable, EqualityComparable): self.step = 1 @always_inline("nodebug") - fn __init__[ - T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType - ](inout self, start: T0, end: T1, step: T2): + fn __init__( + inout self, + start: OptionalReg[Int], + end: OptionalReg[Int], + step: OptionalReg[Int], + ): """Construct slice given the start, end and step values. - Parameters: - T0: Type of the start value. - T1: Type of the end value. - T2: Type of the step value. - Args: start: The start value. end: The end value. step: The step value. """ - self.start = _default_or(start, 0) - self.end = _default_or(end, _int_max_value()) - self.step = _default_or(step, 1) + self.start = start + self.end = end + self.step = step.value() if step else 1 fn __str__(self) -> String: """Gets the string representation of the span. @@ -101,10 +85,10 @@ struct Slice(Stringable, EqualityComparable): Returns: The string representation of the span. """ - var res = str(self.start) + var res = str(self.start.value()) if self.start else "" res += ":" - if self._has_end(): - res += str(self.end) + if self.end: + res += str(self.end.value()) if self.end else "" res += ":" res += str(self.step) return res @@ -121,8 +105,8 @@ struct Slice(Stringable, EqualityComparable): corresponding values of the other slice and False otherwise. """ return ( - self.start == other.start - and self.end == other.end + _compare_optional(self.start, other.start) + and _compare_optional(self.end, other.end) and self.step == other.step ) @@ -149,7 +133,7 @@ struct Slice(Stringable, EqualityComparable): The length of the slice. """ - return len(range(self.start, self.end, self.step)) + return len(range(self.start.value(), self.end.value(), self.step)) @always_inline fn __getitem__(self, idx: Int) -> Int: @@ -161,11 +145,63 @@ struct Slice(Stringable, EqualityComparable): Returns: The slice index. """ - return self.start + index(idx) * self.step + return self.start.value() + idx * self.step - @always_inline("nodebug") - fn _has_end(self) -> Bool: - return self.end != _int_max_value() + fn indices(self, length: Int) -> (Int, Int, Int): + """Returns a tuple of 3 intergers representing the start, end, and step + of the slice if applied to a container of the given length. + + Uses the target container length to normalize negative, out of bounds, + or None indices. + + Negative indices are wrapped using the length of the container. + ```mojo + s = slice(0, -1, 1) + s.indices(5) # returns (0, 4, 1) + ``` + + None indices are defaulted to the start or the end of the container + based on whether `step` is positive or negative. + ```mojo + s = slice(None, None, 1) + s.indices(5) # returns (0, 5, 1) + ``` + + Out of bounds indices are clamped using the size of the container. + ```mojo + s = slice(20) + s.indices(5) # returns (0, 5, 1) + ``` + + Args: + length: The length of the target container. + + Returns: + A tuple containing three integers for start, end, and step. + """ + var start = self.start + var end = self.end + var positive_step = self.step > 0 + + if not start: + start = 0 if positive_step else length - 1 + elif start.value() < 0: + start = start.value() + length + if start.value() < 0: + start = 0 if positive_step else -1 + elif start.value() >= length: + start = length if positive_step else length - 1 + + if not end: + end = length if positive_step else -1 + elif end.value() < 0: + end = end.value() + length + if end.value() < 0: + end = 0 if positive_step else -1 + elif end.value() >= length: + end = length if positive_step else length - 1 + + return (start.value(), end.value(), self.step) @always_inline("nodebug") @@ -195,18 +231,12 @@ fn slice(start: Int, end: Int) -> Slice: return Slice(start, end) -# TODO(30496): Modernize the slice type @always_inline("nodebug") -fn slice[ - T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType -](start: T0, end: T1, step: T2) -> Slice: +fn slice( + start: OptionalReg[Int], end: OptionalReg[Int], step: OptionalReg[Int] +) -> Slice: """Construct a Slice given the start, end and step values. - Parameters: - T0: Type of the start value. - T1: Type of the end value. - T2: Type of the step value. - Args: start: The start value. end: The end value. @@ -223,13 +253,6 @@ fn slice[ # ===-------------------------------------------------------------------===# -@always_inline("nodebug") -fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: - if x and y: - return x.value() == y.value() - return not x and not y - - @register_passable("trivial") struct SliceNew(Stringable, EqualityComparable): """Represents a new slice implementation that corrects incorrect behaviour in the existing implementation. diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index bd72e13aaa..4a90f788db 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -179,9 +179,10 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): @always_inline("nodebug") fn __len__(self) -> Int: - # FIXME(#38392) - # if (self.step > 0) == (self.start > self.end): - # return 0 + if (self.step > 0 and self.start > self.end) or ( + self.step < 0 and self.start < self.end + ): + return 0 return _div_ceil_positive(abs(self.start - self.end), abs(self.step)) @always_inline("nodebug") diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5cb6fc53da..5b8d989fb4 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1029,7 +1029,6 @@ struct String( buf.append(0) return String(buf^) - @always_inline fn __getitem__(self, span: Slice) -> String: """Gets the sequence of characters at the specified positions. @@ -1039,18 +1038,24 @@ struct String( Returns: A new string containing the string at the specified positions. """ - - var adjusted_span = self._adjust_span(span) - var adjusted_span_len = adjusted_span.unsafe_indices() - if adjusted_span.step == 1: - return StringRef(self._buffer.data + span.start, adjusted_span_len) + var start: Int + var end: Int + var step: Int + start, end, step = span.indices(len(self)) + var r = range(start, end, step) + if step == 1: + return StringRef( + self._buffer.data + start, + len(r), + ) var buffer = Self._buffer_type() - buffer.resize(adjusted_span_len + 1, 0) + var result_len = len(r) + buffer.resize(result_len + 1, 0) var ptr = self.unsafe_ptr() - for i in range(adjusted_span_len): - buffer[i] = ptr[adjusted_span[i]] - buffer[adjusted_span_len] = 0 + for i in range(result_len): + buffer[i] = ptr[r[i]] + buffer[result_len] = 0 return Self(buffer^) @always_inline @@ -1267,26 +1272,6 @@ struct String( # Methods # ===------------------------------------------------------------------=== # - @always_inline - fn _adjust_span(self, span: Slice) -> Slice: - """Adjusts the span based on the string length.""" - var adjusted_span = span - - if adjusted_span.start < 0: - adjusted_span.start = len(self) + adjusted_span.start - - if not adjusted_span._has_end(): - adjusted_span.end = len(self) - elif adjusted_span.end < 0: - adjusted_span.end = len(self) + adjusted_span.end - - if span.step < 0: - var tmp = adjusted_span.end - adjusted_span.end = adjusted_span.start - 1 - adjusted_span.start = tmp - 1 - - return adjusted_span - fn format_to(self, inout writer: Formatter): """ Formats this string to the provided formatter. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 639d43ac99..12c3257145 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -701,26 +701,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.capacity = 0 return ptr - @always_inline - fn _adjust_span(self, span: Slice) -> Slice: - """Adjusts the span based on the list length.""" - var adjusted_span = span - - if adjusted_span.start < 0: - adjusted_span.start = len(self) + adjusted_span.start - - if not adjusted_span._has_end(): - adjusted_span.end = len(self) - elif adjusted_span.end < 0: - adjusted_span.end = len(self) + adjusted_span.end - - if span.step < 0: - var tmp = adjusted_span.end - adjusted_span.end = adjusted_span.start - 1 - adjusted_span.start = tmp - 1 - - return adjusted_span - @always_inline fn __getitem__(self, span: Slice) -> Self: """Gets the sequence of elements at the specified positions. @@ -732,15 +712,18 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): A new list containing the list at the specified span. """ - var adjusted_span = self._adjust_span(span) - var adjusted_span_len = adjusted_span.unsafe_indices() + var start: Int + var end: Int + var step: Int + start, end, step = span.indices(len(self)) + var r = range(start, end, step) - if not adjusted_span_len: + if not len(r): return Self() - var res = Self(capacity=adjusted_span_len) - for i in range(adjusted_span_len): - res.append(self[adjusted_span[i]]) + var res = Self(capacity=len(r)) + for i in r: + res.append(self[i]) return res^ diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 68a82c0e64..8a5d531d7b 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -175,15 +175,16 @@ struct Span[ Returns: A new span that points to the same data as the current span. """ - var adjusted_span = self._adjust_span(slc) + var start: Int + var end: Int + var step: Int + start, end, step = slc.indices(len(self)) debug_assert( - 0 <= adjusted_span.start <= self._len - and 0 <= adjusted_span.end <= self._len, - "Slice must be within bounds.", + step == 1, "Slice must be within bounds and step must be 1" ) var res = Self( - unsafe_ptr=(self._data + adjusted_span.start), - len=adjusted_span.unsafe_indices(), + unsafe_ptr=(self._data + start), + len=len(range(start, end, step)), ) return res @@ -214,26 +215,6 @@ struct Span[ # Methods # ===------------------------------------------------------------------===# - @always_inline - fn _adjust_span(self, span: Slice) -> Slice: - """Adjusts the span based on the list length.""" - var adjusted_span = span - - if adjusted_span.start < 0: - adjusted_span.start = len(self) + adjusted_span.start - - if not adjusted_span._has_end(): - adjusted_span.end = len(self) - elif adjusted_span.end < 0: - adjusted_span.end = len(self) + adjusted_span.end - - if span.step < 0: - var tmp = adjusted_span.end - adjusted_span.end = adjusted_span.start - 1 - adjusted_span.start = tmp - 1 - - return adjusted_span - fn unsafe_ptr(self) -> UnsafePointer[T]: """ Gets a pointer to the first element of this slice. diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 4c941ca076..ea847f3c93 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -30,11 +30,11 @@ def test_range_len(): assert_equal(range(0, 0).__len__(), 0, "len(range(0, 0))") assert_equal(range(10, 0).__len__(), 0, "len(range(10, 0))") assert_equal(range(0, 0, 1).__len__(), 0, "len(range(0, 0, 1))") - # FIXME(#38392) - # assert_equal(range(5, 10, -1).__len__(), 0, "len(range(5, 10, -1))") - # assert_equal(range(10, 5, 1).__len__(), 0, "len(range(10, 5, 1))") - # assert_equal(range(5, 10, -10).__len__(), 0, "len(range(5, 10, -10))") - # assert_equal(range(10, 5, 10).__len__(), 0, "len(range(10, 5, 10))") + + assert_equal(range(5, 10, -1).__len__(), 0, "len(range(5, 10, -1))") + assert_equal(range(10, 5, 1).__len__(), 0, "len(range(10, 5, 1))") + assert_equal(range(5, 10, -10).__len__(), 0, "len(range(5, 10, -10))") + assert_equal(range(10, 5, 10).__len__(), 0, "len(range(10, 5, 10))") assert_equal(range(5, 10, 20).__len__(), 1, "len(range(5, 10, 20))") assert_equal(range(10, 5, -20).__len__(), 1, "len(range(10, 5, -20))") diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 38ba1006d1..c99227fd38 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -17,8 +17,8 @@ from testing import assert_equal, assert_false, assert_true def test_none_end_folds(): alias all_def_slice = slice(0, None, 1) - assert_equal(all_def_slice.start, 0) - assert_equal(all_def_slice.end, int(Int32.MAX)) + assert_equal(all_def_slice.start.value(), 0) + assert_true(all_def_slice.end is None) assert_equal(all_def_slice.step, 1) @@ -62,11 +62,6 @@ def test_slicable(): assert_equal(boring_slice.c, "foo") -def test_has_end(): - alias is_end = Slice(None, None, None)._has_end() - assert_false(is_end) - - struct SliceStringable: fn __init__(inout self): pass @@ -79,7 +74,60 @@ def test_slice_stringable(): var s = SliceStringable() assert_equal(s[2::-1], "2::-1") assert_equal(s[1:-1:2], "1:-1:2") - assert_equal(s[:-1], "0:-1:1") + assert_equal(s[:-1], ":-1:1") + + +def test_slice_eq(): + assert_equal(slice(1, 2, 3), slice(1, 2, 3)) + assert_equal(slice(0, 1), slice(1)) + assert_true(slice(2, 3) != slice(4, 5)) + assert_equal(slice(1, None, 1), slice(1, None, None)) + + +def test_slice_adjust(): + var start: Int + var end: Int + var step: Int + var s = slice(1, 10) + start, end, step = s.indices(9) + assert_equal(slice(start, end, step), slice(1, 9)) + s = slice(1, None, 1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(1, 5)) + s = slice(1, None, -1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(1, -1, -1)) + s = slice(-1, None, 1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(4, 5, 1)) + s = slice(None, 2, 1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(0, 2, 1)) + s = slice(None, 2, -1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(4, 2, -1)) + s = slice(0, -1, 1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(0, 4, 1)) + s = slice(None, None, 1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(0, 5, 1)) + s = slice(20) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(0, 5, 1)) + s = slice(10, -10, 1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(5, 0, 1)) + assert_equal(len(range(start, end, step)), 0) + s = slice(-12, -10, -1) + start, end, step = s.indices(5) + assert_equal(slice(start, end, step), slice(-1, -1, -1)) + assert_equal(len(range(start, end, step)), 0) + # TODO: Decide how to handle 0 step + # s = slice(-10, -2, 0) + # start, end, step = s.indices(5) + # assert_equal(slice(start, end, step), slice(-1, 3, 0)) + # assert_equal(len(range(start, end, step)), 0) def test_indexing(): @@ -148,9 +196,10 @@ def test_none_end_folds_new(): def main(): test_none_end_folds() test_slicable() - test_has_end() test_slice_stringable() test_indexing() test_slice_indices_new() test_slice_eq_new() test_none_end_folds_new() + test_slice_eq() + test_slice_adjust() diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 937773e66c..5eb1b077c8 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -293,11 +293,19 @@ fn test_string_indexing() raises: assert_equal("!!ojoM olleH", str[::-1]) - assert_equal("!!ojoM oll", str[2::-1]) + assert_equal("leH", str[2::-1]) assert_equal("!oo le", str[::-2]) - assert_equal("!jMolH", str[:-1:-2]) + assert_equal("", str[:-1:-2]) + assert_equal("", str[-50::-1]) + assert_equal("Hello Mojo!!", str[-50::]) + assert_equal("!!ojoM olleH", str[:-50:-1]) + assert_equal("Hello Mojo!!", str[:50:]) + assert_equal("H", str[::50]) + assert_equal("!", str[::-50]) + assert_equal("!", str[50::-50]) + assert_equal("H", str[-50::50]) fn test_atol() raises: diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 42e90ee602..f2ff58397c 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -692,6 +692,45 @@ def test_list_span(): assert_equal(es[2], 3) assert_equal(len(es), 3) + assert_equal(vs[1:0:-1][0], 2) + assert_equal(vs[2:1:-1][0], 3) + es = vs[:0:-1] + assert_equal(es[0], 3) + assert_equal(es[1], 2) + assert_equal(vs[2::-1][0], 3) + + assert_equal(len(vs[1:2:-1]), 0) + + assert_equal(0, len(vs[:-1:-2])) + assert_equal(0, len(vs[-50::-1])) + es = vs[-50::] + assert_equal(3, len(es)) + assert_equal(es[0], 1) + assert_equal(es[1], 2) + assert_equal(es[2], 3) + es = vs[:-50:-1] + assert_equal(3, len(es)) + assert_equal(es[0], 3) + assert_equal(es[1], 2) + assert_equal(es[2], 1) + es = vs[:50:] + assert_equal(3, len(es)) + assert_equal(es[0], 1) + assert_equal(es[1], 2) + assert_equal(es[2], 3) + es = vs[::50] + assert_equal(1, len(es)) + assert_equal(es[0], 1) + es = vs[::-50] + assert_equal(1, len(es)) + assert_equal(es[0], 3) + es = vs[50::-50] + assert_equal(1, len(es)) + assert_equal(es[0], 3) + es = vs[-50::50] + assert_equal(1, len(es)) + assert_equal(es[0], 1) + def test_list_boolable(): assert_true(List[Int](1)) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 388d5aa83b..2292a9d707 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -120,9 +120,33 @@ def test_indexing(): assert_equal(s[3], 4) +def test_span_slice(): + def compare(s: Span[Int], l: List[Int]) -> Bool: + if len(s) != len(l): + return False + for i in range(len(s)): + if s[i] != l[i]: + return False + return True + + var l = List(1, 2, 3, 4, 5) + var s = Span(l) + var res = s[1:2] + assert_equal(res[0], 2) + res = s[1:-1:1] + assert_equal(res[0], 2) + assert_equal(res[1], 3) + assert_equal(res[2], 4) + # TODO: Fix Span slicing + # res = s[1::-1] + # assert_equal(res[0], 2) + # assert_equal(res[1], 1) + + def main(): test_span_list_int() test_span_list_str() test_span_array_int() test_span_array_str() test_indexing() + test_span_slice() From 84993a5ba4f24cafc05c412c93d0dfbb799212a0 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 11 Jun 2024 06:56:52 +0300 Subject: [PATCH 0933/2019] [stdlib] Remove `SliceNew` Because we were able to modify the existing `Slice` implementation without having to incrementally phase in the new optionaly start/end semantics. MODULAR_ORIG_COMMIT_REV_ID: f1d1121164d86fd37924c4e3536072dadb7f4615 --- docs/changelog.md | 4 - stdlib/src/builtin/builtin_slice.mojo | 207 -------------------------- stdlib/test/builtin/test_slice.mojo | 59 -------- 3 files changed, 270 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0d10bac85c..aa73a724b6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -81,10 +81,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added `NamedTemporaryFile` in module `tempfile`. ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) -- Added temporary `SliceNew` type with corrected behaviour from `Slice` to facilitate - an incremental internal migration due to reliance on the old, incorrect behaviour. - ([PR #2894](https://github.com/modularml/mojo/pull/2894) by [@bgreni](https://github.com/bgreni)) - - Added `String.format` method. ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 4699035970..062baf2335 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -246,210 +246,3 @@ fn slice( The constructed slice. """ return Slice(start, end, step) - - -# ===-------------------------------------------------------------------===# -# SliceNew -# ===-------------------------------------------------------------------===# - - -@register_passable("trivial") -struct SliceNew(Stringable, EqualityComparable): - """Represents a new slice implementation that corrects incorrect behaviour in the existing implementation. - """ - - var start: OptionalReg[Int] - """The starting index of the slice.""" - var end: OptionalReg[Int] - """The end index of the slice.""" - var step: Int - """The step increment value of the slice.""" - - @always_inline("nodebug") - fn __init__(inout self, start: Int, end: Int): - """Construct slice given the start and end values. - - Args: - start: The start value. - end: The end value. - """ - self.start = start - self.end = end - self.step = 1 - - @always_inline("nodebug") - fn __init__( - inout self, - start: OptionalReg[Int], - end: OptionalReg[Int], - step: OptionalReg[Int], - ): - """Construct slice given the start, end and step values. - - Args: - start: The start value. - end: The end value. - step: The step value. - """ - self.start = start - self.end = end - - if step and step.value() == 0: - abort("Cannot build slice with a step of 0") - - self.step = step.value() if step else 1 - - fn __str__(self) -> String: - """Gets the string representation of the span. - - Returns: - The string representation of the span. - """ - var res = str(self.start.value()) if self.start else "" - res += ":" - if self.end: - res += str(self.end.value()) if self.end else "" - res += ":" - res += str(self.step) - return res - - @always_inline("nodebug") - fn __eq__(self, other: Self) -> Bool: - """Compare this slice to the other. - - Args: - other: The slice to compare to. - - Returns: - True if start, end, and step values of this slice match the - corresponding values of the other slice and False otherwise. - """ - return ( - _compare_optional(self.start, other.start) - and _compare_optional(self.end, other.end) - and self.step == other.step - ) - - @always_inline("nodebug") - fn __ne__(self, other: Self) -> Bool: - """Compare this slice to the other. - - Args: - other: The slice to compare to. - - Returns: - False if start, end, and step values of this slice match the - corresponding values of the other slice and True otherwise. - """ - return not (self == other) - - @always_inline - fn unsafe_indices(self) -> Int: - """Return the length of the slice. - - Only use this function if start/end is guaranteed to be not None. - - Returns: - The length of the slice. - """ - - return len(range(self.start.value(), self.end.value(), self.step)) - - fn indices(self, src_len: Int) -> (Int, Int, Int): - """Returns a tuple of 3 intergers representing the start, end, and step - of the slice if applied to a container of the given length. - - Uses the target container length to normalize negative, out of bounds, or None indices. - - Negative indices are wrapped using the length of the container. - ```mojo - s = slice_new(0, -1, 1) - s.indices(5) # returns (0, 4, 1) - ``` - - None indices are defaulted to the start or the end of the container based - on whether `step` is positive or negative. - ```mojo - s = slice_new(None, None, 1) - s.indices(5) # returns (0, 5, 1) - ``` - - Out of bounds indices are clamped using the size of the container. - ```mojo - s = slice_new(20) - s.indices(5) # returns (0, 5, 1) - ``` - - Args: - src_len: The length of the target container. - - Returns: - A tuple containing three integers for start, end, and step. - """ - var start = self.start - var end = self.end - var positive_step = self.step > 0 - - if not start: - start = 0 if positive_step else src_len - 1 - elif start.value() < 0: - start = start.value() + src_len - if start.value() < 0: - start = 0 if positive_step else -1 - elif start.value() >= src_len: - start = src_len if positive_step else src_len - 1 - - if not end: - end = src_len if positive_step else -1 - elif end.value() < 0: - end = end.value() + src_len - if end.value() < 0: - end = 0 if positive_step else -1 - elif end.value() >= src_len: - end = src_len if positive_step else src_len - 1 - - return (start.value(), end.value(), self.step) - - -@always_inline("nodebug") -fn slice_new(end: Int) -> SliceNew: - """Construct slice given the end value. - - Args: - end: The end value. - - Returns: - The constructed slice. - """ - return SliceNew(0, end) - - -@always_inline("nodebug") -fn slice_new(start: Int, end: Int) -> SliceNew: - """Construct slice given the start and end values. - - Args: - start: The start value. - end: The end value. - - Returns: - The constructed slice. - """ - return SliceNew(start, end) - - -@always_inline("nodebug") -fn slice_new( - start: OptionalReg[Int], end: OptionalReg[Int], step: OptionalReg[Int] -) -> SliceNew: - """Construct a Slice given the start, end and step values. - - Args: - start: The start value. - end: The end value. - step: The step value. - - Returns: - The constructed slice. - """ - return SliceNew(start, end, step) diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index c99227fd38..c2f5e2a593 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -137,69 +137,10 @@ def test_indexing(): assert_equal(s[2], 3) -def test_slice_indices_new(): - var start: Int - var end: Int - var step: Int - var s = slice_new(1, 10) - start, end, step = s.indices(9) - assert_equal(slice_new(start, end, step), slice_new(1, 9)) - s = slice_new(1, None, 1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(1, 5)) - s = slice_new(1, None, -1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(1, -1, -1)) - s = slice_new(-1, None, 1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(4, 5, 1)) - s = slice_new(None, 2, 1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(0, 2, 1)) - s = slice_new(None, 2, -1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(4, 2, -1)) - s = slice_new(0, -1, 1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(0, 4, 1)) - s = slice_new(None, None, 1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(0, 5, 1)) - s = slice_new(20) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(0, 5, 1)) - s = slice_new(10, -10, 1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(5, 0, 1)) - # FIXME(#38392) - # assert_equal(len(range(start, end, step)), 0) - s = slice_new(-12, -10, -1) - start, end, step = s.indices(5) - assert_equal(slice_new(start, end, step), slice_new(-1, -1, -1)) - assert_equal(len(range(start, end, step)), 0) - - -def test_slice_eq_new(): - assert_equal(slice_new(1, 2, 3), slice_new(1, 2, 3)) - assert_equal(slice_new(0, 1), slice_new(1)) - assert_true(slice_new(2, 3) != slice_new(4, 5)) - assert_equal(slice_new(1, None, 1), slice_new(1, None, None)) - - -def test_none_end_folds_new(): - alias all_def_slice = slice_new(0, None, 1) - assert_equal(all_def_slice.start.value(), 0) - assert_true(all_def_slice.end is None) - assert_equal(all_def_slice.step, 1) - - def main(): test_none_end_folds() test_slicable() test_slice_stringable() test_indexing() - test_slice_indices_new() - test_slice_eq_new() - test_none_end_folds_new() test_slice_eq() test_slice_adjust() From 02491877294b8ddae96400513a6cc4cec1e0d7d4 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 11 Jun 2024 20:02:40 -0500 Subject: [PATCH 0934/2019] [External] [stdlib] Use autoderef in `Dict.__get_ref()` (#41737) [External] [stdlib] Use autoderef in `Dict.__get_ref()` We'll make `__getitem__` autoderef soon after the compiler understands that `my_dict["value"] = x` should call `__setitem__` and not `__getitem__`. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3011 MODULAR_ORIG_COMMIT_REV_ID: 2d65d11dafc000a6e4455aa8520680931744db47 --- stdlib/src/collections/dict.mojo | 10 +++++----- stdlib/test/collections/test_dict.mojo | 13 +++++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 875e03eb1e..8cb3a8e3f6 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -522,12 +522,12 @@ struct Dict[K: KeyElement, V: CollectionElement]( Raises: "KeyError" if the key isn't present. """ - return self._find_ref(key)[] + return self._find_ref(key) # TODO(MSTDL-452): rename to __getitem__ returning a reference fn __get_ref( ref [_]self: Self, key: K - ) raises -> Reference[V, __lifetime_of(self)]: + ) raises -> ref [__lifetime_of(self)] Self.V: """Retrieve a value out of the dictionary. Args: @@ -692,14 +692,14 @@ struct Dict[K: KeyElement, V: CollectionElement]( otherwise an empty Optional. """ try: # TODO(MOCO-604): push usage through - return self._find_ref(key)[] + return self._find_ref(key) except: return None # TODO(MOCO-604): Return Optional[Reference] instead of raising fn _find_ref( ref [_]self: Self, key: K - ) raises -> Reference[V, __lifetime_of(self)]: + ) raises -> ref [__lifetime_of(self)] Self.V: """Find a value in the dictionary by key. Args: @@ -717,7 +717,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( if found: var entry = self._entries.__get_ref(index) debug_assert(entry[].__bool__(), "entry in index must be full") - return Reference(entry[].value().value) + return entry[].value().value raise "KeyError" fn get(self, key: K) -> Optional[V]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index a8f38a81f2..2bbf739790 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -66,6 +66,15 @@ def test_basic(): assert_equal(2, dict["b"]) +def test_basic_no_copies(): + var dict = Dict[String, Int]() + dict["a"] = 1 + dict["b"] = 2 + + assert_equal(1, dict["a"]) + assert_equal(2, dict["b"]) + + def test_multiple_resizes(): var dict = Dict[String, Int]() for i in range(20): @@ -274,8 +283,8 @@ def test_dict_copy_calls_copy_constructor(): # are coming from :) assert_equal(1, orig["a"].copy_count) assert_equal(2, copy["a"].copy_count) - assert_equal(0, orig.__get_ref("a")[].copy_count) - assert_equal(1, copy.__get_ref("a")[].copy_count) + assert_equal(0, orig.__get_ref("a").copy_count) + assert_equal(1, copy.__get_ref("a").copy_count) def test_dict_update_nominal(): From bea6730ecc51aa455f8a4904286461498cad5fa5 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 11 Jun 2024 20:03:01 -0500 Subject: [PATCH 0935/2019] [External] [stdlib] Use autoderef in `List.unsafe_get()` (#41736) [External] [stdlib] Use autoderef in `List.unsafe_get()` Use autoderef in `List.unsafe_get()` which cleans up the call sites a little bit. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3012 MODULAR_ORIG_COMMIT_REV_ID: 1f37adf192dcc83639afc6e8487cd86d652009c7 --- stdlib/src/builtin/string.mojo | 8 ++++---- stdlib/src/collections/list.mojo | 2 +- stdlib/test/collections/test_list.mojo | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5b8d989fb4..7eaf7e2ead 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1676,7 +1676,7 @@ struct String( # Python adds all "whitespace chars" as one separator # if no separator was specified while lhs <= str_iter_len: - if not _isspace(self._buffer.unsafe_get(lhs)[]): + if not _isspace(self._buffer.unsafe_get(lhs)): break lhs += 1 # if it went until the end of the String, then @@ -1690,7 +1690,7 @@ struct String( break rhs = lhs + 1 while rhs <= str_iter_len: - if _isspace(self._buffer.unsafe_get(rhs)[]): + if _isspace(self._buffer.unsafe_get(rhs)): break rhs += 1 @@ -1847,7 +1847,7 @@ struct String( """ # TODO: should use self.__iter__ and self.isspace() var r_idx = len(self) - while r_idx > 0 and _isspace(self._buffer.unsafe_get(r_idx - 1)[]): + while r_idx > 0 and _isspace(self._buffer.unsafe_get(r_idx - 1)): r_idx -= 1 return self[:r_idx] @@ -1875,7 +1875,7 @@ struct String( """ # TODO: should use self.__iter__ and self.isspace() var l_idx = 0 - while l_idx < len(self) and _isspace(self._buffer.unsafe_get(l_idx)[]): + while l_idx < len(self) and _isspace(self._buffer.unsafe_get(l_idx)): l_idx += 1 return self[l_idx:] diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 12c3257145..937a5365dd 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -770,7 +770,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): @always_inline fn unsafe_get( ref [_]self: Self, idx: Int - ) -> Reference[Self.T, __lifetime_of(self)]: + ) -> ref [__lifetime_of(self)] Self.T: """Get a reference to an element of self without checking index bounds. Users should consider using `__getitem__` instead of this method as it diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index f2ff58397c..9b0c5c9b2e 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -67,18 +67,18 @@ def test_list_unsafe_get(): list.append(i) assert_equal(5, len(list)) - assert_equal(0, list.unsafe_get(0)[]) - assert_equal(1, list.unsafe_get(1)[]) - assert_equal(2, list.unsafe_get(2)[]) - assert_equal(3, list.unsafe_get(3)[]) - assert_equal(4, list.unsafe_get(4)[]) + assert_equal(0, list.unsafe_get(0)) + assert_equal(1, list.unsafe_get(1)) + assert_equal(2, list.unsafe_get(2)) + assert_equal(3, list.unsafe_get(3)) + assert_equal(4, list.unsafe_get(4)) list[2] = -2 - assert_equal(-2, list.unsafe_get(2)[]) + assert_equal(-2, list.unsafe_get(2)) list.clear() list.append(2) - assert_equal(2, list.unsafe_get(0)[]) + assert_equal(2, list.unsafe_get(0)) def test_list_unsafe_set(): From 15c347c8549ed3f0b2a1b444fbf2c05db85355a9 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 11 Jun 2024 20:04:39 -0500 Subject: [PATCH 0936/2019] [External] [stdlib] Add method `UInt.__repr__()` (#41739) [External] [stdlib] Add method `UInt.__repr__()` * https://github.com/modularml/mojo/issues/2919 I also added the Stringable trait, which was forgotten at the last PR. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3014 MODULAR_ORIG_COMMIT_REV_ID: bb10a17d396d6419c6ab72e80129b6bc1a76e090 --- stdlib/src/builtin/uint.mojo | 19 +++++++++++++++++-- stdlib/test/builtin/test_uint.mojo | 10 ++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index ad894f98b4..8776dcfc9d 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -19,7 +19,7 @@ These are Mojo built-ins, so you don't need to import them. @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct UInt: +struct UInt(Stringable, Representable): """This type represents an unsigned integer. An unsigned integer is represents a positive integral number. @@ -81,6 +81,21 @@ struct UInt: ``` Returns: - The string representation of this Int. + The string representation of this UInt. """ return str(UInt64(self)) + + @always_inline("nodebug") + fn __repr__(self) -> String: + """Convert this UInt to a string. + + A small example. + ```mojo + var x = UInt(50) + var x_as_string = repr(x) # x_as_string = "UInt(50)" + ``` + + Returns: + The string representation of this UInt. + """ + return "UInt(" + str(self) + ")" diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index d3180c285c..f2288ce32b 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -29,5 +29,15 @@ def test_simple_uint(): assert_equal(str(UInt(18446744073709551615)), "18446744073709551615") +def test_uint_representation(): + assert_equal(repr(UInt(32)), "UInt(32)") + + assert_equal(repr(UInt(0)), "UInt(0)") + assert_equal(repr(UInt()), "UInt(0)") + + assert_equal(repr(UInt(18446744073709551615)), "UInt(18446744073709551615)") + + def main(): test_simple_uint() + test_uint_representation() From 81d3c7152352e729089d1f9943a636d78d30d441 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Wed, 12 Jun 2024 08:37:15 -0500 Subject: [PATCH 0937/2019] [External] [stdlib] Fix Span slicing behaviour (#41740) [External] [stdlib] Fix Span slicing behaviour - Allow more sophisticated slicing behaviour in `Span` by storing the currently active slice inside the span - Alter `String` constructor from `StringSlice` to copy in a for loop instead of `memcpy` to correct behaviour when the underlying `Span` has been sliced Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2950 MODULAR_ORIG_COMMIT_REV_ID: e7838a11398bdaba997f2a282b8c5439a8951cbd --- stdlib/src/builtin/string.mojo | 11 ++-- stdlib/src/utils/span.mojo | 35 +++++++++---- stdlib/test/utils/test_span.mojo | 65 +++++++++++++++++++----- stdlib/test/utils/test_string_slice.mojo | 5 +- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 7eaf7e2ead..14fc81ebb7 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -847,15 +847,14 @@ struct String( """ # Calculate length in bytes - var length: Int = len(str_slice.as_bytes_slice()) + var bytes = str_slice.as_bytes_slice() + var length: Int = len(bytes) var buffer = Self._buffer_type() # +1 for null terminator, initialized to 0 buffer.resize(length + 1, 0) - memcpy( - dest=buffer.data, - src=str_slice.as_bytes_slice().unsafe_ptr(), - count=length, - ) + for i in range(length): + buffer[i] = bytes[i] + self = Self(buffer^) @always_inline diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 8a5d531d7b..ce7e843db4 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -86,21 +86,30 @@ struct Span[ # Field var _data: UnsafePointer[T] var _len: Int + var _slice: Slice # ===------------------------------------------------------------------===# # Life cycle methods # ===------------------------------------------------------------------===# @always_inline - fn __init__(inout self, *, unsafe_ptr: UnsafePointer[T], len: Int): + fn __init__( + inout self, + *, + unsafe_ptr: UnsafePointer[T], + len: Int, + slc: Optional[Slice] = None, + ): """Unsafe construction from a pointer and length. Args: unsafe_ptr: The underlying pointer of the span. len: The length of the view. + slc: The slice of we want to use of the source data. """ self._data = unsafe_ptr self._len = len + self._slice = slc.value() if slc else Slice(0, self._len, 1) @always_inline fn __init__(inout self, *, other: Self): @@ -111,21 +120,29 @@ struct Span[ """ self._data = other._data self._len = other._len + self._slice = other._slice - @always_inline - fn __init__(inout self, ref [lifetime]list: List[T]): + fn __init__( + inout self, ref [lifetime]list: List[T], slc: Optional[Slice] = None + ): """Construct a Span from a List. Args: list: The list to which the span refers. + slc: The slice of we want to use of the source data. """ self._data = list.data self._len = len(list) + self._slice = slc.value() if slc else Slice(0, self._len, 1) @always_inline fn __init__[ T2: CollectionElementNew, size: Int - ](inout self, ref [lifetime]array: InlineArray[T2, size]): + ]( + inout self, + ref [lifetime]array: InlineArray[T2, size], + slc: Optional[Slice] = None, + ): """Construct a Span from an InlineArray. Parameters: @@ -134,12 +151,14 @@ struct Span[ Args: array: The array to which the span refers. + slc: The slice of we want to use of the source data. """ constrained[_type_is_eq[T, T2](), "array element is not Span.T"]() self._data = UnsafePointer.address_of(array).bitcast[T]() self._len = size + self._slice = slc.value() if slc else Slice(0, self._len, 1) # ===------------------------------------------------------------------===# # Operator dunders @@ -163,7 +182,7 @@ struct Span[ var offset = idx if offset < 0: offset += len(self) - return self._data[offset] + return self._data[self._slice[offset]] @always_inline fn __getitem__(self, slc: Slice) -> Self: @@ -179,12 +198,10 @@ struct Span[ var end: Int var step: Int start, end, step = slc.indices(len(self)) - debug_assert( - step == 1, "Slice must be within bounds and step must be 1" - ) var res = Self( - unsafe_ptr=(self._data + start), + unsafe_ptr=self._data, len=len(range(start, end, step)), + slc=Slice(start, end, step), ) return res diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 2292a9d707..7e1fa3ea1b 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -121,26 +121,62 @@ def test_indexing(): def test_span_slice(): - def compare(s: Span[Int], l: List[Int]) -> Bool: - if len(s) != len(l): - return False - for i in range(len(s)): - if s[i] != l[i]: - return False - return True - var l = List(1, 2, 3, 4, 5) var s = Span(l) - var res = s[1:2] + var res = s[Slice(1, 2)] assert_equal(res[0], 2) - res = s[1:-1:1] + + res = s[Slice(1, -1, 1)] assert_equal(res[0], 2) assert_equal(res[1], 3) assert_equal(res[2], 4) - # TODO: Fix Span slicing - # res = s[1::-1] - # assert_equal(res[0], 2) - # assert_equal(res[1], 1) + + res = s[1::-1] + assert_equal(res[0], 2) + assert_equal(res[1], 1) + + res = s[1:6:2] + assert_equal(len(res), 2) + assert_equal(res[0], 2) + assert_equal(res[1], 4) + + res = s[-1:-4:-1] + assert_equal(len(res), 3) + assert_equal(res[0], 5) + assert_equal(res[1], 4) + assert_equal(res[2], 3) + + res = s[1:-1] + assert_equal(len(res), 3) + assert_equal(res[0], 2) + assert_equal(res[1], 3) + assert_equal(res[2], 4) + + res = s[3:2] + assert_equal(len(res), 0) + + var str_l = String("[1,2,3,4]") + var str_span = str_l.as_bytes_slice() + var sliced = str_span[1:-1] + assert_equal(len(sliced), 7) + assert_equal(chr(int(sliced[0])), "1") + assert_equal(chr(int(sliced[1])), ",") + assert_equal(chr(int(sliced[2])), "2") + assert_equal(chr(int(sliced[3])), ",") + assert_equal(chr(int(sliced[4])), "3") + assert_equal(chr(int(sliced[5])), ",") + assert_equal(chr(int(sliced[6])), "4") + + +def test_span_iter(): + var l = List(1, 2, 3, 4, 5) + var s = Span(l) + var it = s[1:6:2].__iter__() + assert_equal(len(it), 2) + var first = it.__next__() + assert_equal(first[], 2) + var second = it.__next__() + assert_equal(second[], 4) def main(): @@ -150,3 +186,4 @@ def main(): test_span_array_str() test_indexing() test_span_slice() + test_span_iter() diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 41057780b0..df727ac79b 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_not_equal from utils import Span @@ -86,7 +86,8 @@ fn test_string_byte_slice() raises: assert_equal(len(sub5), 0) # Empty slices still have a pointer value - assert_equal(int(sub5.unsafe_ptr()) - int(sub4.unsafe_ptr()), 2) + assert_not_equal(int(sub4.unsafe_ptr()), 0) + assert_not_equal(int(sub5.unsafe_ptr()), 0) # ---------------------------------- # Test invalid slicing From 6418f713252421ff564461b56f6c62befa3e9681 Mon Sep 17 00:00:00 2001 From: Terrance Williams Date: Wed, 12 Jun 2024 08:45:23 -0500 Subject: [PATCH 0938/2019] [External] [stdlib] Fixed recursive calls to String.__getitem__ for negative idxs (#41741) [External] [stdlib] Fixed recursive calls to String.__getitem__ for negative idxs Currently, `String.__getitem__(self, idx: Int)` has a behavior in which really large indices result in many recursive calls, causing the REPL to hang. Given, ```mojo 1> var s: String = "there" 2. print(s[-1000000000000000]) 3. ``` The REPL hangs. At the time of writing, it is still hanging after an hour. Co-authored-by: Terrance Williams Closes modularml/mojo#2952 MODULAR_ORIG_COMMIT_REV_ID: a63131763b4d0038694d76471dc64ac4fd8e3640 --- stdlib/src/builtin/string.mojo | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 14fc81ebb7..f141f67336 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from bit import countl_zero from collections import List, KeyElement +from collections._index_normalization import normalize_index from sys import llvm_intrinsic, bitwidthof from sys.ffi import C_char @@ -1010,21 +1011,21 @@ struct String( # Operator dunders # ===------------------------------------------------------------------=== # - fn __getitem__(self, idx: Int) -> String: + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> String: """Gets the character at the specified position. + Parameters: + IndexerType: The inferred type of an indexer argument. + Args: idx: The index value. Returns: A new string containing the character at the specified position. """ - if idx < 0: - return self.__getitem__(len(self) + idx) - - debug_assert(0 <= idx < len(self), "index must be in range") + var normalized_idx = normalize_index["String"](idx, self) var buf = Self._buffer_type(capacity=1) - buf.append(self._buffer[idx]) + buf.append(self._buffer[normalized_idx]) buf.append(0) return String(buf^) From f49d41e7c5df3fa9f623ca719bb4d8756f728ef7 Mon Sep 17 00:00:00 2001 From: codingonion Date: Wed, 12 Jun 2024 09:10:16 -0500 Subject: [PATCH 0939/2019] [External] [docs] Fix typo in functions.ipynb (#41743) [External] [docs] Fix typo in functions.ipynb Fix typo in functions.ipynb Co-authored-by: codingonion Closes modularml/mojo#3021 MODULAR_ORIG_COMMIT_REV_ID: ca77a8300d41e226d88ac7e2d140378bba4a159a --- docs/manual/functions.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 docs/manual/functions.ipynb diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb old mode 100644 new mode 100755 index 5cd00427b9..541af41f3c --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -165,7 +165,7 @@ " `def` function doesn't declare a return type of `None`, it's considered to\n", " return an `object` by default.)\n", "\n", - "- Arguments are mutable. Arguments default to using the using the `borrowed` \n", + "- Arguments are mutable. Arguments default to using the `borrowed` \n", " [argument convention](/mojo/manual/values/ownership#argument-conventions))\n", " like an `fn` function, with a special addition: if the function mutates the\n", " argument, it makes a mutable copy. \n", From 191668f0fcb0e2f9fe41063d6c7c4fa7446bf8bb Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 12 Jun 2024 17:10:48 +0300 Subject: [PATCH 0940/2019] [stdlib] Change minimum macos version in a unit test It's unclear why this unit test was using 13 as the minimum macOS version, but this kept failing on my machine. The test should probably be improved but this at least alleviates the pain. MODULAR_ORIG_COMMIT_REV_ID: e8d28406082d556c17400d4540026013b7d0e913 --- stdlib/test/sys/test_macos_target.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 1337bb92ad..1d07b467e4 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -52,7 +52,7 @@ def test_os_version(): major, minor, patch = _macos_version() - assert_true(major >= 13) + assert_true(major >= 12) assert_true(minor >= 0) assert_true(patch >= 0) From 12bb9a3ce2c4b7ef66f3cc948225a8cdd7589680 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:17:22 -0500 Subject: [PATCH 0941/2019] [External] [stdlib] Rename `Arc.as_ptr()` to `Arc.unsafe_ptr()` and document safe dereferencing (`[]`) (#41745) [External] [stdlib] Rename `Arc.as_ptr()` to `Arc.unsafe_ptr()` and document safe dereferencing (`[]`) Hello, here is a patch toward removing a foot gunner for users. Mojo's ASAP deletion policy works really well, users might unsafely dereference the `Arc._inner` field, only to find out that mojo already freed the pointer ! :rocket: We might understand and love ASAP, but users need :shield: safeguards ! With this patch, people are encouraged to use the `Lifetime` dereference subscript `[]`. It also includes further educating by expanding the documentation of `Arc` . Left a `TODO: remove this method` on `Arc.unsafe_ptr()`, in order to not forget. it is recommended to consider removing this method. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3003 MODULAR_ORIG_COMMIT_REV_ID: a9a46da2215e65981c052b9c79ac2036290c778b --- stdlib/src/memory/arc.mojo | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index f199484c98..20e4b7c171 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -18,9 +18,30 @@ Example usage: from memory import Arc var p = Arc(4) var p2 = p -p2.set(3) -print(3 == p.get()) +p2[]=3 +print(3 == p[]) ``` + +Subscripting(`[]`) is done by `Reference`, +in order to ensure that the underlying `Arc` outlive the operation. + +It is highly DISCOURAGED to manipulate an `Arc` trough `UnsafePointer`. +Mojo's ASAP deletion policy ensure values are destroyed at last use. +Do not unsafely dereference the `Arc` inner `UnsafePointer` field. +See [Lifecycle](https://docs.modular.com/mojo/manual/lifecycle/). + +```mojo +# Illustration of what NOT to do, in order to understand: +print(Arc(String("ok"))._inner[].payload) +#........................^ASAP ^already freed +``` + +Always use `Reference` subscripting (`[]`): + +```mojo +print(Arc(String("ok"))[]) +``` + """ from os.atomic import Atomic @@ -122,10 +143,11 @@ struct Arc[T: Movable](CollectionElement): """ return self._inner[].payload - fn as_ptr(self) -> UnsafePointer[T]: + fn unsafe_ptr(self) -> UnsafePointer[T]: """Retrieves a pointer to the underlying memory. Returns: The UnsafePointer to the underlying memory. """ + # TODO: consider removing this method. return UnsafePointer.address_of(self._inner[].payload) From f363c0fa56ba5702f0d4f98ce861dd2e5735119e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 12 Jun 2024 17:21:13 +0300 Subject: [PATCH 0942/2019] [stdlib] Add `round` overload for `FloatLiteral` There seems to be a bug that forces materialization for `FloatLiteral` when the `round` method is called on it. This patch adds explicit overloads of `round` to work around this. It also makes the existing `round` functions have the same argument names as their Python counterparts. MODULAR_ORIG_COMMIT_REV_ID: 5a27ea0e90613620c471f4a4f179cfb7f8c08b78 --- stdlib/src/builtin/math.mojo | 41 +++++++++++++++++++++++++----- stdlib/test/builtin/test_math.mojo | 6 +++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index b88eac3e22..e8e3eb5d90 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -299,33 +299,62 @@ trait Roundable: @always_inline -fn round[T: Roundable](value: T) -> T: +fn round[T: Roundable, //](number: T) -> T: """Get the rounded value of the given object. Parameters: T: The type conforming to Roundable. Args: - value: The object to get the rounded value of. + number: The object to get the rounded value of. Returns: The rounded value of the object. """ - return value.__round__() + return number.__round__() +# TODO: remove this when conformance issue for FloatLiteral is fixed. @always_inline -fn round[T: Roundable](value: T, ndigits: Int) -> T: +fn round(number: FloatLiteral) -> FloatLiteral: + """Get the rounded value of the given FloatLiteral. + + Args: + number: The FloatLiteral to get the rounded value of. + + Returns: + The rounded value of the object. + """ + return number.__round__() + + +@always_inline +fn round[T: Roundable, //](number: T, ndigits: Int) -> T: """Get the rounded value of the given object. Parameters: T: The type conforming to Roundable. Args: - value: The object to get the rounded value of. + number: The object to get the rounded value of. + ndigits: The number of digits to round to. + + Returns: + The rounded value of the object. + """ + return number.__round__(ndigits) + + +# TODO: remove this when conformance issue for FloatLiteral is fixed. +@always_inline +fn round(number: FloatLiteral, ndigits: Int) -> FloatLiteral: + """Get the rounded value of the given FloatLiteral. + + Args: + number: The FloatLiteral to get the rounded value of. ndigits: The number of digits to round to. Returns: The rounded value of the object. """ - return value.__round__(ndigits) + return number.__round__(ndigits) diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 4dd0684b6b..b474be6978 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -80,6 +80,12 @@ def test_round(): var expected = SIMD[DType.float32, 4](1.0, 2.0, 2.0, 2.0) assert_equal(expected, round(lhs)) + # Ensure that round works on float literal + alias r1: FloatLiteral = round(2.3) + assert_equal(r1, 2.0) + alias r2: FloatLiteral = round(2.3324, 2) + assert_equal(r2, 2.33) + def test_pow(): alias F = SIMD[DType.float32, 4] From bd57d0dbc5a33db481c459444c439b39c70ea3a2 Mon Sep 17 00:00:00 2001 From: Denali Lumma Date: Wed, 12 Jun 2024 13:43:10 -0700 Subject: [PATCH 0943/2019] Revert "[External] [stdlib] Fix Span slicing behaviour" (#41756) Reverts modularml/modular#41740 Causing deterministic failures in pre and post submit. MODULAR_ORIG_COMMIT_REV_ID: 86cb5be58a65e37b265575e440a6a5255e2928ae --- stdlib/src/builtin/string.mojo | 11 ++-- stdlib/src/utils/span.mojo | 35 ++++--------- stdlib/test/utils/test_span.mojo | 65 +++++------------------- stdlib/test/utils/test_string_slice.mojo | 5 +- 4 files changed, 31 insertions(+), 85 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index f141f67336..30b452cb6f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -848,14 +848,15 @@ struct String( """ # Calculate length in bytes - var bytes = str_slice.as_bytes_slice() - var length: Int = len(bytes) + var length: Int = len(str_slice.as_bytes_slice()) var buffer = Self._buffer_type() # +1 for null terminator, initialized to 0 buffer.resize(length + 1, 0) - for i in range(length): - buffer[i] = bytes[i] - + memcpy( + dest=buffer.data, + src=str_slice.as_bytes_slice().unsafe_ptr(), + count=length, + ) self = Self(buffer^) @always_inline diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index ce7e843db4..8a5d531d7b 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -86,30 +86,21 @@ struct Span[ # Field var _data: UnsafePointer[T] var _len: Int - var _slice: Slice # ===------------------------------------------------------------------===# # Life cycle methods # ===------------------------------------------------------------------===# @always_inline - fn __init__( - inout self, - *, - unsafe_ptr: UnsafePointer[T], - len: Int, - slc: Optional[Slice] = None, - ): + fn __init__(inout self, *, unsafe_ptr: UnsafePointer[T], len: Int): """Unsafe construction from a pointer and length. Args: unsafe_ptr: The underlying pointer of the span. len: The length of the view. - slc: The slice of we want to use of the source data. """ self._data = unsafe_ptr self._len = len - self._slice = slc.value() if slc else Slice(0, self._len, 1) @always_inline fn __init__(inout self, *, other: Self): @@ -120,29 +111,21 @@ struct Span[ """ self._data = other._data self._len = other._len - self._slice = other._slice - fn __init__( - inout self, ref [lifetime]list: List[T], slc: Optional[Slice] = None - ): + @always_inline + fn __init__(inout self, ref [lifetime]list: List[T]): """Construct a Span from a List. Args: list: The list to which the span refers. - slc: The slice of we want to use of the source data. """ self._data = list.data self._len = len(list) - self._slice = slc.value() if slc else Slice(0, self._len, 1) @always_inline fn __init__[ T2: CollectionElementNew, size: Int - ]( - inout self, - ref [lifetime]array: InlineArray[T2, size], - slc: Optional[Slice] = None, - ): + ](inout self, ref [lifetime]array: InlineArray[T2, size]): """Construct a Span from an InlineArray. Parameters: @@ -151,14 +134,12 @@ struct Span[ Args: array: The array to which the span refers. - slc: The slice of we want to use of the source data. """ constrained[_type_is_eq[T, T2](), "array element is not Span.T"]() self._data = UnsafePointer.address_of(array).bitcast[T]() self._len = size - self._slice = slc.value() if slc else Slice(0, self._len, 1) # ===------------------------------------------------------------------===# # Operator dunders @@ -182,7 +163,7 @@ struct Span[ var offset = idx if offset < 0: offset += len(self) - return self._data[self._slice[offset]] + return self._data[offset] @always_inline fn __getitem__(self, slc: Slice) -> Self: @@ -198,10 +179,12 @@ struct Span[ var end: Int var step: Int start, end, step = slc.indices(len(self)) + debug_assert( + step == 1, "Slice must be within bounds and step must be 1" + ) var res = Self( - unsafe_ptr=self._data, + unsafe_ptr=(self._data + start), len=len(range(start, end, step)), - slc=Slice(start, end, step), ) return res diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 7e1fa3ea1b..2292a9d707 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -121,62 +121,26 @@ def test_indexing(): def test_span_slice(): + def compare(s: Span[Int], l: List[Int]) -> Bool: + if len(s) != len(l): + return False + for i in range(len(s)): + if s[i] != l[i]: + return False + return True + var l = List(1, 2, 3, 4, 5) var s = Span(l) - var res = s[Slice(1, 2)] + var res = s[1:2] assert_equal(res[0], 2) - - res = s[Slice(1, -1, 1)] + res = s[1:-1:1] assert_equal(res[0], 2) assert_equal(res[1], 3) assert_equal(res[2], 4) - - res = s[1::-1] - assert_equal(res[0], 2) - assert_equal(res[1], 1) - - res = s[1:6:2] - assert_equal(len(res), 2) - assert_equal(res[0], 2) - assert_equal(res[1], 4) - - res = s[-1:-4:-1] - assert_equal(len(res), 3) - assert_equal(res[0], 5) - assert_equal(res[1], 4) - assert_equal(res[2], 3) - - res = s[1:-1] - assert_equal(len(res), 3) - assert_equal(res[0], 2) - assert_equal(res[1], 3) - assert_equal(res[2], 4) - - res = s[3:2] - assert_equal(len(res), 0) - - var str_l = String("[1,2,3,4]") - var str_span = str_l.as_bytes_slice() - var sliced = str_span[1:-1] - assert_equal(len(sliced), 7) - assert_equal(chr(int(sliced[0])), "1") - assert_equal(chr(int(sliced[1])), ",") - assert_equal(chr(int(sliced[2])), "2") - assert_equal(chr(int(sliced[3])), ",") - assert_equal(chr(int(sliced[4])), "3") - assert_equal(chr(int(sliced[5])), ",") - assert_equal(chr(int(sliced[6])), "4") - - -def test_span_iter(): - var l = List(1, 2, 3, 4, 5) - var s = Span(l) - var it = s[1:6:2].__iter__() - assert_equal(len(it), 2) - var first = it.__next__() - assert_equal(first[], 2) - var second = it.__next__() - assert_equal(second[], 4) + # TODO: Fix Span slicing + # res = s[1::-1] + # assert_equal(res[0], 2) + # assert_equal(res[1], 1) def main(): @@ -186,4 +150,3 @@ def main(): test_span_array_str() test_indexing() test_span_slice() - test_span_iter() diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index df727ac79b..41057780b0 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_not_equal +from testing import assert_equal from utils import Span @@ -86,8 +86,7 @@ fn test_string_byte_slice() raises: assert_equal(len(sub5), 0) # Empty slices still have a pointer value - assert_not_equal(int(sub4.unsafe_ptr()), 0) - assert_not_equal(int(sub5.unsafe_ptr()), 0) + assert_equal(int(sub5.unsafe_ptr()) - int(sub4.unsafe_ptr()), 2) # ---------------------------------- # Test invalid slicing From d9b4c865293473e17dffad940aea905af35f79c6 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Wed, 12 Jun 2024 15:46:16 -0700 Subject: [PATCH 0944/2019] [mojo-stdlib] Use `@parameter for` in `Variant` Replaces several uses of `unroll` in `Variant` with the new `@parameter for` construct, simplifying implementation. MODULAR_ORIG_COMMIT_REV_ID: 48051ae4f08206cd091f88a2ef1d393fee8e3317 --- stdlib/src/utils/variant.mojo | 95 ++++++++++++----------------------- 1 file changed, 33 insertions(+), 62 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 25053ee165..5c832fd985 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -42,7 +42,6 @@ from sys import alignof, sizeof from sys.intrinsics import _type_is_eq from memory import UnsafePointer -from utils import unroll # ===----------------------------------------------------------------------=== # # Utilities @@ -60,38 +59,6 @@ fn _align_up(value: Int, alignment: Int) -> Int: # ===----------------------------------------------------------------------=== # -# FIXME(#27380): Can't pass *Ts to a function parameter, only type parameter. -struct _UnionSize[*Ts: CollectionElement](): - @staticmethod - fn compute() -> Int: - var size = 0 - - @parameter - fn each[i: Int](): - size = max(size, _align_up(sizeof[Ts[i]](), alignof[Ts[i]]())) - - unroll[each, len(VariadicList(Ts))]() - return _align_up(size, alignof[Int]()) - - -# FIXME(#27380): Can't pass *Ts to a function parameter, only type parameter. -struct _UnionTypeIndex[T: CollectionElement, *Ts: CollectionElement]: - @staticmethod - fn compute() -> Int8: - var result = -1 - - @parameter - fn each[i: Int](): - alias q = Ts[i] - - @parameter - if _type_is_eq[q, T](): - result = i - - unroll[each, len(VariadicList(Ts))]() - return result - - struct Variant[*Ts: CollectionElement]( CollectionElement, ExplicitlyCopyable, @@ -166,7 +133,7 @@ struct Variant[*Ts: CollectionElement]( value: The value to initialize the variant with. """ self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] - self._get_state()[] = Self._check[T]() + self._get_state() = Self._check[T]() self._get_ptr[T]().init_pointee_move(value^) fn __init__(inout self, *, other: Self): @@ -176,19 +143,13 @@ struct Variant[*Ts: CollectionElement]( other: The value to copy from. """ self = Self(unsafe_uninitialized=()) - self._get_state()[] = other._get_state()[] + self._get_state() = other._get_state() @parameter - fn each[i: Int](): - if self._get_state()[] == i: - alias T = Ts[i] - UnsafePointer.address_of(self._impl).bitcast[ - T - ]().init_pointee_move( - UnsafePointer.address_of(other._impl).bitcast[T]()[], - ) - - unroll[each, len(VariadicList(Ts))]() + for i in range(len(VariadicList(Ts))): + alias T = Ts[i] + if self._get_state() == i: + self._get_ptr[T]().init_pointee_move(other._get_ptr[T]()[]) fn __copyinit__(inout self, other: Self): """Creates a deep copy of an existing variant. @@ -207,17 +168,15 @@ struct Variant[*Ts: CollectionElement]( other: The variant to move. """ self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] - self._get_state()[] = other._get_state()[] + self._get_state() = other._get_state() @parameter - fn each[i: Int](): - if self._get_state()[] == i: - alias T = Ts[i] + for i in range(len(VariadicList(Ts))): + alias T = Ts[i] + if self._get_state() == i: # Calls the correct __moveinit__ other._get_ptr[T]().move_pointee_into(self._get_ptr[T]()) - unroll[each, len(VariadicList(Ts))]() - fn __del__(owned self): """Destroy the variant.""" self._call_correct_deleter() @@ -259,19 +218,16 @@ struct Variant[*Ts: CollectionElement]( ]() return UnsafePointer.address_of(self._impl).bitcast[T]() - fn _get_state(ref [_]self: Self) -> Reference[Int8, __lifetime_of(self)]: + fn _get_state(ref [_]self: Self) -> ref [__lifetime_of(self)] Int8: var int8_self = UnsafePointer.address_of(self).bitcast[Int8]() - return (int8_self + _UnionSize[Ts].compute())[] + return (int8_self + Self._size())[] @always_inline fn _call_correct_deleter(inout self): @parameter - fn each[i: Int](): - if self._get_state()[] == i: - alias q = Ts[i] - self._get_ptr[q]().destroy_pointee() - - unroll[each, len(VariadicList(Ts))]() + for i in range(len(VariadicList(Ts))): + if self._get_state() == i: + self._get_ptr[Ts[i]]().destroy_pointee() @always_inline fn take[T: CollectionElement](inout self) -> T: @@ -313,7 +269,7 @@ struct Variant[*Ts: CollectionElement]( """ debug_assert(self.isa[T](), "taking wrong type") # don't call the variant's deleter later - self._get_state()[] = Self._sentinel + self._get_state() = Self._sentinel return self._get_ptr[T]().take_pointee() @always_inline @@ -396,7 +352,7 @@ struct Variant[*Ts: CollectionElement]( True if the variant contains the requested type. """ alias idx = Self._check[T]() - return self._get_state()[] == idx + return self._get_state() == idx fn unsafe_get[ T: CollectionElement @@ -422,4 +378,19 @@ struct Variant[*Ts: CollectionElement]( @staticmethod fn _check[T: CollectionElement]() -> Int8: - return _UnionTypeIndex[T, Ts].compute() + var result = -1 + + @parameter + for i in range(len(VariadicList(Ts))): + if _type_is_eq[Ts[i], T](): + result = i + return result + + @staticmethod + fn _size() -> Int: + var size = 0 + + @parameter + for i in range(len(VariadicList(Ts))): + size = max(size, _align_up(sizeof[Ts[i]](), alignof[Ts[i]]())) + return _align_up(size, alignof[Int]()) From af04ba275be8b4a875cf66c29bf72bfd47d66877 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 13 Jun 2024 02:13:37 +0300 Subject: [PATCH 0945/2019] [stdlib][NFC] Tighten `Int` and `{Int,Float}Literal` unit tests To avoid unintentional implicit conversions, these numeric types should test their dunder methods directly. This patch fixes their unit tests to conform to this. As a result of this, some tests turned out to be not testing the literal types' methods; these (and some other unnecessary tests) are removed. MODULAR_ORIG_COMMIT_REV_ID: abc5b5685d9fadd517c42ec389a62cae612f981a --- stdlib/test/builtin/test_float_literal.mojo | 55 +++++--------- stdlib/test/builtin/test_int.mojo | 82 +++++++++++---------- stdlib/test/builtin/test_int_literal.mojo | 54 ++++++++------ 3 files changed, 94 insertions(+), 97 deletions(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 4a8373a8d8..3991a0a4ec 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -104,45 +104,31 @@ def test_round(): assert_equal(FloatLiteral.__round__(123.456, -3), 0.0) -fn round10(x: Float64) -> Float64: - # TODO: implement __div__ on FloatLiteral? - return (round(Float64(x * 10)) / 10).value +fn round10(x: FloatLiteral) -> FloatLiteral: + return round(x * 10.0) / 10.0 def test_round10(): - assert_equal(round10(4.4 % 0.5), 0.4) - assert_equal(round10(-4.4 % 0.5), 0.1) - assert_equal(round10(4.4 % -0.5), -0.1) - assert_equal(round10(-4.4 % -0.5), -0.4) - assert_equal(round10(3.1 % 1.0), 0.1) + assert_equal(round10(FloatLiteral.__mod__(4.4, 0.5)), 0.4) + assert_equal(round10(FloatLiteral.__mod__(-4.4, 0.5)), 0.1) + assert_equal(round10(FloatLiteral.__mod__(4.4, -0.5)), -0.1) + assert_equal(round10(FloatLiteral.__mod__(-4.4, -0.5)), -0.4) + assert_equal(round10(FloatLiteral.__mod__(3.1, 1.0)), 0.1) def test_division(): - assert_equal(4.4 / 0.5, 8.8) + assert_equal(FloatLiteral.__truediv__(4.4, 0.5), 8.8) - alias f1 = 4.4 // 0.5 - assert_equal(f1, 8.0) - alias f2 = -4.4 // 0.5 - assert_equal(f2, -9.0) - alias f3 = 4.4 // -0.5 - assert_equal(f3, -9.0) - alias f4 = -4.4 // -0.5 - assert_equal(f4, 8.0) - - -def test_power(): - assert_almost_equal(4.5**2.5, 42.95673695) - assert_almost_equal(4.5**-2.5, 0.023279235) - # TODO (https://github.com/modularml/modular/issues/33045): Float64/SIMD has - # issues with negative numbers raised to fractional powers. - # assert_almost_equal((-4.5) ** 2.5, -42.95673695) - # assert_almost_equal((-4.5) ** -2.5, -0.023279235) + assert_equal(FloatLiteral.__floordiv__(4.4, 0.5), 8.0) + assert_equal(FloatLiteral.__floordiv__(-4.4, 0.5), -9.0) + assert_equal(FloatLiteral.__floordiv__(4.4, -0.5), -9.0) + assert_equal(FloatLiteral.__floordiv__(-4.4, -0.5), 8.0) def test_mod(): - assert_equal(4.5 % 2, 0.5) - assert_equal(-4.5 % 2, 1.5) - assert_equal(6 % 2.5, 1.0) + assert_equal(FloatLiteral.__mod__(4.5, 2), 0.5) + assert_equal(FloatLiteral.__mod__(-4.5, 2), 1.5) + assert_equal(FloatLiteral.__mod__(6, 2.5), 1.0) def test_div_mod(): @@ -183,11 +169,11 @@ def test_boolean_comparable(): def test_equality(): - var f1 = 4.4 - var f2 = 4.4 - var f3 = 42.0 - assert_equal(f1, f2) - assert_not_equal(f1, f3) + # TODO: add tests for special values + assert_true(FloatLiteral.__eq__(4.4, 4.4)) + assert_false(FloatLiteral.__eq__(4.4, 42.0)) + assert_false(FloatLiteral.__ne__(4.4, 4.4)) + assert_true(FloatLiteral.__ne__(4.4, 42.0)) def test_is_special_value(): @@ -216,7 +202,6 @@ def main(): test_round() test_round10() test_division() - test_power() test_mod() test_div_mod() test_int_conversion() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index ae3439d265..586d6e26f4 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -17,36 +17,39 @@ from sys.info import bitwidthof from testing import assert_equal -def test_constructors(): - var i1 = Int(3) # Constructible from IntLiteral - - def test_properties(): assert_equal(Int.MAX, (1 << bitwidthof[DType.index]() - 1) - 1) assert_equal(Int.MIN, -(1 << bitwidthof[DType.index]() - 1)) def test_add(): - assert_equal(6, Int(3) + Int(3)) + assert_equal(Int.__add__(Int(3), Int(3)), 6) + assert_equal(Int.__add__(Int(-2), Int(3)), 1) + assert_equal(Int.__add__(Int(2), Int(-3)), -1) + assert_equal(Int.__add__(Int(5), Int(-5)), 0) + assert_equal(Int.__add__(Int(-5), Int(-4)), -9) def test_sub(): - assert_equal(3, Int(4) - Int(1)) - assert_equal(5, Int(6) - Int(1)) + assert_equal(Int.__sub__(Int(3), Int(3)), 0) + assert_equal(Int.__sub__(Int(-2), Int(3)), -5) + assert_equal(Int.__sub__(Int(2), Int(-3)), 5) + assert_equal(Int.__sub__(Int(5), Int(4)), 1) + assert_equal(Int.__sub__(Int(4), Int(5)), -1) def test_div(): var n = Int(5) var d = Int(2) - assert_equal(2.5, n / d) - n /= d + assert_equal(2.5, Int.__truediv__(n, d)) + Int.__itruediv__(n, d) assert_equal(2, n) def test_pow(): - assert_equal(1, Int(3) ** Int(0)) - assert_equal(27, Int(3) ** Int(3)) - assert_equal(81, Int(3) ** Int(4)) + assert_equal(1, Int.__pow__(Int(3), Int(0))) + assert_equal(27, Int.__pow__(Int(3), Int(3))) + assert_equal(81, Int.__pow__(Int(3), Int(4))) def test_ceil(): @@ -78,23 +81,23 @@ def test_trunc(): def test_floordiv(): - assert_equal(1, Int(2) // Int(2)) - assert_equal(0, Int(2) // Int(3)) - assert_equal(-1, Int(2) // Int(-2)) - assert_equal(-50, Int(99) // Int(-2)) - assert_equal(-1, Int(-1) // Int(10)) + assert_equal(1, Int.__floordiv__(Int(2), Int(2))) + assert_equal(0, Int.__floordiv__(Int(2), Int(3))) + assert_equal(-1, Int.__floordiv__(Int(2), Int(-2))) + assert_equal(-50, Int.__floordiv__(Int(99), Int(-2))) + assert_equal(-1, Int.__floordiv__(Int(-1), Int(10))) def test_mod(): - assert_equal(0, Int(99) % Int(1)) - assert_equal(0, Int(99) % Int(3)) - assert_equal(-1, Int(99) % Int(-2)) - assert_equal(3, Int(99) % Int(8)) - assert_equal(-5, Int(99) % Int(-8)) - assert_equal(0, Int(2) % Int(-1)) - assert_equal(0, Int(2) % Int(-2)) - assert_equal(-1, Int(3) % Int(-2)) - assert_equal(1, Int(-3) % Int(2)) + assert_equal(0, Int.__mod__(Int(99), Int(1))) + assert_equal(0, Int.__mod__(Int(99), Int(3))) + assert_equal(-1, Int.__mod__(Int(99), Int(-2))) + assert_equal(3, Int.__mod__(Int(99), Int(8))) + assert_equal(-5, Int.__mod__(Int(99), Int(-8))) + assert_equal(0, Int.__mod__(Int(2), Int(-1))) + assert_equal(0, Int.__mod__(Int(2), Int(-2))) + assert_equal(-1, Int.__mod__(Int(3), Int(-2))) + assert_equal(1, Int.__mod__(Int(-3), Int(2))) def test_divmod(): @@ -126,25 +129,25 @@ def test_divmod(): def test_abs(): - assert_equal(abs(Int(-5)), 5) - assert_equal(abs(Int(2)), 2) - assert_equal(abs(Int(0)), 0) + assert_equal(Int(-5).__abs__(), 5) + assert_equal(Int(2).__abs__(), 2) + assert_equal(Int(0).__abs__(), 0) def test_string_conversion(): - assert_equal(str(Int(3)), "3") - assert_equal(str(Int(-3)), "-3") - assert_equal(str(Int(0)), "0") - assert_equal(str(Int(100)), "100") - assert_equal(str(Int(-100)), "-100") + assert_equal(Int(3).__str__(), "3") + assert_equal(Int(-3).__str__(), "-3") + assert_equal(Int(0).__str__(), "0") + assert_equal(Int(100).__str__(), "100") + assert_equal(Int(-100).__str__(), "-100") def test_int_representation(): - assert_equal(repr(Int(3)), "3") - assert_equal(repr(Int(-3)), "-3") - assert_equal(repr(Int(0)), "0") - assert_equal(repr(Int(100)), "100") - assert_equal(repr(Int(-100)), "-100") + assert_equal(Int(3).__repr__(), "3") + assert_equal(Int(-3).__repr__(), "-3") + assert_equal(Int(0).__repr__(), "0") + assert_equal(Int(100).__repr__(), "100") + assert_equal(Int(-100).__repr__(), "-100") def test_indexer(): @@ -153,7 +156,6 @@ def test_indexer(): def main(): - test_constructors() test_properties() test_add() test_sub() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index ea7c22da68..b5a512d17b 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -15,11 +15,20 @@ from testing import assert_equal -def test_int(): - assert_equal(3, 3) - assert_equal(3 + 3, 6) - assert_equal(4 - 1, 3) - assert_equal(6 - 1, 5) +def test_add(): + assert_equal(IntLiteral.__add__(3, 3), 6) + assert_equal(IntLiteral.__add__(-2, 3), 1) + assert_equal(IntLiteral.__add__(2, -3), -1) + assert_equal(IntLiteral.__add__(5, -5), 0) + assert_equal(IntLiteral.__add__(-5, -4), -9) + + +def test_sub(): + assert_equal(IntLiteral.__sub__(3, 3), 0) + assert_equal(IntLiteral.__sub__(-2, 3), -5) + assert_equal(IntLiteral.__sub__(2, -3), 5) + assert_equal(IntLiteral.__sub__(5, 4), 1) + assert_equal(IntLiteral.__sub__(4, 5), -1) def test_ceil(): @@ -51,22 +60,22 @@ def test_trunc(): def test_floordiv(): - assert_equal(2 // 2, 1) - assert_equal(2 // 3, 0) - assert_equal(2 // -2, -1) - assert_equal(99 // -2, -50) + assert_equal(IntLiteral.__floordiv__(2, 2), 1) + assert_equal(IntLiteral.__floordiv__(2, 3), 0) + assert_equal(IntLiteral.__floordiv__(2, -2), -1) + assert_equal(IntLiteral.__floordiv__(99, -2), -50) def test_mod(): - assert_equal(99 % 1, 0) - assert_equal(99 % 3, 0) - assert_equal(99 % -2, -1) - assert_equal(99 % 8, 3) - assert_equal(99 % -8, -5) - assert_equal(2 % -1, 0) - assert_equal(2 % -2, 0) - assert_equal(3 % -2, -1) - assert_equal(-3 % 2, 1) + assert_equal(IntLiteral.__mod__(99, 1), 0) + assert_equal(IntLiteral.__mod__(99, 3), 0) + assert_equal(IntLiteral.__mod__(99, -2), -1) + assert_equal(IntLiteral.__mod__(99, 8), 3) + assert_equal(IntLiteral.__mod__(99, -8), -5) + assert_equal(IntLiteral.__mod__(2, -1), 0) + assert_equal(IntLiteral.__mod__(2, -2), 0) + assert_equal(IntLiteral.__mod__(3, -2), -1) + assert_equal(IntLiteral.__mod__(-3, 2), 1) def test_bit_width(): @@ -77,9 +86,9 @@ def test_bit_width(): def test_abs(): - assert_equal(abs(-5), 5) - assert_equal(abs(2), 2) - assert_equal(abs(0), 0) + assert_equal(IntLiteral.__abs__(-5), 5) + assert_equal(IntLiteral.__abs__(2), 2) + assert_equal(IntLiteral.__abs__(0), 0) def test_indexer(): @@ -100,7 +109,8 @@ def test_divmod(): def main(): - test_int() + test_add() + test_sub() test_ceil() test_floor() test_round() From b6f18a31df9b673c7a00f4d4a1d0033626a38c55 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 12 Jun 2024 18:21:50 -0500 Subject: [PATCH 0946/2019] [External] [stdlib] Add convenience overload of `UnsafeMaybeUninitialized.move_from` (#41744) [External] [stdlib] Add convenience overload of `UnsafeMaybeUninitialized.move_from` This is part of #2965 . Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2972 MODULAR_ORIG_COMMIT_REV_ID: fa49109cb7ac5d5af9bd4e54c582df7ed8677c5b --- stdlib/src/memory/maybe_uninitialized.mojo | 16 +++++++++++++++- stdlib/test/memory/test_maybe_uninitialized.mojo | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index baf3ae6af3..8f7b5f0e4a 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -108,7 +108,21 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( Args: other: The object to move. """ - other.unsafe_ptr().move_pointee_into(self.unsafe_ptr()) + self.move_from(other.unsafe_ptr()) + + @always_inline + fn move_from(inout self, other: UnsafePointer[ElementType]): + """Move another object. + + This function assumes that the current memory is uninitialized + and the other object is initialized memory. + + After the function is called, the `other` object is considered uninitialized. + + Args: + other: The pointer to the object to move. + """ + other.move_pointee_into(self.unsafe_ptr()) @always_inline fn write(inout self, owned value: Self.ElementType): diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index f6f1bfcfad..9ad183180e 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -84,6 +84,19 @@ def test_maybe_uninitialized_move(): b.assume_initialized_destroy() +def test_maybe_uninitialized_move_from_pointer(): + var a = MoveCounter[Int](10) + assert_equal(a.move_count, 0) + + var b = UnsafeMaybeUninitialized[MoveCounter[Int]]() + # b is uninitialized here. + b.move_from(UnsafePointer.address_of(a)) + + # a is uninitialized now. Thankfully, we're working with trivial types + assert_equal(b.assume_initialized().move_count, 1) + b.assume_initialized_destroy() + + def test_maybe_uninitialized_copy(): var a = UnsafeMaybeUninitialized[CopyCounter]() a.write(CopyCounter()) @@ -104,4 +117,5 @@ def main(): test_maybe_uninitialized() test_write_does_not_trigger_destructor() test_maybe_uninitialized_move() + test_maybe_uninitialized_move_from_pointer() test_maybe_uninitialized_copy() From 8b1f03ef043262b89d42ec8647a8b0705ec61f99 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 13 Jun 2024 02:54:28 +0300 Subject: [PATCH 0947/2019] [stdlib] Tighten `StringLiteral` dunder method unit tests Instead of operators, literals should test their dunder methods directly to avoid unintentional implicit conversions. This patch fixes tests that violate this, and also extends the existing unit tests in a few places. MODULAR_ORIG_COMMIT_REV_ID: ec42f7b285c07942c76c713b18c9a9c270666426 --- stdlib/test/builtin/test_string_literal.mojo | 63 +++++++++++--------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index a856a254b8..1c9349e942 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -23,20 +23,34 @@ from testing import ( ) -def test_basics(): - assert_equal(4, len("four")) - assert_equal("fivesix", "five" + "six") - assert_not_equal("five", "six") - assert_equal("five", "five") +def test_add(): + assert_equal("five", StringLiteral.__add__("five", "")) + assert_equal("six", StringLiteral.__add__("", "six")) + assert_equal("fivesix", StringLiteral.__add__("five", "six")) - assert_true("not_empty") - assert_false("") + +def test_equality(): + assert_false(StringLiteral.__eq__("five", "six")) + assert_true(StringLiteral.__eq__("six", "six")) + + assert_true(StringLiteral.__ne__("five", "six")) + assert_false(StringLiteral.__ne__("six", "six")) + + +def test_len(): + assert_equal(0, StringLiteral.__len__("")) + assert_equal(4, StringLiteral.__len__("four")) + + +def test_bool(): + assert_true(StringLiteral.__bool__("not_empty")) + assert_false(StringLiteral.__bool__("")) def test_contains(): - assert_true("abc" in "abcde") - assert_true("bc" in "abcde") - assert_true("xy" not in "abcde") + assert_true(StringLiteral.__contains__("abcde", "abc")) + assert_true(StringLiteral.__contains__("abcde", "bc")) + assert_false(StringLiteral.__contains__("abcde", "xy")) def test_find(): @@ -80,7 +94,7 @@ def test_rfind(): assert_equal(-1, "abc".rfind("abcd")) -fn test_comparison_operators() raises: +def test_comparison_operators(): # Test less than and greater than assert_true(StringLiteral.__lt__("abc", "def")) assert_false(StringLiteral.__lt__("def", "abc")) @@ -111,37 +125,29 @@ fn test_comparison_operators() raises: def test_hash(): # Test a couple basic hash behaviors. # `test_hash.test_hash_bytes` has more comprehensive tests. - assert_not_equal(0, hash("test")) - assert_not_equal(hash("a"), hash("b")) - assert_equal(hash("a"), hash("a")) - assert_equal(hash("b"), hash("b")) + assert_not_equal(0, StringLiteral.__hash__("test")) + assert_not_equal(StringLiteral.__hash__("a"), StringLiteral.__hash__("b")) + assert_equal(StringLiteral.__hash__("a"), StringLiteral.__hash__("a")) + assert_equal(StringLiteral.__hash__("b"), StringLiteral.__hash__("b")) def test_intable(): - assert_equal(int("123"), 123) + assert_equal(StringLiteral.__int__("123"), 123) with assert_raises(): - _ = int("hi") + _ = StringLiteral.__int__("hi") def test_layout(): - # # Test empty StringLiteral contents - # - var empty = "".unsafe_ptr() - # An empty string literal is stored as just the NUL terminator. assert_true(int(empty) != 0) # TODO(MSTDL-596): This seems to hang? # assert_equal(empty[0], 0) - # # Test non-empty StringLiteral C string - # - var ptr: UnsafePointer[C_char] = "hello".unsafe_cstr_ptr() - assert_equal(ptr[0], ord("h")) assert_equal(ptr[1], ord("e")) assert_equal(ptr[2], ord("l")) @@ -150,7 +156,7 @@ def test_layout(): assert_equal(ptr[5], 0) # Verify NUL terminated -fn test_repr() raises: +def test_repr(): # Usual cases assert_equal(StringLiteral.__repr__("hello"), "'hello'") @@ -171,7 +177,10 @@ fn test_repr() raises: def main(): - test_basics() + test_add() + test_equality() + test_len() + test_bool() test_contains() test_find() test_rfind() From ac09ba0648a4639dc422a6ddf3ecdb6b643b62cb Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 13 Jun 2024 06:58:21 -0700 Subject: [PATCH 0948/2019] Add changelog entries for `MOJO_PYTHON` environment variable, and no longer requiring `Python.add_to_path` for local modules. MODULAR_ORIG_COMMIT_REV_ID: ceb326a76c8c02ac49ae02d5d7dffc1fc72eb422 --- docs/changelog.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index aa73a724b6..c6933b8abb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -100,6 +100,22 @@ by [@jayzhan211](https://github.com/jayzhan211)) #True 1.125 2 ``` +- Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo + to a specific version: + + ```sh + export MOJO_PYTHON="/usr/bin/python3.11" + ``` + + Or a virtual environment to always have access to those Python modules: + + ```sh + export MOJO_PYTHON="~/venv/bin/python" + ``` + + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + but no Python executable. + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that @@ -140,6 +156,12 @@ by [@jayzhan211](https://github.com/jayzhan211)) print(s.start.value()) # must retrieve the value from the optional ``` +- Accessing local Python modules with `Python.add_to_path(".")` is no longer + required, it now behaves the same as Python, you can access modules in the + same folder as the target file: + - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` + - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` From f270bf2926dc996ab3f65f0ab9bbef588d3b166b Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 13 Jun 2024 12:42:22 -0500 Subject: [PATCH 0949/2019] [External] [stdlib] Give the `CollectionElementNew` trait to `UnsafeMaybeUninitalized` (#41760) [External] [stdlib] Give the `CollectionElementNew` trait to `UnsafeMaybeUninitalized` This is part of the PR https://github.com/modularml/mojo/pull/2965 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2971 MODULAR_ORIG_COMMIT_REV_ID: 994417a3cb7a2b160fb44f4455cd8f71de03af8b --- stdlib/src/memory/maybe_uninitialized.mojo | 97 ++++++++++++++++--- stdlib/src/memory/unsafe_pointer.mojo | 2 +- .../test/memory/test_maybe_uninitialized.mojo | 19 ++-- stdlib/test/test_utils/__init__.mojo | 8 +- stdlib/test/test_utils/types.mojo | 14 ++- 5 files changed, 112 insertions(+), 28 deletions(-) diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 8f7b5f0e4a..4ad65d0a0d 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -12,9 +12,7 @@ # ===----------------------------------------------------------------------=== # -struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( - CollectionElement -): +struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): """A memory location that may or may not be initialized. Note that the destructor is a no-op. If the memory was initialized, the caller @@ -38,14 +36,39 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( """The memory is now considered uninitialized.""" self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + @doc_private @always_inline - fn __init__(inout self, owned value: Self.ElementType): + fn __init__(inout self, *, other: Self): + """It is not possible to call this method. + + Trying to call this method will abort. + """ + abort( + "You should never call the explicit copy constructor of" + " UnsafeMaybeUninitialized because it's ambiguous to copy" + " possibly uninitialized memory. Use" + " `UnsafeMaybeUninitialized.copy_from()` instead if you want to" + " trigger an explicit copy of the content of" + " UnsafeMaybeUninitialized. It has very specific semantics." + ) + self = Self() + + @always_inline + fn __init__[ + MovableType: Movable + ]( + inout self: UnsafeMaybeUninitialized[MovableType], + owned value: MovableType, + ): """The memory is now considered initialized. + Parameters: + MovableType: The type of the element to store. + Args: value: The value to initialize the memory with. """ - self = Self() + self = UnsafeMaybeUninitialized[MovableType]() self.write(value^) @always_inline @@ -55,7 +78,7 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( This method should never be called as implicit copy should not be done on memory that may be uninitialized. - Calling this method will actally cause the program to abort. + Trying to call this method will abort. If you wish to perform a copy, you should manually call the method `copy_from` instead. @@ -67,16 +90,42 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( self = Self() @always_inline - fn copy_from(inout self, other: Self): + fn copy_from[ + CopyableType: ExplicitlyCopyable + ]( + inout self: UnsafeMaybeUninitialized[CopyableType], + other: UnsafeMaybeUninitialized[CopyableType], + ): """Copy another object. This function assumes that the current memory is uninitialized and the other object is initialized memory. + Parameters: + CopyableType: The type object to copy. + + Args: + other: The object to copy. + """ + self.unsafe_ptr().initialize_pointee_explicit_copy( + other.assume_initialized() + ) + + @always_inline + fn copy_from[ + CopyableType: ExplicitlyCopyable + ](inout self: UnsafeMaybeUninitialized[CopyableType], other: CopyableType): + """Copy another object. + + This function assumes that the current memory is uninitialized. + + Parameters: + CopyableType: The type object to copy. + Args: other: The object to copy. """ - self.unsafe_ptr().init_pointee_copy(other.assume_initialized()) + self.unsafe_ptr().initialize_pointee_explicit_copy(other) @always_inline fn __moveinit__(inout self, owned other: Self): @@ -85,7 +134,7 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( This method should never be called as implicit moves should not be done on memory that may be uninitialized. - Calling this method will actally cause the program to abort. + Trying to call this method will abort. If you wish to perform a move, you should manually call the method `move_from` instead. @@ -97,7 +146,12 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( self = Self() @always_inline - fn move_from(inout self, inout other: Self): + fn move_from[ + MovableType: Movable + ]( + inout self: UnsafeMaybeUninitialized[MovableType], + inout other: UnsafeMaybeUninitialized[MovableType], + ): """Move another object. This function assumes that the current memory is uninitialized @@ -105,13 +159,21 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( After the function is called, the other object is considered uninitialized. + Parameters: + MovableType: The type object to move. + Args: other: The object to move. """ self.move_from(other.unsafe_ptr()) @always_inline - fn move_from(inout self, other: UnsafePointer[ElementType]): + fn move_from[ + MovableType: Movable + ]( + inout self: UnsafeMaybeUninitialized[MovableType], + other: UnsafePointer[MovableType], + ): """Move another object. This function assumes that the current memory is uninitialized @@ -119,17 +181,28 @@ struct UnsafeMaybeUninitialized[ElementType: CollectionElement]( After the function is called, the `other` object is considered uninitialized. + Parameters: + MovableType: The type object to move. + Args: other: The pointer to the object to move. """ other.move_pointee_into(self.unsafe_ptr()) @always_inline - fn write(inout self, owned value: Self.ElementType): + fn write[ + MovableType: Movable + ]( + inout self: UnsafeMaybeUninitialized[MovableType], + owned value: MovableType, + ): """Write a value into an uninitialized memory location. Calling this method assumes that the memory is uninitialized. + Parameters: + MovableType: The type of the element to store. + Args: value: The value to write. """ diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 7ae3ec9dab..b1849fa83d 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -343,7 +343,7 @@ struct UnsafePointer[ @always_inline fn initialize_pointee_explicit_copy[ T2: ExplicitlyCopyable - ](inout self: UnsafePointer[T, address_space], value: T2): + ](self: UnsafePointer[T, address_space], value: T2): """Emplace a copy of `value` into this pointer location. The pointer memory location is assumed to contain uninitialized data, diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index 9ad183180e..1428a0a617 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -14,25 +14,18 @@ from memory.unsafe import UnsafeMaybeUninitialized from testing import assert_equal -from test_utils import MoveCounter, CopyCounter - - -@value -struct ValueToCountDestructor(CollectionElement): - var value: Int - var destructor_counter: UnsafePointer[List[Int]] - - fn __del__(owned self): - self.destructor_counter[].append(self.value) +from test_utils import MoveCounter, CopyCounter, ValueDestructorRecorder def test_maybe_uninitialized(): # Every time an Int is destroyed, it's going to be reccorded here. var destructor_counter = List[Int]() - var a = UnsafeMaybeUninitialized[ValueToCountDestructor]() + var a = UnsafeMaybeUninitialized[ValueDestructorRecorder]() a.write( - ValueToCountDestructor(42, UnsafePointer.address_of(destructor_counter)) + ValueDestructorRecorder( + 42, UnsafePointer.address_of(destructor_counter) + ) ) assert_equal(a.assume_initialized().value, 42) @@ -52,7 +45,7 @@ def test_maybe_uninitialized(): @value -struct ImpossibleToDestroy(CollectionElement): +struct ImpossibleToDestroy: var value: Int fn __del__(owned self): diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index 97d6bf4ebb..50cb4749b9 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -12,4 +12,10 @@ # ===----------------------------------------------------------------------=== # from .test_utils import libm_call -from .types import CopyCounter, ExplicitCopyOnly, MoveCounter, MoveOnly +from .types import ( + CopyCounter, + ExplicitCopyOnly, + MoveCounter, + MoveOnly, + ValueDestructorRecorder, +) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 168e8944c0..67bd9a8beb 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -52,7 +52,7 @@ struct ExplicitCopyOnly(ExplicitlyCopyable): self.copy_count = other.copy_count + 1 -struct CopyCounter(CollectionElement): +struct CopyCounter(CollectionElement, ExplicitlyCopyable): """Counts the number of copies performed on a value.""" var copy_count: Int @@ -60,6 +60,9 @@ struct CopyCounter(CollectionElement): fn __init__(inout self): self.copy_count = 0 + fn __init__(inout self, *, other: Self): + self.copy_count = other.copy_count + 1 + fn __moveinit__(inout self, owned existing: Self): self.copy_count = existing.copy_count @@ -103,3 +106,12 @@ struct MoveCounter[T: CollectionElementNew]( # print("ERROR: _MoveCounter copy constructor called unexpectedly!") self.value = T(other=existing.value) self.move_count = existing.move_count + + +@value +struct ValueDestructorRecorder: + var value: Int + var destructor_counter: UnsafePointer[List[Int]] + + fn __del__(owned self): + self.destructor_counter[].append(self.value) From f747fbc892ab8ffe72b44980cf61f9ef18516361 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 13 Jun 2024 19:30:03 -0500 Subject: [PATCH 0950/2019] [External] [stdlib] Add a benchmark for int to string conversion (#41634) [External] [stdlib] Add a benchmark for int to string conversion It's a very simple benchmark. It will be useful when working on SSO, or whatever tool that needs to display integers. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2985 MODULAR_ORIG_COMMIT_REV_ID: 5eeed87902840065e0e0476c77797fcbc100851f --- stdlib/benchmarks/builtin/bench_int.mojo | 41 ++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 stdlib/benchmarks/builtin/bench_int.mojo diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo new file mode 100644 index 0000000000..420632e512 --- /dev/null +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -0,0 +1,41 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s -t + +from benchmark import Bench, Bencher, BenchId, BenchConfig + + +# ===----------------------------------------------------------------------===# +# Benchmarks +# ===----------------------------------------------------------------------===# +@parameter +fn bench_stringify_small_integers(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + for i in range(1_000): + var a = str(i) + benchmark.keep(a) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100)) + m.bench_function[bench_stringify_small_integers]( + BenchId("bench_stringify_small_integers") + ) + m.dump_report() From 9ccbb0f783be1b6d0bfb0656bc24a1147c42bf6c Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 13 Jun 2024 20:08:45 -0500 Subject: [PATCH 0951/2019] [External] [stdlib] Add `UInt.__eq__()` and `UInt.__ne__()` (#41789) [External] [stdlib] Add `UInt.__eq__()` and `UInt.__ne__()` Related to * https://github.com/modularml/mojo/issues/2919 The operations are the same for signed and unsigned integers. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3033 MODULAR_ORIG_COMMIT_REV_ID: 5aa6194dd708d2cd49aefb4213db72a84388e2b2 --- stdlib/src/builtin/uint.mojo | 32 +++++++++++++++++ stdlib/test/builtin/test_uint.mojo | 56 +++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 8776dcfc9d..0788a2d33b 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -99,3 +99,35 @@ struct UInt(Stringable, Representable): The string representation of this UInt. """ return "UInt(" + str(self) + ")" + + @always_inline("nodebug") + fn __eq__(self, rhs: UInt) -> Bool: + """Compare this UInt to the RHS using EQ comparison. + + Args: + rhs: The other UInt to compare against. + + Returns: + True if this UInt is equal to the RHS UInt and False otherwise. + """ + return Bool( + __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + ) + + @always_inline("nodebug") + fn __ne__(self, rhs: UInt) -> Bool: + """Compare this UInt to the RHS using NE comparison. + + Args: + rhs: The other UInt to compare against. + + Returns: + True if this UInt is non-equal to the RHS UInt and False otherwise. + """ + return Bool( + __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + ) diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index f2288ce32b..35d79b2a32 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_false, assert_true, assert_not_equal def test_simple_uint(): @@ -38,6 +38,60 @@ def test_uint_representation(): assert_equal(repr(UInt(18446744073709551615)), "UInt(18446744073709551615)") +def test_equality(): + assert_equal(UInt(32), UInt(32)) + assert_equal(UInt(0), UInt(0)) + assert_equal(UInt(), UInt(0)) + assert_equal(UInt(18446744073709551615), UInt(18446744073709551615)) + assert_equal( + UInt(18446744073709551615 - 10), UInt(18446744073709551615 - 10) + ) + + assert_true(UInt(32).__eq__(UInt(32))) + assert_true(UInt(0).__eq__(UInt(0))) + assert_true(UInt().__eq__(UInt(0))) + assert_true(UInt(18446744073709551615).__eq__(UInt(18446744073709551615))) + assert_true( + UInt(18446744073709551615 - 10).__eq__(UInt(18446744073709551615 - 10)) + ) + + assert_false(UInt(32).__eq__(UInt(0))) + assert_false(UInt(0).__eq__(UInt(32))) + assert_false(UInt(0).__eq__(UInt(18446744073709551615))) + assert_false(UInt(18446744073709551615).__eq__(UInt(0))) + assert_false( + UInt(18446744073709551615).__eq__(UInt(18446744073709551615 - 10)) + ) + + +def test_inequality(): + assert_not_equal(UInt(32), UInt(0)) + assert_not_equal(UInt(0), UInt(32)) + assert_not_equal(UInt(0), UInt(18446744073709551615)) + assert_not_equal(UInt(18446744073709551615), UInt(0)) + assert_not_equal( + UInt(18446744073709551615), UInt(18446744073709551615 - 10) + ) + + assert_false(UInt(32).__ne__(UInt(32))) + assert_false(UInt(0).__ne__(UInt(0))) + assert_false(UInt().__ne__(UInt(0))) + assert_false(UInt(18446744073709551615).__ne__(UInt(18446744073709551615))) + assert_false( + UInt(18446744073709551615 - 10).__ne__(UInt(18446744073709551615 - 10)) + ) + + assert_true(UInt(32).__ne__(UInt(0))) + assert_true(UInt(0).__ne__(UInt(32))) + assert_true(UInt(0).__ne__(UInt(18446744073709551615))) + assert_true(UInt(18446744073709551615).__ne__(UInt(0))) + assert_true( + UInt(18446744073709551615).__ne__(UInt(18446744073709551615 - 10)) + ) + + def main(): test_simple_uint() test_uint_representation() + test_equality() + test_inequality() From 0d8824013603835cbda3a168184c147e1a26b240 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 14 Jun 2024 00:14:15 -0700 Subject: [PATCH 0952/2019] Add optimized GCD for positive values MODULAR_ORIG_COMMIT_REV_ID: 2a362f83ab0d7d1ba503f1d2a0eb56da645022f2 --- stdlib/src/builtin/_math.mojo | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 89717218ad..7e601258e3 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -17,6 +17,8 @@ module should be exposed by the current `math` module. The contents of this module should be eventually moved to the `math` module when it's open sourced. """ +from bit import countr_zero + # ===----------------------------------------------------------------------=== # # Ceilable # ===----------------------------------------------------------------------=== # @@ -202,7 +204,7 @@ trait Truncable: # ===----------------------------------------------------------------------=== # -fn gcd(owned m: Int, owned n: Int, /) -> Int: +fn gcd(m: Int, n: Int, /) -> Int: """Compute the greatest common divisor of two integers. Args: @@ -212,9 +214,34 @@ fn gcd(owned m: Int, owned n: Int, /) -> Int: Returns: The greatest common divisor of the two integers. """ - while n: - m, n = n, m % n - return abs(m) + if m == 0 or n == 0: + return max(m, n) + + if m > 0 and n > 0: + var trailing_zeros_a = countr_zero(m) + var trailing_zeros_b = countr_zero(n) + + var u = m >> trailing_zeros_a + var v = n >> trailing_zeros_b + var trailing_zeros_common = min(trailing_zeros_a, trailing_zeros_b) + + if u == 1 or v == 1: + return 1 << trailing_zeros_common + + while u != v: + if u > v: + u, v = v, u + v -= u + if u == 0: + break + v >>= countr_zero(v) + return u << trailing_zeros_common + + var u = m + var v = n + while v: + u, v = v, u % v + return abs(u) fn gcd(s: Span[Int], /) -> Int: From 8a290126e33c1affe24dbd1b7ed775e65220d4de Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 14 Jun 2024 02:15:20 -0500 Subject: [PATCH 0953/2019] [External] [stdlib] Add `String.isdigit` (#41788) [External] [stdlib] Add `String.isdigit` Partially addresses #2975 - Added convenience `String` overload `isdigit` function - Add `isdigit` member to `String` that checks if all characters in a string are digits as python has Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3027 MODULAR_ORIG_COMMIT_REV_ID: ef774445f416c57af49885d289f53847a145418e --- stdlib/src/builtin/string.mojo | 23 +++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 13 +++++++++++++ 2 files changed, 36 insertions(+) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 30b452cb6f..f23415d27b 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -510,6 +510,18 @@ fn isdigit(c: UInt8) -> Bool: return ord_0 <= int(c) <= ord_9 +fn isdigit(c: String) -> Bool: + """Determines whether the given character is a digit [0-9]. + + Args: + c: The character to check. + + Returns: + True if the character is a digit. + """ + return isdigit(ord(c)) + + # ===----------------------------------------------------------------------=== # # isupper # ===----------------------------------------------------------------------=== # @@ -2135,6 +2147,17 @@ struct String( return res^ + fn isdigit(self) -> Bool: + """Returns True if all characters in the string are digits. + + Returns: + True if all characters are digits else False. + """ + for c in self: + if not isdigit(c): + return False + return True + # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 5eb1b077c8..44405b582e 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -1308,6 +1308,18 @@ def test_format_args(): ) +def test_isdigit(): + assert_true(isdigit(ord("1"))) + assert_true(isdigit("1")) + # TODO: What to do with multi-character strings? + # assert_false(isdigit("1gt")) + assert_false(isdigit(ord("g"))) + assert_false(isdigit("g")) + assert_true(String("123").isdigit()) + assert_false(String("asdg").isdigit()) + assert_false(String("123asdg").isdigit()) + + def main(): test_constructors() test_copy() @@ -1354,3 +1366,4 @@ def main(): test_indexing() test_string_iter() test_format_args() + test_isdigit() From 62f5b1e3a006f63f8bc9a74f107de7d41887583d Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:24:51 -0500 Subject: [PATCH 0954/2019] [External] [stdlib] Fix when sep is suffix is `String.split` (#41790) [External] [stdlib] Fix when sep is suffix is `String.split` Fix the case in `String.split` where the `sep` is the string suffix, but `maxsplit` has been reached. Fixes https://github.com/modularml/mojo/issues/2879. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2897 MODULAR_ORIG_COMMIT_REV_ID: d96cc767a0dfeb98172dc7db808fde6b3fdcd624 --- stdlib/src/builtin/string.mojo | 2 +- stdlib/test/builtin/test_string.mojo | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index f23415d27b..dccb39b728 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1644,7 +1644,7 @@ struct String( output.append(self[lhs:rhs]) lhs = rhs + sep_len - if self.endswith(sep): + if self.endswith(sep) and (len(output) <= maxsplit or maxsplit == -1): output.append("") return output diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 44405b582e..2f52a9ede3 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -699,6 +699,29 @@ fn test_split() raises: assert_equal(res4[0], "he") assert_equal(res4[1], "o") + # related to #2879 + # TODO: replace string comparison when __eq__ is implemented for List + assert_equal( + String("abbaaaabbba").split("a").__str__(), + "['', 'bb', '', '', '', 'bbb', '']", + ) + assert_equal( + String("abbaaaabbba").split("a", 8).__str__(), + "['', 'bb', '', '', '', 'bbb', '']", + ) + assert_equal( + String("abbaaaabbba").split("a", 5).__str__(), + "['', 'bb', '', '', '', 'bbba']", + ) + assert_equal(String("aaa").split("a", 0).__str__(), "['aaa']") + assert_equal(String("a").split("a").__str__(), "['', '']") + assert_equal(String("1,2,3").split("3", 0).__str__(), "['1,2,3']") + assert_equal(String("1,2,3").split("3", 1).__str__(), "['1,2,', '']") + assert_equal(String("1,2,3,3").split("3", 2).__str__(), "['1,2,', ',', '']") + assert_equal( + String("1,2,3,3,3").split("3", 2).__str__(), "['1,2,', ',', ',3']" + ) + fn test_splitlines() raises: # Test with no line breaks From d94c9188b11be52c3dfb7b3d30ec15e6ba1a3311 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 14 Jun 2024 11:00:47 -0700 Subject: [PATCH 0955/2019] [mojo-stdlib] Move `Bool` off `-> Self` inits. This modernizes the initializers in Bool. MODULAR_ORIG_COMMIT_REV_ID: 7e223f4bfeb5630ca296e666076c2f7f0fe68271 --- stdlib/src/builtin/bool.mojo | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 58e1c5d717..f3309093c5 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -73,55 +73,45 @@ struct Bool( """The underlying storage of the boolean value.""" @always_inline("nodebug") - fn __init__(*, other: Self) -> Bool: + fn __init__(inout self, *, other: Self): """Explicitly construct a deep copy of the provided value. Args: other: The value to copy. - - Returns: - The constructed Bool value. """ - return Self {value: other.value} + self.value = other.value @always_inline("nodebug") - fn __init__(value: __mlir_type.i1) -> Bool: + fn __init__(inout self, value: __mlir_type.i1): """Construct a Bool value given a __mlir_type.i1 value. Args: value: The initial __mlir_type.i1 value. - - Returns: - The constructed Bool value. """ - return Self {value: value} + self.value = value @always_inline("nodebug") - fn __init__(value: __mlir_type.`!pop.scalar`) -> Bool: + fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct a Bool value given a `!pop.scalar` value. Args: value: The initial value. - - Returns: - The constructed Bool value. """ - return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.i1](value) + self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.i1]( + value + ) @always_inline("nodebug") - fn __init__[boolable: Boolable](value: boolable) -> Bool: + fn __init__[T: Boolable](inout self, value: T): """Implicitly convert a Boolable value to a Bool. Parameters: - boolable: The Boolable type. + T: The Boolable type. Args: value: The boolable value. - - Returns: - The constructed Bool value. """ - return value.__bool__() + self = value.__bool__() @always_inline("nodebug") fn __bool__(self) -> Bool: From 103a3b2f81722a1f77aeb9f2432f46f13ef17b2f Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 14 Jun 2024 11:33:32 -0700 Subject: [PATCH 0956/2019] Adds a community contribution that was missed from the changelog. MODULAR_ORIG_COMMIT_REV_ID: a76b4158422e44c885fd58f92dd01656d77803f3 --- docs/changelog-released.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 82446c3348..150d28b107 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -683,6 +683,11 @@ Big themes for this release: will likely shuffle around as we're reworking our built-in `sort()` function and optimizing it. +- `infinity` and `NaN` are now correctly handled in + [`testing.assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal) + and an `inf` function has been added to `utils/numerics.mojo`. + ([PR #2375](https://github.com/modularml/mojo/pull/2375)) + ### Tooling changes - Invoking `mojo package my-package -o my-dir` on the command line, where @@ -825,7 +830,8 @@ Special thanks to our community contributors: [@martinvuyk](https://github.com/martinvuyk), [@ChristopherLR](https://github.com/ChristopherLR), [@mzaks](https://github.com/mzaks), [@bgreni](https://github.com/bgreni), -[@Brian-M-J](https://github.com/Brian-M-J) +[@Brian-M-J](https://github.com/Brian-M-J), +[@leandrolcampos](https://github.com/leandrolcampos) ## v24.3 (2024-05-02) From 1d1d146d0bfed8e65569698400a86b803511820c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 14 Jun 2024 13:02:01 -0700 Subject: [PATCH 0957/2019] [mojo-stdlib] Move `Error` off `-> Self` init. This moves Error off of legacy initializer syntax. MODULAR_ORIG_COMMIT_REV_ID: 28a555093c030226062a8578fe0fdcbe668a130c --- stdlib/src/builtin/error.mojo | 52 ++++++++++++++--------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index a563791900..6f878d1465 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -41,38 +41,27 @@ struct Error(Stringable, Boolable, Representable): """ @always_inline("nodebug") - fn __init__() -> Error: - """Default constructor. - - Returns: - The constructed Error object. - """ - return Error {data: UnsafePointer[UInt8](), loaded_length: 0} + fn __init__(inout self): + """Default constructor.""" + self.data = UnsafePointer[UInt8]() + self.loaded_length = 0 @always_inline("nodebug") - fn __init__(value: StringLiteral) -> Error: + fn __init__(inout self, value: StringLiteral): """Construct an Error object with a given string literal. Args: value: The error message. - - Returns: - The constructed Error object. """ - return Error { - data: value.unsafe_ptr(), - loaded_length: len(value), - } + self.data = value.unsafe_ptr() + self.loaded_length = len(value) @always_inline("nodebug") - fn __init__(src: String) -> Error: + fn __init__(inout self, src: String): """Construct an Error object with a given string. Args: src: The error message. - - Returns: - The constructed Error object. """ var length = len(src) var dest = UnsafePointer[UInt8].alloc(length + 1) @@ -83,17 +72,15 @@ struct Error(Stringable, Boolable, Representable): count=length, ) dest[length] = 0 - return Error {data: dest, loaded_length: -length} + self.data = dest + self.loaded_length = -length @always_inline("nodebug") - fn __init__(src: StringRef) -> Error: + fn __init__(inout self, src: StringRef): """Construct an Error object with a given string ref. Args: src: The error message. - - Returns: - The constructed Error object. """ var length = len(src) var dest = UnsafePointer[UInt8].alloc(length + 1) @@ -103,7 +90,8 @@ struct Error(Stringable, Boolable, Representable): count=length, ) dest[length] = 0 - return Error {data: dest, loaded_length: -length} + self.data = dest + self.loaded_length = -length fn __del__(owned self): """Releases memory if allocated.""" @@ -111,22 +99,22 @@ struct Error(Stringable, Boolable, Representable): self.data.free() @always_inline("nodebug") - fn __copyinit__(existing: Self) -> Self: + fn __copyinit__(inout self, existing: Self): """Creates a deep copy of an existing error. - Returns: - The copy of the original error. + Args: + existing: The error to copy. """ if existing.loaded_length < 0: var length = -existing.loaded_length var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy(dest, existing.data, length) dest[length] = 0 - return Error {data: dest, loaded_length: existing.loaded_length} + self.data = dest + self.loaded_length = existing.loaded_length else: - return Error { - data: existing.data, loaded_length: existing.loaded_length - } + self.data = existing.data + self.loaded_length = existing.loaded_length fn __bool__(self) -> Bool: """Returns True if the error is set and false otherwise. From 908e34aa9dbb1eed186a22284f7e4449f5db2248 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 14 Jun 2024 15:32:17 -0700 Subject: [PATCH 0958/2019] Revert "[mojo-stdlib] Move Error off -> Self init." I'm trying to debug CI issues. MODULAR_ORIG_COMMIT_REV_ID: 0a2a59f23cb7bf61584182bc33f1bde4970c4592 --- stdlib/src/builtin/error.mojo | 52 +++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 6f878d1465..a563791900 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -41,27 +41,38 @@ struct Error(Stringable, Boolable, Representable): """ @always_inline("nodebug") - fn __init__(inout self): - """Default constructor.""" - self.data = UnsafePointer[UInt8]() - self.loaded_length = 0 + fn __init__() -> Error: + """Default constructor. + + Returns: + The constructed Error object. + """ + return Error {data: UnsafePointer[UInt8](), loaded_length: 0} @always_inline("nodebug") - fn __init__(inout self, value: StringLiteral): + fn __init__(value: StringLiteral) -> Error: """Construct an Error object with a given string literal. Args: value: The error message. + + Returns: + The constructed Error object. """ - self.data = value.unsafe_ptr() - self.loaded_length = len(value) + return Error { + data: value.unsafe_ptr(), + loaded_length: len(value), + } @always_inline("nodebug") - fn __init__(inout self, src: String): + fn __init__(src: String) -> Error: """Construct an Error object with a given string. Args: src: The error message. + + Returns: + The constructed Error object. """ var length = len(src) var dest = UnsafePointer[UInt8].alloc(length + 1) @@ -72,15 +83,17 @@ struct Error(Stringable, Boolable, Representable): count=length, ) dest[length] = 0 - self.data = dest - self.loaded_length = -length + return Error {data: dest, loaded_length: -length} @always_inline("nodebug") - fn __init__(inout self, src: StringRef): + fn __init__(src: StringRef) -> Error: """Construct an Error object with a given string ref. Args: src: The error message. + + Returns: + The constructed Error object. """ var length = len(src) var dest = UnsafePointer[UInt8].alloc(length + 1) @@ -90,8 +103,7 @@ struct Error(Stringable, Boolable, Representable): count=length, ) dest[length] = 0 - self.data = dest - self.loaded_length = -length + return Error {data: dest, loaded_length: -length} fn __del__(owned self): """Releases memory if allocated.""" @@ -99,22 +111,22 @@ struct Error(Stringable, Boolable, Representable): self.data.free() @always_inline("nodebug") - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(existing: Self) -> Self: """Creates a deep copy of an existing error. - Args: - existing: The error to copy. + Returns: + The copy of the original error. """ if existing.loaded_length < 0: var length = -existing.loaded_length var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy(dest, existing.data, length) dest[length] = 0 - self.data = dest - self.loaded_length = existing.loaded_length + return Error {data: dest, loaded_length: existing.loaded_length} else: - self.data = existing.data - self.loaded_length = existing.loaded_length + return Error { + data: existing.data, loaded_length: existing.loaded_length + } fn __bool__(self) -> Bool: """Returns True if the error is set and false otherwise. From 3b0747df9ff7e1d96f239e8591c5eaff6f04f320 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 14 Jun 2024 16:45:22 -0700 Subject: [PATCH 0959/2019] [stdlib] Make string.join work on List - Implements join for String and StringLiteral. - Adding unit tests. MODULAR_ORIG_COMMIT_REV_ID: e3df88832136deae5a2dc00a9d179aead9e4afad --- stdlib/src/builtin/string.mojo | 24 +++++++++++++++++++++++ stdlib/src/builtin/string_literal.mojo | 24 +++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 27 ++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index dccb39b728..b7062ba759 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1362,6 +1362,30 @@ struct String( elems.each[add_elt]() return result + fn join[T: StringableCollectionElement](self, elems: List[T]) -> String: + """Joins string elements using the current string as a delimiter. + + Parameters: + T: The types of the elements. + + Args: + elems: The input values. + + Returns: + The joined string. + """ + var result: String = "" + var is_first = True + + for e in elems: + if is_first: + is_first = False + else: + result += self + result += str(e[]) + + return result + fn _strref_dangerous(self) -> StringRef: """ Returns an inner pointer to the string as a StringRef. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index dc7c731fc1..c2a9d379fa 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -361,3 +361,27 @@ struct StringLiteral( The offset of `substr` relative to the beginning of the string. """ return StringRef(self).rfind(substr, start=start) + + fn join[T: StringableCollectionElement](self, elems: List[T]) -> String: + """Joins string elements using the current string as a delimiter. + + Parameters: + T: The types of the elements. + + Args: + elems: The input values. + + Returns: + The joined string. + """ + var result: String = "" + var is_first = True + + for e in elems: + if is_first: + is_first = False + else: + result += self + result += str(e[]) + + return result diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 2f52a9ede3..c1fa107fef 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -202,6 +202,32 @@ fn test_string_join() raises: assert_equal(sep.join(1, "abc", 3), "1,abc,3") + var s2 = String(",").join(List[UInt8](1, 2, 3)) + assert_equal(s2, "1,2,3") + + var s3 = String(",").join(List[UInt8](1, 2, 3, 4, 5, 6, 7, 8, 9)) + assert_equal(s3, "1,2,3,4,5,6,7,8,9") + + var s4 = String(",").join(List[UInt8]()) + assert_equal(s4, "") + + var s5 = String(",").join(List[UInt8](1)) + assert_equal(s5, "1") + + +fn test_string_literal_join() raises: + var s2 = ",".join(List[UInt8](1, 2, 3)) + assert_equal(s2, "1,2,3") + + var s3 = ",".join(List[UInt8](1, 2, 3, 4, 5, 6, 7, 8, 9)) + assert_equal(s3, "1,2,3,4,5,6,7,8,9") + + var s4 = ",".join(List[UInt8]()) + assert_equal(s4, "") + + var s5 = ",".join(List[UInt8](1)) + assert_equal(s5, "1") + fn test_stringref() raises: var a = StringRef("AAA") @@ -1352,6 +1378,7 @@ def main(): test_stringable() test_repr() test_string_join() + test_string_literal_join() test_stringref() test_stringref_from_dtypepointer() test_stringref_strip() From ef07e81f8adc8930c0c453a77e1fd8561214856b Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 14 Jun 2024 16:46:19 -0700 Subject: [PATCH 0960/2019] [mojo-lang] Isolate ownership/ownership-eh.mojo, NFC. (#41820) This moves one ownership testcase to isolation mode to speed up tests. MODULAR_ORIG_COMMIT_REV_ID: ae3e8a7bfc688b0f2d6ccc9530726decf45879ea --- stdlib/src/builtin/error.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index a563791900..e49f628b34 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -41,7 +41,7 @@ struct Error(Stringable, Boolable, Representable): """ @always_inline("nodebug") - fn __init__() -> Error: + fn __init__() -> Self: """Default constructor. Returns: @@ -50,7 +50,7 @@ struct Error(Stringable, Boolable, Representable): return Error {data: UnsafePointer[UInt8](), loaded_length: 0} @always_inline("nodebug") - fn __init__(value: StringLiteral) -> Error: + fn __init__(value: StringLiteral) -> Self: """Construct an Error object with a given string literal. Args: @@ -65,7 +65,7 @@ struct Error(Stringable, Boolable, Representable): } @always_inline("nodebug") - fn __init__(src: String) -> Error: + fn __init__(src: String) -> Self: """Construct an Error object with a given string. Args: @@ -86,7 +86,7 @@ struct Error(Stringable, Boolable, Representable): return Error {data: dest, loaded_length: -length} @always_inline("nodebug") - fn __init__(src: StringRef) -> Error: + fn __init__(src: StringRef) -> Self: """Construct an Error object with a given string ref. Args: From 25a36e09409efb218e63a1fa08f5a6bd478c179f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 15 Jun 2024 20:16:39 -0700 Subject: [PATCH 0961/2019] [Stdlib] Add is and isnot dunders for DType and remove is_*, NFC This removes the `is_*` functions that were on the DType enum and instead uses the newly added `is` and `isnot` dunders. This allows one to write code more naturally as `some_dtype is DType.xyz` and `some_dtype is not DType.abc`. MODULAR_ORIG_COMMIT_REV_ID: ec1c470fa251c2181a3ea360e9e491bb235f4584 --- stdlib/src/builtin/dtype.mojo | 268 ++++++------------------- stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/builtin/io.mojo | 12 +- stdlib/src/builtin/simd.mojo | 62 +++--- stdlib/src/random/random.mojo | 2 +- stdlib/src/testing/testing.mojo | 8 +- stdlib/src/utils/numerics.mojo | 88 ++++---- stdlib/test/builtin/test_dtype.mojo | 6 + stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/builtin/test_sort.mojo | 14 +- stdlib/test/test_utils/test_utils.mojo | 2 +- 11 files changed, 166 insertions(+), 300 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 8503bdc8ed..2772e0528e 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -164,7 +164,7 @@ struct DType(Stringable, Representable, KeyElement): ](val) @always_inline("nodebug") - fn __eq__(self, rhs: DType) -> Bool: + fn __is__(self, rhs: DType) -> Bool: """Compares one DType to another for equality. Args: @@ -173,201 +173,50 @@ struct DType(Stringable, Representable, KeyElement): Returns: True if the DTypes are the same and False otherwise. """ - return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - self._as_i8(), rhs._as_i8() - ) + return self == rhs @always_inline("nodebug") - fn __ne__(self, rhs: DType) -> Bool: - """Compares one DType to another for non-equality. + fn __isnot__(self, rhs: DType) -> Bool: + """Compares one DType to another for inequality. Args: rhs: The DType to compare against. - Returns: - False if the DTypes are the same and True otherwise. - """ - return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - self._as_i8(), rhs._as_i8() - ) - - fn __hash__(self) -> Int: - return hash(UInt8(self._as_i8())) - - @always_inline("nodebug") - fn isa[other: DType](self) -> Bool: - """Checks if this DType matches the other one, specified as a - parameter. - - Parameters: - other: The DType to compare against. - Returns: True if the DTypes are the same and False otherwise. """ - return self == other - - @always_inline("nodebug") - fn is_bool(self) -> Bool: - """Checks if this DType is Bool. - - Returns: - True if the DType is Bool and False otherwise. - """ - return self.isa[DType.bool]() - - @always_inline("nodebug") - fn is_uint8(self) -> Bool: - """Checks if this DType is UInt8. - - Returns: - True if the DType is UInt8 and False otherwise. - """ - return self.isa[DType.uint8]() - - @always_inline("nodebug") - fn is_int8(self) -> Bool: - """Checks if this DType is Int8. - - Returns: - True if the DType is Int8 and False otherwise. - """ - return self.isa[DType.int8]() - - @always_inline("nodebug") - fn is_uint16(self) -> Bool: - """Checks if this DType is UInt16. - - Returns: - True if the DType is UInt16 and False otherwise. - """ - return self.isa[DType.uint16]() - - @always_inline("nodebug") - fn is_int16(self) -> Bool: - """Checks if this DType is Int16. - - Returns: - True if the DType is Int16 and False otherwise. - """ - return self.isa[DType.int16]() - - @always_inline("nodebug") - fn is_uint32(self) -> Bool: - """Checks if this DType is UInt32. - - Returns: - True if the DType is UInt32 and False otherwise. - """ - return self.isa[DType.uint32]() - - @always_inline("nodebug") - fn is_int32(self) -> Bool: - """Checks if this DType is Int32. - - Returns: - True if the DType is Int32 and False otherwise. - """ - return self.isa[DType.int32]() - - @always_inline("nodebug") - fn is_uint64(self) -> Bool: - """Checks if this DType is UInt64. - - Returns: - True if the DType is UInt64 and False otherwise. - """ - return self.isa[DType.uint64]() - - @always_inline("nodebug") - fn is_int64(self) -> Bool: - """Checks if this DType is Int64. - - Returns: - True if the DType is Int64 and False otherwise. - """ - return self.isa[DType.int64]() + return self != rhs @always_inline("nodebug") - fn is_bfloat16(self) -> Bool: - """Checks if this DType is BFloat16. - - Returns: - True if the DType is BFloat16 and False otherwise. - """ - return self.isa[DType.bfloat16]() - - @always_inline("nodebug") - fn is_float16(self) -> Bool: - """Checks if this DType is Float16. - - Returns: - True if the DType is Float16 and False otherwise. - """ - return self.isa[DType.float16]() - - @always_inline("nodebug") - fn is_float32(self) -> Bool: - """Checks if this DType is Float32. - - Returns: - True if the DType is Float32 and False otherwise. - """ - return self.isa[DType.float32]() - - @always_inline("nodebug") - fn is_tensor_float32(self) -> Bool: - """Checks if this DType is Tensor Float32. - - Returns: - True if the DType is Tensor Float32 and False otherwise. - """ - return self.isa[DType.tensor_float32]() - - @always_inline("nodebug") - fn is_float64(self) -> Bool: - """Checks if this DType is Float64. - - Returns: - True if the DType is Float64 and False otherwise. - """ - return self.isa[DType.float64]() + fn __eq__(self, rhs: DType) -> Bool: + """Compares one DType to another for equality. - @always_inline("nodebug") - fn is_index(self) -> Bool: - """Checks if this DType is Index. + Args: + rhs: The DType to compare against. Returns: - True if the DType is Index and False otherwise. + True if the DTypes are the same and False otherwise. """ - return self.isa[DType.index]() + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self._as_i8(), rhs._as_i8() + ) @always_inline("nodebug") - fn is_index32(self) -> Bool: - """Checks if this DType is Index and 32 bit. - - Returns: - True if this DType is Index and 32 bit, False otherwise. - """ - return self.is_index() and (self.sizeof() == DType.int32.sizeof()) + fn __ne__(self, rhs: DType) -> Bool: + """Compares one DType to another for inequality. - @always_inline("nodebug") - fn is_index64(self) -> Bool: - """Checks if this DType is Index and 64 bit. + Args: + rhs: The DType to compare against. Returns: - True if this DType is Index and 64 bit, False otherwise. + False if the DTypes are the same and True otherwise. """ - return self.is_index() and (self.sizeof() == DType.int64.sizeof()) - - @always_inline("nodebug") - fn is_address(self) -> Bool: - """Checks if this DType is Address. + return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( + self._as_i8(), rhs._as_i8() + ) - Returns: - True if the DType is Address and False otherwise. - """ - return self.isa[DType.address]() + fn __hash__(self) -> Int: + return hash(UInt8(self._as_i8())) @always_inline("nodebug") fn is_unsigned(self) -> Bool: @@ -392,7 +241,7 @@ struct DType(Stringable, Representable, KeyElement): Returns: Returns True if the input type parameter is signed. """ - if self.is_index() or self.is_floating_point(): + if self is DType.index or self.is_floating_point(): return True if not self.is_integral(): return False @@ -410,7 +259,7 @@ struct DType(Stringable, Representable, KeyElement): Returns: Returns True if the input type parameter is an integer. """ - if self.is_index(): + if self is DType.index: return True return Bool( __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( @@ -445,7 +294,7 @@ struct DType(Stringable, Representable, KeyElement): True if the type is a half-precision float, false otherwise.. """ - return self.is_float16() or self.is_bfloat16() + return self.bitwidth() == 16 and self.is_floating_point() @always_inline("nodebug") fn is_numeric(self) -> Bool: @@ -474,8 +323,7 @@ struct DType(Stringable, Representable, KeyElement): Returns: Returns the size in bits of the current DType. """ - var size_in_bytes = self.sizeof() - return 8 * size_in_bytes + return 8 * self.sizeof() # ===----------------------------------------------------------------------===# # dispatch_integral @@ -493,23 +341,23 @@ struct DType(Stringable, Representable, KeyElement): Parameters: func: A parametrized on dtype function to dispatch. """ - if self.is_uint8(): + if self is DType.uint8: func[DType.uint8]() - elif self.is_int8(): + elif self is DType.int8: func[DType.int8]() - elif self.is_uint16(): + elif self is DType.uint16: func[DType.uint16]() - elif self.is_int16(): + elif self is DType.int16: func[DType.int16]() - elif self.is_uint32(): + elif self is DType.uint32: func[DType.uint32]() - elif self.is_int32(): + elif self is DType.int32: func[DType.int32]() - elif self.is_uint64(): - func[DType.uint64.value]() - elif self.is_int64(): + elif self is DType.uint64: + func[DType.uint64]() + elif self is DType.int64: func[DType.int64]() - elif self.is_index(): + elif self is DType.index: func[DType.index]() else: raise Error("only integral types are supported") @@ -530,14 +378,14 @@ struct DType(Stringable, Representable, KeyElement): Parameters: func: A parametrized on dtype function to dispatch. """ - if self.is_float16(): + if self is DType.float16: func[DType.float16]() # TODO(#15473): Enable after extending LLVM support - # elif self.is_bfloat16(): + # elif self is DType.bfloat16: # func[DType.bfloat16]() - elif self.is_float32(): + elif self is DType.float32: func[DType.float32]() - elif self.is_float64(): + elif self is DType.float64: func[DType.float64]() else: raise Error("only floating point types are supported") @@ -627,15 +475,15 @@ fn _integral_type_of[type: DType]() -> DType: return type @parameter - if type == DType.bfloat16 or type == DType.float16: + if type is DType.bfloat16 or type is DType.float16: return DType.int16 @parameter - if type == DType.float32 or type == DType.tensor_float32: + if type is DType.float32 or type is DType.tensor_float32: return DType.int32 @parameter - if type == DType.float64: + if type is DType.float64: return DType.int64 return type.invalid @@ -648,12 +496,12 @@ fn _scientific_notation_digits[type: DType]() -> StringLiteral: constrained[type.is_floating_point(), "expected floating point type"]() @parameter - if type == DType.bfloat16 or type == DType.float16: + if type is DType.bfloat16 or type is DType.float16: return "4" - elif type == DType.float32 or type == DType.tensor_float32: + elif type is DType.float32 or type is DType.tensor_float32: return "8" else: - constrained[type == DType.float64, "unknown floating point type"]() + constrained[type is DType.float64, "unknown floating point type"]() return "16" @@ -694,38 +542,38 @@ fn _index_printf_format() -> StringLiteral: @always_inline fn _get_dtype_printf_format[type: DType]() -> StringLiteral: @parameter - if type == DType.bool: + if type is DType.bool: return _index_printf_format() - elif type == DType.uint8: + elif type is DType.uint8: return "%hhu" - elif type == DType.int8: + elif type is DType.int8: return "%hhi" - elif type == DType.uint16: + elif type is DType.uint16: return "%hu" - elif type == DType.int16: + elif type is DType.int16: return "%hi" - elif type == DType.uint32: + elif type is DType.uint32: return "%u" - elif type == DType.int32: + elif type is DType.int32: return "%i" - elif type == DType.int64: + elif type is DType.int64: @parameter if os_is_windows(): return "%lld" else: return "%ld" - elif type == DType.uint64: + elif type is DType.uint64: @parameter if os_is_windows(): return "%llu" else: return "%lu" - elif type == DType.index: + elif type is DType.index: return _index_printf_format() - elif type == DType.address: + elif type is DType.address: return "%p" elif type.is_floating_point(): diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index cc517102a1..b211f68b1d 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -164,7 +164,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: """ @parameter - if type == DType.bool: + if type is DType.bool: return _hash_simd(data.cast[DType.int8]()) var hash_data = _ankerl_init[type, size]() diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 410c55bc42..2c91e0c860 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -181,19 +181,19 @@ fn _snprintf_scalar[ float_format: StringLiteral = "%.17g", ](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type]) -> Int: @parameter - if type == DType.bool: + if type is DType.bool: if x: return _snprintf["True"](buffer, size) else: return _snprintf["False"](buffer, size) - elif type.is_integral() or type == DType.address: + elif type.is_integral() or type is DType.address: return _snprintf[_get_dtype_printf_format[type]()](buffer, size, x) elif ( - type == DType.float16 or type == DType.bfloat16 or type == DType.float32 + type is DType.float16 or type is DType.bfloat16 or type is DType.float32 ): # We need to cast the value to float64 to print it. return _float_repr[float_format](buffer, size, x.cast[DType.float64]()) - elif type == DType.float64: + elif type is DType.float64: return _float_repr[float_format](buffer, size, rebind[Float64](x)) return 0 @@ -262,9 +262,9 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): alias format = _get_dtype_printf_format[type]() @parameter - if type == DType.bool: + if type is DType.bool: _put["True"]() if x else _put["False"]() - elif type.is_integral() or type == DType.address: + elif type.is_integral() or type is DType.address: _printf[format](x) elif type.is_floating_point(): diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index cc95c2e0f4..dbcb6fd884 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -99,11 +99,13 @@ fn _simd_construction_checks[type: DType, size: Int](): type: The data type of SIMD vector elements. size: The number of elements in the SIMD vector. """ - constrained[type != DType.invalid, "simd type cannot be DType.invalid"]() + constrained[ + type is not DType.invalid, "simd type cannot be DType.invalid" + ]() constrained[size > 0, "simd width must be > 0"]() constrained[size & (size - 1) == 0, "simd width must be power of 2"]() constrained[ - type != DType.bfloat16 or not has_neon(), + type is not DType.bfloat16 or not has_neon(), "bf16 is not supported for ARM architectures", ]() @@ -387,7 +389,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # TODO (#36686): This introduces uneeded casts here to work around # parameter if issues. @parameter - if type == DType.float16: + if type is DType.float16: self = SIMD[type, size]( __mlir_op.`pop.simd.splat`[ _type = __mlir_type[ @@ -407,7 +409,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) ) ) - elif type == DType.bfloat16: + elif type is DType.bfloat16: self = Self( __mlir_op.`pop.simd.splat`[ _type = __mlir_type[ @@ -427,7 +429,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) ) ) - elif type == DType.float32: + elif type is DType.float32: self = Self( __mlir_op.`pop.simd.splat`[ _type = __mlir_type[ @@ -539,7 +541,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ @parameter - if type == DType.bool: + if type is DType.bool: return (rebind[Self._Mask](self) & rebind[Self._Mask](rhs)).cast[ type ]() @@ -707,7 +709,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # TODO(KERN-228): support BF16 on neon systems. # As a workaround, we roll our own implementation @parameter - if has_neon() and type == DType.bfloat16: + if has_neon() and type is DType.bfloat16: var int_self = bitcast[_integral_type_of[type](), size](self) var int_rhs = bitcast[_integral_type_of[type](), size](rhs) return int_self == int_rhs @@ -732,7 +734,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # TODO(KERN-228): support BF16 on neon systems. # As a workaround, we roll our own implementation. @parameter - if has_neon() and type == DType.bfloat16: + if has_neon() and type is DType.bfloat16: var int_self = bitcast[_integral_type_of[type](), size](self) var int_rhs = bitcast[_integral_type_of[type](), size](rhs) return int_self != int_rhs @@ -809,7 +811,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self & rhs`. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() return __mlir_op.`pop.and`(self.value, rhs.value) @@ -828,7 +830,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self ^ rhs`. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() return __mlir_op.`pop.xor`(self.value, rhs.value) @@ -847,7 +849,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self | rhs`. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() return __mlir_op.`pop.or`(self.value, rhs.value) @@ -897,12 +899,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( The `~self` value. """ constrained[ - type.is_bool() or type.is_integral(), + type is DType.bool or type.is_integral(), "must be an bool or integral type", ]() @parameter - if type.is_bool(): + if type is DType.bool: return self.select(Self(False), Self(True)) else: return self ^ -1 @@ -1013,7 +1015,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rhs: The RHS value. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() self = self & rhs @@ -1029,7 +1031,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rhs: The RHS value. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() self = self ^ rhs @@ -1045,7 +1047,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( rhs: The RHS value. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() self = self | rhs @@ -1176,7 +1178,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `value & self`. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() return value & self @@ -1195,7 +1197,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `value ^ self`. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() return value ^ self @@ -1214,7 +1216,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `value | self`. """ constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() return value | self @@ -1370,7 +1372,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ @parameter - if type.is_unsigned() or type.is_bool(): + if type.is_unsigned() or type is DType.bool: return self elif type.is_floating_point(): alias integral_type = FPUtils[type].integral_type @@ -1432,15 +1434,15 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if type == target: return rebind[SIMD[target, size]](self) elif has_neon() and ( - type == DType.bfloat16 or target == DType.bfloat16 + type is DType.bfloat16 or target == DType.bfloat16 ): # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[target, size]() - elif type == DType.bool: + elif type is DType.bool: return self.select(SIMD[target, size](1), SIMD[target, size](0)) elif target == DType.bool: return rebind[SIMD[target, size]](self != 0) - elif type == DType.bfloat16: + elif type is DType.bfloat16: var cast_result = _bfloat16_to_f32( rebind[SIMD[DType.bfloat16, size]](self) ).cast[target]() @@ -1463,7 +1465,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ](index_val) ) return rebind[SIMD[target, size]](tmp) - elif (type == DType.address) and target.is_integral(): + elif (type is DType.address) and target.is_integral(): var index_tmp = SIMD[DType.index, size]( __mlir_op.`pop.pointer_to_index`[ _type = __mlir_type[ @@ -1603,9 +1605,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ]() @parameter - if type.is_bool() or type.is_integral(): + if type is DType.bool or type.is_integral(): return self - elif has_neon() and type == DType.bfloat16: + elif has_neon() and type is DType.bfloat16: # TODO(KERN-228): support BF16 on neon systems. # As a workaround, we cast to float32. return ( @@ -2341,7 +2343,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size_out <= size, "`size_out` must not exceed width of the vector." ]() constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "The element type of the vector must be integer or boolean.", ]() @@ -2387,7 +2389,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size_out <= size, "`size_out` must not exceed width of the vector." ]() constrained[ - type.is_integral() or type.is_bool(), + type.is_integral() or type is DType.bool, "The element type of the vector must be integer or boolean.", ]() @@ -2425,7 +2427,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ @parameter - if type.is_bool(): + if type is DType.bool: return int(self.cast[DType.uint8]().reduce_add()) else: constrained[ @@ -2463,7 +2465,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( A new vector of the form `[true_case[i] if elem else false_case[i] for i, elem in enumerate(self)]`. """ - constrained[type.is_bool(), "the simd dtype must be bool"]() + constrained[type is DType.bool, "the simd dtype must be bool"]() return __mlir_op.`pop.simd.select`( rebind[Self._Mask](self).value, true_case.value, diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 5687d2e8db..41455ae5f8 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -140,7 +140,7 @@ fn rand[type: DType](ptr: DTypePointer[type], size: Int): return @parameter - if type == DType.bool: + if type is DType.bool: for i in range(size): ptr[i] = random_ui64(0, 1).cast[type]() return diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 770018c287..966d7d29b6 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -37,12 +37,14 @@ fn _isclose( equal_nan: Bool, ) -> SIMD[DType.bool, a.size]: constrained[ - a.type.is_bool() or a.type.is_integral() or a.type.is_floating_point(), + a.type is DType.bool + or a.type.is_integral() + or a.type.is_floating_point(), "input type must be boolean, integral, or floating-point", ]() @parameter - if a.type.is_bool() or a.type.is_integral(): + if a.type is DType.bool or a.type.is_integral(): return a == b else: var both_nan = isnan(a) & isnan(b) @@ -279,7 +281,7 @@ fn assert_almost_equal[ An Error with the provided message if assert fails and `None` otherwise. """ constrained[ - type.is_bool() or type.is_integral() or type.is_floating_point(), + type is DType.bool or type.is_integral() or type.is_floating_point(), "type must be boolean, integral, or floating-point", ]() diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index a45f868390..608504897a 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -65,14 +65,14 @@ struct FPUtils[ """ @parameter - if type == DType.float16: + if type is DType.float16: return 10 - elif type == DType.bfloat16: + elif type is DType.bfloat16: return 7 - elif type == DType.float32: + elif type is DType.float32: return 23 else: - constrained[type == DType.float64, "unsupported float type"]() + constrained[type is DType.float64, "unsupported float type"]() return 52 @staticmethod @@ -85,12 +85,12 @@ struct FPUtils[ """ @parameter - if type == DType.float16: + if type is DType.float16: return 16 - elif type == DType.float32 or type == DType.bfloat16: + elif type is DType.float32 or type is DType.bfloat16: return 128 else: - constrained[type == DType.float64, "unsupported float type"]() + constrained[type is DType.float64, "unsupported float type"]() return 1024 @staticmethod @@ -103,12 +103,12 @@ struct FPUtils[ """ @parameter - if type == DType.float16: + if type is DType.float16: return 5 - elif type == DType.float32 or type == DType.bfloat16: + elif type is DType.float32 or type is DType.bfloat16: return 8 else: - constrained[type == DType.float64, "unsupported float type"]() + constrained[type is DType.float64, "unsupported float type"]() return 11 @staticmethod @@ -462,28 +462,28 @@ fn nan[type: DType]() -> Scalar[type]: """ @parameter - if type == DType.float16: + if type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], ]() ) - elif type == DType.bfloat16: + elif type is DType.bfloat16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], ]() ) - elif type == DType.float32: + elif type is DType.float32: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], ]() ) - elif type == DType.float64: + elif type is DType.float64: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], @@ -522,7 +522,7 @@ fn isnan[ return False @parameter - if type == DType.bfloat16: + if type is DType.bfloat16: alias int_dtype = _integral_type_of[type]() alias x7FFF = SIMD[int_dtype, simd_width](0x7FFF) alias x7F80 = SIMD[int_dtype, simd_width](0x7F80) @@ -555,28 +555,28 @@ fn inf[type: DType]() -> Scalar[type]: """ @parameter - if type == DType.float16: + if type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], ]() ) - elif type == DType.bfloat16: + elif type is DType.bfloat16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], ]() ) - elif type == DType.float32: + elif type is DType.float32: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], ]() ) - elif type == DType.float64: + elif type is DType.float64: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], @@ -608,28 +608,28 @@ fn neg_inf[type: DType]() -> Scalar[type]: """ @parameter - if type == DType.float16: + if type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], ]() ) - elif type == DType.bfloat16: + elif type is DType.bfloat16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], ]() ) - elif type == DType.float32: + elif type is DType.float32: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], ]() ) - elif type == DType.float64: + elif type is DType.float64: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], @@ -659,29 +659,33 @@ fn max_finite[type: DType]() -> Scalar[type]: """ @parameter - if type == DType.int8: + if type is DType.int8: return 127 - elif type == DType.uint8: + elif type is DType.uint8: return 255 - elif type == DType.int16: + elif type is DType.int16: return 32767 - elif type == DType.uint16: + elif type is DType.uint16: return 65535 - elif type == DType.int32 or type.is_index32(): + elif type is DType.int32 or ( + type is DType.index and bitwidthof[DType.index]() == 32 + ): return 2147483647 - elif type == DType.uint32: + elif type is DType.uint32: return 4294967295 - elif type == DType.int64 or type.is_index64(): + elif type is DType.int64 or ( + type is DType.index and bitwidthof[DType.index]() == 64 + ): return 9223372036854775807 - elif type == DType.uint64: + elif type is DType.uint64: return 18446744073709551615 - elif type == DType.float16: + elif type is DType.float16: return 65504 - elif type == DType.bfloat16: + elif type is DType.bfloat16: return 3.38953139e38 - elif type == DType.float32: + elif type is DType.float32: return 3.40282346638528859812e38 - elif type == DType.float64: + elif type is DType.float64: return 1.79769313486231570815e308 else: constrained[False, "max_finite() called on unsupported type"]() @@ -708,13 +712,17 @@ fn min_finite[type: DType]() -> Scalar[type]: @parameter if type.is_unsigned(): return 0 - elif type == DType.int8: + elif type is DType.int8: return -128 - elif type == DType.int16: + elif type is DType.int16: return -32768 - elif type == DType.int32 or type.is_index32(): + elif type is DType.int32 or ( + type is DType.index and bitwidthof[DType.index]() == 32 + ): return -2147483648 - elif type == DType.int64 or type.is_index64(): + elif type is DType.int64 or ( + type is DType.index and bitwidthof[DType.index]() == 64 + ): return -9223372036854775808 elif type.is_floating_point(): return -max_finite[type]() @@ -911,7 +919,7 @@ fn nextafter[ constrained[type.is_floating_point(), "input type must be floating point"]() @parameter - if type == DType.float64: + if type is DType.float64: return _simd_apply[_float64_dispatch, type, simd_width](arg0, arg1) return _simd_apply[_float32_dispatch, type, simd_width](arg0, arg1) diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index 08315aef65..b6b990a1ba 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -17,6 +17,11 @@ from collections import Set from testing import assert_equal, assert_false, assert_true +fn test_equality() raises: + assert_true(DType.float32 is DType.float32) + assert_true(DType.float32 is not DType.int32) + + fn test_stringable() raises: assert_equal("float32", str(DType.float32)) assert_equal("int64", str(DType.int64)) @@ -40,6 +45,7 @@ fn test_key_element() raises: fn main() raises: + test_equality() test_stringable() test_representable() test_key_element() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index d846c4d72b..abcbe1b3c6 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1342,7 +1342,7 @@ def test_reduce(): assert_equal(X2(6, -3).reduce_max(), 6) @parameter - if type.is_bool(): + if type is DType.bool: # reduce_and x8 = X8(False, False, True, True, False, True, False, True) x4 = X4(False, False, False, True) diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 2e9fe2d710..25891022bc 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -29,16 +29,16 @@ fn random_numbers[ @parameter if ( - dtype == DType.int8 - or dtype == DType.int16 - or dtype == DType.int32 - or dtype == DType.int64 + dtype is DType.int8 + or dtype is DType.int16 + or dtype is DType.int32 + or dtype is DType.int64 ): result.append(random_si64(0, max).cast[dtype]()) elif ( - dtype == DType.float16 - or dtype == DType.float32 - or dtype == DType.float64 + dtype is DType.float16 + or dtype is DType.float32 + or dtype is DType.float64 ): result.append(random_float64(0, max).cast[dtype]()) else: diff --git a/stdlib/test/test_utils/test_utils.mojo b/stdlib/test/test_utils/test_utils.mojo index 807028ea76..4591c17f24 100644 --- a/stdlib/test/test_utils/test_utils.mojo +++ b/stdlib/test/test_utils/test_utils.mojo @@ -37,7 +37,7 @@ fn libm_call[ constrained[type.is_floating_point(), "input type must be floating point"]() @parameter - if type == DType.float64: + if type is DType.float64: return _simd_apply[_float64_dispatch, type, simd_width](arg) return _simd_apply[_float32_dispatch, DType.float32, simd_width]( arg.cast[DType.float32]() From 0d5cd05d359fd03e31b5d89258ba02e20c2a9f79 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 15 Jun 2024 23:06:17 -0700 Subject: [PATCH 0962/2019] [Stdlib] Make the math package open source MODULAR_ORIG_COMMIT_REV_ID: c0dac412ac856487f8b14624e1cf658381f49715 --- stdlib/src/math/__init__.mojo | 93 ++ stdlib/src/math/math.mojo | 2002 +++++++++++++++++++++++++++++++ stdlib/src/math/polynomial.mojo | 100 ++ 3 files changed, 2195 insertions(+) create mode 100644 stdlib/src/math/__init__.mojo create mode 100644 stdlib/src/math/math.mojo create mode 100644 stdlib/src/math/polynomial.mojo diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo new file mode 100644 index 0000000000..44c51e49b0 --- /dev/null +++ b/stdlib/src/math/__init__.mojo @@ -0,0 +1,93 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the math package.""" + +from .math import ( + Ceilable, + CeilDivable, + CeilDivableRaising, + Floorable, + acos, + acosh, + asin, + asinh, + atan, + atan2, + atanh, + cbrt, + ceil, + # comb, # TODO: implement this + copysign, + cos, + cosh, + # degrees, # TODO: implement this + # dist, # TODO: implement this + # e, # TODO: implement this + erf, + erfc, + exp, + exp2, + expm1, + # fabs, # TODO: implement this + factorial, + floor, + # fmod, # TODO: implement this + frexp, + # fsum, # TODO: implement this + gamma, + gcd, + hypot, + isclose, + # isqrt, # TODO: implement this + lcm, + ldexp, + lgamma, + log, + log10, + log1p, + log2, + # modf, # TODO: implement this + # perm, # TODO: implement this + # pi, # TODO: implement this + # pow, # TODO: implement this. Note that it's different from the builtin. + # prod, # TODO: implement this + # radians, # TODO: implement this + remainder, + sin, + sinh, + sqrt, + tan, + tanh, + # tau, # TODO: implement this + trunc, +) + +# These are not part of Python's `math` module, but we define them here. +from .math import ( + align_down, + align_up, + ceildiv, + fma, + iota, + j0, + j1, + logb, + rsqrt, + scalb, + y0, + y1, +) + + +# In Python, these are in the math module, so we also expose them here. +from utils.numerics import inf, isfinite, isinf, isnan, nan, nextafter, ulp diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo new file mode 100644 index 0000000000..167e1b15d0 --- /dev/null +++ b/stdlib/src/math/math.mojo @@ -0,0 +1,2002 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines math utilities. + +You can import these APIs from the `math` package. For example: + +```mojo +from math import floor +``` +""" + +from collections import List +from sys import llvm_intrinsic +from sys._assembly import inlined_assembly +from sys.ffi import _external_call_const +from sys.info import bitwidthof, has_avx512f, simdwidthof, triple_is_nvidia_cuda + +from builtin._math import * +from builtin._math import gcd as _gcd +from builtin._math import lcm as _lcm +from builtin.dtype import _integral_type_of +from builtin.simd import _simd_apply + +from utils.index import StaticIntTuple +from utils.numerics import FPUtils, isnan, nan +from utils.static_tuple import StaticTuple + +from .polynomial import polynomial_evaluate + +# ===----------------------------------------------------------------------=== # +# floor +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn floor[T: Floorable, //](value: T) -> T: + """Get the floor value of the given object. + + Parameters: + T: The type conforming to Floorable. + + Args: + value: The object to get the floor value of. + + Returns: + The floor value of the object. + """ + return value.__floor__() + + +# ===----------------------------------------------------------------------=== # +# ceil +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn ceil[T: Ceilable, //](value: T) -> T: + """Get the ceiling value of the given object. + + Parameters: + T: The type conforming to Ceilable. + + Args: + value: The object to get the ceiling value of. + + Returns: + The ceiling value of the object. + """ + return value.__ceil__() + + +# ===----------------------------------------------------------------------=== # +# ceildiv +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn ceildiv[T: CeilDivable, //](numerator: T, denominator: T) -> T: + """Return the rounded-up result of dividing x by y. + + Parameters: + T: A type that support floor division. + + Args: + numerator: The numerator. + denominator: The denominator. + + Returns: + The ceiling of dividing x by y. + """ + return -(numerator // -denominator) + + +@always_inline +fn ceildiv[T: CeilDivableRaising, //](numerator: T, denominator: T) raises -> T: + """Return the rounded-up result of dividing x by y, potentially raising. + + Parameters: + T: A type that support floor division. + + Args: + numerator: The numerator. + denominator: The denominator. + + Returns: + The ceiling of dividing x by y. + """ + return -(numerator // -denominator) + + +# ===----------------------------------------------------------------------=== # +# trunc +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn trunc[T: Truncable, //](value: T) -> T: + """Get the truncated value of the given object. + + Parameters: + T: The type conforming to Truncable. + + Args: + value: The object to get the truncated value of. + + Returns: + The truncated value of the object. + """ + return value.__trunc__() + + +# ===----------------------------------------------------------------------=== # +# sqrt +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn sqrt(x: Int) -> Int: + """Performs square root on an integer. + + Args: + x: The integer value to perform square root on. + + Returns: + The square root of x. + """ + if x < 0: + return 0 + + var r = 0 + var r2 = 0 + + @parameter + for p in range(bitwidthof[Int]() // 4, -1, -1): + var tr2 = r2 + (r << (p + 1)) + (1 << (p + p)) + if tr2 <= x: + r2 = tr2 + r |= 1 << p + + return r + + +@always_inline +fn sqrt[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Performs elementwise square root on the elements of a SIMD vector. + + Constraints: + The type must be an arithmetic or boolean type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector to perform square root on. + + Returns: + The elementwise square root of x. + """ + constrained[ + type.is_numeric() or type is DType.bool, + "type must be arithmetic or boolean", + ]() + + @parameter + if type is DType.bool: + return x + elif type.is_integral(): + var res = SIMD[type, simd_width]() + + @parameter + for i in range(simd_width): + res[i] = sqrt(int(x[i])) + return res + else: + return llvm_intrinsic["llvm.sqrt", __type_of(x), has_side_effect=False]( + x + ) + + +# ===----------------------------------------------------------------------=== # +# rsqrt +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn rsqrt(x: SIMD) -> __type_of(x): + """Performs elementwise reciprocal square root on a SIMD vector. + + Args: + x: SIMD vector to perform reciprocal square root on. + + Returns: + The elementwise reciprocal square root of x. + """ + return 1 / sqrt(x) + + +# ===----------------------------------------------------------------------=== # +# exp2 +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn exp2[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes elementwise 2 raised to the power of n, where n is an element + of the input SIMD vector. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector to perform exp2 on. + + Returns: + Vector containing $2^n$ computed elementwise, where n is an element in + the input SIMD vector. + """ + alias integral_type = FPUtils[type].integral_type + + var xc = x.clamp(-126, 126) + + var m = xc.cast[integral_type]() + + xc -= m.cast[type]() + + var r = polynomial_evaluate[ + List[SIMD[type, simd_width]]( + 1.0, + 0.693144857883, + 0.2401793301105, + 5.551834031939e-2, + 9.810352697968e-3, + 1.33336498402e-3, + ), + ](xc) + + return ( + r._float_to_bits[integral_type]() + + (m << FPUtils[type].mantissa_width()) + )._bits_to_float[type]() + + +# ===----------------------------------------------------------------------=== # +# ldexp +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _ldexp_impl[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width], exp: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes elementwise ldexp function. + + The ldexp function multiplies a floating point value x by the number 2 + raised to the exp power. I.e. $ldexp(x,exp)$ calculate the value of $x * + 2^{exp}$ and is used within the $erf$ function. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector of floating point values. + exp: SIMD vector containing the exponents. + + Returns: + Vector containing elementwise result of ldexp on x and exp. + """ + + alias hardware_simd_width = simdwidthof[type]() + + @parameter + if ( + has_avx512f() + and type is DType.float32 + and simd_width >= hardware_simd_width + ): + var res: SIMD[type, simd_width] = 0 + var zero: SIMD[type, hardware_simd_width] = 0 + + @parameter + for idx in range(simd_width // hardware_simd_width): + alias i = idx * hardware_simd_width + # On AVX512, we can use the scalef intrinsic to compute the ldexp + # function. + var part = llvm_intrinsic[ + "llvm.x86.avx512.mask.scalef.ps.512", + SIMD[type, hardware_simd_width], + has_side_effect=False, + ]( + x.slice[hardware_simd_width, offset=i](), + exp.slice[hardware_simd_width, offset=i](), + zero, + Int16(-1), + Int32(4), + ) + res = res.insert[offset=i](part) + + return res + + alias integral_type = FPUtils[type].integral_type + var m: SIMD[integral_type, simd_width] = ( + exp.cast[integral_type]() + FPUtils[type].exponent_bias() + ) + + return x * (m << FPUtils[type].mantissa_width())._bits_to_float[type]() + + +@always_inline +fn ldexp[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width], exp: SIMD[DType.int32, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes elementwise ldexp function. + + The ldexp function multiplies a floating point value x by the number 2 + raised to the exp power. I.e. $ldexp(x,exp)$ calculate the value of $x * + 2^{exp}$ and is used within the $erf$ function. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector of floating point values. + exp: SIMD vector containing the exponents. + + Returns: + Vector containing elementwise result of ldexp on x and exp. + """ + return _ldexp_impl(x, exp.cast[type]()) + + +# ===----------------------------------------------------------------------=== # +# exp +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _exp_taylor[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + alias coefficients = List[SIMD[type, simd_width]]( + 1.0, + 1.0, + 0.5, + 0.16666666666666666667, + 0.041666666666666666667, + 0.0083333333333333333333, + 0.0013888888888888888889, + 0.00019841269841269841270, + 0.000024801587301587301587, + 2.7557319223985890653e-6, + 2.7557319223985890653e-7, + 2.5052108385441718775e-8, + 2.0876756987868098979e-9, + ) + return polynomial_evaluate[ + coefficients if type is DType.float64 else coefficients[:8], + ](x) + + +@always_inline +fn exp[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Calculates elementwise exponential of the input vector. + + Given an input vector $X$ and an output vector $Y$, sets $Y_i = e^{X_i}$ for + each position $i$ in the input vector (where $e$ is the mathmatical constant + $e$). + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: The input SIMD vector. + + Returns: + A SIMD vector containing $e$ raised to the power $X_i$ where $X_i$ is an + element in the input SIMD vector. + """ + constrained[type.is_floating_point(), "must be a floating point value"]() + alias neg_ln2 = -0.69314718055966295651160180568695068359375 + alias inv_lg2 = 1.442695040888963407359924681001892137426646 + + @parameter + if triple_is_nvidia_cuda(): + + @parameter + if type is DType.float16: + return _call_ptx_intrinsic[ + instruction="ex2.approx.f16", constraints="=h,h" + ](x * inv_lg2) + elif type is DType.float32: + return _call_ptx_intrinsic[ + instruction="ex2.approx.ftz.f32", constraints="=f,f" + ](x * inv_lg2) + + @parameter + if ( + not type is DType.float64 + and type is not DType.float32 + and type.sizeof() < DType.float32.sizeof() + ): + return exp(x.cast[DType.float32]()).cast[type]() + + var min_val: SIMD[type, simd_width] + var max_val: SIMD[type, simd_width] + + @parameter + if type is DType.float64: + min_val = -709.436139303 + max_val = 709.437 + else: + min_val = -88.3762626647949 + max_val = 88.3762626647950 + + var xc = x.clamp(min_val, max_val) + var k = floor(xc.fma(inv_lg2, 0.5)) + var r = k.fma(neg_ln2, xc) + return max(_ldexp_impl(_exp_taylor(r), k), xc) + + +# ===----------------------------------------------------------------------=== # +# frexp +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _frexp_mask1[ + simd_width: Int, type: DType, integral_type: DType +]() -> SIMD[integral_type, simd_width]: + @parameter + if type is DType.float16: + return 0x7C00 + elif type is DType.bfloat16: + return 0x7F80 + elif type is DType.float32: + return 0x7F800000 + else: + constrained[type is DType.float64, "unhandled fp type"]() + return 0x7FF0000000000000 + + +@always_inline +fn _frexp_mask2[ + simd_width: Int, type: DType, integral_type: DType +]() -> SIMD[integral_type, simd_width]: + @parameter + if type is DType.float16: + return 0x3800 + elif type is DType.bfloat16: + return 0x3F00 + elif type is DType.float32: + return 0x3F000000 + else: + constrained[type is DType.float64, "unhandled fp type"]() + return 0x3FE0000000000000 + + +@always_inline +fn frexp[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> StaticTuple[SIMD[type, simd_width], 2]: + """Breaks floating point values into a fractional part and an exponent part. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: The input values. + + Returns: + A tuple of two SIMD vectors containing the fractional and exponent parts + of the input floating point values. + """ + # Based on the implementation in boost/simd/arch/common/simd/function/ifrexp.hpp + constrained[type.is_floating_point(), "must be a floating point value"]() + alias integral_type = _integral_type_of[type]() + alias zero = SIMD[type, simd_width](0) + alias max_exponent = FPUtils[type].max_exponent() - 2 + alias mantissa_width = FPUtils[type].mantissa_width() + var mask1 = _frexp_mask1[simd_width, type, integral_type]() + var mask2 = _frexp_mask2[simd_width, type, integral_type]() + var x_int = x._float_to_bits[integral_type]() + var selector = x != zero + var exp = selector.select( + (((mask1 & x_int) >> mantissa_width) - max_exponent).cast[type](), + zero, + ) + var frac = selector.select( + ((x_int & ~mask1) | mask2)._bits_to_float[type](), zero + ) + return StaticTuple[SIMD[type, simd_width], 2](frac, exp) + + +# ===----------------------------------------------------------------------=== # +# log +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _log_base[ + type: DType, simd_width: Int, //, base: Int +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Performs elementwise log of a SIMD vector with a specific base. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + base: The logarithm base. + + Args: + x: Vector to perform logarithm operation on. + + Returns: + Vector containing result of performing logarithm on x. + """ + # Based on the Cephes approximation. + alias sqrt2_div_2 = 0.70710678118654752440 + + constrained[base == 2 or base == 27, "input base must be either 2 or 27"]() + + var frexp_result = frexp(x) + var x1 = frexp_result[0] + var exp = frexp_result[1] + exp = (x1 < sqrt2_div_2).select(exp - 1, exp) + x1 = (x1 < sqrt2_div_2).select(x1 + x1, x1) - 1 + + var x2 = x1 * x1 + var x3 = x2 * x1 + + var y = ( + polynomial_evaluate[ + List[SIMD[type, simd_width]]( + 3.3333331174e-1, + -2.4999993993e-1, + 2.0000714765e-1, + -1.6668057665e-1, + 1.4249322787e-1, + -1.2420140846e-1, + 1.1676998740e-1, + -1.1514610310e-1, + 7.0376836292e-2, + ), + ](x1) + * x3 + ) + y = x1 + x2.fma(-0.5, y) + + # TODO: fix this hack + @parameter + if base == 27: # Natural log + alias ln2 = 0.69314718055994530942 + y = exp.fma(ln2, y) + else: + alias log2e = 1.4426950408889634073599 + y = y.fma(log2e, exp) + return (x == 0).select(Scalar[type].MIN, (x > 0).select(y, nan[type]())) + + +@always_inline +fn log(x: SIMD) -> __type_of(x): + """Performs elementwise natural log (base E) of a SIMD vector. + + Args: + x: Vector to perform logarithm operation on. + + Returns: + Vector containing result of performing natural log base E on x. + """ + return _log_base[27](x) + + +# ===----------------------------------------------------------------------=== # +# log2 +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn log2(x: SIMD) -> __type_of(x): + """Performs elementwise log (base 2) of a SIMD vector. + + Args: + x: Vector to perform logarithm operation on. + + Returns: + Vector containing result of performing log base 2 on x. + """ + return _log_base[2](x) + + +# ===----------------------------------------------------------------------=== # +# copysign +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn copysign[ + type: DType, simd_width: Int, // +](magnitude: SIMD[type, simd_width], sign: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Returns a value with the magnitude of the first operand and the sign of + the second operand. + + Constraints: + The type of the input must be numeric. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + magnitude: The magnitude to use. + sign: The sign to copy. + + Returns: + Copies the sign from sign to magnitude. + """ + + @parameter + if type.is_integral(): + var mag_abs = abs(magnitude) + return (sign > 0).select(mag_abs, -mag_abs) + else: + constrained[type.is_numeric(), "operands must be a numeric type"]() + return llvm_intrinsic[ + "llvm.copysign", SIMD[type, simd_width], has_side_effect=False + ](magnitude, sign) + + +# ===----------------------------------------------------------------------=== # +# erf +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn erf[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Performs the elementwise Erf on a SIMD vector. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + Constraints: must be a floating-point type. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector to perform elementwise Erf on. + + Returns: + The result of the elementwise Erf operation. + """ + constrained[type.is_floating_point(), "must be a floating point value"]() + var x_abs = abs(x) + + var r_large = polynomial_evaluate[ + List[SIMD[type, simd_width]]( + 1.28717512e-1, + 6.34846687e-1, + 1.06777847e-1, + -2.42545605e-2, + 3.88393435e-3, + -3.83208680e-4, + 1.72948930e-5, + ), + ](x_abs.min(3.925)) + + r_large = r_large.fma(x_abs, x_abs) + r_large = copysign(1 - exp(-r_large), x) + + var r_small = polynomial_evaluate[ + List[SIMD[type, simd_width]]( + 1.28379151e-1, + -3.76124859e-1, + 1.12818025e-1, + -2.67667342e-2, + 4.99339588e-3, + -5.99104969e-4, + ), + ](x_abs * x_abs).fma(x, x) + + return (x_abs > 0.921875).select[type](r_large, r_small) + + +# ===----------------------------------------------------------------------=== # +# tanh +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn tanh[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Performs elementwise evaluation of the tanh function. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: The vector to perform the elementwise tanh on. + + Returns: + The result of the elementwise tanh operation. + """ + var xc = x.clamp(-9, 9) + var x_squared = xc * xc + + var numerator = xc * polynomial_evaluate[ + List[SIMD[type, simd_width]]( + 4.89352455891786e-03, + 6.37261928875436e-04, + 1.48572235717979e-05, + 5.12229709037114e-08, + -8.60467152213735e-11, + 2.00018790482477e-13, + -2.76076847742355e-16, + ), + ](x_squared) + + var denominator = polynomial_evaluate[ + List[SIMD[type, simd_width]]( + 4.89352518554385e-03, + 2.26843463243900e-03, + 1.18534705686654e-04, + 1.19825839466702e-06, + ), + ](x_squared) + + return numerator / denominator + + +# ===----------------------------------------------------------------------=== # +# isclose +# ===----------------------------------------------------------------------=== # + + +# TODO: control symmetric behavior with flag so we can be compatible with Python +@always_inline +fn isclose[ + type: DType, simd_width: Int +]( + a: SIMD[type, simd_width], + b: SIMD[type, simd_width], + *, + atol: Scalar[type] = 1e-08, + rtol: Scalar[type] = 1e-05, + equal_nan: Bool = False, +) -> SIMD[DType.bool, simd_width]: + """Checks if the two input values are numerically within a tolerance. + + When the type is integral, then equality is checked. When the type is + floating point, then this checks if the two input values are numerically the + close using the $abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)$ + formula. + + Unlike Pythons's `math.isclose`, this implementation is symmetric. I.e. + `isclose(a,b) == isclose(b,a)`. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + a: The first value to compare. + b: The second value to compare. + atol: The absolute tolerance. + rtol: The relative tolerance. + equal_nan: Whether to treat nans as equal. + + Returns: + A boolean vector where a and b are equal within the specified tolerance. + """ + + @parameter + if type is DType.bool or type.is_integral(): + return a == b + + var atol_vec = SIMD[type, simd_width](atol) + var rtol_vec = SIMD[type, simd_width](rtol) + var res = abs(a - b) <= (atol_vec.max(rtol_vec * abs(a).max(abs(b)))) + + if not equal_nan: + return res + + return res.select(res, isnan(a) & isnan(b)) + + +# ===----------------------------------------------------------------------=== # +# iota +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: + """Creates a SIMD vector containing an increasing sequence, starting from 0. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Returns: + An increasing sequence of values, starting from 0. + """ + return iota[type, simd_width](0) + + +@always_inline +fn iota[ + type: DType, simd_width: Int +](offset: Scalar[type]) -> SIMD[type, simd_width]: + """Creates a SIMD vector containing an increasing sequence, starting from + offset. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + offset: The value to start the sequence at. Default is zero. + + Returns: + An increasing sequence of values, starting from offset. + """ + + @parameter + if simd_width == 1: + return offset + elif type.is_integral(): + var step = llvm_intrinsic[ + "llvm.experimental.stepvector", + SIMD[type, simd_width], + has_side_effect=False, + ]() + return step + offset + else: + var it = llvm_intrinsic[ + "llvm.experimental.stepvector", + SIMD[DType.index, simd_width], + has_side_effect=False, + ]() + return it.cast[type]() + offset + + +fn iota[type: DType](buff: DTypePointer[type], len: Int, offset: Int = 0): + """Fill the buffer with numbers ranging from offset to offset + len - 1, + spaced by 1. + + The function doesn't return anything, the buffer is updated inplace. + + Parameters: + type: DType of the underlying data. + + Args: + buff: The buffer to fill. + len: The length of the buffer to fill. + offset: The value to fill at index 0. + """ + alias simd_width = simdwidthof[type]() + var vector_end = align_down(len, simd_width) + for i in range(0, vector_end, simd_width): + SIMD.store(buff, i, iota[type, simd_width](i + offset)) + for i in range(vector_end, len): + Scalar.store(buff, i, i + offset) + + +fn iota[type: DType](v: List[Scalar[type]], offset: Int = 0): + """Fill the vector with numbers ranging from offset to offset + len - 1, + spaced by 1. + + The function doesn't return anything, the vector is updated inplace. + + Parameters: + type: DType of the underlying data. + + Args: + v: The vector to fill. + offset: The value to fill at index 0. + """ + var buff = rebind[DTypePointer[type]](v.data) + iota(buff, len(v), offset) + + +fn iota(v: List[Int], offset: Int = 0): + """Fill the vector with numbers ranging from offset to offset + len - 1, + spaced by 1. + + The function doesn't return anything, the vector is updated inplace. + + Args: + v: The vector to fill. + offset: The value to fill at index 0. + """ + var buff = DTypePointer[DType.index]( + Scalar[DType.index](int(v.data)).cast[DType.address]() + ) + iota(buff, len(v), offset=offset) + + +# ===----------------------------------------------------------------------=== # +# fma +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn fma(a: Int, b: Int, c: Int) -> Int: + """Performs `fma` (fused multiply-add) on the inputs. + + The result is `(a * b) + c`. + + Args: + a: The first input. + b: The second input. + c: The third input. + + Returns: + `(a * b) + c`. + """ + return a * b + c + + +@always_inline("nodebug") +fn fma[ + type: DType, simd_width: Int +]( + a: SIMD[type, simd_width], + b: SIMD[type, simd_width], + c: SIMD[type, simd_width], +) -> SIMD[type, simd_width]: + """Performs elementwise `fma` (fused multiply-add) on the inputs. + + Each element in the result SIMD vector is $(A_i * B_i) + C_i$, where $A_i$, + $B_i$ and $C_i$ are elements at index $i$ in a, b, and c respectively. + + Parameters: + type: The `dtype` of the input SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + a: The first vector of inputs. + b: The second vector of inputs. + c: The third vector of inputs. + + Returns: + Elementwise `fma` of a, b and c. + """ + return a.fma(b, c) + + +# ===----------------------------------------------------------------------=== # +# align_down +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn align_down(value: Int, alignment: Int) -> Int: + """Returns the closest multiple of alignment that is less than or equal to + value. + + Args: + value: The value to align. + alignment: Value to align to. + + Returns: + Closest multiple of the alignment that is less than or equal to the + input value. In other words, floor(value / alignment) * alignment. + """ + debug_assert(alignment != 0, "zero alignment") + return (value // alignment) * alignment + + +# ===----------------------------------------------------------------------=== # +# align_up +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn align_up(value: Int, alignment: Int) -> Int: + """Returns the closest multiple of alignment that is greater than or equal + to value. + + Args: + value: The value to align. + alignment: Value to align to. + + Returns: + Closest multiple of the alignment that is greater than or equal to the + input value. In other words, ceiling(value / alignment) * alignment. + """ + debug_assert(alignment != 0, "zero alignment") + return ceildiv(value, alignment) * alignment + + +# ===----------------------------------------------------------------------=== # +# acos +# ===----------------------------------------------------------------------=== # + + +fn acos(x: SIMD) -> __type_of(x): + """Computes the `acos` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `acos` of the input. + """ + return _call_libm["acos"](x) + + +# ===----------------------------------------------------------------------=== # +# asin +# ===----------------------------------------------------------------------=== # + + +fn asin(x: SIMD) -> __type_of(x): + """Computes the `asin` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `asin` of the input. + """ + return _call_libm["asin"](x) + + +# ===----------------------------------------------------------------------=== # +# atan +# ===----------------------------------------------------------------------=== # + + +fn atan(x: SIMD) -> __type_of(x): + """Computes the `atan` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `atan` of the input. + """ + return _call_libm["atan"](x) + + +# ===----------------------------------------------------------------------=== # +# atan2 +# ===----------------------------------------------------------------------=== # + + +fn atan2[ + type: DType, simd_width: Int, // +](y: SIMD[type, simd_width], x: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes the `atan2` of the inputs. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + y: The first input argument. + x: The second input argument. + + Returns: + The `atan2` of the inputs. + """ + + @always_inline("nodebug") + @parameter + fn _float32_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["atan2f", SIMD[result_type, 1]](arg0, arg1) + + @always_inline("nodebug") + @parameter + fn _float64_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["atan2", SIMD[result_type, 1]](arg0, arg1) + + constrained[type.is_floating_point(), "input type must be floating point"]() + + @parameter + if type is DType.float64: + return _simd_apply[_float64_dispatch, type, simd_width](y, x) + else: + return _simd_apply[_float32_dispatch, type, simd_width](y, x) + + +# ===----------------------------------------------------------------------=== # +# cos +# ===----------------------------------------------------------------------=== # + + +fn _call_ptx_intrinsic_scalar[ + type: DType, //, + *, + instruction: StringLiteral, + constraints: StringLiteral, +](arg: Scalar[type]) -> Scalar[type]: + return inlined_assembly[ + instruction + " $0, $1;", + Scalar[type], + constraints=constraints, + has_side_effect=False, + ](arg).cast[type]() + + +fn _call_ptx_intrinsic[ + type: DType, + simd_width: Int, //, + *, + instruction: StringLiteral, + constraints: StringLiteral, +](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + @parameter + if simd_width == 1: + return _call_ptx_intrinsic_scalar[ + instruction=instruction, constraints=constraints + ](arg[0]) + + var res = SIMD[type, simd_width]() + + @parameter + for i in range(simd_width): + res[i] = _call_ptx_intrinsic_scalar[ + instruction=instruction, constraints=constraints + ](arg[i]) + return res + + +fn cos[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the `cos` of the inputs. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: The input argument. + + Returns: + The `cos` of the input. + """ + + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() <= sizeof[DType.float32](): + return _call_ptx_intrinsic[ + instruction="cos.approx.ftz.f32", constraints="=f,f" + ](x) + else: + return _call_libm["cos"](x) + + +# ===----------------------------------------------------------------------=== # +# sin +# ===----------------------------------------------------------------------=== # + + +fn sin[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the `sin` of the inputs. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: The input argument. + + Returns: + The `sin` of the input. + """ + + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() <= sizeof[DType.float32](): + return _call_ptx_intrinsic[ + instruction="sin.approx.ftz.f32", constraints="=f,f" + ](x) + else: + return _call_libm["sin"](x) + + +# ===----------------------------------------------------------------------=== # +# tan +# ===----------------------------------------------------------------------=== # + + +fn tan(x: SIMD) -> __type_of(x): + """Computes the `tan` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `tan` of the input. + """ + return _call_libm["tan"](x) + + +# ===----------------------------------------------------------------------=== # +# acosh +# ===----------------------------------------------------------------------=== # + + +fn acosh(x: SIMD) -> __type_of(x): + """Computes the `acosh` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `acosh` of the input. + """ + return _call_libm["acosh"](x) + + +# ===----------------------------------------------------------------------=== # +# asinh +# ===----------------------------------------------------------------------=== # + + +fn asinh(x: SIMD) -> __type_of(x): + """Computes the `asinh` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `asinh` of the input. + """ + return _call_libm["asinh"](x) + + +# ===----------------------------------------------------------------------=== # +# atanh +# ===----------------------------------------------------------------------=== # + + +fn atanh(x: SIMD) -> __type_of(x): + """Computes the `atanh` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `atanh` of the input. + """ + return _call_libm["atanh"](x) + + +# ===----------------------------------------------------------------------=== # +# cosh +# ===----------------------------------------------------------------------=== # + + +fn cosh(x: SIMD) -> __type_of(x): + """Computes the `cosh` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `cosh` of the input. + """ + return _call_libm["cosh"](x) + + +# ===----------------------------------------------------------------------=== # +# sinh +# ===----------------------------------------------------------------------=== # + + +fn sinh(x: SIMD) -> __type_of(x): + """Computes the `sinh` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `sinh` of the input. + """ + return _call_libm["sinh"](x) + + +# ===----------------------------------------------------------------------=== # +# expm1 +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn expm1(x: SIMD) -> __type_of(x): + """Computes the `expm1` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `expm1` of the input. + """ + return _call_libm["expm1"](x) + + +# ===----------------------------------------------------------------------=== # +# log10 +# ===----------------------------------------------------------------------=== # + + +fn log10(x: SIMD) -> __type_of(x): + """Computes the `log10` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `log10` of the input. + """ + return _call_libm["log10"](x) + + +# ===----------------------------------------------------------------------=== # +# log1p +# ===----------------------------------------------------------------------=== # + + +fn log1p(x: SIMD) -> __type_of(x): + """Computes the `log1p` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `log1p` of the input. + """ + return _call_libm["log1p"](x) + + +# ===----------------------------------------------------------------------=== # +# logb +# ===----------------------------------------------------------------------=== # + + +fn logb(x: SIMD) -> __type_of(x): + """Computes the `logb` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `logb` of the input. + """ + return _call_libm["logb"](x) + + +# ===----------------------------------------------------------------------=== # +# cbrt +# ===----------------------------------------------------------------------=== # + + +fn cbrt(x: SIMD) -> __type_of(x): + """Computes the `cbrt` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `cbrt` of the input. + """ + return _call_libm["cbrt"](x) + + +# ===----------------------------------------------------------------------=== # +# hypot +# ===----------------------------------------------------------------------=== # + + +# TODO: implement for variadic inputs as Python. +fn hypot[ + type: DType, simd_width: Int, // +](arg0: SIMD[type, simd_width], arg1: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes the `hypot` of the inputs. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + arg0: The first input argument. + arg1: The second input argument. + + Returns: + The `hypot` of the inputs. + """ + + @always_inline("nodebug") + @parameter + fn _float32_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["hypotf", SIMD[result_type, 1]](arg0, arg1) + + @always_inline("nodebug") + @parameter + fn _float64_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["hypot", SIMD[result_type, 1]](arg0, arg1) + + constrained[type.is_floating_point(), "input type must be floating point"]() + + @parameter + if type is DType.float64: + return _simd_apply[_float64_dispatch, type, simd_width](arg0, arg1) + return _simd_apply[_float32_dispatch, type, simd_width](arg0, arg1) + + +# ===----------------------------------------------------------------------=== # +# erfc +# ===----------------------------------------------------------------------=== # + + +fn erfc(x: SIMD) -> __type_of(x): + """Computes the `erfc` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `erfc` of the input. + """ + return _call_libm["erfc"](x) + + +# ===----------------------------------------------------------------------=== # +# lgamma +# ===----------------------------------------------------------------------=== # + + +fn lgamma(x: SIMD) -> __type_of(x): + """Computes the `lgamma` of the inputs. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The `lgamma` of the input. + """ + return _call_libm["lgamma"](x) + + +# ===----------------------------------------------------------------------=== # +# gamma +# ===----------------------------------------------------------------------=== # + + +fn gamma(x: SIMD) -> __type_of(x): + """Computes the Gamma of the input. + + For details, see https://en.wikipedia.org/wiki/Gamma_function. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input argument. + + Returns: + The Gamma function evaluated at the input. + """ + return _call_libm["tgamma"](x) + + +# ===----------------------------------------------------------------------=== # +# remainder +# ===----------------------------------------------------------------------=== # + + +fn remainder[ + type: DType, simd_width: Int, // +](x: SIMD[type, simd_width], y: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes the `remainder` of the inputs. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: The first input argument. + y: The second input argument. + + Returns: + The `remainder` of the inputs. + """ + + @always_inline("nodebug") + @parameter + fn _float32_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["remainderf", SIMD[result_type, 1]]( + arg0, arg1 + ) + + @always_inline("nodebug") + @parameter + fn _float64_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["remainder", SIMD[result_type, 1]]( + arg0, arg1 + ) + + constrained[type.is_floating_point(), "input type must be floating point"]() + + @parameter + if type is DType.float64: + return _simd_apply[_float64_dispatch, type, simd_width](x, y) + return _simd_apply[_float32_dispatch, type, simd_width](x, y) + + +# ===----------------------------------------------------------------------=== # +# j0 +# ===----------------------------------------------------------------------=== # + + +fn j0(x: SIMD) -> __type_of(x): + """Computes the Bessel function of the first kind of order 0 for each input + value. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input vector. + + Returns: + A vector containing the computed value for each value in the input. + """ + return _call_libm["j0"](x) + + +# ===----------------------------------------------------------------------=== # +# j1 +# ===----------------------------------------------------------------------=== # + + +fn j1(x: SIMD) -> __type_of(x): + """Computes the Bessel function of the first kind of order 1 for each input + value. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input vector. + + Returns: + A vector containing the computed value for each value in the input. + """ + return _call_libm["j1"](x) + + +# ===----------------------------------------------------------------------=== # +# y0 +# ===----------------------------------------------------------------------=== # + + +fn y0(x: SIMD) -> __type_of(x): + """Computes the Bessel function of the second kind of order 0 for each input + value. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input vector. + + Returns: + A vector containing the computed value for each value in the input. + """ + return _call_libm["y0"](x) + + +# ===----------------------------------------------------------------------=== # +# y1 +# ===----------------------------------------------------------------------=== # + + +fn y1(x: SIMD) -> __type_of(x): + """Computes the Bessel function of the second kind of order 1 for each input + value. + + Constraints: + The input must be a floating-point type. + + Args: + x: The input vector. + + Returns: + A vector containing the computed value for each value in the input. + """ + return _call_libm["y1"](x) + + +# ===----------------------------------------------------------------------=== # +# scalb +# ===----------------------------------------------------------------------=== # + + +fn scalb[ + type: DType, simd_width: Int, // +](arg0: SIMD[type, simd_width], arg1: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes the `scalb` of the inputs. + + Constraints: + The input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + arg0: The first input argument. + arg1: The second input argument. + + Returns: + The `scalb` of the inputs. + """ + + @always_inline("nodebug") + @parameter + fn _float32_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["scalbf", SIMD[result_type, 1]](arg0, arg1) + + @always_inline("nodebug") + @parameter + fn _float64_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["scalb", SIMD[result_type, 1]](arg0, arg1) + + constrained[type.is_floating_point(), "input type must be floating point"]() + + @parameter + if type is DType.float64: + return _simd_apply[_float64_dispatch, type, simd_width](arg0, arg1) + return _simd_apply[_float32_dispatch, type, simd_width](arg0, arg1) + + +# ===----------------------------------------------------------------------=== # +# gcd +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn gcd(m: Int, n: Int, /) -> Int: + """Compute the greatest common divisor of two integers. + + Args: + m: The first integer. + n: The second integrer. + + Returns: + The greatest common divisor of the two integers. + """ + return _gcd(m, n) + + +@always_inline +fn gcd(s: Span[Int], /) -> Int: + """Computes the greatest common divisor of a span of integers. + + Args: + s: A span containing a collection of integers. + + Returns: + The greatest common divisor of all the integers in the span. + """ + return _gcd(s) + + +@always_inline +fn gcd(l: List[Int], /) -> Int: + """Computes the greatest common divisor of a list of integers. + + Args: + l: A list containing a collection of integers. + + Returns: + The greatest common divisor of all the integers in the list. + """ + return _gcd(l) + + +fn gcd(*values: Int) -> Int: + """Computes the greatest common divisor of a variadic number of integers. + + Args: + values: A variadic list of integers. + + Returns: + The greatest common divisor of the given integers. + """ + # TODO: Deduplicate when we can create a Span from VariadicList + if len(values) == 0: + return 0 + var result = values[0] + for i in range(1, len(values)): + result = gcd(values[i], result) + if result == 1: + return result + return result + + +# ===----------------------------------------------------------------------=== # +# lcm +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn lcm(m: Int, n: Int, /) -> Int: + """Computes the least common multiple of two integers. + + Args: + m: The first integer. + n: The second integer. + + Returns: + The least common multiple of the two integers. + """ + return _lcm(m, n) + + +@always_inline +fn lcm(s: Span[Int], /) -> Int: + """Computes the least common multiple of a span of integers. + + Args: + s: A span of integers. + + Returns: + The least common multiple of the span. + """ + return _lcm(s) + + +@always_inline +fn lcm(l: List[Int], /) -> Int: + """Computes the least common multiple of a list of integers. + + Args: + l: A list of integers. + + Returns: + The least common multiple of the list. + """ + return _lcm(l) + + +fn lcm(*values: Int) -> Int: + """Computes the least common multiple of a variadic list of integers. + + Args: + values: A variadic list of integers. + + Returns: + The least common multiple of the list. + """ + # TODO: Deduplicate when we can create a Span from VariadicList + if len(values) == 0: + return 1 + + var result = values[0] + for i in range(1, len(values)): + result = lcm(result, values[i]) + return result + + +# ===----------------------------------------------------------------------=== # +# factorial +# ===----------------------------------------------------------------------=== # + + +# TODO: implement for IntLiteral +@always_inline +fn factorial(n: Int) -> Int: + """Computes the factorial of the integer. + + Args: + n: The input value. Must be non-negative. + + Returns: + The factorial of the input. Results are undefined for negative inputs. + """ + alias table = StaticIntTuple[21]( + 1, + 1, + 2, + 6, + 24, + 120, + 720, + 5040, + 40320, + 362880, + 3628800, + 39916800, + 479001600, + 6227020800, + 87178291200, + 1307674368000, + 20922789888000, + 355687428096000, + 6402373705728000, + 121645100408832000, + 2432902008176640000, + ) + debug_assert(0 <= n <= 20, "input value causes an overflow") + return table[n] + + +# ===----------------------------------------------------------------------=== # +# utilities +# ===----------------------------------------------------------------------=== # + + +fn _type_is_libm_supported(type: DType) -> Bool: + if type is DType.float32 and type is DType.float64: + return True + return type.is_integral() + + +fn _call_libm[ + func_name: StringLiteral, + arg_type: DType, + simd_width: Int, + *, + result_type: DType = arg_type, +](arg: SIMD[arg_type, simd_width]) -> SIMD[result_type, simd_width]: + constrained[ + arg_type.is_floating_point(), "argument type must be floating point" + ]() + constrained[ + arg_type == result_type, "the argument type must match the result type" + ]() + constrained[ + not triple_is_nvidia_cuda(), + "the libm operation is not available on the CUDA target", + ]() + + @parameter + if not _type_is_libm_supported(arg_type): + # Coerse to f32 if the value is not representable by libm. + return _call_libm_impl[func_name, result_type = DType.float32]( + arg.cast[DType.float32]() + ).cast[result_type]() + + return _call_libm_impl[func_name, result_type=result_type](arg) + + +fn _call_libm_impl[ + func_name: StringLiteral, + arg_type: DType, + simd_width: Int, + *, + result_type: DType = arg_type, +](arg: SIMD[arg_type, simd_width]) -> SIMD[result_type, simd_width]: + alias libm_name = func_name if arg_type is DType.float64 else func_name + "f" + + var res = SIMD[result_type, simd_width]() + + @parameter + for i in range(simd_width): + res[i] = _external_call_const[libm_name, Scalar[result_type]](arg[i]) + + return res diff --git a/stdlib/src/math/polynomial.mojo b/stdlib/src/math/polynomial.mojo new file mode 100644 index 0000000000..bc66eab5a7 --- /dev/null +++ b/stdlib/src/math/polynomial.mojo @@ -0,0 +1,100 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provides two implementations for evaluating polynomials. + +You can import these APIs from the `math` package. For example: + +```mojo +from math.polynomial import polynomial_evaluate +``` +""" + +from collections import List + +from utils.loop import unroll + +# ===----------------------------------------------------------------------===# +# polynomial_evaluate +# ===----------------------------------------------------------------------===# + + +@always_inline +fn polynomial_evaluate[ + dtype: DType, + simd_width: Int, //, + coefficients: List[SIMD[dtype, simd_width]], +](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: + """Evaluates the polynomial. + + Parameters: + dtype: The dtype of the value. + simd_width: The simd_width of the computed value. + coefficients: The coefficients. + + Args: + x: The value to compute the polynomial with. + + Returns: + The polynomial evaluation results using the specified value and the + constant coefficients. + """ + return _horner_evaluate[coefficients](x) + + +# ===----------------------------------------------------------------------===# +# Horner Method +# ===----------------------------------------------------------------------===# + + +@always_inline +fn _horner_evaluate[ + dtype: DType, + simd_width: Int, //, + coefficients: List[SIMD[dtype, simd_width]], +](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: + """Evaluates the polynomial using the passed in value and the specified + coefficients using the Horner scheme. The Horner scheme evaluates the + polynomial as `horner(val, coeffs)` where val is a scalar and coeffs is a + list of coefficients [c0, c1, c2, ..., cn] by: + ``` + horner(val, coeffs) = c0 + val * (c1 + val * (c2 + val * (... + val * cn))) + = fma(val, horner(val, coeffs[1:]), c0) + ``` + + Parameters: + dtype: The dtype of the value. + simd_width: The simd_width of the computed value. + coefficients: The coefficients. + + Args: + x: The value to compute the polynomial with. + + Returns: + The polynomial evaluation results using the specified value and the + constant coefficients. + """ + alias num_coefficients = len(coefficients) + alias c_last = coefficients[num_coefficients - 1] + alias c_second_from_last = coefficients[num_coefficients - 2] + + var result = x.fma(c_last, c_second_from_last) + + @parameter + @always_inline + fn iter[idx: Int](): + alias c = coefficients[num_coefficients - 3 - idx] + result = result.fma(x, c) + + unroll[iter, num_coefficients - 2]() + + return result From 47fca5465442d9db61012bb8c06fb0a6a83a7e2e Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 07:34:31 -0700 Subject: [PATCH 0963/2019] Implement floating-point mod for SIMD MODULAR_ORIG_COMMIT_REV_ID: f7a188d99dda9c9a66238bbd66412217c97e27c2 --- stdlib/src/builtin/simd.mojo | 46 ++++++++++++++++++++++++++++++ stdlib/test/builtin/test_simd.mojo | 36 ++++++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index dbcb6fd884..c7c8c68ece 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3046,3 +3046,49 @@ fn _format_scalar[ ) writer.write_str(str_slice) + + +# ===----------------------------------------------------------------------=== # +# modf +# ===----------------------------------------------------------------------=== # + + +fn _modf_scalar(x: Scalar) -> Tuple[__type_of(x), __type_of(x)]: + constrained[x.type.is_floating_point(), "the type must be floating point"]() + if x < 1: + if x < 0: + var res = _modf_scalar(-x) + return (-res[0], -res[1]) + if x == 0: + return (x, x) + return (Scalar[x.type](0), x) + + alias integral_type = FPUtils[x.type].integral_type + alias bitwidth = bitwidthof[x.type]() + alias exponent_width = FPUtils[x.type].exponent_width() + alias mantissa_width = FPUtils[x.type].mantissa_width() + alias mask = (1 << exponent_width) - 1 + alias bias = FPUtils[x.type].exponent_bias() + + var bits = bitcast[integral_type](x) + var e = (int(bits >> mantissa_width) & mask) - bias + if e < (bitwidth - exponent_width - 1): + var upper_mask = (1 << (bitwidth - exponent_width - 1 - e)) - 1 + bits &= ~upper_mask + var r = bitcast[x.type](bits) + return (r, x - r) + + +fn _modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: + constrained[x.type.is_floating_point(), "the type must be floating point"]() + + var result_int = __type_of(x)() + var result_frac = __type_of(x)() + + @parameter + for i in range(x.size): + var tup = _modf_scalar(x[i]) + result_int[i] = tup[0] + result_frac[i] = tup[1] + + return (result_int, result_frac) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index abcbe1b3c6..7aadd6b5a2 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -14,10 +14,17 @@ from sys import has_neon -from testing import assert_equal, assert_false, assert_not_equal, assert_true +from testing import ( + assert_equal, + assert_false, + assert_not_equal, + assert_true, + assert_almost_equal, +) from utils.numerics import isfinite, isinf, isnan, nan from utils.static_tuple import StaticTuple +from builtin.simd import _modf def test_cast(): @@ -1472,6 +1479,32 @@ def test_pow(): assert_equal(simd_val_int.__pow__(2), I(0, 1, 4, 9)) +def test_modf(): + var f32 = _modf(Float32(123.5)) + assert_almost_equal(f32[0], 123) + assert_almost_equal(f32[1], 0.5) + + var f64 = _modf(Float64(123.5)) + assert_almost_equal(f64[0], 123) + assert_almost_equal(f64[1], 0.5) + + f64 = _modf(Float64(0)) + assert_almost_equal(f64[0], 0) + assert_almost_equal(f64[1], 0) + + f64 = _modf(Float64(0.5)) + assert_almost_equal(f64[0], 0) + assert_almost_equal(f64[1], 0.5) + + f64 = _modf(Float64(-0.5)) + assert_almost_equal(f64[0], -0) + assert_almost_equal(f64[1], -0.5) + + f64 = _modf(Float64(-1.5)) + assert_almost_equal(f64[0], -1) + assert_almost_equal(f64[1], -0.5) + + def main(): test_abs() test_add() @@ -1517,4 +1550,5 @@ def main(): test_sub_with_overflow() test_trunc() test_truthy() + test_modf() # TODO: add tests for __and__, __or__, anc comparison operators From 298413c5bb2972c2ed0ce18da089d7ecf73093cd Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 08:29:40 -0700 Subject: [PATCH 0964/2019] [******][GPU] Use modf in the powf implementation of SIMD (#41848) Continue working towards a mojo implementation of powf Part of KERN-402 BEGIN_PRIVATE Use the modf function in the powf implementation to get the fractional part of the exponent. This is on the path to getting a mojo implementation of powf END_PRIVATE MODULAR_ORIG_COMMIT_REV_ID: 9ca020eb273b5935c440bf06e9519f246e9f9124 --- stdlib/src/builtin/simd.mojo | 58 ++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c7c8c68ece..242793f1fa 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2792,28 +2792,7 @@ fn _pow[ @parameter if ExpTy.is_floating_point() and BaseTy == ExpTy: - var rhs_quotient = exp.__floor__() - if all((exp >= 0) & (rhs_quotient == exp)): - return _pow(base, rhs_quotient.cast[_integral_type_of[ExpTy]()]()) - - var result = __type_of(base)() - - @parameter - if triple_is_nvidia_cuda(): - _print_fmt( - "ABORT: pow with two floating point operands is not supported" - " on GPU" - ) - abort() - else: - - @parameter - for i in range(simd_width): - result[i] = llvm_intrinsic[ - "llvm.pow", Scalar[BaseTy], has_side_effect=False - ](base[i], exp[i]) - - return result + return _powf(base, exp) elif ExpTy.is_integral(): # Common cases if all(exp == 2): @@ -2832,6 +2811,41 @@ fn _pow[ return __type_of(base)() +@always_inline +fn _powf[ + BaseTy: DType, simd_width: Int, ExpTy: DType +](base: SIMD[BaseTy, simd_width], exp: SIMD[ExpTy, simd_width]) -> __type_of( + base +): + constrained[ExpTy.is_floating_point(), "exponent must be floating point"]() + + var integral: SIMD[ExpTy, simd_width] + var fractional: SIMD[ExpTy, simd_width] + integral, fractional = _modf(exp) + + if all((exp >= 0) & (integral == exp)): + return _pow(base, integral.cast[_integral_type_of[ExpTy]()]()) + + var result = __type_of(base)() + + @parameter + if triple_is_nvidia_cuda(): + _print_fmt( + "ABORT: pow with two floating point operands is not supported" + " on GPU" + ) + abort() + else: + + @parameter + for i in range(simd_width): + result[i] = llvm_intrinsic[ + "llvm.pow", Scalar[BaseTy], has_side_effect=False + ](base[i], exp[i]) + + return result + + @always_inline fn _powi[type: DType](base: Scalar[type], exp: Int32) -> __type_of(base): if type.is_integral() and exp < 0: From 90c42fb013bcab960bcd4e758923d66abd0aa592 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 12:05:23 -0700 Subject: [PATCH 0965/2019] We do not need to depend on isclose in testing, since assert_almost_equal does the same thing. So, just enable the tests. MODULAR_ORIG_COMMIT_REV_ID: acd4014e6a163c8d81195d3967e3b6f5c52a8d03 --- stdlib/test/builtin/test_simd.mojo | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 7aadd6b5a2..8f47350b56 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1468,9 +1468,10 @@ def test_pow(): assert_equal(simd_val.__pow__(3), F(0.0, 1.0, 8.0, 27.0)) assert_equal(simd_val.__pow__(-1), F(inf, 1.0, 0.5, 0.3333333432674408)) - # TODO: enable when math.isclose is open sourced - # assert_equal(simd_val.__pow__(0.5), F(0.0, 1.0, 1.41421, 1.73205)) - # assert_equal(simd_val.__pow__(2, -0.5), F(0.70710, 0.57735, 0.5, 0.44721)) + assert_almost_equal(simd_val.__pow__(0.5), F(0.0, 1.0, 1.41421, 1.73205)) + assert_almost_equal( + (simd_val + 2).__pow__(-0.5), F(0.70710, 0.57735, 0.5, 0.44721) + ) alias I = SIMD[DType.int32, 4] var simd_val_int = I(0, 1, 2, 3) From 28e1bdd9a9b751a784665fffae88ff00f8770aee Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 12:42:15 -0700 Subject: [PATCH 0966/2019] Move gcd/lcm implementation to math package MODULAR_ORIG_COMMIT_REV_ID: 5426b2a59fb8c084d022a0f93badc32fc9fd213b --- stdlib/src/builtin/_math.mojo | 168 ----------------------------- stdlib/src/math/math.mojo | 63 ++++++++--- stdlib/test/builtin/test_math.mojo | 41 ------- 3 files changed, 50 insertions(+), 222 deletions(-) diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 7e601258e3..21edefd587 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -197,171 +197,3 @@ trait Truncable: # associated types. fn __trunc__(self) -> Self: ... - - -# ===----------------------------------------------------------------------=== # -# gcd -# ===----------------------------------------------------------------------=== # - - -fn gcd(m: Int, n: Int, /) -> Int: - """Compute the greatest common divisor of two integers. - - Args: - m: The first integer. - n: The second integrer. - - Returns: - The greatest common divisor of the two integers. - """ - if m == 0 or n == 0: - return max(m, n) - - if m > 0 and n > 0: - var trailing_zeros_a = countr_zero(m) - var trailing_zeros_b = countr_zero(n) - - var u = m >> trailing_zeros_a - var v = n >> trailing_zeros_b - var trailing_zeros_common = min(trailing_zeros_a, trailing_zeros_b) - - if u == 1 or v == 1: - return 1 << trailing_zeros_common - - while u != v: - if u > v: - u, v = v, u - v -= u - if u == 0: - break - v >>= countr_zero(v) - return u << trailing_zeros_common - - var u = m - var v = n - while v: - u, v = v, u % v - return abs(u) - - -fn gcd(s: Span[Int], /) -> Int: - """Computes the greatest common divisor of a span of integers. - - Args: - s: A span containing a collection of integers. - - Returns: - The greatest common divisor of all the integers in the span. - """ - if len(s) == 0: - return 0 - var result = s[0] - for item in s[1:]: - result = gcd(item[], result) - if result == 1: - return result - return result - - -@always_inline -fn gcd(l: List[Int], /) -> Int: - """Computes the greatest common divisor of a list of integers. - - Args: - l: A list containing a collection of integers. - - Returns: - The greatest common divisor of all the integers in the list. - """ - return gcd(Span(l)) - - -fn gcd(*values: Int) -> Int: - """Computes the greatest common divisor of a variadic number of integers. - - Args: - values: A variadic list of integers. - - Returns: - The greatest common divisor of the given integers. - """ - # TODO: Deduplicate when we can create a Span from VariadicList - if len(values) == 0: - return 0 - var result = values[0] - for i in range(1, len(values)): - result = gcd(values[i], result) - if result == 1: - return result - return result - - -# ===----------------------------------------------------------------------=== # -# lcm -# ===----------------------------------------------------------------------=== # - - -fn lcm(owned m: Int, owned n: Int, /) -> Int: - """Computes the least common multiple of two integers. - - Args: - m: The first integer. - n: The second integer. - - Returns: - The least common multiple of the two integers. - """ - var d: Int - if d := gcd(m, n): - return abs((m // d) * n if m > n else (n // d) * m) - return 0 - - -fn lcm(s: Span[Int], /) -> Int: - """Computes the least common multiple of a span of integers. - - Args: - s: A span of integers. - - Returns: - The least common multiple of the span. - """ - if len(s) == 0: - return 1 - - var result = s[0] - for item in s[1:]: - result = lcm(result, item[]) - return result - - -@always_inline -fn lcm(l: List[Int], /) -> Int: - """Computes the least common multiple of a list of integers. - - Args: - l: A list of integers. - - Returns: - The least common multiple of the list. - """ - return lcm(Span(l)) - - -fn lcm(*values: Int) -> Int: - """Computes the least common multiple of a variadic list of integers. - - Args: - values: A variadic list of integers. - - Returns: - The least common multiple of the list. - """ - # TODO: Deduplicate when we can create a Span from VariadicList - if len(values) == 0: - return 1 - - var result = values[0] - for i in range(1, len(values)): - result = lcm(result, values[i]) - return result diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 167e1b15d0..dc13878052 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -26,8 +26,6 @@ from sys.ffi import _external_call_const from sys.info import bitwidthof, has_avx512f, simdwidthof, triple_is_nvidia_cuda from builtin._math import * -from builtin._math import gcd as _gcd -from builtin._math import lcm as _lcm from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply @@ -1778,7 +1776,6 @@ fn scalb[ # ===----------------------------------------------------------------------=== # -@always_inline fn gcd(m: Int, n: Int, /) -> Int: """Compute the greatest common divisor of two integers. @@ -1789,10 +1786,36 @@ fn gcd(m: Int, n: Int, /) -> Int: Returns: The greatest common divisor of the two integers. """ - return _gcd(m, n) + if m == 0 or n == 0: + return max(m, n) + + if m > 0 and n > 0: + var trailing_zeros_a = countr_zero(m) + var trailing_zeros_b = countr_zero(n) + + var u = m >> trailing_zeros_a + var v = n >> trailing_zeros_b + var trailing_zeros_common = min(trailing_zeros_a, trailing_zeros_b) + + if u == 1 or v == 1: + return 1 << trailing_zeros_common + + while u != v: + if u > v: + u, v = v, u + v -= u + if u == 0: + break + v >>= countr_zero(v) + return u << trailing_zeros_common + + var u = m + var v = n + while v: + u, v = v, u % v + return abs(u) -@always_inline fn gcd(s: Span[Int], /) -> Int: """Computes the greatest common divisor of a span of integers. @@ -1802,7 +1825,14 @@ fn gcd(s: Span[Int], /) -> Int: Returns: The greatest common divisor of all the integers in the span. """ - return _gcd(s) + if len(s) == 0: + return 0 + var result = s[0] + for item in s[1:]: + result = gcd(item[], result) + if result == 1: + return result + return result @always_inline @@ -1815,7 +1845,7 @@ fn gcd(l: List[Int], /) -> Int: Returns: The greatest common divisor of all the integers in the list. """ - return _gcd(l) + return gcd(Span(l)) fn gcd(*values: Int) -> Int: @@ -1843,8 +1873,7 @@ fn gcd(*values: Int) -> Int: # ===----------------------------------------------------------------------=== # -@always_inline -fn lcm(m: Int, n: Int, /) -> Int: +fn lcm(owned m: Int, owned n: Int, /) -> Int: """Computes the least common multiple of two integers. Args: @@ -1854,10 +1883,12 @@ fn lcm(m: Int, n: Int, /) -> Int: Returns: The least common multiple of the two integers. """ - return _lcm(m, n) + var d: Int + if d := gcd(m, n): + return abs((m // d) * n if m > n else (n // d) * m) + return 0 -@always_inline fn lcm(s: Span[Int], /) -> Int: """Computes the least common multiple of a span of integers. @@ -1867,7 +1898,13 @@ fn lcm(s: Span[Int], /) -> Int: Returns: The least common multiple of the span. """ - return _lcm(s) + if len(s) == 0: + return 1 + + var result = s[0] + for item in s[1:]: + result = lcm(result, item[]) + return result @always_inline @@ -1880,7 +1917,7 @@ fn lcm(l: List[Int], /) -> Int: Returns: The least common multiple of the list. """ - return _lcm(l) + return lcm(Span(l)) fn lcm(*values: Int) -> Int: diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index b474be6978..b1a76eb109 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -96,45 +96,6 @@ def test_pow(): assert_equal(pow(I(0, 1, 2, 3), int(2)), I(0, 1, 4, 9)) -def test_gcd(): - var l = List(2, 4, 6, 8, 16) - var il = InlineArray[Int, 5](4, 16, 2, 8, 6) - assert_equal(gcd(Span[Int](il)), 2) - assert_equal(gcd(2, 4, 6, 8, 16), 2) - assert_equal(gcd(l), 2) - assert_equal(gcd(88, 24), 8) - assert_equal(gcd(0, 0), 0) - assert_equal(gcd(1, 0), 1) - assert_equal(gcd(-2, 4), 2) - assert_equal(gcd(-2, -4), 2) - assert_equal(gcd(24826148, 45296490), 526) - assert_equal(gcd(0, 9), 9) - assert_equal(gcd(4, 4), 4) - assert_equal(gcd(8), 8) - assert_equal(gcd(), 0) - assert_equal(gcd(List[Int]()), 0) - assert_equal(gcd(List(16)), 16) - - -def test_lcm(): - assert_equal(lcm(-2, 4), 4) - assert_equal(lcm(2345, 23452), 54994940) - var l = List(4, 6, 7, 3) - assert_equal(lcm(Span(l)), 84) - assert_equal(lcm(l), 84) - assert_equal(lcm(4, 6, 7, 3), 84) - assert_equal(lcm(), 1) - assert_equal(lcm(List(3)), 3) - assert_equal(lcm(List[Int]()), 1) - assert_equal(lcm(0, 4), 0) - assert_equal(lcm(5, 33), 165) - assert_equal(lcm(-34, -56, -32), 3808) - var il = InlineArray[Int, 5](4, 16, 2, 8, 6) - assert_equal(lcm(Span[Int](il)), 48) - assert_equal(lcm(345, 623, 364, 84, 93), 346475220) - assert_equal(lcm(0, 0), 0) - - def main(): test_abs() test_divmod() @@ -142,5 +103,3 @@ def main(): test_min() test_round() test_pow() - test_gcd() - test_lcm() From f52246be031992f7acb786ce4288717d32697238 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 12:58:36 -0700 Subject: [PATCH 0967/2019] This implements the is / is not operations on AddressSpace to make writing the code a bit more pythonic (when using enums). MODULAR_ORIG_COMMIT_REV_ID: bb0da3605814f1e9328d86ba69238688a4cfac82 --- stdlib/src/memory/memory.mojo | 4 +- stdlib/src/memory/reference.mojo | 84 +++++++++++++++++++++++++-- stdlib/src/memory/unsafe_pointer.mojo | 2 +- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index a4f6726e36..662f18e562 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -573,7 +573,7 @@ fn _malloc[ @parameter if triple_is_nvidia_cuda(): constrained[ - address_space == AddressSpace.GENERIC, + address_space is AddressSpace.GENERIC, "address space must be generic", ]() return external_call["malloc", Pointer[NoneType, address_space]]( @@ -595,7 +595,7 @@ fn _free(ptr: UnsafePointer): @parameter if triple_is_nvidia_cuda(): constrained[ - ptr.address_space == AddressSpace.GENERIC, + ptr.address_space is AddressSpace.GENERIC, "address space must be generic", ]() external_call["free", NoneType](ptr.bitcast[NoneType]()) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 15722f33e8..b22c9e5666 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -72,7 +72,7 @@ struct _GPUAddressSpace(EqualityComparable): Returns: True if the two address spaces are equal and False otherwise. """ - return self.value() == other.value() + return self is other @always_inline("nodebug") fn __eq__(self, other: AddressSpace) -> Bool: @@ -81,7 +81,7 @@ struct _GPUAddressSpace(EqualityComparable): Returns: True if the two address spaces are equal and False otherwise. """ - return self.value() == other.value() + return self is other @always_inline("nodebug") fn __ne__(self, other: Self) -> Bool: @@ -93,7 +93,7 @@ struct _GPUAddressSpace(EqualityComparable): Returns: True if the two address spaces are inequal and False otherwise. """ - return not self == other + return self is not other @always_inline("nodebug") fn __ne__(self, other: AddressSpace) -> Bool: @@ -105,7 +105,55 @@ struct _GPUAddressSpace(EqualityComparable): Returns: True if the two address spaces are inequal and False otherwise. """ - return not self == other + return self is not other + + @always_inline("nodebug") + fn __is__(self, other: Self) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() == other.value() + + @always_inline("nodebug") + fn __is__(self, other: AddressSpace) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() == other.value() + + @always_inline("nodebug") + fn __isnot__(self, other: Self) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() != other.value() + + @always_inline("nodebug") + fn __isnot__(self, other: AddressSpace) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() != other.value() @value @@ -173,7 +221,7 @@ struct AddressSpace(EqualityComparable): Returns: True if the two address spaces are equal and False otherwise. """ - return self.value() == other.value() + return self is other @always_inline("nodebug") fn __ne__(self, other: Self) -> Bool: @@ -185,7 +233,31 @@ struct AddressSpace(EqualityComparable): Returns: True if the two address spaces are inequal and False otherwise. """ - return not self == other + return self is not other + + @always_inline("nodebug") + fn __is__(self, other: Self) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() == other.value() + + @always_inline("nodebug") + fn __isnot__(self, other: Self) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() != other.value() # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b1849fa83d..d1a760a4f3 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -363,7 +363,7 @@ struct UnsafePointer[ """ constrained[ - address_space == AddressSpace.GENERIC, + address_space is AddressSpace.GENERIC, "can not initialize pointer in non-GENERIC address space", ]() From d9d13cec65069488e95f928449e8f1f152ae9ed5 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 13:57:08 -0700 Subject: [PATCH 0968/2019] [******] Implement the powf function in Mojo Do not depend on libm for the pow function. MODULAR_ORIG_COMMIT_REV_ID: 06e80c72313ca9dcdfa04028a232ac3b97a00b28 --- stdlib/src/builtin/simd.mojo | 50 ++++++++++++++++-------------- stdlib/test/builtin/test_simd.mojo | 20 ++++++++++++ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 242793f1fa..f42d8f49a8 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -50,6 +50,7 @@ from .dtype import ( ) from .io import _snprintf_scalar, _printf, _print_fmt from .string import _calc_initial_buffer_size, _calc_format_buffer_size +import math # ===----------------------------------------------------------------------=== # # Type Aliases @@ -2812,36 +2813,37 @@ fn _pow[ @always_inline -fn _powf[ - BaseTy: DType, simd_width: Int, ExpTy: DType -](base: SIMD[BaseTy, simd_width], exp: SIMD[ExpTy, simd_width]) -> __type_of( - base -): - constrained[ExpTy.is_floating_point(), "exponent must be floating point"]() +fn _powf_scalar(base: Scalar, exponent: Scalar) -> __type_of(base): + constrained[ + exponent.type.is_floating_point(), "exponent must be floating point" + ]() + + var integral: __type_of(exponent) + var fractional: __type_of(exponent) + integral, fractional = _modf_scalar(exponent) + + if integral == exponent: + return _powi(base, integral.cast[DType.int32]()) - var integral: SIMD[ExpTy, simd_width] - var fractional: SIMD[ExpTy, simd_width] - integral, fractional = _modf(exp) + if fractional and base < 0: + return _nan[base.type]() - if all((exp >= 0) & (integral == exp)): - return _pow(base, integral.cast[_integral_type_of[ExpTy]()]()) + return math.exp(exponent.cast[base.type]() * math.log(base)) + + +@always_inline +fn _powf[ + simd_width: Int +](base: SIMD[_, simd_width], exp: SIMD[_, simd_width]) -> __type_of(base): + constrained[ + exp.type.is_floating_point(), "exponent must be floating point" + ]() var result = __type_of(base)() @parameter - if triple_is_nvidia_cuda(): - _print_fmt( - "ABORT: pow with two floating point operands is not supported" - " on GPU" - ) - abort() - else: - - @parameter - for i in range(simd_width): - result[i] = llvm_intrinsic[ - "llvm.pow", Scalar[BaseTy], has_side_effect=False - ](base[i], exp[i]) + for i in range(simd_width): + result[i] = _powf_scalar(base[i], exp[i]) return result diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8f47350b56..6754ab58e1 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1480,6 +1480,25 @@ def test_pow(): assert_equal(simd_val_int.__pow__(2), I(0, 1, 4, 9)) +def test_powf(): + assert_almost_equal(Float32(2.0) ** Float32(0.5), 1.4142135) + assert_almost_equal(Float32(2.0) ** Float32(-0.5), 0.707107) + assert_almost_equal(Float32(50.0) ** Float32(2.5), 17677.6695297) + assert_almost_equal(Float32(12.0) ** Float32(0.4), 2.70192) + assert_almost_equal(Float32(-1.0) ** Float32(-1), -1) + assert_almost_equal(Float32(0.001) ** Float32(0.001), 0.99311605) + + assert_almost_equal(Float64(0.001) ** Float64(0.001), 0.99311605) + + assert_almost_equal(Float32(-4) ** Float32(-3), -0.015625) + + assert_almost_equal( + SIMD[DType.float64, 8](1.0, -1.0, 2.0, -2.0, 4.0, -4.0, -2.0, 3.0) + ** SIMD[DType.float64, 8](1, 2, 3, 4, 5, 6, 2, 1), + SIMD[DType.float64, 8](1, 1, 8, 16, 1024, 4096, 4, 3), + ) + + def test_modf(): var f32 = _modf(Float32(123.5)) assert_almost_equal(f32[0], 123) @@ -1535,6 +1554,7 @@ def main(): test_mod() test_mul_with_overflow() test_pow() + test_powf() test_radd() test_reduce() test_reduce_bit_count() From d2b86f921e9422ad4559e915dca3a1ff279c19b1 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 16:08:36 -0700 Subject: [PATCH 0969/2019] current_cpu is not the right name here since we might be on non-cpu systems. Rename to current_arch instead. MODULAR_ORIG_COMMIT_REV_ID: a3afef0cffd83d853809055750c49486fc0dd026 --- examples/deviceinfo.mojo | 4 ++-- stdlib/src/sys/info.mojo | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/deviceinfo.mojo b/examples/deviceinfo.mojo index 726d664d6f..f731782413 100644 --- a/examples/deviceinfo.mojo +++ b/examples/deviceinfo.mojo @@ -15,7 +15,7 @@ # This sample prints the current host system information using APIs from the # sys module. from sys.info import ( - _current_cpu, + _current_arch, _current_target, _triple_attr, ) @@ -48,7 +48,7 @@ def main(): os = "macOS" else: os = "windows" - var cpu = String(_current_cpu()) + var cpu = String(_current_arch()) var arch = String(_triple_attr()) var cpu_features = String("") if has_sse4(): diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 556d1a3b0b..e8a32a53c3 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -28,7 +28,7 @@ fn _current_target() -> __mlir_type.`!kgen.target`: @always_inline("nodebug") -fn _current_cpu() -> __mlir_type.`!kgen.string`: +fn _current_arch() -> __mlir_type.`!kgen.string`: return __mlir_attr[ `#kgen.param.expr Bool: """ return __mlir_attr[ `#kgen.param.expr : i1`, ] @@ -219,7 +219,7 @@ fn is_apple_m2() -> Bool: """ return __mlir_attr[ `#kgen.param.expr : i1`, ] @@ -236,7 +236,7 @@ fn is_apple_m3() -> Bool: """ return __mlir_attr[ `#kgen.param.expr : i1`, ] @@ -264,7 +264,7 @@ fn is_neoverse_n1() -> Bool: """ return __mlir_attr[ `#kgen.param.expr : i1`, ] From 3031d30cdb0570a7c8bd02765994f5990d855afc Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 18:17:59 -0700 Subject: [PATCH 0970/2019] This introduces a new SIMD split function that splits a SIMD vector into two elements. This is a pattern that is commonly used in the ******, so define it and replace usages with the split function MODULAR_ORIG_COMMIT_REV_ID: cf5da90b5094e9115f555ac4f5617915ae6f9fb2 --- stdlib/src/builtin/simd.mojo | 28 ++++++++++++++++++++++++---- stdlib/test/builtin/test_simd.mojo | 7 +++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f42d8f49a8..c0acc5fb5c 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2066,6 +2066,26 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( alias _SIMDHalfType = SIMD[type, size // 2] + @always_inline("nodebug") + fn split( + self, + ) -> Tuple[Self._SIMDHalfType, Self._SIMDHalfType]: + """Splits the SIMD vector into 2 subvectors. + + Returns: + A new vector `self_0:N/2, self_N/2:N`. + """ + + constrained[size > 1, "the simd width must be at least 2"]() + + alias half_size = size // 2 + var lhs = rebind[Self._SIMDHalfType](self.slice[half_size, offset=0]()) + var rhs = rebind[Self._SIMDHalfType]( + self.slice[half_size, offset=half_size]() + ) + + return (lhs, rhs) + @always_inline("nodebug") fn deinterleave( self, @@ -2157,10 +2177,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if size == size_out: return rebind[SIMD[type, size_out]](self) else: - alias half_size = size // 2 - var lhs = self.slice[half_size, offset=0]() - var rhs = self.slice[half_size, offset=half_size]() - return func[type, half_size](lhs, rhs).reduce[func, size_out]() + var lhs: Self._SIMDHalfType + var rhs: Self._SIMDHalfType + lhs, rhs = self.split() + return func(lhs, rhs).reduce[func, size_out]() @always_inline("nodebug") fn reduce_max[size_out: Int = 1](self) -> SIMD[type, size_out]: diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 6754ab58e1..09e172ebe5 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1525,6 +1525,12 @@ def test_modf(): assert_almost_equal(f64[1], -0.5) +def test_split(): + var tup = SIMD[DType.index, 8](1, 2, 3, 4, 5, 6, 7, 8).split() + assert_equal(tup[0], SIMD[DType.index, 4](1, 2, 3, 4)) + assert_equal(tup[1], SIMD[DType.index, 4](5, 6, 7, 8)) + + def main(): test_abs() test_add() @@ -1572,4 +1578,5 @@ def main(): test_trunc() test_truthy() test_modf() + test_split() # TODO: add tests for __and__, __or__, anc comparison operators From b6f8cb4b5f64e0103d288b6402b7e8121c9a0434 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 18:18:12 -0700 Subject: [PATCH 0971/2019] Refactor the floor implementation out of the modf function. Note that we are still not dispatching to the floor function, since the llvm.floor generates better assembly here. MODULAR_ORIG_COMMIT_REV_ID: bb481d898d2991ad8b2deda241434bf91cd76811 --- stdlib/src/builtin/simd.mojo | 43 ++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c0acc5fb5c..7714dac5e5 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3099,20 +3099,8 @@ fn _modf_scalar(x: Scalar) -> Tuple[__type_of(x), __type_of(x)]: return (x, x) return (Scalar[x.type](0), x) - alias integral_type = FPUtils[x.type].integral_type - alias bitwidth = bitwidthof[x.type]() - alias exponent_width = FPUtils[x.type].exponent_width() - alias mantissa_width = FPUtils[x.type].mantissa_width() - alias mask = (1 << exponent_width) - 1 - alias bias = FPUtils[x.type].exponent_bias() - - var bits = bitcast[integral_type](x) - var e = (int(bits >> mantissa_width) & mask) - bias - if e < (bitwidth - exponent_width - 1): - var upper_mask = (1 << (bitwidth - exponent_width - 1 - e)) - 1 - bits &= ~upper_mask - var r = bitcast[x.type](bits) - return (r, x - r) + var f = _floor(x) + return (f, x - f) fn _modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: @@ -3128,3 +3116,30 @@ fn _modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: result_frac[i] = tup[1] return (result_int, result_frac) + + +# ===----------------------------------------------------------------------=== # +# floor +# ===----------------------------------------------------------------------=== # + + +fn _floor(x: SIMD) -> __type_of(x): + @parameter + if x.type.is_integral(): + return x + + alias integral_type = FPUtils[x.type].integral_type + alias bitwidth = bitwidthof[x.type]() + alias exponent_width = FPUtils[x.type].exponent_width() + alias mantissa_width = FPUtils[x.type].mantissa_width() + alias mask = (1 << exponent_width) - 1 + alias bias = FPUtils[x.type].exponent_bias() + alias shift_factor = bitwidth - exponent_width - 1 + + var bits = bitcast[integral_type, x.size](x) + var e = ((bits >> mantissa_width) & mask) - bias + bits = (e < shift_factor).select( + bits & ~((1 << (shift_factor - e)) - 1), + bits, + ) + return bitcast[x.type, x.size](bits) From 1e9dfc01fe4dc9d45979d0e3e91822417dfe88fb Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 22:00:57 -0700 Subject: [PATCH 0972/2019] Reformat all mojo imports MODULAR_ORIG_COMMIT_REV_ID: 35103ef325aaf463736cb5f80e1634d561c8d9bd --- examples/deviceinfo.mojo | 25 ++++----- examples/mandelbrot.mojo | 3 +- examples/matmul.mojo | 4 +- examples/nbody.mojo | 5 +- examples/reduce.mojo | 3 +- .../algorithm/bench_elementwise.mojo | 3 +- .../benchmarks/algorithm/bench_vectorize.mojo | 10 ++-- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 5 +- stdlib/benchmarks/math/bench_math.mojo | 5 +- stdlib/benchmarks/utils/bench_formatter.mojo | 6 +-- stdlib/benchmarks/utils/bench_memmem.mojo | 6 +-- stdlib/src/base64/__init__.mojo | 2 +- stdlib/src/bit/__init__.mojo | 14 ++--- stdlib/src/builtin/bool.mojo | 3 +- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/builtin_slice.mojo | 2 +- stdlib/src/builtin/debug_assert.mojo | 3 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/format_int.mojo | 1 + stdlib/src/builtin/hash.mojo | 3 +- stdlib/src/builtin/int.mojo | 8 +-- stdlib/src/builtin/io.mojo | 7 ++- stdlib/src/builtin/object.mojo | 6 +-- stdlib/src/builtin/range.mojo | 3 +- stdlib/src/builtin/reversed.mojo | 5 +- stdlib/src/builtin/simd.mojo | 38 +++++++------ stdlib/src/builtin/sort.mojo | 5 +- stdlib/src/builtin/string.mojo | 8 +-- stdlib/src/builtin/string_literal.mojo | 6 +-- stdlib/src/builtin/tuple.mojo | 4 +- stdlib/src/collections/__init__.mojo | 5 +- stdlib/src/collections/inline_list.mojo | 3 +- stdlib/src/collections/list.mojo | 7 ++- stdlib/src/collections/vector.mojo | 3 +- stdlib/src/math/__init__.mojo | 51 ++++++------------ stdlib/src/memory/__init__.mojo | 32 ++--------- stdlib/src/memory/arc.mojo | 3 +- stdlib/src/memory/memory.mojo | 3 +- stdlib/src/memory/unsafe.mojo | 12 ++--- stdlib/src/os/__init__.mojo | 14 ++--- stdlib/src/os/os.mojo | 6 +-- stdlib/src/os/path/__init__.mojo | 2 +- stdlib/src/os/path/path.mojo | 2 +- stdlib/src/pathlib/__init__.mojo | 7 +-- stdlib/src/pathlib/path.mojo | 5 +- stdlib/src/python/_cpython.mojo | 4 +- stdlib/src/python/python.mojo | 4 +- stdlib/src/random/__init__.mojo | 10 ++-- stdlib/src/random/random.mojo | 2 +- stdlib/src/stat/__init__.mojo | 16 +++--- stdlib/src/sys/__init__.mojo | 54 +++++++++---------- stdlib/src/sys/info.mojo | 2 +- stdlib/src/tempfile/__init__.mojo | 4 +- stdlib/src/tempfile/tempfile.mojo | 3 +- stdlib/src/testing/__init__.mojo | 6 +-- stdlib/src/testing/testing.mojo | 4 +- stdlib/src/time/__init__.mojo | 2 +- stdlib/src/utils/__init__.mojo | 10 ++-- stdlib/src/utils/inline_string.mojo | 8 ++- stdlib/src/utils/numerics.mojo | 5 +- stdlib/src/utils/span.mojo | 4 +- stdlib/src/utils/static_tuple.mojo | 3 +- stdlib/src/utils/stringref.mojo | 1 - stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 4 +- stdlib/test/builtin/test_sort.mojo | 5 +- stdlib/test/builtin/test_uint.mojo | 2 +- .../test/memory/test_maybe_uninitialized.mojo | 2 +- stdlib/test/os/path/test_dirname.mojo | 1 + 70 files changed, 224 insertions(+), 288 deletions(-) diff --git a/examples/deviceinfo.mojo b/examples/deviceinfo.mojo index f731782413..fa012d5471 100644 --- a/examples/deviceinfo.mojo +++ b/examples/deviceinfo.mojo @@ -12,33 +12,28 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s | FileCheck %s -# This sample prints the current host system information using APIs from the -# sys module. -from sys.info import ( - _current_arch, - _current_target, - _triple_attr, -) - - from sys import ( - os_is_linux, - os_is_macos, - os_is_windows, - has_sse4, has_avx, has_avx2, has_avx512f, - has_vnni, has_intel_amx, has_neon, + has_sse4, + has_vnni, is_apple_m1, is_apple_m2, is_apple_m3, - num_physical_cores, num_logical_cores, + num_physical_cores, + os_is_linux, + os_is_macos, + os_is_windows, ) +# This sample prints the current host system information using APIs from the +# sys module. +from sys.info import _current_arch, _current_target, _triple_attr + def main(): var os = "" diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index a5aa1c366e..6e2e9deef3 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -13,9 +13,10 @@ # RUN: %mojo %s | FileCheck %s -import benchmark from math import iota from sys import num_physical_cores + +import benchmark from algorithm import parallelize, vectorize from complex import ComplexFloat64, ComplexSIMD diff --git a/examples/matmul.mojo b/examples/matmul.mojo index b37ccf617a..f8faf1c02e 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -15,15 +15,15 @@ # This sample demonstrates how various systems optimizations can be applied to a # naive matmul implementation in Mojo to gain significant performance speedups +from os.env import getenv from random import rand +from sys import info import benchmark from algorithm import Static2DTileUnitFunc as Tile2DFunc from algorithm import parallelize, vectorize -from sys import info from memory import memset_zero from python import Python -from os.env import getenv alias M = 512 # rows of A and C alias N = 4096 # cols of B and C diff --git a/examples/nbody.mojo b/examples/nbody.mojo index 91cfc2d0ac..37ee4fd669 100644 --- a/examples/nbody.mojo +++ b/examples/nbody.mojo @@ -15,9 +15,10 @@ # This sample implements the nbody benchmarking in # https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/nbody.html -from math import sqrt -from benchmark import run, keep from collections import List +from math import sqrt + +from benchmark import keep, run from testing import assert_almost_equal alias PI = 3.141592653589793 diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 493097523a..ae96c416b4 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -16,12 +16,13 @@ # large array of values to produce a single result. # Reductions and scans are common algorithm patterns in parallel computing. +from random import rand from time import now + from algorithm import sum from benchmark import Unit, benchmark, keep from buffer import Buffer from python import Python -from random import rand # Change these numbers to reduce on different sizes alias size_small: Int = 1 << 21 diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index bc8ba5696b..6a06366218 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -13,8 +13,9 @@ # RUN: %mojo %s -t from algorithm import elementwise -from benchmark import Bench, Bencher, BenchId, BenchConfig +from benchmark import Bench, BenchConfig, Bencher, BenchId from buffer import Buffer + from utils.index import Index, StaticIntTuple diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 389bb2abc3..2f0385ed77 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -20,21 +20,21 @@ # CHECK: Benchmark results from random import rand -from algorithm import vectorize -from benchmark import Unit, run +from algorithm import vectorize from benchmark import ( Bench, Bencher, BenchId, BenchMetric, ThroughputMeasure, + Unit, keep, + run, ) - -from memory.unsafe import DTypePointer -from memory import memcmp from buffer import Buffer +from memory import memcmp +from memory.unsafe import DTypePointer @value diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index 420632e512..f0bcf74f6f 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -t -from benchmark import Bench, Bencher, BenchId, BenchConfig +from benchmark import Bench, BenchConfig, Bencher, BenchId # ===----------------------------------------------------------------------===# diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index f35faf0c72..f089a9d31b 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -12,10 +12,11 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -t -from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run -from stdlib.collections import Dict from random import * +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run +from stdlib.collections import Dict + # ===----------------------------------------------------------------------===# # Benchmark Data diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index b48af60067..bc547b7a97 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -t -from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run -from random import * from math import * +from random import * + +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run # ===----------------------------------------------------------------------===# # Benchmark Data diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index ffe6e1dec9..db3d469ab4 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -13,12 +13,12 @@ # RUN: %mojo %s -t -from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run -from utils.stringref import _memmem, _memchr, _align_down - +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import countr_zero from builtin.dtype import _uint_type_of_width +from utils.stringref import _align_down, _memchr, _memmem + # ===----------------------------------------------------------------------===# # Benchmark Data # ===----------------------------------------------------------------------===# diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 220c43771a..ca700490dc 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -13,12 +13,12 @@ # RUN: %mojo %s -t -from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run -from utils.stringref import _memmem, _memchr, _align_down - +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import countr_zero from builtin.dtype import _uint_type_of_width +from utils.stringref import _align_down, _memchr, _memmem + # ===----------------------------------------------------------------------===# # Benchmark Data # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/base64/__init__.mojo b/stdlib/src/base64/__init__.mojo index adf2f51b40..50822dfeac 100644 --- a/stdlib/src/base64/__init__.mojo +++ b/stdlib/src/base64/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the base64 package.""" -from .base64 import b64encode, b64decode, b16encode, b16decode +from .base64 import b16decode, b16encode, b64decode, b64encode diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index f249493069..9421224e6c 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -13,16 +13,16 @@ """Implements the bit package.""" from .bit import ( - countl_zero, - countr_zero, + bit_ceil, + bit_floor, + bit_not, bit_reverse, + bit_width, byte_swap, + countl_zero, + countr_zero, + is_power_of_two, pop_count, - bit_not, - bit_width, rotate_bits_left, rotate_bits_right, - is_power_of_two, - bit_ceil, - bit_floor, ) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index f3309093c5..63e8261528 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -15,10 +15,9 @@ These are Mojo built-ins, so you don't need to import them. """ -from utils._visualizers import lldb_formatter_wrapping_type - from collections import Set +from utils._visualizers import lldb_formatter_wrapping_type # ===----------------------------------------------------------------------=== # # Boolable diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index d7eb137c5f..64016e0738 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory import Reference, UnsafePointer, LegacyPointer +from memory import LegacyPointer, Reference, UnsafePointer # ===----------------------------------------------------------------------===# # ListLiteral diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 062baf2335..09dd306471 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -15,8 +15,8 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys.intrinsics import _mlirtype_is_eq from collections import OptionalReg +from sys.intrinsics import _mlirtype_is_eq @always_inline("nodebug") diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 269e4430c4..e8be68feed 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -17,8 +17,9 @@ These are Mojo built-ins, so you don't need to import them. from os import abort +from sys import is_defined, triple_is_nvidia_cuda from sys._build import is_kernels_debug_build -from sys import triple_is_nvidia_cuda, is_defined + from builtin._location import __call_location, _SourceLocation diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index e49f628b34..83f4af61f5 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -17,8 +17,8 @@ These are Mojo built-ins, so you don't need to import them. from sys import alignof, sizeof +from memory import UnsafePointer, memcmp, memcpy from memory.memory import _free -from memory import memcmp, memcpy, UnsafePointer # ===----------------------------------------------------------------------===# # Error diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index b9e99cf423..a36c9397fd 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import Optional + from utils import InlineArray alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index b211f68b1d..862d6f7724 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -25,16 +25,15 @@ There are a few main tools in this module: These are useful helpers to specialize for the general bytes implementation. """ -from builtin.dtype import _uint_type_of_width import random from sys.ffi import _get_global +from builtin.dtype import _uint_type_of_width from memory import memcpy, memset_zero, stack_allocation # TODO remove this import onece InlineArray is moved to collections from utils import InlineArray - # ===----------------------------------------------------------------------=== # # Implementation # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 6a831f0eef..d91e538fe0 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -18,15 +18,15 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement from builtin._math import Ceilable, CeilDivable, Floorable, Truncable +from builtin.format_int import _try_write_int from builtin.hash import _hash_simd -from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf -from builtin.format_int import _try_write_int from builtin.simd import _format_scalar +from builtin.string import _calc_initial_buffer_size -from utils._visualizers import lldb_formatter_wrapping_type -from utils._format import Formattable, Formatter from utils import InlineArray +from utils._format import Formattable, Formatter +from utils._visualizers import lldb_formatter_wrapping_type # ===----------------------------------------------------------------------=== # # Indexer diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 2c91e0c860..089bae9f7b 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -17,21 +17,20 @@ These are Mojo built-ins, so you don't need to import them. from sys import ( bitwidthof, - os_is_windows, - triple_is_nvidia_cuda, external_call, + os_is_windows, stdout, + triple_is_nvidia_cuda, ) -from builtin.dtype import _get_dtype_printf_format from builtin.builtin_list import _LITRefPackHelper +from builtin.dtype import _get_dtype_printf_format from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, unroll from utils._format import Formattable, Formatter, write_to - # ===----------------------------------------------------------------------=== # # _file_handle # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 0e473831ef..808d26de2d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -16,13 +16,11 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import Dict, List - from sys.intrinsics import _type_is_eq -from memory import memcmp, memcpy -from memory import Arc +from memory import Arc, memcmp, memcpy -from utils import StringRef, unroll, Variant +from utils import StringRef, Variant, unroll # ===----------------------------------------------------------------------=== # # _ObjectImpl diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 4a90f788db..464bd9455e 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -16,10 +16,9 @@ These are Mojo built-ins, so you don't need to import them. """ -from python import PythonObject - # FIXME(MOCO-658): Explicit conformance to these traits shouldn't be needed. from builtin._stubs import _IntIterable, _StridedIterable +from python import PythonObject # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index f8cb7996df..3f69e34ca2 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -15,11 +15,10 @@ These are Mojo built-ins, so you don't need to import them. """ -from .range import _StridedRange - +from collections.dict import _DictEntryIter, _DictKeyIter, _DictValueIter from collections.list import _ListIter -from collections.dict import _DictKeyIter, _DictValueIter, _DictEntryIter +from .range import _StridedRange # ===----------------------------------------------------------------------=== # # Reversible diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7714dac5e5..d0452645fc 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -15,42 +15,40 @@ These are Mojo built-ins, so you don't need to import them. """ -from bit import pop_count +import math from sys import ( - llvm_intrinsic, + PrefetchOptions, + _RegisterPackType, has_neon, is_x86, - triple_is_nvidia_cuda, - simdwidthof, - _RegisterPackType, - PrefetchOptions, + llvm_intrinsic, prefetch, + simdwidthof, + triple_is_nvidia_cuda, ) +from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from memory import bitcast -from utils.numerics import ( - FPUtils, - isnan as _isnan, - nan as _nan, - max_finite as _max_finite, - min_finite as _min_finite, - max_or_inf as _max_or_inf, - min_or_neg_inf as _min_or_neg_inf, -) -from utils._visualizers import lldb_formatter_wrapping_type from utils import InlineArray, StringSlice +from utils._visualizers import lldb_formatter_wrapping_type +from utils.numerics import FPUtils +from utils.numerics import isnan as _isnan +from utils.numerics import max_finite as _max_finite +from utils.numerics import max_or_inf as _max_or_inf +from utils.numerics import min_finite as _min_finite +from utils.numerics import min_or_neg_inf as _min_or_neg_inf +from utils.numerics import nan as _nan from .dtype import ( - _integral_type_of, _get_dtype_printf_format, + _integral_type_of, _scientific_notation_digits, ) -from .io import _snprintf_scalar, _printf, _print_fmt -from .string import _calc_initial_buffer_size, _calc_format_buffer_size -import math +from .io import _print_fmt, _printf, _snprintf_scalar +from .string import _calc_format_buffer_size, _calc_initial_buffer_size # ===----------------------------------------------------------------------=== # # Type Aliases diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index c9ed9e03fc..17991379ab 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -15,11 +15,12 @@ These are Mojo built-ins, so you don't need to import them. """ -from bit import countl_zero from collections import List -from memory import Pointer, UnsafePointer from sys import bitwidthof +from bit import countl_zero +from memory import Pointer, UnsafePointer + # ===----------------------------------------------------------------------===# # sort # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index b7062ba759..210b2a6526 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -15,15 +15,15 @@ These are Mojo built-ins, so you don't need to import them. """ -from bit import countl_zero -from collections import List, KeyElement +from collections import KeyElement, List from collections._index_normalization import normalize_index -from sys import llvm_intrinsic, bitwidthof +from sys import bitwidthof, llvm_intrinsic from sys.ffi import C_char +from bit import countl_zero from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy -from utils import StringRef, StaticIntTuple, Span, StringSlice +from utils import Span, StaticIntTuple, StringRef, StringSlice from utils._format import Formattable, Formatter, ToFormatter # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c2a9d379fa..97ab53487d 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -15,13 +15,13 @@ These are Mojo built-ins, so you don't need to import them. """ +from sys.ffi import C_char + from memory import DTypePointer from utils import StringRef -from utils._visualizers import lldb_formatter_wrapping_type from utils._format import Formattable, Formatter - -from sys.ffi import C_char +from utils._visualizers import lldb_formatter_wrapping_type from .string import _atol diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index ed47f0825b..4a770cc55e 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -15,10 +15,10 @@ These are Mojo built-ins, so you don't need to import them. """ -from utils._visualizers import lldb_formatter_wrapping_type - from sys.intrinsics import _type_is_eq +from utils._visualizers import lldb_formatter_wrapping_type + # ===----------------------------------------------------------------------===# # Tuple # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/collections/__init__.mojo b/stdlib/src/collections/__init__.mojo index c1b1617316..c1accfcc99 100644 --- a/stdlib/src/collections/__init__.mojo +++ b/stdlib/src/collections/__init__.mojo @@ -18,7 +18,4 @@ from .inline_list import InlineList from .list import List from .optional import Optional, OptionalReg from .set import Set -from .vector import ( - CollectionElement, - InlinedFixedVector, -) +from .vector import CollectionElement, InlinedFixedVector diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index f1e303d63f..f3dbb9ca0d 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -19,9 +19,10 @@ from collections import InlineList ``` """ -from utils import InlineArray from sys.intrinsics import _type_is_eq +from utils import InlineArray + # ===----------------------------------------------------------------------===# # InlineList diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 937a5365dd..cf0c258b8e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,11 +20,14 @@ from collections import List """ -from memory import UnsafePointer, Reference from sys.intrinsics import _type_is_eq -from .optional import Optional + +from memory import Reference, UnsafePointer + from utils import Span +from .optional import Optional + # ===----------------------------------------------------------------------===# # List # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index a8637dd9cf..6e81b1b012 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -19,7 +19,8 @@ from collections.vector import InlinedFixedVector ``` """ -from memory import UnsafePointer, Reference +from memory import Reference, UnsafePointer + from utils import StaticTuple # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 44c51e49b0..8ecd0dbd04 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -12,13 +12,19 @@ # ===----------------------------------------------------------------------=== # """Implements the math package.""" -from .math import ( +# In Python, these are in the math module, so we also expose them here. +from utils.numerics import inf, isfinite, isinf, isnan, nan, nextafter, ulp + +# These are not part of Python's `math` module, but we define them here. +from .math import ( # comb, # TODO: implement this; degrees, # TODO: implement this; dist, # TODO: implement this; e, # TODO: implement this; fabs, # TODO: implement this; fmod, # TODO: implement this; fsum, # TODO: implement this; isqrt, # TODO: implement this; modf, # TODO: implement this; perm, # TODO: implement this; pi, # TODO: implement this; pow, # TODO: implement this. Note that it's different from the builtin.; prod, # TODO: implement this; radians, # TODO: implement this; tau, # TODO: implement this Ceilable, CeilDivable, CeilDivableRaising, Floorable, acos, acosh, + align_down, + align_up, asin, asinh, atan, @@ -26,68 +32,43 @@ from .math import ( atanh, cbrt, ceil, - # comb, # TODO: implement this + ceildiv, copysign, cos, cosh, - # degrees, # TODO: implement this - # dist, # TODO: implement this - # e, # TODO: implement this erf, erfc, exp, exp2, expm1, - # fabs, # TODO: implement this factorial, floor, - # fmod, # TODO: implement this + fma, frexp, - # fsum, # TODO: implement this gamma, gcd, hypot, + iota, isclose, - # isqrt, # TODO: implement this + j0, + j1, lcm, ldexp, lgamma, log, - log10, log1p, log2, - # modf, # TODO: implement this - # perm, # TODO: implement this - # pi, # TODO: implement this - # pow, # TODO: implement this. Note that it's different from the builtin. - # prod, # TODO: implement this - # radians, # TODO: implement this + log10, + logb, remainder, + rsqrt, + scalb, sin, sinh, sqrt, tan, tanh, - # tau, # TODO: implement this trunc, -) - -# These are not part of Python's `math` module, but we define them here. -from .math import ( - align_down, - align_up, - ceildiv, - fma, - iota, - j0, - j1, - logb, - rsqrt, - scalb, y0, y1, ) - - -# In Python, these are in the math module, so we also expose them here. -from utils.numerics import inf, isfinite, isinf, isnan, nan, nextafter, ulp diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index d9962246cc..688d4ef0d2 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -12,30 +12,8 @@ # ===----------------------------------------------------------------------=== # """Implements the memory package.""" -from .memory import ( - memcmp, - memcpy, - memset, - memset_zero, - stack_allocation, -) - -from .unsafe_pointer import ( - UnsafePointer, -) - -from .unsafe import ( - bitcast, - Pointer, - LegacyPointer, - DTypePointer, -) - -from .reference import ( - Reference, - AddressSpace, -) - -from .arc import ( - Arc, -) +from .arc import Arc +from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation +from .reference import AddressSpace, Reference +from .unsafe import DTypePointer, LegacyPointer, Pointer, bitcast +from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 20e4b7c171..390ca81f14 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -45,8 +45,9 @@ print(Arc(String("ok"))[]) """ from os.atomic import Atomic -from memory import UnsafePointer, stack_allocation + from builtin.builtin_list import _lit_mut_cast +from memory import UnsafePointer, stack_allocation struct _ArcInner[T: Movable]: diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 662f18e562..6e8f352b68 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -21,9 +21,10 @@ from memory import memcmp from sys import llvm_intrinsic, sizeof, triple_is_nvidia_cuda -from builtin.dtype import _integral_type_of +from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace + from .unsafe import DTypePointer, LegacyPointer # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 94b4d3feac..ae2d3a353e 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -20,20 +20,14 @@ from memory import Pointer """ -from sys import ( - alignof, - bitwidthof, - simdwidthof, - sizeof, - triple_is_nvidia_cuda, -) +from sys import alignof, bitwidthof, simdwidthof, sizeof, triple_is_nvidia_cuda from sys.intrinsics import gather, scatter, strided_load, strided_store + from bit import is_power_of_two +from .maybe_uninitialized import UnsafeMaybeUninitialized from .memory import _free, _malloc from .reference import AddressSpace -from .maybe_uninitialized import UnsafeMaybeUninitialized - # ===----------------------------------------------------------------------===# # bitcast diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index d0efc581f2..3cc9a67924 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -13,18 +13,18 @@ """Implements the os package.""" from .atomic import Atomic -from .env import setenv, getenv +from .env import getenv, setenv from .fstat import lstat, stat, stat_result from .os import ( - sep, - abort, - listdir, - remove, - unlink, - SEEK_SET, SEEK_CUR, SEEK_END, + SEEK_SET, + abort, + listdir, mkdir, + remove, rmdir, + sep, + unlink, ) from .pathlike import PathLike diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index eb59f5e737..9916bb5ffc 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,11 +23,9 @@ from collections import List from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda from sys.ffi import C_char -from memory import ( - DTypePointer, -) +from memory import DTypePointer -from utils import StringRef, InlineArray +from utils import InlineArray, StringRef from .path import isdir from .pathlike import PathLike diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index 56b12d786e..ee778f121a 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -11,4 +11,4 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from .path import exists, isdir, isfile, islink, lexists, getsize, join, dirname +from .path import dirname, exists, getsize, isdir, isfile, islink, join, lexists diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 4784b7e02a..4e4364dcda 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -22,7 +22,6 @@ from os.path import isdir from stat import S_ISDIR, S_ISLNK, S_ISREG from sys import has_neon, os_is_linux, os_is_macos, os_is_windows -from ..os import sep from .. import PathLike from .._linux_aarch64 import _lstat as _lstat_linux_arm from .._linux_aarch64 import _stat as _stat_linux_arm @@ -31,6 +30,7 @@ from .._linux_x86 import _stat as _stat_linux_x86 from .._macos import _lstat as _lstat_macos from .._macos import _stat as _stat_macos from ..fstat import stat +from ..os import sep # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/pathlib/__init__.mojo b/stdlib/src/pathlib/__init__.mojo index 89c2319de5..bfc08df541 100644 --- a/stdlib/src/pathlib/__init__.mojo +++ b/stdlib/src/pathlib/__init__.mojo @@ -12,9 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the pathlib package.""" -from .path import ( - DIR_SEPARATOR, - cwd, - _dir_of_current_file, - Path, -) +from .path import DIR_SEPARATOR, Path, _dir_of_current_file, cwd diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index dcd512eca8..1f848aa39d 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -16,14 +16,13 @@ import os from os import PathLike, listdir, stat_result from sys import os_is_windows -from builtin._location import __call_location +from sys.ffi import C_char +from builtin._location import __call_location from memory import stack_allocation from utils import StringRef -from sys.ffi import C_char - alias DIR_SEPARATOR = "\\" if os_is_windows() else "/" diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index f122f9c14c..b736486c29 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -12,15 +12,15 @@ # ===----------------------------------------------------------------------=== # from os import getenv, setenv -from pathlib import Path from os.path import dirname +from pathlib import Path from sys import external_call from sys.arg import argv from sys.ffi import DLHandle from memory import DTypePointer, UnsafePointer -from utils import StringRef, InlineArray +from utils import InlineArray, StringRef # https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/compile.h#L7 alias Py_single_input = 256 diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 762f38f6fe..b2085122db 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -19,10 +19,12 @@ from python import Python ``` """ +from os.env import getenv from sys import external_call, sizeof from sys.ffi import _get_global -from os.env import getenv + from memory import UnsafePointer + from utils import StringRef from ._cpython import CPython, Py_eval_input, Py_file_input diff --git a/stdlib/src/random/__init__.mojo b/stdlib/src/random/__init__.mojo index 7222b6054e..d71e458454 100644 --- a/stdlib/src/random/__init__.mojo +++ b/stdlib/src/random/__init__.mojo @@ -13,12 +13,12 @@ """Implements the random package.""" from .random import ( - seed, + rand, + randint, + randn, + randn_float64, random_float64, random_si64, random_ui64, - randint, - rand, - randn_float64, - randn, + seed, ) diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 41455ae5f8..13e98bc260 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -19,7 +19,7 @@ from random import seed ``` """ -from sys import external_call, bitwidthof +from sys import bitwidthof, external_call from time import now from memory import DTypePointer diff --git a/stdlib/src/stat/__init__.mojo b/stdlib/src/stat/__init__.mojo index 47c8095d39..326f82daed 100644 --- a/stdlib/src/stat/__init__.mojo +++ b/stdlib/src/stat/__init__.mojo @@ -13,19 +13,19 @@ """Implements the stat package.""" from .stat import ( - S_IFMT, - S_IFDIR, - S_IFCHR, S_IFBLK, - S_IFREG, + S_IFCHR, + S_IFDIR, S_IFIFO, S_IFLNK, + S_IFMT, + S_IFREG, S_IFSOCK, - S_ISLNK, - S_ISREG, - S_ISDIR, - S_ISCHR, S_ISBLK, + S_ISCHR, + S_ISDIR, S_ISFIFO, + S_ISLNK, + S_ISREG, S_ISSOCK, ) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 911f9513cb..088da46b32 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -12,61 +12,57 @@ # ===----------------------------------------------------------------------=== # """Implements the sys package.""" +from ._io import stderr, stdout from .arg import argv from .debug import breakpointhook -from ._io import stderr, stdout -from .ffi import RTLD, DEFAULT_RTLD, DLHandle, external_call +from .ffi import DEFAULT_RTLD, RTLD, DLHandle, external_call from .info import ( - is_x86, - has_sse4, + alignof, + bitwidthof, has_avx, has_avx2, has_avx512f, - has_vnni, + has_intel_amx, has_neon, has_neon_int8_dotprod, has_neon_int8_matmul, + has_sse4, + has_vnni, is_apple_m1, is_apple_m2, is_apple_m3, is_apple_silicon, + is_big_endian, + is_little_endian, is_neoverse_n1, - has_intel_amx, - os_is_macos, + is_x86, + num_logical_cores, + num_performance_cores, + num_physical_cores, os_is_linux, + os_is_macos, os_is_windows, - triple_is_nvidia_cuda, - is_little_endian, - is_big_endian, simdbitwidth, simdbytewidth, - sizeof, - alignof, - bitwidthof, simdwidthof, - num_physical_cores, - num_logical_cores, - num_performance_cores, + sizeof, + triple_is_nvidia_cuda, ) from .intrinsics import ( - llvm_intrinsic, - gather, - scatter, - PrefetchLocality, - PrefetchRW, PrefetchCache, + PrefetchLocality, PrefetchOptions, - prefetch, + PrefetchRW, + _RegisterPackType, + compressed_store, + gather, + llvm_intrinsic, masked_load, masked_store, - compressed_store, + prefetch, + scatter, strided_load, strided_store, - _RegisterPackType, -) -from .param_env import ( - is_defined, - env_get_int, - env_get_string, ) +from .param_env import env_get_int, env_get_string, is_defined from .terminate import exit diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index e8a32a53c3..c696d67e8d 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -19,7 +19,7 @@ from sys import is_x86 ``` """ -from .ffi import external_call, _external_call_const +from .ffi import _external_call_const, external_call @always_inline("nodebug") diff --git a/stdlib/src/tempfile/__init__.mojo b/stdlib/src/tempfile/__init__.mojo index 5e62fe3b17..8ca33ac0ab 100644 --- a/stdlib/src/tempfile/__init__.mojo +++ b/stdlib/src/tempfile/__init__.mojo @@ -13,8 +13,8 @@ """Implements the tempfile package.""" from .tempfile import ( + NamedTemporaryFile, + TemporaryDirectory, gettempdir, mkdtemp, - TemporaryDirectory, - NamedTemporaryFile, ) diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 8b8dff6a36..1c2556ca56 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -19,12 +19,11 @@ from tempfile import gettempdir ``` """ -from collections import Optional import os import sys +from collections import Optional from pathlib import Path - alias TMP_MAX = 10_000 diff --git a/stdlib/src/testing/__init__.mojo b/stdlib/src/testing/__init__.mojo index 3c52ffa593..c3d2e32298 100644 --- a/stdlib/src/testing/__init__.mojo +++ b/stdlib/src/testing/__init__.mojo @@ -14,10 +14,10 @@ """Implements the testing package.""" from .testing import ( - assert_true, - assert_false, + assert_almost_equal, assert_equal, + assert_false, assert_not_equal, - assert_almost_equal, assert_raises, + assert_true, ) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 966d7d29b6..4998a96f92 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,9 +19,11 @@ from testing import assert_true ``` """ from collections import Optional -from utils.numerics import isfinite, isnan + from builtin._location import __call_location, _SourceLocation +from utils.numerics import isfinite, isnan + # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/time/__init__.mojo b/stdlib/src/time/__init__.mojo index 4362ecf492..169c2f82e2 100644 --- a/stdlib/src/time/__init__.mojo +++ b/stdlib/src/time/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the time package.""" -from .time import now, time_function, sleep +from .time import now, sleep, time_function diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 459696c36f..6436eb9853 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -12,15 +12,11 @@ # ===----------------------------------------------------------------------=== # """Implements the utils package.""" -from .index import ( - StaticIntTuple, - Index, - product, -) +from .index import Index, StaticIntTuple, product from .inline_string import InlineString from .loop import unroll from .span import Span -from .static_tuple import StaticTuple, InlineArray -from .stringref import StringRef +from .static_tuple import InlineArray, StaticTuple from .string_slice import StringSlice +from .stringref import StringRef from .variant import Variant diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index bad52eed95..18aa8a3e75 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -15,16 +15,14 @@ avoids heap allocations for short strings. """ +from collections import Optional from sys import sizeof -from memory import memcpy, LegacyPointer, UnsafePointer - -from collections import Optional +from memory import LegacyPointer, UnsafePointer, memcpy -from utils import InlineArray, Variant, StringSlice +from utils import InlineArray, StringSlice, Variant from utils._format import ToFormatter - # ===----------------------------------------------------------------------===# # InlineString # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 608504897a..7da22695a9 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -19,15 +19,14 @@ from utils.numerics import FPUtils ``` """ -from sys import llvm_intrinsic, bitwidthof, has_neon, has_sse4 -from sys.ffi import _external_call_const +from sys import bitwidthof, has_neon, has_sse4, llvm_intrinsic from sys._assembly import inlined_assembly +from sys.ffi import _external_call_const from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply from memory import UnsafePointer, bitcast - # ===----------------------------------------------------------------------=== # # FPUtils # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 8a5d531d7b..e716878b18 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -20,10 +20,10 @@ from utils import Span ``` """ -from . import InlineArray - from sys.intrinsics import _type_is_eq +from . import InlineArray + @value struct _SpanIter[ diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index db5e8e2e29..77c1626dc5 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -19,10 +19,11 @@ from utils import StaticTuple ``` """ from collections._index_normalization import normalize_index +from sys.intrinsics import _type_is_eq + from memory import Pointer from utils import unroll -from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# # Utilities diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 3584ea2d2a..f4df8b8ad6 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -19,7 +19,6 @@ from builtin.string import _atol, _isspace from memory import DTypePointer, UnsafePointer, memcmp from memory.memory import _memcmp_impl_unconstrained - # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 4ce10ad262..c323ee9f7a 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -14,8 +14,8 @@ from pathlib import Path, _dir_of_current_file -from tempfile import gettempdir from sys import os_is_windows +from tempfile import gettempdir from testing import assert_equal, assert_true diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 09e172ebe5..b20e13f6a8 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -14,17 +14,17 @@ from sys import has_neon +from builtin.simd import _modf from testing import ( + assert_almost_equal, assert_equal, assert_false, assert_not_equal, assert_true, - assert_almost_equal, ) from utils.numerics import isfinite, isinf, isnan, nan from utils.static_tuple import StaticTuple -from builtin.simd import _modf def test_cast(): diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 25891022bc..c37badc503 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -13,9 +13,8 @@ # RUN: %mojo %s from pathlib import Path, _dir_of_current_file -from sys import os_is_windows, env_get_string - -from random import random_si64, random_ui64, random_float64, seed +from random import random_float64, random_si64, random_ui64, seed +from sys import env_get_string, os_is_windows from builtin.sort import _quicksort, _small_sort from testing import assert_equal, assert_false, assert_true diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 35d79b2a32..5a40fab77f 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_false, assert_true, assert_not_equal +from testing import assert_equal, assert_false, assert_not_equal, assert_true def test_simple_uint(): diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index 1428a0a617..b19c8db2fb 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -13,8 +13,8 @@ # RUN: %mojo %s from memory.unsafe import UnsafeMaybeUninitialized +from test_utils import CopyCounter, MoveCounter, ValueDestructorRecorder from testing import assert_equal -from test_utils import MoveCounter, CopyCounter, ValueDestructorRecorder def test_maybe_uninitialized(): diff --git a/stdlib/test/os/path/test_dirname.mojo b/stdlib/test/os/path/test_dirname.mojo index 97580f295e..5b60f789ca 100644 --- a/stdlib/test/os/path/test_dirname.mojo +++ b/stdlib/test/os/path/test_dirname.mojo @@ -15,6 +15,7 @@ from os.path import dirname + from testing import assert_equal From abc68b510b381e0765bd570b3f4e3cddd4402fbd Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 16 Jun 2024 23:05:03 -0700 Subject: [PATCH 0973/2019] Use param for inplace of unroll in an effort to remove the need for the unroll function. MODULAR_ORIG_COMMIT_REV_ID: c8ed2452f9d729f753d64c54e1425f4b1fd88c6c --- stdlib/src/math/polynomial.mojo | 7 ++---- stdlib/src/utils/index.mojo | 41 +++++++++++---------------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/stdlib/src/math/polynomial.mojo b/stdlib/src/math/polynomial.mojo index bc66eab5a7..2f4473c9f6 100644 --- a/stdlib/src/math/polynomial.mojo +++ b/stdlib/src/math/polynomial.mojo @@ -90,11 +90,8 @@ fn _horner_evaluate[ var result = x.fma(c_last, c_second_from_last) @parameter - @always_inline - fn iter[idx: Int](): - alias c = coefficients[num_coefficients - 3 - idx] + for i in reversed(range(num_coefficients - 2)): + alias c = coefficients[i] result = result.fma(x, c) - unroll[iter, num_coefficients - 2]() - return result diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 364592e7a8..58eab55d4f 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -31,9 +31,6 @@ from .static_tuple import StaticTuple # ===----------------------------------------------------------------------===# -alias mlir_bool = __mlir_type.`!pop.scalar` - - @always_inline fn _reduce_and_fn(a: Bool, b: Bool) -> Bool: """Performs AND operation on two boolean inputs. @@ -83,14 +80,11 @@ fn _int_tuple_binary_apply[ var c = StaticTuple[Int, size]() - @always_inline @parameter - fn do_apply[idx: Int](): - var a_elem: Int = a.__getitem__[idx]() - var b_elem: Int = b.__getitem__[idx]() - c.__setitem__[idx](binary_fn(a_elem, b_elem)) - - unroll[do_apply, size]() + for i in range(size): + var a_elem = a.__getitem__[i]() + var b_elem = b.__getitem__[i]() + c.__setitem__[i](binary_fn(a_elem, b_elem)) return c @@ -100,8 +94,7 @@ fn _int_tuple_compare[ size: Int, comp_fn: fn (Int, Int) -> Bool, ](a: StaticTuple[Int, size], b: StaticTuple[Int, size]) -> StaticTuple[ - mlir_bool, - size, + Bool, size ]: """Applies a given element compare function to each pair of corresponding elements in two tuples and produces a tuple of Bools containing result. @@ -123,16 +116,13 @@ fn _int_tuple_compare[ Tuple containing the result. """ - var c = StaticTuple[mlir_bool, size]() + var c = StaticTuple[Bool, size]() - @always_inline @parameter - fn do_compare[idx: Int](): - var a_elem: Int = a.__getitem__[idx]() - var b_elem: Int = b.__getitem__[idx]() - c.__setitem__[idx](comp_fn(a_elem, b_elem)._as_scalar_bool()) - - unroll[do_compare, size]() + for i in range(size): + var a_elem: Int = a.__getitem__[i]() + var b_elem: Int = b.__getitem__[i]() + c.__setitem__[i](comp_fn(a_elem, b_elem)) return c @@ -141,12 +131,12 @@ fn _int_tuple_compare[ fn _bool_tuple_reduce[ size: Int, reduce_fn: fn (Bool, Bool) -> Bool, -](a: StaticTuple[mlir_bool, size], init: Bool) -> Bool: +](a: StaticTuple[Bool, size], init: Bool) -> Bool: """Reduces the tuple argument with the given reduce function and initial value. Example Usage: - var a: StaticTuple[mlir_bool, size] + var a: StaticTuple[Bool, size] var c = _bool_tuple_reduce[size, _reduce_and_fn](a, True) Parameters: @@ -163,12 +153,9 @@ fn _bool_tuple_reduce[ var c: Bool = init - @always_inline @parameter - fn do_reduce[idx: Int](): - c = reduce_fn(c, a.__getitem__[idx]()) - - unroll[do_reduce, size]() + for i in range(size): + c = reduce_fn(c, a.__getitem__[i]()) return c From e91703f04e8adf96f6dbaad7f9aad88601ea9145 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 17 Jun 2024 18:31:33 +0300 Subject: [PATCH 0974/2019] [stdlib] Add `ImplicitlyBoolable` trait This patch changes the implicit conversion rules for `Bool` by introducing a trait for types that should be implicitly passable as a `Bool`. This behavior will now be restricted to numeric types and a few other struct to avoid problems with unintented conversions. The `assert_{true,false}` testing utilities are update to take `Boolable` values, keeping it easy to test (explicitly) `Boolable` types. Fixes https://github.com/modularml/mojo/issues/2860 MODULAR_ORIG_COMMIT_REV_ID: e441b3629b18dba84c5f12f501f60c87f56b6dfc --- docs/changelog.md | 12 +++- stdlib/src/builtin/bool.mojo | 67 ++++++++++++++++++--- stdlib/src/builtin/debug_assert.mojo | 5 +- stdlib/src/builtin/float_literal.mojo | 11 +++- stdlib/src/builtin/int.mojo | 11 +++- stdlib/src/builtin/int_literal.mojo | 13 +++- stdlib/src/builtin/object.mojo | 12 +++- stdlib/src/builtin/simd.mojo | 14 ++++- stdlib/src/builtin/sort.mojo | 2 + stdlib/src/builtin/string_literal.mojo | 12 ++-- stdlib/src/memory/unsafe_pointer.mojo | 11 +++- stdlib/src/python/object.mojo | 11 +++- stdlib/src/testing/testing.mojo | 24 +++++--- stdlib/test/builtin/test_bool.mojo | 7 ++- stdlib/test/builtin/test_float_literal.mojo | 15 ++--- stdlib/test/builtin/test_int.mojo | 10 ++- stdlib/test/builtin/test_int_literal.mojo | 10 ++- stdlib/test/builtin/test_simd.mojo | 17 ++++++ stdlib/test/memory/test_unsafepointer.mojo | 15 ++++- 19 files changed, 227 insertions(+), 52 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c6933b8abb..2712bb6c74 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -147,8 +147,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) The default store size is the size of the `SIMD` value to be stored. - `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been removed - since a Slice with no end is now represented by an empty `Slice.end` option. + a constructor which accepts optional values. `Slice._has_end()` has also been + removed since a Slice with no end is now represented by an empty `Slice.end` + option. ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) ```mojo @@ -162,6 +163,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` +- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer + implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced + for types where this behavior is desired. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` @@ -176,6 +181,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD + instead. ### 🛠️ Fixed diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 63e8261528..e55f41a0ba 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -25,7 +25,8 @@ from utils._visualizers import lldb_formatter_wrapping_type trait Boolable: - """The `Boolable` trait describes a type that can be converted to a bool. + """The `Boolable` trait describes a type that can be explicitly converted to + a `Bool` or evaluated as a boolean expression in `if` or `while` conditions. This trait requires the type to implement the `__bool__()` method. For example: @@ -49,6 +50,45 @@ trait Boolable: ... +# ===----------------------------------------------------------------------=== # +# ImplicitlyBoolable +# ===----------------------------------------------------------------------=== # + + +trait ImplicitlyBoolable(Boolable): + """The `ImplicitlyBoolable` trait describes a type that can be implicitly + converted to a `Bool`. + + Types conforming to this trait can be passed to a function that expects a + `Bool` without explicitly converting to it. Accordingly, most types should + conform to `Boolable` instead, since implicit conversions to `Bool` can have + unintuitive consequences. + + This trait requires the type to implement the `__as_bool__()` method. For + example: + + ```mojo + @value + struct Foo(ImplicitlyBoolable): + var val: Bool + + fn __as_bool__(self) -> Bool: + return self.val + + fn __bool__(self) -> Bool: + return self.__as_bool__() + ``` + """ + + fn __as_bool__(self) -> Bool: + """Get the boolean representation of the value. + + Returns: + The boolean representation of the value. + """ + ... + + # ===----------------------------------------------------------------------=== # # Bool # ===----------------------------------------------------------------------=== # @@ -58,13 +98,13 @@ trait Boolable: @value @register_passable("trivial") struct Bool( - Stringable, + CollectionElementNew, ComparableCollectionElement, - Boolable, - Intable, + ImplicitlyBoolable, Indexer, - CollectionElementNew, + Intable, Representable, + Stringable, ): """The primitive Bool scalar value used in Mojo.""" @@ -101,11 +141,11 @@ struct Bool( ) @always_inline("nodebug") - fn __init__[T: Boolable](inout self, value: T): - """Implicitly convert a Boolable value to a Bool. + fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): + """Convert an ImplicitlyBoolable value to a Bool. Parameters: - T: The Boolable type. + T: The ImplicitlyBoolable type. Args: value: The boolable value. @@ -121,6 +161,15 @@ struct Bool( """ return self + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert to Bool. + + Returns: + This value. + """ + return self.__bool__() + @always_inline("nodebug") fn __mlir_i1__(self) -> __mlir_type.i1: """Convert this Bool to __mlir_type.i1. @@ -432,7 +481,7 @@ fn bool(value: None) -> Bool: @always_inline -fn bool[T: Boolable](value: T) -> Bool: +fn bool[T: Boolable, //](value: T) -> Bool: """Get the bool representation of the object. Parameters: diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index e8be68feed..36bede39ad 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -25,8 +25,8 @@ from builtin._location import __call_location, _SourceLocation @always_inline fn debug_assert[ - *stringable: Stringable -](cond: Bool, *message_parts: *stringable): + CondType: Boolable, *stringable: Stringable +](cond: CondType, *message_parts: *stringable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -37,6 +37,7 @@ fn debug_assert[ for enabling assertions in the library. Parameters: + CondType: The type of condition. stringable: The type of the message parts. Args: diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 603e0a976a..c4b356cad8 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -27,11 +27,11 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct FloatLiteral( Absable, - Boolable, Ceilable, CeilDivable, Comparable, Floorable, + ImplicitlyBoolable, Intable, Roundable, Stringable, @@ -161,6 +161,15 @@ struct FloatLiteral( """ return self != 0.0 + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """A FloatLiteral value is true if it is non-zero. + + Returns: + True if non-zero. + """ + return self.__bool__() + @always_inline("nodebug") fn __neg__(self) -> FloatLiteral: """Return the negation of the FloatLiteral value. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index d91e538fe0..7abcfe628f 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -243,7 +243,6 @@ fn int(value: String, base: Int = 10) raises -> Int: @register_passable("trivial") struct Int( Absable, - Boolable, Ceilable, CeilDivable, Comparable, @@ -251,6 +250,7 @@ struct Int( Formattable, Indexer, Intable, + ImplicitlyBoolable, KeyElement, Powable, Roundable, @@ -952,6 +952,15 @@ struct Int( """ return self != 0 + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert this Int to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index d6f11097fd..ce9c97beb6 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -20,16 +20,16 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct IntLiteral( Absable, - Boolable, Ceilable, CeilDivable, Comparable, Floorable, + ImplicitlyBoolable, + Indexer, Intable, Roundable, Stringable, Truncable, - Indexer, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -581,6 +581,15 @@ struct IntLiteral( """ return self != Self() + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert this IntLiteral to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 808d26de2d..04b1126691 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -629,7 +629,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===----------------------------------------------------------------------=== # -struct object(IntableRaising, Boolable, Stringable): +struct object(IntableRaising, ImplicitlyBoolable, Stringable): """Represents an object without a concrete type. This is the type of arguments in `def` functions that do not have a type @@ -900,6 +900,16 @@ struct object(IntableRaising, Boolable, Stringable): raise "object type cannot be converted to an integer" + fn __as_bool__(self) -> Bool: + """Performs conversion to bool according to Python semantics. Integers + and floats are true if they are non-zero, and strings and lists are true + if they are non-empty. + + Returns: + Whether the object is considered true. + """ + return self.__bool__() + @always_inline fn __str__(self) -> String: """Performs conversion to string according to Python diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d0452645fc..6293f315a4 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -135,7 +135,6 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, - Boolable, Ceilable, CeilDivable, CollectionElement, @@ -143,6 +142,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Floorable, Hashable, Intable, + ImplicitlyBoolable, Powable, Roundable, Sized, @@ -1286,6 +1286,18 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Converts the SIMD scalar into a boolean value. + + Constraints: + The size of the SIMD vector must be 1. + + Returns: + True if the SIMD scalar is non-zero and False otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __int__(self) -> Int: """Casts to the value to an Int. If there is a fractional component, diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 17991379ab..402d68c6a4 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -256,6 +256,7 @@ fn partition[ fn sort(inout buff: Pointer[Int], len: Int): """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Args: @@ -272,6 +273,7 @@ fn sort(inout buff: Pointer[Int], len: Int): fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Parameters: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 97ab53487d..11db3af8e2 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -33,14 +33,14 @@ from .string import _atol @lldb_formatter_wrapping_type @register_passable("trivial") struct StringLiteral( - Sized, - IntableRaising, - Stringable, - Representable, - KeyElement, Boolable, - Formattable, Comparable, + Formattable, + IntableRaising, + KeyElement, + Representable, + Sized, + Stringable, ): """This type represents a string literal. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index d1a760a4f3..fda8bd5f0a 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -32,7 +32,7 @@ from memory.memory import _free, _malloc struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ]( - Boolable, + ImplicitlyBoolable, CollectionElement, Stringable, Intable, @@ -322,6 +322,15 @@ struct UnsafePointer[ """ return int(self) != 0 + @always_inline + fn __as_bool__(self) -> Bool: + """Return true if the pointer is non-null. + + Returns: + Whether the pointer is null. + """ + return self.__bool__() + @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index d54e4f7c2b..ebc550df2a 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,8 +101,8 @@ struct _PyIter(Sized): @register_passable struct PythonObject( - Boolable, CollectionElement, + ImplicitlyBoolable, Indexer, Intable, KeyElement, @@ -390,6 +390,15 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyObject_IsTrue(self.py_object) == 1 + @always_inline + fn __as_bool__(self) -> Bool: + """Evaluate the boolean value of the object. + + Returns: + Whether the object evaluates as true. + """ + return self.__bool__() + fn __is__(self, other: PythonObject) -> Bool: """Test if the PythonObject is the `other` PythonObject, the same as `x is y` in Python. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 4998a96f92..edd8628831 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -76,11 +76,13 @@ fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: @always_inline -fn assert_true( - val: Bool, msg: String = "condition was unexpectedly False" -) raises: - """Asserts that the input value is True. If it is not then an - Error is raised. +fn assert_true[ + T: Boolable, // +](val: T, msg: String = "condition was unexpectedly False") raises: + """Asserts that the input value is True and raises an Error if it's not. + + Parameters: + T: The type of the value argument. Args: val: The value to assert to be True. @@ -94,11 +96,13 @@ fn assert_true( @always_inline -fn assert_false( - val: Bool, msg: String = "condition was unexpectedly True" -) raises: - """Asserts that the input value is False. If it is not then an Error is - raised. +fn assert_false[ + T: Boolable, // +](val: T, msg: String = "condition was unexpectedly True") raises: + """Asserts that the input value is False and raises an Error if it's not. + + Parameters: + T: The type of the value argument. Args: val: The value to assert to be False. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index c97ee2a827..74071d7bcc 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -34,12 +34,15 @@ struct MyTrue: fn __bool__(self) -> Bool: return True + fn __as_bool__(self) -> Bool: + return self.__bool__() + fn takes_bool(cond: Bool) -> Bool: return cond -def test_convert_from_boolable(): +def test_convert_from_implicitly_boolable(): assert_true(takes_bool(MyTrue())) assert_true(bool(MyTrue())) @@ -145,7 +148,7 @@ def test_comparisons(): def main(): test_bool_cast_to_int() test_bool_none() - test_convert_from_boolable() + test_convert_from_implicitly_boolable() test_bool_to_string() test_bool_representation() test_bitwise() diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 3991a0a4ec..f6457eacc3 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -157,15 +157,12 @@ def test_int_conversion(): assert_equal(int(4.0), 4) -def test_boolean_comparable(): - var f1 = 0.0 - assert_false(f1) +def test_bool(): + assert_false(FloatLiteral.__bool__(0.0)) + assert_false(FloatLiteral.__as_bool__(0.0)) - var f2 = 2.0 - assert_true(f2) - - var f3 = 1.0 - assert_true(f3) + assert_true(FloatLiteral.__bool__(2.0)) + assert_true(FloatLiteral.__as_bool__(2.0)) def test_equality(): @@ -205,7 +202,7 @@ def main(): test_mod() test_div_mod() test_int_conversion() - test_boolean_comparable() + test_bool() test_equality() test_is_special_value() test_abs() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 586d6e26f4..d173a285c9 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,7 +14,7 @@ from sys.info import bitwidthof -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false def test_properties(): @@ -155,6 +155,13 @@ def test_indexer(): assert_equal(987, Int(987).__index__()) +def test_bool(): + assert_true(Int(5).__bool__()) + assert_false(Int(0).__bool__()) + assert_true(Int(5).__as_bool__()) + assert_false(Int(0).__as_bool__()) + + def main(): test_properties() test_add() @@ -172,3 +179,4 @@ def main(): test_string_conversion() test_int_representation() test_indexer() + test_bool() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index b5a512d17b..6a4eb04edd 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false def test_add(): @@ -108,6 +108,13 @@ def test_divmod(): assert_equal(t[1], -1) +def test_bool(): + assert_true(IntLiteral.__bool__(5)) + assert_false(IntLiteral.__bool__(0)) + assert_true(IntLiteral.__as_bool__(5)) + assert_false(IntLiteral.__as_bool__(0)) + + def main(): test_add() test_sub() @@ -121,3 +128,4 @@ def main(): test_bit_width() test_abs() test_indexer() + test_bool() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index b20e13f6a8..2871949c76 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -214,6 +214,22 @@ def test_issue_30237(): assert_equal(result1, result2) +def test_bool(): + assert_true(Scalar[DType.bool](True).__bool__()) + assert_false(Scalar[DType.bool](False).__bool__()) + assert_true(Scalar[DType.int32](5).__bool__()) + assert_false(Scalar[DType.int32](0).__bool__()) + assert_true(Scalar[DType.float32](5.0).__bool__()) + assert_false(Scalar[DType.float32](0.0).__bool__()) + + assert_true(Scalar[DType.bool](True).__as_bool__()) + assert_false(Scalar[DType.bool](False).__as_bool__()) + assert_true(Scalar[DType.int32](5).__as_bool__()) + assert_false(Scalar[DType.int32](0).__as_bool__()) + assert_true(Scalar[DType.float32](5.0).__as_bool__()) + assert_false(Scalar[DType.float32](0.0).__as_bool__()) + + def test_truthy(): alias dtypes = ( DType.bool, @@ -1576,6 +1592,7 @@ def main(): test_sub() test_sub_with_overflow() test_trunc() + test_bool() test_truthy() test_modf() test_split() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 471c3cad42..808451fcfb 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -14,7 +14,7 @@ from memory import UnsafePointer from test_utils import ExplicitCopyOnly, MoveCounter -from testing import assert_equal, assert_not_equal, assert_true +from testing import assert_equal, assert_not_equal, assert_true, assert_false struct MoveOnlyType(Movable): @@ -192,6 +192,18 @@ def test_indexing(): assert_equal(ptr[3], 3) +def test_bool(): + var nullptr = UnsafePointer[Int]() + var ptr = UnsafePointer[Int].alloc(1) + + assert_true(ptr.__bool__()) + assert_false(nullptr.__bool__()) + assert_true(ptr.__as_bool__()) + assert_false(nullptr.__as_bool__()) + + ptr.free() + + def main(): test_address_of() @@ -209,3 +221,4 @@ def main(): test_unsafepointer_address_space() test_indexing() + test_bool() From 38e16e862c218b5c9e9c1ac5ce07347cd8fdb99b Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 17 Jun 2024 18:59:14 +0300 Subject: [PATCH 0975/2019] [mojo-stdlib] Revert "[mojo-stdlib] Add ImplicitlyBoolable trait" Reverts modularml/modular#41730 because it was automerged accidentally despite failing CI. MODULAR_ORIG_COMMIT_REV_ID: cbb1a0c5519cb3a36fc4001de2c02df42746f7a5 --- docs/changelog.md | 12 +--- stdlib/src/builtin/bool.mojo | 67 +++------------------ stdlib/src/builtin/debug_assert.mojo | 5 +- stdlib/src/builtin/float_literal.mojo | 11 +--- stdlib/src/builtin/int.mojo | 11 +--- stdlib/src/builtin/int_literal.mojo | 13 +--- stdlib/src/builtin/object.mojo | 12 +--- stdlib/src/builtin/simd.mojo | 14 +---- stdlib/src/builtin/sort.mojo | 2 - stdlib/src/builtin/string_literal.mojo | 12 ++-- stdlib/src/memory/unsafe_pointer.mojo | 11 +--- stdlib/src/python/object.mojo | 11 +--- stdlib/src/testing/testing.mojo | 24 +++----- stdlib/test/builtin/test_bool.mojo | 7 +-- stdlib/test/builtin/test_float_literal.mojo | 15 +++-- stdlib/test/builtin/test_int.mojo | 10 +-- stdlib/test/builtin/test_int_literal.mojo | 10 +-- stdlib/test/builtin/test_simd.mojo | 17 ------ stdlib/test/memory/test_unsafepointer.mojo | 15 +---- 19 files changed, 52 insertions(+), 227 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2712bb6c74..c6933b8abb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -147,9 +147,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) The default store size is the size of the `SIMD` value to be stored. - `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been - removed since a Slice with no end is now represented by an empty `Slice.end` - option. + a constructor which accepts optional values. `Slice._has_end()` has also been removed + since a Slice with no end is now represented by an empty `Slice.end` option. ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) ```mojo @@ -163,10 +162,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` -- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer - implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced - for types where this behavior is desired. - ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` @@ -181,7 +176,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD - instead. +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. ### 🛠️ Fixed diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index e55f41a0ba..63e8261528 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -25,8 +25,7 @@ from utils._visualizers import lldb_formatter_wrapping_type trait Boolable: - """The `Boolable` trait describes a type that can be explicitly converted to - a `Bool` or evaluated as a boolean expression in `if` or `while` conditions. + """The `Boolable` trait describes a type that can be converted to a bool. This trait requires the type to implement the `__bool__()` method. For example: @@ -50,45 +49,6 @@ trait Boolable: ... -# ===----------------------------------------------------------------------=== # -# ImplicitlyBoolable -# ===----------------------------------------------------------------------=== # - - -trait ImplicitlyBoolable(Boolable): - """The `ImplicitlyBoolable` trait describes a type that can be implicitly - converted to a `Bool`. - - Types conforming to this trait can be passed to a function that expects a - `Bool` without explicitly converting to it. Accordingly, most types should - conform to `Boolable` instead, since implicit conversions to `Bool` can have - unintuitive consequences. - - This trait requires the type to implement the `__as_bool__()` method. For - example: - - ```mojo - @value - struct Foo(ImplicitlyBoolable): - var val: Bool - - fn __as_bool__(self) -> Bool: - return self.val - - fn __bool__(self) -> Bool: - return self.__as_bool__() - ``` - """ - - fn __as_bool__(self) -> Bool: - """Get the boolean representation of the value. - - Returns: - The boolean representation of the value. - """ - ... - - # ===----------------------------------------------------------------------=== # # Bool # ===----------------------------------------------------------------------=== # @@ -98,13 +58,13 @@ trait ImplicitlyBoolable(Boolable): @value @register_passable("trivial") struct Bool( - CollectionElementNew, + Stringable, ComparableCollectionElement, - ImplicitlyBoolable, - Indexer, + Boolable, Intable, + Indexer, + CollectionElementNew, Representable, - Stringable, ): """The primitive Bool scalar value used in Mojo.""" @@ -141,11 +101,11 @@ struct Bool( ) @always_inline("nodebug") - fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): - """Convert an ImplicitlyBoolable value to a Bool. + fn __init__[T: Boolable](inout self, value: T): + """Implicitly convert a Boolable value to a Bool. Parameters: - T: The ImplicitlyBoolable type. + T: The Boolable type. Args: value: The boolable value. @@ -161,15 +121,6 @@ struct Bool( """ return self - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Convert to Bool. - - Returns: - This value. - """ - return self.__bool__() - @always_inline("nodebug") fn __mlir_i1__(self) -> __mlir_type.i1: """Convert this Bool to __mlir_type.i1. @@ -481,7 +432,7 @@ fn bool(value: None) -> Bool: @always_inline -fn bool[T: Boolable, //](value: T) -> Bool: +fn bool[T: Boolable](value: T) -> Bool: """Get the bool representation of the object. Parameters: diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 36bede39ad..e8be68feed 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -25,8 +25,8 @@ from builtin._location import __call_location, _SourceLocation @always_inline fn debug_assert[ - CondType: Boolable, *stringable: Stringable -](cond: CondType, *message_parts: *stringable): + *stringable: Stringable +](cond: Bool, *message_parts: *stringable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -37,7 +37,6 @@ fn debug_assert[ for enabling assertions in the library. Parameters: - CondType: The type of condition. stringable: The type of the message parts. Args: diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index c4b356cad8..603e0a976a 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -27,11 +27,11 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct FloatLiteral( Absable, + Boolable, Ceilable, CeilDivable, Comparable, Floorable, - ImplicitlyBoolable, Intable, Roundable, Stringable, @@ -161,15 +161,6 @@ struct FloatLiteral( """ return self != 0.0 - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """A FloatLiteral value is true if it is non-zero. - - Returns: - True if non-zero. - """ - return self.__bool__() - @always_inline("nodebug") fn __neg__(self) -> FloatLiteral: """Return the negation of the FloatLiteral value. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 7abcfe628f..d91e538fe0 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -243,6 +243,7 @@ fn int(value: String, base: Int = 10) raises -> Int: @register_passable("trivial") struct Int( Absable, + Boolable, Ceilable, CeilDivable, Comparable, @@ -250,7 +251,6 @@ struct Int( Formattable, Indexer, Intable, - ImplicitlyBoolable, KeyElement, Powable, Roundable, @@ -952,15 +952,6 @@ struct Int( """ return self != 0 - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Convert this Int to Bool. - - Returns: - False Bool value if the value is equal to 0 and True otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index ce9c97beb6..d6f11097fd 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -20,16 +20,16 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct IntLiteral( Absable, + Boolable, Ceilable, CeilDivable, Comparable, Floorable, - ImplicitlyBoolable, - Indexer, Intable, Roundable, Stringable, Truncable, + Indexer, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -581,15 +581,6 @@ struct IntLiteral( """ return self != Self() - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Convert this IntLiteral to Bool. - - Returns: - False Bool value if the value is equal to 0 and True otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 04b1126691..808d26de2d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -629,7 +629,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===----------------------------------------------------------------------=== # -struct object(IntableRaising, ImplicitlyBoolable, Stringable): +struct object(IntableRaising, Boolable, Stringable): """Represents an object without a concrete type. This is the type of arguments in `def` functions that do not have a type @@ -900,16 +900,6 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): raise "object type cannot be converted to an integer" - fn __as_bool__(self) -> Bool: - """Performs conversion to bool according to Python semantics. Integers - and floats are true if they are non-zero, and strings and lists are true - if they are non-empty. - - Returns: - Whether the object is considered true. - """ - return self.__bool__() - @always_inline fn __str__(self) -> String: """Performs conversion to string according to Python diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6293f315a4..d0452645fc 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -135,6 +135,7 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, + Boolable, Ceilable, CeilDivable, CollectionElement, @@ -142,7 +143,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Floorable, Hashable, Intable, - ImplicitlyBoolable, Powable, Roundable, Sized, @@ -1286,18 +1286,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Converts the SIMD scalar into a boolean value. - - Constraints: - The size of the SIMD vector must be 1. - - Returns: - True if the SIMD scalar is non-zero and False otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __int__(self) -> Int: """Casts to the value to an Int. If there is a fractional component, diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 402d68c6a4..17991379ab 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -256,7 +256,6 @@ fn partition[ fn sort(inout buff: Pointer[Int], len: Int): """Sort the buffer inplace. - The function doesn't return anything, the buffer is updated inplace. Args: @@ -273,7 +272,6 @@ fn sort(inout buff: Pointer[Int], len: Int): fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """Sort the buffer inplace. - The function doesn't return anything, the buffer is updated inplace. Parameters: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 11db3af8e2..97ab53487d 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -33,14 +33,14 @@ from .string import _atol @lldb_formatter_wrapping_type @register_passable("trivial") struct StringLiteral( - Boolable, - Comparable, - Formattable, - IntableRaising, - KeyElement, - Representable, Sized, + IntableRaising, Stringable, + Representable, + KeyElement, + Boolable, + Formattable, + Comparable, ): """This type represents a string literal. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index fda8bd5f0a..d1a760a4f3 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -32,7 +32,7 @@ from memory.memory import _free, _malloc struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ]( - ImplicitlyBoolable, + Boolable, CollectionElement, Stringable, Intable, @@ -322,15 +322,6 @@ struct UnsafePointer[ """ return int(self) != 0 - @always_inline - fn __as_bool__(self) -> Bool: - """Return true if the pointer is non-null. - - Returns: - Whether the pointer is null. - """ - return self.__bool__() - @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index ebc550df2a..d54e4f7c2b 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,8 +101,8 @@ struct _PyIter(Sized): @register_passable struct PythonObject( + Boolable, CollectionElement, - ImplicitlyBoolable, Indexer, Intable, KeyElement, @@ -390,15 +390,6 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyObject_IsTrue(self.py_object) == 1 - @always_inline - fn __as_bool__(self) -> Bool: - """Evaluate the boolean value of the object. - - Returns: - Whether the object evaluates as true. - """ - return self.__bool__() - fn __is__(self, other: PythonObject) -> Bool: """Test if the PythonObject is the `other` PythonObject, the same as `x is y` in Python. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index edd8628831..4998a96f92 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -76,13 +76,11 @@ fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: @always_inline -fn assert_true[ - T: Boolable, // -](val: T, msg: String = "condition was unexpectedly False") raises: - """Asserts that the input value is True and raises an Error if it's not. - - Parameters: - T: The type of the value argument. +fn assert_true( + val: Bool, msg: String = "condition was unexpectedly False" +) raises: + """Asserts that the input value is True. If it is not then an + Error is raised. Args: val: The value to assert to be True. @@ -96,13 +94,11 @@ fn assert_true[ @always_inline -fn assert_false[ - T: Boolable, // -](val: T, msg: String = "condition was unexpectedly True") raises: - """Asserts that the input value is False and raises an Error if it's not. - - Parameters: - T: The type of the value argument. +fn assert_false( + val: Bool, msg: String = "condition was unexpectedly True" +) raises: + """Asserts that the input value is False. If it is not then an Error is + raised. Args: val: The value to assert to be False. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 74071d7bcc..c97ee2a827 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -34,15 +34,12 @@ struct MyTrue: fn __bool__(self) -> Bool: return True - fn __as_bool__(self) -> Bool: - return self.__bool__() - fn takes_bool(cond: Bool) -> Bool: return cond -def test_convert_from_implicitly_boolable(): +def test_convert_from_boolable(): assert_true(takes_bool(MyTrue())) assert_true(bool(MyTrue())) @@ -148,7 +145,7 @@ def test_comparisons(): def main(): test_bool_cast_to_int() test_bool_none() - test_convert_from_implicitly_boolable() + test_convert_from_boolable() test_bool_to_string() test_bool_representation() test_bitwise() diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index f6457eacc3..3991a0a4ec 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -157,12 +157,15 @@ def test_int_conversion(): assert_equal(int(4.0), 4) -def test_bool(): - assert_false(FloatLiteral.__bool__(0.0)) - assert_false(FloatLiteral.__as_bool__(0.0)) +def test_boolean_comparable(): + var f1 = 0.0 + assert_false(f1) - assert_true(FloatLiteral.__bool__(2.0)) - assert_true(FloatLiteral.__as_bool__(2.0)) + var f2 = 2.0 + assert_true(f2) + + var f3 = 1.0 + assert_true(f3) def test_equality(): @@ -202,7 +205,7 @@ def main(): test_mod() test_div_mod() test_int_conversion() - test_bool() + test_boolean_comparable() test_equality() test_is_special_value() test_abs() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index d173a285c9..586d6e26f4 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,7 +14,7 @@ from sys.info import bitwidthof -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal def test_properties(): @@ -155,13 +155,6 @@ def test_indexer(): assert_equal(987, Int(987).__index__()) -def test_bool(): - assert_true(Int(5).__bool__()) - assert_false(Int(0).__bool__()) - assert_true(Int(5).__as_bool__()) - assert_false(Int(0).__as_bool__()) - - def main(): test_properties() test_add() @@ -179,4 +172,3 @@ def main(): test_string_conversion() test_int_representation() test_indexer() - test_bool() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 6a4eb04edd..b5a512d17b 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal def test_add(): @@ -108,13 +108,6 @@ def test_divmod(): assert_equal(t[1], -1) -def test_bool(): - assert_true(IntLiteral.__bool__(5)) - assert_false(IntLiteral.__bool__(0)) - assert_true(IntLiteral.__as_bool__(5)) - assert_false(IntLiteral.__as_bool__(0)) - - def main(): test_add() test_sub() @@ -128,4 +121,3 @@ def main(): test_bit_width() test_abs() test_indexer() - test_bool() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 2871949c76..b20e13f6a8 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -214,22 +214,6 @@ def test_issue_30237(): assert_equal(result1, result2) -def test_bool(): - assert_true(Scalar[DType.bool](True).__bool__()) - assert_false(Scalar[DType.bool](False).__bool__()) - assert_true(Scalar[DType.int32](5).__bool__()) - assert_false(Scalar[DType.int32](0).__bool__()) - assert_true(Scalar[DType.float32](5.0).__bool__()) - assert_false(Scalar[DType.float32](0.0).__bool__()) - - assert_true(Scalar[DType.bool](True).__as_bool__()) - assert_false(Scalar[DType.bool](False).__as_bool__()) - assert_true(Scalar[DType.int32](5).__as_bool__()) - assert_false(Scalar[DType.int32](0).__as_bool__()) - assert_true(Scalar[DType.float32](5.0).__as_bool__()) - assert_false(Scalar[DType.float32](0.0).__as_bool__()) - - def test_truthy(): alias dtypes = ( DType.bool, @@ -1592,7 +1576,6 @@ def main(): test_sub() test_sub_with_overflow() test_trunc() - test_bool() test_truthy() test_modf() test_split() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 808451fcfb..471c3cad42 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -14,7 +14,7 @@ from memory import UnsafePointer from test_utils import ExplicitCopyOnly, MoveCounter -from testing import assert_equal, assert_not_equal, assert_true, assert_false +from testing import assert_equal, assert_not_equal, assert_true struct MoveOnlyType(Movable): @@ -192,18 +192,6 @@ def test_indexing(): assert_equal(ptr[3], 3) -def test_bool(): - var nullptr = UnsafePointer[Int]() - var ptr = UnsafePointer[Int].alloc(1) - - assert_true(ptr.__bool__()) - assert_false(nullptr.__bool__()) - assert_true(ptr.__as_bool__()) - assert_false(nullptr.__as_bool__()) - - ptr.free() - - def main(): test_address_of() @@ -221,4 +209,3 @@ def main(): test_unsafepointer_address_space() test_indexing() - test_bool() From 16be7e987f528240dc3498c6939527f04d58079e Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 17 Jun 2024 09:34:10 -0700 Subject: [PATCH 0976/2019] Update changelog MODULAR_ORIG_COMMIT_REV_ID: 1985d6d776db243243929e13fb79b95755eda92a --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c6933b8abb..45a0d74c37 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -162,6 +162,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` +- The rank argument for `algorihtm.elementwise` is no longer required and is + only inferred. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` From d1c2f488b58236141fb3a381ffcdae4ad311ca91 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 17 Jun 2024 10:23:52 -0700 Subject: [PATCH 0977/2019] The ulp function was placed in a numerics file but in python it exists in the math module. Move it. MODULAR_ORIG_COMMIT_REV_ID: 7a36ee3b7a165a5cbbeb1bad4282891cf55302c9 --- docs/changelog.md | 2 ++ stdlib/src/math/math.mojo | 44 ++++++++++++++++++++++++++++ stdlib/test/utils/test_numerics.mojo | 11 ------- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 45a0d74c37..873b7f6df8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -165,6 +165,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The rank argument for `algorihtm.elementwise` is no longer required and is only inferred. +- The `ulp` function in `numerics` have been moved to the `math` module. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index dc13878052..2f29d83a39 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1939,6 +1939,50 @@ fn lcm(*values: Int) -> Int: return result +# ===----------------------------------------------------------------------=== # +# ulp +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn ulp[ + type: DType, simd_width: Int +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the ULP (units of last place) or (units of least precision) of + the number. + + Constraints: + The element type of the inpiut must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector input. + + Returns: + The ULP of x. + """ + + constrained[type.is_floating_point(), "the type must be floating point"]() + + var nan_mask = isnan(x) + var xabs = abs(x) + var inf_mask = isinf(xabs) + alias inf_val = SIMD[type, simd_width](inf[type]()) + var x2 = nextafter(xabs, inf_val) + var x2_inf_mask = isinf(x2) + + return nan_mask.select( + x, + inf_mask.select( + xabs, + x2_inf_mask.select(xabs - nextafter(xabs, -inf_val), x2 - xabs), + ), + ) + + # ===----------------------------------------------------------------------=== # # factorial # ===----------------------------------------------------------------------=== # diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index 0640d226a9..5490746a46 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -257,16 +257,6 @@ def test_nextafter(): ) -def test_ulp(): - assert_true(isnan(ulp(nan[DType.float32]()))) - assert_true(isinf(ulp(inf[DType.float32]()))) - assert_true(isinf(ulp(-inf[DType.float32]()))) - assert_almost_equal(ulp(Float64(0)), 5e-324) - assert_equal(ulp(max_finite[DType.float64]()), 1.99584030953472e292) - assert_equal(ulp(Float64(5)), 8.881784197001252e-16) - assert_equal(ulp(Float64(-5)), 8.881784197001252e-16) - - def main(): test_FPUtils() test_get_accum_type() @@ -279,4 +269,3 @@ def main(): test_min_or_neg_inf() test_neg_inf() test_nextafter() - test_ulp() From 5215b43b054eb4363eaa84edb3ce2fb3b8582436 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 17 Jun 2024 12:27:56 -0500 Subject: [PATCH 0978/2019] [External] [stdlib] Add explicit copy constructor to `Reference` (#41776) [External] [stdlib] Add explicit copy constructor to `Reference` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3037 MODULAR_ORIG_COMMIT_REV_ID: aa51841de2a909dac00171323d73348ee1c1654c --- stdlib/src/memory/reference.mojo | 12 +++++++++- stdlib/test/memory/test_reference.mojo | 31 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/memory/test_reference.mojo diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index b22c9e5666..d8bb363ddb 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -272,7 +272,7 @@ struct Reference[ type: AnyType, lifetime: AnyLifetime[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, -]: +](CollectionElementNew): """Defines a non-nullable safe reference. Parameters: @@ -310,6 +310,16 @@ struct Reference[ """ self.value = __get_mvalue_as_litref(value) + fn __init__(inout self, *, other: Self): + """Constructs a copy from another Reference. + + Note that this does **not** copy the underlying data. + + Args: + other: The `Reference` to copy. + """ + self.value = other.value + # ===------------------------------------------------------------------===# # Operator dunders # ===------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo new file mode 100644 index 0000000000..ba26997bf7 --- /dev/null +++ b/stdlib/test/memory/test_reference.mojo @@ -0,0 +1,31 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s +from testing import assert_equal + + +def test_copy_reference_explicitly(): + var a = List[Int](1, 2, 3) + + var b = Reference(a) + + var c = Reference(other=b) + + c[][0] = 4 + assert_equal(a[0], 4) + assert_equal(b[][0], 4) + assert_equal(c[][0], 4) + + +def main(): + test_copy_reference_explicitly() From 135b03ca66757577ffa53db3bc6031ad42db3826 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Mon, 17 Jun 2024 10:35:52 -0700 Subject: [PATCH 0979/2019] [stdlib] unroll function now supports range parameters Adds ZeroStartedRange, SequentialRange, and StridedRange support to the unroll builtin. MODULAR_ORIG_COMMIT_REV_ID: a93ff0e7b4a386f2a20d09dd98789a83726735ad --- stdlib/src/builtin/range.mojo | 6 -- stdlib/src/utils/loop.mojo | 162 +++++++++++++++++++++-------- stdlib/test/utils/test_unroll.mojo | 51 +++++++++ 3 files changed, 172 insertions(+), 47 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 464bd9455e..f13993d701 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -154,12 +154,6 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): var end: Int var step: Int - @always_inline("nodebug") - fn __init__(inout self, end: Int): - self.start = 0 - self.end = end - self.step = 1 - @always_inline("nodebug") fn __init__(inout self, start: Int, end: Int): self.start = start diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index 79b3f149bd..4aefa61db2 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from builtin.range import _ZeroStartingRange, _SequentialRange, _StridedRange + """Implements higher-order functions. You can import these APIs from the `utils.loop` module. For example: @@ -26,47 +28,6 @@ from utils import unroll # ===----------------------------------------------------------------------===# -@always_inline -fn unroll[ - func: fn[idx: Int] () capturing -> None, - count: Int, -](): - """Repeatedly evaluates a function `count` times. - - Parameters: - func: The function to evaluate. The function should take a single `Int` - argument, which is the loop index value. - count: A number of repetitions. - """ - - @parameter - for i in range(count): - func[i]() - - -@always_inline -fn unroll[ - func: fn[idx: Int] () raises capturing -> None, - count: Int, -]() raises: - """Repeatedly evaluates a function `count` times. - - Parameters: - func: The function to evaluate. The function should take a single `Int` - argument, which is the loop index value. - count: A number of repetitions. - """ - - @parameter - for i in range(count): - func[i]() - - -# ===----------------------------------------------------------------------===# -# unroll -# ===----------------------------------------------------------------------===# - - @always_inline fn unroll[ func: fn[idx0: Int, idx1: Int] () capturing -> None, @@ -121,3 +82,122 @@ fn unroll[ @parameter for k in range(dim2): func[i, j, k]() + + +# ===----------------------------------------------------------------------===# +# unroll _ZeroStartingRange +# ===----------------------------------------------------------------------===# + + +@always_inline +fn unroll[ + func: fn[idx: Int] () capturing -> None, + zeroStartingRange: _ZeroStartingRange, +](): + """Repeatedly evaluates a function `range` times. + + Parameters: + func: The function to evaluate. The function should take a single `Int` + argument, which is the loop index value. + zeroStartingRange: A range representing the number of single step repetitions starting from zero. + """ + + @parameter + for i in zeroStartingRange: + func[i]() + + +@always_inline +fn unroll[ + func: fn[idx: Int] () raises capturing -> None, + zeroStartingRange: _ZeroStartingRange, +]() raises: + """Repeatedly evaluates a function `range` times. + + Parameters: + func: The function to evaluate. The function should take a single `Int` + argument, which is the loop index value. + zeroStartingRange: A range representing the number of single step repetitions starting from zero. + """ + + @parameter + for i in zeroStartingRange: + func[i]() + + +# ===----------------------------------------------------------------------===# +# unroll _SequentialRange +# ===----------------------------------------------------------------------===# +@always_inline +fn unroll[ + func: fn[idx: Int] () capturing -> None, + sequentialRange: _SequentialRange, +](): + """Repeatedly evaluates a function `range` times. + + Parameters: + func: The function to evaluate. The function should take a single `Int` + argument, which is the loop index value. + sequentialRange: A range representing the number of single step repetitions from [start; end). + """ + + @parameter + for i in sequentialRange: + func[i]() + + +@always_inline +fn unroll[ + func: fn[idx: Int] () raises capturing -> None, + sequentialRange: _SequentialRange, +]() raises: + """Repeatedly evaluates a function `range` times. + + Parameters: + func: The function to evaluate. The function should take a single `Int` + argument, which is the loop index value. + sequentialRange: A range representing the number of single step repetitions from [start; end). + """ + + @parameter + for i in sequentialRange: + func[i]() + + +# ===----------------------------------------------------------------------===# +# unroll _StridedRange +# ===----------------------------------------------------------------------===# +@always_inline +fn unroll[ + func: fn[idx: Int] () capturing -> None, + stridedRange: _StridedRange, +](): + """Repeatedly evaluates a function `range` times. + + Parameters: + func: The function to evaluate. The function should take a single `Int` + argument, which is the loop index value. + stridedRange: A range representing the number of strided repetitions from [start; end). + """ + + @parameter + for i in stridedRange: + func[i]() + + +@always_inline +fn unroll[ + func: fn[idx: Int] () raises capturing -> None, + stridedRange: _StridedRange, +]() raises: + """Repeatedly evaluates a function `range` times. + + Parameters: + func: The function to evaluate. The function should take a single `Int` + argument, which is the loop index value. + stridedRange: A range representing the number of strided repetitions from [start; end). + """ + + @parameter + for i in stridedRange: + func[i]() diff --git a/stdlib/test/utils/test_unroll.mojo b/stdlib/test/utils/test_unroll.mojo index c28837635c..2929becf98 100644 --- a/stdlib/test/utils/test_unroll.mojo +++ b/stdlib/test/utils/test_unroll.mojo @@ -112,8 +112,59 @@ fn test_unroll_raises() raises: assert_equal(len(indexes_seen), 1) +def test_unroll_zero_starting_range(): + var indexes_seen = List[Int]() + + @parameter + fn func[idx: Int](): + indexes_seen.append(idx) + + unroll[func, range(6)]() + + assert_equal(indexes_seen[0], 0) + assert_equal(indexes_seen[1], 1) + assert_equal(indexes_seen[2], 2) + assert_equal(indexes_seen[3], 3) + assert_equal(indexes_seen[4], 4) + assert_equal(indexes_seen[5], 5) + assert_equal(len(indexes_seen), 6) + + +def test_unroll_sequential_range(): + var indexes_seen = List[Int]() + + @parameter + fn func[idx: Int](): + indexes_seen.append(idx) + + unroll[func, range(3, 6)]() + + assert_equal(indexes_seen[0], 3) + assert_equal(indexes_seen[1], 4) + assert_equal(indexes_seen[2], 5) + assert_equal(len(indexes_seen), 3) + + +def test_unroll_strided_range(): + var indexes_seen = List[Int]() + + @parameter + fn func[idx: Int](): + indexes_seen.append(idx) + + unroll[func, range(0, 9, 3)]() + + assert_equal(indexes_seen[0], 0) + assert_equal(indexes_seen[1], 3) + assert_equal(indexes_seen[2], 6) + assert_equal(len(indexes_seen), 3) + + fn main() raises: test_unroll() test_unroll2() test_unroll3() test_unroll_raises() + test_unroll_zero_starting_range() + test_unroll_sequential_range() + test_unroll_strided_range() From 45e7b1cb40f03bb4d8ab6e51b43b0950c025115d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 17 Jun 2024 11:19:43 -0700 Subject: [PATCH 0980/2019] This is just a wrapper around the SIMD modf function and exists in Python https://docs.python.org/3/library/math.html#math.modf. MODULAR_ORIG_COMMIT_REV_ID: be16fd32359663b88616a1087f9cb1687786c7b3 --- stdlib/src/builtin/simd.mojo | 6 +++++- stdlib/src/math/__init__.mojo | 5 +++-- stdlib/src/math/math.mojo | 19 ++++++++++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d0452645fc..ea1c6eb4b7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3102,7 +3102,11 @@ fn _modf_scalar(x: Scalar) -> Tuple[__type_of(x), __type_of(x)]: fn _modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: - constrained[x.type.is_floating_point(), "the type must be floating point"]() + constrained[x.type.is_numeric(), "the type must be numeric"]() + + @parameter + if x.type.is_integral(): + return (x, __type_of(x)(0)) var result_int = __type_of(x)() var result_frac = __type_of(x)() diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 8ecd0dbd04..ce98bc20e4 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -16,7 +16,7 @@ from utils.numerics import inf, isfinite, isinf, isnan, nan, nextafter, ulp # These are not part of Python's `math` module, but we define them here. -from .math import ( # comb, # TODO: implement this; degrees, # TODO: implement this; dist, # TODO: implement this; e, # TODO: implement this; fabs, # TODO: implement this; fmod, # TODO: implement this; fsum, # TODO: implement this; isqrt, # TODO: implement this; modf, # TODO: implement this; perm, # TODO: implement this; pi, # TODO: implement this; pow, # TODO: implement this. Note that it's different from the builtin.; prod, # TODO: implement this; radians, # TODO: implement this; tau, # TODO: implement this +from .math import ( Ceilable, CeilDivable, CeilDivableRaising, @@ -56,10 +56,11 @@ from .math import ( # comb, # TODO: implement this; degrees, # TODO: impleme ldexp, lgamma, log, + log10, log1p, log2, - log10, logb, + modf, remainder, rsqrt, scalb, diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 2f29d83a39..bbf45604a9 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -27,7 +27,7 @@ from sys.info import bitwidthof, has_avx512f, simdwidthof, triple_is_nvidia_cuda from builtin._math import * from builtin.dtype import _integral_type_of -from builtin.simd import _simd_apply +from builtin.simd import _simd_apply, _modf from utils.index import StaticIntTuple from utils.numerics import FPUtils, isnan, nan @@ -1939,6 +1939,23 @@ fn lcm(*values: Int) -> Int: return result +# ===----------------------------------------------------------------------=== # +# modf +# ===----------------------------------------------------------------------=== # + + +fn modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: + """Computes the integral and fractional part of the value. + + Args: + x: The input value. + + Returns: + A tuple containing the integral and fractional part of the value. + """ + return _modf(x) + + # ===----------------------------------------------------------------------=== # # ulp # ===----------------------------------------------------------------------=== # From b0f55c0f51b7f209da0aad8c73475df317f414a0 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Mon, 17 Jun 2024 11:49:02 -0700 Subject: [PATCH 0981/2019] None functional change. Converting the parameter names to camel case. MODULAR_ORIG_COMMIT_REV_ID: c47cf5980a90df74e5b647062529f2be24cd8e36 --- stdlib/src/utils/loop.mojo | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index 4aefa61db2..d7f0251d53 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -92,36 +92,36 @@ fn unroll[ @always_inline fn unroll[ func: fn[idx: Int] () capturing -> None, - zeroStartingRange: _ZeroStartingRange, + zero_starting_range: _ZeroStartingRange, ](): """Repeatedly evaluates a function `range` times. Parameters: func: The function to evaluate. The function should take a single `Int` argument, which is the loop index value. - zeroStartingRange: A range representing the number of single step repetitions starting from zero. + zero_starting_range: A range representing the number of single step repetitions starting from zero. """ @parameter - for i in zeroStartingRange: + for i in zero_starting_range: func[i]() @always_inline fn unroll[ func: fn[idx: Int] () raises capturing -> None, - zeroStartingRange: _ZeroStartingRange, + zero_starting_range: _ZeroStartingRange, ]() raises: """Repeatedly evaluates a function `range` times. Parameters: func: The function to evaluate. The function should take a single `Int` argument, which is the loop index value. - zeroStartingRange: A range representing the number of single step repetitions starting from zero. + zero_starting_range: A range representing the number of single step repetitions starting from zero. """ @parameter - for i in zeroStartingRange: + for i in zero_starting_range: func[i]() @@ -131,36 +131,36 @@ fn unroll[ @always_inline fn unroll[ func: fn[idx: Int] () capturing -> None, - sequentialRange: _SequentialRange, + sequential_range: _SequentialRange, ](): """Repeatedly evaluates a function `range` times. Parameters: func: The function to evaluate. The function should take a single `Int` argument, which is the loop index value. - sequentialRange: A range representing the number of single step repetitions from [start; end). + sequential_range: A range representing the number of single step repetitions from [start; end). """ @parameter - for i in sequentialRange: + for i in sequential_range: func[i]() @always_inline fn unroll[ func: fn[idx: Int] () raises capturing -> None, - sequentialRange: _SequentialRange, + sequential_range: _SequentialRange, ]() raises: """Repeatedly evaluates a function `range` times. Parameters: func: The function to evaluate. The function should take a single `Int` argument, which is the loop index value. - sequentialRange: A range representing the number of single step repetitions from [start; end). + sequential_range: A range representing the number of single step repetitions from [start; end). """ @parameter - for i in sequentialRange: + for i in sequential_range: func[i]() @@ -170,34 +170,34 @@ fn unroll[ @always_inline fn unroll[ func: fn[idx: Int] () capturing -> None, - stridedRange: _StridedRange, + strided_range: _StridedRange, ](): """Repeatedly evaluates a function `range` times. Parameters: func: The function to evaluate. The function should take a single `Int` argument, which is the loop index value. - stridedRange: A range representing the number of strided repetitions from [start; end). + strided_range: A range representing the number of strided repetitions from [start; end). """ @parameter - for i in stridedRange: + for i in strided_range: func[i]() @always_inline fn unroll[ func: fn[idx: Int] () raises capturing -> None, - stridedRange: _StridedRange, + strided_range: _StridedRange, ]() raises: """Repeatedly evaluates a function `range` times. Parameters: func: The function to evaluate. The function should take a single `Int` argument, which is the loop index value. - stridedRange: A range representing the number of strided repetitions from [start; end). + strided_range: A range representing the number of strided repetitions from [start; end). """ @parameter - for i in stridedRange: + for i in strided_range: func[i]() From 00b56c79a4c0114e4cf37c3d956ef626fc5730e6 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 17 Jun 2024 13:42:29 -0600 Subject: [PATCH 0982/2019] [stdlib] Fix `libm` detection in `math` for floats Detecting whether `libm` is supported for a given type is incorrect. Previously, it checked if the type was both float32 *and* float64, which, of course, is impossible to be true. So, we were only saying `libm` was supported for integral types. This is incorrect. Fix this to properly use *or* so that we claim libm functions are supported for both float32 and float64 types. MODULAR_ORIG_COMMIT_REV_ID: d579662bfb2d3550dab9355697847b27b93b3fd6 --- stdlib/src/math/math.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index bbf45604a9..56c53ca923 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -2049,7 +2049,7 @@ fn factorial(n: Int) -> Int: fn _type_is_libm_supported(type: DType) -> Bool: - if type is DType.float32 and type is DType.float64: + if type in (DType.float32, DType.float64): return True return type.is_integral() From a0f9e88669904a13ad2399a4c600281f81116c27 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 17 Jun 2024 13:43:55 -0600 Subject: [PATCH 0983/2019] [stdlib] Abort with message when `dlopen` fails When `dlopen` fails, it returns a null pointer. Instead of gracefully continuing in the program after this constructor call, call `dlerror` to get the error message from the previous `dlopen` call for the current thread. Abort with said message rather than continuing. MODULAR_ORIG_COMMIT_REV_ID: fe06fe24924bcd4655f0192fdd4064661fcc643f --- stdlib/src/sys/ffi.mojo | 8 +++++++- stdlib/test/sys/test_dlhandle.mojo | 11 ++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index e665cfacee..908197f11d 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -66,9 +66,15 @@ struct DLHandle(CollectionElement, Boolable): @parameter if not os_is_windows(): - self.handle = external_call["dlopen", DTypePointer[DType.int8]]( + var handle = external_call["dlopen", DTypePointer[DType.int8]]( path.unsafe_ptr(), flags ) + if handle == DTypePointer[DType.int8](): + var error_message = external_call[ + "dlerror", UnsafePointer[UInt8] + ]() + abort("dlopen failed: " + String(error_message)) + self.handle = handle else: self.handle = DTypePointer[DType.int8]() diff --git a/stdlib/test/sys/test_dlhandle.mojo b/stdlib/test/sys/test_dlhandle.mojo index ba983710bb..2392d50f1a 100644 --- a/stdlib/test/sys/test_dlhandle.mojo +++ b/stdlib/test/sys/test_dlhandle.mojo @@ -10,17 +10,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# REQUIRES: has_not +# RUN: not --crash mojo %s 2>&1 -from sys.ffi import DLHandle - -from testing import assert_false +from sys import DLHandle def check_invalid_dlhandle(): - assert_false( - DLHandle("/an/invalid/library"), "the library is not valid location" - ) + _ = DLHandle("/an/invalid/library") def main(): From 2860916ecd315eeae31308b517fbdf2ce85693f6 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Mon, 17 Jun 2024 21:38:51 -0500 Subject: [PATCH 0984/2019] [External] [Stdlib] Speedup `Dict` (changing modulus to bitshifting) (#41913) [External] [Stdlib] Speedup `Dict` (changing modulus to bitshifting) Hello, it could be a nice improvement, around +80% here (Ubuntu); Hard to tell without feedbacks, here is the benchmark used: ```mojo from time import now from random import * from sys.param_env import is_defined from __dict_simd import Dict2 alias iteration_size = 128 def main(): var result: Int=0 var start = now() var stop = now() small = Dict2[Int,Int]() start = now() for x in range(100): for i in range(iteration_size): small[i]=i for i in range(iteration_size): result += small[i] stop = now() print(stop-start, result) result = 0 small2 = Dict[Int,Int]() start = now() for x in range(100): for i in range(iteration_size): small2[i]=i for i in range(iteration_size): result += small2[i] stop = now() print(stop-start, result) ``` 486225 812800 807652 812800 --- Because the dict always augment the reserved size by `<<=1`, it is possible to use `&=(self.reserved-1)` instead of modulus :partying_face: hope you got the same improvements as here Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3071 MODULAR_ORIG_COMMIT_REV_ID: 522c3ccd50ac350ea321951ac526bdf137deaac7 --- stdlib/src/collections/dict.mojo | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 8cb3a8e3f6..9568fc1491 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -271,30 +271,30 @@ struct _DictIndex: fn get_index(self, reserved: Int, slot: Int) -> Int: if reserved <= 128: var data = self.data.bitcast[DType.int8]() - return int(Scalar.load(data, slot % reserved)) + return int(Scalar.load(data, slot & (reserved - 1))) elif reserved <= 2**16 - 2: var data = self.data.bitcast[DType.int16]() - return int(Scalar.load(data, slot % reserved)) + return int(Scalar.load(data, slot & (reserved - 1))) elif reserved <= 2**32 - 2: var data = self.data.bitcast[DType.int32]() - return int(Scalar.load(data, slot % reserved)) + return int(Scalar.load(data, slot & (reserved - 1))) else: var data = self.data.bitcast[DType.int64]() - return int(Scalar.load(data, slot % reserved)) + return int(Scalar.load(data, slot & (reserved - 1))) fn set_index(inout self, reserved: Int, slot: Int, value: Int): if reserved <= 128: var data = self.data.bitcast[DType.int8]() - return Scalar.store(data, slot % reserved, value) + return Scalar.store(data, slot & (reserved - 1), value) elif reserved <= 2**16 - 2: var data = self.data.bitcast[DType.int16]() - return Scalar.store(data, slot % reserved, value) + return Scalar.store(data, slot & (reserved - 1), value) elif reserved <= 2**32 - 2: var data = self.data.bitcast[DType.int32]() - return Scalar.store(data, slot % reserved, value) + return Scalar.store(data, slot & (reserved - 1), value) else: var data = self.data.bitcast[DType.int64]() - return Scalar.store(data, slot % reserved, value) + return Scalar.store(data, slot & (reserved - 1), value) fn __del__(owned self): self.data.free() @@ -888,10 +888,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _next_index_slot(self, inout slot: Int, inout perturb: UInt64): alias PERTURB_SHIFT = 5 perturb >>= PERTURB_SHIFT - slot = ((5 * slot) + int(perturb + 1)) % self._reserved() + slot = ((5 * slot) + int(perturb + 1)) & (self._reserved() - 1) fn _find_empty_index(self, hash: Int) -> Int: - var slot = hash % self._reserved() + var slot = hash & (self._reserved() - 1) var perturb = bitcast[DType.uint64](Int64(hash)) while True: var index = self._get_index(slot) @@ -901,7 +901,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _find_index(self, hash: Int, key: K) -> (Bool, Int, Int): # Return (found, slot, index) - var slot = hash % self._reserved() + var slot = hash & (self._reserved() - 1) var perturb = bitcast[DType.uint64](Int64(hash)) while True: var index = self._get_index(slot) From cd204589c17e16cb37f3a500b2231a6d7839ff6d Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 18 Jun 2024 13:57:49 -0500 Subject: [PATCH 0985/2019] [stdlib] cleanup: Fix remaining String Int8 => UInt8 stragglers * Remove `.bitcast[UInt8]()` calls that are no longer necessary. * Change `_OwnedStringRef` to store an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`) * Remove `hash(DTypePointer[DType.int8])`. Callers should use the `uint8` overload instead. MODULAR_ORIG_COMMIT_REV_ID: f170fed9e503c799de47df747b7212658a5b7276 --- stdlib/src/base64/base64.mojo | 3 +-- stdlib/src/builtin/error.mojo | 3 +-- stdlib/src/builtin/file.mojo | 16 +++++++-------- stdlib/src/builtin/format_int.mojo | 3 +-- stdlib/src/builtin/hash.mojo | 33 +++++------------------------- 5 files changed, 15 insertions(+), 43 deletions(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index e959930cf3..42af991a78 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -78,8 +78,7 @@ fn b64encode(str: String) -> String: @parameter @always_inline fn s(idx: Int) -> Int: - # TODO: Remove cast once transition to UInt8 string types is complete. - return int(str.unsafe_ptr().bitcast[UInt8]()[idx]) + return int(str.unsafe_ptr()[idx]) # This algorithm is based on https://arxiv.org/abs/1704.00605 var end = length - (length % 3) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 83f4af61f5..ac343d3df1 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -78,8 +78,7 @@ struct Error(Stringable, Boolable, Representable): var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy( dest=dest, - # TODO: Remove cast once string UInt8 transition is complete. - src=src.unsafe_ptr().bitcast[UInt8](), + src=src.unsafe_ptr(), count=length, ) dest[length] = 0 diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index e89c168478..d61c11add1 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -39,11 +39,11 @@ from memory import AddressSpace, DTypePointer, Pointer @register_passable struct _OwnedStringRef(Boolable): - var data: DTypePointer[DType.int8] + var data: UnsafePointer[UInt8] var length: Int fn __init__() -> _OwnedStringRef: - return Self {data: DTypePointer[DType.int8](), length: 0} + return Self {data: UnsafePointer[UInt8](), length: 0} fn __del__(owned self): if self.data: @@ -51,15 +51,13 @@ struct _OwnedStringRef(Boolable): fn consume_as_error(owned self) -> Error: var data = self.data + # Don't free self.data in our dtor. - self.data = DTypePointer[DType.int8]() - var length = self.length + self.data = UnsafePointer[UInt8]() + return Error { - data: UnsafePointer[UInt8]._from_dtype_ptr( - # TODO: Remove cast once string UInt8 transition is complete. - data.bitcast[DType.uint8]() - ), - loaded_length: -length, + data: data, + loaded_length: -self.length, } fn __bool__(self) -> Bool: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index a36c9397fd..06ed2bcf82 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -390,8 +390,7 @@ fn _try_write_int[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. var str_slice = StringSlice[__lifetime_of(buf)]( - # TODO: Remove cast after transition to UInt8 strings is complete. - unsafe_from_utf8_ptr=buf_ptr.bitcast[UInt8](), + unsafe_from_utf8_ptr=buf_ptr, len=len, ) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 862d6f7724..3f8cc02585 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -17,11 +17,11 @@ There are a few main tools in this module: - `Hashable` trait for types implementing `__hash__(self) -> Int` - `hash[T: Hashable](hashable: T) -> Int` built-in function. - A `hash()` implementation for arbitrary byte strings, - `hash(data: DTypePointer[DType.int8], n: Int) -> Int`, + `hash(data: DTypePointer[DType.uint8], n: Int) -> Int`, is the workhorse function, which implements efficient hashing via SIMD vectors. See the documentation of this function for more details on the hash implementation. -- `hash(SIMD)` and `hash(Int8)` implementations +- `hash(SIMD)` and `hash(UInt8)` implementations These are useful helpers to specialize for the general bytes implementation. """ @@ -186,27 +186,6 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: """Hash a byte array using a SIMD-modified DJBX33A hash algorithm. - Similar to `hash(bytes: DTypePointer[DType.int8], n: Int) -> Int` but - takes a `DTypePointer[DType.uint8]` instead of `DTypePointer[DType.int8]`. - See the overload for a complete description of the algorithm. - - Args: - bytes: The byte array to hash. - n: The length of the byte array. - - Returns: - A 64-bit integer hash. This hash is _not_ suitable for - cryptographic purposes, but will have good low-bit - hash collision statistical properties for common data structures. - """ - return hash(bytes.bitcast[DType.int8](), n) - - -# TODO: Remove this overload once we have finished the transition to uint8 -# for bytes. See https://github.com/modularml/mojo/issues/2317 -fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: - """Hash a byte array using a SIMD-modified hash algorithm. - _This hash function is not suitable for cryptographic purposes._ The algorithm is easy to reverse and produce deliberate hash collisions. The hash function is designed to have relatively good mixing and statistical @@ -240,7 +219,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: ```mojo from random import rand var n = 64 - var rand_bytes = DTypePointer[DType.int8].alloc(n) + var rand_bytes = DTypePointer[DType.uint8].alloc(n) rand(rand_bytes, n) hash(rand_bytes, n) ``` @@ -278,10 +257,8 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: # 3. Copy the tail data (smaller than the SIMD register) into # a final hash state update vector that's stack-allocated. if r != 0: - var remaining = InlineArray[Int8, stride](unsafe_uninitialized=True) - var ptr = DTypePointer[DType.int8]( - UnsafePointer.address_of(remaining).bitcast[Int8]() - ) + var remaining = InlineArray[UInt8, stride](unsafe_uninitialized=True) + var ptr = DTypePointer[DType.uint8](remaining.unsafe_ptr()) memcpy(ptr, bytes + k * stride, r) memset_zero(ptr + r, stride - r) # set the rest to 0 var last_value = SIMD[size=simd_width].load(ptr.bitcast[type]()) From d34cdc378d173d44d11831f931a97af1fb085713 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 18 Jun 2024 15:22:42 -0600 Subject: [PATCH 0986/2019] [stdlib] Fix algorithm benchmarks Recently, `elementwise` was taught to infer its rank, which broke one of the benchmarks. Fix that so it compiles. This exposed an issue with how we run the benchmarks in CI too. MODULAR_ORIG_COMMIT_REV_ID: c05c370857f90456baebad99445b7411c6763e89 --- stdlib/benchmarks/algorithm/bench_elementwise.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 6a06366218..34adbe7f1d 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -37,7 +37,7 @@ fn bench_elementwise[n: Int](inout b: Bencher) raises: fn func[simd_width: Int, rank: Int](idx: StaticIntTuple[rank]): vector[idx[0]] = 42 - elementwise[func, 1, 1](Index(n)) + elementwise[func, 1](Index(n)) elementwise[func=func, simd_width = simdwidthof[DType.index](), rank=1]( Index(n) ) From 3f58d87b1e490af14389abad1e3b559e2f397567 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 18 Jun 2024 14:31:51 -0700 Subject: [PATCH 0987/2019] [stdlib] Implement arithmetic operators for UInt This change introduces the arithmetic operators for the Mojo UInt builtin. MODULAR_ORIG_COMMIT_REV_ID: b9e5e7e151484f5b7860a663303a658f32adf27a --- stdlib/src/builtin/math.mojo | 19 + stdlib/src/builtin/uint.mojo | 578 ++++++++++++++++++++++++++++- stdlib/test/builtin/test_uint.mojo | 144 +++++++ 3 files changed, 738 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index e8e3eb5d90..47af0345e5 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -117,6 +117,25 @@ fn divmod(numerator: Int, denominator: Int) -> Tuple[Int, Int]: return numerator.__divmod__(denominator) +fn divmod(numerator: UInt, denominator: UInt) -> Tuple[UInt, UInt]: + """Performs integer division and returns the quotient and the remainder. + + Currently supported only for integers. Support for more standard library + types like Int8, Int16... is planned. + + This method calls `a.__divmod__(b)`, thus, the actual implementation of + divmod should go in the `__divmod__` method of the struct of `a`. + + Args: + numerator: The dividend. + denominator: The divisor. + + Returns: + A `Tuple` containing the quotient and the remainder. + """ + return numerator.__divmod__(denominator) + + # ===----------------------------------------------------------------------=== # # max # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 0788a2d33b..9fb70a8d11 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -30,12 +30,19 @@ struct UInt(Stringable, Representable): `UInt8`, `UInt16`, `UInt32`, or `UInt64`. """ + alias MAX = (2 ** bitwidthof[DType.index]()) - 1 + """Returns the maximum integer value.""" + + alias MIN = UInt(0) + """Returns the minimum value of type.""" + var value: __mlir_type.index """The underlying storage for the integer value. + Note that it is the same type as the `Int.value` field. MLIR doesn't differentiate between signed and unsigned integers - when it comes to storing them with the index dialect. + when it comes to storing them with the index dialect. The difference is in the operations that are performed on them, which have signed and unsigned variants. """ @@ -47,7 +54,7 @@ struct UInt(Stringable, Representable): @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.index): - """Construct Int from the given index value. + """Construct UInt from the given index value. Args: value: The init value. @@ -56,7 +63,7 @@ struct UInt(Stringable, Representable): @always_inline("nodebug") fn __init__(inout self, value: IntLiteral): - """Construct Int from the given IntLiteral value. + """Construct UInt from the given IntLiteral value. Args: value: The init value. @@ -131,3 +138,568 @@ struct UInt(Stringable, Representable): pred = __mlir_attr.`#index` ](self.value, rhs.value) ) + + @always_inline("nodebug") + fn __add__(self, rhs: UInt) -> UInt: + """Return `self + rhs`. + + Args: + rhs: The value to add. + + Returns: + `self + rhs` value. + """ + return __mlir_op.`index.add`(self.value, rhs.value) + + @always_inline("nodebug") + fn __sub__(self, rhs: UInt) -> UInt: + """Return `self - rhs`. + + Args: + rhs: The value to subtract. + + Returns: + `self - rhs` value. + """ + return __mlir_op.`index.sub`(self.value, rhs.value) + + @always_inline("nodebug") + fn __mul__(self, rhs: UInt) -> UInt: + """Return `self * rhs`. + + Args: + rhs: The value to multiply with. + + Returns: + `self * rhs` value. + """ + return __mlir_op.`index.mul`(self.value, rhs.value) + + fn __truediv__(self, rhs: UInt) -> Float64: + """Return the floating point division of `self` and `rhs`. + + Args: + rhs: The value to divide on. + + Returns: + `float(self)/float(rhs)` value. + """ + return Float64(self) / Float64(rhs) + + @always_inline("nodebug") + fn __floordiv__(self, rhs: UInt) -> UInt: + """Return the division of `self` and `rhs` rounded down to the nearest + integer. + + Args: + rhs: The value to divide on. + + Returns: + `floor(self/rhs)` value. + """ + if rhs == 0: + # this should raise an exception. + return 0 + var div: UInt = self._positive_div(rhs) + return div + + @always_inline("nodebug") + fn __mod__(self, rhs: UInt) -> UInt: + """Return the remainder of self divided by rhs. + + Args: + rhs: The value to divide on. + + Returns: + The remainder of dividing self by rhs. + """ + if rhs == 0: + # this should raise an exception. + return 0 + return self._positive_rem(rhs) + + @always_inline("nodebug") + fn __divmod__(self, rhs: UInt) -> Tuple[UInt, UInt]: + """Computes both the quotient and remainder using integer division. + + Args: + rhs: The value to divide on. + + Returns: + The quotient and remainder as a `Tuple(self // rhs, self % rhs)`. + """ + if rhs == 0: + return Tuple[UInt, UInt](0, 0) + var div: UInt = self._positive_div(rhs) + return div, self._positive_rem(rhs) + + @always_inline("nodebug") + fn __pow__(self, exp: Self) -> Self: + """Return the value raised to the power of the given exponent. + + Computes the power of an integer using the Russian Peasant Method. + + Args: + exp: The exponent value. + + Returns: + The value of `self` raised to the power of `exp`. + """ + if exp < 0: + # Not defined for Integers, this should raise an + # exception. + return 0 + var res: UInt = 1 + var x = self + var n = exp + while n > 0: + if n & 1 != 0: + res *= x + x *= x + n >>= 1 + return res + + @always_inline("nodebug") + fn __lshift__(self, rhs: UInt) -> UInt: + """Return `self << rhs`. + + Args: + rhs: The value to shift with. + + Returns: + `self << rhs`. + """ + if rhs < 0: + # this should raise an exception. + return 0 + return __mlir_op.`index.shl`(self.value, rhs.value) + + @always_inline("nodebug") + fn __rshift__(self, rhs: UInt) -> UInt: + """Return `self >> rhs`. + + Args: + rhs: The value to shift with. + + Returns: + `self >> rhs`. + """ + if rhs < 0: + # this should raise an exception. + return 0 + return __mlir_op.`index.shru`(self.value, rhs.value) + + @always_inline("nodebug") + fn __and__(self, rhs: UInt) -> UInt: + """Return `self & rhs`. + + Args: + rhs: The RHS value. + + Returns: + `self & rhs`. + """ + return __mlir_op.`index.and`(self.value, rhs.value) + + @always_inline("nodebug") + fn __xor__(self, rhs: UInt) -> UInt: + """Return `self ^ rhs`. + + Args: + rhs: The RHS value. + + Returns: + `self ^ rhs`. + """ + return __mlir_op.`index.xor`(self.value, rhs.value) + + @always_inline("nodebug") + fn __or__(self, rhs: UInt) -> UInt: + """Return `self | rhs`. + + Args: + rhs: The RHS value. + + Returns: + `self | rhs`. + """ + return __mlir_op.`index.or`(self.value, rhs.value) + + # ===----------------------------------------------------------------------===# + # In place operations. + # ===----------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __iadd__(inout self, rhs: UInt): + """Compute `self + rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self + rhs + + @always_inline("nodebug") + fn __isub__(inout self, rhs: UInt): + """Compute `self - rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self - rhs + + @always_inline("nodebug") + fn __imul__(inout self, rhs: UInt): + """Compute self*rhs and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self * rhs + + fn __itruediv__(inout self, rhs: UInt): + """Compute `self / rhs`, convert to int, and save the result in self. + + Since `floor(self / rhs)` is equivalent to `self // rhs`, this yields + the same as `__ifloordiv__`. + + Args: + rhs: The RHS value. + """ + self = self // rhs + + @always_inline("nodebug") + fn __ifloordiv__(inout self, rhs: UInt): + """Compute `self // rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self // rhs + + fn __imod__(inout self, rhs: UInt): + """Compute `self % rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self % rhs + + @always_inline("nodebug") + fn __ipow__(inout self, rhs: UInt): + """Compute `pow(self, rhs)` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self**rhs + + @always_inline("nodebug") + fn __ilshift__(inout self, rhs: UInt): + """Compute `self << rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self << rhs + + @always_inline("nodebug") + fn __irshift__(inout self, rhs: UInt): + """Compute `self >> rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self >> rhs + + @always_inline("nodebug") + fn __iand__(inout self, rhs: UInt): + """Compute `self & rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self & rhs + + @always_inline("nodebug") + fn __ixor__(inout self, rhs: UInt): + """Compute `self ^ rhs` and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self ^ rhs + + @always_inline("nodebug") + fn __ior__(inout self, rhs: UInt): + """Compute self|rhs and save the result in self. + + Args: + rhs: The RHS value. + """ + self = self | rhs + + # ===----------------------------------------------------------------------===# + # Reversed operations + # ===----------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __radd__(self, value: Self) -> Self: + """Return `value + self`. + + Args: + value: The other value. + + Returns: + `value + self`. + """ + return self + value + + @always_inline("nodebug") + fn __rsub__(self, value: UInt) -> UInt: + """Return `value - self`. + + Args: + value: The other value. + + Returns: + `value - self`. + """ + return value - self + + @always_inline("nodebug") + fn __rmul__(self, value: UInt) -> UInt: + """Return `value * self`. + + Args: + value: The other value. + + Returns: + `value * self`. + """ + return self * value + + @always_inline("nodebug") + fn __rfloordiv__(self, value: UInt) -> UInt: + """Return `value // self`. + + Args: + value: The other value. + + Returns: + `value // self`. + """ + return value // self + + @always_inline("nodebug") + fn __rmod__(self, value: UInt) -> UInt: + """Return `value % self`. + + Args: + value: The other value. + + Returns: + `value % self`. + """ + return value % self + + @always_inline("nodebug") + fn __rpow__(self, value: UInt) -> UInt: + """Return `pow(value,self)`. + + Args: + value: The other value. + + Returns: + `pow(value,self)`. + """ + return value**self + + @always_inline("nodebug") + fn __rlshift__(self, value: UInt) -> UInt: + """Return `value << self`. + + Args: + value: The other value. + + Returns: + `value << self`. + """ + return value << self + + @always_inline("nodebug") + fn __rrshift__(self, value: UInt) -> UInt: + """Return `value >> self`. + + Args: + value: The other value. + + Returns: + `value >> self`. + """ + return value >> self + + @always_inline("nodebug") + fn __rand__(self, value: UInt) -> UInt: + """Return `value & self`. + + Args: + value: The other value. + + Returns: + `value & self`. + """ + return value & self + + @always_inline("nodebug") + fn __ror__(self, value: UInt) -> UInt: + """Return `value | self`. + + Args: + value: The other value. + + Returns: + `value | self`. + """ + return value | self + + @always_inline("nodebug") + fn __rxor__(self, value: UInt) -> UInt: + """Return `value ^ self`. + + Args: + value: The other value. + + Returns: + `value ^ self`. + """ + return value ^ self + + @always_inline("nodebug") + fn _positive_div(self, rhs: UInt) -> UInt: + """Return the division of `self` and `rhs` assuming that the arguments + are both positive. + + Args: + rhs: The value to divide on. + + Returns: + The integer division of `self` and `rhs` . + """ + return __mlir_op.`index.divu`(self.value, rhs.value) + + @always_inline("nodebug") + fn __gt__(self, rhs: UInt) -> Bool: + """Compare this Int to the RHS using GT comparison. + + Args: + rhs: The other Int to compare against. + + Returns: + True if this Int is greater-than the RHS Int and False otherwise. + """ + return __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + + @always_inline("nodebug") + fn __lt__(self, rhs: UInt) -> Bool: + """Compare this Int to the RHS using LT comparison. + + Args: + rhs: The other Int to compare against. + + Returns: + True if this Int is less-than the RHS Int and False otherwise. + """ + return __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + + @always_inline("nodebug") + fn __bool__(self) -> Bool: + """Convert this Int to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self != 0 + + @always_inline("nodebug") + fn _positive_rem(self, rhs: UInt) -> UInt: + """Return the modulus of `self` and `rhs` assuming that the arguments + are both positive. + + Args: + rhs: The value to divide on. + + Returns: + The integer modulus of `self` and `rhs` . + """ + return __mlir_op.`index.remu`(self.value, rhs.value) + + @always_inline("nodebug") + fn __index__(self) -> UInt: + """Return self converted to an unsigned integer, if self is suitable for use as + an index into a list. + + For Int type this is simply the value. + + Returns: + The corresponding Int value. + """ + return self + + @always_inline("nodebug") + fn __ceil__(self) -> Self: + """Return the ceiling of the UInt value, which is itself. + + Returns: + The UInt value itself. + """ + return self + + @always_inline("nodebug") + fn __floor__(self) -> Self: + """Return the floor of the UInt value, which is itself. + + Returns: + The UInt value itself. + """ + return self + + @always_inline("nodebug") + fn __round__(self) -> Self: + """Return the rounded value of the UInt value, which is itself. + + Returns: + The UInt value itself. + """ + return self + + @always_inline("nodebug") + fn __round__(self, _ndigits: UInt) -> Self: + """Return the rounded value of the UInt value, which is itself. + Args: + ndigits: The number of digits to round to. + Returns: + The UInt value itself if ndigits >= 0 else the rounded value. + """ + return self + + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Return the truncated UInt value, which is itself. + + Returns: + The Int value itself. + """ + return self + + @always_inline("nodebug") + fn __abs__(self) -> Self: + """Return the absolute value of the UInt value. + + Returns: + The absolute value. + """ + return self diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 5a40fab77f..7ccfa5a43d 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -90,8 +90,152 @@ def test_inequality(): ) +def test_properties(): + assert_equal(UInt.MIN, UInt(0)) + if bitwidthof[DType.index]() == 32: + assert_equal(UInt.MAX, (2**32) - 1) + else: + assert_equal(UInt.MAX, (2**64) - 1) + + +def test_add(): + assert_equal(UInt.__add__(UInt(3), UInt(3)), UInt(6)) + assert_equal(UInt.__add__(UInt(-2), UInt(3)), UInt(1)) + assert_equal(UInt.__add__(UInt(2), UInt(-3)), UInt(-1)) + assert_equal(UInt.__add__(UInt(5), UInt(-5)), UInt(0)) + assert_equal(UInt.__add__(UInt(-5), UInt(-4)), UInt(-9)) + + +def test_sub(): + assert_equal(UInt.__sub__(UInt(3), UInt(3)), UInt(0)) + assert_equal(UInt.__sub__(UInt(-2), UInt(3)), UInt(-5)) + assert_equal(UInt.__sub__(UInt(2), UInt(-3)), UInt(5)) + assert_equal(UInt.__sub__(UInt(5), UInt(4)), UInt(1)) + assert_equal(UInt.__sub__(UInt(4), UInt(5)), UInt(-1)) + + +def test_div(): + var n = UInt(5) + var d = UInt(2) + assert_equal(2.5, UInt.__truediv__(n, d)) + UInt.__itruediv__(n, d) + assert_equal(UInt(2), n) + + +def test_pow(): + assert_equal(UInt(1), UInt.__pow__(UInt(3), UInt(0))) + assert_equal(UInt(27), UInt.__pow__(UInt(3), UInt(3))) + assert_equal(UInt(81), UInt.__pow__(UInt(3), UInt(4))) + + +def test_ceil(): + assert_equal(UInt.__ceil__(UInt(5)), UInt(5)) + assert_equal(UInt.__ceil__(UInt(0)), UInt(0)) + assert_equal(UInt.__ceil__(UInt(-5)), UInt(-5)) + + +def test_floor(): + assert_equal(UInt.__floor__(UInt(5)), UInt(5)) + assert_equal(UInt.__floor__(UInt(0)), UInt(0)) + assert_equal(UInt.__floor__(UInt(-5)), UInt(-5)) + + +def test_round(): + assert_equal(UInt.__round__(UInt(5)), UInt(5)) + assert_equal(UInt.__round__(UInt(0)), UInt(0)) + assert_equal(UInt.__round__(UInt(-5)), UInt(-5)) + assert_equal(UInt.__round__(UInt(5), UInt(1)), UInt(5)) + assert_equal(UInt.__round__(UInt(0), UInt(1)), UInt(0)) + assert_equal(UInt.__round__(UInt(-5), UInt(1)), UInt(-5)) + assert_equal(UInt.__round__(UInt(100), UInt(-2)), UInt(100)) + + +def test_trunc(): + assert_equal(UInt.__trunc__(UInt(5)), UInt(5)) + assert_equal(UInt.__trunc__(UInt(0)), UInt(0)) + + +def test_floordiv(): + assert_equal(UInt(1), UInt.__floordiv__(UInt(2), UInt(2))) + assert_equal(UInt(0), UInt.__floordiv__(UInt(2), UInt(3))) + assert_equal(UInt(2), UInt.__floordiv__(UInt(100), UInt(50))) + assert_equal(UInt(0), UInt.__floordiv__(UInt(2), UInt(-2))) + assert_equal(UInt(0), UInt.__floordiv__(UInt(99), UInt(-2))) + + +def test_mod(): + assert_equal(UInt(0), UInt.__mod__(UInt(99), UInt(1))) + assert_equal(UInt(0), UInt.__mod__(UInt(99), UInt(3))) + assert_equal(UInt(99), UInt.__mod__(UInt(99), UInt(-2))) + assert_equal(UInt(3), UInt.__mod__(UInt(99), UInt(8))) + assert_equal(UInt(99), UInt.__mod__(UInt(99), UInt(-8))) + assert_equal(UInt(2), UInt.__mod__(UInt(2), UInt(-1))) + assert_equal(UInt(2), UInt.__mod__(UInt(2), UInt(-2))) + assert_equal(UInt(3), UInt.__mod__(UInt(3), UInt(-2))) + assert_equal(UInt(1), UInt.__mod__(UInt(-3), UInt(2))) + + +def test_divmod(): + var a: UInt + var b: UInt + a, b = divmod(UInt(7), UInt(3)) + assert_equal(a, UInt(2)) + assert_equal(b, UInt(1)) + + a, b = divmod(UInt(0), UInt(5)) + assert_equal(a, UInt(0)) + assert_equal(b, UInt(0)) + + a, b = divmod(UInt(5), UInt(0)) + assert_equal(a, UInt(0)) + assert_equal(b, UInt(0)) + + +def test_abs(): + assert_equal(UInt(-5).__abs__(), UInt(18446744073709551611)) + assert_equal(UInt(2).__abs__(), UInt(2)) + assert_equal(UInt(0).__abs__(), UInt(0)) + + +def test_string_conversion(): + assert_equal(UInt(3).__str__(), "3") + assert_equal(UInt(-3).__str__(), "18446744073709551613") + assert_equal(UInt(0).__str__(), "0") + assert_equal(UInt(100).__str__(), "100") + assert_equal(UInt(-100).__str__(), "18446744073709551516") + + +def test_int_representation(): + assert_equal(UInt(3).__repr__(), "UInt(3)") + assert_equal(UInt(-3).__repr__(), "UInt(18446744073709551613)") + assert_equal(UInt(0).__repr__(), "UInt(0)") + assert_equal(UInt(100).__repr__(), "UInt(100)") + assert_equal(UInt(-100).__repr__(), "UInt(18446744073709551516)") + + +def test_indexer(): + assert_equal(UInt(5), UInt(5).__index__()) + assert_equal(UInt(987), UInt(987).__index__()) + + def main(): test_simple_uint() test_uint_representation() test_equality() test_inequality() + test_properties() + test_add() + test_sub() + test_div() + test_pow() + test_ceil() + test_floor() + test_round() + test_trunc() + test_floordiv() + test_mod() + test_divmod() + test_abs() + test_string_conversion() + test_int_representation() + test_indexer() From d4980063e45727aa8381ce87fdb086734478a0c6 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 18 Jun 2024 16:35:35 -0500 Subject: [PATCH 0988/2019] [stdlib] docs: Changelog new `oct` function MODULAR_ORIG_COMMIT_REV_ID: 3f66a6fd04e1de7c52c0b5a5a770936bad410457 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 873b7f6df8..bf37a42223 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -81,6 +81,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added `NamedTemporaryFile` in module `tempfile`. ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) +- Added `oct(..)` function for formatting an integer in octal. + ([PR #2914](https://github.com/modularml/mojo/pull/2914) by [@bgreni](https://github.com/bgreni)) + - Added `String.format` method. ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) From f7f902f58c5717cd691ca5e2d1494e20c59ef5f4 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 18 Jun 2024 16:40:34 -0500 Subject: [PATCH 0989/2019] [stdlib] feature: Add StaticString to replace StringLiteral args This is motivated by the work to make StringLiteral nonmaterializable. MODULAR_ORIG_COMMIT_REV_ID: 7c6f7f2614d17489b10cc7d93a26bab049119b08 --- docs/changelog.md | 5 ++++ stdlib/src/builtin/simd.mojo | 1 + stdlib/src/builtin/string.mojo | 10 +++---- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/_format.mojo | 1 + stdlib/src/utils/string_slice.mojo | 46 +++++++++++++++++++++++++++--- 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bf37a42223..c5ec4edd29 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -75,6 +75,11 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Added `C_char` type alias in `sys.ffi`. +- Added `StringSlice(..)` initializer from a `StringLiteral`. + +- Added new `StaticString` type alias. This can be used in place of + `StringLiteral` for runtime string arguments. + - Added `TemporaryDirectory` in module `tempfile`. ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ea1c6eb4b7..f0cc3e4dff 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1330,6 +1330,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if size > 1: # TODO: Fix when slice indexing is implemented on StringSlice values = StringSlice(unsafe_from_utf8=output.as_bytes_slice()[1:-1]) + return ( "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" ) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 210b2a6526..845e648e9b 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1442,7 +1442,7 @@ struct String( return copy @always_inline - fn as_bytes_slice(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes_slice(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -1459,7 +1459,7 @@ struct String( ) @always_inline - fn as_string_slice(ref [_]self: Self) -> StringSlice[__lifetime_of(self)]: + fn as_string_slice(ref [_]self) -> StringSlice[__lifetime_of(self)]: """Returns a string slice of the data owned by this string. Returns: @@ -1468,9 +1468,7 @@ struct String( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in String so this is actually # guaranteed to be valid. - return StringSlice[__lifetime_of(self)]( - unsafe_from_utf8=self.as_bytes_slice() - ) + return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) fn _byte_length(self) -> Int: """Get the string length in bytes. @@ -1478,7 +1476,7 @@ struct String( This does not include the trailing null terminator in the count. Returns: - The length of this StringLiteral in bytes, excluding null terminator. + The length of this string in bytes, excluding null terminator. """ var buffer_len = len(self._buffer) diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 6436eb9853..6894eec99c 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -17,6 +17,6 @@ from .inline_string import InlineString from .loop import unroll from .span import Span from .static_tuple import InlineArray, StaticTuple -from .string_slice import StringSlice from .stringref import StringRef +from .string_slice import StaticString, StringSlice from .variant import Variant diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index 1d156c7a9d..50a4307a61 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -102,6 +102,7 @@ struct Formatter: alias slc = literal.as_string_slice() self.write_str(slc) + # TODO: Constrain to only require an immutable StringSlice[..]` @always_inline fn write_str(inout self, str_slice: StringSlice[_]): """ diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 6922066129..defa1f8b70 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -22,11 +22,14 @@ from utils import StringSlice from utils import Span +alias StaticString = StringSlice[ImmutableStaticLifetime] +"""An immutable static string slice.""" + struct StringSlice[ is_mutable: Bool, //, lifetime: AnyLifetime[is_mutable].type, -](Stringable): +](Stringable, Sized, Formattable): """ A non-owning view to encoded string data. @@ -44,6 +47,31 @@ struct StringSlice[ # Initializers # ===------------------------------------------------------------------===# + fn __init__(inout self, literal: StringLiteral): + """Construct a new string slice from a string literal. + + Args: + literal: The literal to construct this string slice from. + """ + + # Its not legal to try to mutate a StringLiteral. String literals are + # static data. + constrained[ + not is_mutable, "cannot create mutable StringSlice of StringLiteral" + ]() + + # Since a StringLiteral has static lifetime, it will outlive + # whatever arbitrary `lifetime` the user has specified they need this + # slice to live for. + # SAFETY: + # StringLiteral is guaranteed to use UTF-8 encoding. + # FIXME(MSTDL-160): + # Ensure StringLiteral _actually_ always uses UTF-8 encoding. + self = StringSlice[lifetime]( + unsafe_from_utf8_ptr=literal.unsafe_ptr(), + len=literal._byte_length(), + ) + @always_inline fn __init__(inout self, *, owned unsafe_from_utf8: Span[UInt8, lifetime]): """ @@ -117,13 +145,23 @@ struct StringSlice[ return String(str_slice=self) fn __len__(self) -> Int: - """Returns the StringSlice byte length. + """Nominally returns the _length in Unicode codepoints_ (not bytes!). Returns: - The StringSlice byte length. + The length in Unicode codepoints. """ + # FIXME(MSTDL-160): + # Actually perform UTF-8 decoding here to count the codepoints. + return len(self._slice) - return self._byte_length() + fn format_to(self, inout writer: Formatter): + """ + Formats this string slice to the provided formatter. + + Args: + writer: The formatter to write to. + """ + writer.write_str(str_slice=self) # ===------------------------------------------------------------------===# # Methods From 9ffdf3f9629b16f5eac95edd854673b0d4282d8d Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 18 Jun 2024 14:54:24 -0700 Subject: [PATCH 0990/2019] [Docs] Fix two typos in "Death of a value" MODULAR_ORIG_COMMIT_REV_ID: c18a47a845855c15c4fd1095cfdd76034f0bd082 --- docs/manual/lifecycle/death.ipynb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index d13e357940..980c199199 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -36,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -178,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -206,7 +206,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -224,7 +224,7 @@ "\n", " fn __del__(owned self):\n", " for i in range(self.size):\n", - " (self.data + 1).destroy_pointee()\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()" ] }, @@ -244,7 +244,7 @@ "\n", "You can't just call the destructor explicitly. Because `__del__()`\n", "takes `self` as an `owned` value, and owned arguments are copied by default,\n", - "`foo._del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", + "`foo.__del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", "destroys a value, however, it passes in the original value as `self`, not a\n", "copy.\n", "\n", @@ -264,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -306,7 +306,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -337,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -437,7 +437,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ From 6f54ef7ce43f4e35f5a08efc4403be17561be631 Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 18 Jun 2024 20:56:24 -0500 Subject: [PATCH 0991/2019] [External] [stdlib] Remove use of `StaticTuple` from `test_simd.mojo` (#41939) [External] [stdlib] Remove use of `StaticTuple` from `test_simd.mojo` Co-authored-by: soraros Closes modularml/mojo#3063 MODULAR_ORIG_COMMIT_REV_ID: b61a60a0750bbb11fa56578659becf83b5d973c8 --- stdlib/test/builtin/test_simd.mojo | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index b20e13f6a8..3ba0173caa 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -24,7 +24,6 @@ from testing import ( ) from utils.numerics import isfinite, isinf, isnan, nan -from utils.static_tuple import StaticTuple def test_cast(): @@ -167,7 +166,7 @@ def test_issue_30237(): alias dtype = DType.float32 alias simd_width = 1 alias coefficients_len = 7 - alias coefficients = StaticTuple[SIMD[dtype, simd_width], coefficients_len]( + alias coefficients = InlineArray[SIMD[dtype, simd_width], coefficients_len]( 4.89352455891786e-03, 6.37261928875436e-04, 1.48572235717979e-05, From 3b9eeeaebda5c7e28b58ecb9dfe2742294d45a73 Mon Sep 17 00:00:00 2001 From: codingonion Date: Wed, 19 Jun 2024 00:18:22 -0500 Subject: [PATCH 0992/2019] [External] [docs] Fix typo in death.ipynb and traits.ipynb (#41982) [External] [docs] Fix typo in death.ipynb and traits.ipynb Fix typo in death.ipynb and traits.ipynb Co-authored-by: codingonion Closes modularml/mojo#3046 MODULAR_ORIG_COMMIT_REV_ID: f72bd5ea9f3fb41795894fe4522dafa0bbe7e1e5 --- docs/manual/traits.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 docs/manual/traits.ipynb diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb old mode 100644 new mode 100755 index dd47c7ef1c..46e44fd8c1 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -613,7 +613,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 6 9 " + "1 2 3 " ] } ], From b4dc1291344b7a4e525a3e9665ab6d457ac25f8a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 20 Jun 2024 09:54:15 -0700 Subject: [PATCH 0993/2019] [Stdlib] Do not do float division then floor Use integer division instead of doing a float division followed by a floor operation. MODULAR_ORIG_COMMIT_REV_ID: dc2b7108d0dcf952a261c71654dc2f8e31abaa5f --- stdlib/src/base64/base64.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 42af991a78..d042c27e21 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -224,7 +224,7 @@ fn b16decode(str: String) -> String: var n = len(str) debug_assert(n % 2 == 0, "Input length must be divisible by 2") - var p = List[UInt8](capacity=int(n / 2) + 1) + var p = List[UInt8](capacity=n // 2 + 1) for i in range(0, n, 2): var hi = str[i] From 452ddf9ed7701a3a1f9cdf3656d7b308adc3ed16 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 20 Jun 2024 09:54:33 -0700 Subject: [PATCH 0994/2019] [Stdlib] Remove special case for List._reverse This function is only used by a test case and had a weird pattern where the function throws but the only caller catches it. MODULAR_ORIG_COMMIT_REV_ID: cc4e42b103279e1feb66500a9eeee5c07b90b861 --- stdlib/src/collections/list.mojo | 21 +------ stdlib/test/collections/test_list.mojo | 85 -------------------------- 2 files changed, 3 insertions(+), 103 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index cf0c258b8e..e4d8536aa6 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -593,29 +593,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.size = new_size self.reserve(new_size) + @always_inline fn reverse(inout self): """Reverses the elements of the list.""" - try: - self._reverse() - except: - abort("unreachable: default _reverse start unexpectedly fails") - - # This method is private to avoid exposing the non-Pythonic `start` argument. - @always_inline - fn _reverse(inout self, start: Int = 0) raises: - """Reverses the elements of the list at positions after `start`. - - Args: - start: An integer indicating the position after which to reverse elements. - """ - var start_idx = start if start >= 0 else len(self) + start - if start_idx < 0 or start_idx > len(self): - raise "IndexError: start index out of range." - var earlier_idx = start_idx + var earlier_idx = 0 var later_idx = len(self) - 1 - var effective_len = len(self) - start_idx + var effective_len = len(self) var half_len = effective_len // 2 for _ in range(half_len): diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 9b0c5c9b2e..e4f6d86987 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -236,80 +236,6 @@ def test_list_reverse(): assert_equal(vec[0], 10) assert_equal(vec[1], 5) - # - # Test reversing the list [1, 2, 3, 4, 5] starting at the 3rd position - # to produce [1, 2, 5, 4, 3] - # - - vec = List[Int]() - vec.append(1) - vec.append(2) - vec.append(3) - vec.append(4) - vec.append(5) - - assert_equal(len(vec), 5) - assert_equal(vec[0], 1) - assert_equal(vec[1], 2) - assert_equal(vec[2], 3) - assert_equal(vec[3], 4) - assert_equal(vec[4], 5) - - vec._reverse(start=2) - - assert_equal(len(vec), 5) - assert_equal(vec[0], 1) - assert_equal(vec[1], 2) - assert_equal(vec[2], 5) - assert_equal(vec[3], 4) - assert_equal(vec[4], 3) - - # - # Test reversing the list [1, 2, 3] with negative indexes - # - - vec = List[Int]() - vec.append(1) - vec.append(2) - vec.append(3) - - vec._reverse(start=-2) - - assert_equal(len(vec), 3) - assert_equal(vec[0], 1) - assert_equal(vec[1], 3) - assert_equal(vec[2], 2) - - # - # Test reversing the list [1, 2] with out of bounds indexes - # - vec = List[Int]() - vec.append(1) - vec.append(2) - - with assert_raises(contains="IndexError"): - vec._reverse(start=-3) - - with assert_raises(contains="IndexError"): - vec._reverse(start=3) - - # - # Test edge case of reversing the list [1, 2, 3] but starting after the - # last element. - # - - vec = List[Int]() - vec.append(1) - vec.append(2) - vec.append(3) - - vec._reverse(start=len(vec)) - - assert_equal(len(vec), 3) - assert_equal(vec[0], 1) - assert_equal(vec[1], 2) - assert_equal(vec[2], 3) - def test_list_reverse_move_count(): # Create this vec with enough capacity to avoid moves due to resizing. @@ -529,17 +455,6 @@ def test_list_extend(): assert_equal(vec[4], 2) assert_equal(vec[5], 3) - vec._reverse(start=3) - - # vec == [1, 2, 3, 3, 2, 1] - assert_equal(len(vec), 6) - assert_equal(vec[0], 1) - assert_equal(vec[1], 2) - assert_equal(vec[2], 3) - assert_equal(vec[3], 3) - assert_equal(vec[4], 2) - assert_equal(vec[5], 1) - def test_list_extend_non_trivial(): # Tests three things: From 25589b5da4a865f1ed6a235f1c2757bc58b9bbdc Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 20 Jun 2024 11:58:41 -0500 Subject: [PATCH 0995/2019] [External] [stdlib] Move `builtin_list.mojo` off `unroll` (#42019) [External] [stdlib] Move `builtin_list.mojo` off `unroll` As part of migrating off of `unroll` in favor of `@parameter for`, apply the use of `@parameter for` in `builtin_list.mojo`. Co-authored-by: soraros Closes modularml/mojo#3052 MODULAR_ORIG_COMMIT_REV_ID: 80f0632ae17d42980076abe67287b0124a0203c8 --- stdlib/src/builtin/builtin_list.mojo | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 64016e0738..a04a4d83c7 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -522,21 +522,14 @@ struct VariadicPack[ # Immutable variadics never own the memory underlying them, # microoptimize out a check of _is_owned. @parameter - if not Bool(elt_is_mutable): - return - else: + if Bool(elt_is_mutable): # If the elements are unowned, just return. if not self._is_owned: return - alias len = Self.__len__() - @parameter - fn destroy_elt[i: Int](): - # destroy the elements in reverse order. - UnsafePointer.address_of(self[len - i - 1]).destroy_pointee() - - unroll[destroy_elt, len]() + for i in reversed(range(Self.__len__())): + UnsafePointer.address_of(self[i]).destroy_pointee() @always_inline @staticmethod @@ -602,11 +595,9 @@ struct VariadicPack[ """ @parameter - fn unrolled[i: Int](): + for i in range(Self.__len__()): func(self[i]) - unroll[unrolled, Self.__len__()]() - @always_inline fn each_idx[ func: fn[idx: Int, T: element_trait] (T) capturing -> None @@ -621,7 +612,5 @@ struct VariadicPack[ """ @parameter - fn unrolled[i: Int](): - func[i, element_types[i.value]](self[i]) - - unroll[unrolled, Self.__len__()]() + for i in range(Self.__len__()): + func[i](self[i]) From fc6aecf44842f97c588b346947e6c9d23dd94cdf Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 20 Jun 2024 12:00:16 -0500 Subject: [PATCH 0996/2019] [External] [stdlib] Move `hash.mojo` off `unroll` (#42020) [External] [stdlib] Move `hash.mojo` off `unroll` As part of migrating off of `unroll` in favor of `@parameter for`, apply the use of `@parameter for` in `hash.mojo`. Co-authored-by: soraros Closes modularml/mojo#3053 MODULAR_ORIG_COMMIT_REV_ID: 94a58d7314b1634f9a4dff020531c387d0890723 --- stdlib/src/builtin/hash.mojo | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 3f8cc02585..39fa33490a 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -173,13 +173,12 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: var final_data = bitcast[int_type, 1](hash_data[0]).cast[DType.uint64]() @parameter - fn hash_value[i: Int](): + for i in range(1, size): final_data = _ankerl_hash_update( final_data, - bitcast[int_type, 1](hash_data[i + 1]).cast[DType.uint64](), + bitcast[int_type, 1](hash_data[i]).cast[DType.uint64](), ) - unroll[hash_value, size - 1]() return int(final_data) From cebc67994aa55a771609122aeca4312573abc497 Mon Sep 17 00:00:00 2001 From: Scott Brenner Date: Thu, 20 Jun 2024 12:20:41 -0500 Subject: [PATCH 0997/2019] [External] [docs] Correct "Python integration" hyperlink in manual (#42002) [External] [docs] Correct "Python integration" hyperlink in manual Seem to be getting a 308 response code from the `/index` path hyperlinked here - https://docs.modular.com/mojo/manual/python/index - which inconsistently redirects me to https://docs.modular.com/mojo/manual/python, but sometimes does not and results in this Page Not Found error, is anyone else able to reproduce this? ![image](https://github.com/modularml/mojo/assets/416477/7fb01e57-e907-41b3-a04f-7b83195bd6ac) Proposing changing the hyperlink to the redirected page directly. Co-authored-by: Scott Brenner Closes modularml/mojo#3078 MODULAR_ORIG_COMMIT_REV_ID: 7afac3e2590cb8835caf847ac1f03d9263ff62a5 --- docs/manual/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/index.md b/docs/manual/index.md index 139148920e..fc1754026b 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -60,7 +60,7 @@ feedback](/mojo/community). - **Python** - - [Python integration](/mojo/manual/python/index) + - [Python integration](/mojo/manual/python/) - [Python types](/mojo/manual/python/types) - **Tools** From 2d3cd76d907cdec9c05be8d6d7ecb534b94293f7 Mon Sep 17 00:00:00 2001 From: Helehex Date: Thu, 20 Jun 2024 12:22:46 -0500 Subject: [PATCH 0998/2019] [External] [stdlib] Add `assert_is()` and `assert_is_not()` functions (#41952) [External] [stdlib] Add `assert_is()` and `assert_is_not()` functions Adds functions for asserting identity comparisons, along with some helper traits to make it possible. Having the address information in the error message isn't always helpful, but sometimes it is. Co-authored-by: Helehex Closes modularml/mojo#2807 MODULAR_ORIG_COMMIT_REV_ID: f34a4f707186d66811d4e476346bfa60ec8e0781 --- docs/changelog.md | 7 ++ stdlib/src/builtin/identifiable.mojo | 52 +++++++++++++ stdlib/src/testing/__init__.mojo | 2 + stdlib/src/testing/testing.mojo | 97 ++++++++++++++++++------- stdlib/test/testing/test_assertion.mojo | 24 +++++- 5 files changed, 151 insertions(+), 31 deletions(-) create mode 100644 stdlib/src/builtin/identifiable.mojo diff --git a/docs/changelog.md b/docs/changelog.md index c5ec4edd29..179d46b6ff 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -65,6 +65,13 @@ what we publish. This supports work to transition the standard library collection types away from implicit copyability, which can lead to unintended expensive copies. +- Added `Identifiable` trait, used to describe types that implement the `__is__` + and `__isnot__` trait methods. + ([PR #2807](https://github.com/modularml/mojo/pull/2807)) + + - Also added new `assert_is()` and `assert_is_not()` test utilities to the + `testing` module. + - `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. ([PR #2701](https://github.com/modularml/mojo/pull/2701) by [@jayzhan211](https://github.com/jayzhan211)) diff --git a/stdlib/src/builtin/identifiable.mojo b/stdlib/src/builtin/identifiable.mojo new file mode 100644 index 0000000000..288768856d --- /dev/null +++ b/stdlib/src/builtin/identifiable.mojo @@ -0,0 +1,52 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +trait Identifiable: + """The Identifiable trait denotes a type with an identity + which can be compared with other instances of itself. + """ + + fn __is__(self, rhs: Self) -> Bool: + """Define whether `self` has the same identity as `rhs`. + + Args: + rhs: The right hand side of the comparison. + + Returns: + True if `self` is `rhs`. + """ + ... + + fn __isnot__(self, rhs: Self) -> Bool: + """Define whether `self` has a different identity than `rhs`. + + Args: + rhs: The right hand side of the comparison. + + Returns: + True if `self` is not `rhs`. + """ + ... + + +trait StringableIdentifiable(Stringable, Identifiable): + """The StringableIdentifiable trait denotes a trait composition + of the `Stringable` and `Identifiable` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `Stringable & Identifiable` in the parameter. + """ + + pass diff --git a/stdlib/src/testing/__init__.mojo b/stdlib/src/testing/__init__.mojo index c3d2e32298..23636b5081 100644 --- a/stdlib/src/testing/__init__.mojo +++ b/stdlib/src/testing/__init__.mojo @@ -20,4 +20,6 @@ from .testing import ( assert_not_equal, assert_raises, assert_true, + assert_is, + assert_is_not, ) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 4998a96f92..2d4a8c5c37 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -135,7 +135,9 @@ fn assert_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs != rhs: - raise _assert_equal_error(str(lhs), str(rhs), msg, __call_location()) + raise _assert_cmp_error["`left == right` comparison"]( + str(lhs), str(rhs), msg, __call_location() + ) # TODO: Remove the String and SIMD overloads once we have more powerful traits. @@ -153,7 +155,9 @@ fn assert_equal(lhs: String, rhs: String, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs != rhs: - raise _assert_equal_error(lhs, rhs, msg, __call_location()) + raise _assert_cmp_error["`left == right` comparison"]( + lhs, rhs, msg, __call_location() + ) @always_inline @@ -176,7 +180,9 @@ fn assert_equal[ An Error with the provided message if assert fails and `None` otherwise. """ if any(lhs != rhs): - raise _assert_equal_error(str(lhs), str(rhs), msg, __call_location()) + raise _assert_cmp_error["`left == right` comparison"]( + str(lhs), str(rhs), msg, __call_location() + ) @always_inline @@ -196,7 +202,7 @@ fn assert_not_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: - raise _assert_not_equal_error( + raise _assert_cmp_error["`left != right` comparison"]( str(lhs), str(rhs), msg, __call_location() ) @@ -215,7 +221,9 @@ fn assert_not_equal(lhs: String, rhs: String, msg: String = "") raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: - raise _assert_not_equal_error(lhs, rhs, msg, __call_location()) + raise _assert_cmp_error["`left != right` comparison"]( + lhs, rhs, msg, __call_location() + ) @always_inline @@ -238,7 +246,7 @@ fn assert_not_equal[ An Error with the provided message if assert fails and `None` otherwise. """ if all(lhs == rhs): - raise _assert_not_equal_error( + raise _assert_cmp_error["`left != right` comparison"]( str(lhs), str(rhs), msg, __call_location() ) @@ -304,29 +312,64 @@ fn assert_almost_equal[ raise _assert_error(err, __call_location()) -fn _assert_equal_error( - lhs: String, rhs: String, msg: String, loc: _SourceLocation -) -> String: - var err = ( - "`left == right` comparison failed:\n left: " - + lhs - + "\n right: " - + rhs - ) - if msg: - err += "\n reason: " + msg - return _assert_error(err, loc) +@always_inline +fn assert_is[ + T: StringableIdentifiable +](lhs: T, rhs: T, msg: String = "") raises: + """Asserts that the input values have the same identity. If they do not + then an Error is raised. + Parameters: + T: A StringableIdentifiable type. -fn _assert_not_equal_error( - lhs: String, rhs: String, msg: String, loc: _SourceLocation -) -> String: - var err = ( - "`left != right` comparison failed:\n left: " - + lhs - + "\n right: " - + rhs - ) + Args: + lhs: The lhs of the `is` statement. + rhs: The rhs of the `is` statement. + msg: The message to be printed if the assertion fails. + + Raises: + An Error with the provided message if assert fails and `None` otherwise. + """ + if lhs is not rhs: + raise _assert_cmp_error["`left is right` identification"]( + str(lhs), + str(rhs), + msg, + __call_location(), + ) + + +@always_inline +fn assert_is_not[ + T: StringableIdentifiable +](lhs: T, rhs: T, msg: String = "") raises: + """Asserts that the input values have different identities. If they do not + then an Error is raised. + + Parameters: + T: A StringableIdentifiable type. + + Args: + lhs: The lhs of the `is not` statement. + rhs: The rhs of the `is not` statement. + msg: The message to be printed if the assertion fails. + + Raises: + An Error with the provided message if assert fails and `None` otherwise. + """ + if lhs is rhs: + raise _assert_cmp_error["`left is not right` identification"]( + str(lhs), + str(rhs), + msg, + __call_location(), + ) + + +fn _assert_cmp_error[ + cmp: String +](lhs: String, rhs: String, msg: String, loc: _SourceLocation) -> String: + var err = (cmp + " failed:\n left: " + lhs + "\n right: " + rhs) if msg: err += "\n reason: " + msg return _assert_error(err, loc) diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 0d1d59cf94..d152776a6b 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -19,6 +19,8 @@ from testing import ( assert_not_equal, assert_raises, assert_true, + assert_is, + assert_is_not, ) from utils.numerics import inf, nan @@ -63,22 +65,22 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:64:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:66:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:69:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:71:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:74:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:76:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:79:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:81:25: AssertionError:" in str(e)) def test_assert_almost_equal(): @@ -181,9 +183,23 @@ def test_assert_almost_equal(): ) +def test_assert_is(): + var a = PythonObject("mojo") + var b = a + assert_is(a, b) + + +def test_assert_is_not(): + var a = PythonObject("mojo") + var b = PythonObject("mojo") + assert_is_not(a, b) + + def main(): test_assert_equal_is_generic() test_assert_not_equal_is_generic() test_assert_equal_with_simd() test_assert_messages() test_assert_almost_equal() + test_assert_is() + test_assert_is_not() From 89cc7632bf2b9226a4d4390755efe1dcf11d27bc Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 20 Jun 2024 13:07:38 -0500 Subject: [PATCH 0999/2019] [External] [stdlib] Move `object.mojo` off `unroll` (#42021) [External] [stdlib] Move `object.mojo` off `unroll` As part of migrating off of `unroll` in favor of `@parameter for`, apply the use of `@parameter for` in `object.mojo`. Co-authored-by: soraros Closes modularml/mojo#3054 MODULAR_ORIG_COMMIT_REV_ID: 7a15e8f8a2fa47e3d500d58ae43b9968478abf9d --- stdlib/src/builtin/object.mojo | 5 +---- stdlib/src/python/object.mojo | 8 ++------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 808d26de2d..1cc452cb8e 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -763,8 +763,7 @@ struct object(IntableRaising, Boolable, Stringable): self._value = _RefCountedListRef() @parameter - @always_inline - fn append[i: Int](): + for i in range(len(VariadicList(Ts))): # We need to rebind the element to one we know how to convert from. # FIXME: This doesn't handle implicit conversions or nested lists. alias T = Ts[i] @@ -785,8 +784,6 @@ struct object(IntableRaising, Boolable, Stringable): False, "cannot convert nested list element to object" ]() - unroll[append, len(VariadicList(Ts))]() - @always_inline fn __init__(inout self, func: Self.nullary_function): """Initializes an object from a function that takes no arguments. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index d54e4f7c2b..ea8832a238 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -228,7 +228,7 @@ struct PythonObject( self.py_object = cpython.PyList_New(len(value)) @parameter - fn fill[i: Int](): + for i in range(len(VariadicList(Ts))): # We need to rebind the element to one we know how to convert from. # FIXME: This doesn't handle implicit conversions or nested lists. alias T = Ts[i] @@ -254,8 +254,6 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) - unroll[fill, len(VariadicList(Ts))]() - fn __init__[*Ts: Movable](inout self, value: Tuple[Ts]): """Initialize the object from a tuple literal. @@ -270,7 +268,7 @@ struct PythonObject( self.py_object = cpython.PyTuple_New(length) @parameter - fn fill[i: Int](): + for i in range(length): # We need to rebind the element to one we know how to convert from. # FIXME: This doesn't handle implicit conversions or nested lists. alias T = Ts[i] @@ -296,8 +294,6 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyTuple_SetItem(self.py_object, i, obj.py_object) - unroll[fill, length]() - fn __init__(inout self, value: Dict[Self, Self]): """Initialize the object from a dictionary of PythonObjects. From 6356e700692c32ce24b96c6aa8b77345935c96db Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 20 Jun 2024 13:14:35 -0500 Subject: [PATCH 1000/2019] [External] [stdlib] Add method `List.format_to()` (#41761) [External] [stdlib] Add method `List.format_to()` Going through the code of `utils/_format.mojo` I see a lot of overlap between this functionality and the file-like objects in Python. I think that in the future we can replace the formatter by a File-like object without any issue. For this reason, I changed the function `write_to` to be a method of `Formatter`, so that in the future, when we replace `Formatter` by a `FileLike` trait (or similar), the `write` method is already used. I think it would be good to flush out the `io` python module. This would give us access to structs and traits that would have made the code in `_format.mojo` unnecessary. Writing to buffers / files is very common in python, there should be no need to re-invent it. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2998 MODULAR_ORIG_COMMIT_REV_ID: a194e84bb3647a659cd16dd0a40d12769aca758a --- stdlib/src/collections/list.mojo | 36 +++++++++++++++----------- stdlib/src/utils/_format.mojo | 11 ++++++++ stdlib/test/collections/test_list.mojo | 4 +++ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index e4d8536aa6..63bf454c50 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -25,6 +25,7 @@ from sys.intrinsics import _type_is_eq from memory import Reference, UnsafePointer from utils import Span +from utils._format import write_to from .optional import Optional @@ -351,23 +352,28 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): Returns: A string representation of the list. """ - # we do a rough estimation of the number of chars that we'll see - # in the final string, we assume that str(x) will be at least one char. - var minimum_capacity = ( - 2 # '[' and ']' - + len(self) * 3 # str(x) and ", " - - 2 # remove the last ", " - ) - var string_buffer = List[UInt8](capacity=minimum_capacity) - string_buffer.append(0) # Null terminator - var result = String(string_buffer^) - result += "[" + var output = String() + var writer = output._unsafe_to_formatter() + self.format_to(writer) + return output^ + + fn format_to[ + U: RepresentableCollectionElement + ](self: List[U], inout writer: Formatter): + """Write `my_list.__str__()` to a `Formatter`. + + Parameters: + U: The type of the List elements. Must have the trait `RepresentableCollectionElement`. + + Args: + writer: The formatter to write to. + """ + writer.write("[") for i in range(len(self)): - result += repr(self[i]) + writer.write(repr(self[i])) if i < len(self) - 1: - result += ", " - result += "]" - return result + writer.write(", ") + writer.write("]") fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index 50a4307a61..121afabef5 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -120,6 +120,16 @@ struct Formatter: self._write_func(self._write_func_arg, strref) + fn write[*Ts: Formattable](inout self: Formatter, *args: *Ts): + """Write a sequence of formattable arguments to the provided formatter. + """ + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(self) + + args.each[write_arg]() + # ===------------------------------------------------------------------=== # # Factory methods # ===------------------------------------------------------------------=== # @@ -138,6 +148,7 @@ struct Formatter: return Formatter(write_to_stdout, UnsafePointer[NoneType]()) +# TODO: Use Formatter.write instead. fn write_to[*Ts: Formattable](inout writer: Formatter, *args: *Ts): """ Write a sequence of formattable arguments to the provided formatter. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index e4f6d86987..66c609cbe3 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -684,6 +684,10 @@ def test_constructor_from_other_list_through_pointer(): def test_converting_list_to_string(): + # This is also testing the method `to_format` because + # essentially, `List.__str__()` just creates a String and applies `to_format` to it. + # If we were to write unit tests for `to_format`, we would essentially copy-paste the code + # of `List.__str__()` var my_list = List[Int](1, 2, 3) assert_equal(my_list.__str__(), "[1, 2, 3]") From c6d44a3c23c1f051bc09b3582042b7df6b56ea86 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:39:41 -0500 Subject: [PATCH 1001/2019] [External] [stdlib] Split dict pop into two overloads (#42030) [External] [stdlib] Split dict pop into two overloads Fixes https://github.com/modularml/mojo/issues/2921 Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3059 MODULAR_ORIG_COMMIT_REV_ID: 5fb021d8a55ddd5e99f1f7a9aafa8f051c449b5e --- stdlib/src/collections/dict.mojo | 50 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 9568fc1491..a74bb9d539 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -744,21 +744,35 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return self.find(key).or_else(default) - fn pop(inout self, key: K, owned default: Optional[V] = None) raises -> V: + fn pop(inout self, key: K, owned default: V) -> V: """Remove a value from the dictionary by key. Args: key: The key to remove from the dictionary. - default: Optionally provide a default value to return if the key + default: A default value to return if the key was not found instead of raising. Returns: The value associated with the key, if it was in the dictionary. If it wasn't, return the provided default value instead. + """ + try: + return self.pop(key) + except: + return default + + fn pop(inout self, key: K) raises -> V: + """Remove a value from the dictionary by key. + + Args: + key: The key to remove from the dictionary. + + Returns: + The value associated with the key, if it was in the dictionary. + Raises otherwise. Raises: - "KeyError" if the key was not present in the dictionary and no - default value was provided. + "KeyError" if the key was not present in the dictionary. """ var hash = hash(key) var found: Bool @@ -773,8 +787,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( entry[] = None self.size -= 1 return entry_value.value^ - elif default: - return default.value() raise "KeyError" fn popitem(inout self) raises -> DictEntry[K, V]: @@ -1067,25 +1079,35 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): return self._dict.find(key) @always_inline("nodebug") - fn pop( - inout self, key: self.key_type, owned default: Optional[V] = None - ) raises -> V: - """Remove a value from the keyword dictionary by key. + fn pop(inout self, key: self.key_type, owned default: V) -> V: + """Remove a value from the dictionary by key. Args: key: The key to remove from the dictionary. - default: Optionally provide a default value to return if the key + default: A default value to return if the key was not found instead of raising. Returns: The value associated with the key, if it was in the dictionary. If it wasn't, return the provided default value instead. + """ + return self._dict.pop(key, default^) + + @always_inline("nodebug") + fn pop(inout self, key: self.key_type) raises -> V: + """Remove a value from the dictionary by key. + + Args: + key: The key to remove from the dictionary. + + Returns: + The value associated with the key, if it was in the dictionary. + Raises otherwise. Raises: - "KeyError" if the key was not present in the dictionary and no - default value was provided. + "KeyError" if the key was not present in the dictionary. """ - return self._dict.pop(key, default^) + return self._dict.pop(key) fn __iter__( ref [_]self: Self, From afb78488ceec910b9a3e4ba8fd7b471e45a14535 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 20 Jun 2024 11:56:57 -0700 Subject: [PATCH 1002/2019] [Stdlib][Math] Add the pi/e/tau math constants This adds pi/e/tau constants to the math module. Closes #2135 MODULAR_ORIG_COMMIT_REV_ID: 068e9dd6e4bc755f9c9e800d35b85fa13941f646 --- docs/changelog.md | 3 +++ stdlib/src/math/__init__.mojo | 2 ++ stdlib/src/math/constants.mojo | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 stdlib/src/math/constants.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 179d46b6ff..2229f95407 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -131,6 +131,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, but no Python executable. +- The `math` package now includes the `pi`, `e`, and `tau` constants (Closes + Issue [#2135](https://github.com/modularml/mojo/issues/2135)). + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index ce98bc20e4..3683fc3ff4 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -15,6 +15,8 @@ # In Python, these are in the math module, so we also expose them here. from utils.numerics import inf, isfinite, isinf, isnan, nan, nextafter, ulp +from .constants import pi, e, tau + # These are not part of Python's `math` module, but we define them here. from .math import ( Ceilable, diff --git a/stdlib/src/math/constants.mojo b/stdlib/src/math/constants.mojo new file mode 100644 index 0000000000..49c814406a --- /dev/null +++ b/stdlib/src/math/constants.mojo @@ -0,0 +1,30 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines math utilities. + +You can import these APIs from the `math` package. For example: + +```mojo +from math import pi +``` +""" + + +alias pi = 3.1415926535897932384626433832795028841971693993751058209749445923 +"""The mathematical constant π = 3.141592...""" + +alias e = 2.7182818284590452353602874713526624977572470936999595749669676277 +"""The euler constant e = 2.718281...""" + +alias tau = 2 * pi +"""The mathematical constant τ = 6.283185.... Tau is a circumference of a circle (2π).""" From 5cef77a1d551d401ad9d17156cb9bbc7bfc1a5b1 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 20 Jun 2024 14:19:27 -0500 Subject: [PATCH 1003/2019] [External] [stdlib] Add explicit copy constructor to `Optional` (#41774) [External] [stdlib] Add explicit copy constructor to `Optional` We can remove `__copyinit__` in a later PR. This will be useful to use `Optional` in collections as they often do copies in methods. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3036 MODULAR_ORIG_COMMIT_REV_ID: 9ef1ccc9a271b4bab7a5b2a975c4c47b29d3d409 --- stdlib/src/builtin/string.mojo | 1 + stdlib/src/collections/optional.mojo | 12 +++++++++++- stdlib/test/collections/test_optional.mojo | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 845e648e9b..ed492c2448 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -771,6 +771,7 @@ struct String( Boolable, Formattable, ToFormatter, + CollectionElementNew, ): """Represents a mutable string.""" diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 9af65e4836..f68153c62e 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -46,7 +46,9 @@ struct _NoneType(CollectionElement): @value -struct Optional[T: CollectionElement](CollectionElement, Boolable): +struct Optional[T: CollectionElement]( + CollectionElement, CollectionElementNew, Boolable +): """A type modeling a value which may or may not be present. Optional values can be thought of as a type-safe nullable pattern. @@ -104,6 +106,14 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): """ self = Self() + fn __init__(inout self, *, other: Self): + """Copy construct an Optional. + + Args: + other: The Optional to copy. + """ + self.__copyinit__(other) + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 5936959b3c..04929e6efd 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -120,6 +120,20 @@ def test_optional_take_mutates(): assert_false(opt1) +def test_optional_explicit_copy(): + var v1 = Optional[String](String("test")) + + var v2 = Optional(other=v1) + + assert_equal(v1.value(), "test") + assert_equal(v2.value(), "test") + + v2.value() += "ing" + + assert_equal(v1.value(), "test") + assert_equal(v2.value(), "testing") + + def main(): test_basic() test_optional_reg_basic() @@ -128,3 +142,4 @@ def main(): test_optional_reg_is() test_optional_reg_isnot() test_optional_take_mutates() + test_optional_explicit_copy() From 14de4dd775975ad5cb741da1ee711592116aeee6 Mon Sep 17 00:00:00 2001 From: codingonion Date: Thu, 20 Jun 2024 14:53:32 -0500 Subject: [PATCH 1004/2019] [External] [docs] Update UnsafePointer APIs in pointers.ipynb (#42032) [External] [docs] Update UnsafePointer APIs in pointers.ipynb Fix typo in pointers.ipynb Co-authored-by: codingonion Closes modularml/mojo#3080 MODULAR_ORIG_COMMIT_REV_ID: 576d18f0234b28daa464102859b8a0449194f100 --- docs/manual/pointers.ipynb | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 07c2d6e575..90ea6741c0 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -51,12 +51,12 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, initialize_pointee_move\n", + "from memory.unsafe_pointer import UnsafePointer\n", "\n", "# Allocate memory to hold a value\n", "var ptr = UnsafePointer[Int].alloc(1)\n", "# Initialize the allocated memory\n", - "initialize_pointee_copy(ptr, 100)" + "ptr.init_pointee_copy(100)" ] }, { @@ -143,9 +143,9 @@ " the `address_of()` static method to get a pointer to an existing value. \n", "\n", " ```mojo\n", - " initialize_pointee_copy(ptr, value)\n", + " ptr.init_pointee_copy(value)\n", " # or\n", - " initalize_pointee_move(ptr, value^)\n", + " ptr.init_pointee_move(value^)\n", " # or \n", " ptr = UnsafePointer[Int].address_of(value)\n", " ```\n", @@ -201,19 +201,19 @@ "\n", "The `unsafe_pointer` module includes a number of free functions for working with\n", "the `UnsafePointer` type. To initialize allocated memory, you can use the \n", - "[`initialize_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy)\n", - "or [`initialize_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move)\n", + "[`init_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy)\n", + "or [`init_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move)\n", "functions:\n", "\n", "```mojo\n", - "initialize_pointee_copy(ptr, 5)\n", + "ptr.init_pointee_copy(5)\n", "```\n", "\n", "To move a value into the pointer's memory location, use\n", - "`initialize_pointee_move()`:\n", + "`init_pointee_move`:\n", "\n", "```mojo\n", - "initialize_pointee_move(str_ptr, my_string^)\n", + "str_ptr.init_pointee_move(my_string^)\n", "```\n", "\n", "Note that to move the value, you usually need to add the transfer operator\n", @@ -222,7 +222,7 @@ "`Int`) or a newly-constructed, \"owned\" value:\n", "\n", "```mojo\n", - "initialize_pointee_move(str_ptr, str(\"Owned string\"))\n", + "str_ptr.init_pointee_move(str(\"Owned string\"))\n", "```" ] }, @@ -368,7 +368,7 @@ "```mojo\n", "str_ptr = UnsafePointer[String].alloc(1)\n", "# str_ptr[] = \"Testing\" # Undefined behavior!\n", - "initialize_pointee_move(str_ptr, \"Testing\")\n", + "str_ptr.init_pointee_move(\"Testing\")\n", "str_ptr[] += \" pointers\" # Works now\n", "```" ] @@ -380,20 +380,20 @@ "### Destroying or removing values\n", "\n", "The \n", - "[`move_from_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee)\n", + "[`take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee)\n", "function moves the pointee from the memory location pointed to by `ptr`. This is\n", "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n", "the memory location uninitialized.\n", "\n", - "The [`destroy_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n", + "The [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee)\n", "function calls the destructor on the pointee, and leaves the memory location\n", "pointed to by `ptr` uninitialized. \n", "\n", - "Both `move_from_pointee()` and `destroy_pointee()` require that the pointer is \n", + "Both `take_pointee()` and `destroy_pointee()` require that the pointer is \n", "non-null, and the memory location contains a valid, initialized value of the \n", "pointee's type; otherwise the function results in undefined behavior.\n", "\n", - "The [`move_pointee(src, dst)`](/mojo/stdlib/memory/unsafe_pointer/move_pointee)\n", + "The [`move_pointee_into(self, dst)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into)\n", "function moves the pointee from one pointer location to another. Both pointers\n", "must be non-null. The source location must contain a valid, initialized value of \n", "the pointee's type, and is left uninitialized after the call. The destination \n", @@ -413,7 +413,7 @@ "pointer frees the memory allocated by the pointer. It doesn't call the \n", "destructors on any values stored in the memory—you need to do that explicitly\n", "(for example, using\n", - "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) or\n", + "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) or\n", "one of the other functions described in \n", "[Destroying or removing values](#destroying-or-removing-values)).\n", "\n", @@ -473,7 +473,7 @@ "source": [ "float_ptr = UnsafePointer[Float64].alloc(6)\n", "for offset in range(6):\n", - " initialize_pointee_copy(float_ptr+offset, 0.0)" + " (float_ptr+offset).init_pointee_copy(0.0)" ] }, { From bc52a814e9346a0f85ce589abd3557ba831cab7d Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:36:23 -0500 Subject: [PATCH 1005/2019] [External] [stdlib] Implement `__str__` and `__repr__` for `Set` (#41758) [External] [stdlib] Implement `__str__` and `__repr__` for `Set` Implement `__str__` and `__repr__` using `format_to` pattern. --------- Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3019 MODULAR_ORIG_COMMIT_REV_ID: fad592198ce86f6fd44c8df02c8f4a25076cecf3 --- stdlib/src/collections/set.mojo | 53 ++++++++++++++++++++++++++- stdlib/test/collections/test_set.mojo | 13 ++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index f830d43b56..c45ba98123 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -12,7 +12,13 @@ # ===----------------------------------------------------------------------=== # """Implements the Set datatype.""" -from .dict import Dict, KeyElement, _DictEntryIter, _DictKeyIter +from .dict import ( + Dict, + KeyElement, + _DictEntryIter, + _DictKeyIter, + RepresentableKeyElement, +) struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): @@ -296,6 +302,51 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): hash_value ^= hash(e[]) return hash_value + fn __str__[U: RepresentableKeyElement](self: Set[U]) -> String: + """Returns the string representation of the set. + + Parameters: + U: The type of the List elements. Must have the trait `RepresentableCollectionElement`. + + Returns: + The string representation of the set. + """ + var output = String() + var writer = output._unsafe_to_formatter() + self.format_to(writer) + return output + + fn __repr__[U: RepresentableKeyElement](self: Set[U]) -> String: + """Returns the string representation of the set. + + Parameters: + U: The type of the List elements. Must have the trait `RepresentableCollectionElement`. + + Returns: + The string representation of the set. + """ + return self.__str__() + + fn format_to[ + U: RepresentableKeyElement, + ](self: Set[U], inout writer: Formatter): + """Write Set string representation to a `Formatter`. + + Parameters: + U: The type of the List elements. Must have the trait `RepresentableCollectionElement`. + + Args: + writer: The formatter to write to. + """ + write_to(writer, "{") + var written = 0 + for item in self: + write_to(writer, repr(item[])) + if written < len(self) - 1: + write_to(writer, ", ") + written += 1 + write_to(writer, "}") + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 18a30c4aff..22bb20697d 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -15,6 +15,7 @@ from collections import Set from testing import assert_false, assert_raises, assert_true +from testing import assert_equal as AE fn assert_equal[T: EqualityComparable](lhs: T, rhs: T) raises: @@ -252,7 +253,7 @@ def test_pop_insertion_order(): assert_equal(s, Set[Int]()) with assert_raises(): - s.pop() # pop from empty set raises + _ = s.pop() # pop from empty set raises def test_issubset(): @@ -484,6 +485,15 @@ def test_clear(): assert_true(len(set5) == 0) +def test_set_str(): + var a = Set[Int](1, 2, 3) + AE(a.__str__(), "{1, 2, 3}") + AE(a.__repr__(), "{1, 2, 3}") + var b = Set[String]("a", "b") + AE(b.__str__(), "{'a', 'b'}") + AE(Set[Int]().__str__(), "{}") + + fn test[name: String, test_fn: fn () raises -> object]() raises: var name_val = name # FIXME(#26974): Can't pass 'name' directly. print("Test", name_val, "...", end="") @@ -518,3 +528,4 @@ def main(): test["test_symmetric_difference_update", test_symmetric_difference_update]() test["test_discard", test_discard]() test["test_clear", test_clear]() + test["test_set_str", test_set_str]() From e48e34a91c9f84bb12c84f74eb415fa934a86d49 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 20 Jun 2024 15:09:48 -0700 Subject: [PATCH 1006/2019] [Docs] Fix typo in ownership.ipynb. MODULAR_ORIG_COMMIT_REV_ID: d0e28351c72ad4e678a6d2a4edbaf11271a75043 --- docs/manual/values/ownership.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index a92d507b7a..d4160853cc 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -241,7 +241,7 @@ "when compared to languages like C++ and Rust, and moves this optimization from\n", "every call site to a declaration on the type definition.\n", "\n", - "In the future, Mojo's lifetime checker will enforces the exclusivity of\n", + "In the future, Mojo's lifetime checker will enforce the exclusivity of\n", "mutable references, similar to Rust.\n", "The major difference between Rust and Mojo is that Mojo does not require a\n", "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", From dc0ad5905de9bbd7c98b32615e4d8b40e5ef421a Mon Sep 17 00:00:00 2001 From: Samay Kapadia Date: Thu, 20 Jun 2024 17:28:49 -0500 Subject: [PATCH 1007/2019] [External] [stdlib] Specify alignment in UnsafePointer.alloc (#41757) [External] [stdlib] Specify alignment in UnsafePointer.alloc Fixes https://github.com/modularml/mojo/issues/3006 Adds a function parameter in `UnsafePointer.alloc` to specify alignment at compile time. Co-authored-by: Samay Kapadia Closes modularml/mojo#3007 MODULAR_ORIG_COMMIT_REV_ID: c33c03e46dc36a493693361c4a44c24ea77878a4 --- stdlib/src/memory/unsafe_pointer.mojo | 14 ++++++++------ stdlib/test/memory/test_unsafepointer.mojo | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index d1a760a4f3..b69e845833 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -129,8 +129,11 @@ struct UnsafePointer[ @staticmethod @always_inline - fn alloc(count: Int) -> Self: - """Allocate an array with default alignment. + fn alloc[alignment: Int = alignof[T]()](count: Int) -> Self: + """Allocate an array with specified or default alignment. + + Parameters: + alignment: The alignment in bytes of the allocated memory. Args: count: The number of elements in the array. @@ -139,18 +142,17 @@ struct UnsafePointer[ The pointer to the newly allocated array. """ alias sizeof_t = sizeof[T]() - alias alignof_t = alignof[T]() constrained[sizeof_t > 0, "size must be greater than zero"]() - constrained[alignof_t > 0, "alignment must be greater than zero"]() + constrained[alignment > 0, "alignment must be greater than zero"]() constrained[ - sizeof_t % alignof_t == 0, "size must be a multiple of alignment" + sizeof_t % alignment == 0, "size must be a multiple of alignment" ]() return Self( address=int( _malloc[Int8, address_space=address_space]( - sizeof_t * count, alignment=alignof_t + sizeof_t * count, alignment=alignment ) ) ) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 471c3cad42..c0313509cb 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -169,6 +169,26 @@ def test_unsafepointer_address_space(): p2.free() +def test_unsafepointer_aligned_alloc(): + alias alignment_1 = 32 + var ptr = UnsafePointer[UInt8].alloc[alignment=alignment_1](1) + var ptr_uint64 = UInt64(int(ptr)) + ptr.free() + assert_equal(ptr_uint64 % alignment_1, 0) + + alias alignment_2 = 64 + var ptr_2 = UnsafePointer[UInt8].alloc[alignment=alignment_2](1) + var ptr_uint64_2 = UInt64(int(ptr_2)) + ptr_2.free() + assert_equal(ptr_uint64_2 % alignment_2, 0) + + alias alignment_3 = 128 + var ptr_3 = UnsafePointer[UInt8].alloc[alignment=alignment_3](1) + var ptr_uint64_3 = UInt64(int(ptr_3)) + ptr_3.free() + assert_equal(ptr_uint64_3 % alignment_3, 0) + + # NOTE: Tests fails due to a `UnsafePointer` size # and alignment constraint failing to be satisfied. # From 2352c410c7a091dd43b8e235ff0e85c83c41bb5d Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Thu, 20 Jun 2024 17:31:13 -0500 Subject: [PATCH 1008/2019] [External] [stdlib] Assert valid UTF-8 byte in ord function (#42040) [External] [stdlib] Assert valid UTF-8 byte in ord function Based on issue #2842 Co-authored-by: Maxim Zaks Closes modularml/mojo#3062 MODULAR_ORIG_COMMIT_REV_ID: e726d770d12f9971fcdd1943ab94a858e7e9b0b9 --- stdlib/src/builtin/string.mojo | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index ed492c2448..a50b83dc42 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -56,11 +56,18 @@ fn ord(s: String) -> Int: return int(b1) var num_bytes = countl_zero(~b1) debug_assert(len(s) == int(num_bytes), "input string must be one character") + debug_assert( + 1 < int(num_bytes) < 5, "invalid UTF-8 byte " + str(b1) + " at index 0" + ) var shift = int((6 * (num_bytes - 1))) var b1_mask = 0b11111111 >> (num_bytes + 1) var result = int(b1 & b1_mask) << shift - for _ in range(1, num_bytes): + for i in range(1, num_bytes): p += 1 + debug_assert( + p[] >> 6 == 0b00000010, + "invalid UTF-8 byte " + str(b1) + " at index " + str(i), + ) shift -= 6 result |= int(p[] & 0b00111111) << shift return result From ddf2b594f50aa6f2a52341de35a6b3b0160bae8b Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 20 Jun 2024 16:49:38 -0700 Subject: [PATCH 1009/2019] [Docs] Update diagram & wording for pointer API changes. Follow-up to https://github.com/modularml/mojo/pull/3080, updating the diagram and some wording to match the new UnsafePointer APIs. MODULAR_ORIG_COMMIT_REV_ID: 01d145d8a4dbba6471fc03dd199d7bdad6fcd3c3 --- docs/manual/images/pointer-lifecycle-dark.png | Bin 82779 -> 79528 bytes docs/manual/images/pointer-lifecycle.png | Bin 81374 -> 78112 bytes docs/manual/pointers.ipynb | 17 ++++++++--------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/manual/images/pointer-lifecycle-dark.png b/docs/manual/images/pointer-lifecycle-dark.png index eeaff88c8a373ef87e2dda5a5cb6646068b54434..76ebc2cbf0ee790cc22eec5571b5c3a606c3ffb6 100644 GIT binary patch delta 54888 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 delta 58165 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 diff --git a/docs/manual/images/pointer-lifecycle.png b/docs/manual/images/pointer-lifecycle.png index fb2c0622934801c38a001d0aa6bf0a1d017d548c..a52034b334a32963751fc41ef4f198dabb375ce0 100644 GIT binary patch delta 52983 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 OpN^&hv5a6H`hNfs*pYt# delta 56271 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 WY`PI}j)8%Tfx*+&&t;ucLK6UnoTBvr diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 90ea6741c0..9aedf781e3 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -199,18 +199,17 @@ "\n", "### Initializing the pointee\n", "\n", - "The `unsafe_pointer` module includes a number of free functions for working with\n", - "the `UnsafePointer` type. To initialize allocated memory, you can use the \n", + "To initialize allocated memory, `UnsafePointer` provides the\n", "[`init_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy)\n", - "or [`init_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move)\n", - "functions:\n", + "and [`init_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move)\n", + "methods. For example:\n", "\n", "```mojo\n", - "ptr.init_pointee_copy(5)\n", + "ptr.init_pointee_copy(my_value)\n", "```\n", "\n", "To move a value into the pointer's memory location, use\n", - "`init_pointee_move`:\n", + "`init_pointee_move()`:\n", "\n", "```mojo\n", "str_ptr.init_pointee_move(my_string^)\n", @@ -381,12 +380,12 @@ "\n", "The \n", "[`take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee)\n", - "function moves the pointee from the memory location pointed to by `ptr`. This is\n", + "method moves the pointee from the memory location pointed to by `ptr`. This is\n", "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n", "the memory location uninitialized.\n", "\n", "The [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee)\n", - "function calls the destructor on the pointee, and leaves the memory location\n", + "method calls the destructor on the pointee, and leaves the memory location\n", "pointed to by `ptr` uninitialized. \n", "\n", "Both `take_pointee()` and `destroy_pointee()` require that the pointer is \n", @@ -394,7 +393,7 @@ "pointee's type; otherwise the function results in undefined behavior.\n", "\n", "The [`move_pointee_into(self, dst)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into)\n", - "function moves the pointee from one pointer location to another. Both pointers\n", + "method moves the pointee from one pointer location to another. Both pointers\n", "must be non-null. The source location must contain a valid, initialized value of \n", "the pointee's type, and is left uninitialized after the call. The destination \n", "location is assumed to be uninitialized—if it contains a valid value, that\n", From c1c7d46095c40d4c7b036ed9ae50b7914cf88dc2 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 21 Jun 2024 10:46:30 -0500 Subject: [PATCH 1010/2019] [stdlib] feature: Use `StaticString` and `StringSlice` in io.mojo This fixes some places where the use of `StringLiteral` parameter meant a runtime string value could no longer be passed. It also removes a few more uses of `_strref_dangerous()`. MODULAR_ORIG_COMMIT_REV_ID: be46af2a8f7ab6e7491531d622d12a2b49f92aa7 --- stdlib/src/builtin/format_int.mojo | 10 +- stdlib/src/builtin/io.mojo | 142 +++++++++-------------------- 2 files changed, 51 insertions(+), 101 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 06ed2bcf82..4cd86aed7c 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -336,7 +336,7 @@ fn _try_write_int[ # Stack allocate enough bytes to store any formatted 64-bit integer # TODO: use a dynamic size when #2194 is resolved - alias CAPACITY: Int = 64 + alias CAPACITY: Int = 64 + 1 # +1 for storing NUL terminator. var buf = InlineArray[UInt8, CAPACITY](unsafe_uninitialized=True) @@ -345,6 +345,12 @@ fn _try_write_int[ # earlier in the buffer as we write the more-significant digits. var offset = CAPACITY - 1 + buf[offset] = 0 # Write NUL terminator at the end + + # Position the offset to write the least-significant digit just before the + # NUL terminator. + offset -= 1 + # Write the digits of the number var remaining_int = value @@ -385,7 +391,7 @@ fn _try_write_int[ # Calculate the length of the buffer we've filled. This is the number of # bytes from our final `buf_ptr` to the end of the buffer. - var len = CAPACITY - offset + var len = (CAPACITY - offset) - 1 # -1 because NUL terminator # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 089bae9f7b..d798ed1667 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -28,7 +28,7 @@ from builtin.dtype import _get_dtype_printf_format from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer -from utils import StringRef, unroll +from utils import StringRef, unroll, StaticString, StringSlice from utils._format import Formattable, Formatter, write_to # ===----------------------------------------------------------------------=== # @@ -271,7 +271,7 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): if triple_is_nvidia_cuda(): _printf[format](x.cast[DType.float64]()) else: - _put(str(x)) + _put(str(x).as_string_slice()) else: constrained[False, "invalid dtype"]() @@ -302,40 +302,56 @@ fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): _put[", "]() _put["]"]() else: - _put(str(x)) + _put(str(x).as_string_slice()) @no_inline -fn _put(x: String, file: FileDescriptor = stdout): - # 'x' is borrowed, so we know it will outlive the call to print. - _put(x._strref_dangerous(), file=file) +fn _put[x: StringLiteral](file: FileDescriptor = stdout): + _put(x.as_string_slice(), file=file) + + +fn _put(strref: StringRef): + var str_slice = StringSlice[ImmutableStaticLifetime]( + unsafe_from_utf8_strref=strref + ) + + _put(str_slice) + + +@no_inline +fn _put(x: DType, file: FileDescriptor = stdout): + _put(str(x).as_string_slice(), file=file) +# TODO: Constrain to `StringSlice[False, _]` @no_inline -fn _put(x: StringRef, file: FileDescriptor = stdout): +fn _put(x: StringSlice, file: FileDescriptor = stdout): # Avoid printing "(null)" for an empty/default constructed `String` - var str_len = len(x) + var str_len = x._byte_length() if not str_len: return @parameter if triple_is_nvidia_cuda(): + # Note: + # This assumes that the `StringSlice` that was passed in is NUL + # terminated. var tmp = 0 var arg_ptr = UnsafePointer.address_of(tmp) _ = external_call["vprintf", Int32]( - x.data, arg_ptr.bitcast[UnsafePointer[NoneType]]() + x.unsafe_ptr(), arg_ptr.bitcast[UnsafePointer[NoneType]]() ) else: alias MAX_STR_LEN = 0x1000_0000 # The string can be printed, so that's fine. if str_len < MAX_STR_LEN: - _printf["%.*s"](x.length, x.data, file=file) + _printf["%.*s"](x._byte_length(), x.unsafe_ptr(), file=file) return # The string is large, then we need to chunk it. - var p = x.data + var p = x.unsafe_ptr() while str_len: var ll = min(str_len, MAX_STR_LEN) _printf["%.*s"](ll, p, file=file) @@ -343,16 +359,6 @@ fn _put(x: StringRef, file: FileDescriptor = stdout): p += ll -@no_inline -fn _put[x: StringLiteral](file: FileDescriptor = stdout): - _put(StringRef(x), file=file) - - -@no_inline -fn _put(x: DType, file: FileDescriptor = stdout): - _put(str(x), file=file) - - # ===----------------------------------------------------------------------=== # # print # ===----------------------------------------------------------------------=== # @@ -361,73 +367,10 @@ fn _put(x: DType, file: FileDescriptor = stdout): @no_inline fn print[ *Ts: Stringable -](*values: *Ts, flush: Bool = False, file: FileDescriptor = stdout): - """Prints elements to the text stream. Each element is separated by a - whitespace and followed by a newline character. - - Parameters: - Ts: The elements types. - - Args: - values: The elements to print. - flush: If set to true, then the stream is forcibly flushed. - file: The output stream. - """ - _print(values, sep=" ", end="\n", flush=flush, file=file) - - -@no_inline -fn print[ - *Ts: Stringable, EndTy: Stringable ]( *values: *Ts, - end: EndTy, - flush: Bool = False, - file: FileDescriptor = stdout, -): - """Prints elements to the text stream. Each element is separated by a - whitespace and followed by `end`. - - Parameters: - Ts: The elements types. - EndTy: The type of end argument. - - Args: - values: The elements to print. - end: The String to write after printing the elements. - flush: If set to true, then the stream is forcibly flushed. - file: The output stream. - """ - _print(values, sep=" ", end=str(end), flush=flush, file=file) - - -@no_inline -fn print[ - SepTy: Stringable, *Ts: Stringable -](*values: *Ts, sep: SepTy, flush: Bool = False, file: FileDescriptor = stdout): - """Prints elements to the text stream. Each element is separated by `sep` - and followed by a newline character. - - Parameters: - SepTy: The type of separator. - Ts: The elements types. - - Args: - values: The elements to print. - sep: The separator used between elements. - flush: If set to true, then the stream is forcibly flushed. - file: The output stream. - """ - _print(values, sep=str(sep), end="\n", flush=flush, file=file) - - -@no_inline -fn print[ - SepTy: Stringable, EndTy: Stringable, *Ts: Stringable -]( - *values: *Ts, - sep: SepTy, - end: EndTy, + sep: StaticString = " ", + end: StaticString = "\n", flush: Bool = False, file: FileDescriptor = stdout, ): @@ -435,8 +378,6 @@ fn print[ and followed by `end`. Parameters: - SepTy: The type of separator. - EndTy: The type of end argument. Ts: The elements types. Args: @@ -446,7 +387,7 @@ fn print[ flush: If set to true, then the stream is forcibly flushed. file: The output stream. """ - _print(values, sep=str(sep), end=str(end), flush=flush, file=file) + _print(values, sep=sep, end=end, flush=flush, file=file) @no_inline @@ -455,14 +396,14 @@ fn _print[ ]( values: VariadicPack[_, _, Stringable, Ts], *, - sep: String, - end: String, + sep: StringSlice, + end: StringSlice, flush: Bool, file: FileDescriptor, ): @parameter fn print_with_separator[i: Int, T: Stringable](value: T): - _put(str(value), file=file) + _put(str(value).as_string_slice(), file=file) @parameter if i < values.__len__() - 1: @@ -485,11 +426,14 @@ fn _print[ # default, replace `print` with this function. @no_inline fn _print_fmt[ - T: Formattable, - *Ts: Formattable, - sep: StringLiteral = " ", - end: StringLiteral = "\n", -](first: T, *rest: *Ts, flush: Bool = False): + T: Formattable, *Ts: Formattable +]( + first: T, + *rest: *Ts, + sep: StaticString = " ", + end: StaticString = "\n", + flush: Bool = False, +): """Prints elements to the text stream. Each element is separated by `sep` and followed by `end`. @@ -499,12 +443,12 @@ fn _print_fmt[ Parameters: T: The first element type. Ts: The remaining element types. - sep: The separator used between elements. - end: The String to write after printing the elements. Args: first: The first element. rest: The remaining elements. + sep: The separator used between elements. + end: The String to write after printing the elements. flush: If set to true, then the stream is forcibly flushed. """ var writer = Formatter.stdout() From 442152ed6c4b2e043c194e6a2aa43775d86f5b47 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Fri, 21 Jun 2024 12:51:48 -0700 Subject: [PATCH 1011/2019] [mojo-stdlib] Implicit conversion proposal Proposes an `@implicit_conversion` decorator, and updating the implicit conversion semantics to be opt-in; in other words, constructors _not_ explicitly annotated will never be inserted as implicit conversions. MODULAR_ORIG_COMMIT_REV_ID: b0a8124816b4d2b278f3400906d31baead212fd8 --- proposals/opt-in-implicit-conversion.md | 102 ++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 proposals/opt-in-implicit-conversion.md diff --git a/proposals/opt-in-implicit-conversion.md b/proposals/opt-in-implicit-conversion.md new file mode 100644 index 0000000000..ea3858da06 --- /dev/null +++ b/proposals/opt-in-implicit-conversion.md @@ -0,0 +1,102 @@ +# Opt-in Implicit Conversion + +## Background + +`Conversion`: Any mechanism which allows a value of type `S` to be treated as a +different type `T`, without manually calling some function `fn (S) -> T`. + +For the purposes of this discussion I'm omitting conversions to interfaces or +supertypes, since these are conceptually polymorphism, ie. the function is +defined for type `S` but you need to pick the right function implementation +rather than convert to `T` (though this is not strictly true in C++). + +Samples of conversion in a few languages: + +C++ + +```cpp +struct Foo { Foo(int x) {} }; +Foo foo = 10; + +``` + +Rust + +```rust +struct Foo; +impl From for Foo { + fn from(i: i64) -> Foo { Foo(); } +} +let foo = Foo::from(10i64); +let foo2: Foo = (10i64).into(); + +``` + +Scala + +```scala +implicit int2foo(int: Int): Foo = Foo +let foo: Foo = 10 + +``` + +Scala3 + +```scala +given Conversion[Foo, Int] = (_: Int) => Foo +let foo: Foo = 10 + +``` + +## Design Considerations + +- Goal: Make implicit conversion constructors opt-in +- Non-goal: Design the full long-term conversion semantics for Mojo + +## Detailed Design + +### `@implicit_conversion` decorator for conversion constructors + +- Constructors *not* decorated may not be used as conversions (this is an + **opt-in** design) +- Since we know which conversion we’re inserting at a call-site at compile time, + we can correctly mark it as `raises` or not. Implicit conversion constructors + are therefore allowed to be `raises` . +- We continue with our same rules on 1-hop conversion and overload selection. + +### Example + +```python +struct Foo: + @implicit_conversion + fn __init__(inout self, i: Int): + pass + +var foo: Foo = 10 +``` + +## Alternatives Considered + +Since this proposal is just about the specifics of conversion functions, the +design space looks like: + +- Opt-in vs opt-out + - Our existing experience is that we absolutely *do* want to be either opt-in + or opt-out (there’s been pain with existing Stdlib constructors which + shouldn’t be conversions). + - Opt-in has the advantages of forcing explicitness in the call site as well + as removing a vector of unexpected behavior from users coming from Python + and defining plain constructors. + - C++ is opt-out, and has developed a reputation for difficult to debug + conversion semantics. + [Google’s C++ style guide requires never using implicit conversion](https://google.github.io/styleguide/cppguide.html#Implicit_Conversions) + and marking all such methods `explicit`. +- Constructor vs another method (say `__from__[T]`) + - Constructor conversion has the advantage of already existing, and since + Python is strongly typed there’s no real precedent here. + - There doesn’t seem to be appetite for changing this from a constructor to + another method. +- No implicit conversions + - The Google C++ style guide [bans implicit conversions](https://google.github.io/styleguide/cppguide.html#Implicit_Conversions). + - This would be a hard change for the current language. Literal types for + example are only really usable through implicit conversion. From f241f28e7124729fbc72a0695bb224983b646e45 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Fri, 21 Jun 2024 17:13:44 -0500 Subject: [PATCH 1012/2019] [External] [stdlib] Add `Span.copy_from` method (#42058) [External] [stdlib] Add `Span.copy_from` method This PR adds a `copy_from` method to `Span`, which is meant to allow users to easily copy each element from one span to another. Co-authored-by: Lukas Hermann Closes modularml/mojo#3084 MODULAR_ORIG_COMMIT_REV_ID: 59d7b416bc9cc3927a61753a1388d6b2aea72f11 --- stdlib/src/utils/span.mojo | 17 +++++++++++++++++ stdlib/test/utils/test_span.mojo | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index e716878b18..0cfd0f1e48 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -224,3 +224,20 @@ struct Span[ """ return self._data + + @always_inline + fn copy_from[ + lifetime: MutableLifetime, + ](self: Span[T, lifetime], other: Span[T, _]): + """ + Performs an element wise copy from all elements of `other` into all elements of `self`. + + Parameters: + lifetime: The inferred mutable lifetime of the data within the Span. + + Args: + other: The Span to copy all elements from. + """ + debug_assert(len(self) == len(other), "Spans must be of equal length") + for i in range(len(self)): + self[i] = other[i] diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 2292a9d707..0aed924154 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -143,6 +143,17 @@ def test_span_slice(): # assert_equal(res[1], 1) +def test_copy_from(): + var a = List[Int](0, 1, 2, 3) + var b = List[Int](4, 5, 6, 7, 8, 9, 10) + var s = Span(a) + var s2 = Span(b) + s.copy_from(s2[: len(a)]) + for i in range(len(a)): + assert_equal(a[i], b[i]) + assert_equal(s[i], s2[i]) + + def main(): test_span_list_int() test_span_list_str() From b35d8835d017837b6dc8b5c095266b1cd549da65 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 21 Jun 2024 20:27:01 -0700 Subject: [PATCH 1013/2019] [mojo] Prefetch UB fixes (#42129) Progressively land these to avoid merge conflicts as much as possible. MODULAR_ORIG_COMMIT_REV_ID: d7aa01409775aa14274c373a6691371515a4b0df --- stdlib/src/builtin/builtin_list.mojo | 2 ++ stdlib/src/builtin/format_int.mojo | 4 ++++ stdlib/src/builtin/io.mojo | 2 ++ stdlib/src/builtin/string.mojo | 1 + stdlib/src/os/os.mojo | 1 + stdlib/src/python/_cpython.mojo | 4 ++++ stdlib/src/sys/ffi.mojo | 8 ++++++-- stdlib/src/time/time.mojo | 1 + stdlib/src/utils/numerics.mojo | 1 + stdlib/src/utils/static_tuple.mojo | 4 +++- stdlib/test/builtin/test_simd.mojo | 3 ++- stdlib/test/memory/test_maybe_uninitialized.mojo | 1 + stdlib/test/memory/test_memory.mojo | 5 +++++ stdlib/test/memory/test_unsafepointer.mojo | 4 ++++ 14 files changed, 37 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index a04a4d83c7..a259511cac 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -292,6 +292,7 @@ struct VariadicListMem[ # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. self.value = UnsafePointer.address_of(tmp).bitcast[Self._mlir_type]()[] + _ = tmp self._is_owned = False # Provide support for variadics of *owned* arguments. The reference will @@ -312,6 +313,7 @@ struct VariadicListMem[ # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. self.value = UnsafePointer.address_of(tmp).bitcast[Self._mlir_type]()[] + _ = tmp self._is_owned = True @always_inline diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 4cd86aed7c..1f622c0d0c 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -384,6 +384,9 @@ fn _try_write_int[ process_digits[neg_digit_value]() + _ = remaining_int + _ = digit_chars_array + # Re-add +1 byte since the loop ended so we didn't write another char. offset += 1 @@ -401,5 +404,6 @@ fn _try_write_int[ ) fmt.write_str(str_slice) + _ = buf^ return None diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index d798ed1667..040a30beb3 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -113,6 +113,7 @@ fn _printf[ _ = external_call["vprintf", Int32]( fmt.unsafe_cstr_ptr(), UnsafePointer.address_of(loaded_pack) ) + _ = loaded_pack else: with _fdopen(file) as fd: _ = __mlir_op.`pop.external_call`[ @@ -342,6 +343,7 @@ fn _put(x: StringSlice, file: FileDescriptor = stdout): _ = external_call["vprintf", Int32]( x.unsafe_ptr(), arg_ptr.bitcast[UnsafePointer[NoneType]]() ) + _ = tmp else: alias MAX_STR_LEN = 0x1000_0000 diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index a50b83dc42..fa20d41d1b 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1368,6 +1368,7 @@ struct String( result += str(a) elems.each[add_elt]() + _ = is_first return result fn join[T: StringableCollectionElement](self, elems: List[T]) -> String: diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 9916bb5ffc..81fc6f9cd1 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -180,6 +180,7 @@ struct _DirHandle: if name_str == "." or name_str == "..": continue res.append(name_str) + _ = name^ return res diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index b736486c29..fe76a3bbf1 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -720,6 +720,9 @@ struct CPython: self._Py_REFCNT(r), ) self._inc_total_rc() + _ = type + _ = value + _ = traceback return r fn Py_Is( @@ -851,6 +854,7 @@ struct CPython: "refcnt(value)", self._Py_REFCNT(value), ) + _ = v return PyKeyValuePair { key: key, value: value, diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 908197f11d..edeb7ec0bd 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -161,9 +161,11 @@ struct DLHandle(CollectionElement, Boolable): var opaque_function_ptr = external_call[ "dlsym", DTypePointer[DType.int8] ](self.handle.address, name) - return UnsafePointer.address_of(opaque_function_ptr).bitcast[ + var result = UnsafePointer.address_of(opaque_function_ptr).bitcast[ result_type ]()[] + _ = opaque_function_ptr + return result else: return abort[result_type]("get_function isn't supported on windows") @@ -246,7 +248,9 @@ fn _get_dylib_function[ alias func_cache_name = name + "/" + func_name var func_ptr = _get_global_or_null[func_cache_name]() if func_ptr: - return UnsafePointer.address_of(func_ptr).bitcast[result_type]()[] + var result = UnsafePointer.address_of(func_ptr).bitcast[result_type]()[] + _ = func_ptr + return result var dylib = _get_dylib[name, init_fn, destroy_fn](payload) var new_func = dylib._get_function[func_name, result_type]() diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index bb65fb68e4..5d442b1af8 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -253,6 +253,7 @@ fn sleep(sec: Float64): var req = UnsafePointer[_CTimeSpec].address_of(tv_spec) var rem = UnsafePointer[_CTimeSpec]() _ = external_call["nanosleep", Int32](req, rem) + _ = tv_spec fn sleep(sec: Int): diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 7da22695a9..9263c512c4 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -391,6 +391,7 @@ struct FlushDenormals: llvm_intrinsic["llvm.x86.sse.ldmxcsr", NoneType]( UnsafePointer[Int32].address_of(mxcsr) ) + _ = mxcsr return alias ARM_FPCR_FZ = Int64(1) << 24 diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 77c1626dc5..1af6ee3bad 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -213,7 +213,9 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): var ptr = __mlir_op.`pop.array.gep`( Pointer.address_of(arrayCopy).address, offset.value ) - return Pointer(ptr).load() + var result = Pointer(ptr).load() + _ = arrayCopy + return result @always_inline("nodebug") fn __setitem__[ diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 3ba0173caa..b287eadc53 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -200,7 +200,8 @@ def test_issue_30237(): var result = x.fma(c_last, c_second_from_last) for idx in range(coefficients_len - 2): - var c = coefficients[coefficients_len - 3 - idx] + var coefs = coefficients + var c = coefs[coefficients_len - 3 - idx] result = x.fma(result, c) return result diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index b19c8db2fb..e1f60da387 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -84,6 +84,7 @@ def test_maybe_uninitialized_move_from_pointer(): var b = UnsafeMaybeUninitialized[MoveCounter[Int]]() # b is uninitialized here. b.move_from(UnsafePointer.address_of(a)) + _ = a^ # a is uninitialized now. Thankfully, we're working with trivial types assert_equal(b.assume_initialized().move_count, 1) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 8440786e1f..c6105d2c2a 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -86,6 +86,8 @@ def test_memcpy(): _test_memcpy_buf[12]() _test_memcpy_buf[16]() _test_memcpy_buf[19]() + _ = pair1 + _ = pair2 def test_memcpy_dtype(): @@ -128,6 +130,8 @@ def test_memcmp(): var errors2 = memcmp(ptr1, ptr2, 1) assert_equal(errors2, 0) + _ = pair1 + _ = pair2 def test_memcmp_overflow(): @@ -303,6 +307,7 @@ def test_memset(): var buf1 = DTypePointer[DType.int8].alloc(2) memset(buf1, 5, 2) assert_equal(Scalar.load(buf1, 0), 5) + _ = pair def test_pointer_string(): diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index c0313509cb..4c9fedea21 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -112,6 +112,7 @@ def test_refitem_offset(): def test_address_of(): var local = 1 assert_not_equal(0, int(UnsafePointer[Int].address_of(local))) + _ = local def test_bitcast(): @@ -122,6 +123,7 @@ def test_bitcast(): assert_equal(int(ptr), int(ptr.bitcast[Int]())) assert_equal(int(ptr), int(aliased_ptr)) + _ = local def test_unsafepointer_string(): @@ -146,6 +148,8 @@ def test_eq(): var p4 = UnsafePointer[Int].address_of(local) assert_equal(p1, p4) + _ = local + _ = other_local def test_comparisons(): From 9a365aa9434e745b5bc984c5308d610818d443f1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 23 Jun 2024 19:59:44 -0700 Subject: [PATCH 1014/2019] [mojo-lang] Infer parameters from specific Self types in initializers. This teaches the compiler to notice when an initializer has bound parameters indicated in the type of 'self', rather than using the plain old default 'Self' type. When these exist, we can infer unbound parameters from it. See the changelog for an example. This will allow simplifing use of things like `StaticIntTuple` MODULAR_ORIG_COMMIT_REV_ID: 0c50aaf8af1fa4418d56db4e78b084bd08e16355 --- docs/changelog.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 2229f95407..9483f7d1a5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -56,6 +56,23 @@ what we publish. Note that `async` functions do not yet support indirect calls, `ref` results, and constructors. +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: + + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass + + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. + ``` + - The `Reference` type (and many iterators) now use "inferred" parameters to represent the mutability of their lifetime, simplifying the interface. From 1c77da5baf45a25a8d7d4a493305be228f9b7387 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 23 Jun 2024 20:27:19 -0700 Subject: [PATCH 1015/2019] [mojo-stdlib] Switch `List.__getitem__` to returning a ref. This switches `List.__getitem__` (when taking a single Int index to return a reference to the element instead of having a getter + setter pair. MODULAR_ORIG_COMMIT_REV_ID: 990424b38cb10d1562bc55ba610de5700af9b11f --- stdlib/src/collections/list.mojo | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 63bf454c50..4bcef6a022 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -197,24 +197,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # Operator dunders # ===-------------------------------------------------------------------===# - fn __setitem__(inout self, idx: Int, owned value: T): - """Sets a list element at the given index. - - Args: - idx: The index of the element. - value: The value to assign. - """ - var normalized_idx = idx - debug_assert( - -self.size <= normalized_idx < self.size, - "index must be within bounds", - ) - - if normalized_idx < 0: - normalized_idx += len(self) - - self.unsafe_set(normalized_idx, value^) - @always_inline fn __contains__[ T2: ComparableCollectionElement @@ -722,16 +704,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): return res^ @always_inline - fn __getitem__(self, idx: Int) -> T: - """Gets a copy of the list element at the given index. - - FIXME(lifetimes): This should return a reference, not a copy! + fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T: + """Gets the list element at the given index. Args: idx: The index of the element. Returns: - A copy of the element at the given index. + A reference to the element at the given index. """ var normalized_idx = idx debug_assert( From 9810fe7d64cb98db3c30dd70215a845e8a029f3d Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 24 Jun 2024 01:01:15 -0700 Subject: [PATCH 1016/2019] [mojo] Fix UB MODULAR_ORIG_COMMIT_REV_ID: 4aa71edb97646e40abf1bb5d2b334c15967a9aec --- stdlib/src/builtin/_closure.mojo | 4 ++++ stdlib/src/builtin/string.mojo | 1 + stdlib/src/os/os.mojo | 1 + 3 files changed, 6 insertions(+) diff --git a/stdlib/src/builtin/_closure.mojo b/stdlib/src/builtin/_closure.mojo index 26898877cc..b1731c6371 100644 --- a/stdlib/src/builtin/_closure.mojo +++ b/stdlib/src/builtin/_closure.mojo @@ -49,3 +49,7 @@ fn __closure_wrapper_noop_copy( owned other: __mlir_type.`!kgen.pointer`, / ) -> __mlir_type.`!kgen.pointer`: return other + + +fn __ownership_keepalive[*Ts: AnyType](*args: *Ts): + pass diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index fa20d41d1b..81627c6ba7 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -994,6 +994,7 @@ struct String( arg.format_to(writer) args.each[write_arg]() + _ = writer^ return output^ diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 81fc6f9cd1..b2c2489cc7 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -155,6 +155,7 @@ struct _DirHandle: if name_str == "." or name_str == "..": continue res.append(name_str) + _ = name^ return res From b70a9f1b213cc681106a10db3a94678ed322eff3 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:31:57 -0400 Subject: [PATCH 1017/2019] [stdlib] Fix number of bytes returned by `FileHandle.read`. The currently implementation simply returns the argument `size` multiplied by `sizeof[type]`, which is incorrect. MODULAR_ORIG_COMMIT_REV_ID: c4921cc5a2d9fca6183851fba64c9e5afd4a43d1 --- stdlib/src/builtin/file.mojo | 10 +++++---- stdlib/test/builtin/test_file.mojo | 33 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index d61c11add1..4a09cf1353 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -266,19 +266,21 @@ struct FileHandle: if not self.handle: raise Error("invalid file handle") - var size_copy = size * sizeof[type]() var err_msg = _OwnedStringRef() - external_call["KGEN_CompilerRT_IO_FileReadToAddress", NoneType]( + var bytes_read = external_call[ + "KGEN_CompilerRT_IO_FileReadToAddress", Int64 + ]( self.handle, ptr, - UnsafePointer.address_of(size_copy), + size * sizeof[type](), UnsafePointer.address_of(err_msg), ) if err_msg: raise (err_msg^).consume_as_error() - return size_copy + + return bytes_read fn read_bytes(self, size: Int64 = -1) raises -> List[UInt8]: """Reads data from a file and sets the file handle seek position. If diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index c323ee9f7a..af7fc094bd 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -102,6 +102,39 @@ def test_file_read_context(): ) +def test_file_read_to_address(): + with open( + _dir_of_current_file() / "test_file_dummy_input.txt", + "r", + ) as f: + var ptr = DTypePointer[DType.uint8].alloc(1000) + assert_equal(f.read(ptr), 954) + assert_equal(Scalar.load(ptr, 0), 76) # L + assert_equal(Scalar.load(ptr, 1), 111) # o + assert_equal(Scalar.load(ptr, 2), 114) # r + assert_equal(Scalar.load(ptr, 3), 101) # e + assert_equal(Scalar.load(ptr, 4), 109) # m + assert_equal(Scalar.load(ptr, 5), 32) # + assert_equal(Scalar.load(ptr, 56), 10) # + + with open( + _dir_of_current_file() / "test_file_dummy_input.txt", + "r", + ) as f: + var ptr = DTypePointer[DType.uint8].alloc(1000) + assert_equal(f.read(ptr, 1000), 954) + + with open( + _dir_of_current_file() / "test_file_dummy_input.txt", + "r", + ) as f: + var ptr = DTypePointer[DType.uint8].alloc(1000) + assert_equal(f.read(ptr, 30), 30) + assert_equal(f.read(ptr, 1), 1) + assert_equal(f.read(ptr, 2), 2) + assert_equal(f.read(ptr, 100), 100) + + def test_file_seek(): import os From 513a312a8cc760b778527f37dc4036fe7ad780fd Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 24 Jun 2024 11:43:02 -0500 Subject: [PATCH 1018/2019] [External] [stdlib] Use `ValueDestructorRecorder` in `test_inline_list.mojo` (#42108) [External] [stdlib] Use `ValueDestructorRecorder` in `test_inline_list.mojo` This PR is a small piece of mojo#2965 . I'm trying to make mojo#2965 smaller to make it easier to work on. In itself, this PR is a refactoring. The goal is to unify the "destructor counters" that are present in the unit tests and use only the one in `stdlib/test/test_utils/types.mojo`. The goal of those structs is to record the number of `__del__` call being made to make sure the lifecycle of elements in a collection is correct. To make it usable with `InlineList`, I had to add the explicit copy constructor. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3094 MODULAR_ORIG_COMMIT_REV_ID: 4ff2d0ea67c29b144b6c76d3fb0f8db93143e41a --- stdlib/test/collections/test_inline_list.mojo | 24 +++---------------- stdlib/test/test_utils/types.mojo | 6 ++++- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index ca0f2e831a..f572304775 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -14,7 +14,7 @@ from collections import InlineList, Set -from test_utils import MoveCounter +from test_utils import MoveCounter, ValueDestructorRecorder from testing import assert_equal, assert_false, assert_raises, assert_true @@ -58,33 +58,15 @@ def test_append_triggers_a_move(): assert_equal(inline_list[i].move_count, 1) -@value -struct ValueToCountDestructor(CollectionElementNew): - var value: Int - var destructor_counter: UnsafePointer[List[Int]] - - fn __init__(inout self, *, other: Self): - """Explicitly copy the provided value. - - Args: - other: The value to copy. - """ - self.value = other.value - self.destructor_counter = other.destructor_counter - - fn __del__(owned self): - self.destructor_counter[].append(self.value) - - def test_destructor(): """Ensure we delete the right number of elements.""" var destructor_counter = List[Int]() alias capacity = 32 - var inline_list = InlineList[ValueToCountDestructor, capacity=capacity]() + var inline_list = InlineList[ValueDestructorRecorder, capacity=capacity]() for index in range(capacity): inline_list.append( - ValueToCountDestructor( + ValueDestructorRecorder( index, UnsafePointer.address_of(destructor_counter) ) ) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 67bd9a8beb..a47f4e6351 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -109,9 +109,13 @@ struct MoveCounter[T: CollectionElementNew]( @value -struct ValueDestructorRecorder: +struct ValueDestructorRecorder(ExplicitlyCopyable): var value: Int var destructor_counter: UnsafePointer[List[Int]] + fn __init__(inout self, *, other: Self): + self.value = other.value + self.destructor_counter = other.destructor_counter + fn __del__(owned self): self.destructor_counter[].append(self.value) From 4ab3d56e1649b47018078b43bac0274d93086976 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 24 Jun 2024 10:15:30 -0700 Subject: [PATCH 1019/2019] [Docs] Cleanup--remove obsolete material, modernize links MODULAR_ORIG_COMMIT_REV_ID: 2bfff50b353e9f733582b4d1207e2b8c84b92cad --- docs/changelog-released.md | 52 ++--- docs/faq.md | 8 +- docs/manual/basics.ipynb | 16 +- .../manual/decorators/register-passable.ipynb | 20 +- docs/manual/decorators/staticmethod.ipynb | 2 +- docs/manual/decorators/value.ipynb | 2 +- docs/manual/functions.ipynb | 6 +- docs/manual/index.md | 6 +- docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/lifecycle/index.ipynb | 14 +- docs/manual/lifecycle/life.ipynb | 42 ++-- docs/manual/packages.md | 8 +- docs/manual/parameters/index.ipynb | 12 +- docs/manual/python/index.ipynb | 2 +- docs/manual/structs.ipynb | 14 +- docs/manual/traits.ipynb | 22 +- docs/manual/types.ipynb | 51 ++-- docs/manual/values/index.ipynb | 2 +- docs/manual/values/ownership.ipynb | 18 +- docs/manual/values/value-semantics.ipynb | 4 +- docs/manual/variables.ipynb | 39 +--- docs/upgrade-guide.md | 219 ------------------ 22 files changed, 149 insertions(+), 412 deletions(-) delete mode 100644 docs/upgrade-guide.md diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 150d28b107..8deb4b1a1b 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -2358,7 +2358,7 @@ experience without dedicated sugar. ### ⭐️ New - A new Mojo-native dictionary type, - [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. + [`Dict`](/mojo/stdlib/collections/dict) for storing key-value pairs. `Dict` stores values that conform to the [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. Keys need to conform to the new @@ -2843,7 +2843,7 @@ experience without dedicated sugar. a given trait. The following section gives a brief overview of traits—see the - [Mojo Manual](/mojo/manual/traits.html) and this + [Mojo Manual](/mojo/manual/traits) and this [traits blog post](https://modul.ar/traits-blog) for more details! Traits are declared with the `trait` keyword. The bodies of traits should @@ -2906,7 +2906,7 @@ experience without dedicated sugar. the_parents(x) ``` - For more information, see the [Traits page](/mojo/manual/traits.html) + For more information, see the [Traits page](/mojo/manual/traits) in the Mojo Manual. - A fundamental `Destructable` trait has been added to the language. This is a @@ -2959,8 +2959,8 @@ experience without dedicated sugar. - The [Mojo Manual](/mojo/manual/) is an all-new, complete Mojo user guide. It doesn't include _everything_ about Mojo yet, but it includes a lot, - and more than the original [programming - manual](/mojo/programming-manual.html) (now deprecated). + and more than the original programming + manual (now deprecated). Plus, the entire Mojo Manual and other Mojo docs are now [open-sourced on GitHub](https://github.com/modularml/mojo/tree/main/docs), and we'd love @@ -3091,7 +3091,7 @@ the previous "read to EOF" behavior when size is negative. let data = binary_path.read_bytes() ``` -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `save()` and `load()` +- [`Tensor`](/mojo/stdlib/tensor/tensor) has new `save()` and `load()` methods to save and load to file. These methods preserve shape and datatype information. For example: @@ -3165,15 +3165,15 @@ the previous "read to EOF" behavior when size is negative. print(len(nums)) ``` -- The assert functions in the [`testing`](/mojo/stdlib/testing/testing.html) +- The assert functions in the [`testing`](/mojo/stdlib/testing/testing) package now raise an `Error` when the assertion fails instead of returning a `Bool` for whether the assertion succeeded or not. -- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases.html) type are no +- Parameters of [`AnyType`](/mojo/stdlib/builtin/type_aliases) type are no longer (implicitly) assumed to be register-passable. A new `AnyRegType` type is used to represent generic types that are register passable. -- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) +- Changing the units in a [`benchmark`](/mojo/stdlib/benchmark/benchmark) report is now an argument instead of a parameter: ```mojo @@ -3269,8 +3269,8 @@ the previous "read to EOF" behavior when size is negative. KwParamStruct[msg="hello", a=42]() # prints 'hello 42' ``` - For more detail, see the [programming - manual](/mojo/manual/parameters/index.html#optional-parameters-and-keyword-parameters). + For more detail, see the [Mojo + Manual](/mojo/manual/parameters/#optional-parameters-and-keyword-parameters). For the time being, the following notable limitations apply: @@ -3348,7 +3348,7 @@ the previous "read to EOF" behavior when size is negative. pass ``` -- The [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module has been +- The [`benchmark`](/mojo/stdlib/benchmark/benchmark) module has been simplified and improved so you can now run: ```mojo @@ -3415,14 +3415,14 @@ the previous "read to EOF" behavior when size is negative. CtadStructWithDefault[b=7].foo(Thing[6]()) # prints '🔥 6 7 8' ``` -- [`Tensor`](/mojo/stdlib/tensor/tensor.html) has new `fromfile()` and +- [`Tensor`](/mojo/stdlib/tensor/tensor) has new `fromfile()` and `tofile()` methods to save and load as bytes from a file. - The built-in `print()` function now works on the - [`Tensor`](/mojo/stdlib/tensor/tensor.html) type. + [`Tensor`](/mojo/stdlib/tensor/tensor) type. -- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and - [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors +- [`TensorShape`](/mojo/stdlib/tensor/tensor_shape) and + [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape) now have constructors that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list/List) and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. @@ -3556,7 +3556,7 @@ the previous "read to EOF" behavior when size is negative. print(DefaultParams["meow"]().message) # prints 'meow' ``` -- The new [`file`](/mojo/stdlib/builtin/file.html) module adds basic file I/O +- The new [`file`](/mojo/stdlib/builtin/file) module adds basic file I/O support. You can now write: ```mojo @@ -3856,7 +3856,7 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco value from one location to another. For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life.html#move-constructors). + [move constructors](/mojo/manual/lifecycle/life#move-constructors). - The Error type in Mojo has changed. Instead of extracting the error message using `error.value` you will now extract the error message using @@ -3911,23 +3911,23 @@ All earlier releases were considered version 0.1. ### ⭐️ New - A new `clobber_memory` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. + [`benchmark`](/mojo/stdlib/benchmark/benchmark) module. The clobber memory function tells the system to flush all memory operations at the specified program point. This allows you to benchmark operations without the compiler reordering memory operations. - A new `keep` function has been added to the - [`benchmark`](/mojo/stdlib/benchmark/benchmark.html) module. The `keep` + [`benchmark`](/mojo/stdlib/benchmark/benchmark) module. The `keep` function tries to tell the compiler not to optimize the variable away if not used. This allows you to avoid compiler's dead code elimination mechanism, with a low footprint side effect. - New `shift_right` and `shift_left` functions have been added to the - [`simd`](/mojo/stdlib/builtin/simd.html) module. They shift the elements in + [`simd`](/mojo/stdlib/builtin/simd) module. They shift the elements in a SIMD vector right/left, filling elements with zeros as needed. - A new `cumsum` function has been added to the - [`reduction`](/mojo/stdlib/algorithm/reduction.html) module that computes + [`reduction`](/mojo/stdlib/algorithm/reduction) module that computes the cumulative sum (also known as scan) of input elements. - Mojo Jupyter kernel now supports code completion. @@ -4235,7 +4235,7 @@ All earlier releases were considered version 0.1. dependencies between global variables, and their destructors are called in the reverse order. -- The [Mojo programming manual](/mojo/programming-manual.html) is now written +- The Mojo programming manual is now written as a Jupyter notebook, and available in its entirety in the Mojo Playground (`programming-manual.ipynb`). (Previously, `HelloMojo.ipynb` included most of the same material, but it was not up-to-date.) @@ -4359,7 +4359,7 @@ possible to write the following: ``` For details on the overload resolution logic, see the Mojo Manual section on - [parameters](/mojo/manual/parameters/index.html#overloading-on-parameters). + [parameters](/mojo/manual/parameters/#overloading-on-parameters). - A new `cost_of()` function has been added to `Autotune`. This meta-function must be invoked at compile time, and it returns the number of MLIR operations @@ -4491,7 +4491,7 @@ only in declared parameter names, e.g. the following now works correctly: ``` When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) + [`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and) value (this is common in initializers for example), it is allowed to do whatever it wants with the value and destroy it when it is finished. In order to support this, @@ -4649,7 +4649,7 @@ only in declared parameter names, e.g. the following now works correctly: optimized Matmul implementation is 3x faster. - Renamed the [`^` postfix -operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and) +operator](/mojo/manual/values/ownership#transfer-arguments-owned-and) from "consume" to "transfer." #### 🛠️ Fixed diff --git a/docs/faq.md b/docs/faq.md index a6d385cbc0..49ca3bea84 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -44,7 +44,7 @@ it’s missing. We are guided more by pragmatism than novelty, but Mojo’s use and domains in a way that other languages haven’t demonstrated (for an example of Mojo talking directly to MLIR, see our [low-level IR in Mojo notebook](/mojo/notebooks/BoolMLIR)). It also -includes autotuning, and has caching and distributed compilation built into its +has caching and distributed compilation built into its core. We also believe Mojo has a good chance of unifying hybrid packages in the broader Python community. @@ -210,12 +210,6 @@ means that Mojo is easily extensible to any hardware backend. For more information, read about our vision for [pluggable hardware](https://www.modular.com/hardware). -### How does Mojo autotuning work? - -For details about what autotuning capabilities we support so far, check out -the Mojo Manual section on [metaprogramming](/mojo/manual/parameters/). -But stay tuned for more details! - ### Who writes the software to add more hardware support for Mojo? Mojo provides all the language functionality necessary for anyone to extend diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index 59e6caffae..f7730cf07e 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -46,7 +46,7 @@ ":::note\n", "\n", "Mojo is a young language and there are many [features still\n", - "missing](/mojo/roadmap.html). As such, Mojo is currently **not** meant for\n", + "missing](/mojo/roadmap). As such, Mojo is currently **not** meant for\n", "beginners. Even this basics section assumes some programming experience.\n", "However, throughout the Mojo Manual, we try not to assume experience with any\n", "particular language.\n", @@ -147,7 +147,7 @@ "metadata": {}, "source": [ "For more details, see the page about\n", - "[functions](/mojo/manual/functions.html)." + "[functions](/mojo/manual/functions)." ] }, { @@ -237,7 +237,7 @@ "(only the argument and return types must be declared in `fn` functions).\n", "\n", "For more details, see the page about\n", - "[variables](/mojo/manual/variables.html)." + "[variables](/mojo/manual/variables)." ] }, { @@ -303,7 +303,7 @@ "metadata": {}, "source": [ "For more details, see the page about\n", - "[structs](/mojo/manual/structs.html)." + "[structs](/mojo/manual/structs)." ] }, { @@ -398,7 +398,7 @@ "`SomeTrait`. Thus, `fun_with_traits()` is known as a \"generic function\" because\n", "it accepts a _generalized_ type instead of a specific type.\n", "\n", - "For more details, see the page about [traits](/mojo/manual/traits.html)." + "For more details, see the page about [traits](/mojo/manual/traits)." ] }, { @@ -629,7 +629,7 @@ "source": [ "Documenting your code with these kinds of comments (known as \"docstrings\")\n", "is a topic we've yet to fully specify, but you can generate an API reference\n", - "from docstrings using the [`mojo doc` command](/mojo/cli/doc.html)." + "from docstrings using the [`mojo doc` command](/mojo/cli/doc)." ] }, { @@ -700,7 +700,7 @@ "\n", "If you're in the mood to read more, continue through each page of this\n", "Mojo Manual using the buttons at the bottom of each page—the next page from\n", - "here is [Functions](/mojo/manual/functions.html).\n", + "here is [Functions](/mojo/manual/functions).\n", "\n", "Otherwise, here are some other resources to check out:\n", "\n", @@ -716,7 +716,7 @@ " that teach advanced Mojo features.\n", "\n", "- To see all the available Mojo APIs, check out the [Mojo standard library\n", - " reference](/mojo/lib.html)." + " reference](/mojo/lib)." ] } ], diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb index 682a6f9259..9627f7f5dd 100644 --- a/docs/manual/decorators/register-passable.ipynb +++ b/docs/manual/decorators/register-passable.ipynb @@ -85,19 +85,6 @@ "This behavior is what we expect from `Pair`, with or without the decorator." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note Updated syntax\n", - "\n", - "Register passable types now accept standard lifecycle methods, as shown above. \n", - "Prior to Mojo 24.1, register-passable types required a non-standard constructor that returned a value, with syntax like this: `return Self{a: one, b: two}`. \n", - "The older syntax is still supported but will be removed in a future release. For more information about this change, see the [Mojo 24.1 release notes](/mojo/changelog#v241-2024-02-29).\n", - "\n", - ":::" - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -115,7 +102,7 @@ "instead of being passed by-pointer.\n", "\n", "1. `@register_passable` types cannot have a [`__moveinit__()`\n", - "constructor](/mojo/manual/lifecycle/life.html#move-constructors), because\n", + "constructor](/mojo/manual/lifecycle/life#move-constructors), because\n", "values passed in a register cannot be passed by reference.\n" ] }, @@ -154,7 +141,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This is similar to the [`@value`](/mojo/manual/decorators/value.html) decorator,\n", + "This is similar to the [`@value`](/mojo/manual/decorators/value) decorator,\n", "except when using `@register_passable(\"trivial\")` the only lifecycle method\n", "you're allowed to define is the `__init__()` constructor (but you don't have\n", "to)—you _cannot_ define any copy or move constructors or a destructor.\n", @@ -183,8 +170,7 @@ "This decorator is due for reconsideration. Lack of custom\n", "copy/move/destroy logic and \"passability in a register\" are orthogonal concerns\n", "and should be split. This former logic should be subsumed into a more general\n", - "[`@value(\"trivial\")`](/mojo/manual/decorators/value.html) decorator, which is\n", - "orthogonal from `@register_passable`.\n", + "decorator, which is orthogonal to `@register_passable`.\n", "\n", ":::" ] diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index b8d46eae86..f6e798b97a 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -60,7 +60,7 @@ "access instance data.\n", "\n", "For more information see the documentation on\n", - "[static methods](/mojo/manual/structs.html#static-methods)." + "[static methods](/mojo/manual/structs#static-methods)." ] } ], diff --git a/docs/manual/decorators/value.ipynb b/docs/manual/decorators/value.ipynb index b6753b42e5..807bd962cb 100644 --- a/docs/manual/decorators/value.ipynb +++ b/docs/manual/decorators/value.ipynb @@ -86,7 +86,7 @@ "metadata": {}, "source": [ "For more information about these lifecycle methods, read\n", - "[Life of a value](/mojo/manual/lifecycle/life.html)." + "[Life of a value](/mojo/manual/lifecycle/life)." ] } ], diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 541af41f3c..17ef6e6fd9 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -261,7 +261,7 @@ "metadata": {}, "source": [ "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership.html#mutable-arguments-inout).\n", + "[`inout`](/mojo/manual/values/ownership#mutable-arguments-inout).\n", "\n", "Any optional arguments must appear after any required arguments. [Keyword-only\n", "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", @@ -599,7 +599,7 @@ "\n", " - Variadic keyword arguments are always implicitly treated as if they\n", " were declared with the `owned` [argument \n", - " convention](/mojo/manual/values/ownership.html#argument-conventions), and\n", + " convention](/mojo/manual/values/ownership#argument-conventions), and\n", " can't be declared otherwise:\n", "\n", " ```mojo\n", @@ -787,7 +787,7 @@ "ambiguity by explicitly casting your value to a supported argument type. For\n", "example, in the following code, we want to call the overloaded `foo()`\n", "function, but both implementations accept an argument that supports [implicit\n", - "conversion](/mojo/manual/variables.html#implicit-type-conversion) from\n", + "conversion](/mojo/manual/variables#implicit-type-conversion) from\n", "`StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a\n", "compiler error. We can fix it by casting the value to the type we really want:" ] diff --git a/docs/manual/index.md b/docs/manual/index.md index fc1754026b..7f8256e80c 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -39,20 +39,20 @@ feedback](/mojo/community). - **Value ownership** - - [Intro to value ownership](/mojo/manual/values/index) + - [Intro to value ownership](/mojo/manual/values/) - [Value semantics](/mojo/manual/values/value-semantics) - [Ownership and borrowing](/mojo/manual/values/ownership) - **Value lifecycle** - - [Intro to value lifecycle](/mojo/manual/lifecycle/index) + - [Intro to value lifecycle](/mojo/manual/lifecycle/) - [Life of a value](/mojo/manual/lifecycle/life) - [Death of a value](/mojo/manual/lifecycle/death) - **Traits and parameters** - [Traits](/mojo/manual/traits) - - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/index) + - [Parameterization: compile-time metaprogramming](/mojo/manual/parameters/) - **Pointers** diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 980c199199..dde64c99bf 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -418,7 +418,7 @@ "dismantle the `existing`/`self` value that's `owned`. That is, `__moveinit__()`\n", "implicitly destroys sub-elements of `existing` in order to transfer ownership\n", "to a new instance (read more about the [move\n", - "constructor](/mojo/manual/lifecycle/life.html#move-constructor)),\n", + "constructor](/mojo/manual/lifecycle/life#move-constructor)),\n", "while `__del__()` implements the deletion logic for its `self`. As such, they\n", "both need to own and transform elements of the `owned` value, and they\n", "definitely don’t want the original `owned` value's destructor to also run—that\n", diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 1c3226f9d0..39f645eca4 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -21,7 +21,7 @@ "source": [ "So far, we've explained how Mojo allows you to build high-performance code that\n", "is memory safe _without_ manually managing memory, using Mojo's [ownership\n", - "model](/mojo/manual/values/ownership.html). However, Mojo is designed for\n", + "model](/mojo/manual/values/ownership). However, Mojo is designed for\n", "[systems programming](https://en.wikipedia.org/wiki/Systems_programming), which\n", "often requires manual memory management for custom data types. So, Mojo lets\n", "you do that as you see fit. To be clear, Mojo has no reference counter and no\n", @@ -31,9 +31,9 @@ "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", - "[structs](/mojo/manual/structs.html). You can actually write your own\n", + "[structs](/mojo/manual/structs). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", - "[MLIR dialects](/mojo/notebooks/BoolMLIR.html).\n", + "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", "\n", "What's great about the Mojo language is that it provides you these low-level\n", "tools for systems programming, but within a framework that helps you build\n", @@ -43,7 +43,7 @@ "semantics](/mojo/manual/values/value-semantics), the programmer instantiating\n", "your type/object doesn't need to think about memory management at all, and the\n", "behavior will be safe and predictable, thanks to [value\n", - "ownership](/mojo/manual/values/ownership.html).\n", + "ownership](/mojo/manual/values/ownership).\n", "\n", "In summary, it's the responsibility of the type author to manage the memory and\n", "resources for each value type, by implementing specific lifecycle methods, such\n", @@ -65,7 +65,7 @@ "First, let's clarify some terminology:\n", "\n", "- The \"lifecycle\" of a value is defined by various [dunder\n", - "methods](/mojo/manual/structs.html#special-methods) in a struct.\n", + "methods](/mojo/manual/structs#special-methods) in a struct.\n", "Each lifecycle event is handled by a different method,\n", "such as the constructor (`__init__()`), the destructor (`__del__()`), the copy\n", "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", @@ -86,8 +86,8 @@ "As you might imagine, keeping track of a value's lifetime can be difficult if a\n", "value is shared across functions many times during the life of a program.\n", "However, Mojo makes this predictable partly through its [value\n", - "semantics](/mojo/manual/values/value-semantics.html) and [value\n", - "ownership](/mojo/manual/values/ownership.html) (both prerequisite readings for\n", + "semantics](/mojo/manual/values/value-semantics) and [value\n", + "ownership](/mojo/manual/values/ownership) (both prerequisite readings for\n", "the following sections). The final piece of the puzzle for lifetime management\n", "is the value lifecycle: every value (defined in a struct) needs to implement\n", "key lifecycle methods that define how a value is created and destroyed." diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index a8d0d00b4c..0a0a599315 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -23,7 +23,7 @@ "up until the value is last used, at which point Mojo destroys it. This page\n", "describes how every value in Mojo is created, copied, and moved. (The next\n", "page describes [how values are\n", - "destroyed](/mojo/manual/lifecycle/death.html).)\n", + "destroyed](/mojo/manual/lifecycle/death).)\n", "\n", "All data types in Mojo—including basic types in the standard library such as\n", "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", @@ -31,7 +31,7 @@ "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", - "[struct](/mojo/manual/structs.html). This means the creation and\n", + "[struct](/mojo/manual/structs). This means the creation and\n", "destruction of any piece of data follows the same lifecycle rules, and you can\n", "define your own data types that work exactly the same way.\n", "\n", @@ -130,7 +130,7 @@ "metadata": {}, "source": [ "An instance of `MyPet` can also be\n", - "[borrowed](/mojo/manual/values/ownership.html#immutable-arguments-borrowed)\n", + "[borrowed](/mojo/manual/values/ownership#immutable-arguments-borrowed)\n", "and destroyed, but it currently can't be copied or moved.\n", "\n", "We believe this is a good default starting point, because there are no built-in\n", @@ -143,9 +143,9 @@ "Mojo does not require a destructor to destroy an object. As long as\n", "all fields in the struct are destructible (every type in the standard library\n", "is destructible, except for\n", - "[pointers](/mojo/stdlib/memory/unsafe.html)), then Mojo knows how to destroy\n", + "[pointers](/mojo/stdlib/memory/unsafe)), then Mojo knows how to destroy\n", "the type when its lifetime ends. We'll discuss that more in [Death of a\n", - "value](/mojo/manual/lifecycle/death.html).\n", + "value](/mojo/manual/lifecycle/death).\n", "\n", ":::" ] @@ -157,14 +157,14 @@ "### Overloading the constructor\n", "\n", "Like any other function/method, you can\n", - "[overload](/mojo/manual/functions.html#overloaded-functions) the\n", + "[overload](/mojo/manual/functions#overloaded-functions) the\n", "`__init__()` constructor to initialize the object with different arguments. For\n", "example, you might want a default constructor that sets some default values and\n", "takes no arguments, and then additional constructors that accept more arguments.\n", "\n", "Just be aware that, in order to modify any fields, each constructor must\n", "declare the `self` argument with the [`inout`\n", - "convention](/mojo/manual/values/ownership.html#mutable-arguments-inout). If you\n", + "convention](/mojo/manual/values/ownership#mutable-arguments-inout). If you\n", "want to call one constructor from another, you simply call upon that\n", "constructor as you would externally (you don't need to pass `self`).\n", "\n", @@ -384,7 +384,7 @@ "\n", "Also, notice that the `existing` argument in `__copyinit__()` is immutable\n", "because the default [argument\n", - "convention](/mojo/manual/values/ownership.html#argument-conventions) in an `fn`\n", + "convention](/mojo/manual/values/ownership#argument-conventions) in an `fn`\n", "function is `borrowed`—this is a good thing because this function should not\n", "modify the contents of the value being copied.\n", "\n", @@ -414,7 +414,7 @@ "source": [ "What makes Mojo's copy behavior different, compared to other languages, is that\n", "`__copyinit__()` is designed to perform a deep copy of all fields in the type\n", - "(as per [value semantics](/mojo/manual/values/value-semantics.html)). That is,\n", + "(as per [value semantics](/mojo/manual/values/value-semantics)). That is,\n", "it copies heap-allocated values, rather than just copying the pointer.\n", "\n", "However, the Mojo compiler doesn't enforce this, so it's the type author's\n", @@ -512,7 +512,7 @@ "In `HeapArray`, we must use the `__del__()` destructor to free the\n", "heap-allocated data when the `HeapArray` lifetime ends, but Mojo automatically\n", "destroys all other fields when their respective lifetimes end. We'll discuss\n", - "this destructor more in [Death of a value](/mojo/manual/lifecycle/death.html).\n", + "this destructor more in [Death of a value](/mojo/manual/lifecycle/death).\n", "\n", ":::" ] @@ -524,7 +524,7 @@ "If your type doesn't use any pointers for heap-allocated data, then writing the\n", "constructor and copy constructor is all boilerplate code that you shouldn't\n", "have to write. For most structs that don't manage memory explicitly, you can \n", - "just add the [`@value` decorator](/mojo/manual/decorators/value.html) to your\n", + "just add the [`@value` decorator](/mojo/manual/decorators/value) to your\n", "struct definition and Mojo will synthesize the `__init__()`, `__copyinit__()`,\n", "and `__moveinit__()` methods.\n", "\n", @@ -532,7 +532,7 @@ "\n", "Mojo also calls upon the copy constructor when a value is passed to a\n", "function that takes the argument as\n", - "[`owned`](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)\n", + "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", "operator `^`), then Mojo instead invokes the move constructor.\n", @@ -547,7 +547,7 @@ "## Move constructor\n", "\n", "Although copying values provides predictable behavior that matches Mojo's\n", - "[value semantics](/mojo/manual/values/value-semantics.html), copying some data\n", + "[value semantics](/mojo/manual/values/value-semantics), copying some data\n", "types can be a significant hit on performance. If you're familiar with\n", "reference semantics, then the solution here might seem clear: instead of making\n", "a copy when passing a value, share the value as a reference. And if the\n", @@ -558,7 +558,7 @@ "\n", "To support moving a value, implement the `__moveinit__()` method. The \n", "`__moveinit__()` method performs a consuming move: it [transfers\n", - "ownership](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)\n", + "ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", "of a value from one variable to another when the original variable's lifetime\n", "ends (also called a \"destructive move\").\n", "\n", @@ -569,7 +569,7 @@ "the move constructors are only part of the implementation for how Mojo\n", "transfers ownership of a value. You can learn more in the section about\n", "[ownership\n", - "transfer](/mojo/manual/values/ownership.html#transfer-arguments-owned-and).\n", + "transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and).\n", "\n", ":::" ] @@ -640,7 +640,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "operator](/mojo/manual/values/ownership.html#transfer-arguments-owned-and)).\n", + "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", @@ -693,7 +693,7 @@ "integers, floats, and booleans, is very cheap. Yet, if you allow your type to\n", "be copied, then there's generally no reason to disallow moves, so you can\n", "synthesize both constructors by adding the [`@value`\n", - "decorator](/mojo/manual/decorators/value.html).\n", + "decorator](/mojo/manual/decorators/value).\n", "\n", ":::" ] @@ -705,13 +705,13 @@ "## Simple value types {#value-decorator}\n", "\n", "Because copy and move constructors are opt-in, Mojo provides great control for\n", - "exotic usecases (such as for atomic values that should never be copied or\n", + "exotic use cases (such as for atomic values that should never be copied or\n", "moved), but most structs are simple aggregations of other types that should be\n", "easily copied and moved, and we don't want to write a lot of boilerplate\n", "constructors for those simple value types.\n", "\n", "To solve this, Mojo provides the [`@value`\n", - "decorator](/mojo/manual/decorators/value.html), which synthesizes the\n", + "decorator](/mojo/manual/decorators/value), which synthesizes the\n", "boilerplate code for the `__init__()`, `__copyinit__()`, and `__moveinit__()`\n", "methods.\n", "\n", @@ -825,7 +825,7 @@ "Also notice that the `MyPet` struct above doesn't include the `__del__()`\n", "destructor (the `@value` decorator does not synthesize this), because Mojo\n", "doesn't need it to destroy fields, as discussed in [Death of a\n", - "value](/mojo/manual/lifecycle/death.html)\n", + "value](/mojo/manual/lifecycle/death)\n", "\n", ":::" ] @@ -871,7 +871,7 @@ "it is safe to ignore for general application-level code.\n", "\n", "For more information, see the [`@register_passable`\n", - "documentation](/mojo/manual/decorators/register-passable.html).\n", + "documentation](/mojo/manual/decorators/register-passable).\n", "\n", ":::note TODO\n", "\n", diff --git a/docs/manual/packages.md b/docs/manual/packages.md index aec79c6583..f9a146f37e 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -9,7 +9,7 @@ Mojo provides a packaging system that allows you to organize and compile code libraries into importable files. This page introduces the necessary concepts about how to organize your code into modules and packages (which is a lot like Python), and shows you how to create a packaged binary with the [`mojo -package`](/mojo/cli/package.html) command. +package`](/mojo/cli/package) command. ## Mojo modules @@ -93,7 +93,7 @@ from a compiled `.mojopkg`/`.📦` file. It makes no real difference to Mojo which way you import a package. When importing from source files, the directory name works as the package name, whereas when importing from a compiled package, the filename is the package name (which you specify with the [`mojo -package`](/mojo/cli/package.html) command—it can differ from the directory +package`](/mojo/cli/package) command—it can differ from the directory name). For example, consider a project with these files: @@ -200,7 +200,7 @@ from mypackage import MyPair This feature explains why some members in the Mojo standard library can be imported from their package name, while others required the `.` notation. For example, the -[`functional`](/mojo/stdlib/algorithm/functional.html) module resides in the +[`functional`](/mojo/stdlib/algorithm/functional/) module resides in the `algorithm` package, so you can import members of that module (such as the `map()` function) like this: @@ -227,6 +227,6 @@ from algorithm import map Which modules in the standard library are imported to the package scope varies, and is subject to change. Refer to the [documentation for each -module](/mojo/lib.html) to see how you can import its members. +module](/mojo/lib) to see how you can import its members. ::: diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 2b38241046..ef0b28f893 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -56,7 +56,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The [`@parameter`](/mojo/manual/decorators/parameter.html) directive shown here \n", + "The [`@parameter`](/mojo/manual/decorators/parameter) directive shown here \n", "causes the `for` loop to be evaluated at compile time. The directive only works\n", "if the loop limits are compile-time constants. Since `count` is a parameter,\n", "`range(count)` can be calculated at compile time.\n", @@ -134,7 +134,7 @@ "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n", "but not generic. A generic function or struct is parameterized on _type_. For\n", "example, we could rewrite `repeat[]()` to take any type of argument that\n", - "conforms to the [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) trait: " + "conforms to the [`Stringable`](/mojo/stdlib/builtin/str#stringable) trait: " ] }, { @@ -258,7 +258,7 @@ "This struct has a single parameter, `ElementType`, which is a placeholder for\n", "the data type you want to store in the array, sometimes called a _type\n", "parameter_. `ElementType` is typed as\n", - "[`CollectionElement`](/mojo/stdlib/builtin/type_aliases.html), which is a\n", + "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement), which is a\n", "[trait](/mojo/manual/traits) representing any type that can be copied and moved.\n", "\n", "As with parameterized functions, you need to pass in parameter values when you\n", @@ -1083,7 +1083,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter.html) decorator to create a parametric if condition, which is an `if` statement that\n", + "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that\n", "runs at compile-time. It requires that its condition be a valid parameter\n", "expression, and ensures that only the live branch of the `if` statement is\n", "compiled into the program. (This is similar to use of the `@parameter` decorator\n", @@ -1740,9 +1740,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Mojo Source", + "display_name": "Mojo", "language": "mojo", - "name": "mojo-source-kernel" + "name": "mojo-jupyter-kernel" }, "language_info": { "codemirror_mode": { diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 94d6ab5f2a..6e414cc178 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -101,7 +101,7 @@ " the function signature. You'll also see this when calling Python functions\n", " that may raise exceptions. (Raising exceptions is much more common in Python\n", " code than in the Mojo standard library, which \n", - " [limits their use for performance reasons](/mojo/roadmap.html#the-standard-library-has-limited-exceptions-use).)\n", + " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", "\n", ":::note\n", "\n", diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index 2067504437..ba46521ccd 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -34,8 +34,8 @@ "example, all the data types in Mojo's standard library (such as `Int`,\n", "`Bool`, `String`, and `Tuple`) are defined as structs.\n", "\n", - "If you understand how [functions](/mojo/manual/functions.html) and\n", - "[variables](/mojo/manual/variables.html) work in Mojo, you probably\n", + "If you understand how [functions](/mojo/manual/functions) and\n", + "[variables](/mojo/manual/variables) work in Mojo, you probably\n", "noticed that Mojo is designed to provide dynamic programming features in a\n", "`def` function while enforcing stronger code safety in `fn` functions. When it\n", "comes to structs, Mojo leans toward the safe side: You can still choose whether\n", @@ -92,7 +92,7 @@ "source": [ "Notice that the first argument in the `__init__()` method is `inout self`. For\n", "now, ignore `inout` (it's an [argument\n", - "convention](/mojo/manual/values/ownership.html#argument-conventions) that\n", + "convention](/mojo/manual/values/ownership#argument-conventions) that\n", "declares `self` as a mutable reference); all you need to know right now is that\n", "`self` must be the first argument. It references the current struct instance\n", "(it allows code in the method to refer to \"itself\"). *When you call the\n", @@ -283,7 +283,7 @@ "while being safe and easy to use.\n", "\n", "- Mojo structs do not support inheritance (\"sub-classing\"), but a struct can\n", - " implement [traits](/mojo/manual/traits.html).\n", + " implement [traits](/mojo/manual/traits).\n", "\n", "- Python classes support class attributes—values that are shared by all\n", " instances of the class, equivalent to class variables or static data members\n", @@ -337,7 +337,7 @@ "\n", "Mojo supports a long list of special methods; far too many to discuss here, but\n", "they generally match all of [Python's special\n", - "methods](https://docs.python.org/3/reference/datamodel.html#special-method-names)\n", + "methods](https://docs.python.org/3/reference/datamodel#special-method-names)\n", "and they usually accomplish one of two types of tasks:\n", "\n", "- Operator overloading: A lot of special methods are designed to overload\n", @@ -367,7 +367,7 @@ "source": [ "### `@value` decorator\n", "\n", - "When you add the [`@value` decorator](/mojo/manual/decorators/value.html) to a\n", + "When you add the [`@value` decorator](/mojo/manual/decorators/value) to a\n", "struct, Mojo will synthesize the essential lifecycle methods so your object\n", "provides full value semantics. Specifically, it generates the `__init__()`,\n", "`__copyinit__()`, and `__moveinit__()` methods, which allow you to construct,\n", @@ -460,7 +460,7 @@ "`__init__()`, this code also introduces `owned`, which is another argument\n", "convention that ensures the argument has unique ownership of the value.\n", "For more detail, see the section about [value\n", - "ownership](/mojo/manual/values/ownership.html).\n" + "ownership](/mojo/manual/values/ownership).\n" ] } ], diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 46e44fd8c1..962de79a2e 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -68,7 +68,7 @@ "you're passing it, only the fact that they implement the `quack()` method.\n", "\n", "In a statically-typed environment, this approach doesn't work:\n", - "[`fn` functions](/mojo/manual/functions.html#fn-functions) require you to\n", + "[`fn` functions](/mojo/manual/functions#fn-functions) require you to\n", "specify the type of each argument. If you wanted to write this example in Mojo \n", "_without_ traits, you'd need to write a function overload for each input type.\n", "All of the examples from here on are in Mojo, so we'll just call the function\n", @@ -208,7 +208,7 @@ "metadata": {}, "source": [ "This syntax may look a little unfamiliar if you haven't dealt with Mojo\n", - "[parameters](/mojo/manual/parameters/index.html) before. What this signature\n", + "[parameters](/mojo/manual/parameters/) before. What this signature\n", "means is that `maybe_a_duck` is an argument of type `T`, where `T` is a type\n", "that must conform to the `Quackable` trait. TODO: This syntax is a little \n", "verbose, and we hope to make it more ergonomic in a future release.\n", @@ -378,7 +378,7 @@ "`MassProducible` type has a default (no-argument) constructor and can be moved.\n", "It uses the built-in [`Movable`](/mojo/stdlib/builtin/value/Movable) trait,\n", "which requires the type to have a [move \n", - "constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", + "constructor](/mojo/manual/lifecycle/life#move-constructor).\n", "\n", "The `factory[]()` function returns a newly-constructed instance of a \n", "`MassProducible` type." @@ -415,7 +415,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable.html#register_passabletrivial) \n", + "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable#register_passabletrivial) \n", "types have restrictions on their lifecycle methods: they can't define copy or\n", "move constructors, because they don't require any custom logic.\n", "\n", @@ -441,11 +441,11 @@ "by a number of standard library types, and you can also implement these on your\n", "own types:\n", "\n", - " - [`Absable`](/mojo/stdlib/builtin/anytype/Absable)\n", + " - [`Absable`](/mojo/stdlib/builtin/math/Absable)\n", " - [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType)\n", " - [`Boolable`](/mojo/stdlib/builtin/bool/Boolable)\n", " - [`BoolableCollectionElement`](/mojo/stdlib/builtin/value/BoolableCollectionElement)\n", - " - [`BoolableKeyElement`](/mojo/stdlib/collections/dict/BoolableKeyElement)\n", + " - [`BoolableKeyElement`](/mojo/stdlib/builtin/value/BoolableKeyElement)\n", " - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement)\n", " - [`Comparable`](/mojo/stdlib/builtin/comparable/Comparable)\n", " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", @@ -460,7 +460,7 @@ " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", " - [`Powable`](/mojo/stdlib/builtin/math/Powable)\n", " - [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", - " - [`RepresentableCollectionElement`](/mojo/stdlib/collections/dict/RepresentableCollectionElement)\n", + " - [`RepresentableCollectionElement`](/mojo/stdlib/builtin/value/RepresentableCollectionElement)\n", " - [`RepresentableKeyElement`](/mojo/stdlib/collections/dict/RepresentableKeyElement)\n", " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", @@ -571,7 +571,7 @@ "When building a generic container type, one challenge is knowing how to dispose\n", "of the contained items when the container is destroyed. Any type that \n", "dynamically allocates memory needs to supply a \n", - "[destructor](/mojo/manual/lifecycle/death.html#destructor) (`__del__()` method)\n", + "[destructor](/mojo/manual/lifecycle/death#destructor) (`__del__()` method)\n", "that must be called to free the allocated memory. But not all types have a \n", "destructor, and your Mojo code has no way to determine which is which.\n", "\n", @@ -599,7 +599,7 @@ "to be able to identify the types at compile time. For example, if the container\n", "needs to copy a value, the compiler needs to verify that the type can be copied.\n", "\n", - "The [`List`](/mojo/stdlib/collections/list.html) type is an example of a\n", + "The [`List`](/mojo/stdlib/collections/list) type is an example of a\n", "generic container. A single `List` can only hold a single type of data.\n", "For example, you can create a list of integer values like this:" ] @@ -633,8 +633,8 @@ "container. For example, `List` requires elements that can be moved and\n", "copied. To store a struct in a `List`, the struct needs to conform to\n", "the `CollectionElement` trait, which requires a \n", - "[copy constructor](/mojo/manual/lifecycle/life.html#copy-constructor) and a \n", - "[move constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", + "[copy constructor](/mojo/manual/lifecycle/life#copy-constructor) and a \n", + "[move constructor](/mojo/manual/lifecycle/life#move-constructor).\n", "\n", "Building generic containers is an advanced topic. For an introduction, see the\n", "section on \n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 1aac8f6ccb..db01ab1928 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -131,7 +131,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -204,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -248,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -265,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -333,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -381,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -404,16 +404,13 @@ "source": [ "Most standard library types conform to the \n", "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", - "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", - "explicitly convert a value to a string.\n", - "\n", - "When concatenating values to a string using the `+` operator, `Stringable` types\n", - "implicitly convert to `String`:" + "a type that can be converted to a string. Use `str(value)` to\n", + "explicitly convert a value to a string:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -425,7 +422,7 @@ } ], "source": [ - "var s = str(\"Items in list: \") + 5\n", + "var s = str(\"Items in list: \") + str(5)\n", "print(s)" ] }, @@ -473,7 +470,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -487,7 +484,7 @@ "source": [ "# print(\"Strings play nicely with others: \" + True)\n", "# Error: ... right hand side cannot be converted from Bool to StringLiteral\n", - "print(str(\"Strings play nicely with others: \") + True)" + "print(str(\"Strings play nicely with others: \") + str(True))" ] }, { @@ -505,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -560,7 +557,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -579,7 +576,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -623,7 +620,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -685,7 +682,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -714,7 +711,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -760,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -801,7 +798,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -840,7 +837,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -866,7 +863,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -881,7 +878,7 @@ "var opt: Optional[String] = str(\"Testing\")\n", "if opt:\n", " var value_ref = opt.value()\n", - " print(value_ref[])" + " print(value_ref)" ] }, { @@ -895,7 +892,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 103a77717e..7fbd408e8b 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -102,7 +102,7 @@ "\n", "But before we explain the rules and syntax for Mojo's value ownership model,\n", "you first need to understand [value\n", - "semantics](/mojo/manual/values/value-semantics.html)." + "semantics](/mojo/manual/values/value-semantics)." ] } ], diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index d4160853cc..908c7db760 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -29,7 +29,7 @@ "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", "each value at a time, while still allowing you to share references with other\n", "functions. When the lifetime of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death.html).\n", + "value](/mojo/manual/lifecycle/death).\n", "\n", "On this page, we'll explain the rules that govern this ownership model and how\n", "to specify different argument conventions that define how values are shared into\n", @@ -49,7 +49,7 @@ "performance, and safety of the language.\n", "\n", "In Mojo, we want to provide full [value\n", - "semantics](/mojo/manual/values/value-semantics.html) by default, which provides\n", + "semantics](/mojo/manual/values/value-semantics) by default, which provides\n", "consistent and predictable behavior. But as a systems programming language, we\n", "also need to offer full control over memory optimizations, which generally\n", "requires reference semantics. The trick is to introduce reference semantics in\n", @@ -104,7 +104,7 @@ "functions treat `borrowed` arguments somewhat differently:\n", "\n", "\n", - "- In an [`fn` function](/mojo/manual/functions.html#fn-functions), the function\n", + "- In an [`fn` function](/mojo/manual/functions#fn-functions), the function\n", " always receives an immutable reference. If you want a mutable copy, you can\n", " assign it to a local variable:\n", "\n", @@ -112,7 +112,7 @@ " var my_copy = borrowed_arg\n", " ```\n", "\n", - "- In a [`def` function](/mojo/manual/functions.html#def-functions), if the \n", + "- In a [`def` function](/mojo/manual/functions#def-functions), if the \n", " function mutates the value, the function receives a mutable copy of the \n", " argument. Otherwise, it receives an immutable reference. This allows you to\n", " treat arguments as mutable, but avoid the overhead of making extra copies when\n", @@ -166,7 +166,7 @@ "references are and aren't valid (an invalid reference is one whose lifetime has\n", "ended, perhaps because the value ownership was transferred). Importantly, this\n", "logic allows Mojo to immediately [destroy\n", - "values](/mojo/manual/lifecycle/death.html) when their lifetime ends.\n", + "values](/mojo/manual/lifecycle/death) when their lifetime ends.\n", "\n", "-->" ] @@ -340,7 +340,7 @@ ":::note\n", "\n", "You cannot define [default\n", - "values](/mojo/manual/functions.html#optional-arguments) for `inout`\n", + "values](/mojo/manual/functions#optional-arguments) for `inout`\n", "arguments.\n", "\n", ":::" @@ -362,7 +362,7 @@ "Technically, the `owned` keyword does not guarantee that the received value is\n", "_the original value_—it guarantees only that the function\n", "gets unique ownership of a value (enforcing [value\n", - "semantics](/mojo/manual/values/value-semantics.html)). This happens in one of\n", + "semantics](/mojo/manual/values/value-semantics)). This happens in one of\n", "three ways:\n", "\n", "- The caller passes the argument with the `^` transfer operator, which ends the\n", @@ -489,7 +489,7 @@ "making a copy:\n", "\n", "- If a type implements the [move\n", - " constructor](/mojo/manual/lifecycle/life.html#consuming-move-constructor),\n", + " constructor](/mojo/manual/lifecycle/life#consuming-move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", " transferred into a function as an `owned` argument, _and_ the original value's\n", " lifetime ends at the same point (with or without use of the `^` transfer\n", @@ -509,7 +509,7 @@ "## Comparing `def` and `fn` argument conventions\n", "\n", "As mentioned in the section about\n", - "[functions](/mojo/manual/functions.html), `def` and `fn` functions\n", + "[functions](/mojo/manual/functions), `def` and `fn` functions\n", "are interchangeable, as far as a caller is concerned, and they can both\n", "accomplish the same things. It's only the inside that differs, and Mojo's `def`\n", "function is essentially just sugaring for the `fn` function:\n", diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index 5ece6cbdb8..903cabd474 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -27,7 +27,7 @@ "provides tight controls for reference semantics that avoid memory errors.\n", "\n", "The controls over reference semantics are provided by the [value ownership\n", - "model](/mojo/manual/values/ownership.html), but before we get into the syntax\n", + "model](/mojo/manual/values/ownership), but before we get into the syntax\n", "and rules for that, it's important that you understand the principles of value\n", "semantics. Generally, it means that each variable has unique access to a value,\n", "and any code outside the scope of that variable cannot modify its value." @@ -186,7 +186,7 @@ "metadata": {}, "source": [ "The arguments above are mutable because a [`def`\n", - "function](/mojo/manual/functions.html#def-functions) has special treatment for\n", + "function](/mojo/manual/functions#def-functions) has special treatment for\n", "the default\n", "[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions).\n", "\n", diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 7115d3a5f5..7cac974ebd 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -23,12 +23,12 @@ "A variable is a name that holds a value or object. All variables in Mojo are \n", "mutable—their value can be changed. (If you want to define a constant value that\n", "can't change at runtime, see the \n", - "[`alias` keyword](/mojo/manual/parameters/index.html#alias-named-parameter-expressions).)\n", + "[`alias` keyword](/mojo/manual/parameters/#alias-named-parameter-expressions).)\n", "\n", "Mojo has two kinds of variables:\n", "\n", "- Declared variables are created with the `var` keyword, and may include\n", - " [type annotations](#type-annotation).\n", + " [type annotations](#type-annotations).\n", "\n", " ```mojo\n", " var a = 5\n", @@ -80,16 +80,7 @@ "source": [ "In this example, the `temperature` variable is explicitly typed as `Float64`,\n", "but assigned an integer value, so the value is implicitly converted to a \n", - "`Float64`. \n", - "\n", - ":::note\n", - "\n", - "Mojo formerly supported the `let` keyword for declaring immutable variables.\n", - "This has been removed to simplify the language, and for other reasons\n", - "[discussed\n", - "elsewhere](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md).\n", - "\n", - ":::" + "`Float64`. " ] }, { @@ -142,24 +133,12 @@ "source": [ "## Declared variables\n", "\n", - "You can declare a variable with the `var` keyword. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ + "You can declare a variable with the `var` keyword. For example:\n", + "\n", + "```mojo\n", "var name = str(\"Sam\")\n", - "var user_id: Int" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ + "var user_id: Int\n", + "```\n", "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", "declared values are typed—either explicitly with a \n", @@ -373,7 +352,7 @@ "is lossless.\n", "\n", "Implicit conversion follows the logic of [overloaded\n", - "functions](/mojo/manual/functions.html#overloaded-functions). If the destination\n", + "functions](/mojo/manual/functions#overloaded-functions). If the destination\n", "type has a single-argument constructor that takes an argument of the source\n", "type, it can be invoked for implicit conversion. \n", "\n", diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md deleted file mode 100644 index 613652d45c..0000000000 --- a/docs/upgrade-guide.md +++ /dev/null @@ -1,219 +0,0 @@ ---- -title: Mojo🔥 upgrade guide -sidebar_label: Upgrade guide -description: Shows how to upgrade your Mojo programs from one release to the next. ---- - -The following update steps are ordered from most to least common. If you have -compiler warnings or errors, you can go down the list and try recompiling your -program after each item. - -## Upgrading Mojo 0.7 to 24.1 - -### Rename `let` to `var` - -There is now a compiler warning to change the `let` keyword to `var`. But `let` -is a common word that you might mistakenly overwrite a comment or doc string. VS -Code has a nice `find and replace` feature to visualize the changes before -making them. - -First click the search icon and select the icon for Match Whole Word `[ab]`, -then click the arrow to expand the replace bar. Put `let` in the first bar and -`var` in the second, then press enter. You can change one file at a time by -pressing the `Replace All` button next to each file: - -![let to var](./images/let-to-var.png) - -### `vectorize()` and `unroll()` signature change - -The `vectorize()` function now has the ability to add an `unroll_factor` -parameter to unroll the loop. This will often be used as a keyword parameter for -clarity like this: `vectorize[fn, simd_width, unroll_factor=2](size)`. The `fn` -parameter was moved to the front of the signature to accommodate this. - -You can update old code using the find and replace regex syntax to swap the -parameters: - -```re -vectorize\[(.*?), (.*?)\] -vectorize[$2, $1] -``` - -In VS Code back in search, you can press the `.*` icon and paste regex to fix -the signatures: - -![vectorize fix](./images/vectorize-fix.png) - -Similarly, `unroll` has changed to match the vectorize signature, which can be -updated in the same way with the following regex: - -```re -unroll\[(.*?), (.*?)\] -unroll[$2, $1] -``` - -### `vectorize_unroll()` function removed - -You must change `vectorize_unroll[width, unroll_factor, fn](size)` to -`vectorize[fn, width, unroll_factor](size)`. The regex for this is: - -```re -vectorize_unroll\[(.*?), (.*?), (.*?)\] -vectorize[$3, $1, $2] -``` - -### `DynamicVector` constructor `capacity` now keyword-only - -The [`DynamicVector`](/mojo/stdlib/collections/list/List) struct had -a constructor that took a single integer value for the vector's capacity. This -had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. -This was not intended to support implicit conversion, so `capacity` is now a -keyword-only argument to avoid this. To update your code you can use this regex: - -```re -(DynamicVector\[.*Int\]\()(\w.*\)) -$1capacity=$2 -``` - -Which in VS Code looks like this: - -![DynamicVector capacity](./images/dynamic-vector-capacity.png) - -### `NDBuffer` signature change - -The shape of an -[`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) can -now default to being unknown, so the parameter list has been rearranged to -accommodate this: - -For example this: - -```mojo -var buf = NDBuffer[rank, shape, type, address_space]() -``` - -Becomes this: - -```mojo -var buf = NDBuffer[type, rank, shape, address_space]() -``` - -And the `shape` and `address_space` parameters are now optional: - -```mojo -var buf = NDBuffer[type, rank]() -``` - -The regex to fix this is: - -```re -NDBuffer\[(.*?), (.*?), (.*?), -NDBuffer[$3, $1, $2] -``` - -### Dereference `Variant` with `[]` - -Previously, using [`Variant`](/mojo/stdlib/utils/variant/Variant) -was unsafe with heap allocated objects, it now -returns a reference. If you had code that looks like this: - -```mojo -from utils import Variant - -fn foo(variant: Variant[String, Int]): - if variant.isa[String](): - var s = variant.get[String]() - print(s) - -fn main(): - foo(String("foo")) -``` - -You now need to dereference to get access to the pointed to value with `[]`: - -```mojo - var s = variant.get[String]() - print(s[]) -``` - -Note that dereferencing with `[]` is temporary, Mojo will add auto-dereferencing -in the future so this is invisible to the user. - -### String method changes - -Methods on `String` were changed to match Python naming conventions: - -```mojo -var s = String("Foo") -var lower = s.tolower() -var upper = s.toupper() -``` - -Has become: - -```mojo -var s = String("Foo") -var lower = s.lower() -var upper = s.upper() -``` - -The regex to fix this is: - -```re -\.tolower\(\) -lower() -``` - -And: - -```re -\.toupper\(\) -upper() -``` - -### Implicit parameters for arguments - -Previously you could explicitly specify the implicit parameters of an -[automatically parameterized function](/mojo/manual/parameters/#automatic-parameterization-of-functions) -by passing them in the parameter list: - -```mojo -fn foo(x: SIMD): - print(x) - -fn main(): - foo[DType.int32, 1](SIMD[DType.int32, 1](4)) -``` - -This was not intended to be possible and is now a compiler error. These implicit -parameters are always inferred from the function arguments. For example: - -```mojo -fn foo(x: SIMD): - print(x) - -fn main(): - var v =SIMD[DType.int32, 2](4, 2) - foo(v) -``` - -There is no regex to accommodate all the variations of this, so it needs to be -done manually. - -### Using a trait inside another trait - -This no longer compiles: - -```mojo -trait Foo: - fn foo[T: Bar](self): ... - -trait Bar: ... - -struct MyStruct(Foo): - fn foo[T: Bar](self): ... -``` - -You must use concrete types inside trait methods for now, this was an unintended -regression as our codebase wasn't using this feature, it will be fixed in a -future release. From 6a7d32562ac2db7202971e47ff2583dab6a2a2d2 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 24 Jun 2024 13:03:44 -0500 Subject: [PATCH 1020/2019] [External] [stdlib] Make `InlineArray` call its elements' destructor (#42041) [External] [stdlib] Make `InlineArray` call its elements' destructor Fix this issue: * https://github.com/modularml/mojo/issues/2869 A little explanation of what is going on here, this should make the review easier. ### Modifications to `InlineArray` `InlineArray` made use of the `@value` decorator, which generates, in addition to a constructor, the following methods: `__moveinit__`, `__copyinit__` and `__del__`. While it's easy to understand what is generated when working with attributes that are values, it's less clear what is happenning when working with plain MLIR types like we do here. The next step is to manually define those to make sure they do what we want. **We will always assume that the `InlineArray` is completely initialized**. Thus when copying, moving or deleting, we must copy, move and delete each element in the `InlineArray` too. To make the implementation easier, instead of using an `UnsafePointer` and its methods to control the lifecycle of the values (`move_from_pointee` & friends), we fill the array with `UnsafeMaybeUninitialized`. This struct has methods that allows us to control the lifecycle without necessarily work with pointers. ### What do we do with `InlineArray[...](unsafe_uninitialized=True)`? This method is a massive footgun. This is because throughout the methods of `InlineArray`, we always assume that the values are initialized. So doing any moving, copying or deleting with uninitialized memory is UB. This is not much of an issue for very trivial types where moving and copying are just copying bits and the destructor does nothing. This can still cause UB if the user uses some uninitialized memory but it's fine if the caller is careful. For example, in the stdlib, we use `unsafe_uninitialized` to store some `UInt8` here and there and it's fine. As such, the constructor was kept, but an extensive docstring was added to explain in which situation it is okay to use it. ### How `InlineArray` is now used by `InlineList` `InlineList` wants to carefully manage the lifecycle of each element in the `InlineArray` while still making the `InlineArray` believe that it owns the lifecycle of its elements. In those kind of situation, `UnsafeMaybeUninitialized` is the right struct to use. So we use an `InlineArray[UnsafeMaybeUninitialized[ElementType]]` inside `InlineList`. I know it can be confusing since `InlineArray` itself also uses `UnsafeMaybeUninitialized` but this shouldn't be visible from outside `InlineArray` so it should be fine. Basically it disables all lifecycle methods, and `__del__` is a no-op. This allows `InlineList` to manually control the lifecycle of each elements which has an index below the size (above the size of the `InlineList`, it's truly uninitialized memory). Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2965 MODULAR_ORIG_COMMIT_REV_ID: 5d98bc8f2a9bfc35ee0a004b8ee6a36f4a475452 --- stdlib/src/collections/inline_list.mojo | 15 ++--- stdlib/src/utils/static_tuple.mojo | 84 +++++++++++++++++++------ stdlib/test/utils/test_tuple.mojo | 25 ++++++++ 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index f3dbb9ca0d..ceea8ec2b9 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -20,6 +20,7 @@ from collections import InlineList """ from sys.intrinsics import _type_is_eq +from memory.unsafe import UnsafeMaybeUninitialized from utils import InlineArray @@ -89,7 +90,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """ # Fields - var _array: InlineArray[ElementType, capacity] + var _array: InlineArray[UnsafeMaybeUninitialized[ElementType], capacity] var _size: Int # ===-------------------------------------------------------------------===# @@ -99,9 +100,9 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __init__(inout self): """This constructor creates an empty InlineList.""" - self._array = InlineArray[ElementType, capacity]( - unsafe_uninitialized=True - ) + self._array = InlineArray[ + UnsafeMaybeUninitialized[ElementType], capacity + ](unsafe_uninitialized=True) self._size = 0 # TODO: Avoid copying elements in once owned varargs @@ -121,7 +122,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __del__(owned self): """Destroy all the elements in the list and free the memory.""" for i in range(self._size): - UnsafePointer.address_of(self._array[i]).destroy_pointee() + self._array[i].assume_initialized_destroy() # ===-------------------------------------------------------------------===# # Operator dunders @@ -146,7 +147,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): if idx < 0: idx += len(self) - return self._array[idx] + return self._array[idx].assume_initialized() # ===-------------------------------------------------------------------===# # Trait implementations @@ -245,5 +246,5 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): value: The value to append. """ debug_assert(self._size < capacity, "List is full.") - self._array[self._size] = value^ + self._array[self._size].write(value^) self._size += 1 diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 1af6ee3bad..3e2cf5684f 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -24,6 +24,7 @@ from sys.intrinsics import _type_is_eq from memory import Pointer from utils import unroll +from memory.unsafe import UnsafeMaybeUninitialized # ===----------------------------------------------------------------------===# # Utilities @@ -245,7 +246,6 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): # ===----------------------------------------------------------------------===# -@value struct InlineArray[ ElementType: CollectionElementNew, size: Int, @@ -259,7 +259,11 @@ struct InlineArray[ # Fields alias type = __mlir_type[ - `!pop.array<`, size.value, `, `, Self.ElementType, `>` + `!pop.array<`, + size.value, + `, `, + UnsafeMaybeUninitialized[Self.ElementType], + `>`, ] var _array: Self.type """The underlying storage for the array.""" @@ -277,8 +281,8 @@ struct InlineArray[ False, ( "Initialize with either a variadic list of arguments, a default" - " fill element or pass the keyword argument" - " 'unsafe_uninitialized'." + " fill element or use the type" + " 'UnsafeMaybeUninitialized'." ), ]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() @@ -287,18 +291,28 @@ struct InlineArray[ fn __init__(inout self, *, unsafe_uninitialized: Bool): """Create an InlineArray with uninitialized memory. - Note that this is highly unsafe and should be used with caution. + Note that this is highly unsafe and should be used with extreme caution. + It's very difficult to get it right. We recommend to use the `InlineList` instead if all the objects - are not available when creating the array. + are not available when creating the array. That works well for the + general case. + + If you do not want to pay the small performance overhead of `InlineList` and + still want raw uninitalized memory, then make sure to understand the + following: + + Never use this with types that do not have a trivial destructor. + If you want to use an uninitialized array with a type with + a non-trivial destructor, + then use `InlineArray[UnsafeMaybeUninitialized[MyType]]`, but you'll have + to manually call the destructors yourself. If despite those workarounds, one still needs an uninitialized array, it is possible with: - ```mojo var uninitialized_array = InlineArray[Int, 10](unsafe_uninitialized=True) ``` - Args: unsafe_uninitialized: A boolean to indicate if the array should be initialized. Always set to `True` (it's not actually used inside the constructor). @@ -318,8 +332,9 @@ struct InlineArray[ @parameter for i in range(size): - var ptr = UnsafePointer.address_of(self._get_reference_unsafe(i)[]) - ptr.initialize_pointee_explicit_copy(fill) + self._get_maybe_uninitialized(i)[].write( + Self.ElementType(other=fill) + ) @always_inline fn __init__(inout self, owned *elems: Self.ElementType): @@ -350,9 +365,8 @@ struct InlineArray[ # Move each element into the array storage. @parameter for i in range(size): - var eltref = self._get_reference_unsafe(i) - UnsafePointer.address_of(storage[i]).move_pointee_into( - UnsafePointer[Self.ElementType].address_of(eltref[]) + self._get_maybe_uninitialized(i)[].move_from( + UnsafePointer.address_of(storage[i]) ) # Mark the elements as already destroyed. @@ -365,12 +379,37 @@ struct InlineArray[ other: The value to copy. """ - self = Self(unsafe_uninitialized=True) + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() for idx in range(size): - var ptr = self.unsafe_ptr() + idx + self._get_maybe_uninitialized(idx)[].copy_from(other[idx]) + + fn __del__(owned self): + """Runs the destructor for all elements of the array.""" + for i in range(len(self)): + self._get_maybe_uninitialized(i)[].assume_initialized_destroy() - ptr.initialize_pointee_explicit_copy(other[idx]) + fn __copyinit__(inout self, other: Self): + """Copy construct the array. + + Args: + other: The value to copy from. + """ + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + for idx in range(size): + self._get_maybe_uninitialized(idx)[].copy_from(other[idx]) + + fn __moveinit__(inout self, owned other: Self): + """Move construct the array. + + Args: + other: The value to move from. + """ + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + for idx in range(size): + self._get_maybe_uninitialized(idx)[].move_from( + other._get_maybe_uninitialized(idx)[] + ) # ===------------------------------------------------------------------===# # Operator dunders @@ -452,9 +491,16 @@ struct InlineArray[ Returns: A reference to the element at the given index. """ - var idx_as_int = index(idx) + return self._get_maybe_uninitialized(idx)[].assume_initialized() + + @always_inline + fn _get_maybe_uninitialized( + ref [_]self: Self, idx: Int + ) -> Reference[ + UnsafeMaybeUninitialized[Self.ElementType], __lifetime_of(self) + ]: debug_assert( - 0 <= idx_as_int < size, + 0 <= idx < size, ( "Index must be within bounds when using" " `InlineArray.unsafe_get()`." @@ -462,7 +508,7 @@ struct InlineArray[ ) var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(self._array).address, - idx_as_int.value, + idx.value, ) return UnsafePointer(ptr)[] diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index b35a6ae4f2..592648a44c 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -15,6 +15,7 @@ from testing import assert_equal, assert_false, assert_true from utils import InlineArray, StaticIntTuple, StaticTuple +from test_utils import ValueDestructorRecorder def test_static_tuple(): @@ -213,6 +214,29 @@ def test_array_contains(): assert_true(not str("greetings") in arr) +def test_inline_array_runs_destructors(): + """Ensure we delete the right number of elements.""" + var destructor_counter = List[Int]() + var pointer_to_destructor_counter = UnsafePointer.address_of( + destructor_counter + ) + alias capacity = 32 + var inline_list = InlineArray[ValueDestructorRecorder, 4]( + ValueDestructorRecorder(0, pointer_to_destructor_counter), + ValueDestructorRecorder(10, pointer_to_destructor_counter), + ValueDestructorRecorder(20, pointer_to_destructor_counter), + ValueDestructorRecorder(30, pointer_to_destructor_counter), + ) + _ = inline_list + # This is the last use of the inline list, so it should be destroyed here, + # along with each element. + assert_equal(len(destructor_counter), 4) + assert_equal(destructor_counter[0], 0) + assert_equal(destructor_counter[1], 10) + assert_equal(destructor_counter[2], 20) + assert_equal(destructor_counter[3], 30) + + def main(): test_static_tuple() test_static_int_tuple() @@ -222,3 +246,4 @@ def main(): test_array_str() test_array_int_pointer() test_array_contains() + test_inline_array_runs_destructors() From e05772902a952002ed4450a75ad2574b3dc2d1eb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 24 Jun 2024 13:20:58 -0500 Subject: [PATCH 1021/2019] [stdlib] Use `StaticString` to replace `StringLiteral` params in format_int.mojo This is part of the work towards making StringLiteral nonmaterializable. MODULAR_ORIG_COMMIT_REV_ID: 71e8420b17286dedfe109be280e4b3b5a8bc8666 --- stdlib/src/builtin/format_int.mojo | 114 +++++++++++------------ stdlib/src/builtin/string.mojo | 5 +- stdlib/test/builtin/test_format_int.mojo | 24 ++--- 3 files changed, 73 insertions(+), 70 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 1f622c0d0c..b7b9aeb498 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -16,9 +16,8 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import Optional - -from utils import InlineArray +from collections import List, Optional +from utils import InlineArray, StringSlice, StaticString alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @@ -29,7 +28,7 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @always_inline -fn bin[prefix: StringLiteral = "0b"](num: Scalar, /) -> String: +fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: """Return the binary string representation an integral value. ```mojo @@ -41,51 +40,47 @@ fn bin[prefix: StringLiteral = "0b"](num: Scalar, /) -> String: '-0b1111011' ``` - Parameters: - prefix: The prefix of the formatted int. - Args: num: An integral scalar value. + prefix: The prefix of the formatted int. Returns: The binary string representation of num. """ - return _try_format_int[prefix](num, 2) + return _try_format_int(num, 2, prefix=prefix) # Need this until we have constraints to stop the compiler from matching this # directly to bin[type: DType](num: Scalar[type]). @always_inline("nodebug") -fn bin[prefix: StringLiteral = "0b"](b: Scalar[DType.bool], /) -> String: +fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of a scalar bool. - Parameters: - prefix: The prefix of the formatted int. - Args: b: A scalar bool value. + prefix: The prefix of the formatted int. Returns: The binary string representation of b. """ - return bin[prefix](b.cast[DType.int8]()) + return bin(b.cast[DType.int8](), prefix=prefix) @always_inline("nodebug") -fn bin[T: Indexer, //, prefix: StringLiteral = "0b"](num: T, /) -> String: +fn bin[T: Indexer, //](num: T, /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of an indexer type. Parameters: T: The Indexer type. - prefix: The prefix of the formatted int. Args: num: An indexer value. + prefix: The prefix of the formatted int. Returns: The binary string representation of num. """ - return bin[prefix](Scalar[DType.index](index(num))) + return bin(Scalar[DType.index](index(num)), prefix=prefix) # ===----------------------------------------------------------------------===# @@ -93,8 +88,7 @@ fn bin[T: Indexer, //, prefix: StringLiteral = "0b"](num: T, /) -> String: # ===----------------------------------------------------------------------===# -@always_inline -fn hex[prefix: StringLiteral = "0x"](value: Scalar, /) -> String: +fn hex(value: Scalar, /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. @@ -102,20 +96,18 @@ fn hex[prefix: StringLiteral = "0x"](value: Scalar, /) -> String: The returned string will be prefixed with "0x" to indicate that the subsequent digits are hex. - Parameters: - prefix: The prefix of the formatted int. - Args: value: The integer value to format. + prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given integer. """ - return _try_format_int[prefix](value, 16) + return _try_format_int(value, 16, prefix=prefix) @always_inline -fn hex[T: Indexer, //, prefix: StringLiteral = "0x"](value: T, /) -> String: +fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. @@ -125,19 +117,19 @@ fn hex[T: Indexer, //, prefix: StringLiteral = "0x"](value: T, /) -> String: Parameters: T: The indexer type to represent in hexadecimal. - prefix: The prefix of the formatted int. Args: value: The integer value to format. + prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given integer. """ - return hex[prefix](Scalar[DType.index](index(value))) + return hex(Scalar[DType.index](index(value)), prefix=prefix) @always_inline -fn hex[prefix: StringLiteral = "0x"](value: Scalar[DType.bool], /) -> String: +fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given scalar bool. The hexadecimal representation is a base-16 encoding of the bool. @@ -145,16 +137,14 @@ fn hex[prefix: StringLiteral = "0x"](value: Scalar[DType.bool], /) -> String: The returned string will be prefixed with "0x" to indicate that the subsequent digits are hex. - Parameters: - prefix: The prefix of the formatted int. - Args: value: The bool value to format. + prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given bool. """ - return hex[prefix](value.cast[DType.int8]()) + return hex(value.cast[DType.int8](), prefix=prefix) # ===----------------------------------------------------------------------===# @@ -163,7 +153,7 @@ fn hex[prefix: StringLiteral = "0x"](value: Scalar[DType.bool], /) -> String: @always_inline -fn oct[prefix: StringLiteral = "0o"](value: Scalar, /) -> String: +fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. The octal representation is a base-8 encoding of the integer value. @@ -171,20 +161,18 @@ fn oct[prefix: StringLiteral = "0o"](value: Scalar, /) -> String: The returned string will be prefixed with "0o" to indicate that the subsequent digits are octal. - Parameters: - prefix: The prefix of the formatted int. - Args: value: The integer value to format. + prefix: The prefix of the formatted int. Returns: A string containing the octal representation of the given integer. """ - return _try_format_int[prefix](value, 8) + return _try_format_int(value, 8, prefix=prefix) @always_inline -fn oct[T: Indexer, //, prefix: StringLiteral = "0o"](value: T, /) -> String: +fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. The octal representation is a base-8 encoding of the integer value. @@ -194,19 +182,19 @@ fn oct[T: Indexer, //, prefix: StringLiteral = "0o"](value: T, /) -> String: Parameters: T: The indexer type to represent in octal. - prefix: The prefix of the formatted int. Args: value: The integer value to format. + prefix: The prefix of the formatted int. Returns: A string containing the octal representation of the given integer. """ - return oct[prefix](Scalar[DType.index](index(value))) + return oct(Scalar[DType.index](index(value)), prefix=prefix) @always_inline -fn oct[prefix: StringLiteral = "0o"](value: Scalar[DType.bool], /) -> String: +fn oct(value: Scalar[DType.bool], /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given scalar bool. The octal representation is a base-8 encoding of the bool. @@ -214,16 +202,14 @@ fn oct[prefix: StringLiteral = "0o"](value: Scalar[DType.bool], /) -> String: The returned string will be prefixed with "0o" to indicate that the subsequent digits are octal. - Parameters: - prefix: The prefix of the formatted int. - Args: value: The bool value to format. + prefix: The prefix of the formatted int. Returns: A string containing the octal representation of the given bool. """ - return oct[prefix](value.cast[DType.int8]()) + return oct(value.cast[DType.int8](), prefix=prefix) # ===----------------------------------------------------------------------===# @@ -231,11 +217,15 @@ fn oct[prefix: StringLiteral = "0o"](value: Scalar[DType.bool], /) -> String: # ===----------------------------------------------------------------------===# -fn _try_format_int[ - prefix: StringLiteral = "" -](value: Scalar, radix: Int = 10) -> String: +fn _try_format_int( + value: Scalar[_], + /, + radix: Int = 10, + *, + prefix: StaticString = "", +) -> String: try: - return _format_int[prefix=prefix](value, radix) + return _format_int(value, radix, prefix=prefix) except e: # This should not be reachable as _format_int only throws if we pass # incompatible radix and custom digit chars, which we aren't doing @@ -246,42 +236,52 @@ fn _try_format_int[ fn _format_int[ - type: DType, prefix: StringLiteral = "" + dtype: DType ]( - value: Scalar[type], + value: Scalar[dtype], radix: Int = 10, - digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, + *, + digit_chars: StaticString = _DEFAULT_DIGIT_CHARS, + prefix: StaticString = "", ) raises -> String: var string = String() var fmt = string._unsafe_to_formatter() - _write_int[prefix](fmt, value, radix, digit_chars) + _write_int(fmt, value, radix, digit_chars=digit_chars, prefix=prefix) return string^ @always_inline fn _write_int[ - type: DType, //, prefix: StringLiteral = "" + type: DType, //, ]( inout fmt: Formatter, value: Scalar[type], + /, radix: Int = 10, - digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, + *, + digit_chars: StaticString = _DEFAULT_DIGIT_CHARS, + prefix: StaticString = "", ) raises: - var err = _try_write_int[prefix](fmt, value, radix, digit_chars) + var err = _try_write_int( + fmt, value, radix, digit_chars=digit_chars, prefix=prefix + ) if err: raise err.value() @always_inline fn _try_write_int[ - type: DType, //, prefix: StringLiteral = "" + type: DType, //, ]( inout fmt: Formatter, value: Scalar[type], + /, radix: Int = 10, - digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, + *, + digit_chars: StaticString = _DEFAULT_DIGIT_CHARS, + prefix: StaticString = "", ) -> Optional[Error]: """Writes a formatted string representation of the given integer using the specified radix. @@ -318,7 +318,7 @@ fn _try_write_int[ # Add the custom number prefix, e.g. "0x" commonly used for hex numbers. # This comes *after* the minus sign, if present. - fmt.write_str[prefix]() + fmt.write_str(prefix) if value == 0: # TODO: Replace with safe digit_chars[:1] syntax. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 81627c6ba7..276f205284 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -170,7 +170,10 @@ fn _repr_ascii(c: UInt8) -> String: return r"\r" else: var uc = c.cast[DType.uint8]() - return hex[r"\x0"](uc) if uc < 16 else hex[r"\x"](uc) + if uc < 16: + return hex(uc, prefix=r"\x0") + else: + return hex(uc, prefix=r"\x") # TODO: This is currently the same as repr, should change with unicode strings diff --git a/stdlib/test/builtin/test_format_int.mojo b/stdlib/test/builtin/test_format_int.mojo index 228be3da42..9f352f940c 100644 --- a/stdlib/test/builtin/test_format_int.mojo +++ b/stdlib/test/builtin/test_format_int.mojo @@ -135,21 +135,21 @@ def test_indexer(): def test_different_prefix(): - assert_equal(bin["binary"](Int8(1)), "binary1") - assert_equal(hex["hexidecimal"](Int8(1)), "hexidecimal1") - assert_equal(oct["octal"](Int8(1)), "octal1") + assert_equal(bin(Int8(1), prefix="binary"), "binary1") + assert_equal(hex(Int8(1), prefix="hexidecimal"), "hexidecimal1") + assert_equal(oct(Int8(1), prefix="octal"), "octal1") - assert_equal(bin["binary"](0), "binary0") - assert_equal(hex["hexidecimal"](0), "hexidecimal0") - assert_equal(oct["octal"](0), "octal0") + assert_equal(bin(0, prefix="binary"), "binary0") + assert_equal(hex(0, prefix="hexidecimal"), "hexidecimal0") + assert_equal(oct(0, prefix="octal"), "octal0") - assert_equal(bin["I'mAnIndexer!"](Ind()), "I'mAnIndexer!1") - assert_equal(hex["I'mAnIndexer!"](Ind()), "I'mAnIndexer!1") - assert_equal(oct["I'mAnIndexer!"](Ind()), "I'mAnIndexer!1") + assert_equal(bin(Ind(), prefix="I'mAnIndexer!"), "I'mAnIndexer!1") + assert_equal(hex(Ind(), prefix="I'mAnIndexer!"), "I'mAnIndexer!1") + assert_equal(oct(Ind(), prefix="I'mAnIndexer!"), "I'mAnIndexer!1") - assert_equal(bin["test"](Scalar[DType.bool](True)), "test1") - assert_equal(hex["test"](Scalar[DType.bool](True)), "test1") - assert_equal(oct["test"](Scalar[DType.bool](True)), "test1") + assert_equal(bin(Scalar[DType.bool](True), prefix="test"), "test1") + assert_equal(hex(Scalar[DType.bool](True), prefix="test"), "test1") + assert_equal(oct(Scalar[DType.bool](True), prefix="test"), "test1") def main(): From b12a52c3163762fbc0a76db2c7c1b6ac1dc8c7e6 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 24 Jun 2024 21:24:21 +0300 Subject: [PATCH 1022/2019] [stdlib] Reland: Add `ImplicitlyBoolable` trait This patch changes the implicit conversion rules for `Bool` by introducing a trait for types that should be implicitly passable as a `Bool`. This behavior will now be restricted to numeric types and a few other struct to avoid problems with unintented conversions. The `assert_{true,false}` testing utilities are update to take `Boolable` values, keeping it easy to test (explicitly) `Boolable` types. Fixes https://github.com/modularml/mojo/issues/2860 MODULAR_ORIG_COMMIT_REV_ID: 7c8c141b051b35f859c269d2e371ad05d625fd39 --- docs/changelog.md | 12 +++- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 8 +-- stdlib/src/builtin/bool.mojo | 67 ++++++++++++++++--- stdlib/src/builtin/debug_assert.mojo | 34 ++++++++++ stdlib/src/builtin/float_literal.mojo | 11 ++- stdlib/src/builtin/int.mojo | 11 ++- stdlib/src/builtin/int_literal.mojo | 13 +++- stdlib/src/builtin/object.mojo | 12 +++- stdlib/src/builtin/simd.mojo | 14 +++- stdlib/src/builtin/sort.mojo | 2 + stdlib/src/builtin/string_literal.mojo | 12 ++-- stdlib/src/memory/unsafe_pointer.mojo | 11 ++- stdlib/src/python/object.mojo | 11 ++- stdlib/src/testing/testing.mojo | 24 ++++--- stdlib/test/builtin/test_bool.mojo | 7 +- stdlib/test/builtin/test_float_literal.mojo | 15 ++--- stdlib/test/builtin/test_int.mojo | 10 ++- stdlib/test/builtin/test_int_literal.mojo | 10 ++- stdlib/test/builtin/test_simd.mojo | 17 +++++ stdlib/test/memory/test_unsafepointer.mojo | 15 ++++- 21 files changed, 263 insertions(+), 55 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9483f7d1a5..8bd3d26370 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -182,8 +182,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) The default store size is the size of the `SIMD` value to be stored. - `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been removed - since a Slice with no end is now represented by an empty `Slice.end` option. + a constructor which accepts optional values. `Slice._has_end()` has also been + removed since a Slice with no end is now represented by an empty `Slice.end` + option. ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) ```mojo @@ -202,6 +203,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `ulp` function in `numerics` have been moved to the `math` module. +- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer + implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced + for types where this behavior is desired. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` @@ -216,6 +221,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD + instead. ### 🛠️ Fixed diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index f0bcf74f6f..22d5611e3f 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -25,7 +25,7 @@ fn bench_stringify_small_integers(inout b: Bencher) raises: fn call_fn(): for i in range(1_000): var a = str(i) - benchmark.keep(a) + benchmark.keep(bool(a)) b.iter[call_fn]() diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index f089a9d31b..ebd089488b 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -62,7 +62,7 @@ fn bench_dict_small_insert(inout b: Bencher) raises: small[key] = random.random_si64(0, small_n).value b.iter[call_fn]() - keep(small) + keep(bool(small)) # ===----------------------------------------------------------------------===# @@ -77,7 +77,7 @@ fn bench_dict_large_insert(inout b: Bencher) raises: large[key] = random.random_si64(0, large_n).value b.iter[call_fn]() - keep(large) + keep(bool(large)) # ===----------------------------------------------------------------------===# @@ -92,7 +92,7 @@ fn bench_dict_small_lookup(inout b: Bencher) raises: _ = small[key] b.iter[call_fn]() - keep(small) + keep(bool(small)) # ===----------------------------------------------------------------------===# @@ -107,7 +107,7 @@ fn bench_dict_large_lookup(inout b: Bencher) raises: _ = large[key] b.iter[call_fn]() - keep(large) + keep(bool(large)) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 63e8261528..e55f41a0ba 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -25,7 +25,8 @@ from utils._visualizers import lldb_formatter_wrapping_type trait Boolable: - """The `Boolable` trait describes a type that can be converted to a bool. + """The `Boolable` trait describes a type that can be explicitly converted to + a `Bool` or evaluated as a boolean expression in `if` or `while` conditions. This trait requires the type to implement the `__bool__()` method. For example: @@ -49,6 +50,45 @@ trait Boolable: ... +# ===----------------------------------------------------------------------=== # +# ImplicitlyBoolable +# ===----------------------------------------------------------------------=== # + + +trait ImplicitlyBoolable(Boolable): + """The `ImplicitlyBoolable` trait describes a type that can be implicitly + converted to a `Bool`. + + Types conforming to this trait can be passed to a function that expects a + `Bool` without explicitly converting to it. Accordingly, most types should + conform to `Boolable` instead, since implicit conversions to `Bool` can have + unintuitive consequences. + + This trait requires the type to implement the `__as_bool__()` method. For + example: + + ```mojo + @value + struct Foo(ImplicitlyBoolable): + var val: Bool + + fn __as_bool__(self) -> Bool: + return self.val + + fn __bool__(self) -> Bool: + return self.__as_bool__() + ``` + """ + + fn __as_bool__(self) -> Bool: + """Get the boolean representation of the value. + + Returns: + The boolean representation of the value. + """ + ... + + # ===----------------------------------------------------------------------=== # # Bool # ===----------------------------------------------------------------------=== # @@ -58,13 +98,13 @@ trait Boolable: @value @register_passable("trivial") struct Bool( - Stringable, + CollectionElementNew, ComparableCollectionElement, - Boolable, - Intable, + ImplicitlyBoolable, Indexer, - CollectionElementNew, + Intable, Representable, + Stringable, ): """The primitive Bool scalar value used in Mojo.""" @@ -101,11 +141,11 @@ struct Bool( ) @always_inline("nodebug") - fn __init__[T: Boolable](inout self, value: T): - """Implicitly convert a Boolable value to a Bool. + fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): + """Convert an ImplicitlyBoolable value to a Bool. Parameters: - T: The Boolable type. + T: The ImplicitlyBoolable type. Args: value: The boolable value. @@ -121,6 +161,15 @@ struct Bool( """ return self + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert to Bool. + + Returns: + This value. + """ + return self.__bool__() + @always_inline("nodebug") fn __mlir_i1__(self) -> __mlir_type.i1: """Convert this Bool to __mlir_type.i1. @@ -432,7 +481,7 @@ fn bool(value: None) -> Bool: @always_inline -fn bool[T: Boolable](value: T) -> Bool: +fn bool[T: Boolable, //](value: T) -> Bool: """Get the bool representation of the object. Parameters: diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index e8be68feed..3b4bf214e2 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -23,6 +23,40 @@ from sys._build import is_kernels_debug_build from builtin._location import __call_location, _SourceLocation +@always_inline +fn debug_assert[CondType: Boolable, //](cond: CondType, message: String): + """Asserts that the condition is true. + + The `debug_assert` is similar to `assert` in C++. It is a no-op in release + builds unless MOJO_ENABLE_ASSERTIONS is defined. + + Right now, users of the mojo-sdk must explicitly specify + `-D MOJO_ENABLE_ASSERTIONS` to enable assertions. It is not sufficient to + compile programs with `-debug-level full` for enabling assertions in the + library. + + Parameters: + CondType: The type of condition. + + Args: + cond: The bool value to assert. + message: The message to print on failure. + """ + + # Print an error and fail. + alias err = is_kernels_debug_build() or is_defined[ + "MOJO_ENABLE_ASSERTIONS" + ]() + + # Print a warning, but do not fail (useful for testing assert behavior). + alias warn = is_defined["ASSERT_WARNING"]() + + @parameter + if err or warn: + if not cond: + _debug_assert_msg[err](message, __call_location()) + + @always_inline fn debug_assert[ *stringable: Stringable diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 603e0a976a..c4b356cad8 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -27,11 +27,11 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct FloatLiteral( Absable, - Boolable, Ceilable, CeilDivable, Comparable, Floorable, + ImplicitlyBoolable, Intable, Roundable, Stringable, @@ -161,6 +161,15 @@ struct FloatLiteral( """ return self != 0.0 + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """A FloatLiteral value is true if it is non-zero. + + Returns: + True if non-zero. + """ + return self.__bool__() + @always_inline("nodebug") fn __neg__(self) -> FloatLiteral: """Return the negation of the FloatLiteral value. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index d91e538fe0..7abcfe628f 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -243,7 +243,6 @@ fn int(value: String, base: Int = 10) raises -> Int: @register_passable("trivial") struct Int( Absable, - Boolable, Ceilable, CeilDivable, Comparable, @@ -251,6 +250,7 @@ struct Int( Formattable, Indexer, Intable, + ImplicitlyBoolable, KeyElement, Powable, Roundable, @@ -952,6 +952,15 @@ struct Int( """ return self != 0 + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert this Int to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index d6f11097fd..ce9c97beb6 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -20,16 +20,16 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct IntLiteral( Absable, - Boolable, Ceilable, CeilDivable, Comparable, Floorable, + ImplicitlyBoolable, + Indexer, Intable, Roundable, Stringable, Truncable, - Indexer, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -581,6 +581,15 @@ struct IntLiteral( """ return self != Self() + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert this IntLiteral to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 1cc452cb8e..298171cbd5 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -629,7 +629,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===----------------------------------------------------------------------=== # -struct object(IntableRaising, Boolable, Stringable): +struct object(IntableRaising, ImplicitlyBoolable, Stringable): """Represents an object without a concrete type. This is the type of arguments in `def` functions that do not have a type @@ -897,6 +897,16 @@ struct object(IntableRaising, Boolable, Stringable): raise "object type cannot be converted to an integer" + fn __as_bool__(self) -> Bool: + """Performs conversion to bool according to Python semantics. Integers + and floats are true if they are non-zero, and strings and lists are true + if they are non-empty. + + Returns: + Whether the object is considered true. + """ + return self.__bool__() + @always_inline fn __str__(self) -> String: """Performs conversion to string according to Python diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f0cc3e4dff..7d9aa630a8 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -135,7 +135,6 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, - Boolable, Ceilable, CeilDivable, CollectionElement, @@ -143,6 +142,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Floorable, Hashable, Intable, + ImplicitlyBoolable, Powable, Roundable, Sized, @@ -1286,6 +1286,18 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Converts the SIMD scalar into a boolean value. + + Constraints: + The size of the SIMD vector must be 1. + + Returns: + True if the SIMD scalar is non-zero and False otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __int__(self) -> Int: """Casts to the value to an Int. If there is a fractional component, diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 17991379ab..402d68c6a4 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -256,6 +256,7 @@ fn partition[ fn sort(inout buff: Pointer[Int], len: Int): """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Args: @@ -272,6 +273,7 @@ fn sort(inout buff: Pointer[Int], len: Int): fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Parameters: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 97ab53487d..11db3af8e2 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -33,14 +33,14 @@ from .string import _atol @lldb_formatter_wrapping_type @register_passable("trivial") struct StringLiteral( - Sized, - IntableRaising, - Stringable, - Representable, - KeyElement, Boolable, - Formattable, Comparable, + Formattable, + IntableRaising, + KeyElement, + Representable, + Sized, + Stringable, ): """This type represents a string literal. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b69e845833..0985d26732 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -32,7 +32,7 @@ from memory.memory import _free, _malloc struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ]( - Boolable, + ImplicitlyBoolable, CollectionElement, Stringable, Intable, @@ -324,6 +324,15 @@ struct UnsafePointer[ """ return int(self) != 0 + @always_inline + fn __as_bool__(self) -> Bool: + """Return true if the pointer is non-null. + + Returns: + Whether the pointer is null. + """ + return self.__bool__() + @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index ea8832a238..9186563b4b 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,8 +101,8 @@ struct _PyIter(Sized): @register_passable struct PythonObject( - Boolable, CollectionElement, + ImplicitlyBoolable, Indexer, Intable, KeyElement, @@ -386,6 +386,15 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyObject_IsTrue(self.py_object) == 1 + @always_inline + fn __as_bool__(self) -> Bool: + """Evaluate the boolean value of the object. + + Returns: + Whether the object evaluates as true. + """ + return self.__bool__() + fn __is__(self, other: PythonObject) -> Bool: """Test if the PythonObject is the `other` PythonObject, the same as `x is y` in Python. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 2d4a8c5c37..483650fe6c 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -76,11 +76,13 @@ fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: @always_inline -fn assert_true( - val: Bool, msg: String = "condition was unexpectedly False" -) raises: - """Asserts that the input value is True. If it is not then an - Error is raised. +fn assert_true[ + T: Boolable, // +](val: T, msg: String = "condition was unexpectedly False") raises: + """Asserts that the input value is True and raises an Error if it's not. + + Parameters: + T: The type of the value argument. Args: val: The value to assert to be True. @@ -94,11 +96,13 @@ fn assert_true( @always_inline -fn assert_false( - val: Bool, msg: String = "condition was unexpectedly True" -) raises: - """Asserts that the input value is False. If it is not then an Error is - raised. +fn assert_false[ + T: Boolable, // +](val: T, msg: String = "condition was unexpectedly True") raises: + """Asserts that the input value is False and raises an Error if it's not. + + Parameters: + T: The type of the value argument. Args: val: The value to assert to be False. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index c97ee2a827..74071d7bcc 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -34,12 +34,15 @@ struct MyTrue: fn __bool__(self) -> Bool: return True + fn __as_bool__(self) -> Bool: + return self.__bool__() + fn takes_bool(cond: Bool) -> Bool: return cond -def test_convert_from_boolable(): +def test_convert_from_implicitly_boolable(): assert_true(takes_bool(MyTrue())) assert_true(bool(MyTrue())) @@ -145,7 +148,7 @@ def test_comparisons(): def main(): test_bool_cast_to_int() test_bool_none() - test_convert_from_boolable() + test_convert_from_implicitly_boolable() test_bool_to_string() test_bool_representation() test_bitwise() diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 3991a0a4ec..f6457eacc3 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -157,15 +157,12 @@ def test_int_conversion(): assert_equal(int(4.0), 4) -def test_boolean_comparable(): - var f1 = 0.0 - assert_false(f1) +def test_bool(): + assert_false(FloatLiteral.__bool__(0.0)) + assert_false(FloatLiteral.__as_bool__(0.0)) - var f2 = 2.0 - assert_true(f2) - - var f3 = 1.0 - assert_true(f3) + assert_true(FloatLiteral.__bool__(2.0)) + assert_true(FloatLiteral.__as_bool__(2.0)) def test_equality(): @@ -205,7 +202,7 @@ def main(): test_mod() test_div_mod() test_int_conversion() - test_boolean_comparable() + test_bool() test_equality() test_is_special_value() test_abs() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 586d6e26f4..d173a285c9 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,7 +14,7 @@ from sys.info import bitwidthof -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false def test_properties(): @@ -155,6 +155,13 @@ def test_indexer(): assert_equal(987, Int(987).__index__()) +def test_bool(): + assert_true(Int(5).__bool__()) + assert_false(Int(0).__bool__()) + assert_true(Int(5).__as_bool__()) + assert_false(Int(0).__as_bool__()) + + def main(): test_properties() test_add() @@ -172,3 +179,4 @@ def main(): test_string_conversion() test_int_representation() test_indexer() + test_bool() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index b5a512d17b..6a4eb04edd 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false def test_add(): @@ -108,6 +108,13 @@ def test_divmod(): assert_equal(t[1], -1) +def test_bool(): + assert_true(IntLiteral.__bool__(5)) + assert_false(IntLiteral.__bool__(0)) + assert_true(IntLiteral.__as_bool__(5)) + assert_false(IntLiteral.__as_bool__(0)) + + def main(): test_add() test_sub() @@ -121,3 +128,4 @@ def main(): test_bit_width() test_abs() test_indexer() + test_bool() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index b287eadc53..e496e10697 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -214,6 +214,22 @@ def test_issue_30237(): assert_equal(result1, result2) +def test_bool(): + assert_true(Scalar[DType.bool](True).__bool__()) + assert_false(Scalar[DType.bool](False).__bool__()) + assert_true(Scalar[DType.int32](5).__bool__()) + assert_false(Scalar[DType.int32](0).__bool__()) + assert_true(Scalar[DType.float32](5.0).__bool__()) + assert_false(Scalar[DType.float32](0.0).__bool__()) + + assert_true(Scalar[DType.bool](True).__as_bool__()) + assert_false(Scalar[DType.bool](False).__as_bool__()) + assert_true(Scalar[DType.int32](5).__as_bool__()) + assert_false(Scalar[DType.int32](0).__as_bool__()) + assert_true(Scalar[DType.float32](5.0).__as_bool__()) + assert_false(Scalar[DType.float32](0.0).__as_bool__()) + + def test_truthy(): alias dtypes = ( DType.bool, @@ -1576,6 +1592,7 @@ def main(): test_sub() test_sub_with_overflow() test_trunc() + test_bool() test_truthy() test_modf() test_split() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 4c9fedea21..e0e9e7e7f9 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -14,7 +14,7 @@ from memory import UnsafePointer from test_utils import ExplicitCopyOnly, MoveCounter -from testing import assert_equal, assert_not_equal, assert_true +from testing import assert_equal, assert_not_equal, assert_true, assert_false struct MoveOnlyType(Movable): @@ -216,6 +216,18 @@ def test_indexing(): assert_equal(ptr[3], 3) +def test_bool(): + var nullptr = UnsafePointer[Int]() + var ptr = UnsafePointer[Int].alloc(1) + + assert_true(ptr.__bool__()) + assert_false(nullptr.__bool__()) + assert_true(ptr.__as_bool__()) + assert_false(nullptr.__as_bool__()) + + ptr.free() + + def main(): test_address_of() @@ -233,3 +245,4 @@ def main(): test_unsafepointer_address_space() test_indexing() + test_bool() From 8aa27c0f93adfe5c492dc9747aa12396b9cfb4e7 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 24 Jun 2024 15:02:21 -0500 Subject: [PATCH 1023/2019] [stdlib] feature: Implement `Formattable` for many types This is in preparation to change `print()` to require `Formattable` instead of `Stringable`. * Implement Formattable for: - Bool - Error - StaticIntTuple - StringRef - DType - PythonObject MODULAR_ORIG_COMMIT_REV_ID: 6125383bb7024ce6ec04173e21fd9dfaf25d7997 --- stdlib/src/builtin/bool.mojo | 13 ++++++++- stdlib/src/builtin/dtype.mojo | 50 ++++++++++++++++++++------------- stdlib/src/builtin/error.mojo | 15 ++++++++-- stdlib/src/python/object.mojo | 12 ++++++++ stdlib/src/utils/index.mojo | 18 +++++++++++- stdlib/src/utils/stringref.mojo | 19 ++++++++++++- 6 files changed, 103 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index e55f41a0ba..49b4eda356 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -105,6 +105,7 @@ struct Bool( Intable, Representable, Stringable, + Formattable, ): """The primitive Bool scalar value used in Mojo.""" @@ -198,7 +199,17 @@ struct Bool( Returns: A string representation. """ - return "True" if self else "False" + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this boolean to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + writer.write("True" if self else "False") fn __repr__(self) -> String: """Get the bool as a string. diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 2772e0528e..f6abe343b9 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -27,7 +27,7 @@ alias _mIsFloat = UInt8(1 << 6) @value @register_passable("trivial") -struct DType(Stringable, Representable, KeyElement): +struct DType(Stringable, Formattable, Representable, KeyElement): """Represents DType and provides methods for working with it.""" alias type = __mlir_type.`!kgen.dtype` @@ -90,41 +90,53 @@ struct DType(Stringable, Representable, KeyElement): Returns: The name of the dtype. """ + + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this dtype to the provided formatter. + + Args: + writer: The formatter to write to. + """ + if self == DType.bool: - return "bool" + return writer.write_str["bool"]() if self == DType.int8: - return "int8" + return writer.write_str["int8"]() if self == DType.uint8: - return "uint8" + return writer.write_str["uint8"]() if self == DType.int16: - return "int16" + return writer.write_str["int16"]() if self == DType.uint16: - return "uint16" + return writer.write_str["uint16"]() if self == DType.int32: - return "int32" + return writer.write_str["int32"]() if self == DType.uint32: - return "uint32" + return writer.write_str["uint32"]() if self == DType.int64: - return "int64" + return writer.write_str["int64"]() if self == DType.uint64: - return "uint64" + return writer.write_str["uint64"]() if self == DType.index: - return "index" + return writer.write_str["index"]() if self == DType.bfloat16: - return "bfloat16" + return writer.write_str["bfloat16"]() if self == DType.float16: - return "float16" + return writer.write_str["float16"]() if self == DType.float32: - return "float32" + return writer.write_str["float32"]() if self == DType.tensor_float32: - return "tensor_float32" + return writer.write_str["tensor_float32"]() if self == DType.float64: - return "float64" + return writer.write_str["float64"]() if self == DType.invalid: - return "invalid" + return writer.write_str["invalid"]() if self == DType.address: - return "address" - return "<>" + return writer.write_str["address"]() + + return writer.write_str["<>"]() @always_inline("nodebug") fn __repr__(self) -> String: diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index ac343d3df1..d64bfbeedb 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -26,7 +26,7 @@ from memory.memory import _free @register_passable -struct Error(Stringable, Boolable, Representable): +struct Error(Stringable, Boolable, Representable, Formattable): """This type represents an Error.""" var data: UnsafePointer[UInt8] @@ -141,7 +141,18 @@ struct Error(Stringable, Boolable, Representable): Returns: A String of the error message. """ - return self._message() + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this error to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # TODO: Avoid this unnecessary intermediate String allocation. + writer.write(self._message()) fn __repr__(self) -> String: """Converts the Error to printable representation. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 9186563b4b..fc8cdb1987 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -108,6 +108,7 @@ struct PythonObject( KeyElement, SizedRaising, Stringable, + Formattable, ): """A Python object.""" @@ -1172,3 +1173,14 @@ struct PythonObject( # keep python object alive so the copy can occur _ = python_str return mojo_str + + fn format_to(self, inout writer: Formatter): + """ + Formats this Python object to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # TODO: Avoid this intermediate String allocation, if possible. + writer.write(str(self)) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 58eab55d4f..adfc6ab277 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -167,7 +167,12 @@ fn _bool_tuple_reduce[ @value @register_passable("trivial") -struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): +struct StaticIntTuple[size: Int]( + Sized, + Stringable, + Formattable, + Comparable, +): """A base struct that implements size agnostic index functions. Parameters: @@ -656,6 +661,17 @@ struct StaticIntTuple[size: Int](Sized, Stringable, Comparable): buf.size += 1 # for the null terminator. return buf^ + fn format_to(self, inout writer: Formatter): + """ + Formats this int tuple to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # TODO: Optimize this to avoid the intermediate String allocation. + writer.write(str(self)) + # ===----------------------------------------------------------------------===# # Factory functions for creating index. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index f4df8b8ad6..0eee0017b9 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -41,6 +41,7 @@ struct StringRef( IntableRaising, CollectionElement, Stringable, + Formattable, Hashable, Boolable, Comparable, @@ -401,7 +402,23 @@ struct StringRef( Returns: A new string. """ - return self + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this StringRef to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # SAFETY: + # Safe because our use of this StringSlice does not outlive `self`. + var str_slice = StringSlice[ImmutableStaticLifetime]( + unsafe_from_utf8_strref=self + ) + + writer.write_str(str_slice) # ===-------------------------------------------------------------------===# # Methods From d10ae1127879bbe8be3d1a2b02a81e0aa3ae093b Mon Sep 17 00:00:00 2001 From: Ian Tramble <117689425+itramble@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:07:07 -0700 Subject: [PATCH 1024/2019] Fixes and re-enables disabled test for https://github.com/modularml/mojo/issues/1505. MODULAR_ORIG_COMMIT_REV_ID: fc7e9f4f47c4ea00d80803900180332b70c8d9c2 --- stdlib/test/builtin/test_issue_1505.mojo | 213 +---------------------- 1 file changed, 4 insertions(+), 209 deletions(-) diff --git a/stdlib/test/builtin/test_issue_1505.mojo b/stdlib/test/builtin/test_issue_1505.mojo index 70b6512e22..1f77f3debf 100644 --- a/stdlib/test/builtin/test_issue_1505.mojo +++ b/stdlib/test/builtin/test_issue_1505.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: disabled # RUN: %mojo %s # Test for https://github.com/modularml/mojo/issues/1505 @@ -21,156 +20,19 @@ from testing import assert_equal fn gen_perm() -> StaticIntTuple[64]: var result = StaticIntTuple[64]() + for i in range(64): - result[i] = i + result[i] = 64 - i - 1 return result def main(): alias p = gen_perm() - assert_equal( - p, - StaticIntTuple[64]( - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - ), - ) # generate random data to prevent that everything gets simplified var data1 = SIMD[DType.uint8, 64]() for i in range(64): data1[i] = random_ui64(0, 100).cast[DType.uint8]() - assert_equal( - data1, - SIMD[DType.uint8, 64]( - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - ), - ) var data2 = data1.shuffle[ p[0], @@ -239,72 +101,5 @@ def main(): p[63], ]() - assert_equal( - data2, - SIMD[DType.uint8, 64]( - 0, - 13, - 76, - 46, - 53, - 22, - 4, - 68, - 68, - 94, - 38, - 52, - 83, - 3, - 5, - 53, - 67, - 0, - 38, - 6, - 42, - 69, - 59, - 93, - 85, - 53, - 9, - 66, - 42, - 70, - 91, - 76, - 26, - 4, - 74, - 33, - 63, - 76, - 100, - 36, - 24, - 99, - 72, - 76, - 65, - 7, - 63, - 89, - 27, - 44, - 77, - 48, - 24, - 27, - 36, - 16, - 49, - 90, - 91, - 6, - 91, - 50, - 52, - 32, - ), - ) + for i in range(64): + assert_equal(data1[p[i]], data2[i]) From eb1be4011bd31c3e17032bd5fe259d07141e81b3 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 24 Jun 2024 16:28:50 -0500 Subject: [PATCH 1025/2019] [stdlib] cleanup: Very minor readability cleanup to object.__getitem__ #42176 This avoids an unnecessary `take_pointee()` call when a normal pointer dereference will do. MODULAR_ORIG_COMMIT_REV_ID: 808b17342071305f3aa9b6c44e62b35469a609e1 --- stdlib/src/builtin/object.mojo | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 298171cbd5..c2105c0a8d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1733,14 +1733,16 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): """ if self._value.is_obj(): return object(self._value.get_obj_attr("__getitem__"))(self, i) + if not self._value.is_str() and not self._value.is_list(): raise Error("TypeError: can only index into lists and strings") + var index = Self._convert_index_to_int(i) if self._value.is_str(): + # Construct a new single-character string. var impl = _ImmutableString(UnsafePointer[UInt8].alloc(1), 1) - impl.data.init_pointee_copy( - (self._value.get_as_string().data + index).take_pointee(), - ) + var char = self._value.get_as_string().data[index] + impl.data.init_pointee_move(char^) return _ObjectImpl(impl) return self._value.get_list_element(i._value.get_as_int().value) From 10315e99e3e6b8f45cc575d7bc2767ca00b81da3 Mon Sep 17 00:00:00 2001 From: codingonion Date: Mon, 24 Jun 2024 16:43:12 -0500 Subject: [PATCH 1026/2019] [External] [docs] Fix typo in parameters/index.ipynb (#42179) [External] [docs] Fix typo in parameters/index.ipynb Co-authored-by: codingonion Closes modularml/mojo#3100 MODULAR_ORIG_COMMIT_REV_ID: 587f20d2163d775c16092a7a458a146de0f2fb55 --- docs/manual/parameters/index.ipynb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index ef0b28f893..14df3b8a5e 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -1260,9 +1260,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here, `Bytes` is a type alias for a `SIMD` vector of bytes. The underscore `_`\n", - "in the parameter list indicates that the second parameter, `width`, is unbound.\n", - "You specify the `width` parameter later, when you use `Bytes`.\n", + "Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The\n", + "underscore `_` in the parameter list indicates that the second parameter,\n", + "`V` (the value type), is unbound.\n", + "You specify the `V` parameter later, when you use `StringKeyDict`.\n", "\n", "For example, given the following type:" ] From 64bb569e952cef12def9a52496847bd638b5da63 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 24 Jun 2024 15:57:24 -0700 Subject: [PATCH 1027/2019] [mojo] Fix UB MODULAR_ORIG_COMMIT_REV_ID: 8bfa33c1d0c90e480270546e6e048bf6f80c4ad9 --- stdlib/benchmarks/algorithm/bench_elementwise.mojo | 1 + stdlib/benchmarks/utils/bench_formatter.mojo | 2 ++ stdlib/src/builtin/simd.mojo | 1 + 3 files changed, 4 insertions(+) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 34adbe7f1d..f3d5250a20 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -43,6 +43,7 @@ fn bench_elementwise[n: Int](inout b: Bencher) raises: ) b.iter[call_fn]() + _ = vector fn main() raises: diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index db3d469ab4..88848b105b 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -35,6 +35,7 @@ fn bench_formatter_int[n: Int](inout b: Bencher) raises: var s1 = String() var s1_fmt = Formatter(s1) Int(n).format_to(s1_fmt) + _ = s1^ b.iter[call_fn]() @@ -47,6 +48,7 @@ fn bench_formatter_simd[n: Int](inout b: Bencher) raises: var s1 = String() var s1_fmt = Formatter(s1) SIMD[DType.int32](n).format_to(s1_fmt) + _ = s1^ b.iter[call_fn]() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7d9aa630a8..47139304cb 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3093,6 +3093,7 @@ fn _format_scalar[ ) writer.write_str(str_slice) + _ = buf^ # ===----------------------------------------------------------------------=== # From e3039dc4af22c13c1adf4ccfba90165dbe47905f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 24 Jun 2024 17:35:08 -0700 Subject: [PATCH 1028/2019] [stdlib] Fix transfer warning MODULAR_ORIG_COMMIT_REV_ID: dad991a4427f65a0bd78de15078a21be97ab1013 --- stdlib/src/builtin/object.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index c2105c0a8d..ce348691d1 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1742,7 +1742,7 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): # Construct a new single-character string. var impl = _ImmutableString(UnsafePointer[UInt8].alloc(1), 1) var char = self._value.get_as_string().data[index] - impl.data.init_pointee_move(char^) + impl.data.init_pointee_move(char) return _ObjectImpl(impl) return self._value.get_list_element(i._value.get_as_int().value) From e17bc41f05b4c296b74d67093556e9a9ffd8aa46 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:46:25 -0500 Subject: [PATCH 1029/2019] [External] [stdlib] List __getitem__ returns auto-dereferenced ref (#40663) [External] [stdlib] Add tests for `List.__getitem__` returns auto-dereferenced ref `List.__getitem__()` no longer makes copies when returning a value. I also added a test to show that setting an individual field using sugared `my_list[0].value = 1` no longer produces extra copies. Passing that test is why `__getitem__` receives a `Reference[Self, ...]` argument rather than just `self`. I removed uses of `__get_ref` in `list.mojo` and `test_list.mojo` other than leaving one test to show that `__get_ref` was still functioning correctly until it is removed. I left usage of `List.__get_ref` in other modules untouched. From a few quick efforts to eliminate `__get_ref` it was leading to some lifetime casting decisions that seemed complex and numerous enough they should be handled separately. ORIGINAL_AUTHOR=Michael Kowalski <1331470+mikowals@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2847 Co-authored-by: Michael Kowalski <1331470+mikowals@users.noreply.github.com> Closes modularml/mojo#2847 MODULAR_ORIG_COMMIT_REV_ID: 3a03e1669aad93bd512210b373c05df0e2adb313 --- stdlib/src/collections/list.mojo | 4 +-- stdlib/test/collections/test_list.mojo | 50 +++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 4bcef6a022..29c9272c59 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -64,10 +64,10 @@ struct _ListIter[ @parameter if forward: self.index += 1 - return self.src[].__get_ref(self.index - 1)[] + return self.src[][self.index - 1] else: self.index -= 1 - return self.src[].__get_ref(self.index)[] + return self.src[][self.index] fn __len__(self) -> Int: @parameter diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 66c609cbe3..c8d8f5daa9 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -522,7 +522,8 @@ def test_2d_dynamic_list(): assert_equal(2, list.capacity) -def test_list_explicit_copy(): +# TODO(30737): remove this test along with other __get_ref() uses. +def test_list_explicit_copy_using_get_ref(): var list = List[CopyCounter]() list.append(CopyCounter()) var list_copy = List(list) @@ -539,6 +540,51 @@ def test_list_explicit_copy(): assert_equal(l2[i], l2_copy[i]) +def test_list_explicit_copy(): + var list = List[CopyCounter]() + list.append(CopyCounter()) + var list_copy = List(list) + assert_equal(0, list[0].copy_count) + assert_equal(1, list_copy[0].copy_count) + + var l2 = List[Int]() + for i in range(10): + l2.append(i) + + var l2_copy = List(l2) + assert_equal(len(l2), len(l2_copy)) + for i in range(len(l2)): + assert_equal(l2[i], l2_copy[i]) + + +@value +struct CopyCountedStruct(CollectionElement): + var counter: CopyCounter + var value: String + + fn __init__(inout self, value: String): + self.counter = CopyCounter() + self.value = value + + +def test_no_extra_copies_with_sugared_set_by_field(): + var list = List[List[CopyCountedStruct]](capacity=1) + var child_list = List[CopyCountedStruct](capacity=2) + child_list.append(CopyCountedStruct("Hello")) + child_list.append(CopyCountedStruct("World")) + + # No copies here. Contructing with List[CopyCountedStruct](CopyCountedStruct("Hello")) is a copy. + assert_equal(0, child_list[0].counter.copy_count) + assert_equal(0, child_list[1].counter.copy_count) + list.append(child_list^) + + list[0][1].value = "Mojo" + assert_equal("Mojo", list[0][1].value) + + assert_equal(0, list[0][0].counter.copy_count) + assert_equal(0, list[0][1].counter.copy_count) + + # Ensure correct behavior of __copyinit__ # as reported in GH issue 27875 internally and # https://github.com/modularml/mojo/issues/1493 @@ -802,7 +848,9 @@ def main(): test_list_index() test_list_extend() test_list_extend_non_trivial() + test_list_explicit_copy_using_get_ref() test_list_explicit_copy() + test_no_extra_copies_with_sugared_set_by_field() test_list_copy_constructor() test_2d_dynamic_list() test_list_iter() From cf5b8613c334fd0c8619c708c7db9c50ec4d16f1 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Tue, 25 Jun 2024 17:37:27 -0500 Subject: [PATCH 1030/2019] [External] [stdlib] Add equality methods to Span (#40306) [External] [stdlib] Add equality methods to Span With the increased ability for structs to conditionally conform to traits, this PR adds the ability for Span to check for equality. Co-authored-by: Lukas Hermann Closes modularml/mojo#2698 MODULAR_ORIG_COMMIT_REV_ID: b6fbdbf03102b5db27bd5a9669313a1606d8e054 --- stdlib/src/utils/span.mojo | 60 ++++++++++++++++++++++++++++++-- stdlib/test/utils/test_span.mojo | 28 +++++++++++++-- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 0cfd0f1e48..97237b90ca 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -20,9 +20,9 @@ from utils import Span ``` """ -from sys.intrinsics import _type_is_eq - from . import InlineArray +from collections.list import ComparableCollectionElement +from sys.intrinsics import _type_is_eq @value @@ -241,3 +241,59 @@ struct Span[ debug_assert(len(self) == len(other), "Spans must be of equal length") for i in range(len(self)): self[i] = other[i] + + fn __bool__(self) -> Bool: + """Check if a span is non-empty. + + Returns: + True if a span is non-empty, False otherwise. + """ + return len(self) > 0 + + fn __eq__[ + T2: ComparableCollectionElement + ](ref [_]self: Span[T2, lifetime], ref [_]rhs: Span[T]) -> Bool: + """Verify if span is equal to another span. + + Parameters: + T2: The type of the elements in the span. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + rhs: The span to compare against. + + Returns: + True if the spans are equal in length and contain the same elements, False otherwise. + """ + constrained[_type_is_eq[T, T2](), "T must be equal to T2"]() + # both empty + if not self and not rhs: + return True + if len(self) != len(rhs): + return False + # same pointer and length, so equal + if self.unsafe_ptr().bitcast[T]() == rhs.unsafe_ptr(): + return True + for i in range(len(self)): + if self[i] != rhs.unsafe_ptr().bitcast[T2]()[i]: + return False + return True + + @always_inline + fn __ne__[ + T2: ComparableCollectionElement + ](ref [_]self: Span[T2, lifetime], ref [_]rhs: Span[T]) -> Bool: + """Verify if span is not equal to another span. + + Parameters: + T2: The type of the elements in the span. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + rhs: The span to compare against. + + Returns: + True if the spans are not equal in length or contents, False otherwise. + """ + constrained[_type_is_eq[T, T2](), "T must be equal to T2"]() + return not self == rhs diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 0aed924154..7463d9c68e 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -13,8 +13,7 @@ # RUN: %mojo %s from collections.list import List - -from testing import assert_equal +from testing import assert_equal, assert_true from utils import InlineArray, Span @@ -154,6 +153,29 @@ def test_copy_from(): assert_equal(s[i], s2[i]) +def test_bool(): + var l = InlineArray[String, 7]("a", "b", "c", "d", "e", "f", "g") + var s = Span[String](l) + assert_true(s) + assert_true(not s[0:0]) + + +def test_equality(): + var l = InlineArray[String, 7]("a", "b", "c", "d", "e", "f", "g") + var l2 = List[String]("a", "b", "c", "d", "e", "f", "g") + var sp = Span[String](l) + var sp2 = Span[String](l) + var sp3 = Span(l2) + # same pointer + assert_true(sp == sp2) + # different pointer + assert_true(sp == sp3) + # different length + assert_true(sp != sp3[:-1]) + # empty + assert_true(sp[0:0] == sp3[0:0]) + + def main(): test_span_list_int() test_span_list_str() @@ -161,3 +183,5 @@ def main(): test_span_array_str() test_indexing() test_span_slice() + test_equality() + test_bool() From 460297e3d31d031f07a5cce62bfffd69d2e184e8 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 26 Jun 2024 17:33:38 +0300 Subject: [PATCH 1031/2019] [mojo-stdlib] Revert "[mojo-stdlib] Reland: Add `ImplicitlyBoolable` trait (#41880)" This reverts commit 7c8c141b051b35f859c269d2e371ad05d625fd39 because it causes a perf regresseion. MODULAR_ORIG_COMMIT_REV_ID: 08996ccffc942d189e48b1478e937f6e92ad4a40 --- docs/changelog.md | 12 +--- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 8 +-- stdlib/src/builtin/bool.mojo | 63 +++---------------- stdlib/src/builtin/debug_assert.mojo | 34 ---------- stdlib/src/builtin/float_literal.mojo | 11 +--- stdlib/src/builtin/int.mojo | 11 +--- stdlib/src/builtin/int_literal.mojo | 13 +--- stdlib/src/builtin/object.mojo | 12 +--- stdlib/src/builtin/simd.mojo | 14 +---- stdlib/src/builtin/sort.mojo | 2 - stdlib/src/builtin/string_literal.mojo | 12 ++-- stdlib/src/memory/unsafe_pointer.mojo | 11 +--- stdlib/src/python/object.mojo | 11 +--- stdlib/src/testing/testing.mojo | 24 +++---- stdlib/test/builtin/test_bool.mojo | 7 +-- stdlib/test/builtin/test_float_literal.mojo | 15 +++-- stdlib/test/builtin/test_int.mojo | 10 +-- stdlib/test/builtin/test_int_literal.mojo | 10 +-- stdlib/test/builtin/test_simd.mojo | 17 ----- stdlib/test/memory/test_unsafepointer.mojo | 15 +---- 21 files changed, 53 insertions(+), 261 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 8bd3d26370..9483f7d1a5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -182,9 +182,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) The default store size is the size of the `SIMD` value to be stored. - `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been - removed since a Slice with no end is now represented by an empty `Slice.end` - option. + a constructor which accepts optional values. `Slice._has_end()` has also been removed + since a Slice with no end is now represented by an empty `Slice.end` option. ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) ```mojo @@ -203,10 +202,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `ulp` function in `numerics` have been moved to the `math` module. -- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer - implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced - for types where this behavior is desired. - ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` @@ -221,7 +216,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD - instead. +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. ### 🛠️ Fixed diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index 22d5611e3f..f0bcf74f6f 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -25,7 +25,7 @@ fn bench_stringify_small_integers(inout b: Bencher) raises: fn call_fn(): for i in range(1_000): var a = str(i) - benchmark.keep(bool(a)) + benchmark.keep(a) b.iter[call_fn]() diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index ebd089488b..f089a9d31b 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -62,7 +62,7 @@ fn bench_dict_small_insert(inout b: Bencher) raises: small[key] = random.random_si64(0, small_n).value b.iter[call_fn]() - keep(bool(small)) + keep(small) # ===----------------------------------------------------------------------===# @@ -77,7 +77,7 @@ fn bench_dict_large_insert(inout b: Bencher) raises: large[key] = random.random_si64(0, large_n).value b.iter[call_fn]() - keep(bool(large)) + keep(large) # ===----------------------------------------------------------------------===# @@ -92,7 +92,7 @@ fn bench_dict_small_lookup(inout b: Bencher) raises: _ = small[key] b.iter[call_fn]() - keep(bool(small)) + keep(small) # ===----------------------------------------------------------------------===# @@ -107,7 +107,7 @@ fn bench_dict_large_lookup(inout b: Bencher) raises: _ = large[key] b.iter[call_fn]() - keep(bool(large)) + keep(large) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 49b4eda356..330c822887 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -25,8 +25,7 @@ from utils._visualizers import lldb_formatter_wrapping_type trait Boolable: - """The `Boolable` trait describes a type that can be explicitly converted to - a `Bool` or evaluated as a boolean expression in `if` or `while` conditions. + """The `Boolable` trait describes a type that can be converted to a bool. This trait requires the type to implement the `__bool__()` method. For example: @@ -50,45 +49,6 @@ trait Boolable: ... -# ===----------------------------------------------------------------------=== # -# ImplicitlyBoolable -# ===----------------------------------------------------------------------=== # - - -trait ImplicitlyBoolable(Boolable): - """The `ImplicitlyBoolable` trait describes a type that can be implicitly - converted to a `Bool`. - - Types conforming to this trait can be passed to a function that expects a - `Bool` without explicitly converting to it. Accordingly, most types should - conform to `Boolable` instead, since implicit conversions to `Bool` can have - unintuitive consequences. - - This trait requires the type to implement the `__as_bool__()` method. For - example: - - ```mojo - @value - struct Foo(ImplicitlyBoolable): - var val: Bool - - fn __as_bool__(self) -> Bool: - return self.val - - fn __bool__(self) -> Bool: - return self.__as_bool__() - ``` - """ - - fn __as_bool__(self) -> Bool: - """Get the boolean representation of the value. - - Returns: - The boolean representation of the value. - """ - ... - - # ===----------------------------------------------------------------------=== # # Bool # ===----------------------------------------------------------------------=== # @@ -98,14 +58,14 @@ trait ImplicitlyBoolable(Boolable): @value @register_passable("trivial") struct Bool( + Boolable, CollectionElementNew, ComparableCollectionElement, - ImplicitlyBoolable, Indexer, Intable, + Formattable, Representable, Stringable, - Formattable, ): """The primitive Bool scalar value used in Mojo.""" @@ -142,11 +102,11 @@ struct Bool( ) @always_inline("nodebug") - fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): - """Convert an ImplicitlyBoolable value to a Bool. + fn __init__[T: Boolable](inout self, value: T): + """Implicitly convert a Boolable value to a Bool. Parameters: - T: The ImplicitlyBoolable type. + T: The Boolable type. Args: value: The boolable value. @@ -162,15 +122,6 @@ struct Bool( """ return self - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Convert to Bool. - - Returns: - This value. - """ - return self.__bool__() - @always_inline("nodebug") fn __mlir_i1__(self) -> __mlir_type.i1: """Convert this Bool to __mlir_type.i1. @@ -492,7 +443,7 @@ fn bool(value: None) -> Bool: @always_inline -fn bool[T: Boolable, //](value: T) -> Bool: +fn bool[T: Boolable](value: T) -> Bool: """Get the bool representation of the object. Parameters: diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 3b4bf214e2..e8be68feed 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -23,40 +23,6 @@ from sys._build import is_kernels_debug_build from builtin._location import __call_location, _SourceLocation -@always_inline -fn debug_assert[CondType: Boolable, //](cond: CondType, message: String): - """Asserts that the condition is true. - - The `debug_assert` is similar to `assert` in C++. It is a no-op in release - builds unless MOJO_ENABLE_ASSERTIONS is defined. - - Right now, users of the mojo-sdk must explicitly specify - `-D MOJO_ENABLE_ASSERTIONS` to enable assertions. It is not sufficient to - compile programs with `-debug-level full` for enabling assertions in the - library. - - Parameters: - CondType: The type of condition. - - Args: - cond: The bool value to assert. - message: The message to print on failure. - """ - - # Print an error and fail. - alias err = is_kernels_debug_build() or is_defined[ - "MOJO_ENABLE_ASSERTIONS" - ]() - - # Print a warning, but do not fail (useful for testing assert behavior). - alias warn = is_defined["ASSERT_WARNING"]() - - @parameter - if err or warn: - if not cond: - _debug_assert_msg[err](message, __call_location()) - - @always_inline fn debug_assert[ *stringable: Stringable diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index c4b356cad8..603e0a976a 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -27,11 +27,11 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct FloatLiteral( Absable, + Boolable, Ceilable, CeilDivable, Comparable, Floorable, - ImplicitlyBoolable, Intable, Roundable, Stringable, @@ -161,15 +161,6 @@ struct FloatLiteral( """ return self != 0.0 - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """A FloatLiteral value is true if it is non-zero. - - Returns: - True if non-zero. - """ - return self.__bool__() - @always_inline("nodebug") fn __neg__(self) -> FloatLiteral: """Return the negation of the FloatLiteral value. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 7abcfe628f..d91e538fe0 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -243,6 +243,7 @@ fn int(value: String, base: Int = 10) raises -> Int: @register_passable("trivial") struct Int( Absable, + Boolable, Ceilable, CeilDivable, Comparable, @@ -250,7 +251,6 @@ struct Int( Formattable, Indexer, Intable, - ImplicitlyBoolable, KeyElement, Powable, Roundable, @@ -952,15 +952,6 @@ struct Int( """ return self != 0 - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Convert this Int to Bool. - - Returns: - False Bool value if the value is equal to 0 and True otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index ce9c97beb6..d6f11097fd 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -20,16 +20,16 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct IntLiteral( Absable, + Boolable, Ceilable, CeilDivable, Comparable, Floorable, - ImplicitlyBoolable, - Indexer, Intable, Roundable, Stringable, Truncable, + Indexer, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -581,15 +581,6 @@ struct IntLiteral( """ return self != Self() - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Convert this IntLiteral to Bool. - - Returns: - False Bool value if the value is equal to 0 and True otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index ce348691d1..7f5ef21278 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -629,7 +629,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===----------------------------------------------------------------------=== # -struct object(IntableRaising, ImplicitlyBoolable, Stringable): +struct object(IntableRaising, Boolable, Stringable): """Represents an object without a concrete type. This is the type of arguments in `def` functions that do not have a type @@ -897,16 +897,6 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): raise "object type cannot be converted to an integer" - fn __as_bool__(self) -> Bool: - """Performs conversion to bool according to Python semantics. Integers - and floats are true if they are non-zero, and strings and lists are true - if they are non-empty. - - Returns: - Whether the object is considered true. - """ - return self.__bool__() - @always_inline fn __str__(self) -> String: """Performs conversion to string according to Python diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 47139304cb..07bd38cf47 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -135,6 +135,7 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Absable, + Boolable, Ceilable, CeilDivable, CollectionElement, @@ -142,7 +143,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Floorable, Hashable, Intable, - ImplicitlyBoolable, Powable, Roundable, Sized, @@ -1286,18 +1286,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Converts the SIMD scalar into a boolean value. - - Constraints: - The size of the SIMD vector must be 1. - - Returns: - True if the SIMD scalar is non-zero and False otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __int__(self) -> Int: """Casts to the value to an Int. If there is a fractional component, diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 402d68c6a4..17991379ab 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -256,7 +256,6 @@ fn partition[ fn sort(inout buff: Pointer[Int], len: Int): """Sort the buffer inplace. - The function doesn't return anything, the buffer is updated inplace. Args: @@ -273,7 +272,6 @@ fn sort(inout buff: Pointer[Int], len: Int): fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """Sort the buffer inplace. - The function doesn't return anything, the buffer is updated inplace. Parameters: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 11db3af8e2..97ab53487d 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -33,14 +33,14 @@ from .string import _atol @lldb_formatter_wrapping_type @register_passable("trivial") struct StringLiteral( - Boolable, - Comparable, - Formattable, - IntableRaising, - KeyElement, - Representable, Sized, + IntableRaising, Stringable, + Representable, + KeyElement, + Boolable, + Formattable, + Comparable, ): """This type represents a string literal. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 0985d26732..b69e845833 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -32,7 +32,7 @@ from memory.memory import _free, _malloc struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ]( - ImplicitlyBoolable, + Boolable, CollectionElement, Stringable, Intable, @@ -324,15 +324,6 @@ struct UnsafePointer[ """ return int(self) != 0 - @always_inline - fn __as_bool__(self) -> Bool: - """Return true if the pointer is non-null. - - Returns: - Whether the pointer is null. - """ - return self.__bool__() - @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index fc8cdb1987..52fcaf43f1 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,8 +101,8 @@ struct _PyIter(Sized): @register_passable struct PythonObject( + Boolable, CollectionElement, - ImplicitlyBoolable, Indexer, Intable, KeyElement, @@ -387,15 +387,6 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyObject_IsTrue(self.py_object) == 1 - @always_inline - fn __as_bool__(self) -> Bool: - """Evaluate the boolean value of the object. - - Returns: - Whether the object evaluates as true. - """ - return self.__bool__() - fn __is__(self, other: PythonObject) -> Bool: """Test if the PythonObject is the `other` PythonObject, the same as `x is y` in Python. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 483650fe6c..2d4a8c5c37 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -76,13 +76,11 @@ fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: @always_inline -fn assert_true[ - T: Boolable, // -](val: T, msg: String = "condition was unexpectedly False") raises: - """Asserts that the input value is True and raises an Error if it's not. - - Parameters: - T: The type of the value argument. +fn assert_true( + val: Bool, msg: String = "condition was unexpectedly False" +) raises: + """Asserts that the input value is True. If it is not then an + Error is raised. Args: val: The value to assert to be True. @@ -96,13 +94,11 @@ fn assert_true[ @always_inline -fn assert_false[ - T: Boolable, // -](val: T, msg: String = "condition was unexpectedly True") raises: - """Asserts that the input value is False and raises an Error if it's not. - - Parameters: - T: The type of the value argument. +fn assert_false( + val: Bool, msg: String = "condition was unexpectedly True" +) raises: + """Asserts that the input value is False. If it is not then an Error is + raised. Args: val: The value to assert to be False. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 74071d7bcc..c97ee2a827 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -34,15 +34,12 @@ struct MyTrue: fn __bool__(self) -> Bool: return True - fn __as_bool__(self) -> Bool: - return self.__bool__() - fn takes_bool(cond: Bool) -> Bool: return cond -def test_convert_from_implicitly_boolable(): +def test_convert_from_boolable(): assert_true(takes_bool(MyTrue())) assert_true(bool(MyTrue())) @@ -148,7 +145,7 @@ def test_comparisons(): def main(): test_bool_cast_to_int() test_bool_none() - test_convert_from_implicitly_boolable() + test_convert_from_boolable() test_bool_to_string() test_bool_representation() test_bitwise() diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index f6457eacc3..3991a0a4ec 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -157,12 +157,15 @@ def test_int_conversion(): assert_equal(int(4.0), 4) -def test_bool(): - assert_false(FloatLiteral.__bool__(0.0)) - assert_false(FloatLiteral.__as_bool__(0.0)) +def test_boolean_comparable(): + var f1 = 0.0 + assert_false(f1) - assert_true(FloatLiteral.__bool__(2.0)) - assert_true(FloatLiteral.__as_bool__(2.0)) + var f2 = 2.0 + assert_true(f2) + + var f3 = 1.0 + assert_true(f3) def test_equality(): @@ -202,7 +205,7 @@ def main(): test_mod() test_div_mod() test_int_conversion() - test_bool() + test_boolean_comparable() test_equality() test_is_special_value() test_abs() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index d173a285c9..586d6e26f4 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,7 +14,7 @@ from sys.info import bitwidthof -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal def test_properties(): @@ -155,13 +155,6 @@ def test_indexer(): assert_equal(987, Int(987).__index__()) -def test_bool(): - assert_true(Int(5).__bool__()) - assert_false(Int(0).__bool__()) - assert_true(Int(5).__as_bool__()) - assert_false(Int(0).__as_bool__()) - - def main(): test_properties() test_add() @@ -179,4 +172,3 @@ def main(): test_string_conversion() test_int_representation() test_indexer() - test_bool() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 6a4eb04edd..b5a512d17b 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal def test_add(): @@ -108,13 +108,6 @@ def test_divmod(): assert_equal(t[1], -1) -def test_bool(): - assert_true(IntLiteral.__bool__(5)) - assert_false(IntLiteral.__bool__(0)) - assert_true(IntLiteral.__as_bool__(5)) - assert_false(IntLiteral.__as_bool__(0)) - - def main(): test_add() test_sub() @@ -128,4 +121,3 @@ def main(): test_bit_width() test_abs() test_indexer() - test_bool() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index e496e10697..b287eadc53 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -214,22 +214,6 @@ def test_issue_30237(): assert_equal(result1, result2) -def test_bool(): - assert_true(Scalar[DType.bool](True).__bool__()) - assert_false(Scalar[DType.bool](False).__bool__()) - assert_true(Scalar[DType.int32](5).__bool__()) - assert_false(Scalar[DType.int32](0).__bool__()) - assert_true(Scalar[DType.float32](5.0).__bool__()) - assert_false(Scalar[DType.float32](0.0).__bool__()) - - assert_true(Scalar[DType.bool](True).__as_bool__()) - assert_false(Scalar[DType.bool](False).__as_bool__()) - assert_true(Scalar[DType.int32](5).__as_bool__()) - assert_false(Scalar[DType.int32](0).__as_bool__()) - assert_true(Scalar[DType.float32](5.0).__as_bool__()) - assert_false(Scalar[DType.float32](0.0).__as_bool__()) - - def test_truthy(): alias dtypes = ( DType.bool, @@ -1592,7 +1576,6 @@ def main(): test_sub() test_sub_with_overflow() test_trunc() - test_bool() test_truthy() test_modf() test_split() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index e0e9e7e7f9..4c9fedea21 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -14,7 +14,7 @@ from memory import UnsafePointer from test_utils import ExplicitCopyOnly, MoveCounter -from testing import assert_equal, assert_not_equal, assert_true, assert_false +from testing import assert_equal, assert_not_equal, assert_true struct MoveOnlyType(Movable): @@ -216,18 +216,6 @@ def test_indexing(): assert_equal(ptr[3], 3) -def test_bool(): - var nullptr = UnsafePointer[Int]() - var ptr = UnsafePointer[Int].alloc(1) - - assert_true(ptr.__bool__()) - assert_false(nullptr.__bool__()) - assert_true(ptr.__as_bool__()) - assert_false(nullptr.__as_bool__()) - - ptr.free() - - def main(): test_address_of() @@ -245,4 +233,3 @@ def main(): test_unsafepointer_address_space() test_indexing() - test_bool() From 3e338cf7a9243bbfee72e224f9231dbbc2e8d34e Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 26 Jun 2024 08:39:23 -0700 Subject: [PATCH 1032/2019] [******] Fold aligned_stack_allocation into stack_allocation This folds the aligned_stack_allocation function into the stack_allocation function. It also changes the default alignment so that it's more GPU friendly. MODULAR_ORIG_COMMIT_REV_ID: 8cfadfc3de51966645c6cdbad787f8ed3b9f56d7 --- stdlib/src/memory/memory.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 6e8f352b68..0476da6f04 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -500,7 +500,7 @@ fn stack_allocation[ count: Int, type: DType, /, - alignment: Int = 1, + alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> DTypePointer[type, address_space]: """Allocates data buffer space on the stack given a data type and number of @@ -526,7 +526,7 @@ fn stack_allocation[ count: Int, type: AnyTrivialRegType, /, - alignment: Int = 1, + alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> Pointer[type, address_space]: """Allocates data buffer space on the stack given a data type and number of From 6a0c93cf6a32d36120af7f16d85c8402b1fc1a57 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 26 Jun 2024 19:27:39 +0300 Subject: [PATCH 1033/2019] [stdlib] Add `max.tensor` compatibility module This is the first step toward moving the tensor out of the stdlib and into `max`. MODULAR_ORIG_COMMIT_REV_ID: 4e1126f57db5472bad1d84d4e4f66d736b262fbf --- docs/manual/decorators/staticmethod.ipynb | 2 +- docs/manual/values/ownership.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index f6e798b97a..f0c0def9a8 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -31,7 +31,7 @@ "metadata": {}, "outputs": [], "source": [ - "from tensor import Tensor\n", + "from max.tensor import Tensor\n", "from pathlib import Path\n", "\n", "\n", diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 908c7db760..f899e3e8f2 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -203,7 +203,7 @@ } ], "source": [ - "from tensor import Tensor, TensorShape\n", + "from max.tensor import Tensor, TensorShape\n", "\n", "def print_shape(tensor: Tensor[DType.float32]):\n", " shape = tensor.shape()\n", From a3f3e74d4decc714f7e5e5d4429173394287af95 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 26 Jun 2024 10:28:38 -0700 Subject: [PATCH 1034/2019] [Stdlib] Remove the bin_op from cmpxchg instruction, NFC This is a bogus param that is being passed into the pop.atomic.cmpxchg instruction. MODULAR_ORIG_COMMIT_REV_ID: 8e63b77b0c4bee2ad60da58964ebe90fa3993d84 --- stdlib/src/os/atomic.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index c3d8dddfae..26b2b0b99e 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -209,7 +209,6 @@ struct Atomic[type: DType]: var desired_integral = bitcast[integral_type](desired) var cmpxchg_res = __mlir_op.`pop.atomic.cmpxchg`[ - bin_op = __mlir_attr.`#pop`, failure_ordering = __mlir_attr.`#pop`, success_ordering = __mlir_attr.`#pop`, ]( From 454efb07bfad227df884075bc5059493756145ad Mon Sep 17 00:00:00 2001 From: soraros Date: Wed, 26 Jun 2024 13:51:03 -0500 Subject: [PATCH 1035/2019] [External] [stdlib] Move `tuple.mojo` off `unroll` (#42022) [External] [stdlib] Move `tuple.mojo` off `unroll` Remove the use of the `unroll` function from `tuple.mojo` in favour of `@parameter if`. Co-authored-by: soraros Closes modularml/mojo#3055 MODULAR_ORIG_COMMIT_REV_ID: baea5b29de0ece95a79023028832195891fd3af8 --- stdlib/src/builtin/tuple.mojo | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 4a770cc55e..6c34c2e466 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -71,16 +71,14 @@ struct Tuple[*element_types: Movable](Sized, Movable): __get_mvalue_as_litref(self.storage) ) + # Move each element into the tuple storage. @parameter - fn initialize_elt[idx: Int](): - UnsafePointer.address_of(storage[idx]).move_pointee_into( - UnsafePointer.address_of(self[idx]) + for i in range(Self.__len__()): + UnsafePointer.address_of(storage[i]).move_pointee_into( + UnsafePointer.address_of(self[i]) ) - # Move each element into the tuple storage. - unroll[initialize_elt, Self.__len__()]() - - # Mark the elements as already destroyed. + # Mark the elements as destroyed. storage._is_owned = False fn __del__(owned self): @@ -89,10 +87,8 @@ struct Tuple[*element_types: Movable](Sized, Movable): # Run the destructor on each member, the destructor of !kgen.pack is # trivial and won't do anything. @parameter - fn destroy_elt[idx: Int](): - UnsafePointer.address_of(self[idx]).destroy_pointee() - - unroll[destroy_elt, Self.__len__()]() + for i in range(Self.__len__()): + UnsafePointer.address_of(self[i]).destroy_pointee() @always_inline("nodebug") fn __moveinit__(inout self, owned existing: Self): @@ -107,13 +103,11 @@ struct Tuple[*element_types: Movable](Sized, Movable): ) @parameter - fn initialize_elt[idx: Int](): - UnsafePointer.address_of(existing[idx]).move_pointee_into( - UnsafePointer.address_of(self[idx]) + for i in range(Self.__len__()): + UnsafePointer.address_of(existing[i]).move_pointee_into( + UnsafePointer.address_of(self[i]) ) - unroll[initialize_elt, Self.__len__()]() - @always_inline @staticmethod fn __len__() -> Int: From e1bad4f114632878b47b0a7ab5876565a2f9a8e5 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 26 Jun 2024 13:58:24 -0500 Subject: [PATCH 1036/2019] [stdlib] feature: Change `print()` to require `Formattable` instead of `Stringable` * Combine _print_fmt() and print() * Changelog print() requires `Formattable` now * Add `Formatter.__init__(FileDescriptor)` initializer * Change `abort(message)` to require `Formattable` instead of `Stringable` * Add `file` argument to `_put(StringRef)` Squashed History: * cleanup: Manually inline `_print()` helper into `print()` MODULAR_ORIG_COMMIT_REV_ID: 2a7d8194d5201ca4835de39fc9c7b679333930c0 --- docs/changelog.md | 61 ++++++++++++++- stdlib/src/builtin/io.mojo | 79 +++----------------- stdlib/src/builtin/simd.mojo | 3 +- stdlib/src/builtin/string.mojo | 11 +++ stdlib/src/memory/unsafe_pointer.mojo | 12 +++ stdlib/src/os/os.mojo | 6 +- stdlib/src/pathlib/path.mojo | 14 +++- stdlib/src/utils/_format.mojo | 22 ++++++ stdlib/src/utils/_serialize.mojo | 8 +- stdlib/test/utils/test_format_to_stdout.mojo | 13 ---- 10 files changed, 135 insertions(+), 94 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9483f7d1a5..0ae90c2077 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -66,7 +66,7 @@ what we publish. fn __init__(inout self: MyStruct[0]): pass fn __init__(inout self: MyStruct[1], a: Int): pass fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass - + def test(x: Int): a = MyStruct() # Infers size=0 from 'self' type. b = MyStruct(x) # Infers size=1 from 'self' type. @@ -162,6 +162,65 @@ by [@jayzhan211](https://github.com/jayzhan211)) - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) +- `print()` now requires that its arguments conform to the `Formattable` trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. + + Previously, `print()` required types conform to `Stringable`. This meant that + to execute a call like `print(a, b, c)`, at least three separate String heap + allocations were down, to hold the formatted values of `a`, `b`, and `c` + respectively. The total number of allocations could be much higher if, for + example, `a.__str__()` was implemented to concatenate together the fields of + `a`, like in the following example: + + ```mojo + struct Point(Stringable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + # Performs 3 allocations: 1 each for str(..) of each of the fields, + # and then the final returned `String` allocation. + return "(" + str(self.x) + ", " + str(self.y) + ")" + ``` + + A type like the one above can transition to additionally implementing + `Formattable` with the following changes: + + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` + + In the example above, `String.format_sequence()` is used to construct a + `String` from a type that implements `Formattable`. This pattern of + implementing a types `Stringable` implementation in terms of its `Formattable` + implementation minimizes boilerplate and duplicated code, while retaining + backwards compatibility with the requirements of the commonly used `str(..)` + function. + + + + > [!WARNING] + > The error shown when passing a type that does not implement `Formattable` to + > `print()` is currently not entirely descriptive of the underlying cause: + > + > ```shell + > error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified + > print(point) + > ~~~~~^~~~~~~ + > ``` + > + > If the above error is seen, ensure that all argument types implement + > `Formattable`. + - The `StringRef` constructors from `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, reflecting their use for compatibility with C APIs. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 040a30beb3..08955c66fa 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -311,12 +311,12 @@ fn _put[x: StringLiteral](file: FileDescriptor = stdout): _put(x.as_string_slice(), file=file) -fn _put(strref: StringRef): +fn _put(strref: StringRef, file: FileDescriptor = stdout): var str_slice = StringSlice[ImmutableStaticLifetime]( unsafe_from_utf8_strref=strref ) - _put(str_slice) + _put(str_slice, file=file) @no_inline @@ -368,7 +368,7 @@ fn _put(x: StringSlice, file: FileDescriptor = stdout): @no_inline fn print[ - *Ts: Stringable + *Ts: Formattable ]( *values: *Ts, sep: StaticString = " ", @@ -389,84 +389,23 @@ fn print[ flush: If set to true, then the stream is forcibly flushed. file: The output stream. """ - _print(values, sep=sep, end=end, flush=flush, file=file) + var writer = Formatter(fd=file) -@no_inline -fn _print[ - *Ts: Stringable -]( - values: VariadicPack[_, _, Stringable, Ts], - *, - sep: StringSlice, - end: StringSlice, - flush: Bool, - file: FileDescriptor, -): @parameter - fn print_with_separator[i: Int, T: Stringable](value: T): - _put(str(value).as_string_slice(), file=file) + fn print_with_separator[i: Int, T: Formattable](value: T): + writer.write(value) @parameter if i < values.__len__() - 1: - _put(sep, file=file) + writer.write(sep) values.each_idx[print_with_separator]() - _put(end, file=file) - if flush: - _flush(file=file) - - -# ===----------------------------------------------------------------------=== # -# print_fmt -# ===----------------------------------------------------------------------=== # - - -# TODO: -# Finish transition to using non-allocating formatting abstractions by -# default, replace `print` with this function. -@no_inline -fn _print_fmt[ - T: Formattable, *Ts: Formattable -]( - first: T, - *rest: *Ts, - sep: StaticString = " ", - end: StaticString = "\n", - flush: Bool = False, -): - """Prints elements to the text stream. Each element is separated by `sep` - and followed by `end`. - - This print function does not perform unnecessary intermediate String - allocations during formatting. - - Parameters: - T: The first element type. - Ts: The remaining element types. - - Args: - first: The first element. - rest: The remaining elements. - sep: The separator used between elements. - end: The String to write after printing the elements. - flush: If set to true, then the stream is forcibly flushed. - """ - var writer = Formatter.stdout() - - write_to(writer, first) - - @parameter - fn print_elt[T: Formattable](a: T): - write_to(writer, sep, a) - - rest.each[print_elt]() - - write_to(writer, end) + writer.write(end) # TODO: What is a flush function that works on CUDA? @parameter if not triple_is_nvidia_cuda(): if flush: - _flush() + _flush(file=file) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 07bd38cf47..0e5553626f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -47,7 +47,7 @@ from .dtype import ( _integral_type_of, _scientific_notation_digits, ) -from .io import _print_fmt, _printf, _snprintf_scalar +from .io import _printf, _snprintf_scalar from .string import _calc_format_buffer_size, _calc_initial_buffer_size # ===----------------------------------------------------------------------=== # @@ -147,6 +147,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Roundable, Sized, Stringable, + Formattable, Truncable, Representable, ): diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 276f205284..c77660503e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -987,6 +987,17 @@ struct String( Returns: A string formed by formatting the argument sequence. + + Examples: + + Construct a String from several `Formattable` arguments: + + ```mojo + var string = String.format_sequence(1, ", ", 2.0, ", ", "three") + + assert_equal(string, "1, 2.0, three") + ``` + . """ var output = String() diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b69e845833..f3b4fbcc32 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -35,6 +35,7 @@ struct UnsafePointer[ Boolable, CollectionElement, Stringable, + Formattable, Intable, Comparable, ): @@ -338,6 +339,17 @@ struct UnsafePointer[ fn __str__(self) -> String: return hex(int(self)) + fn format_to(self, inout writer: Formatter): + """ + Formats this pointer address to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # TODO: Avoid intermediate String allocation. + writer.write(str(self)) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index b2c2489cc7..6af0133d2e 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -244,13 +244,13 @@ fn abort[result: AnyType = NoneType]() -> result: @always_inline("nodebug") fn abort[ - result: AnyType = NoneType, *, stringable: Stringable -](message: stringable) -> result: + result: AnyType = NoneType, *, formattable: Formattable +](message: formattable) -> result: """Calls a target dependent trap instruction if available. Parameters: result: The result type. - stringable: The Stringable type. + formattable: The Formattable type. Args: message: The message to include when aborting. diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 1f848aa39d..517d32cc8f 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -60,7 +60,7 @@ fn _dir_of_current_file() raises -> Path: return Path(str(file_name)[0:i]) -struct Path(Stringable, CollectionElement, PathLike, KeyElement): +struct Path(Stringable, Formattable, CollectionElement, PathLike, KeyElement): """The Path object.""" var path: String @@ -137,7 +137,17 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): Returns: A string representation of the path. """ - return self.path + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this path to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + writer.write(self.path) @always_inline fn __fspath__(self) -> String: diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index 121afabef5..25dfa6b673 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -28,6 +28,12 @@ trait Formattable: """ fn format_to(self, inout writer: Formatter): + """ + Formats the string representation of this type to the provided formatter. + + Args: + writer: The formatter to write to. + """ ... @@ -69,6 +75,22 @@ struct Formatter: fn __init__[F: ToFormatter](inout self, inout output: F): self = output._unsafe_to_formatter() + fn __init__(inout self, *, fd: FileDescriptor): + """ + Constructs a formatter that writes to the given file descriptor. + """ + + @always_inline + fn write_to_fd(ptr: UnsafePointer[NoneType], strref: StringRef): + var fd0 = ptr.bitcast[FileDescriptor]()[].value + + _put(strref, file=fd0) + + self = Formatter( + write_to_fd, + UnsafePointer.address_of(fd).bitcast[NoneType](), + ) + fn __init__( inout self, func: fn (UnsafePointer[NoneType], StringRef) -> None, diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index b1528e3063..5c5bdfb4c5 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -23,7 +23,7 @@ alias _kCompactElemPerSide = _kCompactMaxElemsToPrint // 2 fn _serialize_elements_compact[ - serialize_fn: fn[T: Stringable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, ](ptr: DTypePointer, len: Int): serialize_fn(_kStartTensorMarker) if len < _kCompactMaxElemsToPrint: @@ -43,7 +43,7 @@ fn _serialize_elements_compact[ fn _serialize_elements_complete[ - serialize_fn: fn[T: Stringable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, ](ptr: DTypePointer, len: Int): if len == 0: return @@ -54,7 +54,7 @@ fn _serialize_elements_complete[ fn _serialize_elements[ - serialize_fn: fn[T: Stringable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, compact: Bool = False, ](ptr: DTypePointer, len: Int): @parameter @@ -65,7 +65,7 @@ fn _serialize_elements[ fn _serialize[ - serialize_fn: fn[T: Stringable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, serialize_dtype: Bool = True, serialize_shape: Bool = True, serialize_end_line: Bool = True, diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index 11f9bb09cf..76fb2713e9 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,8 +12,6 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from builtin.io import _print_fmt - from utils._format import Formattable, Formatter, write_to @@ -42,14 +40,3 @@ fn test_write_to_stdout(): # CHECK: point = Point(1, 1) var point = Point(1, 1) write_to(stdout, "point = ", point) - - -# CHECK-LABEL: test_print_fmt() -fn test_print_fmt(): - print("== test_print_fmt") - - # CHECK: The quick brown fox... - _print_fmt("The quick brown fox...") - - # CHECK: ...jumps over the lazy ..Point(2, 5) ??? - _print_fmt("...jumps over the lazy ..", Point(2, 5), " ???") From 39a36fe0790acd0bac24df1a12f0bcdd102178e8 Mon Sep 17 00:00:00 2001 From: codingonion Date: Wed, 26 Jun 2024 14:07:26 -0500 Subject: [PATCH 1037/2019] [External] [stdlib] Fix typo in simd.mojo (#42354) [External] [stdlib] Fix typo in simd.mojo Co-authored-by: codingonion Closes modularml/mojo#3125 MODULAR_ORIG_COMMIT_REV_ID: d99b5480fd3a573e5e43ce59d1b5b7f764425249 --- stdlib/src/builtin/simd.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 0e5553626f..8664015b15 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1975,7 +1975,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn insert[*, offset: Int = 0](self, value: SIMD[type, _]) -> Self: - """Returns a the vector where the elements between `offset` and + """Returns a new vector where the elements between `offset` and `offset + input_width` have been replaced with the elements in `value`. Parameters: From fbf974a05b1d8ce19141277f1197990c0843872a Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 27 Jun 2024 00:31:31 +0300 Subject: [PATCH 1038/2019] [stdlib] Move the builtin `tensor` module to `max` This patch moves `tensor` to `max.tensor` without removing or changing any of its actual contents. MODULAR_ORIG_COMMIT_REV_ID: b590d52cce6fe953bcc595c21f18d79437c74769 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 0ae90c2077..6e84561aaa 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -277,4 +277,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. +- The builtin `tensor` module has been removed. Identical functionality is + available in `max.tensor`, but it is generally recommended to use `buffer` + when possible instead. + ### 🛠️ Fixed From 042d4fc25cea4c246637ad4075d232e7babacd30 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 26 Jun 2024 15:30:04 -0700 Subject: [PATCH 1039/2019] [mojo-lang] Support raising with statements with unconditional exits When using a context manager in a raising region, the language requires the context mangaer to define `fn __exit__(self, e: Error) -> Bool`. This method conditionally handles errors in the `with` region, allowing it to be swallowed instead of propagated. This is a useful feature for some applications, but in most cases, users just want the context manager to unconditionally exit: many of these are defined as trivially delegating to the unconditional exit. However, because the compiler still things that the `with` may conditionally exit, it still treats code as reachable along a raising path. For example, the following now work: ```mojo def foo() -> Int: with ContextMgr(): return 2 # no longer complains about missing return def bar(): var x: Int with ContextMgr(): x = might_raise() use(x) # no longer complains about uninitialized ``` This is because the compiler statically knows that if the `with` region raises, the context manager always allows the error to propagate. MODULAR_ORIG_COMMIT_REV_ID: a4cc4af3d5d3af36094e9d74b4a177505a2805ef --- docs/changelog.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 6e84561aaa..868e715ea9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,33 @@ what we publish. ### ⭐️ New +- Mojo context managers used in regions of code that may raise no longer need to + define a "conditional" exit function in the form of + `fn __exit__(self, e: Error) -> Bool`. This function allows the context + manager to conditionally intercept and handle the error and allow the function + to continue executing. This is useful for some applications, but in many cases + the conditional exit would delegate to the unconditional exit function + `fn __exit__(self)`. + + Concretely, this enables defining `with` regions that unconditionally + propagate inner errors, allowing code like: + + ```mojo + def might_raise() -> Int: + ... + + def foo() -> Int: + with ContextMgr(): + return might_raise() + # no longer complains about missing return + + def bar(): + var x: Int + with ContextMgr(): + x = might_raise() + print(x) # no longer complains about 'x' being uninitialized + ``` + - Now supports "conditional conformances" where some methods on a struct have additional trait requirements that the struct itself doesn't. This is expressed through an explicitly declared `self` type: From 3ffd2557fdab07a9bed11451d68ba88d18839d36 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 26 Jun 2024 16:33:07 -0700 Subject: [PATCH 1040/2019] [mojo-doc] Treat "normal" dunder functions as public by default Fixes a regression where user-defined dunder methods such as `__len__()` were not included in the generated API docs. Also adds API doc for some dunder methods where it was previously missing. MODULAR_ORIG_COMMIT_REV_ID: ab3cec7c86500bbfddb39f4de8ccdcc06314004d --- stdlib/src/builtin/_startup.mojo | 6 ++-- stdlib/src/builtin/dtype.mojo | 5 +++ stdlib/src/builtin/file.mojo | 6 +++- stdlib/src/builtin/float_literal.mojo | 2 +- stdlib/src/builtin/hash.mojo | 6 +++- stdlib/src/builtin/int.mojo | 2 ++ stdlib/src/builtin/math.mojo | 24 +++++++++++-- stdlib/src/builtin/object.mojo | 46 +++++++++++++++++++++++++ stdlib/src/builtin/simd.mojo | 3 ++ stdlib/src/builtin/string.mojo | 12 +++++-- stdlib/src/builtin/uint.mojo | 4 ++- stdlib/src/collections/dict.mojo | 12 +++++-- stdlib/src/collections/inline_list.mojo | 6 +++- stdlib/src/collections/list.mojo | 3 +- stdlib/src/memory/unsafe_pointer.mojo | 5 +++ stdlib/src/python/object.mojo | 7 ++++ stdlib/src/python/python.mojo | 3 +- stdlib/src/tempfile/tempfile.mojo | 32 ++++++++++++++--- stdlib/src/testing/testing.mojo | 8 ++++- stdlib/src/utils/inline_string.mojo | 10 ++++++ stdlib/src/utils/string_slice.mojo | 5 +++ 21 files changed, 186 insertions(+), 21 deletions(-) diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 25f1b028be..434d967bd8 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -70,7 +70,7 @@ fn __wrap_and_execute_main[ argc: Int32, argv: __mlir_type[`!kgen.pointer>>`], ) -> Int32: - """Define a C-ABI compatible entry point for non-raising main function""" + """Define a C-ABI compatible entry point for non-raising main function.""" # Initialize the global runtime. _ = _get_current_or_global_runtime() @@ -94,7 +94,7 @@ fn __wrap_and_execute_raising_main[ argc: Int32, argv: __mlir_type[`!kgen.pointer>>`], ) -> Int32: - """Define a C-ABI compatible entry point for a raising main function""" + """Define a C-ABI compatible entry point for a raising main function.""" # Initialize the global runtime. _ = _get_current_or_global_runtime() @@ -123,7 +123,7 @@ fn __wrap_and_execute_object_raising_main[ argv: __mlir_type[`!kgen.pointer>>`], ) -> Int32: """Define a C-ABI compatible entry point for a raising main function that - returns an object""" + returns an object.""" fn wrapped_main() raises: _ = main_func() diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index f6abe343b9..5b9d4b649d 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -228,6 +228,11 @@ struct DType(Stringable, Formattable, Representable, KeyElement): ) fn __hash__(self) -> Int: + """Return a 64-bit hash for this `DType` value. + + Returns: + A 64-bit integer hash of this `DType` value. + """ return hash(UInt8(self._as_i8())) @always_inline("nodebug") diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 4a09cf1353..d0a68e546c 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -459,7 +459,11 @@ struct FileHandle: raise (err_msg^).consume_as_error() fn __enter__(owned self) -> Self: - """The function to call when entering the context.""" + """The function to call when entering the context. + + Returns: + The file handle. + """ return self^ fn _get_raw_fd(self) -> Int: diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 603e0a976a..8696981453 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -409,7 +409,7 @@ struct FloatLiteral( rhs: The value to divide on. Returns: - The tuple with the dividend and the remainder + A tuple with the dividend and the remainder. """ var quotient: Self = self.__floordiv__(rhs) var remainder: Self = self - (quotient * rhs) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 39fa33490a..80442ffae9 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -88,7 +88,11 @@ trait Hashable: """ fn __hash__(self) -> Int: - """Return a 64-bit hash of the type's data.""" + """Return a 64-bit hash of the type's data. + + Returns: + A 64-bit integer hash of this instance's data. + """ ... diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index d91e538fe0..196994d766 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1012,8 +1012,10 @@ struct Int( @always_inline("nodebug") fn __round__(self, ndigits: Int) -> Self: """Return the rounded value of the Int value, which is itself. + Args: ndigits: The number of digits to round to. + Returns: The Int value itself if ndigits >= 0 else the rounded value. """ diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 47af0345e5..79d906cfb0 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -44,6 +44,11 @@ trait Absable: # TODO(MOCO-333): Reconsider the signature when we have parametric traits or # associated types. fn __abs__(self) -> Self: + """Get the absolute value of this instance. + + Returns: + The absolute value of the instance. + """ ... @@ -311,9 +316,22 @@ trait Roundable: # TODO(MOCO-333): Reconsider the signature when we have parametric traits or # associated types. fn __round__(self) -> Self: + """Get a rounded value for the type. + + Returns: + The rounded value. + """ ... fn __round__(self, ndigits: Int) -> Self: + """Get a rounded value for the type. + + Args: + ndigits: Number of digits after the decimal point. + + Returns: + The rounded value. + """ ... @@ -349,7 +367,8 @@ fn round(number: FloatLiteral) -> FloatLiteral: @always_inline fn round[T: Roundable, //](number: T, ndigits: Int) -> T: - """Get the rounded value of the given object. + """Get the value of this object, rounded to a specified number of + digits after the decimal point. Parameters: T: The type conforming to Roundable. @@ -367,7 +386,8 @@ fn round[T: Roundable, //](number: T, ndigits: Int) -> T: # TODO: remove this when conformance issue for FloatLiteral is fixed. @always_inline fn round(number: FloatLiteral, ndigits: Int) -> FloatLiteral: - """Get the rounded value of the given FloatLiteral. + """Get the value of this FloatLiteral, rounded to a specified number of + digits after the decimal point. Args: number: The FloatLiteral to get the rounded value of. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 7f5ef21278..e313b7a895 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1789,6 +1789,14 @@ struct object(IntableRaising, Boolable, Stringable): @always_inline fn __getattr__(self, key: StringLiteral) raises -> object: + """Gets the named attribute. + + Args: + key: The attribute name. + + Returns: + The attribute value. + """ if not self._value.is_obj(): raise Error( "TypeError: Type '" @@ -1801,6 +1809,12 @@ struct object(IntableRaising, Boolable, Stringable): @always_inline fn __setattr__(inout self, key: StringLiteral, value: object) raises: + """Sets the named attribute. + + Args: + key: The attribute name. + value: The attribute value. + """ if not self._value.is_obj(): raise Error( "TypeError: Type '" @@ -1813,18 +1827,40 @@ struct object(IntableRaising, Boolable, Stringable): @always_inline fn __call__(self) raises -> object: + """Calls the object as a function. + + Returns: + The function return value, as an object. + """ if not self._value.is_func(): raise Error("TypeError: Object is not a function") return self._value.get_as_func().invoke() @always_inline fn __call__(self, arg0: object) raises -> object: + """Calls the object as a function. + + Args: + arg0: The first function argument. + + Returns: + The function return value, as an object. + """ if not self._value.is_func(): raise Error("TypeError: Object is not a function") return self._value.get_as_func().invoke(arg0) @always_inline fn __call__(self, arg0: object, arg1: object) raises -> object: + """Calls the object as a function. + + Args: + arg0: The first function argument. + arg1: The second function argument. + + Returns: + The function return value, as an object. + """ if not self._value.is_func(): raise Error("TypeError: Object is not a function") return self._value.get_as_func().invoke(arg0, arg1) @@ -1833,6 +1869,16 @@ struct object(IntableRaising, Boolable, Stringable): fn __call__( self, arg0: object, arg1: object, arg2: object ) raises -> object: + """Calls the object as a function. + + Args: + arg0: The first function argument. + arg1: The second function argument. + arg2: The third function argument. + + Returns: + The function return value, as an object. + """ if not self._value.is_func(): raise Error("TypeError: Object is not a function") return self._value.get_as_func().invoke(arg0, arg1, arg2) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8664015b15..c9896fc697 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1396,9 +1396,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn __round__(self, ndigits: Int) -> Self: """Performs elementwise rounding on the elements of a SIMD vector. + This rounding goes to the nearest integer with ties away from zero. + Args: ndigits: The number of digits to round to. + Returns: The elementwise rounded value of this SIMD vector. """ diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index c77660503e..da6d07079a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1267,10 +1267,10 @@ struct String( @always_inline fn __len__(self) -> Int: - """Returns the string byte length. + """Gets the string length, in bytes. Returns: - The string byte length. + The string length, in bytes. """ # Avoid returning -1 if the buffer is not initialized if not self.unsafe_ptr(): @@ -1281,6 +1281,14 @@ struct String( @always_inline fn __str__(self) -> String: + """Gets the string itself. + + This method ensures that you can pass a `String` to a method that + takes a `Stringable` value. + + Returns: + The string itself. + """ return self @always_inline diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 9fb70a8d11..a974c013c4 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -677,10 +677,12 @@ struct UInt(Stringable, Representable): return self @always_inline("nodebug") - fn __round__(self, _ndigits: UInt) -> Self: + fn __round__(self, ndigits: UInt) -> Self: """Return the rounded value of the UInt value, which is itself. + Args: ndigits: The number of digits to round to. + Returns: The UInt value itself if ndigits >= 0 else the rounded value. """ diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index a74bb9d539..dcdcdbca2e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -609,7 +609,11 @@ struct Dict[K: KeyElement, V: CollectionElement]( # ===-------------------------------------------------------------------===# fn __len__(self) -> Int: - """The number of elements currently stored in the dictionary.""" + """The number of elements currently stored in the dictionary. + + Returns: + The number of elements currently stored in the dictionary. + """ return self.size fn __bool__(self) -> Bool: @@ -1058,7 +1062,11 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn __len__(self) -> Int: - """The number of elements currently stored in the keyword dictionary.""" + """The number of elements currently stored in the keyword dictionary. + + Returns: + The number of elements currently stored in the keyword dictionary. + """ return len(self._dict) # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index ceea8ec2b9..9f0cc79986 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -155,7 +155,11 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __len__(self) -> Int: - """Returns the length of the list.""" + """Returns the length of the list. + + Returns: + The number of elements in the list. + """ return self._size @always_inline diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 29c9272c59..f4e33ef95f 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -325,7 +325,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): When the compiler supports conditional methods, then a simple `str(my_list)` will be enough. - The elements' type must implement the `__repr__()` for this to work. + The elements' type must implement the `__repr__()` method for this to work. Parameters: U: The type of the elements in the list. Must implement the @@ -359,6 +359,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. + Note that since we can't condition methods on a trait yet, the way to call this method is a bit special. Here is an example below: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f3b4fbcc32..481edc30e0 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -337,6 +337,11 @@ struct UnsafePointer[ ](self.address) fn __str__(self) -> String: + """Gets a string representation of the pointer. + + Returns: + The string representation of the pointer. + """ return hex(int(self)) fn format_to(self, inout writer: Formatter): diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 52fcaf43f1..771c7f108a 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -1080,6 +1080,13 @@ struct PythonObject( ) raises -> PythonObject: """Call the underlying object as if it were a function. + Args: + args: Positional arguments to the function. + kwargs: Keyword arguments to the function. + + Raises: + If the function cannot be called for any reason. + Returns: The return value from the called object. """ diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index b2085122db..c4437544a4 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -227,7 +227,8 @@ struct Python: fn __str__(inout self, str_obj: PythonObject) -> StringRef: """Return a string representing the given Python object. - This function allows to convert Python objects to Mojo string type. + Args: + str_obj: The Python object. Returns: Mojo string representing the given Python object. diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 1c2556ca56..2feec1be5b 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -214,7 +214,10 @@ struct TemporaryDirectory: dir: Optional[String] = None, ignore_cleanup_errors: Bool = False, ) raises: - """Create a temporary directory. Can be used as a context manager. + """Create a temporary directory. + + Can be used as a context manager. When used as a context manager, + the directory is removed when the context manager exits. Args: suffix: Suffix to use for the directory name. @@ -227,12 +230,26 @@ struct TemporaryDirectory: self.name = mkdtemp(suffix, prefix, dir) fn __enter__(self) -> String: + """The function to call when entering the context. + + Returns: + The temporary directory name. + """ return self.name fn __exit__(self) raises: + """Called when exiting the context with no error.""" _rmtree(self.name, ignore_errors=self._ignore_cleanup_errors) fn __exit__(self, err: Error) -> Bool: + """Called when exiting the context with an error. + + Args: + err: The error raised inside the context. + + Returns: + True if the temporary directory was removed successfully. + """ try: self.__exit__() return True @@ -259,9 +276,13 @@ struct NamedTemporaryFile: dir: Optional[String] = None, delete: Bool = True, ) raises: - """Create a named temporary file. Can be used as a context manager. + """Create a named temporary file. + This is a wrapper around a `FileHandle`, - os.remove is called in close method if `delete` is True. + `os.remove()` is called in the `close()` method if `delete` is True. + + Can be used as a context manager. When used as a context manager, the + `close()` is called when the context manager exits. Args: mode: The mode to open the file in (the mode can be "r" or "w"). @@ -367,5 +388,8 @@ struct NamedTemporaryFile: self._file_handle.write(data) fn __enter__(owned self) -> Self: - """The function to call when entering the context.""" + """The function to call when entering the context. + + Returns: + The file handle.""" return self^ diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 2d4a8c5c37..2571f74370 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -442,8 +442,14 @@ struct assert_raises: fn __exit__(self, error: Error) raises -> Bool: """Exit the context manager with an error. + Args: + error: The error raised. + Raises: - Error: If the error raised doesn't match the expected error to raise. + Error: If the error raised doesn't include the expected string. + + Returns: + True if the error message contained the expected string. """ if self.message_contains: return self.message_contains.value() in str(error) diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 18aa8a3e75..e19177c2db 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -208,6 +208,11 @@ struct InlineString(Sized, Stringable, CollectionElement): # ===------------------------------------------------------------------=== # fn __len__(self) -> Int: + """Gets the string length, in bytes. + + Returns: + The string length, in bytes. + """ if self._is_small(): return len(self._storage[_FixedString[Self.SMALL_CAP]]) else: @@ -218,6 +223,11 @@ struct InlineString(Sized, Stringable, CollectionElement): return len(self._storage[String]) fn __str__(self) -> String: + """Gets this string as a standard `String`. + + Returns: + The string representation of the type. + """ if self._is_small(): return str(self._storage[_FixedString[Self.SMALL_CAP]]) else: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index defa1f8b70..f17ef12945 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -142,6 +142,11 @@ struct StringSlice[ # ===------------------------------------------------------------------===# fn __str__(self) -> String: + """Gets this slice as a standard `String`. + + Returns: + The string representation of the slice. + """ return String(str_slice=self) fn __len__(self) -> Int: From f4df13373ecc6b2b5293be0574c909cd80acd8d0 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 26 Jun 2024 18:47:08 -0700 Subject: [PATCH 1041/2019] Revert "[External] [stdlib] Make InlineArray call its elements' destructor" Regresses internal performance MODULAR_ORIG_COMMIT_REV_ID: aa1753e3a443190fed92b8ba3fbc2f1ac45e14ff --- stdlib/src/collections/inline_list.mojo | 15 +++-- stdlib/src/utils/static_tuple.mojo | 84 ++++++------------------- stdlib/test/utils/test_tuple.mojo | 25 -------- 3 files changed, 26 insertions(+), 98 deletions(-) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 9f0cc79986..c8bd0c02b5 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -20,7 +20,6 @@ from collections import InlineList """ from sys.intrinsics import _type_is_eq -from memory.unsafe import UnsafeMaybeUninitialized from utils import InlineArray @@ -90,7 +89,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """ # Fields - var _array: InlineArray[UnsafeMaybeUninitialized[ElementType], capacity] + var _array: InlineArray[ElementType, capacity] var _size: Int # ===-------------------------------------------------------------------===# @@ -100,9 +99,9 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __init__(inout self): """This constructor creates an empty InlineList.""" - self._array = InlineArray[ - UnsafeMaybeUninitialized[ElementType], capacity - ](unsafe_uninitialized=True) + self._array = InlineArray[ElementType, capacity]( + unsafe_uninitialized=True + ) self._size = 0 # TODO: Avoid copying elements in once owned varargs @@ -122,7 +121,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __del__(owned self): """Destroy all the elements in the list and free the memory.""" for i in range(self._size): - self._array[i].assume_initialized_destroy() + UnsafePointer.address_of(self._array[i]).destroy_pointee() # ===-------------------------------------------------------------------===# # Operator dunders @@ -147,7 +146,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): if idx < 0: idx += len(self) - return self._array[idx].assume_initialized() + return self._array[idx] # ===-------------------------------------------------------------------===# # Trait implementations @@ -250,5 +249,5 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): value: The value to append. """ debug_assert(self._size < capacity, "List is full.") - self._array[self._size].write(value^) + self._array[self._size] = value^ self._size += 1 diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 3e2cf5684f..1af6ee3bad 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -24,7 +24,6 @@ from sys.intrinsics import _type_is_eq from memory import Pointer from utils import unroll -from memory.unsafe import UnsafeMaybeUninitialized # ===----------------------------------------------------------------------===# # Utilities @@ -246,6 +245,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): # ===----------------------------------------------------------------------===# +@value struct InlineArray[ ElementType: CollectionElementNew, size: Int, @@ -259,11 +259,7 @@ struct InlineArray[ # Fields alias type = __mlir_type[ - `!pop.array<`, - size.value, - `, `, - UnsafeMaybeUninitialized[Self.ElementType], - `>`, + `!pop.array<`, size.value, `, `, Self.ElementType, `>` ] var _array: Self.type """The underlying storage for the array.""" @@ -281,8 +277,8 @@ struct InlineArray[ False, ( "Initialize with either a variadic list of arguments, a default" - " fill element or use the type" - " 'UnsafeMaybeUninitialized'." + " fill element or pass the keyword argument" + " 'unsafe_uninitialized'." ), ]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() @@ -291,28 +287,18 @@ struct InlineArray[ fn __init__(inout self, *, unsafe_uninitialized: Bool): """Create an InlineArray with uninitialized memory. - Note that this is highly unsafe and should be used with extreme caution. - It's very difficult to get it right. + Note that this is highly unsafe and should be used with caution. We recommend to use the `InlineList` instead if all the objects - are not available when creating the array. That works well for the - general case. - - If you do not want to pay the small performance overhead of `InlineList` and - still want raw uninitalized memory, then make sure to understand the - following: - - Never use this with types that do not have a trivial destructor. - If you want to use an uninitialized array with a type with - a non-trivial destructor, - then use `InlineArray[UnsafeMaybeUninitialized[MyType]]`, but you'll have - to manually call the destructors yourself. + are not available when creating the array. If despite those workarounds, one still needs an uninitialized array, it is possible with: + ```mojo var uninitialized_array = InlineArray[Int, 10](unsafe_uninitialized=True) ``` + Args: unsafe_uninitialized: A boolean to indicate if the array should be initialized. Always set to `True` (it's not actually used inside the constructor). @@ -332,9 +318,8 @@ struct InlineArray[ @parameter for i in range(size): - self._get_maybe_uninitialized(i)[].write( - Self.ElementType(other=fill) - ) + var ptr = UnsafePointer.address_of(self._get_reference_unsafe(i)[]) + ptr.initialize_pointee_explicit_copy(fill) @always_inline fn __init__(inout self, owned *elems: Self.ElementType): @@ -365,8 +350,9 @@ struct InlineArray[ # Move each element into the array storage. @parameter for i in range(size): - self._get_maybe_uninitialized(i)[].move_from( - UnsafePointer.address_of(storage[i]) + var eltref = self._get_reference_unsafe(i) + UnsafePointer.address_of(storage[i]).move_pointee_into( + UnsafePointer[Self.ElementType].address_of(eltref[]) ) # Mark the elements as already destroyed. @@ -379,37 +365,12 @@ struct InlineArray[ other: The value to copy. """ - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self = Self(unsafe_uninitialized=True) for idx in range(size): - self._get_maybe_uninitialized(idx)[].copy_from(other[idx]) - - fn __del__(owned self): - """Runs the destructor for all elements of the array.""" - for i in range(len(self)): - self._get_maybe_uninitialized(i)[].assume_initialized_destroy() + var ptr = self.unsafe_ptr() + idx - fn __copyinit__(inout self, other: Self): - """Copy construct the array. - - Args: - other: The value to copy from. - """ - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - for idx in range(size): - self._get_maybe_uninitialized(idx)[].copy_from(other[idx]) - - fn __moveinit__(inout self, owned other: Self): - """Move construct the array. - - Args: - other: The value to move from. - """ - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - for idx in range(size): - self._get_maybe_uninitialized(idx)[].move_from( - other._get_maybe_uninitialized(idx)[] - ) + ptr.initialize_pointee_explicit_copy(other[idx]) # ===------------------------------------------------------------------===# # Operator dunders @@ -491,16 +452,9 @@ struct InlineArray[ Returns: A reference to the element at the given index. """ - return self._get_maybe_uninitialized(idx)[].assume_initialized() - - @always_inline - fn _get_maybe_uninitialized( - ref [_]self: Self, idx: Int - ) -> Reference[ - UnsafeMaybeUninitialized[Self.ElementType], __lifetime_of(self) - ]: + var idx_as_int = index(idx) debug_assert( - 0 <= idx < size, + 0 <= idx_as_int < size, ( "Index must be within bounds when using" " `InlineArray.unsafe_get()`." @@ -508,7 +462,7 @@ struct InlineArray[ ) var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(self._array).address, - idx.value, + idx_as_int.value, ) return UnsafePointer(ptr)[] diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 592648a44c..b35a6ae4f2 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -15,7 +15,6 @@ from testing import assert_equal, assert_false, assert_true from utils import InlineArray, StaticIntTuple, StaticTuple -from test_utils import ValueDestructorRecorder def test_static_tuple(): @@ -214,29 +213,6 @@ def test_array_contains(): assert_true(not str("greetings") in arr) -def test_inline_array_runs_destructors(): - """Ensure we delete the right number of elements.""" - var destructor_counter = List[Int]() - var pointer_to_destructor_counter = UnsafePointer.address_of( - destructor_counter - ) - alias capacity = 32 - var inline_list = InlineArray[ValueDestructorRecorder, 4]( - ValueDestructorRecorder(0, pointer_to_destructor_counter), - ValueDestructorRecorder(10, pointer_to_destructor_counter), - ValueDestructorRecorder(20, pointer_to_destructor_counter), - ValueDestructorRecorder(30, pointer_to_destructor_counter), - ) - _ = inline_list - # This is the last use of the inline list, so it should be destroyed here, - # along with each element. - assert_equal(len(destructor_counter), 4) - assert_equal(destructor_counter[0], 0) - assert_equal(destructor_counter[1], 10) - assert_equal(destructor_counter[2], 20) - assert_equal(destructor_counter[3], 30) - - def main(): test_static_tuple() test_static_int_tuple() @@ -246,4 +222,3 @@ def main(): test_array_str() test_array_int_pointer() test_array_contains() - test_inline_array_runs_destructors() From a8930ee66f0f4e68fdfdd48ff207243d38ce9146 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 26 Jun 2024 19:33:24 -0700 Subject: [PATCH 1042/2019] [mojo-stdlib] Remove some `.value` uses (NFC) MODULAR_ORIG_COMMIT_REV_ID: 77618511d25a2e97b7c32be398b8ba64df3602d0 --- stdlib/src/python/object.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 771c7f108a..e7f82b3810 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -178,7 +178,7 @@ struct PythonObject( self.py_object = cpython.toPython(int_val) else: var fp_val = value.cast[DType.float64]() - self.py_object = cpython.PyFloat_FromDouble(fp_val.value) + self.py_object = cpython.PyFloat_FromDouble(fp_val) fn __init__(inout self, value: Bool): """Initialize the object from a bool. From 67b3dd028f825251c3f31685ae44940606515b5b Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Thu, 27 Jun 2024 09:28:10 -0500 Subject: [PATCH 1043/2019] [External] [stdlib] Add equality methods to StringSlice (#42373) [External] [stdlib] Add equality methods to StringSlice Now that [Span implements equality methods](https://github.com/modularml/mojo/pull/2698), it makes sense to do the same for StringSlice. Note that we can't just defer to `Span`'s equality method, since `SIMD.__eq__()` does not return a `Bool` (which is a discussion out of scope of this PR). Co-authored-by: Lukas Hermann Closes modularml/mojo#3127 MODULAR_ORIG_COMMIT_REV_ID: 1ea234a09517ab349cfc9298703dd3375dcfea5b --- stdlib/src/utils/string_slice.mojo | 89 ++++++++++++++++++++++++ stdlib/test/utils/test_string_slice.mojo | 34 ++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index f17ef12945..0d3b1e5dbd 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -168,6 +168,95 @@ struct StringSlice[ """ writer.write_str(str_slice=self) + fn __bool__(self) -> Bool: + """Check if a string slice is non-empty. + + Returns: + True if a string slice is non-empty, False otherwise. + """ + return len(self._slice) > 0 + + fn __eq__(self, rhs: StringSlice) -> Bool: + """Verify if a string slice is equal to another string slice. + + Args: + rhs: The string slice to compare against. + + Returns: + True if the string slices are equal in length and contain the same elements, False otherwise. + """ + if not self and not rhs: + return True + if len(self) != len(rhs): + return False + # same pointer and length, so equal + if self._slice.unsafe_ptr() == rhs._slice.unsafe_ptr(): + return True + for i in range(len(self)): + if self._slice[i] != rhs._slice.unsafe_ptr()[i]: + return False + return True + + @always_inline + fn __eq__(self, rhs: String) -> Bool: + """Verify if a string slice is equal to a string. + + Args: + rhs: The string to compare against. + + Returns: + True if the string slice is equal to the input string in length and contain the same bytes, False otherwise. + """ + return self == rhs.as_string_slice() + + @always_inline + fn __eq__(self, rhs: StringLiteral) -> Bool: + """Verify if a string slice is equal to a literal. + + Args: + rhs: The literal to compare against. + + Returns: + True if the string slice is equal to the input literal in length and contain the same bytes, False otherwise. + """ + return self == rhs.as_string_slice() + + @always_inline + fn __ne__(self, rhs: StringSlice) -> Bool: + """Verify if span is not equal to another string slice. + + Args: + rhs: The string slice to compare against. + + Returns: + True if the string slices are not equal in length or contents, False otherwise. + """ + return not self == rhs + + @always_inline + fn __ne__(self, rhs: String) -> Bool: + """Verify if span is not equal to another string slice. + + Args: + rhs: The string slice to compare against. + + Returns: + True if the string and slice are not equal in length or contents, False otherwise. + """ + return not self == rhs + + @always_inline + fn __ne__(self, rhs: StringLiteral) -> Bool: + """Verify if span is not equal to a literal. + + Args: + rhs: The string literal to compare against. + + Returns: + True if the slice is not equal to the literal in length or contents, False otherwise. + """ + return not self == rhs + # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 41057780b0..39742474f2 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false from utils import Span @@ -145,8 +145,40 @@ fn test_slice_len() raises: assert_equal(1, len(slice5)) +fn test_slice_eq() raises: + var str1: String = "12345" + var str2: String = "12345" + var str3: StringLiteral = "12345" + var str4: String = "abc" + var str5: String = "abcdef" + var str6: StringLiteral = "abcdef" + + # eq + + assert_true(str1.as_string_slice().__eq__(str1)) + assert_true(str1.as_string_slice().__eq__(str2)) + assert_true(str2.as_string_slice().__eq__(str2.as_string_slice())) + assert_true(str1.as_string_slice().__eq__(str3)) + + # ne + + assert_true(str1.as_string_slice().__ne__(str4)) + assert_true(str1.as_string_slice().__ne__(str5)) + assert_true(str1.as_string_slice().__ne__(str5.as_string_slice())) + assert_true(str1.as_string_slice().__ne__(str6)) + + +fn test_slice_bool() raises: + var str1: String = "abc" + assert_true(str1.as_string_slice().__bool__()) + var str2: String = "" + assert_true(not str2.as_string_slice().__bool__()) + + fn main() raises: test_string_literal_byte_slice() test_string_byte_slice() test_heap_string_from_string_slice() test_slice_len() + test_slice_eq() + test_slice_bool() From 0767c00eee012926847dec86f5abb6502d011543 Mon Sep 17 00:00:00 2001 From: Lily Brown Date: Thu, 27 Jun 2024 10:00:26 -0700 Subject: [PATCH 1044/2019] [LSP] Remove unused argument warnings We cannot reliably detect unused arguments, since some arguments are used to influence overload selection rather than being used in the actual function body. MODULAR_ORIG_COMMIT_REV_ID: f271d35aa6625fc7e9068084118c33369fedc851 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 868e715ea9..2722b621b9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -308,4 +308,6 @@ by [@jayzhan211](https://github.com/jayzhan211)) available in `max.tensor`, but it is generally recommended to use `buffer` when possible instead. +- Removed the Mojo Language Server warnings for unused function arguments. + ### 🛠️ Fixed From 1f6318e91fd22597faab1a00a956826dcebe0e59 Mon Sep 17 00:00:00 2001 From: Lily Brown Date: Thu, 27 Jun 2024 10:03:28 -0700 Subject: [PATCH 1045/2019] [LSP] Fix crash when importing the current file ``` MODULAR_ORIG_COMMIT_REV_ID: ecf2d83081a1a2e723033fff26e4bdab01cfe5f2 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 2722b621b9..e44b38fec0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -311,3 +311,5 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed the Mojo Language Server warnings for unused function arguments. ### 🛠️ Fixed + +- Fixed a crash in the Mojo Language Server when importing the current file. From 1304fa011c48b710dc7ff92afd2ff5c6d13595de Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Thu, 27 Jun 2024 16:33:05 -0400 Subject: [PATCH 1046/2019] [LSP] Drop . as a commit character for auto-completion The Mojo Language Server no longer sets `.` as a commit character for auto-completion. MODULAR_ORIG_COMMIT_REV_ID: a16305c2b5a893f2a402f94c993296e294ba2317 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e44b38fec0..683a645edd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -288,6 +288,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `ulp` function in `numerics` have been moved to the `math` module. +- The Mojo Language Server no longer sets `.` as a commit character for + auto-completion. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` From d490dcfc731d36d0844e85eacc65f4cd36932af3 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 27 Jun 2024 14:16:11 -0700 Subject: [PATCH 1047/2019] [Stdlib] Lower to native bf16 instructions Do not use the software conversion if the hardware has native bf16 conversion functions. MODULAR_ORIG_COMMIT_REV_ID: c5babb30518ae1f890aa405cd911504ae08bd576 --- stdlib/src/builtin/simd.mojo | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c9896fc697..284cabfe18 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -126,6 +126,11 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: } +@always_inline("nodebug") +fn _has_native_bf16_support() -> Bool: + return triple_is_nvidia_cuda() + + # ===----------------------------------------------------------------------=== # # SIMD # ===----------------------------------------------------------------------=== # @@ -1446,12 +1451,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return self.select(SIMD[target, size](1), SIMD[target, size](0)) elif target == DType.bool: return rebind[SIMD[target, size]](self != 0) - elif type is DType.bfloat16: + elif type is DType.bfloat16 and not _has_native_bf16_support(): var cast_result = _bfloat16_to_f32( rebind[SIMD[DType.bfloat16, size]](self) ).cast[target]() return rebind[SIMD[target, size]](cast_result) - elif target == DType.bfloat16: + elif target == DType.bfloat16 and not _has_native_bf16_support(): return rebind[SIMD[target, size]]( _f32_to_bfloat16(self.cast[DType.float32]()) ) From 58f200a293f6fa611967058072d1196beb9c254f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 27 Jun 2024 14:58:01 -0700 Subject: [PATCH 1048/2019] [mojo-stdlib] Remove `SIMD.prefetch` and the default SIMD size The default SIMD size is overload resolution to sometimes pick the default size instead of inferring it from somewhere else. This is leading to hard-to-spot and incorrect codegen in certain cases. It actually isn't used very often: it only mattered for `SIMD.prefetch` calls, where the parameter itself wasn't used. This PR removes the default parameter and removes `SIMD.prefetch`. Users of the former should call `prefetch` directly. MODULAR_ORIG_COMMIT_REV_ID: ba8f8da9c23b5dcd3c442b12e856b6eabc8d9a95 --- stdlib/benchmarks/utils/bench_formatter.mojo | 2 +- stdlib/src/builtin/simd.mojo | 21 +------------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 88848b105b..b4b97babb9 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -47,7 +47,7 @@ fn bench_formatter_simd[n: Int](inout b: Bencher) raises: fn call_fn(): var s1 = String() var s1_fmt = Formatter(s1) - SIMD[DType.int32](n).format_to(s1_fmt) + SIMD[DType.int32, simdwidthof[DType.int32]()](n).format_to(s1_fmt) _ = s1^ b.iter[call_fn]() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 284cabfe18..3490fd89f3 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -138,7 +138,7 @@ fn _has_native_bf16_support() -> Bool: @lldb_formatter_wrapping_type @register_passable("trivial") -struct SIMD[type: DType, size: Int = simdwidthof[type]()]( +struct SIMD[type: DType, size: Int]( Absable, Boolable, Ceilable, @@ -2644,25 +2644,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "llvm.vector.splice", Self, has_side_effect=False ](zero_simd, self, Int32(-shift)) - @staticmethod - @always_inline - fn prefetch[ - params: PrefetchOptions, - *, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space]): - # Prefetch at the underlying address. - """Prefetches memory at the underlying address. - - Parameters: - params: Prefetch options (see `PrefetchOptions` for details). - address_space: The address space the pointer is in. - - Args: - ptr: The pointer to prefetch from. - """ - prefetch[params](ptr) - @staticmethod @always_inline("nodebug") fn load[ From 400c3de3013fc52759a894c82a63d9816d541961 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Fri, 28 Jun 2024 18:49:45 +0300 Subject: [PATCH 1049/2019] [mojo-stdlib] Reland again: Add `ImplicitlyBoolable` trait This patch changes the implicit conversion rules for Bool by introducing a trait for types that should be implicitly passable as a Bo ol. This behavior will now be restricted to numeric types and a few other struct to avoid problems with unintented conversions. The a ssert_{true,false} testing utilities are update to take Boolable values, keeping it easy to test (explicitly) Boolable types. Previously, the patch introduced unintentional string allocations in `debug_assert` calls which tanked performance, and so it was reverted. This should be now fixed. MODULAR_ORIG_COMMIT_REV_ID: 80c678e957fb5fd0ef4f64cbc822b596139f5cee --- docs/changelog.md | 12 +++- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 8 +-- stdlib/src/builtin/bool.mojo | 63 ++++++++++++++++--- stdlib/src/builtin/float_literal.mojo | 11 +++- stdlib/src/builtin/int.mojo | 11 +++- stdlib/src/builtin/int_literal.mojo | 13 +++- stdlib/src/builtin/object.mojo | 12 +++- stdlib/src/builtin/simd.mojo | 14 ++++- stdlib/src/builtin/sort.mojo | 2 + stdlib/src/builtin/string_literal.mojo | 12 ++-- stdlib/src/memory/unsafe_pointer.mojo | 11 +++- stdlib/src/python/object.mojo | 11 +++- stdlib/src/testing/testing.mojo | 24 ++++--- stdlib/test/builtin/test_bool.mojo | 7 ++- stdlib/test/builtin/test_float_literal.mojo | 15 ++--- stdlib/test/builtin/test_int.mojo | 10 ++- stdlib/test/builtin/test_int_literal.mojo | 10 ++- stdlib/test/builtin/test_simd.mojo | 17 +++++ stdlib/test/memory/test_unsafepointer.mojo | 15 ++++- 20 files changed, 227 insertions(+), 53 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 683a645edd..b4a5815a60 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -268,8 +268,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) The default store size is the size of the `SIMD` value to be stored. - `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been removed - since a Slice with no end is now represented by an empty `Slice.end` option. + a constructor which accepts optional values. `Slice._has_end()` has also been + removed since a Slice with no end is now represented by an empty `Slice.end` + option. ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) ```mojo @@ -291,6 +292,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The Mojo Language Server no longer sets `.` as a commit character for auto-completion. +- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer + implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced + for types where this behavior is desired. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` @@ -305,7 +310,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD + instead. - The builtin `tensor` module has been removed. Identical functionality is available in `max.tensor`, but it is generally recommended to use `buffer` diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index f0bcf74f6f..22d5611e3f 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -25,7 +25,7 @@ fn bench_stringify_small_integers(inout b: Bencher) raises: fn call_fn(): for i in range(1_000): var a = str(i) - benchmark.keep(a) + benchmark.keep(bool(a)) b.iter[call_fn]() diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index f089a9d31b..ebd089488b 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -62,7 +62,7 @@ fn bench_dict_small_insert(inout b: Bencher) raises: small[key] = random.random_si64(0, small_n).value b.iter[call_fn]() - keep(small) + keep(bool(small)) # ===----------------------------------------------------------------------===# @@ -77,7 +77,7 @@ fn bench_dict_large_insert(inout b: Bencher) raises: large[key] = random.random_si64(0, large_n).value b.iter[call_fn]() - keep(large) + keep(bool(large)) # ===----------------------------------------------------------------------===# @@ -92,7 +92,7 @@ fn bench_dict_small_lookup(inout b: Bencher) raises: _ = small[key] b.iter[call_fn]() - keep(small) + keep(bool(small)) # ===----------------------------------------------------------------------===# @@ -107,7 +107,7 @@ fn bench_dict_large_lookup(inout b: Bencher) raises: _ = large[key] b.iter[call_fn]() - keep(large) + keep(bool(large)) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 330c822887..49b4eda356 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -25,7 +25,8 @@ from utils._visualizers import lldb_formatter_wrapping_type trait Boolable: - """The `Boolable` trait describes a type that can be converted to a bool. + """The `Boolable` trait describes a type that can be explicitly converted to + a `Bool` or evaluated as a boolean expression in `if` or `while` conditions. This trait requires the type to implement the `__bool__()` method. For example: @@ -49,6 +50,45 @@ trait Boolable: ... +# ===----------------------------------------------------------------------=== # +# ImplicitlyBoolable +# ===----------------------------------------------------------------------=== # + + +trait ImplicitlyBoolable(Boolable): + """The `ImplicitlyBoolable` trait describes a type that can be implicitly + converted to a `Bool`. + + Types conforming to this trait can be passed to a function that expects a + `Bool` without explicitly converting to it. Accordingly, most types should + conform to `Boolable` instead, since implicit conversions to `Bool` can have + unintuitive consequences. + + This trait requires the type to implement the `__as_bool__()` method. For + example: + + ```mojo + @value + struct Foo(ImplicitlyBoolable): + var val: Bool + + fn __as_bool__(self) -> Bool: + return self.val + + fn __bool__(self) -> Bool: + return self.__as_bool__() + ``` + """ + + fn __as_bool__(self) -> Bool: + """Get the boolean representation of the value. + + Returns: + The boolean representation of the value. + """ + ... + + # ===----------------------------------------------------------------------=== # # Bool # ===----------------------------------------------------------------------=== # @@ -58,14 +98,14 @@ trait Boolable: @value @register_passable("trivial") struct Bool( - Boolable, CollectionElementNew, ComparableCollectionElement, + ImplicitlyBoolable, Indexer, Intable, - Formattable, Representable, Stringable, + Formattable, ): """The primitive Bool scalar value used in Mojo.""" @@ -102,11 +142,11 @@ struct Bool( ) @always_inline("nodebug") - fn __init__[T: Boolable](inout self, value: T): - """Implicitly convert a Boolable value to a Bool. + fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): + """Convert an ImplicitlyBoolable value to a Bool. Parameters: - T: The Boolable type. + T: The ImplicitlyBoolable type. Args: value: The boolable value. @@ -122,6 +162,15 @@ struct Bool( """ return self + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert to Bool. + + Returns: + This value. + """ + return self.__bool__() + @always_inline("nodebug") fn __mlir_i1__(self) -> __mlir_type.i1: """Convert this Bool to __mlir_type.i1. @@ -443,7 +492,7 @@ fn bool(value: None) -> Bool: @always_inline -fn bool[T: Boolable](value: T) -> Bool: +fn bool[T: Boolable, //](value: T) -> Bool: """Get the bool representation of the object. Parameters: diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 8696981453..8b405f33e2 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -27,11 +27,11 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct FloatLiteral( Absable, - Boolable, Ceilable, CeilDivable, Comparable, Floorable, + ImplicitlyBoolable, Intable, Roundable, Stringable, @@ -161,6 +161,15 @@ struct FloatLiteral( """ return self != 0.0 + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """A FloatLiteral value is true if it is non-zero. + + Returns: + True if non-zero. + """ + return self.__bool__() + @always_inline("nodebug") fn __neg__(self) -> FloatLiteral: """Return the negation of the FloatLiteral value. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 196994d766..23856d06b3 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -243,7 +243,6 @@ fn int(value: String, base: Int = 10) raises -> Int: @register_passable("trivial") struct Int( Absable, - Boolable, Ceilable, CeilDivable, Comparable, @@ -251,6 +250,7 @@ struct Int( Formattable, Indexer, Intable, + ImplicitlyBoolable, KeyElement, Powable, Roundable, @@ -952,6 +952,15 @@ struct Int( """ return self != 0 + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert this Int to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index d6f11097fd..ce9c97beb6 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -20,16 +20,16 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @register_passable("trivial") struct IntLiteral( Absable, - Boolable, Ceilable, CeilDivable, Comparable, Floorable, + ImplicitlyBoolable, + Indexer, Intable, Roundable, Stringable, Truncable, - Indexer, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -581,6 +581,15 @@ struct IntLiteral( """ return self != Self() + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Convert this IntLiteral to Bool. + + Returns: + False Bool value if the value is equal to 0 and True otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __index__(self) -> Int: """Return self converted to an integer, if self is suitable for use as diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index e313b7a895..80928e5c5d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -629,7 +629,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===----------------------------------------------------------------------=== # -struct object(IntableRaising, Boolable, Stringable): +struct object(IntableRaising, ImplicitlyBoolable, Stringable): """Represents an object without a concrete type. This is the type of arguments in `def` functions that do not have a type @@ -897,6 +897,16 @@ struct object(IntableRaising, Boolable, Stringable): raise "object type cannot be converted to an integer" + fn __as_bool__(self) -> Bool: + """Performs conversion to bool according to Python semantics. Integers + and floats are true if they are non-zero, and strings and lists are true + if they are non-empty. + + Returns: + Whether the object is considered true. + """ + return self.__bool__() + @always_inline fn __str__(self) -> String: """Performs conversion to string according to Python diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 3490fd89f3..61a3b558f4 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -140,7 +140,6 @@ fn _has_native_bf16_support() -> Bool: @register_passable("trivial") struct SIMD[type: DType, size: Int]( Absable, - Boolable, Ceilable, CeilDivable, CollectionElement, @@ -148,6 +147,7 @@ struct SIMD[type: DType, size: Int]( Floorable, Hashable, Intable, + ImplicitlyBoolable, Powable, Roundable, Sized, @@ -1292,6 +1292,18 @@ struct SIMD[type: DType, size: Int]( ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value + @always_inline("nodebug") + fn __as_bool__(self) -> Bool: + """Converts the SIMD scalar into a boolean value. + + Constraints: + The size of the SIMD vector must be 1. + + Returns: + True if the SIMD scalar is non-zero and False otherwise. + """ + return self.__bool__() + @always_inline("nodebug") fn __int__(self) -> Int: """Casts to the value to an Int. If there is a fractional component, diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 17991379ab..402d68c6a4 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -256,6 +256,7 @@ fn partition[ fn sort(inout buff: Pointer[Int], len: Int): """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Args: @@ -272,6 +273,7 @@ fn sort(inout buff: Pointer[Int], len: Int): fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """Sort the buffer inplace. + The function doesn't return anything, the buffer is updated inplace. Parameters: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 97ab53487d..11db3af8e2 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -33,14 +33,14 @@ from .string import _atol @lldb_formatter_wrapping_type @register_passable("trivial") struct StringLiteral( - Sized, - IntableRaising, - Stringable, - Representable, - KeyElement, Boolable, - Formattable, Comparable, + Formattable, + IntableRaising, + KeyElement, + Representable, + Sized, + Stringable, ): """This type represents a string literal. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 481edc30e0..35b7955fe0 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -32,7 +32,7 @@ from memory.memory import _free, _malloc struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ]( - Boolable, + ImplicitlyBoolable, CollectionElement, Stringable, Formattable, @@ -325,6 +325,15 @@ struct UnsafePointer[ """ return int(self) != 0 + @always_inline + fn __as_bool__(self) -> Bool: + """Return true if the pointer is non-null. + + Returns: + Whether the pointer is null. + """ + return self.__bool__() + @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index e7f82b3810..756adf2f70 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,8 +101,8 @@ struct _PyIter(Sized): @register_passable struct PythonObject( - Boolable, CollectionElement, + ImplicitlyBoolable, Indexer, Intable, KeyElement, @@ -387,6 +387,15 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyObject_IsTrue(self.py_object) == 1 + @always_inline + fn __as_bool__(self) -> Bool: + """Evaluate the boolean value of the object. + + Returns: + Whether the object evaluates as true. + """ + return self.__bool__() + fn __is__(self, other: PythonObject) -> Bool: """Test if the PythonObject is the `other` PythonObject, the same as `x is y` in Python. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 2571f74370..45a056f185 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -76,11 +76,13 @@ fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: @always_inline -fn assert_true( - val: Bool, msg: String = "condition was unexpectedly False" -) raises: - """Asserts that the input value is True. If it is not then an - Error is raised. +fn assert_true[ + T: Boolable, // +](val: T, msg: String = "condition was unexpectedly False") raises: + """Asserts that the input value is True and raises an Error if it's not. + + Parameters: + T: The type of the value argument. Args: val: The value to assert to be True. @@ -94,11 +96,13 @@ fn assert_true( @always_inline -fn assert_false( - val: Bool, msg: String = "condition was unexpectedly True" -) raises: - """Asserts that the input value is False. If it is not then an Error is - raised. +fn assert_false[ + T: Boolable, // +](val: T, msg: String = "condition was unexpectedly True") raises: + """Asserts that the input value is False and raises an Error if it's not. + + Parameters: + T: The type of the value argument. Args: val: The value to assert to be False. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index c97ee2a827..74071d7bcc 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -34,12 +34,15 @@ struct MyTrue: fn __bool__(self) -> Bool: return True + fn __as_bool__(self) -> Bool: + return self.__bool__() + fn takes_bool(cond: Bool) -> Bool: return cond -def test_convert_from_boolable(): +def test_convert_from_implicitly_boolable(): assert_true(takes_bool(MyTrue())) assert_true(bool(MyTrue())) @@ -145,7 +148,7 @@ def test_comparisons(): def main(): test_bool_cast_to_int() test_bool_none() - test_convert_from_boolable() + test_convert_from_implicitly_boolable() test_bool_to_string() test_bool_representation() test_bitwise() diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 3991a0a4ec..f6457eacc3 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -157,15 +157,12 @@ def test_int_conversion(): assert_equal(int(4.0), 4) -def test_boolean_comparable(): - var f1 = 0.0 - assert_false(f1) +def test_bool(): + assert_false(FloatLiteral.__bool__(0.0)) + assert_false(FloatLiteral.__as_bool__(0.0)) - var f2 = 2.0 - assert_true(f2) - - var f3 = 1.0 - assert_true(f3) + assert_true(FloatLiteral.__bool__(2.0)) + assert_true(FloatLiteral.__as_bool__(2.0)) def test_equality(): @@ -205,7 +202,7 @@ def main(): test_mod() test_div_mod() test_int_conversion() - test_boolean_comparable() + test_bool() test_equality() test_is_special_value() test_abs() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 586d6e26f4..d173a285c9 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,7 +14,7 @@ from sys.info import bitwidthof -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false def test_properties(): @@ -155,6 +155,13 @@ def test_indexer(): assert_equal(987, Int(987).__index__()) +def test_bool(): + assert_true(Int(5).__bool__()) + assert_false(Int(0).__bool__()) + assert_true(Int(5).__as_bool__()) + assert_false(Int(0).__as_bool__()) + + def main(): test_properties() test_add() @@ -172,3 +179,4 @@ def main(): test_string_conversion() test_int_representation() test_indexer() + test_bool() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index b5a512d17b..6a4eb04edd 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false def test_add(): @@ -108,6 +108,13 @@ def test_divmod(): assert_equal(t[1], -1) +def test_bool(): + assert_true(IntLiteral.__bool__(5)) + assert_false(IntLiteral.__bool__(0)) + assert_true(IntLiteral.__as_bool__(5)) + assert_false(IntLiteral.__as_bool__(0)) + + def main(): test_add() test_sub() @@ -121,3 +128,4 @@ def main(): test_bit_width() test_abs() test_indexer() + test_bool() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index b287eadc53..e496e10697 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -214,6 +214,22 @@ def test_issue_30237(): assert_equal(result1, result2) +def test_bool(): + assert_true(Scalar[DType.bool](True).__bool__()) + assert_false(Scalar[DType.bool](False).__bool__()) + assert_true(Scalar[DType.int32](5).__bool__()) + assert_false(Scalar[DType.int32](0).__bool__()) + assert_true(Scalar[DType.float32](5.0).__bool__()) + assert_false(Scalar[DType.float32](0.0).__bool__()) + + assert_true(Scalar[DType.bool](True).__as_bool__()) + assert_false(Scalar[DType.bool](False).__as_bool__()) + assert_true(Scalar[DType.int32](5).__as_bool__()) + assert_false(Scalar[DType.int32](0).__as_bool__()) + assert_true(Scalar[DType.float32](5.0).__as_bool__()) + assert_false(Scalar[DType.float32](0.0).__as_bool__()) + + def test_truthy(): alias dtypes = ( DType.bool, @@ -1576,6 +1592,7 @@ def main(): test_sub() test_sub_with_overflow() test_trunc() + test_bool() test_truthy() test_modf() test_split() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 4c9fedea21..e0e9e7e7f9 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -14,7 +14,7 @@ from memory import UnsafePointer from test_utils import ExplicitCopyOnly, MoveCounter -from testing import assert_equal, assert_not_equal, assert_true +from testing import assert_equal, assert_not_equal, assert_true, assert_false struct MoveOnlyType(Movable): @@ -216,6 +216,18 @@ def test_indexing(): assert_equal(ptr[3], 3) +def test_bool(): + var nullptr = UnsafePointer[Int]() + var ptr = UnsafePointer[Int].alloc(1) + + assert_true(ptr.__bool__()) + assert_false(nullptr.__bool__()) + assert_true(ptr.__as_bool__()) + assert_false(nullptr.__as_bool__()) + + ptr.free() + + def main(): test_address_of() @@ -233,3 +245,4 @@ def main(): test_unsafepointer_address_space() test_indexing() + test_bool() From cc7ab2d993b889016ecf93480026504040151f61 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:58:11 -0500 Subject: [PATCH 1050/2019] [External] [stdlib] Add more `String` property check methods (#42456) [External] [stdlib] Add more `String` property check methods Add `String` convenience overloads to `isupper` `islower` `_isspace` and `isprintable` Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3072 MODULAR_ORIG_COMMIT_REV_ID: 77bb1d1796d9f2270103304be5775da18780f9c1 --- stdlib/src/builtin/string.mojo | 98 ++++++++++++++++++++++++++-- stdlib/test/builtin/test_string.mojo | 26 ++++++++ 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index da6d07079a..eaf530004e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -537,6 +537,22 @@ fn isdigit(c: String) -> Bool: # ===----------------------------------------------------------------------=== # +@always_inline +fn isupper(c: String) -> Bool: + """Determines whether the given character is an uppercase character. + + This currently only respects the default "C" locale, i.e. returns True iff + the character specified is one of "ABCDEFGHIJKLMNOPQRSTUVWXYZ". + + Args: + c: The character to check. + + Returns: + True if the character is uppercase. + """ + return isupper(ord(c)) + + fn isupper(c: UInt8) -> Bool: """Determines whether the given character is an uppercase character. @@ -563,6 +579,22 @@ fn _is_ascii_uppercase(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # +@always_inline +fn islower(c: String) -> Bool: + """Determines whether the given character is an lowercase character. + + This currently only respects the default "C" locale, i.e. returns True iff + the character specified is one of "abcdefghijklmnopqrstuvwxyz". + + Args: + c: The character to check. + + Returns: + True if the character is lowercase. + """ + return islower(ord(c)) + + fn islower(c: UInt8) -> Bool: """Determines whether the given character is an lowercase character. @@ -589,6 +621,23 @@ fn _is_ascii_lowercase(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # +@always_inline +fn _isspace(c: String) -> Bool: + """Determines whether the given character is a whitespace character. + + This only respects the default "C" locale, i.e. returns True only if the + character specified is one of " \\t\\n\\r\\f\\v". For semantics similar + to Python, use `String.isspace()`. + + Args: + c: The character to check. + + Returns: + True iff the character is one of the whitespace characters listed above. + """ + return _isspace(ord(c)) + + fn _isspace(c: UInt8) -> Bool: """Determines whether the given character is a whitespace character. @@ -668,6 +717,19 @@ fn _isnewline(s: String) -> Bool: # ===----------------------------------------------------------------------=== # +@always_inline +fn isprintable(c: String) -> Bool: + """Determines whether the given character is a printable character. + + Args: + c: The character to check. + + Returns: + True if the character is a printable character, otherwise False. + """ + return isprintable(ord(c)) + + fn isprintable(c: UInt8) -> Bool: """Determines whether the given character is a printable character. @@ -2207,10 +2269,31 @@ struct String( Returns: True if all characters are digits else False. """ - for c in self: - if not isdigit(c): - return False - return True + return _all[isdigit](self) + + fn isupper(self) -> Bool: + """Returns True if all characters in the string are uppercase. + + Returns: + True if all characters are uppercase else False. + """ + return _all[isupper](self) + + fn islower(self) -> Bool: + """Returns True if all characters in the string are lowercase. + + Returns: + True if all characters are lowercase else False. + """ + return _all[islower](self) + + fn isprintable(self) -> Bool: + """Returns True if all characters in the string are printable. + + Returns: + True if all characters are printable else False. + """ + return _all[isprintable](self) # ===----------------------------------------------------------------------=== # @@ -2218,6 +2301,13 @@ struct String( # ===----------------------------------------------------------------------=== # +fn _all[func: fn (String) -> Bool](s: String) -> Bool: + for c in s: + if not func(c): + return False + return True + + fn _toggle_ascii_case(char: UInt8) -> UInt8: """Assuming char is a cased ASCII character, this function will return the opposite-cased letter. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index c1fa107fef..c5d04fb356 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -843,6 +843,11 @@ fn test_isupper() raises: assert_false(isupper(ord("!"))) assert_false(isupper(ord("0"))) + assert_true(isupper("A")) + assert_false(isupper("b")) + assert_true(String("ASDG").isupper()) + assert_false(String("AsDG").isupper()) + fn test_islower() raises: assert_true(islower(ord("a"))) @@ -856,6 +861,12 @@ fn test_islower() raises: assert_false(islower(ord("!"))) assert_false(islower(ord("0"))) + assert_false(islower("A")) + assert_true(islower("b")) + + assert_true(String("asdfg").islower()) + assert_false(String("asdFDg").islower()) + fn test_lower() raises: assert_equal(String("HELLO").lower(), "hello") @@ -883,6 +894,7 @@ fn test_isspace() raises: # checking true cases assert_true(_isspace(ord(" "))) assert_true(_isspace(ord("\n"))) + assert_true(_isspace("\n")) assert_true(_isspace(ord("\t"))) assert_true(_isspace(ord("\r"))) assert_true(_isspace(ord("\v"))) @@ -890,6 +902,7 @@ fn test_isspace() raises: # Checking false cases assert_false(_isspace(ord("a"))) + assert_false(_isspace("a")) assert_false(_isspace(ord("u"))) assert_false(_isspace(ord("s"))) assert_false(_isspace(ord("t"))) @@ -1369,6 +1382,18 @@ def test_isdigit(): assert_false(String("123asdg").isdigit()) +def test_isprintable(): + assert_true(isprintable(ord("a"))) + assert_true(isprintable("a")) + assert_true(isprintable("J")) + + assert_false(isprintable(ord("\n"))) + assert_false(isprintable("\t")) + + assert_true(String("aasdg").isprintable()) + assert_false(String("aa\nae").isprintable()) + + def main(): test_constructors() test_copy() @@ -1417,3 +1442,4 @@ def main(): test_string_iter() test_format_args() test_isdigit() + test_isprintable() From 0ab1c0abb8ea31c3562811d2e340c66a152fb6f1 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sat, 29 Jun 2024 00:37:10 +0300 Subject: [PATCH 1051/2019] [stdlib] Add missing comparison methods to `UInt` I.e. `__le__` and `__ge__`. Also add tests and improve docstrings for the existing `__lt__` and `__gt__`. MODULAR_ORIG_COMMIT_REV_ID: 024883764accd5c31c7a928ba6470e54cd60a3c0 --- stdlib/src/builtin/uint.mojo | 45 +++++++++++++++++++++++++----- stdlib/test/builtin/test_uint.mojo | 19 +++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index a974c013c4..b18d6789e6 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -19,7 +19,7 @@ These are Mojo built-ins, so you don't need to import them. @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct UInt(Stringable, Representable): +struct UInt(Comparable, Representable, Stringable): """This type represents an unsigned integer. An unsigned integer is represents a positive integral number. @@ -589,13 +589,14 @@ struct UInt(Stringable, Representable): @always_inline("nodebug") fn __gt__(self, rhs: UInt) -> Bool: - """Compare this Int to the RHS using GT comparison. + """Return whether this UInt is strictly greater than another. Args: - rhs: The other Int to compare against. + rhs: The other UInt to compare against. Returns: - True if this Int is greater-than the RHS Int and False otherwise. + True if this UInt is greater than the other UInt and False + otherwise. """ return __mlir_op.`index.cmp`[ pred = __mlir_attr.`#index` @@ -603,18 +604,48 @@ struct UInt(Stringable, Representable): @always_inline("nodebug") fn __lt__(self, rhs: UInt) -> Bool: - """Compare this Int to the RHS using LT comparison. + """Return whether this UInt is strictly less than another. Args: - rhs: The other Int to compare against. + rhs: The other UInt to compare against. Returns: - True if this Int is less-than the RHS Int and False otherwise. + True if this UInt is less than the other UInt and False otherwise. """ return __mlir_op.`index.cmp`[ pred = __mlir_attr.`#index` ](self.value, rhs.value) + @always_inline("nodebug") + fn __ge__(self, rhs: UInt) -> Bool: + """Return whether this UInt is greater than or equal to another. + + Args: + rhs: The other UInt to compare against. + + Returns: + True if this UInt is greater than or equal to the other UInt and + False otherwise. + """ + return __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + + @always_inline("nodebug") + fn __le__(self, rhs: UInt) -> Bool: + """Return whether this UInt is less than or equal to another. + + Args: + rhs: The other UInt to compare against. + + Returns: + True if this UInt is less than or equal to the other UInt and False + otherwise. + """ + return __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + @always_inline("nodebug") fn __bool__(self) -> Bool: """Convert this Int to Bool. diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 7ccfa5a43d..83d5c76d97 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -218,6 +218,24 @@ def test_indexer(): assert_equal(UInt(987), UInt(987).__index__()) +def test_comparison(): + assert_true(UInt.__lt__(UInt(1), UInt(7))) + assert_false(UInt.__lt__(UInt(7), UInt(7))) + assert_false(UInt.__lt__(UInt(7), UInt(2))) + + assert_true(UInt.__le__(UInt(1), UInt(7))) + assert_true(UInt.__le__(UInt(7), UInt(7))) + assert_false(UInt.__le__(UInt(7), UInt(2))) + + assert_false(UInt.__gt__(UInt(1), UInt(7))) + assert_false(UInt.__gt__(UInt(7), UInt(7))) + assert_true(UInt.__gt__(UInt(7), UInt(2))) + + assert_false(UInt.__ge__(UInt(1), UInt(7))) + assert_true(UInt.__ge__(UInt(7), UInt(7))) + assert_true(UInt.__ge__(UInt(7), UInt(2))) + + def main(): test_simple_uint() test_uint_representation() @@ -239,3 +257,4 @@ def main(): test_string_conversion() test_int_representation() test_indexer() + test_comparison() From fb70069e94d7129aa457ff804988ff77373bb4b7 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sat, 29 Jun 2024 00:49:34 +0300 Subject: [PATCH 1052/2019] [stdlib] Add `ceildiv` overload for `UInt` Currently, `UInt` cannot opt in to `CeilDivable`, since `__neg__` doesn't make sense for it, and therefore it has to have its own overload with a different implementation. MODULAR_ORIG_COMMIT_REV_ID: 97df617f6e8cac52d52e6c968a6da5802549c9a9 --- stdlib/src/math/math.mojo | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 56c53ca923..0166ea2474 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -116,6 +116,20 @@ fn ceildiv[T: CeilDivableRaising, //](numerator: T, denominator: T) raises -> T: return -(numerator // -denominator) +@always_inline +fn ceildiv(numerator: UInt, denominator: UInt) -> UInt: + """Return the rounded-up result of dividing x by y. + + Args: + numerator: The numerator. + denominator: The denominator. + + Returns: + The ceiling of dividing x by y. + """ + return (numerator + denominator - 1) // denominator + + # ===----------------------------------------------------------------------=== # # trunc # ===----------------------------------------------------------------------=== # From 1b25b43a21a77961b0818b1cfba7bac9610c719f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sat, 29 Jun 2024 01:12:57 +0300 Subject: [PATCH 1053/2019] [stdlib] Add `__pos__` method to `UInt` MODULAR_ORIG_COMMIT_REV_ID: 4723f440520fb812307f8bcadb7662065bb49a45 --- stdlib/src/builtin/uint.mojo | 9 +++++++++ stdlib/test/builtin/test_uint.mojo | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index b18d6789e6..a86fe6570c 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -736,3 +736,12 @@ struct UInt(Comparable, Representable, Stringable): The absolute value. """ return self + + @always_inline("nodebug") + fn __pos__(self) -> UInt: + """Return +self, which is the UInt value itself. + + Returns: + The self value. + """ + return self diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 83d5c76d97..265178adbb 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -236,6 +236,11 @@ def test_comparison(): assert_true(UInt.__ge__(UInt(7), UInt(2))) +def test_pos(): + assert_equal(UInt(2).__pos__(), UInt(2)) + assert_equal(UInt(0).__pos__(), UInt(0)) + + def main(): test_simple_uint() test_uint_representation() @@ -258,3 +263,4 @@ def main(): test_int_representation() test_indexer() test_comparison() + test_pos() From 14e84a46a35cd97d5c9c25ac956e3c334249b16e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sat, 29 Jun 2024 02:09:13 +0300 Subject: [PATCH 1054/2019] [stdlib] Update changelog with new `UInt` type MODULAR_ORIG_COMMIT_REV_ID: 754d5bdfb659f5b67bf2c394800b50a027f0b6fb --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b4a5815a60..4217f061b1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -178,6 +178,10 @@ by [@jayzhan211](https://github.com/jayzhan211)) - The `math` package now includes the `pi`, `e`, and `tau` constants (Closes Issue [#2135](https://github.com/modularml/mojo/issues/2135)). +- Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a + paltform-dependent width. `UInt` implements most arithmethic operations that + make sense for integers, with the notable exception of `__neg__`. + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that From 71ee77e6d0f3e96bedb35aad512e13577466523a Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 28 Jun 2024 19:37:21 -0500 Subject: [PATCH 1055/2019] [stdlib] Fix `bfloat16` formatting on GPU Fixes cases where smaller float values need to be casted to float64 before being passed directly to printf, avoiding an ABI mismatch. MODULAR_ORIG_COMMIT_REV_ID: e41ec7a639df871990a4e41e6c918f90623fb77e --- stdlib/src/builtin/simd.mojo | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 61a3b558f4..72ca70eae6 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1559,7 +1559,14 @@ struct SIMD[type: DType, size: Int]( if triple_is_nvidia_cuda(): @parameter - if type.is_floating_point(): + if ( + type is DType.float16 + or type is DType.bfloat16 + or type is DType.float32 + ): + # We need to cast the value to float64 to print it. + _printf["%g"](element.cast[DType.float64]()) + elif type.is_floating_point(): # get_dtype_printf_format hardcodes 17 digits of precision. _printf["%g"](element) else: From a367137a11868a5ad7d75c388f7d24eb14b3ffeb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 28 Jun 2024 20:28:33 -0500 Subject: [PATCH 1056/2019] [stdlib] Add some new private methods to support internal formatting needs * Formatter._write_int_padded() * Formatter._write_repeated() * Int._decimal_digit_count() MODULAR_ORIG_COMMIT_REV_ID: 8c8a49c0de993726f2494f0570a2fd92d86c30ff --- stdlib/src/builtin/int.mojo | 39 +++++++++++++++++++++++++++++- stdlib/src/builtin/string.mojo | 11 +-------- stdlib/src/utils/_format.mojo | 16 ++++++++++++ stdlib/test/builtin/test_int.mojo | 26 ++++++++++++++++++++ stdlib/test/utils/test_format.mojo | 26 ++++++++++++++++++++ 5 files changed, 107 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 23856d06b3..6916868481 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -22,7 +22,10 @@ from builtin.format_int import _try_write_int from builtin.hash import _hash_simd from builtin.io import _snprintf from builtin.simd import _format_scalar -from builtin.string import _calc_initial_buffer_size +from builtin.string import ( + _calc_initial_buffer_size_int32, + _calc_initial_buffer_size_int64, +) from utils import InlineArray from utils._format import Formattable, Formatter @@ -1126,3 +1129,37 @@ struct Int( The integer modulus of `self` and `rhs` . """ return __mlir_op.`index.rems`(self.value, rhs.value) + + fn _decimal_digit_count(self) -> Int: + """ + Returns the number of decimal digits required to display this integer. + + Note that if this integer is negative, the returned count does not + include space to store a leading minus character. + + Returns: + A count of the number of decimal digits required to display this integer. + + Examples: + + ```mojo + assert_equal(10._decimal_digit_count(), 2) + + assert_equal(-10._decimal_digit_count(), 2) + ``` + . + """ + + var n = abs(self) + + alias is_32bit_system = bitwidthof[DType.index]() == 32 + + @parameter + if is_32bit_system: + return _calc_initial_buffer_size_int32(n) + + # The value only has low-bits. + if n >> 32 == 0: + return _calc_initial_buffer_size_int32(n) + + return _calc_initial_buffer_size_int64(n) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index eaf530004e..19c4b75712 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2379,19 +2379,10 @@ fn _calc_initial_buffer_size_int64(n0: UInt64) -> Int: @always_inline fn _calc_initial_buffer_size(n0: Int) -> Int: - var n = abs(n0) var sign = 0 if n0 > 0 else 1 - alias is_32bit_system = bitwidthof[DType.index]() == 32 # Add 1 for the terminator - @parameter - if is_32bit_system: - return sign + _calc_initial_buffer_size_int32(n) + 1 - - # The value only has low-bits. - if n >> 32 == 0: - return sign + _calc_initial_buffer_size_int32(n) + 1 - return sign + _calc_initial_buffer_size_int64(n) + 1 + return sign + n0._decimal_digit_count() + 1 fn _calc_initial_buffer_size(n: Float64) -> Int: diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index 25dfa6b673..1d6d98b465 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -152,6 +152,22 @@ struct Formatter: args.each[write_arg]() + fn _write_int_padded(inout self, value: Int, *, width: Int): + var int_width = value._decimal_digit_count() + + # TODO: Assumes user wants right-aligned content. + if int_width < width: + self._write_repeated( + " ".as_string_slice(), + width - int_width, + ) + + self.write(value) + + fn _write_repeated(inout self, str: StringSlice, count: Int): + for _ in range(count): + self.write_str(str) + # ===------------------------------------------------------------------=== # # Factory methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index d173a285c9..9f5edcf096 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -162,6 +162,31 @@ def test_bool(): assert_false(Int(0).__as_bool__()) +def test_decimal_digit_count(): + assert_equal(Int(0)._decimal_digit_count(), 1) + assert_equal(Int(1)._decimal_digit_count(), 1) + assert_equal(Int(2)._decimal_digit_count(), 1) + assert_equal(Int(3)._decimal_digit_count(), 1) + assert_equal(Int(9)._decimal_digit_count(), 1) + + assert_equal(Int(10)._decimal_digit_count(), 2) + assert_equal(Int(11)._decimal_digit_count(), 2) + assert_equal(Int(99)._decimal_digit_count(), 2) + + assert_equal(Int(100)._decimal_digit_count(), 3) + assert_equal(Int(101)._decimal_digit_count(), 3) + assert_equal(Int(999)._decimal_digit_count(), 3) + + assert_equal(Int(1000)._decimal_digit_count(), 4) + + assert_equal(Int(-1000)._decimal_digit_count(), 4) + assert_equal(Int(-999)._decimal_digit_count(), 3) + assert_equal(Int(-1)._decimal_digit_count(), 1) + + assert_equal(Int.MAX._decimal_digit_count(), 19) + assert_equal(Int.MIN._decimal_digit_count(), 19) + + def main(): test_properties() test_add() @@ -180,3 +205,4 @@ def main(): test_int_representation() test_indexer() test_bool() + test_decimal_digit_count() diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 5d45382a8d..f76b9f3ec4 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -25,6 +25,8 @@ fn main() raises: test_formatter_of_fixed_string() + test_formatter_write_int_padded() + @value struct Point(Formattable, Stringable): @@ -76,3 +78,27 @@ fn test_formatter_of_fixed_string() raises: var s1_fmt = Formatter(s1) write_to(s1_fmt, "Hello, World!") assert_equal(str(s1), "Hello, World!") + + +fn test_formatter_write_int_padded() raises: + var s1 = String() + var s1_fmt = Formatter(s1) + + s1_fmt._write_int_padded(5, width=5) + + assert_equal(s1, " 5") + + s1_fmt._write_int_padded(123, width=5) + + assert_equal(s1, " 5 123") + + # ---------------------------------- + # Test writing int larger than width + # ---------------------------------- + + var s2 = String() + var s2_fmt = Formatter(s2) + + s2_fmt._write_int_padded(12345, width=3) + + assert_equal(s2, "12345") From d167c56b3f9fc680997e734846a24cc394623eff Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Sat, 29 Jun 2024 05:35:37 +0300 Subject: [PATCH 1057/2019] [stdlib] Add `min` and `max` overload for `UInt` MODULAR_ORIG_COMMIT_REV_ID: a049f70a5566a715eed62b8fe87a6b857ec94e4f --- docs/changelog.md | 4 +++- stdlib/src/builtin/math.mojo | 32 ++++++++++++++++++++++++++++-- stdlib/test/builtin/test_math.mojo | 12 ++++++++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4217f061b1..b886d19acc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -180,7 +180,9 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a paltform-dependent width. `UInt` implements most arithmethic operations that - make sense for integers, with the notable exception of `__neg__`. + make sense for integers, with the notable exception of `__neg__`. Builtin + functions such as `min`/`max`, as well as utilities like `math.ceildiv` are + also implemented for `UInt`. ### 🦋 Changed diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 79d906cfb0..88666264d8 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -160,6 +160,20 @@ fn max(x: Int, y: Int) -> Int: return __mlir_op.`index.maxs`(x.value, y.value) +@always_inline +fn max(x: UInt, y: UInt) -> UInt: + """Gets the maximum of two integers. + + Args: + x: Integer input to max. + y: Integer input to max. + + Returns: + Maximum of x and y. + """ + return __mlir_op.`index.maxu`(x.value, y.value) + + @always_inline fn max[ type: DType, simd_width: Int @@ -195,8 +209,8 @@ fn min(x: Int, y: Int) -> Int: """Gets the minimum of two integers. Args: - x: Integer input to max. - y: Integer input to max. + x: Integer input to min. + y: Integer input to min. Returns: Minimum of x and y. @@ -204,6 +218,20 @@ fn min(x: Int, y: Int) -> Int: return __mlir_op.`index.mins`(x.value, y.value) +@always_inline +fn min(x: UInt, y: UInt) -> UInt: + """Gets the minimum of two integers. + + Args: + x: Integer input to min. + y: Integer input to min. + + Returns: + Minimum of x and y. + """ + return __mlir_op.`index.minu`(x.value, y.value) + + @always_inline fn min[ type: DType, simd_width: Int diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index b1a76eb109..551693a8cc 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -41,9 +41,14 @@ def test_divmod(): def test_min(): + assert_equal(-2, min(-2, -1)) + assert_equal(-1, min(0, -1)) assert_equal(0, min(0, 1)) assert_equal(1, min(1, 42)) + assert_equal(UInt(0), min(UInt(0), UInt(1))) + assert_equal(UInt(1), min(UInt(1), UInt(42))) + var lhs = SIMD[DType.int32, 4](1, 2, 3, 4) var rhs = SIMD[DType.int32, 4](0, 1, 5, 7) var expected = SIMD[DType.int32, 4](0, 1, 3, 4) @@ -52,8 +57,13 @@ def test_min(): def test_max(): + assert_equal(-1, max(-2, -1)) + assert_equal(0, max(0, -1)) assert_equal(1, max(0, 1)) - assert_equal(2, max(1, 2)) + assert_equal(2, max(2, 1)) + + assert_equal(UInt(1), max(UInt(0), UInt(1))) + assert_equal(UInt(2), max(UInt(1), UInt(2))) var lhs = SIMD[DType.int32, 4](1, 2, 3, 4) var rhs = SIMD[DType.int32, 4](0, 1, 5, 7) From 49188a76f92a006fa537bf307d098b14c2aa1b53 Mon Sep 17 00:00:00 2001 From: Fabio Riccardi Date: Sat, 29 Jun 2024 11:52:40 -0700 Subject: [PATCH 1058/2019] [stdlib] Moved locks.mojo to open-source/mojo/stdlib/stdlib/utils MODULAR_ORIG_COMMIT_REV_ID: b862af676aff17b64a20ee451aab83da0981ae8f --- stdlib/src/utils/__init__.mojo | 1 + stdlib/src/utils/lock.mojo | 143 ++++++++++++++++++++++++++++++ stdlib/test/utils/test_locks.mojo | 81 +++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 stdlib/src/utils/lock.mojo create mode 100644 stdlib/test/utils/test_locks.mojo diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 6894eec99c..dd858c531c 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -20,3 +20,4 @@ from .static_tuple import InlineArray, StaticTuple from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant +from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo new file mode 100644 index 0000000000..8608a430d4 --- /dev/null +++ b/stdlib/src/utils/lock.mojo @@ -0,0 +1,143 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from os import Atomic +from time import sleep + + +# ===----------------------------------------------------------------------===# +# SpinWaiter +# ===----------------------------------------------------------------------===# + + +struct SpinWaiter: + """A proxy for the C++ runtime's SpinWaiter type.""" + + var storage: Pointer[NoneType] + """Pointer to the underlying SpinWaiter instance.""" + + fn __init__(inout self: Self): + """Initializes a SpinWaiter instance.""" + self.storage = external_call[ + "KGEN_CompilerRT_LLCL_InitializeSpinWaiter", + Pointer[NoneType], + ]() + + fn __del__(owned self: Self): + """Destroys the SpinWaiter instance.""" + external_call["KGEN_CompilerRT_LLCL_DestroySpinWaiter", NoneType]( + self.storage + ) + + fn wait(self: Self): + """Blocks the current task for a duration determined by the underlying + policy.""" + external_call["KGEN_CompilerRT_LLCL_SpinWaiter_Wait", NoneType]( + self.storage + ) + + +struct BlockingSpinLock: + """A basic locking implementation that uses an integer to represent the + owner of the lock.""" + + alias UNLOCKED = -1 + """non-zero means locked, -1 means unlocked.""" + + var counter: Atomic[DType.int64] + """The atomic counter implementing the spin lock.""" + + fn __init__(inout self: Self): + """Default constructor.""" + + self.counter = Atomic[DType.int64](Self.UNLOCKED) + + fn lock(inout self: Self, owner: Int): + """Acquires the lock. + + Args: + owner: The lock's owner (usually an address). + """ + + var expected = Int64(Self.UNLOCKED) + var waiter = SpinWaiter() + while not self.counter.compare_exchange_weak(expected, owner): + # this should be yield + waiter.wait() + expected = Self.UNLOCKED + + fn unlock(inout self: Self, owner: Int) -> Bool: + """Releases the lock. + + Args: + owner: The lock's owner (usually an address). + + Returns: + The successful release of the lock. + """ + + var expected = Int64(owner) + if self.counter.load() != owner: + # No one else can modify other than owner + return False + while not self.counter.compare_exchange_weak(expected, Self.UNLOCKED): + expected = owner + return True + + +struct BlockingScopedLock: + """A scope adapter for BlockingSpinLock.""" + + alias LockType = BlockingSpinLock + """The type of the lock.""" + + var lock: UnsafePointer[Self.LockType] + """The underlying lock instance.""" + + fn __init__( + inout self, + lock: UnsafePointer[Self.LockType], + ): + """Primary constructor. + + Args: + lock: A pointer to the underlying lock. + """ + + self.lock = lock + + fn __init__( + inout self, + inout lock: Self.LockType, + ): + """Secondary constructor. + + Args: + lock: A mutable reference to the underlying lock. + """ + + self.lock = UnsafePointer.address_of(lock) + + @no_inline + fn __enter__(inout self): + """Acquire the lock on entry. + This is done by setting the owner of the lock to own address.""" + var address = UnsafePointer[Self].address_of(self) + self.lock[].lock(int(address)) + + @no_inline + fn __exit__(inout self): + """Release the lock on exit. + Reset the address on the underlying lock.""" + var address = UnsafePointer[Self].address_of(self) + _ = self.lock[].unlock(int(address)) diff --git a/stdlib/test/utils/test_locks.mojo b/stdlib/test/utils/test_locks.mojo new file mode 100644 index 0000000000..e135f7b55b --- /dev/null +++ b/stdlib/test/utils/test_locks.mojo @@ -0,0 +1,81 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s | FileCheck %s + + +from os import Atomic +from time import now, sleep, time_function + +from utils.lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock +from runtime.llcl import TaskGroup +from testing import assert_equal, assert_true + + +# CHECK-LABEL: test_spin_waiter +def test_spin_waiter(): + print("== test_spin_waiter") + var waiter = SpinWaiter() + alias RUNS = 1000 + for i in range(RUNS): + waiter.wait() + assert_true(True) + + +fn test_basic_lock() raises: + var lock = BlockingSpinLock() + var rawCounter = 0 + var counter = Atomic[DType.int64](False) + alias maxI = 100 + alias maxJ = 100 + + @parameter + async fn inc() capturing: + with BlockingScopedLock(lock): + rawCounter += 1 + _ = counter.fetch_add(1) + + # CHECK: PRE::Atomic counter is 0 , and raw counter, 0 + print( + "PRE::Atomic counter is ", + counter.load(), + ", and raw counter, ", + rawCounter, + ) + + @parameter + fn test_atomic() capturing -> None: + var tg = TaskGroup[__lifetime_of()]() + for i in range(0, maxI): + for j in range(0, maxJ): + tg.create_task(inc()) + tg.wait() + + var time_ns = time_function[test_atomic]() + _ = lock^ + # print("Total time taken ", time_ns / (1_000_000_000), " s") + + # CHECK: POST::Atomic counter is 10000 , and raw counter, 10000 + print( + "POST::Atomic counter is ", + counter.load(), + ", and raw counter, ", + rawCounter, + ) + assert_equal(counter.load(), rawCounter, "atomic stress test failed") + + return + + +def main(): + test_spin_waiter() + test_basic_lock() From 4fcf78e3ce9b5785db4a77bfbca2d2e877f12b1e Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Sat, 29 Jun 2024 14:13:54 -0500 Subject: [PATCH 1059/2019] [stdlib] Add style guide code headers suggestion for struct aliases Also adjusts to specifically mention a header for fields. Small, simple types generally don't _have_ to include a fields header, but if a type also defines associated aliases, it helps readability to separate them clearly from the fields. MODULAR_ORIG_COMMIT_REV_ID: da9ff4fc8d1cd84174152195ae79eccef0fd8402 --- stdlib/docs/style-guide.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 6140f2342a..d743a0171c 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -119,7 +119,16 @@ defined on structs. struct MyStruct(Sized, Stringable): """Description goes here.""" + # ===-------------------------------------------------------------------===# + # Aliases + # ===-------------------------------------------------------------------===# + + alias factor = 5 + + # ===-------------------------------------------------------------------===# # Fields + # ===-------------------------------------------------------------------===# + var field: Int # ===-------------------------------------------------------------------===# @@ -160,6 +169,8 @@ struct MyStruct(Sized, Stringable): fn __len__ fn __str__ + fn __abs__ + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# From 00a12e723d4bf4c57bc97949c09028c098b5ea1f Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 29 Jun 2024 14:53:27 -0500 Subject: [PATCH 1060/2019] [External] [stdlib] Add the trait `CollectionElementNew` to `List` (#42499) [External] [stdlib] Add the trait `CollectionElementNew` to `List` At first I wanted to make a second constructor but it was not possible because of this bug: * https://github.com/modularml/mojo/issues/3137 so in the end, I replaced the existing one. Hopefully end users won't get too many surprises with that, as it can change the type of the result when doing things like `List(List[Int]())` since it uses then the constructor with variadic arguments. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3138 MODULAR_ORIG_COMMIT_REV_ID: f2e100d916ae7cefc49933583e390502ab732522 --- stdlib/src/collections/list.mojo | 18 ++++++++++-------- stdlib/test/collections/test_list.mojo | 8 ++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index f4e33ef95f..0da6eba125 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -77,7 +77,9 @@ struct _ListIter[ return self.index -struct List[T: CollectionElement](CollectionElement, Sized, Boolable): +struct List[T: CollectionElement]( + CollectionElement, CollectionElementNew, Sized, Boolable +): """The `List` type is a dynamically-allocated list. It supports pushing and popping from the back resizing the underlying @@ -105,14 +107,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.size = 0 self.capacity = 0 - fn __init__(inout self, existing: Self): + fn __init__(inout self, *, other: Self): """Creates a deep copy of the given list. Args: - existing: The list to copy. + other: The list to copy. """ - self.__init__(capacity=existing.capacity) - for e in existing: + self.__init__(capacity=other.capacity) + for e in other: self.append(e[]) fn __init__(inout self, *, capacity: Int): @@ -237,7 +239,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # avoid the copy since it would be cleared immediately anyways if x == 0: return Self() - var result = List(self) + var result = List(other=self) result.__mul(x) return result^ @@ -260,7 +262,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): Returns: The newly created list. """ - var result = List(self) + var result = List(other=self) result.extend(other^) return result^ @@ -455,7 +457,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if x == 0: self.clear() return - var orig = List(self) + var orig = List(other=self) self.reserve(len(self) * x) for i in range(x - 1): self.extend(orig) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index c8d8f5daa9..6f5a8a7fb7 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -526,7 +526,7 @@ def test_2d_dynamic_list(): def test_list_explicit_copy_using_get_ref(): var list = List[CopyCounter]() list.append(CopyCounter()) - var list_copy = List(list) + var list_copy = List(other=list) assert_equal(0, list.__get_ref(0)[].copy_count) assert_equal(1, list_copy.__get_ref(0)[].copy_count) @@ -534,7 +534,7 @@ def test_list_explicit_copy_using_get_ref(): for i in range(10): l2.append(i) - var l2_copy = List(l2) + var l2_copy = List(other=l2) assert_equal(len(l2), len(l2_copy)) for i in range(len(l2)): assert_equal(l2[i], l2_copy[i]) @@ -543,7 +543,7 @@ def test_list_explicit_copy_using_get_ref(): def test_list_explicit_copy(): var list = List[CopyCounter]() list.append(CopyCounter()) - var list_copy = List(list) + var list_copy = List(other=list) assert_equal(0, list[0].copy_count) assert_equal(1, list_copy[0].copy_count) @@ -551,7 +551,7 @@ def test_list_explicit_copy(): for i in range(10): l2.append(i) - var l2_copy = List(l2) + var l2_copy = List(other=l2) assert_equal(len(l2), len(l2_copy)) for i in range(len(l2)): assert_equal(l2[i], l2_copy[i]) From 0c338ec4d23609809eda58fd03df914e10c541ba Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 29 Jun 2024 15:18:05 -0500 Subject: [PATCH 1061/2019] [External] [stdlib] Add `CollectionElementNew` trait to `Dict` and `DictEntry` (#42498) [External] [stdlib] Add `CollectionElementNew` trait to `Dict` and `DictEntry` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3136 MODULAR_ORIG_COMMIT_REV_ID: 29eb294785f5568bb4e87a59e7f5c7bab7241f05 --- stdlib/src/collections/dict.mojo | 30 ++++++++++++++++++-------- stdlib/test/collections/test_dict.mojo | 8 +++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index dcdcdbca2e..491fc71985 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -177,7 +177,9 @@ struct _DictValueIter[ @value -struct DictEntry[K: KeyElement, V: CollectionElement](CollectionElement): +struct DictEntry[K: KeyElement, V: CollectionElement]( + CollectionElement, CollectionElementNew +): """Store a key-value pair entry inside a dictionary. Parameters: @@ -203,6 +205,16 @@ struct DictEntry[K: KeyElement, V: CollectionElement](CollectionElement): self.key = key^ self.value = value^ + fn __init__(inout self, *, other: Self): + """Copy an existing entry. + + Args: + other: The existing entry to copy. + """ + self.hash = other.hash + self.key = other.key + self.value = other.value + alias _EMPTY = -1 alias _REMOVED = -2 @@ -301,7 +313,7 @@ struct _DictIndex: struct Dict[K: KeyElement, V: CollectionElement]( - Sized, CollectionElement, Boolable + Sized, CollectionElement, CollectionElementNew, Boolable ): """A container that stores key-value pairs. @@ -439,16 +451,16 @@ struct Dict[K: KeyElement, V: CollectionElement]( return len(self._entries) @always_inline - fn __init__(inout self, existing: Self): + fn __init__(inout self, *, other: Self): """Copy an existing dictiontary. Args: - existing: The existing dict. + other: The existing dict. """ - self.size = existing.size - self._n_entries = existing._n_entries - self._index = existing._index.copy(existing._reserved()) - self._entries = existing._entries + self.size = other.size + self._n_entries = other._n_entries + self._index = other._index.copy(other._reserved()) + self._entries = other._entries @staticmethod fn fromkeys(keys: List[K], value: V) -> Self: @@ -592,7 +604,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( Returns: The result of the merge. """ - var result = Dict(self) + var result = Dict(other=self) result.update(other) return result^ diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 2bbf739790..3bc94c51b4 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -238,7 +238,7 @@ def test_dict_copy(): orig["a"] = 1 # test values copied to new Dict - var copy = Dict(orig) + var copy = Dict(other=orig) assert_equal(1, copy["a"]) # test there are two copies of dict and @@ -253,7 +253,7 @@ def test_dict_copy_delete_original(): orig["a"] = 1 # test values copied to new Dict - var copy = Dict(orig) + var copy = Dict(other=orig) # don't access the original dict, anymore, confirm that # deleting the original doesn't violate the integrity of the copy assert_equal(1, copy["a"]) @@ -264,7 +264,7 @@ def test_dict_copy_add_new_item(): orig["a"] = 1 # test values copied to new Dict - var copy = Dict(orig) + var copy = Dict(other=orig) assert_equal(1, copy["a"]) # test there are two copies of dict and @@ -278,7 +278,7 @@ def test_dict_copy_calls_copy_constructor(): orig["a"] = CopyCounter() # test values copied to new Dict - var copy = Dict(orig) + var copy = Dict(other=orig) # I _may_ have thoughts about where our performance issues # are coming from :) assert_equal(1, orig["a"].copy_count) From 1a8774d037d847845bc18c1705a11da71157b0a8 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Sat, 29 Jun 2024 15:21:37 -0500 Subject: [PATCH 1062/2019] [External] [stdlib] Make `Slice` `Representable` and some other housekeeping. (#42497) [External] [stdlib] Make `Slice` `Representable` and some other housekeeping. - Make `Slice` `Representable` - add `format_to` to `Slice` - Make `Slice` string representation match Python - Remove `__getitem__`, which was done previously for `SliceNew` in #2963 Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3064 MODULAR_ORIG_COMMIT_REV_ID: 460b2c1529a1221b43fdb4e4ad02f76efa3a1f14 --- stdlib/src/builtin/builtin_slice.mojo | 63 +++++++++++++++++---------- stdlib/test/builtin/test_slice.mojo | 31 ++++++------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 09dd306471..a9c52fbd4b 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -27,7 +27,7 @@ fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: @register_passable("trivial") -struct Slice(Stringable, EqualityComparable): +struct Slice(Stringable, EqualityComparable, Representable, Formattable): """Represents a slice expression. Objects of this type are generated when slice syntax is used within square @@ -85,13 +85,40 @@ struct Slice(Stringable, EqualityComparable): Returns: The string representation of the span. """ - var res = str(self.start.value()) if self.start else "" - res += ":" - if self.end: - res += str(self.end.value()) if self.end else "" - res += ":" - res += str(self.step) - return res + var output = String() + var writer = output._unsafe_to_formatter() + self.format_to(writer) + return output + + fn __repr__(self) -> String: + """Gets the string representation of the span. + + Returns: + The string representation of the span. + """ + return self.__str__() + + fn format_to(self, inout writer: Formatter): + """Write Slice string representation to a `Formatter`. + + Args: + writer: The formatter to write to. + """ + + @parameter + fn write_optional(opt: OptionalReg[Int]): + if opt: + writer.write(repr(opt.value())) + else: + writer.write(repr(None)) + + writer.write("slice(") + write_optional(self.start) + writer.write(", ") + write_optional(self.end) + writer.write(", ") + writer.write(repr(self.step)) + writer.write(")") @always_inline("nodebug") fn __eq__(self, other: Self) -> Bool: @@ -135,18 +162,6 @@ struct Slice(Stringable, EqualityComparable): return len(range(self.start.value(), self.end.value(), self.step)) - @always_inline - fn __getitem__(self, idx: Int) -> Int: - """Get the slice index. - - Args: - idx: The index. - - Returns: - The slice index. - """ - return self.start.value() + idx * self.step - fn indices(self, length: Int) -> (Int, Int, Int): """Returns a tuple of 3 intergers representing the start, end, and step of the slice if applied to a container of the given length. @@ -179,9 +194,11 @@ struct Slice(Stringable, EqualityComparable): Returns: A tuple containing three integers for start, end, and step. """ + var step = self.step + var start = self.start var end = self.end - var positive_step = self.step > 0 + var positive_step = step > 0 if not start: start = 0 if positive_step else length - 1 @@ -201,7 +218,7 @@ struct Slice(Stringable, EqualityComparable): elif end.value() >= length: end = length if positive_step else length - 1 - return (start.value(), end.value(), self.step) + return (start.value(), end.value(), step) @always_inline("nodebug") @@ -214,7 +231,7 @@ fn slice(end: Int) -> Slice: Returns: The constructed slice. """ - return Slice(0, end) + return Slice(None, end, None) @always_inline("nodebug") diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index c2f5e2a593..fd736e58ea 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -72,28 +72,33 @@ struct SliceStringable: def test_slice_stringable(): var s = SliceStringable() - assert_equal(s[2::-1], "2::-1") - assert_equal(s[1:-1:2], "1:-1:2") - assert_equal(s[:-1], ":-1:1") + assert_equal(s[2::-1], "slice(2, None, -1)") + assert_equal(s[1:-1:2], "slice(1, -1, 2)") + assert_equal(s[:-1], "slice(None, -1, 1)") + assert_equal(s[::], "slice(None, None, 1)") + assert_equal(s[::4], "slice(None, None, 4)") + assert_equal(repr(slice(None, 2, 3)), "slice(None, 2, 3)") + assert_equal(repr(slice(10)), "slice(None, 10, 1)") def test_slice_eq(): assert_equal(slice(1, 2, 3), slice(1, 2, 3)) - assert_equal(slice(0, 1), slice(1)) + assert_equal(slice(None, 1, None), slice(1)) assert_true(slice(2, 3) != slice(4, 5)) - assert_equal(slice(1, None, 1), slice(1, None, None)) + assert_equal(slice(1, None, None), slice(1, None, None)) + assert_equal(slice(1, 2), slice(1, 2, None)) -def test_slice_adjust(): +def test_slice_indices(): var start: Int var end: Int var step: Int var s = slice(1, 10) start, end, step = s.indices(9) - assert_equal(slice(start, end, step), slice(1, 9)) + assert_equal(slice(start, end, step), slice(1, 9, 1)) s = slice(1, None, 1) start, end, step = s.indices(5) - assert_equal(slice(start, end, step), slice(1, 5)) + assert_equal(slice(start, end, step), slice(1, 5, 1)) s = slice(1, None, -1) start, end, step = s.indices(5) assert_equal(slice(start, end, step), slice(1, -1, -1)) @@ -130,17 +135,9 @@ def test_slice_adjust(): # assert_equal(len(range(start, end, step)), 0) -def test_indexing(): - var s = slice(1, 10) - assert_equal(s[True], 2) - assert_equal(s[int(0)], 1) - assert_equal(s[2], 3) - - def main(): test_none_end_folds() test_slicable() test_slice_stringable() - test_indexing() test_slice_eq() - test_slice_adjust() + test_slice_indices() From 5bdfa6c7b50dd2bca0a73390af7a6dad25c8437f Mon Sep 17 00:00:00 2001 From: Helehex Date: Sat, 29 Jun 2024 15:33:12 -0500 Subject: [PATCH 1063/2019] [External] [stdlib] Fix `String.removesuffix("")` removing entire string. (#42239) [External] [stdlib] Fix `String.removesuffix("")` removing entire string. A fix to #3117 Co-authored-by: Helehex Closes modularml/mojo#3118 MODULAR_ORIG_COMMIT_REV_ID: 22f22e0ec077e0a215417607987807b694652605 --- stdlib/src/builtin/string.mojo | 2 +- stdlib/test/builtin/test_string.mojo | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 19c4b75712..0e52f3bf2e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2154,7 +2154,7 @@ struct String( `string[:-len(suffix)]` if the string ends with the suffix string, or a copy of the original string otherwise. """ - if self.endswith(suffix): + if suffix and self.endswith(suffix): return self[: -len(suffix)] return self diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index c5d04fb356..a66163d6d1 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -1134,6 +1134,7 @@ fn test_endswith() raises: def test_removeprefix(): + assert_equal(String("hello world").removeprefix(""), String("hello world")) assert_equal(String("hello world").removeprefix("hello"), " world") assert_equal(String("hello world").removeprefix("world"), "hello world") assert_equal(String("hello world").removeprefix("hello world"), "") @@ -1141,6 +1142,7 @@ def test_removeprefix(): def test_removesuffix(): + assert_equal(String("hello world").removesuffix(""), String("hello world")) assert_equal(String("hello world").removesuffix("world"), "hello ") assert_equal(String("hello world").removesuffix("hello"), "hello world") assert_equal(String("hello world").removesuffix("hello world"), "") From 9d191dbc52edc5072425464237434f91360d0a53 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Sat, 29 Jun 2024 15:34:58 -0500 Subject: [PATCH 1064/2019] [External] [stdlib] add `__str__` and `__repr__` for `Optional` (#42453) [External] [stdlib] add `__str__` and `__repr__` for `Optional` Add `format_to` `__str__` and `__repr__` Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3061 MODULAR_ORIG_COMMIT_REV_ID: a72f9f9aecd83a069f585cc470e88f573c7d01d6 --- stdlib/src/collections/optional.mojo | 50 ++++++++++++++++++++++ stdlib/test/collections/test_optional.mojo | 9 ++++ 2 files changed, 59 insertions(+) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index f68153c62e..818f8f4982 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -164,6 +164,56 @@ struct Optional[T: CollectionElement]( """ return not self + fn __str__[U: RepresentableCollectionElement](self: Optional[U]) -> String: + """Return the string representation of the value of the Optional. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Representable` and `CollectionElement`. + + Returns: + A string represenation of the Optional. + """ + var output = String() + var writer = output._unsafe_to_formatter() + self.format_to(writer) + return output + + # TODO: Include the Parameter type in the string as well. + fn __repr__[U: RepresentableCollectionElement](self: Optional[U]) -> String: + """Returns the verbose string representation of the Optional. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Representable` and `CollectionElement`. + + Returns: + A verbose string representation of the Optional. + """ + var output = String() + var writer = output._unsafe_to_formatter() + write_to(writer, "Optional(") + self.format_to(writer) + write_to(writer, ")") + return output + + fn format_to[ + U: RepresentableCollectionElement + ](self: Optional[U], inout writer: Formatter): + """Write Optional string representation to a `Formatter`. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Representable` and `CollectionElement`. + + Args: + writer: The formatter to write to. + """ + if self: + write_to(writer, repr(self.value())) + else: + write_to(writer, "None") + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 04929e6efd..a4e7e34062 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -134,6 +134,14 @@ def test_optional_explicit_copy(): assert_equal(v2.value(), "testing") +def test_optional_str_repr(): + var o = Optional(10) + assert_equal(o.__str__(), "10") + assert_equal(o.__repr__(), "Optional(10)") + assert_equal(Optional[Int](None).__str__(), "None") + assert_equal(Optional[Int](None).__repr__(), "Optional(None)") + + def main(): test_basic() test_optional_reg_basic() @@ -143,3 +151,4 @@ def main(): test_optional_reg_isnot() test_optional_take_mutates() test_optional_explicit_copy() + test_optional_str_repr() From cd512281dc1f3d420b4ef2f964204140d9123b3a Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Sat, 29 Jun 2024 16:25:21 -0500 Subject: [PATCH 1065/2019] [External] [stdlib] Add `fill` method to `Span` (#42237) [External] [stdlib] Add `fill` method to `Span` This PR tried to round out `Span` a little more by adding a `fill` method. Precedents are [std::fill](https://en.cppreference.com/w/cpp/algorithm/fill) and Rust's [slice fill](https://doc.rust-lang.org/std/primitive.slice.html#method.fill). Co-authored-by: Lukas Hermann Closes modularml/mojo#3110 MODULAR_ORIG_COMMIT_REV_ID: 16e6c1bb95b7a78eca2871b02f6ca90f7c8beb8e --- stdlib/src/utils/span.mojo | 13 +++++++++++++ stdlib/test/utils/test_span.mojo | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 97237b90ca..5a980bedf2 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -297,3 +297,16 @@ struct Span[ """ constrained[_type_is_eq[T, T2](), "T must be equal to T2"]() return not self == rhs + + fn fill[lifetime: MutableLifetime](self: Span[T, lifetime], value: T): + """ + Fill the memory that a span references with a given value. + + Parameters: + lifetime: The inferred mutable lifetime of the data within the Span. + + Args: + value: The value to assign to each element. + """ + for element in self: + element[] = value diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 7463d9c68e..c7086f5b31 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -176,6 +176,17 @@ def test_equality(): assert_true(sp[0:0] == sp3[0:0]) +def test_fill(): + var a = List[Int](0, 1, 2, 3, 4, 5, 6, 7, 8) + var s = Span(a) + + s.fill(2) + + for i in range(len(a)): + assert_equal(a[i], 2) + assert_equal(s[i], 2) + + def main(): test_span_list_int() test_span_list_str() @@ -185,3 +196,4 @@ def main(): test_span_slice() test_equality() test_bool() + test_fill() From 9f36bd204d487380410f13bcfecee24a24234220 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Sat, 29 Jun 2024 16:26:40 -0500 Subject: [PATCH 1066/2019] [External] [stdlib] Add `SIMD.__contains__` (#42557) [External] [stdlib] Add `SIMD.__contains__` Add `SIMD.__contains__` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3104 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3104 MODULAR_ORIG_COMMIT_REV_ID: 697ab71163dc1ef42693f9765de189206faeb5d6 --- stdlib/src/builtin/simd.mojo | 11 +++++++++++ stdlib/test/builtin/test_simd.mojo | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 72ca70eae6..36629578e3 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -505,6 +505,17 @@ struct SIMD[type: DType, size: Int]( self.value, val.value, index(idx).value ) + fn __contains__(self, value: Scalar[type]) -> Bool: + """Whether the vector contains the value. + + Args: + value: The value. + + Returns: + Whether the vector contains the value. + """ + return (self == value).reduce_or() + @always_inline("nodebug") fn __add__(self, rhs: Self) -> Self: """Computes `self + rhs`. diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index e496e10697..6259e92847 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1547,6 +1547,15 @@ def test_split(): assert_equal(tup[1], SIMD[DType.index, 4](5, 6, 7, 8)) +def test_contains(): + var x = SIMD[DType.int8, 4](1, 2, 3, 4) + assert_true(1 in x and 2 in x and 3 in x and 4 in x) + assert_false(0 in x or 5 in x) + var y = SIMD[DType.float16, 4](1, 2, 3, 4) + assert_true(1 in y and 2 in y and 3 in y and 4 in y) + assert_false(0 in y or 5 in y) + + def main(): test_abs() test_add() @@ -1596,4 +1605,5 @@ def main(): test_truthy() test_modf() test_split() + test_contains() # TODO: add tests for __and__, __or__, anc comparison operators From 79491198ba38f30e608f57c01dd15e6ebb5ec8f9 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 29 Jun 2024 17:02:48 -0500 Subject: [PATCH 1067/2019] [External] [stdlib] Add `CollectionElementNew` to a few structs, part 1 (#42561) [External] [stdlib] Add `CollectionElementNew` to a few structs, part 1 When switching all collection to `CollectionElementNew` that requires that many structs used throughout the stdlib implement this trait too. This PR modifies `DType`, `Error`, `_NoneMarker`, `_ObjectImpl` and `StringLiteral` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3135 MODULAR_ORIG_COMMIT_REV_ID: 0acafc226c075d76901c7b0e6538bb9a8ea04123 --- stdlib/src/builtin/dtype.mojo | 13 ++++++++++++- stdlib/src/builtin/error.mojo | 20 +++++++++++++++++++- stdlib/src/builtin/object.mojo | 14 ++++++++++++-- stdlib/src/builtin/string_literal.mojo | 10 ++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 5b9d4b649d..52f118e3e4 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -27,7 +27,9 @@ alias _mIsFloat = UInt8(1 << 6) @value @register_passable("trivial") -struct DType(Stringable, Formattable, Representable, KeyElement): +struct DType( + Stringable, Formattable, Representable, KeyElement, CollectionElementNew +): """Represents DType and provides methods for working with it.""" alias type = __mlir_type.`!kgen.dtype` @@ -83,6 +85,15 @@ struct DType(Stringable, Formattable, Representable, KeyElement): of the hardware's pointer type (32-bit on 32-bit machines and 64-bit on 64-bit machines).""" + @always_inline + fn __init__(inout self, *, other: Self): + """Copy this DType. + + Args: + other: The DType to copy. + """ + self = other + @always_inline("nodebug") fn __str__(self) -> String: """Gets the name of the DType. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index d64bfbeedb..4c582bdd8a 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -26,7 +26,14 @@ from memory.memory import _free @register_passable -struct Error(Stringable, Boolable, Representable, Formattable): +struct Error( + Stringable, + Boolable, + Representable, + Formattable, + CollectionElement, + CollectionElementNew, +): """This type represents an Error.""" var data: UnsafePointer[UInt8] @@ -104,6 +111,17 @@ struct Error(Stringable, Boolable, Representable, Formattable): dest[length] = 0 return Error {data: dest, loaded_length: -length} + fn __init__(*, other: Self) -> Self: + """Copy the object. + + Args: + other: The value to copy. + + Returns: + The copied `Error`. + """ + return other + fn __del__(owned self): """Releases memory if allocated.""" if self.loaded_length < 0: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 80928e5c5d..f98819640d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -28,10 +28,11 @@ from utils import StringRef, Variant, unroll @register_passable("trivial") -struct _NoneMarker: +struct _NoneMarker(CollectionElementNew): """This is a trivial class to indicate that an object is `None`.""" - pass + fn __init__(inout self, *, other: Self): + pass @register_passable("trivial") @@ -316,6 +317,15 @@ struct _ObjectImpl(CollectionElement, Stringable): fn __init__(inout self, value: _RefCountedAttrsDictRef): self.value = Self.type(value) + @always_inline + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self = other.value + @always_inline fn __copyinit__(inout self, existing: Self): self = existing.value diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 11db3af8e2..c3d0ab1128 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -35,6 +35,7 @@ from .string import _atol struct StringLiteral( Boolable, Comparable, + CollectionElementNew, Formattable, IntableRaising, KeyElement, @@ -68,6 +69,15 @@ struct StringLiteral( """ self.value = value + @always_inline("nodebug") + fn __init__(inout self, *, other: Self): + """Copy constructor. + + Args: + other: The string literal to copy. + """ + self = other + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# From f87d2ce9e52393c919504cf523516bdbb24546ad Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 29 Jun 2024 17:03:34 -0500 Subject: [PATCH 1068/2019] [External] [stdlib] Add `CollectionElementNew` to a few structs, part 2 (#42562) [External] [stdlib] Add `CollectionElementNew` to a few structs, part 2 That should make the final transition of our collections to CollectionElementNew easier. I can see that the trait was needed for those structs. See the global PR here: https://github.com/modularml/mojo/pull/3134 This PR adds the explicit copy constructor to `Arc`, `UnsafePointer`, `DTypePointer` and `LegacyPointer` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3139 MODULAR_ORIG_COMMIT_REV_ID: 4441af9cf3139a261dfb0af5d7e9011c46ba68a0 --- stdlib/src/memory/arc.mojo | 11 ++++++++- stdlib/src/memory/unsafe.mojo | 28 +++++++++++++++++++++- stdlib/src/memory/unsafe_pointer.mojo | 13 ++++++++++ stdlib/test/memory/test_arc.mojo | 16 +++++++++++++ stdlib/test/memory/test_memory.mojo | 9 +++++++ stdlib/test/memory/test_unsafepointer.mojo | 9 +++++++ 6 files changed, 84 insertions(+), 2 deletions(-) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 390ca81f14..6a78d5b44d 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -70,7 +70,7 @@ struct _ArcInner[T: Movable]: @register_passable -struct Arc[T: Movable](CollectionElement): +struct Arc[T: Movable](CollectionElement, CollectionElementNew): """Atomic reference-counted pointer. This smart pointer owns an instance of `T` indirectly managed on the heap. @@ -101,6 +101,15 @@ struct Arc[T: Movable](CollectionElement): value^ ) + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + other._inner[].add_ref() + self._inner = other._inner + fn __copyinit__(inout self, existing: Self): """Copy an existing reference. Increment the refcount to the object. diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index ae2d3a353e..045628ce6f 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -140,7 +140,14 @@ alias Pointer = LegacyPointer @register_passable("trivial") struct LegacyPointer[ type: AnyTrivialRegType, address_space: AddressSpace = AddressSpace.GENERIC -](Boolable, CollectionElement, Intable, Stringable, EqualityComparable): +]( + Boolable, + CollectionElement, + CollectionElementNew, + Intable, + Stringable, + EqualityComparable, +): """Defines a LegacyPointer struct that contains the address of a register passable type. @@ -167,6 +174,17 @@ struct LegacyPointer[ """ return __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] + fn __init__(*, other: Self) -> Self: + """Copy the object. + + Args: + other: The value to copy. + + Returns: + Constructed LegacyPointer object. + """ + return other + @always_inline("nodebug") fn __init__(address: Self._mlir_type) -> Self: """Constructs a LegacyPointer from the address. @@ -569,6 +587,14 @@ struct DTypePointer[ self.address = Self._pointer_type() + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self = other + @always_inline("nodebug") fn __init__( inout self, diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 35b7955fe0..ef4cb99358 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -34,6 +34,7 @@ struct UnsafePointer[ ]( ImplicitlyBoolable, CollectionElement, + CollectionElementNew, Stringable, Formattable, Intable, @@ -100,6 +101,18 @@ struct UnsafePointer[ ) } + @always_inline + fn __init__(*, other: Self) -> Self: + """Copy the object. + + Args: + other: The value to copy. + + Returns: + A copy of the object. + """ + return Self {address: other.address} + # ===-------------------------------------------------------------------===# # Factory methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 57fe268ee0..fea5c15c41 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -48,6 +48,22 @@ def test_deleter_not_called_until_no_references(): assert_true(deleted) +def test_deleter_not_called_until_no_references_explicit_copy(): + var deleted = False + var p = Arc(ObservableDel(UnsafePointer.address_of(deleted))) + var p2 = Arc(other=p) + _ = p^ + assert_false(deleted) + + var vec = List[Arc[ObservableDel]]() + vec.append(Arc(other=p2)^) + _ = p2^ + assert_false(deleted) + _ = vec^ + assert_true(deleted) + + def main(): test_basic() test_deleter_not_called_until_no_references() + test_deleter_not_called_until_no_references_explicit_copy() diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index c6105d2c2a..f3aa2d65ca 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -330,6 +330,14 @@ def test_dtypepointer_string(): ptr.free() +def test_pointer_explicit_copy(): + var ptr = Pointer[Int].alloc(1) + ptr[] = 42 + var copy = Pointer(other=ptr) + assert_equal(copy[], 42) + ptr.free() + + def test_pointer_refitem(): var ptr = Pointer[Int].alloc(1) ptr[] = 42 @@ -534,6 +542,7 @@ def main(): test_memcpy_unsafe_pointer() test_memset() + test_pointer_explicit_copy() test_dtypepointer_string() test_pointer_refitem() test_pointer_refitem_string() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index e0e9e7e7f9..d0563c4993 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -115,6 +115,14 @@ def test_address_of(): _ = local +def test_explicit_copy_of_pointer_address(): + var local = 1 + var ptr = UnsafePointer[Int].address_of(local) + var copy = UnsafePointer(other=ptr) + assert_equal(int(ptr), int(copy)) + _ = local + + def test_bitcast(): var local = 1 var ptr = UnsafePointer[Int].address_of(local) @@ -238,6 +246,7 @@ def main(): test_unsafepointer_move_pointee_move_count() test_unsafepointer_initialize_pointee_explicit_copy() + test_explicit_copy_of_pointer_address() test_bitcast() test_unsafepointer_string() test_eq() From 1d8012c0054c9616915004f09f7b1097cbcf1bf3 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 29 Jun 2024 17:04:45 -0500 Subject: [PATCH 1069/2019] [External] [stdlib] Add `CollectionElementNew` to a few structs, part 3 (#42563) [External] [stdlib] Add `CollectionElementNew` to a few structs, part 3 As I worked on removing `CollectionElement`, I noticed that those structs needed the new trait in the migration. Those are pretty trivial and the new constructor will soon be used left and right so there is no test, but if you believe that's needed, just ask. This PR adds the explicit copy constructor to `StringRef`, `_FixedString`, `InlineString`, `DLHandle`, `PythonObject` and `Path` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3140 MODULAR_ORIG_COMMIT_REV_ID: 7dac1c0ccd6ec361290b9991c83bb838390b7ddf --- stdlib/src/pathlib/path.mojo | 17 ++++++++++++++++- stdlib/src/python/object.mojo | 8 ++++++++ stdlib/src/sys/ffi.mojo | 10 +++++++++- stdlib/src/utils/inline_string.mojo | 25 +++++++++++++++++++++++-- stdlib/src/utils/stringref.mojo | 13 +++++++++++++ 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 517d32cc8f..6499d25689 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -60,7 +60,14 @@ fn _dir_of_current_file() raises -> Path: return Path(str(file_name)[0:i]) -struct Path(Stringable, Formattable, CollectionElement, PathLike, KeyElement): +struct Path( + Stringable, + Formattable, + CollectionElement, + CollectionElementNew, + PathLike, + KeyElement, +): """The Path object.""" var path: String @@ -78,6 +85,14 @@ struct Path(Stringable, Formattable, CollectionElement, PathLike, KeyElement): """ self.path = path + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self.path = String(other=other.path) + fn __moveinit__(inout self, owned existing: Self): """Move data of an existing Path into a new one. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 756adf2f70..89992d0220 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -119,6 +119,14 @@ struct PythonObject( """Initialize the object with a `None` value.""" self.__init__(None) + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self = other + fn __init__(inout self, ptr: PyObjectPtr): """Initialize the object with a `PyObjectPtr` value. diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index edeb7ec0bd..4650e8d386 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -44,7 +44,7 @@ alias DEFAULT_RTLD = RTLD.NOW | RTLD.GLOBAL @value @register_passable("trivial") -struct DLHandle(CollectionElement, Boolable): +struct DLHandle(CollectionElement, CollectionElementNew, Boolable): """Represents a dynamically linked library that can be loaded and unloaded. The library is loaded on initialization and unloaded by `close`. @@ -78,6 +78,14 @@ struct DLHandle(CollectionElement, Boolable): else: self.handle = DTypePointer[DType.int8]() + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self = other + fn check_symbol(self, name: String) -> Bool: """Check that the symbol exists in the dynamic library. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index e19177c2db..5257772fcc 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -29,7 +29,7 @@ from utils._format import ToFormatter @value -struct InlineString(Sized, Stringable, CollectionElement): +struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): """A string that performs small-string optimization to avoid heap allocations for short strings. """ @@ -89,6 +89,14 @@ struct InlineString(Sized, Stringable, CollectionElement): """ self._storage = Self.Layout(heap_string^) + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self = other + # ===------------------------------------------------------------------=== # # Operator dunders # ===------------------------------------------------------------------=== # @@ -294,7 +302,12 @@ struct InlineString(Sized, Stringable, CollectionElement): @value struct _FixedString[CAP: Int]( - Sized, Stringable, Formattable, ToFormatter, CollectionElement + Sized, + Stringable, + Formattable, + ToFormatter, + CollectionElement, + CollectionElementNew, ): """A string with a fixed available capacity. @@ -319,6 +332,14 @@ struct _FixedString[CAP: Int]( self.buffer = InlineArray[UInt8, CAP](unsafe_uninitialized=True) self.size = 0 + fn __init__(inout self, *, other: Self): + """Copy the object. + + Args: + other: The value to copy. + """ + self = other + @always_inline fn __init__(inout self, literal: StringLiteral) raises: """Constructs a FixedString value given a string literal. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0eee0017b9..db44bc724d 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -40,6 +40,7 @@ struct StringRef( Sized, IntableRaising, CollectionElement, + CollectionElementNew, Stringable, Formattable, Hashable, @@ -70,6 +71,18 @@ struct StringRef( """ return StringRef(UnsafePointer[UInt8](), 0) + @always_inline + fn __init__(*, other: Self) -> Self: + """Copy the object. + + Args: + other: The value to copy. + + Returns: + Constructed `StringRef` object. + """ + return Self(other.data, other.length) + @always_inline fn __init__(str: StringLiteral) -> Self: """Construct a StringRef value given a constant string. From 68fe053c8bd90d7ced60558d5bfec42e04a0c465 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 29 Jun 2024 16:56:53 -0700 Subject: [PATCH 1070/2019] [mojo-stdlib] Simplify code working with kgen packs. Various type conversion problems have now been fixed, so we can use a helper function instead of an inline mlir op to load a kgen pack. MODULAR_ORIG_COMMIT_REV_ID: bc1b38ecb04c1131ed857bf3e7b28e8dddd2a4ec --- stdlib/src/builtin/builtin_list.mojo | 15 +++++++++------ stdlib/src/builtin/io.mojo | 6 +----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index a259511cac..25653ecc06 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -455,19 +455,22 @@ struct _LITRefPackHelper[ fn get_as_kgen_pack(self) -> Self.kgen_pack_with_pointer_type: return rebind[Self.kgen_pack_with_pointer_type](self.storage) + alias _variadic_with_pointers_removed = __mlir_attr[ + `#kgen.param.expr: !kgen.variadic`, + ] + # This is the `!kgen.pack` type that happens if one loads all the elements # of the pack. alias loaded_kgen_pack_type = __mlir_type[ - `!kgen.pack<:variadic `, Self._kgen_element_types, `>` + `!kgen.pack<:variadic `, Self._variadic_with_pointers_removed, `>` ] # This returns the stored KGEN pack after loading all of the elements. - # FIXME(37129): This doesn't actually work because vtables aren't getting - # removed from TypeConstants correctly. fn get_loaded_kgen_pack(self) -> Self.loaded_kgen_pack_type: - return rebind[Self.loaded_kgen_pack_type]( - __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) - ) + # FIXME: Remove rebind[Self.loaded_kgen_pack_type]( + return __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 08955c66fa..a13c3a9396 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -102,11 +102,7 @@ fn _printf[ # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C snprintf call. Load # all the members of the pack. - var kgen_pack = _LITRefPackHelper(arguments._value).get_as_kgen_pack() - - # FIXME(37129): Cannot use get_loaded_kgen_pack because vtables on types - # aren't stripped off correctly. - var loaded_pack = __mlir_op.`kgen.pack.load`(kgen_pack) + var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() @parameter if triple_is_nvidia_cuda(): From d02ec97a66b7c0a272adaebba0fbe705400fc6fc Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Sat, 29 Jun 2024 23:37:11 -0500 Subject: [PATCH 1071/2019] [External] [stdlib] Add CollectionElementNew to a few structs, part 4 (#42564) [External] [stdlib] Add CollectionElementNew to a few structs, part 4 Adding the explicit copy constructor is needed for when collections will require all their elements to have an explicit copy constructor. This PR contains the modification for `_ImmutableString`, `_ObjectImpl`, `_FormatCurlyEntry`, `OwnedKwargsDict` and `_NoneType` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3143 MODULAR_ORIG_COMMIT_REV_ID: 4722182a83f5a0ae701dd7d57999591fda9fb087 --- stdlib/src/builtin/object.mojo | 8 ++++++-- stdlib/src/builtin/string.mojo | 10 ++++++++-- stdlib/src/collections/dict.mojo | 12 +++++++++++- stdlib/src/collections/optional.mojo | 5 +++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f98819640d..3d1fbefad6 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -36,7 +36,7 @@ struct _NoneMarker(CollectionElementNew): @register_passable("trivial") -struct _ImmutableString: +struct _ImmutableString(CollectionElement, CollectionElementNew): """Python strings are immutable. This class is marked as trivially register passable because its memory will be managed by `_ObjectImpl`. It is a pointer and integer pair. Memory will be dynamically allocated. @@ -53,6 +53,10 @@ struct _ImmutableString: self.data = data self.length = length + @always_inline + fn __init__(inout self, *, other: Self): + self = other + @always_inline fn string_compare(self, rhs: _ImmutableString) -> Int: var res = memcmp(self.data, rhs.data, min(self.length, rhs.length)) @@ -234,7 +238,7 @@ struct _Function: ) -struct _ObjectImpl(CollectionElement, Stringable): +struct _ObjectImpl(CollectionElement, CollectionElementNew, Stringable): """This class is the underlying implementation of the value of an `object`. It is a variant of primitive types and pointers to implementations of more complex types. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 0e52f3bf2e..b34ed59fc6 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2431,7 +2431,7 @@ fn _calc_format_buffer_size[type: DType]() -> Int: @value -struct _FormatCurlyEntry: +struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): """ Internally used by the `format()` method. @@ -2447,14 +2447,20 @@ struct _FormatCurlyEntry: var last_curly: Int """The index of an closing brace around a substitution field.""" - var field: Variant[ + alias _FieldVariantType = Variant[ String, # kwargs indexing (`{field_name}`) Int, # args manual indexing (`{3}`) NoneType, # args automatic indexing (`{}`) Bool, # for escaped curlies ('{{') ] + var field: Self._FieldVariantType """Store the substitution field.""" + fn __init__(inout self, *, other: Self): + self.first_curly = other.first_curly + self.last_curly = other.last_curly + self.field = Self._FieldVariantType(other=other.field) + fn is_escaped_brace(ref [_]self) -> Bool: return self.field.isa[Bool]() diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 491fc71985..46aefeda0a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -986,7 +986,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._n_entries = self.size -struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): +struct OwnedKwargsDict[V: CollectionElement]( + Sized, CollectionElement, CollectionElementNew +): """Container used to pass owned variadic keyword arguments to functions. This type mimics the interface of a dictionary with `String` keys, and @@ -1010,6 +1012,14 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """Initialize an empty keyword dictionary.""" self._dict = Dict[Self.key_type, V]() + fn __init__(inout self, *, other: Self): + """Copy an existing keyword dictionary. + + Args: + other: The existing keyword dictionary. + """ + self._dict = other._dict + fn __copyinit__(inout self, existing: Self): """Copy an existing keyword dictionary. diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 818f8f4982..0f9ed6aa20 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -36,8 +36,9 @@ from utils import Variant # TODO(27780): NoneType can't currently conform to traits @value -struct _NoneType(CollectionElement): - pass +struct _NoneType(CollectionElement, CollectionElementNew): + fn __init__(inout self, *, other: Self): + pass # ===----------------------------------------------------------------------===# From 3186e0a84a5aff909dda44065ef46d0f09e15c9c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 30 Jun 2024 11:51:21 -0700 Subject: [PATCH 1072/2019] [mojo-stdlib] Simplify `_snprintf`, NFC This simplifies the internal implementation of this function and removes a FIXME comment. MODULAR_ORIG_COMMIT_REV_ID: 0180da523c6746f9baeccb08b005122c79523878 --- stdlib/src/builtin/builtin_list.mojo | 1 - stdlib/src/builtin/io.mojo | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 25653ecc06..a275b63f8f 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -469,7 +469,6 @@ struct _LITRefPackHelper[ # This returns the stored KGEN pack after loading all of the elements. fn get_loaded_kgen_pack(self) -> Self.loaded_kgen_pack_type: - # FIXME: Remove rebind[Self.loaded_kgen_pack_type]( return __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index a13c3a9396..b6a3a131d6 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -150,11 +150,7 @@ fn _snprintf[ # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C snprintf call. Load # all the members of the pack. - var kgen_pack = _LITRefPackHelper(arguments._value).get_as_kgen_pack() - - # FIXME(37129): Cannot use get_loaded_kgen_pack because vtables on types - # aren't stripped off correctly. - var loaded_pack = __mlir_op.`kgen.pack.load`(kgen_pack) + var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() return int( __mlir_op.`pop.external_call`[ From 2f1ef5923a25eb2b4c63968ac2d6cd00ca45dfdf Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 30 Jun 2024 15:32:52 -0700 Subject: [PATCH 1073/2019] [mojo-lang] Expand `pop.external_call` to work with packs. (#42584) This adds support for `pop.external_call` to take kgen.packs and unpack them when lowering to LLVM. MODULAR_ORIG_COMMIT_REV_ID: cadb2421f2798550e6cdd2fe0821a8f4f5d4cf17 --- stdlib/src/builtin/io.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index b6a3a131d6..e542be1188 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -100,7 +100,7 @@ fn _printf[ fmt: StringLiteral, *types: AnyType ](*arguments: *types, file: FileDescriptor = stdout): # The argument pack will contain references for each value in the pack, - # but we want to pass their values directly into the C snprintf call. Load + # but we want to pass their values directly into the C printf call. Load # all the members of the pack. var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() From 057b973c111ea8d3c6513b19510f49c90d529cb1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 30 Jun 2024 17:32:42 -0700 Subject: [PATCH 1074/2019] [mojo-stdlib] Replace many `external_call` overloads with varargs This replaces about half of the external_call overloads with one variadic one. MODULAR_ORIG_COMMIT_REV_ID: a6044b2ea3fa885134a1f4b3de2883d64a1d4c98 --- stdlib/src/sys/ffi.mojo | 829 ++-------------------------------------- 1 file changed, 30 insertions(+), 799 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 4650e8d386..640faa865c 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -18,6 +18,7 @@ from utils import StringRef from .info import os_is_linux, os_is_windows from .intrinsics import _mlirtype_is_eq +from builtin.builtin_list import _LITRefPackHelper alias C_char = Int8 """C `char` type.""" @@ -275,490 +276,48 @@ fn _get_dylib_function[ # ===----------------------------------------------------------------------===# -@always_inline("nodebug") -fn external_call[callee: StringLiteral, type: AnyTrivialRegType]() -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]() - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]() - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, type: AnyTrivialRegType, T0: AnyTrivialRegType -](arg0: T0) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - - Args: - arg0: The first argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None](arg0) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, -](arg0: T0, arg1: T1) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1 - ) - - @always_inline("nodebug") fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2) -> type: + callee: StringLiteral, type: AnyTrivialRegType, *types: AnyType +](*arguments: *types) -> type: """Calls an external function. - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: - """Calls an external function. + arguments: The arguments to pass to the external function. Parameters: callee: The name of the external function. type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. + types: The argument types. Returns: The external call result. """ - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - - Returns: - The external call result. - """ + # The argument pack will contain references for each value in the pack, + # but we want to pass their values directly into the C printf call. Load + # all the members of the pack. + var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() @parameter if _mlirtype_is_eq[type, NoneType](): __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4 + loaded_pack ) return rebind[type](None) else: return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4 + loaded_pack ) @always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4, arg5 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, -) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, -) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, -) -> type: +fn external_call[callee: StringLiteral, type: AnyTrivialRegType]() -> type: """Calls an external function. Parameters: callee: The name of the external function. type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - T9: The tenth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - arg9: The tenth argument. Returns: The external call result. @@ -766,73 +325,25 @@ fn external_call[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 - ) + __mlir_op.`pop.external_call`[func = callee.value, _type=None]() return rebind[type](None) else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 - ) + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]() @always_inline("nodebug") fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, - T10: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, - arg10: T10, -) -> type: + callee: StringLiteral, type: AnyTrivialRegType, T0: AnyTrivialRegType +](arg0: T0) -> type: """Calls an external function. Parameters: callee: The name of the external function. type: The return type. T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - T9: The tenth argument type. - T10: The eleventh argument type. Args: arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - arg9: The tenth argument. - arg10: The eleventh argument. Returns: The external call result. @@ -840,13 +351,11 @@ fn external_call[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 - ) + __mlir_op.`pop.external_call`[func = callee.value, _type=None](arg0) return rebind[type](None) else: return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 + arg0 ) @@ -856,30 +365,7 @@ fn external_call[ type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, - T10: AnyTrivialRegType, - T11: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, - arg10: T10, - arg11: T11, -) -> type: +](arg0: T0, arg1: T1) -> type: """Calls an external function. Parameters: @@ -887,30 +373,10 @@ fn external_call[ type: The return type. T0: The first argument type. T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - T9: The tenth argument type. - T10: The eleventh argument type. - T11: The twelfth argument type. Args: arg0: The first argument. arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - arg9: The tenth argument. - arg10: The eleventh argument. - arg11: The twelfth argument. Returns: The external call result. @@ -919,34 +385,12 @@ fn external_call[ @parameter if _mlirtype_is_eq[type, NoneType](): __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, + arg0, arg1 ) return rebind[type](None) else: return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, + arg0, arg1 ) @@ -957,31 +401,7 @@ fn external_call[ T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, - T10: AnyTrivialRegType, - T11: AnyTrivialRegType, - T12: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, - arg10: T10, - arg11: T11, - arg12: T12, -) -> type: +](arg0: T0, arg1: T1, arg2: T2) -> type: """Calls an external function. Parameters: @@ -990,31 +410,11 @@ fn external_call[ T0: The first argument type. T1: The second argument type. T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - T9: The tenth argument type. - T10: The eleventh argument type. - T11: The twelfth argument type. - T12: The thirteenth argument type. Args: arg0: The first argument. arg1: The second argument. arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - arg9: The tenth argument. - arg10: The eleventh argument. - arg11: The twelfth argument. - arg12: The thirteenth argument. Returns: The external call result. @@ -1023,36 +423,12 @@ fn external_call[ @parameter if _mlirtype_is_eq[type, NoneType](): __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, - arg12, + arg0, arg1, arg2 ) return rebind[type](None) else: return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, - arg12, + arg0, arg1, arg2 ) @@ -1064,32 +440,7 @@ fn external_call[ T1: AnyTrivialRegType, T2: AnyTrivialRegType, T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, - T10: AnyTrivialRegType, - T11: AnyTrivialRegType, - T12: AnyTrivialRegType, - T13: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, - arg10: T10, - arg11: T11, - arg12: T12, - arg13: T13, -) -> type: +](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: """Calls an external function. Parameters: @@ -1099,32 +450,12 @@ fn external_call[ T1: The second argument type. T2: The third argument type. T3: The fourth argument type. - T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - T9: The tenth argument type. - T10: The eleventh argument type. - T11: The twelfth argument type. - T12: The thirteenth argument type. - T13: The fourteenth argument type. Args: arg0: The first argument. arg1: The second argument. arg2: The third argument. arg3: The fourth argument. - arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - arg9: The tenth argument. - arg10: The eleventh argument. - arg11: The twelfth argument. - arg12: The thirteenth argument. - arg13: The fourteenth argument. Returns: The external call result. @@ -1133,38 +464,12 @@ fn external_call[ @parameter if _mlirtype_is_eq[type, NoneType](): __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, - arg12, - arg13, + arg0, arg1, arg2, arg3 ) return rebind[type](None) else: return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, - arg12, - arg13, + arg0, arg1, arg2, arg3 ) @@ -1177,33 +482,7 @@ fn external_call[ T2: AnyTrivialRegType, T3: AnyTrivialRegType, T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, - T10: AnyTrivialRegType, - T11: AnyTrivialRegType, - T12: AnyTrivialRegType, - T13: AnyTrivialRegType, - T14: AnyTrivialRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, - arg10: T10, - arg11: T11, - arg12: T12, - arg13: T13, - arg14: T14, -) -> type: +](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: """Calls an external function. Parameters: @@ -1214,16 +493,6 @@ fn external_call[ T2: The third argument type. T3: The fourth argument type. T4: The fifth argument type. - T5: The sixth argument type. - T6: The seventh argument type. - T7: The eighth argument type. - T8: The ninth argument type. - T9: The tenth argument type. - T10: The eleventh argument type. - T11: The twelfth argument type. - T12: The thirteenth argument type. - T13: The fourteenth argument type. - T14: The fifteenth argument type. Args: arg0: The first argument. @@ -1231,16 +500,6 @@ fn external_call[ arg2: The third argument. arg3: The fourth argument. arg4: The fifth argument. - arg5: The sixth argument. - arg6: The seventh argument. - arg7: The eighth argument. - arg8: The ninth argument. - arg9: The tenth argument. - arg10: The eleventh argument. - arg11: The twelfth argument. - arg12: The thirteenth argument. - arg13: The fourteenth argument. - arg14: The fifteenth argument. Returns: The external call result. @@ -1249,40 +508,12 @@ fn external_call[ @parameter if _mlirtype_is_eq[type, NoneType](): __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, - arg12, - arg13, - arg14, + arg0, arg1, arg2, arg3, arg4 ) return rebind[type](None) else: return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, - arg1, - arg2, - arg3, - arg4, - arg5, - arg6, - arg7, - arg8, - arg9, - arg10, - arg11, - arg12, - arg13, - arg14, + arg0, arg1, arg2, arg3, arg4 ) From b1fb478c622dc59973d1e42546d3259852cc62af Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 30 Jun 2024 21:36:05 -0700 Subject: [PATCH 1075/2019] [mojo-stdlib] Remove another overload of `external_call`, NFC MODULAR_ORIG_COMMIT_REV_ID: 3f2f2ddf846c43a9d603f43f4f3581a63bfec481 --- stdlib/src/sys/ffi.mojo | 44 ----------------------------------------- 1 file changed, 44 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 640faa865c..8ae6b84b01 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -473,50 +473,6 @@ fn external_call[ ) -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - T4: The fifth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - arg4: The fifth argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3, arg4 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3, arg4 - ) - - # ===----------------------------------------------------------------------===# # _external_call_const # ===----------------------------------------------------------------------===# From 5c2e810fff173504713e9514306a355d3438581f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 1 Jul 2024 19:09:48 +0300 Subject: [PATCH 1076/2019] [stdlib] Add `math.align_{up,down}` overloads for `UInt` MODULAR_ORIG_COMMIT_REV_ID: 1dca0ed69af29883867b43948a76e3dd678e6c30 --- docs/changelog.md | 4 ++-- stdlib/src/math/math.mojo | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b886d19acc..eb41509f29 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -181,8 +181,8 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a paltform-dependent width. `UInt` implements most arithmethic operations that make sense for integers, with the notable exception of `__neg__`. Builtin - functions such as `min`/`max`, as well as utilities like `math.ceildiv` are - also implemented for `UInt`. + functions such as `min`/`max`, as well as `math` functions like `ceildiv`, + `align_down`, and `align_up` are also implemented for `UInt`. ### 🦋 Changed diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 0166ea2474..196429b53b 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1031,6 +1031,23 @@ fn align_down(value: Int, alignment: Int) -> Int: return (value // alignment) * alignment +@always_inline +fn align_down(value: UInt, alignment: UInt) -> UInt: + """Returns the closest multiple of alignment that is less than or equal to + value. + + Args: + value: The value to align. + alignment: Value to align to. + + Returns: + Closest multiple of the alignment that is less than or equal to the + input value. In other words, floor(value / alignment) * alignment. + """ + debug_assert(alignment != 0, "zero alignment") + return (value // alignment) * alignment + + # ===----------------------------------------------------------------------=== # # align_up # ===----------------------------------------------------------------------=== # @@ -1053,6 +1070,23 @@ fn align_up(value: Int, alignment: Int) -> Int: return ceildiv(value, alignment) * alignment +@always_inline +fn align_up(value: UInt, alignment: UInt) -> UInt: + """Returns the closest multiple of alignment that is greater than or equal + to value. + + Args: + value: The value to align. + alignment: Value to align to. + + Returns: + Closest multiple of the alignment that is greater than or equal to the + input value. In other words, ceiling(value / alignment) * alignment. + """ + debug_assert(alignment != 0, "zero alignment") + return ceildiv(value, alignment) * alignment + + # ===----------------------------------------------------------------------=== # # acos # ===----------------------------------------------------------------------=== # From 45e810eafd95475716cdf4a37105f8ae1ef9f44d Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 1 Jul 2024 12:12:23 -0500 Subject: [PATCH 1077/2019] [stdlib] hotfix: Revert test_locks.mojo move introducing internal dep from public tests This test files depends on the `runtime` module, which is currently an internal module. The open source tests should not depend on the internal portions of the standard library, as that can break test running. MODULAR_ORIG_COMMIT_REV_ID: e2a8f1bab9793cf92709734232abb40922084733 --- stdlib/test/utils/test_locks.mojo | 81 ------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 stdlib/test/utils/test_locks.mojo diff --git a/stdlib/test/utils/test_locks.mojo b/stdlib/test/utils/test_locks.mojo deleted file mode 100644 index e135f7b55b..0000000000 --- a/stdlib/test/utils/test_locks.mojo +++ /dev/null @@ -1,81 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s - - -from os import Atomic -from time import now, sleep, time_function - -from utils.lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock -from runtime.llcl import TaskGroup -from testing import assert_equal, assert_true - - -# CHECK-LABEL: test_spin_waiter -def test_spin_waiter(): - print("== test_spin_waiter") - var waiter = SpinWaiter() - alias RUNS = 1000 - for i in range(RUNS): - waiter.wait() - assert_true(True) - - -fn test_basic_lock() raises: - var lock = BlockingSpinLock() - var rawCounter = 0 - var counter = Atomic[DType.int64](False) - alias maxI = 100 - alias maxJ = 100 - - @parameter - async fn inc() capturing: - with BlockingScopedLock(lock): - rawCounter += 1 - _ = counter.fetch_add(1) - - # CHECK: PRE::Atomic counter is 0 , and raw counter, 0 - print( - "PRE::Atomic counter is ", - counter.load(), - ", and raw counter, ", - rawCounter, - ) - - @parameter - fn test_atomic() capturing -> None: - var tg = TaskGroup[__lifetime_of()]() - for i in range(0, maxI): - for j in range(0, maxJ): - tg.create_task(inc()) - tg.wait() - - var time_ns = time_function[test_atomic]() - _ = lock^ - # print("Total time taken ", time_ns / (1_000_000_000), " s") - - # CHECK: POST::Atomic counter is 10000 , and raw counter, 10000 - print( - "POST::Atomic counter is ", - counter.load(), - ", and raw counter, ", - rawCounter, - ) - assert_equal(counter.load(), rawCounter, "atomic stress test failed") - - return - - -def main(): - test_spin_waiter() - test_basic_lock() From 856b04d3b85b4865a2239e18cd17bca50e916043 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 1 Jul 2024 10:18:36 -0700 Subject: [PATCH 1078/2019] [mojo-stdlib] Remove 0/1 arg overloads of external_call. NFC This continues pruning these down. The variadic version should handle this. MODULAR_ORIG_COMMIT_REV_ID: d1bcaf8b29ee89f0bb908ea3b55f8ffadbfe7484 --- stdlib/src/sys/ffi.mojo | 48 ----------------------------------------- 1 file changed, 48 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 8ae6b84b01..aec2409804 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -311,54 +311,6 @@ fn external_call[ ) -@always_inline("nodebug") -fn external_call[callee: StringLiteral, type: AnyTrivialRegType]() -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]() - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]() - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, type: AnyTrivialRegType, T0: AnyTrivialRegType -](arg0: T0) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - - Args: - arg0: The first argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None](arg0) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0 - ) - - @always_inline("nodebug") fn external_call[ callee: StringLiteral, From b3968051e6acc9e3136a1d50b1b0466b6886b961 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 1 Jul 2024 10:21:53 -0700 Subject: [PATCH 1079/2019] [mojo-stdlib] Remove 3+ arg overloads of external_call. This continues pruning these down. MODULAR_ORIG_COMMIT_REV_ID: aaf2691894f036076c389f8eb1bcedc9b3ad880d --- stdlib/src/sys/ffi.mojo | 79 ----------------------------------------- 1 file changed, 79 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index aec2409804..933353df45 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -346,85 +346,6 @@ fn external_call[ ) -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2 - ) - - -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - T2: The third argument type. - T3: The fourth argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - arg2: The third argument. - arg3: The fourth argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1, arg2, arg3 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1, arg2, arg3 - ) - - # ===----------------------------------------------------------------------===# # _external_call_const # ===----------------------------------------------------------------------===# From ebaad04a3b714d7552fedf5427994572ea9442db Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 1 Jul 2024 20:42:52 +0300 Subject: [PATCH 1080/2019] [stdlib] Add `ceildiv` overload for `Int` With a direct implementation using the `index.ceildivs` mlir op. Also change the `UInt` overload to use an mlir op directly. MODULAR_ORIG_COMMIT_REV_ID: 8a99c6f720880f335ba2ed117a776ea2402c726c --- stdlib/src/math/math.mojo | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 196429b53b..cbf88372a9 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -116,7 +116,37 @@ fn ceildiv[T: CeilDivableRaising, //](numerator: T, denominator: T) raises -> T: return -(numerator // -denominator) +# NOTE: this overload is needed because of overload precedence; without it the +# Int overload would be preferred, and ceildiv wouldn't work on IntLiteral. @always_inline +fn ceildiv(numerator: IntLiteral, denominator: IntLiteral) -> IntLiteral: + """Return the rounded-up result of dividing x by y. + + Args: + numerator: The numerator. + denominator: The denominator. + + Returns: + The ceiling of dividing x by y. + """ + return -(numerator // -denominator) + + +@always_inline("nodebug") +fn ceildiv(numerator: Int, denominator: Int) -> Int: + """Return the rounded-up result of dividing x by y. + + Args: + numerator: The numerator. + denominator: The denominator. + + Returns: + The ceiling of dividing x by y. + """ + return __mlir_op.`index.ceildivs`(numerator.value, denominator.value) + + +@always_inline("nodebug") fn ceildiv(numerator: UInt, denominator: UInt) -> UInt: """Return the rounded-up result of dividing x by y. @@ -127,7 +157,7 @@ fn ceildiv(numerator: UInt, denominator: UInt) -> UInt: Returns: The ceiling of dividing x by y. """ - return (numerator + denominator - 1) // denominator + return __mlir_op.`index.ceildivu`(numerator.value, denominator.value) # ===----------------------------------------------------------------------=== # From ecbe89c5ae59341ca6d8a5bf8b71491158df7515 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 1 Jul 2024 11:54:28 -0700 Subject: [PATCH 1081/2019] [mojo-stdlib] Unify `external_call` into a single variadic overload, NFC This kills off the remaining overloads of `external_call` and upgrades `_external_call_const` to use the same approach. MODULAR_ORIG_COMMIT_REV_ID: e3c0d31461307d86eb6b0797f4a99cbb3f76ed63 --- stdlib/src/sys/ffi.mojo | 114 ++++------------------------------------ 1 file changed, 10 insertions(+), 104 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 933353df45..a45ba7be49 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -311,41 +311,6 @@ fn external_call[ ) -@always_inline("nodebug") -fn external_call[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, -](arg0: T0, arg1: T1) -> type: - """Calls an external function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - - Args: - arg0: The first argument. - arg1: The second argument. - - Returns: - The external call result. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.external_call`[func = callee.value, _type=None]( - arg0, arg1 - ) - return rebind[type](None) - else: - return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( - arg0, arg1 - ) - - # ===----------------------------------------------------------------------===# # _external_call_const # ===----------------------------------------------------------------------===# @@ -353,88 +318,29 @@ fn external_call[ @always_inline("nodebug") fn _external_call_const[ - callee: StringLiteral, type: AnyTrivialRegType -]() -> type: + callee: StringLiteral, type: AnyTrivialRegType, *types: AnyType +](*arguments: *types) -> type: """Mark the external function call as having no observable effects to the program state. This allows the compiler to optimize away successive calls to the same function. - Parameters: - callee: The name of the external function. - type: The return type. - - Returns: - The external call result. - """ - return __mlir_op.`pop.external_call`[ - func = callee.value, - resAttrs = __mlir_attr.`[{llvm.noundef}]`, - funcAttrs = __mlir_attr.`["willreturn"]`, - memory = __mlir_attr[ - `#llvm.memory_effects`, - ], - _type=type, - ]() - - -@always_inline("nodebug") -fn _external_call_const[ - callee: StringLiteral, type: AnyTrivialRegType, T0: AnyTrivialRegType -](arg0: T0) -> type: - """Mark the external function call as having no observable effects to the - program state. This allows the compiler to optimize away successive calls - to the same function. + Args: + arguments: The arguments to pass to the external function. Parameters: callee: The name of the external function. type: The return type. - T0: The first argument type. - - Args: - arg0: The first argument. + types: The argument types. Returns: The external call result. """ - return __mlir_op.`pop.external_call`[ - func = callee.value, - resAttrs = __mlir_attr.`[{llvm.noundef}]`, - funcAttrs = __mlir_attr.`["willreturn"]`, - memory = __mlir_attr[ - `#llvm.memory_effects`, - ], - _type=type, - ](arg0) - -@always_inline("nodebug") -fn _external_call_const[ - callee: StringLiteral, - type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, -](arg0: T0, arg1: T1) -> type: - """Mark the external function call as having no observable effects to the - program state. This allows the compiler to optimize away successive calls - to the same function. - - Parameters: - callee: The name of the external function. - type: The return type. - T0: The first argument type. - T1: The second argument type. - - Args: - arg0: The first argument. - arg1: The second argument. + # The argument pack will contain references for each value in the pack, + # but we want to pass their values directly into the C printf call. Load + # all the members of the pack. + var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() - Returns: - The external call result. - """ return __mlir_op.`pop.external_call`[ func = callee.value, resAttrs = __mlir_attr.`[{llvm.noundef}]`, @@ -445,4 +351,4 @@ fn _external_call_const[ `inaccessibleMem = none>`, ], _type=type, - ](arg0, arg1) + ](loaded_pack) From f1281ac2558d78410f4b360d91cfa69bcb855088 Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Mon, 1 Jul 2024 14:29:28 -0500 Subject: [PATCH 1082/2019] [External] [docs] Fix typos in pointers.ipynb (#42610) [External] [docs] Fix typos in pointers.ipynb - Change "in as" to "as a" - Change "The DTypePointer" to "A DTypePointer" - Change "with where" to "where" Co-authored-by: Brian M Johnson Closes modularml/mojo#3155 MODULAR_ORIG_COMMIT_REV_ID: 83e00d3de758ba98c405ead55072303787567481 --- docs/manual/pointers.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 9aedf781e3..4e7dce77b3 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -437,7 +437,7 @@ "\n", "As mentioned in [Allocating memory](#allocating-memory), you can use an \n", "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n", - "in as single, contiguous block. Pointers support arithmetic: adding an integer\n", + "as a single, contiguous block. Pointers support arithmetic: adding an integer\n", "to a pointer returns a new pointer offset by the specified number of values from\n", "the original pointer:\n", "\n", @@ -575,7 +575,7 @@ "source": [ "## `DTypePointer`: handling numeric data\n", "\n", - "The [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", + "A [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", "pointer that supports some additional methods for loading and storing numeric\n", "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n", "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n", @@ -646,7 +646,7 @@ "and \n", "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n", "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", - "that this function only handles images with where the number of pixels is evenly\n", + "that this function only handles images where the number of pixels is evenly\n", "divisible by eight.)" ] }, From 6945c95e427afe843f7d95eafbeb6e77d96816d4 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 1 Jul 2024 23:54:22 +0300 Subject: [PATCH 1083/2019] [stdlib] Ensure `SIMD.{load,store}` cannot be offset with `Intable` The offset could be something that's implicitly convertible to `Int`, but it's probably not a good idea to allow floats and other coercible-to-int types to be used as offset. MODULAR_ORIG_COMMIT_REV_ID: 60fbd16c60a4b7b4f975f3dd51e3c23967bf9698 --- stdlib/src/builtin/simd.mojo | 73 ++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 36629578e3..33c60708f6 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2675,7 +2675,7 @@ struct SIMD[type: DType, size: Int]( ](zero_simd, self, Int32(-shift)) @staticmethod - @always_inline("nodebug") + @always_inline fn load[ *, alignment: Int = Self._default_alignment, @@ -2697,24 +2697,51 @@ struct SIMD[type: DType, size: Int]( The loaded value. """ return Self.load[alignment=alignment, address_space=address_space]( - ptr, 0 + ptr, offset=0 + ) + + @staticmethod + @always_inline + fn load[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: Scalar) -> Self: + """Loads the value the Pointer object points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + The offset must be integer. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + offset: The offset to load from. + + Returns: + The loaded value. + """ + constrained[offset.type.is_integral(), "offset must be integer"]() + return Self.load[alignment=alignment, address_space=address_space]( + ptr, offset=int(offset) ) @staticmethod @always_inline("nodebug") fn load[ - T: Intable, *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: T) -> Self: + ](ptr: DTypePointer[type, address_space], offset: Int) -> Self: """Loads the value the Pointer object points to with the given offset. Constraints: The width and alignment must be positive integer values. Parameters: - T: The Intable type of the offset. alignment: The minimal alignment of the address. address_space: The address space the pointer is in. @@ -2737,7 +2764,7 @@ struct SIMD[type: DType, size: Int]( # intentionally don't unroll, otherwise the compiler vectorizes for i in range(size): - v[i] = ptr.address.offset(int(offset) + i).load[ + v[i] = ptr.address.offset(offset + i).load[ alignment=alignment ]() return v @@ -2749,21 +2776,19 @@ struct SIMD[type: DType, size: Int]( ) @staticmethod - @always_inline("nodebug") + @always_inline fn store[ - T: Intable, - /, *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: T, val: Self): + ](ptr: DTypePointer[type, address_space], offset: Int, val: Self): """Stores a single element value at the given offset. Constraints: The width and alignment must be positive integer values. + The offset must be integer. Parameters: - T: The Intable type of the offset. alignment: The minimal alignment of the address. address_space: The address space the pointer is in. @@ -2776,6 +2801,32 @@ struct SIMD[type: DType, size: Int]( ptr.offset(offset), val ) + @staticmethod + @always_inline + fn store[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: Scalar, val: Self): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + offset: The offset to store to. + val: The value to store. + """ + constrained[offset.type.is_integral(), "offset must be integer"]() + Self.store[alignment=alignment, address_space=address_space]( + ptr, int(offset), val + ) + @staticmethod @always_inline("nodebug") fn store[ From 13988706fc4c4763c95e0298a3098cce81890c71 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Jul 2024 00:01:14 +0300 Subject: [PATCH 1084/2019] [stdlib] Add `StringSlice` overload for `ord` This prevents unintentional `String` allocations when using `ord`. MODULAR_ORIG_COMMIT_REV_ID: f82a09c3527df9680bbb6b211f330e3828446ebc --- stdlib/src/builtin/string.mojo | 25 +++++++++++++++++++++++-- stdlib/test/builtin/test_string.mojo | 7 +++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index b34ed59fc6..d0f7b499a5 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -31,6 +31,7 @@ from utils._format import Formattable, Formatter, ToFormatter # ===----------------------------------------------------------------------=== # +@always_inline fn ord(s: String) -> Int: """Returns an integer that represents the given one-character string. @@ -38,6 +39,22 @@ fn ord(s: String) -> Int: representing the code point of that character. For example, `ord("a")` returns the integer `97`. This is the inverse of the `chr()` function. + Args: + s: The input string slice, which must contain only a single character. + + Returns: + An integer representing the code point of the given character. + """ + return ord(s.as_string_slice()) + + +fn ord(s: StringSlice) -> Int: + """Returns an integer that represents the given one-character string. + + Given a string representing one character, return an integer + representing the code point of that character. For example, `ord("a")` + returns the integer `97`. This is the inverse of the `chr()` function. + Args: s: The input string, which must contain only a single character. @@ -52,10 +69,12 @@ fn ord(s: String) -> Int: var p = s.unsafe_ptr().bitcast[UInt8]() var b1 = p[] if (b1 >> 7) == 0: # This is 1 byte ASCII char - debug_assert(len(s) == 1, "input string length must be 1") + debug_assert(s._byte_length() == 1, "input string length must be 1") return int(b1) var num_bytes = countl_zero(~b1) - debug_assert(len(s) == int(num_bytes), "input string must be one character") + debug_assert( + s._byte_length() == int(num_bytes), "input string must be one character" + ) debug_assert( 1 < int(num_bytes) < 5, "invalid UTF-8 byte " + str(b1) + " at index 0" ) @@ -1055,6 +1074,8 @@ struct String( Construct a String from several `Formattable` arguments: ```mojo + from testing import assert_equal + var string = String.format_sequence(1, ", ", 2.0, ", ", "three") assert_equal(string, "1, 2.0, three") diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index a66163d6d1..dc93a0d136 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -290,6 +290,7 @@ fn test_ord() raises: alias single_byte2 = ord("!") assert_equal(single_byte2, 33) + # TODO: change these to parameter domain when it work. var multi_byte = ord("α") assert_equal(multi_byte, 945) var multi_byte2 = ord("➿") @@ -297,6 +298,12 @@ fn test_ord() raises: var multi_byte3 = ord("🔥") assert_equal(multi_byte3, 128293) + # Test StringSlice overload + assert_equal(ord("A".as_string_slice()), 65) + assert_equal(ord("α".as_string_slice()), 945) + assert_equal(ord("➿".as_string_slice()), 10175) + assert_equal(ord("🔥".as_string_slice()), 128293) + fn test_chr() raises: assert_equal("A", chr(65)) From ee942f5ebdb1dc4478a1a42cf579e46f9754ea04 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 1 Jul 2024 18:16:08 -0700 Subject: [PATCH 1085/2019] [Stdlib] Allow the assertion functions to take the location info This allows one to write wrappers around the testing functions. MODULAR_ORIG_COMMIT_REV_ID: 04e24a1ec07bbb3e7cda54fd474f4459f94815d0 --- stdlib/src/collections/optional.mojo | 3 +- stdlib/src/testing/testing.mojo | 147 ++++++++++++++++----- stdlib/test/pathlib/test_pathlib.mojo | 8 +- stdlib/test/python/test_python_object.mojo | 5 +- stdlib/test/testing/test_assertion.mojo | 23 +++- 5 files changed, 143 insertions(+), 43 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 0f9ed6aa20..f08adfcea8 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -299,7 +299,8 @@ struct Optional[T: CollectionElement]( return self._value.unsafe_replace[_NoneType, T](_NoneType()) fn or_else(self, default: T) -> T: - """Return the underlying value contained in the Optional or a default value if the Optional's underlying value is not present. + """Return the underlying value contained in the Optional or a default + value if the Optional's underlying value is not present. Args: default: The new value to use if no value was present. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 45a056f185..cacbcda422 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -78,7 +78,12 @@ fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: @always_inline fn assert_true[ T: Boolable, // -](val: T, msg: String = "condition was unexpectedly False") raises: +]( + val: T, + msg: String = "condition was unexpectedly False", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input value is True and raises an Error if it's not. Parameters: @@ -87,18 +92,24 @@ fn assert_true[ Args: val: The value to assert to be True. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if not val: - raise _assert_error(msg, __call_location()) + raise _assert_error(msg, location.or_else(__call_location())) @always_inline fn assert_false[ T: Boolable, // -](val: T, msg: String = "condition was unexpectedly True") raises: +]( + val: T, + msg: String = "condition was unexpectedly True", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input value is False and raises an Error if it's not. Parameters: @@ -107,12 +118,13 @@ fn assert_false[ Args: val: The value to assert to be False. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if val: - raise _assert_error(msg, __call_location()) + raise _assert_error(msg, location.or_else(__call_location())) trait Testable(EqualityComparable, Stringable): @@ -123,7 +135,15 @@ trait Testable(EqualityComparable, Stringable): @always_inline -fn assert_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: +fn assert_equal[ + T: Testable +]( + lhs: T, + rhs: T, + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values are equal. If it is not then an Error is raised. @@ -134,19 +154,26 @@ fn assert_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: lhs: The lhs of the equality. rhs: The rhs of the equality. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs != rhs: raise _assert_cmp_error["`left == right` comparison"]( - str(lhs), str(rhs), msg, __call_location() + str(lhs), str(rhs), msg=msg, loc=location.or_else(__call_location()) ) # TODO: Remove the String and SIMD overloads once we have more powerful traits. @always_inline -fn assert_equal(lhs: String, rhs: String, msg: String = "") raises: +fn assert_equal( + lhs: String, + rhs: String, + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values are equal. If it is not then an Error is raised. @@ -154,20 +181,27 @@ fn assert_equal(lhs: String, rhs: String, msg: String = "") raises: lhs: The lhs of the equality. rhs: The rhs of the equality. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs != rhs: raise _assert_cmp_error["`left == right` comparison"]( - lhs, rhs, msg, __call_location() + lhs, rhs, msg=msg, loc=location.or_else(__call_location()) ) @always_inline fn assert_equal[ type: DType, size: Int -](lhs: SIMD[type, size], rhs: SIMD[type, size], msg: String = "") raises: +]( + lhs: SIMD[type, size], + rhs: SIMD[type, size], + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values are equal. If it is not then an Error is raised. @@ -179,18 +213,27 @@ fn assert_equal[ lhs: The lhs of the equality. rhs: The rhs of the equality. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if any(lhs != rhs): raise _assert_cmp_error["`left == right` comparison"]( - str(lhs), str(rhs), msg, __call_location() + str(lhs), str(rhs), msg=msg, loc=location.or_else(__call_location()) ) @always_inline -fn assert_not_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: +fn assert_not_equal[ + T: Testable +]( + lhs: T, + rhs: T, + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values are not equal. If it is not then an Error is raised. @@ -201,18 +244,25 @@ fn assert_not_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: lhs: The lhs of the inequality. rhs: The rhs of the inequality. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: raise _assert_cmp_error["`left != right` comparison"]( - str(lhs), str(rhs), msg, __call_location() + str(lhs), str(rhs), msg=msg, loc=location.or_else(__call_location()) ) @always_inline -fn assert_not_equal(lhs: String, rhs: String, msg: String = "") raises: +fn assert_not_equal( + lhs: String, + rhs: String, + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values are not equal. If it is not then an an Error is raised. @@ -220,20 +270,27 @@ fn assert_not_equal(lhs: String, rhs: String, msg: String = "") raises: lhs: The lhs of the inequality. rhs: The rhs of the inequality. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs == rhs: raise _assert_cmp_error["`left != right` comparison"]( - lhs, rhs, msg, __call_location() + lhs, rhs, msg=msg, loc=location.or_else(__call_location()) ) @always_inline fn assert_not_equal[ type: DType, size: Int -](lhs: SIMD[type, size], rhs: SIMD[type, size], msg: String = "") raises: +]( + lhs: SIMD[type, size], + rhs: SIMD[type, size], + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values are not equal. If it is not then an Error is raised. @@ -245,13 +302,14 @@ fn assert_not_equal[ lhs: The lhs of the inequality. rhs: The rhs of the inequality. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if all(lhs == rhs): raise _assert_cmp_error["`left != right` comparison"]( - str(lhs), str(rhs), msg, __call_location() + str(lhs), str(rhs), msg=msg, loc=location.or_else(__call_location()) ) @@ -261,12 +319,12 @@ fn assert_almost_equal[ ]( lhs: SIMD[type, size], rhs: SIMD[type, size], - /, - *, msg: String = "", + *, atol: Scalar[type] = 1e-08, rtol: Scalar[type] = 1e-05, equal_nan: Bool = False, + location: Optional[_SourceLocation] = None, ) raises: """Asserts that the input values are equal up to a tolerance. If it is not then an Error is raised. @@ -290,6 +348,7 @@ fn assert_almost_equal[ atol: The absolute tolerance. rtol: The relative tolerance. equal_nan: Whether to treat nans as equal. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. @@ -313,13 +372,19 @@ fn assert_almost_equal[ if msg: err += " (" + msg + ")" - raise _assert_error(err, __call_location()) + raise _assert_error(err, location.or_else(__call_location())) @always_inline fn assert_is[ T: StringableIdentifiable -](lhs: T, rhs: T, msg: String = "") raises: +]( + lhs: T, + rhs: T, + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values have the same identity. If they do not then an Error is raised. @@ -330,23 +395,27 @@ fn assert_is[ lhs: The lhs of the `is` statement. rhs: The rhs of the `is` statement. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs is not rhs: raise _assert_cmp_error["`left is right` identification"]( - str(lhs), - str(rhs), - msg, - __call_location(), + str(lhs), str(rhs), msg=msg, loc=location.or_else(__call_location()) ) @always_inline fn assert_is_not[ T: StringableIdentifiable -](lhs: T, rhs: T, msg: String = "") raises: +]( + lhs: T, + rhs: T, + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: """Asserts that the input values have different identities. If they do not then an Error is raised. @@ -357,22 +426,20 @@ fn assert_is_not[ lhs: The lhs of the `is not` statement. rhs: The rhs of the `is not` statement. msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). Raises: An Error with the provided message if assert fails and `None` otherwise. """ if lhs is rhs: raise _assert_cmp_error["`left is not right` identification"]( - str(lhs), - str(rhs), - msg, - __call_location(), + str(lhs), str(rhs), msg=msg, loc=location.or_else(__call_location()) ) fn _assert_cmp_error[ cmp: String -](lhs: String, rhs: String, msg: String, loc: _SourceLocation) -> String: +](lhs: String, rhs: String, *, msg: String, loc: _SourceLocation) -> String: var err = (cmp + " failed:\n left: " + lhs + "\n right: " + rhs) if msg: err += "\n reason: " + msg @@ -413,21 +480,31 @@ struct assert_raises: """Assigned the value returned by __call_locations() at Self.__init__.""" @always_inline - fn __init__(inout self): - """Construct a context manager with no message pattern.""" + fn __init__(inout self, *, location: Optional[_SourceLocation] = None): + """Construct a context manager with no message pattern. + + Args: + location: The location of the error (default to the `__call_location`). + """ self.message_contains = None - self.call_location = __call_location() + self.call_location = location.or_else(__call_location()) @always_inline - fn __init__(inout self, *, contains: String): + fn __init__( + inout self, + *, + contains: String, + location: Optional[_SourceLocation] = None, + ): """Construct a context manager matching specific errors. Args: contains: The test will only pass if the error message includes the literal text passed. + location: The location of the error (default to the `__call_location`). """ self.message_contains = contains - self.call_location = __call_location() + self.call_location = location.or_else(__call_location()) fn __enter__(self): """Enter the context manager.""" diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index f0e58ce613..389cbe17f2 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -41,9 +41,13 @@ def test_path(): def test_path_exists(): - assert_true(Path(__source_location().file_name).exists(), "does not exist") + assert_true( + Path(__source_location().file_name).exists(), msg="does not exist" + ) - assert_false((Path() / "this_path_does_not_exist.mojo").exists(), "exists") + assert_false( + (Path() / "this_path_does_not_exist.mojo").exists(), msg="exists" + ) def test_path_isdir(): diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 8280797478..b60323830f 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -360,7 +360,10 @@ fn test_iter() raises: for x in not_iterable: assert_false( True, - "This should not be reachable as the object is not iterable.", + msg=( + "This should not be reachable as the object is not" + " iterable." + ), ) diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index d152776a6b..9dd47215e9 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -24,6 +24,7 @@ from testing import ( ) from utils.numerics import inf, nan +from builtin._location import _SourceLocation @value @@ -65,22 +66,22 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:66:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:67:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:71:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:72:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:76:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:77:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:81:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:82:25: AssertionError:" in str(e)) def test_assert_almost_equal(): @@ -195,6 +196,19 @@ def test_assert_is_not(): assert_is_not(a, b) +def test_assert_custom_location(): + var location = _SourceLocation(2, 0, "my_file_location.mojo") + try: + assert_true( + False, + msg="always_false", + location=location, + ) + except e: + assert_true(str(location) in str(e)) + assert_true("always_false" in str(e)) + + def main(): test_assert_equal_is_generic() test_assert_not_equal_is_generic() @@ -203,3 +217,4 @@ def main(): test_assert_almost_equal() test_assert_is() test_assert_is_not() + test_assert_custom_location() From af176aa2bab201cee95bb4fd871180e4cab44637 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 1 Jul 2024 20:32:19 -0700 Subject: [PATCH 1086/2019] Adds `os.path.expanduser`, `pathlib.Path.expanduser`, and `pathlib.Path.home`. Follows all the Python behavior except for allowing raw bytes and using `pwd` on posix to expand e.g. `~user`. Also adds equality comparison for `String` == `Path`. MODULAR_ORIG_COMMIT_REV_ID: 58346919be4bc2acc2ac682b7212e7817d826339 --- docs/changelog.md | 26 ++++++-- stdlib/src/os/path/__init__.mojo | 13 +++- stdlib/src/os/path/path.mojo | 59 +++++++++++++++++++ stdlib/src/pathlib/path.mojo | 31 ++++++++++ stdlib/test/os/path/test_expanduser.mojo | 75 ++++++++++++++++++++++++ stdlib/test/pathlib/test_pathlib.mojo | 55 +++++++++++++++++ 6 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 stdlib/test/os/path/test_expanduser.mojo diff --git a/docs/changelog.md b/docs/changelog.md index eb41509f29..9576d4040e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -44,7 +44,7 @@ what we publish. ``` - Now supports "conditional conformances" where some methods on a struct have - additional trait requirements that the struct itself doesn't. This is + additional trait requirements that the struct itself doesn't. This is expressed through an explicitly declared `self` type: ```mojo @@ -85,7 +85,7 @@ what we publish. - As a specific form of "conditional conformances", initializers in a struct may indicate specific parameter bindings to use in the type of their `self` - argument. For example: + argument. For example: ```mojo @value @@ -117,8 +117,8 @@ what we publish. `testing` module. - `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. -([PR #2701](https://github.com/modularml/mojo/pull/2701) -by [@jayzhan211](https://github.com/jayzhan211)) + ([PR #2701](https://github.com/modularml/mojo/pull/2701) + by [@jayzhan211](https://github.com/jayzhan211)) - Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that returns an `UnsafePointer[C_char]` for convenient interoperability with C @@ -184,12 +184,25 @@ by [@jayzhan211](https://github.com/jayzhan211)) functions such as `min`/`max`, as well as `math` functions like `ceildiv`, `align_down`, and `align_up` are also implemented for `UInt`. +- `os.path.expanduser()` and `pathlib.Path.exapanduser()` have been added to + allow expanding a prefixed `~` in a `String` or `Path` with the users home + path: + + ```mojo + import os + print(os.path.expanduser("~/.modular")) + # /home/username/.modular + ``` + +- `Path.home()` has been added to return a path of the users home directory. + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that coroutines can only be awaited once. - Continued transition to `UnsafePointer` and unsigned byte type for strings: + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` @@ -287,6 +300,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Accessing local Python modules with `Python.add_to_path(".")` is no longer required, it now behaves the same as Python, you can access modules in the same folder as the target file: + - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` @@ -305,7 +319,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the `UnsafePointer.address_of(someRef[])` which makes the code explicit that the `UnsafePointer` gets the address of what the reference points to. @@ -316,7 +330,7 @@ by [@jayzhan211](https://github.com/jayzhan211)) - Removed `UnsafePointer.offset(offset:Int)`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. - The builtin `tensor` module has been removed. Identical functionality is diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index ee778f121a..da94f2ec91 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -11,4 +11,15 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from .path import dirname, exists, getsize, isdir, isfile, islink, join, lexists +from .path import ( + dirname, + exists, + expanduser, + getsize, + isdir, + isfile, + islink, + join, + lexists, + _get_home_path, +) diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 4e4364dcda..b6da47caee 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -31,6 +31,7 @@ from .._macos import _lstat as _lstat_macos from .._macos import _stat as _stat_macos from ..fstat import stat from ..os import sep +from ..env import getenv # ===----------------------------------------------------------------------=== # @@ -64,6 +65,64 @@ fn _get_lstat_st_mode(path: String) raises -> Int: return int(_lstat_linux_x86(path).st_mode) +# ===----------------------------------------------------------------------=== # +# expanduser +# ===----------------------------------------------------------------------=== # + + +fn _get_home_path() -> String: + @parameter + if os_is_windows(): + return getenv("USERPROFILE") + return getenv("HOME") + + +# TODO: When `pwd` module is implemented for POSIX, fallback to: +# pwd.getpwuid(os.getuid()).pw_dir if $HOME is not set, and allow for `~user`. +fn expanduser(path: String) raises -> String: + """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on windows. If + environment variables are not set or the `path` is not prefixed with `~`, + returns the `path` unmodified. + + Args: + path: The path that is being expanded. + + Returns: + The expanded path. + """ + if not path.startswith("~"): + return path + var userhome = _get_home_path() + if not userhome: + return path + # If there is more than a single `~` without correct separator, raise error. + if len(path) > 1 and path[1] != os.sep: + raise "malformed path, could not determine home directory." + var path_split = path.split(os.sep, 1) + # If there is a properly formatted seperator, return expanded path. + if len(path_split) == 2: + return os.path.join(userhome, path_split[1]) + # Path was a single `~` character, return home path + return userhome + + +fn expanduser[PathLike: os.PathLike, //](path: PathLike) raises -> String: + """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on windows. If + environment variables are not set or the `path` is not prefixed with `~`, + returns the `path` unmodified. + + Parameters: + PathLike: The type conforming to the os.PathLike trait. + + Args: + path: The path that is being expanded. + + Returns: + The expanded path. + """ + return expanduser(path.__fspath__()) + + # ===----------------------------------------------------------------------=== # # isdir # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 6499d25689..ccfb33d7e9 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -192,6 +192,17 @@ struct Path( """ return str(self) == str(other) + fn __eq__(self, other: String) -> Bool: + """Returns True if the two paths are equal. + + Args: + other: The other path to compare against. + + Returns: + True if the String and Path are equal, and False otherwise. + """ + return self.path == other + fn __ne__(self, other: Self) -> Bool: """Returns True if the two paths are not equal. @@ -239,6 +250,26 @@ struct Path( """ return os.path.exists(self) + fn expanduser(self) raises -> Path: + """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on + windows. If environment variables are not set or the `path` is not + prefixed with `~`, returns the `path` unmodified. + + Returns: + The expanded path. + """ + return os.path.expanduser(self) + + @staticmethod + fn home() raises -> Path: + """Returns $HOME on posix or $USERPROFILE on windows. If environment + variables are not set it returns `~`. + + Returns: + Path to user home directory. + """ + return os.path._get_home_path() + fn is_dir(self) -> Bool: """Returns True if the path is a directory and False otherwise. diff --git a/stdlib/test/os/path/test_expanduser.mojo b/stdlib/test/os/path/test_expanduser.mojo new file mode 100644 index 0000000000..b04871215b --- /dev/null +++ b/stdlib/test/os/path/test_expanduser.mojo @@ -0,0 +1,75 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: !windows +# RUN: %mojo %s + + +import os +from os.path import expanduser, join +from os.env import setenv, getenv +from testing import assert_equal, assert_raises +from sys.info import os_is_windows + + +fn get_user_path() -> String: + @parameter + if os_is_windows(): + return join("C:", "Users", "user") + return "/home/user" + + +fn get_current_home() -> String: + @parameter + if os_is_windows(): + return getenv("USERPROFILE") + return getenv("HOME") + + +def set_home(path: String): + @parameter + if os_is_windows(): + _ = os.env.setenv("USERPROFILE", path) + else: + _ = os.env.setenv("HOME", path) + + +fn main() raises: + alias user_path = get_user_path() + var original_home = get_current_home() + set_home(user_path) + + assert_equal(user_path, expanduser("~")) + + # Path with home directory + assert_equal(join(user_path, "folder"), expanduser("~/folder")) + + # Path with trailing slash + assert_equal(join(user_path, "folder/"), expanduser("~/folder/")) + + # Path without user home directory + assert_equal("/usr/bin", expanduser("/usr/bin")) + + # Relative path + assert_equal("../folder", expanduser("../folder")) + + # Empty string + assert_equal("", expanduser("")) + + # Malformed path should raise + with assert_raises(): + _ = expanduser("~badpath/folder") + + # Path with multiple tildes + assert_equal(join(user_path, "~folder"), expanduser("~/~folder")) + + set_home(original_home) diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 389cbe17f2..169ebc391f 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -13,6 +13,7 @@ # REQUIRES: !windows # RUN: %mojo -D TEMP_FILE=%t %s +import os from pathlib import DIR_SEPARATOR, Path, cwd from sys import env_get_string @@ -87,6 +88,58 @@ def test_read_write(): assert_equal(Path(TEMP_FILE).read_text(), "hello") +fn get_user_path() -> Path: + @parameter + if os_is_windows(): + return Path("C:") / "Users" / "user" + return "/home/user" + + +fn get_current_home() -> String: + @parameter + if os_is_windows(): + return os.env.getenv("USERPROFILE") + return os.env.getenv("HOME") + + +def set_home(path: Path): + path_str = str(path) + + @parameter + if os_is_windows(): + _ = os.env.setenv("USERPROFILE", path_str) + else: + _ = os.env.setenv("HOME", path_str) + + +# More elaborate tests in `os/path/test_expanduser.mojo` +def test_expand_user(): + alias user_path = get_user_path() + var original_home = get_current_home() + set_home(user_path) + + path = Path("~") / "test" + test_path = user_path / "test" + assert_equal(test_path, os.path.expanduser(path)) + # Original path should remain unmodified + assert_equal(path, os.path.join("~", "test")) + + # Make sure this process doesn't break other tests by changing the home dir. + set_home(original_home) + + +def test_home(): + alias user_path = get_user_path() + var original_home = get_current_home() + set_home(user_path) + + assert_equal(user_path, Path.home()) + # Match Python behavior allowing `home()` to overwrite existing path. + assert_equal(user_path, Path("test").home()) + # Ensure other tests in this process aren't broken by changing the home dir. + set_home(original_home) + + def main(): test_cwd() test_path() @@ -96,3 +149,5 @@ def main(): test_suffix() test_joinpath() test_read_write() + test_expand_user() + test_home() From 66c8327de14519ea5e36ced0ee2352a0bfab9b36 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 1 Jul 2024 22:51:06 -0700 Subject: [PATCH 1087/2019] Add `os.path.split` and tests, behaves the same as Python equivalent. MODULAR_ORIG_COMMIT_REV_ID: 9de626f3f91d2ec8ae9ac51a8206979be9d8d105 --- docs/changelog.md | 11 +++++ stdlib/src/os/path/__init__.mojo | 1 + stdlib/src/os/path/path.mojo | 49 +++++++++++++++++++++ stdlib/test/os/path/test_split.mojo | 67 +++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 stdlib/test/os/path/test_split.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 9576d4040e..ca42bb8184 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -196,6 +196,17 @@ what we publish. - `Path.home()` has been added to return a path of the users home directory. +- `os.path.split()` has been added for splitting a path into `head, tail`: + + ```mojo + import os + head, tail = os.path.split("/this/is/head/tail") + print("head:", head) + print("tail:", tail) + # head: /this/is/head + # tail: tail + ``` + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index da94f2ec91..a54b57e219 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -20,6 +20,7 @@ from .path import ( isfile, islink, join, + split, lexists, _get_home_path, ) diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index b6da47caee..6df8f25263 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -413,6 +413,55 @@ fn join(path: String, *paths: String) -> String: return joined_path +# ===----------------------------------------------------------------------=== # +# split +# ===----------------------------------------------------------------------=== # + + +def split(path: String) -> (String, String): + """ + Split a given pathname into two components: head and tail. This is useful + for separating the directory path from the filename. If the input path ends + with a seperator, the tail component will be empty. If there is no seperator + in the path, the head component will be empty, and the entire path will be + considered the tail. Trailing seperators in the head are stripped unless the + head is the root directory. + + Args: + path: The path to be split. + + Returns: + A tuple containing two strings: (head, tail). + """ + i = path.rfind(os.sep) + 1 + head, tail = path[:i], path[i:] + if head and head != str(os.sep) * len(head): + head = head.rstrip(sep) + return head, tail + + +def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): + """ + Split a given pathname into two components: head and tail. This is useful + for separating the directory path from the filename. If the input path ends + with a seperator, the tail component will be empty. If there is no seperator + in the path, the head component will be empty, and the entire path will be + considered the tail. Trailing seperators in the head are stripped unless the + head is the root directory. + + Parameters: + PathLike: The type conforming to the os.PathLike trait. + + Args: + path: The path to be split. + + Returns: + A tuple containing two strings: (head, tail). + + """ + return split(path.__fspath__()) + + # TODO uncomment this when unpacking is supported # fn join[pathlike: os.PathLike](path: pathlike, *paths: pathlike) -> String: # """Join two or more pathname components, inserting '/' as needed. diff --git a/stdlib/test/os/path/test_split.mojo b/stdlib/test/os/path/test_split.mojo new file mode 100644 index 0000000000..5192a5766b --- /dev/null +++ b/stdlib/test/os/path/test_split.mojo @@ -0,0 +1,67 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +import os +from os.path import split, expanduser +from pathlib import Path + +from builtin._location import __source_location +from testing import assert_equal + + +def main(): + # Normal case + head, tail = split(os.path.join("a", "b", "c.txt")) + assert_equal(head, os.path.join("a", "b")) + assert_equal(tail, "c.txt") + + # Absolute and empty tail + head, tail = split(Path.home() / "a" / "b" / "") + assert_equal(head, expanduser(os.path.join("~", "a", "b"))) + assert_equal(tail, "") + + # Empty head + head, tail = split("c.txt") + assert_equal(head, "") + assert_equal(tail, "c.txt") + + # Empty head and tail + head, tail = split("") + assert_equal(head, "") + assert_equal(tail, "") + + # Single seperator + head, tail = split(os.sep) + assert_equal(head, os.sep) + assert_equal(tail, "") + + # Two chars, absolute on Linux. + head, tail = split(os.path.join(os.sep, "a")) + assert_equal(head, os.sep) + assert_equal(tail, "a") + + # Two chars relative, empty tail + head, tail = split(os.path.join("a", "")) + assert_equal(head, "a") + assert_equal(tail, "") + + # Test with Path objects + head, tail = split(Path("a") / "b" / "c.txt") + assert_equal(head, os.path.join("a", "b")) + assert_equal(tail, "c.txt") + + # Test with __source_location() + source_location = __source_location().file_name + head, tail = split(source_location) + assert_equal(head + os.sep + tail, source_location) From 0fa02a7e66e6b116c0ce2f5fca8c51f5f6e53417 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 1 Jul 2024 23:33:08 -0700 Subject: [PATCH 1088/2019] Adds `os.path.makedirs` and `os.path.removedirs` for adding and removing nested directories. Follows Python logic. MODULAR_ORIG_COMMIT_REV_ID: 1f928bc01f67b66a498bfdbc28f32d6e4976559a --- docs/changelog.md | 9 ++ stdlib/src/os/__init__.mojo | 2 + stdlib/src/os/os.mojo | 87 +++++++++++++++++- stdlib/test/os/test_mkdir_and_rmdir.mojo | 111 ++++++++++++++--------- 4 files changed, 166 insertions(+), 43 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ca42bb8184..07c6e6a131 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -207,6 +207,15 @@ what we publish. # tail: tail ``` +- `os.path.makedirs()` and `os.path.removedirs()` have been added for creating + and removing nested directories: + + ```mojo + path = os.path.join("dir1", "dir2", "dir3") + os.path.makedirs(path, exist_ok=True) + os.path.removedirs(path) + ``` + ### 🦋 Changed - `await` on a coroutine now consumes it. This strengthens the invariant that diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index 3cc9a67924..9494dc1050 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -22,8 +22,10 @@ from .os import ( abort, listdir, mkdir, + makedirs, remove, rmdir, + removedirs, sep, unlink, ) diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 6af0133d2e..7b5a098eb4 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -27,7 +27,7 @@ from memory import DTypePointer from utils import InlineArray, StringRef -from .path import isdir +from .path import isdir, split from .pathlike import PathLike # TODO move this to a more accurate location once nt/posix like modules are in stdlib @@ -366,6 +366,55 @@ fn mkdir[pathlike: os.PathLike](path: pathlike, mode: Int = 0o777) raises: mkdir(path.__fspath__(), mode) +def makedirs(path: String, mode: Int = 0o777, exist_ok: Bool = False) -> None: + """Creates a specified leaf directory along with any necessary intermediate + directories that don't already exist. + + Args: + path: The path to the directory. + mode: The mode to create the directory with. + exist_ok: Ignore error if `True` and path exists (default `False`). + """ + head, tail = split(path) + if not tail: + head, tail = split(head) + if head and tail and not os.path.exists(head): + try: + makedirs(head, exist_ok=exist_ok) + except: + # Defeats race condition when another thread created the path + pass + # xxx/newdir/. exists if xxx/newdir exists + if tail == ".": + return None + try: + mkdir(path, mode) + except e: + if not exist_ok: + raise str( + e + ) + "\nset `makedirs(path, exist_ok=True)` to allow existing dirs" + if not os.path.isdir(path): + raise "path not created: " + path + "\n" + str(e) + + +def makedirs[ + pathlike: os.PathLike +](path: pathlike, mode: Int = 0o777, exist_ok: Bool = False) -> None: + """Creates a specified leaf directory along with any necessary intermediate + directories that don't already exist. + + Parameters: + pathlike: The a type conforming to the os.PathLike trait. + + Args: + path: The path to the directory. + mode: The mode to create the directory with. + exist_ok: Ignore error if `True` and path exists (default `False`). + """ + makedirs(path.__fspath__(), mode, exist_ok) + + fn rmdir(path: String) raises: """Removes the specified directory. If the path is not a directory or it can not be deleted, an error is raised. @@ -391,3 +440,39 @@ fn rmdir[pathlike: os.PathLike](path: pathlike) raises: path: The path to the directory. """ rmdir(path.__fspath__()) + + +def removedirs(path: String) -> None: + """Remove a leaf directory and all empty intermediate ones. Directories + corresponding to rightmost path segments will be pruned away until either + the whole path is consumed or an error occurs. Errors during this latter + phase are ignored, which occur when a directory was not empty. + + Args: + path: The path to the directory. + """ + rmdir(path) + head, tail = os.path.split(path) + if not tail: + head, tail = os.path.split(head) + while head and tail: + try: + rmdir(head) + except: + break + head, tail = os.path.split(head) + + +def removedirs[pathlike: os.PathLike](path: pathlike) -> None: + """Remove a leaf directory and all empty intermediate ones. Directories + corresponding to rightmost path segments will be pruned away until either + the whole path is consumed or an error occurs. Errors during this latter + phase are ignored, which occur when a directory was not empty. + + Parameters: + pathlike: The a type conforming to the os.PathLike trait. + + Args: + path: The path to the directory. + """ + removedirs(path.__fspath__()) diff --git a/stdlib/test/os/test_mkdir_and_rmdir.mojo b/stdlib/test/os/test_mkdir_and_rmdir.mojo index caeb7e63c4..f9f58086e7 100644 --- a/stdlib/test/os/test_mkdir_and_rmdir.mojo +++ b/stdlib/test/os/test_mkdir_and_rmdir.mojo @@ -12,68 +12,90 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from os import mkdir, remove, rmdir +import os from os.path import exists from pathlib import Path from testing import assert_false, assert_raises, assert_true -fn create_dir_and_test_delete_string[ - func_create: fn (String, Int) raises -> None, - func_delete: fn (String) raises -> None, -](dir_name: String) raises: +fn create_and_delete(path: String) raises: # verify that the test dir does not exist before starting the test assert_false( - exists(dir_name), - "Unexpected dir " + dir_name + " it should not exist", + exists(path), + "Unexpected dir " + path + " it should not exist", ) - func_create(dir_name, 0o777) - assert_true(exists(dir_name)) + os.mkdir(path, 0o777) + assert_true(exists(path)) - func_delete(dir_name) + os.rmdir(path) # trying to delete non existing dir with assert_raises(contains="Can not remove directory: "): - func_delete(dir_name) + os.rmdir(path) -fn create_dir_and_test_delete_path[ - func_create: fn[pathlike: PathLike] (pathlike, Int) raises -> None, - func_delete: fn[pathlike: PathLike] (pathlike) raises -> None, -](dir_path: Path) raises: +fn test_mkdir_and_rmdir(path: String) raises: + try: + os.rmdir(path) + except: + pass # verify that the test dir does not exist before starting the test assert_false( - exists(dir_path), - "Unexpected dir " + dir_path.__fspath__() + " it should not exist", + exists(path), + "Unexpected dir " + str(path) + " it should not exist", ) - func_create(dir_path, 0o777) - assert_true(exists(dir_path)) + os.mkdir(path, 0o777) + assert_true(exists(path)) - func_delete(dir_path) + os.rmdir(path) # trying to delete non existing dir with assert_raises(contains="Can not remove directory: "): - func_delete(dir_path) + os.rmdir(path) + + +fn test_mkdir_and_rmdir(path: Path) raises: + try: + os.rmdir(path) + except: + pass + # verify that the test dir does not exist before starting the test + assert_false( + exists(path), + "Unexpected dir " + str(path) + " it should not exist", + ) + os.mkdir(path, 0o777) + assert_true(exists(path)) -fn test_mkdir_and_rmdir() raises: - var cwd_path = Path() - var my_dir_path = cwd_path / "my_dir" - var my_dir_name = str(my_dir_path) + os.rmdir(path) + # trying to delete non existing dir + with assert_raises(contains="Can not remove directory: "): + os.rmdir(path) - create_dir_and_test_delete_path[mkdir, rmdir](my_dir_path) - create_dir_and_test_delete_string[mkdir, rmdir](my_dir_name) - # test relative path - create_dir_and_test_delete_string[mkdir, rmdir]("my_relative_dir") - create_dir_and_test_delete_path[mkdir, rmdir](Path("my_relative_dir")) +fn test_makedirs_and_removedirs(path: Path) raises: + try: + os.removedirs(path) + except: + pass + # verify that the test dir does not exist before starting the test + assert_false( + exists(path), + "Unexpected dir " + str(path) + " it should not exist", + ) + os.makedirs(path, exist_ok=True) + assert_true(exists(path)) + with assert_raises(): + os.makedirs(path) + # Make sure this doesn't throw error + os.makedirs(path, exist_ok=True) + os.removedirs(path) fn test_mkdir_mode() raises: - var cwd_path = Path() - var my_dir_path = cwd_path / "my_dir" - var file_name = my_dir_path / "file.txt" + var my_dir_path = Path("my_dir") assert_false( exists(my_dir_path), @@ -81,7 +103,7 @@ fn test_mkdir_mode() raises: ) # creating dir without writing permission - mkdir(my_dir_path, 0o111) + os.mkdir(my_dir_path, 0o111) # TODO: This test is failing on Graviton internally in CI, revisit. # with assert_raises(contains="Permission denied"): @@ -91,12 +113,11 @@ fn test_mkdir_mode() raises: # remove(file_name) if exists(my_dir_path): - rmdir(my_dir_path) + os.rmdir(my_dir_path) fn test_rmdir_not_empty() raises: - var cwd_path = Path() - var my_dir_path = cwd_path / "my_dir" + var my_dir_path = Path("my_dir") var file_name = my_dir_path / "file.txt" assert_false( @@ -104,19 +125,25 @@ fn test_rmdir_not_empty() raises: "Unexpected dir " + my_dir_path.__fspath__() + " it should not exist", ) - mkdir(my_dir_path) + os.mkdir(my_dir_path) with open(file_name, "w"): pass with assert_raises(contains="Can not remove directory: "): - rmdir(my_dir_path) + os.rmdir(my_dir_path) - remove(file_name) - rmdir(my_dir_path) + os.remove(file_name) + os.rmdir(my_dir_path) assert_false(exists(my_dir_path), "Failed to remove dir") def main(): - test_mkdir_and_rmdir() + test_mkdir_and_rmdir("my_dir") + test_mkdir_and_rmdir(Path("my_dir")) + test_mkdir_and_rmdir(Path.home() / "my_dir") + + test_makedirs_and_removedirs(os.path.join("dir1", "dir2", "dir3")) + test_makedirs_and_removedirs(Path("dir1") / "dir2" / "dir3") + test_mkdir_mode() test_rmdir_not_empty() From 273192a23c030383988f0ab09368d3ea04617b80 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Jul 2024 17:22:22 +0300 Subject: [PATCH 1089/2019] [stdlib] Remove `is{upper,lower,digit,printable}` free functions They are a footgun since they don't actually enforce single character strings. Moreover, their functionality is entirely replacable by the identically named methods on `String`. This patch also changes the behavior of `String.is{upper,lower}` so that it follows Python's behavior when there are no cased characters in a string. Note that Unicode support remains lacking for these methods. MODULAR_ORIG_COMMIT_REV_ID: 81d0ba67ac601a0bf16098f9b29def3d488eb8ca --- stdlib/src/builtin/string.mojo | 119 ++++++++++----------------- stdlib/test/builtin/test_string.mojo | 20 ++--- 2 files changed, 52 insertions(+), 87 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index d0f7b499a5..b017dea3ef 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -539,39 +539,11 @@ fn isdigit(c: UInt8) -> Bool: return ord_0 <= int(c) <= ord_9 -fn isdigit(c: String) -> Bool: - """Determines whether the given character is a digit [0-9]. - - Args: - c: The character to check. - - Returns: - True if the character is a digit. - """ - return isdigit(ord(c)) - - # ===----------------------------------------------------------------------=== # # isupper # ===----------------------------------------------------------------------=== # -@always_inline -fn isupper(c: String) -> Bool: - """Determines whether the given character is an uppercase character. - - This currently only respects the default "C" locale, i.e. returns True iff - the character specified is one of "ABCDEFGHIJKLMNOPQRSTUVWXYZ". - - Args: - c: The character to check. - - Returns: - True if the character is uppercase. - """ - return isupper(ord(c)) - - fn isupper(c: UInt8) -> Bool: """Determines whether the given character is an uppercase character. @@ -598,22 +570,6 @@ fn _is_ascii_uppercase(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # -@always_inline -fn islower(c: String) -> Bool: - """Determines whether the given character is an lowercase character. - - This currently only respects the default "C" locale, i.e. returns True iff - the character specified is one of "abcdefghijklmnopqrstuvwxyz". - - Args: - c: The character to check. - - Returns: - True if the character is lowercase. - """ - return islower(ord(c)) - - fn islower(c: UInt8) -> Bool: """Determines whether the given character is an lowercase character. @@ -736,19 +692,6 @@ fn _isnewline(s: String) -> Bool: # ===----------------------------------------------------------------------=== # -@always_inline -fn isprintable(c: String) -> Bool: - """Determines whether the given character is a printable character. - - Args: - c: The character to check. - - Returns: - True if the character is a printable character, otherwise False. - """ - return isprintable(ord(c)) - - fn isprintable(c: UInt8) -> Bool: """Determines whether the given character is a printable character. @@ -1209,7 +1152,8 @@ struct String( rhs: The other String to compare against. Returns: - True if this String is strictly less than the RHS String and False otherwise. + True if this String is strictly less than the RHS String and False + otherwise. """ return self._strref_dangerous() < rhs._strref_dangerous() @@ -2287,34 +2231,68 @@ struct String( fn isdigit(self) -> Bool: """Returns True if all characters in the string are digits. + Note that this currently only works with ASCII strings. + Returns: True if all characters are digits else False. """ - return _all[isdigit](self) + for c in self: + if not isdigit(ord(c)): + return False + return True + + @always_inline + fn _isupper_islower[*, upper: Bool](self) -> Bool: + fn is_ascii_cased(c: UInt8) -> Bool: + return _is_ascii_uppercase(c) or _is_ascii_lowercase(c) + + for c in self: + debug_assert(c._byte_length() == 1, "only implemented for ASCII") + if is_ascii_cased(ord(c)): + + @parameter + if upper: + return self == self.upper() + else: + return self == self.lower() + return False fn isupper(self) -> Bool: - """Returns True if all characters in the string are uppercase. + """Returns True if all cased characters in the string are uppercase and + there is at least one cased character. + + Note that this currently only works with ASCII strings. Returns: - True if all characters are uppercase else False. + True if all cased characters in the string are uppercase and there + is at least one cased character, False otherwise. """ - return _all[isupper](self) + return self._isupper_islower[upper=True]() fn islower(self) -> Bool: - """Returns True if all characters in the string are lowercase. + """Returns True if all cased characters in the string are lowercase and + there is at least one cased character. + + Note that this currently only works with ASCII strings. Returns: - True if all characters are lowercase else False. + True if all cased characters in the string are lowercase and there + is at least one cased character, False otherwise. """ - return _all[islower](self) + return self._isupper_islower[upper=False]() fn isprintable(self) -> Bool: - """Returns True if all characters in the string are printable. + """Returns True if all characters in the string are ASCII printable. + + Note that this currently only works with ASCII strings. Returns: True if all characters are printable else False. """ - return _all[isprintable](self) + for c in self: + if not isprintable(ord(c)): + return False + return True # ===----------------------------------------------------------------------=== # @@ -2322,13 +2300,6 @@ struct String( # ===----------------------------------------------------------------------=== # -fn _all[func: fn (String) -> Bool](s: String) -> Bool: - for c in s: - if not func(c): - return False - return True - - fn _toggle_ascii_case(char: UInt8) -> UInt8: """Assuming char is a cased ASCII character, this function will return the opposite-cased letter. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index dc93a0d136..2862477465 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -850,10 +850,10 @@ fn test_isupper() raises: assert_false(isupper(ord("!"))) assert_false(isupper(ord("0"))) - assert_true(isupper("A")) - assert_false(isupper("b")) assert_true(String("ASDG").isupper()) assert_false(String("AsDG").isupper()) + assert_true(String("ABC123").isupper()) + assert_false(String("1!").isupper()) fn test_islower() raises: @@ -868,11 +868,10 @@ fn test_islower() raises: assert_false(islower(ord("!"))) assert_false(islower(ord("0"))) - assert_false(islower("A")) - assert_true(islower("b")) - assert_true(String("asdfg").islower()) assert_false(String("asdFDg").islower()) + assert_true(String("abc123").islower()) + assert_false(String("1!").islower()) fn test_lower() raises: @@ -1381,11 +1380,8 @@ def test_format_args(): def test_isdigit(): assert_true(isdigit(ord("1"))) - assert_true(isdigit("1")) - # TODO: What to do with multi-character strings? - # assert_false(isdigit("1gt")) assert_false(isdigit(ord("g"))) - assert_false(isdigit("g")) + assert_true(String("123").isdigit()) assert_false(String("asdg").isdigit()) assert_false(String("123asdg").isdigit()) @@ -1393,14 +1389,12 @@ def test_isdigit(): def test_isprintable(): assert_true(isprintable(ord("a"))) - assert_true(isprintable("a")) - assert_true(isprintable("J")) - assert_false(isprintable(ord("\n"))) - assert_false(isprintable("\t")) + assert_false(isprintable(ord("\t"))) assert_true(String("aasdg").isprintable()) assert_false(String("aa\nae").isprintable()) + assert_false(String("aa\tae").isprintable()) def main(): From 67d623b8e29b557d9d51c1dc09712c21d90bc036 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Tue, 2 Jul 2024 09:45:30 -0500 Subject: [PATCH 1090/2019] [External] [stdlib] Implement `collections.Counter` (#42560) [External] [stdlib] Implement `collections.Counter` Support for Python's [collections.Counter](https://docs.python.org/3/library/collections.html#collections.Counter) Done: - [X] Implement some of the `Dict` methods: `keys()`, `values()`, `get()`, `pop()`, `clear()`, `update()` - [X] Implement some specific `Counter` methods: `total()`, `most_common()`, `elements()`, `substract()` - [X] Implement `__add__`, `__iadd__`, `__sub__` and `__isub__` - [x] Implement `__eq__`, `__lt__`, `__le__`, `__gt__`, `__ge__`. Pending to match Python signature: - [ ] Ideally, it should accept all the iterable types, but for now, it will only work with `List`. I think we need to wait for proper support from Mojo - [ ] More `Dict` methods, like `popitem()`, `fromkeys()`, `__contains__()`, etc. - [ ] Implement `__sub__` and `__isub__` - [ ] Implement `__and__`, `__iand__`, `__or__`, `__ior__` and `__neg__` Ticket: https://github.com/modularml/mojo/issues/2908 Closes modularml/mojo#2910 MODULAR_ORIG_COMMIT_REV_ID: 0de65221b774d1c358dcb8a3cad1cbaf30fb89f0 --- docs/changelog.md | 5 + stdlib/src/collections/counter.mojo | 462 +++++++++++++++++++++- stdlib/test/collections/test_counter.mojo | 301 ++++++++++++++ 3 files changed, 759 insertions(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 07c6e6a131..f9e8979ee1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,11 @@ what we publish. ### ⭐️ New +- Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) + dictionary-like type, matching most of the features of the Python one. + ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by + [@msaelices](https://github.com/msaelices)) + - Mojo context managers used in regions of code that may raise no longer need to define a "conditional" exit function in the form of `fn __exit__(self, e: Error) -> Bool`. This function allows the context diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index b584abbbc0..4e78a7f9a4 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -11,14 +11,11 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from collections.dict import Dict +from collections.dict import Dict, _DictKeyIter, _DictValueIter, _DictEntryIter -struct Counter[V: KeyElement]( - # Sized, - # CollectionElement, - # Boolable -): +@value +struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """A container for counting hashable items. The value type must be specified statically, unlike a Python @@ -39,14 +36,19 @@ struct Counter[V: KeyElement]( V: The value type to be counted. Currently must be KeyElement. """ + # Fields var _data: Dict[V, Int] - def __init__(inout self): + # ===------------------------------------------------------------------=== # + # Life cycle methods + # ===------------------------------------------------------------------=== # + + fn __init__(inout self): """Create a new, empty Counter object.""" self._data = Dict[V, Int]() # TODO: Change List to Iterable when it is supported in Mojo - def __init__(inout self, items: List[V]): + fn __init__(inout self, items: List[V]): """Create a from an input iterable. Args: @@ -57,6 +59,10 @@ struct Counter[V: KeyElement]( var item = item_ref[] self._data[item] = self._data.get(item, 0) + 1 + # ===------------------------------------------------------------------=== # + # Operator dunders + # ===------------------------------------------------------------------=== # + def __getitem__(self, key: V) -> Int: """Get the count of a key. @@ -66,7 +72,7 @@ struct Counter[V: KeyElement]( Returns: The count of the key. """ - return self._data.get(key, 0) + return self.get(key, 0) fn __setitem__(inout self, value: V, count: Int): """Set a value in the keyword Counter by key. @@ -76,3 +82,441 @@ struct Counter[V: KeyElement]( count: The count to store in the Counter. """ self._data[value] = count + + fn __iter__(self: Self) -> _DictKeyIter[V, Int, __lifetime_of(self)]: + """Iterate over the keyword dict's keys as immutable references. + + Returns: + An iterator of immutable references to the Counter values. + """ + return self._data.__iter__() + + fn __contains__(self, key: V) -> Bool: + """Check if a given key is in the dictionary or not. + + Args: + key: The key to check. + + Returns: + True if there key exists in the dictionary, False otherwise. + """ + return key in self._data + + # ===------------------------------------------------------------------=== # + # Trait implementations + # ===------------------------------------------------------------------=== # + + fn __len__(self) -> Int: + """Returns the number of elements currently stored in the Counter. + + Returns: + The number of elements currently stored in the Counter. + """ + return len(self._data) + + fn __bool__(self) -> Bool: + """Check if the Counter is empty or not. + + Returns: + `False` if the Counter is empty, `True` otherwise. + """ + return bool(len(self)) + + # ===------------------------------------------------------------------=== # + # Comparison operators + # ===------------------------------------------------------------------=== # + + fn __eq__(self, other: Self) -> Bool: + """Check if all counts agree. Missing counts are treated as zero. + + Args: + other: The other Counter to compare to. + + Returns: + True if the two Counters are equal, False otherwise. + """ + + @parameter + @always_inline + fn is_eq(keys: _DictKeyIter[V, Int, _]) -> Bool: + for e_ref in keys: + var e = e_ref[] + if self.get(e, 0) != other.get(e, 0): + return False + return True + + return is_eq(self.keys()) and is_eq(other.keys()) + + fn __ne__(self, other: Self) -> Bool: + """Check if all counts disagree. Missing counts are treated as zero. + + Args: + other: The other Counter to compare to. + + Returns: + True if the two Counters are not equal, False otherwise. + """ + return not self == other + + fn __le__(self, other: Self) -> Bool: + """Check if all counts are less than or equal to the other Counter. + + Args: + other: The other Counter to compare to. + + Returns: + True if all counts are less than or equal to the other Counter, + False otherwise. + """ + + @parameter + @always_inline + fn is_le(keys: _DictKeyIter[V, Int, _]) -> Bool: + for e_ref in keys: + var e = e_ref[] + if self.get(e, 0) > other.get(e, 0): + return False + return True + + return is_le(self.keys()) + + fn __lt__(self, other: Self) -> Bool: + """Check if all counts are less than in the other Counter. + + Args: + other: The other Counter to compare to. + + Returns: + True if all counts are less than in the other Counter, False + otherwise. + """ + return self <= other and self != other + + fn __gt__(self, other: Self) -> Bool: + """Check if all counts are greater than in the other Counter. + + Args: + other: The other Counter to compare to. + + Returns: + True if all counts are greater than in the other Counter, False + otherwise. + """ + return other < self + + fn __ge__(self, other: Self) -> Bool: + """Check if all counts are greater than or equal to the other Counter. + + Args: + other: The other Counter to compare to. + + Returns: + True if all counts are greater than or equal to the other Counter, + False otherwise. + """ + return other <= self + + # ===------------------------------------------------------------------=== # + # Binary operators + # ===------------------------------------------------------------------=== # + + fn __add__(self, other: Self) -> Self: + """Add counts from two Counters. + + Args: + other: The other Counter to add to this Counter. + + Returns: + A new Counter with the counts from both Counters added together. + """ + var result = Counter[V]() + + result.update(self) + result.update(other) + + return +result^ # Remove zero and negative counts + + fn __iadd__(inout self, other: Self) raises: + """Add counts from another Counter to this Counter. + + Args: + other: The other Counter to add to this Counter. + """ + self.update(other) + self._keep_positive() + + fn _keep_positive(inout self) raises: + """Remove zero and negative counts from the Counter.""" + for key_ref in self.keys(): + var key = key_ref[] + if self[key] <= 0: + _ = self.pop(key) + + # ===------------------------------------------------------------------=== # + # Unary operators + # ===------------------------------------------------------------------=== # + + fn __pos__(self) -> Self: + """Return a shallow copy of the Counter. + + Returns: + A shallow copy of the Counter. + """ + var result = Counter[V]() + for item_ref in self.items(): + var item = item_ref[] + if item.value > 0: + result[item.key] = item.value + return result^ + + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # + + fn get(self, value: V) -> Optional[Int]: + """Get a value from the counter. + + Args: + value: The value to search for in the Counter. + + Returns: + An optional value containing a copy of the value if it was present, + otherwise an empty Optional. + """ + return self._data.get(value) + + fn get(self, value: V, default: Int) -> Int: + """Get a value from the Counter. + + Args: + value: The value to search for in the counter. + default: Default count to return. + + Returns: + A copy of the value if it was present, otherwise default. + """ + return self._data.get(value, default) + + fn pop(inout self, value: V) raises -> Int: + """Remove a value from the Counter by value. + + Args: + value: The value to remove from the Counter. + + Returns: + The value associated with the key, if it was in the Counter. + + Raises: + "KeyError" if the key was not present in the Counter. + """ + return self._data.pop(value) + + fn pop(inout self, value: V, owned default: Int) raises -> Int: + """Remove a value from the Counter by value. + + Args: + value: The value to remove from the Counter. + default: Optionally provide a default value to return if the value + was not found instead of raising. + + Returns: + The value associated with the key, if it was in the Counter. + If it wasn't, return the provided default value instead. + + Raises: + "KeyError" if the key was not present in the Counter and no + default value was provided. + """ + return self._data.pop(value, default) + + fn keys(ref [_]self: Self) -> _DictKeyIter[V, Int, __lifetime_of(self)]: + """Iterate over the Counter's keys as immutable references. + + Returns: + An iterator of immutable references to the Counter keys. + """ + return self._data.keys() + + fn values(ref [_]self: Self) -> _DictValueIter[V, Int, __lifetime_of(self)]: + """Iterate over the Counter's values as references. + + Returns: + An iterator of references to the Counter values. + """ + return self._data.values() + + fn items(self: Self) -> _DictEntryIter[V, Int, __lifetime_of(self)]: + """Iterate over the dict's entries as immutable references. + + Returns: + An iterator of immutable references to the Counter entries. + """ + return self._data.items() + + fn clear(inout self): + """Remove all elements from the Counter.""" + self._data.clear() + + # Special methods for counter + + fn total(self) -> Int: + """Return the total of all counts in the Counter. + + Returns: + The total of all counts in the Counter. + """ + var total = 0 + for count_ref in self.values(): + total += count_ref[] + return total + + fn most_common(self, n: Int) -> List[CountTuple[V]]: + """Return a list of the `n` most common elements and their counts from + the most common to the least. + + Args: + n: The number of most common elements to return. + + Returns: + A list of the n most common elements and their counts. + """ + var items: List[CountTuple[V]] = List[CountTuple[V]]() + for item_ref in self._data.items(): + var item = item_ref[] + var t = CountTuple[V](item.key, item.value) + items.append(t) + + @parameter + fn comparator(a: CountTuple[V], b: CountTuple[V]) -> Bool: + return a < b + + sort[type = CountTuple[V], cmp_fn=comparator](items) + return items[:n] + + fn elements(self) -> List[V]: + """Return an iterator over elements repeating each as many times as its + count. + + Returns: + An iterator over the elements in the Counter. + """ + var elements: List[V] = List[V]() + for item_ref in self._data.items(): + var item = item_ref[] + for _ in range(item.value): + elements.append(item.key) + return elements + + fn update(inout self, other: Self): + """Update the Counter, like `dict.update()` but add counts instead of + replacing them. + + Args: + other: The Counter to update this Counter with. + """ + for item_ref in other.items(): + var item = item_ref[] + self._data[item.key] = self._data.get(item.key, 0) + item.value + + fn subtract(inout self, other: Self): + """Subtract count. Both inputs and outputs may be zero or negative. + + Args: + other: The Counter to subtract from this Counter. + """ + for item_ref in other.items(): + var item = item_ref[] + self[item.key] = self.get(item.key, 0) - item.value + + +struct CountTuple[V: KeyElement]( + CollectionElement, +): + """A tuple representing a value and its count in a Counter. + + Parameters: + V: The value in the Counter. + """ + + # Fields + var _value: V + """ The value in the Counter.""" + var _count: Int + """ The count of the value in the Counter.""" + + # ===------------------------------------------------------------------=== # + # Life cycle methods + # ===------------------------------------------------------------------=== # + + fn __init__(inout self, value: V, count: Int): + """Create a new CountTuple. + + Args: + value: The value in the Counter. + count: The count of the value in the Counter. + """ + self._value = value + self._count = count + + fn __copyinit__(inout self, other: Self): + """Create a new CountTuple by copying another CountTuple. + + Args: + other: The CountTuple to copy. + """ + self._value = other._value + self._count = other._count + + fn __moveinit__(inout self, owned other: Self): + """Create a new CountTuple by moving another CountTuple. + + Args: + other: The CountTuple to move. + """ + self._value = other._value^ + self._count = other._count + + # ===------------------------------------------------------------------=== # + # Operator dunders + # ===------------------------------------------------------------------=== # + + fn __lt__(self, other: Self) -> Bool: + """Compare two CountTuples by count, then by value. + + Args: + other: The other CountTuple to compare to. + + Returns: + True if this CountTuple is less than the other, False otherwise. + """ + return self._count > other._count + + fn __eq__(self, other: Self) -> Bool: + """Compare two CountTuples for equality. + + Args: + other: The other CountTuple to compare to. + + Returns: + True if the two CountTuples are equal, False otherwise. + """ + return self._count == other._count + + @always_inline("nodebug") + fn __getitem__(self, idx: Int) -> Variant[V, Int]: + """Get an element in the tuple. + + Args: + idx: The element to return. + + Returns: + The value if idx is 0 and the count if idx is 1. + """ + debug_assert( + 0 <= idx <= 1, + "index must be within bounds", + ) + if idx == 0: + return self._value + else: + return self._count diff --git a/stdlib/test/collections/test_counter.mojo b/stdlib/test/collections/test_counter.mojo index e2afa7cb54..d02245a9b4 100644 --- a/stdlib/test/collections/test_counter.mojo +++ b/stdlib/test/collections/test_counter.mojo @@ -17,6 +17,36 @@ from collections.counter import Counter from testing import assert_equal, assert_false, assert_raises, assert_true +def test_bool(): + var c = Counter[String]() + assert_false(c) + c["a"] = 1 + assert_true(c) + c.pop("a") + assert_false(c) + + +def test_clear(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + c.clear() + + assert_equal(len(c), 0) + assert_false(c) + + +def test_contains(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + assert_true("a" in c) + assert_true("b" in c) + assert_false("c" in c) + + def test_counter_construction(): _ = Counter[Int]() _ = Counter[Int](List[Int]()) @@ -32,6 +62,244 @@ def test_counter_getitem(): assert_equal(c[5], 0) +def test_get(): + var counter = Counter[String]() + counter["a"] = 1 + counter["b"] = 2 + + var a: Int = counter.get("a").value() + var b: Int = counter.get("b").value() + var c: Int = counter.get("c", 3) + + var d: Optional[Int] = counter.get("d") + + assert_equal(a, 1) + assert_equal(b, 2) + assert_equal(c, 3) + assert_false(d) + + +def test_iter(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + var keys = String("") + for key in c: + keys += key[] + + assert_equal(keys, "ab") + + +def test_iter_keys(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + var keys = String("") + for key in c.keys(): + keys += key[] + + assert_equal(keys, "ab") + + +def test_iter_values(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + var sum = 0 + for value in c.values(): + sum += value[] + + assert_equal(sum, 3) + + +def test_iter_values_mut(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + for value in c.values(): + value[] += 1 + + assert_equal(2, c["a"]) + assert_equal(3, c["b"]) + assert_equal(2, len(c)) + + +def test_iter_items(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + var keys = String("") + var sum = 0 + for entry in c.items(): + keys += entry[].key + sum += entry[].value + + assert_equal(keys, "ab") + assert_equal(sum, 3) + + +def test_len(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + assert_equal(len(c), 2) + c.pop("a") + assert_equal(len(c), 1) + c.clear() + assert_equal(len(c), 0) + + +def test_total(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + assert_equal(c.total(), 3) + + +def test_most_common(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + c["c"] = 3 + + var most_common = c.most_common(2) + assert_equal(len(most_common), 2) + assert_equal(most_common[0][0][String], "c") + assert_equal(most_common[0][1][Int], 3) + assert_equal(most_common[1][0][String], "b") + assert_equal(most_common[1][1][Int], 2) + + +def test_eq_and_ne(): + var c1 = Counter[String]() + c1["a"] = 1 + c1["b"] = 2 + c1["d"] = 0 + + var c2 = Counter[String]() + c2["a"] = 1 + c2["b"] = 2 + c2["c"] = 0 + + assert_true(c1.__eq__(c2)) + assert_false(c1.__ne__(c2)) + + c2["b"] = 3 + assert_false(c1.__eq__(c2)) + assert_true(c1.__ne__(c2)) + + +def test_lt_le_gt_and_ge(): + var c1 = Counter[String]() + c1["a"] = 1 + c1["b"] = 2 + c1["d"] = 0 + + var c2 = Counter[String]() + c2["a"] = 1 + c2["b"] = 2 + c2["c"] = 0 + + assert_false(c1.__lt__(c2)) + assert_true(c1.__le__(c2)) + assert_false(c1.__gt__(c2)) + assert_true(c1.__ge__(c2)) + + c2["b"] = 3 + assert_true(c1.__lt__(c2)) + assert_true(c1.__le__(c2)) + assert_false(c1.__gt__(c2)) + assert_true(c2.__gt__(c1)) + assert_false(c1.__ge__(c2)) + + +def test_elements(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + c["c"] = 3 + + var elements = c.elements() + + assert_equal(len(elements), 6) + assert_equal(elements[0], "a") + assert_equal(elements[1], "b") + assert_equal(elements[2], "b") + assert_equal(elements[3], "c") + assert_equal(elements[4], "c") + assert_equal(elements[5], "c") + + +def test_update(): + var c1 = Counter[String]() + c1["a"] = 1 + c1["b"] = 2 + + var c2 = Counter[String]() + c2["b"] = 3 + c2["c"] = 4 + + c1.update(c2) + + assert_equal(c1["a"], 1) + assert_equal(c1["b"], 5) + assert_equal(c1["c"], 4) + + +def test_add(): + var c1 = Counter[String]() + c1["a"] = 3 + c1["b"] = 2 + c1["d"] = -1 # should be ignored + + var c2 = Counter[String]() + c2["a"] = -2 + c2["b"] = 3 + c2["c"] = 4 + c2["e"] = 0 # should be ignored + + var c3 = c1 + c2 + + assert_equal(c3["a"], 1) + assert_equal(c3["b"], 5) + assert_equal(c3["c"], 4) + # Check that the original counters are not modified + assert_equal(c1["a"], 3) + assert_equal(c1["b"], 2) + assert_equal(c1["d"], -1) + + c2 += c1 + + assert_equal(c2["a"], 1) + assert_equal(c2["b"], 5) + assert_equal(c2["c"], 4) + + +def test_substract(): + var c1 = Counter[String]() + c1["a"] = 4 + c1["b"] = 2 + c1["c"] = 0 + + var c2 = Counter[String]() + c2["a"] = 1 + c2["b"] = -2 + c2["c"] = 3 + + c1.subtract(c2) + + assert_equal(c1["a"], 3) + assert_equal(c1["b"], 4) + assert_equal(c1["c"], -3) + + def test_counter_setitem(): c = Counter[Int]() c[1] = 1 @@ -41,7 +309,40 @@ def test_counter_setitem(): assert_equal(c[3], 0) +def test_pop(): + var counter = Counter[String]() + counter["a"] = 1 + counter["b"] = 2 + + var a = counter.pop("a") + var b = counter.pop("b") + var c = counter.pop("c", 3) + + assert_equal(a, 1) + assert_equal(b, 2) + assert_equal(c, 3) + + def main(): + test_add() + test_bool() + test_clear() + test_contains() test_counter_construction() test_counter_getitem() test_counter_setitem() + test_elements() + test_eq_and_ne() + test_get() + test_iter() + test_iter_keys() + test_iter_items() + test_iter_values() + test_iter_values_mut() + test_len() + test_lt_le_gt_and_ge() + test_most_common() + test_pop() + test_substract() + test_total() + test_update() From fb073ed2a70078ce1a2724f89110ebdbb5619108 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Jul 2024 17:45:56 +0300 Subject: [PATCH 1091/2019] [stdlib] Make `UInt` conform to `Formattable` Also simplify the implementation of `Int.format_to`. MODULAR_ORIG_COMMIT_REV_ID: 73df9c119ecbe719e8e2a8072b4701b20aa1adfd --- stdlib/src/builtin/uint.mojo | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index a86fe6570c..ade5688300 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -15,11 +15,14 @@ These are Mojo built-ins, so you don't need to import them. """ +from builtin.format_int import _try_write_int +from builtin.simd import _format_scalar + @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct UInt(Comparable, Representable, Stringable): +struct UInt(Comparable, Formattable, Representable, Stringable): """This type represents an unsigned integer. An unsigned integer is represents a positive integral number. @@ -90,7 +93,7 @@ struct UInt(Comparable, Representable, Stringable): Returns: The string representation of this UInt. """ - return str(UInt64(self)) + return String.format_sequence(self) @always_inline("nodebug") fn __repr__(self) -> String: @@ -745,3 +748,21 @@ struct UInt(Comparable, Representable, Stringable): The self value. """ return self + + fn format_to(self, inout writer: Formatter): + """Formats this integer to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + @parameter + if triple_is_nvidia_cuda(): + var err = _try_write_int(writer, UInt64(self)) + if err: + abort( + "unreachable: unexpected write Uint failure condition: " + + str(err.value()) + ) + else: + _format_scalar(writer, UInt64(self)) From f7cc333adc8f469cfa5af82ca1a353e2c7681250 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Jul 2024 18:07:21 +0300 Subject: [PATCH 1092/2019] [stdlib][NFC] Use `def` instead of `fn` with `raising` in `test_string.mojo` MODULAR_ORIG_COMMIT_REV_ID: d6c381c68fe6a61f55a5decac2091601b5fdf743 --- stdlib/test/builtin/test_string.mojo | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 2862477465..541ef040a3 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -37,14 +37,14 @@ struct AString(Stringable): return "a string" -fn test_stringable() raises: +def test_stringable(): assert_equal("hello", str("hello")) assert_equal("0", str(0)) assert_equal("AAA", str(StringRef("AAA"))) assert_equal("a string", str(AString())) -fn test_repr() raises: +def test_repr(): # Usual cases assert_equal(String.__repr__("hello"), "'hello'") assert_equal(String.__repr__(str(0)), "'0'") @@ -65,7 +65,7 @@ fn test_repr() raises: assert_equal(String.__repr__("\x7f"), r"'\x7f'") -fn test_constructors() raises: +def test_constructors(): # Default construction assert_equal(0, len(String())) assert_true(not String()) @@ -99,7 +99,7 @@ fn test_constructors() raises: assert_equal(s4, "2") -fn test_copy() raises: +def test_copy(): var s0 = String("find") var s1 = str(s0) s1._buffer[3] = ord("e") @@ -107,7 +107,7 @@ fn test_copy() raises: assert_equal("fine", s1) -fn test_equality_operators() raises: +def test_equality_operators(): var s0 = String("abc") var s1 = String("def") assert_equal(s0, s0) @@ -128,7 +128,7 @@ fn test_equality_operators() raises: assert_not_equal(s0, "notabc") -fn test_comparison_operators() raises: +def test_comparison_operators(): var abc = String("abc") var de = String("de") var ABC = String("ABC") @@ -168,7 +168,7 @@ fn test_comparison_operators() raises: assert_true(String.__ge__("", "")) -fn test_add() raises: +def test_add(): var s1 = String("123") var s2 = String("abc") var s3 = s1 + s2 @@ -192,7 +192,7 @@ fn test_add() raises: assert_equal("abc is a string", str(s8) + str(s9)) -fn test_string_join() raises: +def test_string_join(): var sep = String(",") var s0 = String("abc") var s1 = sep.join(s0, s0, s0, s0) @@ -215,7 +215,7 @@ fn test_string_join() raises: assert_equal(s5, "1") -fn test_string_literal_join() raises: +def test_string_literal_join(): var s2 = ",".join(List[UInt8](1, 2, 3)) assert_equal(s2, "1,2,3") @@ -229,7 +229,7 @@ fn test_string_literal_join() raises: assert_equal(s5, "1") -fn test_stringref() raises: +def test_stringref(): var a = StringRef("AAA") var b = StringRef("BBB") var c = StringRef("AAA") @@ -250,7 +250,7 @@ fn test_stringref() raises: assert_equal(a, c) -fn test_stringref_from_dtypepointer() raises: +def test_stringref_from_dtypepointer(): var a = StringRef("AAA") var b = StringRef(a.data) assert_equal(3, len(a)) @@ -258,7 +258,7 @@ fn test_stringref_from_dtypepointer() raises: assert_equal(a, b) -fn test_stringref_strip() raises: +def test_stringref_strip(): var a = StringRef(" mojo rocks ") var b = StringRef("mojo ") var c = StringRef(" mojo") @@ -269,7 +269,7 @@ fn test_stringref_strip() raises: assert_equal(d.strip(), "") -fn test_ord() raises: +def test_ord(): # Regular ASCII assert_equal(ord("A"), 65) assert_equal(ord("Z"), 90) @@ -305,7 +305,7 @@ fn test_ord() raises: assert_equal(ord("🔥".as_string_slice()), 128293) -fn test_chr() raises: +def test_chr(): assert_equal("A", chr(65)) assert_equal("a", chr(97)) assert_equal("!", chr(33)) @@ -314,7 +314,7 @@ fn test_chr() raises: assert_equal("🔥", chr(128293)) -fn test_string_indexing() raises: +def test_string_indexing(): var str = String("Hello Mojo!!") assert_equal("H", str[0]) @@ -341,7 +341,7 @@ fn test_string_indexing() raises: assert_equal("H", str[-50::50]) -fn test_atol() raises: +def test_atol(): # base 10 assert_equal(375, atol(String("375"))) assert_equal(1, atol(String("001"))) @@ -415,7 +415,7 @@ fn test_atol() raises: _ = atol(String("9223372036854775832")) -fn test_atol_base_0() raises: +def test_atol_base_0(): assert_equal(155, atol(" 155", base=0)) assert_equal(155_155, atol("155_155 ", base=0)) @@ -469,7 +469,7 @@ fn test_atol_base_0() raises: _ = atol("0of_", base=0) -fn test_atof() raises: +def test_atof(): assert_equal(375.0, atof(String("375.f"))) assert_equal(1.0, atof(String("001."))) assert_equal(+5.0, atof(String(" +005."))) @@ -529,7 +529,7 @@ fn test_atof() raises: _ = atof(String(" ++94. ")) -fn test_calc_initial_buffer_size_int32() raises: +def test_calc_initial_buffer_size_int32(): assert_equal(1, _calc_initial_buffer_size_int32(0)) assert_equal(1, _calc_initial_buffer_size_int32(9)) assert_equal(2, _calc_initial_buffer_size_int32(10)) @@ -541,7 +541,7 @@ fn test_calc_initial_buffer_size_int32() raises: assert_equal(10, _calc_initial_buffer_size_int32(4294967295)) -fn test_calc_initial_buffer_size_int64() raises: +def test_calc_initial_buffer_size_int64(): assert_equal(1, _calc_initial_buffer_size_int64(0)) assert_equal(1, _calc_initial_buffer_size_int64(9)) assert_equal(2, _calc_initial_buffer_size_int64(10)) @@ -553,7 +553,7 @@ fn test_calc_initial_buffer_size_int64() raises: assert_equal(20, _calc_initial_buffer_size_int64(18446744073709551615)) -fn test_contains() raises: +def test_contains(): var str = String("Hello world") assert_true(str.__contains__("")) @@ -566,7 +566,7 @@ fn test_contains() raises: assert_true("bellow" not in str) -fn test_find() raises: +def test_find(): var str = String("Hello world") assert_equal(0, str.find("")) @@ -589,7 +589,7 @@ fn test_find() raises: assert_equal(-1, String("abc").find("abcd")) -fn test_count() raises: +def test_count(): var str = String("Hello world") assert_equal(12, str.count("")) @@ -603,7 +603,7 @@ fn test_count() raises: assert_equal(String("aaaaaa").count("aa"), 3) -fn test_replace() raises: +def test_replace(): # Replace empty var s1 = String("abc") assert_equal("xaxbxc", s1.replace("", "x")) @@ -623,7 +623,7 @@ fn test_replace() raises: assert_equal("a complex test case with some spaces", s3.replace(" ", " ")) -fn test_rfind() raises: +def test_rfind(): # Basic usage. assert_equal(String("hello world").rfind("world"), 6) assert_equal(String("hello world").rfind("bye"), -1) @@ -650,7 +650,7 @@ fn test_rfind() raises: # assert_equal(String("🔥🔥").rfind("🔥"), 1) -fn test_split() raises: +def test_split(): # empty separators default to whitespace var d = String("hello world").split() assert_true(len(d) == 2) @@ -756,7 +756,7 @@ fn test_split() raises: ) -fn test_splitlines() raises: +def test_splitlines(): # Test with no line breaks var in1 = String("hello world") var res1 = in1.splitlines() @@ -838,7 +838,7 @@ fn test_splitlines() raises: assert_equal(res11[2], "mojo") -fn test_isupper() raises: +def test_isupper(): assert_true(isupper(ord("A"))) assert_true(isupper(ord("B"))) assert_true(isupper(ord("Y"))) @@ -856,7 +856,7 @@ fn test_isupper() raises: assert_false(String("1!").isupper()) -fn test_islower() raises: +def test_islower(): assert_true(islower(ord("a"))) assert_true(islower(ord("b"))) assert_true(islower(ord("y"))) @@ -874,7 +874,7 @@ fn test_islower() raises: assert_false(String("1!").islower()) -fn test_lower() raises: +def test_lower(): assert_equal(String("HELLO").lower(), "hello") assert_equal(String("hello").lower(), "hello") assert_equal(String("FoOBaR").lower(), "foobar") @@ -885,7 +885,7 @@ fn test_lower() raises: assert_equal(String("É").lower(), "É") -fn test_upper() raises: +def test_upper(): assert_equal(String("hello").upper(), "HELLO") assert_equal(String("HELLO").upper(), "HELLO") assert_equal(String("FoOBaR").upper(), "FOOBAR") @@ -896,7 +896,7 @@ fn test_upper() raises: assert_equal(String("É").upper(), "É") -fn test_isspace() raises: +def test_isspace(): # checking true cases assert_true(_isspace(ord(" "))) assert_true(_isspace(ord("\n"))) @@ -957,7 +957,7 @@ fn test_isspace() raises: _ = sep -fn test_ascii_aliases() raises: +def test_ascii_aliases(): assert_true(String("a") in String.ASCII_LOWERCASE) assert_true(String("b") in String.ASCII_LOWERCASE) assert_true(String("y") in String.ASCII_LOWERCASE) @@ -1000,7 +1000,7 @@ fn test_ascii_aliases() raises: assert_true(text[i] in String.PRINTABLE) -fn test_rstrip() raises: +def test_rstrip(): # with default rstrip chars var empty_string = String("") assert_true(empty_string.rstrip() == "") @@ -1026,7 +1026,7 @@ fn test_rstrip() raises: assert_true(str4.rstrip("sip \n") == "mississippim") -fn test_lstrip() raises: +def test_lstrip(): # with default lstrip chars var empty_string = String("") assert_true(empty_string.lstrip() == "") @@ -1052,7 +1052,7 @@ fn test_lstrip() raises: assert_true(str4.lstrip("mis \n") == "ppimississippi") -fn test_strip() raises: +def test_strip(): # with default strip chars var empty_string = String("") assert_true(empty_string.strip() == "") @@ -1096,7 +1096,7 @@ fn test_strip() raises: assert_true(comp_str4_stripped == "\n mississippimississippi \n") -fn test_hash() raises: +def test_hash(): fn assert_hash_equals_literal_hash[s: StringLiteral]() raises: assert_equal(hash(s), hash(String(s))) @@ -1115,7 +1115,7 @@ Pink: No! Darkness! (Pink is floating in the air)""" ]() -fn test_startswith() raises: +def test_startswith(): var str = String("Hello world") assert_true(str.startswith("Hello")) @@ -1126,7 +1126,7 @@ fn test_startswith() raises: assert_false(str.startswith("llo", 2, 3)) -fn test_endswith() raises: +def test_endswith(): var str = String("Hello world") assert_true(str.endswith("")) From b5a43e517a5946adb115dc8c19a48eeb977adc3c Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 2 Jul 2024 10:58:29 -0500 Subject: [PATCH 1093/2019] [External] [stdlib] Add `CollectionElementNew` to a few structs in `object.mojo` (#42642) [External] [stdlib] Add `CollectionElementNew` to a few structs in `object.mojo` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3165 MODULAR_ORIG_COMMIT_REV_ID: d74882e3cc267c2c05eff01b04990c00f5286122 --- stdlib/src/builtin/object.mojo | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 3d1fbefad6..1a05a40049 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -82,7 +82,7 @@ struct _RefCountedList: @register_passable("trivial") -struct _RefCountedListRef: +struct _RefCountedListRef(CollectionElement, CollectionElementNew): # FIXME(#3335): Use indirection to avoid a recursive struct definition. var lst: UnsafePointer[NoneType] """The reference to the list.""" @@ -93,6 +93,10 @@ struct _RefCountedListRef: __get_address_as_uninit_lvalue(ptr.address) = _RefCountedList() self.lst = ptr.bitcast[NoneType]() + @always_inline + fn __init__(inout self, *, other: Self): + self.lst = other.lst + @always_inline fn copy(self) -> Self: _ = self.lst.bitcast[_RefCountedList]()[].impl @@ -166,7 +170,7 @@ struct Attr: @register_passable("trivial") -struct _RefCountedAttrsDictRef: +struct _RefCountedAttrsDictRef(CollectionElement, CollectionElementNew): # FIXME(#3335): Use indirection to avoid a recursive struct definition. # FIXME(#12604): Distinguish this type from _RefCountedListRef. var attrs: UnsafePointer[Int8] @@ -182,6 +186,10 @@ struct _RefCountedAttrsDictRef: self.attrs = ptr.bitcast[Int8]() + @always_inline + fn __init__(inout self, *, other: Self): + self = other + @always_inline fn copy(self) -> Self: _ = self.attrs.bitcast[_RefCountedAttrsDict]()[].impl @@ -192,7 +200,7 @@ struct _RefCountedAttrsDictRef: @register_passable("trivial") -struct _Function: +struct _Function(CollectionElement, CollectionElementNew): # The MLIR function type has two arguments: # 1. The self value, or the single argument. # 2. None, or an additional argument. @@ -206,6 +214,10 @@ struct _Function: UnsafePointer.address_of(f).bitcast[FnT]()[] = value self.value = f + @always_inline + fn __init__(inout self, *, other: Self): + self.value = other.value + alias fn0 = fn () raises -> object """Nullary function type.""" alias fn1 = fn (object) raises -> object From 6b0c9a605cbccc62f3fd74bfc1e6580273604ca0 Mon Sep 17 00:00:00 2001 From: Davood Mohajerani Date: Tue, 2 Jul 2024 12:16:28 -0400 Subject: [PATCH 1094/2019] [Kenels][Stdlib] Fixed issue for printing ops name in `bench_vectorize` (#42696) MODULAR_ORIG_COMMIT_REV_ID: 0911eb108322a77eae2fb00cf85409c6ec429773 --- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 2f0385ed77..fc0748b5d3 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -54,7 +54,7 @@ struct Op(Stringable): @always_inline("nodebug") fn __str__(self) -> String: - alias op_list = List[String]( + var op_list = List[String]( "add", "sub", "mul", "div", "fma", "ld", "st" ) return "op." + op_list[self.op_code] From 36f4b44dec5e34e3e8e43cb3108cd98a308d4739 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 2 Jul 2024 09:23:26 -0700 Subject: [PATCH 1095/2019] [mojo-stdlib] Tidy up various `external_call` calls, NFC This cleans up various external_call calls, not passing StringRef by value in some places, using Reference instead of UnsafePointer etc. MODULAR_ORIG_COMMIT_REV_ID: 27e3d3e6f7e370644698c915be690f1763dfef23 --- stdlib/src/builtin/file.mojo | 24 ++++++++++++------------ stdlib/src/builtin/io.mojo | 4 ++-- stdlib/src/builtin/string_literal.mojo | 9 ++------- stdlib/src/os/_linux_aarch64.mojo | 4 ++-- stdlib/src/os/_linux_x86.mojo | 4 ++-- stdlib/src/os/_macos.mojo | 8 ++------ stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/sys/arg.mojo | 4 +--- stdlib/src/sys/ffi.mojo | 2 +- stdlib/src/sys/info.mojo | 2 +- stdlib/src/time/time.mojo | 10 +++------- 11 files changed, 29 insertions(+), 44 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index d0a68e546c..dfc1124c88 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -96,11 +96,11 @@ struct FileHandle: var err_msg = _OwnedStringRef() var handle = external_call[ "KGEN_CompilerRT_IO_FileOpen", DTypePointer[DType.invalid] - ](path, mode, UnsafePointer.address_of(err_msg)) + ](path, mode, Reference(err_msg)) if err_msg: self.handle = DTypePointer[DType.invalid]() - raise (err_msg^).consume_as_error() + raise err_msg^.consume_as_error() self.handle = handle @@ -119,11 +119,11 @@ struct FileHandle: var err_msg = _OwnedStringRef() external_call["KGEN_CompilerRT_IO_FileClose", NoneType]( - self.handle, UnsafePointer.address_of(err_msg) + self.handle, Reference(err_msg) ) if err_msg: - raise (err_msg^).consume_as_error() + raise err_msg^.consume_as_error() self.handle = DTypePointer[DType.invalid]() @@ -196,12 +196,12 @@ struct FileHandle: "KGEN_CompilerRT_IO_FileRead", UnsafePointer[UInt8] ]( self.handle, - UnsafePointer.address_of(size_copy), - UnsafePointer.address_of(err_msg), + Reference(size_copy), + Reference(err_msg), ) if err_msg: - raise (err_msg^).consume_as_error() + raise err_msg^.consume_as_error() return String(buf, int(size_copy) + 1) @@ -274,7 +274,7 @@ struct FileHandle: self.handle, ptr, size * sizeof[type](), - UnsafePointer.address_of(err_msg), + Reference(err_msg), ) if err_msg: @@ -340,8 +340,8 @@ struct FileHandle: "KGEN_CompilerRT_IO_FileReadBytes", UnsafePointer[UInt8] ]( self.handle, - UnsafePointer.address_of(size_copy), - UnsafePointer.address_of(err_msg), + Reference(size_copy), + Reference(err_msg), ) if err_msg: @@ -398,7 +398,7 @@ struct FileHandle: ) var err_msg = _OwnedStringRef() var pos = external_call["KGEN_CompilerRT_IO_FileSeek", UInt64]( - self.handle, offset, whence, UnsafePointer.address_of(err_msg) + self.handle, offset, whence, Reference(err_msg) ) if err_msg: @@ -452,7 +452,7 @@ struct FileHandle: self.handle, ptr.address, len, - UnsafePointer.address_of(err_msg), + Reference(err_msg), ) if err_msg: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index e542be1188..4c131bcd79 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -87,7 +87,7 @@ struct _fdopen: @no_inline fn _flush(file: FileDescriptor = stdout): with _fdopen(file) as fd: - _ = external_call["fflush", Int32](fd) + _ = external_call["fflush", Int32](fd.handle) # ===----------------------------------------------------------------------=== # @@ -107,7 +107,7 @@ fn _printf[ @parameter if triple_is_nvidia_cuda(): _ = external_call["vprintf", Int32]( - fmt.unsafe_cstr_ptr(), UnsafePointer.address_of(loaded_pack) + fmt.unsafe_cstr_ptr(), Reference(loaded_pack) ) _ = loaded_pack else: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c3d0ab1128..4f10eb5a6d 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -221,17 +221,12 @@ struct StringLiteral( A new string. """ var string = String() - var length: Int = __mlir_op.`pop.string.size`(self.value) + var length = self._byte_length() var buffer = String._buffer_type() var new_capacity = length + 1 buffer._realloc(new_capacity) buffer.size = new_capacity - var uint8Ptr = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type.`!kgen.pointer>` - ](__mlir_op.`pop.string.address`(self.value)) - var data: DTypePointer[DType.uint8] = DTypePointer[DType.uint8]( - uint8Ptr - ) + var data: DTypePointer[DType.uint8] = self.as_uint8_ptr() memcpy(DTypePointer(buffer.data), data, length) (buffer.data + length).init_pointee_move(0) string._buffer = buffer^ diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index d2a0c30f03..142a01c244 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -109,7 +109,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), Reference(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -120,7 +120,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), Reference(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index ab36f40c95..75ae041e0d 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -107,7 +107,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), Reference(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -118,7 +118,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path.unsafe_ptr(), UnsafePointer.address_of(stat) + Int32(0), path.unsafe_ptr(), Reference(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 7c13100c99..725ae6fb4e 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -113,9 +113,7 @@ struct _c_stat(Stringable): @always_inline fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() - var err = external_call["stat", Int32]( - path.unsafe_ptr(), UnsafePointer.address_of(stat) - ) + var err = external_call["stat", Int32](path.unsafe_ptr(), Reference(stat)) if err == -1: raise "unable to stat '" + path + "'" return stat @@ -124,9 +122,7 @@ fn _stat(path: String) raises -> _c_stat: @always_inline fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() - var err = external_call["lstat", Int32]( - path.unsafe_ptr(), UnsafePointer.address_of(stat) - ) + var err = external_call["lstat", Int32](path.unsafe_ptr(), Reference(stat)) if err == -1: raise "unable to lstat '" + path + "'" return stat diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index ccfb33d7e9..b6a16edafc 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -38,7 +38,7 @@ fn cwd() raises -> Path: var buf = UnsafePointer[C_char]._from_dtype_ptr(buf0) var res = external_call["getcwd", UnsafePointer[C_char]]( - buf, MAX_CWD_BUFFER_SIZE + buf, Int(MAX_CWD_BUFFER_SIZE) ) # If we get a nullptr, then we raise an error. diff --git a/stdlib/src/sys/arg.mojo b/stdlib/src/sys/arg.mojo index fd9dc41852..6284910c4d 100644 --- a/stdlib/src/sys/arg.mojo +++ b/stdlib/src/sys/arg.mojo @@ -35,7 +35,5 @@ fn argv() -> VariadicList[StringRef]: The list of command line arguments provided when mojo was invoked. """ var result = VariadicList[StringRef]("") - external_call["KGEN_CompilerRT_GetArgV", NoneType]( - UnsafePointer[VariadicList[StringRef]].address_of(result) - ) + external_call["KGEN_CompilerRT_GetArgV", NoneType](Reference(result)) return result diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index a45ba7be49..3c65863f0d 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -231,7 +231,7 @@ fn _get_global[ fn _get_global_or_null[name: StringLiteral]() -> UnsafePointer[NoneType]: return external_call[ "KGEN_CompilerRT_GetGlobalOrNull", UnsafePointer[NoneType] - ](StringRef(name)) + ](name.unsafe_ptr(), name._byte_length()) @always_inline diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index c696d67e8d..b949007356 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -734,7 +734,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: var err = external_call["sysctlbyname", Int32]( "kern.osproductversion".unsafe_cstr_ptr(), buf.data, - UnsafePointer.address_of(buf_len), + Reference(buf_len), UnsafePointer[NoneType](), Int(0), ) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 5d442b1af8..70634d75b5 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -96,9 +96,7 @@ fn _clock_gettime(clockid: Int) -> _CTimeSpec: var ts = _CTimeSpec() # Call libc's clock_gettime. - _ = external_call["clock_gettime", Int32]( - Int32(clockid), UnsafePointer.address_of(ts) - ) + _ = external_call["clock_gettime", Int32](Int32(clockid), Reference(ts)) return ts @@ -127,9 +125,7 @@ fn _monotonic_nanoseconds() -> Int: @parameter if os_is_windows(): var ft = _FILETIME() - external_call["GetSystemTimePreciseAsFileTime", NoneType]( - UnsafePointer.address_of(ft) - ) + external_call["GetSystemTimePreciseAsFileTime", NoneType](Reference(ft)) return ft.as_nanoseconds() else: @@ -268,4 +264,4 @@ fn sleep(sec: Int): # In Windows the argument is in milliseconds. external_call["Sleep", NoneType](sec * 1000) else: - external_call["sleep", NoneType](sec) + external_call["sleep", NoneType](Int32(sec)) From 210edd6a6b4ff300579ab7ab8536130d1ded06a6 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 2 Jul 2024 09:53:17 -0700 Subject: [PATCH 1096/2019] Follow the style guide update for type parameters here: https://github.com/modularml/mojo/blob/main/stdlib/docs/style-guide.md#identifier-naming-conventions MODULAR_ORIG_COMMIT_REV_ID: e4cf6cb047be422ac851d682d994fa2824c3cc6f --- stdlib/src/builtin/file.mojo | 6 +++--- stdlib/src/os/fstat.mojo | 8 ++++---- stdlib/src/os/os.mojo | 30 +++++++++++++++--------------- stdlib/src/os/path/path.mojo | 4 ++-- stdlib/test/os/test_remove.mojo | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index dfc1124c88..38e4594358 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -489,13 +489,13 @@ fn open(path: String, mode: String) raises -> FileHandle: fn open[ - pathlike: os.PathLike -](path: pathlike, mode: String) raises -> FileHandle: + PathLike: os.PathLike +](path: PathLike, mode: String) raises -> FileHandle: """Opens the file specified by path using the mode provided, returning a FileHandle. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the file to open. diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index 3c70b94e91..4c6b3860f8 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -206,11 +206,11 @@ fn stat(path: String) raises -> stat_result: return _stat_linux_x86(path)._to_stat_result() -fn stat[pathlike: os.PathLike](path: pathlike) raises -> stat_result: +fn stat[PathLike: os.PathLike](path: PathLike) raises -> stat_result: """Get the status of a file or a file descriptor. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -245,12 +245,12 @@ fn lstat(path: String) raises -> stat_result: return _lstat_linux_x86(path)._to_stat_result() -fn lstat[pathlike: os.PathLike](path: pathlike) raises -> stat_result: +fn lstat[PathLike: os.PathLike](path: PathLike) raises -> stat_result: """Get the status of a file or a file descriptor (similar to stat, but does not follow symlinks). Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 7b5a098eb4..5b0286b4a6 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -203,11 +203,11 @@ fn listdir(path: String = "") raises -> List[String]: return dir.list() -fn listdir[pathlike: os.PathLike](path: pathlike) raises -> List[String]: +fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: """Gets the list of entries contained in the path provided. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -288,13 +288,13 @@ fn remove(path: String) raises: raise Error("Can not remove file: " + path) -fn remove[pathlike: os.PathLike](path: pathlike) raises: +fn remove[PathLike: os.PathLike](path: PathLike) raises: """Removes the specified file. If the path is a directory or it can not be deleted, an error is raised. Absolute and relative paths are allowed, relative paths are resolved from cwd. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the file. @@ -315,13 +315,13 @@ fn unlink(path: String) raises: remove(path) -fn unlink[pathlike: os.PathLike](path: pathlike) raises: +fn unlink[PathLike: os.PathLike](path: PathLike) raises: """Removes the specified file. If the path is a directory or it can not be deleted, an error is raised. Absolute and relative paths are allowed, relative paths are resolved from cwd. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the file. @@ -350,13 +350,13 @@ fn mkdir(path: String, mode: Int = 0o777) raises: raise Error("Can not create directory: " + path) -fn mkdir[pathlike: os.PathLike](path: pathlike, mode: Int = 0o777) raises: +fn mkdir[PathLike: os.PathLike](path: PathLike, mode: Int = 0o777) raises: """Creates a directory at the specified path. If the directory can not be created an error is raised. Absolute and relative paths are allowed, relative paths are resolved from cwd. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -399,13 +399,13 @@ def makedirs(path: String, mode: Int = 0o777, exist_ok: Bool = False) -> None: def makedirs[ - pathlike: os.PathLike -](path: pathlike, mode: Int = 0o777, exist_ok: Bool = False) -> None: + PathLike: os.PathLike +](path: PathLike, mode: Int = 0o777, exist_ok: Bool = False) -> None: """Creates a specified leaf directory along with any necessary intermediate directories that don't already exist. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -428,13 +428,13 @@ fn rmdir(path: String) raises: raise Error("Can not remove directory: " + path) -fn rmdir[pathlike: os.PathLike](path: pathlike) raises: +fn rmdir[PathLike: os.PathLike](path: PathLike) raises: """Removes the specified directory. If the path is not a directory or it can not be deleted, an error is raised. Absolute and relative paths are allowed, relative paths are resolved from cwd. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. @@ -463,14 +463,14 @@ def removedirs(path: String) -> None: head, tail = os.path.split(head) -def removedirs[pathlike: os.PathLike](path: pathlike) -> None: +def removedirs[PathLike: os.PathLike](path: PathLike) -> None: """Remove a leaf directory and all empty intermediate ones. Directories corresponding to rightmost path segments will be pruned away until either the whole path is consumed or an error occurs. Errors during this latter phase are ignored, which occur when a directory was not empty. Parameters: - pathlike: The a type conforming to the os.PathLike trait. + PathLike: The a type conforming to the os.PathLike trait. Args: path: The path to the directory. diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 6df8f25263..1e7cf32d17 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -463,14 +463,14 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): # TODO uncomment this when unpacking is supported -# fn join[pathlike: os.PathLike](path: pathlike, *paths: pathlike) -> String: +# fn join[PathLike: os.PathLike](path: PathLike, *paths: PathLike) -> String: # """Join two or more pathname components, inserting '/' as needed. # If any component is an absolute path, all previous path components # will be discarded. An empty last part will result in a path that # ends with a separator. # Parameters: -# pathlike: The type conforming to the os.PathLike trait. +# PathLike: The type conforming to the os.PathLike trait. # Args: # path: The path to join. diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index 5a280938b9..0dc2d5508c 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -34,7 +34,7 @@ fn create_file_and_test_delete_string[ fn create_file_and_test_delete_path[ - func: fn[pathlike: PathLike] (pathlike) raises -> None, + func: fn[PathLike: PathLike] (PathLike) raises -> None, name: StringLiteral, ](filepath: Path) raises: try: From 549a35b71c9dfd9ce665e2eb54d91145f858abca Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 2 Jul 2024 10:31:37 -0700 Subject: [PATCH 1097/2019] [mojo] Internal compiler ABI change MODULAR_ORIG_COMMIT_REV_ID: eca69e8b159ca5e1eee8648f25d295cee1137dfd --- stdlib/src/builtin/_closure.mojo | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/_closure.mojo b/stdlib/src/builtin/_closure.mojo index b1731c6371..b456d1405a 100644 --- a/stdlib/src/builtin/_closure.mojo +++ b/stdlib/src/builtin/_closure.mojo @@ -39,14 +39,12 @@ struct __ParameterClosureCaptureList[ __mlir_op.`kgen.capture_list.expand`(self.value) -fn __closure_wrapper_noop_dtor( - owned self: __mlir_type.`!kgen.pointer`, / -): +fn __closure_wrapper_noop_dtor(self: __mlir_type.`!kgen.pointer`, /): pass fn __closure_wrapper_noop_copy( - owned other: __mlir_type.`!kgen.pointer`, / + other: __mlir_type.`!kgen.pointer`, / ) -> __mlir_type.`!kgen.pointer`: return other From 2097199db050861789be8118e27af3e6ac2a20c4 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 2 Jul 2024 12:34:06 -0500 Subject: [PATCH 1098/2019] [stdlib] Support `range` for `UInt` Allow constructing a range from `UInt` arguments which creates an internal range type backed by `UInt` types. This is useful in certain ****** to improve the assembly by avoiding the sign check when we know we'll always be dealing with positive numbers. Note: - We don't support a reversed sequential range for `UInt` arguments since the way that machinery works currently is by using a step size of `-1`. In the future, we could make this sort of thing happen, but it's not needed by the ****** right this moment. MODULAR_ORIG_COMMIT_REV_ID: 6b91d6eafb59cb317f9cb140436727662676c6eb --- stdlib/src/builtin/_stubs.mojo | 17 +++- stdlib/src/builtin/len.mojo | 46 +++++++++++ stdlib/src/builtin/range.mojo | 121 +++++++++++++++++++++++++++- stdlib/test/builtin/test_range.mojo | 28 +++++++ 4 files changed, 209 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index 2006128733..bdb830bece 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from builtin.range import _StridedRangeIterator +from builtin.range import _StridedRangeIterator, _UIntStridedRangeIterator # ===----------------------------------------------------------------------===# # __MLIRType @@ -33,11 +33,21 @@ trait _IntNext(Copyable): ... +trait _UIntNext(Copyable): + fn __next__(inout self) -> UInt: + ... + + trait _IntIter(_IntNext): fn __len__(self) -> Int: ... +trait _UIntIter(_UIntNext): + fn __len__(self) -> UInt: + ... + + trait _IntIterable(_IntIter): fn __iter__(self) -> Self: ... @@ -48,6 +58,11 @@ trait _StridedIterable(_IntIter): ... +trait _UIntStridedIterable(_UIntIter): + fn __iter__(self) -> _UIntStridedRangeIterator: + ... + + struct _ParamForIterator[IteratorT: Copyable]: var next_it: IteratorT var value: Int diff --git a/stdlib/src/builtin/len.mojo b/stdlib/src/builtin/len.mojo index adebf760bd..073a6f5b30 100644 --- a/stdlib/src/builtin/len.mojo +++ b/stdlib/src/builtin/len.mojo @@ -66,6 +66,52 @@ trait Sized: ... +trait UIntSized: + """The `Sized` trait describes a type that has an integer length (such as a + string or array). + + Any type that conforms to `Sized` or + [`SizedRaising`](/mojo/stdlib/builtin/len/SizedRaising) works with the + built-in [`len()`](/mojo/stdlib/builtin/len/len) function. + + The `Sized` trait requires a type to implement the `__len__()` + method. For example: + + ```mojo + @value + struct Foo(Sized): + var length: Int + + fn __len__(self) -> Int: + return self.length + ``` + + You can pass an instance of `Foo` to the `len()` function to get its + length: + + ```mojo + var foo = Foo(42) + print(len(foo) == 42) + ``` + + ```plaintext + True + ``` + + **Note:** If the `__len__()` method can raise an error, use the + [`SizedRaising`](/mojo/stdlib/builtin/len/SizedRaising) trait instead. + + """ + + fn __len__(self) -> UInt: + """Get the length of the type. + + Returns: + The length of the type. + """ + ... + + trait SizedRaising: """The `SizedRaising` trait describes a type that has an integer length, which might raise an error if the length can't be determined. diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index f13993d701..344182f3e6 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -17,8 +17,11 @@ These are Mojo built-ins, so you don't need to import them. # FIXME(MOCO-658): Explicit conformance to these traits shouldn't be needed. -from builtin._stubs import _IntIterable, _StridedIterable -from python import PythonObject +from builtin._stubs import _IntIterable, _StridedIterable, _UIntStridedIterable +from python import ( + PythonObject, +) # TODO: remove this and fixup downstream imports +from math import ceildiv # ===----------------------------------------------------------------------=== # # Utilities @@ -93,6 +96,35 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): return range(self.end - 1, -1, -1) +@register_passable("trivial") +struct _UIntZeroStartingRange(UIntSized): + var curr: UInt + var end: UInt + + @always_inline + fn __init__(inout self, end: UInt): + self.curr = max(0, end) + self.end = self.curr + + @always_inline + fn __iter__(self) -> Self: + return self + + @always_inline + fn __next__(inout self) -> UInt: + var curr = self.curr + self.curr -= 1 + return self.end - curr + + @always_inline + fn __len__(self) -> UInt: + return self.curr + + @always_inline + fn __getitem__(self, idx: UInt) -> UInt: + return idx + + @value @register_passable("trivial") struct _SequentialRange(Sized, ReversibleRange, _IntIterable): @@ -147,6 +179,28 @@ struct _StridedRangeIterator(Sized): return result +@value +@register_passable("trivial") +struct _UIntStridedRangeIterator(UIntSized): + var start: UInt + var end: UInt + var step: UInt + + @always_inline + fn __len__(self) -> UInt: + if self.start < self.end: + return self.end - self.start + elif self.start > self.end: + return self.start - self.end + return 0 + + @always_inline + fn __next__(inout self) -> UInt: + var result = self.start + self.start += self.step + return result + + @value @register_passable("trivial") struct _StridedRange(Sized, ReversibleRange, _StridedIterable): @@ -191,6 +245,41 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return range(start, end, step) +@value +@register_passable("trivial") +struct _UIntStridedRange(UIntSized, _UIntStridedIterable): + var start: UInt + var end: UInt + var step: UInt + + @always_inline + fn __init__(inout self, start: UInt, end: UInt, step: UInt): + self.start = start + self.end = end + debug_assert(step != 0, "step cannot be 0") + self.step = step + + @always_inline + fn __iter__(self) -> _UIntStridedRangeIterator: + return _UIntStridedRangeIterator(self.start, self.end, self.step) + + @always_inline + fn __next__(inout self) -> UInt: + var result = self.start + self.start += self.step + return result + + @always_inline + fn __len__(self) -> UInt: + if self.start > self.end: + return 0 + return ceildiv(self.end - self.start, self.step) + + @always_inline + fn __getitem__(self, idx: UInt) -> UInt: + return self.start + idx * self.step + + @always_inline("nodebug") fn range[type: Intable](end: type) -> _ZeroStartingRange: """Constructs a [0; end) Range. @@ -207,6 +296,19 @@ fn range[type: Intable](end: type) -> _ZeroStartingRange: return _ZeroStartingRange(int(end)) +@always_inline +fn range(end: UInt) -> _UIntZeroStartingRange: + """Constructs a [0; end) Range. + + Args: + end: The end of the range. + + Returns: + The constructed range. + """ + return _UIntZeroStartingRange(end) + + @always_inline fn range[type: IntableRaising](end: type) raises -> _ZeroStartingRange: """Constructs a [0; end) Range. @@ -283,6 +385,21 @@ fn range[ return _StridedRange(int(start), int(end), int(step)) +@always_inline +fn range(start: UInt, end: UInt, step: UInt = 1) -> _UIntStridedRange: + """Constructs a [start; end) Range with a given step. + + Args: + start: The start of the range. + end: The end of the range. + step: The step for the range. Defaults to 1. + + Returns: + The constructed range. + """ + return _UIntStridedRange(start, end, step) + + @always_inline fn range[ t0: IntableRaising, t1: IntableRaising, t2: IntableRaising diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index ea847f3c93..84fb806e1a 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -39,6 +39,21 @@ def test_range_len(): assert_equal(range(10, 5, -20).__len__(), 1, "len(range(10, 5, -20))") +def test_range_len_uint(): + assert_equal(range(UInt(10)).__len__(), 10, "len(range(10))") + + # start < end + assert_equal(range(UInt(0), UInt(10)).__len__(), 10, "len(range(0, 10))") + assert_equal(range(UInt(5), UInt(10)).__len__(), 5, "len(range(5, 10))") + assert_equal( + range(UInt(0), UInt(10), UInt(2)).__len__(), 5, "len(range(0, 10, 2))" + ) + # start > end + assert_equal( + range(UInt(10), UInt(0), UInt(1)).__len__(), 0, "len(range(10, 0, 1))" + ) + + def test_range_getitem(): # Usual cases assert_equal(range(10)[3], 3, "range(10)[3]") @@ -50,6 +65,17 @@ def test_range_getitem(): assert_equal(range(38, -13, -23)[1], 15, "range(38, -13, -23)[1]") +def test_range_getitem_uint(): + assert_equal(range(UInt(10))[3], 3, "range(10)[3]") + + assert_equal(range(UInt(0), UInt(10))[3], 3, "range(0, 10)[3]") + assert_equal(range(UInt(5), UInt(10))[3], 8, "range(5, 10)[3]") + assert_equal(range(UInt(5), UInt(10))[4], 9, "range(5, 10)[4]") + + # Specify the step size > 1 + assert_equal(range(UInt(0), UInt(10), UInt(2))[4], 8, "range(0, 10, 2)[4]") + + def test_range_reversed(): # Zero starting assert_equal( @@ -122,6 +148,8 @@ def test_indexing(): def main(): test_range_len() + test_range_len_uint() test_range_getitem() + test_range_getitem_uint() test_range_reversed() test_indexing() From 1d1c7c7d0382825b89bba8ba63e342f7a754d8b1 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 2 Jul 2024 11:08:15 -0700 Subject: [PATCH 1099/2019] [mojo] Add an `exclusive` parameter to the pointer types The pointer variants (`DTypePointer`, `UnsafePointer`, etc.) now have a new `exclusive: Bool = False` parameter. Setting this parameter to true tells the compiler that the user knows this pointer and all those derived from it have exclusive access to the underlying memory allocation. The compiler is not guaranteed to do anything with this information. MODULAR_ORIG_COMMIT_REV_ID: ba9dde1a990ec2dd206cf6cb94be8140054c079d --- docs/changelog.md | 6 +++ stdlib/src/builtin/simd.mojo | 20 ++++------ stdlib/src/memory/unsafe.mojo | 57 +++++++++++++++++++++++---- stdlib/src/memory/unsafe_pointer.mojo | 33 ++++++++++++++-- stdlib/src/sys/intrinsics.mojo | 10 ++--- 5 files changed, 99 insertions(+), 27 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f9e8979ee1..060b58a38f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,12 @@ what we publish. ### ⭐️ New +- The pointer variants (`DTypePointer`, `UnsafePointer`, etc.) now have a new + `exclusive: Bool = False` parameter. Setting this parameter to true tells the + compiler that the user knows this pointer and all those derived from it have + exclusive access to the underlying memory allocation. The compiler is not + guaranteed to do anything with this information. + - Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) dictionary-like type, matching most of the features of the Python one. ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 33c60708f6..57f6cbd53c 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2680,7 +2680,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space]) -> Self: + ](ptr: DTypePointer[type, address_space, _]) -> Self: """Loads the value the Pointer object points to. Constraints: @@ -2696,9 +2696,7 @@ struct SIMD[type: DType, size: Int]( Returns: The loaded value. """ - return Self.load[alignment=alignment, address_space=address_space]( - ptr, offset=0 - ) + return Self.load[alignment=alignment](ptr, offset=0) @staticmethod @always_inline @@ -2706,7 +2704,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: Scalar) -> Self: + ](ptr: DTypePointer[type, address_space, _], offset: Scalar) -> Self: """Loads the value the Pointer object points to with the given offset. Constraints: @@ -2725,9 +2723,7 @@ struct SIMD[type: DType, size: Int]( The loaded value. """ constrained[offset.type.is_integral(), "offset must be integer"]() - return Self.load[alignment=alignment, address_space=address_space]( - ptr, offset=int(offset) - ) + return Self.load[alignment=alignment](ptr, offset=int(offset)) @staticmethod @always_inline("nodebug") @@ -2735,7 +2731,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: Int) -> Self: + ](ptr: DTypePointer[type, address_space, _], offset: Int) -> Self: """Loads the value the Pointer object points to with the given offset. Constraints: @@ -2781,7 +2777,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: Int, val: Self): + ](ptr: DTypePointer[type, address_space, _], offset: Int, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2807,7 +2803,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: Scalar, val: Self): + ](ptr: DTypePointer[type, address_space, _], offset: Scalar, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2833,7 +2829,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], val: Self): + ](ptr: DTypePointer[type, address_space, _], val: Self): """Stores a single element value. Constraints: diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 045628ce6f..a4387af725 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -139,7 +139,9 @@ alias Pointer = LegacyPointer @value @register_passable("trivial") struct LegacyPointer[ - type: AnyTrivialRegType, address_space: AddressSpace = AddressSpace.GENERIC + type: AnyTrivialRegType, + address_space: AddressSpace = AddressSpace.GENERIC, + exclusive: Bool = False, ]( Boolable, CollectionElement, @@ -154,10 +156,17 @@ struct LegacyPointer[ Parameters: type: Type of the underlying data. address_space: The address space the pointer is in. + exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. """ alias _mlir_type = __mlir_type[ - `!kgen.pointer<`, type, `,`, address_space._value.value, `>` + `!kgen.pointer<`, + type, + `, `, + address_space._value.value, + ` exclusive(`, + exclusive.value, + `)>`, ] var address: Self._mlir_type @@ -174,6 +183,22 @@ struct LegacyPointer[ """ return __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] + @always_inline + fn __init__(other: LegacyPointer[type, address_space, _]) -> Self: + """Exclusivity parameter cast a pointer. + + Args: + other: Pointer to cast. + + Returns: + Constructed LegacyPointer object. + """ + return Self { + address: __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( + other.address + ) + } + fn __init__(*, other: Self) -> Self: """Copy the object. @@ -271,7 +296,7 @@ struct LegacyPointer[ """ return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = Self._ref_type._mlir_type]( - self.address + LegacyPointer[type, address_space, False](self).address ) ) @@ -561,7 +586,9 @@ struct LegacyPointer[ @value @register_passable("trivial") struct DTypePointer[ - type: DType, address_space: AddressSpace = AddressSpace.GENERIC + type: DType, + address_space: AddressSpace = AddressSpace.GENERIC, + exclusive: Bool = False, ](Boolable, CollectionElement, Intable, Stringable, EqualityComparable): """Defines a `DTypePointer` struct that contains an address of the given dtype. @@ -569,11 +596,12 @@ struct DTypePointer[ Parameters: type: DType of the underlying data. address_space: The address space the pointer is in. + exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. """ # Fields alias element_type = Scalar[type] - alias _pointer_type = Pointer[Scalar[type], address_space] + alias _pointer_type = Pointer[Scalar[type], address_space, exclusive] var address: Self._pointer_type """The pointed-to address.""" @@ -603,7 +631,9 @@ struct DTypePointer[ type.value, `>,`, address_space._value.value, - `>`, + ` exclusive(`, + exclusive.value, + `)>`, ], ): """Constructs a `DTypePointer` from a scalar pointer of the same type. @@ -615,8 +645,21 @@ struct DTypePointer[ __mlir_type[`!pop.scalar<`, type.value, `>`], address_space ](value).bitcast[Scalar[type]]() + @always_inline + fn __init__(inout self, other: DTypePointer[type, address_space, _]): + """Exclusivity parameter cast a pointer. + + Args: + other: Pointer to cast. + """ + self.address = __mlir_op.`pop.pointer.bitcast`[ + _type = Self._pointer_type._mlir_type + ](other.address.address) + @always_inline("nodebug") - fn __init__(inout self, value: Pointer[Scalar[type], address_space]): + fn __init__( + inout self, value: Pointer[Scalar[type], address_space, exclusive] + ): """Constructs a `DTypePointer` from a scalar pointer of the same type. Args: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index ef4cb99358..4518d7e968 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -28,9 +28,13 @@ from memory.memory import _free, _malloc # ===----------------------------------------------------------------------=== # # UnsafePointer # ===----------------------------------------------------------------------=== # + + @register_passable("trivial") struct UnsafePointer[ - T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC + T: AnyType, + address_space: AddressSpace = AddressSpace.GENERIC, + exclusive: Bool = False, ]( ImplicitlyBoolable, CollectionElement, @@ -45,11 +49,18 @@ struct UnsafePointer[ Parameters: T: The type the pointer points to. address_space: The address space associated with the UnsafePointer allocated memory. + exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. """ # Fields alias _mlir_type = __mlir_type[ - `!kgen.pointer<`, T, `,`, address_space._value.value, `>` + `!kgen.pointer<`, + T, + `, `, + address_space._value.value, + ` exclusive(`, + exclusive.value, + `)>`, ] alias type = T @@ -85,6 +96,22 @@ struct UnsafePointer[ """ return Self {address: value} + @always_inline + fn __init__(other: UnsafePointer[T, address_space, _]) -> Self: + """Exclusivity parameter cast a pointer. + + Args: + other: Pointer to cast. + + Returns: + Constructed UnsafePointer object. + """ + return Self { + address: __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( + other.address + ) + } + @always_inline fn __init__(*, address: Int) -> Self: """Create an unsafe UnsafePointer from an address in an integer. @@ -191,7 +218,7 @@ struct UnsafePointer[ alias _ref_type = Reference[T, MutableStaticLifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( - self.address + UnsafePointer[T, address_space, False](self).address ) ) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index e501a0cca2..a0d39c5525 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1168,7 +1168,7 @@ struct PrefetchOptions: @always_inline("nodebug") fn prefetch[ params: PrefetchOptions, type: DType, address_space: AddressSpace -](addr: DTypePointer[type, address_space]): +](addr: DTypePointer[type, address_space, _]): """Prefetches an instruction or data into cache before it is used. The prefetch function provides prefetching hints for the target @@ -1329,7 +1329,7 @@ fn strided_load[ /, address_space: AddressSpace = AddressSpace.GENERIC, ]( - addr: DTypePointer[type, address_space], + addr: DTypePointer[type, address_space, _], stride: Int, mask: SIMD[DType.bool, simd_width], ) -> SIMD[type, simd_width]: @@ -1373,7 +1373,7 @@ fn strided_load[ simd_width: Int, /, address_space: AddressSpace = AddressSpace.GENERIC, -](addr: DTypePointer[type, address_space], stride: Int) -> SIMD[ +](addr: DTypePointer[type, address_space, _], stride: Int) -> SIMD[ type, simd_width ]: """Loads values from addr according to a specific stride. @@ -1411,7 +1411,7 @@ fn strided_store[ address_space: AddressSpace = AddressSpace.GENERIC, ]( value: SIMD[type, simd_width], - addr: DTypePointer[type, address_space], + addr: DTypePointer[type, address_space, _], stride: Int, mask: SIMD[DType.bool, simd_width], ): @@ -1455,7 +1455,7 @@ fn strided_store[ address_space: AddressSpace = AddressSpace.GENERIC, ]( value: SIMD[type, simd_width], - addr: DTypePointer[type, address_space], + addr: DTypePointer[type, address_space, _], stride: Int, ): """Loads values from addr according to a specific stride. From e0661ee0e642ba224062d46474f765d919ac5f8f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 2 Jul 2024 11:39:29 -0700 Subject: [PATCH 1100/2019] [Stdlib] Extend the sleep function MODULAR_ORIG_COMMIT_REV_ID: 596228233ca85cdc0c57b3a0ac3a467c839a8097 --- stdlib/src/time/time.mojo | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 70634d75b5..4bd0c6a1f6 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -19,7 +19,8 @@ from time import now ``` """ -from sys import external_call, os_is_linux, os_is_windows +from sys import external_call, os_is_linux, os_is_windows, triple_is_nvidia_cuda +from sys._assembly import inlined_assembly from memory import UnsafePointer @@ -240,6 +241,15 @@ fn sleep(sec: Float64): Args: sec: The number of seconds to sleep for. """ + + @parameter + if triple_is_nvidia_cuda(): + var nsec = sec * 1.0e9 + inlined_assembly["nanosleep.u32 $0;", NoneType, constraints="r"]( + nsec.cast[DType.uint32]() + ) + return + alias NANOSECONDS_IN_SECOND = 1_000_000_000 var total_secs = sec.__floor__() var tv_spec = _CTimeSpec( From 130aad1fde53214a045c7b26db0ee07ce71b60b4 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 2 Jul 2024 11:39:36 -0700 Subject: [PATCH 1101/2019] [Stdlib] Add or_else on the OptionalReg type MODULAR_ORIG_COMMIT_REV_ID: f7a4915deb5d2088120a7855bc1e95d935ccb568 --- stdlib/src/collections/optional.mojo | 14 ++++++++++++++ stdlib/test/collections/test_optional.mojo | 3 +++ 2 files changed, 17 insertions(+) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index f08adfcea8..2b0bcb2abc 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -415,3 +415,17 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): The contained value. """ return __mlir_op.`kgen.variant.take`[index = Int(0).value](self._value) + + fn or_else(self, default: T) -> T: + """Return the underlying value contained in the Optional or a default + value if the Optional's underlying value is not present. + + Args: + default: The new value to use if no value was present. + + Returns: + The underlying value contained in the Optional or a default value. + """ + if self: + return self.value() + return default diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index a4e7e34062..1606aee680 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -75,6 +75,9 @@ def test_optional_reg_basic(): assert_true(False or val) assert_true(True and val) + assert_equal(OptionalReg[Int]().or_else(33), 33) + assert_equal(OptionalReg[Int](42).or_else(33), 42) + def test_optional_is(): a = Optional(1) From 53a26002e249d4638dd6766f05eccae59f6447d3 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Jul 2024 23:36:33 +0300 Subject: [PATCH 1102/2019] [stdlib] Remove `Int` and `IntLiteral` overloads of `ceildiv` The direct mlir op implementation for `Int` is causing a regression on some platforms. MODULAR_ORIG_COMMIT_REV_ID: c036d884354b734e014b5906d0f037d37243ff1d --- stdlib/src/math/math.mojo | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index cbf88372a9..27fdbf1e0a 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -132,20 +132,6 @@ fn ceildiv(numerator: IntLiteral, denominator: IntLiteral) -> IntLiteral: return -(numerator // -denominator) -@always_inline("nodebug") -fn ceildiv(numerator: Int, denominator: Int) -> Int: - """Return the rounded-up result of dividing x by y. - - Args: - numerator: The numerator. - denominator: The denominator. - - Returns: - The ceiling of dividing x by y. - """ - return __mlir_op.`index.ceildivs`(numerator.value, denominator.value) - - @always_inline("nodebug") fn ceildiv(numerator: UInt, denominator: UInt) -> UInt: """Return the rounded-up result of dividing x by y. From 973eb4548b6443223f3201e6607723e027595fb1 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:50:36 -0400 Subject: [PATCH 1103/2019] [stdlib] Use `repeat` keyword for the splat constructor for `StaticIntTuple`. This avoids confusion with the variadic constructor, which is not type-safe. MODULAR_ORIG_COMMIT_REV_ID: 481a592ad4eb516bfd73eea1e219851bda5bc991 --- stdlib/src/utils/index.mojo | 8 ++++---- stdlib/test/utils/test_tuple.mojo | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index adfc6ab277..ce8b5dd22f 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -185,7 +185,7 @@ struct StaticIntTuple[size: Int]( @always_inline fn __init__(inout self): """Constructs a static int tuple of the given size.""" - self = 0 + self = Self(repeat=0) @always_inline fn __init__(inout self, value: __mlir_type.index): @@ -302,16 +302,16 @@ struct StaticIntTuple[size: Int]( self = tup @always_inline - fn __init__(inout self, elem: Int): + fn __init__(inout self, *, repeat: Int): """Constructs a static int tuple given a set of arguments. Args: - elem: The elem to splat into the tuple. + repeat: The elem to splat into the tuple. """ self.data = __mlir_op.`pop.array.repeat`[ _type = __mlir_type[`!pop.array<`, size.value, `, `, Int, `>`] - ](elem) + ](repeat) @always_inline fn __init__(inout self, values: VariadicList[Int]): diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index b35a6ae4f2..042731fefd 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -38,7 +38,7 @@ def test_static_tuple(): def test_static_int_tuple(): assert_equal(str(StaticIntTuple[1](1)), "(1,)") - assert_equal(str(StaticIntTuple[3](2)), "(2, 2, 2)") + assert_equal(str(StaticIntTuple[3](repeat=2)), "(2, 2, 2)") assert_equal( str(StaticIntTuple[3](1, 2, 3) * StaticIntTuple[3](4, 5, 6)), From 4db2df1595cdfef5a97363b964f0326c204bde4b Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 2 Jul 2024 17:05:44 -0500 Subject: [PATCH 1104/2019] [stdlib] Add `int()` overload for `UInt` Add `int()` overload for `UInt` to avoid the need for `.value` everywhere in ****** that want to make use of `UInt` when working with an `Int`. MODULAR_ORIG_COMMIT_REV_ID: 34885cb070eb24ccf9e7dacdfbf41ed38120b876 --- stdlib/src/builtin/int.mojo | 12 ++++++++++++ stdlib/src/builtin/uint.mojo | 13 +++++++++++++ stdlib/test/builtin/test_int.mojo | 9 +++++++++ 3 files changed, 34 insertions(+) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 6916868481..f3e99a070c 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -236,6 +236,18 @@ fn int(value: String, base: Int = 10) raises -> Int: return atol(value, base) +fn int(value: UInt) -> Int: + """Get the Int representation of the value. + + Args: + value: The object to get the integral representation of. + + Returns: + The integral representation of the value. + """ + return value.value + + # ===----------------------------------------------------------------------=== # # Int # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index ade5688300..1f2d5fd190 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -766,3 +766,16 @@ struct UInt(Comparable, Formattable, Representable, Stringable): ) else: _format_scalar(writer, UInt64(self)) + + +fn _temp_uint_from_int(x: Int) -> UInt: + """Constructs a UInt from an Int. + + This is intentionally not an explicit constructor of UInt for + greppability purposes as we intend to remove this function entirely + once migration is done with UInt in internal code. + + Args: + x: The Int value to construct a UInt from. + """ + return UInt(x.value) diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 9f5edcf096..f6cc87f919 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -187,6 +187,14 @@ def test_decimal_digit_count(): assert_equal(Int.MIN._decimal_digit_count(), 19) +def test_int_uint(): + var u1 = UInt(42) + assert_equal(42, int(u1)) + + var u2 = UInt(0) + assert_equal(0, int(u2)) + + def main(): test_properties() test_add() @@ -206,3 +214,4 @@ def main(): test_indexer() test_bool() test_decimal_digit_count() + test_int_uint() From b0f1201c659f83bc48949d4365495d7d358bfa1f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 2 Jul 2024 17:37:53 -0500 Subject: [PATCH 1105/2019] [stdlib] Migrate some `Pointer.address_of` uses As part of consolidating off of `Pointer`/`LegacyPointer`, switch many uses of `Pointer.address_of` over to `UnsafePointer.address_of`. MODULAR_ORIG_COMMIT_REV_ID: 496752de076e231cf0abdabe7f26f9c075c97808 --- docs/manual/pointers.ipynb | 4 ++-- examples/notebooks/programming-manual.ipynb | 2 +- stdlib/src/utils/static_tuple.mojo | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 4e7dce77b3..b15a3e1898 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -585,7 +585,7 @@ "\n", "- You can [`alloc()`](/mojo/stdlib/memory/unsafe/DTypePointer#alloc) and\n", " [`free()`](/mojo/stdlib/memory/unsafe/DTypePointer#free) memory, or use \n", - " [`address_of()`](/mojo/stdlib/memory/unsafe/DTypePointer#address_of) to point\n", + " [`address_of()`](/mojo/stdlib/memory/unsafe/DTypeUnsafePointer.address_of) to point\n", " to an existing value.\n", "- The pointer supports pointer arithmetic to access adjacent memory locations.\n", "- You can dereference a `DTypePointer` using subscript notation.\n", diff --git a/examples/notebooks/programming-manual.ipynb b/examples/notebooks/programming-manual.ipynb index ace149a6ba..f1ead404f5 100644 --- a/examples/notebooks/programming-manual.ipynb +++ b/examples/notebooks/programming-manual.ipynb @@ -3388,7 +3388,7 @@ "```mojo\n", "__get_address_as_lvalue(x)\n", "__get_address_as_uninit_lvalue(x)\n", - "__get_lvalue_as_address(x): use Pointer.address_of instead\n", + "__get_lvalue_as_address(x): use UnsafePointer.address_of instead\n", "__get_address_as_owned_value(x)\n", "```\n", "-->\n", diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 1af6ee3bad..799d6f4de4 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -21,7 +21,7 @@ from utils import StaticTuple from collections._index_normalization import normalize_index from sys.intrinsics import _type_is_eq -from memory import Pointer +from memory import Pointer, UnsafePointer from utils import unroll @@ -211,7 +211,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): # address of 'self' in a non-mutating method. var arrayCopy = self.array var ptr = __mlir_op.`pop.array.gep`( - Pointer.address_of(arrayCopy).address, offset.value + UnsafePointer.address_of(arrayCopy).address, offset.value ) var result = Pointer(ptr).load() _ = arrayCopy @@ -234,7 +234,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): debug_assert(offset < size, "index must be within bounds") var tmp = self var ptr = __mlir_op.`pop.array.gep`( - Pointer.address_of(tmp.array).address, offset.value + UnsafePointer.address_of(tmp.array).address, offset.value ) Pointer(ptr).store(val) self = tmp From 5816a321bea7b80de4438c29b9aec4636e1627bf Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 2 Jul 2024 17:44:28 -0500 Subject: [PATCH 1106/2019] [External] [stdlib] Clarify error message in `test_tempfile.mojo` (#42749) [External] [stdlib] Clarify error message in `test_tempfile.mojo` Fairly simple change that helps contributors know what went wrong and which directory to delete if an error is triggered there. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3152 MODULAR_ORIG_COMMIT_REV_ID: 1fad5998e40bef14373b60b251da31be13456be5 --- stdlib/test/tempfile/test_tempfile.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index f4e48cdc65..ebe9e2497d 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -98,7 +98,10 @@ def _set_up_gettempdir_test( os.mkdir(dir_without_writing_access, mode=0o100) except: os.rmdir(dir_with_writing_access) - raise Error("Failed to setup test") + raise Error( + "Failed to setup test, couldn't create " + + str(dir_without_writing_access) + ) def test_gettempdir(): From 85620f73402a3cc02e8edbf8e80f727f50947739 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 2 Jul 2024 17:45:14 -0500 Subject: [PATCH 1107/2019] [External] [stdlib] Be explicit about `PythonObject` conversions (#42748) [External] [stdlib] Be explicit about `PythonObject` conversions Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3166 MODULAR_ORIG_COMMIT_REV_ID: 15f58fa019d51135c34d0d0da446331ae608e08c --- stdlib/src/python/object.mojo | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 89992d0220..5aaa3a94da 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -58,16 +58,16 @@ struct _PyIter(Sized): var maybeNextItem = cpython.PyIter_Next(self.iterator.py_object) if maybeNextItem.is_null(): self.isDone = True - self.preparedNextItem = PyObjectPtr() + self.preparedNextItem = PythonObject(PyObjectPtr()) else: - self.preparedNextItem = maybeNextItem + self.preparedNextItem = PythonObject(maybeNextItem) self.isDone = False fn __init__(inout self): """Initialize an empty iterator.""" - self.iterator = PyObjectPtr() + self.iterator = PythonObject(PyObjectPtr()) self.isDone = True - self.preparedNextItem = PyObjectPtr() + self.preparedNextItem = PythonObject(PyObjectPtr()) fn __next__(inout self: _PyIter) -> PythonObject: """Return the next item and update to point to subsequent item. @@ -84,7 +84,7 @@ struct _PyIter(Sized): if maybeNextItem.is_null(): self.isDone = True else: - self.preparedNextItem = maybeNextItem + self.preparedNextItem = PythonObject(maybeNextItem) return current fn __len__(self) -> Int: @@ -101,7 +101,6 @@ struct _PyIter(Sized): @register_passable struct PythonObject( - CollectionElement, ImplicitlyBoolable, Indexer, Intable, @@ -246,15 +245,15 @@ struct PythonObject( @parameter if _type_is_eq[T, Int](): - obj = value.get[i, Int]() + obj = PythonObject(value.get[i, Int]()) elif _type_is_eq[T, Float64](): - obj = value.get[i, Float64]() + obj = PythonObject(value.get[i, Float64]()) elif _type_is_eq[T, Bool](): - obj = value.get[i, Bool]() + obj = PythonObject(value.get[i, Bool]()) elif _type_is_eq[T, StringRef](): - obj = value.get[i, StringRef]() + obj = PythonObject(value.get[i, StringRef]()) elif _type_is_eq[T, StringLiteral](): - obj = value.get[i, StringLiteral]() + obj = PythonObject(value.get[i, StringLiteral]()) else: obj = PythonObject(0) constrained[ @@ -286,15 +285,15 @@ struct PythonObject( @parameter if _type_is_eq[T, Int](): - obj = value.get[i, Int]() + obj = PythonObject(value.get[i, Int]()) elif _type_is_eq[T, Float64](): - obj = value.get[i, Float64]() + obj = PythonObject(value.get[i, Float64]()) elif _type_is_eq[T, Bool](): - obj = value.get[i, Bool]() + obj = PythonObject(value.get[i, Bool]()) elif _type_is_eq[T, StringRef](): - obj = value.get[i, StringRef]() + obj = PythonObject(value.get[i, StringRef]()) elif _type_is_eq[T, StringLiteral](): - obj = value.get[i, StringLiteral]() + obj = PythonObject(value.get[i, StringLiteral]()) else: obj = PythonObject(0) constrained[ @@ -340,7 +339,7 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() var iter = cpython.PyObject_GetIter(self.py_object) Python.throw_python_exception_if_error_state(cpython) - return _PyIter(iter) + return _PyIter(PythonObject(iter)) fn __del__(owned self): """Destroy the object. From b191d56e03f4d3ae3ddd15dc5ba4524fe14dd5c5 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 2 Jul 2024 16:59:39 -0700 Subject: [PATCH 1108/2019] [mojo-stdlib] Reduce `@always_inline` usage in String and List Excessive use of the decorator for non-trivial functions is causing compile time explosion. These aren't particularly perf-sensitive functions and they don't stand to benefit much from inlining anyways. MODULAR_ORIG_COMMIT_REV_ID: 833e2908964a4c7239820aa26174a227ac3864b1 --- stdlib/src/builtin/string.mojo | 16 +--------------- stdlib/src/collections/list.mojo | 19 ++++--------------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index b017dea3ef..501533fdea 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -31,7 +31,6 @@ from utils._format import Formattable, Formatter, ToFormatter # ===----------------------------------------------------------------------=== # -@always_inline fn ord(s: String) -> Int: """Returns an integer that represents the given one-character string. @@ -149,7 +148,6 @@ fn chr(c: Int) -> String: # ===----------------------------------------------------------------------=== # -@always_inline("nodebug") fn _chr_ascii(c: UInt8) -> String: """Returns a string based on the given ASCII code point. @@ -162,7 +160,6 @@ fn _chr_ascii(c: UInt8) -> String: return String(String._buffer_type(c, 0)) -@always_inline("nodebug") fn _repr_ascii(c: UInt8) -> String: """Returns a printable representation of the given ASCII code point. @@ -196,7 +193,7 @@ fn _repr_ascii(c: UInt8) -> String: # TODO: This is currently the same as repr, should change with unicode strings -@always_inline("nodebug") +@always_inline fn ascii(value: String) -> String: """Get the ASCII representation of the object. @@ -214,7 +211,6 @@ fn ascii(value: String) -> String: # ===----------------------------------------------------------------------=== # -@always_inline fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: """Implementation of `atol` for StringRef inputs. @@ -406,7 +402,6 @@ fn _atof_error(str_ref: StringRef) -> Error: return Error("String is not convertible to float: '" + str(str_ref) + "'") -@always_inline fn _atof(str_ref: StringRef) raises -> Float64: """Implementation of `atof` for StringRef inputs. @@ -869,7 +864,6 @@ struct String( """ self.__copyinit__(other) - @always_inline fn __init__(inout self, str: StringRef): """Construct a string from a StringRef object. @@ -883,7 +877,6 @@ struct String( memcpy(dest=buffer.data, src=str.data, count=length) self = Self(buffer^) - @always_inline fn __init__(inout self, str_slice: StringSlice): """Construct a string from a string slice. @@ -966,7 +959,6 @@ struct String( """ self = String(ptr.address, len) - @always_inline fn __init__(inout self, obj: PythonObject): """Creates a string from a python object. @@ -1193,7 +1185,6 @@ struct String( """ return not (self < rhs) - @always_inline fn __add__(self, other: String) -> String: """Creates a string by appending another string at the end. @@ -1236,7 +1227,6 @@ struct String( """ return other + self - @always_inline fn __iadd__(inout self, other: String): """Appends another string to this string. @@ -1318,7 +1308,6 @@ struct String( """ return self - @always_inline fn __repr__(self) -> String: """Return a Mojo-compatible representation of the `String` instance. @@ -2021,7 +2010,6 @@ struct String( # outside of the standard ASCII letters. return self._toggle_ascii_case[_is_ascii_lowercase]() - @always_inline fn _toggle_ascii_case[check_case: fn (UInt8) -> Bool](self) -> String: var copy: String = self @@ -2158,7 +2146,6 @@ struct String( ) return String(buf^) - @always_inline fn format[*Ts: Stringable](self, *args: *Ts) raises -> String: """Format a template with *args. @@ -2241,7 +2228,6 @@ struct String( return False return True - @always_inline fn _isupper_islower[*, upper: Bool](self) -> Bool: fn is_ascii_cased(c: UInt8) -> Bool: return _is_ascii_uppercase(c) or _is_ascii_lowercase(c) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 0da6eba125..cce0d1bedd 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -187,7 +187,6 @@ struct List[T: CollectionElement]( for i in range(len(existing)): self.append(existing[i]) - @always_inline fn __del__(owned self): """Destroy all elements in the list and free its memory.""" for i in range(self.size): @@ -199,7 +198,6 @@ struct List[T: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - @always_inline fn __contains__[ T2: ComparableCollectionElement ](self: List[T], value: T2) -> Bool: @@ -226,7 +224,7 @@ struct List[T: CollectionElement]( return True return False - @always_inline("nodebug") + @always_inline fn __mul__(self, x: Int) -> Self: """Multiplies the list by x and returns a new list. @@ -243,7 +241,7 @@ struct List[T: CollectionElement]( result.__mul(x) return result^ - @always_inline("nodebug") + @always_inline fn __imul__(inout self, x: Int): """Multiplies the list by x in place. @@ -252,7 +250,7 @@ struct List[T: CollectionElement]( """ self.__mul(x) - @always_inline("nodebug") + @always_inline fn __add__(self, owned other: Self) -> Self: """Concatenates self with other and returns the result as a new list. @@ -266,7 +264,7 @@ struct List[T: CollectionElement]( result.extend(other^) return result^ - @always_inline("nodebug") + @always_inline fn __iadd__(inout self, owned other: Self): """Appends the elements of other into self. @@ -388,7 +386,6 @@ struct List[T: CollectionElement]( # Methods # ===-------------------------------------------------------------------===# - @always_inline fn _realloc(inout self, new_capacity: Int): var new_data = UnsafePointer[T].alloc(new_capacity) @@ -412,7 +409,6 @@ struct List[T: CollectionElement]( (self.data + self.size).init_pointee_move(value^) self.size += 1 - @always_inline fn insert(inout self, i: Int, owned value: T): """Inserts a value to the list at the given index. `a.insert(len(a), value)` is equivalent to `a.append(value)`. @@ -442,7 +438,6 @@ struct List[T: CollectionElement]( earlier_idx -= 1 later_idx -= 1 - @always_inline fn __mul(inout self, x: Int): """Appends the original elements of this list x-1 times. @@ -462,7 +457,6 @@ struct List[T: CollectionElement]( for i in range(x - 1): self.extend(orig) - @always_inline fn extend(inout self, owned other: List[T]): """Extends this list by consuming the elements of `other`. @@ -503,7 +497,6 @@ struct List[T: CollectionElement]( # list. self.size = final_size - @always_inline fn pop(inout self, i: Int = -1) -> T: """Pops a value from the list at the given index. @@ -542,7 +535,6 @@ struct List[T: CollectionElement]( return self._realloc(new_capacity) - @always_inline fn resize(inout self, new_size: Int, value: T): """Resizes the list to the given new size. @@ -562,7 +554,6 @@ struct List[T: CollectionElement]( (self.data + i).init_pointee_copy(value) self.size = new_size - @always_inline fn resize(inout self, new_size: Int): """Resizes the list to the given new size. @@ -584,7 +575,6 @@ struct List[T: CollectionElement]( self.size = new_size self.reserve(new_size) - @always_inline fn reverse(inout self): """Reverses the elements of the list.""" @@ -680,7 +670,6 @@ struct List[T: CollectionElement]( self.capacity = 0 return ptr - @always_inline fn __getitem__(self, span: Slice) -> Self: """Gets the sequence of elements at the specified positions. From d99583d37c0ea57e03239cf5a37e84d5873574a4 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 2 Jul 2024 16:59:55 -0700 Subject: [PATCH 1109/2019] [mojo] Mark certain things as `@no_inline` The Mojo inliner is in general a bit too aggressive in certain cases, inlining moderately sized functions without clear optimization benefits. MODULAR_ORIG_COMMIT_REV_ID: a8e3846f78d9964200e5cd2f0c4c8a9e12ae9553 --- stdlib/src/memory/arc.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 6a78d5b44d..0aefb67801 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -121,6 +121,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew): existing._inner[].add_ref() self._inner = existing._inner + @no_inline fn __del__(owned self): """Delete the smart pointer reference. From 8faff5b3d40d799ba86aa9a1d4490fcc2cf03c79 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 2 Jul 2024 19:11:37 -0700 Subject: [PATCH 1110/2019] [Stdlib] Add special cases for rsqrt and sqrt MODULAR_ORIG_COMMIT_REV_ID: 8041acd6ca3f79467dbd1c254fc126d11d107cff --- stdlib/src/math/math.mojo | 60 ++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 27fdbf1e0a..f12ab36091 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -198,6 +198,22 @@ fn sqrt(x: Int) -> Int: return r +@always_inline +fn _sqrt_nvvm(x: SIMD) -> __type_of(x): + constrained[ + x.type in (DType.float32, DType.float64), "must be f32 or f64 type" + ]() + alias instruction = "llvm.nvvm.sqrt.approx.ftz.f" if x.type is DType.float32 else "llvm.nvvm.sqrt.approx.d" + var res = __type_of(x)() + + @parameter + for i in range(x.size): + res[i] = llvm_intrinsic[ + instruction, Scalar[x.type], has_side_effect=False + ](x[i]) + return res + + @always_inline fn sqrt[ type: DType, simd_width: Int, // @@ -232,10 +248,14 @@ fn sqrt[ for i in range(simd_width): res[i] = sqrt(int(x[i])) return res - else: - return llvm_intrinsic["llvm.sqrt", __type_of(x), has_side_effect=False]( - x - ) + elif triple_is_nvidia_cuda(): + + @parameter + if x.type in (DType.float16, DType.bfloat16): + return _sqrt_nvvm(x.cast[DType.float32]()).cast[x.type]() + return _sqrt_nvvm(x) + + return llvm_intrinsic["llvm.sqrt", __type_of(x), has_side_effect=False](x) # ===----------------------------------------------------------------------=== # @@ -243,6 +263,23 @@ fn sqrt[ # ===----------------------------------------------------------------------=== # +@always_inline +fn _rsqrt_nvvm(x: SIMD) -> __type_of(x): + constrained[ + x.type in (DType.float32, DType.float64), "must be f32 or f64 type" + ]() + + alias instruction = "llvm.nvvm.rsqrt.approx.ftz.f" if x.type is DType.float32 else "llvm.nvvm.rsqrt.approx.d" + var res = __type_of(x)() + + @parameter + for i in range(x.size): + res[i] = llvm_intrinsic[ + instruction, Scalar[x.type], has_side_effect=False + ](x[i]) + return res + + @always_inline fn rsqrt(x: SIMD) -> __type_of(x): """Performs elementwise reciprocal square root on a SIMD vector. @@ -253,6 +290,17 @@ fn rsqrt(x: SIMD) -> __type_of(x): Returns: The elementwise reciprocal square root of x. """ + constrained[x.type.is_floating_point(), "type must be floating point"]() + + @parameter + if triple_is_nvidia_cuda(): + + @parameter + if x.type in (DType.float16, DType.bfloat16): + return _rsqrt_nvvm(x.cast[DType.float32]()).cast[x.type]() + + return _rsqrt_nvvm(x) + return 1 / sqrt(x) @@ -2113,9 +2161,7 @@ fn factorial(n: Int) -> Int: fn _type_is_libm_supported(type: DType) -> Bool: - if type in (DType.float32, DType.float64): - return True - return type.is_integral() + return type in (DType.float32, DType.float64) or type.is_integral() fn _call_libm[ From be55fd16a0dbb329531f08031a2169d112f18c86 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Jul 2024 12:01:21 -0500 Subject: [PATCH 1111/2019] [stdlib] Remove < 0 checks in `UInt` Since `UInt` is guaranteed to be `>= 0` by definition, there's no need for any of its member methods to do comparisons with `< 0`. So, remove them. They were likely just copy-pasted implementation wise from `Int` when bringing up `UInt` recently. MODULAR_ORIG_COMMIT_REV_ID: 17827909329f41629d4b9f463d528f17005b63db --- stdlib/src/builtin/uint.mojo | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 1f2d5fd190..f2f4901660 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -248,10 +248,6 @@ struct UInt(Comparable, Formattable, Representable, Stringable): Returns: The value of `self` raised to the power of `exp`. """ - if exp < 0: - # Not defined for Integers, this should raise an - # exception. - return 0 var res: UInt = 1 var x = self var n = exp @@ -272,9 +268,6 @@ struct UInt(Comparable, Formattable, Representable, Stringable): Returns: `self << rhs`. """ - if rhs < 0: - # this should raise an exception. - return 0 return __mlir_op.`index.shl`(self.value, rhs.value) @always_inline("nodebug") @@ -287,9 +280,6 @@ struct UInt(Comparable, Formattable, Representable, Stringable): Returns: `self >> rhs`. """ - if rhs < 0: - # this should raise an exception. - return 0 return __mlir_op.`index.shru`(self.value, rhs.value) @always_inline("nodebug") From 95ac710efac3a3a8b6ab8301344ae78e5939c9c9 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 3 Jul 2024 13:04:44 -0700 Subject: [PATCH 1112/2019] [mojo-stdlib] Remove even more `@always_inline` MODULAR_ORIG_COMMIT_REV_ID: f0a92776bb1d3047b092a439ad0bc3d08e6627cc --- .../benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/bit/bit.mojo | 2 +- stdlib/src/builtin/error.mojo | 8 ++------ stdlib/src/builtin/file.mojo | 5 ----- stdlib/src/builtin/format_int.mojo | 10 ---------- stdlib/src/builtin/string.mojo | 1 - stdlib/src/collections/counter.mojo | 2 +- stdlib/src/collections/dict.mojo | 18 +++++++++--------- stdlib/src/collections/inline_list.mojo | 3 --- stdlib/src/os/os.mojo | 4 ++-- stdlib/src/pathlib/path.mojo | 1 - stdlib/src/tempfile/tempfile.mojo | 2 -- stdlib/src/utils/inline_string.mojo | 1 - 13 files changed, 16 insertions(+), 43 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index fc0748b5d3..b50b4c9458 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -52,7 +52,7 @@ struct Op(Stringable): fn __eq__(self, other: Op) -> Bool: return self.op_code == other.op_code - @always_inline("nodebug") + @always_inline fn __str__(self) -> String: var op_list = List[String]( "add", "sub", "mul", "div", "fma", "ld", "st" diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 5c9a05b633..5d5e5a1591 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -357,7 +357,7 @@ fn is_power_of_two[ # reference: https://en.cppreference.com/w/cpp/numeric/bit_ceil -@always_inline("nodebug") +@always_inline fn bit_ceil(val: Int) -> Int: """Computes the smallest power of 2 that is greater than or equal to the input value. Any integral value less than or equal to 1 will be ceiled to 1. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 4c582bdd8a..59b05566bf 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -47,7 +47,7 @@ struct Error( ownership and a free is executed in the destructor. """ - @always_inline("nodebug") + @always_inline fn __init__() -> Self: """Default constructor. @@ -56,7 +56,7 @@ struct Error( """ return Error {data: UnsafePointer[UInt8](), loaded_length: 0} - @always_inline("nodebug") + @always_inline fn __init__(value: StringLiteral) -> Self: """Construct an Error object with a given string literal. @@ -71,7 +71,6 @@ struct Error( loaded_length: len(value), } - @always_inline("nodebug") fn __init__(src: String) -> Self: """Construct an Error object with a given string. @@ -91,7 +90,6 @@ struct Error( dest[length] = 0 return Error {data: dest, loaded_length: -length} - @always_inline("nodebug") fn __init__(src: StringRef) -> Self: """Construct an Error object with a given string ref. @@ -127,7 +125,6 @@ struct Error( if self.loaded_length < 0: self.data.free() - @always_inline("nodebug") fn __copyinit__(existing: Self) -> Self: """Creates a deep copy of an existing error. @@ -180,7 +177,6 @@ struct Error( """ return "Error(" + repr(self._message()) + ")" - @always_inline fn _message(self) -> String: """Converts the Error to string representation. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 38e4594358..7d7b1ca9f3 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -104,7 +104,6 @@ struct FileHandle: self.handle = handle - @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -136,7 +135,6 @@ struct FileHandle: self.handle = existing.handle existing.handle = DTypePointer[DType.invalid]() - @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads data from a file and sets the file handle seek position. If size is left as the default of -1, it will read to the end of the file. @@ -205,7 +203,6 @@ struct FileHandle: return String(buf, int(size_copy) + 1) - @always_inline fn read[ type: DType ](self, ptr: DTypePointer[type], size: Int64 = -1) raises -> Int64: @@ -422,7 +419,6 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) - @always_inline fn write(self, data: StringRef) raises: """Write the data to the file. @@ -431,7 +427,6 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) - @always_inline fn _write[ address_space: AddressSpace ](self, ptr: UnsafePointer[UInt8, address_space], len: Int) raises: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index b7b9aeb498..63a344c6d1 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -27,7 +27,6 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" # ===----------------------------------------------------------------------===# -@always_inline fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: """Return the binary string representation an integral value. @@ -52,7 +51,6 @@ fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: # Need this until we have constraints to stop the compiler from matching this # directly to bin[type: DType](num: Scalar[type]). -@always_inline("nodebug") fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of a scalar bool. @@ -66,7 +64,6 @@ fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: return bin(b.cast[DType.int8](), prefix=prefix) -@always_inline("nodebug") fn bin[T: Indexer, //](num: T, /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of an indexer type. @@ -106,7 +103,6 @@ fn hex(value: Scalar, /, *, prefix: StaticString = "0x") -> String: return _try_format_int(value, 16, prefix=prefix) -@always_inline fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given integer. @@ -128,7 +124,6 @@ fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: return hex(Scalar[DType.index](index(value)), prefix=prefix) -@always_inline fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given scalar bool. @@ -152,7 +147,6 @@ fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: # ===----------------------------------------------------------------------===# -@always_inline fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. @@ -171,7 +165,6 @@ fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: return _try_format_int(value, 8, prefix=prefix) -@always_inline fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. @@ -193,7 +186,6 @@ fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: return oct(Scalar[DType.index](index(value)), prefix=prefix) -@always_inline fn oct(value: Scalar[DType.bool], /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given scalar bool. @@ -252,7 +244,6 @@ fn _format_int[ return string^ -@always_inline fn _write_int[ type: DType, //, ]( @@ -271,7 +262,6 @@ fn _write_int[ raise err.value() -@always_inline fn _try_write_int[ type: DType, //, ]( diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 501533fdea..dca633d6a3 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2355,7 +2355,6 @@ fn _calc_initial_buffer_size_int64(n0: UInt64) -> Int: result += 4 -@always_inline fn _calc_initial_buffer_size(n0: Int) -> Int: var sign = 0 if n0 > 0 else 1 diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 4e78a7f9a4..87976f67f8 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -502,7 +502,7 @@ struct CountTuple[V: KeyElement]( """ return self._count == other._count - @always_inline("nodebug") + @always_inline fn __getitem__(self, idx: Int) -> Variant[V, Int]: """Get an element in the tuple. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 46aefeda0a..eb9d52a890 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -1040,7 +1040,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __getitem__(self, key: Self.key_type) raises -> V: """Retrieve a value out of the keyword dictionary. @@ -1055,7 +1055,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict[key] - @always_inline("nodebug") + @always_inline fn __setitem__(inout self, key: Self.key_type, value: V): """Set a value in the keyword dictionary by key. @@ -1069,7 +1069,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Trait implementations # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __contains__(self, key: Self.key_type) -> Bool: """Check if a given key is in the keyword dictionary or not. @@ -1082,7 +1082,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return key in self._dict - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: """The number of elements currently stored in the keyword dictionary. @@ -1095,7 +1095,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Methods # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn find(self, key: Self.key_type) -> Optional[V]: """Find a value in the keyword dictionary by key. @@ -1108,7 +1108,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict.find(key) - @always_inline("nodebug") + @always_inline fn pop(inout self, key: self.key_type, owned default: V) -> V: """Remove a value from the dictionary by key. @@ -1123,7 +1123,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict.pop(key, default^) - @always_inline("nodebug") + @always_inline fn pop(inout self, key: self.key_type) raises -> V: """Remove a value from the dictionary by key. @@ -1196,10 +1196,10 @@ struct OwnedKwargsDict[V: CollectionElement]( # return self[]._dict.items() return _DictEntryIter(0, 0, self._dict) - @always_inline("nodebug") + @always_inline fn _insert(inout self, owned key: Self.key_type, owned value: V): self._dict._insert(key^, value^) - @always_inline("nodebug") + @always_inline fn _insert(inout self, key: StringLiteral, owned value: V): self._insert(String(key), value^) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index c8bd0c02b5..c5267afb1c 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -180,7 +180,6 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """ return _InlineListIter(0, self) - @always_inline fn __contains__[ C: ComparableCollectionElement ](self: Self, value: C) -> Bool: @@ -213,7 +212,6 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # Methods # ===-------------------------------------------------------------------===# - @always_inline fn count[C: ComparableCollectionElement](self: Self, value: C) -> Int: """Counts the number of occurrences of a value in the list. @@ -241,7 +239,6 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): count += 1 return count - @always_inline fn append(inout self, owned value: ElementType): """Appends a value to the list. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 5b0286b4a6..6ff2f17dbc 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -224,7 +224,7 @@ fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: # ===----------------------------------------------------------------------=== # -@always_inline("nodebug") +@no_inline fn abort[result: AnyType = NoneType]() -> result: """Calls a target dependent trap instruction if available. @@ -242,7 +242,7 @@ fn abort[result: AnyType = NoneType]() -> result: pass -@always_inline("nodebug") +@no_inline fn abort[ result: AnyType = NoneType, *, formattable: Formattable ](message: formattable) -> result: diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index b6a16edafc..244edfb9df 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -318,7 +318,6 @@ struct Path( with open(self, "w") as f: f.write(str(value)) - @always_inline fn suffix(self) -> String: """The path's extension, if any. This includes the leading period. For example: '.txt'. diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 2feec1be5b..86a78a9eb0 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -316,7 +316,6 @@ struct NamedTemporaryFile: except: raise Error("Failed to create temporary file") - @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -340,7 +339,6 @@ struct NamedTemporaryFile: self._delete = existing._delete self.name = existing.name^ - @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads the data from the file. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 5257772fcc..d5481cd223 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -340,7 +340,6 @@ struct _FixedString[CAP: Int]( """ self = other - @always_inline fn __init__(inout self, literal: StringLiteral) raises: """Constructs a FixedString value given a string literal. From 06655494bbd9a44c7119bcb7934be7bace837d17 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:39:32 -0400 Subject: [PATCH 1113/2019] [stdlib] Removing `FileCheck` from standard library tests. This PR gives a way to avoid `FileCheck` in `test_print.mojo`. The output of `print` is redirected into a temporary file and re-read to check if the output matches. MODULAR_ORIG_COMMIT_REV_ID: 41c5e6ce25efb1d99983ce00036e7a355e0e6608 --- stdlib/src/tempfile/tempfile.mojo | 10 +- stdlib/test/builtin/test_print.mojo | 181 +++++++++++++++++++--------- 2 files changed, 131 insertions(+), 60 deletions(-) diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 86a78a9eb0..9864ba1b5f 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -286,7 +286,7 @@ struct NamedTemporaryFile: Args: mode: The mode to open the file in (the mode can be "r" or "w"). - name: The name of the temp file; if it is unspecified, then random name will be provided. + name: The name of the temp file. If it is unspecified, then a random name will be provided. suffix: Suffix to use for the file name if name is not provided. prefix: Prefix to use for the file name if name is not provided. dir: Directory in which the file will be created. @@ -362,11 +362,15 @@ struct NamedTemporaryFile: """ return self._file_handle.read_bytes(size) - fn seek(self, offset: UInt64) raises -> UInt64: + fn seek(self, offset: UInt64, whence: UInt8 = os.SEEK_SET) raises -> UInt64: """Seeks to the given offset in the file. Args: offset: The byte offset to seek to from the start of the file. + whence: The reference point for the offset: + os.SEEK_SET = 0: start of file (Default). + os.SEEK_CUR = 1: current position. + os.SEEK_END = 2: end of file. Raises: An error if this file handle is invalid, or if file seek returned a @@ -375,7 +379,7 @@ struct NamedTemporaryFile: Returns: The resulting byte offset from the start of the file. """ - return self._file_handle.seek(offset) + return self._file_handle.seek(offset, whence) fn write(self, data: String) raises: """Write the data to the file. diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 080355a76b..b84079b904 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -10,72 +10,139 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s import sys +from tempfile import NamedTemporaryFile +from testing import assert_equal from memory import DTypePointer +from builtin._location import __call_location, _SourceLocation from utils import StaticIntTuple, StringRef -# CHECK-LABEL: test_print -fn test_print(): - print("== test_print") - - # CHECK: Hello - print("Hello") - - # CHECK: World - print("World", flush=True) - - var hello: StringRef = "Hello," - var world: String = "world!" - var f: Bool = False - # CHECK: > Hello, world! 42 True False - print(">", hello, world, 42, True, f) - - # CHECK: > 3.14000{{[0-9]+}} 99.90000{{[0-9]+}} -129.29018{{[0-9]+}} (1, 2, 3) - var float32: Float32 = 99.9 - var float64: Float64 = -129.2901823 - print("> ", end="") - print(3.14, float32, float64, StaticIntTuple[3](1, 2, 3), end="") - print() - - # CHECK: > 9223372036854775806 - print(">", 9223372036854775806) - - var pi = 3.1415916535897743 - # CHECK: > 3.1415916535{{[0-9]+}} - print(">", pi) - var x = (pi - 3.141591) * 1e6 - # CHECK: > 0.6535{{[0-9]+}} - print(">", x) - - # CHECK: Hello world - print(String("Hello world")) - - -# CHECK-LABEL: test_print_end -fn test_print_end(): - print("== test_print_end") - # CHECK: Hello World - print("Hello", end=" World\n") - - -# CHECK-LABEL: test_print_sep -fn test_print_sep(): - print("== test_print_sep") - - # CHECK: a/b/c - print("a", "b", "c", sep="/") - - # CHECK: a/1/2xx - print("a", 1, 2, sep="/", end="xx\n") - - -fn main(): +@always_inline +fn _assert_error[T: Stringable](msg: T, loc: _SourceLocation) -> String: + return loc.prefix("AssertionError: " + str(msg)) + + +fn _assert_equal_error( + lhs: String, rhs: String, msg: String, loc: _SourceLocation +) -> String: + var err = ( + "`left == right` comparison failed:\n left: " + + lhs + + "\n right: " + + rhs + ) + if msg: + err += "\n reason: " + msg + return _assert_error(err, loc) + + +struct PrintChecker: + var tmp: NamedTemporaryFile + var cursor: UInt64 + var call_location: _SourceLocation + + @always_inline + fn __init__(inout self) raises: + self.tmp = NamedTemporaryFile("rw") + self.call_location = __call_location() + self.cursor = 0 + + fn __enter__(owned self) -> Self: + return self^ + + fn __moveinit__(inout self, owned existing: Self): + self.tmp = existing.tmp^ + self.cursor = existing.cursor + self.call_location = existing.call_location + + fn stream(self) -> FileDescriptor: + return self.tmp._file_handle._get_raw_fd() + + fn check_line(inout self, expected: String, msg: String = "") raises: + print(end="", file=self.stream(), flush=True) + _ = self.tmp.seek(self.cursor) + var result = self.tmp.read()[:-1] + if result != expected: + raise _assert_equal_error(result, expected, msg, self.call_location) + self.cursor += len(result) + 1 + + fn check_line_starts_with( + inout self, prefix: String, msg: String = "" + ) raises: + print(end="", file=self.stream(), flush=True) + _ = self.tmp.seek(self.cursor) + var result = self.tmp.read()[:-1] + var prefix_len = len(prefix) + if len(result) < prefix_len: + raise _assert_error(msg, self.call_location) + if result[:prefix_len] != prefix: + raise _assert_equal_error( + result[:prefix_len], prefix, msg, self.call_location + ) + self.cursor += len(result) + 1 + + +def test_print(): + with PrintChecker() as checker: + print("Hello", file=checker.stream()) + checker.check_line("Hello") + + print("World", flush=True, file=checker.stream()) + checker.check_line("World") + + var hello: StringRef = "Hello," + var world: String = "world!" + var f: Bool = False + print(">", hello, world, 42, True, f, file=checker.stream()) + checker.check_line("> Hello, world! 42 True False") + + var float32: Float32 = 99.9 + var float64: Float64 = -129.2901823 + print(">", 3.14, file=checker.stream()) + checker.check_line_starts_with("> 3.14000") + print(">", float32, file=checker.stream()) + checker.check_line_starts_with("> 99.90000") + print(">", float64, file=checker.stream()) + checker.check_line_starts_with("> -129.29018") + print(">", StaticIntTuple[3](1, 2, 3), file=checker.stream()) + checker.check_line_starts_with("> (1, 2, 3)") + + print(">", 9223372036854775806, file=checker.stream()) + checker.check_line("> 9223372036854775806") + + var pi = 3.1415916535897743 + print(">", pi, file=checker.stream()) + checker.check_line_starts_with("> 3.1415916535") + var x = (pi - 3.141591) * 1e6 + print(">", x, file=checker.stream()) + checker.check_line_starts_with("> 0.6535") + + print(String("Hello world"), file=checker.stream()) + checker.check_line("Hello world") + + +def test_print_end(): + with PrintChecker() as checker: + print("Hello", end=" World\n", file=checker.stream()) + checker.check_line("Hello World") + + +def test_print_sep(): + with PrintChecker() as checker: + print("a", "b", "c", sep="/", file=checker.stream()) + checker.check_line("a/b/c") + + print("a", 1, 2, sep="/", end="xx\n", file=checker.stream()) + checker.check_line("a/1/2xx") + + +def main(): test_print() test_print_end() test_print_sep() From fd5ea62b3c1f2120f8f8b744591a740ef651ad07 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Wed, 3 Jul 2024 15:53:21 -0500 Subject: [PATCH 1114/2019] [External] [stdlib] benchmark sort scalar list (#42556) [External] [stdlib] benchmark sort scalar list This PR adds a benchmark for sorting a list of scalars. The benchmarks are performed on the public `sort` and internal `_insertion_sort` and `_small_sort` functions. The intention of the benchmark are following: 1. Evaluate results on tiny lists (size 2 to 5) and observe how much faster the `_small_sort` function is compared to `_insertion_sort`. 2. Evaluate result on tiny lists (size 2 to 5) and observe how much overhead does the `sort` function produce compared to internal functions 3. Evaluate results on small lists (size 10 to 100) and observe how much overhead does the `sort` function produce compared to calling the internal insertion sort function My observations running the benchmarks on an Apple M1: 1. `_small_sort` is sometimes ~10% faster 2. `sort` function performs 5 - 6x slower than internal functions 3. `sort` function performs 3 - 100x slower than internal insertion sort Suggestions: - We should run the benchmark on other architectures to identify if my observations are consistent. - Consider removing the `_small_sort` function if it does really provided almost no benefits compared to insertion sort. - Expose insertion sort as top level function, as it has some nice characteristics which users might want to utilise directly. - Consider simplifying the `_quicksort` function and maybe provide a parameter, which defines upper bound of the list size to be sorted with insertion sort. - Consider adding similar parameter to `sort` function. Co-authored-by: Maxim Zaks Closes modularml/mojo#3022 MODULAR_ORIG_COMMIT_REV_ID: 86e412f3310544c3198b7be355cc3c527b584cc5 --- stdlib/benchmarks/builtin/bench_sort.mojo | 238 ++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 stdlib/benchmarks/builtin/bench_sort.mojo diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo new file mode 100644 index 0000000000..bdb9ebb72d --- /dev/null +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -0,0 +1,238 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo %s -t + +from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from random import * +from stdlib.builtin.sort import sort, _small_sort, _insertion_sort + +# ===----------------------------------------------------------------------===# +# Benchmark Utils +# ===----------------------------------------------------------------------===# + +alias dtypes = List( + DType.uint8, + DType.int8, + DType.uint16, + DType.int16, + DType.float16, + DType.uint32, + DType.int32, + DType.float32, + DType.uint64, + DType.int64, + DType.float64, +) + + +fn random_scalar_list[ + dt: DType +](size: Int, max: Scalar[dt] = Scalar[dt].MAX) -> List[Scalar[dt]]: + var result = List[Scalar[dt]](capacity=size) + for _ in range(size): + + @parameter + if dt.is_integral() and dt.is_signed(): + result.append(random_si64(0, max.cast[DType.int64]()).cast[dt]()) + elif dt.is_integral() and dt.is_unsigned(): + result.append(random_ui64(0, max.cast[DType.uint64]()).cast[dt]()) + else: + var res = random_float64(0, max.cast[DType.float64]()) + # GCC doesn't support cast from float64 to float16 + result.append(res.cast[DType.float32]().cast[dt]()) + return result + + +# ===----------------------------------------------------------------------===# +# Benchmark sort functions with a tiny list size +# ===----------------------------------------------------------------------===# + + +@parameter +fn bench_tiny_list_sort(inout m: Bench) raises: + alias counts = List(2, 3, 4, 5) + + @parameter + for type_index in range(len(dtypes)): + alias dt = dtypes[type_index] + + @parameter + for count_index in range(len(counts)): + alias count = counts[count_index] + var list = random_scalar_list[dt](count) + + @parameter + fn bench_sort_list(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + sort(l1) + + b.iter[call_fn]() + + @parameter + fn bench_small_sort(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + var small_p = rebind[Pointer[Scalar[dt]]](l1.data) + + @parameter + fn _less_than_equal[ + ty: AnyTrivialRegType + ](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( + rhs + ) + + _small_sort[4, Scalar[dt], _less_than_equal](small_p) + _ = l1 + + b.iter[call_fn]() + + @parameter + fn bench_insertion_sort(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + var small_p = rebind[Pointer[Scalar[dt]]](l1.data) + + @parameter + fn _less_than_equal[ + ty: AnyTrivialRegType + ](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( + rhs + ) + + _insertion_sort[Scalar[dt], _less_than_equal]( + small_p, 0, count + ) + _ = l1 + + b.iter[call_fn]() + + m.bench_function[bench_sort_list]( + BenchId( + "bench_std_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + m.bench_function[bench_small_sort]( + BenchId( + "bench_sml_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + m.bench_function[bench_insertion_sort]( + BenchId( + "bench_ins_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + _ = list^ + + +# ===----------------------------------------------------------------------===# +# Benchmark sort functions with a small list size +# ===----------------------------------------------------------------------===# + + +@parameter +fn bench_small_list_sort(inout m: Bench) raises: + alias counts = List(10, 20, 32, 64, 100) + + @parameter + for type_index in range(len(dtypes)): + alias dt = dtypes[type_index] + + @parameter + for count_index in range(len(counts)): + alias count = counts[count_index] + var list = random_scalar_list[dt](count) + + @parameter + fn bench_sort_list(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + sort(l1) + + b.iter[call_fn]() + + @parameter + fn bench_insertion_sort(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + var small_p = rebind[Pointer[Scalar[dt]]](l1.data) + + @parameter + fn _less_than_equal[ + ty: AnyTrivialRegType + ](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( + rhs + ) + + _insertion_sort[Scalar[dt], _less_than_equal]( + small_p, 0, count + ) + _ = l1 + + b.iter[call_fn]() + + m.bench_function[bench_sort_list]( + BenchId( + "bench_std_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + m.bench_function[bench_insertion_sort]( + BenchId( + "bench_ins_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + _ = list^ + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# + + +def main(): + seed() + var m = Bench(BenchConfig(num_repetitions=3, warmup_iters=100)) + + bench_tiny_list_sort(m) + bench_small_list_sort(m) + + m.dump_report() From 119b60ff9094d359384bcf8c688e384a49750cfa Mon Sep 17 00:00:00 2001 From: Tatiana Shpeisman Date: Wed, 3 Jul 2024 14:09:25 -0700 Subject: [PATCH 1115/2019] [stdlib] Use sizeof[DType] and bitwidthof[DType] when DType is known at compile time This replaces `DType.sizeof()` with `sizeof[DType]()` and `DType.bitwidth` with `bitwidth[DType]()` when `DType` is a parameter. This is cleaner because DType is treated as a parameter and is guaranteed to work with target-dependent types. MODULAR_ORIG_COMMIT_REV_ID: 2ab52a594fa1ca1e525f8cc50360284c56b6b42a --- stdlib/src/builtin/hash.mojo | 8 ++++---- stdlib/src/math/math.mojo | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 80442ffae9..f03d91192d 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -124,7 +124,7 @@ fn _djbx33a_hash_update[ # Based on the hash function used by ankerl::unordered_dense::hash # https://martin.ankerl.com/2022/08/27/hashmap-bench-01/#ankerl__unordered_dense__hash fn _ankerl_init[type: DType, size: Int]() -> SIMD[type, size]: - alias int_type = _uint_type_of_width[type.bitwidth()]() + alias int_type = _uint_type_of_width[bitwidthof[type]()]() alias init = Int64(-7046029254386353131).cast[int_type]() return SIMD[type, size](bitcast[type, 1](init)) @@ -133,7 +133,7 @@ fn _ankerl_hash_update[ type: DType, size: Int ](data: SIMD[type, size], next: SIMD[type, size]) -> SIMD[type, size]: # compute the hash as though the type is uint - alias int_type = _uint_type_of_width[type.bitwidth()]() + alias int_type = _uint_type_of_width[bitwidthof[type]()]() var data_int = bitcast[int_type, size](data) var next_int = bitcast[int_type, size](next) var result = (data_int * next_int) ^ next_int @@ -173,7 +173,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: var hash_data = _ankerl_init[type, size]() hash_data = _ankerl_hash_update(hash_data, data) - alias int_type = _uint_type_of_width[type.bitwidth()]() + alias int_type = _uint_type_of_width[bitwidthof[type]()]() var final_data = bitcast[int_type, 1](hash_data[0]).cast[DType.uint64]() @parameter @@ -237,7 +237,7 @@ fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: hash collision statistical properties for common data structures. """ alias type = DType.uint64 - alias type_width = type.bitwidth() // DType.int8.bitwidth() + alias type_width = bitwidthof[type]() // bitwidthof[DType.int8]() alias simd_width = simdwidthof[type]() # stride is the byte length of the whole SIMD vector alias stride = type_width * simd_width diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index f12ab36091..5d00e5036a 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -520,7 +520,7 @@ fn exp[ if ( not type is DType.float64 and type is not DType.float32 - and type.sizeof() < DType.float32.sizeof() + and sizeof[type]() < sizeof[DType.float32]() ): return exp(x.cast[DType.float32]()).cast[type]() From 63a784e2019bb7a8dfe50c3308f9fba08d8f157e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 3 Jul 2024 16:48:29 -0700 Subject: [PATCH 1116/2019] Revert "[mojo-stdlib] Remove even more `@always_inline` (#42773)" (#42842) Stacked PRs: * #42829 * #42814 * #42783 * #42781 * #42845 * #42782 * #42844 * #42841 * #42843 * __->__#42842 --- --- --- ### Revert "[mojo-stdlib] Remove even more `@always_inline` (#42773)" This is breaking the GPU build This reverts commit f0a92776bb1d3047b092a439ad0bc3d08e6627cc. MODULAR_ORIG_COMMIT_REV_ID: 9e0331fd9289b4c1c1652943e4a9acb76d2951a0 --- .../benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/bit/bit.mojo | 2 +- stdlib/src/builtin/error.mojo | 8 ++++++-- stdlib/src/builtin/file.mojo | 5 +++++ stdlib/src/builtin/format_int.mojo | 10 ++++++++++ stdlib/src/builtin/string.mojo | 1 + stdlib/src/collections/counter.mojo | 2 +- stdlib/src/collections/dict.mojo | 18 +++++++++--------- stdlib/src/collections/inline_list.mojo | 3 +++ stdlib/src/os/os.mojo | 4 ++-- stdlib/src/pathlib/path.mojo | 1 + stdlib/src/tempfile/tempfile.mojo | 2 ++ stdlib/src/utils/inline_string.mojo | 1 + 13 files changed, 43 insertions(+), 16 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index b50b4c9458..fc0748b5d3 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -52,7 +52,7 @@ struct Op(Stringable): fn __eq__(self, other: Op) -> Bool: return self.op_code == other.op_code - @always_inline + @always_inline("nodebug") fn __str__(self) -> String: var op_list = List[String]( "add", "sub", "mul", "div", "fma", "ld", "st" diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 5d5e5a1591..5c9a05b633 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -357,7 +357,7 @@ fn is_power_of_two[ # reference: https://en.cppreference.com/w/cpp/numeric/bit_ceil -@always_inline +@always_inline("nodebug") fn bit_ceil(val: Int) -> Int: """Computes the smallest power of 2 that is greater than or equal to the input value. Any integral value less than or equal to 1 will be ceiled to 1. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 59b05566bf..4c582bdd8a 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -47,7 +47,7 @@ struct Error( ownership and a free is executed in the destructor. """ - @always_inline + @always_inline("nodebug") fn __init__() -> Self: """Default constructor. @@ -56,7 +56,7 @@ struct Error( """ return Error {data: UnsafePointer[UInt8](), loaded_length: 0} - @always_inline + @always_inline("nodebug") fn __init__(value: StringLiteral) -> Self: """Construct an Error object with a given string literal. @@ -71,6 +71,7 @@ struct Error( loaded_length: len(value), } + @always_inline("nodebug") fn __init__(src: String) -> Self: """Construct an Error object with a given string. @@ -90,6 +91,7 @@ struct Error( dest[length] = 0 return Error {data: dest, loaded_length: -length} + @always_inline("nodebug") fn __init__(src: StringRef) -> Self: """Construct an Error object with a given string ref. @@ -125,6 +127,7 @@ struct Error( if self.loaded_length < 0: self.data.free() + @always_inline("nodebug") fn __copyinit__(existing: Self) -> Self: """Creates a deep copy of an existing error. @@ -177,6 +180,7 @@ struct Error( """ return "Error(" + repr(self._message()) + ")" + @always_inline fn _message(self) -> String: """Converts the Error to string representation. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 7d7b1ca9f3..38e4594358 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -104,6 +104,7 @@ struct FileHandle: self.handle = handle + @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -135,6 +136,7 @@ struct FileHandle: self.handle = existing.handle existing.handle = DTypePointer[DType.invalid]() + @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads data from a file and sets the file handle seek position. If size is left as the default of -1, it will read to the end of the file. @@ -203,6 +205,7 @@ struct FileHandle: return String(buf, int(size_copy) + 1) + @always_inline fn read[ type: DType ](self, ptr: DTypePointer[type], size: Int64 = -1) raises -> Int64: @@ -419,6 +422,7 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) + @always_inline fn write(self, data: StringRef) raises: """Write the data to the file. @@ -427,6 +431,7 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) + @always_inline fn _write[ address_space: AddressSpace ](self, ptr: UnsafePointer[UInt8, address_space], len: Int) raises: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 63a344c6d1..b7b9aeb498 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -27,6 +27,7 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" # ===----------------------------------------------------------------------===# +@always_inline fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: """Return the binary string representation an integral value. @@ -51,6 +52,7 @@ fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: # Need this until we have constraints to stop the compiler from matching this # directly to bin[type: DType](num: Scalar[type]). +@always_inline("nodebug") fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of a scalar bool. @@ -64,6 +66,7 @@ fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: return bin(b.cast[DType.int8](), prefix=prefix) +@always_inline("nodebug") fn bin[T: Indexer, //](num: T, /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of an indexer type. @@ -103,6 +106,7 @@ fn hex(value: Scalar, /, *, prefix: StaticString = "0x") -> String: return _try_format_int(value, 16, prefix=prefix) +@always_inline fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given integer. @@ -124,6 +128,7 @@ fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: return hex(Scalar[DType.index](index(value)), prefix=prefix) +@always_inline fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given scalar bool. @@ -147,6 +152,7 @@ fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: # ===----------------------------------------------------------------------===# +@always_inline fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. @@ -165,6 +171,7 @@ fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: return _try_format_int(value, 8, prefix=prefix) +@always_inline fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. @@ -186,6 +193,7 @@ fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: return oct(Scalar[DType.index](index(value)), prefix=prefix) +@always_inline fn oct(value: Scalar[DType.bool], /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given scalar bool. @@ -244,6 +252,7 @@ fn _format_int[ return string^ +@always_inline fn _write_int[ type: DType, //, ]( @@ -262,6 +271,7 @@ fn _write_int[ raise err.value() +@always_inline fn _try_write_int[ type: DType, //, ]( diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index dca633d6a3..501533fdea 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2355,6 +2355,7 @@ fn _calc_initial_buffer_size_int64(n0: UInt64) -> Int: result += 4 +@always_inline fn _calc_initial_buffer_size(n0: Int) -> Int: var sign = 0 if n0 > 0 else 1 diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 87976f67f8..4e78a7f9a4 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -502,7 +502,7 @@ struct CountTuple[V: KeyElement]( """ return self._count == other._count - @always_inline + @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Variant[V, Int]: """Get an element in the tuple. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index eb9d52a890..46aefeda0a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -1040,7 +1040,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - @always_inline + @always_inline("nodebug") fn __getitem__(self, key: Self.key_type) raises -> V: """Retrieve a value out of the keyword dictionary. @@ -1055,7 +1055,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict[key] - @always_inline + @always_inline("nodebug") fn __setitem__(inout self, key: Self.key_type, value: V): """Set a value in the keyword dictionary by key. @@ -1069,7 +1069,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Trait implementations # ===-------------------------------------------------------------------===# - @always_inline + @always_inline("nodebug") fn __contains__(self, key: Self.key_type) -> Bool: """Check if a given key is in the keyword dictionary or not. @@ -1082,7 +1082,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return key in self._dict - @always_inline + @always_inline("nodebug") fn __len__(self) -> Int: """The number of elements currently stored in the keyword dictionary. @@ -1095,7 +1095,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Methods # ===-------------------------------------------------------------------===# - @always_inline + @always_inline("nodebug") fn find(self, key: Self.key_type) -> Optional[V]: """Find a value in the keyword dictionary by key. @@ -1108,7 +1108,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict.find(key) - @always_inline + @always_inline("nodebug") fn pop(inout self, key: self.key_type, owned default: V) -> V: """Remove a value from the dictionary by key. @@ -1123,7 +1123,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict.pop(key, default^) - @always_inline + @always_inline("nodebug") fn pop(inout self, key: self.key_type) raises -> V: """Remove a value from the dictionary by key. @@ -1196,10 +1196,10 @@ struct OwnedKwargsDict[V: CollectionElement]( # return self[]._dict.items() return _DictEntryIter(0, 0, self._dict) - @always_inline + @always_inline("nodebug") fn _insert(inout self, owned key: Self.key_type, owned value: V): self._dict._insert(key^, value^) - @always_inline + @always_inline("nodebug") fn _insert(inout self, key: StringLiteral, owned value: V): self._insert(String(key), value^) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index c5267afb1c..c8bd0c02b5 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -180,6 +180,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """ return _InlineListIter(0, self) + @always_inline fn __contains__[ C: ComparableCollectionElement ](self: Self, value: C) -> Bool: @@ -212,6 +213,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # Methods # ===-------------------------------------------------------------------===# + @always_inline fn count[C: ComparableCollectionElement](self: Self, value: C) -> Int: """Counts the number of occurrences of a value in the list. @@ -239,6 +241,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): count += 1 return count + @always_inline fn append(inout self, owned value: ElementType): """Appends a value to the list. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 6ff2f17dbc..5b0286b4a6 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -224,7 +224,7 @@ fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: # ===----------------------------------------------------------------------=== # -@no_inline +@always_inline("nodebug") fn abort[result: AnyType = NoneType]() -> result: """Calls a target dependent trap instruction if available. @@ -242,7 +242,7 @@ fn abort[result: AnyType = NoneType]() -> result: pass -@no_inline +@always_inline("nodebug") fn abort[ result: AnyType = NoneType, *, formattable: Formattable ](message: formattable) -> result: diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 244edfb9df..b6a16edafc 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -318,6 +318,7 @@ struct Path( with open(self, "w") as f: f.write(str(value)) + @always_inline fn suffix(self) -> String: """The path's extension, if any. This includes the leading period. For example: '.txt'. diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 9864ba1b5f..1ce7915ae5 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -316,6 +316,7 @@ struct NamedTemporaryFile: except: raise Error("Failed to create temporary file") + @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -339,6 +340,7 @@ struct NamedTemporaryFile: self._delete = existing._delete self.name = existing.name^ + @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads the data from the file. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index d5481cd223..5257772fcc 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -340,6 +340,7 @@ struct _FixedString[CAP: Int]( """ self = other + @always_inline fn __init__(inout self, literal: StringLiteral) raises: """Constructs a FixedString value given a string literal. From 13ad33e180f6d0a05ee4b169949b8d8583dc1e6c Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 3 Jul 2024 18:36:36 -0700 Subject: [PATCH 1117/2019] Remove passing the closure to improve clarity, there is a small amount of duplication but makes it more clear what is going on at first glance. Also adds `Path.__bool__` to avoid having to convert it to a String first. MODULAR_ORIG_COMMIT_REV_ID: e023b785d8b433903d268eb3d227eb9aca14a47d --- stdlib/src/pathlib/path.mojo | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index b6a16edafc..a08e9ab59f 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -62,6 +62,7 @@ fn _dir_of_current_file() raises -> Path: struct Path( Stringable, + Boolable, Formattable, CollectionElement, CollectionElementNew, @@ -154,6 +155,15 @@ struct Path( """ return String.format_sequence(self) + @always_inline + fn __bool__(self) -> Bool: + """Checks if the path is not empty. + + Returns: + True if the path length is greater than zero, and False otherwise. + """ + return len(self.path) > 0 + fn format_to(self, inout writer: Formatter): """ Formats this path to the provided formatter. From 79e846b3e962c3feb0c9ad4e8554d89eb84f4d71 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 3 Jul 2024 21:00:18 -0700 Subject: [PATCH 1118/2019] If `HOME` is not set e.g. in CI `Path.home()` should return `~` and not an empty string. MODULAR_ORIG_COMMIT_REV_ID: b283ffa6ffe207d530d7d7808366c87f45d182bf --- stdlib/src/os/path/__init__.mojo | 1 - stdlib/src/pathlib/path.mojo | 2 +- stdlib/test/os/path/test_expanduser.mojo | 5 +++++ stdlib/test/pathlib/test_pathlib.mojo | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index a54b57e219..ac7d4aa8ec 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -22,5 +22,4 @@ from .path import ( join, split, lexists, - _get_home_path, ) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index a08e9ab59f..6fc841f8d1 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -278,7 +278,7 @@ struct Path( Returns: Path to user home directory. """ - return os.path._get_home_path() + return os.path.expanduser("~") fn is_dir(self) -> Bool: """Returns True if the path is a directory and False otherwise. diff --git a/stdlib/test/os/path/test_expanduser.mojo b/stdlib/test/os/path/test_expanduser.mojo index b04871215b..24cc965732 100644 --- a/stdlib/test/os/path/test_expanduser.mojo +++ b/stdlib/test/os/path/test_expanduser.mojo @@ -72,4 +72,9 @@ fn main() raises: # Path with multiple tildes assert_equal(join(user_path, "~folder"), expanduser("~/~folder")) + # Test that empty HOME returns `~` + set_home("") + assert_equal(expanduser("~/test/path"), "~/test/path") + assert_equal(expanduser("~"), "~") + set_home(original_home) diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 169ebc391f..e640a26b2d 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -124,6 +124,10 @@ def test_expand_user(): # Original path should remain unmodified assert_equal(path, os.path.join("~", "test")) + # Test that empty HOME returns `~` + set_home(Path("")) + assert_equal(Path.home(), Path("~")) + # Make sure this process doesn't break other tests by changing the home dir. set_home(original_home) From aa84f6f95165f4bd31a2716223eb637297e7b461 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 4 Jul 2024 07:55:05 -0700 Subject: [PATCH 1119/2019] [mojo-stdlib] Remove even more `@always_inline` MODULAR_ORIG_COMMIT_REV_ID: 339eb128d861a9c4d59d15c6eb00a32fabe58f99 --- .../benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/bit/bit.mojo | 2 +- stdlib/src/builtin/error.mojo | 8 ++------ stdlib/src/builtin/file.mojo | 5 ----- stdlib/src/builtin/format_int.mojo | 10 ---------- stdlib/src/builtin/string.mojo | 1 - stdlib/src/collections/counter.mojo | 2 +- stdlib/src/collections/dict.mojo | 18 +++++++++--------- stdlib/src/collections/inline_list.mojo | 3 --- stdlib/src/os/os.mojo | 4 ++-- stdlib/src/pathlib/path.mojo | 1 - stdlib/src/tempfile/tempfile.mojo | 2 -- stdlib/src/utils/inline_string.mojo | 1 - 13 files changed, 16 insertions(+), 43 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index fc0748b5d3..b50b4c9458 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -52,7 +52,7 @@ struct Op(Stringable): fn __eq__(self, other: Op) -> Bool: return self.op_code == other.op_code - @always_inline("nodebug") + @always_inline fn __str__(self) -> String: var op_list = List[String]( "add", "sub", "mul", "div", "fma", "ld", "st" diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 5c9a05b633..5d5e5a1591 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -357,7 +357,7 @@ fn is_power_of_two[ # reference: https://en.cppreference.com/w/cpp/numeric/bit_ceil -@always_inline("nodebug") +@always_inline fn bit_ceil(val: Int) -> Int: """Computes the smallest power of 2 that is greater than or equal to the input value. Any integral value less than or equal to 1 will be ceiled to 1. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 4c582bdd8a..59b05566bf 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -47,7 +47,7 @@ struct Error( ownership and a free is executed in the destructor. """ - @always_inline("nodebug") + @always_inline fn __init__() -> Self: """Default constructor. @@ -56,7 +56,7 @@ struct Error( """ return Error {data: UnsafePointer[UInt8](), loaded_length: 0} - @always_inline("nodebug") + @always_inline fn __init__(value: StringLiteral) -> Self: """Construct an Error object with a given string literal. @@ -71,7 +71,6 @@ struct Error( loaded_length: len(value), } - @always_inline("nodebug") fn __init__(src: String) -> Self: """Construct an Error object with a given string. @@ -91,7 +90,6 @@ struct Error( dest[length] = 0 return Error {data: dest, loaded_length: -length} - @always_inline("nodebug") fn __init__(src: StringRef) -> Self: """Construct an Error object with a given string ref. @@ -127,7 +125,6 @@ struct Error( if self.loaded_length < 0: self.data.free() - @always_inline("nodebug") fn __copyinit__(existing: Self) -> Self: """Creates a deep copy of an existing error. @@ -180,7 +177,6 @@ struct Error( """ return "Error(" + repr(self._message()) + ")" - @always_inline fn _message(self) -> String: """Converts the Error to string representation. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 38e4594358..7d7b1ca9f3 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -104,7 +104,6 @@ struct FileHandle: self.handle = handle - @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -136,7 +135,6 @@ struct FileHandle: self.handle = existing.handle existing.handle = DTypePointer[DType.invalid]() - @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads data from a file and sets the file handle seek position. If size is left as the default of -1, it will read to the end of the file. @@ -205,7 +203,6 @@ struct FileHandle: return String(buf, int(size_copy) + 1) - @always_inline fn read[ type: DType ](self, ptr: DTypePointer[type], size: Int64 = -1) raises -> Int64: @@ -422,7 +419,6 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) - @always_inline fn write(self, data: StringRef) raises: """Write the data to the file. @@ -431,7 +427,6 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), len(data)) - @always_inline fn _write[ address_space: AddressSpace ](self, ptr: UnsafePointer[UInt8, address_space], len: Int) raises: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index b7b9aeb498..63a344c6d1 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -27,7 +27,6 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" # ===----------------------------------------------------------------------===# -@always_inline fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: """Return the binary string representation an integral value. @@ -52,7 +51,6 @@ fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: # Need this until we have constraints to stop the compiler from matching this # directly to bin[type: DType](num: Scalar[type]). -@always_inline("nodebug") fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of a scalar bool. @@ -66,7 +64,6 @@ fn bin(b: Scalar[DType.bool], /, *, prefix: StaticString = "0b") -> String: return bin(b.cast[DType.int8](), prefix=prefix) -@always_inline("nodebug") fn bin[T: Indexer, //](num: T, /, *, prefix: StaticString = "0b") -> String: """Returns the binary representation of an indexer type. @@ -106,7 +103,6 @@ fn hex(value: Scalar, /, *, prefix: StaticString = "0x") -> String: return _try_format_int(value, 16, prefix=prefix) -@always_inline fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given integer. @@ -128,7 +124,6 @@ fn hex[T: Indexer, //](value: T, /, *, prefix: StaticString = "0x") -> String: return hex(Scalar[DType.index](index(value)), prefix=prefix) -@always_inline fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: """Returns the hex string representation of the given scalar bool. @@ -152,7 +147,6 @@ fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: # ===----------------------------------------------------------------------===# -@always_inline fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. @@ -171,7 +165,6 @@ fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: return _try_format_int(value, 8, prefix=prefix) -@always_inline fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given integer. @@ -193,7 +186,6 @@ fn oct[T: Indexer, //](value: T, /, *, prefix: StaticString = "0o") -> String: return oct(Scalar[DType.index](index(value)), prefix=prefix) -@always_inline fn oct(value: Scalar[DType.bool], /, *, prefix: StaticString = "0o") -> String: """Returns the octal string representation of the given scalar bool. @@ -252,7 +244,6 @@ fn _format_int[ return string^ -@always_inline fn _write_int[ type: DType, //, ]( @@ -271,7 +262,6 @@ fn _write_int[ raise err.value() -@always_inline fn _try_write_int[ type: DType, //, ]( diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 501533fdea..dca633d6a3 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2355,7 +2355,6 @@ fn _calc_initial_buffer_size_int64(n0: UInt64) -> Int: result += 4 -@always_inline fn _calc_initial_buffer_size(n0: Int) -> Int: var sign = 0 if n0 > 0 else 1 diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 4e78a7f9a4..87976f67f8 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -502,7 +502,7 @@ struct CountTuple[V: KeyElement]( """ return self._count == other._count - @always_inline("nodebug") + @always_inline fn __getitem__(self, idx: Int) -> Variant[V, Int]: """Get an element in the tuple. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 46aefeda0a..eb9d52a890 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -1040,7 +1040,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __getitem__(self, key: Self.key_type) raises -> V: """Retrieve a value out of the keyword dictionary. @@ -1055,7 +1055,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict[key] - @always_inline("nodebug") + @always_inline fn __setitem__(inout self, key: Self.key_type, value: V): """Set a value in the keyword dictionary by key. @@ -1069,7 +1069,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Trait implementations # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __contains__(self, key: Self.key_type) -> Bool: """Check if a given key is in the keyword dictionary or not. @@ -1082,7 +1082,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return key in self._dict - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: """The number of elements currently stored in the keyword dictionary. @@ -1095,7 +1095,7 @@ struct OwnedKwargsDict[V: CollectionElement]( # Methods # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn find(self, key: Self.key_type) -> Optional[V]: """Find a value in the keyword dictionary by key. @@ -1108,7 +1108,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict.find(key) - @always_inline("nodebug") + @always_inline fn pop(inout self, key: self.key_type, owned default: V) -> V: """Remove a value from the dictionary by key. @@ -1123,7 +1123,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ return self._dict.pop(key, default^) - @always_inline("nodebug") + @always_inline fn pop(inout self, key: self.key_type) raises -> V: """Remove a value from the dictionary by key. @@ -1196,10 +1196,10 @@ struct OwnedKwargsDict[V: CollectionElement]( # return self[]._dict.items() return _DictEntryIter(0, 0, self._dict) - @always_inline("nodebug") + @always_inline fn _insert(inout self, owned key: Self.key_type, owned value: V): self._dict._insert(key^, value^) - @always_inline("nodebug") + @always_inline fn _insert(inout self, key: StringLiteral, owned value: V): self._insert(String(key), value^) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index c8bd0c02b5..c5267afb1c 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -180,7 +180,6 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """ return _InlineListIter(0, self) - @always_inline fn __contains__[ C: ComparableCollectionElement ](self: Self, value: C) -> Bool: @@ -213,7 +212,6 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # Methods # ===-------------------------------------------------------------------===# - @always_inline fn count[C: ComparableCollectionElement](self: Self, value: C) -> Int: """Counts the number of occurrences of a value in the list. @@ -241,7 +239,6 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): count += 1 return count - @always_inline fn append(inout self, owned value: ElementType): """Appends a value to the list. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 5b0286b4a6..6ff2f17dbc 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -224,7 +224,7 @@ fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: # ===----------------------------------------------------------------------=== # -@always_inline("nodebug") +@no_inline fn abort[result: AnyType = NoneType]() -> result: """Calls a target dependent trap instruction if available. @@ -242,7 +242,7 @@ fn abort[result: AnyType = NoneType]() -> result: pass -@always_inline("nodebug") +@no_inline fn abort[ result: AnyType = NoneType, *, formattable: Formattable ](message: formattable) -> result: diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 6fc841f8d1..9d67ddf916 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -328,7 +328,6 @@ struct Path( with open(self, "w") as f: f.write(str(value)) - @always_inline fn suffix(self) -> String: """The path's extension, if any. This includes the leading period. For example: '.txt'. diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 1ce7915ae5..9864ba1b5f 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -316,7 +316,6 @@ struct NamedTemporaryFile: except: raise Error("Failed to create temporary file") - @always_inline fn __del__(owned self): """Closes the file handle.""" try: @@ -340,7 +339,6 @@ struct NamedTemporaryFile: self._delete = existing._delete self.name = existing.name^ - @always_inline fn read(self, size: Int64 = -1) raises -> String: """Reads the data from the file. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 5257772fcc..d5481cd223 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -340,7 +340,6 @@ struct _FixedString[CAP: Int]( """ self = other - @always_inline fn __init__(inout self, literal: StringLiteral) raises: """Constructs a FixedString value given a string literal. From c361c7dbff3003d25de550bbc6b0e8b5d3af0a9d Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 4 Jul 2024 08:00:13 -0700 Subject: [PATCH 1120/2019] Fix `makedirs` test failing in CI with no `HOME` path set MODULAR_ORIG_COMMIT_REV_ID: 45d46cc72fbd5d66ca76c4432fcefafd44001f81 --- stdlib/test/os/test_mkdir_and_rmdir.mojo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/test/os/test_mkdir_and_rmdir.mojo b/stdlib/test/os/test_mkdir_and_rmdir.mojo index f9f58086e7..f2fd2171ee 100644 --- a/stdlib/test/os/test_mkdir_and_rmdir.mojo +++ b/stdlib/test/os/test_mkdir_and_rmdir.mojo @@ -140,7 +140,8 @@ fn test_rmdir_not_empty() raises: def main(): test_mkdir_and_rmdir("my_dir") test_mkdir_and_rmdir(Path("my_dir")) - test_mkdir_and_rmdir(Path.home() / "my_dir") + if os.env.getenv("HOME") or os.env.getenv("USERPROFILE"): + test_mkdir_and_rmdir(Path("~/my_dir").expanduser()) test_makedirs_and_removedirs(os.path.join("dir1", "dir2", "dir3")) test_makedirs_and_removedirs(Path("dir1") / "dir2" / "dir3") From 8ab60971910b31ada1cdf7671eca8d92d3419f02 Mon Sep 17 00:00:00 2001 From: Mikhail Zolotukhin Date: Thu, 4 Jul 2024 10:36:39 -0700 Subject: [PATCH 1121/2019] Revert "[stdlib] Use repeat keyword for the splat constructor for StaticIntTuple." The PR caused a model failure. MODULAR_ORIG_COMMIT_REV_ID: b5f119fcfc09a33ef0283163e699c659e79f6d47 --- stdlib/src/utils/index.mojo | 8 ++++---- stdlib/test/utils/test_tuple.mojo | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index ce8b5dd22f..adfc6ab277 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -185,7 +185,7 @@ struct StaticIntTuple[size: Int]( @always_inline fn __init__(inout self): """Constructs a static int tuple of the given size.""" - self = Self(repeat=0) + self = 0 @always_inline fn __init__(inout self, value: __mlir_type.index): @@ -302,16 +302,16 @@ struct StaticIntTuple[size: Int]( self = tup @always_inline - fn __init__(inout self, *, repeat: Int): + fn __init__(inout self, elem: Int): """Constructs a static int tuple given a set of arguments. Args: - repeat: The elem to splat into the tuple. + elem: The elem to splat into the tuple. """ self.data = __mlir_op.`pop.array.repeat`[ _type = __mlir_type[`!pop.array<`, size.value, `, `, Int, `>`] - ](repeat) + ](elem) @always_inline fn __init__(inout self, values: VariadicList[Int]): diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 042731fefd..b35a6ae4f2 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -38,7 +38,7 @@ def test_static_tuple(): def test_static_int_tuple(): assert_equal(str(StaticIntTuple[1](1)), "(1,)") - assert_equal(str(StaticIntTuple[3](repeat=2)), "(2, 2, 2)") + assert_equal(str(StaticIntTuple[3](2)), "(2, 2, 2)") assert_equal( str(StaticIntTuple[3](1, 2, 3) * StaticIntTuple[3](4, 5, 6)), From b7c3343c463b70b28609464eba12500e3e772a59 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 4 Jul 2024 16:41:48 -0700 Subject: [PATCH 1122/2019] [Stdlib] Introduce time.perf_counter methods and deprecate time.now This deprecates the time.now method and replaces all occurrences with time.perf_counter. This more matches with the Python naming of these functions. MODULAR_ORIG_COMMIT_REV_ID: 482649dbda2b3a8f7e04b6240a3359f51f552805 --- docs/changelog.md | 3 ++ stdlib/src/random/random.mojo | 4 +-- stdlib/src/time/__init__.mojo | 2 +- stdlib/src/time/time.mojo | 55 +++++++++++++++++++++++++++++---- stdlib/test/time/test_time.mojo | 10 +++--- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 060b58a38f..02884c3550 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -347,6 +347,9 @@ what we publish. implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced for types where this behavior is desired. +- The `time.now()` function has been deprecated. Please use `time.perf_counter` + or `time.perf_counter_ns` instead. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 13e98bc260..ee19cb8adf 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -20,7 +20,7 @@ from random import seed """ from sys import bitwidthof, external_call -from time import now +from time import perf_counter_ns from memory import DTypePointer @@ -34,7 +34,7 @@ fn _get_random_state() -> DTypePointer[DType.invalid]: fn seed(): """Seeds the random number generator using the current time.""" - seed(now()) + seed(perf_counter_ns()) fn seed(a: Int): diff --git a/stdlib/src/time/__init__.mojo b/stdlib/src/time/__init__.mojo index 169c2f82e2..885fa654c8 100644 --- a/stdlib/src/time/__init__.mojo +++ b/stdlib/src/time/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the time package.""" -from .time import now, sleep, time_function +from .time import now, perf_counter, perf_counter_ns, sleep, time_function diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 4bd0c6a1f6..83caf4050b 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -21,6 +21,7 @@ from time import now from sys import external_call, os_is_linux, os_is_windows, triple_is_nvidia_cuda from sys._assembly import inlined_assembly +from math import floor from memory import UnsafePointer @@ -154,6 +155,44 @@ fn _thread_cputime_nanoseconds() -> Int: return _gettime_as_nsec_unix(_CLOCK_THREAD_CPUTIME_ID) +# ===----------------------------------------------------------------------===# +# perf_counter +# ===----------------------------------------------------------------------===# + + +@always_inline +fn perf_counter() -> Float64: + """Return the value (in fractional seconds) of a performance counter, i.e. + a clock with the highest available resolution to measure a short duration. + It does include time elapsed during sleep and is system-wide. The reference + point of the returned value is undefined, so that only the difference + between the results of two calls is valid. + + Returns: + The current time in ns. + """ + return Float64(_monotonic_nanoseconds()) / _NSEC_PER_SEC + + +# ===----------------------------------------------------------------------===# +# perf_counter_ns +# ===----------------------------------------------------------------------===# + + +@always_inline +fn perf_counter_ns() -> Int: + """Return the value (in nanoseconds) of a performance counter, i.e. + a clock with the highest available resolution to measure a short duration. + It does include time elapsed during sleep and is system-wide. The reference + point of the returned value is undefined, so that only the difference + between the results of two calls is valid. + + Returns: + The current time in ns. + """ + return _monotonic_nanoseconds() + + # ===----------------------------------------------------------------------===# # now # ===----------------------------------------------------------------------===# @@ -161,7 +200,9 @@ fn _thread_cputime_nanoseconds() -> Int: @always_inline fn now() -> Int: - """Returns the current monotonic time time in nanoseconds. This function + """Deprecated: Please use time.perf_counter_ns instead. + + Returns the current monotonic time time in nanoseconds. This function queries the current platform's monotonic clock, making it useful for measuring time differences, but the significance of the returned value varies depending on the underlying implementation. @@ -169,7 +210,7 @@ fn now() -> Int: Returns: The current time in ns. """ - return _monotonic_nanoseconds() + return perf_counter_ns() # ===----------------------------------------------------------------------===# @@ -224,9 +265,9 @@ fn time_function[func: fn () capturing -> None]() -> Int: if os_is_windows(): return _time_function_windows[func]() - var tic = now() + var tic = perf_counter_ns() func() - var toc = now() + var toc = perf_counter_ns() return toc - tic @@ -251,15 +292,17 @@ fn sleep(sec: Float64): return alias NANOSECONDS_IN_SECOND = 1_000_000_000 - var total_secs = sec.__floor__() + var total_secs = floor(sec) var tv_spec = _CTimeSpec( - int(total_secs.cast[DType.index]()), + int(total_secs), int((sec - total_secs) * NANOSECONDS_IN_SECOND), ) var req = UnsafePointer[_CTimeSpec].address_of(tv_spec) var rem = UnsafePointer[_CTimeSpec]() _ = external_call["nanosleep", Int32](req, rem) _ = tv_spec + _ = req + _ = rem fn sleep(sec: Int): diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 4945c20665..d30ba26558 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from sys import os_is_windows -from time import now, sleep, time_function +from time import now, perf_counter, perf_counter_ns, sleep, time_function from testing import assert_true @@ -51,6 +51,8 @@ fn time_capturing_function(iters: Int) -> Int: fn test_time() raises: alias ns_per_sec = 1_000_000_000 + assert_true(perf_counter() > 0) + assert_true(perf_counter_ns() > 0) assert_true(now() > 0) var t1 = time_function[time_me]() @@ -65,10 +67,10 @@ fn test_time() raises: assert_true(t3 > 1 * ns_per_sec) assert_true(t3 < 10 * ns_per_sec) - # test now() directly since time_function doesn't use now on windows - var t4 = now() + # test perf_counter_ns() directly since time_function doesn't use now on windows + var t4 = perf_counter_ns() time_me() - var t5 = now() + var t5 = perf_counter_ns() assert_true((t5 - t4) > 1 * ns_per_sec) assert_true((t5 - t4) < 10 * ns_per_sec) From dbf2ef5333cfc6db7aec571f347329fd8b889044 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 5 Jul 2024 02:21:41 -0500 Subject: [PATCH 1123/2019] [stdlib] Use `pop.offset` in `UnsafePointer.__add__` Instead of doing pointer arithmetic directly in `UnsafePointer.__add__`, use `pop.offset` which will be simpler for the compiler to understand and optimize. While here, rewrite `UnsafePointer.__iadd__` in terms of `UnsafePointer.__add__` instead of doing the similar pointer arithmetic. MODULAR_ORIG_COMMIT_REV_ID: 5dd5070f7b5a326e3a44e1069e1501564e817f97 --- stdlib/src/memory/unsafe_pointer.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 4518d7e968..e71a84b75e 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -246,7 +246,7 @@ struct UnsafePointer[ Returns: An offset pointer. """ - return Self(address=int(self) + offset * sizeof[T]()) + return __mlir_op.`pop.offset`(self.address, int(offset).value) @always_inline fn __sub__(self, offset: Int) -> Self: @@ -267,7 +267,7 @@ struct UnsafePointer[ Args: offset: The offset index. """ - self = Self(address=int(self) + offset * sizeof[T]()) + self = self + offset @always_inline fn __isub__(inout self, offset: Int): From 01f3d2d2591c03f1bb706e048ca8fe98bdc2859b Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 00:55:54 -0700 Subject: [PATCH 1124/2019] [mojo-stdlib] Convert some LegacyPointer -> UnsafePointer (NFC) Convert some LegacyPointer users to UnsafePointer. At the same time, add a convenience constructor to convert from UnsafePointer to LegacyPointer and add `fn offset` to UnsafePointer just for API compat. MODULAR_ORIG_COMMIT_REV_ID: 1fa27d8f847a29ab47a96995c52e38fbdc081e8c --- stdlib/src/memory/memory.mojo | 32 +++++++++++++-------------- stdlib/src/memory/unsafe.mojo | 16 ++++++++++++-- stdlib/src/memory/unsafe_pointer.mojo | 15 +++++++++++++ 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 0476da6f04..2760b53fe6 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -391,7 +391,7 @@ fn memset[ address_space: The address space of the pointer. Args: - ptr: Pointer to the beginning of the memory block to fill. + ptr: UnsafePointer to the beginning of the memory block to fill. value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ @@ -400,7 +400,7 @@ fn memset[ @always_inline fn memset[ - type: AnyTrivialRegType, address_space: AddressSpace + type: AnyType, address_space: AddressSpace ](ptr: UnsafePointer[type, address_space], value: UInt8, count: Int): """Fills memory with the given value. @@ -409,7 +409,7 @@ fn memset[ address_space: The address space of the pointer. Args: - ptr: Pointer to the beginning of the memory block to fill. + ptr: UnsafePointer to the beginning of the memory block to fill. value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ @@ -427,7 +427,7 @@ fn memset[ address_space: The address space of the pointer. Args: - ptr: Pointer to the beginning of the memory block to fill. + ptr: UnsafePointer to the beginning of the memory block to fill. value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ @@ -450,7 +450,7 @@ fn memset_zero[ address_space: The address space of the pointer. Args: - ptr: Pointer to the beginning of the memory block to fill. + ptr: UnsafePointer to the beginning of the memory block to fill. count: Number of elements to set (in elements, not bytes). """ memset(ptr, 0, count) @@ -458,7 +458,7 @@ fn memset_zero[ @always_inline fn memset_zero[ - type: AnyTrivialRegType, address_space: AddressSpace + type: AnyType, address_space: AddressSpace ](ptr: UnsafePointer[type, address_space], count: Int): """Fills memory with zeros. @@ -467,7 +467,7 @@ fn memset_zero[ address_space: The address space of the pointer. Args: - ptr: Pointer to the beginning of the memory block to fill. + ptr: UnsafePointer to the beginning of the memory block to fill. count: Number of elements to fill (in elements, not bytes). """ memset(ptr, 0, count) @@ -484,7 +484,7 @@ fn memset_zero[ address_space: The address space of the pointer. Args: - ptr: Pointer to the beginning of the memory block to fill. + ptr: UnsafePointer to the beginning of the memory block to fill. count: Number of elements to fill (in elements, not bytes). """ memset(ptr, 0, count) @@ -524,11 +524,11 @@ fn stack_allocation[ @always_inline fn stack_allocation[ count: Int, - type: AnyTrivialRegType, + type: AnyType, /, alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, address_space: AddressSpace = AddressSpace.GENERIC, -]() -> Pointer[type, address_space]: +]() -> UnsafePointer[type, address_space]: """Allocates data buffer space on the stack given a data type and number of elements. @@ -546,14 +546,14 @@ fn stack_allocation[ if triple_is_nvidia_cuda() and address_space == _GPUAddressSpace.SHARED: return __mlir_op.`pop.global_alloc`[ count = count.value, - _type = Pointer[type, address_space]._mlir_type, + _type = UnsafePointer[type, address_space]._mlir_type, alignment = alignment.value, address_space = address_space._value.value, ]() else: return __mlir_op.`pop.stack_allocation`[ count = count.value, - _type = Pointer[type, address_space]._mlir_type, + _type = UnsafePointer[type, address_space]._mlir_type, alignment = alignment.value, address_space = address_space._value.value, ]() @@ -566,23 +566,23 @@ fn stack_allocation[ @always_inline fn _malloc[ - type: AnyTrivialRegType, + type: AnyType, /, *, address_space: AddressSpace = AddressSpace.GENERIC, -](size: Int, /, *, alignment: Int = -1) -> Pointer[type, address_space]: +](size: Int, /, *, alignment: Int = -1) -> UnsafePointer[type, address_space]: @parameter if triple_is_nvidia_cuda(): constrained[ address_space is AddressSpace.GENERIC, "address space must be generic", ]() - return external_call["malloc", Pointer[NoneType, address_space]]( + return external_call["malloc", UnsafePointer[NoneType, address_space]]( size ).bitcast[type]() else: return __mlir_op.`pop.aligned_alloc`[ - _type = Pointer[type, address_space]._mlir_type + _type = UnsafePointer[type, address_space]._mlir_type ](alignment.value, size.value) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index a4387af725..c6beb38e40 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -210,6 +210,18 @@ struct LegacyPointer[ """ return other + @always_inline + fn __init__(other: UnsafePointer[type, address_space, exclusive]) -> Self: + """Construct a legacy pointer (deprecated) from an UnsafePointer. + + Args: + other: The UnsafePointer. + + Returns: + Constructed LegacyPointer object. + """ + return other.address + @always_inline("nodebug") fn __init__(address: Self._mlir_type) -> Self: """Constructs a LegacyPointer from the address. @@ -907,13 +919,13 @@ struct DTypePointer[ return self.address.bitcast[SIMD[new_type, 1], address_space]() @always_inline("nodebug") - fn _as_scalar_pointer(self) -> Pointer[Scalar[type], address_space]: + fn _as_scalar_pointer(self) -> UnsafePointer[Scalar[type], address_space]: """Converts the `DTypePointer` to a scalar pointer of the same dtype. Returns: A `Pointer` to a scalar of the same dtype. """ - return self.address + return self.address.address alias _default_alignment = alignof[ Scalar[type] diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index e71a84b75e..b2ea562bf6 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -222,6 +222,21 @@ struct UnsafePointer[ ) ) + @always_inline + fn offset[T: Intable](self, idx: T) -> Self: + """Returns a new pointer shifted by the specified offset. + + Parameters: + T: The Intable type of the offset. + + Args: + idx: The offset of the new pointer. + + Returns: + The new constructed DTypePointer. + """ + return __mlir_op.`pop.offset`(self.address, int(idx).value) + @always_inline fn __getitem__( self, offset: Int From 138dfabd0b8fb5b1c31c7b4e514c8d7acf500a16 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 08:13:47 -0700 Subject: [PATCH 1125/2019] [mojo-stdlib] Mostly remove `*Pointer(address=` constructors (NFC) This PR deletes the `address=` constructors for UnsafePointer, LegacyPointer, and removes all but one use of it for DTypePointer. It also removes the constructor from `Scalar[DType.address]` to LegacyPointer, but users of the constructor for DTypePointer remain in the masked gather and scatter intrinsics. This is part of removing the ability to convert integers to pointers, in an attempt to move away from C pointer semantics. MODULAR_ORIG_COMMIT_REV_ID: 68841ea1e4ff262abe1f507d087fb29b55deb577 --- examples/notebooks/RayTracing.ipynb | 4 +-- stdlib/src/math/math.mojo | 4 +-- stdlib/src/memory/unsafe.mojo | 45 +++++++++++---------------- stdlib/src/memory/unsafe_pointer.mojo | 35 ++++----------------- 4 files changed, 27 insertions(+), 61 deletions(-) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 852ca89d58..10aa8ba1fe 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -253,7 +253,7 @@ " var np_image = np.zeros((self.height, self.width, 3), np.float32)\n", "\n", " # We use raw pointers to efficiently copy the pixels to the numpy array\n", - " var out_pointer = UnsafePointer[Float32](\n", + " var out_pointer = DTypePointer[DType.float32](\n", " address=int(np_image.__array_interface__[\"data\"][0])\n", " )\n", " var in_pointer = self.pixels.bitcast[Float32]()\n", @@ -276,7 +276,7 @@ " var cols = int(np_image.shape[1])\n", " var image = Image(rows, cols)\n", "\n", - " var in_pointer = UnsafePointer[Float32](\n", + " var in_pointer = DTypePointer[DType.float32](\n", " address=int(np_image.__array_interface__[\"data\"][0])\n", " )\n", " var out_pointer = image.pixels.bitcast[Float32]()\n", diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 5d00e5036a..c42b2198b6 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1017,9 +1017,7 @@ fn iota(v: List[Int], offset: Int = 0): v: The vector to fill. offset: The value to fill at index 0. """ - var buff = DTypePointer[DType.index]( - Scalar[DType.index](int(v.data)).cast[DType.address]() - ) + var buff = DTypePointer[DType.index](v.data.bitcast[Scalar[DType.index]]()) iota(buff, len(v), offset=offset) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index c6beb38e40..ee71b68daa 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -249,20 +249,6 @@ struct LegacyPointer[ ) return Self {address: address} - @always_inline("nodebug") - fn __init__(*, address: Int) -> Self: - """Constructs a Pointer from an address in an integer. - - Args: - address: The input address. - - Returns: - Constructed Pointer object. - """ - return __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( - Scalar[DType.index](address).value - ) - fn __str__(self) -> String: """Format this pointer as a hexadecimal string. @@ -688,18 +674,6 @@ struct DTypePointer[ """ self.address = other.address - @always_inline("nodebug") - fn __init__(inout self, value: Scalar[DType.address]): - """Constructs a `DTypePointer` from the value of scalar address. - - Args: - value: The input pointer index. - """ - var address = __mlir_op.`pop.index_to_pointer`[ - _type = Self._pointer_type._mlir_type - ](value.cast[DType.index]().value) - self.address = address - @always_inline fn __init__(inout self, *, address: Int): """Constructs a `DTypePointer` from an integer address. @@ -707,7 +681,9 @@ struct DTypePointer[ Args: address: The input address. """ - self.address = Self._pointer_type(address=address) + self.address = __mlir_op.`pop.index_to_pointer`[ + _type = self._pointer_type._mlir_type + ](Scalar[DType.index](address).value) # ===------------------------------------------------------------------=== # # Factory methods @@ -918,6 +894,21 @@ struct DTypePointer[ """ return self.address.bitcast[SIMD[new_type, 1], address_space]() + @always_inline("nodebug") + fn bitcast[ + type: AnyType + ](self) -> UnsafePointer[type, address_space, exclusive]: + """Bitcasts `DTypePointer` to a different scalar type. + + Parameters: + type: The target scalar type. + + Returns: + A new `UnsafePointer` object with the specified type and the same + address, as the original `DTypePointer`. + """ + return self._as_scalar_pointer().bitcast[type]() + @always_inline("nodebug") fn _as_scalar_pointer(self) -> UnsafePointer[Scalar[type], address_space]: """Converts the `DTypePointer` to a scalar pointer of the same dtype. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b2ea562bf6..3f73e7e313 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -112,22 +112,6 @@ struct UnsafePointer[ ) } - @always_inline - fn __init__(*, address: Int) -> Self: - """Create an unsafe UnsafePointer from an address in an integer. - - Args: - address: The address to construct the pointer with. - - Returns: - The pointer. - """ - return Self { - address: __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( - Scalar[DType.index](address).value - ) - } - @always_inline fn __init__(*, other: Self) -> Self: """Copy the object. @@ -163,10 +147,7 @@ struct UnsafePointer[ ](ptr: DTypePointer[dtype, address_space]) -> UnsafePointer[ Scalar[dtype], address_space ]: - # TODO: - # Is there a better way to create an UnsafePointer from a - # DTypePointer? - return UnsafePointer[Scalar[dtype], address_space](address=int(ptr)) + return ptr.address.address @staticmethod @always_inline @@ -190,12 +171,8 @@ struct UnsafePointer[ sizeof_t % alignment == 0, "size must be a multiple of alignment" ]() - return Self( - address=int( - _malloc[Int8, address_space=address_space]( - sizeof_t * count, alignment=alignment - ) - ) + return _malloc[T, address_space=address_space]( + sizeof_t * count, alignment=alignment ) # ===-------------------------------------------------------------------===# @@ -261,7 +238,7 @@ struct UnsafePointer[ Returns: An offset pointer. """ - return __mlir_op.`pop.offset`(self.address, int(offset).value) + return self.offset(offset) @always_inline fn __sub__(self, offset: Int) -> Self: @@ -282,7 +259,7 @@ struct UnsafePointer[ Args: offset: The offset index. """ - self = self + offset + self = self.offset(offset) @always_inline fn __isub__(inout self, offset: Int): @@ -459,7 +436,7 @@ struct UnsafePointer[ @always_inline fn free(self): """Free the memory referenced by the pointer.""" - Pointer[Int8, address_space=address_space](address=int(self)).free() + _free(self) @always_inline("nodebug") fn bitcast[ From 79ef2f8e0da3afe02b86b30f5c7c9883a1940d5d Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 5 Jul 2024 11:02:42 -0500 Subject: [PATCH 1126/2019] [stdlib] Remove old `FIXME` in `_SequentialRange` Now that `@unroll` got reworked a bit, go back to just using `max` directly in `__len__` for `_SequentialRange`. MODULAR_ORIG_COMMIT_REV_ID: 04397b1ebc5859bee6ae4b5090e2a8fa4d8f09cb --- stdlib/src/builtin/range.mojo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 344182f3e6..996d6dd1d7 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -143,9 +143,7 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): @always_inline("nodebug") fn __len__(self) -> Int: - # FIXME(#38392): - # return max(0, self.end - self.start) - return self.end - self.start if self.start < self.end else 0 + return max(0, self.end - self.start) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: From 96cc77b729afa7701d476412ecba407c20fa8828 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 5 Jul 2024 09:05:31 -0700 Subject: [PATCH 1127/2019] Follows Python syntax for the function `os.getuid()`. MODULAR_ORIG_COMMIT_REV_ID: 90e23234adbea8bc1718ef52ac2e6af5c6482574 --- stdlib/src/os/__init__.mojo | 1 + stdlib/src/os/os.mojo | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index 9494dc1050..fe813bb7d9 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -20,6 +20,7 @@ from .os import ( SEEK_END, SEEK_SET, abort, + getuid, listdir, mkdir, makedirs, diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 6ff2f17dbc..de66d03d19 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -186,6 +186,24 @@ struct _DirHandle: return res +# ===----------------------------------------------------------------------=== # +# getuid +# ===----------------------------------------------------------------------=== # +fn getuid() -> Int: + """Retrieve the user ID of the calling process. + + Returns: + The user ID of the calling process. + + Constraints: + This function is constrained to run on Linux or macOS operating systems only. + """ + constrained[ + not os_is_windows(), "operating system must be Linux or macOS" + ]() + return int(external_call["getuid", UInt32]()) + + # ===----------------------------------------------------------------------=== # # listdir # ===----------------------------------------------------------------------=== # From 2582ffb36940b40756487220a38a59e3fe1b31d3 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 09:31:32 -0700 Subject: [PATCH 1128/2019] [mojo-stdlib] Cordone remaining int -> pointer conversions There are two remaining use cases where we have to support int -> pointer conversions: in intrinsics.mojo to convert `Scalar[DType.address]` to a pointer to handle the `size==1` case and to convert a PythonObject to a pointer so that we can directly read data from numpy. In both these cases we have to be extremely careful around the new pointer semantics in the language: converting a PythonObject to a pointer is safe, because it does not alias any Mojo-derived pointer (the pointer is allocated from Python i.e. the C++ impl of numpy). In the former case, the pointer may derive from a Mojo pointer, and thus the load is unsafe in the presence of potentially modref'ing behaviour. MODULAR_ORIG_COMMIT_REV_ID: af8d7ea63df60d10703776164d033785a427a7fc --- docs/manual/pointers.ipynb | 5 ++--- examples/notebooks/RayTracing.ipynb | 8 ++------ stdlib/src/memory/unsafe.mojo | 26 -------------------------- stdlib/src/python/object.mojo | 12 ++++++++++++ stdlib/src/sys/intrinsics.mojo | 13 +++++++++++-- 5 files changed, 27 insertions(+), 37 deletions(-) diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index b15a3e1898..17037e30a8 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -290,8 +290,7 @@ "def share_array():\n", " np = Python.import_module(\"numpy\")\n", " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - " addr = int(arr.__array_interface__[\"data\"][0])\n", - " ptr = UnsafePointer[Int64](address=addr)\n", + " ptr = arr.__array_interface__[\"data\"][0]._unsafe_get_as_pointer[DType.int64]()\n", " for i in range(9):\n", " print(ptr[i], end=\", \")\n", "\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 10aa8ba1fe..618326e495 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -253,9 +253,7 @@ " var np_image = np.zeros((self.height, self.width, 3), np.float32)\n", "\n", " # We use raw pointers to efficiently copy the pixels to the numpy array\n", - " var out_pointer = DTypePointer[DType.float32](\n", - " address=int(np_image.__array_interface__[\"data\"][0])\n", - " )\n", + " var out_pointer = np_image.__array_interface__[\"data\"][0]._unsafe_get_as_pointer[DType.float32]()\n", " var in_pointer = self.pixels.bitcast[Float32]()\n", "\n", " for row in range(self.height):\n", @@ -276,9 +274,7 @@ " var cols = int(np_image.shape[1])\n", " var image = Image(rows, cols)\n", "\n", - " var in_pointer = DTypePointer[DType.float32](\n", - " address=int(np_image.__array_interface__[\"data\"][0])\n", - " )\n", + " var in_pointer = np_image.__array_interface__[\"data\"][0]._unsafe_get_as_pointer[DType.float32]()\n", " var out_pointer = image.pixels.bitcast[Float32]()\n", "\n", " for row in range(rows):\n", diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index ee71b68daa..f79f6fcb4f 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -234,21 +234,6 @@ struct LegacyPointer[ """ return Self {address: address} - @always_inline("nodebug") - fn __init__(value: Scalar[DType.address]) -> Self: - """Constructs a LegacyPointer from the value of scalar address. - - Args: - value: The input pointer index. - - Returns: - Constructed LegacyPointer object. - """ - var address = __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( - value.cast[DType.index]().value - ) - return Self {address: address} - fn __str__(self) -> String: """Format this pointer as a hexadecimal string. @@ -674,17 +659,6 @@ struct DTypePointer[ """ self.address = other.address - @always_inline - fn __init__(inout self, *, address: Int): - """Constructs a `DTypePointer` from an integer address. - - Args: - address: The input address. - """ - self.address = __mlir_op.`pop.index_to_pointer`[ - _type = self._pointer_type._mlir_type - ](Scalar[DType.index](address).value) - # ===------------------------------------------------------------------=== # # Factory methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 5aaa3a94da..691f5a051d 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -1170,6 +1170,18 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyLong_AsLong(self.py_object.value) + @always_inline + fn _unsafe_get_as_pointer[type: DType](self) -> DTypePointer[type]: + # Warning: converting from an integer to a pointer is unsafe! The + # compiler assumes the resulting pointer DOES NOT alias any Mojo-derived + # pointer. This is OK because the pointer originates from Python. + var tmp = int(self) + var result = UnsafePointer.address_of(tmp).bitcast[ + DTypePointer[type] + ]()[] + _ = tmp + return result + fn __str__(self) -> String: """Returns a string representation of the object. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index a0d39c5525..e2dbf37326 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -806,6 +806,15 @@ fn llvm_intrinsic[ # ===----------------------------------------------------------------------===# +# NOTE: Converting from a scalar to a pointer is unsafe! The resulting pointer +# is assumed not to alias any Mojo-derived pointer. DO NOT proliferate usage of +# this function! +fn _unsafe_aliasing_address_to_pointer[ + type: DType +](owned addr: Scalar[DType.address]) -> DTypePointer[type]: + return UnsafePointer.address_of(addr).bitcast[DTypePointer[type]]()[] + + @always_inline("nodebug") fn gather[ type: DType, size: Int @@ -859,7 +868,7 @@ fn gather[ @parameter if size == 1: return Scalar.load( - DTypePointer[type](base[0]) + _unsafe_aliasing_address_to_pointer[type](base[0]) ) if mask else passthrough[0] return llvm_intrinsic[ "llvm.masked.gather", @@ -936,7 +945,7 @@ fn scatter[ @parameter if size == 1: if mask: - var ptr = DTypePointer[type](base[0]) + var ptr = _unsafe_aliasing_address_to_pointer[type](base[0]) Scalar.store(ptr, value[0]) return llvm_intrinsic["llvm.masked.scatter", NoneType]( From 0ae0d5b2213210656a549beb9bbaa554923c9495 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 09:46:12 -0700 Subject: [PATCH 1129/2019] [mojo] Update changelog with new Mojo pointer restrictions MODULAR_ORIG_COMMIT_REV_ID: d47354c6602d507e64e652f7f55bf26df64e4839 --- docs/changelog.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 02884c3550..50106c9fd9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -229,6 +229,24 @@ what we publish. ### 🦋 Changed +- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a + C-like set of semantics around pointer aliasing and derivation. However, the C + semantics bring a lot of history and baggage that are not needed in Mojo and + which complexify compiler optimizations. The language overall provides a + stronger set of invariants around pointer aliasing with lifetimes and + exclusive mutable references to values, etc. + + It is now forbidden to convert a non-pointer-typed value derived from a + Mojo-allocated pointer, such as an integer address, to a pointer-typed value. + "Derived" means there is overlap in the bits of the non-pointer-typed value + with the original pointer value. + + It is still possible to make this conversion in certain cases where it is + absolutely necessary, such as interoperating with other languages like Python. + In this case, the compiler makes two assumptions: any pointer derived from a + non-pointer-typed value does not alias any Mojo-derived pointer and that any + external function calls have arbitrary memory effects. + - `await` on a coroutine now consumes it. This strengthens the invariant that coroutines can only be awaited once. From 5403339fc9ed8b2090eb353d0981ea9a3f64b6fd Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 5 Jul 2024 10:14:29 -0700 Subject: [PATCH 1130/2019] Adds the `pwd` module following Python syntax and behavior, and the same format when printing the `Passwd` struct directly. MODULAR_ORIG_COMMIT_REV_ID: 6c496e78640d64b8b11f472c47077793c57b4f5c --- docs/changelog.md | 20 +++++ stdlib/src/pwd/__init__.mojo | 15 ++++ stdlib/src/pwd/_linux.mojo | 56 ++++++++++++++ stdlib/src/pwd/_macos.mojo | 61 ++++++++++++++++ stdlib/src/pwd/pwd.mojo | 133 ++++++++++++++++++++++++++++++++++ stdlib/test/pwd/test_pwd.mojo | 52 +++++++++++++ 6 files changed, 337 insertions(+) create mode 100644 stdlib/src/pwd/__init__.mojo create mode 100644 stdlib/src/pwd/_linux.mojo create mode 100644 stdlib/src/pwd/_macos.mojo create mode 100644 stdlib/src/pwd/pwd.mojo create mode 100644 stdlib/test/pwd/test_pwd.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 50106c9fd9..0874d4dfa6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -368,6 +368,26 @@ what we publish. - The `time.now()` function has been deprecated. Please use `time.perf_counter` or `time.perf_counter_ns` instead. +- The `pwd` module has been added for accessing user information in + `/etc/passwd` on POSIX systems. This follows the same logic as Python: + + ```mojo + import pwd + import os + current_user = pwd.getpwuid(os.getuid()) + print(current_user) + + # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, + # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', + # pw_shell='/bin/zsh') + + root = pwd.getpwnam("root") + print(root) + + # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, + # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') + ``` + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/pwd/__init__.mojo b/stdlib/src/pwd/__init__.mojo new file mode 100644 index 0000000000..a62388303c --- /dev/null +++ b/stdlib/src/pwd/__init__.mojo @@ -0,0 +1,15 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the pwd package.""" + +from .pwd import getpwuid, getpwnam diff --git a/stdlib/src/pwd/_linux.mojo b/stdlib/src/pwd/_linux.mojo new file mode 100644 index 0000000000..51d800ec71 --- /dev/null +++ b/stdlib/src/pwd/_linux.mojo @@ -0,0 +1,56 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +from .pwd import Passwd +from memory import UnsafePointer + +alias uid_t = Int32 +alias gid_t = Int32 +alias char = UnsafePointer[C_char] + + +@register_passable("trivial") +struct _C_Passwd: + var pw_name: char + var pw_passwd: char + var pw_uid: uid_t + var pw_gid: gid_t + var pw_gecos: char + var pw_dir: char + var pw_shell: char + + +fn _build_pw_struct(passwd_ptr: UnsafePointer[_C_Passwd]) raises -> Passwd: + var c_pwuid = passwd_ptr[] + return Passwd( + pw_name=String(c_pwuid.pw_name), + pw_passwd=String(c_pwuid.pw_passwd), + pw_uid=int(c_pwuid.pw_uid), + pw_gid=int(c_pwuid.pw_gid), + pw_gecos=String(c_pwuid.pw_gecos), + pw_dir=String(c_pwuid.pw_dir), + pw_shell=String(c_pwuid.pw_shell), + ) + + +fn _getpw_linux(uid: UInt32) raises -> Passwd: + var passwd_ptr = external_call["getpwuid", UnsafePointer[_C_Passwd]](uid) + if not passwd_ptr: + raise "user ID not found in the password database: " + str(uid) + return _build_pw_struct(passwd_ptr) + + +fn _getpw_linux(name: String) raises -> Passwd: + var passwd_ptr = external_call["getpwnam", UnsafePointer[_C_Passwd]](name) + if not passwd_ptr: + raise "user name not found in the password database: " + name + return _build_pw_struct(passwd_ptr) diff --git a/stdlib/src/pwd/_macos.mojo b/stdlib/src/pwd/_macos.mojo new file mode 100644 index 0000000000..8bc2318a69 --- /dev/null +++ b/stdlib/src/pwd/_macos.mojo @@ -0,0 +1,61 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +from .pwd import Passwd +from memory import UnsafePointer + +alias uid_t = Int32 +alias gid_t = Int32 +alias time_t = Int +alias char = UnsafePointer[C_char] + + +@register_passable("trivial") +struct _C_Passwd: + var pw_name: char + var pw_passwd: char + var pw_uid: uid_t + var pw_gid: gid_t + var pw_change: time_t # Always 0 + var pw_class: char # Always empty + var pw_gecos: char + var pw_dir: char + var pw_shell: char + var pw_expire: time_t # Always 0 + + +fn _build_pw_struct(passwd_ptr: UnsafePointer[_C_Passwd]) raises -> Passwd: + var c_pwuid = passwd_ptr[] + var passwd = Passwd( + pw_name=String(c_pwuid.pw_name), + pw_passwd=String(c_pwuid.pw_passwd), + pw_uid=int(c_pwuid.pw_uid), + pw_gid=int(c_pwuid.pw_gid), + pw_gecos=String(c_pwuid.pw_gecos), + pw_dir=String(c_pwuid.pw_dir), + pw_shell=String(c_pwuid.pw_shell), + ) + return passwd + + +fn _getpw_macos(uid: UInt32) raises -> Passwd: + var passwd_ptr = external_call["getpwuid", UnsafePointer[_C_Passwd]](uid) + if not passwd_ptr: + raise "user ID not found in the password database: " + str(uid) + return _build_pw_struct(passwd_ptr) + + +fn _getpw_macos(name: String) raises -> Passwd: + var passwd_ptr = external_call["getpwnam", UnsafePointer[_C_Passwd]](name) + if not passwd_ptr: + raise "user name not found in the password database: " + name + return _build_pw_struct(passwd_ptr) diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo new file mode 100644 index 0000000000..0c12f7768d --- /dev/null +++ b/stdlib/src/pwd/pwd.mojo @@ -0,0 +1,133 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# ===----------------------------------------------------------------------=== # +# Passwd +# ===----------------------------------------------------------------------=== # +from ._linux import _getpw_linux +from ._macos import _getpw_macos +from sys.info import os_is_macos +from sys.info import os_is_linux + + +@value +struct Passwd(Stringable): + """Represents user account information retrieved from the user password + database related to a user ID.""" + + var pw_name: String + """User name.""" + var pw_passwd: String + """User password.""" + var pw_uid: Int + """User ID.""" + var pw_gid: Int + """Group ID.""" + var pw_gecos: String + """Real name or comment field.""" + var pw_dir: String + """Home directory.""" + var pw_shell: String + """Shell program.""" + + fn format_to(self, inout writer: Formatter): + """Formats this string to the provided formatter. + + Args: + writer: The formatter to write to. + """ + writer.write("pwd.struct_passwd(pw_name='", self.pw_name) + writer.write("', pw_passwd='", self.pw_passwd) + writer.write(", pw_uid=", self.pw_uid) + writer.write(", pw_gid=", self.pw_gid) + writer.write(", pw_gecos='", self.pw_gecos) + writer.write("', pw_dir='", self.pw_dir) + writer.write("', pw_shell='", self.pw_shell) + writer.write("')") + + fn __str__(self) -> String: + """Gets the Passwd struct as a string. + + Returns: + A compact string of the Passwd struct. + """ + return String.format_sequence(self) + + fn __repr__(self) -> String: + """Gets the Passwd struct as a string. + + Returns: + A compact string representation of Passwd struct. + """ + return String.format_sequence(self) + + +fn getpwuid(uid: Int) raises -> Passwd: + """Retrieve the password database entry for a given user ID. + + Args: + uid: The user ID for which to retrieve the password database entry. + + Returns: + An object containing the user's account information, including login + name, encrypted password, user ID, group ID, real name, home directory, + and shell program. + + Raises: + If the user ID does not exist or there is an error retrieving the + information. + + Constraints: + This function is constrained to run on Linux or macOS operating systems + only. + """ + constrained[ + not os_is_windows(), "operating system must be Linux or macOS" + ]() + + @parameter + if os_is_macos(): + return _getpw_macos(uid) + else: + return _getpw_linux(uid) + + +fn getpwnam(name: String) raises -> Passwd: + """ + Retrieves the user ID in the password database for the given user name. + + Args: + name: The name of the user to retrieve the password entry for. + + Returns: + An object containing the user's account information, including login + name, encrypted password, user ID, group ID, real name, home directory, + and shell program. + + Raises: + If the user name does not exist or there is an error retrieving the + information. + + Constraints: + This function is constrained to run on Linux or macOS operating systems + only. + """ + constrained[ + not os_is_windows(), "operating system must be Linux or macOS" + ]() + + @parameter + if os_is_macos(): + return _getpw_macos(name) + else: + return _getpw_linux(name) diff --git a/stdlib/test/pwd/test_pwd.mojo b/stdlib/test/pwd/test_pwd.mojo new file mode 100644 index 0000000000..78178b68a7 --- /dev/null +++ b/stdlib/test/pwd/test_pwd.mojo @@ -0,0 +1,52 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: !windows +# RUN: %mojo %s + +import pwd +import os +from testing import assert_equal, assert_true, assert_raises + + +def test_pwuid(): + # Test current process user works + passwd = pwd.getpwuid(os.getuid()) + assert_true(len(passwd.pw_dir) > 2) + assert_true(passwd.pw_uid >= 0) + assert_true(len(passwd.pw_name) > 0) + # Test root user works + passwd = pwd.getpwuid(0) + assert_true(len(passwd.pw_dir) > 2) + assert_equal(passwd.pw_uid, 0) + assert_equal(passwd.pw_name, "root") + + # Ensure incorrect ID fails + with assert_raises(): + _ = pwd.getpwuid(456789431974) + + +def test_pwnam(): + # Test root user works + passwd = pwd.getpwnam("root") + assert_true(len(passwd.pw_dir) > 2) + assert_equal(passwd.pw_uid, 0) + assert_equal(passwd.pw_name, "root") + + # Ensure incorrect name fails + with assert_raises(): + _ = pwd.getpwnam("zxcvarahoijewklvnab") + + +def main(): + test_pwuid() + test_pwnam() From 40c252a2e7f0014807b83f67cac355c65593e2c7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 5 Jul 2024 12:59:00 -0500 Subject: [PATCH 1131/2019] [stdlib] Fix UInt strided range length The `len` of a `_UIntStridedRangeIterator` was incorrect in the case where the `end > start` for a UInt strided range. Specifically, we would do `start - end` (which would be a negative result), but the return type is a `UInt`, so we would end up reporting the length as a very large `UInt` number. This would manifest when iterating UInt strided ranges since we would iterate *way* too many times. Fix this behavior by just returning `0` for the `len` in this case. MODULAR_ORIG_COMMIT_REV_ID: c467e1b960c9bbe253584db5f787620af1c036a4 --- stdlib/src/builtin/range.mojo | 2 -- stdlib/test/builtin/test_range.mojo | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 996d6dd1d7..836f4e88a5 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -188,8 +188,6 @@ struct _UIntStridedRangeIterator(UIntSized): fn __len__(self) -> UInt: if self.start < self.end: return self.end - self.start - elif self.start > self.end: - return self.start - self.end return 0 @always_inline diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 84fb806e1a..84f5124cfc 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -75,6 +75,11 @@ def test_range_getitem_uint(): # Specify the step size > 1 assert_equal(range(UInt(0), UInt(10), UInt(2))[4], 8, "range(0, 10, 2)[4]") + # start > end + var bad_strided_uint_range = range(UInt(10), UInt(5), UInt(1)) + var bad_strided_uint_range_iter = bad_strided_uint_range.__iter__() + assert_equal(UInt(0), bad_strided_uint_range_iter.__len__()) + def test_range_reversed(): # Zero starting From e38fcab747b5c33195984e127880680fa520cf32 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 5 Jul 2024 13:44:47 -0500 Subject: [PATCH 1132/2019] This is necessary for use in ****** unless we want to pollute use of `.value` to all of the callers to reuse the existing `__mlir_type.index` constructor in `Int`. To leverage `range` properly in a ******, this required adding the `__mlir_index__` hook the compiler uses. MODULAR_ORIG_COMMIT_REV_ID: f2c4c3c8f9411ebe8a42e269fc72ee03fceb0327 --- stdlib/src/builtin/int.mojo | 9 +++++++++ stdlib/src/builtin/uint.mojo | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index f3e99a070c..977c9a07b0 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -383,6 +383,15 @@ struct Int( """ self = value.__index__() + @always_inline("nodebug") + fn __init__(inout self, value: UInt): + """Construct Int from the given UInt value. + + Args: + value: The init value. + """ + self = Self(value.value) + # ===------------------------------------------------------------------=== # # Operator dunders # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index f2f4901660..b769745a77 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -80,6 +80,15 @@ struct UInt(Comparable, Formattable, Representable, Stringable): # Change when https://github.com/modularml/mojo/issues/2933 is fixed self.value = int(UInt64(value)).value + @always_inline("nodebug") + fn __mlir_index__(self) -> __mlir_type.index: + """Convert to index. + + Returns: + The corresponding __mlir_type.index value. + """ + return self.value + @always_inline("nodebug") fn __str__(self) -> String: """Convert this UInt to a string. From 3c1ac53b167071850b0d775b5a6a3801a83a3bb7 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 5 Jul 2024 15:15:47 -0400 Subject: [PATCH 1133/2019] [stdlib] Remove `load` from `LegacyPointer`. This will make the `LegacyPointer` API surface more harmonized with `UnsafePointer`, which should make ultimately removing `LegacyPointer` a step easier. Instead of doing `ptr.load(i)` we should do `ptr[i]`. MODULAR_ORIG_COMMIT_REV_ID: be0695f03a551251da7d7fda20aba400473c28a9 --- docs/changelog.md | 3 + stdlib/src/builtin/simd.mojo | 20 +++-- stdlib/src/memory/memory.mojo | 32 +++---- stdlib/src/memory/unsafe.mojo | 86 ------------------- stdlib/src/utils/static_tuple.mojo | 4 +- stdlib/test/builtin/test_sort_issue_1018.mojo | 4 +- 6 files changed, 35 insertions(+), 114 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0874d4dfa6..98ed28c73b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -388,6 +388,9 @@ what we publish. # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') ``` +- `LegacyPointer.load/store` are now removed. It's use is replaced with + `__getitem__` or `__setitem__`. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 57f6cbd53c..dfa705e8a2 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2749,6 +2749,10 @@ struct SIMD[type: DType, size: Int]( The loaded value. """ + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() + @parameter if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types @@ -2760,15 +2764,13 @@ struct SIMD[type: DType, size: Int]( # intentionally don't unroll, otherwise the compiler vectorizes for i in range(size): - v[i] = ptr.address.offset(offset + i).load[ - alignment=alignment - ]() + v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( + ptr.address.offset(int(offset) + i).address + ) return v - return ( - ptr.address.offset(offset) - .bitcast[SIMD[type, size]]() - .load[alignment=alignment]() + return __mlir_op.`pop.load`[alignment = alignment.value]( + ptr.address.offset(offset).bitcast[SIMD[type, size]]().address ) @staticmethod @@ -2847,7 +2849,9 @@ struct SIMD[type: DType, size: Int]( constrained[ alignment > 0, "alignment must be a positive integer value" ]() - ptr.address.bitcast[SIMD[type, size]]().store[alignment=alignment](val) + __mlir_op.`pop.store`[alignment = alignment.value]( + val, ptr.address.bitcast[SIMD[type, size]]().address + ) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 2760b53fe6..49b9f9d90c 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -199,17 +199,17 @@ fn memcpy[count: Int](dest: LegacyPointer, src: __type_of(dest)): @parameter if n >= 8: var ui64_size = sizeof[Int64]() - dest_data.bitcast[Int64]().store(src_data.bitcast[Int64]()[0]) - dest_data.offset(n - ui64_size).bitcast[Int64]().store( - src_data.offset(n - ui64_size).bitcast[Int64]()[0] - ) + dest_data.bitcast[Int64]()[] = src_data.bitcast[Int64]()[0] + dest_data.offset(n - ui64_size).bitcast[ + Int64 + ]()[] = src_data.offset(n - ui64_size).bitcast[Int64]()[0] return var ui32_size = sizeof[Int32]() - dest_data.bitcast[Int32]().store(src_data.bitcast[Int32]()[0]) - dest_data.offset(n - ui32_size).bitcast[Int32]().store( - src_data.offset(n - ui32_size).bitcast[Int32]()[0] - ) + dest_data.bitcast[Int32]()[] = src_data.bitcast[Int32]()[0] + dest_data.offset(n - ui32_size).bitcast[Int32]()[] = src_data.offset( + n - ui32_size + ).bitcast[Int32]()[0] return var dest_dtype_ptr = DTypePointer[DType.int8, dest.address_space](dest_data) @@ -265,16 +265,16 @@ fn memcpy( if n <= 16: if n >= 8: var ui64_size = sizeof[Int64]() - dest_data.bitcast[Int64]().store(src_data.bitcast[Int64]()[0]) - dest_data.offset(n - ui64_size).bitcast[Int64]().store( - src_data.offset(n - ui64_size).bitcast[Int64]()[0] - ) + dest_data.bitcast[Int64]()[] = src_data.bitcast[Int64]()[0] + dest_data.offset(n - ui64_size).bitcast[ + Int64 + ]()[] = src_data.offset(n - ui64_size).bitcast[Int64]()[0] return var ui32_size = sizeof[Int32]() - dest_data.bitcast[Int32]().store(src_data.bitcast[Int32]()[0]) - dest_data.offset(n - ui32_size).bitcast[Int32]().store( - src_data.offset(n - ui32_size).bitcast[Int32]()[0] - ) + dest_data.bitcast[Int32]()[] = src_data.bitcast[Int32]()[0] + dest_data.offset(n - ui32_size).bitcast[Int32]()[] = src_data.offset( + n - ui32_size + ).bitcast[Int32]()[0] return # TODO (#10566): This branch appears to cause a 12% regression in BERT by diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index f79f6fcb4f..4435c28a37 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -297,92 +297,6 @@ struct LegacyPointer[ """ return (self + offset)[] - # ===------------------------------------------------------------------=== # - # Load/Store - # ===------------------------------------------------------------------=== # - - alias _default_alignment = alignof[type]() if triple_is_nvidia_cuda() else 1 - - @always_inline("nodebug") - fn load[*, alignment: Int = Self._default_alignment](self) -> type: - """Loads the value the LegacyPointer object points to. - - Constraints: - The alignment must be a positive integer value. - - Parameters: - alignment: The minimal alignment of the address. - - Returns: - The loaded value. - """ - return self.load[alignment=alignment](0) - - @always_inline("nodebug") - fn load[ - T: Intable, *, alignment: Int = Self._default_alignment - ](self, offset: T) -> type: - """Loads the value the LegacyPointer object points to with the given offset. - - Constraints: - The alignment must be a positive integer value. - - Parameters: - T: The Intable type of the offset. - alignment: The minimal alignment of the address. - - Args: - offset: The offset to load from. - - Returns: - The loaded value. - """ - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - return __mlir_op.`pop.load`[alignment = alignment.value]( - self.offset(offset).address - ) - - @always_inline("nodebug") - fn store[ - T: Intable, /, *, alignment: Int = Self._default_alignment - ](self, offset: T, value: type): - """Stores the specified value to the location the LegacyPointer object points - to with the given offset. - - Constraints: - The alignment must be a positive integer value. - - Parameters: - T: The Intable type of the offset. - alignment: The minimal alignment of the address. - - Args: - offset: The offset to store to. - value: The value to store. - """ - self.offset(offset).store[alignment=alignment](value) - - @always_inline("nodebug") - fn store[*, alignment: Int = Self._default_alignment](self, value: type): - """Stores the specified value to the location the LegacyPointer object points - to. - - Constraints: - The alignment value must be a positive integer. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - value: The value to store. - """ - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - __mlir_op.`pop.store`[alignment = alignment.value](value, self.address) - @always_inline("nodebug") fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 799d6f4de4..d4974c1329 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -213,7 +213,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(arrayCopy).address, offset.value ) - var result = Pointer(ptr).load() + var result = Pointer(ptr)[] _ = arrayCopy return result @@ -236,7 +236,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(tmp.array).address, offset.value ) - Pointer(ptr).store(val) + Pointer(ptr)[] = val self = tmp diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index 116226ae69..d0333e87c4 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -25,10 +25,10 @@ fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: print(name, "size:", size, "max:", max, "incorrect sort") print("p[", end="") print(i - 1, end="") - print("] =", p.load(i - 1)) + print("] =", p[i - 1]) print("p[", end="") print(i, end="") - print("] =", p.load(i)) + print("] =", p[i]) print() p.free() raise "Failed" From 3f0a261fef7781a8ab39e7f14895501f360a726a Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 13:20:23 -0700 Subject: [PATCH 1134/2019] [mojo-stdlib] Remove `DType.address` `DType.address` is an alternate representation of pointers in the SIMD dtype system. Its use cases are extremely narrow and we don't need or want this generality in the representation of the language. Remove it entirely and zoom in on the two use cases the matter: masked scatter and gather (interop with LLVM intrinsics). MODULAR_ORIG_COMMIT_REV_ID: d14cfa2460a44280288883687497e8cbe12ce312 --- stdlib/src/builtin/dtype.mojo | 12 ---------- stdlib/src/builtin/io.mojo | 4 ++-- stdlib/src/builtin/simd.mojo | 35 ----------------------------- stdlib/src/memory/unsafe.mojo | 4 ++-- stdlib/src/sys/intrinsics.mojo | 20 +++++++++-------- stdlib/test/builtin/test_dtype.mojo | 1 - stdlib/test/builtin/test_simd.mojo | 12 ---------- 7 files changed, 15 insertions(+), 73 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 52f118e3e4..66509e1e47 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -78,12 +78,6 @@ struct DType( alias index = DType(__mlir_attr.`#kgen.dtype.constant : !kgen.dtype`) """Represents an integral type whose bitwidth is the maximum integral value on the system.""" - alias address = DType( - __mlir_attr.`#kgen.dtype.constant
    : !kgen.dtype` - ) - """Represents a pointer type whose bitwidth is the same as the bitwidth - of the hardware's pointer type (32-bit on 32-bit machines and 64-bit on - 64-bit machines).""" @always_inline fn __init__(inout self, *, other: Self): @@ -144,8 +138,6 @@ struct DType( return writer.write_str["float64"]() if self == DType.invalid: return writer.write_str["invalid"]() - if self == DType.address: - return writer.write_str["address"]() return writer.write_str["<>"]() @@ -601,9 +593,6 @@ fn _get_dtype_printf_format[type: DType]() -> StringLiteral: elif type is DType.index: return _index_printf_format() - elif type is DType.address: - return "%p" - elif type.is_floating_point(): return "%.17g" @@ -637,7 +626,6 @@ fn _get_runtime_dtype_size(type: DType) -> Int: DType.uint64, DType.float64, DType.index, - DType.address, ) @parameter diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 4c131bcd79..788253ad03 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -178,7 +178,7 @@ fn _snprintf_scalar[ return _snprintf["True"](buffer, size) else: return _snprintf["False"](buffer, size) - elif type.is_integral() or type is DType.address: + elif type.is_integral(): return _snprintf[_get_dtype_printf_format[type]()](buffer, size, x) elif ( type is DType.float16 or type is DType.bfloat16 or type is DType.float32 @@ -256,7 +256,7 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): @parameter if type is DType.bool: _put["True"]() if x else _put["False"]() - elif type.is_integral() or type is DType.address: + elif type.is_integral(): _printf[format](x) elif type.is_floating_point(): diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index dfa705e8a2..87147dfa8e 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1483,41 +1483,6 @@ struct SIMD[type: DType, size: Int]( return rebind[SIMD[target, size]]( _f32_to_bfloat16(self.cast[DType.float32]()) ) - elif target == DType.address: - var index_val = __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.simd<`, size.value, `, index>`] - ](self.value) - var tmp = SIMD[DType.address, size]( - __mlir_op.`pop.index_to_pointer`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, address >`, - ] - ](index_val) - ) - return rebind[SIMD[target, size]](tmp) - elif (type is DType.address) and target.is_integral(): - var index_tmp = SIMD[DType.index, size]( - __mlir_op.`pop.pointer_to_index`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - DType.index.value, - `>`, - ] - ]( - rebind[ - __mlir_type[ - `!pop.simd<`, - size.value, - `, address >`, - ] - ](self.value) - ) - ) - return index_tmp.cast[target]() else: return __mlir_op.`pop.cast`[ _type = __mlir_type[ diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 4435c28a37..b6bcfa59f8 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -928,7 +928,7 @@ struct DTypePointer[ ]() var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) - return gather(base.cast[DType.address](), mask, default, alignment) + return gather(base, mask, default, alignment) @always_inline("nodebug") fn scatter[ @@ -1006,7 +1006,7 @@ struct DTypePointer[ ]() var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) - scatter(val, base.cast[DType.address](), mask, alignment) + scatter(val, base, mask, alignment) @always_inline fn is_aligned[alignment: Int](self) -> Bool: diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index e2dbf37326..2fac00d50a 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -811,7 +811,7 @@ fn llvm_intrinsic[ # this function! fn _unsafe_aliasing_address_to_pointer[ type: DType -](owned addr: Scalar[DType.address]) -> DTypePointer[type]: +](owned addr: Scalar[DType.index]) -> DTypePointer[type]: return UnsafePointer.address_of(addr).bitcast[DTypePointer[type]]()[] @@ -819,7 +819,7 @@ fn _unsafe_aliasing_address_to_pointer[ fn gather[ type: DType, size: Int ]( - base: SIMD[DType.address, size], + owned base: SIMD[DType.index, size], mask: SIMD[DType.bool, size], passthrough: SIMD[type, size], alignment: Int = 0, @@ -874,7 +874,9 @@ fn gather[ "llvm.masked.gather", __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], ]( - base, + UnsafePointer.address_of(base).bitcast[ + __mlir_type[`!pop.simd<`, size.value, `, address>`], + ]()[], Int32(alignment), mask, passthrough, @@ -891,7 +893,7 @@ fn scatter[ type: DType, size: Int ]( value: SIMD[type, size], - base: SIMD[DType.address, size], + owned base: SIMD[DType.index, size], mask: SIMD[DType.bool, size], alignment: Int = 0, ): @@ -950,7 +952,9 @@ fn scatter[ return llvm_intrinsic["llvm.masked.scatter", NoneType]( value, - base, + UnsafePointer.address_of(base).bitcast[ + __mlir_type[`!pop.simd<`, size.value, `, address>`], + ]()[], Int32(alignment), mask, ) @@ -1371,9 +1375,7 @@ fn strided_load[ sizeof[type]() ) var passthrough = SIMD[type, simd_width]() - return gather[type, simd_width]( - offset.cast[DType.address](), mask, passthrough - ) + return gather[type, simd_width](offset, mask, passthrough) @always_inline("nodebug") @@ -1453,7 +1455,7 @@ fn strided_store[ sizeof[type]() ) - scatter[type, simd_width](value, offset.cast[DType.address](), mask) + scatter[type, simd_width](value, offset, mask) @always_inline("nodebug") diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index b6b990a1ba..e1d8b1646c 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -31,7 +31,6 @@ fn test_representable() raises: assert_equal(repr(DType.float32), "DType.float32") assert_equal(repr(DType.int64), "DType.int64") assert_equal(repr(DType.bool), "DType.bool") - assert_equal(repr(DType.address), "DType.address") assert_equal(repr(DType.index), "DType.index") diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 6259e92847..48ff557cee 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -65,8 +65,6 @@ def test_convert_simd_to_string(): # TODO: uncomment when https://github.com/modularml/mojo/issues/2353 is fixed # assert_equal(str(UInt32(-1)), "4294967295") assert_equal(str(UInt64(-1)), "18446744073709551615") - assert_equal(str(Scalar[DType.address](22)), "0x16") - assert_equal(str(Scalar[DType.address](0xDEADBEAF)), "0xdeadbeaf") assert_equal(str((UInt16(32768))), "32768") assert_equal(str((UInt16(65535))), "65535") @@ -245,7 +243,6 @@ def test_truthy(): DType.float32, DType.float64, DType.index, - # DType.address # TODO(29920) ) @parameter @@ -911,14 +908,6 @@ def test_deinterleave(): assert_equal(tup4[1], SIMD[DType.index, 2](1, 3)) -def test_address(): - assert_equal(Scalar[DType.address](1), 1) - assert_not_equal(Scalar[DType.address](1), 0) - - assert_true(Bool(Scalar[DType.address](12) > 1)) - assert_true(Bool(Scalar[DType.address](1) < 12)) - - def test_extract(): assert_equal(Int64(99).slice[1](), 99) assert_equal(Int64(99).slice[1, offset=0](), 99) @@ -1560,7 +1549,6 @@ def main(): test_abs() test_add() test_add_with_overflow() - test_address() test_cast() test_ceil() test_convert_simd_to_string() From 6a63ff188791a7c33618e94382db2fa010770c75 Mon Sep 17 00:00:00 2001 From: Katherine Wu <31663267+k-w-w@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:30:02 -0700 Subject: [PATCH 1135/2019] Implement `Pathlike` trait method in `String`, `StringLiteral`, and `StringRef`. This allows us to remove code duplication in `os` and `os.path`. MODULAR_ORIG_COMMIT_REV_ID: e0867ef3e8345ccdb260c3fd68d6463cb61e92db --- stdlib/src/builtin/file.mojo | 14 -- stdlib/src/builtin/string.mojo | 8 + stdlib/src/builtin/string_literal.mojo | 10 +- stdlib/src/os/fstat.mojo | 55 ++---- stdlib/src/os/os.mojo | 139 ++++------------ stdlib/src/os/path/path.mojo | 221 ++++++------------------- stdlib/src/utils/stringref.mojo | 8 + stdlib/test/os/test_remove.mojo | 20 +-- 8 files changed, 127 insertions(+), 348 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 7d7b1ca9f3..143ba314df 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -469,20 +469,6 @@ struct FileHandle: return Int(i64_res.value) -fn open(path: String, mode: String) raises -> FileHandle: - """Opens the file specified by path using the mode provided, returning a - FileHandle. - - Args: - path: The path to the file to open. - mode: The mode to open the file in. - - Returns: - A file handle. - """ - return FileHandle(path, mode) - - fn open[ PathLike: os.PathLike ](path: PathLike, mode: String) raises -> FileHandle: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index dca633d6a3..e0eaaf366c 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1328,6 +1328,14 @@ struct String( else: return "'" + result + "'" + fn __fspath__(self) -> String: + """Return the file system path representation (just the string itself). + + Returns: + The file system path representation as a string. + """ + return self + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 4f10eb5a6d..60b77847e7 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -178,7 +178,7 @@ struct StringLiteral( return substr in StringRef(self) # ===-------------------------------------------------------------------===# - # Trait impelemntations + # Trait implementations # ===-------------------------------------------------------------------===# @always_inline("nodebug") @@ -252,6 +252,14 @@ struct StringLiteral( """ return hash(self.unsafe_ptr(), len(self)) + fn __fspath__(self) -> String: + """Return the file system path representation of the object. + + Returns: + The file system path representation as a string. + """ + return self.__str__() + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index 4c6b3860f8..91783e1040 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -186,24 +186,6 @@ struct stat_result(Stringable): # ===----------------------------------------------------------------------=== # # stat # ===----------------------------------------------------------------------=== # -fn stat(path: String) raises -> stat_result: - """Get the status of a file or a file descriptor. - - Args: - path: The path to the directory. - - Returns: - Returns the stat_result on the path. - """ - _constrain_unix() - - @parameter - if os_is_macos(): - return _stat_macos(path)._to_stat_result() - elif has_neon(): - return _stat_linux_arm(path)._to_stat_result() - else: - return _stat_linux_x86(path)._to_stat_result() fn stat[PathLike: os.PathLike](path: PathLike) raises -> stat_result: @@ -212,22 +194,6 @@ fn stat[PathLike: os.PathLike](path: PathLike) raises -> stat_result: Parameters: PathLike: The a type conforming to the os.PathLike trait. - Args: - path: The path to the directory. - - Returns: - Returns the stat_result on the path. - """ - return stat(path.__fspath__()) - - -# ===----------------------------------------------------------------------=== # -# lstat -# ===----------------------------------------------------------------------=== # -fn lstat(path: String) raises -> stat_result: - """Get the status of a file or a file descriptor (similar to stat, but does - not follow symlinks). - Args: path: The path to the directory. @@ -235,16 +201,20 @@ fn lstat(path: String) raises -> stat_result: Returns the stat_result on the path. """ _constrain_unix() + var fspath = path.__fspath__() @parameter if os_is_macos(): - return _lstat_macos(path)._to_stat_result() + return _stat_macos(fspath)._to_stat_result() elif has_neon(): - return _lstat_linux_arm(path)._to_stat_result() + return _stat_linux_arm(fspath)._to_stat_result() else: - return _lstat_linux_x86(path)._to_stat_result() + return _stat_linux_x86(fspath)._to_stat_result() +# ===----------------------------------------------------------------------=== # +# lstat +# ===----------------------------------------------------------------------=== # fn lstat[PathLike: os.PathLike](path: PathLike) raises -> stat_result: """Get the status of a file or a file descriptor (similar to stat, but does not follow symlinks). @@ -258,4 +228,13 @@ fn lstat[PathLike: os.PathLike](path: PathLike) raises -> stat_result: Returns: Returns the stat_result on the path. """ - return lstat(path.__fspath__()) + _constrain_unix() + var fspath = path.__fspath__() + + @parameter + if os_is_macos(): + return _lstat_macos(fspath)._to_stat_result() + elif has_neon(): + return _lstat_linux_arm(fspath)._to_stat_result() + else: + return _lstat_linux_x86(fspath)._to_stat_result() diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index de66d03d19..1d9305dae8 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -207,18 +207,6 @@ fn getuid() -> Int: # ===----------------------------------------------------------------------=== # # listdir # ===----------------------------------------------------------------------=== # -fn listdir(path: String = "") raises -> List[String]: - """Gets the list of entries contained in the path provided. - - Args: - path: The path to the directory. - - Returns: - Returns the list of entries in the path provided. - """ - - var dir = _DirHandle(path) - return dir.list() fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: @@ -230,11 +218,11 @@ fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: Args: path: The path to the directory. - Returns: Returns the list of entries in the path provided. """ - return listdir(path.__fspath__()) + var dir = _DirHandle(path.__fspath__()) + return dir.list() # ===----------------------------------------------------------------------=== # @@ -287,50 +275,27 @@ fn abort[ # ===----------------------------------------------------------------------=== # # remove/unlink # ===----------------------------------------------------------------------=== # -fn remove(path: String) raises: +fn remove[PathLike: os.PathLike](path: PathLike) raises: """Removes the specified file. If the path is a directory or it can not be deleted, an error is raised. Absolute and relative paths are allowed, relative paths are resolved from cwd. + Parameters: + PathLike: The a type conforming to the os.PathLike trait. + Args: path: The path to the file. """ - var error = external_call["unlink", Int32](path.unsafe_ptr()) + var fspath = path.__fspath__() + var error = external_call["unlink", Int32](fspath.unsafe_ptr()) if error != 0: # TODO get error message, the following code prints it # var error_str = String("Something went wrong") # _ = external_call["perror", UnsafePointer[NoneType]](error_str.unsafe_ptr()) # _ = error_str - raise Error("Can not remove file: " + path) - - -fn remove[PathLike: os.PathLike](path: PathLike) raises: - """Removes the specified file. - If the path is a directory or it can not be deleted, an error is raised. - Absolute and relative paths are allowed, relative paths are resolved from cwd. - - Parameters: - PathLike: The a type conforming to the os.PathLike trait. - - Args: - path: The path to the file. - - """ - remove(path.__fspath__()) - - -fn unlink(path: String) raises: - """Removes the specified file. - If the path is a directory or it can not be deleted, an error is raised. - Absolute and relative paths are allowed, relative paths are resolved from cwd. - - Args: - path: The path to the file. - - """ - remove(path) + raise Error("Can not remove file: " + fspath) fn unlink[PathLike: os.PathLike](path: PathLike) raises: @@ -353,21 +318,6 @@ fn unlink[PathLike: os.PathLike](path: PathLike) raises: # ===----------------------------------------------------------------------=== # -fn mkdir(path: String, mode: Int = 0o777) raises: - """Creates a directory at the specified path. - If the directory can not be created an error is raised. - Absolute and relative paths are allowed, relative paths are resolved from cwd. - - Args: - path: The path to the directory. - mode: The mode to create the directory with. - """ - - var error = external_call["mkdir", Int32](path.unsafe_ptr(), mode) - if error != 0: - raise Error("Can not create directory: " + path) - - fn mkdir[PathLike: os.PathLike](path: PathLike, mode: Int = 0o777) raises: """Creates a directory at the specified path. If the directory can not be created an error is raised. @@ -381,13 +331,21 @@ fn mkdir[PathLike: os.PathLike](path: PathLike, mode: Int = 0o777) raises: mode: The mode to create the directory with. """ - mkdir(path.__fspath__(), mode) + var fspath = path.__fspath__() + var error = external_call["mkdir", Int32](fspath.unsafe_ptr(), mode) + if error != 0: + raise Error("Can not create directory: " + fspath) -def makedirs(path: String, mode: Int = 0o777, exist_ok: Bool = False) -> None: +def makedirs[ + PathLike: os.PathLike +](path: PathLike, mode: Int = 0o777, exist_ok: Bool = False) -> None: """Creates a specified leaf directory along with any necessary intermediate directories that don't already exist. + Parameters: + PathLike: The a type conforming to the os.PathLike trait. + Args: path: The path to the directory. mode: The mode to create the directory with. @@ -413,37 +371,7 @@ def makedirs(path: String, mode: Int = 0o777, exist_ok: Bool = False) -> None: e ) + "\nset `makedirs(path, exist_ok=True)` to allow existing dirs" if not os.path.isdir(path): - raise "path not created: " + path + "\n" + str(e) - - -def makedirs[ - PathLike: os.PathLike -](path: PathLike, mode: Int = 0o777, exist_ok: Bool = False) -> None: - """Creates a specified leaf directory along with any necessary intermediate - directories that don't already exist. - - Parameters: - PathLike: The a type conforming to the os.PathLike trait. - - Args: - path: The path to the directory. - mode: The mode to create the directory with. - exist_ok: Ignore error if `True` and path exists (default `False`). - """ - makedirs(path.__fspath__(), mode, exist_ok) - - -fn rmdir(path: String) raises: - """Removes the specified directory. - If the path is not a directory or it can not be deleted, an error is raised. - Absolute and relative paths are allowed, relative paths are resolved from cwd. - - Args: - path: The path to the directory. - """ - var error = external_call["rmdir", Int32](path.unsafe_ptr()) - if error != 0: - raise Error("Can not remove directory: " + path) + raise "path not created: " + path.__fspath__() + "\n" + str(e) fn rmdir[PathLike: os.PathLike](path: PathLike) raises: @@ -457,15 +385,21 @@ fn rmdir[PathLike: os.PathLike](path: PathLike) raises: Args: path: The path to the directory. """ - rmdir(path.__fspath__()) + var fspath = path.__fspath__() + var error = external_call["rmdir", Int32](fspath.unsafe_ptr()) + if error != 0: + raise Error("Can not remove directory: " + fspath) -def removedirs(path: String) -> None: +def removedirs[PathLike: os.PathLike](path: PathLike) -> None: """Remove a leaf directory and all empty intermediate ones. Directories corresponding to rightmost path segments will be pruned away until either - the whole path is consumed or an error occurs. Errors during this latter + the whole path is consumed or an error occurs. Errors during this latter phase are ignored, which occur when a directory was not empty. + Parameters: + PathLike: The a type conforming to the os.PathLike trait. + Args: path: The path to the directory. """ @@ -479,18 +413,3 @@ def removedirs(path: String) -> None: except: break head, tail = os.path.split(head) - - -def removedirs[PathLike: os.PathLike](path: PathLike) -> None: - """Remove a leaf directory and all empty intermediate ones. Directories - corresponding to rightmost path segments will be pruned away until either - the whole path is consumed or an error occurs. Errors during this latter - phase are ignored, which occur when a directory was not empty. - - Parameters: - PathLike: The a type conforming to the os.PathLike trait. - - Args: - path: The path to the directory. - """ - removedirs(path.__fspath__()) diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 1e7cf32d17..e6f4d420ee 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -79,26 +79,30 @@ fn _get_home_path() -> String: # TODO: When `pwd` module is implemented for POSIX, fallback to: # pwd.getpwuid(os.getuid()).pw_dir if $HOME is not set, and allow for `~user`. -fn expanduser(path: String) raises -> String: +fn expanduser[PathLike: os.PathLike, //](path: PathLike) raises -> String: """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on windows. If environment variables are not set or the `path` is not prefixed with `~`, returns the `path` unmodified. + Parameters: + PathLike: The type conforming to the os.PathLike trait. + Args: path: The path that is being expanded. Returns: The expanded path. """ - if not path.startswith("~"): - return path + var fspath = path.__fspath__() + if not fspath.startswith("~"): + return fspath var userhome = _get_home_path() if not userhome: - return path + return fspath # If there is more than a single `~` without correct separator, raise error. - if len(path) > 1 and path[1] != os.sep: + if len(fspath) > 1 and fspath[1] != os.sep: raise "malformed path, could not determine home directory." - var path_split = path.split(os.sep, 1) + var path_split = fspath.split(os.sep, 1) # If there is a properly formatted seperator, return expanded path. if len(path_split) == 2: return os.path.join(userhome, path_split[1]) @@ -106,30 +110,16 @@ fn expanduser(path: String) raises -> String: return userhome -fn expanduser[PathLike: os.PathLike, //](path: PathLike) raises -> String: - """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on windows. If - environment variables are not set or the `path` is not prefixed with `~`, - returns the `path` unmodified. - - Parameters: - PathLike: The type conforming to the os.PathLike trait. - - Args: - path: The path that is being expanded. - - Returns: - The expanded path. - """ - return expanduser(path.__fspath__()) - - # ===----------------------------------------------------------------------=== # # isdir # ===----------------------------------------------------------------------=== # -fn isdir(path: String) -> Bool: +fn isdir[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path is an existing directory. This follows symbolic links, so both islink() and isdir() can be true for the same path. + Parameters: + PathLike: The type conforming to the os.PathLike trait. + Args: path: The path to the directory. @@ -138,40 +128,27 @@ fn isdir(path: String) -> Bool: False otherwise. """ _constrain_unix() + var fspath = path.__fspath__() try: - var st_mode = _get_stat_st_mode(path) + var st_mode = _get_stat_st_mode(fspath) if S_ISDIR(st_mode): return True - return S_ISLNK(st_mode) and S_ISDIR(_get_lstat_st_mode(path)) + return S_ISLNK(st_mode) and S_ISDIR(_get_lstat_st_mode(fspath)) except: return False -fn isdir[PathLike: os.PathLike, //](path: PathLike) -> Bool: - """Return True if path is an existing directory. This follows - symbolic links, so both islink() and isdir() can be true for the same path. - - Parameters: - PathLike: The type conforming to the os.PathLike trait. - - Args: - path: The path to the directory. - - Returns: - True if the path is a directory or a link to a directory and - False otherwise. - """ - return isdir(path.__fspath__()) - - # ===----------------------------------------------------------------------=== # # isfile # ===----------------------------------------------------------------------=== # -fn isfile(path: String) -> Bool: +fn isfile[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Test whether a path is a regular file. + Parameters: + PathLike: The type conforming to the os.PathLike trait. + Args: path: The path to the directory. @@ -179,50 +156,19 @@ fn isfile(path: String) -> Bool: Returns True if the path is a regular file. """ _constrain_unix() + var fspath = path.__fspath__() try: - var st_mode = _get_stat_st_mode(path) + var st_mode = _get_stat_st_mode(fspath) if S_ISREG(st_mode): return True - return S_ISLNK(st_mode) and S_ISREG(_get_lstat_st_mode(path)) + return S_ISLNK(st_mode) and S_ISREG(_get_lstat_st_mode(fspath)) except: return False -fn isfile[PathLike: os.PathLike, //](path: PathLike) -> Bool: - """Test whether a path is a regular file. - - Parameters: - PathLike: The type conforming to the os.PathLike trait. - - Args: - path: The path to the directory. - - Returns: - Returns True if the path is a regular file. - """ - return isfile(path.__fspath__()) - - # ===----------------------------------------------------------------------=== # # islink # ===----------------------------------------------------------------------=== # -fn islink(path: String) -> Bool: - """Return True if path refers to an existing directory entry that is a - symbolic link. - - Args: - path: The path to the directory. - - Returns: - True if the path is a link to a directory and False otherwise. - """ - _constrain_unix() - try: - return S_ISLNK(_get_lstat_st_mode(path)) - except: - return False - - fn islink[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path refers to an existing directory entry that is a symbolic link. @@ -236,7 +182,11 @@ fn islink[PathLike: os.PathLike, //](path: PathLike) -> Bool: Returns: True if the path is a link to a directory and False otherwise. """ - return islink(path.__fspath__()) + _constrain_unix() + try: + return S_ISLNK(_get_lstat_st_mode(path.__fspath__())) + except: + return False # ===----------------------------------------------------------------------=== # @@ -244,23 +194,6 @@ fn islink[PathLike: os.PathLike, //](path: PathLike) -> Bool: # ===----------------------------------------------------------------------=== # -fn dirname(path: String) -> String: - """Returns the directory component of a pathname. - - Args: - path: The path to a file. - - Returns: - The directory component of a pathname. - """ - alias sep = str(os.sep) - var i = path.rfind(sep) + 1 - var head = path[:i] - if head and head != sep * len(head): - return head.rstrip(sep) - return head - - fn dirname[PathLike: os.PathLike, //](path: PathLike) -> String: """Returns the directory component of a pathname. @@ -273,7 +206,13 @@ fn dirname[PathLike: os.PathLike, //](path: PathLike) -> String: Returns: The directory component of a pathname. """ - return dirname(path.__fspath__()) + var fspath = path.__fspath__() + alias sep = str(os.sep) + var i = fspath.rfind(sep) + 1 + var head = fspath[:i] + if head and head != sep * len(head): + return head.rstrip(sep) + return head # ===----------------------------------------------------------------------=== # @@ -281,9 +220,12 @@ fn dirname[PathLike: os.PathLike, //](path: PathLike) -> String: # ===----------------------------------------------------------------------=== # -fn exists(path: String) -> Bool: +fn exists[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path exists. + Parameters: + PathLike: The type conforming to the os.PathLike trait. + Args: path: The path to the directory. @@ -292,35 +234,23 @@ fn exists(path: String) -> Bool: """ _constrain_unix() try: - _ = _get_stat_st_mode(path) + _ = _get_stat_st_mode(path.__fspath__()) return True except: return False -fn exists[PathLike: os.PathLike, //](path: PathLike) -> Bool: - """Return True if path exists. - - Parameters: - PathLike: The type conforming to the os.PathLike trait. - - Args: - path: The path to the directory. - - Returns: - Returns True if the path exists and is not a broken symbolic link. - """ - return exists(path.__fspath__()) - - # ===----------------------------------------------------------------------=== # # lexists # ===----------------------------------------------------------------------=== # -fn lexists(path: String) -> Bool: +fn lexists[PathLike: os.PathLike, //](path: PathLike) -> Bool: """Return True if path exists or is a broken symlink. + Parameters: + PathLike: The type conforming to the os.PathLike trait. + Args: path: The path to the directory. @@ -329,44 +259,17 @@ fn lexists(path: String) -> Bool: """ _constrain_unix() try: - _ = _get_lstat_st_mode(path) + _ = _get_lstat_st_mode(path.__fspath__()) return True except: return False -fn lexists[PathLike: os.PathLike, //](path: PathLike) -> Bool: - """Return True if path exists or is a broken symlink. - - Parameters: - PathLike: The type conforming to the os.PathLike trait. - - Args: - path: The path to the directory. - - Returns: - Returns True if the path exists or is a broken symbolic link. - """ - return exists(path.__fspath__()) - - # ===----------------------------------------------------------------------=== # # getsize # ===----------------------------------------------------------------------=== # -fn getsize(path: String) raises -> Int: - """Return the size, in bytes, of the specified path. - - Args: - path: The path to the file. - - Returns: - The size of the path in bytes. - """ - return stat(path).st_size - - fn getsize[PathLike: os.PathLike, //](path: PathLike) raises -> Int: """Return the size, in bytes, of the specified path. @@ -379,7 +282,7 @@ fn getsize[PathLike: os.PathLike, //](path: PathLike) raises -> Int: Returns: The size of the path in bytes. """ - return getsize(path.__fspath__()) + return stat(path.__fspath__()).st_size # ===----------------------------------------------------------------------=== # @@ -418,28 +321,6 @@ fn join(path: String, *paths: String) -> String: # ===----------------------------------------------------------------------=== # -def split(path: String) -> (String, String): - """ - Split a given pathname into two components: head and tail. This is useful - for separating the directory path from the filename. If the input path ends - with a seperator, the tail component will be empty. If there is no seperator - in the path, the head component will be empty, and the entire path will be - considered the tail. Trailing seperators in the head are stripped unless the - head is the root directory. - - Args: - path: The path to be split. - - Returns: - A tuple containing two strings: (head, tail). - """ - i = path.rfind(os.sep) + 1 - head, tail = path[:i], path[i:] - if head and head != str(os.sep) * len(head): - head = head.rstrip(sep) - return head, tail - - def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): """ Split a given pathname into two components: head and tail. This is useful @@ -457,9 +338,13 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): Returns: A tuple containing two strings: (head, tail). - """ - return split(path.__fspath__()) + fspath = path.__fspath__() + i = fspath.rfind(os.sep) + 1 + head, tail = fspath[:i], fspath[i:] + if head and head != str(os.sep) * len(head): + head = head.rstrip(sep) + return head, tail # TODO uncomment this when unpacking is supported diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index db44bc724d..69356755fa 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -433,6 +433,14 @@ struct StringRef( writer.write_str(str_slice) + fn __fspath__(self) -> String: + """Return the file system path representation of the object. + + Returns: + The file system path representation as a string. + """ + return self.__str__() + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index 0dc2d5508c..681e1abc9c 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -19,20 +19,6 @@ from pathlib import Path from testing import assert_false, assert_raises, assert_true -fn create_file_and_test_delete_string[ - func: fn (String) raises -> None, name: StringLiteral -](filename: String) raises: - try: - with open(filename, "w"): - pass - except: - assert_true(False, "Failed to create file for test") - - assert_true(exists(filename)) - func(filename) - assert_false(exists(filename), "test with '" + name + "' failed") - - fn create_file_and_test_delete_path[ func: fn[PathLike: PathLike] (PathLike) raises -> None, name: StringLiteral, @@ -65,14 +51,14 @@ fn test_remove() raises: with assert_raises(contains="Can not remove file: "): remove(my_file_path) - create_file_and_test_delete_string[remove, "remove"](my_file_name) - create_file_and_test_delete_string[unlink, "unlink"](my_file_name) + create_file_and_test_delete_path[remove, "remove"](my_file_name) + create_file_and_test_delete_path[unlink, "unlink"](my_file_name) create_file_and_test_delete_path[remove, "remove"](my_file_path) create_file_and_test_delete_path[unlink, "unlink"](my_file_path) # test with relative path my_file_name = str(Path("my_relative_file.test")) - create_file_and_test_delete_string[remove, "remove"](my_file_name) + create_file_and_test_delete_path[remove, "remove"](my_file_name) def main(): From 2bb6ec8ffb51f87bf8b7959477c206536266b28a Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 13:34:54 -0700 Subject: [PATCH 1136/2019] [mojo-stdlib] Simplify `*Pointer.__int__` The result type is now always an `__mlir_type.index`. MODULAR_ORIG_COMMIT_REV_ID: ba23f7c44fb1e655e7e3d85f01d36292b52027e2 --- stdlib/src/memory/unsafe.mojo | 4 +--- stdlib/src/memory/unsafe_pointer.mojo | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index b6bcfa59f8..872be68a4b 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -304,9 +304,7 @@ struct LegacyPointer[ Returns: The address of the pointer as an Int. """ - return __mlir_op.`pop.pointer_to_index`[ - _type = __mlir_type.`!pop.scalar` - ](self.address) + return __mlir_op.`pop.pointer_to_index`(self.address) # ===------------------------------------------------------------------=== # # Allocate/Free diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 3f73e7e313..016458026a 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -373,9 +373,7 @@ struct UnsafePointer[ Returns: The address of the pointer as an Int. """ - return __mlir_op.`pop.pointer_to_index`[ - _type = __mlir_type.`!pop.scalar` - ](self.address) + return __mlir_op.`pop.pointer_to_index`(self.address) fn __str__(self) -> String: """Gets a string representation of the pointer. From 8cb469a16b4fcc852d219f18008815152b66e647 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 13:35:36 -0700 Subject: [PATCH 1137/2019] [mojo-stdlib] Make `PythonObject._unsafe_get_as_pointer` public (NFC) Make this method public so that users will clearly see through the LSP and docs that it is unsafe and the restrctions around it. MODULAR_ORIG_COMMIT_REV_ID: 45624848acfcafb682963ca70194b85f38a5ee64 --- docs/manual/pointers.ipynb | 2 +- examples/notebooks/RayTracing.ipynb | 4 ++-- stdlib/src/python/object.mojo | 18 +++++++++++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 17037e30a8..e4ac8d8723 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -290,7 +290,7 @@ "def share_array():\n", " np = Python.import_module(\"numpy\")\n", " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - " ptr = arr.__array_interface__[\"data\"][0]._unsafe_get_as_pointer[DType.int64]()\n", + " ptr = arr.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.int64]()\n", " for i in range(9):\n", " print(ptr[i], end=\", \")\n", "\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 618326e495..52af34e1e4 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -253,7 +253,7 @@ " var np_image = np.zeros((self.height, self.width, 3), np.float32)\n", "\n", " # We use raw pointers to efficiently copy the pixels to the numpy array\n", - " var out_pointer = np_image.__array_interface__[\"data\"][0]._unsafe_get_as_pointer[DType.float32]()\n", + " var out_pointer = np_image.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.float32]()\n", " var in_pointer = self.pixels.bitcast[Float32]()\n", "\n", " for row in range(self.height):\n", @@ -274,7 +274,7 @@ " var cols = int(np_image.shape[1])\n", " var image = Image(rows, cols)\n", "\n", - " var in_pointer = np_image.__array_interface__[\"data\"][0]._unsafe_get_as_pointer[DType.float32]()\n", + " var in_pointer = np_image.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.float32]()\n", " var out_pointer = image.pixels.bitcast[Float32]()\n", "\n", " for row in range(rows):\n", diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 691f5a051d..907073ddf2 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -1170,11 +1170,19 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyLong_AsLong(self.py_object.value) - @always_inline - fn _unsafe_get_as_pointer[type: DType](self) -> DTypePointer[type]: - # Warning: converting from an integer to a pointer is unsafe! The - # compiler assumes the resulting pointer DOES NOT alias any Mojo-derived - # pointer. This is OK because the pointer originates from Python. + fn unsafe_get_as_pointer[type: DType](self) -> DTypePointer[type]: + """Convert a Python-owned and managed pointer into a Mojo pointer. + + Warning: converting from an integer to a pointer is unsafe! The + compiler assumes the resulting pointer DOES NOT alias any Mojo-derived + pointer. This is OK because the pointer originates from Python. + + Parameters: + type: The desired DType of the pointer. + + Returns: + A `DTypePointer` for the underlying Python data. + """ var tmp = int(self) var result = UnsafePointer.address_of(tmp).bitcast[ DTypePointer[type] From 6c4c4a291eed3ba7142d8c641553aaf00682c88b Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 5 Jul 2024 13:40:59 -0700 Subject: [PATCH 1138/2019] [stdlib] Adding range unit tests and reorganizing the UInt range implementations This change adds more internal validation to the stdlib range implementation and adds additional unit tests to verify basic functionality. For example, the addition of `debug_assert` for bounds checks in `__getitem__` functions. This change also fixes an issue with the `UInt.MAX` alias type not being deduced as an UInt. MODULAR_ORIG_COMMIT_REV_ID: 99d357338e1d6a4c455fb51283107cf605894bfd --- stdlib/src/builtin/range.mojo | 232 +++++++++--------- stdlib/src/builtin/uint.mojo | 4 +- stdlib/test/builtin/test_range.mojo | 39 +++ .../builtin/test_range_out_of_bounds.mojo | 41 ++++ stdlib/test/builtin/test_uint.mojo | 4 +- 5 files changed, 206 insertions(+), 114 deletions(-) create mode 100644 stdlib/test/builtin/test_range_out_of_bounds.mojo diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 836f4e88a5..86d9725341 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -89,6 +89,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: + debug_assert(idx < self.__len__(), "index out of range") return index(idx) @always_inline("nodebug") @@ -96,35 +97,6 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): return range(self.end - 1, -1, -1) -@register_passable("trivial") -struct _UIntZeroStartingRange(UIntSized): - var curr: UInt - var end: UInt - - @always_inline - fn __init__(inout self, end: UInt): - self.curr = max(0, end) - self.end = self.curr - - @always_inline - fn __iter__(self) -> Self: - return self - - @always_inline - fn __next__(inout self) -> UInt: - var curr = self.curr - self.curr -= 1 - return self.end - curr - - @always_inline - fn __len__(self) -> UInt: - return self.curr - - @always_inline - fn __getitem__(self, idx: UInt) -> UInt: - return idx - - @value @register_passable("trivial") struct _SequentialRange(Sized, ReversibleRange, _IntIterable): @@ -147,6 +119,7 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: + debug_assert(idx < self.__len__(), "index out of range") return self.start + index(idx) @always_inline("nodebug") @@ -177,26 +150,6 @@ struct _StridedRangeIterator(Sized): return result -@value -@register_passable("trivial") -struct _UIntStridedRangeIterator(UIntSized): - var start: UInt - var end: UInt - var step: UInt - - @always_inline - fn __len__(self) -> UInt: - if self.start < self.end: - return self.end - self.start - return 0 - - @always_inline - fn __next__(inout self) -> UInt: - var result = self.start - self.start += self.step - return result - - @value @register_passable("trivial") struct _StridedRange(Sized, ReversibleRange, _StridedIterable): @@ -230,6 +183,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: + debug_assert(idx < self.__len__(), "index out of range") return self.start + index(idx) * self.step @always_inline("nodebug") @@ -241,41 +195,6 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return range(start, end, step) -@value -@register_passable("trivial") -struct _UIntStridedRange(UIntSized, _UIntStridedIterable): - var start: UInt - var end: UInt - var step: UInt - - @always_inline - fn __init__(inout self, start: UInt, end: UInt, step: UInt): - self.start = start - self.end = end - debug_assert(step != 0, "step cannot be 0") - self.step = step - - @always_inline - fn __iter__(self) -> _UIntStridedRangeIterator: - return _UIntStridedRangeIterator(self.start, self.end, self.step) - - @always_inline - fn __next__(inout self) -> UInt: - var result = self.start - self.start += self.step - return result - - @always_inline - fn __len__(self) -> UInt: - if self.start > self.end: - return 0 - return ceildiv(self.end - self.start, self.step) - - @always_inline - fn __getitem__(self, idx: UInt) -> UInt: - return self.start + idx * self.step - - @always_inline("nodebug") fn range[type: Intable](end: type) -> _ZeroStartingRange: """Constructs a [0; end) Range. @@ -292,19 +211,6 @@ fn range[type: Intable](end: type) -> _ZeroStartingRange: return _ZeroStartingRange(int(end)) -@always_inline -fn range(end: UInt) -> _UIntZeroStartingRange: - """Constructs a [0; end) Range. - - Args: - end: The end of the range. - - Returns: - The constructed range. - """ - return _UIntZeroStartingRange(end) - - @always_inline fn range[type: IntableRaising](end: type) raises -> _ZeroStartingRange: """Constructs a [0; end) Range. @@ -382,37 +288,143 @@ fn range[ @always_inline -fn range(start: UInt, end: UInt, step: UInt = 1) -> _UIntStridedRange: +fn range[ + t0: IntableRaising, t1: IntableRaising, t2: IntableRaising +](start: t0, end: t1, step: t2) raises -> _StridedRange: """Constructs a [start; end) Range with a given step. + Parameters: + t0: The type of the start value. + t1: The type of the end value. + t2: The type of the step value. + Args: start: The start of the range. end: The end of the range. - step: The step for the range. Defaults to 1. + step: The step for the range. Returns: The constructed range. """ - return _UIntStridedRange(start, end, step) + return _StridedRange(int(start), int(end), int(step)) + + +# ===----------------------------------------------------------------------=== # +# Range UInt +# ===----------------------------------------------------------------------=== # + + +@register_passable("trivial") +struct _UIntZeroStartingRange(UIntSized): + var curr: UInt + var end: UInt + + @always_inline + fn __init__(inout self, end: UInt): + self.curr = max(0, end) + self.end = self.curr + + @always_inline + fn __iter__(self) -> Self: + return self + + @always_inline + fn __next__(inout self) -> UInt: + var curr = self.curr + self.curr -= 1 + return self.end - curr + + @always_inline + fn __len__(self) -> UInt: + return self.curr + + @always_inline + fn __getitem__(self, idx: UInt) -> UInt: + debug_assert(idx < self.__len__(), "index out of range") + return idx + + +@value +@register_passable("trivial") +struct _UIntStridedRangeIterator(UIntSized): + var start: UInt + var end: UInt + var step: UInt + + @always_inline + fn __len__(self) -> UInt: + if self.start < self.end: + return self.end - self.start + return 0 + + @always_inline + fn __next__(inout self) -> UInt: + var result = self.start + self.start += self.step + return result + + +@value +@register_passable("trivial") +struct _UIntStridedRange(UIntSized, _UIntStridedIterable): + var start: UInt + var end: UInt + var step: UInt + + @always_inline + fn __init__(inout self, start: UInt, end: UInt, step: UInt): + self.start = start + self.end = end + debug_assert(step != 0, "step cannot be 0") + self.step = step + + @always_inline + fn __iter__(self) -> _UIntStridedRangeIterator: + return _UIntStridedRangeIterator(self.start, self.end, self.step) + + @always_inline + fn __next__(inout self) -> UInt: + if self.start >= self.end: + return self.end + var result = self.start + self.start += self.step + return result + + @always_inline + fn __len__(self) -> UInt: + if self.start >= self.end: + return 0 + return ceildiv(self.end - self.start, self.step) + + @always_inline + fn __getitem__(self, idx: UInt) -> UInt: + debug_assert(idx < self.__len__(), "index out of range") + return self.start + idx * self.step @always_inline -fn range[ - t0: IntableRaising, t1: IntableRaising, t2: IntableRaising -](start: t0, end: t1, step: t2) raises -> _StridedRange: - """Constructs a [start; end) Range with a given step. +fn range(end: UInt) -> _UIntZeroStartingRange: + """Constructs a [0; end) Range. - Parameters: - t0: The type of the start value. - t1: The type of the end value. - t2: The type of the step value. + Args: + end: The end of the range. + + Returns: + The constructed range. + """ + return _UIntZeroStartingRange(end) + + +@always_inline +fn range(start: UInt, end: UInt, step: UInt = 1) -> _UIntStridedRange: + """Constructs a [start; end) Range with a given step. Args: start: The start of the range. end: The end of the range. - step: The step for the range. + step: The step for the range. Defaults to 1. Returns: The constructed range. """ - return _StridedRange(int(start), int(end), int(step)) + return _UIntStridedRange(start, end, step) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index b769745a77..265c686e67 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -33,10 +33,10 @@ struct UInt(Comparable, Formattable, Representable, Stringable): `UInt8`, `UInt16`, `UInt32`, or `UInt64`. """ - alias MAX = (2 ** bitwidthof[DType.index]()) - 1 + alias MAX: UInt = (UInt(2) ** bitwidthof[DType.index]()) - UInt(1) """Returns the maximum integer value.""" - alias MIN = UInt(0) + alias MIN: UInt = 0 """Returns the minimum value of type.""" var value: __mlir_type.index diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 84f5124cfc..f0095089cd 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -39,6 +39,26 @@ def test_range_len(): assert_equal(range(10, 5, -20).__len__(), 1, "len(range(10, 5, -20))") +def test_range_len_uint_maxuint(): + assert_equal( + range(UInt(0), UInt.MAX).__len__(), UInt.MAX, "len(range(0, UInt.MAX))" + ) + assert_equal( + range(UInt.MAX, UInt(0), UInt(1)).__len__(), + 0, + "len(range(UInt.MAX, 0, 1))", + ) + + +def test_range_len_uint_empty(): + assert_equal( + range(UInt(0), UInt(0), UInt(1)).__len__(), 0, "len(range(0, 0, 1))" + ) + assert_equal( + range(UInt(10), UInt(10), UInt(1)).__len__(), 0, "len(range(10, 10, 1))" + ) + + def test_range_len_uint(): assert_equal(range(UInt(10)).__len__(), 10, "len(range(10))") @@ -151,10 +171,29 @@ def test_indexing(): assert_equal(r[3], 3) +def test_range_bounds(): + var start = 0 + var end = 10 + + # verify loop iteration + var r = range(start, end) + var last_seen = -1 + for x in r: + last_seen = x + assert_equal(last_seen, end - 1) + + # verify index lookup + var ln = r.__len__() + assert_equal(r[ln - 1], last_seen) + + def main(): test_range_len() test_range_len_uint() + test_range_len_uint_maxuint() + test_range_len_uint_empty() test_range_getitem() test_range_getitem_uint() test_range_reversed() test_indexing() + test_range_bounds() diff --git a/stdlib/test/builtin/test_range_out_of_bounds.mojo b/stdlib/test/builtin/test_range_out_of_bounds.mojo new file mode 100644 index 0000000000..24e15b6f7d --- /dev/null +++ b/stdlib/test/builtin/test_range_out_of_bounds.mojo @@ -0,0 +1,41 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: has_not +# RUN: not --crash mojo -D MOJO_ENABLE_ASSERTIONS %s 2>&1 + +from testing import assert_equal + + +def test_range_getitem_uint_out_of_bounds(): + assert_equal(range(UInt(0), UInt.MAX)[0], UInt(0), "range(0, UInt.MAX)[0]") + assert_equal( + range(UInt(0), UInt.MAX)[UInt.MAX - 1], + UInt.MAX - 1, + "range(0, UInt.MAX)[UInt.MAX - 1]", + ) + assert_equal( + range(UInt(10), UInt(0), UInt(1)).__len__(), + 0, + "invalid start/end does not generate a zero length range", + ) + + # validate basic range guarantees + var rng_obj = range(UInt(0), UInt(10)) + assert_equal(rng_obj.__len__(), 10, "incorrect range length") + + # ensure out-of-bounds access calls abort through the assert handler + _ = rng_obj[10] + + +def main(): + test_range_getitem_uint_out_of_bounds() diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 265178adbb..925729600c 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -93,9 +93,9 @@ def test_inequality(): def test_properties(): assert_equal(UInt.MIN, UInt(0)) if bitwidthof[DType.index]() == 32: - assert_equal(UInt.MAX, (2**32) - 1) + assert_equal(UInt.MAX, UInt(((2**32) - 1).value)) else: - assert_equal(UInt.MAX, (2**64) - 1) + assert_equal(UInt.MAX, UInt(((2**64) - 1).value)) def test_add(): From ba1f2b3dd8ff71b0901d6ddb50fcf36f44636471 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 5 Jul 2024 14:23:54 -0700 Subject: [PATCH 1139/2019] - Fallback for home dir if `HOME` is not set on POSIX - Add the ability to expand a username with `os.path.expanduser("~user/folder")` MODULAR_ORIG_COMMIT_REV_ID: 1a3af1263b107edab4964d201e6ab41498c1a40f --- docs/changelog.md | 50 ++++++++++++++---------- stdlib/src/os/path/path.mojo | 43 ++++++++++++++------ stdlib/test/os/path/test_expanduser.mojo | 25 ++++++++---- stdlib/test/pathlib/test_pathlib.mojo | 13 ++++-- 4 files changed, 86 insertions(+), 45 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 98ed28c73b..961c586dee 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -202,7 +202,10 @@ what we publish. ```mojo import os print(os.path.expanduser("~/.modular")) - # /home/username/.modular + # /Users/username/.modular + print(os.path.expanduser("~root/folder")) + # /var/root/folder (on macos) + # /root/folder (on linux) ``` - `Path.home()` has been added to return a path of the users home directory. @@ -222,11 +225,36 @@ what we publish. and removing nested directories: ```mojo + import os path = os.path.join("dir1", "dir2", "dir3") os.path.makedirs(path, exist_ok=True) os.path.removedirs(path) ``` +- The `pwd` module has been added for accessing user information in + `/etc/passwd` on POSIX systems. This follows the same logic as Python: + + ```mojo + import pwd + import os + current_user = pwd.getpwuid(os.getuid()) + print(current_user) + + # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, + # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', + # pw_shell='/bin/zsh') + + print(current_user.pw_uid) + + # 501 + + root = pwd.getpwnam("root") + print(root) + + # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, + # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') + ``` + ### 🦋 Changed - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a @@ -368,26 +396,6 @@ what we publish. - The `time.now()` function has been deprecated. Please use `time.perf_counter` or `time.perf_counter_ns` instead. -- The `pwd` module has been added for accessing user information in - `/etc/passwd` on POSIX systems. This follows the same logic as Python: - - ```mojo - import pwd - import os - current_user = pwd.getpwuid(os.getuid()) - print(current_user) - - # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, - # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', - # pw_shell='/bin/zsh') - - root = pwd.getpwnam("root") - print(root) - - # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, - # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') - ``` - - `LegacyPointer.load/store` are now removed. It's use is replaced with `__getitem__` or `__setitem__`. diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index e6f4d420ee..fb72faf888 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -32,6 +32,7 @@ from .._macos import _stat as _stat_macos from ..fstat import stat from ..os import sep from ..env import getenv +from pwd import getpwuid # ===----------------------------------------------------------------------=== # @@ -70,19 +71,40 @@ fn _get_lstat_st_mode(path: String) raises -> Int: # ===----------------------------------------------------------------------=== # -fn _get_home_path() -> String: +fn _user_home_path(path: String) -> String: @parameter if os_is_windows(): return getenv("USERPROFILE") - return getenv("HOME") + else: + var user_end = path.find(sep, 1) + if user_end < 0: + user_end = len(path) + # Special POSIX syntax for ~[user-name]/path + if len(path) > 1 and user_end > 1: + try: + return pwd.getpwnam(path[1:user_end]).pw_dir + except: + return "" + else: + var user_home = getenv("HOME") + # Fallback to password database if `HOME` not set + if not user_home: + try: + user_home = pwd.getpwuid(getuid()).pw_dir + except: + return "" + return user_home -# TODO: When `pwd` module is implemented for POSIX, fallback to: -# pwd.getpwuid(os.getuid()).pw_dir if $HOME is not set, and allow for `~user`. fn expanduser[PathLike: os.PathLike, //](path: PathLike) raises -> String: - """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on windows. If - environment variables are not set or the `path` is not prefixed with `~`, - returns the `path` unmodified. + """Expands a tilde "~" prefix in `path` to the user's home directory. + + For example, `~/folder` becomes `/home/current_user/folder`. On macOS and + Linux a path starting with `~user/` will expand to the specified user's home + directory, so `~user/folder` becomes `/home/user/folder`. + + If the home directory cannot be determined, or the `path` is not prefixed + with "~", the original path is returned unchanged. Parameters: PathLike: The type conforming to the os.PathLike trait. @@ -96,14 +118,11 @@ fn expanduser[PathLike: os.PathLike, //](path: PathLike) raises -> String: var fspath = path.__fspath__() if not fspath.startswith("~"): return fspath - var userhome = _get_home_path() + var userhome = _user_home_path(fspath) if not userhome: return fspath - # If there is more than a single `~` without correct separator, raise error. - if len(fspath) > 1 and fspath[1] != os.sep: - raise "malformed path, could not determine home directory." var path_split = fspath.split(os.sep, 1) - # If there is a properly formatted seperator, return expanded path. + # If there is a properly formatted seperator, return expanded fspath. if len(path_split) == 2: return os.path.join(userhome, path_split[1]) # Path was a single `~` character, return home path diff --git a/stdlib/test/os/path/test_expanduser.mojo b/stdlib/test/os/path/test_expanduser.mojo index 24cc965732..c1932bfd5d 100644 --- a/stdlib/test/os/path/test_expanduser.mojo +++ b/stdlib/test/os/path/test_expanduser.mojo @@ -17,7 +17,7 @@ import os from os.path import expanduser, join from os.env import setenv, getenv -from testing import assert_equal, assert_raises +from testing import assert_equal, assert_raises, assert_true from sys.info import os_is_windows @@ -48,6 +48,7 @@ fn main() raises: var original_home = get_current_home() set_home(user_path) + # Single `~` assert_equal(user_path, expanduser("~")) # Path with home directory @@ -65,16 +66,24 @@ fn main() raises: # Empty string assert_equal("", expanduser("")) - # Malformed path should raise - with assert_raises(): - _ = expanduser("~badpath/folder") - # Path with multiple tildes assert_equal(join(user_path, "~folder"), expanduser("~/~folder")) - # Test that empty HOME returns `~` + # Tests with empty "HOME" and "USERPROFILE" set_home("") - assert_equal(expanduser("~/test/path"), "~/test/path") - assert_equal(expanduser("~"), "~") + if os_is_windows(): + # Don't expand on windows if home isn't set + assert_equal("~/folder", expanduser("~/folder")) + else: + # Test fallback to `/etc/passwd` works on linux + alias folder = "~/folder" + assert_true(len(expanduser(folder)) > len(folder)) + + # Test expanding user name on Unix + assert_true(expanduser("~root") != "~root") + + # Test path is returned unchanged on missing user + var missing_user = "~asdfasdzvxewr/user" + assert_equal(expanduser(missing_user), missing_user) set_home(original_home) diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index e640a26b2d..7c12368ced 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -124,10 +124,6 @@ def test_expand_user(): # Original path should remain unmodified assert_equal(path, os.path.join("~", "test")) - # Test that empty HOME returns `~` - set_home(Path("")) - assert_equal(Path.home(), Path("~")) - # Make sure this process doesn't break other tests by changing the home dir. set_home(original_home) @@ -140,6 +136,15 @@ def test_home(): assert_equal(user_path, Path.home()) # Match Python behavior allowing `home()` to overwrite existing path. assert_equal(user_path, Path("test").home()) + # Tests with empty "HOME" and "USERPROFILE" + set_home(Path("")) + if os_is_windows(): + # Don't expand on windows if home isn't set + assert_equal(Path("~"), Path.home()) + else: + # Test fallback to `/etc/passwd` works on linux + assert_true(len(Path.home().path) > 1) + # Ensure other tests in this process aren't broken by changing the home dir. set_home(original_home) From 767e43f703cbfae89540621bbc6457de3e7035cf Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 5 Jul 2024 17:31:40 -0400 Subject: [PATCH 1140/2019] [stdlib] Add `memcmp` function working on `UnsafePointer` Currently, there's `memcmp` functions for `LegacyPointer` operands and `DTypePointer`, but not `UnsafePointer`. This PR adds an overload and augment the tests in `test_memory.mojo` to use `UnsafePointer` instead where applicable. MODULAR_ORIG_COMMIT_REV_ID: 9e735916bcce7f5e4ee9eee80d38a0ef1c98017f --- stdlib/src/memory/memory.mojo | 37 +++++++++++++++++++++++++++++ stdlib/test/memory/test_memory.mojo | 37 +++++++++++++++++------------ 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 49b9f9d90c..b4090d6427 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -164,6 +164,43 @@ fn memcmp[ return _memcmp_impl(ds1, ds2, byte_count) +@always_inline +fn memcmp[ + type: AnyType, address_space: AddressSpace +]( + s1: UnsafePointer[type, address_space], + s2: UnsafePointer[type, address_space], + count: Int, +) -> Int: + """Compares two buffers. Both strings are assumed to be of the same length. + + Parameters: + type: The element type. + address_space: The address space of the pointer. + + Args: + s1: The first buffer address. + s2: The second buffer address. + count: The number of elements in the buffers. + + Returns: + Returns 0 if the bytes strings are identical, 1 if s1 > s2, and -1 if + s1 < s2. The comparison is performed by the first different byte in the + byte strings. + """ + var byte_count = count * sizeof[type]() + + @parameter + if sizeof[type]() >= sizeof[DType.int32](): + var ds1 = DTypePointer[DType.int32, address_space](s1.bitcast[Int32]()) + var ds2 = DTypePointer[DType.int32, address_space](s2.bitcast[Int32]()) + return _memcmp_impl(ds1, ds2, byte_count // sizeof[DType.int32]()) + + var ds1 = DTypePointer[DType.int8, address_space](s1.bitcast[Int8]()) + var ds2 = DTypePointer[DType.int8, address_space](s2.bitcast[Int8]()) + return _memcmp_impl(ds1, ds2, byte_count) + + # ===----------------------------------------------------------------------===# # memcpy # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index f3aa2d65ca..9abe9391c5 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -14,7 +14,14 @@ from sys import sizeof -from memory import DTypePointer, Pointer, memcmp, memcpy, memset, memset_zero +from memory import ( + DTypePointer, + UnsafePointer, + memcmp, + memcpy, + memset, + memset_zero, +) from testing import ( assert_almost_equal, assert_equal, @@ -40,10 +47,10 @@ def test_memcpy(): var pair1 = Pair(1, 2) var pair2 = Pair(0, 0) - var src = Pointer.address_of(pair1) + var src = UnsafePointer.address_of(pair1) var dsrc = DTypePointer[DType.int8](src.bitcast[int8_pop]().address) - var dest = Pointer.address_of(pair2) + var dest = UnsafePointer.address_of(pair2) var ddest = DTypePointer[DType.int8](dest.bitcast[int8_pop]().address) # DTypePointer test @@ -52,7 +59,7 @@ def test_memcpy(): assert_equal(pair2.lo, 1) assert_equal(pair2.hi, 2) - # Pointer test + # UnsafePointer test pair2.lo = 0 pair2.hi = 0 memcpy(dest, src, 1) @@ -117,10 +124,10 @@ def test_memcmp(): var pair1 = Pair(1, 2) var pair2 = Pair(1, 2) - var ptr1 = Pointer.address_of(pair1) + var ptr1 = UnsafePointer.address_of(pair1) var dptr1 = DTypePointer[DType.int8](ptr1.bitcast[int8_pop]().address) - var ptr2 = Pointer.address_of(pair2) + var ptr2 = UnsafePointer.address_of(pair2) var dptr2 = DTypePointer[DType.int8](ptr2.bitcast[int8_pop]().address) var errors1 = memcmp(dptr1, dptr2, 1) @@ -183,8 +190,8 @@ def test_memcmp_simd(): def test_memcmp_extensive[ type: DType, extermes: StringLiteral = "" ](count: Int): - var ptr1 = Pointer[Scalar[type]].alloc(count) - var ptr2 = Pointer[Scalar[type]].alloc(count) + var ptr1 = UnsafePointer[Scalar[type]].alloc(count) + var ptr2 = UnsafePointer[Scalar[type]].alloc(count) var dptr1 = DTypePointer[type].alloc(count) var dptr2 = DTypePointer[type].alloc(count) @@ -285,7 +292,7 @@ def test_memcmp_extensive(): def test_memset(): var pair = Pair(1, 2) - var ptr = Pointer.address_of(pair) + var ptr = UnsafePointer.address_of(pair) memset_zero(ptr, 1) assert_equal(pair.lo, 0) @@ -311,10 +318,10 @@ def test_memset(): def test_pointer_string(): - var nullptr = Pointer[Int]() + var nullptr = UnsafePointer[Int]() assert_equal(str(nullptr), "0x0") - var ptr = Pointer[Int].alloc(1) + var ptr = UnsafePointer[Int].alloc(1) assert_true(str(ptr).startswith("0x")) assert_not_equal(str(ptr), "0x0") ptr.free() @@ -331,15 +338,15 @@ def test_dtypepointer_string(): def test_pointer_explicit_copy(): - var ptr = Pointer[Int].alloc(1) + var ptr = UnsafePointer[Int].alloc(1) ptr[] = 42 - var copy = Pointer(other=ptr) + var copy = UnsafePointer(other=ptr) assert_equal(copy[], 42) ptr.free() def test_pointer_refitem(): - var ptr = Pointer[Int].alloc(1) + var ptr = UnsafePointer[Int].alloc(1) ptr[] = 42 assert_equal(ptr[], 42) ptr.free() @@ -355,7 +362,7 @@ def test_pointer_refitem_string(): def test_pointer_refitem_pair(): - var ptr = Pointer[Pair].alloc(1) + var ptr = UnsafePointer[Pair].alloc(1) ptr[].lo = 42 ptr[].hi = 24 # NOTE: We want to write the below but we can't implement a generic assert_equal yet. From 020bb131cb9cc71d3c2027567226e6c07a8ecc7e Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 5 Jul 2024 14:55:49 -0700 Subject: [PATCH 1141/2019] [stdlib] Merging changes required by interfaces to using UInt. Merging changes required by migrating user facing APIs to the new UInt type. MODULAR_ORIG_COMMIT_REV_ID: 1591b7d12ac2931c9a2c6bebb55f8697a6204c24 --- stdlib/src/builtin/simd.mojo | 50 ++++++++++++++++++++++++++++++++++ stdlib/src/builtin/uint.mojo | 50 +++++++++++++++++++++++++--------- stdlib/src/memory/unsafe.mojo | 51 +++++++++++++++++++++++++++++++++++ stdlib/src/utils/index.mojo | 27 +++++++++++++++++++ 4 files changed, 165 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 87147dfa8e..beed0ae888 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2738,6 +2738,31 @@ struct SIMD[type: DType, size: Int]( ptr.address.offset(offset).bitcast[SIMD[type, size]]().address ) + @staticmethod + @always_inline("nodebug") + fn load[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: UInt) -> Self: + """Loads the value the Pointer object points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + offset: The offset to load from. + + Returns: + The loaded value. + """ + return Self.load(ptr, Int(offset.value)) + @staticmethod @always_inline fn store[ @@ -2818,6 +2843,31 @@ struct SIMD[type: DType, size: Int]( val, ptr.address.bitcast[SIMD[type, size]]().address ) + @staticmethod + @always_inline("nodebug") + fn store[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space], offset: UInt, val: Self): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + offset: The offset to store to. + val: The value to store. + """ + Self.store[alignment=alignment, address_space=address_space]( + ptr.offset(offset.value), val + ) + # ===----------------------------------------------------------------------=== # # _pow diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 265c686e67..562603540a 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -64,6 +64,15 @@ struct UInt(Comparable, Formattable, Representable, Stringable): """ self.value = value + @always_inline("nodebug") + fn __init__(inout self, value: Int): + """Construct UInt from the given index value. + + Args: + value: The init value. + """ + self.value = value.value + @always_inline("nodebug") fn __init__(inout self, value: IntLiteral): """Construct UInt from the given IntLiteral value. @@ -618,36 +627,50 @@ struct UInt(Comparable, Formattable, Representable, Stringable): pred = __mlir_attr.`#index` ](self.value, rhs.value) + # TODO(rparolin): remove this before you submit this change @always_inline("nodebug") - fn __ge__(self, rhs: UInt) -> Bool: - """Return whether this UInt is greater than or equal to another. + fn __lt__(self, rhs: Int) -> Bool: + """Compare this Int to the RHS using LT comparison. Args: - rhs: The other UInt to compare against. + rhs: The other Int to compare against. Returns: - True if this UInt is greater than or equal to the other UInt and - False otherwise. + True if this Int is less-than the RHS Int and False otherwise. """ return __mlir_op.`index.cmp`[ - pred = __mlir_attr.`#index` + pred = __mlir_attr.`#index` ](self.value, rhs.value) @always_inline("nodebug") fn __le__(self, rhs: UInt) -> Bool: - """Return whether this UInt is less than or equal to another. + """Compare this Int to the RHS using LE comparison. Args: rhs: The other UInt to compare against. Returns: - True if this UInt is less than or equal to the other UInt and False - otherwise. + True if this Int is less-than the RHS Int and False otherwise. """ return __mlir_op.`index.cmp`[ pred = __mlir_attr.`#index` ](self.value, rhs.value) + @always_inline("nodebug") + fn __ge__(self, rhs: UInt) -> Bool: + """Return whether this UInt is greater than or equal to another. + + Args: + rhs: The other UInt to compare against. + + Returns: + True if this UInt is greater than or equal to the other UInt and + False otherwise. + """ + return __mlir_op.`index.cmp`[ + pred = __mlir_attr.`#index` + ](self.value, rhs.value) + @always_inline("nodebug") fn __bool__(self) -> Bool: """Convert this Int to Bool. @@ -741,15 +764,16 @@ struct UInt(Comparable, Formattable, Representable, Stringable): @always_inline("nodebug") fn __pos__(self) -> UInt: - """Return +self, which is the UInt value itself. + """Return +self. Returns: - The self value. + The +self value. """ return self fn format_to(self, inout writer: Formatter): - """Formats this integer to the provided formatter. + """ + Formats this integer to the provided formatter. Args: writer: The formatter to write to. @@ -760,7 +784,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): var err = _try_write_int(writer, UInt64(self)) if err: abort( - "unreachable: unexpected write Uint failure condition: " + "unreachable: unexpected write int failure condition: " + str(err.value()) ) else: diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 872be68a4b..fc732b8f21 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -418,6 +418,19 @@ struct LegacyPointer[ # Returns a new pointer shifted by the specified offset. return __mlir_op.`pop.offset`(self.address, int(idx).value) + @always_inline("nodebug") + fn offset(self, idx: UInt) -> Self: + """Returns a new pointer shifted by the specified offset. + + Args: + idx: The offset. + + Returns: + The new LegacyPointer shifted by the offset. + """ + # Returns a new pointer shifted by the specified offset. + return __mlir_op.`pop.offset`(self.address, idx.value) + @always_inline("nodebug") fn __add__[T: Intable](self, rhs: T) -> Self: """Returns a new pointer shifted by the specified offset. @@ -433,6 +446,18 @@ struct LegacyPointer[ """ return self.offset(rhs) + @always_inline("nodebug") + fn __add__(self, rhs: UInt) -> Self: + """Returns a new pointer shifted by the specified offset. + + Args: + rhs: The offset. + + Returns: + The new LegacyPointer shifted by the offset. + """ + return self.offset(rhs.value) + @always_inline("nodebug") fn __sub__[T: Intable](self, rhs: T) -> Self: """Returns a new pointer shifted back by the specified offset. @@ -624,6 +649,20 @@ struct DTypePointer[ """ return self.address[offset] + @always_inline("nodebug") + fn __getitem__( + self, offset: UInt = 0 + ) -> ref [MutableStaticLifetime, address_space._value.value] Scalar[type]: + """Enable subscript syntax `ptr[]` to access the element. + + Args: + offset: The offset to load from. + + Returns: + The reference for the Mojo compiler to use. + """ + return self.__getitem__(Int(offset.value)) + @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: """Returns True if the two pointers are equal. @@ -1036,3 +1075,15 @@ struct DTypePointer[ The new constructed DTypePointer. """ return self.address.offset(idx) + + @always_inline("nodebug") + fn offset(self, idx: UInt) -> Self: + """Returns a new pointer shifted by the specified offset. + + Args: + idx: The offset of the new pointer. + + Returns: + The new constructed DTypePointer. + """ + return self.address.offset(idx) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index adfc6ab277..386d59f83c 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -692,6 +692,19 @@ fn Index[T0: Intable](x: T0) -> StaticIntTuple[1]: return StaticIntTuple[1](int(x)) +@always_inline +fn Index(x: UInt) -> StaticIntTuple[1]: + """Constructs a 1-D Index from the given value. + + Args: + x: The initial value. + + Returns: + The constructed StaticIntTuple. + """ + return StaticIntTuple[1](x.value) + + @always_inline fn Index[T0: Intable, T1: Intable](x: T0, y: T1) -> StaticIntTuple[2]: """Constructs a 2-D Index from the given values. @@ -710,6 +723,20 @@ fn Index[T0: Intable, T1: Intable](x: T0, y: T1) -> StaticIntTuple[2]: return StaticIntTuple[2](int(x), int(y)) +@always_inline +fn Index(x: UInt, y: UInt) -> StaticIntTuple[2]: + """Constructs a 2-D Index from the given values. + + Args: + x: The 1st initial value. + y: The 2nd initial value. + + Returns: + The constructed StaticIntTuple. + """ + return StaticIntTuple[2](x.value, y.value) + + @always_inline fn Index[ T0: Intable, T1: Intable, T2: Intable From 39417c2344841374f8dd28e5bf0e2a0364b542d3 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 5 Jul 2024 15:53:50 -0700 Subject: [PATCH 1142/2019] [Stdlib] Do not inline the body of the debug_assert statement MODULAR_ORIG_COMMIT_REV_ID: ca9b9def22d712915157dd3c342e925891b4042e --- stdlib/src/builtin/debug_assert.mojo | 68 +++++++++---------- stdlib/src/collections/list.mojo | 10 ++- ...t_debug_assert_mojo_enable_assertions.mojo | 4 +- 3 files changed, 37 insertions(+), 45 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index e8be68feed..750a678a91 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -22,11 +22,17 @@ from sys._build import is_kernels_debug_build from builtin._location import __call_location, _SourceLocation +# Print an error and fail. +alias _ERROR_ON_ASSERT = is_kernels_debug_build() or is_defined[ + "MOJO_ENABLE_ASSERTIONS" +]() + +# Print a warning, but do not fail (useful for testing assert behavior). +alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() + @always_inline -fn debug_assert[ - *stringable: Stringable -](cond: Bool, *message_parts: *stringable): +fn debug_assert[stringable: Stringable](cond: Bool, message: stringable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -37,42 +43,30 @@ fn debug_assert[ for enabling assertions in the library. Parameters: - stringable: The type of the message parts. + stringable: The type of the message. Args: cond: The bool value to assert. - message_parts: The message parts to convert to `String` and concatenate - before displaying it on failure. + message: The message to convert to `String` before displaying it on failure. """ - # Print an error and fail. - alias err = is_kernels_debug_build() or is_defined[ - "MOJO_ENABLE_ASSERTIONS" - ]() - - # Print a warning, but do not fail (useful for testing assert behavior). - alias warn = is_defined["ASSERT_WARNING"]() - @parameter - if err or warn: - if not cond: - var full_message: String = "" - - @parameter - fn add_to_full_message[ - i: Int, StringableType: Stringable - ](msg: StringableType): - full_message += str(msg) - - message_parts.each_idx[add_to_full_message]() - - _debug_assert_msg[err](full_message, __call_location()) + if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: + if cond: + return + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( + message, __call_location() + ) @no_inline -fn _debug_assert_msg[err: Bool](msg: String, loc: _SourceLocation): +fn _debug_assert_msg[ + stringable: Stringable, //, *, is_warning: Bool = False +](msg: stringable, loc: _SourceLocation): """Aborts with (or prints) the given message and location. + This function is intentionally marked as no_inline to reduce binary size. + Note that it's important that this function doesn't get inlined; otherwise, an indirect recursion of @always_inline functions is possible (e.g. because abort's implementation could use debug_assert) @@ -83,14 +77,14 @@ fn _debug_assert_msg[err: Bool](msg: String, loc: _SourceLocation): # On GPUs, assert shouldn't allocate. @parameter - if err: - abort() - else: + if is_warning: print("Assert Warning") - return - - @parameter - if err: - abort(loc.prefix("Assert Error: " + msg)) + else: + abort() else: - print(loc.prefix("Assert Warning:"), msg) + + @parameter + if is_warning: + print(loc.prefix("Assert Warning:"), str(msg)) + else: + abort(loc.prefix("Assert Error: " + str(msg))) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index cce0d1bedd..dc7f7851dc 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -756,12 +756,10 @@ struct List[T: CollectionElement]( """ debug_assert( 0 <= idx < len(self), - "The index provided must be within the range [0,", - len(self), - "[", - " when using List.unsafe_get(). But you provided: ", - idx, - ".", + ( + "The index provided must be within the range [0, len(List) -1]" + " when using List.unsafe_get()" + ), ) return (self.data + idx)[] diff --git a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo index 206e119938..8e6ce41119 100644 --- a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo +++ b/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo @@ -21,7 +21,7 @@ # CHECK-LABEL: test_fail fn main(): print("== test_fail") - # CHECK: Assert Error: fail,1,some string - debug_assert(False, "fail,", 1, String(",some string")) + # CHECK: Assert Error: this fails + debug_assert(False, "this fails") # CHECK-NOT: is never reached print("is never reached") From 70f117d2df0e8d9889ef8a70573b033b53a75903 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 5 Jul 2024 15:57:03 -0700 Subject: [PATCH 1143/2019] [stdlib] UInt.MAX calculated without explicitly casting to UInt MODULAR_ORIG_COMMIT_REV_ID: 421dc0bae85df08859c2d686535e9fec96519f56 --- stdlib/src/builtin/uint.mojo | 2 +- stdlib/test/builtin/test_uint.mojo | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 562603540a..23ecf2847a 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -33,7 +33,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): `UInt8`, `UInt16`, `UInt32`, or `UInt64`. """ - alias MAX: UInt = (UInt(2) ** bitwidthof[DType.index]()) - UInt(1) + alias MAX: UInt = (1 << bitwidthof[DType.index]()) - 1 """Returns the maximum integer value.""" alias MIN: UInt = 0 diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 925729600c..1aad4c141c 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -93,9 +93,9 @@ def test_inequality(): def test_properties(): assert_equal(UInt.MIN, UInt(0)) if bitwidthof[DType.index]() == 32: - assert_equal(UInt.MAX, UInt(((2**32) - 1).value)) + assert_equal(UInt.MAX, (1 << 32) - 1) else: - assert_equal(UInt.MAX, UInt(((2**64) - 1).value)) + assert_equal(UInt.MAX, (1 << 64) - 1) def test_add(): From 77895bc3eca4cba232241f27546e28d2decb6ce2 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 16:41:36 -0700 Subject: [PATCH 1144/2019] [mojo-stdlib] Remove more `@always_inline` MODULAR_ORIG_COMMIT_REV_ID: 25eecf71a6ca7e99e6605b64c8431b1b451bc204 --- stdlib/src/builtin/string.mojo | 2 -- stdlib/src/collections/list.mojo | 7 ------- 2 files changed, 9 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index e0eaaf366c..21bf6bf690 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -591,7 +591,6 @@ fn _is_ascii_lowercase(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # -@always_inline fn _isspace(c: String) -> Bool: """Determines whether the given character is a whitespace character. @@ -1282,7 +1281,6 @@ struct String( """ return len(self) > 0 - @always_inline fn __len__(self) -> Int: """Gets the string length, in bytes. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index dc7f7851dc..26c365e920 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -224,7 +224,6 @@ struct List[T: CollectionElement]( return True return False - @always_inline fn __mul__(self, x: Int) -> Self: """Multiplies the list by x and returns a new list. @@ -241,7 +240,6 @@ struct List[T: CollectionElement]( result.__mul(x) return result^ - @always_inline fn __imul__(inout self, x: Int): """Multiplies the list by x in place. @@ -250,7 +248,6 @@ struct List[T: CollectionElement]( """ self.__mul(x) - @always_inline fn __add__(self, owned other: Self) -> Self: """Concatenates self with other and returns the result as a new list. @@ -264,7 +261,6 @@ struct List[T: CollectionElement]( result.extend(other^) return result^ - @always_inline fn __iadd__(inout self, owned other: Self): """Appends the elements of other into self. @@ -397,7 +393,6 @@ struct List[T: CollectionElement]( self.data = new_data self.capacity = new_capacity - @always_inline fn append(inout self, owned value: T): """Appends a value to this list. @@ -521,7 +516,6 @@ struct List[T: CollectionElement]( self._realloc(self.capacity // 2) return ret_val^ - @always_inline fn reserve(inout self, new_capacity: Int): """Reserves the requested capacity. @@ -695,7 +689,6 @@ struct List[T: CollectionElement]( return res^ - @always_inline fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T: """Gets the list element at the given index. From 7ad400aae66874ebf2be05befc029315e081e7c8 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 5 Jul 2024 20:21:59 -0700 Subject: [PATCH 1145/2019] [mojo-stdlib] `free` is tolerant to null pointers Leverage this fact to not generate a branch in `List.__del__`. MODULAR_ORIG_COMMIT_REV_ID: 9df10bb2555c06ffd33635ef35712809a74fdb51 --- stdlib/src/collections/list.mojo | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 26c365e920..377b47ecdb 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -191,8 +191,7 @@ struct List[T: CollectionElement]( """Destroy all elements in the list and free its memory.""" for i in range(self.size): (self.data + i).destroy_pointee() - if self.data: - self.data.free() + self.data.free() # ===-------------------------------------------------------------------===# # Operator dunders From 6e67dc416fa5259b2d9567ac9f5f59cf6807c9d9 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sat, 6 Jul 2024 10:14:30 -0700 Subject: [PATCH 1146/2019] [stdlib] Verify List[T] element destructors are being called for non-trivial types. When debugging another issue, I discovered that commenting out the code triggering each element contained within the List destructor would still successfully pass the stdlib test suite. ``` fn __del__(owned self): """Destroy all elements in the list and free its memory.""" # for i in range(self.size): # (self.data + i).destroy_pointee() self.data.free() ``` This change introduces a unit test that verifies that element destructors are called upon List destruction. MODULAR_ORIG_COMMIT_REV_ID: b1b3e35838af2ae68d234580798036be4bbb413c --- stdlib/test/collections/test_list.mojo | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 6f5a8a7fb7..5dd85fa296 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -832,6 +832,55 @@ def test_indexing(): assert_equal(l[2], 3) +# ===-------------------------------------------------------------------===# +# List dtor tests +# ===-------------------------------------------------------------------===# +var g_dtor_count: Int = 0 + + +struct DtorCounter(CollectionElement): + # NOTE: payload is required because List does not support zero sized structs. + var payload: Int + + fn __init__(inout self): + self.payload = 0 + + fn __copyinit__(inout self, existing: Self, /): + self.payload = existing.payload + + fn __moveinit__(inout self, owned existing: Self, /): + self.payload = existing.payload + existing.payload = 0 + + fn __del__(owned self): + g_dtor_count += 1 + + +def inner_test_list_dtor(): + # explicity reset global counter + g_dtor_count = 0 + + var l = List[DtorCounter]() + assert_equal(g_dtor_count, 0) + + l.append(DtorCounter()) + assert_equal(g_dtor_count, 0) + + l.__del__() + assert_equal(g_dtor_count, 1) + + +def test_list_dtor(): + # call another function to force the destruction of the list + inner_test_list_dtor() + + # verify we still only ran the destructor once + assert_equal(g_dtor_count, 1) + + +# ===-------------------------------------------------------------------===# +# main +# ===-------------------------------------------------------------------===# def main(): test_mojo_issue_698() test_list() @@ -865,3 +914,4 @@ def main(): test_list_mult() test_list_contains() test_indexing() + test_list_dtor() From 33c474b5d350e916027a11d8eeff73d67a1201b0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Jul 2024 15:58:34 -0700 Subject: [PATCH 1147/2019] [mojo-lang] Remove a few references to `__refitem__` NFC. This got removed some time ago, this is just a cleanup. MODULAR_ORIG_COMMIT_REV_ID: 8a4f293feb914d0586bab87feb9a090912f7da28 --- stdlib/docs/style-guide.md | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index d743a0171c..43de2f4bad 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -156,7 +156,6 @@ struct MyStruct(Sized, Stringable): fn __getitem__ fn __getitem__ - fn __refitem__ fn __add__ fn __iadd__ From 44e78d54085f760e5486d2ea2fd1333cac0c4f13 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Jul 2024 20:47:27 -0700 Subject: [PATCH 1148/2019] [mojo-lang] Fix longstanding problem resolving static methods. (#42991) Mojo allows you to call a static method on a struct with either `T.method()` or `someT.method()` where the later is supposed to drop the value (like Python does) when the method doesn't take it. We had a long standing problem that prevented us from overloading methods on static-ness, which was due to how and when we resolved this bit. Overload resolution was mostly ready for this, but we didn't propagate it out of overload resolution. Fix this by having resolveOverloadSet mutate the operand list when the self operand is being intentionally ignored, making it so call emission then does this right thing. This resolves a long standing fixme in `AttributeRefNode::emitIR`. MODULAR_ORIG_COMMIT_REV_ID: 0000719063304ddb7a3c75b93700eb43be603f1d --- stdlib/src/builtin/io.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 788253ad03..54ba79d319 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -389,7 +389,7 @@ fn print[ writer.write(value) @parameter - if i < values.__len__() - 1: + if i < len(VariadicList(Ts)) - 1: writer.write(sep) values.each_idx[print_with_separator]() From 8a5d0a5c5041ce5f3ea8765d6c02a6eb9d236303 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Jul 2024 23:55:39 -0700 Subject: [PATCH 1149/2019] [mojo-lang] Improve parameter inference for conditional conformances. This fixes parameter inference to handle more complex conditional conformance cases than just "x.method(" notably including cases that use binary operators `x == y` and things like `T.method(a, b)` as well. When defining a function like: ``` struct Optional[T: CollectionElement]: fn __eq__[U: ComparableCollectionElement](self: Optional[U], rhs: Optional[U]) -> Bool: ``` the "eq" method gets two parameters: T and U. The U parameter is inferrable from the self value naturally, but the T parameter isn't used anywhere in the signature list. This patch adds special handling to infer these parameters when on a method of a struct, because we know that 'self' should correspond to a value that is (perhaps refined) convertable to Self. It would be better to get away from this repr of conditional conformances someday by adding a 'requires' clause, but this is a logical step forward and will enable us to make `Optional` comparable. MODULAR_ORIG_COMMIT_REV_ID: 664a0ef0f2a0d9aa792e6be46021fcc916b0e8c0 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 961c586dee..d4db170575 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -79,6 +79,8 @@ what we publish. b.needs_move(NonMovable()) ``` + Conditional conformance works with dunder methods and other things as well. + - `async` functions now support memory-only results (like `String`, `List`, etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means From b6bcaee3a11f00b1c7cdd9a7f080bfac3ea7692e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 7 Jul 2024 12:44:05 -0700 Subject: [PATCH 1150/2019] [mojo-lang] Rework setitem/setattr emission to use keyword arguments. This changes setitem emission to pass the new value using they keyword argument of the selected setitem instead of passing it as a positional argument. This enables it to work with variadic setitem methods. This fixes Mojo Issue #248 MODULAR_ORIG_COMMIT_REV_ID: 2ad836b8bd1466dbdb881e14491b8a6205a08b79 --- docs/changelog.md | 12 ++++++++++++ stdlib/src/python/object.mojo | 13 +++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d4db170575..b476df54cc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,18 @@ what we publish. ### ⭐️ New +- `__setitem__` now works with variadic argument lists such as: + + ```mojo + struct YourType: + fn __setitem__(inout self, *indices: Int, val: Int): ... + ``` + + The Mojo compiler now always passes the "new value" being set using the last + keyword argument of the `__setitem__`, e.g. turning `yourType[1, 2] = 3` into + `yourType.__setitem__(1, 2, val=3)`. This fixes + [Issue #248](https://github.com/modularml/mojo/issues/248). + - The pointer variants (`DTypePointer`, `UnsafePointer`, etc.) now have a new `exclusive: Bool = False` parameter. Setting this parameter to true tells the compiler that the user knows this pointer and all those derived from it have diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 907073ddf2..fb5d7dc13e 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -482,17 +482,17 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return PythonObject(result) - fn __setitem__(inout self, *args: PythonObject) raises: + fn __setitem__(inout self, *args: PythonObject, value: PythonObject) raises: """Set the value with the given key or keys. Args: - args: The key or keys to set on this object, followed by the value. + args: The key or keys to set on this object. + value: The value to set. """ var size = len(args) - debug_assert(size > 0, "must provide at least a value to __setitem__") var cpython = _get_global_python_itf().cpython() - var tuple_obj = cpython.PyTuple_New(size) + var tuple_obj = cpython.PyTuple_New(size + 1) for i in range(size): var arg_value = args[i].py_object cpython.Py_IncRef(arg_value) @@ -500,6 +500,11 @@ struct PythonObject( if result != 0: raise Error("internal error: PyTuple_SetItem failed") + cpython.Py_IncRef(value.py_object) + var result2 = cpython.PyTuple_SetItem(tuple_obj, size, value.py_object) + if result2 != 0: + raise Error("internal error: PyTuple_SetItem failed") + var callable_obj = cpython.PyObject_GetAttrString( self.py_object, "__setitem__" ) From 21ccd7762bc7e913ceeba77da015e6d75bcfe316 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:42:00 -0400 Subject: [PATCH 1151/2019] [stdlib] Clean up uses of `memcmp` fix imports and remove `LegacyPointer`-taking versino of `memcmp`. MODULAR_ORIG_COMMIT_REV_ID: 2868da83bb53494f680ce0d2b53ac893a336f5af --- docs/changelog.md | 2 + .../benchmarks/algorithm/bench_vectorize.mojo | 1 - stdlib/src/builtin/error.mojo | 2 +- stdlib/src/memory/memory.mojo | 37 ------------------- 4 files changed, 3 insertions(+), 39 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b476df54cc..ee7a63b884 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -413,6 +413,8 @@ what we publish. - `LegacyPointer.load/store` are now removed. It's use is replaced with `__getitem__` or `__setitem__`. +- `memcmp` no longer takes in `LegacyPointer`, instead, use `UnsafePointer`. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index b50b4c9458..9a2bbe3cff 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -33,7 +33,6 @@ from benchmark import ( run, ) from buffer import Buffer -from memory import memcmp from memory.unsafe import DTypePointer diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 59b05566bf..f80f276378 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import alignof, sizeof -from memory import UnsafePointer, memcmp, memcpy +from memory import UnsafePointer, memcpy from memory.memory import _free # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index b4090d6427..313f37280e 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -127,43 +127,6 @@ fn memcmp(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: return _memcmp_impl(s1, s2, count) -@always_inline -fn memcmp[ - type: AnyTrivialRegType, address_space: AddressSpace -]( - s1: LegacyPointer[type, address_space], - s2: LegacyPointer[type, address_space], - count: Int, -) -> Int: - """Compares two buffers. Both strings are assumed to be of the same length. - - Parameters: - type: The element type. - address_space: The address space of the pointer. - - Args: - s1: The first buffer address. - s2: The second buffer address. - count: The number of elements in the buffers. - - Returns: - Returns 0 if the bytes strings are identical, 1 if s1 > s2, and -1 if - s1 < s2. The comparison is performed by the first different byte in the - byte strings. - """ - var byte_count = count * sizeof[type]() - - @parameter - if sizeof[type]() >= sizeof[DType.int32](): - var ds1 = DTypePointer[DType.int32, address_space](s1.bitcast[Int32]()) - var ds2 = DTypePointer[DType.int32, address_space](s2.bitcast[Int32]()) - return _memcmp_impl(ds1, ds2, byte_count // sizeof[DType.int32]()) - - var ds1 = DTypePointer[DType.int8, address_space](s1.bitcast[Int8]()) - var ds2 = DTypePointer[DType.int8, address_space](s2.bitcast[Int8]()) - return _memcmp_impl(ds1, ds2, byte_count) - - @always_inline fn memcmp[ type: AnyType, address_space: AddressSpace From 9ea4757173671bfd65af3c2a8f88769e3acf3ba6 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:42:31 -0400 Subject: [PATCH 1152/2019] [stdlib] Add `_heap_sort` and benchmark against `sort` Then benchmarks were ran on Apple M3. From #42556 we see that `sort` performs extra bad when sorting floats. When running on size $2^{12}$ random lists of floats, `sort` takes 30x longer than `_heap_sort` ``` "bench_std_sort_random_list_4096_type_float16",23.704722772277229,101 "bench_std_sort_random_list_4096_type_float16",23.153861386138612,101 "bench_std_sort_random_list_4096_type_float16",22.338534653465345,101 "bench_heap_sort_random_list_4096_type_float16",0.077990831696136215,1527 "bench_heap_sort_random_list_4096_type_float16",0.078081592689295029,1532 "bench_heap_sort_random_list_4096_type_float16",0.078111038536903987,1531 ``` When sorting ints, `sort` takes up to 6x longer than `_heap_sort`: ``` "bench_std_sort_random_list_4096_type_uint8",0.109719,1000 "bench_std_sort_random_list_4096_type_uint8",0.12102800000000001,1000 "bench_std_sort_random_list_4096_type_uint8",0.114569,1000 "bench_heap_sort_random_list_4096_type_uint8",0.085964157706093197,1395 "bench_heap_sort_random_list_4096_type_uint8",0.088480783176214647,1379 "bench_heap_sort_random_list_4096_type_uint8",0.086336219336219336,1386 "bench_std_sort_random_list_65536_type_uint8",12.534207920792079,101 "bench_std_sort_random_list_65536_type_uint8",11.537118811881188,101 "bench_std_sort_random_list_65536_type_uint8",11.488762376237624,101 "bench_heap_sort_random_list_65536_type_uint8",1.9294752475247525,101 "bench_heap_sort_random_list_65536_type_uint8",1.9696039603960396,101 "bench_heap_sort_random_list_65536_type_uint8",1.9374257425742576,101 ``` MODULAR_ORIG_COMMIT_REV_ID: 82e67cbd3b883b88563e8bb4e52a49340296d8ed --- stdlib/benchmarks/builtin/bench_sort.mojo | 72 ++++++++++++++++++++++- stdlib/src/builtin/sort.mojo | 31 ++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index bdb9ebb72d..2d7bbcd3c6 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -15,7 +15,7 @@ from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from random import * -from stdlib.builtin.sort import sort, _small_sort, _insertion_sort +from stdlib.builtin.sort import sort, _small_sort, _insertion_sort, _heap_sort # ===----------------------------------------------------------------------===# # Benchmark Utils @@ -223,6 +223,75 @@ fn bench_small_list_sort(inout m: Bench) raises: _ = list^ +# ===----------------------------------------------------------------------===# +# Benchmark sort functions with a large list size +# ===----------------------------------------------------------------------===# + + +@parameter +fn bench_large_list_sort(inout m: Bench) raises: + alias counts = List(1 << 12, 1 << 16) + + @parameter + for type_index in range(len(dtypes)): + alias dt = dtypes[type_index] + + @parameter + for count_index in range(len(counts)): + alias count = counts[count_index] + var list = random_scalar_list[dt](count) + + @parameter + fn bench_sort_list(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + sort(l1) + + b.iter[call_fn]() + + @parameter + fn bench_heap_sort(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + var ptr = rebind[Pointer[Scalar[dt]]](l1.data) + + @parameter + fn _less_than_equal[ + ty: AnyTrivialRegType + ](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( + rhs + ) + + _heap_sort[Scalar[dt], _less_than_equal](ptr, count) + _ = l1 + + b.iter[call_fn]() + + m.bench_function[bench_sort_list]( + BenchId( + "bench_std_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + + m.bench_function[bench_heap_sort]( + BenchId( + "bench_heap_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + _ = list^ + + # ===----------------------------------------------------------------------===# # Benchmark Main # ===----------------------------------------------------------------------===# @@ -234,5 +303,6 @@ def main(): bench_tiny_list_sort(m) bench_small_list_sort(m) + bench_large_list_sort(m) m.dump_report() diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 402d68c6a4..eebe1d0d0c 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -68,6 +68,37 @@ fn _insertion_sort[ array[j] = value +@always_inline +fn _heap_sort_fix_down[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], size: Int, idx: Int): + var i = idx + var j = i * 2 + 1 + while j < size: # has left child + # if right child exist and has higher value, swap with right + if i * 2 + 2 < size and cmp_fn(array[j], array[i * 2 + 2]): + j = i * 2 + 2 + if not cmp_fn(array[i], array[j]): + return + swap(array[j], array[i]) + i = j + j = i * 2 + 1 + + +@always_inline +fn _heap_sort[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], owned size: Int): + # heapify + for i in range(size // 2 - 1, -1, -1): + _heap_sort_fix_down[type, cmp_fn](array, size, i) + # sort + while size > 1: + size -= 1 + swap(array[0], array[size]) + _heap_sort_fix_down[type, cmp_fn](array, size, 0) + + @always_inline fn _partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type From b0edc6547b1a395e496383035a420ec13b1a36e2 Mon Sep 17 00:00:00 2001 From: Katherine Wu <31663267+k-w-w@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:52:02 -0700 Subject: [PATCH 1153/2019] Argument order was incorrect in the doc example, this has been fixed. MODULAR_ORIG_COMMIT_REV_ID: 13f467b53397b89f598aafa4c608dab6c5d8216a --- stdlib/src/builtin/file.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 143ba314df..32132cad13 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -374,7 +374,7 @@ struct FileHandle: ```mojo import os var f = open("/tmp/example.txt", "r") - f.seek(os.SEEK_CUR, 32) + f.seek(32, os.SEEK_CUR) ``` Start from 32 bytes from the end of the file: @@ -382,7 +382,7 @@ struct FileHandle: ```mojo import os var f = open("/tmp/example.txt", "r") - f.seek(os.SEEK_END, -32) + f.seek(-32, os.SEEK_END) ``` . """ From a2c71d44cc35bf062ad3e0b888d4f035d2bf567a Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 8 Jul 2024 13:04:35 -0500 Subject: [PATCH 1154/2019] [stdlib] Mark `List.unsafe_set` as `inout` `List.unsafe_set` should take `self` as an `inout` argument so it's not mutating a borrowed list. MODULAR_ORIG_COMMIT_REV_ID: 97a7603183691b6d3cc2591edab2a3f8096da88f --- stdlib/src/collections/list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 377b47ecdb..446502ddf2 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -756,7 +756,7 @@ struct List[T: CollectionElement]( return (self.data + idx)[] @always_inline - fn unsafe_set(self, idx: Int, owned value: T): + fn unsafe_set(inout self, idx: Int, owned value: T): """Write a value to a given location without checking index bounds. Users should consider using `my_list[idx] = value` instead of this method as it From e0af3bf0d9349dbdf334ad43c996efbe66bbcaf5 Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Mon, 8 Jul 2024 13:43:11 -0500 Subject: [PATCH 1155/2019] [External] [docs] Fix typos in changelog.md (#43032) [External] [docs] Fix typos in changelog.md - Change "types" to "type's" - Change "have" to "has" Co-authored-by: Brian M Johnson Closes modularml/mojo#3191 MODULAR_ORIG_COMMIT_REV_ID: b327d937de4403b89c8feebf2be7e89bc6e275fa --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ee7a63b884..98a88ad091 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -338,7 +338,7 @@ what we publish. In the example above, `String.format_sequence()` is used to construct a `String` from a type that implements `Formattable`. This pattern of - implementing a types `Stringable` implementation in terms of its `Formattable` + implementing a type's `Stringable` implementation in terms of its `Formattable` implementation minimizes boilerplate and duplicated code, while retaining backwards compatibility with the requirements of the commonly used `str(..)` function. @@ -398,7 +398,7 @@ what we publish. - The rank argument for `algorihtm.elementwise` is no longer required and is only inferred. -- The `ulp` function in `numerics` have been moved to the `math` module. +- The `ulp` function in `numerics` has been moved to the `math` module. - The Mojo Language Server no longer sets `.` as a commit character for auto-completion. From 61665ba6be996ac4ac3af41e13d3ff55db343bd5 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:10:17 -0400 Subject: [PATCH 1156/2019] Revert "[stdlib] Add `_heap_sort` and benchmark against `sort`" Reverts modularml/modular#42864 MODULAR_ORIG_COMMIT_REV_ID: 9b7862ed08e91b8ba33002166c61b93b9252d4ca --- stdlib/benchmarks/builtin/bench_sort.mojo | 72 +---------------------- stdlib/src/builtin/sort.mojo | 31 ---------- 2 files changed, 1 insertion(+), 102 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 2d7bbcd3c6..bdb9ebb72d 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -15,7 +15,7 @@ from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from random import * -from stdlib.builtin.sort import sort, _small_sort, _insertion_sort, _heap_sort +from stdlib.builtin.sort import sort, _small_sort, _insertion_sort # ===----------------------------------------------------------------------===# # Benchmark Utils @@ -223,75 +223,6 @@ fn bench_small_list_sort(inout m: Bench) raises: _ = list^ -# ===----------------------------------------------------------------------===# -# Benchmark sort functions with a large list size -# ===----------------------------------------------------------------------===# - - -@parameter -fn bench_large_list_sort(inout m: Bench) raises: - alias counts = List(1 << 12, 1 << 16) - - @parameter - for type_index in range(len(dtypes)): - alias dt = dtypes[type_index] - - @parameter - for count_index in range(len(counts)): - alias count = counts[count_index] - var list = random_scalar_list[dt](count) - - @parameter - fn bench_sort_list(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - sort(l1) - - b.iter[call_fn]() - - @parameter - fn bench_heap_sort(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - var ptr = rebind[Pointer[Scalar[dt]]](l1.data) - - @parameter - fn _less_than_equal[ - ty: AnyTrivialRegType - ](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( - rhs - ) - - _heap_sort[Scalar[dt], _less_than_equal](ptr, count) - _ = l1 - - b.iter[call_fn]() - - m.bench_function[bench_sort_list]( - BenchId( - "bench_std_sort_random_list_" - + str(count) - + "_type_" - + str(dt) - ) - ) - - m.bench_function[bench_heap_sort]( - BenchId( - "bench_heap_sort_random_list_" - + str(count) - + "_type_" - + str(dt) - ) - ) - _ = list^ - - # ===----------------------------------------------------------------------===# # Benchmark Main # ===----------------------------------------------------------------------===# @@ -303,6 +234,5 @@ def main(): bench_tiny_list_sort(m) bench_small_list_sort(m) - bench_large_list_sort(m) m.dump_report() diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index eebe1d0d0c..402d68c6a4 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -68,37 +68,6 @@ fn _insertion_sort[ array[j] = value -@always_inline -fn _heap_sort_fix_down[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int, idx: Int): - var i = idx - var j = i * 2 + 1 - while j < size: # has left child - # if right child exist and has higher value, swap with right - if i * 2 + 2 < size and cmp_fn(array[j], array[i * 2 + 2]): - j = i * 2 + 2 - if not cmp_fn(array[i], array[j]): - return - swap(array[j], array[i]) - i = j - j = i * 2 + 1 - - -@always_inline -fn _heap_sort[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], owned size: Int): - # heapify - for i in range(size // 2 - 1, -1, -1): - _heap_sort_fix_down[type, cmp_fn](array, size, i) - # sort - while size > 1: - size -= 1 - swap(array[0], array[size]) - _heap_sort_fix_down[type, cmp_fn](array, size, 0) - - @always_inline fn _partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type From b358e4f7da5f2f0d6279de5f03b96be369d1f0e2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Jul 2024 12:32:44 -0700 Subject: [PATCH 1157/2019] [mojo-stdlib] Make `Optional` comparable with `==` and `!=` This takes advantage of recent improvements in the compiler to implement `==` and `!=` on Optional when the underlying element type is equatable. This also adds a new `EqualityComparableCollectionElement` trait to support this. MODULAR_ORIG_COMMIT_REV_ID: 97670b2158151c23f9c1229002130be7012906e3 --- docs/changelog.md | 3 ++ stdlib/src/builtin/builtin_slice.mojo | 2 ++ stdlib/src/builtin/value.mojo | 24 ++++++++++--- stdlib/src/collections/optional.mojo | 40 ++++++++++++++++++++++ stdlib/test/collections/test_optional.mojo | 15 +++++++- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 98a88ad091..66c772d1a0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -34,6 +34,9 @@ what we publish. exclusive access to the underlying memory allocation. The compiler is not guaranteed to do anything with this information. +- `Optional` values are now equality comparable with `==` and `!=` when their + element type is equality comparable. + - Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) dictionary-like type, matching most of the features of the Python one. ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index a9c52fbd4b..28733d7779 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -19,6 +19,7 @@ from collections import OptionalReg from sys.intrinsics import _mlirtype_is_eq +# TODO: When Slice switches to Optional, just use == instead of this function. @always_inline("nodebug") fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: if x and y: @@ -131,6 +132,7 @@ struct Slice(Stringable, EqualityComparable, Representable, Formattable): True if start, end, and step values of this slice match the corresponding values of the other slice and False otherwise. """ + # TODO: When Slice switches to Optional, just use ==. return ( _compare_optional(self.start, other.start) and _compare_optional(self.end, other.end) diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index f230694289..2ead21c4dd 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -221,13 +221,27 @@ trait StringableCollectionElement(CollectionElement, Stringable): pass +trait EqualityComparableCollectionElement( + CollectionElement, EqualityComparable +): + """ + This trait denotes a trait composition of the `CollectionElement` and `EqualityComparable` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `CollectionElement & EqualityComparable` in the parameter. + """ + + pass + + trait ComparableCollectionElement(CollectionElement, Comparable): """ - This trait is a temporary solution to enable comparison of - collection elements as utilized in the `index` and `count` methods of - a list. - This approach will be revised with the introduction of conditional trait - conformances. + This trait denotes a trait composition of the `CollectionElement` and `Comparable` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `CollectionElement & Comparable` in the parameter. """ pass diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 2b0bcb2abc..523592c7b6 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -145,6 +145,46 @@ struct Optional[T: CollectionElement]( """ return self.__bool__() + fn __eq__[ + T: EqualityComparableCollectionElement + ](self: Optional[T], rhs: Optional[T]) -> Bool: + """Return `True` if this is the same as another optional value, meaning + both are absent, or both are present and have the same underlying value. + + Parameters: + T: The type of the elements in the list. Must implement the + traits `CollectionElement` and `EqualityComparable`. + + Args: + rhs: The value to compare to. + + Returns: + True if the values are the same. + """ + if self: + if rhs: + return self.value() == rhs.value() + return False + return not rhs + + fn __ne__[ + T: EqualityComparableCollectionElement + ](self: Optional[T], rhs: Optional[T]) -> Bool: + """Return `False` if this is the same as another optional value, meaning + both are absent, or both are present and have the same underlying value. + + Parameters: + T: The type of the elements in the list. Must implement the + traits `CollectionElement` and `EqualityComparable`. + + Args: + rhs: The value to compare to. + + Returns: + False if the values are the same. + """ + return not (self == rhs) + # ===-------------------------------------------------------------------===# # Trait implementations # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 1606aee680..70cca5de6f 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -14,7 +14,7 @@ from collections import Optional, OptionalReg -from testing import assert_equal, assert_false, assert_true +from testing import * def test_basic(): @@ -145,6 +145,18 @@ def test_optional_str_repr(): assert_equal(Optional[Int](None).__repr__(), "Optional(None)") +def test_optional_equality(): + o = Optional(10) + n = Optional[Int]() + assert_true(o == 10) + assert_true(o != 11) + assert_true(o != n) + assert_true(o != None) + assert_true(n != 11) + assert_true(n == n) + assert_true(n == None) + + def main(): test_basic() test_optional_reg_basic() @@ -155,3 +167,4 @@ def main(): test_optional_take_mutates() test_optional_explicit_copy() test_optional_str_repr() + test_optional_equality() From 7aa7d02d738a785839391818e6da745b80ddb2c9 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Mon, 8 Jul 2024 22:53:10 +0300 Subject: [PATCH 1158/2019] [stdlib] Fix crash when `**kwargs` has no type annotation in `def` MODULAR_ORIG_COMMIT_REV_ID: 96af591cf856b57ae976c1a19d6c57dec3e344ac --- docs/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 66c772d1a0..1c9e571a3c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -444,3 +444,10 @@ what we publish. ### 🛠️ Fixed - Fixed a crash in the Mojo Language Server when importing the current file. + +- Fixed crash when specifying variadic keyword arguments without a type + expression in `def` functions, e.g.: + + ```mojo + def foo(**kwargs): ... # now works + ``` From 9f2540c4c80e9f3a0127a16b4b7135de035f18d0 Mon Sep 17 00:00:00 2001 From: Brian M Johnson Date: Mon, 8 Jul 2024 15:24:05 -0500 Subject: [PATCH 1159/2019] [External] [docs] Fix typo in changelog.md (#43048) [External] [docs] Fix typo in changelog.md Change "complexify" to "complicate" Co-authored-by: Brian M Johnson Closes modularml/mojo#3187 MODULAR_ORIG_COMMIT_REV_ID: a863810494154c260f8e5606f9a4a0133f0c3c55 --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1c9e571a3c..7c362d7a7a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -277,7 +277,7 @@ what we publish. - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a C-like set of semantics around pointer aliasing and derivation. However, the C semantics bring a lot of history and baggage that are not needed in Mojo and - which complexify compiler optimizations. The language overall provides a + which complicate compiler optimizations. The language overall provides a stronger set of invariants around pointer aliasing with lifetimes and exclusive mutable references to values, etc. From a89b485d75324432e282ce8ff874fc45ea27fb92 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Jul 2024 13:46:01 -0700 Subject: [PATCH 1160/2019] [mojo-docs] Add a couple things to the changelog MODULAR_ORIG_COMMIT_REV_ID: 19edaaf27502027e3a5627ce39a2c0041859e9d1 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7c362d7a7a..3d7e8acef2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -451,3 +451,8 @@ what we publish. ```mojo def foo(**kwargs): ... # now works ``` + +- [#3142](https://github.com/modularml/mojo/issues/3142) - [QoI] Confusing + `__setitem__` method is failing with a "must be mutable" error. +- [#248](https://github.com/modularml/mojo/issues/248) - [Feature] Enable + `__setitem__` to take variadic arguments From 3f237d26125e91f60ce2cda0637304a4b695d9c9 Mon Sep 17 00:00:00 2001 From: Taylor Date: Mon, 8 Jul 2024 15:46:30 -0500 Subject: [PATCH 1161/2019] [External] [Documentation] Fix typo in arc.mojo (#43054) [External] [Documentation] Fix typo in arc.mojo trough -> through Co-authored-by: Taylor Closes modularml/mojo#3192 MODULAR_ORIG_COMMIT_REV_ID: 0d8654dadd68b8b802aceab4f9abf522617230ec --- stdlib/src/memory/arc.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 0aefb67801..6e2c2a64a3 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -25,7 +25,7 @@ print(3 == p[]) Subscripting(`[]`) is done by `Reference`, in order to ensure that the underlying `Arc` outlive the operation. -It is highly DISCOURAGED to manipulate an `Arc` trough `UnsafePointer`. +It is highly DISCOURAGED to manipulate an `Arc` through `UnsafePointer`. Mojo's ASAP deletion policy ensure values are destroyed at last use. Do not unsafely dereference the `Arc` inner `UnsafePointer` field. See [Lifecycle](https://docs.modular.com/mojo/manual/lifecycle/). From 78842e094c7c5eb94a9df2a1dc2719217e8620dd Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 8 Jul 2024 16:03:39 -0500 Subject: [PATCH 1162/2019] [stdlib] Remove `Intable` from `StaticTuple.__getitem__` As mentioned at https://github.com/modularml/mojo/issues/2337#issuecomment-2212522543, we've pivoted a bit and moved away from `Intable` for offsets in `__getitem__` implementations now that we have `Indexer`. Fix this last use of `__getitem__` that works on a `Intable` type to use `Int` explicitly. MODULAR_ORIG_COMMIT_REV_ID: 87e2eca61466072aa0e36741296cb1d8fbc0eaed --- stdlib/src/utils/static_tuple.mojo | 12 ++++-------- stdlib/test/utils/test_tuple.mojo | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index d4974c1329..bd034a5ab6 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -193,25 +193,21 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): self = tmp @always_inline("nodebug") - fn __getitem__[intable: Intable](self, index: intable) -> Self.element_type: + fn __getitem__(self, idx: Int) -> Self.element_type: """Returns the value of the tuple at the given dynamic index. - Parameters: - intable: The intable type. - Args: - index: The index into the tuple. + idx: The index into the tuple. Returns: The value at the specified position. """ - var offset = int(index) - debug_assert(offset < size, "index must be within bounds") + debug_assert(idx < size, "index must be within bounds") # Copy the array so we can get its address, because we can't take the # address of 'self' in a non-mutating method. var arrayCopy = self.array var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(arrayCopy).address, offset.value + UnsafePointer.address_of(arrayCopy).address, idx.value ) var result = Pointer(ptr)[] _ = arrayCopy diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index b35a6ae4f2..2f2ac70e3b 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -32,7 +32,6 @@ def test_static_tuple(): assert_equal(tup3[0], 1) assert_equal(tup3[Int(0)], 1) - assert_equal(tup3[Int64(0)], 1) def test_static_int_tuple(): From 20d5f1c3284bde6aba8085698e9d2a8dba0c91c8 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 8 Jul 2024 16:31:00 -0700 Subject: [PATCH 1163/2019] [******][Stdlib] Mark all serialization methods as no_inline This PR moves all str, repr, and format methods to be never inlined. There is no perf gain from inlining serialization method, but there is massive perf hit on compilation. MODULAR_ORIG_COMMIT_REV_ID: 9755ab6bd16a72e6dfd1739864b2c41215307029 --- stdlib/src/builtin/_location.mojo | 2 ++ stdlib/src/builtin/bool.mojo | 2 ++ stdlib/src/builtin/builtin_slice.mojo | 3 +++ stdlib/src/builtin/dtype.mojo | 3 ++- stdlib/src/builtin/error.mojo | 3 +++ stdlib/src/builtin/float_literal.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 ++ stdlib/src/builtin/int_literal.mojo | 2 +- stdlib/src/builtin/object.mojo | 3 ++- stdlib/src/builtin/simd.mojo | 5 +++-- stdlib/src/builtin/str.mojo | 6 +++--- stdlib/src/builtin/string.mojo | 1 + stdlib/src/builtin/string_literal.mojo | 2 ++ stdlib/src/builtin/uint.mojo | 4 ++-- stdlib/src/collections/dict.mojo | 1 + stdlib/src/collections/list.mojo | 3 +++ stdlib/src/collections/set.mojo | 2 ++ stdlib/src/memory/unsafe.mojo | 2 ++ stdlib/src/memory/unsafe_pointer.mojo | 2 ++ stdlib/src/os/_linux_aarch64.mojo | 1 + stdlib/src/os/_linux_x86.mojo | 1 + stdlib/src/os/_macos.mojo | 1 + stdlib/src/os/fstat.mojo | 1 + stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/pwd/pwd.mojo | 2 ++ stdlib/src/python/python.mojo | 1 + stdlib/src/time/time.mojo | 1 + stdlib/src/utils/index.mojo | 2 ++ stdlib/src/utils/inline_string.mojo | 3 ++- stdlib/src/utils/string_slice.mojo | 1 + stdlib/src/utils/stringref.mojo | 2 ++ stdlib/test/builtin/test_file.mojo | 1 + stdlib/test/testing/test_assertion.mojo | 9 +++++---- stdlib/test/utils/test_format.mojo | 2 ++ 34 files changed, 63 insertions(+), 17 deletions(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 6a9c0ab30f..3bdd1bf767 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -23,9 +23,11 @@ struct _SourceLocation(Stringable): var col: Int var file_name: StringLiteral + @no_inline fn __str__(self) -> String: return str(self.file_name) + ":" + str(self.line) + ":" + str(self.col) + @no_inline fn prefix[T: Stringable](self, msg: T) -> String: """Return the given message prefixed with the pretty-printer location. diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 49b4eda356..e6e6fe6165 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -191,6 +191,7 @@ struct Bool( _type = __mlir_type.`!pop.scalar` ](self.value) + @no_inline fn __str__(self) -> String: """Get the bool as a string. @@ -201,6 +202,7 @@ struct Bool( """ return String.format_sequence(self) + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this boolean to the provided formatter. diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 28733d7779..fd90fdcdbd 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -80,6 +80,7 @@ struct Slice(Stringable, EqualityComparable, Representable, Formattable): self.end = end self.step = step.value() if step else 1 + @no_inline fn __str__(self) -> String: """Gets the string representation of the span. @@ -91,6 +92,7 @@ struct Slice(Stringable, EqualityComparable, Representable, Formattable): self.format_to(writer) return output + @no_inline fn __repr__(self) -> String: """Gets the string representation of the span. @@ -99,6 +101,7 @@ struct Slice(Stringable, EqualityComparable, Representable, Formattable): """ return self.__str__() + @no_inline fn format_to(self, inout writer: Formatter): """Write Slice string representation to a `Formatter`. diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 66509e1e47..aa06273720 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -88,7 +88,7 @@ struct DType( """ self = other - @always_inline("nodebug") + @no_inline fn __str__(self) -> String: """Gets the name of the DType. @@ -98,6 +98,7 @@ struct DType( return String.format_sequence(self) + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this dtype to the provided formatter. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index f80f276378..fc054e907b 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -150,6 +150,7 @@ struct Error( """ return self.data.__bool__() + @no_inline fn __str__(self) -> String: """Converts the Error to string representation. @@ -158,6 +159,7 @@ struct Error( """ return String.format_sequence(self) + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this error to the provided formatter. @@ -169,6 +171,7 @@ struct Error( # TODO: Avoid this unnecessary intermediate String allocation. writer.write(self._message()) + @no_inline fn __repr__(self) -> String: """Converts the Error to printable representation. diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 8b405f33e2..d0a90d0807 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -112,7 +112,7 @@ struct FloatLiteral( # Conversion Operators # ===------------------------------------------------------------------===# - @always_inline("nodebug") + @no_inline fn __str__(self) -> String: """Get the float as a string. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 977c9a07b0..c1ffeff092 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1065,6 +1065,7 @@ struct Int( """ return self + @no_inline fn __str__(self) -> String: """Get the integer as a string. @@ -1074,6 +1075,7 @@ struct Int( return String.format_sequence(self) + @no_inline fn __repr__(self) -> String: """Get the integer as a string. Returns the same `String` as `__str__`. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index ce9c97beb6..e45da7e465 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -682,7 +682,7 @@ struct IntLiteral( mod -= multiplier return self - mod - @always_inline + @no_inline fn __str__(self) -> String: """Convert from IntLiteral to String. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 1a05a40049..79858bd9cf 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -557,6 +557,7 @@ struct _ObjectImpl(CollectionElement, CollectionElementNew, Stringable): else: lhs = lhs.convert_bool_to_int() + @no_inline fn __str__(self) -> String: """Returns the name (in lowercase) of the specific object type.""" if self.is_none(): @@ -933,7 +934,7 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): """ return self.__bool__() - @always_inline + @no_inline fn __str__(self) -> String: """Performs conversion to string according to Python semantics. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index beed0ae888..4a46b93675 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1331,7 +1331,7 @@ struct SIMD[type: DType, size: Int]( rebind[Scalar[type]](self).value ) - @always_inline + @no_inline fn __str__(self) -> String: """Get the SIMD as a string. @@ -1341,7 +1341,7 @@ struct SIMD[type: DType, size: Int]( return String.format_sequence(self) - @always_inline + @no_inline fn __repr__(self) -> String: """Get the representation of the SIMD value e.g. "SIMD[DType.int8, 2](1, 2)". @@ -1507,6 +1507,7 @@ struct SIMD[type: DType, size: Int]( # This overload is required to keep SIMD compliant with the Formattable # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will # fail to compile. + @always_inline fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): """ Formats this SIMD value to the provided formatter. diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index 2bd46cd108..d5eea357af 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -130,7 +130,7 @@ trait StringableRaising: # ===----------------------------------------------------------------------=== # -@always_inline +@no_inline fn str[T: Stringable](value: T) -> String: """Get the string representation of a value. @@ -146,7 +146,7 @@ fn str[T: Stringable](value: T) -> String: return value.__str__() -@always_inline +@no_inline fn str(value: None) -> String: """Get the string representation of the `None` type. @@ -159,7 +159,7 @@ fn str(value: None) -> String: return "None" -@always_inline +@no_inline fn str[T: StringableRaising](value: T) raises -> String: """Get the string representation of a value. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 21bf6bf690..4d0b222036 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -989,6 +989,7 @@ struct String( # ===------------------------------------------------------------------=== # @staticmethod + @no_inline fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: """ Construct a string by concatenating a sequence of formattable arguments. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 60b77847e7..8858a31d5f 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -214,6 +214,7 @@ struct StringLiteral( """ return _atol(self) + @no_inline fn __str__(self) -> String: """Convert the string literal to a string. @@ -232,6 +233,7 @@ struct StringLiteral( string._buffer = buffer^ return string + @no_inline fn __repr__(self) -> String: """Return a representation of the `StringLiteral` instance. diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 23ecf2847a..0c2b7ecbe7 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -98,7 +98,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): """ return self.value - @always_inline("nodebug") + @no_inline fn __str__(self) -> String: """Convert this UInt to a string. @@ -113,7 +113,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): """ return String.format_sequence(self) - @always_inline("nodebug") + @no_inline fn __repr__(self) -> String: """Convert this UInt to a string. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index eb9d52a890..b3a6d89c1f 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -636,6 +636,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return len(self).__bool__() + @no_inline fn __str__[ T: RepresentableKeyElement, U: RepresentableCollectionElement ](self: Dict[T, U]) -> String: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 446502ddf2..c60edf4d20 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -306,6 +306,7 @@ struct List[T: CollectionElement]( """ return len(self) > 0 + @no_inline fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. @@ -334,6 +335,7 @@ struct List[T: CollectionElement]( self.format_to(writer) return output^ + @no_inline fn format_to[ U: RepresentableCollectionElement ](self: List[U], inout writer: Formatter): @@ -352,6 +354,7 @@ struct List[T: CollectionElement]( writer.write(", ") writer.write("]") + @no_inline fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index c45ba98123..6002229a85 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -302,6 +302,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): hash_value ^= hash(e[]) return hash_value + @no_inline fn __str__[U: RepresentableKeyElement](self: Set[U]) -> String: """Returns the string representation of the set. @@ -316,6 +317,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): self.format_to(writer) return output + @no_inline fn __repr__[U: RepresentableKeyElement](self: Set[U]) -> String: """Returns the string representation of the set. diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index fc732b8f21..46f234311b 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -234,6 +234,7 @@ struct LegacyPointer[ """ return Self {address: address} + @no_inline fn __str__(self) -> String: """Format this pointer as a hexadecimal string. @@ -770,6 +771,7 @@ struct DTypePointer[ """ return int(self.address) + @no_inline fn __str__(self) -> String: """Format this pointer as a hexadecimal string. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 016458026a..e99e31572e 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -375,6 +375,7 @@ struct UnsafePointer[ """ return __mlir_op.`pop.pointer_to_index`(self.address) + @no_inline fn __str__(self) -> String: """Gets a string representation of the pointer. @@ -383,6 +384,7 @@ struct UnsafePointer[ """ return hex(int(self)) + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this pointer address to the provided formatter. diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index 142a01c244..5fd4d95dd9 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -67,6 +67,7 @@ struct _c_stat(Stringable): self.st_birthtimespec = _CTimeSpec() self.unused = InlineArray[Int64, 2](0, 0) + @no_inline fn __str__(self) -> String: var res = String("{\n") res += "st_dev: " + str(self.st_dev) + ",\n" diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index 75ae041e0d..c9cbd12ed1 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -65,6 +65,7 @@ struct _c_stat(Stringable): self.st_birthtimespec = _CTimeSpec() self.unused = InlineArray[Int64, 3](0, 0, 0) + @no_inline fn __str__(self) -> String: var res = String("{\n") res += "st_dev: " + str(self.st_dev) + ",\n" diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 725ae6fb4e..85532168e4 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -70,6 +70,7 @@ struct _c_stat(Stringable): self.st_lspare = 0 self.st_qspare = InlineArray[Int64, 2](0, 0) + @no_inline fn __str__(self) -> String: var res = String("{\n") res += "st_dev: " + str(self.st_dev) + ",\n" diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index 91783e1040..f3d367944e 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -150,6 +150,7 @@ struct stat_result(Stringable): self.st_rdev = st_rdev self.st_flags = st_flags + @no_inline fn __str__(self) -> String: """Constructs a string representation of stat_result. diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 9d67ddf916..b41e5e6f09 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -146,7 +146,7 @@ struct Path( else: self.path += DIR_SEPARATOR + suffix - @always_inline + @no_inline fn __str__(self) -> String: """Returns a string representation of the path. diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo index 0c12f7768d..34cf29a9f6 100644 --- a/stdlib/src/pwd/pwd.mojo +++ b/stdlib/src/pwd/pwd.mojo @@ -55,6 +55,7 @@ struct Passwd(Stringable): writer.write("', pw_shell='", self.pw_shell) writer.write("')") + @no_inline fn __str__(self) -> String: """Gets the Passwd struct as a string. @@ -63,6 +64,7 @@ struct Passwd(Stringable): """ return String.format_sequence(self) + @no_inline fn __repr__(self) -> String: """Gets the Passwd struct as a string. diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index c4437544a4..53def0b000 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -224,6 +224,7 @@ struct Python: """ return PythonObject([]) + @no_inline fn __str__(inout self, str_obj: PythonObject) -> StringRef: """Return a string representing the given Python object. diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 83caf4050b..f1c7c427ee 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -67,6 +67,7 @@ struct _CTimeSpec(Stringable): else: return self.tv_sec * _NSEC_PER_SEC + self.tv_subsec * _NSEC_PER_USEC + @no_inline fn __str__(self) -> String: return str(self.as_nanoseconds()) + "ns" diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 386d59f83c..dd3a1120c1 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -629,6 +629,7 @@ struct StaticIntTuple[size: Int]( _int_tuple_compare[size, apply_fn](self.data, rhs.data), True ) + @no_inline fn __str__(self) -> String: """Get the tuple as a string. @@ -661,6 +662,7 @@ struct StaticIntTuple[size: Int]( buf.size += 1 # for the null terminator. return buf^ + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this int tuple to the provided formatter. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index d5481cd223..0cf47eeb27 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -230,6 +230,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): ) return len(self._storage[String]) + @no_inline fn __str__(self) -> String: """Gets this string as a standard `String`. @@ -426,7 +427,7 @@ struct _FixedString[CAP: Int]( # Trait implementations # ===------------------------------------------------------------------=== # - @always_inline + @no_inline fn __str__(self) -> String: return String(self.as_string_slice()) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 0d3b1e5dbd..576f541743 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -141,6 +141,7 @@ struct StringSlice[ # Trait implementations # ===------------------------------------------------------------------===# + @no_inline fn __str__(self) -> String: """Gets this slice as a standard `String`. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 69356755fa..945efa2065 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -409,6 +409,7 @@ struct StringRef( """ return self.length + @no_inline fn __str__(self) -> String: """Convert the string reference to a string. @@ -417,6 +418,7 @@ struct StringRef( """ return String.format_sequence(self) + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this StringRef to the provided formatter. diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index af7fc094bd..7c4985f0e2 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -214,6 +214,7 @@ struct Word: var fourth_letter: UInt8 var fith_letter: UInt8 + @no_inline fn __str__(self) -> String: var word = List[UInt8](capacity=6) word.append(self.first_letter) diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 9dd47215e9..1f9dce6aad 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -37,6 +37,7 @@ struct DummyStruct: fn __ne__(self, other: Self) -> Bool: return self.value != other.value + @no_inline fn __str__(self) -> String: return "Dummy" # Can't be used for equality @@ -66,22 +67,22 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:67:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:68:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:72:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:73:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:77:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:78:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:82:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:83:25: AssertionError:" in str(e)) def test_assert_almost_equal(): diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index f76b9f3ec4..002b50d3db 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -33,9 +33,11 @@ struct Point(Formattable, Stringable): var x: Int var y: Int + @no_inline fn format_to(self, inout writer: Formatter): write_to(writer, "Point(", self.x, ", ", self.y, ")") + @no_inline fn __str__(self) -> String: return String.format_sequence(self) From 99b7cdb941fa45cf4557a3f6dcce0b3b90d51217 Mon Sep 17 00:00:00 2001 From: Tatiana Shpeisman Date: Mon, 8 Jul 2024 20:20:11 -0700 Subject: [PATCH 1164/2019] [stdlib] Remove MLIR magic from DType.sizeof This changes implementation of `DType.sizeof` to compute the size of the type directly instead of generating `pop.dtype.sizeof`. With this change `DType.sizeof` for index works correctly instead of giving a compile-time error. It also changes implementation of `DType.sizeof.is_half_point` to compute the result directly instead of going via DType.sizeof. MODULAR_ORIG_COMMIT_REV_ID: f9e492c0dc122081101d225862fdca24d2c9e1ab --- stdlib/src/builtin/dtype.mojo | 57 +++++++++++++++++++++++++---- stdlib/test/builtin/test_dtype.mojo | 7 ++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index aa06273720..f84bc0994e 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -22,6 +22,7 @@ from utils import unroll alias _mIsSigned = UInt8(1) alias _mIsInteger = UInt8(1 << 7) +alias _mIsNotInteger = UInt8(~(1 << 7)) alias _mIsFloat = UInt8(1 << 6) @@ -274,14 +275,12 @@ struct DType( ) @always_inline("nodebug") - fn is_integral(self) -> Bool: - """Returns True if the type parameter is an integer and False otherwise. + fn _is_non_index_integral(self) -> Bool: + """Returns True if the type parameter is a non-index integer value and False otherwise. Returns: - Returns True if the input type parameter is an integer. + Returns True if the input type parameter is a non-index integer. """ - if self is DType.index: - return True return Bool( __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( __mlir_op.`pop.and`(self._as_i8(), _mIsInteger.value), @@ -289,6 +288,17 @@ struct DType( ) ) + @always_inline("nodebug") + fn is_integral(self) -> Bool: + """Returns True if the type parameter is an integer and False otherwise. + + Returns: + Returns True if the input type parameter is an integer. + """ + if self is DType.index: + return True + return self._is_non_index_integral() + @always_inline("nodebug") fn is_floating_point(self) -> Bool: """Returns True if the type parameter is a floating-point and False @@ -315,7 +325,7 @@ struct DType( True if the type is a half-precision float, false otherwise.. """ - return self.bitwidth() == 16 and self.is_floating_point() + return self in (DType.bfloat16, DType.float16) @always_inline("nodebug") fn is_numeric(self) -> Bool: @@ -335,7 +345,40 @@ struct DType( Returns: Returns the size in bytes of the current DType. """ - return __mlir_op.`pop.dtype.sizeof`(self.value) + + if self._is_non_index_integral(): + return int( + UInt8( + __mlir_op.`pop.shl`( + UInt8(1).value, + __mlir_op.`pop.sub`( + __mlir_op.`pop.shr`( + __mlir_op.`pop.and`( + self._as_i8(), _mIsNotInteger.value + ), + UInt8(1).value, + ), + UInt8(3).value, + ), + ) + ) + ) + + if self == DType.bool: + return sizeof[DType.bool]() + if self == DType.index: + return sizeof[DType.index]() + if self == DType.bfloat16: + return sizeof[DType.bfloat16]() + if self == DType.float16: + return sizeof[DType.float16]() + if self == DType.float32: + return sizeof[DType.float32]() + if self == DType.tensor_float32: + return sizeof[DType.tensor_float32]() + if self == DType.float64: + return sizeof[DType.float64]() + return sizeof[DType.invalid]() @always_inline fn bitwidth(self) -> Int: diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index e1d8b1646c..6799f1bcfd 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -43,8 +43,15 @@ fn test_key_element() raises: assert_true(DType.int64 in set) +fn test_sizeof() raises: + assert_equal(DType.int16.sizeof(), sizeof[DType.int16]()) + assert_equal(DType.float32.sizeof(), sizeof[DType.float32]()) + assert_equal(DType.index.sizeof(), sizeof[DType.index]()) + + fn main() raises: test_equality() test_stringable() test_representable() test_key_element() + test_sizeof() From 1d13059f32d3295dcffd32fb65e750e2ca0f6482 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 07:44:00 -0500 Subject: [PATCH 1165/2019] [stdlib] Remove `Intable` from `InlineArray.__getitem__` As mentioned at modularml/mojo#2337 (comment), we've pivoted a bit and moved away from `Intable` for offsets in `__getitem__` implementations now that we have `Indexer`. Fix this last use of `__getitem__` that works on a `Intable` type to use `Int` explicitly. Do the same for `__setitem__` while we're here. MODULAR_ORIG_COMMIT_REV_ID: cf13a3daf2056b0eeffe879049f9e865a1595e94 --- stdlib/src/utils/static_tuple.mojo | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index bd034a5ab6..c888b573a8 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -214,23 +214,17 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): return result @always_inline("nodebug") - fn __setitem__[ - intable: Intable - ](inout self, index: intable, val: Self.element_type): + fn __setitem__(inout self, idx: Int, val: Self.element_type): """Stores a single value into the tuple at the specified dynamic index. - Parameters: - intable: The intable type. - Args: - index: The index into the tuple. + idx: The index into the tuple. val: The value to store. """ - var offset = int(index) - debug_assert(offset < size, "index must be within bounds") + debug_assert(idx < size, "index must be within bounds") var tmp = self var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(tmp.array).address, offset.value + UnsafePointer.address_of(tmp.array).address, idx.value ) Pointer(ptr)[] = val self = tmp @@ -390,25 +384,22 @@ struct InlineArray[ @always_inline("nodebug") fn __getitem__[ - IntableType: Intable, - index: IntableType, + idx: Int, ](ref [_]self: Self) -> ref [__lifetime_of(self)] Self.ElementType: """Get a `Reference` to the element at the given index. Parameters: - IntableType: The inferred type of an intable argument. - index: The index of the item. + idx: The index of the item. Returns: A reference to the item at the given index. """ - alias i = int(index) - constrained[-size <= i < size, "Index must be within bounds."]() + constrained[-size <= idx < size, "Index must be within bounds."]() - var normalized_idx = i + var normalized_idx = idx @parameter - if i < 0: + if idx < 0: normalized_idx += size return self._get_reference_unsafe(normalized_idx)[] From 6faee4f5476729c48f0e555d0cb6abbdc54389ff Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 07:44:17 -0500 Subject: [PATCH 1166/2019] [stdlib] Simplify `List.__contains__` Now that some parameter inference issues have been fixed, simplify `List.__contains__` to avoid the crazy rebind. MODULAR_ORIG_COMMIT_REV_ID: 0d7111afd43f0ad938eaca8a5bcb1baa6b56a2ef --- stdlib/src/collections/list.mojo | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index c60edf4d20..dbe876f6ca 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -198,8 +198,8 @@ struct List[T: CollectionElement]( # ===-------------------------------------------------------------------===# fn __contains__[ - T2: ComparableCollectionElement - ](self: List[T], value: T2) -> Bool: + U: ComparableCollectionElement + ](self: List[U], value: U) -> Bool: """Verify if a given value is present in the list. ```mojo @@ -207,7 +207,7 @@ struct List[T: CollectionElement]( if 3 in x: print("x contains 3") ``` Parameters: - T2: The type of the elements in the list. Must implement the + U: The type of the elements in the list. Must implement the traits `EqualityComparable` and `CollectionElement`. Args: @@ -216,10 +216,8 @@ struct List[T: CollectionElement]( Returns: True if the value is contained in the list, False otherwise. """ - - constrained[_type_is_eq[T, T2](), "value type is not self.T"]() for i in self: - if rebind[Reference[T2, __lifetime_of(self)]](i)[] == value: + if i[] == value: return True return False From 02bdb3043e19831c3f160f2ee65678216d0d9400 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 07:47:01 -0500 Subject: [PATCH 1167/2019] [stdlib][docs] Fix various typos Inspired by https://github.com/modularml/mojo/pull/2295, run an initial pass of `codespell` on the open source Mojo files, including stdlib code, docs, and proposals. Fix these up using `codespell` while ignoring `inout` and `modul` words in the dictionary used by `codespell`. In the future, we may be in a position to enable this internally to avoid regressions. MODULAR_ORIG_COMMIT_REV_ID: 756b454345c0364d147e2acfbe49f3b7b4cf092a --- CONTRIBUTING.md | 2 +- docs/changelog.md | 2 +- docs/manual/functions.ipynb | 2 +- docs/manual/types.ipynb | 2 +- docs/manual/variables.ipynb | 2 +- docs/tools/testing.ipynb | 4 ++-- proposals/improved-hash-module.md | 8 ++++---- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/docs/bencher/BenchConfig.md | 2 +- stdlib/src/builtin/builtin_slice.mojo | 2 +- stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string.mojo | 4 ++-- stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/math/math.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/src/os/path/path.mojo | 6 +++--- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/python/python.mojo | 2 +- stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/builtin/test_format_int.mojo | 4 ++-- stdlib/test/builtin/test_math.mojo | 2 +- stdlib/test/builtin/test_slice.mojo | 8 ++++---- stdlib/test/builtin/test_string.mojo | 8 ++++---- stdlib/test/builtin/test_string_literal.mojo | 2 +- stdlib/test/collections/test_list.mojo | 8 ++++---- stdlib/test/memory/test_maybe_uninitialized.mojo | 4 ++-- stdlib/test/os/path/test_isdir.mojo | 2 +- stdlib/test/os/path/test_islink.mojo | 2 +- stdlib/test/os/path/test_split.mojo | 2 +- stdlib/test/os/test_remove.mojo | 2 +- 33 files changed, 51 insertions(+), 51 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94b402dfb1..21a863dcd5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,7 +138,7 @@ We ask that contributors make pull requests as small as possible. When you are opening a pull request, check the number of lines modified in GitHub. The smaller the better (but don't exclude the tests or docstrings). If your pull request is over 100 lines, please try to split it into multiple pull -requests. If you make them independant, it's even better as no synchronization +requests. If you make them independent, it's even better as no synchronization will be needed for the merge. This guideline is here for the following reasons: diff --git a/docs/changelog.md b/docs/changelog.md index 3d7e8acef2..47a81a404c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -207,7 +207,7 @@ what we publish. Issue [#2135](https://github.com/modularml/mojo/issues/2135)). - Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a - paltform-dependent width. `UInt` implements most arithmethic operations that + paltform-dependent width. `UInt` implements most arithmetic operations that make sense for integers, with the notable exception of `__neg__`. Builtin functions such as `min`/`max`, as well as `math` functions like `ceildiv`, `align_down`, and `align_up` are also implemented for `UInt`. diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 17ef6e6fd9..f7ae2d9fba 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -338,7 +338,7 @@ "- Heterogeneous variadic arguments, which can accept a set of different argument\n", " types.\n", "\n", - "The following sections describe how to work with homogeneous and heterogenous\n", + "The following sections describe how to work with homogeneous and heterogeneous\n", "variadic arguments.\n", "\n", ":::note Variadic parameters\n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index db01ab1928..993eea5cdc 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -844,7 +844,7 @@ "# Two ways to initialize an Optional with a value\n", "var opt1 = Optional(5)\n", "var opt2: Optional[Int] = 5\n", - "# Two ways to initalize an Optional with no value\n", + "# Two ways to initialize an Optional with no value\n", "var opt3 = Optional[Int]()\n", "var opt4: Optional[Int] = None" ] diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 7cac974ebd..8d1dd916b6 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -178,7 +178,7 @@ "```mojo\n", "var name = \"Sam\"\n", "# Somewhere later...\n", - "nane = \"Sammy\" # This is not allowed in an `fn` function\n", + "name = \"Sammy\" # This is not allowed in an `fn` function\n", "```\n", "\n", "Although you can use `var` in a `def` function, this benefit is\n", diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb index 2db4aad051..94b06f1ebf 100644 --- a/docs/tools/testing.ipynb +++ b/docs/tools/testing.ipynb @@ -310,7 +310,7 @@ "output_type": "stream", "text": [ "Test passes because the error contains the substring\n", - "Test fails because the error doesnt contain the substring\n", + "Test fails because the error doesn't contain the substring\n", "Error: invalid value\n" ] } @@ -320,7 +320,7 @@ "with assert_raises(contains=\"required\"):\n", " raise Error(\"missing required argument\")\n", "\n", - "print(\"Test fails because the error doesnt contain the substring\")\n", + "print(\"Test fails because the error doesn't contain the substring\")\n", "with assert_raises(contains=\"required\"):\n", " raise Error(\"invalid value\")" ] diff --git a/proposals/improved-hash-module.md b/proposals/improved-hash-module.md index 1c82233c5a..c2f9c460fb 100644 --- a/proposals/improved-hash-module.md +++ b/proposals/improved-hash-module.md @@ -45,7 +45,7 @@ struct Person(Hashable): As you can see above we, computed hashes for all of the struct fields, but we are uncertain how to combine those values in a way which produces a good (non compromised) hash value. Python [docs](https://docs.python.org/3/reference/datamodel.html#object.__hash__) -suggest to pack fileds into a tuple and hash the tuple, but this is not +suggest to pack fields into a tuple and hash the tuple, but this is not possible at this point in time in Mojo. ## Proposal @@ -87,7 +87,7 @@ The standard library should provide a default `Hasher` implementation, but it would be possible for the developers to implement, or choose other hash algorithms, if they better fit their use case. -Bellow you can see a dummy implementation of a `DefaultHasher` +Below you can see a dummy implementation of a `DefaultHasher` ```mojo struct DefaultHasher(Hasher): @@ -147,7 +147,7 @@ fn hash[T: Hashable, H: Hasher = DefaultHasher](value: T) -> UInt64: ## Prove of concept -Bellow you can find a fully working POC implementation: +Below you can find a fully working POC implementation: ```mojo from os.env import getenv, setenv @@ -291,4 +291,4 @@ Current compiler does not allow parameters on trait definition. A parametrization on Hasher trait for for hash value dtype would be beneficial as a hashing algorithm might differ. For example in [Fowler–Noll–Vo hash function](https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function#FNV_hash_parameters) -parameters prime and offset basis dependend on hash value width. +parameters prime and offset basis depend on hash value width. diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 9a2bbe3cff..5b00a232dd 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -256,7 +256,7 @@ fn bench_compare(): alias type = DType.uint8 alias width = simdwidthof[type]() alias unit = Unit.ns - # increasing will reduce the benefit of passing the size as a paramater + # increasing will reduce the benefit of passing the size as a parameter alias multiplier = 2 # Add .5 of the elements that fit into a simd register alias size: Int = int(multiplier * width + (width * 0.5)) diff --git a/stdlib/docs/bencher/BenchConfig.md b/stdlib/docs/bencher/BenchConfig.md index 6c65296515..65f4092d79 100644 --- a/stdlib/docs/bencher/BenchConfig.md +++ b/stdlib/docs/bencher/BenchConfig.md @@ -53,7 +53,7 @@ __init__(inout self: Self, out_file: Optional[Path] = #kgen.none, min_runtime_se -Constructs and initializes Benchmark config object with default and inputed values. +Constructs and initializes Benchmark config object with default and inputted values. **Args:** diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index fd90fdcdbd..577227036c 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -168,7 +168,7 @@ struct Slice(Stringable, EqualityComparable, Representable, Formattable): return len(range(self.start.value(), self.end.value(), self.step)) fn indices(self, length: Int) -> (Int, Int, Int): - """Returns a tuple of 3 intergers representing the start, end, and step + """Returns a tuple of 3 integers representing the start, end, and step of the slice if applied to a container of the given length. Uses the target container length to normalize negative, out of bounds, diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index f03d91192d..b84ee627d8 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -31,7 +31,7 @@ from sys.ffi import _get_global from builtin.dtype import _uint_type_of_width from memory import memcpy, memset_zero, stack_allocation -# TODO remove this import onece InlineArray is moved to collections +# TODO remove this import once InlineArray is moved to collections from utils import InlineArray # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index c1ffeff092..39be20095e 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -74,7 +74,7 @@ fn index[T: Indexer](idx: T, /) -> Int: idx: The value. Returns: - An `Int` respresenting the index value. + An `Int` representing the index value. """ return idx.__index__() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 4a46b93675..4b799faf6a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -391,7 +391,7 @@ struct SIMD[type: DType, size: Int]( """ _simd_construction_checks[type, size]() - # TODO (#36686): This introduces uneeded casts here to work around + # TODO (#36686): This introduces unneeded casts here to work around # parameter if issues. @parameter if type is DType.float16: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 4d0b222036..0c6fba4682 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -487,7 +487,7 @@ fn _atof(str_ref: StringRef) raises -> Float64: # NOTE: Instead of `var result *= 10.0 ** exponent`, we calculate a positive # integer factor as shift and multiply or divide by it based on the shift # direction. This allows for better precision. - # TODO: investigate if there is a floating point arithmethic problem. + # TODO: investigate if there is a floating point arithmetic problem. var shift: Int = 10 ** abs(exponent) if exponent > 0: result *= shift @@ -1638,7 +1638,7 @@ struct String( listed above, otherwise False. """ # TODO add line and paragraph separator as stringliteral - # once unicode escape secuences are accepted + # once unicode escape sequences are accepted var next_line = List[UInt8](0xC2, 0x85) """TODO: \\x85""" var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 6c34c2e466..3e9b6b7c36 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -145,7 +145,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): idx: The element to return. Returns: - A referece to the specified element. + A reference to the specified element. """ # Return a reference to an element at the specified index, propagating # mutability of self. diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 523592c7b6..929432e079 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -213,7 +213,7 @@ struct Optional[T: CollectionElement]( traits `Representable` and `CollectionElement`. Returns: - A string represenation of the Optional. + A string representation of the Optional. """ var output = String() var writer = output._unsafe_to_formatter() diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index c42b2198b6..c909f31e16 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -482,7 +482,7 @@ fn exp[ """Calculates elementwise exponential of the input vector. Given an input vector $X$ and an output vector $Y$, sets $Y_i = e^{X_i}$ for - each position $i$ in the input vector (where $e$ is the mathmatical constant + each position $i$ in the input vector (where $e$ is the mathematical constant $e$). Constraints: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index e99e31572e..53d1537f61 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -465,7 +465,7 @@ struct UnsafePointer[ The pointer must not be null, and the pointer memory location is assumed to contain a valid initialized instance of `T`. This is equivalent to `_ = self.take_pointee()` but doesn't require `Movable` and is - more efficient becase it doesn't invoke `__moveinit__`. + more efficient because it doesn't invoke `__moveinit__`. """ _ = __get_address_as_owned_value(self.address) diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index fb72faf888..9aeac9d374 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -122,7 +122,7 @@ fn expanduser[PathLike: os.PathLike, //](path: PathLike) raises -> String: if not userhome: return fspath var path_split = fspath.split(os.sep, 1) - # If there is a properly formatted seperator, return expanded fspath. + # If there is a properly formatted separator, return expanded fspath. if len(path_split) == 2: return os.path.join(userhome, path_split[1]) # Path was a single `~` character, return home path @@ -344,9 +344,9 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): """ Split a given pathname into two components: head and tail. This is useful for separating the directory path from the filename. If the input path ends - with a seperator, the tail component will be empty. If there is no seperator + with a separator, the tail component will be empty. If there is no separator in the path, the head component will be empty, and the entire path will be - considered the tail. Trailing seperators in the head are stripped unless the + considered the tail. Trailing separators in the head are stripped unless the head is the root directory. Parameters: diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index fe76a3bbf1..f7d8bfc70b 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -175,7 +175,7 @@ struct CPython: fn check_init_error(self) raises: """Used for entry points that initialize Python on first use, will - raise an error if one occured when initializing the global CPython. + raise an error if one occurred when initializing the global CPython. """ if self.init_error: var error: String = self.init_error diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 53def0b000..7f191b25d6 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -200,7 +200,7 @@ struct Python: The Python module. """ var cpython = _get_global_python_itf().cpython() - # Throw error if it occured during initialization + # Throw error if it occurred during initialization cpython.check_init_error() var module_maybe = cpython.PyImport_ImportModule(module) Python.throw_python_exception_if_error_state(cpython) diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 7c4985f0e2..3167260c33 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -154,7 +154,7 @@ def test_file_seek(): _ = f.read(6) - # Seek from current possition, skip the space + # Seek from current position, skip the space pos = f.seek(1, os.SEEK_CUR) assert_equal(pos, 945) assert_equal(f.read(7), "rhoncus") diff --git a/stdlib/test/builtin/test_format_int.mojo b/stdlib/test/builtin/test_format_int.mojo index 9f352f940c..58d8308c9b 100644 --- a/stdlib/test/builtin/test_format_int.mojo +++ b/stdlib/test/builtin/test_format_int.mojo @@ -136,11 +136,11 @@ def test_indexer(): def test_different_prefix(): assert_equal(bin(Int8(1), prefix="binary"), "binary1") - assert_equal(hex(Int8(1), prefix="hexidecimal"), "hexidecimal1") + assert_equal(hex(Int8(1), prefix="hexadecimal"), "hexidecimal1") assert_equal(oct(Int8(1), prefix="octal"), "octal1") assert_equal(bin(0, prefix="binary"), "binary0") - assert_equal(hex(0, prefix="hexidecimal"), "hexidecimal0") + assert_equal(hex(0, prefix="hexadecimal"), "hexidecimal0") assert_equal(oct(0, prefix="octal"), "octal0") assert_equal(bin(Ind(), prefix="I'mAnIndexer!"), "I'mAnIndexer!1") diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 551693a8cc..4ae6bdf8f2 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -81,7 +81,7 @@ def test_round(): assert_equal(2, round(2.0)) assert_equal(1, round(1.4, 0)) # FIXME: round(2.5) is 2.0 (roundeven) but it's using roundhalfup - # Fix when the math libray is open sourced + # Fix when the math library is open sourced # assert_equal(2, round(2.5)) # assert_equal(1.5, round(1.5, 1)) # assert_equal(1.61, round(1.613, 2)) diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index fd736e58ea..7d467f7cbf 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -37,7 +37,7 @@ struct BoringSlice: var c: String -struct Slicable: +struct Sliceable: fn __init__(inout self): pass @@ -49,14 +49,14 @@ struct Slicable: def test_slicable(): - var slicable = Slicable() + var sliceable = Sliceable() - var new_slice = slicable[1:"hello":4.0] + var new_slice = sliceable[1:"hello":4.0] assert_equal(new_slice.start, 1) assert_equal(new_slice.upper, "hello") assert_equal(new_slice.stride, 4.0) - var boring_slice = slicable[1:2:"foo"] + var boring_slice = sliceable[1:2:"foo"] assert_equal(boring_slice.a, 1) assert_equal(boring_slice.b, 2) assert_equal(boring_slice.c, "foo") diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 541ef040a3..f16f97eec4 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -562,8 +562,8 @@ def test_contains(): assert_true(str.__contains__(" ")) assert_true(str.__contains__("ld")) - assert_false(str.__contains__("bellow")) - assert_true("bellow" not in str) + assert_false(str.__contains__("below")) + assert_true("below" not in str) def test_find(): @@ -635,7 +635,7 @@ def test_rfind(): assert_equal(String("").rfind("ab"), -1) assert_equal(String("foo").rfind(""), 3) - # Test that rfind(start) returned pos is absolute, not relative to specifed + # Test that rfind(start) returned pos is absolute, not relative to specified # start. Also tests positive and negative start offsets. assert_equal(String("hello world").rfind("l", 5), 9) assert_equal(String("hello world").rfind("l", -5), 9) @@ -926,7 +926,7 @@ def test_isspace(): alias unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9, 0) """TODO: \\u2029""" # TODO add line and paragraph separator as stringliteral once unicode - # escape secuences are accepted + # escape sequences are accepted var univ_sep_var = List[String]( String(" "), String("\t"), diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 1c9349e942..62da1e7294 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -84,7 +84,7 @@ def test_rfind(): assert_equal("".rfind("ab"), -1) assert_equal("foo".rfind(""), 3) - # Test that rfind(start) returned pos is absolute, not relative to specifed + # Test that rfind(start) returned pos is absolute, not relative to specified # start. Also tests positive and negative start offsets. assert_equal("hello world".rfind("l", 5), 9) assert_equal("hello world".rfind("l", -5), 9) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 5dd85fa296..b8e3b02f56 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -124,7 +124,7 @@ def test_list_pop(): for i in range(6): list.append(i) - # try poping from index 3 for 3 times + # try popping from index 3 for 3 times for i in range(3, 6): assert_equal(i, list.pop(3)) @@ -573,7 +573,7 @@ def test_no_extra_copies_with_sugared_set_by_field(): child_list.append(CopyCountedStruct("Hello")) child_list.append(CopyCountedStruct("World")) - # No copies here. Contructing with List[CopyCountedStruct](CopyCountedStruct("Hello")) is a copy. + # No copies here. Constructing with List[CopyCountedStruct](CopyCountedStruct("Hello")) is a copy. assert_equal(0, child_list[0].counter.copy_count) assert_equal(0, child_list[1].counter.copy_count) list.append(child_list^) @@ -592,7 +592,7 @@ def test_list_copy_constructor(): var vec = List[Int](capacity=1) var vec_copy = vec vec_copy.append(1) # Ensure copy constructor doesn't crash - _ = vec^ # To ensure previous one doesn't invoke move constuctor + _ = vec^ # To ensure previous one doesn't invoke move constructor def test_list_iter(): @@ -857,7 +857,7 @@ struct DtorCounter(CollectionElement): def inner_test_list_dtor(): - # explicity reset global counter + # explicitly reset global counter g_dtor_count = 0 var l = List[DtorCounter]() diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index e1f60da387..9e287d49a6 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -18,7 +18,7 @@ from testing import assert_equal def test_maybe_uninitialized(): - # Every time an Int is destroyed, it's going to be reccorded here. + # Every time an Int is destroyed, it's going to be recorded here. var destructor_counter = List[Int]() var a = UnsafeMaybeUninitialized[ValueDestructorRecorder]() @@ -40,7 +40,7 @@ def test_maybe_uninitialized(): _ = a # Last use of a, but the destructor should not have run - # since we asssume uninitialized memory + # since we assume uninitialized memory assert_equal(len(destructor_counter), 1) diff --git a/stdlib/test/os/path/test_isdir.mojo b/stdlib/test/os/path/test_isdir.mojo index 9ebc00c5dd..74e4b3e7fc 100644 --- a/stdlib/test/os/path/test_isdir.mojo +++ b/stdlib/test/os/path/test_isdir.mojo @@ -22,5 +22,5 @@ from testing import assert_false, assert_true def main(): assert_true(isdir(Path())) assert_true(isdir(str(cwd()))) - assert_false(isdir(str(cwd() / "nonexistant"))) + assert_false(isdir(str(cwd() / "nonexistent"))) assert_false(isdir(__source_location().file_name)) diff --git a/stdlib/test/os/path/test_islink.mojo b/stdlib/test/os/path/test_islink.mojo index c4489b02b9..9e6cac2153 100644 --- a/stdlib/test/os/path/test_islink.mojo +++ b/stdlib/test/os/path/test_islink.mojo @@ -29,4 +29,4 @@ def main(): assert_true(isdir(Path(TEMP_DIR))) assert_true(isdir(TEMP_DIR)) assert_true(islink(TEMP_DIR)) - assert_false(islink(str(Path(TEMP_DIR) / "nonexistant"))) + assert_false(islink(str(Path(TEMP_DIR) / "nonexistent"))) diff --git a/stdlib/test/os/path/test_split.mojo b/stdlib/test/os/path/test_split.mojo index 5192a5766b..eaa6c008ea 100644 --- a/stdlib/test/os/path/test_split.mojo +++ b/stdlib/test/os/path/test_split.mojo @@ -41,7 +41,7 @@ def main(): assert_equal(head, "") assert_equal(tail, "") - # Single seperator + # Single separator head, tail = split(os.sep) assert_equal(head, os.sep) assert_equal(tail, "") diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index 681e1abc9c..eef9ce3331 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -45,7 +45,7 @@ fn test_remove() raises: "Unexpected file " + my_file_name + " it should not exist", ) - # tyring to delete non existing file + # trying to delete non existing file with assert_raises(contains="Can not remove file: "): remove(my_file_name) with assert_raises(contains="Can not remove file: "): From 55f70c9248d75a36536ad031ac15d5d2d67f1561 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 08:19:01 -0500 Subject: [PATCH 1168/2019] [stdlib] Convert `Pointer` use in `static_tuple.mojo` As part of migrating code off of `Pointer` which is getting phased out, use `UnsafePointer` constructor here for setting a value at a particular memory address. MODULAR_ORIG_COMMIT_REV_ID: e9317889ced454ba8b0725c0b60623554916f46f --- stdlib/src/utils/static_tuple.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index c888b573a8..16983094c8 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -21,7 +21,7 @@ from utils import StaticTuple from collections._index_normalization import normalize_index from sys.intrinsics import _type_is_eq -from memory import Pointer, UnsafePointer +from memory import UnsafePointer from utils import unroll @@ -209,7 +209,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(arrayCopy).address, idx.value ) - var result = Pointer(ptr)[] + var result = UnsafePointer(ptr)[] _ = arrayCopy return result @@ -226,7 +226,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(tmp.array).address, idx.value ) - Pointer(ptr)[] = val + UnsafePointer(ptr)[] = val self = tmp From fa15ef47a8ad443712b9f0de54502d79c7d38d4d Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:39:14 -0500 Subject: [PATCH 1169/2019] [External] [stdlib] `Int` comparison operator unit tests (#43107) Added unit tests for `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, and `__ge__` dunders of `Int`. Related to #3145 ORIGINAL_AUTHOR=Matvey Fedoseev <41521530+MatveyF@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3161 Co-authored-by: Matvey Fedoseev <41521530+MatveyF@users.noreply.github.com> Closes modularml/mojo#3161 MODULAR_ORIG_COMMIT_REV_ID: 12875e85edc971dcd61913293988525cf7c19c71 --- stdlib/test/builtin/test_int.mojo | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index f6cc87f919..a7de184003 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -195,6 +195,38 @@ def test_int_uint(): assert_equal(0, int(u2)) +def test_comparison(): + assert_true(Int(5).__lt__(Int(10))) + assert_true(Int(-10).__lt__(Int(-5))) + assert_false(Int(0).__lt__(Int(0))) + assert_false(Int(10).__lt__(Int(5))) + + assert_true(Int(5).__le__(Int(10))) + assert_true(Int(-10).__le__(Int(-5))) + assert_true(Int(0).__le__(Int(0))) + assert_false(Int(10).__le__(Int(5))) + + assert_true(Int(5).__eq__(Int(5))) + assert_true(Int(0).__eq__(Int(0))) + assert_false(Int(0).__eq__(Int(1))) + assert_false(Int(5).__eq__(Int(10))) + + assert_true(Int(5).__ne__(Int(10))) + assert_true(Int(0).__ne__(Int(1))) + assert_false(Int(5).__ne__(Int(5))) + assert_false(Int(0).__ne__(Int(0))) + + assert_true(Int(10).__gt__(Int(5))) + assert_true(Int(-5).__gt__(Int(-10))) + assert_false(Int(0).__gt__(Int(0))) + assert_false(Int(5).__gt__(Int(10))) + + assert_true(Int(10).__ge__(Int(5))) + assert_true(Int(5).__ge__(Int(5))) + assert_true(Int(-5).__ge__(Int(-10))) + assert_false(Int(5).__ge__(Int(10))) + + def main(): test_properties() test_add() @@ -214,4 +246,5 @@ def main(): test_indexer() test_bool() test_decimal_digit_count() + test_comparison() test_int_uint() From df0cc29616a17e6a1602c79500bd4f1fc80f596e Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 9 Jul 2024 11:20:21 -0400 Subject: [PATCH 1170/2019] [stdlib] Add `_heap_sort` and benchmark against `sort` This PR resolves this [Internal link] and fixes some issues in [Internal link] The previous PR calls `_small_sort` with `size=4` no matter what the size of list was in `bench_sort`. It also generate random float lists with `random_float64(0, inf)` which will always return an `inf`. Then benchmarks were ran on Apple M3. When sorting ints, `sort` takes up to 6x longer than `_heap_sort`: ``` "bench_std_sort_random_list_4096_type_uint8",0.109719,1000 "bench_std_sort_random_list_4096_type_uint8",0.12102800000000001,1000 "bench_std_sort_random_list_4096_type_uint8",0.114569,1000 "bench_heap_sort_random_list_4096_type_uint8",0.085964157706093197,1395 "bench_heap_sort_random_list_4096_type_uint8",0.088480783176214647,1379 "bench_heap_sort_random_list_4096_type_uint8",0.086336219336219336,1386 "bench_std_sort_random_list_65536_type_uint8",12.534207920792079,101 "bench_std_sort_random_list_65536_type_uint8",11.537118811881188,101 "bench_std_sort_random_list_65536_type_uint8",11.488762376237624,101 "bench_heap_sort_random_list_65536_type_uint8",1.9294752475247525,101 "bench_heap_sort_random_list_65536_type_uint8",1.9696039603960396,101 "bench_heap_sort_random_list_65536_type_uint8",1.9374257425742576,101 ``` MODULAR_ORIG_COMMIT_REV_ID: 32eb54f4598006cf52fae20f38c3d471df8b685f --- stdlib/benchmarks/builtin/bench_sort.mojo | 147 +++++++++++++++------- stdlib/src/builtin/sort.mojo | 31 +++++ 2 files changed, 132 insertions(+), 46 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index bdb9ebb72d..cab3a345f8 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -15,7 +15,7 @@ from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from random import * -from stdlib.builtin.sort import sort, _small_sort, _insertion_sort +from stdlib.builtin.sort import sort, _small_sort, _insertion_sort, _heap_sort # ===----------------------------------------------------------------------===# # Benchmark Utils @@ -36,11 +36,12 @@ alias dtypes = List( ) +@always_inline fn random_scalar_list[ dt: DType ](size: Int, max: Scalar[dt] = Scalar[dt].MAX) -> List[Scalar[dt]]: var result = List[Scalar[dt]](capacity=size) - for _ in range(size): + for i in range(size): @parameter if dt.is_integral() and dt.is_signed(): @@ -48,12 +49,34 @@ fn random_scalar_list[ elif dt.is_integral() and dt.is_unsigned(): result.append(random_ui64(0, max.cast[DType.uint64]()).cast[dt]()) else: - var res = random_float64(0, max.cast[DType.float64]()) + var res = random_float64() # GCC doesn't support cast from float64 to float16 result.append(res.cast[DType.float32]().cast[dt]()) return result +@always_inline +fn insertion_sort[type: DType](list: List[Scalar[type]]): + var ptr = rebind[Pointer[Scalar[type]]](list.data) + + @parameter + fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + + _insertion_sort[Scalar[type], _less_than_equal](ptr, 0, len(list)) + + +@always_inline +fn small_sort[size: Int, type: DType](list: List[Scalar[type]]): + var ptr = rebind[Pointer[Scalar[type]]](list.data) + + @parameter + fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + + _small_sort[size, Scalar[type], _less_than_equal](ptr) + + # ===----------------------------------------------------------------------===# # Benchmark sort functions with a tiny list size # ===----------------------------------------------------------------------===# @@ -61,15 +84,14 @@ fn random_scalar_list[ @parameter fn bench_tiny_list_sort(inout m: Bench) raises: - alias counts = List(2, 3, 4, 5) + alias small_list_size = 5 @parameter for type_index in range(len(dtypes)): alias dt = dtypes[type_index] @parameter - for count_index in range(len(counts)): - alias count = counts[count_index] + for count in range(small_list_size): var list = random_scalar_list[dt](count) @parameter @@ -88,18 +110,7 @@ fn bench_tiny_list_sort(inout m: Bench) raises: @parameter fn call_fn(): var l1 = list - var small_p = rebind[Pointer[Scalar[dt]]](l1.data) - - @parameter - fn _less_than_equal[ - ty: AnyTrivialRegType - ](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( - rhs - ) - - _small_sort[4, Scalar[dt], _less_than_equal](small_p) - _ = l1 + small_sort[count, dt](l1) b.iter[call_fn]() @@ -109,20 +120,7 @@ fn bench_tiny_list_sort(inout m: Bench) raises: @parameter fn call_fn(): var l1 = list - var small_p = rebind[Pointer[Scalar[dt]]](l1.data) - - @parameter - fn _less_than_equal[ - ty: AnyTrivialRegType - ](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( - rhs - ) - - _insertion_sort[Scalar[dt], _less_than_equal]( - small_p, 0, count - ) - _ = l1 + insertion_sort[dt](l1) b.iter[call_fn]() @@ -187,20 +185,7 @@ fn bench_small_list_sort(inout m: Bench) raises: @parameter fn call_fn(): var l1 = list - var small_p = rebind[Pointer[Scalar[dt]]](l1.data) - - @parameter - fn _less_than_equal[ - ty: AnyTrivialRegType - ](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[dt]](lhs) <= rebind[Scalar[dt]]( - rhs - ) - - _insertion_sort[Scalar[dt], _less_than_equal]( - small_p, 0, count - ) - _ = l1 + insertion_sort[dt](l1) b.iter[call_fn]() @@ -223,6 +208,75 @@ fn bench_small_list_sort(inout m: Bench) raises: _ = list^ +# ===----------------------------------------------------------------------===# +# Benchmark sort functions with a large list size +# ===----------------------------------------------------------------------===# + + +@always_inline +fn heap_sort[type: DType](list: List[Scalar[type]]): + var ptr = rebind[Pointer[Scalar[type]]](list.data) + + @parameter + fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + + _heap_sort[Scalar[type], _less_than_equal](ptr, len(list)) + + +@parameter +fn bench_large_list_sort(inout m: Bench) raises: + alias counts = List(1 << 12, 1 << 16) + + @parameter + for type_index in range(len(dtypes)): + alias dt = dtypes[type_index] + + @parameter + for count_index in range(len(counts)): + alias count = counts[count_index] + var list = random_scalar_list[dt](count) + + @parameter + fn bench_sort_list(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + sort(l1) + + b.iter[call_fn]() + + @parameter + fn bench_heap_sort(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + heap_sort(l1) + + b.iter[call_fn]() + + m.bench_function[bench_sort_list]( + BenchId( + "bench_std_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + + m.bench_function[bench_heap_sort]( + BenchId( + "bench_heap_sort_random_list_" + + str(count) + + "_type_" + + str(dt) + ) + ) + _ = list^ + + # ===----------------------------------------------------------------------===# # Benchmark Main # ===----------------------------------------------------------------------===# @@ -234,5 +288,6 @@ def main(): bench_tiny_list_sort(m) bench_small_list_sort(m) + bench_large_list_sort(m) m.dump_report() diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 402d68c6a4..c8bf888965 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -128,6 +128,37 @@ fn _partition[ return right +@always_inline +fn _heap_sort_fix_down[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], size: Int, idx: Int): + var i = idx + var j = i * 2 + 1 + while j < size: # has left child + # if right child exist and has higher value, swap with right + if i * 2 + 2 < size and cmp_fn(array[j], array[i * 2 + 2]): + j = i * 2 + 2 + if not cmp_fn(array[i], array[j]): + return + swap(array[j], array[i]) + i = j + j = i * 2 + 1 + + +@always_inline +fn _heap_sort[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], owned size: Int): + # heapify + for i in range(size // 2 - 1, -1, -1): + _heap_sort_fix_down[type, cmp_fn](array, size, i) + # sort + while size > 1: + size -= 1 + swap(array[0], array[size]) + _heap_sort_fix_down[type, cmp_fn](array, size, 0) + + @always_inline fn _estimate_initial_height(size: Int) -> Int: # Compute the log2 of the size rounded upward. From 93671c3da7a10c069e1bbbbb17918defd7e7fa13 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:58:50 -0500 Subject: [PATCH 1171/2019] [External] [Stdlib] Speedup `Dict` in `_maybe_resize` (#43072) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [Stdlib] Speedup `Dict` in `_maybe_resize` Here is a PR to remove a small recursion. Specifically, in `_maybe_resize`, when overloaded, we double the capacity, and when re-inserting the entries, `_insert` uses `_maybe_resize` again. Theses two checks are performed: - `_over_load_factor` - `_over_compact_factor` (Before, they were using modulo, so it can explain the massive speedup we had) We can grab a few performance by not doing those when resizing up :+1:   ### ⏱️ Benchmark 🔥 1.29x on small dictionary growing from `1<<3` to `1<<9` 2368375 **PR** 32640 3059269 32640 ```mojo from time import now def main(): var dict_size = 1<<8 var x = Dict[Int,Int]() var start = now() var stop = now() start = now() for n in range(dict_size): x = Dict[Int,Int]() for i in range(dict_size): x[i] = i stop = now() print(stop-start) var result = 0 for i in range(len(x)): result+=x[i] print(result) ```   Because we are re-inserting 'already compared' entries, we could use `_find_empty_slot` to do the job faster! (equality check for `K` is not necessary since they never the same) (another PR later after tests) Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3164 MODULAR_ORIG_COMMIT_REV_ID: b77484a6d0e96573926a1e370546e237179fc41b --- stdlib/src/collections/dict.mojo | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index b3a6d89c1f..aa535d999f 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -895,8 +895,12 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _insert(inout self, owned key: K, owned value: V): self._insert(DictEntry[K, V](key^, value^)) - fn _insert(inout self, owned entry: DictEntry[K, V]): - self._maybe_resize() + fn _insert[ + safe_context: Bool = False + ](inout self, owned entry: DictEntry[K, V]): + @parameter + if not safe_context: + self._maybe_resize() var found: Bool var slot: Int var index: Int @@ -966,7 +970,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( for i in range(len(old_entries)): var entry = old_entries.__get_ref(i) if entry[]: - self._insert(entry[].unsafe_take()) + self._insert[safe_context=True](entry[].unsafe_take()) fn _compact(inout self): self._index = _DictIndex(self._reserved()) From 33608af3111591d434846a1fe15b5d008fb876e2 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 9 Jul 2024 13:25:51 -0400 Subject: [PATCH 1172/2019] [stdlib] Clean up uses of `memset` and `memset_zero`. The `LegacyPointer` -taking version is almost never used. We should use the `UnsafePointer`-taking version instead. MODULAR_ORIG_COMMIT_REV_ID: 49e8851736290bb7afc4e45f7ad89cb3849f52ed --- docs/changelog.md | 3 ++- stdlib/src/memory/memory.mojo | 40 ++++------------------------------- 2 files changed, 6 insertions(+), 37 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 47a81a404c..461d08c8c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -416,7 +416,8 @@ what we publish. - `LegacyPointer.load/store` are now removed. It's use is replaced with `__getitem__` or `__setitem__`. -- `memcmp` no longer takes in `LegacyPointer`, instead, use `UnsafePointer`. +- `memcmp`, `memset` and `memset_zero` no longer take in `LegacyPointer`, + instead, use `UnsafePointer`. ### ❌ Removed diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 313f37280e..eab6dc8798 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -395,7 +395,10 @@ fn memset[ value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ - memset(ptr.address, value, count) + # TODO (43028) call memset when DTypePointer's underlying data uses UnsafePointer + _memset_llvm( + ptr.address.bitcast[UInt8]().address, value, count * sizeof[type]() + ) @always_inline @@ -416,24 +419,6 @@ fn memset[ _memset_llvm(ptr.bitcast[UInt8](), value, count * sizeof[type]()) -@always_inline -fn memset[ - type: AnyTrivialRegType, address_space: AddressSpace -](ptr: LegacyPointer[type, address_space], value: UInt8, count: Int): - """Fills memory with the given value. - - Parameters: - type: The element dtype. - address_space: The address space of the pointer. - - Args: - ptr: UnsafePointer to the beginning of the memory block to fill. - value: The value to fill with. - count: Number of elements to fill (in elements, not bytes). - """ - _memset_llvm(ptr.bitcast[UInt8]().address, value, count * sizeof[type]()) - - # ===----------------------------------------------------------------------===# # memset_zero # ===----------------------------------------------------------------------===# @@ -473,23 +458,6 @@ fn memset_zero[ memset(ptr, 0, count) -@always_inline -fn memset_zero[ - type: AnyTrivialRegType, address_space: AddressSpace -](ptr: LegacyPointer[type, address_space], count: Int): - """Fills memory with zeros. - - Parameters: - type: The element type. - address_space: The address space of the pointer. - - Args: - ptr: UnsafePointer to the beginning of the memory block to fill. - count: Number of elements to fill (in elements, not bytes). - """ - memset(ptr, 0, count) - - # ===----------------------------------------------------------------------===# # stack_allocation # ===----------------------------------------------------------------------===# From 68acdc0ce38fd370a6da01e520012b5290c8af30 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 13:29:45 -0500 Subject: [PATCH 1173/2019] [stdlib] Make `Slice` conform to `CollectionElementNew` Make `Slice` conform to `CollectionElementNew`, so that callers can use `InlineArray` with `Slice` since `InlineArray` mandates its types are explicitly copyable. This may not be desired in the long-term (we should have a more cohesive story for cheaply copyable types like `Slice`). However, this is important to unblock ongoing work to get `Slice` off of using `OptionalReg` and updated to use `Optional`. MODULAR_ORIG_COMMIT_REV_ID: 727534c8ebef130d8483a9d9f55e12ac179872f9 --- stdlib/src/builtin/builtin_slice.mojo | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 577227036c..398c06e55b 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -28,7 +28,13 @@ fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: @register_passable("trivial") -struct Slice(Stringable, EqualityComparable, Representable, Formattable): +struct Slice( + Stringable, + EqualityComparable, + Representable, + Formattable, + CollectionElementNew, +): """Represents a slice expression. Objects of this type are generated when slice syntax is used within square @@ -80,6 +86,14 @@ struct Slice(Stringable, EqualityComparable, Representable, Formattable): self.end = end self.step = step.value() if step else 1 + fn __init__(inout self, *, other: Self): + """Creates a deep copy of the Slice. + + Args: + other: The slice to copy. + """ + self.__init__(start=other.start, end=other.end, step=other.step) + @no_inline fn __str__(self) -> String: """Gets the string representation of the span. From 02ab421e8941f2abcf1cefc45a381fe87c8fbedc Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Tue, 9 Jul 2024 13:54:04 -0500 Subject: [PATCH 1174/2019] [External] [stdlib] Disallow implicit float to int conversions in SIMD types (#43040) Disallow this kind of implicit conversion: ```mojo var x: Int64 = 1.679 # x will be 1, so we lose 0.679 var y: Int64 = Float64(2.123) # y will be 2 ``` Related to issue https://github.com/modularml/mojo/issues/3149 Closes modularml/mojo#3154 MODULAR_ORIG_COMMIT_REV_ID: 6990fb8bcc2f9bcd45cbb0bb70c9a8f4cd0f6510 --- stdlib/src/builtin/simd.mojo | 6 ++++++ stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/testing/test_assertion.mojo | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 4b799faf6a..783e3cec0f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -216,6 +216,9 @@ struct SIMD[type: DType, size: Int]( value: The input value. """ _simd_construction_checks[type, size]() + constrained[ + type.is_floating_point(), "the SIMD type must be floating point" + ]() var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] @@ -390,6 +393,9 @@ struct SIMD[type: DType, size: Int]( value: The input value. """ _simd_construction_checks[type, size]() + constrained[ + type.is_floating_point(), "the SIMD type must be floating point" + ]() # TODO (#36686): This introduces unneeded casts here to work around # parameter if issues. diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 3167260c33..19127cd9bd 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -64,7 +64,7 @@ def test_file_read_bytes_multi(): assert_equal(string2, "dolor ") # Read where N is greater than the number of bytes in the file. - var s: String = f.read(1e9) + var s: String = f.read(1_000_000_000) assert_equal(len(s), 936) assert_true(s.startswith("sit amet, consectetur adipiscing elit.")) diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 1f9dce6aad..120f851063 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -146,7 +146,7 @@ def test_assert_almost_equal(): _should_fail[DType.bool, 1](True, False) _should_fail( - SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, -1), atol=5.0 + SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, -1), atol=5 ) _should_fail( SIMD[float_type, 2](-_inf, 0.0), From 8843e5fb945bd80a40887e43fd03e81e065e2072 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:03:18 -0400 Subject: [PATCH 1175/2019] [stdlib] Improve `_partition` implementation. The benchmarks are run on Apple M3. This PR removes the overhead for initializing a `List` in `_quicksort` by directly calling `_small_sort` or `_insertion_sort` for small lists. ``` bench_std_sort_random_list_5_type_uint8",0.00023595638360713315,313185 "bench_std_sort_random_list_5_type_uint8",0.00015298106077138139,779018 "bench_std_sort_random_list_5_type_uint8",0.00015954332663839353,790149 "bench_sml_sort_random_list_5_type_uint8",0.00016149757214507958,713181 "bench_sml_sort_random_list_5_type_uint8",0.00016289847660978758,750891 "bench_sml_sort_random_list_5_type_uint8",0.00015807081274565597,752068 "bench_std_sort_random_list_20_type_float64",0.00025977859454907496,455996 "bench_std_sort_random_list_20_type_float64",0.00025917298884732369,457648 "bench_std_sort_random_list_20_type_float64",0.00025908579828587175,457142 "bench_ins_sort_random_list_20_type_float64",0.00027385985551736181,435208 "bench_ins_sort_random_list_20_type_float64",0.00027517807273159955,428617 "bench_ins_sort_random_list_20_type_float64",0.00027440038931244643,431530 ``` original benchmark: #42556 The current implementation does a lot of unnecessary conditional checking. The new implementation of `_quicksort_partition` removes unnecesary checks in the main loop of partition. The improved version now runs at most 2x longer than `_heap_sort` and outperforms `_heap_sort` for larger types. ``` "bench_std_sort_random_list_65536_type_uint8",4.2574356435643566,101 "bench_std_sort_random_list_65536_type_uint8",4.1786930693069309,101 "bench_std_sort_random_list_65536_type_uint8",4.2674653465346539,101 "bench_heap_sort_random_list_65536_type_uint8",1.9663663366336634,101 "bench_heap_sort_random_list_65536_type_uint8",1.992881188118812,101 "bench_heap_sort_random_list_65536_type_uint8",1.9536930693069308,101 "bench_std_sort_random_list_65536_type_uint64",2.3269900990099011,101 "bench_std_sort_random_list_65536_type_uint64",2.3153267326732676,101 "bench_std_sort_random_list_65536_type_uint64",2.3153168316831683,101 "bench_heap_sort_random_list_65536_type_uint64",2.6635940594059409,101 "bench_heap_sort_random_list_65536_type_uint64",2.6634950495049501,101 "bench_heap_sort_random_list_65536_type_uint64",2.6651188118811882,101 ``` MODULAR_ORIG_COMMIT_REV_ID: 8ba4f4230c9afe1a65a769df97b580926acf9168 --- stdlib/benchmarks/builtin/bench_sort.mojo | 12 +-- stdlib/src/builtin/sort.mojo | 122 ++++++++++++++++------ stdlib/test/builtin/test_sort.mojo | 30 ++---- 3 files changed, 108 insertions(+), 56 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index cab3a345f8..b49c125eb7 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -60,10 +60,10 @@ fn insertion_sort[type: DType](list: List[Scalar[type]]): var ptr = rebind[Pointer[Scalar[type]]](list.data) @parameter - fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) - _insertion_sort[Scalar[type], _less_than_equal](ptr, 0, len(list)) + _insertion_sort[Scalar[type], _less_than](ptr, 0, len(list)) @always_inline @@ -71,10 +71,10 @@ fn small_sort[size: Int, type: DType](list: List[Scalar[type]]): var ptr = rebind[Pointer[Scalar[type]]](list.data) @parameter - fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) - _small_sort[size, Scalar[type], _less_than_equal](ptr) + _small_sort[size, Scalar[type], _less_than](ptr) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index c8bf888965..02b7596e12 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -68,6 +68,30 @@ fn _insertion_sort[ array[j] = value +# put everything thats "<" to the left of pivot +@always_inline +fn _quicksort_partition[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], start: Int, end: Int) -> Int: + var left = start + 1 + var right = end - 1 + var pivot_value = array[start] + + while True: + # no need for left < right since quick sort pick median of 3 as pivot + while cmp_fn(array[left], pivot_value): + left += 1 + while left < right and not cmp_fn(array[right], pivot_value): + right -= 1 + if left >= right: + var pivot_pos = left - 1 + swap(array[pivot_pos], array[start]) + return pivot_pos + swap(array[left], array[right]) + left += 1 + right -= 1 + + @always_inline fn _partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type @@ -166,6 +190,27 @@ fn _estimate_initial_height(size: Int) -> Int: return max(2, log2) +@always_inline +fn _delegate_small_sort[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], len: Int): + if len == 2: + _small_sort[2, type, cmp_fn](array) + return + + if len == 3: + _small_sort[3, type, cmp_fn](array) + return + + if len == 4: + _small_sort[4, type, cmp_fn](array) + return + + if len == 5: + _small_sort[5, type, cmp_fn](array) + return + + @always_inline fn _quicksort[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type @@ -181,36 +226,26 @@ fn _quicksort[ var start = stack.pop() var len = end - start - if len < 2: - continue - - if len == 2: - _small_sort[2, type, cmp_fn](array + start) - continue - - if len == 3: - _small_sort[3, type, cmp_fn](array + start) - continue - if len == 4: - _small_sort[4, type, cmp_fn](array + start) - continue - - if len == 5: - _small_sort[5, type, cmp_fn](array + start) + if len <= 5: + _delegate_small_sort[type, cmp_fn](array + start, len) continue if len < 32: _insertion_sort[type, cmp_fn](array, start, end) continue - var pivot = _partition[type, cmp_fn](array, start, end) + # pick median of 3 as pivot + _sort3[type, cmp_fn](array, (start + end) >> 1, start, end - 1) + var pivot = _quicksort_partition[type, cmp_fn](array, start, end) - stack.append(pivot + 1) - stack.append(end) + if end > pivot + 2: + stack.append(pivot + 1) + stack.append(end) - stack.append(start) - stack.append(pivot) + if pivot > start + 1: + stack.append(start) + stack.append(pivot) @always_inline @@ -251,7 +286,7 @@ fn partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](buff: Pointer[type], k: Int, size: Int): """Partition the input buffer inplace such that first k elements are the - largest (or smallest if cmp_fn is <= operator) elements. + largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: @@ -296,10 +331,18 @@ fn sort(inout buff: Pointer[Int], len: Int): """ @parameter - fn _less_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) <= rebind[Int](rhs) + fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) < rebind[Int](rhs) + + if len <= 5: + _delegate_small_sort[Int, _less_than](buff, len) + return + + if len < 32: + _insertion_sort[Int, _less_than](buff, 0, len) + return - _quicksort[Int, _less_than_equal](buff, len) + _quicksort[Int, _less_than](buff, len) fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): @@ -316,10 +359,18 @@ fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): """ @parameter - fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) + + if len <= 5: + _delegate_small_sort[Scalar[type], _less_than](buff, len) + return + + if len < 32: + _insertion_sort[Scalar[type], _less_than](buff, 0, len) + return - _quicksort[Scalar[type], _less_than_equal](buff, len) + _quicksort[Scalar[type], _less_than](buff, len) fn sort(inout list: List[Int]): @@ -378,10 +429,10 @@ fn sort[type: ComparableCollectionElement](inout list: List[type]): """ @parameter - fn _less_than_equal(a: type, b: type) -> Bool: - return a <= b + fn _less_than(a: type, b: type) -> Bool: + return a < b - _quicksort[type, _less_than_equal](list.data, len(list)) + _quicksort[type, _less_than](list.data, len(list)) # ===----------------------------------------------------------------------===# @@ -400,6 +451,15 @@ fn _sort2[ array[offset1] = a +@always_inline +fn _sort3[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], offset0: Int, offset1: Int, offset2: Int): + _sort2[type, cmp_fn](array, offset0, offset1) + _sort2[type, cmp_fn](array, offset1, offset2) + _sort2[type, cmp_fn](array, offset0, offset1) + + @always_inline fn _sort_partial_3[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index c37badc503..f8a7c17ee2 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -77,11 +77,11 @@ fn test_sort_small_3() raises: list.append(2) @parameter - fn _less_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) <= rebind[Int](rhs) + fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) < rebind[Int](rhs) var ptr = rebind[Pointer[Int]](list.data) - _small_sort[length, Int, _less_than_equal](ptr) + _small_sort[length, Int, _less_than](ptr) var expected = List[Int](1, 2, 9) for i in range(length): @@ -100,11 +100,11 @@ fn test_sort_small_5() raises: list.append(4) @parameter - fn _less_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) <= rebind[Int](rhs) + fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) < rebind[Int](rhs) var ptr = rebind[Pointer[Int]](list.data) - _small_sort[length, Int, _less_than_equal](ptr) + _small_sort[length, Int, _less_than](ptr) var expected = List[Int](1, 2, 3, 4, 9) for i in range(length): @@ -187,12 +187,7 @@ fn test_sort3_dupe_elements() raises: fn _lt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: return rebind[Int](lhs) < rebind[Int](rhs) - @parameter - fn _leq[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) <= rebind[Int](rhs) - test[_lt]() - test[_leq]() fn test_sort4() raises: @@ -432,15 +427,14 @@ fn test_partition_top_k(length: Int, k: Int) raises: list.append(i) @parameter - fn _great_than_equal[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Float32](lhs) >= rebind[Float32](rhs) + fn _great_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: + return rebind[Float32](lhs) > rebind[Float32](rhs) var ptr = rebind[Pointer[Float32]](list.data) - _ = partition[Float32, _great_than_equal](ptr, k, len(list)) + _ = partition[Float32, _great_than](ptr, k, len(list)) for i in range(0, k): - if list[i] < length - k: - assert_true(False) + assert_false(list[i] < length - k) fn test_sort_stress() raises: @@ -487,9 +481,7 @@ fn test_sort_stress() raises: for i in range(len(lens)): var length = lens[i] test[_gt, _geq](length) - test[_geq, _geq](length) test[_lt, _leq](length) - test[_leq, _leq](length) @value @@ -506,7 +498,7 @@ fn test_sort_custom() raises: @parameter fn compare_fn(lhs: MyStruct, rhs: MyStruct) -> Bool: - return lhs.val <= rhs.val + return lhs.val < rhs.val sort[MyStruct, compare_fn](list) From 9d377745967b865e4660de1e1a1a980b8a99a824 Mon Sep 17 00:00:00 2001 From: Tatiana Shpeisman Date: Tue, 9 Jul 2024 12:16:06 -0700 Subject: [PATCH 1176/2019] ] [stdlib] Remove DType._get_runtime_dtype_size. Removes `DType._get_runtime_dtype_size` and replaces its uses with `DType.sizeof`. The two functions implement the same functionality.`DType.sizeof` is more efficient. [ MODULAR_ORIG_COMMIT_REV_ID: 5b5b6904a1d9213f9c653853e9b86ae0642f997d --- stdlib/src/builtin/dtype.mojo | 37 ----------------------------------- 1 file changed, 37 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index f84bc0994e..c2ae4a1091 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -644,40 +644,3 @@ fn _get_dtype_printf_format[type: DType]() -> StringLiteral: constrained[False, "invalid dtype"]() return "" - - -fn _get_runtime_dtype_size(type: DType) -> Int: - """ - Get the size of the dynamic dtype. - - We cannot directly using type.sizeof(), since that only works with - statically known dtypes. Instead, we have to perform a dispatch to - determine the size of the dtype. - """ - alias type_list = List[DType]( - DType.bool, - DType.int8, - DType.uint8, - DType.int16, - DType.uint16, - DType.bfloat16, - DType.float16, - DType.int32, - DType.uint32, - DType.float32, - DType.tensor_float32, - DType.int64, - DType.uint64, - DType.float64, - DType.index, - ) - - @parameter - for idx in range(len(type_list)): - alias concrete_type = type_list[idx] - if concrete_type == type: - return sizeof[concrete_type]() - - abort("unable to get the dtype size of " + str(type)) - - return -1 From 03a5e53f57c9b6f24898f9f6c9bfdfadfe46ee77 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 15:31:00 -0500 Subject: [PATCH 1177/2019] [stdlib] Convert remaining `LegacyPointer.address_of` Convert remaining uses of `LegacyPointer.address_of` to use `UnsafePointer.address_of` to help with migrating off of `Pointer`/`LegacyPointer`. MODULAR_ORIG_COMMIT_REV_ID: 89ddfab5c3da44347dfcb4c3d9ce1637c357fe06 --- stdlib/src/memory/unsafe.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 46f234311b..eb46575efb 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -612,7 +612,7 @@ struct DTypePointer[ Returns: A DTypePointer struct which contains the address of the argument. """ - return LegacyPointer.address_of(arg) + return UnsafePointer.address_of(arg) @staticmethod @always_inline From 8c259bc893f9914c4cc80e742e09171675808052 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:57:28 -0400 Subject: [PATCH 1178/2019] [stdlib] Benchmark `sort` with lists with low delta The benchmarks are run on Apple M3. If we have a list of all 1's, our partition scheme will put the pivot as the first element no matter what. This results in `_quicksort` being $\mathcal{O}(N^2)$. ``` "bench_std_sort_low_card_list_4096_type_",0.21546703296703296,546 "bench_std_sort_low_card_list_4096_type_",0.21502727272727273,550 "bench_std_sort_low_card_list_4096_type_",0.21541272727272728,550 "bench_heap_sort_low_card_list_4096_type_",0.112399,1000 "bench_heap_sort_low_card_list_4096_type_",0.081822358346094942,1306 "bench_heap_sort_low_card_list_4096_type_",0.082701815642458099,1432 "bench_std_sort_low_card_list_65536_type_",29.717089108910891,101 "bench_std_sort_low_card_list_65536_type_",30.25737623762376,101 "bench_std_sort_low_card_list_65536_type_",30.345722772277227,101 "bench_heap_sort_low_card_list_65536_type_",1.9583366336633663,101 "bench_heap_sort_low_card_list_65536_type_",1.9567821782178216,101 "bench_heap_sort_low_card_list_65536_type_",1.9556534653465345,101 ``` MODULAR_ORIG_COMMIT_REV_ID: 994a71ac7fe08301918537d97aa879a62b6c49ce --- stdlib/benchmarks/builtin/bench_sort.mojo | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index b49c125eb7..e07369c3ec 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -277,6 +277,65 @@ fn bench_large_list_sort(inout m: Bench) raises: _ = list^ +# ===----------------------------------------------------------------------===# +# Benchmark sort functions with low delta lists +# ===----------------------------------------------------------------------===# + + +@parameter +fn bench_low_cardinality_list_sort(inout m: Bench) raises: + alias counts = List(1 << 12, 1 << 16) + alias deltas = List(0, 2, 5, 20, 100) + + @parameter + for delta_index in range(len(deltas)): + var delta = deltas[delta_index] + + @parameter + for count_index in range(len(counts)): + alias count = counts[count_index] + var list = random_scalar_list[DType.uint8](count, delta) + + @parameter + fn bench_sort_list(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + sort(l1) + + b.iter[call_fn]() + + @parameter + fn bench_heap_sort(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var l1 = list + heap_sort(l1) + + b.iter[call_fn]() + + m.bench_function[bench_sort_list]( + BenchId( + "bench_std_sort_low_card_list_" + + str(count) + + "_delta_" + + str(delta) + ) + ) + + m.bench_function[bench_heap_sort]( + BenchId( + "bench_heap_sort_low_card_list_" + + str(count) + + "_delta_" + + str(delta) + ) + ) + _ = list^ + + # ===----------------------------------------------------------------------===# # Benchmark Main # ===----------------------------------------------------------------------===# @@ -289,5 +348,6 @@ def main(): bench_tiny_list_sort(m) bench_small_list_sort(m) bench_large_list_sort(m) + bench_low_cardinality_list_sort(m) m.dump_report() From 6619711d74f9d7e8bb4ad4dd2e80b74b2d6aee39 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 9 Jul 2024 15:30:59 -0700 Subject: [PATCH 1179/2019] [Docs] Remove unmaintained docs. MODULAR_ORIG_COMMIT_REV_ID: 6f7f9c25f810bc0bd40f6a3357c89fb2d851d234 --- examples/notebooks/HelloMojo.ipynb | 696 ---- examples/notebooks/programming-manual.ipynb | 3425 ------------------- 2 files changed, 4121 deletions(-) delete mode 100644 examples/notebooks/HelloMojo.ipynb delete mode 100644 examples/notebooks/programming-manual.ipynb diff --git a/examples/notebooks/HelloMojo.ipynb b/examples/notebooks/HelloMojo.ipynb deleted file mode 100644 index 43c01875cf..0000000000 --- a/examples/notebooks/HelloMojo.ipynb +++ /dev/null @@ -1,696 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "---\n", - "title: Mojo language basics\n", - "sidebar_label: Language basics\n", - "description: A short introduction to the Mojo language basics.\n", - "css: /static/styles/page-navigation.css\n", - "aliases:\n", - " - /mojo/notebooks/HelloMojo.html\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[//]: # REMOVE_FOR_WEBSITE\n", - "*Copyright 2023 Modular, Inc: Licensed under the Apache License v2.0 with LLVM Exceptions.*" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[//]: # REMOVE_FOR_WEBSITE\n", - "# Mojo language basics" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo is a powerful programming language that's primarily designed for\n", - "high-performance systems programming, so it has a lot in common with other\n", - "systems languages like Rust and C++. Yet, Mojo is also designed to become a\n", - "superset of Python, so a lot of language features and concepts you might know\n", - "from Python translate nicely to Mojo. \n", - "\n", - "For example, if you're in a REPL environment or Jupyter notebook (like this\n", - "document), you can run top-level code just like Python:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello Mojo!\n" - ] - } - ], - "source": [ - "#| CHECK: Hello Mojo!\n", - "print(\"Hello Mojo!\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You don't normally see that with other systems programming languages.\n", - "\n", - "Mojo preserves Python's dynamic features and language syntax, and it even\n", - "allows you to import and run code from Python packages. However, it's important\n", - "to know that Mojo is an entirely new language, not just a new implementation of\n", - "Python with syntax sugar. Mojo takes the Python language to a whole new level,\n", - "with systems programming features, strong type-checking, memory safety,\n", - "next-generation compiler technologies, and more. Yet, it's still designed to be\n", - "a simple language that's useful for general-purpose programming.\n", - "\n", - "This page provides a gentle introduction to the Mojo language, and requires\n", - "only a little programming experience. So let's get started!\n", - "\n", - "For more details about everything covered here, check out the\n", - "[Mojo Manual](https://docs.modular.com/mojo/manual/)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Language basics\n", - "\n", - "First and foremost, Mojo is a compiled language and a lot of its performance\n", - "and memory-safety features are derived from that fact. Mojo code can be\n", - "ahead-of-time (AOT) or just-in-time (JIT) compiled.\n", - "\n", - "Like other compiled languages, Mojo programs (`.mojo` or `.🔥` files) require a\n", - "`main()` function as the entry point to the program. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "fn main():\n", - " var x: Int = 1\n", - " x += 1\n", - " print(x)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you know Python, you might have expected the function name to be `def\n", - "main()` instead of `fn main()`. Both actually work in Mojo, but using `fn`\n", - "behaves a bit differently, as we'll discuss below.\n", - "\n", - "Of course, if you're building a Mojo module (an API library), not a Mojo\n", - "program, then your file doesn't need a `main()` function (because it will be\n", - "imported by other programs that do have one).\n", - "\n", - "
    \n", - "\n", - "**Note:** When you're writing code in a `.mojo`/`.🔥` file, you can't run\n", - "top-level code as shown on this page—all code in a Mojo program or module\n", - "must be encased in a function or struct. However, top-level code does work in a\n", - "REPL or Jupyter notebook (such as the [notebook for this\n", - "page](https://github.com/modularml/mojo/blob/main/examples/notebooks/HelloMojo.ipynb)).\n", - "\n", - "
    \n", - "\n", - "Now let's explain the code in this `main()` function." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Syntax and semantics\n", - "\n", - "This is simple: Mojo supports (or will support) all of Python's syntax and\n", - "semantics. If you're not familiar with Python syntax, there are a ton of great\n", - "resources online that can teach you.\n", - "\n", - "For example, like Python, Mojo uses line breaks and indentation to define code\n", - "blocks (not curly braces), and Mojo supports all of Python's control-flow syntax\n", - "such as `if` conditions and `for` loops.\n", - "\n", - "However, Mojo is still a work in progress, so there are some things from Python\n", - "that aren't implemented in Mojo yet (see the [Mojo\n", - "roadmap](https://docs.modular.com/mojo/roadmap.html)). All the missing Python\n", - "features will arrive in time, but Mojo already includes many features and\n", - "capabilities beyond what's available in Python.\n", - "\n", - "As such, the following sections will focus on some of the language features that\n", - "are unique to Mojo (compared to Python)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Functions\n", - "\n", - "Mojo functions can be declared with either `fn` (shown above) or `def` (as\n", - "in Python). The `fn` declaration enforces strongly-typed and memory-safe\n", - "behaviors, while `def` provides Python-style dynamic behaviors.\n", - "\n", - "Both `fn` and `def` functions have their value, and it's important that you\n", - "learn them both. However, for the purposes of this introduction, we're going to\n", - "focus on `fn` functions only. For more detail about both, see the [functions\n", - "page in the manual](https://docs.modular.com/mojo/manual/functions.html).\n", - "\n", - "In the following sections, you'll learn how `fn` functions enforce\n", - "strongly-typed and memory-safe behaviors in your code." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Variables\n", - "\n", - "You can declare variables (such as `x` in the above `main()` function) with\n", - "`var` to create a mutable value, or with `let` to create an immutable value.\n", - "\n", - "If you change `var` to `let` in the `main()` function above and run it, you'll\n", - "get a compiler error like this:\n", - "\n", - "```text\n", - "error: Expression [15]:7:5: expression must be mutable for in-place operator destination\n", - " x += 1\n", - " ^\n", - "```\n", - "\n", - "That's because `let` makes the value immutable, so you can't increment it.\n", - "\n", - "And if you delete `var` completely, you'll get an error because `fn` functions\n", - "require explicit variable declarations (unlike Python-style `def` functions).\n", - "\n", - "Finally, notice that the `x` variable has an explicit `Int` type specification.\n", - "Declaring the type is not required for variables in `fn`, but it is desirable\n", - "sometimes. If you omit it, Mojo infers the type, as shown here:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n" - ] - } - ], - "source": [ - "fn do_math():\n", - " var x: Int = 1\n", - " var y = 2\n", - " print(x + y)\n", - "\n", - "do_math()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Function arguments and returns\n", - "\n", - "Although types aren't required for variables declared in the function body,\n", - "they are required for arguments and return values for an `fn` function.\n", - "\n", - "For example, here's how to declare `Int` as the type for function arguments and\n", - "the return value:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n" - ] - } - ], - "source": [ - "fn add(x: Int, y: Int) -> Int:\n", - " return x + y\n", - "\n", - "z = add(1, 2)\n", - "print(z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Optional arguments and keyword arguments\n", - "\n", - "You can also specify argument default values (also known as optional\n", - "arguments), and pass values with keyword argument names. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "9\n", - "8\n" - ] - } - ], - "source": [ - "fn my_pow(base: Int, exp: Int = 2) -> Int:\n", - " return base ** exp\n", - "\n", - "# Uses default value for `exp`\n", - "z = my_pow(3)\n", - "print(z)\n", - "\n", - "# Uses keyword argument names (with order reversed)\n", - "z = my_pow(exp=3, base=2)\n", - "print(z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
    \n", - "\n", - "**Note:** Mojo currently includes only partial support for keyword arguments, so\n", - "some features such as keyword-only arguments and variadic keyword arguments (e.g. `**kwargs`)\n", - "are not supported yet.\n", - "\n", - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Argument mutability and ownership\n", - "\n", - "Mojo supports full [value\n", - "semantics](https://en.wikipedia.org/wiki/Value_semantics) and enforces memory\n", - "safety with a robust value ownership model (similar to the Rust borrow\n", - "checker). Essentially, that means Mojo allows you to share references to values\n", - "(instead of making a copy every time you pass a value to a function), but doing\n", - "so requires that you follow Mojo's ownership rules (to ensure memory safety) as\n", - "described in this section.\n", - "\n", - "Notice that, above, `add()` doesn't modify `x` or `y`, it only reads the\n", - "values. In fact, as written, the function *cannot* modify them because `fn`\n", - "arguments are **immutable references** by default. This ensures memory safety\n", - "(no surprise changes to the data) while also avoiding a copy (which could be\n", - "a performance hit).\n", - "\n", - "In terms of argument conventions, this is called \"borrowing,\" and although it's\n", - "the default for `fn` functions, you can make it explicit with the `borrowed`\n", - "declaration like this (this behaves exactly the same as the `add()` above):" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "fn add(borrowed x: Int, borrowed y: Int) -> Int:\n", - " return x + y" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you want the arguments to be mutable, you need to declare each argument\n", - "convention as `inout`. This means that changes made to the arguments *in*side\n", - "the function are visible *out*side the function. \n", - "\n", - "For example, this function is able to modify the original variables:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2\n", - "3\n", - "5\n" - ] - } - ], - "source": [ - "fn add_inout(inout x: Int, inout y: Int) -> Int:\n", - " x += 1\n", - " y += 1\n", - " return x + y\n", - "\n", - "var a = 1\n", - "var b = 2\n", - "c = add_inout(a, b)\n", - "print(a)\n", - "print(b)\n", - "print(c)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another option is to declare the argument as `owned`, which provides\n", - "the function full ownership of the value (it's mutable and guaranteed unique).\n", - "This way, the function can modify the value and not worry about affecting\n", - "variables outside the function. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "mojo\n", - "mojo🔥\n" - ] - } - ], - "source": [ - "fn set_fire(owned text: String) -> String:\n", - " text += \"🔥\"\n", - " return text\n", - "\n", - "fn mojo():\n", - " var a: String = \"mojo\"\n", - " var b = set_fire(a)\n", - " print(a)\n", - " print(b)\n", - "\n", - "mojo()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this case, Mojo makes a copy of `a` and passes it as the `text` argument.\n", - "The original `a` string is still alive and well.\n", - "\n", - "However, if you want to give the function ownership of the value and **do not**\n", - "want to make a copy (which can be an expensive operation for some types), then\n", - "you can add the `^` \"transfer\" operator when you pass `a` to the function. The\n", - "transfer operator effectively destroys the local variable name—any attempt to\n", - "call upon it later causes a compiler error.\n", - "\n", - "Try it above by changing the call to `set_fire()` to look like this:\n", - "\n", - "```mojo\n", - " var b = set_fire(a^)\n", - "```\n", - "\n", - "You'll now get an error because the transfer operator effectively destroys the\n", - "`a` variable, so when the following `print()` function tries to use `a`, that\n", - "variable isn't initialized anymore.\n", - "\n", - "If you delete `print(a)`, then it works fine.\n", - "\n", - "These argument conventions are designed to provide systems programmers with\n", - "total control for memory optimizations while ensuring safe access and timely\n", - "deallocations—the Mojo compiler ensures that no two variables have mutable\n", - "access to the same value at the same time, and the lifetime of each value is\n", - "well-defined to strictly prevent any memory errors such as \"use-after-free\" and\n", - "\"double-free.\"\n", - "\n", - "
    \n", - "\n", - "**Note:** Currently, Mojo always makes a copy when a function returns a value.\n", - "\n", - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Structures\n", - "\n", - "You can build high-level abstractions for types (or \"objects\") in a `struct`. A\n", - "`struct` in Mojo is similar to a `class` in Python: they both support methods,\n", - "fields, operator overloading, decorators for metaprogramming, etc. However,\n", - "Mojo structs are completely static—they are bound at compile-time, so they do\n", - "not allow dynamic dispatch or any runtime changes to the structure. (Mojo will\n", - "also support classes in the future.)\n", - "\n", - "For example, here's a basic struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPair:\n", - " var first: Int\n", - " var second: Int\n", - "\n", - " fn __init__(inout self, first: Int, second: Int):\n", - " self.first = first\n", - " self.second = second\n", - "\n", - " fn dump(self):\n", - " print(self.first, self.second)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how you can use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 4\n" - ] - } - ], - "source": [ - "var mine = MyPair(2, 4)\n", - "mine.dump()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you're familiar with Python, then the `__init__()` method and the `self`\n", - "argument should be familiar to you. If you're _not_ familiar with Python, then\n", - "notice that, when we call `dump()`, we don't actually pass a value for the\n", - "`self` argument. The value for `self` is automatically provided with the\n", - "current instance of the struct (it's used similar to the `this` name used in\n", - "some other languages to refer to the current instance of the object/type).\n", - "\n", - "For more detail, see the sections of the Mojo Manual about\n", - "[structs](https://docs.modular.com/mojo/manual/basics/structs.html) and [value\n", - "lifecycle](https://docs.modular.com/mojo/manual/lifeclcye/)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Python integration\n", - "\n", - "Although Mojo is still a work in progress and is not a full superset of Python\n", - "yet, we've built a mechanism to import Python modules as-is, so you can\n", - "leverage existing Python code right away. Under the hood, this mechanism uses\n", - "the CPython interpreter to run Python code, and thus it works seamlessly with\n", - "all Python modules today.\n", - "\n", - "For example, here's how you can import and use NumPy (you must have Python\n", - "`numpy` installed):" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 0 1 2 3 4]\n", - " [ 5 6 7 8 9]\n", - " [10 11 12 13 14]]\n", - "(3, 5)\n" - ] - } - ], - "source": [ - "from python import Python\n", - "\n", - "var np = Python.import_module(\"numpy\")\n", - "\n", - "ar = np.arange(15).reshape(3, 5)\n", - "print(ar)\n", - "print(ar.shape)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
    \n", - "\n", - "**Note:** Mojo is not a feature-complete superset of Python yet. So, you can't\n", - "always copy Python code and run it in Mojo. For more details on our plans,\n", - "please refer to the [Mojo roadmap and sharp edges](/mojo/roadmap.html).\n", - "\n", - "
    " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
    \n", - "\n", - "**Caution:** When you install Mojo, the installer searches your system for a\n", - "version of Python to use with Mojo, and adds the path to the `modular.cfg`\n", - "config file. If you change your Python version or switch virtual environments,\n", - "Mojo will then be looking at the wrong Python library, which can cause problems\n", - "such as errors when you import Python packages (Mojo says only `An error\n", - "occurred in Python`—this is a separate [known\n", - "issue](https://github.com/modularml/mojo/issues/536)). The current solution is\n", - "to override Mojo's path to the Python library, using the `MOJO_PYTHON_LIBRARY`\n", - "environment variable. For instructions on how to find and set this path, see\n", - "[this related issue](https://github.com/modularml/mojo/issues/551).\n", - "\n", - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next steps\n", - "\n", - "We hope this page covered enough of the basics to get you started. It's\n", - "intentionally brief, so if you want more detail about any of the topics touched\n", - "upon here, check out the [Mojo\n", - "programming manual](https://docs.modular.com/mojo/programming-manual.html).\n", - "\n", - "- If you want to package your code as a library, read about\n", - " [Mojo modules and packages](/mojo/manual/get-started/packages.html).\n", - "\n", - "- If you want to explore some Mojo code, check out our\n", - " [code examples on GitHub](https://github.com/modularml/mojo/tree/main/examples#mojo-code-examples).\n", - "\n", - "- To see all the available Mojo APIs, check out the [Mojo standard library\n", - " reference](/mojo/lib.html)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
    \n", - "\n", - "**Note:** The Mojo SDK is still in early development. Some things are still\n", - "rough, but you can expect constant changes and improvements to both the\n", - "language and tools. Please see the [known\n", - "issues](/mojo/roadmap.html#mojo-sdk-known-issues) and [report any other\n", - "issues on GitHub](https://github.com/modularml/mojo/issues/new/choose).\n", - "\n", - "
    " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/notebooks/programming-manual.ipynb b/examples/notebooks/programming-manual.ipynb deleted file mode 100644 index f1ead404f5..0000000000 --- a/examples/notebooks/programming-manual.ipynb +++ /dev/null @@ -1,3425 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "---\n", - "title: Mojo🔥 programming manual (deprecated)\n", - "sidebar_label: Programming manual\n", - "description: A tour of major Mojo language features with code examples.\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", - "---\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[//]: # REMOVE_FOR_WEBSITE\n", - "*Copyright 2023 Modular, Inc: Licensed under the Apache License v2.0 with LLVM Exceptions.*" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[//]: # REMOVE_FOR_WEBSITE\n", - "# Mojo🔥 programming manual" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Mojo is a programming language that is as easy to use as Python but with the\n", - "performance of C++ and Rust. Furthermore, Mojo provides the ability to leverage\n", - "the entire Python library ecosystem.\n", - "\n", - "Mojo achieves this feat by utilizing next-generation compiler technologies with\n", - "integrated caching, multithreading, and cloud distribution technologies.\n", - "Furthermore, Mojo's autotuning and compile-time metaprogramming features allow\n", - "you to write code that is portable to even the most exotic hardware.\n", - "\n", - "More importantly, **Mojo allows you to leverage the entire Python ecosystem**\n", - "so you can continue to use tools you are familiar with. Mojo is designed to\n", - "become a **superset** of Python over time by preserving Python's dynamic\n", - "features while adding new primitives for [systems\n", - "programming](https://en.wikipedia.org/wiki/Systems_programming). These new\n", - "system programming primitives will allow Mojo developers to build\n", - "high-performance libraries that currently require C, C++, Rust, CUDA, and other\n", - "accelerator systems. By bringing together the best of dynamic languages and\n", - "systems languages, we hope to provide a **unified** programming model that\n", - "works across levels of abstraction, is friendly for novice programmers, and\n", - "scales across many use cases from accelerators through to application\n", - "programming and scripting.\n", - "\n", - "This document is an introduction to the Mojo programming language, not a\n", - "complete language guide. It assumes knowledge of Python and systems programming\n", - "concepts. At the moment, Mojo is still a work in progress and the documentation\n", - "is targeted to developers with systems programming experience. As the language\n", - "grows and becomes more broadly available, we intend for it to be friendly and\n", - "accessible to everyone, including beginner programmers. It's just not there\n", - "today.\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using the Mojo compiler\n", - "\n", - "With the [Mojo SDK](https://docs.modular.com/mojo/manual/get-started/), you can\n", - "run a Mojo program from a terminal just like you can with Python. So if you\n", - "have a file named `hello.mojo` (or `hello.🔥`—yes, the file extension can be an\n", - "emoji!), just type `mojo hello.mojo`:\n", - "\n", - "```mojo\n", - "$ cat hello.🔥\n", - "def main():\n", - " print(\"hello world\")\n", - " for x in range(9, 0, -3):\n", - " print(x)\n", - "$ mojo hello.🔥\n", - "hello world\n", - "9\n", - "6\n", - "3\n", - "$\n", - "```\n", - "\n", - "Again, you can use either the `.🔥` or `.mojo` suffix.\n", - "\n", - "For more details about the Mojo compiler tools, see the [`mojo` CLI\n", - "docs](https://docs.modular.com/mojo/cli/)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Basic systems programming extensions\n", - "\n", - "Given our goal of compatibility and Python's strength with high-level\n", - "applications and dynamic APIs, we don't have to spend much time explaining\n", - "how those portions of the language work. On the other hand, Python's support\n", - "for systems programming is mainly delegated to C, and we want to provide a\n", - "single system that is great in that world. As such, this section breaks down\n", - "each major component and feature and describes how to use them with examples.\n", - "\n", - "### `let` and `var` declarations\n", - "\n", - "Inside a `def` in Mojo, you may assign a value to a name and it implicitly\n", - "creates a function scope variable just like in Python. This provides a very\n", - "dynamic and low-ceremony way to write code, but it is a challenge for two\n", - "reasons:\n", - "\n", - "1) Systems programmers often want to declare that a value is immutable for\n", - " type-safety and performance.\n", - "2) They may want to get an error if they mistype a variable name in an\n", - " assignment.\n", - "\n", - "To support this, Mojo provides scoped runtime value declarations: `let` is\n", - "immutable, and `var` is mutable. These values use lexical scoping and support\n", - "name shadowing:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n" - ] - } - ], - "source": [ - "def your_function(a, b):\n", - " var c = a\n", - " # Uncomment to see an error:\n", - " # c = b # error: c is immutable\n", - "\n", - " if c != b:\n", - " var d = b\n", - " print(d)\n", - "\n", - "your_function(2, 3)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`let` and `var` declarations support type specifiers as well as patterns, and\n", - "late initialization:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0\n" - ] - } - ], - "source": [ - "def your_function():\n", - " var x: Int = 42\n", - " var y: Float64 = 17.0\n", - "\n", - " var z: Float32\n", - " if x != 0:\n", - " z = 1.0\n", - " else:\n", - " z = foo()\n", - " print(z)\n", - "\n", - "def foo() -> Float32:\n", - " return 3.14\n", - "\n", - "your_function()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that `let` and `var` are completely optional when in a `def` function\n", - "(you can instead use implicitly declared values, just like Python),\n", - "but they're required for all variables in an `fn` function.\n", - "\n", - "Also beware that when using Mojo in a REPL environment (such as this notebook),\n", - "top-level variables (variables that live outside a function or struct) are\n", - "treated like variables in a `def`, so they allow implicit value type\n", - "declarations (they do not require `var` or `let` declarations, nor type\n", - "declarations). This matches the Python REPL behavior.\n", - "\n", - "### `struct` types\n", - "\n", - "Mojo is based on MLIR and LLVM, which offer a cutting-edge compiler and code\n", - "generation system used in many programming languages. This lets us have better\n", - "control over data organization, direct access to data fields, and other ways to\n", - "improve performance. An important feature of modern systems programming\n", - "languages is the ability to build high-level and safe abstractions on top of\n", - "these complex, low-level operations without any performance loss. In Mojo, this\n", - "is provided by the `struct` type.\n", - "\n", - "A `struct` in Mojo is similar to a Python `class`: they both support methods,\n", - "fields, operator overloading, decorators for metaprogramming, etc. Their\n", - "differences are as follows:\n", - "\n", - "- Python classes are dynamic: they allow for dynamic dispatch, monkey-patching\n", - "(or \"swizzling\"), and dynamically binding instance properties at runtime.\n", - "\n", - "- Mojo structs are static: they are bound at compile-time (you cannot add\n", - "methods at runtime). Structs allow you to trade flexibility for performance\n", - "while being safe and easy to use.\n", - "\n", - "Here's a simple definition of a struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPair:\n", - " var first: Int\n", - " var second: Int\n", - "\n", - " # We use 'fn' instead of 'def' here - we'll explain that soon\n", - " fn __init__(inout self, first: Int, second: Int):\n", - " self.first = first\n", - " self.second = second\n", - "\n", - " fn __lt__(self, rhs: MyPair) -> Bool:\n", - " return self.first < rhs.first or\n", - " (self.first == rhs.first and\n", - " self.second < rhs.second)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Syntactically, the biggest difference compared to a Python `class` is that all\n", - "instance properties in a `struct` **must** be explicitly declared with a `var`\n", - "or `let` declaration.\n", - "\n", - "In Mojo, the structure and contents of a \"struct\" are set in advance and can't\n", - "be changed while the program is running. Unlike in Python, where you can add,\n", - "remove, or change attributes of an object on the fly, Mojo doesn't allow that\n", - "for structs. This means you can't use `del` to remove a method or change its\n", - "value in the middle of running the program.\n", - "\n", - "However, the static nature of `struct` has some great benefits! It helps Mojo\n", - "run your code faster. The program knows exactly where to find the struct's\n", - "information and how to use it without any extra steps or delays.\n", - "\n", - "Mojo's structs also work really well with features you might already know from\n", - "Python, like operator overloading (which lets you change how math symbols like\n", - "`+` and `-` work with your own data). Furthermore, *all* the \"standard types\"\n", - "(like `Int`, `Bool`, `String` and even `Tuple`) are made using structs. This\n", - "means they're part of the standard set of tools you can use, rather than being\n", - "hardwired into the language itself. This gives you more flexibility and control\n", - "when writing your code.\n", - "\n", - "
    \n", - "\n", - "If you're wondering what the `inout` means on the `self` argument: this\n", - "indicates that the argument is mutable and changes made inside the function are\n", - "visible to the caller. For details, see below about\n", - "[inout arguments](#mutable-arguments-inout).\n", - "\n", - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### `Int` vs `int`\n", - "\n", - "In Mojo, you might notice that we use `Int` (with a capital \"I\"), which is\n", - "different from Python's `int` (with a lowercase \"i\"). This difference is on\n", - "purpose, and it's actually a good thing!\n", - "\n", - "In Python, the `int` type can handle really big numbers and has some extra\n", - "features, like checking if two numbers are the same object. But this comes with\n", - "some extra baggage that can slow things down. Mojo's `Int` is different. It's\n", - "designed to be simple, fast, and tuned for your computer's hardware to handle\n", - "quickly.\n", - "\n", - "We made this choice for two main reasons:\n", - "\n", - "1. We want to give programmers who need to work closely with computer hardware\n", - "(systems programmers) a transparent and reliable way to interact with hardware.\n", - "We don't want to rely on fancy tricks (like JIT compilers) to make things\n", - "faster.\n", - "\n", - "2. We want Mojo to work well with Python without causing any issues. By using a\n", - "different name (Int instead of int), we can keep both types in Mojo without\n", - "changing how Python's int works.\n", - "\n", - "As a bonus, `Int` follows the same naming style as other custom data types you\n", - "might create in Mojo. Additionally, `Int` is a `struct` that's included in\n", - "Mojo's standard set of tools.\n", - "\n", - "### Strong type checking\n", - "\n", - "Even though you can still use flexible types like in Python, Mojo lets you use\n", - "strict type checking. Type-checking can make your code more predictable,\n", - "manageable, and secure.\n", - "\n", - "One of the primary ways to employ strong type checking is with Mojo's `struct`\n", - "type. A `struct` definition in Mojo defines a compile-time-bound name, and\n", - "references to that name in a type context are treated as a strong specification\n", - "for the value being defined. For example, consider the following code that uses\n", - "the `MyPair` struct shown above:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def pair_test() -> Bool:\n", - " var p = MyPair(1, 2)\n", - " # Uncomment to see an error:\n", - " # return p < 4 # gives a compile time error\n", - " return True\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you uncomment the first return statement and run it, you’ll get a\n", - "compile-time error telling you that `4` cannot be converted to `MyPair`, which\n", - "is what the right-hand-side of `__lt__()` requires (in the `MyPair` definition).\n", - "\n", - "This is a familiar experience when working with systems programming languages,\n", - "but it's not how Python works. Python has syntactically identical features for\n", - "[MyPy](https://mypy.readthedocs.io/) type annotations, but they are not\n", - "enforced by the compiler: instead, they are hints that inform static analysis.\n", - "By tying types to specific declarations, Mojo can handle both the classical\n", - "type annotation hints and strong type specifications without breaking\n", - "compatibility.\n", - "\n", - "Type checking isn't the only use-case for strong types. Since we know the types\n", - "are accurate, we can optimize the code based on those types, pass values in\n", - "registers, and be as efficient as C for argument passing and other low-level\n", - "details. This is the foundation of the safety and predictability guarantees\n", - "Mojo provides to systems programmers.\n", - "\n", - "### Overloaded functions and methods\n", - "\n", - "Like Python, you can define functions in Mojo without specifying argument data\n", - "types and Mojo will handle them dynamically. This is nice when you want\n", - "expressive APIs that just work by accepting arbitrary inputs and let dynamic\n", - "dispatch decide how to handle the data. However, when you want to ensure type\n", - "safety, as discussed above, Mojo also offers full support for overloaded\n", - "functions and methods.\n", - "\n", - "This allows you to define multiple functions with the same name but with\n", - "different arguments. This is a common feature seen in many languages, such as\n", - "C++, Java, and Swift.\n", - "\n", - "When resolving a function call, Mojo tries each candidate and uses the one that\n", - "works (if only one works), or it picks the closest match (if it can determine a\n", - "close match), or it reports that the call is ambiguous if it can't figure\n", - "out which one to pick. In the latter case, you can resolve the ambiguity by\n", - "adding an explicit cast on the call site. \n", - "\n", - "Let's look at an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "struct Complex:\n", - " var re: Float32\n", - " var im: Float32\n", - "\n", - " fn __init__(inout self, x: Float32):\n", - " \"\"\"Construct a complex number given a real number.\"\"\"\n", - " self.re = x\n", - " self.im = 0.0\n", - "\n", - " fn __init__(inout self, r: Float32, i: Float32):\n", - " \"\"\"Construct a complex number given its real and imaginary components.\"\"\"\n", - " self.re = r\n", - " self.im = i\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "You can overload methods in structs and classes and overload module-level\n", - "functions.\n", - "\n", - "Mojo doesn't support overloading solely on result type, and doesn't use result\n", - "type or contextual type information for type inference, keeping things simple,\n", - "fast, and predictable. Mojo will never produce an \"expression too complex\"\n", - "error, because its type-checker is simple and fast by definition.\n", - "\n", - "Again, if you leave your argument names without type definitions, then the\n", - "function behaves just like Python with dynamic types. As soon as you define a\n", - "single argument type, Mojo will look for overload candidates and resolve\n", - "function calls as described above.\n", - "\n", - "Although we haven't discussed parameters yet (they're different from function\n", - "arguments), you can also [overload functions and methods based on\n", - "parameters](#overloading-on-parameters).\n", - "\n", - "### `fn` definitions\n", - "\n", - "The extensions above are the cornerstone that provides low-level programming\n", - "and provide abstraction capabilities, but many systems programmers prefer more\n", - "control and predictability than what `def` in Mojo provides. To recap, `def` is\n", - "defined by necessity to be very dynamic, flexible and generally compatible with\n", - "Python: arguments are mutable, local variables are implicitly declared on first\n", - "use, and scoping isn't enforced. This is great for high level programming and\n", - "scripting, but is not always great for systems programming. To complement this,\n", - "Mojo provides an `fn` declaration which is like a \"strict mode\" for `def`.\n", - "\n", - "> Alternative: instead of using a new keyword like `fn`, we could instead add a\n", - "modifier or decorator like `@strict def`. However, we need to take new keywords\n", - "anyway and there is little cost to doing so. Also, in practice in systems\n", - "programming domains, `fn` is used all the time so it probably makes sense to\n", - "make it first class.\n", - "\n", - "As far as a caller is concerned, `fn` and `def` are interchangeable: there is\n", - "nothing a `def` can provide that a `fn` cannot (and vice versa). The\n", - "difference is that a `fn` is more limited and controlled on the *inside* of\n", - "its body (alternatively: pedantic and strict). Specifically, `fn`s have a\n", - "number of limitations compared to `def` functions:\n", - "\n", - "1. Argument values default to being immutable in the body of the function (like\n", - "a `let`), instead of mutable (like a `var`). This catches accidental mutations,\n", - "and permits the use of non-copyable types as arguments.\n", - "\n", - "2. Argument values require a type specification (except for `self` in a\n", - "method), catching accidental omission of type specifications. Similarly, a\n", - "missing return type specifier is interpreted as returning `None` instead of an\n", - "unknown return type. Note that both can be explicitly declared to return\n", - "`object`, which allows one to opt-in to the behavior of a `def` if desired.\n", - "\n", - "3. Implicit declaration of local variables is disabled, so all locals must be\n", - "declared. This catches name typos and dovetails with the scoping provided by\n", - "`let` and `var`.\n", - "\n", - "4. Both support raising exceptions, but this must be explicitly declared on a\n", - "`fn` with the `raises` keyword.\n", - "\n", - "Programming patterns will vary widely across teams, and this level of\n", - "strictness will not be for everyone. We expect that folks who are used to C++\n", - "and already use MyPy-style type annotations in Python to prefer the use of\n", - "`fn`s, but higher level programmers and ML researchers to continue to use\n", - "`def`. Mojo allows you to freely intermix `def` and `fn` declarations, e.g.\n", - "implementing some methods with one and others with the other, and allows each\n", - "team or programmer to decide what is best for their use-case.\n", - "\n", - "For more about argument behavior in Mojo functions, see the section below about\n", - "[Argument passing control and memory\n", - "ownership](#argument-passing-control-and-memory-ownership)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `__copyinit__` and `__moveinit__` special methods\n", - "\n", - "Mojo supports full \"value semantics\" as seen in languages like C++ and Swift,\n", - "and it makes defining simple aggregates of fields very easy with the [`@value`\n", - "decorator](#value-decorator).\n", - "\n", - "For advanced use cases, Mojo allows you to define custom constructors (using\n", - "Python's existing `__init__` special method), custom destructors (using the\n", - "existing `__del__` special method) and custom copy and move constructors using\n", - "the `__copyinit__` and `__moveinit__` special methods.\n", - "\n", - "These low-level customization hooks can be useful when doing low level systems\n", - "programming, e.g. with manual memory management. For example, consider a\n", - "dynamic string type that needs to allocate memory for the string data when\n", - "constructed and destroy it when the value is destroyed:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import Pointer\n", - "\n", - "struct HeapArray:\n", - " var data: Pointer[Int]\n", - " var size: Int\n", - "\n", - " fn __init__(inout self, size: Int, val: Int):\n", - " self.size = size\n", - " self.data = Pointer[Int].alloc(self.size)\n", - " for i in range(self.size):\n", - " self.data[i] = val\n", - "\n", - " fn __del__(owned self):\n", - " self.data.free()\n", - "\n", - " fn dump(self):\n", - " print(\"[\", end=\"\")\n", - " for i in range(self.size):\n", - " if i > 0:\n", - " print(\", \", end=\"\")\n", - " print(self.data.load(i), end=\"\")\n", - " print(\"]\")\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This array type is implemented using low level functions to show a simple\n", - "example of how this works. However, if you try to copy an instance of\n", - "`HeapArray` with the `=` operator, you might be surprised:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 1, 1]\n", - "[2, 2, 2, 2]\n", - "[1, 1, 1]\n" - ] - } - ], - "source": [ - "var a = HeapArray(3, 1)\n", - "a.dump() # Should print [1, 1, 1]\n", - "# Uncomment to see an error:\n", - "# var b = a # ERROR: Vector doesn't implement __copyinit__\n", - "\n", - "var b = HeapArray(4, 2)\n", - "b.dump() # Should print [2, 2, 2, 2]\n", - "a.dump() # Should print [1, 1, 1]\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you uncomment the line to copy `a` into `b`, you'll see that Mojo doesn't\n", - "allow you to make a copy of our array: `HeapArray` contains an instance of\n", - "`Pointer` (which is equivalent to a low-level C pointer), and Mojo doesn't know\n", - "what kind of data it points to or how to copy it. More generally, some types\n", - "(like atomic numbers) cannot be copied or moved around because their address\n", - "provides an **identity** just like a class instance does.\n", - "\n", - "In this case, we do want our array to be copyable. To enable this, we have to\n", - "implement the `__copyinit__` special method, which is conventionally\n", - "implemented like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "struct HeapArray:\n", - " var data: Pointer[Int]\n", - " var size: Int\n", - "\n", - " fn __init__(inout self, size: Int, val: Int):\n", - " self.size = size\n", - " self.data = Pointer[Int].alloc(self.size)\n", - " for i in range(self.size):\n", - " self.data[i] = val\n", - "\n", - " fn __copyinit__(inout self, existing: Self):\n", - " self.size = existing.size\n", - " self.data = Pointer[Int].alloc(self.size)\n", - " for i in range(self.size):\n", - " self.data[i] = existing.data[i]\n", - "\n", - " fn __del__(owned self):\n", - " self.data.free()\n", - "\n", - " fn dump(self):\n", - " print(\"[\", end=\"\")\n", - " for i in range(self.size):\n", - " if i > 0:\n", - " print(\", \", end=\"\")\n", - " print(self.data.load(i), end=\"\")\n", - " print(\"]\")\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With this implementation, our code above works correctly and the `b = a` copy\n", - "produces a logically distinct instance of the array with its own lifetime and\n", - "data:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 1, 1]\n", - "[1, 1, 1]\n", - "[1, 1, 1]\n" - ] - } - ], - "source": [ - "var a = HeapArray(3, 1)\n", - "a.dump() # Should print [1, 1, 1]\n", - "# This is no longer an error:\n", - "var b = a\n", - "\n", - "b.dump() # Should print [1, 1, 1]\n", - "a.dump() # Should print [1, 1, 1]\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo also supports the `__moveinit__` method which allows Rust-style\n", - "moves (which transfers a value from one place to another when the source \n", - "lifetime ends), and allows for defining custom move logic. For more detail, see\n", - "the [Value Lifecycle](#value-lifecycle-birth-life-and-death-of-a-value)\n", - "section below.\n", - "\n", - "Mojo provides full control over the lifetime of a value, including the ability\n", - "to make types copyable, move-only, and not-movable. This is more control than\n", - "languages like Swift and Rust offer, which require values to at least be\n", - "movable. If you are curious how `existing` can be passed into the\n", - "`__copyinit__` method without itself creating a copy, check out the section on\n", - "[Borrowed arguments](#immutable-arguments-borrowed) below." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Argument passing control and memory ownership\n", - "\n", - "In both Python and Mojo, much of the language revolves around function calls: a\n", - "lot of the (apparently) built-in behaviors are implemented in the standard\n", - "library with \"dunder\" (double-underscore) methods. Inside these\n", - "magic functions is where a lot of memory ownership is determined through\n", - "argument passing.\n", - "\n", - "Let's review some details about how Python and Mojo pass arguments:\n", - "\n", - "- All values passed into a *Python* `def` function use reference semantics. This\n", - "means the function can modify mutable objects passed into it and those changes\n", - "are visible outside the function. However, the behavior is sometimes surprising\n", - "for the uninitiated, because you can change the object that an argument points\n", - "to and that change is not visible outside the function.\n", - "\n", - "- All values passed into a *Mojo* `def` function use value semantics by default.\n", - "Compared to Python, this is an important difference: A Mojo `def` function\n", - "receives a copy of all arguments—it can modify arguments inside the function,\n", - "but the changes are **not** visible outside the function.\n", - "\n", - "- All values passed into a Mojo [`fn` function](#fn-definitions) are immutable\n", - "references by default. This means the function can read the original object (it\n", - "is *not* a copy), but it cannot modify the object at all.\n", - "\n", - "This convention for immutable argument passing in a Mojo `fn` is called\n", - "\"borrowing.\" In the following sections, we'll explain how you can change the\n", - "argument passing behavior in Mojo, for both `def` and `fn` functions.\n", - "\n", - "### Why argument conventions are important\n", - "\n", - "In Python, all fundamental values are references to objects—as described above,\n", - "a Python function can modify the original object. Thus, Python developers are\n", - "used to thinking about everything as reference semantic. However, at the\n", - "CPython or machine level, you can see that the references themselves are\n", - "actually passed *by-copy*—Python copies a pointer and adjusts reference counts.\n", - "\n", - "This Python approach provides a comfortable programming model for most people,\n", - "but it requires all values to be heap-allocated (and results are occasionally\n", - "surprising due to reference sharing). Mojo classes (TODO: will) follow\n", - "the same reference-semantic approach for most objects, but this isn't practical\n", - "for simple types like integers in a systems programming context. In these\n", - "scenarios, we want the values to live on the stack or even in hardware\n", - "registers. As such, Mojo structs are always inlined into their container,\n", - "whether that be as the field of another type or into the stack frame of the\n", - "containing function.\n", - "\n", - "This raises some interesting questions: How do you implement methods that need\n", - "to mutate `self` of a structure type, such as `__iadd__`? How does `let` work,\n", - "and how does it prevent mutation? How are the lifetimes of these values\n", - "controlled to keep Mojo a memory-safe language?\n", - "\n", - "The answer is that the Mojo compiler uses dataflow analysis and type\n", - "annotations to provide full control over value copies, aliasing of references,\n", - "and mutation control. These features are similar in many ways to features in\n", - "the Rust language, but they work somewhat differently in order to make\n", - "Mojo easier to learn, and they integrate better into the Python ecosystem\n", - "without requiring a massive annotation burden.\n", - "\n", - "In the following sections, you'll learn about how you can control memory\n", - "ownership for objects passed into Mojo `fn` functions." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Immutable arguments (`borrowed`)\n", - "\n", - "A borrowed object is an **immutable reference** to an object that a function\n", - "receives, instead of receiving a copy of the object. So the\n", - "callee function has full read-and-execute access to the object, but it cannot\n", - "modify it (the caller still has exclusive \"ownership\" of the object).\n", - "\n", - "For example, consider this struct that we don't want to copy when passing around\n", - "instances of it:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Don't worry about this code yet. It's just needed for the function below.\n", - "# It's a type so expensive to copy around so it does not have a\n", - "# __copyinit__ method.\n", - "struct SomethingBig:\n", - " var id_number: Int\n", - " var huge: HeapArray\n", - " fn __init__(inout self, id: Int):\n", - " self.huge = HeapArray(1000, 0)\n", - " self.id_number = id\n", - "\n", - " # self is passed by-reference for mutation as described above.\n", - " fn set_id(inout self, number: Int):\n", - " self.id_number = number\n", - "\n", - " # Arguments like self are passed as borrowed by default.\n", - " fn print_id(self): # Same as: fn print_id(borrowed self):\n", - " print(self.id_number)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When passing an instance of `SomethingBig` to a function, it's necessary to\n", - "pass a reference because `SomethingBig` cannot be copied (it has no\n", - "`__copyinit__` method). And, as mentioned above, `fn` arguments are immutable\n", - "references by default, but you can explicitly define it with the `borrowed`\n", - "keyword as shown in the `use_something_big()` function here:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "10\n", - "20\n" - ] - } - ], - "source": [ - "fn use_something_big(borrowed a: SomethingBig, b: SomethingBig):\n", - " \"\"\"'a' and 'b' are both immutable, because 'borrowed' is the default.\"\"\"\n", - " a.print_id()\n", - " b.print_id()\n", - "\n", - "var a = SomethingBig(10)\n", - "var b = SomethingBig(20)\n", - "use_something_big(a, b)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This default applies to all arguments uniformly, including the `self` argument\n", - "of methods. This is much more efficient when passing large values or when\n", - "passing expensive values like a reference-counted pointer (which is the default\n", - "for Python/Mojo classes), because the copy constructor and destructor don't\n", - "have to be invoked when passing the argument. \n", - "\n", - "Because the default argument convention for `fn` functions is `borrowed`, Mojo\n", - "has simple and logical code that does the right thing by default. For example,\n", - "we don't want to copy or move all of `SomethingBig` just to invoke the\n", - "`print_id()` method, or when calling `use_something_big()`.\n", - "\n", - "This borrowed argument convention is similar in some ways to passing an\n", - "argument by `const&` in C++, which avoids a copy of the value and disables\n", - "mutability in the callee. However, the borrowed convention differs from\n", - "`const&` in C++ in two important ways:\n", - "\n", - "1. The Mojo compiler implements a borrow checker (similar to Rust) that\n", - "prevents code from dynamically forming mutable references to a value when there\n", - "are immutable references outstanding, and it prevents multiple mutable\n", - "references to the same value. You are allowed to have multiple borrows (as the\n", - "call to `use_something_big` does above) but you cannot pass something by mutable\n", - "reference and borrow at the same time. (TODO: Not currently enabled).\n", - "\n", - "2. Small values like `Int`, `Float`, and `SIMD` are passed directly in machine\n", - "registers instead of through an extra indirection (this is because they are\n", - "declared with the [`@register_passable`\n", - "decorator](#register_passable-struct-decorator)). This is a [significant\n", - "performance\n", - "enhancement](https://www.forrestthewoods.com/blog/should-small-rust-structs-be-passed-by-copy-or-by-borrow/)\n", - "when compared to languages like C++ and Rust, and moves this optimization from\n", - "every call site to being declarative on a type.\n", - "\n", - "Similar to Rust, Mojo's borrow checker enforces the exclusivity of invariants.\n", - "The major difference between Rust and Mojo is that Mojo does not require a\n", - "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", - "passing small values, and Rust defaults to moving values instead of passing\n", - "them around by borrow. These policy and syntax decisions allow Mojo to provide\n", - "an easier-to-use programming model." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mutable arguments (`inout`)\n", - "\n", - "On the other hand, if you define an `fn` function and want an argument to be\n", - "mutable, you must declare the argument as mutable with the `inout` keyword.\n", - "\n", - "
    \n", - "\n", - "**Tip:** When you see `inout`, it means any changes made to the argument\n", - "**in**side the function are visible **out**side the function.\n", - "\n", - "
    \n", - "\n", - "Consider the following example, in which the `__iadd__` function (which\n", - "implements the in-place add operation such as `x += 2`) tries to modify `self`:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyInt:\n", - " var value: Int\n", - "\n", - " fn __init__(inout self, v: Int):\n", - " self.value = v\n", - "\n", - " fn __copyinit__(inout self, existing: MyInt):\n", - " self.value = existing.value\n", - "\n", - " # self and rhs are both immutable in __add__.\n", - " fn __add__(self, rhs: MyInt) -> MyInt:\n", - " return MyInt(self.value + rhs.value)\n", - "\n", - " # ... but this cannot work for __iadd__\n", - " # Uncomment to see the error:\n", - " #fn __iadd__(self, rhs: Int):\n", - " # self = self + rhs # ERROR: cannot assign to self!\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you uncomment the `__iadd__()` method, you'll get a compiler error.\n", - "\n", - "The problem here is that `self` is immutable because this is a Mojo `fn`\n", - "function, so it can't change the internal state of the argument (the default\n", - "argument convention is `borrowed`). The solution is to declare that the\n", - "argument is mutable by adding the `inout` keyword on the `self` argument name:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyInt:\n", - " var value: Int\n", - "\n", - " fn __init__(inout self, v: Int):\n", - " self.value = v\n", - "\n", - " fn __copyinit__(inout self, existing: MyInt):\n", - " self.value = existing.value\n", - "\n", - " # self and rhs are both immutable in __add__.\n", - " fn __add__(self, rhs: MyInt) -> MyInt:\n", - " return MyInt(self.value + rhs.value)\n", - "\n", - " # ... now this works:\n", - " fn __iadd__(inout self, rhs: Int):\n", - " self = self + rhs\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now the `self` argument is mutable in the function and any changes are visible\n", - "in the caller, so we can perform in-place addition with `MyInt`:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "43\n" - ] - } - ], - "source": [ - "var x: MyInt = 42\n", - "x += 1\n", - "print(x.value) # prints 43 as expected\n", - "\n", - "# However...\n", - "var y = x\n", - "# Uncomment to see the error:\n", - "# y += 1 # ERROR: Cannot mutate 'let' value\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you uncomment the last line above, mutation of the `let` value fails\n", - "because it isn't possible to form a mutable reference to an immutable value\n", - "(`let` makes the variable immutable).\n", - "\n", - "Of course, you can declare multiple `inout` arguments. For example, you can\n", - "define and use a swap function like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42 12\n", - "12 42\n" - ] - } - ], - "source": [ - "fn swap(inout lhs: Int, inout rhs: Int):\n", - " var tmp = lhs\n", - " lhs = rhs\n", - " rhs = tmp\n", - "\n", - "var x = 42\n", - "var y = 12\n", - "print(x, y) # Prints 42, 12\n", - "swap(x, y)\n", - "print(x, y) # Prints 12, 42\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A very important aspect of this system is that it all composes correctly." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
    \n", - "\n", - "Notice that we don't call this argument passing \"by reference.\" Although the\n", - "`inout` convention is conceptually the same, we don't call it by-reference\n", - "passing because the implementation may actually pass values using pointers.\n", - "\n", - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Transfer arguments (`owned` and `^`)\n", - "\n", - "The final argument convention that Mojo supports is the `owned` argument\n", - "convention. This convention is used for functions that want to take exclusive\n", - "ownership over a value, and it is often used with the postfixed `^` operator.\n", - "\n", - "For example, imagine you're working with a move-only type like a unique\n", - "pointer:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# This is not really a unique pointer, we just model its behavior here\n", - "# to serve the examples below.\n", - "struct UniquePointer:\n", - " var ptr: Int\n", - "\n", - " fn __init__(inout self, ptr: Int):\n", - " self.ptr = ptr\n", - "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", - " self.ptr = existing.ptr\n", - "\n", - " fn __del__(owned self):\n", - " self.ptr = 0\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "While the `borrow` convention makes it easy to work with this unique pointer\n", - "without ceremony, at some point you might want to transfer ownership to some\n", - "other function. This is a situation where you want to use the `^` \"transfer\"\n", - "operator with your movable type.\n", - "\n", - "The `^` operator ends the lifetime of a value binding and transfers the value\n", - "ownership to something else (in the following example, ownership is transferred\n", - "to the `take_ptr()` function). To support this, you can define functions as\n", - "taking `owned` arguments. For example, you can define `take_ptr()` to take\n", - "ownership of an argument as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "use_ptr\n", - "100\n", - "take_ptr\n", - "100\n" - ] - } - ], - "source": [ - "fn take_ptr(owned p: UniquePointer):\n", - " print(\"take_ptr\")\n", - " print(p.ptr)\n", - "\n", - "fn use_ptr(borrowed p: UniquePointer):\n", - " print(\"use_ptr\")\n", - " print(p.ptr)\n", - "\n", - "fn work_with_unique_ptrs():\n", - " var p = UniquePointer(100)\n", - " use_ptr(p) # Pass to borrowing function.\n", - " take_ptr(p^) # Pass ownership of the `p` value to another function.\n", - "\n", - " # Uncomment to see an error:\n", - " # use_ptr(p) # ERROR: p is no longer valid here!\n", - "\n", - "work_with_unique_ptrs()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that if you uncomment the second call to `use_ptr()`, you get an error\n", - "because the `p` value has been transferred to the `take_ptr()` function and,\n", - "thus, the `p` value is destroyed.\n", - "\n", - "Because it is declared `owned`, the `take_ptr()` function knows it has unique\n", - "access to the value. This is very important for things like unique pointers,\n", - "and it's useful when you want to avoid copies.\n", - "\n", - "For example, you will notably see the `owned` convention on destructors and on\n", - "consuming move initializers. For example, our `HeapArray` struct defined\n", - "earlier uses `owned` in its `__del__()` method, because you need to own a value\n", - "to destroy it (or to steal its parts, in the case of a move constructor)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Comparing `def` and `fn` argument passing\n", - "\n", - "Mojo's `def` function is essentially just sugaring for the `fn` function:\n", - "\n", - "- A `def` argument without an explicit type annotation defaults to `Object`.\n", - "\n", - "- A `def` argument without a convention keyword (such as `inout` or `owned`) is\n", - "passed by implicit copy into a mutable var with the same name as the argument.\n", - "(This requires that the type have a `__copyinit__` method.)\n", - "\n", - "For example, these two functions have the same behavior:\n", - "\n", - "```mojo\n", - "def example(inout a: Int, b: Int, c):\n", - " # b and c use value semantics so they're mutable in the function\n", - " ...\n", - "\n", - "fn example(inout a: Int, b_in: Int, c_in: Object):\n", - " # b_in and c_in are immutable references, so we make mutable shadow copies\n", - " var b = b_in\n", - " var c = c_in\n", - " ...\n", - "```\n", - "\n", - "The shadow copies typically add no overhead, because references for small types\n", - "like `Object` are cheap to copy. The expensive part is adjusting the reference\n", - "count, but that's eliminated by a move optimization." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Python integration\n", - "\n", - "It's easy to use Python modules you know and love in Mojo. You can import\n", - "any Python module into your Mojo program and create Python types from Mojo\n", - "types.\n", - "\n", - "### Importing Python modules\n", - "\n", - "To import a Python module in Mojo, just call `Python.import_module()` with the\n", - "module name:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from python import Python\n", - "\n", - "fn use_array() raises:\n", - " # This is equivalent to Python's `import numpy as np`\n", - " var np = Python.import_module(\"numpy\")\n", - "\n", - " # Now use numpy as if writing in Python\n", - " var array = np.array([1, 2, 3])\n", - " print(array)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1 2 3]\n" - ] - } - ], - "source": [ - "use_array()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Yes, this imports Python NumPy, and you can import *any other Python module* you \n", - "have installed.\n", - "\n", - "A few things to note:\n", - "\n", - "- Currently, you cannot import individual members (such as a single Python class\n", - " or function)—you must import the whole Python module and then access members\n", - " through the module name.\n", - "\n", - "- Mojo doesn't yet support top-level code, so the `import_module()` call must\n", - " be inside another method. This means you may need to import a module multiple\n", - " times or pass around a reference to the module.\n", - "\n", - "- `import_module()` may raise an exception (for example, if the module isn't\n", - " installed). If you're using it inside a `fn` function, you need to either\n", - " handle errors (using a `try/except` clause), or add the `raises` keyword to\n", - " the function signature. You'll also see this when calling Python functions\n", - " that may raise exceptions. (Raising exceptions is much more common in Python\n", - " code than in the Mojo standard library, which \n", - " [limits their use for performance reasons](/mojo/roadmap.html#the-standard-library-has-limited-exceptions-use).)\n", - "\n", - "### Mojo types in Python\n", - "\n", - "Mojo primitive types implicitly convert into Python objects.\n", - "Today we support lists, tuples, integers, floats, booleans, and strings.\n", - "\n", - "For example, given this Python function that prints Python types:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "%%python\n", - "def type_printer(my_list, my_tuple, my_int, my_string, my_float):\n", - " print(type(my_list))\n", - " print(type(my_tuple))\n", - " print(type(my_int))\n", - " print(type(my_string))\n", - " print(type(my_float))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can pass the Python function Mojo types with no problem:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\n", - "\n", - "\n" - ] - } - ], - "source": [ - "type_printer([0, 3], (False, True), 4, \"orange\", 3.4)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that in a Jupyter notebook, the Python function declared above is\n", - "automatically available to any Mojo code in following code cells.\n", - "\n", - "Mojo doesn't have a standard Dictionary yet, so it is not yet possible\n", - "to create a Python dictionary from a Mojo dictionary. You can work with\n", - "Python dictionaries in Mojo though! To create a Python dictionary, use the\n", - "[`dict`](/mojo/stdlib/python/python.html#dict) method:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from python import Python\n", - "from python import PythonObject\n", - "\n", - "fn use_dict() raises:\n", - " var dictionary = Python.dict()\n", - " dictionary[\"fruit\"] = \"apple\"\n", - " dictionary[\"starch\"] = \"potato\"\n", - "\n", - " var keys: PythonObject = [\"fruit\", \"starch\", \"protein\"]\n", - " var N: Int = keys.__len__().__index__()\n", - " print(N, \"items\")\n", - "\n", - " for i in range(N):\n", - " if Python.is_type(dictionary.get(keys[i]), Python.none()):\n", - " print(keys[i], \"is not in dictionary\")\n", - " else:\n", - " print(keys[i], \"is included\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then call the `use_dict()` function to see the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 items\n", - "fruit is included\n", - "starch is included\n", - "protein is not in dictionary\n" - ] - } - ], - "source": [ - "use_dict()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Importing local Python modules\n", - "\n", - "If you have some local Python code you want to use in Mojo, just add\n", - "the directory to the Python path and then import the module.\n", - "\n", - "For example, suppose you have a Python file named `mypython.py`:\n", - "\n", - "```python\n", - "import numpy as np\n", - "\n", - "def my_algorithm(a, b):\n", - " array_a = np.random.rand(a, a)\n", - " return array_a + b\n", - "```\n", - "\n", - "Here's how you can import it and use it in a Mojo file:\n", - "\n", - "```mojo\n", - "from python import Python\n", - "\n", - "fn use_my_module() raises:\n", - " Python.add_to_path(\"path/to/module\")\n", - " var mypython = Python.import_module(\"mypython\")\n", - "\n", - " var c = mypython.my_algorithm(2, 3)\n", - " print(c)\n", - "```\n", - "\n", - "There's no need to worry about memory management when using Python in Mojo.\n", - "Everything just works because Mojo was designed for Python from the beginning." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization: compile-time metaprogramming\n", - "\n", - "One of Python's most amazing features is its extensible runtime\n", - "metaprogramming features. This has enabled a wide range of libraries and\n", - "provides a flexible and extensible programming model that Python programmers\n", - "everywhere benefit from. Unfortunately, these features also come at a cost:\n", - "because they are evaluated at runtime, they directly impact run-time efficiency\n", - "of the underlying code. Because they are not known to the IDE, it is difficult\n", - "for IDE features like code completion to understand them and use them to\n", - "improve the developer experience.\n", - "\n", - "Outside the Python ecosystem, static metaprogramming is also an important part\n", - "of development, enabling the development of new programming paradigms and\n", - "advanced libraries. There are many examples of prior art in this space, with\n", - "different tradeoffs, for example:\n", - "\n", - "1. Preprocessors (e.g. C preprocessor, Lex/YACC, etc) are perhaps the heaviest\n", - "handed. They are fully general but the worst in terms of developer experience\n", - "and tools integration.\n", - "\n", - "2. Some languages (like Lisp and Rust) support (sometimes \"hygienic\") macro\n", - "expansion features, enabling syntactic extension and boilerplate reduction with\n", - "somewhat better tooling integration.\n", - "\n", - "3. Some older languages like C++ have very large and complex metaprogramming\n", - "languages (templates) that are a dual to the *runtime* language. These are\n", - "notably difficult to learn and have poor compile times and error messages.\n", - "\n", - "4. Some languages (like Swift) build many features into the core language in a\n", - "first-class way to provide good ergonomics for common cases at the expense of\n", - "generality.\n", - "\n", - "5. Some newer languages like Zig integrate a language interpreter into the\n", - "compilation flow, and allow the interpreter to reflect over the AST as it is\n", - "compiled. This allows many of the same features as a macro system with better\n", - "extensibility and generality.\n", - "\n", - "For Modular's work in AI, high-performance machine learning kernels, and\n", - "accelerators, we need high abstraction capabilities provided by advanced\n", - "metaprogramming systems. We needed high-level zero-cost abstractions,\n", - "expressive libraries, and large-scale integration of multiple variants of\n", - "algorithms. We want library developers to be able to extend the system, just\n", - "like they do in Python, providing an extensible developer platform.\n", - "\n", - "That said, we are not willing to sacrifice developer experience (including\n", - "compile times and error messages) nor are we interested in building a parallel\n", - "language ecosystem that is difficult to teach. We can learn from these previous\n", - "systems but also have new technologies to build on top of, including MLIR and\n", - "fine-grained language-integrated caching technologies.\n", - "\n", - "As such, Mojo supports compile-time metaprogramming built\n", - "into the compiler as a separate stage of compilation—after parsing, semantic\n", - "analysis, and IR generation, but before lowering to target-specific code. It\n", - "uses the same host language for runtime programs as it does for metaprograms,\n", - "and leverages MLIR to represent and evaluate these programs predictably.\n", - "\n", - "Let's take a look at some simple examples.\n", - "\n", - "
    \n", - "\n", - "**About \"parameters\":** Python developers use the words \"arguments\" and\n", - "\"parameters\" fairly interchangeably for \"things that are passed into\n", - "functions.\" We decided to reclaim \"parameter\" and \"parameter expression\" to\n", - "represent a compile-time value in Mojo, and continue to use \"argument\" and\n", - "\"expression\" to refer to runtime values. This allows us to align around words\n", - "like \"parameterized\" and \"parametric\" for compile-time metaprogramming.\n", - "\n", - "
    " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Defining parameterized types and functions\n", - "\n", - "You can parameterize structs and functions by specifying parameter names and\n", - "types in square brackets (using an extended version of the [PEP695\n", - "syntax](https://peps.python.org/pep-0695/)). Unlike argument values, parameter\n", - "values are known at compile-time, which enables an additional level of\n", - "abstraction and code reuse, plus compiler optimizations such as\n", - "[autotuning](#autotuning-adaptive-compilation).\n", - "\n", - "For instance, let's look at a\n", - "[SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) type,\n", - "which represents a low-level vector register in hardware that holds multiple\n", - "instances of a scalar data-type. Hardware accelerators are constantly\n", - "introducing new vector data types, and even CPUs may have 512-bit or longer SIMD\n", - "vectors. In order to access the SIMD instructions on these processors, the data\n", - "must be shaped into the proper SIMD width (data type) and length (vector size).\n", - "\n", - "However, it's not feasible to define all the different SIMD variations with\n", - "Mojo's built-in types. So, Mojo's `SIMD` type (defined as a struct) exposes the\n", - "common SIMD operations in its methods, and makes the SIMD data type and size\n", - "values parametric. This allows you to directly map your data to the SIMD vectors\n", - "on any hardware. \n", - "\n", - "Here is a cut-down (non-functional) version of Mojo's `SIMD` type definition:\n", - "\n", - "```mojo\n", - "struct SIMD[type: DType, size: Int]:\n", - " var value: … # Some low-level MLIR stuff here\n", - "\n", - " # Create a new SIMD from a number of scalars\n", - " fn __init__(inout self, *elems: Scalar[type]): ...\n", - "\n", - " # Fill a SIMD with a duplicated scalar value.\n", - " @staticmethod\n", - " fn splat(x: Scalar[type]) -> SIMD[type, size]: ...\n", - "\n", - " # Cast the elements of the SIMD to a different elt type.\n", - " fn cast[target: DType](self) -> SIMD[target, size]: ...\n", - "\n", - " # Many standard operators are supported.\n", - " fn __add__(self, rhs: Self) -> Self: ...\n", - "```\n", - "\n", - "Defining each SIMD variant with parameters is great for code reuse because the\n", - "`SIMD` type can express all the different vector variants statically, instead of\n", - "requiring the language to pre-define every variant.\n", - "\n", - "Because `SIMD` is a parameterized type, the `self` argument in its functions\n", - "carries those parameters—the full type name is `SIMD[type, size]`. Although\n", - "it's valid to write this out (as shown in the return type of `splat()`), this\n", - "can be verbose, so we recommend using the `Self` type (from\n", - "[PEP673](https://peps.python.org/pep-0673/)) like the `__add__` example does." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Overloading on parameters\n", - "\n", - "Functions and methods can be overloaded on their parameter signatures. The\n", - "overload resolution logic filters for candidates according to the following\n", - "rules, in order of precedence:\n", - "\n", - "1) Candidates with the minimal number of implicit conversions (in both arguments\n", - "and parameters).\n", - "2) Candidates without variadic arguments.\n", - "3) Candidates without variadic parameters.\n", - "4) Candidates with the shortest parameter signature.\n", - "5) Non-`@staticmethod` candidates (over `@staticmethod` ones, if available). \n", - "\n", - "If there is more than one candidate after applying these rules, the overload\n", - "resolution fails. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "foo[x: MyInt, a: Int]()\n", - "bar[a: Int](b: Int)\n", - "bar[*a: Int](b: Int)\n" - ] - } - ], - "source": [ - "@register_passable(\"trivial\")\n", - "struct MyInt:\n", - " \"\"\"A type that is implicitly convertible to `Int`.\"\"\"\n", - " var value: Int\n", - "\n", - " @always_inline(\"nodebug\")\n", - " fn __init__(inout self, _a: Int):\n", - " self.value = _a\n", - "\n", - "fn foo[x: MyInt, a: Int]():\n", - " print(\"foo[x: MyInt, a: Int]()\")\n", - "\n", - "fn foo[x: MyInt, y: MyInt]():\n", - " print(\"foo[x: MyInt, y: MyInt]()\")\n", - "\n", - "fn bar[a: Int](b: Int):\n", - " print(\"bar[a: Int](b: Int)\")\n", - "\n", - "fn bar[a: Int](*b: Int):\n", - " print(\"bar[a: Int](*b: Int)\")\n", - "\n", - "fn bar[*a: Int](b: Int):\n", - " print(\"bar[*a: Int](b: Int)\")\n", - "\n", - "fn parameter_overloads[a: Int, b: Int, x: MyInt]():\n", - " # `foo[x: MyInt, a: Int]()` is called because it requires no implicit\n", - " # conversions, whereas `foo[x: MyInt, y: MyInt]()` requires one.\n", - " foo[x, a]()\n", - "\n", - " # `bar[a: Int](b: Int)` is called because it does not have variadic\n", - " # arguments or parameters.\n", - " bar[a](b)\n", - "\n", - " # `bar[*a: Int](b: Int)` is called because it has variadic parameters.\n", - " bar[a, a, a](b)\n", - "\n", - "parameter_overloads[1, 2, MyInt(3)]()\n", - "\n", - "struct MyStruct:\n", - " fn __init__(inout self):\n", - " pass\n", - "\n", - " fn foo(inout self):\n", - " print(\"calling instance menthod\")\n", - "\n", - " @staticmethod\n", - " fn foo():\n", - " print(\"calling static menthod\")\n", - "\n", - "fn test_static_overload():\n", - " var a = MyStruct()\n", - " # `foo(inout self)` takes precedence over a static method.\n", - " a.foo()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using parameterized types and functions\n", - "\n", - "You can instantiate parametric types and functions by passing values to the\n", - "parameters in square brackets. For example, for the `SIMD` type above, `type`\n", - "specifies the data type and `size` specifies the length of the SIMD vector (it\n", - "must be a power of 2):" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "small_vec type: float32 length: 4\n", - "bigger_vec2 type: float32 length: 32\n" - ] - } - ], - "source": [ - "# Make a vector of 4 floats.\n", - "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n", - "\n", - "# Make a big vector containing 1.0 in float16 format.\n", - "var big_vec = SIMD[DType.float16, 32](1.0)\n", - "\n", - "# Do some math and convert the elements to float32.\n", - "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n", - "\n", - "# You can write types out explicitly if you want of course.\n", - "var bigger_vec2 : SIMD[DType.float32, 32] = bigger_vec\n", - "\n", - "print('small_vec type:', small_vec.element_type, 'length:', len(small_vec))\n", - "print('bigger_vec2 type:', bigger_vec2.element_type, 'length:', len(bigger_vec2))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `cast()` method also needs a parameter to specify the type you\n", - "want from the cast (the method definition above expects a `target` parametric\n", - "value). Thus, just like how the `SIMD` struct is a generic type definition, the\n", - "`cast()` method is a generic method definition that gets instantiated at\n", - "compile-time instead of runtime, based on the parameter value.\n", - "\n", - "The code above shows the use of concrete types (that is, it\n", - "instantiates `SIMD` using known type values), but the major power of parameters\n", - "comes from the ability to define parametric algorithms and types (code that\n", - "uses the parameter values). For example, here's how to define a parametric\n", - "algorithm with `SIMD` that is type- and width-agnostic:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0.154296875, 0.154296875, 0.154296875, 0.154296875]\n", - "\n" - ] - } - ], - "source": [ - "from math import sqrt\n", - "\n", - "fn rsqrt[dt: DType, width: Int](x: SIMD[dt, width]) -> SIMD[dt, width]:\n", - " return 1 / sqrt(x)\n", - "\n", - "print(rsqrt[DType.float16, 4](42))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the `x` argument is actually a `SIMD` type based on the function\n", - "parameters. The runtime program can use the value of parameters, because the\n", - "parameters are resolved at compile-time before they are needed by the runtime\n", - "program (but compile-time parameter expressions cannot use runtime values).\n", - "\n", - "The Mojo compiler is also smart about type inference with parameters. Note\n", - "that the above function is able to call the parametric\n", - "[`sqrt[]()`](https://docs.modular.com/mojo/stdlib/math/math.html#sqrt) function\n", - "without specifying the parameters—the compiler infers its parameters based on\n", - "the parametric `x` value passed into it, as if you\n", - "wrote `sqrt[dt, width](x)` explicitly. Also note that `rsqrt()` chose to\n", - "define its first parameter named `width` even though the `SIMD` type names it\n", - "`size`, and there is no problem." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optional parameters and keyword parameters\n", - "\n", - "Just as you can specify [optional arguments](/mojo/manual/basics/#optional-arguments)\n", - "in function signatures, you can also define an optional _parameter_ by \n", - "giving it a default value. You can also pass parameters by keyword.\n", - "For a function or struct with multiple optional parameters, using keywords\n", - "allows you to pass only the parameters you want to specify, regardless of\n", - "their position in the function signature. \n", - "\n", - "For example, here's a function with two parameters, each with a default value:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "fn speak[a: Int = 3, msg: StringLiteral = \"woof\"]():\n", - " print(msg, a)\n", - "\n", - "fn use_defaults() raises:\n", - " speak() # prints 'woof 3'\n", - " speak[5]() # prints 'woof 5'\n", - " speak[7, \"meow\"]() # prints 'meow 7'\n", - " speak[msg=\"baaa\"]() # prints 'baaa 3'\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Recall that Mojo can infer parameter values in a parametric function, based on\n", - "the parametric values attached to an argument value (see the `rsqrt[]()`\n", - "example above). If the parametric function also has a default value defined,\n", - "then the inferred parameter type takes precedence.\n", - "\n", - "For example, in the following code, we update the parametric `speak[]()` function\n", - "to take an argument with a parametric type. Although the function has a default\n", - "parameter value for `a`, Mojo instead uses the inferred `a` parameter value\n", - "from the `bar` argument (as written, the default `a` value can never be used,\n", - "but this is just for demonstration purposes):" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct Bar[v: Int]:\n", - " pass\n", - "\n", - "fn foo[a: Int = 3, msg: StringLiteral = \"woof\"](bar: Bar[a]):\n", - " print(msg, a)\n", - "\n", - "fn use_inferred():\n", - " foo(Bar[9]()) # prints 'woof 9'\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As mentioned above, you can also use optional parameters and keyword \n", - "parameters in a struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "struct KwParamStruct[greeting: String = \"Hello\", name: String = \"🔥mojo🔥\"]:\n", - " fn __init__(inout self):\n", - " print(greeting, name)\n", - "\n", - "fn use_kw_params():\n", - " var a = KwParamStruct[]() # prints 'Hello 🔥mojo🔥'\n", - " var b = KwParamStruct[name=\"World\"]() # prints 'Hello World'\n", - " var c = KwParamStruct[greeting=\"Hola\"]() # prints 'Hola 🔥mojo🔥'\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
    \n", - "\n", - "**Note:** Mojo currently includes only partial support for keyword parameters, so\n", - "some features such as keyword-only parameters and variadic keyword parameters \n", - "(for example, `**kwparams`) are not supported yet.\n", - "\n", - "
    " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Automatic parameterization of functions\n", - "\n", - "Mojo supports \"automatic\" parameterization of functions. If a \n", - "function argument type is parametric but the function signature\n", - "*doesn't* specify the parameters, they are automatically added as \n", - "input parameters on the function. This is easier to understand\n", - "with an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "fn print_params(vec: SIMD):\n", - " print(vec.type)\n", - " print(vec.size)\n", - "\n", - "fn main():\n", - " var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)\n", - " print_params(v)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the above example, the `print_params` function is automatically \n", - "parameterized. It takes a parameterized type (`SIMD`), but doesn't specify \n", - "parameter values for it. Instead, it treats the types' parameters as its own, as\n", - "if you had written them explicitly:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "fn print_params[type: DType, size: Int](vec: SIMD[type, size]):\n", - " print(vec.type)\n", - " print(vec.size)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "When you pass `print_params()` a concrete instance of the `SIMD` type, it can \n", - "access the original input parameters as attributes on the instance (for example,\n", - "`vec.type`). This is necessary for an automatically parameterized\n", - "function, since it doesn't have any other way to access the parameters. But it \n", - "actually works in any context. You can access the input parameters of a \n", - "parameterized type as attributes on the type:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "fn main():\n", - " print(SIMD[DType.float32, 2].size) # prints 2\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or as attributes on an _instance_ of the type:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "fn main():\n", - " var x = SIMD[DType.int32, 2](4, 8)\n", - " print(x.type) # prints int32\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Parameter expressions are just Mojo code\n", - "\n", - "A parameter expression is any code expression (such as `a+b`) that occurs where\n", - "a parameter is expected. Parameter expressions support operators and function\n", - "calls, just like runtime code, and all parameter types use the same type\n", - "system as the runtime program (such as `Int` and `DType`).\n", - "\n", - "Because parameter expressions use the same grammar and types as runtime\n", - "Mojo code, you can use many \"dependent type\" features. For example, you might\n", - "want to define a helper function to concatenate two SIMD vectors:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "result type: float32 length: 4\n" - ] - } - ], - "source": [ - "fn concat[ty: DType, len1: Int, len2: Int](\n", - " lhs: SIMD[ty, len1], rhs: SIMD[ty, len2]) -> SIMD[ty, len1+len2]:\n", - "\n", - " var result = SIMD[ty, len1 + len2]()\n", - " for i in range(len1):\n", - " result[i] = SIMD[ty, 1](lhs[i])\n", - " for j in range(len2):\n", - " result[len1 + j] = SIMD[ty, 1](rhs[j])\n", - " return result\n", - "\n", - "var a = SIMD[DType.float32, 2](1, 2)\n", - "var x = concat[DType.float32, 2, 2](a, a)\n", - "\n", - "print('result type:', x.element_type, 'length:', len(x))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note how the resulting length is the sum of the input vector lengths, and you\n", - "can express that with a simple `+` operation. For a more complex example, take\n", - "a look at the [`SIMD.shuffle()`](https://docs.modular.com/mojo/MojoStdlib/SIMD.html#shuffle) method in\n", - "the standard library: it takes two input SIMD values, a vector shuffle mask as\n", - "a list, and returns a SIMD that matches the length of the shuffle mask.\n", - "\n", - "### Powerful compile-time programming\n", - "\n", - "While simple expressions are useful, sometimes you want to write imperative\n", - "compile-time logic with control flow. For example, the `isclose()` function in\n", - "the Mojo `Math` module uses exact equality for integers but \"close\" comparison\n", - "for floating-point. You can even do compile-time recursion. For instance, here\n", - "is an example \"tree reduction\" algorithm that sums all elements of a vector\n", - "recursively into a scalar:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 2, 3, 4]\n", - "Elements sum: 10\n" - ] - } - ], - "source": [ - "fn slice[ty: DType, new_size: Int, size: Int](\n", - " x: SIMD[ty, size], offset: Int) -> SIMD[ty, new_size]:\n", - " var result = SIMD[ty, new_size]()\n", - " for i in range(new_size):\n", - " result[i] = SIMD[ty, 1](x[i + offset])\n", - " return result\n", - "\n", - "fn reduce_add[ty: DType, size: Int](x: SIMD[ty, size]) -> Int:\n", - " @parameter\n", - " if size == 1:\n", - " return int(x[0])\n", - " elif size == 2:\n", - " return int(x[0]) + int(x[1])\n", - "\n", - " # Extract the top/bottom halves, add them, sum the elements.\n", - " alias half_size = size // 2\n", - " var lhs = slice[ty, half_size, size](x, 0)\n", - " var rhs = slice[ty, half_size, size](x, half_size)\n", - " return reduce_add[ty, half_size](lhs + rhs)\n", - "\n", - "var x = SIMD[DType.index, 4](1, 2, 3, 4)\n", - "print(x)\n", - "print(\"Elements sum:\", reduce_add[DType.index, 4](x))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This makes use of the `@parameter if` feature, which is an `if` statement that\n", - "runs at compile-time. It requires that its condition be a valid parameter\n", - "expression, and ensures that only the live branch of the `if` statement is\n", - "compiled into the program.\n", - "\n", - "### Mojo types are just parameter expressions\n", - "\n", - "While we've shown how you can use parameter expressions within types, type\n", - "annotations can themselves be arbitrary expressions (just like in Python).\n", - "Types in Mojo have a special metatype type, allowing type-parametric algorithms\n", - "and functions to be defined. \n", - "\n", - "For example, we can create a simplified `Array` that supports arbitrary types of\n", - "the elements (via the `AnyTrivialRegType` parameter):" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3.1400001049041748 3.1400001049041748 3.1400001049041748 3.1400001049041748\n" - ] - } - ], - "source": [ - "struct Array[T: AnyTrivialRegType]:\n", - " var data: Pointer[T]\n", - " var size: Int\n", - "\n", - " fn __init__(inout self, size: Int, value: T):\n", - " self.size = size\n", - " self.data = Pointer[T].alloc(self.size)\n", - " for i in range(self.size):\n", - " self.data[i] = value\n", - "\n", - " fn __getitem__(self, i: Int) -> T:\n", - " return self.data[i]\n", - "\n", - " fn __del__(owned self):\n", - " self.data.free()\n", - "\n", - "var v = Array[Float32](4, 3.14)\n", - "print(v[0], v[1], v[2], v[3])\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the `T` parameter is being used as the formal type for the\n", - "`value` arguments and the return type of the `__getitem__` function. Parameters\n", - "allow the `Array` type to provide different APIs based on the different\n", - "use-cases. \n", - "\n", - "There are many other cases that benefit from more advanced use of parameters.\n", - "For example, you can execute a closure N times in parallel, feeding in a value\n", - "from the context, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "fn parallelize[func: fn (Int) -> None](num_work_items: Int):\n", - " # Not actually parallel: see the 'algorithm' module for real implementation.\n", - " for i in range(num_work_items):\n", - " func(i)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another example where this is important is with variadic generics, where an\n", - "algorithm or data structure may need to be defined over a list of heterogeneous\n", - "types such as for a tuple:\n", - "\n", - "```mojo\n", - "struct Tuple[*Ts: AnyTrivialRegType]:\n", - " var _storage : *Ts\n", - "```\n", - "\n", - "And although we don't have enough metatype helpers in place yet, we should be\n", - "able to write something like this in the future (though overloading is still a\n", - "better way to handle this):\n", - "\n", - "```mojo\n", - "struct Array[T: AnyTrivialRegType]:\n", - " fn __getitem__[IndexType: AnyTrivialRegType](self, idx: IndexType)\n", - " -> (ArraySlice[T] if issubclass(IndexType, Range) else T):\n", - " ...\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `alias`: named parameter expressions\n", - "\n", - "It is very common to want to *name* compile-time values. Whereas `var` defines a\n", - "runtime value, and `let` defines a runtime constant, we need a way to define a\n", - "compile-time temporary value. For this, Mojo uses an `alias` declaration. \n", - "\n", - "For example, the `DType` struct implements a simple enum using aliases for the\n", - "enumerators like this (the actual `DType` implementation details vary a bit):\n", - "\n", - "```mojo\n", - "struct DType:\n", - " var value : UI8\n", - " alias invalid = DType(0)\n", - " alias bool = DType(1)\n", - " alias int8 = DType(2)\n", - " alias uint8 = DType(3)\n", - " alias int16 = DType(4)\n", - " alias int16 = DType(5)\n", - " ...\n", - " alias float32 = DType(15)\n", - "```\n", - "\n", - "This allows clients to use `DType.float32` as a parameter expression (which also\n", - "works as a runtime value) naturally. Note that this is invoking the\n", - "runtime constructor for `DType` at compile-time.\n", - "\n", - "Types are another common use for alias. Because types are compile-time\n", - "expressions, it is handy to be able to do things like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "alias Float16 = SIMD[DType.float16, 1]\n", - "alias UInt8 = SIMD[DType.uint8, 1]\n", - "\n", - "var x : Float16 # FLoat16 works like a \"typedef\"\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Like `var` and `let`, aliases obey scope, and you can use local aliases within\n", - "functions as you'd expect.\n", - "\n", - "By the way, both `None` and `AnyTrivialRegType` are defined as [type\n", - "aliases](https://docs.modular.com/mojo/MojoBuiltin/TypeAliases.html)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## \"Value Lifecycle\": Birth, life and death of a value\n", - "\n", - "At this point, you should understand the core semantics and features for Mojo\n", - "functions and types, so we can now discuss how they fit together to express\n", - "new types in Mojo.\n", - "\n", - "Many existing languages express design points with different tradeoffs: C++, for\n", - "example, is very powerful but often accused of\n", - "\"getting the defaults wrong\" which leads to bugs and mis-features. Swift is\n", - "easy to work with, but has a less predictable model that copies values a lot and\n", - "is dependent on an \"ARC optimizer\" for performance. Rust started with strong\n", - "value ownership goals to satisfy its borrow checker, but relies on values being\n", - "movable, which makes it challenging to express custom move constructors and\n", - "can put a lot of stress on `memcpy` performance. In Python, everything is a\n", - "reference to a class, so it never really faces issues with types.\n", - "\n", - "For Mojo, we've learned from these existing systems, and we aim to\n", - "provide a model that's very powerful while still easy to learn and understand.\n", - "We also don't want to require \"best effort\" and difficult-to-predict\n", - "optimization passes built into a \"sufficiently smart\" compiler.\n", - "\n", - "To explore these issues, we look at different value classifications and the\n", - "relevant Mojo features that go into expressing them, and build from the\n", - "bottom-up. We use C++ as the primary comparison point in examples because it is\n", - "widely known, but we occasionally reference other languages if they provide a\n", - "better comparison point.\n", - "\n", - "### Types that cannot be instantiated\n", - "\n", - "The most bare-bones type in Mojo is one that doesn't allow you to create\n", - "instances of it: these types have no initializer at all, and if they have a\n", - "destructor, it will never be invoked (because there cannot be instances to\n", - "destroy):" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "struct NoInstances:\n", - " var state: Int # Pretty useless\n", - "\n", - " alias my_int = Int\n", - "\n", - " @staticmethod\n", - " fn print_hello():\n", - " print(\"hello world\")\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo types do not get default constructors, move constructors, member-wise\n", - "initializers or anything else by default, so it is impossible to create an\n", - "instance of this `NoInstances` type. In order to get them, you need to define\n", - "an `__init__` method or use a decorator that synthesizes an initializer. As\n", - "shown, these types can be useful as \"namespaces\" because you can refer to\n", - "static members like `NoInstances.my_int` or `NoInstances.print_hello()` even\n", - "though you cannot instantiate an instance of the type.\n", - "\n", - "### Non-movable and non-copyable types\n", - "\n", - "If we take a step up the ladder of sophistication, we’ll get to types that can\n", - "be instantiated, but once they are pinned to an address in memory, they cannot\n", - "be implicitly moved or copied. This can be useful to implement types like\n", - "atomic operations (such as `std::atomic` in C++) or other types where the memory\n", - "address of the value is its identity and is critical to its purpose:" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```mojo\n", - "struct Atomic:\n", - " var state: Int\n", - "\n", - " fn __init__(inout self, state: Int = 0):\n", - " self.state = state\n", - "\n", - " fn __iadd__(inout self, rhs: Int):\n", - " #...atomic magic...\n", - "\n", - " fn get_value(self) -> Int:\n", - " return atomic_load_int(self.state)\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This class defines an initializer but no copy or move constructors, so once it\n", - "is initialized it can never be moved or copied. This is safe and useful because\n", - "Mojo's ownership system is fully \"address correct\" - when this is initialized\n", - "onto the stack or in the field of some other type, it never needs to move.\n", - "\n", - "Note that Mojo’s approach controls only the built-in move operations, such as\n", - "`a = b` copies and the [`^` transfer operator](#owned-arguments). One\n", - "useful pattern you can use for your own types (like `Atomic` above) is to add\n", - "an explicit `copy()` method (a non-\"dunder\" method). This can be useful to make\n", - "explicit copies of an instance when it is known safe to the programmer." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unique \"move-only\" types\n", - "\n", - "If we take one more step up the ladder of capabilities, we will encounter types\n", - "that are \"unique\" - there are many examples of this in C++, such as types like\n", - "`std::unique_ptr` or even a `FileDescriptor` type that owns an underlying POSIX\n", - "file descriptor. These types are pervasive in languages like Rust, where\n", - "copying is discouraged, but \"move\" is free. In Mojo, you can implement these\n", - "kinds of moves by defining the `__moveinit__` method to take ownership of a\n", - "unique type. For example:" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "```mojo\n", - "# This is a simple wrapper around POSIX-style fcntl.h functions.\n", - "struct FileDescriptor:\n", - " var fd: Int\n", - "\n", - " # This is how we move our unique type.\n", - " fn __moveinit__(inout self, owned existing: Self):\n", - " self.fd = existing.fd\n", - "\n", - " # This takes ownership of a POSIX file descriptor.\n", - " fn __init__(inout self, fd: Int):\n", - " self.fd = fd\n", - "\n", - " fn __init__(inout self, path: String):\n", - " # Error handling omitted, call the open(2) syscall.\n", - " self = FileDescriptor(open(path, ...))\n", - "\n", - " fn __del__(owned self):\n", - " close(self.fd) # pseudo code, call close(2)\n", - "\n", - " fn dup(self) -> Self:\n", - " # Invoke the dup(2) system call.\n", - " return Self(dup(self.fd))\n", - " fn read(...): ...\n", - " fn write(...): ...\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The consuming move constructor (`__moveinit__`) takes ownership of an existing\n", - "`FileDescriptor`, and moves its internal implementation details over to a new\n", - "instance. This is because instances of `FileDescriptor` may exist at different\n", - "locations, and they can be logically moved around—stealing the body of one\n", - "value and moving it into another.\n", - "\n", - "Here is an egregious example that will invoke `__moveinit__` multiple times:" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```mojo\n", - "fn egregious_moves(owned fd1: FileDescriptor):\n", - " # fd1 and fd2 have different addresses in memory, but the\n", - " # transfer operator moves unique ownership from fd1 to fd2.\n", - " var fd2 = fd1^\n", - "\n", - " # Do it again, a use of fd2 after this point will produce an error.\n", - " var fd3 = fd2^\n", - "\n", - " # We can do this all day...\n", - " var fd4 = fd3^\n", - " fd4.read(...)\n", - " # fd4.__del__() runs here\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note how ownership of the value is transferred between various values that own\n", - "it, using the postfix-`^` \"transfer\" operator, which destroys a previous\n", - "binding and transfer ownership to a new constant. If you are familiar with C++,\n", - "the simple way to think about the transfer operator is like `std::move`, but in\n", - "this case, we can see that it is able to move things without resetting them to\n", - "a state that can be destroyed: in C++, if your move operator failed to change\n", - "the old value’s `fd` instance, it would get closed twice.\n", - "\n", - "Mojo tracks the liveness of values and allows you to define custom move\n", - "constructors. This is rarely needed, but extremely powerful when it is. For\n", - "example, some types like the [`llvm::SmallVector\n", - "type`](https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h)\n", - "use the \"inline storage\" optimization technique, and they may want to be\n", - "implemented with an \"inner pointer\" into their instance. This is a well-known\n", - "trick to reduce pressure on the malloc memory allocator, but it means that a\n", - "\"move\" operation needs custom logic to update the pointer when that happens.\n", - "\n", - "With Mojo, this is as simple as implementing a custom `__moveinit__` method.\n", - "This is something that is also easy to implement in C++ (though, with\n", - "boilerplate in the cases where you don’t need custom logic) but is difficult to\n", - "implement in other popular memory-safe languages.\n", - "\n", - "One additional note is that while the Mojo compiler provides good predictability\n", - "and control, it is also very sophisticated. It reserves the right to eliminate\n", - "temporaries and the corresponding copy/move operations. If this is\n", - "inappropriate for your type, you should use explicit methods like `copy()`\n", - "instead of the dunder methods." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Copyable types\n", - "\n", - "The next step up from movable types are copyable types. Copyable types are\n", - "also very common - programmers generally expect things like strings and arrays\n", - "to be copyable, and every Python Object reference is copyable - by copying the\n", - "pointer and adjusting the reference count.\n", - "\n", - "There are many ways to implement copyable types. One can implement reference\n", - "semantic types like Python or Java, where you propagate shared pointers around,\n", - "one can use immutable data structures that are easily shareable because they are\n", - "never mutated once created, and one can implement deep value semantics through\n", - "lazy copy-on-write as Swift does. Each of these approaches has different\n", - "tradeoffs, and Mojo takes the opinion that while we want a few common sets of\n", - "collection types, we can also support a wide range of specialized ones that\n", - "focus on particular use cases.\n", - "\n", - "In Mojo, you can do this by implementing the `__copyinit__` method. Here is an\n", - "example of that using a simple `String` (in pseudo-code):" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```mojo\n", - "struct MyString:\n", - " var data: Pointer[UI8]\n", - "\n", - " # StringRef is a pointer + length and works with StringLiteral.\n", - " def __init__(inout self, input: StringRef):\n", - " self.data = ...\n", - "\n", - " # Copy the string by deep copying the underlying malloc'd data.\n", - " def __copyinit__(inout self, existing: Self):\n", - " self.data = strdup(existing.data)\n", - "\n", - " # This isn't required, but optimizes unneeded copies.\n", - " def __moveinit__(inout self, owned existing: Self):\n", - " self.data = existing.data\n", - "\n", - " def __del__(owned self):\n", - " free(self.data.address)\n", - "\n", - " def __add__(self, rhs: MyString) -> MyString: ...\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This simple type is a pointer to a \"null-terminated\" string data allocated with\n", - "malloc, using old-school C APIs for clarity. It implements the `__copyinit__`,\n", - "which maintains the invariant that each instance of `MyString` owns its\n", - "underlying pointer and frees it upon destruction. This implementation builds on\n", - "tricks we’ve seen above, and implements a `__moveinit__` constructor, which\n", - "allows it to completely eliminate temporary copies in some common cases. You\n", - "can see this behavior in this code sequence:" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```mojo\n", - "fn test_my_string():\n", - " var s1 = MyString(\"hello \")\n", - "\n", - " var s2 = s1 # s2.__copyinit__(s1) runs here\n", - "\n", - " print(s1)\n", - "\n", - " var s3 = s1^ # s3.__moveinit__(s1) runs here\n", - "\n", - " print(s2)\n", - " # s2.__del__() runs here\n", - " print(s3)\n", - " # s3.__del__() runs here\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this case, you can see both why a copy constructor is needed: without one,\n", - "the duplication of the `s1` value into `s2` would be an error - because you\n", - "cannot have two live instances of the same non-copyable type. The move\n", - "constructor is optional but helps the assignment into `s3`: without it, the\n", - "compiler would invoke the copy constructor from s1, then destroy the old `s1`\n", - "instance. This is logically correct but introduces extra runtime overhead.\n", - "\n", - "Mojo destroys values eagerly, which allows it to transform\n", - "copy+destroy pairs into single move operations, which can lead to much better\n", - "performance than C++ without requiring the need for pervasive micromanagement\n", - "of `std::move`." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Trivial types\n", - "\n", - "The most flexible types are ones that are just \"bags of bits\". These types are\n", - "\"trivial\" because they can be copied, moved, and destroyed without invoking\n", - "custom code. Types like these are arguably the most common basic type that\n", - "surrounds us: things like integers and floating point values are all trivial.\n", - "From a language perspective, Mojo doesn’t need special support for these, it\n", - "would be perfectly fine for type authors to implement these things as no-ops,\n", - "and allow the inliner to just make them go away.\n", - "\n", - "There are two reasons that approach would be suboptimal: one is that we don’t\n", - "want the boilerplate of having to define a bunch of methods on trivial types,\n", - "and second, we don’t want the compile-time overhead of generating and pushing\n", - "around a bunch of function calls, only to have them inline away to nothing.\n", - "Furthermore, there is an orthogonal concern, which is that many of these types\n", - "are trivial in another way: they are tiny, and should be passed around in the\n", - "registers of a CPU, not indirectly in memory.\n", - "\n", - "As such, Mojo provides a struct decorator that solves all of these problems.\n", - "You can implement a type with the `@register_passable(\"trivial\")` decorator,\n", - "and this tells Mojo that the type should be copyable and movable but that it\n", - "has no user-defined logic for doing this. It also tells Mojo to prefer to pass\n", - "the value in CPU registers, which can lead to efficiency benefits.\n", - "\n", - "TODO: This decorator is due for reconsideration. Lack of custom logic\n", - "copy/move/destroy logic and \"passability in a register\" are orthogonal concerns\n", - "and should be split. This former logic should be subsumed into a more general\n", - "`@value(\"trivial\")` decorator, which is orthogonal from `@register_passable`." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `@value` decorator\n", - "\n", - "Mojo's [value lifecycle](#value-lifecycle-birth-life-and-death-of-a-value) provides simple and predictable\n", - "hooks that give you the ability to express exotic low-level things like\n", - "`Atomic` correctly. This is great for control and for a simple programming\n", - "model, but most structs are simple aggregations of other types,\n", - "and we don't want to write a lot of boilerplate for them. To solve\n", - "this, Mojo provides a `@value` decorator for structs that synthesizes a lot of\n", - "boilerplate for you.\n", - "\n", - "You can think of `@value` as an extension of Python's\n", - "[`@dataclass`](https://docs.python.org/3/library/dataclasses.html) that also\n", - "handles Mojo's `__moveinit__` and `__copyinit__` methods.\n", - "\n", - "The `@value` decorator takes a look at the fields of your type, and generates\n", - "some members that are missing. For example, consider a simple struct like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo will notice that you do not have a member-wise initializer, a move\n", - "constructor, or a copy constructor, and it will synthesize these for you as if\n", - "you had written:" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(inout self, owned name: String, age: Int):\n", - " self.name = name^\n", - " self.age = age\n", - "\n", - " fn __copyinit__(inout self, existing: Self):\n", - " self.name = existing.name\n", - " self.age = existing.age\n", - "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", - " self.name = existing.name^\n", - " self.age = existing.age\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you add the `@value` decorator, Mojo synthesizes each of these special\n", - "methods only when it doesn't exist. You can override the behavior of one or\n", - "more by defining your own version. For example, it is fairly common to want a\n", - "custom copy constructor but use the default member-wise and move constructor.\n", - "\n", - "The arguments to`__init__` are all passed as `owned` arguments since the struct\n", - "takes ownership and stores the value. This is a useful micro-optimization and\n", - "enables the use of move-only types. Trivial types like `Int` are also passed\n", - "as owned values, but since that doesn't mean anything for them, we elide the\n", - "marker and the transfer operator (`^`) for clarity.\n", - "\n", - "
    \n", - "\n", - "**Note:** If your type contains any [move-only](#unique-move-only-types)\n", - "fields, Mojo will not generate a copy constructor because it cannot copy those\n", - "fields. Further, the `@value` decorator only works on types whose members are\n", - "copyable and/or movable. If you have something like `Atomic` in your struct,\n", - "then it probably isn't a value type, and you don't want these members anyway.\n", - "\n", - "Also notice that the `MyPet` struct above doesn't include the `__del__()`\n", - "destructor—Mojo also synthesizes this, but it doesn't require the `@value`\n", - "decorator (see the section below about [destructors](#behavior-of-destructors)).\n", - "\n", - "
    \n", - "\n", - "There is no way to suppress the generation of specific methods or customize\n", - "generation at this time, but we can add arguments to the `@value` generator to\n", - "do this if there is demand." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Behavior of destructors\n", - "\n", - "Any struct in Mojo can have a destructor (a `__del__()` method), which is\n", - "automatically run when the value's lifetime ends (typically the point at which\n", - "the value is last used). For example, a simple string might look like this (in\n", - "pseudo code):\n", - "\n", - "```mojo\n", - "@value\n", - "struct MyString:\n", - " var data: Pointer[UInt8]\n", - "\n", - " def __init__(inout self, input: StringRef): ...\n", - " def __add__(self, rhs: String) -> MyString: ...\n", - " def __del__(owned self):\n", - " free(self.data.address)\n", - "```\n", - "\n", - "Mojo destroys values like `MyString` (it calls the `__del__()` destructor)\n", - "using an **\"As Soon As Possible\"** (ASAP) policy that runs after every call.\n", - "Mojo does *not* wait until the end of the code block to destroy unused values.\n", - "Even in an expression like `a+b+c+d`, Mojo destroys the intermediate\n", - "expressions eagerly, as soon as they are no longer needed—it does not wait\n", - "until the end of the statement.\n", - "\n", - "The Mojo compiler automatically invokes the destructor when a value is dead\n", - "and provides strong guarantees about when the destructor is run. Mojo uses\n", - "static compiler analysis to reason about your code and decide when to insert\n", - "calls to the destructor. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "hello a\n", - "hello b\n", - "final a\n" - ] - } - ], - "source": [ - "fn use_strings():\n", - " var a = String(\"hello a\")\n", - " var b = String(\"hello b\")\n", - " print(a)\n", - " # a.__del__() runs here for \"hello a\"\n", - "\n", - "\n", - " print(b)\n", - " # b.__del__() runs here\n", - "\n", - " a = String(\"temporary a\")\n", - " # a.__del__() runs here because \"temporary a\" is never used\n", - "\n", - " # Other stuff happens here\n", - "\n", - " a = String(\"final a\")\n", - " print(a)\n", - " # a.__del__() runs again here for \"final a\"\n", - "\n", - "use_strings()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the code above, you’ll see that the `a` and `b` values are created early on,\n", - "and each initialization of a value is matched with a call to a destructor.\n", - "Notice that `a` is destroyed multiple times—once for each time it receives a\n", - "new value.\n", - "\n", - "Now, this might be surprising to a C++ programmer, because it's different from\n", - "the [RAII pattern](https://en.cppreference.com/w/cpp/language/raii) in which\n", - "C++ destroys values at the end of a scope. Mojo also follows the principle\n", - "that values acquire resources in a constructor and release resources in a\n", - "destructor, but eager destruction in Mojo has a number of strong advantages\n", - "over scope-based destruction in C++:\n", - "\n", - "- The Mojo approach eliminates the need for types to implement re-assignment\n", - " operators, like `operator=(const T&)` and `operator=(T&&)` in C++, making it\n", - " easier to define types and eliminating a concept.\n", - "\n", - "- Mojo does not allow mutable references to overlap with other mutable\n", - " references or with immutable borrows. One major way that it provides a\n", - " predictable programming model is by making sure that references to objects die\n", - " as soon as possible, avoiding confusing situations where the compiler thinks a\n", - " value could still be alive and interfere with another value, but that isn’t\n", - " clear to the user.\n", - "\n", - "- Destroying values at last-use composes nicely with \"move\" optimization, which\n", - " transforms a \"copy+del\" pair into a \"move\" operation, a generalization of\n", - " C++ move optimizations like NRVO (named return value optimization).\n", - "\n", - "- Destroying values at end-of-scope in C++ is problematic for some common\n", - " patterns like tail recursion because the destructor calls happen after the\n", - " tail call. This can be a significant performance and memory problem for\n", - " certain functional programming patterns.\n", - "\n", - "Importantly, Mojo's eager destruction also works well within Python-style `def`\n", - "functions to provide destruction guarantees (without a garbage collector) at a\n", - "fine-grain level—recall that Python doesn’t really provide scopes beyond a\n", - "function, so C++-style destruction in Mojo would be a lot less useful.\n", - "\n", - "
    \n", - "\n", - "**Note:** Mojo also supports the Python-style [`with`\n", - "statement](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement),\n", - "which provides more deliberately-scoped access to resources.\n", - "\n", - "
    \n", - "\n", - "The Mojo approach is more similar to how Rust and Swift work, because they both\n", - "have strong value ownership tracking and provide memory safety. One difference\n", - "is that their implementations require the use of a [dynamic \"drop\n", - "flag\"](https://doc.rust-lang.org/nomicon/drop-flags.html)—they maintain hidden\n", - "shadow variables to keep track of the state of your values to provide safety.\n", - "These are often optimized away, but the Mojo approach eliminates this overhead\n", - "entirely, making the generated code faster and avoiding ambiguity." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field-sensitive lifetime management\n", - "\n", - "In addition to Mojo’s lifetime analysis being fully control-flow aware, it is\n", - "also fully field-sensitive (each field of a structure is tracked\n", - "independently). That is, Mojo separately keeps track of whether a \"whole\n", - "object\" is fully or only partially initialized/destroyed.\n", - "\n", - "For example, consider this code:" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "foo\n", - "hello\n" - ] - } - ], - "source": [ - "@value\n", - "struct TwoStrings:\n", - " var str1: String\n", - " var str2: String\n", - "\n", - "fn use_two_strings():\n", - " var ts = TwoStrings(\"foo\", \"bar\")\n", - " print(ts.str1)\n", - " # ts.str1.__del__() runs here\n", - "\n", - " # Other stuff happens here\n", - "\n", - " ts.str1 = String(\"hello\") # Overwrite ts.str1\n", - " print(ts.str1)\n", - " # ts.__del__() runs here\n", - "\n", - "use_two_strings()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `ts.str1` field is destroyed almost immediately,\n", - "because Mojo knows that it will be overwritten down below. You can also see\n", - "this when using the [transfer operator](#owned-arguments), for example:" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "hello\n" - ] - } - ], - "source": [ - "fn consume(owned arg: String):\n", - " pass\n", - "\n", - "fn use(arg: TwoStrings):\n", - " print(arg.str1)\n", - "\n", - "fn consume_and_use_two_strings():\n", - " var ts = TwoStrings(\"foo\", \"bar\")\n", - " consume(ts.str1^)\n", - " # ts.str1.__moveinit__() runs here\n", - "\n", - " # ts is now only partially initialized here!\n", - "\n", - " ts.str1 = String(\"hello\") # All together now\n", - " use(ts) # This is ok\n", - " # ts.__del__() runs here\n", - "\n", - "consume_and_use_two_strings()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the code transfers ownership of the `str1` field: for the duration\n", - "of `other_stuff()`, the `str1` field is completely uninitialized because\n", - "ownership was transferred to `consume()`. Then\n", - "`str1` is reinitialized before it is used by the `use()` function (if it\n", - "weren’t, Mojo would reject the code with an uninitialized field error).\n", - "\n", - "Mojo's rule on this is powerful and intentionally straight-forward: fields can\n", - "be temporarily transferred, but the \"whole object\" must be constructed with the\n", - "aggregate type’s initializer and destroyed with the aggregate destructor. This\n", - "means that it isn’t possible to create an object by initializing only its\n", - "fields, nor is it possible to tear down an object by destroying only its\n", - "fields. For example, this code does not compile:" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "fn consume_and_use_two_strings():\n", - " var ts = TwoStrings(\"foo\", \"bar\") # ts is initialized\n", - " # Uncomment to see an error:\n", - " # consume(ts.str1^)\n", - " # Because `ts` is not used anymore, it should be destroyed here, but\n", - " # the object is not whole, preventing the overall value from being destroyed\n", - "\n", - " var ts2 : TwoStrings # ts2 type is declared but not initialized\n", - " ts2.str1 = String(\"foo\")\n", - " ts2.str2 = String(\"bar\") # Both the member are initialized\n", - " # Uncomment to see an error:\n", - " # use(ts2) # Error: 'ts2' isn't fully initialized\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "While we could allow patterns like this to happen, we reject this because a\n", - "value is more than a sum of its parts. Consider a `FileDescriptor` that\n", - "contains a POSIX file descriptor as an integer value: there is a\n", - "big difference between destroying the integer (a no-op) and destroying the\n", - "`FileDescriptor` (it might call the `close()` system call). Because of this, we\n", - "require all full-value initialization to go through initializers and be\n", - "destroyed with their full-value destructor.\n", - "\n", - "For what it's worth, Mojo does internally have an equivalent of the Rust\n", - "[`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) function,\n", - "which explicitly disables a destructor and has a corresponding internal feature\n", - "for \"blessing\" an object, but they aren’t exposed for user consumption at this\n", - "point." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field lifetimes in `__init__`\n", - "\n", - "The behavior of an `__init__` method works almost like any other method—there\n", - "is a small bit of magic: it knows that the fields of an object\n", - "are uninitialized, but it believes the full object is initialized. This means\n", - "that you can use `self` as a whole object as soon as all the fields are\n", - "initialized:" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "fn use(arg: TwoStrings2):\n", - " pass\n", - "\n", - "struct TwoStrings2:\n", - " var str1: String\n", - " var str2: String\n", - "\n", - " fn __init__(inout self, cond: Bool, other: String):\n", - " self.str1 = String()\n", - " if cond:\n", - " self.str2 = other\n", - " use(self) # Safe to use immediately!\n", - " # self.str2.__del__(): destroyed because overwritten below.\n", - "\n", - " self.str2 = self.str1\n", - " use(self) # Safe to use immediately!\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Similarly, it's safe for initializers in Mojo to completely\n", - "overwrite `self`, such as by delegating to other initializers:" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "struct TwoStrings3:\n", - " var str1: String\n", - " var str2: String\n", - "\n", - " fn __init__(inout self):\n", - " self.str1 = String()\n", - " self.str2 = String()\n", - "\n", - " fn __init__(inout self, one: String):\n", - " self = TwoStrings3() # Delegate to the basic init\n", - " self.str1 = one\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field lifetimes of `owned` arguments in `__moveinit__` and `__del__` \n", - "\n", - "A final bit of magic exists for the `owned` arguments of a `__moveinit__()` move\n", - "initializer and a `__del__()` destructor. To recap, these method signatures look\n", - "like this:\n", - "\n", - "```mojo\n", - "struct TwoStrings:\n", - " ...\n", - " fn __moveinit__(inout self, owned existing: Self):\n", - " # Initializes a new `self` by consuming the contents of `existing`\n", - " fn __del__(owned self):\n", - " # Destroys all resources in `self`\n", - "```\n", - "\n", - "These methods face an interesting but obscure problem: both methods are in\n", - "charge of dismantling the `owned` `existing`/`self` value. That is,\n", - "`__moveinit__()` destroys sub-elements of `existing` in order to transfer\n", - "ownership to a new instance, while `__del__()` implements the deletion logic\n", - "for its `self`. As such, they both want to own and transform elements of the\n", - "`owned` value, and they definitely don’t want the `owned` value's destructor to\n", - "also run (in the case of the `__del__()` method, that would turn into an\n", - "infinite loop).\n", - "\n", - "To solve this problem, Mojo handles these two methods specially by assuming\n", - "that their whole values are destroyed upon reaching any return from the method.\n", - "This means that the whole object may be used before the field values are\n", - "transferred. For example, this works as you expect:" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "str1: foo\n", - "str2: bar\n", - "Consumed foo\n" - ] - } - ], - "source": [ - "fn consume(owned str: String):\n", - " print('Consumed', str)\n", - "\n", - "struct TwoStrings4:\n", - " var str1: String\n", - " var str2: String\n", - "\n", - " fn __init__(inout self, one: String):\n", - " self.str1 = one\n", - " self.str2 = String(\"bar\")\n", - "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", - " self.str1 = existing.str1\n", - " self.str2 = existing.str2\n", - "\n", - " fn __del__(owned self):\n", - " self.dump() # Self is still whole here\n", - " # Mojo calls self.str2.__del__() since str2 isn't used anymore\n", - "\n", - " consume(self.str1^)\n", - " # str1 has now been transferred;\n", - " # `self.__del__()` is not called (avoiding an infinite loop).\n", - "\n", - " fn dump(inout self):\n", - " print('str1:', self.str1)\n", - " print('str2:', self.str2)\n", - "\n", - "fn use_two_strings():\n", - " var two_strings = TwoStrings4(\"foo\")\n", - "\n", - "# We use a function call to ensure the `two_strings` ownership is enforced\n", - "# (Currently, ownership is not enforced for top-level code in notebooks)\n", - "use_two_strings()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You should not generally have to think about this, but if you have logic with\n", - "inner pointers into members, you may need to keep them alive for some logic\n", - "within the destructor or move initializer itself. You can do this by assigning\n", - "to the `_` \"discard\" pattern:\n", - "\n", - "```mojo\n", - "fn __del__(owned self):\n", - " self.dump() # Self is still whole here\n", - "\n", - " consume(self.str1^)\n", - " _ = self.str2\n", - " # self.str2.__del__(): Mojo destroys str2 after its last use.\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this case, if `consume()` implicitly refers to some value in `str2` somehow,\n", - "this will ensure that `str2` isn’t destroyed until the last use when it is\n", - "accessed by the `_` discard pattern." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Defining the `__del__` destructor\n", - "\n", - "You should define the `__del__()` method to perform any kind of cleanup the\n", - "type requires. Usually, that includes freeing memory for any fields that are\n", - "not trivial or destructible—Mojo automatically destroys any trivial and\n", - "destructible types as soon as they're not used anymore.\n", - "\n", - "For example, consider this struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(inout self, owned name: String, age: Int):\n", - " self.name = name^\n", - " self.age = age\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There's no need to define the `__del__()` method because `String` is a\n", - "destructible (it has its own `__del__()` method) and Mojo destroys it as soon\n", - "as it's no longer used (which is exactly when the `MyPet` instance is no longer\n", - "used), and `Int` is a [trivial type](#trivial-types) and Mojo reclaims this\n", - "memory also as soon as possible (although a little differently, without need\n", - "for a `__del__()` method).\n", - "\n", - "Whereas, the following struct must define the `__del__()` method to free\n", - "the memory allocated for its `Pointer`:" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [], - "source": [ - "struct Array[Type: AnyTrivialRegType]:\n", - " var data: Pointer[Type]\n", - " var size: Int\n", - "\n", - " fn __init__(inout self, size: Int, value: Type):\n", - " self.size = size\n", - " self.data = Pointer[Type].alloc(self.size)\n", - " for i in range(self.size):\n", - " self.data[i] = value\n", - "\n", - " fn __del__(owned self):\n", - " self.data.free()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Lifetimes\n", - "\n", - "TODO: Explain how returning references work, tied into lifetimes which dovetail\n", - "with parameters. This is not enabled yet.\n", - "\n", - "## Type traits\n", - "\n", - "This is a feature very much like Rust traits or Swift protocols or Haskell type\n", - "classes. Note, this is not implemented yet." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Advanced/Obscure Mojo features\n", - "\n", - "This section describes power-user features that are important for building the\n", - "bottom-est level of the standard library. This level of the stack is inhabited\n", - "by narrow features that require experience with compiler internals to\n", - "understand and utilize effectively.\n", - "\n", - "### `@register_passable` struct decorator\n", - "\n", - "The default model for working with values is\n", - "they live in memory, so they have an identity, which means they are passed\n", - "indirectly to and from functions (equivalently, they are passed \"by reference\"\n", - "at the machine level). This is great for types that cannot be moved, and is a\n", - "safe default for large objects or things with expensive copy operations.\n", - "However, it is inefficient for tiny things like a single integer or\n", - "floating point number.\n", - "\n", - "To solve this, Mojo allows structs to opt-in to being passed in a register\n", - "instead of passing through memory with the `@register_passable` decorator.\n", - "You'll see this decorator on types like `Int` in the standard library:\n", - "\n", - "```mojo\n", - "@register_passable(\"trivial\")\n", - "struct Int:\n", - " var value: __mlir_type.`!pop.scalar`\n", - "\n", - " fn __init__(value: __mlir_type.`!pop.scalar`) -> Self:\n", - " return Self {value: value}\n", - " ...\n", - "```\n", - "\n", - "The basic `@register_passable` decorator does not change the fundamental\n", - "behavior of a type: it still needs to have a `__copyinit__` method to be\n", - "copyable, may still have a `__init__` and `__del__` methods, etc. The major\n", - "effect of this decorator is on\n", - "internal implementation details: `@register_passable` types are typically\n", - "passed in machine registers (subject to the details of the underlying\n", - "architecture).\n", - "\n", - "There are only a few observable effects of this decorator to the typical Mojo\n", - "programmer:\n", - "\n", - "1. `@register_passable` types are not able to hold instances of types\n", - "that are not themselves `@register_passable`.\n", - "\n", - "2. Instances of `@register_passable` types do not have predictable identity,\n", - "and so the `self` pointer is not stable/predictable (e.g. in hash tables).\n", - "\n", - "3. `@register_passable` arguments and result are exposed to C and C++ directly,\n", - "instead of being passed by-pointer.\n", - "\n", - "4. The `__init__` and `__copyinit__` methods of this type are implicitly static\n", - "(like `__new__` in Python) and return their results by-value instead of taking\n", - "`inout self`.\n", - "\n", - "We expect that this decorator will be used pervasively on core standard library\n", - "types, but is safe to ignore for general application level code.\n", - "\n", - "The `Int` example above actually uses the \"trivial\" variant of this decorator.\n", - "It changes the passing convention as described above but also disallows copy\n", - "and move constructors and destructors (synthesizing them all trivially).\n", - "\n", - "> TODO: Trivial needs to be decoupled to its own decorator since it applies to\n", - "memory types as well.\n", - "\n", - "\n", - "\n", - "### `@always_inline` decorator\n", - "\n", - "`@always_inline(\"nodebug\")`: same thing but without debug information so you\n", - "don't step into the + method on Int.\n", - "\n", - "### `@parameter` decorator\n", - "\n", - "The `@parameter` decorator can be placed on nested functions that capture\n", - "runtime values to create \"parametric\" capturing closures. This is an unsafe\n", - "feature in Mojo, because we do not currently model the lifetimes of\n", - "capture-by-reference. A particular aspect of this feature is that it allows\n", - "closures that capture runtime values to be passed as parameter values.\n", - "\n", - "### Magic operators\n", - "\n", - "C++ code has a number of magic operators that intersect with value lifecycle,\n", - "things like \"placement new\", \"placement delete\" and \"operator=\" that reassign\n", - "over an existing value. Mojo is a safe language when you use all its language\n", - "features and compose on top of safe constructs, but of any stack is a world of\n", - "C-style pointers and rampant unsafety. Mojo is a pragmatic language, and since\n", - "we are interested in both interoperating with C/C++ and in implementing safe\n", - "constructs like String directly in Mojo itself, we need a way to express unsafe\n", - "things.\n", - "\n", - "The Mojo standard library `Pointer[element_type]` type is implemented with an\n", - "underlying `!kgen.pointer` type in MLIR, and we desire a way to\n", - "implement these C++-equivalent unsafe constructs in Mojo. Eventually, these\n", - "will migrate to all being methods on the Pointer type, but until then, some\n", - "need to be exposed as built-in operators.\n", - "\n", - "\n", - "\n", - "### Direct access to MLIR\n", - "\n", - "Mojo provides full access to the MLIR dialects and ecosystem. Please take a\n", - "look at the [Low level IR in Mojo](/mojo/notebooks/BoolMLIR.html) to learn how\n", - "to use the `__mlir_type`, `__mlir_op`, and `__mlir_type` constructs. All of the\n", - "built-in and standard library APIs are implemented by just calling the\n", - "underlying MLIR constructs, and in doing so, Mojo effectively serves as syntax\n", - "sugar on top of MLIR.\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} From 7bea19586f6fae72979750a57cd17d63af6026cc Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 9 Jul 2024 18:38:57 -0500 Subject: [PATCH 1180/2019] [External] [stdlib] Fix `String.split()` and start fixing `String.__len__()` (#43119) Fix string split to take in NoneType as input and its body to use `isspace(self)` method. Fixes #2880 Leave the fix for `lstrip` and `rstrip` to use `String.isspace()` as `TODO` once llvm intrinsics can be used at comp time Added a method `String.byte_length()` but left the builtin `String.__len__()` alone for now since many other methods assume it returns byte length and it will require mayor refactoring of too many places for 1 PR. Added deprecation warning to `_byte_length()` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#2960 MODULAR_ORIG_COMMIT_REV_ID: 8ed4f3418bc1681d45252d2373227d65eeb31e04 --- docs/changelog.md | 6 + stdlib/src/base64/base64.mojo | 8 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/file.mojo | 8 +- stdlib/src/builtin/format_int.mojo | 4 +- stdlib/src/builtin/io.mojo | 4 +- stdlib/src/builtin/string.mojo | 206 ++++++++++++++----------- stdlib/src/builtin/string_literal.mojo | 22 ++- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/sys/ffi.mojo | 2 +- stdlib/src/tempfile/tempfile.mojo | 4 +- stdlib/src/utils/inline_string.mojo | 10 +- stdlib/src/utils/string_slice.mojo | 42 +++-- stdlib/test/builtin/test_string.mojo | 29 +++- 14 files changed, 213 insertions(+), 136 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 461d08c8c0..bf8ca3c37e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -156,6 +156,12 @@ what we publish. - Added `StringSlice(..)` initializer from a `StringLiteral`. +- Added a `byte_length()` method to `String`, `StringSlice`, and `StringLiteral` +and deprecated their private `_byte_length()` methods. Added a warning to +`String.__len__` method that it will return length in Unicode codepoints in the +future and `StringSlice.__len__` now does return the Unicode codepoints length. +([PR #2960](https://github.com/modularml/mojo/pull/2960) by [@martinvuyk](https://github.com/martinvuyk)) + - Added new `StaticString` type alias. This can be used in place of `StringLiteral` for runtime string arguments. diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index d042c27e21..62e021d38f 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -72,7 +72,7 @@ fn b64encode(str: String) -> String: alias lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" var b64chars = lookup.unsafe_ptr() - var length = len(str) + var length = str.byte_length() var out = String._buffer_type(capacity=length + 1) @parameter @@ -121,7 +121,7 @@ fn b64decode(str: String) -> String: Returns: The decoded string. """ - var n = len(str) + var n = str.byte_length() debug_assert(n % 4 == 0, "Input length must be divisible by 4") var p = String._buffer_type(capacity=n + 1) @@ -170,7 +170,7 @@ fn b16encode(str: String) -> String: alias lookup = "0123456789ABCDEF" var b16chars = lookup.unsafe_ptr() - var length = len(str) + var length = str.byte_length() var out = List[UInt8](capacity=length * 2 + 1) @parameter @@ -221,7 +221,7 @@ fn b16decode(str: String) -> String: return -1 - var n = len(str) + var n = str.byte_length() debug_assert(n % 2 == 0, "Input length must be divisible by 2") var p = List[UInt8](capacity=n // 2 + 1) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index fc054e907b..7c79220d0c 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -80,7 +80,7 @@ struct Error( Returns: The constructed Error object. """ - var length = len(src) + var length = src.byte_length() var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy( dest=dest, diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 32132cad13..faa7e73653 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -239,7 +239,7 @@ struct FileHandle: var bytes = file.read(ptr, 8) print("bytes read", bytes) - var first_element = ptr.load(0) + var first_element = ptr[0] print(first_element) # Skip 2 elements @@ -374,7 +374,7 @@ struct FileHandle: ```mojo import os var f = open("/tmp/example.txt", "r") - f.seek(32, os.SEEK_CUR) + _ = f.seek(32, os.SEEK_CUR) ``` Start from 32 bytes from the end of the file: @@ -382,7 +382,7 @@ struct FileHandle: ```mojo import os var f = open("/tmp/example.txt", "r") - f.seek(-32, os.SEEK_END) + _ = f.seek(-32, os.SEEK_END) ``` . """ @@ -409,7 +409,7 @@ struct FileHandle: Args: data: The data to write to the file. """ - self._write(data.unsafe_ptr(), len(data)) + self._write(data.unsafe_ptr(), data.byte_length()) fn write(self, data: Span[UInt8, _]) raises: """Write a borrowed sequence of data to the file. diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 63a344c6d1..68dada8ce5 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -285,13 +285,13 @@ fn _try_write_int[ if radix < 2: return Error("Unable to format integer to string with radix < 2") - if radix > len(digit_chars): + if radix > digit_chars.byte_length(): return Error( "Unable to format integer to string when provided radix is larger " "than length of available digit value characters" ) - if not len(digit_chars) >= 2: + if not digit_chars.byte_length() >= 2: return Error( "Unable to format integer to string when provided digit_chars" " mapping len is not >= 2" diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 54ba79d319..f0d725f349 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -320,7 +320,7 @@ fn _put(x: DType, file: FileDescriptor = stdout): @no_inline fn _put(x: StringSlice, file: FileDescriptor = stdout): # Avoid printing "(null)" for an empty/default constructed `String` - var str_len = x._byte_length() + var str_len = x.byte_length() if not str_len: return @@ -341,7 +341,7 @@ fn _put(x: StringSlice, file: FileDescriptor = stdout): # The string can be printed, so that's fine. if str_len < MAX_STR_LEN: - _printf["%.*s"](x._byte_length(), x.unsafe_ptr(), file=file) + _printf["%.*s"](x.byte_length(), x.unsafe_ptr(), file=file) return # The string is large, then we need to chunk it. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 0c6fba4682..808a7d9c6f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -68,11 +68,11 @@ fn ord(s: StringSlice) -> Int: var p = s.unsafe_ptr().bitcast[UInt8]() var b1 = p[] if (b1 >> 7) == 0: # This is 1 byte ASCII char - debug_assert(s._byte_length() == 1, "input string length must be 1") + debug_assert(s.byte_length() == 1, "input string length must be 1") return int(b1) var num_bytes = countl_zero(~b1) debug_assert( - s._byte_length() == int(num_bytes), "input string must be one character" + s.byte_length() == int(num_bytes), "input string must be one character" ) debug_assert( 1 < int(num_bytes) < 5, "invalid UTF-8 byte " + str(b1) + " at index 0" @@ -1009,11 +1009,10 @@ struct String( Construct a String from several `Formattable` arguments: ```mojo - from testing import assert_equal - var string = String.format_sequence(1, ", ", 2.0, ", ", "three") - - assert_equal(string, "1, 2.0, three") + print(string) # "1, 2.0, three" + %# from testing import assert_equal + %# assert_equal(string, "1, 2.0, three") ``` . """ @@ -1077,6 +1076,7 @@ struct String( Returns: A new string containing the character at the specified position. """ + # TODO(#933): implement this for unicode when we support llvm intrinsic evaluation at compile time var normalized_idx = normalize_index["String"](idx, self) var buf = Self._buffer_type(capacity=1) buf.append(self._buffer[normalized_idx]) @@ -1095,13 +1095,12 @@ struct String( var start: Int var end: Int var step: Int - start, end, step = span.indices(len(self)) + # TODO(#933): implement this for unicode when we support llvm intrinsic evaluation at compile time + + start, end, step = span.indices(self.byte_length()) var r = range(start, end, step) if step == 1: - return StringRef( - self._buffer.data + start, - len(r), - ) + return StringRef(self._buffer.data + start, len(r)) var buffer = Self._buffer_type() var result_len = len(r) @@ -1198,8 +1197,8 @@ struct String( return other if not other: return self - var self_len = len(self) - var other_len = len(other) + var self_len = self.byte_length() + var other_len = other.byte_length() var total_len = self_len + other_len var buffer = Self._buffer_type() buffer.resize(total_len + 1, 0) @@ -1238,8 +1237,8 @@ struct String( return if not other: return - var self_len = len(self) - var other_len = len(other) + var self_len = self.byte_length() + var other_len = other.byte_length() var total_len = self_len + other_len self._buffer.resize(total_len + 1, 0) # Copy the data alongside the terminator. @@ -1256,7 +1255,7 @@ struct String( An iterator of references to the string elements. """ return _StringIter[__lifetime_of(self)]( - unsafe_pointer=self.unsafe_ptr(), length=len(self) + unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) fn __reversed__(ref [_]self) -> _StringIter[__lifetime_of(self), False]: @@ -1266,7 +1265,7 @@ struct String( A reversed iterator of references to the string elements. """ return _StringIter[__lifetime_of(self), forward=False]( - unsafe_pointer=self.unsafe_ptr(), length=len(self) + unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) # ===------------------------------------------------------------------=== # @@ -1280,20 +1279,24 @@ struct String( Returns: True if the string length is greater than zero, and False otherwise. """ - return len(self) > 0 + return self.byte_length() > 0 fn __len__(self) -> Int: - """Gets the string length, in bytes. + """Gets the string length, in bytes (for now) PREFER: + String.byte_length(), a future version will make this method return + Unicode codepoints. Returns: - The string length, in bytes. + The string length, in bytes (for now). """ - # Avoid returning -1 if the buffer is not initialized - if not self.unsafe_ptr(): - return 0 + var unicode_length = self.byte_length() + + # TODO: everything uses this method assuming it's byte length + # for i in range(unicode_length): + # if _utf8_byte_type(self._buffer[i]) == 1: + # unicode_length -= 1 - # The negative 1 is to account for the terminator. - return len(self._buffer) - 1 + return unicode_length @always_inline fn __str__(self) -> String: @@ -1448,7 +1451,7 @@ struct String( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef(self.unsafe_ptr(), len(self)) + return StringRef(self.unsafe_ptr(), self.byte_length()) fn _strref_keepalive(self): """ @@ -1498,19 +1501,18 @@ struct String( @always_inline fn as_bytes_slice(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: - """ - Returns a contiguous slice of the bytes owned by this string. - - This does not include the trailing null terminator. + """Returns a contiguous slice of the bytes owned by this string. Returns: A contiguous slice pointing to the bytes owned by this string. + + Notes: + This does not include the trailing null terminator. """ + # Does NOT include the NUL terminator. return Span[UInt8, __lifetime_of(self)]( - unsafe_ptr=self._buffer.unsafe_ptr(), - # Does NOT include the NUL terminator. - len=self._byte_length(), + unsafe_ptr=self._buffer.unsafe_ptr(), len=self.byte_length() ) @always_inline @@ -1525,21 +1527,30 @@ struct String( # guaranteed to be valid. return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) - fn _byte_length(self) -> Int: + @always_inline + fn byte_length(self) -> Int: """Get the string length in bytes. - This does not include the trailing null terminator in the count. - Returns: The length of this string in bytes, excluding null terminator. + + Notes: + This does not include the trailing null terminator in the count. """ + return max(len(self._buffer) - 1, 0) - var buffer_len = len(self._buffer) + @always_inline + @deprecated("use byte_length() instead") + fn _byte_length(self) -> Int: + """Get the string length in bytes. - if buffer_len > 0: - return buffer_len - 1 - else: - return buffer_len + Returns: + The length of this string in bytes, excluding null terminator. + + Notes: + This does not include the trailing null terminator in the count. + """ + return max(len(self._buffer) - 1, 0) fn _steal_ptr(inout self) -> UnsafePointer[UInt8]: """Transfer ownership of pointer to the underlying memory. @@ -1579,7 +1590,7 @@ struct String( break res += 1 - offset = pos + len(substr) + offset = pos + substr.byte_length() return res @@ -1654,11 +1665,11 @@ struct String( var ptr2 = DTypePointer(item2) return memcmp(ptr1, ptr2, amnt) == 0 - if len(self) == 0: + if self.byte_length() == 0: return False for s in self: - var no_null_len = len(s) + var no_null_len = s.byte_length() var ptr = s.unsafe_ptr() if no_null_len == 1 and not _isspace(ptr[0]): return False @@ -1698,15 +1709,15 @@ struct String( """ var output = List[String]() - var str_iter_len = len(self) - 1 + var str_byte_len = self.byte_length() - 1 var lhs = 0 var rhs = 0 var items = 0 - var sep_len = len(sep) + var sep_len = sep.byte_length() if sep_len == 0: raise Error("ValueError: empty separator") - while lhs <= str_iter_len: + while lhs <= str_byte_len: rhs = self.find(sep, lhs) if rhs == -1: output.append(self[lhs:]) @@ -1725,12 +1736,11 @@ struct String( output.append("") return output - fn split(self, *, maxsplit: Int = -1) -> List[String]: + fn split(self, sep: NoneType = None, maxsplit: Int = -1) -> List[String]: """Split the string by every Whitespace separator. - Currently only uses C style separators. - Args: + sep: None. maxsplit: The maximum amount of items to split from String. Defaults to unlimited. @@ -1746,43 +1756,40 @@ struct String( # Splitting a string with leading, trailing, and middle whitespaces _ = String(" hello world ").split() # ["hello", "world"] + # Splitting adjacent universal newlines: + _ = String( + "hello \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029world" + ).split() # ["hello", "world"] ``` . """ - # TODO: implement and document splitting adjacent universal newlines: - # _ = String( - # "hello \\t\\n\\r\\f\\v\\x1c\\x1e\\x85\\u2028\\u2029world" - # ).split() # ["hello", "world"] var output = List[String]() - - var str_iter_len = len(self) - 1 + var str_byte_len = self.byte_length() - 1 var lhs = 0 var rhs = 0 var items = 0 - # FIXME: this should iterate and build unicode strings - # and use self.isspace() - while lhs <= str_iter_len: + while lhs <= str_byte_len: # Python adds all "whitespace chars" as one separator # if no separator was specified - while lhs <= str_iter_len: - if not _isspace(self._buffer.unsafe_get(lhs)): + for s in self[lhs:]: + if not str(s).isspace(): # TODO: with StringSlice.isspace() break - lhs += 1 + lhs += s.byte_length() # if it went until the end of the String, then # it should be sliced up until the original # start of the whitespace which was already appended - if lhs - 1 == str_iter_len: + if lhs - 1 == str_byte_len: break - elif lhs == str_iter_len: + elif lhs == str_byte_len: # if the last char is not whitespace - output.append(self[str_iter_len]) + output.append(self[str_byte_len]) break rhs = lhs + 1 - while rhs <= str_iter_len: - if _isspace(self._buffer.unsafe_get(rhs)): + for s in self[lhs + 1 :]: + if str(s).isspace(): # TODO: with StringSlice.isspace() break - rhs += 1 + rhs += s.byte_length() if maxsplit > -1: if items == maxsplit: @@ -1805,7 +1812,7 @@ struct String( A List of Strings containing the input split by line boundaries. """ var output = List[String]() - var length = len(self) + var length = self.byte_length() var current_offset = 0 while current_offset < length: @@ -1856,9 +1863,9 @@ struct String( var self_ptr = self.unsafe_ptr() var new_ptr = new.unsafe_ptr() - var self_len = len(self) - var old_len = len(old) - var new_len = len(new) + var self_len = self.byte_length() + var old_len = old.byte_length() + var new_len = new.byte_length() var res = List[UInt8]() res.reserve(self_len + (old_len - new_len) * occurrences + 1) @@ -1923,7 +1930,7 @@ struct String( A copy of the string with no trailing characters. """ - var r_idx = len(self) + var r_idx = self.byte_length() while r_idx > 0 and self[r_idx - 1] in chars: r_idx -= 1 @@ -1935,8 +1942,12 @@ struct String( Returns: A copy of the string with no trailing whitespaces. """ - # TODO: should use self.__iter__ and self.isspace() - var r_idx = len(self) + var r_idx = self.byte_length() + # TODO (#933): should use this once llvm intrinsics can be used at comp time + # for s in self.__reversed__(): + # if not s.isspace(): + # break + # r_idx -= 1 while r_idx > 0 and _isspace(self._buffer.unsafe_get(r_idx - 1)): r_idx -= 1 return self[:r_idx] @@ -1952,7 +1963,7 @@ struct String( """ var l_idx = 0 - while l_idx < len(self) and self[l_idx] in chars: + while l_idx < self.byte_length() and self[l_idx] in chars: l_idx += 1 return self[l_idx:] @@ -1963,9 +1974,15 @@ struct String( Returns: A copy of the string with no leading whitespaces. """ - # TODO: should use self.__iter__ and self.isspace() var l_idx = 0 - while l_idx < len(self) and _isspace(self._buffer.unsafe_get(l_idx)): + # TODO (#933): should use this once llvm intrinsics can be used at comp time + # for s in self: + # if not s.isspace(): + # break + # l_idx += 1 + while l_idx < self.byte_length() and _isspace( + self._buffer.unsafe_get(l_idx) + ): l_idx += 1 return self[l_idx:] @@ -1983,9 +2000,9 @@ struct String( var res = List[UInt8]() var val_ptr = val.unsafe_ptr() var self_ptr = self.unsafe_ptr() - res.reserve(len(val) * len(self) + 1) - for i in range(len(self)): - for j in range(len(val)): + res.reserve(val.byte_length() * self.byte_length() + 1) + for i in range(self.byte_length()): + for j in range(val.byte_length()): res.append(val_ptr[j]) res.append(self_ptr[i]) res.append(0) @@ -2022,7 +2039,7 @@ struct String( var char_ptr = copy.unsafe_ptr() - for i in range(len(self)): + for i in range(self.byte_length()): var char: UInt8 = char_ptr[i] if check_case(char): var lower = _toggle_ascii_case(char) @@ -2044,7 +2061,7 @@ struct String( """ if end == -1: return StringRef( - self.unsafe_ptr() + start, len(self) - start + self.unsafe_ptr() + start, self.byte_length() - start ).startswith(prefix._strref_dangerous()) return StringRef(self.unsafe_ptr() + start, end - start).startswith( @@ -2065,7 +2082,7 @@ struct String( """ if end == -1: return StringRef( - self.unsafe_ptr() + start, len(self) - start + self.unsafe_ptr() + start, self.byte_length() - start ).endswith(suffix._strref_dangerous()) return StringRef(self.unsafe_ptr() + start, end - start).endswith( @@ -2092,7 +2109,7 @@ struct String( or a copy of the original string otherwise. """ if self.startswith(prefix): - return self[len(prefix) :] + return self[prefix.byte_length() :] return self fn removesuffix(self, suffix: String, /) -> String: @@ -2115,7 +2132,7 @@ struct String( or a copy of the original string otherwise. """ if suffix and self.endswith(suffix): - return self[: -len(suffix)] + return self[: -suffix.byte_length()] return self fn __int__(self) raises -> Int: @@ -2141,7 +2158,7 @@ struct String( """ if n <= 0: return "" - var len_self = len(self) + var len_self = self.byte_length() var count = len_self * n + 1 var buf = Self._buffer_type(capacity=count) buf.resize(count, 0) @@ -2194,7 +2211,10 @@ struct String( var current_automatic_arg_index = 0 for e in entries: - debug_assert(pos_in_self < len(self), "pos_in_self >= len(self)") + debug_assert( + pos_in_self < self.byte_length(), + "pos_in_self >= self.byte_length()", + ) res += self[pos_in_self : e[].first_curly] if e[].is_escaped_brace(): @@ -2217,8 +2237,8 @@ struct String( pos_in_self = e[].last_curly + 1 - if pos_in_self < len(self): - res += self[pos_in_self : len(self)] + if pos_in_self < self.byte_length(): + res += self[pos_in_self : self.byte_length()] return res^ @@ -2240,7 +2260,7 @@ struct String( return _is_ascii_uppercase(c) or _is_ascii_lowercase(c) for c in self: - debug_assert(c._byte_length() == 1, "only implemented for ASCII") + debug_assert(c.byte_length() == 1, "only implemented for ASCII") if is_ascii_cased(ord(c)): @parameter @@ -2493,7 +2513,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): var entries = List[Self]() var start = Optional[Int](None) var skip_next = False - for i in range(len(format_src)): + for i in range(format_src.byte_length()): if skip_next: skip_next = False continue @@ -2550,7 +2570,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): start = None else: # python escapes double curlies - if (i + 1) < len(format_src): + if (i + 1) < format_src.byte_length(): if format_src[i + 1] == "}": var curren_entry = Self( first_curly=i, last_curly=i + 1, field=True diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 8858a31d5f..3931b0a08a 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -191,7 +191,7 @@ struct StringLiteral( # TODO(MSTDL-160): # Properly count Unicode codepoints instead of returning this length # in bytes. - return self._byte_length() + return self.byte_length() @always_inline("nodebug") fn __bool__(self) -> Bool: @@ -222,7 +222,7 @@ struct StringLiteral( A new string. """ var string = String() - var length = self._byte_length() + var length = self.byte_length() var buffer = String._buffer_type() var new_capacity = length + 1 buffer._realloc(new_capacity) @@ -267,11 +267,27 @@ struct StringLiteral( # ===-------------------------------------------------------------------===# @always_inline + fn byte_length(self) -> Int: + """Get the string length in bytes. + + Returns: + The length of this StringLiteral in bytes. + + Notes: + This does not include the trailing null terminator in the count. + """ + return __mlir_op.`pop.string.size`(self.value) + + @always_inline + @deprecated("use byte_length() instead") fn _byte_length(self) -> Int: """Get the string length in bytes. Returns: The length of this StringLiteral in bytes. + + Notes: + This does not include the trailing null terminator in the count. """ return __mlir_op.`pop.string.size`(self.value) @@ -338,7 +354,7 @@ struct StringLiteral( return Span[UInt8, ImmutableStaticLifetime]( unsafe_ptr=ptr, - len=self._byte_length(), + len=self.byte_length(), ) fn format_to(self, inout writer: Formatter): diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index b41e5e6f09..22e3a259be 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -162,7 +162,7 @@ struct Path( Returns: True if the path length is greater than zero, and False otherwise. """ - return len(self.path) > 0 + return self.path.byte_length() > 0 fn format_to(self, inout writer: Formatter): """ diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 3c65863f0d..fd2d3b8d50 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -231,7 +231,7 @@ fn _get_global[ fn _get_global_or_null[name: StringLiteral]() -> UnsafePointer[NoneType]: return external_call[ "KGEN_CompilerRT_GetGlobalOrNull", UnsafePointer[NoneType] - ](name.unsafe_ptr(), name._byte_length()) + ](name.unsafe_ptr(), name.byte_length()) @always_inline diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 9864ba1b5f..bd3c9d69f2 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -31,7 +31,9 @@ fn _get_random_name(size: Int = 8) -> String: alias characters = String("abcdefghijklmnopqrstuvwxyz0123456789_") var name_list = List[UInt8](capacity=size + 1) for _ in range(size): - var rand_index = int(random.random_ui64(0, len(characters) - 1)) + var rand_index = int( + random.random_ui64(0, characters.byte_length() - 1) + ) name_list.append(ord(characters[rand_index])) name_list.append(0) return String(name_list^) diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 0cf47eeb27..838d2679f0 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -123,7 +123,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): Args: str_slice: The string to append. """ - var total_len = len(self) + str_slice._byte_length() + var total_len = len(self) + str_slice.byte_length() # NOTE: Not guaranteed that we're in the small layout even if our # length is shorter than the small capacity. @@ -157,7 +157,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): memcpy( dest=buffer.unsafe_ptr() + len(self), src=str_slice.unsafe_ptr(), - count=str_slice._byte_length(), + count=str_slice.byte_length(), ) # Record that we've initialized `total_len` count of elements @@ -442,14 +442,14 @@ struct _FixedString[CAP: Int]( inout self, str_slice: StringSlice[_], ) -> Optional[Error]: - var total_len = len(self) + str_slice._byte_length() + var total_len = len(self) + str_slice.byte_length() # Ensure there is sufficient capacity to append `str_slice` if total_len > CAP: return Optional( Error( "Insufficient capacity to append len=" - + str(str_slice._byte_length()) + + str(str_slice.byte_length()) + " string to len=" + str(len(self)) + " FixedString with capacity=" @@ -461,7 +461,7 @@ struct _FixedString[CAP: Int]( memcpy( dest=self.buffer.unsafe_ptr() + len(self), src=str_slice.unsafe_ptr(), - count=str_slice._byte_length(), + count=str_slice.byte_length(), ) self.size = total_len diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 576f541743..87f1f3bebb 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -21,6 +21,7 @@ from utils import StringSlice """ from utils import Span +from builtin.string import _isspace, _utf8_byte_type alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" @@ -68,8 +69,7 @@ struct StringSlice[ # FIXME(MSTDL-160): # Ensure StringLiteral _actually_ always uses UTF-8 encoding. self = StringSlice[lifetime]( - unsafe_from_utf8_ptr=literal.unsafe_ptr(), - len=literal._byte_length(), + unsafe_from_utf8_ptr=literal.unsafe_ptr(), len=literal.byte_length() ) @always_inline @@ -156,9 +156,13 @@ struct StringSlice[ Returns: The length in Unicode codepoints. """ - # FIXME(MSTDL-160): - # Actually perform UTF-8 decoding here to count the codepoints. - return len(self._slice) + var unicode_length = self.byte_length() + + for i in range(unicode_length): + if _utf8_byte_type(self._slice[i]) == 1: + unicode_length -= 1 + + return unicode_length fn format_to(self, inout writer: Formatter): """ @@ -264,8 +268,7 @@ struct StringSlice[ @always_inline fn as_bytes_slice(self) -> Span[UInt8, lifetime]: - """ - Get the sequence of encoded bytes as a slice of the underlying string. + """Get the sequence of encoded bytes as a slice of the underlying string. Returns: A slice containing the underlying sequence of encoded bytes. @@ -274,8 +277,7 @@ struct StringSlice[ @always_inline fn unsafe_ptr(self) -> UnsafePointer[UInt8]: - """ - Gets a pointer to the first element of this string slice. + """Gets a pointer to the first element of this string slice. Returns: A pointer pointing at the first element of this string slice. @@ -284,9 +286,19 @@ struct StringSlice[ return self._slice.unsafe_ptr() @always_inline - fn _byte_length(self) -> Int: + fn byte_length(self) -> Int: + """Get the length of this string slice in bytes. + + Returns: + The length of this string slice in bytes. """ - Get the length of this string slice in bytes. + + return len(self.as_bytes_slice()) + + @always_inline + @deprecated("use byte_length() instead") + fn _byte_length(self) -> Int: + """Get the length of this string slice in bytes. Returns: The length of this string slice in bytes. @@ -295,8 +307,7 @@ struct StringSlice[ return len(self.as_bytes_slice()) fn _strref_dangerous(self) -> StringRef: - """ - Returns an inner pointer to the string as a StringRef. + """Returns an inner pointer to the string as a StringRef. Safety: This functionality is extremely dangerous because Mojo eagerly @@ -304,11 +315,10 @@ struct StringSlice[ _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef(self.unsafe_ptr(), self._byte_length()) + return StringRef(self.unsafe_ptr(), self.byte_length()) fn _strref_keepalive(self): - """ - A no-op that keeps `self` alive through the call. This + """A no-op that keeps `self` alive through the call. This can be carefully used with `_strref_dangerous()` to wield inner pointers without the string getting deallocated early. """ diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index f16f97eec4..4e9fcd1b36 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -661,8 +661,31 @@ def test_split(): assert_true(d[0] == "hello \t" and d[1] == "" and d[2] == "\v\fworld") # Should add all whitespace-like chars as one - alias utf8_spaces = String(" \t\n\r\v\f") - var s = utf8_spaces + "hello" + utf8_spaces + "world" + utf8_spaces + # test all unicode separators + # 0 is to build a String with null terminator + alias next_line = List[UInt8](0xC2, 0x85, 0) + """TODO: \\x85""" + alias unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8, 0) + """TODO: \\u2028""" + alias unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9, 0) + """TODO: \\u2029""" + # TODO add line and paragraph separator as stringliteral once unicode + # escape secuences are accepted + var univ_sep_var = ( + String(" ") + + String("\t") + + String("\n") + + String("\r") + + String("\v") + + String("\f") + + String("\x1c") + + String("\x1d") + + String("\x1e") + + String(next_line) + + String(unicode_line_sep) + + String(unicode_paragraph_sep) + ) + var s = univ_sep_var + "hello" + univ_sep_var + "world" + univ_sep_var d = s.split() assert_true(len(d) == 2) assert_true(d[0] == "hello" and d[1] == "world") @@ -1251,7 +1274,7 @@ def test_string_iter(): var utf8_sequence_len = 0 var byte_idx = 0 for v in item: - var byte_len = len(v) + var byte_len = v.byte_length() assert_equal(item[byte_idx : byte_idx + byte_len], v) byte_idx += byte_len utf8_sequence_len += 1 From 7bf889f3d0ebe524dec87807d12e02830c3c9f0f Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 9 Jul 2024 23:52:35 -0400 Subject: [PATCH 1181/2019] [stdlib] Add `_quicksort_partition_left` This improves the performance of `_quicksort` on lists with small delta. This potentially eliminates $\mathcal{O}(N^2)$ runtime caused by trivial case where the entire list contains the same element. This is motivated by Orson Peters' implementation of [pdqsort](https://github.com/orlp/pdqsort/blob/master/pdqsort.h) The benchmarks were run on Apple M3. Original benchmark: #42866 ``` bench_std_sort_low_card_list_65536_delta_0",0.073340251572327042,1590 "bench_std_sort_low_card_list_65536_delta_0",0.073129562043795615,1644 "bench_std_sort_low_card_list_65536_delta_0",0.072495390288875236,1627 "bench_heap_sort_low_card_list_65536_delta_0",0.112761,1000 "bench_heap_sort_low_card_list_65536_delta_0",0.112274,1000 "bench_heap_sort_low_card_list_65536_delta_0",0.11236,1000 "bench_std_sort_low_card_list_65536_delta_20",0.68095402298850582,174 "bench_std_sort_low_card_list_65536_delta_20",0.68333142857142859,175 "bench_std_sort_low_card_list_65536_delta_20",0.68587951807228908,166 "bench_heap_sort_low_card_list_65536_delta_20",1.9963168316831681,101 "bench_heap_sort_low_card_list_65536_delta_20",1.9639801980198019,101 "bench_heap_sort_low_card_list_65536_delta_20",1.9727425742574258,101 "bench_std_sort_low_card_list_65536_delta_100",1.0541061946902655,113 "bench_std_sort_low_card_list_65536_delta_100",1.0466754385964911,114 "bench_std_sort_low_card_list_65536_delta_100",1.0174957264957265,117 "bench_heap_sort_low_card_list_65536_delta_100",1.9494950495049506,101 "bench_heap_sort_low_card_list_65536_delta_100",1.944059405940594,101 "bench_heap_sort_low_card_list_65536_delta_100",1.9365841584158416,101 ``` MODULAR_ORIG_COMMIT_REV_ID: b43a832b7383945dac0e66cc98747e8a00db0ed5 --- stdlib/benchmarks/builtin/bench_sort.mojo | 2 +- stdlib/src/builtin/sort.mojo | 40 +++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index e07369c3ec..fc282dadf1 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -289,7 +289,7 @@ fn bench_low_cardinality_list_sort(inout m: Bench) raises: @parameter for delta_index in range(len(deltas)): - var delta = deltas[delta_index] + alias delta = deltas[delta_index] @parameter for count_index in range(len(counts)): diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 02b7596e12..78bd88f18a 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -70,7 +70,7 @@ fn _insertion_sort[ # put everything thats "<" to the left of pivot @always_inline -fn _quicksort_partition[ +fn _quicksort_partition_right[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type ](array: Pointer[type], start: Int, end: Int) -> Int: var left = start + 1 @@ -92,6 +92,29 @@ fn _quicksort_partition[ right -= 1 +# put everything thats "<=" to the left of pivot +@always_inline +fn _quicksort_partition_left[ + type: AnyTrivialRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], start: Int, end: Int) -> Int: + var left = start + 1 + var right = end - 1 + var pivot_value = array[start] + + while True: + while left < right and not cmp_fn(pivot_value, array[left]): + left += 1 + while cmp_fn(pivot_value, array[right]): + right -= 1 + if left >= right: + var pivot_pos = left - 1 + swap(array[pivot_pos], array[start]) + return pivot_pos + swap(array[left], array[right]) + left += 1 + right -= 1 + + @always_inline fn _partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type @@ -237,7 +260,20 @@ fn _quicksort[ # pick median of 3 as pivot _sort3[type, cmp_fn](array, (start + end) >> 1, start, end - 1) - var pivot = _quicksort_partition[type, cmp_fn](array, start, end) + + # if array[start - 1] == pivot_value, then everything in between will + # be the same, so no need to recurse that interval + # already have array[start - 1] <= array[start] + if start > 0 and not cmp_fn(array[start - 1], array[start]): + var pivot = _quicksort_partition_left[type, cmp_fn]( + array, start, end + ) + if end > pivot + 2: + stack.append(pivot + 1) + stack.append(end) + continue + + var pivot = _quicksort_partition_right[type, cmp_fn](array, start, end) if end > pivot + 2: stack.append(pivot + 1) From fcd51acf75fb2a93174e9d5878f59202ffacfef7 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 9 Jul 2024 21:23:00 -0700 Subject: [PATCH 1182/2019] [mojo-stdlib] Move `Slice` off `OptionalReg`. This reduces a use of the `OptionalReg` type, which we'd like to eventually remove. MODULAR_ORIG_COMMIT_REV_ID: abbd34e1c50b5c64121724ccd18e4fffeaa0a067 --- stdlib/src/builtin/builtin_slice.mojo | 42 +++++++-------------------- stdlib/test/builtin/test_slice.mojo | 2 +- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 398c06e55b..3408caf30d 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -16,18 +16,9 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import OptionalReg -from sys.intrinsics import _mlirtype_is_eq -# TODO: When Slice switches to Optional, just use == instead of this function. -@always_inline("nodebug") -fn _compare_optional(x: OptionalReg[Int], y: OptionalReg[Int]) -> Bool: - if x and y: - return x.value() == y.value() - return not x and not y - - -@register_passable("trivial") +@value struct Slice( Stringable, EqualityComparable, @@ -49,9 +40,9 @@ struct Slice( ``` """ - var start: OptionalReg[Int] + var start: Optional[Int] """The starting index of the slice.""" - var end: OptionalReg[Int] + var end: Optional[Int] """The end index of the slice.""" var step: Int """The step increment value of the slice.""" @@ -71,9 +62,9 @@ struct Slice( @always_inline("nodebug") fn __init__( inout self, - start: OptionalReg[Int], - end: OptionalReg[Int], - step: OptionalReg[Int], + start: Optional[Int], + end: Optional[Int], + step: Optional[Int], ): """Construct slice given the start, end and step values. @@ -124,7 +115,7 @@ struct Slice( """ @parameter - fn write_optional(opt: OptionalReg[Int]): + fn write_optional(opt: Optional[Int]): if opt: writer.write(repr(opt.value())) else: @@ -149,10 +140,9 @@ struct Slice( True if start, end, and step values of this slice match the corresponding values of the other slice and False otherwise. """ - # TODO: When Slice switches to Optional, just use ==. return ( - _compare_optional(self.start, other.start) - and _compare_optional(self.end, other.end) + self.start == other.start + and self.end == other.end and self.step == other.step ) @@ -169,18 +159,6 @@ struct Slice( """ return not (self == other) - @always_inline - fn unsafe_indices(self) -> Int: - """Return the length of the slice. - - Only use this function if start/end is guaranteed to be not None. - - Returns: - The length of the slice. - """ - - return len(range(self.start.value(), self.end.value(), self.step)) - fn indices(self, length: Int) -> (Int, Int, Int): """Returns a tuple of 3 integers representing the start, end, and step of the slice if applied to a container of the given length. @@ -269,7 +247,7 @@ fn slice(start: Int, end: Int) -> Slice: @always_inline("nodebug") fn slice( - start: OptionalReg[Int], end: OptionalReg[Int], step: OptionalReg[Int] + start: Optional[Int], end: Optional[Int], step: Optional[Int] ) -> Slice: """Construct a Slice given the start, end and step values. diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 7d467f7cbf..7736dae236 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -16,7 +16,7 @@ from testing import assert_equal, assert_false, assert_true def test_none_end_folds(): - alias all_def_slice = slice(0, None, 1) + var all_def_slice = slice(0, None, 1) assert_equal(all_def_slice.start.value(), 0) assert_true(all_def_slice.end is None) assert_equal(all_def_slice.step, 1) From e2d18e07891e25354f9e2cd1fbf23b2bd19898de Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 23:24:31 -0500 Subject: [PATCH 1183/2019] [stdlib] Rename `countl_zero` to `count_leading_zeros` For clarity, rename `countl_zero` to `count_leading_zeros`. Ditto for `countr_zero` to `count_trailing_zeros`. MODULAR_ORIG_COMMIT_REV_ID: 2f1585b813a8d62c868d5da3449ca6c93d90338e --- docs/changelog.md | 4 + stdlib/benchmarks/utils/bench_formatter.mojo | 2 +- stdlib/benchmarks/utils/bench_memmem.mojo | 4 +- stdlib/src/bit/__init__.mojo | 4 +- stdlib/src/bit/bit.mojo | 22 ++--- stdlib/src/builtin/_math.mojo | 2 +- stdlib/src/builtin/sort.mojo | 6 +- stdlib/src/builtin/string.mojo | 10 ++- stdlib/src/math/math.mojo | 6 +- stdlib/src/utils/stringref.mojo | 6 +- stdlib/test/bit/test_bit.mojo | 94 ++++++++++++-------- 11 files changed, 92 insertions(+), 68 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bf8ca3c37e..d2df36d413 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -425,6 +425,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `memcmp`, `memset` and `memset_zero` no longer take in `LegacyPointer`, instead, use `UnsafePointer`. +- A few bit functions have been renamed for clarity: +- `countl_zero` -> `count_leading_zeros` +- `countr_zero` -> `count_trailing_zeros` + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index b4b97babb9..e41abf84f5 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -14,7 +14,7 @@ # RUN: %mojo %s -t from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run -from bit import countr_zero +from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from utils.stringref import _align_down, _memchr, _memmem diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index ca700490dc..89be28bb5e 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -14,7 +14,7 @@ # RUN: %mojo %s -t from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run -from bit import countr_zero +from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from utils.stringref import _align_down, _memchr, _memmem @@ -169,7 +169,7 @@ fn _memmem_baseline[ ) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: - var offset = i + countr_zero(mask) + var offset = i + count_trailing_zeros(mask) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index 9421224e6c..e41c3ca52c 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -19,8 +19,8 @@ from .bit import ( bit_reverse, bit_width, byte_swap, - countl_zero, - countr_zero, + count_leading_zeros, + count_trailing_zeros, is_power_of_two, pop_count, rotate_bits_left, diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 5d5e5a1591..cc0c15f281 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `bit` package. For example: ```mojo -from bit import countl_zero +from bit import count_leading_zeros ``` """ @@ -23,12 +23,12 @@ from sys import llvm_intrinsic from sys.info import bitwidthof # ===----------------------------------------------------------------------===# -# countl_zero +# count_leading_zeros # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn countl_zero(val: Int) -> Int: +fn count_leading_zeros(val: Int) -> Int: """Counts the number of leading zeros of an integer. Args: @@ -41,7 +41,7 @@ fn countl_zero(val: Int) -> Int: @always_inline("nodebug") -fn countl_zero[ +fn count_leading_zeros[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Counts the per-element number of leading zeros in a SIMD vector. @@ -67,12 +67,12 @@ fn countl_zero[ # ===----------------------------------------------------------------------===# -# countr_zero +# count_trailing_zeros # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn countr_zero(val: Int) -> Int: +fn count_trailing_zeros(val: Int) -> Int: """Counts the number of trailing zeros for an integer. Args: @@ -85,7 +85,7 @@ fn countr_zero(val: Int) -> Int: @always_inline("nodebug") -fn countr_zero[ +fn count_trailing_zeros[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Counts the per-element number of trailing zeros in a SIMD vector. @@ -267,7 +267,7 @@ fn bit_width(val: Int) -> Int: """ alias bitwidth = bitwidthof[Int]() - return bitwidth - countl_zero(~val if val < 0 else val) + return bitwidth - count_leading_zeros(~val if val < 0 else val) @always_inline @@ -299,10 +299,10 @@ fn bit_width[ @parameter if type.is_unsigned(): - return bitwidth - countl_zero(val) + return bitwidth - count_leading_zeros(val) else: - var leading_zero_pos = countl_zero(val) - var leading_zero_neg = countl_zero(bit_not(val)) + var leading_zero_pos = count_leading_zeros(val) + var leading_zero_neg = count_leading_zeros(bit_not(val)) var leading_zero = (val < 0).select(leading_zero_neg, leading_zero_pos) return bitwidth - leading_zero diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 21edefd587..dccb8eee64 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -17,7 +17,7 @@ module should be exposed by the current `math` module. The contents of this module should be eventually moved to the `math` module when it's open sourced. """ -from bit import countr_zero +from bit import count_trailing_zeros # ===----------------------------------------------------------------------=== # # Ceilable diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 78bd88f18a..2d25c1eb63 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import List from sys import bitwidthof -from bit import countl_zero +from bit import count_leading_zeros from memory import Pointer, UnsafePointer # ===----------------------------------------------------------------------===# @@ -209,7 +209,9 @@ fn _heap_sort[ @always_inline fn _estimate_initial_height(size: Int) -> Int: # Compute the log2 of the size rounded upward. - var log2 = int((bitwidthof[DType.index]() - 1) ^ countl_zero(size | 1)) + var log2 = int( + (bitwidthof[DType.index]() - 1) ^ count_leading_zeros(size | 1) + ) return max(2, log2) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 808a7d9c6f..350f948043 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -20,7 +20,7 @@ from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic from sys.ffi import C_char -from bit import countl_zero +from bit import count_leading_zeros from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy from utils import Span, StaticIntTuple, StringRef, StringSlice @@ -70,7 +70,7 @@ fn ord(s: StringSlice) -> Int: if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(s.byte_length() == 1, "input string length must be 1") return int(b1) - var num_bytes = countl_zero(~b1) + var num_bytes = count_leading_zeros(~b1) debug_assert( s.byte_length() == int(num_bytes), "input string must be one character" ) @@ -716,7 +716,7 @@ fn _utf8_byte_type(b: UInt8) -> UInt8: 3 -> start of 3 byte long sequence. 4 -> start of 4 byte long sequence. """ - return countl_zero(~(b & 0b1111_0000)) + return count_leading_zeros(~(b & 0b1111_0000)) @value @@ -2362,7 +2362,9 @@ fn _calc_initial_buffer_size_int32(n0: Int) -> Int: 42949672960, ) var n = UInt32(n0) - var log2 = int((bitwidthof[DType.uint32]() - 1) ^ countl_zero(n | 1)) + var log2 = int( + (bitwidthof[DType.uint32]() - 1) ^ count_leading_zeros(n | 1) + ) return (n0 + lookup_table[int(log2)]) >> 32 diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index c909f31e16..3324ef5d6a 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1900,8 +1900,8 @@ fn gcd(m: Int, n: Int, /) -> Int: return max(m, n) if m > 0 and n > 0: - var trailing_zeros_a = countr_zero(m) - var trailing_zeros_b = countr_zero(n) + var trailing_zeros_a = count_trailing_zeros(m) + var trailing_zeros_b = count_trailing_zeros(n) var u = m >> trailing_zeros_a var v = n >> trailing_zeros_b @@ -1916,7 +1916,7 @@ fn gcd(m: Int, n: Int, /) -> Int: v -= u if u == 0: break - v >>= countr_zero(v) + v >>= count_trailing_zeros(v) return u << trailing_zeros_common var u = m diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 945efa2065..dfa26408cc 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -13,7 +13,7 @@ """Implements the StringRef class. """ -from bit import countr_zero +from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from builtin.string import _atol, _isspace from memory import DTypePointer, UnsafePointer, memcmp @@ -682,7 +682,7 @@ fn _memchr[ ) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) if mask: - return source + i + countr_zero(mask) + return source + i + count_trailing_zeros(mask) for i in range(vectorized_end, len): if source[i] == char: @@ -727,7 +727,7 @@ fn _memmem[ var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: - var offset = i + countr_zero(mask) + var offset = i + count_trailing_zeros(mask) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 95f8bb9f63..4863fcb534 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -19,8 +19,8 @@ from bit import ( bit_reverse, bit_width, byte_swap, - countl_zero, - countr_zero, + count_leading_zeros, + count_trailing_zeros, is_power_of_two, pop_count, rotate_bits_left, @@ -29,21 +29,21 @@ from bit import ( from testing import assert_equal -def test_countl_zero(): - assert_equal(countl_zero(-(2**59)), 0) - assert_equal(countl_zero(-(2**20)), 0) - assert_equal(countl_zero(-1), 0) - assert_equal(countl_zero(-1), 0) - assert_equal(countl_zero(0), 64) - assert_equal(countl_zero(1), 63) - assert_equal(countl_zero(2), 62) - assert_equal(countl_zero(3), 62) - assert_equal(countl_zero(4), 61) - assert_equal(countl_zero(2**20), 43) - assert_equal(countl_zero(2**59), 4) +def test_count_leading_zeros(): + assert_equal(count_leading_zeros(-(2**59)), 0) + assert_equal(count_leading_zeros(-(2**20)), 0) + assert_equal(count_leading_zeros(-1), 0) + assert_equal(count_leading_zeros(-1), 0) + assert_equal(count_leading_zeros(0), 64) + assert_equal(count_leading_zeros(1), 63) + assert_equal(count_leading_zeros(2), 62) + assert_equal(count_leading_zeros(3), 62) + assert_equal(count_leading_zeros(4), 61) + assert_equal(count_leading_zeros(2**20), 43) + assert_equal(count_leading_zeros(2**59), 4) -def test_countl_zero_simd(): +def test_count_leading_zeros_simd(): alias simd_width = 4 alias int8_t = DType.int8 alias int16_t = DType.int16 @@ -51,36 +51,44 @@ def test_countl_zero_simd(): alias int64_t = DType.int64 alias var1 = SIMD[int8_t, simd_width](-(2**6), 0, -1, 2**6) - assert_equal(countl_zero(var1), SIMD[int8_t, simd_width](0, 8, 0, 1)) + assert_equal( + count_leading_zeros(var1), SIMD[int8_t, simd_width](0, 8, 0, 1) + ) alias var3 = SIMD[int16_t, simd_width](-(2**14), 0, -1, 2**14) - assert_equal(countl_zero(var3), SIMD[int16_t, simd_width](0, 16, 0, 1)) + assert_equal( + count_leading_zeros(var3), SIMD[int16_t, simd_width](0, 16, 0, 1) + ) alias var5 = SIMD[int32_t, simd_width](-(2**30), 0, -1, 2**30) - assert_equal(countl_zero(var5), SIMD[int32_t, simd_width](0, 32, 0, 1)) + assert_equal( + count_leading_zeros(var5), SIMD[int32_t, simd_width](0, 32, 0, 1) + ) # TODO: use this line after #2882 is fixed # alias var7 = SIMD[int64_t, simd_width](-(2**62), 0, -1, 2**62) alias var7 = SIMD[int64_t, simd_width]( -4611686018427387904, 0, -1, 4611686018427387904 ) - assert_equal(countl_zero(var7), SIMD[int64_t, simd_width](0, 64, 0, 1)) + assert_equal( + count_leading_zeros(var7), SIMD[int64_t, simd_width](0, 64, 0, 1) + ) -def test_countr_zero(): - assert_equal(countr_zero(-(2**59)), 59) - assert_equal(countr_zero(-(2**20)), 20) - assert_equal(countr_zero(-1), 0) - assert_equal(countr_zero(0), 64) - assert_equal(countr_zero(1), 0) - assert_equal(countr_zero(2), 1) - assert_equal(countr_zero(3), 0) - assert_equal(countr_zero(4), 2) - assert_equal(countr_zero(2**20), 20) - assert_equal(countr_zero(2**59), 59) +def test_count_trailing_zeros(): + assert_equal(count_trailing_zeros(-(2**59)), 59) + assert_equal(count_trailing_zeros(-(2**20)), 20) + assert_equal(count_trailing_zeros(-1), 0) + assert_equal(count_trailing_zeros(0), 64) + assert_equal(count_trailing_zeros(1), 0) + assert_equal(count_trailing_zeros(2), 1) + assert_equal(count_trailing_zeros(3), 0) + assert_equal(count_trailing_zeros(4), 2) + assert_equal(count_trailing_zeros(2**20), 20) + assert_equal(count_trailing_zeros(2**59), 59) -def test_countr_zero_simd(): +def test_count_trailing_zeros_simd(): alias simd_width = 4 alias int8_t = DType.int8 alias int16_t = DType.int16 @@ -88,20 +96,28 @@ def test_countr_zero_simd(): alias int64_t = DType.int64 alias var1 = SIMD[int8_t, simd_width](-(2**6), 0, -1, 2**6) - assert_equal(countr_zero(var1), SIMD[int8_t, simd_width](6, 8, 0, 6)) + assert_equal( + count_trailing_zeros(var1), SIMD[int8_t, simd_width](6, 8, 0, 6) + ) alias var3 = SIMD[int16_t, simd_width](-(2**14), 0, -1, 2**14) - assert_equal(countr_zero(var3), SIMD[int16_t, simd_width](14, 16, 0, 14)) + assert_equal( + count_trailing_zeros(var3), SIMD[int16_t, simd_width](14, 16, 0, 14) + ) alias var5 = SIMD[int32_t, simd_width](-(2**30), 0, -1, 2**30) - assert_equal(countr_zero(var5), SIMD[int32_t, simd_width](30, 32, 0, 30)) + assert_equal( + count_trailing_zeros(var5), SIMD[int32_t, simd_width](30, 32, 0, 30) + ) # TODO: use this line after #2882 is fixed # alias var7 = SIMD[int64_t, simd_width](-(2**62), 0, -1, 2**62) alias var7 = SIMD[int64_t, simd_width]( -4611686018427387904, 0, -1, 4611686018427387904 ) - assert_equal(countr_zero(var7), SIMD[int64_t, simd_width](62, 64, 0, 62)) + assert_equal( + count_trailing_zeros(var7), SIMD[int64_t, simd_width](62, 64, 0, 62) + ) def test_bit_reverse_simd(): @@ -453,10 +469,10 @@ def main(): test_bit_width_simd() test_is_power_of_two() test_is_power_of_two_simd() - test_countl_zero() - test_countl_zero_simd() - test_countr_zero() - test_countr_zero_simd() + test_count_leading_zeros() + test_count_leading_zeros_simd() + test_count_trailing_zeros() + test_count_trailing_zeros_simd() test_bit_reverse_simd() test_byte_swap_simd() test_pop_count_simd() From fbe4f6e572cbf41a71f1e3c890766095cc992f18 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Jul 2024 23:25:34 -0500 Subject: [PATCH 1184/2019] [stdlib] Make `_SourceLocation` `Formattable` Implement `format_to` on `_SourceLocation` to improve the use of `debug_assert` on GPUs in avoiding allocations. MODULAR_ORIG_COMMIT_REV_ID: 164ab97cd5786318cea137486d7d4dd867e12567 --- stdlib/src/builtin/_location.mojo | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 3bdd1bf767..733a24817e 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -16,7 +16,7 @@ @value @register_passable("trivial") -struct _SourceLocation(Stringable): +struct _SourceLocation(Formattable, Stringable): """Type to carry file name, line, and column information.""" var line: Int @@ -25,7 +25,7 @@ struct _SourceLocation(Stringable): @no_inline fn __str__(self) -> String: - return str(self.file_name) + ":" + str(self.line) + ":" + str(self.col) + return String.format_sequence(self) @no_inline fn prefix[T: Stringable](self, msg: T) -> String: @@ -39,6 +39,15 @@ struct _SourceLocation(Stringable): """ return "At " + str(self) + ": " + str(msg) + fn format_to(self, inout writer: Formatter): + """ + Formats the source location to the provided formatter. + + Args: + writer: The formatter to write to. + """ + writer.write(self.file_name, ":", self.line, ":", self.col) + @always_inline("nodebug") fn __source_location() -> _SourceLocation: From c12733daca0a427df3f8bd85ebd1cce02343ad7a Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 10 Jul 2024 00:30:13 -0400 Subject: [PATCH 1185/2019] [stdlib] Drop redundant `@parameter` in `bench_sort.mojo` Remove some of the `@parameter for` if the iteration variable is not used in parameter. MODULAR_ORIG_COMMIT_REV_ID: 2a3037556864509cee819959b011973621f381fd --- stdlib/benchmarks/builtin/bench_sort.mojo | 47 +++++++++-------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index fc282dadf1..fdde4d8e8c 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -158,16 +158,14 @@ fn bench_tiny_list_sort(inout m: Bench) raises: @parameter fn bench_small_list_sort(inout m: Bench) raises: - alias counts = List(10, 20, 32, 64, 100) + var counts = List(10, 20, 32, 64, 100) @parameter for type_index in range(len(dtypes)): alias dt = dtypes[type_index] - @parameter - for count_index in range(len(counts)): - alias count = counts[count_index] - var list = random_scalar_list[dt](count) + for count in counts: + var list = random_scalar_list[dt](count[]) @parameter fn bench_sort_list(inout b: Bencher) raises: @@ -192,7 +190,7 @@ fn bench_small_list_sort(inout m: Bench) raises: m.bench_function[bench_sort_list]( BenchId( "bench_std_sort_random_list_" - + str(count) + + str(count[]) + "_type_" + str(dt) ) @@ -200,7 +198,7 @@ fn bench_small_list_sort(inout m: Bench) raises: m.bench_function[bench_insertion_sort]( BenchId( "bench_ins_sort_random_list_" - + str(count) + + str(count[]) + "_type_" + str(dt) ) @@ -226,16 +224,14 @@ fn heap_sort[type: DType](list: List[Scalar[type]]): @parameter fn bench_large_list_sort(inout m: Bench) raises: - alias counts = List(1 << 12, 1 << 16) + var counts = List(1 << 12, 1 << 16) @parameter for type_index in range(len(dtypes)): alias dt = dtypes[type_index] - @parameter - for count_index in range(len(counts)): - alias count = counts[count_index] - var list = random_scalar_list[dt](count) + for count in counts: + var list = random_scalar_list[dt](count[]) @parameter fn bench_sort_list(inout b: Bencher) raises: @@ -260,7 +256,7 @@ fn bench_large_list_sort(inout m: Bench) raises: m.bench_function[bench_sort_list]( BenchId( "bench_std_sort_random_list_" - + str(count) + + str(count[]) + "_type_" + str(dt) ) @@ -269,7 +265,7 @@ fn bench_large_list_sort(inout m: Bench) raises: m.bench_function[bench_heap_sort]( BenchId( "bench_heap_sort_random_list_" - + str(count) + + str(count[]) + "_type_" + str(dt) ) @@ -284,17 +280,12 @@ fn bench_large_list_sort(inout m: Bench) raises: @parameter fn bench_low_cardinality_list_sort(inout m: Bench) raises: - alias counts = List(1 << 12, 1 << 16) - alias deltas = List(0, 2, 5, 20, 100) - - @parameter - for delta_index in range(len(deltas)): - alias delta = deltas[delta_index] + var counts = List(1 << 12, 1 << 16) + var deltas = List(0, 2, 5, 20, 100) - @parameter - for count_index in range(len(counts)): - alias count = counts[count_index] - var list = random_scalar_list[DType.uint8](count, delta) + for delta in deltas: + for count in counts: + var list = random_scalar_list[DType.uint8](count[], delta[]) @parameter fn bench_sort_list(inout b: Bencher) raises: @@ -319,18 +310,18 @@ fn bench_low_cardinality_list_sort(inout m: Bench) raises: m.bench_function[bench_sort_list]( BenchId( "bench_std_sort_low_card_list_" - + str(count) + + str(count[]) + "_delta_" - + str(delta) + + str(delta[]) ) ) m.bench_function[bench_heap_sort]( BenchId( "bench_heap_sort_low_card_list_" - + str(count) + + str(count[]) + "_delta_" - + str(delta) + + str(delta[]) ) ) _ = list^ From a972027f2ae12af55e74a2335783f80184675c41 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 10 Jul 2024 02:12:21 -0400 Subject: [PATCH 1186/2019] [stdlib] Clean up arguments for various sort functions. The arguments of `_partition` and `_insertion_sort` now has the form ``` _partition(ptr, start, end) ``` and `_quicksort` has ``` _quicksort(ptr, len) ``` We should make them consistent with the signature of `_quicksort`. MODULAR_ORIG_COMMIT_REV_ID: 3b296b35a7826505035287e15ae1aad579edfb1d --- stdlib/benchmarks/builtin/bench_sort.mojo | 2 +- stdlib/src/builtin/sort.mojo | 98 ++++++++++++----------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index fdde4d8e8c..efaad4249d 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -63,7 +63,7 @@ fn insertion_sort[type: DType](list: List[Scalar[type]]): fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) - _insertion_sort[Scalar[type], _less_than](ptr, 0, len(list)) + _insertion_sort[Scalar[type], _less_than](ptr, len(list)) @always_inline diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 2d25c1eb63..52bc2d7e6b 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -31,17 +31,17 @@ alias _cmp_fn_type = fn[type: AnyTrivialRegType] (type, type) capturing -> Bool @always_inline fn _insertion_sort[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], start: Int, end: Int): +](array: Pointer[type], size: Int): """Sort the array[start:end] slice""" - for i in range(start + 1, end): + for i in range(1, size): var value = array[i] var j = i # Find the placement of the value in the array, shifting as we try to # find the position. Throughout, we assume array[start:i] has already # been sorted. - while j > start and not cmp_fn[type](array[j - 1], value): + while j > 0 and not cmp_fn[type](array[j - 1], value): array[j] = array[j - 1] j -= 1 @@ -51,17 +51,17 @@ fn _insertion_sort[ @always_inline fn _insertion_sort[ type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool -](array: UnsafePointer[type], start: Int, end: Int): +](array: UnsafePointer[type], size: Int): """Sort the array[start:end] slice""" - for i in range(start + 1, end): + for i in range(1, size): var value = array[i] var j = i # Find the placement of the value in the array, shifting as we try to # find the position. Throughout, we assume array[start:i] has already # been sorted. - while j > start and not cmp_fn(array[j - 1], value): + while j > 0 and not cmp_fn(array[j - 1], value): array[j] = array[j - 1] j -= 1 @@ -72,10 +72,10 @@ fn _insertion_sort[ @always_inline fn _quicksort_partition_right[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], start: Int, end: Int) -> Int: - var left = start + 1 - var right = end - 1 - var pivot_value = array[start] +](array: Pointer[type], size: Int) -> Int: + var left = 1 + var right = size - 1 + var pivot_value = array[0] while True: # no need for left < right since quick sort pick median of 3 as pivot @@ -85,7 +85,7 @@ fn _quicksort_partition_right[ right -= 1 if left >= right: var pivot_pos = left - 1 - swap(array[pivot_pos], array[start]) + swap(array[pivot_pos], array[0]) return pivot_pos swap(array[left], array[right]) left += 1 @@ -96,10 +96,10 @@ fn _quicksort_partition_right[ @always_inline fn _quicksort_partition_left[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], start: Int, end: Int) -> Int: - var left = start + 1 - var right = end - 1 - var pivot_value = array[start] +](array: Pointer[type], size: Int) -> Int: + var left = 1 + var right = size - 1 + var pivot_value = array[0] while True: while left < right and not cmp_fn(pivot_value, array[left]): @@ -108,7 +108,7 @@ fn _quicksort_partition_left[ right -= 1 if left >= right: var pivot_pos = left - 1 - swap(array[pivot_pos], array[start]) + swap(array[pivot_pos], array[0]) return pivot_pos swap(array[left], array[right]) left += 1 @@ -118,18 +118,18 @@ fn _quicksort_partition_left[ @always_inline fn _partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], start: Int, end: Int) -> Int: - if start == end: - return end +](array: Pointer[type], size: Int) -> Int: + if size == 0: + return 0 - var pivot = start + (end - start) // 2 + var pivot = size // 2 var pivot_value = array[pivot] - var left = start - var right = end - 2 + var left = 0 + var right = size - 2 - swap(array[pivot], array[end - 1]) + swap(array[pivot], array[size - 1]) while left < right: if cmp_fn[type](array[left], pivot_value): @@ -141,25 +141,25 @@ fn _partition[ if cmp_fn[type](array[right], pivot_value): right += 1 - swap(array[end - 1], array[right]) + swap(array[size - 1], array[right]) return right @always_inline fn _partition[ type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool -](array: UnsafePointer[type], start: Int, end: Int) -> Int: - if start == end: - return end +](array: UnsafePointer[type], size: Int) -> Int: + if size == 0: + return size - var pivot = start + (end - start) // 2 + var pivot = size // 2 var pivot_value = array[pivot] - var left = start - var right = end - 2 + var left = 0 + var right = size - 2 - swap(array[pivot], array[end - 1]) + swap(array[pivot], array[size - 1]) while left < right: if cmp_fn(array[left], pivot_value): @@ -171,7 +171,7 @@ fn _partition[ if cmp_fn(array[right], pivot_value): right += 1 - swap(array[end - 1], array[right]) + swap(array[size - 1], array[right]) return right @@ -218,20 +218,20 @@ fn _estimate_initial_height(size: Int) -> Int: @always_inline fn _delegate_small_sort[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], len: Int): - if len == 2: +](array: Pointer[type], size: Int): + if size == 2: _small_sort[2, type, cmp_fn](array) return - if len == 3: + if size == 3: _small_sort[3, type, cmp_fn](array) return - if len == 4: + if size == 4: _small_sort[4, type, cmp_fn](array) return - if len == 5: + if size == 5: _small_sort[5, type, cmp_fn](array) return @@ -257,7 +257,7 @@ fn _quicksort[ continue if len < 32: - _insertion_sort[type, cmp_fn](array, start, end) + _insertion_sort[type, cmp_fn](array + start, len) continue # pick median of 3 as pivot @@ -267,15 +267,17 @@ fn _quicksort[ # be the same, so no need to recurse that interval # already have array[start - 1] <= array[start] if start > 0 and not cmp_fn(array[start - 1], array[start]): - var pivot = _quicksort_partition_left[type, cmp_fn]( - array, start, end + var pivot = start + _quicksort_partition_left[type, cmp_fn]( + array + start, len ) if end > pivot + 2: stack.append(pivot + 1) stack.append(end) continue - var pivot = _quicksort_partition_right[type, cmp_fn](array, start, end) + var pivot = start + _quicksort_partition_right[type, cmp_fn]( + array + start, len + ) if end > pivot + 2: stack.append(pivot + 1) @@ -305,10 +307,10 @@ fn _quicksort[ continue if len < 8: - _insertion_sort[type, cmp_fn](array, start, end) + _insertion_sort[type, cmp_fn](array + start, len) continue - var pivot = _partition[type, cmp_fn](array, start, end) + var pivot = start + _partition[type, cmp_fn](array + start, len) stack.append(pivot + 1) stack.append(end) @@ -322,7 +324,7 @@ fn _quicksort[ # ===----------------------------------------------------------------------===# fn partition[ type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](buff: Pointer[type], k: Int, size: Int): +](array: Pointer[type], k: Int, size: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. @@ -332,7 +334,7 @@ fn partition[ cmp_fn: Comparison functor of type, type) capturing -> Bool type. Args: - buff: Input buffer. + array: Input buffer. k: Index of the partition element. size: The length of the buffer. """ @@ -342,7 +344,7 @@ fn partition[ while len(stack) > 0: var end = stack.pop() var start = stack.pop() - var pivot = _partition[type, cmp_fn](buff, start, end) + var pivot = start + _partition[type, cmp_fn](array + start, end - start) if pivot == k: break elif k < pivot: @@ -377,7 +379,7 @@ fn sort(inout buff: Pointer[Int], len: Int): return if len < 32: - _insertion_sort[Int, _less_than](buff, 0, len) + _insertion_sort[Int, _less_than](buff, len) return _quicksort[Int, _less_than](buff, len) @@ -405,7 +407,7 @@ fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): return if len < 32: - _insertion_sort[Scalar[type], _less_than](buff, 0, len) + _insertion_sort[Scalar[type], _less_than](buff, len) return _quicksort[Scalar[type], _less_than](buff, len) From f794dbe8f3c0a30ef8d35f9244a48f8d123f8767 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 10 Jul 2024 09:48:53 -0500 Subject: [PATCH 1187/2019] [stdlib] Remove `OptionalReg` import from `builtin_slice` `Slice` recently upgraded to use `Optional`, but this old import was left around for backward-compatibility since several things relied on it being transitively included. Fix the import to just import `Optional` and not `OptionalReg`; update downstream users. MODULAR_ORIG_COMMIT_REV_ID: a7980de48e78f7bed4263e748bc11dd8243ca5a2 --- stdlib/src/builtin/builtin_slice.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 3408caf30d..abb50a212e 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import OptionalReg +from collections import Optional @value From 99db0eb00344f1914a4e38fd83caf2412c226421 Mon Sep 17 00:00:00 2001 From: soraros Date: Wed, 10 Jul 2024 10:12:26 -0500 Subject: [PATCH 1188/2019] [External] [stdlib] Move to `EqualityComparableCollectionElement` (#43178) [External] [stdlib] Move to `EqualityComparableCollectionElement` `EqualityComparable` part of this trait decomposition is less strict of a requirement than `Comparable` which requires the `<`, `<=`, `>`, and `>=` operators in addition to the `==` and `!=` operators. Changing usage requirements to `EqualityComparableCollectionElement` instead of `ComparableCollectionElement` makes these types work with a wider set of types. Co-authored-by: soraros Closes modularml/mojo#3211 MODULAR_ORIG_COMMIT_REV_ID: 3a8a7dd06d5c40bd52ce9e7c1579b8164f98a24d --- stdlib/src/collections/inline_list.mojo | 6 ++++-- stdlib/src/collections/list.mojo | 10 ++++++---- stdlib/src/utils/static_tuple.mojo | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index c5267afb1c..b14e2341aa 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -181,7 +181,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): return _InlineListIter(0, self) fn __contains__[ - C: ComparableCollectionElement + C: EqualityComparableCollectionElement ](self: Self, value: C) -> Bool: """Verify if a given value is present in the list. @@ -212,7 +212,9 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # Methods # ===-------------------------------------------------------------------===# - fn count[C: ComparableCollectionElement](self: Self, value: C) -> Int: + fn count[ + C: EqualityComparableCollectionElement + ](self: Self, value: C) -> Int: """Counts the number of occurrences of a value in the list. ```mojo diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index dbe876f6ca..70fbfa776a 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -198,7 +198,7 @@ struct List[T: CollectionElement]( # ===-------------------------------------------------------------------===# fn __contains__[ - U: ComparableCollectionElement + U: EqualityComparableCollectionElement ](self: List[U], value: U) -> Bool: """Verify if a given value is present in the list. @@ -591,7 +591,7 @@ struct List[T: CollectionElement]( # TODO: Remove explicit self type when issue 1876 is resolved. fn index[ - C: ComparableCollectionElement + C: EqualityComparableCollectionElement ]( ref [_]self: List[C], value: C, @@ -616,7 +616,7 @@ struct List[T: CollectionElement]( Parameters: C: The type of the elements in the list. Must implement the - `ComparableCollectionElement` trait. + `EqualityComparableCollectionElement` trait. Returns: The index of the first occurrence of the value in the list. @@ -783,7 +783,9 @@ struct List[T: CollectionElement]( (self.data + idx).destroy_pointee() (self.data + idx).init_pointee_move(value^) - fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: + fn count[ + T: EqualityComparableCollectionElement + ](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. Note that since we can't condition methods on a trait yet, the way to call this method is a bit special. Here is an example below. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 16983094c8..db21f999a2 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -469,7 +469,9 @@ struct InlineArray[ return UnsafePointer.address_of(self._array).bitcast[Self.ElementType]() @always_inline - fn __contains__[T: ComparableCollectionElement](self, value: T) -> Bool: + fn __contains__[ + T: EqualityComparableCollectionElement + ](self, value: T) -> Bool: """Verify if a given value is present in the array. ```mojo From 217f1fb545c854fad29bf32c15a753eea60c4446 Mon Sep 17 00:00:00 2001 From: soraros Date: Wed, 10 Jul 2024 11:10:59 -0500 Subject: [PATCH 1189/2019] [External] [stdlib] Modernise `Span.__[eq/ne]__` with latest conditional conformance support (#43177) [External] [stdlib] Modernise `Span.__[eq/ne]__` with latest conditional conformance support Co-authored-by: soraros Closes modularml/mojo#3210 MODULAR_ORIG_COMMIT_REV_ID: 7cf6612d0a4917be7cb96fe0c20e5b9fd9a3e7db --- stdlib/src/utils/span.mojo | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 5a980bedf2..e27d8d6bb4 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -21,7 +21,6 @@ from utils import Span """ from . import InlineArray -from collections.list import ComparableCollectionElement from sys.intrinsics import _type_is_eq @@ -251,12 +250,12 @@ struct Span[ return len(self) > 0 fn __eq__[ - T2: ComparableCollectionElement - ](ref [_]self: Span[T2, lifetime], ref [_]rhs: Span[T]) -> Bool: + T: EqualityComparableCollectionElement + ](ref [_]self: Span[T, lifetime], ref [_]rhs: Span[T]) -> Bool: """Verify if span is equal to another span. Parameters: - T2: The type of the elements in the span. Must implement the + T: The type of the elements in the span. Must implement the traits `EqualityComparable` and `CollectionElement`. Args: @@ -265,29 +264,28 @@ struct Span[ Returns: True if the spans are equal in length and contain the same elements, False otherwise. """ - constrained[_type_is_eq[T, T2](), "T must be equal to T2"]() # both empty if not self and not rhs: return True if len(self) != len(rhs): return False # same pointer and length, so equal - if self.unsafe_ptr().bitcast[T]() == rhs.unsafe_ptr(): + if self.unsafe_ptr() == rhs.unsafe_ptr(): return True for i in range(len(self)): - if self[i] != rhs.unsafe_ptr().bitcast[T2]()[i]: + if self[i] != rhs[i]: return False return True @always_inline fn __ne__[ - T2: ComparableCollectionElement - ](ref [_]self: Span[T2, lifetime], ref [_]rhs: Span[T]) -> Bool: + T: EqualityComparableCollectionElement + ](ref [_]self: Span[T, lifetime], ref [_]rhs: Span[T]) -> Bool: """Verify if span is not equal to another span. Parameters: - T2: The type of the elements in the span. Must implement the - traits `EqualityComparable` and `CollectionElement`. + T: The type of the elements in the span. Must implement the + traits `EqualityComparable` and `CollectionElement`. Args: rhs: The span to compare against. @@ -295,7 +293,6 @@ struct Span[ Returns: True if the spans are not equal in length or contents, False otherwise. """ - constrained[_type_is_eq[T, T2](), "T must be equal to T2"]() return not self == rhs fn fill[lifetime: MutableLifetime](self: Span[T, lifetime], value: T): From 317666161f41244fbb836b54288c114ad0a9e982 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 10 Jul 2024 12:19:51 -0500 Subject: [PATCH 1190/2019] [stdlib] feature: Replace `NoneType` alias with a `struct NoneType` This will make it possible to add trait conformances for `NoneType`. MODULAR_ORIG_COMMIT_REV_ID: bfc23d47a6f8b60d443755f235f9b80fbcef923d --- docs/changelog.md | 6 ++++ stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/none.mojo | 31 ++++++++++++++++++ stdlib/src/builtin/string.mojo | 2 +- stdlib/src/builtin/type_aliases.mojo | 3 -- stdlib/src/collections/optional.mojo | 39 +++++++++++++++++++++- stdlib/src/os/os.mojo | 4 +-- stdlib/src/python/object.mojo | 11 +++++++ stdlib/src/sys/intrinsics.mojo | 2 +- stdlib/test/builtin/test_none.mojo | 49 ++++++++++++++++++++++++++++ 10 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 stdlib/src/builtin/none.mojo create mode 100644 stdlib/test/builtin/test_none.mojo diff --git a/docs/changelog.md b/docs/changelog.md index d2df36d413..17fc11fdfe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -397,6 +397,12 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. print(s.start.value()) # must retrieve the value from the optional ``` +- `NoneType` is now a normal standard library type, and not an alias for a raw + MLIR type. + + Function signatures spelled as `fn(...) -> NoneType` should transition to + being written as `fn(...) -> None`. + - Accessing local Python modules with `Python.add_to_path(".")` is no longer required, it now behaves the same as Python, you can access modules in the same folder as the target file: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 68dada8ce5..eed72c548c 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -320,7 +320,7 @@ fn _try_write_int[ len=1, ) fmt.write_str(zero) - return + return None # Create a buffer to store the formatted value diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo new file mode 100644 index 0000000000..fb459c97b5 --- /dev/null +++ b/stdlib/src/builtin/none.mojo @@ -0,0 +1,31 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines the builtin `NoneType`. + +These are Mojo built-ins, so you don't need to import them. +""" + + +@value +@register_passable("trivial") +struct NoneType(CollectionElement): + """Represents the absence of a value.""" + + alias _mlir_type = __mlir_type.`!kgen.none` + """Raw MLIR type of the `None` value.""" + + var _value: Self._mlir_type + + fn __init__(inout self): + """Construct an instance of the `None` type.""" + self._value = None diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 350f948043..4a28061cf2 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2540,7 +2540,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): if start: var start_value = start.value() var current_entry = Self( - first_curly=start_value, last_curly=i, field=None + first_curly=start_value, last_curly=i, field=NoneType() ) if i - start_value != 1: var field = format_src[start_value + 1 : i] diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index cd8720e507..7ae2875e9b 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -18,9 +18,6 @@ These are Mojo built-ins, so you don't need to import them. alias AnyTrivialRegType = __mlir_type.`!kgen.type` """Represents any register passable Mojo data type.""" -alias NoneType = __mlir_type.`!kgen.none` -"""Represents the absence of a value.""" - alias ImmutableLifetime = __mlir_type.`!lit.lifetime<0>` """Immutable lifetime reference type.""" diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 929432e079..d4f9867b7c 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -46,7 +46,6 @@ struct _NoneType(CollectionElement, CollectionElementNew): # ===----------------------------------------------------------------------===# -@value struct Optional[T: CollectionElement]( CollectionElement, CollectionElementNew, Boolable ): @@ -99,6 +98,17 @@ struct Optional[T: CollectionElement]( """ self._value = Self._type(value^) + # TODO(MSTDL-715): + # This initializer should not be necessary, we should need + # only the initilaizer from a `NoneType`. + fn __init__(inout self, value: NoneType._mlir_type): + """Construct an empty Optional. + + Args: + value: Must be exactly `None`. + """ + self = Self(value=NoneType(value)) + fn __init__(inout self, value: NoneType): """Construct an empty Optional. @@ -115,6 +125,22 @@ struct Optional[T: CollectionElement]( """ self.__copyinit__(other) + fn __copyinit__(inout self, other: Self): + """Copy construct an Optional. + + Args: + other: The Optional to copy. + """ + self._value = other._value + + fn __moveinit__(inout self, owned other: Self): + """Move this `Optional`. + + Args: + other: The `Optional` to move from. + """ + self._value = other._value^ + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# @@ -391,6 +417,17 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): _type = Self._mlir_type, index = Int(0).value ](value) + # TODO(MSTDL-715): + # This initializer should not be necessary, we should need + # only the initilaizer from a `NoneType`. + fn __init__(inout self, value: NoneType._mlir_type): + """Construct an empty Optional. + + Args: + value: Must be exactly `None`. + """ + self = Self(value=NoneType(value)) + fn __init__(inout self, value: NoneType): """Create an optional without a value from a None literal. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 1d9305dae8..fb7cb0df26 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -231,7 +231,7 @@ fn listdir[PathLike: os.PathLike](path: PathLike) raises -> List[String]: @no_inline -fn abort[result: AnyType = NoneType]() -> result: +fn abort[result: AnyType = NoneType._mlir_type]() -> result: """Calls a target dependent trap instruction if available. Parameters: @@ -250,7 +250,7 @@ fn abort[result: AnyType = NoneType]() -> result: @no_inline fn abort[ - result: AnyType = NoneType, *, formattable: Formattable + result: AnyType = NoneType._mlir_type, *, formattable: Formattable ](message: formattable) -> result: """Calls a target dependent trap instruction if available. diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index fb5d7dc13e..076901a4c7 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -136,6 +136,17 @@ struct PythonObject( """ self.py_object = ptr + # TODO(MSTDL-715): + # This initializer should not be necessary, we should need + # only the initilaizer from a `NoneType`. + fn __init__(inout self, none: NoneType._mlir_type): + """Initialize a none value object from a `None` literal. + + Args: + none: None. + """ + self = Self(none=NoneType(none)) + fn __init__(inout self, none: NoneType): """Initialize a none value object from a `None` literal. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 2fac00d50a..90489298d3 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1195,7 +1195,7 @@ fn prefetch[ Args: addr: The data pointer to prefetch. """ - return llvm_intrinsic["llvm.prefetch", NoneType]( + llvm_intrinsic["llvm.prefetch", NoneType]( addr.bitcast[DType.invalid.value](), params.rw, params.locality, diff --git a/stdlib/test/builtin/test_none.mojo b/stdlib/test/builtin/test_none.mojo new file mode 100644 index 0000000000..afa682405a --- /dev/null +++ b/stdlib/test/builtin/test_none.mojo @@ -0,0 +1,49 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + + +def main(): + test_type_from_none() + + +struct FromNone: + var value: Int + + fn __init__(inout self, none: NoneType): + self.value = -1 + + fn __init__(inout self, value: Int): + self.value = value + + +def test_type_from_none(): + obj = FromNone(5) + + obj = FromNone(None) + + # ------------------------------------- + # Test implicit conversion from `None` + # ------------------------------------- + + fn foo(arg: FromNone): + pass + + # FIXME: + # This currently fails, because it requires 2 "hops" of conversion: + # 1. !kgen.none => NoneType + # 2. NoneType => FromNone + # foo(None) + # + # But, interestingly, this does not fail? + var obj2: FromNone = None From 7ad5ede20f600815931260df0ba67d8b5ff71ed1 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 10 Jul 2024 12:39:35 -0500 Subject: [PATCH 1191/2019] [External] [stdlib] Abort on list resize to bigger sizes without fill value (#43160) Fix https://github.com/modularml/mojo/issues/2938 Note that I had to remove the `@always_inline` to avoid infinite recursion of `@always_inline` functions. Closes modularml/mojo#2945 MODULAR_ORIG_COMMIT_REV_ID: 396f4d22b553ed44a17c2e9c5b7de486e4a00d20 --- stdlib/src/collections/list.mojo | 14 +++++++------- stdlib/test/pathlib/test_pathlib.mojo | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 70fbfa776a..31a7f7bafb 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -557,13 +557,13 @@ struct List[T: CollectionElement]( Args: new_size: The new size. """ - debug_assert( - new_size <= self.size, - ( - "New size must be smaller than or equal to current size when no" - " new value is provided." - ), - ) + if self.size < new_size: + abort( + "You are calling List.resize with a new_size bigger than the" + " current size. If you want to make the List bigger, provide a" + " value to fill the new slots with. If not, make sure the new" + " size is smaller than the current size." + ) for i in range(new_size, self.size): (self.data + i).destroy_pointee() self.size = new_size diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 7c12368ced..2c039f907d 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -114,7 +114,7 @@ def set_home(path: Path): # More elaborate tests in `os/path/test_expanduser.mojo` def test_expand_user(): - alias user_path = get_user_path() + var user_path = get_user_path() var original_home = get_current_home() set_home(user_path) @@ -129,7 +129,7 @@ def test_expand_user(): def test_home(): - alias user_path = get_user_path() + var user_path = get_user_path() var original_home = get_current_home() set_home(user_path) From c0765b9d13ed52916c85a0811ce7bb7158985f93 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 10 Jul 2024 12:44:27 -0500 Subject: [PATCH 1192/2019] [stdlib] Remove unused `unroll` imports Several files import `unroll`, but no longer use it. Most use the new `@parameter for` decorator instead as of recent refactorings. Remove this unused import. Fix downstream callers to explicitly import `unroll` too. MODULAR_ORIG_COMMIT_REV_ID: e1a0fd957d04d8a07709dffa1a0e08f4eaa2f856 --- stdlib/src/builtin/dtype.mojo | 2 -- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/math/polynomial.mojo | 2 -- stdlib/src/python/object.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 2 -- stdlib/test/builtin/test_simd.mojo | 1 + 7 files changed, 4 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index c2ae4a1091..bd88207820 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -18,8 +18,6 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement from sys import sizeof as _sizeof -from utils import unroll - alias _mIsSigned = UInt8(1) alias _mIsInteger = UInt8(1 << 7) alias _mIsNotInteger = UInt8(~(1 << 7)) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index f0d725f349..2dbf31008b 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -28,7 +28,7 @@ from builtin.dtype import _get_dtype_printf_format from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer -from utils import StringRef, unroll, StaticString, StringSlice +from utils import StringRef, StaticString, StringSlice from utils._format import Formattable, Formatter, write_to # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 79858bd9cf..01a1eb877c 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -20,7 +20,7 @@ from sys.intrinsics import _type_is_eq from memory import Arc, memcmp, memcpy -from utils import StringRef, Variant, unroll +from utils import StringRef, Variant # ===----------------------------------------------------------------------=== # # _ObjectImpl diff --git a/stdlib/src/math/polynomial.mojo b/stdlib/src/math/polynomial.mojo index 2f4473c9f6..28efe2d1a8 100644 --- a/stdlib/src/math/polynomial.mojo +++ b/stdlib/src/math/polynomial.mojo @@ -21,8 +21,6 @@ from math.polynomial import polynomial_evaluate from collections import List -from utils.loop import unroll - # ===----------------------------------------------------------------------===# # polynomial_evaluate # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 076901a4c7..3dc95199d8 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -21,7 +21,7 @@ from python import PythonObject from sys.intrinsics import _type_is_eq -from utils import StringRef, unroll +from utils import StringRef from ._cpython import CPython, PyObjectPtr from .python import Python, _get_global_python_itf diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index db21f999a2..c3765e0fe3 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -23,8 +23,6 @@ from sys.intrinsics import _type_is_eq from memory import UnsafePointer -from utils import unroll - # ===----------------------------------------------------------------------===# # Utilities # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 48ff557cee..36650881f6 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -23,6 +23,7 @@ from testing import ( assert_true, ) +from utils import unroll from utils.numerics import isfinite, isinf, isnan, nan From 43d152021e869b229745b8df5551afae13eb3f31 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 10 Jul 2024 13:24:41 -0500 Subject: [PATCH 1193/2019] [stdlib] Use `Optional.or_else` in `Slice` Instead of the branch inline, use `Optional.or_else(default_value)` API to improve readability a smidge. MODULAR_ORIG_COMMIT_REV_ID: 330655bd76a5064675b98528ecb0f783bedf78b6 --- stdlib/src/builtin/builtin_slice.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index abb50a212e..48958291a1 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -75,7 +75,7 @@ struct Slice( """ self.start = start self.end = end - self.step = step.value() if step else 1 + self.step = step.or_else(1) fn __init__(inout self, *, other: Self): """Creates a deep copy of the Slice. From 9b1f8afcc04cf1b549fb491e396debbb9443ca5b Mon Sep 17 00:00:00 2001 From: soraros Date: Wed, 10 Jul 2024 13:52:41 -0500 Subject: [PATCH 1194/2019] [External] [stdlib] Simplify several methods in the `UnsafePointer` struct: (#43187) - Change the `offset` method to take `Int` instead of `Intable`. `UnsafePointer` is itself `Intable`, and being able to offset a pointer by another pointer is undesirable. - Clean up `initialize_pointee_explicit_copy` by utilizing the latest conditional conformance support. - Change the parameter name for `bitcast` to improve consistency. - Modify several methods to infer parameters automatically; relying on the default parameter instead of passing it manually. Co-authored-by: soraros Closes modularml/mojo#3203 MODULAR_ORIG_COMMIT_REV_ID: ad282a055daf19e33b2a51bc799d8ef428245b95 --- stdlib/src/memory/unsafe_pointer.mojo | 53 ++++++++++++--------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 53d1537f61..cd68962f7e 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -200,19 +200,16 @@ struct UnsafePointer[ ) @always_inline - fn offset[T: Intable](self, idx: T) -> Self: + fn offset(self, idx: Int) -> Self: """Returns a new pointer shifted by the specified offset. - Parameters: - T: The Intable type of the offset. - Args: idx: The offset of the new pointer. Returns: The new constructed DTypePointer. """ - return __mlir_op.`pop.offset`(self.address, int(idx).value) + return __mlir_op.`pop.offset`(self.address, idx.value) @always_inline fn __getitem__( @@ -259,7 +256,7 @@ struct UnsafePointer[ Args: offset: The offset index. """ - self = self.offset(offset) + self = self + offset @always_inline fn __isub__(inout self, offset: Int): @@ -268,7 +265,7 @@ struct UnsafePointer[ Args: offset: The offset index. """ - self.__iadd__(-offset) + self = self - offset @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: @@ -402,8 +399,8 @@ struct UnsafePointer[ @always_inline fn initialize_pointee_explicit_copy[ - T2: ExplicitlyCopyable - ](self: UnsafePointer[T, address_space], value: T2): + T: ExplicitlyCopyable, // + ](self: UnsafePointer[T], value: T): """Emplace a copy of `value` into this pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -415,23 +412,13 @@ struct UnsafePointer[ the callee side when the value must be copied. Parameters: - T2: The type the pointer points to, which must be + T: The type the pointer points to, which must be `ExplicitlyCopyable`. Args: value: The value to emplace. """ - - constrained[ - address_space is AddressSpace.GENERIC, - "can not initialize pointer in non-GENERIC address space", - ]() - - constrained[_type_is_eq[T, T2](), "pointee type is not self.T"]() - - var ptr = self.bitcast[T2, address_space = AddressSpace.GENERIC]() - - __get_address_as_uninit_lvalue(ptr.address) = T2(other=value) + __get_address_as_uninit_lvalue(self.address) = T(other=value) @always_inline fn free(self): @@ -440,14 +427,14 @@ struct UnsafePointer[ @always_inline("nodebug") fn bitcast[ - new_type: AnyType = T, + T: AnyType = Self.T, /, address_space: AddressSpace = Self.address_space, - ](self) -> UnsafePointer[new_type, address_space]: + ](self) -> UnsafePointer[T, address_space]: """Bitcasts a UnsafePointer to a different type. Parameters: - new_type: The target type. + T: The target type. address_space: The address space of the result. Returns: @@ -455,11 +442,11 @@ struct UnsafePointer[ as the original UnsafePointer. """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, address_space]._mlir_type, + _type = UnsafePointer[T, address_space]._mlir_type, ](self.address) @always_inline - fn destroy_pointee(self: UnsafePointer[_, AddressSpace.GENERIC]): + fn destroy_pointee(self: UnsafePointer[_]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed @@ -471,7 +458,9 @@ struct UnsafePointer[ _ = __get_address_as_owned_value(self.address) @always_inline - fn take_pointee[T: Movable](self: UnsafePointer[T]) -> T: + fn take_pointee[ + T: Movable, //, + ](self: UnsafePointer[T]) -> T: """Move the value at the pointer out, leaving it uninitialized. The pointer must not be null, and the pointer memory location is assumed @@ -492,7 +481,9 @@ struct UnsafePointer[ # TODO: Allow overloading on more specific traits @always_inline - fn init_pointee_move[T: Movable](self: UnsafePointer[T], owned value: T): + fn init_pointee_move[ + T: Movable, //, + ](self: UnsafePointer[T], owned value: T): """Emplace a new value into the pointer location, moving from `value`. The pointer memory location is assumed to contain uninitialized data, @@ -512,7 +503,9 @@ struct UnsafePointer[ __get_address_as_uninit_lvalue(self.address) = value^ @always_inline - fn init_pointee_copy[T: Copyable](self: UnsafePointer[T], value: T): + fn init_pointee_copy[ + T: Copyable, //, + ](self: UnsafePointer[T], value: T): """Emplace a copy of `value` into the pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -533,7 +526,7 @@ struct UnsafePointer[ @always_inline fn move_pointee_into[ - T: Movable + T: Movable, //, ](self: UnsafePointer[T], dst: UnsafePointer[T]): """Moves the value `self` points to into the memory location pointed to by `dst`. From 873eb83dfd59a28f082fb5595a077be418f42096 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 10 Jul 2024 14:03:09 -0500 Subject: [PATCH 1195/2019] [stdlib] bugfix: Fix printing Int(0) on GPU Fix print of 0 on GPU passing non-null-terminated buffer to `printf`. MODULAR_ORIG_COMMIT_REV_ID: 0e106362c14acf1f7df04b0f018865759281eeb2 --- stdlib/src/builtin/format_int.mojo | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index eed72c548c..a12244419d 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -315,11 +315,23 @@ fn _try_write_int[ # SAFETY: # This static lifetime is valid as long as we're using a # `StringLiteral` for `digit_chars`. + var zero_char = digit_chars_array[0] + + # Construct a null-terminated buffer of single-byte char. + var zero_buf = InlineArray[UInt8, 2](zero_char, 0) + var zero = StringSlice[ImmutableStaticLifetime]( - unsafe_from_utf8_ptr=digit_chars_array, + # TODO(MSTDL-720): + # Support printing non-null-terminated strings on GPU and switch + # back to this code without a workaround. + # unsafe_from_utf8_ptr=digit_chars_array, + unsafe_from_utf8_ptr=zero_buf.unsafe_ptr(), len=1, ) fmt.write_str(zero) + + _ = zero_buf + return None # Create a buffer to store the formatted value From 77fd3fd31ce7adc1d7f1e3d9193c48b0487ad843 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:05:23 -0400 Subject: [PATCH 1196/2019] [stdlib] Use `UnsafePointer` in `sort.mojo` This PR removes the `LegacyPointer` API and migrates to `UnsafePointer` for `sort` module. **Problem:** The following pattern fails when `type` is register passable. ``` fn f[type: CollectionElement, cmp: fn(type, type) capturing -> Bool](): ``` **Solution:** Have a wrapper that wraps is to a memory-only type. ``` @value struct SortWrapper[type: CollectionElement]: var data: type ``` and we can instead have ``` fn f[ type: CollectionElement, cmp: fn(SortWrapper[type]], SortWrapper[type]) capturing -> Bool ](): ``` Since we are wrapping register passable types to a memory only type, we might expect some performance regression. - [ ] PR that benchmarks the regression. MODULAR_ORIG_COMMIT_REV_ID: 367759de39192277bd92f60e71b5e9624391c926 --- docs/changelog.md | 6 + stdlib/benchmarks/builtin/bench_sort.mojo | 38 +- stdlib/src/builtin/sort.mojo | 485 +++++++++++------- stdlib/src/collections/counter.mojo | 2 +- stdlib/test/builtin/test_sort.mojo | 77 ++- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- 6 files changed, 367 insertions(+), 243 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 17fc11fdfe..1b63d5a2e4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -435,6 +435,12 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `countl_zero` -> `count_leading_zeros` - `countr_zero` -> `count_trailing_zeros` +- `sort` no longer takes `LegacyPointer`. The current API supports: + - `sort(list)` just plain list + - `sort[type, cmp_fn](list)` list with custom compare function + - `sort(ptr, len)` a pointer and length (can change to Span in future) + - `sort[type, cmp_fn](ptr, len)` above with custom compare + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index efaad4249d..7b0d8a5719 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -15,7 +15,13 @@ from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from random import * -from stdlib.builtin.sort import sort, _small_sort, _insertion_sort, _heap_sort +from stdlib.builtin.sort import ( + sort, + _small_sort, + _insertion_sort, + _heap_sort, + _SortWrapper, +) # ===----------------------------------------------------------------------===# # Benchmark Utils @@ -57,24 +63,24 @@ fn random_scalar_list[ @always_inline fn insertion_sort[type: DType](list: List[Scalar[type]]): - var ptr = rebind[Pointer[Scalar[type]]](list.data) - @parameter - fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) + fn _less_than( + lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] + ) -> Bool: + return lhs.data < rhs.data - _insertion_sort[Scalar[type], _less_than](ptr, len(list)) + _insertion_sort[Scalar[type], _less_than](list.data, len(list)) @always_inline fn small_sort[size: Int, type: DType](list: List[Scalar[type]]): - var ptr = rebind[Pointer[Scalar[type]]](list.data) - @parameter - fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) + fn _less_than( + lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] + ) -> Bool: + return lhs.data < rhs.data - _small_sort[size, Scalar[type], _less_than](ptr) + _small_sort[size, Scalar[type], _less_than](list.data) # ===----------------------------------------------------------------------===# @@ -213,13 +219,13 @@ fn bench_small_list_sort(inout m: Bench) raises: @always_inline fn heap_sort[type: DType](list: List[Scalar[type]]): - var ptr = rebind[Pointer[Scalar[type]]](list.data) - @parameter - fn _less_than_equal[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + fn _less_than( + lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] + ) -> Bool: + return lhs.data < rhs.data - _heap_sort[Scalar[type], _less_than_equal](ptr, len(list)) + _heap_sort[Scalar[type], _less_than](list.data, len(list)) @parameter diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 52bc2d7e6b..55bb7544e4 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -19,38 +19,22 @@ from collections import List from sys import bitwidthof from bit import count_leading_zeros -from memory import Pointer, UnsafePointer +from memory import UnsafePointer # ===----------------------------------------------------------------------===# # sort # ===----------------------------------------------------------------------===# -alias _cmp_fn_type = fn[type: AnyTrivialRegType] (type, type) capturing -> Bool - -@always_inline -fn _insertion_sort[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int): - """Sort the array[start:end] slice""" - - for i in range(1, size): - var value = array[i] - var j = i - - # Find the placement of the value in the array, shifting as we try to - # find the position. Throughout, we assume array[start:i] has already - # been sorted. - while j > 0 and not cmp_fn[type](array[j - 1], value): - array[j] = array[j - 1] - j -= 1 - - array[j] = value +@value +struct _SortWrapper[type: CollectionElement](CollectionElement): + var data: type @always_inline fn _insertion_sort[ - type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, ](array: UnsafePointer[type], size: Int): """Sort the array[start:end] slice""" @@ -71,8 +55,9 @@ fn _insertion_sort[ # put everything thats "<" to the left of pivot @always_inline fn _quicksort_partition_right[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int) -> Int: + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], size: Int) -> Int: var left = 1 var right = size - 1 var pivot_value = array[0] @@ -95,8 +80,9 @@ fn _quicksort_partition_right[ # put everything thats "<=" to the left of pivot @always_inline fn _quicksort_partition_left[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int) -> Int: + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], size: Int) -> Int: var left = 1 var right = size - 1 var pivot_value = array[0] @@ -115,70 +101,10 @@ fn _quicksort_partition_left[ right -= 1 -@always_inline -fn _partition[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int) -> Int: - if size == 0: - return 0 - - var pivot = size // 2 - - var pivot_value = array[pivot] - - var left = 0 - var right = size - 2 - - swap(array[pivot], array[size - 1]) - - while left < right: - if cmp_fn[type](array[left], pivot_value): - left += 1 - elif not cmp_fn[type](array[right], pivot_value): - right -= 1 - else: - swap(array[left], array[right]) - - if cmp_fn[type](array[right], pivot_value): - right += 1 - swap(array[size - 1], array[right]) - return right - - -@always_inline -fn _partition[ - type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool -](array: UnsafePointer[type], size: Int) -> Int: - if size == 0: - return size - - var pivot = size // 2 - - var pivot_value = array[pivot] - - var left = 0 - var right = size - 2 - - swap(array[pivot], array[size - 1]) - - while left < right: - if cmp_fn(array[left], pivot_value): - left += 1 - elif not cmp_fn(array[right], pivot_value): - right -= 1 - else: - swap(array[left], array[right]) - - if cmp_fn(array[right], pivot_value): - right += 1 - swap(array[size - 1], array[right]) - return right - - -@always_inline fn _heap_sort_fix_down[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int, idx: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], size: Int, idx: Int): var i = idx var j = i * 2 + 1 while j < size: # has left child @@ -194,8 +120,9 @@ fn _heap_sort_fix_down[ @always_inline fn _heap_sort[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], owned size: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], owned size: Int): # heapify for i in range(size // 2 - 1, -1, -1): _heap_sort_fix_down[type, cmp_fn](array, size, i) @@ -217,12 +144,13 @@ fn _estimate_initial_height(size: Int) -> Int: @always_inline fn _delegate_small_sort[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], size: Int): if size == 2: _small_sort[2, type, cmp_fn](array) - return + return if size == 3: _small_sort[3, type, cmp_fn](array) return @@ -238,11 +166,11 @@ fn _delegate_small_sort[ @always_inline fn _quicksort[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], size: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], size: Int): if size == 0: return - var stack = List[Int](capacity=_estimate_initial_height(size)) stack.append(0) stack.append(size) @@ -288,156 +216,305 @@ fn _quicksort[ stack.append(pivot) +# ===----------------------------------------------------------------------===# +# partition +# ===----------------------------------------------------------------------===# + + @always_inline -fn _quicksort[ - type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool -](array: UnsafePointer[type], size: Int): +fn _partition[ + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], size: Int) -> Int: if size == 0: - return + return size + + var pivot = size // 2 + + var pivot_value = array[pivot] + + var left = 0 + var right = size - 2 + + swap(array[pivot], array[size - 1]) + + while left < right: + if cmp_fn(array[left], pivot_value): + left += 1 + elif not cmp_fn(array[right], pivot_value): + right -= 1 + else: + swap(array[left], array[right]) + + if cmp_fn(array[right], pivot_value): + right += 1 + swap(array[size - 1], array[right]) + return right + +fn _partition[ + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], k: Int, size: Int): var stack = List[Int](capacity=_estimate_initial_height(size)) stack.append(0) stack.append(size) while len(stack) > 0: var end = stack.pop() var start = stack.pop() + var pivot = start + _partition[type, cmp_fn](array + start, end - start) + if pivot == k: + break + elif k < pivot: + stack.append(start) + stack.append(pivot) + else: + stack.append(pivot + 1) + stack.append(end) - var len = end - start - if len < 2: - continue - if len < 8: - _insertion_sort[type, cmp_fn](array + start, len) - continue +fn partition[ + type: CollectionElement, + cmp_fn: fn (type, type) capturing -> Bool, +](array: UnsafePointer[type], k: Int, size: Int): + """Partition the input buffer inplace such that first k elements are the + largest (or smallest if cmp_fn is < operator) elements. + The ordering of the first k elements is undefined. - var pivot = start + _partition[type, cmp_fn](array + start, len) + Parameters: + type: Type of the underlying data. + cmp_fn: Comparison functor of (type, type) capturing -> Bool type. - stack.append(pivot + 1) - stack.append(end) + Args: + array: Input buffer. + k: Index of the partition element. + size: The length of the buffer. + """ - stack.append(start) - stack.append(pivot) + @parameter + fn _cmp_fn(lhs: _SortWrapper[type], rhs: _SortWrapper[type]) -> Bool: + return cmp_fn(lhs.data, rhs.data) + + _partition[type, _cmp_fn](array, k, size) -# ===----------------------------------------------------------------------===# -# partition -# ===----------------------------------------------------------------------===# fn partition[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], k: Int, size: Int): + cmp_fn: fn (Int, Int) capturing -> Bool, +](array: UnsafePointer[Int], k: Int, size: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: - type: Trivial reg type of the underlying data. - cmp_fn: Comparison functor of type, type) capturing -> Bool type. + cmp_fn: Comparison functor of (type, type) capturing -> Bool type. Args: array: Input buffer. k: Index of the partition element. size: The length of the buffer. """ - var stack = List[Int](capacity=_estimate_initial_height(size)) - stack.append(0) - stack.append(size) - while len(stack) > 0: - var end = stack.pop() - var start = stack.pop() - var pivot = start + _partition[type, cmp_fn](array + start, end - start) - if pivot == k: - break - elif k < pivot: - stack.append(start) - stack.append(pivot) - else: - stack.append(pivot + 1) - stack.append(end) + @parameter + fn _cmp_fn(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return cmp_fn(lhs.data, rhs.data) -# ===----------------------------------------------------------------------===# -# sort -# ===----------------------------------------------------------------------===# + _partition[Int, _cmp_fn](array, k, size) -fn sort(inout buff: Pointer[Int], len: Int): - """Sort the buffer inplace. +fn partition[ + type: DType, + cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, +](array: UnsafePointer[Scalar[type]], k: Int, size: Int): + """Partition the input buffer inplace such that first k elements are the + largest (or smallest if cmp_fn is < operator) elements. + The ordering of the first k elements is undefined. - The function doesn't return anything, the buffer is updated inplace. + Parameters: + type: DType of the underlying data. + cmp_fn: Comparison functor of (type, type) capturing -> Bool type. Args: - buff: Input buffer. - len: The length of the buffer. + array: Input buffer. + k: Index of the partition element. + size: The length of the buffer. """ @parameter - fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) < rebind[Int](rhs) + fn _cmp_fn( + lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] + ) -> Bool: + return cmp_fn(lhs.data, rhs.data) + + _partition[Scalar[type], _cmp_fn](array, k, size) + + +# ===----------------------------------------------------------------------===# +# sort +# ===----------------------------------------------------------------------===# + +# Junction from public to private API +fn _sort[ + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](ptr: UnsafePointer[type], len: Int): if len <= 5: - _delegate_small_sort[Int, _less_than](buff, len) + _delegate_small_sort[type, cmp_fn](ptr, len) return if len < 32: - _insertion_sort[Int, _less_than](buff, len) + _insertion_sort[type, cmp_fn](ptr, len) return - _quicksort[Int, _less_than](buff, len) + _quicksort[type, cmp_fn](ptr, len) -fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): - """Sort the buffer inplace. +# TODO (MSTDL-766): The Int and Scalar[type] overload should be remove +# (same for partition) +# Eventually we want a sort that takes a Span and one that takes a List with +# optional cmp_fn. +fn sort[ + type: CollectionElement, + cmp_fn: fn (type, type) capturing -> Bool, +](ptr: UnsafePointer[type], len: Int): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. - The function doesn't return anything, the buffer is updated inplace. + Parameters: + type: CollectionElement type of the underlying data. + cmp_fn: The comparison function. + + Args: + ptr: Pointer to the start of the memory to be sorted. + len: Number of elements from ptr that to be sorted. + """ + + @parameter + fn _cmp_fn(lhs: _SortWrapper[type], rhs: _SortWrapper[type]) -> Bool: + return cmp_fn(lhs.data, rhs.data) + + _sort[type, _cmp_fn](ptr, len) + + +fn sort[ + type: CollectionElement, + cmp_fn: fn (Int, Int) capturing -> Bool, +](ptr: UnsafePointer[Int], len: Int): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. Parameters: - type: DType of the underlying data. + type: CollectionElement type of the underlying data. + cmp_fn: The comparison function. Args: - buff: Input buffer. - len: The length of the buffer. + ptr: Pointer to the start of the memory to be sorted. + len: Number of elements from ptr that to be sorted. """ @parameter - fn _less_than[ty: AnyTrivialRegType](lhs: ty, rhs: ty) -> Bool: - return rebind[Scalar[type]](lhs) < rebind[Scalar[type]](rhs) + fn _cmp_fn(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return cmp_fn(lhs.data, rhs.data) - if len <= 5: - _delegate_small_sort[Scalar[type], _less_than](buff, len) - return + _sort[Int, _cmp_fn](ptr, len) - if len < 32: - _insertion_sort[Scalar[type], _less_than](buff, len) - return - _quicksort[Scalar[type], _less_than](buff, len) +fn sort[ + type: DType, + cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, +](ptr: UnsafePointer[Scalar[type]], len: Int): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. + Parameters: + type: CollectionElement type of the underlying data. + cmp_fn: The comparison function. -fn sort(inout list: List[Int]): + Args: + ptr: Pointer to the start of the memory to be sorted. + len: Number of elements from ptr that to be sorted. + """ + + @parameter + fn _cmp_fn( + lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] + ) -> Bool: + return cmp_fn(lhs.data, rhs.data) + + _sort[Scalar[type], _cmp_fn](ptr, len) + + +fn sort(ptr: UnsafePointer[Int], len: Int): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Args: - list: Input integer list to sort. + ptr: Pointer to the start of the memory to be sorted. + len: Number of elements from ptr that to be sorted. """ - # Downcast any pointer to register-passable pointer. - var ptr = rebind[Pointer[Int]](list.data) - sort(ptr, len(list)) + @parameter + fn _cmp_fn(lhs: Int, rhs: Int) -> Bool: + return lhs < rhs -fn sort[type: DType](inout list: List[Scalar[type]]): + sort[Int, _cmp_fn](ptr, len) + + +fn sort[ + type: DType, +](ptr: UnsafePointer[Scalar[type]], len: Int): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. + + Parameters: + type: CollectionElement type of the underlying data. + + Args: + ptr: Pointer to the start of the memory to be sorted. + len: Number of elements from ptr that to be sorted. + """ + + @parameter + fn _cmp_fn(lhs: Scalar[type], rhs: Scalar[type]) -> Bool: + return lhs < rhs + + sort[type, _cmp_fn](ptr, len) + + +fn sort[ + type: CollectionElement, + cmp_fn: fn (Int, Int) capturing -> Bool, +](inout list: List[Int]): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. + + Parameters: + type: CollectionElement type of the underlying data. + cmp_fn: The comparison function. + + Args: + list: Input list to sort. + """ + + sort[Int, cmp_fn](list.data, len(list)) + + +fn sort[ + type: DType, + cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, +](inout list: List[Scalar[type]]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: type: DType of the underlying data. + cmp_fn: The comparison function. Args: - list: Input vector to sort. + list: Input list to sort. """ - var ptr = rebind[Pointer[Scalar[type]]](list.data) - sort[type](ptr, len(list)) + sort[type, cmp_fn](list.data, len(list)) fn sort[ @@ -455,7 +532,40 @@ fn sort[ list: Input list to sort. """ - _quicksort[type, cmp_fn](list.data, len(list)) + sort[type, cmp_fn](list.data, len(list)) + + +fn sort(inout list: List[Int]): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. + + Args: + list: Input integer list to sort. + """ + + @parameter + fn _cmp_fn(lhs: Int, rhs: Int) -> Bool: + return lhs < rhs + + sort[Int, _cmp_fn](list.data, len(list)) + + +fn sort[type: DType](inout list: List[Scalar[type]]): + """Sort the list inplace. + The function doesn't return anything, the list is updated inplace. + + Parameters: + type: DType of the underlying data. + + Args: + list: Input vector to sort. + """ + + @parameter + fn _cmp_fn(lhs: Scalar[type], rhs: Scalar[type]) -> Bool: + return lhs < rhs + + sort[type, _cmp_fn](list.data, len(list)) fn sort[type: ComparableCollectionElement](inout list: List[type]): @@ -469,10 +579,10 @@ fn sort[type: ComparableCollectionElement](inout list: List[type]): """ @parameter - fn _less_than(a: type, b: type) -> Bool: + fn _cmp_fn(a: type, b: type) -> Bool: return a < b - _quicksort[type, _less_than](list.data, len(list)) + sort[type, _cmp_fn](list.data, len(list)) # ===----------------------------------------------------------------------===# @@ -482,19 +592,21 @@ fn sort[type: ComparableCollectionElement](inout list: List[type]): @always_inline fn _sort2[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], offset0: Int, offset1: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], offset0: Int, offset1: Int): var a = array[offset0] var b = array[offset1] - if not cmp_fn[type](a, b): + if not cmp_fn(a, b): array[offset0] = b array[offset1] = a @always_inline fn _sort3[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], offset0: Int, offset1: Int, offset2: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], offset0: Int, offset1: Int, offset2: Int): _sort2[type, cmp_fn](array, offset0, offset1) _sort2[type, cmp_fn](array, offset1, offset2) _sort2[type, cmp_fn](array, offset0, offset1) @@ -502,16 +614,17 @@ fn _sort3[ @always_inline fn _sort_partial_3[ - type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type], offset0: Int, offset1: Int, offset2: Int): + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type], offset0: Int, offset1: Int, offset2: Int): var a = array[offset0] var b = array[offset1] var c = array[offset2] - var r = cmp_fn[type](c, a) + var r = cmp_fn(c, a) var t = c if r else a if r: array[offset2] = a - if cmp_fn[type](b, t): + if cmp_fn(b, t): array[offset0] = b array[offset1] = t elif r: @@ -520,8 +633,10 @@ fn _sort_partial_3[ @always_inline fn _small_sort[ - n: Int, type: AnyTrivialRegType, cmp_fn: _cmp_fn_type -](array: Pointer[type]): + n: Int, + type: CollectionElement, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](array: UnsafePointer[type]): @parameter if n == 2: _sort2[type, cmp_fn](array, 0, 1) diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 87976f67f8..6afbc02227 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -390,7 +390,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): fn comparator(a: CountTuple[V], b: CountTuple[V]) -> Bool: return a < b - sort[type = CountTuple[V], cmp_fn=comparator](items) + sort[CountTuple[V], comparator](items) return items[:n] fn elements(self) -> List[V]: diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index f8a7c17ee2..de5bd553cd 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -16,7 +16,7 @@ from pathlib import Path, _dir_of_current_file from random import random_float64, random_si64, random_ui64, seed from sys import env_get_string, os_is_windows -from builtin.sort import _quicksort, _small_sort +from builtin.sort import _quicksort, _small_sort, _SortWrapper from testing import assert_equal, assert_false, assert_true @@ -77,11 +77,10 @@ fn test_sort_small_3() raises: list.append(2) @parameter - fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) < rebind[Int](rhs) + fn _less_than(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data < rhs.data - var ptr = rebind[Pointer[Int]](list.data) - _small_sort[length, Int, _less_than](ptr) + _small_sort[length, Int, _less_than](list.data) var expected = List[Int](1, 2, 9) for i in range(length): @@ -100,11 +99,10 @@ fn test_sort_small_5() raises: list.append(4) @parameter - fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) < rebind[Int](rhs) + fn _less_than(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data < rhs.data - var ptr = rebind[Pointer[Int]](list.data) - _small_sort[length, Int, _less_than](ptr) + _small_sort[length, Int, _less_than](list.data) var expected = List[Int](1, 2, 3, 4, 9) for i in range(length): @@ -169,23 +167,22 @@ fn test_sort3_dupe_elements() raises: alias length = 3 fn test[ - cmp_fn: fn[type: AnyTrivialRegType] (type, type) capturing -> Bool, + cmp_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing -> Bool, ]() raises: var list = List[Int](capacity=3) list.append(5) list.append(3) list.append(3) - var ptr = rebind[Pointer[Int]](list.data) - _quicksort[Int, cmp_fn](ptr, len(list)) + _quicksort[Int, cmp_fn](list.data, len(list)) var expected = List[Int](3, 3, 5) for i in range(length): assert_equal(expected[i], list[i]) @parameter - fn _lt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) < rebind[Int](rhs) + fn _lt(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data < rhs.data test[_lt]() @@ -308,7 +305,7 @@ fn test_sort_any_103() raises: for i in range(length): list.append(length - i - 1) - sort[DType.float32](list) + sort(list) for i in range(1, length): assert_false(list[i - 1] > list[i]) @@ -325,11 +322,12 @@ fn test_quick_sort_repeated_val() raises: list.append(i + 1) @parameter - fn _greater_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Float32](lhs) > rebind[Float32](rhs) + fn _greater_than( + lhs: _SortWrapper[Float32], rhs: _SortWrapper[Float32] + ) -> Bool: + return lhs.data > rhs.data - var ptr = rebind[Pointer[Float32]](list.data) - _quicksort[Float32, _greater_than](ptr, len(list)) + _quicksort[Float32, _greater_than](list.data, len(list)) var expected = List[Float32]( 9.0, @@ -373,8 +371,10 @@ fn test_quick_sort_repeated_val() raises: assert_equal(expected[i], list[i]) @parameter - fn _less_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Float32](lhs) < rebind[Float32](rhs) + fn _less_than( + lhs: _SortWrapper[Float32], rhs: _SortWrapper[Float32] + ) -> Bool: + return lhs.data < rhs.data expected = List[Float32]( 1.0, @@ -414,8 +414,7 @@ fn test_quick_sort_repeated_val() raises: 9.0, 9.0, ) - var sptr = rebind[Pointer[Float32]](list.data) - _quicksort[Float32, _less_than](sptr, len(list)) + _quicksort[Float32, _less_than](list.data, len(list)) for i in range(0, length): assert_equal(expected[i], list[i]) @@ -427,11 +426,10 @@ fn test_partition_top_k(length: Int, k: Int) raises: list.append(i) @parameter - fn _great_than[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Float32](lhs) > rebind[Float32](rhs) + fn _great_than(lhs: Float32, rhs: Float32) -> Bool: + return lhs > rhs - var ptr = rebind[Pointer[Float32]](list.data) - _ = partition[Float32, _great_than](ptr, k, len(list)) + _ = partition[DType.float32, _great_than](list.data, k, len(list)) for i in range(0, k): assert_false(list[i] < length - k) @@ -445,38 +443,37 @@ fn test_sort_stress() raises: @__copy_capture(random_seed) @parameter fn test[ - cmp_fn: fn[type: AnyTrivialRegType] (type, type) capturing -> Bool, - check_fn: fn[type: AnyTrivialRegType] (type, type) capturing -> Bool, + cmp_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing -> Bool, + check_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing -> Bool, ](length: Int) raises: var list = List[Int](capacity=length) for _ in range(length): list.append(int(random_si64(-length, length))) - var ptr = rebind[Pointer[Int]](list.data) - _quicksort[Int, cmp_fn](ptr, len(list)) + _quicksort[Int, cmp_fn](list.data, len(list)) for i in range(length - 1): - assert_true(check_fn[Int](list[i], list[i + 1])) + assert_true(check_fn(list[i], list[i + 1])) @parameter @always_inline - fn _gt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) > rebind[Int](rhs) + fn _gt(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data > rhs.data @parameter @always_inline - fn _geq[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) >= rebind[Int](rhs) + fn _geq(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data >= rhs.data @parameter @always_inline - fn _lt[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) < rebind[Int](rhs) + fn _lt(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data < rhs.data @parameter @always_inline - fn _leq[type: AnyTrivialRegType](lhs: type, rhs: type) -> Bool: - return rebind[Int](lhs) <= rebind[Int](rhs) + fn _leq(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: + return lhs.data <= rhs.data for i in range(len(lens)): var length = lens[i] diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index d0333e87c4..786a3b62cd 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -17,7 +17,7 @@ from random import rand fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: - var p = Pointer[SIMD[D, 1]].alloc(size) + var p = UnsafePointer[SIMD[D, 1]].alloc(size) rand[D](p, size) sort[D](p, size) for i in range(1, size - 1): From 8dd5c4d25f3af3544fce199eaed8d8544edfeff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20Sz=C5=B1cs?= Date: Wed, 10 Jul 2024 14:11:46 -0500 Subject: [PATCH 1197/2019] [External] [stdlib] Implement equality comparisons for `List` (#43193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Implement equality comparisons for `List` Implement equality comparison operators for `List` when its underlying elements are themselves `EqualityComparable`. Co-authored-by: Krisztián Szűcs Closes modularml/mojo#3195 MODULAR_ORIG_COMMIT_REV_ID: bc5651d21ad20442b8e24f85270f9176ba252fae --- docs/changelog.md | 5 +++ stdlib/src/collections/list.mojo | 58 ++++++++++++++++++++++++++ stdlib/test/collections/test_list.mojo | 23 ++++++++++ 3 files changed, 86 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 1b63d5a2e4..4d661f5b2a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,11 @@ what we publish. ### ⭐️ New +- `List[T]` values are now equality comparable with `==` and `!=` when `T` is + equality comparable. + ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by + [@kszucs](https://github.com/kszucs)) + - `__setitem__` now works with variadic argument lists such as: ```mojo diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 31a7f7bafb..1cbe1de365 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -197,6 +197,64 @@ struct List[T: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# + @always_inline + fn __eq__[ + U: EqualityComparableCollectionElement + ](self: List[U], other: List[U]) -> Bool: + """Checks if two lists are equal. + + Examples: + ```mojo + var x = List[Int](1, 2, 3) + var y = List[Int](1, 2, 3) + if x == y: print("x and y are equal") + ``` + + Parameters: + U: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + other: The list to compare with. + + Returns: + True if the lists are equal, False otherwise. + """ + if len(self) != len(other): + return False + var index = 0 + for element in self: + if element[] != other[index]: + return False + index += 1 + return True + + @always_inline + fn __ne__[ + U: EqualityComparableCollectionElement + ](self: List[U], other: List[U]) -> Bool: + """Checks if two lists are not equal. + + Examples: + + ```mojo + var x = List[Int](1, 2, 3) + var y = List[Int](1, 2, 4) + if x != y: print("x and y are not equal") + ``` + + Parameters: + U: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + other: The list to compare with. + + Returns: + True if the lists are not equal, False otherwise. + """ + return not (self == other) + fn __contains__[ U: EqualityComparableCollectionElement ](self: List[U], value: U) -> Bool: diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index b8e3b02f56..2f313906bc 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -816,6 +816,29 @@ def test_list_contains(): # assert_equal(List(0,1) in y,False) +def test_list_eq_ne(): + var l1 = List[Int](1, 2, 3) + var l2 = List[Int](1, 2, 3) + assert_true(l1 == l2) + assert_false(l1 != l2) + + var l3 = List[Int](1, 2, 3, 4) + assert_false(l1 == l3) + assert_true(l1 != l3) + + var l4 = List[Int]() + var l5 = List[Int]() + assert_true(l4 == l5) + assert_true(l1 != l4) + + var l6 = List[String]("a", "b", "c") + var l7 = List[String]("a", "b", "c") + var l8 = List[String]("a", "b") + assert_true(l6 == l7) + assert_false(l6 != l7) + assert_false(l6 == l8) + + def test_list_init_span(): var l = List[String]("a", "bb", "cc", "def") var sp = Span(l) From 3f85a20e45a4c1b4e46550cab049874c314f2f8a Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 10 Jul 2024 12:15:20 -0700 Subject: [PATCH 1198/2019] [Docs] Debugging updates. - Adds brief sections on the Variables and Call Stack sections of the Run & Debug view. - Adds instructions for starting a debug session from the command line (including attaching to a running process). - Adds a "Tips & tricks" section that mentions the `breakpoint()` built-in and using the `param_env` module to turn debug code on/off using the `-D` command-line option. MODULAR_ORIG_COMMIT_REV_ID: d7bbeca575cbff1be5d0f186aeba26216c22c2b9 --- docs/tools/debugging.ipynb | 283 ++++++++++++++++-- .../images/debugger-call-stack-nested1.png | Bin 0 -> 105875 bytes docs/tools/images/debugger-variables.png | Bin 0 -> 71527 bytes 3 files changed, 252 insertions(+), 31 deletions(-) create mode 100644 docs/tools/images/debugger-call-stack-nested1.png create mode 100644 docs/tools/images/debugger-variables.png diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 216f5cb11e..b5bd109810 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -31,8 +31,9 @@ "\n", "The Mojo SDK includes the [LLDB debugger](https://lldb.llvm.org/) and a Mojo\n", "LLDB plugin. Together these provide the low-level debugging interface for the\n", - "Mojo extension. You can also use `mojo debug` to start a command-line debugging\n", - "session using LLDB.\n", + "Mojo extension. You can also use the `mojo debug` command to start a \n", + "command-line debugging session using LLDB or to launch a Mojo debugging session\n", + "in VS Code.\n", "\n", "## Start debugging\n", "\n", @@ -103,8 +104,13 @@ "menu to select debug configurations. It also has areas to display current\n", "variables, watch expressions, the current call stack, and breakpoints.\n", "\n", + "
    \n", + "\n", "![](images/run-and-debug-view.png)\n", "\n", + "
    Figure 1. Run and Debug view
    \n", + "
    \n", + "\n", "To open **Run and Debug** view, click the **Run and Debug** icon in the\n", "**Activity Bar** (on the left side of the VS Code window) or press\n", "Control+Shift+D \n", @@ -115,14 +121,24 @@ "If you haven't created any launch configurations in the current project,\n", "VS Code shows the **Run start view**.\n", "\n", + "
    \n", + "\n", "![](images/run-start-view.png)\n", "\n", + "
    Figure 2. Run start view
    \n", + "
    \n", + "\n", "If you've already launched a debug session or created a `launch.json` file to\n", "define launch configurations, you'll see the **Launch configurations** menu,\n", "which lets you choose configurations and start debug sessions:\n", "\n", + "
    \n", + "\n", "![](images/launch-configuration-menu.png)\n", "\n", + "
    Figure 3. Launch configurations menu
    \n", + "
    \n", + "\n", "### Other ways to start a debug session\n", "\n", "There are a number of other ways to start a debug session.\n", @@ -139,7 +155,7 @@ "same debug configurations described in [Quick run or\n", "debug](#quick-run-or-debug).\n", "\n", - "#### Launching from the File Explorer\n", + "#### Launch from the File Explorer\n", "\n", "To launch a debug session from the the **File Explorer** view:\n", "\n", @@ -163,28 +179,79 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Edit launch configurations\n", + "## Starting the debugger from the command line\n", "\n", - "To edit launch configurations:\n", + "Use the `mojo debug` command to start a debug session from the command line. You\n", + "can choose from two debugging interfaces:\n", "\n", - "1. If the **Run and Debug** view isn't already open, click the **Run and\n", - "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", - "or press Control+Shift+D (Command+Shift+D on macOS).\n", + "- With the `--rpc` flag, `mojo debug` starts an RPC debug session in an external\n", + " editor with an active RPC debug server. If you have VS Code running, this\n", + " starts a debug session in VS Code. \n", "\n", - " ![](images/run-and-debug-icon.png)\n", - " \n", - "1. Create or open the `launch.json` file:\n", - " 1. If you see the **Run start view**, click **create a launch.json file**.\n", - " 1. If you already have launch configurations set up, click the gear icon\n", - " next to the **Launch configurations** menu.\n", - " ![](images/launch-configuration-menu.png)\n", - "1. Select **Mojo** from the list of debuggers.\n", + "- Without the `--rpc` flag, `mojo debug` starts a command-line [LLDB \n", + " debugger](https://lldb.llvm.org/) session.\n", "\n", - "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", - "some common debug actions. Click **Add configuration** to add a new\n", - "configuration template. \n", + "You can choose to build and debug a Mojo file, run and debug a compiled binary,\n", + "or to attach the debugger to a running process.\n", + "\n", + ":::note Environment variables\n", + "\n", + "When you debug a program from the command line using `--rpc`, the program runs\n", + "with the environment variables set in the terminal. When launching from inside\n", + "VS Code, the environment is defined by the VS Code [launch \n", + "configuration](#launch-configurations).\n", + "\n", + ":::\n", + "\n", + "For a full list of command-line options, see the [`mojo debug` reference\n", + "page](/mojo/cli/debug).\n", + "\n", + "### Start a debug session from the command line\n", + "\n", + "With VS Code open, run the following command (either from VS Code's integrated\n", + "terminal or an external shell):\n", + "\n", + "```bash\n", + "mojo debug --rpc myproject.mojo\n", + "```\n", + "\n", + "Or to debug a compiled binary:\n", "\n", - "### Mojo launch configurations\n", + "```bash\n", + "mojo debug --rpc myproject\n", + "```\n", + "\n", + "\n", + "For best results, build with the `-O0 -g` command-line options when you build a\n", + "binary that you intend to debug—this produces a binary with full debug info.\n", + "(When you call `mojo debug` on a Mojo source file, it includes debug\n", + "information by default.) See the [`mojo build` reference page](/mojo/cli/build)\n", + "for details on compilation options.\n", + "\n", + "### Attach the debugger to a running process from the command line\n", + "\n", + "You can also attach the debugger to a running process by specifying either the\n", + "process ID or process name on the command line:\n", + "\n", + "```bash\n", + "mojo debug --rpc --pid \n", + "```\n", + "\n", + "Or:\n", + "\n", + "```bash\n", + "mojo debug --rpc --process-name \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Launch configurations\n", + "\n", + "VS Code _launch configurations_ let you define setup information for debugging\n", + "your applications.\n", "\n", "The Mojo debugger provides the following launch configuration templates:\n", "\n", @@ -220,22 +287,59 @@ "- `description`. A longer description of the configuration, not shown in the UI.\n", "- `env`. Environment variables to be set before running the program.\n", "- `mojoFile`. Path to a Mojo file to launch and debug.\n", - "- `program`. Path to a compiled binary to launch and debug.\n", + "- `pid`. Process ID of the running process to attach to.\n", + "- `program`. Path to a compiled binary to launch and debug, or the \n", + " program to attach to.\n", "- `runInTerminal`. True to run the program with a dedicated terminal, which\n", " allows the program to receive standard input from the terminal. False to run\n", " the program with its output directed to the **Debug Console**.\n", "\n", - "Mojo `launch` configurations must include either the `mojoFile` or `program`\n", - "attribute.\n", + "If configuration is a `launch` request, the configuration must include either\n", + "the `mojoFile` or `program` attribute.\n", + "\n", + "For `attach` requests, the configuration must include either the `pid` or \n", + "`program` attribute.\n", "\n", "VS Code performs variable substitution on the launch configurations. You can\n", "use `${workspaceFolder}` to substitute the path to the current workspace, and\n", "`${file}` to represent the file in the active editor tab. For a complete list\n", - "of variables, see the [Variables\n", + "of variables, see the VS Code [Variables\n", "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", "\n", - "Note that for launch configurations that launch a Mojo file, there is currently\n", - "no way to specify compilation options.\n" + "For more information, see the VS Code documentation for [Launch \n", + "configurations]](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", + "\n", + ":::note Compilation options\n", + "\n", + "Mojo launch configurations don't allow you to specify compilation options. If\n", + "you need to specify compilation options, you can build the binary using [`mojo\n", + "build`](/mojo/cli/build), then use a launch configuration with the `program`\n", + "option to launch the compiled binary. Or if you [start the debugger from the\n", + "command line](#starting-the-debugger-from-the-command-line), you can pass\n", + "compilation options to the `mojo debug` command.\n", + "\n", + ":::\n", + "\n", + "### Edit launch configurations\n", + "\n", + "To edit launch configurations:\n", + "\n", + "1. If the **Run and Debug** view isn't already open, click the **Run and\n", + "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", + "or press Control+Shift+D (Command+Shift+D on macOS).\n", + "\n", + " ![](images/run-and-debug-icon.png)\n", + " \n", + "1. Create or open the `launch.json` file:\n", + " 1. If you see the **Run start view**, click **create a launch.json file**.\n", + " 1. If you already have launch configurations set up, click the gear icon\n", + " next to the **Launch configurations** menu.\n", + " ![](images/launch-configuration-menu.png)\n", + "1. Select **Mojo** from the list of debuggers.\n", + "\n", + "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", + "some common debug actions. Click **Add configuration** to add a new\n", + "configuration template. " ] }, { @@ -302,16 +406,22 @@ "- **Log Message**. Add a logpoint (supported)\n", "- **Wait for Breakpoint**. Add a triggered breakpoint (supported).\n", "\n", - "#### Hit counts\n", + "#### Set a hit count breakpoint\n", "\n", "A hit count breakpoint is a breakpoint that only breaks execution after the\n", "debugger hits it a specified number of times.\n", "\n", - "To set a hit count for a breakpoint:\n", + "To add a hit count breakpoint:\n", + "\n", + "1. Right click in the left gutter of the editor where you want to place the \n", + " breakpoint, and select **Add Conditional Breakpoint.**\n", + "2. Select **Hit Count** from the menu and enter the desired hit count.\n", "\n", - "- Right click on the breakpoint in the left gutter of the editor and select\n", + "To change an existing breakpoint to a hit count breakpoint:\n", + "\n", + "1. Right click on the breakpoint in the left gutter of the editor and select\n", " **Edit breakpoint**.\n", - "- Select **Hit Count** from the menu and enter the desired hit count.\n", + "2. Select **Hit Count** from the menu and enter the desired hit count.\n", "\n", "You can also edit a breakpoint from the **Breakpoints** section of the **Run and\n", "Debug** view:\n", @@ -326,7 +436,51 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Using the Debug Console\n", + "### View local variables\n", + "\n", + "When a program is paused in the debugger, the editor shows local variable values\n", + "inline. You can also find them in the **Variables** section of the **Run and\n", + "Debug** view.\n", + "\n", + "
    \n", + "\n", + "![VS Code window showing a program paused in the debugger, with the variables sections of the Run and Debug view visible. The edit shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2.](images/debugger-variables.png)\n", + "\n", + "
    Figure 4. Local variable values displayed in the debugger
    \n", + "
    \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View the call stack\n", + "\n", + "When a program is paused in the debugger, the **Run and Debug** view shows the\n", + "current call stack. (You may see multiple call stacks, one for each active\n", + "thread in the program.)\n", + "\n", + "
    \n", + "\n", + "![VS Code window showing a program paused in the debugger, with the call stack and variables sections of the Run and Debug view visible. The call stack shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2; the parent function nested1 is selected in the call stack, and editor highlights the current line in nested1 (the call to nested2()).](images/debugger-call-stack-nested1.png)\n", + "\n", + "
    Figure 5. Call stack in Run and Debug view
    \n", + "
    \n", + "\n", + "The **Call Stack** section of the Run and Debug view shows a stack frame for\n", + "each function call in the current call stack. Clicking on the name of the\n", + "function highlights the current line in that function. For example, in Figure\n", + "5, the program is paused at a breakpoint in `nested2()`, but the parent\n", + "function, `nested1()` is selected in the call stack. The editor highlights the\n", + "current line in `nested1()` (that is, the call to `nested2()`) and shows the\n", + "current local variable values for `nested1()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use the Debug Console\n", "\n", "The **Debug Console** gives you a command-line interface to the debugger. The \n", "**Debug Console** processes LLDB commands and Mojo expressions.\n", @@ -348,6 +502,73 @@ "\n", ":::" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tips and tricks\n", + "\n", + " There are several features in the standard library that aren't directly related\n", + " to the debugger, but which can help you debug your programs. These include:\n", + "\n", + " * Programmatic breakpoints.\n", + " * Setting parameters from the Mojo command line.\n", + "\n", + " ### Set a programmatic breakpoint\n", + "\n", + " To break at a specific point in your code, you can use the built-in\n", + " [`breakpoint()`](/mojo/stdlib/builtin/breakpoint/breakpoint) function:\n", + "\n", + " ```mojo\n", + "if some_value.is_valid():\n", + " do_the_right_thing()\n", + "else:\n", + " # We should never get here!\n", + " breakpoint()\n", + "```\n", + "\n", + "If you have VS Code open and run this code in debug mode (either using VS Code\n", + "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", + "triggers the debugger.\n", + "\n", + "::: note Assertions\n", + "\n", + "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", + "ways to specify assertions. Assertions also trigger an error, so can open the \n", + "debugger in the same way that a `breakpoint()` call will.\n", + "\n", + ":::\n", + "\n", + "### Set parameters from the Mojo command line\n", + "\n", + "You can use the [`param_env`](/mojo/stdlib/sys/param_env/) module to retrieve\n", + "parameter values specified on the Mojo command line. Among other things, this\n", + "is an easy way to switch debugging logic on and off. For example:\n", + "\n", + "```mojo\n", + "from param_env import is_defined\n", + "\n", + "def some_function_with_issues():\n", + " # ...\n", + " @parameter\n", + " if is_defined[\"DEBUG_ME\"]():\n", + " breakpoint()\n", + "```\n", + "\n", + "To activate this code, use the [`-D` command-line\n", + "option](/mojo/cli/debug#compilation-options) to define `DEBUG_ME`:\n", + "\n", + "```bash\n", + "mojo debug -D DEBUG_ME main.mojo\n", + "```\n", + "\n", + "The `is_defined()` function returns a compile-time true or false value based on\n", + "whether the specified name is defined. Since the `breakpoint()` call is inside a\n", + "[parametric `if` statement](/mojo/manual/decorators/parameter#parametric-if-statement),\n", + "it is only included in the compiled code when the `DEBUG_ME` name is defined on\n", + "the command line." + ] } ], "metadata": { diff --git a/docs/tools/images/debugger-call-stack-nested1.png b/docs/tools/images/debugger-call-stack-nested1.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf33e7dfdeacf7e791c8a2d76fe2e00287b387c GIT binary patch literal 105875 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 Date: Wed, 10 Jul 2024 16:21:49 -0500 Subject: [PATCH 1199/2019] [External] [stdlib] Add is_valid_utf8 and test (#43197) Verify that the bytes are valid UTF-8. This algorithm is good enough since it will be decently fast for languages with many ASCII characters, in case a faster algorithm is desired, there are projects like https://github.com/cyb70289/utf8 . Some benchmarking would be needed to assess the actual effects though. Moved the `_utf8_byte_type` implementation from `builtin.string` to `utils.string_slice` and refactored it to be usable with any size of SIMD vector ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3196 MODULAR_ORIG_COMMIT_REV_ID: b83df9d59689f830d447cd924aa4eeedeabc0511 --- stdlib/src/builtin/string.mojo | 23 +-- stdlib/src/utils/string_slice.mojo | 172 ++++++++++++++++++++++- stdlib/test/utils/test_string_slice.mojo | 85 +++++++++++ 3 files changed, 261 insertions(+), 19 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 4a28061cf2..faab8046f9 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -25,6 +25,7 @@ from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy from utils import Span, StaticIntTuple, StringRef, StringSlice from utils._format import Formattable, Formatter, ToFormatter +from utils.string_slice import _utf8_byte_type # ===----------------------------------------------------------------------=== # # ord @@ -705,20 +706,6 @@ fn isprintable(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # -fn _utf8_byte_type(b: UInt8) -> UInt8: - """UTF-8 byte type. - - Returns: - The byte type: - 0 -> ASCII byte. - 1 -> continuation byte. - 2 -> start of 2 byte long sequence. - 3 -> start of 3 byte long sequence. - 4 -> start of 4 byte long sequence. - """ - return count_leading_zeros(~(b & 0b1111_0000)) - - @value struct _StringIter[ is_mutable: Bool, //, @@ -746,7 +733,7 @@ struct _StringIter[ self.length = length self.continuation_bytes = 0 for i in range(length): - if _utf8_byte_type(int(unsafe_pointer[i])) == 1: + if _utf8_byte_type(unsafe_pointer[i]) == 1: self.continuation_bytes += 1 fn __iter__(self) -> Self: @@ -757,7 +744,7 @@ struct _StringIter[ if forward: var byte_len = 1 if self.continuation_bytes > 0: - var byte_type = _utf8_byte_type(int(self.ptr[self.index])) + var byte_type = _utf8_byte_type(self.ptr[self.index]) if byte_type != 0: byte_len = int(byte_type) self.continuation_bytes -= byte_len - 1 @@ -769,11 +756,11 @@ struct _StringIter[ else: var byte_len = 1 if self.continuation_bytes > 0: - var byte_type = _utf8_byte_type(int(self.ptr[self.index - 1])) + var byte_type = _utf8_byte_type(self.ptr[self.index - 1]) if byte_type != 0: while byte_type == 1: byte_len += 1 - var b = int(self.ptr[self.index - byte_len]) + var b = self.ptr[self.index - byte_len] byte_type = _utf8_byte_type(b) self.continuation_bytes -= byte_len - 1 self.index -= byte_len diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 87f1f3bebb..e197b0d9c0 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -20,13 +20,178 @@ from utils import StringSlice ``` """ +from bit import count_leading_zeros from utils import Span -from builtin.string import _isspace, _utf8_byte_type +from builtin.string import _isspace alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" +fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): + """UTF-8 byte type. + + Returns: + The byte type. + + Notes: + + - 0 -> ASCII byte. + - 1 -> continuation byte. + - 2 -> start of 2 byte long sequence. + - 3 -> start of 3 byte long sequence. + - 4 -> start of 4 byte long sequence. + """ + return count_leading_zeros(~(b & UInt8(0b1111_0000))) + + +fn _validate_utf8_simd_slice[ + width: Int, remainder: Bool = False +](ptr: DTypePointer[DType.uint8], length: Int, owned iter_len: Int) -> Int: + """Internal method to validate utf8, use _is_valid_utf8. + + Parameters: + width: The width of the SIMD vector to build for validation. + remainder: Whether it is computing the remainder that doesn't fit in the + SIMD vector. + + Args: + ptr: Pointer to the data. + length: The length of the items in the pointer. + iter_len: The amount of items to still iterate through. + + Returns: + The new amount of items to iterate through that don't fit in the + specified width of SIMD vector. If -1 then it is invalid. + """ + # TODO: implement a faster algorithm like https://github.com/cyb70289/utf8 + # and benchmark the difference. + var idx = length - iter_len + while iter_len >= width or remainder: + var d: SIMD[DType.uint8, width] # use a vector of the specified width + + @parameter + if not remainder: + d = ptr.offset(idx).simd_strided_load[width](1) + else: + debug_assert(iter_len > -1, "iter_len must be > -1") + d = SIMD[DType.uint8, width](0) + for i in range(iter_len): + d[i] = ptr[idx + i] + + var is_ascii = d < 0b1000_0000 + if is_ascii.reduce_and(): # skip all ASCII bytes + + @parameter + if not remainder: + idx += width + iter_len -= width + continue + else: + return 0 + elif is_ascii[0]: + for i in range(1, width): + if is_ascii[i]: + continue + idx += i + iter_len -= i + break + continue + + var byte_types = _utf8_byte_type(d) + var first_byte_type = byte_types[0] + + # byte_type has to match against the amount of continuation bytes + alias Vec = SIMD[DType.uint8, 4] + alias n4_byte_types = Vec(4, 1, 1, 1) + alias n3_byte_types = Vec(3, 1, 1, 0) + alias n3_mask = Vec(0b111, 0b111, 0b111, 0) + alias n2_byte_types = Vec(2, 1, 0, 0) + alias n2_mask = Vec(0b111, 0b111, 0, 0) + var byte_types_4 = byte_types.slice[4]() + var valid_n4 = (byte_types_4 == n4_byte_types).reduce_and() + var valid_n3 = ((byte_types_4 & n3_mask) == n3_byte_types).reduce_and() + var valid_n2 = ((byte_types_4 & n2_mask) == n2_byte_types).reduce_and() + if not (valid_n4 or valid_n3 or valid_n2): + return -1 + + # special unicode ranges + var b0 = d[0] + var b1 = d[1] + if first_byte_type == 2 and b0 < UInt8(0b1100_0010): + return -1 + elif b0 == 0xE0 and not (UInt8(0xA0) <= b1 <= UInt8(0xBF)): + return -1 + elif b0 == 0xED and not (UInt8(0x80) <= b1 <= UInt8(0x9F)): + return -1 + elif b0 == 0xF0 and not (UInt8(0x90) <= b1 <= UInt8(0xBF)): + return -1 + elif b0 == 0xF4 and not (UInt8(0x80) <= b1 <= UInt8(0x8F)): + return -1 + + # amount of bytes evaluated + idx += int(first_byte_type) + iter_len -= int(first_byte_type) + + @parameter + if remainder: + break + return iter_len + + +fn _is_valid_utf8(data: UnsafePointer[UInt8], length: Int) -> Bool: + """Verify that the bytes are valid UTF-8. + + Args: + data: The pointer to the data. + length: The length of the items pointed to. + + Returns: + Whether the data is valid UTF-8. + + #### UTF-8 coding format + [Table 3-7 page 94](http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf). + Well-Formed UTF-8 Byte Sequences + + Code Points | First Byte | Second Byte | Third Byte | Fourth Byte | + :---------- | :--------- | :---------- | :--------- | :---------- | + U+0000..U+007F | 00..7F | | | | + U+0080..U+07FF | C2..DF | 80..BF | | | + U+0800..U+0FFF | E0 | ***A0***..BF| 80..BF | | + U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | | + U+D000..U+D7FF | ED | 80..***9F***| 80..BF | | + U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | | + U+10000..U+3FFFF | F0 | ***90***..BF| 80..BF | 80..BF | + U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | + U+100000..U+10FFFF | F4 | 80..***8F***| 80..BF | 80..BF | + . + """ + + var ptr = DTypePointer(data) + var iter_len = length + if iter_len >= 64 and simdwidthof[DType.uint8]() >= 64: + iter_len = _validate_utf8_simd_slice[64](ptr, length, iter_len) + if iter_len < 0: + return False + if iter_len >= 32 and simdwidthof[DType.uint8]() >= 32: + iter_len = _validate_utf8_simd_slice[32](ptr, length, iter_len) + if iter_len < 0: + return False + if iter_len >= 16 and simdwidthof[DType.uint8]() >= 16: + iter_len = _validate_utf8_simd_slice[16](ptr, length, iter_len) + if iter_len < 0: + return False + if iter_len >= 8: + iter_len = _validate_utf8_simd_slice[8](ptr, length, iter_len) + if iter_len < 0: + return False + if iter_len >= 4: + iter_len = _validate_utf8_simd_slice[4](ptr, length, iter_len) + if iter_len < 0: + return False + return _validate_utf8_simd_slice[4, True](ptr, length, iter_len) == 0 + + struct StringSlice[ is_mutable: Bool, //, lifetime: AnyLifetime[is_mutable].type, @@ -68,6 +233,11 @@ struct StringSlice[ # StringLiteral is guaranteed to use UTF-8 encoding. # FIXME(MSTDL-160): # Ensure StringLiteral _actually_ always uses UTF-8 encoding. + # TODO(#933): use when llvm intrinsics can be used at compile time + # debug_assert( + # _is_valid_utf8(literal.unsafe_ptr(), literal._byte_length()), + # "StringLiteral doesn't have valid UTF-8 encoding", + # ) self = StringSlice[lifetime]( unsafe_from_utf8_ptr=literal.unsafe_ptr(), len=literal.byte_length() ) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 39742474f2..7e38a784d3 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -15,6 +15,7 @@ from testing import assert_equal, assert_true, assert_false from utils import Span +from utils.string_slice import _is_valid_utf8 fn test_string_literal_byte_slice() raises: @@ -175,6 +176,89 @@ fn test_slice_bool() raises: assert_true(not str2.as_string_slice().__bool__()) +fn test_utf8_validation() raises: + var text = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam + varius tellus quis tincidunt dictum. Donec eros orci, ultricies ac metus non + , rutrum faucibus neque. Nunc ultricies turpis ut lacus consequat dapibus. + Nulla nec risus a purus volutpat blandit. Donec sit amet massa velit. Aenean + fermentum libero eu pharetra placerat. Sed id molestie tellus. Fusce + sollicitudin a purus ac placerat. + Lorem Ipsum,也称乱数假文或者哑元文本, 是印刷及排版领域所常用的虚拟文字 + 由于曾经一台匿名的打印机刻意打乱了一盒印刷字体从而造出一本字体样品书,Lorem + Ipsum从西元15世纪起就被作为此领域的标准文本使用。它不仅延续了五个世纪, + 还通过了电子排版的挑战,其雏形却依然保存至今。在1960年代,”Leatraset”公司发布了印刷着 + Lorem Ipsum段落的纸张,从而广泛普及了它的使用。最近,计算机桌面出版软件 + למה אנו משתמשים בזה? + זוהי עובדה מבוססת שדעתו של הקורא תהיה מוסחת על ידי טקטס קריא כאשר הוא יביט בפריסתו. המטרה בשימוש + ב- Lorem Ipsum הוא שיש לו פחות או יותר תפוצה של אותיות, בניגוד למלל ' יסוי + יסוי יסוי', ונותן חזות קריאה יותר.הרבה הוצאות מחשבים ועורכי דפי אינטרנט משתמשים כיום ב- + Lorem Ipsum כטקסט ברירת המחדל שלהם, וחיפוש של 'lorem ipsum' יחשוף אתרים רבים בראשית + דרכם.גרסאות רבות נוצרו במהלך השנים, לעתים בשגגה + Lorem Ipsum е едноставен модел на текст кој се користел во печатарската + индустрија. + Lorem Ipsum - це текст-"риба", що використовується в друкарстві та дизайні. + Lorem Ipsum คือ เนื้อหาจำลองแบบเรียบๆ ที่ใช้กันในธุรกิจงานพิมพ์หรืองานเรียงพิมพ์ + มันได้กลายมาเป็นเนื้อหาจำลองมาตรฐานของธุรกิจดังกล่าวมาตั้งแต่ศตวรรษที่ + Lorem ipsum" في أي محرك بحث ستظهر العديد + من المواقع الحديثة العهد في نتائج البحث. على مدى السنين + ظهرت نسخ جديدة ومختلفة من نص لوريم إيبسوم، أحياناً عن طريق + الصدفة، وأحياناً عن عمد كإدخال بعض العبارات الفكاهية إليها. + """ + assert_true(_is_valid_utf8(text.unsafe_ptr(), text._byte_length())) + assert_true(_is_valid_utf8(text.unsafe_ptr(), text._byte_length())) + + var positive = List[List[UInt8]]( + List[UInt8](0x0), + List[UInt8](0x00), + List[UInt8](0x66), + List[UInt8](0x7F), + List[UInt8](0x00, 0x7F), + List[UInt8](0x7F, 0x00), + List[UInt8](0xC2, 0x80), + List[UInt8](0xDF, 0xBF), + List[UInt8](0xE0, 0xA0, 0x80), + List[UInt8](0xE0, 0xA0, 0xBF), + List[UInt8](0xED, 0x9F, 0x80), + List[UInt8](0xEF, 0x80, 0xBF), + List[UInt8](0xF0, 0x90, 0xBF, 0x80), + List[UInt8](0xF2, 0x81, 0xBE, 0x99), + List[UInt8](0xF4, 0x8F, 0x88, 0xAA), + ) + for item in positive: + assert_true(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) + assert_true(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) + var negative = List[List[UInt8]]( + List[UInt8](0x80), + List[UInt8](0xBF), + List[UInt8](0xC0, 0x80), + List[UInt8](0xC1, 0x00), + List[UInt8](0xC2, 0x7F), + List[UInt8](0xDF, 0xC0), + List[UInt8](0xE0, 0x9F, 0x80), + List[UInt8](0xE0, 0xC2, 0x80), + List[UInt8](0xED, 0xA0, 0x80), + List[UInt8](0xED, 0x7F, 0x80), + List[UInt8](0xEF, 0x80, 0x00), + List[UInt8](0xF0, 0x8F, 0x80, 0x80), + List[UInt8](0xF0, 0xEE, 0x80, 0x80), + List[UInt8](0xF2, 0x90, 0x91, 0x7F), + List[UInt8](0xF4, 0x90, 0x88, 0xAA), + List[UInt8](0xF4, 0x00, 0xBF, 0xBF), + List[UInt8]( + 0xC2, 0x80, 0x00, 0x00, 0xE1, 0x80, 0x80, 0x00, 0xC2, 0xC2, 0x80 + ), + List[UInt8](0x00, 0xC2, 0xC2, 0x80, 0x00, 0x00, 0xE1, 0x80, 0x80), + List[UInt8](0x00, 0x00, 0x00, 0xF1, 0x80, 0x00), + List[UInt8](0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1), + List[UInt8](0x00, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x80, 0x80), + List[UInt8](0x00, 0x00, 0xF1, 0x80, 0xC2, 0x80, 0x00), + List[UInt8](0x00, 0x00, 0xF0, 0x80, 0x80, 0x80), + ) + for item in negative: + assert_false(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) + assert_false(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) + + fn main() raises: test_string_literal_byte_slice() test_string_byte_slice() @@ -182,3 +266,4 @@ fn main() raises: test_slice_len() test_slice_eq() test_slice_bool() + test_utf8_validation() From a36161384f37982ad3505db2586d98d6e16866ea Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 10 Jul 2024 16:25:19 -0500 Subject: [PATCH 1200/2019] [stdlib] Apply infer-only to collections Apply infer-only to parameters that will always be inferred in a variety of places in collections. Note: - There are many more places that would benefit from this in the stdlib, and we can progressively apply it wider in follow-up PRs. MODULAR_ORIG_COMMIT_REV_ID: adb01edb97c39d9019616a67619a519649a4d201 --- stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/inline_list.mojo | 4 ++-- stdlib/src/collections/list.mojo | 16 ++++++++-------- stdlib/src/collections/optional.mojo | 10 +++++++--- stdlib/src/utils/span.mojo | 10 +++++----- stdlib/src/utils/static_tuple.mojo | 2 +- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index aa535d999f..b5b9a18478 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -638,7 +638,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( @no_inline fn __str__[ - T: RepresentableKeyElement, U: RepresentableCollectionElement + T: RepresentableKeyElement, U: RepresentableCollectionElement, // ](self: Dict[T, U]) -> String: """Returns a string representation of a `Dict`. diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index b14e2341aa..2e29256b27 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -181,7 +181,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): return _InlineListIter(0, self) fn __contains__[ - C: EqualityComparableCollectionElement + C: EqualityComparableCollectionElement, // ](self: Self, value: C) -> Bool: """Verify if a given value is present in the list. @@ -213,7 +213,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # ===-------------------------------------------------------------------===# fn count[ - C: EqualityComparableCollectionElement + C: EqualityComparableCollectionElement, // ](self: Self, value: C) -> Int: """Counts the number of occurrences of a value in the list. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1cbe1de365..07ee7a3729 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -199,7 +199,7 @@ struct List[T: CollectionElement]( @always_inline fn __eq__[ - U: EqualityComparableCollectionElement + U: EqualityComparableCollectionElement, // ](self: List[U], other: List[U]) -> Bool: """Checks if two lists are equal. @@ -231,7 +231,7 @@ struct List[T: CollectionElement]( @always_inline fn __ne__[ - U: EqualityComparableCollectionElement + U: EqualityComparableCollectionElement, // ](self: List[U], other: List[U]) -> Bool: """Checks if two lists are not equal. @@ -256,7 +256,7 @@ struct List[T: CollectionElement]( return not (self == other) fn __contains__[ - U: EqualityComparableCollectionElement + U: EqualityComparableCollectionElement, // ](self: List[U], value: U) -> Bool: """Verify if a given value is present in the list. @@ -363,7 +363,7 @@ struct List[T: CollectionElement]( return len(self) > 0 @no_inline - fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: + fn __str__[U: RepresentableCollectionElement, //](self: List[U]) -> String: """Returns a string representation of a `List`. Note that since we can't condition methods on a trait yet, @@ -393,7 +393,7 @@ struct List[T: CollectionElement]( @no_inline fn format_to[ - U: RepresentableCollectionElement + U: RepresentableCollectionElement, // ](self: List[U], inout writer: Formatter): """Write `my_list.__str__()` to a `Formatter`. @@ -411,7 +411,7 @@ struct List[T: CollectionElement]( writer.write("]") @no_inline - fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: + fn __repr__[U: RepresentableCollectionElement, //](self: List[U]) -> String: """Returns a string representation of a `List`. Note that since we can't condition methods on a trait yet, @@ -649,7 +649,7 @@ struct List[T: CollectionElement]( # TODO: Remove explicit self type when issue 1876 is resolved. fn index[ - C: EqualityComparableCollectionElement + C: EqualityComparableCollectionElement, // ]( ref [_]self: List[C], value: C, @@ -842,7 +842,7 @@ struct List[T: CollectionElement]( (self.data + idx).init_pointee_move(value^) fn count[ - T: EqualityComparableCollectionElement + T: EqualityComparableCollectionElement, // ](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. Note that since we can't condition methods on a trait yet, diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index d4f9867b7c..8460579450 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -231,7 +231,9 @@ struct Optional[T: CollectionElement]( """ return not self - fn __str__[U: RepresentableCollectionElement](self: Optional[U]) -> String: + fn __str__[ + U: RepresentableCollectionElement, // + ](self: Optional[U]) -> String: """Return the string representation of the value of the Optional. Parameters: @@ -247,7 +249,9 @@ struct Optional[T: CollectionElement]( return output # TODO: Include the Parameter type in the string as well. - fn __repr__[U: RepresentableCollectionElement](self: Optional[U]) -> String: + fn __repr__[ + U: RepresentableCollectionElement, // + ](self: Optional[U]) -> String: """Returns the verbose string representation of the Optional. Parameters: @@ -265,7 +269,7 @@ struct Optional[T: CollectionElement]( return output fn format_to[ - U: RepresentableCollectionElement + U: RepresentableCollectionElement, // ](self: Optional[U], inout writer: Formatter): """Write Optional string representation to a `Formatter`. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index e27d8d6bb4..13d78d5ada 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -123,7 +123,7 @@ struct Span[ @always_inline fn __init__[ - T2: CollectionElementNew, size: Int + T2: CollectionElementNew, size: Int, // ](inout self, ref [lifetime]array: InlineArray[T2, size]): """Construct a Span from an InlineArray. @@ -226,7 +226,7 @@ struct Span[ @always_inline fn copy_from[ - lifetime: MutableLifetime, + lifetime: MutableLifetime, // ](self: Span[T, lifetime], other: Span[T, _]): """ Performs an element wise copy from all elements of `other` into all elements of `self`. @@ -250,7 +250,7 @@ struct Span[ return len(self) > 0 fn __eq__[ - T: EqualityComparableCollectionElement + T: EqualityComparableCollectionElement, // ](ref [_]self: Span[T, lifetime], ref [_]rhs: Span[T]) -> Bool: """Verify if span is equal to another span. @@ -279,7 +279,7 @@ struct Span[ @always_inline fn __ne__[ - T: EqualityComparableCollectionElement + T: EqualityComparableCollectionElement, // ](ref [_]self: Span[T, lifetime], ref [_]rhs: Span[T]) -> Bool: """Verify if span is not equal to another span. @@ -295,7 +295,7 @@ struct Span[ """ return not self == rhs - fn fill[lifetime: MutableLifetime](self: Span[T, lifetime], value: T): + fn fill[lifetime: MutableLifetime, //](self: Span[T, lifetime], value: T): """ Fill the memory that a span references with a given value. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index c3765e0fe3..fe11dd2732 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -468,7 +468,7 @@ struct InlineArray[ @always_inline fn __contains__[ - T: EqualityComparableCollectionElement + T: EqualityComparableCollectionElement, // ](self, value: T) -> Bool: """Verify if a given value is present in the array. From 4fb97901cc75b9326fd2b88afdf514e4b8e6390b Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Wed, 10 Jul 2024 23:56:49 -0400 Subject: [PATCH 1201/2019] [Debugging] Add support for breakpoints on raise. MODULAR_ORIG_COMMIT_REV_ID: b281991e286eb48890b4a4a2970cd851c14f8578 --- stdlib/src/builtin/error.mojo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 7c79220d0c..e08dea91e1 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -193,3 +193,9 @@ struct Error( if length < 0: length = -length return String(StringRef(self.data, length)) + + +@export("__mojo_debugger_raise_hook") +fn __mojo_debugger_raise_hook(): + """This function is used internally by the Mojo Debugger.""" + pass From 9367b9085a6e4e99e6e1efd663efb010e4df3351 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 11 Jul 2024 03:20:32 -0400 Subject: [PATCH 1202/2019] [stdlib] Remove `LegacyPointer` version of `memcpy`. MODULAR_ORIG_COMMIT_REV_ID: 725dd7a41a21a0c04bb66a5a7d7a507cd68df71e --- docs/changelog.md | 3 +++ stdlib/src/memory/memory.mojo | 21 ++++----------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4d661f5b2a..b5cfe7d242 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -446,6 +446,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `sort(ptr, len)` a pointer and length (can change to Span in future) - `sort[type, cmp_fn](ptr, len)` above with custom compare +- `memcpy` with `LegacyPointer` has been removed. Please use the `UnsafePointer` + overload instead. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index eab6dc8798..122aac5ecf 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -170,7 +170,7 @@ fn memcmp[ @always_inline -fn memcpy[count: Int](dest: LegacyPointer, src: __type_of(dest)): +fn memcpy[count: Int](dest: UnsafePointer, src: __type_of(dest)): """Copies a memory area. Parameters: @@ -237,12 +237,12 @@ fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): dest: The destination pointer. src: The source pointer. """ - memcpy[count](dest.address, src.address) + memcpy[count](dest.address.address, src.address.address) @always_inline fn memcpy( - dest_data: LegacyPointer[Int8, *_], src_data: __type_of(dest_data), n: Int + dest_data: UnsafePointer[Int8, *_], src_data: __type_of(dest_data), n: Int ): """Copies a memory area. @@ -307,19 +307,6 @@ fn memcpy( Scalar.store(dest_dtype_ptr, i, Scalar.load(src_dtype_ptr, i)) -@always_inline -fn memcpy(dest: LegacyPointer, src: __type_of(dest), count: Int): - """Copies a memory area. - - Args: - dest: The destination pointer. - src: The source pointer. - count: The number of elements to copy. - """ - var n = count * sizeof[dest.type]() - memcpy(dest.bitcast[Int8](), src.bitcast[Int8](), n) - - @always_inline fn memcpy(dest: UnsafePointer, src: __type_of(dest), count: Int): """Copies a memory area. @@ -342,7 +329,7 @@ fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): src: The source pointer. count: The number of elements to copy (not bytes!). """ - memcpy(dest.address, src.address, count) + memcpy(dest.address.address, src.address.address, count) @always_inline From d612f248d5df0905abfe90345188321f55d0ba83 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 11 Jul 2024 03:55:21 -0400 Subject: [PATCH 1203/2019] [stdlib] Remove `LegacyPointer` version of `_free` MODULAR_ORIG_COMMIT_REV_ID: c9990efc93a455a0213028d411f44f3a01c13859 --- stdlib/src/memory/memory.mojo | 9 ++------- stdlib/src/memory/unsafe.mojo | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 122aac5ecf..e1377d4a81 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -25,7 +25,7 @@ from sys import llvm_intrinsic, sizeof, triple_is_nvidia_cuda from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace -from .unsafe import DTypePointer, LegacyPointer +from .unsafe import DTypePointer # ===----------------------------------------------------------------------=== # # Utilities @@ -561,9 +561,4 @@ fn _free(ptr: UnsafePointer): @always_inline fn _free(ptr: DTypePointer): - _free(ptr.address) - - -@always_inline -fn _free(ptr: LegacyPointer): - _free(UnsafePointer(ptr.address)) + _free(ptr.address.address) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index eb46575efb..d12f5091c8 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -332,7 +332,7 @@ struct LegacyPointer[ @always_inline fn free(self): """Frees the heap allocated memory.""" - return _free(self) + return _free(self.address) # ===------------------------------------------------------------------=== # # Casting From ed09a08c5bd33909c862782452021442d6f1871a Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 11 Jul 2024 09:12:57 -0500 Subject: [PATCH 1204/2019] [External] [stdlib] Add a few explicit copy constructors (#43261) This pull request is part of https://github.com/modularml/mojo/pull/3153 and can be merged before. It's basically adding a few explicit copy constructors where necessary. I didn't add any traits, as `CollectionElement` will very soon require the explicit copy constructor. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3217 MODULAR_ORIG_COMMIT_REV_ID: 5593d75e811050ee28b5d981ab93ee6f789cdec2 --- stdlib/src/builtin/_location.mojo | 3 +++ stdlib/src/builtin/none.mojo | 8 ++++++++ stdlib/src/builtin/sort.mojo | 3 +++ stdlib/src/collections/optional.mojo | 22 ++++++++++++++++++++++ stdlib/src/utils/index.mojo | 8 ++++++++ stdlib/src/utils/static_tuple.mojo | 8 ++++++++ stdlib/test/builtin/test_sort.mojo | 9 ++++++++- stdlib/test/collections/test_dict.mojo | 3 +++ stdlib/test/collections/test_list.mojo | 7 +++++++ stdlib/test/memory/test_arc.mojo | 3 +++ stdlib/test/utils/test_variant.mojo | 9 +++++++++ 11 files changed, 82 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 733a24817e..1b6ddb583d 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -23,6 +23,9 @@ struct _SourceLocation(Formattable, Stringable): var col: Int var file_name: StringLiteral + fn __init__(inout self, *, other: Self): + self = other + @no_inline fn __str__(self) -> String: return String.format_sequence(self) diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo index fb459c97b5..df23c63141 100644 --- a/stdlib/src/builtin/none.mojo +++ b/stdlib/src/builtin/none.mojo @@ -29,3 +29,11 @@ struct NoneType(CollectionElement): fn __init__(inout self): """Construct an instance of the `None` type.""" self._value = None + + fn __init__(inout self, *, other: Self): + """Explicit copy constructor. + + Args: + other: Another `NoneType` instance to copy. + """ + self._value = None diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 55bb7544e4..97ce890c34 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -30,6 +30,9 @@ from memory import UnsafePointer struct _SortWrapper[type: CollectionElement](CollectionElement): var data: type + fn __init__(inout self, *, other: Self): + self.data = other.data + @always_inline fn _insertion_sort[ diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 8460579450..06827639b2 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -171,6 +171,17 @@ struct Optional[T: CollectionElement]( """ return self.__bool__() + fn __eq__(self, rhs: NoneType) -> Bool: + """Return `True` if a value is not present. + + Args: + rhs: The `None` value to compare to. + + Returns: + `True` if a value is not present, `False` otherwise. + """ + return self is None + fn __eq__[ T: EqualityComparableCollectionElement ](self: Optional[T], rhs: Optional[T]) -> Bool: @@ -193,6 +204,17 @@ struct Optional[T: CollectionElement]( return False return not rhs + fn __ne__(self, rhs: NoneType) -> Bool: + """Return `True` if a value is present. + + Args: + rhs: The `None` value to compare to. + + Returns: + `False` if a value is not present, `True` otherwise. + """ + return self is not None + fn __ne__[ T: EqualityComparableCollectionElement ](self: Optional[T], rhs: Optional[T]) -> Bool: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index dd3a1120c1..6689783f78 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -313,6 +313,14 @@ struct StaticIntTuple[size: Int]( _type = __mlir_type[`!pop.array<`, size.value, `, `, Int, `>`] ](elem) + fn __init__(inout self, *, other: Self): + """Copy constructor. + + Args: + other: The other tuple to copy from. + """ + self.data = StaticTuple[Int, size](other=other.data) + @always_inline fn __init__(inout self, values: VariadicList[Int]): """Creates a tuple constant using the specified values. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index fe11dd2732..6bf94d1f2c 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -149,6 +149,14 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): _static_tuple_construction_checks[size]() self.array = _create_array[size, Self.element_type](values) + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided StaticTuple. + + Args: + other: The StaticTuple to copy. + """ + self.array = other.array + @always_inline("nodebug") fn __len__(self) -> Int: """Returns the length of the array. This is a known constant value. diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index de5bd553cd..892999abf3 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -482,9 +482,12 @@ fn test_sort_stress() raises: @value -struct MyStruct: +struct MyStruct(CollectionElement): var val: Int + fn __init__(inout self, *, other: Self): + self.val = other.val + fn test_sort_custom() raises: alias length = 103 @@ -535,6 +538,10 @@ struct Person(ComparableCollectionElement): var name: String var age: Int + fn __init__(inout self, *, other: Self): + self.name = String(other=other.name) + self.age = other.age + fn __lt__(self, other: Self) -> Bool: if self.age < other.age: return True diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 3bc94c51b4..ee71090cff 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -393,6 +393,9 @@ def test_dict_update_empty_new(): struct DummyKey(KeyElement): var value: Int + fn __init__(inout self, *, other: Self): + self = other + fn __hash__(self) -> Int: return self.value diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 2f313906bc..54f92afc63 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -562,6 +562,10 @@ struct CopyCountedStruct(CollectionElement): var counter: CopyCounter var value: String + fn __init__(inout self, *, other: Self): + self.counter = CopyCounter(other=other.counter) + self.value = String(other=other.value) + fn __init__(inout self, value: String): self.counter = CopyCounter() self.value = value @@ -868,6 +872,9 @@ struct DtorCounter(CollectionElement): fn __init__(inout self): self.payload = 0 + fn __init__(inout self, *, other: Self): + self.payload = other.payload + fn __copyinit__(inout self, existing: Self, /): self.payload = existing.payload diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index fea5c15c41..fff670a0ca 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -29,6 +29,9 @@ def test_basic(): struct ObservableDel(CollectionElement): var target: UnsafePointer[Bool] + fn __init__(inout self, *, other: Self): + self = other + fn __del__(owned self): self.target.init_pointee_move(True) diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 76b6c64906..bd00fdcdcb 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -27,6 +27,9 @@ struct TestCounter(CollectionElement): self.copied = 0 self.moved = 0 + fn __init__(inout self, *, other: Self): + self = other + fn __copyinit__(inout self, other: Self): self.copied = other.copied + 1 self.moved = other.moved @@ -63,6 +66,9 @@ struct Poison(CollectionElement): fn __init__(inout self): pass + fn __init__(inout self, *, other: Self): + _poison_ptr().init_pointee_move(True) + fn __copyinit__(inout self, other: Self): _poison_ptr().init_pointee_move(True) @@ -138,6 +144,9 @@ def test_move(): struct ObservableDel(CollectionElement): var target: UnsafePointer[Bool] + fn __init__(inout self, *, other: Self): + self = other + fn __del__(owned self): self.target.init_pointee_move(True) From c21a0a3db70b300d1e4784eefa952798e65cc259 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 11 Jul 2024 09:45:10 -0500 Subject: [PATCH 1205/2019] [stdlib] Remove `List.__get_ref` Rework the uses of `List.__get_ref` in `Dict` to use `List.__getitem__` which returns a `ref` now. In the few cases where we actually need to manipulate through a `Reference` (and `ref` won't do in some cases). This is a tricksy area, so tread with caution. Now that no `List.__get_ref` uses remain, remove the method. In a follow-up PR, we'll remove `Dict.__get_ref` since `Dict._find_ref` is sufficient until we rework `Dict.__getitem__` to return a `ref`. MODULAR_ORIG_COMMIT_REV_ID: f438196f923e8b2e25641fa7bf7c346475fc31d2 --- stdlib/src/collections/dict.mojo | 30 +++++++++++++------------- stdlib/src/collections/list.mojo | 18 ---------------- stdlib/test/collections/test_list.mojo | 19 ---------------- 3 files changed, 15 insertions(+), 52 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index b5b9a18478..764fac4877 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -84,7 +84,7 @@ struct _DictEntryIter[ @always_inline fn __next__(inout self) -> Reference[DictEntry[K, V], Self.dict_lifetime]: while True: - var opt_entry_ref = self.src[]._entries.__get_ref(self.index) + var opt_entry_ref = Reference(self.src[]._entries[self.index]) @parameter if forward: @@ -732,7 +732,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var index: Int found, slot, index = self._find_index(hash, key) if found: - var entry = self._entries.__get_ref(index) + var entry = Reference(self._entries[index]) debug_assert(entry[].__bool__(), "entry in index must be full") return entry[].value().value raise "KeyError" @@ -798,7 +798,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( found, slot, index = self._find_index(hash, key) if found: self._set_index(slot, Self.REMOVED) - var entry = self._entries.__get_ref(index) + var entry = Reference(self._entries[index]) debug_assert(entry[].__bool__(), "entry in index must be full") var entry_value = entry[].unsafe_take() entry[] = None @@ -943,9 +943,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( elif index == Self.REMOVED: pass else: - var entry = self._entries.__get_ref(index) - debug_assert(entry[].__bool__(), "entry in index must be full") - if hash == entry[].value().hash and key == entry[].value().key: + var entry = self._entries[index] + debug_assert(entry.__bool__(), "entry in index must be full") + if hash == entry.value().hash and key == entry.value().key: return (True, slot, index) self._next_index_slot(slot, perturb) @@ -968,24 +968,24 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = _DictIndex(self._reserved()) for i in range(len(old_entries)): - var entry = old_entries.__get_ref(i) - if entry[]: - self._insert[safe_context=True](entry[].unsafe_take()) + var entry = old_entries[i] + if entry: + self._insert[safe_context=True](entry.unsafe_take()) fn _compact(inout self): self._index = _DictIndex(self._reserved()) var right = 0 for left in range(self.size): - while not self._entries.__get_ref(right)[]: + while not self._entries[right]: right += 1 debug_assert(right < self._reserved(), "Invalid dict state") - var entry = self._entries.__get_ref(right) - debug_assert(entry[].__bool__(), "Logic error") - var slot = self._find_empty_index(entry[].value().hash) + var entry = self._entries[right] + debug_assert(entry.__bool__(), "Logic error") + var slot = self._find_empty_index(entry.value().hash) self._set_index(slot, left) if left != right: - self._entries[left] = entry[].unsafe_take() - entry[] = None + self._entries[left] = entry.unsafe_take() + entry = None right += 1 self._n_entries = self.size diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 07ee7a3729..4f29e2c046 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -766,24 +766,6 @@ struct List[T: CollectionElement]( return (self.data + normalized_idx)[] - # TODO(30737): Replace __getitem__ with this, but lots of places use it - fn __get_ref( - ref [_]self: Self, i: Int - ) -> Reference[T, __lifetime_of(self)]: - """Gets a reference to the list element at the given index. - - Args: - i: The index of the element. - - Returns: - An immutable reference to the element at the given index. - """ - var normalized_idx = i - if i < 0: - normalized_idx += self.size - - return self.unsafe_get(normalized_idx) - @always_inline fn unsafe_get( ref [_]self: Self, idx: Int diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 54f92afc63..9db6539e16 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -522,24 +522,6 @@ def test_2d_dynamic_list(): assert_equal(2, list.capacity) -# TODO(30737): remove this test along with other __get_ref() uses. -def test_list_explicit_copy_using_get_ref(): - var list = List[CopyCounter]() - list.append(CopyCounter()) - var list_copy = List(other=list) - assert_equal(0, list.__get_ref(0)[].copy_count) - assert_equal(1, list_copy.__get_ref(0)[].copy_count) - - var l2 = List[Int]() - for i in range(10): - l2.append(i) - - var l2_copy = List(other=l2) - assert_equal(len(l2), len(l2_copy)) - for i in range(len(l2)): - assert_equal(l2[i], l2_copy[i]) - - def test_list_explicit_copy(): var list = List[CopyCounter]() list.append(CopyCounter()) @@ -927,7 +909,6 @@ def main(): test_list_index() test_list_extend() test_list_extend_non_trivial() - test_list_explicit_copy_using_get_ref() test_list_explicit_copy() test_no_extra_copies_with_sugared_set_by_field() test_list_copy_constructor() From 8465befc33b305ca1adf7b4e238385b4df7611ed Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:48:27 -0500 Subject: [PATCH 1206/2019] [External] [stdlib] Make `object` `Representable` and `Formattable` (#43262) Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3159 MODULAR_ORIG_COMMIT_REV_ID: c9394cf4e28f99123cefc0ad7c1342866fa47160 --- stdlib/src/builtin/object.mojo | 100 +++++++++++++++++++++------ stdlib/test/builtin/test_object.mojo | 1 + 2 files changed, 79 insertions(+), 22 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 01a1eb877c..dfb3d2dda5 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -250,7 +250,13 @@ struct _Function(CollectionElement, CollectionElementNew): ) -struct _ObjectImpl(CollectionElement, CollectionElementNew, Stringable): +struct _ObjectImpl( + CollectionElement, + CollectionElementNew, + Stringable, + Representable, + Formattable, +): """This class is the underlying implementation of the value of an `object`. It is a variant of primitive types and pointers to implementations of more complex types. @@ -557,19 +563,24 @@ struct _ObjectImpl(CollectionElement, CollectionElementNew, Stringable): else: lhs = lhs.convert_bool_to_int() - @no_inline - fn __str__(self) -> String: - """Returns the name (in lowercase) of the specific object type.""" + fn format_to(self, inout writer: Formatter): + """Performs conversion to string according to Python + semantics. + """ if self.is_none(): - return "None" + writer.write("None") + return if self.is_bool(): - return str(self.get_as_bool()) + writer.write(str(self.get_as_bool())) + return if self.is_int(): - return str(self.get_as_int()) + writer.write(str(self.get_as_int())) + return if self.is_float(): - return str(self.get_as_float()) + writer.write(str(self.get_as_float())) + return if self.is_str(): - return ( + writer.write( "'" + str( StringRef( @@ -578,32 +589,56 @@ struct _ObjectImpl(CollectionElement, CollectionElementNew, Stringable): ) + "'" ) + return if self.is_func(): - return "Function at address " + hex(int(self.get_as_func().value)) + writer.write( + "Function at address " + hex(int(self.get_as_func().value)) + ) + return if self.is_list(): - var res = String("[") + writer.write(String("[")) for j in range(self.get_list_length()): if j != 0: - res += ", " - res += str(object(self.get_list_element(j))) - res += "]" - return res + writer.write(", ") + writer.write(str(object(self.get_list_element(j)))) + writer.write("]") + return var ptr = self.get_obj_attrs_ptr() - var res = String("{") + writer.write(String("{")) var print_sep = False for entry in ptr[].impl[].items(): if print_sep: - res += ", " - res += ( + writer.write(", ") + writer.write( "'" + str(entry[].key) + "' = " + str(object(entry[].value.copy())) ) print_sep = True - res += "}" - return res + writer.write("}") + return + + @no_inline + fn __repr__(self) -> String: + """Performs conversion to string according to Python + semantics. + + Returns: + The String representation of the object. + """ + return self.__str__() + + @no_inline + fn __str__(self) -> String: + """Performs conversion to string according to Python + semantics. + + Returns: + The String representation of the object. + """ + return String.format_sequence(self) # ===------------------------------------------------------------------=== # # List Functions @@ -656,7 +691,9 @@ struct _ObjectImpl(CollectionElement, CollectionElementNew, Stringable): # ===----------------------------------------------------------------------=== # -struct object(IntableRaising, ImplicitlyBoolable, Stringable): +struct object( + IntableRaising, ImplicitlyBoolable, Stringable, Representable, Formattable +): """Represents an object without a concrete type. This is the type of arguments in `def` functions that do not have a type @@ -934,6 +971,15 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): """ return self.__bool__() + fn format_to(self, inout writer: Formatter): + """Performs conversion to string according to Python + semantics. + + Args: + writer: The Formatter to write to. + """ + self._value.format_to(writer) + @no_inline fn __str__(self) -> String: """Performs conversion to string according to Python @@ -942,7 +988,17 @@ struct object(IntableRaising, ImplicitlyBoolable, Stringable): Returns: The String representation of the object. """ - return str(self._value) + return String.format_sequence(self._value) + + @no_inline + fn __repr__(self) -> String: + """Performs conversion to string according to Python + semantics. + + Returns: + The String representation of the object. + """ + return repr(self._value) # ===------------------------------------------------------------------=== # # Comparison Operators diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index a02e50948b..65c118665a 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -301,6 +301,7 @@ def test_convert_to_string(): assert_equal(str(a.bar), "'hello'") a.bar = [1, 2] assert_equal(str(a), "{'foo' = 5, 'bar' = [1, 2], 'baz' = False}") + assert_equal(repr(a), "{'foo' = 5, 'bar' = [1, 2], 'baz' = False}") def main(): From 26893c91519af6a78d8fe746f4f7ab556c693540 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:02:28 -0400 Subject: [PATCH 1207/2019] [stdlib][******] Switch `DTypePointer`'s underlying pointer to be `UnsafePointer` MODULAR_ORIG_COMMIT_REV_ID: bb21068a01d692d3404cf8f3eda37de0dc14fd34 --- stdlib/src/memory/unsafe.mojo | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index d12f5091c8..e533d55261 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -522,7 +522,7 @@ struct DTypePointer[ # Fields alias element_type = Scalar[type] - alias _pointer_type = Pointer[Scalar[type], address_space, exclusive] + alias _pointer_type = UnsafePointer[Scalar[type], address_space, exclusive] var address: Self._pointer_type """The pointed-to address.""" @@ -562,7 +562,7 @@ struct DTypePointer[ Args: value: The scalar pointer. """ - self = Pointer[ + self = UnsafePointer[ __mlir_type[`!pop.scalar<`, type.value, `>`], address_space ](value).bitcast[Scalar[type]]() @@ -579,7 +579,7 @@ struct DTypePointer[ @always_inline("nodebug") fn __init__( - inout self, value: Pointer[Scalar[type], address_space, exclusive] + inout self, value: UnsafePointer[Scalar[type], address_space, exclusive] ): """Constructs a `DTypePointer` from a scalar pointer of the same type. @@ -588,15 +588,6 @@ struct DTypePointer[ """ self.address = value - @always_inline("nodebug") - fn __init__(inout self, other: UnsafePointer[Scalar[type], address_space]): - """Constructs a `DTypePointer` from a scalar pointer of the same type. - - Args: - other: The scalar pointer. - """ - self.address = other.address - # ===------------------------------------------------------------------=== # # Factory methods # ===------------------------------------------------------------------=== # @@ -841,7 +832,7 @@ struct DTypePointer[ """Converts the `DTypePointer` to a scalar pointer of the same dtype. Returns: - A `Pointer` to a scalar of the same dtype. + An `UnsafePointer` to a scalar of the same dtype. """ return self.address.address @@ -1076,7 +1067,7 @@ struct DTypePointer[ Returns: The new constructed DTypePointer. """ - return self.address.offset(idx) + return self.address.offset(int(idx)) @always_inline("nodebug") fn offset(self, idx: UInt) -> Self: @@ -1088,4 +1079,4 @@ struct DTypePointer[ Returns: The new constructed DTypePointer. """ - return self.address.offset(idx) + return self.address.offset(idx.value) From 4680fcf7d05a450f9d22497269bb9ec5a62d21f6 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:18:39 -0400 Subject: [PATCH 1208/2019] [stdlib] Clean up uses of `Pointer` in stdlib No more `Pointer` in stdlib!! MODULAR_ORIG_COMMIT_REV_ID: 0d0f28141a8ccd14231427bf9443088fdeb312d3 --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/simd.mojo | 8 ++++---- stdlib/src/builtin/string.mojo | 21 +-------------------- stdlib/src/sys/ffi.mojo | 17 ++--------------- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/lock.mojo | 4 ++-- 7 files changed, 12 insertions(+), 44 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index a275b63f8f..368e9a9cfc 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory import LegacyPointer, Reference, UnsafePointer +from memory import Reference, UnsafePointer # ===----------------------------------------------------------------------===# # ListLiteral diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index faa7e73653..8d030831fb 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -34,7 +34,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call -from memory import AddressSpace, DTypePointer, Pointer +from memory import AddressSpace, DTypePointer @register_passable diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 783e3cec0f..213c22d966 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2653,7 +2653,7 @@ struct SIMD[type: DType, size: Int]( alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, ](ptr: DTypePointer[type, address_space, _]) -> Self: - """Loads the value the Pointer object points to. + """Loads the value the pointer points to. Constraints: The width and alignment must be positive integer values. @@ -2677,7 +2677,7 @@ struct SIMD[type: DType, size: Int]( alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, ](ptr: DTypePointer[type, address_space, _], offset: Scalar) -> Self: - """Loads the value the Pointer object points to with the given offset. + """Loads the value the pointer points to with the given offset. Constraints: The width and alignment must be positive integer values. @@ -2704,7 +2704,7 @@ struct SIMD[type: DType, size: Int]( alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, ](ptr: DTypePointer[type, address_space, _], offset: Int) -> Self: - """Loads the value the Pointer object points to with the given offset. + """Loads the value the pointer points to with the given offset. Constraints: The width and alignment must be positive integer values. @@ -2752,7 +2752,7 @@ struct SIMD[type: DType, size: Int]( alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, ](ptr: DTypePointer[type, address_space], offset: UInt) -> Self: - """Loads the value the Pointer object points to with the given offset. + """Loads the value the pointer points to with the given offset. Constraints: The width and alignment must be positive integer values. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index faab8046f9..a195f47c8f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -21,7 +21,7 @@ from sys import bitwidthof, llvm_intrinsic from sys.ffi import C_char from bit import count_leading_zeros -from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy +from memory import DTypePointer, UnsafePointer, memcmp, memcpy from utils import Span, StaticIntTuple, StringRef, StringSlice from utils._format import Formattable, Formatter, ToFormatter @@ -913,25 +913,6 @@ struct String( ) ) - @always_inline - fn __init__(inout self, ptr: LegacyPointer[UInt8], len: Int): - """Creates a string from the buffer. Note that the string now owns - the buffer. - - The buffer must be terminated with a null byte. - - Args: - ptr: The pointer to the buffer. - len: The length of the buffer, including the null terminator. - """ - self = Self( - Self._buffer_type( - unsafe_pointer=UnsafePointer(ptr.address), - size=len, - capacity=len, - ) - ) - @always_inline fn __init__(inout self, ptr: DTypePointer[DType.uint8], len: Int): """Creates a string from the buffer. Note that the string now owns diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index fd2d3b8d50..fd2f44b2ba 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements a foreign functions interface (FFI).""" -from memory import DTypePointer, LegacyPointer +from memory import DTypePointer from utils import StringRef @@ -201,19 +201,6 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): # ===----------------------------------------------------------------------===# -@always_inline -fn _get_global[ - name: StringLiteral, - init_fn: fn (LegacyPointer[NoneType]) -> LegacyPointer[NoneType], - destroy_fn: fn (LegacyPointer[NoneType]) -> None, -]( - payload: LegacyPointer[NoneType] = LegacyPointer[NoneType]() -) -> LegacyPointer[NoneType]: - return external_call[ - "KGEN_CompilerRT_GetGlobalOrCreate", LegacyPointer[NoneType] - ](StringRef(name), payload, init_fn, destroy_fn) - - @always_inline fn _get_global[ name: StringLiteral, @@ -265,7 +252,7 @@ fn _get_dylib_function[ var new_func = dylib._get_function[func_name, result_type]() external_call["KGEN_CompilerRT_InsertGlobal", NoneType]( StringRef(func_cache_name), - UnsafePointer.address_of(new_func).bitcast[Pointer[NoneType]]()[], + UnsafePointer.address_of(new_func).bitcast[UnsafePointer[NoneType]]()[], ) return new_func diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 838d2679f0..df5365a9ec 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -18,7 +18,7 @@ from collections import Optional from sys import sizeof -from memory import LegacyPointer, UnsafePointer, memcpy +from memory import UnsafePointer, memcpy from utils import InlineArray, StringSlice, Variant from utils._format import ToFormatter diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 8608a430d4..7cbe7b52c5 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -23,14 +23,14 @@ from time import sleep struct SpinWaiter: """A proxy for the C++ runtime's SpinWaiter type.""" - var storage: Pointer[NoneType] + var storage: UnsafePointer[NoneType] """Pointer to the underlying SpinWaiter instance.""" fn __init__(inout self: Self): """Initializes a SpinWaiter instance.""" self.storage = external_call[ "KGEN_CompilerRT_LLCL_InitializeSpinWaiter", - Pointer[NoneType], + UnsafePointer[NoneType], ]() fn __del__(owned self: Self): From fa4f58a902ed1bd04fef9cdfb284edf36bccb598 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 11 Jul 2024 14:38:25 -0700 Subject: [PATCH 1209/2019] [Stdlib] Remove positive_rem / positive_div from UInt, NFC UInts are always positive, so there is no need for those special methods. MODULAR_ORIG_COMMIT_REV_ID: 9e8809865477c581815c0d999739d09f5ad4dfa5 --- stdlib/src/builtin/uint.mojo | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 0c2b7ecbe7..2395f758ad 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -221,8 +221,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): if rhs == 0: # this should raise an exception. return 0 - var div: UInt = self._positive_div(rhs) - return div + return __mlir_op.`index.divu`(self.value, rhs.value) @always_inline("nodebug") fn __mod__(self, rhs: UInt) -> UInt: @@ -237,7 +236,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): if rhs == 0: # this should raise an exception. return 0 - return self._positive_rem(rhs) + return __mlir_op.`index.remu`(self.value, rhs.value) @always_inline("nodebug") fn __divmod__(self, rhs: UInt) -> Tuple[UInt, UInt]: @@ -251,8 +250,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): """ if rhs == 0: return Tuple[UInt, UInt](0, 0) - var div: UInt = self._positive_div(rhs) - return div, self._positive_rem(rhs) + return self // rhs, self % rhs @always_inline("nodebug") fn __pow__(self, exp: Self) -> Self: @@ -585,19 +583,6 @@ struct UInt(Comparable, Formattable, Representable, Stringable): """ return value ^ self - @always_inline("nodebug") - fn _positive_div(self, rhs: UInt) -> UInt: - """Return the division of `self` and `rhs` assuming that the arguments - are both positive. - - Args: - rhs: The value to divide on. - - Returns: - The integer division of `self` and `rhs` . - """ - return __mlir_op.`index.divu`(self.value, rhs.value) - @always_inline("nodebug") fn __gt__(self, rhs: UInt) -> Bool: """Return whether this UInt is strictly greater than another. @@ -680,19 +665,6 @@ struct UInt(Comparable, Formattable, Representable, Stringable): """ return self != 0 - @always_inline("nodebug") - fn _positive_rem(self, rhs: UInt) -> UInt: - """Return the modulus of `self` and `rhs` assuming that the arguments - are both positive. - - Args: - rhs: The value to divide on. - - Returns: - The integer modulus of `self` and `rhs` . - """ - return __mlir_op.`index.remu`(self.value, rhs.value) - @always_inline("nodebug") fn __index__(self) -> UInt: """Return self converted to an unsigned integer, if self is suitable for use as From f70c7eacf521b524aabeaa43b26ab0a71ab2ff83 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 11 Jul 2024 17:02:30 -0500 Subject: [PATCH 1210/2019] [stdlib] Remove `Dict.__get_ref` `Dict.__get_ref` is unused. The equivalent functionality exists recently with `Dict._find_ref`, anyways. So, remove `Dict.__get_ref`. Soon, we'll rework `Dict.__getitem__` too so we can remove `_find_ref`. MODULAR_ORIG_COMMIT_REV_ID: 18ce0de9d04e448a823c8077603cec0b078f4468 --- stdlib/src/collections/dict.mojo | 18 +----------------- stdlib/test/collections/test_dict.mojo | 4 ++-- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 764fac4877..3d1dc75082 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -522,24 +522,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - fn __getitem__(self, key: K) raises -> V: - """Retrieve a value out of the dictionary. - - Args: - key: The key to retrieve. - - Returns: - The value associated with the key, if it's present. - - Raises: - "KeyError" if the key isn't present. - """ - return self._find_ref(key) - # TODO(MSTDL-452): rename to __getitem__ returning a reference - fn __get_ref( - ref [_]self: Self, key: K - ) raises -> ref [__lifetime_of(self)] Self.V: + fn __getitem__(self, key: K) raises -> V: """Retrieve a value out of the dictionary. Args: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index ee71090cff..adeafa6539 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -283,8 +283,8 @@ def test_dict_copy_calls_copy_constructor(): # are coming from :) assert_equal(1, orig["a"].copy_count) assert_equal(2, copy["a"].copy_count) - assert_equal(0, orig.__get_ref("a").copy_count) - assert_equal(1, copy.__get_ref("a").copy_count) + assert_equal(0, orig._find_ref("a").copy_count) + assert_equal(1, copy._find_ref("a").copy_count) def test_dict_update_nominal(): From 238bea7f89d46a6b7a342445590421a7ac982263 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 11 Jul 2024 16:34:13 -0700 Subject: [PATCH 1211/2019] [Stdlib] Use the isclose from the math library, NFC MODULAR_ORIG_COMMIT_REV_ID: fcd909069ba8b1776d9ab985fbe047cb148a53d6 --- stdlib/src/math/math.mojo | 29 ++++++++++++++------- stdlib/src/testing/testing.mojo | 45 ++------------------------------- 2 files changed, 22 insertions(+), 52 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 3324ef5d6a..0407f5153c 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -898,18 +898,29 @@ fn isclose[ A boolean vector where a and b are equal within the specified tolerance. """ + constrained[ + a.type is DType.bool or a.type.is_numeric(), + "input type must be boolean, integral, or floating-point", + ]() + @parameter - if type is DType.bool or type.is_integral(): + if a.type is DType.bool or a.type.is_integral(): return a == b + else: + var both_nan = isnan(a) & isnan(b) + if equal_nan and all(both_nan): + return True + + var res = (a == b) | ( + isfinite(a) + & isfinite(b) + & ( + abs(a - b) + <= max(__type_of(a)(atol), rtol * max(abs(a), abs(b))) + ) + ) - var atol_vec = SIMD[type, simd_width](atol) - var rtol_vec = SIMD[type, simd_width](rtol) - var res = abs(a - b) <= (atol_vec.max(rtol_vec * abs(a).max(abs(b)))) - - if not equal_nan: - return res - - return res.select(res, isnan(a) & isnan(b)) + return res | both_nan if equal_nan else res # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index cacbcda422..4d417a18cb 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,51 +19,10 @@ from testing import assert_true ``` """ from collections import Optional +from math import isclose from builtin._location import __call_location, _SourceLocation -from utils.numerics import isfinite, isnan - -# ===----------------------------------------------------------------------=== # -# Utilities -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _isclose( - a: SIMD, - b: __type_of(a), - *, - atol: Scalar[a.type], - rtol: Scalar[a.type], - equal_nan: Bool, -) -> SIMD[DType.bool, a.size]: - constrained[ - a.type is DType.bool - or a.type.is_integral() - or a.type.is_floating_point(), - "input type must be boolean, integral, or floating-point", - ]() - - @parameter - if a.type is DType.bool or a.type.is_integral(): - return a == b - else: - var both_nan = isnan(a) & isnan(b) - if equal_nan and all(both_nan): - return True - - var res = (a == b) - var atol_vec = SIMD[a.type, a.size](atol) - var rtol_vec = SIMD[a.type, a.size](rtol) - res |= ( - isfinite(a) - & isfinite(b) - & (abs(a - b) <= (atol_vec.max(rtol_vec * abs(a).max(abs(b))))) - ) - - return res | both_nan if equal_nan else res - # ===----------------------------------------------------------------------=== # # Assertions @@ -358,7 +317,7 @@ fn assert_almost_equal[ "type must be boolean, integral, or floating-point", ]() - var almost_equal = _isclose( + var almost_equal = isclose( lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan ) From 7b586f637732e32d660aeafc98704a83ad91767e Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Thu, 11 Jul 2024 17:43:02 -0700 Subject: [PATCH 1212/2019] [stdlib] NFC - Removing outdated comment (#43324) [stdlib] NFC - Removing outdated comment MODULAR_ORIG_COMMIT_REV_ID: 57cc340155fc0b32efe1da4687042b7dbab75e5b --- stdlib/src/builtin/uint.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 2395f758ad..ced6ccca04 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -612,7 +612,6 @@ struct UInt(Comparable, Formattable, Representable, Stringable): pred = __mlir_attr.`#index` ](self.value, rhs.value) - # TODO(rparolin): remove this before you submit this change @always_inline("nodebug") fn __lt__(self, rhs: Int) -> Bool: """Compare this Int to the RHS using LT comparison. From 845ab3867f00d786024bad6b01bca560128f3327 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 11 Jul 2024 20:22:08 -0500 Subject: [PATCH 1213/2019] [stdlib] Remove test printing workaround Now that bug #26974 about forming pack arguments with memory-only types violating memory-only guarantees is fixed, remove the workaround in the stdlib tests. MODULAR_ORIG_COMMIT_REV_ID: 7085ebca35257005c1b1d45c01adea262ac4340d --- stdlib/test/collections/test_dict.mojo | 3 +-- stdlib/test/collections/test_set.mojo | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index adeafa6539..f0a4cc9c8c 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -427,8 +427,7 @@ def test_mojo_issue_1729(): fn test[name: String, test_fn: fn () raises -> object]() raises: - var name_val = name # FIXME(#26974): Can't pass 'name' directly. - print("Test", name_val, "...", end="") + print("Test", name, "...", end="") try: _ = test_fn() except e: diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 22bb20697d..3166a7115f 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -495,8 +495,7 @@ def test_set_str(): fn test[name: String, test_fn: fn () raises -> object]() raises: - var name_val = name # FIXME(#26974): Can't pass 'name' directly. - print("Test", name_val, "...", end="") + print("Test", name, "...", end="") try: _ = test_fn() except e: From 389a88c312f76585b91b92eb2cbfd974d1134122 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:38:04 -0400 Subject: [PATCH 1214/2019] [stdlib] Remove `LegacyPointer` `LegacyPointer` is now gone from the codebase. MODULAR_ORIG_COMMIT_REV_ID: 8e44b3fe130b9febe24622d94b634a3cb9d0292d --- docs/changelog.md | 3 + docs/manual/lifecycle/life.ipynb | 18 +- docs/manual/parameters/index.ipynb | 6 +- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 372 +--------------------------- stdlib/test/memory/test_memory.mojo | 2 +- 6 files changed, 18 insertions(+), 385 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b5cfe7d242..62b1df5d67 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -449,6 +449,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `memcpy` with `LegacyPointer` has been removed. Please use the `UnsafePointer` overload instead. +- `LegacyPointer` and `Pointer` has been removed. Please use `UnsafePointer` + instead. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 0a0a599315..cb84920593 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -429,14 +429,14 @@ "outputs": [], "source": [ "struct HeapArray:\n", - " var data: Pointer[Int]\n", + " var data: UnsafePointer[Int]\n", " var size: Int\n", " var cap: Int\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", " self.cap = size * 2\n", - " self.data = Pointer[Int].alloc(self.cap)\n", + " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", " self.data.store(i, val)\n", "\n", @@ -444,7 +444,7 @@ " # Deep-copy the existing value\n", " self.size = existing.size\n", " self.cap = existing.cap\n", - " self.data = Pointer[Int].alloc(self.cap)\n", + " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", " self.data.store(i, existing.data.load(i))\n", " # The lifetime of `existing` continues unchanged\n", @@ -476,9 +476,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Notice that `__copyinit__()` does not copy the `Pointer` value (doing so would\n", + "Notice that `__copyinit__()` does not copy the `UnsafePointer` value (doing so would\n", "make the copied value refer to the same `data` memory address as the original\n", - "value, which is a shallow copy). Instead, we initialize a new `Pointer` to\n", + "value, which is a shallow copy). Instead, we initialize a new `UnsafePointer` to\n", "allocate a new block of memory, and then copy over all the heap-allocated\n", "values (this is a deep copy).\n", "\n", @@ -593,19 +593,19 @@ "outputs": [], "source": [ "struct HeapArray:\n", - " var data: Pointer[Int]\n", + " var data: UnsafePointer[Int]\n", " var size: Int\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", - " self.data = Pointer[Int].alloc(self.size)\n", + " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", " self.data.store(i, val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", " self.size = existing.size\n", - " self.data = Pointer[Int].alloc(self.size)\n", + " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", " self.data.store(i, existing.data.load(i))\n", "\n", diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 14df3b8a5e..d19e206ba5 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -1115,12 +1115,12 @@ ], "source": [ "struct Array[T: AnyTrivialRegType]:\n", - " var data: Pointer[T]\n", + " var data: UnsafePointer[T]\n", " var size: Int\n", "\n", " fn __init__(inout self, size: Int, value: T):\n", " self.size = size\n", - " self.data = Pointer[T].alloc(self.size)\n", + " self.data = UnsafePointer[T].alloc(self.size)\n", " for i in range(self.size):\n", " self.data[i] = value\n", "\n", diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 688d4ef0d2..b1fbed67d2 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -15,5 +15,5 @@ from .arc import Arc from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation from .reference import AddressSpace, Reference -from .unsafe import DTypePointer, LegacyPointer, Pointer, bitcast +from .unsafe import DTypePointer, bitcast from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index e533d55261..2434457c2b 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory import Pointer +from memory import DTypePointer ``` """ @@ -129,376 +129,6 @@ fn bitcast[ ](val.value) -# ===----------------------------------------------------------------------===# -# LegacyPointer -# ===----------------------------------------------------------------------===# - -alias Pointer = LegacyPointer - - -@value -@register_passable("trivial") -struct LegacyPointer[ - type: AnyTrivialRegType, - address_space: AddressSpace = AddressSpace.GENERIC, - exclusive: Bool = False, -]( - Boolable, - CollectionElement, - CollectionElementNew, - Intable, - Stringable, - EqualityComparable, -): - """Defines a LegacyPointer struct that contains the address of a register passable - type. - - Parameters: - type: Type of the underlying data. - address_space: The address space the pointer is in. - exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. - """ - - alias _mlir_type = __mlir_type[ - `!kgen.pointer<`, - type, - `, `, - address_space._value.value, - ` exclusive(`, - exclusive.value, - `)>`, - ] - - var address: Self._mlir_type - """The pointed-to address.""" - - alias _ref_type = Reference[type, MutableStaticLifetime, address_space] - - @always_inline("nodebug") - fn __init__() -> Self: - """Constructs a null LegacyPointer from the value of pop.pointer type. - - Returns: - Constructed LegacyPointer object. - """ - return __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] - - @always_inline - fn __init__(other: LegacyPointer[type, address_space, _]) -> Self: - """Exclusivity parameter cast a pointer. - - Args: - other: Pointer to cast. - - Returns: - Constructed LegacyPointer object. - """ - return Self { - address: __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( - other.address - ) - } - - fn __init__(*, other: Self) -> Self: - """Copy the object. - - Args: - other: The value to copy. - - Returns: - Constructed LegacyPointer object. - """ - return other - - @always_inline - fn __init__(other: UnsafePointer[type, address_space, exclusive]) -> Self: - """Construct a legacy pointer (deprecated) from an UnsafePointer. - - Args: - other: The UnsafePointer. - - Returns: - Constructed LegacyPointer object. - """ - return other.address - - @always_inline("nodebug") - fn __init__(address: Self._mlir_type) -> Self: - """Constructs a LegacyPointer from the address. - - Args: - address: The input pointer address. - - Returns: - Constructed LegacyPointer object. - """ - return Self {address: address} - - @no_inline - fn __str__(self) -> String: - """Format this pointer as a hexadecimal string. - - Returns: - A String containing the hexadecimal representation of the memory - location destination of this pointer. - """ - return hex(int(self)) - - @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Checks if the LegacyPointer is null. - - Returns: - Returns False if the LegacyPointer is null and True otherwise. - """ - return self != Self() - - @staticmethod - @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: type) -> Self: - """Gets the address of the argument. - - Args: - arg: The value to get the address of. - - Returns: - A LegacyPointer struct which contains the address of the argument. - """ - # Work around AnyTrivialRegType vs AnyType. - return __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( - UnsafePointer.address_of(arg).address - ) - - @always_inline("nodebug") - fn __getitem__( - self, - ) -> ref [MutableStaticLifetime, address_space._value.value] type: - """Enable subscript syntax `ptr[]` to access the element. - - Returns: - The reference for the Mojo compiler to use. - """ - return __get_litref_as_mvalue( - __mlir_op.`lit.ref.from_pointer`[_type = Self._ref_type._mlir_type]( - LegacyPointer[type, address_space, False](self).address - ) - ) - - @always_inline("nodebug") - fn __getitem__( - self, offset: Int - ) -> ref [MutableStaticLifetime, address_space._value.value] type: - """Enable subscript syntax `ptr[idx]` to access the element. - - Args: - offset: The offset to load from. - - Returns: - The reference for the Mojo compiler to use. - """ - return (self + offset)[] - - @always_inline("nodebug") - fn __int__(self) -> Int: - """Returns the pointer address as an integer. - - Returns: - The address of the pointer as an Int. - """ - return __mlir_op.`pop.pointer_to_index`(self.address) - - # ===------------------------------------------------------------------=== # - # Allocate/Free - # ===------------------------------------------------------------------=== # - - @staticmethod - @always_inline - fn alloc(count: Int, /, *, alignment: Int = alignof[type]()) -> Self: - """Heap-allocates a number of element of the specified type using - the specified alignment. - - Args: - count: The number of elements to allocate (note that this is not - the bytecount). - alignment: The alignment used for the allocation. - - Returns: - A new LegacyPointer object which has been allocated on the heap. - """ - return _malloc[type, address_space=address_space]( - count * sizeof[type](), alignment=alignment - ) - - @always_inline - fn free(self): - """Frees the heap allocated memory.""" - return _free(self.address) - - # ===------------------------------------------------------------------=== # - # Casting - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn bitcast[ - new_type: AnyTrivialRegType = type, - /, - address_space: AddressSpace = Self.address_space, - ](self) -> LegacyPointer[new_type, address_space]: - """Bitcasts a LegacyPointer to a different type. - - Parameters: - new_type: The target type. - address_space: The address space of the result. - - Returns: - A new LegacyPointer object with the specified type and the same address, - as the original LegacyPointer. - """ - return __mlir_op.`pop.pointer.bitcast`[ - _type = LegacyPointer[new_type, address_space]._mlir_type, - ](self.address) - - # ===------------------------------------------------------------------=== # - # Comparisons - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn __eq__(self, rhs: Self) -> Bool: - """Returns True if the two pointers are equal. - - Args: - rhs: The value of the other pointer. - - Returns: - True if the two pointers are equal and False otherwise. - """ - return int(self) == int(rhs) - - @always_inline("nodebug") - fn __ne__(self, rhs: Self) -> Bool: - """Returns True if the two pointers are not equal. - - Args: - rhs: The value of the other pointer. - - Returns: - True if the two pointers are not equal and False otherwise. - """ - return int(self) != int(rhs) - - @always_inline("nodebug") - fn __lt__(self, rhs: Self) -> Bool: - """Returns True if this pointer represents a lower address than rhs. - - Args: - rhs: The value of the other pointer. - - - Returns: - True if this pointer represents a lower address and False otherwise. - """ - return int(self) < int(rhs) - - # ===------------------------------------------------------------------=== # - # Pointer Arithmetic - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn offset[T: Intable](self, idx: T) -> Self: - """Returns a new pointer shifted by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - idx: The offset. - - Returns: - The new LegacyPointer shifted by the offset. - """ - # Returns a new pointer shifted by the specified offset. - return __mlir_op.`pop.offset`(self.address, int(idx).value) - - @always_inline("nodebug") - fn offset(self, idx: UInt) -> Self: - """Returns a new pointer shifted by the specified offset. - - Args: - idx: The offset. - - Returns: - The new LegacyPointer shifted by the offset. - """ - # Returns a new pointer shifted by the specified offset. - return __mlir_op.`pop.offset`(self.address, idx.value) - - @always_inline("nodebug") - fn __add__[T: Intable](self, rhs: T) -> Self: - """Returns a new pointer shifted by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - - Returns: - The new LegacyPointer shifted by the offset. - """ - return self.offset(rhs) - - @always_inline("nodebug") - fn __add__(self, rhs: UInt) -> Self: - """Returns a new pointer shifted by the specified offset. - - Args: - rhs: The offset. - - Returns: - The new LegacyPointer shifted by the offset. - """ - return self.offset(rhs.value) - - @always_inline("nodebug") - fn __sub__[T: Intable](self, rhs: T) -> Self: - """Returns a new pointer shifted back by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - - Returns: - The new LegacyPointer shifted back by the offset. - """ - return self.offset(-int(rhs)) - - @always_inline("nodebug") - fn __iadd__[T: Intable](inout self, rhs: T): - """Shifts the current pointer by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - """ - self = self + rhs - - @always_inline("nodebug") - fn __isub__[T: Intable](inout self, rhs: T): - """Shifts back the current pointer by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - """ - self = self - rhs - - # ===----------------------------------------------------------------------===# # DTypePointer # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 9abe9391c5..e13d8707e4 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -489,7 +489,7 @@ def test_dtypepointer_scatter(): def test_memcpy_unsafe_pointer(): # Tests memcpy for the UnsafePointer type # Note: - # Eventually as DTypePointer and LegacyPointer are fully replaced with + # Eventually as DTypePointer is fully replaced with # UnsafePointer, this test will be redundant as all the other tests in # this file will have been updated to use `UnsafePointer`. From 913b988a67e9a816f8e182639369153b36246a03 Mon Sep 17 00:00:00 2001 From: Joshua Peterson Date: Fri, 12 Jul 2024 10:20:59 -0400 Subject: [PATCH 1215/2019] [stdlib] Rename some methods from LLCL to AsyncRT This change renames a few methods. It should have no impact on behavior. MODULAR_ORIG_COMMIT_REV_ID: e381faa6b06bdbc3b393bc3efe38861c6668e2ee --- stdlib/docs/faq.md | 2 +- stdlib/src/builtin/_startup.mojo | 6 +++--- stdlib/src/utils/lock.mojo | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index 02009c62db..b70afe5aaf 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -44,7 +44,7 @@ public-facing API has stabilized. Mojo depends on certain features that are still written in C++, collectively called "the compiler runtime." This may manifest in the standard library code -through references like `KGEN_CompilerRT_LLCL_CreateRuntime`. Like the MLIR +through references like `KGEN_CompilerRT_AsyncRT_CreateRuntime`. Like the MLIR dialects, the compiler runtime is currently private and undocumented. We plan on reducing the C++ dependencies in the future. diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 434d967bd8..5f99c73da0 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -35,13 +35,13 @@ fn _init_global_runtime( case where the runtime has the same number of threads as the number of cores. """ return external_call[ - "KGEN_CompilerRT_LLCL_CreateRuntime", UnsafePointer[NoneType] + "KGEN_CompilerRT_AsyncRT_CreateRuntime", UnsafePointer[NoneType] ](0) fn _destroy_global_runtime(ptr: UnsafePointer[NoneType]): """Destroy the global runtime if ever used.""" - external_call["KGEN_CompilerRT_LLCL_DestroyRuntime", NoneType](ptr) + external_call["KGEN_CompilerRT_AsyncRT_DestroyRuntime", NoneType](ptr) @always_inline @@ -55,7 +55,7 @@ fn _get_current_or_global_runtime() -> UnsafePointer[NoneType]: is created with number of threads equal to the number of cores. """ var current_runtime = external_call[ - "KGEN_CompilerRT_LLCL_GetCurrentRuntime", UnsafePointer[NoneType] + "KGEN_CompilerRT_AsyncRT_GetCurrentRuntime", UnsafePointer[NoneType] ]() if current_runtime: return current_runtime diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 7cbe7b52c5..8045c714d0 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -29,20 +29,20 @@ struct SpinWaiter: fn __init__(inout self: Self): """Initializes a SpinWaiter instance.""" self.storage = external_call[ - "KGEN_CompilerRT_LLCL_InitializeSpinWaiter", + "KGEN_CompilerRT_AsyncRT_InitializeSpinWaiter", UnsafePointer[NoneType], ]() fn __del__(owned self: Self): """Destroys the SpinWaiter instance.""" - external_call["KGEN_CompilerRT_LLCL_DestroySpinWaiter", NoneType]( + external_call["KGEN_CompilerRT_AsyncRT_DestroySpinWaiter", NoneType]( self.storage ) fn wait(self: Self): """Blocks the current task for a duration determined by the underlying policy.""" - external_call["KGEN_CompilerRT_LLCL_SpinWaiter_Wait", NoneType]( + external_call["KGEN_CompilerRT_AsyncRT_SpinWaiter_Wait", NoneType]( self.storage ) From bb17f15e99699403ed6c6115c7c94550be0eb977 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:03:28 -0400 Subject: [PATCH 1216/2019] [stdlib] Clean up `memory` module in stdlib Remove useless `.address`. MODULAR_ORIG_COMMIT_REV_ID: 9490aab6dd537e26a01d0de5df29f2c51a3eda85 --- stdlib/src/memory/memory.mojo | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index e1377d4a81..f4dd59e9db 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -237,7 +237,7 @@ fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): dest: The destination pointer. src: The source pointer. """ - memcpy[count](dest.address.address, src.address.address) + memcpy[count](dest.address, src.address) @always_inline @@ -317,7 +317,7 @@ fn memcpy(dest: UnsafePointer, src: __type_of(dest), count: Int): count: The number of elements to copy. """ var n = count * sizeof[dest.type]() - memcpy(dest.bitcast[Int8]().address, src.bitcast[Int8]().address, n) + memcpy(dest.bitcast[Int8](), src.bitcast[Int8](), n) @always_inline @@ -329,7 +329,7 @@ fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): src: The source pointer. count: The number of elements to copy (not bytes!). """ - memcpy(dest.address.address, src.address.address, count) + memcpy(dest.address, src.address, count) @always_inline @@ -382,10 +382,7 @@ fn memset[ value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ - # TODO (43028) call memset when DTypePointer's underlying data uses UnsafePointer - _memset_llvm( - ptr.address.bitcast[UInt8]().address, value, count * sizeof[type]() - ) + memset(ptr.address, value, count) @always_inline @@ -561,4 +558,4 @@ fn _free(ptr: UnsafePointer): @always_inline fn _free(ptr: DTypePointer): - _free(ptr.address.address) + _free(ptr.address) From 3569d7ac6e2a71a714ce18d1b9fb1d79fcb33f81 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 12 Jul 2024 12:45:58 -0500 Subject: [PATCH 1217/2019] [stdlib] Use `NamedTemporaryFile` in `test_getsize.mojo` Now that we have `NamedTemporaryFile`, use this in `test_getsize.mojo` instead of relying on a hard-coded filename in the test. This avoids some issues where the file may not get removed if the assert hit in the middle of the test before the `os.remove` call too. MODULAR_ORIG_COMMIT_REV_ID: 98dc07a6b4dad3e911f0e0c5f23d79624de8f8a3 --- stdlib/test/os/path/test_getsize.mojo | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/stdlib/test/os/path/test_getsize.mojo b/stdlib/test/os/path/test_getsize.mojo index c1fa61829f..6975d91335 100644 --- a/stdlib/test/os/path/test_getsize.mojo +++ b/stdlib/test/os/path/test_getsize.mojo @@ -12,20 +12,16 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -import os from os.path import getsize +from tempfile import NamedTemporaryFile +from testing import assert_equal -from testing import assert_equal, assert_false - -fn main() raises: - # TODO: use `NamedTemporaryFile` once we implement it. - alias file_name = "test_file" - assert_false(os.path.exists(file_name), "File should not exist") - with open(file_name, "w"): - pass - assert_equal(getsize(file_name), 0) - with open(file_name, "w") as my_file: - my_file.write(String("test")) - assert_equal(getsize(file_name), 4) - os.remove(file_name) +def main(): + with NamedTemporaryFile(delete=False) as tmp_file: + file_path = tmp_file.name + # No bytes written yet, 0 size. + assert_equal(getsize(file_path), 0) + var data_to_write = "test" + tmp_file.write(data_to_write) + assert_equal(getsize(file_path), len(data_to_write)) From be3550a25af8e8755d174c25a2b69a00f5ce3c38 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 12 Jul 2024 12:46:39 -0500 Subject: [PATCH 1218/2019] [stdlib] Guard against bad step sizes in `range` To avoid misuse of `range`, `debug_assert` when constructing a `range` with a `-1` step size for `UInt`. In the future, we'll reject zero-size step for the non-UInt `StridedRange` case too. MODULAR_ORIG_COMMIT_REV_ID: bedfab131583f36ea476b2767aa8a1f93143e946 --- stdlib/src/builtin/range.mojo | 11 +++++++- .../test_range_uint_reverse_range_bad.mojo | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 86d9725341..0fe4e722ca 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -375,7 +375,16 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): fn __init__(inout self, start: UInt, end: UInt, step: UInt): self.start = start self.end = end - debug_assert(step != 0, "step cannot be 0") + debug_assert( + step != 0, "range() arg 3 (the step size) must not be zero" + ) + debug_assert( + step != UInt(-1), + ( + "range() arg 3 (the step size) cannot be -1. Reverse range is" + " not supported yet for UInt ranges." + ), + ) self.step = step @always_inline diff --git a/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo b/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo new file mode 100644 index 0000000000..944c5d3a02 --- /dev/null +++ b/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo @@ -0,0 +1,26 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: has_not +# RUN: not --crash mojo -D MOJO_ENABLE_ASSERTIONS %s 2>&1 + +from testing import assert_equal + + +def test_range_uint_bad_step_size(): + # Ensure constructing a range with a "-1" step size (i.e. reverse range) + # with UInt is rejected and aborts now via `debug_assert` handler. + var r = range(UInt(0), UInt(10), UInt(-1)) + + +def main(): + test_range_uint_bad_step_size() From 07b016b68f5b85977788ab7c6ca417216a7bd4b6 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 12 Jul 2024 12:46:56 -0500 Subject: [PATCH 1219/2019] [stdlib] Remove `Optional._value_copy()` Now that `Optional.value()` works in the parameter context, use it everywhere internally and remove `_value_copy()` method. MODULAR_ORIG_COMMIT_REV_ID: 64bf35b7f7331a2d0821a2a607aa44a92a616c8d --- stdlib/src/collections/optional.mojo | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 06827639b2..496f74f0e1 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -343,18 +343,6 @@ struct Optional[T: CollectionElement]( debug_assert(self.__bool__(), ".value() on empty Optional") return self._value[T] - @always_inline - fn _value_copy(self) -> T: - """Unsafely retrieve the value out of the Optional. - - Note: only used for Optionals when used in a parameter context - due to compiler bugs. In general, prefer using the public `Optional.value()` - function that returns a `Reference[T]`. - """ - - debug_assert(self.__bool__(), ".value() on empty Optional") - return self._value[T] - fn take(inout self) -> T: """Move the value out of the Optional. From 8ace20ca1f711c9e7e08ad7c5b388fc0d03a4d9d Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:12:20 -0400 Subject: [PATCH 1220/2019] [stdlib] Add `UnsafePointer` overloads to `SIMD.load/store`. Make the API for `UnsafePointer` same as `DTypePointer`. MODULAR_ORIG_COMMIT_REV_ID: 46c9f1c9fdb8824be8c1b46e77e3d81c16166ebf --- docs/changelog.md | 5 + stdlib/src/builtin/simd.mojo | 58 ++++++- stdlib/src/memory/unsafe_pointer.mojo | 240 +++++++++++++++++++++++++- stdlib/src/sys/intrinsics.mojo | 60 +++++++ 4 files changed, 356 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 62b1df5d67..d6c3679cd8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -452,6 +452,11 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `LegacyPointer` and `Pointer` has been removed. Please use `UnsafePointer` instead. +- `UnsafePointer` now supports `simd_strided_load/store`, `gather`, and `scatter` + when the underlying type is `Scalar[DType]`. + +- `SIMD.load/store` now supports `UnsafePointer` overloads. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 213c22d966..a81791fc3d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2703,7 +2703,7 @@ struct SIMD[type: DType, size: Int]( *, alignment: Int = Self._default_alignment, address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _], offset: Int) -> Self: + ](ptr: UnsafePointer[Scalar[type], address_space, _], offset: Int) -> Self: """Loads the value the pointer points to with the given offset. Constraints: @@ -2737,14 +2737,40 @@ struct SIMD[type: DType, size: Int]( # intentionally don't unroll, otherwise the compiler vectorizes for i in range(size): v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( - ptr.address.offset(int(offset) + i).address + ptr.offset(int(offset) + i).address ) return v return __mlir_op.`pop.load`[alignment = alignment.value]( - ptr.address.offset(offset).bitcast[SIMD[type, size]]().address + ptr.offset(offset).bitcast[SIMD[type, size]]().address ) + @staticmethod + @always_inline("nodebug") + fn load[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: DTypePointer[type, address_space, _], offset: Int) -> Self: + """Loads the value the pointer points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to load from. + offset: The offset to load from. + + Returns: + The loaded value. + """ + + return Self.load[alignment=alignment](ptr.address, offset) + @staticmethod @always_inline("nodebug") fn load[ @@ -2831,6 +2857,30 @@ struct SIMD[type: DType, size: Int]( ](ptr: DTypePointer[type, address_space, _], val: Self): """Stores a single element value. + Constraints: + The width and alignment must be positive integer values. + + Parameters: + alignment: The minimal alignment of the address. + address_space: The address space the pointer is in. + + Args: + ptr: The pointer to store to. + val: The value to store. + """ + Self.store[alignment=alignment, address_space=address_space]( + ptr.address, val + ) + + @staticmethod + @always_inline("nodebug") + fn store[ + *, + alignment: Int = Self._default_alignment, + address_space: AddressSpace = AddressSpace.GENERIC, + ](ptr: UnsafePointer[Scalar[type], address_space, _], val: Self): + """Stores a single element value. + Constraints: The width and alignment must be positive integer values. @@ -2847,7 +2897,7 @@ struct SIMD[type: DType, size: Int]( alignment > 0, "alignment must be a positive integer value" ]() __mlir_op.`pop.store`[alignment = alignment.value]( - val, ptr.address.bitcast[SIMD[type, size]]().address + val, ptr.bitcast[SIMD[type, size]]().address ) @staticmethod diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index cd68962f7e..04d1898aa5 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,9 +19,17 @@ from memory import UnsafePointer ``` """ -from sys import alignof, sizeof -from sys.intrinsics import _mlirtype_is_eq, _type_is_eq - +from sys import alignof, sizeof, triple_is_nvidia_cuda +from sys.intrinsics import ( + _mlirtype_is_eq, + _type_is_eq, + gather, + scatter, + strided_load, + strided_store, +) + +from bit import is_power_of_two from memory.memory import _free, _malloc @@ -397,6 +405,232 @@ struct UnsafePointer[ # Methods # ===-------------------------------------------------------------------===# + @always_inline("nodebug") + fn simd_strided_load[ + type: DType, width: Int, T: Intable + ](self: UnsafePointer[Scalar[type]], stride: T) -> SIMD[type, width]: + """Performs a strided load of the SIMD vector. + + Parameters: + type: DType of returned SIMD value. + width: The SIMD width. + T: The Intable type of the stride. + + Args: + stride: The stride between loads. + + Returns: + A vector which is stride loaded. + """ + return strided_load[type, width]( + self, int(stride), SIMD[DType.bool, width](1) + ) + + @always_inline("nodebug") + fn simd_strided_store[ + type: DType, width: Int, T: Intable + ](self: UnsafePointer[Scalar[type]], val: SIMD[type, width], stride: T): + """Performs a strided store of the SIMD vector. + + Parameters: + type: DType of `val`, the SIMD value to store. + width: The SIMD width. + T: The Intable type of the stride. + + Args: + val: The SIMD value to store. + stride: The stride between stores. + """ + strided_store(val, self, int(stride), True) + + @always_inline("nodebug") + fn gather[ + type: DType, + *, + width: Int = 1, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ](self: UnsafePointer[Scalar[type]], offset: SIMD[_, width]) -> SIMD[ + type, width + ]: + """Gathers a SIMD vector from offsets of the current pointer. + + This method loads from memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + type: DType of the return SIMD. + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to gather from. + + Returns: + The SIMD vector containing the gathered values. + """ + var mask = SIMD[DType.bool, width](True) + var default = SIMD[type, width]() + return self.gather[width=width, alignment=alignment]( + offset, mask, default + ) + + @always_inline("nodebug") + fn gather[ + *, + type: DType, + width: Int = 1, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ]( + self: UnsafePointer[Scalar[type]], + offset: SIMD[_, width], + mask: SIMD[DType.bool, width], + default: SIMD[type, width], + ) -> SIMD[type, width]: + """Gathers a SIMD vector from offsets of the current pointer. + + This method loads from memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector, + or takes from the `default` SIMD vector, depending on the values of + the `mask` SIMD vector. + + If a mask element is `True`, the respective result element is given + by the current pointer and the `offset` SIMD vector; otherwise, the + result element is taken from the `default` SIMD vector. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + type: DType of the return SIMD. + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to gather from. + mask: The SIMD vector of boolean values, indicating for each + element whether to load from memory or to take from the + `default` SIMD vector. + default: The SIMD vector providing default values to be taken + where the `mask` SIMD vector is `False`. + + Returns: + The SIMD vector containing the gathered values. + """ + constrained[ + offset.type.is_integral(), + "offset type must be an integral type", + ]() + constrained[ + is_power_of_two(alignment), + "alignment must be a power of two integer value", + ]() + + var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) + return gather(base, mask, default, alignment) + + @always_inline("nodebug") + fn scatter[ + *, + type: DType, + width: Int = 1, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ]( + self: UnsafePointer[Scalar[type]], + offset: SIMD[_, width], + val: SIMD[type, width], + ): + """Scatters a SIMD vector into offsets of the current pointer. + + This method stores at memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector. + + If the same offset is targeted multiple times, the values are stored + in the order they appear in the `val` SIMD vector, from the first to + the last element. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + type: DType of `value`, the result SIMD buffer. + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to scatter into. + val: The SIMD vector containing the values to be scattered. + """ + var mask = SIMD[DType.bool, width](True) + self.scatter[width=width, alignment=alignment](offset, val, mask) + + @always_inline("nodebug") + fn scatter[ + *, + type: DType, + width: Int = 1, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ]( + self: UnsafePointer[Scalar[type]], + offset: SIMD[_, width], + val: SIMD[type, width], + mask: SIMD[DType.bool, width], + ): + """Scatters a SIMD vector into offsets of the current pointer. + + This method stores at memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector, + depending on the values of the `mask` SIMD vector. + + If a mask element is `True`, the respective element in the `val` SIMD + vector is stored at the memory address defined by the current pointer + and the `offset` SIMD vector; otherwise, no action is taken for that + element in `val`. + + If the same offset is targeted multiple times, the values are stored + in the order they appear in the `val` SIMD vector, from the first to + the last element. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + type: DType of `value`, the result SIMD buffer. + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to scatter into. + val: The SIMD vector containing the values to be scattered. + mask: The SIMD vector of boolean values, indicating for each + element whether to store at memory or not. + """ + constrained[ + offset.type.is_integral(), + "offset type must be an integral type", + ]() + constrained[ + is_power_of_two(alignment), + "alignment must be a power of two integer value", + ]() + + var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) + scatter(val, base, mask, alignment) + @always_inline fn initialize_pointee_explicit_copy[ T: ExplicitlyCopyable, // diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 90489298d3..f8da09749c 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1348,6 +1348,36 @@ fn strided_load[ ) -> SIMD[type, simd_width]: """Loads values from addr according to a specific stride. + Parameters: + type: DType of `value`, the value to store. + simd_width: The width of the SIMD vectors. + address_space: The address space of the memory location. + + Args: + addr: The memory location to load data from. + stride: How many lanes to skip before loading again. + mask: A binary vector which prevents memory access to certain lanes of + `value`. + + Returns: + A vector containing the loaded data. + """ + return strided_load[type, simd_width](addr.address, stride, mask) + + +@always_inline("nodebug") +fn strided_load[ + type: DType, + simd_width: Int, + /, + address_space: AddressSpace = AddressSpace.GENERIC, +]( + addr: UnsafePointer[Scalar[type], address_space, _], + stride: Int, + mask: SIMD[DType.bool, simd_width], +) -> SIMD[type, simd_width]: + """Loads values from addr according to a specific stride. + Parameters: type: DType of `value`, the value to store. simd_width: The width of the SIMD vectors. @@ -1441,6 +1471,36 @@ fn strided_store[ `value`. """ + strided_store[type, simd_width](value, addr.address, stride, mask) + + +@always_inline("nodebug") +fn strided_store[ + type: DType, + simd_width: Int, + /, + address_space: AddressSpace = AddressSpace.GENERIC, +]( + value: SIMD[type, simd_width], + addr: UnsafePointer[Scalar[type], address_space, _], + stride: Int, + mask: SIMD[DType.bool, simd_width], +): + """Loads values from addr according to a specific stride. + + Parameters: + type: DType of `value`, the value to store. + simd_width: The width of the SIMD vectors. + address_space: The address space of the memory location. + + Args: + value: The values to store. + addr: The location to store values at. + stride: How many lanes to skip before storing again. + mask: A binary vector which prevents memory access to certain lanes of + `value`. + """ + @parameter if simd_width == 1: if mask: From 1dfc052b441d0915be8abb42cd5f806cb11934e4 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 12 Jul 2024 17:12:24 -0500 Subject: [PATCH 1221/2019] [stdlib] Tweak `lit.cfg.py` for running with assertions We now always run with Mojo assertions by default across our codebase. So, we can move the config substitution to only happen externally since, internally, it happens in a different, shared, `lit.cfg.py`. MODULAR_ORIG_COMMIT_REV_ID: 3e8d7a67c3ce495fe5812e30bd1c5cb90fefbf5d --- stdlib/test/lit.cfg.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index ce41cab3c9..1dc17808a5 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -39,22 +39,6 @@ def has_not(): if has_not(): config.available_features.add("has_not") -# Insert at 0th position to intentionally override -# %mojo from the global utils/build/llvm-lit/lit.common.cfg.py -# (only matters internally). -# In the future, we can do other fancy things like with sanitizers -# and build type. -if bool(int(os.environ.get("MOJO_ENABLE_ASSERTIONS_IN_TESTS", 1))): - base_mojo_command = "mojo -D MOJO_ENABLE_ASSERTIONS" -else: - print("Running tests with assertions disabled.") - base_mojo_command = "mojo" -config.substitutions.insert(0, ("%mojo", base_mojo_command)) - -# Mojo without assertions. Only use this for known tests that do not work -# with assertions enabled. -config.substitutions.insert(1, ("%bare-mojo", "mojo")) - # This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRE: darwin`. config.available_features.add(platform.system().lower()) @@ -84,6 +68,21 @@ def has_not(): # polluting the source tree. config.test_exec_root = build_root / "stdlib" / "test" + # Note: only do this for external builds since we can use the common config substitutions + # which do the moral equivalent from our utils/build/llvm-lit/lit.common.cfg.py + # In the future, we can do other fancy things like with sanitizers + # and build type. + if bool(int(os.environ.get("MOJO_ENABLE_ASSERTIONS_IN_TESTS", 1))): + base_mojo_command = "mojo -D MOJO_ENABLE_ASSERTIONS" + else: + print("Running tests with assertions disabled.") + base_mojo_command = "mojo" + config.substitutions.insert(0, ("%mojo", base_mojo_command)) + + # Mojo without assertions. Only use this for known tests that do not work + # with assertions enabled. + config.substitutions.insert(1, ("%bare-mojo", "mojo")) + # The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the # open-source stdlib, we need to specify the paths to the just-built # `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the From 844306fb2cfcf5ec9a4de0b171a751549c791bd8 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 09:30:08 -0400 Subject: [PATCH 1222/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `hash`. MODULAR_ORIG_COMMIT_REV_ID: c595e38e3bf97c37c632034e99a2a8a565ccba87 --- stdlib/src/builtin/_hasher.mojo | 4 +--- stdlib/src/builtin/hash.mojo | 12 ++++++------ stdlib/test/builtin/test_hasher.mojo | 6 ++---- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/_hasher.mojo b/stdlib/src/builtin/_hasher.mojo index 4c0ea632a0..c75fecb1ab 100644 --- a/stdlib/src/builtin/_hasher.mojo +++ b/stdlib/src/builtin/_hasher.mojo @@ -21,9 +21,7 @@ trait _Hasher: fn __init__(inout self): ... - fn _update_with_bytes( - inout self, data: DTypePointer[DType.uint8], length: Int - ): + fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): ... fn _update_with_simd(inout self, value: SIMD[_, _]): diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index b84ee627d8..348d270f74 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -17,7 +17,7 @@ There are a few main tools in this module: - `Hashable` trait for types implementing `__hash__(self) -> Int` - `hash[T: Hashable](hashable: T) -> Int` built-in function. - A `hash()` implementation for arbitrary byte strings, - `hash(data: DTypePointer[DType.uint8], n: Int) -> Int`, + `hash(data: UnsafePointer[UInt8], n: Int) -> Int`, is the workhorse function, which implements efficient hashing via SIMD vectors. See the documentation of this function for more details on the hash implementation. @@ -186,7 +186,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: return int(final_data) -fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: +fn hash(bytes: UnsafePointer[UInt8], n: Int) -> Int: """Hash a byte array using a SIMD-modified DJBX33A hash algorithm. _This hash function is not suitable for cryptographic purposes._ The @@ -222,7 +222,7 @@ fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: ```mojo from random import rand var n = 64 - var rand_bytes = DTypePointer[DType.uint8].alloc(n) + var rand_bytes = UnsafePointer[UInt8].alloc(n) rand(rand_bytes, n) hash(rand_bytes, n) ``` @@ -249,7 +249,7 @@ fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: debug_assert(n == k * stride + r, "wrong hash tail math") # 1. Reinterpret the underlying data as a larger int type - var simd_data = bytes.bitcast[type]() + var simd_data = bytes.bitcast[Scalar[type]]() # 2. Compute the hash, but strided across the SIMD vector width. var hash_data = _HASH_INIT[type, simd_width]() @@ -261,10 +261,10 @@ fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: # a final hash state update vector that's stack-allocated. if r != 0: var remaining = InlineArray[UInt8, stride](unsafe_uninitialized=True) - var ptr = DTypePointer[DType.uint8](remaining.unsafe_ptr()) + var ptr = remaining.unsafe_ptr() memcpy(ptr, bytes + k * stride, r) memset_zero(ptr + r, stride - r) # set the rest to 0 - var last_value = SIMD[size=simd_width].load(ptr.bitcast[type]()) + var last_value = SIMD[size=simd_width].load(ptr.bitcast[Scalar[type]]()) hash_data = _HASH_UPDATE(hash_data, last_value) _ = remaining # We make sure the array lives long enough. diff --git a/stdlib/test/builtin/test_hasher.mojo b/stdlib/test/builtin/test_hasher.mojo index ed92b76fe3..964400519b 100644 --- a/stdlib/test/builtin/test_hasher.mojo +++ b/stdlib/test/builtin/test_hasher.mojo @@ -23,9 +23,7 @@ struct DummyHasher(_Hasher): fn __init__(inout self): self._dummy_value = 0 - fn _update_with_bytes( - inout self, data: DTypePointer[DType.uint8], length: Int - ): + fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): for i in range(length): self._dummy_value += data[i].cast[DType.uint64]() @@ -97,7 +95,7 @@ struct ComplexHashableStructWithList(_HashableWithHasher): # This is okay because self is passed as borrowed so the pointer will # be valid until at least the end of the function hasher._update_with_bytes( - data=DTypePointer(self._value3.unsafe_ptr()), + data=self._value3.unsafe_ptr(), length=len(self._value3), ) _ = self._value3 From 3e2ffbeaecb510c27933384cc2302b4e182c9e30 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:23:27 -0400 Subject: [PATCH 1223/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in string modules. MODULAR_ORIG_COMMIT_REV_ID: 31ba6b51cc032a9c112e5f755e0728889f43802b --- stdlib/benchmarks/utils/bench_memmem.mojo | 12 +-- stdlib/src/builtin/string.mojo | 29 ++----- stdlib/src/builtin/string_literal.mojo | 21 +---- stdlib/src/os/env.mojo | 4 +- stdlib/src/utils/string_slice.mojo | 9 +-- stdlib/src/utils/stringref.mojo | 97 ++++++++--------------- 6 files changed, 54 insertions(+), 118 deletions(-) diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 89be28bb5e..c494fb0dce 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -146,15 +146,15 @@ var needle = "school" # a word intentionally not in the test data fn _memmem_baseline[ type: DType ]( - haystack: DTypePointer[type], + haystack: UnsafePointer[Scalar[type]], haystack_len: Int, - needle: DTypePointer[type], + needle: UnsafePointer[Scalar[type]], needle_len: Int, -) -> DTypePointer[type]: +) -> UnsafePointer[Scalar[type]]: if not needle_len: return haystack if needle_len > haystack_len: - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() if needle_len == 1: return _memchr[type](haystack, needle[0], haystack_len) @@ -169,7 +169,7 @@ fn _memmem_baseline[ ) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: - var offset = i + count_trailing_zeros(mask) + var offset = int(i + count_trailing_zeros(mask)) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) @@ -180,7 +180,7 @@ fn _memmem_baseline[ if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: return haystack + i - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index a195f47c8f..0802c570cf 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -21,7 +21,7 @@ from sys import bitwidthof, llvm_intrinsic from sys.ffi import C_char from bit import count_leading_zeros -from memory import DTypePointer, UnsafePointer, memcmp, memcpy +from memory import UnsafePointer, memcmp, memcpy from utils import Span, StaticIntTuple, StringRef, StringSlice from utils._format import Formattable, Formatter, ToFormatter @@ -132,7 +132,7 @@ fn chr(c: Int) -> String: return int(mask.cast[DType.uint8]().reduce_add()) var num_bytes = _utf8_len(c) - var p = DTypePointer[DType.uint8].alloc(num_bytes + 1) + var p = UnsafePointer[UInt8].alloc(num_bytes + 1) var shift = 6 * (num_bytes - 1) var mask = UInt8(0xFF) >> (num_bytes + 1) var num_bytes_marker = UInt8(0xFF) << (8 - num_bytes) @@ -141,7 +141,7 @@ fn chr(c: Int) -> String: shift -= 6 Scalar.store(p, i, ((c >> shift) & 0b00111111) | 0b10000000) Scalar.store(p, num_bytes, 0) - return String(p.bitcast[DType.uint8](), num_bytes + 1) + return String(p.bitcast[UInt8](), num_bytes + 1) # ===----------------------------------------------------------------------=== # @@ -913,19 +913,6 @@ struct String( ) ) - @always_inline - fn __init__(inout self, ptr: DTypePointer[DType.uint8], len: Int): - """Creates a string from the buffer. Note that the string now owns - the buffer. - - The buffer must be terminated with a null byte. - - Args: - ptr: The pointer to the buffer. - len: The length of the buffer, including the null terminator. - """ - self = String(ptr.address, len) - fn __init__(inout self, obj: PythonObject): """Creates a string from a python object. @@ -999,7 +986,7 @@ struct String( @staticmethod @always_inline - fn _from_bytes(owned buff: DTypePointer[DType.uint8]) -> String: + fn _from_bytes(owned buff: UnsafePointer[UInt8]) -> String: """Construct a string from a sequence of bytes. This does no validation that the given bytes are valid in any specific @@ -1171,12 +1158,12 @@ struct String( var buffer = Self._buffer_type() buffer.resize(total_len + 1, 0) memcpy( - DTypePointer(buffer.data), + buffer.data, self.unsafe_ptr(), self_len, ) memcpy( - DTypePointer(buffer.data + self_len), + buffer.data + self_len, other.unsafe_ptr(), other_len + 1, # Also copy the terminator ) @@ -1629,9 +1616,7 @@ struct String( fn _compare( item1: UnsafePointer[UInt8], item2: UnsafePointer[UInt8], amnt: Int ) -> Bool: - var ptr1 = DTypePointer(item1) - var ptr2 = DTypePointer(item2) - return memcmp(ptr1, ptr2, amnt) == 0 + return memcmp(item1, item2, amnt) == 0 if self.byte_length() == 0: return False diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 3931b0a08a..d512635794 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -17,8 +17,6 @@ These are Mojo built-ins, so you don't need to import them. from sys.ffi import C_char -from memory import DTypePointer - from utils import StringRef from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type @@ -227,8 +225,8 @@ struct StringLiteral( var new_capacity = length + 1 buffer._realloc(new_capacity) buffer.size = new_capacity - var data: DTypePointer[DType.uint8] = self.as_uint8_ptr() - memcpy(DTypePointer(buffer.data), data, length) + var data: UnsafePointer[UInt8] = self.unsafe_ptr() + memcpy(buffer.data, data, length) (buffer.data + length).init_pointee_move(0) string._buffer = buffer^ return string @@ -298,14 +296,12 @@ struct StringLiteral( Returns: The raw pointer to the data. """ - var ptr = DTypePointer[DType.int8]( - __mlir_op.`pop.string.address`(self.value) - ) + var ptr = UnsafePointer(__mlir_op.`pop.string.address`(self.value)) # TODO(MSTDL-555): # Remove bitcast after changing pop.string.address # return type. - return UnsafePointer[Int8]._from_dtype_ptr(ptr).bitcast[UInt8]() + return ptr.bitcast[UInt8]() fn unsafe_cstr_ptr(self) -> UnsafePointer[C_char]: """Retrieves a C-string-compatible pointer to the underlying memory. @@ -317,15 +313,6 @@ struct StringLiteral( """ return self.unsafe_ptr().bitcast[C_char]() - @always_inline("nodebug") - fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: - """Get raw pointer to the underlying data. - - Returns: - The raw pointer to the data. - """ - return self.unsafe_ptr().bitcast[UInt8]() - @always_inline fn as_string_slice(self) -> StringSlice[ImmutableStaticLifetime]: """Returns a string slice of this static string literal. diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index f50cea6bdf..6ce4622a00 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -72,9 +72,7 @@ fn getenv(name: String, default: String = "") -> String: if not os_is_supported: return default - var ptr = external_call["getenv", DTypePointer[DType.uint8]]( - name.unsafe_ptr() - ) + var ptr = external_call["getenv", UnsafePointer[UInt8]](name.unsafe_ptr()) if not ptr: return default return String(StringRef(ptr)) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index e197b0d9c0..441493676a 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -47,7 +47,7 @@ fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): fn _validate_utf8_simd_slice[ width: Int, remainder: Bool = False -](ptr: DTypePointer[DType.uint8], length: Int, owned iter_len: Int) -> Int: +](ptr: UnsafePointer[UInt8], length: Int, owned iter_len: Int) -> Int: """Internal method to validate utf8, use _is_valid_utf8. Parameters: @@ -72,7 +72,7 @@ fn _validate_utf8_simd_slice[ @parameter if not remainder: - d = ptr.offset(idx).simd_strided_load[width](1) + d = ptr.offset(idx).simd_strided_load[DType.uint8, width](1) else: debug_assert(iter_len > -1, "iter_len must be > -1") d = SIMD[DType.uint8, width](0) @@ -139,11 +139,11 @@ fn _validate_utf8_simd_slice[ return iter_len -fn _is_valid_utf8(data: UnsafePointer[UInt8], length: Int) -> Bool: +fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: """Verify that the bytes are valid UTF-8. Args: - data: The pointer to the data. + ptr: The pointer to the data. length: The length of the items pointed to. Returns: @@ -167,7 +167,6 @@ fn _is_valid_utf8(data: UnsafePointer[UInt8], length: Int) -> Bool: . """ - var ptr = DTypePointer(data) var iter_len = length if iter_len >= 64 and simdwidthof[DType.uint8]() >= 64: iter_len = _validate_utf8_simd_slice[64](ptr, length, iter_len) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index dfa26408cc..da12d04d30 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,7 +16,7 @@ from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from builtin.string import _atol, _isspace -from memory import DTypePointer, UnsafePointer, memcmp +from memory import UnsafePointer, memcmp from memory.memory import _memcmp_impl_unconstrained # ===----------------------------------------------------------------------=== # @@ -102,12 +102,12 @@ struct StringRef( The constructor takes a raw pointer and a length. - Note that you should use the constructor from `DTypePointer[DType.uint8]` instead + Note that you should use the constructor from `UnsafePointer[UInt8]` instead as we are now storing the bytes as UInt8. See https://github.com/modularml/mojo/issues/2317 for more information. Args: - ptr: DTypePointer to the string. + ptr: UnsafePointer to the string. len: The length of the string. Returns: @@ -116,24 +116,6 @@ struct StringRef( return Self {data: ptr.bitcast[UInt8](), length: len} - @always_inline - fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> Self: - """Construct a StringRef value given a (potentially non-0 terminated - string). - - The constructor takes a raw pointer and a length. - - Args: - ptr: DTypePointer to the string. - len: The length of the string. - - Returns: - Constructed `StringRef` object. - """ - var unsafe_ptr = UnsafePointer[UInt8]._from_dtype_ptr(ptr) - - return Self {data: unsafe_ptr, length: len} - @always_inline fn __init__(ptr: UnsafePointer[UInt8]) -> Self: """Construct a StringRef value given a null-terminated string. @@ -145,23 +127,6 @@ struct StringRef( Constructed `StringRef` object. """ - return DTypePointer[DType.uint8](ptr) - - @always_inline - fn __init__(ptr: UnsafePointer[C_char]) -> Self: - """Construct a StringRef value given a null-terminated string. - - Note that you should use the constructor from `DTypePointer[DType.uint8]` instead - as we are now storing the bytes as UInt8. - See https://github.com/modularml/mojo/issues/2317 for more information. - - Args: - ptr: DTypePointer to the string. - - Returns: - Constructed `StringRef` object. - """ - var len = 0 while Scalar.load(ptr, len): len += 1 @@ -169,11 +134,15 @@ struct StringRef( return StringRef(ptr, len) @always_inline - fn __init__(ptr: DTypePointer[DType.uint8]) -> Self: + fn __init__(ptr: UnsafePointer[C_char]) -> Self: """Construct a StringRef value given a null-terminated string. + Note that you should use the constructor from `UnsafePointer[UInt8]` instead + as we are now storing the bytes as UInt8. + See https://github.com/modularml/mojo/issues/2317 for more information. + Args: - ptr: DTypePointer to the string. + ptr: UnsafePointer to the string. Returns: Constructed `StringRef` object. @@ -183,9 +152,7 @@ struct StringRef( while Scalar.load(ptr, len): len += 1 - var ptr1 = UnsafePointer[C_char]._from_dtype_ptr(ptr) - - return StringRef(ptr1, len) + return StringRef(ptr, len) # ===-------------------------------------------------------------------===# # Helper methods for slicing @@ -667,11 +634,11 @@ struct StringRef( @always_inline fn _memchr[ type: DType -](source: DTypePointer[type], char: Scalar[type], len: Int) -> DTypePointer[ - type -]: +]( + source: UnsafePointer[Scalar[type]], char: Scalar[type], len: Int +) -> UnsafePointer[Scalar[type]]: if not len: - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() alias bool_mask_width = simdwidthof[DType.bool]() var first_needle = SIMD[type, bool_mask_width](char) var vectorized_end = _align_down(len, bool_mask_width) @@ -682,27 +649,27 @@ fn _memchr[ ) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) if mask: - return source + i + count_trailing_zeros(mask) + return source + int(i + count_trailing_zeros(mask)) for i in range(vectorized_end, len): if source[i] == char: return source + i - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() @always_inline fn _memmem[ type: DType ]( - haystack: DTypePointer[type], + haystack: UnsafePointer[Scalar[type]], haystack_len: Int, - needle: DTypePointer[type], + needle: UnsafePointer[Scalar[type]], needle_len: Int, -) -> DTypePointer[type]: +) -> UnsafePointer[Scalar[type]]: if not needle_len: return haystack if needle_len > haystack_len: - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() if needle_len == 1: return _memchr[type](haystack, needle[0], haystack_len) @@ -727,7 +694,7 @@ fn _memmem[ var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: - var offset = i + count_trailing_zeros(mask) + var offset = int(i + count_trailing_zeros(mask)) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) @@ -741,36 +708,36 @@ fn _memmem[ if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: return haystack + i - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() @always_inline fn _memrchr[ type: DType -](source: DTypePointer[type], char: Scalar[type], len: Int) -> DTypePointer[ - type -]: +]( + source: UnsafePointer[Scalar[type]], char: Scalar[type], len: Int +) -> UnsafePointer[Scalar[type]]: if not len: - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() for i in reversed(range(len)): if source[i] == char: return source + i - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() @always_inline fn _memrmem[ type: DType ]( - haystack: DTypePointer[type], + haystack: UnsafePointer[Scalar[type]], haystack_len: Int, - needle: DTypePointer[type], + needle: UnsafePointer[Scalar[type]], needle_len: Int, -) -> DTypePointer[type]: +) -> UnsafePointer[Scalar[type]]: if not needle_len: return haystack if needle_len > haystack_len: - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() if needle_len == 1: return _memrchr[type](haystack, needle[0], haystack_len) for i in reversed(range(haystack_len - needle_len + 1)): @@ -778,4 +745,4 @@ fn _memrmem[ continue if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: return haystack + i - return DTypePointer[type]() + return UnsafePointer[Scalar[type]]() From d39c0f20eabedb4f17f21e31d07f8b5e3ed4490c Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:35:16 -0400 Subject: [PATCH 1224/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `file` module. MODULAR_ORIG_COMMIT_REV_ID: 5dccc7ff6ace7c450ed10af2ede098d86c48efdc --- stdlib/src/builtin/file.mojo | 20 ++++++++++---------- stdlib/src/builtin/simd.mojo | 2 +- stdlib/test/builtin/test_file.mojo | 11 ++++++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 8d030831fb..006d895727 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -34,7 +34,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call -from memory import AddressSpace, DTypePointer +from memory import AddressSpace, UnsafePointer @register_passable @@ -67,12 +67,12 @@ struct _OwnedStringRef(Boolable): struct FileHandle: """File handle to an opened file.""" - var handle: DTypePointer[DType.invalid] + var handle: UnsafePointer[NoneType] """The underlying pointer to the file handle.""" fn __init__(inout self): """Default constructor.""" - self.handle = DTypePointer[DType.invalid]() + self.handle = UnsafePointer[NoneType]() fn __init__(inout self, path: String, mode: String) raises: """Construct the FileHandle using the file path and mode. @@ -95,11 +95,11 @@ struct FileHandle: """ var err_msg = _OwnedStringRef() var handle = external_call[ - "KGEN_CompilerRT_IO_FileOpen", DTypePointer[DType.invalid] + "KGEN_CompilerRT_IO_FileOpen", UnsafePointer[NoneType] ](path, mode, Reference(err_msg)) if err_msg: - self.handle = DTypePointer[DType.invalid]() + self.handle = UnsafePointer[NoneType]() raise err_msg^.consume_as_error() self.handle = handle @@ -124,7 +124,7 @@ struct FileHandle: if err_msg: raise err_msg^.consume_as_error() - self.handle = DTypePointer[DType.invalid]() + self.handle = UnsafePointer[NoneType]() fn __moveinit__(inout self, owned existing: Self): """Moves constructor for the file handle. @@ -133,7 +133,7 @@ struct FileHandle: existing: The existing file handle. """ self.handle = existing.handle - existing.handle = DTypePointer[DType.invalid]() + existing.handle = UnsafePointer[NoneType]() fn read(self, size: Int64 = -1) raises -> String: """Reads data from a file and sets the file handle seek position. If @@ -205,7 +205,7 @@ struct FileHandle: fn read[ type: DType - ](self, ptr: DTypePointer[type], size: Int64 = -1) raises -> Int64: + ](self, ptr: UnsafePointer[Scalar[type]], size: Int64 = -1) raises -> Int64: """Read data from the file into the pointer. Setting size will read up to `sizeof(type) * size`. The default value of `size` is -1 which will read to the end of the file. Starts reading from the file handle @@ -235,7 +235,7 @@ struct FileHandle: var file = open(file_name, "r") # Allocate and load 8 elements - var ptr = DTypePointer[DType.float32].alloc(8) + var ptr = UnsafePointer[Float32].alloc(8) var bytes = file.read(ptr, 8) print("bytes read", bytes) @@ -246,7 +246,7 @@ struct FileHandle: _ = file.seek(2 * sizeof[DType.float32](), os.SEEK_CUR) # Allocate and load 8 more elements from file handle seek position - var ptr2 = DTypePointer[DType.float32].alloc(8) + var ptr2 = UnsafePointer[Float32].alloc(8) var bytes2 = file.read(ptr2, 8) var eleventh_element = ptr2[0] diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a81791fc3d..d38e5281be 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -30,7 +30,7 @@ from sys import ( from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd -from memory import bitcast +from memory import bitcast, DTypePointer from utils import InlineArray, StringSlice from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 19127cd9bd..6a48efecfd 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -16,6 +16,7 @@ from pathlib import Path, _dir_of_current_file from sys import os_is_windows from tempfile import gettempdir +from memory import UnsafePointer from testing import assert_equal, assert_true @@ -107,7 +108,7 @@ def test_file_read_to_address(): _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) as f: - var ptr = DTypePointer[DType.uint8].alloc(1000) + var ptr = UnsafePointer[UInt8].alloc(1000) assert_equal(f.read(ptr), 954) assert_equal(Scalar.load(ptr, 0), 76) # L assert_equal(Scalar.load(ptr, 1), 111) # o @@ -121,14 +122,14 @@ def test_file_read_to_address(): _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) as f: - var ptr = DTypePointer[DType.uint8].alloc(1000) + var ptr = UnsafePointer[UInt8].alloc(1000) assert_equal(f.read(ptr, 1000), 954) with open( _dir_of_current_file() / "test_file_dummy_input.txt", "r", ) as f: - var ptr = DTypePointer[DType.uint8].alloc(1000) + var ptr = UnsafePointer[UInt8].alloc(1000) assert_equal(f.read(ptr, 30), 30) assert_equal(f.read(ptr, 1), 1) assert_equal(f.read(ptr, 2), 2) @@ -228,14 +229,14 @@ struct Word: def test_file_read_to_dtype_pointer(): with open(_dir_of_current_file() / "test_file_dummy_input.txt", "r") as f: - var ptr = DTypePointer[DType.int8].alloc(8) + var ptr = UnsafePointer[UInt8].alloc(8) var data = f.read(ptr, 8) assert_equal( str(SIMD[size=8].load(ptr, 0)), "[76, 111, 114, 101, 109, 32, 105, 112]", ) - var ptr2 = DTypePointer[DType.int8].alloc(8) + var ptr2 = UnsafePointer[Int8].alloc(8) var data2 = f.read(ptr2, 8) assert_equal( str(SIMD[size=8].load(ptr2, 0)), From 5bc8302fea63ab804d97be9719fc73d6cc2b3db6 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:12:11 -0400 Subject: [PATCH 1225/2019] [stdlib] Covert `DTypePointer` uses to `UnsafePointer` in `Dict` MODULAR_ORIG_COMMIT_REV_ID: 8799d3093896a94512e0a22bd5b64bf3d22dcda4 --- stdlib/src/collections/dict.mojo | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 3d1dc75082..cb5fd406e2 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -232,48 +232,48 @@ struct _DictIndex: this in the current type system. """ - var data: DTypePointer[DType.invalid] + var data: UnsafePointer[NoneType] @always_inline fn __init__(inout self, reserved: Int): if reserved <= 128: - var data = DTypePointer[DType.int8].alloc(reserved) + var data = UnsafePointer[Int8].alloc(reserved) for i in range(reserved): data[i] = _EMPTY - self.data = data.bitcast[DType.invalid]() + self.data = data.bitcast[NoneType]() elif reserved <= 2**16 - 2: - var data = DTypePointer[DType.int16].alloc(reserved) + var data = UnsafePointer[Int16].alloc(reserved) for i in range(reserved): data[i] = _EMPTY - self.data = data.bitcast[DType.invalid]() + self.data = data.bitcast[NoneType]() elif reserved <= 2**32 - 2: - var data = DTypePointer[DType.int32].alloc(reserved) + var data = UnsafePointer[Int32].alloc(reserved) for i in range(reserved): data[i] = _EMPTY - self.data = data.bitcast[DType.invalid]() + self.data = data.bitcast[NoneType]() else: - var data = DTypePointer[DType.int64].alloc(reserved) + var data = UnsafePointer[Int64].alloc(reserved) for i in range(reserved): data[i] = _EMPTY - self.data = data.bitcast[DType.invalid]() + self.data = data.bitcast[NoneType]() fn copy(self, reserved: Int) -> Self: var index = Self(reserved) if reserved <= 128: - var data = self.data.bitcast[DType.int8]() - var new_data = index.data.bitcast[DType.int8]() + var data = self.data.bitcast[Int8]() + var new_data = index.data.bitcast[Int8]() memcpy(new_data, data, reserved) elif reserved <= 2**16 - 2: - var data = self.data.bitcast[DType.int16]() - var new_data = index.data.bitcast[DType.int16]() + var data = self.data.bitcast[Int16]() + var new_data = index.data.bitcast[Int16]() memcpy(new_data, data, reserved) elif reserved <= 2**32 - 2: - var data = self.data.bitcast[DType.int32]() - var new_data = index.data.bitcast[DType.int32]() + var data = self.data.bitcast[Int32]() + var new_data = index.data.bitcast[Int32]() memcpy(new_data, data, reserved) else: - var data = self.data.bitcast[DType.int64]() - var new_data = index.data.bitcast[DType.int64]() + var data = self.data.bitcast[Int64]() + var new_data = index.data.bitcast[Int64]() memcpy(new_data, data, reserved) return index^ @@ -282,30 +282,30 @@ struct _DictIndex: fn get_index(self, reserved: Int, slot: Int) -> Int: if reserved <= 128: - var data = self.data.bitcast[DType.int8]() + var data = self.data.bitcast[Int8]() return int(Scalar.load(data, slot & (reserved - 1))) elif reserved <= 2**16 - 2: - var data = self.data.bitcast[DType.int16]() + var data = self.data.bitcast[Int16]() return int(Scalar.load(data, slot & (reserved - 1))) elif reserved <= 2**32 - 2: - var data = self.data.bitcast[DType.int32]() + var data = self.data.bitcast[Int32]() return int(Scalar.load(data, slot & (reserved - 1))) else: - var data = self.data.bitcast[DType.int64]() + var data = self.data.bitcast[Int64]() return int(Scalar.load(data, slot & (reserved - 1))) fn set_index(inout self, reserved: Int, slot: Int, value: Int): if reserved <= 128: - var data = self.data.bitcast[DType.int8]() + var data = self.data.bitcast[Int8]() return Scalar.store(data, slot & (reserved - 1), value) elif reserved <= 2**16 - 2: - var data = self.data.bitcast[DType.int16]() + var data = self.data.bitcast[Int16]() return Scalar.store(data, slot & (reserved - 1), value) elif reserved <= 2**32 - 2: - var data = self.data.bitcast[DType.int32]() + var data = self.data.bitcast[Int32]() return Scalar.store(data, slot & (reserved - 1), value) else: - var data = self.data.bitcast[DType.int64]() + var data = self.data.bitcast[Int64]() return Scalar.store(data, slot & (reserved - 1), value) fn __del__(owned self): From a8d9b09b2fd12a4a7af447d3b553496030a53fe1 Mon Sep 17 00:00:00 2001 From: jon-chuang <9093549+jon-chuang@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:30:45 -0500 Subject: [PATCH 1226/2019] [External] [examples] Improve `matmul.mojo` example by 2-3x via split-K sub-tiling (#43438) [External] [examples] Improve `matmul.mojo` example by 2-3x via split-K sub-tiling Adds a matmul implementation using an accumulator and sub-tiling to outperform numpy. Co-authored-by: jon-chuang <9093549+jon-chuang@users.noreply.github.com> Closes modularml/mojo#3250 MODULAR_ORIG_COMMIT_REV_ID: a311eeacd735fd778cc14e817999be458635a97a --- examples/matmul.mojo | 122 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 19 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index f8faf1c02e..9a6066077c 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -30,9 +30,19 @@ alias N = 4096 # cols of B and C alias K = 512 # cols of A and rows of B alias type = DType.float32 -# simdwidth of = amount of `type` elements that fit into a single SIMD register -# 2x multiplier will use multiple SIMD registers in parallel where possible -alias nelts = simdwidthof[type]() * 2 +# Get optimal number of elements to run with vectorize at compile time. +# 2x or 4x helps with pipelining and running multiple SIMD operations in parallel. +alias nelts = get_simd_width() + + +fn get_simd_width() -> Int: + @parameter + if info.is_apple_silicon(): + return 4 * simdwidthof[type]() + else: + return 2 * simdwidthof[type]() + + alias tile_n = 64 # N must be a multiple of this alias tile_k = 4 # K must be a multiple of this @@ -79,13 +89,15 @@ def run_matmul_python() -> Float64: return gflops -def run_matmul_numpy(): +def run_matmul_numpy() -> Float64: var pymatmul: PythonObject = Python.import_module("pymatmul") var py = Python.import_module("builtins") var gflops = pymatmul.benchmark_matmul_numpy(M, N, K).to_float64() py.print(py.str("{:<18}{:>8.3f} GFLOPS").format("Numpy:", gflops)) + return gflops + fn matmul_naive(inout C: Matrix, A: Matrix, B: Matrix): for m in range(C.rows): @@ -201,10 +213,76 @@ fn matmul_unrolled[mode: Int](inout C: Matrix, A: Matrix, B: Matrix): parallelize[calc_row](C.rows, num_workers) +# Perform 2D tiling on the iteration space defined by end_m and end_n, parallelizing over m. +fn tile_parallel[ + tiled_fn: Tile2DFunc, tile_m: Int, tile_n: Int +](end_m: Int, end_n: Int,): + # Note: this assumes that ends are multiples of the tiles. + @parameter + fn row(mo: Int): + var m = tile_m * mo + for n in range(0, end_n, tile_n): + tiled_fn[tile_m, tile_n](m, n) + + parallelize[row](end_m // tile_m, M) + + +# Use per-tile accumulator to avoid repeated reads and writes to +# a global memory location, which can thrash the cache. +# Also partially unroll the loop over the reduction dimension (K) +# and reorder the reduction inner loop with the row iteration inner loop +fn matmul_reordered(inout C: Matrix, A: Matrix, B: Matrix): + alias tile_m = 32 + alias tile_n = 32 + alias tile_k = max(4, K // 256) + + constrained[M % tile_m == 0, "M must be a multiple of tile_m"]() + constrained[N % tile_n == 0, "N must be a multiple of tile_n"]() + constrained[K % tile_k == 0, "K must be a multiple of tile_k"]() + + @parameter + fn calc_tile[tile_m: Int, tile_n: Int](mo: Int, no: Int): + # Allocate the tile of accumulators on the stack. + var accumulator = Matrix[tile_m, tile_n]( + stack_allocation[tile_m * tile_n, type]() + ) + memset_zero(accumulator.data, tile_m * tile_n) + + for ko in range(0, A.cols, tile_k): + + @parameter + fn calc_tile_row[](m: Int): + @parameter + for k in range(tile_k): + + @parameter + fn dot[nelts: Int](n: Int): + accumulator.store[nelts]( + m, + n, + accumulator.load[nelts](m, n) + + A[mo + m, ko + k] * B.load[nelts](ko + k, no + n), + ) + + vectorize[ + dot, nelts, size=tile_n, unroll_factor = tile_n // nelts + ]() + + for m in range(tile_m): + calc_tile_row(m) + + # Copy the local tile to the output + for m in range(tile_m): + for n in range(tile_n): + C[mo + m, no + n] = accumulator[m, n] + + tile_parallel[calc_tile, tile_m, tile_n](C.rows, C.cols) + + @always_inline fn bench[ func: fn (inout Matrix, Matrix, Matrix) -> None, name: StringLiteral -](base_gflops: Float64) raises: +](base_gflops: Float64, np_gflops: Float64) raises: var A = Matrix[M, K].rand() var B = Matrix[K, N].rand() var C = Matrix[M, N]() @@ -222,11 +300,12 @@ fn bench[ var gflops = ((2 * M * N * K) / secs) / 1e9 var speedup: Float64 = gflops / base_gflops + var numpy_speedup: Float64 = gflops / np_gflops var py = Python.import_module("builtins") _ = py.print( - py.str("{:<18}{:>8.3f} GFLOPS {:>9.2f}x Python").format( - name, gflops, speedup + py.str("{:<18}{:>8.3f} GFLOPS {:>9.2f}x Python {:.2f}x Numpy").format( + name, gflops, speedup, numpy_speedup ) ) @@ -234,7 +313,7 @@ fn bench[ @always_inline fn test_matrix_equal[ func: fn (inout Matrix, Matrix, Matrix) -> None -](inout C: Matrix, A: Matrix, B: Matrix) raises -> Bool: +](C: Matrix, A: Matrix, B: Matrix) raises -> Bool: """Runs a matmul function on A and B and tests the result for equality with C on every element. """ @@ -268,6 +347,8 @@ def test_all(): raise Error("Unroll/logical cores does not match naive implementation") if not test_matrix_equal[matmul_unrolled[3]](C, A, B): raise Error("Unroll/perf cores does not match naive implementation") + if not test_matrix_equal[matmul_reordered](C, A, B): + raise Error("Loop reorder output does not match naive implementation") A.data.free() B.data.free() @@ -278,19 +359,22 @@ def main(): constrained[N % tile_n == 0, "N must be a multiple of tile_n"]() constrained[K % tile_k == 0, "K must be a multiple of tile_k"]() + print("Problem Size (M N K):", M, N, K) + test_all() print("CPU Results\n") - var python_gflops = run_matmul_python() - run_matmul_numpy() + var py_gflops = run_matmul_python() + var np_gflops = run_matmul_numpy() # Don't run all these benchmarks in CI, too resource intensive if not getenv("CI"): - bench[matmul_naive, "Naive:"](python_gflops) - bench[matmul_vectorized, "Vectorized: "](python_gflops) - bench[matmul_parallelized, "Parallelized:"](python_gflops) - bench[matmul_tiled, "Tiled:"](python_gflops) - bench[matmul_unrolled[0], "Unrolled:"](python_gflops) - bench[matmul_unrolled[1], "Physical Cores:"](python_gflops) - bench[matmul_unrolled[2], "Logical Cores:"](python_gflops) - # CHECK: Performance Cores - bench[matmul_unrolled[3], "Performance Cores:"](python_gflops) + bench[matmul_naive, "Naive:"](py_gflops, np_gflops) + bench[matmul_vectorized, "Vectorized:"](py_gflops, np_gflops) + bench[matmul_parallelized, "Parallelized:"](py_gflops, np_gflops) + bench[matmul_tiled, "Tiled:"](py_gflops, np_gflops) + bench[matmul_unrolled[0], "Unrolled:"](py_gflops, np_gflops) + bench[matmul_unrolled[1], "Physical Cores:"](py_gflops, np_gflops) + bench[matmul_unrolled[2], "Logical Cores:"](py_gflops, np_gflops) + bench[matmul_unrolled[3], "Performance Cores:"](py_gflops, np_gflops) + # CHECK: Reordered + bench[matmul_reordered, "Reordered:"](py_gflops, np_gflops) From 2afe2e76c1446bf739439ad351090b14bb999bd6 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:37:33 -0400 Subject: [PATCH 1227/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `ffi`. MODULAR_ORIG_COMMIT_REV_ID: c6f116845d66cceb2dbcab2bccc56084f6862e47 --- stdlib/src/sys/ffi.mojo | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index fd2f44b2ba..75bbf107e5 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements a foreign functions interface (FFI).""" -from memory import DTypePointer +from memory import UnsafePointer from utils import StringRef @@ -51,7 +51,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): The library is loaded on initialization and unloaded by `close`. """ - var handle: DTypePointer[DType.int8] + var handle: UnsafePointer[Int8] """The handle to the dynamic library.""" # TODO(#15590): Implement support for windows and remove the always_inline. @@ -67,17 +67,17 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @parameter if not os_is_windows(): - var handle = external_call["dlopen", DTypePointer[DType.int8]]( + var handle = external_call["dlopen", UnsafePointer[Int8]]( path.unsafe_ptr(), flags ) - if handle == DTypePointer[DType.int8](): + if handle == UnsafePointer[Int8](): var error_message = external_call[ "dlerror", UnsafePointer[UInt8] ]() abort("dlopen failed: " + String(error_message)) self.handle = handle else: - self.handle = DTypePointer[DType.int8]() + self.handle = UnsafePointer[Int8]() fn __init__(inout self, *, other: Self): """Copy the object. @@ -101,9 +101,9 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): "Checking dynamic library symbol is not supported on Windows", ]() - var opaque_function_ptr = external_call[ - "dlsym", DTypePointer[DType.int8] - ](self.handle.address, name.unsafe_ptr()) + var opaque_function_ptr = external_call["dlsym", UnsafePointer[Int8]]( + self.handle.address, name.unsafe_ptr() + ) if opaque_function_ptr: return True @@ -118,7 +118,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @parameter if not os_is_windows(): _ = external_call["dlclose", Int](self.handle) - self.handle = DTypePointer[DType.int8]() + self.handle = UnsafePointer[Int8]() fn __bool__(self) -> Bool: """Checks if the handle is valid. @@ -168,7 +168,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @parameter if not os_is_windows(): var opaque_function_ptr = external_call[ - "dlsym", DTypePointer[DType.int8] + "dlsym", UnsafePointer[Int8] ](self.handle.address, name) var result = UnsafePointer.address_of(opaque_function_ptr).bitcast[ result_type From 1ea646aa99f78eee78a017f7a95eb7a6db8f2fc2 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:37:49 -0400 Subject: [PATCH 1228/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `random` module MODULAR_ORIG_COMMIT_REV_ID: 44883b51ec835eb934ca66f4412edcdba40402fa --- examples/matmul.mojo | 2 +- examples/notebooks/Matmul.ipynb | 2 +- examples/reduce.mojo | 4 ++-- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/random/random.mojo | 12 ++++++------ stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 9a6066077c..0d0b1c11c9 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -63,7 +63,7 @@ struct Matrix[rows: Int, cols: Int]: @staticmethod fn rand() -> Self: var data = DTypePointer[type].alloc(rows * cols) - rand(data, rows * cols) + rand(data.address, rows * cols) return Self(data) fn __getitem__(self, y: Int, x: Int) -> Scalar[type]: diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index 7e2d149531..80deb716c6 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -427,7 +427,7 @@ " @staticmethod\n", " fn rand() -> Self:\n", " var data = DTypePointer[type].alloc(rows * cols)\n", - " rand(data, rows * cols)\n", + " rand(data.address, rows * cols)\n", " return Self(data)\n", "\n", " fn __getitem__(self, y: Int, x: Int) -> Scalar[type]:\n", diff --git a/examples/reduce.mojo b/examples/reduce.mojo index ae96c416b4..a4dcbde3ff 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -83,8 +83,8 @@ fn main() raises: var ptr_small = DTypePointer[type].alloc(size_small) var ptr_large = DTypePointer[type].alloc(size_large) - rand(ptr_small, size_small) - rand(ptr_large, size_large) + rand(ptr_small.address, size_small) + rand(ptr_large.address, size_large) var buffer_small = Buffer[type, size_small](ptr_small) var buffer_large = Buffer[type, size_large](ptr_large) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 5b00a232dd..0cd8564eb3 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -266,7 +266,7 @@ fn bench_compare(): var p1 = DTypePointer[type].alloc(size) var p2 = DTypePointer[type].alloc(size) print("Benchmark results") - rand(p1, size) + rand(p1.address, size) @parameter fn arg_size(): diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index ee19cb8adf..2a563f1431 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -22,13 +22,13 @@ from random import seed from sys import bitwidthof, external_call from time import perf_counter_ns -from memory import DTypePointer +from memory import UnsafePointer -fn _get_random_state() -> DTypePointer[DType.invalid]: +fn _get_random_state() -> UnsafePointer[NoneType]: return external_call[ "KGEN_CompilerRT_GetRandomState", - DTypePointer[DType.invalid], + UnsafePointer[NoneType], ]() @@ -95,7 +95,7 @@ fn random_ui64(min: UInt64, max: UInt64) -> UInt64: fn randint[ type: DType -](ptr: DTypePointer[type], size: Int, low: Int, high: Int): +](ptr: UnsafePointer[Scalar[type]], size: Int, low: Int, high: Int): """Fills memory with uniform random in range [low, high]. Constraints: @@ -121,7 +121,7 @@ fn randint[ ptr[ui] = random_ui64(low, high).cast[type]() -fn rand[type: DType](ptr: DTypePointer[type], size: Int): +fn rand[type: DType](ptr: UnsafePointer[Scalar[type]], size: Int): """Fills memory with random values from a uniform distribution. Parameters: @@ -178,7 +178,7 @@ fn randn_float64(mean: Float64 = 0.0, variance: Float64 = 1.0) -> Float64: fn randn[ type: DType ]( - ptr: DTypePointer[type], + ptr: UnsafePointer[Scalar[type]], size: Int, mean: Float64 = 0.0, variance: Float64 = 1.0, diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index 786a3b62cd..2f935cb42e 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -18,7 +18,7 @@ from random import rand fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = UnsafePointer[SIMD[D, 1]].alloc(size) - rand[D](p, size) + rand[D](p.address, size) sort[D](p, size) for i in range(1, size - 1): if p[i] < p[i - 1]: From 99aa152cb980b7cce7431d1e6fa1aaa77e666558 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:28:23 -0500 Subject: [PATCH 1229/2019] [External] [stdlib] Move `String.isspace` and `_StringIter` implementations to `StringSlice` (#43431) [External] [stdlib] Move `String.isspace` and `_StringIter` implementations to `StringSlice` Move `String.isspace` and `_StringIter` implementations to `StringSlice` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3197 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3197 MODULAR_ORIG_COMMIT_REV_ID: 31a6949b2a6b71611713ac144f12c7e39fe7a2f4 --- stdlib/src/builtin/string.mojo | 124 +++----------------------- stdlib/src/utils/string_slice.mojo | 135 +++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 112 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 0802c570cf..66e648085c 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -25,7 +25,7 @@ from memory import UnsafePointer, memcmp, memcpy from utils import Span, StaticIntTuple, StringRef, StringSlice from utils._format import Formattable, Formatter, ToFormatter -from utils.string_slice import _utf8_byte_type +from utils.string_slice import _utf8_byte_type, _StringSliceIter # ===----------------------------------------------------------------------=== # # ord @@ -706,76 +706,6 @@ fn isprintable(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # -@value -struct _StringIter[ - is_mutable: Bool, //, - lifetime: AnyLifetime[is_mutable].type, - forward: Bool = True, -]: - """Iterator for String. - - Parameters: - is_mutable: Whether the slice is mutable. - lifetime: The lifetime of the underlying string data. - forward: The iteration direction. `False` is backwards. - """ - - var index: Int - var continuation_bytes: Int - var ptr: UnsafePointer[UInt8] - var length: Int - - fn __init__( - inout self, *, unsafe_pointer: UnsafePointer[UInt8], length: Int - ): - self.index = 0 if forward else length - self.ptr = unsafe_pointer - self.length = length - self.continuation_bytes = 0 - for i in range(length): - if _utf8_byte_type(unsafe_pointer[i]) == 1: - self.continuation_bytes += 1 - - fn __iter__(self) -> Self: - return self - - fn __next__(inout self) -> StringSlice[lifetime]: - @parameter - if forward: - var byte_len = 1 - if self.continuation_bytes > 0: - var byte_type = _utf8_byte_type(self.ptr[self.index]) - if byte_type != 0: - byte_len = int(byte_type) - self.continuation_bytes -= byte_len - 1 - self.index += byte_len - return StringSlice[lifetime]( - unsafe_from_utf8_ptr=self.ptr + (self.index - byte_len), - len=byte_len, - ) - else: - var byte_len = 1 - if self.continuation_bytes > 0: - var byte_type = _utf8_byte_type(self.ptr[self.index - 1]) - if byte_type != 0: - while byte_type == 1: - byte_len += 1 - var b = self.ptr[self.index - byte_len] - byte_type = _utf8_byte_type(b) - self.continuation_bytes -= byte_len - 1 - self.index -= byte_len - return StringSlice[lifetime]( - unsafe_from_utf8_ptr=self.ptr + self.index, len=byte_len - ) - - fn __len__(self) -> Int: - @parameter - if forward: - return self.length - self.index - self.continuation_bytes - else: - return self.index - self.continuation_bytes - - struct String( Sized, Stringable, @@ -1203,23 +1133,25 @@ struct String( count=other_len + 1, ) - fn __iter__(ref [_]self) -> _StringIter[__lifetime_of(self)]: + fn __iter__(ref [_]self) -> _StringSliceIter[__lifetime_of(self)]: """Iterate over elements of the string, returning immutable references. Returns: An iterator of references to the string elements. """ - return _StringIter[__lifetime_of(self)]( + return _StringSliceIter[__lifetime_of(self)]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) - fn __reversed__(ref [_]self) -> _StringIter[__lifetime_of(self), False]: + fn __reversed__( + ref [_]self, + ) -> _StringSliceIter[__lifetime_of(self), False]: """Iterate backwards over the string, returning immutable references. Returns: A reversed iterator of references to the string elements. """ - return _StringIter[__lifetime_of(self), forward=False]( + return _StringSliceIter[__lifetime_of(self), forward=False]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) @@ -1593,49 +1525,17 @@ struct String( ) fn isspace(self) -> Bool: - """Determines whether the given String is a python - whitespace String. This corresponds to Python's + """Determines whether every character in the given String is a + python whitespace String. This corresponds to Python's [universal separators]( https://docs.python.org/3/library/stdtypes.html#str.splitlines) - `" \\t\\n\\r\\f\\v\\x1c\\x1e\\x85\\u2028\\u2029"`. + `" \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Returns: - True if the String is one of the whitespace characters + True if the whole String is made up of whitespace characters listed above, otherwise False. """ - # TODO add line and paragraph separator as stringliteral - # once unicode escape sequences are accepted - var next_line = List[UInt8](0xC2, 0x85) - """TODO: \\x85""" - var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8) - """TODO: \\u2028""" - var unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9) - """TODO: \\u2029""" - - @always_inline - fn _compare( - item1: UnsafePointer[UInt8], item2: UnsafePointer[UInt8], amnt: Int - ) -> Bool: - return memcmp(item1, item2, amnt) == 0 - - if self.byte_length() == 0: - return False - - for s in self: - var no_null_len = s.byte_length() - var ptr = s.unsafe_ptr() - if no_null_len == 1 and not _isspace(ptr[0]): - return False - elif no_null_len == 2 and not _compare( - ptr, next_line.unsafe_ptr(), 2 - ): - return False - elif no_null_len == 3 and not ( - _compare(ptr, unicode_line_sep.unsafe_ptr(), 3) - or _compare(ptr, unicode_paragraph_sep.unsafe_ptr(), 3) - ): - return False - return True + return self.as_string_slice().isspace() fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]: """Split the string by a separator. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 441493676a..f01f1874e8 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -191,6 +191,76 @@ fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: return _validate_utf8_simd_slice[4, True](ptr, length, iter_len) == 0 +@value +struct _StringSliceIter[ + is_mutable: Bool, //, + lifetime: AnyLifetime[is_mutable].type, + forward: Bool = True, +]: + """Iterator for StringSlice + + Parameters: + is_mutable: Whether the slice is mutable. + lifetime: The lifetime of the underlying string data. + forward: The iteration direction. `False` is backwards. + """ + + var index: Int + var continuation_bytes: Int + var ptr: UnsafePointer[UInt8] + var length: Int + + fn __init__( + inout self, *, unsafe_pointer: UnsafePointer[UInt8], length: Int + ): + self.index = 0 if forward else length + self.ptr = unsafe_pointer + self.length = length + self.continuation_bytes = 0 + for i in range(length): + if _utf8_byte_type(unsafe_pointer[i]) == 1: + self.continuation_bytes += 1 + + fn __iter__(self) -> Self: + return self + + fn __next__(inout self) -> StringSlice[lifetime]: + @parameter + if forward: + var byte_len = 1 + if self.continuation_bytes > 0: + var byte_type = _utf8_byte_type(self.ptr[self.index]) + if byte_type != 0: + byte_len = int(byte_type) + self.continuation_bytes -= byte_len - 1 + self.index += byte_len + return StringSlice[lifetime]( + unsafe_from_utf8_ptr=self.ptr + (self.index - byte_len), + len=byte_len, + ) + else: + var byte_len = 1 + if self.continuation_bytes > 0: + var byte_type = _utf8_byte_type(self.ptr[self.index - 1]) + if byte_type != 0: + while byte_type == 1: + byte_len += 1 + var b = self.ptr[self.index - byte_len] + byte_type = _utf8_byte_type(b) + self.continuation_bytes -= byte_len - 1 + self.index -= byte_len + return StringSlice[lifetime]( + unsafe_from_utf8_ptr=self.ptr + self.index, len=byte_len + ) + + fn __len__(self) -> Int: + @parameter + if forward: + return self.length - self.index - self.continuation_bytes + else: + return self.index - self.continuation_bytes + + struct StringSlice[ is_mutable: Bool, //, lifetime: AnyLifetime[is_mutable].type, @@ -431,6 +501,28 @@ struct StringSlice[ """ return not self == rhs + fn __iter__(ref [_]self) -> _StringSliceIter[__lifetime_of(self)]: + """Iterate over elements of the string slice, returning immutable references. + + Returns: + An iterator of references to the string elements. + """ + return _StringSliceIter[__lifetime_of(self)]( + unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() + ) + + fn __reversed__( + ref [_]self, + ) -> _StringSliceIter[__lifetime_of(self), False]: + """Iterate backwards over the string, returning immutable references. + + Returns: + A reversed iterator of references to the string elements. + """ + return _StringSliceIter[__lifetime_of(self), forward=False]( + unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() + ) + # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# @@ -492,3 +584,46 @@ struct StringSlice[ without the string getting deallocated early. """ pass + + fn isspace(self) -> Bool: + """Determines whether every character in the given StringSlice is a + python whitespace String. This corresponds to Python's + [universal separators]( + https://docs.python.org/3/library/stdtypes.html#str.splitlines) + `" \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + + Returns: + True if the whole StringSlice is made up of whitespace characters + listed above, otherwise False. + """ + + if self.byte_length() == 0: + return False + + # TODO add line and paragraph separator as stringliteral + # once Unicode escape sequences are accepted + var next_line = List[UInt8](0xC2, 0x85) + """TODO: \\x85""" + var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8) + """TODO: \\u2028""" + var unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9) + """TODO: \\u2029""" + + for s in self: + var no_null_len = s.byte_length() + var ptr = s.unsafe_ptr() + if no_null_len == 1 and _isspace(ptr[0]): + continue + elif ( + no_null_len == 2 and memcmp(ptr, next_line.unsafe_ptr(), 2) == 0 + ): + continue + elif no_null_len == 3 and ( + memcmp(ptr, unicode_line_sep.unsafe_ptr(), 3) == 0 + or memcmp(ptr, unicode_paragraph_sep.unsafe_ptr(), 3) == 0 + ): + continue + else: + return False + _ = next_line, unicode_line_sep, unicode_paragraph_sep + return True From 7699d93ff96d8f7da302758e665fd3d6d028865c Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:50:34 -0400 Subject: [PATCH 1230/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `math` module Mainly for `iota` function. MODULAR_ORIG_COMMIT_REV_ID: 08efacc03c2d4b4c9df46f5049838fb6f344361f --- stdlib/src/math/math.mojo | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 0407f5153c..b3e369078a 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -25,6 +25,8 @@ from sys._assembly import inlined_assembly from sys.ffi import _external_call_const from sys.info import bitwidthof, has_avx512f, simdwidthof, triple_is_nvidia_cuda +from memory import UnsafePointer + from builtin._math import * from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply, _modf @@ -979,7 +981,9 @@ fn iota[ return it.cast[type]() + offset -fn iota[type: DType](buff: DTypePointer[type], len: Int, offset: Int = 0): +fn iota[ + type: DType +](buff: UnsafePointer[Scalar[type]], len: Int, offset: Int = 0): """Fill the buffer with numbers ranging from offset to offset + len - 1, spaced by 1. @@ -1014,8 +1018,7 @@ fn iota[type: DType](v: List[Scalar[type]], offset: Int = 0): v: The vector to fill. offset: The value to fill at index 0. """ - var buff = rebind[DTypePointer[type]](v.data) - iota(buff, len(v), offset) + iota(v.data, len(v), offset) fn iota(v: List[Int], offset: Int = 0): @@ -1028,7 +1031,7 @@ fn iota(v: List[Int], offset: Int = 0): v: The vector to fill. offset: The value to fill at index 0. """ - var buff = DTypePointer[DType.index](v.data.bitcast[Scalar[DType.index]]()) + var buff = v.data.bitcast[Scalar[DType.index]]() iota(buff, len(v), offset=offset) From 0d7723f1c50eb238601295cadfe309d1c72b1546 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 00:39:43 -0400 Subject: [PATCH 1231/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in Python modules. MODULAR_ORIG_COMMIT_REV_ID: a9bdbe04775b0b4d585cec4db92867013b3ad814 --- stdlib/src/python/_cpython.mojo | 70 ++++++++++++++++----------------- stdlib/src/python/object.mojo | 6 +-- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index f7d8bfc70b..1a3f91b1a7 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -18,7 +18,7 @@ from sys import external_call from sys.arg import argv from sys.ffi import DLHandle -from memory import DTypePointer, UnsafePointer +from memory import UnsafePointer from utils import InlineArray, StringRef @@ -41,11 +41,11 @@ struct PyKeyValuePair: @value @register_passable("trivial") struct PyObjectPtr: - var value: DTypePointer[DType.int8] + var value: UnsafePointer[Int8] @always_inline("nodebug") fn __init__(inout self): - self.value = DTypePointer[DType.int8]() + self.value = UnsafePointer[Int8]() fn is_null(self) -> Bool: return int(self.value) == 0 @@ -308,9 +308,9 @@ struct CPython: inout self, name: StringRef, ) -> PyObjectPtr: - var r = self.lib.get_function[ - fn (DTypePointer[DType.uint8]) -> PyObjectPtr - ]("PyImport_ImportModule")(name.data) + var r = self.lib.get_function[fn (UnsafePointer[UInt8]) -> PyObjectPtr]( + "PyImport_ImportModule" + )(name.data) if self.logging_enabled: print( r._get_ptr_as_int(), @@ -332,9 +332,9 @@ struct CPython: `True` if the code executed successfully or `False` if the code raised an exception. """ - var status = self.lib.get_function[ - fn (DTypePointer[DType.uint8]) -> Int - ](StringRef("PyRun_SimpleString"))(strref.data) + var status = self.lib.get_function[fn (UnsafePointer[UInt8]) -> Int]( + StringRef("PyRun_SimpleString") + )(strref.data) # PyRun_SimpleString returns 0 on success and -1 if an exception was # raised. return status == 0 @@ -349,8 +349,8 @@ struct CPython: var result = PyObjectPtr( self.lib.get_function[ fn ( - DTypePointer[DType.uint8], Int32, PyObjectPtr, PyObjectPtr - ) -> DTypePointer[DType.int8] + UnsafePointer[UInt8], Int32, PyObjectPtr, PyObjectPtr + ) -> UnsafePointer[Int8] ]("PyRun_String")(strref.data, Int32(run_mode), globals, locals) ) if self.logging_enabled: @@ -376,7 +376,7 @@ struct CPython: self.lib.get_function[ fn ( PyObjectPtr, PyObjectPtr, PyObjectPtr - ) -> DTypePointer[DType.int8] + ) -> UnsafePointer[Int8] ]("PyEval_EvalCode")(co, globals, locals) ) self._inc_total_rc() @@ -390,7 +390,7 @@ struct CPython: ) -> PyObjectPtr: var r = self.lib.get_function[ fn ( - DTypePointer[DType.uint8], DTypePointer[DType.uint8], Int32 + UnsafePointer[UInt8], UnsafePointer[UInt8], Int32 ) -> PyObjectPtr ]("Py_CompileString")(strref.data, filename.data, Int32(compile_mode)) self._inc_total_rc() @@ -402,7 +402,7 @@ struct CPython: name: StringRef, ) -> PyObjectPtr: var r = self.lib.get_function[ - fn (PyObjectPtr, DTypePointer[DType.uint8]) -> PyObjectPtr + fn (PyObjectPtr, UnsafePointer[UInt8]) -> PyObjectPtr ]("PyObject_GetAttrString")(obj, name.data) if self.logging_enabled: print( @@ -421,7 +421,7 @@ struct CPython: inout self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr ) -> Int: var r = self.lib.get_function[ - fn (PyObjectPtr, DTypePointer[DType.uint8], PyObjectPtr) -> Int + fn (PyObjectPtr, UnsafePointer[UInt8], PyObjectPtr) -> Int ]("PyObject_SetAttrString")(obj, name.data, new_value) if self.logging_enabled: print( @@ -531,7 +531,7 @@ struct CPython: fn PyString_FromStringAndSize(inout self, strref: StringRef) -> PyObjectPtr: var r = self.lib.get_function[ fn ( - DTypePointer[DType.uint8], + UnsafePointer[UInt8], Int, UnsafePointer[C_char], ) -> PyObjectPtr @@ -566,13 +566,13 @@ struct CPython: fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: var value = self.lib.get_function[ - fn (PyObjectPtr) -> DTypePointer[DType.int8] + fn (PyObjectPtr) -> UnsafePointer[Int8] ]("PyModule_GetDict")(name.value) return PyObjectPtr {value: value} fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: var value = self.lib.get_function[ - fn (DTypePointer[DType.uint8]) -> DTypePointer[DType.int8] + fn (UnsafePointer[UInt8]) -> UnsafePointer[Int8] ]("PyImport_AddModule")(name.data) return PyObjectPtr {value: value} @@ -694,22 +694,20 @@ struct CPython: return not value.is_null() fn PyErr_Fetch(inout self) -> PyObjectPtr: - var type = DTypePointer[DType.int8]() - var value = DTypePointer[DType.int8]() - var traceback = DTypePointer[DType.int8]() + var type = UnsafePointer[Int8]() + var value = UnsafePointer[Int8]() + var traceback = UnsafePointer[Int8]() - var type_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of(type) - var value_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( - value - ) - var traceback_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( + var type_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(type) + var value_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(value) + var traceback_ptr = UnsafePointer[UnsafePointer[Int8]].address_of( traceback ) var func = self.lib.get_function[ fn ( - UnsafePointer[DTypePointer[DType.int8]], - UnsafePointer[DTypePointer[DType.int8]], - UnsafePointer[DTypePointer[DType.int8]], + UnsafePointer[UnsafePointer[Int8]], + UnsafePointer[UnsafePointer[Int8]], + UnsafePointer[UnsafePointer[Int8]], ) -> None ]("PyErr_Fetch")(type_ptr, value_ptr, traceback_ptr) var r = PyObjectPtr {value: value} @@ -817,20 +815,18 @@ struct CPython: fn PyDict_Next( inout self, dictionary: PyObjectPtr, p: Int ) -> PyKeyValuePair: - var key = DTypePointer[DType.int8]() - var value = DTypePointer[DType.int8]() + var key = UnsafePointer[Int8]() + var value = UnsafePointer[Int8]() var v = p var position = UnsafePointer[Int].address_of(v) - var value_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( - value - ) - var key_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of(key) + var value_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(value) + var key_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(key) var result = self.lib.get_function[ fn ( PyObjectPtr, UnsafePointer[Int], - UnsafePointer[DTypePointer[DType.int8]], - UnsafePointer[DTypePointer[DType.int8]], + UnsafePointer[UnsafePointer[Int8]], + UnsafePointer[UnsafePointer[Int8]], ) -> Int ]("PyDict_Next")( dictionary, diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 3dc95199d8..b449cf08ca 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -1186,7 +1186,7 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() return cpython.PyLong_AsLong(self.py_object.value) - fn unsafe_get_as_pointer[type: DType](self) -> DTypePointer[type]: + fn unsafe_get_as_pointer[type: DType](self) -> UnsafePointer[Scalar[type]]: """Convert a Python-owned and managed pointer into a Mojo pointer. Warning: converting from an integer to a pointer is unsafe! The @@ -1197,11 +1197,11 @@ struct PythonObject( type: The desired DType of the pointer. Returns: - A `DTypePointer` for the underlying Python data. + An `UnsafePointer` for the underlying Python data. """ var tmp = int(self) var result = UnsafePointer.address_of(tmp).bitcast[ - DTypePointer[type] + UnsafePointer[Scalar[type]] ]()[] _ = tmp return result From b6f91f30110f7ee3b2f1c6d9a6c9b87aab04def4 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 02:40:10 -0400 Subject: [PATCH 1232/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `_serialize` MODULAR_ORIG_COMMIT_REV_ID: 8a0d8d5411cfb95579d736af3cb01db1237ab7d3 --- stdlib/src/utils/_serialize.mojo | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 5c5bdfb4c5..fe87eafaf0 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -13,7 +13,7 @@ from pathlib import Path -from memory import AddressSpace, DTypePointer, bitcast +from memory import AddressSpace, bitcast alias _kStartTensorMarker = "[" alias _kEndTensorMarker = "]" @@ -23,8 +23,9 @@ alias _kCompactElemPerSide = _kCompactMaxElemsToPrint // 2 fn _serialize_elements_compact[ + type: DType, //, serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, -](ptr: DTypePointer, len: Int): +](ptr: UnsafePointer[Scalar[type], _], len: Int): serialize_fn(_kStartTensorMarker) if len < _kCompactMaxElemsToPrint: _serialize_elements_complete[serialize_fn=serialize_fn](ptr, len) @@ -43,20 +44,22 @@ fn _serialize_elements_compact[ fn _serialize_elements_complete[ + type: DType, //, serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, -](ptr: DTypePointer, len: Int): +](ptr: UnsafePointer[Scalar[type], _], len: Int): if len == 0: return - serialize_fn(Scalar.load(ptr)) + serialize_fn(Scalar[type].load(ptr)) for i in range(1, len): serialize_fn(", ") - serialize_fn(Scalar.load(ptr, i)) + serialize_fn(Scalar[type].load(ptr, i)) fn _serialize_elements[ + type: DType, //, serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, compact: Bool = False, -](ptr: DTypePointer, len: Int): +](ptr: UnsafePointer[Scalar[type], _], len: Int): @parameter if compact: _serialize_elements_compact[serialize_fn=serialize_fn](ptr, len) @@ -65,11 +68,12 @@ fn _serialize_elements[ fn _serialize[ + type: DType, //, serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, serialize_dtype: Bool = True, serialize_shape: Bool = True, serialize_end_line: Bool = True, -](ptr: DTypePointer, shape: List[Int]): +](ptr: UnsafePointer[Scalar[type], _], shape: List[Int]): var rank = len(shape) if rank == 0: if serialize_end_line: @@ -158,7 +162,7 @@ fn _serialize[ if serialize_dtype: serialize_fn(", dtype=") - serialize_fn(ptr.type) + serialize_fn(type) if serialize_shape: serialize_fn(", shape=") From 4a5c21fccb2bfde1d8408c991856de6a098e504a Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 16 Jul 2024 18:01:29 +0300 Subject: [PATCH 1233/2019] [stdlib] Do not sign extend unsigned types in `SIMD.__int__` The `pop.cast` op would sign extend when casting a smaller unsigned type to the `index` type, which would cause incorrect behavior for `int(UInt8(128))` and similar code. This patch fixes that by first upcasting to a larger unsigned scalar value and then converting to `Int`. Fixes https://github.com/modularml/mojo/issues/3065 MODULAR_ORIG_COMMIT_REV_ID: 111bb5545c02663ac0e7bf7cdb3678ea23c261fd --- stdlib/src/builtin/simd.mojo | 17 ++++++++++++++--- stdlib/test/builtin/test_simd.mojo | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d38e5281be..f4afdbf274 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -29,6 +29,7 @@ from sys import ( from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable +from builtin.dtype import _uint_type_of_width from builtin.hash import _hash_simd from memory import bitcast, DTypePointer @@ -1333,9 +1334,19 @@ struct SIMD[type: DType, size: Int]( The value as an integer. """ constrained[size == 1, "expected a scalar type"]() - return __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( - rebind[Scalar[type]](self).value - ) + + alias int_width = bitwidthof[Int]() + alias type_width = bitwidthof[type]() + + @parameter + if type.is_unsigned() and int_width > type_width: + # If we are casting up, prevent sign extension by first casting to + # a large unsigned + return self.cast[_uint_type_of_width[int_width]()]().__int__() + else: + return __mlir_op.`pop.cast`[ + _type = __mlir_type.`!pop.scalar` + ](rebind[Scalar[type]](self).value) @no_inline fn __str__(self) -> String: diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 36650881f6..97dc2cd7ae 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -48,6 +48,12 @@ def test_cast(): SIMD[DType.bool, 4](False, True, False, True), ) + var b: UInt16 = 128 + assert_equal(int(b.cast[DType.uint8]()), 128) + assert_equal(int(b.cast[DType.uint16]()), 128) + assert_equal(int(b.cast[DType.int8]()), -128) + assert_equal(int(b.cast[DType.int16]()), 128) + def test_simd_variadic(): assert_equal(str(SIMD[DType.index, 4](52, 12, 43, 5)), "[52, 12, 43, 5]") From 87d399b969fcdc6bbc28fe4e96694876c673aa93 Mon Sep 17 00:00:00 2001 From: Victor Guerra Date: Tue, 16 Jul 2024 11:35:30 -0500 Subject: [PATCH 1234/2019] [External] [stdlib] UTests for `IntLiteral`'s operators required by `Comparable` (#43484) [External] [stdlib] UTests for `IntLiteral`'s operators required by `Comparable` Testing the following dunder methods in `IntLiteral`: `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__` and `__ge__`. Note that we test directly dunder methods to avoid any unintended implicit conversions possibly introduced trough operator syntax sugar. Partially resolves #3145. Co-authored-by: Victor Guerra Closes modularml/mojo#3214 MODULAR_ORIG_COMMIT_REV_ID: d516752f3b9dce381e8b7b164b8e1a56054dbc0c --- stdlib/test/builtin/test_int_literal.mojo | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 6a4eb04edd..a8cf3d5ccf 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -115,6 +115,38 @@ def test_bool(): assert_false(IntLiteral.__as_bool__(0)) +def test_comparison(): + assert_true(IntLiteral(5).__lt__(10)) + assert_true(IntLiteral(-10).__lt__(-5)) + assert_false(IntLiteral(0).__lt__(0)) + assert_false(IntLiteral(10).__lt__(5)) + + assert_true(IntLiteral(5).__le__(10)) + assert_true(IntLiteral(-10).__le__(-5)) + assert_true(IntLiteral(0).__le__(0)) + assert_false(IntLiteral(10).__le__(5)) + + assert_true(IntLiteral(5).__eq__(5)) + assert_true(IntLiteral(0).__eq__(0)) + assert_false(IntLiteral(0).__eq__(1)) + assert_false(IntLiteral(5).__eq__(10)) + + assert_true(IntLiteral(5).__ne__(10)) + assert_true(IntLiteral(0).__ne__(1)) + assert_false(IntLiteral(5).__ne__(5)) + assert_false(IntLiteral(0).__ne__(0)) + + assert_true(IntLiteral(10).__gt__(5)) + assert_true(IntLiteral(-5).__gt__(-10)) + assert_false(IntLiteral(0).__gt__(0)) + assert_false(IntLiteral(5).__gt__(10)) + + assert_true(IntLiteral(10).__ge__(5)) + assert_true(IntLiteral(5).__ge__(5)) + assert_true(IntLiteral(-5).__ge__(-10)) + assert_false(IntLiteral(5).__ge__(10)) + + def main(): test_add() test_sub() @@ -129,3 +161,4 @@ def main(): test_abs() test_indexer() test_bool() + test_comparison() From 8446b8883c46798730387e8ae8b9f1da1c6a226a Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:39:28 -0400 Subject: [PATCH 1235/2019] [stdlib] Remove `DTypePointer` taking version of `memcmp` MODULAR_ORIG_COMMIT_REV_ID: 9af2f149fd0a16fb365506737ecf8038c557fd81 --- stdlib/src/memory/memory.mojo | 63 ++++------------- stdlib/test/memory/test_memory.mojo | 104 ++++++++++++---------------- 2 files changed, 58 insertions(+), 109 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index f4dd59e9db..01bf27b986 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -25,8 +25,6 @@ from sys import llvm_intrinsic, sizeof, triple_is_nvidia_cuda from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace -from .unsafe import DTypePointer - # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # @@ -43,10 +41,10 @@ fn _align_down(value: Int, alignment: Int) -> Int: @always_inline -fn _memcmp_impl_unconstrained( - s1: DTypePointer, s2: __type_of(s1), count: Int -) -> Int: - alias simd_width = simdwidthof[s1.type]() +fn _memcmp_impl_unconstrained[ + type: DType +](s1: UnsafePointer[Scalar[type], *_], s2: __type_of(s1), count: Int) -> Int: + alias simd_width = simdwidthof[type]() if count < simd_width: for i in range(count): var s1i = s1[i] @@ -87,46 +85,13 @@ fn _memcmp_impl_unconstrained( @always_inline -fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: - constrained[s1.type.is_integral(), "the input dtype must be integral"]() +fn _memcmp_impl[ + type: DType +](s1: UnsafePointer[Scalar[type], *_], s2: __type_of(s1), count: Int) -> Int: + constrained[type.is_integral(), "the input dtype must be integral"]() return _memcmp_impl_unconstrained(s1, s2, count) -@always_inline -fn memcmp(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: - """Compares two buffers. Both strings are assumed to be of the same length. - - Args: - s1: The first buffer address. - s2: The second buffer address. - count: The number of elements in the buffers. - - Returns: - Returns 0 if the bytes buffers are identical, 1 if s1 > s2, and -1 if - s1 < s2. The comparison is performed by the first different byte in the - buffer. - """ - - @parameter - if s1.type.is_floating_point(): - alias integral_type = _integral_type_of[s1.type]() - return _memcmp_impl( - s1.bitcast[integral_type](), s2.bitcast[integral_type](), count - ) - - var byte_count = count * sizeof[s1.type]() - - @parameter - if sizeof[s1.type]() >= sizeof[DType.int32](): - return _memcmp_impl( - s1.bitcast[DType.int32](), - s2.bitcast[DType.int32](), - byte_count // sizeof[DType.int32](), - ) - - return _memcmp_impl(s1, s2, count) - - @always_inline fn memcmp[ type: AnyType, address_space: AddressSpace @@ -155,13 +120,13 @@ fn memcmp[ @parameter if sizeof[type]() >= sizeof[DType.int32](): - var ds1 = DTypePointer[DType.int32, address_space](s1.bitcast[Int32]()) - var ds2 = DTypePointer[DType.int32, address_space](s2.bitcast[Int32]()) - return _memcmp_impl(ds1, ds2, byte_count // sizeof[DType.int32]()) + return _memcmp_impl( + s1.bitcast[Int32](), + s2.bitcast[Int32](), + byte_count // sizeof[DType.int32](), + ) - var ds1 = DTypePointer[DType.int8, address_space](s1.bitcast[Int8]()) - var ds2 = DTypePointer[DType.int8, address_space](s2.bitcast[Int8]()) - return _memcmp_impl(ds1, ds2, byte_count) + return _memcmp_impl(s1.bitcast[Int8](), s2.bitcast[Int8](), byte_count) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index e13d8707e4..e92c2b8f02 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -48,16 +48,7 @@ def test_memcpy(): var pair2 = Pair(0, 0) var src = UnsafePointer.address_of(pair1) - var dsrc = DTypePointer[DType.int8](src.bitcast[int8_pop]().address) - var dest = UnsafePointer.address_of(pair2) - var ddest = DTypePointer[DType.int8](dest.bitcast[int8_pop]().address) - - # DTypePointer test - memcpy(ddest, dsrc, sizeof[Pair]()) - - assert_equal(pair2.lo, 1) - assert_equal(pair2.hi, 2) # UnsafePointer test pair2.lo = 0 @@ -69,10 +60,10 @@ def test_memcpy(): @parameter def _test_memcpy_buf[size: Int](): - var buf = DTypePointer[DType.uint8]().alloc(size * 2) + var buf = UnsafePointer[UInt8]().alloc(size * 2) memset_zero(buf + size, size) - var src = DTypePointer[DType.uint8]().alloc(size * 2) - var dst = DTypePointer[DType.uint8]().alloc(size * 2) + var src = UnsafePointer[UInt8]().alloc(size * 2) + var dst = UnsafePointer[UInt8]().alloc(size * 2) for i in range(size * 2): buf[i] = src[i] = 2 dst[i] = 0 @@ -98,8 +89,8 @@ def test_memcpy(): def test_memcpy_dtype(): - var a = DTypePointer[DType.int32].alloc(4) - var b = DTypePointer[DType.int32].alloc(4) + var a = UnsafePointer[Int32].alloc(4) + var b = UnsafePointer[Int32].alloc(4) for i in range(4): a[i] = i b[i] = -1 @@ -125,25 +116,18 @@ def test_memcmp(): var pair2 = Pair(1, 2) var ptr1 = UnsafePointer.address_of(pair1) - var dptr1 = DTypePointer[DType.int8](ptr1.bitcast[int8_pop]().address) - var ptr2 = UnsafePointer.address_of(pair2) - var dptr2 = DTypePointer[DType.int8](ptr2.bitcast[int8_pop]().address) - - var errors1 = memcmp(dptr1, dptr2, 1) - - assert_equal(errors1, 0) - var errors2 = memcmp(ptr1, ptr2, 1) + var errors = memcmp(ptr1, ptr2, 1) - assert_equal(errors2, 0) + assert_equal(errors, 0) _ = pair1 _ = pair2 def test_memcmp_overflow(): - var p1 = DTypePointer[DType.int8].alloc(1) - var p2 = DTypePointer[DType.int8].alloc(1) + var p1 = UnsafePointer[Int8].alloc(1) + var p2 = UnsafePointer[Int8].alloc(1) Scalar.store(p1, -120) Scalar.store(p2, 120) @@ -157,8 +141,8 @@ def test_memcmp_overflow(): def test_memcmp_simd(): var length = simdwidthof[DType.int8]() + 10 - var p1 = DTypePointer[DType.int8].alloc(length) - var p2 = DTypePointer[DType.int8].alloc(length) + var p1 = UnsafePointer[Int8].alloc(length) + var p2 = UnsafePointer[Int8].alloc(length) memset_zero(p1, length) memset_zero(p2, length) Scalar.store(p1, 120) @@ -193,8 +177,8 @@ def test_memcmp_extensive[ var ptr1 = UnsafePointer[Scalar[type]].alloc(count) var ptr2 = UnsafePointer[Scalar[type]].alloc(count) - var dptr1 = DTypePointer[type].alloc(count) - var dptr2 = DTypePointer[type].alloc(count) + var dptr1 = UnsafePointer[Scalar[type]].alloc(count) + var dptr2 = UnsafePointer[Scalar[type]].alloc(count) for i in range(count): ptr1[i] = i @@ -305,13 +289,13 @@ def test_memset(): assert_equal(pair.lo, 0) assert_equal(pair.hi, 0) - var buf0 = DTypePointer[DType.int32].alloc(2) + var buf0 = UnsafePointer[Int32].alloc(2) memset(buf0, 1, 2) assert_equal(Scalar.load(buf0, 0), 16843009) memset(buf0, -1, 2) assert_equal(Scalar.load(buf0, 0), -1) - var buf1 = DTypePointer[DType.int8].alloc(2) + var buf1 = UnsafePointer[Int8].alloc(2) memset(buf1, 5, 2) assert_equal(Scalar.load(buf1, 0), 5) _ = pair @@ -328,10 +312,10 @@ def test_pointer_string(): def test_dtypepointer_string(): - var nullptr = DTypePointer[DType.float32]() + var nullptr = UnsafePointer[Float32]() assert_equal(str(nullptr), "0x0") - var ptr = DTypePointer[DType.float32].alloc(1) + var ptr = UnsafePointer[Float32].alloc(1) assert_true(str(ptr).startswith("0x")) assert_not_equal(str(ptr), "0x0") ptr.free() @@ -373,13 +357,13 @@ def test_pointer_refitem_pair(): def test_dtypepointer_gather(): - var ptr = DTypePointer[DType.float32].alloc(4) - SIMD.store(ptr, 0, SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0)) + var ptr = UnsafePointer[Float32].alloc(4) + SIMD.store(ptr, 0, SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 3.0)) @parameter def _test_gather[ width: Int - ](offset: SIMD[_, width], desired: SIMD[ptr.type, width]): + ](offset: SIMD[_, width], desired: SIMD[ptr.type.type, width]): var actual = ptr.gather(offset) assert_almost_equal( actual, desired, msg="_test_gather", atol=0.0, rtol=0.0 @@ -391,8 +375,8 @@ def test_dtypepointer_gather(): ]( offset: SIMD[_, width], mask: SIMD[DType.bool, width], - default: SIMD[ptr.type, width], - desired: SIMD[ptr.type, width], + default: SIMD[ptr.type.type, width], + desired: SIMD[ptr.type.type, width], ): var actual = ptr.gather(offset, mask, default) assert_almost_equal( @@ -400,15 +384,15 @@ def test_dtypepointer_gather(): ) var offset = SIMD[DType.int64, 8](3, 0, 2, 1, 2, 0, 3, 1) - var desired = SIMD[ptr.type, 8](3.0, 0.0, 2.0, 1.0, 2.0, 0.0, 3.0, 1.0) + var desired = SIMD[ptr.type.type, 8](3.0, 0.0, 2.0, 1.0, 2.0, 0.0, 3.0, 1.0) _test_gather[1](UInt16(2), 2.0) _test_gather(offset.cast[DType.uint32]().slice[2](), desired.slice[2]()) _test_gather(offset.cast[DType.uint64]().slice[4](), desired.slice[4]()) var mask = (offset >= 0) & (offset < 3) - var default = SIMD[ptr.type, 8](-1.0) - desired = SIMD[ptr.type, 8](-1.0, 0.0, 2.0, 1.0, 2.0, 0.0, -1.0, 1.0) + var default = SIMD[ptr.type.type, 8](-1.0) + desired = SIMD[ptr.type.type, 8](-1.0, 0.0, 2.0, 1.0, 2.0, 0.0, -1.0, 1.0) _test_masked_gather[1](Int16(2), False, -1.0, -1.0) _test_masked_gather[1](Int32(2), True, -1.0, 2.0) @@ -418,16 +402,16 @@ def test_dtypepointer_gather(): def test_dtypepointer_scatter(): - var ptr = DTypePointer[DType.float32].alloc(4) - SIMD.store(ptr, 0, SIMD[ptr.type, 4](0.0)) + var ptr = UnsafePointer[Float32].alloc(4) + SIMD.store(ptr, 0, SIMD[ptr.type.type, 4](0.0)) @parameter def _test_scatter[ width: Int ]( offset: SIMD[_, width], - val: SIMD[ptr.type, width], - desired: SIMD[ptr.type, 4], + val: SIMD[ptr.type.type, width], + desired: SIMD[ptr.type.type, 4], ): ptr.scatter(offset, val) var actual = SIMD[size=4].load(ptr, 0) @@ -440,9 +424,9 @@ def test_dtypepointer_scatter(): width: Int ]( offset: SIMD[_, width], - val: SIMD[ptr.type, width], + val: SIMD[ptr.type.type, width], mask: SIMD[DType.bool, width], - desired: SIMD[ptr.type, 4], + desired: SIMD[ptr.type.type, 4], ): ptr.scatter(offset, val, mask) var actual = SIMD[size=4].load(ptr, 0) @@ -450,37 +434,37 @@ def test_dtypepointer_scatter(): actual, desired, msg="_test_masked_scatter", atol=0.0, rtol=0.0 ) - _test_scatter[1](UInt16(2), 2.0, SIMD[ptr.type, 4](0.0, 0.0, 2.0, 0.0)) + _test_scatter[1](UInt16(2), 2.0, SIMD[ptr.type.type, 4](0.0, 0.0, 2.0, 0.0)) _test_scatter( # Test with repeated offsets SIMD[DType.uint32, 4](1, 1, 1, 1), - SIMD[ptr.type, 4](-1.0, 2.0, -2.0, 1.0), - SIMD[ptr.type, 4](0.0, 1.0, 2.0, 0.0), + SIMD[ptr.type.type, 4](-1.0, 2.0, -2.0, 1.0), + SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 0.0), ) _test_scatter( SIMD[DType.uint64, 4](3, 2, 1, 0), - SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0), - SIMD[ptr.type, 4](3.0, 2.0, 1.0, 0.0), + SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 3.0), + SIMD[ptr.type.type, 4](3.0, 2.0, 1.0, 0.0), ) - SIMD.store(ptr, 0, SIMD[ptr.type, 4](0.0)) + SIMD.store(ptr, 0, SIMD[ptr.type.type, 4](0.0)) _test_masked_scatter[1]( - Int16(2), 2.0, False, SIMD[ptr.type, 4](0.0, 0.0, 0.0, 0.0) + Int16(2), 2.0, False, SIMD[ptr.type.type, 4](0.0, 0.0, 0.0, 0.0) ) _test_masked_scatter[1]( - Int32(2), 2.0, True, SIMD[ptr.type, 4](0.0, 0.0, 2.0, 0.0) + Int32(2), 2.0, True, SIMD[ptr.type.type, 4](0.0, 0.0, 2.0, 0.0) ) _test_masked_scatter( # Test with repeated offsets SIMD[DType.int64, 4](1, 1, 1, 1), - SIMD[ptr.type, 4](-1.0, 2.0, -2.0, 1.0), + SIMD[ptr.type.type, 4](-1.0, 2.0, -2.0, 1.0), SIMD[DType.bool, 4](True, True, True, False), - SIMD[ptr.type, 4](0.0, -2.0, 2.0, 0.0), + SIMD[ptr.type.type, 4](0.0, -2.0, 2.0, 0.0), ) _test_masked_scatter( SIMD[DType.index, 4](3, 2, 1, 0), - SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0), + SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 3.0), SIMD[DType.bool, 4](True, False, True, True), - SIMD[ptr.type, 4](3.0, 2.0, 2.0, 0.0), + SIMD[ptr.type.type, 4](3.0, 2.0, 2.0, 0.0), ) ptr.free() @@ -530,7 +514,7 @@ def test_memcpy_unsafe_pointer(): def test_indexing(): - var ptr = DTypePointer[DType.float32].alloc(4) + var ptr = UnsafePointer[Float32].alloc(4) for i in range(4): ptr[i] = i From cd999089322cf7a64b185f9afd80f7e7e8c42177 Mon Sep 17 00:00:00 2001 From: Victor Guerra Date: Tue, 16 Jul 2024 11:39:58 -0500 Subject: [PATCH 1236/2019] [External] [stdlib] UTests for `FloatLiteral`'s operators required by `Comparable` (#43485) [External] [stdlib] UTests for `FloatLiteral`'s operators required by `Comparable` Testing the following dunder methods in `FloatLiteral`: `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__` and `__ge__`. Note that we test directly dunder methods to avoid any unintended implicit conversions possibly introduced trough operator syntax sugar. Partially resolves #3145 Co-authored-by: Victor Guerra Closes modularml/mojo#3216 MODULAR_ORIG_COMMIT_REV_ID: fc13864bfb0e4ffd5152f9a4af9940722933b4ab --- stdlib/test/builtin/test_float_literal.mojo | 38 ++++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index f6457eacc3..a438ce9d1d 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -165,14 +165,6 @@ def test_bool(): assert_true(FloatLiteral.__as_bool__(2.0)) -def test_equality(): - # TODO: add tests for special values - assert_true(FloatLiteral.__eq__(4.4, 4.4)) - assert_false(FloatLiteral.__eq__(4.4, 42.0)) - assert_false(FloatLiteral.__ne__(4.4, 4.4)) - assert_true(FloatLiteral.__ne__(4.4, 42.0)) - - def test_is_special_value(): assert_true(nan.is_nan()) assert_false(neg_zero.is_nan()) @@ -192,6 +184,34 @@ def test_abs(): assert_equal(FloatLiteral.__abs__(neg_inf), inf) +def test_comparison(): + assert_true(FloatLiteral.__lt__(4.4, 10.4)) + assert_true(FloatLiteral.__lt__(-10.4, -4.4)) + assert_false(FloatLiteral.__lt__(0.0, 0.0)) + assert_false(FloatLiteral.__lt__(10.4, 4.4)) + + assert_true(FloatLiteral.__le__(4.4, 10.4)) + assert_true(FloatLiteral.__le__(-10.4, 4.4)) + assert_true(FloatLiteral.__le__(0.0, 0.0)) + assert_false(FloatLiteral.__le__(10.4, 4.4)) + + # TODO: add tests for special values + assert_true(FloatLiteral.__eq__(4.4, 4.4)) + assert_false(FloatLiteral.__eq__(4.4, 42.0)) + assert_false(FloatLiteral.__ne__(4.4, 4.4)) + assert_true(FloatLiteral.__ne__(4.4, 42.0)) + + assert_true(FloatLiteral.__gt__(10.4, 4.4)) + assert_true(FloatLiteral.__gt__(-4.4, -10.4)) + assert_false(FloatLiteral.__gt__(0.0, 0.0)) + assert_false(FloatLiteral.__gt__(4.4, 10.4)) + + assert_true(FloatLiteral.__ge__(10.4, 4.4)) + assert_true(FloatLiteral.__ge__(-4.4, -10.4)) + assert_true(FloatLiteral.__ge__(4.4, 4.4)) + assert_false(FloatLiteral.__ge__(4.4, 10.4)) + + def main(): test_ceil() test_floor() @@ -203,6 +223,6 @@ def main(): test_div_mod() test_int_conversion() test_bool() - test_equality() test_is_special_value() test_abs() + test_comparison() From 7fda40716a26700db024dc58bdaaec6bdbdda6ae Mon Sep 17 00:00:00 2001 From: Ethan Wu Date: Tue, 16 Jul 2024 11:44:39 -0500 Subject: [PATCH 1237/2019] [External] [stdlib] Remove incorrect alignment constraint (#43451) [External] [stdlib] Remove incorrect alignment constraint I believe the constraint ```mojo constrained[ sizeof_t % alignment == 0, "size must be a multiple of alignment" ]() ``` is a misunderstanding of that `size_t*count` (size of bytes) must be a multiple of `alignment`. This is not compile time checkable since `count` is a run-time value. The following should be possible ```mojo var ptr = UnsafePointer[Int64].alloc[alignment=64](8) ``` Notably, `DTypePointer` doesn't have this constraint. Co-authored-by: Ethan Wu Closes modularml/mojo#3236 MODULAR_ORIG_COMMIT_REV_ID: 13394e4e2d8934cee71ce3bcdd82b881448a566a --- stdlib/src/memory/unsafe_pointer.mojo | 3 --- stdlib/test/memory/test_unsafepointer.mojo | 11 +++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 04d1898aa5..f0ac4007f7 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -175,9 +175,6 @@ struct UnsafePointer[ constrained[sizeof_t > 0, "size must be greater than zero"]() constrained[alignment > 0, "alignment must be greater than zero"]() - constrained[ - sizeof_t % alignment == 0, "size must be a multiple of alignment" - ]() return _malloc[T, address_space=address_space]( sizeof_t * count, alignment=alignment diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index d0563c4993..36e725b446 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -236,6 +236,16 @@ def test_bool(): ptr.free() +def test_alignment(): + var ptr = UnsafePointer[Int64].alloc[alignment=64](8) + assert_equal(int(ptr) % 64, 0) + ptr.free() + + var ptr_2 = UnsafePointer[UInt8].alloc[alignment=32](32) + assert_equal(int(ptr_2) % 32, 0) + ptr_2.free() + + def main(): test_address_of() @@ -255,3 +265,4 @@ def main(): test_unsafepointer_address_space() test_indexing() test_bool() + test_alignment() From f83ae37b1fb480c7bc8077bee6b3b2a741afb680 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Tue, 16 Jul 2024 12:19:53 -0500 Subject: [PATCH 1238/2019] [External] [stdlib] Implement `collections.Counter` - Part 2 (#43487) [External] [stdlib] Implement `collections.Counter` - Part 2 Support for Python's [collections.Counter](https://docs.python.org/3/library/collections.html#collections.Counter) - Part 2 Pending to match Python signature: - More `Dict` methods, like `popitem()`, `fromkeys()` etc. - Copy constructor - Implement `__sub__` and `__isub__` - Implement `__and__`, `__iand__` Pending for a part 3: - `__or__`, `__ior__` and `__neg__` Co-authored-by: Manuel Saelices Closes modularml/mojo#3193 MODULAR_ORIG_COMMIT_REV_ID: 0974c4df03b70ff48a3986cc5d62438e230582de --- stdlib/src/collections/counter.mojo | 103 +++++++++++++++++++++- stdlib/test/collections/test_counter.mojo | 98 ++++++++++++++++++++ 2 files changed, 199 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 6afbc02227..01eb9f62bb 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -59,6 +59,36 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): var item = item_ref[] self._data[item] = self._data.get(item, 0) + 1 + @always_inline + fn __init__(inout self, *, other: Self): + """Create a new Counter by copying another Counter. + + Args: + other: The Counter to copy. + """ + self._data = Dict[V, Int](other=other._data) + + @staticmethod + fn fromkeys(keys: List[V], value: Int) -> Self: + """Create a new Counter from a list of keys and a default value. + + Args: + keys: The keys to create the Counter from. + value: The default value to associate with each key. + + Returns: + A new Counter with the keys and default value. + """ + debug_assert( + value >= 0, + "value must be non-negative", + ) + var result = Counter[V]() + for key_ref in keys: + var key = key_ref[] + result[key] = value + return result + # ===------------------------------------------------------------------=== # # Operator dunders # ===------------------------------------------------------------------=== # @@ -110,7 +140,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """Returns the number of elements currently stored in the Counter. Returns: - The number of elements currently stored in the Counter. + The number of elements in the Counter. """ return len(self._data) @@ -245,6 +275,63 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): self.update(other) self._keep_positive() + fn __sub__(self, other: Self) -> Self: + """Subtract counts, but keep only results with positive counts. + + Args: + other: The other Counter to subtract from this Counter. + + Returns: + A new Counter with the counts from the other Counter subtracted from + this Counter. + """ + var result = Counter[V](other=self) + + result.subtract(other) + + return +result^ # Remove zero and negative counts + + fn __isub__(inout self, other: Self) raises: + """Subtract counts from another Counter from this Counter. + + Args: + other: The other Counter to subtract from this Counter. + """ + self.subtract(other) + self._keep_positive() + + fn __and__(self, other: Self) raises -> Self: + """Intersection: keep common elements with the minimum count. + + Args: + other: The other Counter to intersect with. + + Returns: + A new Counter with the common elements and the minimum count of + the two Counters. + """ + var result = Counter[V]() + + for key_ref in self.keys(): + var key = key_ref[] + if key in other: + result[key] = min(self[key], other[key]) + + return result^ + + fn __iand__(inout self, other: Self) raises: + """Intersection: keep common elements with the minimum count. + + Args: + other: The other Counter to intersect with. + """ + for key_ref in self.keys(): + var key = key_ref[] + if key not in other: + _ = self.pop(key) + else: + self[key] = min(self[key], other[key]) + fn _keep_positive(inout self) raises: """Remove zero and negative counts from the Counter.""" for key_ref in self.keys(): @@ -257,7 +344,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): # ===------------------------------------------------------------------=== # fn __pos__(self) -> Self: - """Return a shallow copy of the Counter. + """Return a shallow copy of the Counter, stripping non-positive counts. Returns: A shallow copy of the Counter. @@ -357,6 +444,18 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """Remove all elements from the Counter.""" self._data.clear() + fn popitem(inout self) raises -> CountTuple[V]: + """Remove and return an arbitrary (key, value) pair from the Counter. + + Returns: + A CountTuple containing the key and value of the removed item. + + Raises: + "KeyError" if the Counter is empty. + """ + var item_ref = self._data.popitem() + return CountTuple[V](item_ref.key, item_ref.value) + # Special methods for counter fn total(self) -> Int: diff --git a/stdlib/test/collections/test_counter.mojo b/stdlib/test/collections/test_counter.mojo index d02245a9b4..e87a849771 100644 --- a/stdlib/test/collections/test_counter.mojo +++ b/stdlib/test/collections/test_counter.mojo @@ -17,6 +17,28 @@ from collections.counter import Counter from testing import assert_equal, assert_false, assert_raises, assert_true +def test_and(): + var c1 = Counter[String]() + c1["a"] = 1 + c1["b"] = 2 + + var c2 = Counter[String]() + c2["b"] = 3 + c2["c"] = 4 + + var c3 = c1 & c2 + + assert_equal(c3["a"], 0) + assert_equal(c3["b"], 2) + assert_equal(c3["c"], 0) + + c1 &= c2 + + assert_equal(c1["a"], 0) + assert_equal(c1["b"], 2) + assert_equal(c1["c"], 0) + + def test_bool(): var c = Counter[String]() assert_false(c) @@ -47,6 +69,23 @@ def test_contains(): assert_false("c" in c) +def test_copy(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = 2 + + var copy = Counter[String](other=c) + + assert_equal(copy["a"], 1) + assert_equal(copy["b"], 2) + assert_equal(len(copy), 2) + + c["c"] = 3 + + assert_equal(copy["c"], 0) + assert_equal(len(copy), 2) + + def test_counter_construction(): _ = Counter[Int]() _ = Counter[Int](List[Int]()) @@ -62,6 +101,16 @@ def test_counter_getitem(): assert_equal(c[5], 0) +def test_fromkeys(): + var keys = List[String]("a", "b", "c") + var c = Counter[String].fromkeys(keys, 3) + + assert_equal(c["a"], 3) + assert_equal(c["b"], 3) + assert_equal(c["c"], 3) + assert_equal(len(c), 3) + + def test_get(): var counter = Counter[String]() counter["a"] = 1 @@ -300,6 +349,34 @@ def test_substract(): assert_equal(c1["c"], -3) +def test_sub(): + var c1 = Counter[String]() + c1["a"] = 4 + c1["b"] = 2 + c1["c"] = 0 + + var c2 = Counter[String]() + c2["a"] = 1 + c2["b"] = -2 + c2["c"] = 3 + + var c3 = c1 - c2 + + assert_equal(c3["a"], 3) + assert_equal(c3["b"], 4) + assert_equal(c3["c"], -3) + # Check that the original counters are not modified + assert_equal(c1["a"], 4) + assert_equal(c1["b"], 2) + assert_equal(c1["c"], 0) + + c2 -= c1 + + assert_equal(c2["a"], -3) + assert_equal(c2["b"], -4) + assert_equal(c2["c"], 3) + + def test_counter_setitem(): c = Counter[Int]() c[1] = 1 @@ -323,16 +400,36 @@ def test_pop(): assert_equal(c, 3) +def test_popitem(): + var counter = Counter[String]() + counter["a"] = 1 + counter["b"] = 2 + + var item = counter.popitem() + assert_equal(item[0][String], "b") + assert_equal(item[1][Int], 2) + + item = counter.popitem() + assert_equal(item[0][String], "a") + assert_equal(item[1][Int], 1) + + with assert_raises(): + counter.popitem() + + def main(): test_add() + test_and() test_bool() test_clear() test_contains() + test_copy() test_counter_construction() test_counter_getitem() test_counter_setitem() test_elements() test_eq_and_ne() + test_fromkeys() test_get() test_iter() test_iter_keys() @@ -343,6 +440,7 @@ def main(): test_lt_le_gt_and_ge() test_most_common() test_pop() + test_popitem() test_substract() test_total() test_update() From 513b6b8371df4908107238043590b72870c0ffa7 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 16 Jul 2024 11:10:07 -0700 Subject: [PATCH 1239/2019] [mojo-stdlib] Rewrite some functions that use `__call_location` (NFC) Ensure the bodies of these functions don't get inlined only because `__call_location` requires them to be inlined. MODULAR_ORIG_COMMIT_REV_ID: e862ea163807f5b391b386bfc66132b5c756768e --- stdlib/src/pathlib/path.mojo | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 22e3a259be..b07e4835a8 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -18,7 +18,7 @@ from os import PathLike, listdir, stat_result from sys import os_is_windows from sys.ffi import C_char -from builtin._location import __call_location +from builtin._location import __call_location, _SourceLocation from memory import stack_allocation from utils import StringRef @@ -55,7 +55,11 @@ fn _dir_of_current_file() raises -> Path: Returns: The directory the file calling is at. """ - var file_name = __call_location().file_name + return _dir_of_current_file_impl(__call_location().file_name) + + +@no_inline +fn _dir_of_current_file_impl(file_name: StringLiteral) raises -> Path: var i = str(file_name).rfind(DIR_SEPARATOR) return Path(str(file_name)[0:i]) From 33d230df164a7e69875f8840a1416d719088cfe6 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 16 Jul 2024 21:37:41 +0300 Subject: [PATCH 1240/2019] [stdlib] Remove the `SIMD.{add,mul,sub}_with_overflow` methods Since they were unused. MODULAR_ORIG_COMMIT_REV_ID: 6ff9f93f9f8e715b7a59f05405b6de2f88aa68d2 --- docs/changelog.md | 2 + stdlib/src/builtin/simd.mojo | 99 -------------- stdlib/test/builtin/test_simd.mojo | 205 ----------------------------- 3 files changed, 2 insertions(+), 304 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d6c3679cd8..86f843e2f7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -480,6 +480,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - Removed the Mojo Language Server warnings for unused function arguments. +- Removed the `SIMD.{add,mul,sub}_with_overflow` methods. + ### 🛠️ Fixed - Fixed a crash in the Mojo Language Server when importing the current file. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f4afdbf274..b6e7aa5a80 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1673,105 +1673,6 @@ struct SIMD[type: DType, size: Int]( self ) - @always_inline - fn add_with_overflow(self, rhs: Self) -> (Self, Self._Mask): - """Computes `self + rhs` and a mask of which indices overflowed. - - Args: - rhs: The rhs value. - - Returns: - A tuple with the results of the operation and a mask for overflows. - The first is a new vector whose element at position `i` is computed - as `self[i] + rhs[i]`. The second item is a vector of booleans where - a `1` at position `i` represents `self[i] + rhs[i]` overflowed. - """ - constrained[type.is_integral()]() - - @parameter - if type.is_signed(): - var result = llvm_intrinsic[ - "llvm.sadd.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - else: - var result = llvm_intrinsic[ - "llvm.uadd.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - - @always_inline - fn sub_with_overflow(self, rhs: Self) -> (Self, Self._Mask): - """Computes `self - rhs` and a mask of which indices overflowed. - - Args: - rhs: The rhs value. - - Returns: - A tuple with the results of the operation and a mask for overflows. - The first is a new vector whose element at position `i` is computed - as `self[i] - rhs[i]`. The second item is a vector of booleans where - a `1` at position `i` represents `self[i] - rhs[i]` overflowed. - """ - constrained[type.is_integral()]() - - @parameter - if type.is_signed(): - var result = llvm_intrinsic[ - "llvm.ssub.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - else: - var result = llvm_intrinsic[ - "llvm.usub.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - - @always_inline - fn mul_with_overflow(self, rhs: Self) -> (Self, Self._Mask): - """Computes `self * rhs` and a mask of which indices overflowed. - - Args: - rhs: The rhs value. - - Returns: - A tuple with the results of the operation and a mask for overflows. - The first is a new vector whose element at position `i` is computed - as `self[i] * rhs[i]`. The second item is a vector of booleans where - a `1` at position `i` represents `self[i] * rhs[i]` overflowed. - """ - constrained[type.is_integral()]() - - @parameter - if type.is_signed(): - var result = llvm_intrinsic[ - "llvm.smul.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - else: - var result = llvm_intrinsic[ - "llvm.umul.with.overflow", - _RegisterPackType[Self, Self._Mask], - Self, - Self, - ](self, rhs) - return (result[0], result[1]) - # TODO: Move to global function. @always_inline("nodebug") fn fma(self, multiplier: Self, accumulator: Self) -> Self: diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 97dc2cd7ae..1be81ceaab 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -958,208 +958,6 @@ def test_limits(): test_integral_overflow[DType.uint64]() -def test_add_with_overflow(): - var value_u8: UInt8 - var overflowed_u8: Scalar[DType.bool] - value_u8, overflowed_u8 = UInt8(UInt8.MAX).add_with_overflow(1) - assert_equal(value_u8, UInt8.MIN) - assert_equal(overflowed_u8, True) - - var value_u8x4: SIMD[DType.uint8, 4] - var overflowed_u8x4: SIMD[DType.bool, 4] - value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( - 1, UInt8.MAX, 1, UInt8.MAX - ).add_with_overflow(SIMD[DType.uint8, 4](0, 1, 0, 1)) - assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, UInt8.MIN, 1, UInt8.MIN)) - assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) - - var value_i8: Int8 - var overflowed_i8: Scalar[DType.bool] - value_i8, overflowed_i8 = Int8(Int8.MAX).add_with_overflow(1) - assert_equal(value_i8, Int8.MIN) - assert_equal(overflowed_i8, True) - - var value_i8x4: SIMD[DType.int8, 4] - var overflowed_i8x4: SIMD[DType.bool, 4] - value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( - 1, Int8.MAX, 1, Int8.MAX - ).add_with_overflow(SIMD[DType.int8, 4](0, 1, 0, 1)) - assert_equal(value_i8x4, SIMD[DType.int8, 4](1, Int8.MIN, 1, Int8.MIN)) - assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) - - var value_u32: UInt32 - var overflowed_u32: Scalar[DType.bool] - value_u32, overflowed_u32 = UInt32(UInt32.MAX).add_with_overflow(1) - assert_equal(value_u32, UInt32.MIN) - assert_equal(overflowed_u32, True) - - var value_u32x4: SIMD[DType.uint32, 4] - var overflowed_u32x4: SIMD[DType.bool, 4] - value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( - 1, UInt32.MAX, 1, UInt32.MAX - ).add_with_overflow(SIMD[DType.uint32, 4](0, 1, 0, 1)) - assert_equal( - value_u32x4, SIMD[DType.uint32, 4](1, UInt32.MIN, 1, UInt32.MIN) - ) - assert_equal( - overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) - ) - - var value_i32: Int32 - var overflowed_i32: Scalar[DType.bool] - value_i32, overflowed_i32 = Int32(Int32.MAX).add_with_overflow(1) - assert_equal(value_i32, Int32.MIN) - assert_equal(overflowed_i32, True) - - var value_i32x4: SIMD[DType.int32, 4] - var overflowed_i32x4: SIMD[DType.bool, 4] - value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( - 1, Int32.MAX, 1, Int32.MAX - ).add_with_overflow(SIMD[DType.int32, 4](0, 1, 0, 1)) - assert_equal(value_i32x4, SIMD[DType.int32, 4](1, Int32.MIN, 1, Int32.MIN)) - assert_equal( - overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) - ) - - -def test_sub_with_overflow(): - var value_u8: UInt8 - var overflowed_u8: Scalar[DType.bool] - value_u8, overflowed_u8 = UInt8(UInt8.MIN).sub_with_overflow(1) - assert_equal(value_u8, UInt8.MAX) - assert_equal(overflowed_u8, True) - - var value_u8x4: SIMD[DType.uint8, 4] - var overflowed_u8x4: SIMD[DType.bool, 4] - value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( - 1, UInt8.MIN, 1, UInt8.MIN - ).sub_with_overflow(SIMD[DType.uint8, 4](0, 1, 0, 1)) - assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, UInt8.MAX, 1, UInt8.MAX)) - assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) - - var value_i8: Int8 - var overflowed_i8: Scalar[DType.bool] - value_i8, overflowed_i8 = Int8(Int8.MIN).sub_with_overflow(1) - assert_equal(value_i8, Int8.MAX) - assert_equal(overflowed_i8, True) - - var value_i8x4: SIMD[DType.int8, 4] - var overflowed_i8x4: SIMD[DType.bool, 4] - value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( - 1, Int8.MIN, 1, Int8.MIN - ).sub_with_overflow(SIMD[DType.int8, 4](0, 1, 0, 1)) - assert_equal(value_i8x4, SIMD[DType.int8, 4](1, Int8.MAX, 1, Int8.MAX)) - assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) - - var value_u32: UInt32 - var overflowed_u32: Scalar[DType.bool] - value_u32, overflowed_u32 = UInt32(UInt32.MIN).sub_with_overflow(1) - assert_equal(value_u32, UInt32.MAX) - assert_equal(overflowed_u32, True) - - var value_u32x4: SIMD[DType.uint32, 4] - var overflowed_u32x4: SIMD[DType.bool, 4] - value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( - 1, UInt32.MIN, 1, UInt32.MIN - ).sub_with_overflow(SIMD[DType.uint32, 4](0, 1, 0, 1)) - assert_equal( - value_u32x4, SIMD[DType.uint32, 4](1, UInt32.MAX, 1, UInt32.MAX) - ) - assert_equal( - overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) - ) - - var value_i32: Int32 - var overflowed_i32: Scalar[DType.bool] - value_i32, overflowed_i32 = Int32(Int32.MIN).sub_with_overflow(1) - assert_equal(value_i32, Int32.MAX) - assert_equal(overflowed_i32, True) - - var value_i32x4: SIMD[DType.int32, 4] - var overflowed_i32x4: SIMD[DType.bool, 4] - value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( - 1, Int32.MIN, 1, Int32.MIN - ).sub_with_overflow(SIMD[DType.int32, 4](0, 1, 0, 1)) - assert_equal(value_i32x4, SIMD[DType.int32, 4](1, Int32.MAX, 1, Int32.MAX)) - assert_equal( - overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) - ) - - -def test_mul_with_overflow(): - alias uint8_max_x2 = 254 - var value_u8: UInt8 - var overflowed_u8: Scalar[DType.bool] - value_u8, overflowed_u8 = UInt8(UInt8.MAX).mul_with_overflow(2) - assert_equal(value_u8, uint8_max_x2) - assert_equal(overflowed_u8, True) - - var value_u8x4: SIMD[DType.uint8, 4] - var overflowed_u8x4: SIMD[DType.bool, 4] - value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( - 1, UInt8.MAX, 1, UInt8.MAX - ).mul_with_overflow(SIMD[DType.uint8, 4](0, 2, 0, 2)) - assert_equal( - value_u8x4, SIMD[DType.uint8, 4](0, uint8_max_x2, 0, uint8_max_x2) - ) - assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) - - alias int8_max_x2 = -2 - var value_i8: Int8 - var overflowed_i8: Scalar[DType.bool] - value_i8, overflowed_i8 = Int8(Int8.MAX).mul_with_overflow(2) - assert_equal(value_i8, int8_max_x2) - assert_equal(overflowed_i8, True) - - var value_i8x4: SIMD[DType.int8, 4] - var overflowed_i8x4: SIMD[DType.bool, 4] - value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( - 1, Int8.MAX, 1, Int8.MAX - ).mul_with_overflow(SIMD[DType.int8, 4](0, 2, 0, 2)) - assert_equal( - value_i8x4, SIMD[DType.int8, 4](0, int8_max_x2, 0, int8_max_x2) - ) - assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) - - alias uint32_max_x2 = 4294967294 - var value_u32: UInt32 - var overflowed_u32: Scalar[DType.bool] - value_u32, overflowed_u32 = UInt32(UInt32.MAX).mul_with_overflow(2) - assert_equal(value_u32, uint32_max_x2) - assert_equal(overflowed_u32, True) - - var value_u32x4: SIMD[DType.uint32, 4] - var overflowed_u32x4: SIMD[DType.bool, 4] - value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( - 1, UInt32.MAX, 1, UInt32.MAX - ).mul_with_overflow(SIMD[DType.uint32, 4](0, 2, 0, 2)) - assert_equal( - value_u32x4, SIMD[DType.uint32, 4](0, uint32_max_x2, 0, uint32_max_x2) - ) - assert_equal( - overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) - ) - - alias int32_max_x2 = -2 - var value_i32: Int32 - var overflowed_i32: Scalar[DType.bool] - value_i32, overflowed_i32 = Int32(Int32.MAX).mul_with_overflow(2) - assert_equal(value_i32, int32_max_x2) - assert_equal(overflowed_i32, True) - - var value_i32x4: SIMD[DType.int32, 4] - var overflowed_i32x4: SIMD[DType.bool, 4] - value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( - 1, Int32.MAX, 1, Int32.MAX - ).mul_with_overflow(SIMD[DType.int32, 4](0, 2, 0, 2)) - assert_equal( - value_i32x4, SIMD[DType.int32, 4](0, int32_max_x2, 0, int32_max_x2) - ) - assert_equal( - overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) - ) - - def test_abs(): assert_equal(abs(Float32(1.0)), 1) assert_equal(abs(Float32(-1.0)), 1) @@ -1555,7 +1353,6 @@ def test_contains(): def main(): test_abs() test_add() - test_add_with_overflow() test_cast() test_ceil() test_convert_simd_to_string() @@ -1578,7 +1375,6 @@ def main(): test_limits() test_min_max_clamp() test_mod() - test_mul_with_overflow() test_pow() test_powf() test_radd() @@ -1594,7 +1390,6 @@ def main(): test_shuffle() test_simd_variadic() test_sub() - test_sub_with_overflow() test_trunc() test_bool() test_truthy() From 8869cc1ee996a8be88b2add1fa0323d432e1b8c3 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:56:43 -0400 Subject: [PATCH 1241/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `memory` module Use `UnsafePointer` in `stack_allocation` and remove `DTypePointer` version of `_free`. MODULAR_ORIG_COMMIT_REV_ID: 1a0db5e478c2cdec02f21d6ad51a0c07e97450e7 --- stdlib/src/memory/memory.mojo | 7 +------ stdlib/src/memory/unsafe.mojo | 2 +- stdlib/src/pathlib/path.mojo | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 01bf27b986..f99bef7500 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -419,7 +419,7 @@ fn stack_allocation[ /, alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, address_space: AddressSpace = AddressSpace.GENERIC, -]() -> DTypePointer[type, address_space]: +]() -> UnsafePointer[Scalar[type], address_space]: """Allocates data buffer space on the stack given a data type and number of elements. @@ -519,8 +519,3 @@ fn _free(ptr: UnsafePointer): external_call["free", NoneType](ptr.bitcast[NoneType]()) else: __mlir_op.`pop.aligned_free`(ptr.address) - - -@always_inline -fn _free(ptr: DTypePointer): - _free(ptr.address) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 2434457c2b..3ac8a14cef 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -418,7 +418,7 @@ struct DTypePointer[ @always_inline fn free(self): """Frees the heap allocates memory.""" - _free(self) + _free(self.address) # ===------------------------------------------------------------------=== # # Casting diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index b07e4835a8..27f3d12bd1 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -33,7 +33,7 @@ fn cwd() raises -> Path: The current directory. """ alias MAX_CWD_BUFFER_SIZE = 1024 - var buf0 = stack_allocation[MAX_CWD_BUFFER_SIZE, C_char.type]() + var buf0 = stack_allocation[MAX_CWD_BUFFER_SIZE, C_char]() var buf = UnsafePointer[C_char]._from_dtype_ptr(buf0) From 9e6135228c0fdede610ca0538a9b7e62ccac9c43 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:25:30 -0400 Subject: [PATCH 1242/2019] [stdlib] Remove `DTypePointer` version of `memset` and `memset_zero` MODULAR_ORIG_COMMIT_REV_ID: a2ccf72f5e2a660d37a6259b61422ef58e1b0de1 --- examples/matmul.mojo | 4 +-- examples/notebooks/Matmul.ipynb | 2 +- .../benchmarks/algorithm/bench_vectorize.mojo | 6 ++-- stdlib/src/memory/memory.mojo | 35 ------------------- stdlib/test/sys/test_intrinsics.mojo | 6 ++-- 5 files changed, 9 insertions(+), 44 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 0d0b1c11c9..1eaa08970b 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -53,7 +53,7 @@ struct Matrix[rows: Int, cols: Int]: # Initialize zeroeing all values fn __init__(inout self): self.data = DTypePointer[type].alloc(rows * cols) - memset_zero(self.data, rows * cols) + memset_zero(self.data.address, rows * cols) # Initialize taking a pointer, don't set any elements fn __init__(inout self, data: DTypePointer[type]): @@ -246,7 +246,7 @@ fn matmul_reordered(inout C: Matrix, A: Matrix, B: Matrix): var accumulator = Matrix[tile_m, tile_n]( stack_allocation[tile_m * tile_n, type]() ) - memset_zero(accumulator.data, tile_m * tile_n) + memset_zero(accumulator.data.address, tile_m * tile_n) for ko in range(0, A.cols, tile_k): diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index 80deb716c6..b4f1749164 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -417,7 +417,7 @@ " # Initialize zeroeing all values\n", " fn __init__(inout self):\n", " self.data = DTypePointer[type].alloc(rows * cols)\n", - " memset_zero(self.data, rows * cols)\n", + " memset_zero(self.data.address, rows * cols)\n", "\n", " # Initialize taking a pointer, don't set any elements\n", " fn __init__(inout self, data: DTypePointer[type]):\n", diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 0cd8564eb3..bf2cdff8a1 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -322,15 +322,15 @@ fn bench_compare(): var arg = run[arg_size](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) - memset_zero(p2, size) + memset_zero(p2.address, size) var param = run[param_size](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) - memset_zero(p2, size) + memset_zero(p2.address, size) var arg_unroll = run[arg_size_unroll](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) - memset_zero(p2, size) + memset_zero(p2.address, size) var param_unroll = run[param_size_unroll](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index f99bef7500..83cda14f20 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -332,24 +332,6 @@ fn _memset_llvm[ ) -@always_inline -fn memset[ - type: DType, address_space: AddressSpace -](ptr: DTypePointer[type, address_space], value: UInt8, count: Int): - """Fills memory with the given value. - - Parameters: - type: The element dtype. - address_space: The address space of the pointer. - - Args: - ptr: UnsafePointer to the beginning of the memory block to fill. - value: The value to fill with. - count: Number of elements to fill (in elements, not bytes). - """ - memset(ptr.address, value, count) - - @always_inline fn memset[ type: AnyType, address_space: AddressSpace @@ -373,23 +355,6 @@ fn memset[ # ===----------------------------------------------------------------------===# -@always_inline -fn memset_zero[ - type: DType, address_space: AddressSpace -](ptr: DTypePointer[type, address_space], count: Int): - """Fills memory with zeros. - - Parameters: - type: The element dtype. - address_space: The address space of the pointer. - - Args: - ptr: UnsafePointer to the beginning of the memory block to fill. - count: Number of elements to set (in elements, not bytes). - """ - memset(ptr, 0, count) - - @always_inline fn memset_zero[ type: AnyType, address_space: AddressSpace diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 81507ec85d..506aaf6871 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -31,7 +31,7 @@ alias iota_8 = F32x8(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0) def test_compressed_store(): var vector = DTypePointer[DType.float32]().alloc(5) - memset_zero(vector, 5) + memset_zero(vector.address, 5) compressed_store(iota_4, vector, iota_4 >= 2) assert_equal(SIMD[size=4].load(vector, 0), F32x4(2.0, 3.0, 0.0, 0.0)) @@ -77,7 +77,7 @@ def test_masked_load(): def test_masked_store(): var vector = DTypePointer[DType.float32]().alloc(5) - memset_zero(vector, 5) + memset_zero(vector.address, 5) masked_store[4](iota_4, vector, iota_4 < 5) assert_equal(SIMD[size=4].load(vector, 0), F32x4(0.0, 1.0, 2.0, 3.0)) @@ -106,7 +106,7 @@ fn test_strided_load() raises: fn test_strided_store() raises: alias size = 8 var vector = DTypePointer[DType.float32]().alloc(size) - memset_zero(vector, size) + memset_zero(vector.address, size) strided_store(SIMD[DType.float32, 4](99, 12, 23, 56), vector, 2) assert_equal(vector[0], 99.0) From 111be5381c5028a7fa06c95024b5d89a7d09ab87 Mon Sep 17 00:00:00 2001 From: codingonion Date: Tue, 16 Jul 2024 16:37:33 -0500 Subject: [PATCH 1243/2019] [External] [stdlib] Fix typo in intrinsics.mojo (#43522) [External] [stdlib] Fix typo in intrinsics.mojo Co-authored-by: codingonion Closes modularml/mojo#3256 MODULAR_ORIG_COMMIT_REV_ID: 74d45fd58c1b9045f7829dcf7d649b0968cb52b6 --- stdlib/src/sys/intrinsics.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index f8da09749c..9532168731 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Defines intrinsics. -You can import these APIs from the `complex` package. For example: +You can import these APIs from the `sys` package. For example: ```mojo from sys import PrefetchLocality From 78b3a65bbf4a00efc67fb5188e5f104dbe01a598 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:38:51 -0500 Subject: [PATCH 1244/2019] [External] [stdlib] Fix `String.splitlines` and test and move to string_slice (#43524) [External] [stdlib] Fix `String.splitlines` and test and move to string_slice Fix `String.splitlines` and test and move the implementation of `splitlines` to `StringSlice`. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3223 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3223 MODULAR_ORIG_COMMIT_REV_ID: e1bef52d8bc55950b3cf4493a16417984775a72a --- stdlib/src/builtin/string.mojo | 70 ++-------------------- stdlib/src/utils/string_slice.mojo | 90 ++++++++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 30 ++++++++-- 3 files changed, 119 insertions(+), 71 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 66e648085c..5483d65ca0 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -647,41 +647,6 @@ fn _isspace(c: UInt8) -> Bool: ) -# ===----------------------------------------------------------------------=== # -# _isnewline -# ===----------------------------------------------------------------------=== # - - -fn _isnewline(s: String) -> Bool: - if len(s._buffer) != 2: - return False - - # TODO: add \u2028 and \u2029 when they are properly parsed - # FIXME: \x85 is parsed but not encoded in utf-8 - if s == "\x85": - return True - - # NOTE: a global LUT doesn't work at compile time so we can't use it here. - alias `\n` = UInt8(ord("\n")) - alias `\r` = UInt8(ord("\r")) - alias `\f` = UInt8(ord("\f")) - alias `\v` = UInt8(ord("\v")) - alias `\x1c` = UInt8(ord("\x1c")) - alias `\x1d` = UInt8(ord("\x1d")) - alias `\x1e` = UInt8(ord("\x1e")) - - var c = UInt8(ord(s)) - return ( - c == `\n` - or c == `\r` - or c == `\f` - or c == `\v` - or c == `\x1c` - or c == `\x1d` - or c == `\x1e` - ) - - # ===----------------------------------------------------------------------=== # # isprintable # ===----------------------------------------------------------------------=== # @@ -1656,7 +1621,10 @@ struct String( return output fn splitlines(self, keepends: Bool = False) -> List[String]: - """Split the string at line boundaries. + """Split the string at line boundaries. This corresponds to Python's + [universal newlines]( + https://docs.python.org/3/library/stdtypes.html#str.splitlines) + `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Args: keepends: If True, line breaks are kept in the resulting strings. @@ -1664,35 +1632,7 @@ struct String( Returns: A List of Strings containing the input split by line boundaries. """ - var output = List[String]() - var length = self.byte_length() - var current_offset = 0 - - while current_offset < length: - var loc = -1 - var eol_length = 1 - - for i in range(current_offset, length): - var char = self[i] - var next_char = self[i + 1] if i + 1 < length else "" - - if _isnewline(char): - loc = i - if char == "\r" and next_char == "\n": - eol_length = 2 - break - else: - output.append(self[current_offset:]) - break - - if keepends: - output.append(self[current_offset : loc + eol_length]) - else: - output.append(self[current_offset:loc]) - - current_offset = loc + eol_length - - return output + return self.as_string_slice().splitlines(keepends) fn replace(self, old: String, new: String) -> String: """Return a copy of the string with all occurrences of substring `old` diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index f01f1874e8..f188cc43ea 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -191,6 +191,44 @@ fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: return _validate_utf8_simd_slice[4, True](ptr, length, iter_len) == 0 +fn _is_newline_start( + ptr: UnsafePointer[UInt8], read_ahead: Int = 1 +) -> (Bool, Int): + """Returns if the first item in the pointer is the start of + a newline sequence, and its length. + """ + # TODO add line and paragraph separator as StringLiteral + # once Unicode escape sequences are accepted + alias ` ` = UInt8(ord(" ")) + var rn = "\r\n" + var next_line = List[UInt8](0xC2, 0x85) + """TODO: \\x85""" + var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8) + """TODO: \\u2028""" + var unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9) + """TODO: \\u2029""" + + var val = _utf8_byte_type(ptr[0]) + if val == 0: + if read_ahead > 1: + if memcmp(ptr, rn.unsafe_ptr(), 2) == 0: + return True, 2 + _ = rn + return ptr[0] != ` ` and _isspace(ptr[0]), 1 + elif val == 2 and read_ahead > 1: + var comp = memcmp(ptr, next_line.unsafe_ptr(), 2) == 0 + _ = next_line + return comp, 2 + elif val == 3 and read_ahead > 2: + var comp = ( + memcmp(ptr, unicode_line_sep.unsafe_ptr(), 3) == 0 + or memcmp(ptr, unicode_paragraph_sep.unsafe_ptr(), 3) == 0 + ) + _ = unicode_line_sep, unicode_paragraph_sep + return comp, 3 + return False, 1 + + @value struct _StringSliceIter[ is_mutable: Bool, //, @@ -627,3 +665,55 @@ struct StringSlice[ return False _ = next_line, unicode_line_sep, unicode_paragraph_sep return True + + fn splitlines(self, keepends: Bool = False) -> List[String]: + """Split the string at line boundaries. This corresponds to Python's + [universal newlines]( + https://docs.python.org/3/library/stdtypes.html#str.splitlines) + `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + + Args: + keepends: If True, line breaks are kept in the resulting strings. + + Returns: + A List of Strings containing the input split by line boundaries. + """ + var output = List[String]() + var length = self.byte_length() + var current_offset = 0 + var ptr = self.unsafe_ptr() + + while current_offset < length: + var eol_location = length - current_offset + var eol_length = 0 + var curr_ptr = ptr.offset(current_offset) + + for i in range(current_offset, length): + var read_ahead = 3 if i < length - 2 else ( + 2 if i < length - 1 else 1 + ) + var res = _is_newline_start(ptr.offset(i), read_ahead) + if res[0]: + eol_location = i - current_offset + eol_length = res[1] + break + + var str_len: Int + var end_of_string = False + if current_offset >= length: + end_of_string = True + str_len = 0 + elif keepends: + str_len = eol_location + eol_length + else: + str_len = eol_location + + output.append( + String(Self(unsafe_from_utf8_ptr=curr_ptr, len=str_len)) + ) + + if end_of_string: + break + current_offset += eol_location + eol_length + + return output^ diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 4e9fcd1b36..1a0fe0c470 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -669,7 +669,7 @@ def test_split(): """TODO: \\u2028""" alias unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9, 0) """TODO: \\u2029""" - # TODO add line and paragraph separator as stringliteral once unicode + # TODO add line and paragraph separator as StringLiteral once unicode # escape secuences are accepted var univ_sep_var = ( String(" ") @@ -838,14 +838,30 @@ def test_splitlines(): assert_equal(res8[2], "mojo") assert_equal(res8[3], "language") - # test \x1e \x85 - var in9 = String("hello\x1eworld\x85mojo") + # test \x1e \x1d + var in9 = String("hello\x1eworld\x1dmojo") var res9 = in9.splitlines() assert_equal(len(res9), 3) assert_equal(res9[0], "hello") assert_equal(res9[1], "world") assert_equal(res9[2], "mojo") + # test \x85 \u2028 \u2029 + var next_line = List[UInt8](0xC2, 0x85, 0) + """TODO: \\x85""" + var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8, 0) + """TODO: \\u2028""" + var unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9, 0) + """TODO: \\u2029""" + + for i in List(next_line, unicode_line_sep, unicode_paragraph_sep): + var in9 = "hello\x1eworld" + String(i[]) + "mojo" + var res9 = in9.splitlines() + assert_equal(len(res9), 3) + assert_equal(res9[0], "hello") + assert_equal(res9[1], "world") + assert_equal(res9[2], "mojo") + # test with keepends=True var res10 = in8.splitlines(keepends=True) assert_equal(len(res10), 4) @@ -854,10 +870,12 @@ def test_splitlines(): assert_equal(res10[2], "mojo\x1c") assert_equal(res10[3], "language\x1d") - var res11 = in9.splitlines(keepends=True) + var res11 = ("hello\x1eworld" + String(next_line) + "mojo").splitlines( + keepends=True + ) assert_equal(len(res11), 3) assert_equal(res11[0], "hello\x1e") - assert_equal(res11[1], "world\x85") + assert_equal(res11[1], "world" + String(next_line)) assert_equal(res11[2], "mojo") @@ -948,7 +966,7 @@ def test_isspace(): """TODO: \\u2028""" alias unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9, 0) """TODO: \\u2029""" - # TODO add line and paragraph separator as stringliteral once unicode + # TODO add line and paragraph separator as StringLiteral once unicode # escape sequences are accepted var univ_sep_var = List[String]( String(" "), From 2950935e8369561e06c641d61423458014a1fe81 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 16 Jul 2024 17:10:49 -0500 Subject: [PATCH 1245/2019] [stdlib] Don't make `math.lcm` take `owned` arguments There's no need for `math.lcm` to take ownership of its arguments. Remove the `owned` keyword so the default argument convention of `borrowed` is used for the function arguments. This will also simplify https://github.com/modularml/mojo/pull/3041. MODULAR_ORIG_COMMIT_REV_ID: 782c1016c7fe287fc21115504e75098431fe80dd --- stdlib/src/math/math.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index b3e369078a..57d6e91eb8 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1997,7 +1997,7 @@ fn gcd(*values: Int) -> Int: # ===----------------------------------------------------------------------=== # -fn lcm(owned m: Int, owned n: Int, /) -> Int: +fn lcm(m: Int, n: Int, /) -> Int: """Computes the least common multiple of two integers. Args: From bc86c9f3017269c4c7f63066c73f9eae8f7e7351 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:32:07 -0500 Subject: [PATCH 1246/2019] [External] [Stdlib] Add `Dict.__init__` overload with `power_of_two_initial_capacity` (#43531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [Stdlib] Add `Dict.__init__` overload with `power_of_two_initial_capacity` Hello, here is a new `__init__` that let user specify the initial reservation. Skipping the un-necessary growth phases when the final size is known to be big.   2.2x speedup on insertion, lookup should be faster aswel (during overcapacity). 1056082202 **PR** 35184367894528 2388001454 35184367894528 ```mojo from time import now def main(): var dict_size = 1<<23 var start = now() var stop = now() start = now() x = Dict[Int,Int](power_of_two_initial_capacity = 1<<24) for i in range(dict_size): x[i] = i stop = now() print(stop-start) var result = 0 for i in range(len(x)): result+=x[i] print(result) ``` We started with 24 and not 23 because the dict grows at 2/3 of capacity, but reserving 23 is a huge speedup too.   💡 Could this ameliorate variadic keyword arguments ? If mojo knows there are 8 arguments passed, maybe reserve 16 (and skip the dict growth phases). Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3171 MODULAR_ORIG_COMMIT_REV_ID: 66a7a33fda4dd852c9333590aeec63c7804b5532 --- docs/changelog.md | 14 ++++++++++++++ stdlib/src/collections/dict.mojo | 26 ++++++++++++++++++++++++++ stdlib/test/collections/test_dict.mojo | 14 ++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 86f843e2f7..038ba8deb5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -283,6 +283,20 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') ``` +- Added `Dict.__init__` overload to specify initial capacity. + ([PR #3171](https://github.com/modularml/mojo/pull/3171) by [@rd4com](https://github.com/rd4com)) + + The capacity has to be a power of two and above or equal 8. + + It allows for faster initialization by skipping incremental growth steps. + + Example: + + ```mojo + var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) + # Insert (2/3 of 1024) entries + ``` + ### 🦋 Changed - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index cb5fd406e2..1b74ccbfa0 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -34,6 +34,7 @@ See the `Dict` docs for more details. from builtin.value import StringableCollectionElement from .optional import Optional +from bit import is_power_of_two trait KeyElement(CollectionElement, Hashable, EqualityComparable): @@ -444,6 +445,31 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._entries = Self._new_entries(Self._initial_reservation) self._index = _DictIndex(len(self._entries)) + @always_inline + fn __init__(inout self, *, power_of_two_initial_capacity: Int): + """Initialize an empty dictiontary with a pre-reserved initial capacity. + + Args: + power_of_two_initial_capacity: At least 8, has to be a power of two. + + Example usage: + + ```mojo + var x = Dict[Int,Int](power_of_two_initial_capacity = 1024) + # Insert (2/3 of 1024) entries without reallocation. + ``` + + """ + debug_assert( + bit.is_power_of_two(power_of_two_initial_capacity) + and power_of_two_initial_capacity >= 8, + "power_of_two_initial_capacity need to be >=8 and a power of two", + ) + self.size = 0 + self._n_entries = 0 + self._entries = Self._new_entries(power_of_two_initial_capacity) + self._index = _DictIndex(len(self._entries)) + # TODO: add @property when Mojo supports it to make # it possible to do `self._reserved`. @always_inline diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index f0a4cc9c8c..0b96330db7 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -550,6 +550,19 @@ fn test_clear() raises: assert_equal(len(some_dict), 0) +def test_init_initial_capacity(): + var initial_capacity = 16 + var x = Dict[Int, Int](power_of_two_initial_capacity=initial_capacity) + assert_equal(x._reserved(), initial_capacity) + for i in range(initial_capacity): + x[i] = i + for i in range(initial_capacity): + assert_equal(i, x[i]) + + var y = Dict[Int, Int](power_of_two_initial_capacity=64) + assert_equal(y._reserved(), 64) + + def main(): test_dict() test_dict_fromkeys() @@ -560,3 +573,4 @@ def main(): test_bool_conversion() test_find_get() test_clear() + test_init_initial_capacity() From b4ab757ede38e1bd479456125f208f14734e3d60 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 18:54:02 -0400 Subject: [PATCH 1247/2019] [stdlib] Remove `DTypePointer` version of `memcpy` MODULAR_ORIG_COMMIT_REV_ID: a1a61fde495ea9666791d7f682e0a64b0b1172d9 --- stdlib/src/memory/memory.mojo | 71 ++++------------------------------- 1 file changed, 8 insertions(+), 63 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 83cda14f20..6529cd3480 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -177,32 +177,16 @@ fn memcpy[count: Int](dest: UnsafePointer, src: __type_of(dest)): ).bitcast[Int32]()[0] return - var dest_dtype_ptr = DTypePointer[DType.int8, dest.address_space](dest_data) - var src_dtype_ptr = DTypePointer[DType.int8, src.address_space](src_data) + var dest_ptr = dest_data.bitcast[Int8]() + var src_ptr = src_data.bitcast[Int8]() # Copy in 32-byte chunks. alias chunk_size = 32 alias vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - SIMD.store( - dest_dtype_ptr, i, SIMD[size=chunk_size].load(src_dtype_ptr, i) - ) + SIMD.store(dest_ptr, i, SIMD[size=chunk_size].load(src_ptr, i)) for i in range(vector_end, n): - Scalar.store(dest_dtype_ptr, i, Scalar.load(src_dtype_ptr, i)) - - -@always_inline -fn memcpy[count: Int](dest: DTypePointer, src: __type_of(dest)): - """Copies a memory area. - - Parameters: - count: The number of elements to copy (not bytes!). - - Args: - dest: The destination pointer. - src: The source pointer. - """ - memcpy[count](dest.address, src.address) + Scalar.store(dest_ptr, i, Scalar.load(src_ptr, i)) @always_inline @@ -254,22 +238,16 @@ fn memcpy( # ) # return - var dest_dtype_ptr = DTypePointer[DType.int8, dest_data.address_space]( - dest_data - ) - var src_dtype_ptr = DTypePointer[DType.int8, src_data.address_space]( - src_data - ) + var dest_ptr = dest_data.bitcast[Int8]() + var src_ptr = src_data.bitcast[Int8]() # Copy in 32-byte chunks. alias chunk_size = 32 var vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - SIMD.store( - dest_dtype_ptr, i, SIMD[size=chunk_size].load(src_dtype_ptr, i) - ) + SIMD.store(dest_ptr, i, SIMD[size=chunk_size].load(src_ptr, i)) for i in range(vector_end, n): - Scalar.store(dest_dtype_ptr, i, Scalar.load(src_dtype_ptr, i)) + Scalar.store(dest_ptr, i, Scalar.load(src_ptr, i)) @always_inline @@ -285,39 +263,6 @@ fn memcpy(dest: UnsafePointer, src: __type_of(dest), count: Int): memcpy(dest.bitcast[Int8](), src.bitcast[Int8](), n) -@always_inline -fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): - """Copies a memory area. - - Args: - dest: The destination pointer. - src: The source pointer. - count: The number of elements to copy (not bytes!). - """ - memcpy(dest.address, src.address, count) - - -@always_inline -fn memcpy[ - dtype: DType, // -](*, dest: UnsafePointer[Scalar[dtype]], src: __type_of(dest), count: Int): - """Copies a memory area. - - Parameters: - dtype: *Inferred* The dtype of the data to copy. - - Args: - dest: The destination pointer. - src: The source pointer. - count: The number of elements to copy (not bytes!). - """ - memcpy( - dest=DTypePointer(dest), - src=DTypePointer(src), - count=count, - ) - - # ===----------------------------------------------------------------------===# # memset # ===----------------------------------------------------------------------===# From c23fdff4f81757b99a68effe90565d7b6513ca62 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 17 Jul 2024 02:07:33 +0300 Subject: [PATCH 1248/2019] [stdlib] Remove `SIMD.{min,max}` methods in favor of builtin functions This simplifies the API. The patch also makes the `min` and `max` functions "nodebug" since they are all just wrapping mlir ops, and makes their signatures take pos-only arguments. MODULAR_ORIG_COMMIT_REV_ID: 6f64eff6eb4c263c9fa83e2137163b6595efabcc --- docs/changelog.md | 3 ++ stdlib/src/builtin/math.mojo | 48 +++++++++++++----------------- stdlib/src/builtin/simd.mojo | 35 ++-------------------- stdlib/src/math/math.mojo | 2 +- stdlib/test/builtin/test_math.mojo | 28 ++++++++++------- stdlib/test/builtin/test_simd.mojo | 15 ++-------- 6 files changed, 48 insertions(+), 83 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 038ba8deb5..3cb11dc845 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -496,6 +496,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - Removed the `SIMD.{add,mul,sub}_with_overflow` methods. +- Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is + available using the builting `min` and `max` functions. + ### 🛠️ Fixed - Fixed a crash in the Mojo Language Server when importing the current file. diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 88666264d8..492460d097 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -146,8 +146,8 @@ fn divmod(numerator: UInt, denominator: UInt) -> Tuple[UInt, UInt]: # ===----------------------------------------------------------------------=== # -@always_inline -fn max(x: Int, y: Int) -> Int: +@always_inline("nodebug") +fn max(x: Int, y: Int, /) -> Int: """Gets the maximum of two integers. Args: @@ -160,8 +160,8 @@ fn max(x: Int, y: Int) -> Int: return __mlir_op.`index.maxs`(x.value, y.value) -@always_inline -fn max(x: UInt, y: UInt) -> UInt: +@always_inline("nodebug") +fn max(x: UInt, y: UInt, /) -> UInt: """Gets the maximum of two integers. Args: @@ -174,20 +174,15 @@ fn max(x: UInt, y: UInt) -> UInt: return __mlir_op.`index.maxu`(x.value, y.value) -@always_inline -fn max[ - type: DType, simd_width: Int -](x: SIMD[type, simd_width], y: SIMD[type, simd_width]) -> SIMD[ - type, simd_width -]: +@always_inline("nodebug") +fn max(x: SIMD, y: __type_of(x), /) -> __type_of(x): """Performs elementwise maximum of x and y. An element of the result SIMD vector will be the maximum of the corresponding elements in x and y. - Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. + Constraints: + The type of the inputs must be numeric. Args: x: First SIMD vector. @@ -196,7 +191,8 @@ fn max[ Returns: A SIMD vector containing the elementwise maximum of x and y. """ - return x.max(y) + constrained[x.type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.max`(x.value, y.value) # ===----------------------------------------------------------------------=== # @@ -204,8 +200,8 @@ fn max[ # ===----------------------------------------------------------------------=== # -@always_inline -fn min(x: Int, y: Int) -> Int: +@always_inline("nodebug") +fn min(x: Int, y: Int, /) -> Int: """Gets the minimum of two integers. Args: @@ -218,8 +214,8 @@ fn min(x: Int, y: Int) -> Int: return __mlir_op.`index.mins`(x.value, y.value) -@always_inline -fn min(x: UInt, y: UInt) -> UInt: +@always_inline("nodebug") +fn min(x: UInt, y: UInt, /) -> UInt: """Gets the minimum of two integers. Args: @@ -232,20 +228,15 @@ fn min(x: UInt, y: UInt) -> UInt: return __mlir_op.`index.minu`(x.value, y.value) -@always_inline -fn min[ - type: DType, simd_width: Int -](x: SIMD[type, simd_width], y: SIMD[type, simd_width]) -> SIMD[ - type, simd_width -]: +@always_inline("nodebug") +fn min(x: SIMD, y: __type_of(x), /) -> __type_of(x): """Gets the elementwise minimum of x and y. An element of the result SIMD vector will be the minimum of the corresponding elements in x and y. - Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. + Constraints: + The type of the inputs must be numeric. Args: x: First SIMD vector. @@ -254,7 +245,8 @@ fn min[ Returns: A SIMD vector containing the elementwise minimum of x and y. """ - return x.min(y) + constrained[x.type.is_numeric(), "the SIMD type must be numeric"]() + return __mlir_op.`pop.min`(x.value, y.value) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b6e7aa5a80..ac6d5634f1 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1655,8 +1655,7 @@ struct SIMD[type: DType, size: Int]( A new SIMD vector containing x clamped to be within lower_bound and upper_bound. """ - - return self.min(upper_bound).max(lower_bound) + return max(min(self, upper_bound), lower_bound) @always_inline("nodebug") fn roundeven(self) -> Self: @@ -2042,34 +2041,6 @@ struct SIMD[type: DType, size: Int]( rebind[Self._SIMDHalfType](res[1]), ) - @always_inline("nodebug") - fn min(self, other: Self) -> Self: - """Computes the elementwise minimum between the two vectors. - - Args: - other: The other SIMD vector. - - Returns: - A new SIMD vector where each element at position `i` is - `min(self[i], other[i])`. - """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.min`(self.value, other.value) - - @always_inline("nodebug") - fn max(self, other: Self) -> Self: - """Computes the elementwise maximum between the two vectors. - - Args: - other: The other SIMD vector. - - Returns: - A new SIMD vector where each element at position `i` is - `max(self[i], other[i])`. - """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.max`(self.value, other.value) - # ===------------------------------------------------------------------=== # # Reduce operations # ===------------------------------------------------------------------=== # @@ -2133,7 +2104,7 @@ struct SIMD[type: DType, size: Int]( ](v1: SIMD[type, width], v2: SIMD[type, width]) -> SIMD[ type, width ]: - return v1.max(v2) + return max(v1, v2) return self.reduce[max_reduce_body, size_out]() @@ -2191,7 +2162,7 @@ struct SIMD[type: DType, size: Int]( ](v1: SIMD[type, width], v2: SIMD[type, width]) -> SIMD[ type, width ]: - return v1.min(v2) + return min(v1, v2) return self.reduce[min_reduce_body, size_out]() diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 57d6e91eb8..9de8cd1be4 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -791,7 +791,7 @@ fn erf[ -3.83208680e-4, 1.72948930e-5, ), - ](x_abs.min(3.925)) + ](min(x_abs, 3.925)) r_large = r_large.fma(x_abs, x_abs) r_large = copysign(1 - exp(-r_large), x) diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 4ae6bdf8f2..3c931a4553 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -49,11 +49,15 @@ def test_min(): assert_equal(UInt(0), min(UInt(0), UInt(1))) assert_equal(UInt(1), min(UInt(1), UInt(42))) - var lhs = SIMD[DType.int32, 4](1, 2, 3, 4) - var rhs = SIMD[DType.int32, 4](0, 1, 5, 7) - var expected = SIMD[DType.int32, 4](0, 1, 3, 4) - assert_equal(expected, lhs.min(rhs)) - assert_equal(expected, rhs.min(lhs)) + alias F = SIMD[DType.float32, 4] + var f = F(-10.5, -5.0, 5.0, 10.0) + assert_equal(min(f, F(-9.0, -6.0, -4.0, 10.5)), F(-10.5, -6.0, -4.0, 10.0)) + assert_equal(min(f, -4.0), F(-10.5, -5.0, -4.0, -4.0)) + + alias I = SIMD[DType.int32, 4] + var i = I(-10, -5, 5, 10) + assert_equal(min(i, I(-9, -6, -4, 11)), I(-10, -6, -4, 10)) + assert_equal(min(i, -4), I(-10, -5, -4, -4)) def test_max(): @@ -65,11 +69,15 @@ def test_max(): assert_equal(UInt(1), max(UInt(0), UInt(1))) assert_equal(UInt(2), max(UInt(1), UInt(2))) - var lhs = SIMD[DType.int32, 4](1, 2, 3, 4) - var rhs = SIMD[DType.int32, 4](0, 1, 5, 7) - var expected = SIMD[DType.int32, 4](1, 2, 5, 7) - assert_equal(expected, lhs.max(rhs)) - assert_equal(expected, rhs.max(lhs)) + alias F = SIMD[DType.float32, 4] + var f = F(-10.5, -5.0, 5.0, 10.0) + assert_equal(max(f, F(-9.0, -6.0, -4.0, 10.5)), F(-9.0, -5.0, 5.0, 10.5)) + assert_equal(max(f, -4.0), F(-4.0, -4.0, 5.0, 10.0)) + + alias I = SIMD[DType.int32, 4] + var i = I(-10, -5, 5, 10) + assert_equal(max(i, I(-9, -6, -4, 11)), I(-9, -5, 5, 11)) + assert_equal(max(i, -4), I(-4, -4, 5, 10)) def test_round(): diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 1be81ceaab..c433cc2e91 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -980,22 +980,13 @@ def test_abs(): ) -def test_min_max_clamp(): +def test_clamp(): alias F = SIMD[DType.float32, 4] - var f = F(-10.5, -5.0, 5.0, 10.0) - assert_equal(f.min(F(-9.0, -6.0, -4.0, 10.5)), F(-10.5, -6.0, -4.0, 10.0)) - assert_equal(f.min(-4.0), F(-10.5, -5.0, -4.0, -4.0)) - assert_equal(f.max(F(-9.0, -6.0, -4.0, 10.5)), F(-9.0, -5.0, 5.0, 10.5)) - assert_equal(f.max(-4.0), F(-4.0, -4.0, 5.0, 10.0)) assert_equal(f.clamp(-6.0, 5.5), F(-6.0, -5.0, 5.0, 5.5)) - alias I = SIMD[DType.float32, 4] + alias I = SIMD[DType.int32, 4] var i = I(-10, -5, 5, 10) - assert_equal(i.min(I(-9, -6, -4, 11)), I(-10, -6, -4, 10)) - assert_equal(i.min(-4), I(-10, -5, -4, -4)) - assert_equal(i.max(I(-9, -6, -4, 11)), I(-9, -5, 5, 11)) - assert_equal(i.max(-4), I(-4, -4, 5, 10)) assert_equal(i.clamp(-7, 4), I(-7, -5, 4, 4)) @@ -1373,7 +1364,7 @@ def main(): test_join() test_len() test_limits() - test_min_max_clamp() + test_clamp() test_mod() test_pow() test_powf() From 5049f421a59089c8e6b5eba845c98b25f53b407e Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 16 Jul 2024 18:12:31 -0500 Subject: [PATCH 1249/2019] [External] [stdlib] Fix `math.gcd` for negative numbers (#42355) [External] [stdlib] Fix `math.gcd` for negative numbers Fix `math.gcd` for negative numbers. For example, `gcd(0, -2)` should be `2`. While here, simplify the implementation a bit. Co-authored-by: soraros Closes modularml/mojo#3057 MODULAR_ORIG_COMMIT_REV_ID: 32efe40b8e40b276c54c3589608b2301ecdd82ba --- stdlib/src/math/math.mojo | 48 ++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 9de8cd1be4..89a91df3ee 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -27,6 +27,7 @@ from sys.info import bitwidthof, has_avx512f, simdwidthof, triple_is_nvidia_cuda from memory import UnsafePointer +from bit import count_trailing_zeros from builtin._math import * from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply, _modf @@ -1910,34 +1911,25 @@ fn gcd(m: Int, n: Int, /) -> Int: Returns: The greatest common divisor of the two integers. """ - if m == 0 or n == 0: - return max(m, n) - - if m > 0 and n > 0: - var trailing_zeros_a = count_trailing_zeros(m) - var trailing_zeros_b = count_trailing_zeros(n) - - var u = m >> trailing_zeros_a - var v = n >> trailing_zeros_b - var trailing_zeros_common = min(trailing_zeros_a, trailing_zeros_b) - - if u == 1 or v == 1: - return 1 << trailing_zeros_common - - while u != v: - if u > v: - u, v = v, u - v -= u - if u == 0: - break - v >>= count_trailing_zeros(v) - return u << trailing_zeros_common - - var u = m - var v = n - while v: - u, v = v, u % v - return abs(u) + var u = abs(m) + var v = abs(n) + if u == 0: + return v + if v == 0: + return u + + var uz = count_trailing_zeros(u) + var vz = count_trailing_zeros(v) + var shift = min(uz, vz) + u >>= shift + while True: + v >>= vz + var diff = v - u + if diff == 0: + break + u, v = min(u, v), abs(diff) + vz = count_trailing_zeros(diff) + return u << shift fn gcd(s: Span[Int], /) -> Int: From be238f2422eed9df6719766383fec3afd4149b2b Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:35:37 -0400 Subject: [PATCH 1250/2019] [stdlib] Convert `DTypePointer` uses to `UnsafePointer` in `intrinsics` MODULAR_ORIG_COMMIT_REV_ID: dc219fbd9d5e81762bd8795d9abea34222eeacd0 --- stdlib/src/memory/unsafe.mojo | 4 +- stdlib/src/sys/intrinsics.mojo | 160 ++++----------------------- stdlib/test/sys/test_intrinsics.mojo | 18 +-- 3 files changed, 31 insertions(+), 151 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 3ac8a14cef..37afb4b525 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -487,7 +487,7 @@ struct DTypePointer[ A vector which is stride loaded. """ return strided_load[type, width]( - self, int(stride), SIMD[DType.bool, width](1) + self.address, int(stride), SIMD[DType.bool, width](1) ) @always_inline("nodebug") @@ -504,7 +504,7 @@ struct DTypePointer[ val: The SIMD value to store. stride: The stride between stores. """ - strided_store(val, self, int(stride), True) + strided_store(val, self.address, int(stride), True) # ===------------------------------------------------------------------=== # # Gather / Scatter diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 9532168731..a121726518 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -21,7 +21,7 @@ from sys import PrefetchLocality from sys import sizeof -from memory import AddressSpace, DTypePointer +from memory import AddressSpace, UnsafePointer # ===----------------------------------------------------------------------===# # llvm_intrinsic @@ -811,8 +811,10 @@ fn llvm_intrinsic[ # this function! fn _unsafe_aliasing_address_to_pointer[ type: DType -](owned addr: Scalar[DType.index]) -> DTypePointer[type]: - return UnsafePointer.address_of(addr).bitcast[DTypePointer[type]]()[] +](owned addr: Scalar[DType.index]) -> UnsafePointer[Scalar[type]]: + return UnsafePointer.address_of(addr).bitcast[ + UnsafePointer[Scalar[type]] + ]()[] @always_inline("nodebug") @@ -1181,7 +1183,7 @@ struct PrefetchOptions: @always_inline("nodebug") fn prefetch[ params: PrefetchOptions, type: DType, address_space: AddressSpace -](addr: DTypePointer[type, address_space, _]): +](addr: UnsafePointer[Scalar[type], address_space, _]): """Prefetches an instruction or data into cache before it is used. The prefetch function provides prefetching hints for the target @@ -1196,7 +1198,7 @@ fn prefetch[ addr: The data pointer to prefetch. """ llvm_intrinsic["llvm.prefetch", NoneType]( - addr.bitcast[DType.invalid.value](), + addr.bitcast[NoneType](), params.rw, params.locality, params.cache, @@ -1210,17 +1212,18 @@ fn prefetch[ @always_inline("nodebug") fn masked_load[ - size: Int + type: DType, //, size: Int ]( - addr: DTypePointer, + addr: UnsafePointer[Scalar[type], *_], mask: SIMD[DType.bool, size], - passthrough: SIMD[addr.type, size], + passthrough: SIMD[type, size], alignment: Int = 1, -) -> SIMD[addr.type, size]: +) -> SIMD[type, size]: """Loads data from memory and return it, replacing masked lanes with values from the passthrough vector. Parameters: + type: DType of the return SIMD buffer. size: Size of the return SIMD buffer. Args: @@ -1240,8 +1243,8 @@ fn masked_load[ if size == 1: return Scalar.load(addr) if mask else passthrough[0] - return llvm_intrinsic["llvm.masked.load", SIMD[addr.type, size]]( - addr.bitcast[DType.invalid.value]().address, + return llvm_intrinsic["llvm.masked.load", SIMD[type, size]]( + addr.bitcast[NoneType]().address, Int32(alignment), mask, passthrough, @@ -1258,7 +1261,7 @@ fn masked_store[ size: Int ]( value: SIMD, - addr: DTypePointer[value.type], + addr: UnsafePointer[Scalar[value.type]], mask: SIMD[DType.bool, size], alignment: Int = 1, ): @@ -1284,7 +1287,7 @@ fn masked_store[ llvm_intrinsic["llvm.masked.store", NoneType]( value, - addr.bitcast[DType.invalid.value]().address, + addr.bitcast[NoneType]().address, Int32(alignment), mask, ) @@ -1300,7 +1303,7 @@ fn compressed_store[ type: DType, size: Int ]( value: SIMD[type, size], - addr: DTypePointer[type], + addr: UnsafePointer[Scalar[type]], mask: SIMD[DType.bool, size], ): """Compresses the lanes of `value`, skipping `mask` lanes, and stores @@ -1325,7 +1328,7 @@ fn compressed_store[ llvm_intrinsic["llvm.masked.compressstore", NoneType]( value, - addr.bitcast[DType.invalid.value]().address, + addr.bitcast[NoneType]().address, mask, ) @@ -1335,36 +1338,6 @@ fn compressed_store[ # ===----------------------------------------------------------------------===# -@always_inline("nodebug") -fn strided_load[ - type: DType, - simd_width: Int, - /, - address_space: AddressSpace = AddressSpace.GENERIC, -]( - addr: DTypePointer[type, address_space, _], - stride: Int, - mask: SIMD[DType.bool, simd_width], -) -> SIMD[type, simd_width]: - """Loads values from addr according to a specific stride. - - Parameters: - type: DType of `value`, the value to store. - simd_width: The width of the SIMD vectors. - address_space: The address space of the memory location. - - Args: - addr: The memory location to load data from. - stride: How many lanes to skip before loading again. - mask: A binary vector which prevents memory access to certain lanes of - `value`. - - Returns: - A vector containing the loaded data. - """ - return strided_load[type, simd_width](addr.address, stride, mask) - - @always_inline("nodebug") fn strided_load[ type: DType, @@ -1374,7 +1347,7 @@ fn strided_load[ ]( addr: UnsafePointer[Scalar[type], address_space, _], stride: Int, - mask: SIMD[DType.bool, simd_width], + mask: SIMD[DType.bool, simd_width] = True, ) -> SIMD[type, simd_width]: """Loads values from addr according to a specific stride. @@ -1408,72 +1381,11 @@ fn strided_load[ return gather[type, simd_width](offset, mask, passthrough) -@always_inline("nodebug") -fn strided_load[ - type: DType, - simd_width: Int, - /, - address_space: AddressSpace = AddressSpace.GENERIC, -](addr: DTypePointer[type, address_space, _], stride: Int) -> SIMD[ - type, simd_width -]: - """Loads values from addr according to a specific stride. - - Parameters: - type: DType of `value`, the value to store. - simd_width: The width of the SIMD vectors. - address_space: The address space of the memory location. - - Args: - addr: The memory location to load data from. - stride: How many lanes to skip before loading again. - - Returns: - A vector containing the loaded data. - """ - - @parameter - if simd_width == 1: - return Scalar.load(addr) - - return strided_load[type, simd_width](addr, stride, True) - - # ===----------------------------------------------------------------------===# # strided store # ===----------------------------------------------------------------------===# -@always_inline("nodebug") -fn strided_store[ - type: DType, - simd_width: Int, - /, - address_space: AddressSpace = AddressSpace.GENERIC, -]( - value: SIMD[type, simd_width], - addr: DTypePointer[type, address_space, _], - stride: Int, - mask: SIMD[DType.bool, simd_width], -): - """Loads values from addr according to a specific stride. - - Parameters: - type: DType of `value`, the value to store. - simd_width: The width of the SIMD vectors. - address_space: The address space of the memory location. - - Args: - value: The values to store. - addr: The location to store values at. - stride: How many lanes to skip before storing again. - mask: A binary vector which prevents memory access to certain lanes of - `value`. - """ - - strided_store[type, simd_width](value, addr.address, stride, mask) - - @always_inline("nodebug") fn strided_store[ type: DType, @@ -1484,7 +1396,7 @@ fn strided_store[ value: SIMD[type, simd_width], addr: UnsafePointer[Scalar[type], address_space, _], stride: Int, - mask: SIMD[DType.bool, simd_width], + mask: SIMD[DType.bool, simd_width] = True, ): """Loads values from addr according to a specific stride. @@ -1518,38 +1430,6 @@ fn strided_store[ scatter[type, simd_width](value, offset, mask) -@always_inline("nodebug") -fn strided_store[ - type: DType, - simd_width: Int, - /, - address_space: AddressSpace = AddressSpace.GENERIC, -]( - value: SIMD[type, simd_width], - addr: DTypePointer[type, address_space, _], - stride: Int, -): - """Loads values from addr according to a specific stride. - - Parameters: - type: DType of `value`, the value to store. - simd_width: The width of the SIMD vectors. - address_space: The address space of the memory location. - - Args: - value: The values to store. - addr: The location to store values at. - stride: How many lanes to skip before storing again. - """ - - @parameter - if simd_width == 1: - Scalar.store(addr, value[0]) - return - - strided_store[type, simd_width](value, addr, stride, True) - - # ===-------------------------------------------------------------------===# # _mlirtype_is_eq # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 506aaf6871..a0a365452a 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -20,7 +20,7 @@ from sys import ( strided_store, ) -from memory import DTypePointer +from memory import UnsafePointer from testing import assert_equal alias F32x4 = SIMD[DType.float32, 4] @@ -30,8 +30,8 @@ alias iota_8 = F32x8(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0) def test_compressed_store(): - var vector = DTypePointer[DType.float32]().alloc(5) - memset_zero(vector.address, 5) + var vector = UnsafePointer[Float32]().alloc(5) + memset_zero(vector, 5) compressed_store(iota_4, vector, iota_4 >= 2) assert_equal(SIMD[size=4].load(vector, 0), F32x4(2.0, 3.0, 0.0, 0.0)) @@ -46,7 +46,7 @@ def test_compressed_store(): def test_masked_load(): - var vector = DTypePointer[DType.float32]().alloc(5) + var vector = UnsafePointer[Float32]().alloc(5) for i in range(5): vector[i] = 1 @@ -76,8 +76,8 @@ def test_masked_load(): def test_masked_store(): - var vector = DTypePointer[DType.float32]().alloc(5) - memset_zero(vector.address, 5) + var vector = UnsafePointer[Float32]().alloc(5) + memset_zero(vector, 5) masked_store[4](iota_4, vector, iota_4 < 5) assert_equal(SIMD[size=4].load(vector, 0), F32x4(0.0, 1.0, 2.0, 3.0)) @@ -92,7 +92,7 @@ def test_masked_store(): fn test_strided_load() raises: alias size = 16 - var vector = DTypePointer[DType.float32]().alloc(size) + var vector = UnsafePointer[Float32]().alloc(size) for i in range(size): vector[i] = i @@ -105,8 +105,8 @@ fn test_strided_load() raises: fn test_strided_store() raises: alias size = 8 - var vector = DTypePointer[DType.float32]().alloc(size) - memset_zero(vector.address, size) + var vector = UnsafePointer[Float32]().alloc(size) + memset_zero(vector, size) strided_store(SIMD[DType.float32, 4](99, 12, 23, 56), vector, 2) assert_equal(vector[0], 99.0) From 2a3e57ab407a2d06ef06e77198d129c75117b2d9 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:01:31 -0500 Subject: [PATCH 1251/2019] [External] [GitHub Actions] Extract install build tools into separate scripts (#43435) [External] [GitHub Actions] Extract install build tools into separate scripts Related to https://github.com/modularml/mojo/issues/3200. Putting the install scripts as separate files would allow the users to easily install the dependencies. The setup instructions will be added to the contributing guide later. ORIGINAL_AUTHOR=Haifeng Jin <5476582+haifeng-jin@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3234 Co-authored-by: Haifeng Jin <5476582+haifeng-jin@users.noreply.github.com> Closes modularml/mojo#3234 MODULAR_ORIG_COMMIT_REV_ID: ad31b57a05d286a25f3679b638f7a46068a90b05 --- stdlib/scripts/install-build-tools-linux.sh | 32 +++++++++++++++++++++ stdlib/scripts/install-build-tools-macos.sh | 20 +++++++++++++ 2 files changed, 52 insertions(+) create mode 100755 stdlib/scripts/install-build-tools-linux.sh create mode 100755 stdlib/scripts/install-build-tools-macos.sh diff --git a/stdlib/scripts/install-build-tools-linux.sh b/stdlib/scripts/install-build-tools-linux.sh new file mode 100755 index 0000000000..251e40e1b1 --- /dev/null +++ b/stdlib/scripts/install-build-tools-linux.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +##===----------------------------------------------------------------------===## +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +##===----------------------------------------------------------------------===## +set -euo pipefail + +LLVM_VERSION=17 +wget https://apt.llvm.org/llvm.sh +chmod +x llvm.sh +sudo ./llvm.sh $LLVM_VERSION +rm llvm.sh + +# Make common LLVM binaries (including FileCheck) in our PATH so they work when used in an unversioned context +# For example, instead of saying `FileCheck-17` which exists in `/usr/bin`, this allows us to just call +# FileCheck unqualified. +sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-$LLVM_VERSION 100 +sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-$LLVM_VERSION 100 +sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-$LLVM_VERSION 100 +sudo update-alternatives --install /usr/bin/ld.lld ld.lld /usr/bin/ld.lld-$LLVM_VERSION 100 +sudo update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-$LLVM_VERSION 100 +sudo update-alternatives --install /usr/bin/FileCheck FileCheck /usr/bin/FileCheck-$LLVM_VERSION 100 + +python3 -m pip install lit diff --git a/stdlib/scripts/install-build-tools-macos.sh b/stdlib/scripts/install-build-tools-macos.sh new file mode 100755 index 0000000000..a1c8ed4f71 --- /dev/null +++ b/stdlib/scripts/install-build-tools-macos.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +##===----------------------------------------------------------------------===## +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +##===----------------------------------------------------------------------===## +set -euo pipefail + +# Install `lit` for use in the tests +brew install lit + +# Ensure `FileCheck` from the pre-installed LLVM 15 package is visible +echo $(brew --prefix llvm@15)/bin/ >> $GITHUB_PATH From ccb9edcc931105f3e9bcbee964a830dc4f44842e Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 16 Jul 2024 17:38:24 -0700 Subject: [PATCH 1252/2019] Update docs for migration to Docusaurus 3.4 MODULAR_ORIG_COMMIT_REV_ID: c060d3cd9695fb8e473c5f1272a1b2db402a528e --- docs/{community.md => community.mdx} | 0 docs/{lib.md => lib.mdx} | 4 ++-- docs/manual/decorators/{index.md => index.mdx} | 2 +- docs/notebooks/{index.md => index.mdx} | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename docs/{community.md => community.mdx} (100%) rename docs/{lib.md => lib.mdx} (84%) rename docs/manual/decorators/{index.md => index.mdx} (98%) rename docs/notebooks/{index.md => index.mdx} (94%) diff --git a/docs/community.md b/docs/community.mdx similarity index 100% rename from docs/community.md rename to docs/community.mdx diff --git a/docs/lib.md b/docs/lib.mdx similarity index 84% rename from docs/lib.md rename to docs/lib.mdx index 93ff45569b..bbbab3104a 100644 --- a/docs/lib.md +++ b/docs/lib.mdx @@ -5,12 +5,12 @@ hide_table_of_contents: true description: A list of all modules in the Mojo standard library. listing: - id: stdlib - contents: "stdlib/*/*/index.md" + contents: 'stdlib/*/*/index.md' type: grid page-size: 99 --- These are all the modules in the Mojo standard library. -:::{#stdlib} +:::🔥#stdlib ::: diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.mdx similarity index 98% rename from docs/manual/decorators/index.md rename to docs/manual/decorators/index.mdx index 4f7f11ee43..f7c7d1a6af 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.mdx @@ -34,5 +34,5 @@ built directly into the compiler. The following pages describe each built-in decorator with examples. -:::{#docs} +:::🔥#docs ::: diff --git a/docs/notebooks/index.md b/docs/notebooks/index.mdx similarity index 94% rename from docs/notebooks/index.md rename to docs/notebooks/index.mdx index 09e76b05fe..636b8e4a25 100644 --- a/docs/notebooks/index.md +++ b/docs/notebooks/index.mdx @@ -13,7 +13,7 @@ listing: - RayTracing.ipynb type: grid grid-columns: 2 - sort: "false" + sort: 'false' --- The following pages are rendered from the Jupyter notebooks that are [available @@ -21,5 +21,5 @@ on GitHub](https://github.com/modularml/mojo/tree/main/examples/notebooks).


    -:::{#docs} +:::🔥#docs ::: From 8a23e1d8b1f751b7a3b2996ba1c81492f5e83197 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:46:24 -0400 Subject: [PATCH 1253/2019] [stdlib] Clean up `DTypePointer` uses in stdlib tests and benchmarks also fix imports MODULAR_ORIG_COMMIT_REV_ID: 5e707b675527f0c508ac200d804af5e04ca18a0a --- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 12 ++++++------ stdlib/src/os/env.mojo | 2 -- stdlib/src/os/os.mojo | 2 -- stdlib/test/builtin/test_print.mojo | 1 - stdlib/test/builtin/test_simd.mojo | 6 +++--- stdlib/test/memory/test_memory.mojo | 1 - 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index bf2cdff8a1..0e32e8ae83 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -33,7 +33,7 @@ from benchmark import ( run, ) from buffer import Buffer -from memory.unsafe import DTypePointer +from memory import UnsafePointer @value @@ -70,14 +70,14 @@ fn test_vectorize[ constrained[(N % simd_width) == 0]() # Create a mem of size N alias buffer_align = 64 - var vector = DTypePointer[dtype].alloc(N, alignment=buffer_align) - var result = DTypePointer[dtype].alloc(N, alignment=buffer_align) + var vector = UnsafePointer[Scalar[dtype]].alloc[alignment=buffer_align](N) + var result = UnsafePointer[Scalar[dtype]].alloc[alignment=buffer_align](N) @always_inline @parameter fn ld_vector[simd_width: Int](idx: Int): SIMD[size=simd_width].store( - vector, idx + 1, SIMD[vector.type, simd_width](idx) + vector, idx + 1, SIMD[dtype, simd_width](idx) ) @always_inline @@ -263,8 +263,8 @@ fn bench_compare(): alias unroll_factor = 2 alias its = 1000 - var p1 = DTypePointer[type].alloc(size) - var p2 = DTypePointer[type].alloc(size) + var p1 = UnsafePointer[Scalar[type]].alloc(size) + var p2 = UnsafePointer[Scalar[type]].alloc(size) print("Benchmark results") rand(p1.address, size) diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index 6ce4622a00..f7016bfa93 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -21,8 +21,6 @@ from os import setenv from sys import external_call, os_is_linux, os_is_macos -from memory import DTypePointer - from utils import StringRef diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index fb7cb0df26..01e5d7457e 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,8 +23,6 @@ from collections import List from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda from sys.ffi import C_char -from memory import DTypePointer - from utils import InlineArray, StringRef from .path import isdir, split diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index b84079b904..c0c9483014 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -17,7 +17,6 @@ import sys from tempfile import NamedTemporaryFile from testing import assert_equal -from memory import DTypePointer from builtin._location import __call_location, _SourceLocation from utils import StaticIntTuple, StringRef diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index c433cc2e91..b1f05dc7bd 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -136,7 +136,7 @@ def test_simd_repr(): def test_issue_1625(): var size = 16 alias simd_width = 8 - var ptr = DTypePointer[DType.int64].alloc(size) + var ptr = UnsafePointer[Int64].alloc(size) for i in range(size): ptr[i] = i @@ -156,10 +156,10 @@ def test_issue_1625(): def test_issue_20421(): - var a = DTypePointer[DType.uint8]().alloc(16 * 64, alignment=64) + var a = UnsafePointer[UInt8].alloc[alignment=64](16 * 64) for i in range(16 * 64): a[i] = i & 255 - var av16 = SIMD[size=4].load(a.offset(128 + 64 + 4).bitcast[DType.int32]()) + var av16 = SIMD[size=4].load(a.offset(128 + 64 + 4).bitcast[Int32]()) assert_equal( av16, SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448), diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index e92c2b8f02..c9b39efeb2 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -15,7 +15,6 @@ from sys import sizeof from memory import ( - DTypePointer, UnsafePointer, memcmp, memcpy, From b0ee8a241d0a14d1bbc43a7875580a38cb048a55 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 16 Jul 2024 21:37:22 -0700 Subject: [PATCH 1254/2019] Fix SIMD load/store MODULAR_ORIG_COMMIT_REV_ID: 0ab7248c56353257c565382d6aa26c551b864324 --- stdlib/src/builtin/simd.mojo | 77 ++++++------------------------------ 1 file changed, 13 insertions(+), 64 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ac6d5634f1..dbebb73d7b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2534,8 +2534,7 @@ struct SIMD[type: DType, size: Int]( fn load[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _]) -> Self: + ](ptr: DTypePointer[type, *_]) -> Self: """Loads the value the pointer points to. Constraints: @@ -2543,7 +2542,6 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to load from. @@ -2558,8 +2556,7 @@ struct SIMD[type: DType, size: Int]( fn load[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _], offset: Scalar) -> Self: + ](ptr: DTypePointer[type, *_], offset: Scalar) -> Self: """Loads the value the pointer points to with the given offset. Constraints: @@ -2568,7 +2565,6 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to load from. @@ -2585,8 +2581,7 @@ struct SIMD[type: DType, size: Int]( fn load[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: UnsafePointer[Scalar[type], address_space, _], offset: Int) -> Self: + ](ptr: UnsafePointer[Scalar[type], *_], offset: Int) -> Self: """Loads the value the pointer points to with the given offset. Constraints: @@ -2594,7 +2589,6 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to load from. @@ -2633,8 +2627,7 @@ struct SIMD[type: DType, size: Int]( fn load[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _], offset: Int) -> Self: + ](ptr: DTypePointer[type, *_], offset: Int) -> Self: """Loads the value the pointer points to with the given offset. Constraints: @@ -2642,7 +2635,6 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to load from. @@ -2654,38 +2646,12 @@ struct SIMD[type: DType, size: Int]( return Self.load[alignment=alignment](ptr.address, offset) - @staticmethod - @always_inline("nodebug") - fn load[ - *, - alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: UInt) -> Self: - """Loads the value the pointer points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. - - Args: - ptr: The pointer to load from. - offset: The offset to load from. - - Returns: - The loaded value. - """ - return Self.load(ptr, Int(offset.value)) - @staticmethod @always_inline fn store[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _], offset: Int, val: Self): + ](ptr: DTypePointer[type, *_], offset: Int, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2694,24 +2660,20 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to store to. offset: The offset to store to. val: The value to store. """ - Self.store[alignment=alignment, address_space=address_space]( - ptr.offset(offset), val - ) + Self.store[alignment=alignment](ptr.offset(offset), val) @staticmethod @always_inline fn store[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _], offset: Scalar, val: Self): + ](ptr: DTypePointer[type, *_], offset: Scalar, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2719,7 +2681,6 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to store to. @@ -2727,17 +2688,14 @@ struct SIMD[type: DType, size: Int]( val: The value to store. """ constrained[offset.type.is_integral(), "offset must be integer"]() - Self.store[alignment=alignment, address_space=address_space]( - ptr, int(offset), val - ) + Self.store[alignment=alignment](ptr, int(offset), val) @staticmethod @always_inline("nodebug") fn store[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space, _], val: Self): + ](ptr: DTypePointer[type, *_], val: Self): """Stores a single element value. Constraints: @@ -2745,23 +2703,19 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to store to. val: The value to store. """ - Self.store[alignment=alignment, address_space=address_space]( - ptr.address, val - ) + Self.store[alignment=alignment](ptr.address, val) @staticmethod @always_inline("nodebug") fn store[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: UnsafePointer[Scalar[type], address_space, _], val: Self): + ](ptr: UnsafePointer[Scalar[type], *_], val: Self): """Stores a single element value. Constraints: @@ -2769,7 +2723,6 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to store to. @@ -2788,8 +2741,7 @@ struct SIMD[type: DType, size: Int]( fn store[ *, alignment: Int = Self._default_alignment, - address_space: AddressSpace = AddressSpace.GENERIC, - ](ptr: DTypePointer[type, address_space], offset: UInt, val: Self): + ](ptr: DTypePointer[type, *_], offset: UInt, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2797,16 +2749,13 @@ struct SIMD[type: DType, size: Int]( Parameters: alignment: The minimal alignment of the address. - address_space: The address space the pointer is in. Args: ptr: The pointer to store to. offset: The offset to store to. val: The value to store. """ - Self.store[alignment=alignment, address_space=address_space]( - ptr.offset(offset.value), val - ) + Self.store[alignment=alignment](ptr.offset(offset), val) # ===----------------------------------------------------------------------=== # From 1c30444cf1ec78b5be4f3e057c8e3a853b95db77 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 17 Jul 2024 07:27:15 -0700 Subject: [PATCH 1255/2019] [******][Math] Remove overload from iota function Use default arg to remove an overload of iota. MODULAR_ORIG_COMMIT_REV_ID: 764fc8f1b985d9efacb76ab62472aaa5cd043efe --- stdlib/src/math/math.mojo | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 89a91df3ee..8fe3d6fa56 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -931,24 +931,10 @@ fn isclose[ # ===----------------------------------------------------------------------=== # -@always_inline -fn iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: - """Creates a SIMD vector containing an increasing sequence, starting from 0. - - Parameters: - type: The `dtype` of the input and output SIMD vector. - simd_width: The width of the input and output SIMD vector. - - Returns: - An increasing sequence of values, starting from 0. - """ - return iota[type, simd_width](0) - - @always_inline fn iota[ type: DType, simd_width: Int -](offset: Scalar[type]) -> SIMD[type, simd_width]: +](offset: Scalar[type] = 0) -> SIMD[type, simd_width]: """Creates a SIMD vector containing an increasing sequence, starting from offset. From 065eb62b97dcacb12159506c440d24d2fc5ba29b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 17 Jul 2024 07:28:10 -0700 Subject: [PATCH 1256/2019] [Stdlib] Micro-optimize some arithmetic in the hash function, NFC MODULAR_ORIG_COMMIT_REV_ID: 67295929ee01b6bd26269444da8411cfa0eb3fbd --- stdlib/src/builtin/hash.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 348d270f74..3e6d96a05f 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -244,8 +244,8 @@ fn hash(bytes: UnsafePointer[UInt8], n: Int) -> Int: # Compute our SIMD strides and tail length # n == k * stride + r - var k = n // stride - var r = n % stride + var k = n._positive_div(stride) + var r = n._positive_rem(stride) debug_assert(n == k * stride + r, "wrong hash tail math") # 1. Reinterpret the underlying data as a larger int type From b9ab5fcd37e8da96b9bc6ad1c7fed5ae5bfc1d8f Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 17 Jul 2024 10:28:13 -0700 Subject: [PATCH 1257/2019] [Docs] Fix typo in types.ipynb MODULAR_ORIG_COMMIT_REV_ID: 5493172c6dc89238d6bf076df3570928ec61d71d --- docs/manual/types.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 993eea5cdc..b8630ad8d7 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -658,6 +658,7 @@ " ```mojo\n", " # Doesn't work!\n", " var list: List[Int] = [2, 3, 5]\n", + " ```\n", "\n", " But you can use variadic arguments to achieve the same thing:\n", "\n", From bba2e5f1a02786418280f32231d7e176debaaf81 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:58:38 -0400 Subject: [PATCH 1258/2019] [stdlib] Change `__hash__` to return a `UInt` This would simplify code and increase performance. MSTDL-791 MODULAR_ORIG_COMMIT_REV_ID: 8ffdc7c13192fd3009ddc82e771f52163aa74066 --- docs/changelog.md | 4 ++++ stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/hash.mojo | 16 ++++++++-------- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/collections/set.mojo | 2 +- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/python/object.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/collections/test_dict.mojo | 2 +- 12 files changed, 22 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3cb11dc845..1c7f775ffe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -471,6 +471,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD.load/store` now supports `UnsafePointer` overloads. +- Now that we have a `UInt` type, use this to represent the return type of hash. + In general, hashes should be an unsigned integer, and can also lead to improved + performance in certain cases. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index bd88207820..9fc0a9bdb5 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -230,7 +230,7 @@ struct DType( self._as_i8(), rhs._as_i8() ) - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Return a 64-bit hash for this `DType` value. Returns: diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 3e6d96a05f..463d9bd987 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -14,7 +14,7 @@ There are a few main tools in this module: -- `Hashable` trait for types implementing `__hash__(self) -> Int` +- `Hashable` trait for types implementing `__hash__(self) -> UInt` - `hash[T: Hashable](hashable: T) -> Int` built-in function. - A `hash()` implementation for arbitrary byte strings, `hash(data: UnsafePointer[UInt8], n: Int) -> Int`, @@ -45,11 +45,11 @@ from utils import InlineArray # var HASH_SECRET = int(random.random_ui64(0, UInt64.MAX) -fn _HASH_SECRET() -> Int: +fn _HASH_SECRET() -> UInt: var ptr = _get_global[ "HASH_SECRET", _initialize_hash_secret, _destroy_hash_secret ]() - return ptr.bitcast[Int]()[0] + return ptr.bitcast[UInt]()[0] fn _initialize_hash_secret( @@ -79,7 +79,7 @@ trait Hashable: ```mojo @value struct Foo(Hashable): - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: return 4 # chosen by fair random dice roll var foo = Foo() @@ -87,7 +87,7 @@ trait Hashable: ``` """ - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Return a 64-bit hash of the type's data. Returns: @@ -96,7 +96,7 @@ trait Hashable: ... -fn hash[T: Hashable](hashable: T) -> Int: +fn hash[T: Hashable](hashable: T) -> UInt: """Hash a Hashable type using its underlying hash implementation. Parameters: @@ -148,7 +148,7 @@ alias _HASH_UPDATE = _djbx33a_hash_update # performance issue we've been seeing with Dict. It's still not ideal as # a long-term hash function. @always_inline -fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: +fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> UInt: """Hash a SIMD byte vector using direct DJBX33A hash algorithm. See `hash(bytes, n)` documentation for more details. @@ -186,7 +186,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: return int(final_data) -fn hash(bytes: UnsafePointer[UInt8], n: Int) -> Int: +fn hash(bytes: UnsafePointer[UInt8], n: Int) -> UInt: """Hash a byte array using a SIMD-modified DJBX33A hash algorithm. _This hash function is not suitable for cryptographic purposes._ The diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 39be20095e..963b9db0d3 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1084,7 +1084,7 @@ struct Int( """ return str(self) - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Hash the int using builtin hash. Returns: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index dbebb73d7b..e3cd454322 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1453,7 +1453,7 @@ struct SIMD[type: DType, size: Int]( # TODO: see how can we implement this. return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Hash the value using builtin hash. Returns: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5483d65ca0..d33bb11525 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1779,7 +1779,7 @@ struct String( l_idx += 1 return self[l_idx:] - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Hash the underlying buffer using builtin hash. Returns: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index d512635794..650904b235 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -242,7 +242,7 @@ struct StringLiteral( """ return self.__str__().__repr__() - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Hash the underlying buffer using builtin hash. Returns: diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 6002229a85..0878db4b78 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -287,7 +287,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return len(self._data) - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """A hash value of the elements in the set. The hash value is order independent, so s1 == s2 -> hash(s1) == hash(s2). diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 27f3d12bd1..30117da11b 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -228,7 +228,7 @@ struct Path( """ return not self == other - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Hash the underlying path string using builtin hash. Returns: diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index b449cf08ca..67c0a5b5b5 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -453,7 +453,7 @@ struct PythonObject( raise Error("object has no len()") return result - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Returns the length of the object. Returns: diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index da12d04d30..13becfa999 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -345,7 +345,7 @@ struct StringRef( """ return len(self) != 0 - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: """Hash the underlying buffer using builtin hash. Returns: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 0b96330db7..fbe8823671 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -396,7 +396,7 @@ struct DummyKey(KeyElement): fn __init__(inout self, *, other: Self): self = other - fn __hash__(self) -> Int: + fn __hash__(self) -> UInt: return self.value fn __eq__(self, other: DummyKey) -> Bool: From 952b38e4f1a173f816330384bb95b8d5b0265b35 Mon Sep 17 00:00:00 2001 From: Yinon Burgansky Date: Wed, 17 Jul 2024 14:47:40 -0500 Subject: [PATCH 1259/2019] [External] [stdlib] `write_to(writer, ...)` -> `writer.write(...)` (#43532) [External] [stdlib] `write_to(writer, ...)` -> `writer.write(...)` Use `writer.write(...)` instead of `write_to(writer, ...)`. Remove the `write_to` method. Co-authored-by: Yinon Burgansky Closes modularml/mojo#3225 MODULAR_ORIG_COMMIT_REV_ID: 8a6fff0507f13f7f79ff679fa94ae1fac4d8ee4d --- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/collections/list.mojo | 3 +-- stdlib/src/collections/optional.mojo | 8 ++++---- stdlib/src/collections/set.mojo | 8 ++++---- stdlib/src/utils/_format.mojo | 13 ------------- stdlib/test/utils/test_format.mojo | 10 +++++----- stdlib/test/utils/test_format_to_stdout.mojo | 8 ++++---- 7 files changed, 19 insertions(+), 33 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 2dbf31008b..e8b5e0e4ee 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -29,7 +29,7 @@ from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, StaticString, StringSlice -from utils._format import Formattable, Formatter, write_to +from utils._format import Formattable, Formatter # ===----------------------------------------------------------------------=== # # _file_handle diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 4f29e2c046..3dd83fb545 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -25,7 +25,6 @@ from sys.intrinsics import _type_is_eq from memory import Reference, UnsafePointer from utils import Span -from utils._format import write_to from .optional import Optional @@ -419,7 +418,7 @@ struct List[T: CollectionElement]( ```mojo var my_list = List[Int](1, 2, 3) - print(my_list.__repr__(my_list)) + print(my_list.__repr__()) ``` When the compiler supports conditional methods, then a simple `repr(my_list)` will diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 496f74f0e1..34cc718fe9 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -285,9 +285,9 @@ struct Optional[T: CollectionElement]( """ var output = String() var writer = output._unsafe_to_formatter() - write_to(writer, "Optional(") + writer.write("Optional(") self.format_to(writer) - write_to(writer, ")") + writer.write(")") return output fn format_to[ @@ -303,9 +303,9 @@ struct Optional[T: CollectionElement]( writer: The formatter to write to. """ if self: - write_to(writer, repr(self.value())) + writer.write(repr(self.value())) else: - write_to(writer, "None") + writer.write("None") # ===-------------------------------------------------------------------===# # Methods diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 0878db4b78..64fca39d4a 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -340,14 +340,14 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): Args: writer: The formatter to write to. """ - write_to(writer, "{") + writer.write("{") var written = 0 for item in self: - write_to(writer, repr(item[])) + writer.write(repr(item[])) if written < len(self) - 1: - write_to(writer, ", ") + writer.write(", ") written += 1 - write_to(writer, "}") + writer.write("}") # ===-------------------------------------------------------------------===# # Methods diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index 1d6d98b465..bfa928db63 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -184,16 +184,3 @@ struct Formatter: _put(strref) return Formatter(write_to_stdout, UnsafePointer[NoneType]()) - - -# TODO: Use Formatter.write instead. -fn write_to[*Ts: Formattable](inout writer: Formatter, *args: *Ts): - """ - Write a sequence of formattable arguments to the provided formatter. - """ - - @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(writer) - - args.each[write_arg]() diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 002b50d3db..e876b3082e 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -14,7 +14,7 @@ from testing import assert_equal -from utils._format import Formattable, Formatter, write_to +from utils._format import Formattable, Formatter from utils.inline_string import _FixedString @@ -35,7 +35,7 @@ struct Point(Formattable, Stringable): @no_inline fn format_to(self, inout writer: Formatter): - write_to(writer, "Point(", self.x, ", ", self.y, ")") + writer.write("Point(", self.x, ", ", self.y, ")") @no_inline fn __str__(self) -> String: @@ -52,11 +52,11 @@ fn test_formatter_of_string() raises: assert_equal(s1, "Point(2, 7)") # - # Test write_to(String, ..) + # Test fmt.write(String, ..) # var s2 = String() var s2_fmt = Formatter(s2) - write_to(s2_fmt, Point(3, 8)) + s2_fmt.write(Point(3, 8)) assert_equal(s2, "Point(3, 8)") @@ -78,7 +78,7 @@ fn test_stringable_based_on_format() raises: fn test_formatter_of_fixed_string() raises: var s1 = _FixedString[100]() var s1_fmt = Formatter(s1) - write_to(s1_fmt, "Hello, World!") + s1_fmt.write("Hello, World!") assert_equal(str(s1), "Hello, World!") diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index 76fb2713e9..9d5a6a4d9c 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils._format import Formattable, Formatter, write_to +from utils._format import Formattable, Formatter fn main() raises: @@ -25,7 +25,7 @@ struct Point(Formattable): var y: Int fn format_to(self, inout writer: Formatter): - write_to(writer, "Point(", self.x, ", ", self.y, ")") + writer.write("Point(", self.x, ", ", self.y, ")") # CHECK-LABEL: test_write_to_stdout @@ -35,8 +35,8 @@ fn test_write_to_stdout(): var stdout = Formatter.stdout() # CHECK: Hello, World! - write_to(stdout, "Hello, World!") + stdout.write("Hello, World!") # CHECK: point = Point(1, 1) var point = Point(1, 1) - write_to(stdout, "point = ", point) + stdout.write("point = ", point) From 4e129c64976c279eb9fbdef12868067d4f57bdca Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:22:54 -0500 Subject: [PATCH 1260/2019] [External] [stdlib] `__contains__` for `ListLiteral` (#43606) [External] [stdlib] `__contains__` for `ListLiteral` An implementation of the `__contains__` function for `ListLiteral` as per issue https://github.com/modularml/mojo/issues/2658. The function parameterizes the `EqualityComparable` trait, this maintains consistency with `Tuple.__contains__` behavior (in nightly). ORIGINAL_AUTHOR=Joshua James Venter <67124214+jjvraw@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3251 Co-authored-by: Joshua James Venter <67124214+jjvraw@users.noreply.github.com> Closes modularml/mojo#3251 MODULAR_ORIG_COMMIT_REV_ID: 2ebceae2217a745fc10d8a61205c2cad5220969d --- docs/changelog.md | 4 ++++ stdlib/src/builtin/builtin_list.mojo | 28 ++++++++++++++++++++++ stdlib/test/builtin/test_list.mojo | 36 +++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1c7f775ffe..50939f18c4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -297,6 +297,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. # Insert (2/3 of 1024) entries ``` +- `ListLiteral` now supports `__contains__`. + ([PR #3251](https://github.com/modularml/mojo/pull/3251) by + [@jjvraw](https://github.com/jjvraw)) + ### 🦋 Changed - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 368e9a9cfc..df3b19ae14 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -17,6 +17,8 @@ These are Mojo built-ins, so you don't need to import them. from memory import Reference, UnsafePointer +from sys.intrinsics import _type_is_eq + # ===----------------------------------------------------------------------===# # ListLiteral # ===----------------------------------------------------------------------===# @@ -75,6 +77,32 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): # FIXME: Rebinding to a different lifetime. return UnsafePointer.address_of(self.storage[i]).bitcast[T]()[] + @always_inline("nodebug") + fn __contains__[T: EqualityComparable](self, value: T) -> Bool: + """Determines if a given value exists in the ListLiteral. + + Parameters: + T: The type of the value to search for. Must implement the + `EqualityComparable` trait. + + Args: + value: The value to search for in the ListLiteral. + + Returns: + True if the value is found in the ListLiteral, False otherwise. + """ + + @parameter + for i in range(len(VariadicList(Ts))): + if _type_is_eq[Ts[i], T](): + var elt_ptr = UnsafePointer.address_of(self.storage[i]).bitcast[ + T + ]() + if elt_ptr[] == value: + return True + + return False + # ===----------------------------------------------------------------------===# # VariadicList / VariadicListMem diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 52e7a8801d..66e5048888 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false fn test_list() raises: @@ -39,7 +39,41 @@ fn test_repr_list() raises: assert_equal(empty.__repr__(), "[]") +fn test_contains() raises: + # List + var l = List[String]("Hello", ",", "World", "!") + assert_true("Hello" in l) + assert_true(l.__contains__(String(","))) + assert_true("World" in l) + assert_true("!" in l) + assert_false("Mojo" in l) + assert_false(l.__contains__("hello")) + assert_false("" in l or l.__contains__("")) + + # ListLiteral + var h = [1, False, String("Mojo")] + assert_true(1 in h) + assert_true(h.__contains__(1)) + assert_false(True in h) + assert_false(h.__contains__(True)) + assert_false(0 in h) + assert_true(False in h) + assert_false("Mojo" in h) + assert_true(String("Mojo") in h) + assert_false(String("") in h) + assert_false("" in h) + + # TODO: + # Reevaluate the strict type checking behaviour in ListLiteral.__contains__. + # Consider aligning with the behavior with List.__contains__ when possible + # or a feasible workaround is identified. + # For instance, consider the following: + assert_true("Hello" in l and String("Hello") in l) + assert_true("Mojo" not in h and String("Mojo") in h) + + def main(): test_list() test_variadic_list() test_repr_list() + test_contains() From fb8da51bed3aab4087dc83e68a45c06c8a121940 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:26:21 -0400 Subject: [PATCH 1261/2019] [stdlib] Add `DType` overloads to `bitcast` This makes removing `DTypePointer` easier and we can remove it very easily by doing a find-and-replace. MODULAR_ORIG_COMMIT_REV_ID: c9ff4a9f98dad3862324b46d38040d6eb389a28e --- stdlib/src/memory/unsafe_pointer.mojo | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f0ac4007f7..54b48fbc8b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -676,6 +676,26 @@ struct UnsafePointer[ _type = UnsafePointer[T, address_space]._mlir_type, ](self.address) + @always_inline("nodebug") + fn bitcast[ + T: DType, + /, + address_space: AddressSpace = Self.address_space, + ](self) -> UnsafePointer[Scalar[T], address_space]: + """Bitcasts a UnsafePointer to a different type. + + Parameters: + T: The target type. + address_space: The address space of the result. + + Returns: + A new UnsafePointer object with the specified type and the same address, + as the original UnsafePointer. + """ + return __mlir_op.`pop.pointer.bitcast`[ + _type = UnsafePointer[Scalar[T], address_space]._mlir_type, + ](self.address) + @always_inline fn destroy_pointee(self: UnsafePointer[_]): """Destroy the pointed-to value. From 9057bb3140a2c016376971d81527e7be4400b936 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:43:04 -0500 Subject: [PATCH 1262/2019] [External] [stdlib] Fix atol parsing for prefixed integer literals with leading underscores (#43607) [External] [stdlib] Fix atol parsing for prefixed integer literals with leading underscores This addresses https://github.com/modularml/mojo/issues/3182 to correctly parse prefixed string literals. Changes include: - Bug-fix - Added appropriate test cases to verify the new behavior When the base 2, 8, 16 are set or inferred (base 0), for a string prefixed appropriately, leading underscores should be allowed as per Python's integer literal grammar: ``` integer ::= decinteger | bininteger | octinteger | hexinteger decinteger ::= nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")* bininteger ::= "0" ("b" | "B") (["_"] bindigit)+ octinteger ::= "0" ("o" | "O") (["_"] octdigit)+ hexinteger ::= "0" ("x" | "X") (["_"] hexdigit)+ nonzerodigit ::= "1"..."9" digit ::= "0"..."9" bindigit ::= "0" | "1" octdigit ::= "0"..."7" hexdigit ::= digit | "a"..."f" | "A"..."F" ``` Currently, this such a string passed to `atol` raises. The changes in this PR fixes this issue. ORIGINAL_AUTHOR=Joshua James Venter <67124214+jjvraw@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3180 Co-authored-by: Joshua James Venter <67124214+jjvraw@users.noreply.github.com> Closes modularml/mojo#3180 MODULAR_ORIG_COMMIT_REV_ID: 71bba679c2eeeea09f0bd4393611da50bc6570a2 --- docs/changelog.md | 7 +++++ stdlib/src/builtin/string.mojo | 12 +++++--- stdlib/test/builtin/test_string.mojo | 41 ++++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 50939f18c4..791379bfe9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -479,6 +479,13 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. In general, hashes should be an unsigned integer, and can also lead to improved performance in certain cases. +- The `atol` function now correctly supports leading underscores, + (e.g.`atol("0x_ff", 0)`), when the appropriate base is specified or inferred + (base 0). non-base-10 integer literals as per Python's [Integer Literals](\ + ). + ([PR #3180](https://github.com/modularml/mojo/pull/3180) + by [@jjvraw](https://github.com/jjvraw)) + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index d33bb11525..3be49de23a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -228,6 +228,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: var ord_letter_max = (-1, -1) var result = 0 var is_negative: Bool = False + var has_prefix: Bool = False var start: Int = 0 var str_len = len(str_ref) var buff = str_ref.unsafe_ptr() @@ -250,14 +251,17 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: str_ref[start + 1] == "b" or str_ref[start + 1] == "B" ): start += 2 + has_prefix = True elif base == 8 and ( str_ref[start + 1] == "o" or str_ref[start + 1] == "O" ): start += 2 + has_prefix = True elif base == 16 and ( str_ref[start + 1] == "x" or str_ref[start + 1] == "X" ): start += 2 + has_prefix = True alias ord_0 = ord("0") # FIXME: @@ -269,6 +273,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: var real_base_new_start = _identify_base(str_ref, start) real_base = real_base_new_start[0] start = real_base_new_start[1] + has_prefix = real_base != 10 if real_base == -1: raise Error(_atol_error(base, str_ref)) else: @@ -285,10 +290,9 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: var found_valid_chars_after_start = False var has_space_after_number = False - # single underscores are only allowed between digits - # starting "was_last_digit_undescore" to true such that - # if the first digit is an undesrcore an error is raised - var was_last_digit_undescore = True + # Prefixed integer literals with real_base 2, 8, 16 may begin with leading + # underscores under the conditions they have a prefix + var was_last_digit_undescore = not (real_base in (2, 8, 16) and has_prefix) for pos in range(start, str_len): var ord_current = int(buff[pos]) if ord_current == ord_underscore: diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 1a0fe0c470..025db4956f 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -365,6 +365,9 @@ def test_atol(): assert_equal(10, atol("0o12", 8)) assert_equal(10, atol("0O12", 8)) assert_equal(35, atol("Z", 36)) + assert_equal(255, atol("0x_00_ff", 16)) + assert_equal(18, atol("0b0001_0010", 2)) + assert_equal(18, atol("0b_000_1001_0", 2)) # Negative cases with assert_raises( @@ -398,12 +401,37 @@ def test_atol(): ): _ = atol("5", 5) + with assert_raises( + contains="String is not convertible to integer with base 10: '0x_ff'" + ): + _ = atol("0x_ff") + + with assert_raises( + contains="String is not convertible to integer with base 3: '_12'" + ): + _ = atol("_12", 3) + with assert_raises(contains="Base must be >= 2 and <= 36, or 0."): _ = atol("0", 1) with assert_raises(contains="Base must be >= 2 and <= 36, or 0."): _ = atol("0", 37) + with assert_raises( + contains="String is not convertible to integer with base 16: '_ff'" + ): + _ = atol("_ff", base=16) + + with assert_raises( + contains="String is not convertible to integer with base 2: ' _01'" + ): + _ = atol(" _01", base=2) + + with assert_raises( + contains="String is not convertible to integer with base 10: '0x_ff'" + ): + _ = atol("0x_ff") + with assert_raises( contains="String is not convertible to integer with base 10: ''" ): @@ -433,6 +461,14 @@ def test_atol_base_0(): assert_equal(0, atol("0X0", base=0)) + assert_equal(255, atol("0x_00_ff", base=0)) + + assert_equal(18, atol("0b_0001_0010", base=0)) + assert_equal(18, atol("0b000_1001_0", base=0)) + + assert_equal(10, atol("0o_000_12", base=0)) + assert_equal(10, atol("0o00_12", base=0)) + with assert_raises( contains="String is not convertible to integer with base 0: ' 0x'" ): @@ -453,11 +489,6 @@ def test_atol_base_0(): ): _ = atol("0r100", base=0) - with assert_raises( - contains="String is not convertible to integer with base 0: '0b_0'" - ): - _ = atol("0b_0", base=0) - with assert_raises( contains="String is not convertible to integer with base 0: '0xf__f'" ): From 5999dd6c109d7437e99a75c3e7769bf92d288fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20Sz=C5=B1cs?= Date: Wed, 17 Jul 2024 18:10:23 -0500 Subject: [PATCH 1263/2019] [External] [stdlib] Support creating nested python objects using list literals (#43636) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Support creating nested python objects using list literals Previously creating python objects from list literals was not possible making the python experience less convenient. Noticed this while I wanted to create a pyarrow schema from mojo. Instead of the following: ```mojo var pa = Python.import_module("pyarrow") var int_field = pa.field("int_field", pa.int32()) var string_field = pa.field("string_field", pa.string()) var schema = pa.schema([int_field, string_field]) ``` I had to append the fields to an empty schema: ```mojo var schema = pa.schema([]) schema = schema.append(some_int) schema = schema.append(some_string) ``` This change allows creating a `PythonObject` from both `ListLiteral`s and `Tuple`s containing `PythonObject` values. Co-authored-by: Krisztián Szűcs Closes modularml/mojo#3264 MODULAR_ORIG_COMMIT_REV_ID: 8f0560a624c5f3b19409486c7d16386cda93f502 --- docs/changelog.md | 21 +++++++++++++++++++++ stdlib/src/python/object.mojo | 14 ++++++++++---- stdlib/test/python/test_python_object.mojo | 11 +++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 791379bfe9..206ea2aae4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,27 @@ what we publish. ### ⭐️ New +- Creating nested `PythonObject` from a list or tuple of python objects is + possible now: + + ```mojo + var np = Python.import_module("numpy") + var a = np.array([1, 2, 3]) + var b = np.array([4, 5, 6]) + var arrays = PythonObject([a, b]) + assert_equal(len(arrays), 2) + ``` + + Also allowing more convenient call syntax: + + ```mojo + var stacked = np.hstack((a, b)) + assert_equal(str(stacked), "[1 2 3 4 5 6]") + ``` + + ([PR 3264#](https://github.com/modularml/mojo/pull/3264) by + [@kszucs](https://github.com/kszucs)) + - `List[T]` values are now equality comparable with `==` and `!=` when `T` is equality comparable. ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 67c0a5b5b5..1b38ccee7a 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -255,7 +255,9 @@ struct PythonObject( var obj: PythonObject @parameter - if _type_is_eq[T, Int](): + if _type_is_eq[T, PythonObject](): + obj = value.get[i, PythonObject]() + elif _type_is_eq[T, Int](): obj = PythonObject(value.get[i, Int]()) elif _type_is_eq[T, Float64](): obj = PythonObject(value.get[i, Float64]()) @@ -268,8 +270,9 @@ struct PythonObject( else: obj = PythonObject(0) constrained[ - False, "cannot convert nested list element to object" + False, "cannot convert list element to python object" ]() + cpython.Py_IncRef(obj.py_object) _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) @@ -295,7 +298,9 @@ struct PythonObject( var obj: PythonObject @parameter - if _type_is_eq[T, Int](): + if _type_is_eq[T, PythonObject](): + obj = value.get[i, PythonObject]() + elif _type_is_eq[T, Int](): obj = PythonObject(value.get[i, Int]()) elif _type_is_eq[T, Float64](): obj = PythonObject(value.get[i, Float64]()) @@ -308,8 +313,9 @@ struct PythonObject( else: obj = PythonObject(0) constrained[ - False, "cannot convert nested list element to object" + False, "cannot convert list element to python object" ]() + cpython.Py_IncRef(obj.py_object) _ = cpython.PyTuple_SetItem(self.py_object, i, obj.py_object) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index b60323830f..c0c7b01574 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -339,6 +339,16 @@ def test_is(): assert_true(l1 is not l2) +def test_nested_object(): + var a = PythonObject([1, 2, 3]) + var b = PythonObject([4, 5, 6]) + var nested_list = PythonObject([a, b]) + var nested_tuple = PythonObject((a, b)) + + assert_equal(str(nested_list), "[[1, 2, 3], [4, 5, 6]]") + assert_equal(str(nested_tuple), "([1, 2, 3], [4, 5, 6])") + + fn test_iter() raises: var list_obj: PythonObject = ["apple", "orange", "banana"] var i = 0 @@ -411,3 +421,4 @@ def main(): test_setitem() test_dict() test_none() + test_nested_object() From b8aa6521a8c0d6a79c8ab0015a538bf6930e4dfe Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 18 Jul 2024 13:31:55 -0700 Subject: [PATCH 1264/2019] [External] [stdlib] Implement `Dict.setdefault(key, default)` (#42558) [External] [stdlib] Implement `Dict.setdefault(key, default)` Implement `Dict.setdefault` which returns a reference with the value of the item with the specified key. If the key does not exist, it is inserted with the specified value into the `Dict`. Co-authored-by: Manuel Saelices Closes modularml/mojo#2803 MODULAR_ORIG_COMMIT_REV_ID: a5514f823c3b7dc978607435d8082f1efe14aa13 --- docs/changelog.md | 5 +++++ stdlib/src/collections/dict.mojo | 18 ++++++++++++++++++ stdlib/test/collections/test_dict.mojo | 23 +++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 206ea2aae4..d4b4344f67 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -157,6 +157,11 @@ what we publish. - The `Reference` type (and many iterators) now use "inferred" parameters to represent the mutability of their lifetime, simplifying the interface. +- `Dict` now implements `setdefault`, to get a value from the dictionary by + key, or set it to a default if it doesn't exist + ([PR #2803](https://github.com/modularml/mojo/pull/2803) + by [@msaelices](https://github.com/msaelices)) + - Added new `ExplicitlyCopyable` trait, to mark types that can be copied explicitly, but which might not be implicitly copyable. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 1b74ccbfa0..37b2d10c13 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -893,6 +893,24 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._entries = Self._new_entries(Self._initial_reservation) self._index = _DictIndex(self._reserved()) + fn setdefault( + ref [_]self: Self, key: K, owned default: V + ) raises -> Reference[V, __lifetime_of(self)]: + """Get a value from the dictionary by key, or set it to a default if it doesn't exist. + + Args: + key: The key to search for in the dictionary. + default: The default value to set if the key is not present. + + Returns: + The value associated with the key, or the default value if it wasn't present. + """ + try: + return self._find_ref(key) + except KeyError: + self[key] = default^ + return self._find_ref(key) + @staticmethod @always_inline fn _new_entries(reserve_at_least: Int) -> List[Optional[DictEntry[K, V]]]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index fbe8823671..579f8bd887 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -563,6 +563,28 @@ def test_init_initial_capacity(): assert_equal(y._reserved(), 64) +fn test_dict_setdefault() raises: + var some_dict = Dict[String, Int]() + some_dict["key1"] = 1 + some_dict["key2"] = 2 + assert_equal(some_dict.setdefault("key1", 0)[], 1) + assert_equal(some_dict.setdefault("key2", 0)[], 2) + assert_equal(some_dict.setdefault("not_key", 0)[], 0) + assert_equal(some_dict["not_key"], 0) + + # Check that there is no copy of the default value, so it's performant + var other_dict = Dict[String, CopyCounter]() + var a = CopyCounter() + var a_def = CopyCounter() + var b_def = CopyCounter() + other_dict["a"] = a^ + assert_equal(1, other_dict["a"].copy_count) + _ = other_dict.setdefault("a", a_def^) + _ = other_dict.setdefault("b", b_def^) + assert_equal(1, other_dict["a"].copy_count) + assert_equal(1, other_dict["b"].copy_count) + + def main(): test_dict() test_dict_fromkeys() @@ -574,3 +596,4 @@ def main(): test_find_get() test_clear() test_init_initial_capacity() + test_dict_setdefault() From baa0be90c5c9d75d6b4a684e00f0f0da6047950f Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 18 Jul 2024 13:37:53 -0700 Subject: [PATCH 1265/2019] [External] [stdlib] Improve docstring in `polynomial.mojo` (#42085) [External] [stdlib] Improve docstring in `polynomial.mojo` Improve the `_horner_evaluate` docstring to match the argument names. Co-authored-by: soraros Closes modularml/mojo#3066 MODULAR_ORIG_COMMIT_REV_ID: 228e5b772e138765b9e2d73a8f5f1bead515661d --- stdlib/src/math/polynomial.mojo | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/src/math/polynomial.mojo b/stdlib/src/math/polynomial.mojo index 28efe2d1a8..8f2e27f584 100644 --- a/stdlib/src/math/polynomial.mojo +++ b/stdlib/src/math/polynomial.mojo @@ -62,11 +62,12 @@ fn _horner_evaluate[ ](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: """Evaluates the polynomial using the passed in value and the specified coefficients using the Horner scheme. The Horner scheme evaluates the - polynomial as `horner(val, coeffs)` where val is a scalar and coeffs is a - list of coefficients [c0, c1, c2, ..., cn] by: + polynomial at point x as `horner(x, coeffs)` where x is a scalar and coeffs + is a list of coefficients [c0, c1, c2, ..., cn] by: ``` - horner(val, coeffs) = c0 + val * (c1 + val * (c2 + val * (... + val * cn))) - = fma(val, horner(val, coeffs[1:]), c0) + horner(x, coeffs) + = c0 + x * (c1 + x * (c2 + x * (... + x * cn))) + = fma(x, horner(x, coeffs[1:]), coeffs[0]) ``` Parameters: @@ -78,8 +79,7 @@ fn _horner_evaluate[ x: The value to compute the polynomial with. Returns: - The polynomial evaluation results using the specified value and the - constant coefficients. + The polynomial specified by the coefficients evaluated at value x. """ alias num_coefficients = len(coefficients) alias c_last = coefficients[num_coefficients - 1] From 3ae79a730770b021d6acba3e13dbd072843bb884 Mon Sep 17 00:00:00 2001 From: codingonion Date: Thu, 18 Jul 2024 14:05:28 -0700 Subject: [PATCH 1266/2019] [External] [examples] Fix typo in examples/matmul.mojo (#43605) [External] [examples] Fix typo in examples/matmul.mojo Co-authored-by: codingonion Closes modularml/mojo#3265 MODULAR_ORIG_COMMIT_REV_ID: c3999f569e9c968f8e2b2c1199a62648873c3730 --- examples/matmul.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 1eaa08970b..f297d4d54d 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -50,7 +50,7 @@ alias tile_k = 4 # K must be a multiple of this struct Matrix[rows: Int, cols: Int]: var data: DTypePointer[type] - # Initialize zeroeing all values + # Initialize zeroing all values fn __init__(inout self): self.data = DTypePointer[type].alloc(rows * cols) memset_zero(self.data.address, rows * cols) From f2dc69e4cdf22db2cab00366a0acb4f0d8c935db Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Fri, 19 Jul 2024 03:44:35 +0300 Subject: [PATCH 1267/2019] [stdlib] Simplify splat constructors for SIMD and make it not `ImplicitlyBoolable` A new, explicit splatting constructor is added, and the splat logic from the variadic scalar constructor is removed. This ensures that constructing from a scalar doesn't get deprioritized during overload resolution (due to variadic signatures having a lower priority, even if the variadic is passed only a single value). The existing `Float64` constructor is removed to remove ambiguity with the new generic scalar constructor; this also fixes some unintentional implicit conversions that could degrare floating point arithmetic precision. These changes had a ripple effect that neccessitated removing the `ImplicitlyBoolable` trait conformance to avoid accidental conversion from `SIMD` to `SIMD` through `Bool` Fixes https://github.com/modularml/mojo/issues/3045. MODULAR_ORIG_COMMIT_REV_ID: c832f47e16ffa62419a5e42ae959dbbcfe289b1f --- docs/changelog.md | 7 +++ examples/mandelbrot.mojo | 6 +-- examples/notebooks/Mandelbrot.ipynb | 6 +-- examples/notebooks/RayTracing.ipynb | 20 +++---- stdlib/src/builtin/bool.mojo | 9 ++++ stdlib/src/builtin/simd.mojo | 82 ++++++++--------------------- stdlib/test/builtin/test_simd.mojo | 11 +--- 7 files changed, 57 insertions(+), 84 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d4b4344f67..4c33358d45 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -553,5 +553,12 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#3142](https://github.com/modularml/mojo/issues/3142) - [QoI] Confusing `__setitem__` method is failing with a "must be mutable" error. + - [#248](https://github.com/modularml/mojo/issues/248) - [Feature] Enable `__setitem__` to take variadic arguments + +- [#3065] - Fix incorrect behavior + of `SIMD.__int__` on unsigned types + +- [#3045] - Disable implicit SIMD + conversion routes through `Bool` diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 6e2e9deef3..d6b3b10df3 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -73,14 +73,14 @@ fn main() raises: @parameter fn worker(row: Int): - var scale_x = (max_x - min_x) / cols - var scale_y = (max_y - min_y) / rows + alias scale_x = (max_x - min_x) / cols + alias scale_y = (max_y - min_y) / rows @parameter fn compute_vector[simd_width: Int](col: Int): """Each time we operate on a `simd_width` vector of pixels.""" var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x - var cy = min_y + row * scale_y + var cy = min_y + row * SIMD[float_type, simd_width](scale_y) var c = ComplexSIMD[float_type, simd_width](cx, cy) matrix.store(row, col, mandelbrot_kernel_SIMD(c)) diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 7f291a96fb..4cb35b7359 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -285,14 +285,14 @@ "\n", " @parameter\n", " fn worker(row: Int):\n", - " var scale_x = (max_x - min_x) / width\n", - " var scale_y = (max_y - min_y) / height\n", + " alias scale_x = (max_x - min_x) / width\n", + " alias scale_y = (max_y - min_y) / height\n", "\n", " @parameter\n", " fn compute_vector[simd_width: Int](col: Int):\n", " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", - " var cy = min_y + row * scale_y\n", + " var cy = min_y + row * SIMD[float_type, simd_width](scale_y)\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", " matrix.store(row, col, mandelbrot_kernel_SIMD[simd_width](c))\n", "\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 52af34e1e4..8effcb4b42 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -523,9 +523,9 @@ "\n", " @parameter\n", " fn _process_row(row: Int):\n", - " var y = -((2.0 * row + 1) / height - 1)\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", " for col in range(width):\n", - " var x = ((2.0 * col + 1) / width - 1) * width / height\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", " var dir = Vec3f(x, y, -1).normalize()\n", " image.set(row, col, cast_ray(Vec3f.zero(), dir, sphere))\n", "\n", @@ -616,9 +616,9 @@ "\n", " @parameter\n", " fn _process_row(row: Int):\n", - " var y = -((2.0 * row + 1) / height - 1)\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", " for col in range(width):\n", - " var x = ((2.0 * col + 1) / width - 1) * width / height\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", " var dir = Vec3f(x, y, -1).normalize()\n", " image.set(row, col, cast_ray(Vec3f.zero(), dir, spheres).color)\n", "\n", @@ -745,9 +745,9 @@ "\n", " @parameter\n", " fn _process_row(row: Int):\n", - " var y = -((2.0 * row + 1) / height - 1)\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", " for col in range(width):\n", - " var x = ((2.0 * col + 1) / width - 1) * width / height\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", " var dir = Vec3f(x, y, -1).normalize()\n", " image.set(\n", " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights).color\n", @@ -854,9 +854,9 @@ "\n", " @parameter\n", " fn _process_row(row: Int):\n", - " var y = -((2.0 * row + 1) / height - 1)\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", " for col in range(width):\n", - " var x = ((2.0 * col + 1) / width - 1) * width / height\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", " var dir = Vec3f(x, y, -1).normalize()\n", " image.set(\n", " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights).color\n", @@ -966,9 +966,9 @@ "\n", " @parameter\n", " fn _process_row(row: Int):\n", - " var y = -((2.0 * row + 1) / height - 1)\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", " for col in range(width):\n", - " var x = ((2.0 * col + 1) / width - 1) * width / height\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", " var dir = Vec3f(x, y, -1).normalize()\n", " image.set(\n", " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights, bg).color\n", diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index e6e6fe6165..dbd6b74694 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -153,6 +153,15 @@ struct Bool( """ self = value.__bool__() + @always_inline("nodebug") + fn __init__(inout self, value: SIMD[DType.bool, 1]): + """Convert a scalar SIMD value to a Bool. + + Args: + value: The scalar value. + """ + self = value.__bool__() + @always_inline("nodebug") fn __bool__(self) -> Bool: """Convert to Bool. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index e3cd454322..79fcaf7978 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -141,21 +141,21 @@ fn _has_native_bf16_support() -> Bool: @register_passable("trivial") struct SIMD[type: DType, size: Int]( Absable, + Boolable, Ceilable, CeilDivable, CollectionElement, CollectionElementNew, Floorable, + Formattable, Hashable, Intable, - ImplicitlyBoolable, Powable, + Representable, Roundable, Sized, Stringable, - Formattable, Truncable, - Representable, ): """Represents a small vector that is backed by a hardware vector element. @@ -206,29 +206,6 @@ struct SIMD[type: DType, size: Int]( _simd_construction_checks[type, size]() self = _unchecked_zero[type, size]() - @always_inline("nodebug") - fn __init__(inout self, value: SIMD[DType.float64, 1]): - """Initializes the SIMD vector with a float. - - The value is splatted across all the elements of the SIMD - vector. - - Args: - value: The input value. - """ - _simd_construction_checks[type, size]() - constrained[ - type.is_floating_point(), "the SIMD type must be floating point" - ]() - - var casted = __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] - ](value.value) - var vec = __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] - ](casted) - self.value = vec - @always_inline("nodebug") fn __init__(inout self, *, other: SIMD[type, size]): """Explicitly copy the provided value. @@ -333,19 +310,31 @@ struct SIMD[type: DType, size: Int]( _simd_construction_checks[type, size]() self.value = value - # Construct via a variadic type which has the same number of elements as - # the SIMD value. + @always_inline("nodebug") + fn __init__(inout self, value: Scalar[type], /): + """Constructs a SIMD vector by splatting a scalar value. + + The input value is splatted across all elements of the SIMD vector. + + Args: + value: The value to splat to the elements of the vector. + """ + _simd_construction_checks[type, size]() + + # Construct by broadcasting a scalar. + self.value = __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] + ](value.value) + @always_inline("nodebug") fn __init__(inout self, *elems: Scalar[type]): """Constructs a SIMD vector via a variadic list of elements. - If there is just one input value, then it is splatted to all elements - of the SIMD vector. Otherwise, the input values are assigned to the - corresponding elements of the SIMD vector. + The input values are assigned to the corresponding elements of the SIMD + vector. Constraints: - The number of input values is 1 or equal to size of the SIMD - vector. + The number of input values is equal to size of the SIMD vector. Args: elems: The variadic list of elements from which the SIMD vector is @@ -353,22 +342,9 @@ struct SIMD[type: DType, size: Int]( """ _simd_construction_checks[type, size]() - var num_elements = len(elems) - if num_elements == 1: - # Construct by broadcasting a scalar. - self.value = __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - type.value, - `>`, - ] - ](elems[0].value) - return # TODO: Make this a compile-time check when possible. debug_assert( - size == num_elements, + size == len(elems), ( "mismatch in the number of elements in the SIMD variadic" " constructor" @@ -1310,18 +1286,6 @@ struct SIMD[type: DType, size: Int]( ]() return rebind[Scalar[DType.bool]](self.cast[DType.bool]()).value - @always_inline("nodebug") - fn __as_bool__(self) -> Bool: - """Converts the SIMD scalar into a boolean value. - - Constraints: - The size of the SIMD vector must be 1. - - Returns: - True if the SIMD scalar is non-zero and False otherwise. - """ - return self.__bool__() - @always_inline("nodebug") fn __int__(self) -> Int: """Casts to the value to an Int. If there is a fractional component, diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index b1f05dc7bd..36d5df2ae2 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -211,8 +211,8 @@ def test_issue_30237(): return result - var x = 6.0 - var x2 = x * x + alias x = 6.0 + alias x2 = x * x var result1 = eval1(x2) var result2 = eval2(x2) @@ -227,13 +227,6 @@ def test_bool(): assert_true(Scalar[DType.float32](5.0).__bool__()) assert_false(Scalar[DType.float32](0.0).__bool__()) - assert_true(Scalar[DType.bool](True).__as_bool__()) - assert_false(Scalar[DType.bool](False).__as_bool__()) - assert_true(Scalar[DType.int32](5).__as_bool__()) - assert_false(Scalar[DType.int32](0).__as_bool__()) - assert_true(Scalar[DType.float32](5.0).__as_bool__()) - assert_false(Scalar[DType.float32](0.0).__as_bool__()) - def test_truthy(): alias dtypes = ( From d8368c4ce64e72bdeb59de8cd6146e92c4f932d4 Mon Sep 17 00:00:00 2001 From: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Date: Fri, 19 Jul 2024 11:02:56 -0700 Subject: [PATCH 1268/2019] [External] [stdlib] Implement remaining int fns for `bit` module (#43620) [External] [stdlib] Implement remaining int fns for `bit` module This PR implements the rest of the `bit` module with Int type from https://github.com/modularml/mojo/issues/2862. Co-authored-by: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> Closes modularml/mojo#3150 MODULAR_ORIG_COMMIT_REV_ID: 1cfcffd97e22d821efbdbb102efba139cf2cfedf --- docs/changelog.md | 4 +++ stdlib/src/bit/bit.mojo | 64 +++++++++++++++++++++++++++-------- stdlib/test/bit/test_bit.mojo | 34 +++++++++++++++++++ 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4c33358d45..c9c9cbfd4b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -327,6 +327,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. ([PR #3251](https://github.com/modularml/mojo/pull/3251) by [@jjvraw](https://github.com/jjvraw)) +- `bit` module now supports `bit_reverse()`, `byte_swap()` and `pop_count()` for + `Int` type. + ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) + ### 🦋 Changed - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index cc0c15f281..d79f1a8e11 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -113,7 +113,19 @@ fn count_trailing_zeros[ # ===----------------------------------------------------------------------===# # bit_reverse # ===----------------------------------------------------------------------===# -# TODO: implement bit_reverse for Int type + + +@always_inline("nodebug") +fn bit_reverse(val: Int) -> Int: + """Reverses the bitpattern of an integer value. + + Args: + val: The input value. + + Returns: + The input value with its bitpattern reversed. + """ + return llvm_intrinsic["llvm.bitreverse", Int, has_side_effect=False](val) @always_inline("nodebug") @@ -145,7 +157,24 @@ fn bit_reverse[ # ===----------------------------------------------------------------------===# # byte_swap # ===----------------------------------------------------------------------===# -# TODO: implement byte_swap for Int type + + +@always_inline("nodebug") +fn byte_swap(val: Int) -> Int: + """Byte-swaps an integer value with an even number of bytes. + + Byte swap an integer value (8 bytes) with an even number of bytes (positive multiple + of 16 bits). This returns an integer value (8 bytes) that has its bytes swapped. For + example, if the input bytes are numbered 0, 1, 2, 3, 4, 5, 6, 7 then the returned + integer will have its bytes in 7, 6, 5, 4, 3, 2, 1, 0 order. + + Args: + val: The input value. + + Returns: + The input value with its bytes swapped. + """ + return llvm_intrinsic["llvm.bswap", Int, has_side_effect=False](val) @always_inline("nodebug") @@ -155,16 +184,12 @@ fn byte_swap[ """Byte-swaps a SIMD vector of integer values with an even number of bytes. Byte swap an integer value or vector of integer values with an even number - of bytes (positive multiple of 16 bits). This is equivalent to `llvm.bswap` - intrinsic that has the following semantics: - - The `llvm.bswap.i16` intrinsic returns an i16 value that has the high and - low byte of the input i16 swapped. Similarly, the `llvm.bswap.i32` intrinsic - returns an i32 value that has the four bytes of the input i32 swapped, so - that if the input bytes are numbered 0, 1, 2, 3 then the returned i32 will - have its bytes in 3, 2, 1, 0 order. The `llvm.bswap.i48`, `llvm.bswap.i64` - and other intrinsics extend this concept to additional even-byte lengths (6 - bytes, 8 bytes and more, respectively). + of bytes (positive multiple of 16 bits). For example, The Int16 returns an + Int16 value that has the high and low byte of the input Int16 swapped. + Similarly, Int32 returns an Int32 value that has the four bytes of the input Int32 swapped, + so that if the input bytes are numbered 0, 1, 2, 3 then the returned Int32 will + have its bytes in 3, 2, 1, 0 order. Int64 and other integer type extend this + concept to additional even-byte lengths (6 bytes, 8 bytes and more, respectively). Parameters: type: `dtype` used for the computation. @@ -190,7 +215,19 @@ fn byte_swap[ # ===----------------------------------------------------------------------===# # pop_count # ===----------------------------------------------------------------------===# -# TODO: implement pop_count for Int type + + +@always_inline("nodebug") +fn pop_count(val: Int) -> Int: + """Counts the number of bits set in an integer value. + + Args: + val: The input value. + + Returns: + The number of bits set in the input value. + """ + return llvm_intrinsic["llvm.ctpop", Int, has_side_effect=False](val) @always_inline("nodebug") @@ -222,7 +259,6 @@ fn pop_count[ # ===----------------------------------------------------------------------===# # bit_not # ===----------------------------------------------------------------------===# -# TODO: implement bit_not for Int type @always_inline("nodebug") diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 4863fcb534..c58fc00a26 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -120,6 +120,16 @@ def test_count_trailing_zeros_simd(): ) +def test_bit_reverse(): + assert_equal(bit_reverse(-(2**32)), 4294967295) + assert_equal(bit_reverse(-1), -1) + assert_equal(bit_reverse(0), 0) + assert_equal(bit_reverse(1), -(2**63)) + assert_equal(bit_reverse(2), 2**62) + assert_equal(bit_reverse(8), 2**60) + assert_equal(bit_reverse(2**63), 1) + + def test_bit_reverse_simd(): alias simd_width = 4 alias int8_t = DType.int8 @@ -150,6 +160,16 @@ def test_bit_reverse_simd(): ) +def test_byte_swap(): + assert_equal(byte_swap(0x0000), 0x0000000000000000) + assert_equal(byte_swap(0x0102), 0x0201000000000000) + assert_equal(byte_swap(0x0201), 0x0102000000000000) + assert_equal(byte_swap(-0x0123456789ABCDEF), 0x1132547698BADCFE) + assert_equal(byte_swap(0x0000000001234567), 0x6745230100000000) + assert_equal(byte_swap(0x56789ABCDEF01234), 0x3412F0DEBC9A7856) + assert_equal(byte_swap(0x23456789ABCDEF01), 0x01EFCDAB89674523) + + def test_byte_swap_simd(): alias simd_width = 4 alias int16_t = DType.int16 @@ -189,6 +209,17 @@ def test_byte_swap_simd(): ) +def test_pop_count(): + assert_equal(pop_count(-111444444), 51) + assert_equal(pop_count(0), 0) + assert_equal(pop_count(1), 1) + assert_equal(pop_count(2), 1) + assert_equal(pop_count(3), 2) + assert_equal(pop_count(4), 1) + assert_equal(pop_count(5), 2) + assert_equal(pop_count(3000000), 10) + + def test_pop_count_simd(): alias simd_width = 4 alias int8_t = DType.int8 @@ -473,7 +504,10 @@ def main(): test_count_leading_zeros_simd() test_count_trailing_zeros() test_count_trailing_zeros_simd() + test_bit_reverse() test_bit_reverse_simd() + test_byte_swap() test_byte_swap_simd() + test_pop_count() test_pop_count_simd() test_bit_not_simd() From 68b7a09f75832e76ce4cdf71ad2981e46805884e Mon Sep 17 00:00:00 2001 From: William G Hatch Date: Fri, 19 Jul 2024 14:01:30 -0600 Subject: [PATCH 1269/2019] [mojo-lang][stdlib] improve IntLiteral conversion, support unsigned index This is in support of conversion for the stdlib UInt type. There were really two issues: one is that the converter always assumed that index is signed (like with Int), and the other is that the converter failed for converting unsigned integers with the high bit set. This fixes them. Closes https://github.com/modularml/mojo/issues/2933 MODULAR_ORIG_COMMIT_REV_ID: fcef1ef4b58ab55c117f8ea9acaef4219aced9db --- stdlib/src/builtin/int_literal.mojo | 13 ++++- stdlib/src/builtin/range.mojo | 2 +- stdlib/src/builtin/uint.mojo | 9 +-- .../test_range_uint_reverse_range_bad.mojo | 2 +- stdlib/test/builtin/test_uint.mojo | 55 +++++++++---------- stdlib/test/builtin/test_uint_error.mojo | 25 +++++++++ 6 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 stdlib/test/builtin/test_uint_error.mojo diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index e45da7e465..4ea94f5c49 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -605,10 +605,21 @@ struct IntLiteral( """Convert from IntLiteral to Int. Returns: - The value as an integer. + The value as an integer of platform-specific width. """ return Int(self.__as_mlir_index()) + @always_inline("nodebug") + fn __uint__(self) -> UInt: + """Convert from IntLiteral to UInt. + + Returns: + The value as an unsigned integer of platform-specific width. + """ + return __mlir_op.`kgen.int_literal.convert`[ + _type = __mlir_type.index, treatIndexAsUnsigned = __mlir_attr.unit + ](self.value) + @always_inline("nodebug") fn __abs__(self) -> Self: """Return the absolute value of the IntLiteral value. diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 0fe4e722ca..c45886e096 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -379,7 +379,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): step != 0, "range() arg 3 (the step size) must not be zero" ) debug_assert( - step != UInt(-1), + step != UInt(Int(-1)), ( "range() arg 3 (the step size) cannot be -1. Reverse range is" " not supported yet for UInt ranges." diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index ced6ccca04..2b9c64cb4c 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -80,14 +80,7 @@ struct UInt(Comparable, Formattable, Representable, Stringable): Args: value: The init value. """ - # TODO: Find a way to convert directly without using UInt64. - # This is because the existing - # __mlir_op.`kgen.int_literal.convert` - # in IntLiteral.__as_mlir_index() - # assumes that the index represents an signed integer. - # We need a variant for unsigned integers. - # Change when https://github.com/modularml/mojo/issues/2933 is fixed - self.value = int(UInt64(value)).value + self = value.__uint__() @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: diff --git a/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo b/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo index 944c5d3a02..259f00d13e 100644 --- a/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo +++ b/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo @@ -19,7 +19,7 @@ from testing import assert_equal def test_range_uint_bad_step_size(): # Ensure constructing a range with a "-1" step size (i.e. reverse range) # with UInt is rejected and aborts now via `debug_assert` handler. - var r = range(UInt(0), UInt(10), UInt(-1)) + var r = range(UInt(0), UInt(10), UInt(Int(-1))) def main(): diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 1aad4c141c..2bfb4ab294 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -21,11 +21,6 @@ def test_simple_uint(): assert_equal(str(UInt(0)), "0") assert_equal(str(UInt()), "0") - # (2 ** 64) - 1 - # TODO: raise an error in the future when - # https://github.com/modularml/mojo/issues/2933 is fixed - assert_equal(str(UInt(-1)), "18446744073709551615") - assert_equal(str(UInt(18446744073709551615)), "18446744073709551615") @@ -100,18 +95,18 @@ def test_properties(): def test_add(): assert_equal(UInt.__add__(UInt(3), UInt(3)), UInt(6)) - assert_equal(UInt.__add__(UInt(-2), UInt(3)), UInt(1)) - assert_equal(UInt.__add__(UInt(2), UInt(-3)), UInt(-1)) - assert_equal(UInt.__add__(UInt(5), UInt(-5)), UInt(0)) - assert_equal(UInt.__add__(UInt(-5), UInt(-4)), UInt(-9)) + assert_equal(UInt.__add__(UInt(Int(-2)), UInt(3)), UInt(1)) + assert_equal(UInt.__add__(UInt(2), UInt(Int(-3))), UInt(Int(-1))) + assert_equal(UInt.__add__(UInt(5), UInt(Int(-5))), UInt(0)) + assert_equal(UInt.__add__(UInt(Int(-5)), UInt(Int(-4))), UInt(Int(-9))) def test_sub(): assert_equal(UInt.__sub__(UInt(3), UInt(3)), UInt(0)) - assert_equal(UInt.__sub__(UInt(-2), UInt(3)), UInt(-5)) - assert_equal(UInt.__sub__(UInt(2), UInt(-3)), UInt(5)) + assert_equal(UInt.__sub__(UInt(Int(-2)), UInt(3)), UInt(Int(-5))) + assert_equal(UInt.__sub__(UInt(2), UInt(Int(-3))), UInt(5)) assert_equal(UInt.__sub__(UInt(5), UInt(4)), UInt(1)) - assert_equal(UInt.__sub__(UInt(4), UInt(5)), UInt(-1)) + assert_equal(UInt.__sub__(UInt(4), UInt(5)), UInt(Int(-1))) def test_div(): @@ -131,23 +126,23 @@ def test_pow(): def test_ceil(): assert_equal(UInt.__ceil__(UInt(5)), UInt(5)) assert_equal(UInt.__ceil__(UInt(0)), UInt(0)) - assert_equal(UInt.__ceil__(UInt(-5)), UInt(-5)) + assert_equal(UInt.__ceil__(UInt(Int(-5))), UInt(Int(-5))) def test_floor(): assert_equal(UInt.__floor__(UInt(5)), UInt(5)) assert_equal(UInt.__floor__(UInt(0)), UInt(0)) - assert_equal(UInt.__floor__(UInt(-5)), UInt(-5)) + assert_equal(UInt.__floor__(UInt(Int(-5))), UInt(Int(-5))) def test_round(): assert_equal(UInt.__round__(UInt(5)), UInt(5)) assert_equal(UInt.__round__(UInt(0)), UInt(0)) - assert_equal(UInt.__round__(UInt(-5)), UInt(-5)) + assert_equal(UInt.__round__(UInt(Int(-5))), UInt(Int(-5))) assert_equal(UInt.__round__(UInt(5), UInt(1)), UInt(5)) assert_equal(UInt.__round__(UInt(0), UInt(1)), UInt(0)) - assert_equal(UInt.__round__(UInt(-5), UInt(1)), UInt(-5)) - assert_equal(UInt.__round__(UInt(100), UInt(-2)), UInt(100)) + assert_equal(UInt.__round__(UInt(Int(-5)), UInt(1)), UInt(Int(-5))) + assert_equal(UInt.__round__(UInt(100), UInt(Int(-2))), UInt(100)) def test_trunc(): @@ -159,20 +154,20 @@ def test_floordiv(): assert_equal(UInt(1), UInt.__floordiv__(UInt(2), UInt(2))) assert_equal(UInt(0), UInt.__floordiv__(UInt(2), UInt(3))) assert_equal(UInt(2), UInt.__floordiv__(UInt(100), UInt(50))) - assert_equal(UInt(0), UInt.__floordiv__(UInt(2), UInt(-2))) - assert_equal(UInt(0), UInt.__floordiv__(UInt(99), UInt(-2))) + assert_equal(UInt(0), UInt.__floordiv__(UInt(2), UInt(Int(-2)))) + assert_equal(UInt(0), UInt.__floordiv__(UInt(99), UInt(Int(-2)))) def test_mod(): assert_equal(UInt(0), UInt.__mod__(UInt(99), UInt(1))) assert_equal(UInt(0), UInt.__mod__(UInt(99), UInt(3))) - assert_equal(UInt(99), UInt.__mod__(UInt(99), UInt(-2))) + assert_equal(UInt(99), UInt.__mod__(UInt(99), UInt(Int(-2)))) assert_equal(UInt(3), UInt.__mod__(UInt(99), UInt(8))) - assert_equal(UInt(99), UInt.__mod__(UInt(99), UInt(-8))) - assert_equal(UInt(2), UInt.__mod__(UInt(2), UInt(-1))) - assert_equal(UInt(2), UInt.__mod__(UInt(2), UInt(-2))) - assert_equal(UInt(3), UInt.__mod__(UInt(3), UInt(-2))) - assert_equal(UInt(1), UInt.__mod__(UInt(-3), UInt(2))) + assert_equal(UInt(99), UInt.__mod__(UInt(99), UInt(Int(-8)))) + assert_equal(UInt(2), UInt.__mod__(UInt(2), UInt(Int(-1)))) + assert_equal(UInt(2), UInt.__mod__(UInt(2), UInt(Int(-2)))) + assert_equal(UInt(3), UInt.__mod__(UInt(3), UInt(Int(-2)))) + assert_equal(UInt(1), UInt.__mod__(UInt(Int(-3)), UInt(2))) def test_divmod(): @@ -192,25 +187,25 @@ def test_divmod(): def test_abs(): - assert_equal(UInt(-5).__abs__(), UInt(18446744073709551611)) + assert_equal(UInt(Int(-5)).__abs__(), UInt(18446744073709551611)) assert_equal(UInt(2).__abs__(), UInt(2)) assert_equal(UInt(0).__abs__(), UInt(0)) def test_string_conversion(): assert_equal(UInt(3).__str__(), "3") - assert_equal(UInt(-3).__str__(), "18446744073709551613") + assert_equal(UInt(Int(-3)).__str__(), "18446744073709551613") assert_equal(UInt(0).__str__(), "0") assert_equal(UInt(100).__str__(), "100") - assert_equal(UInt(-100).__str__(), "18446744073709551516") + assert_equal(UInt(Int(-100)).__str__(), "18446744073709551516") def test_int_representation(): assert_equal(UInt(3).__repr__(), "UInt(3)") - assert_equal(UInt(-3).__repr__(), "UInt(18446744073709551613)") + assert_equal(UInt(Int(-3)).__repr__(), "UInt(18446744073709551613)") assert_equal(UInt(0).__repr__(), "UInt(0)") assert_equal(UInt(100).__repr__(), "UInt(100)") - assert_equal(UInt(-100).__repr__(), "UInt(18446744073709551516)") + assert_equal(UInt(Int(-100)).__repr__(), "UInt(18446744073709551516)") def test_indexer(): diff --git a/stdlib/test/builtin/test_uint_error.mojo b/stdlib/test/builtin/test_uint_error.mojo new file mode 100644 index 0000000000..e36e15859f --- /dev/null +++ b/stdlib/test/builtin/test_uint_error.mojo @@ -0,0 +1,25 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# +# This file only tests that conversion of negative IntLiteral to UInt fails. +# +# ===----------------------------------------------------------------------=== # +# REQUIRES: has_not +# RUN: not mojo %s 2>&1 | FileCheck %s + + +fn main(): + # CHECK: integer value -1 is negative, but is being converted to an unsigned type + print(UInt(-1)) + # CHECK-NOT: is never reached + print("is never reached") From cfd2948ab867c98df0e0a03d0e7f6839471bb764 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 19 Jul 2024 13:34:03 -0700 Subject: [PATCH 1270/2019] [stdlib] Remove is_relwithdebinfo_build This is unused. It's also unlikely that code needs to differentiate beyond debug or release and specifically know it's RelWithDebInfo MODULAR_ORIG_COMMIT_REV_ID: 0cdd452eec71f2c0d2c3dd45599d18b8da1874ef --- stdlib/src/sys/_build.mojo | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/stdlib/src/sys/_build.mojo b/stdlib/src/sys/_build.mojo index d3f4632b1f..808b4776b6 100644 --- a/stdlib/src/sys/_build.mojo +++ b/stdlib/src/sys/_build.mojo @@ -87,21 +87,3 @@ fn is_release_build() -> Bool: ) else: return True - - -@always_inline("nodebug") -fn is_relwithdebinfo_build() -> Bool: - """ - Returns True if the build is in relwithdebinfo mode. - - Returns: - Bool: True if the build is in relwithdebinfo mode and False otherwise. - """ - - @parameter - if is_defined["DEBUG"](): - return True - elif is_defined["BUILD_TYPE"](): - return _build_type() == "relwithdebinfo" - else: - return False From 621a9ca865828ccb76dc99c107e3de7cbee99bd0 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Fri, 19 Jul 2024 15:08:19 -0700 Subject: [PATCH 1271/2019] [Docs] Fix typo in parameters doc. MODULAR_ORIG_COMMIT_REV_ID: babdaf4f452bbb6ebcd1ffa46aeb0084447bcfc1 --- docs/manual/parameters/index.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index d19e206ba5..c3a36110de 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -874,7 +874,7 @@ "\n", "Infer-only parameters are a special class of parameters that are **always** \n", "inferred from context. Infer-only parameters are placed at the **beginning** of\n", - "the parameter list, set off from other parameters by the `//` sigil`:\n", + "the parameter list, set off from other parameters by the `//` sigil:\n", "\n", "```mojo\n", "fn example[type: CollectionElement, //, list: List[type]]()\n", From ce5a730d801f3f5933523158649fe63e261cb644 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Sat, 20 Jul 2024 09:32:15 -0500 Subject: [PATCH 1272/2019] [stdlib] Implement `pop.select` wrapper As discovered internally, there's some IR size wins to be had when using `pop.select` over `if` in some performance-sensitive code. So, add this wrapper function: `select` which delegates to `pop.select`. Apply the named function that abstracts the `pop.select` in `bool.mojo` which previously called the `pop.select` op directly. MODULAR_ORIG_COMMIT_REV_ID: 30134c6c036d291c2b31a4fe9a526605e43fc790 --- stdlib/src/builtin/bool.mojo | 3 ++- stdlib/src/utils/__init__.mojo | 1 + stdlib/src/utils/select_if.mojo | 31 ++++++++++++++++++++++++++++++ stdlib/test/utils/test_select.mojo | 25 ++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 stdlib/src/utils/select_if.mojo create mode 100644 stdlib/test/utils/test_select.mojo diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index dbd6b74694..ec9d96fbe1 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -18,6 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import Set from utils._visualizers import lldb_formatter_wrapping_type +from utils import select # ===----------------------------------------------------------------------=== # # Boolable @@ -239,7 +240,7 @@ struct Bool( Returns: 1 if the Bool is True, 0 otherwise. """ - return __mlir_op.`pop.select`[_type=Int](self.value, Int(1), Int(0)) + return select(self.value, Int(1), Int(0)) @always_inline("nodebug") fn __index__(self) -> Int: diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index dd858c531c..0324841858 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -15,6 +15,7 @@ from .index import Index, StaticIntTuple, product from .inline_string import InlineString from .loop import unroll +from .select_if import select from .span import Span from .static_tuple import InlineArray, StaticTuple from .stringref import StringRef diff --git a/stdlib/src/utils/select_if.mojo b/stdlib/src/utils/select_if.mojo new file mode 100644 index 0000000000..72b47b978a --- /dev/null +++ b/stdlib/src/utils/select_if.mojo @@ -0,0 +1,31 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn select[T: AnyTrivialRegType](condition: Bool, lhs: T, rhs: T) -> T: + """Choose one value based on a condition, without IR-level branching. + Use this over normal `if` branches to reduce the size of the generated IR. + + Parameters: + T: The type of the lhs and rhs. + + Args: + condition: The condition to test. + lhs: The value to select if the condition is met. + rhs: The value to select if the condition is not met. + + Returns: + The value selected based on the condition. + """ + return __mlir_op.`pop.select`(condition.value, lhs, rhs) diff --git a/stdlib/test/utils/test_select.mojo b/stdlib/test/utils/test_select.mojo new file mode 100644 index 0000000000..c64d3d85c5 --- /dev/null +++ b/stdlib/test/utils/test_select.mojo @@ -0,0 +1,25 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from utils import select +from testing import assert_equal + + +def test_select(): + assert_equal(select(True, 42, 100), 42) + assert_equal(select(False, 42, 100), 100) + + +def main(): + test_select() From 0807bd89c7608bff069853f72f7f4acfd6528f0c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 20 Jul 2024 14:37:38 -0700 Subject: [PATCH 1273/2019] [mojo-lang] Fix overload resolution of 'self' in conditional conformance. (#43795) This fixes overload resolution to reject selection of constructors whose Self type mismatches the expectation on the call side. Not doing this was causing infinite loops in the type checker because we'd select the wrong constructor to create something, then try to implicitly convert it back in the result resolution. This fixes MOCO-990 MODULAR_ORIG_COMMIT_REV_ID: e279abfb016b24f7aa53eda55437d730f309dda6 --- stdlib/test/builtin/test_int_literal.mojo | 58 +++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index a8cf3d5ccf..72ffa712fa 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -116,35 +116,35 @@ def test_bool(): def test_comparison(): - assert_true(IntLiteral(5).__lt__(10)) - assert_true(IntLiteral(-10).__lt__(-5)) - assert_false(IntLiteral(0).__lt__(0)) - assert_false(IntLiteral(10).__lt__(5)) - - assert_true(IntLiteral(5).__le__(10)) - assert_true(IntLiteral(-10).__le__(-5)) - assert_true(IntLiteral(0).__le__(0)) - assert_false(IntLiteral(10).__le__(5)) - - assert_true(IntLiteral(5).__eq__(5)) - assert_true(IntLiteral(0).__eq__(0)) - assert_false(IntLiteral(0).__eq__(1)) - assert_false(IntLiteral(5).__eq__(10)) - - assert_true(IntLiteral(5).__ne__(10)) - assert_true(IntLiteral(0).__ne__(1)) - assert_false(IntLiteral(5).__ne__(5)) - assert_false(IntLiteral(0).__ne__(0)) - - assert_true(IntLiteral(10).__gt__(5)) - assert_true(IntLiteral(-5).__gt__(-10)) - assert_false(IntLiteral(0).__gt__(0)) - assert_false(IntLiteral(5).__gt__(10)) - - assert_true(IntLiteral(10).__ge__(5)) - assert_true(IntLiteral(5).__ge__(5)) - assert_true(IntLiteral(-5).__ge__(-10)) - assert_false(IntLiteral(5).__ge__(10)) + assert_true((5).__lt__(10)) + assert_true((-10).__lt__(-5)) + assert_false((0).__lt__(0)) + assert_false((10).__lt__(5)) + + assert_true((5).__le__(10)) + assert_true((-10).__le__(-5)) + assert_true((0).__le__(0)) + assert_false((10).__le__(5)) + + assert_true((5).__eq__(5)) + assert_true((0).__eq__(0)) + assert_false((0).__eq__(1)) + assert_false((5).__eq__(10)) + + assert_true((5).__ne__(10)) + assert_true((0).__ne__(1)) + assert_false((5).__ne__(5)) + assert_false((0).__ne__(0)) + + assert_true((10).__gt__(5)) + assert_true((-5).__gt__(-10)) + assert_false((0).__gt__(0)) + assert_false((5).__gt__(10)) + + assert_true((10).__ge__(5)) + assert_true((5).__ge__(5)) + assert_true((-5).__ge__(-10)) + assert_false((5).__ge__(10)) def main(): From c6a118133de2e9b80e2cc523e180d364b41099d0 Mon Sep 17 00:00:00 2001 From: Victor Guerra Date: Mon, 22 Jul 2024 09:02:07 -0700 Subject: [PATCH 1274/2019] [External] [stdlib] UTests for `SIMD`'s operators required by `Comparable` (#43826) [External] [stdlib] UTests for `SIMD`'s operators required by `Comparable` Testing the following dunder methods in `SIMD`: `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__` and `__ge__`. Note that we test directly dunder methods to avoid any unintended implicit conversions possibly introduced trough operator syntax sugar. Partially resolves #3145 Co-authored-by: Victor Guerra Closes modularml/mojo#3221 MODULAR_ORIG_COMMIT_REV_ID: 55ece3450c8b074ae3279f79cec8aa642382b036 --- stdlib/test/builtin/test_simd.mojo | 197 +++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 36d5df2ae2..247ef4a5f4 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1334,6 +1334,202 @@ def test_contains(): assert_false(0 in y or 5 in y) +def test_comparison(): + alias dtypes = ( + DType.bool, + DType.int8, + DType.int16, + DType.int32, + DType.int64, + DType.uint8, + DType.uint16, + DType.uint32, + DType.uint64, + DType.float16, + DType.float32, + DType.float64, + DType.index, + ) + + @parameter + fn test_dtype[type: DType]() raises: + alias X4 = SIMD[type, 4] + + @parameter + if type.is_signed(): + var simd_val = X4(-10, -8, -6, -4) + + assert_true(simd_val.__lt__(X4(-1)).reduce_and()) + assert_false(simd_val.__lt__(X4(-12)).reduce_or()) + var mixed_lt = simd_val.__lt__(X4(-6)) + assert_true(mixed_lt[0]) + assert_true(mixed_lt[1]) + assert_false(mixed_lt[2]) + assert_false(mixed_lt[3]) + + assert_true(simd_val.__le__(X4(-4)).reduce_and()) + assert_false(simd_val.__le__(X4(-11)).reduce_or()) + var mixed_le = simd_val.__le__(X4(-8)) + assert_true(mixed_le[0]) + assert_true(mixed_le[1]) + assert_false(mixed_le[2]) + assert_false(mixed_le[3]) + + assert_true(simd_val.__eq__(X4(-10, -8, -6, -4)).reduce_and()) + assert_false(simd_val.__eq__(X4(0)).reduce_or()) + var mixed_eq = simd_val.__eq__(X4(-10)) + assert_true(mixed_eq[0]) + assert_false(mixed_eq[1]) + assert_false(mixed_eq[2]) + assert_false(mixed_eq[3]) + + assert_true(simd_val.__ne__(X4(0)).reduce_and()) + assert_false(simd_val.__ne__(X4(-10, -8, -6, -4)).reduce_or()) + var mixed_ne = simd_val.__ne__(X4(-8)) + assert_true(mixed_ne[0]) + assert_false(mixed_ne[1]) + assert_true(mixed_ne[2]) + assert_true(mixed_ne[3]) + + assert_true(simd_val.__gt__(X4(-11)).reduce_and()) + assert_false(simd_val.__gt__(X4(-1)).reduce_or()) + var mixed_gt = simd_val.__gt__(X4(-6)) + assert_false(mixed_gt[0]) + assert_false(mixed_gt[1]) + assert_false(mixed_gt[2]) + assert_true(mixed_gt[3]) + + assert_true(simd_val.__ge__(X4(-10)).reduce_and()) + assert_false(simd_val.__ge__(X4(-1)).reduce_or()) + var mixed_ge = simd_val.__ge__(X4(-6)) + assert_false(mixed_ge[0]) + assert_false(mixed_ge[1]) + assert_true(mixed_ge[2]) + assert_true(mixed_ge[3]) + + @parameter + if type.is_numeric(): + var simd_val = X4(1, 2, 3, 4) + + assert_true(simd_val.__lt__(X4(5)).reduce_and()) + assert_false(simd_val.__lt__(X4(0)).reduce_or()) + var mixed_lt = simd_val.__lt__(X4(3)) + assert_true(mixed_lt[0]) + assert_true(mixed_lt[1]) + assert_false(mixed_lt[2]) + assert_false(mixed_lt[3]) + + assert_true(simd_val.__le__(X4(4)).reduce_and()) + assert_false(simd_val.__le__(X4(0)).reduce_or()) + var mixed_le = simd_val.__le__(X4(3)) + assert_true(mixed_le[0]) + assert_true(mixed_le[1]) + assert_true(mixed_le[2]) + assert_false(mixed_le[3]) + + assert_true(simd_val.__eq__(X4(1, 2, 3, 4)).reduce_and()) + assert_false(simd_val.__eq__(X4(5)).reduce_or()) + var mixed_eq = simd_val.__eq__(X4(1)) + assert_true(mixed_eq[0]) + assert_false(mixed_eq[1]) + assert_false(mixed_eq[2]) + assert_false(mixed_eq[3]) + + assert_true(simd_val.__ne__(X4(5)).reduce_and()) + assert_false(simd_val.__ne__(X4(1, 2, 3, 4)).reduce_or()) + var mixed_ne = simd_val.__ne__(X4(4)) + assert_true(mixed_ne[0]) + assert_true(mixed_ne[1]) + assert_true(mixed_ne[2]) + assert_false(mixed_ne[3]) + + assert_true(simd_val.__gt__(X4(0)).reduce_and()) + assert_false(simd_val.__gt__(X4(4)).reduce_or()) + var mixed_gt = simd_val.__gt__(X4(2)) + assert_false(mixed_gt[0]) + assert_false(mixed_gt[1]) + assert_true(mixed_gt[2]) + assert_true(mixed_gt[3]) + + assert_true(simd_val.__ge__(X4(1)).reduce_and()) + assert_false(simd_val.__ge__(X4(5)).reduce_or()) + var mixed_ge = simd_val.__ge__(X4(2)) + assert_false(mixed_ge[0]) + assert_true(mixed_ge[1]) + assert_true(mixed_ge[2]) + assert_true(mixed_ge[3]) + + @parameter + if type is DType.bool: + var all_true = X4(True) + var all_false = X4(False) + var mixed = X4(True, True, False, False) + + assert_true(all_false.__lt__(all_true).reduce_and()) + assert_false(all_true.__lt__(all_false).reduce_or()) + var mixed_lt = all_false.__lt__(mixed) + assert_true(mixed_lt[0]) + assert_true(mixed_lt[1]) + assert_false(mixed_lt[2]) + assert_false(mixed_lt[3]) + + assert_true(all_false.__le__(all_true).reduce_and()) + assert_false(all_true.__le__(all_false).reduce_or()) + var mixed_le = all_true.__le__(mixed) + assert_true(mixed_le[0]) + assert_true(mixed_le[1]) + assert_false(mixed_le[2]) + assert_false(mixed_le[3]) + + assert_true( + all_true.__eq__(X4(True, True, True, True)).reduce_and() + ) + assert_false(all_true.__eq__(all_false).reduce_or()) + var mixed_eq = all_true.__eq__(mixed) + assert_true(mixed_eq[0]) + assert_true(mixed_eq[1]) + assert_false(mixed_le[2]) + assert_false(mixed_le[3]) + + assert_true(all_true.__ne__(all_false).reduce_and()) + assert_false( + all_true.__ne__(X4(True, True, True, True)).reduce_or() + ) + var mixed_ne = all_true.__ne__(mixed) + assert_false(mixed_ne[0]) + assert_false(mixed_ne[1]) + assert_true(mixed_ne[2]) + assert_true(mixed_ne[3]) + + assert_true(all_true.__gt__(all_false).reduce_and()) + assert_false(all_false.__gt__(all_true).reduce_or()) + var mixed_gt = all_true.__gt__(mixed) + assert_false(mixed_gt[0]) + assert_false(mixed_gt[1]) + assert_true(mixed_gt[2]) + assert_true(mixed_gt[3]) + + assert_true(all_true.__ge__(all_false).reduce_and()) + assert_false(all_false.__ge__(all_true).reduce_or()) + var mixed_ge = all_true.__ge__(mixed) + assert_true(mixed_ge[0]) + assert_true(mixed_ge[1]) + assert_true(mixed_ge[2]) + assert_true(mixed_ge[3]) + + @parameter + fn test_dtype_unrolled[i: Int]() raises: + alias type = dtypes.get[i, DType]() + test_dtype[type]() + + unroll[test_dtype_unrolled, dtypes.__len__()]() + + # TODO(KERN-228): support BF16 on neon systems. + @parameter + if not has_neon(): + test_dtype[DType.bfloat16]() + + def main(): test_abs() test_add() @@ -1380,4 +1576,5 @@ def main(): test_modf() test_split() test_contains() + test_comparison() # TODO: add tests for __and__, __or__, anc comparison operators From 71e762274d59904848e56f6a4a751af7b54e38f8 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 22 Jul 2024 09:16:49 -0700 Subject: [PATCH 1275/2019] Fix rendering on docusaurus3 by renaming .md to .mdx MODULAR_ORIG_COMMIT_REV_ID: b08cb7377481cb40525202e43dcf024232736837 --- docs/manual/{get-started.md => get-started.mdx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/manual/{get-started.md => get-started.mdx} (100%) diff --git a/docs/manual/get-started.md b/docs/manual/get-started.mdx similarity index 100% rename from docs/manual/get-started.md rename to docs/manual/get-started.mdx From c65ff38eadba8863ae84b26cbfb1faed4048f0ad Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 22 Jul 2024 12:29:24 -0500 Subject: [PATCH 1276/2019] [stdlib] Rename `utils.select` to clarify intent Rename `utils.select` to `utils._select_register_value`. Update the docs to clarify it should not be used pervasively in general code. MODULAR_ORIG_COMMIT_REV_ID: e795117160ed8ddda781ff2e6ebb6267818a95ba --- stdlib/src/builtin/bool.mojo | 4 ++-- stdlib/src/utils/__init__.mojo | 1 - stdlib/src/utils/{select_if.mojo => _select.mojo} | 8 +++++++- stdlib/test/utils/test_select.mojo | 10 +++++----- 4 files changed, 14 insertions(+), 9 deletions(-) rename stdlib/src/utils/{select_if.mojo => _select.mojo} (78%) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index ec9d96fbe1..7b634507fa 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import Set from utils._visualizers import lldb_formatter_wrapping_type -from utils import select +from utils._select import _select_register_value # ===----------------------------------------------------------------------=== # # Boolable @@ -240,7 +240,7 @@ struct Bool( Returns: 1 if the Bool is True, 0 otherwise. """ - return select(self.value, Int(1), Int(0)) + return _select_register_value(self.value, Int(1), Int(0)) @always_inline("nodebug") fn __index__(self) -> Int: diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 0324841858..dd858c531c 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -15,7 +15,6 @@ from .index import Index, StaticIntTuple, product from .inline_string import InlineString from .loop import unroll -from .select_if import select from .span import Span from .static_tuple import InlineArray, StaticTuple from .stringref import StringRef diff --git a/stdlib/src/utils/select_if.mojo b/stdlib/src/utils/_select.mojo similarity index 78% rename from stdlib/src/utils/select_if.mojo rename to stdlib/src/utils/_select.mojo index 72b47b978a..46746f0a5f 100644 --- a/stdlib/src/utils/select_if.mojo +++ b/stdlib/src/utils/_select.mojo @@ -13,10 +13,16 @@ @always_inline("nodebug") -fn select[T: AnyTrivialRegType](condition: Bool, lhs: T, rhs: T) -> T: +fn _select_register_value[ + T: AnyTrivialRegType +](condition: Bool, lhs: T, rhs: T) -> T: """Choose one value based on a condition, without IR-level branching. Use this over normal `if` branches to reduce the size of the generated IR. + This should not be used pervasively in general code. It should only be + used in very low level libraries where IR size/compile time matters because it + flattens if statements early in the compiler pipeline. + Parameters: T: The type of the lhs and rhs. diff --git a/stdlib/test/utils/test_select.mojo b/stdlib/test/utils/test_select.mojo index c64d3d85c5..cd82f84a2e 100644 --- a/stdlib/test/utils/test_select.mojo +++ b/stdlib/test/utils/test_select.mojo @@ -12,14 +12,14 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils import select +from utils._select import _select_register_value from testing import assert_equal -def test_select(): - assert_equal(select(True, 42, 100), 42) - assert_equal(select(False, 42, 100), 100) +def test_select_register_value(): + assert_equal(_select_register_value(True, 42, 100), 42) + assert_equal(_select_register_value(False, 42, 100), 100) def main(): - test_select() + test_select_register_value() From a3309acd0792e0cc940024c844b3c1e081e6048a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 22 Jul 2024 10:34:41 -0700 Subject: [PATCH 1277/2019] [Stdlib] Add the clamp function to the math module This allows one to constrain a value to be within the lower and upper bound range provided. MODULAR_ORIG_COMMIT_REV_ID: 124fc14446daa288ef1c061a4fe73b007a523e4e --- stdlib/src/math/__init__.mojo | 1 + stdlib/src/math/math.mojo | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 3683fc3ff4..5e6531316d 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -74,4 +74,5 @@ from .math import ( trunc, y0, y1, + clamp, ) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 8fe3d6fa56..88a3df0855 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -2145,6 +2145,64 @@ fn factorial(n: Int) -> Int: return table[n] +# ===----------------------------------------------------------------------=== # +# clamp +# ===----------------------------------------------------------------------=== # + + +fn clamp( + val: Int, lower_bound: __type_of(val), upper_bound: __type_of(val) +) -> __type_of(val): + """Clamps the integer value vector to be in a certain range. + + Args: + val: The value to clamp. + lower_bound: Minimum of the range to clamp to. + upper_bound: Maximum of the range to clamp to. + + Returns: + An integer clamped to be within lower_bound and upper_bound. + """ + return max(min(val, upper_bound), lower_bound) + + +fn clamp( + val: UInt, lower_bound: __type_of(val), upper_bound: __type_of(val) +) -> __type_of(val): + """Clamps the integer value vector to be in a certain range. + + Args: + val: The value to clamp. + lower_bound: Minimum of the range to clamp to. + upper_bound: Maximum of the range to clamp to. + + Returns: + An integer clamped to be within lower_bound and upper_bound. + """ + return max(min(val, upper_bound), lower_bound) + + +fn clamp( + val: SIMD, lower_bound: __type_of(val), upper_bound: __type_of(val) +) -> __type_of(val): + """Clamps the values in a SIMD vector to be in a certain range. + + Clamp cuts values in the input SIMD vector off at the upper bound and + lower bound values. For example, SIMD vector `[0, 1, 2, 3]` clamped to + a lower bound of 1 and an upper bound of 2 would return `[1, 1, 2, 2]`. + + Args: + val: The value to clamp. + lower_bound: Minimum of the range to clamp to. + upper_bound: Maximum of the range to clamp to. + + Returns: + A SIMD vector containing x clamped to be within lower_bound and + upper_bound. + """ + return val.clamp(lower_bound, upper_bound) + + # ===----------------------------------------------------------------------=== # # utilities # ===----------------------------------------------------------------------=== # From b6a64eb8f310f70b5ce8262d46958be10a7ed967 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:44:06 -0400 Subject: [PATCH 1278/2019] [stdlib] Add `UnsafePointer.alloc` overload that takes `alignment` as argument Harmonize API with `DTypePointer`. We can conclude which one to keep in follow up PRs. Also see #43451 MODULAR_ORIG_COMMIT_REV_ID: f00b2081dfe93b30003e946e683bc55ef26b5fe9 --- stdlib/src/memory/unsafe_pointer.mojo | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 54b48fbc8b..87ab5c8ad7 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -157,6 +157,26 @@ struct UnsafePointer[ ]: return ptr.address.address + @staticmethod + @always_inline + fn alloc(count: Int, alignment: Int = alignof[T]()) -> Self: + """Allocate an array with specified or default alignment. + + Args: + count: The number of elements in the array. + alignment: The alignment in bytes of the allocated memory. + + Returns: + The pointer to the newly allocated array. + """ + alias sizeof_t = sizeof[T]() + + constrained[sizeof_t > 0, "size must be greater than zero"]() + + return _malloc[T, address_space=address_space]( + sizeof_t * count, alignment=alignment + ) + @staticmethod @always_inline fn alloc[alignment: Int = alignof[T]()](count: Int) -> Self: From b62f74b782e03778409f299ddb5a30487c6c46d9 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 22 Jul 2024 12:33:04 -0700 Subject: [PATCH 1279/2019] [Stdlib] Take address space and exclusivity into account in methods MODULAR_ORIG_COMMIT_REV_ID: 69b5a13e2315e589ef33534ff95b933f703f5bf1 --- stdlib/src/memory/unsafe_pointer.mojo | 20 ++++++++++---------- stdlib/src/sys/intrinsics.mojo | 25 ++++++++----------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 87ab5c8ad7..50bdb6db7c 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -425,7 +425,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn simd_strided_load[ type: DType, width: Int, T: Intable - ](self: UnsafePointer[Scalar[type]], stride: T) -> SIMD[type, width]: + ](self: UnsafePointer[Scalar[type], *_], stride: T) -> SIMD[type, width]: """Performs a strided load of the SIMD vector. Parameters: @@ -446,7 +446,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn simd_strided_store[ type: DType, width: Int, T: Intable - ](self: UnsafePointer[Scalar[type]], val: SIMD[type, width], stride: T): + ](self: UnsafePointer[Scalar[type], *_], val: SIMD[type, width], stride: T): """Performs a strided store of the SIMD vector. Parameters: @@ -466,9 +466,9 @@ struct UnsafePointer[ *, width: Int = 1, alignment: Int = alignof[ - Scalar[type] + SIMD[type, width] ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type]], offset: SIMD[_, width]) -> SIMD[ + ](self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width]) -> SIMD[ type, width ]: """Gathers a SIMD vector from offsets of the current pointer. @@ -503,10 +503,10 @@ struct UnsafePointer[ type: DType, width: Int = 1, alignment: Int = alignof[ - Scalar[type] + SIMD[type, width] ]() if triple_is_nvidia_cuda() else 1, ]( - self: UnsafePointer[Scalar[type]], + self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width], mask: SIMD[DType.bool, width], default: SIMD[type, width], @@ -560,10 +560,10 @@ struct UnsafePointer[ type: DType, width: Int = 1, alignment: Int = alignof[ - Scalar[type] + SIMD[type, width] ]() if triple_is_nvidia_cuda() else 1, ]( - self: UnsafePointer[Scalar[type]], + self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width], val: SIMD[type, width], ): @@ -598,10 +598,10 @@ struct UnsafePointer[ type: DType, width: Int = 1, alignment: Int = alignof[ - Scalar[type] + SIMD[type, width] ]() if triple_is_nvidia_cuda() else 1, ]( - self: UnsafePointer[Scalar[type]], + self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width], val: SIMD[type, width], mask: SIMD[DType.bool, width], diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index a121726518..638941ca57 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1182,8 +1182,8 @@ struct PrefetchOptions: @always_inline("nodebug") fn prefetch[ - params: PrefetchOptions, type: DType, address_space: AddressSpace -](addr: UnsafePointer[Scalar[type], address_space, _]): + params: PrefetchOptions, type: DType +](addr: UnsafePointer[Scalar[type], *_]): """Prefetches an instruction or data into cache before it is used. The prefetch function provides prefetching hints for the target @@ -1192,7 +1192,6 @@ fn prefetch[ Parameters: params: Configuration options for the prefect intrinsic. type: The DType of value stored in addr. - address_space: The address space of the pointer. Args: addr: The data pointer to prefetch. @@ -1261,7 +1260,7 @@ fn masked_store[ size: Int ]( value: SIMD, - addr: UnsafePointer[Scalar[value.type]], + addr: UnsafePointer[Scalar[value.type], *_], mask: SIMD[DType.bool, size], alignment: Int = 1, ): @@ -1303,7 +1302,7 @@ fn compressed_store[ type: DType, size: Int ]( value: SIMD[type, size], - addr: UnsafePointer[Scalar[type]], + addr: UnsafePointer[Scalar[type], *_], mask: SIMD[DType.bool, size], ): """Compresses the lanes of `value`, skipping `mask` lanes, and stores @@ -1340,12 +1339,9 @@ fn compressed_store[ @always_inline("nodebug") fn strided_load[ - type: DType, - simd_width: Int, - /, - address_space: AddressSpace = AddressSpace.GENERIC, + type: DType, simd_width: Int ]( - addr: UnsafePointer[Scalar[type], address_space, _], + addr: UnsafePointer[Scalar[type], *_], stride: Int, mask: SIMD[DType.bool, simd_width] = True, ) -> SIMD[type, simd_width]: @@ -1354,7 +1350,6 @@ fn strided_load[ Parameters: type: DType of `value`, the value to store. simd_width: The width of the SIMD vectors. - address_space: The address space of the memory location. Args: addr: The memory location to load data from. @@ -1388,13 +1383,10 @@ fn strided_load[ @always_inline("nodebug") fn strided_store[ - type: DType, - simd_width: Int, - /, - address_space: AddressSpace = AddressSpace.GENERIC, + type: DType, simd_width: Int ]( value: SIMD[type, simd_width], - addr: UnsafePointer[Scalar[type], address_space, _], + addr: UnsafePointer[Scalar[type], *_], stride: Int, mask: SIMD[DType.bool, simd_width] = True, ): @@ -1403,7 +1395,6 @@ fn strided_store[ Parameters: type: DType of `value`, the value to store. simd_width: The width of the SIMD vectors. - address_space: The address space of the memory location. Args: value: The values to store. From 6aa27d1f9dd031f370d851d404087a6433a96b3a Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:08:59 -0400 Subject: [PATCH 1280/2019] [******] Convert `DTypePointer` uses to `UnsafePointer` Convert some uses of `DTypePointer` to `UnsafePointer`. MODULAR_ORIG_COMMIT_REV_ID: 8eb19626ec9ab17d0b92e76052486958f8250b18 --- examples/mandelbrot.mojo | 4 ++-- examples/matmul.mojo | 8 ++++---- examples/reduce.mojo | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index d6b3b10df3..4798a5cd45 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -36,10 +36,10 @@ alias max_y = 1.5 struct Matrix[type: DType, rows: Int, cols: Int]: - var data: DTypePointer[type] + var data: UnsafePointer[Scalar[type]] fn __init__(inout self): - self.data = DTypePointer[type].alloc(rows * cols) + self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) fn store[nelts: Int](self, row: Int, col: Int, val: SIMD[type, nelts]): SIMD[size=nelts].store(self.data, row * cols + col, val) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index f297d4d54d..92bdb5e936 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -48,21 +48,21 @@ alias tile_k = 4 # K must be a multiple of this struct Matrix[rows: Int, cols: Int]: - var data: DTypePointer[type] + var data: UnsafePointer[Scalar[type]] # Initialize zeroing all values fn __init__(inout self): - self.data = DTypePointer[type].alloc(rows * cols) + self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) memset_zero(self.data.address, rows * cols) # Initialize taking a pointer, don't set any elements - fn __init__(inout self, data: DTypePointer[type]): + fn __init__(inout self, data: UnsafePointer[Scalar[type]]): self.data = data ## Initialize with random values @staticmethod fn rand() -> Self: - var data = DTypePointer[type].alloc(rows * cols) + var data = UnsafePointer[Scalar[type]].alloc(rows * cols) rand(data.address, rows * cols) return Self(data) diff --git a/examples/reduce.mojo b/examples/reduce.mojo index a4dcbde3ff..9667c13b9b 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -80,8 +80,8 @@ fn main() raises: "Shows algorithm.sum from stdlib with much better performance\n" ) # Allocate and randomize data, then create two buffers - var ptr_small = DTypePointer[type].alloc(size_small) - var ptr_large = DTypePointer[type].alloc(size_large) + var ptr_small = UnsafePointer[Scalar[type]].alloc(size_small) + var ptr_large = UnsafePointer[Scalar[type]].alloc(size_large) rand(ptr_small.address, size_small) rand(ptr_large.address, size_large) From ecda457e0f8b17f41aca542c66b95a4ff96a0460 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:26:26 -0400 Subject: [PATCH 1281/2019] [stdlib] Fix and simplify `bench_sort` - Fix `count` loop in `bench_tiny_list_sort`. They iterate from 0 to 4 originally, but `_small_sort` should be sorting lists of size from 2 to 5. - Remove `int`s from `dtypes` list as the result is very similar to `uint`s. - Shorten names in `BenchId`. - Set `num_repetition` and `warmup_iterations` to default. - Seed random list generation MODULAR_ORIG_COMMIT_REV_ID: 755407bc084cc99507f1183f0f4652593d5cd781 --- stdlib/benchmarks/builtin/bench_sort.mojo | 83 +++++++---------------- 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 7b0d8a5719..ebe3c58c9c 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -47,7 +47,7 @@ fn random_scalar_list[ dt: DType ](size: Int, max: Scalar[dt] = Scalar[dt].MAX) -> List[Scalar[dt]]: var result = List[Scalar[dt]](capacity=size) - for i in range(size): + for _ in range(size): @parameter if dt.is_integral() and dt.is_signed(): @@ -83,6 +83,17 @@ fn small_sort[size: Int, type: DType](list: List[Scalar[type]]): _small_sort[size, Scalar[type], _less_than](list.data) +@always_inline +fn heap_sort[type: DType](list: List[Scalar[type]]): + @parameter + fn _less_than( + lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] + ) -> Bool: + return lhs.data < rhs.data + + _heap_sort[Scalar[type], _less_than](list.data, len(list)) + + # ===----------------------------------------------------------------------===# # Benchmark sort functions with a tiny list size # ===----------------------------------------------------------------------===# @@ -97,7 +108,7 @@ fn bench_tiny_list_sort(inout m: Bench) raises: alias dt = dtypes[type_index] @parameter - for count in range(small_list_size): + for count in range(2, small_list_size + 1): var list = random_scalar_list[dt](count) @parameter @@ -131,28 +142,13 @@ fn bench_tiny_list_sort(inout m: Bench) raises: b.iter[call_fn]() m.bench_function[bench_sort_list]( - BenchId( - "bench_std_sort_random_list_" - + str(count) - + "_type_" - + str(dt) - ) + BenchId("std_sort_random_" + str(count) + "_" + str(dt)) ) m.bench_function[bench_small_sort]( - BenchId( - "bench_sml_sort_random_list_" - + str(count) - + "_type_" - + str(dt) - ) + BenchId("sml_sort_random_" + str(count) + "_" + str(dt)) ) m.bench_function[bench_insertion_sort]( - BenchId( - "bench_ins_sort_random_list_" - + str(count) - + "_type_" - + str(dt) - ) + BenchId("ins_sort_random_" + str(count) + "_" + str(dt)) ) _ = list^ @@ -194,20 +190,10 @@ fn bench_small_list_sort(inout m: Bench) raises: b.iter[call_fn]() m.bench_function[bench_sort_list]( - BenchId( - "bench_std_sort_random_list_" - + str(count[]) - + "_type_" - + str(dt) - ) + BenchId("std_sort_random_" + str(count[]) + "_" + str(dt)) ) m.bench_function[bench_insertion_sort]( - BenchId( - "bench_ins_sort_random_list_" - + str(count[]) - + "_type_" - + str(dt) - ) + BenchId("ins_sort_random_" + str(count[]) + "_" + str(dt)) ) _ = list^ @@ -217,17 +203,6 @@ fn bench_small_list_sort(inout m: Bench) raises: # ===----------------------------------------------------------------------===# -@always_inline -fn heap_sort[type: DType](list: List[Scalar[type]]): - @parameter - fn _less_than( - lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] - ) -> Bool: - return lhs.data < rhs.data - - _heap_sort[Scalar[type], _less_than](list.data, len(list)) - - @parameter fn bench_large_list_sort(inout m: Bench) raises: var counts = List(1 << 12, 1 << 16) @@ -260,21 +235,11 @@ fn bench_large_list_sort(inout m: Bench) raises: b.iter[call_fn]() m.bench_function[bench_sort_list]( - BenchId( - "bench_std_sort_random_list_" - + str(count[]) - + "_type_" - + str(dt) - ) + BenchId("std_sort_random_" + str(count[]) + "_" + str(dt)) ) m.bench_function[bench_heap_sort]( - BenchId( - "bench_heap_sort_random_list_" - + str(count[]) - + "_type_" - + str(dt) - ) + BenchId("heap_sort_random_" + str(count[]) + "_" + str(dt)) ) _ = list^ @@ -315,7 +280,7 @@ fn bench_low_cardinality_list_sort(inout m: Bench) raises: m.bench_function[bench_sort_list]( BenchId( - "bench_std_sort_low_card_list_" + "std_sort_low_card_" + str(count[]) + "_delta_" + str(delta[]) @@ -324,7 +289,7 @@ fn bench_low_cardinality_list_sort(inout m: Bench) raises: m.bench_function[bench_heap_sort]( BenchId( - "bench_heap_sort_low_card_list_" + "heap_sort_low_card_" + str(count[]) + "_delta_" + str(delta[]) @@ -339,8 +304,8 @@ fn bench_low_cardinality_list_sort(inout m: Bench) raises: def main(): - seed() - var m = Bench(BenchConfig(num_repetitions=3, warmup_iters=100)) + seed(1) + var m = Bench() bench_tiny_list_sort(m) bench_small_list_sort(m) From ffdec9c9ecb4525e8b1fffddd2051fdd3483da7c Mon Sep 17 00:00:00 2001 From: "Ehsan M. Kermani" <6980212+ehsanmok@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:36:34 -0600 Subject: [PATCH 1282/2019] This PR fixes the String to C string conversions in ffi module that require null termination. MODULAR_ORIG_COMMIT_REV_ID: 70b790a3efa1665b002a860bcd9756309822835c --- stdlib/src/sys/ffi.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 75bbf107e5..24281fd398 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -68,7 +68,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @parameter if not os_is_windows(): var handle = external_call["dlopen", UnsafePointer[Int8]]( - path.unsafe_ptr(), flags + path.unsafe_cstr_ptr(), flags ) if handle == UnsafePointer[Int8](): var error_message = external_call[ @@ -102,7 +102,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): ]() var opaque_function_ptr = external_call["dlsym", UnsafePointer[Int8]]( - self.handle.address, name.unsafe_ptr() + self.handle.address, name.unsafe_cstr_ptr() ) if opaque_function_ptr: return True From 94121bf4ab52f324be489b6f6c3df132fb130dd6 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 23 Jul 2024 00:58:09 +0300 Subject: [PATCH 1283/2019] [mojo-stdlib] Make `SIMD` constructor from `Bool` conditional on dtype (#43835) Only allow constructing `DType.bool` data types from `Bool`. This makes the `SIMD` type less prone to accidental implicit conversions. END_PUBLIC MODULAR_ORIG_COMMIT_REV_ID: 5e575c2cf1005d4be190adfe88c6bff7eeda423e --- docs/changelog.md | 2 + stdlib/src/builtin/simd.mojo | 8 +-- stdlib/test/builtin/test_simd.mojo | 78 ++++++++++++++++-------------- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c9c9cbfd4b..5961834bd4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -516,6 +516,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. ([PR #3180](https://github.com/modularml/mojo/pull/3180) by [@jjvraw](https://github.com/jjvraw)) +- `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 79fcaf7978..219b89aaf6 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -280,7 +280,7 @@ struct SIMD[type: DType, size: Int]( ](casted) @always_inline("nodebug") - fn __init__(inout self, value: Bool): + fn __init__(inout self: SIMD[DType.bool, size], value: Bool, /): """Initializes the SIMD vector with a bool value. The bool value is splatted across all elements of the SIMD vector. @@ -291,10 +291,10 @@ struct SIMD[type: DType, size: Int]( _simd_construction_checks[type, size]() var casted = __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] + _type = __mlir_type[`!pop.simd<1, bool>`] ](value._as_scalar_bool()) self.value = __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] + _type = __mlir_type[`!pop.simd<`, size.value, `, bool>`] ](casted) @always_inline("nodebug") @@ -904,7 +904,7 @@ struct SIMD[type: DType, size: Int]( @parameter if type is DType.bool: - return self.select(Self(False), Self(True)) + return rebind[Self](self.select(Self(False), Self(True))) else: return self ^ -1 diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 247ef4a5f4..33a85cd543 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1145,38 +1145,42 @@ def test_reduce(): @parameter if type is DType.bool: # reduce_and - x8 = X8(False, False, True, True, False, True, False, True) - x4 = X4(False, False, False, True) - x2 = X2(False, False) - x1 = X1(False) - assert_equal(x8.reduce_and(), x1) - assert_equal(x4.reduce_and(), x1) - assert_equal(x2.reduce_and(), x1) - assert_equal(x1.reduce_and(), x1) - assert_equal(x8.reduce_and[2](), x2) - assert_equal(x4.reduce_and[2](), x2) - assert_equal(x2.reduce_and[2](), x2) - assert_equal(x8.reduce_and[4](), x4) - assert_equal(x4.reduce_and[4](), x4) - assert_equal(x8.reduce_and[8](), x8) - assert_equal(X2(True, True).reduce_and(), True) + var x8b = SIMD[DType.bool, 8]( + False, False, True, True, False, True, False, True + ) + var x4b = SIMD[DType.bool, 4](False, False, False, True) + var x2b = SIMD[DType.bool, 2](False, False) + var x1b = SIMD[DType.bool, 1](False) + assert_equal(x8b.reduce_and(), x1b) + assert_equal(x4b.reduce_and(), x1b) + assert_equal(x2b.reduce_and(), x1b) + assert_equal(x1b.reduce_and(), x1b) + assert_equal(x8b.reduce_and[2](), x2b) + assert_equal(x4b.reduce_and[2](), x2b) + assert_equal(x2b.reduce_and[2](), x2b) + assert_equal(x8b.reduce_and[4](), x4b) + assert_equal(x4b.reduce_and[4](), x4b) + assert_equal(x8b.reduce_and[8](), x8b) + assert_equal(SIMD[DType.bool, 2](True, True).reduce_and(), True) # reduce_or - x8 = X8(False, False, True, True, False, True, False, True) - x4 = X4(False, True, True, True) - x2 = X2(True, True) - x1 = X1(True) - assert_equal(x8.reduce_or(), x1) - assert_equal(x4.reduce_or(), x1) - assert_equal(x2.reduce_or(), x1) - assert_equal(x1.reduce_or(), x1) - assert_equal(x8.reduce_or[2](), x2) - assert_equal(x4.reduce_or[2](), x2) - assert_equal(x2.reduce_or[2](), x2) - assert_equal(x8.reduce_or[4](), x4) - assert_equal(x4.reduce_or[4](), x4) - assert_equal(x8.reduce_or[8](), x8) - assert_equal(X2(False, False).reduce_or(), False) + x8b = SIMD[DType.bool, 8]( + False, False, True, True, False, True, False, True + ) + x4b = SIMD[DType.bool, 4](False, True, True, True) + x2b = SIMD[DType.bool, 2](True, True) + x1b = SIMD[DType.bool, 1](True) + assert_equal(x8b.reduce_or(), x1b) + assert_equal(x4b.reduce_or(), x1b) + assert_equal(x2b.reduce_or(), x1b) + assert_equal(x1b.reduce_or(), x1b) + assert_equal(x8b.reduce_or[2](), x2b) + assert_equal(x4b.reduce_or[2](), x2b) + assert_equal(x2b.reduce_or[2](), x2b) + assert_equal(x8b.reduce_or[4](), x4b) + assert_equal(x4b.reduce_or[4](), x4b) + assert_equal(x8b.reduce_or[8](), x8b) + assert_equal(SIMD[DType.bool, 2](False, False).reduce_or(), False) @parameter if type.is_integral(): @@ -1461,9 +1465,9 @@ def test_comparison(): @parameter if type is DType.bool: - var all_true = X4(True) - var all_false = X4(False) - var mixed = X4(True, True, False, False) + var all_true = SIMD[DType.bool, 4](True) + var all_false = SIMD[DType.bool, 4](False) + var mixed = SIMD[DType.bool, 4](True, True, False, False) assert_true(all_false.__lt__(all_true).reduce_and()) assert_false(all_true.__lt__(all_false).reduce_or()) @@ -1482,7 +1486,9 @@ def test_comparison(): assert_false(mixed_le[3]) assert_true( - all_true.__eq__(X4(True, True, True, True)).reduce_and() + all_true.__eq__( + SIMD[DType.bool, 4](True, True, True, True) + ).reduce_and() ) assert_false(all_true.__eq__(all_false).reduce_or()) var mixed_eq = all_true.__eq__(mixed) @@ -1493,7 +1499,9 @@ def test_comparison(): assert_true(all_true.__ne__(all_false).reduce_and()) assert_false( - all_true.__ne__(X4(True, True, True, True)).reduce_or() + all_true.__ne__( + SIMD[DType.bool, 4](True, True, True, True) + ).reduce_or() ) var mixed_ne = all_true.__ne__(mixed) assert_false(mixed_ne[0]) From 9aabe69e0a358331774a06f5e6f92ca6b58fd28b Mon Sep 17 00:00:00 2001 From: Shayan Date: Mon, 22 Jul 2024 15:15:18 -0700 Subject: [PATCH 1284/2019] [External] [docs] Add `str()` in `print_many()` in Functions (#43863) [External] [docs] Add `str()` in `print_many()` in Functions Resolves `error: invalid call to 'print_string': argument #0 cannot be converted from 'T' to 'String' ` Co-authored-by: Shayan Closes modularml/mojo#3286 MODULAR_ORIG_COMMIT_REV_ID: e556526e87c815cf2d9c24a5e959370880d2e245 --- docs/manual/functions.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index f7ae2d9fba..89aed06792 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -552,7 +552,7 @@ " @parameter\n", " fn print_elt[T: Stringable](a: T):\n", " print_string(\" \")\n", - " print_string(a)\n", + " print_string(str(a))\n", " rest.each[print_elt]()\n", "print_many(\"Bob\")" ] From ad34cc5084919bb8940be0db53efd9dd680a84ed Mon Sep 17 00:00:00 2001 From: Stephen McGroarty Date: Mon, 22 Jul 2024 16:34:08 -0700 Subject: [PATCH 1285/2019] [stdlib] Remove branches from builtins Streamline IR generation for some very frequently used builtin functions including Int and range functions. Replacing the branches with selects allows us to lower to simplier IR and perform more optimizations earlier. MODULAR_ORIG_COMMIT_REV_ID: 4f98c64ddb26fcee9606d99ff6f02551c944318f --- stdlib/src/builtin/int.mojo | 38 +++++++++++++-------------- stdlib/src/builtin/range.mojo | 41 ++++++++++++++++++------------ stdlib/test/builtin/test_simd.mojo | 9 ++++--- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 963b9db0d3..31e0e4c234 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -30,6 +30,7 @@ from builtin.string import ( from utils import InlineArray from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type +from utils._select import _select_register_value as select # ===----------------------------------------------------------------------=== # # Indexer @@ -570,15 +571,14 @@ struct Int( Returns: `floor(self/rhs)` value. """ - if rhs == 0: - # this should raise an exception. - return 0 - var div: Int = self._positive_div(rhs) - if self > 0 and rhs > 0: - return div + # This should raise an exception + var denominator = select(rhs == 0, 1, rhs) + var div: Int = self._positive_div(denominator) + var mod = self - div * rhs - if ((rhs < 0) ^ (self < 0)) and mod: - return div - 1 + var divMod = select(((rhs < 0) ^ (self < 0)) & mod, div - 1, div) + div = select(self > 0 & rhs > 0, div, divMod) + div = select(rhs == 0, 0, div) return div @always_inline("nodebug") @@ -591,15 +591,15 @@ struct Int( Returns: The remainder of dividing self by rhs. """ - if rhs == 0: - # this should raise an exception. - return 0 - if rhs > 0 and self > 0: - return self._positive_rem(rhs) - var div: Int = self._positive_div(rhs) + var denominator = select(rhs == 0, 1, rhs) + var div: Int = self._positive_div(denominator) + var mod = self - div * rhs - if ((rhs < 0) ^ (self < 0)) and mod: - return mod + rhs + var divMod = select(((rhs < 0) ^ (self < 0)) & mod, mod + rhs, mod) + mod = select( + self > 0 & rhs > 0, self._positive_rem(denominator), divMod + ) + mod = select(rhs == 0, 0, mod) return mod @always_inline("nodebug") @@ -615,10 +615,10 @@ struct Int( if rhs == 0: return 0, 0 var div: Int = self._positive_div(rhs) - if rhs > 0 and self > 0: + if rhs > 0 & self > 0: return div, self._positive_rem(rhs) var mod = self - div * rhs - if ((rhs < 0) ^ (self < 0)) and mod: + if ((rhs < 0) ^ (self < 0)) & mod: return div - 1, mod + rhs return div, mod @@ -1013,7 +1013,7 @@ struct Int( Returns: The absolute value. """ - return -self if self < 0 else self + return select(self < 0, -self, self) @always_inline("nodebug") fn __ceil__(self) -> Self: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index c45886e096..913514530d 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -22,6 +22,7 @@ from python import ( PythonObject, ) # TODO: remove this and fixup downstream imports from math import ceildiv +from utils._select import _select_register_value as select # ===----------------------------------------------------------------------=== # # Utilities @@ -49,13 +50,12 @@ fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: return (numerator + denominator - 1)._positive_div(denominator) -@always_inline +@always_inline("nodebug") fn _sign(x: Int) -> Int: - if x > 0: - return 1 - if x < 0: - return -1 - return 0 + var result = 0 + result = select(x > 0, 1, result) + result = select(x < 0, -1, result) + return result # ===----------------------------------------------------------------------=== # @@ -167,7 +167,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): fn __iter__(self) -> _StridedRangeIterator: return _StridedRangeIterator(self.start, self.end, self.step) - @always_inline + @always_inline("nodebug") fn __next__(inout self) -> Int: var result = self.start self.start += self.step @@ -175,11 +175,22 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): @always_inline("nodebug") fn __len__(self) -> Int: - if (self.step > 0 and self.start > self.end) or ( - self.step < 0 and self.start < self.end - ): - return 0 - return _div_ceil_positive(abs(self.start - self.end), abs(self.step)) + # If the step is positive we want to check that the start is smaller + # than the end, if the step is negative we want to check the reverse. + # We break this into selects to avoid generating branches. + var c1 = (self.step > 0) & (self.start > self.end) + var c2 = (self.step < 0) & (self.start < self.end) + var cnd = c1 | c2 + + var numerator = abs(self.start - self.end) + var denominator = abs(self.step) + + # If the start is after the end and step is positive then we + # are generating an empty range. In this case divide 0/1 to + # return 0 without a branch. + return _div_ceil_positive( + select(cnd, 0, numerator), select(cnd, 1, denominator) + ) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: @@ -353,9 +364,7 @@ struct _UIntStridedRangeIterator(UIntSized): @always_inline fn __len__(self) -> UInt: - if self.start < self.end: - return self.end - self.start - return 0 + return select(self.start < self.end, self.end - self.start, 0) @always_inline fn __next__(inout self) -> UInt: @@ -371,7 +380,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): var end: UInt var step: UInt - @always_inline + @always_inline("nodebug") fn __init__(inout self, start: UInt, end: UInt, step: UInt): self.start = start self.end = end diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 33a85cd543..083914b71f 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -904,8 +904,9 @@ def test_deinterleave(): assert_equal(tup2[1], Float32(2)) var tup4 = SIMD[DType.index, 4](0, 1, 2, 3).deinterleave() - assert_equal(tup4[0], SIMD[DType.index, 2](0, 2)) - assert_equal(tup4[1], SIMD[DType.index, 2](1, 3)) + + assert_equal(tup4[0], __type_of(tup4[0])(0, 2)) + assert_equal(tup4[1], __type_of(tup4[0])(1, 3)) def test_extract(): @@ -1325,8 +1326,8 @@ def test_modf(): def test_split(): var tup = SIMD[DType.index, 8](1, 2, 3, 4, 5, 6, 7, 8).split() - assert_equal(tup[0], SIMD[DType.index, 4](1, 2, 3, 4)) - assert_equal(tup[1], SIMD[DType.index, 4](5, 6, 7, 8)) + assert_equal(tup[0], __type_of(tup[0])(1, 2, 3, 4)) + assert_equal(tup[1], __type_of(tup[1])(5, 6, 7, 8)) def test_contains(): From c20a6de71e66537fa347e076b8768d7c7ae64126 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 22 Jul 2024 17:06:24 -0700 Subject: [PATCH 1286/2019] [mojo-docs] Add bugfix to changelog MODULAR_ORIG_COMMIT_REV_ID: 0fac362cb510a7dbe0c2f7cf1b3e868e24b62b7c --- docs/changelog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5961834bd4..dfe4689906 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -563,8 +563,11 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#248](https://github.com/modularml/mojo/issues/248) - [Feature] Enable `__setitem__` to take variadic arguments -- [#3065] - Fix incorrect behavior +- [#3065](https://github.com/modularml/mojo/issues/3065) - Fix incorrect behavior of `SIMD.__int__` on unsigned types -- [#3045] - Disable implicit SIMD +- [#3045](https://github.com/modularml/mojo/issues/3045) - Disable implicit SIMD conversion routes through `Bool` + +- [#3126](https://github.com/modularml/mojo/issues/3126) - [BUG] List doesn't + work at compile time. From 111c76c8213a7aa3ab811b6e074e2d1fed5c959b Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 23 Jul 2024 08:15:18 -0700 Subject: [PATCH 1287/2019] [External] [stdlib] Fix argument convention for `iota` (#43906) [External] [stdlib] Fix argument convention for `iota` - The `List` arguments should be `inout`. - Improved the docstinrg a bit. - Make `DType` parameter infer-only. Co-authored-by: soraros Closes modularml/mojo#3261 MODULAR_ORIG_COMMIT_REV_ID: 0aad112d2b696e42a6b199299b83054e3c4be591 --- stdlib/src/math/math.mojo | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 88a3df0855..7218e074fb 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -992,31 +992,25 @@ fn iota[ Scalar.store(buff, i, i + offset) -fn iota[type: DType](v: List[Scalar[type]], offset: Int = 0): - """Fill the vector with numbers ranging from offset to offset + len - 1, - spaced by 1. - - The function doesn't return anything, the vector is updated inplace. +fn iota[type: DType, //](inout v: List[Scalar[type]], offset: Int = 0): + """Fill a list with consecutive numbers starting from the specified offset. Parameters: type: DType of the underlying data. Args: - v: The vector to fill. - offset: The value to fill at index 0. + v: The list to fill with numbers. + offset: The starting value to fill at index 0. """ iota(v.data, len(v), offset) -fn iota(v: List[Int], offset: Int = 0): - """Fill the vector with numbers ranging from offset to offset + len - 1, - spaced by 1. - - The function doesn't return anything, the vector is updated inplace. +fn iota(inout v: List[Int], offset: Int = 0): + """Fill a list with consecutive numbers starting from the specified offset. Args: - v: The vector to fill. - offset: The value to fill at index 0. + v: The list to fill with numbers. + offset: The starting value to fill at index 0. """ var buff = v.data.bitcast[Scalar[DType.index]]() iota(buff, len(v), offset=offset) From 2af2655dac0ca0c48e2342bf0fa29f2ad50e01b9 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Tue, 23 Jul 2024 11:50:14 -0700 Subject: [PATCH 1288/2019] [External] [stdlib] Add string justify methods (#43917) [External] [stdlib] Add string justify methods Co-authored-by: Maxim Zaks Closes modularml/mojo#3278 MODULAR_ORIG_COMMIT_REV_ID: cb25845fe54d44e5122a3723f460239bab2726ea --- docs/changelog.md | 5 +++ stdlib/src/builtin/string.mojo | 52 ++++++++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 21 +++++++++++ 3 files changed, 78 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index dfe4689906..a3c8948973 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,11 @@ what we publish. ### ⭐️ New +- `String` class now have `rjust`, `ljust` and `center` methods to return + a justified string based on width and fillchar. + ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by + [@mzaks](https://github.com/mzaks)) + - Creating nested `PythonObject` from a list or tuple of python objects is possible now: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 3be49de23a..cfa413bfd2 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -2104,6 +2104,58 @@ struct String( return False return True + fn rjust(self, width: Int, fillchar: StringLiteral = " ") -> String: + """Returns the string right justified in a string of specified width. + + Args: + width: The width of the field containing the string. + fillchar: Specifies the padding character. + + Returns: + Returns right justified string, or self if width is not bigger than self length. + """ + return self._justify(width - len(self), width, fillchar) + + fn ljust(self, width: Int, fillchar: StringLiteral = " ") -> String: + """Returns the string left justified in a string of specified width. + + Args: + width: The width of the field containing the string. + fillchar: Specifies the padding character. + + Returns: + Returns left justified string, or self if width is not bigger than self length. + """ + return self._justify(0, width, fillchar) + + fn center(self, width: Int, fillchar: StringLiteral = " ") -> String: + """Returns the string center justified in a string of specified width. + + Args: + width: The width of the field containing the string. + fillchar: Specifies the padding character. + + Returns: + Returns center justified string, or self if width is not bigger than self length. + """ + return self._justify(width - len(self) >> 1, width, fillchar) + + fn _justify( + self, start: Int, width: Int, fillchar: StringLiteral + ) -> String: + if len(self) >= width: + return self + debug_assert( + len(fillchar) == 1, "fill char needs to be a one byte literal" + ) + var fillbyte = fillchar.as_bytes_slice()[0] + var buffer = List[UInt8](capacity=width + 1) + buffer.resize(width, fillbyte) + buffer.append(0) + memcpy(buffer.unsafe_ptr().offset(start), self.unsafe_ptr(), len(self)) + var result = String(buffer) + return result^ + # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 025db4956f..e9fc0225ef 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -1469,6 +1469,24 @@ def test_isprintable(): assert_false(String("aa\tae").isprintable()) +def test_rjust(): + assert_equal(String("hello").rjust(4), "hello") + assert_equal(String("hello").rjust(8), " hello") + assert_equal(String("hello").rjust(8, "*"), "***hello") + + +def test_ljust(): + assert_equal(String("hello").ljust(4), "hello") + assert_equal(String("hello").ljust(8), "hello ") + assert_equal(String("hello").ljust(8, "*"), "hello***") + + +def test_center(): + assert_equal(String("hello").center(4), "hello") + assert_equal(String("hello").center(8), " hello ") + assert_equal(String("hello").center(8, "*"), "*hello**") + + def main(): test_constructors() test_copy() @@ -1518,3 +1536,6 @@ def main(): test_format_args() test_isdigit() test_isprintable() + test_rjust() + test_ljust() + test_center() From 554045e4c1230e9a1026484064a0ed7a4803cdb5 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 23 Jul 2024 13:33:37 -0700 Subject: [PATCH 1289/2019] [mojo] Mildly refactor `_startup.mojo` (NFC) These functions aren't meant to be public APIs. MODULAR_ORIG_COMMIT_REV_ID: 583ac8b63d4fbf229771a0d4f7633809995eb334 --- stdlib/src/builtin/_startup.mojo | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 5f99c73da0..039fb16a27 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -13,27 +13,12 @@ """Implements functionality to start a mojo execution.""" from sys import external_call - - -@always_inline -fn _get_global[ - name: StringLiteral, - init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], - destroy_fn: fn (UnsafePointer[NoneType]) -> None, -]( - payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]() -) -> UnsafePointer[NoneType]: - return external_call[ - "KGEN_CompilerRT_GetGlobalOrCreate", UnsafePointer[NoneType] - ](StringRef(name), payload, init_fn, destroy_fn) +from sys.ffi import _get_global fn _init_global_runtime( ignored: UnsafePointer[NoneType], ) -> UnsafePointer[NoneType]: - """Initialize the global runtime. This is a singleton that handle the common - case where the runtime has the same number of threads as the number of cores. - """ return external_call[ "KGEN_CompilerRT_AsyncRT_CreateRuntime", UnsafePointer[NoneType] ](0) @@ -46,14 +31,6 @@ fn _destroy_global_runtime(ptr: UnsafePointer[NoneType]): @always_inline fn _get_current_or_global_runtime() -> UnsafePointer[NoneType]: - """Returns the current runtime, or returns the Mojo singleton global - runtime, creating it if it does not already exist. When Mojo is used within - the Modular Execution Engine the current runtime will be that already - constructed by the execution engine. If the user has already manually - constructed a runtime and added tasks to it, the current runtime for those - tasks will be that runtime. Otherwise, the singleton runtime is used, which - is created with number of threads equal to the number of cores. - """ var current_runtime = external_call[ "KGEN_CompilerRT_AsyncRT_GetCurrentRuntime", UnsafePointer[NoneType] ]() From b2ef0b647da77eeab837957aa062c3963e22e7a0 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:44:29 -0700 Subject: [PATCH 1290/2019] [External] [stdlib] Allow `!r` conversion flag in `String.format` (#43914) [External] [stdlib] Allow `!r` conversion flag in `String.format` This addresses https://github.com/modularml/mojo/issues/3267. - Implemented parsing and handling of `!s` and `!r` conversion flags in format strings. - Updated the `_FormatCurlyEntry` struct to include a `conversion_flag` field. - Modified the `create_entries` method to correctly parse and store conversion flags. - Updated the string formatting logic to apply the appropriate conversion (`str` or `repr`) based on the flag. Additionally, I was conscious of potential future support of `!a` flag and `format_spec` , which is not currently supported. The additions of this PR should be direct to modify for this support. Hence why `supported_conversion_flags` was added, and a TODO was left in the code for the latter. For reference, see [Python's Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax): ``` replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}" field_name ::= arg_name ("." attribute_name | "[" element_index "]")* arg_name ::= [identifier | digit+] attribute_name ::= identifier element_index ::= digit+ | index_string index_string ::= + conversion ::= "r" | "s" | "a" format_spec ::= ``` ORIGINAL_AUTHOR=Joshua James Venter <67124214+jjvraw@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3279 Co-authored-by: Joshua James Venter <67124214+jjvraw@users.noreply.github.com> Closes modularml/mojo#3279 MODULAR_ORIG_COMMIT_REV_ID: 9ee92a55ac683be22f7b93012f37c6980f7110b6 --- docs/changelog.md | 14 +++ stdlib/src/builtin/string.mojo | 126 ++++++++++++++++++++++----- stdlib/test/builtin/test_string.mojo | 102 ++++++++++++++++++++++ 3 files changed, 218 insertions(+), 24 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a3c8948973..eecbc11c7d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -336,6 +336,20 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. `Int` type. ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) +- `String.format()` now supports conversion flags `!s` and `!r`, allowing for + `str()` and `repr()` conversions within format strings. + ([PR #3279](https://github.com/modularml/mojo/pull/3279) by [@jjvraw](https://github.com/jjvraw)) + + Example: + + ```mojo + String("{} {!r}").format("Mojo", "Mojo") + # "Mojo 'Mojo'" + + String("{0!s} {0!r}").format("Mojo") + # "Mojo 'Mojo'" + ``` + ### 🦋 Changed - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index cfa413bfd2..adf369e9ff 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1967,7 +1967,7 @@ struct String( ) return String(buf^) - fn format[*Ts: Stringable](self, *args: *Ts) raises -> String: + fn format[*Ts: StringRepresentable](self, *args: *Ts) raises -> String: """Format a template with *args. Example of manual indexing: @@ -2022,14 +2022,21 @@ struct String( @parameter for i in range(num_pos_args): if i == e[].field[Int]: - res += str(args[i]) + if e[].conversion_flag == "r": + res += repr(args[i]) + else: + res += str(args[i]) if e[].is_automatic_indexing(): @parameter for i in range(num_pos_args): if i == current_automatic_arg_index: - res += str(args[i]) + if e[].conversion_flag == "r": + res += repr(args[i]) + else: + res += str(args[i]) + current_automatic_arg_index += 1 pos_in_self = e[].last_curly + 1 @@ -2285,6 +2292,22 @@ fn _calc_format_buffer_size[type: DType]() -> Int: # ===----------------------------------------------------------------------===# +trait StringRepresentable(Stringable, Representable): + """The `StringRepresentable` trait denotes a trait composition of the + `Stringable` and `Representable` traits. + + This trait is used by the `format()` method to support both `{!s}` (or `{}`) + and `{!r}` format specifiers. It allows the method to handle types that + can be formatted using both their string representation and their + more detailed representation. + + Types implementing this trait must provide both `__str__()` and `__repr__()` + methods as defined in `Stringable` and `Representable` traits respectively. + """ + + pass + + @value struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): """ @@ -2302,6 +2325,9 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): var last_curly: Int """The index of an closing brace around a substitution field.""" + var conversion_flag: String + """Store the format specifier (e.g., 'r' for repr).""" + alias _FieldVariantType = Variant[ String, # kwargs indexing (`{field_name}`) Int, # args manual indexing (`{3}`) @@ -2314,6 +2340,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): fn __init__(inout self, *, other: Self): self.first_curly = other.first_curly self.last_curly = other.last_curly + self.conversion_flag = other.conversion_flag self.field = Self._FieldVariantType(other=other.field) fn is_escaped_brace(ref [_]self) -> Bool: @@ -2360,6 +2387,10 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): var raised_manual_index = Optional[Int](None) var raised_automatic_index = Optional[Int](None) var raised_kwarg_field = Optional[String](None) + alias supported_conversion_flags = ( + String("s"), # __str__ + String("r"), # __repr__ + ) var entries = List[Self]() var start = Optional[Int](None) @@ -2373,10 +2404,13 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): # already one there. if i - start.value() == 1: # python escapes double curlies - var curren_entry = Self( - first_curly=start.value(), last_curly=i, field=False + var current_entry = Self( + first_curly=start.value(), + last_curly=i, + field=False, + conversion_flag="", ) - entries.append(curren_entry^) + entries.append(current_entry^) start = None continue raise ( @@ -2389,27 +2423,68 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): if start: var start_value = start.value() var current_entry = Self( - first_curly=start_value, last_curly=i, field=NoneType() + first_curly=start_value, + last_curly=i, + field=NoneType(), + conversion_flag="", ) + if i - start_value != 1: var field = format_src[start_value + 1 : i] - try: - # field is a number for manual indexing: - var number = int(field) - current_entry.field = number - if number >= len_pos_args or number < 0: - raised_manual_index = number + var exclamation_index = field.find("!") + + # TODO: Future implementation of format specifiers + # When implementing format specifiers, modify this section to handle: + # replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}" + # this will involve: + # 1. finding a colon ':' after the conversion flag (if present) + # 2. extracting the format_spec if a colon is found + # 3. adjusting the field and conversion_flag parsing accordingly + + if exclamation_index != -1: + if exclamation_index + 1 < len(field): + var conversion_flag: String = field[ + exclamation_index + 1 : + ] + if ( + conversion_flag + not in supported_conversion_flags + ): + raise 'Conversion flag "' + conversion_flag + '" not recognised.' + current_entry.conversion_flag = conversion_flag + else: + raise "Empty conversion flag." + + field = field[:exclamation_index] + + if ( + field == "" + ): # an empty field, so it's automatic indexing + if automatic_indexing_count >= len_pos_args: + raised_automatic_index = ( + automatic_indexing_count + ) break - manual_indexing_count += 1 - except e: - debug_assert( - "not convertible to integer" in str(e), - "Not the expected error from atol", - ) - # field is an keyword for **kwargs: - current_entry.field = field - raised_kwarg_field = field - break + automatic_indexing_count += 1 + else: + try: + # field is a number for manual indexing: + var number = int(field) + current_entry.field = number + if number >= len_pos_args or number < 0: + raised_manual_index = number + break + manual_indexing_count += 1 + except e: + debug_assert( + "not convertible to integer" in str(e), + "Not the expected error from atol", + ) + # field is an keyword for **kwargs: + current_entry.field = field + raised_kwarg_field = field + break + else: # automatic indexing # current_entry.field is already None @@ -2424,7 +2499,10 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): if (i + 1) < format_src.byte_length(): if format_src[i + 1] == "}": var curren_entry = Self( - first_curly=i, last_curly=i + 1, field=True + first_curly=i, + last_curly=i + 1, + field=True, + conversion_flag="", ) entries.append(curren_entry^) skip_next = True diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index e9fc0225ef..3516f09735 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -1450,6 +1450,107 @@ def test_format_args(): ) +def test_format_conversion_flags(): + assert_equal(String("{!r}").format(""), "''") + var special_str = "a\nb\tc" + assert_equal( + String("{} {!r}").format(special_str, special_str), + "a\nb\tc 'a\\nb\\tc'", + ) + assert_equal( + String("{!s} {!r}").format(special_str, special_str), + "a\nb\tc 'a\\nb\\tc'", + ) + + var a = "Mojo" + assert_equal(String("{} {!r}").format(a, a), "Mojo 'Mojo'") + assert_equal(String("{!s} {!r}").format(a, a), "Mojo 'Mojo'") + assert_equal(String("{0!s} {0!r}").format(a), "Mojo 'Mojo'") + + var b = 21.1 + assert_true( + "21.100000000000001 SIMD[DType.float64, 1](2" + in String("{} {!r}").format(b, b), + ) + assert_true( + "21.100000000000001 SIMD[DType.float64, 1](2" + in String("{!s} {!r}").format(b, b), + ) + + var c = 1e100 + assert_equal( + String("{} {!r}").format(c, c), + "1e+100 SIMD[DType.float64, 1](1.0000000000000000e+100)", + ) + assert_equal( + String("{!s} {!r}").format(c, c), + "1e+100 SIMD[DType.float64, 1](1.0000000000000000e+100)", + ) + + var d = 42 + assert_equal(String("{} {!r}").format(d, d), "42 42") + assert_equal(String("{!s} {!r}").format(d, d), "42 42") + + assert_true( + "Mojo SIMD[DType.float64, 1](2" + in String("{} {!r} {} {!r}").format(a, b, c, d) + ) + assert_true( + "Mojo SIMD[DType.float64, 1](2" + in String("{!s} {!r} {!s} {!r}").format(a, b, c, d) + ) + + var e = True + assert_equal(String("{} {!r}").format(e, e), "True True") + + assert_true( + "Mojo SIMD[DType.float64, 1](2" + in String("{0} {1!r} {2} {3}").format(a, b, c, d) + ) + assert_true( + "Mojo SIMD[DType.float64, 1](2" + in String("{0!s} {1!r} {2} {3!s}").format(a, b, c, d) + ) + + assert_equal( + String("{3} {2} {1} {0}").format(a, d, c, b), + "21.100000000000001 1e+100 42 Mojo", + ) + + assert_true( + "'Mojo' 42 SIMD[DType.float64, 1](2" + in String("{0!r} {3} {1!r}").format(a, b, c, d) + ) + + assert_equal(String("{0!s} {0!r}").format(a), "Mojo 'Mojo'") + + assert_true( + "True 'Mojo' 42 SIMD[DType.float64, 1](2" + in String("{4} {0!r} {3} {1!r}").format(a, b, c, d, True) + ) + + with assert_raises(contains='Conversion flag "x" not recognised.'): + _ = String("{!x}").format(1) + + with assert_raises(contains="Empty conversion flag."): + _ = String("{!}").format(1) + + with assert_raises(contains='Conversion flag "rs" not recognised.'): + _ = String("{!rs}").format(1) + + with assert_raises(contains='Conversion flag "r123" not recognised.'): + _ = String("{!r123}").format(1) + + with assert_raises(contains='Conversion flag "r!" not recognised.'): + _ = String("{!r!}").format(1) + + with assert_raises(contains='Conversion flag "x" not recognised.'): + _ = String("{0!x}").format(1) + + with assert_raises(contains='Conversion flag "r:d" not recognised.'): + _ = String("{!r:d}").format(1) + + def test_isdigit(): assert_true(isdigit(ord("1"))) assert_false(isdigit(ord("g"))) @@ -1534,6 +1635,7 @@ def main(): test_indexing() test_string_iter() test_format_args() + test_format_conversion_flags() test_isdigit() test_isprintable() test_rjust() From 4273b33f71cd14418506fd94ba6bbc07760ab511 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 23 Jul 2024 19:16:10 -0400 Subject: [PATCH 1291/2019] [stdlib] Remove `DTypePointer` (#43702) MODULAR_ORIG_COMMIT_REV_ID: 93eb0155bd4af30ce002e10ff1b20c76c58b7ff2 --- docs/changelog.md | 10 + docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/pointers.ipynb | 120 +--- examples/notebooks/Mandelbrot.ipynb | 6 +- examples/notebooks/Matmul.ipynb | 8 +- stdlib/src/builtin/simd.mojo | 41 +- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 595 +----------------- stdlib/src/memory/unsafe_pointer.mojo | 10 +- stdlib/src/pathlib/path.mojo | 4 +- .../test/memory/test_maybe_uninitialized.mojo | 3 +- stdlib/test/memory/test_memory.mojo | 44 -- 12 files changed, 39 insertions(+), 806 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index eecbc11c7d..34bd9ca318 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -537,6 +537,16 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. +- `LegacyPointer` and `Pointer` has been removed. Please use `UnsafePointer` + instead. Functions that previously take in a `DTypePointer` now takes an + equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to + `UnsafePointer` is + + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] and + DTypePointer[DType.invalid] -> UnsafePointer[NoneType] + ``` + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index dde64c99bf..91913f32aa 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -167,7 +167,7 @@ "\n", "You should define the `__del__()` method to perform any kind of cleanup the\n", "type requires. Usually, that includes freeing memory for any fields where you\n", - "dynamically allocated memory (for example, via `Pointer` or `DTypePointer`) and\n", + "dynamically allocated memory (for example, via `UnsafePointer`) and\n", "closing any long-lived resources such as file handles.\n", "\n", "However, any struct that is just a simple collection of other types does not\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index e4ac8d8723..18b53a0bd9 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -568,119 +568,6 @@ "```" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `DTypePointer`: handling numeric data\n", - "\n", - "A [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n", - "pointer that supports some additional methods for loading and storing numeric\n", - "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n", - "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n", - "[SIMD and DType](/mojo/manual/types#simd-and-dtype).\n", - "\n", - "`DTypePointer` has a similar API to `UnsafePointer`:\n", - "\n", - "- You can [`alloc()`](/mojo/stdlib/memory/unsafe/DTypePointer#alloc) and\n", - " [`free()`](/mojo/stdlib/memory/unsafe/DTypePointer#free) memory, or use \n", - " [`address_of()`](/mojo/stdlib/memory/unsafe/DTypeUnsafePointer.address_of) to point\n", - " to an existing value.\n", - "- The pointer supports pointer arithmetic to access adjacent memory locations.\n", - "- You can dereference a `DTypePointer` using subscript notation.\n", - "- You can construct a `DTypePointer` from an `Int` address.\n", - "\n", - "\n", - "You can also construct a `DTypePointer` from an `UnsafePointer` of a scalar\n", - "type like `Int64` or `Float32`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import DTypePointer, UnsafePointer\n", - "\n", - "uptr = UnsafePointer[Float64].alloc(10)\n", - "dptr = DTypePointer(uptr)\n", - "# Or:\n", - "dptr = DTypePointer[DType.float64].alloc(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Unlike `UnsafePointer`, `DTypePointer` doesn't have special methods to\n", - "initialize values, destroy them, or move them out. Because all of the values\n", - "that `DTypePointer` works with are trivial types, `DTypePointer` doesn't need to\n", - "destroy values before overwriting them or freeing memory. Instead, you can use\n", - "subscript notation (like `UnsafePointer`) or use the\n", - "[`load()`](/mojo/stdlib/memory/unsafe/DTypePointer#load) and \n", - "[`store()`](/mojo/stdlib/memory/unsafe/DTypePointer#store) methods to access \n", - "values.\n", - "\n", - "What `DTypePointer` adds is various methods of loading and storing SIMD values\n", - "to memory. In particular: strided load/store and gather/scatter.\n", - "\n", - "Strided load loads values from memory into a SIMD vector using an offset (the\n", - "\"stride\") between successive memory addresses. This can be useful for\n", - "extracting rows or columns from tabular data, or for extracting individual\n", - "values from structured data. For example, consider the data for an RGB image,\n", - "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n", - "you want to access just the red values, you can use a strided load or store.\n", - "\n", - "
    \n", - "\n", - " ![](./images/strided-load-storage.png#light)\n", - " ![](./images/strided-load-storage-dark.png#dark)\n", - "\n", - "
    Figure 4. Strided load
    \n", - "
    \n", - "\n", - "The following function uses the \n", - "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_load)\n", - "and \n", - "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n", - "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", - "that this function only handles images where the number of pixels is evenly\n", - "divisible by eight.)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def invert_red_channel(ptr: DTypePointer[DType.uint8], pixel_count: Int):\n", - " # number of values loaded or stored at a time\n", - " alias simd_width = 8\n", - " # bytes per pixel, which is also the stride size\n", - " bpp = 3\n", - " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", - " red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n", - " # Invert values and store them in their original locations\n", - " ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note Future of `DTypePointer`\n", - "\n", - "The `DTypePointer` type exists for historical reasons, but it no longer really\n", - "needs to be a separate type. `UnsafePointer` can handle most things that\n", - "`DTypePointer` does except for a few features related to reading and writing\n", - "`SIMD` values. At some point in the future, these features will probably be\n", - "integrated into the `SIMD` type, so you can use them with `UnsafePointer`.\n", - "\n", - ":::" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -693,14 +580,13 @@ " and free memory, and be aware of when other APIs are allocating or freeing\n", " memory for you.\n", "\n", - "- `UnsafePointer` and `DTypePointer` values are _nullable_—that is, the pointer\n", + "- `UnsafePointer` values are _nullable_—that is, the pointer\n", " is not guaranteed to point to anything. And even when a pointer points to\n", " allocated memory, that memory may not be _initialized_.\n", "\n", "- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`.\n", " When you use an `UnsafePointer`, managing memory and knowing when to destroy\n", - " objects is your responsibility. (Since `DTypePointer` only works with trivial\n", - " types, this is not typically an issue for `DTypePointer`.)\n", + " objects is your responsibility. \n", "\n", "\n", "\n", diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 4cb35b7359..49c2df2d87 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": {}, @@ -98,10 +98,10 @@ "source": [ "@value\n", "struct Matrix[type: DType, rows: Int, cols: Int]:\n", - " var data: DTypePointer[type]\n", + " var data: UnsafePointer[Scalar[type]]\n", "\n", " fn __init__(inout self):\n", - " self.data = DTypePointer[type].alloc(rows * cols)\n", + " self.data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", "\n", " fn __getitem__(self, row: Int, col: Int) -> Scalar[type]:\n", " return Scalar.load(self.data, row * cols + col)\n", diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index b4f1749164..c3ee9ebd29 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -412,21 +412,21 @@ "alias type = DType.float32\n", "\n", "struct Matrix[rows: Int, cols: Int]:\n", - " var data: DTypePointer[type]\n", + " var data: UnsafePointer[Scalar[type]]\n", "\n", " # Initialize zeroeing all values\n", " fn __init__(inout self):\n", - " self.data = DTypePointer[type].alloc(rows * cols)\n", + " self.data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", " memset_zero(self.data.address, rows * cols)\n", "\n", " # Initialize taking a pointer, don't set any elements\n", - " fn __init__(inout self, data: DTypePointer[type]):\n", + " fn __init__(inout self, data: UnsafePointer[Scalar[type]]):\n", " self.data = data\n", "\n", " # Initialize with random values\n", " @staticmethod\n", " fn rand() -> Self:\n", - " var data = DTypePointer[type].alloc(rows * cols)\n", + " var data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", " rand(data.address, rows * cols)\n", " return Self(data)\n", "\n", diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 219b89aaf6..3f18cf35aa 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -31,7 +31,7 @@ from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from builtin.hash import _hash_simd -from memory import bitcast, DTypePointer +from memory import bitcast, UnsafePointer from utils import InlineArray, StringSlice from utils._visualizers import lldb_formatter_wrapping_type @@ -2498,11 +2498,12 @@ struct SIMD[type: DType, size: Int]( fn load[ *, alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_]) -> Self: - """Loads the value the pointer points to. + ](ptr: UnsafePointer[Scalar[type], *_]) -> Self: + """Loads the value the pointer points to with the given offset. Constraints: The width and alignment must be positive integer values. + The offset must be integer. Parameters: alignment: The minimal alignment of the address. @@ -2513,14 +2514,14 @@ struct SIMD[type: DType, size: Int]( Returns: The loaded value. """ - return Self.load[alignment=alignment](ptr, offset=0) + return Self.load[alignment=alignment](ptr, int(0)) @staticmethod @always_inline fn load[ *, alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_], offset: Scalar) -> Self: + ](ptr: UnsafePointer[Scalar[type], *_], offset: Scalar) -> Self: """Loads the value the pointer points to with the given offset. Constraints: @@ -2591,7 +2592,7 @@ struct SIMD[type: DType, size: Int]( fn load[ *, alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_], offset: Int) -> Self: + ](ptr: UnsafePointer[Scalar[type], *_], offset: UInt) -> Self: """Loads the value the pointer points to with the given offset. Constraints: @@ -2608,14 +2609,14 @@ struct SIMD[type: DType, size: Int]( The loaded value. """ - return Self.load[alignment=alignment](ptr.address, offset) + return Self.load[alignment=alignment](ptr, int(offset)) @staticmethod @always_inline fn store[ *, alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_], offset: Int, val: Self): + ](ptr: UnsafePointer[Scalar[type], *_], offset: Int, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2637,7 +2638,7 @@ struct SIMD[type: DType, size: Int]( fn store[ *, alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_], offset: Scalar, val: Self): + ](ptr: UnsafePointer[Scalar[type], *_], offset: Scalar, val: Self): """Stores a single element value at the given offset. Constraints: @@ -2654,26 +2655,6 @@ struct SIMD[type: DType, size: Int]( constrained[offset.type.is_integral(), "offset must be integer"]() Self.store[alignment=alignment](ptr, int(offset), val) - @staticmethod - @always_inline("nodebug") - fn store[ - *, - alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_], val: Self): - """Stores a single element value. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to store to. - val: The value to store. - """ - Self.store[alignment=alignment](ptr.address, val) - @staticmethod @always_inline("nodebug") fn store[ @@ -2705,7 +2686,7 @@ struct SIMD[type: DType, size: Int]( fn store[ *, alignment: Int = Self._default_alignment, - ](ptr: DTypePointer[type, *_], offset: UInt, val: Self): + ](ptr: UnsafePointer[Scalar[type], *_], offset: UInt, val: Self): """Stores a single element value at the given offset. Constraints: diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index b1fbed67d2..f13370de53 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -15,5 +15,5 @@ from .arc import Arc from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation from .reference import AddressSpace, Reference -from .unsafe import DTypePointer, bitcast +from .unsafe import bitcast from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 37afb4b525..93d7e2266b 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -15,19 +15,11 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory import DTypePointer +from memory import bitcast ``` """ - -from sys import alignof, bitwidthof, simdwidthof, sizeof, triple_is_nvidia_cuda -from sys.intrinsics import gather, scatter, strided_load, strided_store - -from bit import is_power_of_two - -from .maybe_uninitialized import UnsafeMaybeUninitialized -from .memory import _free, _malloc -from .reference import AddressSpace +from sys import bitwidthof # ===----------------------------------------------------------------------===# # bitcast @@ -127,586 +119,3 @@ fn bitcast[ return __mlir_op.`pop.bitcast`[ _type = __mlir_type[`!pop.scalar<`, new_type.value, `>`] ](val.value) - - -# ===----------------------------------------------------------------------===# -# DTypePointer -# ===----------------------------------------------------------------------===# - - -@value -@register_passable("trivial") -struct DTypePointer[ - type: DType, - address_space: AddressSpace = AddressSpace.GENERIC, - exclusive: Bool = False, -](Boolable, CollectionElement, Intable, Stringable, EqualityComparable): - """Defines a `DTypePointer` struct that contains an address of the given - dtype. - - Parameters: - type: DType of the underlying data. - address_space: The address space the pointer is in. - exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. - """ - - # Fields - alias element_type = Scalar[type] - alias _pointer_type = UnsafePointer[Scalar[type], address_space, exclusive] - var address: Self._pointer_type - """The pointed-to address.""" - - # ===-------------------------------------------------------------------===# - # Life cycle methods - # ===-------------------------------------------------------------------===# - - @always_inline("nodebug") - fn __init__(inout self): - """Constructs a null `DTypePointer` from the given type.""" - - self.address = Self._pointer_type() - - fn __init__(inout self, *, other: Self): - """Copy the object. - - Args: - other: The value to copy. - """ - self = other - - @always_inline("nodebug") - fn __init__( - inout self, - value: __mlir_type[ - `!kgen.pointer,`, - address_space._value.value, - ` exclusive(`, - exclusive.value, - `)>`, - ], - ): - """Constructs a `DTypePointer` from a scalar pointer of the same type. - - Args: - value: The scalar pointer. - """ - self = UnsafePointer[ - __mlir_type[`!pop.scalar<`, type.value, `>`], address_space - ](value).bitcast[Scalar[type]]() - - @always_inline - fn __init__(inout self, other: DTypePointer[type, address_space, _]): - """Exclusivity parameter cast a pointer. - - Args: - other: Pointer to cast. - """ - self.address = __mlir_op.`pop.pointer.bitcast`[ - _type = Self._pointer_type._mlir_type - ](other.address.address) - - @always_inline("nodebug") - fn __init__( - inout self, value: UnsafePointer[Scalar[type], address_space, exclusive] - ): - """Constructs a `DTypePointer` from a scalar pointer of the same type. - - Args: - value: The scalar pointer. - """ - self.address = value - - # ===------------------------------------------------------------------=== # - # Factory methods - # ===------------------------------------------------------------------=== # - - @staticmethod - @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: Scalar[type]) -> Self: - """Gets the address of the argument. - - Args: - arg: The value to get the address of. - - Returns: - A DTypePointer struct which contains the address of the argument. - """ - return UnsafePointer.address_of(arg) - - @staticmethod - @always_inline - fn alloc(count: Int, /, *, alignment: Int = alignof[type]()) -> Self: - """Heap-allocates a number of element of the specified type using - the specified alignment. - - Args: - count: The number of elements to allocate (note that this is not - the bytecount). - alignment: The alignment used for the allocation. - - Returns: - A new `DTypePointer` object which has been allocated on the heap. - """ - return _malloc[Self.element_type, address_space=address_space]( - count * sizeof[type](), alignment=alignment - ) - - # ===-------------------------------------------------------------------===# - # Operator dunders - # ===-------------------------------------------------------------------===# - - @always_inline("nodebug") - fn __getitem__( - self, offset: Int = 0 - ) -> ref [MutableStaticLifetime, address_space._value.value] Scalar[type]: - """Enable subscript syntax `ptr[]` to access the element. - - Args: - offset: The offset to load from. - - Returns: - The reference for the Mojo compiler to use. - """ - return self.address[offset] - - @always_inline("nodebug") - fn __getitem__( - self, offset: UInt = 0 - ) -> ref [MutableStaticLifetime, address_space._value.value] Scalar[type]: - """Enable subscript syntax `ptr[]` to access the element. - - Args: - offset: The offset to load from. - - Returns: - The reference for the Mojo compiler to use. - """ - return self.__getitem__(Int(offset.value)) - - @always_inline("nodebug") - fn __eq__(self, rhs: Self) -> Bool: - """Returns True if the two pointers are equal. - - Args: - rhs: The value of the other pointer. - - Returns: - True if the two pointers are equal and False otherwise. - """ - return self.address == rhs.address - - @always_inline("nodebug") - fn __ne__(self, rhs: Self) -> Bool: - """Returns True if the two pointers are not equal. - - Args: - rhs: The value of the other pointer. - - Returns: - True if the two pointers are not equal and False otherwise. - """ - return self.address != rhs.address - - @always_inline("nodebug") - fn __lt__(self, rhs: Self) -> Bool: - """Returns True if this pointer represents a lower address than rhs. - - Args: - rhs: The value of the other pointer. - - Returns: - True if this pointer represents a lower address and False otherwise. - """ - return self.address < rhs.address - - # ===------------------------------------------------------------------=== # - # Pointer arithmetic - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn __add__[T: Intable](self, rhs: T) -> Self: - """Returns a new pointer shifted by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - - Returns: - The new DTypePointer shifted by the offset. - """ - return self.offset(rhs) - - @always_inline("nodebug") - fn __sub__[T: Intable](self, rhs: T) -> Self: - """Returns a new pointer shifted back by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - - Returns: - The new DTypePointer shifted by the offset. - """ - return self.offset(-int(rhs)) - - @always_inline("nodebug") - fn __iadd__[T: Intable](inout self, rhs: T): - """Shifts the current pointer by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - """ - self = self + rhs - - @always_inline("nodebug") - fn __isub__[T: Intable](inout self, rhs: T): - """Shifts back the current pointer by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - rhs: The offset. - """ - self = self - rhs - - # ===------------------------------------------------------------------=== # - # Trait implementations - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn __int__(self) -> Int: - """Returns the pointer address as an integer. - - Returns: - The address of the pointer as an Int. - """ - return int(self.address) - - @no_inline - fn __str__(self) -> String: - """Format this pointer as a hexadecimal string. - - Returns: - A String containing the hexadecimal representation of the memory location - destination of this pointer. - """ - return str(self.address) - - @always_inline("nodebug") - fn __bool__(self) -> Bool: - """Checks if the DTypePointer is *null*. - - Returns: - Returns False if the DTypePointer is *null* and True otherwise. - """ - return self.address.__bool__() - - # ===------------------------------------------------------------------=== # - # Methods - # ===------------------------------------------------------------------=== # - - @always_inline - fn free(self): - """Frees the heap allocates memory.""" - _free(self.address) - - # ===------------------------------------------------------------------=== # - # Casting - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn bitcast[ - new_type: DType = type, - /, - address_space: AddressSpace = Self.address_space, - ](self) -> DTypePointer[new_type, address_space]: - """Bitcasts `DTypePointer` to a different dtype. - - Parameters: - new_type: The target dtype. - address_space: The address space of the result. - - Returns: - A new `DTypePointer` object with the specified dtype and the same - address, as the original `DTypePointer`. - """ - return self.address.bitcast[SIMD[new_type, 1], address_space]() - - @always_inline("nodebug") - fn bitcast[ - type: AnyType - ](self) -> UnsafePointer[type, address_space, exclusive]: - """Bitcasts `DTypePointer` to a different scalar type. - - Parameters: - type: The target scalar type. - - Returns: - A new `UnsafePointer` object with the specified type and the same - address, as the original `DTypePointer`. - """ - return self._as_scalar_pointer().bitcast[type]() - - @always_inline("nodebug") - fn _as_scalar_pointer(self) -> UnsafePointer[Scalar[type], address_space]: - """Converts the `DTypePointer` to a scalar pointer of the same dtype. - - Returns: - An `UnsafePointer` to a scalar of the same dtype. - """ - return self.address.address - - alias _default_alignment = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1 - - @always_inline("nodebug") - fn simd_strided_load[ - width: Int, T: Intable - ](self, stride: T) -> SIMD[type, width]: - """Performs a strided load of the SIMD vector. - - Parameters: - width: The SIMD width. - T: The Intable type of the stride. - - Args: - stride: The stride between loads. - - Returns: - A vector which is stride loaded. - """ - return strided_load[type, width]( - self.address, int(stride), SIMD[DType.bool, width](1) - ) - - @always_inline("nodebug") - fn simd_strided_store[ - width: Int, T: Intable - ](self, val: SIMD[type, width], stride: T): - """Performs a strided store of the SIMD vector. - - Parameters: - width: The SIMD width. - T: The Intable type of the stride. - - Args: - val: The SIMD value to store. - stride: The stride between stores. - """ - strided_store(val, self.address, int(stride), True) - - # ===------------------------------------------------------------------=== # - # Gather / Scatter - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn gather[ - *, width: Int = 1, alignment: Int = Self._default_alignment - ](self, offset: SIMD[_, width]) -> SIMD[type, width]: - """Gathers a SIMD vector from offsets of the current pointer. - - This method loads from memory addresses calculated by appropriately - shifting the current pointer according to the `offset` SIMD vector. - - Constraints: - The offset type must be an integral type. - The alignment must be a power of two integer value. - - Parameters: - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The SIMD vector of offsets to gather from. - - Returns: - The SIMD vector containing the gathered values. - """ - var mask = SIMD[DType.bool, width](True) - var default = SIMD[type, width]() - return self.gather[width=width, alignment=alignment]( - offset, mask, default - ) - - @always_inline("nodebug") - fn gather[ - *, width: Int = 1, alignment: Int = Self._default_alignment - ]( - self, - offset: SIMD[_, width], - mask: SIMD[DType.bool, width], - default: SIMD[type, width], - ) -> SIMD[type, width]: - """Gathers a SIMD vector from offsets of the current pointer. - - This method loads from memory addresses calculated by appropriately - shifting the current pointer according to the `offset` SIMD vector, - or takes from the `default` SIMD vector, depending on the values of - the `mask` SIMD vector. - - If a mask element is `True`, the respective result element is given - by the current pointer and the `offset` SIMD vector; otherwise, the - result element is taken from the `default` SIMD vector. - - Constraints: - The offset type must be an integral type. - The alignment must be a power of two integer value. - - Parameters: - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The SIMD vector of offsets to gather from. - mask: The SIMD vector of boolean values, indicating for each - element whether to load from memory or to take from the - `default` SIMD vector. - default: The SIMD vector providing default values to be taken - where the `mask` SIMD vector is `False`. - - Returns: - The SIMD vector containing the gathered values. - """ - constrained[ - offset.type.is_integral(), - "offset type must be an integral type", - ]() - constrained[ - is_power_of_two(alignment), - "alignment must be a power of two integer value", - ]() - - var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) - return gather(base, mask, default, alignment) - - @always_inline("nodebug") - fn scatter[ - *, width: Int = 1, alignment: Int = Self._default_alignment - ](self, offset: SIMD[_, width], val: SIMD[type, width]): - """Scatters a SIMD vector into offsets of the current pointer. - - This method stores at memory addresses calculated by appropriately - shifting the current pointer according to the `offset` SIMD vector. - - If the same offset is targeted multiple times, the values are stored - in the order they appear in the `val` SIMD vector, from the first to - the last element. - - Constraints: - The offset type must be an integral type. - The alignment must be a power of two integer value. - - Parameters: - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The SIMD vector of offsets to scatter into. - val: The SIMD vector containing the values to be scattered. - """ - var mask = SIMD[DType.bool, width](True) - self.scatter[width=width, alignment=alignment](offset, val, mask) - - @always_inline("nodebug") - fn scatter[ - *, width: Int = 1, alignment: Int = Self._default_alignment - ]( - self, - offset: SIMD[_, width], - val: SIMD[type, width], - mask: SIMD[DType.bool, width], - ): - """Scatters a SIMD vector into offsets of the current pointer. - - This method stores at memory addresses calculated by appropriately - shifting the current pointer according to the `offset` SIMD vector, - depending on the values of the `mask` SIMD vector. - - If a mask element is `True`, the respective element in the `val` SIMD - vector is stored at the memory address defined by the current pointer - and the `offset` SIMD vector; otherwise, no action is taken for that - element in `val`. - - If the same offset is targeted multiple times, the values are stored - in the order they appear in the `val` SIMD vector, from the first to - the last element. - - Constraints: - The offset type must be an integral type. - The alignment must be a power of two integer value. - - Parameters: - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The SIMD vector of offsets to scatter into. - val: The SIMD vector containing the values to be scattered. - mask: The SIMD vector of boolean values, indicating for each - element whether to store at memory or not. - """ - constrained[ - offset.type.is_integral(), - "offset type must be an integral type", - ]() - constrained[ - is_power_of_two(alignment), - "alignment must be a power of two integer value", - ]() - - var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) - scatter(val, base, mask, alignment) - - @always_inline - fn is_aligned[alignment: Int](self) -> Bool: - """Checks if the pointer is aligned. - - Parameters: - alignment: The minimal desired alignment. - - Returns: - `True` if the pointer is at least `alignment`-aligned or `False` - otherwise. - """ - constrained[ - is_power_of_two(alignment), "alignment must be a power of 2." - ]() - return int(self) % alignment == 0 - - @always_inline("nodebug") - fn offset[T: Intable](self, idx: T) -> Self: - """Returns a new pointer shifted by the specified offset. - - Parameters: - T: The Intable type of the offset. - - Args: - idx: The offset of the new pointer. - - Returns: - The new constructed DTypePointer. - """ - return self.address.offset(int(idx)) - - @always_inline("nodebug") - fn offset(self, idx: UInt) -> Self: - """Returns a new pointer shifted by the specified offset. - - Args: - idx: The offset of the new pointer. - - Returns: - The new constructed DTypePointer. - """ - return self.address.offset(idx.value) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 50bdb6db7c..b2ca7b188b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -149,14 +149,6 @@ struct UnsafePointer[ """ return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) - @staticmethod - fn _from_dtype_ptr[ - dtype: DType, - ](ptr: DTypePointer[dtype, address_space]) -> UnsafePointer[ - Scalar[dtype], address_space - ]: - return ptr.address.address - @staticmethod @always_inline fn alloc(count: Int, alignment: Int = alignof[T]()) -> Self: @@ -232,7 +224,7 @@ struct UnsafePointer[ idx: The offset of the new pointer. Returns: - The new constructed DTypePointer. + The new constructed UnsafePointer. """ return __mlir_op.`pop.offset`(self.address, idx.value) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 30117da11b..e01844f8f6 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -33,9 +33,7 @@ fn cwd() raises -> Path: The current directory. """ alias MAX_CWD_BUFFER_SIZE = 1024 - var buf0 = stack_allocation[MAX_CWD_BUFFER_SIZE, C_char]() - - var buf = UnsafePointer[C_char]._from_dtype_ptr(buf0) + var buf = stack_allocation[MAX_CWD_BUFFER_SIZE, C_char]() var res = external_call["getcwd", UnsafePointer[C_char]]( buf, Int(MAX_CWD_BUFFER_SIZE) diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index 9e287d49a6..04347f1cfc 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -12,7 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from memory.unsafe import UnsafeMaybeUninitialized +from memory.maybe_uninitialized import UnsafeMaybeUninitialized + from test_utils import CopyCounter, MoveCounter, ValueDestructorRecorder from testing import assert_equal diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index c9b39efeb2..02d92f24e7 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -469,49 +469,6 @@ def test_dtypepointer_scatter(): ptr.free() -def test_memcpy_unsafe_pointer(): - # Tests memcpy for the UnsafePointer type - # Note: - # Eventually as DTypePointer is fully replaced with - # UnsafePointer, this test will be redundant as all the other tests in - # this file will have been updated to use `UnsafePointer`. - - var list_a = List[Int8](capacity=10) - var list_b = List[Int8](1, 2, 3, 4, 5) - - assert_equal(len(list_b), 5) - - var dest_ptr: UnsafePointer[Int8] = list_a.unsafe_ptr() - - memcpy( - dest=dest_ptr, - src=list_b.unsafe_ptr(), - count=len(list_b), - ) - memcpy( - dest=dest_ptr + 5, - src=list_b.unsafe_ptr(), - count=len(list_b), - ) - - _ = list_b^ - - # Mark the initialized size of list_a. - list_a.size = 10 - - assert_equal(len(list_a), 10) - assert_equal(list_a[0], 1) - assert_equal(list_a[1], 2) - assert_equal(list_a[2], 3) - assert_equal(list_a[3], 4) - assert_equal(list_a[4], 5) - assert_equal(list_a[5], 1) - assert_equal(list_a[6], 2) - assert_equal(list_a[7], 3) - assert_equal(list_a[8], 4) - assert_equal(list_a[9], 5) - - def test_indexing(): var ptr = UnsafePointer[Float32].alloc(4) for i in range(4): @@ -529,7 +486,6 @@ def main(): test_memcmp_overflow() test_memcmp_simd() test_memcmp_extensive() - test_memcpy_unsafe_pointer() test_memset() test_pointer_explicit_copy() From 3d03a7d2f4eb4651e1c5ca61b8a575d45e8cacdf Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 24 Jul 2024 00:43:03 -0400 Subject: [PATCH 1292/2019] [stdlib] Use `Span` in `sort` module. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using unsafe `UnsafePointer` and simplifies code. The benchmarks were ran on AWS `c6i.metal`, with the following script been ran: ``` #!/bin/bash sudo cpupower frequency-set --governor performance sudo sh -c "mkdir -p /sys/devices/system/cpu/intel_pstate" sudo sh -c "echo 1 > $ /sys/devices/system/cpu/intel_pstate/no_turbo" sudo turbostat stress -c 2 -t 10 for cpunum in $(seq 1 127); do echo 0 >/sys/devices/system/cpu/cpu$cpunum/online done echo 0 | sudo tee /proc/sys/******/randomize_va_space echo -1 | sudo tee /proc/sys/******/perf_event_paranoid ``` Directly using `Span.__getitem__` results up to 30% regression in large lists. When switching to using `Span._data.__getitem__` and manually slicing the `Span` this regression is gone. There's consistent improvement in `heap_sort`: ``` $ grep heap sort_with_span_vs_sort_without_span.csv heap_sort_random_4096_uint8 , 0.209698, 0.223651,0.94 ✅, 563, 532,1.06 ✅ heap_sort_random_65536_uint8 , 4.272739, 4.746973,0.90 ✅, 28, 25,1.12 ✅ heap_sort_random_4096_int8 , 0.205083, 0.220615,0.93 ✅, 595, 540,1.10 ✅ heap_sort_random_65536_int8 , 4.189158, 4.533758,0.92 ✅, 28, 26,1.08 ✅ heap_sort_random_4096_uint16 , 0.220983, 0.229957,0.96 ✅, 542, 521,1.04 ✅ heap_sort_random_65536_uint16 , 5.049737, 5.468732,0.92 ✅, 20, 20,1.00 ✅ heap_sort_random_4096_int16 , 0.217032, 0.228879,0.95 ✅, 551, 522,1.06 ✅ heap_sort_random_65536_int16 , 5.065190, 5.413011,0.94 ✅, 20, 20,1.00 ✅ heap_sort_random_4096_float16 , 0.340672, 0.348372,0.98 ✅, 350, 344,1.02 ✅ heap_sort_random_65536_float16 , 7.623814, 8.157023,0.93 ✅, 15, 14,1.07 ✅ heap_sort_random_4096_uint32 , 0.217621, 0.226995,0.96 ✅, 541, 527,1.03 ✅ heap_sort_random_65536_uint32 , 5.102467, 5.449968,0.94 ✅, 20, 20,1.00 ✅ heap_sort_random_4096_int32 , 0.221531, 0.230219,0.96 ✅, 541, 520,1.04 ✅ heap_sort_random_65536_int32 , 5.074338, 5.530628,0.92 ✅, 20, 20,1.00 ✅ heap_sort_random_4096_float32 , 0.233772, 0.255295,0.92 ✅, 513, 467,1.10 ✅ heap_sort_random_65536_float32 , 5.587119, 6.042747,0.92 ✅, 20, 19,1.05 ✅ heap_sort_random_4096_uint64 , 0.218686, 0.228828,0.96 ✅, 539, 521,1.03 ✅ heap_sort_random_65536_uint64 , 5.228370, 5.586136,0.94 ✅, 20, 20,1.00 ✅ heap_sort_random_4096_int64 , 0.214599, 0.224689,0.96 ✅, 552, 532,1.04 ✅ heap_sort_random_65536_int64 , 5.185541, 5.539784,0.94 ✅, 20, 20,1.00 ✅ heap_sort_random_4096_float64 , 0.242243, 0.249143,0.97 ✅, 491, 476,1.03 ✅ heap_sort_random_65536_float64 , 5.720028, 6.078389,0.94 ✅, 20, 19,1.05 ✅ heap_sort_low_card_4096_delta_0 , 0.010978, 0.011352,0.97 ✅, 11076, 10574,1.05 ✅ heap_sort_low_card_65536_delta_0 , 0.173349, 0.178188,0.97 ✅, 685, 673,1.02 ✅ heap_sort_low_card_4096_delta_2 , 0.116305, 0.105093,1.11 ❌, 1006, 1149,0.88 ❌ heap_sort_low_card_65536_delta_2 , 2.554418, 2.344059,1.09 ❌, 47, 51,0.92 ❌ heap_sort_low_card_4096_delta_5 , 0.140329, 0.150708,0.93 ✅, 847, 797,1.06 ✅ heap_sort_low_card_65536_delta_5 , 2.944623, 3.061078,0.96 ✅, 41, 39,1.05 ✅ heap_sort_low_card_4096_delta_20 , 0.192652, 0.201301,0.96 ✅, 625, 583,1.07 ✅ heap_sort_low_card_65536_delta_20 , 3.431197, 3.887155,0.88 ✅, 34, 31,1.10 ✅ heap_sort_low_card_4096_delta_100 , 0.199931, 0.219439,0.91 ✅, 594, 531,1.12 ✅ heap_sort_low_card_65536_delta_100, 4.029965, 4.526370,0.89 ✅, 29, 26,1.12 ✅ ``` However, there appears to be some regression in `insertion_sort`: ``` ins_sort_random_10_uint8 , 0.000206, 0.000198,1.04 ❌, 580974, 605070,0.96 ❌ ins_sort_random_20_uint8 , 0.000264, 0.000242,1.09 ❌, 456220, 493137,0.93 ❌ ins_sort_random_32_uint8 , 0.000378, 0.000343,1.10 ❌, 317382, 351555,0.90 ❌ ins_sort_random_64_uint8 , 0.000838, 0.000666,1.26 ❌, 142852, 166319,0.86 ❌ ins_sort_random_100_uint8 , 0.001762, 0.001518,1.16 ❌, 67971, 83957,0.81 ❌ ins_sort_random_10_int8 , 0.000204, 0.000200,1.02 ❌, 589072, 598968,0.98 ❌ ins_sort_random_20_int8 , 0.000257, 0.000242,1.06 ❌, 466562, 495962,0.94 ❌ ins_sort_random_32_int8 , 0.000338, 0.000321,1.05 ❌, 354203, 373488,0.95 ❌ ins_sort_random_64_int8 , 0.000691, 0.000689,1.00 ❌, 173591, 175171,0.99 ❌ ins_sort_random_100_int8 , 0.002289, 0.001464,1.56 ❌, 52474, 79375,0.66 ❌ ins_sort_random_10_uint16 , 0.000197, 0.000193,1.02 ❌, 607885, 618647,0.98 ❌ ins_sort_random_20_uint16 , 0.000257, 0.000246,1.04 ❌, 454880, 489710,0.93 ❌ ins_sort_random_32_uint16 , 0.000383, 0.000336,1.14 ❌, 313437, 358597,0.87 ❌ ins_sort_random_64_uint16 , 0.000902, 0.000685,1.32 ❌, 133054, 175401,0.76 ❌ ins_sort_random_100_uint16 , 0.001565, 0.001195,1.31 ❌, 76796, 96522,0.80 ❌ ins_sort_random_10_int16 , 0.000199, 0.000216,0.92 ✅, 600510, 600383,1.00 ✅ ins_sort_random_20_int16 , 0.000245, 0.000256,0.96 ✅, 484505, 469459,1.03 ✅ ins_sort_random_32_int16 , 0.000346, 0.000378,0.92 ✅, 347201, 317431,1.09 ✅ ins_sort_random_64_int16 , 0.000680, 0.000865,0.79 ✅, 176424, 138461,1.27 ✅ ins_sort_random_100_int16 , 0.001402, 0.001868,0.75 ✅, 85077, 64269,1.32 ✅ ins_sort_random_10_float16 , 0.000237, 0.000221,1.07 ❌, 505751, 544184,0.93 ❌ ins_sort_random_20_float16 , 0.000392, 0.000371,1.06 ❌, 305977, 324105,0.94 ❌ ins_sort_random_32_float16 , 0.000583, 0.000541,1.08 ❌, 200000, 200000,1.00 ✅ ins_sort_random_64_float16 , 0.001598, 0.001597,1.00 ❌, 75106, 75256,1.00 ❌ ins_sort_random_100_float16 , 0.003750, 0.003739,1.00 ❌, 31955, 31854,1.00 ✅ ins_sort_random_10_uint32 , 0.000206, 0.000201,1.02 ❌, 584560, 594678,0.98 ❌ ins_sort_random_20_uint32 , 0.000262, 0.000249,1.05 ❌, 456844, 483272,0.95 ❌ ins_sort_random_32_uint32 , 0.000364, 0.000322,1.13 ❌, 329426, 371968,0.89 ❌ ins_sort_random_64_uint32 , 0.000768, 0.000619,1.24 ❌, 156253, 193522,0.81 ❌ ins_sort_random_100_uint32 , 0.001786, 0.001482,1.21 ❌, 65946, 86863,0.76 ❌ ins_sort_random_10_int32 , 0.000198, 0.000196,1.01 ❌, 606790, 611880,0.99 ❌ ins_sort_random_20_int32 , 0.000251, 0.000244,1.03 ❌, 477394, 491899,0.97 ❌ ins_sort_random_32_int32 , 0.000370, 0.000333,1.11 ❌, 323406, 361444,0.89 ❌ ins_sort_random_64_int32 , 0.000915, 0.000708,1.29 ❌, 127892, 169117,0.76 ❌ ins_sort_random_100_int32 , 0.001654, 0.001317,1.26 ❌, 72381, 90613,0.80 ❌ ins_sort_random_10_float32 , 0.000205, 0.000201,1.02 ❌, 582931, 596366,0.98 ❌ ins_sort_random_20_float32 , 0.000255, 0.000241,1.06 ❌, 470887, 498336,0.94 ❌ ins_sort_random_32_float32 , 0.000352, 0.000322,1.09 ❌, 340515, 369304,0.92 ❌ ins_sort_random_64_float32 , 0.000920, 0.000825,1.12 ❌, 129948, 150200,0.87 ❌ ins_sort_random_100_float32 , 0.001803, 0.001476,1.22 ❌, 65242, 81268,0.80 ❌ ins_sort_random_10_uint64 , 0.000211, 0.000200,1.05 ❌, 569040, 602800,0.94 ❌ ins_sort_random_20_uint64 , 0.000275, 0.000250,1.10 ❌, 434685, 477642,0.91 ❌ ins_sort_random_32_uint64 , 0.000335, 0.000296,1.13 ❌, 358302, 388926,0.92 ❌ ins_sort_random_64_uint64 , 0.000923, 0.000667,1.38 ❌, 126881, 179727,0.71 ❌ ins_sort_random_100_uint64 , 0.001920, 0.001450,1.32 ❌, 62201, 82367,0.76 ❌ ins_sort_random_10_int64 , 0.000206, 0.000210,0.98 ✅, 583397, 569220,1.02 ✅ ins_sort_random_20_int64 , 0.000255, 0.000273,0.93 ✅, 472415, 439146,1.08 ✅ ins_sort_random_32_int64 , 0.000299, 0.000331,0.90 ✅, 398429, 363542,1.10 ✅ ins_sort_random_64_int64 , 0.000884, 0.000901,0.98 ✅, 162706, 132185,1.23 ✅ ins_sort_random_100_int64 , 0.001396, 0.001879,0.74 ✅, 85657, 62754,1.36 ✅ ins_sort_random_10_float64 , 0.000207, 0.000202,1.02 ❌, 580119, 592469,0.98 ❌ ins_sort_random_20_float64 , 0.000281, 0.000269,1.04 ❌, 427655, 445451,0.96 ❌ ins_sort_random_32_float64 , 0.000346, 0.000323,1.07 ❌, 345742, 372103,0.93 ❌ ins_sort_random_64_float64 , 0.000803, 0.000778,1.03 ❌, 149039, 155137,0.96 ❌ ins_sort_random_100_float64 , 0.001998, 0.001932,1.03 ❌, 59354, 62436,0.95 ❌ ``` MSTDL-761 MODULAR_ORIG_COMMIT_REV_ID: df8fa4a07b794f6ac581ead7a43f27d1694492f3 --- stdlib/benchmarks/builtin/bench_sort.mojo | 10 +- stdlib/src/builtin/sort.mojo | 274 ++++++++---------- stdlib/src/collections/counter.mojo | 2 +- stdlib/test/builtin/test_sort.mojo | 12 +- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- 5 files changed, 127 insertions(+), 173 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index ebe3c58c9c..3c269f6c42 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -62,18 +62,18 @@ fn random_scalar_list[ @always_inline -fn insertion_sort[type: DType](list: List[Scalar[type]]): +fn insertion_sort[type: DType](inout list: List[Scalar[type]]): @parameter fn _less_than( lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] ) -> Bool: return lhs.data < rhs.data - _insertion_sort[Scalar[type], _less_than](list.data, len(list)) + _insertion_sort[_less_than](list) @always_inline -fn small_sort[size: Int, type: DType](list: List[Scalar[type]]): +fn small_sort[size: Int, type: DType](inout list: List[Scalar[type]]): @parameter fn _less_than( lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] @@ -84,14 +84,14 @@ fn small_sort[size: Int, type: DType](list: List[Scalar[type]]): @always_inline -fn heap_sort[type: DType](list: List[Scalar[type]]): +fn heap_sort[type: DType](inout list: List[Scalar[type]]): @parameter fn _less_than( lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] ) -> Bool: return lhs.data < rhs.data - _heap_sort[Scalar[type], _less_than](list.data, len(list)) + _heap_sort[_less_than](list) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 97ce890c34..5cfbce4d5e 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -37,9 +37,12 @@ struct _SortWrapper[type: CollectionElement](CollectionElement): @always_inline fn _insertion_sort[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int): +](span: Span[type, lifetime]): """Sort the array[start:end] slice""" + var array = span.unsafe_ptr() + var size = len(span) for i in range(1, size): var value = array[i] @@ -59,8 +62,12 @@ fn _insertion_sort[ @always_inline fn _quicksort_partition_right[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int) -> Int: +](span: Span[type, lifetime]) -> Int: + var array = span.unsafe_ptr() + var size = len(span) + var left = 1 var right = size - 1 var pivot_value = array[0] @@ -84,8 +91,12 @@ fn _quicksort_partition_right[ @always_inline fn _quicksort_partition_left[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int) -> Int: +](span: Span[type, lifetime]) -> Int: + var array = span.unsafe_ptr() + var size = len(span) + var left = 1 var right = size - 1 var pivot_value = array[0] @@ -106,8 +117,11 @@ fn _quicksort_partition_left[ fn _heap_sort_fix_down[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int, idx: Int): +](span: Span[type, lifetime], idx: Int): + var array = span.unsafe_ptr() + var size = len(span) var i = idx var j = i * 2 + 1 while j < size: # has left child @@ -124,16 +138,19 @@ fn _heap_sort_fix_down[ @always_inline fn _heap_sort[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], owned size: Int): +](span: Span[type, lifetime]): + var array = span.unsafe_ptr() + var size = len(span) # heapify for i in range(size // 2 - 1, -1, -1): - _heap_sort_fix_down[type, cmp_fn](array, size, i) + _heap_sort_fix_down[cmp_fn](span, i) # sort while size > 1: size -= 1 swap(array[0], array[size]) - _heap_sort_fix_down[type, cmp_fn](array, size, 0) + _heap_sort_fix_down[cmp_fn](span, 0) @always_inline @@ -148,8 +165,11 @@ fn _estimate_initial_height(size: Int) -> Int: @always_inline fn _delegate_small_sort[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int): +](span: Span[type, lifetime]): + var array = span.unsafe_ptr() + var size = len(span) if size == 2: _small_sort[2, type, cmp_fn](array) @@ -170,8 +190,11 @@ fn _delegate_small_sort[ @always_inline fn _quicksort[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int): +](span: Span[type, lifetime]): + var array = span.unsafe_ptr() + var size = len(span) if size == 0: return var stack = List[Int](capacity=_estimate_initial_height(size)) @@ -184,11 +207,15 @@ fn _quicksort[ var len = end - start if len <= 5: - _delegate_small_sort[type, cmp_fn](array + start, len) + _delegate_small_sort[cmp_fn]( + Span[type, lifetime](unsafe_ptr=array + start, len=len) + ) continue if len < 32: - _insertion_sort[type, cmp_fn](array + start, len) + _insertion_sort[cmp_fn]( + Span[type, lifetime](unsafe_ptr=array + start, len=len) + ) continue # pick median of 3 as pivot @@ -198,16 +225,16 @@ fn _quicksort[ # be the same, so no need to recurse that interval # already have array[start - 1] <= array[start] if start > 0 and not cmp_fn(array[start - 1], array[start]): - var pivot = start + _quicksort_partition_left[type, cmp_fn]( - array + start, len + var pivot = start + _quicksort_partition_left[cmp_fn]( + Span[type, lifetime](unsafe_ptr=array + start, len=len) ) if end > pivot + 2: stack.append(pivot + 1) stack.append(end) continue - var pivot = start + _quicksort_partition_right[type, cmp_fn]( - array + start, len + var pivot = start + _quicksort_partition_right[cmp_fn]( + Span[type, lifetime](unsafe_ptr=array + start, len=len) ) if end > pivot + 2: @@ -227,11 +254,14 @@ fn _quicksort[ @always_inline fn _partition[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], size: Int) -> Int: - if size == 0: - return size +](span: Span[type, lifetime]) -> Int: + var size = len(span) + if size <= 1: + return 0 + var array = span.unsafe_ptr() var pivot = size // 2 var pivot_value = array[pivot] @@ -257,89 +287,89 @@ fn _partition[ fn _partition[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](array: UnsafePointer[type], k: Int, size: Int): - var stack = List[Int](capacity=_estimate_initial_height(size)) - stack.append(0) - stack.append(size) - while len(stack) > 0: - var end = stack.pop() - var start = stack.pop() - var pivot = start + _partition[type, cmp_fn](array + start, end - start) +](owned span: Span[type, lifetime], owned k: Int): + while True: + var pivot = _partition[cmp_fn](span) if pivot == k: - break + return elif k < pivot: - stack.append(start) - stack.append(pivot) + span._len = pivot + span = span[:pivot] else: - stack.append(pivot + 1) - stack.append(end) + span._data += pivot + 1 + span._len -= pivot + 1 + k -= pivot + 1 fn partition[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (type, type) capturing -> Bool, -](array: UnsafePointer[type], k: Int, size: Int): +](span: Span[type, lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: type: Type of the underlying data. + lifetime: Lifetime of span. cmp_fn: Comparison functor of (type, type) capturing -> Bool type. Args: - array: Input buffer. + span: Input buffer. k: Index of the partition element. - size: The length of the buffer. """ @parameter fn _cmp_fn(lhs: _SortWrapper[type], rhs: _SortWrapper[type]) -> Bool: return cmp_fn(lhs.data, rhs.data) - _partition[type, _cmp_fn](array, k, size) + _partition[_cmp_fn](span, k) fn partition[ + lifetime: MutableLifetime, //, cmp_fn: fn (Int, Int) capturing -> Bool, -](array: UnsafePointer[Int], k: Int, size: Int): +](span: Span[Int, lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: + lifetime: Lifetime of span. cmp_fn: Comparison functor of (type, type) capturing -> Bool type. Args: - array: Input buffer. + span: Input buffer. k: Index of the partition element. - size: The length of the buffer. """ @parameter fn _cmp_fn(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: return cmp_fn(lhs.data, rhs.data) - _partition[Int, _cmp_fn](array, k, size) + _partition[_cmp_fn](span, k) fn partition[ type: DType, + lifetime: MutableLifetime, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, -](array: UnsafePointer[Scalar[type]], k: Int, size: Int): +](span: Span[Scalar[type], lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: type: DType of the underlying data. + lifetime: Lifetime of span. cmp_fn: Comparison functor of (type, type) capturing -> Bool type. Args: - array: Input buffer. + span: Input buffer. k: Index of the partition element. - size: The length of the buffer. """ @parameter @@ -348,7 +378,7 @@ fn partition[ ) -> Bool: return cmp_fn(lhs.data, rhs.data) - _partition[Scalar[type], _cmp_fn](array, k, size) + _partition[_cmp_fn](span, k) # ===----------------------------------------------------------------------===# @@ -359,17 +389,18 @@ fn partition[ # Junction from public to private API fn _sort[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](ptr: UnsafePointer[type], len: Int): - if len <= 5: - _delegate_small_sort[type, cmp_fn](ptr, len) +](span: Span[type, lifetime]): + if len(span) <= 5: + _delegate_small_sort[cmp_fn](span) return - if len < 32: - _insertion_sort[type, cmp_fn](ptr, len) + if len(span) < 32: + _insertion_sort[cmp_fn](span) return - _quicksort[type, cmp_fn](ptr, len) + _quicksort[cmp_fn](span) # TODO (MSTDL-766): The Int and Scalar[type] overload should be remove @@ -378,64 +409,65 @@ fn _sort[ # optional cmp_fn. fn sort[ type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (type, type) capturing -> Bool, -](ptr: UnsafePointer[type], len: Int): +](span: Span[type, lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: type: CollectionElement type of the underlying data. + lifetime: Lifetime of span. cmp_fn: The comparison function. Args: - ptr: Pointer to the start of the memory to be sorted. - len: Number of elements from ptr that to be sorted. + span: The span to be sorted. """ @parameter fn _cmp_fn(lhs: _SortWrapper[type], rhs: _SortWrapper[type]) -> Bool: return cmp_fn(lhs.data, rhs.data) - _sort[type, _cmp_fn](ptr, len) + _sort[_cmp_fn](span) fn sort[ - type: CollectionElement, + lifetime: MutableLifetime, //, cmp_fn: fn (Int, Int) capturing -> Bool, -](ptr: UnsafePointer[Int], len: Int): +](span: Span[Int, lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: - type: CollectionElement type of the underlying data. + lifetime: Lifetime of span. cmp_fn: The comparison function. Args: - ptr: Pointer to the start of the memory to be sorted. - len: Number of elements from ptr that to be sorted. + span: The span to be sorted. """ @parameter fn _cmp_fn(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: return cmp_fn(lhs.data, rhs.data) - _sort[Int, _cmp_fn](ptr, len) + _sort[_cmp_fn](span) fn sort[ type: DType, + lifetime: MutableLifetime, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, -](ptr: UnsafePointer[Scalar[type]], len: Int): +](span: Span[Scalar[type], lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: - type: CollectionElement type of the underlying data. + type: DType type of the underlying data. + lifetime: Lifetime of span. cmp_fn: The comparison function. Args: - ptr: Pointer to the start of the memory to be sorted. - len: Number of elements from ptr that to be sorted. + span: The span to be sorted. """ @parameter @@ -444,148 +476,70 @@ fn sort[ ) -> Bool: return cmp_fn(lhs.data, rhs.data) - _sort[Scalar[type], _cmp_fn](ptr, len) - - -fn sort(ptr: UnsafePointer[Int], len: Int): - """Sort the list inplace. - The function doesn't return anything, the list is updated inplace. - - Args: - ptr: Pointer to the start of the memory to be sorted. - len: Number of elements from ptr that to be sorted. - """ - - @parameter - fn _cmp_fn(lhs: Int, rhs: Int) -> Bool: - return lhs < rhs - - sort[Int, _cmp_fn](ptr, len) + _sort[_cmp_fn](span) fn sort[ - type: DType, -](ptr: UnsafePointer[Scalar[type]], len: Int): + lifetime: MutableLifetime, //, +](span: Span[Int, lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: - type: CollectionElement type of the underlying data. + lifetime: Lifetime of span. Args: - ptr: Pointer to the start of the memory to be sorted. - len: Number of elements from ptr that to be sorted. + span: The span to be sorted. """ @parameter - fn _cmp_fn(lhs: Scalar[type], rhs: Scalar[type]) -> Bool: + fn _cmp_fn(lhs: Int, rhs: Int) -> Bool: return lhs < rhs - sort[type, _cmp_fn](ptr, len) - - -fn sort[ - type: CollectionElement, - cmp_fn: fn (Int, Int) capturing -> Bool, -](inout list: List[Int]): - """Sort the list inplace. - The function doesn't return anything, the list is updated inplace. - - Parameters: - type: CollectionElement type of the underlying data. - cmp_fn: The comparison function. - - Args: - list: Input list to sort. - """ - - sort[Int, cmp_fn](list.data, len(list)) + sort[_cmp_fn](span) fn sort[ type: DType, - cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, -](inout list: List[Scalar[type]]): - """Sort the list inplace. - The function doesn't return anything, the list is updated inplace. - - Parameters: - type: DType of the underlying data. - cmp_fn: The comparison function. - - Args: - list: Input list to sort. - """ - - sort[type, cmp_fn](list.data, len(list)) - - -fn sort[ - type: CollectionElement, - cmp_fn: fn (type, type) capturing -> Bool, -](inout list: List[type]): + lifetime: MutableLifetime, //, +](span: Span[Scalar[type], lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: type: CollectionElement type of the underlying data. - cmp_fn: The comparison function. - - Args: - list: Input list to sort. - """ - - sort[type, cmp_fn](list.data, len(list)) - - -fn sort(inout list: List[Int]): - """Sort the list inplace. - The function doesn't return anything, the list is updated inplace. + lifetime: Lifetime of span. Args: - list: Input integer list to sort. - """ - - @parameter - fn _cmp_fn(lhs: Int, rhs: Int) -> Bool: - return lhs < rhs - - sort[Int, _cmp_fn](list.data, len(list)) - - -fn sort[type: DType](inout list: List[Scalar[type]]): - """Sort the list inplace. - The function doesn't return anything, the list is updated inplace. - - Parameters: - type: DType of the underlying data. - - Args: - list: Input vector to sort. + span: The span to be sorted. """ @parameter fn _cmp_fn(lhs: Scalar[type], rhs: Scalar[type]) -> Bool: return lhs < rhs - sort[type, _cmp_fn](list.data, len(list)) + sort[_cmp_fn](span) -fn sort[type: ComparableCollectionElement](inout list: List[type]): +fn sort[ + type: ComparableCollectionElement, + lifetime: MutableLifetime, //, +](span: Span[type, lifetime]): """Sort list of the order comparable elements in-place. Parameters: type: The order comparable collection element type. + lifetime: Lifetime of span. Args: - list: The list of the scalars which will be sorted in-place. + span: The span to be sorted. """ @parameter fn _cmp_fn(a: type, b: type) -> Bool: return a < b - sort[type, _cmp_fn](list.data, len(list)) + sort[_cmp_fn](span) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 01eb9f62bb..cb27ae22f5 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -489,7 +489,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): fn comparator(a: CountTuple[V], b: CountTuple[V]) -> Bool: return a < b - sort[CountTuple[V], comparator](items) + sort[comparator](items) return items[:n] fn elements(self) -> List[V]: diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 892999abf3..b3f457e2a4 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -174,7 +174,7 @@ fn test_sort3_dupe_elements() raises: list.append(3) list.append(3) - _quicksort[Int, cmp_fn](list.data, len(list)) + _quicksort[cmp_fn](list) var expected = List[Int](3, 3, 5) for i in range(length): @@ -327,7 +327,7 @@ fn test_quick_sort_repeated_val() raises: ) -> Bool: return lhs.data > rhs.data - _quicksort[Float32, _greater_than](list.data, len(list)) + _quicksort[_greater_than](list) var expected = List[Float32]( 9.0, @@ -414,7 +414,7 @@ fn test_quick_sort_repeated_val() raises: 9.0, 9.0, ) - _quicksort[Float32, _less_than](list.data, len(list)) + _quicksort[_less_than](list) for i in range(0, length): assert_equal(expected[i], list[i]) @@ -429,7 +429,7 @@ fn test_partition_top_k(length: Int, k: Int) raises: fn _great_than(lhs: Float32, rhs: Float32) -> Bool: return lhs > rhs - _ = partition[DType.float32, _great_than](list.data, k, len(list)) + _ = partition[_great_than](list, k) for i in range(0, k): assert_false(list[i] < length - k) @@ -450,7 +450,7 @@ fn test_sort_stress() raises: for _ in range(length): list.append(int(random_si64(-length, length))) - _quicksort[Int, cmp_fn](list.data, len(list)) + _quicksort[cmp_fn](list) for i in range(length - 1): assert_true(check_fn(list[i], list[i + 1])) @@ -500,7 +500,7 @@ fn test_sort_custom() raises: fn compare_fn(lhs: MyStruct, rhs: MyStruct) -> Bool: return lhs.val < rhs.val - sort[MyStruct, compare_fn](list) + sort[compare_fn](list) for i in range(1, length): assert_false(list[i - 1].val > list[i].val) diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index 2f935cb42e..a5c8719773 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -19,7 +19,7 @@ from random import rand fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = UnsafePointer[SIMD[D, 1]].alloc(size) rand[D](p.address, size) - sort[D](p, size) + sort(Span[Scalar[D], MutableStaticLifetime](unsafe_ptr=p, len=size)) for i in range(1, size - 1): if p[i] < p[i - 1]: print(name, "size:", size, "max:", max, "incorrect sort") From 823a9727cbc083f89bb4b17e2d6cf6c127ce72a9 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 24 Jul 2024 04:43:05 -0400 Subject: [PATCH 1293/2019] [stdlib][******] Remove redundant `.address` (#43850) MODULAR_ORIG_COMMIT_REV_ID: 6bd97a5b6e00308671c1521ddd7eec5ec7a2e9a9 --- examples/matmul.mojo | 6 +++--- examples/reduce.mojo | 4 ++-- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 8 ++++---- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 92bdb5e936..bfb1fc584d 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -53,7 +53,7 @@ struct Matrix[rows: Int, cols: Int]: # Initialize zeroing all values fn __init__(inout self): self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) - memset_zero(self.data.address, rows * cols) + memset_zero(self.data, rows * cols) # Initialize taking a pointer, don't set any elements fn __init__(inout self, data: UnsafePointer[Scalar[type]]): @@ -63,7 +63,7 @@ struct Matrix[rows: Int, cols: Int]: @staticmethod fn rand() -> Self: var data = UnsafePointer[Scalar[type]].alloc(rows * cols) - rand(data.address, rows * cols) + rand(data, rows * cols) return Self(data) fn __getitem__(self, y: Int, x: Int) -> Scalar[type]: @@ -246,7 +246,7 @@ fn matmul_reordered(inout C: Matrix, A: Matrix, B: Matrix): var accumulator = Matrix[tile_m, tile_n]( stack_allocation[tile_m * tile_n, type]() ) - memset_zero(accumulator.data.address, tile_m * tile_n) + memset_zero(accumulator.data, tile_m * tile_n) for ko in range(0, A.cols, tile_k): diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 9667c13b9b..6b39fad01c 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -83,8 +83,8 @@ fn main() raises: var ptr_small = UnsafePointer[Scalar[type]].alloc(size_small) var ptr_large = UnsafePointer[Scalar[type]].alloc(size_large) - rand(ptr_small.address, size_small) - rand(ptr_large.address, size_large) + rand(ptr_small, size_small) + rand(ptr_large, size_large) var buffer_small = Buffer[type, size_small](ptr_small) var buffer_large = Buffer[type, size_large](ptr_large) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 0e32e8ae83..cf89c69816 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -266,7 +266,7 @@ fn bench_compare(): var p1 = UnsafePointer[Scalar[type]].alloc(size) var p2 = UnsafePointer[Scalar[type]].alloc(size) print("Benchmark results") - rand(p1.address, size) + rand(p1, size) @parameter fn arg_size(): @@ -322,15 +322,15 @@ fn bench_compare(): var arg = run[arg_size](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) - memset_zero(p2.address, size) + memset_zero(p2, size) var param = run[param_size](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) - memset_zero(p2.address, size) + memset_zero(p2, size) var arg_unroll = run[arg_size_unroll](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) - memset_zero(p2.address, size) + memset_zero(p2, size) var param_unroll = run[param_size_unroll](max_runtime_secs=0.5).mean(unit) print(SIMD[size=size].load(p2)) diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index a5c8719773..bea13f7d8c 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -18,7 +18,7 @@ from random import rand fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = UnsafePointer[SIMD[D, 1]].alloc(size) - rand[D](p.address, size) + rand[D](p, size) sort(Span[Scalar[D], MutableStaticLifetime](unsafe_ptr=p, len=size)) for i in range(1, size - 1): if p[i] < p[i - 1]: From b78ed47ce43deb5ace84183c8c3ff9afd04262ea Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 24 Jul 2024 10:18:36 -0700 Subject: [PATCH 1294/2019] [External] [stdlib] Move `find()` from `StringRef` to `StringSlice` (#43931) We are just moving the StringRef implementation of `find`. We'll delete `StringRef.find()` later on. The base implementation is here: https://github.com/modularml/mojo/blob/nightly/stdlib/src/utils/stringref.mojo#L497 `String.find()` is now plugged to `StringSlice.find()` In general we are trying to move away from `StringRef` and at some point maybe delete it in favor of `StringSlice` which is safer, both in terms of memory and in terms of utf-8 validity. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3228 MODULAR_ORIG_COMMIT_REV_ID: 973e4efceee95f5a35b47aff1ec8bf3f5a4e9e21 --- stdlib/src/builtin/string.mojo | 4 +- stdlib/src/utils/string_slice.mojo | 74 ++++++++++++++++++++++++ stdlib/src/utils/stringref.mojo | 2 + stdlib/test/utils/test_string_slice.mojo | 33 +++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index adf369e9ff..0aeb35b963 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1473,9 +1473,7 @@ struct String( The offset of `substr` relative to the beginning of the string. """ - return self._strref_dangerous().find( - substr._strref_dangerous(), start=start - ) + return self.as_string_slice().find(substr.as_string_slice(), start) fn rfind(self, substr: String, start: Int = 0) -> Int: """Finds the offset of the last occurrence of `substr` starting at diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index f188cc43ea..56d49f65d6 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -623,6 +623,80 @@ struct StringSlice[ """ pass + fn _from_start(self, start: Int) -> Self: + """Gets the `StringSlice` pointing to the substring after the specified slice start position. + + If start is negative, it is interpreted as the number of characters + from the end of the string to start at. + + Args: + start: Starting index of the slice. + + Returns: + A `StringSlice` borrowed from the current string containing the + characters of the slice starting at start. + """ + + var self_len = len(self) + + var abs_start: Int + if start < 0: + # Avoid out of bounds earlier than the start + # len = 5, start = -3, then abs_start == 2, i.e. a partial string + # len = 5, start = -10, then abs_start == 0, i.e. the full string + abs_start = max(self_len + start, 0) + else: + # Avoid out of bounds past the end + # len = 5, start = 2, then abs_start == 2, i.e. a partial string + # len = 5, start = 8, then abs_start == 5, i.e. an empty string + abs_start = min(start, self_len) + + debug_assert( + abs_start >= 0, "strref absolute start must be non-negative" + ) + debug_assert( + abs_start <= self_len, + "strref absolute start must be less than source String len", + ) + + # TODO: We assumes the StringSlice only has ASCII. + # When we support utf-8 slicing, we should drop self._slice[abs_start:] + # and use something smarter. + return StringSlice(unsafe_from_utf8=self._slice[abs_start:]) + + fn find(self, substr: StringSlice, start: Int = 0) -> Int: + """Finds the offset of the first occurrence of `substr` starting at + `start`. If not found, returns -1. + + Args: + substr: The substring to find. + start: The offset from which to find. + + Returns: + The offset of `substr` relative to the beginning of the string. + """ + if not substr: + return 0 + + if len(self) < len(substr) + start: + return -1 + + # The substring to search within, offset from the beginning if `start` + # is positive, and offset from the end if `start` is negative. + var haystack_str = self._from_start(start) + + var loc = stringref._memmem( + haystack_str.unsafe_ptr(), + len(haystack_str), + substr.unsafe_ptr(), + len(substr), + ) + + if not loc: + return -1 + + return int(loc) - int(self.unsafe_ptr()) + fn isspace(self) -> Bool: """Determines whether every character in the given StringSlice is a python whitespace String. This corresponds to Python's diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 13becfa999..5be8102e07 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -461,6 +461,8 @@ struct StringRef( return res + # TODO: remove this method later on when nothing depends on it anymore. + # It has already been copied to `StringSlice`. fn find(self, substr: StringRef, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at `start`. If not found, returns -1. diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 7e38a784d3..ff6db549c4 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -259,6 +259,38 @@ fn test_utf8_validation() raises: assert_false(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) +def test_find(): + haystack = str("abcdefg").as_string_slice() + haystack_with_special_chars = str("abcdefg@#$").as_string_slice() + haystack_repeated_chars = str("aaaaaaaaaaaaaaaaaaaaaaaa").as_string_slice() + + assert_equal(haystack.find(str("a").as_string_slice()), 0) + assert_equal(haystack.find(str("ab").as_string_slice()), 0) + assert_equal(haystack.find(str("abc").as_string_slice()), 0) + assert_equal(haystack.find(str("bcd").as_string_slice()), 1) + assert_equal(haystack.find(str("de").as_string_slice()), 3) + assert_equal(haystack.find(str("fg").as_string_slice()), 5) + assert_equal(haystack.find(str("g").as_string_slice()), 6) + assert_equal(haystack.find(str("z").as_string_slice()), -1) + assert_equal(haystack.find(str("zzz").as_string_slice()), -1) + + assert_equal(haystack.find(str("@#$").as_string_slice()), -1) + assert_equal( + haystack_with_special_chars.find(str("@#$").as_string_slice()), 7 + ) + + assert_equal(haystack_repeated_chars.find(str("aaa").as_string_slice()), 0) + assert_equal(haystack_repeated_chars.find(str("AAa").as_string_slice()), -1) + + assert_equal( + haystack.find(str("hijklmnopqrstuvwxyz").as_string_slice()), -1 + ) + + assert_equal( + str("").as_string_slice().find(str("abc").as_string_slice()), -1 + ) + + fn main() raises: test_string_literal_byte_slice() test_string_byte_slice() @@ -267,3 +299,4 @@ fn main() raises: test_slice_eq() test_slice_bool() test_utf8_validation() + test_find() From fba87f937415cf9026584b0582bd84edd2a24d80 Mon Sep 17 00:00:00 2001 From: Ian Tramble <117689425+itramble@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:39:04 -0700 Subject: [PATCH 1295/2019] Add debug_assert to DLHandle._get_function checking for null handle. MODULAR_ORIG_COMMIT_REV_ID: 4659384802d0d67395f18e476bb0d41dd090da52 --- stdlib/src/sys/ffi.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 24281fd398..21174d5897 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -164,6 +164,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): Returns: A handle to the function. """ + debug_assert(self.handle, "Dylib handle is null") @parameter if not os_is_windows(): From e6056fabedf7f7f0ef63631a1f0370c2f8124c8c Mon Sep 17 00:00:00 2001 From: Ian Tramble <117689425+itramble@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:33:49 -0700 Subject: [PATCH 1296/2019] Add debug_assert taking function parameter to validate side-effecting expressions. MODULAR_ORIG_COMMIT_REV_ID: eb700f2eeb9bff9ed785d33c3234d72aa7e2fd16 --- stdlib/src/builtin/debug_assert.mojo | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 750a678a91..0a757b8324 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -31,6 +31,34 @@ alias _ERROR_ON_ASSERT = is_kernels_debug_build() or is_defined[ alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() +@always_inline +fn debug_assert[ + func: fn () capturing -> Bool, stringable: Stringable +](message: stringable): + """Asserts that the condition is true. + + The `debug_assert` is similar to `assert` in C++. It is a no-op in release + builds unless MOJO_ENABLE_ASSERTIONS is defined. + + Right now, users of the mojo-sdk must explicitly specify `-D MOJO_ENABLE_ASSERTIONS` + to enable assertions. It is not sufficient to compile programs with `-debug-level full` + for enabling assertions in the library. + + Parameters: + func: The function to invoke to check if the assertion holds. Can be used + if the function is side-effecting, in which case a debug_assert taking + a Bool will evaluate the expression producing the Bool even in release mode. + stringable: The type of the message. + + Args: + message: The message to convert to `String` before displaying it on failure. + """ + + @parameter + if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: + debug_assert(func(), message) + + @always_inline fn debug_assert[stringable: Stringable](cond: Bool, message: stringable): """Asserts that the condition is true. From 6edcdb8a467574253a599e81649435c28e7b2ca4 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Wed, 24 Jul 2024 16:30:45 -0700 Subject: [PATCH 1297/2019] [External] [stdlib] Remove duplicate test from PR #3279 (#43985) [External] [stdlib] Remove duplicate test from PR https://github.com/modularml/mojo/pull/3279 Also on line `1468`. Co-authored-by: Joshua James Venter Closes modularml/mojo#3302 MODULAR_ORIG_COMMIT_REV_ID: 88a7c8b72c6688b55a481f7135a783446b2815a3 --- stdlib/test/builtin/test_string.mojo | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 3516f09735..6341bce169 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -1522,8 +1522,6 @@ def test_format_conversion_flags(): in String("{0!r} {3} {1!r}").format(a, b, c, d) ) - assert_equal(String("{0!s} {0!r}").format(a), "Mojo 'Mojo'") - assert_true( "True 'Mojo' 42 SIMD[DType.float64, 1](2" in String("{4} {0!r} {3} {1!r}").format(a, b, c, d, True) From b112e6acf40465c6a494a8f3e1635ea7042ba3b6 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 24 Jul 2024 22:10:45 -0700 Subject: [PATCH 1298/2019] ### [mojo] Rename VariantTakeOp back to VariantGetOp (NFC) MODULAR_ORIG_COMMIT_REV_ID: 683aa1a5c436cc5c235cfdbe9d5b041d259c8738 --- stdlib/src/collections/optional.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 34cc718fe9..81f43b5153 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -505,7 +505,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): Returns: The contained value. """ - return __mlir_op.`kgen.variant.take`[index = Int(0).value](self._value) + return __mlir_op.`kgen.variant.get`[index = Int(0).value](self._value) fn or_else(self, default: T) -> T: """Return the underlying value contained in the Optional or a default From b6ac01e105ef0a0982b9d59301a3ea4ee0c13efe Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 25 Jul 2024 07:47:36 -0700 Subject: [PATCH 1299/2019] [Stdlib] Add _int_type_of_width private method This complements the _uint_type_of_width function MODULAR_ORIG_COMMIT_REV_ID: d84d9480bb4e082df3648c01f66c7793e3099192 --- stdlib/src/builtin/dtype.mojo | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 9fc0a9bdb5..6e9d8eb0a9 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -567,6 +567,24 @@ fn _scientific_notation_digits[type: DType]() -> StringLiteral: return "16" +# ===-------------------------------------------------------------------===# +# _int_type_of_width +# ===-------------------------------------------------------------------===# + + +fn _int_type_of_width[width: Int]() -> DType: + @parameter + if width == 8: + return DType.int8 + elif width == 16: + return DType.int16 + elif width == 32: + return DType.int32 + else: + constrained[width == 64]() + return DType.int64 + + # ===-------------------------------------------------------------------===# # _uint_type_of_width # ===-------------------------------------------------------------------===# From d6384baf759a93846be5a52d1421cd399e776263 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 25 Jul 2024 07:52:00 -0700 Subject: [PATCH 1300/2019] ### [Stdlib] Refactor the atomic cmpexch implementation, NFC This refactors the code to avoid code duplication. MODULAR_ORIG_COMMIT_REV_ID: 4943bae3811829a71b0130916cca9db58509aad4 --- stdlib/src/os/atomic.mojo | 88 +++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 26b2b0b99e..e373b5e9da 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -178,52 +178,23 @@ struct Atomic[type: DType]: @parameter if type.is_integral(): - var value_addr = UnsafePointer.address_of(self.value.value) - var cmpxchg_res = __mlir_op.`pop.atomic.cmpxchg`[ - bin_op = __mlir_attr.`#pop`, - failure_ordering = __mlir_attr.`#pop`, - success_ordering = __mlir_attr.`#pop`, - ]( - value_addr.address, - expected.value, - desired.value, + return _compare_exchange_weak_integral_impl( + UnsafePointer.address_of(self.value), expected, desired ) - var ok = Bool( - __mlir_op.`kgen.struct.extract`[index = __mlir_attr.`1:index`]( - cmpxchg_res - ) - ) - if not ok: - expected = self.load() - return ok # For the floating point case, we need to bitcast the floating point # values to their integral representation and perform the atomic # operation on that. alias integral_type = _integral_type_of[type]() - var value_integral_addr = UnsafePointer.address_of( - self.value.value - ).bitcast[__mlir_type[`!pop.scalar<`, integral_type.value, `>`]]() + var value_integral_addr = UnsafePointer.address_of(self.value).bitcast[ + Scalar[integral_type] + ]() var expected_integral = bitcast[integral_type](expected) var desired_integral = bitcast[integral_type](desired) - - var cmpxchg_res = __mlir_op.`pop.atomic.cmpxchg`[ - failure_ordering = __mlir_attr.`#pop`, - success_ordering = __mlir_attr.`#pop`, - ]( - value_integral_addr.address, - expected_integral.value, - desired_integral.value, + return _compare_exchange_weak_integral_impl( + value_integral_addr, expected_integral, desired_integral ) - var ok = Bool( - __mlir_op.`kgen.struct.extract`[index = __mlir_attr.`1:index`]( - cmpxchg_res - ) - ) - if not ok: - expected = self.load() - return ok @always_inline fn max(inout self, rhs: Scalar[type]): @@ -240,10 +211,7 @@ struct Atomic[type: DType]: Args: rhs: Value to max. """ - constrained[ - type.is_integral() or type.is_floating_point(), - "the input type must be arithmetic", - ]() + constrained[type.is_numeric(), "the input type must be arithmetic"]() var value_addr = UnsafePointer.address_of(self.value.value) _ = __mlir_op.`pop.atomic.rmw`[ @@ -268,10 +236,7 @@ struct Atomic[type: DType]: rhs: Value to min. """ - constrained[ - type.is_integral() or type.is_floating_point(), - "the input type must be arithmetic", - ]() + constrained[type.is_numeric(), "the input type must be arithmetic"]() var value_addr = UnsafePointer.address_of(self.value.value) _ = __mlir_op.`pop.atomic.rmw`[ @@ -279,3 +244,38 @@ struct Atomic[type: DType]: ordering = __mlir_attr.`#pop`, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ](value_addr.address, rhs.value) + + +# ===----------------------------------------------------------------------===# +# Utilities +# ===----------------------------------------------------------------------===# + + +@always_inline +fn _compare_exchange_weak_integral_impl[ + type: DType, // +]( + value_addr: UnsafePointer[Scalar[type], *_], + inout expected: Scalar[type], + desired: Scalar[type], +) -> Bool: + constrained[type.is_integral(), "the input type must be integral"]() + var cmpxchg_res = __mlir_op.`pop.atomic.cmpxchg`[ + bin_op = __mlir_attr.`#pop`, + failure_ordering = __mlir_attr.`#pop`, + success_ordering = __mlir_attr.`#pop`, + ]( + value_addr.bitcast[ + __mlir_type[`!pop.scalar<`, type.value, `>`] + ]().address, + expected.value, + desired.value, + ) + var ok = Bool( + __mlir_op.`kgen.struct.extract`[index = __mlir_attr.`1:index`]( + cmpxchg_res + ) + ) + if not ok: + expected = value_addr[] + return ok From 972f9a74234d91cc18e7f5d71caea61f3ee23f5f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 25 Jul 2024 08:26:11 -0700 Subject: [PATCH 1301/2019] ### [******][GPU] Extend atomic operations to use raw pointers MODULAR_ORIG_COMMIT_REV_ID: fe8ca03089e64d97ddbe6869610b7a96f2a1e937 --- stdlib/src/os/atomic.mojo | 61 ++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index e373b5e9da..46e99b9201 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -196,6 +196,34 @@ struct Atomic[type: DType]: value_integral_addr, expected_integral, desired_integral ) + @staticmethod + @always_inline + fn max(ptr: UnsafePointer[Scalar[type]], rhs: Scalar[type]): + """Performs atomic in-place max on the pointer. + + Atomically replaces the current value pointer to by `ptr` by the result + of max of the value and arg. The operation is a read-modify-write + operation. The operation is a read-modify-write operation perform + according to sequential consistency semantics. + + Constraints: + The input type must be either integral or floating-point type. + + Args: + ptr: The source pointer. + rhs: Value to max. + """ + constrained[type.is_numeric(), "the input type must be arithmetic"]() + + var value_addr = ptr.bitcast[ + __mlir_type[`!pop.scalar<`, type.value, `>`] + ]() + _ = __mlir_op.`pop.atomic.rmw`[ + bin_op = __mlir_attr.`#pop`, + ordering = __mlir_attr.`#pop`, + _type = __mlir_type[`!pop.scalar<`, type.value, `>`], + ](value_addr.address, rhs.value) + @always_inline fn max(inout self, rhs: Scalar[type]): """Performs atomic in-place max. @@ -213,9 +241,31 @@ struct Atomic[type: DType]: """ constrained[type.is_numeric(), "the input type must be arithmetic"]() - var value_addr = UnsafePointer.address_of(self.value.value) + Self.max(UnsafePointer.address_of(self.value), rhs) + + @staticmethod + @always_inline + fn min(ptr: UnsafePointer[Scalar[type]], rhs: Scalar[type]): + """Performs atomic in-place min on the pointer. + + Atomically replaces the current value pointer to by `ptr` by the result + of min of the value and arg. The operation is a read-modify-write + operation. The operation is a read-modify-write operation perform + according to sequential consistency semantics. + + Constraints: + The input type must be either integral or floating-point type. + + Args: + ptr: The source pointer. + rhs: Value to min. + """ + + var value_addr = ptr.bitcast[ + __mlir_type[`!pop.scalar<`, type.value, `>`] + ]() _ = __mlir_op.`pop.atomic.rmw`[ - bin_op = __mlir_attr.`#pop`, + bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ](value_addr.address, rhs.value) @@ -238,12 +288,7 @@ struct Atomic[type: DType]: constrained[type.is_numeric(), "the input type must be arithmetic"]() - var value_addr = UnsafePointer.address_of(self.value.value) - _ = __mlir_op.`pop.atomic.rmw`[ - bin_op = __mlir_attr.`#pop`, - ordering = __mlir_attr.`#pop`, - _type = __mlir_type[`!pop.scalar<`, type.value, `>`], - ](value_addr.address, rhs.value) + Self.min(UnsafePointer.address_of(self.value), rhs) # ===----------------------------------------------------------------------===# From a6847c461471a4b2ab7291c0f3d90d17663db057 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:56:41 -0400 Subject: [PATCH 1302/2019] [Docs] Changelog for `DTypePointer` removal. MODULAR_ORIG_COMMIT_REV_ID: 5bab8ecf43554f3997512d52797fbaa843dbaaab --- docs/changelog.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 34bd9ca318..3a0805a5e6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -537,16 +537,36 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. -- `LegacyPointer` and `Pointer` has been removed. Please use `UnsafePointer` - instead. Functions that previously take in a `DTypePointer` now takes an +- `DTypePointer` has been removed. + Please use [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) + instead. Functions that previously took a `DTypePointer` now take an equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to - `UnsafePointer` is + `UnsafePointer` is: + + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] + ``` + + There could be places that you have code of the form ```mojo - DTypePointer[type] -> UnsafePointer[Scalar[type]] and - DTypePointer[DType.invalid] -> UnsafePointer[NoneType] + fn f(ptr: DTypePointer): ``` + which is equivalent to `DTypePointer[*_]`. In this case you would have to add + a type parameter to the function: + + ```mojo + fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): + ``` + + because we can’t have an unbound struct inside the parameter. + + There could also be places where you use `DTypePointer[Scalar[DType.invalid/index]]`, + and it would be natural to change it to `UnsafePointer[NoneType/Int]`. But + since they are not an `UnsafePointer` that stores a `Scalar`, you might have to + `rebind/bitcast` to appropriate types. + ### ❌ Removed - It is no longer possible to cast (implicitly or explicitly) from `Reference` From f81b194e1e5c1b8b173e145cfb7b5cda60d0e6ec Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 25 Jul 2024 09:30:46 -0700 Subject: [PATCH 1303/2019] [Stdlib] Lift address_space constraints on low-level atomics MODULAR_ORIG_COMMIT_REV_ID: ed63405dc8919cc616acae59a60445e0decf5378 --- stdlib/src/os/atomic.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 46e99b9201..eb2908511d 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -60,7 +60,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline fn _fetch_add( - ptr: UnsafePointer[Scalar[type]], rhs: Scalar[type] + ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type] ) -> Scalar[type]: """Performs atomic in-place add. @@ -245,7 +245,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline - fn min(ptr: UnsafePointer[Scalar[type]], rhs: Scalar[type]): + fn min(ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): """Performs atomic in-place min on the pointer. Atomically replaces the current value pointer to by `ptr` by the result From 131f87fbfe2714ad78e83194761f33e613bb5306 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 25 Jul 2024 13:25:43 -0700 Subject: [PATCH 1304/2019] update Python min version to 3.9 MODULAR_ORIG_COMMIT_REV_ID: 5eca475cbdf174100e3632d35d01ac9b0422af8c --- docs/manual/get-started.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 36e41a6bc9..3a2d5f1c15 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -25,7 +25,7 @@ If you already installed Mojo, see [how to update](#update-mojo). - Apple silicon (M1 or M2 processor) - macOS Ventura (12) or later -- Python 3.8 - 3.11 +- Python 3.9 - 3.11 - Xcode or Xcode Command Line Tools - [Homebrew](https://brew.sh) @@ -37,7 +37,7 @@ If you already installed Mojo, see [how to update](#update-mojo). newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) or AWS Graviton2/3 CPU - Minimum 8 GiB RAM -- Python 3.8 - 3.11 +- Python 3.9 - 3.11 - g++ or clang++ C++ compiler From 3938df7861466fcc9a4c08039e95c4c445aaca8e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 26 Jul 2024 16:47:54 -0700 Subject: [PATCH 1305/2019] [mojo-stdlib] Optimize the implementation of Variant Exit early in the parameter loops when we know only 1 if condition can fire, instead of relying on LLVM to figure this out. Also, ensure specific functions are called in the parameter domain to ensure we don't accidentally codegen them. In the future, we should mark them as consteval or whatever the feature will be called. MODULAR_ORIG_COMMIT_REV_ID: c00ab62339b59a41bff2c2d85772fe5bcca39930 --- stdlib/src/utils/variant.mojo | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 5c832fd985..da077980ab 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -133,7 +133,8 @@ struct Variant[*Ts: CollectionElement]( value: The value to initialize the variant with. """ self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] - self._get_state() = Self._check[T]() + alias idx = Self._check[T]() + self._get_state() = idx self._get_ptr[T]().init_pointee_move(value^) fn __init__(inout self, *, other: Self): @@ -150,6 +151,7 @@ struct Variant[*Ts: CollectionElement]( alias T = Ts[i] if self._get_state() == i: self._get_ptr[T]().init_pointee_move(other._get_ptr[T]()[]) + return fn __copyinit__(inout self, other: Self): """Creates a deep copy of an existing variant. @@ -176,6 +178,7 @@ struct Variant[*Ts: CollectionElement]( if self._get_state() == i: # Calls the correct __moveinit__ other._get_ptr[T]().move_pointee_into(self._get_ptr[T]()) + return fn __del__(owned self): """Destroy the variant.""" @@ -220,7 +223,8 @@ struct Variant[*Ts: CollectionElement]( fn _get_state(ref [_]self: Self) -> ref [__lifetime_of(self)] Int8: var int8_self = UnsafePointer.address_of(self).bitcast[Int8]() - return (int8_self + Self._size())[] + alias size = Self._size() + return (int8_self + size)[] @always_inline fn _call_correct_deleter(inout self): @@ -228,6 +232,7 @@ struct Variant[*Ts: CollectionElement]( for i in range(len(VariadicList(Ts))): if self._get_state() == i: self._get_ptr[Ts[i]]().destroy_pointee() + return @always_inline fn take[T: CollectionElement](inout self) -> T: @@ -378,13 +383,11 @@ struct Variant[*Ts: CollectionElement]( @staticmethod fn _check[T: CollectionElement]() -> Int8: - var result = -1 - @parameter for i in range(len(VariadicList(Ts))): if _type_is_eq[Ts[i], T](): - result = i - return result + return i + return -1 @staticmethod fn _size() -> Int: @@ -392,5 +395,6 @@ struct Variant[*Ts: CollectionElement]( @parameter for i in range(len(VariadicList(Ts))): - size = max(size, _align_up(sizeof[Ts[i]](), alignof[Ts[i]]())) + alias element_size = _align_up(sizeof[Ts[i]](), alignof[Ts[i]]()) + size = max(size, element_size) return _align_up(size, alignof[Int]()) From d1073c62946db034d5da0e71d6162b4720458497 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 26 Jul 2024 18:52:39 -0700 Subject: [PATCH 1306/2019] [mojo-stdlib] Make `Variant.(unsafe_)replace` take the value as `owned` This function is always making a copy of the value to replace, even though `set` takes the value as `owned`. Make the argument `owned` here as well to move the responsibility of the copy up the caller. MODULAR_ORIG_COMMIT_REV_ID: 4b7a0ee2c96fea10af73eaf6e19ecba404fb557f --- stdlib/src/utils/variant.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index da077980ab..f37e42b993 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -280,7 +280,7 @@ struct Variant[*Ts: CollectionElement]( @always_inline fn replace[ Tin: CollectionElement, Tout: CollectionElement - ](inout self, value: Tin) -> Tout: + ](inout self, owned value: Tin) -> Tout: """Replace the current value of the variant with the provided type. The caller takes ownership of the underlying value. @@ -302,12 +302,12 @@ struct Variant[*Ts: CollectionElement]( if not self.isa[Tout](): abort("taking out the wrong type!") - return self.unsafe_replace[Tin, Tout](value) + return self.unsafe_replace[Tin, Tout](value^) @always_inline fn unsafe_replace[ Tin: CollectionElement, Tout: CollectionElement - ](inout self, value: Tin) -> Tout: + ](inout self, owned value: Tin) -> Tout: """Unsafely replace the current value of the variant with the provided type. The caller takes ownership of the underlying value. @@ -330,7 +330,7 @@ struct Variant[*Ts: CollectionElement]( debug_assert(self.isa[Tout](), "taking out the wrong type!") var x = self.unsafe_take[Tout]() - self.set[Tin](value) + self.set[Tin](value^) return x^ fn set[T: CollectionElement](inout self, owned value: T): From 017306f8e56766e875c69330e17c9fad00937e45 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Sat, 27 Jul 2024 01:44:28 -0700 Subject: [PATCH 1307/2019] [External] [stdlib] Remove `_div_ceil_positive` in favour of `math.ceildiv` (#44204) [External] [stdlib] Remove `_div_ceil_positive` in favour of `math.ceildiv` Addresses the `TODO` from this [commit](https://github.com/modularml/mojo/commit/c97d7c9bfd3c607e4c20f560d7b105c5d8f04c13). Co-authored-by: Joshua James Venter Closes modularml/mojo#3303 MODULAR_ORIG_COMMIT_REV_ID: f901dd676884cc4888355c440be348d532dd88df --- stdlib/src/builtin/range.mojo | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 913514530d..fa87b798ee 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -29,27 +29,6 @@ from utils._select import _select_register_value as select # ===----------------------------------------------------------------------=== # -# TODO: use math.ceildiv when open sourced. -@always_inline -fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: - """Divides an integer by another integer, and round up to the nearest - integer. - - Constraints: - Will raise an exception if denominator is zero. - Assumes that both inputs are positive. - - Args: - numerator: The numerator. - denominator: The denominator. - - Returns: - The ceiling of numerator divided by denominator. - """ - debug_assert(denominator != 0, "divide by zero") - return (numerator + denominator - 1)._positive_div(denominator) - - @always_inline("nodebug") fn _sign(x: Int) -> Int: var result = 0 @@ -188,9 +167,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): # If the start is after the end and step is positive then we # are generating an empty range. In this case divide 0/1 to # return 0 without a branch. - return _div_ceil_positive( - select(cnd, 0, numerator), select(cnd, 1, denominator) - ) + return ceildiv(select(cnd, 0, numerator), select(cnd, 1, denominator)) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: From 055b7489ba113d991efaf01711eb71c2044694b7 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Sat, 27 Jul 2024 04:40:47 -0700 Subject: [PATCH 1308/2019] [External] [stdlib] Extend tests for `SIMD.__pow__` (#44205) [External] [stdlib] Extend tests for `SIMD.__pow__` Partially addresses Issue #2867 Co-authored-by: Joshua James Venter Closes modularml/mojo#3315 MODULAR_ORIG_COMMIT_REV_ID: 581daf8c0c3a8823e3bc7460e04a3d770b8924c2 --- stdlib/test/builtin/test_simd.mojo | 147 ++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 12 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 083914b71f..df45b35c87 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1257,26 +1257,149 @@ def test_reduce_bit_count(): def test_pow(): + alias nan = FloatLiteral.nan + alias neg_zero = FloatLiteral.negative_zero alias inf = FloatLiteral.infinity - alias F = SIMD[DType.float32, 4] + alias neg_inf = FloatLiteral.negative_infinity - var simd_val = F(0, 1, 2, 3) + # Float32 tests + alias F32x4 = SIMD[DType.float32, 4] + alias F32x8 = SIMD[DType.float32, 8] - assert_equal(simd_val.__pow__(2.0), F(0.0, 1.0, 4.0, 9.0)) - assert_equal(simd_val.__pow__(2), F(0.0, 1.0, 4.0, 9.0)) - assert_equal(simd_val.__pow__(3), F(0.0, 1.0, 8.0, 27.0)) - assert_equal(simd_val.__pow__(-1), F(inf, 1.0, 0.5, 0.3333333432674408)) + var f32x4_val = F32x4(0, 1, 2, 3) + var f32x8_val = F32x8(0, 1, 2, 3, 4, 5, 6, 7) + assert_equal(f32x4_val.__pow__(10.0), F32x4(0.0, 1.0, 1024.0, 59049.0)) + assert_almost_equal( + f32x8_val.__pow__(15.0), + F32x8( + 0.0, + 1.0, + 32768.0, + 14348907.0, + 1073741824.0, + 30517578125.0, + 470184984576.0, + 4747561509943.0, + ), + ) + assert_almost_equal( + f32x4_val.__pow__(-1.0), F32x4(inf, 1.0, 0.5, 0.333333333) + ) + assert_equal(f32x4_val.__pow__(0.0), F32x4(1.0, 1.0, 1.0, 1.0)) + assert_equal(F32x4(1, 1, 1, 1).__pow__(100.0), F32x4(1.0, 1.0, 1.0, 1.0)) + assert_equal( + F32x4(inf, -inf, nan, 1).__pow__(3.0), F32x4(inf, -inf, nan, 1.0) + ) + assert_almost_equal( + f32x4_val.__pow__(0.5), F32x4(0.0, 1.0, 1.414213562, 1.732050808) + ) - assert_almost_equal(simd_val.__pow__(0.5), F(0.0, 1.0, 1.41421, 1.73205)) assert_almost_equal( - (simd_val + 2).__pow__(-0.5), F(0.70710, 0.57735, 0.5, 0.44721) + F32x4(1, 2, 3, 4).__pow__(F32x4(2, 3, 2, 1)), F32x4(1.0, 8.0, 9.0, 4.0) ) - alias I = SIMD[DType.int32, 4] - var simd_val_int = I(0, 1, 2, 3) + var f32x4_neg_zero = F32x4(neg_zero, neg_zero, neg_zero, neg_zero) + assert_equal( + f32x4_neg_zero.__pow__(F32x4(2.0, 3.0, 1.0, 4.0)), + F32x4(0.0, 0.0, 0.0, 0.0), + ) + assert_equal( + f32x4_neg_zero.__pow__(3.0), + F32x4(neg_zero, neg_zero, neg_zero, neg_zero), + ) + + assert_almost_equal( + F32x4(neg_zero, 1.0, 2.0, 3.0).__pow__(F32x4(2.0, 4.0, 8.0, 16.0)), + F32x4(0.0, 1.0, 256.0, 43046721.0), + ) + + assert_equal( + F32x4(2.0, 3.0, 4.0, 5.0).__pow__(neg_zero), F32x4(1.0, 1.0, 1.0, 1.0) + ) + + assert_equal( + F32x4(inf, neg_inf, nan, 1.0).__pow__(F32x4(2.0, 3.0, 2.0, 0.0)), + F32x4(inf, neg_inf, nan, 1.0), + ) + + assert_equal( + F32x4(neg_inf, neg_inf, neg_inf, neg_inf).__pow__( + F32x4(2.0, 3.0, 4.0, 5.0) + ), + F32x4(inf, neg_inf, inf, neg_inf), + ) + + # Float64 tests + alias F64x4 = SIMD[DType.float64, 4] + + assert_equal( + F64x4(0, 1, 2, 3).__pow__(20.0), + F64x4(0.0, 1.0, 1048576.0, 3486784401.0), + ) + + assert_almost_equal( + F64x4(1.0, 2.0, 3.0, 4.0).__pow__(F64x4(2.0, 3.0, 2.0, 1.0)), + F64x4(1.0, 8.0, 9.0, 4.0), + ) + + # Int32 tests + alias I32x4 = SIMD[DType.int32, 4] + + var i32x4_val = I32x4(0, 1, 2, 3) + + assert_equal(i32x4_val.__pow__(20), I32x4(0, 1, 1048576, 3486784401)) + assert_equal(i32x4_val.__pow__(0), I32x4(1, 1, 1, 1)) + assert_equal(I32x4(-2, -1, 0, 1).__pow__(3), I32x4(-8, -1, 0, 1)) + assert_equal( + I32x4(2, 2, 2, 2).__pow__(30), + I32x4(1073741824, 1073741824, 1073741824, 1073741824), + ) + + assert_equal( + I32x4(2, 3, 4, 5).__pow__(I32x4(3, 2, 1, 0)), I32x4(8, 9, 4, 1) + ) + + var i32x4_edge_base = I32x4(-2147483648, -1, 0, 2147483647) + var i32x4_edge_exp = I32x4(31, 31, 31, 31) + assert_equal( + i32x4_edge_base.__pow__(i32x4_edge_exp), I32x4(0, -1, 0, 2147483647) + ) + assert_equal(i32x4_edge_base.__pow__(32), I32x4(0, 1, 0, 1)) + + # UInt32 tests + alias U32x4 = SIMD[DType.uint32, 4] + + var u32x4_val = U32x4(0, 1, 2, 3) + + assert_equal(u32x4_val.__pow__(20), U32x4(0, 1, 1048576, 3486784401)) + + assert_equal( + U32x4(1, 2, 3, 4).__pow__(U32x4(0, 1, 2, 3)), U32x4(1, 2, 9, 64) + ) + + var u32x4_edge_base = U32x4(0, 1, 2147483647, 4294967295) + assert_equal( + u32x4_edge_base.__pow__(U32x4(31, 31, 31, 31)), + U32x4(0, 1, 2147483647, 4294967295), + ) + assert_equal(u32x4_edge_base.__pow__(32), U32x4(0, 1, 1, 1)) + + # Int8 tests + alias I8x4 = SIMD[DType.int8, 4] + + var i8x4_val = I8x4(0, 1, 2, 3) + + assert_equal(i8x4_val.__pow__(2), I8x4(0, 1, 4, 9)) + assert_equal(i8x4_val.__pow__(7), I8x4(0, 1, 128, -117)) + assert_equal(I8x4(-128, -1, 0, 127).__pow__(3), I8x4(0, -1, 0, 127)) + + # UInt8 tests + alias U8x4 = SIMD[DType.uint8, 4] - # TODO: extend/improve these tests - assert_equal(simd_val_int.__pow__(2), I(0, 1, 4, 9)) + var u8x4_val = U8x4(0, 1, 2, 3) + assert_equal(u8x4_val.__pow__(2), U8x4(0, 1, 4, 9)) + assert_equal(u8x4_val.__pow__(8), U8x4(0, 1, 0, 161)) + assert_equal(u8x4_val.__pow__(U8x4(3, 5, 7, 9)), U8x4(0, 1, 128, 227)) def test_powf(): From 071ade03d1ede92eee2cc55c451eaa2ee477451a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 27 Jul 2024 10:30:29 -0700 Subject: [PATCH 1309/2019] [******][GPU] Use tanh intrinsic when running on the GPU MODULAR_ORIG_COMMIT_REV_ID: a04e4daf6541f7ef5cf540cf6b7bd25136064d92 --- stdlib/src/math/math.mojo | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 7218e074fb..f6bd04d8eb 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -832,6 +832,25 @@ fn tanh[ Returns: The result of the elementwise tanh operation. """ + + constrained[ + type.is_floating_point(), "the input type must be floating point" + ]() + + @parameter + if triple_is_nvidia_cuda(): + alias instruction = "tanh.approx.f32" + + @parameter + if sizeof[type]() < sizeof[DType.float32](): + return _call_ptx_intrinsic[ + instruction=instruction, constraints="=f,f" + ](x.cast[DType.float32]()).cast[type]() + elif type is DType.float32: + return _call_ptx_intrinsic[ + instruction=instruction, constraints="=f,f" + ](x) + var xc = x.clamp(-9, 9) var x_squared = xc * xc From eace26abb8d33dd64769af066736542fa8c6e470 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 27 Jul 2024 16:01:50 -0700 Subject: [PATCH 1310/2019] [mojo-stdlib] Move UnsafePointer.__init__ off of -> Self This was made possible by a wide range of improvements across the entire stack. MODULAR_ORIG_COMMIT_REV_ID: 2343b489f2e75b2821bad4e62053906f2349de87 --- stdlib/src/memory/unsafe_pointer.mojo | 39 ++++++++------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index b2ca7b188b..c2985a86fb 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -82,55 +82,38 @@ struct UnsafePointer[ # ===-------------------------------------------------------------------===# @always_inline - fn __init__() -> Self: - """Create a null pointer. - - Returns: - A null pointer. - """ - return Self { - address: __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] - } + fn __init__(inout self): + """Create a null pointer.""" + self.address = __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] @always_inline - fn __init__(value: Self._mlir_type) -> Self: + fn __init__(inout self, value: Self._mlir_type): """Create a pointer with the input value. Args: value: The MLIR value of the pointer to construct with. - - Returns: - The pointer. """ - return Self {address: value} + self.address = value @always_inline - fn __init__(other: UnsafePointer[T, address_space, _]) -> Self: + fn __init__(inout self, other: UnsafePointer[T, address_space, _]): """Exclusivity parameter cast a pointer. Args: other: Pointer to cast. - - Returns: - Constructed UnsafePointer object. """ - return Self { - address: __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( - other.address - ) - } + self.address = __mlir_op.`pop.pointer.bitcast`[_type = Self._mlir_type]( + other.address + ) @always_inline - fn __init__(*, other: Self) -> Self: + fn __init__(inout self, *, other: Self): """Copy the object. Args: other: The value to copy. - - Returns: - A copy of the object. """ - return Self {address: other.address} + self.address = other.address # ===-------------------------------------------------------------------===# # Factory methods From 79cb16a86f56c0c6c111358011a7a9d646760ab1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 27 Jul 2024 16:56:12 -0700 Subject: [PATCH 1311/2019] [mojo-stdlib] Update a bunch of inits, NFC This moves a bunch of initializers off of the legacy `-> Self` syntax. MODULAR_ORIG_COMMIT_REV_ID: 94ad0f3ae155d1f0635460a4f6a29c01e0f1b856 --- stdlib/src/builtin/error.mojo | 58 ++++++++++++--------------------- stdlib/src/builtin/file.mojo | 5 +-- stdlib/src/python/_cpython.mojo | 4 +-- stdlib/src/sys/intrinsics.mojo | 21 ++++-------- stdlib/src/utils/stringref.mojo | 47 +++++++++----------------- 5 files changed, 47 insertions(+), 88 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index e08dea91e1..22353b376e 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -48,37 +48,26 @@ struct Error( """ @always_inline - fn __init__() -> Self: - """Default constructor. - - Returns: - The constructed Error object. - """ - return Error {data: UnsafePointer[UInt8](), loaded_length: 0} + fn __init__(inout self): + """Default constructor.""" + self.data = UnsafePointer[UInt8]() + self.loaded_length = 0 @always_inline - fn __init__(value: StringLiteral) -> Self: + fn __init__(inout self, value: StringLiteral): """Construct an Error object with a given string literal. Args: value: The error message. - - Returns: - The constructed Error object. """ - return Error { - data: value.unsafe_ptr(), - loaded_length: len(value), - } + self.data = value.unsafe_ptr() + self.loaded_length = len(value) - fn __init__(src: String) -> Self: + fn __init__(inout self, src: String): """Construct an Error object with a given string. Args: src: The error message. - - Returns: - The constructed Error object. """ var length = src.byte_length() var dest = UnsafePointer[UInt8].alloc(length + 1) @@ -88,16 +77,14 @@ struct Error( count=length, ) dest[length] = 0 - return Error {data: dest, loaded_length: -length} + self.data = dest + self.loaded_length = -length - fn __init__(src: StringRef) -> Self: + fn __init__(inout self, src: StringRef): """Construct an Error object with a given string ref. Args: src: The error message. - - Returns: - The constructed Error object. """ var length = len(src) var dest = UnsafePointer[UInt8].alloc(length + 1) @@ -107,40 +94,37 @@ struct Error( count=length, ) dest[length] = 0 - return Error {data: dest, loaded_length: -length} + self.data = dest + self.loaded_length = -length - fn __init__(*, other: Self) -> Self: + fn __init__(inout self, *, other: Self): """Copy the object. Args: other: The value to copy. - - Returns: - The copied `Error`. """ - return other + self = other fn __del__(owned self): """Releases memory if allocated.""" if self.loaded_length < 0: self.data.free() - fn __copyinit__(existing: Self) -> Self: + fn __copyinit__(inout self, existing: Self): """Creates a deep copy of an existing error. - Returns: - The copy of the original error. + Args: + existing: The error to copy from. """ if existing.loaded_length < 0: var length = -existing.loaded_length var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy(dest, existing.data, length) dest[length] = 0 - return Error {data: dest, loaded_length: existing.loaded_length} + self.data = dest else: - return Error { - data: existing.data, loaded_length: existing.loaded_length - } + self.data = existing.data + self.loaded_length = existing.loaded_length fn __bool__(self) -> Bool: """Returns True if the error is set and false otherwise. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 006d895727..9e2e6ee24e 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -42,8 +42,9 @@ struct _OwnedStringRef(Boolable): var data: UnsafePointer[UInt8] var length: Int - fn __init__() -> _OwnedStringRef: - return Self {data: UnsafePointer[UInt8](), length: 0} + fn __init__(inout self): + self.data = UnsafePointer[UInt8]() + self.length = 0 fn __del__(owned self): if self.data: diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 1a3f91b1a7..6f33325719 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -68,7 +68,7 @@ struct PythonVersion: var minor: Int var patch: Int - fn __init__(version: StringRef) -> PythonVersion: + fn __init__(inout self, version: StringRef): var version_string = String(version) var components = InlineArray[Int, 3](-1) var start = 0 @@ -86,7 +86,7 @@ struct PythonVersion: i += 1 start = next_idx + 1 next_idx += 1 - return PythonVersion(components[0], components[1], components[2]) + self = PythonVersion(components[0], components[1], components[2]) fn _py_get_version(lib: DLHandle) -> StringRef: diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 638941ca57..d130f40d9e 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -988,17 +988,14 @@ struct PrefetchLocality: """Extremely local locality (keep in cache).""" @always_inline("nodebug") - fn __init__(value: Int) -> PrefetchLocality: + fn __init__(inout self, value: Int): """Constructs a prefetch locality option. Args: value: An integer value representing the locality. Should be a value in the range `[0, 3]`. - - Returns: - The prefetch locality constructed. """ - return PrefetchLocality {value: value} + self.value = value @register_passable("trivial") @@ -1013,17 +1010,14 @@ struct PrefetchRW: """Write prefetch.""" @always_inline("nodebug") - fn __init__(value: Int) -> PrefetchRW: + fn __init__(inout self, value: Int): """Constructs a prefetch read-write option. Args: value: An integer value representing the prefetch read-write option to be used. Should be a value in the range `[0, 1]`. - - Returns: - The prefetch read-write option constructed. """ - return PrefetchRW {value: value} + self.value = value # LLVM prefetch cache type @@ -1039,17 +1033,14 @@ struct PrefetchCache: """The data prefetching option.""" @always_inline("nodebug") - fn __init__(value: Int) -> PrefetchCache: + fn __init__(inout self, value: Int): """Constructs a prefetch option. Args: value: An integer value representing the prefetch cache option to be used. Should be a value in the range `[0, 1]`. - - Returns: - The prefetch cache type that was constructed. """ - return PrefetchCache {value: value} + self.value = value @register_passable("trivial") diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 5be8102e07..d24687ffa1 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -63,40 +63,31 @@ struct StringRef( # ===-------------------------------------------------------------------===# @always_inline - fn __init__() -> Self: - """Construct a StringRef value with length zero. - - Returns: - Constructed `StringRef` object. - """ - return StringRef(UnsafePointer[UInt8](), 0) + fn __init__(inout self): + """Construct a StringRef value with length zero.""" + self = StringRef(UnsafePointer[UInt8](), 0) @always_inline - fn __init__(*, other: Self) -> Self: + fn __init__(inout self, *, other: Self): """Copy the object. Args: other: The value to copy. - - Returns: - Constructed `StringRef` object. """ - return Self(other.data, other.length) + self.data = other.data + self.length = other.length @always_inline - fn __init__(str: StringLiteral) -> Self: + fn __init__(inout self, str: StringLiteral): """Construct a StringRef value given a constant string. Args: str: The input constant string. - - Returns: - Constructed `StringRef` object. """ - return StringRef(str.unsafe_ptr(), len(str)) + self = StringRef(str.unsafe_ptr(), len(str)) @always_inline - fn __init__(ptr: UnsafePointer[C_char], len: Int) -> Self: + fn __init__(inout self, ptr: UnsafePointer[C_char], len: Int): """Construct a StringRef value given a (potentially non-0 terminated string). @@ -109,32 +100,27 @@ struct StringRef( Args: ptr: UnsafePointer to the string. len: The length of the string. - - Returns: - Constructed `StringRef` object. """ - return Self {data: ptr.bitcast[UInt8](), length: len} + self.data = ptr.bitcast[UInt8]() + self.length = len @always_inline - fn __init__(ptr: UnsafePointer[UInt8]) -> Self: + fn __init__(inout self, ptr: UnsafePointer[UInt8]): """Construct a StringRef value given a null-terminated string. Args: ptr: UnsafePointer to the string. - - Returns: - Constructed `StringRef` object. """ var len = 0 while Scalar.load(ptr, len): len += 1 - return StringRef(ptr, len) + self = StringRef(ptr, len) @always_inline - fn __init__(ptr: UnsafePointer[C_char]) -> Self: + fn __init__(inout self, ptr: UnsafePointer[C_char]): """Construct a StringRef value given a null-terminated string. Note that you should use the constructor from `UnsafePointer[UInt8]` instead @@ -143,16 +129,13 @@ struct StringRef( Args: ptr: UnsafePointer to the string. - - Returns: - Constructed `StringRef` object. """ var len = 0 while Scalar.load(ptr, len): len += 1 - return StringRef(ptr, len) + self = StringRef(ptr, len) # ===-------------------------------------------------------------------===# # Helper methods for slicing From 66c9d3d2be30cf197033092c79252831b5689b11 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 27 Jul 2024 17:43:20 -0700 Subject: [PATCH 1312/2019] [mojo-lang] Don't assume Coroutine returns an SRValue This changes the compiler to not assume Coroutine.__init__ returns an SRValue, allowing us to remove the `-> Self` initializer from it. MODULAR_ORIG_COMMIT_REV_ID: 4d32e838fc58af97522dfbc264830f0c7b94a564 --- stdlib/src/builtin/coroutine.mojo | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 75c6801d65..c7d74bb558 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -120,16 +120,13 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: ) @always_inline - fn __init__(handle: AnyCoroutine) -> Self: + fn __init__(inout self, handle: AnyCoroutine): """Construct a coroutine object from a handle. Args: handle: The init handle. - - Returns: - The constructed coroutine object. """ - return Self {_handle: handle} + self._handle = handle @always_inline fn __del__(owned self): @@ -204,16 +201,13 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: ) @always_inline - fn __init__(handle: AnyCoroutine) -> Self: + fn __init__(inout self, handle: AnyCoroutine): """Construct a coroutine object from a handle. Args: handle: The init handle. - - Returns: - An owning coroutine. """ - return Self {_handle: handle} + self._handle = handle @always_inline fn __del__(owned self): From c9841dc95ae3e5b702a8853626252c8475e99774 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 28 Jul 2024 17:13:57 -0700 Subject: [PATCH 1313/2019] [mojo-lang] Remove support for -> Self initializers. This removes the machinery in place to support the legacy `-> Self` initializers that have been subsumed with `inout self` initializers. MODULAR_ORIG_COMMIT_REV_ID: 932beb57e17a1279abdab037312b844f2ff07894 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 3a0805a5e6..ac531666b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -569,6 +569,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. ### ❌ Removed +- Support for the legacy `fn __init__(...) -> Self:` form has been removed from + the compiler, please switch to using `fn __init__(inout self, ...):` instead. + - It is no longer possible to cast (implicitly or explicitly) from `Reference` to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the `UnsafePointer.address_of(someRef[])` which makes the code explicit that the From 47c646fc35c0c03b7449aa32abd5cd260f6b3ff9 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Mon, 29 Jul 2024 09:50:08 -0700 Subject: [PATCH 1314/2019] [External] [stdlib] Remove obsolete `FIXME` (#44234) [External] [stdlib] Remove obsolete `FIXME` While working in the same file, I noticed that the workaround introduced in commit [945a542](https://github.com/modularml/mojo/commit/945a542be74b81dfdb03b860ba38be4ac5228aa6) seems to resolve this issue. Additionally, the `TODO` in that commit a clearer direction. Co-authored-by: Joshua James Venter Closes modularml/mojo#3326 MODULAR_ORIG_COMMIT_REV_ID: 8f664d5394d1935036490eafa5bc2c9cb03fe492 --- stdlib/test/builtin/test_math.mojo | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 3c931a4553..4f79fe6da5 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -88,11 +88,9 @@ def test_round(): assert_equal(2, round(1.5)) assert_equal(2, round(2.0)) assert_equal(1, round(1.4, 0)) - # FIXME: round(2.5) is 2.0 (roundeven) but it's using roundhalfup - # Fix when the math library is open sourced - # assert_equal(2, round(2.5)) - # assert_equal(1.5, round(1.5, 1)) - # assert_equal(1.61, round(1.613, 2)) + assert_equal(2, round(2.5)) + assert_equal(1.5, round(1.5, 1)) + assert_equal(1.61, round(1.613, 2)) var lhs = SIMD[DType.float32, 4](1.1, 1.5, 1.9, 2.0) var expected = SIMD[DType.float32, 4](1.0, 2.0, 2.0, 2.0) From 654a425b2868d692137e3a16f098ee7c9b185bff Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 29 Jul 2024 09:50:29 -0700 Subject: [PATCH 1315/2019] [External] [stdlib] Use inferred-only in `rebind` (#44235) [External] [stdlib] Use inferred-only in `rebind` Co-authored-by: soraros Closes modularml/mojo#3325 MODULAR_ORIG_COMMIT_REV_ID: 18774f823abd9bfa72f9d4ef33c2243304779dd9 --- stdlib/src/builtin/rebind.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index 9a06bec09c..47b4e66675 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -18,8 +18,8 @@ These are Mojo built-ins, so you don't need to import them. @always_inline("nodebug") fn rebind[ + src_type: AnyTrivialRegType, //, dest_type: AnyTrivialRegType, - src_type: AnyTrivialRegType, ](val: src_type) -> dest_type: """Statically assert that a parameter input type `src_type` resolves to the same type as a parameter result type `dest_type` after function @@ -30,8 +30,8 @@ fn rebind[ the type with the constrained parameter value. Parameters: - dest_type: The type to rebind to. src_type: The original type. + dest_type: The type to rebind to. Args: val: The value to rebind. From 2c72169685145c51a9a596276edcd007c99f78b8 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 29 Jul 2024 10:58:55 -0700 Subject: [PATCH 1316/2019] [External] [stdlib] Rename `_get_reference_unsafe` to `unsafe_get` in `InlineArray` (#41738) [External] [stdlib] Rename `_get_reference_unsafe` to `unsafe_get` in `InlineArray` And also add autoderef. The renaming is to have something consistent with `List` which has the methods `unsafe_get` and `unsafe_get`. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3013 MODULAR_ORIG_COMMIT_REV_ID: 5991295a4afb50ff9f2c4a7b3d4b2eee1013d293 --- stdlib/src/utils/static_tuple.mojo | 14 ++++++++------ stdlib/test/utils/test_tuple.mojo | 18 +++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 6bf94d1f2c..5024399b34 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -314,7 +314,7 @@ struct InlineArray[ @parameter for i in range(size): - var ptr = UnsafePointer.address_of(self._get_reference_unsafe(i)[]) + var ptr = UnsafePointer.address_of(self.unsafe_get(i)) ptr.initialize_pointee_explicit_copy(fill) @always_inline @@ -346,7 +346,9 @@ struct InlineArray[ # Move each element into the array storage. @parameter for i in range(size): - var eltref = self._get_reference_unsafe(i) + var eltref: Reference[ + Self.ElementType, __lifetime_of(self) + ] = self.unsafe_get(i) UnsafePointer.address_of(storage[i]).move_pointee_into( UnsafePointer[Self.ElementType].address_of(eltref[]) ) @@ -386,7 +388,7 @@ struct InlineArray[ """ var normalized_index = normalize_index["InlineArray"](idx, self) - return self._get_reference_unsafe(normalized_index)[] + return self.unsafe_get(normalized_index) @always_inline("nodebug") fn __getitem__[ @@ -408,7 +410,7 @@ struct InlineArray[ if idx < 0: normalized_idx += size - return self._get_reference_unsafe(normalized_idx)[] + return self.unsafe_get(normalized_idx) # ===------------------------------------------------------------------=== # # Trait implementations @@ -428,9 +430,9 @@ struct InlineArray[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn _get_reference_unsafe( + fn unsafe_get( ref [_]self: Self, idx: Int - ) -> Reference[Self.ElementType, __lifetime_of(self)]: + ) -> ref [__lifetime_of(self)] Self.ElementType: """Get a reference to an element of self without checking index bounds. Users should opt for `__getitem__` instead of this method as it is diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 2f2ac70e3b..75e8920d10 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -75,22 +75,22 @@ def test_tuple_literal(): assert_equal(len(()), 0) -def test_array_get_reference_unsafe(): - # Negative indexing is undefined behavior with _get_reference_unsafe +def test_array_unsafe_get(): + # Negative indexing is undefined behavior with unsafe_get # so there are not test cases for it. var arr = InlineArray[Int, 3](0, 0, 0) - assert_equal(arr._get_reference_unsafe(0)[], 0) - assert_equal(arr._get_reference_unsafe(1)[], 0) - assert_equal(arr._get_reference_unsafe(2)[], 0) + assert_equal(arr.unsafe_get(0), 0) + assert_equal(arr.unsafe_get(1), 0) + assert_equal(arr.unsafe_get(2), 0) arr[0] = 1 arr[1] = 2 arr[2] = 3 - assert_equal(arr._get_reference_unsafe(0)[], 1) - assert_equal(arr._get_reference_unsafe(1)[], 2) - assert_equal(arr._get_reference_unsafe(2)[], 3) + assert_equal(arr.unsafe_get(0), 1) + assert_equal(arr.unsafe_get(1), 2) + assert_equal(arr.unsafe_get(2), 3) def test_array_int(): @@ -216,7 +216,7 @@ def main(): test_static_tuple() test_static_int_tuple() test_tuple_literal() - test_array_get_reference_unsafe() + test_array_unsafe_get() test_array_int() test_array_str() test_array_int_pointer() From 3c92f0bafd5c9d16048419963226b0474e38716d Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 29 Jul 2024 14:00:31 -0500 Subject: [PATCH 1317/2019] [stdlib] Shorten `initialize_pointee_explicit_copy` Rename `UnsafePointer.initialize_pointee_explicit_copy` to `init_pointee_explicit_copy`. This makes it consistent with the other API naming in `UnsafePointer` like `init_pointee_copy`. While here, move it to be by its other `initialize_*` methods. This makes the generated API documentation a bit easier to follow. MODULAR_ORIG_COMMIT_REV_ID: 24d962783fe9db6926eb549eb2f95bc94e415cb2 --- stdlib/src/memory/maybe_uninitialized.mojo | 6 +-- stdlib/src/memory/unsafe_pointer.mojo | 46 +++++++++++----------- stdlib/src/utils/static_tuple.mojo | 4 +- stdlib/test/memory/test_unsafepointer.mojo | 6 +-- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 4ad65d0a0d..fe4ccc31eb 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -107,9 +107,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): Args: other: The object to copy. """ - self.unsafe_ptr().initialize_pointee_explicit_copy( - other.assume_initialized() - ) + self.unsafe_ptr().init_pointee_explicit_copy(other.assume_initialized()) @always_inline fn copy_from[ @@ -125,7 +123,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): Args: other: The object to copy. """ - self.unsafe_ptr().initialize_pointee_explicit_copy(other) + self.unsafe_ptr().init_pointee_explicit_copy(other) @always_inline fn __moveinit__(inout self, owned other: Self): diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index c2985a86fb..28a4da775b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -623,29 +623,6 @@ struct UnsafePointer[ var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) scatter(val, base, mask, alignment) - @always_inline - fn initialize_pointee_explicit_copy[ - T: ExplicitlyCopyable, // - ](self: UnsafePointer[T], value: T): - """Emplace a copy of `value` into this pointer location. - - The pointer memory location is assumed to contain uninitialized data, - and consequently the current contents of this pointer are not destructed - before writing `value`. Similarly, ownership of `value` is logically - transferred into the pointer location. - - When compared to `init_pointee_move`, this avoids an extra move on - the callee side when the value must be copied. - - Parameters: - T: The type the pointer points to, which must be - `ExplicitlyCopyable`. - - Args: - value: The value to emplace. - """ - __get_address_as_uninit_lvalue(self.address) = T(other=value) - @always_inline fn free(self): """Free the memory referenced by the pointer.""" @@ -770,6 +747,29 @@ struct UnsafePointer[ """ __get_address_as_uninit_lvalue(self.address) = value + @always_inline + fn init_pointee_explicit_copy[ + T: ExplicitlyCopyable, // + ](self: UnsafePointer[T], value: T): + """Emplace a copy of `value` into this pointer location. + + The pointer memory location is assumed to contain uninitialized data, + and consequently the current contents of this pointer are not destructed + before writing `value`. Similarly, ownership of `value` is logically + transferred into the pointer location. + + When compared to `init_pointee_move`, this avoids an extra move on + the callee side when the value must be copied. + + Parameters: + T: The type the pointer points to, which must be + `ExplicitlyCopyable`. + + Args: + value: The value to emplace. + """ + __get_address_as_uninit_lvalue(self.address) = T(other=value) + @always_inline fn move_pointee_into[ T: Movable, //, diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 5024399b34..7d58dd1169 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -315,7 +315,7 @@ struct InlineArray[ @parameter for i in range(size): var ptr = UnsafePointer.address_of(self.unsafe_get(i)) - ptr.initialize_pointee_explicit_copy(fill) + ptr.init_pointee_explicit_copy(fill) @always_inline fn __init__(inout self, owned *elems: Self.ElementType): @@ -368,7 +368,7 @@ struct InlineArray[ for idx in range(size): var ptr = self.unsafe_ptr() + idx - ptr.initialize_pointee_explicit_copy(other[idx]) + ptr.init_pointee_explicit_copy(other[idx]) # ===------------------------------------------------------------------===# # Operator dunders diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 36e725b446..1a823ca4fd 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -79,14 +79,14 @@ def test_unsafepointer_move_pointee_move_count(): assert_equal(2, ptr_2[].move_count) -def test_unsafepointer_initialize_pointee_explicit_copy(): +def test_unsafepointer_init_pointee_explicit_copy(): var ptr = UnsafePointer[ExplicitCopyOnly].alloc(1) var orig = ExplicitCopyOnly(5) assert_equal(orig.copy_count, 0) # Test initialize pointee from `ExplicitlyCopyable` type - ptr.initialize_pointee_explicit_copy(orig) + ptr.init_pointee_explicit_copy(orig) assert_equal(ptr[].value, 5) assert_equal(ptr[].copy_count, 1) @@ -254,7 +254,7 @@ def main(): test_unsafepointer_of_move_only_type() test_unsafepointer_move_pointee_move_count() - test_unsafepointer_initialize_pointee_explicit_copy() + test_unsafepointer_init_pointee_explicit_copy() test_explicit_copy_of_pointer_address() test_bitcast() From a688c327909f939bb74b8aa8cca0d165d7ab1acf Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 29 Jul 2024 12:02:00 -0700 Subject: [PATCH 1318/2019] [Stdlib] Add math.reciprocal operation MODULAR_ORIG_COMMIT_REV_ID: 7a8985fb96c60b8e61a20b93c47f59ad4f5472f3 --- stdlib/src/math/__init__.mojo | 1 + stdlib/src/math/math.mojo | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 5e6531316d..5c046033f5 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -75,4 +75,5 @@ from .math import ( y0, y1, clamp, + recip, ) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index f6bd04d8eb..07b683f78e 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -307,6 +307,52 @@ fn rsqrt(x: SIMD) -> __type_of(x): return 1 / sqrt(x) +# ===----------------------------------------------------------------------=== # +# recip +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _recip_nvvm(x: SIMD) -> __type_of(x): + constrained[ + x.type in (DType.float32, DType.float64), "must be f32 or f64 type" + ]() + + alias instruction = "llvm.nvvm.rcp.approx.ftz.f" if x.type is DType.float32 else "llvm.nvvm.rcp.approx.ftz.d" + var res = __type_of(x)() + + @parameter + for i in range(x.size): + res[i] = llvm_intrinsic[ + instruction, Scalar[x.type], has_side_effect=False + ](x[i]) + return res + + +@always_inline +fn recip(x: SIMD) -> __type_of(x): + """Performs elementwise reciprocal on a SIMD vector. + + Args: + x: SIMD vector to perform reciprocal on. + + Returns: + The elementwise reciprocal of x. + """ + constrained[x.type.is_floating_point(), "type must be floating point"]() + + @parameter + if triple_is_nvidia_cuda(): + + @parameter + if x.type in (DType.float16, DType.bfloat16): + return _recip_nvvm(x.cast[DType.float32]()).cast[x.type]() + + return _recip_nvvm(x) + + return 1 / x + + # ===----------------------------------------------------------------------=== # # exp2 # ===----------------------------------------------------------------------=== # From 057e6c9a2c14d1a0e123ccf96ea3a92cbec048e0 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Mon, 29 Jul 2024 12:51:04 -0700 Subject: [PATCH 1319/2019] Fixed various broken anchors in the documentation. MODULAR_ORIG_COMMIT_REV_ID: 223735a7caa72a9b9a0066af57d8abf0ff60be97 --- docs/changelog-released.md | 76 ++++++++++--------- docs/manual/basics.ipynb | 2 +- .../manual/decorators/register-passable.ipynb | 2 +- docs/manual/functions.ipynb | 2 +- docs/manual/lifecycle/life.ipynb | 12 +-- docs/manual/parameters/index.ipynb | 4 +- docs/manual/values/ownership.ipynb | 2 +- docs/tools/debugging.ipynb | 2 +- 8 files changed, 52 insertions(+), 50 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 8deb4b1a1b..1eafdd18c6 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -414,7 +414,7 @@ Big themes for this release: to the `Sized` trait. This clarifies the ambiguity of the semantics: the length of a slice always depends on the length of the object being sliced. Users that need the existing functionality can use the - [`Slice.unsafe_indices()`](/mojo/stdlib/builtin/builtin_slice/Slice#unsafe_indices) + [`Slice.unsafe_indices()`](/mojo/stdlib/builtin/builtin_slice/Slice#indices) method. This makes it explicit that this implementation does not check if the slice bounds are concrete or within any given object's length. @@ -591,7 +591,7 @@ Big themes for this release: `StaticIntTuple` mask. ([PR #2315](https://github.com/modularml/mojo/pull/2315)) - - [`SIMD.__bool__()`](/mojo/stdlib/builtin/simd/SIMD#bool) is constrained + - [`SIMD.__bool__()`](/mojo/stdlib/builtin/simd/SIMD#__bool__) is constrained such that it only works when `size` is `1`. For SIMD vectors with more than one element, use [`any()`](/mojo/stdlib/builtin/bool/any) or [`all()`](/mojo/stdlib/builtin/bool/all). @@ -847,13 +847,13 @@ Special thanks to our community contributors: methods have been changed to top-level functions and renamed. The new functions are: - - [`initialize_pointee_copy`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy) - - [`initialize_pointee_move`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move) - - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee) - - [`move_pointee`](/mojo/stdlib/memory/unsafe_pointer/move_pointee) + - [`initialize_pointee_copy`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) + - [`initialize_pointee_move`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move) + - [`move_from_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee) + - [`move_pointee`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) - A new - [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) + [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) function runs the destructor on the pointee. - `UnsafePointer` can be initialized directly from a @@ -871,8 +871,8 @@ Special thanks to our community contributors: - Improvements to variadic arguments support. - - Heterogeneous variadic pack arguments now work reliably even with memory types, - and have a more convenient API to use, as defined by the + - Heterogeneous variadic pack arguments now work reliably even with memory + types, and have a more convenient API to use, as defined by the [`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack) type. For example, a simplified version of `print` can be implemented like this: @@ -964,8 +964,8 @@ Special thanks to our community contributors: my_assert(False, "always fails") # some_file.mojo, line 193 ``` - This prints "`In /path/to/some_file.mojo on line 193: always fails`". Note that - `__call_location()` only works in `@always_inline` or + This prints "`In /path/to/some_file.mojo on line 193: always fails`". + Note that `__call_location()` only works in `@always_inline` or `@always_inline("nodebug")` functions. It gives incorrect results if placed in an `@always_inline` function that's called _from_ an `@always_inline("nodebug")` function. @@ -1087,7 +1087,7 @@ Special thanks to our community contributors: - It has moved to the `memory.reference` module instead of `memory.unsafe`. - `Reference` now has an - [`unsafe_bitcast()`](/mojo/stdlib/memory/reference/Reference#unsafe_bitcast) + [`unsafe_bitcast()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#bitcast) method, similar to the pointer types. - Several unsafe methods were removed, including `offset()`, @@ -1252,9 +1252,9 @@ Special thanks to our community contributors: now perform checked additions, subtractions, and multiplications using the following new methods: - - [`add_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#add_with_overflow) - - [`sub_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#sub_with_overflow) - - [`mul_with_overflow()`](/mojo/stdlib/builtin/simd/SIMD#mul_with_overflow) + - `add_with_overflow()` + - `sub_with_overflow()` + - `mul_with_overflow()` Checked arithmetic allows the caller to determine if an operation exceeded the numeric limits of the type. For example: @@ -1827,12 +1827,12 @@ fixed in a future release. binding Reference to lvalue with subtype lifetime. - [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` should return `T` instead of `Optional[T]`. -- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` - to floating point or integral types. +- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain + `math.copysign` to floating point or integral types. - [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` does not work when specifying `end=""` -- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods - correctly handle edge cases where `size_out >= size`. +- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` + methods correctly handle edge cases where `size_out >= size`. ## v24.1.1 (2024-03-18) @@ -2076,7 +2076,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. [`Reference`](/mojo/stdlib/memory/reference/Reference) instead of having to return an MLIR internal reference type. -- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_into) +- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) method, for moving a value from one pointer memory location to another. - Added built-in [`hex()`](/mojo/stdlib/builtin/format_int/hex) function, which @@ -2137,7 +2137,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - The [`find()`](/mojo/stdlib/builtin/string_literal/StringLiteral#find), [`rfind()`](/mojo/stdlib/builtin/string_literal/StringLiteral#rfind), - [`count()`](/mojo/stdlib/builtin/string_literal/StringLiteral#count), and + [`count()`](/mojo/stdlib/utils/stringref/StringRef#count), and [`__contains__()`](/mojo/stdlib/builtin/string_literal/StringLiteral#__contains__) methods now work on string literals. This means that you can write: @@ -2201,7 +2201,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - It is no longer possible to explicitly specify implicit argument parameters in [automatically parameterized - functions](/mojo/manual/parameters/#automatic-parameterization-of-functions)). + functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). This ability was an oversight and this is now an error: ```mojo @@ -2270,9 +2270,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `capacity` as a keyword-only argument to prevent implicit conversion from `Int`. -- [`Variant.get[T]()`](/mojo/stdlib/utils/variant/Variant#get) now returns a -[`Reference`](/mojo/stdlib/memory/reference/Reference) to the value rather than -a copy. +- [`Variant.get[T]()`](/mojo/stdlib/utils/variant/Variant#__getitem__) + now returns a [`Reference`](/mojo/stdlib/memory/reference/Reference) + to the value rather than a copy. - The [`String`](/mojo/stdlib/builtin/string/String) methods `tolower()` and `toupper()` have been renamed to `str.lower()` and `str.upper()`. @@ -2510,8 +2510,8 @@ experience without dedicated sugar. duplication due to mutability specifiers](https://duckki.github.io/2024/01/01/inferred-mutability.html) and provides the base for unified user-level types. For example, it could be - used to implement an array slice object that handles both mutable and immutable - array slices. + used to implement an array slice object that handles both mutable and + immutable array slices. While this is a major step forward for the lifetimes system in Mojo, it is still _very_ early and awkward to use. Notably, there is no syntactic sugar @@ -2770,8 +2770,8 @@ experience without dedicated sugar. and [`Copyable`](/mojo/stdlib/builtin/value/Copyable) built-in traits. - [`String`](/mojo/stdlib/builtin/string/String) now has new - [`toupper()`](/mojo/stdlib/builtin/string/String#toupper) and - [`tolower()`](/mojo/stdlib/builtin/string/String#tolower) methods analogous, + [`toupper()`](/mojo/stdlib/builtin/string/String#upper) and + [`tolower()`](/mojo/stdlib/builtin/string/String#lower) methods analogous, respectively, to Python's `str.toupper()` and `str.tolower()`. - Added a [`hash()`](/mojo/stdlib/builtin/hash/hash) built-in function and @@ -3029,7 +3029,7 @@ experience without dedicated sugar. explicitly unbound by the user. For more information, see the - [Mojo Manual](/mojo/manual/parameters/#partial-automatic-parameterization). + [Mojo Manual](/mojo/manual/parameters/#fully-bound-partially-bound-and-unbound-types). - Parametric types can now be partially bound in certain contexts. For example, a new `Scalar` type alias has been added defined as: @@ -3244,8 +3244,8 @@ the previous "read to EOF" behavior when size is negative. - Mojo now supports compile-time _keyword parameters_, in addition to existing support for [keyword - arguments](/mojo/manual/basics/#optional-arguments-and-keyword-arguments). For - example: + arguments](/mojo/manual/parameters/#optional-parameters-and-keyword-parameters). + For example: ```mojo fn foo[a: Int, b: Int = 42](): @@ -3856,7 +3856,7 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco value from one location to another. For more information, see the Mojo Manual section on - [move constructors](/mojo/manual/lifecycle/life#move-constructors). + [move constructors](/mojo/manual/lifecycle/life#move-constructor). - The Error type in Mojo has changed. Instead of extracting the error message using `error.value` you will now extract the error message using @@ -4446,14 +4446,16 @@ only in declared parameter names, e.g. the following now works correctly: #### ❌ Removed - Mojo Playground no longer includes the following Python packages (due to size, - compute costs, and [environment complications](https://github.com/modularml/mojo/issues/300)): + compute costs, and + [environment complications](https://github.com/modularml/mojo/issues/300)): `torch`, `tensorflow`, `keras`, `transformers`. #### 🦋 Changed - The data types and scalar names now conform to the naming convention used by numpy. So we use `Int32` instead of `SI32`, similarly using `Float32` - instead of `F32`. Closes [Issue #152](https://github.com/modularml/mojo/issues/152). + instead of `F32`. Closes + [Issue #152](https://github.com/modularml/mojo/issues/152). #### 🛠️ Fixed @@ -4491,7 +4493,7 @@ only in declared parameter names, e.g. the following now works correctly: ``` When `takeValueAsOwned()` takes its argument as an - [`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and) + [`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-) value (this is common in initializers for example), it is allowed to do whatever it wants with the value and destroy it when it is finished. In order to support this, @@ -4649,7 +4651,7 @@ only in declared parameter names, e.g. the following now works correctly: optimized Matmul implementation is 3x faster. - Renamed the [`^` postfix -operator](/mojo/manual/values/ownership#transfer-arguments-owned-and) +operator](/mojo/manual/values/ownership#transfer-arguments-owned-and-) from "consume" to "transfer." #### 🛠️ Fixed diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index f7730cf07e..511b3ad849 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -135,7 +135,7 @@ ":::note\n", "\n", "You don't need a `main()` function when coding in the\n", - "[REPL](/mojo/manual/get-started#run-code-in-the-repl) or in a\n", + "[REPL](/mojo/manual/get-started#2-run-code-in-the-repl) or in a\n", "[Jupyter\n", "notebook](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme).\n", "\n", diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb index 9627f7f5dd..31c31d8e4d 100644 --- a/docs/manual/decorators/register-passable.ipynb +++ b/docs/manual/decorators/register-passable.ipynb @@ -102,7 +102,7 @@ "instead of being passed by-pointer.\n", "\n", "1. `@register_passable` types cannot have a [`__moveinit__()`\n", - "constructor](/mojo/manual/lifecycle/life#move-constructors), because\n", + "constructor](/mojo/manual/lifecycle/life#move-constructor), because\n", "values passed in a register cannot be passed by reference.\n" ] }, diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 89aed06792..1183291215 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -205,7 +205,7 @@ "enforce value semantics. So, be careful if using `object` values alongside other\n", "strongly-typed values—their behavior might be inconsistent because `object` is \n", "the only type in the standard library that does not conform to [full value\n", - "semantics](/mojo/manual/values/value-semantics#full-value-semantics).\n", + "semantics](/mojo/manual/values/value-semantics#intro-to-value-semantics).\n", "\n", ":::note TODO\n", "\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index cb84920593..c6140e7ef3 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -130,7 +130,7 @@ "metadata": {}, "source": [ "An instance of `MyPet` can also be\n", - "[borrowed](/mojo/manual/values/ownership#immutable-arguments-borrowed)\n", + "[borrowed](/mojo/manual/values/ownership#borrowed-arguments-borrowed)\n", "and destroyed, but it currently can't be copied or moved.\n", "\n", "We believe this is a good default starting point, because there are no built-in\n", @@ -532,7 +532,7 @@ "\n", "Mojo also calls upon the copy constructor when a value is passed to a\n", "function that takes the argument as\n", - "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", + "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", "operator `^`), then Mojo instead invokes the move constructor.\n", @@ -558,7 +558,7 @@ "\n", "To support moving a value, implement the `__moveinit__()` method. The \n", "`__moveinit__()` method performs a consuming move: it [transfers\n", - "ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and)\n", + "ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", "of a value from one variable to another when the original variable's lifetime\n", "ends (also called a \"destructive move\").\n", "\n", @@ -569,7 +569,7 @@ "the move constructors are only part of the implementation for how Mojo\n", "transfers ownership of a value. You can learn more in the section about\n", "[ownership\n", - "transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and).\n", + "transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and-).\n", "\n", ":::" ] @@ -640,7 +640,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and)).\n", + "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index c3a36110de..04a46f62ca 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -134,7 +134,7 @@ "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n", "but not generic. A generic function or struct is parameterized on _type_. For\n", "example, we could rewrite `repeat[]()` to take any type of argument that\n", - "conforms to the [`Stringable`](/mojo/stdlib/builtin/str#stringable) trait: " + "conforms to the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait: " ] }, { diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index f899e3e8f2..447c0dd528 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -489,7 +489,7 @@ "making a copy:\n", "\n", "- If a type implements the [move\n", - " constructor](/mojo/manual/lifecycle/life#consuming-move-constructor),\n", + " constructor](/mojo/manual/lifecycle/life#move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", " transferred into a function as an `owned` argument, _and_ the original value's\n", " lifetime ends at the same point (with or without use of the `^` transfer\n", diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index b5bd109810..3136b6d6af 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -47,7 +47,7 @@ "\n", "If you're already familiar with debugging in VS Code, the\n", "material in this section will mostly be review. You might want to skip ahead to\n", - "[Mojo launch configurations](#mojo-launch-configurations)\n", + "[Launch configurations](#launch-configurations)\n", "or see [Using the debugger](#using-the-debugger) for notes on the features\n", "supported in the Mojo debugger. \n", "\n", From e9a85fd247fd5051ceddd2a93951cbf30bfe0e01 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:19:07 -0400 Subject: [PATCH 1320/2019] [stdlib] Add `IntLike` trait `IntLike` takes the intersection of all the traits implemented by `Int` and `UInt` and requires implementing ```mojo fn __mlir_index__() -> _mlir_type.index: ``` This allows functions to work on both `Int` and `UInt`. MSTDL-802 MODULAR_ORIG_COMMIT_REV_ID: e513ad7adcd03aa569f3f4dc44a53b9dbc8071c7 --- stdlib/src/builtin/_math.mojo | 15 +++++++++ stdlib/src/builtin/int.mojo | 38 +++++++++++++++++----- stdlib/src/builtin/uint.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 7 ++-- stdlib/test/memory/test_unsafepointer.mojo | 11 +++++++ 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index dccb8eee64..67b818e9f4 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -48,6 +48,11 @@ trait Ceilable: # TODO(MOCO-333): Reconsider the signature when we have parametric traits or # associated types. fn __ceil__(self) -> Self: + """Return the ceiling of the Int value, which is itself. + + Returns: + The Int value itself. + """ ... @@ -80,6 +85,11 @@ trait Floorable: # TODO(MOCO-333): Reconsider the signature when we have parametric traits or # associated types. fn __floor__(self) -> Self: + """Return the floor of the Int value, which is itself. + + Returns: + The Int value itself. + """ ... @@ -196,4 +206,9 @@ trait Truncable: # TODO(MOCO-333): Reconsider the signature when we have parametric traits or # associated types. fn __trunc__(self) -> Self: + """Return the truncated Int value, which is itself. + + Returns: + The Int value itself. + """ ... diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 31e0e4c234..714a92b30f 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -177,6 +177,35 @@ trait IntableRaising: ... +# ===----------------------------------------------------------------------=== # +# IntLike +# ===----------------------------------------------------------------------=== # + + +trait IntLike( + Absable, + Ceilable, + Comparable, + Floorable, + Formattable, + Powable, + Stringable, + Truncable, +): + """ + The `IntLike` trait is a tag for `Int` or `UInt`. This allows writing + functions that works on either. + """ + + fn __mlir_index__(self) -> __mlir_type.index: + """Convert to index. + + Returns: + The corresponding __mlir_type.index value. + """ + ... + + # ===----------------------------------------------------------------------=== # # int # ===----------------------------------------------------------------------=== # @@ -258,20 +287,13 @@ fn int(value: UInt) -> Int: @value @register_passable("trivial") struct Int( - Absable, - Ceilable, CeilDivable, - Comparable, - Floorable, - Formattable, Indexer, Intable, ImplicitlyBoolable, KeyElement, - Powable, Roundable, - Stringable, - Truncable, + IntLike, ): """This type represents an integer value.""" diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 2b9c64cb4c..5075b5392f 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -22,7 +22,7 @@ from builtin.simd import _format_scalar @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct UInt(Comparable, Formattable, Representable, Stringable): +struct UInt(IntLike): """This type represents an unsigned integer. An unsigned integer is represents a positive integral number. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 28a4da775b..7f077c265b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -200,16 +200,19 @@ struct UnsafePointer[ ) @always_inline - fn offset(self, idx: Int) -> Self: + fn offset[T: IntLike](self, idx: T) -> Self: """Returns a new pointer shifted by the specified offset. + Parameters: + T: The type of idx; either `Int` or `UInt`. + Args: idx: The offset of the new pointer. Returns: The new constructed UnsafePointer. """ - return __mlir_op.`pop.offset`(self.address, idx.value) + return __mlir_op.`pop.offset`(self.address, idx.__mlir_index__()) @always_inline fn __getitem__( diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 1a823ca4fd..e1cfdf6401 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -246,6 +246,17 @@ def test_alignment(): ptr_2.free() +def test_offset(): + var ptr = UnsafePointer[Int].alloc(5) + for i in range(5): + ptr[i] = i + var x = UInt(3) + var y = Int(4) + assert_equal(ptr[x], 3) + assert_equal(ptr[y], 4) + ptr.free() + + def main(): test_address_of() From d1ab05a977fcbdef1d71ea438bd9c37a29b61b53 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 29 Jul 2024 18:32:31 -0700 Subject: [PATCH 1321/2019] [Stdlib] Specialize the SIMD.cast operation MODULAR_ORIG_COMMIT_REV_ID: 9a7ddc7663e4023fe0ac61ab99364abb0d716a59 --- stdlib/src/builtin/simd.mojo | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 3f18cf35aa..6e27a9f9f0 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -27,6 +27,8 @@ from sys import ( triple_is_nvidia_cuda, ) +from sys._assembly import inlined_assembly + from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width @@ -1446,6 +1448,23 @@ struct SIMD[type: DType, size: Int]( @parameter if type == target: return rebind[SIMD[target, size]](self) + elif ( + triple_is_nvidia_cuda() + and type is DType.float32 + and target is DType.bfloat16 + and size == 2 + ): + var bf16x2_as_uint32 = inlined_assembly[ + "cvt.rn.bf16x2.f32 $0, $1, $2;", + UInt32, + constraints="=r,f,f", + has_side_effect=False, + ](rebind[Float32](self[1]), rebind[Float32](self[0])) + + return rebind[SIMD[target, size]]( + bitcast[DType.bfloat16, 2](bf16x2_as_uint32) + ) + elif has_neon() and ( type is DType.bfloat16 or target == DType.bfloat16 ): From b131a6d0ba9588a988bb3026f5a96c0cec01597a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 29 Jul 2024 19:09:34 -0700 Subject: [PATCH 1322/2019] [Stdlib] Make arg type infer only for inline_assembly and llvm_intrinsic Users should not pass in the arg types for inline_assembly and llvm_intrinsic so make them always inferred. MODULAR_ORIG_COMMIT_REV_ID: bfe72b3c19794be2ebb52f82ca111197d7cdc809 --- stdlib/src/sys/_assembly.mojo | 54 ++++++++++---------- stdlib/src/sys/intrinsics.mojo | 92 +++++++++++++++++----------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/stdlib/src/sys/_assembly.mojo b/stdlib/src/sys/_assembly.mojo index aaa9b3808e..1d3487dcd1 100644 --- a/stdlib/src/sys/_assembly.mojo +++ b/stdlib/src/sys/_assembly.mojo @@ -73,9 +73,9 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ + arg0_type: AnyTrivialRegType, //, asm: StringLiteral, result_type: AnyTrivialRegType, - arg0_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r", @@ -126,10 +126,10 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ + arg0_type: AnyTrivialRegType, + arg1_type: AnyTrivialRegType, //, asm: StringLiteral, result_type: AnyTrivialRegType, - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r", @@ -180,11 +180,11 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, + arg2_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r", @@ -235,12 +235,12 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, + arg3_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r", @@ -293,13 +293,13 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, + arg4_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r", @@ -356,14 +356,14 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, arg3_type: AnyTrivialRegType, arg4_type: AnyTrivialRegType, - arg5_type: AnyTrivialRegType, + arg5_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r", @@ -421,15 +421,15 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, arg3_type: AnyTrivialRegType, arg4_type: AnyTrivialRegType, arg5_type: AnyTrivialRegType, - arg6_type: AnyTrivialRegType, + arg6_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r", @@ -488,8 +488,6 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, @@ -497,7 +495,9 @@ fn inlined_assembly[ arg4_type: AnyTrivialRegType, arg5_type: AnyTrivialRegType, arg6_type: AnyTrivialRegType, - arg7_type: AnyTrivialRegType, + arg7_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r,r", @@ -557,8 +557,6 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, @@ -567,7 +565,9 @@ fn inlined_assembly[ arg5_type: AnyTrivialRegType, arg6_type: AnyTrivialRegType, arg7_type: AnyTrivialRegType, - arg8_type: AnyTrivialRegType, + arg8_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r,r,r", @@ -628,8 +628,6 @@ fn inlined_assembly[ @always_inline("nodebug") fn inlined_assembly[ - asm: StringLiteral, - result_type: AnyTrivialRegType, arg0_type: AnyTrivialRegType, arg1_type: AnyTrivialRegType, arg2_type: AnyTrivialRegType, @@ -639,7 +637,9 @@ fn inlined_assembly[ arg6_type: AnyTrivialRegType, arg7_type: AnyTrivialRegType, arg8_type: AnyTrivialRegType, - arg9_type: AnyTrivialRegType, + arg9_type: AnyTrivialRegType, //, + asm: StringLiteral, + result_type: AnyTrivialRegType, /, *, constraints: StringLiteral = "r,r,r,r,r,r,r,r,r,r", diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index d130f40d9e..760c2a7d27 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -81,9 +81,9 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ + T0: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, - T0: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0) -> type: """Calls an LLVM intrinsic with one argument. @@ -92,9 +92,9 @@ fn llvm_intrinsic[ arg0. Parameters: + T0: The type of the first argument to the intrinsic (arg0). intrin: The name of the llvm intrinsic. type: The return type of the intrinsic. - T0: The type of the first argument to the intrinsic (arg0). has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: @@ -136,10 +136,10 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ + T0: AnyTrivialRegType, + T1: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1) -> type: """Calls an LLVM intrinsic with two arguments. @@ -148,10 +148,10 @@ fn llvm_intrinsic[ arguments arg0 and arg1. Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: @@ -194,11 +194,11 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, - T2: AnyTrivialRegType, + T2: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2) -> type: """Calls an LLVM intrinsic with three arguments. @@ -207,11 +207,11 @@ fn llvm_intrinsic[ arguments arg0, arg1 and arg2. Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: @@ -258,12 +258,12 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, - T3: AnyTrivialRegType, + T3: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: """Calls an LLVM intrinsic with four arguments. @@ -272,12 +272,12 @@ fn llvm_intrinsic[ arguments arg0, arg1, arg2 and arg3. Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). T3: The type of the fourth argument to the intrinsic (arg3). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: @@ -326,13 +326,13 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, T3: AnyTrivialRegType, - T4: AnyTrivialRegType, + T4: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: """Calls an LLVM intrinsic with five arguments. @@ -341,13 +341,13 @@ fn llvm_intrinsic[ arguments arg0, arg1, arg2, arg3 and arg4. Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). T3: The type of the fourth argument to the intrinsic (arg3). T4: The type of the fifth argument to the intrinsic (arg4). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. @@ -393,14 +393,14 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, T3: AnyTrivialRegType, T4: AnyTrivialRegType, - T5: AnyTrivialRegType, + T5: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: """Calls an LLVM intrinsic with six arguments. @@ -409,14 +409,14 @@ fn llvm_intrinsic[ arguments arg0, arg1, ..., arg5 Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). T3: The type of the fourth argument to the intrinsic (arg3). T4: The type of the fifth argument to the intrinsic (arg4). T5: The type of the sixth argument to the intrinsic (arg5). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. @@ -464,15 +464,15 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, T3: AnyTrivialRegType, T4: AnyTrivialRegType, T5: AnyTrivialRegType, - T6: AnyTrivialRegType, + T6: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: """Calls an LLVM intrinsic with seven arguments. @@ -481,8 +481,6 @@ fn llvm_intrinsic[ arguments arg0, arg1, ..., arg6 Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). @@ -490,6 +488,8 @@ fn llvm_intrinsic[ T4: The type of the fifth argument to the intrinsic (arg4). T5: The type of the sixth argument to the intrinsic (arg5). T6: The type of the seventh argument to the intrinsic (arg6). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. @@ -537,8 +537,6 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, @@ -546,7 +544,9 @@ fn llvm_intrinsic[ T4: AnyTrivialRegType, T5: AnyTrivialRegType, T6: AnyTrivialRegType, - T7: AnyTrivialRegType, + T7: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ]( arg0: T0, @@ -564,8 +564,6 @@ fn llvm_intrinsic[ arguments arg0, arg1, ..., arg7 Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). @@ -574,6 +572,8 @@ fn llvm_intrinsic[ T5: The type of the sixth argument to the intrinsic (arg5). T6: The type of the seventh argument to the intrinsic (arg6). T7: The type of the eighth argument to the intrinsic (arg7). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: @@ -621,8 +621,6 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, @@ -631,7 +629,9 @@ fn llvm_intrinsic[ T5: AnyTrivialRegType, T6: AnyTrivialRegType, T7: AnyTrivialRegType, - T8: AnyTrivialRegType, + T8: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ]( arg0: T0, @@ -650,8 +650,6 @@ fn llvm_intrinsic[ arguments arg0, arg1, ..., arg8 Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). @@ -661,6 +659,8 @@ fn llvm_intrinsic[ T6: The type of the seventh argument to the intrinsic (arg6). T7: The type of the eighth argument to the intrinsic (arg7). T8: The type of the ninth argument to the intrinsic (arg8). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: @@ -710,8 +710,6 @@ fn llvm_intrinsic[ @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, - type: AnyTrivialRegType, T0: AnyTrivialRegType, T1: AnyTrivialRegType, T2: AnyTrivialRegType, @@ -721,7 +719,9 @@ fn llvm_intrinsic[ T6: AnyTrivialRegType, T7: AnyTrivialRegType, T8: AnyTrivialRegType, - T9: AnyTrivialRegType, + T9: AnyTrivialRegType, //, + intrin: StringLiteral, + type: AnyTrivialRegType, has_side_effect: Bool = True, ]( arg0: T0, @@ -741,8 +741,6 @@ fn llvm_intrinsic[ arguments arg0, arg1, ..., arg10 Parameters: - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). @@ -753,6 +751,8 @@ fn llvm_intrinsic[ T7: The type of the eighth argument to the intrinsic (arg7). T8: The type of the ninth argument to the intrinsic (arg8). T9: The type of the tenth argument to the intrinsic (arg9). + intrin: The name of the llvm intrinsic. + type: The return type of the intrinsic. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. From befd811be73c3ca834af037c5ba808e2e1025ec2 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 29 Jul 2024 23:38:00 -0700 Subject: [PATCH 1323/2019] [Docs] Update pointer docs & dedupe changelog entries. Update the Unsafe Pointers chapter for the removal of DTypePointer, as well as the pointer aliasing changes. Also edit & consolidate unreleased changelog entries for pointer changes. MODULAR_ORIG_COMMIT_REV_ID: 96e82e7ab0d29559a1b8016004b9b69ad00c3c40 --- docs/changelog.md | 174 +++++++++++++------------- docs/manual/pointers.ipynb | 248 +++++++++++++++++++++++++++---------- 2 files changed, 270 insertions(+), 152 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ac531666b5..90a958b23d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -59,12 +59,6 @@ what we publish. `yourType.__setitem__(1, 2, val=3)`. This fixes [Issue #248](https://github.com/modularml/mojo/issues/248). -- The pointer variants (`DTypePointer`, `UnsafePointer`, etc.) now have a new - `exclusive: Bool = False` parameter. Setting this parameter to true tells the - compiler that the user knows this pointer and all those derived from it have - exclusive access to the underlying memory allocation. The compiler is not - guaranteed to do anything with this information. - - `Optional` values are now equality comparable with `==` and `!=` when their element type is equality comparable. @@ -159,7 +153,8 @@ what we publish. c = MyStruct(x, x) # Infers size=2 from 'self' type. ``` -- The `Reference` type (and many iterators) now use "inferred" parameters to +- The `Reference` type (and many iterators) now use + [infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) to represent the mutability of their lifetime, simplifying the interface. - `Dict` now implements `setdefault`, to get a value from the dictionary by @@ -362,7 +357,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. It is now forbidden to convert a non-pointer-typed value derived from a Mojo-allocated pointer, such as an integer address, to a pointer-typed value. "Derived" means there is overlap in the bits of the non-pointer-typed value - with the original pointer value. + with the original pointer value. Accordingly, the `UnsafePointer` constructor + that took an `address` keyword argument has been removed. It is still possible to make this conversion in certain cases where it is absolutely necessary, such as interoperating with other languages like Python. @@ -370,8 +366,88 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. non-pointer-typed value does not alias any Mojo-derived pointer and that any external function calls have arbitrary memory effects. -- `await` on a coroutine now consumes it. This strengthens the invariant that - coroutines can only be awaited once. +- `DTypePointer` , `LegacyPointer` and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) instead. For more + information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in + the Mojo Manual. + + Functions that previously took a `DTypePointer` now take an + equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to + `UnsafePointer` is: + + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] + ``` + + There could be places that you have code of the form: + + ```mojo + fn f(ptr: DTypePointer): + ``` + + which is equivalent to `DTypePointer[*_]`. In this case you would have to add + an infer-only `type` parameter to the function: + + ```mojo + fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): + ``` + + because we can’t have an unbound parameter inside the struct. + + There could also be places where you use + `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to + change these to `UnsafePointer[NoneType/Int]`. But since these are not an + `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to + appropriate types. + +- The `DTypePointer` `load()`, `store()`, and `prefetch()` methods have been + moved to `SIMD` and now take an + `UnsafePointer` as an argument. Instead of using `ptr.load[width=4](offset)` + one should use `SIMD[size=4].load(ptr, offset)`. Note the default load width + before was 1, but the default size of `SIMD` is the size of the SIMD type. The + default store size is the size of the `SIMD` value to be stored. + +- `UnsafePointer` now supports `simd_strided_load()`, `simd_strided_store()`, + `gather()`, and `scatter()` when the underlying type is `Scalar[DType]`. + +- The global functions for working with `UnsafePointer` have transitioned to + being methods through the use of conditional conformances: + + - `destroy_pointee(p)` => `p.destroy_pointee()` + - `move_from_pointee(p)` => `p.take_pointee()` + - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` + - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` + - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` + +- The `UnsafePointer.offset()` method has been removed. Use + [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. + + ```mojo + new_ptr = ptr.offset(1) + ``` + + Becomes: + + ```mojo + new_ptr = ptr + 1 + ``` + +- `UnsafePointer` has a new + `exclusive: Bool = False` parameter. Setting this parameter to true tells the + compiler that the user knows this pointer and all those derived from it have + exclusive access to the underlying memory allocation. The compiler is not + guaranteed to do anything with this information. + +- It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + `UnsafePointer.address_of(someRef[])` which makes the code explicit that the + `UnsafePointer` gets the address of what the reference points to. + +- `sort` no longer takes `LegacyPointer`. The current API supports: + - `sort(list)` just plain list + - `sort[type, cmp_fn](list)` list with custom compare function + - `sort(ptr, len)` a pointer and length (can change to Span in future) + - `sort[type, cmp_fn](ptr, len)` above with custom compare - Continued transition to `UnsafePointer` and unsigned byte type for strings: @@ -380,6 +456,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) +- `await` on a coroutine now consumes it. This strengthens the invariant that + coroutines can only be awaited once. + - `print()` now requires that its arguments conform to the `Formattable` trait. This enables efficient stream-based writing by default, avoiding unnecessary intermediate String heap allocations. @@ -443,21 +522,6 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. take a `UnsafePointer[C_char]`, reflecting their use for compatibility with C APIs. -- The global functions for working with `UnsafePointer` have transitioned to - being methods through the use of conditional conformances: - - - `destroy_pointee(p)` => `p.destroy_pointee()` - - `move_from_pointee(p)` => `p.take_pointee()` - - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` - - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` - - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` - -- `DTypePointer.load/store/prefetch` has been now moved to `SIMD`. Instead of - using `ptr.load[width=4](offset)` one should use `SIMD[size=4].load(ptr, offset)`. - Note the default load width before was 1, but the default size of `SIMD` is - the size of the SIMD type. - The default store size is the size of the `SIMD` value to be stored. - - `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements a constructor which accepts optional values. `Slice._has_end()` has also been removed since a Slice with no end is now represented by an empty `Slice.end` @@ -497,33 +561,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - The `time.now()` function has been deprecated. Please use `time.perf_counter` or `time.perf_counter_ns` instead. -- `LegacyPointer.load/store` are now removed. It's use is replaced with - `__getitem__` or `__setitem__`. - -- `memcmp`, `memset` and `memset_zero` no longer take in `LegacyPointer`, - instead, use `UnsafePointer`. - - A few bit functions have been renamed for clarity: - `countl_zero` -> `count_leading_zeros` - `countr_zero` -> `count_trailing_zeros` -- `sort` no longer takes `LegacyPointer`. The current API supports: - - `sort(list)` just plain list - - `sort[type, cmp_fn](list)` list with custom compare function - - `sort(ptr, len)` a pointer and length (can change to Span in future) - - `sort[type, cmp_fn](ptr, len)` above with custom compare - -- `memcpy` with `LegacyPointer` has been removed. Please use the `UnsafePointer` - overload instead. - -- `LegacyPointer` and `Pointer` has been removed. Please use `UnsafePointer` - instead. - -- `UnsafePointer` now supports `simd_strided_load/store`, `gather`, and `scatter` - when the underlying type is `Scalar[DType]`. - -- `SIMD.load/store` now supports `UnsafePointer` overloads. - - Now that we have a `UInt` type, use this to represent the return type of hash. In general, hashes should be an unsigned integer, and can also lead to improved performance in certain cases. @@ -537,53 +578,16 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. -- `DTypePointer` has been removed. - Please use [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) - instead. Functions that previously took a `DTypePointer` now take an - equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to - `UnsafePointer` is: - - ```mojo - DTypePointer[type] -> UnsafePointer[Scalar[type]] - ``` - - There could be places that you have code of the form - - ```mojo - fn f(ptr: DTypePointer): - ``` - - which is equivalent to `DTypePointer[*_]`. In this case you would have to add - a type parameter to the function: - - ```mojo - fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): - ``` - - because we can’t have an unbound struct inside the parameter. - - There could also be places where you use `DTypePointer[Scalar[DType.invalid/index]]`, - and it would be natural to change it to `UnsafePointer[NoneType/Int]`. But - since they are not an `UnsafePointer` that stores a `Scalar`, you might have to - `rebind/bitcast` to appropriate types. - ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from the compiler, please switch to using `fn __init__(inout self, ...):` instead. -- It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the - `UnsafePointer.address_of(someRef[])` which makes the code explicit that the - `UnsafePointer` gets the address of what the reference points to. - - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. - Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. -- Removed `UnsafePointer.offset(offset:Int)`. - - Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 18b53a0bd9..c996d6ffd4 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -47,7 +47,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -79,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -236,7 +236,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -252,64 +252,6 @@ "of time, since you're pointing to an existing value." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "#### Initializing from an address\n", - "\n", - "When exchanging data with other programming languages, you may need to construct\n", - "an `UnsafePointer` from an address. For example, if you're working with a\n", - "pointer allocated by a C or C++ library, or a Python object that implements the\n", - "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n", - "you can construct an `UnsafePointer` to access the data from the Mojo side.\n", - "\n", - "You can construct an `UnsafePointer` from an integer address using the `address`\n", - "keyword argument. For example, the following code creates a NumPy array and then\n", - "accesses the data using a Mojo pointer:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, 3, 4, 5, 6, 7, 8, 9, " - ] - } - ], - "source": [ - "from python import Python\n", - "from memory.unsafe_pointer import UnsafePointer\n", - "\n", - "def share_array():\n", - " np = Python.import_module(\"numpy\")\n", - " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - " ptr = arr.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.int64]()\n", - " for i in range(9):\n", - " print(ptr[i], end=\", \")\n", - "\n", - "share_array()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When dealing with memory allocated elsewhere, you need to be aware of who's\n", - "responsible for freeing the memory. Freeing memory allocated elsewhere \n", - "can result in undefined behavior.\n", - "\n", - "You also need to be aware of the format of the data stored in memory, including\n", - "data types and byte order. For more information, see \n", - "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -324,14 +266,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "110\n" + "5\n" ] } ], @@ -465,7 +407,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -483,7 +425,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -500,6 +442,110 @@ " print(float_ptr[offset], end=\", \")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with foreign pointers\n", + "\n", + "When exchanging data with other programming languages, you may need to construct\n", + "an `UnsafePointer` from an a foreign pointer. Mojo restricts creating \n", + "`UnsafePointer` instances from arbitrary addresses, to avoid users accidentally \n", + "creating pointers that _alias_ each other (that is, two pointers that refer to\n", + "the same location). However, there are specific methods you can use to get an\n", + "`UnsafePointer` from a Python or C/C++ pointer.\n", + "\n", + "When dealing with memory allocated elsewhere, you need to be aware of who's\n", + "responsible for freeing the memory. Freeing memory allocated elsewhere \n", + "can result in undefined behavior.\n", + "\n", + "You also need to be aware of the format of the data stored in memory, including\n", + "data types and byte order. For more information, see \n", + "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Creating a Mojo pointer from a Python pointer\n", + "\n", + "The `PythonObject` type defines\n", + "an [`unsafe_get_as_pointer()`](/mojo/stdlib/python/object/PythonObject#unsafe_get_as_pointer) \n", + "method to construct an `UnsafePointer` from a Python address.\n", + "\n", + "For example, the following code creates a NumPy array and then accesses the\n", + "data using a Mojo pointer:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, 3, 4, 5, 6, 7, 8, 9, " + ] + } + ], + "source": [ + "from python import Python\n", + "from memory.unsafe_pointer import UnsafePointer\n", + "\n", + "def share_array():\n", + " np = Python.import_module(\"numpy\")\n", + " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", + " ptr = arr.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.int64]()\n", + " for i in range(9):\n", + " print(ptr[i], end=\", \")\n", + "\n", + "share_array()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NumPy arrays implement the\n", + "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n", + "which defines the `__array_interface__` object used in the example, where \n", + "`__array_interface__[\"data\"][0]` is a Python integer holding the address of the\n", + "underlying data. The `unsafe_get_as_pointer()` method constructs an \n", + "`UnsafePointer` to this address." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Working with C/C++ pointers\n", + "\n", + "If you call a C/C++ function that returns a pointer using the\n", + "[`external_call`](/mojo/stdlib/sys/ffi/external_call) function, you can specify\n", + "the return type as an `UnsafePointer`, and Mojo will handle the type conversion\n", + "for you." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "from sys.ffi import external_call\n", + "\n", + "def get_foreign_pointer() -> UnsafePointer[Int]:\n", + " ptr = external_call[\n", + " \"my_c_function\", # external function name\n", + " UnsafePointer[Int] # return type\n", + " ]()\n", + " return ptr" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -519,7 +565,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -568,6 +614,74 @@ "```" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with SIMD vectors\n", + "\n", + "The standard library provides a few special methods for loading and storing SIMD\n", + "values. The `SIMD` type includes static \n", + "[`load()`](/mojo/stdlib/builtin/simd/SIMD#load) and \n", + "[`store()`](/mojo/stdlib/builtin/simd/SIMD#store) methods for performing aligned\n", + "loads and stores of scalar values.\n", + "\n", + "The `UnsafePointer` type includes various methods of loading and storing SIMD\n", + "values to memory. In particular: strided load/store and gather/scatter.\n", + "\n", + "Strided load loads values from memory into a SIMD vector using an offset (the\n", + "\"stride\") between successive memory addresses. This can be useful for\n", + "extracting rows or columns from tabular data, or for extracting individual\n", + "values from structured data. For example, consider the data for an RGB image,\n", + "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n", + "you want to access just the red values, you can use a strided load or store.\n", + "\n", + "
    \n", + "\n", + " ![](./images/strided-load-storage.png#light)\n", + " ![](./images/strided-load-storage-dark.png#dark)\n", + "\n", + "
    Figure 4. Strided load
    \n", + "
    \n", + "\n", + "The following function uses the \n", + "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#simd_strided_load)\n", + "and \n", + "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#simd_strided_store)\n", + "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", + "that this function only handles images where the number of pixels is evenly\n", + "divisible by eight.)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def invert_red_channel(ptr: UnsafePointer[UInt8], pixel_count: Int):\n", + " # number of values loaded or stored at a time\n", + " alias simd_width = 8\n", + " # bytes per pixel, which is also the stride size\n", + " bpp = 3\n", + " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", + " red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n", + " # Invert values and store them in their original locations\n", + " ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The [`gather()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#gather) and\n", + "[`scatter()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#scatter) methods\n", + "let you load or store a set of values that are stored in arbitrary locations. \n", + "You do this by passing in a SIMD vector of _offsets_ to the current pointer. For\n", + "example, when using `gather()`, the nth value in the vector is loaded\n", + "from (pointer address) + offset[n]." + ] + }, { "cell_type": "markdown", "metadata": {}, From 82d6cd75491540c85a43d0eb0eefca231e6f5a86 Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 30 Jul 2024 07:33:34 -0700 Subject: [PATCH 1324/2019] [External] [stdlib] Add equality methods to `Reference`. (#44131) [External] [stdlib] Add equality methods to `Reference`. Make `Reference` conform to `EqualityComparable` by implementing `__eq__` and `__ne__` for `Reference`. Co-authored-by: Helehex Closes modularml/mojo#3298 MODULAR_ORIG_COMMIT_REV_ID: f785dafde5496ba728031375657eba432e11b47e --- stdlib/src/memory/reference.mojo | 26 ++++++++++++++++++++++++++ stdlib/test/memory/test_reference.mojo | 12 +++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index d8bb363ddb..5c2ccb6097 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -332,3 +332,29 @@ struct Reference[ The MLIR reference for the Mojo compiler to use. """ return __get_litref_as_mvalue(self.value) + + @always_inline("nodebug") + fn __eq__(self, rhs: Reference[type, _, address_space]) -> Bool: + """Returns True if the two pointers are equal. + + Args: + rhs: The value of the other pointer. + + Returns: + True if the two pointers are equal and False otherwise. + """ + return UnsafePointer( + __mlir_op.`lit.ref.to_pointer`(self.value) + ) == UnsafePointer(__mlir_op.`lit.ref.to_pointer`(rhs.value)) + + @always_inline("nodebug") + fn __ne__(self, rhs: Reference[type, _, address_space]) -> Bool: + """Returns True if the two pointers are not equal. + + Args: + rhs: The value of the other pointer. + + Returns: + True if the two pointers are not equal and False otherwise. + """ + return not (self == rhs) diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo index ba26997bf7..8eaa4df330 100644 --- a/stdlib/test/memory/test_reference.mojo +++ b/stdlib/test/memory/test_reference.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal +from testing import assert_equal, assert_true def test_copy_reference_explicitly(): @@ -27,5 +27,15 @@ def test_copy_reference_explicitly(): assert_equal(c[][0], 4) +def test_equality(): + var a = List[Int](1, 2, 3) + var b = List[Int](4, 5, 6) + + assert_true(Reference(a) == Reference(a)) + assert_true(Reference(b) == Reference(b)) + assert_true(Reference(a) != Reference(b)) + + def main(): test_copy_reference_explicitly() + test_equality() From a3d0302b8315826d12cb5d8cfd7c906e06e43de7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Jul 2024 09:52:02 -0500 Subject: [PATCH 1325/2019] [stdlib] Use `@parameter for` in `InlineArray.__contains__` Now that `@parameter for` doesn't crash here anymore, apply it in `InlineArray.__contains__`. MODULAR_ORIG_COMMIT_REV_ID: 5b3b6b45fe48d70adc52ec578df7ae19d8fac348 --- stdlib/src/utils/static_tuple.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 7d58dd1169..2bcf3b87b0 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -503,7 +503,7 @@ struct InlineArray[ "T must be equal to Self.ElementType", ]() - # TODO: use @parameter for soon once it stabilizes a bit + @parameter for i in range(size): if ( rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] From 6bc7b124b0fe868426b22ee0307321ce2f66b674 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Jul 2024 10:10:00 -0500 Subject: [PATCH 1326/2019] [stdlib] Import what you use in `stdlib` When starting to pull on the thread for changing how builtin entities are parsed and auto-imported into ever Mojo program, this exposed many files in the `stdlib` are relying on entities getting transitive included on their behalf. Since the transitive closure import behavior will eventually go away when we're done, fix these uses to import what they use explicitly. MODULAR_ORIG_COMMIT_REV_ID: 37aaacde471043788e9235964cbb141ceb2d2bb0 --- stdlib/src/bit/bit.mojo | 2 +- stdlib/src/builtin/bool.mojo | 2 +- stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/file.mojo | 1 + stdlib/src/builtin/int.mojo | 1 + stdlib/src/builtin/reversed.mojo | 1 + stdlib/src/builtin/simd.mojo | 3 ++- stdlib/src/builtin/sort.mojo | 1 + stdlib/src/builtin/string.mojo | 5 +++-- stdlib/src/builtin/string_literal.mojo | 4 +++- stdlib/src/builtin/uint.mojo | 1 + stdlib/src/collections/counter.mojo | 1 + stdlib/src/collections/dict.mojo | 1 + stdlib/src/collections/list.mojo | 3 +-- stdlib/src/collections/optional.mojo | 1 + stdlib/src/math/math.mojo | 1 + stdlib/src/memory/maybe_uninitialized.mojo | 2 ++ stdlib/src/memory/memory.mojo | 2 +- stdlib/src/os/_linux_x86.mojo | 2 +- stdlib/src/os/os.mojo | 2 +- stdlib/src/os/path/path.mojo | 1 + stdlib/src/pathlib/path.mojo | 3 ++- stdlib/src/pwd/_linux.mojo | 1 + stdlib/src/pwd/_macos.mojo | 1 + stdlib/src/pwd/pwd.mojo | 3 +-- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/python/object.mojo | 1 + stdlib/src/python/python.mojo | 1 + stdlib/src/tempfile/tempfile.mojo | 2 +- stdlib/src/utils/_format.mojo | 1 + stdlib/src/utils/inline_string.mojo | 1 + stdlib/src/utils/lock.mojo | 2 ++ stdlib/src/utils/span.mojo | 1 + stdlib/src/utils/string_slice.mojo | 3 +++ stdlib/src/utils/stringref.mojo | 2 ++ stdlib/src/utils/variant.mojo | 1 + 36 files changed, 47 insertions(+), 17 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index d79f1a8e11..4fb32bcc5f 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -19,7 +19,7 @@ from bit import count_leading_zeros ``` """ -from sys import llvm_intrinsic +from sys import llvm_intrinsic, sizeof from sys.info import bitwidthof # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 7b634507fa..d760ce9767 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import Set +from collections import Set, List from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 6e9d8eb0a9..433d34b795 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -16,7 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import KeyElement -from sys import sizeof as _sizeof +from sys import sizeof as _sizeof, bitwidthof, os_is_windows alias _mIsSigned = UInt8(1) alias _mIsInteger = UInt8(1 << 7) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 9e2e6ee24e..4c9e727688 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -33,6 +33,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call +from utils import Span from memory import AddressSpace, UnsafePointer diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 714a92b30f..ad1c933c34 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -31,6 +31,7 @@ from utils import InlineArray from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select +from sys import triple_is_nvidia_cuda # ===----------------------------------------------------------------------=== # # Indexer diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 3f69e34ca2..fb5cd21e53 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -15,6 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ +from collections import Dict from collections.dict import _DictEntryIter, _DictKeyIter, _DictValueIter from collections.list import _ListIter diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6e27a9f9f0..00ed74f232 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -35,7 +35,7 @@ from builtin.dtype import _uint_type_of_width from builtin.hash import _hash_simd from memory import bitcast, UnsafePointer -from utils import InlineArray, StringSlice +from utils import InlineArray, StringSlice, StaticIntTuple from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan @@ -44,6 +44,7 @@ from utils.numerics import max_or_inf as _max_or_inf from utils.numerics import min_finite as _min_finite from utils.numerics import min_or_neg_inf as _min_or_neg_inf from utils.numerics import nan as _nan +from sys import sizeof, alignof from .dtype import ( _get_dtype_printf_format, diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 5cfbce4d5e..2a12e6eace 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -20,6 +20,7 @@ from sys import bitwidthof from bit import count_leading_zeros from memory import UnsafePointer +from utils import Span # ===----------------------------------------------------------------------===# # sort diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 0aeb35b963..873ae07efd 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -15,15 +15,16 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import KeyElement, List +from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic from sys.ffi import C_char from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy +from python import PythonObject -from utils import Span, StaticIntTuple, StringRef, StringSlice +from utils import Span, StaticIntTuple, StringRef, StringSlice, Variant from utils._format import Formattable, Formatter, ToFormatter from utils.string_slice import _utf8_byte_type, _StringSliceIter diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 650904b235..ff04253935 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -17,7 +17,9 @@ These are Mojo built-ins, so you don't need to import them. from sys.ffi import C_char -from utils import StringRef +from memory import memcpy +from collections import List +from utils import StringRef, Span, StringSlice from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 5075b5392f..ed9d4b44fa 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from builtin.format_int import _try_write_int from builtin.simd import _format_scalar +from sys import triple_is_nvidia_cuda, bitwidthof @lldb_formatter_wrapping_type diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index cb27ae22f5..8164cf8110 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from collections.dict import Dict, _DictKeyIter, _DictValueIter, _DictEntryIter +from utils import Variant @value diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 37b2d10c13..f574a67125 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -35,6 +35,7 @@ from builtin.value import StringableCollectionElement from .optional import Optional from bit import is_power_of_two +from memory import memcpy, bitcast trait KeyElement(CollectionElement, Hashable, EqualityComparable): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 3dd83fb545..44029d06b5 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -21,9 +21,8 @@ from collections import List from sys.intrinsics import _type_is_eq - +from os import abort from memory import Reference, UnsafePointer - from utils import Span from .optional import Optional diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 81f43b5153..17d5d6acdf 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -31,6 +31,7 @@ print(d) # prints 2 ``` """ +from os import abort from utils import Variant diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 07b683f78e..562c7096a6 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -32,6 +32,7 @@ from builtin._math import * from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply, _modf +from utils import Span from utils.index import StaticIntTuple from utils.numerics import FPUtils, isnan, nan from utils.static_tuple import StaticTuple diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index fe4ccc31eb..1c61bb3ea8 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from os import abort + struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): """A memory location that may or may not be initialized. diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 6529cd3480..a7199f4839 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -20,7 +20,7 @@ from memory import memcmp """ -from sys import llvm_intrinsic, sizeof, triple_is_nvidia_cuda +from sys import alignof, llvm_intrinsic, sizeof, triple_is_nvidia_cuda from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index c9cbd12ed1..e709df9866 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils.index import InlineArray +from utils import InlineArray from .fstat import stat_result diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 01e5d7457e..6ec9ef2234 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -20,7 +20,7 @@ from os import listdir """ from collections import List -from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda +from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call from sys.ffi import C_char from utils import InlineArray, StringRef diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 9aeac9d374..8cacd9af54 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -19,6 +19,7 @@ from os.path import isdir ``` """ +from collections import List from stat import S_ISDIR, S_ISLNK, S_ISREG from sys import has_neon, os_is_linux, os_is_macos, os_is_windows diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index e01844f8f6..6c44c798d3 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -14,8 +14,9 @@ """ import os +from collections import List from os import PathLike, listdir, stat_result -from sys import os_is_windows +from sys import os_is_windows, external_call from sys.ffi import C_char from builtin._location import __call_location, _SourceLocation diff --git a/stdlib/src/pwd/_linux.mojo b/stdlib/src/pwd/_linux.mojo index 51d800ec71..bef5c01387 100644 --- a/stdlib/src/pwd/_linux.mojo +++ b/stdlib/src/pwd/_linux.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from .pwd import Passwd from memory import UnsafePointer +from sys.ffi import C_char alias uid_t = Int32 alias gid_t = Int32 diff --git a/stdlib/src/pwd/_macos.mojo b/stdlib/src/pwd/_macos.mojo index 8bc2318a69..16d8690a51 100644 --- a/stdlib/src/pwd/_macos.mojo +++ b/stdlib/src/pwd/_macos.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from .pwd import Passwd from memory import UnsafePointer +from sys.ffi import C_char alias uid_t = Int32 alias gid_t = Int32 diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo index 34cf29a9f6..bc583cd616 100644 --- a/stdlib/src/pwd/pwd.mojo +++ b/stdlib/src/pwd/pwd.mojo @@ -16,8 +16,7 @@ # ===----------------------------------------------------------------------=== # from ._linux import _getpw_linux from ._macos import _getpw_macos -from sys.info import os_is_macos -from sys.info import os_is_linux +from sys import os_is_windows, os_is_macos, os_is_linux @value diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 6f33325719..e5586596b3 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -16,7 +16,7 @@ from os.path import dirname from pathlib import Path from sys import external_call from sys.arg import argv -from sys.ffi import DLHandle +from sys.ffi import DLHandle, C_char from memory import UnsafePointer diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 1b38ccee7a..98b70c3da7 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -21,6 +21,7 @@ from python import PythonObject from sys.intrinsics import _type_is_eq +from collections import Dict from utils import StringRef from ._cpython import CPython, PyObjectPtr diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 7f191b25d6..762cbf440b 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -19,6 +19,7 @@ from python import Python ``` """ +from collections import Dict from os.env import getenv from sys import external_call, sizeof from sys.ffi import _get_global diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index bd3c9d69f2..244f1e0e31 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -21,7 +21,7 @@ from tempfile import gettempdir import os import sys -from collections import Optional +from collections import Optional, List from pathlib import Path alias TMP_MAX = 10_000 diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index bfa928db63..e6b47d7808 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -15,6 +15,7 @@ themselves to a string. """ from builtin.io import _put +from memory import UnsafePointer # ===----------------------------------------------------------------------===# # Interface traits diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index df5365a9ec..d087047632 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -15,6 +15,7 @@ avoids heap allocations for short strings. """ +from os import abort from collections import Optional from sys import sizeof diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 8045c714d0..93beea947c 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -11,8 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from memory import UnsafePointer from os import Atomic from time import sleep +from sys import external_call # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 13d78d5ada..4d26b9f9cb 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -21,6 +21,7 @@ from utils import Span """ from . import InlineArray +from memory import Reference from sys.intrinsics import _type_is_eq diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 56d49f65d6..2bf3103e3a 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -23,6 +23,9 @@ from utils import StringSlice from bit import count_leading_zeros from utils import Span from builtin.string import _isspace +from collections import List +from memory import memcmp +from sys import simdwidthof alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index d24687ffa1..5a21c414f8 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -18,6 +18,8 @@ from builtin.dtype import _uint_type_of_width from builtin.string import _atol, _isspace from memory import UnsafePointer, memcmp from memory.memory import _memcmp_impl_unconstrained +from utils import StringSlice +from sys.ffi import C_char # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index f37e42b993..9c02cbc080 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -38,6 +38,7 @@ print(to_string(who_knows)) ``` """ +from os import abort from sys import alignof, sizeof from sys.intrinsics import _type_is_eq From bc60817fa2ce4f759badc02c6813fc29ab6bc6e6 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Jul 2024 10:51:23 -0500 Subject: [PATCH 1327/2019] [stdlib] Import what you use in tests When starting to pull on the thread for changing how builtin entities are parsed and auto-imported into every Mojo program, this exposed many files in the `stdlib` tests are relying on entities getting transitive included on their behalf. Since the transitive closure import behavior will eventually go away when we're done, fix these uses to import what they use explicitly. MODULAR_ORIG_COMMIT_REV_ID: bf22411b8f374ca96498cefbd6c908130ada5eda --- stdlib/test/builtin/test_issue_1505.mojo | 1 + stdlib/test/builtin/test_reversed.mojo | 1 + stdlib/test/builtin/test_simd.mojo | 3 +-- stdlib/test/builtin/test_sort_issue_1018.mojo | 1 + stdlib/test/collections/test_counter.mojo | 1 + stdlib/test/memory/test_unsafepointer.mojo | 2 +- stdlib/test/os/test_remove.mojo | 2 +- stdlib/test/pathlib/test_pathlib.mojo | 2 +- stdlib/test/python/test_python_object.mojo | 1 + stdlib/test/sys/test_intrinsics.mojo | 2 +- stdlib/test/tempfile/test_tempfile.mojo | 1 + stdlib/test/testing/test_assertion.mojo | 9 +++++---- stdlib/test/utils/test_string_slice.mojo | 2 +- 13 files changed, 17 insertions(+), 11 deletions(-) diff --git a/stdlib/test/builtin/test_issue_1505.mojo b/stdlib/test/builtin/test_issue_1505.mojo index 1f77f3debf..e092856f2b 100644 --- a/stdlib/test/builtin/test_issue_1505.mojo +++ b/stdlib/test/builtin/test_issue_1505.mojo @@ -15,6 +15,7 @@ from random import random_ui64 +from utils import StaticIntTuple from testing import assert_equal diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index 1c08dc2ac0..59b466da98 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from collections import Dict from testing import assert_equal diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index df45b35c87..c3ac385e72 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -22,8 +22,7 @@ from testing import ( assert_not_equal, assert_true, ) - -from utils import unroll +from utils import unroll, StaticIntTuple, InlineArray from utils.numerics import isfinite, isinf, isnan, nan diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index bea13f7d8c..c9d97d4be6 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -14,6 +14,7 @@ # RUN: %mojo %s | FileCheck %s from random import rand +from utils import Span fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: diff --git a/stdlib/test/collections/test_counter.mojo b/stdlib/test/collections/test_counter.mojo index e87a849771..54e984fcde 100644 --- a/stdlib/test/collections/test_counter.mojo +++ b/stdlib/test/collections/test_counter.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections.counter import Counter +from collections import Optional from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index e1cfdf6401..969ce6dfe4 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from memory import UnsafePointer +from memory import UnsafePointer, AddressSpace from test_utils import ExplicitCopyOnly, MoveCounter from testing import assert_equal, assert_not_equal, assert_true, assert_false diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index eef9ce3331..820e1d21bb 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from os import remove, unlink +from os import remove, unlink, PathLike from os.path import exists from pathlib import Path diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 2c039f907d..010ada600e 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -15,7 +15,7 @@ import os from pathlib import DIR_SEPARATOR, Path, cwd -from sys import env_get_string +from sys import env_get_string, os_is_windows from builtin._location import __source_location from testing import assert_equal, assert_false, assert_not_equal, assert_true diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index c0c7b01574..72f8efc640 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -13,6 +13,7 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s +from collections import Dict from python import Python, PythonObject from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index a0a365452a..149fbe8f2a 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -20,7 +20,7 @@ from sys import ( strided_store, ) -from memory import UnsafePointer +from memory import UnsafePointer, memset_zero from testing import assert_equal alias F32x4 = SIMD[DType.float32, 4] diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index ebe9e2497d..be8818b408 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s import os +from collections import Dict, Optional from os.path import exists from pathlib import Path from tempfile import NamedTemporaryFile, TemporaryDirectory, gettempdir, mkdtemp diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 120f851063..a19360cc63 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -25,6 +25,7 @@ from testing import ( from utils.numerics import inf, nan from builtin._location import _SourceLocation +from python import PythonObject @value @@ -67,22 +68,22 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:68:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:69:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:73:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:74:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:78:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:79:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:83:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:84:25: AssertionError:" in str(e)) def test_assert_almost_equal(): diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index ff6db549c4..24c03cee55 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -14,7 +14,7 @@ from testing import assert_equal, assert_true, assert_false -from utils import Span +from utils import Span, StringSlice from utils.string_slice import _is_valid_utf8 From 7e2ef9e3accdf0dce461c0788b19da24d6c5455e Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 30 Jul 2024 08:51:49 -0700 Subject: [PATCH 1328/2019] [Stdlib] Extend the SIMD cast operation MODULAR_ORIG_COMMIT_REV_ID: 1d75e0765d6c889c52f0c2588d97cb25c2009a63 --- stdlib/src/builtin/simd.mojo | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 00ed74f232..72db87bb5f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1453,18 +1453,23 @@ struct SIMD[type: DType, size: Int]( triple_is_nvidia_cuda() and type is DType.float32 and target is DType.bfloat16 - and size == 2 + and size >= 2 ): - var bf16x2_as_uint32 = inlined_assembly[ - "cvt.rn.bf16x2.f32 $0, $1, $2;", - UInt32, - constraints="=r,f,f", - has_side_effect=False, - ](rebind[Float32](self[1]), rebind[Float32](self[0])) + var res = SIMD[target, size]() - return rebind[SIMD[target, size]]( - bitcast[DType.bfloat16, 2](bf16x2_as_uint32) - ) + @parameter + for i in range(0, size, 2): + var bf16x2_as_uint32 = inlined_assembly[ + "cvt.rn.bf16x2.f32 $0, $1, $2;", + UInt32, + constraints="=r,f,f", + has_side_effect=False, + ](rebind[Float32](self[i + 1]), rebind[Float32](self[i])) + var val = bitcast[target, 2](bf16x2_as_uint32) + res[i] = val[0] + res[i + 1] = val[1] + + return res elif has_neon() and ( type is DType.bfloat16 or target == DType.bfloat16 From 23245a9c5f393ae440599da03c6d2de8136fdb3f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 30 Jul 2024 10:32:00 -0700 Subject: [PATCH 1329/2019] [******] Implement atomic max/min MODULAR_ORIG_COMMIT_REV_ID: d46e7ab3f76ffee23e2612aeaf2f24bf1dc30fd9 --- stdlib/src/builtin/dtype.mojo | 34 +++++++++++++ stdlib/src/os/atomic.mojo | 92 ++++++++++++++++++++++++++++------- 2 files changed, 108 insertions(+), 18 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 433d34b795..3baf48f0a5 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -551,6 +551,40 @@ fn _integral_type_of[type: DType]() -> DType: return type.invalid +# ===-------------------------------------------------------------------===# +# _unsigned_integral_type_of +# ===-------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn _unsigned_integral_type_of[type: DType]() -> DType: + """Gets the unsigned integral type which has the same bitwidth as + the input type.""" + + @parameter + if type.is_integral(): + return _uint_type_of_width[bitwidthof[type]()]() + + @parameter + if type is DType.bfloat16 or type is DType.float16: + return DType.uint16 + + @parameter + if type is DType.float32 or type is DType.tensor_float32: + return DType.uint32 + + @parameter + if type is DType.float64: + return DType.uint64 + + return type.invalid + + +# ===-------------------------------------------------------------------===# +# _scientific_notation_digits +# ===-------------------------------------------------------------------===# + + fn _scientific_notation_digits[type: DType]() -> StringLiteral: """Get the number of digits as a StringLiteral for the scientific notation representation of a float. diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index eb2908511d..6b7bd43d90 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -19,8 +19,9 @@ from os import Atomic ``` """ -from builtin.dtype import _integral_type_of +from builtin.dtype import _integral_type_of, _unsigned_integral_type_of from memory import UnsafePointer, bitcast +from sys.info import triple_is_nvidia_cuda struct Atomic[type: DType]: @@ -198,7 +199,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline - fn max(ptr: UnsafePointer[Scalar[type]], rhs: Scalar[type]): + fn max(ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): """Performs atomic in-place max on the pointer. Atomically replaces the current value pointer to by `ptr` by the result @@ -215,14 +216,7 @@ struct Atomic[type: DType]: """ constrained[type.is_numeric(), "the input type must be arithmetic"]() - var value_addr = ptr.bitcast[ - __mlir_type[`!pop.scalar<`, type.value, `>`] - ]() - _ = __mlir_op.`pop.atomic.rmw`[ - bin_op = __mlir_attr.`#pop`, - ordering = __mlir_attr.`#pop`, - _type = __mlir_type[`!pop.scalar<`, type.value, `>`], - ](value_addr.address, rhs.value) + _max_impl(ptr, rhs) @always_inline fn max(inout self, rhs: Scalar[type]): @@ -260,15 +254,9 @@ struct Atomic[type: DType]: ptr: The source pointer. rhs: Value to min. """ + constrained[type.is_numeric(), "the input type must be arithmetic"]() - var value_addr = ptr.bitcast[ - __mlir_type[`!pop.scalar<`, type.value, `>`] - ]() - _ = __mlir_op.`pop.atomic.rmw`[ - bin_op = __mlir_attr.`#pop`, - ordering = __mlir_attr.`#pop`, - _type = __mlir_type[`!pop.scalar<`, type.value, `>`], - ](value_addr.address, rhs.value) + _min_impl(ptr, rhs) @always_inline fn min(inout self, rhs: Scalar[type]): @@ -324,3 +312,71 @@ fn _compare_exchange_weak_integral_impl[ if not ok: expected = value_addr[] return ok + + +@always_inline +fn _max_impl_base[ + type: DType, // +](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): + var value_addr = ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]() + _ = __mlir_op.`pop.atomic.rmw`[ + bin_op = __mlir_attr.`#pop`, + ordering = __mlir_attr.`#pop`, + _type = __mlir_type[`!pop.scalar<`, type.value, `>`], + ](value_addr.address, rhs.value) + + +@always_inline +fn _min_impl_base[ + type: DType, // +](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): + var value_addr = ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]() + _ = __mlir_op.`pop.atomic.rmw`[ + bin_op = __mlir_attr.`#pop`, + ordering = __mlir_attr.`#pop`, + _type = __mlir_type[`!pop.scalar<`, type.value, `>`], + ](value_addr.address, rhs.value) + + +@always_inline +fn _max_impl[ + type: DType, // +](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): + @parameter + if triple_is_nvidia_cuda() and type.is_floating_point(): + alias integral_type = _integral_type_of[type]() + alias unsigned_integral_type = _unsigned_integral_type_of[type]() + if rhs >= 0: + _max_impl_base( + ptr.bitcast[integral_type](), bitcast[integral_type](rhs) + ) + return + _min_impl_base( + ptr.bitcast[unsigned_integral_type](), + bitcast[unsigned_integral_type](rhs), + ) + return + + _max_impl_base(ptr, rhs) + + +@always_inline +fn _min_impl[ + type: DType, // +](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): + @parameter + if triple_is_nvidia_cuda() and type.is_floating_point(): + alias integral_type = _integral_type_of[type]() + alias unsigned_integral_type = _unsigned_integral_type_of[type]() + if rhs >= 0: + _min_impl_base( + ptr.bitcast[integral_type](), bitcast[integral_type](rhs) + ) + return + _max_impl_base( + ptr.bitcast[unsigned_integral_type](), + bitcast[unsigned_integral_type](rhs), + ) + return + + _min_impl_base(ptr, rhs) From 2520ea4036db080e0f8533b24258eaddf707f4e5 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 30 Jul 2024 10:39:50 -0700 Subject: [PATCH 1330/2019] [Stdlib] Make has_side_effect a keyword arg, NFC Change has_side_effect to be a keyword arg for the llvm_intrinsic op. MODULAR_ORIG_COMMIT_REV_ID: 60b33771a20840f23eaecf91cc1c7c696bd5d1d9 --- stdlib/src/sys/intrinsics.mojo | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 760c2a7d27..0614c09007 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -32,7 +32,10 @@ from memory import AddressSpace, UnsafePointer @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, type: AnyTrivialRegType, has_side_effect: Bool = True + intrin: StringLiteral, + type: AnyTrivialRegType, + *, + has_side_effect: Bool = True, ]() -> type: """Calls an LLVM intrinsic with no arguments. @@ -84,6 +87,7 @@ fn llvm_intrinsic[ T0: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0) -> type: """Calls an LLVM intrinsic with one argument. @@ -140,6 +144,7 @@ fn llvm_intrinsic[ T1: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0, arg1: T1) -> type: """Calls an LLVM intrinsic with two arguments. @@ -199,6 +204,7 @@ fn llvm_intrinsic[ T2: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2) -> type: """Calls an LLVM intrinsic with three arguments. @@ -264,6 +270,7 @@ fn llvm_intrinsic[ T3: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: """Calls an LLVM intrinsic with four arguments. @@ -333,6 +340,7 @@ fn llvm_intrinsic[ T4: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: """Calls an LLVM intrinsic with five arguments. @@ -401,6 +409,7 @@ fn llvm_intrinsic[ T5: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: """Calls an LLVM intrinsic with six arguments. @@ -473,6 +482,7 @@ fn llvm_intrinsic[ T6: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: """Calls an LLVM intrinsic with seven arguments. @@ -547,6 +557,7 @@ fn llvm_intrinsic[ T7: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ]( arg0: T0, @@ -632,6 +643,7 @@ fn llvm_intrinsic[ T8: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ]( arg0: T0, @@ -722,6 +734,7 @@ fn llvm_intrinsic[ T9: AnyTrivialRegType, //, intrin: StringLiteral, type: AnyTrivialRegType, + *, has_side_effect: Bool = True, ]( arg0: T0, From 1627e1d7c9bf6f22771a93b73aee7660b62964bc Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 30 Jul 2024 13:37:05 -0500 Subject: [PATCH 1331/2019] [stdlib] Remove unnecessary `+` in `LITRefPackHelper` There's no special parsing of `+` in this MLIR type, so remove the superfluous `+` in the address space specification for `_LITRefPackHelper`. Do the same for the use of the MLIR type in `Tuple`. MODULAR_ORIG_COMMIT_REV_ID: 9125353e6803b4e9cbb125c90df2dbd559576e5c --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index df3b19ae14..df0e3c8d81 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -454,7 +454,7 @@ struct _LITRefPackHelper[ `, `, lifetime, `, `, - +address_space, + address_space, `>`, ] diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 3e9b6b7c36..4ff107102d 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -38,7 +38,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): `!kgen.pack<:!kgen.variadic<`, Movable, `> `, - +element_types, + element_types, `>`, ] From 4120c8737f63fa409f7022465b8160da564ac8a1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 30 Jul 2024 14:29:00 -0500 Subject: [PATCH 1332/2019] [stdlib] cleanup: Implement {Int, UInt}.format_to in terms of SIMD.format_to + cleanups This reduces the number of places that `@parameter if` branching is needed to support printing on GPUs. * Make `dtype` param of `_format_scalar` infer-only * Simplify use of Span and StringSlice in _format_scalar * Use `InlineArray.unsafe_ptr()` instead of casting `InlineArray` itself MODULAR_ORIG_COMMIT_REV_ID: faff10ab2352d24550ba97ab525e295059d8ee90 --- stdlib/src/builtin/int.mojo | 13 +-------- stdlib/src/builtin/simd.mojo | 55 +++++++++++++++++++----------------- stdlib/src/builtin/uint.mojo | 32 +++++++-------------- 3 files changed, 40 insertions(+), 60 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index ad1c933c34..f7c14bead2 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -18,10 +18,8 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement from builtin._math import Ceilable, CeilDivable, Floorable, Truncable -from builtin.format_int import _try_write_int from builtin.hash import _hash_simd from builtin.io import _snprintf -from builtin.simd import _format_scalar from builtin.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, @@ -1130,16 +1128,7 @@ struct Int( writer: The formatter to write to. """ - @parameter - if triple_is_nvidia_cuda(): - var err = _try_write_int(writer, Int64(self)) - if err: - abort( - "unreachable: unexpected write int failure condition: " - + str(err.value()) - ) - else: - _format_scalar(writer, Int64(self)) + writer.write(Int64(self)) @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 72db87bb5f..958aa46262 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -33,6 +33,7 @@ from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from builtin.hash import _hash_simd +from builtin.format_int import _try_write_int from memory import bitcast, UnsafePointer from utils import InlineArray, StringSlice, StaticIntTuple @@ -1540,29 +1541,34 @@ struct SIMD[type: DType, size: Int]( @parameter if triple_is_nvidia_cuda(): + # FIXME(MSTDL-406): + # The uses of `printf` below prints "out of band" with the + # `Formatter` passed in, meaning this will only work if + # `Formatter` is an unbuffered wrapper around printf (which + # Formatter.stdout currently is by default). + # + # This is a workaround to permit debug formatting of + # floating-point values on GPU, where printing to stdout + # is the only way the Formatter framework is currently + # used. @parameter - if ( - type is DType.float16 - or type is DType.bfloat16 - or type is DType.float32 - ): - # We need to cast the value to float64 to print it. - _printf["%g"](element.cast[DType.float64]()) - elif type.is_floating_point(): + if type is DType.float64: # get_dtype_printf_format hardcodes 17 digits of precision. _printf["%g"](element) + elif type.is_floating_point(): + # We need to cast the value to float64 to print it, to avoid + # an ABI mismatch. + _printf["%g"](element.cast[DType.float64]()) + elif type.is_integral(): + var err = _try_write_int(writer, element) + if err: + abort( + "unreachable: unexpected write int failure" + " condition: " + + str(err.value()) + ) else: - # FIXME(MSTDL-406): - # This prints "out of band" with the `Formatter` passed - # in, meaning this will only work if `Formatter` is an - # unbuffered wrapper around printf (which Formatter.stdout - # currently is by default). - # - # This is a workaround to permit debug formatting of - # floating-point values on GPU, where printing to stdout - # is the only way the Formatter framework is currently - # used. _printf[_get_dtype_printf_format[type]()](element) else: @@ -1571,7 +1577,7 @@ struct SIMD[type: DType, size: Int]( alias float_format = "%." + _scientific_notation_digits[ type ]() + "e" - _format_scalar[type, float_format](writer, element) + _format_scalar[float_format](writer, element) else: _format_scalar(writer, element) @@ -2999,12 +3005,10 @@ fn _simd_apply[ # ===----------------------------------------------------------------------=== # -# _format_scalar -# ===----------------------------------------------------------------------=== # fn _format_scalar[ - dtype: DType, + dtype: DType, //, float_format: StringLiteral = "%.17g", ](inout writer: Formatter, value: Scalar[dtype]): # Stack allocate enough bytes to store any formatted Scalar value of any @@ -3021,12 +3025,11 @@ fn _format_scalar[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. - var str_slice = StringSlice[__lifetime_of(buf)]( - unsafe_from_utf8_ptr=buf.unsafe_ptr(), len=wrote - ) + var span = Span[UInt8](buf)[:wrote] + + var str_slice = StringSlice(unsafe_from_utf8=span) writer.write_str(str_slice) - _ = buf^ # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index ed9d4b44fa..d00d6643ac 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -15,9 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin.format_int import _try_write_int -from builtin.simd import _format_scalar -from sys import triple_is_nvidia_cuda, bitwidthof +from sys import bitwidthof @lldb_formatter_wrapping_type @@ -108,6 +106,15 @@ struct UInt(IntLike): return String.format_sequence(self) @no_inline + fn format_to(self, inout writer: Formatter): + """Formats this integer to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + writer.write(UInt64(self)) + fn __repr__(self) -> String: """Convert this UInt to a string. @@ -736,25 +743,6 @@ struct UInt(IntLike): """ return self - fn format_to(self, inout writer: Formatter): - """ - Formats this integer to the provided formatter. - - Args: - writer: The formatter to write to. - """ - - @parameter - if triple_is_nvidia_cuda(): - var err = _try_write_int(writer, UInt64(self)) - if err: - abort( - "unreachable: unexpected write int failure condition: " - + str(err.value()) - ) - else: - _format_scalar(writer, UInt64(self)) - fn _temp_uint_from_int(x: Int) -> UInt: """Constructs a UInt from an Int. From cae850d7076efa97c54ba55d0d8cd76703f47c6f Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:53:56 -0400 Subject: [PATCH 1333/2019] [stdlib] Add `load/store` to `UnsafePointer` In #40801 we moved `load/store` away from `DTypePointer` into `SIMD` to make the transition easier. Now that `DTypePointer` is removed, we can move those functions back into `UnsafePointer`. Then `ptr.load/store` syntax is just more friendly than `SIMD.load/store(ptr)`. MODULAR_ORIG_COMMIT_REV_ID: c3b83f74d112ebdb20f30d697b3d90d39b14edca --- stdlib/src/memory/unsafe_pointer.mojo | 253 +++++++++++++++++++++ stdlib/test/memory/test_unsafepointer.mojo | 17 ++ 2 files changed, 270 insertions(+) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 7f077c265b..3832713b8a 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -400,6 +400,259 @@ struct UnsafePointer[ # Methods # ===-------------------------------------------------------------------===# + @always_inline("nodebug") + fn load[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ](self: UnsafePointer[Scalar[type], *_]) -> SIMD[type, width]: + """Loads the value the pointer points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + The offset must be integer. + + Parameters: + type: The data type of SIMD vector. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Returns: + The loaded value. + """ + return self.load[width=width, alignment=alignment](int(0)) + + @always_inline + fn load[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ](self: UnsafePointer[Scalar[type], *_], offset: Scalar) -> SIMD[ + type, width + ]: + """Loads the value the pointer points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + The offset must be integer. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to load from. + + Returns: + The loaded value. + """ + constrained[offset.type.is_integral(), "offset must be integer"]() + return self.load[width=width, alignment=alignment](int(offset)) + + @always_inline("nodebug") + fn load[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ](self: UnsafePointer[Scalar[type], *_], offset: Int) -> SIMD[type, width]: + """Loads the value the pointer points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to load from. + + Returns: + The loaded value. + """ + + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() + + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: + # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types + # regardless of the alignment that is passed. This causes issues if + # this method is called on an unaligned pointer. + # TODO #37823 We can make this smarter when we add an `aligned` + # trait to the pointer class. + var v = SIMD[type, width]() + + # intentionally don't unroll, otherwise the compiler vectorizes + for i in range(width): + v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( + self.offset(int(offset) + i).address + ) + return v + + return __mlir_op.`pop.load`[alignment = alignment.value]( + self.offset(offset).bitcast[SIMD[type, width]]().address + ) + + @always_inline("nodebug") + fn load[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ](self: UnsafePointer[Scalar[type], *_], offset: UInt) -> SIMD[type, width]: + """Loads the value the pointer points to with the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to load from. + + Returns: + The loaded value. + """ + + return self.load[width=width, alignment=alignment](int(offset)) + + @always_inline + fn store[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ]( + self: UnsafePointer[Scalar[type], *_], + offset: Int, + val: SIMD[type, width], + ): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + The offset must be integer. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to store to. + val: The value to store. + """ + self.offset(offset).store[alignment=alignment](val) + + @always_inline + fn store[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ]( + self: UnsafePointer[Scalar[type], *_], + offset: Scalar, + val: SIMD[type, width], + ): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to store to. + val: The value to store. + """ + constrained[offset.type.is_integral(), "offset must be integer"]() + self.offset(int(offset)).store[alignment=alignment](val) + + @always_inline("nodebug") + fn store[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ](self: UnsafePointer[Scalar[type], *_], val: SIMD[type, width]): + """Stores a single element value. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + val: The value to store. + """ + constrained[width > 0, "width must be a positive integer value"]() + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() + __mlir_op.`pop.store`[alignment = alignment.value]( + val, self.bitcast[SIMD[type, width]]().address + ) + + @always_inline("nodebug") + fn store[ + type: DType, //, + width: Int = 1, + *, + alignment: Int = alignof[ + Scalar[type] + ]() if triple_is_nvidia_cuda() else 1, + ]( + self: UnsafePointer[Scalar[type], *_], + offset: UInt, + val: SIMD[type, width], + ): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + width: The size of the SIMD vector. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to store to. + val: The value to store. + """ + self.offset(offset).store[alignment=alignment](val) + @always_inline("nodebug") fn simd_strided_load[ type: DType, width: Int, T: Intable diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 969ce6dfe4..d123ecf520 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -257,6 +257,21 @@ def test_offset(): ptr.free() +def test_load_and_store_simd(): + var ptr = UnsafePointer[Int8].alloc(16) + for i in range(16): + ptr[i] = i + for i in range(0, 16, 4): + var vec = ptr.load[width=4](i) + assert_equal(vec, SIMD[DType.int8, 4](i, i + 1, i + 2, i + 3)) + + var ptr2 = UnsafePointer[Int8].alloc(16) + for i in range(0, 16, 4): + ptr2.store[width=4](i, i) + for i in range(16): + assert_equal(ptr2[i], i // 4 * 4) + + def main(): test_address_of() @@ -277,3 +292,5 @@ def main(): test_indexing() test_bool() test_alignment() + test_offset() + test_load_and_store_simd() From 23028a42aac2f03dc97f1a952125a7a42f18db2a Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 30 Jul 2024 18:51:08 -0400 Subject: [PATCH 1334/2019] [stdlib] Move `SIMD.load/store` uses to `UnsafePointer` Going back to the original `ptr.load/store` syntax. MODULAR_ORIG_COMMIT_REV_ID: cb451f0b6c92f3a08e3898eddface66c8252b7d3 --- .../benchmarks/algorithm/bench_vectorize.mojo | 93 +++++++------------ stdlib/benchmarks/utils/bench_memmem.mojo | 4 +- stdlib/src/builtin/hash.mojo | 4 +- stdlib/src/collections/dict.mojo | 16 ++-- stdlib/src/math/math.mojo | 4 +- stdlib/src/memory/memory.mojo | 16 ++-- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/sys/intrinsics.mojo | 18 ++-- stdlib/src/utils/_serialize.mojo | 4 +- stdlib/src/utils/stringref.mojo | 14 ++- stdlib/test/builtin/test_file.mojo | 14 +-- stdlib/test/builtin/test_simd.mojo | 4 +- stdlib/test/memory/test_memory.mojo | 36 +++---- stdlib/test/sys/test_intrinsics.mojo | 8 +- 14 files changed, 105 insertions(+), 132 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index cf89c69816..b708661fc5 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -76,16 +76,12 @@ fn test_vectorize[ @always_inline @parameter fn ld_vector[simd_width: Int](idx: Int): - SIMD[size=simd_width].store( - vector, idx + 1, SIMD[dtype, simd_width](idx) - ) + vector.store(idx + 1, SIMD[dtype, simd_width](idx)) @always_inline @parameter fn st_vector[simd_width: Int](idx: Int): - SIMD[size=simd_width].store( - result, idx, SIMD[size=simd_width].load(vector, idx) - ) + result.store(idx, vector.load[width=simd_width](idx)) @__copy_capture(vector) @always_inline @@ -96,25 +92,15 @@ fn test_vectorize[ @parameter if op == Op.add: - SIMD[size=simd_width].store( - vector, idx, SIMD[size=simd_width].load(vector, idx) + x - ) + vector.store(idx, vector.load[width=simd_width](idx) + x) elif op == Op.sub: - SIMD[size=simd_width].store( - vector, idx, SIMD[size=simd_width].load(vector, idx) - x - ) + vector.store(idx, vector.load[width=simd_width](idx) - x) elif op == Op.mul: - SIMD[size=simd_width].store( - vector, idx, SIMD[size=simd_width].load(vector, idx) * x - ) + vector.store(idx, vector.load[width=simd_width](idx) * x) elif op == Op.div: - SIMD[size=simd_width].store( - vector, idx, SIMD[size=simd_width].load(vector, idx) / x - ) + vector.store(idx, vector.load[width=simd_width](idx) / x) elif op == Op.fma: - SIMD[size=simd_width].store( - vector, idx, SIMD[size=simd_width].load(vector, idx) * x + y - ) + vector.store(idx, vector.load[width=simd_width](idx) * x + y) @__copy_capture(vector) @always_inline @@ -122,40 +108,35 @@ fn test_vectorize[ fn arithmetic_vector[simd_width: Int](idx: Int): @parameter if op == Op.add: - SIMD[size=simd_width].store( - vector, + vector.store( idx, - SIMD[size=simd_width].load(vector, idx) - + SIMD[size=simd_width].load(vector, idx), + vector.load[width=simd_width](idx) + + vector.load[width=simd_width](idx), ) elif op == Op.sub: - SIMD[size=simd_width].store( - vector, + vector.store( idx, - SIMD[size=simd_width].load(vector, idx) - - SIMD[size=simd_width].load(vector, idx), + vector.load[width=simd_width](idx) + - vector.load[width=simd_width](idx), ) elif op == Op.mul: - SIMD[size=simd_width].store( - vector, + vector.store( idx, - SIMD[size=simd_width].load(vector, idx) - * SIMD[size=simd_width].load(vector, idx), + vector.load[width=simd_width](idx) + * vector.load[width=simd_width](idx), ) elif op == Op.div: - SIMD[size=simd_width].store( - vector, + vector.store( idx, - SIMD[size=simd_width].load(vector, idx) - / SIMD[size=simd_width].load(vector, idx), + vector.load[width=simd_width](idx) + / vector.load[width=simd_width](idx), ) elif op == Op.fma: - SIMD[size=simd_width].store( - vector, + vector.store( idx, - SIMD[size=simd_width].load(vector, idx) - * SIMD[size=simd_width].load(vector, idx) - + SIMD[size=simd_width].load(vector, idx), + vector.load[width=simd_width](idx) + * vector.load[width=simd_width](idx) + + vector.load[width=simd_width](idx), ) @always_inline @@ -272,10 +253,9 @@ fn bench_compare(): fn arg_size(): @parameter fn closure[width: Int](i: Int): - SIMD.store( - p2, + p2.store( i, - SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + p1.load[width=width](i) + p2.load[width=width](i), ) for i in range(its): @@ -285,10 +265,9 @@ fn bench_compare(): fn param_size(): @parameter fn closure[width: Int](i: Int): - SIMD.store( - p2, + p2.store( i, - SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + p1.load[width=width](i) + p2.load[width=width](i), ) for i in range(its): @@ -298,10 +277,9 @@ fn bench_compare(): fn arg_size_unroll(): @parameter fn closure[width: Int](i: Int): - SIMD.store( - p2, + p2.store( i, - SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + p1.load[width=width](i) + p2.load[width=width](i), ) for i in range(its): @@ -311,29 +289,28 @@ fn bench_compare(): fn param_size_unroll(): @parameter fn closure[width: Int](i: Int): - SIMD.store( - p2, + p2.store( i, - SIMD[size=width].load(p1, i) + SIMD[size=width].load(p2, i), + p1.load[width=width](i) + p2.load[width=width](i), ) for i in range(its): vectorize[closure, width, size=size, unroll_factor=unroll_factor]() var arg = run[arg_size](max_runtime_secs=0.5).mean(unit) - print(SIMD[size=size].load(p2)) + print(p2.load[width=width]()) memset_zero(p2, size) var param = run[param_size](max_runtime_secs=0.5).mean(unit) - print(SIMD[size=size].load(p2)) + print(p2.load[width=width]()) memset_zero(p2, size) var arg_unroll = run[arg_size_unroll](max_runtime_secs=0.5).mean(unit) - print(SIMD[size=size].load(p2)) + print(p2.load[width=width]()) memset_zero(p2, size) var param_unroll = run[param_size_unroll](max_runtime_secs=0.5).mean(unit) - print(SIMD[size=size].load(p2)) + print(p2.load[width=width]()) print( "calculating", diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index c494fb0dce..3c8ecb31c7 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -164,9 +164,7 @@ fn _memmem_baseline[ haystack_len - needle_len + 1, bool_mask_width ) for i in range(0, vectorized_end, bool_mask_width): - var bool_mask = SIMD[size=bool_mask_width].load( - haystack, i - ) == first_needle + var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: var offset = int(i + count_trailing_zeros(mask)) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 463d9bd987..84bad072cd 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -254,7 +254,7 @@ fn hash(bytes: UnsafePointer[UInt8], n: Int) -> UInt: # 2. Compute the hash, but strided across the SIMD vector width. var hash_data = _HASH_INIT[type, simd_width]() for i in range(k): - var update = SIMD[size=simd_width].load(simd_data, i * simd_width) + var update = simd_data.load[width=simd_width](i * simd_width) hash_data = _HASH_UPDATE(hash_data, update) # 3. Copy the tail data (smaller than the SIMD register) into @@ -264,7 +264,7 @@ fn hash(bytes: UnsafePointer[UInt8], n: Int) -> UInt: var ptr = remaining.unsafe_ptr() memcpy(ptr, bytes + k * stride, r) memset_zero(ptr + r, stride - r) # set the rest to 0 - var last_value = SIMD[size=simd_width].load(ptr.bitcast[Scalar[type]]()) + var last_value = ptr.bitcast[Scalar[type]]().load[width=simd_width]() hash_data = _HASH_UPDATE(hash_data, last_value) _ = remaining # We make sure the array lives long enough. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index f574a67125..24cd1b3561 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -285,30 +285,30 @@ struct _DictIndex: fn get_index(self, reserved: Int, slot: Int) -> Int: if reserved <= 128: var data = self.data.bitcast[Int8]() - return int(Scalar.load(data, slot & (reserved - 1))) + return int(data.load(slot & (reserved - 1))) elif reserved <= 2**16 - 2: var data = self.data.bitcast[Int16]() - return int(Scalar.load(data, slot & (reserved - 1))) + return int(data.load(slot & (reserved - 1))) elif reserved <= 2**32 - 2: var data = self.data.bitcast[Int32]() - return int(Scalar.load(data, slot & (reserved - 1))) + return int(data.load(slot & (reserved - 1))) else: var data = self.data.bitcast[Int64]() - return int(Scalar.load(data, slot & (reserved - 1))) + return int(data.load(slot & (reserved - 1))) fn set_index(inout self, reserved: Int, slot: Int, value: Int): if reserved <= 128: var data = self.data.bitcast[Int8]() - return Scalar.store(data, slot & (reserved - 1), value) + return data.store(slot & (reserved - 1), value) elif reserved <= 2**16 - 2: var data = self.data.bitcast[Int16]() - return Scalar.store(data, slot & (reserved - 1), value) + return data.store(slot & (reserved - 1), value) elif reserved <= 2**32 - 2: var data = self.data.bitcast[Int32]() - return Scalar.store(data, slot & (reserved - 1), value) + return data.store(slot & (reserved - 1), value) else: var data = self.data.bitcast[Int64]() - return Scalar.store(data, slot & (reserved - 1), value) + return data.store(slot & (reserved - 1), value) fn __del__(owned self): self.data.free() diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 562c7096a6..2c2876c5d5 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1053,9 +1053,9 @@ fn iota[ alias simd_width = simdwidthof[type]() var vector_end = align_down(len, simd_width) for i in range(0, vector_end, simd_width): - SIMD.store(buff, i, iota[type, simd_width](i + offset)) + buff.store(i, iota[type, simd_width](i + offset)) for i in range(vector_end, len): - Scalar.store(buff, i, i + offset) + buff.store(i, i + offset) fn iota[type: DType, //](inout v: List[Scalar[type]], offset: Int = 0): diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index a7199f4839..a45f8afdab 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -62,8 +62,8 @@ fn _memcmp_impl_unconstrained[ var last = count - simd_width for i in range(0, last, simd_width): - var s1i = SIMD[size=simd_width].load(s1, i) - var s2i = SIMD[size=simd_width].load(s2, i) + var s1i = s1.load[width=simd_width](i) + var s2i = s2.load[width=simd_width](i) var diff = s1i != s2i if any(diff): var index = int( @@ -73,8 +73,8 @@ fn _memcmp_impl_unconstrained[ ) return -1 if s1i[index] < s2i[index] else 1 - var s1i = SIMD[size=simd_width].load(s1, last) - var s2i = SIMD[size=simd_width].load(s2, last) + var s1i = s1.load[width=simd_width](last) + var s2i = s2.load[width=simd_width](last) var diff = s1i != s2i if any(diff): var index = int( @@ -184,9 +184,9 @@ fn memcpy[count: Int](dest: UnsafePointer, src: __type_of(dest)): alias chunk_size = 32 alias vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - SIMD.store(dest_ptr, i, SIMD[size=chunk_size].load(src_ptr, i)) + dest_ptr.store(i, src_ptr.load[width=chunk_size](i)) for i in range(vector_end, n): - Scalar.store(dest_ptr, i, Scalar.load(src_ptr, i)) + dest_ptr.store(i, src_ptr.load(i)) @always_inline @@ -245,9 +245,9 @@ fn memcpy( alias chunk_size = 32 var vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - SIMD.store(dest_ptr, i, SIMD[size=chunk_size].load(src_ptr, i)) + dest_ptr.store(i, src_ptr.load[width=chunk_size](i)) for i in range(vector_end, n): - Scalar.store(dest_ptr, i, Scalar.load(src_ptr, i)) + dest_ptr.store(i, src_ptr.load(i)) @always_inline diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index e5586596b3..7757e8b084 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -261,7 +261,7 @@ struct CPython: fn _Py_REFCNT(inout self, ptr: PyObjectPtr) -> Int: if ptr._get_ptr_as_int() == 0: return -1 - return int(Scalar.load(ptr.value)) + return int(ptr.value.load()) fn PyDict_New(inout self) -> PyObjectPtr: var r = self.lib.get_function[fn () -> PyObjectPtr]("PyDict_New")() diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 0614c09007..7624272b3e 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -882,9 +882,9 @@ fn gather[ @parameter if size == 1: - return Scalar.load( - _unsafe_aliasing_address_to_pointer[type](base[0]) - ) if mask else passthrough[0] + return _unsafe_aliasing_address_to_pointer[type]( + base[0] + ).load() if mask else passthrough[0] return llvm_intrinsic[ "llvm.masked.gather", __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], @@ -963,7 +963,7 @@ fn scatter[ if size == 1: if mask: var ptr = _unsafe_aliasing_address_to_pointer[type](base[0]) - Scalar.store(ptr, value[0]) + ptr.store(value[0]) return llvm_intrinsic["llvm.masked.scatter", NoneType]( value, @@ -1244,7 +1244,7 @@ fn masked_load[ @parameter if size == 1: - return Scalar.load(addr) if mask else passthrough[0] + return addr.load() if mask else passthrough[0] return llvm_intrinsic["llvm.masked.load", SIMD[type, size]]( addr.bitcast[NoneType]().address, @@ -1285,7 +1285,7 @@ fn masked_store[ @parameter if size == 1: if mask: - Scalar.store(addr, value[0]) + addr.store(value[0]) return llvm_intrinsic["llvm.masked.store", NoneType]( @@ -1326,7 +1326,7 @@ fn compressed_store[ @parameter if size == 1: if mask: - Scalar.store(addr, value[0]) + addr.store(value[0]) return llvm_intrinsic["llvm.masked.compressstore", NoneType]( @@ -1367,7 +1367,7 @@ fn strided_load[ @parameter if simd_width == 1: - return Scalar.load(addr) if mask else Scalar[type]() + return addr.load() if mask else Scalar[type]() alias IndexTy = SIMD[DType.index, simd_width] var iota = llvm_intrinsic[ @@ -1411,7 +1411,7 @@ fn strided_store[ @parameter if simd_width == 1: if mask: - Scalar.store(addr, value[0]) + addr.store(value[0]) return alias IndexTy = SIMD[DType.index, simd_width] diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index fe87eafaf0..44916920f6 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -49,10 +49,10 @@ fn _serialize_elements_complete[ ](ptr: UnsafePointer[Scalar[type], _], len: Int): if len == 0: return - serialize_fn(Scalar[type].load(ptr)) + serialize_fn(ptr.load()) for i in range(1, len): serialize_fn(", ") - serialize_fn(Scalar[type].load(ptr, i)) + serialize_fn(ptr.load(i)) fn _serialize_elements[ diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 5a21c414f8..8fcca3dbc9 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -116,7 +116,7 @@ struct StringRef( """ var len = 0 - while Scalar.load(ptr, len): + while ptr.load(len): len += 1 self = StringRef(ptr, len) @@ -134,7 +134,7 @@ struct StringRef( """ var len = 0 - while Scalar.load(ptr, len): + while ptr.load(len): len += 1 self = StringRef(ptr, len) @@ -631,9 +631,7 @@ fn _memchr[ var vectorized_end = _align_down(len, bool_mask_width) for i in range(0, vectorized_end, bool_mask_width): - var bool_mask = SIMD[size=bool_mask_width].load( - source, i - ) == first_needle + var bool_mask = source.load[width=bool_mask_width](i) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) if mask: return source + int(i + count_trailing_zeros(mask)) @@ -669,9 +667,9 @@ fn _memmem[ var last_needle = SIMD[type, bool_mask_width](needle[needle_len - 1]) for i in range(0, vectorized_end, bool_mask_width): - var first_block = SIMD[size=bool_mask_width].load(haystack, i) - var last_block = SIMD[size=bool_mask_width].load( - haystack, i + needle_len - 1 + var first_block = haystack.load[width=bool_mask_width](i) + var last_block = haystack.load[width=bool_mask_width]( + i + needle_len - 1 ) var eq_first = first_needle == first_block diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 6a48efecfd..6e021a6dc9 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -110,13 +110,13 @@ def test_file_read_to_address(): ) as f: var ptr = UnsafePointer[UInt8].alloc(1000) assert_equal(f.read(ptr), 954) - assert_equal(Scalar.load(ptr, 0), 76) # L - assert_equal(Scalar.load(ptr, 1), 111) # o - assert_equal(Scalar.load(ptr, 2), 114) # r - assert_equal(Scalar.load(ptr, 3), 101) # e - assert_equal(Scalar.load(ptr, 4), 109) # m - assert_equal(Scalar.load(ptr, 5), 32) # - assert_equal(Scalar.load(ptr, 56), 10) # + assert_equal(ptr.load(0), 76) # L + assert_equal(ptr.load(1), 111) # o + assert_equal(ptr.load(2), 114) # r + assert_equal(ptr.load(3), 101) # e + assert_equal(ptr.load(4), 109) # m + assert_equal(ptr.load(5), 32) # + assert_equal(ptr.load(56), 10) # with open( _dir_of_current_file() / "test_file_dummy_input.txt", diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index c3ac385e72..c59c88bea3 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -139,7 +139,7 @@ def test_issue_1625(): for i in range(size): ptr[i] = i - var x = SIMD[size = 2 * simd_width].load(ptr, 0) + var x = ptr.load[width = 2 * simd_width](0) var evens_and_odds = x.deinterleave() # FIXME (40568) should directly use the SIMD assert_equal @@ -158,7 +158,7 @@ def test_issue_20421(): var a = UnsafePointer[UInt8].alloc[alignment=64](16 * 64) for i in range(16 * 64): a[i] = i & 255 - var av16 = SIMD[size=4].load(a.offset(128 + 64 + 4).bitcast[Int32]()) + var av16 = a.offset(128 + 64 + 4).bitcast[Int32]().load[width=4]() assert_equal( av16, SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448), diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 02d92f24e7..d02bff3dfd 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -127,8 +127,8 @@ def test_memcmp(): def test_memcmp_overflow(): var p1 = UnsafePointer[Int8].alloc(1) var p2 = UnsafePointer[Int8].alloc(1) - Scalar.store(p1, -120) - Scalar.store(p2, 120) + p1.store(-120) + p2.store(120) var c = memcmp(p1, p2, 1) assert_equal(c, -1, "-120 is smaller than 120") @@ -144,10 +144,10 @@ def test_memcmp_simd(): var p2 = UnsafePointer[Int8].alloc(length) memset_zero(p1, length) memset_zero(p2, length) - Scalar.store(p1, 120) - Scalar.store(p1, 1, 100) - Scalar.store(p2, 120) - Scalar.store(p2, 1, 90) + p1.store(120) + p1.store(1, 100) + p2.store(120) + p2.store(1, 90) var c = memcmp(p1, p2, length) assert_equal(c, 1, "[120, 100, 0, ...] is bigger than [120, 90, 0, ...]") @@ -158,10 +158,10 @@ def test_memcmp_simd(): memset_zero(p1, length) memset_zero(p2, length) - Scalar.store(p1, length - 2, 120) - Scalar.store(p1, length - 1, 100) - Scalar.store(p2, length - 2, 120) - Scalar.store(p2, length - 1, 90) + p1.store(length - 2, 120) + p1.store(length - 1, 100) + p2.store(length - 2, 120) + p2.store(length - 1, 90) c = memcmp(p1, p2, length) assert_equal(c, 1, "[..., 0, 120, 100] is bigger than [..., 0, 120, 90]") @@ -290,13 +290,13 @@ def test_memset(): var buf0 = UnsafePointer[Int32].alloc(2) memset(buf0, 1, 2) - assert_equal(Scalar.load(buf0, 0), 16843009) + assert_equal(buf0.load(0), 16843009) memset(buf0, -1, 2) - assert_equal(Scalar.load(buf0, 0), -1) + assert_equal(buf0.load(0), -1) var buf1 = UnsafePointer[Int8].alloc(2) memset(buf1, 5, 2) - assert_equal(Scalar.load(buf1, 0), 5) + assert_equal(buf1.load(0), 5) _ = pair @@ -357,7 +357,7 @@ def test_pointer_refitem_pair(): def test_dtypepointer_gather(): var ptr = UnsafePointer[Float32].alloc(4) - SIMD.store(ptr, 0, SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 3.0)) + ptr.store(0, SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 3.0)) @parameter def _test_gather[ @@ -402,7 +402,7 @@ def test_dtypepointer_gather(): def test_dtypepointer_scatter(): var ptr = UnsafePointer[Float32].alloc(4) - SIMD.store(ptr, 0, SIMD[ptr.type.type, 4](0.0)) + ptr.store(0, SIMD[ptr.type.type, 4](0.0)) @parameter def _test_scatter[ @@ -413,7 +413,7 @@ def test_dtypepointer_scatter(): desired: SIMD[ptr.type.type, 4], ): ptr.scatter(offset, val) - var actual = SIMD[size=4].load(ptr, 0) + var actual = ptr.load[width=4](0) assert_almost_equal( actual, desired, msg="_test_scatter", atol=0.0, rtol=0.0 ) @@ -428,7 +428,7 @@ def test_dtypepointer_scatter(): desired: SIMD[ptr.type.type, 4], ): ptr.scatter(offset, val, mask) - var actual = SIMD[size=4].load(ptr, 0) + var actual = ptr.load[width=4](0) assert_almost_equal( actual, desired, msg="_test_masked_scatter", atol=0.0, rtol=0.0 ) @@ -445,7 +445,7 @@ def test_dtypepointer_scatter(): SIMD[ptr.type.type, 4](3.0, 2.0, 1.0, 0.0), ) - SIMD.store(ptr, 0, SIMD[ptr.type.type, 4](0.0)) + ptr.store(0, SIMD[ptr.type.type, 4](0.0)) _test_masked_scatter[1]( Int16(2), 2.0, False, SIMD[ptr.type.type, 4](0.0, 0.0, 0.0, 0.0) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 149fbe8f2a..405d85f004 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -34,14 +34,14 @@ def test_compressed_store(): memset_zero(vector, 5) compressed_store(iota_4, vector, iota_4 >= 2) - assert_equal(SIMD[size=4].load(vector, 0), F32x4(2.0, 3.0, 0.0, 0.0)) + assert_equal(vector.load[width=4](0), F32x4(2.0, 3.0, 0.0, 0.0)) # Just clear the buffer. - SIMD[size=4].store(vector, 0, 0) + vector.store[width=4](0, 0) var val = F32x4(0.0, 1.0, 3.0, 0.0) compressed_store(val, vector, val != 0) - assert_equal(SIMD[size=4].load(vector, 0), F32x4(1.0, 3.0, 0.0, 0.0)) + assert_equal(vector.load[width=4](0), F32x4(1.0, 3.0, 0.0, 0.0)) vector.free() @@ -80,7 +80,7 @@ def test_masked_store(): memset_zero(vector, 5) masked_store[4](iota_4, vector, iota_4 < 5) - assert_equal(SIMD[size=4].load(vector, 0), F32x4(0.0, 1.0, 2.0, 3.0)) + assert_equal(vector.load[width=4](0), F32x4(0.0, 1.0, 2.0, 3.0)) masked_store[8](iota_8, vector, iota_8 < 5) assert_equal( From 4e7c9380c4c5252d7e80297bfacb76d60e00d4d5 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:54:24 -0700 Subject: [PATCH 1335/2019] [External] [stdlib] Add `has_fma` function to `sys` package (#44374) [External] [stdlib] Add `has_fma` function to `sys` package This PR adds a new function `has_fma` to the `sys` package. This function checks if the host system supports Fused Multiply-Add (FMA) instructions. **Justification**: The `has_fma` function is important for developers implementing math functions. Numerical algorithms can adopt different strategies depending on whether the target architecture supports FMA or not. For example, the implementation of the `log10` function in the LLVM libc library considers the presence of FMA support. **Note on Unit Testing:** ~~I have not added a unit test for this function because I am unsure how to implement it. I searched for unit tests for similar functions, such as `has_avx`, but could not find any. If anyone has suggestions on how to write a unit test for this function, they are welcome.~~ As suggested by @laszlokindrat [here](https://github.com/modularml/mojo/pull/3306#issuecomment-2254066284), I added a basic test for this function to ensures it exists and returns a boolable value. This test prevents accidental deletion or changes but does not validate the correctness of the `has_fma` output. If anyone has suggestions on how to write a better test for this function, they are welcome. ORIGINAL_AUTHOR=Leandro Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3323 Co-authored-by: Leandro Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> Closes modularml/mojo#3323 MODULAR_ORIG_COMMIT_REV_ID: a4caac44ad056ee72b5f55e1e78dbb45b4d4b575 --- stdlib/src/sys/__init__.mojo | 1 + stdlib/src/sys/info.mojo | 16 ++++++++++++++++ stdlib/test/sys/test_targetinfo.mojo | 27 ++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 088da46b32..ff5d750a79 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -22,6 +22,7 @@ from .info import ( has_avx, has_avx2, has_avx512f, + has_fma, has_intel_amx, has_neon, has_neon_int8_dotprod, diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index b949007356..95567d1020 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -107,6 +107,22 @@ fn has_avx512f() -> Bool: ] +@always_inline("nodebug") +fn has_fma() -> Bool: + """Returns True if the host system has FMA (Fused Multiply-Add) support, + otherwise returns False. + + Returns: + True if the host system has FMA support, otherwise returns False. + """ + return __mlir_attr[ + `#kgen.param.expr : i1`, + ] + + @always_inline("nodebug") fn has_vnni() -> Bool: """Returns True if the host system has avx512_vnni, otherwise returns False. diff --git a/stdlib/test/sys/test_targetinfo.mojo b/stdlib/test/sys/test_targetinfo.mojo index 69bcfe14c9..34cbc155a1 100644 --- a/stdlib/test/sys/test_targetinfo.mojo +++ b/stdlib/test/sys/test_targetinfo.mojo @@ -12,8 +12,18 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.info import ( +from sys import ( alignof, + has_avx, + has_avx2, + has_avx512f, + has_fma, + has_intel_amx, + has_neon, + has_neon_int8_dotprod, + has_neon_int8_matmul, + has_sse4, + has_vnni, num_logical_cores, num_performance_cores, num_physical_cores, @@ -53,7 +63,22 @@ fn test_cores() raises: assert_true(num_performance_cores() > 0) +fn test_target_has_feature(): + # Ensures target feature check functions exist and return a boolable value. + var has_feature: Bool = has_avx() + has_feature = has_avx2() + has_feature = has_avx512f() + has_feature = has_fma() + has_feature = has_intel_amx() + has_feature = has_neon() + has_feature = has_neon_int8_dotprod() + has_feature = has_neon_int8_matmul() + has_feature = has_sse4() + has_feature = has_vnni() + + def main(): test_sizeof() test_alignof() test_cores() + test_target_has_feature() From 01d6734922e4768318c290b642277fb9615e3e29 Mon Sep 17 00:00:00 2001 From: Katherine Wu <31663267+k-w-w@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:26:28 -0700 Subject: [PATCH 1336/2019] Fix bug where `str.endswith` was incorrectly reporting `True` in certain cases. MODULAR_ORIG_COMMIT_REV_ID: 830f4d5689dbc53174bbfdb7d66accfbe4d282de --- stdlib/src/builtin/string.mojo | 2 ++ stdlib/src/utils/stringref.mojo | 3 ++- stdlib/test/utils/test_stringref.mojo | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 873ae07efd..5bf4f36c2a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1537,6 +1537,8 @@ struct String( var sep_len = sep.byte_length() if sep_len == 0: raise Error("ValueError: empty separator") + if str_byte_len < 0: + output.append("") while lhs <= str_byte_len: rhs = self.find(sep, lhs) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 8fcca3dbc9..6010416168 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -605,7 +605,8 @@ struct StringRef( Returns: True if the self[start:end] is suffixed by the input suffix. """ - + if len(suffix) > len(self): + return False if end == -1: return self.rfind(suffix, start) + len(suffix) == len(self) return StringRef(self.unsafe_ptr() + start, end - start).endswith( diff --git a/stdlib/test/utils/test_stringref.mojo b/stdlib/test/utils/test_stringref.mojo index 3dbca7cca5..b5f9d35976 100644 --- a/stdlib/test/utils/test_stringref.mojo +++ b/stdlib/test/utils/test_stringref.mojo @@ -108,9 +108,30 @@ def test_find(): assert_equal(StringRef("").find("abc"), -1) +def test_endswith(): + var empty = StringRef("") + assert_true(empty.endswith("")) + assert_false(empty.endswith("a")) + assert_false(empty.endswith("ab")) + + var a = StringRef("a") + assert_true(a.endswith("")) + assert_true(a.endswith("a")) + assert_false(a.endswith("ab")) + + var ab = StringRef("ab") + assert_true(ab.endswith("")) + assert_false(ab.endswith("a")) + assert_true(ab.endswith("b")) + assert_true(ab.endswith("b", start=1)) + assert_true(ab.endswith("a", end=1)) + assert_true(ab.endswith("ab")) + + def main(): test_strref_from_start() test_comparison_operators() test_intable() test_indexing() test_find() + test_endswith() From baf574eca20a6acbc8b22848b7911a348c880e29 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 30 Jul 2024 19:45:47 -0400 Subject: [PATCH 1337/2019] [******][SDK] Move `SIMD.load/store` uses to `UnsafePointer` (#44341) MODULAR_ORIG_COMMIT_REV_ID: 42d3bc29b41b3ae033c765e5715a9b3b39d87f7d --- examples/matmul.mojo | 2 +- stdlib/src/builtin/string.mojo | 6 +++--- stdlib/test/builtin/test_file.mojo | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index bfb1fc584d..461b882790 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -73,7 +73,7 @@ struct Matrix[rows: Int, cols: Int]: self.store[1](y, x, val) fn load[nelts: Int](self, y: Int, x: Int) -> SIMD[type, nelts]: - return SIMD[size=nelts].load(self.data, y * self.cols + x) + return self.data.load[width=nelts](y * self.cols + x) fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]): SIMD[size=nelts].store(self.data, y * self.cols + x, val) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5bf4f36c2a..ab9c216543 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -137,11 +137,11 @@ fn chr(c: Int) -> String: var shift = 6 * (num_bytes - 1) var mask = UInt8(0xFF) >> (num_bytes + 1) var num_bytes_marker = UInt8(0xFF) << (8 - num_bytes) - Scalar.store(p, ((c >> shift) & mask) | num_bytes_marker) + p.store[width=1](((c >> shift) & mask) | num_bytes_marker) for i in range(1, num_bytes): shift -= 6 - Scalar.store(p, i, ((c >> shift) & 0b00111111) | 0b10000000) - Scalar.store(p, num_bytes, 0) + p.store[width=1](i, ((c >> shift) & 0b00111111) | 0b10000000) + p.store[width=1](num_bytes, 0) return String(p.bitcast[UInt8](), num_bytes + 1) diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 6e021a6dc9..bec5e69800 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -232,14 +232,14 @@ def test_file_read_to_dtype_pointer(): var ptr = UnsafePointer[UInt8].alloc(8) var data = f.read(ptr, 8) assert_equal( - str(SIMD[size=8].load(ptr, 0)), + str(ptr.load[width=8](0)), "[76, 111, 114, 101, 109, 32, 105, 112]", ) var ptr2 = UnsafePointer[Int8].alloc(8) var data2 = f.read(ptr2, 8) assert_equal( - str(SIMD[size=8].load(ptr2, 0)), + str(ptr2.load[width=8](0)), "[115, 117, 109, 32, 100, 111, 108, 111]", ) From f7d2aac4733e7342a05951ff80c38d820052518b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Wed, 31 Jul 2024 10:47:19 -0500 Subject: [PATCH 1338/2019] [MAX] Add support for MAX nightly builds to standard library tests Update lit configs to support Mojo from the MAX nightly release channel. MODULAR_ORIG_COMMIT_REV_ID: a3cf63f791bc2fec3cbde517e664920d7ec6a989 --- stdlib/benchmarks/lit.cfg.py | 4 ++++ stdlib/test/lit.cfg.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/stdlib/benchmarks/lit.cfg.py b/stdlib/benchmarks/lit.cfg.py index d7f1b7045c..52cd122b0b 100644 --- a/stdlib/benchmarks/lit.cfg.py +++ b/stdlib/benchmarks/lit.cfg.py @@ -67,6 +67,9 @@ os.environ[ "MODULAR_MOJO_NIGHTLY_IMPORT_PATH" ] = f"{build_root},{pre_built_packages_path}" + os.environ[ + "MODULAR_MOJO_MAX_NIGHTLY_IMPORT_PATH" + ] = f"{build_root},{pre_built_packages_path}" # Pass through several environment variables # to the underlying subprocesses that run the tests. @@ -75,6 +78,7 @@ [ "MODULAR_HOME", "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + "MODULAR_MOJO_MAX_NIGHTLY_IMPORT_PATH", "PATH", ] ) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 1dc17808a5..b267ad4446 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -91,6 +91,8 @@ def has_not(): # here to support both versions of the compiler. os.environ["MODULAR_MOJO_IMPORT_PATH"] = str(build_root) os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = str(build_root) + os.environ["MODULAR_MOJO_MAX_IMPORT_PATH"] = str(build_root) + os.environ["MODULAR_MOJO_MAX_NIGHTLY_IMPORT_PATH"] = str(build_root) # Pass through several environment variables # to the underlying subprocesses that run the tests. @@ -100,5 +102,7 @@ def has_not(): "MODULAR_HOME", "MODULAR_MOJO_IMPORT_PATH", "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + "MODULAR_MOJO_MAX_IMPORT_PATH", + "MODULAR_MOJO_MAX_NIGHTLY_IMPORT_PATH", ] ) From c94974d1fe5a522201912cc640fe6278d971a43f Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:43:43 -0400 Subject: [PATCH 1339/2019] [stdlib] Remove `SIMD.load/store` `SIMD.load/store` is now moved to `UnsafePointer`. MODULAR_ORIG_COMMIT_REV_ID: 642e98bc5e9e7733d4567207fe30e5ebb90cbd67 --- docs/changelog.md | 2 + examples/mandelbrot.mojo | 2 +- examples/matmul.mojo | 2 +- examples/notebooks/Mandelbrot.ipynb | 4 +- examples/notebooks/Matmul.ipynb | 4 +- stdlib/src/builtin/simd.mojo | 209 ---------------------------- 6 files changed, 8 insertions(+), 215 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 90a958b23d..887b9ef263 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -578,6 +578,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. +- `SIMD.load/store` are moved to `UnsafePointer`. + ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 4798a5cd45..7a5e48634b 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -42,7 +42,7 @@ struct Matrix[type: DType, rows: Int, cols: Int]: self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) fn store[nelts: Int](self, row: Int, col: Int, val: SIMD[type, nelts]): - SIMD[size=nelts].store(self.data, row * cols + col, val) + self.data.store[width=nelts](row * cols + col, val) fn mandelbrot_kernel_SIMD[ diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 461b882790..35412ebcc4 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -76,7 +76,7 @@ struct Matrix[rows: Int, cols: Int]: return self.data.load[width=nelts](y * self.cols + x) fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]): - SIMD[size=nelts].store(self.data, y * self.cols + x, val) + self.data.store[width=nelts](y * self.cols + x, val) def run_matmul_python() -> Float64: diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 49c2df2d87..c8603c1482 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -104,10 +104,10 @@ " self.data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", "\n", " fn __getitem__(self, row: Int, col: Int) -> Scalar[type]:\n", - " return Scalar.load(self.data, row * cols + col)\n", + " return self.data.load(row * cols + col)\n", "\n", " fn store[width: Int = 1](self, row: Int, col: Int, val: SIMD[type, width]):\n", - " SIMD[size=width].store(self.data, row * cols + col, val)" + " self.data.store[width=width](row * cols + col, val)" ] }, { diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index c3ee9ebd29..9a1bf2f4ac 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -437,10 +437,10 @@ " self.store[1](y, x, val)\n", "\n", " fn load[nelts: Int](self, y: Int, x: Int) -> SIMD[type, nelts]:\n", - " return SIMD[size=nelts].load(self.data, y * self.cols + x)\n", + " return self.data.load[width=nelts](y * self.cols + x)\n", "\n", " fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]):\n", - " return SIMD[size=nelts].store(self.data, y * self.cols + x, val)" + " return self.data.store[width=nelts](y * self.cols + x, val)" ] }, { diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 958aa46262..7e7e758763 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2524,215 +2524,6 @@ struct SIMD[type: DType, size: Int]( "llvm.vector.splice", Self, has_side_effect=False ](zero_simd, self, Int32(-shift)) - @staticmethod - @always_inline - fn load[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_]) -> Self: - """Loads the value the pointer points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - The offset must be integer. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to load from. - - Returns: - The loaded value. - """ - return Self.load[alignment=alignment](ptr, int(0)) - - @staticmethod - @always_inline - fn load[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], offset: Scalar) -> Self: - """Loads the value the pointer points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - The offset must be integer. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to load from. - offset: The offset to load from. - - Returns: - The loaded value. - """ - constrained[offset.type.is_integral(), "offset must be integer"]() - return Self.load[alignment=alignment](ptr, offset=int(offset)) - - @staticmethod - @always_inline("nodebug") - fn load[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], offset: Int) -> Self: - """Loads the value the pointer points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to load from. - offset: The offset to load from. - - Returns: - The loaded value. - """ - - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - - @parameter - if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: - # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types - # regardless of the alignment that is passed. This causes issues if - # this method is called on an unaligned pointer. - # TODO #37823 We can make this smarter when we add an `aligned` - # trait to the pointer class. - var v = SIMD[type, size]() - - # intentionally don't unroll, otherwise the compiler vectorizes - for i in range(size): - v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( - ptr.offset(int(offset) + i).address - ) - return v - - return __mlir_op.`pop.load`[alignment = alignment.value]( - ptr.offset(offset).bitcast[SIMD[type, size]]().address - ) - - @staticmethod - @always_inline("nodebug") - fn load[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], offset: UInt) -> Self: - """Loads the value the pointer points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to load from. - offset: The offset to load from. - - Returns: - The loaded value. - """ - - return Self.load[alignment=alignment](ptr, int(offset)) - - @staticmethod - @always_inline - fn store[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], offset: Int, val: Self): - """Stores a single element value at the given offset. - - Constraints: - The width and alignment must be positive integer values. - The offset must be integer. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to store to. - offset: The offset to store to. - val: The value to store. - """ - Self.store[alignment=alignment](ptr.offset(offset), val) - - @staticmethod - @always_inline - fn store[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], offset: Scalar, val: Self): - """Stores a single element value at the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to store to. - offset: The offset to store to. - val: The value to store. - """ - constrained[offset.type.is_integral(), "offset must be integer"]() - Self.store[alignment=alignment](ptr, int(offset), val) - - @staticmethod - @always_inline("nodebug") - fn store[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], val: Self): - """Stores a single element value. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to store to. - val: The value to store. - """ - constrained[size > 0, "width must be a positive integer value"]() - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - __mlir_op.`pop.store`[alignment = alignment.value]( - val, ptr.bitcast[SIMD[type, size]]().address - ) - - @staticmethod - @always_inline("nodebug") - fn store[ - *, - alignment: Int = Self._default_alignment, - ](ptr: UnsafePointer[Scalar[type], *_], offset: UInt, val: Self): - """Stores a single element value at the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - alignment: The minimal alignment of the address. - - Args: - ptr: The pointer to store to. - offset: The offset to store to. - val: The value to store. - """ - Self.store[alignment=alignment](ptr.offset(offset), val) - # ===----------------------------------------------------------------------=== # # _pow From 4041dc7b668aa62c8b71db47ec8d68cbc31a5139 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 31 Jul 2024 12:01:23 -0700 Subject: [PATCH 1340/2019] Unify the MAX/Mojo install procedure. Installing MAX does not require auth anymore, and it always includes the latest Mojo. The standalone Mojo package will soon be unavailable and Mojo will be available only throu the MAX package, which will simplify the install/update procedure and provide a unified package that's free for everybody to use. MODULAR_ORIG_COMMIT_REV_ID: f33278aeecf2c41bcdb718b22ddb9a21cb8434c0 --- docs/faq.md | 28 +-- docs/manual/get-started.mdx | 365 ++---------------------------------- 2 files changed, 22 insertions(+), 371 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 49ca3bea84..a05a863fe6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -270,7 +270,11 @@ dashboard](https://www.modular.com/max/performance). ### How can I get access to the SDK? -You can [get the Mojo SDK here](https://developer.modular.com/download)! +Mojo is included with the MAX SDK, which you can [download and use for +free](/max/install). + +Read more about [why Mojo is bundled with +MAX](/max/faq#why-bundle-mojo-with-max). ### Is the Mojo Playground still available? @@ -303,14 +307,6 @@ and does not require login. Please read the [Mojo SDK License Terms](https://www.modular.com/legal/mojo). -### What does the Mojo SDK ship with? - -The Mojo SDK includes the Mojo standard library and `mojo` command-line tool, -which provides a REPL similar to the `python` command, along with `build`, -`run`, `package`, `doc` and `format` commands. We've also published a [Mojo -language extension for VS -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). - ### What operating systems are supported? Currently, we support Ubuntu Linux 20.04/22.04 (64-bit x86) and macOS (Apple @@ -383,20 +379,6 @@ Please join the [Mojo Discord channel](http://discord.gg/modular) for notifications and [sign up for our newsletter](https://www.modular.com/newsletter) for more coarse-grain updates. -## Mojo Playground {#mojo-playground} - -### What sort of computer is backing each instance in the Mojo Playground? - -The Mojo Playground runs on a fleet of [AWS EC2 -C6i](https://aws.amazon.com/ec2/instance-types/c6i/) (c6i.8xlarge) instances -that is divided among active users. Due to the shared nature of the system, the -number of vCPU cores provided to your session may vary. We guarantee 1 vCPU -core per session, but that may increase when the total number of active users is -low. - -Each user also has a dedicated volume in which you can save your own files that -persist across sessions. - ## Open Source ### Will Mojo be open-sourced? diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 3a2d5f1c15..265ea29bf5 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -7,313 +7,18 @@ description: Install Mojo now and start developing import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -On this page, we'll show you how to install Mojo and create the classic "Hello +On this page, we'll show you how to create the classic "Hello world" starter program with Mojo, in three different ways. If you'd rather read how to write Mojo code beyond just printing text, see the [introduction to Mojo](/mojo/manual/basics). -:::tip Updating? - -If you already installed Mojo, see [how to update](#update-mojo). - -::: - -## Requirements - - - - -- Apple silicon (M1 or M2 processor) -- macOS Ventura (12) or later -- Python 3.9 - 3.11 -- Xcode or Xcode Command Line Tools -- [Homebrew](https://brew.sh) - - - - -- Ubuntu 20.04/22.04 LTS -- x86-64 CPU (with [SSE4.2 or - newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) - or AWS Graviton2/3 CPU -- Minimum 8 GiB RAM -- Python 3.9 - 3.11 -- g++ or clang++ C++ compiler - - - - -Windows support is still in development. - -In the meantime, you can use Mojo on Windows [with -WSL](https://learn.microsoft.com/en-us/windows/wsl/install), using a compatible -version of Ubuntu (see our requirements for Linux). - - - - ## 1. Install Mojo -If you already [installed MAX](/max/install), you can [skip to the next -section](#2-run-code-in-the-repl) because MAX includes Mojo. - -The Mojo SDK is available as either a stable build or a nightly build. -We strive to release stable builds once a month and release nightly builds as -often as possible (not necessarily every day). - - -{/*############################*/} -{/*#### STABLE BUILD SETUP ####*/} -{/*############################*/} - - -1. Open a terminal and install the [`modular`](/cli/) command line tool with - this helper script: - - ```sh - curl -s https://get.modular.com | sh - - ``` - -
    - - Or, click here to see the manual install commands. - -
    - - - - - ```sh - brew update && brew install modularml/packages/modular - ``` - - - - - ```sh - apt-get install -y apt-transport-https && - keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && - apt-get update && - apt-get install -y modular - ``` - - - - -
    -
    - -2. Create a virtual environment: - - Because Mojo interoperates with Python, - it's important to define a predictable Python version and package library to - use. We suggest you do that with either venv or conda: - - - - - For most users, we recommend venv (it's included with Python): - - ```sh - python3 -m venv mojo-venv && source mojo-venv/bin/activate - ``` - - - - - Only if you already use conda as your preferred environment, we suggest you - use that: - - ```sh - conda create -n mojo python=3.10 -y && conda activate mojo - ``` - - - - -3. Install the Mojo SDK: - - ```sh - modular install mojo - ``` - -4. Set environment variables so you can access the [`mojo`](/mojo/cli/) CLI: - - - - - If you're using Bash, run this command: - - ```sh - MOJO_PATH=$(modular config mojo.path) \ - && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ - && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> "$BASHRC" \ - && source "$BASHRC" - ``` - - - - - If you're using ZSH, run this command: - - ```sh - MOJO_PATH=$(modular config mojo.path) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ - && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> ~/.zshrc \ - && source ~/.zshrc - ``` - - - - - If you're using fish, run this command: - - ```sh - set MOJO_PATH (modular config mojo.path) \ - && set -Ux MODULAR_HOME $HOME/.modular \ - && fish_add_path $MOJO_PATH/bin - ``` - - - - -
    -{/*############################*/} -{/*### NIGHTLY BUILD SETUP ####*/} -{/*############################*/} - - -:::caution - -Nightly builds are not fully tested. They might include incomplete features, -performance regressions, and new bugs. When using code from the -[mojo](https://github.com/modularml/mojo) GitHub repo, be sure you checkout -the `nightly` branch, because the `main` branch might not be compatible with -nightly builds. - -::: - -1. Open a terminal and install the [`modular`](/cli/) command line tool with - this helper script (this is the same for stable and nightly builds): - - ```sh - curl -s https://get.modular.com | sh - - ``` - -
    - - Or, click here to see the manual install commands. - -
    - - - - - ```sh - brew update && brew install modularml/packages/modular - ``` - - - - - ```sh - apt-get install -y apt-transport-https && - keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && - curl -1sLf 'https://dl.modular.com/bBNWiLZX5igwHXeu/installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && - apt-get update && - apt-get install -y modular - ``` - - - - -
    -
    - -2. Create a virtual environment for nightly builds: - - Because Mojo interoperates with Python, - it's important to define a predictable Python version and package library to - use. We suggest you do that with either venv or conda: - - - - - For most users, we recommend venv (it's included with Python): - - ```sh - python3 -m venv mojo-nightly-venv && source mojo-nightly-venv/bin/activate - ``` - - - - - Only if you already use conda as your preferred environment, we suggest you - use that: - - ```sh - conda create -n mojo-nightly python=3.10 -y && conda activate mojo-nightly - ``` - - - - -3. Install the nightly Mojo SDK: - - ```sh - modular install nightly/mojo - ``` - -4. Set environment variables so you can access the nightly [`mojo`](/mojo/cli/) - CLI: - - - - - If you're using Bash, run this command: - - ```sh - MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ - && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ - && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> "$BASHRC" \ - && source "$BASHRC" - ``` +Mojo is now bundled with MAX, which provides everything you need to compile, +run, debug, and package Mojo code. (Read [why we bundled Mojo with +MAX](/max/faq#why-bundle-mojo-with-max).) - - - - If you're using ZSH, run this command: - - ```sh - MOJO_NIGHTLY_PATH=$(modular config mojo-nightly.path) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ - && echo 'export PATH="'$MOJO_NIGHTLY_PATH'/bin:$PATH"' >> ~/.zshrc \ - && source ~/.zshrc - ``` - - - - - If you're using fish, run this command: - - ```sh - set MOJO_NIGHTLY_PATH (modular config mojo-nightly.path) \ - && set -Ux MODULAR_HOME $HOME/.modular \ - && fish_add_path $MOJO_NIGHTLY_PATH/bin - ``` - - - - -
    -
    - -Now you're ready to go. +Follow the guide to [install MAX & Mojo](/max/install), and then return here. ## 2. Run code in the REPL @@ -451,55 +156,19 @@ more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). ## Update Mojo -To check your current Mojo version, use the `--version` option: +Because Mojo is now a part of MAX, you soon won't be able to update the +standalone `mojo` package, and you must instead install/update the `max` +package. (Read [why we bundled Mojo with +MAX](/max/faq#why-bundle-mojo-with-max).) -```sh -mojo --version -``` - -And compare your version to the latest stable version in the [Mojo -changelog](/mojo/changelog). Or if you installed a nightly build, look for -release announcements in [this Discord -channel](https://discord.com/channels/1087530497313357884/1224434323193594059). - -If it's time to update, here's what to do: +If you already installed Mojo on its own, you'll need to install MAX to get all +future Mojo updates. -1. Make sure you have the latest `modular` CLI: - - - - - ```sh - brew update \ - && brew upgrade modular - ``` +Before you install `max`, you should uninstall Mojo to avoid conflicting +toolchain versions between the `mojo` and `max` packages: - - - - ```sh - sudo apt update \ - && sudo apt install modular - ``` - - - - -2. Update the `mojo` package: - - - - - ```sh - modular update mojo - ``` - - - - - ```sh - modular update nightly/mojo - ``` +```sh +modular uninstall mojo +``` - - +Then follow the guide to [install MAX & Mojo](/max/install). \ No newline at end of file From f05ef2bebd1c4fd963466dd113711fcfb6e97af0 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 31 Jul 2024 16:18:27 -0700 Subject: [PATCH 1341/2019] [mojo-stdlib] Alter `Variant` implementation to use new POP ops (NFC) This changes the `Variant` implementation to use the new `pop.variant.discr_gep` and `pop.variant.bitcast` operations. These operations provide a stronger contract to the compiler about their semantics. This will allow the compiler to more effectively optimize in-memory variants. MODULAR_ORIG_COMMIT_REV_ID: 4ae5c48cf62b340f96f96d44de468882f91e4aaa --- stdlib/src/utils/variant.mojo | 68 ++++++++++++++++------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 9c02cbc080..bcda383ca5 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -135,7 +135,7 @@ struct Variant[*Ts: CollectionElement]( """ self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] alias idx = Self._check[T]() - self._get_state() = idx + self._get_discr() = idx self._get_ptr[T]().init_pointee_move(value^) fn __init__(inout self, *, other: Self): @@ -145,12 +145,12 @@ struct Variant[*Ts: CollectionElement]( other: The value to copy from. """ self = Self(unsafe_uninitialized=()) - self._get_state() = other._get_state() + self._get_discr() = other._get_discr() @parameter for i in range(len(VariadicList(Ts))): alias T = Ts[i] - if self._get_state() == i: + if self._get_discr() == i: self._get_ptr[T]().init_pointee_move(other._get_ptr[T]()[]) return @@ -171,19 +171,24 @@ struct Variant[*Ts: CollectionElement]( other: The variant to move. """ self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] - self._get_state() = other._get_state() + self._get_discr() = other._get_discr() @parameter for i in range(len(VariadicList(Ts))): alias T = Ts[i] - if self._get_state() == i: + if self._get_discr() == i: # Calls the correct __moveinit__ other._get_ptr[T]().move_pointee_into(self._get_ptr[T]()) return fn __del__(owned self): """Destroy the variant.""" - self._call_correct_deleter() + + @parameter + for i in range(len(VariadicList(Ts))): + if self._get_discr() == i: + self._get_ptr[Ts[i]]().destroy_pointee() + return # ===-------------------------------------------------------------------===# # Operator dunders @@ -216,24 +221,23 @@ struct Variant[*Ts: CollectionElement]( # Methods # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn _get_ptr[T: CollectionElement](self) -> UnsafePointer[T]: - constrained[ - Self._check[T]() != Self._sentinel, "not a union element type" - ]() - return UnsafePointer.address_of(self._impl).bitcast[T]() - - fn _get_state(ref [_]self: Self) -> ref [__lifetime_of(self)] Int8: - var int8_self = UnsafePointer.address_of(self).bitcast[Int8]() - alias size = Self._size() - return (int8_self + size)[] - - @always_inline - fn _call_correct_deleter(inout self): - @parameter - for i in range(len(VariadicList(Ts))): - if self._get_state() == i: - self._get_ptr[Ts[i]]().destroy_pointee() - return + alias idx = Self._check[T]() + constrained[idx != Self._sentinel, "not a union element type"]() + var ptr = UnsafePointer.address_of(self._impl).address + var discr_ptr = __mlir_op.`pop.variant.bitcast`[ + _type = UnsafePointer[T]._mlir_type, index = idx.value + ](ptr) + return discr_ptr + + @always_inline("nodebug") + fn _get_discr(ref [_]self: Self) -> ref [__lifetime_of(self)] UInt8: + var ptr = UnsafePointer.address_of(self._impl).address + var discr_ptr = __mlir_op.`pop.variant.discr_gep`[ + _type = __mlir_type.`!kgen.pointer>` + ](ptr) + return UnsafePointer(discr_ptr).bitcast[UInt8]()[] @always_inline fn take[T: CollectionElement](inout self) -> T: @@ -275,7 +279,7 @@ struct Variant[*Ts: CollectionElement]( """ debug_assert(self.isa[T](), "taking wrong type") # don't call the variant's deleter later - self._get_state() = Self._sentinel + self._get_discr() = Self._sentinel return self._get_ptr[T]().take_pointee() @always_inline @@ -358,7 +362,7 @@ struct Variant[*Ts: CollectionElement]( True if the variant contains the requested type. """ alias idx = Self._check[T]() - return self._get_state() == idx + return self._get_discr() == idx fn unsafe_get[ T: CollectionElement @@ -383,19 +387,9 @@ struct Variant[*Ts: CollectionElement]( return self._get_ptr[T]()[] @staticmethod - fn _check[T: CollectionElement]() -> Int8: + fn _check[T: CollectionElement]() -> Int: @parameter for i in range(len(VariadicList(Ts))): if _type_is_eq[Ts[i], T](): return i - return -1 - - @staticmethod - fn _size() -> Int: - var size = 0 - - @parameter - for i in range(len(VariadicList(Ts))): - alias element_size = _align_up(sizeof[Ts[i]](), alignof[Ts[i]]()) - size = max(size, element_size) - return _align_up(size, alignof[Int]()) + return Self._sentinel From 165bb46b732cc7b01e3b4a21576f3369fcf620d7 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 31 Jul 2024 17:35:20 -0700 Subject: [PATCH 1342/2019] [mojo] Implement named result slots and use them in the stdlib Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This feature provides more flexibility and guarantees around emplacing the result of a function compared to "guaranteed" NRVO. If a `@register_passable` result is bound to a name, the function alters the ABI to return the result through a result slot. ```mojo fn efficiently_return_string(b: Bool) -> String as output: if b: String.__init__(output, "emplaced!") return return "regular return" ``` In a function with a named result, `return` may be used with no operand to signal an exit from the function, or it can be used normally to specify the return value of the function. The compiler will error if the result is not initialized on all normal exit paths from the function. This feature was previously introduced under a decorator while the syntax was under debate. MODULAR_ORIG_COMMIT_REV_ID: c8239c80bfbb5d78fb35844ca7563fd0a68be776 --- docs/changelog.md | 24 ++++++++++++++++++++++++ stdlib/src/builtin/coroutine.mojo | 6 ++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 887b9ef263..911f5cf19e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,30 @@ what we publish. ### ⭐️ New +- Mojo now supports named result bindings. Named result bindings are useful for + directly emplacing function results into the output slot of a function. This + feature provides more flexibility and guarantees around emplacing the result + of a function compared to "guaranteed" NRVO. If a `@register_passable` result + is bound to a name, the result value is made accessible as a mutable + reference. + + ```mojo + fn efficiently_return_string(b: Bool) -> String as output: + if b: + output = "emplaced!" + mutate(output) + return + return "regular return" + ``` + + If we used a temporary for `output` instead, we would need to move into the + result slot, which wouldn't work if the result type was non-movable. + + In a function with a named result, `return` may be used with no operand to + signal an exit from the function, or it can be used normally to specify the + return value of the function. The compiler will error if the result is not + initialized on all normal exit paths from the function. + - `String` class now have `rjust`, `ljust` and `center` methods to return a justified string based on width and fillchar. ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index c7d74bb558..36be7710d1 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -134,8 +134,7 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: __mlir_op.`co.destroy`(self._handle) @always_inline - @__named_result(out) - fn __await__(owned self) -> type: + fn __await__(owned self) -> type as out: """Suspends the current coroutine until the coroutine is complete. Returns: @@ -215,8 +214,7 @@ struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: __mlir_op.`co.destroy`(self._handle) @always_inline - @__named_result(out) - fn __await__(owned self) raises -> type: + fn __await__(owned self) raises -> type as out: """Suspends the current coroutine until the coroutine is complete. Returns: From 504351d374acbb1a43c32f2bb8f0f8dd51fae68c Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 1 Aug 2024 10:01:33 -0500 Subject: [PATCH 1343/2019] [All][NFC] Import what you use When starting to pull on the thread for changing how builtin entities are parsed and auto-imported into every Mojo program, this exposed many files that are relying on entities getting transitively included on their behalf. Since the transitive closure import behavior will eventually go away when we're done, fix these uses to import what they use explicitly. MODULAR_ORIG_COMMIT_REV_ID: 35cbfa810c050cee2a6d945de963ea28581a1acc --- examples/matmul.mojo | 4 ++-- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/benchmarks/utils/bench_memmem.mojo | 1 + stdlib/src/builtin/simd.mojo | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 35412ebcc4..d1b8623abd 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -22,8 +22,8 @@ from sys import info import benchmark from algorithm import Static2DTileUnitFunc as Tile2DFunc from algorithm import parallelize, vectorize -from memory import memset_zero -from python import Python +from memory import memset_zero, stack_allocation +from python import Python, PythonObject alias M = 512 # rows of A and C alias N = 4096 # cols of B and C diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index b708661fc5..20c8ee0966 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -33,7 +33,7 @@ from benchmark import ( run, ) from buffer import Buffer -from memory import UnsafePointer +from memory import UnsafePointer, memset_zero @value diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 3c8ecb31c7..3e7ca664c9 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -16,6 +16,7 @@ from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width +from memory import memcmp from utils.stringref import _align_down, _memchr, _memmem diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7e7e758763..d9d40ab8c1 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -36,7 +36,7 @@ from builtin.hash import _hash_simd from builtin.format_int import _try_write_int from memory import bitcast, UnsafePointer -from utils import InlineArray, StringSlice, StaticIntTuple +from utils import InlineArray, StringSlice, StaticIntTuple, Span from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan From cdd50cf2c491d9f5bc05bc6fddfb5d00688ab636 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 1 Aug 2024 20:47:52 -0400 Subject: [PATCH 1344/2019] [stdlib] Change `SIMD.format_to` to `@no_inline` Excessive uses of `@always_inline` blows up compile time. Since `format_to` is a relatively heavy function, we shouldn't inline it. MODULAR_ORIG_COMMIT_REV_ID: 2ff1fa2525d29153ce9ac1a6e1d00b4a5cea3911 --- stdlib/src/builtin/simd.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d9d40ab8c1..3777c95705 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1501,7 +1501,7 @@ struct SIMD[type: DType, size: Int]( ] ](self.value) - @always_inline + @no_inline fn format_to(self, inout writer: Formatter): """ Formats this SIMD value to the provided formatter. @@ -1514,7 +1514,7 @@ struct SIMD[type: DType, size: Int]( # This overload is required to keep SIMD compliant with the Formattable # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will # fail to compile. - @always_inline + @no_inline fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): """ Formats this SIMD value to the provided formatter. From 4616534a76fb73e92a11d62626a76ee038eff8bc Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:01:06 -0400 Subject: [PATCH 1345/2019] [stdlib] Rework `bench_sort` - Re-seed for each benchmark entry. - Re-generate random list for each iteration of the benchmark. - Separate the list generation into a `preproc` function that is not count towards total time. - `int` and `uint` really give almost the same result everytime. So we remove `int`. - Restructured code. - Benchmark larger list size $2^{20}$. MODULAR_ORIG_COMMIT_REV_ID: 0d059c3966d78c170d4805edfd36a24c31bcff20 --- stdlib/benchmarks/builtin/bench_sort.mojo | 403 ++++++++++++---------- 1 file changed, 230 insertions(+), 173 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 3c269f6c42..b174a72d1a 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -27,38 +27,19 @@ from stdlib.builtin.sort import ( # Benchmark Utils # ===----------------------------------------------------------------------===# -alias dtypes = List( - DType.uint8, - DType.int8, - DType.uint16, - DType.int16, - DType.float16, - DType.uint32, - DType.int32, - DType.float32, - DType.uint64, - DType.int64, - DType.float64, -) - @always_inline -fn random_scalar_list[ +fn randomize_list[ dt: DType -](size: Int, max: Scalar[dt] = Scalar[dt].MAX) -> List[Scalar[dt]]: - var result = List[Scalar[dt]](capacity=size) - for _ in range(size): - - @parameter - if dt.is_integral() and dt.is_signed(): - result.append(random_si64(0, max.cast[DType.int64]()).cast[dt]()) - elif dt.is_integral() and dt.is_unsigned(): - result.append(random_ui64(0, max.cast[DType.uint64]()).cast[dt]()) - else: +](inout list: List[Scalar[dt]], size: Int, max: Scalar[dt] = Scalar[dt].MAX): + @parameter + if dt.is_integral(): + randint(list.data, size, 0, int(max)) + else: + for i in range(size): var res = random_float64() # GCC doesn't support cast from float64 to float16 - result.append(res.cast[DType.float32]().cast[dt]()) - return result + list[i] = res.cast[DType.float32]().cast[dt]() @always_inline @@ -99,103 +80,140 @@ fn heap_sort[type: DType](inout list: List[Scalar[type]]): # ===----------------------------------------------------------------------===# -@parameter -fn bench_tiny_list_sort(inout m: Bench) raises: +fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: alias small_list_size = 5 @parameter - for type_index in range(len(dtypes)): - alias dt = dtypes[type_index] + for count in range(2, small_list_size + 1): @parameter - for count in range(2, small_list_size + 1): - var list = random_scalar_list[dt](count) + fn bench_sort_list(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count + ) + @always_inline @parameter - fn bench_sort_list(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - sort(l1) - - b.iter[call_fn]() + fn preproc(): + randomize_list(list, count) + @always_inline @parameter - fn bench_small_sort(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - small_sort[count, dt](l1) + fn call_fn(): + sort(list) - b.iter[call_fn]() + b.iter_preproc[call_fn, preproc]() + _ = list^ + @parameter + fn bench_small_sort(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count + ) + + @always_inline @parameter - fn bench_insertion_sort(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - insertion_sort[dt](l1) + fn preproc(): + randomize_list(list, count) - b.iter[call_fn]() + @always_inline + @parameter + fn call_fn(): + small_sort[count](list) - m.bench_function[bench_sort_list]( - BenchId("std_sort_random_" + str(count) + "_" + str(dt)) - ) - m.bench_function[bench_small_sort]( - BenchId("sml_sort_random_" + str(count) + "_" + str(dt)) - ) - m.bench_function[bench_insertion_sort]( - BenchId("ins_sort_random_" + str(count) + "_" + str(dt)) + b.iter_preproc[call_fn, preproc]() + _ = list^ + + @parameter + fn bench_insertion_sort(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count ) + + @always_inline + @parameter + fn preproc(): + randomize_list(list, count) + + @always_inline + @parameter + fn call_fn(): + insertion_sort(list) + + b.iter_preproc[call_fn, preproc]() _ = list^ + m.bench_function[bench_sort_list]( + BenchId("std_sort_random_" + str(count) + "_" + str(type)) + ) + m.bench_function[bench_small_sort]( + BenchId("sml_sort_random_" + str(count) + "_" + str(type)) + ) + m.bench_function[bench_insertion_sort]( + BenchId("ins_sort_random_" + str(count) + "_" + str(type)) + ) + # ===----------------------------------------------------------------------===# # Benchmark sort functions with a small list size # ===----------------------------------------------------------------------===# -@parameter -fn bench_small_list_sort(inout m: Bench) raises: - var counts = List(10, 20, 32, 64, 100) - +fn bench_small_list_sort[type: DType](inout m: Bench, count: Int) raises: @parameter - for type_index in range(len(dtypes)): - alias dt = dtypes[type_index] + fn bench_sort_list(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count + ) + + @always_inline + @parameter + fn preproc(): + randomize_list(list, count) - for count in counts: - var list = random_scalar_list[dt](count[]) + @always_inline + @parameter + fn call_fn(): + sort(list) - @parameter - fn bench_sort_list(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - sort(l1) + b.iter_preproc[call_fn, preproc]() + _ = list^ - b.iter[call_fn]() + @parameter + fn bench_insertion_sort(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count + ) + + @always_inline + @parameter + fn preproc(): + randomize_list(list, count) - @parameter - fn bench_insertion_sort(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - insertion_sort[dt](l1) + @always_inline + @parameter + fn call_fn(): + insertion_sort(list) - b.iter[call_fn]() + b.iter_preproc[call_fn, preproc]() + _ = list^ - m.bench_function[bench_sort_list]( - BenchId("std_sort_random_" + str(count[]) + "_" + str(dt)) - ) - m.bench_function[bench_insertion_sort]( - BenchId("ins_sort_random_" + str(count[]) + "_" + str(dt)) - ) - _ = list^ + m.bench_function[bench_sort_list]( + BenchId("std_sort_random_" + str(count) + "_" + str(type)) + ) + m.bench_function[bench_insertion_sort]( + BenchId("ins_sort_random_" + str(count) + "_" + str(type)) + ) # ===----------------------------------------------------------------------===# @@ -203,45 +221,56 @@ fn bench_small_list_sort(inout m: Bench) raises: # ===----------------------------------------------------------------------===# -@parameter -fn bench_large_list_sort(inout m: Bench) raises: - var counts = List(1 << 12, 1 << 16) - +fn bench_large_list_sort[type: DType](inout m: Bench, count: Int) raises: @parameter - for type_index in range(len(dtypes)): - alias dt = dtypes[type_index] + fn bench_sort_list(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count + ) + + @always_inline + @parameter + fn preproc(): + randomize_list(list, count) - for count in counts: - var list = random_scalar_list[dt](count[]) + @always_inline + @parameter + fn call_fn(): + sort(list) - @parameter - fn bench_sort_list(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - sort(l1) + b.iter_preproc[call_fn, preproc]() + _ = list^ - b.iter[call_fn]() + @parameter + fn bench_heap_sort(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[Scalar[type]].alloc(count) + var list = List[Scalar[type]]( + unsafe_pointer=ptr, size=count, capacity=count + ) + + @always_inline + @parameter + fn preproc(): + randomize_list(list, count) - @parameter - fn bench_heap_sort(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - heap_sort(l1) + @always_inline + @parameter + fn call_fn(): + heap_sort(list) - b.iter[call_fn]() + b.iter_preproc[call_fn, preproc]() + _ = list^ - m.bench_function[bench_sort_list]( - BenchId("std_sort_random_" + str(count[]) + "_" + str(dt)) - ) + m.bench_function[bench_sort_list]( + BenchId("std_sort_random_" + str(count) + "_" + str(type)) + ) - m.bench_function[bench_heap_sort]( - BenchId("heap_sort_random_" + str(count[]) + "_" + str(dt)) - ) - _ = list^ + m.bench_function[bench_heap_sort]( + BenchId("heap_sort_random_" + str(count) + "_" + str(type)) + ) # ===----------------------------------------------------------------------===# @@ -249,53 +278,53 @@ fn bench_large_list_sort(inout m: Bench) raises: # ===----------------------------------------------------------------------===# -@parameter -fn bench_low_cardinality_list_sort(inout m: Bench) raises: - var counts = List(1 << 12, 1 << 16) - var deltas = List(0, 2, 5, 20, 100) +fn bench_low_cardinality_list_sort( + inout m: Bench, count: Int, delta: Int +) raises: + @parameter + fn bench_sort_list(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[UInt8].alloc(count) + var list = List[UInt8](unsafe_pointer=ptr, size=count, capacity=count) - for delta in deltas: - for count in counts: - var list = random_scalar_list[DType.uint8](count[], delta[]) + @always_inline + @parameter + fn preproc(): + randomize_list(list, count, delta) - @parameter - fn bench_sort_list(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - sort(l1) + @always_inline + @parameter + fn call_fn(): + sort(list) - b.iter[call_fn]() + b.iter_preproc[call_fn, preproc]() + _ = list^ - @parameter - fn bench_heap_sort(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn(): - var l1 = list - heap_sort(l1) - - b.iter[call_fn]() - - m.bench_function[bench_sort_list]( - BenchId( - "std_sort_low_card_" - + str(count[]) - + "_delta_" - + str(delta[]) - ) - ) + @parameter + fn bench_heap_sort(inout b: Bencher) raises: + seed(1) + var ptr = UnsafePointer[UInt8].alloc(count) + var list = List[UInt8](unsafe_pointer=ptr, size=count, capacity=count) - m.bench_function[bench_heap_sort]( - BenchId( - "heap_sort_low_card_" - + str(count[]) - + "_delta_" - + str(delta[]) - ) - ) - _ = list^ + @always_inline + @parameter + fn preproc(): + randomize_list(list, count, delta) + + @always_inline + @parameter + fn call_fn(): + heap_sort(list) + + b.iter_preproc[call_fn, preproc]() + _ = list^ + + m.bench_function[bench_sort_list]( + BenchId("std_sort_low_card_" + str(count) + "_delta_" + str(delta)) + ) + m.bench_function[bench_heap_sort]( + BenchId("heap_sort_low_card_" + str(count) + "_delta_" + str(delta)) + ) # ===----------------------------------------------------------------------===# @@ -304,12 +333,40 @@ fn bench_low_cardinality_list_sort(inout m: Bench) raises: def main(): - seed(1) - var m = Bench() + var m = Bench(BenchConfig(max_runtime_secs=0.1)) + + alias dtypes = List( + DType.uint8, + DType.uint16, + DType.float16, + DType.uint32, + DType.float32, + DType.uint64, + DType.float64, + ) + var small_counts = List(10, 20, 32, 64, 100) + var large_counts = List(2**12, 2**16, 2**20) + var deltas = List(0, 2, 5, 20, 100) + + @parameter + for i in range(len(dtypes)): + alias type = dtypes[i] + bench_tiny_list_sort[type](m) + + @parameter + for i in range(len(dtypes)): + alias type = dtypes[i] + for count in small_counts: + bench_small_list_sort[type](m, count[]) - bench_tiny_list_sort(m) - bench_small_list_sort(m) - bench_large_list_sort(m) - bench_low_cardinality_list_sort(m) + @parameter + for i in range(len(dtypes)): + alias type = dtypes[i] + for count in large_counts: + bench_large_list_sort[type](m, count[]) + + for count in large_counts: + for delta in deltas: + bench_low_cardinality_list_sort(m, count[], delta[]) m.dump_report() From be885bb77d5ab77793706ab8f53f8e6603ea254c Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 2 Aug 2024 12:47:33 -0400 Subject: [PATCH 1346/2019] [stdlib] Improve `_quicksort` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Push `Span` into the stack instead of pair of `start, end`. This results in small improvement in small lists. ``` [M] (autovenv) dan13@Daniels-MacBook-Pro modular % grep std_sort_random_32 branch_vs_main.csv std_sort_random_32_uint8 , 0.001028, 0.001677,0.61 ✅, 117130, 71422,1.64 ✅ std_sort_random_32_uint16 , 0.001033, 0.001701,0.61 ✅, 115612, 70953,1.63 ✅ std_sort_random_32_float16 , 0.001045, 0.001630,0.64 ✅, 114536, 74039,1.55 ✅ std_sort_random_32_uint32 , 0.001036, 0.001736,0.60 ✅, 115512, 69052,1.67 ✅ std_sort_random_32_float32 , 0.001036, 0.001638,0.63 ✅, 116788, 73072,1.60 ✅ std_sort_random_32_uint64 , 0.001047, 0.001689,0.62 ✅, 115329, 70698,1.63 ✅ std_sort_random_32_float64 , 0.001022, 0.001646,0.62 ✅, 116606, 72921,1.60 ✅ [M] (autovenv) dan13@Daniels-MacBook-Pro modular % grep std_sort_random_64 branch_vs_main.csv std_sort_random_64_uint8 , 0.001835, 0.002734,0.67 ✅, 65134, 43755,1.49 ✅ std_sort_random_64_uint16 , 0.001816, 0.002742,0.66 ✅, 65827, 43722,1.51 ✅ std_sort_random_64_float16 , 0.001901, 0.002701,0.70 ✅, 63048, 44163,1.43 ✅ std_sort_random_64_uint32 , 0.001807, 0.002824,0.64 ✅, 65811, 42468,1.55 ✅ std_sort_random_64_float32 , 0.001875, 0.002720,0.69 ✅, 64000, 44095,1.45 ✅ std_sort_random_64_uint64 , 0.001864, 0.002865,0.65 ✅, 64476, 41787,1.54 ✅ std_sort_random_64_float64 , 0.001892, 0.002722,0.70 ✅, 63428, 43894,1.45 ✅ [M] (autovenv) dan13@Daniels-MacBook-Pro modular % grep std_sort_random_100 branch_vs_main.csv std_sort_random_100_uint8 , 0.002842, 0.003835,0.74 ✅, 42158, 31317,1.35 ✅ std_sort_random_100_uint16 , 0.002833, 0.003848,0.74 ✅, 42335, 31040,1.36 ✅ std_sort_random_100_float16 , 0.002994, 0.003951,0.76 ✅, 40018, 30427,1.32 ✅ std_sort_random_100_uint32 , 0.002844, 0.003974,0.72 ✅, 42188, 30148,1.40 ✅ std_sort_random_100_float32 , 0.002997, 0.003919,0.76 ✅, 39956, 30575,1.31 ✅ std_sort_random_100_uint64 , 0.002911, 0.004017,0.72 ✅, 40463, 29904,1.35 ✅ std_sort_random_100_float64 , 0.002995, 0.003926,0.76 ✅, 40135, 30587,1.31 ✅ [M] (autovenv) dan13@Daniels-MacBook-Pro modular % grep std_sort_random_4096 branch_vs_main.csv std_sort_random_4096_uint8 , 0.114638, 0.112997,1.01 ❌, 1057, 1064,0.99 ❌ std_sort_random_4096_uint16 , 0.125298, 0.127057,0.99 ✅, 966, 948,1.02 ✅ std_sort_random_4096_float16 , 0.150199, 0.154812,0.97 ✅, 800, 775,1.03 ✅ std_sort_random_4096_uint32 , 0.125341, 0.127306,0.98 ✅, 962, 945,1.02 ✅ std_sort_random_4096_float32 , 0.150734, 0.155385,0.97 ✅, 796, 766,1.04 ✅ std_sort_random_4096_uint64 , 0.124346, 0.127339,0.98 ✅, 963, 943,1.02 ✅ std_sort_random_4096_float64 , 0.150279, 0.155252,0.97 ✅, 797, 775,1.03 ✅ [M] (autovenv) dan13@Daniels-MacBook-Pro modular % grep std_sort_random_65536 branch_vs_main.csv std_sort_random_65536_uint8 , 1.406500, 1.360713,1.03 ❌, 84, 87,0.97 ❌ std_sort_random_65536_uint16 , 2.490208, 2.494167,1.00 ✅, 48, 48,1.00 ✅ std_sort_random_65536_float16 , 2.796357, 2.801881,1.00 ✅, 42, 42,1.00 ✅ std_sort_random_65536_uint32 , 2.540022, 2.502938,1.01 ❌, 46, 48,0.96 ❌ std_sort_random_65536_float32 , 3.121211, 3.149132,0.99 ✅, 38, 38,1.00 ✅ std_sort_random_65536_uint64 , 2.505437, 2.506479,1.00 ✅, 48, 48,1.00 ✅ std_sort_random_65536_float64 , 3.123737, 3.154243,0.99 ✅, 38, 37,1.03 ✅ [M] (autovenv) dan13@Daniels-MacBook-Pro modular % grep std_sort_random_1048576 branch_vs_main.csv std_sort_random_1048576_uint8 , 21.877800, 20.965800,1.04 ❌, 5, 5,1.00 ✅ std_sort_random_1048576_uint16 , 47.113000, 46.436667,1.01 ❌, 3, 3,1.00 ✅ std_sort_random_1048576_float16 , 42.685000, 41.707000,1.02 ❌, 3, 3,1.00 ✅ std_sort_random_1048576_uint32 , 49.875667, 49.648333,1.00 ❌, 3, 3,1.00 ✅ std_sort_random_1048576_float32 , 62.845333, 63.281333,0.99 ✅, 3, 3,1.00 ✅ std_sort_random_1048576_uint64 , 49.692000, 49.589667,1.00 ❌, 3, 3,1.00 ✅ std_sort_random_1048576_float64 , 62.904000, 63.306333,0.99 ✅, 3, 3,1.00 ✅ ``` MODULAR_ORIG_COMMIT_REV_ID: e89531b29f4ef49aa3519e8c984edbb849619887 --- stdlib/src/builtin/sort.mojo | 79 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 2a12e6eace..90add3606b 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import List from sys import bitwidthof +from math import ceil from bit import count_leading_zeros from memory import UnsafePointer @@ -26,6 +27,8 @@ from utils import Span # sort # ===----------------------------------------------------------------------===# +alias insertion_sort_threshold = 32 + @value struct _SortWrapper[type: CollectionElement](CollectionElement): @@ -160,7 +163,9 @@ fn _estimate_initial_height(size: Int) -> Int: var log2 = int( (bitwidthof[DType.index]() - 1) ^ count_leading_zeros(size | 1) ) - return max(2, log2) + # The number 1.3 was chosen by experimenting the max stack size for random + # input. This also depends on insertion_sort_threshold + return max(2, int(ceil(1.3 * log2))) @always_inline @@ -188,6 +193,13 @@ fn _delegate_small_sort[ return +# FIXME (MSTDL-808): Using _Pair over Span results in 1-3% improvement +# @value +# struct _Pair[type: AnyType]: +# var ptr: UnsafePointer[type] +# var len: Int + + @always_inline fn _quicksort[ type: CollectionElement, @@ -198,53 +210,50 @@ fn _quicksort[ var size = len(span) if size == 0: return - var stack = List[Int](capacity=_estimate_initial_height(size)) - stack.append(0) - stack.append(size) + var stack = List[Span[type, lifetime]]( + capacity=_estimate_initial_height(size) + ) + stack.append(span) while len(stack) > 0: - var end = stack.pop() - var start = stack.pop() - - var len = end - start + var interval = stack.pop() + var ptr = interval.unsafe_ptr() + var len = len(interval) if len <= 5: - _delegate_small_sort[cmp_fn]( - Span[type, lifetime](unsafe_ptr=array + start, len=len) - ) + _delegate_small_sort[cmp_fn](interval) continue - if len < 32: - _insertion_sort[cmp_fn]( - Span[type, lifetime](unsafe_ptr=array + start, len=len) - ) + if len < insertion_sort_threshold: + _insertion_sort[cmp_fn](interval) continue # pick median of 3 as pivot - _sort3[type, cmp_fn](array, (start + end) >> 1, start, end - 1) + _sort3[type, cmp_fn](ptr, len >> 1, 0, len - 1) - # if array[start - 1] == pivot_value, then everything in between will + # if ptr[-1] == pivot_value, then everything in between will # be the same, so no need to recurse that interval - # already have array[start - 1] <= array[start] - if start > 0 and not cmp_fn(array[start - 1], array[start]): - var pivot = start + _quicksort_partition_left[cmp_fn]( - Span[type, lifetime](unsafe_ptr=array + start, len=len) - ) - if end > pivot + 2: - stack.append(pivot + 1) - stack.append(end) + # already have array[-1] <= array[0] + if ptr > array and not cmp_fn(ptr[-1], ptr[0]): + var pivot = _quicksort_partition_left[cmp_fn](interval) + if len > pivot + 2: + stack.append( + Span[type, lifetime]( + unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1 + ) + ) continue - var pivot = start + _quicksort_partition_right[cmp_fn]( - Span[type, lifetime](unsafe_ptr=array + start, len=len) - ) + var pivot = _quicksort_partition_right[cmp_fn](interval) - if end > pivot + 2: - stack.append(pivot + 1) - stack.append(end) + if len > pivot + 2: + stack.append( + Span[type, lifetime]( + unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1 + ) + ) - if pivot > start + 1: - stack.append(start) - stack.append(pivot) + if pivot > 1: + stack.append(Span[type, lifetime](unsafe_ptr=ptr, len=pivot)) # ===----------------------------------------------------------------------===# @@ -397,7 +406,7 @@ fn _sort[ _delegate_small_sort[cmp_fn](span) return - if len(span) < 32: + if len(span) < insertion_sort_threshold: _insertion_sort[cmp_fn](span) return From 2fb5802144161f84ff0e40ad3266d819acc08ba1 Mon Sep 17 00:00:00 2001 From: soraros Date: Fri, 2 Aug 2024 13:17:05 -0700 Subject: [PATCH 1347/2019] [External] [stdlib] Use infer only in `bit` (#44553) [External] [stdlib] Use infer only in `bit` Also changed parameter names to be more uniform. Co-authored-by: soraros Closes modularml/mojo#3347 MODULAR_ORIG_COMMIT_REV_ID: 383e3c5d0637d847267ea91791128f20d4a7f82c --- stdlib/src/bit/bit.mojo | 86 +++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 4fb32bcc5f..3546244110 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -42,13 +42,13 @@ fn count_leading_zeros(val: Int) -> Int: @always_inline("nodebug") fn count_leading_zeros[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Counts the per-element number of leading zeros in a SIMD vector. Parameters: type: `DType` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -86,13 +86,13 @@ fn count_trailing_zeros(val: Int) -> Int: @always_inline("nodebug") fn count_trailing_zeros[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Counts the per-element number of trailing zeros in a SIMD vector. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -130,13 +130,13 @@ fn bit_reverse(val: Int) -> Int: @always_inline("nodebug") fn bit_reverse[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Element-wise reverses the bitpattern of a SIMD vector of integer values. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Args: val: The input value. @@ -179,8 +179,8 @@ fn byte_swap(val: Int) -> Int: @always_inline("nodebug") fn byte_swap[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Byte-swaps a SIMD vector of integer values with an even number of bytes. Byte swap an integer value or vector of integer values with an even number @@ -193,7 +193,7 @@ fn byte_swap[ Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be an integral type with an @@ -232,13 +232,13 @@ fn pop_count(val: Int) -> Int: @always_inline("nodebug") fn pop_count[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Counts the number of bits set in a SIMD vector of integer values. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -263,13 +263,13 @@ fn pop_count[ @always_inline("nodebug") fn bit_not[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Performs a bitwise NOT operation on an SIMD vector of integer values. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -282,7 +282,7 @@ fn bit_not[ NOT of the integer value at position `i` of the input value. """ constrained[type.is_integral(), "must be integral"]() - var neg_one = SIMD[type, simd_width](-1) + var neg_one = SIMD[type, width](-1) return __mlir_op.`pop.xor`(val.value, neg_one.value) @@ -308,14 +308,14 @@ fn bit_width(val: Int) -> Int: @always_inline fn bit_width[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Computes the minimum number of bits required to represent the SIMD vector of integer values. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -364,13 +364,13 @@ fn is_power_of_two(val: Int) -> Bool: @always_inline fn is_power_of_two[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[DType.bool, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[DType.bool, width]: """Checks if the input value is a power of 2 for each element of a SIMD vector. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -415,15 +415,15 @@ fn bit_ceil(val: Int) -> Int: @always_inline("nodebug") fn bit_ceil[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Computes the smallest power of 2 that is greater than or equal to the input value for each element of a SIMD vector. Any integral value less than or equal to 1 will be ceiled to 1. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -438,7 +438,7 @@ fn bit_ceil[ """ constrained[type.is_integral(), "must be integral"]() - alias ones = SIMD[type, simd_width](1) + alias ones = SIMD[type, width](1) return (val > 1).select(1 << bit_width(val - ones), ones) @@ -468,15 +468,15 @@ fn bit_floor(val: Int) -> Int: @always_inline("nodebug") fn bit_floor[ - type: DType, simd_width: Int -](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + type: DType, width: Int, // +](val: SIMD[type, width]) -> SIMD[type, width]: """Computes the largest power of 2 that is less than or equal to the input value for each element of a SIMD vector. Any integral value less than or equal to 0 will be floored to 0. Parameters: type: `dtype` used for the computation. - simd_width: SIMD width used for the computation. + width: SIMD width used for the computation. Constraints: The element type of the input vector must be integral. @@ -491,7 +491,7 @@ fn bit_floor[ """ constrained[type.is_integral(), "must be integral and unsigned"]() - alias zeros = SIMD[type, simd_width](0) + alias zeros = SIMD[type, width](0) return (val > 0).select(1 << (bit_width(val) - 1), zeros) @@ -536,7 +536,9 @@ fn rotate_bits_left[shift: Int](x: Int) -> Int: fn rotate_bits_left[ - shift: Int, type: DType, width: Int + type: DType, + width: Int, //, + shift: Int, ](x: SIMD[type, width]) -> SIMD[type, width]: """Shifts bits to the left by `shift` positions (with wrap-around) for each element of a SIMD vector. @@ -545,11 +547,11 @@ fn rotate_bits_left[ `0 <= shift < size` Parameters: - shift: The number of positions by which to shift left the bits for each - element of a SIMD vector to the left (with wrap-around). type: The `dtype` of the input and output SIMD vector. Constraints: must be integral and unsigned. width: The width of the input and output SIMD vector. + shift: The number of positions by which to shift left the bits for each + element of a SIMD vector to the left (with wrap-around). Args: x: SIMD vector to perform the operation on. @@ -565,7 +567,7 @@ fn rotate_bits_left[ if shift == 0: return x elif shift < 0: - return rotate_bits_right[-shift, type, width](x) + return rotate_bits_right[-shift](x) else: return llvm_intrinsic["llvm.fshl", __type_of(x), has_side_effect=False]( x, x, SIMD[type, width](shift) @@ -612,9 +614,9 @@ fn rotate_bits_right[shift: Int](x: Int) -> Int: fn rotate_bits_right[ - shift: Int, type: DType, - width: Int, + width: Int, //, + shift: Int, ](x: SIMD[type, width]) -> SIMD[type, width]: """Shifts bits to the right by `shift` positions (with wrap-around) for each element of a SIMD vector. @@ -623,11 +625,11 @@ fn rotate_bits_right[ `0 <= shift < size` Parameters: - shift: The number of positions by which to shift right the bits for each - element of a SIMD vector to the left (with wrap-around). type: The `dtype` of the input and output SIMD vector. Constraints: must be integral and unsigned. width: The width of the input and output SIMD vector. + shift: The number of positions by which to shift right the bits for each + element of a SIMD vector to the left (with wrap-around). Args: x: SIMD vector to perform the operation on. @@ -643,7 +645,7 @@ fn rotate_bits_right[ if shift == 0: return x elif shift < 0: - return rotate_bits_left[-shift, type, width](x) + return rotate_bits_left[-shift](x) else: return llvm_intrinsic["llvm.fshr", __type_of(x), has_side_effect=False]( x, x, SIMD[type, width](shift) From b8c4abbbb49824bca09266f55db6f2661a411712 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 2 Aug 2024 17:00:55 -0500 Subject: [PATCH 1348/2019] [stdlib] polish: Change `debug_assert` to require `Formattable` This makes debug_assert consistent with print() and abort() in requiring `Formattable` instead of `Stringable` for their args, avoiding unnecessary allocations. MODULAR_ORIG_COMMIT_REV_ID: 9d75f8c36d060aae7c0ef37cd70ccbccb31fd9e8 --- docs/changelog.md | 3 +++ stdlib/src/builtin/debug_assert.mojo | 18 +++++++-------- stdlib/test/builtin/test_debug_assert.mojo | 27 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 911f5cf19e..7eb61084ab 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -542,6 +542,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. > If the above error is seen, ensure that all argument types implement > `Formattable`. +- `debug_assert()` now also requires that its `message` argument conform to + `Formattable`. + - The `StringRef` constructors from `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, reflecting their use for compatibility with C APIs. diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 0a757b8324..09a97101de 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -33,8 +33,8 @@ alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() @always_inline fn debug_assert[ - func: fn () capturing -> Bool, stringable: Stringable -](message: stringable): + func: fn () capturing -> Bool, formattable: Formattable +](message: formattable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -48,7 +48,7 @@ fn debug_assert[ func: The function to invoke to check if the assertion holds. Can be used if the function is side-effecting, in which case a debug_assert taking a Bool will evaluate the expression producing the Bool even in release mode. - stringable: The type of the message. + formattable: The type of the message. Args: message: The message to convert to `String` before displaying it on failure. @@ -60,7 +60,7 @@ fn debug_assert[ @always_inline -fn debug_assert[stringable: Stringable](cond: Bool, message: stringable): +fn debug_assert[formattable: Formattable](cond: Bool, message: formattable): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -71,7 +71,7 @@ fn debug_assert[stringable: Stringable](cond: Bool, message: stringable): for enabling assertions in the library. Parameters: - stringable: The type of the message. + formattable: The type of the message. Args: cond: The bool value to assert. @@ -89,8 +89,8 @@ fn debug_assert[stringable: Stringable](cond: Bool, message: stringable): @no_inline fn _debug_assert_msg[ - stringable: Stringable, //, *, is_warning: Bool = False -](msg: stringable, loc: _SourceLocation): + formattable: Formattable, //, *, is_warning: Bool = False +](msg: formattable, loc: _SourceLocation): """Aborts with (or prints) the given message and location. This function is intentionally marked as no_inline to reduce binary size. @@ -113,6 +113,6 @@ fn _debug_assert_msg[ @parameter if is_warning: - print(loc.prefix("Assert Warning:"), str(msg)) + print(loc.prefix("Assert Warning:"), msg) else: - abort(loc.prefix("Assert Error: " + str(msg))) + abort(loc.prefix("Assert Error: " + String.format_sequence(msg))) diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index 594e54ce16..f8a605b978 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -17,10 +17,31 @@ # RUN: %mojo -D DEBUG -debug-level full %s | FileCheck %s -check-prefix=CHECK-OK -# CHECK-OK-LABEL: test_ok -fn main(): - print("== test_ok") +def main(): + test_debug_assert() + test_debug_assert_formattable() + + +# CHECK-OK-LABEL: test_debug_assert +def test_debug_assert(): + print("== test_debug_assert") debug_assert(True, "ok") debug_assert(3, Error("also ok")) # CHECK-OK: is reached print("is reached") + + +# CHECK-OK-LABEL: test_debug_assert_formattable +def test_debug_assert_formattable(): + print("== test_debug_assert_formattable") + debug_assert(True, FormattableOnly("failed with Formattable arg")) + # CHECK-OK: is reached + print("is reached") + + +@value +struct FormattableOnly: + var message: String + + fn format_to(self, inout writer: Formatter): + writer.write(self.message) From 1ff116034ba5107cdb6a787152d32d3df693f15c Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 2 Aug 2024 17:43:11 -0500 Subject: [PATCH 1349/2019] [stdlib] polish: Change `List` variadic initializer to take `owned *values` This makes it clear to users of this initializer that it requires owned values. Previously, this initializer would implicitly rely on being able to copy the passed arguments. MODULAR_ORIG_COMMIT_REV_ID: 5d153d597609e26552eccc9468f368061fca1e38 --- stdlib/src/builtin/builtin_list.mojo | 44 ++++++++++++++++++++++++++ stdlib/src/collections/list.mojo | 29 +++++++++++++---- stdlib/test/collections/test_list.mojo | 11 +++++++ 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index df0e3c8d81..1ba3923f02 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -36,6 +36,10 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): var storage: Tuple[Ts] """The underlying storage for the list.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self, owned *args: *Ts): """Construct the list literal from the given values. @@ -54,6 +58,10 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): self.storage = existing.storage^ + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __len__(self) -> Int: """Get the list length. @@ -63,6 +71,10 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): """ return len(self.storage) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn get[i: Int, T: Movable](self) -> ref [__lifetime_of(self)] T: """Get a list element at the given index. @@ -77,6 +89,10 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): # FIXME: Rebinding to a different lifetime. return UnsafePointer.address_of(self.storage[i]).bitcast[T]()[] + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __contains__[T: EqualityComparable](self, value: T) -> Bool: """Determines if a given value exists in the ListLiteral. @@ -291,6 +307,10 @@ struct VariadicListMem[ # the VariadicListMem is destroyed. var _is_owned: Bool + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + # Provide support for borrowed variadic arguments. @always_inline fn __init__(inout self, value: Self._mlir_type): @@ -375,6 +395,10 @@ struct VariadicListMem[ for i in reversed(range(len(self))): UnsafePointer.address_of(self[i]).destroy_pointee() + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @always_inline fn __len__(self) -> Int: """Gets the size of the list. @@ -384,6 +408,10 @@ struct VariadicListMem[ """ return __mlir_op.`pop.variadic.size`(self.value) + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + @always_inline fn __getitem__( self, idx: Int @@ -536,6 +564,10 @@ struct VariadicPack[ var _value: Self._mlir_type var _is_owned: Bool + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline fn __init__(inout self, value: Self._mlir_type, is_owned: Bool): """Constructs a VariadicPack from the internal representation. @@ -563,6 +595,10 @@ struct VariadicPack[ for i in reversed(range(Self.__len__())): UnsafePointer.address_of(self[i]).destroy_pointee() + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @always_inline @staticmethod fn __len__() -> Int: @@ -590,6 +626,10 @@ struct VariadicPack[ """ return Self.__len__() + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + @always_inline fn __getitem__[ index: Int @@ -615,6 +655,10 @@ struct VariadicPack[ ) return ref_elt.bitcast[element_types[index.value]]()[] + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline fn each[func: fn[T: element_trait] (T) capturing -> None](self): """Apply a function to each element of the pack in order. This applies diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 44029d06b5..a239364511 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -125,17 +125,34 @@ struct List[T: CollectionElement]( self.size = 0 self.capacity = capacity - # TODO: Avoid copying elements in once owned varargs - # allow transfers. - fn __init__(inout self, *values: T): + fn __init__(inout self, owned *values: T): """Constructs a list from the given values. Args: values: The values to populate the list with. """ - self = Self(capacity=len(values)) - for value in values: - self.append(value[]) + self = Self(variadic_list=values^) + + fn __init__(inout self, *, owned variadic_list: VariadicListMem[T, _, _]): + """Constructs a list from the given values. + + Args: + variadic_list: The values to populate the list with. + """ + var length = len(variadic_list) + + self = Self(capacity=length) + + for i in range(length): + var src = UnsafePointer.address_of(variadic_list[i]) + var dest = self.data + i + + src.move_pointee_into(dest) + + # Mark the elements as unowned to avoid del'ing uninitialized objects. + variadic_list._is_owned = False + + self.size = length fn __init__(inout self, span: Span[T]): """Constructs a list from the a Span of values. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 9db6539e16..9b6c942eab 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -160,6 +160,17 @@ def test_list_variadic_constructor(): assert_equal(4, len(l)) assert_equal(8, l[3]) + # + # Test variadic construct copying behavior + # + + var l2 = List[CopyCounter](CopyCounter(), CopyCounter(), CopyCounter()) + + assert_equal(len(l2), 3) + assert_equal(l2[0].copy_count, 0) + assert_equal(l2[1].copy_count, 0) + assert_equal(l2[2].copy_count, 0) + def test_list_resize(): var l = List[Int](1) From 9f379ea3db7698b3dbd7c0e95c74876bd234741e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 3 Aug 2024 21:38:49 -0700 Subject: [PATCH 1350/2019] [mojo-lang] Tighten up MLValue/MRValue/MBValue checking. (#44580) This documents *why* MBValue can have a value of arbitrary mutability and therefore why we can't merge MBValue and MLValue into one thing. This adds assertions that the reference mutability is correct per these requirements, and fixes a place where we had it wrong. We are not handling parametric lifetimes correctly, but that will be handled in a follow-up patch. MODULAR_ORIG_COMMIT_REV_ID: 27e8e5b457bf802711f854a07a524b1edf8a5862 --- stdlib/src/collections/dict.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 24cd1b3561..be07838a5f 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -895,7 +895,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = _DictIndex(self._reserved()) fn setdefault( - ref [_]self: Self, key: K, owned default: V + inout self, key: K, owned default: V ) raises -> Reference[V, __lifetime_of(self)]: """Get a value from the dictionary by key, or set it to a default if it doesn't exist. From 1db280a89e92e16515b01ec5461c63001f002ad2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 4 Aug 2024 00:01:13 -0700 Subject: [PATCH 1351/2019] [mojo-lang] Improve ref argument handling This improves handling of ref arguments in a few ways, making parametric and readonly ref arguments bind to (almost) the same things as borrowed arguments do. This includes: * enables implicit conversion, so you can pass an IntLiteral into a ref argument that expects an immutable int. * Inferring proper immutability of ref lifetime when binding to something that requires a conversion. Getting this right required plumbing through MBPValue in the previous work. This still isn't enough, because MBValue arguments allow passing SBValues and have an interesting hack to enabling that. This will need more generalized writeback support to enable. MODULAR_ORIG_COMMIT_REV_ID: 866586db29a4549ae6161230404fdedfca75ab58 --- stdlib/src/collections/dict.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index be07838a5f..bd91fd17bc 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -84,7 +84,7 @@ struct _DictEntryIter[ return self @always_inline - fn __next__(inout self) -> Reference[DictEntry[K, V], Self.dict_lifetime]: + fn __next__(inout self) -> Reference[DictEntry[K, V], dict_lifetime]: while True: var opt_entry_ref = Reference(self.src[]._entries[self.index]) From 5f9a5372299cc0d5c2331fd4f2e31dd906421304 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 5 Aug 2024 13:17:38 -0500 Subject: [PATCH 1352/2019] [stdlib] polish: Make Variadic{Pack, ListMem}.elt_is_mutable param infer-only Cleans up the usage of this type a bit not having to specify this redundant parameter. MODULAR_ORIG_COMMIT_REV_ID: c833b35ebefc46e0d965af8ac3d6e9fe3d0164e8 --- stdlib/src/builtin/builtin_list.mojo | 10 ++++------ stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 1ba3923f02..7d4b32dbbc 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -227,9 +227,7 @@ struct _VariadicListMemIter[ list_lifetime: The lifetime of the VariadicListMem. """ - alias variadic_list_type = VariadicListMem[ - elt_type, elt_is_mutable.value, elt_lifetime - ] + alias variadic_list_type = VariadicListMem[elt_type, elt_lifetime] var index: Int var src: Reference[Self.variadic_list_type, list_lifetime] @@ -278,8 +276,8 @@ struct _lit_mut_cast[ struct VariadicListMem[ + elt_is_mutable: __mlir_type.i1, //, element_type: AnyType, - elt_is_mutable: __mlir_type.i1, lifetime: __mlir_type[`!lit.lifetime<`, elt_is_mutable, `>`], ](Sized): """A utility class to access variadic function arguments of memory-only @@ -287,9 +285,9 @@ struct VariadicListMem[ way that can be enumerated. Each element may be accessed with `elt[]`. Parameters: - element_type: The type of the elements in the list. elt_is_mutable: True if the elements of the list are mutable for an inout or owned argument. + element_type: The type of the elements in the list. lifetime: The reference lifetime of the underlying elements. """ @@ -535,7 +533,7 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ - elt_is_mutable: __mlir_type.i1, + elt_is_mutable: __mlir_type.i1, //, lifetime: __mlir_type[`!lit.lifetime<`, elt_is_mutable, `>`], element_trait: _AnyTypeMetaType, *element_types: element_trait, diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index dfb3d2dda5..b1b82845b7 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -177,7 +177,7 @@ struct _RefCountedAttrsDictRef(CollectionElement, CollectionElementNew): """The reference to the dictionary.""" @always_inline - fn __init__(inout self, values: VariadicListMem[Attr, _, _]): + fn __init__(inout self, values: VariadicListMem[Attr, _]): var ptr = UnsafePointer[_RefCountedAttrsDict].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() # Elements can only be added on construction. diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 4ff107102d..29b1eda15e 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -58,7 +58,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): fn __init__( inout self, *, - owned storage: VariadicPack[_, _, Movable, element_types], + owned storage: VariadicPack[_, Movable, element_types], ): """Construct the tuple from a low-level internal representation. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index a239364511..1454f089c4 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -133,7 +133,7 @@ struct List[T: CollectionElement]( """ self = Self(variadic_list=values^) - fn __init__(inout self, *, owned variadic_list: VariadicListMem[T, _, _]): + fn __init__(inout self, *, owned variadic_list: VariadicListMem[T, _]): """Constructs a list from the given values. Args: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 2bcf3b87b0..8dc6af81c4 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -331,7 +331,7 @@ struct InlineArray[ fn __init__( inout self, *, - owned storage: VariadicListMem[Self.ElementType, _, _], + owned storage: VariadicListMem[Self.ElementType, _], ): """Construct an array from a low-level internal representation. From c61758a121af2025037daa1181cf277130794e08 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 5 Aug 2024 18:28:28 -0500 Subject: [PATCH 1353/2019] [stdlib] Use `AnyLifetime` and `Bool` in `VariadicPack` and `VariadicListMem` params Also change `_LITRefPackHelper.is_mutable` to `Bool`. This avoids the use of raw MLIR types where more use-friendly names exist. MODULAR_ORIG_COMMIT_REV_ID: 71eb996bf28ade0d090aaed91029f21ca09d1523 --- stdlib/src/builtin/builtin_list.mojo | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 7d4b32dbbc..f7839ba427 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -276,9 +276,9 @@ struct _lit_mut_cast[ struct VariadicListMem[ - elt_is_mutable: __mlir_type.i1, //, + elt_is_mutable: Bool, //, element_type: AnyType, - lifetime: __mlir_type[`!lit.lifetime<`, elt_is_mutable, `>`], + lifetime: AnyLifetime[elt_is_mutable].type, ](Sized): """A utility class to access variadic function arguments of memory-only types that may have ownership. It exposes references to the elements in a @@ -379,7 +379,7 @@ struct VariadicListMem[ # Immutable variadics never own the memory underlying them, # microoptimize out a check of _is_owned. @parameter - if not Bool(elt_is_mutable): + if not elt_is_mutable: return else: @@ -415,14 +415,12 @@ struct VariadicListMem[ self, idx: Int ) -> ref [ _lit_lifetime_union[ - Bool {value: elt_is_mutable}, + elt_is_mutable, lifetime, # cast mutability of self to match the mutability of the element, # since that is what we want to use in the ultimate reference and # the union overall doesn't matter. - _lit_mut_cast[ - __lifetime_of(self), Bool {value: elt_is_mutable} - ].result, + _lit_mut_cast[__lifetime_of(self), elt_is_mutable].result, ].result ] element_type: """Gets a single element on the variadic list. @@ -463,8 +461,8 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @value struct _LITRefPackHelper[ - is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[Bool {value: is_mutable}].type, + is_mutable: Bool, //, + lifetime: AnyLifetime[is_mutable].type, address_space: __mlir_type.index, element_trait: _AnyTypeMetaType, *element_types: element_trait, @@ -534,7 +532,7 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, //, - lifetime: __mlir_type[`!lit.lifetime<`, elt_is_mutable, `>`], + lifetime: AnyLifetime[Bool {value: elt_is_mutable}].type, element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): From e2c032c9c788e8c6350d2304bc6dffd7634a6942 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Mon, 5 Aug 2024 18:03:09 -0700 Subject: [PATCH 1354/2019] [External] [stdlib] Make `InlineArray` call its elements' destructor (#42451) [External] [stdlib] Make `InlineArray` call its elements' destructor Fix this issue: * https://github.com/modularml/mojo/issues/2869 A little explanation of what is going on here, this should make the review easier. ### Modifications to `InlineArray` `InlineArray` made use of the `@value` decorator, which generates, in addition to a constructor, the following methods: `__moveinit__`, `__copyinit__` and `__del__`. While it's easy to understand what is generated when working with attributes that are values, it's less clear what is happenning when working with plain MLIR types like we do here. The next step is to manually define those to make sure they do what we want. **We will always assume that the `InlineArray` is completely initialized**. Thus when copying, moving or deleting, we must copy, move and delete each element in the `InlineArray` too. To make the implementation easier, instead of using an `UnsafePointer` and its methods to control the lifecycle of the values (`move_from_pointee` & friends), we fill the array with `UnsafeMaybeUninitialized`. This struct has methods that allows us to control the lifecycle without necessarily work with pointers. ### What do we do with `InlineArray[...](unsafe_uninitialized=True)`? This method is a massive footgun. This is because throughout the methods of `InlineArray`, we always assume that the values are initialized. So doing any moving, copying or deleting with uninitialized memory is UB. This is not much of an issue for very trivial types where moving and copying are just copying bits and the destructor does nothing. This can still cause UB if the user uses some uninitialized memory but it's fine if the caller is careful. For example, in the stdlib, we use `unsafe_uninitialized` to store some `UInt8` here and there and it's fine. As such, the constructor was kept, but an extensive docstring was added to explain in which situation it is okay to use it. ### How `InlineArray` is now used by `InlineList` `InlineList` wants to carefully manage the lifecycle of each element in the `InlineArray` while still making the `InlineArray` believe that it owns the lifecycle of its elements. In those kind of situation, `UnsafeMaybeUninitialized` is the right struct to use. So we use an `InlineArray[UnsafeMaybeUninitialized[ElementType]]` inside `InlineList`. I know it can be confusing since `InlineArray` itself also uses `UnsafeMaybeUninitialized` but this shouldn't be visible from outside `InlineArray` so it should be fine. Basically it disables all lifecycle methods, and `__del__` is a no-op. This allows `InlineList` to manually control the lifecycle of each elements which has an index below the size (above the size of the `InlineList`, it's truly uninitialized memory). ### Some guideline for the code review: I advise to read the files in this order: * The files `stdlib/test/test_utils/types.mojo` and `stdlib/test/collections/test_inline_list.mojo` have been refactored to make the tests of `InlineArray` use the `ValueDestructorRecorder`. This could have been a separate PR but I was too lazy to split this PR even further. * The file `stdlib/test/utils/test_tuple.mojo` has a new test that ensure that all the elements in the `InlineArray` have their destructors called. * The file `stdlib/src/utils/static_tuple.mojo` has all the changes that makes `InlineArray` use `UnsafeMaybeUninitialized` internally. While we could have technically use pointers, it makes writing code easier when controlling lifecycle. * Finally, we switch the internal type of `InlineList` in `stdlib/src/collections/inline_list.mojo`. We now use `InlineArray[UnsafeMaybeUninitialized[ElementType]]` which allows the `InlineList` to control exactly the lifecycle. I repeat here: yes in can be confusing because overall, we use two `UnsafeMaybeUninitialized` on top of each other when using `InlineArray[UnsafeMaybeUninitialized[ElementType]]`, but the code is still readable, because one "belongs" to `InlineArray` and one "belongs" to `InlineList.` ### Modifications from internal review - The `InlineArray` ability to call element destructors is hidden behind a parameter and defaulted to `False` for compatibility in IR generation internally. We'll progressively move these over to use `True` and then later, hopefully remove the parameter entirely. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2965 MODULAR_ORIG_COMMIT_REV_ID: 7332d9a3f1dc5b21d257da5d1e58294537c5d38c --- stdlib/src/collections/inline_list.mojo | 15 +++++++------- stdlib/src/utils/static_tuple.mojo | 23 +++++++++++++++++++++ stdlib/test/utils/test_tuple.mojo | 27 +++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 2e29256b27..f90784f97e 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -20,6 +20,7 @@ from collections import InlineList """ from sys.intrinsics import _type_is_eq +from memory.maybe_uninitialized import UnsafeMaybeUninitialized from utils import InlineArray @@ -89,7 +90,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): """ # Fields - var _array: InlineArray[ElementType, capacity] + var _array: InlineArray[UnsafeMaybeUninitialized[ElementType], capacity] var _size: Int # ===-------------------------------------------------------------------===# @@ -99,9 +100,9 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __init__(inout self): """This constructor creates an empty InlineList.""" - self._array = InlineArray[ElementType, capacity]( - unsafe_uninitialized=True - ) + self._array = InlineArray[ + UnsafeMaybeUninitialized[ElementType], capacity + ](unsafe_uninitialized=True) self._size = 0 # TODO: Avoid copying elements in once owned varargs @@ -121,7 +122,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __del__(owned self): """Destroy all the elements in the list and free the memory.""" for i in range(self._size): - UnsafePointer.address_of(self._array[i]).destroy_pointee() + self._array[i].assume_initialized_destroy() # ===-------------------------------------------------------------------===# # Operator dunders @@ -146,7 +147,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): if idx < 0: idx += len(self) - return self._array[idx] + return self._array[idx].assume_initialized() # ===-------------------------------------------------------------------===# # Trait implementations @@ -248,5 +249,5 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): value: The value to append. """ debug_assert(self._size < capacity, "List is full.") - self._array[self._size] = value^ + self._array[self._size].write(value^) self._size += 1 diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 8dc6af81c4..a13288a312 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -245,12 +245,15 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): struct InlineArray[ ElementType: CollectionElementNew, size: Int, + *, + run_destructors: Bool = False, ](Sized, Movable, Copyable, ExplicitlyCopyable): """A fixed-size sequence of size homogeneous elements where size is a constant expression. Parameters: ElementType: The type of the elements in the array. size: The size of the array. + run_destructors: Whether to run destructors on the elements. Defaults to False for *backwards compatibility* reasons only. Eventually this will default to `True` and/or the parameter will be removed to unconditionally run destructors on the elements. """ # Fields @@ -370,6 +373,26 @@ struct InlineArray[ ptr.init_pointee_explicit_copy(other[idx]) + fn __copyinit__(inout self, other: Self): + """Copy construct the array. + + Args: + other: The array to copy. + """ + + self = Self(other=other) + + fn __del__(owned self): + """Deallocate the array.""" + + @parameter + if Self.run_destructors: + + @parameter + for idx in range(size): + var ptr = self.unsafe_ptr() + idx + ptr.destroy_pointee() + # ===------------------------------------------------------------------===# # Operator dunders # ===------------------------------------------------------------------===# diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 75e8920d10..911f1dcc20 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -15,6 +15,7 @@ from testing import assert_equal, assert_false, assert_true from utils import InlineArray, StaticIntTuple, StaticTuple +from test_utils import ValueDestructorRecorder def test_static_tuple(): @@ -212,6 +213,31 @@ def test_array_contains(): assert_true(not str("greetings") in arr) +def test_inline_array_runs_destructors(): + """Ensure we delete the right number of elements.""" + var destructor_counter = List[Int]() + var pointer_to_destructor_counter = UnsafePointer.address_of( + destructor_counter + ) + alias capacity = 32 + var inline_list = InlineArray[ + ValueDestructorRecorder, 4, run_destructors=True + ]( + ValueDestructorRecorder(0, pointer_to_destructor_counter), + ValueDestructorRecorder(10, pointer_to_destructor_counter), + ValueDestructorRecorder(20, pointer_to_destructor_counter), + ValueDestructorRecorder(30, pointer_to_destructor_counter), + ) + _ = inline_list + # This is the last use of the inline list, so it should be destroyed here, + # along with each element. + assert_equal(len(destructor_counter), 4) + assert_equal(destructor_counter[0], 0) + assert_equal(destructor_counter[1], 10) + assert_equal(destructor_counter[2], 20) + assert_equal(destructor_counter[3], 30) + + def main(): test_static_tuple() test_static_int_tuple() @@ -221,3 +247,4 @@ def main(): test_array_str() test_array_int_pointer() test_array_contains() + test_inline_array_runs_destructors() From 952ae431233984088bdc328d64adb7e4089c6672 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:11:06 -0400 Subject: [PATCH 1355/2019] [stdlib] Make `UnsafePointer.__add__` and friends take `IntLike` offsets MODULAR_ORIG_COMMIT_REV_ID: 86897702c23c0386f3dad3d2b3ef69c3995d737a --- stdlib/src/memory/unsafe_pointer.mojo | 24 ++++++++++++++++------ stdlib/test/memory/test_unsafepointer.mojo | 15 ++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 3832713b8a..bfe3f08764 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -200,7 +200,7 @@ struct UnsafePointer[ ) @always_inline - fn offset[T: IntLike](self, idx: T) -> Self: + fn offset[T: IntLike, //](self, idx: T) -> Self: """Returns a new pointer shifted by the specified offset. Parameters: @@ -229,9 +229,12 @@ struct UnsafePointer[ return (self + offset)[] @always_inline - fn __add__(self, offset: Int) -> Self: + fn __add__[T: IntLike, //](self, offset: T) -> Self: """Return a pointer at an offset from the current one. + Parameters: + T: The type of idx; either `Int` or `UInt`. + Args: offset: The offset index. @@ -241,30 +244,39 @@ struct UnsafePointer[ return self.offset(offset) @always_inline - fn __sub__(self, offset: Int) -> Self: + fn __sub__[T: IntLike, //](self, offset: T) -> Self: """Return a pointer at an offset from the current one. + Parameters: + T: The type of idx; either `Int` or `UInt`. + Args: offset: The offset index. Returns: An offset pointer. """ - return self + (-offset) + return self + (-1 * Int(offset.__mlir_index__())) @always_inline - fn __iadd__(inout self, offset: Int): + fn __iadd__[T: IntLike, //](inout self, offset: T): """Add an offset to this pointer. + Parameters: + T: The type of idx; either `Int` or `UInt`. + Args: offset: The offset index. """ self = self + offset @always_inline - fn __isub__(inout self, offset: Int): + fn __isub__[T: IntLike, //](inout self, offset: T): """Subtract an offset from this pointer. + Parameters: + T: The type of idx; either `Int` or `UInt`. + Args: offset: The offset index. """ diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index d123ecf520..adfc7569e1 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -252,9 +252,20 @@ def test_offset(): ptr[i] = i var x = UInt(3) var y = Int(4) - assert_equal(ptr[x], 3) - assert_equal(ptr[y], 4) + assert_equal(ptr.offset(x)[], 3) + assert_equal(ptr.offset(y)[], 4) + + var ptr2 = UnsafePointer[Int].alloc(5) + var ptr3 = ptr2 + ptr2 += UInt(3) + assert_equal(ptr2, ptr3.offset(3)) + ptr2 -= UInt(5) + assert_equal(ptr2, ptr3.offset(-2)) + assert_equal(ptr2 + UInt(1), ptr3.offset(-1)) + assert_equal(ptr2 - UInt(4), ptr3.offset(-6)) + ptr.free() + ptr2.free() def test_load_and_store_simd(): From b730e9e499ac933b3313857bd9712040fa2d5932 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 6 Aug 2024 13:51:09 -0400 Subject: [PATCH 1356/2019] [stdlib] Clean up `UnsafePointer.load/store` Use `IntLike` parameter instead of having both `Int` and `UInt` overload. MODULAR_ORIG_COMMIT_REV_ID: 2bb5a8a2e70550a6d26937f56649afe7bb8ab77e --- stdlib/src/memory/unsafe_pointer.mojo | 116 +++++++------------------- 1 file changed, 31 insertions(+), 85 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index bfe3f08764..9dbee3cce0 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -435,7 +435,29 @@ struct UnsafePointer[ Returns: The loaded value. """ - return self.load[width=width, alignment=alignment](int(0)) + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() + + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: + # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types + # regardless of the alignment that is passed. This causes issues if + # this method is called on an unaligned pointer. + # TODO #37823 We can make this smarter when we add an `aligned` + # trait to the pointer class. + var v = SIMD[type, width]() + + # intentionally don't unroll, otherwise the compiler vectorizes + for i in range(width): + v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( + (self + i).address + ) + return v + + return __mlir_op.`pop.load`[alignment = alignment.value]( + self.bitcast[SIMD[type, width]]().address + ) @always_inline fn load[ @@ -466,23 +488,25 @@ struct UnsafePointer[ The loaded value. """ constrained[offset.type.is_integral(), "offset must be integer"]() - return self.load[width=width, alignment=alignment](int(offset)) + return self.offset(int(offset)).load[width=width, alignment=alignment]() @always_inline("nodebug") fn load[ + T: IntLike, type: DType, //, width: Int = 1, *, alignment: Int = alignof[ Scalar[type] ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_], offset: Int) -> SIMD[type, width]: + ](self: UnsafePointer[Scalar[type], *_], offset: T) -> SIMD[type, width]: """Loads the value the pointer points to with the given offset. Constraints: The width and alignment must be positive integer values. Parameters: + T: The type of offset, either `Int` or `UInt`. type: The data type of SIMD vector elements. width: The size of the SIMD vector. alignment: The minimal alignment of the address. @@ -493,61 +517,11 @@ struct UnsafePointer[ Returns: The loaded value. """ - - constrained[ - alignment > 0, "alignment must be a positive integer value" - ]() - - @parameter - if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: - # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types - # regardless of the alignment that is passed. This causes issues if - # this method is called on an unaligned pointer. - # TODO #37823 We can make this smarter when we add an `aligned` - # trait to the pointer class. - var v = SIMD[type, width]() - - # intentionally don't unroll, otherwise the compiler vectorizes - for i in range(width): - v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( - self.offset(int(offset) + i).address - ) - return v - - return __mlir_op.`pop.load`[alignment = alignment.value]( - self.offset(offset).bitcast[SIMD[type, width]]().address - ) - - @always_inline("nodebug") - fn load[ - type: DType, //, - width: Int = 1, - *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_], offset: UInt) -> SIMD[type, width]: - """Loads the value the pointer points to with the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - type: The data type of SIMD vector elements. - width: The size of the SIMD vector. - alignment: The minimal alignment of the address. - - Args: - offset: The offset to load from. - - Returns: - The loaded value. - """ - - return self.load[width=width, alignment=alignment](int(offset)) + return self.offset(offset).load[width=width, alignment=alignment]() @always_inline fn store[ + T: IntLike, type: DType, //, width: Int = 1, *, @@ -556,7 +530,7 @@ struct UnsafePointer[ ]() if triple_is_nvidia_cuda() else 1, ]( self: UnsafePointer[Scalar[type], *_], - offset: Int, + offset: T, val: SIMD[type, width], ): """Stores a single element value at the given offset. @@ -566,6 +540,7 @@ struct UnsafePointer[ The offset must be integer. Parameters: + T: The type of offset, either `Int` or `UInt`. type: The data type of SIMD vector elements. width: The size of the SIMD vector. alignment: The minimal alignment of the address. @@ -636,35 +611,6 @@ struct UnsafePointer[ val, self.bitcast[SIMD[type, width]]().address ) - @always_inline("nodebug") - fn store[ - type: DType, //, - width: Int = 1, - *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, - ]( - self: UnsafePointer[Scalar[type], *_], - offset: UInt, - val: SIMD[type, width], - ): - """Stores a single element value at the given offset. - - Constraints: - The width and alignment must be positive integer values. - - Parameters: - type: The data type of SIMD vector elements. - width: The size of the SIMD vector. - alignment: The minimal alignment of the address. - - Args: - offset: The offset to store to. - val: The value to store. - """ - self.offset(offset).store[alignment=alignment](val) - @always_inline("nodebug") fn simd_strided_load[ type: DType, width: Int, T: Intable From dcd0196c269056e7ff0157e44e8dc8e445d46fa0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 6 Aug 2024 12:22:35 -0700 Subject: [PATCH 1357/2019] [mojo-lang] Update changelog with a bug I fixed a few days ago. NFC. `ref` arguments now behave more nicely with dlvalues and implicit conversions, and infer mutability better. MODULAR_ORIG_COMMIT_REV_ID: f60e5c405eb27eff2475c32ec0049c7b278d006b --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7eb61084ab..f361f980ee 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -656,3 +656,6 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#3126](https://github.com/modularml/mojo/issues/3126) - [BUG] List doesn't work at compile time. + +- [#3237](https://github.com/modularml/mojo/issues/3237) - [BUG] Difference + between `__getitem__` and `[.]` operator. From 6be8aa912e0466ab80f1d89556e0f470cc16978b Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Tue, 6 Aug 2024 18:19:46 -0400 Subject: [PATCH 1358/2019] [REPL] Remove references of let from the REPL docs MODULAR_ORIG_COMMIT_REV_ID: 4bdc79710a8d9b1949eada4dede0bae5cde3d658 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index f361f980ee..4281e08f32 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -659,3 +659,6 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#3237](https://github.com/modularml/mojo/issues/3237) - [BUG] Difference between `__getitem__` and `[.]` operator. + +- [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated + references to `let` in REPL documentation. From 3d430ac60f06ca2c907618bcad76c420e0bc8910 Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 6 Aug 2024 16:35:24 -0700 Subject: [PATCH 1359/2019] [External] [stdlib] Remove `String` constructor from `PythonObject` This will help avoid implicit (unintended) conversions from `Int` to `String ` via the implicit constructor of `PythonObject`. MODULAR_ORIG_COMMIT_REV_ID: bcffaca97ebdba5c15567b6416ad961003ca4357 --- stdlib/src/builtin/string.mojo | 8 -------- stdlib/test/builtin/test_string.mojo | 5 ----- 2 files changed, 13 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index ab9c216543..0c75516372 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -813,14 +813,6 @@ struct String( ) ) - fn __init__(inout self, obj: PythonObject): - """Creates a string from a python object. - - Args: - obj: A python object. - """ - self = str(obj) - @always_inline fn __copyinit__(inout self, existing: Self): """Creates a deep copy of an existing string. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 6341bce169..e9ba932172 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -93,11 +93,6 @@ def test_constructors(): var s3 = String(ptr, 4) assert_equal(s3, "abc") - # Construction from PythonObject - var py = Python.evaluate("1 + 1") - var s4 = String(py) - assert_equal(s4, "2") - def test_copy(): var s0 = String("find") From 4d87f6770e4537ab541cddfc79de342abb597915 Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 6 Aug 2024 17:33:15 -0700 Subject: [PATCH 1360/2019] [External] [stdlib] Move `InlineArray` into collections module (#44668) [External] [stdlib] Move `InlineArray` into collections module Move `InlineArray` into `/stdlib/collections` along with its corresponding tests. While here, remove unnecessary imports and corresponding TODOs with regard to this change. Co-authored-by: Helehex Closes modularml/mojo#3363 MODULAR_ORIG_COMMIT_REV_ID: 015abf36d8a1e0c69e4787a66b815563fe6955a2 --- stdlib/src/builtin/format_int.mojo | 4 +- stdlib/src/builtin/hash.mojo | 3 - stdlib/src/builtin/int.mojo | 1 - stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/collections/__init__.mojo | 3 +- stdlib/src/collections/inline_array.mojo | 332 ++++++++++++++++++ stdlib/src/collections/inline_list.mojo | 2 - stdlib/src/os/_linux_aarch64.mojo | 2 - stdlib/src/os/_linux_x86.mojo | 2 - stdlib/src/os/_macos.mojo | 2 - stdlib/src/os/os.mojo | 2 +- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/span.mojo | 1 - stdlib/src/utils/static_tuple.mojo | 302 ---------------- stdlib/test/builtin/test_simd.mojo | 2 +- .../test/collections/test_inline_array.mojo | 188 ++++++++++ stdlib/test/utils/test_span.mojo | 2 +- stdlib/test/utils/test_tuple.mojo | 170 +-------- 20 files changed, 532 insertions(+), 494 deletions(-) create mode 100644 stdlib/src/collections/inline_array.mojo create mode 100644 stdlib/test/collections/test_inline_array.mojo diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index a12244419d..e4e1b4f526 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -16,8 +16,8 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import List, Optional -from utils import InlineArray, StringSlice, StaticString +from collections import List, Optional, InlineArray +from utils import StringSlice, StaticString alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 84bad072cd..93bc6a2013 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -31,9 +31,6 @@ from sys.ffi import _get_global from builtin.dtype import _uint_type_of_width from memory import memcpy, memset_zero, stack_allocation -# TODO remove this import once InlineArray is moved to collections -from utils import InlineArray - # ===----------------------------------------------------------------------=== # # Implementation # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index f7c14bead2..b46600c129 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -25,7 +25,6 @@ from builtin.string import ( _calc_initial_buffer_size_int64, ) -from utils import InlineArray from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 3777c95705..16bd92e72a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -36,7 +36,7 @@ from builtin.hash import _hash_simd from builtin.format_int import _try_write_int from memory import bitcast, UnsafePointer -from utils import InlineArray, StringSlice, StaticIntTuple, Span +from utils import StringSlice, StaticIntTuple, Span from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan diff --git a/stdlib/src/collections/__init__.mojo b/stdlib/src/collections/__init__.mojo index c1accfcc99..6a220b91b4 100644 --- a/stdlib/src/collections/__init__.mojo +++ b/stdlib/src/collections/__init__.mojo @@ -14,8 +14,9 @@ from .counter import Counter from .dict import Dict, KeyElement +from .inline_array import InlineArray from .inline_list import InlineList from .list import List from .optional import Optional, OptionalReg from .set import Set -from .vector import CollectionElement, InlinedFixedVector +from .vector import InlinedFixedVector diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo new file mode 100644 index 0000000000..ce8dbe86c9 --- /dev/null +++ b/stdlib/src/collections/inline_array.mojo @@ -0,0 +1,332 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines the `InlineArray` type. + +You can import these APIs from the `collections` package. For example: + +```mojo +from collections import InlineArray +``` +""" + +from sys.intrinsics import _type_is_eq + +# ===----------------------------------------------------------------------===# +# Array +# ===----------------------------------------------------------------------===# + + +fn _inline_array_construction_checks[size: Int](): + """Checks if the properties in `InlineArray` are valid. + + Validity right now is just ensuring the number of elements is > 0. + + Parameters: + size: The number of elements. + """ + constrained[size > 0, "number of elements in `InlineArray` must be > 0"]() + + +@value +struct InlineArray[ + ElementType: CollectionElementNew, + size: Int, + *, + run_destructors: Bool = False, +](Sized, Movable, Copyable, ExplicitlyCopyable): + """A fixed-size sequence of size homogeneous elements where size is a constant expression. + + Parameters: + ElementType: The type of the elements in the array. + size: The size of the array. + run_destructors: Whether to run destructors on the elements. Defaults to False for *backwards compatibility* reasons only. Eventually this will default to `True` and/or the parameter will be removed to unconditionally run destructors on the elements. + """ + + # Fields + alias type = __mlir_type[ + `!pop.array<`, size.value, `, `, Self.ElementType, `>` + ] + var _array: Self.type + """The underlying storage for the array.""" + + # ===------------------------------------------------------------------===# + # Life cycle methods + # ===------------------------------------------------------------------===# + + @always_inline + fn __init__(inout self): + """This constructor will always cause a compile time error if used. + It is used to steer users away from uninitialized memory. + """ + constrained[ + False, + ( + "Initialize with either a variadic list of arguments, a default" + " fill element or pass the keyword argument" + " 'unsafe_uninitialized'." + ), + ]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @always_inline + fn __init__(inout self, *, unsafe_uninitialized: Bool): + """Create an InlineArray with uninitialized memory. + + Note that this is highly unsafe and should be used with caution. + + We recommend to use the `InlineList` instead if all the objects + are not available when creating the array. + + If despite those workarounds, one still needs an uninitialized array, + it is possible with: + + ```mojo + var uninitialized_array = InlineArray[Int, 10](unsafe_uninitialized=True) + ``` + + Args: + unsafe_uninitialized: A boolean to indicate if the array should be initialized. + Always set to `True` (it's not actually used inside the constructor). + """ + _inline_array_construction_checks[size]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @always_inline + fn __init__(inout self, fill: Self.ElementType): + """Constructs an empty array where each element is the supplied `fill`. + + Args: + fill: The element to fill each index. + """ + _inline_array_construction_checks[size]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + @parameter + for i in range(size): + var ptr = UnsafePointer.address_of(self.unsafe_get(i)) + ptr.init_pointee_explicit_copy(fill) + + @always_inline + fn __init__(inout self, owned *elems: Self.ElementType): + """Constructs an array given a set of arguments. + + Args: + elems: The element types. + """ + + self = Self(storage=elems^) + + @always_inline("nodebug") + fn __init__( + inout self, + *, + owned storage: VariadicListMem[Self.ElementType, _], + ): + """Construct an array from a low-level internal representation. + + Args: + storage: The variadic list storage to construct from. + """ + + debug_assert(len(storage) == size, "Elements must be of length size") + _inline_array_construction_checks[size]() + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + # Move each element into the array storage. + @parameter + for i in range(size): + var eltref: Reference[ + Self.ElementType, __lifetime_of(self) + ] = self.unsafe_get(i) + UnsafePointer.address_of(storage[i]).move_pointee_into( + UnsafePointer[Self.ElementType].address_of(eltref[]) + ) + + # Mark the elements as already destroyed. + storage._is_owned = False + + fn __init__(inout self, *, other: Self): + """Explicitly copy the provided value. + + Args: + other: The value to copy. + """ + + self = Self(unsafe_uninitialized=True) + + for idx in range(size): + var ptr = self.unsafe_ptr() + idx + + ptr.init_pointee_explicit_copy(other[idx]) + + fn __copyinit__(inout self, other: Self): + """Copy construct the array. + + Args: + other: The array to copy. + """ + + self = Self(other=other) + + fn __del__(owned self): + """Deallocate the array.""" + + @parameter + if Self.run_destructors: + + @parameter + for idx in range(size): + var ptr = self.unsafe_ptr() + idx + ptr.destroy_pointee() + + # ===------------------------------------------------------------------===# + # Operator dunders + # ===------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __getitem__( + ref [_]self: Self, idx: Int + ) -> ref [__lifetime_of(self)] Self.ElementType: + """Get a `Reference` to the element at the given index. + + Args: + idx: The index of the item. + + Returns: + A reference to the item at the given index. + """ + var normalized_index = normalize_index["InlineArray"](idx, self) + + return self.unsafe_get(normalized_index) + + @always_inline("nodebug") + fn __getitem__[ + idx: Int, + ](ref [_]self: Self) -> ref [__lifetime_of(self)] Self.ElementType: + """Get a `Reference` to the element at the given index. + + Parameters: + idx: The index of the item. + + Returns: + A reference to the item at the given index. + """ + constrained[-size <= idx < size, "Index must be within bounds."]() + + var normalized_idx = idx + + @parameter + if idx < 0: + normalized_idx += size + + return self.unsafe_get(normalized_idx) + + # ===------------------------------------------------------------------=== # + # Trait implementations + # ===------------------------------------------------------------------=== # + + @always_inline("nodebug") + fn __len__(self) -> Int: + """Returns the length of the array. This is a known constant value. + + Returns: + The size of the array. + """ + return size + + # ===------------------------------------------------------------------===# + # Methods + # ===------------------------------------------------------------------===# + + @always_inline("nodebug") + fn unsafe_get( + ref [_]self: Self, idx: Int + ) -> ref [__lifetime_of(self)] Self.ElementType: + """Get a reference to an element of self without checking index bounds. + + Users should opt for `__getitem__` instead of this method as it is + unsafe. + + Note that there is no wraparound for negative indices. Using negative + indices is considered undefined behavior. + + Args: + idx: The index of the element to get. + + Returns: + A reference to the element at the given index. + """ + var idx_as_int = index(idx) + debug_assert( + 0 <= idx_as_int < size, + ( + "Index must be within bounds when using" + " `InlineArray.unsafe_get()`." + ), + ) + var ptr = __mlir_op.`pop.array.gep`( + UnsafePointer.address_of(self._array).address, + idx_as_int.value, + ) + return UnsafePointer(ptr)[] + + @always_inline + fn unsafe_ptr(self) -> UnsafePointer[Self.ElementType]: + """Get an `UnsafePointer` to the underlying array. + + That pointer is unsafe but can be used to read or write to the array. + Be careful when using this. As opposed to a pointer to a `List`, + this pointer becomes invalid when the `InlineArray` is moved. + + Make sure to refresh your pointer every time the `InlineArray` is moved. + + Returns: + An `UnsafePointer` to the underlying array. + """ + return UnsafePointer.address_of(self._array).bitcast[Self.ElementType]() + + @always_inline + fn __contains__[ + T: EqualityComparableCollectionElement, // + ](self, value: T) -> Bool: + """Verify if a given value is present in the array. + + ```mojo + from collections import InlineArray + var x = InlineArray[Int, 3](1,2,3) + if 3 in x: print("x contains 3") + ``` + + Parameters: + T: The type of the elements in the array. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + value: The value to find. + + Returns: + True if the value is contained in the array, False otherwise. + """ + constrained[ + _type_is_eq[T, Self.ElementType](), + "T must be equal to Self.ElementType", + ]() + + @parameter + for i in range(size): + if ( + rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] + == value + ): + return True + return False diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index f90784f97e..c1b26d8f00 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -22,8 +22,6 @@ from collections import InlineList from sys.intrinsics import _type_is_eq from memory.maybe_uninitialized import UnsafeMaybeUninitialized -from utils import InlineArray - # ===----------------------------------------------------------------------===# # InlineList diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index 5fd4d95dd9..30ed08c817 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -13,8 +13,6 @@ from time.time import _CTimeSpec -from utils import InlineArray - from .fstat import stat_result alias dev_t = Int64 diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index e709df9866..50632e02c9 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -13,8 +13,6 @@ from time.time import _CTimeSpec -from utils import InlineArray - from .fstat import stat_result alias dev_t = Int64 diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 85532168e4..0a399fc98d 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -13,8 +13,6 @@ from time.time import _CTimeSpec -from utils import InlineArray - from .fstat import stat_result alias dev_t = Int32 diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 6ec9ef2234..6928bdd2f5 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,7 +23,7 @@ from collections import List from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call from sys.ffi import C_char -from utils import InlineArray, StringRef +from utils import StringRef from .path import isdir, split from .pathlike import PathLike diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 7757e8b084..91b85382e2 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -20,7 +20,7 @@ from sys.ffi import DLHandle, C_char from memory import UnsafePointer -from utils import InlineArray, StringRef +from utils import StringRef # https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/compile.h#L7 alias Py_single_input = 256 diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index dd858c531c..ec2d03c08b 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -16,7 +16,7 @@ from .index import Index, StaticIntTuple, product from .inline_string import InlineString from .loop import unroll from .span import Span -from .static_tuple import InlineArray, StaticTuple +from .static_tuple import StaticTuple from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index d087047632..872d2d3d5a 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -21,7 +21,7 @@ from sys import sizeof from memory import UnsafePointer, memcpy -from utils import InlineArray, StringSlice, Variant +from utils import StringSlice, Variant from utils._format import ToFormatter # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 4d26b9f9cb..fdcaf6e336 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -20,7 +20,6 @@ from utils import Span ``` """ -from . import InlineArray from memory import Reference from sys.intrinsics import _type_is_eq diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index a13288a312..9ca59042dc 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -18,8 +18,6 @@ You can import these APIs from the `utils` package. For example: from utils import StaticTuple ``` """ -from collections._index_normalization import normalize_index -from sys.intrinsics import _type_is_eq from memory import UnsafePointer @@ -234,303 +232,3 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): ) UnsafePointer(ptr)[] = val self = tmp - - -# ===----------------------------------------------------------------------===# -# Array -# ===----------------------------------------------------------------------===# - - -@value -struct InlineArray[ - ElementType: CollectionElementNew, - size: Int, - *, - run_destructors: Bool = False, -](Sized, Movable, Copyable, ExplicitlyCopyable): - """A fixed-size sequence of size homogeneous elements where size is a constant expression. - - Parameters: - ElementType: The type of the elements in the array. - size: The size of the array. - run_destructors: Whether to run destructors on the elements. Defaults to False for *backwards compatibility* reasons only. Eventually this will default to `True` and/or the parameter will be removed to unconditionally run destructors on the elements. - """ - - # Fields - alias type = __mlir_type[ - `!pop.array<`, size.value, `, `, Self.ElementType, `>` - ] - var _array: Self.type - """The underlying storage for the array.""" - - # ===------------------------------------------------------------------===# - # Life cycle methods - # ===------------------------------------------------------------------===# - - @always_inline - fn __init__(inout self): - """This constructor will always cause a compile time error if used. - It is used to steer users away from uninitialized memory. - """ - constrained[ - False, - ( - "Initialize with either a variadic list of arguments, a default" - " fill element or pass the keyword argument" - " 'unsafe_uninitialized'." - ), - ]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - - @always_inline - fn __init__(inout self, *, unsafe_uninitialized: Bool): - """Create an InlineArray with uninitialized memory. - - Note that this is highly unsafe and should be used with caution. - - We recommend to use the `InlineList` instead if all the objects - are not available when creating the array. - - If despite those workarounds, one still needs an uninitialized array, - it is possible with: - - ```mojo - var uninitialized_array = InlineArray[Int, 10](unsafe_uninitialized=True) - ``` - - Args: - unsafe_uninitialized: A boolean to indicate if the array should be initialized. - Always set to `True` (it's not actually used inside the constructor). - """ - _static_tuple_construction_checks[size]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - - @always_inline - fn __init__(inout self, fill: Self.ElementType): - """Constructs an empty array where each element is the supplied `fill`. - - Args: - fill: The element to fill each index. - """ - _static_tuple_construction_checks[size]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - - @parameter - for i in range(size): - var ptr = UnsafePointer.address_of(self.unsafe_get(i)) - ptr.init_pointee_explicit_copy(fill) - - @always_inline - fn __init__(inout self, owned *elems: Self.ElementType): - """Constructs an array given a set of arguments. - - Args: - elems: The element types. - """ - - self = Self(storage=elems^) - - @always_inline("nodebug") - fn __init__( - inout self, - *, - owned storage: VariadicListMem[Self.ElementType, _], - ): - """Construct an array from a low-level internal representation. - - Args: - storage: The variadic list storage to construct from. - """ - - debug_assert(len(storage) == size, "Elements must be of length size") - _static_tuple_construction_checks[size]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - - # Move each element into the array storage. - @parameter - for i in range(size): - var eltref: Reference[ - Self.ElementType, __lifetime_of(self) - ] = self.unsafe_get(i) - UnsafePointer.address_of(storage[i]).move_pointee_into( - UnsafePointer[Self.ElementType].address_of(eltref[]) - ) - - # Mark the elements as already destroyed. - storage._is_owned = False - - fn __init__(inout self, *, other: Self): - """Explicitly copy the provided value. - - Args: - other: The value to copy. - """ - - self = Self(unsafe_uninitialized=True) - - for idx in range(size): - var ptr = self.unsafe_ptr() + idx - - ptr.init_pointee_explicit_copy(other[idx]) - - fn __copyinit__(inout self, other: Self): - """Copy construct the array. - - Args: - other: The array to copy. - """ - - self = Self(other=other) - - fn __del__(owned self): - """Deallocate the array.""" - - @parameter - if Self.run_destructors: - - @parameter - for idx in range(size): - var ptr = self.unsafe_ptr() + idx - ptr.destroy_pointee() - - # ===------------------------------------------------------------------===# - # Operator dunders - # ===------------------------------------------------------------------===# - - @always_inline("nodebug") - fn __getitem__( - ref [_]self: Self, idx: Int - ) -> ref [__lifetime_of(self)] Self.ElementType: - """Get a `Reference` to the element at the given index. - - Args: - idx: The index of the item. - - Returns: - A reference to the item at the given index. - """ - var normalized_index = normalize_index["InlineArray"](idx, self) - - return self.unsafe_get(normalized_index) - - @always_inline("nodebug") - fn __getitem__[ - idx: Int, - ](ref [_]self: Self) -> ref [__lifetime_of(self)] Self.ElementType: - """Get a `Reference` to the element at the given index. - - Parameters: - idx: The index of the item. - - Returns: - A reference to the item at the given index. - """ - constrained[-size <= idx < size, "Index must be within bounds."]() - - var normalized_idx = idx - - @parameter - if idx < 0: - normalized_idx += size - - return self.unsafe_get(normalized_idx) - - # ===------------------------------------------------------------------=== # - # Trait implementations - # ===------------------------------------------------------------------=== # - - @always_inline("nodebug") - fn __len__(self) -> Int: - """Returns the length of the array. This is a known constant value. - - Returns: - The size of the array. - """ - return size - - # ===------------------------------------------------------------------===# - # Methods - # ===------------------------------------------------------------------===# - - @always_inline("nodebug") - fn unsafe_get( - ref [_]self: Self, idx: Int - ) -> ref [__lifetime_of(self)] Self.ElementType: - """Get a reference to an element of self without checking index bounds. - - Users should opt for `__getitem__` instead of this method as it is - unsafe. - - Note that there is no wraparound for negative indices. Using negative - indices is considered undefined behavior. - - Args: - idx: The index of the element to get. - - Returns: - A reference to the element at the given index. - """ - var idx_as_int = index(idx) - debug_assert( - 0 <= idx_as_int < size, - ( - "Index must be within bounds when using" - " `InlineArray.unsafe_get()`." - ), - ) - var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(self._array).address, - idx_as_int.value, - ) - return UnsafePointer(ptr)[] - - @always_inline - fn unsafe_ptr(self) -> UnsafePointer[Self.ElementType]: - """Get an `UnsafePointer` to the underlying array. - - That pointer is unsafe but can be used to read or write to the array. - Be careful when using this. As opposed to a pointer to a `List`, - this pointer becomes invalid when the `InlineArray` is moved. - - Make sure to refresh your pointer every time the `InlineArray` is moved. - - Returns: - An `UnsafePointer` to the underlying array. - """ - return UnsafePointer.address_of(self._array).bitcast[Self.ElementType]() - - @always_inline - fn __contains__[ - T: EqualityComparableCollectionElement, // - ](self, value: T) -> Bool: - """Verify if a given value is present in the array. - - ```mojo - from utils import InlineArray - var x = InlineArray[Int, 3](1,2,3) - if 3 in x: print("x contains 3") - ``` - - Parameters: - T: The type of the elements in the array. Must implement the - traits `EqualityComparable` and `CollectionElement`. - - Args: - value: The value to find. - - Returns: - True if the value is contained in the array, False otherwise. - """ - constrained[ - _type_is_eq[T, Self.ElementType](), - "T must be equal to Self.ElementType", - ]() - - @parameter - for i in range(size): - if ( - rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] - == value - ): - return True - return False diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index c59c88bea3..86c04b7283 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -22,7 +22,7 @@ from testing import ( assert_not_equal, assert_true, ) -from utils import unroll, StaticIntTuple, InlineArray +from utils import unroll, StaticIntTuple from utils.numerics import isfinite, isinf, isnan, nan diff --git a/stdlib/test/collections/test_inline_array.mojo b/stdlib/test/collections/test_inline_array.mojo new file mode 100644 index 0000000000..3f9e5bdfcf --- /dev/null +++ b/stdlib/test/collections/test_inline_array.mojo @@ -0,0 +1,188 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal, assert_false, assert_true + +from test_utils import ValueDestructorRecorder + + +def test_array_unsafe_get(): + # Negative indexing is undefined behavior with unsafe_get + # so there are not test cases for it. + var arr = InlineArray[Int, 3](0, 0, 0) + + assert_equal(arr.unsafe_get(0), 0) + assert_equal(arr.unsafe_get(1), 0) + assert_equal(arr.unsafe_get(2), 0) + + arr[0] = 1 + arr[1] = 2 + arr[2] = 3 + + assert_equal(arr.unsafe_get(0), 1) + assert_equal(arr.unsafe_get(1), 2) + assert_equal(arr.unsafe_get(2), 3) + + +def test_array_int(): + var arr = InlineArray[Int, 3](0, 0, 0) + + assert_equal(arr[0], 0) + assert_equal(arr[1], 0) + assert_equal(arr[2], 0) + + arr[0] = 1 + arr[1] = 2 + arr[2] = 3 + + assert_equal(arr[0], 1) + assert_equal(arr[1], 2) + assert_equal(arr[2], 3) + + # test negative indexing + assert_equal(arr[-1], 3) + assert_equal(arr[-2], 2) + + # test negative indexing with dynamic index + var i = -1 + assert_equal(arr[i], 3) + i -= 1 + assert_equal(arr[i], 2) + + var copy = arr + assert_equal(arr[0], copy[0]) + assert_equal(arr[1], copy[1]) + assert_equal(arr[2], copy[2]) + + var move = arr^ + assert_equal(copy[0], move[0]) + assert_equal(copy[1], move[1]) + assert_equal(copy[2], move[2]) + + # fill element initializer + var arr2 = InlineArray[Int, 3](5) + assert_equal(arr2[0], 5) + assert_equal(arr2[1], 5) + assert_equal(arr2[2], 5) + + var arr3 = InlineArray[Int, 1](5) + assert_equal(arr3[0], 5) + + var arr4 = InlineArray[UInt8, 1](42) + assert_equal(arr4[0], 42) + + +def test_array_str(): + var arr = InlineArray[String, 3]("hi", "hello", "hey") + + assert_equal(arr[0], "hi") + assert_equal(arr[1], "hello") + assert_equal(arr[2], "hey") + + # Test mutating an array through its __getitem__ + arr[0] = "howdy" + arr[1] = "morning" + arr[2] = "wazzup" + + assert_equal(arr[0], "howdy") + assert_equal(arr[1], "morning") + assert_equal(arr[2], "wazzup") + + # test negative indexing + assert_equal(arr[-1], "wazzup") + assert_equal(arr[-2], "morning") + + var copy = arr + assert_equal(arr[0], copy[0]) + assert_equal(arr[1], copy[1]) + assert_equal(arr[2], copy[2]) + + var move = arr^ + assert_equal(copy[0], move[0]) + assert_equal(copy[1], move[1]) + assert_equal(copy[2], move[2]) + + # fill element initializer + var arr2 = InlineArray[String, 3]("hi") + assert_equal(arr2[0], "hi") + assert_equal(arr2[1], "hi") + assert_equal(arr2[2], "hi") + + # size 1 array to prevent regressions in the constructors + var arr3 = InlineArray[String, 1]("hi") + assert_equal(arr3[0], "hi") + + +def test_array_int_pointer(): + var arr = InlineArray[Int, 3](0, 10, 20) + + var ptr = arr.unsafe_ptr() + assert_equal(ptr[0], 0) + assert_equal(ptr[1], 10) + assert_equal(ptr[2], 20) + + ptr[0] = 0 + ptr[1] = 1 + ptr[2] = 2 + + assert_equal(arr[0], 0) + assert_equal(arr[1], 1) + assert_equal(arr[2], 2) + + assert_equal(ptr[0], 0) + assert_equal(ptr[1], 1) + assert_equal(ptr[2], 2) + + # We make sure it lives long enough + _ = arr + + +def test_array_contains(): + var arr = InlineArray[String, 3]("hi", "hello", "hey") + assert_true(str("hi") in arr) + assert_true(not str("greetings") in arr) + + +def test_inline_array_runs_destructors(): + """Ensure we delete the right number of elements.""" + var destructor_counter = List[Int]() + var pointer_to_destructor_counter = UnsafePointer.address_of( + destructor_counter + ) + alias capacity = 32 + var inline_list = InlineArray[ + ValueDestructorRecorder, 4, run_destructors=True + ]( + ValueDestructorRecorder(0, pointer_to_destructor_counter), + ValueDestructorRecorder(10, pointer_to_destructor_counter), + ValueDestructorRecorder(20, pointer_to_destructor_counter), + ValueDestructorRecorder(30, pointer_to_destructor_counter), + ) + _ = inline_list + # This is the last use of the inline list, so it should be destroyed here, + # along with each element. + assert_equal(len(destructor_counter), 4) + assert_equal(destructor_counter[0], 0) + assert_equal(destructor_counter[1], 10) + assert_equal(destructor_counter[2], 20) + assert_equal(destructor_counter[3], 30) + + +def main(): + test_array_unsafe_get() + test_array_int() + test_array_str() + test_array_int_pointer() + test_array_contains() + test_inline_array_runs_destructors() diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index c7086f5b31..b03fe81314 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -15,7 +15,7 @@ from collections.list import List from testing import assert_equal, assert_true -from utils import InlineArray, Span +from utils import Span def test_span_list_int(): diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 911f1dcc20..97cd01af54 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -14,7 +14,7 @@ from testing import assert_equal, assert_false, assert_true -from utils import InlineArray, StaticIntTuple, StaticTuple +from utils import StaticIntTuple, StaticTuple from test_utils import ValueDestructorRecorder @@ -76,175 +76,7 @@ def test_tuple_literal(): assert_equal(len(()), 0) -def test_array_unsafe_get(): - # Negative indexing is undefined behavior with unsafe_get - # so there are not test cases for it. - var arr = InlineArray[Int, 3](0, 0, 0) - - assert_equal(arr.unsafe_get(0), 0) - assert_equal(arr.unsafe_get(1), 0) - assert_equal(arr.unsafe_get(2), 0) - - arr[0] = 1 - arr[1] = 2 - arr[2] = 3 - - assert_equal(arr.unsafe_get(0), 1) - assert_equal(arr.unsafe_get(1), 2) - assert_equal(arr.unsafe_get(2), 3) - - -def test_array_int(): - var arr = InlineArray[Int, 3](0, 0, 0) - - assert_equal(arr[0], 0) - assert_equal(arr[1], 0) - assert_equal(arr[2], 0) - - arr[0] = 1 - arr[1] = 2 - arr[2] = 3 - - assert_equal(arr[0], 1) - assert_equal(arr[1], 2) - assert_equal(arr[2], 3) - - # test negative indexing - assert_equal(arr[-1], 3) - assert_equal(arr[-2], 2) - - # test negative indexing with dynamic index - var i = -1 - assert_equal(arr[i], 3) - i -= 1 - assert_equal(arr[i], 2) - - var copy = arr - assert_equal(arr[0], copy[0]) - assert_equal(arr[1], copy[1]) - assert_equal(arr[2], copy[2]) - - var move = arr^ - assert_equal(copy[0], move[0]) - assert_equal(copy[1], move[1]) - assert_equal(copy[2], move[2]) - - # fill element initializer - var arr2 = InlineArray[Int, 3](5) - assert_equal(arr2[0], 5) - assert_equal(arr2[1], 5) - assert_equal(arr2[2], 5) - - var arr3 = InlineArray[Int, 1](5) - assert_equal(arr3[0], 5) - - var arr4 = InlineArray[UInt8, 1](42) - assert_equal(arr4[0], 42) - - -def test_array_str(): - var arr = InlineArray[String, 3]("hi", "hello", "hey") - - assert_equal(arr[0], "hi") - assert_equal(arr[1], "hello") - assert_equal(arr[2], "hey") - - # Test mutating an array through its __getitem__ - arr[0] = "howdy" - arr[1] = "morning" - arr[2] = "wazzup" - - assert_equal(arr[0], "howdy") - assert_equal(arr[1], "morning") - assert_equal(arr[2], "wazzup") - - # test negative indexing - assert_equal(arr[-1], "wazzup") - assert_equal(arr[-2], "morning") - - var copy = arr - assert_equal(arr[0], copy[0]) - assert_equal(arr[1], copy[1]) - assert_equal(arr[2], copy[2]) - - var move = arr^ - assert_equal(copy[0], move[0]) - assert_equal(copy[1], move[1]) - assert_equal(copy[2], move[2]) - - # fill element initializer - var arr2 = InlineArray[String, 3]("hi") - assert_equal(arr2[0], "hi") - assert_equal(arr2[1], "hi") - assert_equal(arr2[2], "hi") - - # size 1 array to prevent regressions in the constructors - var arr3 = InlineArray[String, 1]("hi") - assert_equal(arr3[0], "hi") - - -def test_array_int_pointer(): - var arr = InlineArray[Int, 3](0, 10, 20) - - var ptr = arr.unsafe_ptr() - assert_equal(ptr[0], 0) - assert_equal(ptr[1], 10) - assert_equal(ptr[2], 20) - - ptr[0] = 0 - ptr[1] = 1 - ptr[2] = 2 - - assert_equal(arr[0], 0) - assert_equal(arr[1], 1) - assert_equal(arr[2], 2) - - assert_equal(ptr[0], 0) - assert_equal(ptr[1], 1) - assert_equal(ptr[2], 2) - - # We make sure it lives long enough - _ = arr - - -def test_array_contains(): - var arr = InlineArray[String, 3]("hi", "hello", "hey") - assert_true(str("hi") in arr) - assert_true(not str("greetings") in arr) - - -def test_inline_array_runs_destructors(): - """Ensure we delete the right number of elements.""" - var destructor_counter = List[Int]() - var pointer_to_destructor_counter = UnsafePointer.address_of( - destructor_counter - ) - alias capacity = 32 - var inline_list = InlineArray[ - ValueDestructorRecorder, 4, run_destructors=True - ]( - ValueDestructorRecorder(0, pointer_to_destructor_counter), - ValueDestructorRecorder(10, pointer_to_destructor_counter), - ValueDestructorRecorder(20, pointer_to_destructor_counter), - ValueDestructorRecorder(30, pointer_to_destructor_counter), - ) - _ = inline_list - # This is the last use of the inline list, so it should be destroyed here, - # along with each element. - assert_equal(len(destructor_counter), 4) - assert_equal(destructor_counter[0], 0) - assert_equal(destructor_counter[1], 10) - assert_equal(destructor_counter[2], 20) - assert_equal(destructor_counter[3], 30) - - def main(): test_static_tuple() test_static_int_tuple() test_tuple_literal() - test_array_unsafe_get() - test_array_int() - test_array_str() - test_array_int_pointer() - test_array_contains() - test_inline_array_runs_destructors() From 613b1a67994057ad91710e814de97a28413a6001 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 6 Aug 2024 18:11:37 -0700 Subject: [PATCH 1361/2019] [External] [Docs] Clarify the rounding in the builtin math module (#44681) [External] [Docs] Clarify the rounding in the builtin math module This addresses: [https://github.com/modularml/mojo/issues/3183](https://github.com/modularml/mojo/issues/3183). Previously, it was unclear how the math module rounded for negative numbers, this clears things up. ORIGINAL_AUTHOR=Pinak Paliwal <37413746+pythoncrazy@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3352 Co-authored-by: Pinak Paliwal <37413746+pythoncrazy@users.noreply.github.com> Closes modularml/mojo#3352 MODULAR_ORIG_COMMIT_REV_ID: cdb50a3843db4f983c9688b2ab47f0b24f70fa1a --- stdlib/src/builtin/math.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 492460d097..214c780fc6 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -414,6 +414,6 @@ fn round(number: FloatLiteral, ndigits: Int) -> FloatLiteral: ndigits: The number of digits to round to. Returns: - The rounded value of the object. + The rounded value of the object. Positive ndigits to the right of the decimal, negative ndigits to the left. """ return number.__round__(ndigits) From a83a09801be40a5e89ef7334696da8a8e3d6f3f4 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 7 Aug 2024 07:52:04 -0700 Subject: [PATCH 1362/2019] [******][GPU] Ensure we do not upcast when doing arithmetic operations MODULAR_ORIG_COMMIT_REV_ID: b74e5c74b89c6d256ae224358727e0ed0146a677 --- stdlib/src/builtin/simd.mojo | 54 +++++++++++++++++++++++++++++++++++- stdlib/src/math/math.mojo | 6 +--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 16bd92e72a..df7c57c55a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -26,7 +26,7 @@ from sys import ( simdwidthof, triple_is_nvidia_cuda, ) - +from sys.info import _current_arch from sys._assembly import inlined_assembly from bit import pop_count @@ -136,6 +136,15 @@ fn _has_native_bf16_support() -> Bool: return triple_is_nvidia_cuda() +@always_inline("nodebug") +fn _is_sm_80() -> Bool: + return triple_is_nvidia_cuda() and StringLiteral(_current_arch()) in ( + "sm_80", + "sm_86", + "sm_89", + ) + + # ===----------------------------------------------------------------------=== # # SIMD # ===----------------------------------------------------------------------=== # @@ -515,6 +524,11 @@ struct SIMD[type: DType, size: Int]( `self[i] + rhs[i]`. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() + + @parameter + if _is_sm_80() and type is DType.bfloat16: + return self.fma(1, rhs) + return __mlir_op.`pop.add`(self.value, rhs.value) @always_inline("nodebug") @@ -529,6 +543,10 @@ struct SIMD[type: DType, size: Int]( `self[i] - rhs[i]`. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() + + @parameter + if _is_sm_80() and type is DType.bfloat16: + return rhs.fma(-1, self) return __mlir_op.`pop.sub`(self.value, rhs.value) @always_inline("nodebug") @@ -549,6 +567,10 @@ struct SIMD[type: DType, size: Int]( type ]() + @parameter + if _is_sm_80() and type is DType.bfloat16: + return self.fma(rhs, -0.0) + constrained[type.is_numeric(), "the SIMD type must be numeric"]() return __mlir_op.`pop.mul`(self.value, rhs.value) @@ -1682,6 +1704,36 @@ struct SIMD[type: DType, size: Int]( `self[i]*multiplier[i] + accumulator[i]`. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() + + @parameter + if _is_sm_80() and type is DType.bfloat16: + + @parameter + if size == 1: + return inlined_assembly[ + "fma.rn.bf16 $0, $1, $2, $3;", + Self, + constraints="=h,h,h,h", + has_side_effect=False, + ](self, multiplier, accumulator) + + var res = Self() + + @parameter + for i in range(0, size, 2): + var val = inlined_assembly[ + "fma.rn.bf16x2 $0, $1, $2, $3;", + SIMD[type, 2], + constraints="=r,r,r,r", + has_side_effect=False, + ]( + self.slice[2, offset=i](), + multiplier.slice[2, offset=i](), + accumulator.slice[2, offset=i](), + ) + res = res.insert[offset=i](val) + return res + return __mlir_op.`pop.fma`( self.value, multiplier.value, accumulator.value ) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 2c2876c5d5..b19d0f9c65 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -567,11 +567,7 @@ fn exp[ ](x * inv_lg2) @parameter - if ( - not type is DType.float64 - and type is not DType.float32 - and sizeof[type]() < sizeof[DType.float32]() - ): + if type not in (DType.float32, DType.float64): return exp(x.cast[DType.float32]()).cast[type]() var min_val: SIMD[type, simd_width] From 7b4f644a0f935acbb4bc6e4029de6c692a84c495 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 7 Aug 2024 09:12:53 -0700 Subject: [PATCH 1363/2019] [******][GPU] Ensure we do not upcast when doing arithmetic operations MODULAR_ORIG_COMMIT_REV_ID: 4d522814ebf89a451133fecb63a2ffe979dc9056 --- stdlib/src/builtin/simd.mojo | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index df7c57c55a..6840f06b68 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -526,7 +526,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_80() and type is DType.bfloat16: + if _is_sm_80() and type.is_half_float(): return self.fma(1, rhs) return __mlir_op.`pop.add`(self.value, rhs.value) @@ -545,7 +545,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_80() and type is DType.bfloat16: + if _is_sm_80() and type.is_half_float(): return rhs.fma(-1, self) return __mlir_op.`pop.sub`(self.value, rhs.value) @@ -568,7 +568,7 @@ struct SIMD[type: DType, size: Int]( ]() @parameter - if _is_sm_80() and type is DType.bfloat16: + if _is_sm_80() and type.is_half_float(): return self.fma(rhs, -0.0) constrained[type.is_numeric(), "the SIMD type must be numeric"]() @@ -1706,12 +1706,13 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_80() and type is DType.bfloat16: + if _is_sm_80() and type.is_half_float(): + alias prefix = "fma.rn.bf16" if type is DType.bfloat16 else "fma.rn.f16" @parameter if size == 1: return inlined_assembly[ - "fma.rn.bf16 $0, $1, $2, $3;", + prefix + " $0, $1, $2, $3;", Self, constraints="=h,h,h,h", has_side_effect=False, @@ -1722,7 +1723,7 @@ struct SIMD[type: DType, size: Int]( @parameter for i in range(0, size, 2): var val = inlined_assembly[ - "fma.rn.bf16x2 $0, $1, $2, $3;", + prefix + "x2 $0, $1, $2, $3;", SIMD[type, 2], constraints="=r,r,r,r", has_side_effect=False, From 2c66eeef8655d322e8451a040fa4f8e5c5a3caeb Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 7 Aug 2024 14:30:27 -0700 Subject: [PATCH 1364/2019] [External] [stdlib] Add a `unsafe_assume_initialized` constructor to `InlineArray` (#42256) This is a follow-up on https://github.com/modularml/mojo/pull/2965#discussion_r1648153449 The idea here is to guide the user in the unsafe lands of programming. Such that even when manipulating uninitialized memory, the assumptions about the data are clear. The next step is to give a new constructor so that doing ``` InlineArray[UnsafeMaybeUninitialized[String], 3](unsafe_uninitialized=True) ``` become a bit more user-friendly. We'd like to make it impossible to use `unsafe_uninitialized` without working with `UnsafeMaybeUninitialized`. This is for a next PR. @ConnorGray for review as you were the one to follow the work in this area. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3119 MODULAR_ORIG_COMMIT_REV_ID: 9a57d97dbd4608f3cf20510137cb4cc2dcb64178 --- stdlib/src/collections/inline_array.mojo | 26 +++++++++++++ .../test/collections/test_inline_array.mojo | 37 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index ce8dbe86c9..dd0fbe5cb1 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -20,6 +20,7 @@ from collections import InlineArray """ from sys.intrinsics import _type_is_eq +from memory.maybe_uninitialized import UnsafeMaybeUninitialized # ===----------------------------------------------------------------------===# # Array @@ -101,6 +102,31 @@ struct InlineArray[ _inline_array_construction_checks[size]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + fn __init__( + inout self, + *, + owned unsafe_assume_initialized: InlineArray[ + UnsafeMaybeUninitialized[Self.ElementType], Self.size + ], + ): + """Constructs an `InlineArray` from an `InlineArray` of `UnsafeMaybeUninitialized`. + + Calling this function assumes that all elements in the input array are initialized. + + If the elements of the input array are not initialized, the behavior is undefined, + even if `ElementType` is valid *for every possible bit pattern* (e.g. `Int` or `Float`). + + Args: + unsafe_assume_initialized: The array of `UnsafeMaybeUninitialized` elements. + """ + + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + + for i in range(Self.size): + unsafe_assume_initialized[i].unsafe_ptr().move_pointee_into( + self.unsafe_ptr() + i + ) + @always_inline fn __init__(inout self, fill: Self.ElementType): """Constructs an empty array where each element is the supplied `fill`. diff --git a/stdlib/test/collections/test_inline_array.mojo b/stdlib/test/collections/test_inline_array.mojo index 3f9e5bdfcf..c69405b0c5 100644 --- a/stdlib/test/collections/test_inline_array.mojo +++ b/stdlib/test/collections/test_inline_array.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from testing import assert_equal, assert_false, assert_true - +from memory.maybe_uninitialized import UnsafeMaybeUninitialized from test_utils import ValueDestructorRecorder @@ -148,6 +148,40 @@ def test_array_int_pointer(): _ = arr +def test_array_unsafe_assume_initialized_constructor_string(): + var maybe_uninitialized_arr = InlineArray[ + UnsafeMaybeUninitialized[String], 3 + ](unsafe_uninitialized=True) + maybe_uninitialized_arr[0].write("hello") + maybe_uninitialized_arr[1].write("mojo") + maybe_uninitialized_arr[2].write("world") + + var initialized_arr = InlineArray[String, 3]( + unsafe_assume_initialized=maybe_uninitialized_arr^ + ) + + assert_equal(initialized_arr[0], "hello") + assert_equal(initialized_arr[1], "mojo") + assert_equal(initialized_arr[2], "world") + + # trigger a move + var initialized_arr2 = initialized_arr^ + + assert_equal(initialized_arr2[0], "hello") + assert_equal(initialized_arr2[1], "mojo") + assert_equal(initialized_arr2[2], "world") + + # trigger a copy + var initialized_arr3 = InlineArray(other=initialized_arr2) + + assert_equal(initialized_arr3[0], "hello") + assert_equal(initialized_arr3[1], "mojo") + assert_equal(initialized_arr3[2], "world") + + # We assume the destructor was called correctly, but one + # might want to add a test for that in the future. + + def test_array_contains(): var arr = InlineArray[String, 3]("hi", "hello", "hey") assert_true(str("hi") in arr) @@ -184,5 +218,6 @@ def main(): test_array_int() test_array_str() test_array_int_pointer() + test_array_unsafe_assume_initialized_constructor_string() test_array_contains() test_inline_array_runs_destructors() From 5e6026044f2ed2c6deb542a4be470ddb295bdb94 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 7 Aug 2024 15:19:07 -0700 Subject: [PATCH 1365/2019] [******] Rename rsqrt to isqrt, NFC MODULAR_ORIG_COMMIT_REV_ID: 11779003dc8aedffd7617980a8deae2a4c4ab53e --- examples/notebooks/RayTracing.ipynb | 4 ++-- stdlib/src/math/__init__.mojo | 2 +- stdlib/src/math/math.mojo | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 8effcb4b42..aab92d02ec 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -73,7 +73,7 @@ }, "outputs": [], "source": [ - "from math import rsqrt\n", + "from math import isqrt\n", "\n", "\n", "@register_passable(\"trivial\")\n", @@ -127,7 +127,7 @@ "\n", " @always_inline\n", " fn normalize(self) -> Vec3f:\n", - " return self.data * rsqrt(self @ self)\n" + " return self.data * isqrt(self @ self)\n" ] }, { diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 5c046033f5..066cad8edc 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -64,7 +64,7 @@ from .math import ( logb, modf, remainder, - rsqrt, + isqrt, scalb, sin, sinh, diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index b19d0f9c65..0f5cf20cb6 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -268,7 +268,7 @@ fn sqrt[ @always_inline -fn _rsqrt_nvvm(x: SIMD) -> __type_of(x): +fn _isqrt_nvvm(x: SIMD) -> __type_of(x): constrained[ x.type in (DType.float32, DType.float64), "must be f32 or f64 type" ]() @@ -285,7 +285,7 @@ fn _rsqrt_nvvm(x: SIMD) -> __type_of(x): @always_inline -fn rsqrt(x: SIMD) -> __type_of(x): +fn isqrt(x: SIMD) -> __type_of(x): """Performs elementwise reciprocal square root on a SIMD vector. Args: @@ -301,9 +301,9 @@ fn rsqrt(x: SIMD) -> __type_of(x): @parameter if x.type in (DType.float16, DType.bfloat16): - return _rsqrt_nvvm(x.cast[DType.float32]()).cast[x.type]() + return _isqrt_nvvm(x.cast[DType.float32]()).cast[x.type]() - return _rsqrt_nvvm(x) + return _isqrt_nvvm(x) return 1 / sqrt(x) From 949508537679054f14739d1aec31e7a19c7656b1 Mon Sep 17 00:00:00 2001 From: Chad Date: Thu, 8 Aug 2024 18:04:36 +0200 Subject: [PATCH 1366/2019] [stdlib] Use the GPU log2 instruction for log functions Using the log2 instructions should significantly improve performance of the log functions on the GPU. MODULAR_ORIG_COMMIT_REV_ID: e8f64cab7406d830687d9db671d6e6c91c347c28 --- stdlib/src/math/math.mojo | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 0f5cf20cb6..b4f25b240d 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -739,6 +739,22 @@ fn log(x: SIMD) -> __type_of(x): Returns: Vector containing result of performing natural log base E on x. """ + + @parameter + if triple_is_nvidia_cuda(): + alias ln2 = 0.69314718055966295651160180568695068359375 + + @parameter + if sizeof[x.type]() < sizeof[DType.float32](): + return log(x.cast[DType.float32]()).cast[x.type]() + elif x.type is DType.float32: + return ( + _call_ptx_intrinsic[ + instruction="lg2.approx.f32", constraints="=f,f" + ](x) + * ln2 + ) + return _log_base[27](x) @@ -757,6 +773,18 @@ fn log2(x: SIMD) -> __type_of(x): Returns: Vector containing result of performing log base 2 on x. """ + + @parameter + if triple_is_nvidia_cuda(): + + @parameter + if sizeof[x.type]() < sizeof[DType.float32](): + return log2(x.cast[DType.float32]()).cast[x.type]() + elif x.type is DType.float32: + return _call_ptx_intrinsic[ + instruction="lg2.approx.f32", constraints="=f,f" + ](x) + return _log_base[2](x) @@ -1577,6 +1605,22 @@ fn log10(x: SIMD) -> __type_of(x): Returns: The `log10` of the input. """ + + @parameter + if triple_is_nvidia_cuda(): + alias log10_2 = 0.301029995663981195213738894724493027 + + @parameter + if sizeof[x.type]() < sizeof[DType.float32](): + return log10(x.cast[DType.float32]()).cast[x.type]() + elif x.type is DType.float32: + return ( + _call_ptx_intrinsic[ + instruction="lg2.approx.f32", constraints="=f,f" + ](x) + * log10_2 + ) + return _call_libm["log10"](x) From d26594bc9fa23d24b2603c10f1c1c5aa836f44b9 Mon Sep 17 00:00:00 2001 From: Chad Date: Thu, 8 Aug 2024 18:43:48 +0200 Subject: [PATCH 1367/2019] [stdlib] Optimize the exp2 to use native GPU instructions The GPU has float32 and float16 exp2 instructions. MODULAR_ORIG_COMMIT_REV_ID: 97b2f7a331483b6498d25227639d72c614a700d0 --- stdlib/src/math/math.mojo | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index b4f25b240d..e4e63919b3 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -377,6 +377,24 @@ fn exp2[ Vector containing $2^n$ computed elementwise, where n is an element in the input SIMD vector. """ + + @parameter + if triple_is_nvidia_cuda(): + + @parameter + if type is DType.float16: + return _call_ptx_intrinsic[ + instruction="ex2.approx.f16", constraints="=h,h" + ](x) + elif type is DType.float32: + return _call_ptx_intrinsic[ + instruction="ex2.approx.ftz.f32", constraints="=f,f" + ](x) + + @parameter + if type not in (DType.float32, DType.float64): + return exp2(x.cast[DType.float32]()).cast[type]() + alias integral_type = FPUtils[type].integral_type var xc = x.clamp(-126, 126) From da0765a4a1698afb786ddd00c6330d38634ab786 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 8 Aug 2024 14:12:16 -0600 Subject: [PATCH 1368/2019] [All] [NFC] Import what you use When starting to pull on the thread for changing how builtin entities are parsed and auto-imported into every Mojo program, this exposed many files that are relying on entities getting transitively included on their behalf. Since the transitive closure import behavior will eventually go away when we're done, fix these uses to import what they use explicitly. MODULAR_ORIG_COMMIT_REV_ID: 8a7df9b29f7644821454d5ba9a8a5e250999f1c7 --- stdlib/src/builtin/hash.mojo | 1 + stdlib/src/builtin/simd.mojo | 1 + stdlib/src/collections/inline_array.mojo | 1 + stdlib/src/os/_linux_aarch64.mojo | 1 + stdlib/src/os/_linux_x86.mojo | 1 + stdlib/src/os/_macos.mojo | 1 + stdlib/src/os/os.mojo | 2 +- stdlib/src/python/_cpython.mojo | 1 + stdlib/src/utils/inline_string.mojo | 1 + stdlib/src/utils/span.mojo | 1 + stdlib/test/builtin/test_simd.mojo | 1 + stdlib/test/collections/test_inline_array.mojo | 1 + stdlib/test/utils/test_span.mojo | 2 +- 13 files changed, 13 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 93bc6a2013..4494d09a2d 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -27,6 +27,7 @@ There are a few main tools in this module: import random from sys.ffi import _get_global +from collections import InlineArray from builtin.dtype import _uint_type_of_width from memory import memcpy, memset_zero, stack_allocation diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6840f06b68..b12f8a394c 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -34,6 +34,7 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from builtin.hash import _hash_simd from builtin.format_int import _try_write_int +from collections import InlineArray from memory import bitcast, UnsafePointer from utils import StringSlice, StaticIntTuple, Span diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index dd0fbe5cb1..5e747e7972 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -19,6 +19,7 @@ from collections import InlineArray ``` """ +from collections._index_normalization import normalize_index from sys.intrinsics import _type_is_eq from memory.maybe_uninitialized import UnsafeMaybeUninitialized diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index 30ed08c817..7bb7e08d62 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -11,6 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from collections import InlineArray from time.time import _CTimeSpec from .fstat import stat_result diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index 50632e02c9..0406d8101c 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from time.time import _CTimeSpec +from collections import InlineArray from .fstat import stat_result diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 0a399fc98d..bbf4064e46 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -11,6 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from collections import InlineArray from time.time import _CTimeSpec from .fstat import stat_result diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 6928bdd2f5..422ec28c78 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -19,7 +19,7 @@ from os import listdir ``` """ -from collections import List +from collections import List, InlineArray from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call from sys.ffi import C_char diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 91b85382e2..4fb97b5c39 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -11,6 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from collections import InlineArray from os import getenv, setenv from os.path import dirname from pathlib import Path diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 872d2d3d5a..58ab56bcfd 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -15,6 +15,7 @@ avoids heap allocations for short strings. """ +from collections import InlineArray from os import abort from collections import Optional from sys import sizeof diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index fdcaf6e336..a03a2614cd 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -20,6 +20,7 @@ from utils import Span ``` """ +from collections import InlineArray from memory import Reference from sys.intrinsics import _type_is_eq diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 86c04b7283..8bf54d1f51 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -14,6 +14,7 @@ from sys import has_neon +from collections import InlineArray from builtin.simd import _modf from testing import ( assert_almost_equal, diff --git a/stdlib/test/collections/test_inline_array.mojo b/stdlib/test/collections/test_inline_array.mojo index c69405b0c5..0d891abf63 100644 --- a/stdlib/test/collections/test_inline_array.mojo +++ b/stdlib/test/collections/test_inline_array.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from collections import InlineArray from testing import assert_equal, assert_false, assert_true from memory.maybe_uninitialized import UnsafeMaybeUninitialized from test_utils import ValueDestructorRecorder diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index b03fe81314..121947a036 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from collections.list import List +from collections import InlineArray, List from testing import assert_equal, assert_true from utils import Span From 606f0aba4ef39afedd1d6a72d8321723d0396c3b Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 8 Aug 2024 16:00:56 -0600 Subject: [PATCH 1369/2019] [stdlib] Introduce prelude module Currently, the `builtin` module (and all of its transitive imports from every mojo file within the `builtin` directory) is auto-imported into every Mojo program. This is bad for a few reasons: 1. It couples the implementation location of an entity with where it ought to be exported. 2. It's confusing that there are *additional* things auto-imported into every Mojo program that *do not* live in the `builtin` directory. This manifests in user confusion at best since other stdlib modules such as `random` are imported into user's programs without them even knowing. This is because the imports of implementation files were also transitively included in this builtin set of auto imports. Specifically, prior to this change, the transitive closure set of auto-imported modules was: ``` {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', 'builtin', 'collections'} ``` This is not great - that set represents most of the standard library! Attempt to start clarifying these gotchas by introducing a new module: `prelude`, which has no definitions itself, but re-exports the fixed, enumerated set of entities that we want to be automatically imported into every Mojo program. Currently, this is roughly the set of public (not starting with an `_`) entities in the `builtin` module today, plus a few load-bearing common imports. These load-bearing ones will be chipped away at shortly after and removed soon, but are nice to allow for progressively landing this work while already fixing all of the missing import issues throughout the codebase. MODULAR_ORIG_COMMIT_REV_ID: af800eb391dec3ad535e0d1bbbd185e23717bb7e --- docs/changelog.md | 14 +++ stdlib/src/prelude/__init__.mojo | 143 +++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 stdlib/src/prelude/__init__.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 4281e08f32..0fae1f3418 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -371,6 +371,20 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. ### 🦋 Changed +- The set of automatically imported entities (types, aliases, functions) into user's + Mojo programs has been dramatically reduced. Before, with the way the `builtin` + module was handled, all of the entities in the following modules would be automatically + included: + + {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', + 'builtin', 'collections'} + + Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are + the ones automatically imported into user's Mojo programs. This will break + a lot of user code as users will need to explicitly import what they're using + for cases previously commonly included before (such as `Optional`, `Variant`, + and so on). + - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a C-like set of semantics around pointer aliasing and derivation. However, the C semantics bring a lot of history and baggage that are not needed in Mojo and diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo new file mode 100644 index 0000000000..fd21f3fb98 --- /dev/null +++ b/stdlib/src/prelude/__init__.mojo @@ -0,0 +1,143 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the prelude package. This package provide the public entities + that are automatically imported into every Mojo program. +""" + +from builtin.anytype import AnyType +from builtin.bool import Boolable, ImplicitlyBoolable, Bool, bool, any, all +from builtin.breakpoint import breakpoint +from builtin.builtin_list import ( + ListLiteral, + VariadicList, + VariadicListMem, + VariadicPack, +) +from builtin.builtin_slice import Slice, slice +from builtin.comparable import Comparable +from builtin.constrained import constrained +from builtin.coroutine import Coroutine, RaisingCoroutine, AnyCoroutine +from builtin.debug_assert import debug_assert +from builtin.dtype import DType +from builtin.equality_comparable import EqualityComparable +from builtin.error import Error +from builtin.file import open, FileHandle +from builtin.file_descriptor import FileDescriptor +from builtin.float_literal import FloatLiteral +from builtin.format_int import bin, hex, oct +from builtin.hash import hash, Hashable +from builtin.identifiable import Identifiable, StringableIdentifiable +from builtin.int import ( + Int, + IntLike, + Intable, + IntableRaising, + Indexer, + index, + int, +) +from builtin.int_literal import IntLiteral +from builtin.io import print +from builtin.len import Sized, UIntSized, SizedRaising, len +from builtin.math import ( + Absable, + abs, + divmod, + max, + min, + Powable, + pow, + Roundable, + round, +) +from builtin.none import NoneType +from builtin.object import Attr, object +from builtin.range import range +from builtin.rebind import rebind +from builtin.repr import Representable, repr +from builtin.reversed import ReversibleRange, reversed +from builtin.sort import sort, partition +from builtin.str import Stringable, StringableRaising, str +from builtin.string import ( + String, + ord, + chr, + ascii, + atol, + atof, + isdigit, + isupper, + islower, + isprintable, +) +from builtin.string_literal import StringLiteral +from builtin.swap import swap +from builtin.tuple import ( + Tuple, +) +from builtin.type_aliases import ( + AnyTrivialRegType, + ImmutableLifetime, + MutableLifetime, + ImmutableStaticLifetime, + MutableStaticLifetime, + LifetimeSet, + AnyLifetime, +) +from builtin.uint import UInt +from builtin.value import ( + Movable, + Copyable, + ExplicitlyCopyable, + Defaultable, + CollectionElement, + CollectionElementNew, + StringableCollectionElement, + EqualityComparableCollectionElement, + ComparableCollectionElement, + RepresentableCollectionElement, + BoolableKeyElement, + BoolableCollectionElement, +) +from builtin.simd import ( + Scalar, + Int8, + UInt8, + Int16, + UInt16, + Int32, + UInt32, + Int64, + UInt64, + BFloat16, + Float16, + Float32, + Float64, + SIMD, +) +from builtin.type_aliases import AnyTrivialRegType + +from collections import KeyElement, List +from memory import UnsafePointer, Reference, AddressSpace +from utils import StringRef +from utils._format import Formattable, Formatter + +# Private things +from builtin._documentation import doc_private +from utils._visualizers import lldb_formatter_wrapping_type + +# Load-bearing ones to remove +from sys import alignof, sizeof, bitwidthof, simdwidthof +from memory import bitcast +from os import abort +from sys.ffi import external_call From 9744518663ca46b23cf0b6cee5e061780cd36822 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 8 Aug 2024 17:10:43 -0700 Subject: [PATCH 1370/2019] [Stdlib] Allow one to construct a DType from a string This implementation allows the construction to be done in both the runtime and param domains. MODULAR_ORIG_COMMIT_REV_ID: 4576cfe9b5a5cb5c47a0689761562ecdf676d71b --- stdlib/src/builtin/dtype.mojo | 44 +++++++++++++++++++++++++++++ stdlib/test/builtin/test_dtype.mojo | 20 ++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 3baf48f0a5..ca6fd3c0cc 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -87,6 +87,50 @@ struct DType( """ self = other + @staticmethod + fn _from_str(str: String) -> DType: + """Construct a DType from a string. + + Args: + str: The name of the DType. + """ + if str.startswith(String("DType.")): + return Self._from_str(str.removeprefix("DType.")) + elif str == String("bool"): + return DType.bool + elif str == String("int8"): + return DType.int8 + elif str == String("uint8"): + return DType.uint8 + elif str == String("int16"): + return DType.int16 + elif str == String("uint16"): + return DType.uint16 + elif str == String("int32"): + return DType.int32 + elif str == String("uint32"): + return DType.uint32 + elif str == String("int64"): + return DType.int64 + elif str == String("uint64"): + return DType.uint64 + elif str == String("index"): + return DType.index + elif str == String("bfloat16"): + return DType.bfloat16 + elif str == String("float16"): + return DType.float16 + elif str == String("float32"): + return DType.float32 + elif str == String("float64"): + return DType.float64 + elif str == String("tensor_float32"): + return DType.tensor_float32 + elif str == String("invalid"): + return DType.invalid + else: + return DType.invalid + @no_inline fn __str__(self) -> String: """Gets the name of the DType. diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index 6799f1bcfd..557b1fc66f 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -49,9 +49,27 @@ fn test_sizeof() raises: assert_equal(DType.index.sizeof(), sizeof[DType.index]()) -fn main() raises: +def test_from_str(): + assert_equal(DType._from_str("bool"), DType.bool) + assert_equal(DType._from_str("DType.bool"), DType.bool) + + alias dt = DType._from_str("bool") + assert_equal(dt, DType.bool) + + assert_equal(DType._from_str("bfloat16"), DType.bfloat16) + assert_equal(DType._from_str("DType.bfloat16"), DType.bfloat16) + + assert_equal(DType._from_str("int64"), DType.int64) + assert_equal(DType._from_str("DType.int64"), DType.int64) + + assert_equal(DType._from_str("blahblah"), DType.invalid) + assert_equal(DType._from_str("DType.blahblah"), DType.invalid) + + +def main(): test_equality() test_stringable() test_representable() test_key_element() test_sizeof() + test_from_str() From 4798fe198e3392c98fc801f9db13e22846d21cdf Mon Sep 17 00:00:00 2001 From: Wei Han <56132041+palebluedot19@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:17:30 -0500 Subject: [PATCH 1371/2019] [stdlib] Add uniform random generator in `random.mojo`. MODULAR_ORIG_COMMIT_REV_ID: c79492d1077617fa98b7354251964fa57b0bbab2 --- stdlib/src/random/random.mojo | 63 ++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 2a563f1431..fb28af5f18 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -21,6 +21,7 @@ from random import seed from sys import bitwidthof, external_call from time import perf_counter_ns +from collections import Optional from memory import UnsafePointer @@ -121,7 +122,17 @@ fn randint[ ptr[ui] = random_ui64(low, high).cast[type]() -fn rand[type: DType](ptr: UnsafePointer[Scalar[type]], size: Int): +fn rand[ + type: DType +]( + ptr: UnsafePointer[Scalar[type]], + size: Int, + /, + *, + min: Float64 = 0.0, + max: Float64 = 1.0, + int_scale: Optional[Int] = None, +): """Fills memory with random values from a uniform distribution. Parameters: @@ -130,33 +141,69 @@ fn rand[type: DType](ptr: UnsafePointer[Scalar[type]], size: Int): Args: ptr: The pointer to the memory area to fill. size: The number of elements to fill. + min: The minimum value for random. + max: The maximum value for random. + int_scale: The scale for error checking (float type only). """ alias bitwidth = bitwidthof[type]() + var scale_val = int_scale.or_else(-1) + @parameter if type.is_floating_point(): - for i in range(size): - ptr[i] = random_float64().cast[type]() + if scale_val >= 0: + var scale_double: Float64 = (1 << scale_val) + for i in range(size): + var rnd = random_float64(min, max) + ptr[i] = ( + (rnd * scale_double) + .cast[DType.int64]() + .cast[DType.float64]() + / scale_double + ).cast[type]() + else: + for i in range(size): + var rnd = random_float64(min, max) + ptr[i] = rnd.cast[type]() + return @parameter if type is DType.bool: + var min_: UInt64 = 0 if min < 0 else min.cast[DType.uint64]() + var max_: UInt64 = (1 << bitwidth) - 1 + max_ = ( + max.cast[DType.uint64]() if max.cast[DType.uint64]() + < max_ else max_ + ) for i in range(size): - ptr[i] = random_ui64(0, 1).cast[type]() + ptr[i] = random_ui64(min_, max_).cast[type]() return @parameter if type.is_signed(): + var min_: Int64 = -(1 << (bitwidth - 1)) + min_ = ( + min.cast[DType.int64]() if min.cast[DType.int64]() > min_ else min_ + ) + var max_: Int64 = (1 << (bitwidth - 1)) - 1 + max_ = ( + max.cast[DType.int64]() if max.cast[DType.int64]() < max_ else max_ + ) for i in range(size): - ptr[i] = random_si64( - -(1 << (bitwidth - 1)), (1 << (bitwidth - 1)) - 1 - ).cast[type]() + ptr[i] = random_si64(min_, max_).cast[type]() return @parameter if type.is_unsigned(): + var min_: UInt64 = 0 if min < 0 else min.cast[DType.uint64]() + var max_: UInt64 = (1 << bitwidth) - 1 + max_ = ( + max.cast[DType.uint64]() if max.cast[DType.uint64]() + < max_ else max_ + ) for i in range(size): - ptr[i] = random_ui64(0, (1 << bitwidth) - 1).cast[type]() + ptr[i] = random_ui64(min_, max_).cast[type]() return From 03f6b9fa7c32a5f7b6d3b56f623bfddb39b3a703 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 9 Aug 2024 12:43:27 -0700 Subject: [PATCH 1372/2019] [Stdlib] Make the FileDescriptor copyable MODULAR_ORIG_COMMIT_REV_ID: 071258a540433973c2759a715c16a4d9b14770e0 --- stdlib/src/builtin/file_descriptor.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 2806e7072e..ac458d169e 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -25,6 +25,7 @@ f.close() """ +@value struct FileDescriptor: """File descriptor of a file.""" From 6268a45fb6708f64cf88232003834548490a5c12 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 9 Aug 2024 14:11:50 -0600 Subject: [PATCH 1373/2019] [stdlib] Move `String` to `collections` Move the `String` type out of `builtin` module and into the `collections` module. This is made possible now that we have a `prelude` module and all of the core built-in types don't *actually* need to live in the `builtin` module. MODULAR_ORIG_COMMIT_REV_ID: 277d88598a373d4f711b55068df7d7c205798f15 --- docs/changelog.md | 5 ++++ stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 5 +++- stdlib/src/builtin/string_literal.mojo | 2 +- .../src/{builtin => collections}/string.mojo | 0 stdlib/src/prelude/__init__.mojo | 24 +++++++++---------- stdlib/src/utils/index.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 +- .../{builtin => collections}/test_string.mojo | 2 +- 10 files changed, 27 insertions(+), 19 deletions(-) rename stdlib/src/{builtin => collections}/string.mojo (100%) rename stdlib/test/{builtin => collections}/test_string.mojo (99%) diff --git a/docs/changelog.md b/docs/changelog.md index 0fae1f3418..4ecdfa9954 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -385,6 +385,11 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. for cases previously commonly included before (such as `Optional`, `Variant`, and so on). +- Some types from the `builtin` module have been moved to different modules for clarity + which is made possible now that we have a `prelude` module that can re-export symbols + from modules other than `builtin`. + - `builtin.string` has been moved to `collections.string`. + - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a C-like set of semantics around pointer aliasing and derivation. However, the C semantics bring a lot of history and baggage that are not needed in Mojo and diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b46600c129..450d5f83c1 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -20,7 +20,7 @@ from collections import KeyElement from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from builtin.io import _snprintf -from builtin.string import ( +from collections.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, ) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b12f8a394c..72d8c20b4d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -54,7 +54,10 @@ from .dtype import ( _scientific_notation_digits, ) from .io import _printf, _snprintf_scalar -from .string import _calc_format_buffer_size, _calc_initial_buffer_size +from collections.string import ( + _calc_format_buffer_size, + _calc_initial_buffer_size, +) # ===----------------------------------------------------------------------=== # # Type Aliases diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index ff04253935..ae41e2dfbf 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -23,7 +23,7 @@ from utils import StringRef, Span, StringSlice from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type -from .string import _atol +from collections.string import _atol # ===----------------------------------------------------------------------===# # StringLiteral diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/collections/string.mojo similarity index 100% rename from stdlib/src/builtin/string.mojo rename to stdlib/src/collections/string.mojo diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index fd21f3fb98..e87e401b43 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -68,18 +68,6 @@ from builtin.repr import Representable, repr from builtin.reversed import ReversibleRange, reversed from builtin.sort import sort, partition from builtin.str import Stringable, StringableRaising, str -from builtin.string import ( - String, - ord, - chr, - ascii, - atol, - atof, - isdigit, - isupper, - islower, - isprintable, -) from builtin.string_literal import StringLiteral from builtin.swap import swap from builtin.tuple import ( @@ -128,6 +116,18 @@ from builtin.simd import ( from builtin.type_aliases import AnyTrivialRegType from collections import KeyElement, List +from collections.string import ( + String, + ord, + chr, + ascii, + atol, + atof, + isdigit, + isupper, + islower, + isprintable, +) from memory import UnsafePointer, Reference, AddressSpace from utils import StringRef from utils._format import Formattable, Formatter diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 6689783f78..31db793173 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -21,7 +21,7 @@ from utils import StaticIntTuple """ from builtin.io import _get_dtype_printf_format, _snprintf -from builtin.string import _calc_initial_buffer_size +from collections.string import _calc_initial_buffer_size from . import unroll from .static_tuple import StaticTuple diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 2bf3103e3a..d9b78fd851 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -22,7 +22,7 @@ from utils import StringSlice from bit import count_leading_zeros from utils import Span -from builtin.string import _isspace +from collections.string import _isspace from collections import List from memory import memcmp from sys import simdwidthof diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 6010416168..760edc86f4 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -15,7 +15,7 @@ from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width -from builtin.string import _atol, _isspace +from collections.string import _atol, _isspace from memory import UnsafePointer, memcmp from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/collections/test_string.mojo similarity index 99% rename from stdlib/test/builtin/test_string.mojo rename to stdlib/test/collections/test_string.mojo index e9ba932172..5917c456ff 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -14,7 +14,7 @@ # TODO: Replace %bare-mojo with %mojo # when https://github.com/modularml/mojo/issues/2751 is fixed. -from builtin.string import ( +from collections.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, _isspace, From 130ecb19d08870b744704f313055c1a80d829e78 Mon Sep 17 00:00:00 2001 From: Helehex Date: Fri, 9 Aug 2024 16:12:01 -0700 Subject: [PATCH 1374/2019] [External] [stdlib] mark dict entry as destroyed in `Dict.pop()` (#44897) [External] [stdlib] mark dict entry as destroyed in `Dict.pop()` Fixes https://github.com/modularml/mojo/issues/2756 Co-authored-by: Helehex Closes modularml/mojo#2796 MODULAR_ORIG_COMMIT_REV_ID: d9ee05077d11b2b4b54bdd48d1a67100ab624eee --- stdlib/src/collections/dict.mojo | 11 ++++++++++- stdlib/test/collections/test_dict.mojo | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index bd91fd17bc..4d37544f55 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -217,6 +217,15 @@ struct DictEntry[K: KeyElement, V: CollectionElement]( self.key = other.key self.value = other.value + fn reap_value(owned self) -> V: + """Take the value from an owned entry. + + Returns: + The value of the entry. + """ + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + return self.value^ + alias _EMPTY = -1 alias _REMOVED = -2 @@ -814,7 +823,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var entry_value = entry[].unsafe_take() entry[] = None self.size -= 1 - return entry_value.value^ + return entry_value^.reap_value() raise "KeyError" fn popitem(inout self) raises -> DictEntry[K, V]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 579f8bd887..92972b6215 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -538,6 +538,21 @@ def test_dict_popitem(): _ = dict.popitem() +def test_pop_string_values(): + var dict = Dict[String, String]() + dict["mojo"] = "lang" + dict["max"] = "engine" + dict["a"] = "" + dict[""] = "a" + + assert_equal(dict.pop("mojo"), "lang") + assert_equal(dict.pop("max"), "engine") + assert_equal(dict.pop("a"), "") + assert_equal(dict.pop(""), "a") + with assert_raises(contains="KeyError"): + _ = dict.pop("absent") + + fn test_clear() raises: var some_dict = Dict[String, Int]() some_dict["key"] = 1 @@ -594,6 +609,7 @@ def main(): test_owned_kwargs_dict() test_bool_conversion() test_find_get() + test_pop_string_values() test_clear() test_init_initial_capacity() test_dict_setdefault() From bd53fc141630806f7b75c9060378e758467a3b2d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 10 Aug 2024 12:09:23 -0700 Subject: [PATCH 1375/2019] [******][GPU] Improve static allocation MODULAR_ORIG_COMMIT_REV_ID: 9f3c0c57b342d593dc6ed1f8aaad2795813f4799 --- stdlib/src/memory/memory.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index a45f8afdab..63aeb8600b 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -370,7 +370,10 @@ fn stack_allocation[ """ @parameter - if triple_is_nvidia_cuda() and address_space == _GPUAddressSpace.SHARED: + if triple_is_nvidia_cuda() and address_space in ( + _GPUAddressSpace.SHARED, + _GPUAddressSpace.PARAM, + ): return __mlir_op.`pop.global_alloc`[ count = count.value, _type = UnsafePointer[type, address_space]._mlir_type, From 7b44434a95e39fb8dc171e976aafffb72ab6a827 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 10 Aug 2024 14:01:52 -0700 Subject: [PATCH 1376/2019] [******][GPU] Fix emission of the abs function MODULAR_ORIG_COMMIT_REV_ID: 143a0d69390c03986957c171b77df927978462d9 --- stdlib/src/builtin/simd.mojo | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 72d8c20b4d..6329c8b78b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1415,6 +1415,39 @@ struct SIMD[type: DType, size: Int]( if type.is_unsigned() or type is DType.bool: return self elif type.is_floating_point(): + + @parameter + if triple_is_nvidia_cuda(): + + @parameter + if type.is_half_float(): + alias prefix = "abs.bf16" if type is DType.bfloat16 else "abs.f16" + + @parameter + if size == 1: + return inlined_assembly[ + prefix + " $0, $1;", + Self, + constraints="=h,h", + has_side_effect=False, + ](self) + + var res = Self() + + @parameter + for i in range(0, size, 2): + var val = inlined_assembly[ + prefix + "x2 $0, $1;", + SIMD[type, 2], + constraints="=r,r", + has_side_effect=False, + ](self.slice[2, offset=i]()) + res = res.insert[offset=i](val) + return res + return llvm_intrinsic["llvm.fabs", Self, has_side_effect=False]( + self + ) + alias integral_type = FPUtils[type].integral_type var m = self._float_to_bits[integral_type]() return (m & (FPUtils[type].sign_mask() - 1))._bits_to_float[type]() From e499c273aabfd223f3be6e1e9edafd95e381b6ad Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 10 Aug 2024 16:39:35 -0700 Subject: [PATCH 1377/2019] [******][GPU] Extend prefetch function MODULAR_ORIG_COMMIT_REV_ID: 02798834eed846346af33f00abfb7f61f650300e --- stdlib/src/sys/intrinsics.mojo | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 7624272b3e..fb45a1e12d 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -19,7 +19,8 @@ from sys import PrefetchLocality ``` """ -from sys import sizeof +from .info import sizeof, triple_is_nvidia_cuda +from ._assembly import inlined_assembly from memory import AddressSpace, UnsafePointer @@ -1186,7 +1187,7 @@ struct PrefetchOptions: @always_inline("nodebug") fn prefetch[ - params: PrefetchOptions, type: DType + type: DType, //, params: PrefetchOptions = PrefetchOptions() ](addr: UnsafePointer[Scalar[type], *_]): """Prefetches an instruction or data into cache before it is used. @@ -1194,18 +1195,28 @@ fn prefetch[ to prefetch instruction or data into cache before they are used. Parameters: - params: Configuration options for the prefect intrinsic. type: The DType of value stored in addr. + params: Configuration options for the prefect intrinsic. Args: addr: The data pointer to prefetch. """ - llvm_intrinsic["llvm.prefetch", NoneType]( - addr.bitcast[NoneType](), - params.rw, - params.locality, - params.cache, - ) + + @parameter + if triple_is_nvidia_cuda(): + inlined_assembly[ + "prefetch.global.L2 [$0];", + NoneType, + constraints="l,~{memory}", + has_side_effect=True, + ](addr.bitcast[NoneType]()) + else: + llvm_intrinsic["llvm.prefetch", NoneType]( + addr.bitcast[NoneType](), + params.rw, + params.locality, + params.cache, + ) # ===----------------------------------------------------------------------===# From 33a5afac79dca5cee5823eee308e93a2008dc36b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 10 Aug 2024 17:26:00 -0700 Subject: [PATCH 1378/2019] [******][GPU] Use llvm_intrinsic for the sleep function, NFC MODULAR_ORIG_COMMIT_REV_ID: d78be89bbf5f2f6e6e330588c9ffc42975dcf72f --- stdlib/src/time/time.mojo | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index f1c7c427ee..7baffc3a9f 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -19,8 +19,13 @@ from time import now ``` """ -from sys import external_call, os_is_linux, os_is_windows, triple_is_nvidia_cuda -from sys._assembly import inlined_assembly +from sys import ( + external_call, + os_is_linux, + os_is_windows, + triple_is_nvidia_cuda, + llvm_intrinsic, +) from math import floor from memory import UnsafePointer @@ -287,8 +292,8 @@ fn sleep(sec: Float64): @parameter if triple_is_nvidia_cuda(): var nsec = sec * 1.0e9 - inlined_assembly["nanosleep.u32 $0;", NoneType, constraints="r"]( - nsec.cast[DType.uint32]() + llvm_intrinsic["llvm.nvvm.nanosleep", NoneType]( + nsec.cast[DType.int32]() ) return @@ -313,6 +318,10 @@ fn sleep(sec: Int): sec: The number of seconds to sleep for. """ + @parameter + if triple_is_nvidia_cuda(): + return sleep(Float64(sec)) + @parameter if os_is_windows(): # In Windows the argument is in milliseconds. From 8669ebeed7a1fcb9f259f4adf2ade73c771e5759 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 11 Aug 2024 17:35:07 -0700 Subject: [PATCH 1379/2019] [Stdlib] Use variant.unsafe_get in optional.unsafe_value, NFC This bypasses a check if the variant is the right value, since the unsafe_get should not perform such a check. MODULAR_ORIG_COMMIT_REV_ID: 957ad3af65de026cb76e554dd2267e6937247fb7 --- stdlib/src/collections/optional.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 17d5d6acdf..ad7e0cf60a 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -342,7 +342,7 @@ struct Optional[T: CollectionElement]( A reference to the contained data of the option as a Reference[T]. """ debug_assert(self.__bool__(), ".value() on empty Optional") - return self._value[T] + return self._value.unsafe_get[T]()[] fn take(inout self) -> T: """Move the value out of the Optional. From ae6a11bf0a26d67bb891c2d7884d16ff50da0725 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 12 Aug 2024 10:32:06 -0700 Subject: [PATCH 1380/2019] [Stdlib] Implement a non-intrinsic version of ctlz MODULAR_ORIG_COMMIT_REV_ID: a5c7b7fda8214ad24b7491c00e4d455579e928c2 --- stdlib/src/utils/string_slice.mojo | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index d9b78fd851..3d7d25a1d8 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -20,7 +20,6 @@ from utils import StringSlice ``` """ -from bit import count_leading_zeros from utils import Span from collections.string import _isspace from collections import List @@ -31,6 +30,29 @@ alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" +fn _count_leading_zeros(b: SIMD[DType.uint8, _], /) -> __type_of(b): + var res = __type_of(b)() + + @parameter + for i in range(b.size): + var x = b[i] + if x == 0: + res[i] = bitwidthof[DType.uint8]() + continue + var n = Scalar[DType.uint8](0) + if (x & 0xF0) == 0: + n += 4 + x <<= 4 + if (x & 0xC0) == 0: + n += 2 + x <<= 2 + if (x & 0x80) == 0: + n += 1 + x <<= 1 + res[i] = n + return res + + fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): """UTF-8 byte type. @@ -45,7 +67,7 @@ fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): - 3 -> start of 3 byte long sequence. - 4 -> start of 4 byte long sequence. """ - return count_leading_zeros(~(b & UInt8(0b1111_0000))) + return _count_leading_zeros(~(b & UInt8(0b1111_0000))) fn _validate_utf8_simd_slice[ From ab7e87b99dac0b8956ad852d24f2c10e620c3bf4 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 12 Aug 2024 14:19:57 -0400 Subject: [PATCH 1381/2019] [stdlib] Minor cleanups in `Dict` module MODULAR_ORIG_COMMIT_REV_ID: a1450200bda76ed54e157034dbb876b3ccf05846 --- stdlib/src/collections/dict.mojo | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 4d37544f55..424bb54378 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -527,10 +527,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( Returns: The new dictionary. """ - var dict = Dict[K, Optional[V]]() - for key in keys: - dict[key[]] = value - return dict + return Dict[K, Optional[V]].fromkeys(keys, value) fn __copyinit__(inout self, existing: Self): """Copy an existing dictiontary. From 2182f0b9d950b6e9dae127fcf673d04c4161df53 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 12 Aug 2024 16:17:26 -0700 Subject: [PATCH 1382/2019] [External] [stdlib] Fix TODO item about using os.path.split() in tempfile test (#44957) [External] [stdlib] Fix TODO item about using os.path.split() in tempfile test Fix pending TODO item about using `os.path.split()` in a `tempfile` test Co-authored-by: Manuel Saelices Closes modularml/mojo#3373 MODULAR_ORIG_COMMIT_REV_ID: fdc9b0cc2abed09a501a789794a1b2bccbe8e57b --- stdlib/test/tempfile/test_tempfile.mojo | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index be8818b408..c186776e6e 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -14,7 +14,7 @@ import os from collections import Dict, Optional -from os.path import exists +from os.path import exists, split from pathlib import Path from tempfile import NamedTemporaryFile, TemporaryDirectory, gettempdir, mkdtemp @@ -194,8 +194,7 @@ def test_named_temporary_file_deletion(): assert_true(exists(file_path), "Failed to create file " + file_path) assert_true(file_name.startswith("my_prefix")) assert_true(file_name.endswith("my_suffix")) - # TODO use os.path.split when it exists - assert_equal(file_path[: -len(file_name) - 1], Path().__fspath__()) + assert_equal(split(file_path)[0], Path().__fspath__()) assert_false(exists(file_path), "Failed to delete file " + file_path) with NamedTemporaryFile(delete=False) as my_tmp_file: From b16cefa5fabf9d554662d6fee9024952b40226b4 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 13 Aug 2024 12:30:20 -0600 Subject: [PATCH 1383/2019] [stdlib] Remove `StringLiteral._byte_length()` Remove the deprecated `StringLiteral._byte_length()` in favor of the public API `byte_length`. MODULAR_ORIG_COMMIT_REV_ID: 47fe906f8cb9996b273a281bf535231c15e79f30 --- stdlib/src/builtin/string_literal.mojo | 13 ------------- stdlib/test/utils/test_string_slice.mojo | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index ae41e2dfbf..0c0747623d 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -278,19 +278,6 @@ struct StringLiteral( """ return __mlir_op.`pop.string.size`(self.value) - @always_inline - @deprecated("use byte_length() instead") - fn _byte_length(self) -> Int: - """Get the string length in bytes. - - Returns: - The length of this StringLiteral in bytes. - - Notes: - This does not include the trailing null terminator in the count. - """ - return __mlir_op.`pop.string.size`(self.value) - @always_inline("nodebug") fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Get raw pointer to the underlying data. diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 24c03cee55..e51697104a 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -204,8 +204,8 @@ fn test_utf8_validation() raises: ظهرت نسخ جديدة ومختلفة من نص لوريم إيبسوم، أحياناً عن طريق الصدفة، وأحياناً عن عمد كإدخال بعض العبارات الفكاهية إليها. """ - assert_true(_is_valid_utf8(text.unsafe_ptr(), text._byte_length())) - assert_true(_is_valid_utf8(text.unsafe_ptr(), text._byte_length())) + assert_true(_is_valid_utf8(text.unsafe_ptr(), text.byte_length())) + assert_true(_is_valid_utf8(text.unsafe_ptr(), text.byte_length())) var positive = List[List[UInt8]]( List[UInt8](0x0), From 7c3b6024c5456836629c2d56c3af1fa78bc4accd Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:50:14 -0700 Subject: [PATCH 1384/2019] [External] [stdlib] Added example to ```Powable``` docstring (#45124) [External] [stdlib] Added example to ```Powable``` docstring Addresses https://github.com/modularml/mojo/issues/2867 point number one. I added an example of demonstrating how ```__pow__``` could be implemented on a ```Rational``` struct. Co-authored-by: jlofti <65878716+jlofti@users.noreply.github.com> Closes modularml/mojo#3358 MODULAR_ORIG_COMMIT_REV_ID: c0c6bb751f709f22b74a076bffd308a7ede57111 --- stdlib/src/builtin/math.mojo | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 214c780fc6..e29c3d23a3 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -262,7 +262,37 @@ trait Powable: Types that conform to `Powable` will work with the builtin `pow` function, which will return the same type as the inputs. - TODO: add example + For example: + ```mojo + @value + struct Rational(Powable): + var numerator: Float64 + var denominator: Float64 + + fn __init__(inout self, numerator: Float64, denominator: Float64): + self.numerator = numerator + self.denominator = denominator + + fn __pow__(self, exp: Self) -> Self: + var exp_value = exp.numerator / exp.denominator + return Self(pow(self.numerator, exp_value), pow(self.denominator, exp_value)) + ``` + + You can now use the ** operator to exponentiate objects + inside generic functions: + + ```mojo + fn exponentiate[T: Powable](base: T, exp: T) -> T: + return base ** exp + + var base = Rational(Float64(3.0), 5.0) + var exp = Rational(Float64(1.0), 2.0) + var res = exponentiate(base, exp) + ``` + + ```plaintext + raising to power + ``` """ # TODO(MOCO-333): Reconsider the signature when we have parametric traits or From 811138de883e0e6efe64fc7eae83028fffd1eaa1 Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 13 Aug 2024 13:51:07 -0700 Subject: [PATCH 1385/2019] [External] [stdlib] Fix TODO in `/test_string.mojo` (#45125) [External] [stdlib] Fix TODO in `test_string.mojo` Co-authored-by: Helehex Closes modularml/mojo#3374 MODULAR_ORIG_COMMIT_REV_ID: 365f5444af0d41c7cd8c759ec9c5a99e1b5c1b59 --- stdlib/test/collections/test_string.mojo | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 5917c456ff..5aa41c1f6b 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1260,15 +1260,14 @@ def test_string_iter(): concat += v assert_equal(321, atol(concat)) - # TODO: UnsafePointer does not have a store or __setitem__ method - # for v in vs: - # v.unsafe_ptr().store(0, "1") + for v in vs: + v.unsafe_ptr()[] = ord("1") - # # Borrow immutably - # for v in vs: - # concat += v + # Borrow immutably + for v in vs: + concat += v - # assert_equal(111, atol(concat)) + assert_equal(321111, atol(concat)) var idx = -1 vs = String("mojo🔥") From 634b338d60d111615959b07fca4e841297234dae Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 13 Aug 2024 15:00:42 -0700 Subject: [PATCH 1386/2019] [External] [stdlib] Use memcpy in `List._realloc` for x13.2 speedups in some benchmarks (#44680) [External] [stdlib] Use memcpy in `List._realloc` for x13.2 speedups in some benchmarks Currently, when re-allocating a list, elements are moved one by one. While it is correct, when one knows that `T` is a trivial type, we an use `memcpy` and get massive speedups in some benchmarks. Since the compiler currently doesn't expose this information about a type, we rely on the information being given by the caller. If the caller doesn't give this information, we fallback to a normal loop which will move each element. We fully expect this parameter to be temporary and to go away in the future, when the compiler exposes more information about types. This is extremely useful when working with the `String` type, as moving bytes one by one is quite slow (I noticed it when looking at profiling data and the assembly code when working on SSO). Here are the speedups in some benchmarks (I only use the hint in the `String` struct, as the PR shows). Here is the hardware: Intel(R) Core(TM) i7-10700KF CPU @ 3.80GHz, 16gb ram with WSL 2, windows 11 pro 21H2 * `str(x: Int)` on the first 1000000 integers: 185820us -> 186953us (x1.0) * Adding one char to a string in a loop 30000 times 159887us -> 12103us (x13.2) * Parsing a json list of 5000 integers: 345455us -> 342679us (x1.0) * Call `str(x: List[Int])` on a list of 10000 integers: 205393us -> 17321us (x11.6) Currently this optimization is applied to `String` automatically, but we can expect speedups in the future when the compiler gives the info, and we'll then get speedups for types like `List[Int]`, `List[SIMD[...]]` etc... Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3349 MODULAR_ORIG_COMMIT_REV_ID: 83c9c8cd85acf6df95c1f7e82cf0a00b3a0d256a --- stdlib/scripts/run-tests.sh | 4 +++ stdlib/src/builtin/reversed.mojo | 4 ++- stdlib/src/collections/list.mojo | 41 +++++++++++++++++++++----- stdlib/src/collections/string.mojo | 19 +++++++----- stdlib/test/collections/test_list.mojo | 19 ++++++++++++ 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/stdlib/scripts/run-tests.sh b/stdlib/scripts/run-tests.sh index e228135c99..70a7bf52db 100755 --- a/stdlib/scripts/run-tests.sh +++ b/stdlib/scripts/run-tests.sh @@ -25,6 +25,10 @@ source "${SCRIPT_DIR}"/build-stdlib.sh echo "Packaging up the test_utils." TEST_UTILS_PATH="${REPO_ROOT}/stdlib/test/test_utils" +# This is needed to compile test_utils.mojopkg correctly, otherwise it +# uses the stdlib that's given in the nightly, and will fail compilation +# if some breaking changes are made. +export MODULAR_MOJO_NIGHTLY_IMPORT_PATH=$BUILD_DIR mojo package "${TEST_UTILS_PATH}" -o "${BUILD_DIR}/test_utils.mojopkg" TEST_PATH="${REPO_ROOT}/stdlib/test" diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index fb5cd21e53..83c496c0d4 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -78,7 +78,9 @@ fn reversed[T: ReversibleRange](value: T) -> _StridedRange: fn reversed[ T: CollectionElement -](ref [_]value: List[T]) -> _ListIter[T, __lifetime_of(value), False]: +](ref [_]value: List[T, *_]) -> _ListIter[ + T, __type_of(value).hint_trivial_type, __lifetime_of(value), False +]: """Get a reversed iterator of the input list. **Note**: iterators are currently non-raising. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1454f089c4..deaea46174 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -22,7 +22,7 @@ from collections import List from sys.intrinsics import _type_is_eq from os import abort -from memory import Reference, UnsafePointer +from memory import Reference, UnsafePointer, memcpy from utils import Span from .optional import Optional @@ -36,6 +36,7 @@ from .optional import Optional struct _ListIter[ list_mutability: Bool, //, T: CollectionElement, + hint_trivial_type: Bool, list_lifetime: AnyLifetime[list_mutability].type, forward: Bool = True, ]: @@ -44,11 +45,13 @@ struct _ListIter[ Parameters: list_mutability: Whether the reference to the list is mutable. T: The type of the elements in the list. + hint_trivial_type: Set to `True` if the type `T` is trivial, this is not mandatory, + but it helps performance. Will go away in the future. list_lifetime: The lifetime of the List forward: The iteration direction. `False` is backwards. """ - alias list_type = List[T] + alias list_type = List[T, hint_trivial_type] var index: Int var src: Reference[Self.list_type, list_lifetime] @@ -75,7 +78,7 @@ struct _ListIter[ return self.index -struct List[T: CollectionElement]( +struct List[T: CollectionElement, hint_trivial_type: Bool = False]( CollectionElement, CollectionElementNew, Sized, Boolable ): """The `List` type is a dynamically-allocated list. @@ -85,6 +88,8 @@ struct List[T: CollectionElement]( Parameters: T: The type of the elements. + hint_trivial_type: A hint to the compiler that the type T is trivial. + It's not mandatory, but if set, it allows some optimizations. """ # Fields @@ -339,7 +344,9 @@ struct List[T: CollectionElement]( """ self.extend(other^) - fn __iter__(ref [_]self: Self) -> _ListIter[T, __lifetime_of(self)]: + fn __iter__( + ref [_]self: Self, + ) -> _ListIter[T, hint_trivial_type, __lifetime_of(self)]: """Iterate over elements of the list, returning immutable references. Returns: @@ -349,7 +356,7 @@ struct List[T: CollectionElement]( fn __reversed__( ref [_]self: Self, - ) -> _ListIter[T, __lifetime_of(self), False]: + ) -> _ListIter[T, hint_trivial_type, __lifetime_of(self), False]: """Iterate backwards over the list, returning immutable references. Returns: @@ -458,8 +465,11 @@ struct List[T: CollectionElement]( fn _realloc(inout self, new_capacity: Int): var new_data = UnsafePointer[T].alloc(new_capacity) - for i in range(self.size): - (self.data + i).move_pointee_into(new_data + i) + _move_pointee_into_many_elements[hint_trivial_type]( + dest=new_data, + src=self.data, + size=self.size, + ) if self.data: self.data.free() @@ -525,7 +535,7 @@ struct List[T: CollectionElement]( for i in range(x - 1): self.extend(orig) - fn extend(inout self, owned other: List[T]): + fn extend(inout self, owned other: List[T, *_]): """Extends this list by consuming the elements of `other`. Args: @@ -881,3 +891,18 @@ struct List[T: CollectionElement]( fn _clip(value: Int, start: Int, end: Int) -> Int: return max(start, min(value, end)) + + +fn _move_pointee_into_many_elements[ + T: CollectionElement, //, hint_trivial_type: Bool +](dest: UnsafePointer[T], src: UnsafePointer[T], size: Int): + @parameter + if hint_trivial_type: + memcpy( + dest=dest.bitcast[Int8](), + src=src.bitcast[Int8](), + count=size * sizeof[T](), + ) + else: + for i in range(size): + (src + i).move_pointee_into(dest + i) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 0c75516372..7207bc73c6 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -691,7 +691,7 @@ struct String( """Represents a mutable string.""" # Fields - alias _buffer_type = List[UInt8] + alias _buffer_type = List[UInt8, hint_trivial_type=True] var _buffer: Self._buffer_type """The underlying storage for the string.""" @@ -715,7 +715,7 @@ struct String( # ===------------------------------------------------------------------=== # @always_inline - fn __init__(inout self, owned impl: List[UInt8]): + fn __init__(inout self, owned impl: List[UInt8, *_]): """Construct a string from a buffer of bytes. The buffer must be terminated with a null byte: @@ -735,7 +735,12 @@ struct String( impl[-1] == 0, "expected last element of String buffer to be null terminator", ) - self._buffer = impl^ + # We make a backup because steal_data() will clear size and capacity. + var size = impl.size + var capacity = impl.capacity + self._buffer = Self._buffer_type( + unsafe_pointer=impl.steal_data(), size=size, capacity=capacity + ) @always_inline fn __init__(inout self): @@ -1328,7 +1333,7 @@ struct String( """ return self.unsafe_ptr().bitcast[C_char]() - fn as_bytes(self) -> List[UInt8]: + fn as_bytes(self) -> Self._buffer_type: """Retrieves the underlying byte sequence encoding the characters in this string. @@ -1657,7 +1662,7 @@ struct String( var old_len = old.byte_length() var new_len = new.byte_length() - var res = List[UInt8]() + var res = Self._buffer_type() res.reserve(self_len + (old_len - new_len) * occurrences + 1) for _ in range(occurrences): @@ -1787,7 +1792,7 @@ struct String( return hash(self._strref_dangerous()) fn _interleave(self, val: String) -> String: - var res = List[UInt8]() + var res = Self._buffer_type() var val_ptr = val.unsafe_ptr() var self_ptr = self.unsafe_ptr() res.reserve(val.byte_length() * self.byte_length() + 1) @@ -2149,7 +2154,7 @@ struct String( len(fillchar) == 1, "fill char needs to be a one byte literal" ) var fillbyte = fillchar.as_bytes_slice()[0] - var buffer = List[UInt8](capacity=width + 1) + var buffer = Self._buffer_type(capacity=width + 1) buffer.resize(width, fillbyte) buffer.append(0) memcpy(buffer.unsafe_ptr().offset(start), self.unsafe_ptr(), len(self)) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 9b6c942eab..60ad6b37b7 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -690,6 +690,24 @@ def test_list_span(): assert_equal(es[0], 1) +def test_list_realloc_trivial_types(): + a = List[Int, hint_trivial_type=True]() + for i in range(100): + a.append(i) + + assert_equal(len(a), 100) + for i in range(100): + assert_equal(a[i], i) + + b = List[Int8, hint_trivial_type=True]() + for i in range(100): + b.append(Int8(i)) + + assert_equal(len(b), 100) + for i in range(100): + assert_equal(b[i], Int8(i)) + + def test_list_boolable(): assert_true(List[Int](1)) assert_false(List[Int]()) @@ -927,6 +945,7 @@ def main(): test_list_iter() test_list_iter_mutable() test_list_span() + test_list_realloc_trivial_types() test_list_boolable() test_constructor_from_pointer() test_constructor_from_other_list_through_pointer() From 19c998d8d28d251ae092bae203d74256df0b9f87 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 13 Aug 2024 15:24:14 -0700 Subject: [PATCH 1387/2019] Delete community page and link to new community page. MODULAR_ORIG_COMMIT_REV_ID: 635ffb346fa2b85cd23793d894781f22567d6548 --- docs/community.mdx | 46 -------------------------------------------- docs/faq.md | 6 +++--- docs/manual/index.md | 3 +-- 3 files changed, 4 insertions(+), 51 deletions(-) delete mode 100644 docs/community.mdx diff --git a/docs/community.mdx b/docs/community.mdx deleted file mode 100644 index ba182694e7..0000000000 --- a/docs/community.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Mojo🔥 community -sidebar_label: Community -description: Resources to share feedback, report issues, and chat. -hide_table_of_contents: true ---- - - -import Cards from '@site/src/components/Cards'; -import styles from '@site/src/components/Cards/styles.module.scss'; - -export const cardData = [ -{ -emoji: , -product: 'GitHub', -title: 'Ask a question', -description: -'See existing discussion posts, ask questions, and share your ideas.', -link: 'https://github.com/modularml/mojo/discussions', -}, -{ -emoji: , -product: 'GitHub', -title: 'Report an issue', -description: -'Report bugs or other issues with the Mojo SDK or Mojo Playground.', -link: 'https://github.com/modularml/mojo/issues/new/choose', -}, -{ -emoji: , -product: 'Discord', -title: 'Chat about Mojo', -description: -'Join our discussion about the Mojo language and tools with the community.', -link: 'https://www.discord.gg/modular', -}, -]; - -Mojo is still very young, but we believe an active community and a strong -feedback pipeline is key to its success. - -We'd love to hear from you through the following community channels. - -
    - -
    diff --git a/docs/faq.md b/docs/faq.md index a05a863fe6..d3e11a6a59 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -5,8 +5,8 @@ description: Answers to questions we expect about Mojo. --- We tried to anticipate your questions about Mojo on this page. If this page -doesn't answer all your questions, also check out our [Mojo community -channels](/mojo/community). +doesn't answer all your questions, also check out our [community +channels](https://www.modular.com/community). ## Motivation @@ -405,4 +405,4 @@ provides important information about our current priorities and links to our GitHub channels where you can report issues and discuss new features. To get in touch with the Mojo team and developer community, use the resources -on our [Mojo community page](/mojo/community). +on our [community page](https://www.modular.com/community). diff --git a/docs/manual/index.md b/docs/manual/index.md index 7f8256e80c..6d3e2bbd3e 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -18,7 +18,7 @@ Mojo](/mojo/why-mojo). Beware that Mojo is still a very young language, so there's a lot that hasn't been built yet. Likewise, there's a lot of documentation that hasn't been written yet. But we're excited to share Mojo with you and [get your -feedback](/mojo/community). +feedback](https://www.modular.com/community). ## Contents @@ -73,4 +73,3 @@ feedback](/mojo/community). - [Roadmap and sharp edges](/mojo/roadmap) - [Changelog](/mojo/changelog) - [FAQ](/mojo/faq) - - [Community](/mojo/community) From c088bc4cc32dc8d8f21839337b98b5d4507b4438 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 13 Aug 2024 17:49:29 -0600 Subject: [PATCH 1388/2019] [stdlib] Rename `builtin/test_list.mojo` to `test_list_literal.mojo` Rename `builtin/test_list.mojo` to `builtin/test_list_literal.mojo` to more accurately reflect what it's testing: `ListLiteral`. `List` is tested elsewhere in `collections/test_list.mojo`. While here, change some `fn raises` to `def` for consistency with other tests. Additionally, move the `repr` test out of the `ListLiteral` tests and over to `collections/test_list.mojo` - after all, it's testing the `List` behavior here. MODULAR_ORIG_COMMIT_REV_ID: a0b46272ff47dd7cf0857a32b4ccfa698376b15c --- ...{test_list.mojo => test_list_literal.mojo} | 19 ++++++------------- stdlib/test/collections/test_list.mojo | 8 ++++++++ 2 files changed, 14 insertions(+), 13 deletions(-) rename stdlib/test/builtin/{test_list.mojo => test_list_literal.mojo} (86%) diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list_literal.mojo similarity index 86% rename from stdlib/test/builtin/test_list.mojo rename to stdlib/test/builtin/test_list_literal.mojo index 66e5048888..38ae7764fe 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list_literal.mojo @@ -15,13 +15,13 @@ from testing import assert_equal, assert_true, assert_false -fn test_list() raises: +def test_list(): assert_equal(len([1, 2.0, 3.14, [-1, -2]]), 4) -fn test_variadic_list() raises: +def test_variadic_list(): @parameter - fn check_list(*nums: Int) raises: + def check_list(*nums: Int): assert_equal(nums[0], 5) assert_equal(nums[1], 8) assert_equal(nums[2], 6) @@ -32,15 +32,9 @@ fn test_variadic_list() raises: check_list(5, 8, 6) -fn test_repr_list() raises: - var l = List(1, 2, 3) - assert_equal(l.__repr__(), "[1, 2, 3]") - var empty = List[Int]() - assert_equal(empty.__repr__(), "[]") - - -fn test_contains() raises: - # List +def test_contains(): + # Explicitly showing the difference in behavior in testing `List` vs. `ListLiteral` + # here. There are additional tests for `List.__contains__` in the `test_list.mojo` file. var l = List[String]("Hello", ",", "World", "!") assert_true("Hello" in l) assert_true(l.__contains__(String(","))) @@ -75,5 +69,4 @@ fn test_contains() raises: def main(): test_list() test_variadic_list() - test_repr_list() test_contains() diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 60ad6b37b7..db1230fad9 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -919,6 +919,13 @@ def test_list_dtor(): assert_equal(g_dtor_count, 1) +def test_list_repr(): + var l = List(1, 2, 3) + assert_equal(l.__repr__(), "[1, 2, 3]") + var empty = List[Int]() + assert_equal(empty.__repr__(), "[]") + + # ===-------------------------------------------------------------------===# # main # ===-------------------------------------------------------------------===# @@ -956,3 +963,4 @@ def main(): test_list_contains() test_indexing() test_list_dtor() + test_list_repr() From 99ecc55b9bd43979f3c0b2dfd56f3fa1917609b5 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 13 Aug 2024 21:25:29 -0600 Subject: [PATCH 1389/2019] [stdlib] Rename `python/object.mojo` to `python/python_object.mojo` This helps avoid a clash with the `builtin/object.mojo`. It's slightly inconvenient having the same filename when reading error messages quickly (since both filenames are `object.mojo`. Fix this by simply renaming `python/object.mojo` to `python/python_object.mojo`. MODULAR_ORIG_COMMIT_REV_ID: 6e24b9c7482144bad72accd14362200d1e86ce7a --- stdlib/src/python/__init__.mojo | 2 +- stdlib/src/python/python.mojo | 2 +- stdlib/src/python/{object.mojo => python_object.mojo} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename stdlib/src/python/{object.mojo => python_object.mojo} (100%) diff --git a/stdlib/src/python/__init__.mojo b/stdlib/src/python/__init__.mojo index e2ab5ed4e5..d541bc1ca3 100644 --- a/stdlib/src/python/__init__.mojo +++ b/stdlib/src/python/__init__.mojo @@ -12,5 +12,5 @@ # ===----------------------------------------------------------------------=== # """Implements the python package.""" -from .object import PythonObject +from .python_object import PythonObject from .python import Python diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 762cbf440b..2b50db268d 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -29,7 +29,7 @@ from memory import UnsafePointer from utils import StringRef from ._cpython import CPython, Py_eval_input, Py_file_input -from .object import PythonObject +from .python_object import PythonObject fn _init_global(ignored: UnsafePointer[NoneType]) -> UnsafePointer[NoneType]: diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/python_object.mojo similarity index 100% rename from stdlib/src/python/object.mojo rename to stdlib/src/python/python_object.mojo From 9726f5dec3cfe1a42c6f6174b9439c751c931517 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 10:28:06 -0400 Subject: [PATCH 1390/2019] [stdlib] Remove `os.abort` from prelude module MODULAR_ORIG_COMMIT_REV_ID: ddba82fd1ad1b7ea40e1449bc53783706bc90a81 --- docs/changelog.md | 2 ++ stdlib/src/builtin/format_int.mojo | 1 + stdlib/src/builtin/simd.mojo | 1 + stdlib/src/collections/_index_normalization.mojo | 1 + stdlib/src/prelude/__init__.mojo | 1 - stdlib/src/sys/ffi.mojo | 1 + stdlib/test/memory/test_maybe_uninitialized.mojo | 1 + stdlib/test/os/test_no_trap.mojo | 1 + stdlib/test/utils/test_inlined_string.mojo | 1 + 9 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4ecdfa9954..3d9cd1c9a8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -626,6 +626,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD.load/store` are moved to `UnsafePointer`. +- `abort` is removed from prelude. + ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index e4e1b4f526..4dded088fb 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ +from os import abort from collections import List, Optional, InlineArray from utils import StringSlice, StaticString diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6329c8b78b..f29e53ad59 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -28,6 +28,7 @@ from sys import ( ) from sys.info import _current_arch from sys._assembly import inlined_assembly +from os import abort from bit import pop_count from builtin._math import Ceilable, CeilDivable, Floorable, Truncable diff --git a/stdlib/src/collections/_index_normalization.mojo b/stdlib/src/collections/_index_normalization.mojo index b64f48908c..6df7c773f7 100644 --- a/stdlib/src/collections/_index_normalization.mojo +++ b/stdlib/src/collections/_index_normalization.mojo @@ -14,6 +14,7 @@ to data elements in arrays.""" from sys import triple_is_nvidia_cuda +from os import abort fn get_out_of_bounds_error_message[ diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index e87e401b43..30f2d1d3f9 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -139,5 +139,4 @@ from utils._visualizers import lldb_formatter_wrapping_type # Load-bearing ones to remove from sys import alignof, sizeof, bitwidthof, simdwidthof from memory import bitcast -from os import abort from sys.ffi import external_call diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 21174d5897..689bc42d44 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements a foreign functions interface (FFI).""" +from os import abort from memory import UnsafePointer from utils import StringRef diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index 04347f1cfc..f2937881e1 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from os import abort from memory.maybe_uninitialized import UnsafeMaybeUninitialized from test_utils import CopyCounter, MoveCounter, ValueDestructorRecorder diff --git a/stdlib/test/os/test_no_trap.mojo b/stdlib/test/os/test_no_trap.mojo index e0359b01de..3ccfd1a230 100644 --- a/stdlib/test/os/test_no_trap.mojo +++ b/stdlib/test/os/test_no_trap.mojo @@ -15,6 +15,7 @@ # away. from sys import argv +from os import abort # CHECK-LABEL: OK diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index fd1a41b609..11712e5925 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -15,6 +15,7 @@ from testing import assert_equal, assert_true +from os import abort from utils import InlineString from utils.inline_string import _FixedString From 11a17c635f1c29caf5a738db2e9c1372ff1985e8 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 10:32:34 -0400 Subject: [PATCH 1391/2019] [stdlib] Make `_insertion_sort` stable Minor fix. Change `>=` to `>` in the inner loop makes insertion sort stable. MODULAR_ORIG_COMMIT_REV_ID: e55f9f3335f8fae1d64c0a0069829851050a144a --- stdlib/src/builtin/sort.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 90add3606b..dd4b143f11 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -55,7 +55,7 @@ fn _insertion_sort[ # Find the placement of the value in the array, shifting as we try to # find the position. Throughout, we assume array[start:i] has already # been sorted. - while j > 0 and not cmp_fn(array[j - 1], value): + while j > 0 and cmp_fn(value, array[j - 1]): array[j] = array[j - 1] j -= 1 From dc871381a5e1c6f80c1395f91a4db8608aa21ee3 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:52:50 -0400 Subject: [PATCH 1392/2019] [stdlib] Remove `external_call` from prelude module MODULAR_ORIG_COMMIT_REV_ID: c5640bb8510c24f08124054cc622469d8fcae27d --- docs/changelog.md | 2 +- stdlib/src/memory/memory.mojo | 8 +++++++- stdlib/src/os/_linux_aarch64.mojo | 1 + stdlib/src/os/_linux_x86.mojo | 1 + stdlib/src/os/_macos.mojo | 1 + stdlib/src/prelude/__init__.mojo | 1 - stdlib/src/pwd/_linux.mojo | 2 +- stdlib/src/pwd/_macos.mojo | 2 +- 8 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3d9cd1c9a8..741ab92238 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -626,7 +626,7 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD.load/store` are moved to `UnsafePointer`. -- `abort` is removed from prelude. +- `external_call` and `abort` are removed from prelude. ### ❌ Removed diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 63aeb8600b..df8f6f9e96 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -20,7 +20,13 @@ from memory import memcmp """ -from sys import alignof, llvm_intrinsic, sizeof, triple_is_nvidia_cuda +from sys import ( + alignof, + llvm_intrinsic, + sizeof, + triple_is_nvidia_cuda, + external_call, +) from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index 7bb7e08d62..bebc4bffc9 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from collections import InlineArray +from sys import external_call from time.time import _CTimeSpec from .fstat import stat_result diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index 0406d8101c..e11fb84423 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -13,6 +13,7 @@ from time.time import _CTimeSpec from collections import InlineArray +from sys.ffi import external_call from .fstat import stat_result diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index bbf4064e46..78f3f3abcd 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from collections import InlineArray +from sys import external_call from time.time import _CTimeSpec from .fstat import stat_result diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 30f2d1d3f9..42b2ba8c4d 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -139,4 +139,3 @@ from utils._visualizers import lldb_formatter_wrapping_type # Load-bearing ones to remove from sys import alignof, sizeof, bitwidthof, simdwidthof from memory import bitcast -from sys.ffi import external_call diff --git a/stdlib/src/pwd/_linux.mojo b/stdlib/src/pwd/_linux.mojo index bef5c01387..7635c8afdc 100644 --- a/stdlib/src/pwd/_linux.mojo +++ b/stdlib/src/pwd/_linux.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from .pwd import Passwd from memory import UnsafePointer -from sys.ffi import C_char +from sys.ffi import C_char, external_call alias uid_t = Int32 alias gid_t = Int32 diff --git a/stdlib/src/pwd/_macos.mojo b/stdlib/src/pwd/_macos.mojo index 16d8690a51..33917b3d12 100644 --- a/stdlib/src/pwd/_macos.mojo +++ b/stdlib/src/pwd/_macos.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from .pwd import Passwd from memory import UnsafePointer -from sys.ffi import C_char +from sys.ffi import C_char, external_call alias uid_t = Int32 alias gid_t = Int32 From 0a148266c005be8724189981b97bf39ccb4718f3 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:54:38 -0400 Subject: [PATCH 1393/2019] [stdlib] Introduce stable sort Implemented a naive implementation of [Timsort](https://en.wikipedia.org/wiki/Timsort#:~:text=Timsort%20is%20a%20hybrid%2C%20stable,in%20the%20Python%20programming%20language.) and a merge function. We can now call stable sort by specifying the `stable` parameter for `sort`: ```mojo sort[cmp_fn, stable=True](list) ``` MODULAR_ORIG_COMMIT_REV_ID: bf2e3000ba88190320e2f2f28abeb7c509fbe56a --- docs/changelog.md | 9 ++ stdlib/src/builtin/sort.mojo | 135 +++++++++++++++++++++++++++-- stdlib/test/builtin/test_sort.mojo | 54 ++++++++++++ 3 files changed, 192 insertions(+), 6 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 741ab92238..028040c8bf 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -369,6 +369,15 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. # "Mojo 'Mojo'" ``` +- `sort` now supports `stable` parameter. It can be called by + + ```mojo + sort[cmp_fn, stable=True](list) + ``` + + The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to + allocate, the program will crash. + ### 🦋 Changed - The set of automatically imported entities (types, aliases, functions) into user's diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index dd4b143f11..a1a3f50483 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -256,6 +256,105 @@ fn _quicksort[ stack.append(Span[type, lifetime](unsafe_ptr=ptr, len=pivot)) +# ===----------------------------------------------------------------------===# +# stable sort +# ===----------------------------------------------------------------------===# + + +fn merge[ + type: CollectionElement, + lifetime: MutableLifetime, //, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +]( + span1: Span[type, lifetime], + span2: Span[type, lifetime], + result: Span[type, lifetime], +): + """Merge span1 and span2 into result using the given cmp_fn. The function + will crash if result is not large enough to hold both span1 and span2. + + Parameters: + type: Type of the spans. + lifetime: Lifetime of the spans. + cmp_fn: Comparison functor of (type, type) capturing -> Bool type. + + Args: + span1: The first span to be merged. + span2: The second span to be merged. + result: The output span. + """ + var span1_size = len(span1) + var span2_size = len(span2) + + debug_assert( + span1_size + span2_size <= len(result), + "The merge result does not fit in the span provided", + ) + var i = 0 + var j = 0 + var k = 0 + while i < span1_size: + if j == span2_size: + while i < span1_size: + result[k] = span1[i] + k += 1 + i += 1 + return + if cmp_fn(span2[j], span1[i]): + result[k] = span2[j] + j += 1 + else: + result[k] = span1[i] + i += 1 + k += 1 + + while j < span2_size: + result[k] = span2[j] + k += 1 + j += 1 + + +fn _stable_sort_impl[ + type: CollectionElement, + lifetime: MutableLifetime, //, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](span: Span[type, lifetime], temp_buff: Span[type, lifetime]): + var size = len(span) + if size <= 1: + return + var i = 0 + var array = span.unsafe_ptr() + while i < size: + _insertion_sort[cmp_fn]( + span[i : min(i + insertion_sort_threshold, size)] + ) + i += insertion_sort_threshold + var merge_size = insertion_sort_threshold + while merge_size < size: + var j = 0 + while j + merge_size < size: + var span1 = span[j : j + merge_size] + var span2 = span[j + merge_size : min(size, j + 2 * merge_size)] + merge[cmp_fn](span1, span2, temp_buff) + for i in range(merge_size + len(span2)): + span[j + i] = temp_buff[i] + j += 2 * merge_size + merge_size *= 2 + + +fn _stable_sort[ + type: CollectionElement, + lifetime: MutableLifetime, //, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, +](span: Span[type, lifetime]): + var temp_buff = UnsafePointer[type].alloc(len(span)) + var temp_buff_span = Span[type, lifetime]( + unsafe_ptr=temp_buff, len=len(span) + ) + _stable_sort_impl[cmp_fn](span, temp_buff_span) + temp_buff.free() + + # ===----------------------------------------------------------------------===# # partition # ===----------------------------------------------------------------------===# @@ -401,6 +500,8 @@ fn _sort[ type: CollectionElement, lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + *, + stable: Bool = False, ](span: Span[type, lifetime]): if len(span) <= 5: _delegate_small_sort[cmp_fn](span) @@ -410,6 +511,10 @@ fn _sort[ _insertion_sort[cmp_fn](span) return + if stable: + _stable_sort[cmp_fn](span) + return + _quicksort[cmp_fn](span) @@ -421,6 +526,8 @@ fn sort[ type: CollectionElement, lifetime: MutableLifetime, //, cmp_fn: fn (type, type) capturing -> Bool, + *, + stable: Bool = False, ](span: Span[type, lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. @@ -429,6 +536,7 @@ fn sort[ type: CollectionElement type of the underlying data. lifetime: Lifetime of span. cmp_fn: The comparison function. + stable: Whether the sort should be stable. Args: span: The span to be sorted. @@ -438,12 +546,14 @@ fn sort[ fn _cmp_fn(lhs: _SortWrapper[type], rhs: _SortWrapper[type]) -> Bool: return cmp_fn(lhs.data, rhs.data) - _sort[_cmp_fn](span) + _sort[_cmp_fn, stable=stable](span) fn sort[ lifetime: MutableLifetime, //, cmp_fn: fn (Int, Int) capturing -> Bool, + *, + stable: Bool = False, ](span: Span[Int, lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. @@ -451,6 +561,7 @@ fn sort[ Parameters: lifetime: Lifetime of span. cmp_fn: The comparison function. + stable: Whether the sort should be stable. Args: span: The span to be sorted. @@ -460,13 +571,15 @@ fn sort[ fn _cmp_fn(lhs: _SortWrapper[Int], rhs: _SortWrapper[Int]) -> Bool: return cmp_fn(lhs.data, rhs.data) - _sort[_cmp_fn](span) + _sort[_cmp_fn, stable=stable](span) fn sort[ type: DType, lifetime: MutableLifetime, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, + *, + stable: Bool = False, ](span: Span[Scalar[type], lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. @@ -475,6 +588,7 @@ fn sort[ type: DType type of the underlying data. lifetime: Lifetime of span. cmp_fn: The comparison function. + stable: Whether the sort should be stable. Args: span: The span to be sorted. @@ -486,17 +600,20 @@ fn sort[ ) -> Bool: return cmp_fn(lhs.data, rhs.data) - _sort[_cmp_fn](span) + _sort[_cmp_fn, stable=stable](span) fn sort[ lifetime: MutableLifetime, //, + *, + stable: Bool = False, ](span: Span[Int, lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: lifetime: Lifetime of span. + stable: Whether the sort should be stable. Args: span: The span to be sorted. @@ -506,12 +623,14 @@ fn sort[ fn _cmp_fn(lhs: Int, rhs: Int) -> Bool: return lhs < rhs - sort[_cmp_fn](span) + sort[_cmp_fn, stable=stable](span) fn sort[ type: DType, lifetime: MutableLifetime, //, + *, + stable: Bool = False, ](span: Span[Scalar[type], lifetime]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. @@ -519,6 +638,7 @@ fn sort[ Parameters: type: CollectionElement type of the underlying data. lifetime: Lifetime of span. + stable: Whether the sort should be stable. Args: span: The span to be sorted. @@ -528,18 +648,21 @@ fn sort[ fn _cmp_fn(lhs: Scalar[type], rhs: Scalar[type]) -> Bool: return lhs < rhs - sort[_cmp_fn](span) + sort[_cmp_fn, stable=stable](span) fn sort[ type: ComparableCollectionElement, lifetime: MutableLifetime, //, + *, + stable: Bool = False, ](span: Span[type, lifetime]): """Sort list of the order comparable elements in-place. Parameters: type: The order comparable collection element type. lifetime: Lifetime of span. + stable: Whether the sort should be stable. Args: span: The span to be sorted. @@ -549,7 +672,7 @@ fn sort[ fn _cmp_fn(a: type, b: type) -> Bool: return a < b - sort[_cmp_fn](span) + sort[_cmp_fn, stable=stable](span) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index b3f457e2a4..d4a2962a35 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -297,6 +297,16 @@ fn test_sort103() raises: for i in range(1, length): assert_false(list[i - 1] > list[i]) + var list1 = List[Int](capacity=length) + + for i in range(length): + list1.append(length - i - 1) + + sort[stable=True](list1) + + for i in range(1, length): + assert_false(list1[i - 1] > list1[i]) + fn test_sort_any_103() raises: alias length = 103 @@ -596,6 +606,49 @@ fn test_sort_empty_comparable_elements_list() raises: assert_true(len(person_list) == 0) +@value +struct IntPair: + var x: Int + var idx: Int + + +def test_stable_sort_stress(): + var lens = List[Int](3, 100, 117, 223, 500, 1000, 1500, 2000, 3000) + var random_seed = 0 + seed(random_seed) + + @parameter + fn test[ + cmp_fn: fn (IntPair, IntPair) capturing -> Bool, + check_fn: fn (IntPair, IntPair) capturing -> Bool, + ](length: Int) raises: + var list = List[IntPair](capacity=length) + for i in range(length): + # make the range smaller so we can get more repeats + list.append(IntPair(int(random_si64(0, 100)), i)) + + sort[cmp_fn, stable=True](list) + + for i in range(length - 1): + assert_true(check_fn(list[i], list[i + 1])) + + # sort by only comparing the x value of the IntPair, then check the sort is + # stable by making sure that for the same x value, the idx field is sorted. + @parameter + @always_inline + fn _lt(lhs: IntPair, rhs: IntPair) -> Bool: + return lhs.x < rhs.x + + @parameter + @always_inline + fn _lt_check(lhs: IntPair, rhs: IntPair) -> Bool: + return lhs.idx < rhs.idx if lhs.x == rhs.x else lhs.x < rhs.x + + for i in range(len(lens)): + var length = lens[i] + test[_lt, _lt_check](length) + + def main(): test_sort_small_3() test_sort_small_5() @@ -613,6 +666,7 @@ def main(): test_quick_sort_repeated_val() test_sort_stress() + test_stable_sort_stress() test_sort_custom() From c22b39c99866d5c370309d50744b39a4777ba35f Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:08:27 -0400 Subject: [PATCH 1394/2019] [stdlib] Update `List` arguments In #44680 we added a new parameter to `List`. This PR changes `List[T]` to `List[T, *_]` in various function arguments in order to express unbound parameters correctly. MODULAR_ORIG_COMMIT_REV_ID: 91d02817a08e643743386b1a145fc21307362065 --- stdlib/src/builtin/bool.mojo | 4 ++-- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/collections/counter.mojo | 4 ++-- stdlib/src/collections/dict.mojo | 4 ++-- stdlib/src/collections/list.mojo | 20 ++++++++++++-------- stdlib/src/collections/set.mojo | 2 +- stdlib/src/collections/string.mojo | 2 +- stdlib/src/math/math.mojo | 8 ++++---- stdlib/src/math/polynomial.mojo | 4 ++-- stdlib/src/utils/span.mojo | 2 +- 10 files changed, 28 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index d760ce9767..836e3f4f08 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -527,7 +527,7 @@ fn bool[T: Boolable, //](value: T) -> Bool: # TODO: Combine these into Iterators over Boolable elements -fn any[T: BoolableCollectionElement](list: List[T]) -> Bool: +fn any[T: BoolableCollectionElement](list: List[T, *_]) -> Bool: """Checks if **any** element in the list is truthy. Parameters: @@ -584,7 +584,7 @@ fn any(value: SIMD) -> Bool: # TODO: Combine these into Iterators over Boolable elements -fn all[T: BoolableCollectionElement](list: List[T]) -> Bool: +fn all[T: BoolableCollectionElement](list: List[T, *_]) -> Bool: """Checks if **all** elements in the list are truthy. Parameters: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 0c0747623d..2d50ab137d 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -369,7 +369,7 @@ struct StringLiteral( """ return StringRef(self).rfind(substr, start=start) - fn join[T: StringableCollectionElement](self, elems: List[T]) -> String: + fn join[T: StringableCollectionElement](self, elems: List[T, *_]) -> String: """Joins string elements using the current string as a delimiter. Parameters: diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 8164cf8110..8cf6f41c2d 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -49,7 +49,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): self._data = Dict[V, Int]() # TODO: Change List to Iterable when it is supported in Mojo - fn __init__(inout self, items: List[V]): + fn __init__(inout self, items: List[V, *_]): """Create a from an input iterable. Args: @@ -70,7 +70,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): self._data = Dict[V, Int](other=other._data) @staticmethod - fn fromkeys(keys: List[V], value: Int) -> Self: + fn fromkeys(keys: List[V, *_], value: Int) -> Self: """Create a new Counter from a list of keys and a default value. Args: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 424bb54378..e4f0d66ef7 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -499,7 +499,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._entries = other._entries @staticmethod - fn fromkeys(keys: List[K], value: V) -> Self: + fn fromkeys(keys: List[K, *_], value: V) -> Self: """Create a new dictionary with keys from list and values set to value. Args: @@ -516,7 +516,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( @staticmethod fn fromkeys( - keys: List[K], value: Optional[V] = None + keys: List[K, *_], value: Optional[V] = None ) -> Dict[K, Optional[V]]: """Create a new dictionary with keys from list and values set to value. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index deaea46174..655c0da9c1 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -220,7 +220,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( @always_inline fn __eq__[ U: EqualityComparableCollectionElement, // - ](self: List[U], other: List[U]) -> Bool: + ](self: List[U, *_], other: List[U, *_]) -> Bool: """Checks if two lists are equal. Examples: @@ -252,7 +252,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( @always_inline fn __ne__[ U: EqualityComparableCollectionElement, // - ](self: List[U], other: List[U]) -> Bool: + ](self: List[U, *_], other: List[U, *_]) -> Bool: """Checks if two lists are not equal. Examples: @@ -277,7 +277,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn __contains__[ U: EqualityComparableCollectionElement, // - ](self: List[U], value: U) -> Bool: + ](self: List[U, *_], value: U) -> Bool: """Verify if a given value is present in the list. ```mojo @@ -385,7 +385,9 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return len(self) > 0 @no_inline - fn __str__[U: RepresentableCollectionElement, //](self: List[U]) -> String: + fn __str__[ + U: RepresentableCollectionElement, // + ](self: List[U, *_]) -> String: """Returns a string representation of a `List`. Note that since we can't condition methods on a trait yet, @@ -416,7 +418,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( @no_inline fn format_to[ U: RepresentableCollectionElement, // - ](self: List[U], inout writer: Formatter): + ](self: List[U, *_], inout writer: Formatter): """Write `my_list.__str__()` to a `Formatter`. Parameters: @@ -433,7 +435,9 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( writer.write("]") @no_inline - fn __repr__[U: RepresentableCollectionElement, //](self: List[U]) -> String: + fn __repr__[ + U: RepresentableCollectionElement, // + ](self: List[U, *_]) -> String: """Returns a string representation of a `List`. Note that since we can't condition methods on a trait yet, @@ -676,7 +680,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn index[ C: EqualityComparableCollectionElement, // ]( - ref [_]self: List[C], + ref [_]self: List[C, *_], value: C, start: Int = 0, stop: Optional[Int] = None, @@ -850,7 +854,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn count[ T: EqualityComparableCollectionElement, // - ](self: List[T], value: T) -> Int: + ](self: List[T, *_], value: T) -> Int: """Counts the number of occurrences of a value in the list. Note that since we can't condition methods on a trait yet, the way to call this method is a bit special. Here is an example below. diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 64fca39d4a..7a10537637 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -74,7 +74,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for e in elements: self.add(e[]) - fn __init__(inout self, elements: List[T]): + fn __init__(inout self, elements: List[T, *_]): """Construct a set from a List of elements. Args: diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 7207bc73c6..eb6cf794fd 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1274,7 +1274,7 @@ struct String( _ = is_first return result - fn join[T: StringableCollectionElement](self, elems: List[T]) -> String: + fn join[T: StringableCollectionElement](self, elems: List[T, *_]) -> String: """Joins string elements using the current string as a delimiter. Parameters: diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index e4e63919b3..e278344174 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1100,7 +1100,7 @@ fn iota[ buff.store(i, i + offset) -fn iota[type: DType, //](inout v: List[Scalar[type]], offset: Int = 0): +fn iota[type: DType, //](inout v: List[Scalar[type], *_], offset: Int = 0): """Fill a list with consecutive numbers starting from the specified offset. Parameters: @@ -1113,7 +1113,7 @@ fn iota[type: DType, //](inout v: List[Scalar[type]], offset: Int = 0): iota(v.data, len(v), offset) -fn iota(inout v: List[Int], offset: Int = 0): +fn iota(inout v: List[Int, *_], offset: Int = 0): """Fill a list with consecutive numbers starting from the specified offset. Args: @@ -2056,7 +2056,7 @@ fn gcd(s: Span[Int], /) -> Int: @always_inline -fn gcd(l: List[Int], /) -> Int: +fn gcd(l: List[Int, *_], /) -> Int: """Computes the greatest common divisor of a list of integers. Args: @@ -2128,7 +2128,7 @@ fn lcm(s: Span[Int], /) -> Int: @always_inline -fn lcm(l: List[Int], /) -> Int: +fn lcm(l: List[Int, *_], /) -> Int: """Computes the least common multiple of a list of integers. Args: diff --git a/stdlib/src/math/polynomial.mojo b/stdlib/src/math/polynomial.mojo index 8f2e27f584..8397218380 100644 --- a/stdlib/src/math/polynomial.mojo +++ b/stdlib/src/math/polynomial.mojo @@ -30,7 +30,7 @@ from collections import List fn polynomial_evaluate[ dtype: DType, simd_width: Int, //, - coefficients: List[SIMD[dtype, simd_width]], + coefficients: List[SIMD[dtype, simd_width], *_], ](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: """Evaluates the polynomial. @@ -58,7 +58,7 @@ fn polynomial_evaluate[ fn _horner_evaluate[ dtype: DType, simd_width: Int, //, - coefficients: List[SIMD[dtype, simd_width]], + coefficients: List[SIMD[dtype, simd_width], *_], ](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]: """Evaluates the polynomial using the passed in value and the specified coefficients using the Horner scheme. The Horner scheme evaluates the diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index a03a2614cd..dfc33ee8f6 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -113,7 +113,7 @@ struct Span[ self._len = other._len @always_inline - fn __init__(inout self, ref [lifetime]list: List[T]): + fn __init__(inout self, ref [lifetime]list: List[T, *_]): """Construct a Span from a List. Args: From 285d8d57f00e772292f9af266ffb39bff9cd3c51 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:27:15 -0400 Subject: [PATCH 1395/2019] [stdlib] Remove `{size,simdwidth,bitwidth,align}of` from prelude MODULAR_ORIG_COMMIT_REV_ID: bbbd03fb1f23e9a5dcdda6d2f938b969446df607 --- docs/changelog.md | 3 ++- examples/mandelbrot.mojo | 2 +- examples/matmul.mojo | 1 + examples/notebooks/Mandelbrot.ipynb | 2 +- examples/notebooks/Matmul.ipynb | 1 + stdlib/benchmarks/algorithm/bench_elementwise.mojo | 1 + stdlib/benchmarks/algorithm/bench_vectorize.mojo | 1 + stdlib/benchmarks/utils/bench_formatter.mojo | 1 + stdlib/benchmarks/utils/bench_memmem.mojo | 1 + stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/hash.mojo | 1 + stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 ++ stdlib/src/collections/list.mojo | 1 + stdlib/src/collections/vector.mojo | 1 + stdlib/src/math/math.mojo | 10 ++++++++-- stdlib/src/memory/memory.mojo | 1 + stdlib/src/prelude/__init__.mojo | 1 - stdlib/src/utils/string_slice.mojo | 2 +- stdlib/src/utils/stringref.mojo | 1 + stdlib/test/builtin/test_dtype.mojo | 1 + stdlib/test/builtin/test_uint.mojo | 1 + stdlib/test/memory/test_memory.mojo | 2 +- 24 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 028040c8bf..21cb9f716b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -635,7 +635,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD.load/store` are moved to `UnsafePointer`. -- `external_call` and `abort` are removed from prelude. +- `sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` are + removed from prelude. ### ❌ Removed diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 7a5e48634b..f14c45a620 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -14,7 +14,7 @@ # RUN: %mojo %s | FileCheck %s from math import iota -from sys import num_physical_cores +from sys import num_physical_cores, simdwidthof import benchmark from algorithm import parallelize, vectorize diff --git a/examples/matmul.mojo b/examples/matmul.mojo index d1b8623abd..f8c39bf21b 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -18,6 +18,7 @@ from os.env import getenv from random import rand from sys import info +from sys import simdwidthof import benchmark from algorithm import Static2DTileUnitFunc as Tile2DFunc diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index c8603c1482..834198f7a9 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -49,7 +49,7 @@ "#|code-fold: true\n", "import benchmark\n", "from math import iota\n", - "from sys import num_physical_cores\n", + "from sys import num_physical_cores, simdwidthof\n", "from algorithm import parallelize, vectorize\n", "from complex import ComplexFloat64, ComplexSIMD\n", "from python import Python\n", diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index 9a1bf2f4ac..a34ca9444e 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -582,6 +582,7 @@ }, "outputs": [], "source": [ + "from sys import simdwidthof\n", "# simdwidthof = number of float32 elements that fit into a single SIMD register\n", "# using a 2x multiplier allows some SIMD operations to run in the same cycle\n", "alias nelts = simdwidthof[DType.float32]() * 2\n", diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index f3d5250a20..ce05560301 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -t +from sys import simdwidthof from algorithm import elementwise from benchmark import Bench, BenchConfig, Bencher, BenchId from buffer import Buffer diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 20c8ee0966..6e11c09f77 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -20,6 +20,7 @@ # CHECK: Benchmark results from random import rand +from sys import simdwidthof from algorithm import vectorize from benchmark import ( diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index e41abf84f5..528532475d 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s -t +from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 3e7ca664c9..fd55d900f7 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s -t +from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index ca6fd3c0cc..3142b3f5c3 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -16,7 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import KeyElement -from sys import sizeof as _sizeof, bitwidthof, os_is_windows +from sys import sizeof, bitwidthof, os_is_windows alias _mIsSigned = UInt8(1) alias _mIsInteger = UInt8(1 << 7) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 4c9e727688..9812896cc0 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -32,7 +32,7 @@ with open("my_file.txt", "r") as f: """ from os import PathLike -from sys import external_call +from sys import external_call, sizeof from utils import Span from memory import AddressSpace, UnsafePointer diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 4494d09a2d..1d1daf45cb 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -27,6 +27,7 @@ There are a few main tools in this module: import random from sys.ffi import _get_global +from sys import simdwidthof, bitwidthof from collections import InlineArray from builtin.dtype import _uint_type_of_width diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 450d5f83c1..35ceaec387 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -28,7 +28,7 @@ from collections.string import ( from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select -from sys import triple_is_nvidia_cuda +from sys import triple_is_nvidia_cuda, bitwidthof # ===----------------------------------------------------------------------=== # # Indexer diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f29e53ad59..7025ccbf0f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -25,8 +25,10 @@ from sys import ( prefetch, simdwidthof, triple_is_nvidia_cuda, + bitwidthof, ) from sys.info import _current_arch + from sys._assembly import inlined_assembly from os import abort diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 655c0da9c1..9ef826e7ae 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -21,6 +21,7 @@ from collections import List from sys.intrinsics import _type_is_eq +from sys import sizeof from os import abort from memory import Reference, UnsafePointer, memcpy from utils import Span diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 6e81b1b012..edf4768521 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -20,6 +20,7 @@ from collections.vector import InlinedFixedVector """ from memory import Reference, UnsafePointer +from sys import sizeof from utils import StaticTuple diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index e278344174..a90a1528f6 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -20,10 +20,16 @@ from math import floor """ from collections import List -from sys import llvm_intrinsic from sys._assembly import inlined_assembly from sys.ffi import _external_call_const -from sys.info import bitwidthof, has_avx512f, simdwidthof, triple_is_nvidia_cuda +from sys import ( + llvm_intrinsic, + bitwidthof, + has_avx512f, + simdwidthof, + triple_is_nvidia_cuda, + sizeof, +) from memory import UnsafePointer diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index df8f6f9e96..6732a1f889 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -26,6 +26,7 @@ from sys import ( sizeof, triple_is_nvidia_cuda, external_call, + simdwidthof, ) from builtin.dtype import _integral_type_of diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 42b2ba8c4d..4adfb15fc8 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -137,5 +137,4 @@ from builtin._documentation import doc_private from utils._visualizers import lldb_formatter_wrapping_type # Load-bearing ones to remove -from sys import alignof, sizeof, bitwidthof, simdwidthof from memory import bitcast diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 3d7d25a1d8..3c5171529e 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -24,7 +24,7 @@ from utils import Span from collections.string import _isspace from collections import List from memory import memcmp -from sys import simdwidthof +from sys import simdwidthof, bitwidthof alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 760edc86f4..d6416a0bf7 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -20,6 +20,7 @@ from memory import UnsafePointer, memcmp from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice from sys.ffi import C_char +from sys import simdwidthof # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index 557b1fc66f..49d509d21c 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -15,6 +15,7 @@ from collections import Set from testing import assert_equal, assert_false, assert_true +from sys import sizeof fn test_equality() raises: diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 2bfb4ab294..45f47c7a22 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from testing import assert_equal, assert_false, assert_not_equal, assert_true +from sys import bitwidthof def test_simple_uint(): diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index d02bff3dfd..60b28f0ed1 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys import sizeof +from sys import sizeof, simdwidthof from memory import ( UnsafePointer, From 2fcb225821ff7018f08d060f0765c28ea0622d45 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Wed, 14 Aug 2024 13:37:37 -0700 Subject: [PATCH 1396/2019] Proposal: Classes in Mojo A frequent refrain about Mojo's semantics differing from Python's is that "Mojo doesn't yet have classes", which will behave more like Python types. It is _not_ necessarily a goal of Mojo to precisely implement Python classes; however, in order to decide how Mojo classes should work, it's critical to at least understand how Python classes work, so we can make informed choices about tradeoffs when we choose to differ. This whitepaper attempts to be a path marker in that direction. It is split into two halves with different goals: 1. Fully describe the Python object model. 2. A set of proposals for Mojo to implement classes. MODULAR_ORIG_COMMIT_REV_ID: 84044e22af668e78a15bf1ef9310d82230ba1804 --- proposals/mojo-classes.md | 907 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 907 insertions(+) create mode 100644 proposals/mojo-classes.md diff --git a/proposals/mojo-classes.md b/proposals/mojo-classes.md new file mode 100644 index 0000000000..a4a8fa3551 --- /dev/null +++ b/proposals/mojo-classes.md @@ -0,0 +1,907 @@ +# Classes in Mojo + +**Author: Stef Lindall**\ +**Status: Draft**\ +**Date: August 14, 2024**\ +**Last updated: August 14, 2024**\ + +A frequent refrain about Mojo's semantics differing from Python's is that +"Mojo doesn't yet have classes", which will behave more like Python types. +It is _not_ necessarily a goal of Mojo to precisely implement Python classes; +however, in order to decide how Mojo classes should work, it's critical to at +least understand how Python classes work, so we can make informed choices about +tradeoffs when we choose to differ. + +This whitepaper attempts to be a path marker in that direction. It is split into +two halves with different goals: + +1. Fully describe the Python object model. + + This should be entirely objective, with the goal that if we implemented the + described behavior in Mojo, it would pass 100% of the relevant CPython + conformance test suite. If you know of edge cases where it is inaccurate, + please raise them. + +2. A set of proposals for Mojo to implement classes. + + This will naturally include some degree of my own perspective. The goal + isn't to be objective, but to try to identify the critical vs incidental + complexity in the existing Python object model, judged by its relative + usefulness in the broader Python ecosystem, and attempt to measure tradeoffs + in implementing the behavior in Mojo vs Mojo's own language goals. + +This document is _not_ a full summary of the Python Data Model defined here: +. In particular it 1) +attempts to omit things which are part of the CPython implementation but not +standard (though this is a pretty soft boundary in practice) and 2) does not +make any attempt to exhaustively list the set of `__magic__` dunder attributes +recognized by Python, especially as they relate to specific protocols not +central to the object model (for example, iteration or numerics). + +## The Python object model in detail + +### Scope -- What is the Python object model? + +Concretely, the object model defines: + +- For an object, what is its type? +- How are new types defined? +- When I access an attribute on an object, what happens? What is the type of + that expression? + +I will attempt to aggressively ignore any behavior which does not directly +serve answering these questions. + +### Glossary + +You probably know these terms already, but to make sure we're saying precisely +the same thing, they're defined here. + +- `object`: The base type of all objects in Python, and a name used to describe +instances of python objects. + - Internally it's represented as a pair: + - `__class__: type` + - `__dict__: dict[str, object]` + - (this is different for objects defining `__slots__`, but conceptually + still true) + - Informally, instances are often named `obj` in code to avoid a name + conflict with `object` + - `isinstance(x, object)` for all `x` in Python (including `object`) +- instance: A synonym for an object +- `type`: A heavily overloaded term here. + 1. An object instance describing a type + 2. A function which returns an object's `__class__` + 3. The `type` metatype: The following are all true. + - `type is type(object)` + - `type is type(type)` + - `isinstance(type, object)` + - `isinstance(object, type)` + - `type.__mro__ == (type, object)` + - Informally, instances are often named `cls` in code to avoid a name conflict + with `type` (`class` is a keyword) +- `attribute`: A named member of an `instance` + - Accessed by the syntax `instance.attribute` + - Conceptually, an object's attributes are exactly the members of `__dict__` + - Python provides mechanisms to customize what a type considers to be an attribute +- `__dict__`: The attributes of an object (if it doesn't define `__slots__`) +- `__class__`: An attribute of objects which resolves to the type of that object +- `__bases__`: An attribute of type objects which contains their direct parent types +- `__mro__`: "Method resolution order" -- An attribute of type objects which + contains a totally ordered sequence of all their ancestor types. Python uses + the C3 linearization (see appendix). +- metaclass: A word for `type(type(x))` +- `issubclass(sub, cls)`: A builtin function defined on types. Generally true if + `cls in sub.__mro__`. The object model provides mechanisms to customize this + behavior. +- `isinstance(obj, cls)`: A builtin function. Generally true if + `issubclass(type(obj), cls)`. The object model provides mechanisms to + customize this behavior. +- protocol: An implicit interface or set of behaviors conformed to by a type +- descriptor: An object of some type which implements the "descriptor protocol", + which allows attributes to be lazily evaluated. +- namespace: A `__dict__` without an associated `__class__` +- `locals()`: The locally scoped namespace dictionary +- `globals()`: The module scoped namespace dictionary +- name: A string key in a namespace + - NB: Notice that a name _does not have a type_! +- annotation: A syntactically valid python expression that is never evaluated by + the object model. +- `__annotations__`: A namespace member mapping names to annotation values. + +### Memory model + +All Python objects are reference counted, including compiler builtins like `None`. +Since types are implicitly references, there's not problem defining recursive types: + +```python +class LinkedListNode: + next: Optional[LinkedListNode] # this is fine!! +``` + +Most of the rest of the memory model are CPython implementation details: + +- An object can't be explicitly deleted. + - It will be garbage collected when its reference count goes to 0, or when the + cycle detector detects that it is part of a cycle with no external references. + - When an object is deleted, if it has a `__del__` method, that method _may_ + be called. If it throws, the exception is ignored. +- The only way to explicitly allocate an object is with `object.__new__(cls)`. +- `obj.__weakref__` contains a list of weak references which point at `obj` + +### Object model + +Following is a conceptual implementation of Python's object model, as though it +were implemented directly in Python. It attempts to be accurate, in other words +if we implemented precisely this object model, we would be directly compatible +with Python types. The goal is to provide a shared basis of understanding of +what compatibility looks like, as well as a reference for an eventual implementation. + +#### `object` + +```python +## NOTE: Attribute accesses in this implementation are sometimes fudged +## as direct access, when they couldn't be because of recursion. +class object(metaclass=type): + def __new__(cls): + # Can't be implemented in pure python + # Allocates a new Python object with an empty __dict__, and __class__ = cls + + def __init__(self): + pass + + @property + def __class__(self): + # Isn't implemented in pure python. + # Returns the __class__ from the underlying C object. + + def __dir__(self): + return list(self.__dict__) + + def __getattribute__(self, attr): + for base in type(self).__mro__: + if attr in base.__dict__: + classvar = base.__dict__[attr] + # Resolve data descriptor; See appendix + if hasattr(classvar, ("__set__", "__delete__")): + return classvar.__get__(self, base) + # Found non-data-descriptor + break + else: # No classvar defined, do instance lookup + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError + # resolve non-data descriptor + if hasattr(classvar, "__get__"): + return classvar.__get__(self, base) + return classvar + + def __setattr__(self, attr, value): + for base in type(self).__mro__: + if (value := base.__dict__.get(attr)) and hasattr(value, "__set__"): + value.__set__(self, value) + return + else: + self.__dict__[attr] = value + + def __delattr__(self, attr): + for base in type(self).__mro__: + if (value := base.__dict__.get(attr)) and hasattr(value, "__delete__"): + value.__delete__(self) + return + if attr in self.__dict__: + del self.__dict__[attr] + else: + raise AttributeError + + # Other methods related to str/hash/pickling/comparison/etc omitted + ... +``` + +#### Attribute access + +Attribute access `instance.attribute` is syntax sugar for +`getattr(instance, "attribute")`, with the following implementation: + +```python +def getattr(obj, attr, default=MISSING): + # Try resolving through __getattribute__ + try: + return obj.__getattribute__(attr) + except AttributeError as e: + exc = e + + # Try resolving through __getattr__ + try: + if hasattr(obj, "__getattr__"): + return obj.__getattr__(attr) + except AttributeError as e: + exc = e + + # Nothing found, return default or raise + if default is not MISSING: + return default + raise exc + +def setattr(obj, attr, value): + obj.__setattr__(attr, value) +``` + +#### Methods + +Functions in python implement the descriptor protocol. The `function` can be +thought of like + +```python +class function: + def __get__(self, instance, owner=None): + if instance is None: + return self + return method(self, instance) + + def __call__(self, *args, **kwargs): + # execute the function +``` + +allowing them to be used as free functions, except when accessed as an attribute +of an instance, in which case they become a bound method; conceptually: + +```python +@dataclass +class method: + __func__: function + __self__: object + + def __call__(self, *args, **kwargs): + self.__func__(self.__self__, *args, **kwargs) +``` + +`@property`, `@staticmethod` and `@classmethod` decorators don't require any +special behavior, and may be implemented in pure python as descriptors + +#### `type` + +`type` is a function which returns an object's type: + +```python +## `type` has this special overload +def type(obj): + return obj.__class__ +``` + +`type` is also the base metatype. + +`type` is primarily responsible for defining how inheritance works. Python uses +the C3 type linearization algorithm by default to construct a type's method +resolution order. + +Beyond that, `type` may be subclassed to create different metaclasses, with +customized behavior for new type creation, namespaces, or customizing +`isinstance` and `issubclass` behavior (particularly useful for things like `Protocol`.) + +Conceptually, `type` could be implemented as + +```python +class type: + def __init__(self, name, bases, namespace, **kwargs): + self.__name__ = name + # See appendix for implementation. In ~all python code this does + # resolve_bases is a no-op, but it supports inheriting from non-types. + self.__bases__ = resolve_bases(bases) + self.__mro__ = self.mro(bases) + for name, attr in namespace.items(): + setattr(self, name, attr) + + # Set descriptor names + if hasattr(attr, ("__get__", "__set__", "__delete__")) and hasattr(attr, "__set_name__"): + attr.__set_name__(self, name) + + self.__init_subclass__(**kwargs) + + def __init_subclass__(self, **kwargs): + pass + + def mro(self): + # See appendix for implementation of C3 mro + return [self] + c3_mro(self.__bases__) + + @property + def __dict__(self): + # Not implementable in pure python. This is where + # all accesses of `__dict__` bottom out. + + @classmethod + def __prepare__(cls, **kwargs): + return {} + + def __call__(self, *args, **kwargs): + # This is the real implementation of what we more + # naturally think of as a "constructor" call + new_instance = self.__new__(*args, **kwargs) + new_instance.__init__(*args, **kwargs) + return new_instance + + # NOTE: Attribute access here fudged, the true implementation + # won't recursively call `__getattribute__. + def __getattribute__(self, attr): + # __getattribute__ has different behavior for types: + # - descriptor doesn't pass an `instance` + # - iterate self.__mro__ instead of type(self).__mro__ + for base in self.__mro__: + if attr in base.__dict__: + value = base.__dict__[attr] + if hasattr(value, "__get__"): + value = value.__get__(None, self) + return value + else: + raise AttributeError + + def __instancecheck__(self, instance): + # Called by `isinstance` + return issubclass(type(instance), self) + + def __subclasscheck__(self, subclass): + # Called by `issubclass` + return self in subclass.__mro__ + + def __subclasshook__(self, subclass): + return NotImplemented + + def __subclasses__(self): + # returns the list of types T having self in T.__mro__ + ... + + # This seems to be vestigial + @property + def __base__(self): + return None if self is object else self.__bases__[0] + + # Other methods like str/repr omitted + ... +``` + +#### Class creation + +```python +class NewType(Base1, Base2, metaclass=type, **kwargs): + +``` + +is syntactic sugar for + +```python +## Set up the class namespace +qualname = locals().get("__qualname__") +namespace = metaclass.__prepare__(**kwargs) +namespace["__module__"] = locals().get("__module__") +namespace["__qualname__"] = f"{qualname}.NewType" if qualname else "NewType" +namespace["__annotations__"] = {} + +## See appendix for min_type implementation +metaclass = min_type({metaclass, *(type(base) for base in bases)}) + +## Execute the class body to populate the remainder of the namespace +exec(compile(body), globals=globals(), locals=namespace) + +## Actually create the type +NewType = metaclass(name="NewType", bases=(Base1, Base2), dict=namespace, **kwargs) +``` + +### Descriptor protocol + +Among protocols, the descriptor protocol is undoubtedly the one most central to +Python's object model, evidenced by its heavy use in the above implementation. + +There isn't a formal protocol definition in the implementation, but it would look +like this: + +```python +class Get(Protocol): + def __get__(self, obj, cls=None): ... + +class Set(Protocol): + def __set__(self, obj, value): ... + +class Delete(Protocol): + def __delete__(self, obj): ... + +NonDataDescriptor = Get +DataDescriptor = Set | Delete +Descriptor = DataDescriptor | NonDataDescriptor +``` + +Data descriptors and non-data descriptors differ in that a data descriptor takes +precedence over an instance attribute (one in `__dict__`) while a non-data +descriptor does not. + +Descriptors may also implement `__set_name__(cls, name)`, which is called during +type creation. + +### Generics + +Python has two syntaxes for defining type parameters. It has a mojo-like type +parameter syntax, introduced in [PEP 695](https://peps.python.org/pep-0695/), +as well as `typing.TypeVar` and `typing.Generic`. + +#### Type parameters + +Using the type parameter syntax is syntactic sugar for defining `TypeVar`s and +inheriting from `Generic`, but is also allowed on functions and methods. + +These look approximately like Mojo's parameters: + +```python +class ClassA[T: str]: + def method1(self) -> T: + ... + +def func[T](a: T, b: T) -> T: + ... + +type ListOrSet[T] = list[T] | set[T] +``` + +- `type` implements `__getitem__` where `type[Any] == type`, and `type[T]` + represents `type(T)` in a generic context. +- `type` implements `__or__`, which allows type unions to be defined as `X | Y`. +- `__origin__` on a parameterized type holds the un-parameterized generic type +- `__type_params__` holds a tuple of type parameters of a generic type or function + - Though not specified, `__parameters__` appears to alias `__type_params__` +- `__args__` holds the values of the type parameters of a parameterized generic + type or function +- `__value__` is in the spec as holding the "resolved" type but doesn't appear + to be implemented in CPython +- There appears to be no way to retrieve the annotations of type parameters + +> [!IMPORTANT] +> `T: str` in Python is a _covariance_ declaration, whereas Mojo currently defines +> this as a _type_ declaration. In other words, in Python `class ClassA[T: str]:` +> means that `issubclass(T, str)`, while in Mojo this would currently mean +>`isinstance(T, str)`. This represents an important conflict to resolve in implementation. + +#### `typing.Generic` and `typing.TypeVar` + +`Generic` is of type `GenericMeta`, which implements the machinery for generic +types, such as `__class_getitem__`. This may be seen as a historical artifact, +as `type` itself is now subscriptable to support generic typing. +[PEP 560](https://peps.python.org/pep-0560/) explicitly identifies removing the +dependency on `GenericMeta` as a motivation for the development of +`__class_getitem__` and `__mro_entries__`. + +`Generic` types + +### `typing.Protocol` + +`Protocol` is a type which can be subclassed to provide a Mojo-trait-like type: +any type with the provided interface will type-check as that type in type +checkers like Pylance and MyPy, and with `isinstance` if the type is +additionally annotated with `@runtime_checkable`. + +[MyPy's Protocols and structural subtyping](https://mypy.readthedocs.io/en/stable/protocols.html) +docs have many good examples. Protocols are the most literal way to type check +Python's "duck typing" standard: types are never required to inherit from a +protocol (and generally shouldn't; while Protocols can implement default method +implementations, duck-typed "subclasses" don't inherit them, and `Protocol` +has its own metaclass, so this is all-around better achieved with a mixin type). + +### Abstract base classes + +ABCs were introduced alongside metaclasses in 2007 in Python 3.0. Python type +hints were introduced 7 years later, and `typing.Protocol` was released in 2017. +You can see ABCs as a specific set of predefined protocols, along with mixin +types. In fact, many of them are named similarly to Mojo traits: `Hashable`, +`Sized`, etc. + +Of particular interest are the `Sequence`, `Mapping`, and `Set` types, which +provide mixin methods to define types implementing these protocols more easily. + +ABC also defines the `@abstractmethod` and related decorators, which more +directly map to a traditional OO model. Types inhereting classes annotated with +these methods must either declare themselves to also be abstract, or implement +the required methods, otherwise they will raise during class creation. + +### Metaclass conflicts + +Python needs to choose a single type to use as a metaclass when creating a new +type. It does this in a sensible way: among all candidate metaclasses +(metaclasses used by bases, and any specified metaclass), it tries to use the +most derived one, and if there isn't a unique such type, it errors. + +This is a sensible behavior, but it means that inheriting from a metaclass is a +_very_ expensive thing to do for a library: if someone uses your types, they +can't use _any_ other library which also uses metaclasses. + +This is a particular pain point with `typing.GenericMeta` and +`collections.abc.ABCMeta`. Since these types are implementing typing for +"normal" python types, they (1) can't be used together, and (2) can't be used +with any libraries which use mataclasses, for instance `enum` or most ORMs. + +This is in fact why `@dataclass` is implemented as a decorator, when a metaclass +would have been a much more natural fit. See +[PEP 557](https://peps.python.org/pep-0557/) for more details, which +specifically identifies dataclasses as a natural application of metaclasses, and +_not_ having a metaclass being a central decision in the design. + +### `__slots__` + +There's a separate opt-in object model pattern if a class defines +a `__slots__` class variable, eg. + +```python +class Foo: + __slots__ = ["bar", "baz"] +``` + +I'll link the [docs here](https://docs.python.org/3/reference/datamodel.html#slots) +on how slots behave, since it goes a bit outside the scope of this document. +Critically these objects lack a `__dict__`, and have a number of restrictions on +usage, including not being allowed to assign arbitrary attributes to instances, +and strong limitations on subclassing. This comes with reduced memory usage and +improvements in attribute access time. + +Since types with `__slots__` behave _almost_ exactly like objects without them, +other than restrictions, and could be implemented with an appropriate metaclass +in the existing object model, they don't require substantial support and I won't +cover them more deeply here. + +### Python Crimes + +There's many behaviors supported in the Python object model that the community +has widely agreed are bad practice. These have vanishingly small usage in +practice, and the language provides other usage patterns to accomplish most +things they might be useful for. + +#### Assigning to `__class__` + +- This is the only way to mutably change the type of an object. +- In practice I've only used this extremely rarely, and in retrospect never for + a good reason. +- It's always possible to instead update a reference to point to a shallow or + deep copy as a new type instead. The only semantic difference is that it + doesn't update other referrers. +- Conceivably it might be useful for certain hot-reloading patterns, updating + references in the interpreter in-place to a new instance of the type. PyPy + has experimented with this but the common Python hot-reloaders don't + actually do it. +- This has a storied history; PyPy has already removed this behavior, and + Python 3 raises a TypeError for certain cases of this. + +#### Assigning to `__bases__` or `__mro__` + +- I've never actually seen this done or done this. +- Again this falls into the category of "plausibly you might do this for + hot-reloading, in practice no one does". + +#### Assigning to `__dict__` + +- This is a surefire way to burn yourself. I don't know of any widely used + patterns that do this or might want to. + +#### Assigning arbitrary attributes to `__dict__` + +- This is obviously supported through `__getattr__` et al, but it's additionally + just directly supported by all types not implemented with `__slots__`. +- In some sense this is the "expected" behavior, thinking of Python as a + scripting language: when calling `__init__`, a priori we don't know what + attributes a class _should_ have, so we can't place expectations on those attributes. +- There's a wide tail of other use cases for this behavior, such as + - Decorators frequently assign a new attribute to a function or type to "tag" + it with metadata. This is a very fragile pattern I'm not a fan of, but is + relatively widely used. + - Quick and dirty caching: + `def x(self): return self._x if hasattr(self, "_x") else (self._x := _compute_x())` +- I'm convinced if this pattern didn't exist, everything it's used for could + ~easily be done another way, however I am also prepared to be surprised by + how much code actually does this. + +#### Inheriting from objects that aren't types + +- There's some support in the language for this through `__mro_entries__`. + [PEP 560](https://peps.python.org/pep-0560/) discusses this. It is primarily + useful because of metaclass conflicts, which became more common with the + addition of generic types into python typing depending on `GenericMeta`. +- I doubt this is widely used, but I don't have data to back that up. It's + likely we could design for generic types more directly from the start to + avoid this issue (though harder to avoid the metaclass conflict issue more generally). + +#### Types which provide `__bases__` or `__mro__` dynamically via a descriptor or `__getattr__` + +- I've never heard of anyone doing this but it is clearly chaotic evil. +- I bet you could easily make this mechanism alone turing complete so it + undoubtedly has amazing use cases, and I also am confident this will never + be practiced. You could certainly implement an approximation of this many + other ways. + +#### Dynamic inheritance: `class Foo(Bar if x else Baz):` + +This looks insane but is actually really useful in practice. For instance + +- A CSV-loader interface might want to read the CSV header, and then construct a + new row type which inherits from `collections.namedtuple(headers)`. +- Similarly, Pandas `Dataframe` has dynamic attributes for the name of each + column in the dataset, which are data-dependent. +- A core use case for class decorators is to return a new subclass of the + decorated class. +- It's sometimes useful to create an "anonymous" subclass of a type, created in + some function scope. + +#### Full `exec` dynamism: `exec("class Foo: ...")` + +- `collections.namedtuple` used to be implemented this way +- I believe that the modern object model is expressive enough that there aren't + any regular use cases of this anymore. + +#### [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) violations + +Python subtypes don't prevent you from overridding parent type attributes with +incompatible attributes that would make them illegal to pass as a parent type. +In practice there's lots of code that accidentally does this, although MyPy +recognizes it as a typing error. + +## Proposals to implement Mojo classes + +This section has considerably more of a perspective than the previous. The goal +is to provide a concrete proposal for how Mojo could move forward implementing +classes which is faithful both to Python's object model and to Mojo's own +language goals. + +### Types as types vs types as functions + +Python may be thought of as a strongly typed, dynamically type functional language. +Objects have types, names do not. Types may never be converted into other types +(except the specific "Python Crime" of assigning to `__class__`). While it appears +imperative on the surface, all python types are both objects and functions, and +the "constructor" of a type in fact doesn't even need to do that; for instance, +`__new__` may be overloaded to return singleton instances or interned objects. +Instead, it's often more useful to think of types as a function which transforms +a value into a value of another type. While this perspective is pretty far from +what we normally think of as object-oriented programming, it makes a lot of the +more common "python crimes" listed below make more sense. + +By contrast, Mojo is statically, weakly typed. Names have types, and only objects +of those types may be associated with that name. Mojo allows implicit conversion +of types into other types. Mojo constructors are exactly that in the traditional +sense: they allocate and initialize memory. + +This contrast is in some sense the deepest disparity between Python and Mojo, +and in order to be successful in its goals, Mojo classes will need to fully +embrace Python's perspective on these topics. + +### High level perspective: Mojo's goals + +The Python 3 language spec is a moving target. Many attempts at alternative +language implementations exist, but as CPython is co-developed with the Python +spec, they are necessarily approximations. + +Mojo also has its own goals. It wants to be a compiled, performant, full-stack +language, while building a healthy Pythonic-language ecosystem of libraries +shared between it and CPython. + +Mojo and Python in this sense have a lot of shared goals. We should, at a +minimum aim for most Python libraries to "just work" on both Mojo and CPython. +However, realistically around the edges and in uncommon cases, we will need to +make separate decisions about precise implementation details, and Mojo can't +realastically pass the entire CPython compatibility test suite. + +We should identify some _subset_ of it that we intend to pass, make this a clear +contract with our users, work closely with CPython in spec development in these +areas, and make it easy for users and library authors to write code which is +compatible with both. The _natural_ way to write Python and Mojo should be the +same on this set of language features. The vibe should be "you're using Python? +your use case will almost certainly _just work_ on Mojo", possibly with a small +set of compatibility changes that will continue to allow it to work in Python, +and that we help automate (think python's `six` library). + +The truth is that there are decisions in Python that we should realistically +consider _not_ carrying forward; warts which exist for historical reasons but +are too much work to rip out, and that a vanishingly small percent of the Python +ecosystem relies on. + +### Proposed differences for Mojo's object model + +It would be incredibly valuable to Mojo, as a compiled language, to be able to +reason more about the set of possible things that may be done with an object. +Identifying tradeoffs of where simplifying the object model allows us a better +implementation, vs the cost in compatibility and expressiveness, is the core +job of this proposal. + +I'm framing these tradeoffs primarily with the intuition that moving attribute +and method resolution to compile time where possible is the most valuable +benefit, and that we shouldn't change what isn't broken except in service of +this goal. + +Following is an initial proposal of a Mojo object model for classes, which +retains most of the dynamic use cases of Python objects, while allowing +hopefully the vast majority of existing cases to "just work" with compile-time +method and attribute resolution. + +#### Any Mojo type which is a `class` is implicitly wrapped by an `Arc` + +Existing Mojo `fn` and `def` semantics are unchanged: + +- If an argument is a `struct`, it retains its current behavior +- If an argument is a `class`, it will behave as an owned reference, and that + reference will be reference-counted + - Attribute lookup behaves as it would for a direct reference + - It is implicitly convertible to an `Arc[Object]` for lower level access + +#### Implement Python's object model for classes, with compile-time method resolution + +`__getattribute__` and `__getattr__` may still be used to implement dynamic +attributes, but in the common case attributes are resolved statically. + +- `__dict__` may not be assigned to directly + - For compatibility, it may be accessed, but these function as the variants of + `setattr` and `getattr` described below. +- `__class__`, `__bases__`, and `__mro__` are immutable and may not be assigned to +- Class attributes must have a static type + - All attributes declared in the class body. + - Classes inherit the attributes of all types in their `__mro__`. + - If two types in the `__mro__` share an attribute name, they must either be + the same type, or must be linear ancestors of each other, in which case they + take the most derived type. +- Classes are separated into implementation categories: + - Type 1: Classes which define neither `__getattr__` nor `__getattribute__` + in their `__mro__` + - Assigning to or accessing an undefined attribute is a compile error. + - `__setattr__` may be defined, but will raise an `AttributeError` if called + with any unknown attribute. + - `__delattr__` may not be defined + - Type 2: Classes which define `__getattr__` but not `__getattribute__`. + - Behave like Type 1 classes, except where the compiler would fail td + resolve an attribute lookup, it instead emits a call to `__getattr__`. + - `__delattr__` may be defined, but will raise a `TypeError` if called witd + an attribute that resolves via Type 1 attribute lookup. + - `__setattr__` may be defined with no restrictions + - Type 3: Classes which define `__getattribute__`. + - Any attribute access on these types will emit a call to `__getattribute__`. + - `__delattr__` may be defined in the same way as Type 2 classes. + - `__setattr__` may be defined with no restrictions +- We have a variant of the `getattr`, `hasattr`, `setattr`, and `delattr` + functions which dynamically attempt to perform a Type 1 lookup on the object + using its runtime type, or raise an`AttributeError`. +- Dynamic inheritance is allowed, but any type variable used as a base class + must be specified as a [type parameter](https://docs.python.org/3/reference/compound_stmts.html#type-params) + and the compiler must be able to infer its concrete type at compile time. + +#### More invasive or opinionated changes + +##### Simply the typing model around type parameters and Protocols + +- Remove Abstract Base Classes and decorators. Re-implement ABC types as Protocols. +- Remove `typing.Generic`, only support type parameters. +- A class inheriting from Protocol is treated exactly as a trait definition. +- All traits and protocols behave as though they are annotated with `@runtime_checkable` + +##### Require types to obey the Liskov substitution principle + +- Attributes and method return types in child classes must be covariant to the + same in their parent classes. + - Attributes may be replaced with descriptors satisfying this princple. +- Method arguments must be contravariant to the same in their parent classes. + - They may define additional optional arguments. + - They may make previously required arguments into default arguments. + - Since all functions in Python may raise Exception, any new raises are always + legal. + +##### Allow method overloading + +- All members of a method overload set must be defined in the class body. +- If a subclass overrides an overloaded method, it forms a new overload set. + - A subclass overriding only some overloads of a method will implicitly have + additional overloads in its overload set calling super() methods. + +##### Simplify descriptor semantics + +Eliminate the distinction between non-data and data descriptors. Define a +descriptor as having a `__get__` with the correct interface. Descriptors follow +the same precedence rules as other name lookups. + +##### Help avoid common metaclass conflicts + +- If metaclasses are not linearizable when creating a new type, attempt to +create an anonymous subclass of conflicting metaclasses. + +### Open questions + +- How do we resolve the difference in semantics of Python type parameters + defining covariance vs Mojo type parameters defining type? +- Python uses magic attributes for type parameters, while Mojo treats them as + type attributes. What's the best way to resolve this in implementation? +- Mojo's type system should support some degree of higher-kinded types compared + to Python. Is that in scope for this proposal? + +## Appendix + +### Out-of-line reference implementations + +Find the unique most-derived type among input types, or `TypeError`. + +#### min_type + +```python +def min_type(types): + assert types + min_type = types.pop() + for type in types: + if issubclass(type, min_type): + min_type = type + elif not issubclass(min_type, type): + # Inherited metaclasses must be totally orderable + raise TypeError + return min_type +``` + +#### C3 Method resolution ordering + +This looks a bit complicated, but it's not actually too bad. See the +[MRO documentation](https://docs.python.org/3/howto/mro.html) and +[C3 linearization paper](https://dl.acm.org/doi/10.1145/236337.236343) for more details. + +The Method Resolution Order is a "linearization" of +the ancestor classes of `cls`, which has the property +of being "monotonic": For any types `P` and `Q` in `Base.__mro__` +where `P` appears before `Q`, then for all types `Child` having `Base` +in `Child.__mro__`, `P` must also appear before `Q` in `Child.__mro__`. + +This naive implementation of the C3 algorithm is O(N^2) +for simplicity. + +```python +def c3_mro(bases): + linearization = [] + mros = [list(base.__mro__) for base in bases] + # We reduce the total number of elements in `mros` + # by at least 1 per iteration, so time is bounded. + while mros: + # Search fo a good head + for mro in mros: + head = mro[0] + for other_mro in mros: + # Good iff it doesn't appear in any other tails + if mro is other_mro: continue + if head in other_mro[1:]: break + else: # no break, good head + # Add head as the next base + linearization.append(head) + # Filter head out of all remaining lists + mros = [(remaining := [t for t in mro if t is not head]) for mro in mros if remaining] + break + else: # no break, no good head + raise TypeError(f"MRO conflicts among bases {bases}") + return linearization +``` + +#### resolve_bases + +If any non-type bases have a `__mro_entries__` attribute, +call it and replace them with its results. + +```python +def resolve_bases(bases): + def _bases(base): + if isinstance(base, type) or not hasattr(base, "__mro_entries__"): + return [base] + return base.__mro_entries__(bases) + + return tuple(itertools.chain.from_iterable(map(_bases, bases))) +``` + +## References + +- [Python Data Model](https://docs.python.org/3/reference/datamodel.html) +- [Descriptor Guide](https://docs.python.org/3/howto/descriptor.html) +- [Python typing](https://docs.python.org/3/library/typing.html) +- [Type parameters for python generics](https://docs.python.org/3/reference/compound_stmts.html#type-params) +- [Method resolution order](https://docs.python.org/3/howto/mro.html) +- [C3 linearization](https://dl.acm.org/doi/10.1145/236337.236343) +- [Abstract Base Classes](https://docs.python.org/3/library/abc.html) +- [PEP 484 - Type hints](https://peps.python.org/pep-0484/) +- [PEP 695 - Type Parameter Syntax](https://peps.python.org/pep-0695/) +- [PEP 544 - Protocols: Structural subtyping (static duck typing)](https://peps.python.org/pep-0544/) +- [PEP 560 - Core support for typing module and generic types](https://peps.python.org/pep-0560/) +- [PEP 557 - Data Classes](https://peps.python.org/pep-0557/) +- [PEP 3115 - Metaclasses in Python 3000](https://peps.python.org/pep-3115/) +- [PEP 3119 - Introducing Abstract Base Classes](https://peps.python.org/pep-3119/) +- [MyPy - Protocols and structural subtyping](https://mypy.readthedocs.io/en/stable/protocols.html) +- [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) +- [Python garbage collection](https://devguide.python.org/internals/garbage-collector/index.html) From 96a816fc61cf06a873ffacd7af00d02a3f76b3f3 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:16:13 -0400 Subject: [PATCH 1397/2019] [stdlib] Remove `bitcast` from prelude module MODULAR_ORIG_COMMIT_REV_ID: 10e4f041f4f3535b263df7660b00511881570903 --- docs/changelog.md | 4 ++-- stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/prelude/__init__.mojo | 3 --- stdlib/src/utils/stringref.mojo | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 21cb9f716b..8d057e4649 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -635,8 +635,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `SIMD.load/store` are moved to `UnsafePointer`. -- `sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` are - removed from prelude. +- `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` + are removed from prelude. ### ❌ Removed diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index fd55d900f7..a53f595e2a 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -17,7 +17,7 @@ from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width -from memory import memcmp +from memory import memcmp, bitcast from utils.stringref import _align_down, _memchr, _memmem diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 1d1daf45cb..49fab8bdd2 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -31,7 +31,7 @@ from sys import simdwidthof, bitwidthof from collections import InlineArray from builtin.dtype import _uint_type_of_width -from memory import memcpy, memset_zero, stack_allocation +from memory import memcpy, memset_zero, stack_allocation, bitcast # ===----------------------------------------------------------------------=== # # Implementation diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 4adfb15fc8..305c963b9d 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -135,6 +135,3 @@ from utils._format import Formattable, Formatter # Private things from builtin._documentation import doc_private from utils._visualizers import lldb_formatter_wrapping_type - -# Load-bearing ones to remove -from memory import bitcast diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index d6416a0bf7..bb61f6440c 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,7 +16,7 @@ from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from collections.string import _atol, _isspace -from memory import UnsafePointer, memcmp +from memory import UnsafePointer, memcmp, bitcast from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice from sys.ffi import C_char From eb168a2da3a6b6f45a50c39124dda0dc504f2292 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Thu, 15 Aug 2024 15:52:13 -0700 Subject: [PATCH 1398/2019] [revert] Mojo: Classes proposal This is too far off to give a realistic proposal, removing it for now. MODULAR_ORIG_COMMIT_REV_ID: ba7196723495b46183adcf4e61fc56fd2af6ed62 --- proposals/mojo-classes.md | 907 -------------------------------------- 1 file changed, 907 deletions(-) delete mode 100644 proposals/mojo-classes.md diff --git a/proposals/mojo-classes.md b/proposals/mojo-classes.md deleted file mode 100644 index a4a8fa3551..0000000000 --- a/proposals/mojo-classes.md +++ /dev/null @@ -1,907 +0,0 @@ -# Classes in Mojo - -**Author: Stef Lindall**\ -**Status: Draft**\ -**Date: August 14, 2024**\ -**Last updated: August 14, 2024**\ - -A frequent refrain about Mojo's semantics differing from Python's is that -"Mojo doesn't yet have classes", which will behave more like Python types. -It is _not_ necessarily a goal of Mojo to precisely implement Python classes; -however, in order to decide how Mojo classes should work, it's critical to at -least understand how Python classes work, so we can make informed choices about -tradeoffs when we choose to differ. - -This whitepaper attempts to be a path marker in that direction. It is split into -two halves with different goals: - -1. Fully describe the Python object model. - - This should be entirely objective, with the goal that if we implemented the - described behavior in Mojo, it would pass 100% of the relevant CPython - conformance test suite. If you know of edge cases where it is inaccurate, - please raise them. - -2. A set of proposals for Mojo to implement classes. - - This will naturally include some degree of my own perspective. The goal - isn't to be objective, but to try to identify the critical vs incidental - complexity in the existing Python object model, judged by its relative - usefulness in the broader Python ecosystem, and attempt to measure tradeoffs - in implementing the behavior in Mojo vs Mojo's own language goals. - -This document is _not_ a full summary of the Python Data Model defined here: -. In particular it 1) -attempts to omit things which are part of the CPython implementation but not -standard (though this is a pretty soft boundary in practice) and 2) does not -make any attempt to exhaustively list the set of `__magic__` dunder attributes -recognized by Python, especially as they relate to specific protocols not -central to the object model (for example, iteration or numerics). - -## The Python object model in detail - -### Scope -- What is the Python object model? - -Concretely, the object model defines: - -- For an object, what is its type? -- How are new types defined? -- When I access an attribute on an object, what happens? What is the type of - that expression? - -I will attempt to aggressively ignore any behavior which does not directly -serve answering these questions. - -### Glossary - -You probably know these terms already, but to make sure we're saying precisely -the same thing, they're defined here. - -- `object`: The base type of all objects in Python, and a name used to describe -instances of python objects. - - Internally it's represented as a pair: - - `__class__: type` - - `__dict__: dict[str, object]` - - (this is different for objects defining `__slots__`, but conceptually - still true) - - Informally, instances are often named `obj` in code to avoid a name - conflict with `object` - - `isinstance(x, object)` for all `x` in Python (including `object`) -- instance: A synonym for an object -- `type`: A heavily overloaded term here. - 1. An object instance describing a type - 2. A function which returns an object's `__class__` - 3. The `type` metatype: The following are all true. - - `type is type(object)` - - `type is type(type)` - - `isinstance(type, object)` - - `isinstance(object, type)` - - `type.__mro__ == (type, object)` - - Informally, instances are often named `cls` in code to avoid a name conflict - with `type` (`class` is a keyword) -- `attribute`: A named member of an `instance` - - Accessed by the syntax `instance.attribute` - - Conceptually, an object's attributes are exactly the members of `__dict__` - - Python provides mechanisms to customize what a type considers to be an attribute -- `__dict__`: The attributes of an object (if it doesn't define `__slots__`) -- `__class__`: An attribute of objects which resolves to the type of that object -- `__bases__`: An attribute of type objects which contains their direct parent types -- `__mro__`: "Method resolution order" -- An attribute of type objects which - contains a totally ordered sequence of all their ancestor types. Python uses - the C3 linearization (see appendix). -- metaclass: A word for `type(type(x))` -- `issubclass(sub, cls)`: A builtin function defined on types. Generally true if - `cls in sub.__mro__`. The object model provides mechanisms to customize this - behavior. -- `isinstance(obj, cls)`: A builtin function. Generally true if - `issubclass(type(obj), cls)`. The object model provides mechanisms to - customize this behavior. -- protocol: An implicit interface or set of behaviors conformed to by a type -- descriptor: An object of some type which implements the "descriptor protocol", - which allows attributes to be lazily evaluated. -- namespace: A `__dict__` without an associated `__class__` -- `locals()`: The locally scoped namespace dictionary -- `globals()`: The module scoped namespace dictionary -- name: A string key in a namespace - - NB: Notice that a name _does not have a type_! -- annotation: A syntactically valid python expression that is never evaluated by - the object model. -- `__annotations__`: A namespace member mapping names to annotation values. - -### Memory model - -All Python objects are reference counted, including compiler builtins like `None`. -Since types are implicitly references, there's not problem defining recursive types: - -```python -class LinkedListNode: - next: Optional[LinkedListNode] # this is fine!! -``` - -Most of the rest of the memory model are CPython implementation details: - -- An object can't be explicitly deleted. - - It will be garbage collected when its reference count goes to 0, or when the - cycle detector detects that it is part of a cycle with no external references. - - When an object is deleted, if it has a `__del__` method, that method _may_ - be called. If it throws, the exception is ignored. -- The only way to explicitly allocate an object is with `object.__new__(cls)`. -- `obj.__weakref__` contains a list of weak references which point at `obj` - -### Object model - -Following is a conceptual implementation of Python's object model, as though it -were implemented directly in Python. It attempts to be accurate, in other words -if we implemented precisely this object model, we would be directly compatible -with Python types. The goal is to provide a shared basis of understanding of -what compatibility looks like, as well as a reference for an eventual implementation. - -#### `object` - -```python -## NOTE: Attribute accesses in this implementation are sometimes fudged -## as direct access, when they couldn't be because of recursion. -class object(metaclass=type): - def __new__(cls): - # Can't be implemented in pure python - # Allocates a new Python object with an empty __dict__, and __class__ = cls - - def __init__(self): - pass - - @property - def __class__(self): - # Isn't implemented in pure python. - # Returns the __class__ from the underlying C object. - - def __dir__(self): - return list(self.__dict__) - - def __getattribute__(self, attr): - for base in type(self).__mro__: - if attr in base.__dict__: - classvar = base.__dict__[attr] - # Resolve data descriptor; See appendix - if hasattr(classvar, ("__set__", "__delete__")): - return classvar.__get__(self, base) - # Found non-data-descriptor - break - else: # No classvar defined, do instance lookup - if attr in self.__dict__: - return self.__dict__[attr] - raise AttributeError - # resolve non-data descriptor - if hasattr(classvar, "__get__"): - return classvar.__get__(self, base) - return classvar - - def __setattr__(self, attr, value): - for base in type(self).__mro__: - if (value := base.__dict__.get(attr)) and hasattr(value, "__set__"): - value.__set__(self, value) - return - else: - self.__dict__[attr] = value - - def __delattr__(self, attr): - for base in type(self).__mro__: - if (value := base.__dict__.get(attr)) and hasattr(value, "__delete__"): - value.__delete__(self) - return - if attr in self.__dict__: - del self.__dict__[attr] - else: - raise AttributeError - - # Other methods related to str/hash/pickling/comparison/etc omitted - ... -``` - -#### Attribute access - -Attribute access `instance.attribute` is syntax sugar for -`getattr(instance, "attribute")`, with the following implementation: - -```python -def getattr(obj, attr, default=MISSING): - # Try resolving through __getattribute__ - try: - return obj.__getattribute__(attr) - except AttributeError as e: - exc = e - - # Try resolving through __getattr__ - try: - if hasattr(obj, "__getattr__"): - return obj.__getattr__(attr) - except AttributeError as e: - exc = e - - # Nothing found, return default or raise - if default is not MISSING: - return default - raise exc - -def setattr(obj, attr, value): - obj.__setattr__(attr, value) -``` - -#### Methods - -Functions in python implement the descriptor protocol. The `function` can be -thought of like - -```python -class function: - def __get__(self, instance, owner=None): - if instance is None: - return self - return method(self, instance) - - def __call__(self, *args, **kwargs): - # execute the function -``` - -allowing them to be used as free functions, except when accessed as an attribute -of an instance, in which case they become a bound method; conceptually: - -```python -@dataclass -class method: - __func__: function - __self__: object - - def __call__(self, *args, **kwargs): - self.__func__(self.__self__, *args, **kwargs) -``` - -`@property`, `@staticmethod` and `@classmethod` decorators don't require any -special behavior, and may be implemented in pure python as descriptors - -#### `type` - -`type` is a function which returns an object's type: - -```python -## `type` has this special overload -def type(obj): - return obj.__class__ -``` - -`type` is also the base metatype. - -`type` is primarily responsible for defining how inheritance works. Python uses -the C3 type linearization algorithm by default to construct a type's method -resolution order. - -Beyond that, `type` may be subclassed to create different metaclasses, with -customized behavior for new type creation, namespaces, or customizing -`isinstance` and `issubclass` behavior (particularly useful for things like `Protocol`.) - -Conceptually, `type` could be implemented as - -```python -class type: - def __init__(self, name, bases, namespace, **kwargs): - self.__name__ = name - # See appendix for implementation. In ~all python code this does - # resolve_bases is a no-op, but it supports inheriting from non-types. - self.__bases__ = resolve_bases(bases) - self.__mro__ = self.mro(bases) - for name, attr in namespace.items(): - setattr(self, name, attr) - - # Set descriptor names - if hasattr(attr, ("__get__", "__set__", "__delete__")) and hasattr(attr, "__set_name__"): - attr.__set_name__(self, name) - - self.__init_subclass__(**kwargs) - - def __init_subclass__(self, **kwargs): - pass - - def mro(self): - # See appendix for implementation of C3 mro - return [self] + c3_mro(self.__bases__) - - @property - def __dict__(self): - # Not implementable in pure python. This is where - # all accesses of `__dict__` bottom out. - - @classmethod - def __prepare__(cls, **kwargs): - return {} - - def __call__(self, *args, **kwargs): - # This is the real implementation of what we more - # naturally think of as a "constructor" call - new_instance = self.__new__(*args, **kwargs) - new_instance.__init__(*args, **kwargs) - return new_instance - - # NOTE: Attribute access here fudged, the true implementation - # won't recursively call `__getattribute__. - def __getattribute__(self, attr): - # __getattribute__ has different behavior for types: - # - descriptor doesn't pass an `instance` - # - iterate self.__mro__ instead of type(self).__mro__ - for base in self.__mro__: - if attr in base.__dict__: - value = base.__dict__[attr] - if hasattr(value, "__get__"): - value = value.__get__(None, self) - return value - else: - raise AttributeError - - def __instancecheck__(self, instance): - # Called by `isinstance` - return issubclass(type(instance), self) - - def __subclasscheck__(self, subclass): - # Called by `issubclass` - return self in subclass.__mro__ - - def __subclasshook__(self, subclass): - return NotImplemented - - def __subclasses__(self): - # returns the list of types T having self in T.__mro__ - ... - - # This seems to be vestigial - @property - def __base__(self): - return None if self is object else self.__bases__[0] - - # Other methods like str/repr omitted - ... -``` - -#### Class creation - -```python -class NewType(Base1, Base2, metaclass=type, **kwargs): - -``` - -is syntactic sugar for - -```python -## Set up the class namespace -qualname = locals().get("__qualname__") -namespace = metaclass.__prepare__(**kwargs) -namespace["__module__"] = locals().get("__module__") -namespace["__qualname__"] = f"{qualname}.NewType" if qualname else "NewType" -namespace["__annotations__"] = {} - -## See appendix for min_type implementation -metaclass = min_type({metaclass, *(type(base) for base in bases)}) - -## Execute the class body to populate the remainder of the namespace -exec(compile(body), globals=globals(), locals=namespace) - -## Actually create the type -NewType = metaclass(name="NewType", bases=(Base1, Base2), dict=namespace, **kwargs) -``` - -### Descriptor protocol - -Among protocols, the descriptor protocol is undoubtedly the one most central to -Python's object model, evidenced by its heavy use in the above implementation. - -There isn't a formal protocol definition in the implementation, but it would look -like this: - -```python -class Get(Protocol): - def __get__(self, obj, cls=None): ... - -class Set(Protocol): - def __set__(self, obj, value): ... - -class Delete(Protocol): - def __delete__(self, obj): ... - -NonDataDescriptor = Get -DataDescriptor = Set | Delete -Descriptor = DataDescriptor | NonDataDescriptor -``` - -Data descriptors and non-data descriptors differ in that a data descriptor takes -precedence over an instance attribute (one in `__dict__`) while a non-data -descriptor does not. - -Descriptors may also implement `__set_name__(cls, name)`, which is called during -type creation. - -### Generics - -Python has two syntaxes for defining type parameters. It has a mojo-like type -parameter syntax, introduced in [PEP 695](https://peps.python.org/pep-0695/), -as well as `typing.TypeVar` and `typing.Generic`. - -#### Type parameters - -Using the type parameter syntax is syntactic sugar for defining `TypeVar`s and -inheriting from `Generic`, but is also allowed on functions and methods. - -These look approximately like Mojo's parameters: - -```python -class ClassA[T: str]: - def method1(self) -> T: - ... - -def func[T](a: T, b: T) -> T: - ... - -type ListOrSet[T] = list[T] | set[T] -``` - -- `type` implements `__getitem__` where `type[Any] == type`, and `type[T]` - represents `type(T)` in a generic context. -- `type` implements `__or__`, which allows type unions to be defined as `X | Y`. -- `__origin__` on a parameterized type holds the un-parameterized generic type -- `__type_params__` holds a tuple of type parameters of a generic type or function - - Though not specified, `__parameters__` appears to alias `__type_params__` -- `__args__` holds the values of the type parameters of a parameterized generic - type or function -- `__value__` is in the spec as holding the "resolved" type but doesn't appear - to be implemented in CPython -- There appears to be no way to retrieve the annotations of type parameters - -> [!IMPORTANT] -> `T: str` in Python is a _covariance_ declaration, whereas Mojo currently defines -> this as a _type_ declaration. In other words, in Python `class ClassA[T: str]:` -> means that `issubclass(T, str)`, while in Mojo this would currently mean ->`isinstance(T, str)`. This represents an important conflict to resolve in implementation. - -#### `typing.Generic` and `typing.TypeVar` - -`Generic` is of type `GenericMeta`, which implements the machinery for generic -types, such as `__class_getitem__`. This may be seen as a historical artifact, -as `type` itself is now subscriptable to support generic typing. -[PEP 560](https://peps.python.org/pep-0560/) explicitly identifies removing the -dependency on `GenericMeta` as a motivation for the development of -`__class_getitem__` and `__mro_entries__`. - -`Generic` types - -### `typing.Protocol` - -`Protocol` is a type which can be subclassed to provide a Mojo-trait-like type: -any type with the provided interface will type-check as that type in type -checkers like Pylance and MyPy, and with `isinstance` if the type is -additionally annotated with `@runtime_checkable`. - -[MyPy's Protocols and structural subtyping](https://mypy.readthedocs.io/en/stable/protocols.html) -docs have many good examples. Protocols are the most literal way to type check -Python's "duck typing" standard: types are never required to inherit from a -protocol (and generally shouldn't; while Protocols can implement default method -implementations, duck-typed "subclasses" don't inherit them, and `Protocol` -has its own metaclass, so this is all-around better achieved with a mixin type). - -### Abstract base classes - -ABCs were introduced alongside metaclasses in 2007 in Python 3.0. Python type -hints were introduced 7 years later, and `typing.Protocol` was released in 2017. -You can see ABCs as a specific set of predefined protocols, along with mixin -types. In fact, many of them are named similarly to Mojo traits: `Hashable`, -`Sized`, etc. - -Of particular interest are the `Sequence`, `Mapping`, and `Set` types, which -provide mixin methods to define types implementing these protocols more easily. - -ABC also defines the `@abstractmethod` and related decorators, which more -directly map to a traditional OO model. Types inhereting classes annotated with -these methods must either declare themselves to also be abstract, or implement -the required methods, otherwise they will raise during class creation. - -### Metaclass conflicts - -Python needs to choose a single type to use as a metaclass when creating a new -type. It does this in a sensible way: among all candidate metaclasses -(metaclasses used by bases, and any specified metaclass), it tries to use the -most derived one, and if there isn't a unique such type, it errors. - -This is a sensible behavior, but it means that inheriting from a metaclass is a -_very_ expensive thing to do for a library: if someone uses your types, they -can't use _any_ other library which also uses metaclasses. - -This is a particular pain point with `typing.GenericMeta` and -`collections.abc.ABCMeta`. Since these types are implementing typing for -"normal" python types, they (1) can't be used together, and (2) can't be used -with any libraries which use mataclasses, for instance `enum` or most ORMs. - -This is in fact why `@dataclass` is implemented as a decorator, when a metaclass -would have been a much more natural fit. See -[PEP 557](https://peps.python.org/pep-0557/) for more details, which -specifically identifies dataclasses as a natural application of metaclasses, and -_not_ having a metaclass being a central decision in the design. - -### `__slots__` - -There's a separate opt-in object model pattern if a class defines -a `__slots__` class variable, eg. - -```python -class Foo: - __slots__ = ["bar", "baz"] -``` - -I'll link the [docs here](https://docs.python.org/3/reference/datamodel.html#slots) -on how slots behave, since it goes a bit outside the scope of this document. -Critically these objects lack a `__dict__`, and have a number of restrictions on -usage, including not being allowed to assign arbitrary attributes to instances, -and strong limitations on subclassing. This comes with reduced memory usage and -improvements in attribute access time. - -Since types with `__slots__` behave _almost_ exactly like objects without them, -other than restrictions, and could be implemented with an appropriate metaclass -in the existing object model, they don't require substantial support and I won't -cover them more deeply here. - -### Python Crimes - -There's many behaviors supported in the Python object model that the community -has widely agreed are bad practice. These have vanishingly small usage in -practice, and the language provides other usage patterns to accomplish most -things they might be useful for. - -#### Assigning to `__class__` - -- This is the only way to mutably change the type of an object. -- In practice I've only used this extremely rarely, and in retrospect never for - a good reason. -- It's always possible to instead update a reference to point to a shallow or - deep copy as a new type instead. The only semantic difference is that it - doesn't update other referrers. -- Conceivably it might be useful for certain hot-reloading patterns, updating - references in the interpreter in-place to a new instance of the type. PyPy - has experimented with this but the common Python hot-reloaders don't - actually do it. -- This has a storied history; PyPy has already removed this behavior, and - Python 3 raises a TypeError for certain cases of this. - -#### Assigning to `__bases__` or `__mro__` - -- I've never actually seen this done or done this. -- Again this falls into the category of "plausibly you might do this for - hot-reloading, in practice no one does". - -#### Assigning to `__dict__` - -- This is a surefire way to burn yourself. I don't know of any widely used - patterns that do this or might want to. - -#### Assigning arbitrary attributes to `__dict__` - -- This is obviously supported through `__getattr__` et al, but it's additionally - just directly supported by all types not implemented with `__slots__`. -- In some sense this is the "expected" behavior, thinking of Python as a - scripting language: when calling `__init__`, a priori we don't know what - attributes a class _should_ have, so we can't place expectations on those attributes. -- There's a wide tail of other use cases for this behavior, such as - - Decorators frequently assign a new attribute to a function or type to "tag" - it with metadata. This is a very fragile pattern I'm not a fan of, but is - relatively widely used. - - Quick and dirty caching: - `def x(self): return self._x if hasattr(self, "_x") else (self._x := _compute_x())` -- I'm convinced if this pattern didn't exist, everything it's used for could - ~easily be done another way, however I am also prepared to be surprised by - how much code actually does this. - -#### Inheriting from objects that aren't types - -- There's some support in the language for this through `__mro_entries__`. - [PEP 560](https://peps.python.org/pep-0560/) discusses this. It is primarily - useful because of metaclass conflicts, which became more common with the - addition of generic types into python typing depending on `GenericMeta`. -- I doubt this is widely used, but I don't have data to back that up. It's - likely we could design for generic types more directly from the start to - avoid this issue (though harder to avoid the metaclass conflict issue more generally). - -#### Types which provide `__bases__` or `__mro__` dynamically via a descriptor or `__getattr__` - -- I've never heard of anyone doing this but it is clearly chaotic evil. -- I bet you could easily make this mechanism alone turing complete so it - undoubtedly has amazing use cases, and I also am confident this will never - be practiced. You could certainly implement an approximation of this many - other ways. - -#### Dynamic inheritance: `class Foo(Bar if x else Baz):` - -This looks insane but is actually really useful in practice. For instance - -- A CSV-loader interface might want to read the CSV header, and then construct a - new row type which inherits from `collections.namedtuple(headers)`. -- Similarly, Pandas `Dataframe` has dynamic attributes for the name of each - column in the dataset, which are data-dependent. -- A core use case for class decorators is to return a new subclass of the - decorated class. -- It's sometimes useful to create an "anonymous" subclass of a type, created in - some function scope. - -#### Full `exec` dynamism: `exec("class Foo: ...")` - -- `collections.namedtuple` used to be implemented this way -- I believe that the modern object model is expressive enough that there aren't - any regular use cases of this anymore. - -#### [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) violations - -Python subtypes don't prevent you from overridding parent type attributes with -incompatible attributes that would make them illegal to pass as a parent type. -In practice there's lots of code that accidentally does this, although MyPy -recognizes it as a typing error. - -## Proposals to implement Mojo classes - -This section has considerably more of a perspective than the previous. The goal -is to provide a concrete proposal for how Mojo could move forward implementing -classes which is faithful both to Python's object model and to Mojo's own -language goals. - -### Types as types vs types as functions - -Python may be thought of as a strongly typed, dynamically type functional language. -Objects have types, names do not. Types may never be converted into other types -(except the specific "Python Crime" of assigning to `__class__`). While it appears -imperative on the surface, all python types are both objects and functions, and -the "constructor" of a type in fact doesn't even need to do that; for instance, -`__new__` may be overloaded to return singleton instances or interned objects. -Instead, it's often more useful to think of types as a function which transforms -a value into a value of another type. While this perspective is pretty far from -what we normally think of as object-oriented programming, it makes a lot of the -more common "python crimes" listed below make more sense. - -By contrast, Mojo is statically, weakly typed. Names have types, and only objects -of those types may be associated with that name. Mojo allows implicit conversion -of types into other types. Mojo constructors are exactly that in the traditional -sense: they allocate and initialize memory. - -This contrast is in some sense the deepest disparity between Python and Mojo, -and in order to be successful in its goals, Mojo classes will need to fully -embrace Python's perspective on these topics. - -### High level perspective: Mojo's goals - -The Python 3 language spec is a moving target. Many attempts at alternative -language implementations exist, but as CPython is co-developed with the Python -spec, they are necessarily approximations. - -Mojo also has its own goals. It wants to be a compiled, performant, full-stack -language, while building a healthy Pythonic-language ecosystem of libraries -shared between it and CPython. - -Mojo and Python in this sense have a lot of shared goals. We should, at a -minimum aim for most Python libraries to "just work" on both Mojo and CPython. -However, realistically around the edges and in uncommon cases, we will need to -make separate decisions about precise implementation details, and Mojo can't -realastically pass the entire CPython compatibility test suite. - -We should identify some _subset_ of it that we intend to pass, make this a clear -contract with our users, work closely with CPython in spec development in these -areas, and make it easy for users and library authors to write code which is -compatible with both. The _natural_ way to write Python and Mojo should be the -same on this set of language features. The vibe should be "you're using Python? -your use case will almost certainly _just work_ on Mojo", possibly with a small -set of compatibility changes that will continue to allow it to work in Python, -and that we help automate (think python's `six` library). - -The truth is that there are decisions in Python that we should realistically -consider _not_ carrying forward; warts which exist for historical reasons but -are too much work to rip out, and that a vanishingly small percent of the Python -ecosystem relies on. - -### Proposed differences for Mojo's object model - -It would be incredibly valuable to Mojo, as a compiled language, to be able to -reason more about the set of possible things that may be done with an object. -Identifying tradeoffs of where simplifying the object model allows us a better -implementation, vs the cost in compatibility and expressiveness, is the core -job of this proposal. - -I'm framing these tradeoffs primarily with the intuition that moving attribute -and method resolution to compile time where possible is the most valuable -benefit, and that we shouldn't change what isn't broken except in service of -this goal. - -Following is an initial proposal of a Mojo object model for classes, which -retains most of the dynamic use cases of Python objects, while allowing -hopefully the vast majority of existing cases to "just work" with compile-time -method and attribute resolution. - -#### Any Mojo type which is a `class` is implicitly wrapped by an `Arc` - -Existing Mojo `fn` and `def` semantics are unchanged: - -- If an argument is a `struct`, it retains its current behavior -- If an argument is a `class`, it will behave as an owned reference, and that - reference will be reference-counted - - Attribute lookup behaves as it would for a direct reference - - It is implicitly convertible to an `Arc[Object]` for lower level access - -#### Implement Python's object model for classes, with compile-time method resolution - -`__getattribute__` and `__getattr__` may still be used to implement dynamic -attributes, but in the common case attributes are resolved statically. - -- `__dict__` may not be assigned to directly - - For compatibility, it may be accessed, but these function as the variants of - `setattr` and `getattr` described below. -- `__class__`, `__bases__`, and `__mro__` are immutable and may not be assigned to -- Class attributes must have a static type - - All attributes declared in the class body. - - Classes inherit the attributes of all types in their `__mro__`. - - If two types in the `__mro__` share an attribute name, they must either be - the same type, or must be linear ancestors of each other, in which case they - take the most derived type. -- Classes are separated into implementation categories: - - Type 1: Classes which define neither `__getattr__` nor `__getattribute__` - in their `__mro__` - - Assigning to or accessing an undefined attribute is a compile error. - - `__setattr__` may be defined, but will raise an `AttributeError` if called - with any unknown attribute. - - `__delattr__` may not be defined - - Type 2: Classes which define `__getattr__` but not `__getattribute__`. - - Behave like Type 1 classes, except where the compiler would fail td - resolve an attribute lookup, it instead emits a call to `__getattr__`. - - `__delattr__` may be defined, but will raise a `TypeError` if called witd - an attribute that resolves via Type 1 attribute lookup. - - `__setattr__` may be defined with no restrictions - - Type 3: Classes which define `__getattribute__`. - - Any attribute access on these types will emit a call to `__getattribute__`. - - `__delattr__` may be defined in the same way as Type 2 classes. - - `__setattr__` may be defined with no restrictions -- We have a variant of the `getattr`, `hasattr`, `setattr`, and `delattr` - functions which dynamically attempt to perform a Type 1 lookup on the object - using its runtime type, or raise an`AttributeError`. -- Dynamic inheritance is allowed, but any type variable used as a base class - must be specified as a [type parameter](https://docs.python.org/3/reference/compound_stmts.html#type-params) - and the compiler must be able to infer its concrete type at compile time. - -#### More invasive or opinionated changes - -##### Simply the typing model around type parameters and Protocols - -- Remove Abstract Base Classes and decorators. Re-implement ABC types as Protocols. -- Remove `typing.Generic`, only support type parameters. -- A class inheriting from Protocol is treated exactly as a trait definition. -- All traits and protocols behave as though they are annotated with `@runtime_checkable` - -##### Require types to obey the Liskov substitution principle - -- Attributes and method return types in child classes must be covariant to the - same in their parent classes. - - Attributes may be replaced with descriptors satisfying this princple. -- Method arguments must be contravariant to the same in their parent classes. - - They may define additional optional arguments. - - They may make previously required arguments into default arguments. - - Since all functions in Python may raise Exception, any new raises are always - legal. - -##### Allow method overloading - -- All members of a method overload set must be defined in the class body. -- If a subclass overrides an overloaded method, it forms a new overload set. - - A subclass overriding only some overloads of a method will implicitly have - additional overloads in its overload set calling super() methods. - -##### Simplify descriptor semantics - -Eliminate the distinction between non-data and data descriptors. Define a -descriptor as having a `__get__` with the correct interface. Descriptors follow -the same precedence rules as other name lookups. - -##### Help avoid common metaclass conflicts - -- If metaclasses are not linearizable when creating a new type, attempt to -create an anonymous subclass of conflicting metaclasses. - -### Open questions - -- How do we resolve the difference in semantics of Python type parameters - defining covariance vs Mojo type parameters defining type? -- Python uses magic attributes for type parameters, while Mojo treats them as - type attributes. What's the best way to resolve this in implementation? -- Mojo's type system should support some degree of higher-kinded types compared - to Python. Is that in scope for this proposal? - -## Appendix - -### Out-of-line reference implementations - -Find the unique most-derived type among input types, or `TypeError`. - -#### min_type - -```python -def min_type(types): - assert types - min_type = types.pop() - for type in types: - if issubclass(type, min_type): - min_type = type - elif not issubclass(min_type, type): - # Inherited metaclasses must be totally orderable - raise TypeError - return min_type -``` - -#### C3 Method resolution ordering - -This looks a bit complicated, but it's not actually too bad. See the -[MRO documentation](https://docs.python.org/3/howto/mro.html) and -[C3 linearization paper](https://dl.acm.org/doi/10.1145/236337.236343) for more details. - -The Method Resolution Order is a "linearization" of -the ancestor classes of `cls`, which has the property -of being "monotonic": For any types `P` and `Q` in `Base.__mro__` -where `P` appears before `Q`, then for all types `Child` having `Base` -in `Child.__mro__`, `P` must also appear before `Q` in `Child.__mro__`. - -This naive implementation of the C3 algorithm is O(N^2) -for simplicity. - -```python -def c3_mro(bases): - linearization = [] - mros = [list(base.__mro__) for base in bases] - # We reduce the total number of elements in `mros` - # by at least 1 per iteration, so time is bounded. - while mros: - # Search fo a good head - for mro in mros: - head = mro[0] - for other_mro in mros: - # Good iff it doesn't appear in any other tails - if mro is other_mro: continue - if head in other_mro[1:]: break - else: # no break, good head - # Add head as the next base - linearization.append(head) - # Filter head out of all remaining lists - mros = [(remaining := [t for t in mro if t is not head]) for mro in mros if remaining] - break - else: # no break, no good head - raise TypeError(f"MRO conflicts among bases {bases}") - return linearization -``` - -#### resolve_bases - -If any non-type bases have a `__mro_entries__` attribute, -call it and replace them with its results. - -```python -def resolve_bases(bases): - def _bases(base): - if isinstance(base, type) or not hasattr(base, "__mro_entries__"): - return [base] - return base.__mro_entries__(bases) - - return tuple(itertools.chain.from_iterable(map(_bases, bases))) -``` - -## References - -- [Python Data Model](https://docs.python.org/3/reference/datamodel.html) -- [Descriptor Guide](https://docs.python.org/3/howto/descriptor.html) -- [Python typing](https://docs.python.org/3/library/typing.html) -- [Type parameters for python generics](https://docs.python.org/3/reference/compound_stmts.html#type-params) -- [Method resolution order](https://docs.python.org/3/howto/mro.html) -- [C3 linearization](https://dl.acm.org/doi/10.1145/236337.236343) -- [Abstract Base Classes](https://docs.python.org/3/library/abc.html) -- [PEP 484 - Type hints](https://peps.python.org/pep-0484/) -- [PEP 695 - Type Parameter Syntax](https://peps.python.org/pep-0695/) -- [PEP 544 - Protocols: Structural subtyping (static duck typing)](https://peps.python.org/pep-0544/) -- [PEP 560 - Core support for typing module and generic types](https://peps.python.org/pep-0560/) -- [PEP 557 - Data Classes](https://peps.python.org/pep-0557/) -- [PEP 3115 - Metaclasses in Python 3000](https://peps.python.org/pep-3115/) -- [PEP 3119 - Introducing Abstract Base Classes](https://peps.python.org/pep-3119/) -- [MyPy - Protocols and structural subtyping](https://mypy.readthedocs.io/en/stable/protocols.html) -- [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) -- [Python garbage collection](https://devguide.python.org/internals/garbage-collector/index.html) From 9f34a97ed3c78d3519d50373183e27b196278fe1 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 15 Aug 2024 16:00:02 -0700 Subject: [PATCH 1399/2019] Update get started guide to use Magic MODULAR_ORIG_COMMIT_REV_ID: 6e0558429aaa8a21018407a0c9928aa73f8e199c --- docs/manual/get-started.mdx | 281 ++++++++++++++++++++++++++++-------- 1 file changed, 217 insertions(+), 64 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 265ea29bf5..0b3f59e32b 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -6,47 +6,138 @@ description: Install Mojo now and start developing import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import MaxInstall from '@site/src/components/MaxInstall'; On this page, we'll show you how to create the classic "Hello world" starter program with Mojo, in three different ways. If you'd rather read how to write Mojo code beyond just printing text, see the [introduction to Mojo](/mojo/manual/basics). -## 1. Install Mojo +## 1. Create a new project -Mojo is now bundled with MAX, which provides everything you need to compile, -run, debug, and package Mojo code. (Read [why we bundled Mojo with -MAX](/max/faq#why-bundle-mojo-with-max).) +[`magic`](/magic) is our CLI tool that makes is really simple to get started +with Mojo. With just a couple commands, you'll create a new project with its +own virtual environment that includes everything you need to get started. -Follow the guide to [install MAX & Mojo](/max/install), and then return here. +1. Install Magic on macOS and Ubuntu with this command: -## 2. Run code in the REPL + + + Then run the appropriate `source` command for your terminal so that `magic` + is visible in your `PATH`: + + + + + ```sh + BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) + source "$BASHRC" + ``` + + + + + ```sh + source ~/.zshrc + ``` + + + + + ```sh + source ~/.config/fish/config.fish + ``` -Now that you've installed Mojo, let's write some code! + + + + ```sh + source ~/.tcshrc + ``` + + + + +2. Create a Mojo project called "hello-world": + + ```sh + magic init hello-world --format mojo + ``` + + This creates a directory named `hello-world` and installs the Mojo project + dependencies—the only dependency for a Mojo project is the `max` package + (Mojo is [bundled with MAX](/max/faq#why-bundle-mojo-with-max)). + +3. Start a shell in the project virtual environment: + + ```sh + cd hello-world && magic shell + ``` + +That's it! The `magic shell` command activates the virtual environment so you +can now start using Mojo. For example, you can check your Mojo version like +this: + +```sh +mojo --version +``` + + +
    + Click here to install the Mojo nightly build. +
    + +To install the latest nightly build, specify the `max-nightly` channel when you +initialize your project: + +```sh +magic init hello-world-nightly --format mojo \ + -c conda-forge -c https://conda.modular.com/max-nightly +``` + +When you include the `-c` (`--channel`) option, you must specify all channels. +Be sure to specify the `conda-forge` channel (or another conda channel) where +Magic should look for all packages other than MAX/Mojo. + +Then start a shell in the new environment: + +```sh +cd hello-world-nightly && magic shell +``` + +The nightly version of Mojo installed in this project is fully independent, so +it will not interfere with other projects that use the latest stable build. + +
    +
    + + +## 2. Run code in the REPL First, let's use the Mojo [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), which allows you to write and run Mojo code in a command prompt: -1. To start a REPL session, type `mojo` in your terminal and press - Enter. +1. To start a REPL session, type `mojo` and press Enter. -2. Then type `print("Hello, world!")` and press Enter twice +2. Type `print("Hello, world!")` and press Enter twice (a blank line is required to indicate the end of an expression). -That's it! For example: + The result looks like this: -```text -$ mojo -Welcome to Mojo! 🔥 + ```text + $ mojo + Welcome to Mojo! 🔥 -Expressions are delimited by a blank line. -Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. + Expressions are delimited by a blank line. + Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. -1> print("Hello, world!") -2. -Hello, world! -``` + 1> print("Hello world") + Hello world + ``` + +3. To exit REPL, type `:quit` and press Enter, or press +Ctrl + D. You can write as much code as you want in the REPL. You can press Enter to start a new line and continue writing code, and when you @@ -77,16 +168,10 @@ Now let's write the code in a Mojo source file and run it with the mojo hello.mojo ``` - It should immediately print the message: - - ```text + ```output Hello, world! ``` -If this didn't work for you, double-check that your code looks exactly like the -code in step 1, and make sure you correctly installed either [MAX](/max/install) -(which includes Mojo) or [Mojo](#1-install-mojo). - ## 4. Build an executable binary Finally, let's build and run that same code as an executable: @@ -106,10 +191,60 @@ Finally, let's build and run that same code as an executable: ./hello ``` -This creates a statically compiled binary file, so it contains all the code and -libraries it needs to run. + ```output + Hello, world! + ``` + +The [`build`](/mojo/cli/build) command creates a statically compiled binary +file, so it contains all the code and libraries it needs to run. + +You can now deactivate the virtual environment by just typing `exit`: + +```sh +exit +``` -## 5. Install our VS Code extension (optional) +Now let's try running an existing code example. + +## 5. Run an example from GitHub + +Our Mojo code examples in GitHub include a Magic configuration file so you can +simply clone the repo and run the code with `magic`. For example: + +1. Clone the Mojo repo: + + ```sh + git clone https://github.com/modularml/mojo.git + ``` + + Only if you installed the nightly build, also checkout the nightly branch: + + ```sh + git checkout nightly + ``` + +2. Navigate to the examples: + + ```sh + cd mojo/examples + ``` + +3. Run some code: + + ```sh + magic run mojo hello_interop.mojo + ``` + + ```output + Hello Mojo 🔥! + 9 + 6 + 3 + Hello from Python! + I can even print a numpy array: [1 2 3] + ``` + +## 6. Install our VS Code extension (optional) To provide a first-class developer experience with features like code completion, quick fixes, and hover help, we've created a [Mojo extension for @@ -118,27 +253,64 @@ Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vsco ![](./images/mojo-vscode.png) -## Next steps -- If you're new to Mojo, we suggest you learn the language basics in the - [introduction to Mojo](/mojo/manual/basics). +## Update Mojo + +To update the Mojo version in your project, use `magic add` +and specify the `max` package version. + +For example, if you want to always use the latest version of Mojo, you can use +the `*` wildcard as the version and then simply run `magic update` (you must +run `magic add` within the project path): + +```sh +cd hello-world +``` -- If you want to experiment with some code, clone [the Mojo -repo](https://github.com/modularml/mojo/) to try our code examples: +```sh +magic add "max=*" +``` + +```sh +magic update +``` + +:::note + +Although the wildcard option allows `magic update` to always install the latest +version, it also updates the `magic.lock` file in your project with the +explicit version you've installed. This ensures that anybody else who +initializes the project also gets the same package version (until you run +`magic update` again). + +::: + +To be more specific with your package version, you can use any of the [Python +package version +specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#id5). +For example: + +```sh +magic add "max~=24.4" +``` + +```sh +magic add "max>=24.4,<24.5" +``` - ```sh - git clone https://github.com/modularml/mojo.git - ``` - If you installed the nightly build, also checkout the nightly branch: +## Next steps + +- If you're new to Mojo, we suggest you learn the language basics in the + [introduction to Mojo](/mojo/manual/basics). - ```sh - git checkout nightly - ``` +- To learn more about the `magic` tool, read [Get started with Magic](/magic/). - In addition to several `.mojo` examples, the repo includes [Jupyter - notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) - that teach advanced Mojo features. +- Explore more code examples in the [the Mojo +repo](https://github.com/modularml/mojo/). In addition to several `.mojo` +examples, the repo includes [Jupyter +notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) +that teach advanced Mojo features. - To see all the available Mojo APIs, check out the [Mojo standard library reference](/mojo/lib). @@ -153,22 +325,3 @@ crash reports. [Learn more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). ::: - -## Update Mojo - -Because Mojo is now a part of MAX, you soon won't be able to update the -standalone `mojo` package, and you must instead install/update the `max` -package. (Read [why we bundled Mojo with -MAX](/max/faq#why-bundle-mojo-with-max).) - -If you already installed Mojo on its own, you'll need to install MAX to get all -future Mojo updates. - -Before you install `max`, you should uninstall Mojo to avoid conflicting -toolchain versions between the `mojo` and `max` packages: - -```sh -modular uninstall mojo -``` - -Then follow the guide to [install MAX & Mojo](/max/install). \ No newline at end of file From 935f03c6107d7de25ddb14666623c2410505a2cc Mon Sep 17 00:00:00 2001 From: Victor Guerra Date: Fri, 16 Aug 2024 07:09:41 -0700 Subject: [PATCH 1400/2019] [External] [stdlib] Extending `FloatLiteral` comparison tests with special values (#45379) [External] [stdlib] Extending `FloatLiteral` comparison tests with special values Testing `Comparable` dunder methods with special values: `nan`, `negative_zero`, `infinity`, `negative_infinity` Co-authored-by: Victor Guerra Closes modularml/mojo#3297 MODULAR_ORIG_COMMIT_REV_ID: b163566e0bf2db06be4b09fd597ec17b601f7fa0 --- stdlib/test/builtin/test_float_literal.mojo | 68 ++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index a438ce9d1d..db4841df4d 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -189,27 +189,93 @@ def test_comparison(): assert_true(FloatLiteral.__lt__(-10.4, -4.4)) assert_false(FloatLiteral.__lt__(0.0, 0.0)) assert_false(FloatLiteral.__lt__(10.4, 4.4)) + assert_false(FloatLiteral.__lt__(neg_inf, neg_inf)) + assert_false(FloatLiteral.__lt__(neg_zero, neg_zero)) + assert_false(FloatLiteral.__lt__(neg_zero, 0.0)) + assert_true(FloatLiteral.__lt__(neg_inf, inf)) + assert_false(FloatLiteral.__lt__(inf, inf)) + assert_false(FloatLiteral.__lt__(nan, 10.0)) + assert_false(FloatLiteral.__lt__(10.0, nan)) + assert_false(FloatLiteral.__lt__(nan, inf)) + assert_false(FloatLiteral.__lt__(inf, nan)) + assert_false(FloatLiteral.__lt__(neg_inf, nan)) + assert_false(FloatLiteral.__lt__(nan, neg_zero)) assert_true(FloatLiteral.__le__(4.4, 10.4)) assert_true(FloatLiteral.__le__(-10.4, 4.4)) assert_true(FloatLiteral.__le__(0.0, 0.0)) assert_false(FloatLiteral.__le__(10.4, 4.4)) + assert_true(FloatLiteral.__le__(neg_inf, neg_inf)) + assert_true(FloatLiteral.__le__(neg_zero, neg_zero)) + assert_true(FloatLiteral.__le__(neg_zero, 0.0)) + assert_true(FloatLiteral.__le__(neg_inf, inf)) + assert_true(FloatLiteral.__le__(inf, inf)) + assert_false(FloatLiteral.__le__(nan, 10.0)) + assert_false(FloatLiteral.__le__(10.0, nan)) + assert_false(FloatLiteral.__le__(nan, inf)) + assert_false(FloatLiteral.__le__(inf, nan)) + assert_false(FloatLiteral.__le__(neg_inf, nan)) + assert_false(FloatLiteral.__le__(nan, neg_zero)) - # TODO: add tests for special values assert_true(FloatLiteral.__eq__(4.4, 4.4)) assert_false(FloatLiteral.__eq__(4.4, 42.0)) + assert_true(FloatLiteral.__eq__(neg_inf, neg_inf)) + assert_true(FloatLiteral.__eq__(neg_zero, neg_zero)) + assert_true(FloatLiteral.__eq__(neg_zero, 0.0)) + assert_false(FloatLiteral.__eq__(neg_inf, inf)) + assert_true(FloatLiteral.__eq__(inf, inf)) + assert_false(FloatLiteral.__eq__(nan, 10.0)) + assert_false(FloatLiteral.__eq__(10.0, nan)) + assert_false(FloatLiteral.__eq__(nan, inf)) + assert_false(FloatLiteral.__eq__(inf, nan)) + assert_false(FloatLiteral.__eq__(neg_inf, nan)) + assert_false(FloatLiteral.__eq__(nan, neg_zero)) + assert_false(FloatLiteral.__ne__(4.4, 4.4)) assert_true(FloatLiteral.__ne__(4.4, 42.0)) + assert_false(FloatLiteral.__ne__(neg_inf, neg_inf)) + assert_false(FloatLiteral.__ne__(neg_zero, neg_zero)) + assert_false(FloatLiteral.__ne__(neg_zero, 0.0)) + assert_true(FloatLiteral.__ne__(neg_inf, inf)) + assert_false(FloatLiteral.__ne__(inf, inf)) + assert_true(FloatLiteral.__ne__(nan, 10.0)) + assert_true(FloatLiteral.__ne__(10.0, nan)) + assert_true(FloatLiteral.__ne__(nan, inf)) + assert_true(FloatLiteral.__ne__(inf, nan)) + assert_true(FloatLiteral.__ne__(neg_inf, nan)) + assert_true(FloatLiteral.__ne__(nan, neg_zero)) assert_true(FloatLiteral.__gt__(10.4, 4.4)) assert_true(FloatLiteral.__gt__(-4.4, -10.4)) assert_false(FloatLiteral.__gt__(0.0, 0.0)) assert_false(FloatLiteral.__gt__(4.4, 10.4)) + assert_false(FloatLiteral.__gt__(neg_inf, neg_inf)) + assert_false(FloatLiteral.__gt__(neg_zero, neg_zero)) + assert_false(FloatLiteral.__gt__(neg_zero, 0.0)) + assert_true(FloatLiteral.__gt__(inf, neg_inf)) + assert_false(FloatLiteral.__gt__(inf, inf)) + assert_false(FloatLiteral.__gt__(nan, 10.0)) + assert_false(FloatLiteral.__gt__(10.0, nan)) + assert_false(FloatLiteral.__gt__(nan, inf)) + assert_false(FloatLiteral.__gt__(inf, nan)) + assert_false(FloatLiteral.__gt__(neg_inf, nan)) + assert_false(FloatLiteral.__gt__(nan, neg_zero)) assert_true(FloatLiteral.__ge__(10.4, 4.4)) assert_true(FloatLiteral.__ge__(-4.4, -10.4)) assert_true(FloatLiteral.__ge__(4.4, 4.4)) assert_false(FloatLiteral.__ge__(4.4, 10.4)) + assert_true(FloatLiteral.__ge__(neg_inf, neg_inf)) + assert_true(FloatLiteral.__ge__(neg_zero, neg_zero)) + assert_true(FloatLiteral.__ge__(neg_zero, 0.0)) + assert_true(FloatLiteral.__ge__(inf, neg_inf)) + assert_true(FloatLiteral.__ge__(inf, inf)) + assert_false(FloatLiteral.__ge__(nan, 10.0)) + assert_false(FloatLiteral.__ge__(10.0, nan)) + assert_false(FloatLiteral.__ge__(nan, inf)) + assert_false(FloatLiteral.__ge__(inf, nan)) + assert_false(FloatLiteral.__ge__(neg_inf, nan)) + assert_false(FloatLiteral.__ge__(nan, neg_zero)) def main(): From 8cea188e6c0b592c8c8679f34337763604ea345c Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Fri, 16 Aug 2024 07:26:42 -0700 Subject: [PATCH 1401/2019] [External] [stdlib] Implement `collections.Counter` - Part 3 (#45377) [External] [stdlib] Implement `collections.Counter` - Part 3 Support for Python's [collections.Counter](https://docs.python.org/3/library/collections.html#collections.Counter) - Part 3 Pending to match Python signature: `__or__`, `__ior__` and `__neg__` Co-authored-by: Manuel Saelices Closes modularml/mojo#3266 MODULAR_ORIG_COMMIT_REV_ID: 57f6b3fe205946b3c664e7d1f7256473615173a5 --- stdlib/src/collections/counter.mojo | 77 ++++++++++++++++++++--- stdlib/test/collections/test_counter.mojo | 40 ++++++++++++ 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 8cf6f41c2d..e243fe3c19 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -267,7 +267,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): return +result^ # Remove zero and negative counts - fn __iadd__(inout self, other: Self) raises: + fn __iadd__(inout self, other: Self): """Add counts from another Counter to this Counter. Args: @@ -292,7 +292,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): return +result^ # Remove zero and negative counts - fn __isub__(inout self, other: Self) raises: + fn __isub__(inout self, other: Self): """Subtract counts from another Counter from this Counter. Args: @@ -301,7 +301,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): self.subtract(other) self._keep_positive() - fn __and__(self, other: Self) raises -> Self: + fn __and__(self, other: Self) -> Self: """Intersection: keep common elements with the minimum count. Args: @@ -316,11 +316,11 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): for key_ref in self.keys(): var key = key_ref[] if key in other: - result[key] = min(self[key], other[key]) + result[key] = min(self.get(key, 0), other.get(key, 0)) return result^ - fn __iand__(inout self, other: Self) raises: + fn __iand__(inout self, other: Self): """Intersection: keep common elements with the minimum count. Args: @@ -329,16 +329,59 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): for key_ref in self.keys(): var key = key_ref[] if key not in other: - _ = self.pop(key) + try: + _ = self.pop(key) + except: + pass # this should not happen else: - self[key] = min(self[key], other[key]) + self[key] = min(self.get(key, 0), other.get(key, 0)) - fn _keep_positive(inout self) raises: + fn __or__(self, other: Self) -> Self: + """Union: keep all elements with the maximum count. + + Args: + other: The other Counter to union with. + + Returns: + A new Counter with all elements and the maximum count of the two + Counters. + """ + var result = Counter[V]() + + for key_ref in self.keys(): + var key = key_ref[] + var newcount = max(self.get(key, 0), other.get(key, 0)) + if newcount > 0: + result[key] = newcount + + for key_ref in other.keys(): + var key = key_ref[] + if key not in self and other.get(key, 0) > 0: + result[key] = other.get(key, 0) + + return result^ + + fn __ior__(inout self, other: Self): + """Union: keep all elements with the maximum count. + + Args: + other: The other Counter to union with. + """ + for key_ref in other.keys(): + var key = key_ref[] + var newcount = max(self.get(key, 0), other.get(key, 0)) + if newcount > 0: + self[key] = newcount + + fn _keep_positive(inout self): """Remove zero and negative counts from the Counter.""" for key_ref in self.keys(): var key = key_ref[] - if self[key] <= 0: - _ = self.pop(key) + if self.get(key, 0) <= 0: + try: + _ = self.pop(key) + except: + pass # this should not happen # ===------------------------------------------------------------------=== # # Unary operators @@ -357,6 +400,20 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): result[item.key] = item.value return result^ + fn __neg__(self) -> Self: + """Substract from an empty Counter. Strips positive and zero counts, + and flips the sign on negative counts. + + Returns: + A new Counter with stripped counts and negative counts. + """ + var result = Counter[V]() + for item_ref in self.items(): + var item = item_ref[] + if item.value < 0: + result[item.key] = -item.value + return result + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/test/collections/test_counter.mojo b/stdlib/test/collections/test_counter.mojo index 54e984fcde..0abc8e1287 100644 --- a/stdlib/test/collections/test_counter.mojo +++ b/stdlib/test/collections/test_counter.mojo @@ -387,6 +387,44 @@ def test_counter_setitem(): assert_equal(c[3], 0) +def test_neg(): + var c = Counter[String]() + c["a"] = 1 + c["b"] = -2 + c["c"] = 3 + + var neg = -c + + assert_equal(neg["a"], 0) + assert_equal(neg["b"], 2) + assert_equal(neg["c"], 0) + + +def test_or(): + var c1 = Counter[String]() + c1["a"] = 1 + c1["b"] = 2 + + var c2 = Counter[String]() + c2["b"] = 3 + c2["c"] = 4 + c2["d"] = -1 + + var c3 = c1 | c2 + + assert_equal(c3["a"], 1) + assert_equal(c3["b"], 3) + assert_equal(c3["c"], 4) + assert_equal(c3["d"], 0) + + c1 |= c2 + + assert_equal(c1["a"], 1) + assert_equal(c1["b"], 3) + assert_equal(c1["c"], 4) + assert_equal(c1["d"], 0) + + def test_pop(): var counter = Counter[String]() counter["a"] = 1 @@ -440,6 +478,8 @@ def main(): test_len() test_lt_le_gt_and_ge() test_most_common() + test_neg() + test_or() test_pop() test_popitem() test_substract() From 441a74354a4fbf35c70fd88ebe6009dd6d16f27f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 16 Aug 2024 09:04:14 -0700 Subject: [PATCH 1402/2019] [stdlib] Enable replacement on StringLiteral MODULAR_ORIG_COMMIT_REV_ID: da45b706d17b3a8e3b32cc75e7927ed167175f50 --- stdlib/src/builtin/string_literal.mojo | 13 +++++++++++++ stdlib/test/builtin/test_string_literal.mojo | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 2d50ab137d..9c8e01dca0 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -369,6 +369,19 @@ struct StringLiteral( """ return StringRef(self).rfind(substr, start=start) + fn replace(self, old: StringLiteral, new: StringLiteral) -> StringLiteral: + """Return a copy of the string with all occurrences of substring `old` + if replaced by `new`. This operation only works in the param domain. + + Args: + old: The substring to replace. + new: The substring to replace with. + + Returns: + The string where all occurrences of `old` are replaced with `new`. + """ + return __mlir_op.`pop.string.replace`(self.value, old.value, new.value) + fn join[T: StringableCollectionElement](self, elems: List[T, *_]) -> String: """Joins string elements using the current string as a delimiter. diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 62da1e7294..aafe4b2567 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -94,6 +94,17 @@ def test_rfind(): assert_equal(-1, "abc".rfind("abcd")) +def test_replace(): + assert_equal("".replace("", "hello world"), "") + assert_equal("hello world".replace("", "something"), "hello world") + assert_equal("hello world".replace("world", ""), "hello ") + assert_equal("hello world".replace("world", "mojo"), "hello mojo") + assert_equal( + "hello world hello world".replace("world", "mojo"), + "hello mojo hello mojo", + ) + + def test_comparison_operators(): # Test less than and greater than assert_true(StringLiteral.__lt__("abc", "def")) @@ -184,6 +195,7 @@ def main(): test_contains() test_find() test_rfind() + test_replace() test_comparison_operators() test_hash() test_intable() From 9f717745db928eeae5e1a51256538c11f78374ae Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 16 Aug 2024 10:20:09 -0700 Subject: [PATCH 1403/2019] Revert "[Stdlib] Implement a non-intrinsic version of ctlz" MODULAR_ORIG_COMMIT_REV_ID: 98a2723ca02439a10bab2e7285259976320b706b --- stdlib/src/utils/string_slice.mojo | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 3c5171529e..473a9a68d6 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -20,6 +20,7 @@ from utils import StringSlice ``` """ +from bit import count_leading_zeros from utils import Span from collections.string import _isspace from collections import List @@ -30,29 +31,6 @@ alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" -fn _count_leading_zeros(b: SIMD[DType.uint8, _], /) -> __type_of(b): - var res = __type_of(b)() - - @parameter - for i in range(b.size): - var x = b[i] - if x == 0: - res[i] = bitwidthof[DType.uint8]() - continue - var n = Scalar[DType.uint8](0) - if (x & 0xF0) == 0: - n += 4 - x <<= 4 - if (x & 0xC0) == 0: - n += 2 - x <<= 2 - if (x & 0x80) == 0: - n += 1 - x <<= 1 - res[i] = n - return res - - fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): """UTF-8 byte type. @@ -67,7 +45,7 @@ fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): - 3 -> start of 3 byte long sequence. - 4 -> start of 4 byte long sequence. """ - return _count_leading_zeros(~(b & UInt8(0b1111_0000))) + return count_leading_zeros(~(b & UInt8(0b1111_0000))) fn _validate_utf8_simd_slice[ From eae56a0521a1b907b7a2276a00a4f3399d5f104f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 16 Aug 2024 20:24:05 -0700 Subject: [PATCH 1404/2019] [******][GPU] Reconcile the CPU time functions with the GPU MODULAR_ORIG_COMMIT_REV_ID: 93b02c7465a3fb7067bde01405c60a44a627bf90 --- stdlib/src/time/time.mojo | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 7baffc3a9f..101fc305d7 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -26,6 +26,7 @@ from sys import ( triple_is_nvidia_cuda, llvm_intrinsic, ) +from sys._assembly import inlined_assembly from math import floor from memory import UnsafePointer @@ -196,6 +197,14 @@ fn perf_counter_ns() -> Int: Returns: The current time in ns. """ + + @parameter + if triple_is_nvidia_cuda(): + return int( + inlined_assembly[ + "mov.u64 $0, %globaltimer;", UInt64, constraints="=l" + ]() + ) return _monotonic_nanoseconds() From 910c59b15e2872cabf33ba8a1becf430cf3c2024 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 16 Aug 2024 22:13:58 -0700 Subject: [PATCH 1405/2019] [******][GPU] Cleanup the strided_load / store operations, NFC MODULAR_ORIG_COMMIT_REV_ID: 87fe2cfdede5d8b3b9069e8604929f736d757161 --- docs/changelog.md | 3 +++ docs/manual/pointers.ipynb | 8 +++---- stdlib/src/memory/unsafe_pointer.mojo | 18 ++++++++-------- stdlib/src/sys/intrinsics.mojo | 30 ++++++++++----------------- stdlib/src/utils/string_slice.mojo | 2 +- stdlib/test/sys/test_intrinsics.mojo | 2 +- 6 files changed, 29 insertions(+), 34 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 8d057e4649..22dbd96427 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -638,6 +638,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` are removed from prelude. +- The `simd_strided_load()` and `simd_strided_store()` have been renamed to + `strided_load` and `strided_store` in `UnsafePointer`. + ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index c996d6ffd4..a302461d66 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -645,9 +645,9 @@ "\n", "\n", "The following function uses the \n", - "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#simd_strided_load)\n", + "[`strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_load)\n", "and \n", - "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#simd_strided_store)\n", + "[`strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_store)\n", "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", "that this function only handles images where the number of pixels is evenly\n", "divisible by eight.)" @@ -665,9 +665,9 @@ " # bytes per pixel, which is also the stride size\n", " bpp = 3\n", " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", - " red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n", + " red_values = ptr.offset(i).strided_load[width=simd_width](bpp)\n", " # Invert values and store them in their original locations\n", - " ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)" + " ptr.offset(i).strided_store[width=simd_width](~red_values, bpp)" ] }, { diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 9dbee3cce0..47e961ea3c 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -612,15 +612,15 @@ struct UnsafePointer[ ) @always_inline("nodebug") - fn simd_strided_load[ - type: DType, width: Int, T: Intable + fn strided_load[ + type: DType, T: Intable, //, width: Int ](self: UnsafePointer[Scalar[type], *_], stride: T) -> SIMD[type, width]: """Performs a strided load of the SIMD vector. Parameters: type: DType of returned SIMD value. - width: The SIMD width. T: The Intable type of the stride. + width: The SIMD width. Args: stride: The stride between loads. @@ -628,20 +628,20 @@ struct UnsafePointer[ Returns: A vector which is stride loaded. """ - return strided_load[type, width]( - self, int(stride), SIMD[DType.bool, width](1) - ) + return strided_load(self, int(stride), SIMD[DType.bool, width](True)) @always_inline("nodebug") - fn simd_strided_store[ - type: DType, width: Int, T: Intable + fn strided_store[ + type: DType, + T: Intable, //, + width: Int, ](self: UnsafePointer[Scalar[type], *_], val: SIMD[type, width], stride: T): """Performs a strided store of the SIMD vector. Parameters: type: DType of `val`, the SIMD value to store. - width: The SIMD width. T: The Intable type of the stride. + width: The SIMD width. Args: val: The SIMD value to store. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index fb45a1e12d..19a46701a2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -21,6 +21,7 @@ from sys import PrefetchLocality from .info import sizeof, triple_is_nvidia_cuda from ._assembly import inlined_assembly +import math from memory import AddressSpace, UnsafePointer @@ -833,7 +834,7 @@ fn _unsafe_aliasing_address_to_pointer[ @always_inline("nodebug") fn gather[ - type: DType, size: Int + type: DType, size: Int, // ]( owned base: SIMD[DType.index, size], mask: SIMD[DType.bool, size], @@ -906,7 +907,7 @@ fn gather[ @always_inline("nodebug") fn scatter[ - type: DType, size: Int + type: DType, size: Int, // ]( value: SIMD[type, size], owned base: SIMD[DType.index, size], @@ -1354,7 +1355,7 @@ fn compressed_store[ @always_inline("nodebug") fn strided_load[ - type: DType, simd_width: Int + type: DType, //, simd_width: Int ]( addr: UnsafePointer[Scalar[type], *_], stride: Int, @@ -1380,15 +1381,11 @@ fn strided_load[ if simd_width == 1: return addr.load() if mask else Scalar[type]() - alias IndexTy = SIMD[DType.index, simd_width] - var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", IndexTy, has_side_effect=False + var offset = int(addr) + stride * sizeof[type]() * math.iota[ + DType.index, simd_width ]() - var offset = IndexTy(int(addr)) + IndexTy(stride) * iota * IndexTy( - sizeof[type]() - ) var passthrough = SIMD[type, simd_width]() - return gather[type, simd_width](offset, mask, passthrough) + return gather(offset, mask, passthrough) # ===----------------------------------------------------------------------===# @@ -1398,7 +1395,7 @@ fn strided_load[ @always_inline("nodebug") fn strided_store[ - type: DType, simd_width: Int + type: DType, //, simd_width: Int ]( value: SIMD[type, simd_width], addr: UnsafePointer[Scalar[type], *_], @@ -1425,15 +1422,10 @@ fn strided_store[ addr.store(value[0]) return - alias IndexTy = SIMD[DType.index, simd_width] - var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", IndexTy, has_side_effect=False + var offset = int(addr) + stride * sizeof[type]() * math.iota[ + DType.index, simd_width ]() - var offset = IndexTy(int(addr)) + IndexTy(stride) * iota * IndexTy( - sizeof[type]() - ) - - scatter[type, simd_width](value, offset, mask) + scatter(value, offset, mask) # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 473a9a68d6..0edcaabceb 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -75,7 +75,7 @@ fn _validate_utf8_simd_slice[ @parameter if not remainder: - d = ptr.offset(idx).simd_strided_load[DType.uint8, width](1) + d = ptr.load[width=width](idx) else: debug_assert(iter_len > -1, "iter_len must be > -1") d = SIMD[DType.uint8, width](0) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 405d85f004..bc30c829d4 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -97,7 +97,7 @@ fn test_strided_load() raises: for i in range(size): vector[i] = i - var s = strided_load[DType.float32, 4](vector, 4) + var s = strided_load[4](vector, 4) assert_equal(s, SIMD[DType.float32, 4](0, 4, 8, 12)) vector.free() From 67b146edb10c24ece2aabe22f87d3a7cad7b87ca Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 17 Aug 2024 12:49:39 -0600 Subject: [PATCH 1406/2019] [mojo-lang] Allow implicit declaration of variables in `fn`s Mojo and Python both allow implicit declaration of variables within a `def`. This patch changes the compiler to allow them within an `fn` as well. The rationale here is that we haven't seen significant safety or correctness benefit from requiring explicit variable definitions, and this change makes the language more consistent and ergonomic. We do want systems programmers to use `fn` to signify that functions don't raise, and providing more strict handling of arguments (for example, `fn` statements still strictly scope bindings in `with` blocks). As such, we want `def` and `fn` to be able to be different, just not in this dimension. Mojo still supports `var` declarations and we have no plans to remove them. They are useful in cases where you want to reuse the same name for different purposes in different parts of a function, and are required for struct member definitions. MODULAR_ORIG_COMMIT_REV_ID: dc8d115339ea2a792fbd78d0bacda37a536abd74 --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 22dbd96427..055b68088e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,12 @@ what we publish. ### ⭐️ New +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed and + still denotes the declaration of a new variable with a scope (in both `def` + and `fn`). Relaxing this makes `fn` and `def` more similar, but they still + differ in other important ways. + - Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This feature provides more flexibility and guarantees around emplacing the result From 1bea12bb95ec05afdca2ebd4368e3a4fb951bed3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 17 Aug 2024 15:02:21 -0600 Subject: [PATCH 1407/2019] [mojo-stdlib] Work around lack of field-sensitive lifetimes. We currently treat "foo.field1" and "foo.field2" as having the same foo lifetime, so we cannot allow reading and R^X checking doesn't permit a read and write to the same lifetime. Work around this by adding a temporary. MODULAR_ORIG_COMMIT_REV_ID: 37d9393c7ea9214f23cd910c108e711a39e114d9 --- stdlib/src/tempfile/tempfile.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 244f1e0e31..833f5d5182 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -313,7 +313,10 @@ struct NamedTemporaryFile: # python implementation expands the path, # but several functions are not yet implemented in mojo # i.e. abspath, normpath - self._file_handle = FileHandle(self.name, mode=mode) + + # TODO(field sensitivity lifetimes), eliminate tmp. + var tmp = FileHandle(self.name, mode=mode) + self._file_handle = tmp^ return except: raise Error("Failed to create temporary file") From 6c27d359ad17065177e216e254f2796b0aed0493 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 17 Aug 2024 14:08:47 -0700 Subject: [PATCH 1408/2019] [Stdlib] Make getitem in UnsafePointer work with IntLike types MODULAR_ORIG_COMMIT_REV_ID: 38a3ad321d50f1c83f8daaadc90727a91f738911 --- stdlib/src/memory/unsafe_pointer.mojo | 11 ++++++++--- stdlib/test/memory/test_memory.mojo | 1 - stdlib/test/memory/test_unsafepointer.mojo | 1 - 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 47e961ea3c..5e3af35420 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -215,11 +215,16 @@ struct UnsafePointer[ return __mlir_op.`pop.offset`(self.address, idx.__mlir_index__()) @always_inline - fn __getitem__( - self, offset: Int - ) -> ref [MutableStaticLifetime, address_space._value.value] T: + fn __getitem__[ + IntLike: IntLike, // + ](self, offset: IntLike) -> ref [ + MutableStaticLifetime, address_space._value.value + ] T: """Return a reference to the underlying data, offset by the given index. + Parameters: + IntLike: The type of idx; either `Int` or `UInt`. + Args: offset: The offset index. diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 60b28f0ed1..ab055c54d5 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -474,7 +474,6 @@ def test_indexing(): for i in range(4): ptr[i] = i - assert_equal(ptr[True], 1) assert_equal(ptr[int(2)], 2) assert_equal(ptr[1], 1) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index adfc7569e1..1f4f04fe67 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -219,7 +219,6 @@ def test_indexing(): for i in range(4): ptr[i] = i - assert_equal(ptr[False], 0) assert_equal(ptr[int(1)], 1) assert_equal(ptr[3], 3) From 40ff16581226194de77d2f8487378019acc5f612 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 17 Aug 2024 18:01:10 -0700 Subject: [PATCH 1409/2019] [******][GPU] Simplify the exp function on the GPU MODULAR_ORIG_COMMIT_REV_ID: 38f5db98eecb39fa3a0a059499fa73a72bc8bdef --- stdlib/src/math/math.mojo | 135 +++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 46 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index a90a1528f6..3beb80e9c7 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -37,6 +37,7 @@ from bit import count_trailing_zeros from builtin._math import * from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply, _modf +from sys.info import _current_arch from utils import Span from utils.index import StaticIntTuple @@ -389,8 +390,25 @@ fn exp2[ @parameter if type is DType.float16: + + @parameter + if String(_current_arch()) == "sm_90a": + return _call_ptx_intrinsic[ + scalar_instruction="ex2.approx.f16", + vector2_instruction="ex2.approx.f16x2", + scalar_constraints="=h,h", + vector_constraints="=r,r", + ](x) + else: + return _call_ptx_intrinsic[ + instruction="ex2.approx.f16", constraints="=h,h" + ](x) + elif type is DType.bfloat16 and String(_current_arch()) == "sm_90a": return _call_ptx_intrinsic[ - instruction="ex2.approx.f16", constraints="=h,h" + scalar_instruction="ex2.approx.ftz.bf16", + vector2_instruction="ex2.approx.ftz.bf16x2", + scalar_constraints="=h,h", + vector_constraints="=r,r", ](x) elif type is DType.float32: return _call_ptx_intrinsic[ @@ -581,14 +599,8 @@ fn exp[ if triple_is_nvidia_cuda(): @parameter - if type is DType.float16: - return _call_ptx_intrinsic[ - instruction="ex2.approx.f16", constraints="=h,h" - ](x * inv_lg2) - elif type is DType.float32: - return _call_ptx_intrinsic[ - instruction="ex2.approx.ftz.f32", constraints="=f,f" - ](x * inv_lg2) + if type in (DType.float16, DType.float32): + return exp2(x * inv_lg2) @parameter if type not in (DType.float32, DType.float64): @@ -1373,43 +1385,6 @@ fn atan2[ # ===----------------------------------------------------------------------=== # -fn _call_ptx_intrinsic_scalar[ - type: DType, //, - *, - instruction: StringLiteral, - constraints: StringLiteral, -](arg: Scalar[type]) -> Scalar[type]: - return inlined_assembly[ - instruction + " $0, $1;", - Scalar[type], - constraints=constraints, - has_side_effect=False, - ](arg).cast[type]() - - -fn _call_ptx_intrinsic[ - type: DType, - simd_width: Int, //, - *, - instruction: StringLiteral, - constraints: StringLiteral, -](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - @parameter - if simd_width == 1: - return _call_ptx_intrinsic_scalar[ - instruction=instruction, constraints=constraints - ](arg[0]) - - var res = SIMD[type, simd_width]() - - @parameter - for i in range(simd_width): - res[i] = _call_ptx_intrinsic_scalar[ - instruction=instruction, constraints=constraints - ](arg[i]) - return res - - fn cos[ type: DType, simd_width: Int, // ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: @@ -2380,3 +2355,71 @@ fn _call_libm_impl[ res[i] = _external_call_const[libm_name, Scalar[result_type]](arg[i]) return res + + +fn _call_ptx_intrinsic_scalar[ + type: DType, //, + *, + instruction: StringLiteral, + constraints: StringLiteral, +](arg: Scalar[type]) -> Scalar[type]: + return inlined_assembly[ + instruction + " $0, $1;", + Scalar[type], + constraints=constraints, + has_side_effect=False, + ](arg).cast[type]() + + +fn _call_ptx_intrinsic[ + type: DType, + simd_width: Int, //, + *, + instruction: StringLiteral, + constraints: StringLiteral, +](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + @parameter + if simd_width == 1: + return _call_ptx_intrinsic_scalar[ + instruction=instruction, constraints=constraints + ](arg[0]) + + var res = SIMD[type, simd_width]() + + @parameter + for i in range(simd_width): + res[i] = _call_ptx_intrinsic_scalar[ + instruction=instruction, constraints=constraints + ](arg[i]) + return res + + +fn _call_ptx_intrinsic[ + type: DType, + simd_width: Int, //, + *, + scalar_instruction: StringLiteral, + vector2_instruction: StringLiteral, + scalar_constraints: StringLiteral, + vector_constraints: StringLiteral, +](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + @parameter + if simd_width == 1: + return _call_ptx_intrinsic_scalar[ + instruction=scalar_instruction, constraints=scalar_constraints + ](arg[0]) + + var res = SIMD[type, simd_width]() + + @parameter + for i in range(0, simd_width, 2): + res = res.insert[offset=i]( + inlined_assembly[ + vector2_instruction + " $0, $1;", + SIMD[type, 2], + constraints=vector_constraints, + has_side_effect=False, + ](arg).cast[type]() + ) + + return res From 1ad9fc9d688446dc2c394d855f9ae343fcc27e51 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 17 Aug 2024 18:01:20 -0700 Subject: [PATCH 1410/2019] [******][GPU] Use vector insert to fill the f32->bf16 cast operation MODULAR_ORIG_COMMIT_REV_ID: ac95b3796b024eb94d27cd51c181a942c7174c81 --- stdlib/src/builtin/simd.mojo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7025ccbf0f..d98213d9b3 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1528,9 +1528,7 @@ struct SIMD[type: DType, size: Int]( constraints="=r,f,f", has_side_effect=False, ](rebind[Float32](self[i + 1]), rebind[Float32](self[i])) - var val = bitcast[target, 2](bf16x2_as_uint32) - res[i] = val[0] - res[i + 1] = val[1] + res = res.insert[offset=i](bitcast[target, 2](bf16x2_as_uint32)) return res From c04b439b1c87af75c2d895ee7955595adc4eeddf Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 17 Aug 2024 18:49:13 -0700 Subject: [PATCH 1411/2019] [Stdlib] Fix the call to the vector.extract and insert intrinsics The offset argument must be 64bit which is not always true for Int. MODULAR_ORIG_COMMIT_REV_ID: 7923a40643f97bd32b9d95b3500a2b3988ee7273 --- stdlib/src/builtin/simd.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d98213d9b3..24483f939e 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1978,7 +1978,7 @@ struct SIMD[type: DType, size: Int]( "llvm.vector.extract", SIMD[type, output_width], has_side_effect=False, - ](self, offset) + ](self, Int64(offset)) @always_inline("nodebug") fn insert[*, offset: Int = 0](self, value: SIMD[type, _]) -> Self: @@ -2024,7 +2024,7 @@ struct SIMD[type: DType, size: Int]( return llvm_intrinsic[ "llvm.vector.insert", Self, has_side_effect=False - ](self, value, offset) + ](self, value, Int64(offset)) @always_inline("nodebug") fn join(self, other: Self) -> SIMD[type, 2 * size]: From da7648b89a3d727c083f9636a2d298dedc0bba21 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 18 Aug 2024 10:25:22 -0700 Subject: [PATCH 1412/2019] [mojo-lang] Implement exclusivity checking, currently as a warning. This enhances the Mojo compiler to notice when mutable values are aliased in function calls, diagnosing them with a warning (which needs to be upgraded to an error). Mojo (and [Rust](https://doc.rust-lang.org/beta/book/ch04-02-references-and-borrowing.html) and [Swift](https://swift.org/blog/swift-5-exclusivity/)) require that all mutable values have unique ownership to provide memory safety, and also performance benefits. This has been long-planned and this patch finally implements it. This is being phased in as a warning, because there are a couple of non-trivial uses of aliasing pointers that need to be sorted in the standard library, notably in the `_stable_sort` and `_quicksort` implementations. This fixes https://github.com/modularml/mojo/issues/1734 and possibly others. MODULAR_ORIG_COMMIT_REV_ID: cd9c6b3c45d4eea98fbc2a743a015c0cd80f2dee --- docs/changelog.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 055b68088e..28826cd3b2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,32 @@ what we publish. ### ⭐️ New +- Mojo now diagnoses "argument exclusivity" violations due to aliasing + references. Mojo requires references (including implicit references due to + borrowed/inout arguments) to be uniquely referenced (non-aliased) if mutable. + This is important for code safety, because it allows the compiler (and readers + of code) to understand where and when a value is mutated. It is also useful + for performance optimization because it allows the compiler to know that + accesses through immutable references cannot change behind the scenes. Here is + an invalid example: + + ```mojo + fn take_two_strings(a: String, inout b: String): b += a + + fn invalid_access(): + var my_string = String() + + + take_two_strings(my_string, my_string) + ``` + + This is similar to [Swift exclusivity + checking](https://swift.org/blog/swift-5-exclusivity/) and the [Rust + language](https://doc.rust-lang.org/beta/book/ch04-02-references-and-borrowing.html) + sometimes known as "aliasing xor mutability". That said, the Mojo + implementation details are somewhat different because lifetimes are embedded + in types. + - Mojo now allows implicit definitions of variables within a `fn` in the same way that has been allowed in a `def`. The `var` keyword is still allowed and still denotes the declaration of a new variable with a scope (in both `def` @@ -682,6 +708,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. def foo(**kwargs): ... # now works ``` +- [#1734](https://github.com/modularml/mojo/issues/1734) - Calling + `__copyinit__` on self causes crash. + - [#3142](https://github.com/modularml/mojo/issues/3142) - [QoI] Confusing `__setitem__` method is failing with a "must be mutable" error. From d7090beb466e251e40053af85adf9af9ed1e870b Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 18 Aug 2024 11:39:46 -0700 Subject: [PATCH 1413/2019] [mojo-docs] Fix up changelog thoughts. Finish up some changelog notes. MODULAR_ORIG_COMMIT_REV_ID: 3d19ce02a8c48222a0fd33749dda580a9e844604 --- docs/changelog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 28826cd3b2..104ebb5254 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -26,12 +26,15 @@ what we publish. an invalid example: ```mojo - fn take_two_strings(a: String, inout b: String): b += a + fn take_two_strings(a: String, inout b: String): + # Mojo knows 'a' and 'b' cannot be the same string. + b += a fn invalid_access(): var my_string = String() - + # error: passing `my_string` inout is invalid since it is also passed + # borrowed. take_two_strings(my_string, my_string) ``` From 2c020743c6c01c6ef5c14e480f73d469adae3d05 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 18 Aug 2024 12:45:22 -0700 Subject: [PATCH 1414/2019] [mojo-stdlib] Add a `List.swap_elements` method to address exclusivity issues. It is very common to want to swap two elements of a List, and `swap(a[i], a[j])` is now (correctly) diagnosed as being an exclusivity problem because the two elements may alias. To solve this, we introduce a new method that takes the elements by index and handles it internally. MODULAR_ORIG_COMMIT_REV_ID: 791e09ca01c36894f99cb66b24100014e17abe5d --- stdlib/src/collections/list.mojo | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9ef826e7ae..51442b9108 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -171,7 +171,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.append(value[]) fn __init__( - inout self: Self, + inout self, *, unsafe_pointer: UnsafePointer[T], size: Int, @@ -884,6 +884,32 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( count += 1 return count + fn swap_elements(inout self, elt_idx_1: Int, elt_idx_2: Int): + """Swaps elements at the specified indexes if they are different. + + ```mojo + var my_list = List[Int](1, 2, 3) + my_list.swap_elements(0, 2) + print(my_list) # 3, 2, 1 + ``` + + This is useful because `swap(my_list[i], my_list[j])` cannot be + supported by Mojo, because a mutable alias may be formed. + + Args: + elt_idx_1: The index of one element. + elt_idx_2: The index of the other element. + """ + debug_assert( + 0 <= elt_idx_1 < len(self) and 0 <= elt_idx_2 < len(self), + ( + "The indices provided to swap_elements must be within the range" + " [0, len(List)-1]" + ), + ) + if elt_idx_1 != elt_idx_2: + swap((self.data + elt_idx_1)[], (self.data + elt_idx_2)[]) + @always_inline fn unsafe_ptr(self) -> UnsafePointer[T]: """Retrieves a pointer to the underlying memory. From 8f23260bf6dd67dbc919c8f29894a2b0c38118c6 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 18 Aug 2024 16:24:43 -0700 Subject: [PATCH 1415/2019] [mojo-stdlib] Address quicksort exclusivity warnings. This patch introduces a `Span.get_immutable` method and changes the implementation of `_quicksort` to use it to avoid exclusivity issues that arise from the List of mutable spans. It would be nice to enhance Span with other slicing helpers like `span[:pivot]` etc rather than open coding them in this code, but this patch doesn't introduce them. MODULAR_ORIG_COMMIT_REV_ID: 6b0a4d5e3fa9eefce9a2bcf2a43fbf99dc94c9b4 --- stdlib/src/builtin/sort.mojo | 29 +++++++++++++++-------------- stdlib/src/collections/dict.mojo | 4 ---- stdlib/src/utils/span.mojo | 12 ++++++++++++ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index a1a3f50483..6f7f73d939 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -210,14 +210,19 @@ fn _quicksort[ var size = len(span) if size == 0: return - var stack = List[Span[type, lifetime]]( - capacity=_estimate_initial_height(size) - ) - stack.append(span) + + # Work with an immutable span so we don't run into exclusivity problems with + # the List[Span]. + var imm_span = span.get_immutable() + alias ImmSpan = __type_of(imm_span) + + var stack = List[ImmSpan](capacity=_estimate_initial_height(size)) + stack.append(imm_span) while len(stack) > 0: - var interval = stack.pop() - var ptr = interval.unsafe_ptr() - var len = len(interval) + var imm_interval = stack.pop() + var ptr = imm_interval.unsafe_ptr() + var len = len(imm_interval) + var interval = Span[type, lifetime](unsafe_ptr=ptr, len=len) if len <= 5: _delegate_small_sort[cmp_fn](interval) @@ -237,9 +242,7 @@ fn _quicksort[ var pivot = _quicksort_partition_left[cmp_fn](interval) if len > pivot + 2: stack.append( - Span[type, lifetime]( - unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1 - ) + ImmSpan(unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1) ) continue @@ -247,13 +250,11 @@ fn _quicksort[ if len > pivot + 2: stack.append( - Span[type, lifetime]( - unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1 - ) + ImmSpan(unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1) ) if pivot > 1: - stack.append(Span[type, lifetime](unsafe_ptr=ptr, len=pivot)) + stack.append(ImmSpan(unsafe_ptr=ptr, len=pivot)) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e4f0d66ef7..dec820729e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -72,10 +72,6 @@ struct _DictEntryIter[ forward: The iteration direction. `False` is backwards. """ - alias imm_dict_lifetime = __mlir_attr[ - `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` - ] - var index: Int var seen: Int var src: Reference[Dict[K, V], dict_lifetime] diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index dfc33ee8f6..161dc53fe0 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -23,6 +23,7 @@ from utils import Span from collections import InlineArray from memory import Reference from sys.intrinsics import _type_is_eq +from builtin.builtin_list import _lit_mut_cast @value @@ -308,3 +309,14 @@ struct Span[ """ for element in self: element[] = value + + fn get_immutable(self) -> Span[T, _lit_mut_cast[lifetime, False].result]: + """ + Return an immutable version of this span. + + Returns: + A span covering the same elements, but without mutability. + """ + return Span[T, _lit_mut_cast[lifetime, False].result]( + unsafe_ptr=self._data, len=self._len + ) From d1eab6a36bb3cdd1384a3db3f82edc6f64d3bf46 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 18 Aug 2024 18:13:24 -0700 Subject: [PATCH 1416/2019] [mojo-stdlib] Fix exclusivity warnings in `_stable_sort` This changes the lifetime mechanics in `_stable_sort` to pass exclusivity checking, telling the compiler that the temp buffer is a different lifetime than the input spans, and using immutable spans for the read-only part of the algorithm. This code is still incorrectly storing into the temp buffer even though it is uninitialized. MODULAR_ORIG_COMMIT_REV_ID: 65571d9b90fdc8ff6499d6c263b1956cf1fa81a1 --- stdlib/src/builtin/sort.mojo | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 6f7f73d939..74a3320693 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -264,19 +264,21 @@ fn _quicksort[ fn merge[ type: CollectionElement, - lifetime: MutableLifetime, //, + span_lifetime: ImmutableLifetime, + result_lifetime: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, ]( - span1: Span[type, lifetime], - span2: Span[type, lifetime], - result: Span[type, lifetime], + span1: Span[type, span_lifetime], + span2: Span[type, span_lifetime], + result: Span[type, result_lifetime], ): """Merge span1 and span2 into result using the given cmp_fn. The function will crash if result is not large enough to hold both span1 and span2. Parameters: type: Type of the spans. - lifetime: Lifetime of the spans. + span_lifetime: Lifetime of the input spans. + result_lifetime: Lifetime of the result Span. cmp_fn: Comparison functor of (type, type) capturing -> Bool type. Args: @@ -317,9 +319,10 @@ fn merge[ fn _stable_sort_impl[ type: CollectionElement, - lifetime: MutableLifetime, //, + span_life: MutableLifetime, + tmp_life: MutableLifetime, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, -](span: Span[type, lifetime], temp_buff: Span[type, lifetime]): +](span: Span[type, span_life], temp_buff: Span[type, tmp_life]): var size = len(span) if size <= 1: return @@ -336,7 +339,9 @@ fn _stable_sort_impl[ while j + merge_size < size: var span1 = span[j : j + merge_size] var span2 = span[j + merge_size : min(size, j + 2 * merge_size)] - merge[cmp_fn](span1, span2, temp_buff) + merge[cmp_fn]( + span1.get_immutable(), span2.get_immutable(), temp_buff + ) for i in range(merge_size + len(span2)): span[j + i] = temp_buff[i] j += 2 * merge_size @@ -349,7 +354,10 @@ fn _stable_sort[ cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, ](span: Span[type, lifetime]): var temp_buff = UnsafePointer[type].alloc(len(span)) - var temp_buff_span = Span[type, lifetime]( + # FIXME: This is incorrect: it is passing uninitialized data into + # _stable_sort_impl which then assigns into it with =, which will break with + # non-trivial types. + var temp_buff_span = Span[type, __lifetime_of(temp_buff)]( unsafe_ptr=temp_buff, len=len(span) ) _stable_sort_impl[cmp_fn](span, temp_buff_span) @@ -512,11 +520,11 @@ fn _sort[ _insertion_sort[cmp_fn](span) return + @parameter if stable: _stable_sort[cmp_fn](span) - return - - _quicksort[cmp_fn](span) + else: + _quicksort[cmp_fn](span) # TODO (MSTDL-766): The Int and Scalar[type] overload should be remove From dca821087d07260de9a0d996077c14b0b06ce4ac Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 18 Aug 2024 22:25:29 -0700 Subject: [PATCH 1417/2019] [mojo-lang] Revise memory transfer implementation. (#45488) This rewrites the implementation of the transfer operator (`x^`) for memory values. Instead of generating a `lit.transfer_mem_ownership`, just treat the value as an RValue, allowing it to be directly consumed by whatever it is passed to. This is better in a number of ways: 1) Simplicity: this eliminates `lit.transfer_mem_ownership` and supporting logic. 2) IR compactness: this removes the op, and a new temp lifetime. 3) Correctness: it isn't correct to generate a new lifetime referring to the same memory location! The correctness issue was shown when exclusivity checking didn't notice exclusivity violations with the move operation (as the testcase now shows). However, it is conceivable that one could construct situations where both the previous and the new lifetime were reused, which would have been a miscompilation. MODULAR_ORIG_COMMIT_REV_ID: 5a0d7aad39f2d9ae6779dfbf1bd1c4d7bf0e285c --- stdlib/src/collections/dict.mojo | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index dec820729e..8b2b24a8be 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -213,14 +213,15 @@ struct DictEntry[K: KeyElement, V: CollectionElement]( self.key = other.key self.value = other.value - fn reap_value(owned self) -> V: + fn reap_value(owned self) -> V as result: """Take the value from an owned entry. Returns: The value of the entry. """ + result = self.value^ __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) - return self.value^ + return alias _EMPTY = -1 From 33347307e31d1ced08d4f5858c5f35ede4d1e3c5 Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Mon, 19 Aug 2024 00:25:44 -0700 Subject: [PATCH 1418/2019] [External] [stdlib] Implement the builtin `input` function (#45492) [External] [stdlib] Implement the builtin `input` function Resolves https://github.com/modularml/mojo/issues/167 Adds the builtin `input` function, which behaves the same as Python. Co-authored-by: Mikhail Tavarez Closes modularml/mojo#3392 MODULAR_ORIG_COMMIT_REV_ID: 8a9d15d4a548e9a9d0b0ddfc85bde6fdcfa4bf05 --- docs/changelog.md | 10 +++ stdlib/src/builtin/io.mojo | 116 ++++++++++++++++++++++++++-- stdlib/src/prelude/__init__.mojo | 2 +- stdlib/test/builtin/test_stdin.mojo | 28 +++++++ 4 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 stdlib/test/builtin/test_stdin.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 104ebb5254..5806cde9b8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -283,6 +283,16 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. #True 1.125 2 ``` +- Added the builtin `input` function, which behaves the same as Python. + ([PR #3392](https://github.com/modularml/mojo/pull/3392) by [@thatstoasty](https://github.com/thatstoasty)) + + ```mojo + name = input("Enter your name: ") + print("Hello, " + name + "!") + ``` + + If the user enters "Mojo" it returns "Hello Mojo!" + - Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo to a specific version: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index e8b5e0e4ee..efe57248f0 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -46,9 +46,7 @@ fn _dup(fd: Int32) -> Int32: @value @register_passable("trivial") -struct _fdopen: - alias STDOUT = 1 - alias STDERR = 2 +struct _fdopen[mode: StringLiteral = "a"]: var handle: UnsafePointer[NoneType] fn __init__(inout self, stream_id: FileDescriptor): @@ -57,27 +55,99 @@ struct _fdopen: Args: stream_id: The stream id """ - alias mode = "a" - var handle: UnsafePointer[NoneType] @parameter if os_is_windows(): - handle = external_call["_fdopen", UnsafePointer[NoneType]]( + self.handle = external_call["_fdopen", UnsafePointer[NoneType]]( _dup(stream_id.value), mode.unsafe_cstr_ptr() ) else: - handle = external_call["fdopen", UnsafePointer[NoneType]]( + self.handle = external_call["fdopen", UnsafePointer[NoneType]]( _dup(stream_id.value), mode.unsafe_cstr_ptr() ) - self.handle = handle fn __enter__(self) -> Self: + """Open the file handle for use within a context manager""" return self fn __exit__(self): """Closes the file handle.""" _ = external_call["fclose", Int32](self.handle) + fn readline(self) -> String: + """Reads an entire line from stdin or until EOF. Lines are delimited by a newline character. + + Returns: + The line read from the stdin. + + Examples: + + ```mojo + from builtin.io import _fdopen + + var line = _fdopen["r"](0).readline() + print(line) + ``` + + Assuming the above program is named `my_program.mojo`, feeding it `Hello, World` via stdin would output: + + ```bash + echo "Hello, World" | mojo run my_program.mojo + + # Output from print: + Hello, World + ``` + . + """ + return self.read_until_delimiter("\n") + + fn read_until_delimiter(self, delimiter: String) -> String: + """Reads an entire line from a stream, up to the `delimiter`. + Does not include the delimiter in the result. + + Args: + delimiter: The delimiter to read until. + + Returns: + The text read from the stdin. + + Examples: + + ```mojo + from builtin.io import _fdopen + + var line = _fdopen["r"](0).read_until_delimiter(",") + print(line) + ``` + + Assuming the above program is named `my_program.mojo`, feeding it `Hello, World` via stdin would output: + + ```bash + echo "Hello, World" | mojo run my_program.mojo + + # Output from print: + Hello + ``` + """ + # getdelim will resize the buffer as needed. + var buffer = UnsafePointer[UInt8].alloc(1) + var bytes_read = external_call[ + "getdelim", + Int, + UnsafePointer[UnsafePointer[UInt8]], + UnsafePointer[UInt32], + Int, + UnsafePointer[NoneType], + ]( + UnsafePointer[UnsafePointer[UInt8]].address_of(buffer), + UnsafePointer[UInt32].address_of(UInt32(1)), + ord(delimiter), + self.handle, + ) + # Overwrite the delimiter with a null terminator. + buffer[bytes_read - 1] = 0 + return String(buffer, bytes_read) + # ===----------------------------------------------------------------------=== # # _flush @@ -401,3 +471,33 @@ fn print[ if not triple_is_nvidia_cuda(): if flush: _flush(file=file) + + +# ===----------------------------------------------------------------------=== # +# input +# ===----------------------------------------------------------------------=== # + + +fn input(prompt: String = "") -> String: + """Reads a line of input from the user. + + Reads a line from standard input, converts it to a string, and returns that string. + If the prompt argument is present, it is written to standard output without a trailing newline. + + Args: + prompt: An optional string to be printed before reading input. + + Returns: + A string containing the line read from the user input. + + Examples: + ```mojo + name = input("Enter your name: ") + print("Hello", name) + ``` + + If the user enters "Mojo" it prints "Hello Mojo". + """ + if prompt != "": + print(prompt, end="") + return _fdopen["r"](0).readline() diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 305c963b9d..11c151874b 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -47,7 +47,7 @@ from builtin.int import ( int, ) from builtin.int_literal import IntLiteral -from builtin.io import print +from builtin.io import print, input from builtin.len import Sized, UIntSized, SizedRaising, len from builtin.math import ( Absable, diff --git a/stdlib/test/builtin/test_stdin.mojo b/stdlib/test/builtin/test_stdin.mojo new file mode 100644 index 0000000000..ee50605383 --- /dev/null +++ b/stdlib/test/builtin/test_stdin.mojo @@ -0,0 +1,28 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: !windows +# RUN: echo "Hello, World" | %mojo %s + +from builtin.io import _fdopen +from testing import testing + + +fn test_stdin() raises: + # "Hello, World" piped from RUN command above + var stdin = _fdopen["r"](0) + testing.assert_equal(stdin.read_until_delimiter(","), "Hello") + testing.assert_equal(stdin.readline(), " World") + + +fn main() raises: + test_stdin() From 929adf5955ff2729e0bcf0d3c9af93c00e5458b3 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:14:05 -0400 Subject: [PATCH 1419/2019] [stdlib] Make docstring consistent in `UnsafePointer.load/store` MODULAR_ORIG_COMMIT_REV_ID: 12aaac5fd06db37a727c405b3d49ad29e6f461f3 --- stdlib/src/memory/unsafe_pointer.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 5e3af35420..599a3d489f 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -426,11 +426,10 @@ struct UnsafePointer[ Scalar[type] ]() if triple_is_nvidia_cuda() else 1, ](self: UnsafePointer[Scalar[type], *_]) -> SIMD[type, width]: - """Loads the value the pointer points to with the given offset. + """Loads the value the pointer points to. Constraints: The width and alignment must be positive integer values. - The offset must be integer. Parameters: type: The data type of SIMD vector. @@ -440,6 +439,7 @@ struct UnsafePointer[ Returns: The loaded value. """ + constrained[width > 0, "width must be a positive integer value"]() constrained[ alignment > 0, "alignment must be a positive integer value" ]() From 3135c063c610d00b5a606f8f289b22dd0aadb387 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 20 Aug 2024 10:59:08 -0700 Subject: [PATCH 1420/2019] Copy edit to explain Magic in get started guide. MODULAR_ORIG_COMMIT_REV_ID: 921616e430b5124ca1873fc863115197739fb95e --- docs/manual/get-started.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 0b3f59e32b..ab8463524d 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -15,9 +15,11 @@ Mojo](/mojo/manual/basics). ## 1. Create a new project -[`magic`](/magic) is our CLI tool that makes is really simple to get started -with Mojo. With just a couple commands, you'll create a new project with its -own virtual environment that includes everything you need to get started. +To create a new Mojo project, we'll use [Magic](/magic)—a virtual environment +manager and package manager (based on conda) that simplifies your development +workflow. With just a couple commands, the `magic` command-line tool creates a +new virtual environment that includes everything you need to get started with +Mojo. 1. Install Magic on macOS and Ubuntu with this command: From 1231131069db7d78be0e26c141bc90fe622b0fe1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 20 Aug 2024 14:01:04 -0600 Subject: [PATCH 1421/2019] [stdlib] Add an alignment parameter to `UnsafePointer` - Adds an `alignment` parameter to `UnsafePointer`. - `UnsafePointer.alloc` no longer takes in an `alignment` parameter, instead, it is specified in `UnsafePointer`. ```mojo UnsafePointer[type].alloc[alignment](x) # now becomes UnsafePointer[type, alignment].alloc(x) ``` - Adds a `with_alignment` member function supporting alignment cast for `UnsafePointer`. - `_malloc` now takes `alignment` as a parameter. - `stack_allocation` now returns an `UnsafePointer` with `alignment` specified. - You might want to tag `stack_allocation` calls with a `with_alignment()` to cast to default alignment. MODULAR_ORIG_COMMIT_REV_ID: ecd5462546eddc938b78db6683282768912ef2d4 --- docs/changelog.md | 9 +++ .../benchmarks/algorithm/bench_vectorize.mojo | 4 +- stdlib/src/memory/memory.mojo | 3 +- stdlib/src/memory/unsafe_pointer.mojo | 56 ++++++++----------- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/memory/test_unsafepointer.mojo | 10 ++-- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5806cde9b8..e34fa9fad5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -423,6 +423,15 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to allocate, the program will crash. +- `UnsafePointer` now has an `alignment` parameter to specify the static + alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes + in an alignment parameter, and the alignment should be specified in the type. + + ```mojo + UnsafePointer[type].alloc[alignment](x) # now becomes + UnsafePointer[type, alignment].alloc(x) + ``` + ### 🦋 Changed - The set of automatically imported entities (types, aliases, functions) into user's diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 6e11c09f77..8e34389372 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -71,8 +71,8 @@ fn test_vectorize[ constrained[(N % simd_width) == 0]() # Create a mem of size N alias buffer_align = 64 - var vector = UnsafePointer[Scalar[dtype]].alloc[alignment=buffer_align](N) - var result = UnsafePointer[Scalar[dtype]].alloc[alignment=buffer_align](N) + var vector = UnsafePointer[Scalar[dtype], alignment=buffer_align].alloc(N) + var result = UnsafePointer[Scalar[dtype], alignment=buffer_align].alloc(N) @always_inline @parameter diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 6732a1f889..642a5281c5 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -406,8 +406,9 @@ fn _malloc[ type: AnyType, /, *, + alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, address_space: AddressSpace = AddressSpace.GENERIC, -](size: Int, /, *, alignment: Int = -1) -> UnsafePointer[type, address_space]: +](size: Int, /) -> UnsafePointer[type, address_space, alignment=alignment]: @parameter if triple_is_nvidia_cuda(): constrained[ diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 599a3d489f..46b1310cc5 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -43,6 +43,7 @@ struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC, exclusive: Bool = False, + alignment: Int = alignof[T]() if triple_is_nvidia_cuda() else 1, ]( ImplicitlyBoolable, CollectionElement, @@ -58,8 +59,13 @@ struct UnsafePointer[ T: The type the pointer points to. address_space: The address space associated with the UnsafePointer allocated memory. exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. + alignment: The minimum alignment of this pointer known statically. """ + # ===-------------------------------------------------------------------===# + # Aliases + # ===-------------------------------------------------------------------===# + # Fields alias _mlir_type = __mlir_type[ `!kgen.pointer<`, @@ -73,6 +79,10 @@ struct UnsafePointer[ alias type = T + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + """The underlying pointer type.""" var address: Self._mlir_type """The underlying pointer.""" @@ -96,7 +106,7 @@ struct UnsafePointer[ self.address = value @always_inline - fn __init__(inout self, other: UnsafePointer[T, address_space, _]): + fn __init__(inout self, other: UnsafePointer[T, address_space, *_]): """Exclusivity parameter cast a pointer. Args: @@ -134,32 +144,9 @@ struct UnsafePointer[ @staticmethod @always_inline - fn alloc(count: Int, alignment: Int = alignof[T]()) -> Self: + fn alloc(count: Int) -> Self: """Allocate an array with specified or default alignment. - Args: - count: The number of elements in the array. - alignment: The alignment in bytes of the allocated memory. - - Returns: - The pointer to the newly allocated array. - """ - alias sizeof_t = sizeof[T]() - - constrained[sizeof_t > 0, "size must be greater than zero"]() - - return _malloc[T, address_space=address_space]( - sizeof_t * count, alignment=alignment - ) - - @staticmethod - @always_inline - fn alloc[alignment: Int = alignof[T]()](count: Int) -> Self: - """Allocate an array with specified or default alignment. - - Parameters: - alignment: The alignment in bytes of the allocated memory. - Args: count: The number of elements in the array. @@ -169,10 +156,9 @@ struct UnsafePointer[ alias sizeof_t = sizeof[T]() constrained[sizeof_t > 0, "size must be greater than zero"]() - constrained[alignment > 0, "alignment must be greater than zero"]() - return _malloc[T, address_space=address_space]( - sizeof_t * count, alignment=alignment + return _malloc[T, address_space=address_space, alignment=alignment]( + sizeof_t * count ) # ===-------------------------------------------------------------------===# @@ -852,7 +838,7 @@ struct UnsafePointer[ T: AnyType = Self.T, /, address_space: AddressSpace = Self.address_space, - ](self) -> UnsafePointer[T, address_space]: + ](self) -> UnsafePointer[T, address_space, alignment=alignment]: """Bitcasts a UnsafePointer to a different type. Parameters: @@ -864,7 +850,9 @@ struct UnsafePointer[ as the original UnsafePointer. """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[T, address_space]._mlir_type, + _type = UnsafePointer[ + T, address_space, alignment=alignment + ]._mlir_type, ](self.address) @always_inline("nodebug") @@ -872,7 +860,7 @@ struct UnsafePointer[ T: DType, /, address_space: AddressSpace = Self.address_space, - ](self) -> UnsafePointer[Scalar[T], address_space]: + ](self) -> UnsafePointer[Scalar[T], address_space, alignment=alignment]: """Bitcasts a UnsafePointer to a different type. Parameters: @@ -884,11 +872,13 @@ struct UnsafePointer[ as the original UnsafePointer. """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[Scalar[T], address_space]._mlir_type, + _type = UnsafePointer[ + Scalar[T], address_space, alignment=alignment + ]._mlir_type, ](self.address) @always_inline - fn destroy_pointee(self: UnsafePointer[_]): + fn destroy_pointee(self: UnsafePointer[T, alignment=alignment]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8bf54d1f51..5b7347d2a5 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -156,7 +156,7 @@ def test_issue_1625(): def test_issue_20421(): - var a = UnsafePointer[UInt8].alloc[alignment=64](16 * 64) + var a = UnsafePointer[UInt8, alignment=64].alloc(count=16 * 64) for i in range(16 * 64): a[i] = i & 255 var av16 = a.offset(128 + 64 + 4).bitcast[Int32]().load[width=4]() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 1f4f04fe67..48147d9a6f 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -183,19 +183,19 @@ def test_unsafepointer_address_space(): def test_unsafepointer_aligned_alloc(): alias alignment_1 = 32 - var ptr = UnsafePointer[UInt8].alloc[alignment=alignment_1](1) + var ptr = UnsafePointer[UInt8, alignment=alignment_1].alloc(1) var ptr_uint64 = UInt64(int(ptr)) ptr.free() assert_equal(ptr_uint64 % alignment_1, 0) alias alignment_2 = 64 - var ptr_2 = UnsafePointer[UInt8].alloc[alignment=alignment_2](1) + var ptr_2 = UnsafePointer[UInt8, alignment=alignment_2].alloc(1) var ptr_uint64_2 = UInt64(int(ptr_2)) ptr_2.free() assert_equal(ptr_uint64_2 % alignment_2, 0) alias alignment_3 = 128 - var ptr_3 = UnsafePointer[UInt8].alloc[alignment=alignment_3](1) + var ptr_3 = UnsafePointer[UInt8, alignment=alignment_3].alloc(1) var ptr_uint64_3 = UInt64(int(ptr_3)) ptr_3.free() assert_equal(ptr_uint64_3 % alignment_3, 0) @@ -236,11 +236,11 @@ def test_bool(): def test_alignment(): - var ptr = UnsafePointer[Int64].alloc[alignment=64](8) + var ptr = UnsafePointer[Int64, alignment=64].alloc(8) assert_equal(int(ptr) % 64, 0) ptr.free() - var ptr_2 = UnsafePointer[UInt8].alloc[alignment=32](32) + var ptr_2 = UnsafePointer[UInt8, alignment=32].alloc(32) assert_equal(int(ptr_2) % 32, 0) ptr_2.free() From 87aaa66def313df5f65facf0e018d10fd413dabd Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:44:41 -0400 Subject: [PATCH 1422/2019] [stdlib] Update `merge` to properly handle unitialized data Previously, `stable_sort` is creating a temporary buffer with unitialized data into `merge` which is assigning into unitialized memory with `=`. Since `=` will call the dtor of the data originally stored in the LHS, this will give UB when LHS is unitialized. We change `=` to use `UnsafePointer.init_pointee_copy` instead. MODULAR_ORIG_COMMIT_REV_ID: 3ee9a7a42c3c2822ac29b9e726179966babb38fd --- stdlib/src/builtin/sort.mojo | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 74a3320693..b8b62c5d7f 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -275,6 +275,8 @@ fn merge[ """Merge span1 and span2 into result using the given cmp_fn. The function will crash if result is not large enough to hold both span1 and span2. + Note that if result contains data previously, its destructor will not be called. + Parameters: type: Type of the spans. span_lifetime: Lifetime of the input spans. @@ -288,6 +290,7 @@ fn merge[ """ var span1_size = len(span1) var span2_size = len(span2) + var res_ptr = result.unsafe_ptr() debug_assert( span1_size + span2_size <= len(result), @@ -299,20 +302,20 @@ fn merge[ while i < span1_size: if j == span2_size: while i < span1_size: - result[k] = span1[i] + (res_ptr + k).init_pointee_copy(span1[i]) k += 1 i += 1 return if cmp_fn(span2[j], span1[i]): - result[k] = span2[j] + (res_ptr + k).init_pointee_copy(span2[j]) j += 1 else: - result[k] = span1[i] + (res_ptr + k).init_pointee_copy(span1[i]) i += 1 k += 1 while j < span2_size: - result[k] = span2[j] + (res_ptr + k).init_pointee_copy(span2[j]) k += 1 j += 1 @@ -354,9 +357,6 @@ fn _stable_sort[ cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, ](span: Span[type, lifetime]): var temp_buff = UnsafePointer[type].alloc(len(span)) - # FIXME: This is incorrect: it is passing uninitialized data into - # _stable_sort_impl which then assigns into it with =, which will break with - # non-trivial types. var temp_buff_span = Span[type, __lifetime_of(temp_buff)]( unsafe_ptr=temp_buff, len=len(span) ) From 872eeaec522d3acf576de3d4139defb17be03858 Mon Sep 17 00:00:00 2001 From: Lily Brown Date: Tue, 20 Aug 2024 15:23:15 -0700 Subject: [PATCH 1423/2019] Add `--filter` flag for `mojo test` MODULAR_ORIG_COMMIT_REV_ID: 942812a4f12090db5fb9d7b34a7579526f8897e8 --- docs/changelog.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index e34fa9fad5..a099a21230 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -27,7 +27,7 @@ what we publish. ```mojo fn take_two_strings(a: String, inout b: String): - # Mojo knows 'a' and 'b' cannot be the same string. + # Mojo knows 'a' and 'b' cannot be the same string. b += a fn invalid_access(): @@ -423,6 +423,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to allocate, the program will crash. +- The `mojo test` command now accepts a `--filter` option that will narrow the + set of tests collected and executed. The filter string is a POSIX extended + regular expression. + - `UnsafePointer` now has an `alignment` parameter to specify the static alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes in an alignment parameter, and the alignment should be specified in the type. From 7511255866584fc11e2ea25ff5f16ce3541c760d Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 21 Aug 2024 13:05:09 -0400 Subject: [PATCH 1424/2019] [stdlib][******][SDK] Add trivial type hint to some `List`s Turned some `List[Int]` into `List[Int, hint_trivial_type=True]`. Also fixes some arguments. MODULAR_ORIG_COMMIT_REV_ID: 75beff0dbb66a3b7cc81e6b3e7565b78e9af295a --- stdlib/src/utils/_serialize.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 44916920f6..f7ee606fe1 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -73,7 +73,7 @@ fn _serialize[ serialize_dtype: Bool = True, serialize_shape: Bool = True, serialize_end_line: Bool = True, -](ptr: UnsafePointer[Scalar[type], _], shape: List[Int]): +](ptr: UnsafePointer[Scalar[type], _], shape: List[Int, *_]): var rank = len(shape) if rank == 0: if serialize_end_line: From dc0dc0ae72f320501cc8434ec1efc37124a76523 Mon Sep 17 00:00:00 2001 From: Lily Brown Date: Wed, 21 Aug 2024 10:29:26 -0700 Subject: [PATCH 1425/2019] Add changelog entries about new `mojo test` functionality: - Compilation options (e.g. `-g`) - Debug options - New compilation model for unit tests MODULAR_ORIG_COMMIT_REV_ID: 5ae4ace1c668654361ebef6a3550d6bff3f2f4e1 --- docs/changelog.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index a099a21230..3580bbc5ab 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -427,6 +427,14 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. set of tests collected and executed. The filter string is a POSIX extended regular expression. +- The `mojo test` command now supports using the same compilation options as + `mojo build`. + +- You can now debug unit tests using `mojo test` by passing the `--debug` flag. + Most debug flags are supported; run `mojo test --help` for a full listing. + + Debugging doctests is not currently supported. + - `UnsafePointer` now has an `alignment` parameter to specify the static alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes in an alignment parameter, and the alignment should be specified in the type. @@ -699,6 +707,12 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - The `simd_strided_load()` and `simd_strided_store()` have been renamed to `strided_load` and `strided_store` in `UnsafePointer`. +- `mojo test` now uses the Mojo compiler for running unit tests. This will resolve + compilation issues that sometimes appeared, and will also improve overall test + times, since we will only compile unit tests once before executing all of them. + + These changes do not apply to doctests, due to their different semantics. + ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from From 2361fd293a93d96d530f749a752e37c2303891ca Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 21 Aug 2024 14:53:34 -0700 Subject: [PATCH 1426/2019] Add Magic config for Mojo examples MODULAR_ORIG_COMMIT_REV_ID: 20245fefb532de1a23bccec34c392dd36601c300 --- examples/.gitignore | 7 +++++++ examples/mojoproject.toml | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 examples/.gitignore create mode 100644 examples/mojoproject.toml diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000000..79d5db1e7f --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,7 @@ +# Pixi env +.pixi/ +pixi.lock + +# Magic env +.magic/ +magic.lock diff --git a/examples/mojoproject.toml b/examples/mojoproject.toml new file mode 100644 index 0000000000..e0541dd041 --- /dev/null +++ b/examples/mojoproject.toml @@ -0,0 +1,11 @@ +[project] +name = "Mojo examples" +version = "0.1.0" +description = "A collection of Mojo code examples" +authors = ["Modular "] +channels = ["conda-forge", "https://conda.modular.com/max"] +platforms = ["osx-arm64","linux-64","linux-aarch64"] + +[dependencies] +max = "*" +python = ">=3.11,<3.12" From c3956d63a07a4346ddaea6b73eed013da3910976 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 21 Aug 2024 17:55:14 -0600 Subject: [PATCH 1427/2019] [mojo-lang] Fix CheckLifetimes bug. This fixes a bug handling `lit.mark_destroyed` when it occurs before other consuming uses. I ran into this a few days ago when reworking the transfer operator. This enables restoring `reap_value` in dict.mojo. MODULAR_ORIG_COMMIT_REV_ID: bdc5408d5f572d14501d6dbf95595dcfb7ed3829 --- stdlib/src/collections/dict.mojo | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 8b2b24a8be..dec820729e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -213,15 +213,14 @@ struct DictEntry[K: KeyElement, V: CollectionElement]( self.key = other.key self.value = other.value - fn reap_value(owned self) -> V as result: + fn reap_value(owned self) -> V: """Take the value from an owned entry. Returns: The value of the entry. """ - result = self.value^ __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) - return + return self.value^ alias _EMPTY = -1 From 50de5c482ad324cd1a5b9a22047c4a293f1636b3 Mon Sep 17 00:00:00 2001 From: Helehex Date: Thu, 22 Aug 2024 11:43:06 -0700 Subject: [PATCH 1428/2019] [External] [stdlib] Fix TODO in `/io.mojo`. (#44958) Maybe there's an internal reason this couldn't be done, but i'll open it so it can at least be tracked. Co-authored-by: Connor Gray Closes modularml/mojo#3375 MODULAR_ORIG_COMMIT_REV_ID: cc3fa2124f2e21dae544ac87800d2e01912f3652 --- stdlib/src/builtin/io.mojo | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index efe57248f0..6075a0a933 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -386,9 +386,10 @@ fn _put(x: DType, file: FileDescriptor = stdout): _put(str(x).as_string_slice(), file=file) -# TODO: Constrain to `StringSlice[False, _]` @no_inline -fn _put(x: StringSlice, file: FileDescriptor = stdout): +fn _put[ + lif: ImmutableLifetime, // +](x: StringSlice[lif], file: FileDescriptor = stdout): # Avoid printing "(null)" for an empty/default constructed `String` var str_len = x.byte_length() From 3ca878d8584cdf0d0ddf62fb7c0eae3e54b9ec58 Mon Sep 17 00:00:00 2001 From: Helehex Date: Thu, 22 Aug 2024 16:49:14 -0700 Subject: [PATCH 1429/2019] [External] [stdlib] Make `Bool` Defaultable to False. (#45745) [External] [stdlib] Make `Bool` Defaultable to False Co-authored-by: Helehex Closes modularml/mojo#3389 MODULAR_ORIG_COMMIT_REV_ID: 748515c53fb56fdc42a54a445eb5e3a765cd74e8 --- stdlib/src/builtin/bool.mojo | 6 ++++++ stdlib/test/builtin/test_bool.mojo | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 836e3f4f08..b0241ed634 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -101,6 +101,7 @@ trait ImplicitlyBoolable(Boolable): struct Bool( CollectionElementNew, ComparableCollectionElement, + Defaultable, ImplicitlyBoolable, Indexer, Intable, @@ -113,6 +114,11 @@ struct Bool( var value: __mlir_type.i1 """The underlying storage of the boolean value.""" + @always_inline("nodebug") + fn __init__(inout self): + """Construct a default, `False` Bool.""" + self = False + @always_inline("nodebug") fn __init__(inout self, *, other: Self): """Explicitly construct a deep copy of the provided value. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 74071d7bcc..28400f5d90 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -15,6 +15,10 @@ from testing import assert_equal, assert_false, assert_true +def test_default(): + assert_equal(Bool(), False) + + def test_bool_cast_to_int(): assert_equal(False.__int__(), 0) assert_equal(True.__int__(), 1) @@ -146,6 +150,7 @@ def test_comparisons(): def main(): + test_default() test_bool_cast_to_int() test_bool_none() test_convert_from_implicitly_boolable() From 9224a74fd74e990e9fe901fef4fe1142ec50eac5 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 22 Aug 2024 19:17:50 -0500 Subject: [PATCH 1430/2019] [Examples] Mark `nbody` example as unsupported The `nbody` example is flaky right now on Linux only and is showing up in Mojo nightlies. Just mark it as unsupported for now until we have time to dig into it more to unblock Mojo nightlies. MODULAR_ORIG_COMMIT_REV_ID: cdfa6a173b298cd66a0027be757cdd572ab67bc0 --- examples/nbody.mojo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/nbody.mojo b/examples/nbody.mojo index 37ee4fd669..df7255ad43 100644 --- a/examples/nbody.mojo +++ b/examples/nbody.mojo @@ -10,6 +10,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +# UNSUPPORTED: target-aarch64 +# COM: currently flaky on Linux only # RUN: %mojo %s # This sample implements the nbody benchmarking in From 6cc797533d9d9ffd15a91e805e8f24bb0284bbea Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 22 Aug 2024 17:39:51 -0700 Subject: [PATCH 1431/2019] [External] [stdlib] Add more utf-8 validation unit tests (#45743) [External] [stdlib] Add more utf-8 validation unit tests Part of my work on utf-8 validation. The new algorithm is more complex. So, to ensure everything is working as expected, add some additional unit tests. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3405 MODULAR_ORIG_COMMIT_REV_ID: 0dbb5c80b8326d6063f64f5bd998e509d72ecf67 --- stdlib/test/utils/test_string_slice.mojo | 111 ++++++++++++++++++++++- 1 file changed, 107 insertions(+), 4 deletions(-) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index e51697104a..5591f031af 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -190,14 +190,14 @@ fn test_utf8_validation() raises: Lorem Ipsum段落的纸张,从而广泛普及了它的使用。最近,计算机桌面出版软件 למה אנו משתמשים בזה? זוהי עובדה מבוססת שדעתו של הקורא תהיה מוסחת על ידי טקטס קריא כאשר הוא יביט בפריסתו. המטרה בשימוש - ב- Lorem Ipsum הוא שיש לו פחות או יותר תפוצה של אותיות, בניגוד למלל ' יסוי + ב- Lorem Ipsum הוא שיש לו פחות או יותר תפוצה של אותיות, בניגוד למלל ' יסוי יסוי יסוי', ונותן חזות קריאה יותר.הרבה הוצאות מחשבים ועורכי דפי אינטרנט משתמשים כיום ב- Lorem Ipsum כטקסט ברירת המחדל שלהם, וחיפוש של 'lorem ipsum' יחשוף אתרים רבים בראשית - דרכם.גרסאות רבות נוצרו במהלך השנים, לעתים בשגגה - Lorem Ipsum е едноставен модел на текст кој се користел во печатарската + דרכם.גרסאות רבות נוצרו במהלך השנים, לעתים בשגגה + Lorem Ipsum е едноставен модел на текст кој се користел во печатарската индустрија. Lorem Ipsum - це текст-"риба", що використовується в друкарстві та дизайні. - Lorem Ipsum คือ เนื้อหาจำลองแบบเรียบๆ ที่ใช้กันในธุรกิจงานพิมพ์หรืองานเรียงพิมพ์ + Lorem Ipsum คือ เนื้อหาจำลองแบบเรียบๆ ที่ใช้กันในธุรกิจงานพิมพ์หรืองานเรียงพิมพ์ มันได้กลายมาเป็นเนื้อหาจำลองมาตรฐานของธุรกิจดังกล่าวมาตั้งแต่ศตวรรษที่ Lorem ipsum" في أي محرك بحث ستظهر العديد من المواقع الحديثة العهد في نتائج البحث. على مدى السنين @@ -291,6 +291,102 @@ def test_find(): ) +alias GOOD_SEQUENCES = List[String]( + "a", + "\xc3\xb1", + "\xe2\x82\xa1", + "\xf0\x90\x8c\xbc", + "안녕하세요, 세상", + "\xc2\x80", + "\xf0\x90\x80\x80", + "\xee\x80\x80", + "very very very long string 🔥🔥🔥", +) + + +# TODO: later on, don't use String because +# it will likely refuse non-utf8 data. +alias BAD_SEQUENCES = List[String]( + "\xc3\x28", # continuation bytes does not start with 10xx + "\xa0\xa1", # first byte is continuation byte + "\xe2\x28\xa1", # second byte should be continuation byte + "\xe2\x82\x28", # third byte should be continuation byte + "\xf0\x28\x8c\xbc", # second byte should be continuation byte + "\xf0\x90\x28\xbc", # third byte should be continuation byte + "\xf0\x28\x8c\x28", # fourth byte should be continuation byte + "\xc0\x9f", # overlong, could be just one byte + "\xf5\xff\xff\xff", # missing continuation bytes + "\xed\xa0\x81", # UTF-16 surrogate pair + "\xf8\x90\x80\x80\x80", # 5 bytes is too long + "123456789012345\xed", # Continuation bytes are missing + "123456789012345\xf1", # Continuation bytes are missing + "123456789012345\xc2", # Continuation bytes are missing + "\xC2\x7F", # second byte is not continuation byte + "\xce", # Continuation byte missing + "\xce\xba\xe1", # two continuation bytes missing + "\xce\xba\xe1\xbd", # One continuation byte missing + "\xce\xba\xe1\xbd\xb9\xcf", # fifth byte should be continuation byte + "\xce\xba\xe1\xbd\xb9\xcf\x83\xce", # missing continuation byte + "\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce", # missing continuation byte + "\xdf", # missing continuation byte + "\xef\xbf", # missing continuation byte +) + + +fn validate_utf8(slice: String) -> Bool: + return _is_valid_utf8(slice.unsafe_ptr(), slice.byte_length()) + + +def test_good_utf8_sequences(): + for sequence in GOOD_SEQUENCES: + assert_true(validate_utf8(sequence[])) + + +def test_bad_utf8_sequences(): + for sequence in BAD_SEQUENCES: + assert_false(validate_utf8(sequence[])) + + +def test_combination_good_utf8_sequences(): + # any combination of good sequences should be good + for i in range(0, len(GOOD_SEQUENCES)): + for j in range(i, len(GOOD_SEQUENCES)): + var sequence = GOOD_SEQUENCES[i] + GOOD_SEQUENCES[j] + assert_true(validate_utf8(sequence)) + + +def test_combination_bad_utf8_sequences(): + # any combination of bad sequences should be bad + for i in range(0, len(BAD_SEQUENCES)): + for j in range(i, len(BAD_SEQUENCES)): + var sequence = BAD_SEQUENCES[i] + BAD_SEQUENCES[j] + assert_false(validate_utf8(sequence)) + + +def test_combination_good_bad_utf8_sequences(): + # any combination of good and bad sequences should be bad + for i in range(0, len(GOOD_SEQUENCES)): + for j in range(0, len(BAD_SEQUENCES)): + var sequence = GOOD_SEQUENCES[i] + BAD_SEQUENCES[j] + assert_false(validate_utf8(sequence)) + + +def test_combination_10_good_utf8_sequences(): + # any 10 combination of good sequences should be good + for i in range(0, len(GOOD_SEQUENCES)): + for j in range(i, len(GOOD_SEQUENCES)): + var sequence = GOOD_SEQUENCES[i] * 10 + GOOD_SEQUENCES[j] * 10 + assert_true(validate_utf8(sequence)) + + +def test_combination_10_good_10_bad_utf8_sequences(): + # any 10 combination of good and bad sequences should be bad + for i in range(0, len(GOOD_SEQUENCES)): + for j in range(0, len(BAD_SEQUENCES)): + var sequence = GOOD_SEQUENCES[i] * 10 + BAD_SEQUENCES[j] * 10 + assert_false(validate_utf8(sequence)) + + fn main() raises: test_string_literal_byte_slice() test_string_byte_slice() @@ -300,3 +396,10 @@ fn main() raises: test_slice_bool() test_utf8_validation() test_find() + test_good_utf8_sequences() + test_bad_utf8_sequences() + test_combination_good_utf8_sequences() + test_combination_bad_utf8_sequences() + test_combination_good_bad_utf8_sequences() + test_combination_10_good_utf8_sequences() + test_combination_10_good_10_bad_utf8_sequences() From bb97ae3608461ba35fd5b9d81dad0c6c853e44a9 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Thu, 22 Aug 2024 21:00:17 -0400 Subject: [PATCH 1432/2019] [vscode][mojo] Rename --rpc to --vscode. MODULAR_ORIG_COMMIT_REV_ID: 7a52970a5c947bca81b8ce997ea4e1639f62ef9d --- docs/tools/debugging.ipynb | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 3136b6d6af..b96744a80e 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -175,6 +175,11 @@ "File** action described in [Quick run or debug](#quick-run-or-debug)." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, { "cell_type": "markdown", "metadata": {}, @@ -184,11 +189,10 @@ "Use the `mojo debug` command to start a debug session from the command line. You\n", "can choose from two debugging interfaces:\n", "\n", - "- With the `--rpc` flag, `mojo debug` starts an RPC debug session in an external\n", - " editor with an active RPC debug server. If you have VS Code running, this\n", - " starts a debug session in VS Code. \n", + "- With the `--vscode` flag, `mojo debug` starts a debug session on VS Code if\n", + " it's running and the Mojo extension is enabled.\n", "\n", - "- Without the `--rpc` flag, `mojo debug` starts a command-line [LLDB \n", + "- Without the `--vscode` flag, `mojo debug` starts a command-line [LLDB \n", " debugger](https://lldb.llvm.org/) session.\n", "\n", "You can choose to build and debug a Mojo file, run and debug a compiled binary,\n", @@ -196,9 +200,9 @@ "\n", ":::note Environment variables\n", "\n", - "When you debug a program from the command line using `--rpc`, the program runs\n", + "When you debug a program from the command line using `--vscode`, the program runs\n", "with the environment variables set in the terminal. When launching from inside\n", - "VS Code, the environment is defined by the VS Code [launch \n", + "VS Code via the GUI, the environment is defined by the VS Code [launch \n", "configuration](#launch-configurations).\n", "\n", ":::\n", @@ -212,13 +216,13 @@ "terminal or an external shell):\n", "\n", "```bash\n", - "mojo debug --rpc myproject.mojo\n", + "mojo debug --vscode myproject.mojo\n", "```\n", "\n", "Or to debug a compiled binary:\n", "\n", "```bash\n", - "mojo debug --rpc myproject\n", + "mojo debug --vscode myproject\n", "```\n", "\n", "\n", @@ -234,16 +238,21 @@ "process ID or process name on the command line:\n", "\n", "```bash\n", - "mojo debug --rpc --pid \n", + "mojo debug --vscode --pid \n", "```\n", "\n", "Or:\n", "\n", "```bash\n", - "mojo debug --rpc --process-name \n", + "mojo debug --vscode --process-name \n", "```" ] }, + { + "cell_type": "raw", + "metadata": {}, + "source": [] + }, { "cell_type": "markdown", "metadata": {}, From 83d6a67792367389524240eba8dfd7e71be645fb Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 22 Aug 2024 19:19:51 -0700 Subject: [PATCH 1433/2019] Copyedits for Magic install MODULAR_ORIG_COMMIT_REV_ID: 03281c12c0dd66048b0ab6795f7fccf4f1b17d26 --- docs/manual/get-started.mdx | 52 +++++++------------------------------ 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index ab8463524d..fb41d4f779 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -8,57 +8,23 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import MaxInstall from '@site/src/components/MaxInstall'; -On this page, we'll show you how to create the classic "Hello -world" starter program with Mojo, in three different ways. If you'd rather read -how to write Mojo code beyond just printing text, see the [introduction to -Mojo](/mojo/manual/basics). +On this page, we'll show you how to create the classic "Hello world" starter +program with Mojo. If you'd rather read how to write Mojo code, see the +[introduction to Mojo](/mojo/manual/basics). + +By installing Mojo, you understand and agree to our [software +license](https://www.modular.com/legal/max). ## 1. Create a new project To create a new Mojo project, we'll use [Magic](/magic)—a virtual environment -manager and package manager (based on conda) that simplifies your development -workflow. With just a couple commands, the `magic` command-line tool creates a -new virtual environment that includes everything you need to get started with -Mojo. +manager and package manager based on conda. -1. Install Magic on macOS and Ubuntu with this command: +1. Install Magic on macOS or Ubuntu Linux with this command: - Then run the appropriate `source` command for your terminal so that `magic` - is visible in your `PATH`: - - - - - ```sh - BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) - source "$BASHRC" - ``` - - - - - ```sh - source ~/.zshrc - ``` - - - - - ```sh - source ~/.config/fish/config.fish - ``` - - - - - ```sh - source ~/.tcshrc - ``` - - - + Then run the `source` command printed in your terminal. 2. Create a Mojo project called "hello-world": From b431a0a6bf6d1bd993061ac5dacd1c8a5bb6e148 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 23 Aug 2024 19:52:54 -0500 Subject: [PATCH 1434/2019] [Examples] Remove stale sharing notebooks This sharing notebooks refers to something specific for the Mojo playground which has been removed. So, remove this stale notebook and directory to avoid confusion. MODULAR_ORIG_COMMIT_REV_ID: 77070ade6860132a2fac8a52cd690e01c3cb7eb8 --- .../notebooks/help/sharing-notebooks.ipynb | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 examples/notebooks/help/sharing-notebooks.ipynb diff --git a/examples/notebooks/help/sharing-notebooks.ipynb b/examples/notebooks/help/sharing-notebooks.ipynb deleted file mode 100644 index 8858160b7e..0000000000 --- a/examples/notebooks/help/sharing-notebooks.ipynb +++ /dev/null @@ -1,64 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Sharing notebooks\n", - "\n", - "You can share your Mojo notebooks with other Mojo Playground users.\n", - "\n", - "## Share a Mojo notebook\n", - "\n", - "1. Save a notebook in the `shared` directory. \n", - "2. Right-click on the file and select **Copy Sharable link**.\n", - "3. Share the link.\n", - "4. To stop sharing, remove the file from the `shared` directory.\n", - "\n", - "![](https://docs.modular.com/static/images/playground/copy-sharable-link.png)\n", - "\n", - "\n", - "## Copy a shared Mojo notebook\n", - "\n", - "1. Open the shared link in a browser. \n", - "2. By default, Mojo Playground always opens the `HelloMojo` notebook when\n", - "it opens, so you might need to click the **Preview** tab to see the\n", - "shared notebook.\n", - "3. Click **Import** at the top of the notebook window. This makes a copy\n", - "of the file on your Mojo Playground volume. If the original author makes\n", - "any changes (including removing the file), it will not affect your imported\n", - "copy.\n", - "\n", - "![](https://docs.modular.com/static/images/playground/save-shared-notebook.png)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Caveats\n", - "\n", - "- Only files ending with `.ipynb` can be shared.\n", - "- The maximum sharable notebook size is 256 KiB.\n", - "- You can click **Copy Sharable link** on any file, but links for files that are\n", - " not in the `shared` directory will not work (they will 404)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From fa88b51992da333966177d72c27341773a02ada8 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 23 Aug 2024 19:57:20 -0700 Subject: [PATCH 1435/2019] [******] Change load[1] to be just load, NFC MODULAR_ORIG_COMMIT_REV_ID: f487cfd2de3cc95c4fe7395596a1c22b782cad0c --- examples/matmul.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index f8c39bf21b..8eb6dc0944 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -68,15 +68,15 @@ struct Matrix[rows: Int, cols: Int]: return Self(data) fn __getitem__(self, y: Int, x: Int) -> Scalar[type]: - return self.load[1](y, x) + return self.load(y, x) fn __setitem__(inout self, y: Int, x: Int, val: Scalar[type]): - self.store[1](y, x, val) + self.store(y, x, val) - fn load[nelts: Int](self, y: Int, x: Int) -> SIMD[type, nelts]: + fn load[nelts: Int = 1](self, y: Int, x: Int) -> SIMD[type, nelts]: return self.data.load[width=nelts](y * self.cols + x) - fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]): + fn store[nelts: Int = 1](self, y: Int, x: Int, val: SIMD[type, nelts]): self.data.store[width=nelts](y * self.cols + x, val) From a4d51f0fd7ef05d020096a4f65a39e90c4045094 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 24 Aug 2024 08:16:54 -0700 Subject: [PATCH 1436/2019] [******] Introduce a memset zero with constant count MODULAR_ORIG_COMMIT_REV_ID: 8d29efcb45fafdea934f6ad14da395aa0f6bdd17 --- stdlib/src/memory/memory.mojo | 32 ++++++++++++++++++++++++++++- stdlib/test/memory/test_memory.mojo | 10 +++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 642a5281c5..dcf1a17621 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -309,7 +309,7 @@ fn memset[ @always_inline fn memset_zero[ - type: AnyType, address_space: AddressSpace + type: AnyType, address_space: AddressSpace, // ](ptr: UnsafePointer[type, address_space], count: Int): """Fills memory with zeros. @@ -324,6 +324,36 @@ fn memset_zero[ memset(ptr, 0, count) +@always_inline +fn memset_zero[ + type: DType, address_space: AddressSpace, //, *, count: Int +](ptr: UnsafePointer[Scalar[type], address_space]): + """Fills memory with zeros. + + Parameters: + type: The element type. + address_space: The address space of the pointer. + count: Number of elements to fill (in elements, not bytes). + + Args: + ptr: UnsafePointer to the beginning of the memory block to fill. + """ + alias simd_width = simdwidthof[type]() + alias vector_end = _align_down(count, simd_width) + + @parameter + if count > 128: + return memset_zero(ptr, count) + + @parameter + for i in range(0, vector_end, simd_width): + ptr.store(i, SIMD[type, simd_width](0)) + + @parameter + for i in range(vector_end, count): + ptr.store(i, 0) + + # ===----------------------------------------------------------------------===# # stack_allocation # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index ab055c54d5..08466b18ef 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -293,10 +293,20 @@ def test_memset(): assert_equal(buf0.load(0), 16843009) memset(buf0, -1, 2) assert_equal(buf0.load(0), -1) + buf0.free() var buf1 = UnsafePointer[Int8].alloc(2) memset(buf1, 5, 2) assert_equal(buf1.load(0), 5) + buf1.free() + + var buf3 = UnsafePointer[Int32].alloc(2) + memset(buf3, 1, 2) + memset_zero[count=2](buf3) + assert_equal(buf3.load(0), 0) + assert_equal(buf3.load(1), 0) + buf3.free() + _ = pair From 6933d2f1705bd97977e4e30352d13cb2940c59ea Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Sat, 24 Aug 2024 13:47:39 -0500 Subject: [PATCH 1437/2019] [Examples] Mark nbody example as unsupported The nbody example is flaky right now on Linux only and is showing up in Mojo nightlies. Just mark it as unsupported for all of `system-linux` for now until we have time to dig into it more to unblock Mojo nightlies. MODULAR_ORIG_COMMIT_REV_ID: 98ff9dfb5dd02267247af7f489fb5b5aaa23a972 --- examples/nbody.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nbody.mojo b/examples/nbody.mojo index df7255ad43..694f81be68 100644 --- a/examples/nbody.mojo +++ b/examples/nbody.mojo @@ -10,8 +10,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# UNSUPPORTED: target-aarch64 -# COM: currently flaky on Linux only +# UNSUPPORTED: system-linux +# COM: currently flaky on Linux only, see SDLC-1080 # RUN: %mojo %s # This sample implements the nbody benchmarking in From f98562573125fa01c53483e52d2f3a945f61296b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 24 Aug 2024 11:57:24 -0700 Subject: [PATCH 1438/2019] [KGEN][GPU] Make the name for global_alloc explicit (#45904) MODULAR_ORIG_COMMIT_REV_ID: 1fdb01e17d53c67afac8155ef90a026878428acf --- stdlib/src/memory/memory.mojo | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index dcf1a17621..6079df384a 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -28,7 +28,7 @@ from sys import ( external_call, simdwidthof, ) - +from collections import Optional from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace @@ -390,6 +390,7 @@ fn stack_allocation[ count: Int, type: AnyType, /, + name: Optional[StringLiteral] = None, alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> UnsafePointer[type, address_space]: @@ -399,6 +400,7 @@ fn stack_allocation[ Parameters: count: Number of elements to allocate memory for. type: The data type of each element. + name: The name of the global variable (only honored in certain cases). alignment: Address alignment of the allocated data. address_space: The address space of the pointer. @@ -411,7 +413,9 @@ fn stack_allocation[ _GPUAddressSpace.SHARED, _GPUAddressSpace.PARAM, ): + alias global_name = name.value() if name else "_global_alloc" return __mlir_op.`pop.global_alloc`[ + name = global_name.value, count = count.value, _type = UnsafePointer[type, address_space]._mlir_type, alignment = alignment.value, From 8c908d7b803705e169352481d7731cec4cf655ea Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Sat, 24 Aug 2024 15:20:15 -0500 Subject: [PATCH 1439/2019] [stdlib] Move Mojo public examples Internally reorganize some of the Mojo public examples. MODULAR_ORIG_COMMIT_REV_ID: 5d44e3a1f5e4b6a9c3e0ecd603a2c08c5566bd83 --- examples/ProcessNotebooks.cmake | 94 --------------------------------- 1 file changed, 94 deletions(-) delete mode 100644 examples/ProcessNotebooks.cmake diff --git a/examples/ProcessNotebooks.cmake b/examples/ProcessNotebooks.cmake deleted file mode 100644 index 84e2da989b..0000000000 --- a/examples/ProcessNotebooks.cmake +++ /dev/null @@ -1,94 +0,0 @@ -##===----------------------------------------------------------------------===## -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -##===----------------------------------------------------------------------===## - -# Read the notebook and strip out the CHECK lines first thing. -file(READ "${INPUT_NOTEBOOK}" fileContents) -string(REGEX REPLACE - "\n +\" *#\\| CHECK[^\"]*\"," - "" - fileContents - "${fileContents}") - - -# Write the file without the CHECK lines, but with YAML. -get_filename_component(notebookDir "${OUTPUT_NOTEBOOK}" DIRECTORY) -get_filename_component(notebookName "${OUTPUT_NOTEBOOK}" NAME) -set(WEBSITE_NOTEBOOK ${notebookDir}/nocheckyesyaml/${notebookName}) -file(WRITE "${WEBSITE_NOTEBOOK}" "${fileContents}") -# For the version of notebooks going to the docs website, -# find cells with "REMOVE_FOR_WEBSITE" and strip the entire cell -file(MAKE_DIRECTORY ${notebookDir}/nocheckyesyaml/stripped/) -set(STRIPPED_NOTEBOOK ${notebookDir}/nocheckyesyaml/stripped/${notebookName}) -execute_process( - COMMAND sh -c "cat ${WEBSITE_NOTEBOOK} | jq '.cells = [.cells[] | select(.source[0] | test(\"REMOVE_FOR_WEBSITE\")? | not)]' > ${STRIPPED_NOTEBOOK}" -) -file(REMOVE ${WEBSITE_NOTEBOOK}) # Don't need the "nocheckyesyaml" file anymore - -# For the version of notebooks going to GitHub and the Playground, -# just remove the comment for "REMOVE_FOR_WEBSITE" (leaving the cell intact) -string(REGEX REPLACE - "\n +\" *\\[\\/\\/\\]: # REMOVE_FOR_WEBSITE[^\"]*\"," - "" - fileContents - "${fileContents}") - -# Get the first 'raw' cell. That will be the front matter. -string(JSON numCells LENGTH "${fileContents}" "cells") -math(EXPR numCellsMinus1 "${numCells} - 1") -foreach(i RANGE 0 ${numCellsMinus1}) - string(JSON cellType GET "${fileContents}" "cells" ${i} "cell_type") - if ("${cellType}" STREQUAL "raw") - string(JSON frontMatter GET "${fileContents}" "cells" ${i} "source") - break() - endif() -endforeach() - -# If we have front matter, check if it's valid YAML. -if (NOT ("${frontMatter}" STREQUAL "")) - string(JSON frontMatterArrayLen LENGTH "${frontMatter}") - - # Match the first source line to the YAML start token. - string(JSON srcLine GET "${frontMatter}" 0) - string(REGEX MATCH "---" yamlStart "${srcLine}") - - if ("${yamlStart}" STREQUAL "---") - set(frontMatterIsYaml TRUE) - endif() - - # Match the inner source lines to YAML key/value lines. - math(EXPR arrayLenMinus2 "${frontMatterArrayLen} - 2") - foreach(i RANGE 1 ${arrayLenMinus2}) - string(JSON srcLine GET "${frontMatter}" ${i}) - string(REGEX MATCH "[a-z]+:.*\n" yamlKVLine "${srcLine}") - string(REGEX MATCH "- .*\n" yamlArrayLine "${srcLine}") - if (("${yamlKVLine}" STREQUAL "") AND ("${yamlArrayLine}" STREQUAL "")) - set(frontMatterIsYaml FALSE) - endif() - endforeach() - - # Finally, match the last line to the YAML end token. - math(EXPR arrayLenMinus1 "${frontMatterArrayLen} - 1") - string(JSON srcLine GET "${frontMatter}" ${arrayLenMinus1}) - string(REGEX MATCH "---" yamlEnd "${srcLine}") - if ("${yamlEnd}" STREQUAL "") - set(frontMatterIsYaml FALSE) - endif() - - # OK great - if it is in fact YAML, we're good to remove it. - if (frontMatterIsYaml) - string(JSON fileContents REMOVE "${fileContents}" "cells" 0) - endif() -endif() - -# Write the final output. -file(WRITE "${OUTPUT_NOTEBOOK}" "${fileContents}") From 370daea60c3d7ae6ce908edeb97af93d5fc27b23 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 25 Aug 2024 16:28:29 -0700 Subject: [PATCH 1440/2019] [mojo-tooling] Fix docgen of 'ref' arguments and results. This fixes docgen to work nicely with 'ref' arguments and their results, and handles lifetimes/addrspaces prettily. This fixes MOTO-516 MODULAR_ORIG_COMMIT_REV_ID: 3bce97dbbc52a1ae21d8c3f6a771b9306aac792f --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 3580bbc5ab..83ac4a8820 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -748,6 +748,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. def foo(**kwargs): ... # now works ``` +- Mojo now prints `ref` arguments and results in generated documentation + correctly. + - [#1734](https://github.com/modularml/mojo/issues/1734) - Calling `__copyinit__` on self causes crash. From a5b0135d9124683993375886d9c18b88a29957b0 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 26 Aug 2024 09:31:16 -0700 Subject: [PATCH 1441/2019] [External] [stdlib] Implemented `.lower()` and `.upper()` methods to string literals [External] [stdlib] Implemented `.lower()` and `.upper()` methods to string literals It was implemented in Strings but not for String literals, while in Python is supported: ```python >>> "hello".upper() 'HELLO' ``` Co-authored-by: Manuel Saelices Closes modularml/mojo#3413 MODULAR_ORIG_COMMIT_REV_ID: 1b9b65b379f513701ddfa9af70fab2605eff71d7 --- stdlib/src/builtin/string_literal.mojo | 20 ++++++++++++++++++++ stdlib/test/builtin/test_string_literal.mojo | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 9c8e01dca0..048bbd4ac1 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -405,3 +405,23 @@ struct StringLiteral( result += str(e[]) return result + + fn lower(self) -> String: + """Returns a copy of the string literal with all cased characters + converted to lowercase. + + Returns: + A new string where cased letters have been converted to lowercase. + """ + + return str(self).lower() + + fn upper(self) -> String: + """Returns a copy of the string literal with all cased characters + converted to uppercase. + + Returns: + A new string where cased letters have been converted to uppercase. + """ + + return str(self).upper() diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index aafe4b2567..9f049f5c78 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -167,6 +167,15 @@ def test_layout(): assert_equal(ptr[5], 0) # Verify NUL terminated +def test_lower_upper(): + assert_equal("hello".lower(), "hello") + assert_equal("HELLO".lower(), "hello") + assert_equal("Hello".lower(), "hello") + assert_equal("hello".upper(), "HELLO") + assert_equal("HELLO".upper(), "HELLO") + assert_equal("Hello".upper(), "HELLO") + + def test_repr(): # Usual cases assert_equal(StringLiteral.__repr__("hello"), "'hello'") @@ -200,4 +209,5 @@ def main(): test_hash() test_intable() test_layout() + test_lower_upper() test_repr() From 21d9c74f14b526067cfbbb0ce6c003e8ad6b86a1 Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 26 Aug 2024 09:36:15 -0700 Subject: [PATCH 1442/2019] [External] [stdlib] Remove unnecessary `UnsafePointer.[gather|scatter]` overloads in favour of default arguments (#45938) [External] [stdlib] Remove unnecessary `UnsafePointer.[gather|scatter]` overloads in favour of default arguments Co-authored-by: soraros Closes modularml/mojo#3411 MODULAR_ORIG_COMMIT_REV_ID: 3200cd4249d6c8f8f5c07ea90e4b052c526e44b7 --- stdlib/src/memory/unsafe_pointer.mojo | 91 ++------------------------- 1 file changed, 6 insertions(+), 85 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 46b1310cc5..210877106b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -642,45 +642,8 @@ struct UnsafePointer[ @always_inline("nodebug") fn gather[ - type: DType, - *, - width: Int = 1, - alignment: Int = alignof[ - SIMD[type, width] - ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width]) -> SIMD[ - type, width - ]: - """Gathers a SIMD vector from offsets of the current pointer. - - This method loads from memory addresses calculated by appropriately - shifting the current pointer according to the `offset` SIMD vector. - - Constraints: - The offset type must be an integral type. - The alignment must be a power of two integer value. - - Parameters: - type: DType of the return SIMD. - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The SIMD vector of offsets to gather from. - - Returns: - The SIMD vector containing the gathered values. - """ - var mask = SIMD[DType.bool, width](True) - var default = SIMD[type, width]() - return self.gather[width=width, alignment=alignment]( - offset, mask, default - ) - - @always_inline("nodebug") - fn gather[ + type: DType, //, *, - type: DType, width: Int = 1, alignment: Int = alignof[ SIMD[type, width] @@ -688,8 +651,8 @@ struct UnsafePointer[ ]( self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width], - mask: SIMD[DType.bool, width], - default: SIMD[type, width], + mask: SIMD[DType.bool, width] = True, + default: SIMD[type, width] = 0, ) -> SIMD[type, width]: """Gathers a SIMD vector from offsets of the current pointer. @@ -736,46 +699,8 @@ struct UnsafePointer[ @always_inline("nodebug") fn scatter[ + type: DType, //, *, - type: DType, - width: Int = 1, - alignment: Int = alignof[ - SIMD[type, width] - ]() if triple_is_nvidia_cuda() else 1, - ]( - self: UnsafePointer[Scalar[type], *_], - offset: SIMD[_, width], - val: SIMD[type, width], - ): - """Scatters a SIMD vector into offsets of the current pointer. - - This method stores at memory addresses calculated by appropriately - shifting the current pointer according to the `offset` SIMD vector. - - If the same offset is targeted multiple times, the values are stored - in the order they appear in the `val` SIMD vector, from the first to - the last element. - - Constraints: - The offset type must be an integral type. - The alignment must be a power of two integer value. - - Parameters: - type: DType of `value`, the result SIMD buffer. - width: The SIMD width. - alignment: The minimal alignment of the address. - - Args: - offset: The SIMD vector of offsets to scatter into. - val: The SIMD vector containing the values to be scattered. - """ - var mask = SIMD[DType.bool, width](True) - self.scatter[width=width, alignment=alignment](offset, val, mask) - - @always_inline("nodebug") - fn scatter[ - *, - type: DType, width: Int = 1, alignment: Int = alignof[ SIMD[type, width] @@ -784,7 +709,7 @@ struct UnsafePointer[ self: UnsafePointer[Scalar[type], *_], offset: SIMD[_, width], val: SIMD[type, width], - mask: SIMD[DType.bool, width], + mask: SIMD[DType.bool, width] = True, ): """Scatters a SIMD vector into offsets of the current pointer. @@ -871,11 +796,7 @@ struct UnsafePointer[ A new UnsafePointer object with the specified type and the same address, as the original UnsafePointer. """ - return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[ - Scalar[T], address_space, alignment=alignment - ]._mlir_type, - ](self.address) + return self.bitcast[Scalar[T], address_space]() @always_inline fn destroy_pointee(self: UnsafePointer[T, alignment=alignment]): From 5895417e526622be755f36c3158be7d875eebabc Mon Sep 17 00:00:00 2001 From: Helehex Date: Mon, 26 Aug 2024 10:42:30 -0700 Subject: [PATCH 1443/2019] [External] [stdlib] Remove `Formatter.write_str[StringLiteral]()` overload (#44607) [External] [stdlib] Remove `Formatter.write_str[StringLiteral]()` overload Remove `Formatter.write_str[StringLiteral]()` overload in favor of `StringSlice` overload. Change `StringSlice.__init__(StringLiteral)` to be conditional on `ImmutableStaticLifetime`. Closes modularml/mojo#3342 MODULAR_ORIG_COMMIT_REV_ID: d505bcbeeceec194eeee42cff22e837d1f665b0e --- stdlib/src/builtin/dtype.mojo | 34 +++++++++++++++--------------- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 6 +++--- stdlib/src/utils/_format.mojo | 14 ------------ stdlib/src/utils/string_slice.mojo | 18 ++++++---------- 5 files changed, 28 insertions(+), 46 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 3142b3f5c3..3f2b086bf4 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -151,39 +151,39 @@ struct DType( """ if self == DType.bool: - return writer.write_str["bool"]() + return writer.write_str("bool") if self == DType.int8: - return writer.write_str["int8"]() + return writer.write_str("int8") if self == DType.uint8: - return writer.write_str["uint8"]() + return writer.write_str("uint8") if self == DType.int16: - return writer.write_str["int16"]() + return writer.write_str("int16") if self == DType.uint16: - return writer.write_str["uint16"]() + return writer.write_str("uint16") if self == DType.int32: - return writer.write_str["int32"]() + return writer.write_str("int32") if self == DType.uint32: - return writer.write_str["uint32"]() + return writer.write_str("uint32") if self == DType.int64: - return writer.write_str["int64"]() + return writer.write_str("int64") if self == DType.uint64: - return writer.write_str["uint64"]() + return writer.write_str("uint64") if self == DType.index: - return writer.write_str["index"]() + return writer.write_str("index") if self == DType.bfloat16: - return writer.write_str["bfloat16"]() + return writer.write_str("bfloat16") if self == DType.float16: - return writer.write_str["float16"]() + return writer.write_str("float16") if self == DType.float32: - return writer.write_str["float32"]() + return writer.write_str("float32") if self == DType.tensor_float32: - return writer.write_str["tensor_float32"]() + return writer.write_str("tensor_float32") if self == DType.float64: - return writer.write_str["float64"]() + return writer.write_str("float64") if self == DType.invalid: - return writer.write_str["invalid"]() + return writer.write_str("invalid") - return writer.write_str["<>"]() + return writer.write_str("<>") @always_inline("nodebug") fn __repr__(self) -> String: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 4dded088fb..ebf263e3c7 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -305,7 +305,7 @@ fn _try_write_int[ # Prefix a '-' if the original int was negative and make positive. if value < 0: - fmt.write_str["-"]() + fmt.write_str("-") # Add the custom number prefix, e.g. "0x" commonly used for hex numbers. # This comes *after* the minus sign, if present. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 24483f939e..ea2087d030 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1590,14 +1590,14 @@ struct SIMD[type: DType, size: Int]( # Print an opening `[`. @parameter if size > 1: - writer.write_str["["]() + writer.write_str("[") # Print each element. for i in range(size): var element = self[i] # Print separators between each element. if i != 0: - writer.write_str[", "]() + writer.write_str(", ") @parameter if triple_is_nvidia_cuda(): @@ -1644,7 +1644,7 @@ struct SIMD[type: DType, size: Int]( # Print a closing `]`. @parameter if size > 1: - writer.write_str["]"]() + writer.write_str("]") @always_inline fn _bits_to_float[dest_type: DType](self) -> SIMD[dest_type, size]: diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo index e6b47d7808..65cd2b80b7 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -111,20 +111,6 @@ struct Formatter: # Methods # ===------------------------------------------------------------------=== # - # TODO(cleanup): - # Remove this overload by defining a working - # `StringSlice.__init__(StringLiteral)` implicit conversion. - @always_inline - fn write_str[literal: StringLiteral](inout self): - """ - Write a string literal to this formatter. - - Parameters: - literal: The string literal to write. - """ - alias slc = literal.as_string_slice() - self.write_str(slc) - # TODO: Constrain to only require an immutable StringSlice[..]` @always_inline fn write_str(inout self, str_slice: StringSlice[_]): diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 0edcaabceb..650f750653 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -323,19 +323,15 @@ struct StringSlice[ # Initializers # ===------------------------------------------------------------------===# - fn __init__(inout self, literal: StringLiteral): + @always_inline + fn __init__( + inout self: StringSlice[ImmutableStaticLifetime], lit: StringLiteral + ): """Construct a new string slice from a string literal. Args: - literal: The literal to construct this string slice from. + lit: The literal to construct this string slice from. """ - - # Its not legal to try to mutate a StringLiteral. String literals are - # static data. - constrained[ - not is_mutable, "cannot create mutable StringSlice of StringLiteral" - ]() - # Since a StringLiteral has static lifetime, it will outlive # whatever arbitrary `lifetime` the user has specified they need this # slice to live for. @@ -348,8 +344,8 @@ struct StringSlice[ # _is_valid_utf8(literal.unsafe_ptr(), literal._byte_length()), # "StringLiteral doesn't have valid UTF-8 encoding", # ) - self = StringSlice[lifetime]( - unsafe_from_utf8_ptr=literal.unsafe_ptr(), len=literal.byte_length() + self = StringSlice[ImmutableStaticLifetime]( + unsafe_from_utf8_ptr=lit.unsafe_ptr(), len=lit.byte_length() ) @always_inline From fdc77dfcac1cecbfdc84cbbb64afee2b3f0d5d80 Mon Sep 17 00:00:00 2001 From: "Ehsan M. Kermani" <6980212+ehsanmok@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:54:48 -0700 Subject: [PATCH 1444/2019] Fix the initialization of uninitialized `InlineArray` in builtin `format_int` MODULAR_ORIG_COMMIT_REV_ID: b7dd3c818c7f86942fdc90e9b15f4efe1f4aacd7 --- stdlib/src/builtin/format_int.mojo | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index ebf263e3c7..dc1a1c3583 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -348,7 +348,9 @@ fn _try_write_int[ # earlier in the buffer as we write the more-significant digits. var offset = CAPACITY - 1 - buf[offset] = 0 # Write NUL terminator at the end + buf.unsafe_ptr().offset(offset).init_pointee_copy( + 0 + ) # Write NUL terminator at the end # Position the offset to write the least-significant digit just before the # NUL terminator. @@ -364,7 +366,9 @@ fn _try_write_int[ # Write the char representing the value of the least significant # digit. - buf[offset] = digit_chars_array[int(digit_value)] + buf.unsafe_ptr().offset(offset).init_pointee_copy( + digit_chars_array[int(digit_value)] + ) # Position the offset to write the next digit. offset -= 1 From 6b092df338f8eeabf203bf17fc3b322c4c3efdce Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 26 Aug 2024 11:05:58 -0700 Subject: [PATCH 1445/2019] [External] [stdlib] Remove unused `_put` overloads (#45916) [External] [stdlib] Remove unused `_put` overloads Closes modularml/mojo#3412 MODULAR_ORIG_COMMIT_REV_ID: 251a87fd45cc86c35ebb8e622c39fa9f44f401cf --- stdlib/src/builtin/io.mojo | 78 -------------------------------------- 1 file changed, 78 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 6075a0a933..b8c20c605d 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -300,79 +300,6 @@ fn _float_repr[ # ===----------------------------------------------------------------------=== # -@no_inline -fn _put(x: Int, file: FileDescriptor = stdout): - """Prints a scalar value. - - Args: - x: The value to print. - file: The output stream. - """ - _printf[_get_dtype_printf_format[DType.index]()](x, file=file) - - -@no_inline -fn _put_simd_scalar[type: DType](x: Scalar[type]): - """Prints a scalar value. - - Parameters: - type: The DType of the value. - - Args: - x: The value to print. - """ - alias format = _get_dtype_printf_format[type]() - - @parameter - if type is DType.bool: - _put["True"]() if x else _put["False"]() - elif type.is_integral(): - _printf[format](x) - elif type.is_floating_point(): - - @parameter - if triple_is_nvidia_cuda(): - _printf[format](x.cast[DType.float64]()) - else: - _put(str(x).as_string_slice()) - else: - constrained[False, "invalid dtype"]() - - -@no_inline -fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): - """Prints a scalar value. - - Parameters: - type: The DType of the value. - simd_width: The SIMD width. - - Args: - x: The value to print. - """ - alias format = _get_dtype_printf_format[type]() - - @parameter - if simd_width == 1: - _put_simd_scalar(x[0]) - elif type.is_integral(): - _put["["]() - - @parameter - for i in range(simd_width): - _put_simd_scalar(x[i]) - if i != simd_width - 1: - _put[", "]() - _put["]"]() - else: - _put(str(x).as_string_slice()) - - -@no_inline -fn _put[x: StringLiteral](file: FileDescriptor = stdout): - _put(x.as_string_slice(), file=file) - - fn _put(strref: StringRef, file: FileDescriptor = stdout): var str_slice = StringSlice[ImmutableStaticLifetime]( unsafe_from_utf8_strref=strref @@ -381,11 +308,6 @@ fn _put(strref: StringRef, file: FileDescriptor = stdout): _put(str_slice, file=file) -@no_inline -fn _put(x: DType, file: FileDescriptor = stdout): - _put(str(x).as_string_slice(), file=file) - - @no_inline fn _put[ lif: ImmutableLifetime, // From 642966317c931c40ef5248a6c02e5b5ebe8884c6 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 26 Aug 2024 17:01:46 -0500 Subject: [PATCH 1446/2019] [MAX] Expand stdlib tests in release MODULAR_ORIG_COMMIT_REV_ID: 654adb1426de3f9046cf944c8309cff8af22538b --- pixi.toml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pixi.toml diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 0000000000..445365dcf0 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,14 @@ +[project] +name = "Mojo" +authors = ["Modular "] +channels = ["conda-forge", "anaconda", "https://conda.cloudsmith.io/modular/max-nightly/"] +platforms = ["linux-64", "linux-aarch64", "osx-arm64"] + +[tasks] +tests ="./stdlib/scripts/run-tests.sh" +examples = "./examples/run-examples.sh" +benchmarks = { cmd = ["./stdlib/scripts/run-benchmarks.sh"], env = { MODULAR_MOJO_NIGHTLY_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo" } } + +[dependencies] +python = ">=3.9,<3.13" +max = "*" From b21e00a49f95343a6c6069f5bb95562e2cbba83c Mon Sep 17 00:00:00 2001 From: Evan Ovadia Date: Mon, 26 Aug 2024 19:35:24 -0400 Subject: [PATCH 1447/2019] Added some hints for solving debugging issues MODULAR_ORIG_COMMIT_REV_ID: de4df3db9a7e489b15712cf9d2ec0cb078c7a417 --- docs/tools/debugging.ipynb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index b96744a80e..778cf85631 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -578,6 +578,34 @@ "it is only included in the compiled code when the `DEBUG_ME` name is defined on\n", "the command line." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Troubleshooting\n", + "\n", + "### `error: can't connect to the RPC debug server socket`\n", + "\n", + "If using `mojo debug --rpc` gives you the message `error: can't connect to the RPC debug server socket: Connection refused`, try the following possible fixes:\n", + "\n", + " * Make sure VS Code is open.\n", + " * If VS Code is already open, try restarting VS Code.\n", + " * If there are other VS Code windows open, try closing them and then restarting. This error can sometimes occur when multiple windows have opened and closed in certain orders.\n", + "\n", + "### `error: couldn't get a valid response from the RPC server`\n", + "\n", + "If using `mojo debug --rpc` gives you the message `error: couldn't get a valid response from the RPC server`, try the following possible fixes:\n", + "\n", + " * Make sure VS Code is open to a valid Mojo codebase. This error can sometimes happen if the VS Code window is open to some other codebase.\n", + " * If there are multiple VS Code windows open, try closing all but the one you wish to debug in.\n", + " * Restart VS Code.\n", + " * Reinstall the SDK and restart VSCode.\n", + " * If you are working on a development version of the SDK, make sure that all SDK tools are properly built with your build system, and then reload VSCode.\n", + " * As a last resort, restarting your entire computer can fix this problem.\n", + "\n", + "If these steps don't help, please file an issue. We'd love your help identifying possible causes and fixes!" + ] } ], "metadata": { From 1c75650b1da687b08cd9187cf5015707da8e9b8e Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 26 Aug 2024 17:42:09 -0700 Subject: [PATCH 1448/2019] [stdlib] Disable debug info for some tests Internally these tests fail with full debug info. This disables that while we fix the underlying issue. Externally these act the same as before. MODULAR_ORIG_COMMIT_REV_ID: f53b8370fafe8f80abcacdbb1c54127ab1f8dec0 --- stdlib/test/builtin/test_float_literal.mojo | 2 +- stdlib/test/builtin/test_int_literal.mojo | 2 +- stdlib/test/builtin/test_object.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/lit.cfg.py | 4 ++++ stdlib/test/memory/test_memory.mojo | 2 +- stdlib/test/sys/test_intrinsics.mojo | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index db4841df4d..d059aac25e 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from testing import ( assert_almost_equal, diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 72ffa712fa..84a7460e72 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from testing import assert_equal, assert_true, assert_false diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 65c118665a..9b23cee855 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from random import random_float64 diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 5b7347d2a5..2ffc309ff7 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from sys import has_neon diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index b267ad4446..0a23f7b544 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -79,6 +79,10 @@ def has_not(): base_mojo_command = "mojo" config.substitutions.insert(0, ("%mojo", base_mojo_command)) + # Mojo without debug info. Only use this for known tests that do not work + # with debug info enabled. + config.substitutions.insert(0, ("%mojo-no-debug", base_mojo_command)) + # Mojo without assertions. Only use this for known tests that do not work # with assertions enabled. config.substitutions.insert(1, ("%bare-mojo", "mojo")) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 08466b18ef..b582579545 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from sys import sizeof, simdwidthof diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index bc30c829d4..17133c0e7c 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from sys import ( compressed_store, From 09147d13c9b11ec841a98a5b6bd8a9dac9d6e7d5 Mon Sep 17 00:00:00 2001 From: ematejska Date: Tue, 27 Aug 2024 09:56:03 -0700 Subject: [PATCH 1449/2019] [stdlib] Open source the `math` module tests To accompany the recently open-sourced `math` module, open source its tests too. MODULAR_ORIG_COMMIT_REV_ID: 162be0b30f19f6b76a0fbf4c03253a6c8099e83b --- stdlib/test/math/test_erf.mojo | 92 ++++ stdlib/test/math/test_exp.mojo | 76 ++++ stdlib/test/math/test_ldexp.mojo | 80 ++++ stdlib/test/math/test_math.mojo | 499 ++++++++++++++++++++++ stdlib/test/math/test_modf.mojo | 52 +++ stdlib/test/math/test_polynomial.mojo | 58 +++ stdlib/test/math/test_tanh.mojo | 85 ++++ stdlib/test/test_utils/__init__.mojo | 1 + stdlib/test/test_utils/compare_utils.mojo | 58 +++ 9 files changed, 1001 insertions(+) create mode 100644 stdlib/test/math/test_erf.mojo create mode 100644 stdlib/test/math/test_exp.mojo create mode 100644 stdlib/test/math/test_ldexp.mojo create mode 100644 stdlib/test/math/test_math.mojo create mode 100644 stdlib/test/math/test_modf.mojo create mode 100644 stdlib/test/math/test_polynomial.mojo create mode 100644 stdlib/test/math/test_tanh.mojo create mode 100644 stdlib/test/test_utils/compare_utils.mojo diff --git a/stdlib/test/math/test_erf.mojo b/stdlib/test/math/test_erf.mojo new file mode 100644 index 0000000000..29d92fec8f --- /dev/null +++ b/stdlib/test/math/test_erf.mojo @@ -0,0 +1,92 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: linux +# RUN: %mojo %s + + +from collections import InlineArray +from math import erf +from random import randn, seed + +from test_utils import compare, libm_call +from testing import assert_almost_equal, assert_equal + + +def test_erf_float32(): + assert_equal(erf(Float32(0)), 0.0) + assert_almost_equal(erf(SIMD[DType.float32, 2](2)), 0.995322) + assert_almost_equal(erf(Float32(0.1)), 0.112462) + assert_almost_equal(erf(Float32(-0.1)), -0.112462) + assert_almost_equal(erf(Float32(-1)), -0.8427007) + assert_almost_equal(erf(Float32(-2)), -0.995322) + + +def test_erf_float64(): + assert_equal(erf(Float64(0)), 0.0) + assert_almost_equal(erf(SIMD[DType.float64, 2](2)), 0.995322) + assert_almost_equal(erf(Float64(0.1)), 0.112462) + assert_almost_equal(erf(Float64(-0.1)), -0.112462) + assert_almost_equal(erf(Float64(-1)), -0.8427007) + assert_almost_equal(erf(Float64(-2)), -0.995322) + + +def test_erf_libm(): + seed(0) + var N = 8192 + alias test_dtype = DType.float32 + + # generate input values and write them to file + var x32 = UnsafePointer[Scalar[test_dtype]].alloc(N) + randn[test_dtype](x32, N, 0, 9.0) + print("For N=" + str(N) + " randomly generated vals; mean=0.0, var=9.0") + + #################### + # math.erf result + #################### + var y32 = UnsafePointer[Scalar[test_dtype]].alloc(N) + for i in range(N): + y32[i] = erf(x32[i]) # math.erf + + #################### + ## libm erf result + #################### + @always_inline + fn erf_libm[ + type: DType, simd_width: Int + ](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + return libm_call[type, simd_width, "erff", "err"](arg) + + var libm_out = UnsafePointer[Scalar[test_dtype]].alloc(N) + for i in range(N): + libm_out[i] = erf_libm(x32[i]) + + # abs_rel_err = (abs_min, abs_max, rel_min, rel_max) + var abs_rel_err = SIMD[test_dtype, 4]( + 0.0, 5.9604644775390625e-08, 0.0, 1.172195140952681e-07 + ) + + var err = compare[test_dtype]( + y32, libm_out, N, msg="Compare Mojo math.erf vs. LibM" + ) + + assert_almost_equal(err, abs_rel_err) + + x32.free() + y32.free() + libm_out.free() + + +def main(): + test_erf_float32() + test_erf_float64() + test_erf_libm() diff --git a/stdlib/test/math/test_exp.mojo b/stdlib/test/math/test_exp.mojo new file mode 100644 index 0000000000..02add56114 --- /dev/null +++ b/stdlib/test/math/test_exp.mojo @@ -0,0 +1,76 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s +from math import exp +from random import randn_float64, seed +from sys import has_neon + +from test_utils import libm_call +from testing import assert_almost_equal, assert_equal + + +def test_exp_bfloat16(): + # TODO(KERN-228): support BF16 on neon systems. + @parameter + if not has_neon(): + assert_equal(exp(BFloat16(2.0)), 7.375) + + +def test_exp_float16(): + assert_almost_equal(exp(Float16(-0.1)), 0.9047) + assert_almost_equal(exp(Float16(0.1)), 1.105) + assert_almost_equal(exp(Float16(2)), 7.389) + assert_equal(str(exp(Float16(89))), "inf") + assert_equal(str(exp(Float16(108.5230))), "inf") + + +def test_exp_float32(): + assert_almost_equal(exp(Float32(-0.1)), 0.90483) + assert_almost_equal(exp(Float32(0.1)), 1.10517) + assert_almost_equal(exp(Float32(2)), 7.38905) + assert_equal(str(exp(Float32(89))), "inf") + assert_equal(str(exp(Float32(108.5230))), "inf") + + +def test_exp_float64(): + assert_almost_equal(exp(Float64(-0.1)), 0.90483) + assert_almost_equal(exp(Float64(0.1)), 1.10517) + assert_almost_equal(exp(Float64(2)), 7.38905) + # FIXME (40568) should remove str + assert_equal(str(exp(Float64(89))), str(4.4896128193366053e38)) + assert_equal(str(exp(Float64(108.5230))), str(1.3518859659123633e47)) + + +@always_inline +def exp_libm[ + type: DType, simd_width: Int +](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + var eval = libm_call[type, simd_width, "expf", "exp"](arg) + return eval + + +def test_exp_libm[type: DType](): + seed(0) + alias N = 8192 + for i in range(N): + var x = randn_float64(0, 9.0).cast[type]() + assert_almost_equal(exp(x), exp_libm(x), msg="for the input " + str(x)) + + +def main(): + test_exp_bfloat16() + test_exp_float16() + test_exp_float32() + test_exp_float64() + test_exp_libm[DType.float32]() + test_exp_libm[DType.float64]() diff --git a/stdlib/test/math/test_ldexp.mojo b/stdlib/test/math/test_ldexp.mojo new file mode 100644 index 0000000000..6e5ad105e3 --- /dev/null +++ b/stdlib/test/math/test_ldexp.mojo @@ -0,0 +1,80 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from math import frexp, ldexp +from sys import external_call + +from test_utils import libm_call +from testing import assert_almost_equal, assert_equal + + +def test_ldexp(): + assert_equal(ldexp(Float32(1.5), 4), 24) + assert_equal(ldexp(Float64(1.5), Int32(4)), 24) + + +def test_ldexp_vector(): + assert_equal( + ldexp(SIMD[DType.float32, 4](1.5), SIMD[DType.int32, 4](4)), + SIMD[DType.float32, 4](24), + ) + assert_equal( + ldexp(SIMD[DType.float64, 4](1.5), SIMD[DType.int32, 4](4)), + SIMD[DType.float64, 4](24), + ) + assert_equal( + ldexp(SIMD[DType.float32, 32](1.5), SIMD[DType.int32, 32](4)), + SIMD[DType.float32, 32](24), + ) + assert_equal( + ldexp(SIMD[DType.float64, 32](1.5), SIMD[DType.int32, 32](4)), + SIMD[DType.float64, 32](24), + ) + + +fn ldexp_libm[ + type: DType, simd_width: Int +](arg: SIMD[type, simd_width], e: SIMD[DType.int32, simd_width]) -> SIMD[ + type, simd_width +]: + var res = SIMD[type, simd_width]() + + for i in range(simd_width): + res[i] = external_call["ldexpf", Scalar[type]](arg, e) + return res + + +def test_ldexp_extensive_float32(): + var i = -1e3 + while i < 1e3: + var out = frexp(i.cast[DType.float32]()) + var frac = out[0] + var exp = out[1].cast[DType.int32]() + assert_almost_equal( + ldexp(frac, exp), + ldexp_libm(frac, exp), + msg="unmatched results for frac=" + + str(frac) + + " and exp=" + + str(exp) + + " at index " + + str(i), + ) + i += 1007 + + +def main(): + test_ldexp() + test_ldexp_vector() + test_ldexp_extensive_float32() diff --git a/stdlib/test/math/test_math.mojo b/stdlib/test/math/test_math.mojo new file mode 100644 index 0000000000..0431deae69 --- /dev/null +++ b/stdlib/test/math/test_math.mojo @@ -0,0 +1,499 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from collections import InlineArray, List +from math import ( + align_down, + align_up, + ceil, + ceildiv, + clamp, + copysign, + cos, + exp2, + factorial, + floor, + frexp, + gcd, + iota, + isclose, + isqrt, + lcm, + log, + log2, + sin, + sqrt, + trunc, + ulp, +) +from sys.info import has_neon + +from testing import assert_almost_equal, assert_equal, assert_false, assert_true + +from utils import Span +from utils.numerics import inf, isinf, isnan, nan, neg_inf + + +fn test_sin() raises: + assert_almost_equal(sin(Float32(1.0)), 0.841470956802) + + +fn test_cos() raises: + assert_almost_equal(cos(Float32(1.0)), 0.540302276611) + + # TODO(KERN-228): support BF16 on neon systems. + @parameter + if not has_neon(): + assert_equal(cos(BFloat16(2.0)), -0.416015625) + + +fn test_factorial() raises: + assert_equal(factorial(0), 1) + assert_equal(factorial(1), 1) + assert_equal(factorial(15), 1307674368000) + assert_equal(factorial(20), 2432902008176640000) + + +def test_copysign(): + var x = Int32(2) + assert_equal(2, copysign(x, x)) + assert_equal(-2, copysign(x, -x)) + assert_equal(2, copysign(-x, x)) + assert_equal(-2, copysign(-x, -x)) + + assert_equal(1, copysign(Float32(1), Float32(2))) + assert_equal(-1, copysign(Float32(1), Float32(-2))) + assert_equal(neg_inf[DType.float32](), copysign(inf[DType.float32](), -2.0)) + assert_equal(-nan[DType.float32](), copysign(nan[DType.float32](), -2.0)) + + # Test some cases with 0 and signed zero + assert_equal(1, copysign(Float32(1.0), Float32(0.0))) + assert_equal(0, copysign(Float32(0.0), Float32(1.0))) + assert_equal(0, copysign(Float32(-0.0), Float32(1.0))) + assert_equal(-0, copysign(Float32(0.0), Float32(-1.0))) + + # TODO: Add some test cases for SIMD vector with width > 1 + + +def test_isclose(): + assert_true(isclose(Int64(2), Int64(2), atol=0, rtol=0)) + assert_false(isclose(Int64(2), Int64(3), atol=0, rtol=0)) + + assert_true(isclose(Float32(2), Float32(2))) + assert_true(isclose(Float32(2), Float32(2), rtol=1e-9)) + assert_true(isclose(Float32(2), Float32(2.00001), rtol=1e-3)) + assert_true( + isclose(nan[DType.float32](), nan[DType.float32](), equal_nan=True) + ) + + assert_true( + isclose( + SIMD[DType.float32, 4](1, 2, 3, nan[DType.float32]()), + SIMD[DType.float32, 4](1, 2, 3, nan[DType.float32]()), + equal_nan=True, + ).reduce_and() + ) + + assert_false( + isclose( + SIMD[DType.float32, 4](1, 2, nan[DType.float32](), 3), + SIMD[DType.float32, 4](1, 2, nan[DType.float32](), 4), + equal_nan=True, + ).reduce_and() + ) + + +def test_ceil(): + # We just test that the `ceil` function resolves correctly for a few common + # types. Types should test their own `__ceil__` implementation explicitly. + assert_equal(ceil(0), 0) + assert_equal(ceil(Int(5)), 5) + assert_equal(ceil(1.5), 2.0) + assert_equal(ceil(Float32(1.4)), 2.0) + assert_equal(ceil(Float64(-3.6)), -3.0) + + +def test_floor(): + # We just test that the `floor` function resolves correctly for a few common + # types. Types should test their own `__floor__` implementation explicitly. + assert_equal(floor(0), 0) + assert_equal(floor(Int(5)), 5) + assert_equal(floor(1.5), 1.0) + assert_equal(floor(Float32(1.6)), 1.0) + assert_equal(floor(Float64(-3.4)), -4.0) + + +def test_trunc(): + # We just test that the `trunc` function resolves correctly for a few common + # types. Types should test their own `__trunc__` implementation explicitly. + assert_equal(trunc(0), 0) + assert_equal(trunc(Int(5)), 5) + assert_equal(trunc(1.5), 1.0) + assert_equal(trunc(Float32(1.6)), 1.0) + assert_equal(trunc(Float64(-3.4)), -3.0) + + +def test_exp2(): + assert_equal(exp2(Float32(1)), 2.0) + assert_almost_equal(exp2(Float32(0.2)), 1.148696) + assert_equal(exp2(Float32(0)), 1.0) + assert_equal(exp2(Float32(-1)), 0.5) + assert_equal(exp2(Float32(2)), 4.0) + + +def test_iota(): + alias length = 103 + var offset = 2 + + var vector = List[Int32]() + vector.resize(length, 0) + + var buff = vector.data + iota(buff, length, offset) + + for i in range(length): + assert_equal(vector[i], offset + i) + + iota(vector, offset) + + for i in range(length): + assert_equal(vector[i], offset + i) + + var vector2 = List[Int]() + vector2.resize(length, 0) + iota(vector2, offset) + + for i in range(length): + assert_equal(vector2[i], offset + i) + + +alias F32x4 = SIMD[DType.float32, 4] +alias F64x4 = SIMD[DType.float64, 4] + + +def test_sqrt(): + var i = SIMD[DType.index, 4](0, 1, 2, 3) + assert_equal(sqrt(i**2), i) + assert_equal(sqrt(64), 8) + assert_equal(sqrt(63), 7) + + var f32x4 = 0.5 * F32x4(0.0, 1.0, 2.0, 3.0) + + var s1_f32 = sqrt(f32x4) + assert_equal(s1_f32[0], 0.0) + assert_almost_equal(s1_f32[1], 0.70710) + assert_equal(s1_f32[2], 1.0) + assert_almost_equal(s1_f32[3], 1.22474) + + var s2_f32 = sqrt(0.5 * f32x4) + assert_equal(s2_f32[0], 0.0) + assert_equal(s2_f32[1], 0.5) + assert_almost_equal(s2_f32[2], 0.70710) + assert_almost_equal(s2_f32[3], 0.86602) + + var f64x4 = 0.5 * F64x4(0.0, 1.0, 2.0, 3.0) + + var s1_f64 = sqrt(f64x4) + assert_equal(s1_f64[0], 0.0) + assert_almost_equal(s1_f64[1], 0.70710) + assert_equal(s1_f64[2], 1.0) + assert_almost_equal(s1_f64[3], 1.22474) + + var s2_f64 = sqrt(0.5 * f64x4) + assert_equal(s2_f64[0], 0.0) + assert_equal(s2_f64[1], 0.5) + assert_almost_equal(s2_f64[2], 0.70710) + assert_almost_equal(s2_f64[3], 0.86602) + + +def test_isqrt(): + var f32x4 = 0.5 * F32x4(0.0, 1.0, 2.0, 3.0) + 1 + + var s1_f32 = isqrt(f32x4) + assert_equal(s1_f32[0], 1.0) + assert_almost_equal(s1_f32[1], 0.81649) + assert_almost_equal(s1_f32[2], 0.70710) + assert_almost_equal(s1_f32[3], 0.63245) + + var s2_f32 = isqrt(0.5 * f32x4) + assert_almost_equal(s2_f32[0], 1.41421) + assert_almost_equal(s2_f32[1], 1.15470) + assert_equal(s2_f32[2], 1.0) + assert_almost_equal(s2_f32[3], 0.89442) + + var f64x4 = 0.5 * F64x4(0.0, 1.0, 2.0, 3.0) + 1 + + var s1_f64 = isqrt(f64x4) + assert_equal(s1_f64[0], 1.0) + assert_almost_equal(s1_f64[1], 0.81649) + assert_almost_equal(s1_f64[2], 0.70710) + assert_almost_equal(s1_f64[3], 0.63245) + + var s2_f64 = isqrt(0.5 * f64x4) + assert_almost_equal(s2_f64[0], 1.41421) + assert_almost_equal(s2_f64[1], 1.15470) + assert_equal(s2_f64[2], 1.0) + assert_almost_equal(s2_f64[3], 0.89442) + + +def _test_frexp_impl[type: DType](*, atol: Float32, rtol: Float32): + var res0 = frexp(Scalar[type](123.45)) + assert_almost_equal( + res0[0].cast[DType.float32](), 0.964453, atol=atol, rtol=rtol + ) + assert_almost_equal( + res0[1].cast[DType.float32](), 7.0, atol=atol, rtol=rtol + ) + + var res1 = frexp(Scalar[type](0.1)) + assert_almost_equal( + res1[0].cast[DType.float32](), 0.8, atol=atol, rtol=rtol + ) + assert_almost_equal( + res1[1].cast[DType.float32](), -3.0, atol=atol, rtol=rtol + ) + + var res2 = frexp(Scalar[type](-0.1)) + assert_almost_equal( + res2[0].cast[DType.float32](), -0.8, atol=atol, rtol=rtol + ) + assert_almost_equal( + res2[1].cast[DType.float32](), -3.0, atol=atol, rtol=rtol + ) + + var res3 = frexp(SIMD[type, 4](0, 2, 4, 5)) + assert_almost_equal( + res3[0].cast[DType.float32](), + SIMD[DType.float32, 4](0.0, 0.5, 0.5, 0.625), + atol=atol, + rtol=rtol, + ) + assert_almost_equal( + res3[1].cast[DType.float32](), + SIMD[DType.float32, 4](-0.0, 2.0, 3.0, 3.0), + atol=atol, + rtol=rtol, + ) + + +def _test_log_impl[type: DType](*, atol: Float32, rtol: Float32): + var res0 = log(Scalar[type](123.45)) + assert_almost_equal( + res0.cast[DType.float32](), 4.8158, atol=atol, rtol=rtol + ) + + var res1 = log(Scalar[type](0.1)) + assert_almost_equal( + res1.cast[DType.float32](), -2.3025, atol=atol, rtol=rtol + ) + + var res2 = log(SIMD[type, 4](1, 2, 4, 5)) + assert_almost_equal( + res2.cast[DType.float32](), + SIMD[DType.float32, 4](0.0, 0.693147, 1.38629, 1.6094), + atol=atol, + rtol=rtol, + ) + + var res3 = log(Scalar[type](2.7182818284590452353602874713526624977572)) + assert_almost_equal(res3.cast[DType.float32](), 1.0, atol=atol, rtol=rtol) + + var res4 = isinf(log(SIMD[type, 4](0, 1, 0, 0))) + assert_equal(res4, SIMD[DType.bool, 4](True, False, True, True)) + + +def _test_log2_impl[type: DType](*, atol: Float32, rtol: Float32): + var res0 = log2(Scalar[type](123.45)) + assert_almost_equal( + res0.cast[DType.float32](), 6.9477, atol=atol, rtol=rtol + ) + + var res1 = log2(Scalar[type](0.1)) + assert_almost_equal( + res1.cast[DType.float32](), -3.3219, atol=atol, rtol=rtol + ) + + var res2 = log2(SIMD[type, 4](1, 2, 4, 5)) + assert_almost_equal( + res2.cast[DType.float32](), + SIMD[DType.float32, 4](0.0, 1.0, 2.0, 2.3219), + atol=atol, + rtol=rtol, + ) + + +def test_frexp(): + _test_frexp_impl[DType.float32](atol=1e-4, rtol=1e-5) + _test_frexp_impl[DType.float16](atol=1e-2, rtol=1e-5) + + # TODO(KERN-228): support BF16 on neon systems. + @parameter + if not has_neon(): + _test_frexp_impl[DType.bfloat16](atol=1e-1, rtol=1e-5) + + +def test_log(): + _test_log_impl[DType.float32](atol=1e-4, rtol=1e-5) + _test_log_impl[DType.float16](atol=1e-2, rtol=1e-5) + + # TODO(KERN-228): support BF16 on neon systems. + @parameter + if not has_neon(): + _test_log_impl[DType.bfloat16](atol=1e-1, rtol=1e-5) + + +def test_log2(): + _test_log2_impl[DType.float32](atol=1e-4, rtol=1e-5) + _test_log2_impl[DType.float16](atol=1e-2, rtol=1e-5) + + # TODO(KERN-228): support BF16 on neon systems. + @parameter + if not has_neon(): + _test_log2_impl[DType.bfloat16](atol=1e-1, rtol=1e-5) + + +def test_gcd(): + var l = List(2, 4, 6, 8, 16) + var il = InlineArray[Int, 5](4, 16, 2, 8, 6) + assert_equal(gcd(Span[Int](il)), 2) + assert_equal(gcd(2, 4, 6, 8, 16), 2) + assert_equal(gcd(l), 2) + assert_equal(gcd(88, 24), 8) + assert_equal(gcd(0, 0), 0) + assert_equal(gcd(1, 0), 1) + assert_equal(gcd(-2, 4), 2) + assert_equal(gcd(-2, -4), 2) + assert_equal(gcd(-2, 0), 2) + assert_equal(gcd(2, -4), 2) + assert_equal(gcd(24826148, 45296490), 526) + assert_equal(gcd(0, 9), 9) + assert_equal(gcd(4, 4), 4) + assert_equal(gcd(8), 8) + assert_equal(gcd(), 0) + assert_equal(gcd(List[Int]()), 0) + assert_equal(gcd(List(16)), 16) + + +def test_lcm(): + assert_equal(lcm(-2, 4), 4) + assert_equal(lcm(2345, 23452), 54994940) + var l = List(4, 6, 7, 3) + assert_equal(lcm(Span(l)), 84) + assert_equal(lcm(l), 84) + assert_equal(lcm(4, 6, 7, 3), 84) + assert_equal(lcm(), 1) + assert_equal(lcm(List(3)), 3) + assert_equal(lcm(List[Int]()), 1) + assert_equal(lcm(0, 4), 0) + assert_equal(lcm(5, 33), 165) + assert_equal(lcm(-34, -56, -32), 3808) + var il = InlineArray[Int, 5](4, 16, 2, 8, 6) + assert_equal(lcm(Span[Int](il)), 48) + assert_equal(lcm(345, 623, 364, 84, 93), 346475220) + assert_equal(lcm(0, 0), 0) + + +def test_ulp(): + assert_true(isnan(ulp(nan[DType.float32]()))) + assert_true(isinf(ulp(inf[DType.float32]()))) + assert_true(isinf(ulp(-inf[DType.float32]()))) + assert_almost_equal(ulp(Float64(0)), 5e-324) + assert_equal(ulp(Float64.MAX_FINITE), 1.99584030953472e292) + assert_equal(ulp(Float64(5)), 8.881784197001252e-16) + assert_equal(ulp(Float64(-5)), 8.881784197001252e-16) + + +def test_ceildiv(): + # NOTE: these tests are here mostly to ensure the ceildiv method exists. + # Types that opt in to CeilDivable, should test their own dunder methods for + # correctness. + assert_equal(ceildiv(53.6, 1.35), 40.0) + + # Test the IntLiteral overload. + alias a: IntLiteral = ceildiv(1, 7) + assert_equal(a, 1) + alias b: IntLiteral = ceildiv(548, -7) + assert_equal(b, -78) + + # Test the Int overload. + assert_equal(ceildiv(Int(1), Int(7)), 1) + assert_equal(ceildiv(Int(548), Int(-7)), -78) + assert_equal(ceildiv(Int(-55), Int(8)), -6) + assert_equal(ceildiv(Int(-55), Int(-8)), 7) + + # Test the UInt overload. + assert_equal(ceildiv(UInt(1), UInt(7)), UInt(1)) + assert_equal(ceildiv(UInt(546), UInt(7)), UInt(78)) + + +def test_align_down(): + assert_equal(align_down(1, 7), 0) + assert_equal(align_down(548, -7), 553) + assert_equal(align_down(-548, -7), -546) + assert_equal(align_down(-548, 7), -553) + + # Test the UInt overload. + assert_equal(align_down(UInt(1), UInt(7)), UInt(0)) + assert_equal(align_down(UInt(546), UInt(7)), UInt(546)) + + +def test_align_up(): + assert_equal(align_up(1, 7), 7) + assert_equal(align_up(548, -7), 546) + assert_equal(align_up(-548, -7), -553) + assert_equal(align_up(-548, 7), -546) + + # Test the UInt overload. + assert_equal(align_up(UInt(1), UInt(7)), UInt(7)) + assert_equal(align_up(UInt(546), UInt(7)), UInt(546)) + + +def test_clamp(): + assert_equal(clamp(Int(1), 0, 1), 1) + assert_equal(clamp(Int(2), 0, 1), 1) + assert_equal(clamp(Int(-2), 0, 1), 0) + + assert_equal(clamp(UInt(1), UInt(0), UInt(1)), UInt(1)) + assert_equal(clamp(UInt(2), UInt(0), UInt(1)), UInt(1)) + assert_equal(clamp(UInt(1), UInt(2), UInt(4)), UInt(2)) + + assert_equal( + clamp(SIMD[DType.float32, 4](0, 1, 3, 4), 0, 1), + SIMD[DType.float32, 4](0, 1, 1, 1), + ) + + +def main(): + test_sin() + test_cos() + test_factorial() + test_copysign() + test_isclose() + test_ceil() + test_floor() + test_trunc() + test_exp2() + test_iota() + test_sqrt() + test_isqrt() + test_frexp() + test_log() + test_log2() + test_gcd() + test_lcm() + test_ulp() + test_ceildiv() + test_align_down() + test_align_up() + test_clamp() diff --git a/stdlib/test/math/test_modf.mojo b/stdlib/test/math/test_modf.mojo new file mode 100644 index 0000000000..f0ce61b359 --- /dev/null +++ b/stdlib/test/math/test_modf.mojo @@ -0,0 +1,52 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from math import modf + +from test_utils import libm_call +from testing import assert_almost_equal, assert_equal + + +def test_modf(): + var i32 = modf(Int32(123)) + assert_equal(i32[0], 123) + assert_equal(i32[1], 0) + + var f32 = modf(Float32(123.5)) + assert_almost_equal(f32[0], 123) + assert_almost_equal(f32[1], 0.5) + + var f64 = modf(Float64(123.5)) + assert_almost_equal(f64[0], 123) + assert_almost_equal(f64[1], 0.5) + + f64 = modf(Float64(0)) + assert_almost_equal(f64[0], 0) + assert_almost_equal(f64[1], 0) + + f64 = modf(Float64(0.5)) + assert_almost_equal(f64[0], 0) + assert_almost_equal(f64[1], 0.5) + + f64 = modf(Float64(-0.5)) + assert_almost_equal(f64[0], -0) + assert_almost_equal(f64[1], -0.5) + + f64 = modf(Float64(-1.5)) + assert_almost_equal(f64[0], -1) + assert_almost_equal(f64[1], -0.5) + + +def main(): + test_modf() diff --git a/stdlib/test/math/test_polynomial.mojo b/stdlib/test/math/test_polynomial.mojo new file mode 100644 index 0000000000..822a41bdac --- /dev/null +++ b/stdlib/test/math/test_polynomial.mojo @@ -0,0 +1,58 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from math.polynomial import _horner_evaluate, polynomial_evaluate + +from testing import assert_equal + + +def test_polynomial_evaluate_degree3(): + # Evaluate 1000 + x + x^2 + alias coeffs = List[SIMD[DType.float64, 1]](1000.0, 1.0, 1.0) + + assert_equal(_horner_evaluate[coeffs](1.0), 1002.0) + assert_equal(polynomial_evaluate[coeffs](1.0), 1002.0) + assert_equal(_horner_evaluate[coeffs](0.1), 1000.11) + assert_equal(polynomial_evaluate[coeffs](0.1), 1000.11) + + +def test_polynomial_evaluate_degree4(): + # Evalaute 1000 + 99 x - 43 x^2 + 12 x^3 - 14 x^4 + alias coeffs = List[SIMD[DType.float64, 1]]( + 1000.0, 99.0, -43.0, 12.0, -14.0 + ) + + assert_equal(_horner_evaluate[coeffs](1.0), 1054.0) + assert_equal(polynomial_evaluate[coeffs](1.0), 1054.0) + assert_equal(_horner_evaluate[coeffs](0.1), 1009.4806) + assert_equal(polynomial_evaluate[coeffs](0.1), 1009.4806) + + +def test_polynomial_evaluate_degree10(): + # Evaluate 20.0 + 9.0 x + 1.0 x^2 + 1.0 x^3 + 1.0 x^4 + 1.0 x^5 + 1.0 x^6 + + # 1.0 x^7 + 1.0 x^8 + 43.0 x^9 + 10.0 x^10 + alias coeffs = List[SIMD[DType.float64, 1]]( + 20.0, 9.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 43.0, 10.0 + ) + + assert_equal(_horner_evaluate[coeffs](1.0), 89.0) + assert_equal(polynomial_evaluate[coeffs](1.0), 89.0) + assert_equal(_horner_evaluate[coeffs](0.1), 20.911111154) + assert_equal(polynomial_evaluate[coeffs](0.1), 20.911111154) + + +def main(): + test_polynomial_evaluate_degree3() + test_polynomial_evaluate_degree4() + test_polynomial_evaluate_degree10() diff --git a/stdlib/test/math/test_tanh.mojo b/stdlib/test/math/test_tanh.mojo new file mode 100644 index 0000000000..6a3be0ad10 --- /dev/null +++ b/stdlib/test/math/test_tanh.mojo @@ -0,0 +1,85 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: linux +# RUN: %mojo %s + +from math import tanh +from random import randn, seed + +from test_utils import compare, libm_call +from testing import assert_almost_equal + + +fn tanh_libm[ + type: DType, simd_width: Int +](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + return libm_call[type, simd_width, "tanhf", "tanh"](arg) + + +def test_tanh_libm[N: Int = 8192](): + seed(0) + alias test_dtype = DType.float32 + var x32 = UnsafePointer[Scalar[test_dtype]].alloc(N) + randn[test_dtype](x32, N, 0, 9.0) + print("For N=" + str(N) + " randomly generated vals; mean=0.0, var=9.0") + + #################### + # mojo tanh result + #################### + var y32 = UnsafePointer[Scalar[test_dtype]].alloc(N) + for i in range(N): + y32[i] = tanh(x32[i]) + + #################### + ## libm tanh result + #################### + var libm_out = UnsafePointer[Scalar[test_dtype]].alloc(N) + for i in range(N): + libm_out[i] = tanh_libm(x32[i]) + + # abs_rel_err = (abs_min, abs_max, rel_min, rel_max) + var abs_rel_err = SIMD[test_dtype, 4]( + 0.0, 2.384185791015625e-07, 0.0, 2.5438197326366208e-07 + ) + + var err = compare[test_dtype](y32, libm_out, N, msg="Compare Mojo vs. LibM") + assert_almost_equal(err, abs_rel_err) + + x32.free() + y32.free() + libm_out.free() + + +def test_direct(): + alias F32x4 = SIMD[DType.float32, 4] + var f32x4 = 0.5 * F32x4(0.0, 1.0, 2.0, 3.0) + assert_almost_equal( + tanh(f32x4), F32x4(0.0, 0.462117165, 0.761594176, 0.905148208) + ) + assert_almost_equal( + tanh(0.5 * f32x4), F32x4(0.0, 0.244918659, 0.462117165, 0.635149002) + ) + + alias F64x4 = SIMD[DType.float64, 4] + var f64x4 = 0.5 * F64x4(0.0, 1.0, 2.0, 3.0) + assert_almost_equal( + tanh(f64x4), F64x4(0.0, 0.462117165, 0.761594176, 0.905148208) + ) + assert_almost_equal( + tanh(0.5 * f64x4), F64x4(0.0, 0.244918659, 0.462117165, 0.635149002) + ) + + +def main(): + test_direct() + test_tanh_libm() diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index 50cb4749b9..a0b404cba1 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -11,6 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from .compare_utils import compare from .test_utils import libm_call from .types import ( CopyCounter, diff --git a/stdlib/test/test_utils/compare_utils.mojo b/stdlib/test/test_utils/compare_utils.mojo new file mode 100644 index 0000000000..6f67adc09d --- /dev/null +++ b/stdlib/test/test_utils/compare_utils.mojo @@ -0,0 +1,58 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +fn _minmax[ + type: DType, // +](x: UnsafePointer[Scalar[type]], N: Int) -> Tuple[Scalar[type], Scalar[type]]: + var max_val = x[0] + var min_val = x[0] + for i in range(1, N): + if x[i] > max_val: + max_val = x[i] + if x[i] < min_val: + min_val = x[i] + return (min_val, max_val) + + +fn compare[ + dtype: DType, verbose: Bool = True +]( + x: UnsafePointer[Scalar[dtype]], + y: UnsafePointer[Scalar[dtype]], + num_elements: Int, + *, + msg: String = "", +) -> SIMD[dtype, 4]: + var atol = UnsafePointer[Scalar[dtype]].alloc(num_elements) + var rtol = UnsafePointer[Scalar[dtype]].alloc(num_elements) + + for i in range(num_elements): + var d = abs(x[i] - y[i]) + var e = abs(d / y[i]) + atol[i] = d + rtol[i] = e + + var atol_minmax = _minmax(atol, num_elements) + var rtol_minmax = _minmax(rtol, num_elements) + if verbose: + if msg: + print(msg) + print("AbsErr-Min/Max", atol_minmax[0], atol_minmax[1]) + print("RelErr-Min/Max", rtol_minmax[0], rtol_minmax[1]) + print("==========================================================") + atol.free() + rtol.free() + return SIMD[dtype, 4]( + atol_minmax[0], atol_minmax[1], rtol_minmax[0], rtol_minmax[1] + ) From 15e4ec4c4bdff521cb641e526cb92f5eb0296f49 Mon Sep 17 00:00:00 2001 From: Patrick Rachford Date: Tue, 27 Aug 2024 10:02:24 -0700 Subject: [PATCH 1450/2019] [stdlib] Add documentation for Counter module in collections This PR adds basic documentation for the Counter module in the stdlib collections package. MODULAR_ORIG_COMMIT_REV_ID: 4a5a8e41376e052498e24abcbe5204750bb394a9 --- stdlib/src/collections/counter.mojo | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index e243fe3c19..f53255011a 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -10,7 +10,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +"""Defines the `Counter` type. +You can import these APIs from the `collections` package. For example: + +```mojo +from collections import Counter +``` +""" from collections.dict import Dict, _DictKeyIter, _DictValueIter, _DictEntryIter from utils import Variant From 209077f029f25b3235a3395bea542156433dbb56 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 27 Aug 2024 12:09:38 -0500 Subject: [PATCH 1451/2019] [stdlib] Add method to `Span` for getting `Reference` Add `as_ref` method to `Span` for obtaining a `Reference` to the first element in the `Span`. Even though `Span.__getitem__` returns a little `ref` which can be used by callers to construct a big `Reference`, this improves the ergonomics a little bit. MODULAR_ORIG_COMMIT_REV_ID: 9f84ef483a1aafcb4ee97866df2895f74b5a07d0 --- stdlib/src/utils/span.mojo | 10 ++++++++++ stdlib/test/utils/test_span.mojo | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 161dc53fe0..45876aaa13 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -226,6 +226,16 @@ struct Span[ return self._data + fn as_ref(self) -> Reference[T, lifetime]: + """ + Gets a Reference to the first element of this slice. + + Returns: + A Reference pointing at the first element of this slice. + """ + + return self._data[0] + @always_inline fn copy_from[ lifetime: MutableLifetime, // diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 121947a036..b7c25d7a6d 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -187,6 +187,16 @@ def test_fill(): assert_equal(s[i], 2) +def test_ref(): + var l = InlineArray[Int, 3](1, 2, 3) + var s = Span[Int](array=l) + # Would be nice to just use `assert_equal` with `Reference`, but doesn't quite work yet + # even after `Reference` is `Stringable`. So, just compare for pointer equality right now. + var p1 = UnsafePointer(__mlir_op.`lit.ref.to_pointer`(s.as_ref().value)) + var p2 = l.unsafe_ptr() + assert_equal(p1, p2) + + def main(): test_span_list_int() test_span_list_str() @@ -197,3 +207,4 @@ def main(): test_equality() test_bool() test_fill() + test_ref() From 9143db0172c241e96a39eae3f7cc006b72ffc4a1 Mon Sep 17 00:00:00 2001 From: ematejska Date: Tue, 27 Aug 2024 10:45:24 -0700 Subject: [PATCH 1452/2019] Revert "[stdlib] Open source the `math` module tests" (#46046) Per Abdul's request Reverts modularml/modular#43547 MODULAR_ORIG_COMMIT_REV_ID: cdc93083db6ccde55ccdcf2bc04b2abf37682956 --- stdlib/test/math/test_erf.mojo | 92 ---- stdlib/test/math/test_exp.mojo | 76 ---- stdlib/test/math/test_ldexp.mojo | 80 ---- stdlib/test/math/test_math.mojo | 499 ---------------------- stdlib/test/math/test_modf.mojo | 52 --- stdlib/test/math/test_polynomial.mojo | 58 --- stdlib/test/math/test_tanh.mojo | 85 ---- stdlib/test/test_utils/__init__.mojo | 1 - stdlib/test/test_utils/compare_utils.mojo | 58 --- 9 files changed, 1001 deletions(-) delete mode 100644 stdlib/test/math/test_erf.mojo delete mode 100644 stdlib/test/math/test_exp.mojo delete mode 100644 stdlib/test/math/test_ldexp.mojo delete mode 100644 stdlib/test/math/test_math.mojo delete mode 100644 stdlib/test/math/test_modf.mojo delete mode 100644 stdlib/test/math/test_polynomial.mojo delete mode 100644 stdlib/test/math/test_tanh.mojo delete mode 100644 stdlib/test/test_utils/compare_utils.mojo diff --git a/stdlib/test/math/test_erf.mojo b/stdlib/test/math/test_erf.mojo deleted file mode 100644 index 29d92fec8f..0000000000 --- a/stdlib/test/math/test_erf.mojo +++ /dev/null @@ -1,92 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# REQUIRES: linux -# RUN: %mojo %s - - -from collections import InlineArray -from math import erf -from random import randn, seed - -from test_utils import compare, libm_call -from testing import assert_almost_equal, assert_equal - - -def test_erf_float32(): - assert_equal(erf(Float32(0)), 0.0) - assert_almost_equal(erf(SIMD[DType.float32, 2](2)), 0.995322) - assert_almost_equal(erf(Float32(0.1)), 0.112462) - assert_almost_equal(erf(Float32(-0.1)), -0.112462) - assert_almost_equal(erf(Float32(-1)), -0.8427007) - assert_almost_equal(erf(Float32(-2)), -0.995322) - - -def test_erf_float64(): - assert_equal(erf(Float64(0)), 0.0) - assert_almost_equal(erf(SIMD[DType.float64, 2](2)), 0.995322) - assert_almost_equal(erf(Float64(0.1)), 0.112462) - assert_almost_equal(erf(Float64(-0.1)), -0.112462) - assert_almost_equal(erf(Float64(-1)), -0.8427007) - assert_almost_equal(erf(Float64(-2)), -0.995322) - - -def test_erf_libm(): - seed(0) - var N = 8192 - alias test_dtype = DType.float32 - - # generate input values and write them to file - var x32 = UnsafePointer[Scalar[test_dtype]].alloc(N) - randn[test_dtype](x32, N, 0, 9.0) - print("For N=" + str(N) + " randomly generated vals; mean=0.0, var=9.0") - - #################### - # math.erf result - #################### - var y32 = UnsafePointer[Scalar[test_dtype]].alloc(N) - for i in range(N): - y32[i] = erf(x32[i]) # math.erf - - #################### - ## libm erf result - #################### - @always_inline - fn erf_libm[ - type: DType, simd_width: Int - ](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - return libm_call[type, simd_width, "erff", "err"](arg) - - var libm_out = UnsafePointer[Scalar[test_dtype]].alloc(N) - for i in range(N): - libm_out[i] = erf_libm(x32[i]) - - # abs_rel_err = (abs_min, abs_max, rel_min, rel_max) - var abs_rel_err = SIMD[test_dtype, 4]( - 0.0, 5.9604644775390625e-08, 0.0, 1.172195140952681e-07 - ) - - var err = compare[test_dtype]( - y32, libm_out, N, msg="Compare Mojo math.erf vs. LibM" - ) - - assert_almost_equal(err, abs_rel_err) - - x32.free() - y32.free() - libm_out.free() - - -def main(): - test_erf_float32() - test_erf_float64() - test_erf_libm() diff --git a/stdlib/test/math/test_exp.mojo b/stdlib/test/math/test_exp.mojo deleted file mode 100644 index 02add56114..0000000000 --- a/stdlib/test/math/test_exp.mojo +++ /dev/null @@ -1,76 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -from math import exp -from random import randn_float64, seed -from sys import has_neon - -from test_utils import libm_call -from testing import assert_almost_equal, assert_equal - - -def test_exp_bfloat16(): - # TODO(KERN-228): support BF16 on neon systems. - @parameter - if not has_neon(): - assert_equal(exp(BFloat16(2.0)), 7.375) - - -def test_exp_float16(): - assert_almost_equal(exp(Float16(-0.1)), 0.9047) - assert_almost_equal(exp(Float16(0.1)), 1.105) - assert_almost_equal(exp(Float16(2)), 7.389) - assert_equal(str(exp(Float16(89))), "inf") - assert_equal(str(exp(Float16(108.5230))), "inf") - - -def test_exp_float32(): - assert_almost_equal(exp(Float32(-0.1)), 0.90483) - assert_almost_equal(exp(Float32(0.1)), 1.10517) - assert_almost_equal(exp(Float32(2)), 7.38905) - assert_equal(str(exp(Float32(89))), "inf") - assert_equal(str(exp(Float32(108.5230))), "inf") - - -def test_exp_float64(): - assert_almost_equal(exp(Float64(-0.1)), 0.90483) - assert_almost_equal(exp(Float64(0.1)), 1.10517) - assert_almost_equal(exp(Float64(2)), 7.38905) - # FIXME (40568) should remove str - assert_equal(str(exp(Float64(89))), str(4.4896128193366053e38)) - assert_equal(str(exp(Float64(108.5230))), str(1.3518859659123633e47)) - - -@always_inline -def exp_libm[ - type: DType, simd_width: Int -](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - var eval = libm_call[type, simd_width, "expf", "exp"](arg) - return eval - - -def test_exp_libm[type: DType](): - seed(0) - alias N = 8192 - for i in range(N): - var x = randn_float64(0, 9.0).cast[type]() - assert_almost_equal(exp(x), exp_libm(x), msg="for the input " + str(x)) - - -def main(): - test_exp_bfloat16() - test_exp_float16() - test_exp_float32() - test_exp_float64() - test_exp_libm[DType.float32]() - test_exp_libm[DType.float64]() diff --git a/stdlib/test/math/test_ldexp.mojo b/stdlib/test/math/test_ldexp.mojo deleted file mode 100644 index 6e5ad105e3..0000000000 --- a/stdlib/test/math/test_ldexp.mojo +++ /dev/null @@ -1,80 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s - -from math import frexp, ldexp -from sys import external_call - -from test_utils import libm_call -from testing import assert_almost_equal, assert_equal - - -def test_ldexp(): - assert_equal(ldexp(Float32(1.5), 4), 24) - assert_equal(ldexp(Float64(1.5), Int32(4)), 24) - - -def test_ldexp_vector(): - assert_equal( - ldexp(SIMD[DType.float32, 4](1.5), SIMD[DType.int32, 4](4)), - SIMD[DType.float32, 4](24), - ) - assert_equal( - ldexp(SIMD[DType.float64, 4](1.5), SIMD[DType.int32, 4](4)), - SIMD[DType.float64, 4](24), - ) - assert_equal( - ldexp(SIMD[DType.float32, 32](1.5), SIMD[DType.int32, 32](4)), - SIMD[DType.float32, 32](24), - ) - assert_equal( - ldexp(SIMD[DType.float64, 32](1.5), SIMD[DType.int32, 32](4)), - SIMD[DType.float64, 32](24), - ) - - -fn ldexp_libm[ - type: DType, simd_width: Int -](arg: SIMD[type, simd_width], e: SIMD[DType.int32, simd_width]) -> SIMD[ - type, simd_width -]: - var res = SIMD[type, simd_width]() - - for i in range(simd_width): - res[i] = external_call["ldexpf", Scalar[type]](arg, e) - return res - - -def test_ldexp_extensive_float32(): - var i = -1e3 - while i < 1e3: - var out = frexp(i.cast[DType.float32]()) - var frac = out[0] - var exp = out[1].cast[DType.int32]() - assert_almost_equal( - ldexp(frac, exp), - ldexp_libm(frac, exp), - msg="unmatched results for frac=" - + str(frac) - + " and exp=" - + str(exp) - + " at index " - + str(i), - ) - i += 1007 - - -def main(): - test_ldexp() - test_ldexp_vector() - test_ldexp_extensive_float32() diff --git a/stdlib/test/math/test_math.mojo b/stdlib/test/math/test_math.mojo deleted file mode 100644 index 0431deae69..0000000000 --- a/stdlib/test/math/test_math.mojo +++ /dev/null @@ -1,499 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s - -from collections import InlineArray, List -from math import ( - align_down, - align_up, - ceil, - ceildiv, - clamp, - copysign, - cos, - exp2, - factorial, - floor, - frexp, - gcd, - iota, - isclose, - isqrt, - lcm, - log, - log2, - sin, - sqrt, - trunc, - ulp, -) -from sys.info import has_neon - -from testing import assert_almost_equal, assert_equal, assert_false, assert_true - -from utils import Span -from utils.numerics import inf, isinf, isnan, nan, neg_inf - - -fn test_sin() raises: - assert_almost_equal(sin(Float32(1.0)), 0.841470956802) - - -fn test_cos() raises: - assert_almost_equal(cos(Float32(1.0)), 0.540302276611) - - # TODO(KERN-228): support BF16 on neon systems. - @parameter - if not has_neon(): - assert_equal(cos(BFloat16(2.0)), -0.416015625) - - -fn test_factorial() raises: - assert_equal(factorial(0), 1) - assert_equal(factorial(1), 1) - assert_equal(factorial(15), 1307674368000) - assert_equal(factorial(20), 2432902008176640000) - - -def test_copysign(): - var x = Int32(2) - assert_equal(2, copysign(x, x)) - assert_equal(-2, copysign(x, -x)) - assert_equal(2, copysign(-x, x)) - assert_equal(-2, copysign(-x, -x)) - - assert_equal(1, copysign(Float32(1), Float32(2))) - assert_equal(-1, copysign(Float32(1), Float32(-2))) - assert_equal(neg_inf[DType.float32](), copysign(inf[DType.float32](), -2.0)) - assert_equal(-nan[DType.float32](), copysign(nan[DType.float32](), -2.0)) - - # Test some cases with 0 and signed zero - assert_equal(1, copysign(Float32(1.0), Float32(0.0))) - assert_equal(0, copysign(Float32(0.0), Float32(1.0))) - assert_equal(0, copysign(Float32(-0.0), Float32(1.0))) - assert_equal(-0, copysign(Float32(0.0), Float32(-1.0))) - - # TODO: Add some test cases for SIMD vector with width > 1 - - -def test_isclose(): - assert_true(isclose(Int64(2), Int64(2), atol=0, rtol=0)) - assert_false(isclose(Int64(2), Int64(3), atol=0, rtol=0)) - - assert_true(isclose(Float32(2), Float32(2))) - assert_true(isclose(Float32(2), Float32(2), rtol=1e-9)) - assert_true(isclose(Float32(2), Float32(2.00001), rtol=1e-3)) - assert_true( - isclose(nan[DType.float32](), nan[DType.float32](), equal_nan=True) - ) - - assert_true( - isclose( - SIMD[DType.float32, 4](1, 2, 3, nan[DType.float32]()), - SIMD[DType.float32, 4](1, 2, 3, nan[DType.float32]()), - equal_nan=True, - ).reduce_and() - ) - - assert_false( - isclose( - SIMD[DType.float32, 4](1, 2, nan[DType.float32](), 3), - SIMD[DType.float32, 4](1, 2, nan[DType.float32](), 4), - equal_nan=True, - ).reduce_and() - ) - - -def test_ceil(): - # We just test that the `ceil` function resolves correctly for a few common - # types. Types should test their own `__ceil__` implementation explicitly. - assert_equal(ceil(0), 0) - assert_equal(ceil(Int(5)), 5) - assert_equal(ceil(1.5), 2.0) - assert_equal(ceil(Float32(1.4)), 2.0) - assert_equal(ceil(Float64(-3.6)), -3.0) - - -def test_floor(): - # We just test that the `floor` function resolves correctly for a few common - # types. Types should test their own `__floor__` implementation explicitly. - assert_equal(floor(0), 0) - assert_equal(floor(Int(5)), 5) - assert_equal(floor(1.5), 1.0) - assert_equal(floor(Float32(1.6)), 1.0) - assert_equal(floor(Float64(-3.4)), -4.0) - - -def test_trunc(): - # We just test that the `trunc` function resolves correctly for a few common - # types. Types should test their own `__trunc__` implementation explicitly. - assert_equal(trunc(0), 0) - assert_equal(trunc(Int(5)), 5) - assert_equal(trunc(1.5), 1.0) - assert_equal(trunc(Float32(1.6)), 1.0) - assert_equal(trunc(Float64(-3.4)), -3.0) - - -def test_exp2(): - assert_equal(exp2(Float32(1)), 2.0) - assert_almost_equal(exp2(Float32(0.2)), 1.148696) - assert_equal(exp2(Float32(0)), 1.0) - assert_equal(exp2(Float32(-1)), 0.5) - assert_equal(exp2(Float32(2)), 4.0) - - -def test_iota(): - alias length = 103 - var offset = 2 - - var vector = List[Int32]() - vector.resize(length, 0) - - var buff = vector.data - iota(buff, length, offset) - - for i in range(length): - assert_equal(vector[i], offset + i) - - iota(vector, offset) - - for i in range(length): - assert_equal(vector[i], offset + i) - - var vector2 = List[Int]() - vector2.resize(length, 0) - iota(vector2, offset) - - for i in range(length): - assert_equal(vector2[i], offset + i) - - -alias F32x4 = SIMD[DType.float32, 4] -alias F64x4 = SIMD[DType.float64, 4] - - -def test_sqrt(): - var i = SIMD[DType.index, 4](0, 1, 2, 3) - assert_equal(sqrt(i**2), i) - assert_equal(sqrt(64), 8) - assert_equal(sqrt(63), 7) - - var f32x4 = 0.5 * F32x4(0.0, 1.0, 2.0, 3.0) - - var s1_f32 = sqrt(f32x4) - assert_equal(s1_f32[0], 0.0) - assert_almost_equal(s1_f32[1], 0.70710) - assert_equal(s1_f32[2], 1.0) - assert_almost_equal(s1_f32[3], 1.22474) - - var s2_f32 = sqrt(0.5 * f32x4) - assert_equal(s2_f32[0], 0.0) - assert_equal(s2_f32[1], 0.5) - assert_almost_equal(s2_f32[2], 0.70710) - assert_almost_equal(s2_f32[3], 0.86602) - - var f64x4 = 0.5 * F64x4(0.0, 1.0, 2.0, 3.0) - - var s1_f64 = sqrt(f64x4) - assert_equal(s1_f64[0], 0.0) - assert_almost_equal(s1_f64[1], 0.70710) - assert_equal(s1_f64[2], 1.0) - assert_almost_equal(s1_f64[3], 1.22474) - - var s2_f64 = sqrt(0.5 * f64x4) - assert_equal(s2_f64[0], 0.0) - assert_equal(s2_f64[1], 0.5) - assert_almost_equal(s2_f64[2], 0.70710) - assert_almost_equal(s2_f64[3], 0.86602) - - -def test_isqrt(): - var f32x4 = 0.5 * F32x4(0.0, 1.0, 2.0, 3.0) + 1 - - var s1_f32 = isqrt(f32x4) - assert_equal(s1_f32[0], 1.0) - assert_almost_equal(s1_f32[1], 0.81649) - assert_almost_equal(s1_f32[2], 0.70710) - assert_almost_equal(s1_f32[3], 0.63245) - - var s2_f32 = isqrt(0.5 * f32x4) - assert_almost_equal(s2_f32[0], 1.41421) - assert_almost_equal(s2_f32[1], 1.15470) - assert_equal(s2_f32[2], 1.0) - assert_almost_equal(s2_f32[3], 0.89442) - - var f64x4 = 0.5 * F64x4(0.0, 1.0, 2.0, 3.0) + 1 - - var s1_f64 = isqrt(f64x4) - assert_equal(s1_f64[0], 1.0) - assert_almost_equal(s1_f64[1], 0.81649) - assert_almost_equal(s1_f64[2], 0.70710) - assert_almost_equal(s1_f64[3], 0.63245) - - var s2_f64 = isqrt(0.5 * f64x4) - assert_almost_equal(s2_f64[0], 1.41421) - assert_almost_equal(s2_f64[1], 1.15470) - assert_equal(s2_f64[2], 1.0) - assert_almost_equal(s2_f64[3], 0.89442) - - -def _test_frexp_impl[type: DType](*, atol: Float32, rtol: Float32): - var res0 = frexp(Scalar[type](123.45)) - assert_almost_equal( - res0[0].cast[DType.float32](), 0.964453, atol=atol, rtol=rtol - ) - assert_almost_equal( - res0[1].cast[DType.float32](), 7.0, atol=atol, rtol=rtol - ) - - var res1 = frexp(Scalar[type](0.1)) - assert_almost_equal( - res1[0].cast[DType.float32](), 0.8, atol=atol, rtol=rtol - ) - assert_almost_equal( - res1[1].cast[DType.float32](), -3.0, atol=atol, rtol=rtol - ) - - var res2 = frexp(Scalar[type](-0.1)) - assert_almost_equal( - res2[0].cast[DType.float32](), -0.8, atol=atol, rtol=rtol - ) - assert_almost_equal( - res2[1].cast[DType.float32](), -3.0, atol=atol, rtol=rtol - ) - - var res3 = frexp(SIMD[type, 4](0, 2, 4, 5)) - assert_almost_equal( - res3[0].cast[DType.float32](), - SIMD[DType.float32, 4](0.0, 0.5, 0.5, 0.625), - atol=atol, - rtol=rtol, - ) - assert_almost_equal( - res3[1].cast[DType.float32](), - SIMD[DType.float32, 4](-0.0, 2.0, 3.0, 3.0), - atol=atol, - rtol=rtol, - ) - - -def _test_log_impl[type: DType](*, atol: Float32, rtol: Float32): - var res0 = log(Scalar[type](123.45)) - assert_almost_equal( - res0.cast[DType.float32](), 4.8158, atol=atol, rtol=rtol - ) - - var res1 = log(Scalar[type](0.1)) - assert_almost_equal( - res1.cast[DType.float32](), -2.3025, atol=atol, rtol=rtol - ) - - var res2 = log(SIMD[type, 4](1, 2, 4, 5)) - assert_almost_equal( - res2.cast[DType.float32](), - SIMD[DType.float32, 4](0.0, 0.693147, 1.38629, 1.6094), - atol=atol, - rtol=rtol, - ) - - var res3 = log(Scalar[type](2.7182818284590452353602874713526624977572)) - assert_almost_equal(res3.cast[DType.float32](), 1.0, atol=atol, rtol=rtol) - - var res4 = isinf(log(SIMD[type, 4](0, 1, 0, 0))) - assert_equal(res4, SIMD[DType.bool, 4](True, False, True, True)) - - -def _test_log2_impl[type: DType](*, atol: Float32, rtol: Float32): - var res0 = log2(Scalar[type](123.45)) - assert_almost_equal( - res0.cast[DType.float32](), 6.9477, atol=atol, rtol=rtol - ) - - var res1 = log2(Scalar[type](0.1)) - assert_almost_equal( - res1.cast[DType.float32](), -3.3219, atol=atol, rtol=rtol - ) - - var res2 = log2(SIMD[type, 4](1, 2, 4, 5)) - assert_almost_equal( - res2.cast[DType.float32](), - SIMD[DType.float32, 4](0.0, 1.0, 2.0, 2.3219), - atol=atol, - rtol=rtol, - ) - - -def test_frexp(): - _test_frexp_impl[DType.float32](atol=1e-4, rtol=1e-5) - _test_frexp_impl[DType.float16](atol=1e-2, rtol=1e-5) - - # TODO(KERN-228): support BF16 on neon systems. - @parameter - if not has_neon(): - _test_frexp_impl[DType.bfloat16](atol=1e-1, rtol=1e-5) - - -def test_log(): - _test_log_impl[DType.float32](atol=1e-4, rtol=1e-5) - _test_log_impl[DType.float16](atol=1e-2, rtol=1e-5) - - # TODO(KERN-228): support BF16 on neon systems. - @parameter - if not has_neon(): - _test_log_impl[DType.bfloat16](atol=1e-1, rtol=1e-5) - - -def test_log2(): - _test_log2_impl[DType.float32](atol=1e-4, rtol=1e-5) - _test_log2_impl[DType.float16](atol=1e-2, rtol=1e-5) - - # TODO(KERN-228): support BF16 on neon systems. - @parameter - if not has_neon(): - _test_log2_impl[DType.bfloat16](atol=1e-1, rtol=1e-5) - - -def test_gcd(): - var l = List(2, 4, 6, 8, 16) - var il = InlineArray[Int, 5](4, 16, 2, 8, 6) - assert_equal(gcd(Span[Int](il)), 2) - assert_equal(gcd(2, 4, 6, 8, 16), 2) - assert_equal(gcd(l), 2) - assert_equal(gcd(88, 24), 8) - assert_equal(gcd(0, 0), 0) - assert_equal(gcd(1, 0), 1) - assert_equal(gcd(-2, 4), 2) - assert_equal(gcd(-2, -4), 2) - assert_equal(gcd(-2, 0), 2) - assert_equal(gcd(2, -4), 2) - assert_equal(gcd(24826148, 45296490), 526) - assert_equal(gcd(0, 9), 9) - assert_equal(gcd(4, 4), 4) - assert_equal(gcd(8), 8) - assert_equal(gcd(), 0) - assert_equal(gcd(List[Int]()), 0) - assert_equal(gcd(List(16)), 16) - - -def test_lcm(): - assert_equal(lcm(-2, 4), 4) - assert_equal(lcm(2345, 23452), 54994940) - var l = List(4, 6, 7, 3) - assert_equal(lcm(Span(l)), 84) - assert_equal(lcm(l), 84) - assert_equal(lcm(4, 6, 7, 3), 84) - assert_equal(lcm(), 1) - assert_equal(lcm(List(3)), 3) - assert_equal(lcm(List[Int]()), 1) - assert_equal(lcm(0, 4), 0) - assert_equal(lcm(5, 33), 165) - assert_equal(lcm(-34, -56, -32), 3808) - var il = InlineArray[Int, 5](4, 16, 2, 8, 6) - assert_equal(lcm(Span[Int](il)), 48) - assert_equal(lcm(345, 623, 364, 84, 93), 346475220) - assert_equal(lcm(0, 0), 0) - - -def test_ulp(): - assert_true(isnan(ulp(nan[DType.float32]()))) - assert_true(isinf(ulp(inf[DType.float32]()))) - assert_true(isinf(ulp(-inf[DType.float32]()))) - assert_almost_equal(ulp(Float64(0)), 5e-324) - assert_equal(ulp(Float64.MAX_FINITE), 1.99584030953472e292) - assert_equal(ulp(Float64(5)), 8.881784197001252e-16) - assert_equal(ulp(Float64(-5)), 8.881784197001252e-16) - - -def test_ceildiv(): - # NOTE: these tests are here mostly to ensure the ceildiv method exists. - # Types that opt in to CeilDivable, should test their own dunder methods for - # correctness. - assert_equal(ceildiv(53.6, 1.35), 40.0) - - # Test the IntLiteral overload. - alias a: IntLiteral = ceildiv(1, 7) - assert_equal(a, 1) - alias b: IntLiteral = ceildiv(548, -7) - assert_equal(b, -78) - - # Test the Int overload. - assert_equal(ceildiv(Int(1), Int(7)), 1) - assert_equal(ceildiv(Int(548), Int(-7)), -78) - assert_equal(ceildiv(Int(-55), Int(8)), -6) - assert_equal(ceildiv(Int(-55), Int(-8)), 7) - - # Test the UInt overload. - assert_equal(ceildiv(UInt(1), UInt(7)), UInt(1)) - assert_equal(ceildiv(UInt(546), UInt(7)), UInt(78)) - - -def test_align_down(): - assert_equal(align_down(1, 7), 0) - assert_equal(align_down(548, -7), 553) - assert_equal(align_down(-548, -7), -546) - assert_equal(align_down(-548, 7), -553) - - # Test the UInt overload. - assert_equal(align_down(UInt(1), UInt(7)), UInt(0)) - assert_equal(align_down(UInt(546), UInt(7)), UInt(546)) - - -def test_align_up(): - assert_equal(align_up(1, 7), 7) - assert_equal(align_up(548, -7), 546) - assert_equal(align_up(-548, -7), -553) - assert_equal(align_up(-548, 7), -546) - - # Test the UInt overload. - assert_equal(align_up(UInt(1), UInt(7)), UInt(7)) - assert_equal(align_up(UInt(546), UInt(7)), UInt(546)) - - -def test_clamp(): - assert_equal(clamp(Int(1), 0, 1), 1) - assert_equal(clamp(Int(2), 0, 1), 1) - assert_equal(clamp(Int(-2), 0, 1), 0) - - assert_equal(clamp(UInt(1), UInt(0), UInt(1)), UInt(1)) - assert_equal(clamp(UInt(2), UInt(0), UInt(1)), UInt(1)) - assert_equal(clamp(UInt(1), UInt(2), UInt(4)), UInt(2)) - - assert_equal( - clamp(SIMD[DType.float32, 4](0, 1, 3, 4), 0, 1), - SIMD[DType.float32, 4](0, 1, 1, 1), - ) - - -def main(): - test_sin() - test_cos() - test_factorial() - test_copysign() - test_isclose() - test_ceil() - test_floor() - test_trunc() - test_exp2() - test_iota() - test_sqrt() - test_isqrt() - test_frexp() - test_log() - test_log2() - test_gcd() - test_lcm() - test_ulp() - test_ceildiv() - test_align_down() - test_align_up() - test_clamp() diff --git a/stdlib/test/math/test_modf.mojo b/stdlib/test/math/test_modf.mojo deleted file mode 100644 index f0ce61b359..0000000000 --- a/stdlib/test/math/test_modf.mojo +++ /dev/null @@ -1,52 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s - -from math import modf - -from test_utils import libm_call -from testing import assert_almost_equal, assert_equal - - -def test_modf(): - var i32 = modf(Int32(123)) - assert_equal(i32[0], 123) - assert_equal(i32[1], 0) - - var f32 = modf(Float32(123.5)) - assert_almost_equal(f32[0], 123) - assert_almost_equal(f32[1], 0.5) - - var f64 = modf(Float64(123.5)) - assert_almost_equal(f64[0], 123) - assert_almost_equal(f64[1], 0.5) - - f64 = modf(Float64(0)) - assert_almost_equal(f64[0], 0) - assert_almost_equal(f64[1], 0) - - f64 = modf(Float64(0.5)) - assert_almost_equal(f64[0], 0) - assert_almost_equal(f64[1], 0.5) - - f64 = modf(Float64(-0.5)) - assert_almost_equal(f64[0], -0) - assert_almost_equal(f64[1], -0.5) - - f64 = modf(Float64(-1.5)) - assert_almost_equal(f64[0], -1) - assert_almost_equal(f64[1], -0.5) - - -def main(): - test_modf() diff --git a/stdlib/test/math/test_polynomial.mojo b/stdlib/test/math/test_polynomial.mojo deleted file mode 100644 index 822a41bdac..0000000000 --- a/stdlib/test/math/test_polynomial.mojo +++ /dev/null @@ -1,58 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo %s - -from math.polynomial import _horner_evaluate, polynomial_evaluate - -from testing import assert_equal - - -def test_polynomial_evaluate_degree3(): - # Evaluate 1000 + x + x^2 - alias coeffs = List[SIMD[DType.float64, 1]](1000.0, 1.0, 1.0) - - assert_equal(_horner_evaluate[coeffs](1.0), 1002.0) - assert_equal(polynomial_evaluate[coeffs](1.0), 1002.0) - assert_equal(_horner_evaluate[coeffs](0.1), 1000.11) - assert_equal(polynomial_evaluate[coeffs](0.1), 1000.11) - - -def test_polynomial_evaluate_degree4(): - # Evalaute 1000 + 99 x - 43 x^2 + 12 x^3 - 14 x^4 - alias coeffs = List[SIMD[DType.float64, 1]]( - 1000.0, 99.0, -43.0, 12.0, -14.0 - ) - - assert_equal(_horner_evaluate[coeffs](1.0), 1054.0) - assert_equal(polynomial_evaluate[coeffs](1.0), 1054.0) - assert_equal(_horner_evaluate[coeffs](0.1), 1009.4806) - assert_equal(polynomial_evaluate[coeffs](0.1), 1009.4806) - - -def test_polynomial_evaluate_degree10(): - # Evaluate 20.0 + 9.0 x + 1.0 x^2 + 1.0 x^3 + 1.0 x^4 + 1.0 x^5 + 1.0 x^6 + - # 1.0 x^7 + 1.0 x^8 + 43.0 x^9 + 10.0 x^10 - alias coeffs = List[SIMD[DType.float64, 1]]( - 20.0, 9.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 43.0, 10.0 - ) - - assert_equal(_horner_evaluate[coeffs](1.0), 89.0) - assert_equal(polynomial_evaluate[coeffs](1.0), 89.0) - assert_equal(_horner_evaluate[coeffs](0.1), 20.911111154) - assert_equal(polynomial_evaluate[coeffs](0.1), 20.911111154) - - -def main(): - test_polynomial_evaluate_degree3() - test_polynomial_evaluate_degree4() - test_polynomial_evaluate_degree10() diff --git a/stdlib/test/math/test_tanh.mojo b/stdlib/test/math/test_tanh.mojo deleted file mode 100644 index 6a3be0ad10..0000000000 --- a/stdlib/test/math/test_tanh.mojo +++ /dev/null @@ -1,85 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# REQUIRES: linux -# RUN: %mojo %s - -from math import tanh -from random import randn, seed - -from test_utils import compare, libm_call -from testing import assert_almost_equal - - -fn tanh_libm[ - type: DType, simd_width: Int -](arg: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - return libm_call[type, simd_width, "tanhf", "tanh"](arg) - - -def test_tanh_libm[N: Int = 8192](): - seed(0) - alias test_dtype = DType.float32 - var x32 = UnsafePointer[Scalar[test_dtype]].alloc(N) - randn[test_dtype](x32, N, 0, 9.0) - print("For N=" + str(N) + " randomly generated vals; mean=0.0, var=9.0") - - #################### - # mojo tanh result - #################### - var y32 = UnsafePointer[Scalar[test_dtype]].alloc(N) - for i in range(N): - y32[i] = tanh(x32[i]) - - #################### - ## libm tanh result - #################### - var libm_out = UnsafePointer[Scalar[test_dtype]].alloc(N) - for i in range(N): - libm_out[i] = tanh_libm(x32[i]) - - # abs_rel_err = (abs_min, abs_max, rel_min, rel_max) - var abs_rel_err = SIMD[test_dtype, 4]( - 0.0, 2.384185791015625e-07, 0.0, 2.5438197326366208e-07 - ) - - var err = compare[test_dtype](y32, libm_out, N, msg="Compare Mojo vs. LibM") - assert_almost_equal(err, abs_rel_err) - - x32.free() - y32.free() - libm_out.free() - - -def test_direct(): - alias F32x4 = SIMD[DType.float32, 4] - var f32x4 = 0.5 * F32x4(0.0, 1.0, 2.0, 3.0) - assert_almost_equal( - tanh(f32x4), F32x4(0.0, 0.462117165, 0.761594176, 0.905148208) - ) - assert_almost_equal( - tanh(0.5 * f32x4), F32x4(0.0, 0.244918659, 0.462117165, 0.635149002) - ) - - alias F64x4 = SIMD[DType.float64, 4] - var f64x4 = 0.5 * F64x4(0.0, 1.0, 2.0, 3.0) - assert_almost_equal( - tanh(f64x4), F64x4(0.0, 0.462117165, 0.761594176, 0.905148208) - ) - assert_almost_equal( - tanh(0.5 * f64x4), F64x4(0.0, 0.244918659, 0.462117165, 0.635149002) - ) - - -def main(): - test_direct() - test_tanh_libm() diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index a0b404cba1..50cb4749b9 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -11,7 +11,6 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from .compare_utils import compare from .test_utils import libm_call from .types import ( CopyCounter, diff --git a/stdlib/test/test_utils/compare_utils.mojo b/stdlib/test/test_utils/compare_utils.mojo deleted file mode 100644 index 6f67adc09d..0000000000 --- a/stdlib/test/test_utils/compare_utils.mojo +++ /dev/null @@ -1,58 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # - - -fn _minmax[ - type: DType, // -](x: UnsafePointer[Scalar[type]], N: Int) -> Tuple[Scalar[type], Scalar[type]]: - var max_val = x[0] - var min_val = x[0] - for i in range(1, N): - if x[i] > max_val: - max_val = x[i] - if x[i] < min_val: - min_val = x[i] - return (min_val, max_val) - - -fn compare[ - dtype: DType, verbose: Bool = True -]( - x: UnsafePointer[Scalar[dtype]], - y: UnsafePointer[Scalar[dtype]], - num_elements: Int, - *, - msg: String = "", -) -> SIMD[dtype, 4]: - var atol = UnsafePointer[Scalar[dtype]].alloc(num_elements) - var rtol = UnsafePointer[Scalar[dtype]].alloc(num_elements) - - for i in range(num_elements): - var d = abs(x[i] - y[i]) - var e = abs(d / y[i]) - atol[i] = d - rtol[i] = e - - var atol_minmax = _minmax(atol, num_elements) - var rtol_minmax = _minmax(rtol, num_elements) - if verbose: - if msg: - print(msg) - print("AbsErr-Min/Max", atol_minmax[0], atol_minmax[1]) - print("RelErr-Min/Max", rtol_minmax[0], rtol_minmax[1]) - print("==========================================================") - atol.free() - rtol.free() - return SIMD[dtype, 4]( - atol_minmax[0], atol_minmax[1], rtol_minmax[0], rtol_minmax[1] - ) From 4a6c7b9a2bfbf7246485c9f5e336c61ff338e7b9 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 27 Aug 2024 13:24:59 -0500 Subject: [PATCH 1453/2019] [stdlib] Make `Reference` conform to `Stringable` Implement `Reference.__str__()` so it conforms to `Stringable` and can then be used nicer in tests via `assert_equal` which requires the `Testable` trait (`Stringable` + `EqualityComparable`). MODULAR_ORIG_COMMIT_REV_ID: 80d778c718d484d6a10b919702524dfeb600b99e --- stdlib/src/memory/reference.mojo | 11 ++++++++++- stdlib/test/memory/test_reference.mojo | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 5c2ccb6097..b5db879d2e 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -272,7 +272,7 @@ struct Reference[ type: AnyType, lifetime: AnyLifetime[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, -](CollectionElementNew): +](CollectionElementNew, Stringable): """Defines a non-nullable safe reference. Parameters: @@ -358,3 +358,12 @@ struct Reference[ True if the two pointers are not equal and False otherwise. """ return not (self == rhs) + + @no_inline + fn __str__(self) -> String: + """Gets a string representation of the Reference. + + Returns: + The string representation of the Reference. + """ + return str(UnsafePointer(__mlir_op.`lit.ref.to_pointer`(self.value))) diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo index 8eaa4df330..8d71065bc6 100644 --- a/stdlib/test/memory/test_reference.mojo +++ b/stdlib/test/memory/test_reference.mojo @@ -36,6 +36,13 @@ def test_equality(): assert_true(Reference(a) != Reference(b)) +def test_str(): + var a = Int(42) + var a_ref = Reference(a) + assert_true(str(a_ref).startswith("0x")) + + def main(): test_copy_reference_explicitly() test_equality() + test_str() From df60784dc96d6d0b58eb6a937f8b670159970674 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 27 Aug 2024 17:11:41 -0500 Subject: [PATCH 1454/2019] [examples] Add pixi.tomls for conda package MODULAR_ORIG_COMMIT_REV_ID: 6dd08d3e282daad6fc5b7ddd1b2ee09595eb444e --- pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index 445365dcf0..af506b0fc5 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,7 +1,7 @@ [project] name = "Mojo" authors = ["Modular "] -channels = ["conda-forge", "anaconda", "https://conda.cloudsmith.io/modular/max-nightly/"] +channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max-nightly/"] platforms = ["linux-64", "linux-aarch64", "osx-arm64"] [tasks] From 84166143f9754d626c65a0c210a160092c1c3f06 Mon Sep 17 00:00:00 2001 From: ematejska Date: Tue, 27 Aug 2024 16:07:09 -0700 Subject: [PATCH 1455/2019] [docs] Update `CONTRIBUTING.md` to clarify math contributions Update Contributor guide to clarify that math contributions are not accepted right now. MODULAR_ORIG_COMMIT_REV_ID: e1e63b96037668fa5b75f8ac1cae52190bdb8b37 --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21a863dcd5..b59db72aee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,6 +119,8 @@ accepted. For example: - Changes that do not align with the published roadmap or the core principles of the standard library. +- Changes to the math module until more thorough performance + benchmarking is available. - Code without tests—especially for core primitives. - Changes that break existing API or implicit behavior semantics. - Changes where the contributors’ favorite feature or system isn’t being used From 37c7e1e106b7e26c8253163f186141e23c141aad Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 27 Aug 2024 23:27:09 -0700 Subject: [PATCH 1456/2019] [docs] Fix the format command for magic init (#46123) MODULAR_ORIG_COMMIT_REV_ID: ef52fbfa8f0c40a9b9e5b2f32f1b5fce7abebc79 --- docs/manual/get-started.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index fb41d4f779..08dcbf6503 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -29,7 +29,7 @@ manager and package manager based on conda. 2. Create a Mojo project called "hello-world": ```sh - magic init hello-world --format mojo + magic init hello-world --format mojoproject ``` This creates a directory named `hello-world` and installs the Mojo project @@ -59,7 +59,7 @@ To install the latest nightly build, specify the `max-nightly` channel when you initialize your project: ```sh -magic init hello-world-nightly --format mojo \ +magic init hello-world-nightly --format mojoproject \ -c conda-forge -c https://conda.modular.com/max-nightly ``` From 28dd1228f11a2434819ef3dec35884f6067fb5a9 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 28 Aug 2024 11:01:24 -0700 Subject: [PATCH 1457/2019] [docs] Update public mojo README license desc Update README to clarify the difference between the open source license and MAX/Mojo license. MODULAR_ORIG_COMMIT_REV_ID: 893e767f1280a6a82ca9e8a2fb45e386db1cac86 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 561ed6a259..d4058593c0 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,10 @@ For more general questions or to chat with other Mojo developers, check out our ## License -This repository is licensed under the Apache License v2.0 with LLVM Exceptions -(see the LLVM [License](https://llvm.org/LICENSE.txt)). +This repository and its contributions are licensed under the Apache License v2.0 +with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). +MAX and Mojo usage and distribution are licensed under the +[MAX & Mojo Community License](https://www.modular.com/legal/max-mojo-license). ## Thanks to our contributors From eab686208336152ccd6ff9c145bcc173431d95e7 Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Wed, 28 Aug 2024 13:49:13 -0700 Subject: [PATCH 1458/2019] [stdlib] Re-enable mojo debug tests Re-enable tests that were disabled due to failing to compile under debug-level full. MODULAR_ORIG_COMMIT_REV_ID: c12709ff49848e1621f92fd896735fb4d022d9e6 --- stdlib/test/builtin/test_float_literal.mojo | 2 +- stdlib/test/builtin/test_int_literal.mojo | 2 +- stdlib/test/builtin/test_object.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/lit.cfg.py | 4 ---- stdlib/test/memory/test_memory.mojo | 2 +- stdlib/test/sys/test_intrinsics.mojo | 2 +- 7 files changed, 6 insertions(+), 10 deletions(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index d059aac25e..db4841df4d 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from testing import ( assert_almost_equal, diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 84a7460e72..72ffa712fa 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from testing import assert_equal, assert_true, assert_false diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 9b23cee855..65c118665a 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from random import random_float64 diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 2ffc309ff7..5b7347d2a5 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from sys import has_neon diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 0a23f7b544..b267ad4446 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -79,10 +79,6 @@ def has_not(): base_mojo_command = "mojo" config.substitutions.insert(0, ("%mojo", base_mojo_command)) - # Mojo without debug info. Only use this for known tests that do not work - # with debug info enabled. - config.substitutions.insert(0, ("%mojo-no-debug", base_mojo_command)) - # Mojo without assertions. Only use this for known tests that do not work # with assertions enabled. config.substitutions.insert(1, ("%bare-mojo", "mojo")) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index b582579545..08466b18ef 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from sys import sizeof, simdwidthof diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 17133c0e7c..bc30c829d4 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from sys import ( compressed_store, From 839d8a68dcb84be3dc95e0c26f4854d07efad787 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:14:49 -0700 Subject: [PATCH 1459/2019] Updated Python integration documentation for Magic CLI release MODULAR_ORIG_COMMIT_REV_ID: 0fb48b22d83f05d030e88f1f73aba5b7c122ca21 --- docs/manual/python/index.ipynb | 308 +++++++++------------------------ docs/manual/python/types.ipynb | 12 +- 2 files changed, 87 insertions(+), 233 deletions(-) diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 6e414cc178..e9e3caa2b1 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -20,59 +20,74 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Our long-term goal is to make Mojo a *superset of Python* (that is, to make Mojo \n", - "compatible with existing Python programs). Python programmers should be able to\n", - "use Mojo immediately, and be able to access the huge ecosystem of Python \n", - "packages that are available today. \n", - "\n", - "However, Mojo is still in early development and many Python features are not yet\n", + "Mojo is still in early development and many Python features are not yet\n", "implemented. You can't currently write everything in Mojo that you can write in\n", "Python. And Mojo doesn't have its own ecosystem of packages yet.\n", "\n", "To help bridge this gap, Mojo lets you import Python modules, call Python \n", - "functions and interact with Python objects from Mojo code. It runs Python code\n", - "using a standard Python interpreter (CPython), so your existing Python code\n", + "functions, and interact with Python objects from Mojo code. The Python code\n", + "runs in a standard Python interpreter (CPython), so your existing Python code\n", "doesn't need to change.\n", "\n", + "## Create a Python environment\n", + "\n", + "To successfully integrate Python code with your Mojo project, your environment\n", + "must have a compatible Python runtime installed along with any additional\n", + "Python packages that you want to use. Currently, you can create a compatible\n", + "environment in a couple of ways:\n", + "\n", + "- We recommend that you use [Magic](/magic), our package manager and\n", + " virtual environment manager for MAX and Mojo projects. To use Magic to create\n", + " and manage the virtual environment for your Mojo/Python project, first\n", + " follow the instructions in [Install Magic](/magic/#install-magic).\n", + " Then you can create a new Mojo project like this:\n", + "\n", + " ```sh\n", + " magic init my-mojo-project --format mojoproject\n", + " ```\n", + "\n", + " After creating the project, you can enter the project and install any\n", + " dependencies, for example [NumPy](https://numpy.org/):\n", + "\n", + " ```sh\n", + " cd my-mojo-project\n", + " ```\n", + "\n", + " ```sh\n", + " magic add \"numpy>=2.0\"\n", + " ```\n", + "\n", + "- Alternatively, you can also add MAX and Mojo to a\n", + " [conda](https://docs.conda.io/projects/conda/en/latest/index.html) project.\n", + " To do so, follow the steps in [Add MAX/Mojo to a conda project](/magic/conda).\n", + "\n", + "- It's also possible to convert an existing conda project to Magic as documented\n", + " in [Migrate a conda project to Magic](/magic/#migrate-a-conda-project-to-magic).\n", + "\n", "## Import a Python module\n", "\n", "To import a Python module in Mojo, just call \n", "[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) \n", - "with the module name:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ + "with the module name. The following shows an example of importing the standard\n", + "Python [NumPy](https://numpy.org/) package:\n", + "\n", + "```mojo\n", "from python import Python\n", "\n", - "fn use_array() raises:\n", + "def main():\n", " # This is equivalent to Python's `import numpy as np`\n", - " var np = Python.import_module(\"numpy\")\n", + " np = Python.import_module(\"numpy\")\n", "\n", " # Now use numpy as if writing in Python\n", - " var array = np.array([1, 2, 3])\n", - " print(array)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1 2 3]\n" - ] - } - ], - "source": [ - "use_array()" + " array = np.array([1, 2, 3])\n", + " print(array)\n", + "```\n", + "\n", + "Running this program produces the following output:\n", + "\n", + "```\n", + "[1 2 3]\n", + "```" ] }, { @@ -80,13 +95,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Yes, this imports Python NumPy, and you can import *any other Python module*\n", - "that you have installed.\n", + "Assuming that you have the NumPy package installed in your\n", + "[environment](#create-a-python-environment), this imports NumPy and you can use any\n", + "of its features.\n", "\n", "A few things to note:\n", "\n", + "- The `import_module()` method returns a reference to the module in the form of\n", + " a [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject)\n", + " wrapper. You must store the reference in a variable and then use it as shown\n", + " in the example above to access functions, classes, and other objects defined\n", + " by the module. See [Mojo wrapper objects](/mojo/manual/python/types#mojo-wrapper-objects)\n", + " for more information about the `PythonObject` type.\n", + "\n", "- Currently, you cannot import individual members (such as a single Python class\n", - " or function)—you must import the whole Python module and then access members\n", + " or function). You must import the whole Python module and then access members\n", " through the module name.\n", "\n", "- Mojo doesn't yet support top-level code, so the `import_module()` call must\n", @@ -108,7 +131,7 @@ "Mojo loads the Python interpreter and Python modules at runtime, so\n", "wherever you run a Mojo program, it must be able to access a compatible Python\n", "interpreter, and to locate any imported Python modules. For more information,\n", - "see [Python environment](#python-environment).\n", + "see [Create a Python environment](#create-a-python-environment).\n", "\n", ":::" ] @@ -139,17 +162,17 @@ "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", - "fn main() raises:\n", + "def main():\n", " Python.add_to_path(\"path/to/module\")\n", - " var mypython = Python.import_module(\"mypython\")\n", + " mypython = Python.import_module(\"mypython\")\n", "\n", - " var values = mypython.gen_random_values(2, 3)\n", + " values = mypython.gen_random_values(2, 3)\n", " print(values)\n", "```\n", "\n", "Both absolute and relative paths work with \n", - "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example, you\n", - "can import from the local directory like this:\n", + "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example,\n", + "you can import from the local directory like this:\n", "\n", "```mojo\n", "Python.add_to_path(\".\")\n", @@ -184,17 +207,10 @@ "application drive the event loop and poll for updates. The following example\n", "uses Tkinter, but the basic approach can be applied to other packages.\n", "\n", - "First we create a Python module that defines a Tkinter interface, with a window\n", - "and single button:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%%python\n", + "First you create a Python module that defines a Tkinter interface, with a window\n", + "and single button:\n", + "\n", + "```{.python filename=\"myapp.py\"}\n", "import tkinter as tk\n", "\n", "class App:\n", @@ -218,37 +234,33 @@ " self.create_button(\"Hello Mojo!\")\n", "\n", " def update(self):\n", - " self._root.update()" + " self._root.update()\n", + "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can call this module from Mojo like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ + "You can call this module from Mojo like this:\n", + "\n", + "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", - "fn button_clicked():\n", + "def button_clicked():\n", " print(\"Hi from a Mojo🔥 fn!\")\n", "\n", "def main():\n", " Python.add_to_path(\".\")\n", - " var app = Python.import_module(\"myapp\").App()\n", + " app = Python.import_module(\"myapp\").App()\n", " app.create(\"800x600\")\n", "\n", " while True:\n", " app.update()\n", " if app.clicked:\n", " button_clicked()\n", - " app.clicked = False" + " app.clicked = False\n", + "```" ] }, { @@ -257,159 +269,7 @@ "source": [ "Instead of the Python module calling the Tkinter `mainloop()` method, the Mojo \n", "code calls the `update()` method in a loop and checks the `clicked` attribute \n", - "after each update.\n", - "\n", - "## Python environment\n", - "\n", - "The Mojo SDK depends on an existing Python dynamic library. At runtime, Mojo\n", - "uses the first Python in the search path (`PATH`), to find an associated dynamic\n", - "Python library of the same version. This will also add any modules from the\n", - "activated virtual environment. \n", - "\n", - "### Resolving issues\n", - "\n", - "Finding libpython may fail if the Python interpreter on top of `PATH` does not\n", - "have an associated dynamic library. Some Python distributions don't include the\n", - "shared library, and others only have a static library which isn't supported by\n", - "Mojo yet.\n", - "\n", - "You can find a compatible Python on your system by running this Python script:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "python" - } - }, - "outputs": [], - "source": [ - "import os\n", - "import subprocess\n", - "\n", - "FIND_LIBPYTHON = \"\"\"\n", - "import os\n", - "import sys\n", - "from pathlib import Path\n", - "from sysconfig import get_config_var\n", - "ext = \"dll\" if os.name == \"nt\" else \"dylib\" if sys.platform == \"darwin\" else \"so\"\n", - "binary = f\"libpython{get_config_var('py_version_short')}.{ext}\"\n", - "for folder in [Path(get_config_var(p)) for p in [\"LIBPL\", \"LIBDIR\"]]:\n", - " libpython_path = folder / binary\n", - " if libpython_path.is_file():\n", - " print(libpython_path.resolve())\n", - " exit(0)\n", - "exit(1)\n", - "\"\"\"\n", - "FIND_PYTHON_VER = \"import sysconfig; print(sysconfig.get_python_version())\"\n", - "\n", - "exe_names = [\"python3\", \"python\"] + [f\"python3.{i}\" for i in range(8, 13)]\n", - "seen = []\n", - "executables = []\n", - "\n", - "print(\"Mojo will attempt to use the first python executable from the top:\\n\")\n", - "print(\"vers | compat | path\")\n", - "for path in os.environ[\"PATH\"].split(\":\"):\n", - " for exe in exe_names:\n", - " full_path = os.path.join(path, exe)\n", - " if os.path.exists(full_path):\n", - " pyver = subprocess.check_output([full_path, \"-c\", FIND_PYTHON_VER], text=True).strip()\n", - " res = subprocess.run([full_path, \"-c\", FIND_LIBPYTHON], text=True, capture_output=True)\n", - " libpython = res.stdout.strip()\n", - " if res.returncode != 0:\n", - " print(f\"{pyver:<7} no {full_path}\")\n", - " elif libpython not in seen:\n", - " print(f\"{pyver:<7} yes {full_path}\")\n", - " seen.append(libpython)\n", - " executables.append(full_path)\n", - "\n", - "if not executables:\n", - " print(\"no compatible Python environments found\")\n", - "else:\n", - " print(\"\\ncreate and activate a virtual environment to use a different Python version:\")\n", - " print(f\" {executables[-1]} -m venv .venv\")\n", - " print(\" source .venv/bin/activate\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Which will produce output like:\n", - "\n", - "```output\n", - "Mojo will attempt to use the first python executable from the top:\n", - "\n", - "vers | compat | path\n", - "3.11 yes /opt/homebrew/opt/python@3.11/libexec/bin/python3\n", - "3.12 yes /opt/homebrew/bin/python3\n", - "3.9 yes /usr/bin/python3\n", - "\n", - "create and activate a virtual environment to use a different Python version:\n", - " /usr/bin/python3 -m venv .venv\n", - " source .venv/bin/activate\n", - "```\n", - "\n", - "If you have no compatible environment, you can install a compatible version of\n", - "Python that includes shared libraries. Try following the instructions in [Set up\n", - "a Python environment with Conda](#set-up-a-python-environment-with-conda) to\n", - "install a virtual environment. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Set up a Python environment with Conda\n", - "\n", - "Using a Python virtual environment such as \n", - "[Conda](https://docs.conda.io/en/latest/) is one way to get a version of Python\n", - "that will work reliably with Mojo. It comes with the required dynamic library,\n", - "and ensures there are no conflicts with system dependencies. \n", - "\n", - "To set up a virtual environment with Conda:\n", - "\n", - "1. Install Conda by following the \n", - " [Quick command-line install instructions](https://docs.conda.io/projects/miniconda/en/latest/#quick-command-line-install).\n", - "\n", - "2. Initialize Conda for all the shells on your path:\n", - "\n", - " ```bash\n", - " ~/miniconda3/bin/conda init --all\n", - " ```\n", - "\n", - " Or just one at a time:\n", - "\n", - " ```bash\n", - " ~/miniconda3/bin/conda init zsh\n", - " ```\n", - "\n", - "2. Restart your shell. \n", - "\n", - "3. Install your desired version of Python and activate the environment:\n", - "\n", - " ```bash\n", - " conda create -n 3.10 python=3.10\n", - " conda activate 3.10\n", - " ```\n", - "\n", - "After setting up the Conda virtual environment, you can install any Python \n", - "packages you want to use with Mojo, with `conda install` or `pip install`. For\n", - "example:\n", - "\n", - "```bash\n", - "conda install numpy\n", - "pip install pillow\n", - "```\n", - "\n", - "Now whenever you `conda activate 3.10`, Mojo will be able to find any modules\n", - "you installed into that environment.\n", - "\n", - "For more information on using Conda with Mojo, see \n", - "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on\n", - "the Modular Blog." + "after each update.\n" ] } ], diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index f3c74ebe72..c7c4c4e558 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -128,7 +128,7 @@ "### Mojo wrapper objects\n", "\n", "When you use Python objects in your Mojo code, Mojo adds the \n", - "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", + "[`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) wrapper around\n", "the Python object. This object exposes a number of common double underscore\n", "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n", "through to the underlying Python object. " @@ -219,7 +219,7 @@ "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", - "[`to_float64()`](/mojo/stdlib/python/object/PythonObject#to_float64) for \n", + "[`to_float64()`](/mojo/stdlib/python/python_object/PythonObject#to_float64) for \n", "converting to a Mojo floating point value.\n", "\n", "```mojo\n", @@ -267,13 +267,7 @@ "metadata": {}, "source": [ "One TODO item here: The `Python.is_type()` method is misleadingly named, since \n", - "it doesn't compare _types_, but object identity.\n", - "\n", - "## Further reading\n", - "\n", - "For more information, see \n", - "[Using Mojo with Python](https://www.modular.com/blog/using-mojo-with-python) on \n", - "the Modular Blog." + "it doesn't compare _types_, but object identity.\n" ] } ], From abff40ccafa394ac78e7cd4cff0862757b880055 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Wed, 28 Aug 2024 23:55:21 -0400 Subject: [PATCH 1460/2019] [mojo][vscode] Update changelog with some new mojo tooling features MODULAR_ORIG_COMMIT_REV_ID: 3ecf8d4dc1410b318e020963ed6aee349cdebcd7 --- docs/changelog.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 83ac4a8820..a68bf6f71d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -444,6 +444,21 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. UnsafePointer[type, alignment].alloc(x) ``` +- The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo features, + including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and + more. + +- The Mojo debugger now hides the artificial function arguments `__result__` and + `__error__` created by the compiler for Mojo code. + +- The Mojo debugger now supports a `break-on-raise` command that indicated the + debugger to stop at any `raise` statements. A similar features has been added + to the debugger on VS Code. + +- A proxy has been added to the Mojo Language Server on VS Code that handles + crashes more gracefully. + ### 🦋 Changed - The set of automatically imported entities (types, aliases, functions) into user's @@ -713,6 +728,9 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. These changes do not apply to doctests, due to their different semantics. +- The `mojo debug --rpc` command has been renamed to `mojo debug --vscode`, + which is now able to manage multiple VS Code windows. + ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from @@ -737,6 +755,10 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is available using the builting `min` and `max` functions. +- `Run Mojo File in Dedicated Terminal` action has been removed, and the + action `Run Mojo File` will always open a dedicated terminal for each mojo + file to guarantee a correct environment. + ### 🛠️ Fixed - Fixed a crash in the Mojo Language Server when importing the current file. @@ -774,3 +796,8 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated references to `let` in REPL documentation. + +- The VS Code extension doesn't cache anymore the information of the selected + MAX SDK, which was causing issues upon changes in the SDK. + +- The Mojo debugger now stops showing spurious warnings when parsing closures. From 253de02ae5b31ceba7c7f9806dbc77bb2ab0594f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 28 Aug 2024 22:42:51 -0700 Subject: [PATCH 1461/2019] [mojo-lang] Make lifetimes field sensitive. This makes `lit.ref.struct.ger` automatically propagate a field sensitive lifetime into struct field accesses, and updates everything to work with them. This has no significant effect because CheckLifetimes currently ignores this information, but this handles all the plumbing. MODULAR_ORIG_COMMIT_REV_ID: 4ec7bffb95932accca8dfeccecd8b604d9a4fc73 --- stdlib/src/collections/counter.mojo | 12 ++++++++---- stdlib/src/collections/dict.mojo | 8 ++++---- stdlib/src/collections/set.mojo | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index f53255011a..594dbc1bce 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -121,7 +121,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ self._data[value] = count - fn __iter__(self: Self) -> _DictKeyIter[V, Int, __lifetime_of(self)]: + fn __iter__(self: Self) -> _DictKeyIter[V, Int, __lifetime_of(self._data)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -481,7 +481,9 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.pop(value, default) - fn keys(ref [_]self: Self) -> _DictKeyIter[V, Int, __lifetime_of(self)]: + fn keys( + ref [_]self: Self, + ) -> _DictKeyIter[V, Int, __lifetime_of(self._data)]: """Iterate over the Counter's keys as immutable references. Returns: @@ -489,7 +491,9 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.keys() - fn values(ref [_]self: Self) -> _DictValueIter[V, Int, __lifetime_of(self)]: + fn values( + ref [_]self: Self, + ) -> _DictValueIter[V, Int, __lifetime_of(self._data)]: """Iterate over the Counter's values as references. Returns: @@ -497,7 +501,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.values() - fn items(self: Self) -> _DictEntryIter[V, Int, __lifetime_of(self)]: + fn items(self: Self) -> _DictEntryIter[V, Int, __lifetime_of(self._data)]: """Iterate over the dict's entries as immutable references. Returns: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index dec820729e..f471c71046 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -1177,7 +1177,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn __iter__( ref [_]self: Self, - ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self)]: + ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self._dict)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1189,7 +1189,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn keys( ref [_]self: Self, - ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self)]: + ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self._dict)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1201,7 +1201,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn values( ref [_]self: Self, - ) -> _DictValueIter[Self.key_type, V, __lifetime_of(self)]: + ) -> _DictValueIter[Self.key_type, V, __lifetime_of(self._dict)]: """Iterate over the keyword dict's values as references. Returns: @@ -1213,7 +1213,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn items( ref [_]self: Self, - ) -> _DictEntryIter[Self.key_type, V, __lifetime_of(self)]: + ) -> _DictEntryIter[Self.key_type, V, __lifetime_of(self._dict)]: """Iterate over the keyword dictionary's entries as immutable references. These can't yet be unpacked like Python dict items, but you can diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 7a10537637..866382576e 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -355,7 +355,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): fn __iter__( ref [_]self: Self, - ) -> _DictKeyIter[T, NoneType, __lifetime_of(self)]: + ) -> _DictKeyIter[T, NoneType, __lifetime_of(self._data)]: """Iterate over elements of the set, returning immutable references. Returns: From 4a78a4b58237d6dccab1a31e33f5f062868fa625 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 29 Aug 2024 00:56:19 -0700 Subject: [PATCH 1462/2019] Initial version of v24.5 Mojo changelog MODULAR_ORIG_COMMIT_REV_ID: 140e0180dd920d6997c7e3726ae24253cab14c10 --- docs/changelog-released.md | 860 +++++++++++++++++++++++++++++++++++++ docs/changelog.md | 780 --------------------------------- 2 files changed, 860 insertions(+), 780 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 1eafdd18c6..52e4d4f88c 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -13,6 +13,76 @@ It doesn't include all internal implementation changes. If you don't have Mojo yet, see the [get started guide](/mojo/manual/get-started). +:::caution + +[Magic](/magic) is the preferred package manager and virtual environment manager +for MAX and Mojo projects. [conda](https://docs.conda.io/projects/conda/en/latest/index.html) +is supported as an alternative. + +The legacy [`modular`](/cli) CLI is now deprecated. +We will not release any new `max` or `mojo` packages through the `modular` +tool beyond the 24.5 release. You must now use [Magic](/magic/) or +[conda](/magic/conda) to install MAX and Mojo. + +::: + +### Update Mojo in a Magic virtual environment + +The virtual environment for each Magic project has its own package versions. +The Mojo programming language is distributed as part of the `max` package. + +To view the version of Mojo for a specific Magic project, run the following +command within your project path: + +```sh +magic run mojo --version +``` + +Use the [`magic update`](/magic/commands#magic-update) within your project path +to update the `max` package for that project to the latest release: + +```sh +magic update max +``` + +See the [Magic](/magic) documentation for more information on managing Magic +virtual environments. + +### Update Mojo in a conda virtual environment + +Each conda virtual environment has its own package versions. +The Mojo programming language is distributed as part of the `max` package. + +Use the [`conda list`](https://docs.conda.io/projects/conda/en/latest/commands/list.html) +command to list the version of the `max` package for an environment. +For example, for an environment named `max-project`, run: + +```sh +conda list -n max-project max +``` + +Use the [`conda update`](https://docs.conda.io/projects/conda/en/latest/commands/update.html) +command to update the version of the `max` package for an environment. +For example, for an environment named `max-project`, run: + +```sh +conda update -n max-project max +``` + +See the [conda](https://docs.conda.io/projects/conda/en/latest/index.html) +documentation for more information on managing conda virtual environments. + +### Update Mojo using the `modular` CLI + +:::caution + +The legacy [`modular`](/cli) CLI is now deprecated. +We will not release any new `max` or `mojo` packages through the `modular` +tool beyond the 24.5 release. You must now use [Magic](/magic/) or +[conda](/magic/conda) to install MAX and Mojo. + +::: + To see your Mojo version, run this: ```sh @@ -25,6 +95,796 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## v24.5 (2024-09-10) + +### ⭐️ New + +- Mojo now diagnoses "argument exclusivity" violations due to aliasing + references. Mojo requires references (including implicit references due to + borrowed/inout arguments) to be uniquely referenced (non-aliased) if mutable. + This is important for code safety, because it allows the compiler (and readers + of code) to understand where and when a value is mutated. It is also useful + for performance optimization because it allows the compiler to know that + accesses through immutable references cannot change behind the scenes. Here is + an invalid example: + + ```mojo + fn take_two_strings(a: String, inout b: String): + # Mojo knows 'a' and 'b' cannot be the same string. + b += a + + fn invalid_access(): + var my_string = String() + + # error: passing `my_string` inout is invalid since it is also passed + # borrowed. + take_two_strings(my_string, my_string) + ``` + + This is similar to [Swift exclusivity + checking](https://swift.org/blog/swift-5-exclusivity/) and the [Rust + language](https://doc.rust-lang.org/beta/book/ch04-02-references-and-borrowing.html) + sometimes known as "aliasing xor mutability". That said, the Mojo + implementation details are somewhat different because lifetimes are embedded + in types. + +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed and + still denotes the declaration of a new variable with a scope (in both `def` + and `fn`). Relaxing this makes `fn` and `def` more similar, but they still + differ in other important ways. + +- Mojo now supports named result bindings. Named result bindings are useful for + directly emplacing function results into the output slot of a function. This + feature provides more flexibility and guarantees around emplacing the result + of a function compared to "guaranteed" NRVO. If a `@register_passable` result + is bound to a name, the result value is made accessible as a mutable + reference. + + ```mojo + fn efficiently_return_string(b: Bool) -> String as output: + if b: + output = "emplaced!" + mutate(output) + return + return "regular return" + ``` + + If we used a temporary for `output` instead, we would need to move into the + result slot, which wouldn't work if the result type was non-movable. + + In a function with a named result, `return` may be used with no operand to + signal an exit from the function, or it can be used normally to specify the + return value of the function. The compiler will error if the result is not + initialized on all normal exit paths from the function. + +- `String` class now have `rjust`, `ljust` and `center` methods to return + a justified string based on width and fillchar. + ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by + [@mzaks](https://github.com/mzaks)) + +- Creating nested `PythonObject` from a list or tuple of python objects is + possible now: + + ```mojo + var np = Python.import_module("numpy") + var a = np.array([1, 2, 3]) + var b = np.array([4, 5, 6]) + var arrays = PythonObject([a, b]) + assert_equal(len(arrays), 2) + ``` + + Also allowing more convenient call syntax: + + ```mojo + var stacked = np.hstack((a, b)) + assert_equal(str(stacked), "[1 2 3 4 5 6]") + ``` + + ([PR 3264#](https://github.com/modularml/mojo/pull/3264) by + [@kszucs](https://github.com/kszucs)) + +- `List[T]` values are now equality comparable with `==` and `!=` when `T` is + equality comparable. + ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by + [@kszucs](https://github.com/kszucs)) + +- `__setitem__` now works with variadic argument lists such as: + + ```mojo + struct YourType: + fn __setitem__(inout self, *indices: Int, val: Int): ... + ``` + + The Mojo compiler now always passes the "new value" being set using the last + keyword argument of the `__setitem__`, e.g. turning `yourType[1, 2] = 3` into + `yourType.__setitem__(1, 2, val=3)`. This fixes + [Issue #248](https://github.com/modularml/mojo/issues/248). + +- `Optional` values are now equality comparable with `==` and `!=` when their + element type is equality comparable. + +- Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) + dictionary-like type, matching most of the features of the Python one. + ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by + [@msaelices](https://github.com/msaelices)) + +- Mojo context managers used in regions of code that may raise no longer need to + define a "conditional" exit function in the form of + `fn __exit__(self, e: Error) -> Bool`. This function allows the context + manager to conditionally intercept and handle the error and allow the function + to continue executing. This is useful for some applications, but in many cases + the conditional exit would delegate to the unconditional exit function + `fn __exit__(self)`. + + Concretely, this enables defining `with` regions that unconditionally + propagate inner errors, allowing code like: + + ```mojo + def might_raise() -> Int: + ... + + def foo() -> Int: + with ContextMgr(): + return might_raise() + # no longer complains about missing return + + def bar(): + var x: Int + with ContextMgr(): + x = might_raise() + print(x) # no longer complains about 'x' being uninitialized + ``` + +- Now supports "conditional conformances" where some methods on a struct have + additional trait requirements that the struct itself doesn't. This is + expressed through an explicitly declared `self` type: + + ```mojo + struct GenericThing[Type: AnyType]: # Works with anything + # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' + fn normal_method(self): ... + + # Just redeclare the requirements with more specific types: + fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): + var tmp = val^ # Ok to move 'val' since it is Movable + ... + fn usage_example(): + var a = GenericThing[Int]() + a.normal_method() # Ok, Int conforms to AnyType + a.needs_move(42) # Ok, Int is movable + + var b = GenericThing[NonMovable]() + b.normal_method() # Ok, NonMovable conforms to AnyType + + # error: argument type 'NonMovable' does not conform to trait 'Movable' + b.needs_move(NonMovable()) + ``` + + Conditional conformance works with dunder methods and other things as well. + +- `async` functions now support memory-only results (like `String`, `List`, + etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have + been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means + the result types of `async` functions do not need to be `Movable`. + + ```mojo + async fn raise_or_string(c: Bool) raises -> String: + if c: + raise "whoops!" + return "hello world!" + ``` + + Note that `async` functions do not yet support indirect calls, `ref` results, + and constructors. + +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: + + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass + + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. + ``` + +- The `Reference` type (and many iterators) now use + [infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) to + represent the mutability of their lifetime, simplifying the interface. + +- `Dict` now implements `setdefault`, to get a value from the dictionary by + key, or set it to a default if it doesn't exist + ([PR #2803](https://github.com/modularml/mojo/pull/2803) + by [@msaelices](https://github.com/msaelices)) + +- Added new `ExplicitlyCopyable` trait, to mark types that can be copied + explicitly, but which might not be implicitly copyable. + + This supports work to transition the standard library collection types away + from implicit copyability, which can lead to unintended expensive copies. + +- Added `Identifiable` trait, used to describe types that implement the `__is__` + and `__isnot__` trait methods. + ([PR #2807](https://github.com/modularml/mojo/pull/2807)) + + - Also added new `assert_is()` and `assert_is_not()` test utilities to the + `testing` module. + +- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. + ([PR #2701](https://github.com/modularml/mojo/pull/2701) + by [@jayzhan211](https://github.com/jayzhan211)) + +- Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that + returns an `UnsafePointer[C_char]` for convenient interoperability with C + APIs. + +- Added `C_char` type alias in `sys.ffi`. + +- Added `StringSlice(..)` initializer from a `StringLiteral`. + +- Added a `byte_length()` method to `String`, `StringSlice`, and `StringLiteral` +and deprecated their private `_byte_length()` methods. Added a warning to +`String.__len__` method that it will return length in Unicode codepoints in the +future and `StringSlice.__len__` now does return the Unicode codepoints length. +([PR #2960](https://github.com/modularml/mojo/pull/2960) by [@martinvuyk](https://github.com/martinvuyk)) + +- Added new `StaticString` type alias. This can be used in place of + `StringLiteral` for runtime string arguments. + +- Added `TemporaryDirectory` in module `tempfile`. + ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `NamedTemporaryFile` in module `tempfile`. + ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) + +- Added `oct(..)` function for formatting an integer in octal. + ([PR #2914](https://github.com/modularml/mojo/pull/2914) by [@bgreni](https://github.com/bgreni)) + +- Added `String.format` method. + ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) + + Support automatic and manual indexing of `*args`. + + Examples: + + ```mojo + print( + String("{1} Welcome to {0} {1}").format("mojo", "🔥") + ) + # 🔥 Wecome to mojo 🔥 + ``` + + ```mojo + print(String("{} {} {}").format(True, 1.125, 2)) + #True 1.125 2 + ``` + +- Added the builtin `input` function, which behaves the same as Python. + ([PR #3392](https://github.com/modularml/mojo/pull/3392) by [@thatstoasty](https://github.com/thatstoasty)) + + ```mojo + name = input("Enter your name: ") + print("Hello, " + name + "!") + ``` + + If the user enters "Mojo" it returns "Hello Mojo!" + +- Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo + to a specific version: + + ```sh + export MOJO_PYTHON="/usr/bin/python3.11" + ``` + + Or a virtual environment to always have access to those Python modules: + + ```sh + export MOJO_PYTHON="~/venv/bin/python" + ``` + + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + but no Python executable. + +- The `math` package now includes the `pi`, `e`, and `tau` constants (Closes + Issue [#2135](https://github.com/modularml/mojo/issues/2135)). + +- Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a + paltform-dependent width. `UInt` implements most arithmetic operations that + make sense for integers, with the notable exception of `__neg__`. Builtin + functions such as `min`/`max`, as well as `math` functions like `ceildiv`, + `align_down`, and `align_up` are also implemented for `UInt`. + +- `os.path.expanduser()` and `pathlib.Path.exapanduser()` have been added to + allow expanding a prefixed `~` in a `String` or `Path` with the users home + path: + + ```mojo + import os + print(os.path.expanduser("~/.modular")) + # /Users/username/.modular + print(os.path.expanduser("~root/folder")) + # /var/root/folder (on macos) + # /root/folder (on linux) + ``` + +- `Path.home()` has been added to return a path of the users home directory. + +- `os.path.split()` has been added for splitting a path into `head, tail`: + + ```mojo + import os + head, tail = os.path.split("/this/is/head/tail") + print("head:", head) + print("tail:", tail) + # head: /this/is/head + # tail: tail + ``` + +- `os.path.makedirs()` and `os.path.removedirs()` have been added for creating + and removing nested directories: + + ```mojo + import os + path = os.path.join("dir1", "dir2", "dir3") + os.path.makedirs(path, exist_ok=True) + os.path.removedirs(path) + ``` + +- The `pwd` module has been added for accessing user information in + `/etc/passwd` on POSIX systems. This follows the same logic as Python: + + ```mojo + import pwd + import os + current_user = pwd.getpwuid(os.getuid()) + print(current_user) + + # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, + # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', + # pw_shell='/bin/zsh') + + print(current_user.pw_uid) + + # 501 + + root = pwd.getpwnam("root") + print(root) + + # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, + # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') + ``` + +- Added `Dict.__init__` overload to specify initial capacity. + ([PR #3171](https://github.com/modularml/mojo/pull/3171) by [@rd4com](https://github.com/rd4com)) + + The capacity has to be a power of two and above or equal 8. + + It allows for faster initialization by skipping incremental growth steps. + + Example: + + ```mojo + var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) + # Insert (2/3 of 1024) entries + ``` + +- `ListLiteral` now supports `__contains__`. + ([PR #3251](https://github.com/modularml/mojo/pull/3251) by + [@jjvraw](https://github.com/jjvraw)) + +- `bit` module now supports `bit_reverse()`, `byte_swap()` and `pop_count()` for + `Int` type. + ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) + +- `String.format()` now supports conversion flags `!s` and `!r`, allowing for + `str()` and `repr()` conversions within format strings. + ([PR #3279](https://github.com/modularml/mojo/pull/3279) by [@jjvraw](https://github.com/jjvraw)) + + Example: + + ```mojo + String("{} {!r}").format("Mojo", "Mojo") + # "Mojo 'Mojo'" + + String("{0!s} {0!r}").format("Mojo") + # "Mojo 'Mojo'" + ``` + +- `sort` now supports `stable` parameter. It can be called by + + ```mojo + sort[cmp_fn, stable=True](list) + ``` + + The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to + allocate, the program will crash. + +- The `mojo test` command now accepts a `--filter` option that will narrow the + set of tests collected and executed. The filter string is a POSIX extended + regular expression. + +- The `mojo test` command now supports using the same compilation options as + `mojo build`. + +- You can now debug unit tests using `mojo test` by passing the `--debug` flag. + Most debug flags are supported; run `mojo test --help` for a full listing. + + Debugging doctests is not currently supported. + +- `UnsafePointer` now has an `alignment` parameter to specify the static + alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes + in an alignment parameter, and the alignment should be specified in the type. + + ```mojo + UnsafePointer[type].alloc[alignment](x) # now becomes + UnsafePointer[type, alignment].alloc(x) + ``` + +- The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo features, + including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and + more. + +- The Mojo debugger now hides the artificial function arguments `__result__` and + `__error__` created by the compiler for Mojo code. + +- The Mojo debugger now supports a `break-on-raise` command that indicated the + debugger to stop at any `raise` statements. A similar features has been added + to the debugger on VS Code. + +- A proxy has been added to the Mojo Language Server on VS Code that handles + crashes more gracefully. + +### 🦋 Changed + +- The set of automatically imported entities (types, aliases, functions) into user's + Mojo programs has been dramatically reduced. Before, with the way the `builtin` + module was handled, all of the entities in the following modules would be automatically + included: + + {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', + 'builtin', 'collections'} + + Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are + the ones automatically imported into user's Mojo programs. This will break + a lot of user code as users will need to explicitly import what they're using + for cases previously commonly included before (such as `Optional`, `Variant`, + and so on). + +- Some types from the `builtin` module have been moved to different modules for clarity + which is made possible now that we have a `prelude` module that can re-export symbols + from modules other than `builtin`. + - `builtin.string` has been moved to `collections.string`. + +- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a + C-like set of semantics around pointer aliasing and derivation. However, the C + semantics bring a lot of history and baggage that are not needed in Mojo and + which complicate compiler optimizations. The language overall provides a + stronger set of invariants around pointer aliasing with lifetimes and + exclusive mutable references to values, etc. + + It is now forbidden to convert a non-pointer-typed value derived from a + Mojo-allocated pointer, such as an integer address, to a pointer-typed value. + "Derived" means there is overlap in the bits of the non-pointer-typed value + with the original pointer value. Accordingly, the `UnsafePointer` constructor + that took an `address` keyword argument has been removed. + + It is still possible to make this conversion in certain cases where it is + absolutely necessary, such as interoperating with other languages like Python. + In this case, the compiler makes two assumptions: any pointer derived from a + non-pointer-typed value does not alias any Mojo-derived pointer and that any + external function calls have arbitrary memory effects. + +- `DTypePointer` , `LegacyPointer` and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) instead. For more + information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in + the Mojo Manual. + + Functions that previously took a `DTypePointer` now take an + equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to + `UnsafePointer` is: + + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] + ``` + + There could be places that you have code of the form: + + ```mojo + fn f(ptr: DTypePointer): + ``` + + which is equivalent to `DTypePointer[*_]`. In this case you would have to add + an infer-only `type` parameter to the function: + + ```mojo + fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): + ``` + + because we can’t have an unbound parameter inside the struct. + + There could also be places where you use + `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to + change these to `UnsafePointer[NoneType/Int]`. But since these are not an + `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to + appropriate types. + +- The `DTypePointer` `load()`, `store()`, and `prefetch()` methods have been + moved to `SIMD` and now take an + `UnsafePointer` as an argument. Instead of using `ptr.load[width=4](offset)` + one should use `SIMD[size=4].load(ptr, offset)`. Note the default load width + before was 1, but the default size of `SIMD` is the size of the SIMD type. The + default store size is the size of the `SIMD` value to be stored. + +- `UnsafePointer` now supports `simd_strided_load()`, `simd_strided_store()`, + `gather()`, and `scatter()` when the underlying type is `Scalar[DType]`. + +- The global functions for working with `UnsafePointer` have transitioned to + being methods through the use of conditional conformances: + + - `destroy_pointee(p)` => `p.destroy_pointee()` + - `move_from_pointee(p)` => `p.take_pointee()` + - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` + - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` + - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` + +- The `UnsafePointer.offset()` method has been removed. Use + [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. + + ```mojo + new_ptr = ptr.offset(1) + ``` + + Becomes: + + ```mojo + new_ptr = ptr + 1 + ``` + +- `UnsafePointer` has a new + `exclusive: Bool = False` parameter. Setting this parameter to true tells the + compiler that the user knows this pointer and all those derived from it have + exclusive access to the underlying memory allocation. The compiler is not + guaranteed to do anything with this information. + +- It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + `UnsafePointer.address_of(someRef[])` which makes the code explicit that the + `UnsafePointer` gets the address of what the reference points to. + +- `sort` no longer takes `LegacyPointer`. The current API supports: + - `sort(list)` just plain list + - `sort[type, cmp_fn](list)` list with custom compare function + - `sort(ptr, len)` a pointer and length (can change to Span in future) + - `sort[type, cmp_fn](ptr, len)` above with custom compare + +- Continued transition to `UnsafePointer` and unsigned byte type for strings: + + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `UnsafePointer[Int8]`) + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `UnsafePointer[Int8]`) + +- `await` on a coroutine now consumes it. This strengthens the invariant that + coroutines can only be awaited once. + +- `print()` now requires that its arguments conform to the `Formattable` trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. + + Previously, `print()` required types conform to `Stringable`. This meant that + to execute a call like `print(a, b, c)`, at least three separate String heap + allocations were down, to hold the formatted values of `a`, `b`, and `c` + respectively. The total number of allocations could be much higher if, for + example, `a.__str__()` was implemented to concatenate together the fields of + `a`, like in the following example: + + ```mojo + struct Point(Stringable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + # Performs 3 allocations: 1 each for str(..) of each of the fields, + # and then the final returned `String` allocation. + return "(" + str(self.x) + ", " + str(self.y) + ")" + ``` + + A type like the one above can transition to additionally implementing + `Formattable` with the following changes: + + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` + + In the example above, `String.format_sequence()` is used to construct a + `String` from a type that implements `Formattable`. This pattern of + implementing a type's `Stringable` implementation in terms of its `Formattable` + implementation minimizes boilerplate and duplicated code, while retaining + backwards compatibility with the requirements of the commonly used `str(..)` + function. + + + + > [!WARNING] + > The error shown when passing a type that does not implement `Formattable` to + > `print()` is currently not entirely descriptive of the underlying cause: + > + > ```shell + > error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified + > print(point) + > ~~~~~^~~~~~~ + > ``` + > + > If the above error is seen, ensure that all argument types implement + > `Formattable`. + +- `debug_assert()` now also requires that its `message` argument conform to + `Formattable`. + +- The `StringRef` constructors from `DTypePointer.int8` have been changed to + take a `UnsafePointer[C_char]`, reflecting their use for compatibility with + C APIs. + +- `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements + a constructor which accepts optional values. `Slice._has_end()` has also been + removed since a Slice with no end is now represented by an empty `Slice.end` + option. + ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) + + ```mojo + var s = Slice(1, None, 2) + print(s.start.value()) # must retrieve the value from the optional + ``` + +- `NoneType` is now a normal standard library type, and not an alias for a raw + MLIR type. + + Function signatures spelled as `fn(...) -> NoneType` should transition to + being written as `fn(...) -> None`. + +- Accessing local Python modules with `Python.add_to_path(".")` is no longer + required, it now behaves the same as Python, you can access modules in the + same folder as the target file: + + - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` + - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` + +- The rank argument for `algorihtm.elementwise` is no longer required and is + only inferred. + +- The `ulp` function in `numerics` has been moved to the `math` module. + +- The Mojo Language Server no longer sets `.` as a commit character for + auto-completion. + +- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer + implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced + for types where this behavior is desired. + +- The `time.now()` function has been deprecated. Please use `time.perf_counter` + or `time.perf_counter_ns` instead. + +- A few bit functions have been renamed for clarity: +- `countl_zero` -> `count_leading_zeros` +- `countr_zero` -> `count_trailing_zeros` + +- Now that we have a `UInt` type, use this to represent the return type of hash. + In general, hashes should be an unsigned integer, and can also lead to improved + performance in certain cases. + +- The `atol` function now correctly supports leading underscores, + (e.g.`atol("0x_ff", 0)`), when the appropriate base is specified or inferred + (base 0). non-base-10 integer literals as per Python's [Integer Literals](\ + ). + ([PR #3180](https://github.com/modularml/mojo/pull/3180) + by [@jjvraw](https://github.com/jjvraw)) + +- `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. + +- `SIMD.load/store` are moved to `UnsafePointer`. + +- `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` + are removed from prelude. + +- The `simd_strided_load()` and `simd_strided_store()` have been renamed to + `strided_load` and `strided_store` in `UnsafePointer`. + +- `mojo test` now uses the Mojo compiler for running unit tests. This will resolve + compilation issues that sometimes appeared, and will also improve overall test + times, since we will only compile unit tests once before executing all of them. + + These changes do not apply to doctests, due to their different semantics. + +- The `mojo debug --rpc` command has been renamed to `mojo debug --vscode`, + which is now able to manage multiple VS Code windows. + +### ❌ Removed + +- Support for the legacy `fn __init__(...) -> Self:` form has been removed from + the compiler, please switch to using `fn __init__(inout self, ...):` instead. + +- Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the + same thing. + +- Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. + +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD + instead. + +- The builtin `tensor` module has been removed. Identical functionality is + available in `max.tensor`, but it is generally recommended to use `buffer` + when possible instead. + +- Removed the Mojo Language Server warnings for unused function arguments. + +- Removed the `SIMD.{add,mul,sub}_with_overflow` methods. + +- Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is + available using the builting `min` and `max` functions. + +- `Run Mojo File in Dedicated Terminal` action has been removed, and the + action `Run Mojo File` will always open a dedicated terminal for each mojo + file to guarantee a correct environment. + +### 🛠️ Fixed + +- Fixed a crash in the Mojo Language Server when importing the current file. + +- Fixed crash when specifying variadic keyword arguments without a type + expression in `def` functions, e.g.: + + ```mojo + def foo(**kwargs): ... # now works + ``` + +- Mojo now prints `ref` arguments and results in generated documentation + correctly. + +- [#1734](https://github.com/modularml/mojo/issues/1734) - Calling + `__copyinit__` on self causes crash. + +- [#3142](https://github.com/modularml/mojo/issues/3142) - [QoI] Confusing + `__setitem__` method is failing with a "must be mutable" error. + +- [#248](https://github.com/modularml/mojo/issues/248) - [Feature] Enable + `__setitem__` to take variadic arguments + +- [#3065](https://github.com/modularml/mojo/issues/3065) - Fix incorrect behavior + of `SIMD.__int__` on unsigned types + +- [#3045](https://github.com/modularml/mojo/issues/3045) - Disable implicit SIMD + conversion routes through `Bool` + +- [#3126](https://github.com/modularml/mojo/issues/3126) - [BUG] List doesn't + work at compile time. + +- [#3237](https://github.com/modularml/mojo/issues/3237) - [BUG] Difference + between `__getitem__` and `[.]` operator. + +- [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated + references to `let` in REPL documentation. + +- The VS Code extension doesn't cache anymore the information of the selected + MAX SDK, which was causing issues upon changes in the SDK. + +- The Mojo debugger now stops showing spurious warnings when parsing closures. + ## v24.4 (2024-06-07) ### ✨ Highlights diff --git a/docs/changelog.md b/docs/changelog.md index a68bf6f71d..e0d605860b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,788 +16,8 @@ what we publish. ### ⭐️ New -- Mojo now diagnoses "argument exclusivity" violations due to aliasing - references. Mojo requires references (including implicit references due to - borrowed/inout arguments) to be uniquely referenced (non-aliased) if mutable. - This is important for code safety, because it allows the compiler (and readers - of code) to understand where and when a value is mutated. It is also useful - for performance optimization because it allows the compiler to know that - accesses through immutable references cannot change behind the scenes. Here is - an invalid example: - - ```mojo - fn take_two_strings(a: String, inout b: String): - # Mojo knows 'a' and 'b' cannot be the same string. - b += a - - fn invalid_access(): - var my_string = String() - - # error: passing `my_string` inout is invalid since it is also passed - # borrowed. - take_two_strings(my_string, my_string) - ``` - - This is similar to [Swift exclusivity - checking](https://swift.org/blog/swift-5-exclusivity/) and the [Rust - language](https://doc.rust-lang.org/beta/book/ch04-02-references-and-borrowing.html) - sometimes known as "aliasing xor mutability". That said, the Mojo - implementation details are somewhat different because lifetimes are embedded - in types. - -- Mojo now allows implicit definitions of variables within a `fn` in the same - way that has been allowed in a `def`. The `var` keyword is still allowed and - still denotes the declaration of a new variable with a scope (in both `def` - and `fn`). Relaxing this makes `fn` and `def` more similar, but they still - differ in other important ways. - -- Mojo now supports named result bindings. Named result bindings are useful for - directly emplacing function results into the output slot of a function. This - feature provides more flexibility and guarantees around emplacing the result - of a function compared to "guaranteed" NRVO. If a `@register_passable` result - is bound to a name, the result value is made accessible as a mutable - reference. - - ```mojo - fn efficiently_return_string(b: Bool) -> String as output: - if b: - output = "emplaced!" - mutate(output) - return - return "regular return" - ``` - - If we used a temporary for `output` instead, we would need to move into the - result slot, which wouldn't work if the result type was non-movable. - - In a function with a named result, `return` may be used with no operand to - signal an exit from the function, or it can be used normally to specify the - return value of the function. The compiler will error if the result is not - initialized on all normal exit paths from the function. - -- `String` class now have `rjust`, `ljust` and `center` methods to return - a justified string based on width and fillchar. - ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by - [@mzaks](https://github.com/mzaks)) - -- Creating nested `PythonObject` from a list or tuple of python objects is - possible now: - - ```mojo - var np = Python.import_module("numpy") - var a = np.array([1, 2, 3]) - var b = np.array([4, 5, 6]) - var arrays = PythonObject([a, b]) - assert_equal(len(arrays), 2) - ``` - - Also allowing more convenient call syntax: - - ```mojo - var stacked = np.hstack((a, b)) - assert_equal(str(stacked), "[1 2 3 4 5 6]") - ``` - - ([PR 3264#](https://github.com/modularml/mojo/pull/3264) by - [@kszucs](https://github.com/kszucs)) - -- `List[T]` values are now equality comparable with `==` and `!=` when `T` is - equality comparable. - ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by - [@kszucs](https://github.com/kszucs)) - -- `__setitem__` now works with variadic argument lists such as: - - ```mojo - struct YourType: - fn __setitem__(inout self, *indices: Int, val: Int): ... - ``` - - The Mojo compiler now always passes the "new value" being set using the last - keyword argument of the `__setitem__`, e.g. turning `yourType[1, 2] = 3` into - `yourType.__setitem__(1, 2, val=3)`. This fixes - [Issue #248](https://github.com/modularml/mojo/issues/248). - -- `Optional` values are now equality comparable with `==` and `!=` when their - element type is equality comparable. - -- Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) - dictionary-like type, matching most of the features of the Python one. - ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by - [@msaelices](https://github.com/msaelices)) - -- Mojo context managers used in regions of code that may raise no longer need to - define a "conditional" exit function in the form of - `fn __exit__(self, e: Error) -> Bool`. This function allows the context - manager to conditionally intercept and handle the error and allow the function - to continue executing. This is useful for some applications, but in many cases - the conditional exit would delegate to the unconditional exit function - `fn __exit__(self)`. - - Concretely, this enables defining `with` regions that unconditionally - propagate inner errors, allowing code like: - - ```mojo - def might_raise() -> Int: - ... - - def foo() -> Int: - with ContextMgr(): - return might_raise() - # no longer complains about missing return - - def bar(): - var x: Int - with ContextMgr(): - x = might_raise() - print(x) # no longer complains about 'x' being uninitialized - ``` - -- Now supports "conditional conformances" where some methods on a struct have - additional trait requirements that the struct itself doesn't. This is - expressed through an explicitly declared `self` type: - - ```mojo - struct GenericThing[Type: AnyType]: # Works with anything - # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' - fn normal_method(self): ... - - # Just redeclare the requirements with more specific types: - fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): - var tmp = val^ # Ok to move 'val' since it is Movable - ... - fn usage_example(): - var a = GenericThing[Int]() - a.normal_method() # Ok, Int conforms to AnyType - a.needs_move(42) # Ok, Int is movable - - var b = GenericThing[NonMovable]() - b.normal_method() # Ok, NonMovable conforms to AnyType - - # error: argument type 'NonMovable' does not conform to trait 'Movable' - b.needs_move(NonMovable()) - ``` - - Conditional conformance works with dunder methods and other things as well. - -- `async` functions now support memory-only results (like `String`, `List`, - etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have - been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means - the result types of `async` functions do not need to be `Movable`. - - ```mojo - async fn raise_or_string(c: Bool) raises -> String: - if c: - raise "whoops!" - return "hello world!" - ``` - - Note that `async` functions do not yet support indirect calls, `ref` results, - and constructors. - -- As a specific form of "conditional conformances", initializers in a struct - may indicate specific parameter bindings to use in the type of their `self` - argument. For example: - - ```mojo - @value - struct MyStruct[size: Int]: - fn __init__(inout self: MyStruct[0]): pass - fn __init__(inout self: MyStruct[1], a: Int): pass - fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass - - def test(x: Int): - a = MyStruct() # Infers size=0 from 'self' type. - b = MyStruct(x) # Infers size=1 from 'self' type. - c = MyStruct(x, x) # Infers size=2 from 'self' type. - ``` - -- The `Reference` type (and many iterators) now use - [infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) to - represent the mutability of their lifetime, simplifying the interface. - -- `Dict` now implements `setdefault`, to get a value from the dictionary by - key, or set it to a default if it doesn't exist - ([PR #2803](https://github.com/modularml/mojo/pull/2803) - by [@msaelices](https://github.com/msaelices)) - -- Added new `ExplicitlyCopyable` trait, to mark types that can be copied - explicitly, but which might not be implicitly copyable. - - This supports work to transition the standard library collection types away - from implicit copyability, which can lead to unintended expensive copies. - -- Added `Identifiable` trait, used to describe types that implement the `__is__` - and `__isnot__` trait methods. - ([PR #2807](https://github.com/modularml/mojo/pull/2807)) - - - Also added new `assert_is()` and `assert_is_not()` test utilities to the - `testing` module. - -- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. - ([PR #2701](https://github.com/modularml/mojo/pull/2701) - by [@jayzhan211](https://github.com/jayzhan211)) - -- Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that - returns an `UnsafePointer[C_char]` for convenient interoperability with C - APIs. - -- Added `C_char` type alias in `sys.ffi`. - -- Added `StringSlice(..)` initializer from a `StringLiteral`. - -- Added a `byte_length()` method to `String`, `StringSlice`, and `StringLiteral` -and deprecated their private `_byte_length()` methods. Added a warning to -`String.__len__` method that it will return length in Unicode codepoints in the -future and `StringSlice.__len__` now does return the Unicode codepoints length. -([PR #2960](https://github.com/modularml/mojo/pull/2960) by [@martinvuyk](https://github.com/martinvuyk)) - -- Added new `StaticString` type alias. This can be used in place of - `StringLiteral` for runtime string arguments. - -- Added `TemporaryDirectory` in module `tempfile`. - ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `NamedTemporaryFile` in module `tempfile`. - ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) - -- Added `oct(..)` function for formatting an integer in octal. - ([PR #2914](https://github.com/modularml/mojo/pull/2914) by [@bgreni](https://github.com/bgreni)) - -- Added `String.format` method. - ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) - - Support automatic and manual indexing of `*args`. - - Examples: - - ```mojo - print( - String("{1} Welcome to {0} {1}").format("mojo", "🔥") - ) - # 🔥 Wecome to mojo 🔥 - ``` - - ```mojo - print(String("{} {} {}").format(True, 1.125, 2)) - #True 1.125 2 - ``` - -- Added the builtin `input` function, which behaves the same as Python. - ([PR #3392](https://github.com/modularml/mojo/pull/3392) by [@thatstoasty](https://github.com/thatstoasty)) - - ```mojo - name = input("Enter your name: ") - print("Hello, " + name + "!") - ``` - - If the user enters "Mojo" it returns "Hello Mojo!" - -- Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo - to a specific version: - - ```sh - export MOJO_PYTHON="/usr/bin/python3.11" - ``` - - Or a virtual environment to always have access to those Python modules: - - ```sh - export MOJO_PYTHON="~/venv/bin/python" - ``` - - `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, - but no Python executable. - -- The `math` package now includes the `pi`, `e`, and `tau` constants (Closes - Issue [#2135](https://github.com/modularml/mojo/issues/2135)). - -- Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a - paltform-dependent width. `UInt` implements most arithmetic operations that - make sense for integers, with the notable exception of `__neg__`. Builtin - functions such as `min`/`max`, as well as `math` functions like `ceildiv`, - `align_down`, and `align_up` are also implemented for `UInt`. - -- `os.path.expanduser()` and `pathlib.Path.exapanduser()` have been added to - allow expanding a prefixed `~` in a `String` or `Path` with the users home - path: - - ```mojo - import os - print(os.path.expanduser("~/.modular")) - # /Users/username/.modular - print(os.path.expanduser("~root/folder")) - # /var/root/folder (on macos) - # /root/folder (on linux) - ``` - -- `Path.home()` has been added to return a path of the users home directory. - -- `os.path.split()` has been added for splitting a path into `head, tail`: - - ```mojo - import os - head, tail = os.path.split("/this/is/head/tail") - print("head:", head) - print("tail:", tail) - # head: /this/is/head - # tail: tail - ``` - -- `os.path.makedirs()` and `os.path.removedirs()` have been added for creating - and removing nested directories: - - ```mojo - import os - path = os.path.join("dir1", "dir2", "dir3") - os.path.makedirs(path, exist_ok=True) - os.path.removedirs(path) - ``` - -- The `pwd` module has been added for accessing user information in - `/etc/passwd` on POSIX systems. This follows the same logic as Python: - - ```mojo - import pwd - import os - current_user = pwd.getpwuid(os.getuid()) - print(current_user) - - # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, - # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', - # pw_shell='/bin/zsh') - - print(current_user.pw_uid) - - # 501 - - root = pwd.getpwnam("root") - print(root) - - # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, - # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') - ``` - -- Added `Dict.__init__` overload to specify initial capacity. - ([PR #3171](https://github.com/modularml/mojo/pull/3171) by [@rd4com](https://github.com/rd4com)) - - The capacity has to be a power of two and above or equal 8. - - It allows for faster initialization by skipping incremental growth steps. - - Example: - - ```mojo - var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) - # Insert (2/3 of 1024) entries - ``` - -- `ListLiteral` now supports `__contains__`. - ([PR #3251](https://github.com/modularml/mojo/pull/3251) by - [@jjvraw](https://github.com/jjvraw)) - -- `bit` module now supports `bit_reverse()`, `byte_swap()` and `pop_count()` for - `Int` type. - ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) - -- `String.format()` now supports conversion flags `!s` and `!r`, allowing for - `str()` and `repr()` conversions within format strings. - ([PR #3279](https://github.com/modularml/mojo/pull/3279) by [@jjvraw](https://github.com/jjvraw)) - - Example: - - ```mojo - String("{} {!r}").format("Mojo", "Mojo") - # "Mojo 'Mojo'" - - String("{0!s} {0!r}").format("Mojo") - # "Mojo 'Mojo'" - ``` - -- `sort` now supports `stable` parameter. It can be called by - - ```mojo - sort[cmp_fn, stable=True](list) - ``` - - The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to - allocate, the program will crash. - -- The `mojo test` command now accepts a `--filter` option that will narrow the - set of tests collected and executed. The filter string is a POSIX extended - regular expression. - -- The `mojo test` command now supports using the same compilation options as - `mojo build`. - -- You can now debug unit tests using `mojo test` by passing the `--debug` flag. - Most debug flags are supported; run `mojo test --help` for a full listing. - - Debugging doctests is not currently supported. - -- `UnsafePointer` now has an `alignment` parameter to specify the static - alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes - in an alignment parameter, and the alignment should be specified in the type. - - ```mojo - UnsafePointer[type].alloc[alignment](x) # now becomes - UnsafePointer[type, alignment].alloc(x) - ``` - -- The VS Code extension now supports a vendored MAX SDK for VS Code, which is - automatically downloaded by the extension and it's used for all Mojo features, - including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and - more. - -- The Mojo debugger now hides the artificial function arguments `__result__` and - `__error__` created by the compiler for Mojo code. - -- The Mojo debugger now supports a `break-on-raise` command that indicated the - debugger to stop at any `raise` statements. A similar features has been added - to the debugger on VS Code. - -- A proxy has been added to the Mojo Language Server on VS Code that handles - crashes more gracefully. - ### 🦋 Changed -- The set of automatically imported entities (types, aliases, functions) into user's - Mojo programs has been dramatically reduced. Before, with the way the `builtin` - module was handled, all of the entities in the following modules would be automatically - included: - - {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', - 'builtin', 'collections'} - - Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are - the ones automatically imported into user's Mojo programs. This will break - a lot of user code as users will need to explicitly import what they're using - for cases previously commonly included before (such as `Optional`, `Variant`, - and so on). - -- Some types from the `builtin` module have been moved to different modules for clarity - which is made possible now that we have a `prelude` module that can re-export symbols - from modules other than `builtin`. - - `builtin.string` has been moved to `collections.string`. - -- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a - C-like set of semantics around pointer aliasing and derivation. However, the C - semantics bring a lot of history and baggage that are not needed in Mojo and - which complicate compiler optimizations. The language overall provides a - stronger set of invariants around pointer aliasing with lifetimes and - exclusive mutable references to values, etc. - - It is now forbidden to convert a non-pointer-typed value derived from a - Mojo-allocated pointer, such as an integer address, to a pointer-typed value. - "Derived" means there is overlap in the bits of the non-pointer-typed value - with the original pointer value. Accordingly, the `UnsafePointer` constructor - that took an `address` keyword argument has been removed. - - It is still possible to make this conversion in certain cases where it is - absolutely necessary, such as interoperating with other languages like Python. - In this case, the compiler makes two assumptions: any pointer derived from a - non-pointer-typed value does not alias any Mojo-derived pointer and that any - external function calls have arbitrary memory effects. - -- `DTypePointer` , `LegacyPointer` and `Pointer` have been removed. Use - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) instead. For more - information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in - the Mojo Manual. - - Functions that previously took a `DTypePointer` now take an - equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to - `UnsafePointer` is: - - ```mojo - DTypePointer[type] -> UnsafePointer[Scalar[type]] - ``` - - There could be places that you have code of the form: - - ```mojo - fn f(ptr: DTypePointer): - ``` - - which is equivalent to `DTypePointer[*_]`. In this case you would have to add - an infer-only `type` parameter to the function: - - ```mojo - fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): - ``` - - because we can’t have an unbound parameter inside the struct. - - There could also be places where you use - `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to - change these to `UnsafePointer[NoneType/Int]`. But since these are not an - `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to - appropriate types. - -- The `DTypePointer` `load()`, `store()`, and `prefetch()` methods have been - moved to `SIMD` and now take an - `UnsafePointer` as an argument. Instead of using `ptr.load[width=4](offset)` - one should use `SIMD[size=4].load(ptr, offset)`. Note the default load width - before was 1, but the default size of `SIMD` is the size of the SIMD type. The - default store size is the size of the `SIMD` value to be stored. - -- `UnsafePointer` now supports `simd_strided_load()`, `simd_strided_store()`, - `gather()`, and `scatter()` when the underlying type is `Scalar[DType]`. - -- The global functions for working with `UnsafePointer` have transitioned to - being methods through the use of conditional conformances: - - - `destroy_pointee(p)` => `p.destroy_pointee()` - - `move_from_pointee(p)` => `p.take_pointee()` - - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` - - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` - - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` - -- The `UnsafePointer.offset()` method has been removed. Use - [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. - - ```mojo - new_ptr = ptr.offset(1) - ``` - - Becomes: - - ```mojo - new_ptr = ptr + 1 - ``` - -- `UnsafePointer` has a new - `exclusive: Bool = False` parameter. Setting this parameter to true tells the - compiler that the user knows this pointer and all those derived from it have - exclusive access to the underlying memory allocation. The compiler is not - guaranteed to do anything with this information. - -- It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the - `UnsafePointer.address_of(someRef[])` which makes the code explicit that the - `UnsafePointer` gets the address of what the reference points to. - -- `sort` no longer takes `LegacyPointer`. The current API supports: - - `sort(list)` just plain list - - `sort[type, cmp_fn](list)` list with custom compare function - - `sort(ptr, len)` a pointer and length (can change to Span in future) - - `sort[type, cmp_fn](ptr, len)` above with custom compare - -- Continued transition to `UnsafePointer` and unsigned byte type for strings: - - - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `UnsafePointer[Int8]`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `UnsafePointer[Int8]`) - -- `await` on a coroutine now consumes it. This strengthens the invariant that - coroutines can only be awaited once. - -- `print()` now requires that its arguments conform to the `Formattable` trait. - This enables efficient stream-based writing by default, avoiding unnecessary - intermediate String heap allocations. - - Previously, `print()` required types conform to `Stringable`. This meant that - to execute a call like `print(a, b, c)`, at least three separate String heap - allocations were down, to hold the formatted values of `a`, `b`, and `c` - respectively. The total number of allocations could be much higher if, for - example, `a.__str__()` was implemented to concatenate together the fields of - `a`, like in the following example: - - ```mojo - struct Point(Stringable): - var x: Float64 - var y: Float64 - - fn __str__(self) -> String: - # Performs 3 allocations: 1 each for str(..) of each of the fields, - # and then the final returned `String` allocation. - return "(" + str(self.x) + ", " + str(self.y) + ")" - ``` - - A type like the one above can transition to additionally implementing - `Formattable` with the following changes: - - ```mojo - struct Point(Stringable, Formattable): - var x: Float64 - var y: Float64 - - fn __str__(self) -> String: - return String.format_sequence(self) - - fn format_to(self, inout writer: Formatter): - writer.write("(", self.x, ", ", self.y, ")") - ``` - - In the example above, `String.format_sequence()` is used to construct a - `String` from a type that implements `Formattable`. This pattern of - implementing a type's `Stringable` implementation in terms of its `Formattable` - implementation minimizes boilerplate and duplicated code, while retaining - backwards compatibility with the requirements of the commonly used `str(..)` - function. - - - - > [!WARNING] - > The error shown when passing a type that does not implement `Formattable` to - > `print()` is currently not entirely descriptive of the underlying cause: - > - > ```shell - > error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified - > print(point) - > ~~~~~^~~~~~~ - > ``` - > - > If the above error is seen, ensure that all argument types implement - > `Formattable`. - -- `debug_assert()` now also requires that its `message` argument conform to - `Formattable`. - -- The `StringRef` constructors from `DTypePointer.int8` have been changed to - take a `UnsafePointer[C_char]`, reflecting their use for compatibility with - C APIs. - -- `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been - removed since a Slice with no end is now represented by an empty `Slice.end` - option. - ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) - - ```mojo - var s = Slice(1, None, 2) - print(s.start.value()) # must retrieve the value from the optional - ``` - -- `NoneType` is now a normal standard library type, and not an alias for a raw - MLIR type. - - Function signatures spelled as `fn(...) -> NoneType` should transition to - being written as `fn(...) -> None`. - -- Accessing local Python modules with `Python.add_to_path(".")` is no longer - required, it now behaves the same as Python, you can access modules in the - same folder as the target file: - - - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` - -- The rank argument for `algorihtm.elementwise` is no longer required and is - only inferred. - -- The `ulp` function in `numerics` has been moved to the `math` module. - -- The Mojo Language Server no longer sets `.` as a commit character for - auto-completion. - -- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer - implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced - for types where this behavior is desired. - -- The `time.now()` function has been deprecated. Please use `time.perf_counter` - or `time.perf_counter_ns` instead. - -- A few bit functions have been renamed for clarity: -- `countl_zero` -> `count_leading_zeros` -- `countr_zero` -> `count_trailing_zeros` - -- Now that we have a `UInt` type, use this to represent the return type of hash. - In general, hashes should be an unsigned integer, and can also lead to improved - performance in certain cases. - -- The `atol` function now correctly supports leading underscores, - (e.g.`atol("0x_ff", 0)`), when the appropriate base is specified or inferred - (base 0). non-base-10 integer literals as per Python's [Integer Literals](\ - ). - ([PR #3180](https://github.com/modularml/mojo/pull/3180) - by [@jjvraw](https://github.com/jjvraw)) - -- `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. - -- `SIMD.load/store` are moved to `UnsafePointer`. - -- `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` - are removed from prelude. - -- The `simd_strided_load()` and `simd_strided_store()` have been renamed to - `strided_load` and `strided_store` in `UnsafePointer`. - -- `mojo test` now uses the Mojo compiler for running unit tests. This will resolve - compilation issues that sometimes appeared, and will also improve overall test - times, since we will only compile unit tests once before executing all of them. - - These changes do not apply to doctests, due to their different semantics. - -- The `mojo debug --rpc` command has been renamed to `mojo debug --vscode`, - which is now able to manage multiple VS Code windows. - ### ❌ Removed -- Support for the legacy `fn __init__(...) -> Self:` form has been removed from - the compiler, please switch to using `fn __init__(inout self, ...):` instead. - -- Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the - same thing. - -- Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. - -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD - instead. - -- The builtin `tensor` module has been removed. Identical functionality is - available in `max.tensor`, but it is generally recommended to use `buffer` - when possible instead. - -- Removed the Mojo Language Server warnings for unused function arguments. - -- Removed the `SIMD.{add,mul,sub}_with_overflow` methods. - -- Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is - available using the builting `min` and `max` functions. - -- `Run Mojo File in Dedicated Terminal` action has been removed, and the - action `Run Mojo File` will always open a dedicated terminal for each mojo - file to guarantee a correct environment. - ### 🛠️ Fixed - -- Fixed a crash in the Mojo Language Server when importing the current file. - -- Fixed crash when specifying variadic keyword arguments without a type - expression in `def` functions, e.g.: - - ```mojo - def foo(**kwargs): ... # now works - ``` - -- Mojo now prints `ref` arguments and results in generated documentation - correctly. - -- [#1734](https://github.com/modularml/mojo/issues/1734) - Calling - `__copyinit__` on self causes crash. - -- [#3142](https://github.com/modularml/mojo/issues/3142) - [QoI] Confusing - `__setitem__` method is failing with a "must be mutable" error. - -- [#248](https://github.com/modularml/mojo/issues/248) - [Feature] Enable - `__setitem__` to take variadic arguments - -- [#3065](https://github.com/modularml/mojo/issues/3065) - Fix incorrect behavior - of `SIMD.__int__` on unsigned types - -- [#3045](https://github.com/modularml/mojo/issues/3045) - Disable implicit SIMD - conversion routes through `Bool` - -- [#3126](https://github.com/modularml/mojo/issues/3126) - [BUG] List doesn't - work at compile time. - -- [#3237](https://github.com/modularml/mojo/issues/3237) - [BUG] Difference - between `__getitem__` and `[.]` operator. - -- [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated - references to `let` in REPL documentation. - -- The VS Code extension doesn't cache anymore the information of the selected - MAX SDK, which was causing issues upon changes in the SDK. - -- The Mojo debugger now stops showing spurious warnings when parsing closures. From 6b13c9f637efbea1d1cf4ec86d96d992c9f12d61 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 4 Sep 2024 16:16:26 -0700 Subject: [PATCH 1463/2019] Update READMEs to use Magic/conda MODULAR_ORIG_COMMIT_REV_ID: f276c6c9d17007c1c6771c968e3bdc1f23ff3c99 --- CONTRIBUTING.md | 32 +++++++++++------------ README.md | 41 +++++++++++++++++++---------- examples/README.md | 29 +++++++++++---------- examples/notebooks/README.md | 50 +++++++++++++++++++++--------------- examples/notebooks/pixi.toml | 13 ++++++++++ 5 files changed, 100 insertions(+), 65 deletions(-) create mode 100644 examples/notebooks/pixi.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b59db72aee..c0d372d70d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -261,30 +261,30 @@ git rebase upstream/nightly #### Getting the nightly Mojo compiler Now that you're on the nightly branch, you need to install the latest nightly -Mojo compiler: +build. -```bash -curl https://get.modular.com | sh - - -modular auth +If you're using [`magic`](https://docs.modular.com/magic), create a new +project environment with the `max-nightly` channel like this: -modular install nightly/mojo +```bash +magic init mojo-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly ``` -If you already have an older `nightly/mojo` compiler, replace -`modular install nightly/mojo` with `modular update nightly/mojo`. +If you're [using conda](https://docs.modular.com/magic/conda), add the +`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +file. For example: -Then, follow the instructions from the `modular` tool in adding the `mojo` -compiler to your `PATH` such as: +```yaml +[project] +name = "Mojo nightly example" +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] -```bash -echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc -echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc +[dependencies] +max = "*" ``` -If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. - #### Mojo nightly vscode extension Install the [Mojo nightly VS Code diff --git a/README.md b/README.md index d4058593c0..a3175c6088 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,8 @@ To learn more about Mojo, see the ### Latest Released -To install the last released build of Mojo, you can install the MAX SDK -or the standalone Mojo SDK: - -- [Get the MAX SDK](https://docs.modular.com/engine/get-started) -- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) - -Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started#2-run-code-in-the-repl). +To install the last released build of Mojo, follow the guide to +[Get started with Mojo](https://docs.modular.com/mojo/manual/get-started). ### Latest Nightly @@ -56,13 +50,32 @@ The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk and be patient! -To get nightly builds, see the same instructions to [install the Mojo -SDK](https://docs.modular.com/mojo/manual/get-started/#1-install-mojo), but use -the command shown there to install `nightly/mojo`. +To get nightly builds, see the same instructions to [Get started with +Mojo](https://docs.modular.com/mojo/manual/get-started), but when you create +your project, instead use the following `magic init` command to set the +conda package channel to `max-nightly`: + +```bash +magic init hello-world-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly +``` + +Or, if you're [using conda](https://docs.modular.com/magic/conda), add the +`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +file. For example: + +```yaml +[project] +name = "Mojo nightly example" +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] + +[dependencies] +max = "*" +``` -When you clone this repo, be sure you switch to the `nightly` branch, because -the `main` branch is for stable releases and might not be compatible with -nightly builds: +And when you clone this repo, switch to the `nightly` branch because the `main` +branch might not be compatible with nightly builds: ```bash git clone https://github.com/modularml/mojo.git diff --git a/examples/README.md b/examples/README.md index f5d82b7152..598ad13844 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,11 +1,11 @@ # Mojo code examples -A collection of sample programs and Mojo notebooks written in the +A collection of sample programs and Mojo notebooks written in the [Mojo](https://docs.modular.com/mojo/programming-manual.html) programming language. ## Getting Started -Access a Mojo programming environment available from the +Access a Mojo programming environment available from the Mojo product [page](https://www.modular.com/mojo). Git clone the repository of Mojo samples using the command below: @@ -16,23 +16,24 @@ git clone https://github.com/modularml/mojo.git ## Running -Use the following sample command-line to run the programs: +If you're using [`magic`](https://docs.modular.com/magic), navigate into +the examples directory and use `magic run`. For example: ```bash -mojo matmul.mojo +magic run mojo matmul.mojo ``` -You can run the Mojo notebooks using [JupyterLab or Visual Studio +You can run the Mojo notebooks using [JupyterLab or Visual Studio Code](notebooks/README.md) with the Mojo extension available on the Marketplace. ### Mojo SDK Container -The repo also contains a Dockerfile that can be used to create a -Mojo SDK container for developing and running Mojo programs. Use the -container in conjunction with the Visual Studio Code devcontainers +The repo also contains a Dockerfile that can be used to create a +Mojo SDK container for developing and running Mojo programs. Use the +container in conjunction with the Visual Studio Code devcontainers extension to develop directly inside the container. -The Dockerfile also sets up a `conda` environment and by default, +The Dockerfile also sets up a `conda` environment and by default, starts a `jupyter` server (which you can access via the browser). To build a Mojo container, either use @@ -55,11 +56,11 @@ The script also supports building with `podman` instead of `docker`: ./build-image.sh --auth-key \ --use-podman \ --mojo-version 0.3 - + ``` -You can then run with either `docker` or `podman`. In the example below, -we map the ports, bind mount the current directory and open a shell into +You can then run with either `docker` or `podman`. In the example below, +we map the ports, bind mount the current directory and open a shell into the container: ```bash @@ -85,8 +86,8 @@ podman run \ ## License -The Mojo examples and notebooks in this repository are licensed -under the Apache License v2.0 with LLVM Exceptions +The Mojo examples and notebooks in this repository are licensed +under the Apache License v2.0 with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Contributing diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 7c6eecf342..4ee3854595 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -29,9 +29,10 @@ notebooks. Especially if you're developing with Mojo on a remote system, using VS Code is ideal because it allows you to edit and interact with notebooks on the remote machine where you've installed Mojo. -All you need is the Mojo SDK and the Jupyter VS Code extension: +All you need is Mojo and the Jupyter VS Code extension: -1. Install the [Mojo SDK](https://developer.modular.com/download). +1. [Create a new Mojo +project](https://docs.modular.com/mojo/manual/get-started#1-create-a-new-project). 2. Install [Visual Studio Code](https://code.visualstudio.com/) and the [Jupyter @@ -56,33 +57,40 @@ instructions don't support remote access to the JupyterLab). For more details about using JupyterLab, see the complete [JupyterLab installation guide](https://jupyterlab.readthedocs.io/en/latest/getting_started/installation.html). -**Note:** You must run this setup on the same machine where you've installed -the [Mojo SDK](https://developer.modular.com/download). However, syntax -highlighting for Mojo code is not currently enabled in JupyterLab (coming soon). +### 1. Launch JupyterLab -1. Install JupyterLab: +You can use either Magic or conda. - ```sh - python3 -m pip install jupyterlab - ``` +#### Using Magic -2. Make sure the user-level `bin` is in your `$PATH`: +If you have [`magic`](https://docs.modular.com/magic) you can run the following +command to launch JupyterLab from this directory: - ```sh - export PATH="$HOME/.local/bin:$PATH" - ``` +```sh +magic run jupyter lab +``` -3. Launch JupyterLab: +After a moment, it will open a browser window with JupterLab running. - ```sh - jupyter lab - ``` +#### Using conda -4. When you open any of the `.ipynb` notebooks from this repository, JupyterLab - should automatically select the Mojo kernel (which was installed with the - Mojo SDK). +Create a Conda environment, activate that enviroment, and install JupyterLab. - Now run some Mojo code! +``` sh +# Create a Conda environment if you don't have one +conda create -n mojo-repo +# Activate the environment +conda env update -n mojo-repo -f environment.yml --prune +# run JupyterLab +conda run -n mojo-repo jupyter lab +``` + +After a moment, it will open a browser window with JupterLab running. + +### 2. Run the .ipynb notebooks + +The left nav bar should show all the notebooks in this directory. +Open any `.ipynb` file and start running the code. ## Notes and tips diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml new file mode 100644 index 0000000000..668fdf4caa --- /dev/null +++ b/examples/notebooks/pixi.toml @@ -0,0 +1,13 @@ +[project] +name = "Mojo notebooks" +version = "1.0.0" +description = "Environment for running JupyterLab" +authors = ["Modular "] +channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] + +[dependencies] +python = ">=3.9,<3.13" +max = "*" +pip = ">=24.0,<25" +jupyterlab = ">=4.2.5,<5" From 10dcfcc9fc58f048196e3c57ad7fdc0bd50224b5 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 4 Sep 2024 20:00:01 -0700 Subject: [PATCH 1464/2019] Edited Mojo changelog for the v24.5 release and added API reference links MODULAR_ORIG_COMMIT_REV_ID: 2bf3af7de7efbe3c122e58c7927a6c52d05a4b2c --- docs/changelog-released.md | 1183 ++++++++++++++++++++---------------- 1 file changed, 656 insertions(+), 527 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 52e4d4f88c..126ea36a88 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -16,13 +16,11 @@ guide](/mojo/manual/get-started). :::caution [Magic](/magic) is the preferred package manager and virtual environment manager -for MAX and Mojo projects. [conda](https://docs.conda.io/projects/conda/en/latest/index.html) -is supported as an alternative. +for MAX and Mojo projects. [conda](/magic/conda) is supported as an alternative. -The legacy [`modular`](/cli) CLI is now deprecated. -We will not release any new `max` or `mojo` packages through the `modular` -tool beyond the 24.5 release. You must now use [Magic](/magic/) or -[conda](/magic/conda) to install MAX and Mojo. +The legacy [`modular`](/cli) CLI is now deprecated. We will not release any new +`max` or `mojo` packages through the `modular` tool beyond the 24.5 release. You +must now use [Magic](/magic) or [conda](/magic/conda) to install MAX and Mojo. ::: @@ -30,14 +28,6 @@ tool beyond the 24.5 release. You must now use [Magic](/magic/) or The virtual environment for each Magic project has its own package versions. The Mojo programming language is distributed as part of the `max` package. - -To view the version of Mojo for a specific Magic project, run the following -command within your project path: - -```sh -magic run mojo --version -``` - Use the [`magic update`](/magic/commands#magic-update) within your project path to update the `max` package for that project to the latest release: @@ -52,15 +42,6 @@ virtual environments. Each conda virtual environment has its own package versions. The Mojo programming language is distributed as part of the `max` package. - -Use the [`conda list`](https://docs.conda.io/projects/conda/en/latest/commands/list.html) -command to list the version of the `max` package for an environment. -For example, for an environment named `max-project`, run: - -```sh -conda list -n max-project max -``` - Use the [`conda update`](https://docs.conda.io/projects/conda/en/latest/commands/update.html) command to update the version of the `max` package for an environment. For example, for an environment named `max-project`, run: @@ -74,39 +55,78 @@ documentation for more information on managing conda virtual environments. ### Update Mojo using the `modular` CLI -:::caution +If you are still using the deprecated `modular` CLI, you can update Mojo to +24.5 by running: + +```sh +modular update mojo +``` -The legacy [`modular`](/cli) CLI is now deprecated. We will not release any new `max` or `mojo` packages through the `modular` tool beyond the 24.5 release. You must now use [Magic](/magic/) or [conda](/magic/conda) to install MAX and Mojo. -::: +## v24.5 (2024-09-10) -To see your Mojo version, run this: +### ✨ Highlights -```sh -mojo --version -``` +- The set of automatically imported entities (types, aliases, functions) into + users' Mojo programs has been dramatically reduced. This can break existing + user code as users will need to explicitly import what they're using for cases + previously automatically included before. + +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed, but + is now optional. -To update Mojo, first [update `modular`](/cli/#description), and then run this: +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. -```sh -modular update mojo -``` +- Mojo now diagnoses "argument exclusivity" violations due to aliasing + references. Mojo requires references (including implicit references due to + `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if + mutable. This is a warning in the 24.5 release, but will be upgraded to an + error in subsequent releases. -## v24.5 (2024-09-10) +- `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. + Functions that previously took a `DTypePointer` now take an equivalent + `UnsafePointer`. For more information on using pointers, see [Unsafe + pointers](/mojo/manual/pointers) in the Mojo Manual. -### ⭐️ New +- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments + conform to the `Formattable` trait. This enables efficient stream-based + writing by default, avoiding unnecessary intermediate String heap allocations. + +- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an + optional prompt and reads a line from standard input, in the same way as + Python. + +- There are many new standard library APIs, with new features for strings, + collections, and interacting with the filesystem and environment. Changes are + listed in the standard library section. + +- The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo features, + including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and + more. + +- [`mojo test`](/mojo/cli/test) now uses the Mojo compiler for running unit + tests. This will resolve compilation issues that sometimes appeared, and will + also improve overall test execution times. + +### Language changes + +#### ⭐️ New - Mojo now diagnoses "argument exclusivity" violations due to aliasing - references. Mojo requires references (including implicit references due to - borrowed/inout arguments) to be uniquely referenced (non-aliased) if mutable. - This is important for code safety, because it allows the compiler (and readers - of code) to understand where and when a value is mutated. It is also useful - for performance optimization because it allows the compiler to know that - accesses through immutable references cannot change behind the scenes. Here is - an invalid example: + references. Mojo requires references (including implicit references due to + `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if + mutable. This is important for code safety, because it allows the compiler + (and readers of code) to understand where and when a value is mutated. It is + also useful for performance optimization because it allows the compiler to + know that accesses through immutable references cannot change behind the + scenes. Here is an invalid example: ```mojo fn take_two_strings(a: String, inout b: String): @@ -137,9 +157,9 @@ modular update mojo - Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This feature provides more flexibility and guarantees around emplacing the result - of a function compared to "guaranteed" NRVO. If a `@register_passable` result - is bound to a name, the result value is made accessible as a mutable - reference. + of a function compared to "guaranteed" named return value optimization (NRVO). + If a `@register_passable` result is bound to a name, the result value is made + accessible as a mutable reference. ```mojo fn efficiently_return_string(b: Bool) -> String as output: @@ -158,38 +178,7 @@ modular update mojo return value of the function. The compiler will error if the result is not initialized on all normal exit paths from the function. -- `String` class now have `rjust`, `ljust` and `center` methods to return - a justified string based on width and fillchar. - ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by - [@mzaks](https://github.com/mzaks)) - -- Creating nested `PythonObject` from a list or tuple of python objects is - possible now: - - ```mojo - var np = Python.import_module("numpy") - var a = np.array([1, 2, 3]) - var b = np.array([4, 5, 6]) - var arrays = PythonObject([a, b]) - assert_equal(len(arrays), 2) - ``` - - Also allowing more convenient call syntax: - - ```mojo - var stacked = np.hstack((a, b)) - assert_equal(str(stacked), "[1 2 3 4 5 6]") - ``` - - ([PR 3264#](https://github.com/modularml/mojo/pull/3264) by - [@kszucs](https://github.com/kszucs)) - -- `List[T]` values are now equality comparable with `==` and `!=` when `T` is - equality comparable. - ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by - [@kszucs](https://github.com/kszucs)) - -- `__setitem__` now works with variadic argument lists such as: +- `__setitem__()` now works with variadic argument lists such as: ```mojo struct YourType: @@ -197,18 +186,10 @@ modular update mojo ``` The Mojo compiler now always passes the "new value" being set using the last - keyword argument of the `__setitem__`, e.g. turning `yourType[1, 2] = 3` into + keyword argument of the `__setitem__()`, e.g. turning `yourType[1, 2] = 3` into `yourType.__setitem__(1, 2, val=3)`. This fixes [Issue #248](https://github.com/modularml/mojo/issues/248). -- `Optional` values are now equality comparable with `==` and `!=` when their - element type is equality comparable. - -- Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) - dictionary-like type, matching most of the features of the Python one. - ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by - [@msaelices](https://github.com/msaelices)) - - Mojo context managers used in regions of code that may raise no longer need to define a "conditional" exit function in the form of `fn __exit__(self, e: Error) -> Bool`. This function allows the context @@ -236,8 +217,8 @@ modular update mojo print(x) # no longer complains about 'x' being uninitialized ``` -- Now supports "conditional conformances" where some methods on a struct have - additional trait requirements that the struct itself doesn't. This is +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. This is expressed through an explicitly declared `self` type: ```mojo @@ -263,8 +244,27 @@ modular update mojo Conditional conformance works with dunder methods and other things as well. +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: + + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass + + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. + ``` + - `async` functions now support memory-only results (like `String`, `List`, - etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have + etc.) and `raises`. Accordingly, both + [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) and + [`RaisingCoroutine`](/mojo/stdlib/builtin/coroutine/RaisingCoroutine) have been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means the result types of `async` functions do not need to be `Movable`. @@ -278,565 +278,679 @@ modular update mojo Note that `async` functions do not yet support indirect calls, `ref` results, and constructors. -- As a specific form of "conditional conformances", initializers in a struct - may indicate specific parameter bindings to use in the type of their `self` - argument. For example: +- The [`Reference`](/mojo/stdlib/memory/reference/Reference) type (and many + iterators) now use [infer-only + parameters](/mojo/manual/parameters/#infer-only-parameters) to represent the + mutability of their lifetime, simplifying the interface. - ```mojo - @value - struct MyStruct[size: Int]: - fn __init__(inout self: MyStruct[0]): pass - fn __init__(inout self: MyStruct[1], a: Int): pass - fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass +- The environment variable `MOJO_PYTHON` can be pointed to an executable to pin + Mojo to a specific version: - def test(x: Int): - a = MyStruct() # Infers size=0 from 'self' type. - b = MyStruct(x) # Infers size=1 from 'self' type. - c = MyStruct(x, x) # Infers size=2 from 'self' type. + ```sh + export MOJO_PYTHON="/usr/bin/python3.11" ``` -- The `Reference` type (and many iterators) now use - [infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) to - represent the mutability of their lifetime, simplifying the interface. + Or a virtual environment to always have access to those Python modules: -- `Dict` now implements `setdefault`, to get a value from the dictionary by - key, or set it to a default if it doesn't exist - ([PR #2803](https://github.com/modularml/mojo/pull/2803) - by [@msaelices](https://github.com/msaelices)) + ```sh + export MOJO_PYTHON="~/venv/bin/python" + ``` -- Added new `ExplicitlyCopyable` trait, to mark types that can be copied - explicitly, but which might not be implicitly copyable. + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + but no Python executable. - This supports work to transition the standard library collection types away - from implicit copyability, which can lead to unintended expensive copies. +#### 🦋 Changed -- Added `Identifiable` trait, used to describe types that implement the `__is__` - and `__isnot__` trait methods. - ([PR #2807](https://github.com/modularml/mojo/pull/2807)) +- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a + C-like set of semantics around pointer aliasing and derivation. However, the C + semantics bring a lot of history and baggage that are not needed in Mojo and + which complicate compiler optimizations. The language overall provides a + stronger set of invariants around pointer aliasing with lifetimes and + exclusive mutable references to values, etc. - - Also added new `assert_is()` and `assert_is_not()` test utilities to the - `testing` module. + It is now forbidden to convert a non-pointer-typed value derived from a + Mojo-allocated pointer, such as an integer address, to a pointer-typed value. + "Derived" means there is overlap in the bits of the non-pointer-typed value + with the original pointer value. Accordingly, the + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) + constructor that took an `address` keyword argument has been removed. -- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. - ([PR #2701](https://github.com/modularml/mojo/pull/2701) - by [@jayzhan211](https://github.com/jayzhan211)) + It is still possible to make this conversion in certain cases where it is + absolutely necessary, such as interoperating with other languages like Python. + In this case, the compiler makes two assumptions: any pointer derived from a + non-pointer-typed value does not alias any Mojo-derived pointer and that any + external function calls have arbitrary memory effects. -- Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that - returns an `UnsafePointer[C_char]` for convenient interoperability with C - APIs. +- `await` on a coroutine now consumes it. This strengthens the invariant that + coroutines can be awaited only once. -- Added `C_char` type alias in `sys.ffi`. +### Standard library changes -- Added `StringSlice(..)` initializer from a `StringLiteral`. +- [`builtin`](/mojo/stdlib/builtin/) package: -- Added a `byte_length()` method to `String`, `StringSlice`, and `StringLiteral` -and deprecated their private `_byte_length()` methods. Added a warning to -`String.__len__` method that it will return length in Unicode codepoints in the -future and `StringSlice.__len__` now does return the Unicode codepoints length. -([PR #2960](https://github.com/modularml/mojo/pull/2960) by [@martinvuyk](https://github.com/martinvuyk)) + - The set of automatically imported entities (types, aliases, functions) into + users' Mojo programs has been dramatically reduced. Before, with the way the + `builtin` module was handled, all of the entities in the following modules + would be automatically included: -- Added new `StaticString` type alias. This can be used in place of - `StringLiteral` for runtime string arguments. + `memory`, `sys`, `os`, `utils`, `python`, `bit`, `random`, `math`, + `builtin`, `collections` -- Added `TemporaryDirectory` in module `tempfile`. - ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) + Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are + the ones automatically imported into users' Mojo programs. This will break a + lot of user code as users will need to explicitly import what they're using + for cases previously commonly included before (such as + [`Optional`](/mojo/stdlib/collections/optional/Optional), + [`Variant`](/mojo/stdlib/utils/variant/Variant), and functions such as + [`abort()`](/mojo/stdlib/os/os/abort), + [`alignof()`](/mojo/stdlib/sys/info/alignof), + [`bitcast()`](/mojo/stdlib/memory/unsafe/bitcast), + [`bitwidthof()`](/mojo/stdlib/sys/info/bitwidthof), + [`external_call()`](/mojo/stdlib/sys/ffi/external_call), + [`simdwidthof()`](/mojo/stdlib/sys/info/simdwidthof), and + [`sizeof()`](/mojo/stdlib/sys/info/sizeof)). -- Added `NamedTemporaryFile` in module `tempfile`. - ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) + - Some types from the `builtin` module have been moved to different modules + for clarity which is made possible now that we have a `prelude` module that + can re-export symbols from modules other than `builtin`. -- Added `oct(..)` function for formatting an integer in octal. - ([PR #2914](https://github.com/modularml/mojo/pull/2914) by [@bgreni](https://github.com/bgreni)) + In particular, the `builtin.string` module has been moved to + [`collections.string`](/mojo/stdlib/collections/string/). -- Added `String.format` method. - ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) +- Input and output: - Support automatic and manual indexing of `*args`. + - Added the builtin [`input()`](/mojo/stdlib/builtin/io/input) function, which + behaves the same as Python. + ([PR #3392](https://github.com/modularml/mojo/pull/3392)) - Examples: + ```mojo + name = input("Enter your name: ") + print("Hello, " + name + "!") + ``` - ```mojo - print( - String("{1} Welcome to {0} {1}").format("mojo", "🔥") - ) - # 🔥 Wecome to mojo 🔥 - ``` + If the user enters "Mojo" it returns "Hello Mojo!" - ```mojo - print(String("{} {} {}").format(True, 1.125, 2)) - #True 1.125 2 - ``` + - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments + conform to the `Formattable` trait. This enables efficient stream-based + writing by default, avoiding unnecessary intermediate String heap + allocations. -- Added the builtin `input` function, which behaves the same as Python. - ([PR #3392](https://github.com/modularml/mojo/pull/3392) by [@thatstoasty](https://github.com/thatstoasty)) + Previously, `print()` required types conform to + [`Stringable`](/mojo/stdlib/builtin/str/Stringable). This meant that to + execute a call like `print(a, b, c)`, at least three separate String heap + allocations were down, to hold the formatted values of `a`, `b`, and `c` + respectively. The total number of allocations could be much higher if, for + example, `a.__str__()` was implemented to concatenate together the fields of + `a`, like in the following example: - ```mojo - name = input("Enter your name: ") - print("Hello, " + name + "!") - ``` + ```mojo + struct Point(Stringable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + # Performs 3 allocations: 1 each for str(..) of each of the fields, + # and then the final returned `String` allocation. + return "(" + str(self.x) + ", " + str(self.y) + ")" + ``` - If the user enters "Mojo" it returns "Hello Mojo!" + A type like the one above can transition to additionally implementing + `Formattable` with the following changes: -- Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo - to a specific version: + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 - ```sh - export MOJO_PYTHON="/usr/bin/python3.11" - ``` + fn __str__(self) -> String: + return String.format_sequence(self) - Or a virtual environment to always have access to those Python modules: + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` - ```sh - export MOJO_PYTHON="~/venv/bin/python" - ``` + In the example above, + [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) + is used to construct a `String` from a type that implements `Formattable`. + This pattern of implementing a type's `Stringable` implementation in terms + of its `Formattable` implementation minimizes boilerplate and duplicated + code, while retaining backwards compatibility with the requirements of the + commonly used `str()` function. - `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, - but no Python executable. + -- The `math` package now includes the `pi`, `e`, and `tau` constants (Closes - Issue [#2135](https://github.com/modularml/mojo/issues/2135)). + :::note TODO -- Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a - paltform-dependent width. `UInt` implements most arithmetic operations that - make sense for integers, with the notable exception of `__neg__`. Builtin - functions such as `min`/`max`, as well as `math` functions like `ceildiv`, - `align_down`, and `align_up` are also implemented for `UInt`. + The error shown when passing a type that does not implement `Formattable` to + `print()` is currently not entirely descriptive of the underlying cause: -- `os.path.expanduser()` and `pathlib.Path.exapanduser()` have been added to - allow expanding a prefixed `~` in a `String` or `Path` with the users home - path: + ```shell + error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified + print(point) + ~~~~~^~~~~~~ + ``` - ```mojo - import os - print(os.path.expanduser("~/.modular")) - # /Users/username/.modular - print(os.path.expanduser("~root/folder")) - # /var/root/folder (on macos) - # /root/folder (on linux) - ``` + If you see the above error, ensure that all argument types implement + `Formattable`. -- `Path.home()` has been added to return a path of the users home directory. + ::: -- `os.path.split()` has been added for splitting a path into `head, tail`: + - [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now also + requires that its `message` argument conform to `Formattable`. - ```mojo - import os - head, tail = os.path.split("/this/is/head/tail") - print("head:", head) - print("tail:", tail) - # head: /this/is/head - # tail: tail - ``` + - Added + [`TemporaryDirectory`](/mojo/stdlib/tempfile/tempfile/TemporaryDirectory) in + module `tempfile`. + ([PR 2743](https://github.com/modularml/mojo/pull/2743)) -- `os.path.makedirs()` and `os.path.removedirs()` have been added for creating - and removing nested directories: + - Added + [`NamedTemporaryFile`](/mojo/stdlib/tempfile/tempfile/NamedTemporaryFile) in + module `tempfile`. + ([PR 2762](https://github.com/modularml/mojo/pull/2762)) - ```mojo - import os - path = os.path.join("dir1", "dir2", "dir3") - os.path.makedirs(path, exist_ok=True) - os.path.removedirs(path) - ``` +- [`String`](/mojo/stdlib/collections/string/String) and friends: -- The `pwd` module has been added for accessing user information in - `/etc/passwd` on POSIX systems. This follows the same logic as Python: + - The `builtin.string` module has been moved to + [`collections.string`](/mojo/stdlib/collections/string/). - ```mojo - import pwd - import os - current_user = pwd.getpwuid(os.getuid()) - print(current_user) + - Added the [`String.format()`](/mojo/stdlib/collections/string/String#format) + method. + ([PR #2771](https://github.com/modularml/mojo/pull/2771)) - # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, - # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', - # pw_shell='/bin/zsh') + Supports automatic and manual indexing of `*args`. - print(current_user.pw_uid) + Examples: - # 501 + ```mojo + print( + String("{1} Welcome to {0} {1}").format("mojo", "🔥") + ) + # 🔥 Wecome to mojo 🔥 + ``` - root = pwd.getpwnam("root") - print(root) + ```mojo + print(String("{} {} {}").format(True, 1.125, 2)) + #True 1.125 2 + ``` - # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, - # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') - ``` + - [`String.format()`](/mojo/stdlib/collections/string/String#format) now + supports conversion flags `!s` and `!r`, allowing for `str()` and `repr()` + conversions within format strings. + ([PR #3279](https://github.com/modularml/mojo/pull/3279)) -- Added `Dict.__init__` overload to specify initial capacity. - ([PR #3171](https://github.com/modularml/mojo/pull/3171) by [@rd4com](https://github.com/rd4com)) + Example: - The capacity has to be a power of two and above or equal 8. + ```mojo + String("{} {!r}").format("Mojo", "Mojo") + # "Mojo 'Mojo'" - It allows for faster initialization by skipping incremental growth steps. + String("{0!s} {0!r}").format("Mojo") + # "Mojo 'Mojo'" + ``` - Example: + - The `String` class now has + [`rjust()`](/mojo/stdlib/collections/string/String#rjust), + [`ljust()`](/mojo/stdlib/collections/string/String#ljust), and + [`center()`](/mojo/stdlib/collections/string/String#center) methods to + return a justified string based on width and fillchar. ([PR + #3278](https://github.com/modularml/mojo/pull/3278)) + + - The [`atol()`](/mojo/stdlib/collections/string/atol) function now correctly + supports leading underscores, (e.g.`atol("0x_ff", 0)`), when the appropriate + base is specified or inferred (base 0). non-base-10 integer literals as per + Python's [Integer + Literals](). + ([PR #3180](https://github.com/modularml/mojo/pull/3180)) + + - Added the + [`unsafe_cstr_ptr()`](/mojo/stdlib/collections/string/String#unsafe_cstr_ptr) + method to `String` and `StringLiteral`, which returns an + `UnsafePointer[C_char]` for convenient interoperability with C APIs. + + - Added the `byte_length()` method to `String`, `StringSlice`, and + `StringLiteral` and deprecated their private `_byte_length()` methods. + Added a warning to `String.__len__` method that it will return length in + Unicode codepoints in the future and `StringSlice.__len__()` now does return + the Unicode codepoints length. + ([PR #2960](https://github.com/modularml/mojo/pull/2960)) + + - Added a new [`StaticString`](/mojo/stdlib/utils/string_slice/#aliases) type + alias. This can be used in place of + [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral) for + runtime string arguments. + + - Added a + [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice#__init__) + initializer that accepts a `StringLiteral`. + + - The [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) constructors from + `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, + reflecting their use for compatibility with C APIs. + + - Continued transition to `UnsafePointer` and unsigned byte type for strings: + + - [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr) + now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) + + - [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr) + now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) + +- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and other + reference type changes: + + - `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. + For more information on using pointers, see [Unsafe + pointers](/mojo/manual/pointers) in the Mojo Manual. + + Functions that previously took a `DTypePointer` now take an + equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to + `UnsafePointer` is: - ```mojo - var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) - # Insert (2/3 of 1024) entries - ``` + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] + ``` -- `ListLiteral` now supports `__contains__`. - ([PR #3251](https://github.com/modularml/mojo/pull/3251) by - [@jjvraw](https://github.com/jjvraw)) + There could be places that you have code of the form: -- `bit` module now supports `bit_reverse()`, `byte_swap()` and `pop_count()` for - `Int` type. - ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) + ```mojo + fn f(ptr: DTypePointer): + ``` -- `String.format()` now supports conversion flags `!s` and `!r`, allowing for - `str()` and `repr()` conversions within format strings. - ([PR #3279](https://github.com/modularml/mojo/pull/3279) by [@jjvraw](https://github.com/jjvraw)) + which is equivalent to `DTypePointer[*_]`. In this case you would have to add + an infer-only `type` parameter to the function: - Example: + ```mojo + fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): + ``` - ```mojo - String("{} {!r}").format("Mojo", "Mojo") - # "Mojo 'Mojo'" + because we can’t have an unbound parameter inside the struct. - String("{0!s} {0!r}").format("Mojo") - # "Mojo 'Mojo'" - ``` + There could also be places where you use + `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to + change these to `UnsafePointer[NoneType/Int]`. But since these are not an + `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to + appropriate types. -- `sort` now supports `stable` parameter. It can be called by + - The `DTypePointer` + [`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and + [`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods + have been moved to `UnsafePointer`. - ```mojo - sort[cmp_fn, stable=True](list) - ``` + - `UnsafePointer` now supports + [`strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_load), + [`strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_store), + [`gather()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#gather), and + [`scatter()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#scatter) when + the underlying type is `Scalar[DType]`. - The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to - allocate, the program will crash. + - The global functions for working with `UnsafePointer` have transitioned to + being methods through the use of conditional conformances: -- The `mojo test` command now accepts a `--filter` option that will narrow the - set of tests collected and executed. The filter string is a POSIX extended - regular expression. + - `destroy_pointee(p)` => [`p.destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) + - `move_from_pointee(p)` => [`p.take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee) + - `initialize_pointee_move(p, value)` => [`p.init_pointee_move(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move) + - `initialize_pointee_copy(p, value)` => [`p.init_pointee_copy(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) + - `move_pointee(src=p1, dst=p2)` => [`p.move_pointee_into(p2)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) -- The `mojo test` command now supports using the same compilation options as - `mojo build`. + - The `UnsafePointer.offset()` method has been removed. Use + [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. -- You can now debug unit tests using `mojo test` by passing the `--debug` flag. - Most debug flags are supported; run `mojo test --help` for a full listing. + ```mojo + new_ptr = ptr.offset(1) + ``` - Debugging doctests is not currently supported. + Becomes: -- `UnsafePointer` now has an `alignment` parameter to specify the static - alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes - in an alignment parameter, and the alignment should be specified in the type. + ```mojo + new_ptr = ptr + 1 + ``` - ```mojo - UnsafePointer[type].alloc[alignment](x) # now becomes - UnsafePointer[type, alignment].alloc(x) - ``` + - `UnsafePointer` now has an + [`alignment`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#parameters) + parameter to specify the static alignment of the pointer. Consequently, + [`UnsafePointer.alloc()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#alloc) + no longer takes in an alignment parameter, and the alignment should be + specified in the type. -- The VS Code extension now supports a vendored MAX SDK for VS Code, which is - automatically downloaded by the extension and it's used for all Mojo features, - including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and - more. + ```mojo + UnsafePointer[type].alloc[alignment](x) # now becomes + UnsafePointer[type, alignment].alloc(x) + ``` -- The Mojo debugger now hides the artificial function arguments `__result__` and - `__error__` created by the compiler for Mojo code. + - `UnsafePointer` has a new [`exclusive: Bool = + False`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#parameters) + parameter. Setting this parameter to true tells the compiler that the user + knows this pointer and all those derived from it have exclusive access to + the underlying memory allocation. The compiler is not guaranteed to do + anything with this information. -- The Mojo debugger now supports a `break-on-raise` command that indicated the - debugger to stop at any `raise` statements. A similar features has been added - to the debugger on VS Code. + - It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + [`UnsafePointer.address_of(someRef[])`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#address_of) + which makes the code explicit that the `UnsafePointer` gets the address of + what the reference points to. -- A proxy has been added to the Mojo Language Server on VS Code that handles - crashes more gracefully. +- Python interoperability changes: -### 🦋 Changed + - Creating a nested + [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) from a list + or tuple of Python objects is possible now: -- The set of automatically imported entities (types, aliases, functions) into user's - Mojo programs has been dramatically reduced. Before, with the way the `builtin` - module was handled, all of the entities in the following modules would be automatically - included: + ```mojo + var np = Python.import_module("numpy") + var a = np.array([1, 2, 3]) + var b = np.array([4, 5, 6]) + var arrays = PythonObject([a, b]) + assert_equal(len(arrays), 2) + ``` - {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', - 'builtin', 'collections'} + Also allowing more convenient call syntax: - Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are - the ones automatically imported into user's Mojo programs. This will break - a lot of user code as users will need to explicitly import what they're using - for cases previously commonly included before (such as `Optional`, `Variant`, - and so on). + ```mojo + var stacked = np.hstack((a, b)) + assert_equal(str(stacked), "[1 2 3 4 5 6]") + ``` -- Some types from the `builtin` module have been moved to different modules for clarity - which is made possible now that we have a `prelude` module that can re-export symbols - from modules other than `builtin`. - - `builtin.string` has been moved to `collections.string`. + ([PR #3264](https://github.com/modularml/mojo/pull/3264)) -- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a - C-like set of semantics around pointer aliasing and derivation. However, the C - semantics bring a lot of history and baggage that are not needed in Mojo and - which complicate compiler optimizations. The language overall provides a - stronger set of invariants around pointer aliasing with lifetimes and - exclusive mutable references to values, etc. + - Accessing local Python modules with + [`Python.add_to_path(".")`](/mojo/stdlib/python/python/Python#add_to_path) + is no longer required. It now behaves the same as Python. You can access + modules in the same folder as the target file: - It is now forbidden to convert a non-pointer-typed value derived from a - Mojo-allocated pointer, such as an integer address, to a pointer-typed value. - "Derived" means there is overlap in the bits of the non-pointer-typed value - with the original pointer value. Accordingly, the `UnsafePointer` constructor - that took an `address` keyword argument has been removed. + - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - It is still possible to make this conversion in certain cases where it is - absolutely necessary, such as interoperating with other languages like Python. - In this case, the compiler makes two assumptions: any pointer derived from a - non-pointer-typed value does not alias any Mojo-derived pointer and that any - external function calls have arbitrary memory effects. + - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` -- `DTypePointer` , `LegacyPointer` and `Pointer` have been removed. Use - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) instead. For more - information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in - the Mojo Manual. +- Collections: - Functions that previously took a `DTypePointer` now take an - equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to - `UnsafePointer` is: + - [`List`](/mojo/stdlib/collections/list/List) values are now equality + comparable with `==` and `!=` when their element type is equality + comparable. ([PR #3195](https://github.com/modularml/mojo/pull/3195)) - ```mojo - DTypePointer[type] -> UnsafePointer[Scalar[type]] - ``` + - [`Optional`](/mojo/stdlib/collections/optional/Optional) values are now + equality comparable with `==` and `!=` when their element type is equality + comparable. - There could be places that you have code of the form: + - Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) + dictionary-like type, matching most of the features of the Python one. + ([PR #2910](https://github.com/modularml/mojo/pull/2910)) - ```mojo - fn f(ptr: DTypePointer): - ``` + - [`Dict`](/mojo/stdlib/collections/dict/Dict) now implements + [`setdefault()`](/mojo/stdlib/collections/dict/Dict#setdefault), which gets + a value from the dictionary by key, or sets it to a default if it doesn't + exist. + ([PR #2803](https://github.com/modularml/mojo/pull/2803)) - which is equivalent to `DTypePointer[*_]`. In this case you would have to add - an infer-only `type` parameter to the function: + - `Dict` now supports + [`popitem()`](/mojo/stdlib/collections/dict/Dict#popitem), which removes and + returns the last item in the `Dict`. + ([PR #2701](https://github.com/modularml/mojo/pull/2701)) - ```mojo - fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): - ``` + - Added a [`Dict.__init__()`](/mojo/stdlib/collections/dict/Dict#__init__) + overload to specify initial capacity. + ([PR #3171](https://github.com/modularml/mojo/pull/3171)) - because we can’t have an unbound parameter inside the struct. + The capacity has to be a power of two and greater than or equal to 8. - There could also be places where you use - `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to - change these to `UnsafePointer[NoneType/Int]`. But since these are not an - `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to - appropriate types. + It allows for faster initialization by skipping incremental growth steps. -- The `DTypePointer` `load()`, `store()`, and `prefetch()` methods have been - moved to `SIMD` and now take an - `UnsafePointer` as an argument. Instead of using `ptr.load[width=4](offset)` - one should use `SIMD[size=4].load(ptr, offset)`. Note the default load width - before was 1, but the default size of `SIMD` is the size of the SIMD type. The - default store size is the size of the `SIMD` value to be stored. + Example: -- `UnsafePointer` now supports `simd_strided_load()`, `simd_strided_store()`, - `gather()`, and `scatter()` when the underlying type is `Scalar[DType]`. + ```mojo + var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) + # Insert (2/3 of 1024) entries + ``` -- The global functions for working with `UnsafePointer` have transitioned to - being methods through the use of conditional conformances: + - `ListLiteral` now supports + [`__contains__()`](/mojo/stdlib/builtin/builtin_list/ListLiteral#__contains__). + ([PR #3251](https://github.com/modularml/mojo/pull/3251)) - - `destroy_pointee(p)` => `p.destroy_pointee()` - - `move_from_pointee(p)` => `p.take_pointee()` - - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` - - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` - - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` +- Filesystem and environment utilities: -- The `UnsafePointer.offset()` method has been removed. Use - [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. + - [`Path.home()`](/mojo/stdlib/pathlib/path/Path#home) has been added to + return a path of the users home directory. - ```mojo - new_ptr = ptr.offset(1) - ``` + - [`os.path.expanduser()`](/mojo/stdlib/os/path/path/expanduser) and + [`pathlib.Path.exapanduser()`](/mojo/stdlib/pathlib/path/Path#expanduser) + have been added to allow expanding a prefixed `~` in a `String` or `Path` + with the users home path: - Becomes: + ```mojo + import os + print(os.path.expanduser("~/.modular")) + # /Users/username/.modular + print(os.path.expanduser("~root/folder")) + # /var/root/folder (on macos) + # /root/folder (on linux) + ``` - ```mojo - new_ptr = ptr + 1 - ``` + - [`os.path.split()`](/mojo/stdlib/os/path/path/split) has been added for + splitting a path into `head, tail`: -- `UnsafePointer` has a new - `exclusive: Bool = False` parameter. Setting this parameter to true tells the - compiler that the user knows this pointer and all those derived from it have - exclusive access to the underlying memory allocation. The compiler is not - guaranteed to do anything with this information. + ```mojo + import os + head, tail = os.path.split("/this/is/head/tail") + print("head:", head) + print("tail:", tail) + # head: /this/is/head + # tail: tail + ``` -- It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the - `UnsafePointer.address_of(someRef[])` which makes the code explicit that the - `UnsafePointer` gets the address of what the reference points to. + - [`os.makedirs()`](/mojo/stdlib/os/os/makedirs) and + [`os.removedirs()`](/mojo/stdlib/os/os/removedirs) have been added for + creating and removing nested directories: -- `sort` no longer takes `LegacyPointer`. The current API supports: - - `sort(list)` just plain list - - `sort[type, cmp_fn](list)` list with custom compare function - - `sort(ptr, len)` a pointer and length (can change to Span in future) - - `sort[type, cmp_fn](ptr, len)` above with custom compare + ```mojo + import os + path = os.path.join("dir1", "dir2", "dir3") + os.path.makedirs(path, exist_ok=True) + os.path.removedirs(path) + ``` -- Continued transition to `UnsafePointer` and unsigned byte type for strings: + - The [`pwd`](/mojo/stdlib/pwd/pwd/) module has been added for accessing user + information in `/etc/passwd` on POSIX systems. This follows the same logic + as Python: - - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `UnsafePointer[Int8]`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `UnsafePointer[Int8]`) + ```mojo + import pwd + import os + current_user = pwd.getpwuid(os.getuid()) + print(current_user) -- `await` on a coroutine now consumes it. This strengthens the invariant that - coroutines can only be awaited once. + # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, + # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', + # pw_shell='/bin/zsh') -- `print()` now requires that its arguments conform to the `Formattable` trait. - This enables efficient stream-based writing by default, avoiding unnecessary - intermediate String heap allocations. + print(current_user.pw_uid) - Previously, `print()` required types conform to `Stringable`. This meant that - to execute a call like `print(a, b, c)`, at least three separate String heap - allocations were down, to hold the formatted values of `a`, `b`, and `c` - respectively. The total number of allocations could be much higher if, for - example, `a.__str__()` was implemented to concatenate together the fields of - `a`, like in the following example: + # 501 - ```mojo - struct Point(Stringable): - var x: Float64 - var y: Float64 + root = pwd.getpwnam("root") + print(root) - fn __str__(self) -> String: - # Performs 3 allocations: 1 each for str(..) of each of the fields, - # and then the final returned `String` allocation. - return "(" + str(self.x) + ", " + str(self.y) + ")" - ``` + # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, + # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') + ``` - A type like the one above can transition to additionally implementing - `Formattable` with the following changes: +- Other new traits and related features: - ```mojo - struct Point(Stringable, Formattable): - var x: Float64 - var y: Float64 + - Added the + [`ExplicitlyCopyable`](/mojo/stdlib/builtin/value/ExplicitlyCopyable) trait + to mark types that can be copied explicitly, but which might not be + implicitly copyable. - fn __str__(self) -> String: - return String.format_sequence(self) + This supports work to transition the standard library collection types away + from implicit copyability, which can lead to unintended expensive copies. - fn format_to(self, inout writer: Formatter): - writer.write("(", self.x, ", ", self.y, ")") - ``` + - Added the [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable) + trait, used to describe types that implement the `__is__()` and + `__isnot__()` trait methods. + ([PR #2807](https://github.com/modularml/mojo/pull/2807)) - In the example above, `String.format_sequence()` is used to construct a - `String` from a type that implements `Formattable`. This pattern of - implementing a type's `Stringable` implementation in terms of its `Formattable` - implementation minimizes boilerplate and duplicated code, while retaining - backwards compatibility with the requirements of the commonly used `str(..)` - function. + - Types conforming to [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) (that + is, those implementing `__bool__()`) no longer implicitly convert to `Bool`. + A new [`ImplicitlyBoolable`](/mojo/stdlib/builtin/bool/ImplicitlyBoolable) + trait is introduced for types where this behavior is desired. - +- Miscellaneous: - > [!WARNING] - > The error shown when passing a type that does not implement `Formattable` to - > `print()` is currently not entirely descriptive of the underlying cause: - > - > ```shell - > error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified - > print(point) - > ~~~~~^~~~~~~ - > ``` - > - > If the above error is seen, ensure that all argument types implement - > `Formattable`. + - [`NoneType`](/mojo/stdlib/builtin/none/NoneType) is now a normal standard + library type, and not an alias for a raw MLIR type. -- `debug_assert()` now also requires that its `message` argument conform to - `Formattable`. + Function signatures spelled as `fn() -> NoneType` should transition to + being written as `fn() -> None`. -- The `StringRef` constructors from `DTypePointer.int8` have been changed to - take a `UnsafePointer[C_char]`, reflecting their use for compatibility with - C APIs. + - Mojo now has a [`UInt`](/mojo/stdlib/builtin/uint/UInt) type for modeling + unsigned (scalar) integers with a platform-dependent width. `UInt` + implements most arithmetic operations that make sense for integers, with the + notable exception of `__neg__()`. Builtin functions such as `min()`/`max()`, + as well as `math` functions like `ceildiv()`, `align_down()`, and + `align_up()` are also implemented for `UInt`. -- `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been - removed since a Slice with no end is now represented by an empty `Slice.end` - option. - ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) + - Now that we have a `UInt` type, use this to represent the return type of a + hash. In general, hashes should be an unsigned integer, and can also lead to + improved performance in certain cases. - ```mojo - var s = Slice(1, None, 2) - print(s.start.value()) # must retrieve the value from the optional - ``` + - Added the [`C_char`](/mojo/stdlib/sys/ffi/#aliases) type alias in `sys.ffi`. -- `NoneType` is now a normal standard library type, and not an alias for a raw - MLIR type. + - [`sort()`](/mojo/stdlib/builtin/sort/sort) now supports a `stable` + parameter. It can be called by - Function signatures spelled as `fn(...) -> NoneType` should transition to - being written as `fn(...) -> None`. + ```mojo + sort[cmp_fn, stable=True](list) + ``` -- Accessing local Python modules with `Python.add_to_path(".")` is no longer - required, it now behaves the same as Python, you can access modules in the - same folder as the target file: + The algorithm requires $$O(N)$$ auxiliary memory. If extra memory allocation + fails, the program crashs. - - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` + - `sort()` no longer takes `LegacyPointer` since that type is now removed. -- The rank argument for `algorihtm.elementwise` is no longer required and is - only inferred. + - Added the [`oct()`](/mojo/stdlib/builtin/format_int/oct) builtin function + for formatting an integer in octal. + ([PR #2914](https://github.com/modularml/mojo/pull/2914)) -- The `ulp` function in `numerics` has been moved to the `math` module. + - Added the [`assert_is()`](/mojo/stdlib/testing/testing/assert_is) and + [`assert_is_not()`](/mojo/stdlib/testing/testing/assert_is_not) test + functions to the `testing` module. -- The Mojo Language Server no longer sets `.` as a commit character for - auto-completion. + - The [`math`](/mojo/stdlib/math/constants/) package now includes the `pi`, + `e`, and `tau` constants (Closes Issue + [#2135](https://github.com/modularml/mojo/issues/2135)). -- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer - implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced - for types where this behavior is desired. + - The [`ulp`](/mojo/stdlib/math/math/ulp) function from `numerics` has been + moved to the `math` module. -- The `time.now()` function has been deprecated. Please use `time.perf_counter` - or `time.perf_counter_ns` instead. + - `bit` module now supports + [`bit_reverse()`](/mojo/stdlib/bit/bit/bit_reverse), + [`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap), and + [`pop_count()`](/mojo/stdlib/bit/bit/pop_count) for the `Int` type. + ([PR #3150](https://github.com/modularml/mojo/pull/3150)) -- A few bit functions have been renamed for clarity: -- `countl_zero` -> `count_leading_zeros` -- `countr_zero` -> `count_trailing_zeros` + - A few `bit` functions have been renamed for clarity: -- Now that we have a `UInt` type, use this to represent the return type of hash. - In general, hashes should be an unsigned integer, and can also lead to improved - performance in certain cases. + - `countl_zero()` -> [`count_leading_zeros()`](/mojo/stdlib/bit/bit/count_leading_zeros) -- The `atol` function now correctly supports leading underscores, - (e.g.`atol("0x_ff", 0)`), when the appropriate base is specified or inferred - (base 0). non-base-10 integer literals as per Python's [Integer Literals](\ - ). - ([PR #3180](https://github.com/modularml/mojo/pull/3180) - by [@jjvraw](https://github.com/jjvraw)) + - `countr_zero()` -> [`count_trailing_zeros()`](/mojo/stdlib/bit/bit/count_trailing_zeros) -- `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. + - [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) now uses + `OptionalReg[Int]` for `start` and `end` and implements a constructor which + accepts optional values. `Slice._has_end()` has also been removed since a + Slice with no end is now represented by an empty `Slice.end` option. + ([PR #2495](https://github.com/modularml/mojo/pull/2495)) -- `SIMD.load/store` are moved to `UnsafePointer`. + ```mojo + var s = Slice(1, None, 2) + print(s.start.value()) # must retrieve the value from the optional + ``` + + - The `rank` argument for + [`algorithm.elementwise()`](/mojo/stdlib/algorithm/functional/elementwise) + is no longer required and is only inferred. + + - The `time.now()` function has been deprecated. Please use + [`time.perf_counter()`](/mojo/stdlib/time/time/perf_counter) or + [`time.perf_counter_ns`](/mojo/stdlib/time/time/perf_counter_ns) instead. + + - [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) construction from `Bool` has been + restricted to `DType.bool` data type. + +### Tooling changes + +- [`mojo test`](/mojo/cli/test) new features and changes: + + - `mojo test` now uses the Mojo compiler for running unit tests. This will + resolve compilation issues that sometimes appeared, and will also improve + overall test times, since we will only compile unit tests once before + executing all of them. + + These changes do not apply to doctests, due to their different semantics. + + - The `mojo test` command now accepts a `--filter` option that will narrow the + set of tests collected and executed. The filter string is a POSIX extended + regular expression. + + - The `mojo test` command now supports using the same compilation options as + `mojo build`. + + - You can now debug unit tests using `mojo test` by passing the `--debug` + flag. Most debug flags are supported; run `mojo test --help` for a full + listing. + + Debugging doctests is not currently supported. + +- Mojo debugger new features and changes: + + - The `mojo debug --rpc` command has been renamed to [`mojo debug + --vscode`](/mojo/cli/debug#debug-server-options), which is now able to + manage multiple VS Code windows. -- `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` - are removed from prelude. + - The Mojo debugger now supports a `break-on-raise` command that indicated the + debugger to stop at any `raise` statements. A similar features has been + added to the debugger on VS Code. -- The `simd_strided_load()` and `simd_strided_store()` have been renamed to - `strided_load` and `strided_store` in `UnsafePointer`. + - The Mojo debugger now hides the artificial function arguments `__result__` + and `__error__` created by the compiler for Mojo code. -- `mojo test` now uses the Mojo compiler for running unit tests. This will resolve - compilation issues that sometimes appeared, and will also improve overall test - times, since we will only compile unit tests once before executing all of them. +- VS Code support changes: - These changes do not apply to doctests, due to their different semantics. + - The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo + features, including the Mojo Language Server, the Mojo debugger, the Mojo + formatter, and more. -- The `mojo debug --rpc` command has been renamed to `mojo debug --vscode`, - which is now able to manage multiple VS Code windows. + - A proxy has been added to the Mojo Language Server on VS Code that handles + crashes more gracefully. + +- The Mojo Language Server no longer sets `.` as a commit character for + auto-completion. ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from the compiler, please switch to using `fn __init__(inout self, ...):` instead. +- The builtin `tensor` module has been removed. Identical functionality is + available in [`max.tensor`](/max/api/mojo/tensor/tensor), but it is generally + recommended to use structs from the [`buffer`](/mojo/stdlib/buffer/buffer) + module when possible instead. + - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. - Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for `SIMD` instead. -- The builtin `tensor` module has been removed. Identical functionality is - available in `max.tensor`, but it is generally recommended to use `buffer` - when possible instead. - -- Removed the Mojo Language Server warnings for unused function arguments. +- Removed the `SIMD.{add,mul,sub}_with_overflow()` methods. -- Removed the `SIMD.{add,mul,sub}_with_overflow` methods. +- Removed the `SIMD.min()` and `SIMD.max()` methods. Identical functionality is + available using the builtin [`min()`](/mojo/stdlib/builtin/math/min) and + [`max()`](/mojo/stdlib/builtin/math/max) functions. -- Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is - available using the builting `min` and `max` functions. +- Removed the Mojo Language Server warnings for unused function arguments. - `Run Mojo File in Dedicated Terminal` action has been removed, and the action `Run Mojo File` will always open a dedicated terminal for each mojo @@ -880,11 +994,24 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated references to `let` in REPL documentation. -- The VS Code extension doesn't cache anymore the information of the selected +- The VS Code extension no longer caches the information of the selected MAX SDK, which was causing issues upon changes in the SDK. - The Mojo debugger now stops showing spurious warnings when parsing closures. +### Special thanks + +Special thanks to our community contributors: +[@jjvraw](https://github.com/jjvraw), +[@artemiogr97](https://github.com/artemiogr97), +[@martinvuyk](https://github.com/martinvuyk), +[@jayzhan211](https://github.com/jayzhan211), +[@bgreni](https://github.com/bgreni), [@mzaks](https://github.com/mzaks), +[@msaelices](https://github.com/msaelices), +[@rd4com](https://github.com/rd4com), [@jiex-liu](https://github.com/jiex-liu), +[@kszucs](https://github.com/kszucs), +[@thatstoasty](https://github.com/thatstoasty) + ## v24.4 (2024-06-07) ### ✨ Highlights @@ -1187,31 +1314,31 @@ Big themes for this release: types such as `Bencher` which provides the ability to execute a `Benchmark` and allows for benchmarking configuration via the `BenchmarkConfig` struct. -- [`String`](/mojo/stdlib/builtin/string/String) and friends: +- [`String`](/mojo/stdlib/collections/string/String) and friends: - **Breaking.** Implicit conversion to `String` is now removed for builtin classes/types. Use [`str()`](/mojo/stdlib/builtin/str/str) explicitly to convert to `String`. - - Added [`String.isspace()`](/mojo/stdlib/builtin/string/String#isspace) + - Added [`String.isspace()`](/mojo/stdlib/collections/string/String#isspace) method conformant with Python's universal separators. This replaces the `isspace()` free function from the `string` module. (If you need the old function, it is temporarily available as `_isspace()`. It now takes a `UInt8` but is otherwise unchanged.) - - [`String.split()`](/mojo/stdlib/builtin/string/String#split) now defaults to - whitespace and has Pythonic behavior in that it removes all adjacent - whitespace by default. + - [`String.split()`](/mojo/stdlib/collections/string/String#split) now + defaults to whitespace and has Pythonic behavior in that it removes all + adjacent whitespace by default. - - [`String.strip()`](/mojo/stdlib/builtin/string/String#strip), - [`lstrip()`](/mojo/stdlib/builtin/string/String#lstrip) and - [`rstrip()`](/mojo/stdlib/builtin/string/String#rstrip) can now remove + - [`String.strip()`](/mojo/stdlib/collections/string/String#strip), + [`lstrip()`](/mojo/stdlib/collections/string/String#lstrip) and + [`rstrip()`](/mojo/stdlib/collections/string/String#rstrip) can now remove custom characters other than whitespace. In addition, there are now several useful aliases for whitespace, ASCII lower/uppercase, and so on. ([PR #2555](https://github.com/modularml/mojo/pull/2555)) - `String` now has a - [`splitlines()`](/mojo/stdlib/builtin/string/String#splitlines) method, + [`splitlines()`](/mojo/stdlib/collections/string/String#splitlines) method, which allows splitting strings at line boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) and provides an option to retain or remove the line break characters. @@ -1240,13 +1367,13 @@ Big themes for this release: points to. - Added new - [`as_string_slice()`](/mojo/stdlib/builtin/string/String#as_string_slice) + [`as_string_slice()`](/mojo/stdlib/collections/string/String#as_string_slice) methods to `String` and `StringLiteral`. - Added `StringSlice` initializer from an `UnsafePointer` and a length in bytes. - Added a new - [`as_bytes_slice()`](/mojo/stdlib/builtin/string/String#as_bytes_slice) + [`as_bytes_slice()`](/mojo/stdlib/collections/string/String#as_bytes_slice) method to `String` and `StringLiteral`, which returns a `Span` of the bytes owned by the string. @@ -1254,7 +1381,7 @@ Big themes for this release: [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and unsigned byte type for strings: - Renamed `String._as_ptr()` to - [`String.unsafe_ptr()`](/mojo/stdlib/builtin/string/String#unsafe_ptr), + [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr), and changed return type to `UnsafePointer` (was `DTypePointer`). - Renamed `StringLiteral.data()` to [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr), @@ -1288,14 +1415,16 @@ Big themes for this release: string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is specified, the string will be parsed as if it was an integer literal, with the base determined by whether the string contains the prefix `"0x"`, - `"0o"`, or `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273), + `"0o"`, or `"0b"`. + ([PR #2273](https://github.com/modularml/mojo/pull/2273), fixes [#2274](https://github.com/modularml/mojo/issues/2274)) - Added the [`bin()`](/mojo/stdlib/builtin/format_int/bin) built-in function to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603)) + string representation. + ([PR #2603](https://github.com/modularml/mojo/pull/2603)) - - Added the [`atof()`](/mojo/stdlib/builtin/string/atof) built-in function, + - Added the [`atof()`](/mojo/stdlib/collections/string/atof) built-in function, which can convert a `String` to a `float64`. ([PR #2649](https://github.com/modularml/mojo/pull/2649)) @@ -2052,17 +2181,17 @@ Special thanks to our community contributors: initialized with all zeros. This provides an easy way to fill in the data of a tensor. -- [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and - `removesuffix()` methods. +- [`String`](/mojo/stdlib/collections/string/String) now has `removeprefix()` + and `removesuffix()` methods. ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) -- The [`ord`](/mojo/stdlib/builtin/string/ord) and - [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to +- The [`ord`](/mojo/stdlib/collections/string/ord) and + [`chr`](/mojo/stdlib/collections/string/chr) functions have been improved to accept any Unicode character. ([@mzaks](https://github.com/mzaks), contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) -- [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The +- [`atol()`](/mojo/stdlib/collections/string/atol) now handles whitespace. The `atol()`function is used internally by `String.__int__()`, so `int(String( " 10 "))` now returns `10` instead of raising an error. ([@artemiogr97](https://github.com/artemiogr97)) @@ -3134,7 +3263,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. now returns a [`Reference`](/mojo/stdlib/memory/reference/Reference) to the value rather than a copy. -- The [`String`](/mojo/stdlib/builtin/string/String) methods `tolower()` +- The [`String`](/mojo/stdlib/collections/string/String) methods `tolower()` and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. @@ -3629,9 +3758,9 @@ experience without dedicated sugar. [`Movable`](/mojo/stdlib/builtin/value/Movable) and [`Copyable`](/mojo/stdlib/builtin/value/Copyable) built-in traits. -- [`String`](/mojo/stdlib/builtin/string/String) now has new - [`toupper()`](/mojo/stdlib/builtin/string/String#upper) and - [`tolower()`](/mojo/stdlib/builtin/string/String#lower) methods analogous, +- [`String`](/mojo/stdlib/collections/string/String) now has new + [`toupper()`](/mojo/stdlib/collections/string/String#upper) and + [`tolower()`](/mojo/stdlib/collections/string/String#lower) methods analogous, respectively, to Python's `str.toupper()` and `str.tolower()`. - Added a [`hash()`](/mojo/stdlib/builtin/hash/hash) built-in function and @@ -3975,11 +4104,11 @@ the previous "read to EOF" behavior when size is negative. - `file.FileHandle` now has a `seek()` method. -- [`String`](/mojo/stdlib/builtin/string/String) now has an - [`rfind()`](/mojo/stdlib/builtin/string/String#rfind) method analogous to +- [`String`](/mojo/stdlib/collections/string/String) now has an + [`rfind()`](/mojo/stdlib/collections/string/String#rfind) method analogous to Python's `str.rfind()`. -- `String` now has an [`split()`](/mojo/stdlib/builtin/string/String#split) +- `String` now has an [`split()`](/mojo/stdlib/collections/string/String#split) method analogous to Python's `str.split()`. - [`Path`](/mojo/stdlib/pathlib/path/Path) now has a @@ -4287,7 +4416,7 @@ the previous "read to EOF" behavior when size is negative. and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. -- The [`String`](/mojo/stdlib/builtin/string/String) type now has the +- The [`String`](/mojo/stdlib/collections/string/String) type now has the `count()` and `find()` methods to enable counting the number of occurrences or finding the offset index of a substring in a string. From 5a3df1e82158a7f0d2f9801423c71d174aa777ec Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 5 Sep 2024 15:22:44 -0700 Subject: [PATCH 1465/2019] [cherrypick] Bulk cherrypick of Magic doc changes released with 24.4 (#46682) MODULAR_ORIG_COMMIT_REV_ID: c58650f3004fedd9e3ef4d58ba9a07d52769c95a --- docs/manual/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 08dcbf6503..363fef9d10 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -236,7 +236,7 @@ cd hello-world ``` ```sh -magic add "max=*" +magic add max ``` ```sh From 830dbfe3e0d838817765e2dfdf206f97e6620bb3 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:50:06 -0700 Subject: [PATCH 1466/2019] In v24.5, the `String` type was moved from the `builtin.string` module to the `collections.string` module. This PR updates all links to the String API reference that appear in the documentation. Additionally, The `load()` and `store()` methods from `SIMD` were moved to `UnsafePointer`. This PR updates the Pointers section of the documentation to reflect this change. Finally, this highlights in the changelog that Python 3.12 is now supported. MODULAR_ORIG_COMMIT_REV_ID: 35dab7fb970330ac46063f695e01b51b9e59d72b --- docs/changelog-released.md | 4 ++++ docs/manual/lifecycle/index.ipynb | 2 +- docs/manual/lifecycle/life.ipynb | 2 +- docs/manual/pointers.ipynb | 13 +++++-------- docs/manual/types.ipynb | 8 ++++---- stdlib/src/builtin/str.mojo | 4 ++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 126ea36a88..f525e2dd2f 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -70,6 +70,8 @@ tool beyond the 24.5 release. You must now use [Magic](/magic/) or ### ✨ Highlights +- Mojo now supports Python 3.12 interoperability. + - The set of automatically imported entities (types, aliases, functions) into users' Mojo programs has been dramatically reduced. This can break existing user code as users will need to explicitly import what they're using for cases @@ -633,6 +635,8 @@ tool beyond the 24.5 release. You must now use [Magic](/magic/) or - Python interoperability changes: + - Mojo now supports Python 3.12 interoperability. + - Creating a nested [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) from a list or tuple of Python objects is possible now: diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 39f645eca4..fca51f7fbc 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -30,7 +30,7 @@ "Mojo also has no built-in data types with special privileges. All data types\n", "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", + "[`String`](/mojo/stdlib/collections/string/String)) are implemented as\n", "[structs](/mojo/manual/structs). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index c6140e7ef3..d6f4f860fc 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -28,7 +28,7 @@ "All data types in Mojo—including basic types in the standard library such as\n", "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", + "[`String`](/mojo/stdlib/collections/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", "[struct](/mojo/manual/structs). This means the creation and\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index a302461d66..4c8f74c4f3 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -620,14 +620,11 @@ "source": [ "## Working with SIMD vectors\n", "\n", - "The standard library provides a few special methods for loading and storing SIMD\n", - "values. The `SIMD` type includes static \n", - "[`load()`](/mojo/stdlib/builtin/simd/SIMD#load) and \n", - "[`store()`](/mojo/stdlib/builtin/simd/SIMD#store) methods for performing aligned\n", - "loads and stores of scalar values.\n", - "\n", - "The `UnsafePointer` type includes various methods of loading and storing SIMD\n", - "values to memory. In particular: strided load/store and gather/scatter.\n", + "The `UnsafePointer` type includes\n", + "[`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and\n", + "[`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods for\n", + "performing aligned loads and stores of scalar values. It also has methods\n", + "supporting strided load/store and gather/scatter.\n", "\n", "Strided load loads values from memory into a SIMD vector using an offset (the\n", "\"stride\") between successive memory addresses. This can be useful for\n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index b8630ad8d7..94690bd6d1 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -32,10 +32,10 @@ "\n", "Mojo comes with a standard library that provides a number of useful types and\n", "utility functions. These standard types aren’t privileged. Each of the standard\n", - "library types is defined just like user-defined types—even basic types like \n", - "[`Int`](/mojo/stdlib/builtin/int/Int) and \n", - "[`String`](/mojo/stdlib/builtin/string/String). But these standard library types\n", - "are the building blocks you'll use for most Mojo programs.\n", + "library types is defined just like user-defined types—even basic types like\n", + "[`Int`](/mojo/stdlib/builtin/int/Int) and\n", + "[`String`](/mojo/stdlib/collections/string/String). But these standard library\n", + "types are the building blocks you'll use for most Mojo programs.\n", "\n", "The most common types are _built-in types_, which are always available and\n", "don't need to be imported. These include types for numeric values, strings,\n", diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index d5eea357af..93f1482cf2 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -23,7 +23,7 @@ These are Mojo built-ins, so you don't need to import them. trait Stringable: """ The `Stringable` trait describes a type that can be converted to a - [`String`](/mojo/stdlib/builtin/string/String). + [`String`](/mojo/stdlib/collections/string/String). Any type that conforms to `Stringable` or [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) works @@ -77,7 +77,7 @@ trait Stringable: trait StringableRaising: """The StringableRaising trait describes a type that can be converted to a - [`String`](/mojo/stdlib/builtin/string/String). + [`String`](/mojo/stdlib/collections/string/String). Any type that conforms to [`Stringable`](/mojo/stdlib/builtin/str/Stringable) or From 09527d49193572b382b3133258f399303efc8b8a Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 9 Sep 2024 12:19:47 -0700 Subject: [PATCH 1467/2019] Simplify the changelog intro to emphasize the switch to Magic MODULAR_ORIG_COMMIT_REV_ID: 1db37dabe42b5621eb8c475d8971f2977a23efcd --- docs/changelog-released.md | 63 +++++--------------------------------- 1 file changed, 8 insertions(+), 55 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index f525e2dd2f..811a2d885b 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -5,67 +5,20 @@ description: A history of significant Mojo changes. toc_max_heading_level: 2 --- -This is a running list of significant changes for the Mojo language and tools. -It doesn't include all internal implementation changes. +This is a list of changes to the Mojo language, standard library, and tools. -## Update Mojo +To check your current version, run `mojo --version`, and then [update Mojo with +`magic`](/magic#update-max-and-mojo). -If you don't have Mojo yet, see the [get started -guide](/mojo/manual/get-started). +:::caution Switch to Magic -:::caution - -[Magic](/magic) is the preferred package manager and virtual environment manager -for MAX and Mojo projects. [conda](/magic/conda) is supported as an alternative. - -The legacy [`modular`](/cli) CLI is now deprecated. We will not release any new -`max` or `mojo` packages through the `modular` tool beyond the 24.5 release. You -must now use [Magic](/magic) or [conda](/magic/conda) to install MAX and Mojo. +The `modular` command-line tool is deprecated (see [how to uninstall +it](/max/faq#if-you-installed-with-modular-deprecated-1)). We recommend that +you now [manage your packages with `magic`](/magic), but you can also [use +conda](/magic/conda). ::: -### Update Mojo in a Magic virtual environment - -The virtual environment for each Magic project has its own package versions. -The Mojo programming language is distributed as part of the `max` package. -Use the [`magic update`](/magic/commands#magic-update) within your project path -to update the `max` package for that project to the latest release: - -```sh -magic update max -``` - -See the [Magic](/magic) documentation for more information on managing Magic -virtual environments. - -### Update Mojo in a conda virtual environment - -Each conda virtual environment has its own package versions. -The Mojo programming language is distributed as part of the `max` package. -Use the [`conda update`](https://docs.conda.io/projects/conda/en/latest/commands/update.html) -command to update the version of the `max` package for an environment. -For example, for an environment named `max-project`, run: - -```sh -conda update -n max-project max -``` - -See the [conda](https://docs.conda.io/projects/conda/en/latest/index.html) -documentation for more information on managing conda virtual environments. - -### Update Mojo using the `modular` CLI - -If you are still using the deprecated `modular` CLI, you can update Mojo to -24.5 by running: - -```sh -modular update mojo -``` - -We will not release any new `max` or `mojo` packages through the `modular` -tool beyond the 24.5 release. You must now use [Magic](/magic/) or -[conda](/magic/conda) to install MAX and Mojo. - ## v24.5 (2024-09-10) ### ✨ Highlights From 17a89b76e092c5a47bdee3a38b913add4e577389 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Sep 2024 19:55:35 -0700 Subject: [PATCH 1468/2019] [Docs] Update pointer examples. MODULAR_ORIG_COMMIT_REV_ID: 1100013753169f882b423c6f416d6d8f8d2487e7 --- docs/manual/lifecycle/life.ipynb | 67 +++++++++++------ docs/manual/parameters/index.ipynb | 116 ++++++++++++++--------------- 2 files changed, 101 insertions(+), 82 deletions(-) diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index d6f4f860fc..4917fa8c3c 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -43,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -96,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -118,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -173,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -207,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -355,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -400,7 +400,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -424,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -438,7 +438,7 @@ " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " self.data.store(i, val)\n", + " (self.data + i).init_pointee_copy(val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", @@ -446,18 +446,20 @@ " self.cap = existing.cap\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " self.data.store(i, existing.data.load(i))\n", + " (self.data + i).init_pointee_copy(existing.data[i])\n", " # The lifetime of `existing` continues unchanged\n", "\n", " fn __del__(owned self):\n", " # We must free the heap-allocated data, but\n", " # Mojo knows how to destroy the other fields\n", + " for i in range(self.size):\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", " fn append(inout self, val: Int):\n", " # Update the array for demo purposes\n", " if self.size < self.cap:\n", - " self.data.store(self.size, val)\n", + " (self.data + self.size).init_pointee_copy(val)\n", " self.size += 1\n", " else:\n", " print(\"Out of bounds\")\n", @@ -468,7 +470,7 @@ " for i in range(self.size):\n", " if i > 0:\n", " print(\", \", end=\"\")\n", - " print(self.data.load(i), end=\"\")\n", + " print(self.data[i], end=\"\")\n", " print(\"]\")" ] }, @@ -488,7 +490,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -588,44 +590,63 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "struct HeapArray:\n", " var data: UnsafePointer[Int]\n", " var size: Int\n", + " var cap: Int\n", + "\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", + " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data.store(i, val)\n", + " (self.data + i).init_pointee_copy(val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", " self.size = existing.size\n", - " self.data = UnsafePointer[Int].alloc(self.size)\n", + " self.cap = existing.cap\n", + " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " self.data.store(i, existing.data.load(i))\n", + " (self.data + i).init_pointee_copy(existing.data[i])\n", + " # The lifetime of `existing` continues unchanged\n", "\n", " fn __moveinit__(inout self, owned existing: Self):\n", " print(\"move\")\n", " # Shallow copy the existing value\n", " self.size = existing.size\n", + " self.cap = existing.cap\n", " self.data = existing.data\n", " # Then the lifetime of `existing` ends here, but\n", " # Mojo does NOT call its destructor\n", "\n", " fn __del__(owned self):\n", + " # We must free the heap-allocated data, but\n", + " # Mojo knows how to destroy the other fields\n", + " for i in range(self.size):\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", + " fn append(inout self, val: Int):\n", + " # Update the array for demo purposes\n", + " if self.size < self.cap:\n", + " (self.data + self.size).init_pointee_copy(val)\n", + " self.size += 1\n", + " else:\n", + " print(\"Out of bounds\")\n", + "\n", " fn dump(self):\n", + " # Print the array contents for demo purposes\n", " print(\"[\", end=\"\")\n", " for i in range(self.size):\n", " if i > 0:\n", " print(\", \", end=\"\")\n", - " print(self.data.load(i), end=\"\")\n", + " print(self.data[i], end=\"\")\n", " print(\"]\")" ] }, @@ -649,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -720,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -742,7 +763,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -779,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 04a46f62ca..d098309bf9 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -139,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 52, "metadata": {}, "outputs": [ { @@ -155,7 +155,7 @@ "fn repeat[MsgType: Stringable, count: Int](msg: MsgType):\n", " @parameter\n", " for i in range(count):\n", - " print(msg)\n", + " print(str(msg))\n", "\n", "# Must use keyword parameter for `count`\n", "repeat[count=2](42)" @@ -175,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 53, "metadata": {}, "outputs": [ { @@ -191,7 +191,7 @@ "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", " @parameter\n", " for i in range(count):\n", - " print(msg)\n", + " print(str(msg))\n", "\n", "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", "repeat[2](42)" @@ -227,7 +227,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n", + "from memory.unsafe_pointer import UnsafePointer\n", "\n", "struct GenericArray[ElementType: CollectionElement]:\n", " var data: UnsafePointer[ElementType]\n", @@ -237,11 +237,11 @@ " self.size = len(elements)\n", " self.data = UnsafePointer[ElementType].alloc(self.size)\n", " for i in range(self.size):\n", - " initialize_pointee_move(self.data.offset(i), elements[i])\n", + " (self.data + i).init_pointee_move(elements[i])\n", "\n", " fn __del__(owned self):\n", " for i in range(self.size):\n", - " destroy_pointee(self.data.offset(i))\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", " fn __getitem__(self, i: Int) raises -> ref [__lifetime_of(self)] ElementType:\n", @@ -536,7 +536,7 @@ "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n", "\n", "# Make a big vector containing 1.0 in float16 format.\n", - "var big_vec = SIMD[DType.float16, 32].splat(1.0)\n", + "var big_vec = SIMD[DType.float16, 32](1.0)\n", "\n", "# Do some math and convert the elements to float32.\n", "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n", @@ -690,11 +690,11 @@ " fn __init__(inout self, one: One[Type], another: One[Type]):\n", " self.val1 = one.value\n", " self.val2 = another.value\n", - " print(self.val1, self.val2)\n", + " print(str(self.val1), str(self.val2))\n", "\n", " @staticmethod\n", " fn fire(thing1: One[Type], thing2: One[Type]):\n", - " print(\"🔥\", thing1.value, thing2.value)\n", + " print(\"🔥\", str(thing1.value), str(thing2.value))\n", "\n", "def use_two():\n", " s3 = Two(One(\"infer\"), One(\"me\"))\n", @@ -743,7 +743,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -775,7 +775,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -800,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -840,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -885,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 58, "metadata": {}, "outputs": [ { @@ -929,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -954,7 +954,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -995,7 +995,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 61, "metadata": {}, "outputs": [ { @@ -1040,7 +1040,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 62, "metadata": {}, "outputs": [ { @@ -1102,18 +1102,12 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3.1400001049041748 3.1400001049041748 3.1400001049041748 3.1400001049041748\n" - ] - } - ], + "outputs": [], "source": [ + "from memory import UnsafePointer\n", + "\n", "struct Array[T: AnyTrivialRegType]:\n", " var data: UnsafePointer[T]\n", " var size: Int\n", @@ -1122,12 +1116,14 @@ " self.size = size\n", " self.data = UnsafePointer[T].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data[i] = value\n", + " (self.data + i).init_pointee_copy(value)\n", "\n", " fn __getitem__(self, i: Int) -> T:\n", " return self.data[i]\n", "\n", " fn __del__(owned self):\n", + " for i in range(self.size):\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", "var v = Array[Float32](4, 3.14)\n", @@ -1151,7 +1147,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 67, "metadata": {}, "outputs": [], "source": [ @@ -1211,7 +1207,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -1247,10 +1243,12 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 69, "metadata": {}, "outputs": [], "source": [ + "from collections import Dict\n", + "\n", "alias StringKeyDict = Dict[String, _]\n", "var b = StringKeyDict[UInt8]()\n", "b[\"answer\"] = 42" @@ -1270,7 +1268,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 70, "metadata": {}, "outputs": [], "source": [ @@ -1367,7 +1365,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 71, "metadata": {}, "outputs": [ { @@ -1403,7 +1401,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ @@ -1434,7 +1432,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -1451,7 +1449,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -1472,7 +1470,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 75, "metadata": {}, "outputs": [ { @@ -1519,15 +1517,15 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 80, "metadata": {}, "outputs": [], "source": [ "@value\n", "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", " fn __str__(self) -> String:\n", - " var values = StaticIntTuple[3](sugar, cream, chocolate)\n", - " return str(\"Fudge\") + str(values)" + " return str(\"Fudge (\") + str(sugar) + \",\" +\n", + " str(cream) + \",\" + str(chocolate) + \")\"\n" ] }, { @@ -1540,7 +1538,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -1562,12 +1560,12 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]):\n", - " print(\"Ate \" + str(f))" + " print(\"Ate\", str(f))" ] }, { @@ -1580,15 +1578,15 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 87, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Ate Fudge(5, 5, 7)\n", - "Ate Fudge(5, 8, 9)\n" + "Ate Fudge (5,5,7)\n", + "Ate Fudge (5,8,9)\n" ] } ], @@ -1620,12 +1618,12 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 91, "metadata": {}, "outputs": [], "source": [ "fn devour(f: Fudge[_, 6, _]):\n", - " print(str(\"Devoured \") + str(f))" + " print(\"Devoured\", str(f))" ] }, { @@ -1640,12 +1638,12 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(str(\"Devoured \") + str(f))" + " print(\"Devoured\", str(f))" ] }, { @@ -1661,12 +1659,12 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "fn devour(f: Fudge[_, chocolate=_, cream=6]):\n", - " print(str(\"Devoured \") + str(f))" + " print(\"Devoured\", str(f))" ] }, { @@ -1678,15 +1676,15 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 94, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Devoured Fudge(3, 6, 9)\n", - "Devoured Fudge(4, 6, 8)\n" + "Devoured Fudge (3,6,9)\n", + "Devoured Fudge (4,6,8)\n" ] } ], @@ -1707,12 +1705,12 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "fn nibble(f: Fudge[5]):\n", - " print(\"Ate \" + str(f))" + " print(\"Ate\", str(f))" ] }, { From 4b1c14c8731510e93d1ebcccb60cc02c1fb706a6 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Sep 2024 22:16:06 -0700 Subject: [PATCH 1469/2019] [Docs] Ownership updates. Includes argument exclusivity, new section on lifetimes, and `ref` args and return values. Also renaming transfer operator -> sigil and other cleanup. MODULAR_ORIG_COMMIT_REV_ID: a6dcfaf195967a95ce5ff4e6c32b7043f403a9fb --- docs/manual/decorators/staticmethod.ipynb | 8 +- docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/lifecycle/index.ipynb | 28 +- docs/manual/lifecycle/life.ipynb | 8 +- docs/manual/pointers.ipynb | 2 +- docs/manual/values/index.ipynb | 61 ++- docs/manual/values/lifetimes.ipynb | 559 ++++++++++++++++++++++ docs/manual/values/ownership.ipynb | 246 +++++++--- docs/manual/values/value-semantics.ipynb | 19 +- docs/roadmap.md | 2 +- 10 files changed, 824 insertions(+), 111 deletions(-) create mode 100644 docs/manual/values/lifetimes.ipynb diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index f0c0def9a8..84ff19d90d 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -27,19 +27,19 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "from max.tensor import Tensor\n", + "from collections import List\n", "from pathlib import Path\n", "\n", "\n", "struct MyStruct:\n", - " var data: Tensor[DType.int8]\n", + " var data: List[UInt8]\n", "\n", " fn __init__(inout self):\n", - " self.data = Tensor[DType.int8]()\n", + " self.data = List[UInt8]()\n", "\n", " fn __moveinit__(inout self, owned existing: Self):\n", " self.data = existing.data ^\n", diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 91913f32aa..1440d8fbb8 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -332,7 +332,7 @@ "source": [ "The `pet.name` field is destroyed after the first `print()`, because Mojo knows\n", "that it will be overwritten below. You can also see this behavior when using the\n", - "transfer operator:" + "transfer sigil:" ] }, { diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index fca51f7fbc..9714beaf5b 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -71,19 +71,31 @@ "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", "All values that are declared with the same type have the same lifecycle.\n", "\n", - "- The \"lifetime\" of a value is defined by the span of time during program\n", - "execution in which each value is considered valid. The life of a value begins\n", - "when it is initialized and ends when it is destroyed, which generally (but not\n", - "always) spans from `__init__()` to `__del__()`. No two values have the exact\n", - "same lifetime, because every value is created and destroyed at a different\n", - "point in time (even if the difference is imperceivable).\n", + "- The \"lifetime\" of a value is defined by the span of time during \n", + "program execution in which each value is considered valid. The life of a value \n", + "begins when it is initialized (via `__init__()`, `__copyinit__()` or \n", + "`__moveinit__()`) and ends when it is destroyed (`__del__()`), or consumed in\n", + "some other way (for example, as part of a `__moveinit__()` call). \n", + "\n", + "No two values have the exact same life span, because every value is created and \n", + "destroyed at a different point in time (even if the difference is imperceptible).\n", + "\n", + ":::note Lifetime type\n", + "\n", + "The concept of lifetimes is related to the `lifetime` type, a Mojo primitive\n", + "used to track ownership. For most Mojo programming, you won't need to work with\n", + "`lifetime` values directly. For information, see [Lifetimes and\n", + "references](/mojo/manual/values/lifetimes).\n", + "\n", + ":::\n", "\n", "The life of a value in Mojo begins when a variable is initialized and continues\n", "up until the value is last used, at which point Mojo destroys it. Mojo destroys\n", "every value/object as soon as it's no longer used, using an “as soon as\n", - "possible” (ASAP) destruction policy that runs after every sub-expression.\n", + "possible” (ASAP) destruction policy that runs after every sub-expression. The \n", + "Mojo compiler takes care of releasing resources after last use when needed.\n", "\n", - "As you might imagine, keeping track of a value's lifetime can be difficult if a\n", + "As you might imagine, keeping track of a value's life can be difficult if a\n", "value is shared across functions many times during the life of a program.\n", "However, Mojo makes this predictable partly through its [value\n", "semantics](/mojo/manual/values/value-semantics) and [value\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 4917fa8c3c..ef262ef54b 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -537,7 +537,7 @@ "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", - "operator `^`), then Mojo instead invokes the move constructor.\n", + "sigil `^`), then Mojo instead invokes the move constructor.\n", "\n", ":::" ] @@ -661,7 +661,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", + "sigil](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", @@ -698,7 +698,7 @@ "\"move-only\" by implementing `__moveinit__()` and _excluding_ `__copyinit__()`.\n", "A move-only type can be passed to other variables and passed into functions\n", "with any argument convention (`borrowed`, `inout`, and `owned`)—the only catch\n", - "is that you must use the `^` transfer operator to end the lifetime of a\n", + "is that you must use the `^` transfer sigil to end the lifetime of a\n", "move-only type when assigning it to a new variable or when passing it as an\n", "`owned` argument." ] @@ -828,7 +828,7 @@ "must take ownership to store each value. This is a useful micro-optimization\n", "and enables the use of move-only types. Trivial types like `Int` are also\n", "passed as `owned`, but because ownership doesn't mean anything for integers, we\n", - "can elide that declaration and the transfer operator (`^`) for simplicity. The\n", + "can elide that declaration and the transfer sigil (`^`) for simplicity. The\n", "transfer operator is also just a formality in this case, because, even if it's\n", "not used with `self.name = name^`, the Mojo compiler will notice that `name` is\n", "last used here and convert this assignment into a move, instead of a\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 4c8f74c4f3..9c4392c261 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -215,7 +215,7 @@ "str_ptr.init_pointee_move(my_string^)\n", "```\n", "\n", - "Note that to move the value, you usually need to add the transfer operator\n", + "Note that to move the value, you usually need to add the transfer sigil\n", "(`^`), unless the value is a [trivial\n", "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", "`Int`) or a newly-constructed, \"owned\" value:\n", diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 7fbd408e8b..0c6e34c116 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -24,7 +24,16 @@ "registers, but we won't get into that here). However, each language reads and\n", "writes data a bit differently—sometimes very differently. So in the following\n", "sections, we'll explain how Mojo manages memory in your programs and how this\n", - "affects the way you write Mojo code." + "affects the way you write Mojo code.\n", + "\n", + ":::note\n", + "\n", + "For an alternate introduction to ownership in Mojo, check out our two-part blog\n", + "post: \n", + "[What ownership is really about: a mental model approach](https://www.modular.com/blog/what-ownership-is-really-about-a-mental-model-approach), and [Deep dive into\n", + "ownership in Mojo](https://www.modular.com/blog/deep-dive-into-ownership-in-mojo).\n", + "\n", + ":::" ] }, { @@ -33,21 +42,41 @@ "source": [ "## Stack and heap overview\n", "\n", - "In general, all programming languages use a call stack the same way: When a\n", - "function is called, the compiler allocates a block of memory on the stack that\n", - "is exactly the size required to store the execution logic and _fixed-size_\n", - "local values. When another function is called, its data is likewise added to\n", - "the top of the stack. When a function is done, all its data in the stack is\n", - "destroyed so that memory becomes available for other code.\n", + "In general, all modern programming languages divide a running program's memory\n", + "into four segments:\n", + "\n", + "- Text. The compiled program.\n", + "- Data. Global data, either initialized or uninitialized.\n", + "- Stack. Local data, automatically managed during the program's runtime.\n", + "- Heap. Dynamically-allocated data, managed by the programmer.\n", + "\n", + "The text and data segments are statically sized, but the stack and heap change\n", + "size as the program runs.\n", + "\n", + "The _stack_ stores data local to the current function. When a function is\n", + "called, the program allocates a block of memory—a _stack frame_—that is exactly\n", + "the size required to store the function's data, including any _fixed-size_\n", + "local variables. When another function is called, a new stack frame is pushed\n", + "onto the top of the stack. When a function is done, its stack frame is popped\n", + "off the stack. \n", "\n", "Notice that we said only \"_fixed-size_ local values\" are stored in the stack.\n", "Dynamically-sized values that can change in size at runtime are instead\n", "stored in the heap, which is a much larger region of memory that allows for\n", - "dynamic memory access at runtime. Technically, a local variable for such a value\n", + "dynamic memory allocation. Technically, a local variable for such a value\n", "is still stored in the call stack, but its value is a fixed-size pointer to the\n", - "real value on the heap.\n", + "real value on the heap. Consider a Mojo string: it can be any length, and \n", + "its length can change at runtime. So the Mojo `String` struct includes some statically-sized fields, plus a pointer to a dynamically-allocated buffer\n", + "holding the actual string data.\n", "\n", - "Additionally, values that need to outlive the lifetime of a function (such as\n", + "Another important difference between the heap and the stack is that the stack is \n", + "managed automatically—the code to push and pop stack frames is added by the\n", + "compiler. Heap memory, on the other hand, is managed by the programmer\n", + "explicitly allocating and deallocating memory. You may do this indirectly—by\n", + "using standard library types like `List` and `String`—or directly, using the \n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) API.\n", + "\n", + "Values that need to outlive the lifetime of a function (such as\n", "an array that's passed between functions and should not be copied) are stored\n", "in the heap, because heap memory is accessible from anywhere in the call stack,\n", "even after the function that created it is removed from the stack. This sort of\n", @@ -90,11 +119,13 @@ "\n", "Mojo uses a third approach called \"ownership\" that relies on a collection of\n", "rules that programmers must follow when passing values. The rules ensure there\n", - "is only one \"owner\" for each chunk of memory at a time, and that the memory is\n", - "deallocated accordingly. In this way, Mojo automatically allocates and\n", - "deallocates heap memory for you, but it does so in a way that's deterministic\n", - "and safe from errors such as use-after-free, double-free and memory leaks. Plus,\n", - "it does so with a very low performance overhead.\n", + "is only one \"owner\" for a given value at a time. When a value's lifetime ends,\n", + "Mojo calls its destructor, which is responsible for deallocating any heap memory\n", + "that needs to be deallocated.\n", + "\n", + "In this way, Mojo helps ensure memory is freed, but it does so in a way that's\n", + "deterministic and safe from errors such as use-after-free, double-free and\n", + "memory leaks. Plus, it does so with a very low performance overhead.\n", "\n", "Mojo's value ownership model provides an excellent balance of programming\n", "productivity and strong memory safety. It only requires that you learn some new\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb new file mode 100644 index 0000000000..299323c49c --- /dev/null +++ b/docs/manual/values/lifetimes.ipynb @@ -0,0 +1,559 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Lifetimes and references\n", + "sidebar_position: 4\n", + "description: Working with lifetimes and references.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note Work in progress\n", + "\n", + "Both lifetimes and references are a work in progress and subject to change in\n", + "future releases. \n", + "\n", + ":::\n", + "\n", + "In Mojo, _lifetime_ has two meanings: \n", + "\n", + "- In general terms, a value's lifetime refers to the span of time when the \n", + " value is valid. \n", + "\n", + "- It also refers to a specific type of parameter value used to help track the \n", + " lifetimes of values and references to values. For clarity, we'll use\n", + " `lifetime` in code font to refer to the type.\n", + "\n", + "The Mojo compiler includes a lifetime checker, a compiler pass that analyzes\n", + "dataflow through your program. It identifies when variables are valid and \n", + "inserts destructor calls when a value's lifetime ends.\n", + "\n", + "The Mojo compiler uses `lifetime` values to track the validity of references.\n", + "Specifically, a `lifetime` value answers two questions:\n", + "\n", + "- What logical storage location \"owns\" this value?\n", + "- Can the value be mutated using this reference?\n", + "\n", + "For example, consider the following code:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Joan\n" + ] + } + ], + "source": [ + "fn print_str(s: String):\n", + " print(s)\n", + "\n", + "name = String(\"Joan\")\n", + "print_str(name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The line `name = String(\"Joan\")` declares a variable with an identifier (`name`)\n", + "and logical storage space for a `String` value. When you pass `name` into the\n", + "`print_str()` function, the function gets an immutable reference to the value. \n", + "So both `name` and `s` refer to the same logical storage space, and have\n", + "associated `lifetime` values that lets the Mojo compiler reason about them. \n", + "\n", + "Most of the time, `lifetime` values are handled automatically by the compiler. \n", + "However, in some cases you'll need to interact with `lifetime` values directly:\n", + "\n", + "- When working with references—specifically `ref` arguments and `ref` return\n", + " values. \n", + "\n", + "- When working with types like \n", + " [`Reference`](/mojo/stdlib/memory/reference/Reference) or \n", + " [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the \n", + " `lifetime` of the data they refer to.\n", + "\n", + "This section covers [`ref` arguments](#ref-arguments) and \n", + "[`ref` return values](#ref-return-values), which let functions\n", + "take arguments and provide return values as references with parametric\n", + "lifetimes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with lifetimes\n", + "\n", + "Mojo's `lifetime` values are unlike most other values in the language, because\n", + "they're primitive values, not Mojo structs. Specifying a parameter that takes a \n", + "`lifetime` value, you can't just say, `l: Lifetime`, because there's no \n", + "`Lifetime` type. Likewise, because these values are mostly created by the \n", + "compiler, you can't just create your own `lifetime` value—you usually need to \n", + "derive a `lifetime` from an existing value.\n", + "\n", + "### Lifetime types\n", + "\n", + "Mojo supplies a struct and a set of aliases that you can use to specify \n", + "`lifetime` types. As the names suggest, the `ImmutableLifetime` and \n", + "`MutableLifetime` aliases represent immutable and mutable lifetimes, \n", + "respectively:\n", + "\n", + "```mojo\n", + "struct ImmutableRef[lifetime: ImmutableLifetime]:\n", + " pass\n", + "```\n", + "\n", + "Or you can use the [`AnyLifetime`](mojo/stdlib/builtin/type_aliases/AnyLifetime)\n", + "struct to specify a lifetime with parametric mutability:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [], + "source": [ + "struct ParametricRef[\n", + " is_mutable: Bool,\n", + " //,\n", + " lifetime: AnyLifetime[is_mutable].type\n", + "]:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that `AnyLifetime` _isn't a lifetime value_, it's a helper for specifying a \n", + "`lifetime` **type**. Lifetime types carry the mutability of a reference as a \n", + "boolean parameter value, indicating whether the lifetime is mutable, immutable,\n", + "or even with mutability depending on a parameter specified by the enclosing API.\n", + "\n", + "The `is_mutable` parameter here is an [infer-only\n", + "parameter](/mojo/manual/parameters/#infer-only-parameters). It's never\n", + "specified directly by the user, but always inferred from context. The\n", + "`lifetime` value is often inferred, as well. For example, the following code\n", + "creates a [`Reference`](/mojo/stdlib/memory/reference/Reference) to an existing\n", + "value, but doesn't need to specify a lifetime—the `lifetime` is inferred from\n", + "the variable passed in to the reference." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [], + "source": [ + "from memory import Reference\n", + "\n", + "def use_reference():\n", + " a = 10\n", + " r = Reference(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Lifetime values\n", + "\n", + "Most `lifetime` values are created by the compiler. As a developer, there are a\n", + "few ways to specify `lifetime` values:\n", + "\n", + "- Static lifetimes. The `ImmutableStaticLifetime` and `MutableStaticLifetime`\n", + " aliases are lifetimes that last for the duration of the program. \n", + "- The `__lifetime_of()` magic function, which returns the lifetime associated\n", + " with the value (or values) passed in.\n", + "- Inferred lifetime. You can use inferred parameters to capture the lifetime\n", + " of a value passed in to a function.\n", + "\n", + "#### Static lifetimes\n", + "\n", + "You can use the static lifetimes `ImmutableStaticLifetime` and \n", + "`MutableStaticLifetime` when you have a value that should never be destroyed;\n", + "or when there's no way to construct a meaningful `lifetime` for a value.\n", + "\n", + "For an example of the first case, the `StringLiteral` method\n", + "[`as_string_slice()`](/mojo/stdlib/builtin/string_literal/StringLiteral#as_string_slice)\n", + "returns a [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) pointing\n", + "to the original string literal. String literals are static—they're allocated at\n", + "compile time and never destroyed—so the slice is created with an immutable,\n", + "static lifetime.\n", + "\n", + "Converting an\n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) into a\n", + "`Reference` is an example of the second case: the `UnsafePointer`'s data\n", + "doesn't carry a `lifetime`—one reason that it's considered unsafe—but you need\n", + "to specify a `lifetime` when creating a `Reference`. In this case, there's no\n", + "way to construct a meaningful `lifetime` value, so the new `Reference` is\n", + "constructed with a `MutableStaticLifetime`. Mojo won't destroy this value\n", + "automatically. As with any value stored using a pointer, it's up to the user to\n", + "explicitly [destroy the\n", + "value](/mojo/manual/pointers#destroying-or-removing-values).\n", + "\n", + "#### Derived lifetimes\n", + "\n", + "Use the `__lifetime_of()` magic function to obtain a value's lifetime. This can \n", + "be useful, for example, when creating a container type. Consider the `List`\n", + "type. Subscripting into a list (`list[4]`) returns a reference to the item at\n", + "the specified position. The signature of the `__getitem__()` method that's\n", + "called to return the subscripted item looks like this:\n", + "\n", + "```mojo\n", + "fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T:\n", + "```\n", + "\n", + "The syntax may be unfamiliar—`ref` arguments and `ref` return values are\n", + "described in the following sections. For now it's enough to know that \n", + "the return value is a reference of type `T` (where `T` is the element type\n", + "stored in the list), and the reference has the same lifetime as the list itself.\n", + "This means that as long as you hold the reference, the underlying list won't be\n", + "destroyed.\n", + "\n", + ":::note\n", + "\n", + "Ideally the returned reference's `lifetime` would be linked to the individual\n", + "list item, rather than the list itself. Mojo doesn't yet have a mechanism to \n", + "express this relationship.\n", + "\n", + ":::\n", + "\n", + "#### Inferred lifetimes\n", + "\n", + "The other common way to access a lifetime value is to _infer_ it from the\n", + "the arguments passed to a function or method. For example, the `Span` type\n", + "has an associated `lifetime`:\n", + "\n", + "```mojo\n", + "struct Span[\n", + " is_mutable: Bool, //,\n", + " T: CollectionElement,\n", + " lifetime: AnyLifetime[is_mutable].type,\n", + "](CollectionElementNew):\n", + " \"\"\"A non owning view of contiguous data.\n", + "```\n", + "\n", + "One of its constructors creates a `Span` from an existing `List`, and infers\n", + "its `lifetime` value from the list:\n", + "\n", + "```mojo\n", + " fn __init__(inout self, ref [lifetime]list: List[T, *_]):\n", + " \"\"\"Construct a Span from a List.\n", + "\n", + " Args:\n", + " list: The list to which the span refers.\n", + " \"\"\"\n", + " self._data = list.data\n", + " self._len = len(list)\n", + "```\n", + "\n", + "## Working with references\n", + "\n", + "You can use the `ref` keyword with arguments and return values to specify a \n", + "reference with parametric mutability. That is, they can be either mutable or \n", + "immutable.\n", + "\n", + "These references shouldn't be confused with the `Reference` type, which is\n", + "basically a safe pointer type. A `Reference` needs to be dereferenced, like a \n", + "pointer, to access the underlying value. A `ref` argument, on the other hand,\n", + "looks like a `borrowed` or `inout` argument inside the function. A `ref` return\n", + "value looks like any other return value to the calling function, but it is a\n", + "_reference_ to an existing value, not a copy.\n", + "\n", + "### `ref` arguments\n", + "\n", + "The `ref` argument convention lets you specify an argument of parametric\n", + "mutability: that is, you don't need to know in advance whether the passed\n", + "argument will be mutable or immutable. There are several reasons you might want\n", + "to use a `ref` argument:\n", + "\n", + "- You want to accept an argument with parametric mutability.\n", + "\n", + "- You want to tie the lifetime of one argument to the lifetime of another\n", + " argument.\n", + "\n", + "- When you want an argument that is guaranteed to be passed in memory: this can\n", + " be important and useful for generic arguments that need an identity,\n", + " irrespective of whether the concrete type is register passable.\n", + "\n", + "The syntax for a `ref` argument is:\n", + "\n", + "ref [lifetime] argName: argType\n", + "\n", + "The `lifetime` parameter passed inside the square brackets can be replaced with\n", + "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", + "of it as a wildcard that will accept any lifetime:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "def add_ref(ref [_] a: Int, b: Int) -> Int:\n", + " return a+b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also name the lifetime explicitly. This is useful if you want to specify\n", + "an `ImmutableLifetime` or `MutableLifetime`, or if you want to bind to\n", + "the `is_mutable` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Immutable: Hello\n", + "Mutable: Goodbye\n" + ] + } + ], + "source": [ + "def take_str_ref[\n", + " is_mutable: Bool, //,\n", + " life: AnyLifetime[is_mutable].type\n", + " ](ref [life] s: String):\n", + " @parameter\n", + " if is_mutable:\n", + " print(\"Mutable: \" + s)\n", + " else:\n", + " print(\"Immutable: \" + s)\n", + "\n", + "def pass_refs(s1: String, owned s2: String):\n", + " take_str_ref(s1)\n", + " take_str_ref(s2)\n", + "\n", + "pass_refs(\"Hello\", \"Goodbye\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `ref` return values\n", + "\n", + "Like `ref` arguments, `ref` return values allow a function to return a mutable\n", + "or immutable reference to a value. Like a `borrowed` or `inout` argument, these\n", + "references don't need to be dereferenced.\n", + "\n", + "`ref` return values can be an efficient way to handle updating items in a \n", + "collection. The standard way to do this is by implementing the `__getitem__()`\n", + "and `__setitem__()` dunder methods. These are invoked to read from and write to \n", + "a subscripted item in a collection:\n", + "\n", + "```mojo\n", + "value = list[a]\n", + "list[b] += 10\n", + "```\n", + "\n", + "With a `ref` argument, `__getitem__()` can return a mutable reference that can\n", + "be modified directly. This has pros and cons compared to using a `__setitem__()`\n", + "method:\n", + "\n", + "- The mutable reference is more efficient—a single update isn't broken up across\n", + " two methods. However, the referenced value must be in memory.\n", + " \n", + "- A `__getitem__()`/`__setitem__()` pair allows for arbitrary to be run when\n", + " values are retrieved and set. For example, `__setitem__()` can validate or \n", + " constrain input values.\n", + "\n", + "For example, in the following example, `NameList` has a `get()` method\n", + "that returns a reference: " + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dana\n", + "Dana?\n" + ] + } + ], + "source": [ + "struct NameList:\n", + " var names: List[String]\n", + "\n", + " def __init__(inout self, *names: String):\n", + " self.names = List[String]()\n", + " for name in names:\n", + " self.names.append(name[])\n", + "\n", + " def __getitem__(ref [_] self: Self, index: Int) ->\n", + " ref [__lifetime_of(self)] String:\n", + " if (index >=0 and index < len(self.names)):\n", + " return self.names[index]\n", + " else:\n", + " raise Error(\"index out of bounds\")\n", + "\n", + "def use_name_list():\n", + " list = NameList(\"Thor\", \"Athena\", \"Dana\", \"Vrinda\")\n", + " print(list[2])\n", + " list[2] += \"?\"\n", + " print(list[2])\n", + "\n", + "use_name_list()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that this update succeeds, even though `NameList` doesn't define a\n", + "`__setitem__()` method:\n", + "\n", + "```mojo\n", + "list[2] += \"?\"\n", + "```\n", + "\n", + "Also note that the code uses the return value directly each time, rather than\n", + "assigning the return value to a variable, like this:\n", + "\n", + "```mojo\n", + "name = list[2]\n", + "```\n", + "\n", + "Since a variable needs to own its value, `name` would end up with an owned \n", + "_copy_ of the value that `list[2]` returns. Mojo doesn't currently have \n", + "syntax to express that you want to keep the original reference in `name`. This\n", + "will be added in a future release.\n", + "\n", + "In cases where you need to be able to assign the return value to a variable—for\n", + "example, an iterator which will be used in a `for..in` loop—you might consider \n", + "returning a `Reference` instead of a `ref` return value. For example, see the \n", + "[iterator for the `List` \n", + "type](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L60).\n", + "You can assign a `Reference` to a variable, but you need to use the dereference\n", + "operator (`[]`) to access the underlying value." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "nums = List(1, 2, 3)\n", + "for item in nums: # List iterator returns a Reference\n", + " print(item[])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Parametric mutability of return values\n", + "\n", + "Another advantage of `ref` return arguments is the ability to support parametric\n", + "mutability. For example, recall the signature of the `__getitem__()` method\n", + "above:\n", + "\n", + "```mojo\n", + "def __getitem__(ref [_] self: Self, index: Int) ->\n", + " ref [__lifetime_of(self)] String:\n", + "```\n", + "\n", + "Since the `lifetime` of the return value is tied to the lifetime of `self`, the\n", + "returned reference will be mutable if the method was called using a\n", + "mutable reference. The method still works if you have an immutable reference\n", + "to the `NameList`, but it returns an immutable reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Diana\n" + ] + } + ], + "source": [ + "fn pass_immutable_list(list: NameList) raises:\n", + " print(list[2])\n", + " # list[2] += \"?\" # Error, this list is immutable\n", + "\n", + "def use_name_list_again():\n", + " list = NameList(\"Sophie\", \"Jack\", \"Diana\")\n", + " pass_immutable_list(list)\n", + "\n", + "use_name_list_again()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Without parametric mutability, you'd need to write two versions of \n", + "`__getitem__()`, one that accepts an immutable `self` and another that accepts\n", + "a mutable `self`. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 447c0dd528..4d9ffd533c 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -28,11 +28,14 @@ "\n", "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", "each value at a time, while still allowing you to share references with other\n", - "functions. When the lifetime of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death).\n", - "\n", - "On this page, we'll explain the rules that govern this ownership model and how\n", - "to specify different argument conventions that define how values are shared into\n", + "functions. When the life span of the owner ends, Mojo [destroys the\n", + "value](/mojo/manual/lifecycle/death). Programmers are still responsible for\n", + "making sure any type that allocates resources (including memory) also\n", + "deallocates those resources in its destructor. Mojo's ownership system ensures\n", + "that destructors are called promptly.\n", + "\n", + "On this page, we'll explain the rules that govern this ownership model, and how\n", + "to specify different argument conventions that define how values are passed into\n", "functions." ] }, @@ -40,6 +43,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "\n", "## Argument conventions\n", "\n", "In all programming languages, code quality and performance is heavily dependent\n", @@ -70,18 +74,23 @@ " function can read and mutate the original value (it is *not* a copy).\n", " \n", "- `owned`: The function takes **ownership**. This means the function has\n", - " exclusive mutable access to the argument—the function caller does not have\n", - " access to this value (anymore). Often, this also implies that the caller\n", + " exclusive ownership of the argument. Often, this also implies that the caller\n", " should transfer ownership to this function, but that's not always what\n", " happens and this might instead be a copy (as you'll learn below).\n", "\n", + "- `ref`: The function gets a reference with an associated lifetime. The\n", + " reference can be either mutable or immutable. You can think of `ref` arguments\n", + " as a generalization of the `borrowed` and `inout` conventions. `ref` arguments\n", + " are an advanced topic, and they're described in more detail in [Lifetimes and \n", + " references](/mojo/manual/values/lifetimes).\n", + "\n", "For example, this function has one argument that's a mutable\n", "reference and one that's immutable:" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -131,7 +140,7 @@ "\n", "- The `owned` argument always gets a uniquely owned value, which may have been\n", " copied or transferred from the callee. Using `owned` arguments without the \n", - " transfer operator (`^`) usually results in values being copied.\n", + " transfer sigil (`^`) usually results in values being copied.\n", "\n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." @@ -147,6 +156,7 @@ "\n", "- Every value has only one owner at a time.\n", "- When the lifetime of the owner ends, Mojo destroys the value.\n", + "- If there are outstanding references to a value, Mojo keeps the value alive.\n", "\n", "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", "that only one mutable reference to a value can exist at a time. **This is not\n", @@ -191,38 +201,42 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "256x256\n" + "[1, 2, 3, 4]\n" ] } ], "source": [ - "from max.tensor import Tensor, TensorShape\n", + "from collections import List\n", "\n", - "def print_shape(tensor: Tensor[DType.float32]):\n", - " shape = tensor.shape()\n", - " print(str(shape))\n", + "def print_list(list: List[Int]):\n", + " print(list.__str__())\n", "\n", - "var tensor = Tensor[DType.float32](256, 256)\n", - "print_shape(tensor)" + "var list = List(1, 2, 3, 4)\n", + "print_list(list)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here the `tensor` argument is borrowed and not mutated, so the `print_shape()`\n", - "function gets an immutable reference to the original `Tensor`, and doesn't do \n", - "any copying. In general, passing an immutable reference is much more efficient\n", + "Here the `list` argument to `print_list()` is borrowed and not mutated, so the \n", + "`print_list()` function gets an immutable reference to the original `List`, and\n", + "doesn't do any copying. \n", + "\n", + "In general, passing an immutable reference is much more efficient\n", "when handling large or expensive-to-copy values, because the copy constructor\n", "and destructor are not invoked for a borrow.\n", "\n", + "To avoid expensive copies, types should only be implicitly copyable if the copy\n", + "operation is inexpensive.\n", + "\n", "### Compared to C++ and Rust\n", "\n", "Mojo's borrowed argument convention is similar in some ways to passing an\n", @@ -261,59 +275,64 @@ "means any changes to the value *in*side the function are visible *out*side the\n", "function.\n", "\n", - "For example, this `mutate()` function updates the original `x` value:" + "For example, this `mutate()` function updates the original `list` value:" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2\n" + "[1, 2, 3, 4, 5]\n" ] } ], "source": [ - "def mutate(inout y: Int):\n", - " y += 1\n", + "from collections import List\n", + "\n", + "def mutate(inout l: List[Int]):\n", + " l.append(5)\n", + "\n", + "var list = List(1, 2, 3, 4)\n", "\n", - "var x = 1\n", - "mutate(x)\n", - "print(x)" + "mutate(list)\n", + "print_list(list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "That behaves like an optimized shorthand for this:" + "That behaves like an optimized replacement for this:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2\n" + "[1, 2, 3, 4, 5]\n" ] } ], "source": [ - "def mutate_copy(y: Int) -> Int:\n", - " y += 1\n", - " return y\n", + "from collections import List\n", "\n", - "var x = 1\n", - "x = mutate_copy(x)\n", - "print(x)" + "def mutate_copy(l: List[Int]) -> List[Int]:\n", + " l.append(5)\n", + " return l\n", + "\n", + "var list = List(1, 2, 3, 4)\n", + "list = mutate_copy(list)\n", + "print_list(list)" ] }, { @@ -330,15 +349,6 @@ "\n", ":::note\n", "\n", - "Notice that we don't call this argument passing \"by reference.\"\n", - "Although the `inout` convention is conceptually the same, we don't call it\n", - "by-reference passing because the implementation may actually pass values using\n", - "pointers.\n", - "\n", - ":::\n", - "\n", - ":::note\n", - "\n", "You cannot define [default\n", "values](/mojo/manual/functions#optional-arguments) for `inout`\n", "arguments.\n", @@ -346,6 +356,58 @@ ":::" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Argument exclusivity\n", + "\n", + "Mojo enforces _argument exclusivity_ for mutable references. This means that if\n", + "a function receives a mutable reference to a value (such as an `inout` argument),\n", + "it can't receive any other references to the same value—mutable or immutable.\n", + "That is, a mutable reference can't have any other references that _alias_ it.\n", + "\n", + "For example, consider the following code example:\n", + "\n", + "```mojo\n", + "fn append_twice(inout s: String, other: String):\n", + " # Mojo knows 's' and 'other' cannot be the same string.\n", + " s += other\n", + " s += other\n", + "\n", + "fn invalid_access():\n", + " var my_string = str(\"o\")\n", + "\n", + " # warning: passing `my_string` inout is invalid since it is also passed\n", + " # borrowed.\n", + " append_twice(my_string, my_string)\n", + " print(my_string)\n", + "```\n", + "\n", + "This code is confusing because the user might expect the output to be `ooo`, \n", + "but since the first addition mutates both `s` and `other`, the actual output\n", + "would be `oooo`. Enforcing exclusivity of mutable references not only prevents\n", + "coding errors, it also allows the Mojo compiler to optimize code in some cases.\n", + "\n", + "One way to avoid this issue when you do need both a mutable and an immutable \n", + "reference (or need to pass the same value to two arguments) is to make a copy:\n", + "\n", + "```mojo\n", + "fn valid_access():\n", + " var my_string = str(\"o\")\n", + " var other_string = str(my_string)\n", + " append_twice(my_string, other_string)\n", + " print(my_string)\n", + "```\n", + "\n", + ":::note Only a warning\n", + "\n", + "Aliasing a mutable reference produces a warning in v24.5. This will change to an\n", + "error in a subsequent release.\n", + "\n", + ":::" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -355,21 +417,20 @@ "And finally, if you'd like your function to receive value **ownership**, add the\n", "`owned` keyword in front of the argument name.\n", "\n", - "This convention is usually combined with use of the postfixed `^` \"transfer\"\n", - "operator on the variable that is passed into the function, which ends the\n", + "This convention is often combined with use of the postfixed `^` \"transfer\"\n", + "sigil on the variable that is passed into the function, which ends the\n", "lifetime of that variable.\n", "\n", "Technically, the `owned` keyword does not guarantee that the received value is\n", "_the original value_—it guarantees only that the function\n", - "gets unique ownership of a value (enforcing [value\n", - "semantics](/mojo/manual/values/value-semantics)). This happens in one of\n", + "gets unique ownership of a value. This happens in one of\n", "three ways:\n", "\n", - "- The caller passes the argument with the `^` transfer operator, which ends the\n", + "- The caller passes the argument with the `^` transfer sigil, which ends the\n", "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", "transferred into the function without making a copy of any heap-allocated data.\n", "\n", - "- The caller **does not** use the `^` transfer operator, in which case, the\n", + "- The caller **does not** use the `^` transfer sigil, in which case, the\n", "value is copied into the function argument and the original variable remains\n", "valid. (If the original value is not used again, the compiler may optimize away\n", "the copy and transfer the value).\n", @@ -384,18 +445,21 @@ "\n", " take(str(\"A brand-new String!\"))\n", " ```\n", - "\n", - "Regardless, when the function declares an argument as `owned`, it can be certain\n", - "that it has unique mutable access to that value. \n", - "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "For example, the following code works by making a copy of the string,\n", "because—although `take_text()` uses the `owned` convention—the caller does not\n", - "include the transfer operator:" + "include the transfer sigil:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -424,7 +488,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "However, if you add the `^` transfer operator when calling `take_text()`, the\n", + "However, if you add the `^` transfer sigil when calling `take_text()`, the\n", "compiler complains about `print(message)`, because at that point, the `message`\n", "variable is no longer initialized. That is, this version does not compile:\n", "\n", @@ -443,7 +507,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -463,6 +527,39 @@ "my_function()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Regardless of how it receives the value, when the function declares an argument\n", + "as `owned`, it can be certain that it has unique mutable access to that value. \n", + "Because the value is owned, the value is destroyed when the function \n", + "exits—unless the function transfers the value elsewhere.\n", + "\n", + "For example, in the following example, `add_to_list()` takes a string and\n", + "appends it to the list. Ownership of the string is transferred to the list, so\n", + "it's not destroyed when the function exits. On the other hand, \n", + "`consume_string()` doesn't transfer its `owned` value out, so the value is \n", + "destroyed at the end of the function." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import List\n", + "\n", + "def add_to_list(owned name: String, inout list: List[String]):\n", + " list.append(name^)\n", + " # name is uninitialized, nothing to destroy\n", + "\n", + "def consume_string(owned s: String):\n", + " print(s)\n", + " # s is destroyed here" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -470,7 +567,7 @@ ":::note\n", "\n", "Value lifetimes are not fully implemented for top-level code in\n", - "Mojo's REPL, so the transfer operator currently works as intended only when\n", + "Mojo's REPL, so the transfer sigil currently works as intended only when\n", "used inside a function.\n", "\n", ":::" @@ -482,24 +579,29 @@ "source": [ "### Transfer implementation details\n", "\n", - "In Mojo, it's important that you not conflate \"ownership transfer\" with a \"move\n", + "In Mojo, you shouldn't conflate \"ownership transfer\" with a \"move\n", "operation\"—these are not strictly the same thing. \n", "\n", - "There are multiple ways that Mojo can transfer ownership of a value without\n", - "making a copy:\n", + "There are multiple ways that Mojo can transfer ownership of a value:\n", "\n", "- If a type implements the [move\n", " constructor](/mojo/manual/lifecycle/life#move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", - " transferred into a function as an `owned` argument, _and_ the original value's\n", - " lifetime ends at the same point (with or without use of the `^` transfer\n", - " operator).\n", + " transferred into a function as an `owned` argument, _and_ the original\n", + " variable's lifetime ends at the same point (with or without use of the `^`\n", + " transfer operator).\n", + "\n", + "- If a type implements the [copy \n", + " constructor](/mojo/manual/lifecycle/life#move-constructor), `__copyinit__()`\n", + " and not `__moveinit__()`, Mojo may copy the value and destroy the old value.\n", "\n", - "- If a type hasn't implemented `__moveinit__()` Mojo may transfer ownership by\n", - " simply passing the recipient a reference to the value in the caller's stack.\n", + "- In some cases, Mojo can optimize away the move operation entirely, leaving the \n", + " value in the same memory location but updating its ownership. In these cases,\n", + " a value can be transferred without invoking either the `__copyinit__()` or \n", + " `__moveinit__()` constructors.\n", "\n", "In order for the `owned` convention to work _without_ the transfer\n", - "operator, the value type must be copyable (via `__copyinit__()`)." + "sigil, the value type must be copyable (via `__copyinit__()`)." ] }, { @@ -526,7 +628,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -542,7 +644,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This shadow copy typically adds no overhead, because references for small types\n", + "This shadow copy typically adds no overhead, because small types\n", "like `object` are cheap to copy. However, copying large types that allocate heap\n", "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", "copying large numbers of strings.)" diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index 903cabd474..fa8bf907d2 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -85,6 +85,9 @@ "both. Neither `x` nor `y` would \"own\" the value, and any variable would be\n", "allowed to reference it and mutate it.\n", "\n", + "Numeric values in Mojo are value semantic because they're trivial types, which\n", + "are cheap to copy. \n", + "\n", "Here's another example with a function:" ] }, @@ -121,6 +124,7 @@ "\n", "If you're familiar with Python, this is probably familiar so far, because the\n", "code above behaves the same in Python. However, Python is not value semantic.\n", + "\n", "It gets complicated, but let's consider a situation in which you call a Python\n", "function and pass an object with a pointer to a heap-allocated value. Python\n", "actually gives that function a reference to your object, which allows the\n", @@ -144,7 +148,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -171,7 +175,12 @@ "metadata": {}, "source": [ "If this were Python code, the function would modify the original object, because\n", - "Python shares a reference to the original object." + "Python shares a reference to the original object.\n", + "\n", + "However, not all types are inexpensive to copy. Copying a `String` or `List`\n", + "requires allocating heap memory, so we want to avoid copying one by accident.\n", + "When designing a type like this, ideally you want to prevent _implicit_ copies,\n", + "and only make a copy when it's explicitly requested. " ] }, { @@ -201,7 +210,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -301,7 +310,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -337,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [ { diff --git a/docs/roadmap.md b/docs/roadmap.md index 31529aae5e..028f12fd7c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -636,7 +636,7 @@ print(type(i or s)) # prints ``` In Mojo, given the expression `(a or b)`, the compiler needs to statically -determine a result type that the types of `a` and `b` can both be converted to. +determine a result type that the types of `a` and `b` can both be **converted** to. For example, currently an `Int` can be implicitly converted to a `String`, but a `String` can't be implicitly converted to an `Int`. So given an integer value From 8c7ec408b6cfc80fcf5b228239177d9b71de7a31 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:34:10 -0700 Subject: [PATCH 1470/2019] Documented how to use error breakpoints in VS Code MODULAR_ORIG_COMMIT_REV_ID: c9269750aff3cd53aa2c4bd7c3a3987505433cb0 --- docs/tools/debugging.ipynb | 20 +++++++++++++------- docs/tools/images/break-on-raise.png | Bin 0 -> 341319 bytes 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 docs/tools/images/break-on-raise.png diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 778cf85631..4f4a62bf31 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -248,11 +248,6 @@ "```" ] }, - { - "cell_type": "raw", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -402,6 +397,8 @@ "[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints), and\n", "[triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), \n", "as described in the VS Code documentation.\n", + "The Mojo debugger also supports _error breakpoints_ (also known as \"break on\n", + "raise\"), which break whenever a `raise` statement is executed.\n", "\n", "When debugging Mojo code, the debugger doesn't support function breakpoints,\n", "data breakpoints, or conditional breakpoints based on an expression (it does \n", @@ -438,7 +435,16 @@ "- Right-click on the breakpoint and select **Edit Condition**, or,\n", "- Click the **Edit Condition** icon next to the breakpoint.\n", "\n", - "This brings up the same menu, **next to the breakpoint in the editor tab**." + "This brings up the same menu, **next to the breakpoint in the editor tab**.\n", + "\n", + "#### Enable error breakpoints\n", + "\n", + "You can enable and disable error breakpoints in VS Code by selecting \"Mojo\n", + "Raise\" in the **Breakpoints** section of the **Run and Debug** view. If enabled\n", + "during debugging, executing a `raise` statement causes the debugger to stop\n", + "execution and highlight the line of code where the error was raised.\n", + "\n", + "![VS Code window showing a program paused in the debugger with the Run and Debug view visible. The program is paused at a raise statement.](images/break-on-raise.png)" ] }, { @@ -541,7 +547,7 @@ "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", "triggers the debugger.\n", "\n", - "::: note Assertions\n", + ":::note Assertions\n", "\n", "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", "ways to specify assertions. Assertions also trigger an error, so can open the \n", diff --git a/docs/tools/images/break-on-raise.png b/docs/tools/images/break-on-raise.png new file mode 100644 index 0000000000000000000000000000000000000000..fd80ab13cc2ee1e261f02652606bc913583984e1 GIT binary patch literal 341319 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

    Ed4>_jq{m#8BGiu@($ZGKO>{~V=C7s#s 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 z zLH8C;n45&fzJW}EA7&9OWbl?={qKy2q=#|m>>Jo?qSCY|UX;@b4)2N)BSR00mM5!+ z#))x78egpIn;QRm`X?&BO6 z#$@8`!jJ&a?$?T-+4>(5hS9!MXG%lxnrK&g^ibXN+WP0aQ;E*EjeV?+7|c24nNdYYkM~_{(!~(P56Df|Bsedhl_qymX^HwncM;GE#(cJM^A|ngh_|-^A0#p zD)4*%^gM_3`CCZrx=il0xZ2;njLU6L*OOc|f=Ysq?%5Gt&w{a#xG`h|yrER%6Q||B z=zOG~r?*GAtdMQ%_oVEs#XDkLmEKH`<_3YR^qUiW9+o&GL$dWX5U8%`9*&UkFS6C)%3{q)8+=R6s~r#8rZv{1$EHXgq)xzm zafb%i2Gt`8rU`DKdb!VuWZlKd6Grzc;rxNlXGB29dXbKzR!1HD<(&Hg`G~(X_i6)H zR#nn#ewfg83HeA1zBaQb7|CLlsxKc dp_xGLO&O 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ǫCkFu-@r)NUvTggrYu^^5NU~zN{abTl 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 literal 0 HcmV?d00001 From 9b5fa95082bc8d0a07d8287330fd819b24c7307e Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 11 Sep 2024 17:04:20 -0500 Subject: [PATCH 1471/2019] [stdlib] cleanup: Rename utils._format to utils.format + polish docs for release This fills in some of the doc comments in `format.mojo` to pass linting and adds an example to `Formattable` showing how to implement it for a struct. This also makes a minor clarification to the language in roadmap.md to avoid misleading readers into thinking Mojo's `print()` works identically to Python's `print`, per callout from Joe. MODULAR_ORIG_COMMIT_REV_ID: 78a65307daca3be0233c0ceed98e967fb25513f5 --- docs/roadmap.md | 6 +-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/collections/string.mojo | 12 ++++- stdlib/src/prelude/__init__.mojo | 3 +- stdlib/src/utils/__init__.mojo | 1 + .../src/utils/{_format.mojo => format.mojo} | 54 +++++++++++++++++-- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/test/utils/test_format.mojo | 2 +- stdlib/test/utils/test_format_to_stdout.mojo | 2 +- 11 files changed, 72 insertions(+), 16 deletions(-) rename stdlib/src/utils/{_format.mojo => format.mojo} (78%) diff --git a/docs/roadmap.md b/docs/roadmap.md index 028f12fd7c..80c7422f64 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -504,9 +504,9 @@ class One: print(One()) # prints '1' ``` -Mojo currently supports this feature through the -[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, so that -`print()` works on all `Stringable` types. Similar support exists for the +Mojo currently supports similar functionality through the +[`Formattable`](/mojo/stdlib/utils/format/Formattable) trait, so that +`print()` works on all `Formattable` types. Similar support exists for the [`int()`](/mojo/stdlib/builtin/int/int-function) and [`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 35ceaec387..74bf38c133 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -25,7 +25,7 @@ from collections.string import ( _calc_initial_buffer_size_int64, ) -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select from sys import triple_is_nvidia_cuda, bitwidthof diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index b8c20c605d..a08fd52eaa 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -29,7 +29,7 @@ from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, StaticString, StringSlice -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter # ===----------------------------------------------------------------------=== # # _file_handle diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 048bbd4ac1..6d0efb6a9b 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -20,7 +20,7 @@ from sys.ffi import C_char from memory import memcpy from collections import List from utils import StringRef, Span, StringSlice -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from collections.string import _atol diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index eb6cf794fd..6a39c4b6eb 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -24,8 +24,16 @@ from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy from python import PythonObject -from utils import Span, StaticIntTuple, StringRef, StringSlice, Variant -from utils._format import Formattable, Formatter, ToFormatter +from utils import ( + Span, + StaticIntTuple, + StringRef, + StringSlice, + Variant, + Formattable, + Formatter, +) +from utils.format import ToFormatter from utils.string_slice import _utf8_byte_type, _StringSliceIter # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 11c151874b..35856b1e37 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -129,8 +129,7 @@ from collections.string import ( isprintable, ) from memory import UnsafePointer, Reference, AddressSpace -from utils import StringRef -from utils._format import Formattable, Formatter +from utils import StringRef, Formattable, Formatter # Private things from builtin._documentation import doc_private diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index ec2d03c08b..4e74bd8022 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -21,3 +21,4 @@ from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock +from .format import Formatter, Formattable diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/format.mojo similarity index 78% rename from stdlib/src/utils/_format.mojo rename to stdlib/src/utils/format.mojo index 65cd2b80b7..069d465691 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/format.mojo @@ -26,6 +26,22 @@ trait Formattable: """ The `Formattable` trait describes a type that can be converted to a stream of UTF-8 encoded data by writing to a formatter object. + + Examples: + + Implement `Formattable` and `Stringable` for a type: + + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` """ fn format_to(self, inout writer: Formatter): @@ -74,11 +90,22 @@ struct Formatter: # ===------------------------------------------------------------------===# fn __init__[F: ToFormatter](inout self, inout output: F): + """Construct a new `Formatter` from a value implementing `ToFormatter`. + + Parameters: + F: The type that supports being used to back a `Formatter`. + + Args: + output: Value to accumulate or process output streamed to the `Formatter`. + """ self = output._unsafe_to_formatter() fn __init__(inout self, *, fd: FileDescriptor): """ - Constructs a formatter that writes to the given file descriptor. + Constructs a `Formatter` that writes to the given file descriptor. + + Args: + fd: The file descriptor to write to. """ @always_inline @@ -97,13 +124,24 @@ struct Formatter: func: fn (UnsafePointer[NoneType], StringRef) -> None, arg: UnsafePointer[NoneType], ): - """ - Constructs a formatter from any closure that accepts string refs. + """Constructs a formatter from any closure that accepts `StringRef`s. + + This function should only be used by low-level types that wish to + accept streamed formatted data. + + Args: + func: Raw closure function pointer. + arg: Opaque user data argument that is passed to the closure function pointer. """ self._write_func = func self._write_func_arg = arg fn __moveinit__(inout self, owned other: Self): + """Move this value. + + Args: + other: The value to move. + """ self._write_func = other._write_func self._write_func_arg = other._write_func_arg @@ -131,6 +169,12 @@ struct Formatter: fn write[*Ts: Formattable](inout self: Formatter, *args: *Ts): """Write a sequence of formattable arguments to the provided formatter. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this formatter. """ @parameter @@ -164,6 +208,10 @@ struct Formatter: fn stdout() -> Self: """ Constructs a formatter that writes directly to stdout. + + Returns: + A formatter that writes provided data to the operating system + standard output stream. """ @always_inline diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 58ab56bcfd..741bedae0f 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -23,7 +23,7 @@ from sys import sizeof from memory import UnsafePointer, memcpy from utils import StringSlice, Variant -from utils._format import ToFormatter +from utils.format import ToFormatter # ===----------------------------------------------------------------------===# # InlineString diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index e876b3082e..513f12377f 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -14,7 +14,7 @@ from testing import assert_equal -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter from utils.inline_string import _FixedString diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index 9d5a6a4d9c..f132356755 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter fn main() raises: From abb89f7633871601c45dab6ba1d206fe4be85aea Mon Sep 17 00:00:00 2001 From: Zac Bowling Date: Wed, 11 Sep 2024 15:04:47 -0700 Subject: [PATCH 1472/2019] [SDK] Update MAX SDK repo for 24.5 Update manifest files to point to the right conda repo and the new version of MAX. MODULAR_ORIG_COMMIT_REV_ID: 41934a4730fef19480f7ba90ff7ed7218c8fac2f --- examples/notebooks/pixi.toml | 4 ++-- pixi.toml | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml index 668fdf4caa..ca6b9c9502 100644 --- a/examples/notebooks/pixi.toml +++ b/examples/notebooks/pixi.toml @@ -3,11 +3,11 @@ name = "Mojo notebooks" version = "1.0.0" description = "Environment for running JupyterLab" authors = ["Modular "] -channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] +channels = ["conda-forge", "https://conda.modular.com/max"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] python = ">=3.9,<3.13" max = "*" pip = ">=24.0,<25" -jupyterlab = ">=4.2.5,<5" +jupyterlab = ">=4.2.3,<4.3" diff --git a/pixi.toml b/pixi.toml index af506b0fc5..43e10f94c6 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,13 +1,15 @@ [project] name = "Mojo" authors = ["Modular "] -channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max"] platforms = ["linux-64", "linux-aarch64", "osx-arm64"] [tasks] -tests ="./stdlib/scripts/run-tests.sh" +tests = "./stdlib/scripts/run-tests.sh" examples = "./examples/run-examples.sh" -benchmarks = { cmd = ["./stdlib/scripts/run-benchmarks.sh"], env = { MODULAR_MOJO_NIGHTLY_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo" } } +benchmarks = { cmd = [ + "./stdlib/scripts/run-benchmarks.sh", +], env = { MODULAR_MOJO_NIGHTLY_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo" } } [dependencies] python = ">=3.9,<3.13" From 1b689ba988f8014a5ade404aa35fd16faf47f3aa Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:19:41 -0700 Subject: [PATCH 1473/2019] Added a section to the Mojo "Traits" documentation to describe the Formattable trait, with an example of implementing both it and the Stringable and Representable traits. MODULAR_ORIG_COMMIT_REV_ID: e242bfc10b627374961d195ba27ac6d0618afb2e --- docs/manual/traits.ipynb | 128 +++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 20 deletions(-) diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 962de79a2e..60eb9bfb05 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -451,10 +451,11 @@ " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", " - [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable)\n", - " - [`Formattable`](/mojo/stdlib/utils/_format/Formattable)\n", + " - [`Formattable`](/mojo/stdlib/utils/format/Formattable)\n", " - [`Hashable`](/mojo/stdlib/builtin/hash/Hashable)\n", " - [`Indexer`](/mojo/stdlib/builtin/int/Indexer)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", + " - [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising)\n", " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", @@ -465,6 +466,8 @@ " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", " - [`StringableCollectionElement`](/mojo/stdlib/builtin/value/StringableCollectionElement)\n", + " - [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising)\n", + " - [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", " - [`Roundable`](/mojo/stdlib/builtin/math/Roundable)\n", " - [`ToFormatter`](/mojo/stdlib/utils/_format/ToFormatter)\n", " - [`Truncable`](/mojo/stdlib/builtin/_math/Truncable)\n", @@ -514,50 +517,135 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The `Intable` and `Stringable` traits\n", + "### The `Intable` and `IntableRaising` traits\n", "\n", - "The [`Intable`](/mojo/stdlib/builtin/int/Intable) and \n", - "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) traits identify types\n", - "that can be implicitly converted to `Int` and `String`, respectively. \n", + "The [`Intable`](/mojo/stdlib/builtin/int/Intable) trait identifies a type that\n", + "can be implicitly converted to `Int`. The\n", + "[`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) trait describes a\n", + "type can be converted to an `Int`, but the conversion might raise an error.\n", "\n", - "Any type that conforms to `Stringable` works with the built-in\n", - "[`print()`](/mojo/stdlib/builtin/io/print) and \n", - "[`str()`](/mojo/stdlib/builtin/str/str) functions:" + "Both of these traits require the type to implement the `__int__()` method. For\n", + "example:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "This is a dog named Spot\n" + "True\n" ] } ], "source": [ "@value\n", - "struct Pet(Stringable):\n", - " var name: String\n", - " var type: String\n", + "struct Foo(Intable):\n", + " var i: Int\n", "\n", - " fn __str__(self) -> String:\n", - " return \"This is a \" + self.type + \" named \" + self.name\n", + " fn __int__(self) -> Int:\n", + " return self.i\n", "\n", - "var spot = Pet(\"Spot\", \"dog\")\n", - "print(spot)" + "var foo = Foo(42)\n", + "print(int(foo) == 42)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Similarly, an `Intable` type works with the built-in \n", - "[`int`](/mojo/stdlib/builtin/int/int-function) function. You can find an example\n", - "in the [`Intable` API reference](/mojo/stdlib/builtin/int/Intable)." + "### The `Stringable`, `Representable`, and `Formattable` traits\n", + "\n", + "The [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait identifies a type\n", + "that can be implicitly converted to\n", + "[`String`](/mojo/stdlib/collections/string/String). The\n", + "[`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) trait\n", + "describes a type that can be converted to a `String`, but the conversion might\n", + "raise an error. Any type that conforms to `Stringable` or `StringableRaising`\n", + "also works with the built-in [`str()`](/mojo/stdlib/builtin/str/str) function to\n", + "explicitly return a `String`. These traits also mean that the type can support\n", + "both the `{!s}` and `{}` format specifiers of the `String` class'\n", + "[`format()`](/mojo/stdlib/collections/string/String#format) method. These traits\n", + "require the type to define the\n", + "[`__str__()`](/mojo/stdlib/builtin/str/Stringable#__str__) method.\n", + "\n", + "In contrast, the [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", + "trait that defines a type that can be used with the built-in\n", + "[`repr()`](/mojo/stdlib/builtin/repr/repr) function, as well as the `{!r}`\n", + "format specifier of the `format()` method. This trait requires the type to\n", + "define the [`__repr__()`](/mojo/stdlib/builtin/repr/Representable#__repr__)\n", + "method, which should compute the \"official\" string representation of a type. If\n", + "at all possible, this should look like a valid Mojo expression that could be\n", + "used to recreate a struct instance with the same value.\n", + "\n", + "The [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", + "trait denotes a trait composition of the `Stringable` and `Representable`\n", + "traits. It requires a type to implement both a `__str__()` and a `__repr__()`\n", + "method.\n", + "\n", + "The [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait describes a\n", + "type that can be converted to a stream of UTF-8 encoded data by writing to a\n", + "formatter object. The [`print()`](/mojo/stdlib/builtin/io/print) function\n", + "requires that its arguments conform to the `Formattable` trait. This enables\n", + "efficient stream-based writing by default, avoiding unnecessary intermediate\n", + "String heap allocations.\n", + "\n", + "The `Formattable` trait requires a type to implement a\n", + "[`format_to()`](/mojo/stdlib/utils/format/Formattable#format_to) method, which\n", + "is provided with an instance of\n", + "[`Formatter`](/mojo/stdlib/utils/format/Formatter) as an argument. You then\n", + "invoke the `Formatter` instance's\n", + "[`write()`](/mojo/stdlib/utils/format/Formatter#write) method to write a\n", + "sequence of `Formattable` arguments constituting the `String` representation of\n", + "your type.\n", + "\n", + "While this might sound complex at first, in practice you can minimize\n", + "boilerplate and duplicated code by using the [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) static function to\n", + "implement the type's `Stringable` implementation in terms of its `Formattable`\n", + "implementation. Here is a simple example of a type that implements all of the\n", + "`Stringable`, `Representable`, and `Formattable` traits:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dog(name='Rex', age=5)\n", + "Dog(Rex, 5)\n", + "String: Dog(Rex, 5)\n", + "Representation: Dog(name='Rex', age=5)\n" + ] + } + ], + "source": [ + "@value\n", + "struct Dog(Stringable, Representable, Formattable):\n", + " var name: String\n", + " var age: Int\n", + "\n", + " fn __repr__(self) -> String:\n", + " return \"Dog(name=\" + repr(self.name) + \", age=\" + repr(self.age) + \")\"\n", + "\n", + " fn __str__(self) -> String:\n", + " return String.format_sequence(self)\n", + "\n", + " fn format_to(self, inout writer: Formatter) -> None:\n", + " writer.write(\"Dog(\", self.name, \", \", self.age, \")\")\n", + "\n", + "var dog = Dog(\"Rex\", 5)\n", + "print(repr(dog))\n", + "print(dog)\n", + "\n", + "var dog_info = String(\"String: {!s}\\nRepresentation: {!r}\").format(dog, dog)\n", + "print(dog_info)" ] }, { From b75fc0451c9ae53593cf9a966a5760d53f2e8485 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 12 Sep 2024 12:13:15 -0700 Subject: [PATCH 1474/2019] [Docs] Add conditional conformance section. MODULAR_ORIG_COMMIT_REV_ID: b57b717ce29c6cde7dababfc3234d88e5295996e --- docs/manual/parameters/index.ipynb | 83 +++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index d098309bf9..3d4aa2778e 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -217,7 +217,7 @@ "## Parameterized structs\n", "\n", "You can also add parameters to structs. You can use parameterized structs to\n", - "build generic containers. For example, a generic array type might include code\n", + "build generic collections. For example, a generic array type might include code\n", "like this:" ] }, @@ -322,6 +322,87 @@ "The method returns an instance of `GenericArray[Float64]`." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conditional conformance\n", + "\n", + "When creating a generic struct, you might want to define some methods that\n", + "require extra features. For example, consider a collection like `GenericArray`\n", + "that holds instances of `CollectionElement`. The `CollectionElement` trait\n", + "only requires that the stored data type be copyable and movable. This\n", + "imposes a lot of limitations: you can't implement a `sort()` method because\n", + "you can't guarantee that the stored type supports the comparison operators; you can't\n", + "write a useful `__str__()` or `__repr__()` dunder method because you can't\n", + "guarantee that the stored type supports conversion to a string.\n", + "\n", + "The answer to these issues is _conditional conformance_, which lets you define a \n", + "method that requires additional features. You do this by defining the `self`\n", + "value that has a more specific bound on one or more of its parameters. \n", + "\n", + "For example, the following code defines a `Container` type that holds an\n", + "instance of `CollectionElement`. It also defines a `__str__()` method that can \n", + "only be called if the stored `ElementType` conforms to \n", + "`StringableCollectionElement`:" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "Hello\n" + ] + } + ], + "source": [ + "@value\n", + "struct Container[ElementType: CollectionElement]:\n", + " var element: ElementType\n", + "\n", + " def __str__[StrElementType: StringableCollectionElement, //](\n", + " self: Container[StrElementType]) -> String:\n", + " return str(self.element)\n", + "\n", + "def use_container():\n", + " float_container = Container(5)\n", + " string_container = Container(\"Hello\")\n", + " print(float_container.__str__())\n", + " print(string_container.__str__())\n", + "\n", + "use_container()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the signature of the `__str__()` method, which declares the `self` argument\n", + "with a more specific type. Specifically, it declares that it takes a `Container`\n", + "with an `ElementType` that conforms to the `StringableCollectionElement` trait.\n", + "\n", + "```mojo\n", + "def __str__[StrElementType: StringableCollectionElement, //](\n", + " self: Container[StrElementType]) -> String:\n", + "```\n", + "\n", + "This trait must be a superset of `ElementType`'s original trait: for example,\n", + "`StringableCollectionElement` inherits from `CollectionElement`, so it includes\n", + "all of requirements of the original trait.\n", + "\n", + "Note that the `use_container()` function calls the `__str__()` method directly,\n", + "rather than calling `str(float_container)`. One current limitation of\n", + "conditional conformance is that Mojo can't recognize the struct\n", + "`Container[Int]` as conforming to `Stringable`, even though the `__str__()`\n", + "method is implemented for any `ElementType` that's also `Stringable`." + ] + }, { "attachments": {}, "cell_type": "markdown", From b97989833930745a336a0891ba1e318ccbc16fa3 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 12 Sep 2024 12:57:22 -0700 Subject: [PATCH 1475/2019] [Docs] Update docs for optional var in fn functions. MODULAR_ORIG_COMMIT_REV_ID: e8aacb958bc13d386eb39840cdae30333ddca91b --- docs/manual/functions.ipynb | 8 +--- docs/manual/variables.ipynb | 91 ++++++++++++------------------------- 2 files changed, 30 insertions(+), 69 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 1183291215..65a692ba2d 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -93,9 +93,6 @@ " `inout` [argument\n", " convention](/mojo/manual/values/ownership#argument-conventions)).\n", "\n", - "- [Variables](/mojo/manual/variables) must be declared using the `var`\n", - " keyword.\n", - "\n", "- If the function raises an exception, it must be explicitly declared with the\n", " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", "\n", @@ -174,10 +171,7 @@ " [object reference\n", " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", " \n", - " If an argument is any other declared type, it's received as a value.\n", - "\n", - "- [Variables](/mojo/manual/variables) don't need to be declared using \n", - " `var`." + " If an argument is any other declared type, it's received as a value." ] }, { diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 8d1dd916b6..d9bbae0d29 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -27,7 +27,7 @@ "\n", "Mojo has two kinds of variables:\n", "\n", - "- Declared variables are created with the `var` keyword, and may include\n", + "- Explicitly-declared variables are created with the `var` keyword, and may include\n", " [type annotations](#type-annotations).\n", "\n", " ```mojo\n", @@ -35,7 +35,7 @@ " var b: Float64 = 3.14\n", " ```\n", " \n", - "- Undeclared variables are created with an assignment statement:\n", + "- Implicitly-declared variables are created with an assignment statement:\n", "\n", " ```mojo\n", " a = 5\n", @@ -87,10 +87,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Undeclared variables\n", + "## Implicitly-declared variables\n", "\n", - "Within a `def` function or a REPL environment, you can create a variable with\n", - "just a name and a value. For example:" + "You can create a variable with just a name and a value. For example:" ] }, { @@ -99,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "name = str(\"Sam\")\n", + "name = String(\"Sam\")\n", "user_id = 0" ] }, @@ -107,41 +106,34 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Undeclared variables are strongly typed: they take the type from the first value\n", - "assigned to them. For example, the `user_id` variable above is type `Int`, while\n", - "the `name` variable is type `String`. You can't assign a string to `user_id` or an\n", - "integer to `name`. \n", - "\n", - "Undeclared variables are scoped at the function level. You create an undeclared\n", - "variable the first time you assign a value to a given name inside a function. \n", - "Any subsequent references to that name inside the function refer to the same\n", - "variable. For more information, see [Variable scopes](#variable-scopes), which\n", - "describes how variable scoping differs between declared and undeclared\n", - "variables.\n", - "\n", - ":::note\n", - "\n", - "Undeclared variables are not allowed in an `fn` function or as a struct\n", - "field.\n", - "\n", - ":::" + "Implicitly-declared variables are strongly typed: they take the type from the\n", + "first value assigned to them. For example, the `user_id` variable above is type\n", + "`Int`, while the `name` variable is type `String`. You can't assign a string to\n", + "`user_id` or an integer to `name`.\n", + "\n", + "Implicitly-declared variables are scoped at the function level. You create an\n", + "implicitly-declared variable the first time you assign a value to a given name\n", + "inside a function. Any subsequent references to that name inside the function\n", + "refer to the same variable. For more information, see [Variable\n", + "scopes](#variable-scopes), which describes how variable scoping differs between\n", + "explicitly- and implicitly-declared variables." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Declared variables\n", + "## Explicitly-declared variables\n", "\n", "You can declare a variable with the `var` keyword. For example:\n", "\n", "```mojo\n", - "var name = str(\"Sam\")\n", + "var name = String(\"Sam\")\n", "var user_id: Int\n", "```\n", "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", - "declared values are typed—either explicitly with a \n", + "explicitly-declared variables are typed—either explicitly with a \n", "[type annotation](#type-annotations) or implicitly when they're initialized with\n", "a value.\n", "\n", @@ -154,43 +146,17 @@ "var user_id: Int = \"Sam\"\n", "```\n", "\n", - "There are several main differences between declared variables and undeclared\n", - "variables:\n", + "There are several main differences between explicitly-declared variables and\n", + "implicitly-declared variables:\n", "\n", - "- A declared variable can be declared without initializing it:\n", + "- An explicitly-declared variable can be declared without initializing it:\n", "\n", " ```mojo\n", " var value: Float64\n", " ```\n", "\n", - "- Declared variables follow [lexical scoping](#variable-scopes), unlike \n", - " undeclared variables.\n", - "\n", - "- Declared variables can be used in both `def` and `fn` functions.\n", - "\n", - "Using `var` can help prevent runtime errors caused by typos. For example,\n", - "if you misspell the name of an [undeclared variable](#undeclared-variables),\n", - "Mojo simply creates a new variable using the misspelled name. But when all\n", - "mutable variables must be first declared with `var` (which is the case inside\n", - "an `fn` function), then misspellings such as the following are caught by the\n", - "compiler:\n", - "\n", - "```mojo\n", - "var name = \"Sam\"\n", - "# Somewhere later...\n", - "name = \"Sammy\" # This is not allowed in an `fn` function\n", - "```\n", - "\n", - "Although you can use `var` in a `def` function, this benefit is\n", - "realized only when used inside an `fn` function, where the Mojo compiler will\n", - "flag undeclared variables (such as the above `nane`) as unknown declarations.\n", - "\n", - ":::note\n", - "\n", - "When using Mojo in a REPL environment, top-level variables (variables\n", - "outside a function or struct) do not require `var` declarations.\n", - "\n", - ":::" + "- Explicitly-declared variables follow [lexical scoping](#variable-scopes),\n", + " unlike implicitly-declared variables." ] }, { @@ -460,10 +426,11 @@ "The lifetime of the inner `num` ends exactly where the `if` code block ends,\n", "because that's the scope in which the variable was defined.\n", "\n", - "This is in contrast to undeclared variables (those without the `var`\n", + "This is in contrast to implicitly-declared variables (those without the `var`\n", "keyword), which use **function-level scoping** (consistent with Python variable\n", - "behavior). That means, when you change the value of an undeclared variable\n", - "inside the `if` block, it actually changes the value for the entire function.\n", + "behavior). That means, when you change the value of an implicitly-declared\n", + "variable inside the `if` block, it actually changes the value for the entire\n", + "function.\n", "\n", "For example, here's the same code but *without* the `var` declarations:" ] @@ -500,7 +467,7 @@ "metadata": {}, "source": [ "Now, the last `print()` function sees the updated `num` value from the inner\n", - "scope, because undeclared variables (Python-style variables) use function-level\n", + "scope, because implicitly-declared variables (Python-style variables) use function-level\n", "scope (instead of lexical scope)." ] } From 61a97c6f995ed8a471d9613b908f8f5302283ac4 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:06:05 -0700 Subject: [PATCH 1476/2019] Update v24.5 release date. Incorporated feedback. Added an introduction to the Highlights section. Fixed various typos and formatting issues. MODULAR_ORIG_COMMIT_REV_ID: 04f5c88e787ed477321f8e337b554d9d6c4b4ca1 --- docs/changelog-released.md | 185 ++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 83 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 811a2d885b..4b6d831607 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -7,8 +7,9 @@ toc_max_heading_level: 2 This is a list of changes to the Mojo language, standard library, and tools. -To check your current version, run `mojo --version`, and then [update Mojo with -`magic`](/magic#update-max-and-mojo). +To check your current version, run `mojo --version`. To update the version of +Mojo for your project with the `magic` package manager, follow the instructions +in [Update a package](/magic#update-a-package) to update the `max` package. :::caution Switch to Magic @@ -19,10 +20,13 @@ conda](/magic/conda). ::: -## v24.5 (2024-09-10) +## v24.5 (2024-09-13) ### ✨ Highlights +Here's a brief summary of some of the major changes in this release, with more +detailed information in the following sections: + - Mojo now supports Python 3.12 interoperability. - The set of automatically imported entities (types, aliases, functions) into @@ -30,33 +34,34 @@ conda](/magic/conda). user code as users will need to explicitly import what they're using for cases previously automatically included before. +- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments + conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. + +- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an + optional prompt and reads a line from standard input, in the same way as + Python. + - Mojo now allows implicit definitions of variables within a `fn` in the same way that has been allowed in a `def`. The `var` keyword is still allowed, but is now optional. -- Mojo now supports "conditional conformances" where some methods on a struct - have additional trait requirements that the struct itself doesn't. - - Mojo now diagnoses "argument exclusivity" violations due to aliasing references. Mojo requires references (including implicit references due to `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if mutable. This is a warning in the 24.5 release, but will be upgraded to an error in subsequent releases. +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. + - `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. Functions that previously took a `DTypePointer` now take an equivalent `UnsafePointer`. For more information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in the Mojo Manual. -- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments - conform to the `Formattable` trait. This enables efficient stream-based - writing by default, avoiding unnecessary intermediate String heap allocations. - -- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an - optional prompt and reads a line from standard input, in the same way as - Python. - - There are many new standard library APIs, with new features for strings, collections, and interacting with the filesystem and environment. Changes are listed in the standard library section. @@ -72,7 +77,11 @@ conda](/magic/conda). ### Language changes -#### ⭐️ New +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed and + still denotes the declaration of a new variable with a scope (in both `def` + and `fn`). Relaxing this makes `fn` and `def` more similar, but they still + differ in other important ways. - Mojo now diagnoses "argument exclusivity" violations due to aliasing references. Mojo requires references (including implicit references due to @@ -103,11 +112,59 @@ conda](/magic/conda). implementation details are somewhat different because lifetimes are embedded in types. -- Mojo now allows implicit definitions of variables within a `fn` in the same - way that has been allowed in a `def`. The `var` keyword is still allowed and - still denotes the declaration of a new variable with a scope (in both `def` - and `fn`). Relaxing this makes `fn` and `def` more similar, but they still - differ in other important ways. + This is a warning in the 24.5 release, but will be upgraded to an error in + subsequent releases. + + :::note + + Argument exclusivity is not enforced for register-passable types. They are + passed by copy, so they don't form aliases. + + ::: + +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. This is + expressed through an explicitly declared `self` type: + + ```mojo + struct GenericThing[Type: AnyType]: # Works with anything + # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' + fn normal_method(self): ... + + # Just redeclare the requirements with more specific types: + fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): + var tmp = val^ # Ok to move 'val' since it is Movable + ... + fn usage_example(): + var a = GenericThing[Int]() + a.normal_method() # Ok, Int conforms to AnyType + a.needs_move(42) # Ok, Int is movable + + var b = GenericThing[NonMovable]() + b.normal_method() # Ok, NonMovable conforms to AnyType + + # error: argument type 'NonMovable' does not conform to trait 'Movable' + b.needs_move(NonMovable()) + ``` + + Conditional conformance works with dunder methods and other things as well. + +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: + + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass + + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. + ``` - Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This @@ -172,50 +229,6 @@ conda](/magic/conda). print(x) # no longer complains about 'x' being uninitialized ``` -- Mojo now supports "conditional conformances" where some methods on a struct - have additional trait requirements that the struct itself doesn't. This is - expressed through an explicitly declared `self` type: - - ```mojo - struct GenericThing[Type: AnyType]: # Works with anything - # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' - fn normal_method(self): ... - - # Just redeclare the requirements with more specific types: - fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): - var tmp = val^ # Ok to move 'val' since it is Movable - ... - fn usage_example(): - var a = GenericThing[Int]() - a.normal_method() # Ok, Int conforms to AnyType - a.needs_move(42) # Ok, Int is movable - - var b = GenericThing[NonMovable]() - b.normal_method() # Ok, NonMovable conforms to AnyType - - # error: argument type 'NonMovable' does not conform to trait 'Movable' - b.needs_move(NonMovable()) - ``` - - Conditional conformance works with dunder methods and other things as well. - -- As a specific form of "conditional conformances", initializers in a struct - may indicate specific parameter bindings to use in the type of their `self` - argument. For example: - - ```mojo - @value - struct MyStruct[size: Int]: - fn __init__(inout self: MyStruct[0]): pass - fn __init__(inout self: MyStruct[1], a: Int): pass - fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass - - def test(x: Int): - a = MyStruct() # Infers size=0 from 'self' type. - b = MyStruct(x) # Infers size=1 from 'self' type. - c = MyStruct(x, x) # Infers size=2 from 'self' type. - ``` - - `async` functions now support memory-only results (like `String`, `List`, etc.) and `raises`. Accordingly, both [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) and @@ -251,11 +264,9 @@ conda](/magic/conda). export MOJO_PYTHON="~/venv/bin/python" ``` - `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic `libpython` but no Python executable. -#### 🦋 Changed - - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a C-like set of semantics around pointer aliasing and derivation. However, the C semantics bring a lot of history and baggage that are not needed in Mojo and @@ -323,12 +334,12 @@ conda](/magic/conda). print("Hello, " + name + "!") ``` - If the user enters "Mojo" it returns "Hello Mojo!" + If the user enters "Mojo" it returns "Hello, Mojo!" - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments - conform to the `Formattable` trait. This enables efficient stream-based - writing by default, avoiding unnecessary intermediate String heap - allocations. + conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. Previously, `print()` required types conform to [`Stringable`](/mojo/stdlib/builtin/str/Stringable). This meant that to @@ -374,7 +385,7 @@ conda](/magic/conda). - :::note TODO + :::note The error shown when passing a type that does not implement `Formattable` to `print()` is currently not entirely descriptive of the underlying cause: @@ -462,11 +473,17 @@ conda](/magic/conda). method to `String` and `StringLiteral`, which returns an `UnsafePointer[C_char]` for convenient interoperability with C APIs. - - Added the `byte_length()` method to `String`, `StringSlice`, and - `StringLiteral` and deprecated their private `_byte_length()` methods. - Added a warning to `String.__len__` method that it will return length in - Unicode codepoints in the future and `StringSlice.__len__()` now does return - the Unicode codepoints length. + - Added the `byte_length()` method to + [`String`](/mojo/stdlib/collections/string/String#byte_length), + [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice#byte_length), + and + [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral#byte_length) + and deprecated their private `_byte_length()` methods. Added a warning to + the [`String.__len__()`](/mojo/stdlib/collections/string/String#__len__) + method that it will return the length in Unicode codepoints in the future + and + [`StringSlice.__len__()`](/mojo/stdlib/utils/string_slice/StringSlice#__len__) + now does return the Unicode codepoints length. ([PR #2960](https://github.com/modularml/mojo/pull/2960)) - Added a new [`StaticString`](/mojo/stdlib/utils/string_slice/#aliases) type @@ -482,7 +499,8 @@ conda](/magic/conda). `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, reflecting their use for compatibility with C APIs. - - Continued transition to `UnsafePointer` and unsigned byte type for strings: + - Continued the transition to `UnsafePointer` and unsigned byte type for + strings: - [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr) now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) @@ -548,8 +566,9 @@ conda](/magic/conda). - `initialize_pointee_copy(p, value)` => [`p.init_pointee_copy(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) - `move_pointee(src=p1, dst=p2)` => [`p.move_pointee_into(p2)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) - - The `UnsafePointer.offset()` method has been removed. Use - [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. + - The `UnsafePointer.offset()` method is deprecated and will be removed in a + future release. Use [pointer + arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. ```mojo new_ptr = ptr.offset(1) @@ -667,12 +686,12 @@ conda](/magic/conda). - Filesystem and environment utilities: - [`Path.home()`](/mojo/stdlib/pathlib/path/Path#home) has been added to - return a path of the users home directory. + return a path of the user's home directory. - [`os.path.expanduser()`](/mojo/stdlib/os/path/path/expanduser) and [`pathlib.Path.exapanduser()`](/mojo/stdlib/pathlib/path/Path#expanduser) have been added to allow expanding a prefixed `~` in a `String` or `Path` - with the users home path: + with the user's home path: ```mojo import os @@ -756,7 +775,7 @@ conda](/magic/conda). - [`NoneType`](/mojo/stdlib/builtin/none/NoneType) is now a normal standard library type, and not an alias for a raw MLIR type. - Function signatures spelled as `fn() -> NoneType` should transition to + Function signatures written as `fn() -> NoneType` should transition to being written as `fn() -> None`. - Mojo now has a [`UInt`](/mojo/stdlib/builtin/uint/UInt) type for modeling From 8c35a418eadf31c7343887e1e2088a18d2d9f403 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 16 Sep 2024 16:40:24 -0700 Subject: [PATCH 1477/2019] Add `url` ID to docs issue template (#3489) This allows us to pre-fill the "where is the problem" field with a `url` parameter in the URL. Signed-off-by: Scott Main --- .github/ISSUE_TEMPLATE/doc_issue.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/doc_issue.yaml b/.github/ISSUE_TEMPLATE/doc_issue.yaml index 5a1633728f..b0a709446c 100644 --- a/.github/ISSUE_TEMPLATE/doc_issue.yaml +++ b/.github/ISSUE_TEMPLATE/doc_issue.yaml @@ -12,6 +12,7 @@ body: Please add a title above and fill in the following fields so we can understand the problem. - type: input + id: url attributes: label: Where is the problem? description: Provide a link to the problematic page (with a heading anchor). From 8be14377faa6e39b87770b158c78b91023e3095f Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 26 Sep 2024 16:16:08 -0700 Subject: [PATCH 1478/2019] [issue-template] Change issue template from modular CLI to magic (#3556) Modular CLI is being deprecated for magic, this changes the template to match --- .github/ISSUE_TEMPLATE/magic_cli_issue.yaml | 59 +++++++++++++++++++ .github/ISSUE_TEMPLATE/modular_cli_issue.yaml | 41 ------------- 2 files changed, 59 insertions(+), 41 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/magic_cli_issue.yaml delete mode 100644 .github/ISSUE_TEMPLATE/modular_cli_issue.yaml diff --git a/.github/ISSUE_TEMPLATE/magic_cli_issue.yaml b/.github/ISSUE_TEMPLATE/magic_cli_issue.yaml new file mode 100644 index 0000000000..5f4ff83e65 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/magic_cli_issue.yaml @@ -0,0 +1,59 @@ +##===----------------------------------------------------------------------===## +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +##===----------------------------------------------------------------------===## + +name: Magic CLI issue +description: Create an issue for the Magic CLI tool. +title: "[Magic CLI]: " +labels: + - magic-cli + - mojo-repo +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out an issue report! + + Please provide a descriptive title above and fill in the following fields. + + - type: textarea + id: Description + attributes: + label: Issue description + description: Describe the issue you encountered and what you expected to happen. + validations: + required: true + - type: textarea + id: Steps + attributes: + label: Steps to reproduce + description: Provide the specific steps to reproduce the issue. + value: | + - Include relevant code snippet that did not work as expected. + - If applicable, add screenshots to help explain the problem. + - Include anything else that might help us debug the issue. + validations: + required: true + + - type: textarea + id: Context + attributes: + label: Version Info + description: Which version of the Magic CLI are you using? + value: | + - Provide magic CLI version by pasting the output of `magic --version` + - What OS did you install the magic CLI on? + - If you're on Linux include the flavor and version e.g. Ubuntu 22.04. + - Include your CPU architecture e.g. x86-64 or arm64. + - If related to unresolvable dependencies, include the contents of your + `magic.lock` file. + render: shell diff --git a/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml b/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml deleted file mode 100644 index 5a8027388b..0000000000 --- a/.github/ISSUE_TEMPLATE/modular_cli_issue.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: Modular CLI issue -description: Create an issue for the Modular CLI tool. -title: "[Modular CLI]" -labels: - - "modular-cli,mojo-repo" -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out an issue report! - - Please provide a descriptive title above and fill in the following fields. - - - type: textarea - id: Description - attributes: - label: Issue description - description: Describe the issue you encountered and what you expected to happen. - validations: - required: true - - type: textarea - id: Steps - attributes: - label: Steps to reproduce - description: Provide the specific steps to reproduce the issue. - value: | - - Include relevant code snippet that did not work as expected. - - If applicable, add screenshots to help explain the problem. - - Include anything else that might help us debug the issue. - validations: - required: true - - - type: textarea - id: Context - attributes: - label: Version Info - description: Which version of the Modular CLI are you using ? - value: | - - Provide Modular CLI version by pasting the output of `modular -v` - - What OS did you install modular CLI on ? - render: shell From adc7a16d136d68ebed6ed9593872e87e2dabbe9b Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 1 Oct 2024 14:30:14 -0700 Subject: [PATCH 1479/2019] [stdlib] Update gitignore for magic (#3586) --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 76b6a30505..c77bbb487a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,8 @@ venv/ ENV/ env.bak/ venv.bak/ +.magic +magic.lock + +# MacOS +.DS_Store From ced1ecb1ed102917467a8b099986fe6a7204d6b6 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 1 Oct 2024 15:02:23 -0700 Subject: [PATCH 1480/2019] [stdlib] Use magic to run stdlib tests in CI (#3589) Signed-off-by: Jack Clayton --- .github/workflows/examples.yml | 73 ------------------- .../standard_library_tests_and_examples.yml | 72 ++++++++++++++++++ stdlib/COMPATIBLE_COMPILER_VERSION | 2 +- 3 files changed, 73 insertions(+), 74 deletions(-) delete mode 100644 .github/workflows/examples.yml create mode 100644 .github/workflows/standard_library_tests_and_examples.yml diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml deleted file mode 100644 index 5ddb066fd9..0000000000 --- a/.github/workflows/examples.yml +++ /dev/null @@ -1,73 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # - -name: Test Examples - -on: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - workflow_dispatch: - -permissions: - contents: read - pull-requests: read - -jobs: - test-examples: - runs-on: ubuntu-latest - defaults: - run: - shell: bash - env: - DEBIAN_FRONTEND: noninteractive - LLVM_VERSION: 17 - - steps: - - name: Checkout repo - uses: actions/checkout@v2 - - - name: Download Modular installer - run: | - curl -s https://get.modular.com | sh - - - - name: Install stable Mojo compiler - run: | - # The of "examples" is arbitrary but something - # needs to be provided. - modular auth examples - modular install mojo - - - name: Install build tools - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh ${{env.LLVM_VERSION}} - - # Make common LLVM binaries (including FileCheck) in our PATH so they work when used in an unversioned context - # For example, instead of saying `FileCheck-17` which exists in `/usr/bin`, this allows us to just call - # FileCheck unqualified. - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{env.LLVM_VERSION}} 100 - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${{env.LLVM_VERSION}} 100 - sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-${{env.LLVM_VERSION}} 100 - sudo update-alternatives --install /usr/bin/ld.lld ld.lld /usr/bin/ld.lld-${{env.LLVM_VERSION}} 100 - sudo update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-${{env.LLVM_VERSION}} 100 - sudo update-alternatives --install /usr/bin/FileCheck FileCheck /usr/bin/FileCheck-${{env.LLVM_VERSION}} 100 - - - python3 -m pip install lit - - - name: Run examples - run: | - export MODULAR_HOME="/home/runner/.modular" - export PATH="/home/runner/.modular/pkg/packages.modular.com_mojo/bin:$PATH" - ./examples/run-examples.sh diff --git a/.github/workflows/standard_library_tests_and_examples.yml b/.github/workflows/standard_library_tests_and_examples.yml new file mode 100644 index 0000000000..67c7c080d3 --- /dev/null +++ b/.github/workflows/standard_library_tests_and_examples.yml @@ -0,0 +1,72 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +name: Test stdlib and examples +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +jobs: + test-examples: + name: with ${{ matrix.os }} and assertions=${{ matrix.mojo-enable-assertions }} + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "macos-14"] + mojo-enable-assertions: [0, 1] + + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + + defaults: + run: + shell: bash + env: + DEBIAN_FRONTEND: noninteractive + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Download Magic CLI + run: | + curl -ssL https://magic.modular.com/cfba4c92-2390-4b86-93de-04b2f47114d5 | bash + + # Add magic to PATH + echo "$HOME/.modular/bin" >> $GITHUB_PATH + + # Pin magic to older version to avoid HTTP timeouts and/or client certificate errors + # that manifest as a result of uv/python package solvers from Magic 0.3.0. + "$HOME/.modular/bin/magic" self-update --version 0.2.3 + + - name: Install build tools (Linux) + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + ./stdlib/scripts/install-build-tools-linux.sh + + - name: Install build tools (macOS) + if: ${{ matrix.os == 'macos-14' }} + run: | + ./stdlib/scripts/install-build-tools-macos.sh + + - name: Run standard library tests and examples + env: + MOJO_ENABLE_ASSERTIONS_IN_TESTS: ${{ matrix.mojo-enable-assertions }} + run: | + magic run tests + magic run examples diff --git a/stdlib/COMPATIBLE_COMPILER_VERSION b/stdlib/COMPATIBLE_COMPILER_VERSION index e9a9b4514e..341cb50613 100644 --- a/stdlib/COMPATIBLE_COMPILER_VERSION +++ b/stdlib/COMPATIBLE_COMPILER_VERSION @@ -1 +1 @@ -24.3.0 +24.5.0 From 3cbd7df1890cfb2084d77a5da8c8be950b53de58 Mon Sep 17 00:00:00 2001 From: Steven Truong <5817749+steventr@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:11:00 -0700 Subject: [PATCH 1481/2019] [issue-template] Update mojo issue template to reference magic instead of modular cli (#3646) Modular CLI was deprecated. Signed-off-by: Steven Truong <5817749+steventr@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/mojo_bug_report.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml b/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml index f5920003f2..f859fdde11 100644 --- a/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/mojo_bug_report.yaml @@ -40,5 +40,6 @@ body: value: | - What OS did you do install Mojo on ? - Provide version information for Mojo by pasting the output of `mojo -v` - - Provide Modular CLI version by pasting the output of `modular -v` + - Provide Magic CLI version by pasting the output of `magic -V` or `magic --version` + - Optionally, provide more information with `magic info`. render: shell From 4a7c65449246ac9b1a1a877ababbb2d386a4dfba Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 14 Oct 2024 16:36:20 -0500 Subject: [PATCH 1482/2019] [git] Disable lfs Signed-off-by: Patrick Dougherty --- examples/notebooks/images/.gitattributes | 1 - examples/notebooks/images/background.png | Bin 131 -> 607043 bytes 2 files changed, 1 deletion(-) delete mode 100644 examples/notebooks/images/.gitattributes diff --git a/examples/notebooks/images/.gitattributes b/examples/notebooks/images/.gitattributes deleted file mode 100644 index 24a8e87939..0000000000 --- a/examples/notebooks/images/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.png filter=lfs diff=lfs merge=lfs -text diff --git a/examples/notebooks/images/background.png b/examples/notebooks/images/background.png index 234cd5285c875dc281ad1ace3dd64b2427e651a6..5b8d32f8f99170ece26120e5b985be0d9dc9fde8 100644 GIT binary patch literal 607043 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%0A*>$W1RK?h?H_QK;Z38&v_CP82)Ra{56KQDqtbe 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�>}GpNNNNb85b7E68E3OA91$E|A=+6< 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%0Avg_N*b^KX zp@#gfhOk`iGG<7<3SIJ_e)_3b<;Z5Infv%cvW1wJUHFk7`4Rt_2E%oMj8L5v*ju-i zmlvNx-zkr2xOy1c@MIx44w7B-nFXEQE^>{33%)me4Xy`dWDAc2WH(GfUt0*H8sHf% zf?*={DVMR>2oUY$q#t;9zUh?Vlse=#Vj*XW(=O&jPP-KKC(d3~Y$AreD6pM@2&QRy z4I>A#;r4FBy!EXNmsvnAo0rf|u72{fz{A-ySul&E*(BM`?V zt9KJ~GKD;v=PHwJL#hK`|~_PFnVb@R7r7zSqi`fVRpq{K=#(%0&>lm(QJ72wTgr z1Y3pSA%^s0l4a;20&C@3F%88+vcJ~7GNh&9Zao7~YLyx}J;|p%kaDJ&B^SR z68X~KG99uF>o3g5VH4VVR!_sorekPx4LAE!oDIieSTQvHbPVBvBZL6gPUW`j!f6;y zRQdylWGr^Z>RCug1x)rFo`mps^_TFIHrI?Z#mRi$R(1;iA9eTgYU{V1^&b2m#6a-4 zf4OMlCGiqaF&-mZ6ANA<#F_EqvsfJt~++ri>T^5{oyc79gqy45Q*j2ZB+I5|tyE;{X zT<|g?$1x<*_Jb7Owabv<>EF*75z2^PqI4egC#?b?_*)G|6;?_5KtnBM!>*Y{o#{dp zmgYU-B!7VYu^#|lOxbIj7hQo7IwV$~Vb%04N-K!j5i*1ijAJ1*pq8DoA)`KoH{;9Q ze+d({OWKZ=-Hb}ne67Q7y6nK4IiCD`!Vuz`8!f?(MLI3JD}`GQ@8AC0fBP@}rN88s z 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≥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 w%hSjZbh=J1+!OLrT7VzqYFP* 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 R#wh>*002ovPDHLkV1kC15n})V literal 131 zcmWN?NfN>!5CFh?Ucmc~{=a?F zp2t(ple4^V8@-6xqO$HFbW}@3Z$i? Date: Wed, 16 Oct 2024 13:44:57 -0500 Subject: [PATCH 1483/2019] [Examples] Remove LFS MODULAR_ORIG_COMMIT_REV_ID: 4737cbeb070225ad18e17f130e07fa3d9af243e7 --- docs/changelog-released.md | 4 + .../manual/decorators/nonmaterializable.ipynb | 4 +- docs/manual/packages.md | 18 +-- docs/manual/python/index.ipynb | 21 ++-- docs/manual/values/lifetimes.ipynb | 2 +- docs/roadmap.md | 110 ++---------------- docs/tools/debugging.ipynb | 2 +- 7 files changed, 37 insertions(+), 124 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 4b6d831607..fa078d7193 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -336,6 +336,10 @@ detailed information in the following sections: If the user enters "Mojo" it returns "Hello, Mojo!" + There is a known issue when running the `input()` function with JIT + compilation (see issue + [#3479](https://github.com/modularml/mojo/issues/3479)). + - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. This enables efficient stream-based writing by default, avoiding unnecessary diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index b5a657517b..494a9880a1 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -75,9 +75,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ":::{.callout-note}\n", + ":::note\n", "\n", - "**Note:** A non-materializable struct must have all of its methods annotated\n", + "A non-materializable struct must have all of its methods annotated\n", "as `@always_inline`, and it must be computable in the parameter domain.\n", "\n", ":::" diff --git a/docs/manual/packages.md b/docs/manual/packages.md index f9a146f37e..ec7f1b55ea 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -18,7 +18,7 @@ Mojo module is a single Mojo source file that includes code suitable for use by other files that import it. For example, you can create a module to define a struct such as this one: -```{.mojo filename="mymodule.mojo"} +```mojo title="mymodule.mojo" struct MyPair: var first: Int var second: Int @@ -38,7 +38,7 @@ Notice that this code has no `main()` function, so you can't execute For example, here's how you can import `MyPair` into a file named `main.mojo` that's in the same directory as `mymodule.mojo`: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mymodule import MyPair fn main(): @@ -49,7 +49,7 @@ fn main(): Alternatively, you can import the whole module and then access its members through the module name. For example: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" import mymodule fn main(): @@ -59,7 +59,7 @@ fn main(): You can also create an alias for an imported member with `as`, like this: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" import mymodule as my fn main(): @@ -111,7 +111,7 @@ struct) and `__init__.mojo` is empty. In this case, the `main.mojo` file can now import `MyPair` through the package name like this: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mypackage.mymodule import MyPair fn main(): @@ -148,7 +148,7 @@ mypack.mojopkg Because we named the package file different from the directory, we need to fix the import statement and it all works the same: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mypack.mymodule import MyPair ``` @@ -186,14 +186,14 @@ mypackage/ Let's now add the following line in `__init__.mojo`: -```{.mojo filename="__init__.mojo"} +```mojo title="__init__.mojo" from .mymodule import MyPair ``` That's all that's in there. Now, we can simplify the import statement in `main.mojo` like this: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mypackage import MyPair ``` @@ -210,7 +210,7 @@ from algorithm.functional import map However, the `algorithm/__init__.mojo` file also includes these lines: -```{.mojo filename="algorithm/__init__.mojo"} +```mojo title="algorithm/__init__.mojo" from .functional import * from .reduction import * ``` diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index e9e3caa2b1..3058ef0d98 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -126,12 +126,15 @@ " code than in the Mojo standard library, which \n", " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", "\n", - ":::note\n", "\n", - "Mojo loads the Python interpreter and Python modules at runtime, so\n", - "wherever you run a Mojo program, it must be able to access a compatible Python\n", - "interpreter, and to locate any imported Python modules. For more information,\n", - "see [Create a Python environment](#create-a-python-environment).\n", + ":::caution\n", + "\n", + "[`mojo build`](/mojo/cli/build) doesn't include the Python packages used by\n", + "your Mojo project. Instead, Mojo loads the Python interpreter and Python\n", + "packages at runtime, so they must be provided in the environment where you run\n", + "the Mojo program (such as inside the Magic environment where you built the\n", + "executable). For more information, see the section above to [create a Python\n", + "environment](#create-a-python-environment).\n", "\n", ":::" ] @@ -148,7 +151,7 @@ "\n", "For example, suppose you have a Python file named `mypython.py`:\n", "\n", - "```{.python filename=\"mypython.py\"}\n", + "```python title=\"mypython.py\"\n", "import numpy as np\n", "\n", "def gen_random_values(size, base):\n", @@ -159,7 +162,7 @@ "\n", "Here's how you can import it and use it in a Mojo file:\n", "\n", - "```{.mojo filename=\"main.mojo\"}\n", + "```mojo title=\"main.mojo\"\n", "from python import Python\n", "\n", "def main():\n", @@ -210,7 +213,7 @@ "First you create a Python module that defines a Tkinter interface, with a window\n", "and single button:\n", "\n", - "```{.python filename=\"myapp.py\"}\n", + "```python title=\"myapp.py\"\n", "import tkinter as tk\n", "\n", "class App:\n", @@ -244,7 +247,7 @@ "source": [ "You can call this module from Mojo like this:\n", "\n", - "```{.mojo filename=\"main.mojo\"}\n", + "```mojo title=\"main.mojo\"\n", "from python import Python\n", "\n", "def button_clicked():\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 299323c49c..b66f8828eb 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -298,7 +298,7 @@ "\n", "The syntax for a `ref` argument is:\n", "\n", - "ref [lifetime] argName: argType\n", + "ref [lifetime] argName: argType\n", "\n", "The `lifetime` parameter passed inside the square brackets can be replaced with\n", "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", diff --git a/docs/roadmap.md b/docs/roadmap.md index 80c7422f64..edfe44ec9c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -33,7 +33,7 @@ If you have encountered any bugs with current Mojo behavior, please If you have ideas about how to improve the core Mojo features, we prefer that you first look for similar topics or start a new conversation about it -in our [GitHub Discussions](https://github.com/modularml/mojo/discussions). +on [Discord](https://discord.gg/modular). We also consider Mojo to be a new member of the Python family, so if you have suggestions to improve the experience with Python, we encourage @@ -66,7 +66,7 @@ subsumed by more general features if time and care were given to broader evaluation. - Third, the Python community should tackle some of these ideas first. It is -important to us that Mojo be a good member of the Python family (a "Python++"), +important to us that Mojo be a good member of the Python family, not just a language with Pythonic syntax. As such, we don't want to needlessly diverge from Python evolution: adding a bunch of features could lead to problems down the road if Python makes incompatible decisions. Such a future @@ -83,83 +83,6 @@ better equipped to evaluate these features, they have mature code bases to evaluate them with, and they have processes and infrastructure for making structured language evolution features. -## Mojo SDK known issues - -The Mojo SDK is still in early development -and currently only available for Ubuntu Linux and macOS (Apple silicon) -systems. Here are some of the notable issues that we plan to fix: - -- Missing native support for Windows, Intel Macs, and Linux distributions - other than Ubuntu. Currently, we support Ubuntu systems with x86-64 - processors and Apple Silicon macOS. Support for more Linux distributions - (including Debian and RHEL) and Windows is in progress. - -- Modular CLI install might fail and require `modular clean` before you - re-install. - - If it asks you to perform auth, run `modular auth ` and use the - `MODULAR_AUTH` value shown for the `curl` command on [the download - page](https://developer.modular.com/download). - -- If you attempt to uninstall Mojo with `modular uninstall`, your subsequent - attempt to install Mojo might fail with an HTTP 500 error code. If so, run - `modular clean` and try again. - -- Mojo REPL might hang (become unresponsive for more than 10 seconds) when - interpreting an expression if your system has 4 GiB or less RAM. If you - encounter this issue, please report it with your system specs. - -Additionally, we're aware of some issues that we might not be able to solve, -but we mention them here with some more information: - -- When installing Mojo, if you receive the error, - `failed to reach URL https://cas.modular.com`, it could be because your - network connection is behind a firewall. Try updating your firewall settings - to allow access to these end points: `https://packages.modular.com` and - `https://cas.modular.com`. Then retry with `modular clean` and - `modular install mojo`. - -- When installing Mojo, if you receive the error, - `gpg: no valid OpenGPG data found`, this is likely because you are located - outside our supported geographies. Due to US export control restrictions, we - are unable to provide access to Mojo to users situated in specific countries. - -- If using Windows Subsystem for Linux (WSL), you might face issues with WSL 1. - We recommend you upgrade to WSL 2. To check the version, run `wsl -l -v`. If - you're running WSL 1, refer to the - [WSL upgrade instructions](https://learn.microsoft.com/en-us/windows/wsl/install#upgrade-version-from-wsl-1-to-wsl-2). - -- When installing on macOS (Apple silicon), the Modular CLI install might fail - with the message: - - ```plaintext - modular: The arm64 architecture is required for this software. - ``` - - This occurs because Apple's Rosetta x86 emulation is active. Check the - following: - - - Right click on the terminal application you use (for example, - `Terminal.app`), click **Get Info**, and make sure the **Open in Rosetta** - checkbox is not selected. - - - Run the following command: - - ```bash - brew config | grep Rosetta - ``` - - If the output shows `Rosetta 2: True`, the x86 version of Homebrew is - installed. - [Uninstall and reinstall Homebrew](https://github.com/homebrew/install#uninstall-homebrew) - before retrying the Modular installation. - - **Note:** Before uninstalling Homebrew, verify that you don't have other - projects specifically depending on the x86 version of Homebrew. - -You can see other [reported issues on -GitHub](https://github.com/modularml/mojo/issues). - ## Small independent features There are a number of features that are missing that are important to round out @@ -188,7 +111,7 @@ to use right now. ## Traits support -As of v0.6.0 Mojo has basic support for +Mojo has basic support for [traits](/mojo/manual/traits). Traits allow you to specify a set of requirements for types to implement. Types can implement those requirements to *conform to* the trait. Traits allow you to write @@ -384,9 +307,11 @@ fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]): ### Scoping and mutability of statement variables Python programmers understand that local variables are implicitly declared and -scoped at the function level. As the programming manual explains, this feature -is supported in Mojo only inside `def` functions. However, there are some -nuances to Python's implicit declaration rules that Mojo does not match 1-to-1. +scoped at the function level. As the Mojo Manual explains, this is supported in +Mojo for +[implicitly-declared variables](/mojo/manual/variables#implicitly-declared-variables). +However, there are some nuances to Python's implicit declaration rules that Mojo +does not match 1-to-1. For example, the scope of `for` loop iteration variables and caught exceptions in `except` statements is limited to the next indentation block, for both `def` @@ -412,25 +337,6 @@ dynamic characteristic of the function. Mojo's lifetime tracker is intentionally simple (so lifetimes are easy to use!), and cannot reason that `i` would be defined even when the loop bounds are constant. -Also stated in the programming manual: in `def` functions, the function -arguments are mutable and re-assignable, whereas in `fn`, function arguments are -rvalues and cannot be re-assigned. The same logic extends to statement -variables, like `for` loop iteration variables or caught exceptions: - -```mojo -def foo(): - try: - bad_function(): - except e: - e = Error() # ok: we can overwrite 'e' - -fn bar(): - try: - bad_function(): - except e: - e = Error() # error: 'e' is not mutable -``` - ### Name scoping of nested function declarations In Python, nested function declarations produce dynamic values. They are diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 4f4a62bf31..78dc2e31e7 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -311,7 +311,7 @@ "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", "\n", "For more information, see the VS Code documentation for [Launch \n", - "configurations]](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", + "configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", "\n", ":::note Compilation options\n", "\n", From b65ed35a58ff61b155ddee2c1dfa2fcdbb5680f7 Mon Sep 17 00:00:00 2001 From: Alex Trotta <44127594+Ahajha@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:38:36 -0400 Subject: [PATCH 1484/2019] [stdlib][main] Un-ignore magic.lock files (#3722) Similar to the MAX repo, we will be committing lockfiles soon. Signed-off-by: Alex Trotta --- .gitignore | 1 - examples/.gitignore | 1 - 2 files changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index c77bbb487a..7f9eaec8ff 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ ENV/ env.bak/ venv.bak/ .magic -magic.lock # MacOS .DS_Store diff --git a/examples/.gitignore b/examples/.gitignore index 79d5db1e7f..aa90b40800 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -4,4 +4,3 @@ pixi.lock # Magic env .magic/ -magic.lock From d40e91e1962c787a2d087961453630a6a459ef10 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 29 Aug 2024 14:22:10 -0700 Subject: [PATCH 1485/2019] [mojo-lang] Make exclusivity checking field sensitive. This fixes a bogus class of warnings around things like `mutate(self.a, self.b)` because the two fields can't conflict. This was the major issue people ran into with the exclusivity check. Fixing this allows us to remove some workarounds from the stdlib. MODULAR_ORIG_COMMIT_REV_ID: 3634513e1707f6854bd15f63eea4ec236eaab69d --- CONTRIBUTING.md | 32 +- README.md | 41 +- docs/changelog-released.md | 1295 ++++++++--------- .../manual/decorators/nonmaterializable.ipynb | 4 +- docs/manual/decorators/staticmethod.ipynb | 8 +- docs/manual/functions.ipynb | 8 +- docs/manual/get-started.mdx | 2 +- docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/lifecycle/index.ipynb | 30 +- docs/manual/lifecycle/life.ipynb | 77 +- docs/manual/packages.md | 18 +- docs/manual/parameters/index.ipynb | 199 +-- docs/manual/pointers.ipynb | 15 +- docs/manual/python/index.ipynb | 21 +- docs/manual/traits.ipynb | 128 +- docs/manual/types.ipynb | 8 +- docs/manual/values/index.ipynb | 61 +- docs/manual/values/lifetimes.ipynb | 559 ------- docs/manual/values/ownership.ipynb | 246 +--- docs/manual/values/value-semantics.ipynb | 19 +- docs/manual/variables.ipynb | 91 +- docs/roadmap.md | 118 +- docs/tools/debugging.ipynb | 22 +- docs/tools/images/break-on-raise.png | Bin 341319 -> 0 bytes examples/.gitignore | 1 + examples/README.md | 29 +- examples/notebooks/README.md | 50 +- examples/notebooks/images/.gitattributes | 1 + examples/notebooks/images/background.png | Bin 607043 -> 131 bytes examples/notebooks/pixi.toml | 13 - pixi.toml | 8 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/str.mojo | 4 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/collections/string.mojo | 12 +- stdlib/src/prelude/__init__.mojo | 3 +- stdlib/src/tempfile/tempfile.mojo | 5 +- stdlib/src/utils/__init__.mojo | 1 - .../src/utils/{format.mojo => _format.mojo} | 54 +- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/test/utils/test_format.mojo | 2 +- stdlib/test/utils/test_format_to_stdout.mojo | 2 +- 43 files changed, 1110 insertions(+), 2087 deletions(-) delete mode 100644 docs/manual/values/lifetimes.ipynb delete mode 100644 docs/tools/images/break-on-raise.png create mode 100644 examples/notebooks/images/.gitattributes delete mode 100644 examples/notebooks/pixi.toml rename stdlib/src/utils/{format.mojo => _format.mojo} (78%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c0d372d70d..b59db72aee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -261,30 +261,30 @@ git rebase upstream/nightly #### Getting the nightly Mojo compiler Now that you're on the nightly branch, you need to install the latest nightly -build. - -If you're using [`magic`](https://docs.modular.com/magic), create a new -project environment with the `max-nightly` channel like this: +Mojo compiler: ```bash -magic init mojo-nightly --format mojoproject \ - -c conda-forge -c https://conda.modular.com/max-nightly +curl https://get.modular.com | sh - + +modular auth + +modular install nightly/mojo ``` -If you're [using conda](https://docs.modular.com/magic/conda), add the -`https://conda.modular.com/max-nightly` channel to your `environment.yaml` -file. For example: +If you already have an older `nightly/mojo` compiler, replace +`modular install nightly/mojo` with `modular update nightly/mojo`. -```yaml -[project] -name = "Mojo nightly example" -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] -platforms = ["osx-arm64", "linux-aarch64", "linux-64"] +Then, follow the instructions from the `modular` tool in adding the `mojo` +compiler to your `PATH` such as: -[dependencies] -max = "*" +```bash +echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc +echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc ``` +If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. + #### Mojo nightly vscode extension Install the [Mojo nightly VS Code diff --git a/README.md b/README.md index a3175c6088..d4058593c0 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,14 @@ To learn more about Mojo, see the ### Latest Released -To install the last released build of Mojo, follow the guide to -[Get started with Mojo](https://docs.modular.com/mojo/manual/get-started). +To install the last released build of Mojo, you can install the MAX SDK +or the standalone Mojo SDK: + +- [Get the MAX SDK](https://docs.modular.com/engine/get-started) +- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) + +Then follow the docs to [write your first Mojo +program](https://docs.modular.com/mojo/manual/get-started#2-run-code-in-the-repl). ### Latest Nightly @@ -50,32 +56,13 @@ The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk and be patient! -To get nightly builds, see the same instructions to [Get started with -Mojo](https://docs.modular.com/mojo/manual/get-started), but when you create -your project, instead use the following `magic init` command to set the -conda package channel to `max-nightly`: - -```bash -magic init hello-world-nightly --format mojoproject \ - -c conda-forge -c https://conda.modular.com/max-nightly -``` - -Or, if you're [using conda](https://docs.modular.com/magic/conda), add the -`https://conda.modular.com/max-nightly` channel to your `environment.yaml` -file. For example: - -```yaml -[project] -name = "Mojo nightly example" -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] -platforms = ["osx-arm64", "linux-aarch64", "linux-64"] - -[dependencies] -max = "*" -``` +To get nightly builds, see the same instructions to [install the Mojo +SDK](https://docs.modular.com/mojo/manual/get-started/#1-install-mojo), but use +the command shown there to install `nightly/mojo`. -And when you clone this repo, switch to the `nightly` branch because the `main` -branch might not be compatible with nightly builds: +When you clone this repo, be sure you switch to the `nightly` branch, because +the `main` branch is for stable releases and might not be compatible with +nightly builds: ```bash git clone https://github.com/modularml/mojo.git diff --git a/docs/changelog-released.md b/docs/changelog-released.md index fa078d7193..52e4d4f88c 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -5,92 +5,108 @@ description: A history of significant Mojo changes. toc_max_heading_level: 2 --- -This is a list of changes to the Mojo language, standard library, and tools. +This is a running list of significant changes for the Mojo language and tools. +It doesn't include all internal implementation changes. -To check your current version, run `mojo --version`. To update the version of -Mojo for your project with the `magic` package manager, follow the instructions -in [Update a package](/magic#update-a-package) to update the `max` package. +## Update Mojo -:::caution Switch to Magic +If you don't have Mojo yet, see the [get started +guide](/mojo/manual/get-started). -The `modular` command-line tool is deprecated (see [how to uninstall -it](/max/faq#if-you-installed-with-modular-deprecated-1)). We recommend that -you now [manage your packages with `magic`](/magic), but you can also [use -conda](/magic/conda). +:::caution + +[Magic](/magic) is the preferred package manager and virtual environment manager +for MAX and Mojo projects. [conda](https://docs.conda.io/projects/conda/en/latest/index.html) +is supported as an alternative. + +The legacy [`modular`](/cli) CLI is now deprecated. +We will not release any new `max` or `mojo` packages through the `modular` +tool beyond the 24.5 release. You must now use [Magic](/magic/) or +[conda](/magic/conda) to install MAX and Mojo. ::: -## v24.5 (2024-09-13) +### Update Mojo in a Magic virtual environment -### ✨ Highlights +The virtual environment for each Magic project has its own package versions. +The Mojo programming language is distributed as part of the `max` package. -Here's a brief summary of some of the major changes in this release, with more -detailed information in the following sections: +To view the version of Mojo for a specific Magic project, run the following +command within your project path: -- Mojo now supports Python 3.12 interoperability. +```sh +magic run mojo --version +``` -- The set of automatically imported entities (types, aliases, functions) into - users' Mojo programs has been dramatically reduced. This can break existing - user code as users will need to explicitly import what they're using for cases - previously automatically included before. +Use the [`magic update`](/magic/commands#magic-update) within your project path +to update the `max` package for that project to the latest release: -- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments - conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. - This enables efficient stream-based writing by default, avoiding unnecessary - intermediate String heap allocations. +```sh +magic update max +``` -- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an - optional prompt and reads a line from standard input, in the same way as - Python. +See the [Magic](/magic) documentation for more information on managing Magic +virtual environments. -- Mojo now allows implicit definitions of variables within a `fn` in the same - way that has been allowed in a `def`. The `var` keyword is still allowed, but - is now optional. +### Update Mojo in a conda virtual environment -- Mojo now diagnoses "argument exclusivity" violations due to aliasing - references. Mojo requires references (including implicit references due to - `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if - mutable. This is a warning in the 24.5 release, but will be upgraded to an - error in subsequent releases. +Each conda virtual environment has its own package versions. +The Mojo programming language is distributed as part of the `max` package. -- Mojo now supports "conditional conformances" where some methods on a struct - have additional trait requirements that the struct itself doesn't. +Use the [`conda list`](https://docs.conda.io/projects/conda/en/latest/commands/list.html) +command to list the version of the `max` package for an environment. +For example, for an environment named `max-project`, run: -- `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. - Functions that previously took a `DTypePointer` now take an equivalent - `UnsafePointer`. For more information on using pointers, see [Unsafe - pointers](/mojo/manual/pointers) in the Mojo Manual. +```sh +conda list -n max-project max +``` -- There are many new standard library APIs, with new features for strings, - collections, and interacting with the filesystem and environment. Changes are - listed in the standard library section. +Use the [`conda update`](https://docs.conda.io/projects/conda/en/latest/commands/update.html) +command to update the version of the `max` package for an environment. +For example, for an environment named `max-project`, run: -- The VS Code extension now supports a vendored MAX SDK for VS Code, which is - automatically downloaded by the extension and it's used for all Mojo features, - including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and - more. +```sh +conda update -n max-project max +``` -- [`mojo test`](/mojo/cli/test) now uses the Mojo compiler for running unit - tests. This will resolve compilation issues that sometimes appeared, and will - also improve overall test execution times. +See the [conda](https://docs.conda.io/projects/conda/en/latest/index.html) +documentation for more information on managing conda virtual environments. -### Language changes +### Update Mojo using the `modular` CLI -- Mojo now allows implicit definitions of variables within a `fn` in the same - way that has been allowed in a `def`. The `var` keyword is still allowed and - still denotes the declaration of a new variable with a scope (in both `def` - and `fn`). Relaxing this makes `fn` and `def` more similar, but they still - differ in other important ways. +:::caution + +The legacy [`modular`](/cli) CLI is now deprecated. +We will not release any new `max` or `mojo` packages through the `modular` +tool beyond the 24.5 release. You must now use [Magic](/magic/) or +[conda](/magic/conda) to install MAX and Mojo. + +::: + +To see your Mojo version, run this: + +```sh +mojo --version +``` + +To update Mojo, first [update `modular`](/cli/#description), and then run this: + +```sh +modular update mojo +``` + +## v24.5 (2024-09-10) + +### ⭐️ New - Mojo now diagnoses "argument exclusivity" violations due to aliasing - references. Mojo requires references (including implicit references due to - `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if - mutable. This is important for code safety, because it allows the compiler - (and readers of code) to understand where and when a value is mutated. It is - also useful for performance optimization because it allows the compiler to - know that accesses through immutable references cannot change behind the - scenes. Here is an invalid example: + references. Mojo requires references (including implicit references due to + borrowed/inout arguments) to be uniquely referenced (non-aliased) if mutable. + This is important for code safety, because it allows the compiler (and readers + of code) to understand where and when a value is mutated. It is also useful + for performance optimization because it allows the compiler to know that + accesses through immutable references cannot change behind the scenes. Here is + an invalid example: ```mojo fn take_two_strings(a: String, inout b: String): @@ -112,66 +128,18 @@ detailed information in the following sections: implementation details are somewhat different because lifetimes are embedded in types. - This is a warning in the 24.5 release, but will be upgraded to an error in - subsequent releases. - - :::note - - Argument exclusivity is not enforced for register-passable types. They are - passed by copy, so they don't form aliases. - - ::: - -- Mojo now supports "conditional conformances" where some methods on a struct - have additional trait requirements that the struct itself doesn't. This is - expressed through an explicitly declared `self` type: - - ```mojo - struct GenericThing[Type: AnyType]: # Works with anything - # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' - fn normal_method(self): ... - - # Just redeclare the requirements with more specific types: - fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): - var tmp = val^ # Ok to move 'val' since it is Movable - ... - fn usage_example(): - var a = GenericThing[Int]() - a.normal_method() # Ok, Int conforms to AnyType - a.needs_move(42) # Ok, Int is movable - - var b = GenericThing[NonMovable]() - b.normal_method() # Ok, NonMovable conforms to AnyType - - # error: argument type 'NonMovable' does not conform to trait 'Movable' - b.needs_move(NonMovable()) - ``` - - Conditional conformance works with dunder methods and other things as well. - -- As a specific form of "conditional conformances", initializers in a struct - may indicate specific parameter bindings to use in the type of their `self` - argument. For example: - - ```mojo - @value - struct MyStruct[size: Int]: - fn __init__(inout self: MyStruct[0]): pass - fn __init__(inout self: MyStruct[1], a: Int): pass - fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass - - def test(x: Int): - a = MyStruct() # Infers size=0 from 'self' type. - b = MyStruct(x) # Infers size=1 from 'self' type. - c = MyStruct(x, x) # Infers size=2 from 'self' type. - ``` +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed and + still denotes the declaration of a new variable with a scope (in both `def` + and `fn`). Relaxing this makes `fn` and `def` more similar, but they still + differ in other important ways. - Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This feature provides more flexibility and guarantees around emplacing the result - of a function compared to "guaranteed" named return value optimization (NRVO). - If a `@register_passable` result is bound to a name, the result value is made - accessible as a mutable reference. + of a function compared to "guaranteed" NRVO. If a `@register_passable` result + is bound to a name, the result value is made accessible as a mutable + reference. ```mojo fn efficiently_return_string(b: Bool) -> String as output: @@ -190,7 +158,38 @@ detailed information in the following sections: return value of the function. The compiler will error if the result is not initialized on all normal exit paths from the function. -- `__setitem__()` now works with variadic argument lists such as: +- `String` class now have `rjust`, `ljust` and `center` methods to return + a justified string based on width and fillchar. + ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by + [@mzaks](https://github.com/mzaks)) + +- Creating nested `PythonObject` from a list or tuple of python objects is + possible now: + + ```mojo + var np = Python.import_module("numpy") + var a = np.array([1, 2, 3]) + var b = np.array([4, 5, 6]) + var arrays = PythonObject([a, b]) + assert_equal(len(arrays), 2) + ``` + + Also allowing more convenient call syntax: + + ```mojo + var stacked = np.hstack((a, b)) + assert_equal(str(stacked), "[1 2 3 4 5 6]") + ``` + + ([PR 3264#](https://github.com/modularml/mojo/pull/3264) by + [@kszucs](https://github.com/kszucs)) + +- `List[T]` values are now equality comparable with `==` and `!=` when `T` is + equality comparable. + ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by + [@kszucs](https://github.com/kszucs)) + +- `__setitem__` now works with variadic argument lists such as: ```mojo struct YourType: @@ -198,10 +197,18 @@ detailed information in the following sections: ``` The Mojo compiler now always passes the "new value" being set using the last - keyword argument of the `__setitem__()`, e.g. turning `yourType[1, 2] = 3` into + keyword argument of the `__setitem__`, e.g. turning `yourType[1, 2] = 3` into `yourType.__setitem__(1, 2, val=3)`. This fixes [Issue #248](https://github.com/modularml/mojo/issues/248). +- `Optional` values are now equality comparable with `==` and `!=` when their + element type is equality comparable. + +- Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) + dictionary-like type, matching most of the features of the Python one. + ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by + [@msaelices](https://github.com/msaelices)) + - Mojo context managers used in regions of code that may raise no longer need to define a "conditional" exit function in the form of `fn __exit__(self, e: Error) -> Bool`. This function allows the context @@ -229,10 +236,35 @@ detailed information in the following sections: print(x) # no longer complains about 'x' being uninitialized ``` +- Now supports "conditional conformances" where some methods on a struct have + additional trait requirements that the struct itself doesn't. This is + expressed through an explicitly declared `self` type: + + ```mojo + struct GenericThing[Type: AnyType]: # Works with anything + # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' + fn normal_method(self): ... + + # Just redeclare the requirements with more specific types: + fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): + var tmp = val^ # Ok to move 'val' since it is Movable + ... + fn usage_example(): + var a = GenericThing[Int]() + a.normal_method() # Ok, Int conforms to AnyType + a.needs_move(42) # Ok, Int is movable + + var b = GenericThing[NonMovable]() + b.normal_method() # Ok, NonMovable conforms to AnyType + + # error: argument type 'NonMovable' does not conform to trait 'Movable' + b.needs_move(NonMovable()) + ``` + + Conditional conformance works with dunder methods and other things as well. + - `async` functions now support memory-only results (like `String`, `List`, - etc.) and `raises`. Accordingly, both - [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) and - [`RaisingCoroutine`](/mojo/stdlib/builtin/coroutine/RaisingCoroutine) have + etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means the result types of `async` functions do not need to be `Movable`. @@ -246,692 +278,566 @@ detailed information in the following sections: Note that `async` functions do not yet support indirect calls, `ref` results, and constructors. -- The [`Reference`](/mojo/stdlib/memory/reference/Reference) type (and many - iterators) now use [infer-only - parameters](/mojo/manual/parameters/#infer-only-parameters) to represent the - mutability of their lifetime, simplifying the interface. +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: -- The environment variable `MOJO_PYTHON` can be pointed to an executable to pin - Mojo to a specific version: + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass - ```sh - export MOJO_PYTHON="/usr/bin/python3.11" + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. ``` - Or a virtual environment to always have access to those Python modules: +- The `Reference` type (and many iterators) now use + [infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) to + represent the mutability of their lifetime, simplifying the interface. - ```sh - export MOJO_PYTHON="~/venv/bin/python" - ``` +- `Dict` now implements `setdefault`, to get a value from the dictionary by + key, or set it to a default if it doesn't exist + ([PR #2803](https://github.com/modularml/mojo/pull/2803) + by [@msaelices](https://github.com/msaelices)) - `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic `libpython` - but no Python executable. +- Added new `ExplicitlyCopyable` trait, to mark types that can be copied + explicitly, but which might not be implicitly copyable. -- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a - C-like set of semantics around pointer aliasing and derivation. However, the C - semantics bring a lot of history and baggage that are not needed in Mojo and - which complicate compiler optimizations. The language overall provides a - stronger set of invariants around pointer aliasing with lifetimes and - exclusive mutable references to values, etc. + This supports work to transition the standard library collection types away + from implicit copyability, which can lead to unintended expensive copies. - It is now forbidden to convert a non-pointer-typed value derived from a - Mojo-allocated pointer, such as an integer address, to a pointer-typed value. - "Derived" means there is overlap in the bits of the non-pointer-typed value - with the original pointer value. Accordingly, the - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) - constructor that took an `address` keyword argument has been removed. +- Added `Identifiable` trait, used to describe types that implement the `__is__` + and `__isnot__` trait methods. + ([PR #2807](https://github.com/modularml/mojo/pull/2807)) - It is still possible to make this conversion in certain cases where it is - absolutely necessary, such as interoperating with other languages like Python. - In this case, the compiler makes two assumptions: any pointer derived from a - non-pointer-typed value does not alias any Mojo-derived pointer and that any - external function calls have arbitrary memory effects. + - Also added new `assert_is()` and `assert_is_not()` test utilities to the + `testing` module. -- `await` on a coroutine now consumes it. This strengthens the invariant that - coroutines can be awaited only once. +- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. + ([PR #2701](https://github.com/modularml/mojo/pull/2701) + by [@jayzhan211](https://github.com/jayzhan211)) -### Standard library changes +- Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that + returns an `UnsafePointer[C_char]` for convenient interoperability with C + APIs. -- [`builtin`](/mojo/stdlib/builtin/) package: +- Added `C_char` type alias in `sys.ffi`. - - The set of automatically imported entities (types, aliases, functions) into - users' Mojo programs has been dramatically reduced. Before, with the way the - `builtin` module was handled, all of the entities in the following modules - would be automatically included: +- Added `StringSlice(..)` initializer from a `StringLiteral`. - `memory`, `sys`, `os`, `utils`, `python`, `bit`, `random`, `math`, - `builtin`, `collections` +- Added a `byte_length()` method to `String`, `StringSlice`, and `StringLiteral` +and deprecated their private `_byte_length()` methods. Added a warning to +`String.__len__` method that it will return length in Unicode codepoints in the +future and `StringSlice.__len__` now does return the Unicode codepoints length. +([PR #2960](https://github.com/modularml/mojo/pull/2960) by [@martinvuyk](https://github.com/martinvuyk)) - Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are - the ones automatically imported into users' Mojo programs. This will break a - lot of user code as users will need to explicitly import what they're using - for cases previously commonly included before (such as - [`Optional`](/mojo/stdlib/collections/optional/Optional), - [`Variant`](/mojo/stdlib/utils/variant/Variant), and functions such as - [`abort()`](/mojo/stdlib/os/os/abort), - [`alignof()`](/mojo/stdlib/sys/info/alignof), - [`bitcast()`](/mojo/stdlib/memory/unsafe/bitcast), - [`bitwidthof()`](/mojo/stdlib/sys/info/bitwidthof), - [`external_call()`](/mojo/stdlib/sys/ffi/external_call), - [`simdwidthof()`](/mojo/stdlib/sys/info/simdwidthof), and - [`sizeof()`](/mojo/stdlib/sys/info/sizeof)). +- Added new `StaticString` type alias. This can be used in place of + `StringLiteral` for runtime string arguments. - - Some types from the `builtin` module have been moved to different modules - for clarity which is made possible now that we have a `prelude` module that - can re-export symbols from modules other than `builtin`. +- Added `TemporaryDirectory` in module `tempfile`. + ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) - In particular, the `builtin.string` module has been moved to - [`collections.string`](/mojo/stdlib/collections/string/). +- Added `NamedTemporaryFile` in module `tempfile`. + ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) -- Input and output: +- Added `oct(..)` function for formatting an integer in octal. + ([PR #2914](https://github.com/modularml/mojo/pull/2914) by [@bgreni](https://github.com/bgreni)) - - Added the builtin [`input()`](/mojo/stdlib/builtin/io/input) function, which - behaves the same as Python. - ([PR #3392](https://github.com/modularml/mojo/pull/3392)) +- Added `String.format` method. + ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) - ```mojo - name = input("Enter your name: ") - print("Hello, " + name + "!") - ``` - - If the user enters "Mojo" it returns "Hello, Mojo!" - - There is a known issue when running the `input()` function with JIT - compilation (see issue - [#3479](https://github.com/modularml/mojo/issues/3479)). - - - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments - conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. - This enables efficient stream-based writing by default, avoiding unnecessary - intermediate String heap allocations. - - Previously, `print()` required types conform to - [`Stringable`](/mojo/stdlib/builtin/str/Stringable). This meant that to - execute a call like `print(a, b, c)`, at least three separate String heap - allocations were down, to hold the formatted values of `a`, `b`, and `c` - respectively. The total number of allocations could be much higher if, for - example, `a.__str__()` was implemented to concatenate together the fields of - `a`, like in the following example: - - ```mojo - struct Point(Stringable): - var x: Float64 - var y: Float64 - - fn __str__(self) -> String: - # Performs 3 allocations: 1 each for str(..) of each of the fields, - # and then the final returned `String` allocation. - return "(" + str(self.x) + ", " + str(self.y) + ")" - ``` - - A type like the one above can transition to additionally implementing - `Formattable` with the following changes: - - ```mojo - struct Point(Stringable, Formattable): - var x: Float64 - var y: Float64 - - fn __str__(self) -> String: - return String.format_sequence(self) - - fn format_to(self, inout writer: Formatter): - writer.write("(", self.x, ", ", self.y, ")") - ``` - - In the example above, - [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) - is used to construct a `String` from a type that implements `Formattable`. - This pattern of implementing a type's `Stringable` implementation in terms - of its `Formattable` implementation minimizes boilerplate and duplicated - code, while retaining backwards compatibility with the requirements of the - commonly used `str()` function. - - - - :::note - - The error shown when passing a type that does not implement `Formattable` to - `print()` is currently not entirely descriptive of the underlying cause: - - ```shell - error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified - print(point) - ~~~~~^~~~~~~ - ``` - - If you see the above error, ensure that all argument types implement - `Formattable`. + Support automatic and manual indexing of `*args`. - ::: + Examples: - - [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now also - requires that its `message` argument conform to `Formattable`. + ```mojo + print( + String("{1} Welcome to {0} {1}").format("mojo", "🔥") + ) + # 🔥 Wecome to mojo 🔥 + ``` - - Added - [`TemporaryDirectory`](/mojo/stdlib/tempfile/tempfile/TemporaryDirectory) in - module `tempfile`. - ([PR 2743](https://github.com/modularml/mojo/pull/2743)) + ```mojo + print(String("{} {} {}").format(True, 1.125, 2)) + #True 1.125 2 + ``` - - Added - [`NamedTemporaryFile`](/mojo/stdlib/tempfile/tempfile/NamedTemporaryFile) in - module `tempfile`. - ([PR 2762](https://github.com/modularml/mojo/pull/2762)) +- Added the builtin `input` function, which behaves the same as Python. + ([PR #3392](https://github.com/modularml/mojo/pull/3392) by [@thatstoasty](https://github.com/thatstoasty)) -- [`String`](/mojo/stdlib/collections/string/String) and friends: + ```mojo + name = input("Enter your name: ") + print("Hello, " + name + "!") + ``` - - The `builtin.string` module has been moved to - [`collections.string`](/mojo/stdlib/collections/string/). + If the user enters "Mojo" it returns "Hello Mojo!" - - Added the [`String.format()`](/mojo/stdlib/collections/string/String#format) - method. - ([PR #2771](https://github.com/modularml/mojo/pull/2771)) +- Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo + to a specific version: - Supports automatic and manual indexing of `*args`. + ```sh + export MOJO_PYTHON="/usr/bin/python3.11" + ``` - Examples: + Or a virtual environment to always have access to those Python modules: - ```mojo - print( - String("{1} Welcome to {0} {1}").format("mojo", "🔥") - ) - # 🔥 Wecome to mojo 🔥 - ``` + ```sh + export MOJO_PYTHON="~/venv/bin/python" + ``` - ```mojo - print(String("{} {} {}").format(True, 1.125, 2)) - #True 1.125 2 - ``` + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + but no Python executable. - - [`String.format()`](/mojo/stdlib/collections/string/String#format) now - supports conversion flags `!s` and `!r`, allowing for `str()` and `repr()` - conversions within format strings. - ([PR #3279](https://github.com/modularml/mojo/pull/3279)) +- The `math` package now includes the `pi`, `e`, and `tau` constants (Closes + Issue [#2135](https://github.com/modularml/mojo/issues/2135)). - Example: +- Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a + paltform-dependent width. `UInt` implements most arithmetic operations that + make sense for integers, with the notable exception of `__neg__`. Builtin + functions such as `min`/`max`, as well as `math` functions like `ceildiv`, + `align_down`, and `align_up` are also implemented for `UInt`. - ```mojo - String("{} {!r}").format("Mojo", "Mojo") - # "Mojo 'Mojo'" +- `os.path.expanduser()` and `pathlib.Path.exapanduser()` have been added to + allow expanding a prefixed `~` in a `String` or `Path` with the users home + path: - String("{0!s} {0!r}").format("Mojo") - # "Mojo 'Mojo'" - ``` + ```mojo + import os + print(os.path.expanduser("~/.modular")) + # /Users/username/.modular + print(os.path.expanduser("~root/folder")) + # /var/root/folder (on macos) + # /root/folder (on linux) + ``` - - The `String` class now has - [`rjust()`](/mojo/stdlib/collections/string/String#rjust), - [`ljust()`](/mojo/stdlib/collections/string/String#ljust), and - [`center()`](/mojo/stdlib/collections/string/String#center) methods to - return a justified string based on width and fillchar. ([PR - #3278](https://github.com/modularml/mojo/pull/3278)) - - - The [`atol()`](/mojo/stdlib/collections/string/atol) function now correctly - supports leading underscores, (e.g.`atol("0x_ff", 0)`), when the appropriate - base is specified or inferred (base 0). non-base-10 integer literals as per - Python's [Integer - Literals](). - ([PR #3180](https://github.com/modularml/mojo/pull/3180)) - - - Added the - [`unsafe_cstr_ptr()`](/mojo/stdlib/collections/string/String#unsafe_cstr_ptr) - method to `String` and `StringLiteral`, which returns an - `UnsafePointer[C_char]` for convenient interoperability with C APIs. - - - Added the `byte_length()` method to - [`String`](/mojo/stdlib/collections/string/String#byte_length), - [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice#byte_length), - and - [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral#byte_length) - and deprecated their private `_byte_length()` methods. Added a warning to - the [`String.__len__()`](/mojo/stdlib/collections/string/String#__len__) - method that it will return the length in Unicode codepoints in the future - and - [`StringSlice.__len__()`](/mojo/stdlib/utils/string_slice/StringSlice#__len__) - now does return the Unicode codepoints length. - ([PR #2960](https://github.com/modularml/mojo/pull/2960)) - - - Added a new [`StaticString`](/mojo/stdlib/utils/string_slice/#aliases) type - alias. This can be used in place of - [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral) for - runtime string arguments. - - - Added a - [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice#__init__) - initializer that accepts a `StringLiteral`. - - - The [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) constructors from - `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, - reflecting their use for compatibility with C APIs. - - - Continued the transition to `UnsafePointer` and unsigned byte type for - strings: - - - [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr) - now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) - - - [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr) - now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) - -- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and other - reference type changes: - - - `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. - For more information on using pointers, see [Unsafe - pointers](/mojo/manual/pointers) in the Mojo Manual. - - Functions that previously took a `DTypePointer` now take an - equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to - `UnsafePointer` is: +- `Path.home()` has been added to return a path of the users home directory. - ```mojo - DTypePointer[type] -> UnsafePointer[Scalar[type]] - ``` +- `os.path.split()` has been added for splitting a path into `head, tail`: - There could be places that you have code of the form: + ```mojo + import os + head, tail = os.path.split("/this/is/head/tail") + print("head:", head) + print("tail:", tail) + # head: /this/is/head + # tail: tail + ``` - ```mojo - fn f(ptr: DTypePointer): - ``` +- `os.path.makedirs()` and `os.path.removedirs()` have been added for creating + and removing nested directories: - which is equivalent to `DTypePointer[*_]`. In this case you would have to add - an infer-only `type` parameter to the function: + ```mojo + import os + path = os.path.join("dir1", "dir2", "dir3") + os.path.makedirs(path, exist_ok=True) + os.path.removedirs(path) + ``` - ```mojo - fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): - ``` +- The `pwd` module has been added for accessing user information in + `/etc/passwd` on POSIX systems. This follows the same logic as Python: - because we can’t have an unbound parameter inside the struct. + ```mojo + import pwd + import os + current_user = pwd.getpwuid(os.getuid()) + print(current_user) - There could also be places where you use - `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to - change these to `UnsafePointer[NoneType/Int]`. But since these are not an - `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to - appropriate types. + # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, + # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', + # pw_shell='/bin/zsh') - - The `DTypePointer` - [`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and - [`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods - have been moved to `UnsafePointer`. + print(current_user.pw_uid) - - `UnsafePointer` now supports - [`strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_load), - [`strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_store), - [`gather()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#gather), and - [`scatter()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#scatter) when - the underlying type is `Scalar[DType]`. + # 501 - - The global functions for working with `UnsafePointer` have transitioned to - being methods through the use of conditional conformances: + root = pwd.getpwnam("root") + print(root) - - `destroy_pointee(p)` => [`p.destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) - - `move_from_pointee(p)` => [`p.take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee) - - `initialize_pointee_move(p, value)` => [`p.init_pointee_move(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move) - - `initialize_pointee_copy(p, value)` => [`p.init_pointee_copy(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) - - `move_pointee(src=p1, dst=p2)` => [`p.move_pointee_into(p2)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) + # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, + # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') + ``` - - The `UnsafePointer.offset()` method is deprecated and will be removed in a - future release. Use [pointer - arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. +- Added `Dict.__init__` overload to specify initial capacity. + ([PR #3171](https://github.com/modularml/mojo/pull/3171) by [@rd4com](https://github.com/rd4com)) - ```mojo - new_ptr = ptr.offset(1) - ``` + The capacity has to be a power of two and above or equal 8. - Becomes: + It allows for faster initialization by skipping incremental growth steps. - ```mojo - new_ptr = ptr + 1 - ``` + Example: - - `UnsafePointer` now has an - [`alignment`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#parameters) - parameter to specify the static alignment of the pointer. Consequently, - [`UnsafePointer.alloc()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#alloc) - no longer takes in an alignment parameter, and the alignment should be - specified in the type. + ```mojo + var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) + # Insert (2/3 of 1024) entries + ``` - ```mojo - UnsafePointer[type].alloc[alignment](x) # now becomes - UnsafePointer[type, alignment].alloc(x) - ``` +- `ListLiteral` now supports `__contains__`. + ([PR #3251](https://github.com/modularml/mojo/pull/3251) by + [@jjvraw](https://github.com/jjvraw)) - - `UnsafePointer` has a new [`exclusive: Bool = - False`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#parameters) - parameter. Setting this parameter to true tells the compiler that the user - knows this pointer and all those derived from it have exclusive access to - the underlying memory allocation. The compiler is not guaranteed to do - anything with this information. +- `bit` module now supports `bit_reverse()`, `byte_swap()` and `pop_count()` for + `Int` type. + ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) - - It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the - [`UnsafePointer.address_of(someRef[])`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#address_of) - which makes the code explicit that the `UnsafePointer` gets the address of - what the reference points to. +- `String.format()` now supports conversion flags `!s` and `!r`, allowing for + `str()` and `repr()` conversions within format strings. + ([PR #3279](https://github.com/modularml/mojo/pull/3279) by [@jjvraw](https://github.com/jjvraw)) -- Python interoperability changes: + Example: - - Mojo now supports Python 3.12 interoperability. + ```mojo + String("{} {!r}").format("Mojo", "Mojo") + # "Mojo 'Mojo'" - - Creating a nested - [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) from a list - or tuple of Python objects is possible now: + String("{0!s} {0!r}").format("Mojo") + # "Mojo 'Mojo'" + ``` - ```mojo - var np = Python.import_module("numpy") - var a = np.array([1, 2, 3]) - var b = np.array([4, 5, 6]) - var arrays = PythonObject([a, b]) - assert_equal(len(arrays), 2) - ``` +- `sort` now supports `stable` parameter. It can be called by - Also allowing more convenient call syntax: + ```mojo + sort[cmp_fn, stable=True](list) + ``` - ```mojo - var stacked = np.hstack((a, b)) - assert_equal(str(stacked), "[1 2 3 4 5 6]") - ``` + The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to + allocate, the program will crash. - ([PR #3264](https://github.com/modularml/mojo/pull/3264)) +- The `mojo test` command now accepts a `--filter` option that will narrow the + set of tests collected and executed. The filter string is a POSIX extended + regular expression. - - Accessing local Python modules with - [`Python.add_to_path(".")`](/mojo/stdlib/python/python/Python#add_to_path) - is no longer required. It now behaves the same as Python. You can access - modules in the same folder as the target file: +- The `mojo test` command now supports using the same compilation options as + `mojo build`. - - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` +- You can now debug unit tests using `mojo test` by passing the `--debug` flag. + Most debug flags are supported; run `mojo test --help` for a full listing. - - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` + Debugging doctests is not currently supported. -- Collections: +- `UnsafePointer` now has an `alignment` parameter to specify the static + alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes + in an alignment parameter, and the alignment should be specified in the type. - - [`List`](/mojo/stdlib/collections/list/List) values are now equality - comparable with `==` and `!=` when their element type is equality - comparable. ([PR #3195](https://github.com/modularml/mojo/pull/3195)) + ```mojo + UnsafePointer[type].alloc[alignment](x) # now becomes + UnsafePointer[type, alignment].alloc(x) + ``` - - [`Optional`](/mojo/stdlib/collections/optional/Optional) values are now - equality comparable with `==` and `!=` when their element type is equality - comparable. +- The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo features, + including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and + more. - - Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) - dictionary-like type, matching most of the features of the Python one. - ([PR #2910](https://github.com/modularml/mojo/pull/2910)) +- The Mojo debugger now hides the artificial function arguments `__result__` and + `__error__` created by the compiler for Mojo code. - - [`Dict`](/mojo/stdlib/collections/dict/Dict) now implements - [`setdefault()`](/mojo/stdlib/collections/dict/Dict#setdefault), which gets - a value from the dictionary by key, or sets it to a default if it doesn't - exist. - ([PR #2803](https://github.com/modularml/mojo/pull/2803)) +- The Mojo debugger now supports a `break-on-raise` command that indicated the + debugger to stop at any `raise` statements. A similar features has been added + to the debugger on VS Code. - - `Dict` now supports - [`popitem()`](/mojo/stdlib/collections/dict/Dict#popitem), which removes and - returns the last item in the `Dict`. - ([PR #2701](https://github.com/modularml/mojo/pull/2701)) +- A proxy has been added to the Mojo Language Server on VS Code that handles + crashes more gracefully. - - Added a [`Dict.__init__()`](/mojo/stdlib/collections/dict/Dict#__init__) - overload to specify initial capacity. - ([PR #3171](https://github.com/modularml/mojo/pull/3171)) +### 🦋 Changed - The capacity has to be a power of two and greater than or equal to 8. +- The set of automatically imported entities (types, aliases, functions) into user's + Mojo programs has been dramatically reduced. Before, with the way the `builtin` + module was handled, all of the entities in the following modules would be automatically + included: - It allows for faster initialization by skipping incremental growth steps. + {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', + 'builtin', 'collections'} - Example: + Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are + the ones automatically imported into user's Mojo programs. This will break + a lot of user code as users will need to explicitly import what they're using + for cases previously commonly included before (such as `Optional`, `Variant`, + and so on). - ```mojo - var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) - # Insert (2/3 of 1024) entries - ``` +- Some types from the `builtin` module have been moved to different modules for clarity + which is made possible now that we have a `prelude` module that can re-export symbols + from modules other than `builtin`. + - `builtin.string` has been moved to `collections.string`. - - `ListLiteral` now supports - [`__contains__()`](/mojo/stdlib/builtin/builtin_list/ListLiteral#__contains__). - ([PR #3251](https://github.com/modularml/mojo/pull/3251)) +- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a + C-like set of semantics around pointer aliasing and derivation. However, the C + semantics bring a lot of history and baggage that are not needed in Mojo and + which complicate compiler optimizations. The language overall provides a + stronger set of invariants around pointer aliasing with lifetimes and + exclusive mutable references to values, etc. -- Filesystem and environment utilities: + It is now forbidden to convert a non-pointer-typed value derived from a + Mojo-allocated pointer, such as an integer address, to a pointer-typed value. + "Derived" means there is overlap in the bits of the non-pointer-typed value + with the original pointer value. Accordingly, the `UnsafePointer` constructor + that took an `address` keyword argument has been removed. - - [`Path.home()`](/mojo/stdlib/pathlib/path/Path#home) has been added to - return a path of the user's home directory. + It is still possible to make this conversion in certain cases where it is + absolutely necessary, such as interoperating with other languages like Python. + In this case, the compiler makes two assumptions: any pointer derived from a + non-pointer-typed value does not alias any Mojo-derived pointer and that any + external function calls have arbitrary memory effects. - - [`os.path.expanduser()`](/mojo/stdlib/os/path/path/expanduser) and - [`pathlib.Path.exapanduser()`](/mojo/stdlib/pathlib/path/Path#expanduser) - have been added to allow expanding a prefixed `~` in a `String` or `Path` - with the user's home path: +- `DTypePointer` , `LegacyPointer` and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) instead. For more + information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in + the Mojo Manual. - ```mojo - import os - print(os.path.expanduser("~/.modular")) - # /Users/username/.modular - print(os.path.expanduser("~root/folder")) - # /var/root/folder (on macos) - # /root/folder (on linux) - ``` + Functions that previously took a `DTypePointer` now take an + equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to + `UnsafePointer` is: - - [`os.path.split()`](/mojo/stdlib/os/path/path/split) has been added for - splitting a path into `head, tail`: + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] + ``` - ```mojo - import os - head, tail = os.path.split("/this/is/head/tail") - print("head:", head) - print("tail:", tail) - # head: /this/is/head - # tail: tail - ``` + There could be places that you have code of the form: - - [`os.makedirs()`](/mojo/stdlib/os/os/makedirs) and - [`os.removedirs()`](/mojo/stdlib/os/os/removedirs) have been added for - creating and removing nested directories: + ```mojo + fn f(ptr: DTypePointer): + ``` - ```mojo - import os - path = os.path.join("dir1", "dir2", "dir3") - os.path.makedirs(path, exist_ok=True) - os.path.removedirs(path) - ``` + which is equivalent to `DTypePointer[*_]`. In this case you would have to add + an infer-only `type` parameter to the function: - - The [`pwd`](/mojo/stdlib/pwd/pwd/) module has been added for accessing user - information in `/etc/passwd` on POSIX systems. This follows the same logic - as Python: + ```mojo + fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): + ``` - ```mojo - import pwd - import os - current_user = pwd.getpwuid(os.getuid()) - print(current_user) + because we can’t have an unbound parameter inside the struct. - # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, - # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', - # pw_shell='/bin/zsh') + There could also be places where you use + `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to + change these to `UnsafePointer[NoneType/Int]`. But since these are not an + `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to + appropriate types. - print(current_user.pw_uid) +- The `DTypePointer` `load()`, `store()`, and `prefetch()` methods have been + moved to `SIMD` and now take an + `UnsafePointer` as an argument. Instead of using `ptr.load[width=4](offset)` + one should use `SIMD[size=4].load(ptr, offset)`. Note the default load width + before was 1, but the default size of `SIMD` is the size of the SIMD type. The + default store size is the size of the `SIMD` value to be stored. - # 501 +- `UnsafePointer` now supports `simd_strided_load()`, `simd_strided_store()`, + `gather()`, and `scatter()` when the underlying type is `Scalar[DType]`. - root = pwd.getpwnam("root") - print(root) +- The global functions for working with `UnsafePointer` have transitioned to + being methods through the use of conditional conformances: - # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, - # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') - ``` + - `destroy_pointee(p)` => `p.destroy_pointee()` + - `move_from_pointee(p)` => `p.take_pointee()` + - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` + - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` + - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` -- Other new traits and related features: +- The `UnsafePointer.offset()` method has been removed. Use + [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. - - Added the - [`ExplicitlyCopyable`](/mojo/stdlib/builtin/value/ExplicitlyCopyable) trait - to mark types that can be copied explicitly, but which might not be - implicitly copyable. + ```mojo + new_ptr = ptr.offset(1) + ``` - This supports work to transition the standard library collection types away - from implicit copyability, which can lead to unintended expensive copies. + Becomes: - - Added the [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable) - trait, used to describe types that implement the `__is__()` and - `__isnot__()` trait methods. - ([PR #2807](https://github.com/modularml/mojo/pull/2807)) + ```mojo + new_ptr = ptr + 1 + ``` - - Types conforming to [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) (that - is, those implementing `__bool__()`) no longer implicitly convert to `Bool`. - A new [`ImplicitlyBoolable`](/mojo/stdlib/builtin/bool/ImplicitlyBoolable) - trait is introduced for types where this behavior is desired. +- `UnsafePointer` has a new + `exclusive: Bool = False` parameter. Setting this parameter to true tells the + compiler that the user knows this pointer and all those derived from it have + exclusive access to the underlying memory allocation. The compiler is not + guaranteed to do anything with this information. -- Miscellaneous: +- It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + `UnsafePointer.address_of(someRef[])` which makes the code explicit that the + `UnsafePointer` gets the address of what the reference points to. - - [`NoneType`](/mojo/stdlib/builtin/none/NoneType) is now a normal standard - library type, and not an alias for a raw MLIR type. +- `sort` no longer takes `LegacyPointer`. The current API supports: + - `sort(list)` just plain list + - `sort[type, cmp_fn](list)` list with custom compare function + - `sort(ptr, len)` a pointer and length (can change to Span in future) + - `sort[type, cmp_fn](ptr, len)` above with custom compare - Function signatures written as `fn() -> NoneType` should transition to - being written as `fn() -> None`. +- Continued transition to `UnsafePointer` and unsigned byte type for strings: - - Mojo now has a [`UInt`](/mojo/stdlib/builtin/uint/UInt) type for modeling - unsigned (scalar) integers with a platform-dependent width. `UInt` - implements most arithmetic operations that make sense for integers, with the - notable exception of `__neg__()`. Builtin functions such as `min()`/`max()`, - as well as `math` functions like `ceildiv()`, `align_down()`, and - `align_up()` are also implemented for `UInt`. + - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `UnsafePointer[Int8]`) + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` + (was `UnsafePointer[Int8]`) - - Now that we have a `UInt` type, use this to represent the return type of a - hash. In general, hashes should be an unsigned integer, and can also lead to - improved performance in certain cases. +- `await` on a coroutine now consumes it. This strengthens the invariant that + coroutines can only be awaited once. - - Added the [`C_char`](/mojo/stdlib/sys/ffi/#aliases) type alias in `sys.ffi`. +- `print()` now requires that its arguments conform to the `Formattable` trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. - - [`sort()`](/mojo/stdlib/builtin/sort/sort) now supports a `stable` - parameter. It can be called by + Previously, `print()` required types conform to `Stringable`. This meant that + to execute a call like `print(a, b, c)`, at least three separate String heap + allocations were down, to hold the formatted values of `a`, `b`, and `c` + respectively. The total number of allocations could be much higher if, for + example, `a.__str__()` was implemented to concatenate together the fields of + `a`, like in the following example: - ```mojo - sort[cmp_fn, stable=True](list) - ``` + ```mojo + struct Point(Stringable): + var x: Float64 + var y: Float64 - The algorithm requires $$O(N)$$ auxiliary memory. If extra memory allocation - fails, the program crashs. + fn __str__(self) -> String: + # Performs 3 allocations: 1 each for str(..) of each of the fields, + # and then the final returned `String` allocation. + return "(" + str(self.x) + ", " + str(self.y) + ")" + ``` - - `sort()` no longer takes `LegacyPointer` since that type is now removed. + A type like the one above can transition to additionally implementing + `Formattable` with the following changes: - - Added the [`oct()`](/mojo/stdlib/builtin/format_int/oct) builtin function - for formatting an integer in octal. - ([PR #2914](https://github.com/modularml/mojo/pull/2914)) + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 - - Added the [`assert_is()`](/mojo/stdlib/testing/testing/assert_is) and - [`assert_is_not()`](/mojo/stdlib/testing/testing/assert_is_not) test - functions to the `testing` module. + fn __str__(self) -> String: + return String.format_sequence(self) - - The [`math`](/mojo/stdlib/math/constants/) package now includes the `pi`, - `e`, and `tau` constants (Closes Issue - [#2135](https://github.com/modularml/mojo/issues/2135)). + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` - - The [`ulp`](/mojo/stdlib/math/math/ulp) function from `numerics` has been - moved to the `math` module. + In the example above, `String.format_sequence()` is used to construct a + `String` from a type that implements `Formattable`. This pattern of + implementing a type's `Stringable` implementation in terms of its `Formattable` + implementation minimizes boilerplate and duplicated code, while retaining + backwards compatibility with the requirements of the commonly used `str(..)` + function. - - `bit` module now supports - [`bit_reverse()`](/mojo/stdlib/bit/bit/bit_reverse), - [`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap), and - [`pop_count()`](/mojo/stdlib/bit/bit/pop_count) for the `Int` type. - ([PR #3150](https://github.com/modularml/mojo/pull/3150)) + - - A few `bit` functions have been renamed for clarity: + > [!WARNING] + > The error shown when passing a type that does not implement `Formattable` to + > `print()` is currently not entirely descriptive of the underlying cause: + > + > ```shell + > error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified + > print(point) + > ~~~~~^~~~~~~ + > ``` + > + > If the above error is seen, ensure that all argument types implement + > `Formattable`. - - `countl_zero()` -> [`count_leading_zeros()`](/mojo/stdlib/bit/bit/count_leading_zeros) +- `debug_assert()` now also requires that its `message` argument conform to + `Formattable`. - - `countr_zero()` -> [`count_trailing_zeros()`](/mojo/stdlib/bit/bit/count_trailing_zeros) +- The `StringRef` constructors from `DTypePointer.int8` have been changed to + take a `UnsafePointer[C_char]`, reflecting their use for compatibility with + C APIs. - - [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) now uses - `OptionalReg[Int]` for `start` and `end` and implements a constructor which - accepts optional values. `Slice._has_end()` has also been removed since a - Slice with no end is now represented by an empty `Slice.end` option. - ([PR #2495](https://github.com/modularml/mojo/pull/2495)) +- `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements + a constructor which accepts optional values. `Slice._has_end()` has also been + removed since a Slice with no end is now represented by an empty `Slice.end` + option. + ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) - ```mojo - var s = Slice(1, None, 2) - print(s.start.value()) # must retrieve the value from the optional - ``` + ```mojo + var s = Slice(1, None, 2) + print(s.start.value()) # must retrieve the value from the optional + ``` - - The `rank` argument for - [`algorithm.elementwise()`](/mojo/stdlib/algorithm/functional/elementwise) - is no longer required and is only inferred. +- `NoneType` is now a normal standard library type, and not an alias for a raw + MLIR type. - - The `time.now()` function has been deprecated. Please use - [`time.perf_counter()`](/mojo/stdlib/time/time/perf_counter) or - [`time.perf_counter_ns`](/mojo/stdlib/time/time/perf_counter_ns) instead. + Function signatures spelled as `fn(...) -> NoneType` should transition to + being written as `fn(...) -> None`. - - [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) construction from `Bool` has been - restricted to `DType.bool` data type. +- Accessing local Python modules with `Python.add_to_path(".")` is no longer + required, it now behaves the same as Python, you can access modules in the + same folder as the target file: -### Tooling changes + - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` + - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` -- [`mojo test`](/mojo/cli/test) new features and changes: +- The rank argument for `algorihtm.elementwise` is no longer required and is + only inferred. - - `mojo test` now uses the Mojo compiler for running unit tests. This will - resolve compilation issues that sometimes appeared, and will also improve - overall test times, since we will only compile unit tests once before - executing all of them. +- The `ulp` function in `numerics` has been moved to the `math` module. - These changes do not apply to doctests, due to their different semantics. +- The Mojo Language Server no longer sets `.` as a commit character for + auto-completion. - - The `mojo test` command now accepts a `--filter` option that will narrow the - set of tests collected and executed. The filter string is a POSIX extended - regular expression. +- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer + implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced + for types where this behavior is desired. - - The `mojo test` command now supports using the same compilation options as - `mojo build`. +- The `time.now()` function has been deprecated. Please use `time.perf_counter` + or `time.perf_counter_ns` instead. - - You can now debug unit tests using `mojo test` by passing the `--debug` - flag. Most debug flags are supported; run `mojo test --help` for a full - listing. +- A few bit functions have been renamed for clarity: +- `countl_zero` -> `count_leading_zeros` +- `countr_zero` -> `count_trailing_zeros` - Debugging doctests is not currently supported. +- Now that we have a `UInt` type, use this to represent the return type of hash. + In general, hashes should be an unsigned integer, and can also lead to improved + performance in certain cases. -- Mojo debugger new features and changes: +- The `atol` function now correctly supports leading underscores, + (e.g.`atol("0x_ff", 0)`), when the appropriate base is specified or inferred + (base 0). non-base-10 integer literals as per Python's [Integer Literals](\ + ). + ([PR #3180](https://github.com/modularml/mojo/pull/3180) + by [@jjvraw](https://github.com/jjvraw)) - - The `mojo debug --rpc` command has been renamed to [`mojo debug - --vscode`](/mojo/cli/debug#debug-server-options), which is now able to - manage multiple VS Code windows. +- `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. - - The Mojo debugger now supports a `break-on-raise` command that indicated the - debugger to stop at any `raise` statements. A similar features has been - added to the debugger on VS Code. +- `SIMD.load/store` are moved to `UnsafePointer`. - - The Mojo debugger now hides the artificial function arguments `__result__` - and `__error__` created by the compiler for Mojo code. +- `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` + are removed from prelude. -- VS Code support changes: +- The `simd_strided_load()` and `simd_strided_store()` have been renamed to + `strided_load` and `strided_store` in `UnsafePointer`. - - The VS Code extension now supports a vendored MAX SDK for VS Code, which is - automatically downloaded by the extension and it's used for all Mojo - features, including the Mojo Language Server, the Mojo debugger, the Mojo - formatter, and more. +- `mojo test` now uses the Mojo compiler for running unit tests. This will resolve + compilation issues that sometimes appeared, and will also improve overall test + times, since we will only compile unit tests once before executing all of them. - - A proxy has been added to the Mojo Language Server on VS Code that handles - crashes more gracefully. + These changes do not apply to doctests, due to their different semantics. -- The Mojo Language Server no longer sets `.` as a commit character for - auto-completion. +- The `mojo debug --rpc` command has been renamed to `mojo debug --vscode`, + which is now able to manage multiple VS Code windows. ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from the compiler, please switch to using `fn __init__(inout self, ...):` instead. -- The builtin `tensor` module has been removed. Identical functionality is - available in [`max.tensor`](/max/api/mojo/tensor/tensor), but it is generally - recommended to use structs from the [`buffer`](/mojo/stdlib/buffer/buffer) - module when possible instead. - - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. - Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for `SIMD` +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD instead. -- Removed the `SIMD.{add,mul,sub}_with_overflow()` methods. - -- Removed the `SIMD.min()` and `SIMD.max()` methods. Identical functionality is - available using the builtin [`min()`](/mojo/stdlib/builtin/math/min) and - [`max()`](/mojo/stdlib/builtin/math/max) functions. +- The builtin `tensor` module has been removed. Identical functionality is + available in `max.tensor`, but it is generally recommended to use `buffer` + when possible instead. - Removed the Mojo Language Server warnings for unused function arguments. +- Removed the `SIMD.{add,mul,sub}_with_overflow` methods. + +- Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is + available using the builting `min` and `max` functions. + - `Run Mojo File in Dedicated Terminal` action has been removed, and the action `Run Mojo File` will always open a dedicated terminal for each mojo file to guarantee a correct environment. @@ -974,24 +880,11 @@ detailed information in the following sections: - [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated references to `let` in REPL documentation. -- The VS Code extension no longer caches the information of the selected +- The VS Code extension doesn't cache anymore the information of the selected MAX SDK, which was causing issues upon changes in the SDK. - The Mojo debugger now stops showing spurious warnings when parsing closures. -### Special thanks - -Special thanks to our community contributors: -[@jjvraw](https://github.com/jjvraw), -[@artemiogr97](https://github.com/artemiogr97), -[@martinvuyk](https://github.com/martinvuyk), -[@jayzhan211](https://github.com/jayzhan211), -[@bgreni](https://github.com/bgreni), [@mzaks](https://github.com/mzaks), -[@msaelices](https://github.com/msaelices), -[@rd4com](https://github.com/rd4com), [@jiex-liu](https://github.com/jiex-liu), -[@kszucs](https://github.com/kszucs), -[@thatstoasty](https://github.com/thatstoasty) - ## v24.4 (2024-06-07) ### ✨ Highlights @@ -1294,31 +1187,31 @@ Big themes for this release: types such as `Bencher` which provides the ability to execute a `Benchmark` and allows for benchmarking configuration via the `BenchmarkConfig` struct. -- [`String`](/mojo/stdlib/collections/string/String) and friends: +- [`String`](/mojo/stdlib/builtin/string/String) and friends: - **Breaking.** Implicit conversion to `String` is now removed for builtin classes/types. Use [`str()`](/mojo/stdlib/builtin/str/str) explicitly to convert to `String`. - - Added [`String.isspace()`](/mojo/stdlib/collections/string/String#isspace) + - Added [`String.isspace()`](/mojo/stdlib/builtin/string/String#isspace) method conformant with Python's universal separators. This replaces the `isspace()` free function from the `string` module. (If you need the old function, it is temporarily available as `_isspace()`. It now takes a `UInt8` but is otherwise unchanged.) - - [`String.split()`](/mojo/stdlib/collections/string/String#split) now - defaults to whitespace and has Pythonic behavior in that it removes all - adjacent whitespace by default. + - [`String.split()`](/mojo/stdlib/builtin/string/String#split) now defaults to + whitespace and has Pythonic behavior in that it removes all adjacent + whitespace by default. - - [`String.strip()`](/mojo/stdlib/collections/string/String#strip), - [`lstrip()`](/mojo/stdlib/collections/string/String#lstrip) and - [`rstrip()`](/mojo/stdlib/collections/string/String#rstrip) can now remove + - [`String.strip()`](/mojo/stdlib/builtin/string/String#strip), + [`lstrip()`](/mojo/stdlib/builtin/string/String#lstrip) and + [`rstrip()`](/mojo/stdlib/builtin/string/String#rstrip) can now remove custom characters other than whitespace. In addition, there are now several useful aliases for whitespace, ASCII lower/uppercase, and so on. ([PR #2555](https://github.com/modularml/mojo/pull/2555)) - `String` now has a - [`splitlines()`](/mojo/stdlib/collections/string/String#splitlines) method, + [`splitlines()`](/mojo/stdlib/builtin/string/String#splitlines) method, which allows splitting strings at line boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) and provides an option to retain or remove the line break characters. @@ -1347,13 +1240,13 @@ Big themes for this release: points to. - Added new - [`as_string_slice()`](/mojo/stdlib/collections/string/String#as_string_slice) + [`as_string_slice()`](/mojo/stdlib/builtin/string/String#as_string_slice) methods to `String` and `StringLiteral`. - Added `StringSlice` initializer from an `UnsafePointer` and a length in bytes. - Added a new - [`as_bytes_slice()`](/mojo/stdlib/collections/string/String#as_bytes_slice) + [`as_bytes_slice()`](/mojo/stdlib/builtin/string/String#as_bytes_slice) method to `String` and `StringLiteral`, which returns a `Span` of the bytes owned by the string. @@ -1361,7 +1254,7 @@ Big themes for this release: [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and unsigned byte type for strings: - Renamed `String._as_ptr()` to - [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr), + [`String.unsafe_ptr()`](/mojo/stdlib/builtin/string/String#unsafe_ptr), and changed return type to `UnsafePointer` (was `DTypePointer`). - Renamed `StringLiteral.data()` to [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr), @@ -1395,16 +1288,14 @@ Big themes for this release: string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is specified, the string will be parsed as if it was an integer literal, with the base determined by whether the string contains the prefix `"0x"`, - `"0o"`, or `"0b"`. - ([PR #2273](https://github.com/modularml/mojo/pull/2273), + `"0o"`, or `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273), fixes [#2274](https://github.com/modularml/mojo/issues/2274)) - Added the [`bin()`](/mojo/stdlib/builtin/format_int/bin) built-in function to convert integral types into their binary - string representation. - ([PR #2603](https://github.com/modularml/mojo/pull/2603)) + string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603)) - - Added the [`atof()`](/mojo/stdlib/collections/string/atof) built-in function, + - Added the [`atof()`](/mojo/stdlib/builtin/string/atof) built-in function, which can convert a `String` to a `float64`. ([PR #2649](https://github.com/modularml/mojo/pull/2649)) @@ -2161,17 +2052,17 @@ Special thanks to our community contributors: initialized with all zeros. This provides an easy way to fill in the data of a tensor. -- [`String`](/mojo/stdlib/collections/string/String) now has `removeprefix()` - and `removesuffix()` methods. +- [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and + `removesuffix()` methods. ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) -- The [`ord`](/mojo/stdlib/collections/string/ord) and - [`chr`](/mojo/stdlib/collections/string/chr) functions have been improved to +- The [`ord`](/mojo/stdlib/builtin/string/ord) and + [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to accept any Unicode character. ([@mzaks](https://github.com/mzaks), contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) -- [`atol()`](/mojo/stdlib/collections/string/atol) now handles whitespace. The +- [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The `atol()`function is used internally by `String.__int__()`, so `int(String( " 10 "))` now returns `10` instead of raising an error. ([@artemiogr97](https://github.com/artemiogr97)) @@ -3243,7 +3134,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. now returns a [`Reference`](/mojo/stdlib/memory/reference/Reference) to the value rather than a copy. -- The [`String`](/mojo/stdlib/collections/string/String) methods `tolower()` +- The [`String`](/mojo/stdlib/builtin/string/String) methods `tolower()` and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. @@ -3738,9 +3629,9 @@ experience without dedicated sugar. [`Movable`](/mojo/stdlib/builtin/value/Movable) and [`Copyable`](/mojo/stdlib/builtin/value/Copyable) built-in traits. -- [`String`](/mojo/stdlib/collections/string/String) now has new - [`toupper()`](/mojo/stdlib/collections/string/String#upper) and - [`tolower()`](/mojo/stdlib/collections/string/String#lower) methods analogous, +- [`String`](/mojo/stdlib/builtin/string/String) now has new + [`toupper()`](/mojo/stdlib/builtin/string/String#upper) and + [`tolower()`](/mojo/stdlib/builtin/string/String#lower) methods analogous, respectively, to Python's `str.toupper()` and `str.tolower()`. - Added a [`hash()`](/mojo/stdlib/builtin/hash/hash) built-in function and @@ -4084,11 +3975,11 @@ the previous "read to EOF" behavior when size is negative. - `file.FileHandle` now has a `seek()` method. -- [`String`](/mojo/stdlib/collections/string/String) now has an - [`rfind()`](/mojo/stdlib/collections/string/String#rfind) method analogous to +- [`String`](/mojo/stdlib/builtin/string/String) now has an + [`rfind()`](/mojo/stdlib/builtin/string/String#rfind) method analogous to Python's `str.rfind()`. -- `String` now has an [`split()`](/mojo/stdlib/collections/string/String#split) +- `String` now has an [`split()`](/mojo/stdlib/builtin/string/String#split) method analogous to Python's `str.split()`. - [`Path`](/mojo/stdlib/pathlib/path/Path) now has a @@ -4396,7 +4287,7 @@ the previous "read to EOF" behavior when size is negative. and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. -- The [`String`](/mojo/stdlib/collections/string/String) type now has the +- The [`String`](/mojo/stdlib/builtin/string/String) type now has the `count()` and `find()` methods to enable counting the number of occurrences or finding the offset index of a substring in a string. diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index 494a9880a1..b5a657517b 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -75,9 +75,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ":::note\n", + ":::{.callout-note}\n", "\n", - "A non-materializable struct must have all of its methods annotated\n", + "**Note:** A non-materializable struct must have all of its methods annotated\n", "as `@always_inline`, and it must be computable in the parameter domain.\n", "\n", ":::" diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index 84ff19d90d..f0c0def9a8 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -27,19 +27,19 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "from collections import List\n", + "from max.tensor import Tensor\n", "from pathlib import Path\n", "\n", "\n", "struct MyStruct:\n", - " var data: List[UInt8]\n", + " var data: Tensor[DType.int8]\n", "\n", " fn __init__(inout self):\n", - " self.data = List[UInt8]()\n", + " self.data = Tensor[DType.int8]()\n", "\n", " fn __moveinit__(inout self, owned existing: Self):\n", " self.data = existing.data ^\n", diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 65a692ba2d..1183291215 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -93,6 +93,9 @@ " `inout` [argument\n", " convention](/mojo/manual/values/ownership#argument-conventions)).\n", "\n", + "- [Variables](/mojo/manual/variables) must be declared using the `var`\n", + " keyword.\n", + "\n", "- If the function raises an exception, it must be explicitly declared with the\n", " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", "\n", @@ -171,7 +174,10 @@ " [object reference\n", " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", " \n", - " If an argument is any other declared type, it's received as a value." + " If an argument is any other declared type, it's received as a value.\n", + "\n", + "- [Variables](/mojo/manual/variables) don't need to be declared using \n", + " `var`." ] }, { diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 363fef9d10..08dcbf6503 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -236,7 +236,7 @@ cd hello-world ``` ```sh -magic add max +magic add "max=*" ``` ```sh diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 1440d8fbb8..91913f32aa 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -332,7 +332,7 @@ "source": [ "The `pet.name` field is destroyed after the first `print()`, because Mojo knows\n", "that it will be overwritten below. You can also see this behavior when using the\n", - "transfer sigil:" + "transfer operator:" ] }, { diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 9714beaf5b..39f645eca4 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -30,7 +30,7 @@ "Mojo also has no built-in data types with special privileges. All data types\n", "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/collections/string/String)) are implemented as\n", + "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", "[structs](/mojo/manual/structs). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", @@ -71,31 +71,19 @@ "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", "All values that are declared with the same type have the same lifecycle.\n", "\n", - "- The \"lifetime\" of a value is defined by the span of time during \n", - "program execution in which each value is considered valid. The life of a value \n", - "begins when it is initialized (via `__init__()`, `__copyinit__()` or \n", - "`__moveinit__()`) and ends when it is destroyed (`__del__()`), or consumed in\n", - "some other way (for example, as part of a `__moveinit__()` call). \n", - "\n", - "No two values have the exact same life span, because every value is created and \n", - "destroyed at a different point in time (even if the difference is imperceptible).\n", - "\n", - ":::note Lifetime type\n", - "\n", - "The concept of lifetimes is related to the `lifetime` type, a Mojo primitive\n", - "used to track ownership. For most Mojo programming, you won't need to work with\n", - "`lifetime` values directly. For information, see [Lifetimes and\n", - "references](/mojo/manual/values/lifetimes).\n", - "\n", - ":::\n", + "- The \"lifetime\" of a value is defined by the span of time during program\n", + "execution in which each value is considered valid. The life of a value begins\n", + "when it is initialized and ends when it is destroyed, which generally (but not\n", + "always) spans from `__init__()` to `__del__()`. No two values have the exact\n", + "same lifetime, because every value is created and destroyed at a different\n", + "point in time (even if the difference is imperceivable).\n", "\n", "The life of a value in Mojo begins when a variable is initialized and continues\n", "up until the value is last used, at which point Mojo destroys it. Mojo destroys\n", "every value/object as soon as it's no longer used, using an “as soon as\n", - "possible” (ASAP) destruction policy that runs after every sub-expression. The \n", - "Mojo compiler takes care of releasing resources after last use when needed.\n", + "possible” (ASAP) destruction policy that runs after every sub-expression.\n", "\n", - "As you might imagine, keeping track of a value's life can be difficult if a\n", + "As you might imagine, keeping track of a value's lifetime can be difficult if a\n", "value is shared across functions many times during the life of a program.\n", "However, Mojo makes this predictable partly through its [value\n", "semantics](/mojo/manual/values/value-semantics) and [value\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index ef262ef54b..c6140e7ef3 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -28,7 +28,7 @@ "All data types in Mojo—including basic types in the standard library such as\n", "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/collections/string/String), up to complex types such\n", + "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", "[struct](/mojo/manual/structs). This means the creation and\n", @@ -43,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -96,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -118,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -173,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -207,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -355,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -400,7 +400,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -424,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -438,7 +438,7 @@ " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(val)\n", + " self.data.store(i, val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", @@ -446,20 +446,18 @@ " self.cap = existing.cap\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(existing.data[i])\n", + " self.data.store(i, existing.data.load(i))\n", " # The lifetime of `existing` continues unchanged\n", "\n", " fn __del__(owned self):\n", " # We must free the heap-allocated data, but\n", " # Mojo knows how to destroy the other fields\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", " fn append(inout self, val: Int):\n", " # Update the array for demo purposes\n", " if self.size < self.cap:\n", - " (self.data + self.size).init_pointee_copy(val)\n", + " self.data.store(self.size, val)\n", " self.size += 1\n", " else:\n", " print(\"Out of bounds\")\n", @@ -470,7 +468,7 @@ " for i in range(self.size):\n", " if i > 0:\n", " print(\", \", end=\"\")\n", - " print(self.data[i], end=\"\")\n", + " print(self.data.load(i), end=\"\")\n", " print(\"]\")" ] }, @@ -490,7 +488,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -537,7 +535,7 @@ "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", - "sigil `^`), then Mojo instead invokes the move constructor.\n", + "operator `^`), then Mojo instead invokes the move constructor.\n", "\n", ":::" ] @@ -590,63 +588,44 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "struct HeapArray:\n", " var data: UnsafePointer[Int]\n", " var size: Int\n", - " var cap: Int\n", - "\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", - " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(val)\n", + " self.data.store(i, val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", " self.size = existing.size\n", - " self.cap = existing.cap\n", - " self.data = UnsafePointer[Int].alloc(self.cap)\n", + " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(existing.data[i])\n", - " # The lifetime of `existing` continues unchanged\n", + " self.data.store(i, existing.data.load(i))\n", "\n", " fn __moveinit__(inout self, owned existing: Self):\n", " print(\"move\")\n", " # Shallow copy the existing value\n", " self.size = existing.size\n", - " self.cap = existing.cap\n", " self.data = existing.data\n", " # Then the lifetime of `existing` ends here, but\n", " # Mojo does NOT call its destructor\n", "\n", " fn __del__(owned self):\n", - " # We must free the heap-allocated data, but\n", - " # Mojo knows how to destroy the other fields\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", - " fn append(inout self, val: Int):\n", - " # Update the array for demo purposes\n", - " if self.size < self.cap:\n", - " (self.data + self.size).init_pointee_copy(val)\n", - " self.size += 1\n", - " else:\n", - " print(\"Out of bounds\")\n", - "\n", " fn dump(self):\n", - " # Print the array contents for demo purposes\n", " print(\"[\", end=\"\")\n", " for i in range(self.size):\n", " if i > 0:\n", " print(\", \", end=\"\")\n", - " print(self.data[i], end=\"\")\n", + " print(self.data.load(i), end=\"\")\n", " print(\"]\")" ] }, @@ -661,7 +640,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "sigil](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", + "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", @@ -670,7 +649,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -698,7 +677,7 @@ "\"move-only\" by implementing `__moveinit__()` and _excluding_ `__copyinit__()`.\n", "A move-only type can be passed to other variables and passed into functions\n", "with any argument convention (`borrowed`, `inout`, and `owned`)—the only catch\n", - "is that you must use the `^` transfer sigil to end the lifetime of a\n", + "is that you must use the `^` transfer operator to end the lifetime of a\n", "move-only type when assigning it to a new variable or when passing it as an\n", "`owned` argument." ] @@ -741,7 +720,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -763,7 +742,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -800,7 +779,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -828,7 +807,7 @@ "must take ownership to store each value. This is a useful micro-optimization\n", "and enables the use of move-only types. Trivial types like `Int` are also\n", "passed as `owned`, but because ownership doesn't mean anything for integers, we\n", - "can elide that declaration and the transfer sigil (`^`) for simplicity. The\n", + "can elide that declaration and the transfer operator (`^`) for simplicity. The\n", "transfer operator is also just a formality in this case, because, even if it's\n", "not used with `self.name = name^`, the Mojo compiler will notice that `name` is\n", "last used here and convert this assignment into a move, instead of a\n", diff --git a/docs/manual/packages.md b/docs/manual/packages.md index ec7f1b55ea..f9a146f37e 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -18,7 +18,7 @@ Mojo module is a single Mojo source file that includes code suitable for use by other files that import it. For example, you can create a module to define a struct such as this one: -```mojo title="mymodule.mojo" +```{.mojo filename="mymodule.mojo"} struct MyPair: var first: Int var second: Int @@ -38,7 +38,7 @@ Notice that this code has no `main()` function, so you can't execute For example, here's how you can import `MyPair` into a file named `main.mojo` that's in the same directory as `mymodule.mojo`: -```mojo title="main.mojo" +```{.mojo filename="main.mojo"} from mymodule import MyPair fn main(): @@ -49,7 +49,7 @@ fn main(): Alternatively, you can import the whole module and then access its members through the module name. For example: -```mojo title="main.mojo" +```{.mojo filename="main.mojo"} import mymodule fn main(): @@ -59,7 +59,7 @@ fn main(): You can also create an alias for an imported member with `as`, like this: -```mojo title="main.mojo" +```{.mojo filename="main.mojo"} import mymodule as my fn main(): @@ -111,7 +111,7 @@ struct) and `__init__.mojo` is empty. In this case, the `main.mojo` file can now import `MyPair` through the package name like this: -```mojo title="main.mojo" +```{.mojo filename="main.mojo"} from mypackage.mymodule import MyPair fn main(): @@ -148,7 +148,7 @@ mypack.mojopkg Because we named the package file different from the directory, we need to fix the import statement and it all works the same: -```mojo title="main.mojo" +```{.mojo filename="main.mojo"} from mypack.mymodule import MyPair ``` @@ -186,14 +186,14 @@ mypackage/ Let's now add the following line in `__init__.mojo`: -```mojo title="__init__.mojo" +```{.mojo filename="__init__.mojo"} from .mymodule import MyPair ``` That's all that's in there. Now, we can simplify the import statement in `main.mojo` like this: -```mojo title="main.mojo" +```{.mojo filename="main.mojo"} from mypackage import MyPair ``` @@ -210,7 +210,7 @@ from algorithm.functional import map However, the `algorithm/__init__.mojo` file also includes these lines: -```mojo title="algorithm/__init__.mojo" +```{.mojo filename="algorithm/__init__.mojo"} from .functional import * from .reduction import * ``` diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 3d4aa2778e..04a46f62ca 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -139,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -155,7 +155,7 @@ "fn repeat[MsgType: Stringable, count: Int](msg: MsgType):\n", " @parameter\n", " for i in range(count):\n", - " print(str(msg))\n", + " print(msg)\n", "\n", "# Must use keyword parameter for `count`\n", "repeat[count=2](42)" @@ -175,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -191,7 +191,7 @@ "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", " @parameter\n", " for i in range(count):\n", - " print(str(msg))\n", + " print(msg)\n", "\n", "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", "repeat[2](42)" @@ -217,7 +217,7 @@ "## Parameterized structs\n", "\n", "You can also add parameters to structs. You can use parameterized structs to\n", - "build generic collections. For example, a generic array type might include code\n", + "build generic containers. For example, a generic array type might include code\n", "like this:" ] }, @@ -227,7 +227,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n", "\n", "struct GenericArray[ElementType: CollectionElement]:\n", " var data: UnsafePointer[ElementType]\n", @@ -237,11 +237,11 @@ " self.size = len(elements)\n", " self.data = UnsafePointer[ElementType].alloc(self.size)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_move(elements[i])\n", + " initialize_pointee_move(self.data.offset(i), elements[i])\n", "\n", " fn __del__(owned self):\n", " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", + " destroy_pointee(self.data.offset(i))\n", " self.data.free()\n", "\n", " fn __getitem__(self, i: Int) raises -> ref [__lifetime_of(self)] ElementType:\n", @@ -322,87 +322,6 @@ "The method returns an instance of `GenericArray[Float64]`." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conditional conformance\n", - "\n", - "When creating a generic struct, you might want to define some methods that\n", - "require extra features. For example, consider a collection like `GenericArray`\n", - "that holds instances of `CollectionElement`. The `CollectionElement` trait\n", - "only requires that the stored data type be copyable and movable. This\n", - "imposes a lot of limitations: you can't implement a `sort()` method because\n", - "you can't guarantee that the stored type supports the comparison operators; you can't\n", - "write a useful `__str__()` or `__repr__()` dunder method because you can't\n", - "guarantee that the stored type supports conversion to a string.\n", - "\n", - "The answer to these issues is _conditional conformance_, which lets you define a \n", - "method that requires additional features. You do this by defining the `self`\n", - "value that has a more specific bound on one or more of its parameters. \n", - "\n", - "For example, the following code defines a `Container` type that holds an\n", - "instance of `CollectionElement`. It also defines a `__str__()` method that can \n", - "only be called if the stored `ElementType` conforms to \n", - "`StringableCollectionElement`:" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5\n", - "Hello\n" - ] - } - ], - "source": [ - "@value\n", - "struct Container[ElementType: CollectionElement]:\n", - " var element: ElementType\n", - "\n", - " def __str__[StrElementType: StringableCollectionElement, //](\n", - " self: Container[StrElementType]) -> String:\n", - " return str(self.element)\n", - "\n", - "def use_container():\n", - " float_container = Container(5)\n", - " string_container = Container(\"Hello\")\n", - " print(float_container.__str__())\n", - " print(string_container.__str__())\n", - "\n", - "use_container()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note the signature of the `__str__()` method, which declares the `self` argument\n", - "with a more specific type. Specifically, it declares that it takes a `Container`\n", - "with an `ElementType` that conforms to the `StringableCollectionElement` trait.\n", - "\n", - "```mojo\n", - "def __str__[StrElementType: StringableCollectionElement, //](\n", - " self: Container[StrElementType]) -> String:\n", - "```\n", - "\n", - "This trait must be a superset of `ElementType`'s original trait: for example,\n", - "`StringableCollectionElement` inherits from `CollectionElement`, so it includes\n", - "all of requirements of the original trait.\n", - "\n", - "Note that the `use_container()` function calls the `__str__()` method directly,\n", - "rather than calling `str(float_container)`. One current limitation of\n", - "conditional conformance is that Mojo can't recognize the struct\n", - "`Container[Int]` as conforming to `Stringable`, even though the `__str__()`\n", - "method is implemented for any `ElementType` that's also `Stringable`." - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -617,7 +536,7 @@ "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n", "\n", "# Make a big vector containing 1.0 in float16 format.\n", - "var big_vec = SIMD[DType.float16, 32](1.0)\n", + "var big_vec = SIMD[DType.float16, 32].splat(1.0)\n", "\n", "# Do some math and convert the elements to float32.\n", "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n", @@ -771,11 +690,11 @@ " fn __init__(inout self, one: One[Type], another: One[Type]):\n", " self.val1 = one.value\n", " self.val2 = another.value\n", - " print(str(self.val1), str(self.val2))\n", + " print(self.val1, self.val2)\n", "\n", " @staticmethod\n", " fn fire(thing1: One[Type], thing2: One[Type]):\n", - " print(\"🔥\", str(thing1.value), str(thing2.value))\n", + " print(\"🔥\", thing1.value, thing2.value)\n", "\n", "def use_two():\n", " s3 = Two(One(\"infer\"), One(\"me\"))\n", @@ -824,7 +743,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -856,7 +775,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -881,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -921,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -966,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -1010,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -1035,7 +954,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -1076,7 +995,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -1121,7 +1040,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -1183,12 +1102,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.1400001049041748 3.1400001049041748 3.1400001049041748 3.1400001049041748\n" + ] + } + ], "source": [ - "from memory import UnsafePointer\n", - "\n", "struct Array[T: AnyTrivialRegType]:\n", " var data: UnsafePointer[T]\n", " var size: Int\n", @@ -1197,14 +1122,12 @@ " self.size = size\n", " self.data = UnsafePointer[T].alloc(self.size)\n", " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(value)\n", + " self.data[i] = value\n", "\n", " fn __getitem__(self, i: Int) -> T:\n", " return self.data[i]\n", "\n", " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", "var v = Array[Float32](4, 3.14)\n", @@ -1228,7 +1151,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -1288,7 +1211,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -1324,12 +1247,10 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "from collections import Dict\n", - "\n", "alias StringKeyDict = Dict[String, _]\n", "var b = StringKeyDict[UInt8]()\n", "b[\"answer\"] = 42" @@ -1349,7 +1270,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -1446,7 +1367,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -1482,7 +1403,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -1513,7 +1434,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -1530,7 +1451,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -1551,7 +1472,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -1598,15 +1519,15 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "@value\n", "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", " fn __str__(self) -> String:\n", - " return str(\"Fudge (\") + str(sugar) + \",\" +\n", - " str(cream) + \",\" + str(chocolate) + \")\"\n" + " var values = StaticIntTuple[3](sugar, cream, chocolate)\n", + " return str(\"Fudge\") + str(values)" ] }, { @@ -1619,7 +1540,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -1641,12 +1562,12 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]):\n", - " print(\"Ate\", str(f))" + " print(\"Ate \" + str(f))" ] }, { @@ -1659,15 +1580,15 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Ate Fudge (5,5,7)\n", - "Ate Fudge (5,8,9)\n" + "Ate Fudge(5, 5, 7)\n", + "Ate Fudge(5, 8, 9)\n" ] } ], @@ -1699,12 +1620,12 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "fn devour(f: Fudge[_, 6, _]):\n", - " print(\"Devoured\", str(f))" + " print(str(\"Devoured \") + str(f))" ] }, { @@ -1719,12 +1640,12 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(\"Devoured\", str(f))" + " print(str(\"Devoured \") + str(f))" ] }, { @@ -1740,12 +1661,12 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "fn devour(f: Fudge[_, chocolate=_, cream=6]):\n", - " print(\"Devoured\", str(f))" + " print(str(\"Devoured \") + str(f))" ] }, { @@ -1757,15 +1678,15 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Devoured Fudge (3,6,9)\n", - "Devoured Fudge (4,6,8)\n" + "Devoured Fudge(3, 6, 9)\n", + "Devoured Fudge(4, 6, 8)\n" ] } ], @@ -1786,12 +1707,12 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "fn nibble(f: Fudge[5]):\n", - " print(\"Ate\", str(f))" + " print(\"Ate \" + str(f))" ] }, { diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 9c4392c261..a302461d66 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -215,7 +215,7 @@ "str_ptr.init_pointee_move(my_string^)\n", "```\n", "\n", - "Note that to move the value, you usually need to add the transfer sigil\n", + "Note that to move the value, you usually need to add the transfer operator\n", "(`^`), unless the value is a [trivial\n", "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", "`Int`) or a newly-constructed, \"owned\" value:\n", @@ -620,11 +620,14 @@ "source": [ "## Working with SIMD vectors\n", "\n", - "The `UnsafePointer` type includes\n", - "[`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and\n", - "[`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods for\n", - "performing aligned loads and stores of scalar values. It also has methods\n", - "supporting strided load/store and gather/scatter.\n", + "The standard library provides a few special methods for loading and storing SIMD\n", + "values. The `SIMD` type includes static \n", + "[`load()`](/mojo/stdlib/builtin/simd/SIMD#load) and \n", + "[`store()`](/mojo/stdlib/builtin/simd/SIMD#store) methods for performing aligned\n", + "loads and stores of scalar values.\n", + "\n", + "The `UnsafePointer` type includes various methods of loading and storing SIMD\n", + "values to memory. In particular: strided load/store and gather/scatter.\n", "\n", "Strided load loads values from memory into a SIMD vector using an offset (the\n", "\"stride\") between successive memory addresses. This can be useful for\n", diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 3058ef0d98..e9e3caa2b1 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -126,15 +126,12 @@ " code than in the Mojo standard library, which \n", " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", "\n", + ":::note\n", "\n", - ":::caution\n", - "\n", - "[`mojo build`](/mojo/cli/build) doesn't include the Python packages used by\n", - "your Mojo project. Instead, Mojo loads the Python interpreter and Python\n", - "packages at runtime, so they must be provided in the environment where you run\n", - "the Mojo program (such as inside the Magic environment where you built the\n", - "executable). For more information, see the section above to [create a Python\n", - "environment](#create-a-python-environment).\n", + "Mojo loads the Python interpreter and Python modules at runtime, so\n", + "wherever you run a Mojo program, it must be able to access a compatible Python\n", + "interpreter, and to locate any imported Python modules. For more information,\n", + "see [Create a Python environment](#create-a-python-environment).\n", "\n", ":::" ] @@ -151,7 +148,7 @@ "\n", "For example, suppose you have a Python file named `mypython.py`:\n", "\n", - "```python title=\"mypython.py\"\n", + "```{.python filename=\"mypython.py\"}\n", "import numpy as np\n", "\n", "def gen_random_values(size, base):\n", @@ -162,7 +159,7 @@ "\n", "Here's how you can import it and use it in a Mojo file:\n", "\n", - "```mojo title=\"main.mojo\"\n", + "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", "def main():\n", @@ -213,7 +210,7 @@ "First you create a Python module that defines a Tkinter interface, with a window\n", "and single button:\n", "\n", - "```python title=\"myapp.py\"\n", + "```{.python filename=\"myapp.py\"}\n", "import tkinter as tk\n", "\n", "class App:\n", @@ -247,7 +244,7 @@ "source": [ "You can call this module from Mojo like this:\n", "\n", - "```mojo title=\"main.mojo\"\n", + "```{.mojo filename=\"main.mojo\"}\n", "from python import Python\n", "\n", "def button_clicked():\n", diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 60eb9bfb05..962de79a2e 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -451,11 +451,10 @@ " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", " - [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable)\n", - " - [`Formattable`](/mojo/stdlib/utils/format/Formattable)\n", + " - [`Formattable`](/mojo/stdlib/utils/_format/Formattable)\n", " - [`Hashable`](/mojo/stdlib/builtin/hash/Hashable)\n", " - [`Indexer`](/mojo/stdlib/builtin/int/Indexer)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", - " - [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising)\n", " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", @@ -466,8 +465,6 @@ " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", " - [`StringableCollectionElement`](/mojo/stdlib/builtin/value/StringableCollectionElement)\n", - " - [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising)\n", - " - [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", " - [`Roundable`](/mojo/stdlib/builtin/math/Roundable)\n", " - [`ToFormatter`](/mojo/stdlib/utils/_format/ToFormatter)\n", " - [`Truncable`](/mojo/stdlib/builtin/_math/Truncable)\n", @@ -517,135 +514,50 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The `Intable` and `IntableRaising` traits\n", + "### The `Intable` and `Stringable` traits\n", "\n", - "The [`Intable`](/mojo/stdlib/builtin/int/Intable) trait identifies a type that\n", - "can be implicitly converted to `Int`. The\n", - "[`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) trait describes a\n", - "type can be converted to an `Int`, but the conversion might raise an error.\n", + "The [`Intable`](/mojo/stdlib/builtin/int/Intable) and \n", + "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) traits identify types\n", + "that can be implicitly converted to `Int` and `String`, respectively. \n", "\n", - "Both of these traits require the type to implement the `__int__()` method. For\n", - "example:" + "Any type that conforms to `Stringable` works with the built-in\n", + "[`print()`](/mojo/stdlib/builtin/io/print) and \n", + "[`str()`](/mojo/stdlib/builtin/str/str) functions:" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "True\n" + "This is a dog named Spot\n" ] } ], "source": [ "@value\n", - "struct Foo(Intable):\n", - " var i: Int\n", + "struct Pet(Stringable):\n", + " var name: String\n", + " var type: String\n", "\n", - " fn __int__(self) -> Int:\n", - " return self.i\n", + " fn __str__(self) -> String:\n", + " return \"This is a \" + self.type + \" named \" + self.name\n", "\n", - "var foo = Foo(42)\n", - "print(int(foo) == 42)" + "var spot = Pet(\"Spot\", \"dog\")\n", + "print(spot)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### The `Stringable`, `Representable`, and `Formattable` traits\n", - "\n", - "The [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait identifies a type\n", - "that can be implicitly converted to\n", - "[`String`](/mojo/stdlib/collections/string/String). The\n", - "[`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) trait\n", - "describes a type that can be converted to a `String`, but the conversion might\n", - "raise an error. Any type that conforms to `Stringable` or `StringableRaising`\n", - "also works with the built-in [`str()`](/mojo/stdlib/builtin/str/str) function to\n", - "explicitly return a `String`. These traits also mean that the type can support\n", - "both the `{!s}` and `{}` format specifiers of the `String` class'\n", - "[`format()`](/mojo/stdlib/collections/string/String#format) method. These traits\n", - "require the type to define the\n", - "[`__str__()`](/mojo/stdlib/builtin/str/Stringable#__str__) method.\n", - "\n", - "In contrast, the [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", - "trait that defines a type that can be used with the built-in\n", - "[`repr()`](/mojo/stdlib/builtin/repr/repr) function, as well as the `{!r}`\n", - "format specifier of the `format()` method. This trait requires the type to\n", - "define the [`__repr__()`](/mojo/stdlib/builtin/repr/Representable#__repr__)\n", - "method, which should compute the \"official\" string representation of a type. If\n", - "at all possible, this should look like a valid Mojo expression that could be\n", - "used to recreate a struct instance with the same value.\n", - "\n", - "The [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", - "trait denotes a trait composition of the `Stringable` and `Representable`\n", - "traits. It requires a type to implement both a `__str__()` and a `__repr__()`\n", - "method.\n", - "\n", - "The [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait describes a\n", - "type that can be converted to a stream of UTF-8 encoded data by writing to a\n", - "formatter object. The [`print()`](/mojo/stdlib/builtin/io/print) function\n", - "requires that its arguments conform to the `Formattable` trait. This enables\n", - "efficient stream-based writing by default, avoiding unnecessary intermediate\n", - "String heap allocations.\n", - "\n", - "The `Formattable` trait requires a type to implement a\n", - "[`format_to()`](/mojo/stdlib/utils/format/Formattable#format_to) method, which\n", - "is provided with an instance of\n", - "[`Formatter`](/mojo/stdlib/utils/format/Formatter) as an argument. You then\n", - "invoke the `Formatter` instance's\n", - "[`write()`](/mojo/stdlib/utils/format/Formatter#write) method to write a\n", - "sequence of `Formattable` arguments constituting the `String` representation of\n", - "your type.\n", - "\n", - "While this might sound complex at first, in practice you can minimize\n", - "boilerplate and duplicated code by using the [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) static function to\n", - "implement the type's `Stringable` implementation in terms of its `Formattable`\n", - "implementation. Here is a simple example of a type that implements all of the\n", - "`Stringable`, `Representable`, and `Formattable` traits:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dog(name='Rex', age=5)\n", - "Dog(Rex, 5)\n", - "String: Dog(Rex, 5)\n", - "Representation: Dog(name='Rex', age=5)\n" - ] - } - ], - "source": [ - "@value\n", - "struct Dog(Stringable, Representable, Formattable):\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __repr__(self) -> String:\n", - " return \"Dog(name=\" + repr(self.name) + \", age=\" + repr(self.age) + \")\"\n", - "\n", - " fn __str__(self) -> String:\n", - " return String.format_sequence(self)\n", - "\n", - " fn format_to(self, inout writer: Formatter) -> None:\n", - " writer.write(\"Dog(\", self.name, \", \", self.age, \")\")\n", - "\n", - "var dog = Dog(\"Rex\", 5)\n", - "print(repr(dog))\n", - "print(dog)\n", - "\n", - "var dog_info = String(\"String: {!s}\\nRepresentation: {!r}\").format(dog, dog)\n", - "print(dog_info)" + "Similarly, an `Intable` type works with the built-in \n", + "[`int`](/mojo/stdlib/builtin/int/int-function) function. You can find an example\n", + "in the [`Intable` API reference](/mojo/stdlib/builtin/int/Intable)." ] }, { diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 94690bd6d1..b8630ad8d7 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -32,10 +32,10 @@ "\n", "Mojo comes with a standard library that provides a number of useful types and\n", "utility functions. These standard types aren’t privileged. Each of the standard\n", - "library types is defined just like user-defined types—even basic types like\n", - "[`Int`](/mojo/stdlib/builtin/int/Int) and\n", - "[`String`](/mojo/stdlib/collections/string/String). But these standard library\n", - "types are the building blocks you'll use for most Mojo programs.\n", + "library types is defined just like user-defined types—even basic types like \n", + "[`Int`](/mojo/stdlib/builtin/int/Int) and \n", + "[`String`](/mojo/stdlib/builtin/string/String). But these standard library types\n", + "are the building blocks you'll use for most Mojo programs.\n", "\n", "The most common types are _built-in types_, which are always available and\n", "don't need to be imported. These include types for numeric values, strings,\n", diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 0c6e34c116..7fbd408e8b 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -24,16 +24,7 @@ "registers, but we won't get into that here). However, each language reads and\n", "writes data a bit differently—sometimes very differently. So in the following\n", "sections, we'll explain how Mojo manages memory in your programs and how this\n", - "affects the way you write Mojo code.\n", - "\n", - ":::note\n", - "\n", - "For an alternate introduction to ownership in Mojo, check out our two-part blog\n", - "post: \n", - "[What ownership is really about: a mental model approach](https://www.modular.com/blog/what-ownership-is-really-about-a-mental-model-approach), and [Deep dive into\n", - "ownership in Mojo](https://www.modular.com/blog/deep-dive-into-ownership-in-mojo).\n", - "\n", - ":::" + "affects the way you write Mojo code." ] }, { @@ -42,41 +33,21 @@ "source": [ "## Stack and heap overview\n", "\n", - "In general, all modern programming languages divide a running program's memory\n", - "into four segments:\n", - "\n", - "- Text. The compiled program.\n", - "- Data. Global data, either initialized or uninitialized.\n", - "- Stack. Local data, automatically managed during the program's runtime.\n", - "- Heap. Dynamically-allocated data, managed by the programmer.\n", - "\n", - "The text and data segments are statically sized, but the stack and heap change\n", - "size as the program runs.\n", - "\n", - "The _stack_ stores data local to the current function. When a function is\n", - "called, the program allocates a block of memory—a _stack frame_—that is exactly\n", - "the size required to store the function's data, including any _fixed-size_\n", - "local variables. When another function is called, a new stack frame is pushed\n", - "onto the top of the stack. When a function is done, its stack frame is popped\n", - "off the stack. \n", + "In general, all programming languages use a call stack the same way: When a\n", + "function is called, the compiler allocates a block of memory on the stack that\n", + "is exactly the size required to store the execution logic and _fixed-size_\n", + "local values. When another function is called, its data is likewise added to\n", + "the top of the stack. When a function is done, all its data in the stack is\n", + "destroyed so that memory becomes available for other code.\n", "\n", "Notice that we said only \"_fixed-size_ local values\" are stored in the stack.\n", "Dynamically-sized values that can change in size at runtime are instead\n", "stored in the heap, which is a much larger region of memory that allows for\n", - "dynamic memory allocation. Technically, a local variable for such a value\n", + "dynamic memory access at runtime. Technically, a local variable for such a value\n", "is still stored in the call stack, but its value is a fixed-size pointer to the\n", - "real value on the heap. Consider a Mojo string: it can be any length, and \n", - "its length can change at runtime. So the Mojo `String` struct includes some statically-sized fields, plus a pointer to a dynamically-allocated buffer\n", - "holding the actual string data.\n", + "real value on the heap.\n", "\n", - "Another important difference between the heap and the stack is that the stack is \n", - "managed automatically—the code to push and pop stack frames is added by the\n", - "compiler. Heap memory, on the other hand, is managed by the programmer\n", - "explicitly allocating and deallocating memory. You may do this indirectly—by\n", - "using standard library types like `List` and `String`—or directly, using the \n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) API.\n", - "\n", - "Values that need to outlive the lifetime of a function (such as\n", + "Additionally, values that need to outlive the lifetime of a function (such as\n", "an array that's passed between functions and should not be copied) are stored\n", "in the heap, because heap memory is accessible from anywhere in the call stack,\n", "even after the function that created it is removed from the stack. This sort of\n", @@ -119,13 +90,11 @@ "\n", "Mojo uses a third approach called \"ownership\" that relies on a collection of\n", "rules that programmers must follow when passing values. The rules ensure there\n", - "is only one \"owner\" for a given value at a time. When a value's lifetime ends,\n", - "Mojo calls its destructor, which is responsible for deallocating any heap memory\n", - "that needs to be deallocated.\n", - "\n", - "In this way, Mojo helps ensure memory is freed, but it does so in a way that's\n", - "deterministic and safe from errors such as use-after-free, double-free and\n", - "memory leaks. Plus, it does so with a very low performance overhead.\n", + "is only one \"owner\" for each chunk of memory at a time, and that the memory is\n", + "deallocated accordingly. In this way, Mojo automatically allocates and\n", + "deallocates heap memory for you, but it does so in a way that's deterministic\n", + "and safe from errors such as use-after-free, double-free and memory leaks. Plus,\n", + "it does so with a very low performance overhead.\n", "\n", "Mojo's value ownership model provides an excellent balance of programming\n", "productivity and strong memory safety. It only requires that you learn some new\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb deleted file mode 100644 index b66f8828eb..0000000000 --- a/docs/manual/values/lifetimes.ipynb +++ /dev/null @@ -1,559 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Lifetimes and references\n", - "sidebar_position: 4\n", - "description: Working with lifetimes and references.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note Work in progress\n", - "\n", - "Both lifetimes and references are a work in progress and subject to change in\n", - "future releases. \n", - "\n", - ":::\n", - "\n", - "In Mojo, _lifetime_ has two meanings: \n", - "\n", - "- In general terms, a value's lifetime refers to the span of time when the \n", - " value is valid. \n", - "\n", - "- It also refers to a specific type of parameter value used to help track the \n", - " lifetimes of values and references to values. For clarity, we'll use\n", - " `lifetime` in code font to refer to the type.\n", - "\n", - "The Mojo compiler includes a lifetime checker, a compiler pass that analyzes\n", - "dataflow through your program. It identifies when variables are valid and \n", - "inserts destructor calls when a value's lifetime ends.\n", - "\n", - "The Mojo compiler uses `lifetime` values to track the validity of references.\n", - "Specifically, a `lifetime` value answers two questions:\n", - "\n", - "- What logical storage location \"owns\" this value?\n", - "- Can the value be mutated using this reference?\n", - "\n", - "For example, consider the following code:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Joan\n" - ] - } - ], - "source": [ - "fn print_str(s: String):\n", - " print(s)\n", - "\n", - "name = String(\"Joan\")\n", - "print_str(name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The line `name = String(\"Joan\")` declares a variable with an identifier (`name`)\n", - "and logical storage space for a `String` value. When you pass `name` into the\n", - "`print_str()` function, the function gets an immutable reference to the value. \n", - "So both `name` and `s` refer to the same logical storage space, and have\n", - "associated `lifetime` values that lets the Mojo compiler reason about them. \n", - "\n", - "Most of the time, `lifetime` values are handled automatically by the compiler. \n", - "However, in some cases you'll need to interact with `lifetime` values directly:\n", - "\n", - "- When working with references—specifically `ref` arguments and `ref` return\n", - " values. \n", - "\n", - "- When working with types like \n", - " [`Reference`](/mojo/stdlib/memory/reference/Reference) or \n", - " [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the \n", - " `lifetime` of the data they refer to.\n", - "\n", - "This section covers [`ref` arguments](#ref-arguments) and \n", - "[`ref` return values](#ref-return-values), which let functions\n", - "take arguments and provide return values as references with parametric\n", - "lifetimes." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Working with lifetimes\n", - "\n", - "Mojo's `lifetime` values are unlike most other values in the language, because\n", - "they're primitive values, not Mojo structs. Specifying a parameter that takes a \n", - "`lifetime` value, you can't just say, `l: Lifetime`, because there's no \n", - "`Lifetime` type. Likewise, because these values are mostly created by the \n", - "compiler, you can't just create your own `lifetime` value—you usually need to \n", - "derive a `lifetime` from an existing value.\n", - "\n", - "### Lifetime types\n", - "\n", - "Mojo supplies a struct and a set of aliases that you can use to specify \n", - "`lifetime` types. As the names suggest, the `ImmutableLifetime` and \n", - "`MutableLifetime` aliases represent immutable and mutable lifetimes, \n", - "respectively:\n", - "\n", - "```mojo\n", - "struct ImmutableRef[lifetime: ImmutableLifetime]:\n", - " pass\n", - "```\n", - "\n", - "Or you can use the [`AnyLifetime`](mojo/stdlib/builtin/type_aliases/AnyLifetime)\n", - "struct to specify a lifetime with parametric mutability:" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [], - "source": [ - "struct ParametricRef[\n", - " is_mutable: Bool,\n", - " //,\n", - " lifetime: AnyLifetime[is_mutable].type\n", - "]:\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that `AnyLifetime` _isn't a lifetime value_, it's a helper for specifying a \n", - "`lifetime` **type**. Lifetime types carry the mutability of a reference as a \n", - "boolean parameter value, indicating whether the lifetime is mutable, immutable,\n", - "or even with mutability depending on a parameter specified by the enclosing API.\n", - "\n", - "The `is_mutable` parameter here is an [infer-only\n", - "parameter](/mojo/manual/parameters/#infer-only-parameters). It's never\n", - "specified directly by the user, but always inferred from context. The\n", - "`lifetime` value is often inferred, as well. For example, the following code\n", - "creates a [`Reference`](/mojo/stdlib/memory/reference/Reference) to an existing\n", - "value, but doesn't need to specify a lifetime—the `lifetime` is inferred from\n", - "the variable passed in to the reference." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import Reference\n", - "\n", - "def use_reference():\n", - " a = 10\n", - " r = Reference(a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Lifetime values\n", - "\n", - "Most `lifetime` values are created by the compiler. As a developer, there are a\n", - "few ways to specify `lifetime` values:\n", - "\n", - "- Static lifetimes. The `ImmutableStaticLifetime` and `MutableStaticLifetime`\n", - " aliases are lifetimes that last for the duration of the program. \n", - "- The `__lifetime_of()` magic function, which returns the lifetime associated\n", - " with the value (or values) passed in.\n", - "- Inferred lifetime. You can use inferred parameters to capture the lifetime\n", - " of a value passed in to a function.\n", - "\n", - "#### Static lifetimes\n", - "\n", - "You can use the static lifetimes `ImmutableStaticLifetime` and \n", - "`MutableStaticLifetime` when you have a value that should never be destroyed;\n", - "or when there's no way to construct a meaningful `lifetime` for a value.\n", - "\n", - "For an example of the first case, the `StringLiteral` method\n", - "[`as_string_slice()`](/mojo/stdlib/builtin/string_literal/StringLiteral#as_string_slice)\n", - "returns a [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) pointing\n", - "to the original string literal. String literals are static—they're allocated at\n", - "compile time and never destroyed—so the slice is created with an immutable,\n", - "static lifetime.\n", - "\n", - "Converting an\n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) into a\n", - "`Reference` is an example of the second case: the `UnsafePointer`'s data\n", - "doesn't carry a `lifetime`—one reason that it's considered unsafe—but you need\n", - "to specify a `lifetime` when creating a `Reference`. In this case, there's no\n", - "way to construct a meaningful `lifetime` value, so the new `Reference` is\n", - "constructed with a `MutableStaticLifetime`. Mojo won't destroy this value\n", - "automatically. As with any value stored using a pointer, it's up to the user to\n", - "explicitly [destroy the\n", - "value](/mojo/manual/pointers#destroying-or-removing-values).\n", - "\n", - "#### Derived lifetimes\n", - "\n", - "Use the `__lifetime_of()` magic function to obtain a value's lifetime. This can \n", - "be useful, for example, when creating a container type. Consider the `List`\n", - "type. Subscripting into a list (`list[4]`) returns a reference to the item at\n", - "the specified position. The signature of the `__getitem__()` method that's\n", - "called to return the subscripted item looks like this:\n", - "\n", - "```mojo\n", - "fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T:\n", - "```\n", - "\n", - "The syntax may be unfamiliar—`ref` arguments and `ref` return values are\n", - "described in the following sections. For now it's enough to know that \n", - "the return value is a reference of type `T` (where `T` is the element type\n", - "stored in the list), and the reference has the same lifetime as the list itself.\n", - "This means that as long as you hold the reference, the underlying list won't be\n", - "destroyed.\n", - "\n", - ":::note\n", - "\n", - "Ideally the returned reference's `lifetime` would be linked to the individual\n", - "list item, rather than the list itself. Mojo doesn't yet have a mechanism to \n", - "express this relationship.\n", - "\n", - ":::\n", - "\n", - "#### Inferred lifetimes\n", - "\n", - "The other common way to access a lifetime value is to _infer_ it from the\n", - "the arguments passed to a function or method. For example, the `Span` type\n", - "has an associated `lifetime`:\n", - "\n", - "```mojo\n", - "struct Span[\n", - " is_mutable: Bool, //,\n", - " T: CollectionElement,\n", - " lifetime: AnyLifetime[is_mutable].type,\n", - "](CollectionElementNew):\n", - " \"\"\"A non owning view of contiguous data.\n", - "```\n", - "\n", - "One of its constructors creates a `Span` from an existing `List`, and infers\n", - "its `lifetime` value from the list:\n", - "\n", - "```mojo\n", - " fn __init__(inout self, ref [lifetime]list: List[T, *_]):\n", - " \"\"\"Construct a Span from a List.\n", - "\n", - " Args:\n", - " list: The list to which the span refers.\n", - " \"\"\"\n", - " self._data = list.data\n", - " self._len = len(list)\n", - "```\n", - "\n", - "## Working with references\n", - "\n", - "You can use the `ref` keyword with arguments and return values to specify a \n", - "reference with parametric mutability. That is, they can be either mutable or \n", - "immutable.\n", - "\n", - "These references shouldn't be confused with the `Reference` type, which is\n", - "basically a safe pointer type. A `Reference` needs to be dereferenced, like a \n", - "pointer, to access the underlying value. A `ref` argument, on the other hand,\n", - "looks like a `borrowed` or `inout` argument inside the function. A `ref` return\n", - "value looks like any other return value to the calling function, but it is a\n", - "_reference_ to an existing value, not a copy.\n", - "\n", - "### `ref` arguments\n", - "\n", - "The `ref` argument convention lets you specify an argument of parametric\n", - "mutability: that is, you don't need to know in advance whether the passed\n", - "argument will be mutable or immutable. There are several reasons you might want\n", - "to use a `ref` argument:\n", - "\n", - "- You want to accept an argument with parametric mutability.\n", - "\n", - "- You want to tie the lifetime of one argument to the lifetime of another\n", - " argument.\n", - "\n", - "- When you want an argument that is guaranteed to be passed in memory: this can\n", - " be important and useful for generic arguments that need an identity,\n", - " irrespective of whether the concrete type is register passable.\n", - "\n", - "The syntax for a `ref` argument is:\n", - "\n", - "ref [lifetime] argName: argType\n", - "\n", - "The `lifetime` parameter passed inside the square brackets can be replaced with\n", - "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", - "of it as a wildcard that will accept any lifetime:" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [], - "source": [ - "def add_ref(ref [_] a: Int, b: Int) -> Int:\n", - " return a+b" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also name the lifetime explicitly. This is useful if you want to specify\n", - "an `ImmutableLifetime` or `MutableLifetime`, or if you want to bind to\n", - "the `is_mutable` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Immutable: Hello\n", - "Mutable: Goodbye\n" - ] - } - ], - "source": [ - "def take_str_ref[\n", - " is_mutable: Bool, //,\n", - " life: AnyLifetime[is_mutable].type\n", - " ](ref [life] s: String):\n", - " @parameter\n", - " if is_mutable:\n", - " print(\"Mutable: \" + s)\n", - " else:\n", - " print(\"Immutable: \" + s)\n", - "\n", - "def pass_refs(s1: String, owned s2: String):\n", - " take_str_ref(s1)\n", - " take_str_ref(s2)\n", - "\n", - "pass_refs(\"Hello\", \"Goodbye\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `ref` return values\n", - "\n", - "Like `ref` arguments, `ref` return values allow a function to return a mutable\n", - "or immutable reference to a value. Like a `borrowed` or `inout` argument, these\n", - "references don't need to be dereferenced.\n", - "\n", - "`ref` return values can be an efficient way to handle updating items in a \n", - "collection. The standard way to do this is by implementing the `__getitem__()`\n", - "and `__setitem__()` dunder methods. These are invoked to read from and write to \n", - "a subscripted item in a collection:\n", - "\n", - "```mojo\n", - "value = list[a]\n", - "list[b] += 10\n", - "```\n", - "\n", - "With a `ref` argument, `__getitem__()` can return a mutable reference that can\n", - "be modified directly. This has pros and cons compared to using a `__setitem__()`\n", - "method:\n", - "\n", - "- The mutable reference is more efficient—a single update isn't broken up across\n", - " two methods. However, the referenced value must be in memory.\n", - " \n", - "- A `__getitem__()`/`__setitem__()` pair allows for arbitrary to be run when\n", - " values are retrieved and set. For example, `__setitem__()` can validate or \n", - " constrain input values.\n", - "\n", - "For example, in the following example, `NameList` has a `get()` method\n", - "that returns a reference: " - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dana\n", - "Dana?\n" - ] - } - ], - "source": [ - "struct NameList:\n", - " var names: List[String]\n", - "\n", - " def __init__(inout self, *names: String):\n", - " self.names = List[String]()\n", - " for name in names:\n", - " self.names.append(name[])\n", - "\n", - " def __getitem__(ref [_] self: Self, index: Int) ->\n", - " ref [__lifetime_of(self)] String:\n", - " if (index >=0 and index < len(self.names)):\n", - " return self.names[index]\n", - " else:\n", - " raise Error(\"index out of bounds\")\n", - "\n", - "def use_name_list():\n", - " list = NameList(\"Thor\", \"Athena\", \"Dana\", \"Vrinda\")\n", - " print(list[2])\n", - " list[2] += \"?\"\n", - " print(list[2])\n", - "\n", - "use_name_list()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that this update succeeds, even though `NameList` doesn't define a\n", - "`__setitem__()` method:\n", - "\n", - "```mojo\n", - "list[2] += \"?\"\n", - "```\n", - "\n", - "Also note that the code uses the return value directly each time, rather than\n", - "assigning the return value to a variable, like this:\n", - "\n", - "```mojo\n", - "name = list[2]\n", - "```\n", - "\n", - "Since a variable needs to own its value, `name` would end up with an owned \n", - "_copy_ of the value that `list[2]` returns. Mojo doesn't currently have \n", - "syntax to express that you want to keep the original reference in `name`. This\n", - "will be added in a future release.\n", - "\n", - "In cases where you need to be able to assign the return value to a variable—for\n", - "example, an iterator which will be used in a `for..in` loop—you might consider \n", - "returning a `Reference` instead of a `ref` return value. For example, see the \n", - "[iterator for the `List` \n", - "type](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L60).\n", - "You can assign a `Reference` to a variable, but you need to use the dereference\n", - "operator (`[]`) to access the underlying value." - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n", - "3\n" - ] - } - ], - "source": [ - "nums = List(1, 2, 3)\n", - "for item in nums: # List iterator returns a Reference\n", - " print(item[])\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Parametric mutability of return values\n", - "\n", - "Another advantage of `ref` return arguments is the ability to support parametric\n", - "mutability. For example, recall the signature of the `__getitem__()` method\n", - "above:\n", - "\n", - "```mojo\n", - "def __getitem__(ref [_] self: Self, index: Int) ->\n", - " ref [__lifetime_of(self)] String:\n", - "```\n", - "\n", - "Since the `lifetime` of the return value is tied to the lifetime of `self`, the\n", - "returned reference will be mutable if the method was called using a\n", - "mutable reference. The method still works if you have an immutable reference\n", - "to the `NameList`, but it returns an immutable reference:" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Diana\n" - ] - } - ], - "source": [ - "fn pass_immutable_list(list: NameList) raises:\n", - " print(list[2])\n", - " # list[2] += \"?\" # Error, this list is immutable\n", - "\n", - "def use_name_list_again():\n", - " list = NameList(\"Sophie\", \"Jack\", \"Diana\")\n", - " pass_immutable_list(list)\n", - "\n", - "use_name_list_again()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Without parametric mutability, you'd need to write two versions of \n", - "`__getitem__()`, one that accepts an immutable `self` and another that accepts\n", - "a mutable `self`. " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 4d9ffd533c..447c0dd528 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -28,14 +28,11 @@ "\n", "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", "each value at a time, while still allowing you to share references with other\n", - "functions. When the life span of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death). Programmers are still responsible for\n", - "making sure any type that allocates resources (including memory) also\n", - "deallocates those resources in its destructor. Mojo's ownership system ensures\n", - "that destructors are called promptly.\n", - "\n", - "On this page, we'll explain the rules that govern this ownership model, and how\n", - "to specify different argument conventions that define how values are passed into\n", + "functions. When the lifetime of the owner ends, Mojo [destroys the\n", + "value](/mojo/manual/lifecycle/death).\n", + "\n", + "On this page, we'll explain the rules that govern this ownership model and how\n", + "to specify different argument conventions that define how values are shared into\n", "functions." ] }, @@ -43,7 +40,6 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n", "## Argument conventions\n", "\n", "In all programming languages, code quality and performance is heavily dependent\n", @@ -74,23 +70,18 @@ " function can read and mutate the original value (it is *not* a copy).\n", " \n", "- `owned`: The function takes **ownership**. This means the function has\n", - " exclusive ownership of the argument. Often, this also implies that the caller\n", + " exclusive mutable access to the argument—the function caller does not have\n", + " access to this value (anymore). Often, this also implies that the caller\n", " should transfer ownership to this function, but that's not always what\n", " happens and this might instead be a copy (as you'll learn below).\n", "\n", - "- `ref`: The function gets a reference with an associated lifetime. The\n", - " reference can be either mutable or immutable. You can think of `ref` arguments\n", - " as a generalization of the `borrowed` and `inout` conventions. `ref` arguments\n", - " are an advanced topic, and they're described in more detail in [Lifetimes and \n", - " references](/mojo/manual/values/lifetimes).\n", - "\n", "For example, this function has one argument that's a mutable\n", "reference and one that's immutable:" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -140,7 +131,7 @@ "\n", "- The `owned` argument always gets a uniquely owned value, which may have been\n", " copied or transferred from the callee. Using `owned` arguments without the \n", - " transfer sigil (`^`) usually results in values being copied.\n", + " transfer operator (`^`) usually results in values being copied.\n", "\n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." @@ -156,7 +147,6 @@ "\n", "- Every value has only one owner at a time.\n", "- When the lifetime of the owner ends, Mojo destroys the value.\n", - "- If there are outstanding references to a value, Mojo keeps the value alive.\n", "\n", "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", "that only one mutable reference to a value can exist at a time. **This is not\n", @@ -201,42 +191,38 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[1, 2, 3, 4]\n" + "256x256\n" ] } ], "source": [ - "from collections import List\n", + "from max.tensor import Tensor, TensorShape\n", "\n", - "def print_list(list: List[Int]):\n", - " print(list.__str__())\n", + "def print_shape(tensor: Tensor[DType.float32]):\n", + " shape = tensor.shape()\n", + " print(str(shape))\n", "\n", - "var list = List(1, 2, 3, 4)\n", - "print_list(list)\n" + "var tensor = Tensor[DType.float32](256, 256)\n", + "print_shape(tensor)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here the `list` argument to `print_list()` is borrowed and not mutated, so the \n", - "`print_list()` function gets an immutable reference to the original `List`, and\n", - "doesn't do any copying. \n", - "\n", - "In general, passing an immutable reference is much more efficient\n", + "Here the `tensor` argument is borrowed and not mutated, so the `print_shape()`\n", + "function gets an immutable reference to the original `Tensor`, and doesn't do \n", + "any copying. In general, passing an immutable reference is much more efficient\n", "when handling large or expensive-to-copy values, because the copy constructor\n", "and destructor are not invoked for a borrow.\n", "\n", - "To avoid expensive copies, types should only be implicitly copyable if the copy\n", - "operation is inexpensive.\n", - "\n", "### Compared to C++ and Rust\n", "\n", "Mojo's borrowed argument convention is similar in some ways to passing an\n", @@ -275,64 +261,59 @@ "means any changes to the value *in*side the function are visible *out*side the\n", "function.\n", "\n", - "For example, this `mutate()` function updates the original `list` value:" + "For example, this `mutate()` function updates the original `x` value:" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[1, 2, 3, 4, 5]\n" + "2\n" ] } ], "source": [ - "from collections import List\n", - "\n", - "def mutate(inout l: List[Int]):\n", - " l.append(5)\n", - "\n", - "var list = List(1, 2, 3, 4)\n", + "def mutate(inout y: Int):\n", + " y += 1\n", "\n", - "mutate(list)\n", - "print_list(list)" + "var x = 1\n", + "mutate(x)\n", + "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "That behaves like an optimized replacement for this:" + "That behaves like an optimized shorthand for this:" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[1, 2, 3, 4, 5]\n" + "2\n" ] } ], "source": [ - "from collections import List\n", + "def mutate_copy(y: Int) -> Int:\n", + " y += 1\n", + " return y\n", "\n", - "def mutate_copy(l: List[Int]) -> List[Int]:\n", - " l.append(5)\n", - " return l\n", - "\n", - "var list = List(1, 2, 3, 4)\n", - "list = mutate_copy(list)\n", - "print_list(list)" + "var x = 1\n", + "x = mutate_copy(x)\n", + "print(x)" ] }, { @@ -349,61 +330,18 @@ "\n", ":::note\n", "\n", - "You cannot define [default\n", - "values](/mojo/manual/functions#optional-arguments) for `inout`\n", - "arguments.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Argument exclusivity\n", - "\n", - "Mojo enforces _argument exclusivity_ for mutable references. This means that if\n", - "a function receives a mutable reference to a value (such as an `inout` argument),\n", - "it can't receive any other references to the same value—mutable or immutable.\n", - "That is, a mutable reference can't have any other references that _alias_ it.\n", - "\n", - "For example, consider the following code example:\n", + "Notice that we don't call this argument passing \"by reference.\"\n", + "Although the `inout` convention is conceptually the same, we don't call it\n", + "by-reference passing because the implementation may actually pass values using\n", + "pointers.\n", "\n", - "```mojo\n", - "fn append_twice(inout s: String, other: String):\n", - " # Mojo knows 's' and 'other' cannot be the same string.\n", - " s += other\n", - " s += other\n", - "\n", - "fn invalid_access():\n", - " var my_string = str(\"o\")\n", - "\n", - " # warning: passing `my_string` inout is invalid since it is also passed\n", - " # borrowed.\n", - " append_twice(my_string, my_string)\n", - " print(my_string)\n", - "```\n", - "\n", - "This code is confusing because the user might expect the output to be `ooo`, \n", - "but since the first addition mutates both `s` and `other`, the actual output\n", - "would be `oooo`. Enforcing exclusivity of mutable references not only prevents\n", - "coding errors, it also allows the Mojo compiler to optimize code in some cases.\n", - "\n", - "One way to avoid this issue when you do need both a mutable and an immutable \n", - "reference (or need to pass the same value to two arguments) is to make a copy:\n", - "\n", - "```mojo\n", - "fn valid_access():\n", - " var my_string = str(\"o\")\n", - " var other_string = str(my_string)\n", - " append_twice(my_string, other_string)\n", - " print(my_string)\n", - "```\n", + ":::\n", "\n", - ":::note Only a warning\n", + ":::note\n", "\n", - "Aliasing a mutable reference produces a warning in v24.5. This will change to an\n", - "error in a subsequent release.\n", + "You cannot define [default\n", + "values](/mojo/manual/functions#optional-arguments) for `inout`\n", + "arguments.\n", "\n", ":::" ] @@ -417,20 +355,21 @@ "And finally, if you'd like your function to receive value **ownership**, add the\n", "`owned` keyword in front of the argument name.\n", "\n", - "This convention is often combined with use of the postfixed `^` \"transfer\"\n", - "sigil on the variable that is passed into the function, which ends the\n", + "This convention is usually combined with use of the postfixed `^` \"transfer\"\n", + "operator on the variable that is passed into the function, which ends the\n", "lifetime of that variable.\n", "\n", "Technically, the `owned` keyword does not guarantee that the received value is\n", "_the original value_—it guarantees only that the function\n", - "gets unique ownership of a value. This happens in one of\n", + "gets unique ownership of a value (enforcing [value\n", + "semantics](/mojo/manual/values/value-semantics)). This happens in one of\n", "three ways:\n", "\n", - "- The caller passes the argument with the `^` transfer sigil, which ends the\n", + "- The caller passes the argument with the `^` transfer operator, which ends the\n", "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", "transferred into the function without making a copy of any heap-allocated data.\n", "\n", - "- The caller **does not** use the `^` transfer sigil, in which case, the\n", + "- The caller **does not** use the `^` transfer operator, in which case, the\n", "value is copied into the function argument and the original variable remains\n", "valid. (If the original value is not used again, the compiler may optimize away\n", "the copy and transfer the value).\n", @@ -445,21 +384,18 @@ "\n", " take(str(\"A brand-new String!\"))\n", " ```\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "\n", + "Regardless, when the function declares an argument as `owned`, it can be certain\n", + "that it has unique mutable access to that value. \n", + "\n", "For example, the following code works by making a copy of the string,\n", "because—although `take_text()` uses the `owned` convention—the caller does not\n", - "include the transfer sigil:" + "include the transfer operator:" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -488,7 +424,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "However, if you add the `^` transfer sigil when calling `take_text()`, the\n", + "However, if you add the `^` transfer operator when calling `take_text()`, the\n", "compiler complains about `print(message)`, because at that point, the `message`\n", "variable is no longer initialized. That is, this version does not compile:\n", "\n", @@ -507,7 +443,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -527,39 +463,6 @@ "my_function()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Regardless of how it receives the value, when the function declares an argument\n", - "as `owned`, it can be certain that it has unique mutable access to that value. \n", - "Because the value is owned, the value is destroyed when the function \n", - "exits—unless the function transfers the value elsewhere.\n", - "\n", - "For example, in the following example, `add_to_list()` takes a string and\n", - "appends it to the list. Ownership of the string is transferred to the list, so\n", - "it's not destroyed when the function exits. On the other hand, \n", - "`consume_string()` doesn't transfer its `owned` value out, so the value is \n", - "destroyed at the end of the function." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "from collections import List\n", - "\n", - "def add_to_list(owned name: String, inout list: List[String]):\n", - " list.append(name^)\n", - " # name is uninitialized, nothing to destroy\n", - "\n", - "def consume_string(owned s: String):\n", - " print(s)\n", - " # s is destroyed here" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -567,7 +470,7 @@ ":::note\n", "\n", "Value lifetimes are not fully implemented for top-level code in\n", - "Mojo's REPL, so the transfer sigil currently works as intended only when\n", + "Mojo's REPL, so the transfer operator currently works as intended only when\n", "used inside a function.\n", "\n", ":::" @@ -579,29 +482,24 @@ "source": [ "### Transfer implementation details\n", "\n", - "In Mojo, you shouldn't conflate \"ownership transfer\" with a \"move\n", + "In Mojo, it's important that you not conflate \"ownership transfer\" with a \"move\n", "operation\"—these are not strictly the same thing. \n", "\n", - "There are multiple ways that Mojo can transfer ownership of a value:\n", + "There are multiple ways that Mojo can transfer ownership of a value without\n", + "making a copy:\n", "\n", "- If a type implements the [move\n", " constructor](/mojo/manual/lifecycle/life#move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", - " transferred into a function as an `owned` argument, _and_ the original\n", - " variable's lifetime ends at the same point (with or without use of the `^`\n", - " transfer operator).\n", - "\n", - "- If a type implements the [copy \n", - " constructor](/mojo/manual/lifecycle/life#move-constructor), `__copyinit__()`\n", - " and not `__moveinit__()`, Mojo may copy the value and destroy the old value.\n", + " transferred into a function as an `owned` argument, _and_ the original value's\n", + " lifetime ends at the same point (with or without use of the `^` transfer\n", + " operator).\n", "\n", - "- In some cases, Mojo can optimize away the move operation entirely, leaving the \n", - " value in the same memory location but updating its ownership. In these cases,\n", - " a value can be transferred without invoking either the `__copyinit__()` or \n", - " `__moveinit__()` constructors.\n", + "- If a type hasn't implemented `__moveinit__()` Mojo may transfer ownership by\n", + " simply passing the recipient a reference to the value in the caller's stack.\n", "\n", "In order for the `owned` convention to work _without_ the transfer\n", - "sigil, the value type must be copyable (via `__copyinit__()`)." + "operator, the value type must be copyable (via `__copyinit__()`)." ] }, { @@ -628,7 +526,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -644,7 +542,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This shadow copy typically adds no overhead, because small types\n", + "This shadow copy typically adds no overhead, because references for small types\n", "like `object` are cheap to copy. However, copying large types that allocate heap\n", "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", "copying large numbers of strings.)" diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index fa8bf907d2..903cabd474 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -85,9 +85,6 @@ "both. Neither `x` nor `y` would \"own\" the value, and any variable would be\n", "allowed to reference it and mutate it.\n", "\n", - "Numeric values in Mojo are value semantic because they're trivial types, which\n", - "are cheap to copy. \n", - "\n", "Here's another example with a function:" ] }, @@ -124,7 +121,6 @@ "\n", "If you're familiar with Python, this is probably familiar so far, because the\n", "code above behaves the same in Python. However, Python is not value semantic.\n", - "\n", "It gets complicated, but let's consider a situation in which you call a Python\n", "function and pass an object with a pointer to a heap-allocated value. Python\n", "actually gives that function a reference to your object, which allows the\n", @@ -148,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -175,12 +171,7 @@ "metadata": {}, "source": [ "If this were Python code, the function would modify the original object, because\n", - "Python shares a reference to the original object.\n", - "\n", - "However, not all types are inexpensive to copy. Copying a `String` or `List`\n", - "requires allocating heap memory, so we want to avoid copying one by accident.\n", - "When designing a type like this, ideally you want to prevent _implicit_ copies,\n", - "and only make a copy when it's explicitly requested. " + "Python shares a reference to the original object." ] }, { @@ -210,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -310,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -346,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 9, "metadata": {}, "outputs": [ { diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index d9bbae0d29..8d1dd916b6 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -27,7 +27,7 @@ "\n", "Mojo has two kinds of variables:\n", "\n", - "- Explicitly-declared variables are created with the `var` keyword, and may include\n", + "- Declared variables are created with the `var` keyword, and may include\n", " [type annotations](#type-annotations).\n", "\n", " ```mojo\n", @@ -35,7 +35,7 @@ " var b: Float64 = 3.14\n", " ```\n", " \n", - "- Implicitly-declared variables are created with an assignment statement:\n", + "- Undeclared variables are created with an assignment statement:\n", "\n", " ```mojo\n", " a = 5\n", @@ -87,9 +87,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Implicitly-declared variables\n", + "## Undeclared variables\n", "\n", - "You can create a variable with just a name and a value. For example:" + "Within a `def` function or a REPL environment, you can create a variable with\n", + "just a name and a value. For example:" ] }, { @@ -98,7 +99,7 @@ "metadata": {}, "outputs": [], "source": [ - "name = String(\"Sam\")\n", + "name = str(\"Sam\")\n", "user_id = 0" ] }, @@ -106,34 +107,41 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Implicitly-declared variables are strongly typed: they take the type from the\n", - "first value assigned to them. For example, the `user_id` variable above is type\n", - "`Int`, while the `name` variable is type `String`. You can't assign a string to\n", - "`user_id` or an integer to `name`.\n", - "\n", - "Implicitly-declared variables are scoped at the function level. You create an\n", - "implicitly-declared variable the first time you assign a value to a given name\n", - "inside a function. Any subsequent references to that name inside the function\n", - "refer to the same variable. For more information, see [Variable\n", - "scopes](#variable-scopes), which describes how variable scoping differs between\n", - "explicitly- and implicitly-declared variables." + "Undeclared variables are strongly typed: they take the type from the first value\n", + "assigned to them. For example, the `user_id` variable above is type `Int`, while\n", + "the `name` variable is type `String`. You can't assign a string to `user_id` or an\n", + "integer to `name`. \n", + "\n", + "Undeclared variables are scoped at the function level. You create an undeclared\n", + "variable the first time you assign a value to a given name inside a function. \n", + "Any subsequent references to that name inside the function refer to the same\n", + "variable. For more information, see [Variable scopes](#variable-scopes), which\n", + "describes how variable scoping differs between declared and undeclared\n", + "variables.\n", + "\n", + ":::note\n", + "\n", + "Undeclared variables are not allowed in an `fn` function or as a struct\n", + "field.\n", + "\n", + ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Explicitly-declared variables\n", + "## Declared variables\n", "\n", "You can declare a variable with the `var` keyword. For example:\n", "\n", "```mojo\n", - "var name = String(\"Sam\")\n", + "var name = str(\"Sam\")\n", "var user_id: Int\n", "```\n", "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", - "explicitly-declared variables are typed—either explicitly with a \n", + "declared values are typed—either explicitly with a \n", "[type annotation](#type-annotations) or implicitly when they're initialized with\n", "a value.\n", "\n", @@ -146,17 +154,43 @@ "var user_id: Int = \"Sam\"\n", "```\n", "\n", - "There are several main differences between explicitly-declared variables and\n", - "implicitly-declared variables:\n", + "There are several main differences between declared variables and undeclared\n", + "variables:\n", "\n", - "- An explicitly-declared variable can be declared without initializing it:\n", + "- A declared variable can be declared without initializing it:\n", "\n", " ```mojo\n", " var value: Float64\n", " ```\n", "\n", - "- Explicitly-declared variables follow [lexical scoping](#variable-scopes),\n", - " unlike implicitly-declared variables." + "- Declared variables follow [lexical scoping](#variable-scopes), unlike \n", + " undeclared variables.\n", + "\n", + "- Declared variables can be used in both `def` and `fn` functions.\n", + "\n", + "Using `var` can help prevent runtime errors caused by typos. For example,\n", + "if you misspell the name of an [undeclared variable](#undeclared-variables),\n", + "Mojo simply creates a new variable using the misspelled name. But when all\n", + "mutable variables must be first declared with `var` (which is the case inside\n", + "an `fn` function), then misspellings such as the following are caught by the\n", + "compiler:\n", + "\n", + "```mojo\n", + "var name = \"Sam\"\n", + "# Somewhere later...\n", + "name = \"Sammy\" # This is not allowed in an `fn` function\n", + "```\n", + "\n", + "Although you can use `var` in a `def` function, this benefit is\n", + "realized only when used inside an `fn` function, where the Mojo compiler will\n", + "flag undeclared variables (such as the above `nane`) as unknown declarations.\n", + "\n", + ":::note\n", + "\n", + "When using Mojo in a REPL environment, top-level variables (variables\n", + "outside a function or struct) do not require `var` declarations.\n", + "\n", + ":::" ] }, { @@ -426,11 +460,10 @@ "The lifetime of the inner `num` ends exactly where the `if` code block ends,\n", "because that's the scope in which the variable was defined.\n", "\n", - "This is in contrast to implicitly-declared variables (those without the `var`\n", + "This is in contrast to undeclared variables (those without the `var`\n", "keyword), which use **function-level scoping** (consistent with Python variable\n", - "behavior). That means, when you change the value of an implicitly-declared\n", - "variable inside the `if` block, it actually changes the value for the entire\n", - "function.\n", + "behavior). That means, when you change the value of an undeclared variable\n", + "inside the `if` block, it actually changes the value for the entire function.\n", "\n", "For example, here's the same code but *without* the `var` declarations:" ] @@ -467,7 +500,7 @@ "metadata": {}, "source": [ "Now, the last `print()` function sees the updated `num` value from the inner\n", - "scope, because implicitly-declared variables (Python-style variables) use function-level\n", + "scope, because undeclared variables (Python-style variables) use function-level\n", "scope (instead of lexical scope)." ] } diff --git a/docs/roadmap.md b/docs/roadmap.md index edfe44ec9c..31529aae5e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -33,7 +33,7 @@ If you have encountered any bugs with current Mojo behavior, please If you have ideas about how to improve the core Mojo features, we prefer that you first look for similar topics or start a new conversation about it -on [Discord](https://discord.gg/modular). +in our [GitHub Discussions](https://github.com/modularml/mojo/discussions). We also consider Mojo to be a new member of the Python family, so if you have suggestions to improve the experience with Python, we encourage @@ -66,7 +66,7 @@ subsumed by more general features if time and care were given to broader evaluation. - Third, the Python community should tackle some of these ideas first. It is -important to us that Mojo be a good member of the Python family, +important to us that Mojo be a good member of the Python family (a "Python++"), not just a language with Pythonic syntax. As such, we don't want to needlessly diverge from Python evolution: adding a bunch of features could lead to problems down the road if Python makes incompatible decisions. Such a future @@ -83,6 +83,83 @@ better equipped to evaluate these features, they have mature code bases to evaluate them with, and they have processes and infrastructure for making structured language evolution features. +## Mojo SDK known issues + +The Mojo SDK is still in early development +and currently only available for Ubuntu Linux and macOS (Apple silicon) +systems. Here are some of the notable issues that we plan to fix: + +- Missing native support for Windows, Intel Macs, and Linux distributions + other than Ubuntu. Currently, we support Ubuntu systems with x86-64 + processors and Apple Silicon macOS. Support for more Linux distributions + (including Debian and RHEL) and Windows is in progress. + +- Modular CLI install might fail and require `modular clean` before you + re-install. + + If it asks you to perform auth, run `modular auth ` and use the + `MODULAR_AUTH` value shown for the `curl` command on [the download + page](https://developer.modular.com/download). + +- If you attempt to uninstall Mojo with `modular uninstall`, your subsequent + attempt to install Mojo might fail with an HTTP 500 error code. If so, run + `modular clean` and try again. + +- Mojo REPL might hang (become unresponsive for more than 10 seconds) when + interpreting an expression if your system has 4 GiB or less RAM. If you + encounter this issue, please report it with your system specs. + +Additionally, we're aware of some issues that we might not be able to solve, +but we mention them here with some more information: + +- When installing Mojo, if you receive the error, + `failed to reach URL https://cas.modular.com`, it could be because your + network connection is behind a firewall. Try updating your firewall settings + to allow access to these end points: `https://packages.modular.com` and + `https://cas.modular.com`. Then retry with `modular clean` and + `modular install mojo`. + +- When installing Mojo, if you receive the error, + `gpg: no valid OpenGPG data found`, this is likely because you are located + outside our supported geographies. Due to US export control restrictions, we + are unable to provide access to Mojo to users situated in specific countries. + +- If using Windows Subsystem for Linux (WSL), you might face issues with WSL 1. + We recommend you upgrade to WSL 2. To check the version, run `wsl -l -v`. If + you're running WSL 1, refer to the + [WSL upgrade instructions](https://learn.microsoft.com/en-us/windows/wsl/install#upgrade-version-from-wsl-1-to-wsl-2). + +- When installing on macOS (Apple silicon), the Modular CLI install might fail + with the message: + + ```plaintext + modular: The arm64 architecture is required for this software. + ``` + + This occurs because Apple's Rosetta x86 emulation is active. Check the + following: + + - Right click on the terminal application you use (for example, + `Terminal.app`), click **Get Info**, and make sure the **Open in Rosetta** + checkbox is not selected. + + - Run the following command: + + ```bash + brew config | grep Rosetta + ``` + + If the output shows `Rosetta 2: True`, the x86 version of Homebrew is + installed. + [Uninstall and reinstall Homebrew](https://github.com/homebrew/install#uninstall-homebrew) + before retrying the Modular installation. + + **Note:** Before uninstalling Homebrew, verify that you don't have other + projects specifically depending on the x86 version of Homebrew. + +You can see other [reported issues on +GitHub](https://github.com/modularml/mojo/issues). + ## Small independent features There are a number of features that are missing that are important to round out @@ -111,7 +188,7 @@ to use right now. ## Traits support -Mojo has basic support for +As of v0.6.0 Mojo has basic support for [traits](/mojo/manual/traits). Traits allow you to specify a set of requirements for types to implement. Types can implement those requirements to *conform to* the trait. Traits allow you to write @@ -307,11 +384,9 @@ fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]): ### Scoping and mutability of statement variables Python programmers understand that local variables are implicitly declared and -scoped at the function level. As the Mojo Manual explains, this is supported in -Mojo for -[implicitly-declared variables](/mojo/manual/variables#implicitly-declared-variables). -However, there are some nuances to Python's implicit declaration rules that Mojo -does not match 1-to-1. +scoped at the function level. As the programming manual explains, this feature +is supported in Mojo only inside `def` functions. However, there are some +nuances to Python's implicit declaration rules that Mojo does not match 1-to-1. For example, the scope of `for` loop iteration variables and caught exceptions in `except` statements is limited to the next indentation block, for both `def` @@ -337,6 +412,25 @@ dynamic characteristic of the function. Mojo's lifetime tracker is intentionally simple (so lifetimes are easy to use!), and cannot reason that `i` would be defined even when the loop bounds are constant. +Also stated in the programming manual: in `def` functions, the function +arguments are mutable and re-assignable, whereas in `fn`, function arguments are +rvalues and cannot be re-assigned. The same logic extends to statement +variables, like `for` loop iteration variables or caught exceptions: + +```mojo +def foo(): + try: + bad_function(): + except e: + e = Error() # ok: we can overwrite 'e' + +fn bar(): + try: + bad_function(): + except e: + e = Error() # error: 'e' is not mutable +``` + ### Name scoping of nested function declarations In Python, nested function declarations produce dynamic values. They are @@ -410,9 +504,9 @@ class One: print(One()) # prints '1' ``` -Mojo currently supports similar functionality through the -[`Formattable`](/mojo/stdlib/utils/format/Formattable) trait, so that -`print()` works on all `Formattable` types. Similar support exists for the +Mojo currently supports this feature through the +[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, so that +`print()` works on all `Stringable` types. Similar support exists for the [`int()`](/mojo/stdlib/builtin/int/int-function) and [`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. @@ -542,7 +636,7 @@ print(type(i or s)) # prints ``` In Mojo, given the expression `(a or b)`, the compiler needs to statically -determine a result type that the types of `a` and `b` can both be **converted** to. +determine a result type that the types of `a` and `b` can both be converted to. For example, currently an `Int` can be implicitly converted to a `String`, but a `String` can't be implicitly converted to an `Int`. So given an integer value diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 78dc2e31e7..778cf85631 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -248,6 +248,11 @@ "```" ] }, + { + "cell_type": "raw", + "metadata": {}, + "source": [] + }, { "cell_type": "markdown", "metadata": {}, @@ -311,7 +316,7 @@ "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", "\n", "For more information, see the VS Code documentation for [Launch \n", - "configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", + "configurations]](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", "\n", ":::note Compilation options\n", "\n", @@ -397,8 +402,6 @@ "[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints), and\n", "[triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), \n", "as described in the VS Code documentation.\n", - "The Mojo debugger also supports _error breakpoints_ (also known as \"break on\n", - "raise\"), which break whenever a `raise` statement is executed.\n", "\n", "When debugging Mojo code, the debugger doesn't support function breakpoints,\n", "data breakpoints, or conditional breakpoints based on an expression (it does \n", @@ -435,16 +438,7 @@ "- Right-click on the breakpoint and select **Edit Condition**, or,\n", "- Click the **Edit Condition** icon next to the breakpoint.\n", "\n", - "This brings up the same menu, **next to the breakpoint in the editor tab**.\n", - "\n", - "#### Enable error breakpoints\n", - "\n", - "You can enable and disable error breakpoints in VS Code by selecting \"Mojo\n", - "Raise\" in the **Breakpoints** section of the **Run and Debug** view. If enabled\n", - "during debugging, executing a `raise` statement causes the debugger to stop\n", - "execution and highlight the line of code where the error was raised.\n", - "\n", - "![VS Code window showing a program paused in the debugger with the Run and Debug view visible. The program is paused at a raise statement.](images/break-on-raise.png)" + "This brings up the same menu, **next to the breakpoint in the editor tab**." ] }, { @@ -547,7 +541,7 @@ "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", "triggers the debugger.\n", "\n", - ":::note Assertions\n", + "::: note Assertions\n", "\n", "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", "ways to specify assertions. Assertions also trigger an error, so can open the \n", diff --git a/docs/tools/images/break-on-raise.png b/docs/tools/images/break-on-raise.png deleted file mode 100644 index fd80ab13cc2ee1e261f02652606bc913583984e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341319 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

    Ed4>_jq{m#8BGiu@($ZGKO>{~V=C7s#s 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 z zLH8C;n45&fzJW}EA7&9OWbl?={qKy2q=#|m>>Jo?qSCY|UX;@b4)2N)BSR00mM5!+ z#))x78egpIn;QRm`X?&BO6 z#$@8`!jJ&a?$?T-+4>(5hS9!MXG%lxnrK&g^ibXN+WP0aQ;E*EjeV?+7|c24nNdYYkM~_{(!~(P56Df|Bsedhl_qymX^HwncM;GE#(cJM^A|ngh_|-^A0#p zD)4*%^gM_3`CCZrx=il0xZ2;njLU6L*OOc|f=Ysq?%5Gt&w{a#xG`h|yrER%6Q||B z=zOG~r?*GAtdMQ%_oVEs#XDkLmEKH`<_3YR^qUiW9+o&GL$dWX5U8%`9*&UkFS6C)%3{q)8+=R6s~r#8rZv{1$EHXgq)xzm zafb%i2Gt`8rU`DKdb!VuWZlKd6Grzc;rxNlXGB29dXbKzR!1HD<(&Hg`G~(X_i6)H zR#nn#ewfg83HeA1zBaQb7|CLlsxKc dp_xGLO&O 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ǫCkFu-@r)NUvTggrYu^^5NU~zN{abTl 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 diff --git a/examples/.gitignore b/examples/.gitignore index aa90b40800..79d5db1e7f 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -4,3 +4,4 @@ pixi.lock # Magic env .magic/ +magic.lock diff --git a/examples/README.md b/examples/README.md index 598ad13844..f5d82b7152 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,11 +1,11 @@ # Mojo code examples -A collection of sample programs and Mojo notebooks written in the +A collection of sample programs and Mojo notebooks written in the [Mojo](https://docs.modular.com/mojo/programming-manual.html) programming language. ## Getting Started -Access a Mojo programming environment available from the +Access a Mojo programming environment available from the Mojo product [page](https://www.modular.com/mojo). Git clone the repository of Mojo samples using the command below: @@ -16,24 +16,23 @@ git clone https://github.com/modularml/mojo.git ## Running -If you're using [`magic`](https://docs.modular.com/magic), navigate into -the examples directory and use `magic run`. For example: +Use the following sample command-line to run the programs: ```bash -magic run mojo matmul.mojo +mojo matmul.mojo ``` -You can run the Mojo notebooks using [JupyterLab or Visual Studio +You can run the Mojo notebooks using [JupyterLab or Visual Studio Code](notebooks/README.md) with the Mojo extension available on the Marketplace. ### Mojo SDK Container -The repo also contains a Dockerfile that can be used to create a -Mojo SDK container for developing and running Mojo programs. Use the -container in conjunction with the Visual Studio Code devcontainers +The repo also contains a Dockerfile that can be used to create a +Mojo SDK container for developing and running Mojo programs. Use the +container in conjunction with the Visual Studio Code devcontainers extension to develop directly inside the container. -The Dockerfile also sets up a `conda` environment and by default, +The Dockerfile also sets up a `conda` environment and by default, starts a `jupyter` server (which you can access via the browser). To build a Mojo container, either use @@ -56,11 +55,11 @@ The script also supports building with `podman` instead of `docker`: ./build-image.sh --auth-key \ --use-podman \ --mojo-version 0.3 - + ``` -You can then run with either `docker` or `podman`. In the example below, -we map the ports, bind mount the current directory and open a shell into +You can then run with either `docker` or `podman`. In the example below, +we map the ports, bind mount the current directory and open a shell into the container: ```bash @@ -86,8 +85,8 @@ podman run \ ## License -The Mojo examples and notebooks in this repository are licensed -under the Apache License v2.0 with LLVM Exceptions +The Mojo examples and notebooks in this repository are licensed +under the Apache License v2.0 with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Contributing diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 4ee3854595..7c6eecf342 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -29,10 +29,9 @@ notebooks. Especially if you're developing with Mojo on a remote system, using VS Code is ideal because it allows you to edit and interact with notebooks on the remote machine where you've installed Mojo. -All you need is Mojo and the Jupyter VS Code extension: +All you need is the Mojo SDK and the Jupyter VS Code extension: -1. [Create a new Mojo -project](https://docs.modular.com/mojo/manual/get-started#1-create-a-new-project). +1. Install the [Mojo SDK](https://developer.modular.com/download). 2. Install [Visual Studio Code](https://code.visualstudio.com/) and the [Jupyter @@ -57,40 +56,33 @@ instructions don't support remote access to the JupyterLab). For more details about using JupyterLab, see the complete [JupyterLab installation guide](https://jupyterlab.readthedocs.io/en/latest/getting_started/installation.html). -### 1. Launch JupyterLab +**Note:** You must run this setup on the same machine where you've installed +the [Mojo SDK](https://developer.modular.com/download). However, syntax +highlighting for Mojo code is not currently enabled in JupyterLab (coming soon). -You can use either Magic or conda. +1. Install JupyterLab: -#### Using Magic + ```sh + python3 -m pip install jupyterlab + ``` -If you have [`magic`](https://docs.modular.com/magic) you can run the following -command to launch JupyterLab from this directory: +2. Make sure the user-level `bin` is in your `$PATH`: -```sh -magic run jupyter lab -``` + ```sh + export PATH="$HOME/.local/bin:$PATH" + ``` -After a moment, it will open a browser window with JupterLab running. +3. Launch JupyterLab: -#### Using conda + ```sh + jupyter lab + ``` -Create a Conda environment, activate that enviroment, and install JupyterLab. +4. When you open any of the `.ipynb` notebooks from this repository, JupyterLab + should automatically select the Mojo kernel (which was installed with the + Mojo SDK). -``` sh -# Create a Conda environment if you don't have one -conda create -n mojo-repo -# Activate the environment -conda env update -n mojo-repo -f environment.yml --prune -# run JupyterLab -conda run -n mojo-repo jupyter lab -``` - -After a moment, it will open a browser window with JupterLab running. - -### 2. Run the .ipynb notebooks - -The left nav bar should show all the notebooks in this directory. -Open any `.ipynb` file and start running the code. + Now run some Mojo code! ## Notes and tips diff --git a/examples/notebooks/images/.gitattributes b/examples/notebooks/images/.gitattributes new file mode 100644 index 0000000000..24a8e87939 --- /dev/null +++ b/examples/notebooks/images/.gitattributes @@ -0,0 +1 @@ +*.png filter=lfs diff=lfs merge=lfs -text diff --git a/examples/notebooks/images/background.png b/examples/notebooks/images/background.png index 5b8d32f8f99170ece26120e5b985be0d9dc9fde8..234cd5285c875dc281ad1ace3dd64b2427e651a6 100644 GIT binary patch literal 131 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%0A*>$W1RK?h?H_QK;Z38&v_CP82)Ra{56KQDqtbe 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�>}GpNNNNb85b7E68E3OA91$E|A=+6< zQaGMM>?MU2d!aSdD}WTET>8`k^$ig^E(ik`!YiXh3~eD09EhW{Pgi*%Tnt{aPNAeK z^|8lCkT;q@WorTxndS({R!LPM)m&Z(BfIc8<(_BJG*S_{O?^3Wa=}Wp0 zvMHArF3J>64rKg`AHmQr_yOC-lZqSF8H;a-Rx@NwpK{yAnv|t_a9&BuWefS> z)t#o|AsLtQ-a9Odb5`@>XZKa7cF3j*V(ns|lQqZ_FXjVP%Vp#70Q@<;4^@9gTUCr$Ne&8QkWRe94%3ZXSLmM%QzXCa zzi0LKgoE4GT8OApPha>Dv`a2SDZ&d;=Qxao%s9QL{VYL+_N~VTSx)lBzEnsmwHA$+ zQ6JuJPlQXmM?5WB6S<^#b~t5%w{7(9OHiqmOCVhR$QrvdF`GBC8gRPPRo^S*u*a%b4=BJ%T8WAs53G^u?ZDDGY54&tdFBDzag7VK?;L zlOd{QiTrFC&w{2V*55_eoJUr*di0goS~WZstiU;Z-4^ae-M4+)x7h%g=B4aHPJ^w< zsI@*gkRD7wdiNtL-b*ZUEph2^M0o+wSyug|(2D)6y`5(9>5#S5q$tP&)XHn9aY6K2 zSu(_CR0F%39Rg7_Md2_sMj~8qELCC79Un>icwMZQa{?VES*Woc0zV!tJET(KL)iAY zzNBzPR;M0bOHDtQf+cbd@#N~V(56cEUC`6Nk03;=7-i#;B?x4whf&5{rs>g#OM~z} zNgw|_3m*O_Gg?cb*Jw)3&DJ-p0DZ=z&RY;tmdK|ozRScgFFVStJ3xjqy6*Wtx8DmA zw1!Ra6t4y$TE_?n$kr(6@fk6zKaQhAlh*{%-aD6T!T0OX~p zi^%IkDXg9^&$xU!->nQ;;OkMRH*&?CcsiS}Aun?LllhWu;=Mb0AyDWTg&QV5+LDG; zTWc8EnZocS{kQ(s-}+;J?2rA8zwtM`u(SfzJe|luu>(&Vh*O&_`wEzA;cqkqUYL-| zcA2`kF2i#c?7T{%U|8D;5~$ZOPov>Q{Jq0;%5v^i#RG-cM?|vT{W&dBg1QuDo(cHQsA8SLM}8MulSw$fBw(^dEbQd8bB_fmDP|<+mJEA8Pk8e zQ7pB~`2(*o#_6gCI{>aQE{LJtxmu~RTR_NJMu~<9)RrY&Ncd=s1!m}LEAJ$;!|fqe zd7~6LFeCEj%aF95%i6jPt6@k{?Z|9B0&;i0j+{@@S(pl5M=g9~1HaSRJ5g>Q~|^;Rae zWq&k?Xv14%kB0IFQqXYp6@QPot;V80lVKLNir*kQRsZ9E{Eyz5R%d0WL*HDQl%zm? z^>CS^)c(|(pU!h(>f!Mt>oWvu4fXA%F}F~0PUb>hH(CmPw2C21DzFs1tI+iGRt5qu zq>X(ZVmMBLg|M}9y|>4-XdXCRUc4jdr)k7HS;)$(c@NmJAYQx6f7;m$e(LHmqB7#h zyCP9d8P1^Aek)!G+zEorc(z*1G&68wl44WPdn^u*b1VAY#)CKq{ea4gRfZv^hG1(& zBRHd3#ChyX9bd1t36~Bcg%-jH(Lbua7fD~8eI(^9Fbm4oTyN1FJGV0*r2p*C{;W!N zKBg=@1(VY7akkVHY$wR7#SsXHq`&~KPw#o;i@0rx zkxwH+U+_EkYW$9uL#B!9Fg@ly1ub1$1y}*1*zSN{F?yKm_2e0nO}1Gu3QS>4;3kE+ zA-mz2UzB0ivb6)FPPp!X+MnOzE zT(!wg6Apamzu83cvS(yRW;e`>uLVA43D=u4uXXS5keF;6g@`Kx0`Hb({1tF20}<*O zwF`*<&hz5Gr)GRT9f!|dUwO}pAi&j6H@@CM58-`mG3$|AF+CgD>QP{8!Xpr5dm-mr zB9_Qd<5HN$Wk|tWXbS$3ANi5L^q2mU2O4ji;lyOva%{c3X|bKF(phayMj{%)d;VJi z4P^<@iv{+4++WzpMO<6RX&^MrKx>Yq=#(1hO08Fw7Zu*=H{>d?bbe9e`M`sM(={o5 zNlCHS#BL&t;nZqCfp9~1!w@#LwxVCS{=`rG1eyyLX8iS-?1p(8)`!G7y~8bnh4mNQ z|KUIUheA3W9#Y-8TOak*N&!@u)-b~osSUzFKGS9E%cy53k|Bync$)gNO)x308QB{$ zDz#i}%4c{psz|u-F=sQf#S(Pg$Em>~wL}OS^5(il-W%|p`BZH*TY{kd?-JjBE*9Bm z0v;~gV@p?&a)mhic6EYONr|Qt8OUo2D%?yvxGRj2t7=cR!pAJTtEInZ(g~i6c@%H|ca-z$Pvtk-CgKpd@1`#W z9m;Tt9IsD4`Q$JE<-hFe=fG9TsT>(!gCi)odMeTo4hhs_>H{ap(75D1^ZD zN4_oDyQ}CK9ypiTc`iqQ+^T=~!8jyA^;8Vw)Oob4Bs_iBc|&iSeEYY5dmF=nvmn5g zed?SF<0MrX*=QMh1!tc+i@kt`$W9_PeD$ke{ox<}Vc)x(T2X19fhXa_Q!7e&Crwpw zZ-n^~kr$Sa6^_in6s|F9!0-YahS+}F8{wCbcN)+9OD(~L%sA6M6Zy?h!Jo(m7 zE99cit5r~Tv?iR3m*H-7S$9KfYSKhit6pp&j5xv(>c=dR-kO@#Z~JY(&DyziLY8PD zGZyt|R6saeNd1Yht1Yln*_c|hZn%s`8A=8Ll0wE3}f|U`qg+)v4wrTF$ z#&du>DBQEMZGb7zP?UyK!4NKp@Cf5T+?fm+2+c?@Fc3ci(3hKmyN$7{?Uw8=4$&$y zyIw4#o}n;r3+cc7o|g?V zK@Z}d3)!})DUUz$LKsDrB|HA6*|HdpSzdRkzV_CtI(EH!$*W&G11*#b2$$c?xYaQ_ zB8;j!qY8v;Y&~!F#E&rj8HvPaFE;$7HLvs^y_k`c(#vkX9lq%=oLaVN=3TWAGj&98 ztU%txAySWdx@Jr2{Aw=xLTI`c;fN&_covL_b4KxMLe>{G4p(h+#N>^@Rh^9CWrsv# zNUB<|J^T?N*K@xj$R(tx$R-814Sb!Qp%PNnvn=pJAfmK}dUmq)g`9Q-xRv)94t|S+M_J5jsSsZBD9V4z?zE=OTP{6Nm7G4^)I5=EdudYobLLw=g_B5Y0 zT9A_;a*|Ik+z=<4GTd2I@{j-VKj!K_hyZlNc|U7y$(Fsm`Ys5bX}q%P2k5;}jJ!Vn zhLx&^R0)Enq*jv6rIjw`o$|E3htC+fW9N_a_;WYk`mNvk*Z=xo_ddPtJtkvPqFRTA zUuX?g)l7>$$?6Y015djYWr@tr;Hr}5Ta;WQgq-Py2w9fX9#~W<1~MMiF5wF->a4u* z_|tiI1#q-nXHuZy=_(7PQuP|Jyk~4onh5E{VQcre8(uh?eXfP;AsT{Hz|mwR$mLym zZ+v)zz?BfL0f`sYP*06QB^efe_W0Udl?U;t!0rmu*FBsj@E2bIoT87m1Qk~w0jSwM zdWe1xOfpqEgR3(H!H~kPQ-;81*h)>xFmJhLBe)-Uyr3gBJi^(RXdzrfMhnr0T!z3Y zH$lUYg-q%+x`l^y$nllh>@xJEczg8Z+F?GWN_Qe}w3|z0_GJi%ydtGM-rH>7tu4N7 zYBQq&%=Tud*Daoj(ZWS_ zllaCrzQKjr^SyAcYCd~W$(HVP2_ajb7n7k`Tg6Ad6?Fd-D}6rLNEc zG?cEP)VyWq4L55~09_CiW4C+C0T%L@B}A#h(fqk(2t#1Kz#R@8nh2>?+e?~{c(I~V zxReABIq^0?A+4?0%7_odw-gFac723;oE;G%`0ZO{1lMQ>C1t}j3!EASNc?&VUSGMR z_&!Old3Wd`XWP^mWx2?XJRGv9Cca_hnACVf6jS5MtLFm7@yVXcg2(ROE}0HmRap=_ z!7gH$CNPe0BH4?Y!Ic7}j3@Kaj^BZsY$>)lk5O+ysA-P4IWiPe&(PqqI4=g$Wb1i5 zqeGfFGDF{K5)C563m7`GOo8zW*{V?44wHkWq}EdntG33d?m4vtO#vOs&5+S-<+#uljHm%?ePS z)C{iEen~JKQ(o=$5Z*_E2@(-o&uG~PGKxB5{4+%C>*2^}g$?!F z5`|CXGCX5v<3yvQU4{_MHV+rWu;n$h|Md;^_J-5keV@H|+$gxt+rTloN)68r?+Ugh)!Pd@-m_pXz4%>%*^t$pIz-FW>PL_sZXxoH%z!k>iVM+e z6r4p29F0K@;)d~W|Lwnh{0`DB=4b?`39cSGz?LR%%IsT?cURtq(hbQNZ6b_Y*tS z^{A9rgK!xw#YZ1~_!;xmAyqwH|FuXyHwpCQOADgOG5o8r4ydQNXr){ufIOVKZ9tN=UN1R%Rx58J7bAW> z1x6|EdY^9lT)d_;!YKu}q!y$tWSHj3Ic+r~=qryV?~-Ou0p1+p15dTt)wyBhGi3G* zWYy|Vgk4)nhHNyY#$QPInY!5_n4JS8){$~G^B+w5Ajo^+=S`wSt*Vf8rq;^9Z3(|K z(cDq!kIAUEg@ov9>Ca|h>f!pG@+H#G%jOM_yhKy86bSHi!u5;lT>F`y`57;d%{uXg zGb+3DsD%w#uBASs*x?f47osn3%uA1N4P9h*bP$A0>cW5fkn)--WLMHefNgb#BH0nf zw}8M_K$I(nHCcG`>L${~fzG@?A4dAE&-4Qo30IQg5UW~|TnO1sCk~Mr6FiO6E8O|w zi=cjAZTQG&ys~HuIgQy7BA1=3+8Ejd*_9eC{IMr!|M)Xp{sTL*y_GZC1c$uc zxK;D6o<%*21kPpK;BT0%&Nf&>Q$x_V$1Z%svz<~WPV*ejeu*F9^s+b1SbuZ8oBm@W z2(R9nFE1lJSN#b-%Ul*-c7n^8cN|`=4a2a*ostbpq=(}d5(i;kyTvw!OCemZb(#x- zvm?V3G5q}P!(RZX-Zz=d)3Z?}>NU6$RjgGXkR1pyJ^iM^%ZA{zB9I7E$m>cM!LTNf zT3-d3&O7Jz-(^h1F#Xc$GvJyFsCcJAU|Or^5xfQ8%hn6WXQ<74x{fQgoBQt4=Vf$G z8Vc#CJIo&J?1so=GO|x2DTwgdBZTD2)@J~J&Mn*!OI={a{jT zhIMdHvTYnnU#jUYb%`o4P?mETXwf{g{)>O{FFgHwQ`zIfS;INO`1KXTrm2$Z7YHwW zGb$-gF2#3fB1h|Nqy~hfU|0x5YY6eM>vtd?=)ECF3a$z9l*2Hcawbi*z2;myea46;VqHfj3L6a+c zMzO%~^;Oj{o0?&P*jk03Ypmih10qRp=O#CtYb)*7Xgapl$8k}KewD9S0v0E`r zhQ`oTWw#(O76?~APJxD#`UNw}t|Wz7!)gGY1psTUZ7#^gI74QS1`+Nq;38&K-Ea+? zc*}y&)q_z~NvD9OrK3L$Hd@|nL$i=VyHQ_gLY#vhjg-`ReG-@Y1m9wxf$WCsXC!54 z{q^VU6d$NCxMrwFGb4uHB}nTbB+X}Qd&%_@{)))6A;8+EvmxVIHDJd@k7GKEWi-u@ zTxb?#YJnTZ)Tfrhg;SP7^cq6@+;JCZ2yC?(uIhB6>DgQjJ1%-)3Nd*#IFMkV{tIcZ zZQ@G;MT9WSO5Oe-gC1DXjAnZYG;Ect-nFTvfw!`^`Q63qiEyzQ8l&Z42t37kxtwMv z_^ho$S97ie<7C&DdVL2h8gTe*QM`s1qZB|*wR-n9G5b^Fc*QQfaNQ++-<%O4};`N5bvKI?%C`!#HK5tTvsflPpaMms~mA8?; z`@6r}Pr){fufkS96r}8W*9a4ov|JUF-BP$ZBlyc#f8tO43C86w%~3!jn0Q5|7J^Wn zWu%rN#p`V};$vp(O(#OB*_&YGWk;|q2(?K)8Y$rKBqDMaUr0J|4H*b|R6CI~wUFAW z25ES@MmVKnmQevq?Kpa^>n%w&I|^vL0>@{xCO{KxC);m^zTWon{r~f7RnyetLj-Cx zO9$5=iCbHjL?1CBEFB~J9{TCwD>fJV&bnRCD^)sReVhn-1XATqGb!YTd5Z!jsCZxA zv*i}Tz-fOvqnrLVf8P4^*~+WJG$CBs)w3dJ;An;;G#ueHxTrZIz;4)iE1>@5e>V|(cJ*p&b{nqL)@s9746)u`aWQ0AB7u78JjL2k-THxAA~OtyRG5&w8P#(S`lgFX zv1ai~JzVCfmmUAT+k62n$+vW7Oi-%|TS6m}G_S-iaf zOj#&v!fR}J{kaq;{z?k8nBx$c&gqS&FYov#XR%Y55x-$lQ+UOYYno_miw5ih!T>_7 zyrp)L@1uKoD-10B6cN&CzVNOCIBzfPm73>zdOvdGHyU<{H#N;fS>{>kDr2a)67|iE zCZn0?TL^&}g``k+xb@mRg&1m*(iy%|A!qpT3|@iWfAxyIn<6q-QTo*eoNHdjbc$+N zKmG+Qin60vikRk7x@HuOYgynC^rvPvS~`!e5lUw`CPZtf&p;T+cu6oF1cf;+h6;;c zov#GWb?Ou8qb}QqCfDl0Z>Y~0k`$1EFV?r{99CZ?a)tHYHreT*sm*Zw8G(i!2x_CSnNJpOsV?kb!AZXVfw}(EfUo3FtcD?uH)-$EoZI zGJe6D0+zkw7*cuF?>GWm1U7!h4(E+^mV!w9j16fzr~IJYFL8a3?w|aVf8xQ*=~nhL z1szf4on|1~1ZQ8!r_{y9j6Agq;qwnG_|!RboiqgKh|U6PqN?A@v(IxRDLN4@WqjE1t>@cC z-3(RMy{&nkOEz+#-t_dElhEUco-I(1yfYJGdK#m~mMC(cNllyq*BHnre%c5`+=*QR z+Vl|h6n;x~Pp41krugm`OOFd(qQ`VY(y0L9@GfmMf%2+h8G!`?XT-0(TPgRz`ef_z z3ptr+!%y!fQtE}Af-l)=Qs^C~^jpAlvX9|0D`OkL?Cfk!?54)3RhYimf#HT_;Z=_z z+4RHLUjk2@op^>a+F%KCHK_`2=xOqtDcg?SaTHjSlxy`=t)V9ye}U`ge(vYcM6H-- zjWU)_PefG3lm$T(%hk`I6?01QVn#^Ez0egWu-%nHeZQf1L{5=Mdr9Th425LVbS?AF zu0*nwl&gaE?CPmE#1W-W_Ay;ZObTcgK^wln=|dFYwU&+AF?~sPcp|dYNO4V>5k9_Q zb{u>q>6=L52+<7vbzN^v;@}$&Zz6_3O^C%*Jj2i?@fxpLq+GgXF9p|-N`-6eT=Ozc zJK7M#*$m+dXrELYP-E*CyNu0FM%U*!clxaxIgTNM1;K}S_a3Na8$$zvR4k5B|QJY|MeB{3cDK)7XlP&zj+2sn1P(Agnm7kgR z$HY8L;6%2(an9JcRQUNh%V=uV&{6AHD=Y)Z>o`_3Tf3|V*CXMZ>0t{ku5&2(#Chb zXFDj}t^Bl-VfZpg6g~J)soYaZ1we64s z$7EPlEt-C}__mN-lD>tFfGSGwSnN(7l88eHttHhQ-M&(xpa ze!5Itgk-%ShJp2NSE~2pNF}zD!cD+1GCPq#vtsD|Z7jW8qe(IZ3eiW~QYKhW=ggAq z`f-LZfZ=*Y`XC-?^*sqmnp%8$arB1uhB%1?CKZ3Q4NoL-{6ac~Oe#*`a@|?Cr4C_4 zV626lfw>-;3z@Ol<||8y;0)lHuN#;if*o-a5o_2qi+a714k+e5`7No(zFTd)kl{(6K{^oGcOEq!3z=sl{0qd%)AbHx%u?Wk2&~ zb7`ex&|Ff6bgsy7c2E4C{{5i|dwZ3nbM{<{AtlxYh&=@DmhYa?5rIQWdMeCLM_(YO zl~;*LQTB~2cU+&a8OG6@OR}|p2yb3W}L*%pO?h|Fy&xLPTUf?pa1NG|JF%4YE zI3bT-f*Nloq5{d%!Niu!;eUkRc7inokqM^ik+a7zkQJe&d7ZdKknkFxMb@Pb@8 z61m>`Fwkcs{#Jxbr4$Q^e1^}6e7yO8{?Gq$qjUwc{w`nN@-5$@_r?c; z@8aouLr*VPB2MX1UtwBay)D(97hzux7cq;hv) z8m4d&p>v@{u3r{I;f7i+!;WsxD-lXjKjw@S>VfC5a6Qd4oDIqASc+0DP&08Z>jTsl zh-|3Oz|0tU_dMPIvd{iP8#$vaar9@>iA-U}T&H0=p8ow*^ap<62V6<}pDdT>xP2w{>|p-)9vBdjAH$7r{k6G8MLkJ-rnmMMkcB&ZkAr1#eSTlAan@N09ea zn2lCcvc*ECIW-23eor=qJlhxLt#neF8VJ@5Ed`M#+t8Pwcw5p(!BWFhU=(XO7mz)L z6Mqzpe5>sKaOsKdH~z-o=&{2?KbIS=$L`+kHl$gi^#d!Tq5hS&dKelhu~o|!s9z$( zR?LlGZTPP!UtD+j8{gDY9-+L3CoXCFz;Jy`J@0*yo{nGz!?ip8c2i72zi>4#sfo-5 zELK#6h9fuBUzp_$9C^kWH8Ap|WQ+z^XTxMGhS$(DsC(Fskma2K06+jqL_t&^_yd37 zOJDku{}SE?c6%Ekal`Frd&aPLj(B}ygRfC*H7cOM*<2K=gzXEclG<|JR!UdMxpWhu zQxnSw*Pr&l3_6>|V__Ha8;4CH+3Ktwwo2IA>JQP^q~I-<0i!AOCUpEbAl7ycyM_h7|yx zomxFmJH6~PU4r$Pb1mv5yb^n%Jq{_?ay@GU9|Iv~I=sLZ|8^Mei=ap70O>Pgo{1*H zcs5tFj>f(W&+sXnS?=ZBx_JvZIm>&B$In&Yy{153Gn(dU4`kG)KV$gRLKHA5c3z0> zC%dZ?nqikIpsnbf1-jw^|J#52Z=ZbfNiPXZ8FJ1DLS%_5Mo%z1*XQX!-4h?6qCR!# z6|G;PlUl8O}PHCUT3pN8J*=*J=LjrgwR5RyS;FrAX(D3Yph@*%31IIMM1yjzC;;hAFg1XF03 z_f#IA0bfb%H7TZsW|Oo}I$mf%97Z&FN0R~M(#}LvV<1o~Has;Uz#7~0y^F%H|MkD# zyPG`^p~dI**yQTK2odG2?snG@AiI{h@-#>n9blRn6|6R1v4-{Gm!2!Hq-}}c`+I-y zum07)8cii$26#bIg$q2B0!yTxOBwG2pk={Kp>T)|z-GYlwPX_+Sj}9um~k%C$@Sho zWz#$pkrKFEZ0Q>2g1Cq6aEsO66?OYeoLy>o^EJG)p8md*%|CtQTvlLj5)~dtdH#z7 z*Yop!StTVZ^)gb+UXMT=Uas&nioL1vR?osek3snoUU}-Nq`c;6c7ggWTAV~SWgDId zPW%Kri03RWc|X%|D^O~~O=O7dG`Sc99l`2>sIa6%l$TxqnM!({ES(RR{Ar-=%=mEa zOlLT$Y~CB`-DQC{gaI5m1HwB(U_-OhF8t3kB-m||yn~qb!!nzC^I#rVrW$3nUm+s2BgM!m#6K!SSs|`|x!#|U8 zo8HkrRsT#X$V95rY}s9IDz2h=wG$C#Ej-wH9N+5TbfrtPeib$}b4`fh8yf{o;(DhUMu!d zz!FvYIZXdd2U(UeXJEEn3tZnSIW8Jb3cwCo`r*}l3JNi-508IxW%W=+3BnSswZWK!>%?FYB6Q!)oWF#XS>9}*_zn;3^oNP;LZ2$xSV-sr47Ko;)=h-*pmgv#Am-Zxbde1DXvVdNY_@NcP;mN$% z0&ByU0>Zwn;Bn=RCWK)M4GT#pvfdCLGeSd1yGX1Bw?u{!AgUD$A&$w0@G{0>YsewP zA-83{SA{SGfs}@DHuyQ59p6w$BI?YWi#Q`2r(!Vs>bd`n<=_09f8+9QSW@8`dToW- z$?PTww}nV)lvfPH(DvTfqA-`5?OmkRmU4swXHPJ3y+pht^mq#Fn@I3NGH$$g+kR4? z$55(PF%05O91?CwKLX>P|LMHr+7^nF0`PoR0OlpaRsE39*{H42WD}H2RShAfYYOFU z59GoJYN~A_dgSrBKDfxo*b=#qlqF^0GKMsfh4Vsc2!>o5#4uaW4vC|$adqXH_nqg9 z`L_b5weps|+D4e6o?KI4Y$@7E$iOiZKPQsHG~aF%cuJM46xn(NO)(BxjU96LJkwPJ z{WC?pWW(9cbT`(9da28p!us(~WOWXlAy*+8ZFps8_@MGv{EA;;F8Dw_Da-_I6=1L4 zxl+&rtB|2pDnv6(Z3TQz(Wg_@5X;JA%^uzS25di^sv(Zev06DaKU*^sp&27 zG+KgI0sfXo?W9$VToTbSY$^4*LKd>{2n`2D6H=5O!dsnp_=ae?T7-rbaCbhPY}GS_ zj&>^NTF*QCV&tkXH9~3)Pq5g?Ren+1u;c4*#rP~0=mzfr4mhRW7@k-2!pBbm7@kx|6Ovb7%Uf*7J?@(h zrDqynr17z}Kgo}8t6)}@zRSew>1;|C&5Fe*tMVa+_vO636@ff{HIUV~$Szk#u4jAv zq8uIn(~YqVGBGkvkY3|{MC&J!z7K?w1>SaAAkHY#4Xm)QD8SscARN4iY?F(M_a)V z%@+E$Z~Hc1Gy8jg@9%jN_i{i_GMz%|FT>@6vm0U>s;~=-7v8q9&3TDUa3N=wFUls~ zlIpdciR=olB=gmrGU1lgf`|p`4dF^!6eHIcRp%}JJ(qea!vSJsE3lG!_0Tvk8F#M55$LjGi+D^Sg;k`>IiyiL5*r{D@%S%ezf@eSyF-#V+ zB4ufp4*5LBhra+&Q_IoPn50_S6~`MvZ?-KBFOH3+^@z}NG)WxfW}fm*;@#=@ndY3- zxh`~8TX;zN+3*?Jl~G^RkSbru@Dn*xzzYE)uQ#voWyzZkdqc=8Z8S!-l(Xk;xWWv% zwD4zpTiHJT=l}el^ZHqpYQ6RASFNJbc{b|3pxD9*cF={`@lF+ACHDri$3)+d1A31T z^HSuQovmT&(|k7jE#JHCbP{BTLl_ER+=)+@`;&Rkb4wJzVRp32IBm0=!or7~hLfTn z2`NJpm>OGE`Vv)}1L2)i;c;^9dI7}me8ll|Rn*c=Sr%t5vKBbbONJ~CjYof9_!i}O z=~r9nNI@`7id{qFRXG<#e;#_S28D23I#rb`6b!?ft8M)vx-}m%aov^aAgl z|N8HwGS=gf(nA&lX=vp={r{%l^qYKp&8wFlYDfEdGOCTZGODCvvP)`-&g>1XlJnFT zT3igR%su*P^B@rEWDh^}PpQvoSENdO+(*h;CU50&mDEsE48$JT?0DhPc!}^PsPEvm zg)T*=Z~;H-utcQf)hecXDv3)AM>zTAQZs@O!@Tu|1?odi*;hw+J@UM>BN)!ebs3HT zPh_)$3`BdjT?#e~X~7pBm;#Vjmfo%S>Q}$&4b%VnU;nFJ-L(Z76UVaa@hy9T46fb; zVH43J+!Egd5z1@GDCy_#pI*3sfKYQ-O_kNKTGI^Cn^+uydm@1Z>JcmrBjr;hPK))H zQt?k(;np;q&M76Y^0c#HgcHwuXMNhmM~i7#B101~WI#ww98#2inpG8^`Z&3ka`?rV z{tSf0&Ym$>>tJ{WUc`C5TJ!%{b^Gb7=WW&tbNY;Gek2T84UxBoEs=EoqR4;v5C6gW z(_2jooFN5~=`@`8(Ws(MKOKG%h#;KiPUnRXI`?unh@8OAt*HD;=ciM_5VE8ls4+$G7cO zi&HMRa4Xh#Jb;pR(p6!5hS%rv5KNqaAh-I zX~;F4vNKMk8X&jg?pp<+bfe|fbH$h1pIqy-t#Yvu>M<+NP{>ZPdy?BCgfqOB^GDpL z%y?{z%4K-rENQr*hGsXqRe#_2ec!iy%eQd#P|I}*|6+%w-mX}+!>tUkZGcwxXgIu9 zECX0nd`#Z>5t90%>X+AwIjkPm{cTOxXD{12;+0ncOU;1fQi--o0=Eb@xvQB!BAnW2 z?=_b9y-Wo4)FRJrJxL9z)Wm`KO3mA87@^BoxIVtQxc)?YOI4qhBa!TSfwLQi%)Ts6 zJ4!5EQ>QEKHrUGGN}(wja@CLXtWngm7bQ?Db@myHtSL#Goe`}N{i0GkYs)2S9Cm>z z54U^WdJH3%G{J!*UyK9;&s-sG5BmYl!d8H7u(JbF!$+2y5jmq7aWQy_z@x2iqrOx6 zbiSCMr+uzZ0FP(On11PGHQop4a(tGVCl3JprPNBl|#O2aUCyId&;VMLh z!uKw32fb=vDu&3y$!aaaOZ+$Dl4_I_FG?;gTiFO3j@%mJZ~+&XU7w2rFGPrmC)H42 zYA!P}%v)bz%1eZ!Sx+wikXEdpbZXHRb6SdoI3T+j}PPC+WB@v}Y52eo$?d zCtYjR%B0K%Z_#!(q1}USuLE6p^;4+q=@{}hj5G2VQaNRN(v$iR|L_m{O!$xf(Ld@d zi%P0I6KM@`s)4b*s~M-V^*|hFz2EKl=AO?#1*&;bkvBYz1W)7@`_wrL$X0o!R!?i# z(jk;cVfJK)rbH0lXbbVFh%?cP$&_Cy>2tcKKO@1aJr{@Fus(iM_IAnL`Sc#8Mc{?l zd-^6g*#hC0jGM>>Z>Yb7j5dS9Y~*E}@`;SFkY{PU5%3FXf1XEBAlHuLMd$59iPE`c zdiQ?KzxhIH=mr>+e@kMZ<8eOs+}ceuC9 zIVCE+^QR>)T+syetvI7;^EQ)_(I72E#;ufl6QwaDm*ti6iWDQ`fe8w~x&nAj|W<2eMoZ##;-3eMZ_3-M4Ga#0i0e?lA3(c`k!4+Q< z;?O^PM3B9^;DZAEGu_25yCYixqAE|U%c!ry`vlExhWFXp0-tqV5?zK>B~Ut>v$;1c z7rTa;F@=WJIdBGV3hU>}KqxjQyWwbtxlSZ|i;3X9H!qDC*1u)MF^scgnwq}n>83NL zVXafxQW!4p>xNup@x^M$ZhQJEAQ*N`t4FCgh3wo%(?~huHrQYLYk%!u|LcD((1V4V z^BTU?sA*^jUY&Z9pd%LeQpoVcmqL+ArKVbK-l8rcK$W8jY^JlDGTh>1i!DW2VxHhh zvp$gnB?_qw3W@M)*`)G@D6C<9mCv4QQ4#LKx1}SZC(@CEL)gI5%_}f++rUti*LV%} z#ljh)h?Gm8NFk1XKPX3wN!IPt0d0u<*b)g(md$uJ~hsAxSCr@3#jonTnMoBgs}Bhf(CE9rW{@%ozGM7V(GXtyYl~< zU-N6e{N*qE@PtcWo#kC&47g*hMM#IORamprJH%xDX^`a_`GRanVfJS-DnP#|E7{V8 zRF$$BTjbT8fyv+~(ZvKfXKW+A}U3_;T}^y!3{<9!Xj z=`=fMk&RrU^(Cqw$T;zf9qm$BA@-I^@T=+~d(n9u(sYBdl~d@LcK9C2yzUCVjNyXb z?ctFVq8Q{VtZcTsvex+{sjUJ$%PzJkeeI*eBxYs9Au0qy0?P=`2-l-&aqtt@I~?f%8Sxpl zm_Rg*0WWqU40uCHJM=h^+Uy0s^L$De2SlymKt`z-sEm>_0vqaQz@4^+&4r;nwtfZ< zabP{ZKYsO_fAeoP3t&<>_@-Z?XM4=ryc<&1hM#sIL^|&>?8-Ls&!2IY$2P$V3)fdw zB~`|K3*4SZ4eccaw#p6J1zNO*ZMLZe>OZ(8Rk-&Z=>PbS|G1xK{nStW6i}TIM!Pg} zGiquItJFAL@taO_5%n`a^E2M@aRm!6-51--Rojdj$_Qag$1v1p zNv8^*^`2n4r~zB6)5xpWWJh2i-(Hb?^Oy1$g)sdIh7_)^c+8$?&$&Rfn4-Ak{l&ld z7rPOr#%ovzq?SH#b;1jWtOdfUInkbi?@ab+2yA_ulkwgr+@pZ~pgd&ZkiBk6%|zxZ zSF_N=^;%3r%d7W`Pk$iDHv#;%*h_-`*AB{DFEw7x1QCS$e=2%uL>&H7)bNv2k-2W7 z_jV51D>&P*eNwt}&#ZuMIju%V;nb1D8YU~GboGg+!)8Pq=K`LGuh^HE@Xf9(@R&17 zr|b?fymTW_;B9t+4q|z^I-m&7-zEs;)o;E+XM<^BHO|NCtK_GKxS^eu*PZA`Yj z8!|S`VpNzq*+RO)cNQB8seM?HuSCf^Z#8U* z)WD@?seN|+(MKO~H5Z8e{7Zl7FTHHtrz-FNi1lw%ZaPc!3t*h{dL`Wo#%bjti|x2@ z4PhXh7nWDV-|M3GN?YDIr`j0>Zx$zE_L$A9qsd0#ipGF+WW#rq8=m?_o%}@f$?6k? zNN0yY^cgLweI9>Fc_E#r`boiYxHM`h>$CO4@02g5`=-0jvy!dCqf6ad^{sBSlV8!m z%`=?>?X4}!qkc&G(|L&oPqXZ|%8uK>Ks5GQ?I{?IH)EQoRQwZpoB|{eP32mAU>BNj zAALer08o2rI6On(CdJ6kNF)TU0*bn4xTQD~HOz%PW=38*NhJ$(8-R>6!_|}hJATLS z@IEeMpWrVLm@SuGLF6pPbEJ23-D!TwFZm^3`N~&ZdCPK!FIUDUb-^EpF(<`H9DZ&^ zcnyw_>m7Exj=+~1{+MWBhMtIL(+U{B;mF=S@}*?&gL?eMH+Q{vD1Ea9XJodfFb$|r z5ZRJ)0U>wRt+S*arWt|y2t}pP<9nR&XM1En1V{c0f8j6umA~>=ybSQ!f+)D`DR*@P zhL=lW2yD&H;cBNDvWx9|Bl=Pop5|FhAvd9WGkv=V!N%(~0|l{X$fGTE^-!poGIHVb zZea^g6LWbX1tJu+o=Z!|ao0`LzJNck!zyVtHpjD6pawZ-)P}KQT;Xxh zfM#LXdM0J4C35QIePds#BNMb4JokH7!ohNgU8zb@>KzmyJ3f){iVs9Eqg;@}4L1e$ z@=iRRG9n0!!#Md1e2Y`Qy;-FL?8bSJRPP2^{3SMr`njO z5p&41%8pA1uBjQyRU7{uaNA(B84dMf!*7TAzA?l`SoR3vapVO;3}@hnw4{aSb={kb zm+j=T#{!qgfyjn4#3F0#`qbDA4UVaub|3`KKHI17;hku95^P~javUx#xf#57_?yz? z6~3B6R?6!`nqpg*vg0#^r(ifF!ukl0LyOgP5Q=Rnfs>lCaKNB= z##M9pJ+inv8m1Fx-k!;T5lX?9Wr+$gjB_$`5wUb?c%|U7r;m)Om59jJgn_d}>4#Jg z+my|S;V#}^@FZopmTbeMy3o`&k<|PNaK``oU;oR+mnK`A&c%4ebzvIHyH?=Y=Q?TZ z$}?r&To6X>41b3`T}0;cLdcKj{P(36WJ~9XAKws|VDHxZ?eQnC*5nyz7&k~8C+ZHB?Yp*phCD5%%vq2e^muO+Y7NnxW4@5FL$8T38at+SH{F` zn~rbyzvpN;WyKV-su*j+ zwT5WJ8Pp)le$rL|u#*K180TctIYBgQrEdeIgWwG9(3I{hgEwV;2Bg|j23Bpk0`;Uu zYbZquT#-ZCWkZ+Im~DfO>A-0EwxVN^L0NbT!bgKE!wNt=`uCrHv_1vG zH8hPOHj##GMuKHk~u<7W5DsTN6A$l#4QJ}s9u_KEQ)S5fy=U zCgu2gG{g2P1%^+sy|%f8`^u$_I)33eA&e$Ps(GE^z=q|Gsn?L_T}TM!U18>W-B>Zv z&SAE`8W2Lt6}Y@JqHVbFkY#CT^~`84c7#i=X;#3zBTr#S#!GnK)EqvG=I}*6Ye1`h zMs*t6Kigo2TC{U>CW1+GoWPJm^d-ePv$s*_Wz?i#timZ*1BFEDF*6cp7r0?k%T9UuLp%rCO7!CE8p`@?m zoyett3qJ9r?vhWkTz0@E#b_SdY`9~-_h*KJ1L<5=*_RhTtolMfB7%)uA~IDbn3Zgh}tX| z?No0q3qdmkYRfWX3Pav&2*J5kf4@AXaOy|6q?nAf%~A9J+WwZ`@>@i`cV4d$HrbPN)^Osl+oXD2^ErU;>|V&t zLh)$XTbZ_>VMs?7Cz`9a*Fui?-~QWw`yc#+f8ZsbCk(FfB`Of;nYBwcuyBTD{2%}0 zfAm7pu+0V(W%$@S`qi1pY$1L{prp>b^6Jl;163l$0w1$nkTWR-Lw%gnNIV3`(v{k* zwbGr+m>OI?k2vI9Np=br+$+-W`mXQV=S!`F6d5!NtfvD>jl$OUmizuOMFcL@S~?5p zuXz2dfAz0a)kR=B>DMDWYTgR@JAdczIQlMkUi|`jwTdw;ixg#nbW$(^-`co_|AD@?BXk@Yn$Ee_)y{IqTxM%eIV4*aZ!n$M7F&Oo?uy!RBSp6y#ALN+za z)rw8LkPS}*(>r3WAo>X-epU&e^jVG>wt!{mYpS4qaCTJzL^ z6pBr1G)6Y27AK@Nx%)ruA}rUZj_0hYJ?Fe(GU_F|C%<*3ENa!VO)VN`(=>c6;OSHx z^(KFB7wUy@@xBXs|G#vSn!*slxl#$A_jJ8C%_;sm+0t~#g|wjf&mO|pb)NNBbH(W8 z(iq{}riO8duyK6j&>va%B$RA?+t-~ExH?5ue7(ajHZqsS=IWU~{MkA&iF0*&3t?9( zTNA6Hv6aocA#j>pZf35PKk_3#@?$^tWA1G(rkDs5VHXlEqeKD`qV}qmwxF|<1PE}vY$HH@SFP4raa6;w#O86Po$6vZb2Z@;frl~KlP(OGvGWuy;h*o4BLE<^rBmzQlGcE}WD9S#5%n>X&H7 zq(YW-{1GlyjTxb#ev@SjDXJbbqjf;gwi@i^8k3P=UcDia47P@o7fxBPrQqne&?Ta; zof!opFFXTn2K{V(W#4mtQ{H6Hkdrz5Dg77v&wZ{>SH)9UQs=VkA)_hmfGz~Sh1wMG zYp2?5AwKC%s_+yRo>9g{5oC|qa9#$assviP3Sj4|pXRBZvISG91D#OCj;sKV4N{uLJueY+@()M!Uq3 z>C_fGFMK8PUJ4lh?M4cE%Azcal}b5;ouS`xo$Kw1#$3i_p_ZVYx2K=+b9qK|SN=19 z=FjL|ble&p+RC=!d{e?>A|rCjC4y+(4%Od=2rQhvTo;3@eX~G>mWYlsVLFCA+tEbO zF0zImMEp3c+j(BHGbooU1D*&24x}uLTcc^3zEx_1Tx*b*+BmNmVlQ6VC&<;OW-uBDmqi4b3rfb2sbe+upDle)^|>8et0<`Tth04EI70c7OG+{#7sV z+!#D$E9SmnPYcL4N3^D_rZO5KKJ;O;t{W42W3#4p#b*Gs~lGO@L@*Q($L| zoJc)a%!(`!l2-^q&1vE#qCcjtY)Dx|$#_l#-omzkR=bbsOoXe2Z84<~PO23sb;>c5 zg`ipF>Rf74*%=kQvMKOJsIRb63)!rRN9L*$d|#=iv%*F7w zM)#vx4`f(v6KAw0`c{KCV<9o;{owT>IL*_j!i(${@Qx8fM*Y(97Cxh4%-M#i2{$`K{MIVYV;|vl z&STVjk;gi#?F>9QY$v-invwb;8vcw{qm=<}ve8auE)COHd|h^2pbV!oqY$o3?2^Vz zidwjZ_1ENn?&p5aF|FWw-fDxdBnVsOlj3!h^%~`m?(dIuZH!Y-jaNe(ZU~<%``LaX zFL|HEFJK~OXUZBcJMdoGZG{Ii0yFeyyIoDrF?*AF*2X_WPVz?C~4ZS9eBfZ$nI=}T&Da|<-)6{GOR{w@lO#nPaAn7XTu}wg?p9a zMn2k>cT&YFULbqDDYF}zW-h~|mf{s7^0S1S?osDC4A)m!2AfW6%V?M@JhIt!IDotw zW43kR{p6ERJZ$yiE$`VEl74IDy5p(zCw}55e)LCwl;QQm$~XgOueKpSABGwnGP|x{ zYTBz!T>Tl6HWx$9R@7IUo@+-urpY$nqJa24H9~|K#xx9}*`7CK3r8qXOpo$jg8u10 z{ilEXZ~yIHr8s4Jw~4nVaE2q|6#M$uzivj3Q6I1Mq+U9Gt%>X9LiUKzhasdQG{L7F zFaJY9Q??#!>ytrh1~1Jf$XJ7yF+(vX_(MPRL*M`X-~W&P(LeH8likvl#2?`?BdcLp zc={FK1%N+<->E7oP>*otHCe+-nl}O_Z?hmChpQ$d!w#v`WOHTfMd>N%mxx^~FTP<> zk=bR;#XCIO+xwKKKjZBRpOv|ctyMdp?eLk@hO30D$!>AV*p$(>H`q0VjB!3|ktcte zzzH(S5)w9w$2SK}(LBUrTT#o+KpZZh3=lA@cKl^9@EWV$&`JAvz?uFk_RHe=s?UDt- z1;_Q=;o~w?;mTk`sx5`v1l|$2FwLU74}vQvHE+VYLPm>|h`y@QZx}L{p+NnN(-r=~ zy}VlQkh&u;MMjC@KwimvJxG^=ZY%V#SCYFh-#Db!%dC~fxF@+C9)@G!_?5IiW%dW; z-OR~m)6pyq*)Fe!TnL5?!*wUOHE`(1-KS?u3Cjt-d2N9bLM2Q85P53RD9k_(r0uC z(42%;VAmbw2zTOpI^@sOQdfhPO^rRxfsmHV6-Q&=gKl%w*br~;;V0!Ik?={KvRnxk zDAutS;_(%C=!_V}I1ewVn+@&}T$$4fP1x zs%lHaBj1B=&p;ZQej-8|)(cdaz;fw7`skz7;M31Q11i|C9@8Ql>PrNtJiZWDEw?TQ z)ax;i&VHucs|8FAK_CC6zVdj@f?ZpRg_rjx`mW(H;aXTx4V&PXzx?G474j12^`q*3 zOVhfEW!zT$eq1nZFt$MaEq;Xwk5;w%Qp0Pq05wB>73zf>wvz0MAwD~fzF9=L3BB7Y z;g!Lj<^p9Lr>KS>)>BYk?ZXt_7_}*zn(bt0hNBr)yk2wB=zD$a%QP|Z&9Nwb1cp}Cwg6|IOdv0!3FYm#VUO>0(+1!GK%0k05^A}SIP1TTkkB2i;3_*#BT?`O{M-t8Iu z=GtqowfA!l_>RKZeYDwR2)zxkVU&9$~xY(ss!tj>`UY!K$p{oK#_p}li>3i57J z3n`bLcP-HH^v2PDWK%W;;=Vxm>8GDsz?L0i5frA#2rCi}F}$RH`VByJU#JChJ?XuW z-!&v+K|La@6o!_po~xVVKvEx*u}ac!^Ex-JNVvN|StM=k-c|pr@YM98);AYJt?bk6 zO8DuY{%L;^1$bTtx{#^S5-4=xY;p;Xb!43Dqr}* z7yQLT&+8+f^F>Rx&3ZC#E?ETPGpvKxQMm0sLmusw`sn)Hk$x8OJ>T;^{$#uPx{%w- zD$iE4=T4tP`$anIW7rC`I9sA>+pt-r)BT~7(1qzdVCqE`BG(WcL#;$zHOPJzjPK*s z0=F6?GrD#Df`^IQs-s3gw5B|g&^GX zym_%lXh_5kMuVi#M1+Wi+*92;i>4LGrLiSStr`N6xz^u;?=5h}jLgZ_bIGpqg(qA1 zt9lCQQ*f18K=0^G_EjRUB}-b~uAPR~M_z674cE7HR`s&Zg|tv?fiYX9QcUEvMUGD) z`(m8R)hxTlwmXap6T84^J{qF53+^#MgOjSI2BB%K^!MX-Tbb&)sXXXR7Ke@@4x8aQ z&yM>`8=dEvNkz_vOQ&BV_G2R66uJZu7?5&F!JvZ%Kf{-$@UuT@C;qwEFBiwObn&}y zsM#=t%TRA>Zl_ii{;o;+K;8Sgzw%f9ia+_~?>+p$5Bz}dE_RbYCsnwfW?6U*>odY* z77NiV?@EHN@-`^JqT){(Q%eegj$N~5CKZmq*BZ10b1je|7Bd}vS(a<~hZ*U}BD+_s zZ~Vq@?C-62OO%>y2akc)kz1|^8#3N}JVy7?Hy-b7#$|ZA-fOE#trS`*Nzc6SE)QT? z)Nj>)Q^_Ie&mu84u=}X~d(t(5f zqISjbY7qVN%N4=xLmBb))i0x&c8f$)Tm55^L&VO`=vqzr;(8oT|4|2dAey#eI!nQ@ zP`+;Q?ce_GF2(=yU;c}i6VF@QCiC)e)*LS)D~Wy?i(2+}bHnj}0S!~I$O`b=DgJi? zh8^MFVS8nfl7M{p@N5Lf=*I^8$6E8A{rP#(>~>VXN|rX$v44!kMLz1&YOqAPTu*QEbdK88(!S z%p0M=WeL1HZ%KuuCQ5^cbTce995Y&KAKW`a{@5;A7xG!&2MfM8u$eY}6OkIy;x}Ik zRbpuF>{g&z<0I>VG2zs-@fl@#Z!ymh2yylbynfZ$W;^TVM#IdCADoa3W;PHe5Qi zDh$yZmR+5g=`gesF-9Jecd|G3=h4!yU(yMm*$2*WIcx4Mu16qp{H@Q1ypNSz1UT~d zec$&nx_{m^KD+C}Yj8~Vq7t`yh3K??B|uA@e2ZKNj&7lzC1WX?{Q|VF1tDIS@S~J@U7Sr z8=43vPE{=`P(PeuI(nN0?@QPWWeY#~n^2{Ketl)y^zMx`uyrC{h0nz&MFdb{GnG<}cV{!0fO*I*)ye$y>Bd#xtk5izB+Uyn$EoELSW=7^kq4`>Y zGab7c>?0!%kRed78H&nl=)GS>LbxWL;%WrYOu7fo8)hF)XUv zKC)r2FNqs2O9pWUyezI_FJ~^~bht{9vc49sS4NugXLD(WWcBSP1Ao&oq~=lrYV4w( z?2$FD=V?#IJ_AnnRm!4T2lLWwuD{i0>8kLOu3e6QrW-Anb?}PsjkmkdHf5a3Ywj13 z!V5uD&&aC>s0_>&KRdNjWY?eikVF_NO#e3Lzec zTYn?uCw}55ynd;Qnldu7Tc|QBqarU!Z%6h5eNBhzuRy0J^YpH~iy^iK?}Hy7&H7|oUU=jTh~bmqMYGBWp5e_U zad=FIhb@!xV@-ecNZ|SGWg?LJXjG5+Zd$K8f^2sND zb|<`a>2Nh%k>RDjfGPtJCPQgw@qxARJ|8XKl{xtoPD-if_np6&z*Tvkdz~ExsH~C7ana2t22Y( zTr<*a-wUsvDI>JAdAG}O1?WlXGb*xT()q}q>=a6&45!^Iox37LJr%DQmGEjps9XrV~E!G8`V6)Ci1{ z#*uyAGAaPrI?i)9Bad%rWm*=2;XulIQ8k1vz{2$zokpCH$ZQMf8#tYxT&2!F z`zLW=jXrV@(~Rnrg|d11l>G;P@CPx!|NFn+d4sphn7}j3qg~AvOtzsYMzJ%BjTr&I zi8Gc6AsrJ}Ta{0Qi%$InEoq>uxLd(!Cb%-lhbv61Iud{{;$-6r#P zyKR{%P=iaM8R|!Xmq>q)`|P%cwtC{%hbLINi7-B_m0hfil~E7ZWK0&Aajp+SV8%GW zx!5g2VB$sT4Kde-5Ah%7_#po;|K-2@XaDS<^)}##e&~lFdzFcna73J1a zHL)vcHiP$h*+uy>{mER4fu#CO&eKfqw&%JSm9!JX(Bg2h9WSd<3ZI$)ArW|88(cuh z%3~)RZs?}mcZY-k4T-4GN2e=KxCY0`F3aqTri))aehU{o<14Pi-AyEmIBW{HLq3O*G|_jpk46_Xo6cz)3M8~Nt4|Q^uc!f z#=C#t-6wfombOP9u!dy&#hdT? zuJ8I&f9g-kZb&>SWjJH@+D`!f;$QrW{(7CeM00<6E(ObipP?^yMy~ZUWVcXOo}r}M z65NlOKvTBhO`MC7xL89iW>O^rDqd89`e-K9%BYu73eB=W z>X)lBhM%chMlKC47ac=(;D!j}aKR0y5MLkP>&PefKiTfTP5e#2={I3Mz6zwz#mE+$ zf-TU9Yq|7Y7ps9`;;S=eeWy_fFPc`h3?NQLN>MoD&U(Y-)k0=e2Jr5D<6YF}Vx(L~ z>DEfRzFeaf^}ql3|Aq@O7YHMMw!Wo^%2h$p zSTTW#OnEvP$Xc@FFo5k~_a6iZ!P|W9wRK$-8()uBo0O33*Y=mdk`kAfRKqke*AN1& z2E&jmWc~Hj=2+x8T=2y^trr>>ol~vS=>*1PZy4e1pHEsKj*x79;TiVK@9D%sz^KSIA`55GRCPE`7!n8kR-W-GA#+=*dan^)HH`mmd#mJsO(zq)vlE z1Wy$nD7h^7NqD)vk{B^D!|aS?BZtUk{naxtd|pO&gw}_qlesByvGwj7?hI^RtJlx;7lKd} zLX8eDyxy=})vV{zRsf`1lkmy1aVUSoH++Mq|HWq9s=uFsXe~7tuvf2phFc0o`elS% zq+U2oN4U>N-0fv#%dVeMQd6Mm$EUn#TZ9)Ty3lBr#k?5VGLmKM;pG(w*Eg&x9K%X- z9)KFe@nEDG0S9u){=q|1w_4xcALy`C%XLu(p6v{5I(i}5GYTCVD{TCo`g z-r=9uE$X!f!88|Gq75@1X>g#qXl%EOZjzQg7q1ynL#Wn^$WCb)k?&%k-B(L}=}VWU z-kvTTlfA0A6k`~ktD%0iC54$Xd^(He>LhSwP)m?2H7>EIKBU#SOif@Z^k=(EUb~a< zLR$@rB&0THteJ|fwh;E2sac=;Z+--MLKk~e4R1*F99}{;jJ#pSS;R?TZ%F^qF0j;h z?rqE0#vhUaZwU;~)Tft?!<(#cllwH$C(}L6#Qd24QrpU?Hp(N+cvrfnueKd}A?M00 z5blB4g`kFv002M$Nkl#7toDH2rxW*WtbYK$?9`8S%HQvn!xy6Y(4%IG77I36(Mm$tF7m) zhG-XsmkeDat}MS2s`qzw3~lA9Mqg?lI&MajJ!u1m~zN^OOy4@kShGf|qyj;RtQY2nTHt({-U*1F&s7_JT>dVXKC2nY~d|>hEr=Pl1 zSINs0J;^K`S8GnTiQJ2Na{v~I%v-BwHn7)t%j>pjXd*G=2tPxn_CjMK@yiHg%++v$ zmm&OQ&W5W~pVW!7O(*ZxWRr@C(7Yz%dh}A_54ZdC*?vanjIE@M_-ExK&`cIW@G^`B z6uXerY#gI%Ll79LRda%EQ{i^Xhdwxk2a1|FgSYV13~MLnw0Y0rd+jIr12Z<9S~G<+ zGUlGcKjRcX$aTE2O@p^6tTj&AY19Y^cB0 z@zjF9=MBIalYNtU@h)OMIlZdCE4?8=T0TZm^)VA^ZtPqe<_&yI2cIjW<-kn5j9gX| zXeoV&>&QATkh6eC@%JK$pR^N^H2n%=w+Ic-sM!n0iKb7ae$2c7b~SpAab)-O79sD* zXkLRmMmsJH^=N4LRs*sE0u58HjLAmRr;w)JYriYU8vwY!731;Ujb2Ml0hXOeiI##< zoBoDrdY>ckE$j9#<~Xy5C@fnUX`YHxgLY|S zJ{p+9;bnXzd;qfS#TvFaW>LDPBk!2u5X^9~46SQtIA%I&B4i9-Afy5i7!C0m+QL&< z6nv}Ar6H&D^4>{yiabyFhL2ycAxP&8e@62e7X{u*N-**nGJA$7WXsr;T_ehKy>x+a zwLTg885D9c0=F`WH{@!MJTO6W0p%6WaIlxokzOTS4H=@~h0lO%n|1Y<6vtz6=I@tE{b_kW3bqIk3Ps)0+*|b&0-ylVAbL8{ z76rUyi3uMwPWS?OsTIq{(c-kfU;N@18GdhJoC%_7a6Rzs<2>0%d(!nJh<^!bc!z)9 zJ+tDgKMvdSs(<7-n+v>YcKmFO>({$7gq9^dwI|C1mXWcf1G(5s*AP;x7%5UBAE&q`M!cA+jUaGe8_742=<=%{XnYm$w4P-%#p%p3lT_ z&MbM`j=83j@x&W|%FUhJ*~1Y|Z}yY@YJK!_<<%$Ht}Hve{adaG8x}t0%W$-lka+2q zmtl=s%~E)w^$;MCOPj)L4Y8X}u|p=yIPKBil`BC7FHp_rLY;vrJE$7Y&N$SF^Qy2E zkft+1%z>LJO?fxW7>zMI7rrL-LaH!)DI^U?qrf{I25%s*hXcb9eLB2rOLQ?X6Tjf^ z9a1hZBfe^v`n?K|gjKt$ntn4HpEn$zT>*@?o}nS+Webeoq1`IM^JZj=iVRFSDLsOg zH$%VJoA2!tEu&c8S2cmou;1$T^zU*|>LvV9Ry`)JIl33}ITX06W~cV3If4)(4W(dc z+m5cC5lV+JgiS$XoaGs|s+eRET2;fyrLY3LY`wqz=g#1*g7tymYmD|()Hc_(U|2DF zoYD<3-0LOK5Z{9WW)E-;edz9%BI9>_$9J&(l#g=NCM9FxQ+|~pFTPnww+h)>I$R7x zhDtpBt1v`gc+8jgq86ARZ3{TdkO10nfWFUU;$%UtZ+9 zm}NQDAXV622#iw?)D#SFF)swJ!pk_j6hzX|4>3o>G+PQoC)4@yqgSsTtyO`^mK4%z zh{{V-18O+z_`K(W-&wbA3iy#B+5}6Kh#rlXltn0o;=Pak?Z5rE!+rC!SB;_|1u`Nt zN;<)DhAiZQJU@I!=g|cTtXEsjtXCc+@9N0~MDyOvB5(nRpZs@KOIElh3P(s$^?}Kz zurjzRlA+y?;q6}0v(0FCz1t#d>B|Mg_YpAA%?Ekj_;$^w6AN#Rh z(F@cInF9M1IqfOG)@TLJb(rj285La3U1oC4C@(LXNj0y?xrnq5dhNA!zJy<$l4|%3 zRq~S9cX5c^w(7oFDMPf&EOM0;c?KHt8CY$lv%mH|j;NLe0j_MrdSqTY$-;3~5*)2z zvieiAK;W5r%$FGI@o43pY6tT9km~nAtlt?>)idPH=;T!7#kJw)l5+Q`Uc(^UYa(Nw zMt0tMGXk<(d?L-UA(z&I%SB(VUf6_qMRbc`0IO%Q1=foaTRJpgbr!O@!%rK>FDLv8m_!JOV8HZfJ5l#cAU1(pp47Eh` z4bfURMz(eEwLLbDD8?zvKJn}}G&{m@i0ucp2tT> zI6{JV?$6_2lt1_3GFE_kDrt!#n0N$3Ml^O?z-Uvt@FVE+-cRWNYiRD6V$WrSKiMjy zl^3O<8A?=keYhJGJbpcVNP%kbzN_zT(gB|Q6+k?%vxh8%PRQx<4Mv}M(md;_Fyxer z(qCFNk8|<9d{}BpwdUxzs=RC&R838OAa5;WHe;77oJR!QHKM`X9elD}!Mo3CgQ2OU4WP0=Mzc zM=i^Rc1wS6!_8e=c^R#|T+#e)bk}qH66j+>ZvgxmAK?~Lpw>*60@7vQZQanTSI-v2 zxkwd)U^@`r)OQrgT(vJTQkawwc0Cun*-a`i++3QYwY2~_Y0F!JaRM~ zM)P94tD)82MR*e5yu`A#%1{p*_+-?3!D7>l+yt8i8_3Re%ldY&85W_1+LR$lRgd*S zD1-tqK}a-#;}^2L4Mic3-*B z@qD>b5T#|OTrZrVk;=evy?1F$;bI^sB{jps_G4ITU-`;cBtmE+X{N@$=)7>a!b;ld zEw)PNmr?5SGD;WWF%d7On}97-|05wJk;tWhpUOlGwHfR zN|fel`>fNBBco}I?gs8f9tm99cJR#D4YLYMlv@3mXQGqvye;ZUg2Gx}`u-;+Z-CvU zJJR?-gvGMCHk{yXk{iqzZwA++9YXrazw9@~8T13PZoYk^QB|T+=(MeXwwsqAkO5(6*qI#e(?_MAZ%dFzs|YBA4h%cV!Ck9pxlYXx$x zq>Q3Yu%(kd*+;>bAXODJTZ)!z;b&FAD$!$VAvnVegtU@Y2~wT-r?}zPHo^K66qtCq zNF|a{E>RiJ^rBAMsTO{EFUVXfk;3=cJRG|OTnmpnt6{Sk!We%b@fw67%FW#ij1;X^ zN%Jn8QB($Rrzz z(L9uEY|JNVtr%CW6oDm$1KIk`m)+XJwTdhSdrXFgkVw@AzP2Y)$c&T?hbX&5r}(^0 zupygK-=vztQknr%l zym#X5s7xf9A$@oXWNW7gg!irI{r_HK-yd)=F0qvjtRXU@tGwY^d0zJfMjOF6iw}>c z--5JsMYkTd&bc)H$e7hB(~w(&D8yS{AOHKiJzxIvm%T6>das$w(eLSB z&Dz3KC=rDj2u&m=m#Dn(*@fuQfK!OMUWjIR5>8vV7A;(_6;;96(}|F#drf~J1-PUU zLh8jbs3kIHBGn%UvP5}PI74FUResLh@C+|w;dAY7cnUt+qJ+FFHT=; zP`{GcckZprdm&?<8)4{9QV8*)3N%c1#bj(4ZDjak4XoQv6l@|2pu`L z%iMp@r^md6m!&?PY*B{iA*_;=NqHyW#k$@E-J8R0;)V<0lwXzl`uzm>0qu9+PcqUWvc)8^6)_y#3Nb(}b^?PM;=(P|$$- zrpBPgTi^779iCF>io=!Ez=nAr!FgX|2wy#qf?TbzBIjMkjFlImq^FBr%XQK&sYjSd z;?|AACD4%9cC>E2YfZ3f+1>!4^}W_K+XA~brU@pVp#Dv{#}!fRTn#s=(}+`+Wam9a zWRE%Ub1}*|sphyLm33hf^f$A&do)0xxSu8HCJ1C&#F%QgR7c| zbkqgOdbGJgu@LU8%WxM3OB)jTt z@wa{3w|(#TelM56!egHNs;aNB(+;`x5;g0SUp*J*2M_5NE0=mKuYQv)JEY~s>@!QW zA;9q;%ec(38I`n*XF3R34Y&&@qZC|Hq^X9v@|Ha>X8Nb=vAiLq2pX1R_+$7Z@<^_R zm~5vPM+$bWIa-G}QumVFjF7`42=S0qVes0LUJTy%A{Rm>4LpbJY9QOLLB?cUh7fb{ zUjlnSY({Es)@MjIHIF066o~7!1WWW7GJ?p8j6j{ zo`Fxfxx|5FY-=QgXri=iQNtPVq(pfSgM-kQ`){@0FF-Rw+l?_DpD7e*$UunCW*~>N zA=#$^mk7-i7zl-*Oo*!V#I;=Yz!Dj9u^ZOsf^AbOk{FA>Ms5dM-j zCL^_XMa`Q4SV5=0vEjS;ABI25Le3&j*<7YP&I~lRW=(cP8HUsntUm?cYYU+%BcuH) zJCL}WgU{d@&R!fiX8ZemI?TuH7w}Y=YerLk6nQU_EFCp#@8!RW(Bca@>wK@mt(fUS)SHe~dO5cx^{_ku*u zyzInV2g8g?DiH$1QuAttTI;ad5T?A?$F+J_dFecQ%lL@D3yH5sSS)eJ@|@sYjN$HC z=4fg_eF{s2Hv4&F57y7qE?!A5h+&l|Mr~W(@cQc4^J=E4DZrzi=M3KkN@0YhKsF>Go4~As){38V*8CL;J$^>?H&)Z?Akn?H+OK;3=;#20jw(Kma*t7V}5iL9p zsVCn6Ysl_}sUr&Sh{go^nYqQ$B24xJ4BIE2yQ{yl zV5cg8tlzfkgE^WC(4%RR>OUaS*ED1+yi!;8=a@I~4-ow9rulrZ#dX2>UE^QzD}IGL z_LW&1ub1JJm&?!;{Ak;M*~=@1Ul`~WI!#Ej z8$v|Y&XUS|A~+4X;uGnR3>hCG1%1Y1Pa~a6j}X$VQ-Bo6fJB%wrec7MXz%S)(2p5n zn26z-dS_!xS4K4aP6cCv(Hfq8E4Fo8_IDX{I$)bDW`%_;V+xmHVEh7CHbcQ&LKqqX zP-Aquq}CwOFq3kxXTVKl-f}(J3qN^gQmeeGM$2p1q-2bs zr{HhHc_!d0e1a1<nFK|63Lf%J((jiEyQP!%5F_A}u7yrad6qv|% z0Syv(OLK35dg&e;axMmC1VfE&vk0(@V*B-g-~GFPx4-r2Mg+u>T5rB*QlMC5&C>uT zgig8YZ#nMDhSkrg0DSfuS_HTn;AktYH}`=eni&p!Q@q@R=r$^XZoz_xp}#W`(J|@-|1qN@bkn zua8}7Gh%2f#t*K2deu-Rm2HJ_4B<6)NXBe}Md{hjM#qDKK;ZwMT9a)?Tb70vgcsO$ zH+OayX{%aDcC-&$oW#4zD3roo4sP)?*zHkO88RSwKP>OpD5~jHp%mv$<7<@hg|bR| z(Hk`@>t5jQ{OPBk{*qtvOZ=VeUdU|*2|I1w8T$5q8zWHTQqVxd5H5KA3oHJ-XlK%j zq0Bh(&m|X46DO;&H_VU%uD5O(@Qk@)5;ubvqZ3=!BDeZ!EsE;@#-K!o^7!HYfnWyv^Yn>aF8 zA-uCQ&X4coI7un0O0)XLq)jA# zF&SKNeYqM!3>8_R@*KsleB~?tV_N5-!w@qCOvaXFRS5(dPPSYlpLpfHs()=bzGA%H z`kwFko-cm!i~r$2{0FP8!gSV~J8+Bt!YnVOofCX-$VpJD;w>qK7SQ2gXlV6ZTkXZ3 z2AX-H=`jtnaWq4l8wh`S6MG?lfXGGRg~O$OGUL1#HRQQEw}u)!9liCjNxz(YesNc=Wanp9|k5YLghh7*?TKtmcLv-MytWz zdRiz<%V>#swF1w2@N+FN+R1se3$aIx_0b@t*vjC_PLsW%f<15fWq^P5kN(l$`+I-S zkB0kcej>bD{6fwe!cU|r$IM&Cl^1zZ87nn14xE&JNX7y$c(OoIg^$_LEKF^Jm~19yy&$kDyJ2;r5#HrpB0^E0KX2 z8$QWVTW6_s9l70|fljzjzPBb2KUwOB00fKT^%Sxn_*TYx3hY1mC;z11xn#WSy9(pa zP&xe#XNOx84cIUPg3oTKFbD!eZ?b+q=4TNO2^yq6gN>#aVg)P(z81#=)}Qzjf5J~z zU0lH4S8&xREGgqH{N~tqCJ9p3$|9s2?8uKsg(aANu}udbq1MKoso_8z8K(@~a0WGj zCdKf&08f)KL5BYX;P3zazweXgrsL5{2#%kqI1--Zq(g%sc&Fz#kqk*y=UGGc^chn3 zKyYS^cGe8R)Z~&eaiEOA@!4GK8LOnEVx2?pXVA=|q=g_j%#Z{rljUl5X&P1u8vaDs z)$n0`vM-61S5upbSbRU3`R?!jZh>z5O_0}6zeOOf4f)7YUsB||IF*!FN!6)$0L-Xk z09RoLtg=n!Di_*B9!j2PxBEUf>1FbsXec`*pD&ZTi7WcSyd|xqFHJ^ zro}mjY)drqDfL1-HTv$SWLc|}=6FCpDfcC|)D3M|HpZaVBwe`YWc@x2}LbSEB+IdfKi<3_Etfw=a9S#XB z%c+(9a-dvczck#{N{LPn>u)wmMf@k<>j5vC&J3!1{ zJDZ3i9Ui?`V)y7zKKaB`Z%=!XJppSJ@QLWr+|z&aZ~o1rEh(DcD|JEjkuZEWnQ@*p zHYo;?hFfZg3Uje3=(Scj99feNn1RO7TS+~d#?T6HXcoWz*Z+Fo5jQW0#jzm2?$`ag zZ~yjh@4o6k6!UF(+bybaw6ZfaU_GOZ)iAIqE>RaS*LsA}FzL9iechIwGUlTQ1b!+f zl5BilQ_ve~T-M4=^~C}g#YT2lbgH+5>03;$is^{5efAGT)@x;{uZE>y;A_@7!CN)= z7W3`swz=8UVF(14g@GeV&y`ni*fAKwFw=IkEPCO3clAH?hyIYCvWn`QizcAOwOy^y(vfDC+S>bLzn1&{z zS=%jahEjnzhG}N#YlX)_)=paX3wwemlN4F!!P`Zbp5fN>qY zT4#&qS>o%E^qCYEM;Sh``s}&q88(;gbPM>wAN)aIaO|CQS6aJ*z_9T>Byri8W}5+t zgW$#QcmB@b`P+WmZ)1>pKJVAgyUH&q-i2Ri?;V=*c^SAXd1&_X?|mw}13^T(7D_L~ zu0TBGSxiP*7#(**w1u2FTYIJ|;%SnSrHdBGpb2EVgi1uJM30&f^}FgY+3}mmsx3T) zxi$& zm1q;a#(&3md1=OA5>@+*(xzL_^ag(kr74P2|b`PQP`e zgcDa!CE>%1iiR_hka=^BMu)8ojZ1^j?=9SD5H7uDjvmem1U6;8*C)Sv>pjsrPf0I&=8^4uC3<4>dzL-5UXvkA>l76GIGq*4k7Do`|+3GYIPIqW{9@A zyd=fPa~=yjHCqtVcN+B?Zy^xR*)5>CFnHq!o;t6xsios8X57=>-q$4pK^O<hX?31J1 zRB<$3`yr$=#PG8Hzy8<%N@R;-_%&@3(O1vcG|R-BSFa@|?u@!!0xgjhW7OD6$`)&g zYz^_*kjqi7etkyigg0bBPFDm7P0jWxiBS!5dFkDg1aeZmc1@I)W;%&O^2X1FnQ`)G z=e^0j70hK=l-|RKS>Gw2eSR>Sj5vl1$ykc%T#WJIvqf20(~mq66Nmqw|MP!Jr1x0n zI{E8={jYmP{3CzlkNE!(ycw9zCD%E`A?119a}2}Emu?G-&_+ngUXv7(6tF9f5olNx z1XIK0Vo+wt#dTJ4+C}NzyWK8*8g1FVx_cb&(|6>;Q!chBHZborLrNWJn$5AQfQC+8 z;d;BQcLz`^!*ujGw+|$a|C*-|ZvcP@hG%Mg;8(x;)xY^S|K^YV*pEdHgg?_Zm(=P7 zR&CSKYXhkjm44p%a1E|+;-$ucWHVB;=FXBYvDid&ZykK0#oyP$#9`Y7b72=7P8nZc zuEhP?vFGDN!j-gKj43=C(aMqU9Qy z2!{9?(BUk^iox@Ged**-Xjuy6O;fC8@rO9!mQ?I=G1@FHO^Ak{IB!jbDeqk2VDc8o zFhp}dmuqjFPEJfcaRgry^K?RoEs8Q+mR5s}N!E~vX2`A{b_nBx8->-M$VIbZQ*%$S z&hC4d-bXoU#p)f=FMa7tCh|}I$v^47fztxEl1<_HI?M7r=jC3NYgH(Ut#^L9lvHIp zwyE;CRNLAv3Q|cop?gD1&}B=3Rp-QdssnLpz^xC8vh|M(yM zB=8^ngMZLjT9(JaUnpc}Rc&1z_jP)+?6a-2wYAhAT&G3Tq|l!_ohympY|XB@y1hoT zy`cEzdr9q=&BFQM0v-dY$@_?(NY(1C032WO`qoORz`RZ-Lp#I2&UWR!?hSy7w+dY_ z)*TXPo*s{I{L+2fhNCrYOrVYF6IO40;q5U_Ymr(Dk!G>wg+z<+0sWKVOY{MP&xc57 zYUXHOn1t5JEmkXuj2xmJ1g8N6o$T6TZI$$k8%e(I-W zm%<%Q7C$oc2H=N&=!bl^-8+-?i7!!_dYX#S*dyfSiV)|t@sXpQWOzjaH6^)jy|f@} zMU|`mQcsCen9jvGQ)DM4T`Rw#K%f+s#bc0H3&j|E3G}&wPnW;xo4(2KJ)^;kD%YjY zRsF(E%F~|bT1LkJL5QAL#*1xO8OfrNQj)FdmSO14iB*c30Pkc8J$KH1SQoQwm1$g9j}YsPnCMs zgGR(dmFAYx`kaJ!*|Ox4rKA-Qo?Q*mPH%-_nnE15mJMuJVE9s4VYHt98F0tUb@zAv z&fmc|7ekjMTuVWZsmZG^DIEvEe-G>jBR}yIKVjl<1WiUgre_F?#+c5d5kFiYW9O>C zg+CgPY9UpQUu!jhx8m-tDO6`(fyhpE20Jf9;Eug$IF9MNWmzlD5LigIUX!K1L#`)< zMg*QteX-pQxC}Kk{0R4|-z@A9hB6#oH}hZptAF*UpML6hq9F3>S1Ku*8a_~#zUiT! z^VqL8v>F6oAVE>pe^>fEJLO5YA}1Ygs0I(Idef{(eTGL>E^h$rg+puBMI~rUp2ZLc%E2DMBF^maeS4;|Kr@8-#f|TWBs+GtRd0qIe zzxB8Ns$cc1{LgU?hM%-lo?MphWAvYoZJN(>=0!gFEt>Ke?lAMVVtR<1PpdkS*BX-* zNV8?H49c2?Y9sXSTAzIK38ohUKlJ16Nv4usmAbsrWu%a8Q|yt6dxUG7PGwA$@b;R8 zEDEThu`lGNaDu;3_UqmNn9nAYVJ1p9c?a6Md&+by{>T6LAKe1tW7Z;DKCk`Wt}%oN zZ#m)^-j?m{75F)>3;jp6uSKG=NY@!Hg~|3&pCba-oU_R9A z*J~F7q|f;O;2->h9$vixaE{%2Y>-@j6cgW(J)al?>+hXu^~(}%AmdEIYdFs7D&+LGbdW%)Q?oBtKCj+>`C6fm|9|vHe-vTj37$Qj zhLvGxRsCP83DUo1ddp#;tpx0*+_FWiQJ!y=u$5$%>V<3ahzd*qhq)&ox1isMSWG5CSZgF+M{}hoJ@> zuUN^{3WKxtvW!q|D-1Kcj6NOk0VI1L4OAP1jy|0Xg#i^)7A|-$nhaj;j@{nsbRmR~ zocF_e;_1wIQfFRwo0dYsRoD}MSCCj>J4IGgum$&)fD5zBycE3e%Jn4vDv-De_2)9O zE8C7JlHjf*c6`Pbe0Cy+)$CSDA^v7@Mq$3TN#WYp`yqgnG)7_kMWODGI>{tbQR)c|ZHJKMS;06?`rQ+;FkK;Ku9T0G#DE z4Kp+)OV$PEImyTWXb|&nwjCOXrO~^b{^>vcr)(?hf~1p_p0^YN8TA9t>>qC@{_#4` zC`e)M=G8gSBb!dQq2|>pVH#=-4cL0JcihDq>IpL92eMnea9|D2prH3<4+oclsc%!* z9qSZ?LvrB&XT#|jzU!V|>PrEMR0f+@uZ0+D&D7$!gk;fx`lUvObXo?cvmPPZiOgp3 zE*3dgxePVll2%(=rX);D^fN#6Gd+pA#V8p66lX&OYDq!ZXT~#xok&qhwY<)T)E-~l zbbjM+{Eb%IH*vkD!U=YSifY&tC@>&;oO0=V`Z0^88%+qNcY~r33Sn1yB~2%gHzaRl zNMQY#aW+J5g(32`=_h!XxveT=OtuR3O@Ep*Fnb6d@?L}57LDDcEWWDPxs+!M+^HZ5 zX2gG2iiN8xzMhSrev5BU8McPkykr?4^T`xdl}nwj1)nB(8_(tfeHw;FIpJBxo*Koep;(e#UAD?YVy$5V zO=JKIyi6pQK#hK1<81$FJ`HB>`oc!28`N=Jyk!Z5Y$frc#tCS#nKdQ6Q` z-i3#Me81XWqFgv0I7vW^4R=tNY#p+faaEBJ9&>7`ZL3=I#TFD&#Vp)CGT6wfNp3{qukR&;1b3 z=b@g6Fg?gxz)3wB`?-J>c}ZmWSUgZk*3*J8dZp-ab_uITRIx2uwxu&hG?MOMV*1_Dl%qW6&$Zn26goT_F--0lT zt;fOFlWGCSTsWlPyXeO4O15$CJ2uWI5N_+FkkJxp2$>;7{k<3`yO7f9GlnZBq`ihW zTsTB4OP`3GNaXqs3>}11q}U6R{@IrWE&WrZ*w(F6I-i4tA2h$&Fh)#{K;G? z&qaZtzOuW42DZGIz*T6d8RBT!U--fo{4fo;1H&e(M@W;c1u|M62Eue0kelw?XY0I% zHZ-!nR_eE-H&JsHIPb+Eh@ips%{1FG_Bp+bTxP^D1q*)CnuvbjV}+NbA25`ces!vu zLRlD1K~)~m72L`rbK!5Di@KynaE5_>7+gpPAXz_f^Snqz0mw^w(K&}D6^^{cL2%hB zuX``f8A4ox3}5U?d*0pzGbW`5h_qy~)Oi`E#+5hmRe}}{p%xB7V>8-NFKvb?XRxiRjrQN6ux&*5)uQyZ zg&0yRMdEs1jZqDo4iMi8z_pl+H=?in=OY6ZL!m8GiBsn`_z(Z#KU9V3d)!wx0-A!o zP4Y7F#V>xbnc4zFUc;vISbMRL_^S|wNzr1Kx{O4Cxfo`FnKDr7);0osMz~%GJR^Nk zHN&LN9>{wlkG3piogZIWalraTiyohz)O=L<7-Z!!qnE{}-qI=Bp0R7iVd-IFE*@T7B2Kb@6!QI}h#xHmO~- z3SB~scCJ%G$NtewP~UpFWseEePR{dnevaN!m;Ga8DVT3+j+CV^JHxZjt4GMS6>Cl4 z)xeltR64wHwzd>2+p~hDc8-0W+E_?KQm*MBhes|8Fg&CF;!2@dHcpe>6hf+#T|M6P zE5LPrs->?77Ot2RJcn?@kW+*$gpvB)rO!g5xW^ps*ImkPj>pH zpll2DYS0v53l!|N83BUNJMxUPBvB^iN5DP}>WdQzdI_P%W#|;S44bmm0A@>ZN}c*0 zhG^lp67PqkDCwTj zR6W~C))@F9Biw}E-U9>m%??vWO=>M~!$gYZdQ?DuZChal(qvq~M@@vX=c>+Z=hn9! zJpD6lN@@w3pbOl6@+W`tC-sIztfaQrnlsmX%ZU6E+J&>?t8hutw7iV%DMUM`c?l^B zc#FTc2%^TE!9V-aDExi|Zf}y^7L?(J@O;#x&Icf+epld=1*pwE#M51$M%~xx&1uJP z(ez!Olv~3mSMptXFL-LMEN5)TCP6Mq<wjPr}r_^3Ak+l*#wUHTx|@|k># zXxa=bt7pU61sb-g3`n?rWw&;P@P3_Jc|SgT)6}bnmUR3birOvh@j`!@JU( zN0aRMdP8%x-Pt9Alr969Y@BY9K*NxW@i-hgzM&N^Ddh@i5lS>ZgI8ZYF8F6|(9xtQ zBoX~IiDc{9@%5Uh5H{}}d(-)1s4Y`%r=qf@C?s;q$?9jMtY-W;%aW@cq=e`L85i3S zA@YUOuyEqqB{srn>UV$iOOvMKdDKy42ywa!qyW*EvH5a+?oCN+#MxT}cIm3E4Gmey z_{HXRQFsmbEx+Zrkn+6P1uoo+hh6Y0Vwyf8O2i!7M_}_!MBl85z^C6(U+P>93xsEI zEd?nh`Ja-%^;^Hy7vd3wqTm8hmA5;d4l*Q8 zvw+wLw;g{?%ioRQ6>DvI8G3#|%cfVXgcMm2vxU*1b#;Ce>(BMn{w zdS1P0nuUESWPuq7KlgJ#=jne_6D34T{1SD^_~dXYp7a7ourCs=5(Y#`8e6ZbkcJT; zUGJpWTAKJ7Xj;m7;m;X7?yR=Q%$*w1$eu*Ke0w zoF=8<86Qx5E9_l|kCLUXW`a_18R`x75Umu|U;o;qOTnP5dBfw4nR`ypPConp+kg9S zds{21drSO!xfsfDEBMd<`9FL5cdx)Xt9orcUpM}nTj7W0Yq5HHqu>(i?q>eh!iqQ? zxBa;{3#YZX@U5LdpdNyNyJ1x|%>LT-vR}k2^#Zv7wN63Yz zRIz#KRE(F~j8kE>j7F40uuunpI_3F)f9@dP+T=4>pp0S9q!Ot^h`}P(#PnMfxsX+-ki%Ex?hCo@0dN&Zy zB#ty!^*k!Ty;pIw)vQ&==3V5+@Lwq8v+r`ewt^O2-%UYyZ~6`04XvHGjQ7R-?3o_N zy68pKZ-Fq=5y(3{Ci2&zN%}daYYI)LS+hG9UIX?xyQETU9X?A@c!<8~Q(zdLQL`Dd zw_K6oUcd%EX>_>DRS)U0qu!U>`VFwkNQ5#Pemz$@?x&*4en}S@h^eO_s*HN?9<5Sp z3ukDj&SuIu@pswLBqCnA>P(hbgKy<4OpgHH@Ll@TiQg28%2i-V^=Wnz*!WWGl@XIs zsgc=mOhb(~SqPC_O@TdcQ4k^e(_lx_v#BL`BfY&s8F@2K!j1H{O#zVRUF^+OT$J8< za1ZrFs_#fEp7%mw$P1E0$FCvp-5V0nfs50~#9sO!hSO`OirDJ%##9?enl_W$-8n7sV z?`^9GldTm}B_Vp^wP+QS4M&J?uPuT+!A0pc-m{0x9;d2!6(Fyr<8>yx2i}6;u7+jz0ihv- zV1y|%G_v}8&}TR8Kt(d7%Uf*_&1KU2io3>}1DXB7{mOo?f&y?(KEyCwuUV^SbS&M) zD`W^e@e`c)V+^t#4|8@; zMBb&bWifpmQC($@y`?Y&BJ*}{FK`)OWl*NpC6G;a3to1D44_1MN6t`@`d8(Oto|lQ zO-8n-d3T-`2dI_n8FA#EvWj|!`HRQ(;a>r=(5}tWWD76;) z?dbNe@IZp&L(=EP$&UQFFW+Ptvp+J6f=QVLo#xe1?tNsRFUHYlTrvs|pMkLQ7`61@ zjy?;ysGYGFmsrcLFx%&PPD-^5=f0lzD_{AF7rch2Li6&iP?I$;1YS~l_2&(QWV9^n zWyj3xC0`9)NP&wITX=noGcV&}H%z2lcgpP!oT>GTA^I zwOmy8~Qy_k7bUMyF&y{jJ~W5bg_kU_b|?l4z2 zKEy|2CgLy0JG6b5#zN)N3)G9+bQp=W=IVDh@V>DJ0(_eEi)Ay;260`K!k6fLes48a z9wtH}#pZ%b8ndNlHw$GH6<=TWKv9hT5MfNiT1>qZ5K*dP7p^~D*+3%fDq#rJ!xMQU z{r3KF0q1?MG37^Qnh3QBKr?N!W^uY66?h-PA@e>jGCpG=CwQ?_vu+)mqAL4brD)C= zqmAIL34xd+kxNEHoInPeb}8T-Ucyh;$Jiexdq!;v%E&&wrgkZhSKG6JS>I2=XFnC^ zg}@iv+Fo>CHKZB;mizt?(wf_sPGGs(DNNa8u7+2o6V()U3xa6wnJa@K`>SOCuOL5n zh~GN_>t3^1$ZZwFN_olQYF8Nd^?Y--y1KZg&)e1l86(FznG5;FZ&bFq%;Ji_ATCnZZOn%3X-?r{ z6dvgG781CsHiTFZpYQsMOe(QrT+v!;3feUHh(85}KpR1)T!xDxrKL%bOEbi*KgA(h zA=Ph~QJ+h1Sbuui@fXF&ZofuQgK7gEvvE>0G#$48mhOvR{363S?ASb+w5aEMffAA3 z)4=#;Z`hiL=nbupS3BQ!LofuYME%|ps0v8As0q&C@|Be`vbC6ubTG?8a8WXbx1qka z?vrnb`n>{G0|m63;@dk09UNXr&%45b8YC$O4iPgdV+ip1hBhfoYE7h4AqdI?LbS+L zD^XQd38wOd`18>Hv2z5VRt?$uY6iA|dM_d#tR-@>xF?uJG|ZAtBq_b5dKqoRzxg-+ z#-Hx>_9OB*MFAn%6&$THAPUG!a|-ZtxQuEmumYCrG`MJ@G3Z!_vxY!?kJo+tQB`oX zn5UXsM{jaY@U=GaGsN&jOuxJgZA^u&o{KBaM2fnrex{uF6bz|fmM4uf>Pf=R2JaS- zEwBw@ERk6x=)kbYUzQAf-bF<_ktZwp8iA`VeN|nMcjZMxZl;TRNi1?_H6xt@(`hN# zQd?x7ZT0B<67eUiC6yWl;xxM%GQ#VN?IVu4R{#J&07*naR5U`6*%LfD1J9C10J2NO zmSwbzF{g7e9tl&pFrOSE*Pir#O~KFB>oLvSsy@5&aZI(wYp6FI?x5IvKQ?u*ce680 zHgZf0-H@S0sJHaO)8{&sBS7r2HbDrxAx^P@hQm)+Qum-YUC8IsOZ}wI%68+{49Bd9 zYGyOcJus=55K$8!;nYb4XU547AA+M9whIJ%MQONK7emd~c%1SfoIS-XkYT^ze#HNm z{?cFiO+u~lgvVYkACB)&CiF}cvRyk~ZZmX}UhoQ-iH zvokzoyEH@@YAfun^q#IndKEezp8ved@~g(qrw4ax$*yNq_QfzSAH917Pp~!d*}O0I z_$ZnboPL6FD6DmQH)N!+s)U%Ao~vyoWvI8OeuV(&sh79?Es^1J6`M%0Avg_N*b^KX zp@#gfhOk`iGG<7<3SIJ_e)_3b<;Z5Infv%cvW1wJUHFk7`4Rt_2E%oMj8L5v*ju-i zmlvNx-zkr2xOy1c@MIx44w7B-nFXEQE^>{33%)me4Xy`dWDAc2WH(GfUt0*H8sHf% zf?*={DVMR>2oUY$q#t;9zUh?Vlse=#Vj*XW(=O&jPP-KKC(d3~Y$AreD6pM@2&QRy z4I>A#;r4FBy!EXNmsvnAo0rf|u72{fz{A-ySul&E*(BM`?V zt9KJ~GKD;v=PHwJL#hK`|~_PFnVb@R7r7zSqi`fVRpq{K=#(%0&>lm(QJ72wTgr z1Y3pSA%^s0l4a;20&C@3F%88+vcJ~7GNh&9Zao7~YLyx}J;|p%kaDJ&B^SR z68X~KG99uF>o3g5VH4VVR!_sorekPx4LAE!oDIieSTQvHbPVBvBZL6gPUW`j!f6;y zRQdylWGr^Z>RCug1x)rFo`mps^_TFIHrI?Z#mRi$R(1;iA9eTgYU{V1^&b2m#6a-4 zf4OMlCGiqaF&-mZ6ANA<#F_EqvsfJt~++ri>T^5{oyc79gqy45Q*j2ZB+I5|tyE;{X zT<|g?$1x<*_Jb7Owabv<>EF*75z2^PqI4egC#?b?_*)G|6;?_5KtnBM!>*Y{o#{dp zmgYU-B!7VYu^#|lOxbIj7hQo7IwV$~Vb%04N-K!j5i*1ijAJ1*pq8DoA)`KoH{;9Q ze+d({OWKZ=-Hb}ne67Q7y6nK4IiCD`!Vuz`8!f?(MLI3JD}`GQ@8AC0fBP@}rN88s z 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≥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 w%hSjZbh=J1+!OLrT7VzqYFP* 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 R#wh>*002ovPDHLkV1kC15n})V diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml deleted file mode 100644 index ca6b9c9502..0000000000 --- a/examples/notebooks/pixi.toml +++ /dev/null @@ -1,13 +0,0 @@ -[project] -name = "Mojo notebooks" -version = "1.0.0" -description = "Environment for running JupyterLab" -authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max"] -platforms = ["osx-arm64", "linux-aarch64", "linux-64"] - -[dependencies] -python = ">=3.9,<3.13" -max = "*" -pip = ">=24.0,<25" -jupyterlab = ">=4.2.3,<4.3" diff --git a/pixi.toml b/pixi.toml index 43e10f94c6..af506b0fc5 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,15 +1,13 @@ [project] name = "Mojo" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max"] +channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max-nightly/"] platforms = ["linux-64", "linux-aarch64", "osx-arm64"] [tasks] -tests = "./stdlib/scripts/run-tests.sh" +tests ="./stdlib/scripts/run-tests.sh" examples = "./examples/run-examples.sh" -benchmarks = { cmd = [ - "./stdlib/scripts/run-benchmarks.sh", -], env = { MODULAR_MOJO_NIGHTLY_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo" } } +benchmarks = { cmd = ["./stdlib/scripts/run-benchmarks.sh"], env = { MODULAR_MOJO_NIGHTLY_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo" } } [dependencies] python = ">=3.9,<3.13" diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 74bf38c133..35ceaec387 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -25,7 +25,7 @@ from collections.string import ( _calc_initial_buffer_size_int64, ) -from utils import Formattable, Formatter +from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select from sys import triple_is_nvidia_cuda, bitwidthof diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index a08fd52eaa..b8c20c605d 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -29,7 +29,7 @@ from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, StaticString, StringSlice -from utils import Formattable, Formatter +from utils._format import Formattable, Formatter # ===----------------------------------------------------------------------=== # # _file_handle diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index 93f1482cf2..d5eea357af 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -23,7 +23,7 @@ These are Mojo built-ins, so you don't need to import them. trait Stringable: """ The `Stringable` trait describes a type that can be converted to a - [`String`](/mojo/stdlib/collections/string/String). + [`String`](/mojo/stdlib/builtin/string/String). Any type that conforms to `Stringable` or [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) works @@ -77,7 +77,7 @@ trait Stringable: trait StringableRaising: """The StringableRaising trait describes a type that can be converted to a - [`String`](/mojo/stdlib/collections/string/String). + [`String`](/mojo/stdlib/builtin/string/String). Any type that conforms to [`Stringable`](/mojo/stdlib/builtin/str/Stringable) or diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 6d0efb6a9b..048bbd4ac1 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -20,7 +20,7 @@ from sys.ffi import C_char from memory import memcpy from collections import List from utils import StringRef, Span, StringSlice -from utils import Formattable, Formatter +from utils._format import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from collections.string import _atol diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 6a39c4b6eb..eb6cf794fd 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -24,16 +24,8 @@ from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy from python import PythonObject -from utils import ( - Span, - StaticIntTuple, - StringRef, - StringSlice, - Variant, - Formattable, - Formatter, -) -from utils.format import ToFormatter +from utils import Span, StaticIntTuple, StringRef, StringSlice, Variant +from utils._format import Formattable, Formatter, ToFormatter from utils.string_slice import _utf8_byte_type, _StringSliceIter # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 35856b1e37..11c151874b 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -129,7 +129,8 @@ from collections.string import ( isprintable, ) from memory import UnsafePointer, Reference, AddressSpace -from utils import StringRef, Formattable, Formatter +from utils import StringRef +from utils._format import Formattable, Formatter # Private things from builtin._documentation import doc_private diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 833f5d5182..244f1e0e31 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -313,10 +313,7 @@ struct NamedTemporaryFile: # python implementation expands the path, # but several functions are not yet implemented in mojo # i.e. abspath, normpath - - # TODO(field sensitivity lifetimes), eliminate tmp. - var tmp = FileHandle(self.name, mode=mode) - self._file_handle = tmp^ + self._file_handle = FileHandle(self.name, mode=mode) return except: raise Error("Failed to create temporary file") diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 4e74bd8022..ec2d03c08b 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -21,4 +21,3 @@ from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock -from .format import Formatter, Formattable diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/_format.mojo similarity index 78% rename from stdlib/src/utils/format.mojo rename to stdlib/src/utils/_format.mojo index 069d465691..65cd2b80b7 100644 --- a/stdlib/src/utils/format.mojo +++ b/stdlib/src/utils/_format.mojo @@ -26,22 +26,6 @@ trait Formattable: """ The `Formattable` trait describes a type that can be converted to a stream of UTF-8 encoded data by writing to a formatter object. - - Examples: - - Implement `Formattable` and `Stringable` for a type: - - ```mojo - struct Point(Stringable, Formattable): - var x: Float64 - var y: Float64 - - fn __str__(self) -> String: - return String.format_sequence(self) - - fn format_to(self, inout writer: Formatter): - writer.write("(", self.x, ", ", self.y, ")") - ``` """ fn format_to(self, inout writer: Formatter): @@ -90,22 +74,11 @@ struct Formatter: # ===------------------------------------------------------------------===# fn __init__[F: ToFormatter](inout self, inout output: F): - """Construct a new `Formatter` from a value implementing `ToFormatter`. - - Parameters: - F: The type that supports being used to back a `Formatter`. - - Args: - output: Value to accumulate or process output streamed to the `Formatter`. - """ self = output._unsafe_to_formatter() fn __init__(inout self, *, fd: FileDescriptor): """ - Constructs a `Formatter` that writes to the given file descriptor. - - Args: - fd: The file descriptor to write to. + Constructs a formatter that writes to the given file descriptor. """ @always_inline @@ -124,24 +97,13 @@ struct Formatter: func: fn (UnsafePointer[NoneType], StringRef) -> None, arg: UnsafePointer[NoneType], ): - """Constructs a formatter from any closure that accepts `StringRef`s. - - This function should only be used by low-level types that wish to - accept streamed formatted data. - - Args: - func: Raw closure function pointer. - arg: Opaque user data argument that is passed to the closure function pointer. + """ + Constructs a formatter from any closure that accepts string refs. """ self._write_func = func self._write_func_arg = arg fn __moveinit__(inout self, owned other: Self): - """Move this value. - - Args: - other: The value to move. - """ self._write_func = other._write_func self._write_func_arg = other._write_func_arg @@ -169,12 +131,6 @@ struct Formatter: fn write[*Ts: Formattable](inout self: Formatter, *args: *Ts): """Write a sequence of formattable arguments to the provided formatter. - - Parameters: - Ts: Types of the provided argument sequence. - - Args: - args: Sequence of arguments to write to this formatter. """ @parameter @@ -208,10 +164,6 @@ struct Formatter: fn stdout() -> Self: """ Constructs a formatter that writes directly to stdout. - - Returns: - A formatter that writes provided data to the operating system - standard output stream. """ @always_inline diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 741bedae0f..58ab56bcfd 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -23,7 +23,7 @@ from sys import sizeof from memory import UnsafePointer, memcpy from utils import StringSlice, Variant -from utils.format import ToFormatter +from utils._format import ToFormatter # ===----------------------------------------------------------------------===# # InlineString diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 513f12377f..e876b3082e 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -14,7 +14,7 @@ from testing import assert_equal -from utils import Formattable, Formatter +from utils._format import Formattable, Formatter from utils.inline_string import _FixedString diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index f132356755..9d5a6a4d9c 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils import Formattable, Formatter +from utils._format import Formattable, Formatter fn main() raises: From 043b597ce9bfc184cc75670cdeae788216ed4641 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 29 Aug 2024 18:56:08 -0700 Subject: [PATCH 1486/2019] [Stdlib] Add bytecount method to List and use for constant mem init Adds a bytecount helper method for List. MODULAR_ORIG_COMMIT_REV_ID: 8567bed1eb1b1d67313a816cfc213ba9478e7b09 --- stdlib/src/collections/list.mojo | 8 ++++++++ stdlib/test/collections/test_list.mojo | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 51442b9108..af3ba54e5f 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -467,6 +467,14 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( # Methods # ===-------------------------------------------------------------------===# + fn bytecount(self) -> Int: + """Gets the bytecount of the List. + + Returns: + The bytecount of the List. + """ + return len(self) * sizeof[T]() + fn _realloc(inout self, new_capacity: Int): var new_data = UnsafePointer[T].alloc(new_capacity) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index db1230fad9..f03595948d 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from collections import List - +from sys.info import sizeof from test_utils import CopyCounter, MoveCounter from testing import assert_equal, assert_false, assert_raises, assert_true @@ -39,6 +39,7 @@ def test_list(): list.append(i) assert_equal(5, len(list)) + assert_equal(5 * sizeof[Int](), list.bytecount()) assert_equal(0, list[0]) assert_equal(1, list[1]) assert_equal(2, list[2]) From 05939f03711986ee4e6321c2f82fce99f2c5d7ef Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Fri, 30 Aug 2024 23:44:11 -0400 Subject: [PATCH 1487/2019] [vscode] Add a way to set the build and run args for mojo files - The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration setting that can be used in conjunction with `mojoFile` to define the build arguments when compiling the Mojo file. - The VS Code extension now supports a `Configure Build and Run Args` command that helps set the build and run args for actions file `Run Mojo File` and `Debug Mojo File`. A corresponding button appears in `Run and Debug` selector in the top right corner of a Mojo File. MODULAR_ORIG_COMMIT_REV_ID: 780d35f21eea2e0dedd54e43889e51cad3682357 --- docs/changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e0d605860b..1085e337b2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,15 @@ what we publish. ### ⭐️ New +- The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration + setting that can be used in conjunction with `mojoFile` to define the build + arguments when compiling the Mojo file. + +- The VS Code extension now supports a `Configure Build and Run Args` command + that helps set the build and run args for actions file `Run Mojo File` and + `Debug Mojo File`. A corresponding button appears in `Run and Debug` selector + in the top right corner of a Mojo File. + ### 🦋 Changed ### ❌ Removed From 98043a983d3e3d12ae6a225b2baf55e1cab4b1ec Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 31 Aug 2024 10:28:15 -0700 Subject: [PATCH 1488/2019] [mojo-lang] Teach CheckLifetimes about hierarchical lifetimes. This teaches CheckLifetimes (and thus uninit checking and dtor insertion tracking) to be field sensitive for lifetime tracking. The functional improtance of this is that it removes a class of bogus "use of uninit variables" warnings, e.g. Fixes MOCO-1025. MODULAR_ORIG_COMMIT_REV_ID: d550cccfbf29eab0152ddc88f744fcda6e4f0474 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 1085e337b2..a02025226f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -30,3 +30,6 @@ what we publish. ### ❌ Removed ### 🛠️ Fixed + +- Lifetime tracking is now fully field sensitive, which means that the + uninitialized variable checker is more precise. From 51f710978b0a1a81b7ca783fbbd111db49bb7648 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sat, 31 Aug 2024 21:35:32 +0100 Subject: [PATCH 1489/2019] [mojo-stdlib] Add a `as_noalias_ptr` method The implementation of `exclusive=True` is not very robust. The metadata can be dropped by the optimizer before it reaches LLVM. This introduces a new `as_noalias_ptr` that is more robust and has better predictability and composes better with other optimizations. The flag remains on the type so that the behaviour of loads and stores can be altered. MODULAR_ORIG_COMMIT_REV_ID: 44b44dc0e958420c3da47408cb2b5a2ed260c1ab --- docs/changelog.md | 4 ++++ stdlib/src/memory/unsafe_pointer.mojo | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a02025226f..cf2324470d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -27,6 +27,10 @@ what we publish. ### 🦋 Changed +- A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method + specifies to the compiler that the resultant pointer is a distinct + identifiable object that does not alias any other memory in the local scope. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 210877106b..8bc71edb23 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -72,9 +72,7 @@ struct UnsafePointer[ T, `, `, address_space._value.value, - ` exclusive(`, - exclusive.value, - `)>`, + `>`, ] alias type = T @@ -403,6 +401,20 @@ struct UnsafePointer[ # Methods # ===-------------------------------------------------------------------===# + @always_inline("nodebug") + fn as_noalias_ptr(self) -> UnsafePointer[T, address_space, True, alignment]: + """Cast the pointer to a new pointer that is known not to locally alias + any other pointer. In other words, the pointer transitively does not + alias any other memory value declared in the local function context. + + This information is relayed to the optimizer. If the pointer does + locally alias another memory value, the behaviour is undefined. + + Returns: + A noalias pointer. + """ + return __mlir_op.`pop.noalias_pointer_cast`(self.address) + @always_inline("nodebug") fn load[ type: DType, //, From d210992657afac416dcb768abdb99851c8b4d7c4 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sat, 31 Aug 2024 23:03:44 +0100 Subject: [PATCH 1490/2019] [mojo-stdlib] Make LOCAL allocas generic addrspace NVPTX backend doesn't like non-generic allocas. Just addrspace cast to LOCAL to fix the issue. MODULAR_ORIG_COMMIT_REV_ID: 244742b797b3f6dde1158c170f7dcbd1f05ea1e2 --- stdlib/src/memory/memory.mojo | 51 ++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 6079df384a..fb375b4df7 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -409,25 +409,38 @@ fn stack_allocation[ """ @parameter - if triple_is_nvidia_cuda() and address_space in ( - _GPUAddressSpace.SHARED, - _GPUAddressSpace.PARAM, - ): - alias global_name = name.value() if name else "_global_alloc" - return __mlir_op.`pop.global_alloc`[ - name = global_name.value, - count = count.value, - _type = UnsafePointer[type, address_space]._mlir_type, - alignment = alignment.value, - address_space = address_space._value.value, - ]() - else: - return __mlir_op.`pop.stack_allocation`[ - count = count.value, - _type = UnsafePointer[type, address_space]._mlir_type, - alignment = alignment.value, - address_space = address_space._value.value, - ]() + if triple_is_nvidia_cuda(): + # On NVGPU, SHARED and PARAM address spaces lower to global memory. + @parameter + if address_space in (_GPUAddressSpace.SHARED, _GPUAddressSpace.PARAM): + alias global_name = name.value() if name else "_global_alloc" + return __mlir_op.`pop.global_alloc`[ + name = global_name.value, + count = count.value, + _type = UnsafePointer[type, address_space]._mlir_type, + alignment = alignment.value, + address_space = address_space._value.value, + ]() + # MSTDL-797: The NVPTX backend requires that `alloca` instructions may + # only have generic address spaces. When allocating LOCAL memory, + # addrspacecast the resulting pointer. + elif address_space == _GPUAddressSpace.LOCAL: + var generic_ptr = __mlir_op.`pop.stack_allocation`[ + count = count.value, + _type = UnsafePointer[type]._mlir_type, + alignment = alignment.value, + ]() + return __mlir_op.`pop.pointer.bitcast`[ + _type = UnsafePointer[type, address_space]._mlir_type + ](generic_ptr) + + # Perofrm a stack allocation of the requested size, alignment, and type. + return __mlir_op.`pop.stack_allocation`[ + count = count.value, + _type = UnsafePointer[type, address_space]._mlir_type, + alignment = alignment.value, + address_space = address_space._value.value, + ]() # ===----------------------------------------------------------------------===# From 7c39ff762cd8b9c2576b3f90507daeb8bcf7f251 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sun, 1 Sep 2024 00:47:33 +0100 Subject: [PATCH 1491/2019] [mojo] Remove redundant `address_space` attributes (NFC) The address space is redundantly encoded as an attribute when it also exists on the result pointer type of these operations. MODULAR_ORIG_COMMIT_REV_ID: 002acd951cf84c65b759cf1e4ebb9ad54563ea4c --- stdlib/src/memory/memory.mojo | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index fb375b4df7..737fe02275 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -419,7 +419,6 @@ fn stack_allocation[ count = count.value, _type = UnsafePointer[type, address_space]._mlir_type, alignment = alignment.value, - address_space = address_space._value.value, ]() # MSTDL-797: The NVPTX backend requires that `alloca` instructions may # only have generic address spaces. When allocating LOCAL memory, @@ -439,7 +438,6 @@ fn stack_allocation[ count = count.value, _type = UnsafePointer[type, address_space]._mlir_type, alignment = alignment.value, - address_space = address_space._value.value, ]() From 034f54a2be938980727c9d077cc3fccc194f37bf Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 31 Aug 2024 17:02:44 -0700 Subject: [PATCH 1492/2019] [Stdlib] Enable compile time hashing The only thing that was stopping us from performing compile time hashing was the use of llvm.memset which is not necessary. Replace with a more efficient vectorized memset and add a compile time hashing test. MODULAR_ORIG_COMMIT_REV_ID: 330c93b84aa01aed33cdccbc8624ef5862f4a3da --- stdlib/src/memory/memory.mojo | 15 ++++++++++----- stdlib/test/builtin/test_hash.mojo | 9 +++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 737fe02275..844ee99570 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -276,12 +276,17 @@ fn memcpy(dest: UnsafePointer, src: __type_of(dest), count: Int): @always_inline("nodebug") -fn _memset_llvm[ +fn _memset_impl[ address_space: AddressSpace ](ptr: UnsafePointer[UInt8, address_space], value: UInt8, count: Int): - llvm_intrinsic["llvm.memset", NoneType]( - ptr.address, value, count.value, False - ) + alias simd_width = simdwidthof[UInt8]() + var vector_end = _align_down(count, simd_width) + + for i in range(0, vector_end, simd_width): + ptr.store(i, SIMD[DType.uint8, simd_width](value)) + + for i in range(vector_end, count): + ptr.store(i, value) @always_inline @@ -299,7 +304,7 @@ fn memset[ value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ - _memset_llvm(ptr.bitcast[UInt8](), value, count * sizeof[type]()) + _memset_impl(ptr.bitcast[UInt8](), value, count * sizeof[type]()) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index e69604df9b..1bf14b85a1 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -138,7 +138,16 @@ fn test_issue_31111(): _ = hash(Int(1)) +def test_hash_comptime(): + alias hash_123 = hash("123") + assert_equal(hash_123, hash("123")) + + alias hash_22 = hash(22) + assert_equal(hash_22, hash(22)) + + def main(): test_hash_byte_array() test_hash_simd() test_issue_31111() + test_hash_comptime() From d4095fc336b35cd2759fbe451038b7f5eac4bcb4 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 1 Sep 2024 11:10:35 -0700 Subject: [PATCH 1493/2019] [mojo-lang] Upgrade exclusivity checks to an error. This changes the warning into an error since 24.5 has branched. Also mention in the release changelog that this will happen. MODULAR_ORIG_COMMIT_REV_ID: 5cc2a6ddea21a3f198d706734289614ab51e4c61 --- docs/changelog-released.md | 5 ++++- stdlib/test/memory/test_reference.mojo | 2 -- stdlib/test/utils/test_span.mojo | 2 -- stdlib/test/utils/test_string_slice.mojo | 2 -- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 52e4d4f88c..61c33ad0ed 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -116,7 +116,7 @@ modular update mojo fn invalid_access(): var my_string = String() - # error: passing `my_string` inout is invalid since it is also passed + # warning: passing `my_string` inout is invalid since it is also passed # borrowed. take_two_strings(my_string, my_string) ``` @@ -128,6 +128,9 @@ modular update mojo implementation details are somewhat different because lifetimes are embedded in types. + This is a warning in the 24.5 release, but will be upgraded to an error in + subsequent releases. + - Mojo now allows implicit definitions of variables within a `fn` in the same way that has been allowed in a `def`. The `var` keyword is still allowed and still denotes the declaration of a new variable with a scope (in both `def` diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo index 8d71065bc6..90be5fd1b4 100644 --- a/stdlib/test/memory/test_reference.mojo +++ b/stdlib/test/memory/test_reference.mojo @@ -31,8 +31,6 @@ def test_equality(): var a = List[Int](1, 2, 3) var b = List[Int](4, 5, 6) - assert_true(Reference(a) == Reference(a)) - assert_true(Reference(b) == Reference(b)) assert_true(Reference(a) != Reference(b)) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index b7c25d7a6d..f39d3a0982 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -166,8 +166,6 @@ def test_equality(): var sp = Span[String](l) var sp2 = Span[String](l) var sp3 = Span(l2) - # same pointer - assert_true(sp == sp2) # different pointer assert_true(sp == sp3) # different length diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 5591f031af..74e19889f7 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -156,9 +156,7 @@ fn test_slice_eq() raises: # eq - assert_true(str1.as_string_slice().__eq__(str1)) assert_true(str1.as_string_slice().__eq__(str2)) - assert_true(str2.as_string_slice().__eq__(str2.as_string_slice())) assert_true(str1.as_string_slice().__eq__(str3)) # ne From 8d2ce9d0ce70f2dfc36e5ef6c4f4264997bd2736 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 3 Sep 2024 09:27:04 -0700 Subject: [PATCH 1494/2019] [mojo-lang] Fix initself arguments in throwing init This fixes an obscure bug handling assignment and initself processing when calling an throwing function. We need to force an intermediate temporary when the destination is an inout argument or a subfield of a value that cannot be left partially uninitialized. While here, make this a bit more aggressive at using lifetimes to do the analysis. This fixes https://github.com/modularml/mojo/issues/3444 MODULAR_ORIG_COMMIT_REV_ID: b18d9105b27b2f934c6406aaf4d7684cb363fcb7 --- docs/changelog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cf2324470d..0c2849cc44 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -35,5 +35,8 @@ what we publish. ### 🛠️ Fixed -- Lifetime tracking is now fully field sensitive, which means that the - uninitialized variable checker is more precise. +- Lifetime tracking is now fully field sensitive, which makes the uninitialized + variable checker more precise. + +- [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init + causing use of uninitialized variable From 9eff9849bbfb36c1bedb60f430b14ce48e98dc8c Mon Sep 17 00:00:00 2001 From: Patrick Rachford Date: Tue, 3 Sep 2024 11:23:24 -0700 Subject: [PATCH 1495/2019] [stdlib] Remove vectorize_unroll api from example Removes vectorize_unroll from example in matmul page reference in favor for unroll_factor. MODULAR_ORIG_COMMIT_REV_ID: 3cae234e93dd8c22b3515c84674e7e49ad407b15 --- examples/notebooks/Matmul.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index a34ca9444e..cc2be3a71b 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -850,7 +850,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "One source of overhead in the above implementation is the fact that the we are not unrolling the loops introduced by vectorize of the dot function. We can do that via the `vectorize_unroll` higher-order function in Mojo:" + "One source of overhead in the above implementation is the fact that the we are not unrolling the loops introduced by vectorize of the dot function. We can do that via the `unroll_factor` higher-order function in Mojo:" ] }, { From bfc669de323ca213189d09e39af5936ffa8f3e40 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 3 Sep 2024 13:06:56 -0700 Subject: [PATCH 1496/2019] Fix magic add command MODULAR_ORIG_COMMIT_REV_ID: 7a2f5c7bae026bb37ec81d4ea9e187de58c33fe3 --- docs/manual/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 08dcbf6503..363fef9d10 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -236,7 +236,7 @@ cd hello-world ``` ```sh -magic add "max=*" +magic add max ``` ```sh From 48b276ed123e540c7c572cdc3d10224ab4d0f921 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 4 Sep 2024 01:10:05 +0100 Subject: [PATCH 1497/2019] [mojo-stdlib] Fix tests that fail with debuginfo (NFC) (#46511) MODULAR_ORIG_COMMIT_REV_ID: f1a51018968cee5866bf5eb07695f466ee14964a --- stdlib/test/builtin/test_float_literal.mojo | 38 +++++++++++++-------- stdlib/test/builtin/test_int_literal.mojo | 26 +++++++++----- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index db4841df4d..d9a246e53a 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -132,21 +132,29 @@ def test_mod(): def test_div_mod(): - var t: Tuple[FloatLiteral, FloatLiteral] = FloatLiteral.__divmod__(4.5, 2.0) - assert_equal(t[0], 2.0) - assert_equal(t[1], 0.5) - - t = FloatLiteral.__divmod__(-4.5, 2.0) - assert_equal(t[0], -3.0) - assert_equal(t[1], 1.5) - - t = FloatLiteral.__divmod__(4.5, -2.0) - assert_equal(t[0], -3.0) - assert_equal(t[1], -1.5) - - t = FloatLiteral.__divmod__(6.0, 2.5) - assert_equal(t[0], 2.0) - assert_equal(t[1], 1.0) + alias t0 = FloatLiteral.__divmod__(4.5, 2.0) + alias q0 = t0[0] + alias r0 = t0[1] + assert_equal(q0, 2.0) + assert_equal(r0, 0.5) + + alias t1 = FloatLiteral.__divmod__(-4.5, 2.0) + alias q1 = t1[0] + alias r1 = t1[1] + assert_equal(q1, -3.0) + assert_equal(r1, 1.5) + + alias t2 = FloatLiteral.__divmod__(4.5, -2.0) + alias q2 = t2[0] + alias r2 = t2[1] + assert_equal(q2, -3.0) + assert_equal(r2, -1.5) + + alias t3 = FloatLiteral.__divmod__(6.0, 2.5) + alias q3 = t3[0] + alias r3 = t3[1] + assert_equal(q3, 2.0) + assert_equal(r3, 1.0) def test_int_conversion(): diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 72ffa712fa..84d23a7955 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -97,15 +97,23 @@ def test_indexer(): def test_divmod(): - t = IntLiteral.__divmod__(2, 2) - assert_equal(t[0], 1) - assert_equal(t[1], 0) - t = IntLiteral.__divmod__(2, 3) - assert_equal(t[0], 0) - assert_equal(t[1], 2) - t = IntLiteral.__divmod__(99, -2) - assert_equal(t[0], -50) - assert_equal(t[1], -1) + alias t0 = IntLiteral.__divmod__(2, 2) + alias q0 = t0[0] + alias r0 = t0[1] + assert_equal(q0, 1) + assert_equal(r0, 0) + + alias t1 = IntLiteral.__divmod__(2, 3) + alias q1 = t1[0] + alias r1 = t1[1] + assert_equal(q1, 0) + assert_equal(r1, 2) + + alias t2 = IntLiteral.__divmod__(99, -2) + alias q2 = t2[0] + alias r2 = t2[1] + assert_equal(q2, -50) + assert_equal(r2, -1) def test_bool(): From f904a9241cbae2fc2cfc013d93acb1abe06eee1d Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 3 Sep 2024 21:24:35 -0500 Subject: [PATCH 1498/2019] [stdlib] feature: Add C_int, C_long, and C_long_long type aliases to ffi.mojo MODULAR_ORIG_COMMIT_REV_ID: 9168c8533db59c9b774bec762512751de17e6071 --- stdlib/src/sys/ffi.mojo | 49 ++++++++++++++++++++++++++- stdlib/test/sys/test_c_types.mojo | 55 +++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/sys/test_c_types.mojo diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 689bc42d44..699e818ceb 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -17,13 +17,60 @@ from memory import UnsafePointer from utils import StringRef -from .info import os_is_linux, os_is_windows +from .info import os_is_linux, os_is_windows, is_64bit, os_is_macos from .intrinsics import _mlirtype_is_eq from builtin.builtin_list import _LITRefPackHelper alias C_char = Int8 """C `char` type.""" +alias C_int = Int32 +"""C `int` type. + +The C `int` type is typically a signed 32-bit integer on commonly used targets +today. +""" + +alias C_long = Scalar[_c_long_dtype()] +"""C `long` type. + +The C `long` type is typically a signed 64-bit integer on macOS and Linux, and a +32-bit integer on Windows.""" + +alias C_long_long = Scalar[_c_long_long_dtype()] +"""C `long long` type. + +The C `long long` type is typically a signed 64-bit integer on commonly used +targets today.""" + + +fn _c_long_dtype() -> DType: + # https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models + + @parameter + if is_64bit() and (os_is_macos() or os_is_linux()): + # LP64 + return DType.int64 + elif is_64bit() and os_is_windows(): + # LLP64 + return DType.int32 + else: + constrained[False, "size of C `long` is unknown on this target"]() + return abort[DType]() + + +fn _c_long_long_dtype() -> DType: + # https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models + + @parameter + if is_64bit() and (os_is_macos() or os_is_linux() or os_is_windows()): + # On a 64-bit CPU, `long long` is *always* 64 bits in every OS's data + # model. + return DType.int64 + else: + constrained[False, "size of C `long long` is unknown on this target"]() + return abort[DType]() + struct RTLD: """Enumeration of the RTLD flags used during dynamic library loading.""" diff --git a/stdlib/test/sys/test_c_types.mojo b/stdlib/test/sys/test_c_types.mojo new file mode 100644 index 0000000000..a8641d9b50 --- /dev/null +++ b/stdlib/test/sys/test_c_types.mojo @@ -0,0 +1,55 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from sys.info import os_is_linux, os_is_macos, os_is_windows, is_64bit, is_32bit +from sys.ffi import C_int, C_long, C_long_long + +from testing import assert_equal, assert_true + +# +# Reference: +# https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models +# + + +def test_c_int_type(): + if is_64bit() and (os_is_macos() or os_is_linux() or os_is_windows()): + # `int` is always 32 bits on the modern 64-bit OSes. + assert_equal(C_int.type, DType.int32) + else: + assert_true(False, "platform C_int size is untested") + + +def test_c_long_type(): + if is_64bit() and (os_is_macos() or os_is_linux()): + # `long` is 64 bits on macOS and Linux. + assert_equal(C_long.type, DType.int64) + elif is_64bit() and os_is_windows(): + # `long` is 32 bits only on Windows. + assert_equal(C_long.type, DType.int32) + else: + assert_true(False, "platform C_long size is untested") + + +def test_c_long_long_type(): + if is_64bit() and (os_is_macos() or os_is_linux() or os_is_windows()): + assert_equal(C_long_long.type, DType.int64) + else: + assert_true(False, "platform C_long_long size is untested") + + +def main(): + test_c_int_type() + test_c_long_type() + test_c_long_long_type() From 2bc170bff8e3c53ee7432aeb8adef3c2bafd5a06 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:47:31 -0700 Subject: [PATCH 1499/2019] [External] [stdlib] Add `Floatable` trait (#45931) [External] [stdlib] Add `Floatable` trait Begins #3157 Add `Floatable` and `FloatableRaising` traits to allow casting compliant types to `Float64`. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3163 MODULAR_ORIG_COMMIT_REV_ID: d352e45f2287d2e47ab79374f8b68f2f2fc0e2e9 --- docs/changelog.md | 13 ++ stdlib/src/builtin/bool.mojo | 10 ++ stdlib/src/builtin/float_literal.mojo | 10 ++ stdlib/src/builtin/floatable.mojo | 151 +++++++++++++++++++ stdlib/src/builtin/simd.mojo | 16 ++ stdlib/src/builtin/string_literal.mojo | 11 ++ stdlib/src/collections/string.mojo | 11 ++ stdlib/src/prelude/__init__.mojo | 1 + stdlib/test/builtin/test_bool.mojo | 6 + stdlib/test/builtin/test_float_literal.mojo | 6 + stdlib/test/builtin/test_int.mojo | 5 + stdlib/test/builtin/test_simd.mojo | 7 + stdlib/test/builtin/test_string_literal.mojo | 8 + stdlib/test/collections/test_string.mojo | 10 ++ 14 files changed, 265 insertions(+) create mode 100644 stdlib/src/builtin/floatable.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 0c2849cc44..68adeaeca3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -25,6 +25,19 @@ what we publish. `Debug Mojo File`. A corresponding button appears in `Run and Debug` selector in the top right corner of a Mojo File. +- Add the `Floatable` and `FloatableRaising` traits to denote types that can + be converted to a `Float64` value using the builtin `float` function. + - Make `SIMD` and `FloatLiteral` conform to the `Floatable` trait. + + ```mojo + fn foo[F: Floatable](v: F): + ... + + var f = float(Int32(45)) + ``` + + ([PR #3163](https://github.com/modularml/mojo/pull/3163) by [@bgreni](https://github.com/bgreni)) + ### 🦋 Changed - A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index b0241ed634..2506270a3c 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -108,6 +108,7 @@ struct Bool( Representable, Stringable, Formattable, + Floatable, ): """The primitive Bool scalar value used in Mojo.""" @@ -248,6 +249,15 @@ struct Bool( """ return _select_register_value(self.value, Int(1), Int(0)) + @always_inline("nodebug") + fn __float__(self) -> Float64: + """Convert this Bool to a float. + + Returns: + 1.0 if True else 0.0 otherwise. + """ + return _select_register_value(self.value, Float64(1.0), Float64(0.0)) + @always_inline("nodebug") fn __index__(self) -> Int: """Convert this Bool to an integer for indexing purposes. diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index d0a90d0807..b8ce2b6b29 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -36,6 +36,7 @@ struct FloatLiteral( Roundable, Stringable, Truncable, + Floatable, ): """Mojo floating point literal type.""" @@ -148,6 +149,15 @@ struct FloatLiteral( """ return self.__int_literal__().__int__() + @always_inline("nodebug") + fn __float__(self) -> Float64: + """Converts the FloatLiteral to a concrete Float64. + + Returns: + The Float value. + """ + return Float64(self) + # ===------------------------------------------------------------------===# # Unary Operators # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/floatable.mojo b/stdlib/src/builtin/floatable.mojo new file mode 100644 index 0000000000..340265d291 --- /dev/null +++ b/stdlib/src/builtin/floatable.mojo @@ -0,0 +1,151 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +"""Implements the `Floatable` and `FloatableRaising` traits. + +These are Mojo built-ins, so you don't need to import them. +""" + + +trait Floatable: + """The `Floatable` trait describes a type that can be converted to a Float. + + Any type that conforms to `Floatable` works with the built-in `float` + function. + + This trait requires the type to implement the `__float__()` method. + + For example: + + ```mojo + @value + struct Foo(Floatable): + var i: Float64 + + fn __float__(self) -> Float64: + return self.i + ``` + + A `Foo` can now be converted to a `Float64` using `float`: + + ```mojo + var f = float(Foo(5.5)) + ``` + + **Note:** If the `__float__()` method can raise an error, use + the [`FloatableRaising`](/mojo/stdlib/builtin/floatable/floatableraising) + trait instead. + """ + + fn __float__(self) -> Float64: + """Get the float point representation of the value. + + Returns: + The float point representation of the value. + """ + ... + + +trait FloatableRaising: + """The `FloatableRaising` trait describes a type that can be converted to a + Float, but the conversion might raise an error (e.g.: a string). + + Any type that conforms to `FloatableRaising` works with the built-in `float` + function. + + This trait requires the type to implement the `__float__()` method, which + can raise an error. + + For example: + + ```mojo + @value + struct MaybeFloat(FloatableRasing): + var value: Variant[Float64, NoneType] + + fn __float__(self) raises -> Float64: + if self.value.isa[NoneType](): + raise "Float expected" + return self.value[Float64] + ``` + + A `MaybeFloat` can now be converted to `Float64` using `float`: + + ```mojo + try: + print(float(MaybeFloat(4.6))) + except: + print("error occured") + ``` + """ + + fn __float__(self) raises -> Float64: + """Get the float point representation of the value. + + Returns: + The float point representation of the value. + + Raises: + If the type does not have a float point representation. + """ + ... + + +@always_inline +fn float[T: Floatable](value: T, /) -> Float64: + """Get the Float representation of the value. + + Parameters: + T: The Floatable type. + + Args: + value: The object to get the float point representation of. + + Returns: + The float point representation of the value. + """ + return value.__float__() + + +@always_inline +fn float[T: FloatableRaising](value: T, /) raises -> Float64: + """Get the Float representation of the value. + + Parameters: + T: The Floatable type. + + Args: + value: The object to get the float point representation of. + + Returns: + The float point representation of the value. + + Raises: + If the type does not have a float point representation. + """ + return value.__float__() + + +# TODO: Int can't conform to Floatable at the moment due to circular +# dependency with SIMD. +@always_inline +fn float(value: Int, /) -> Float64: + """Get the Float representation of the Int. + + Args: + value: The Int to get the float point representation of. + + Returns: + The float point representation of the Int. + """ + return Float64(value) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ea2087d030..514ee23f5c 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -166,6 +166,7 @@ struct SIMD[type: DType, size: Int]( CeilDivable, CollectionElement, CollectionElementNew, + Floatable, Floorable, Formattable, Hashable, @@ -1345,6 +1346,21 @@ struct SIMD[type: DType, size: Int]( _type = __mlir_type.`!pop.scalar` ](rebind[Scalar[type]](self).value) + @always_inline("nodebug") + fn __float__(self) -> Float64: + """Casts the value to a float. + + Constraints: + The size of the SIMD vector must be 1. + + Returns: + The value as a float. + """ + constrained[size == 1, "expected a scalar type"]() + return __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( + rebind[Scalar[type]](self).value + ) + @no_inline fn __str__(self) -> String: """Get the SIMD as a string. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 048bbd4ac1..34c8b49245 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -42,6 +42,7 @@ struct StringLiteral( Representable, Sized, Stringable, + FloatableRaising, ): """This type represents a string literal. @@ -214,6 +215,16 @@ struct StringLiteral( """ return _atol(self) + fn __float__(self) raises -> Float64: + """Parses the string as a float point number and returns that value. + + If the string cannot be parsed as a float, an error is raised. + + Returns: + A float value that represents the string, or otherwise raises. + """ + return atof(self) + @no_inline fn __str__(self) -> String: """Convert the string literal to a string. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index eb6cf794fd..121f98dc68 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -687,6 +687,7 @@ struct String( Formattable, ToFormatter, CollectionElementNew, + FloatableRaising, ): """Represents a mutable string.""" @@ -1942,6 +1943,16 @@ struct String( """ return atol(self) + fn __float__(self) raises -> Float64: + """Parses the string as a float point number and returns that value. + + If the string cannot be parsed as a float, an error is raised. + + Returns: + A float value that represents the string, or otherwise raises. + """ + return atof(self) + fn __mul__(self, n: Int) -> String: """Concatenates the string `n` times. diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 11c151874b..fec77db82d 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -34,6 +34,7 @@ from builtin.error import Error from builtin.file import open, FileHandle from builtin.file_descriptor import FileDescriptor from builtin.float_literal import FloatLiteral +from builtin.floatable import Floatable, FloatableRaising, float from builtin.format_int import bin, hex, oct from builtin.hash import hash, Hashable from builtin.identifiable import Identifiable, StringableIdentifiable diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 28400f5d90..bb3079a48e 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -149,6 +149,11 @@ def test_comparisons(): assert_true(True <= True) +def test_float_conversion(): + assert_equal((True).__float__(), 1.0) + assert_equal((False).__float__(), 0.0) + + def main(): test_default() test_bool_cast_to_int() @@ -160,3 +165,4 @@ def main(): test_neg() test_indexer() test_comparisons() + test_float_conversion() diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index d9a246e53a..6746b140b6 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -286,6 +286,11 @@ def test_comparison(): assert_false(FloatLiteral.__ge__(nan, neg_zero)) +def test_float_conversion(): + assert_equal((4.5).__float__(), 4.5) + assert_equal((0.0).__float__(), 0.0) + + def main(): test_ceil() test_floor() @@ -300,3 +305,4 @@ def main(): test_is_special_value() test_abs() test_comparison() + test_float_conversion() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index a7de184003..ccb1f87da8 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -227,6 +227,10 @@ def test_comparison(): assert_false(Int(5).__ge__(Int(10))) +def test_float_conversion(): + assert_equal(float(Int(45)), Float64(45)) + + def main(): test_properties() test_add() @@ -248,3 +252,4 @@ def main(): test_decimal_digit_count() test_comparison() test_int_uint() + test_float_conversion() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 5b7347d2a5..559ae6632b 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1662,6 +1662,12 @@ def test_comparison(): test_dtype[DType.bfloat16]() +def test_float_conversion(): + assert_almost_equal(float(Int32(45)), 45.0) + assert_almost_equal(float(Float32(34.32)), 34.32) + assert_almost_equal(float(UInt64(36)), 36.0) + + def main(): test_abs() test_add() @@ -1709,4 +1715,5 @@ def main(): test_split() test_contains() test_comparison() + test_float_conversion() # TODO: add tests for __and__, __or__, anc comparison operators diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 9f049f5c78..c4960d31f8 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -196,6 +196,13 @@ def test_repr(): assert_equal(StringLiteral.__repr__("\x7f"), r"'\x7f'") +def test_float_conversion(): + assert_equal(("4.5").__float__(), 4.5) + assert_equal(float("4.5"), 4.5) + with assert_raises(): + _ = ("not a float").__float__() + + def main(): test_add() test_equality() @@ -211,3 +218,4 @@ def main(): test_layout() test_lower_upper() test_repr() + test_float_conversion() diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 5aa41c1f6b..24f3fa2045 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1580,6 +1580,15 @@ def test_center(): assert_equal(String("hello").center(8, "*"), "*hello**") +def test_float_conversion(): + # This is basically just a wrapper around atof which is + # more throughouly tested above + assert_equal(String("4.5").__float__(), 4.5) + assert_equal(float(String("4.5")), 4.5) + with assert_raises(): + _ = float(String("not a float")) + + def main(): test_constructors() test_copy() @@ -1633,3 +1642,4 @@ def main(): test_rjust() test_ljust() test_center() + test_float_conversion() From 3d7409638b6a290382c155804dec7ae2514f8801 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 4 Sep 2024 14:11:12 -0500 Subject: [PATCH 1500/2019] [stdlib] feature: Initial work to support calling Mojo from Python MODULAR_ORIG_COMMIT_REV_ID: 394bd0982a7b0689dde1e645f8ed83ee5e87302b --- stdlib/src/python/_cpython.mojo | 338 +++++++++++++++++- stdlib/src/python/python.mojo | 51 ++- stdlib/src/python/python_object.mojo | 12 + .../python/test_python_module_create.mojo | 35 ++ 4 files changed, 433 insertions(+), 3 deletions(-) create mode 100644 stdlib/test/python/test_python_module_create.mojo diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 4fb97b5c39..4577c01b11 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -17,7 +17,7 @@ from os.path import dirname from pathlib import Path from sys import external_call from sys.arg import argv -from sys.ffi import DLHandle, C_char +from sys.ffi import DLHandle, C_char, C_int from memory import UnsafePointer @@ -101,6 +101,305 @@ fn _py_finalize(lib: DLHandle): lib.get_function[fn () -> None]("Py_Finalize")() +# Ref https://docs.python.org/3/c-api/structures.html#c.PyMethodDef +@value +struct PyMethodDef: + # ===-------------------------------------------------------------------===# + # Aliases + # ===-------------------------------------------------------------------===# + + # Ref: https://docs.python.org/3/c-api/structures.html#c.PyCFunction + # TODO(MOCO-1138): This is a C ABI function pointer, not Mojo a function. + alias _PyCFunction_type = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr + + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var method_name: UnsafePointer[C_char] # called ml_name in CPython + + # TODO: Support keyword-argument only methods + # Pointer to the function to call + var method_impl: Self._PyCFunction_type + + # Flags bits indicating how the call should be constructed. + # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef for the various calling conventions + var method_flags: C_int + + # Points to the contents of the docstring for the module. + var method_docstring: UnsafePointer[C_char] + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + + fn __init__(inout self): + """Constructs a zero initialized PyModuleDef. + + This is suitable for use terminating an array of PyMethodDef values. + """ + self.method_name = UnsafePointer[C_char]() + self.method_impl = _null_fn_ptr[Self._PyCFunction_type]() + self.method_flags = 0 + self.method_docstring = UnsafePointer[C_char]() + + +fn _null_fn_ptr[T: AnyTrivialRegType]() -> T: + return __mlir_op.`pop.pointer.bitcast`[_type=T]( + __mlir_attr.`#interp.pointer<0> : !kgen.pointer` + ) + + +struct PyTypeObject: + # TODO(MSTDL-877): + # Fill this out based on + # https://docs.python.org/3/c-api/typeobj.html#pytypeobject-definition + pass + + +@value +struct PyObject(Stringable, Representable, Formattable): + var object_ref_count: Int + # FIXME: should we use `PyObjectPtr`? I don't think so! + var object_type: UnsafePointer[PyTypeObject] + # var object_type: PyObjectPtr + + fn __init__(inout self): + self.object_ref_count = 0 + self.object_type = UnsafePointer[PyTypeObject]() + + @no_inline + fn __str__(self) -> String: + """Get the PyModuleDef_Base as a string. + + Returns: + A string representation. + """ + + return String.format_sequence(self) + + @no_inline + fn __repr__(self) -> String: + """Get the `PyObject` as a string. Returns the same `String` as `__str__`. + + Returns: + A string representation. + """ + return str(self) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn format_to(self, inout writer: Formatter): + """ + Formats to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + writer.write("PyObject(") + writer.write("object_ref_count=", self.object_ref_count, ",") + writer.write("object_type=", self.object_type) + writer.write(")") + + +# Ref: https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L39 +# Ref2: https://pyo3.rs/main/doc/pyo3/ffi/struct.pymoduledef_base +# Mojo doesn't have macros, so we define it here for ease. +# Note: `PyModuleDef_HEAD_INIT` defaults all of its members, see https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L60 +struct PyModuleDef_Base(Stringable, Representable, Formattable): + # The initial segment of every `PyObject` in CPython + var object_base: PyObject + + # The function used to re-initialize the module. + # TODO(MOCO-1138): This is a C ABI function pointer, not Mojo a function. + alias _init_fn_type = fn () -> UnsafePointer[PyObject] + var init_fn: Self._init_fn_type + + # The module's index into its interpreter's modules_by_index cache. + var index: Int + + # A copy of the module's __dict__ after the first time it was loaded. + var dict_copy: UnsafePointer[PyObject] + + # ===------------------------------------------------------------------=== # + # Life cycle methods + # ===------------------------------------------------------------------=== # + + fn __init__(inout self): + self.object_base = PyObject() + self.init_fn = _null_fn_ptr[Self._init_fn_type]() + self.index = 0 + self.dict_copy = UnsafePointer[PyObject]() + + fn __moveinit__(inout self, owned existing: Self): + self.object_base = existing.object_base + self.init_fn = existing.init_fn + self.index = existing.index + self.dict_copy = existing.dict_copy + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + @no_inline + fn __str__(self) -> String: + """Get the PyModuleDef_Base as a string. + + Returns: + A string representation. + """ + + return String.format_sequence(self) + + @no_inline + fn __repr__(self) -> String: + """Get the PyMdouleDef_Base as a string. Returns the same `String` as `__str__`. + + Returns: + A string representation. + """ + return str(self) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn format_to(self, inout writer: Formatter): + """ + Formats to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + writer.write("PyModuleDef_Base(") + writer.write("object_base=", self.object_base, ",") + writer.write("init_fn=", ",") + writer.write("index=", self.index, ",") + writer.write("dict_copy=", self.dict_copy) + writer.write(")") + + +# Ref: https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot +@value +struct PyModuleDef_Slot: + var slot: Int + var value: UnsafePointer[NoneType] + + +# Ref: https://docs.python.org/3/c-api/module.html#c.PyModuleDef +struct PyModuleDef(Stringable, Representable, Formattable): + # The Python module definition structs that holds all of the information needed + # to create a module. Typically, there is a 1:1 correspondence beteeen a `PyMethodDef` + # and a module. + var base: PyModuleDef_Base + + # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef + var name: UnsafePointer[C_char] + + # Points to the contents of the docstring for the module. + var docstring: UnsafePointer[C_char] + + var size: Int + + # A pointer to a table of module-level functions. Can be null if there + # are no functions present. + var methods: UnsafePointer[PyMethodDef] + + var slots: UnsafePointer[PyModuleDef_Slot] + + # TODO(MOCO-1138): These are C ABI function pointers, not Mojo functions. + alias _visitproc_fn_type = fn (PyObjectPtr, UnsafePointer[NoneType]) -> Int + alias _traverse_fn_type = fn ( + PyObjectPtr, Self._visitproc_fn_type, UnsafePointer[NoneType] + ) -> Int + var traverse_fn: Self._traverse_fn_type + + alias _clear_fn_type = fn (PyObjectPtr) -> Int + var clear_fn: Self._clear_fn_type + + alias _free_fn_type = fn (UnsafePointer[NoneType]) -> UnsafePointer[ + NoneType + ] + var free_fn: Self._free_fn_type + + fn __init__(inout self, name: String): + self.base = PyModuleDef_Base() + self.name = name.unsafe_cstr_ptr() + # self.docstring = UnsafePointer[C_char]() + self.docstring = UnsafePointer[C_char]() + # means that the module does not support sub-interpreters + self.size = -1 + self.methods = UnsafePointer[PyMethodDef]() + self.slots = UnsafePointer[PyModuleDef_Slot]() + + self.slots = UnsafePointer[PyModuleDef_Slot]() + self.traverse_fn = _null_fn_ptr[Self._traverse_fn_type]() + self.clear_fn = _null_fn_ptr[Self._clear_fn_type]() + self.free_fn = _null_fn_ptr[Self._free_fn_type]() + + fn __moveinit__(inout self, owned existing: Self): + self.base = existing.base^ + self.name = existing.name + self.docstring = existing.docstring + self.size = existing.size + self.methods = existing.methods + self.slots = existing.slots + self.traverse_fn = existing.traverse_fn + self.clear_fn = existing.clear_fn + self.free_fn = existing.free_fn + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + @no_inline + fn __str__(self) -> String: + """Get the PyModuleDefe as a string. + + Returns: + A string representation. + """ + + return String.format_sequence(self) + + @no_inline + fn __repr__(self) -> String: + """Get the PyMdouleDef as a string. Returns the same `String` as `__str__`. + + Returns: + A string representation. + """ + return str(self) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn format_to(self, inout writer: Formatter): + """ + Formats to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + writer.write("PyModuleDef(") + writer.write("base=", self.base, ",") + writer.write("name=", self.name, ",") + writer.write("docstring=", self.docstring, ",") + writer.write("size=", self.size, ",") + writer.write("methods=", self.methods, ",") + writer.write("slots=", self.slots, ",") + writer.write("traverse_fn=", ",") + writer.write("clear_fn=", ",") + writer.write("free_fn=") + writer.write(")") + + struct CPython: var lib: DLHandle var none_value: PyObjectPtr @@ -323,6 +622,43 @@ struct CPython: self._inc_total_rc() return r + fn PyModule_Create( + inout self, + name: String, + ) -> PyObjectPtr: + # TODO: See https://docs.python.org/3/c-api/module.html#c.PyModule_Create + # and https://github.com/pybind/pybind11/blob/a1d00916b26b187e583f3bce39cd59c3b0652c32/include/pybind11/pybind11.h#L1326 + # for what we want to do essentially here. + var module_def_ptr = UnsafePointer[PyModuleDef].alloc(1) + var module_def = PyModuleDef(name) + module_def_ptr.init_pointee_move(module_def^) + + var create_module_fn = self.lib.get_function[ + fn (UnsafePointer[PyModuleDef], Int) -> PyObjectPtr + ]("PyModule_Create2") + + # TODO: set gil stuff + # Note: Python automatically calls https://docs.python.org/3/c-api/module.html#c.PyState_AddModule + # after the caller imports said module. + + # TODO: it would be nice to programatically call a CPython API to get the value here + # but I think it's only defined via the `PYTHON_API_VERSION` macro that ships with Python. + # if this mismatches with the user's Python, then a `RuntimeWarning` is emitted according to the + # docs. + var module_api_version = 1013 + return create_module_fn(module_def_ptr, module_api_version) + + fn PyModule_AddFunctions( + inout self, + mod: PyObjectPtr, + functions: UnsafePointer[PyMethodDef], + ) -> Int: + var add_functions_fn = self.lib.get_function[ + fn (PyObjectPtr, UnsafePointer[PyMethodDef]) -> Int + ]("PyModule_AddFunctions") + + return add_functions_fn(mod, functions) + fn PyRun_SimpleString(inout self, strref: StringRef) -> Bool: """Executes the given Python code. diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 2b50db268d..851d40cc1d 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -20,7 +20,7 @@ from python import Python """ from collections import Dict -from os.env import getenv +from os import abort, getenv from sys import external_call, sizeof from sys.ffi import _get_global @@ -28,7 +28,7 @@ from memory import UnsafePointer from utils import StringRef -from ._cpython import CPython, Py_eval_input, Py_file_input +from ._cpython import CPython, Py_eval_input, Py_file_input, PyMethodDef from .python_object import PythonObject @@ -207,6 +207,53 @@ struct Python: Python.throw_python_exception_if_error_state(cpython) return PythonObject(module_maybe) + @staticmethod + fn create_module(name: String) raises -> PythonObject: + """Creates a Python module using the provided name. + + Inspired by https://github.com/pybind/pybind11/blob/a1d00916b26b187e583f3bce39cd59c3b0652c32/include/pybind11/pybind11.h#L1227 + + TODO: allow specifying a doc-string to attach to the module upon creation or lazily added? + + Args: + name: The Python module name. + + Returns: + The Python module. + """ + # Initialize the global instance to the Python interpreter + # in case this is our first time. + + var cpython = _get_global_python_itf().cpython() + + # This will throw an error if there are any errors during initialization. + cpython.check_init_error() + + var module = cpython.PyModule_Create(name) + + # TODO: investigate when `PyModule_Create` can actually produce an error + # This is cargo copy-pasted from other methods in this file essentially. + Python.throw_python_exception_if_error_state(cpython) + + return PythonObject(module) + + @staticmethod + fn add_methods( + module: PythonObject, functions: UnsafePointer[PyMethodDef] + ) -> Int: + """Adds methods to a PyModule object. + + Args: + module: The PyModule object. + functions: A null terminated pointer to function data. + + Returns: + Status code indicating success or failure. + """ + var cpython = _get_global_python_itf().cpython() + + return cpython.PyModule_AddFunctions(module.py_object, functions) + @staticmethod fn dict() -> PythonObject: """Construct an empty Python dictionary. diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 98b70c3da7..ca5dec2a2c 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -359,6 +359,18 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return _PyIter(PythonObject(iter)) + fn steal_data(owned self) -> PyObjectPtr: + """Take ownership of the underlying pointer from the Python object. + + Returns: + The underlying data. + """ + var ptr = self.py_object + + self.py_object = PyObjectPtr() + + return ptr + fn __del__(owned self): """Destroy the object. diff --git a/stdlib/test/python/test_python_module_create.mojo b/stdlib/test/python/test_python_module_create.mojo new file mode 100644 index 0000000000..5c52deec3e --- /dev/null +++ b/stdlib/test/python/test_python_module_create.mojo @@ -0,0 +1,35 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# TODO(MSTDL-875): Fix and un-XFAIL this +# XFAIL: asan && !system-darwin +# RUN: %mojo %s + +from python import Python, PythonObject +from testing import assert_equal +from pathlib import _dir_of_current_file + + +def test_create_module(): + var module_name = "test_module" + var module = Python.create_module(module_name) + + # TODO: inspect properties about the module + # First though, let's see if we can even import it + # Python.add_to_path(str(_dir_of_current_file())) + # var imported_module = Python.import_module(module_name) + # + # _ = module_name + + +def main(): + test_create_module() From f94cdfbe6236e7927b2612ff0ccb1c4b43be4839 Mon Sep 17 00:00:00 2001 From: Patrick Rachford Date: Wed, 4 Sep 2024 13:56:36 -0700 Subject: [PATCH 1501/2019] [stdlib] Fix Mojo numpy module example ```diff - fn use_numpy() raises: + def main(): ``` MODULAR_ORIG_COMMIT_REV_ID: 02667b3c1a8c6692b9e91c8b71582d879459a770 --- docs/manual/basics.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index 511b3ad849..d35752c300 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -658,7 +658,7 @@ "source": [ "from python import Python\n", "\n", - "fn use_numpy() raises:\n", + "def main():\n", " var np = Python.import_module(\"numpy\")\n", " var ar = np.arange(15).reshape(3, 5)\n", " print(ar)\n", From e62e139123b3d9b96b9792c4177ac25d05022464 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:38:23 -0700 Subject: [PATCH 1502/2019] [External] [stdlib] Fix typo in inlined_assembly 9-arg function arguments (#46574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Fix typo in inlined_assembly 9-arg function arguments I discovered this copy-paste typo, arg3 repeated twice for 9 arg assembly generation, while poking around the stdlib. Unfortunately I don't know how to test this. Tests passed both before and after this change, so I assume this code path is not tested on my setup ORIGINAL_AUTHOR=Thomas Børstad <4872288+tboerstad@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3449 Co-authored-by: Thomas Børstad <4872288+tboerstad@users.noreply.github.com> Closes modularml/mojo#3449 MODULAR_ORIG_COMMIT_REV_ID: 735aedcddec7cdd1311c7da2ff1bb4784329c9a1 --- stdlib/src/sys/_assembly.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/sys/_assembly.mojo b/stdlib/src/sys/_assembly.mojo index 1d3487dcd1..6dae67c37c 100644 --- a/stdlib/src/sys/_assembly.mojo +++ b/stdlib/src/sys/_assembly.mojo @@ -612,7 +612,7 @@ fn inlined_assembly[ assembly = asm.value, constraints = constraints.value, hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg3, arg5, arg6, arg7, arg8) + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) else: return __mlir_op.`pop.inline_asm`[ _type=result_type, From 9924e1caed286194a6f126046021b294c3a64848 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 4 Sep 2024 15:17:25 -0700 Subject: [PATCH 1503/2019] Update READMEs to use Magic/conda MODULAR_ORIG_COMMIT_REV_ID: 1191173b2f26f2eb3bd837759de5defe55566c86 --- CONTRIBUTING.md | 32 +++++++++++------------ README.md | 41 +++++++++++++++++++---------- examples/README.md | 29 +++++++++++---------- examples/notebooks/README.md | 50 +++++++++++++++++++++--------------- examples/notebooks/pixi.toml | 13 ++++++++++ 5 files changed, 100 insertions(+), 65 deletions(-) create mode 100644 examples/notebooks/pixi.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b59db72aee..c0d372d70d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -261,30 +261,30 @@ git rebase upstream/nightly #### Getting the nightly Mojo compiler Now that you're on the nightly branch, you need to install the latest nightly -Mojo compiler: +build. -```bash -curl https://get.modular.com | sh - - -modular auth +If you're using [`magic`](https://docs.modular.com/magic), create a new +project environment with the `max-nightly` channel like this: -modular install nightly/mojo +```bash +magic init mojo-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly ``` -If you already have an older `nightly/mojo` compiler, replace -`modular install nightly/mojo` with `modular update nightly/mojo`. +If you're [using conda](https://docs.modular.com/magic/conda), add the +`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +file. For example: -Then, follow the instructions from the `modular` tool in adding the `mojo` -compiler to your `PATH` such as: +```yaml +[project] +name = "Mojo nightly example" +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] -```bash -echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc -echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc +[dependencies] +max = "*" ``` -If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. - #### Mojo nightly vscode extension Install the [Mojo nightly VS Code diff --git a/README.md b/README.md index d4058593c0..a3175c6088 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,8 @@ To learn more about Mojo, see the ### Latest Released -To install the last released build of Mojo, you can install the MAX SDK -or the standalone Mojo SDK: - -- [Get the MAX SDK](https://docs.modular.com/engine/get-started) -- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) - -Then follow the docs to [write your first Mojo -program](https://docs.modular.com/mojo/manual/get-started#2-run-code-in-the-repl). +To install the last released build of Mojo, follow the guide to +[Get started with Mojo](https://docs.modular.com/mojo/manual/get-started). ### Latest Nightly @@ -56,13 +50,32 @@ The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk and be patient! -To get nightly builds, see the same instructions to [install the Mojo -SDK](https://docs.modular.com/mojo/manual/get-started/#1-install-mojo), but use -the command shown there to install `nightly/mojo`. +To get nightly builds, see the same instructions to [Get started with +Mojo](https://docs.modular.com/mojo/manual/get-started), but when you create +your project, instead use the following `magic init` command to set the +conda package channel to `max-nightly`: + +```bash +magic init hello-world-nightly --format mojoproject \ + -c conda-forge -c https://conda.modular.com/max-nightly +``` + +Or, if you're [using conda](https://docs.modular.com/magic/conda), add the +`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +file. For example: + +```yaml +[project] +name = "Mojo nightly example" +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] + +[dependencies] +max = "*" +``` -When you clone this repo, be sure you switch to the `nightly` branch, because -the `main` branch is for stable releases and might not be compatible with -nightly builds: +And when you clone this repo, switch to the `nightly` branch because the `main` +branch might not be compatible with nightly builds: ```bash git clone https://github.com/modularml/mojo.git diff --git a/examples/README.md b/examples/README.md index f5d82b7152..598ad13844 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,11 +1,11 @@ # Mojo code examples -A collection of sample programs and Mojo notebooks written in the +A collection of sample programs and Mojo notebooks written in the [Mojo](https://docs.modular.com/mojo/programming-manual.html) programming language. ## Getting Started -Access a Mojo programming environment available from the +Access a Mojo programming environment available from the Mojo product [page](https://www.modular.com/mojo). Git clone the repository of Mojo samples using the command below: @@ -16,23 +16,24 @@ git clone https://github.com/modularml/mojo.git ## Running -Use the following sample command-line to run the programs: +If you're using [`magic`](https://docs.modular.com/magic), navigate into +the examples directory and use `magic run`. For example: ```bash -mojo matmul.mojo +magic run mojo matmul.mojo ``` -You can run the Mojo notebooks using [JupyterLab or Visual Studio +You can run the Mojo notebooks using [JupyterLab or Visual Studio Code](notebooks/README.md) with the Mojo extension available on the Marketplace. ### Mojo SDK Container -The repo also contains a Dockerfile that can be used to create a -Mojo SDK container for developing and running Mojo programs. Use the -container in conjunction with the Visual Studio Code devcontainers +The repo also contains a Dockerfile that can be used to create a +Mojo SDK container for developing and running Mojo programs. Use the +container in conjunction with the Visual Studio Code devcontainers extension to develop directly inside the container. -The Dockerfile also sets up a `conda` environment and by default, +The Dockerfile also sets up a `conda` environment and by default, starts a `jupyter` server (which you can access via the browser). To build a Mojo container, either use @@ -55,11 +56,11 @@ The script also supports building with `podman` instead of `docker`: ./build-image.sh --auth-key \ --use-podman \ --mojo-version 0.3 - + ``` -You can then run with either `docker` or `podman`. In the example below, -we map the ports, bind mount the current directory and open a shell into +You can then run with either `docker` or `podman`. In the example below, +we map the ports, bind mount the current directory and open a shell into the container: ```bash @@ -85,8 +86,8 @@ podman run \ ## License -The Mojo examples and notebooks in this repository are licensed -under the Apache License v2.0 with LLVM Exceptions +The Mojo examples and notebooks in this repository are licensed +under the Apache License v2.0 with LLVM Exceptions (see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Contributing diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 7c6eecf342..4ee3854595 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -29,9 +29,10 @@ notebooks. Especially if you're developing with Mojo on a remote system, using VS Code is ideal because it allows you to edit and interact with notebooks on the remote machine where you've installed Mojo. -All you need is the Mojo SDK and the Jupyter VS Code extension: +All you need is Mojo and the Jupyter VS Code extension: -1. Install the [Mojo SDK](https://developer.modular.com/download). +1. [Create a new Mojo +project](https://docs.modular.com/mojo/manual/get-started#1-create-a-new-project). 2. Install [Visual Studio Code](https://code.visualstudio.com/) and the [Jupyter @@ -56,33 +57,40 @@ instructions don't support remote access to the JupyterLab). For more details about using JupyterLab, see the complete [JupyterLab installation guide](https://jupyterlab.readthedocs.io/en/latest/getting_started/installation.html). -**Note:** You must run this setup on the same machine where you've installed -the [Mojo SDK](https://developer.modular.com/download). However, syntax -highlighting for Mojo code is not currently enabled in JupyterLab (coming soon). +### 1. Launch JupyterLab -1. Install JupyterLab: +You can use either Magic or conda. - ```sh - python3 -m pip install jupyterlab - ``` +#### Using Magic -2. Make sure the user-level `bin` is in your `$PATH`: +If you have [`magic`](https://docs.modular.com/magic) you can run the following +command to launch JupyterLab from this directory: - ```sh - export PATH="$HOME/.local/bin:$PATH" - ``` +```sh +magic run jupyter lab +``` -3. Launch JupyterLab: +After a moment, it will open a browser window with JupterLab running. - ```sh - jupyter lab - ``` +#### Using conda -4. When you open any of the `.ipynb` notebooks from this repository, JupyterLab - should automatically select the Mojo kernel (which was installed with the - Mojo SDK). +Create a Conda environment, activate that enviroment, and install JupyterLab. - Now run some Mojo code! +``` sh +# Create a Conda environment if you don't have one +conda create -n mojo-repo +# Activate the environment +conda env update -n mojo-repo -f environment.yml --prune +# run JupyterLab +conda run -n mojo-repo jupyter lab +``` + +After a moment, it will open a browser window with JupterLab running. + +### 2. Run the .ipynb notebooks + +The left nav bar should show all the notebooks in this directory. +Open any `.ipynb` file and start running the code. ## Notes and tips diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml new file mode 100644 index 0000000000..5b2ca73adb --- /dev/null +++ b/examples/notebooks/pixi.toml @@ -0,0 +1,13 @@ +[project] +name = "Mojo notebooks" +version = "1.0.0" +description = "Environment for running JupyterLab" +authors = ["Modular "] +channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] +platforms = ["osx-arm64", "linux-aarch64", "linux-64"] + +[dependencies] +python = ">=3.8,<3.12" +max = "*" +pip = ">=24.0,<25" +jupyterlab = ">=4.2.5,<5" From 16d122ead4a3dc077df12d9512ef5c4b485c885d Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 4 Sep 2024 15:54:24 -0700 Subject: [PATCH 1504/2019] Update Python version for v24.5 MODULAR_ORIG_COMMIT_REV_ID: 65c99f86b9b658e04648c5ec476d563672de3ecc --- examples/notebooks/pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml index 5b2ca73adb..668fdf4caa 100644 --- a/examples/notebooks/pixi.toml +++ b/examples/notebooks/pixi.toml @@ -7,7 +7,7 @@ channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] -python = ">=3.8,<3.12" +python = ">=3.9,<3.13" max = "*" pip = ">=24.0,<25" jupyterlab = ">=4.2.5,<5" From 638ea1280b54d590370b679e8eadaec8023f83a5 Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Wed, 4 Sep 2024 16:59:02 -0700 Subject: [PATCH 1505/2019] [stdlib] Fix intrinsics gather/scatter variable lifetime In the gather/scatter functions, the `base` arg is marked dead after getting its address, but before using its address in the llvm intrinsic. This PR extends their lifetime past the llvm intrinsic. MODULAR_ORIG_COMMIT_REV_ID: f9ece89540dfe98648f9d4304fae3b50feb648a9 --- stdlib/src/sys/intrinsics.mojo | 5 ++++- stdlib/test/memory/test_memory.mojo | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 19a46701a2..08886ccc77 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -887,7 +887,7 @@ fn gather[ return _unsafe_aliasing_address_to_pointer[type]( base[0] ).load() if mask else passthrough[0] - return llvm_intrinsic[ + var result = llvm_intrinsic[ "llvm.masked.gather", __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], ]( @@ -898,6 +898,8 @@ fn gather[ mask, passthrough, ) + _ = base + return result # ===----------------------------------------------------------------------===# @@ -975,6 +977,7 @@ fn scatter[ Int32(alignment), mask, ) + _ = base # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 08466b18ef..9deb983caa 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %mojo --debug-level full %s from sys import sizeof, simdwidthof From f9147d5f74b180520e15966b3c93d15c466c12d6 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:38:14 -0700 Subject: [PATCH 1506/2019] Edited Mojo changelog for the v24.5 release and added API reference links MODULAR_ORIG_COMMIT_REV_ID: a2e21a1a944997b4361c2a9e6f594229c5864b75 --- docs/changelog-released.md | 1183 ++++++++++++++++++++---------------- 1 file changed, 656 insertions(+), 527 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 61c33ad0ed..21b233e628 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -16,13 +16,11 @@ guide](/mojo/manual/get-started). :::caution [Magic](/magic) is the preferred package manager and virtual environment manager -for MAX and Mojo projects. [conda](https://docs.conda.io/projects/conda/en/latest/index.html) -is supported as an alternative. +for MAX and Mojo projects. [conda](/magic/conda) is supported as an alternative. -The legacy [`modular`](/cli) CLI is now deprecated. -We will not release any new `max` or `mojo` packages through the `modular` -tool beyond the 24.5 release. You must now use [Magic](/magic/) or -[conda](/magic/conda) to install MAX and Mojo. +The legacy [`modular`](/cli) CLI is now deprecated. We will not release any new +`max` or `mojo` packages through the `modular` tool beyond the 24.5 release. You +must now use [Magic](/magic) or [conda](/magic/conda) to install MAX and Mojo. ::: @@ -30,14 +28,6 @@ tool beyond the 24.5 release. You must now use [Magic](/magic/) or The virtual environment for each Magic project has its own package versions. The Mojo programming language is distributed as part of the `max` package. - -To view the version of Mojo for a specific Magic project, run the following -command within your project path: - -```sh -magic run mojo --version -``` - Use the [`magic update`](/magic/commands#magic-update) within your project path to update the `max` package for that project to the latest release: @@ -52,15 +42,6 @@ virtual environments. Each conda virtual environment has its own package versions. The Mojo programming language is distributed as part of the `max` package. - -Use the [`conda list`](https://docs.conda.io/projects/conda/en/latest/commands/list.html) -command to list the version of the `max` package for an environment. -For example, for an environment named `max-project`, run: - -```sh -conda list -n max-project max -``` - Use the [`conda update`](https://docs.conda.io/projects/conda/en/latest/commands/update.html) command to update the version of the `max` package for an environment. For example, for an environment named `max-project`, run: @@ -74,39 +55,78 @@ documentation for more information on managing conda virtual environments. ### Update Mojo using the `modular` CLI -:::caution +If you are still using the deprecated `modular` CLI, you can update Mojo to +24.5 by running: + +```sh +modular update mojo +``` -The legacy [`modular`](/cli) CLI is now deprecated. We will not release any new `max` or `mojo` packages through the `modular` tool beyond the 24.5 release. You must now use [Magic](/magic/) or [conda](/magic/conda) to install MAX and Mojo. -::: +## v24.5 (2024-09-10) -To see your Mojo version, run this: +### ✨ Highlights -```sh -mojo --version -``` +- The set of automatically imported entities (types, aliases, functions) into + users' Mojo programs has been dramatically reduced. This can break existing + user code as users will need to explicitly import what they're using for cases + previously automatically included before. + +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed, but + is now optional. -To update Mojo, first [update `modular`](/cli/#description), and then run this: +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. -```sh -modular update mojo -``` +- Mojo now diagnoses "argument exclusivity" violations due to aliasing + references. Mojo requires references (including implicit references due to + `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if + mutable. This is a warning in the 24.5 release, but will be upgraded to an + error in subsequent releases. -## v24.5 (2024-09-10) +- `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. + Functions that previously took a `DTypePointer` now take an equivalent + `UnsafePointer`. For more information on using pointers, see [Unsafe + pointers](/mojo/manual/pointers) in the Mojo Manual. -### ⭐️ New +- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments + conform to the `Formattable` trait. This enables efficient stream-based + writing by default, avoiding unnecessary intermediate String heap allocations. + +- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an + optional prompt and reads a line from standard input, in the same way as + Python. + +- There are many new standard library APIs, with new features for strings, + collections, and interacting with the filesystem and environment. Changes are + listed in the standard library section. + +- The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo features, + including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and + more. + +- [`mojo test`](/mojo/cli/test) now uses the Mojo compiler for running unit + tests. This will resolve compilation issues that sometimes appeared, and will + also improve overall test execution times. + +### Language changes + +#### ⭐️ New - Mojo now diagnoses "argument exclusivity" violations due to aliasing - references. Mojo requires references (including implicit references due to - borrowed/inout arguments) to be uniquely referenced (non-aliased) if mutable. - This is important for code safety, because it allows the compiler (and readers - of code) to understand where and when a value is mutated. It is also useful - for performance optimization because it allows the compiler to know that - accesses through immutable references cannot change behind the scenes. Here is - an invalid example: + references. Mojo requires references (including implicit references due to + `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if + mutable. This is important for code safety, because it allows the compiler + (and readers of code) to understand where and when a value is mutated. It is + also useful for performance optimization because it allows the compiler to + know that accesses through immutable references cannot change behind the + scenes. Here is an invalid example: ```mojo fn take_two_strings(a: String, inout b: String): @@ -140,9 +160,9 @@ modular update mojo - Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This feature provides more flexibility and guarantees around emplacing the result - of a function compared to "guaranteed" NRVO. If a `@register_passable` result - is bound to a name, the result value is made accessible as a mutable - reference. + of a function compared to "guaranteed" named return value optimization (NRVO). + If a `@register_passable` result is bound to a name, the result value is made + accessible as a mutable reference. ```mojo fn efficiently_return_string(b: Bool) -> String as output: @@ -161,38 +181,7 @@ modular update mojo return value of the function. The compiler will error if the result is not initialized on all normal exit paths from the function. -- `String` class now have `rjust`, `ljust` and `center` methods to return - a justified string based on width and fillchar. - ([PR 3278#](https://github.com/modularml/mojo/pull/3278) by - [@mzaks](https://github.com/mzaks)) - -- Creating nested `PythonObject` from a list or tuple of python objects is - possible now: - - ```mojo - var np = Python.import_module("numpy") - var a = np.array([1, 2, 3]) - var b = np.array([4, 5, 6]) - var arrays = PythonObject([a, b]) - assert_equal(len(arrays), 2) - ``` - - Also allowing more convenient call syntax: - - ```mojo - var stacked = np.hstack((a, b)) - assert_equal(str(stacked), "[1 2 3 4 5 6]") - ``` - - ([PR 3264#](https://github.com/modularml/mojo/pull/3264) by - [@kszucs](https://github.com/kszucs)) - -- `List[T]` values are now equality comparable with `==` and `!=` when `T` is - equality comparable. - ([PR 3195#](https://github.com/modularml/mojo/pull/3195) by - [@kszucs](https://github.com/kszucs)) - -- `__setitem__` now works with variadic argument lists such as: +- `__setitem__()` now works with variadic argument lists such as: ```mojo struct YourType: @@ -200,18 +189,10 @@ modular update mojo ``` The Mojo compiler now always passes the "new value" being set using the last - keyword argument of the `__setitem__`, e.g. turning `yourType[1, 2] = 3` into + keyword argument of the `__setitem__()`, e.g. turning `yourType[1, 2] = 3` into `yourType.__setitem__(1, 2, val=3)`. This fixes [Issue #248](https://github.com/modularml/mojo/issues/248). -- `Optional` values are now equality comparable with `==` and `!=` when their - element type is equality comparable. - -- Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) - dictionary-like type, matching most of the features of the Python one. - ([PR 2910#](https://github.com/modularml/mojo/pull/2910) by - [@msaelices](https://github.com/msaelices)) - - Mojo context managers used in regions of code that may raise no longer need to define a "conditional" exit function in the form of `fn __exit__(self, e: Error) -> Bool`. This function allows the context @@ -239,8 +220,8 @@ modular update mojo print(x) # no longer complains about 'x' being uninitialized ``` -- Now supports "conditional conformances" where some methods on a struct have - additional trait requirements that the struct itself doesn't. This is +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. This is expressed through an explicitly declared `self` type: ```mojo @@ -266,8 +247,27 @@ modular update mojo Conditional conformance works with dunder methods and other things as well. +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: + + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass + + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. + ``` + - `async` functions now support memory-only results (like `String`, `List`, - etc.) and `raises`. Accordingly, both `Coroutine` and `RaisingCoroutine` have + etc.) and `raises`. Accordingly, both + [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) and + [`RaisingCoroutine`](/mojo/stdlib/builtin/coroutine/RaisingCoroutine) have been changed to accept `AnyType` instead of `AnyTrivialRegType`. This means the result types of `async` functions do not need to be `Movable`. @@ -281,565 +281,679 @@ modular update mojo Note that `async` functions do not yet support indirect calls, `ref` results, and constructors. -- As a specific form of "conditional conformances", initializers in a struct - may indicate specific parameter bindings to use in the type of their `self` - argument. For example: +- The [`Reference`](/mojo/stdlib/memory/reference/Reference) type (and many + iterators) now use [infer-only + parameters](/mojo/manual/parameters/#infer-only-parameters) to represent the + mutability of their lifetime, simplifying the interface. - ```mojo - @value - struct MyStruct[size: Int]: - fn __init__(inout self: MyStruct[0]): pass - fn __init__(inout self: MyStruct[1], a: Int): pass - fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass +- The environment variable `MOJO_PYTHON` can be pointed to an executable to pin + Mojo to a specific version: - def test(x: Int): - a = MyStruct() # Infers size=0 from 'self' type. - b = MyStruct(x) # Infers size=1 from 'self' type. - c = MyStruct(x, x) # Infers size=2 from 'self' type. + ```sh + export MOJO_PYTHON="/usr/bin/python3.11" ``` -- The `Reference` type (and many iterators) now use - [infer-only parameters](/mojo/manual/parameters/#infer-only-parameters) to - represent the mutability of their lifetime, simplifying the interface. + Or a virtual environment to always have access to those Python modules: -- `Dict` now implements `setdefault`, to get a value from the dictionary by - key, or set it to a default if it doesn't exist - ([PR #2803](https://github.com/modularml/mojo/pull/2803) - by [@msaelices](https://github.com/msaelices)) + ```sh + export MOJO_PYTHON="~/venv/bin/python" + ``` -- Added new `ExplicitlyCopyable` trait, to mark types that can be copied - explicitly, but which might not be implicitly copyable. + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + but no Python executable. - This supports work to transition the standard library collection types away - from implicit copyability, which can lead to unintended expensive copies. +#### 🦋 Changed -- Added `Identifiable` trait, used to describe types that implement the `__is__` - and `__isnot__` trait methods. - ([PR #2807](https://github.com/modularml/mojo/pull/2807)) +- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a + C-like set of semantics around pointer aliasing and derivation. However, the C + semantics bring a lot of history and baggage that are not needed in Mojo and + which complicate compiler optimizations. The language overall provides a + stronger set of invariants around pointer aliasing with lifetimes and + exclusive mutable references to values, etc. - - Also added new `assert_is()` and `assert_is_not()` test utilities to the - `testing` module. + It is now forbidden to convert a non-pointer-typed value derived from a + Mojo-allocated pointer, such as an integer address, to a pointer-typed value. + "Derived" means there is overlap in the bits of the non-pointer-typed value + with the original pointer value. Accordingly, the + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) + constructor that took an `address` keyword argument has been removed. -- `Dict` now supports `popitem`, which removes and returns the last item in the `Dict`. - ([PR #2701](https://github.com/modularml/mojo/pull/2701) - by [@jayzhan211](https://github.com/jayzhan211)) + It is still possible to make this conversion in certain cases where it is + absolutely necessary, such as interoperating with other languages like Python. + In this case, the compiler makes two assumptions: any pointer derived from a + non-pointer-typed value does not alias any Mojo-derived pointer and that any + external function calls have arbitrary memory effects. -- Added `unsafe_cstr_ptr()` method to `String` and `StringLiteral`, that - returns an `UnsafePointer[C_char]` for convenient interoperability with C - APIs. +- `await` on a coroutine now consumes it. This strengthens the invariant that + coroutines can be awaited only once. -- Added `C_char` type alias in `sys.ffi`. +### Standard library changes -- Added `StringSlice(..)` initializer from a `StringLiteral`. +- [`builtin`](/mojo/stdlib/builtin/) package: -- Added a `byte_length()` method to `String`, `StringSlice`, and `StringLiteral` -and deprecated their private `_byte_length()` methods. Added a warning to -`String.__len__` method that it will return length in Unicode codepoints in the -future and `StringSlice.__len__` now does return the Unicode codepoints length. -([PR #2960](https://github.com/modularml/mojo/pull/2960) by [@martinvuyk](https://github.com/martinvuyk)) + - The set of automatically imported entities (types, aliases, functions) into + users' Mojo programs has been dramatically reduced. Before, with the way the + `builtin` module was handled, all of the entities in the following modules + would be automatically included: -- Added new `StaticString` type alias. This can be used in place of - `StringLiteral` for runtime string arguments. + `memory`, `sys`, `os`, `utils`, `python`, `bit`, `random`, `math`, + `builtin`, `collections` -- Added `TemporaryDirectory` in module `tempfile`. - ([PR 2743](https://github.com/modularml/mojo/pull/2743) by [@artemiogr97](https://github.com/artemiogr97)) + Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are + the ones automatically imported into users' Mojo programs. This will break a + lot of user code as users will need to explicitly import what they're using + for cases previously commonly included before (such as + [`Optional`](/mojo/stdlib/collections/optional/Optional), + [`Variant`](/mojo/stdlib/utils/variant/Variant), and functions such as + [`abort()`](/mojo/stdlib/os/os/abort), + [`alignof()`](/mojo/stdlib/sys/info/alignof), + [`bitcast()`](/mojo/stdlib/memory/unsafe/bitcast), + [`bitwidthof()`](/mojo/stdlib/sys/info/bitwidthof), + [`external_call()`](/mojo/stdlib/sys/ffi/external_call), + [`simdwidthof()`](/mojo/stdlib/sys/info/simdwidthof), and + [`sizeof()`](/mojo/stdlib/sys/info/sizeof)). -- Added `NamedTemporaryFile` in module `tempfile`. - ([PR 2762](https://github.com/modularml/mojo/pull/2762) by [@artemiogr97](https://github.com/artemiogr97)) + - Some types from the `builtin` module have been moved to different modules + for clarity which is made possible now that we have a `prelude` module that + can re-export symbols from modules other than `builtin`. -- Added `oct(..)` function for formatting an integer in octal. - ([PR #2914](https://github.com/modularml/mojo/pull/2914) by [@bgreni](https://github.com/bgreni)) + In particular, the `builtin.string` module has been moved to + [`collections.string`](/mojo/stdlib/collections/string/). -- Added `String.format` method. - ([PR #2771](https://github.com/modularml/mojo/pull/2771) by [@rd4com](https://github.com/rd4com)) +- Input and output: - Support automatic and manual indexing of `*args`. + - Added the builtin [`input()`](/mojo/stdlib/builtin/io/input) function, which + behaves the same as Python. + ([PR #3392](https://github.com/modularml/mojo/pull/3392)) - Examples: + ```mojo + name = input("Enter your name: ") + print("Hello, " + name + "!") + ``` - ```mojo - print( - String("{1} Welcome to {0} {1}").format("mojo", "🔥") - ) - # 🔥 Wecome to mojo 🔥 - ``` + If the user enters "Mojo" it returns "Hello Mojo!" - ```mojo - print(String("{} {} {}").format(True, 1.125, 2)) - #True 1.125 2 - ``` + - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments + conform to the `Formattable` trait. This enables efficient stream-based + writing by default, avoiding unnecessary intermediate String heap + allocations. -- Added the builtin `input` function, which behaves the same as Python. - ([PR #3392](https://github.com/modularml/mojo/pull/3392) by [@thatstoasty](https://github.com/thatstoasty)) + Previously, `print()` required types conform to + [`Stringable`](/mojo/stdlib/builtin/str/Stringable). This meant that to + execute a call like `print(a, b, c)`, at least three separate String heap + allocations were down, to hold the formatted values of `a`, `b`, and `c` + respectively. The total number of allocations could be much higher if, for + example, `a.__str__()` was implemented to concatenate together the fields of + `a`, like in the following example: - ```mojo - name = input("Enter your name: ") - print("Hello, " + name + "!") - ``` + ```mojo + struct Point(Stringable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + # Performs 3 allocations: 1 each for str(..) of each of the fields, + # and then the final returned `String` allocation. + return "(" + str(self.x) + ", " + str(self.y) + ")" + ``` - If the user enters "Mojo" it returns "Hello Mojo!" + A type like the one above can transition to additionally implementing + `Formattable` with the following changes: -- Environment variable `MOJO_PYTHON` can be pointed to an executable to pin Mojo - to a specific version: + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 - ```sh - export MOJO_PYTHON="/usr/bin/python3.11" - ``` + fn __str__(self) -> String: + return String.format_sequence(self) - Or a virtual environment to always have access to those Python modules: + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` - ```sh - export MOJO_PYTHON="~/venv/bin/python" - ``` + In the example above, + [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) + is used to construct a `String` from a type that implements `Formattable`. + This pattern of implementing a type's `Stringable` implementation in terms + of its `Formattable` implementation minimizes boilerplate and duplicated + code, while retaining backwards compatibility with the requirements of the + commonly used `str()` function. - `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, - but no Python executable. + -- The `math` package now includes the `pi`, `e`, and `tau` constants (Closes - Issue [#2135](https://github.com/modularml/mojo/issues/2135)). + :::note TODO -- Mojo now has a `UInt` type for modeling unsigned (scalar) integers with a - paltform-dependent width. `UInt` implements most arithmetic operations that - make sense for integers, with the notable exception of `__neg__`. Builtin - functions such as `min`/`max`, as well as `math` functions like `ceildiv`, - `align_down`, and `align_up` are also implemented for `UInt`. + The error shown when passing a type that does not implement `Formattable` to + `print()` is currently not entirely descriptive of the underlying cause: -- `os.path.expanduser()` and `pathlib.Path.exapanduser()` have been added to - allow expanding a prefixed `~` in a `String` or `Path` with the users home - path: + ```shell + error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified + print(point) + ~~~~~^~~~~~~ + ``` - ```mojo - import os - print(os.path.expanduser("~/.modular")) - # /Users/username/.modular - print(os.path.expanduser("~root/folder")) - # /var/root/folder (on macos) - # /root/folder (on linux) - ``` + If you see the above error, ensure that all argument types implement + `Formattable`. -- `Path.home()` has been added to return a path of the users home directory. + ::: -- `os.path.split()` has been added for splitting a path into `head, tail`: + - [`debug_assert()`](/mojo/stdlib/builtin/debug_assert/debug_assert) now also + requires that its `message` argument conform to `Formattable`. - ```mojo - import os - head, tail = os.path.split("/this/is/head/tail") - print("head:", head) - print("tail:", tail) - # head: /this/is/head - # tail: tail - ``` + - Added + [`TemporaryDirectory`](/mojo/stdlib/tempfile/tempfile/TemporaryDirectory) in + module `tempfile`. + ([PR 2743](https://github.com/modularml/mojo/pull/2743)) -- `os.path.makedirs()` and `os.path.removedirs()` have been added for creating - and removing nested directories: + - Added + [`NamedTemporaryFile`](/mojo/stdlib/tempfile/tempfile/NamedTemporaryFile) in + module `tempfile`. + ([PR 2762](https://github.com/modularml/mojo/pull/2762)) - ```mojo - import os - path = os.path.join("dir1", "dir2", "dir3") - os.path.makedirs(path, exist_ok=True) - os.path.removedirs(path) - ``` +- [`String`](/mojo/stdlib/collections/string/String) and friends: -- The `pwd` module has been added for accessing user information in - `/etc/passwd` on POSIX systems. This follows the same logic as Python: + - The `builtin.string` module has been moved to + [`collections.string`](/mojo/stdlib/collections/string/). - ```mojo - import pwd - import os - current_user = pwd.getpwuid(os.getuid()) - print(current_user) + - Added the [`String.format()`](/mojo/stdlib/collections/string/String#format) + method. + ([PR #2771](https://github.com/modularml/mojo/pull/2771)) - # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, - # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', - # pw_shell='/bin/zsh') + Supports automatic and manual indexing of `*args`. - print(current_user.pw_uid) + Examples: - # 501 + ```mojo + print( + String("{1} Welcome to {0} {1}").format("mojo", "🔥") + ) + # 🔥 Wecome to mojo 🔥 + ``` - root = pwd.getpwnam("root") - print(root) + ```mojo + print(String("{} {} {}").format(True, 1.125, 2)) + #True 1.125 2 + ``` - # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, - # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') - ``` + - [`String.format()`](/mojo/stdlib/collections/string/String#format) now + supports conversion flags `!s` and `!r`, allowing for `str()` and `repr()` + conversions within format strings. + ([PR #3279](https://github.com/modularml/mojo/pull/3279)) -- Added `Dict.__init__` overload to specify initial capacity. - ([PR #3171](https://github.com/modularml/mojo/pull/3171) by [@rd4com](https://github.com/rd4com)) + Example: - The capacity has to be a power of two and above or equal 8. + ```mojo + String("{} {!r}").format("Mojo", "Mojo") + # "Mojo 'Mojo'" - It allows for faster initialization by skipping incremental growth steps. + String("{0!s} {0!r}").format("Mojo") + # "Mojo 'Mojo'" + ``` - Example: + - The `String` class now has + [`rjust()`](/mojo/stdlib/collections/string/String#rjust), + [`ljust()`](/mojo/stdlib/collections/string/String#ljust), and + [`center()`](/mojo/stdlib/collections/string/String#center) methods to + return a justified string based on width and fillchar. ([PR + #3278](https://github.com/modularml/mojo/pull/3278)) + + - The [`atol()`](/mojo/stdlib/collections/string/atol) function now correctly + supports leading underscores, (e.g.`atol("0x_ff", 0)`), when the appropriate + base is specified or inferred (base 0). non-base-10 integer literals as per + Python's [Integer + Literals](). + ([PR #3180](https://github.com/modularml/mojo/pull/3180)) + + - Added the + [`unsafe_cstr_ptr()`](/mojo/stdlib/collections/string/String#unsafe_cstr_ptr) + method to `String` and `StringLiteral`, which returns an + `UnsafePointer[C_char]` for convenient interoperability with C APIs. + + - Added the `byte_length()` method to `String`, `StringSlice`, and + `StringLiteral` and deprecated their private `_byte_length()` methods. + Added a warning to `String.__len__` method that it will return length in + Unicode codepoints in the future and `StringSlice.__len__()` now does return + the Unicode codepoints length. + ([PR #2960](https://github.com/modularml/mojo/pull/2960)) + + - Added a new [`StaticString`](/mojo/stdlib/utils/string_slice/#aliases) type + alias. This can be used in place of + [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral) for + runtime string arguments. + + - Added a + [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice#__init__) + initializer that accepts a `StringLiteral`. + + - The [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) constructors from + `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, + reflecting their use for compatibility with C APIs. + + - Continued transition to `UnsafePointer` and unsigned byte type for strings: + + - [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr) + now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) + + - [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr) + now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) + +- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and other + reference type changes: + + - `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. + For more information on using pointers, see [Unsafe + pointers](/mojo/manual/pointers) in the Mojo Manual. + + Functions that previously took a `DTypePointer` now take an + equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to + `UnsafePointer` is: - ```mojo - var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) - # Insert (2/3 of 1024) entries - ``` + ```mojo + DTypePointer[type] -> UnsafePointer[Scalar[type]] + ``` -- `ListLiteral` now supports `__contains__`. - ([PR #3251](https://github.com/modularml/mojo/pull/3251) by - [@jjvraw](https://github.com/jjvraw)) + There could be places that you have code of the form: -- `bit` module now supports `bit_reverse()`, `byte_swap()` and `pop_count()` for - `Int` type. - ([PR #3150](https://github.com/modularml/mojo/pull/3150) by [@LJ-9801](https://github.com/LJ-9801)) + ```mojo + fn f(ptr: DTypePointer): + ``` -- `String.format()` now supports conversion flags `!s` and `!r`, allowing for - `str()` and `repr()` conversions within format strings. - ([PR #3279](https://github.com/modularml/mojo/pull/3279) by [@jjvraw](https://github.com/jjvraw)) + which is equivalent to `DTypePointer[*_]`. In this case you would have to add + an infer-only `type` parameter to the function: - Example: + ```mojo + fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): + ``` - ```mojo - String("{} {!r}").format("Mojo", "Mojo") - # "Mojo 'Mojo'" + because we can’t have an unbound parameter inside the struct. - String("{0!s} {0!r}").format("Mojo") - # "Mojo 'Mojo'" - ``` + There could also be places where you use + `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to + change these to `UnsafePointer[NoneType/Int]`. But since these are not an + `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to + appropriate types. -- `sort` now supports `stable` parameter. It can be called by + - The `DTypePointer` + [`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and + [`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods + have been moved to `UnsafePointer`. - ```mojo - sort[cmp_fn, stable=True](list) - ``` + - `UnsafePointer` now supports + [`strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_load), + [`strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_store), + [`gather()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#gather), and + [`scatter()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#scatter) when + the underlying type is `Scalar[DType]`. - The algorithm requires $$O(N)$$ auxiliary memory, if extra memory is failed to - allocate, the program will crash. + - The global functions for working with `UnsafePointer` have transitioned to + being methods through the use of conditional conformances: -- The `mojo test` command now accepts a `--filter` option that will narrow the - set of tests collected and executed. The filter string is a POSIX extended - regular expression. + - `destroy_pointee(p)` => [`p.destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) + - `move_from_pointee(p)` => [`p.take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee) + - `initialize_pointee_move(p, value)` => [`p.init_pointee_move(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move) + - `initialize_pointee_copy(p, value)` => [`p.init_pointee_copy(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) + - `move_pointee(src=p1, dst=p2)` => [`p.move_pointee_into(p2)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) -- The `mojo test` command now supports using the same compilation options as - `mojo build`. + - The `UnsafePointer.offset()` method has been removed. Use + [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. -- You can now debug unit tests using `mojo test` by passing the `--debug` flag. - Most debug flags are supported; run `mojo test --help` for a full listing. + ```mojo + new_ptr = ptr.offset(1) + ``` - Debugging doctests is not currently supported. + Becomes: -- `UnsafePointer` now has an `alignment` parameter to specify the static - alignment of the pointer. Consequently, `UnsafePointer.alloc` no longer takes - in an alignment parameter, and the alignment should be specified in the type. + ```mojo + new_ptr = ptr + 1 + ``` - ```mojo - UnsafePointer[type].alloc[alignment](x) # now becomes - UnsafePointer[type, alignment].alloc(x) - ``` + - `UnsafePointer` now has an + [`alignment`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#parameters) + parameter to specify the static alignment of the pointer. Consequently, + [`UnsafePointer.alloc()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#alloc) + no longer takes in an alignment parameter, and the alignment should be + specified in the type. -- The VS Code extension now supports a vendored MAX SDK for VS Code, which is - automatically downloaded by the extension and it's used for all Mojo features, - including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and - more. + ```mojo + UnsafePointer[type].alloc[alignment](x) # now becomes + UnsafePointer[type, alignment].alloc(x) + ``` -- The Mojo debugger now hides the artificial function arguments `__result__` and - `__error__` created by the compiler for Mojo code. + - `UnsafePointer` has a new [`exclusive: Bool = + False`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#parameters) + parameter. Setting this parameter to true tells the compiler that the user + knows this pointer and all those derived from it have exclusive access to + the underlying memory allocation. The compiler is not guaranteed to do + anything with this information. -- The Mojo debugger now supports a `break-on-raise` command that indicated the - debugger to stop at any `raise` statements. A similar features has been added - to the debugger on VS Code. + - It is no longer possible to cast (implicitly or explicitly) from `Reference` + to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the + [`UnsafePointer.address_of(someRef[])`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#address_of) + which makes the code explicit that the `UnsafePointer` gets the address of + what the reference points to. -- A proxy has been added to the Mojo Language Server on VS Code that handles - crashes more gracefully. +- Python interoperability changes: -### 🦋 Changed + - Creating a nested + [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) from a list + or tuple of Python objects is possible now: -- The set of automatically imported entities (types, aliases, functions) into user's - Mojo programs has been dramatically reduced. Before, with the way the `builtin` - module was handled, all of the entities in the following modules would be automatically - included: + ```mojo + var np = Python.import_module("numpy") + var a = np.array([1, 2, 3]) + var b = np.array([4, 5, 6]) + var arrays = PythonObject([a, b]) + assert_equal(len(arrays), 2) + ``` - {'memory', 'sys', 'os', 'utils', 'python', 'bit', 'random', 'math', - 'builtin', 'collections'} + Also allowing more convenient call syntax: - Now, only the explicitly enumerated entities in `prelude/__init__.mojo` are - the ones automatically imported into user's Mojo programs. This will break - a lot of user code as users will need to explicitly import what they're using - for cases previously commonly included before (such as `Optional`, `Variant`, - and so on). + ```mojo + var stacked = np.hstack((a, b)) + assert_equal(str(stacked), "[1 2 3 4 5 6]") + ``` -- Some types from the `builtin` module have been moved to different modules for clarity - which is made possible now that we have a `prelude` module that can re-export symbols - from modules other than `builtin`. - - `builtin.string` has been moved to `collections.string`. + ([PR #3264](https://github.com/modularml/mojo/pull/3264)) -- The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a - C-like set of semantics around pointer aliasing and derivation. However, the C - semantics bring a lot of history and baggage that are not needed in Mojo and - which complicate compiler optimizations. The language overall provides a - stronger set of invariants around pointer aliasing with lifetimes and - exclusive mutable references to values, etc. + - Accessing local Python modules with + [`Python.add_to_path(".")`](/mojo/stdlib/python/python/Python#add_to_path) + is no longer required. It now behaves the same as Python. You can access + modules in the same folder as the target file: - It is now forbidden to convert a non-pointer-typed value derived from a - Mojo-allocated pointer, such as an integer address, to a pointer-typed value. - "Derived" means there is overlap in the bits of the non-pointer-typed value - with the original pointer value. Accordingly, the `UnsafePointer` constructor - that took an `address` keyword argument has been removed. + - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - It is still possible to make this conversion in certain cases where it is - absolutely necessary, such as interoperating with other languages like Python. - In this case, the compiler makes two assumptions: any pointer derived from a - non-pointer-typed value does not alias any Mojo-derived pointer and that any - external function calls have arbitrary memory effects. + - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` -- `DTypePointer` , `LegacyPointer` and `Pointer` have been removed. Use - [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/) instead. For more - information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in - the Mojo Manual. +- Collections: - Functions that previously took a `DTypePointer` now take an - equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to - `UnsafePointer` is: + - [`List`](/mojo/stdlib/collections/list/List) values are now equality + comparable with `==` and `!=` when their element type is equality + comparable. ([PR #3195](https://github.com/modularml/mojo/pull/3195)) - ```mojo - DTypePointer[type] -> UnsafePointer[Scalar[type]] - ``` + - [`Optional`](/mojo/stdlib/collections/optional/Optional) values are now + equality comparable with `==` and `!=` when their element type is equality + comparable. - There could be places that you have code of the form: + - Added a new [`Counter`](/mojo/stdlib/collections/counter/Counter) + dictionary-like type, matching most of the features of the Python one. + ([PR #2910](https://github.com/modularml/mojo/pull/2910)) - ```mojo - fn f(ptr: DTypePointer): - ``` + - [`Dict`](/mojo/stdlib/collections/dict/Dict) now implements + [`setdefault()`](/mojo/stdlib/collections/dict/Dict#setdefault), which gets + a value from the dictionary by key, or sets it to a default if it doesn't + exist. + ([PR #2803](https://github.com/modularml/mojo/pull/2803)) - which is equivalent to `DTypePointer[*_]`. In this case you would have to add - an infer-only `type` parameter to the function: + - `Dict` now supports + [`popitem()`](/mojo/stdlib/collections/dict/Dict#popitem), which removes and + returns the last item in the `Dict`. + ([PR #2701](https://github.com/modularml/mojo/pull/2701)) - ```mojo - fn f[type: DType, //](ptr: UnsafePointer[Scalar[type]]): - ``` + - Added a [`Dict.__init__()`](/mojo/stdlib/collections/dict/Dict#__init__) + overload to specify initial capacity. + ([PR #3171](https://github.com/modularml/mojo/pull/3171)) - because we can’t have an unbound parameter inside the struct. + The capacity has to be a power of two and greater than or equal to 8. - There could also be places where you use - `DTypePointer[Scalar[DType.invalid/index]]`, and it would be natural to - change these to `UnsafePointer[NoneType/Int]`. But since these are not an - `UnsafePointer` that stores a `Scalar`, you might have to `rebind/bitcast` to - appropriate types. + It allows for faster initialization by skipping incremental growth steps. -- The `DTypePointer` `load()`, `store()`, and `prefetch()` methods have been - moved to `SIMD` and now take an - `UnsafePointer` as an argument. Instead of using `ptr.load[width=4](offset)` - one should use `SIMD[size=4].load(ptr, offset)`. Note the default load width - before was 1, but the default size of `SIMD` is the size of the SIMD type. The - default store size is the size of the `SIMD` value to be stored. + Example: -- `UnsafePointer` now supports `simd_strided_load()`, `simd_strided_store()`, - `gather()`, and `scatter()` when the underlying type is `Scalar[DType]`. + ```mojo + var dictionary = Dict[Int,Int](power_of_two_initial_capacity = 1024) + # Insert (2/3 of 1024) entries + ``` -- The global functions for working with `UnsafePointer` have transitioned to - being methods through the use of conditional conformances: + - `ListLiteral` now supports + [`__contains__()`](/mojo/stdlib/builtin/builtin_list/ListLiteral#__contains__). + ([PR #3251](https://github.com/modularml/mojo/pull/3251)) - - `destroy_pointee(p)` => `p.destroy_pointee()` - - `move_from_pointee(p)` => `p.take_pointee()` - - `initialize_pointee_move(p, value)` => `p.init_pointee_move(value)` - - `initialize_pointee_copy(p, value)` => `p.init_pointee_copy(value)` - - `move_pointee(src=p1, dst=p2)` => `p.move_pointee_into(p2)` +- Filesystem and environment utilities: -- The `UnsafePointer.offset()` method has been removed. Use - [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. + - [`Path.home()`](/mojo/stdlib/pathlib/path/Path#home) has been added to + return a path of the users home directory. - ```mojo - new_ptr = ptr.offset(1) - ``` + - [`os.path.expanduser()`](/mojo/stdlib/os/path/path/expanduser) and + [`pathlib.Path.exapanduser()`](/mojo/stdlib/pathlib/path/Path#expanduser) + have been added to allow expanding a prefixed `~` in a `String` or `Path` + with the users home path: - Becomes: + ```mojo + import os + print(os.path.expanduser("~/.modular")) + # /Users/username/.modular + print(os.path.expanduser("~root/folder")) + # /var/root/folder (on macos) + # /root/folder (on linux) + ``` - ```mojo - new_ptr = ptr + 1 - ``` + - [`os.path.split()`](/mojo/stdlib/os/path/path/split) has been added for + splitting a path into `head, tail`: -- `UnsafePointer` has a new - `exclusive: Bool = False` parameter. Setting this parameter to true tells the - compiler that the user knows this pointer and all those derived from it have - exclusive access to the underlying memory allocation. The compiler is not - guaranteed to do anything with this information. + ```mojo + import os + head, tail = os.path.split("/this/is/head/tail") + print("head:", head) + print("tail:", tail) + # head: /this/is/head + # tail: tail + ``` -- It is no longer possible to cast (implicitly or explicitly) from `Reference` - to `UnsafePointer`. Instead of `UnsafePointer(someRef)` please use the - `UnsafePointer.address_of(someRef[])` which makes the code explicit that the - `UnsafePointer` gets the address of what the reference points to. + - [`os.makedirs()`](/mojo/stdlib/os/os/makedirs) and + [`os.removedirs()`](/mojo/stdlib/os/os/removedirs) have been added for + creating and removing nested directories: -- `sort` no longer takes `LegacyPointer`. The current API supports: - - `sort(list)` just plain list - - `sort[type, cmp_fn](list)` list with custom compare function - - `sort(ptr, len)` a pointer and length (can change to Span in future) - - `sort[type, cmp_fn](ptr, len)` above with custom compare + ```mojo + import os + path = os.path.join("dir1", "dir2", "dir3") + os.path.makedirs(path, exist_ok=True) + os.path.removedirs(path) + ``` -- Continued transition to `UnsafePointer` and unsigned byte type for strings: + - The [`pwd`](/mojo/stdlib/pwd/pwd/) module has been added for accessing user + information in `/etc/passwd` on POSIX systems. This follows the same logic + as Python: - - `String.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `UnsafePointer[Int8]`) - - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` - (was `UnsafePointer[Int8]`) + ```mojo + import pwd + import os + current_user = pwd.getpwuid(os.getuid()) + print(current_user) -- `await` on a coroutine now consumes it. This strengthens the invariant that - coroutines can only be awaited once. + # pwd.struct_passwd(pw_name='jack', pw_passwd='********', pw_uid=501, + # pw_gid=20, pw_gecos='Jack Clayton', pw_dir='/Users/jack', + # pw_shell='/bin/zsh') -- `print()` now requires that its arguments conform to the `Formattable` trait. - This enables efficient stream-based writing by default, avoiding unnecessary - intermediate String heap allocations. + print(current_user.pw_uid) - Previously, `print()` required types conform to `Stringable`. This meant that - to execute a call like `print(a, b, c)`, at least three separate String heap - allocations were down, to hold the formatted values of `a`, `b`, and `c` - respectively. The total number of allocations could be much higher if, for - example, `a.__str__()` was implemented to concatenate together the fields of - `a`, like in the following example: + # 501 - ```mojo - struct Point(Stringable): - var x: Float64 - var y: Float64 + root = pwd.getpwnam("root") + print(root) - fn __str__(self) -> String: - # Performs 3 allocations: 1 each for str(..) of each of the fields, - # and then the final returned `String` allocation. - return "(" + str(self.x) + ", " + str(self.y) + ")" - ``` + # pwd.struct_passwd(pw_name='root', pw_passwd='*', pw_uid=0, pw_gid=0, + # pw_gecos='System Administrator', pw_dir='/var/root', pw_shell='/bin/zsh') + ``` - A type like the one above can transition to additionally implementing - `Formattable` with the following changes: +- Other new traits and related features: - ```mojo - struct Point(Stringable, Formattable): - var x: Float64 - var y: Float64 + - Added the + [`ExplicitlyCopyable`](/mojo/stdlib/builtin/value/ExplicitlyCopyable) trait + to mark types that can be copied explicitly, but which might not be + implicitly copyable. - fn __str__(self) -> String: - return String.format_sequence(self) + This supports work to transition the standard library collection types away + from implicit copyability, which can lead to unintended expensive copies. - fn format_to(self, inout writer: Formatter): - writer.write("(", self.x, ", ", self.y, ")") - ``` + - Added the [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable) + trait, used to describe types that implement the `__is__()` and + `__isnot__()` trait methods. + ([PR #2807](https://github.com/modularml/mojo/pull/2807)) - In the example above, `String.format_sequence()` is used to construct a - `String` from a type that implements `Formattable`. This pattern of - implementing a type's `Stringable` implementation in terms of its `Formattable` - implementation minimizes boilerplate and duplicated code, while retaining - backwards compatibility with the requirements of the commonly used `str(..)` - function. + - Types conforming to [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) (that + is, those implementing `__bool__()`) no longer implicitly convert to `Bool`. + A new [`ImplicitlyBoolable`](/mojo/stdlib/builtin/bool/ImplicitlyBoolable) + trait is introduced for types where this behavior is desired. - +- Miscellaneous: - > [!WARNING] - > The error shown when passing a type that does not implement `Formattable` to - > `print()` is currently not entirely descriptive of the underlying cause: - > - > ```shell - > error: invalid call to 'print': callee with non-empty variadic pack argument expects 0 positional operands, but 1 was specified - > print(point) - > ~~~~~^~~~~~~ - > ``` - > - > If the above error is seen, ensure that all argument types implement - > `Formattable`. + - [`NoneType`](/mojo/stdlib/builtin/none/NoneType) is now a normal standard + library type, and not an alias for a raw MLIR type. -- `debug_assert()` now also requires that its `message` argument conform to - `Formattable`. + Function signatures spelled as `fn() -> NoneType` should transition to + being written as `fn() -> None`. -- The `StringRef` constructors from `DTypePointer.int8` have been changed to - take a `UnsafePointer[C_char]`, reflecting their use for compatibility with - C APIs. + - Mojo now has a [`UInt`](/mojo/stdlib/builtin/uint/UInt) type for modeling + unsigned (scalar) integers with a platform-dependent width. `UInt` + implements most arithmetic operations that make sense for integers, with the + notable exception of `__neg__()`. Builtin functions such as `min()`/`max()`, + as well as `math` functions like `ceildiv()`, `align_down()`, and + `align_up()` are also implemented for `UInt`. -- `Slice` now uses `OptionalReg[Int]` for `start` and `end` and implements - a constructor which accepts optional values. `Slice._has_end()` has also been - removed since a Slice with no end is now represented by an empty `Slice.end` - option. - ([PR #2495](https://github.com/modularml/mojo/pull/2495) by [@bgreni](https://github.com/bgreni)) + - Now that we have a `UInt` type, use this to represent the return type of a + hash. In general, hashes should be an unsigned integer, and can also lead to + improved performance in certain cases. - ```mojo - var s = Slice(1, None, 2) - print(s.start.value()) # must retrieve the value from the optional - ``` + - Added the [`C_char`](/mojo/stdlib/sys/ffi/#aliases) type alias in `sys.ffi`. -- `NoneType` is now a normal standard library type, and not an alias for a raw - MLIR type. + - [`sort()`](/mojo/stdlib/builtin/sort/sort) now supports a `stable` + parameter. It can be called by - Function signatures spelled as `fn(...) -> NoneType` should transition to - being written as `fn(...) -> None`. + ```mojo + sort[cmp_fn, stable=True](list) + ``` -- Accessing local Python modules with `Python.add_to_path(".")` is no longer - required, it now behaves the same as Python, you can access modules in the - same folder as the target file: + The algorithm requires $$O(N)$$ auxiliary memory. If extra memory allocation + fails, the program crashs. - - `mojo run /tmp/main.mojo` can access `/tmp/mymodule.py` - - `mojo build main.mojo -o ~/myexe && ~/myexe` can access `~/mymodule.py` + - `sort()` no longer takes `LegacyPointer` since that type is now removed. -- The rank argument for `algorihtm.elementwise` is no longer required and is - only inferred. + - Added the [`oct()`](/mojo/stdlib/builtin/format_int/oct) builtin function + for formatting an integer in octal. + ([PR #2914](https://github.com/modularml/mojo/pull/2914)) -- The `ulp` function in `numerics` has been moved to the `math` module. + - Added the [`assert_is()`](/mojo/stdlib/testing/testing/assert_is) and + [`assert_is_not()`](/mojo/stdlib/testing/testing/assert_is_not) test + functions to the `testing` module. -- The Mojo Language Server no longer sets `.` as a commit character for - auto-completion. + - The [`math`](/mojo/stdlib/math/constants/) package now includes the `pi`, + `e`, and `tau` constants (Closes Issue + [#2135](https://github.com/modularml/mojo/issues/2135)). -- Types conforming to `Boolable` (i.e. those implementing `__bool__`) no longer - implicitly convert to `Bool`. A new `ImplicitlyBoolable` trait is introduced - for types where this behavior is desired. + - The [`ulp`](/mojo/stdlib/math/math/ulp) function from `numerics` has been + moved to the `math` module. -- The `time.now()` function has been deprecated. Please use `time.perf_counter` - or `time.perf_counter_ns` instead. + - `bit` module now supports + [`bit_reverse()`](/mojo/stdlib/bit/bit/bit_reverse), + [`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap), and + [`pop_count()`](/mojo/stdlib/bit/bit/pop_count) for the `Int` type. + ([PR #3150](https://github.com/modularml/mojo/pull/3150)) -- A few bit functions have been renamed for clarity: -- `countl_zero` -> `count_leading_zeros` -- `countr_zero` -> `count_trailing_zeros` + - A few `bit` functions have been renamed for clarity: -- Now that we have a `UInt` type, use this to represent the return type of hash. - In general, hashes should be an unsigned integer, and can also lead to improved - performance in certain cases. + - `countl_zero()` -> [`count_leading_zeros()`](/mojo/stdlib/bit/bit/count_leading_zeros) -- The `atol` function now correctly supports leading underscores, - (e.g.`atol("0x_ff", 0)`), when the appropriate base is specified or inferred - (base 0). non-base-10 integer literals as per Python's [Integer Literals](\ - ). - ([PR #3180](https://github.com/modularml/mojo/pull/3180) - by [@jjvraw](https://github.com/jjvraw)) + - `countr_zero()` -> [`count_trailing_zeros()`](/mojo/stdlib/bit/bit/count_trailing_zeros) -- `SIMD` construction from `Bool` has been restricted to `DType.bool` data type. + - [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) now uses + `OptionalReg[Int]` for `start` and `end` and implements a constructor which + accepts optional values. `Slice._has_end()` has also been removed since a + Slice with no end is now represented by an empty `Slice.end` option. + ([PR #2495](https://github.com/modularml/mojo/pull/2495)) -- `SIMD.load/store` are moved to `UnsafePointer`. + ```mojo + var s = Slice(1, None, 2) + print(s.start.value()) # must retrieve the value from the optional + ``` + + - The `rank` argument for + [`algorithm.elementwise()`](/mojo/stdlib/algorithm/functional/elementwise) + is no longer required and is only inferred. + + - The `time.now()` function has been deprecated. Please use + [`time.perf_counter()`](/mojo/stdlib/time/time/perf_counter) or + [`time.perf_counter_ns`](/mojo/stdlib/time/time/perf_counter_ns) instead. + + - [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) construction from `Bool` has been + restricted to `DType.bool` data type. + +### Tooling changes + +- [`mojo test`](/mojo/cli/test) new features and changes: + + - `mojo test` now uses the Mojo compiler for running unit tests. This will + resolve compilation issues that sometimes appeared, and will also improve + overall test times, since we will only compile unit tests once before + executing all of them. + + These changes do not apply to doctests, due to their different semantics. + + - The `mojo test` command now accepts a `--filter` option that will narrow the + set of tests collected and executed. The filter string is a POSIX extended + regular expression. + + - The `mojo test` command now supports using the same compilation options as + `mojo build`. + + - You can now debug unit tests using `mojo test` by passing the `--debug` + flag. Most debug flags are supported; run `mojo test --help` for a full + listing. + + Debugging doctests is not currently supported. + +- Mojo debugger new features and changes: + + - The `mojo debug --rpc` command has been renamed to [`mojo debug + --vscode`](/mojo/cli/debug#debug-server-options), which is now able to + manage multiple VS Code windows. -- `bitcast, sizeof, simdwidthof, bitwidthof, alignof, external_call` and `abort` - are removed from prelude. + - The Mojo debugger now supports a `break-on-raise` command that indicated the + debugger to stop at any `raise` statements. A similar features has been + added to the debugger on VS Code. -- The `simd_strided_load()` and `simd_strided_store()` have been renamed to - `strided_load` and `strided_store` in `UnsafePointer`. + - The Mojo debugger now hides the artificial function arguments `__result__` + and `__error__` created by the compiler for Mojo code. -- `mojo test` now uses the Mojo compiler for running unit tests. This will resolve - compilation issues that sometimes appeared, and will also improve overall test - times, since we will only compile unit tests once before executing all of them. +- VS Code support changes: - These changes do not apply to doctests, due to their different semantics. + - The VS Code extension now supports a vendored MAX SDK for VS Code, which is + automatically downloaded by the extension and it's used for all Mojo + features, including the Mojo Language Server, the Mojo debugger, the Mojo + formatter, and more. -- The `mojo debug --rpc` command has been renamed to `mojo debug --vscode`, - which is now able to manage multiple VS Code windows. + - A proxy has been added to the Mojo Language Server on VS Code that handles + crashes more gracefully. + +- The Mojo Language Server no longer sets `.` as a commit character for + auto-completion. ### ❌ Removed - Support for the legacy `fn __init__(...) -> Self:` form has been removed from the compiler, please switch to using `fn __init__(inout self, ...):` instead. +- The builtin `tensor` module has been removed. Identical functionality is + available in [`max.tensor`](/max/api/mojo/tensor/tensor), but it is generally + recommended to use structs from the [`buffer`](/mojo/stdlib/buffer/buffer) + module when possible instead. + - Removed `String.unsafe_uint8_ptr()`. `String.unsafe_ptr()` now returns the same thing. - Removed `StringLiteral.unsafe_uint8_ptr()` and `StringLiteral.as_uint8_ptr()`. -- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for SIMD +- Removed `SIMD.splat(value: Scalar[type])`. Use the constructor for `SIMD` instead. -- The builtin `tensor` module has been removed. Identical functionality is - available in `max.tensor`, but it is generally recommended to use `buffer` - when possible instead. - -- Removed the Mojo Language Server warnings for unused function arguments. +- Removed the `SIMD.{add,mul,sub}_with_overflow()` methods. -- Removed the `SIMD.{add,mul,sub}_with_overflow` methods. +- Removed the `SIMD.min()` and `SIMD.max()` methods. Identical functionality is + available using the builtin [`min()`](/mojo/stdlib/builtin/math/min) and + [`max()`](/mojo/stdlib/builtin/math/max) functions. -- Removed the `SIMD.min` and `SIMD.max` methods. Identical functionality is - available using the builting `min` and `max` functions. +- Removed the Mojo Language Server warnings for unused function arguments. - `Run Mojo File in Dedicated Terminal` action has been removed, and the action `Run Mojo File` will always open a dedicated terminal for each mojo @@ -883,11 +997,24 @@ future and `StringSlice.__len__` now does return the Unicode codepoints length. - [#3336](https://github.com/modularml/mojo/issues/3336) - Fix outdated references to `let` in REPL documentation. -- The VS Code extension doesn't cache anymore the information of the selected +- The VS Code extension no longer caches the information of the selected MAX SDK, which was causing issues upon changes in the SDK. - The Mojo debugger now stops showing spurious warnings when parsing closures. +### Special thanks + +Special thanks to our community contributors: +[@jjvraw](https://github.com/jjvraw), +[@artemiogr97](https://github.com/artemiogr97), +[@martinvuyk](https://github.com/martinvuyk), +[@jayzhan211](https://github.com/jayzhan211), +[@bgreni](https://github.com/bgreni), [@mzaks](https://github.com/mzaks), +[@msaelices](https://github.com/msaelices), +[@rd4com](https://github.com/rd4com), [@jiex-liu](https://github.com/jiex-liu), +[@kszucs](https://github.com/kszucs), +[@thatstoasty](https://github.com/thatstoasty) + ## v24.4 (2024-06-07) ### ✨ Highlights @@ -1190,31 +1317,31 @@ Big themes for this release: types such as `Bencher` which provides the ability to execute a `Benchmark` and allows for benchmarking configuration via the `BenchmarkConfig` struct. -- [`String`](/mojo/stdlib/builtin/string/String) and friends: +- [`String`](/mojo/stdlib/collections/string/String) and friends: - **Breaking.** Implicit conversion to `String` is now removed for builtin classes/types. Use [`str()`](/mojo/stdlib/builtin/str/str) explicitly to convert to `String`. - - Added [`String.isspace()`](/mojo/stdlib/builtin/string/String#isspace) + - Added [`String.isspace()`](/mojo/stdlib/collections/string/String#isspace) method conformant with Python's universal separators. This replaces the `isspace()` free function from the `string` module. (If you need the old function, it is temporarily available as `_isspace()`. It now takes a `UInt8` but is otherwise unchanged.) - - [`String.split()`](/mojo/stdlib/builtin/string/String#split) now defaults to - whitespace and has Pythonic behavior in that it removes all adjacent - whitespace by default. + - [`String.split()`](/mojo/stdlib/collections/string/String#split) now + defaults to whitespace and has Pythonic behavior in that it removes all + adjacent whitespace by default. - - [`String.strip()`](/mojo/stdlib/builtin/string/String#strip), - [`lstrip()`](/mojo/stdlib/builtin/string/String#lstrip) and - [`rstrip()`](/mojo/stdlib/builtin/string/String#rstrip) can now remove + - [`String.strip()`](/mojo/stdlib/collections/string/String#strip), + [`lstrip()`](/mojo/stdlib/collections/string/String#lstrip) and + [`rstrip()`](/mojo/stdlib/collections/string/String#rstrip) can now remove custom characters other than whitespace. In addition, there are now several useful aliases for whitespace, ASCII lower/uppercase, and so on. ([PR #2555](https://github.com/modularml/mojo/pull/2555)) - `String` now has a - [`splitlines()`](/mojo/stdlib/builtin/string/String#splitlines) method, + [`splitlines()`](/mojo/stdlib/collections/string/String#splitlines) method, which allows splitting strings at line boundaries. This method supports [universal newlines](https://docs.python.org/3/glossary.html#term-universal-newlines) and provides an option to retain or remove the line break characters. @@ -1243,13 +1370,13 @@ Big themes for this release: points to. - Added new - [`as_string_slice()`](/mojo/stdlib/builtin/string/String#as_string_slice) + [`as_string_slice()`](/mojo/stdlib/collections/string/String#as_string_slice) methods to `String` and `StringLiteral`. - Added `StringSlice` initializer from an `UnsafePointer` and a length in bytes. - Added a new - [`as_bytes_slice()`](/mojo/stdlib/builtin/string/String#as_bytes_slice) + [`as_bytes_slice()`](/mojo/stdlib/collections/string/String#as_bytes_slice) method to `String` and `StringLiteral`, which returns a `Span` of the bytes owned by the string. @@ -1257,7 +1384,7 @@ Big themes for this release: [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) and unsigned byte type for strings: - Renamed `String._as_ptr()` to - [`String.unsafe_ptr()`](/mojo/stdlib/builtin/string/String#unsafe_ptr), + [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr), and changed return type to `UnsafePointer` (was `DTypePointer`). - Renamed `StringLiteral.data()` to [`StringLiteral.unsafe_ptr()`](/mojo/stdlib/builtin/string_literal/StringLiteral#unsafe_ptr), @@ -1291,14 +1418,16 @@ Big themes for this release: string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is specified, the string will be parsed as if it was an integer literal, with the base determined by whether the string contains the prefix `"0x"`, - `"0o"`, or `"0b"`. ([PR #2273](https://github.com/modularml/mojo/pull/2273), + `"0o"`, or `"0b"`. + ([PR #2273](https://github.com/modularml/mojo/pull/2273), fixes [#2274](https://github.com/modularml/mojo/issues/2274)) - Added the [`bin()`](/mojo/stdlib/builtin/format_int/bin) built-in function to convert integral types into their binary - string representation. ([PR #2603](https://github.com/modularml/mojo/pull/2603)) + string representation. + ([PR #2603](https://github.com/modularml/mojo/pull/2603)) - - Added the [`atof()`](/mojo/stdlib/builtin/string/atof) built-in function, + - Added the [`atof()`](/mojo/stdlib/collections/string/atof) built-in function, which can convert a `String` to a `float64`. ([PR #2649](https://github.com/modularml/mojo/pull/2649)) @@ -2055,17 +2184,17 @@ Special thanks to our community contributors: initialized with all zeros. This provides an easy way to fill in the data of a tensor. -- [`String`](/mojo/stdlib/builtin/string/String) now has `removeprefix()` and - `removesuffix()` methods. +- [`String`](/mojo/stdlib/collections/string/String) now has `removeprefix()` + and `removesuffix()` methods. ([@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) -- The [`ord`](/mojo/stdlib/builtin/string/ord) and - [`chr`](/mojo/stdlib/builtin/string/chr) functions have been improved to +- The [`ord`](/mojo/stdlib/collections/string/ord) and + [`chr`](/mojo/stdlib/collections/string/chr) functions have been improved to accept any Unicode character. ([@mzaks](https://github.com/mzaks), contributes towards [#1616](https://github.com/modularml/mojo/issues/1616)) -- [`atol()`](/mojo/stdlib/builtin/string/atol) now handles whitespace. The +- [`atol()`](/mojo/stdlib/collections/string/atol) now handles whitespace. The `atol()`function is used internally by `String.__int__()`, so `int(String( " 10 "))` now returns `10` instead of raising an error. ([@artemiogr97](https://github.com/artemiogr97)) @@ -3137,7 +3266,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. now returns a [`Reference`](/mojo/stdlib/memory/reference/Reference) to the value rather than a copy. -- The [`String`](/mojo/stdlib/builtin/string/String) methods `tolower()` +- The [`String`](/mojo/stdlib/collections/string/String) methods `tolower()` and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. @@ -3632,9 +3761,9 @@ experience without dedicated sugar. [`Movable`](/mojo/stdlib/builtin/value/Movable) and [`Copyable`](/mojo/stdlib/builtin/value/Copyable) built-in traits. -- [`String`](/mojo/stdlib/builtin/string/String) now has new - [`toupper()`](/mojo/stdlib/builtin/string/String#upper) and - [`tolower()`](/mojo/stdlib/builtin/string/String#lower) methods analogous, +- [`String`](/mojo/stdlib/collections/string/String) now has new + [`toupper()`](/mojo/stdlib/collections/string/String#upper) and + [`tolower()`](/mojo/stdlib/collections/string/String#lower) methods analogous, respectively, to Python's `str.toupper()` and `str.tolower()`. - Added a [`hash()`](/mojo/stdlib/builtin/hash/hash) built-in function and @@ -3978,11 +4107,11 @@ the previous "read to EOF" behavior when size is negative. - `file.FileHandle` now has a `seek()` method. -- [`String`](/mojo/stdlib/builtin/string/String) now has an - [`rfind()`](/mojo/stdlib/builtin/string/String#rfind) method analogous to +- [`String`](/mojo/stdlib/collections/string/String) now has an + [`rfind()`](/mojo/stdlib/collections/string/String#rfind) method analogous to Python's `str.rfind()`. -- `String` now has an [`split()`](/mojo/stdlib/builtin/string/String#split) +- `String` now has an [`split()`](/mojo/stdlib/collections/string/String#split) method analogous to Python's `str.split()`. - [`Path`](/mojo/stdlib/pathlib/path/Path) now has a @@ -4290,7 +4419,7 @@ the previous "read to EOF" behavior when size is negative. and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. -- The [`String`](/mojo/stdlib/builtin/string/String) type now has the +- The [`String`](/mojo/stdlib/collections/string/String) type now has the `count()` and `find()` methods to enable counting the number of occurrences or finding the offset index of a substring in a string. From c530573b1391b2b7e6b4b875f12d5aeead8d8219 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:14:26 -0700 Subject: [PATCH 1507/2019] In v24.5, the `String` type was moved from the `builtin.string` module to the `collections.string` module. This PR updates all links to the String API reference that appear in the documentation. Additionally, The `load()` and `store()` methods from `SIMD` were moved to `UnsafePointer`. This PR updates the Pointers section of the documentation to reflect this change. Finally, this highlights in the changelog that Python 3.12 is now supported. MODULAR_ORIG_COMMIT_REV_ID: d01800846252f4b7dd91b527f153fb264f791005 --- docs/changelog-released.md | 4 ++++ docs/manual/lifecycle/index.ipynb | 2 +- docs/manual/lifecycle/life.ipynb | 2 +- docs/manual/pointers.ipynb | 13 +++++-------- docs/manual/types.ipynb | 8 ++++---- stdlib/src/builtin/str.mojo | 4 ++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 21b233e628..a981b6198b 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -70,6 +70,8 @@ tool beyond the 24.5 release. You must now use [Magic](/magic/) or ### ✨ Highlights +- Mojo now supports Python 3.12 interoperability. + - The set of automatically imported entities (types, aliases, functions) into users' Mojo programs has been dramatically reduced. This can break existing user code as users will need to explicitly import what they're using for cases @@ -636,6 +638,8 @@ tool beyond the 24.5 release. You must now use [Magic](/magic/) or - Python interoperability changes: + - Mojo now supports Python 3.12 interoperability. + - Creating a nested [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) from a list or tuple of Python objects is possible now: diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 39f645eca4..fca51f7fbc 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -30,7 +30,7 @@ "Mojo also has no built-in data types with special privileges. All data types\n", "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", + "[`String`](/mojo/stdlib/collections/string/String)) are implemented as\n", "[structs](/mojo/manual/structs). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index c6140e7ef3..d6f4f860fc 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -28,7 +28,7 @@ "All data types in Mojo—including basic types in the standard library such as\n", "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", + "[`String`](/mojo/stdlib/collections/string/String), up to complex types such\n", "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", "[struct](/mojo/manual/structs). This means the creation and\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index a302461d66..4c8f74c4f3 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -620,14 +620,11 @@ "source": [ "## Working with SIMD vectors\n", "\n", - "The standard library provides a few special methods for loading and storing SIMD\n", - "values. The `SIMD` type includes static \n", - "[`load()`](/mojo/stdlib/builtin/simd/SIMD#load) and \n", - "[`store()`](/mojo/stdlib/builtin/simd/SIMD#store) methods for performing aligned\n", - "loads and stores of scalar values.\n", - "\n", - "The `UnsafePointer` type includes various methods of loading and storing SIMD\n", - "values to memory. In particular: strided load/store and gather/scatter.\n", + "The `UnsafePointer` type includes\n", + "[`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and\n", + "[`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods for\n", + "performing aligned loads and stores of scalar values. It also has methods\n", + "supporting strided load/store and gather/scatter.\n", "\n", "Strided load loads values from memory into a SIMD vector using an offset (the\n", "\"stride\") between successive memory addresses. This can be useful for\n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index b8630ad8d7..94690bd6d1 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -32,10 +32,10 @@ "\n", "Mojo comes with a standard library that provides a number of useful types and\n", "utility functions. These standard types aren’t privileged. Each of the standard\n", - "library types is defined just like user-defined types—even basic types like \n", - "[`Int`](/mojo/stdlib/builtin/int/Int) and \n", - "[`String`](/mojo/stdlib/builtin/string/String). But these standard library types\n", - "are the building blocks you'll use for most Mojo programs.\n", + "library types is defined just like user-defined types—even basic types like\n", + "[`Int`](/mojo/stdlib/builtin/int/Int) and\n", + "[`String`](/mojo/stdlib/collections/string/String). But these standard library\n", + "types are the building blocks you'll use for most Mojo programs.\n", "\n", "The most common types are _built-in types_, which are always available and\n", "don't need to be imported. These include types for numeric values, strings,\n", diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index d5eea357af..93f1482cf2 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -23,7 +23,7 @@ These are Mojo built-ins, so you don't need to import them. trait Stringable: """ The `Stringable` trait describes a type that can be converted to a - [`String`](/mojo/stdlib/builtin/string/String). + [`String`](/mojo/stdlib/collections/string/String). Any type that conforms to `Stringable` or [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) works @@ -77,7 +77,7 @@ trait Stringable: trait StringableRaising: """The StringableRaising trait describes a type that can be converted to a - [`String`](/mojo/stdlib/builtin/string/String). + [`String`](/mojo/stdlib/collections/string/String). Any type that conforms to [`Stringable`](/mojo/stdlib/builtin/str/Stringable) or From b1186e767e648adedb1b40f3f49642fc934651fc Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 6 Sep 2024 05:36:06 -0700 Subject: [PATCH 1508/2019] [Stdlib] Move the stepvector intrinsic out of the llvm.experimental namespace MODULAR_ORIG_COMMIT_REV_ID: 9564195d758265a6f92e800591ecf67e7316727b --- stdlib/src/math/math.mojo | 4 ++-- stdlib/src/memory/memory.mojo | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 3beb80e9c7..3a1634b8cd 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1080,14 +1080,14 @@ fn iota[ return offset elif type.is_integral(): var step = llvm_intrinsic[ - "llvm.experimental.stepvector", + "llvm.stepvector", SIMD[type, simd_width], has_side_effect=False, ]() return step + offset else: var it = llvm_intrinsic[ - "llvm.experimental.stepvector", + "llvm.stepvector", SIMD[DType.index, simd_width], has_side_effect=False, ]() diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 844ee99570..49d14e175e 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -61,7 +61,7 @@ fn _memcmp_impl_unconstrained[ return 0 var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", + "llvm.stepvector", SIMD[DType.uint8, simd_width], has_side_effect=False, ]() From 43c0ce15cfb2409ce1be2b006eea2f1a670008e9 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 6 Sep 2024 05:59:20 -0700 Subject: [PATCH 1509/2019] [Stdlib] Simplify and optimize the rand functions MODULAR_ORIG_COMMIT_REV_ID: 2dacd98004b4499c2d23d7e8dc3e6b186858e90f --- stdlib/src/random/random.mojo | 59 ++++++++++------------------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index fb28af5f18..58bd5967d1 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -24,6 +24,8 @@ from time import perf_counter_ns from collections import Optional from memory import UnsafePointer +from math import floor +import math fn _get_random_state() -> UnsafePointer[NoneType]: @@ -59,9 +61,7 @@ fn random_float64(min: Float64 = 0, max: Float64 = 1) -> Float64: Returns: A random number from the specified range. """ - return external_call["KGEN_CompilerRT_RandomDouble", Float64]( - _get_random_state(), min, max - ) + return external_call["KGEN_CompilerRT_RandomDouble", Float64](min, max) fn random_si64(min: Int64, max: Int64) -> Int64: @@ -74,9 +74,7 @@ fn random_si64(min: Int64, max: Int64) -> Int64: Returns: A random number from the specified range. """ - return external_call["KGEN_CompilerRT_RandomSInt64", Int64]( - _get_random_state(), min, max - ) + return external_call["KGEN_CompilerRT_RandomSInt64", Int64](min, max) fn random_ui64(min: UInt64, max: UInt64) -> UInt64: @@ -89,9 +87,7 @@ fn random_ui64(min: UInt64, max: UInt64) -> UInt64: Returns: A random number from the specified range. """ - return external_call["KGEN_CompilerRT_RandomUInt64", UInt64]( - _get_random_state(), min, max - ) + return external_call["KGEN_CompilerRT_RandomUInt64", UInt64](min, max) fn randint[ @@ -125,7 +121,7 @@ fn randint[ fn rand[ type: DType ]( - ptr: UnsafePointer[Scalar[type]], + ptr: UnsafePointer[Scalar[type], *_], size: Int, /, *, @@ -145,8 +141,6 @@ fn rand[ max: The maximum value for random. int_scale: The scale for error checking (float type only). """ - alias bitwidth = bitwidthof[type]() - var scale_val = int_scale.or_else(-1) @parameter @@ -155,12 +149,7 @@ fn rand[ var scale_double: Float64 = (1 << scale_val) for i in range(size): var rnd = random_float64(min, max) - ptr[i] = ( - (rnd * scale_double) - .cast[DType.int64]() - .cast[DType.float64]() - / scale_double - ).cast[type]() + ptr[i] = (floor(rnd * scale_double) / scale_double).cast[type]() else: for i in range(size): var rnd = random_float64(min, max) @@ -168,39 +157,23 @@ fn rand[ return - @parameter - if type is DType.bool: - var min_: UInt64 = 0 if min < 0 else min.cast[DType.uint64]() - var max_: UInt64 = (1 << bitwidth) - 1 - max_ = ( - max.cast[DType.uint64]() if max.cast[DType.uint64]() - < max_ else max_ - ) - for i in range(size): - ptr[i] = random_ui64(min_, max_).cast[type]() - return - @parameter if type.is_signed(): - var min_: Int64 = -(1 << (bitwidth - 1)) - min_ = ( - min.cast[DType.int64]() if min.cast[DType.int64]() > min_ else min_ + var min_ = math.max( + Scalar[type].MIN.cast[DType.int64](), min.cast[DType.int64]() ) - var max_: Int64 = (1 << (bitwidth - 1)) - 1 - max_ = ( - max.cast[DType.int64]() if max.cast[DType.int64]() < max_ else max_ + var max_ = math.min( + max.cast[DType.int64](), Scalar[type].MAX.cast[DType.int64]() ) for i in range(size): ptr[i] = random_si64(min_, max_).cast[type]() return @parameter - if type.is_unsigned(): - var min_: UInt64 = 0 if min < 0 else min.cast[DType.uint64]() - var max_: UInt64 = (1 << bitwidth) - 1 - max_ = ( - max.cast[DType.uint64]() if max.cast[DType.uint64]() - < max_ else max_ + if type is DType.bool or type.is_unsigned(): + var min_ = math.max(0, min.cast[DType.uint64]()) + var max_ = math.min( + max.cast[DType.uint64](), Scalar[type].MAX.cast[DType.uint64]() ) for i in range(size): ptr[i] = random_ui64(min_, max_).cast[type]() @@ -218,7 +191,7 @@ fn randn_float64(mean: Float64 = 0.0, variance: Float64 = 1.0) -> Float64: A random float64 sampled from Normal(mean, variance). """ return external_call["KGEN_CompilerRT_NormalDouble", Float64]( - _get_random_state(), mean, variance + mean, variance ) From 4b741eb211a95528dd213a34d1a7c859773ccfd4 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 6 Sep 2024 11:13:08 -0500 Subject: [PATCH 1510/2019] [stdlib] feat: Add DLHandle.get_symbol() and simplify CPython.Py_None Previously CPython.Py_None was getting the `None` PyObject ptr using the indirect workaround of calling an arbitrary Python function that returned None. Now we can just reference the static symbol that contains the imortal `None` instance directly. MODULAR_ORIG_COMMIT_REV_ID: 5cd4b51d3ebb23a4bdc951f77052f7b70a7782e9 --- docs/changelog.md | 6 +++- stdlib/src/python/_cpython.mojo | 32 ++++++++--------- stdlib/src/python/python.mojo | 4 +++ stdlib/src/sys/ffi.mojo | 63 ++++++++++++++++++++++++++------- 4 files changed, 75 insertions(+), 30 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 68adeaeca3..1171ada6fb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -35,9 +35,13 @@ what we publish. var f = float(Int32(45)) ``` - + ([PR #3163](https://github.com/modularml/mojo/pull/3163) by [@bgreni](https://github.com/bgreni)) +- Add `DLHandle.get_symbol()`, for getting a pointer to a symbol in a dynamic + library. This is more general purpose than the existing methods for getting + function pointers. + ### 🦋 Changed - A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 4577c01b11..73ac413667 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from collections import InlineArray -from os import getenv, setenv +from os import getenv, setenv, abort from os.path import dirname from pathlib import Path from sys import external_call @@ -402,14 +402,13 @@ struct PyModuleDef(Stringable, Representable, Formattable): struct CPython: var lib: DLHandle - var none_value: PyObjectPtr var dict_type: PyObjectPtr var logging_enabled: Bool var version: PythonVersion var total_ref_count: UnsafePointer[Int] var init_error: StringRef - fn __init__(inout self: CPython): + fn __init__(inout self): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") @@ -445,7 +444,6 @@ struct CPython: self.lib = DLHandle(python_lib) self.total_ref_count = UnsafePointer[Int].alloc(1) - self.none_value = PyObjectPtr() self.dict_type = PyObjectPtr() self.logging_enabled = logging_enabled if not self.init_error: @@ -460,7 +458,6 @@ struct CPython: @staticmethod fn destroy(inout existing: CPython): - existing.Py_DecRef(existing.none_value) if existing.logging_enabled: print("CPython destroy") var remaining_refs = existing.total_ref_count.take_pointee() @@ -494,23 +491,26 @@ struct CPython: fn Py_None(inout self) -> PyObjectPtr: """Get a None value, of type NoneType.""" - if self.none_value.is_null(): - var list_obj = self.PyList_New(0) - var tuple_obj = self.PyTuple_New(0) - var callable_obj = self.PyObject_GetAttrString(list_obj, "reverse") - self.none_value = self.PyObject_CallObject(callable_obj, tuple_obj) - self.Py_DecRef(tuple_obj) - self.Py_DecRef(callable_obj) - self.Py_DecRef(list_obj) - return self.none_value + + # Get pointer to the immortal `None` PyObject struct instance. + # Note: + # The name of this global is technical a private part of the + # CPython API, but unfortunately the only stable ways to access it are + # macros. + var ptr = self.lib.get_symbol[Int8]( + "_Py_NoneStruct", + ) + + if not ptr: + abort("error: unable to get pointer to CPython `None` struct") + + return PyObjectPtr(ptr) fn __del__(owned self): pass fn __copyinit__(inout self, existing: Self): self.lib = existing.lib - # None is a global variable - self.none_value = existing.none_value self.dict_type = existing.dict_type self.logging_enabled = existing.logging_enabled self.version = existing.version diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 851d40cc1d..dbd6f1ed02 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -69,6 +69,10 @@ struct Python: var impl: _PythonInterfaceImpl """The underlying implementation of Mojo's Python interface.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): """Default constructor.""" self.impl = _get_global_python_itf() diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 699e818ceb..e2903a28b6 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -212,20 +212,13 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): Returns: A handle to the function. """ - debug_assert(self.handle, "Dylib handle is null") + var opaque_function_ptr = self.get_symbol[NoneType](name) - @parameter - if not os_is_windows(): - var opaque_function_ptr = external_call[ - "dlsym", UnsafePointer[Int8] - ](self.handle.address, name) - var result = UnsafePointer.address_of(opaque_function_ptr).bitcast[ - result_type - ]()[] - _ = opaque_function_ptr - return result - else: - return abort[result_type]("get_function isn't supported on windows") + var result = UnsafePointer.address_of(opaque_function_ptr).bitcast[ + result_type + ]()[] + _ = opaque_function_ptr + return result @always_inline fn _get_function[ @@ -244,6 +237,50 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): return self._get_function[result_type](func_name.unsafe_cstr_ptr()) + fn get_symbol[ + result_type: AnyType, + ](self, name: StringLiteral) -> UnsafePointer[result_type]: + """Returns a pointer to the symbol with the given name in the dynamic + library. + + Parameters: + result_type: The type of the symbol to return. + + Args: + name: The name of the symbol to get the handle for. + + Returns: + A pointer to the symbol. + """ + return self.get_symbol[result_type](name.unsafe_cstr_ptr()) + + fn get_symbol[ + result_type: AnyType + ](self, name: UnsafePointer[Int8]) -> UnsafePointer[result_type]: + """Returns a pointer to the symbol with the given name in the dynamic + library. + + Parameters: + result_type: The type of the symbol to return. + + Args: + name: The name of the symbol to get the handle for. + + Returns: + A pointer to the symbol. + """ + debug_assert(self.handle, "Dylib handle is null") + + @parameter + if not os_is_windows(): + return external_call["dlsym", UnsafePointer[result_type]]( + self.handle.address, name + ) + else: + return abort[UnsafePointer[result_type]]( + "get_symbol isn't supported on windows" + ) + # ===----------------------------------------------------------------------===# # Library Load From 99fe258c3b8112e5d19433cf9900c9ad0f13c6d5 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 6 Sep 2024 12:38:06 -0500 Subject: [PATCH 1511/2019] [stdlib] feat: Use new TypedPythonObject to add static type safety to module operations * Add TypedPythonObject, an initial attempt at a light-weight way of "tagging" PythonObject values with static type information. * Add PythonObject.unsafe_as_py_object_ptr() * Incidentally flesh out the style-guide.md examples of operator dunder methods MODULAR_ORIG_COMMIT_REV_ID: 0538f0b1d625f2e4e09712325288d07b6f61df86 --- docs/changelog.md | 4 + stdlib/docs/style-guide.md | 12 ++- stdlib/src/python/__init__.mojo | 2 +- stdlib/src/python/python.mojo | 27 ++++-- stdlib/src/python/python_object.mojo | 119 +++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 8 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1171ada6fb..26bbf848a9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -42,6 +42,10 @@ what we publish. library. This is more general purpose than the existing methods for getting function pointers. +- Introduce `TypedPythonObject` as a light-weight way to annotate `PythonObject` + values with static type information. This design will likely evolve and + chagne significantly. + ### 🦋 Changed - A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 43de2f4bad..2ed8ca349f 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -152,10 +152,18 @@ struct MyStruct(Sized, Stringable): # Operator dunders # ===-------------------------------------------------------------------===# - # Basically anything that "backs" special syntax: [..], *, +, /, //, etc... + # Anything that "backs" special syntax: [..], *, +, /, //, etc... fn __getitem__ - fn __getitem__ + fn __setitem__ + + fn __getattr__ + fn __setattr__ + + fn __iter__ # `for x in self` + fn __next__ + fn __contains__ # `x in self` + fn __is__ # `x is self` fn __add__ fn __iadd__ diff --git a/stdlib/src/python/__init__.mojo b/stdlib/src/python/__init__.mojo index d541bc1ca3..c91813320c 100644 --- a/stdlib/src/python/__init__.mojo +++ b/stdlib/src/python/__init__.mojo @@ -12,5 +12,5 @@ # ===----------------------------------------------------------------------=== # """Implements the python package.""" -from .python_object import PythonObject +from .python_object import PythonObject, TypedPythonObject from .python import Python diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index dbd6f1ed02..ce986b0789 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -29,7 +29,7 @@ from memory import UnsafePointer from utils import StringRef from ._cpython import CPython, Py_eval_input, Py_file_input, PyMethodDef -from .python_object import PythonObject +from .python_object import PythonObject, TypedPythonObject fn _init_global(ignored: UnsafePointer[NoneType]) -> UnsafePointer[NoneType]: @@ -181,6 +181,11 @@ struct Python: var directory: PythonObject = dir_path _ = sys.path.append(directory) + # ===-------------------------------------------------------------------===# + # PythonObject "Module" Operations + # ===-------------------------------------------------------------------===# + + # TODO(MSTDL-880): Change this to return `TypedPythonObject["Module"]` @staticmethod fn import_module(module: StringRef) raises -> PythonObject: """Imports a Python module. @@ -212,7 +217,7 @@ struct Python: return PythonObject(module_maybe) @staticmethod - fn create_module(name: String) raises -> PythonObject: + fn create_module(name: String) raises -> TypedPythonObject["Module"]: """Creates a Python module using the provided name. Inspired by https://github.com/pybind/pybind11/blob/a1d00916b26b187e583f3bce39cd59c3b0652c32/include/pybind11/pybind11.h#L1227 @@ -239,11 +244,14 @@ struct Python: # This is cargo copy-pasted from other methods in this file essentially. Python.throw_python_exception_if_error_state(cpython) - return PythonObject(module) + return TypedPythonObject["Module"]( + unsafe_unchecked_from=PythonObject(module) + ) @staticmethod fn add_methods( - module: PythonObject, functions: UnsafePointer[PyMethodDef] + inout module: TypedPythonObject["Module"], + functions: UnsafePointer[PyMethodDef], ) -> Int: """Adds methods to a PyModule object. @@ -256,7 +264,16 @@ struct Python: """ var cpython = _get_global_python_itf().cpython() - return cpython.PyModule_AddFunctions(module.py_object, functions) + return cpython.PyModule_AddFunctions( + # Safety: `module` pointer lives long enough because its reference + # argument. + module.unsafe_as_py_object_ptr(), + functions, + ) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# @staticmethod fn dict() -> PythonObject: diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index ca5dec2a2c..7df8934d64 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -31,6 +31,10 @@ from .python import Python, _get_global_python_itf struct _PyIter(Sized): """A Python iterator.""" + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + var iterator: PythonObject """The iterator object that stores location.""" var preparedNextItem: PythonObject @@ -38,6 +42,10 @@ struct _PyIter(Sized): var isDone: Bool """Stores True if the iterator is pointing to the last item.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __copyinit__(inout self, existing: Self): """Copy another iterator. @@ -70,6 +78,10 @@ struct _PyIter(Sized): self.isDone = True self.preparedNextItem = PythonObject(PyObjectPtr()) + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + fn __next__(inout self: _PyIter) -> PythonObject: """Return the next item and update to point to subsequent item. @@ -100,6 +112,67 @@ struct _PyIter(Sized): return 1 +@register_passable +struct TypedPythonObject[type_hint: StringLiteral]: + """A wrapper around `PythonObject` that indicates the type of the contained + object. + + The PythonObject structure is entirely dynamically typed. This type provides + a weak layer of optional static typing. + + Parameters: + type_hint: The type name hint indicating the static type of this + object. + """ + + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var _obj: PythonObject + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + + fn __init__(inout self, *, owned unsafe_unchecked_from: PythonObject): + """Construct a TypedPythonObject without any validation that the given + object is of the specified hinted type. + + Args: + unsafe_unchecked_from: The PythonObject to construct from. This + will not be type checked. + """ + self._obj = unsafe_unchecked_from^ + + fn __copyinit__(inout self, other: Self): + """Copy an instance of this type. + + Args: + other: The value to copy. + """ + self._obj = other._obj + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + # TODO: + # This should have lifetime, or we should do this with a context + # manager, to prevent use after ASAP destruction. + fn unsafe_as_py_object_ptr(inout self) -> PyObjectPtr: + """Get the underlying PyObject pointer. + + Returns: + The underlying PyObject pointer. + + Safety: + Use-after-free: The caller must take care that `self` outlives the + usage of the pointer returned by this function. + """ + return self._obj.unsafe_as_py_object_ptr() + + @register_passable struct PythonObject( ImplicitlyBoolable, @@ -112,9 +185,17 @@ struct PythonObject( ): """A Python object.""" + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + var py_object: PyObjectPtr """A pointer to the underlying Python object.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): """Initialize the object with a `None` value.""" self.__init__(None) @@ -137,6 +218,24 @@ struct PythonObject( """ self.py_object = ptr + fn __init__(inout self, owned typed_obj: TypedPythonObject[_]): + """Construct a PythonObject from a typed object, dropping the type hint + information. + + This is a no-op at runtime. The only information that is lost is static + type information. + + Args: + typed_obj: The typed python object to unwrap. + """ + + # Note: Mark `typed_obj` as destroyed so we can move out of its field. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(typed_obj) + ) + + self = typed_obj._obj^ + # TODO(MSTDL-715): # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @@ -345,6 +444,10 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() cpython.Py_IncRef(self.py_object) + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + fn __iter__(self) raises -> _PyIter: """Iterate over the object. @@ -359,6 +462,22 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return _PyIter(PythonObject(iter)) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn unsafe_as_py_object_ptr(inout self) -> PyObjectPtr: + """Get the underlying PyObject pointer. + + Returns: + The underlying PyObject pointer. + + Safety: + Use-after-free: The caller must take care that `self` outlives the + usage of the pointer returned by this function. + """ + return self.py_object + fn steal_data(owned self) -> PyObjectPtr: """Take ownership of the underlying pointer from the Python object. From 175c6a0685bef06c18b72c299bb6bb3e9a0a4f09 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 7 Sep 2024 15:35:24 -0700 Subject: [PATCH 1512/2019] [mojo-lang] Make field sensitive lifetimes refuse to collapse. This changes lifetime tracking to consider "x.y" a distinct element from "x" and not collapse it. The rationale for this is that (with the previous behavior) we have two different kinds of subfield references, "x.y" could be an use of "the y field of x" or it could be a use of "the y field of something embedded in x" which are different. It is possible that we might want to enable something like this as sugar in the future, but that would require introducing syntax (e.g. "x.y" vs "x.~y") or whatever, and I'd prefer to keep the model simple, rather than adding frills. This makes use of the previous patch's extension to make typeof and lifetimeof much more general, which makes it possible to do this without massive suffering :-) MODULAR_ORIG_COMMIT_REV_ID: 63d33858d7f797a1537ba1bf58fc4abe2ed26347 --- stdlib/src/builtin/builtin_list.mojo | 3 +-- stdlib/src/collections/dict.mojo | 16 +++++++++++----- stdlib/src/collections/inline_list.mojo | 4 ++-- stdlib/src/collections/optional.mojo | 4 ++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index f7839ba427..3dd0317b06 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -246,7 +246,7 @@ struct _VariadicListMemIter[ # Helper to compute the union of two lifetimes: # TODO: parametric aliases would be nice. struct _lit_lifetime_union[ - is_mutable: Bool, + is_mutable: Bool, //, a: AnyLifetime[is_mutable].type, b: AnyLifetime[is_mutable].type, ]: @@ -415,7 +415,6 @@ struct VariadicListMem[ self, idx: Int ) -> ref [ _lit_lifetime_union[ - elt_is_mutable, lifetime, # cast mutability of self to match the mutability of the element, # since that is what we want to use in the ultimate reference and diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index f471c71046..20b6b839c4 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -80,7 +80,11 @@ struct _DictEntryIter[ return self @always_inline - fn __next__(inout self) -> Reference[DictEntry[K, V], dict_lifetime]: + fn __next__( + inout self, + ) -> Reference[ + DictEntry[K, V], __lifetime_of(self.src[]._entries[0].value()) + ]: while True: var opt_entry_ref = Reference(self.src[]._entries[self.index]) @@ -123,7 +127,9 @@ struct _DictKeyIter[ fn __iter__(self) -> Self: return self - fn __next__(inout self) -> Reference[K, dict_lifetime]: + fn __next__( + inout self, + ) -> Reference[K, __lifetime_of(self.iter.__next__()[].key)]: return self.iter.__next__()[].key fn __len__(self) -> Int: @@ -729,7 +735,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MOCO-604): Return Optional[Reference] instead of raising fn _find_ref( ref [_]self: Self, key: K - ) raises -> ref [__lifetime_of(self)] Self.V: + ) raises -> ref [__lifetime_of(self._entries[0].value().value)] Self.V: """Find a value in the dictionary by key. Args: @@ -898,7 +904,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn setdefault( inout self, key: K, owned default: V - ) raises -> Reference[V, __lifetime_of(self)]: + ) raises -> Reference[V, __lifetime_of(self._entries[0].value().value)]: """Get a value from the dictionary by key, or set it to a default if it doesn't exist. Args: @@ -1197,7 +1203,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ # TODO(#36448): Use this instead of the current workaround # return self._dict.keys() - return Self.__iter__(self) + return self.__iter__() fn values( ref [_]self: Self, diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index c1b26d8f00..a273d215d6 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -54,7 +54,7 @@ struct _InlineListIter[ fn __next__( inout self, - ) -> Reference[T, list_lifetime]: + ) -> Reference[T, __lifetime_of(self.src[][0])]: @parameter if forward: self.index += 1 @@ -129,7 +129,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __getitem__( ref [_]self: Self, owned idx: Int - ) -> ref [__lifetime_of(self)] Self.ElementType: + ) -> ref [__lifetime_of(self._array)] Self.ElementType: """Get a `Reference` to the element at the given index. Args: diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index ad7e0cf60a..9ba502ba5e 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -313,7 +313,7 @@ struct Optional[T: CollectionElement]( # ===-------------------------------------------------------------------===# @always_inline - fn value(ref [_]self: Self) -> ref [__lifetime_of(self)] T: + fn value(ref [_]self: Self) -> ref [__lifetime_of(self._value)] T: """Retrieve a reference to the value of the Optional. This check to see if the optional contains a value. @@ -330,7 +330,7 @@ struct Optional[T: CollectionElement]( return self.unsafe_value() @always_inline - fn unsafe_value(ref [_]self: Self) -> ref [__lifetime_of(self)] T: + fn unsafe_value(ref [_]self: Self) -> ref [__lifetime_of(self._value)] T: """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. From e2e08644f3052249e3cad76ba70a93d91c6774db Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 7 Sep 2024 16:09:37 -0700 Subject: [PATCH 1513/2019] [mojo-lang] Make lifetimeof/typeof work with throwing calls. This enhances typeof/lifetime of to be able to evaluate the results of throwing expressions even in non-throwing contexts. This is fine given they aren't actually evaluating the expression. MODULAR_ORIG_COMMIT_REV_ID: b42db2de09a52fb99d199973fc9377ff83c6c559 --- docs/changelog.md | 9 ++++++++- stdlib/src/collections/dict.mojo | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 26bbf848a9..497869e2f3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -44,7 +44,14 @@ what we publish. - Introduce `TypedPythonObject` as a light-weight way to annotate `PythonObject` values with static type information. This design will likely evolve and - chagne significantly. + change significantly. + +- The `__type_of(x)` and `__lifetime_of(x)` operators are much more general now: + they allow arbitrary expressions inside of them, allow referring to dynamic + values in parameter contexts, and even allow referring to raising functions + in non-raising contexts. These operations never evaluate their expression, so + any side effects that occur in the expression are never evaluated at runtime, + eliminating concerns about `__type_of(expensive())` being a problem. ### 🦋 Changed diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 20b6b839c4..dce48b97b3 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -904,7 +904,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn setdefault( inout self, key: K, owned default: V - ) raises -> Reference[V, __lifetime_of(self._entries[0].value().value)]: + ) raises -> Reference[V, __lifetime_of(self._find_ref(key))]: """Get a value from the dictionary by key, or set it to a default if it doesn't exist. Args: From 9cf124f0286592059a986b4e9d100b186b5d985c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 7 Sep 2024 16:54:14 -0700 Subject: [PATCH 1514/2019] [mojo-stdlib] Clean up dict a bit. Mojo has made a lot of progress, so we can remove some old todo's and simplify the code. MODULAR_ORIG_COMMIT_REV_ID: eef6b298a14f7b747689a00fcd6618aa018d452f --- stdlib/src/collections/dict.mojo | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index dce48b97b3..164d5ebb25 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -1189,9 +1189,7 @@ struct OwnedKwargsDict[V: CollectionElement]( Returns: An iterator of immutable references to the dictionary keys. """ - # TODO(#36448): Use this instead of the current workaround - # return self._dict.__iter__() - return _DictKeyIter(_DictEntryIter(0, 0, self._dict)) + return self._dict.keys() fn keys( ref [_]self: Self, @@ -1201,9 +1199,7 @@ struct OwnedKwargsDict[V: CollectionElement]( Returns: An iterator of immutable references to the dictionary keys. """ - # TODO(#36448): Use this instead of the current workaround - # return self._dict.keys() - return self.__iter__() + return self._dict.keys() fn values( ref [_]self: Self, @@ -1213,9 +1209,7 @@ struct OwnedKwargsDict[V: CollectionElement]( Returns: An iterator of references to the dictionary values. """ - # TODO(#36448): Use this instead of the current workaround - # return self._dict.values() - return _DictValueIter(_DictEntryIter(0, 0, self._dict)) + return self._dict.values() fn items( ref [_]self: Self, From a70e5f610c2fc21446926797376b98cb22587df2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 8 Sep 2024 11:39:48 -0700 Subject: [PATCH 1515/2019] The `#lit.lifetime` attribute was added early in the development of Mojo lifetimes, representing a singleton lifetime used for lowering. As things evolved, it got confused semantics: is it the "everything" lifetime, or the "nothing" lifetime, or the "global" lifetime? Mojo is all over the place on this. This leads to a number of complications and some weird behavior, and we need to clarify this. This patch takes a step forward on this, renaming it to `#lit.any.lifetime`, specifying that it can alias any other lifetime, and fixing our subtyping rules around lifetime union collapsing etc. As part of this, we make a tiny user visible change to diagnostics, and introduce new `ImmutableAnyLifetime` and `MutableAnyLifetime` which more precisely capture these effects. This retains `*utableStaticLifetime` for now, but its behavior should be refined with an "actually globally static" lifetime some day. This patch mostly rearranges things without changing behavior in a significant way. Notably, this still allows implicit conversion from references of "any" lifetime to a specific lifetime, even though that isn't correct. This will be refined more in the subsequent steps. MODULAR_ORIG_COMMIT_REV_ID: 069dce44898815cf476bafb826396f5f4193699f --- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 8 ++++---- stdlib/src/builtin/type_aliases.mojo | 13 ++++++++++--- stdlib/src/memory/unsafe_pointer.mojo | 10 +++++----- stdlib/src/prelude/__init__.mojo | 2 ++ stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 12 ++++++------ 11 files changed, 33 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index dc1a1c3583..8b5b1dca21 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -321,7 +321,7 @@ fn _try_write_int[ # Construct a null-terminated buffer of single-byte char. var zero_buf = InlineArray[UInt8, 2](zero_char, 0) - var zero = StringSlice[ImmutableStaticLifetime]( + var zero = StringSlice[ImmutableAnyLifetime]( # TODO(MSTDL-720): # Support printing non-null-terminated strings on GPU and switch # back to this code without a workaround. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index b8c20c605d..49baa1a055 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -301,7 +301,7 @@ fn _float_repr[ fn _put(strref: StringRef, file: FileDescriptor = stdout): - var str_slice = StringSlice[ImmutableStaticLifetime]( + var str_slice = StringSlice[ImmutableAnyLifetime]( unsafe_from_utf8_strref=strref ) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 34c8b49245..51fe353fc6 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -314,7 +314,7 @@ struct StringLiteral( return self.unsafe_ptr().bitcast[C_char]() @always_inline - fn as_string_slice(self) -> StringSlice[ImmutableStaticLifetime]: + fn as_string_slice(self) -> StringSlice[ImmutableAnyLifetime]: """Returns a string slice of this static string literal. Returns: @@ -326,10 +326,10 @@ struct StringLiteral( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually # guaranteed to be valid. - return StringSlice[ImmutableStaticLifetime](unsafe_from_utf8=bytes) + return StringSlice[ImmutableAnyLifetime](unsafe_from_utf8=bytes) @always_inline - fn as_bytes_slice(self) -> Span[UInt8, ImmutableStaticLifetime]: + fn as_bytes_slice(self) -> Span[UInt8, ImmutableAnyLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -339,7 +339,7 @@ struct StringLiteral( var ptr = self.unsafe_ptr() - return Span[UInt8, ImmutableStaticLifetime]( + return Span[UInt8, ImmutableAnyLifetime]( unsafe_ptr=ptr, len=self.byte_length(), ) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 7ae2875e9b..69c58fe19e 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -24,17 +24,24 @@ alias ImmutableLifetime = __mlir_type.`!lit.lifetime<0>` alias MutableLifetime = __mlir_type.`!lit.lifetime<1>` """Mutable lifetime reference type.""" -alias ImmutableStaticLifetime = __mlir_attr.`#lit.lifetime<0>: !lit.lifetime<0>` +alias ImmutableAnyLifetime = __mlir_attr.`#lit.any.lifetime<0>: !lit.lifetime<0>` +"""The immutable lifetime that might access any memory value.""" + +alias MutableAnyLifetime = __mlir_attr.`#lit.any.lifetime<1>: !lit.lifetime<1>` +"""The mutable lifetime that might access any memory value.""" + +# TODO: We don't have a "static" lifetime to use yet, so we use Any. +alias ImmutableStaticLifetime = ImmutableAnyLifetime """The immutable lifetime that lasts for the entire duration of program execution.""" -alias MutableStaticLifetime = __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` +alias MutableStaticLifetime = MutableAnyLifetime """The mutable lifetime that lasts for the entire duration of program execution.""" alias LifetimeSet = __mlir_type.`!lit.lifetime.set` """A set of lifetime parameters.""" -# Helper to build !lit.lifetime type. +# Helper to build a value of !lit.lifetime type. # TODO: Should be a parametric alias. struct AnyLifetime[is_mutable: Bool]: """This represents a lifetime reference of potentially parametric type. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 8bc71edb23..67de68bad9 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -166,7 +166,7 @@ struct UnsafePointer[ @always_inline fn __getitem__( self, - ) -> ref [MutableStaticLifetime, address_space._value.value] T: + ) -> ref [MutableAnyLifetime, address_space._value.value] T: """Return a reference to the underlying data. Returns: @@ -174,9 +174,9 @@ struct UnsafePointer[ """ # We're unsafe, so we can have unsafe things. References we make have - # an immortal mutable lifetime, since we can't come up with a meaningful - # lifetime for them anyway. - alias _ref_type = Reference[T, MutableStaticLifetime, address_space] + # an 'any' mutable lifetime, since UnsafePointer is allowed to alias + # anything. + alias _ref_type = Reference[T, MutableAnyLifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( UnsafePointer[T, address_space, False](self).address @@ -202,7 +202,7 @@ struct UnsafePointer[ fn __getitem__[ IntLike: IntLike, // ](self, offset: IntLike) -> ref [ - MutableStaticLifetime, address_space._value.value + MutableAnyLifetime, address_space._value.value ] T: """Return a reference to the underlying data, offset by the given index. diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index fec77db82d..8d57e778e3 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -78,6 +78,8 @@ from builtin.type_aliases import ( AnyTrivialRegType, ImmutableLifetime, MutableLifetime, + ImmutableAnyLifetime, + MutableAnyLifetime, ImmutableStaticLifetime, MutableStaticLifetime, LifetimeSet, diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 58ab56bcfd..14a9ceb8fe 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -477,7 +477,7 @@ struct _FixedString[CAP: Int]( fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() - var str_slice = StringSlice[ImmutableStaticLifetime]( + var str_slice = StringSlice[ImmutableAnyLifetime]( unsafe_from_utf8_strref=strref ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 650f750653..addd3867b7 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -325,7 +325,7 @@ struct StringSlice[ @always_inline fn __init__( - inout self: StringSlice[ImmutableStaticLifetime], lit: StringLiteral + inout self: StringSlice[ImmutableAnyLifetime], lit: StringLiteral ): """Construct a new string slice from a string literal. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index bb61f6440c..4cbd323e97 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -382,7 +382,7 @@ struct StringRef( # SAFETY: # Safe because our use of this StringSlice does not outlive `self`. - var str_slice = StringSlice[ImmutableStaticLifetime]( + var str_slice = StringSlice[ImmutableAnyLifetime]( unsafe_from_utf8_strref=self ) diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index c9d97d4be6..67849b0f61 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -20,7 +20,7 @@ from utils import Span fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = UnsafePointer[SIMD[D, 1]].alloc(size) rand[D](p, size) - sort(Span[Scalar[D], MutableStaticLifetime](unsafe_ptr=p, len=size)) + sort(Span[Scalar[D], MutableAnyLifetime](unsafe_ptr=p, len=size)) for i in range(1, size - 1): if p[i] < p[i - 1]: print(name, "size:", size, "max:", max, "incorrect sort") diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 74e19889f7..1372ded023 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -118,7 +118,7 @@ fn test_heap_string_from_string_slice() raises: alias string_lit: StringLiteral = "Hello" alias static_str: StringSlice[ - ImmutableStaticLifetime + ImmutableAnyLifetime ] = string_lit.as_string_slice() alias heap_string = String(static_str) @@ -133,11 +133,11 @@ fn test_slice_len() raises: alias str4: StringLiteral = "12" alias str5: StringLiteral = "1" - alias slice1: StringSlice[ImmutableStaticLifetime] = str1.as_string_slice() - alias slice2: StringSlice[ImmutableStaticLifetime] = str2.as_string_slice() - alias slice3: StringSlice[ImmutableStaticLifetime] = str3.as_string_slice() - alias slice4: StringSlice[ImmutableStaticLifetime] = str4.as_string_slice() - alias slice5: StringSlice[ImmutableStaticLifetime] = str5.as_string_slice() + alias slice1: StringSlice[ImmutableAnyLifetime] = str1.as_string_slice() + alias slice2: StringSlice[ImmutableAnyLifetime] = str2.as_string_slice() + alias slice3: StringSlice[ImmutableAnyLifetime] = str3.as_string_slice() + alias slice4: StringSlice[ImmutableAnyLifetime] = str4.as_string_slice() + alias slice5: StringSlice[ImmutableAnyLifetime] = str5.as_string_slice() assert_equal(5, len(slice1)) assert_equal(4, len(slice2)) From 19de6e2afd869e469ab4db64b57f4077467f41ca Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 8 Sep 2024 12:54:16 -0700 Subject: [PATCH 1516/2019] [mojo-stdlib] Rename the `UnsafePointer.T` parameter to `type` This renames the `T` parameter to `UnsafePointer` to `type` to make it more consistent with other types in the standard library, to subsume the existing `type` alias, and because `UnsafePointer` has several methods with local `T` parameters that mean completely different things. MODULAR_ORIG_COMMIT_REV_ID: feabd4479528ba204b83c10fd5ab05681219bd18 --- stdlib/src/memory/unsafe_pointer.mojo | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 67de68bad9..fedd20b159 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -40,10 +40,10 @@ from memory.memory import _free, _malloc @register_passable("trivial") struct UnsafePointer[ - T: AnyType, + type: AnyType, address_space: AddressSpace = AddressSpace.GENERIC, exclusive: Bool = False, - alignment: Int = alignof[T]() if triple_is_nvidia_cuda() else 1, + alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, ]( ImplicitlyBoolable, CollectionElement, @@ -56,7 +56,7 @@ struct UnsafePointer[ """This is a pointer type that can point to any generic value that is movable. Parameters: - T: The type the pointer points to. + type: The type the pointer points to. address_space: The address space associated with the UnsafePointer allocated memory. exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. alignment: The minimum alignment of this pointer known statically. @@ -69,14 +69,12 @@ struct UnsafePointer[ # Fields alias _mlir_type = __mlir_type[ `!kgen.pointer<`, - T, + type, `, `, address_space._value.value, `>`, ] - alias type = T - # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# @@ -104,7 +102,7 @@ struct UnsafePointer[ self.address = value @always_inline - fn __init__(inout self, other: UnsafePointer[T, address_space, *_]): + fn __init__(inout self, other: UnsafePointer[type, address_space, *_]): """Exclusivity parameter cast a pointer. Args: @@ -129,7 +127,7 @@ struct UnsafePointer[ @staticmethod @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: T) -> Self: + fn address_of(ref [_, address_space._value.value]arg: type) -> Self: """Gets the address of the argument. Args: @@ -151,11 +149,11 @@ struct UnsafePointer[ Returns: The pointer to the newly allocated array. """ - alias sizeof_t = sizeof[T]() + alias sizeof_t = sizeof[type]() constrained[sizeof_t > 0, "size must be greater than zero"]() - return _malloc[T, address_space=address_space, alignment=alignment]( + return _malloc[type, address_space=address_space, alignment=alignment]( sizeof_t * count ) @@ -166,7 +164,7 @@ struct UnsafePointer[ @always_inline fn __getitem__( self, - ) -> ref [MutableAnyLifetime, address_space._value.value] T: + ) -> ref [MutableAnyLifetime, address_space._value.value] type: """Return a reference to the underlying data. Returns: @@ -176,10 +174,10 @@ struct UnsafePointer[ # We're unsafe, so we can have unsafe things. References we make have # an 'any' mutable lifetime, since UnsafePointer is allowed to alias # anything. - alias _ref_type = Reference[T, MutableAnyLifetime, address_space] + alias _ref_type = Reference[type, MutableAnyLifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( - UnsafePointer[T, address_space, False](self).address + UnsafePointer[type, address_space, False](self).address ) ) @@ -203,7 +201,7 @@ struct UnsafePointer[ IntLike: IntLike, // ](self, offset: IntLike) -> ref [ MutableAnyLifetime, address_space._value.value - ] T: + ] type: """Return a reference to the underlying data, offset by the given index. Parameters: @@ -402,7 +400,9 @@ struct UnsafePointer[ # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn as_noalias_ptr(self) -> UnsafePointer[T, address_space, True, alignment]: + fn as_noalias_ptr( + self, + ) -> UnsafePointer[type, address_space, True, alignment]: """Cast the pointer to a new pointer that is known not to locally alias any other pointer. In other words, the pointer transitively does not alias any other memory value declared in the local function context. @@ -772,7 +772,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn bitcast[ - T: AnyType = Self.T, + T: AnyType = Self.type, /, address_space: AddressSpace = Self.address_space, ](self) -> UnsafePointer[T, address_space, alignment=alignment]: @@ -811,11 +811,11 @@ struct UnsafePointer[ return self.bitcast[Scalar[T], address_space]() @always_inline - fn destroy_pointee(self: UnsafePointer[T, alignment=alignment]): + fn destroy_pointee(self: UnsafePointer[type, alignment=alignment]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed - to contain a valid initialized instance of `T`. This is equivalent to + to contain a valid initialized instance of `type`. This is equivalent to `_ = self.take_pointee()` but doesn't require `Movable` and is more efficient because it doesn't invoke `__moveinit__`. From 7016094569d8206d811ee6eb403140f25dbd72b1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 8 Sep 2024 13:26:56 -0700 Subject: [PATCH 1517/2019] [mojo-stdlib] Remove false addr space generalization from malloc/free Malloc and free are always tied to the default address space on both CPU and GPU. Use conditional conformance to make this explicit in the type signature. MODULAR_ORIG_COMMIT_REV_ID: 2d1d6f90e05f437b30c592c10a5112709700633a --- stdlib/src/memory/memory.mojo | 23 ++++++++--------------- stdlib/src/memory/unsafe_pointer.mojo | 12 +++++------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 49d14e175e..456d3d6274 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -457,20 +457,17 @@ fn _malloc[ /, *, alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, - address_space: AddressSpace = AddressSpace.GENERIC, -](size: Int, /) -> UnsafePointer[type, address_space, alignment=alignment]: +](size: Int, /) -> UnsafePointer[ + type, AddressSpace.GENERIC, alignment=alignment +]: @parameter if triple_is_nvidia_cuda(): - constrained[ - address_space is AddressSpace.GENERIC, - "address space must be generic", - ]() - return external_call["malloc", UnsafePointer[NoneType, address_space]]( - size - ).bitcast[type]() + return external_call[ + "malloc", UnsafePointer[NoneType, AddressSpace.GENERIC] + ](size).bitcast[type]() else: return __mlir_op.`pop.aligned_alloc`[ - _type = UnsafePointer[type, address_space]._mlir_type + _type = UnsafePointer[type, AddressSpace.GENERIC]._mlir_type ](alignment.value, size.value) @@ -480,13 +477,9 @@ fn _malloc[ @always_inline -fn _free(ptr: UnsafePointer): +fn _free(ptr: UnsafePointer[_, AddressSpace.GENERIC, *_]): @parameter if triple_is_nvidia_cuda(): - constrained[ - ptr.address_space is AddressSpace.GENERIC, - "address space must be generic", - ]() external_call["free", NoneType](ptr.bitcast[NoneType]()) else: __mlir_op.`pop.aligned_free`(ptr.address) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index fedd20b159..f94d79f906 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -140,7 +140,9 @@ struct UnsafePointer[ @staticmethod @always_inline - fn alloc(count: Int) -> Self: + fn alloc( + count: Int, + ) -> UnsafePointer[type, AddressSpace.GENERIC, exclusive, alignment]: """Allocate an array with specified or default alignment. Args: @@ -150,12 +152,8 @@ struct UnsafePointer[ The pointer to the newly allocated array. """ alias sizeof_t = sizeof[type]() - constrained[sizeof_t > 0, "size must be greater than zero"]() - - return _malloc[type, address_space=address_space, alignment=alignment]( - sizeof_t * count - ) + return _malloc[type, alignment=alignment](sizeof_t * count) # ===-------------------------------------------------------------------===# # Operator dunders @@ -766,7 +764,7 @@ struct UnsafePointer[ scatter(val, base, mask, alignment) @always_inline - fn free(self): + fn free(self: UnsafePointer[_, AddressSpace.GENERIC, *_]): """Free the memory referenced by the pointer.""" _free(self) From ae9ce15f4150c1514d960f51dd9c56fdfe6c4ce2 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Mon, 9 Sep 2024 13:18:57 -0400 Subject: [PATCH 1518/2019] [External] [stdlib] Fix string split by whitespace (#46821) [External] [stdlib] Fix string split by whitespace Fixes one case in the bug report https://github.com/modularml/mojo/issues/3457 where string is split by white space Co-authored-by: Maxim Zaks Closes modularml/mojo#3458 MODULAR_ORIG_COMMIT_REV_ID: 226bac1af3e28191c6b54f975e2f03d45627cfda --- stdlib/src/collections/string.mojo | 8 ++++++-- stdlib/test/collections/test_string.mojo | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 121f98dc68..7fc62f7754 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1585,6 +1585,10 @@ struct String( . """ + fn num_bytes(b: UInt8) -> Int: + var flipped = ~b + return int(count_leading_zeros(flipped) + (flipped >> 7)) + var output = List[String]() var str_byte_len = self.byte_length() - 1 var lhs = 0 @@ -1606,8 +1610,8 @@ struct String( # if the last char is not whitespace output.append(self[str_byte_len]) break - rhs = lhs + 1 - for s in self[lhs + 1 :]: + rhs = lhs + num_bytes(self.unsafe_ptr()[lhs]) + for s in self[lhs + num_bytes(self.unsafe_ptr()[lhs]) :]: if str(s).isspace(): # TODO: with StringSlice.isspace() break rhs += s.byte_length() diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 24f3fa2045..755dd6c8a1 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -804,6 +804,12 @@ def test_split(): String("1,2,3,3,3").split("3", 2).__str__(), "['1,2,', ',', ',3']" ) + var in5 = String("Hello 🔥!") + var res5 = in5.split() + assert_equal(len(res5), 2) + assert_equal(res5[0], "Hello") + assert_equal(res5[1], "🔥!") + def test_splitlines(): # Test with no line breaks From d19c11c8a888f4894e52e4042c33b7412ee4bb4c Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 9 Sep 2024 15:08:48 -0400 Subject: [PATCH 1519/2019] [External] [stdlib] Fix `String.isdigit()` when the string is empty (#46833) [External] [stdlib] Fix `String.isdigit()` when the string is empty Corner case that was not working correctly as `String("").isdigit()` is `False` Co-authored-by: Manuel Saelices Closes modularml/mojo#3439 MODULAR_ORIG_COMMIT_REV_ID: 70459a0baa362fb609e8d033f4bada3a77ce72e3 --- stdlib/src/collections/string.mojo | 7 +++++-- stdlib/test/collections/test_string.mojo | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 7fc62f7754..a1ee24abec 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -2060,13 +2060,16 @@ struct String( return res^ fn isdigit(self) -> Bool: - """Returns True if all characters in the string are digits. + """A string is a digit string if all characters in the string are digits + and there is at least one character in the string. Note that this currently only works with ASCII strings. Returns: - True if all characters are digits else False. + True if all characters are digits and it's not empty else False. """ + if not self: + return False for c in self: if not isdigit(ord(c)): return False diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 755dd6c8a1..6b8244e6a8 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1553,6 +1553,7 @@ def test_isdigit(): assert_true(isdigit(ord("1"))) assert_false(isdigit(ord("g"))) + assert_false(String("").isdigit()) assert_true(String("123").isdigit()) assert_false(String("asdg").isdigit()) assert_false(String("123asdg").isdigit()) From b0a0e24d81b9bc619bbcee8e61271a5d44a7619a Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 9 Sep 2024 12:11:21 -0700 Subject: [PATCH 1520/2019] Simplify the changelog intro to emphasize the switch to Magic MODULAR_ORIG_COMMIT_REV_ID: 0d279c5986ab8009c0158436fb6f2fb8d9e91455 --- docs/changelog-released.md | 63 +++++--------------------------------- 1 file changed, 8 insertions(+), 55 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index a981b6198b..35e309e544 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -5,67 +5,20 @@ description: A history of significant Mojo changes. toc_max_heading_level: 2 --- -This is a running list of significant changes for the Mojo language and tools. -It doesn't include all internal implementation changes. +This is a list of changes to the Mojo language, standard library, and tools. -## Update Mojo +To check your current version, run `mojo --version`, and then [update Mojo with +`magic`](/magic#update-max-and-mojo). -If you don't have Mojo yet, see the [get started -guide](/mojo/manual/get-started). +:::caution Switch to Magic -:::caution - -[Magic](/magic) is the preferred package manager and virtual environment manager -for MAX and Mojo projects. [conda](/magic/conda) is supported as an alternative. - -The legacy [`modular`](/cli) CLI is now deprecated. We will not release any new -`max` or `mojo` packages through the `modular` tool beyond the 24.5 release. You -must now use [Magic](/magic) or [conda](/magic/conda) to install MAX and Mojo. +The `modular` command-line tool is deprecated (see [how to uninstall +it](/max/faq#if-you-installed-with-modular-deprecated-1)). We recommend that +you now [manage your packages with `magic`](/magic), but you can also [use +conda](/magic/conda). ::: -### Update Mojo in a Magic virtual environment - -The virtual environment for each Magic project has its own package versions. -The Mojo programming language is distributed as part of the `max` package. -Use the [`magic update`](/magic/commands#magic-update) within your project path -to update the `max` package for that project to the latest release: - -```sh -magic update max -``` - -See the [Magic](/magic) documentation for more information on managing Magic -virtual environments. - -### Update Mojo in a conda virtual environment - -Each conda virtual environment has its own package versions. -The Mojo programming language is distributed as part of the `max` package. -Use the [`conda update`](https://docs.conda.io/projects/conda/en/latest/commands/update.html) -command to update the version of the `max` package for an environment. -For example, for an environment named `max-project`, run: - -```sh -conda update -n max-project max -``` - -See the [conda](https://docs.conda.io/projects/conda/en/latest/index.html) -documentation for more information on managing conda virtual environments. - -### Update Mojo using the `modular` CLI - -If you are still using the deprecated `modular` CLI, you can update Mojo to -24.5 by running: - -```sh -modular update mojo -``` - -We will not release any new `max` or `mojo` packages through the `modular` -tool beyond the 24.5 release. You must now use [Magic](/magic/) or -[conda](/magic/conda) to install MAX and Mojo. - ## v24.5 (2024-09-10) ### ✨ Highlights From fecd524cb3dac2a1a87dab3426c3336fb8675775 Mon Sep 17 00:00:00 2001 From: Sawyer Bergeron Date: Mon, 9 Sep 2024 15:49:45 -0400 Subject: [PATCH 1521/2019] [External] [stdlib] Fix soundness issues in InlinedFixedVector on copy/del (#46832) [External] [stdlib] Fix soundness issues in InlinedFixedVector on copy/del `InlinedFixedVector` had apparent double free (on multiple call of `_del_old()`) that was easily accidentally invoked when `__copyinit__()` is used to create two instances. This: a) Moves to using `__del__()` for destruction, ensuring `dynamic_data` is zeroed to avoid further accidents (cheap, free if MLIR sees the write going into a black hole) b) Switches `__copyinit__()` to use an explicit copy constructor, as mentioned in the TODO c) Adds a `__moveinit__()` to allow for perf-identical behavior in additional remaining situations displaced by removing the original `__copyinit__()` (though non-owning slices are necessary for replacing all possible uses as before) Co-authored-by: Sawyer Bergeron Closes modularml/mojo#3445 MODULAR_ORIG_COMMIT_REV_ID: efcacb41c7675b8435a273578e7e4bb9dec51ed1 --- stdlib/src/collections/vector.mojo | 50 ++++++++++++++---------- stdlib/test/collections/test_vector.mojo | 22 ++++++++--- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index edf4768521..820dd95ed9 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -19,7 +19,7 @@ from collections.vector import InlinedFixedVector ``` """ -from memory import Reference, UnsafePointer +from memory import Reference, UnsafePointer, memcpy from sys import sizeof from utils import StaticTuple @@ -73,7 +73,7 @@ fn _calculate_fixed_vector_default_size[type: AnyTrivialRegType]() -> Int: struct InlinedFixedVector[ type: AnyTrivialRegType, size: Int = _calculate_fixed_vector_default_size[type](), -](Sized): +](Sized, ExplicitlyCopyable): """A dynamically-allocated vector with small-vector optimization and a fixed maximum capacity. @@ -125,37 +125,47 @@ struct InlinedFixedVector[ self.current_size = 0 self.capacity = capacity - # TODO: Probably don't want this to be implicitly no-op copyable when we - # have ownership. @always_inline - fn __copyinit__(inout self, existing: Self): - """Creates a shallow copy (doesn't copy the underlying elements). + fn __init__(inout self, existing: Self): + """ + Copy constructor. Args: existing: The `InlinedFixedVector` to copy. """ self.static_data = existing.static_data - self.dynamic_data = existing.dynamic_data + self.dynamic_data = UnsafePointer[type]() + if existing.dynamic_data: + var ext_len = existing.capacity - size + self.dynamic_data = UnsafePointer[type].alloc(ext_len) + memcpy(self.dynamic_data, existing.dynamic_data, ext_len) + self.current_size = existing.current_size self.capacity = existing.capacity @always_inline - fn _del_old(self): - """Destroys the object.""" - if self.capacity > Self.static_size: - self.dynamic_data.free() + fn __moveinit__(inout self, owned existing: Self): + """ + Move constructor. - @always_inline - fn deepcopy(self) -> Self: - """Creates a deep copy of this vector. + Args: + existing: The `InlinedFixedVector` to consume. + """ + self.static_data = existing.static_data + self.dynamic_data = existing.dynamic_data + self.current_size = existing.current_size + self.capacity = existing.capacity - Returns: - The created copy of this vector. + existing.dynamic_data = UnsafePointer[type]() + + @always_inline + fn __del__(owned self): """ - var res = Self(self.capacity) - for i in range(len(self)): - res.append(self[i]) - return res + Destructor. + """ + if self.dynamic_data: + self.dynamic_data.free() + self.dynamic_data = UnsafePointer[type]() @always_inline fn append(inout self, value: type): diff --git a/stdlib/test/collections/test_vector.mojo b/stdlib/test/collections/test_vector.mojo index 347d90ca41..2a5454db6c 100644 --- a/stdlib/test/collections/test_vector.mojo +++ b/stdlib/test/collections/test_vector.mojo @@ -18,6 +18,23 @@ from test_utils import MoveCounter from testing import assert_equal +def test_inlined_fixed_vector_moves(): + var v1 = InlinedFixedVector[Int, 5](10) + var v2 = InlinedFixedVector[Int, 5](10) + + # do one within the smallvec + v2[3] = 99 + v1[3] = 42 + + # plus one within the dynarray + v2[7] = 9999 + v1[7] = 4242 + v2 = v1^ # moves + + assert_equal(v2[3], 42) + assert_equal(v2[7], 4242) + + def test_inlined_fixed_vector(): var vector = InlinedFixedVector[Int, 5](10) @@ -73,9 +90,6 @@ def test_inlined_fixed_vector(): vector.clear() assert_equal(0, len(vector)) - # Free the memory since we manage it ourselves in `InlinedFixedVector` for now. - vector._del_old() - def test_inlined_fixed_vector_with_default(): var vector = InlinedFixedVector[Int](10) @@ -106,8 +120,6 @@ def test_inlined_fixed_vector_with_default(): vector.clear() assert_equal(0, len(vector)) - vector._del_old() - def test_indexing(): var vector = InlinedFixedVector[Int](10) From c82f4c711038b584017e1f846c654b505d34755b Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 9 Sep 2024 17:47:09 -0500 Subject: [PATCH 1522/2019] [stdlib] cleanup: Rename utils._format to utils.format + polish docs for release This fills in some of the doc comments in `format.mojo` to pass linting and adds an example to `Formattable` showing how to implement it for a struct. This also makes a minor clarification to the language in roadmap.md to avoid misleading readers into thinking Mojo's `print()` works identically to Python's `print`, per callout from Joe. MODULAR_ORIG_COMMIT_REV_ID: 7469f719bc89cb4e8aa8fa3656cdfcc574d7a939 --- docs/roadmap.md | 6 +-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/collections/string.mojo | 12 ++++- stdlib/src/prelude/__init__.mojo | 3 +- stdlib/src/utils/__init__.mojo | 1 + .../src/utils/{_format.mojo => format.mojo} | 54 +++++++++++++++++-- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/test/utils/test_format.mojo | 2 +- stdlib/test/utils/test_format_to_stdout.mojo | 2 +- 11 files changed, 72 insertions(+), 16 deletions(-) rename stdlib/src/utils/{_format.mojo => format.mojo} (78%) diff --git a/docs/roadmap.md b/docs/roadmap.md index 31529aae5e..a33d034f96 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -504,9 +504,9 @@ class One: print(One()) # prints '1' ``` -Mojo currently supports this feature through the -[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, so that -`print()` works on all `Stringable` types. Similar support exists for the +Mojo currently supports similar functionality through the +[`Formattable`](/mojo/stdlib/utils/format/Formattable) trait, so that +`print()` works on all `Formattable` types. Similar support exists for the [`int()`](/mojo/stdlib/builtin/int/int-function) and [`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 35ceaec387..74bf38c133 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -25,7 +25,7 @@ from collections.string import ( _calc_initial_buffer_size_int64, ) -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select from sys import triple_is_nvidia_cuda, bitwidthof diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 49baa1a055..ccadd01089 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -29,7 +29,7 @@ from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, StaticString, StringSlice -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter # ===----------------------------------------------------------------------=== # # _file_handle diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 51fe353fc6..fa698860e0 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -20,7 +20,7 @@ from sys.ffi import C_char from memory import memcpy from collections import List from utils import StringRef, Span, StringSlice -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type from collections.string import _atol diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index a1ee24abec..449e05dd95 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -24,8 +24,16 @@ from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy from python import PythonObject -from utils import Span, StaticIntTuple, StringRef, StringSlice, Variant -from utils._format import Formattable, Formatter, ToFormatter +from utils import ( + Span, + StaticIntTuple, + StringRef, + StringSlice, + Variant, + Formattable, + Formatter, +) +from utils.format import ToFormatter from utils.string_slice import _utf8_byte_type, _StringSliceIter # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 8d57e778e3..53eddfd7c6 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -132,8 +132,7 @@ from collections.string import ( isprintable, ) from memory import UnsafePointer, Reference, AddressSpace -from utils import StringRef -from utils._format import Formattable, Formatter +from utils import StringRef, Formattable, Formatter # Private things from builtin._documentation import doc_private diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index ec2d03c08b..4e74bd8022 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -21,3 +21,4 @@ from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock +from .format import Formatter, Formattable diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/format.mojo similarity index 78% rename from stdlib/src/utils/_format.mojo rename to stdlib/src/utils/format.mojo index 65cd2b80b7..069d465691 100644 --- a/stdlib/src/utils/_format.mojo +++ b/stdlib/src/utils/format.mojo @@ -26,6 +26,22 @@ trait Formattable: """ The `Formattable` trait describes a type that can be converted to a stream of UTF-8 encoded data by writing to a formatter object. + + Examples: + + Implement `Formattable` and `Stringable` for a type: + + ```mojo + struct Point(Stringable, Formattable): + var x: Float64 + var y: Float64 + + fn __str__(self) -> String: + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + writer.write("(", self.x, ", ", self.y, ")") + ``` """ fn format_to(self, inout writer: Formatter): @@ -74,11 +90,22 @@ struct Formatter: # ===------------------------------------------------------------------===# fn __init__[F: ToFormatter](inout self, inout output: F): + """Construct a new `Formatter` from a value implementing `ToFormatter`. + + Parameters: + F: The type that supports being used to back a `Formatter`. + + Args: + output: Value to accumulate or process output streamed to the `Formatter`. + """ self = output._unsafe_to_formatter() fn __init__(inout self, *, fd: FileDescriptor): """ - Constructs a formatter that writes to the given file descriptor. + Constructs a `Formatter` that writes to the given file descriptor. + + Args: + fd: The file descriptor to write to. """ @always_inline @@ -97,13 +124,24 @@ struct Formatter: func: fn (UnsafePointer[NoneType], StringRef) -> None, arg: UnsafePointer[NoneType], ): - """ - Constructs a formatter from any closure that accepts string refs. + """Constructs a formatter from any closure that accepts `StringRef`s. + + This function should only be used by low-level types that wish to + accept streamed formatted data. + + Args: + func: Raw closure function pointer. + arg: Opaque user data argument that is passed to the closure function pointer. """ self._write_func = func self._write_func_arg = arg fn __moveinit__(inout self, owned other: Self): + """Move this value. + + Args: + other: The value to move. + """ self._write_func = other._write_func self._write_func_arg = other._write_func_arg @@ -131,6 +169,12 @@ struct Formatter: fn write[*Ts: Formattable](inout self: Formatter, *args: *Ts): """Write a sequence of formattable arguments to the provided formatter. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this formatter. """ @parameter @@ -164,6 +208,10 @@ struct Formatter: fn stdout() -> Self: """ Constructs a formatter that writes directly to stdout. + + Returns: + A formatter that writes provided data to the operating system + standard output stream. """ @always_inline diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 14a9ceb8fe..032eed819d 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -23,7 +23,7 @@ from sys import sizeof from memory import UnsafePointer, memcpy from utils import StringSlice, Variant -from utils._format import ToFormatter +from utils.format import ToFormatter # ===----------------------------------------------------------------------===# # InlineString diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index e876b3082e..513f12377f 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -14,7 +14,7 @@ from testing import assert_equal -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter from utils.inline_string import _FixedString diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index 9d5a6a4d9c..f132356755 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils._format import Formattable, Formatter +from utils import Formattable, Formatter fn main() raises: From fa86b814e2891eddab75a89e748b5f24fb20375d Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Sep 2024 17:51:53 -0700 Subject: [PATCH 1523/2019] [Docs] Update pointer examples. MODULAR_ORIG_COMMIT_REV_ID: 1704237d3bb7c6e69b3f98e422609f352c5b08cd --- docs/manual/lifecycle/life.ipynb | 67 +++++++++++------ docs/manual/parameters/index.ipynb | 116 ++++++++++++++--------------- 2 files changed, 101 insertions(+), 82 deletions(-) diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index d6f4f860fc..4917fa8c3c 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -43,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -96,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -118,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -173,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -207,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -355,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -400,7 +400,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -424,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -438,7 +438,7 @@ " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " self.data.store(i, val)\n", + " (self.data + i).init_pointee_copy(val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", @@ -446,18 +446,20 @@ " self.cap = existing.cap\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " self.data.store(i, existing.data.load(i))\n", + " (self.data + i).init_pointee_copy(existing.data[i])\n", " # The lifetime of `existing` continues unchanged\n", "\n", " fn __del__(owned self):\n", " # We must free the heap-allocated data, but\n", " # Mojo knows how to destroy the other fields\n", + " for i in range(self.size):\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", " fn append(inout self, val: Int):\n", " # Update the array for demo purposes\n", " if self.size < self.cap:\n", - " self.data.store(self.size, val)\n", + " (self.data + self.size).init_pointee_copy(val)\n", " self.size += 1\n", " else:\n", " print(\"Out of bounds\")\n", @@ -468,7 +470,7 @@ " for i in range(self.size):\n", " if i > 0:\n", " print(\", \", end=\"\")\n", - " print(self.data.load(i), end=\"\")\n", + " print(self.data[i], end=\"\")\n", " print(\"]\")" ] }, @@ -488,7 +490,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -588,44 +590,63 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "struct HeapArray:\n", " var data: UnsafePointer[Int]\n", " var size: Int\n", + " var cap: Int\n", + "\n", "\n", " fn __init__(inout self, size: Int, val: Int):\n", " self.size = size\n", + " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data.store(i, val)\n", + " (self.data + i).init_pointee_copy(val)\n", "\n", " fn __copyinit__(inout self, existing: Self):\n", " # Deep-copy the existing value\n", " self.size = existing.size\n", - " self.data = UnsafePointer[Int].alloc(self.size)\n", + " self.cap = existing.cap\n", + " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", - " self.data.store(i, existing.data.load(i))\n", + " (self.data + i).init_pointee_copy(existing.data[i])\n", + " # The lifetime of `existing` continues unchanged\n", "\n", " fn __moveinit__(inout self, owned existing: Self):\n", " print(\"move\")\n", " # Shallow copy the existing value\n", " self.size = existing.size\n", + " self.cap = existing.cap\n", " self.data = existing.data\n", " # Then the lifetime of `existing` ends here, but\n", " # Mojo does NOT call its destructor\n", "\n", " fn __del__(owned self):\n", + " # We must free the heap-allocated data, but\n", + " # Mojo knows how to destroy the other fields\n", + " for i in range(self.size):\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", + " fn append(inout self, val: Int):\n", + " # Update the array for demo purposes\n", + " if self.size < self.cap:\n", + " (self.data + self.size).init_pointee_copy(val)\n", + " self.size += 1\n", + " else:\n", + " print(\"Out of bounds\")\n", + "\n", " fn dump(self):\n", + " # Print the array contents for demo purposes\n", " print(\"[\", end=\"\")\n", " for i in range(self.size):\n", " if i > 0:\n", " print(\", \", end=\"\")\n", - " print(self.data.load(i), end=\"\")\n", + " print(self.data[i], end=\"\")\n", " print(\"]\")" ] }, @@ -649,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -720,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -742,7 +763,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -779,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 04a46f62ca..d098309bf9 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -139,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 52, "metadata": {}, "outputs": [ { @@ -155,7 +155,7 @@ "fn repeat[MsgType: Stringable, count: Int](msg: MsgType):\n", " @parameter\n", " for i in range(count):\n", - " print(msg)\n", + " print(str(msg))\n", "\n", "# Must use keyword parameter for `count`\n", "repeat[count=2](42)" @@ -175,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 53, "metadata": {}, "outputs": [ { @@ -191,7 +191,7 @@ "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", " @parameter\n", " for i in range(count):\n", - " print(msg)\n", + " print(str(msg))\n", "\n", "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", "repeat[2](42)" @@ -227,7 +227,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n", + "from memory.unsafe_pointer import UnsafePointer\n", "\n", "struct GenericArray[ElementType: CollectionElement]:\n", " var data: UnsafePointer[ElementType]\n", @@ -237,11 +237,11 @@ " self.size = len(elements)\n", " self.data = UnsafePointer[ElementType].alloc(self.size)\n", " for i in range(self.size):\n", - " initialize_pointee_move(self.data.offset(i), elements[i])\n", + " (self.data + i).init_pointee_move(elements[i])\n", "\n", " fn __del__(owned self):\n", " for i in range(self.size):\n", - " destroy_pointee(self.data.offset(i))\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", " fn __getitem__(self, i: Int) raises -> ref [__lifetime_of(self)] ElementType:\n", @@ -536,7 +536,7 @@ "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n", "\n", "# Make a big vector containing 1.0 in float16 format.\n", - "var big_vec = SIMD[DType.float16, 32].splat(1.0)\n", + "var big_vec = SIMD[DType.float16, 32](1.0)\n", "\n", "# Do some math and convert the elements to float32.\n", "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n", @@ -690,11 +690,11 @@ " fn __init__(inout self, one: One[Type], another: One[Type]):\n", " self.val1 = one.value\n", " self.val2 = another.value\n", - " print(self.val1, self.val2)\n", + " print(str(self.val1), str(self.val2))\n", "\n", " @staticmethod\n", " fn fire(thing1: One[Type], thing2: One[Type]):\n", - " print(\"🔥\", thing1.value, thing2.value)\n", + " print(\"🔥\", str(thing1.value), str(thing2.value))\n", "\n", "def use_two():\n", " s3 = Two(One(\"infer\"), One(\"me\"))\n", @@ -743,7 +743,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -775,7 +775,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -800,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -840,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -885,7 +885,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 58, "metadata": {}, "outputs": [ { @@ -929,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -954,7 +954,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -995,7 +995,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 61, "metadata": {}, "outputs": [ { @@ -1040,7 +1040,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 62, "metadata": {}, "outputs": [ { @@ -1102,18 +1102,12 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3.1400001049041748 3.1400001049041748 3.1400001049041748 3.1400001049041748\n" - ] - } - ], + "outputs": [], "source": [ + "from memory import UnsafePointer\n", + "\n", "struct Array[T: AnyTrivialRegType]:\n", " var data: UnsafePointer[T]\n", " var size: Int\n", @@ -1122,12 +1116,14 @@ " self.size = size\n", " self.data = UnsafePointer[T].alloc(self.size)\n", " for i in range(self.size):\n", - " self.data[i] = value\n", + " (self.data + i).init_pointee_copy(value)\n", "\n", " fn __getitem__(self, i: Int) -> T:\n", " return self.data[i]\n", "\n", " fn __del__(owned self):\n", + " for i in range(self.size):\n", + " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", "var v = Array[Float32](4, 3.14)\n", @@ -1151,7 +1147,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 67, "metadata": {}, "outputs": [], "source": [ @@ -1211,7 +1207,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -1247,10 +1243,12 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 69, "metadata": {}, "outputs": [], "source": [ + "from collections import Dict\n", + "\n", "alias StringKeyDict = Dict[String, _]\n", "var b = StringKeyDict[UInt8]()\n", "b[\"answer\"] = 42" @@ -1270,7 +1268,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 70, "metadata": {}, "outputs": [], "source": [ @@ -1367,7 +1365,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 71, "metadata": {}, "outputs": [ { @@ -1403,7 +1401,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ @@ -1434,7 +1432,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -1451,7 +1449,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -1472,7 +1470,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 75, "metadata": {}, "outputs": [ { @@ -1519,15 +1517,15 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 80, "metadata": {}, "outputs": [], "source": [ "@value\n", "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", " fn __str__(self) -> String:\n", - " var values = StaticIntTuple[3](sugar, cream, chocolate)\n", - " return str(\"Fudge\") + str(values)" + " return str(\"Fudge (\") + str(sugar) + \",\" +\n", + " str(cream) + \",\" + str(chocolate) + \")\"\n" ] }, { @@ -1540,7 +1538,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -1562,12 +1560,12 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]):\n", - " print(\"Ate \" + str(f))" + " print(\"Ate\", str(f))" ] }, { @@ -1580,15 +1578,15 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 87, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Ate Fudge(5, 5, 7)\n", - "Ate Fudge(5, 8, 9)\n" + "Ate Fudge (5,5,7)\n", + "Ate Fudge (5,8,9)\n" ] } ], @@ -1620,12 +1618,12 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 91, "metadata": {}, "outputs": [], "source": [ "fn devour(f: Fudge[_, 6, _]):\n", - " print(str(\"Devoured \") + str(f))" + " print(\"Devoured\", str(f))" ] }, { @@ -1640,12 +1638,12 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(str(\"Devoured \") + str(f))" + " print(\"Devoured\", str(f))" ] }, { @@ -1661,12 +1659,12 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "fn devour(f: Fudge[_, chocolate=_, cream=6]):\n", - " print(str(\"Devoured \") + str(f))" + " print(\"Devoured\", str(f))" ] }, { @@ -1678,15 +1676,15 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 94, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Devoured Fudge(3, 6, 9)\n", - "Devoured Fudge(4, 6, 8)\n" + "Devoured Fudge (3,6,9)\n", + "Devoured Fudge (4,6,8)\n" ] } ], @@ -1707,12 +1705,12 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "fn nibble(f: Fudge[5]):\n", - " print(\"Ate \" + str(f))" + " print(\"Ate\", str(f))" ] }, { From 1f884551113381fe02a2c6f300e6673c6d8e6bbe Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Sep 2024 19:54:03 -0700 Subject: [PATCH 1524/2019] [Docs] Ownership updates. Includes argument exclusivity, new section on lifetimes, and `ref` args and return values. Also renaming transfer operator -> sigil and other cleanup. MODULAR_ORIG_COMMIT_REV_ID: 2d26c1d667bb974f8c82ce3e600049fdf1e80427 --- docs/manual/decorators/staticmethod.ipynb | 8 +- docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/lifecycle/index.ipynb | 28 +- docs/manual/lifecycle/life.ipynb | 8 +- docs/manual/pointers.ipynb | 2 +- docs/manual/values/index.ipynb | 61 ++- docs/manual/values/lifetimes.ipynb | 559 ++++++++++++++++++++++ docs/manual/values/ownership.ipynb | 246 +++++++--- docs/manual/values/value-semantics.ipynb | 19 +- docs/roadmap.md | 2 +- 10 files changed, 824 insertions(+), 111 deletions(-) create mode 100644 docs/manual/values/lifetimes.ipynb diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index f0c0def9a8..84ff19d90d 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -27,19 +27,19 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "from max.tensor import Tensor\n", + "from collections import List\n", "from pathlib import Path\n", "\n", "\n", "struct MyStruct:\n", - " var data: Tensor[DType.int8]\n", + " var data: List[UInt8]\n", "\n", " fn __init__(inout self):\n", - " self.data = Tensor[DType.int8]()\n", + " self.data = List[UInt8]()\n", "\n", " fn __moveinit__(inout self, owned existing: Self):\n", " self.data = existing.data ^\n", diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 91913f32aa..1440d8fbb8 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -332,7 +332,7 @@ "source": [ "The `pet.name` field is destroyed after the first `print()`, because Mojo knows\n", "that it will be overwritten below. You can also see this behavior when using the\n", - "transfer operator:" + "transfer sigil:" ] }, { diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index fca51f7fbc..9714beaf5b 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -71,19 +71,31 @@ "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", "All values that are declared with the same type have the same lifecycle.\n", "\n", - "- The \"lifetime\" of a value is defined by the span of time during program\n", - "execution in which each value is considered valid. The life of a value begins\n", - "when it is initialized and ends when it is destroyed, which generally (but not\n", - "always) spans from `__init__()` to `__del__()`. No two values have the exact\n", - "same lifetime, because every value is created and destroyed at a different\n", - "point in time (even if the difference is imperceivable).\n", + "- The \"lifetime\" of a value is defined by the span of time during \n", + "program execution in which each value is considered valid. The life of a value \n", + "begins when it is initialized (via `__init__()`, `__copyinit__()` or \n", + "`__moveinit__()`) and ends when it is destroyed (`__del__()`), or consumed in\n", + "some other way (for example, as part of a `__moveinit__()` call). \n", + "\n", + "No two values have the exact same life span, because every value is created and \n", + "destroyed at a different point in time (even if the difference is imperceptible).\n", + "\n", + ":::note Lifetime type\n", + "\n", + "The concept of lifetimes is related to the `lifetime` type, a Mojo primitive\n", + "used to track ownership. For most Mojo programming, you won't need to work with\n", + "`lifetime` values directly. For information, see [Lifetimes and\n", + "references](/mojo/manual/values/lifetimes).\n", + "\n", + ":::\n", "\n", "The life of a value in Mojo begins when a variable is initialized and continues\n", "up until the value is last used, at which point Mojo destroys it. Mojo destroys\n", "every value/object as soon as it's no longer used, using an “as soon as\n", - "possible” (ASAP) destruction policy that runs after every sub-expression.\n", + "possible” (ASAP) destruction policy that runs after every sub-expression. The \n", + "Mojo compiler takes care of releasing resources after last use when needed.\n", "\n", - "As you might imagine, keeping track of a value's lifetime can be difficult if a\n", + "As you might imagine, keeping track of a value's life can be difficult if a\n", "value is shared across functions many times during the life of a program.\n", "However, Mojo makes this predictable partly through its [value\n", "semantics](/mojo/manual/values/value-semantics) and [value\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 4917fa8c3c..ef262ef54b 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -537,7 +537,7 @@ "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", "lifetime of the value does end there (usually indicated with the transfer\n", - "operator `^`), then Mojo instead invokes the move constructor.\n", + "sigil `^`), then Mojo instead invokes the move constructor.\n", "\n", ":::" ] @@ -661,7 +661,7 @@ "mutable reference to the original value, _not a copy_ (unlike other methods that\n", "may declare an argument as `owned`, but might receive the value as a copy if the\n", "method is called without the [`^` transfer\n", - "operator](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", + "sigil](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", "That is, Mojo calls this move constructor _only_ when the original variable's\n", "lifetime actually ends at the point of transfer.\n", "\n", @@ -698,7 +698,7 @@ "\"move-only\" by implementing `__moveinit__()` and _excluding_ `__copyinit__()`.\n", "A move-only type can be passed to other variables and passed into functions\n", "with any argument convention (`borrowed`, `inout`, and `owned`)—the only catch\n", - "is that you must use the `^` transfer operator to end the lifetime of a\n", + "is that you must use the `^` transfer sigil to end the lifetime of a\n", "move-only type when assigning it to a new variable or when passing it as an\n", "`owned` argument." ] @@ -828,7 +828,7 @@ "must take ownership to store each value. This is a useful micro-optimization\n", "and enables the use of move-only types. Trivial types like `Int` are also\n", "passed as `owned`, but because ownership doesn't mean anything for integers, we\n", - "can elide that declaration and the transfer operator (`^`) for simplicity. The\n", + "can elide that declaration and the transfer sigil (`^`) for simplicity. The\n", "transfer operator is also just a formality in this case, because, even if it's\n", "not used with `self.name = name^`, the Mojo compiler will notice that `name` is\n", "last used here and convert this assignment into a move, instead of a\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 4c8f74c4f3..9c4392c261 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -215,7 +215,7 @@ "str_ptr.init_pointee_move(my_string^)\n", "```\n", "\n", - "Note that to move the value, you usually need to add the transfer operator\n", + "Note that to move the value, you usually need to add the transfer sigil\n", "(`^`), unless the value is a [trivial\n", "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", "`Int`) or a newly-constructed, \"owned\" value:\n", diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb index 7fbd408e8b..0c6e34c116 100644 --- a/docs/manual/values/index.ipynb +++ b/docs/manual/values/index.ipynb @@ -24,7 +24,16 @@ "registers, but we won't get into that here). However, each language reads and\n", "writes data a bit differently—sometimes very differently. So in the following\n", "sections, we'll explain how Mojo manages memory in your programs and how this\n", - "affects the way you write Mojo code." + "affects the way you write Mojo code.\n", + "\n", + ":::note\n", + "\n", + "For an alternate introduction to ownership in Mojo, check out our two-part blog\n", + "post: \n", + "[What ownership is really about: a mental model approach](https://www.modular.com/blog/what-ownership-is-really-about-a-mental-model-approach), and [Deep dive into\n", + "ownership in Mojo](https://www.modular.com/blog/deep-dive-into-ownership-in-mojo).\n", + "\n", + ":::" ] }, { @@ -33,21 +42,41 @@ "source": [ "## Stack and heap overview\n", "\n", - "In general, all programming languages use a call stack the same way: When a\n", - "function is called, the compiler allocates a block of memory on the stack that\n", - "is exactly the size required to store the execution logic and _fixed-size_\n", - "local values. When another function is called, its data is likewise added to\n", - "the top of the stack. When a function is done, all its data in the stack is\n", - "destroyed so that memory becomes available for other code.\n", + "In general, all modern programming languages divide a running program's memory\n", + "into four segments:\n", + "\n", + "- Text. The compiled program.\n", + "- Data. Global data, either initialized or uninitialized.\n", + "- Stack. Local data, automatically managed during the program's runtime.\n", + "- Heap. Dynamically-allocated data, managed by the programmer.\n", + "\n", + "The text and data segments are statically sized, but the stack and heap change\n", + "size as the program runs.\n", + "\n", + "The _stack_ stores data local to the current function. When a function is\n", + "called, the program allocates a block of memory—a _stack frame_—that is exactly\n", + "the size required to store the function's data, including any _fixed-size_\n", + "local variables. When another function is called, a new stack frame is pushed\n", + "onto the top of the stack. When a function is done, its stack frame is popped\n", + "off the stack. \n", "\n", "Notice that we said only \"_fixed-size_ local values\" are stored in the stack.\n", "Dynamically-sized values that can change in size at runtime are instead\n", "stored in the heap, which is a much larger region of memory that allows for\n", - "dynamic memory access at runtime. Technically, a local variable for such a value\n", + "dynamic memory allocation. Technically, a local variable for such a value\n", "is still stored in the call stack, but its value is a fixed-size pointer to the\n", - "real value on the heap.\n", + "real value on the heap. Consider a Mojo string: it can be any length, and \n", + "its length can change at runtime. So the Mojo `String` struct includes some statically-sized fields, plus a pointer to a dynamically-allocated buffer\n", + "holding the actual string data.\n", "\n", - "Additionally, values that need to outlive the lifetime of a function (such as\n", + "Another important difference between the heap and the stack is that the stack is \n", + "managed automatically—the code to push and pop stack frames is added by the\n", + "compiler. Heap memory, on the other hand, is managed by the programmer\n", + "explicitly allocating and deallocating memory. You may do this indirectly—by\n", + "using standard library types like `List` and `String`—or directly, using the \n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) API.\n", + "\n", + "Values that need to outlive the lifetime of a function (such as\n", "an array that's passed between functions and should not be copied) are stored\n", "in the heap, because heap memory is accessible from anywhere in the call stack,\n", "even after the function that created it is removed from the stack. This sort of\n", @@ -90,11 +119,13 @@ "\n", "Mojo uses a third approach called \"ownership\" that relies on a collection of\n", "rules that programmers must follow when passing values. The rules ensure there\n", - "is only one \"owner\" for each chunk of memory at a time, and that the memory is\n", - "deallocated accordingly. In this way, Mojo automatically allocates and\n", - "deallocates heap memory for you, but it does so in a way that's deterministic\n", - "and safe from errors such as use-after-free, double-free and memory leaks. Plus,\n", - "it does so with a very low performance overhead.\n", + "is only one \"owner\" for a given value at a time. When a value's lifetime ends,\n", + "Mojo calls its destructor, which is responsible for deallocating any heap memory\n", + "that needs to be deallocated.\n", + "\n", + "In this way, Mojo helps ensure memory is freed, but it does so in a way that's\n", + "deterministic and safe from errors such as use-after-free, double-free and\n", + "memory leaks. Plus, it does so with a very low performance overhead.\n", "\n", "Mojo's value ownership model provides an excellent balance of programming\n", "productivity and strong memory safety. It only requires that you learn some new\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb new file mode 100644 index 0000000000..299323c49c --- /dev/null +++ b/docs/manual/values/lifetimes.ipynb @@ -0,0 +1,559 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: Lifetimes and references\n", + "sidebar_position: 4\n", + "description: Working with lifetimes and references.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::note Work in progress\n", + "\n", + "Both lifetimes and references are a work in progress and subject to change in\n", + "future releases. \n", + "\n", + ":::\n", + "\n", + "In Mojo, _lifetime_ has two meanings: \n", + "\n", + "- In general terms, a value's lifetime refers to the span of time when the \n", + " value is valid. \n", + "\n", + "- It also refers to a specific type of parameter value used to help track the \n", + " lifetimes of values and references to values. For clarity, we'll use\n", + " `lifetime` in code font to refer to the type.\n", + "\n", + "The Mojo compiler includes a lifetime checker, a compiler pass that analyzes\n", + "dataflow through your program. It identifies when variables are valid and \n", + "inserts destructor calls when a value's lifetime ends.\n", + "\n", + "The Mojo compiler uses `lifetime` values to track the validity of references.\n", + "Specifically, a `lifetime` value answers two questions:\n", + "\n", + "- What logical storage location \"owns\" this value?\n", + "- Can the value be mutated using this reference?\n", + "\n", + "For example, consider the following code:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Joan\n" + ] + } + ], + "source": [ + "fn print_str(s: String):\n", + " print(s)\n", + "\n", + "name = String(\"Joan\")\n", + "print_str(name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The line `name = String(\"Joan\")` declares a variable with an identifier (`name`)\n", + "and logical storage space for a `String` value. When you pass `name` into the\n", + "`print_str()` function, the function gets an immutable reference to the value. \n", + "So both `name` and `s` refer to the same logical storage space, and have\n", + "associated `lifetime` values that lets the Mojo compiler reason about them. \n", + "\n", + "Most of the time, `lifetime` values are handled automatically by the compiler. \n", + "However, in some cases you'll need to interact with `lifetime` values directly:\n", + "\n", + "- When working with references—specifically `ref` arguments and `ref` return\n", + " values. \n", + "\n", + "- When working with types like \n", + " [`Reference`](/mojo/stdlib/memory/reference/Reference) or \n", + " [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the \n", + " `lifetime` of the data they refer to.\n", + "\n", + "This section covers [`ref` arguments](#ref-arguments) and \n", + "[`ref` return values](#ref-return-values), which let functions\n", + "take arguments and provide return values as references with parametric\n", + "lifetimes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with lifetimes\n", + "\n", + "Mojo's `lifetime` values are unlike most other values in the language, because\n", + "they're primitive values, not Mojo structs. Specifying a parameter that takes a \n", + "`lifetime` value, you can't just say, `l: Lifetime`, because there's no \n", + "`Lifetime` type. Likewise, because these values are mostly created by the \n", + "compiler, you can't just create your own `lifetime` value—you usually need to \n", + "derive a `lifetime` from an existing value.\n", + "\n", + "### Lifetime types\n", + "\n", + "Mojo supplies a struct and a set of aliases that you can use to specify \n", + "`lifetime` types. As the names suggest, the `ImmutableLifetime` and \n", + "`MutableLifetime` aliases represent immutable and mutable lifetimes, \n", + "respectively:\n", + "\n", + "```mojo\n", + "struct ImmutableRef[lifetime: ImmutableLifetime]:\n", + " pass\n", + "```\n", + "\n", + "Or you can use the [`AnyLifetime`](mojo/stdlib/builtin/type_aliases/AnyLifetime)\n", + "struct to specify a lifetime with parametric mutability:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [], + "source": [ + "struct ParametricRef[\n", + " is_mutable: Bool,\n", + " //,\n", + " lifetime: AnyLifetime[is_mutable].type\n", + "]:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that `AnyLifetime` _isn't a lifetime value_, it's a helper for specifying a \n", + "`lifetime` **type**. Lifetime types carry the mutability of a reference as a \n", + "boolean parameter value, indicating whether the lifetime is mutable, immutable,\n", + "or even with mutability depending on a parameter specified by the enclosing API.\n", + "\n", + "The `is_mutable` parameter here is an [infer-only\n", + "parameter](/mojo/manual/parameters/#infer-only-parameters). It's never\n", + "specified directly by the user, but always inferred from context. The\n", + "`lifetime` value is often inferred, as well. For example, the following code\n", + "creates a [`Reference`](/mojo/stdlib/memory/reference/Reference) to an existing\n", + "value, but doesn't need to specify a lifetime—the `lifetime` is inferred from\n", + "the variable passed in to the reference." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [], + "source": [ + "from memory import Reference\n", + "\n", + "def use_reference():\n", + " a = 10\n", + " r = Reference(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Lifetime values\n", + "\n", + "Most `lifetime` values are created by the compiler. As a developer, there are a\n", + "few ways to specify `lifetime` values:\n", + "\n", + "- Static lifetimes. The `ImmutableStaticLifetime` and `MutableStaticLifetime`\n", + " aliases are lifetimes that last for the duration of the program. \n", + "- The `__lifetime_of()` magic function, which returns the lifetime associated\n", + " with the value (or values) passed in.\n", + "- Inferred lifetime. You can use inferred parameters to capture the lifetime\n", + " of a value passed in to a function.\n", + "\n", + "#### Static lifetimes\n", + "\n", + "You can use the static lifetimes `ImmutableStaticLifetime` and \n", + "`MutableStaticLifetime` when you have a value that should never be destroyed;\n", + "or when there's no way to construct a meaningful `lifetime` for a value.\n", + "\n", + "For an example of the first case, the `StringLiteral` method\n", + "[`as_string_slice()`](/mojo/stdlib/builtin/string_literal/StringLiteral#as_string_slice)\n", + "returns a [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) pointing\n", + "to the original string literal. String literals are static—they're allocated at\n", + "compile time and never destroyed—so the slice is created with an immutable,\n", + "static lifetime.\n", + "\n", + "Converting an\n", + "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) into a\n", + "`Reference` is an example of the second case: the `UnsafePointer`'s data\n", + "doesn't carry a `lifetime`—one reason that it's considered unsafe—but you need\n", + "to specify a `lifetime` when creating a `Reference`. In this case, there's no\n", + "way to construct a meaningful `lifetime` value, so the new `Reference` is\n", + "constructed with a `MutableStaticLifetime`. Mojo won't destroy this value\n", + "automatically. As with any value stored using a pointer, it's up to the user to\n", + "explicitly [destroy the\n", + "value](/mojo/manual/pointers#destroying-or-removing-values).\n", + "\n", + "#### Derived lifetimes\n", + "\n", + "Use the `__lifetime_of()` magic function to obtain a value's lifetime. This can \n", + "be useful, for example, when creating a container type. Consider the `List`\n", + "type. Subscripting into a list (`list[4]`) returns a reference to the item at\n", + "the specified position. The signature of the `__getitem__()` method that's\n", + "called to return the subscripted item looks like this:\n", + "\n", + "```mojo\n", + "fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T:\n", + "```\n", + "\n", + "The syntax may be unfamiliar—`ref` arguments and `ref` return values are\n", + "described in the following sections. For now it's enough to know that \n", + "the return value is a reference of type `T` (where `T` is the element type\n", + "stored in the list), and the reference has the same lifetime as the list itself.\n", + "This means that as long as you hold the reference, the underlying list won't be\n", + "destroyed.\n", + "\n", + ":::note\n", + "\n", + "Ideally the returned reference's `lifetime` would be linked to the individual\n", + "list item, rather than the list itself. Mojo doesn't yet have a mechanism to \n", + "express this relationship.\n", + "\n", + ":::\n", + "\n", + "#### Inferred lifetimes\n", + "\n", + "The other common way to access a lifetime value is to _infer_ it from the\n", + "the arguments passed to a function or method. For example, the `Span` type\n", + "has an associated `lifetime`:\n", + "\n", + "```mojo\n", + "struct Span[\n", + " is_mutable: Bool, //,\n", + " T: CollectionElement,\n", + " lifetime: AnyLifetime[is_mutable].type,\n", + "](CollectionElementNew):\n", + " \"\"\"A non owning view of contiguous data.\n", + "```\n", + "\n", + "One of its constructors creates a `Span` from an existing `List`, and infers\n", + "its `lifetime` value from the list:\n", + "\n", + "```mojo\n", + " fn __init__(inout self, ref [lifetime]list: List[T, *_]):\n", + " \"\"\"Construct a Span from a List.\n", + "\n", + " Args:\n", + " list: The list to which the span refers.\n", + " \"\"\"\n", + " self._data = list.data\n", + " self._len = len(list)\n", + "```\n", + "\n", + "## Working with references\n", + "\n", + "You can use the `ref` keyword with arguments and return values to specify a \n", + "reference with parametric mutability. That is, they can be either mutable or \n", + "immutable.\n", + "\n", + "These references shouldn't be confused with the `Reference` type, which is\n", + "basically a safe pointer type. A `Reference` needs to be dereferenced, like a \n", + "pointer, to access the underlying value. A `ref` argument, on the other hand,\n", + "looks like a `borrowed` or `inout` argument inside the function. A `ref` return\n", + "value looks like any other return value to the calling function, but it is a\n", + "_reference_ to an existing value, not a copy.\n", + "\n", + "### `ref` arguments\n", + "\n", + "The `ref` argument convention lets you specify an argument of parametric\n", + "mutability: that is, you don't need to know in advance whether the passed\n", + "argument will be mutable or immutable. There are several reasons you might want\n", + "to use a `ref` argument:\n", + "\n", + "- You want to accept an argument with parametric mutability.\n", + "\n", + "- You want to tie the lifetime of one argument to the lifetime of another\n", + " argument.\n", + "\n", + "- When you want an argument that is guaranteed to be passed in memory: this can\n", + " be important and useful for generic arguments that need an identity,\n", + " irrespective of whether the concrete type is register passable.\n", + "\n", + "The syntax for a `ref` argument is:\n", + "\n", + "ref [lifetime] argName: argType\n", + "\n", + "The `lifetime` parameter passed inside the square brackets can be replaced with\n", + "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", + "of it as a wildcard that will accept any lifetime:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "def add_ref(ref [_] a: Int, b: Int) -> Int:\n", + " return a+b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also name the lifetime explicitly. This is useful if you want to specify\n", + "an `ImmutableLifetime` or `MutableLifetime`, or if you want to bind to\n", + "the `is_mutable` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Immutable: Hello\n", + "Mutable: Goodbye\n" + ] + } + ], + "source": [ + "def take_str_ref[\n", + " is_mutable: Bool, //,\n", + " life: AnyLifetime[is_mutable].type\n", + " ](ref [life] s: String):\n", + " @parameter\n", + " if is_mutable:\n", + " print(\"Mutable: \" + s)\n", + " else:\n", + " print(\"Immutable: \" + s)\n", + "\n", + "def pass_refs(s1: String, owned s2: String):\n", + " take_str_ref(s1)\n", + " take_str_ref(s2)\n", + "\n", + "pass_refs(\"Hello\", \"Goodbye\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `ref` return values\n", + "\n", + "Like `ref` arguments, `ref` return values allow a function to return a mutable\n", + "or immutable reference to a value. Like a `borrowed` or `inout` argument, these\n", + "references don't need to be dereferenced.\n", + "\n", + "`ref` return values can be an efficient way to handle updating items in a \n", + "collection. The standard way to do this is by implementing the `__getitem__()`\n", + "and `__setitem__()` dunder methods. These are invoked to read from and write to \n", + "a subscripted item in a collection:\n", + "\n", + "```mojo\n", + "value = list[a]\n", + "list[b] += 10\n", + "```\n", + "\n", + "With a `ref` argument, `__getitem__()` can return a mutable reference that can\n", + "be modified directly. This has pros and cons compared to using a `__setitem__()`\n", + "method:\n", + "\n", + "- The mutable reference is more efficient—a single update isn't broken up across\n", + " two methods. However, the referenced value must be in memory.\n", + " \n", + "- A `__getitem__()`/`__setitem__()` pair allows for arbitrary to be run when\n", + " values are retrieved and set. For example, `__setitem__()` can validate or \n", + " constrain input values.\n", + "\n", + "For example, in the following example, `NameList` has a `get()` method\n", + "that returns a reference: " + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dana\n", + "Dana?\n" + ] + } + ], + "source": [ + "struct NameList:\n", + " var names: List[String]\n", + "\n", + " def __init__(inout self, *names: String):\n", + " self.names = List[String]()\n", + " for name in names:\n", + " self.names.append(name[])\n", + "\n", + " def __getitem__(ref [_] self: Self, index: Int) ->\n", + " ref [__lifetime_of(self)] String:\n", + " if (index >=0 and index < len(self.names)):\n", + " return self.names[index]\n", + " else:\n", + " raise Error(\"index out of bounds\")\n", + "\n", + "def use_name_list():\n", + " list = NameList(\"Thor\", \"Athena\", \"Dana\", \"Vrinda\")\n", + " print(list[2])\n", + " list[2] += \"?\"\n", + " print(list[2])\n", + "\n", + "use_name_list()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that this update succeeds, even though `NameList` doesn't define a\n", + "`__setitem__()` method:\n", + "\n", + "```mojo\n", + "list[2] += \"?\"\n", + "```\n", + "\n", + "Also note that the code uses the return value directly each time, rather than\n", + "assigning the return value to a variable, like this:\n", + "\n", + "```mojo\n", + "name = list[2]\n", + "```\n", + "\n", + "Since a variable needs to own its value, `name` would end up with an owned \n", + "_copy_ of the value that `list[2]` returns. Mojo doesn't currently have \n", + "syntax to express that you want to keep the original reference in `name`. This\n", + "will be added in a future release.\n", + "\n", + "In cases where you need to be able to assign the return value to a variable—for\n", + "example, an iterator which will be used in a `for..in` loop—you might consider \n", + "returning a `Reference` instead of a `ref` return value. For example, see the \n", + "[iterator for the `List` \n", + "type](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L60).\n", + "You can assign a `Reference` to a variable, but you need to use the dereference\n", + "operator (`[]`) to access the underlying value." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "nums = List(1, 2, 3)\n", + "for item in nums: # List iterator returns a Reference\n", + " print(item[])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Parametric mutability of return values\n", + "\n", + "Another advantage of `ref` return arguments is the ability to support parametric\n", + "mutability. For example, recall the signature of the `__getitem__()` method\n", + "above:\n", + "\n", + "```mojo\n", + "def __getitem__(ref [_] self: Self, index: Int) ->\n", + " ref [__lifetime_of(self)] String:\n", + "```\n", + "\n", + "Since the `lifetime` of the return value is tied to the lifetime of `self`, the\n", + "returned reference will be mutable if the method was called using a\n", + "mutable reference. The method still works if you have an immutable reference\n", + "to the `NameList`, but it returns an immutable reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Diana\n" + ] + } + ], + "source": [ + "fn pass_immutable_list(list: NameList) raises:\n", + " print(list[2])\n", + " # list[2] += \"?\" # Error, this list is immutable\n", + "\n", + "def use_name_list_again():\n", + " list = NameList(\"Sophie\", \"Jack\", \"Diana\")\n", + " pass_immutable_list(list)\n", + "\n", + "use_name_list_again()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Without parametric mutability, you'd need to write two versions of \n", + "`__getitem__()`, one that accepts an immutable `self` and another that accepts\n", + "a mutable `self`. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 447c0dd528..4d9ffd533c 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -28,11 +28,14 @@ "\n", "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", "each value at a time, while still allowing you to share references with other\n", - "functions. When the lifetime of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death).\n", - "\n", - "On this page, we'll explain the rules that govern this ownership model and how\n", - "to specify different argument conventions that define how values are shared into\n", + "functions. When the life span of the owner ends, Mojo [destroys the\n", + "value](/mojo/manual/lifecycle/death). Programmers are still responsible for\n", + "making sure any type that allocates resources (including memory) also\n", + "deallocates those resources in its destructor. Mojo's ownership system ensures\n", + "that destructors are called promptly.\n", + "\n", + "On this page, we'll explain the rules that govern this ownership model, and how\n", + "to specify different argument conventions that define how values are passed into\n", "functions." ] }, @@ -40,6 +43,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "\n", "## Argument conventions\n", "\n", "In all programming languages, code quality and performance is heavily dependent\n", @@ -70,18 +74,23 @@ " function can read and mutate the original value (it is *not* a copy).\n", " \n", "- `owned`: The function takes **ownership**. This means the function has\n", - " exclusive mutable access to the argument—the function caller does not have\n", - " access to this value (anymore). Often, this also implies that the caller\n", + " exclusive ownership of the argument. Often, this also implies that the caller\n", " should transfer ownership to this function, but that's not always what\n", " happens and this might instead be a copy (as you'll learn below).\n", "\n", + "- `ref`: The function gets a reference with an associated lifetime. The\n", + " reference can be either mutable or immutable. You can think of `ref` arguments\n", + " as a generalization of the `borrowed` and `inout` conventions. `ref` arguments\n", + " are an advanced topic, and they're described in more detail in [Lifetimes and \n", + " references](/mojo/manual/values/lifetimes).\n", + "\n", "For example, this function has one argument that's a mutable\n", "reference and one that's immutable:" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -131,7 +140,7 @@ "\n", "- The `owned` argument always gets a uniquely owned value, which may have been\n", " copied or transferred from the callee. Using `owned` arguments without the \n", - " transfer operator (`^`) usually results in values being copied.\n", + " transfer sigil (`^`) usually results in values being copied.\n", "\n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." @@ -147,6 +156,7 @@ "\n", "- Every value has only one owner at a time.\n", "- When the lifetime of the owner ends, Mojo destroys the value.\n", + "- If there are outstanding references to a value, Mojo keeps the value alive.\n", "\n", "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", "that only one mutable reference to a value can exist at a time. **This is not\n", @@ -191,38 +201,42 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "256x256\n" + "[1, 2, 3, 4]\n" ] } ], "source": [ - "from max.tensor import Tensor, TensorShape\n", + "from collections import List\n", "\n", - "def print_shape(tensor: Tensor[DType.float32]):\n", - " shape = tensor.shape()\n", - " print(str(shape))\n", + "def print_list(list: List[Int]):\n", + " print(list.__str__())\n", "\n", - "var tensor = Tensor[DType.float32](256, 256)\n", - "print_shape(tensor)" + "var list = List(1, 2, 3, 4)\n", + "print_list(list)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here the `tensor` argument is borrowed and not mutated, so the `print_shape()`\n", - "function gets an immutable reference to the original `Tensor`, and doesn't do \n", - "any copying. In general, passing an immutable reference is much more efficient\n", + "Here the `list` argument to `print_list()` is borrowed and not mutated, so the \n", + "`print_list()` function gets an immutable reference to the original `List`, and\n", + "doesn't do any copying. \n", + "\n", + "In general, passing an immutable reference is much more efficient\n", "when handling large or expensive-to-copy values, because the copy constructor\n", "and destructor are not invoked for a borrow.\n", "\n", + "To avoid expensive copies, types should only be implicitly copyable if the copy\n", + "operation is inexpensive.\n", + "\n", "### Compared to C++ and Rust\n", "\n", "Mojo's borrowed argument convention is similar in some ways to passing an\n", @@ -261,59 +275,64 @@ "means any changes to the value *in*side the function are visible *out*side the\n", "function.\n", "\n", - "For example, this `mutate()` function updates the original `x` value:" + "For example, this `mutate()` function updates the original `list` value:" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2\n" + "[1, 2, 3, 4, 5]\n" ] } ], "source": [ - "def mutate(inout y: Int):\n", - " y += 1\n", + "from collections import List\n", + "\n", + "def mutate(inout l: List[Int]):\n", + " l.append(5)\n", + "\n", + "var list = List(1, 2, 3, 4)\n", "\n", - "var x = 1\n", - "mutate(x)\n", - "print(x)" + "mutate(list)\n", + "print_list(list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "That behaves like an optimized shorthand for this:" + "That behaves like an optimized replacement for this:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2\n" + "[1, 2, 3, 4, 5]\n" ] } ], "source": [ - "def mutate_copy(y: Int) -> Int:\n", - " y += 1\n", - " return y\n", + "from collections import List\n", "\n", - "var x = 1\n", - "x = mutate_copy(x)\n", - "print(x)" + "def mutate_copy(l: List[Int]) -> List[Int]:\n", + " l.append(5)\n", + " return l\n", + "\n", + "var list = List(1, 2, 3, 4)\n", + "list = mutate_copy(list)\n", + "print_list(list)" ] }, { @@ -330,15 +349,6 @@ "\n", ":::note\n", "\n", - "Notice that we don't call this argument passing \"by reference.\"\n", - "Although the `inout` convention is conceptually the same, we don't call it\n", - "by-reference passing because the implementation may actually pass values using\n", - "pointers.\n", - "\n", - ":::\n", - "\n", - ":::note\n", - "\n", "You cannot define [default\n", "values](/mojo/manual/functions#optional-arguments) for `inout`\n", "arguments.\n", @@ -346,6 +356,58 @@ ":::" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Argument exclusivity\n", + "\n", + "Mojo enforces _argument exclusivity_ for mutable references. This means that if\n", + "a function receives a mutable reference to a value (such as an `inout` argument),\n", + "it can't receive any other references to the same value—mutable or immutable.\n", + "That is, a mutable reference can't have any other references that _alias_ it.\n", + "\n", + "For example, consider the following code example:\n", + "\n", + "```mojo\n", + "fn append_twice(inout s: String, other: String):\n", + " # Mojo knows 's' and 'other' cannot be the same string.\n", + " s += other\n", + " s += other\n", + "\n", + "fn invalid_access():\n", + " var my_string = str(\"o\")\n", + "\n", + " # warning: passing `my_string` inout is invalid since it is also passed\n", + " # borrowed.\n", + " append_twice(my_string, my_string)\n", + " print(my_string)\n", + "```\n", + "\n", + "This code is confusing because the user might expect the output to be `ooo`, \n", + "but since the first addition mutates both `s` and `other`, the actual output\n", + "would be `oooo`. Enforcing exclusivity of mutable references not only prevents\n", + "coding errors, it also allows the Mojo compiler to optimize code in some cases.\n", + "\n", + "One way to avoid this issue when you do need both a mutable and an immutable \n", + "reference (or need to pass the same value to two arguments) is to make a copy:\n", + "\n", + "```mojo\n", + "fn valid_access():\n", + " var my_string = str(\"o\")\n", + " var other_string = str(my_string)\n", + " append_twice(my_string, other_string)\n", + " print(my_string)\n", + "```\n", + "\n", + ":::note Only a warning\n", + "\n", + "Aliasing a mutable reference produces a warning in v24.5. This will change to an\n", + "error in a subsequent release.\n", + "\n", + ":::" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -355,21 +417,20 @@ "And finally, if you'd like your function to receive value **ownership**, add the\n", "`owned` keyword in front of the argument name.\n", "\n", - "This convention is usually combined with use of the postfixed `^` \"transfer\"\n", - "operator on the variable that is passed into the function, which ends the\n", + "This convention is often combined with use of the postfixed `^` \"transfer\"\n", + "sigil on the variable that is passed into the function, which ends the\n", "lifetime of that variable.\n", "\n", "Technically, the `owned` keyword does not guarantee that the received value is\n", "_the original value_—it guarantees only that the function\n", - "gets unique ownership of a value (enforcing [value\n", - "semantics](/mojo/manual/values/value-semantics)). This happens in one of\n", + "gets unique ownership of a value. This happens in one of\n", "three ways:\n", "\n", - "- The caller passes the argument with the `^` transfer operator, which ends the\n", + "- The caller passes the argument with the `^` transfer sigil, which ends the\n", "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", "transferred into the function without making a copy of any heap-allocated data.\n", "\n", - "- The caller **does not** use the `^` transfer operator, in which case, the\n", + "- The caller **does not** use the `^` transfer sigil, in which case, the\n", "value is copied into the function argument and the original variable remains\n", "valid. (If the original value is not used again, the compiler may optimize away\n", "the copy and transfer the value).\n", @@ -384,18 +445,21 @@ "\n", " take(str(\"A brand-new String!\"))\n", " ```\n", - "\n", - "Regardless, when the function declares an argument as `owned`, it can be certain\n", - "that it has unique mutable access to that value. \n", - "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "For example, the following code works by making a copy of the string,\n", "because—although `take_text()` uses the `owned` convention—the caller does not\n", - "include the transfer operator:" + "include the transfer sigil:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -424,7 +488,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "However, if you add the `^` transfer operator when calling `take_text()`, the\n", + "However, if you add the `^` transfer sigil when calling `take_text()`, the\n", "compiler complains about `print(message)`, because at that point, the `message`\n", "variable is no longer initialized. That is, this version does not compile:\n", "\n", @@ -443,7 +507,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -463,6 +527,39 @@ "my_function()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Regardless of how it receives the value, when the function declares an argument\n", + "as `owned`, it can be certain that it has unique mutable access to that value. \n", + "Because the value is owned, the value is destroyed when the function \n", + "exits—unless the function transfers the value elsewhere.\n", + "\n", + "For example, in the following example, `add_to_list()` takes a string and\n", + "appends it to the list. Ownership of the string is transferred to the list, so\n", + "it's not destroyed when the function exits. On the other hand, \n", + "`consume_string()` doesn't transfer its `owned` value out, so the value is \n", + "destroyed at the end of the function." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import List\n", + "\n", + "def add_to_list(owned name: String, inout list: List[String]):\n", + " list.append(name^)\n", + " # name is uninitialized, nothing to destroy\n", + "\n", + "def consume_string(owned s: String):\n", + " print(s)\n", + " # s is destroyed here" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -470,7 +567,7 @@ ":::note\n", "\n", "Value lifetimes are not fully implemented for top-level code in\n", - "Mojo's REPL, so the transfer operator currently works as intended only when\n", + "Mojo's REPL, so the transfer sigil currently works as intended only when\n", "used inside a function.\n", "\n", ":::" @@ -482,24 +579,29 @@ "source": [ "### Transfer implementation details\n", "\n", - "In Mojo, it's important that you not conflate \"ownership transfer\" with a \"move\n", + "In Mojo, you shouldn't conflate \"ownership transfer\" with a \"move\n", "operation\"—these are not strictly the same thing. \n", "\n", - "There are multiple ways that Mojo can transfer ownership of a value without\n", - "making a copy:\n", + "There are multiple ways that Mojo can transfer ownership of a value:\n", "\n", "- If a type implements the [move\n", " constructor](/mojo/manual/lifecycle/life#move-constructor),\n", " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", - " transferred into a function as an `owned` argument, _and_ the original value's\n", - " lifetime ends at the same point (with or without use of the `^` transfer\n", - " operator).\n", + " transferred into a function as an `owned` argument, _and_ the original\n", + " variable's lifetime ends at the same point (with or without use of the `^`\n", + " transfer operator).\n", + "\n", + "- If a type implements the [copy \n", + " constructor](/mojo/manual/lifecycle/life#move-constructor), `__copyinit__()`\n", + " and not `__moveinit__()`, Mojo may copy the value and destroy the old value.\n", "\n", - "- If a type hasn't implemented `__moveinit__()` Mojo may transfer ownership by\n", - " simply passing the recipient a reference to the value in the caller's stack.\n", + "- In some cases, Mojo can optimize away the move operation entirely, leaving the \n", + " value in the same memory location but updating its ownership. In these cases,\n", + " a value can be transferred without invoking either the `__copyinit__()` or \n", + " `__moveinit__()` constructors.\n", "\n", "In order for the `owned` convention to work _without_ the transfer\n", - "operator, the value type must be copyable (via `__copyinit__()`)." + "sigil, the value type must be copyable (via `__copyinit__()`)." ] }, { @@ -526,7 +628,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -542,7 +644,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This shadow copy typically adds no overhead, because references for small types\n", + "This shadow copy typically adds no overhead, because small types\n", "like `object` are cheap to copy. However, copying large types that allocate heap\n", "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", "copying large numbers of strings.)" diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index 903cabd474..fa8bf907d2 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -85,6 +85,9 @@ "both. Neither `x` nor `y` would \"own\" the value, and any variable would be\n", "allowed to reference it and mutate it.\n", "\n", + "Numeric values in Mojo are value semantic because they're trivial types, which\n", + "are cheap to copy. \n", + "\n", "Here's another example with a function:" ] }, @@ -121,6 +124,7 @@ "\n", "If you're familiar with Python, this is probably familiar so far, because the\n", "code above behaves the same in Python. However, Python is not value semantic.\n", + "\n", "It gets complicated, but let's consider a situation in which you call a Python\n", "function and pass an object with a pointer to a heap-allocated value. Python\n", "actually gives that function a reference to your object, which allows the\n", @@ -144,7 +148,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -171,7 +175,12 @@ "metadata": {}, "source": [ "If this were Python code, the function would modify the original object, because\n", - "Python shares a reference to the original object." + "Python shares a reference to the original object.\n", + "\n", + "However, not all types are inexpensive to copy. Copying a `String` or `List`\n", + "requires allocating heap memory, so we want to avoid copying one by accident.\n", + "When designing a type like this, ideally you want to prevent _implicit_ copies,\n", + "and only make a copy when it's explicitly requested. " ] }, { @@ -201,7 +210,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -301,7 +310,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -337,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [ { diff --git a/docs/roadmap.md b/docs/roadmap.md index a33d034f96..80c7422f64 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -636,7 +636,7 @@ print(type(i or s)) # prints ``` In Mojo, given the expression `(a or b)`, the compiler needs to statically -determine a result type that the types of `a` and `b` can both be converted to. +determine a result type that the types of `a` and `b` can both be **converted** to. For example, currently an `Int` can be implicitly converted to a `String`, but a `String` can't be implicitly converted to an `Int`. So given an integer value From 8d56d178194acf1720396aa53d952fff892811a2 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 10 Sep 2024 13:26:05 -0700 Subject: [PATCH 1525/2019] [stdlib] Add %mojo-no-debug lit substitution Currently this is the same as %mojo externally, but we should test with debug info as well MODULAR_ORIG_COMMIT_REV_ID: 00b3ebeed4389e0172ea3d43c13050ec16a00853 --- stdlib/benchmarks/algorithm/bench_elementwise.mojo | 2 +- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/builtin/bench_sort.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 2 +- stdlib/benchmarks/lit.cfg.py | 1 + stdlib/benchmarks/math/bench_math.mojo | 2 +- stdlib/benchmarks/utils/bench_formatter.mojo | 2 +- stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- stdlib/test/builtin/test_stdin.mojo | 2 +- stdlib/test/lit.cfg.py | 4 ++++ 11 files changed, 14 insertions(+), 9 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index ce05560301..562ec85589 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from sys import simdwidthof from algorithm import elementwise diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 8e34389372..02c33bdbd6 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -16,7 +16,7 @@ # # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t | FileCheck %s +# RUN: %mojo-no-debug %s -t | FileCheck %s # CHECK: Benchmark results from random import rand diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index 22d5611e3f..a7c265a03c 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from benchmark import Bench, BenchConfig, Bencher, BenchId diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index b174a72d1a..e961f29302 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from random import * diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index ebd089488b..58f249f708 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from random import * diff --git a/stdlib/benchmarks/lit.cfg.py b/stdlib/benchmarks/lit.cfg.py index 52cd122b0b..90dd84838f 100644 --- a/stdlib/benchmarks/lit.cfg.py +++ b/stdlib/benchmarks/lit.cfg.py @@ -26,6 +26,7 @@ config.suffixes = [".mojo"] config.substitutions.insert(0, ("%mojo", "mojo")) +config.substitutions.insert(0, ("%mojo-no-debug", "mojo")) # Internal testing configuration. This environment variable # is set by the internal `start-modular.sh` script. diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index bc547b7a97..ff56002cf7 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from math import * from random import * diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 528532475d..af13596c6f 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index a53f595e2a..f152305b4d 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s -t +# RUN: %mojo-no-debug %s -t from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run diff --git a/stdlib/test/builtin/test_stdin.mojo b/stdlib/test/builtin/test_stdin.mojo index ee50605383..65f338ac7c 100644 --- a/stdlib/test/builtin/test_stdin.mojo +++ b/stdlib/test/builtin/test_stdin.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: !windows -# RUN: echo "Hello, World" | %mojo %s +# RUN: echo "Hello, World" | %mojo-no-debug %s from builtin.io import _fdopen from testing import testing diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index b267ad4446..e2fce8570b 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -83,6 +83,10 @@ def has_not(): # with assertions enabled. config.substitutions.insert(1, ("%bare-mojo", "mojo")) + # NOTE: Right now this is the same as %mojo but we should start testing + # with debug info as well + config.substitutions.insert(0, ("%mojo-no-debug", base_mojo_command)) + # The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the # open-source stdlib, we need to specify the paths to the just-built # `stdlib.mojopkg` and `test_utils.mojopkg`. Otherwise, without this, the From 486c5f57279a48afa40d78581e953cfd4641f8fe Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 10 Sep 2024 14:03:35 -0700 Subject: [PATCH 1526/2019] [stdlib] Remove ****** build type This is now always the same as build type MODULAR_ORIG_COMMIT_REV_ID: 09cafbbc8dba56d15123b44b7a66ee9ac85e2320 --- stdlib/src/builtin/debug_assert.mojo | 4 ++-- stdlib/src/sys/_build.mojo | 27 --------------------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 09a97101de..8301e9acc1 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -18,12 +18,12 @@ These are Mojo built-ins, so you don't need to import them. from os import abort from sys import is_defined, triple_is_nvidia_cuda -from sys._build import is_kernels_debug_build +from sys._build import is_debug_build from builtin._location import __call_location, _SourceLocation # Print an error and fail. -alias _ERROR_ON_ASSERT = is_kernels_debug_build() or is_defined[ +alias _ERROR_ON_ASSERT = is_debug_build() or is_defined[ "MOJO_ENABLE_ASSERTIONS" ]() diff --git a/stdlib/src/sys/_build.mojo b/stdlib/src/sys/_build.mojo index 808b4776b6..241b36bb2f 100644 --- a/stdlib/src/sys/_build.mojo +++ b/stdlib/src/sys/_build.mojo @@ -21,33 +21,6 @@ fn _build_type() -> StringLiteral: return env_get_string["BUILD_TYPE"]() -@always_inline("nodebug") -fn _kernels_build_type() -> StringLiteral: - constrained[ - is_defined["KERNELS_BUILD_TYPE"](), - "the kernels build type must be defined", - ]() - return env_get_string["KERNELS_BUILD_TYPE"]() - - -@always_inline("nodebug") -fn is_kernels_debug_build() -> Bool: - """ - Returns True if the build is in debug mode. - - Returns: - Bool: True if the build is in debug mode and False otherwise. - """ - - @parameter - if is_defined["DEBUG"](): - return True - elif is_defined["KERNELS_BUILD_TYPE"](): - return _kernels_build_type() == "debug" - else: - return False - - @always_inline("nodebug") fn is_debug_build() -> Bool: """ From 3362f10fed2c00c45fd8560cfb02f580e7af5699 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 10 Sep 2024 15:04:10 -0600 Subject: [PATCH 1527/2019] [stdlib] Restore implicit `Copyable` for `Tuple`, `ListLiteral`, etc While we'd like to remove the implicit copyability requirement in the long-term for many core standard library types, we're in a bit of a "stuck" state due to some compiler bugs with traits and conditional conformance to keep pursuing this work. So, unwind this work a bit to get out of this halfway state we're currently stuck in by re-introducing the implicit copyability requirement (via `CollectionElement` trait) to several types like `Tuple` and `ListLiteral`. In the future, we can revisit things like `InlineArray` that currrently *only* work on `CollectionElementNew` to allow them to work with `CollectionElement` again until we pick this work back up. MODULAR_ORIG_COMMIT_REV_ID: 43863eb6e3faee2355615f8c90a1ed2409cd87c3 --- docs/changelog.md | 2 ++ stdlib/src/builtin/builtin_list.mojo | 13 +++++++++++-- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 26 +++++++++++++++++++++----- stdlib/src/python/python_object.mojo | 4 ++-- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 497869e2f3..b08e64cc0d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -59,6 +59,8 @@ what we publish. specifies to the compiler that the resultant pointer is a distinct identifiable object that does not alias any other memory in the local scope. +- Restore implicit copyability of `Tuple` and `ListLiteral`. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 3dd0317b06..c9931caabe 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -24,7 +24,7 @@ from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# -struct ListLiteral[*Ts: Movable](Sized, Movable): +struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): """The type of a literal heterogeneous list expression. A list consists of zero or more values, separated by commas. @@ -49,6 +49,15 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): """ self.storage = Tuple(storage=args^) + @always_inline("nodebug") + fn __copyinit__(inout self, existing: Self): + """Copy construct the tuple. + + Args: + existing: The value to copy from. + """ + self.storage = existing.storage + fn __moveinit__(inout self, owned existing: Self): """Move construct the list. @@ -76,7 +85,7 @@ struct ListLiteral[*Ts: Movable](Sized, Movable): # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn get[i: Int, T: Movable](self) -> ref [__lifetime_of(self)] T: + fn get[i: Int, T: CollectionElement](self) -> ref [__lifetime_of(self)] T: """Get a list element at the given index. Parameters: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index b1b82845b7..37587ae39f 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -815,7 +815,7 @@ struct object( self._value = impl @always_inline - fn __init__[*Ts: Movable](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): """Initializes the object from a list literal. Parameters: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 29b1eda15e..c7c69b100c 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -25,7 +25,7 @@ from utils._visualizers import lldb_formatter_wrapping_type @lldb_formatter_wrapping_type -struct Tuple[*element_types: Movable](Sized, Movable): +struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): """The type of a literal tuple expression. A tuple consists of zero or more values, separated by commas. @@ -36,7 +36,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): alias _mlir_type = __mlir_type[ `!kgen.pack<:!kgen.variadic<`, - Movable, + CollectionElement, `> `, element_types, `>`, @@ -58,7 +58,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): fn __init__( inout self, *, - owned storage: VariadicPack[_, Movable, element_types], + owned storage: VariadicPack[_, CollectionElement, element_types], ): """Construct the tuple from a low-level internal representation. @@ -90,6 +90,22 @@ struct Tuple[*element_types: Movable](Sized, Movable): for i in range(Self.__len__()): UnsafePointer.address_of(self[i]).destroy_pointee() + @always_inline("nodebug") + fn __copyinit__(inout self, existing: Self): + """Copy construct the tuple. + + Args: + existing: The value to copy from. + """ + # Mark 'storage' as being initialized so we can work on it. + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(self.storage) + ) + + @parameter + for i in range(Self.__len__()): + UnsafePointer.address_of(self[i]).init_pointee_copy(existing[i]) + @always_inline("nodebug") fn __moveinit__(inout self, owned existing: Self): """Move construct the tuple. @@ -119,7 +135,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): @parameter fn variadic_size( - x: __mlir_type[`!kgen.variadic<`, Movable, `>`] + x: __mlir_type[`!kgen.variadic<`, CollectionElement, `>`] ) -> Int: return __mlir_op.`pop.variadic.size`(x) @@ -161,7 +177,7 @@ struct Tuple[*element_types: Movable](Sized, Movable): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[i: Int, T: Movable](self) -> ref [__lifetime_of(self)] T: + fn get[i: Int, T: CollectionElement](self) -> ref [__lifetime_of(self)] T: """Get a tuple element and rebind to the specified type. Parameters: diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 7df8934d64..f50f63f9e9 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -334,7 +334,7 @@ struct PythonObject( self.py_object = cpython.toPython(string._strref_dangerous()) string._strref_keepalive() - fn __init__[*Ts: Movable](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): """Initialize the object from a list literal. Parameters: @@ -376,7 +376,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) - fn __init__[*Ts: Movable](inout self, value: Tuple[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: Tuple[Ts]): """Initialize the object from a tuple literal. Parameters: From d0c1e1cc36c6ebc5291444f4096321e162c879b8 Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 10 Sep 2024 18:40:37 -0400 Subject: [PATCH 1528/2019] [External] [stdlib] Make `NoneType` Formattable, Stringable and Representable. (#45495) [External] [stdlib] Make `NoneType` Formattable, Stringable and Representable. Implement `str` and `repr` dunder methods for `None` in addition to `format_to` so that `NoneType` conforms to `Stringable`, `Representable`, and `Formattable`. Fixes https://github.com/modularml/mojo/issues/3319. Co-authored-by: Helehex Closes modularml/mojo#3387 MODULAR_ORIG_COMMIT_REV_ID: 2cbcdfea2a36aa341d855d9401815b5d6f67661c --- stdlib/src/builtin/none.mojo | 37 +++++++++++++++++++++++++++++- stdlib/test/builtin/test_none.mojo | 17 ++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo index df23c63141..6ac97be9d9 100644 --- a/stdlib/src/builtin/none.mojo +++ b/stdlib/src/builtin/none.mojo @@ -18,7 +18,13 @@ These are Mojo built-ins, so you don't need to import them. @value @register_passable("trivial") -struct NoneType(CollectionElement): +struct NoneType( + CollectionElement, + CollectionElementNew, + Formattable, + Representable, + Stringable, +): """Represents the absence of a value.""" alias _mlir_type = __mlir_type.`!kgen.none` @@ -26,10 +32,12 @@ struct NoneType(CollectionElement): var _value: Self._mlir_type + @always_inline fn __init__(inout self): """Construct an instance of the `None` type.""" self._value = None + @always_inline fn __init__(inout self, *, other: Self): """Explicit copy constructor. @@ -37,3 +45,30 @@ struct NoneType(CollectionElement): other: Another `NoneType` instance to copy. """ self._value = None + + @no_inline + fn __str__(self) -> String: + """Returns the string representation of `None`. + + Returns: + `"None"`. + """ + return "None" + + @no_inline + fn __repr__(self) -> String: + """Returns the string representation of `None`. + + Returns: + `"None"`. + """ + return "None" + + @no_inline + fn format_to(self, inout writer: Formatter): + """Write `None` to a formatter stream. + + Args: + writer: The formatter to write to. + """ + writer.write("None") diff --git a/stdlib/test/builtin/test_none.mojo b/stdlib/test/builtin/test_none.mojo index afa682405a..338bd2c386 100644 --- a/stdlib/test/builtin/test_none.mojo +++ b/stdlib/test/builtin/test_none.mojo @@ -12,11 +12,28 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from testing import assert_equal + def main(): + test_str() + test_repr() + test_format_to() test_type_from_none() +def test_str(): + assert_equal(NoneType().__str__(), "None") + + +def test_repr(): + assert_equal(NoneType().__repr__(), "None") + + +def test_format_to(): + assert_equal(String.format_sequence(NoneType()), "None") + + struct FromNone: var value: Int From 71b1af69dedfe1390b588ffd6755a97f7ade71de Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 10 Sep 2024 20:55:17 -0600 Subject: [PATCH 1529/2019] [stdlib] Reduce code size bloat in `debug_assert` Since `debug_assert` is not a macro, if the condition has a side-affect, the code generated persists in the final executable and cannot be DCEd by the compiler. So, in practice, we see a lot of calls to `String.format` (which was called from inside `debug_assert` which does not-so-small amount of work to construct a `String` that may never used at runtime in a program. In [Internal link] `debug_assert` was loosened to work on `Formattable` to be consistent with `print` and `abort`, but given that this isn't free in terms of code size bloat, walk this back a bit to work just on `Stringable` types. We could loosen it even further to `StringLiteral`, but currently the stdlib is passing a runtime `String` to `debug_assert` in a few places, so don't go quite that far yet. MODULAR_ORIG_COMMIT_REV_ID: 379a607cb982fa035834a43acfea7ba18108cefc --- stdlib/src/builtin/debug_assert.mojo | 22 +++++++++++----------- stdlib/test/builtin/test_debug_assert.mojo | 17 ----------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 8301e9acc1..469672616e 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -33,8 +33,8 @@ alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() @always_inline fn debug_assert[ - func: fn () capturing -> Bool, formattable: Formattable -](message: formattable): + func: fn () capturing -> Bool, message_type: Stringable +](message: message_type): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -48,10 +48,10 @@ fn debug_assert[ func: The function to invoke to check if the assertion holds. Can be used if the function is side-effecting, in which case a debug_assert taking a Bool will evaluate the expression producing the Bool even in release mode. - formattable: The type of the message. + message_type: A type conforming to `Stringable` for the message. Args: - message: The message to convert to `String` before displaying it on failure. + message: The message before displaying it on failure. """ @parameter @@ -60,7 +60,7 @@ fn debug_assert[ @always_inline -fn debug_assert[formattable: Formattable](cond: Bool, message: formattable): +fn debug_assert[message_type: Stringable](cond: Bool, message: message_type): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -71,11 +71,11 @@ fn debug_assert[formattable: Formattable](cond: Bool, message: formattable): for enabling assertions in the library. Parameters: - formattable: The type of the message. + message_type: A type conforming to `Stringable` for the message. Args: cond: The bool value to assert. - message: The message to convert to `String` before displaying it on failure. + message: The message before displaying it on failure. """ @parameter @@ -89,8 +89,8 @@ fn debug_assert[formattable: Formattable](cond: Bool, message: formattable): @no_inline fn _debug_assert_msg[ - formattable: Formattable, //, *, is_warning: Bool = False -](msg: formattable, loc: _SourceLocation): + message_type: Stringable, //, *, is_warning: Bool = False +](msg: message_type, loc: _SourceLocation): """Aborts with (or prints) the given message and location. This function is intentionally marked as no_inline to reduce binary size. @@ -113,6 +113,6 @@ fn _debug_assert_msg[ @parameter if is_warning: - print(loc.prefix("Assert Warning:"), msg) + print(loc.prefix("Assert Warning:"), str(msg)) else: - abort(loc.prefix("Assert Error: " + String.format_sequence(msg))) + abort(loc.prefix("Assert Error: " + str(msg))) diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index f8a605b978..89d51c9639 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -19,7 +19,6 @@ def main(): test_debug_assert() - test_debug_assert_formattable() # CHECK-OK-LABEL: test_debug_assert @@ -29,19 +28,3 @@ def test_debug_assert(): debug_assert(3, Error("also ok")) # CHECK-OK: is reached print("is reached") - - -# CHECK-OK-LABEL: test_debug_assert_formattable -def test_debug_assert_formattable(): - print("== test_debug_assert_formattable") - debug_assert(True, FormattableOnly("failed with Formattable arg")) - # CHECK-OK: is reached - print("is reached") - - -@value -struct FormattableOnly: - var message: String - - fn format_to(self, inout writer: Formatter): - writer.write(self.message) From 70abda97b6a5af1a20adefaf7febc85adccf8eda Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 10 Sep 2024 21:30:25 -0600 Subject: [PATCH 1530/2019] [stdlib] Remove unmaintained Docker file for examples The `Dockerfile` is not maintained and is from the early EAP days. Remove it to avoid confusion with users and its reference in the `README.md`. MODULAR_ORIG_COMMIT_REV_ID: 2783e8fa48f7619cea81d97ab4fe4abae18852e8 --- examples/README.md | 58 --------------------------- examples/docker/Dockerfile.mojosdk | 64 ------------------------------ examples/docker/build-image.sh | 50 ----------------------- examples/docker/docker-compose.yml | 19 --------- 4 files changed, 191 deletions(-) delete mode 100644 examples/docker/Dockerfile.mojosdk delete mode 100644 examples/docker/build-image.sh delete mode 100644 examples/docker/docker-compose.yml diff --git a/examples/README.md b/examples/README.md index 598ad13844..63647c925c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -26,64 +26,6 @@ magic run mojo matmul.mojo You can run the Mojo notebooks using [JupyterLab or Visual Studio Code](notebooks/README.md) with the Mojo extension available on the Marketplace. -### Mojo SDK Container - -The repo also contains a Dockerfile that can be used to create a -Mojo SDK container for developing and running Mojo programs. Use the -container in conjunction with the Visual Studio Code devcontainers -extension to develop directly inside the container. - -The Dockerfile also sets up a `conda` environment and by default, -starts a `jupyter` server (which you can access via the browser). - -To build a Mojo container, either use -[docker-compose](https://docs.docker.com/compose/) in `mojo/examples/docker`: - -```bash -docker compose up -d -``` - -Or the convenience script provided: - -```bash -./build-image.sh --auth-key \ - --mojo-version 0.3 -``` - -The script also supports building with `podman` instead of `docker`: - -```bash -./build-image.sh --auth-key \ - --use-podman \ - --mojo-version 0.3 - -``` - -You can then run with either `docker` or `podman`. In the example below, -we map the ports, bind mount the current directory and open a shell into -the container: - -```bash -docker run \ - -it --rm \ - -p 8888:8888 \ - --net host \ - -v ${PWD}:${PWD} \ - modular/mojo-v0.3-20232109-1205 bash -``` - -`podman` requires an additional argument to add the `SYS_PTRACE` capabilities: - -```bash -podman run \ - --cap-add SYS_PTRACE \ - -it --rm \ - -p 8888:8888 \ - --net host \ - -v ${PWD}:${PWD} \ - modular/mojo-v0.3-20232109-1205 bash -``` - ## License The Mojo examples and notebooks in this repository are licensed diff --git a/examples/docker/Dockerfile.mojosdk b/examples/docker/Dockerfile.mojosdk deleted file mode 100644 index a65c198986..0000000000 --- a/examples/docker/Dockerfile.mojosdk +++ /dev/null @@ -1,64 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2023, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# The Dockerfile is provided as reference. Please review the redistribution -# terms of the Mojo license in Section 1 (https://www.modular.com/legal/mojo) -# prior to distributing pre-built container images. -# ===----------------------------------------------------------------------=== # - -# Example command line: -# Use no-cache to force docker to rebuild layers of the image by downloading the SDK from the repos -# ./build-image.sh --mojo-version -# - -FROM ubuntu:20.04 - -ARG DEFAULT_TZ=America/Los_Angeles -ENV DEFAULT_TZ=$DEFAULT_TZ - -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive TZ=$DEFAULT_TZ apt-get install -y \ - tzdata \ - vim \ - nano \ - sudo \ - curl \ - wget \ - git && \ - rm -rf /var/lib/apt/lists/* - -# Download the latest version of minicoda py3.8 for linux x86/x64. -RUN curl -fsSL https://repo.anaconda.com/miniconda/$( wget -O - https://repo.anaconda.com/miniconda/ 2>/dev/null | grep -o 'Miniconda3-py38_[^"]*-Linux-x86_64.sh' | head -n 1) > /tmp/miniconda.sh \ - && chmod +x /tmp/miniconda.sh \ - && /tmp/miniconda.sh -b -p /opt/conda - -ENV PATH=/opt/conda/bin:$PATH -RUN conda init - -RUN pip install \ - jupyterlab \ - ipykernel \ - matplotlib \ - ipywidgets - -RUN curl -s https://get.modular.com | sh - -RUN modular install mojo - -ARG MODULAR_HOME="/root/.modular" -ENV MODULAR_HOME=$MODULAR_HOME -ENV PATH="$PATH:$MODULAR_HOME/pkg/packages.modular.com_mojo/bin" - -# Change permissions to allow for Apptainer/Singularity containers -RUN chmod -R a+rwX /root - -RUN jupyter labextension disable "@jupyterlab/apputils-extension:announcements" -CMD ["jupyter", "lab", "--ip='*'", "--NotebookApp.token=''", "--NotebookApp.password=''","--allow-root"] diff --git a/examples/docker/build-image.sh b/examples/docker/build-image.sh deleted file mode 100644 index a494731645..0000000000 --- a/examples/docker/build-image.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -##===----------------------------------------------------------------------===## -# Copyright (c) 2023, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -##===----------------------------------------------------------------------===## -set -e - -# Usage -# ========== -# ./build-image.sh --mojo-version -# - -# CLI option handling code -mojo_ver=${mojo_ver:=0.3} -container_engine=${container_engine:=docker} -extra_cap=${extra_cap:=} -while [ $# -gt 0 ]; do - case "$1" in - --use-podman) - container_engine=podman - extra_cap="--cap-add SYS_PTRACE" - ;; - --mojo-version) - mojo_ver="$2" - shift - ;; - --*) - echo "Unrecognized option $1" - ;; - esac - shift $(( $# > 0 ? 1 : 0 )) -done - -build_image() { - echo "# Building image with ${container_engine}..." - ${container_engine} build --no-cache ${extra_cap} \ - --pull -t modular/mojo-v${mojo_ver}-`date '+%Y%d%m-%H%M'` \ - --file Dockerfile.mojosdk . -} - -# Wrap the build in a function -build_image diff --git a/examples/docker/docker-compose.yml b/examples/docker/docker-compose.yml deleted file mode 100644 index 7c26a916b5..0000000000 --- a/examples/docker/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -## To build and run the image run: -## docker compose up -d - -version: "3.8" -services: - mojo : - container_name: mojo - build : - context: . - dockerfile: Dockerfile.mojosdk - no_cache : true - ports: - - 8888:8888 - - volumes: - - ./..:/mojo - - working_dir: /mojo - \ No newline at end of file From 2d2967bfbf41c2f9fb760daccdae3cb53628d124 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Wed, 11 Sep 2024 13:27:23 -0400 Subject: [PATCH 1531/2019] [External] [stdlib] Fix string find method for non ASCII (#46979) [External] [stdlib] Fix string find method for non ASCII Fixes part of https://github.com/modularml/mojo/issues/3457 where we split on empty string. Co-authored-by: Maxim Zaks Closes modularml/mojo#3468 MODULAR_ORIG_COMMIT_REV_ID: 355375d74497fc64197f747c1a5277ae6a7aa66a --- stdlib/src/utils/string_slice.mojo | 8 ++++---- stdlib/test/collections/test_string.mojo | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index addd3867b7..031e9b7d75 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -636,7 +636,7 @@ struct StringSlice[ characters of the slice starting at start. """ - var self_len = len(self) + var self_len = self.byte_length() var abs_start: Int if start < 0: @@ -677,7 +677,7 @@ struct StringSlice[ if not substr: return 0 - if len(self) < len(substr) + start: + if self.byte_length() < substr.byte_length() + start: return -1 # The substring to search within, offset from the beginning if `start` @@ -686,9 +686,9 @@ struct StringSlice[ var loc = stringref._memmem( haystack_str.unsafe_ptr(), - len(haystack_str), + haystack_str.byte_length(), substr.unsafe_ptr(), - len(substr), + substr.byte_length(), ) if not loc: diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 6b8244e6a8..0b9aa20786 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -810,6 +810,15 @@ def test_split(): assert_equal(res5[0], "Hello") assert_equal(res5[1], "🔥!") + var in6 = String("Лорем ипсум долор сит амет") + var res6 = in6.split(" ") + assert_equal(len(res6), 5) + assert_equal(res6[0], "Лорем") + assert_equal(res6[1], "ипсум") + assert_equal(res6[2], "долор") + assert_equal(res6[3], "сит") + assert_equal(res6[4], "амет") + def test_splitlines(): # Test with no line breaks From 923e37e1634fed08d15106a6203ac53b1f1fde8a Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Wed, 11 Sep 2024 13:03:48 -0500 Subject: [PATCH 1532/2019] [stdlib] Migrate to explicit assertions for test MODULAR_ORIG_COMMIT_REV_ID: 72ce322f2af6368df2a699e12d956281e4a50c67 --- stdlib/test/builtin/test_debug_assert_error.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_debug_assert_error.mojo b/stdlib/test/builtin/test_debug_assert_error.mojo index a796e8cd2a..02a68a4cf5 100644 --- a/stdlib/test/builtin/test_debug_assert_error.mojo +++ b/stdlib/test/builtin/test_debug_assert_error.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %mojo -D KERNELS_BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL +# RUN: not --crash %bare-mojo -D BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL # CHECK-FAIL-LABEL: test_fail From 198311ccb9a09d8197b1a7fc5f942d23360fa0ec Mon Sep 17 00:00:00 2001 From: Eric Hein Date: Wed, 11 Sep 2024 14:12:33 -0400 Subject: [PATCH 1533/2019] [stdlib] Free getdelim buffer correctly Since `getdelim` may call `malloc` internally, we should make a copy of the buffer before returning it, and explicitly call `free` on the result. Also fix passing the size of the buffer as the wrong type (`size_t` is 64-bit) which led to overwriting the Mojo stack in some cases. MODULAR_ORIG_COMMIT_REV_ID: d09afe7f0e1fbca6f72655f1dec13c9e67b1ed76 --- stdlib/src/builtin/io.mojo | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index ccadd01089..e061dd5e6e 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -129,24 +129,28 @@ struct _fdopen[mode: StringLiteral = "a"]: Hello ``` """ - # getdelim will resize the buffer as needed. - var buffer = UnsafePointer[UInt8].alloc(1) + # getdelim will allocate the buffer using malloc(). + var buffer = UnsafePointer[UInt8]() + # ssize_t getdelim(char **restrict lineptr, size_t *restrict n, + # int delimiter, FILE *restrict stream); var bytes_read = external_call[ "getdelim", Int, UnsafePointer[UnsafePointer[UInt8]], - UnsafePointer[UInt32], + UnsafePointer[UInt64], Int, UnsafePointer[NoneType], ]( - UnsafePointer[UnsafePointer[UInt8]].address_of(buffer), - UnsafePointer[UInt32].address_of(UInt32(1)), + UnsafePointer.address_of(buffer), + UnsafePointer.address_of(UInt64(0)), ord(delimiter), self.handle, ) - # Overwrite the delimiter with a null terminator. - buffer[bytes_read - 1] = 0 - return String(buffer, bytes_read) + # Copy the buffer (excluding the delimiter itself) into a Mojo String. + var s = String(StringRef(buffer, bytes_read - 1)) + # Explicitly free the buffer using free() instead of the Mojo allocator. + external_call["free", NoneType](buffer.bitcast[NoneType]()) + return s # ===----------------------------------------------------------------------=== # From ec420d2b41407ccd3ffe373907f0a6bed7ab3d76 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 11 Sep 2024 11:32:05 -0700 Subject: [PATCH 1534/2019] [mojo-stdlib] Implement `rebind` for memory-only types This uses references to implement support for the `rebind` function on memory only types, without copying. This fixes MSTDL-883. MODULAR_ORIG_COMMIT_REV_ID: 7363f8b12bb1f60ea49958ad9b1d75483a2e0072 --- docs/changelog.md | 3 ++ stdlib/src/builtin/rebind.mojo | 35 ++++++++++-- stdlib/test/builtin/test_rebind.mojo | 79 ++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 stdlib/test/builtin/test_rebind.mojo diff --git a/docs/changelog.md b/docs/changelog.md index b08e64cc0d..c13541bdbb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -53,6 +53,9 @@ what we publish. any side effects that occur in the expression are never evaluated at runtime, eliminating concerns about `__type_of(expensive())` being a problem. +- The `rebind` standard library function now works with memory-only types in + addition to `@register_passable("trivial")` ones, without requiring a copy. + ### 🦋 Changed - A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index 47b4e66675..9d718c903f 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -20,7 +20,7 @@ These are Mojo built-ins, so you don't need to import them. fn rebind[ src_type: AnyTrivialRegType, //, dest_type: AnyTrivialRegType, -](val: src_type) -> dest_type: +](src: src_type) -> dest_type: """Statically assert that a parameter input type `src_type` resolves to the same type as a parameter result type `dest_type` after function instantiation and "rebind" the input to the result type. @@ -34,9 +34,38 @@ fn rebind[ dest_type: The type to rebind to. Args: - val: The value to rebind. + src: The value to rebind. Returns: The rebound value of `dest_type`. """ - return __mlir_op.`kgen.rebind`[_type=dest_type](val) + return __mlir_op.`kgen.rebind`[_type=dest_type](src) + + +@always_inline("nodebug") +fn rebind[ + src_type: AnyType, //, + dest_type: AnyType, +](ref [_]src: src_type) -> ref [__lifetime_of(src)] dest_type: + """Statically assert that a parameter input type `src_type` resolves to the + same type as a parameter result type `dest_type` after function + instantiation and "rebind" the input to the result type, returning a + reference to the input value with an adjusted type. + + This function is meant to be used in uncommon cases where a parametric type + depends on the value of a constrained parameter in order to manually refine + the type with the constrained parameter value. + + Parameters: + src_type: The original type. + dest_type: The type to rebind to. + + Args: + src: The value to rebind. + + Returns: + A reference to the value rebound as `dest_type`. + """ + lit = __get_mvalue_as_litref(src) + rebound = rebind[Reference[dest_type, __lifetime_of(src)]._mlir_type](lit) + return __get_litref_as_mvalue(rebound) diff --git a/stdlib/test/builtin/test_rebind.mojo b/stdlib/test/builtin/test_rebind.mojo new file mode 100644 index 0000000000..f23ae595e4 --- /dev/null +++ b/stdlib/test/builtin/test_rebind.mojo @@ -0,0 +1,79 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s | FileCheck %s + + +def main(): + test_rebind_register() + print() + test_rebind_memory() + + +# ===----------------------------------------------------------------------=== # +# test_rebind_register +# ===----------------------------------------------------------------------=== # + + +fn test_rebind_reg[X: Int](a: SIMD[DType.int32, X]): + print("there: ", rebind[SIMD[DType.int32, 4]](a)) + + +def test_rebind_register(): + # CHECK-LABEL: test_rebind_memory + print("test_rebind_memory") + + value = SIMD[DType.int32, 4](17) + # CHECK-NEXT: here: [17, 17, 17, 17] + print("here:", value) + # CHECK-NEXT: there: [17, 17, 17, 17] + test_rebind_reg(value) + + # CHECK-NEXT: done + print("done") + + +# ===----------------------------------------------------------------------=== # +# test_rebind_memory +# ===----------------------------------------------------------------------=== # + + +@value +struct MyMemStruct[size: Int]: + var value: Int + + fn __copyinit__(inout self, existing: Self): + # Make sure no copy is made due to the rebind. + print("Should not copy this!") + self.value = existing.value + + fn speak(self): + print("hello, I am", size, "I hold", self.value) + + +fn indirect_with_rebind[X: Int](a: MyMemStruct[X]): + rebind[MyMemStruct[4]](a).speak() + + +def test_rebind_memory(): + # CHECK-LABEL: test_rebind_memory + print("test_rebind_memory") + + value = MyMemStruct[4](17) + + # CHECK-NEXT: hello, I am 4 I hold 17 + value.speak() + # CHECK-NEXT: hello, I am 4 I hold 17 + indirect_with_rebind(value) + + # CHECK-NEXT: done + print("done") From 058914b38e07aef080db4ce97b2f10ce7f79634b Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:00:03 -0700 Subject: [PATCH 1535/2019] Documented how to use error breakpoints in VS Code MODULAR_ORIG_COMMIT_REV_ID: 628e8b911e89d208225d725876aeb0acacc90af6 --- docs/tools/debugging.ipynb | 20 +++++++++++++------- docs/tools/images/break-on-raise.png | Bin 0 -> 341319 bytes 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 docs/tools/images/break-on-raise.png diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 778cf85631..4f4a62bf31 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -248,11 +248,6 @@ "```" ] }, - { - "cell_type": "raw", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -402,6 +397,8 @@ "[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints), and\n", "[triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), \n", "as described in the VS Code documentation.\n", + "The Mojo debugger also supports _error breakpoints_ (also known as \"break on\n", + "raise\"), which break whenever a `raise` statement is executed.\n", "\n", "When debugging Mojo code, the debugger doesn't support function breakpoints,\n", "data breakpoints, or conditional breakpoints based on an expression (it does \n", @@ -438,7 +435,16 @@ "- Right-click on the breakpoint and select **Edit Condition**, or,\n", "- Click the **Edit Condition** icon next to the breakpoint.\n", "\n", - "This brings up the same menu, **next to the breakpoint in the editor tab**." + "This brings up the same menu, **next to the breakpoint in the editor tab**.\n", + "\n", + "#### Enable error breakpoints\n", + "\n", + "You can enable and disable error breakpoints in VS Code by selecting \"Mojo\n", + "Raise\" in the **Breakpoints** section of the **Run and Debug** view. If enabled\n", + "during debugging, executing a `raise` statement causes the debugger to stop\n", + "execution and highlight the line of code where the error was raised.\n", + "\n", + "![VS Code window showing a program paused in the debugger with the Run and Debug view visible. The program is paused at a raise statement.](images/break-on-raise.png)" ] }, { @@ -541,7 +547,7 @@ "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", "triggers the debugger.\n", "\n", - "::: note Assertions\n", + ":::note Assertions\n", "\n", "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", "ways to specify assertions. Assertions also trigger an error, so can open the \n", diff --git a/docs/tools/images/break-on-raise.png b/docs/tools/images/break-on-raise.png new file mode 100644 index 0000000000000000000000000000000000000000..fd80ab13cc2ee1e261f02652606bc913583984e1 GIT binary patch literal 341319 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

    Ed4>_jq{m#8BGiu@($ZGKO>{~V=C7s#s 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 z zLH8C;n45&fzJW}EA7&9OWbl?={qKy2q=#|m>>Jo?qSCY|UX;@b4)2N)BSR00mM5!+ z#))x78egpIn;QRm`X?&BO6 z#$@8`!jJ&a?$?T-+4>(5hS9!MXG%lxnrK&g^ibXN+WP0aQ;E*EjeV?+7|c24nNdYYkM~_{(!~(P56Df|Bsedhl_qymX^HwncM;GE#(cJM^A|ngh_|-^A0#p zD)4*%^gM_3`CCZrx=il0xZ2;njLU6L*OOc|f=Ysq?%5Gt&w{a#xG`h|yrER%6Q||B z=zOG~r?*GAtdMQ%_oVEs#XDkLmEKH`<_3YR^qUiW9+o&GL$dWX5U8%`9*&UkFS6C)%3{q)8+=R6s~r#8rZv{1$EHXgq)xzm zafb%i2Gt`8rU`DKdb!VuWZlKd6Grzc;rxNlXGB29dXbKzR!1HD<(&Hg`G~(X_i6)H zR#nn#ewfg83HeA1zBaQb7|CLlsxKc dp_xGLO&O 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ǫCkFu-@r)NUvTggrYu^^5NU~zN{abTl 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 literal 0 HcmV?d00001 From 445eb43964f09e1443da5b46b45ab6adec288892 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 11 Sep 2024 13:43:37 -0600 Subject: [PATCH 1536/2019] [stdlib] Internally reorganize `test_utils` This does some internal moving around of `test_utils` for consistency. MODULAR_ORIG_COMMIT_REV_ID: e6eed24e2228ec8c122b9cfcd6e82e93a5e55f5e --- stdlib/test/lit.cfg.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index e2fce8570b..75b7579b97 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -42,6 +42,10 @@ def has_not(): # This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRE: darwin`. config.available_features.add(platform.system().lower()) +# test_utils does not contain tests, just source code +# that we run `mojo package` on to be used by other tests +config.excludes = ["test_utils"] + # Internal testing configuration. This environment variable # is set by the internal `start-modular.sh` script. if "_START_MODULAR_INCLUDED" in os.environ: @@ -54,10 +58,6 @@ def has_not(): ) # External, public Mojo testing configuration else: - # test_utils does not contain tests, just source code - # that we run `mojo package` on to be used by other tests - config.excludes = ["test_utils"] - # test_source_root: The root path where tests are located. config.test_source_root = Path(__file__).parent.resolve() From 6d65f985400663b57da3edebcef01f40b224c66b Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 11 Sep 2024 17:10:57 -0600 Subject: [PATCH 1537/2019] [stdlib] Don't re-export `doc_private` from `prelude` As part of trimming the exported entities from the `prelude`, remove `doc_private` to free up the identifier for users. Import this explicitly in the few places that use it. MODULAR_ORIG_COMMIT_REV_ID: f1c1c66584a7bf0190108ff3752789b8354fa154 --- stdlib/src/builtin/simd.mojo | 1 + stdlib/src/memory/maybe_uninitialized.mojo | 1 + stdlib/src/prelude/__init__.mojo | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 514ee23f5c..c065ec8fef 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -33,6 +33,7 @@ from sys._assembly import inlined_assembly from os import abort from bit import pop_count +from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from builtin.hash import _hash_simd diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 1c61bb3ea8..bbb593a24c 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # from os import abort +from builtin._documentation import doc_private struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 53eddfd7c6..16366487f7 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -135,5 +135,4 @@ from memory import UnsafePointer, Reference, AddressSpace from utils import StringRef, Formattable, Formatter # Private things -from builtin._documentation import doc_private from utils._visualizers import lldb_formatter_wrapping_type From 82f1c6c9406b131a03ca8f1b1acf0a850ae887d8 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 11 Sep 2024 17:30:32 -0600 Subject: [PATCH 1538/2019] [stdlib] Don't export `lldb_formatter_wrapping_type` from `prelude` As part of trimming the exported entities from the `prelude`, remove `lldb_formatter_wrapping_type` to free up the identifier for users. Import this explicitly in the few places that use it. MODULAR_ORIG_COMMIT_REV_ID: 1028caba26ec4c0fc9cbf189c90c82da7d372e06 --- stdlib/src/builtin/uint.mojo | 1 + stdlib/src/prelude/__init__.mojo | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index d00d6643ac..dee9d73027 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from sys import bitwidthof +from utils._visualizers import lldb_formatter_wrapping_type @lldb_formatter_wrapping_type diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 16366487f7..e6d2fd6a39 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -133,6 +133,3 @@ from collections.string import ( ) from memory import UnsafePointer, Reference, AddressSpace from utils import StringRef, Formattable, Formatter - -# Private things -from utils._visualizers import lldb_formatter_wrapping_type From 42b75b61b00998612fcd3c55f3a6a845157f524a Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 11 Sep 2024 17:38:56 -0600 Subject: [PATCH 1539/2019] [stdlib][test] Cleanup `Python.add_to_path(".")` usage As of recently, it's no longer required - it behaves the same as Python (being able to access modules in the same directory as the target file). So, remove the usage of `Python.add_to_path(".")` in the python tests. MODULAR_ORIG_COMMIT_REV_ID: df553826a2e4a8e9f54d983ca53b927accb52a79 --- stdlib/test/python/test_ownership.mojo | 4 ---- stdlib/test/python/test_python_error_handling.mojo | 4 ---- stdlib/test/python/test_python_interop.mojo | 4 ---- stdlib/test/python/test_python_module_create.mojo | 2 -- 4 files changed, 14 deletions(-) diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index 708798449c..b2f1203332 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -13,14 +13,11 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s -from pathlib import _dir_of_current_file - from python import Python, PythonObject from testing import assert_equal fn test_import(inout python: Python) raises: - Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") var py_string = my_module.my_function("Hello") var str = String(python.__str__(py_string)) @@ -56,7 +53,6 @@ fn test_getitem_ownership(inout python: Python) raises: fn test_getattr_ownership(inout python: Python) raises: - Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") var obj = my_module.Foo(4) var py_string = str(obj.bar) diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index 7427273b77..d74ed3b429 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -13,8 +13,6 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s -from pathlib import _dir_of_current_file - from python import Python, PythonObject from testing import assert_equal, assert_raises @@ -28,7 +26,6 @@ fn test_python_exception_import() raises: fn test_python_exception_getattr() raises: try: - Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") if my_module: var person = my_module.Person() @@ -49,7 +46,6 @@ fn test_python_exception_call() raises: with assert_raises( contains="Can't instantiate abstract class AbstractPerson" ): - Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") if my_module: var person = my_module.AbstractPerson() diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index 089217de6d..be71cfd2dc 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -13,8 +13,6 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s -from pathlib import _dir_of_current_file - from python.python import Python, PythonObject, _get_global_python_itf from testing import assert_equal @@ -29,7 +27,6 @@ fn test_execute_python_string(inout python: Python) -> String: fn test_local_import(inout python: Python) -> String: try: - Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") if my_module: var foo = my_module.Foo("apple") @@ -61,7 +58,6 @@ def hello(name): fn test_call(inout python: Python) -> String: try: - Python.add_to_path(str(_dir_of_current_file())) var my_module: PythonObject = Python.import_module("my_module") return str( my_module.eat_it_all( diff --git a/stdlib/test/python/test_python_module_create.mojo b/stdlib/test/python/test_python_module_create.mojo index 5c52deec3e..0fd71d7545 100644 --- a/stdlib/test/python/test_python_module_create.mojo +++ b/stdlib/test/python/test_python_module_create.mojo @@ -16,7 +16,6 @@ from python import Python, PythonObject from testing import assert_equal -from pathlib import _dir_of_current_file def test_create_module(): @@ -25,7 +24,6 @@ def test_create_module(): # TODO: inspect properties about the module # First though, let's see if we can even import it - # Python.add_to_path(str(_dir_of_current_file())) # var imported_module = Python.import_module(module_name) # # _ = module_name From c80bcab8295d660978d9a70f7ee20cb31852b643 Mon Sep 17 00:00:00 2001 From: Derek Smith Date: Wed, 11 Sep 2024 19:41:30 -0400 Subject: [PATCH 1540/2019] [External] [docs] Delete rogue paren in docs (#47012) [External] [docs] Delete rogue paren in docs Just deleting a loose parenthesis in the functions manual. Co-authored-by: Derek Smith Closes modularml/mojo#3469 MODULAR_ORIG_COMMIT_REV_ID: 904e5f79ad63e46552f5c647ed798e5f6ca3cbc4 --- docs/manual/functions.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 1183291215..ba7b132f22 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -166,7 +166,7 @@ " return an `object` by default.)\n", "\n", "- Arguments are mutable. Arguments default to using the `borrowed` \n", - " [argument convention](/mojo/manual/values/ownership#argument-conventions))\n", + " [argument convention](/mojo/manual/values/ownership#argument-conventions)\n", " like an `fn` function, with a special addition: if the function mutates the\n", " argument, it makes a mutable copy. \n", "\n", From 492822b0b0fdf1427e0bcfa0c2e83423dc0bba34 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 11 Sep 2024 18:00:32 -0600 Subject: [PATCH 1541/2019] [stdlib][docs] Fix typo in variable notebook "from form" -> "from" MODULAR_ORIG_COMMIT_REV_ID: 1d91770ebebd5fc741a6eb01bde8539cad8d9f0a --- docs/manual/variables.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 8d1dd916b6..7b83db715d 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": { @@ -205,7 +205,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Although Mojo can infer a variable type from from the first value assigned to a \n", + "Although Mojo can infer a variable type from the first value assigned to a \n", "variable, it also supports static type annotations on variables. Type \n", "annotations provide a more explicit way of specifying the variable's type.\n", "\n", From 6ae5c5ab189a9490eaf460f2a1a37e7f1918b97f Mon Sep 17 00:00:00 2001 From: Shayan Shams Date: Thu, 12 Sep 2024 11:16:48 -0400 Subject: [PATCH 1542/2019] [External] [docs] Replace `[your-username]` with `origin` (#47067) [External] [docs] Replace `[your-username]` with `origin` Resolves `fatal: [your-username] does not appear to be a git repository ...` Co-authored-by: Shayan Shams Closes modularml/mojo#3289 MODULAR_ORIG_COMMIT_REV_ID: 9ab6b1eb55f8028ae97f8cc43429546b338f6232 --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c0d372d70d..73f374a0a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -304,7 +304,7 @@ request into the `nightly` branch. First push your changes: ```bash -git push -u [your-username] my-fix-pr +git push -u origin my-fix-pr ``` You'll see a link to create a PR: From 9954560650ebd198960b940922cb8d9c42a9c338 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:56:39 -0700 Subject: [PATCH 1543/2019] Added a section to the Mojo "Traits" documentation to describe the Formattable trait, with an example of implementing both it and the Stringable and Representable traits. MODULAR_ORIG_COMMIT_REV_ID: 59ea2b0a9d08bd161b6b8dcff6ea3be53896df32 --- docs/manual/traits.ipynb | 128 +++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 20 deletions(-) diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 962de79a2e..60eb9bfb05 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -451,10 +451,11 @@ " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", " - [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable)\n", - " - [`Formattable`](/mojo/stdlib/utils/_format/Formattable)\n", + " - [`Formattable`](/mojo/stdlib/utils/format/Formattable)\n", " - [`Hashable`](/mojo/stdlib/builtin/hash/Hashable)\n", " - [`Indexer`](/mojo/stdlib/builtin/int/Indexer)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", + " - [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising)\n", " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", @@ -465,6 +466,8 @@ " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", " - [`StringableCollectionElement`](/mojo/stdlib/builtin/value/StringableCollectionElement)\n", + " - [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising)\n", + " - [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", " - [`Roundable`](/mojo/stdlib/builtin/math/Roundable)\n", " - [`ToFormatter`](/mojo/stdlib/utils/_format/ToFormatter)\n", " - [`Truncable`](/mojo/stdlib/builtin/_math/Truncable)\n", @@ -514,50 +517,135 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The `Intable` and `Stringable` traits\n", + "### The `Intable` and `IntableRaising` traits\n", "\n", - "The [`Intable`](/mojo/stdlib/builtin/int/Intable) and \n", - "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) traits identify types\n", - "that can be implicitly converted to `Int` and `String`, respectively. \n", + "The [`Intable`](/mojo/stdlib/builtin/int/Intable) trait identifies a type that\n", + "can be implicitly converted to `Int`. The\n", + "[`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) trait describes a\n", + "type can be converted to an `Int`, but the conversion might raise an error.\n", "\n", - "Any type that conforms to `Stringable` works with the built-in\n", - "[`print()`](/mojo/stdlib/builtin/io/print) and \n", - "[`str()`](/mojo/stdlib/builtin/str/str) functions:" + "Both of these traits require the type to implement the `__int__()` method. For\n", + "example:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "This is a dog named Spot\n" + "True\n" ] } ], "source": [ "@value\n", - "struct Pet(Stringable):\n", - " var name: String\n", - " var type: String\n", + "struct Foo(Intable):\n", + " var i: Int\n", "\n", - " fn __str__(self) -> String:\n", - " return \"This is a \" + self.type + \" named \" + self.name\n", + " fn __int__(self) -> Int:\n", + " return self.i\n", "\n", - "var spot = Pet(\"Spot\", \"dog\")\n", - "print(spot)" + "var foo = Foo(42)\n", + "print(int(foo) == 42)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Similarly, an `Intable` type works with the built-in \n", - "[`int`](/mojo/stdlib/builtin/int/int-function) function. You can find an example\n", - "in the [`Intable` API reference](/mojo/stdlib/builtin/int/Intable)." + "### The `Stringable`, `Representable`, and `Formattable` traits\n", + "\n", + "The [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait identifies a type\n", + "that can be implicitly converted to\n", + "[`String`](/mojo/stdlib/collections/string/String). The\n", + "[`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) trait\n", + "describes a type that can be converted to a `String`, but the conversion might\n", + "raise an error. Any type that conforms to `Stringable` or `StringableRaising`\n", + "also works with the built-in [`str()`](/mojo/stdlib/builtin/str/str) function to\n", + "explicitly return a `String`. These traits also mean that the type can support\n", + "both the `{!s}` and `{}` format specifiers of the `String` class'\n", + "[`format()`](/mojo/stdlib/collections/string/String#format) method. These traits\n", + "require the type to define the\n", + "[`__str__()`](/mojo/stdlib/builtin/str/Stringable#__str__) method.\n", + "\n", + "In contrast, the [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", + "trait that defines a type that can be used with the built-in\n", + "[`repr()`](/mojo/stdlib/builtin/repr/repr) function, as well as the `{!r}`\n", + "format specifier of the `format()` method. This trait requires the type to\n", + "define the [`__repr__()`](/mojo/stdlib/builtin/repr/Representable#__repr__)\n", + "method, which should compute the \"official\" string representation of a type. If\n", + "at all possible, this should look like a valid Mojo expression that could be\n", + "used to recreate a struct instance with the same value.\n", + "\n", + "The [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", + "trait denotes a trait composition of the `Stringable` and `Representable`\n", + "traits. It requires a type to implement both a `__str__()` and a `__repr__()`\n", + "method.\n", + "\n", + "The [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait describes a\n", + "type that can be converted to a stream of UTF-8 encoded data by writing to a\n", + "formatter object. The [`print()`](/mojo/stdlib/builtin/io/print) function\n", + "requires that its arguments conform to the `Formattable` trait. This enables\n", + "efficient stream-based writing by default, avoiding unnecessary intermediate\n", + "String heap allocations.\n", + "\n", + "The `Formattable` trait requires a type to implement a\n", + "[`format_to()`](/mojo/stdlib/utils/format/Formattable#format_to) method, which\n", + "is provided with an instance of\n", + "[`Formatter`](/mojo/stdlib/utils/format/Formatter) as an argument. You then\n", + "invoke the `Formatter` instance's\n", + "[`write()`](/mojo/stdlib/utils/format/Formatter#write) method to write a\n", + "sequence of `Formattable` arguments constituting the `String` representation of\n", + "your type.\n", + "\n", + "While this might sound complex at first, in practice you can minimize\n", + "boilerplate and duplicated code by using the [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) static function to\n", + "implement the type's `Stringable` implementation in terms of its `Formattable`\n", + "implementation. Here is a simple example of a type that implements all of the\n", + "`Stringable`, `Representable`, and `Formattable` traits:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dog(name='Rex', age=5)\n", + "Dog(Rex, 5)\n", + "String: Dog(Rex, 5)\n", + "Representation: Dog(name='Rex', age=5)\n" + ] + } + ], + "source": [ + "@value\n", + "struct Dog(Stringable, Representable, Formattable):\n", + " var name: String\n", + " var age: Int\n", + "\n", + " fn __repr__(self) -> String:\n", + " return \"Dog(name=\" + repr(self.name) + \", age=\" + repr(self.age) + \")\"\n", + "\n", + " fn __str__(self) -> String:\n", + " return String.format_sequence(self)\n", + "\n", + " fn format_to(self, inout writer: Formatter) -> None:\n", + " writer.write(\"Dog(\", self.name, \", \", self.age, \")\")\n", + "\n", + "var dog = Dog(\"Rex\", 5)\n", + "print(repr(dog))\n", + "print(dog)\n", + "\n", + "var dog_info = String(\"String: {!s}\\nRepresentation: {!r}\").format(dog, dog)\n", + "print(dog_info)" ] }, { From e644aca5b66fa604d9c481d8995c0513646f0cc5 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 12 Sep 2024 12:05:30 -0500 Subject: [PATCH 1544/2019] [stdlib] feat: Initial framework for Mojo integration tests. MODULAR_ORIG_COMMIT_REV_ID: e6ff0850ef66397e96c46625c1c257035383757e --- stdlib/test/lit.cfg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 75b7579b97..7c9fbb7dd9 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -39,7 +39,7 @@ def has_not(): if has_not(): config.available_features.add("has_not") -# This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRE: darwin`. +# This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRES: darwin`. config.available_features.add(platform.system().lower()) # test_utils does not contain tests, just source code From a77a5ac40fa13a8d097a7f1eab1fcd4019f1ce11 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 12 Sep 2024 11:56:48 -0700 Subject: [PATCH 1545/2019] [Docs] Add conditional conformance section. MODULAR_ORIG_COMMIT_REV_ID: 400dc45f49fe0d6a88d0302ae5829e0ac4248082 --- docs/manual/parameters/index.ipynb | 83 +++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index d098309bf9..3d4aa2778e 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -217,7 +217,7 @@ "## Parameterized structs\n", "\n", "You can also add parameters to structs. You can use parameterized structs to\n", - "build generic containers. For example, a generic array type might include code\n", + "build generic collections. For example, a generic array type might include code\n", "like this:" ] }, @@ -322,6 +322,87 @@ "The method returns an instance of `GenericArray[Float64]`." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conditional conformance\n", + "\n", + "When creating a generic struct, you might want to define some methods that\n", + "require extra features. For example, consider a collection like `GenericArray`\n", + "that holds instances of `CollectionElement`. The `CollectionElement` trait\n", + "only requires that the stored data type be copyable and movable. This\n", + "imposes a lot of limitations: you can't implement a `sort()` method because\n", + "you can't guarantee that the stored type supports the comparison operators; you can't\n", + "write a useful `__str__()` or `__repr__()` dunder method because you can't\n", + "guarantee that the stored type supports conversion to a string.\n", + "\n", + "The answer to these issues is _conditional conformance_, which lets you define a \n", + "method that requires additional features. You do this by defining the `self`\n", + "value that has a more specific bound on one or more of its parameters. \n", + "\n", + "For example, the following code defines a `Container` type that holds an\n", + "instance of `CollectionElement`. It also defines a `__str__()` method that can \n", + "only be called if the stored `ElementType` conforms to \n", + "`StringableCollectionElement`:" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "Hello\n" + ] + } + ], + "source": [ + "@value\n", + "struct Container[ElementType: CollectionElement]:\n", + " var element: ElementType\n", + "\n", + " def __str__[StrElementType: StringableCollectionElement, //](\n", + " self: Container[StrElementType]) -> String:\n", + " return str(self.element)\n", + "\n", + "def use_container():\n", + " float_container = Container(5)\n", + " string_container = Container(\"Hello\")\n", + " print(float_container.__str__())\n", + " print(string_container.__str__())\n", + "\n", + "use_container()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the signature of the `__str__()` method, which declares the `self` argument\n", + "with a more specific type. Specifically, it declares that it takes a `Container`\n", + "with an `ElementType` that conforms to the `StringableCollectionElement` trait.\n", + "\n", + "```mojo\n", + "def __str__[StrElementType: StringableCollectionElement, //](\n", + " self: Container[StrElementType]) -> String:\n", + "```\n", + "\n", + "This trait must be a superset of `ElementType`'s original trait: for example,\n", + "`StringableCollectionElement` inherits from `CollectionElement`, so it includes\n", + "all of requirements of the original trait.\n", + "\n", + "Note that the `use_container()` function calls the `__str__()` method directly,\n", + "rather than calling `str(float_container)`. One current limitation of\n", + "conditional conformance is that Mojo can't recognize the struct\n", + "`Container[Int]` as conforming to `Stringable`, even though the `__str__()`\n", + "method is implemented for any `ElementType` that's also `Stringable`." + ] + }, { "attachments": {}, "cell_type": "markdown", From e23ec400b84cda15efd316199d2b381ef6e443d0 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 12 Sep 2024 12:13:39 -0700 Subject: [PATCH 1546/2019] [Docs] Update docs for optional var in fn functions. MODULAR_ORIG_COMMIT_REV_ID: 2321b38dbd72bed6bef7e8319681d1c4af37247a --- docs/manual/functions.ipynb | 8 +--- docs/manual/variables.ipynb | 91 ++++++++++++------------------------- 2 files changed, 30 insertions(+), 69 deletions(-) diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index ba7b132f22..420d13aa46 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -93,9 +93,6 @@ " `inout` [argument\n", " convention](/mojo/manual/values/ownership#argument-conventions)).\n", "\n", - "- [Variables](/mojo/manual/variables) must be declared using the `var`\n", - " keyword.\n", - "\n", "- If the function raises an exception, it must be explicitly declared with the\n", " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", "\n", @@ -174,10 +171,7 @@ " [object reference\n", " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", " \n", - " If an argument is any other declared type, it's received as a value.\n", - "\n", - "- [Variables](/mojo/manual/variables) don't need to be declared using \n", - " `var`." + " If an argument is any other declared type, it's received as a value." ] }, { diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 7b83db715d..e4f676b0cd 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -27,7 +27,7 @@ "\n", "Mojo has two kinds of variables:\n", "\n", - "- Declared variables are created with the `var` keyword, and may include\n", + "- Explicitly-declared variables are created with the `var` keyword, and may include\n", " [type annotations](#type-annotations).\n", "\n", " ```mojo\n", @@ -35,7 +35,7 @@ " var b: Float64 = 3.14\n", " ```\n", " \n", - "- Undeclared variables are created with an assignment statement:\n", + "- Implicitly-declared variables are created with an assignment statement:\n", "\n", " ```mojo\n", " a = 5\n", @@ -87,10 +87,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Undeclared variables\n", + "## Implicitly-declared variables\n", "\n", - "Within a `def` function or a REPL environment, you can create a variable with\n", - "just a name and a value. For example:" + "You can create a variable with just a name and a value. For example:" ] }, { @@ -99,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "name = str(\"Sam\")\n", + "name = String(\"Sam\")\n", "user_id = 0" ] }, @@ -107,41 +106,34 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Undeclared variables are strongly typed: they take the type from the first value\n", - "assigned to them. For example, the `user_id` variable above is type `Int`, while\n", - "the `name` variable is type `String`. You can't assign a string to `user_id` or an\n", - "integer to `name`. \n", - "\n", - "Undeclared variables are scoped at the function level. You create an undeclared\n", - "variable the first time you assign a value to a given name inside a function. \n", - "Any subsequent references to that name inside the function refer to the same\n", - "variable. For more information, see [Variable scopes](#variable-scopes), which\n", - "describes how variable scoping differs between declared and undeclared\n", - "variables.\n", - "\n", - ":::note\n", - "\n", - "Undeclared variables are not allowed in an `fn` function or as a struct\n", - "field.\n", - "\n", - ":::" + "Implicitly-declared variables are strongly typed: they take the type from the\n", + "first value assigned to them. For example, the `user_id` variable above is type\n", + "`Int`, while the `name` variable is type `String`. You can't assign a string to\n", + "`user_id` or an integer to `name`.\n", + "\n", + "Implicitly-declared variables are scoped at the function level. You create an\n", + "implicitly-declared variable the first time you assign a value to a given name\n", + "inside a function. Any subsequent references to that name inside the function\n", + "refer to the same variable. For more information, see [Variable\n", + "scopes](#variable-scopes), which describes how variable scoping differs between\n", + "explicitly- and implicitly-declared variables." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Declared variables\n", + "## Explicitly-declared variables\n", "\n", "You can declare a variable with the `var` keyword. For example:\n", "\n", "```mojo\n", - "var name = str(\"Sam\")\n", + "var name = String(\"Sam\")\n", "var user_id: Int\n", "```\n", "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", - "declared values are typed—either explicitly with a \n", + "explicitly-declared variables are typed—either explicitly with a \n", "[type annotation](#type-annotations) or implicitly when they're initialized with\n", "a value.\n", "\n", @@ -154,43 +146,17 @@ "var user_id: Int = \"Sam\"\n", "```\n", "\n", - "There are several main differences between declared variables and undeclared\n", - "variables:\n", + "There are several main differences between explicitly-declared variables and\n", + "implicitly-declared variables:\n", "\n", - "- A declared variable can be declared without initializing it:\n", + "- An explicitly-declared variable can be declared without initializing it:\n", "\n", " ```mojo\n", " var value: Float64\n", " ```\n", "\n", - "- Declared variables follow [lexical scoping](#variable-scopes), unlike \n", - " undeclared variables.\n", - "\n", - "- Declared variables can be used in both `def` and `fn` functions.\n", - "\n", - "Using `var` can help prevent runtime errors caused by typos. For example,\n", - "if you misspell the name of an [undeclared variable](#undeclared-variables),\n", - "Mojo simply creates a new variable using the misspelled name. But when all\n", - "mutable variables must be first declared with `var` (which is the case inside\n", - "an `fn` function), then misspellings such as the following are caught by the\n", - "compiler:\n", - "\n", - "```mojo\n", - "var name = \"Sam\"\n", - "# Somewhere later...\n", - "name = \"Sammy\" # This is not allowed in an `fn` function\n", - "```\n", - "\n", - "Although you can use `var` in a `def` function, this benefit is\n", - "realized only when used inside an `fn` function, where the Mojo compiler will\n", - "flag undeclared variables (such as the above `nane`) as unknown declarations.\n", - "\n", - ":::note\n", - "\n", - "When using Mojo in a REPL environment, top-level variables (variables\n", - "outside a function or struct) do not require `var` declarations.\n", - "\n", - ":::" + "- Explicitly-declared variables follow [lexical scoping](#variable-scopes),\n", + " unlike implicitly-declared variables." ] }, { @@ -460,10 +426,11 @@ "The lifetime of the inner `num` ends exactly where the `if` code block ends,\n", "because that's the scope in which the variable was defined.\n", "\n", - "This is in contrast to undeclared variables (those without the `var`\n", + "This is in contrast to implicitly-declared variables (those without the `var`\n", "keyword), which use **function-level scoping** (consistent with Python variable\n", - "behavior). That means, when you change the value of an undeclared variable\n", - "inside the `if` block, it actually changes the value for the entire function.\n", + "behavior). That means, when you change the value of an implicitly-declared\n", + "variable inside the `if` block, it actually changes the value for the entire\n", + "function.\n", "\n", "For example, here's the same code but *without* the `var` declarations:" ] @@ -500,7 +467,7 @@ "metadata": {}, "source": [ "Now, the last `print()` function sees the updated `num` value from the inner\n", - "scope, because undeclared variables (Python-style variables) use function-level\n", + "scope, because implicitly-declared variables (Python-style variables) use function-level\n", "scope (instead of lexical scope)." ] } From 3f67f05ac9354254709ae896b539d749b7c57762 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Thu, 12 Sep 2024 13:52:26 -0700 Subject: [PATCH 1547/2019] [stdlib] Force has_not on CI If CI is missing `not` it should explicitly fail since that's a misconfiguration MODULAR_ORIG_COMMIT_REV_ID: 52afc5d47b6442f224950016089b5924636d36ae --- stdlib/test/lit.cfg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 7c9fbb7dd9..87e08cbbcf 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -36,7 +36,7 @@ def has_not(): return shutil.which("not") is not None -if has_not(): +if has_not() or os.getenv("GITHUB_REPOSITORY"): config.available_features.add("has_not") # This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRES: darwin`. From 31557d56f95b39d55383cc53507dc9135ff57c05 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 12 Sep 2024 19:00:33 -0700 Subject: [PATCH 1548/2019] [******][GPU] Plumb cluster_dim as an option to launch a ****** (#47122) This starts adding the ability to launch with a cluster_dim. This is optional since it's only valid on H100+ systems. Cloes KERN-890 MODULAR_ORIG_COMMIT_REV_ID: c406cf9fd513c68f7753877cb378f767071074d7 --- stdlib/src/utils/static_tuple.mojo | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 9ca59042dc..88299f6df1 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -70,23 +70,22 @@ fn _create_array[ Returns: The array with values filled from the input list. """ - debug_assert(size == len(lst), "mismatch in the number of elements") if len(lst) == 1: return __mlir_op.`pop.array.repeat`[ _type = __mlir_type[`!pop.array<`, size.value, `, `, type, `>`] ](lst[0]) - else: - var array = __mlir_op.`kgen.undef`[ - _type = __mlir_type[`!pop.array<`, size.value, `, `, type, `>`] - ]() + debug_assert(size == len(lst), "mismatch in the number of elements") + var array = __mlir_op.`kgen.undef`[ + _type = __mlir_type[`!pop.array<`, size.value, `, `, type, `>`] + ]() - @parameter - for idx in range(size): - _set_array_elem[idx, size, type](lst[idx], array) + @parameter + for idx in range(size): + _set_array_elem[idx, size, type](lst[idx], array) - return array + return array # ===----------------------------------------------------------------------===# From 60422b366fd3bddbefde977ad4dfe2eeb75bdf2a Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Fri, 13 Sep 2024 11:35:55 -0400 Subject: [PATCH 1549/2019] [External] [stdlib] Add negative test for `String.split()`(#47145) [External] [stdlib] Add negative test for `String.split()` Add a test for `String.split()` with an empty separator. Co-authored-by: Joshua James Venter Closes modularml/mojo#3461 MODULAR_ORIG_COMMIT_REV_ID: 6529ff9a90d9bc9eabf6b870c51261ab2e33a0f5 --- stdlib/src/collections/string.mojo | 5 ++++- stdlib/test/collections/test_string.mojo | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 449e05dd95..b67304e66d 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1522,6 +1522,9 @@ struct String( Returns: A List of Strings containing the input split by the separator. + Raises: + If the separator is empty. + Examples: ```mojo @@ -1542,7 +1545,7 @@ struct String( var items = 0 var sep_len = sep.byte_length() if sep_len == 0: - raise Error("ValueError: empty separator") + raise Error("Separator cannot be empty.") if str_byte_len < 0: output.append("") diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 0b9aa20786..86c3222260 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -819,6 +819,9 @@ def test_split(): assert_equal(res6[3], "сит") assert_equal(res6[4], "амет") + with assert_raises(contains="Separator cannot be empty."): + _ = String("1, 2, 3").split("") + def test_splitlines(): # Test with no line breaks From 092f9015e3dc0c652a9a0254edf857815c39f54a Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Fri, 13 Sep 2024 08:38:07 -0700 Subject: [PATCH 1550/2019] Updated v24.5 release date. Incorporated feedback. Added an introduction to the Highlights section. Fixed various typos and formatting issues. MODULAR_ORIG_COMMIT_REV_ID: bd597d5eedec9a9398a9d34c5838fe8e9074d0b2 --- docs/changelog-released.md | 182 ++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 83 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 35e309e544..b917ee6c6d 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -7,8 +7,9 @@ toc_max_heading_level: 2 This is a list of changes to the Mojo language, standard library, and tools. -To check your current version, run `mojo --version`, and then [update Mojo with -`magic`](/magic#update-max-and-mojo). +To check your current version, run `mojo --version`. To update the version of +Mojo for your project with the `magic` package manager, follow the instructions +in [Update a package](/magic#update-a-package) to update the `max` package. :::caution Switch to Magic @@ -19,10 +20,13 @@ conda](/magic/conda). ::: -## v24.5 (2024-09-10) +## v24.5 (2024-09-13) ### ✨ Highlights +Here's a brief summary of some of the major changes in this release, with more +detailed information in the following sections: + - Mojo now supports Python 3.12 interoperability. - The set of automatically imported entities (types, aliases, functions) into @@ -30,33 +34,34 @@ conda](/magic/conda). user code as users will need to explicitly import what they're using for cases previously automatically included before. +- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments + conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. + +- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an + optional prompt and reads a line from standard input, in the same way as + Python. + - Mojo now allows implicit definitions of variables within a `fn` in the same way that has been allowed in a `def`. The `var` keyword is still allowed, but is now optional. -- Mojo now supports "conditional conformances" where some methods on a struct - have additional trait requirements that the struct itself doesn't. - - Mojo now diagnoses "argument exclusivity" violations due to aliasing references. Mojo requires references (including implicit references due to `borrowed`/`inout` arguments) to be uniquely referenced (non-aliased) if mutable. This is a warning in the 24.5 release, but will be upgraded to an error in subsequent releases. +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. + - `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. Functions that previously took a `DTypePointer` now take an equivalent `UnsafePointer`. For more information on using pointers, see [Unsafe pointers](/mojo/manual/pointers) in the Mojo Manual. -- [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments - conform to the `Formattable` trait. This enables efficient stream-based - writing by default, avoiding unnecessary intermediate String heap allocations. - -- The new builtin [`input()`](/mojo/stdlib/builtin/io/input) function prints an - optional prompt and reads a line from standard input, in the same way as - Python. - - There are many new standard library APIs, with new features for strings, collections, and interacting with the filesystem and environment. Changes are listed in the standard library section. @@ -72,7 +77,11 @@ conda](/magic/conda). ### Language changes -#### ⭐️ New +- Mojo now allows implicit definitions of variables within a `fn` in the same + way that has been allowed in a `def`. The `var` keyword is still allowed and + still denotes the declaration of a new variable with a scope (in both `def` + and `fn`). Relaxing this makes `fn` and `def` more similar, but they still + differ in other important ways. - Mojo now diagnoses "argument exclusivity" violations due to aliasing references. Mojo requires references (including implicit references due to @@ -106,11 +115,56 @@ conda](/magic/conda). This is a warning in the 24.5 release, but will be upgraded to an error in subsequent releases. -- Mojo now allows implicit definitions of variables within a `fn` in the same - way that has been allowed in a `def`. The `var` keyword is still allowed and - still denotes the declaration of a new variable with a scope (in both `def` - and `fn`). Relaxing this makes `fn` and `def` more similar, but they still - differ in other important ways. + :::note + + Argument exclusivity is not enforced for register-passable types. They are + passed by copy, so they don't form aliases. + + ::: + +- Mojo now supports "conditional conformances" where some methods on a struct + have additional trait requirements that the struct itself doesn't. This is + expressed through an explicitly declared `self` type: + + ```mojo + struct GenericThing[Type: AnyType]: # Works with anything + # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' + fn normal_method(self): ... + + # Just redeclare the requirements with more specific types: + fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): + var tmp = val^ # Ok to move 'val' since it is Movable + ... + fn usage_example(): + var a = GenericThing[Int]() + a.normal_method() # Ok, Int conforms to AnyType + a.needs_move(42) # Ok, Int is movable + + var b = GenericThing[NonMovable]() + b.normal_method() # Ok, NonMovable conforms to AnyType + + # error: argument type 'NonMovable' does not conform to trait 'Movable' + b.needs_move(NonMovable()) + ``` + + Conditional conformance works with dunder methods and other things as well. + +- As a specific form of "conditional conformances", initializers in a struct + may indicate specific parameter bindings to use in the type of their `self` + argument. For example: + + ```mojo + @value + struct MyStruct[size: Int]: + fn __init__(inout self: MyStruct[0]): pass + fn __init__(inout self: MyStruct[1], a: Int): pass + fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass + + def test(x: Int): + a = MyStruct() # Infers size=0 from 'self' type. + b = MyStruct(x) # Infers size=1 from 'self' type. + c = MyStruct(x, x) # Infers size=2 from 'self' type. + ``` - Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This @@ -175,50 +229,6 @@ conda](/magic/conda). print(x) # no longer complains about 'x' being uninitialized ``` -- Mojo now supports "conditional conformances" where some methods on a struct - have additional trait requirements that the struct itself doesn't. This is - expressed through an explicitly declared `self` type: - - ```mojo - struct GenericThing[Type: AnyType]: # Works with anything - # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' - fn normal_method(self): ... - - # Just redeclare the requirements with more specific types: - fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): - var tmp = val^ # Ok to move 'val' since it is Movable - ... - fn usage_example(): - var a = GenericThing[Int]() - a.normal_method() # Ok, Int conforms to AnyType - a.needs_move(42) # Ok, Int is movable - - var b = GenericThing[NonMovable]() - b.normal_method() # Ok, NonMovable conforms to AnyType - - # error: argument type 'NonMovable' does not conform to trait 'Movable' - b.needs_move(NonMovable()) - ``` - - Conditional conformance works with dunder methods and other things as well. - -- As a specific form of "conditional conformances", initializers in a struct - may indicate specific parameter bindings to use in the type of their `self` - argument. For example: - - ```mojo - @value - struct MyStruct[size: Int]: - fn __init__(inout self: MyStruct[0]): pass - fn __init__(inout self: MyStruct[1], a: Int): pass - fn __init__(inout self: MyStruct[2], a: Int, b: Int): pass - - def test(x: Int): - a = MyStruct() # Infers size=0 from 'self' type. - b = MyStruct(x) # Infers size=1 from 'self' type. - c = MyStruct(x, x) # Infers size=2 from 'self' type. - ``` - - `async` functions now support memory-only results (like `String`, `List`, etc.) and `raises`. Accordingly, both [`Coroutine`](/mojo/stdlib/builtin/coroutine/Coroutine) and @@ -254,11 +264,9 @@ conda](/magic/conda). export MOJO_PYTHON="~/venv/bin/python" ``` - `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic libpython, + `MOJO_PYTHON_LIBRARY` still exists for environments with a dynamic `libpython` but no Python executable. -#### 🦋 Changed - - The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a C-like set of semantics around pointer aliasing and derivation. However, the C semantics bring a lot of history and baggage that are not needed in Mojo and @@ -326,12 +334,12 @@ conda](/magic/conda). print("Hello, " + name + "!") ``` - If the user enters "Mojo" it returns "Hello Mojo!" + If the user enters "Mojo" it returns "Hello, Mojo!" - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments - conform to the `Formattable` trait. This enables efficient stream-based - writing by default, avoiding unnecessary intermediate String heap - allocations. + conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. + This enables efficient stream-based writing by default, avoiding unnecessary + intermediate String heap allocations. Previously, `print()` required types conform to [`Stringable`](/mojo/stdlib/builtin/str/Stringable). This meant that to @@ -377,7 +385,7 @@ conda](/magic/conda). - :::note TODO + :::note The error shown when passing a type that does not implement `Formattable` to `print()` is currently not entirely descriptive of the underlying cause: @@ -465,11 +473,17 @@ conda](/magic/conda). method to `String` and `StringLiteral`, which returns an `UnsafePointer[C_char]` for convenient interoperability with C APIs. - - Added the `byte_length()` method to `String`, `StringSlice`, and - `StringLiteral` and deprecated their private `_byte_length()` methods. - Added a warning to `String.__len__` method that it will return length in - Unicode codepoints in the future and `StringSlice.__len__()` now does return - the Unicode codepoints length. + - Added the `byte_length()` method to + [`String`](/mojo/stdlib/collections/string/String#byte_length), + [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice#byte_length), + and + [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral#byte_length) + and deprecated their private `_byte_length()` methods. Added a warning to + the [`String.__len__()`](/mojo/stdlib/collections/string/String#__len__) + method that it will return the length in Unicode codepoints in the future + and + [`StringSlice.__len__()`](/mojo/stdlib/utils/string_slice/StringSlice#__len__) + now does return the Unicode codepoints length. ([PR #2960](https://github.com/modularml/mojo/pull/2960)) - Added a new [`StaticString`](/mojo/stdlib/utils/string_slice/#aliases) type @@ -485,7 +499,8 @@ conda](/magic/conda). `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, reflecting their use for compatibility with C APIs. - - Continued transition to `UnsafePointer` and unsigned byte type for strings: + - Continued the transition to `UnsafePointer` and unsigned byte type for + strings: - [`String.unsafe_ptr()`](/mojo/stdlib/collections/string/String#unsafe_ptr) now returns an `UnsafePointer[UInt8]` (was `UnsafePointer[Int8]`) @@ -551,8 +566,9 @@ conda](/magic/conda). - `initialize_pointee_copy(p, value)` => [`p.init_pointee_copy(value)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) - `move_pointee(src=p1, dst=p2)` => [`p.move_pointee_into(p2)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) - - The `UnsafePointer.offset()` method has been removed. Use - [pointer arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. + - The `UnsafePointer.offset()` method is deprecated and will be removed in a + future release. Use [pointer + arithmetic](/mojo/manual/pointers#storing-multiple-values) instead. ```mojo new_ptr = ptr.offset(1) @@ -670,12 +686,12 @@ conda](/magic/conda). - Filesystem and environment utilities: - [`Path.home()`](/mojo/stdlib/pathlib/path/Path#home) has been added to - return a path of the users home directory. + return a path of the user's home directory. - [`os.path.expanduser()`](/mojo/stdlib/os/path/path/expanduser) and [`pathlib.Path.exapanduser()`](/mojo/stdlib/pathlib/path/Path#expanduser) have been added to allow expanding a prefixed `~` in a `String` or `Path` - with the users home path: + with the user's home path: ```mojo import os @@ -759,7 +775,7 @@ conda](/magic/conda). - [`NoneType`](/mojo/stdlib/builtin/none/NoneType) is now a normal standard library type, and not an alias for a raw MLIR type. - Function signatures spelled as `fn() -> NoneType` should transition to + Function signatures written as `fn() -> NoneType` should transition to being written as `fn() -> None`. - Mojo now has a [`UInt`](/mojo/stdlib/builtin/uint/UInt) type for modeling From 4999fbd3943f66a9d0e3b5063d7b2453b6ac4e06 Mon Sep 17 00:00:00 2001 From: Judy Heflin Date: Fri, 13 Sep 2024 15:08:22 -0700 Subject: [PATCH 1551/2019] [mojo-stdlib][docs] Updated mojo docs to import from generalized modules Updated docs in modules to suggest `from module import foo` instead of `from package.module import foo`. MODULAR_ORIG_COMMIT_REV_ID: f4cb0aab668c9b492c2d1ccffd24548a6bea3e21 --- docs/manual/lifecycle/death.ipynb | 2 +- docs/manual/parameters/index.ipynb | 2 +- docs/manual/pointers.ipynb | 4 ++-- examples/notebooks/RayTracing.ipynb | 2 +- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/memory/reference.mojo | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 1440d8fbb8..74cfb1e86e 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -210,7 +210,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory import UnsafePointer\n", "\n", "struct HeapArray:\n", " var data: UnsafePointer[Int]\n", diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 3d4aa2778e..e7a9ff4181 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -227,7 +227,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory import UnsafePointer\n", "\n", "struct GenericArray[ElementType: CollectionElement]:\n", " var data: UnsafePointer[ElementType]\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 9c4392c261..0bda2759bd 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -51,7 +51,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory import UnsafePointer\n", "\n", "# Allocate memory to hold a value\n", "var ptr = UnsafePointer[Int].alloc(1)\n", @@ -494,7 +494,7 @@ ], "source": [ "from python import Python\n", - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory import UnsafePointer\n", "\n", "def share_array():\n", " np = Python.import_module(\"numpy\")\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index aab92d02ec..a6d9d221ba 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -197,7 +197,7 @@ "source": [ "from python import Python\n", "from python import PythonObject\n", - "from memory.unsafe_pointer import UnsafePointer\n", + "from memory import UnsafePointer\n", "\n", "struct Image:\n", " # reference count used to make the object efficiently copyable\n", diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 820dd95ed9..61dae607b5 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `collections` package. For example: ```mojo -from collections.vector import InlinedFixedVector +from collections import InlinedFixedVector ``` """ diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index b5db879d2e..2c19f6caa2 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory.reference import Reference +from memory import Reference ``` """ From 83733737aa512adf3cfb1a7e92fd77f28c6c49b2 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 13 Sep 2024 16:57:40 -0600 Subject: [PATCH 1552/2019] [stdlib] Make `InlineArray` work on `CollectionElement` As we're unwinding some of the explicit copyability work to get out of the halfway state, make `InlineArray[T, Size]` work in terms of `CollectionElement` and not `CollectionElementNew`. This will allow things to compose a bit nicer in the short-term instead of pushing `CollectionElementNew` around even wider right now. For example, this would enable `InlineFixedVector` to work in terms of `CollectionElement` now since internally it would hold an `InlineArray` data member. MODULAR_ORIG_COMMIT_REV_ID: cf9877f7c26b57acbcc5a0e66c782a23911a14af --- stdlib/src/collections/inline_array.mojo | 6 +++--- stdlib/src/utils/span.mojo | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 5e747e7972..31b58f0788 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -41,7 +41,7 @@ fn _inline_array_construction_checks[size: Int](): @value struct InlineArray[ - ElementType: CollectionElementNew, + ElementType: CollectionElement, size: Int, *, run_destructors: Bool = False, @@ -141,7 +141,7 @@ struct InlineArray[ @parameter for i in range(size): var ptr = UnsafePointer.address_of(self.unsafe_get(i)) - ptr.init_pointee_explicit_copy(fill) + ptr.init_pointee_copy(fill) @always_inline fn __init__(inout self, owned *elems: Self.ElementType): @@ -194,7 +194,7 @@ struct InlineArray[ for idx in range(size): var ptr = self.unsafe_ptr() + idx - ptr.init_pointee_explicit_copy(other[idx]) + ptr.init_pointee_copy(other[idx]) fn __copyinit__(inout self, other: Self): """Copy construct the array. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 45876aaa13..4b5a3955ce 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -125,7 +125,7 @@ struct Span[ @always_inline fn __init__[ - T2: CollectionElementNew, size: Int, // + T2: CollectionElement, size: Int, // ](inout self, ref [lifetime]array: InlineArray[T2, size]): """Construct a Span from an InlineArray. From 7da1a0734d1f27b1bd0c0a35cebb3ca8182c70ec Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 13 Sep 2024 17:21:23 -0600 Subject: [PATCH 1553/2019] [stdlib] Ensure `not` in `PATH` for Linux builds Recently, the `not` tool was upgraded to be required and was only installed on our macOS CI. Set the alternative for `not` in the Linux build scripts too to make the Ubuntu builds run the tests that require the `not` tool. MODULAR_ORIG_COMMIT_REV_ID: bd863f96efcd4fc203736581f19151555b814719 --- stdlib/scripts/install-build-tools-linux.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/scripts/install-build-tools-linux.sh b/stdlib/scripts/install-build-tools-linux.sh index 251e40e1b1..64fd601691 100755 --- a/stdlib/scripts/install-build-tools-linux.sh +++ b/stdlib/scripts/install-build-tools-linux.sh @@ -28,5 +28,6 @@ sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-$LLVM_VERSION 1 sudo update-alternatives --install /usr/bin/ld.lld ld.lld /usr/bin/ld.lld-$LLVM_VERSION 100 sudo update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-$LLVM_VERSION 100 sudo update-alternatives --install /usr/bin/FileCheck FileCheck /usr/bin/FileCheck-$LLVM_VERSION 100 +sudo update-alternatives --install /usr/bin/not not /usr/bin/not-$LLVM_VERSION 100 python3 -m pip install lit From ade7c8c5abdad41b5c142b8a29db80923eb19c72 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Fri, 13 Sep 2024 19:30:28 -0400 Subject: [PATCH 1554/2019] [External] [stdlib] Add new method `SIMD._dynamic_shuffle()` (#45892) [External] [stdlib] Add new method `SIMD._dynamic_shuffle()` This method is incredibly useful to implement fast UTF-8 validation. It's used a lot in the paper [Validating UTF-8 In Less Than One Instruction Per Byte](https://arxiv.org/abs/2010.03090). It notably allowed me to implement a fast utf8 validation 6 times faster than the one currently in the stdlib. Since it's a vectorized dynamic vector-lookup, it can be useful in many other scenarios. Note that I also tried to implement it with calling the llvm intrinsic but I would get those kind of errors: ```mojo return sys.llvm_intrinsic[ "llvm.shufflevector", SIMD[DType.uint8, 16], has_side_effect=False, ](lookup_table, indices) ``` `could not find LLVM intrinsic: "llvm.shufflevector"` or `error: could not find LLVM intrinsic: "shufflevector"` if I remove the llvm prefix. If that's possible to use it, guidance would be appreciated. The current implementation works, even if it's not the fastest for some vector sizes and arch. So I believe if we can't find anything better quickly, we can merge it and iterate on it later. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3397 MODULAR_ORIG_COMMIT_REV_ID: e2b3095316825a6994ad1c4e023727fa53cbebb6 --- stdlib/src/builtin/simd.mojo | 131 +++++++++++++++++++++++++++++ stdlib/test/builtin/test_simd.mojo | 115 +++++++++++++++++++++++++ 2 files changed, 246 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c065ec8fef..af3fa02f85 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1954,6 +1954,94 @@ struct SIMD[type: DType, size: Int]( """ return self._shuffle_list[size, mask](other) + # Not an overload of shuffle because there is ambiguity + # with fn shuffle[*mask: Int](self, other: Self) -> Self: + # TODO: move closer to UTF-8 String validation code - see https://github.com/modularml/mojo/issues/3477 + @always_inline + fn _dynamic_shuffle[ + mask_size: Int, // + ](self, mask: SIMD[DType.uint8, mask_size]) -> SIMD[Self.type, mask_size]: + """Shuffles (also called blend) the values of the current vector. + + It's done using the specified mask (permutation). The mask + values must be within `len(self)`. If that's not the case, + the behavior is undefined. + + The mask is not known at compile time, unlike the `shuffle` method. + + Note that currently, this function is fast only if the following + conditions are met: + 1) The SIMD vector `self` is of type uint8 and size 16 + 2) The CPU supports SSE4 or NEON + + If that's not the case, the function will fallback on a slower path, + which is an unrolled for loop. + + The pseudocode of this function is: + ```mojo + var result = SIMD[Self.type, mask_size]() + for i in range(0, mask_size): + result[i] = self[int(mask[i])] + ``` + + Parameters: + mask_size: The size of the mask. + + Args: + mask: The mask to use. Contains the indices to use to shuffle. + + Returns: + A new vector with the same length as the mask where the value at + position `i` is equal to `self[mask[i]]`. + """ + + @parameter + if ( + # TODO: Allow SSE3 when we have sys.has_sse3() + (sys.has_sse4() or sys.has_neon()) + and Self.type == DType.uint8 + and Self.size == 16 + ): + # The instruction works with mask size of 16 + alias target_mask_size = 16 + + # We know that simd sizes are powers of two, so we can use recursivity + # to iterate on the method until we reach the target size. + @parameter + if mask_size < target_mask_size: + # Make a bigger mask (x2) and retry + var new_mask = mask.join(SIMD[DType.uint8, mask_size]()) + return self._dynamic_shuffle(new_mask).slice[mask_size]() + elif mask_size == target_mask_size: + # The compiler isn't smart enough yet. It complains about parameter mismatch + # because it cannot narrow them. Let's help it a bit. + var indices_copy = rebind[SIMD[DType.uint8, 16]](mask) + var self_copy = rebind[SIMD[DType.uint8, 16]](self) + var result = _pshuf_or_tbl1(self_copy, indices_copy) + return rebind[SIMD[Self.type, mask_size]](result) + elif mask_size > target_mask_size: + # We split it in two and call dynamic_shuffle twice. + var first_half_of_mask = mask.slice[mask_size // 2, offset=0]() + var second_half_of_mask = mask.slice[ + mask_size // 2, offset = mask_size // 2 + ]() + + var first_result = self._dynamic_shuffle(first_half_of_mask) + var second_result = self._dynamic_shuffle(second_half_of_mask) + + var result = first_result.join(second_result) + # The compiler doesn't understand that if divide by 2 and then multiply by 2, + # we get the same value. So we need to help it a bit. + return rebind[SIMD[Self.type, mask_size]](result) + + # Slow path, ~3x slower than pshuf for size 16 + var result = SIMD[Self.type, mask_size]() + + @parameter + for i in range(0, mask_size): + result[i] = self[int(mask[i])] + return result + @always_inline("nodebug") fn slice[ output_width: Int, /, *, offset: Int = 0 @@ -2633,6 +2721,49 @@ struct SIMD[type: DType, size: Int]( ](zero_simd, self, Int32(-shift)) +fn _pshuf_or_tbl1( + lookup_table: SIMD[DType.uint8, 16], indices: SIMD[DType.uint8, 16] +) -> SIMD[DType.uint8, 16]: + @parameter + if sys.has_sse4(): # TODO: Allow SSE3 when we have sys.has_sse3() + return _pshuf(lookup_table, indices) + elif sys.has_neon(): + return _tbl1(lookup_table, indices) + else: + # TODO: Change the error message when we allow SSE3 + constrained[False, "To call _pshuf_or_tbl1() you need sse4 or neon."]() + # Can never happen. TODO: Remove later when the compiler detects it. + return SIMD[DType.uint8, 16]() + + +fn _pshuf( + lookup_table: SIMD[DType.uint8, 16], indices: SIMD[DType.uint8, 16] +) -> SIMD[DType.uint8, 16]: + """Shuffle operation using the SSSE3 `pshuf` instruction. + + See https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8&ig_expand=6003 + """ + return sys.llvm_intrinsic[ + "llvm.x86.ssse3.pshuf.b.128", + SIMD[DType.uint8, 16], + has_side_effect=False, + ](lookup_table, indices) + + +fn _tbl1( + lookup_table: SIMD[DType.uint8, 16], indices: SIMD[DType.uint8, 16] +) -> SIMD[DType.uint8, 16]: + """Shuffle operation using the aarch64 `tbl1` instruction. + + See https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/coding-for-neon---part-5-rearranging-vectors + """ + return sys.llvm_intrinsic[ + "llvm.aarch64.neon.tbl1", + SIMD[DType.uint8, 16], + has_side_effect=False, + ](lookup_table, indices) + + # ===----------------------------------------------------------------------=== # # _pow # ===----------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 559ae6632b..71fbc906df 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -843,6 +843,115 @@ def test_shuffle(): ) +def test_shuffle_dynamic_size_4_uint8(): + var lookup_table = SIMD[DType.uint8, 16]( + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150 + ) + + indices = SIMD[DType.uint8, 4](3, 3, 5, 5) + + result = lookup_table._dynamic_shuffle(indices) + expected_result = SIMD[DType.uint8, 4](30, 30, 50, 50) + assert_equal(result, expected_result) + + +def test_shuffle_dynamic_size_8_uint8(): + var lookup_table = SIMD[DType.uint8, 16]( + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150 + ) + + # Let's use size 8 + indices = SIMD[DType.uint8, 8](3, 3, 5, 5, 7, 7, 9, 0) + + result = lookup_table._dynamic_shuffle(indices) + expected_result = SIMD[DType.uint8, 8](30, 30, 50, 50, 70, 70, 90, 0) + assert_equal(result, expected_result) + + +def test_shuffle_dynamic_size_16_uint8(): + var lookup_table = SIMD[DType.uint8, 16]( + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150 + ) + var indices = SIMD[DType.uint8, 16]( + 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 13, 13, 15, 15, 0, 1 + ) + result = lookup_table._dynamic_shuffle(indices) + expected_result = SIMD[DType.uint8, 16]( + 30, 30, 50, 50, 70, 70, 90, 90, 110, 110, 130, 130, 150, 150, 0, 10 + ) + assert_equal(result, expected_result) + + +def test_shuffle_dynamic_size_32_uint8(): + var table_lookup = SIMD[DType.uint8, 16]( + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150 + ) + # fmt: off + var indices = SIMD[DType.uint8, 32]( + 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , + 11, 11, 13, 13, 15, 15, 0 , 1 , + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 8 , 9 , 10, 11, 12, 13, 14, 15, + ) + result = table_lookup._dynamic_shuffle(indices) + + expected_result = SIMD[DType.uint8, 32]( + 30 , 30 , 50 , 50 , 70 , 70 , 90 , 90 , + 110, 110, 130, 130, 150, 150, 0 , 10 , + 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , + 80 , 90 , 100, 110, 120, 130, 140, 150, + ) + # fmt: on + assert_equal(result, expected_result) + + +def test_shuffle_dynamic_size_64_uint8(): + var table_lookup = SIMD[DType.uint8, 16]( + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150 + ) + # fmt: off + var indices = SIMD[DType.uint8, 32]( + 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , + 11, 11, 13, 13, 15, 15, 0 , 1 , + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 8 , 9 , 10, 11, 12, 13, 14, 15, + ) + result = table_lookup._dynamic_shuffle(indices.join(indices)) + + expected_result = SIMD[DType.uint8, 32]( + 30 , 30 , 50 , 50 , 70 , 70 , 90 , 90 , + 110, 110, 130, 130, 150, 150, 0 , 10 , + 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , + 80 , 90 , 100, 110, 120, 130, 140, 150, + ) + # fmt: on + assert_equal(result, expected_result.join(expected_result)) + + +def test_shuffle_dynamic_size_32_float(): + # fmt: off + var table_lookup = SIMD[DType.float64, 16]( + 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, + 80.0, 90.0, 100.0, 110.0, 120.0, 130.0, 140.0, 150.0, + ) + var indices = SIMD[DType.uint8, 32]( + 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , + 11, 11, 13, 13, 15, 15, 0 , 1 , + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 8 , 9 , 10, 11, 12, 13, 14, 15, + ) + result = table_lookup._dynamic_shuffle(indices) + + expected_result = SIMD[DType.float64, 32]( + 30. , 30. , 50. , 50. , 70. , 70. , 90. , 90. , + 110., 110., 130., 130., 150., 150., 0. , 10. , + 0. , 10. , 20. , 30. , 40. , 50. , 60. , 70. , + 80. , 90. , 100., 110., 120., 130., 140., 150., + ) + # fmt: on + assert_equal(result, expected_result) + + def test_insert(): assert_equal(Int32(3).insert(Int32(4)), 4) @@ -1706,6 +1815,12 @@ def main(): test_rsub() test_shift() test_shuffle() + test_shuffle_dynamic_size_4_uint8() + test_shuffle_dynamic_size_8_uint8() + test_shuffle_dynamic_size_16_uint8() + test_shuffle_dynamic_size_32_uint8() + test_shuffle_dynamic_size_64_uint8() + test_shuffle_dynamic_size_32_float() test_simd_variadic() test_sub() test_trunc() From 49209ec79da9f0e4d0a3c6b195465c4b7ad11118 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Fri, 13 Sep 2024 20:31:45 -0700 Subject: [PATCH 1555/2019] This adds a mention to the Mojo changelog that there is a known issue when running a program containing input() directly with the mojo CLI. MODULAR_ORIG_COMMIT_REV_ID: 2f68a7630443ae0509439f481da529a08b4d3b36 --- docs/changelog-released.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index b917ee6c6d..5c7f78aaff 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -336,6 +336,10 @@ detailed information in the following sections: If the user enters "Mojo" it returns "Hello, Mojo!" + There is a known issue when running the `input()` function with JIT + compilation (see issue + [#3479](https://github.com/modularml/mojo/issues/3479)). + - [`print()`](/mojo/stdlib/builtin/io/print) now requires that its arguments conform to the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait. This enables efficient stream-based writing by default, avoiding unnecessary From 809f971b4c1af4e30f1fdb3aef29c4c4b2916e0f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 15 Sep 2024 18:07:52 -0700 Subject: [PATCH 1556/2019] [mojo-lang] Teach comptime interpreter about LLVM intrinsics. This teaches the mojo comptime interpreter about LLVM intrinsics, at least for simple cases involving integer operands and results (it would be straightforward to extend this to other types now). This fixes https://github.com/modularml/mojo/issues/933 and Fixes MOCO-138 and Fixes MOCO-504 MODULAR_ORIG_COMMIT_REV_ID: 900128df66da907a0bf61a3b2affb730ef253e17 --- docs/changelog.md | 4 ++++ stdlib/test/builtin/test_uint.mojo | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c13541bdbb..6309b54dd3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,10 @@ what we publish. ### ⭐️ New +- Mojo can now interpret simple LLVM intrinsics in parameter expressions, + enabling things like `count_leading_zeros` to work at compile time: + [Issue #933](https://github.com/modularml/mojo/issues/933). + - The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration setting that can be used in conjunction with `mojoFile` to define the build arguments when compiling the Mojo file. diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 45f47c7a22..1a782c52c0 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -14,6 +14,7 @@ from testing import assert_equal, assert_false, assert_not_equal, assert_true from sys import bitwidthof +from bit import count_trailing_zeros def test_simple_uint(): @@ -237,6 +238,13 @@ def test_pos(): assert_equal(UInt(0).__pos__(), UInt(0)) +def test_comptime(): + alias a: UInt = 32 + # Verify that count_trailing_zeros works at comptime. + alias n = count_trailing_zeros(a) + assert_equal(n, 5) + + def main(): test_simple_uint() test_uint_representation() @@ -260,3 +268,4 @@ def main(): test_indexer() test_comparison() test_pos() + test_comptime() From 9282ed2c6564662317d3c4dfab6dfd7211759a3e Mon Sep 17 00:00:00 2001 From: Chad Date: Mon, 16 Sep 2024 16:47:03 +0200 Subject: [PATCH 1557/2019] [stdlib] Use a warmup time rather than warmup iterations in Bencher A minimum warmup time is more useful than a minimum number of warmup iterations in benchmarking. For example some GPUs take about 0.5 seconds to clock up. MODULAR_ORIG_COMMIT_REV_ID: c11e864ecc216406f376e72cebfa73b40d8791bf --- .../algorithm/bench_elementwise.mojo | 2 +- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/collections/bench_dict.mojo | 2 +- stdlib/benchmarks/math/bench_math.mojo | 2 +- stdlib/benchmarks/utils/bench_formatter.mojo | 2 +- stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- stdlib/docs/bencher/BenchConfig.md | 18 +++++++++--------- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 562ec85589..275bbf4afa 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -48,7 +48,7 @@ fn bench_elementwise[n: Int](inout b: Bencher) raises: fn main() raises: - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=10000)) + var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_elementwise[32]](BenchId("bench_elementwise_32")) m.bench_function[bench_elementwise[128]](BenchId("bench_elementwise_128")) m.bench_function[bench_elementwise[1024]](BenchId("bench_elementwise_1024")) diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index a7c265a03c..39aee76006 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -34,7 +34,7 @@ fn bench_stringify_small_integers(inout b: Bencher) raises: # Benchmark Main # ===----------------------------------------------------------------------===# def main(): - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100)) + var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_stringify_small_integers]( BenchId("bench_stringify_small_integers") ) diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index 58f249f708..2a37df1a36 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -115,7 +115,7 @@ fn bench_dict_large_lookup(inout b: Bencher) raises: # ===----------------------------------------------------------------------===# def main(): seed() - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100)) + var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_dict_ctor](BenchId("bench_dict_ctor")) m.bench_function[bench_dict_small_insert]( BenchId("bench_dict_small_insert") diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index ff56002cf7..877c2798a2 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -80,7 +80,7 @@ fn bench_math3[ # ===----------------------------------------------------------------------===# def main(): seed() - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=100000)) + var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_math[sin]](BenchId("bench_math_sin")) m.bench_function[bench_math[cos]](BenchId("bench_math_cos")) m.bench_function[bench_math[tan]](BenchId("bench_math_tan")) diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index af13596c6f..43b9e686ed 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -58,7 +58,7 @@ fn bench_formatter_simd[n: Int](inout b: Bencher) raises: # Benchmark Main # ===----------------------------------------------------------------------===# def main(): - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=10000)) + var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_formatter_int[42]](BenchId("bench_formatter_int_42")) m.bench_function[bench_formatter_int[2**64]]( BenchId("bench_formatter_int_2**64") diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index f152305b4d..a5a81113f3 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -220,7 +220,7 @@ fn bench_find_optimized(inout b: Bencher) raises: # Benchmark Main # ===----------------------------------------------------------------------===# def main(): - var m = Bench(BenchConfig(num_repetitions=1, warmup_iters=10000)) + var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_find_baseline](BenchId("find_baseline")) m.bench_function[bench_find_optimized](BenchId("find_optimized")) m.dump_report() diff --git a/stdlib/docs/bencher/BenchConfig.md b/stdlib/docs/bencher/BenchConfig.md index 65f4092d79..cebb46f13f 100644 --- a/stdlib/docs/bencher/BenchConfig.md +++ b/stdlib/docs/bencher/BenchConfig.md @@ -14,15 +14,15 @@ frequency. ## Fields - ​out_file (`Optional[Path]`): Output file to write results to. -- ​min_runtime_secs (`SIMD[float64, 1]`): Upper bound on benchmarking time +- ​min_runtime_secs (`SIMD[float64, 1]`): Lower bound on benchmarking time in secs. -- ​max_runtime_secs (`SIMD[float64, 1]`): Lower bound on benchmarking time +- ​max_runtime_secs (`SIMD[float64, 1]`): Upper bound on benchmarking time + in secs. +- ​min_warmuptime_secs (`SIMD[float64, 1]`): Lower bound on the warmup time in secs. - ​max_batch_size (`Int`): The maximum number of iterations to perform per time measurement. - ​max_iters (`Int`): Max number of iterations to run. -- ​warmup_iters (`Int`): Number of warmup iterations to run before - starting benchmarking. - ​num_repetitions (`Int`): Number of times the benchmark has to be repeated. - ​flush_denormals (`Bool`): Whether or not the denormal values are @@ -48,7 +48,7 @@ frequency.

    @@ -59,11 +59,11 @@ Constructs and initializes Benchmark config object with default and inputted val - ​out_file (`Optional[Path]`): Output file to write results to. - ​min_runtime_secs (`SIMD[float64, 1]`): Upper bound on benchmarking time - in secs (default `0.1`). + in secs (default `1.0`). - ​max_runtime_secs (`SIMD[float64, 1]`): Lower bound on benchmarking time - in secs (default `1`). -- ​warmup_iters (`Int`): Number of warmup iterations to run before - starting benchmarking (default 2). + in secs (default `2.0`). +- ​min_warmuptime_secs (`SIMD[float64, 1]`): Lower bound on the warmup time + in secs (default `1.0`). - ​max_batch_size (`Int`): The maximum number of iterations to perform per time measurement. - ​max_iters (`Int`): Max number of iterations to run (default From 957f5cda7fc177ba74567cfd89ad4a49dbf2c5b3 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 16 Sep 2024 11:25:36 -0600 Subject: [PATCH 1558/2019] [stdlib] Install LLVM 18 in Linux tests Update the LLVM version for Linux builds from 17 to 18 for the pre-built tools used by the tests. MODULAR_ORIG_COMMIT_REV_ID: 891b1d4e78c1bdea748a1bce5a7c39feaaa9179f --- stdlib/scripts/install-build-tools-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/scripts/install-build-tools-linux.sh b/stdlib/scripts/install-build-tools-linux.sh index 64fd601691..f3f15d65d5 100755 --- a/stdlib/scripts/install-build-tools-linux.sh +++ b/stdlib/scripts/install-build-tools-linux.sh @@ -13,7 +13,7 @@ ##===----------------------------------------------------------------------===## set -euo pipefail -LLVM_VERSION=17 +LLVM_VERSION=18 wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh $LLVM_VERSION From 8ab463cd1060cd72a36baf6bf3fbeb9e6a1220cf Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 16 Sep 2024 14:30:32 -0700 Subject: [PATCH 1559/2019] [stdlib] Remove git-lfs MODULAR_ORIG_COMMIT_REV_ID: accd4f56a6dd25720644c43d5603c473a08ba5f9 --- examples/notebooks/images/.gitattributes | 1 - examples/notebooks/images/background.png | Bin 131 -> 607043 bytes 2 files changed, 1 deletion(-) delete mode 100644 examples/notebooks/images/.gitattributes diff --git a/examples/notebooks/images/.gitattributes b/examples/notebooks/images/.gitattributes deleted file mode 100644 index 24a8e87939..0000000000 --- a/examples/notebooks/images/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.png filter=lfs diff=lfs merge=lfs -text diff --git a/examples/notebooks/images/background.png b/examples/notebooks/images/background.png index 234cd5285c875dc281ad1ace3dd64b2427e651a6..5b8d32f8f99170ece26120e5b985be0d9dc9fde8 100644 GIT binary patch literal 607043 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%0A*>$W1RK?h?H_QK;Z38&v_CP82)Ra{56KQDqtbe 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�>}GpNNNNb85b7E68E3OA91$E|A=+6< zQaGMM>?MU2d!aSdD}WTET>8`k^$ig^E(ik`!YiXh3~eD09EhW{Pgi*%Tnt{aPNAeK z^|8lCkT;q@WorTxndS({R!LPM)m&Z(BfIc8<(_BJG*S_{O?^3Wa=}Wp0 zvMHArF3J>64rKg`AHmQr_yOC-lZqSF8H;a-Rx@NwpK{yAnv|t_a9&BuWefS> z)t#o|AsLtQ-a9Odb5`@>XZKa7cF3j*V(ns|lQqZ_FXjVP%Vp#70Q@<;4^@9gTUCr$Ne&8QkWRe94%3ZXSLmM%QzXCa zzi0LKgoE4GT8OApPha>Dv`a2SDZ&d;=Qxao%s9QL{VYL+_N~VTSx)lBzEnsmwHA$+ zQ6JuJPlQXmM?5WB6S<^#b~t5%w{7(9OHiqmOCVhR$QrvdF`GBC8gRPPRo^S*u*a%b4=BJ%T8WAs53G^u?ZDDGY54&tdFBDzag7VK?;L zlOd{QiTrFC&w{2V*55_eoJUr*di0goS~WZstiU;Z-4^ae-M4+)x7h%g=B4aHPJ^w< zsI@*gkRD7wdiNtL-b*ZUEph2^M0o+wSyug|(2D)6y`5(9>5#S5q$tP&)XHn9aY6K2 zSu(_CR0F%39Rg7_Md2_sMj~8qELCC79Un>icwMZQa{?VES*Woc0zV!tJET(KL)iAY zzNBzPR;M0bOHDtQf+cbd@#N~V(56cEUC`6Nk03;=7-i#;B?x4whf&5{rs>g#OM~z} zNgw|_3m*O_Gg?cb*Jw)3&DJ-p0DZ=z&RY;tmdK|ozRScgFFVStJ3xjqy6*Wtx8DmA zw1!Ra6t4y$TE_?n$kr(6@fk6zKaQhAlh*{%-aD6T!T0OX~p zi^%IkDXg9^&$xU!->nQ;;OkMRH*&?CcsiS}Aun?LllhWu;=Mb0AyDWTg&QV5+LDG; zTWc8EnZocS{kQ(s-}+;J?2rA8zwtM`u(SfzJe|luu>(&Vh*O&_`wEzA;cqkqUYL-| zcA2`kF2i#c?7T{%U|8D;5~$ZOPov>Q{Jq0;%5v^i#RG-cM?|vT{W&dBg1QuDo(cHQsA8SLM}8MulSw$fBw(^dEbQd8bB_fmDP|<+mJEA8Pk8e zQ7pB~`2(*o#_6gCI{>aQE{LJtxmu~RTR_NJMu~<9)RrY&Ncd=s1!m}LEAJ$;!|fqe zd7~6LFeCEj%aF95%i6jPt6@k{?Z|9B0&;i0j+{@@S(pl5M=g9~1HaSRJ5g>Q~|^;Rae zWq&k?Xv14%kB0IFQqXYp6@QPot;V80lVKLNir*kQRsZ9E{Eyz5R%d0WL*HDQl%zm? z^>CS^)c(|(pU!h(>f!Mt>oWvu4fXA%F}F~0PUb>hH(CmPw2C21DzFs1tI+iGRt5qu zq>X(ZVmMBLg|M}9y|>4-XdXCRUc4jdr)k7HS;)$(c@NmJAYQx6f7;m$e(LHmqB7#h zyCP9d8P1^Aek)!G+zEorc(z*1G&68wl44WPdn^u*b1VAY#)CKq{ea4gRfZv^hG1(& zBRHd3#ChyX9bd1t36~Bcg%-jH(Lbua7fD~8eI(^9Fbm4oTyN1FJGV0*r2p*C{;W!N zKBg=@1(VY7akkVHY$wR7#SsXHq`&~KPw#o;i@0rx zkxwH+U+_EkYW$9uL#B!9Fg@ly1ub1$1y}*1*zSN{F?yKm_2e0nO}1Gu3QS>4;3kE+ zA-mz2UzB0ivb6)FPPp!X+MnOzE zT(!wg6Apamzu83cvS(yRW;e`>uLVA43D=u4uXXS5keF;6g@`Kx0`Hb({1tF20}<*O zwF`*<&hz5Gr)GRT9f!|dUwO}pAi&j6H@@CM58-`mG3$|AF+CgD>QP{8!Xpr5dm-mr zB9_Qd<5HN$Wk|tWXbS$3ANi5L^q2mU2O4ji;lyOva%{c3X|bKF(phayMj{%)d;VJi z4P^<@iv{+4++WzpMO<6RX&^MrKx>Yq=#(1hO08Fw7Zu*=H{>d?bbe9e`M`sM(={o5 zNlCHS#BL&t;nZqCfp9~1!w@#LwxVCS{=`rG1eyyLX8iS-?1p(8)`!G7y~8bnh4mNQ z|KUIUheA3W9#Y-8TOak*N&!@u)-b~osSUzFKGS9E%cy53k|Bync$)gNO)x308QB{$ zDz#i}%4c{psz|u-F=sQf#S(Pg$Em>~wL}OS^5(il-W%|p`BZH*TY{kd?-JjBE*9Bm z0v;~gV@p?&a)mhic6EYONr|Qt8OUo2D%?yvxGRj2t7=cR!pAJTtEInZ(g~i6c@%H|ca-z$Pvtk-CgKpd@1`#W z9m;Tt9IsD4`Q$JE<-hFe=fG9TsT>(!gCi)odMeTo4hhs_>H{ap(75D1^ZD zN4_oDyQ}CK9ypiTc`iqQ+^T=~!8jyA^;8Vw)Oob4Bs_iBc|&iSeEYY5dmF=nvmn5g zed?SF<0MrX*=QMh1!tc+i@kt`$W9_PeD$ke{ox<}Vc)x(T2X19fhXa_Q!7e&Crwpw zZ-n^~kr$Sa6^_in6s|F9!0-YahS+}F8{wCbcN)+9OD(~L%sA6M6Zy?h!Jo(m7 zE99cit5r~Tv?iR3m*H-7S$9KfYSKhit6pp&j5xv(>c=dR-kO@#Z~JY(&DyziLY8PD zGZyt|R6saeNd1Yht1Yln*_c|hZn%s`8A=8Ll0wE3}f|U`qg+)v4wrTF$ z#&du>DBQEMZGb7zP?UyK!4NKp@Cf5T+?fm+2+c?@Fc3ci(3hKmyN$7{?Uw8=4$&$y zyIw4#o}n;r3+cc7o|g?V zK@Z}d3)!})DUUz$LKsDrB|HA6*|HdpSzdRkzV_CtI(EH!$*W&G11*#b2$$c?xYaQ_ zB8;j!qY8v;Y&~!F#E&rj8HvPaFE;$7HLvs^y_k`c(#vkX9lq%=oLaVN=3TWAGj&98 ztU%txAySWdx@Jr2{Aw=xLTI`c;fN&_covL_b4KxMLe>{G4p(h+#N>^@Rh^9CWrsv# zNUB<|J^T?N*K@xj$R(tx$R-814Sb!Qp%PNnvn=pJAfmK}dUmq)g`9Q-xRv)94t|S+M_J5jsSsZBD9V4z?zE=OTP{6Nm7G4^)I5=EdudYobLLw=g_B5Y0 zT9A_;a*|Ik+z=<4GTd2I@{j-VKj!K_hyZlNc|U7y$(Fsm`Ys5bX}q%P2k5;}jJ!Vn zhLx&^R0)Enq*jv6rIjw`o$|E3htC+fW9N_a_;WYk`mNvk*Z=xo_ddPtJtkvPqFRTA zUuX?g)l7>$$?6Y015djYWr@tr;Hr}5Ta;WQgq-Py2w9fX9#~W<1~MMiF5wF->a4u* z_|tiI1#q-nXHuZy=_(7PQuP|Jyk~4onh5E{VQcre8(uh?eXfP;AsT{Hz|mwR$mLym zZ+v)zz?BfL0f`sYP*06QB^efe_W0Udl?U;t!0rmu*FBsj@E2bIoT87m1Qk~w0jSwM zdWe1xOfpqEgR3(H!H~kPQ-;81*h)>xFmJhLBe)-Uyr3gBJi^(RXdzrfMhnr0T!z3Y zH$lUYg-q%+x`l^y$nllh>@xJEczg8Z+F?GWN_Qe}w3|z0_GJi%ydtGM-rH>7tu4N7 zYBQq&%=Tud*Daoj(ZWS_ zllaCrzQKjr^SyAcYCd~W$(HVP2_ajb7n7k`Tg6Ad6?Fd-D}6rLNEc zG?cEP)VyWq4L55~09_CiW4C+C0T%L@B}A#h(fqk(2t#1Kz#R@8nh2>?+e?~{c(I~V zxReABIq^0?A+4?0%7_odw-gFac723;oE;G%`0ZO{1lMQ>C1t}j3!EASNc?&VUSGMR z_&!Old3Wd`XWP^mWx2?XJRGv9Cca_hnACVf6jS5MtLFm7@yVXcg2(ROE}0HmRap=_ z!7gH$CNPe0BH4?Y!Ic7}j3@Kaj^BZsY$>)lk5O+ysA-P4IWiPe&(PqqI4=g$Wb1i5 zqeGfFGDF{K5)C563m7`GOo8zW*{V?44wHkWq}EdntG33d?m4vtO#vOs&5+S-<+#uljHm%?ePS z)C{iEen~JKQ(o=$5Z*_E2@(-o&uG~PGKxB5{4+%C>*2^}g$?!F z5`|CXGCX5v<3yvQU4{_MHV+rWu;n$h|Md;^_J-5keV@H|+$gxt+rTloN)68r?+Ugh)!Pd@-m_pXz4%>%*^t$pIz-FW>PL_sZXxoH%z!k>iVM+e z6r4p29F0K@;)d~W|Lwnh{0`DB=4b?`39cSGz?LR%%IsT?cURtq(hbQNZ6b_Y*tS z^{A9rgK!xw#YZ1~_!;xmAyqwH|FuXyHwpCQOADgOG5o8r4ydQNXr){ufIOVKZ9tN=UN1R%Rx58J7bAW> z1x6|EdY^9lT)d_;!YKu}q!y$tWSHj3Ic+r~=qryV?~-Ou0p1+p15dTt)wyBhGi3G* zWYy|Vgk4)nhHNyY#$QPInY!5_n4JS8){$~G^B+w5Ajo^+=S`wSt*Vf8rq;^9Z3(|K z(cDq!kIAUEg@ov9>Ca|h>f!pG@+H#G%jOM_yhKy86bSHi!u5;lT>F`y`57;d%{uXg zGb+3DsD%w#uBASs*x?f47osn3%uA1N4P9h*bP$A0>cW5fkn)--WLMHefNgb#BH0nf zw}8M_K$I(nHCcG`>L${~fzG@?A4dAE&-4Qo30IQg5UW~|TnO1sCk~Mr6FiO6E8O|w zi=cjAZTQG&ys~HuIgQy7BA1=3+8Ejd*_9eC{IMr!|M)Xp{sTL*y_GZC1c$uc zxK;D6o<%*21kPpK;BT0%&Nf&>Q$x_V$1Z%svz<~WPV*ejeu*F9^s+b1SbuZ8oBm@W z2(R9nFE1lJSN#b-%Ul*-c7n^8cN|`=4a2a*ostbpq=(}d5(i;kyTvw!OCemZb(#x- zvm?V3G5q}P!(RZX-Zz=d)3Z?}>NU6$RjgGXkR1pyJ^iM^%ZA{zB9I7E$m>cM!LTNf zT3-d3&O7Jz-(^h1F#Xc$GvJyFsCcJAU|Or^5xfQ8%hn6WXQ<74x{fQgoBQt4=Vf$G z8Vc#CJIo&J?1so=GO|x2DTwgdBZTD2)@J~J&Mn*!OI={a{jT zhIMdHvTYnnU#jUYb%`o4P?mETXwf{g{)>O{FFgHwQ`zIfS;INO`1KXTrm2$Z7YHwW zGb$-gF2#3fB1h|Nqy~hfU|0x5YY6eM>vtd?=)ECF3a$z9l*2Hcawbi*z2;myea46;VqHfj3L6a+c zMzO%~^;Oj{o0?&P*jk03Ypmih10qRp=O#CtYb)*7Xgapl$8k}KewD9S0v0E`r zhQ`oTWw#(O76?~APJxD#`UNw}t|Wz7!)gGY1psTUZ7#^gI74QS1`+Nq;38&K-Ea+? zc*}y&)q_z~NvD9OrK3L$Hd@|nL$i=VyHQ_gLY#vhjg-`ReG-@Y1m9wxf$WCsXC!54 z{q^VU6d$NCxMrwFGb4uHB}nTbB+X}Qd&%_@{)))6A;8+EvmxVIHDJd@k7GKEWi-u@ zTxb?#YJnTZ)Tfrhg;SP7^cq6@+;JCZ2yC?(uIhB6>DgQjJ1%-)3Nd*#IFMkV{tIcZ zZQ@G;MT9WSO5Oe-gC1DXjAnZYG;Ect-nFTvfw!`^`Q63qiEyzQ8l&Z42t37kxtwMv z_^ho$S97ie<7C&DdVL2h8gTe*QM`s1qZB|*wR-n9G5b^Fc*QQfaNQ++-<%O4};`N5bvKI?%C`!#HK5tTvsflPpaMms~mA8?; z`@6r}Pr){fufkS96r}8W*9a4ov|JUF-BP$ZBlyc#f8tO43C86w%~3!jn0Q5|7J^Wn zWu%rN#p`V};$vp(O(#OB*_&YGWk;|q2(?K)8Y$rKBqDMaUr0J|4H*b|R6CI~wUFAW z25ES@MmVKnmQevq?Kpa^>n%w&I|^vL0>@{xCO{KxC);m^zTWon{r~f7RnyetLj-Cx zO9$5=iCbHjL?1CBEFB~J9{TCwD>fJV&bnRCD^)sReVhn-1XATqGb!YTd5Z!jsCZxA zv*i}Tz-fOvqnrLVf8P4^*~+WJG$CBs)w3dJ;An;;G#ueHxTrZIz;4)iE1>@5e>V|(cJ*p&b{nqL)@s9746)u`aWQ0AB7u78JjL2k-THxAA~OtyRG5&w8P#(S`lgFX zv1ai~JzVCfmmUAT+k62n$+vW7Oi-%|TS6m}G_S-iaf zOj#&v!fR}J{kaq;{z?k8nBx$c&gqS&FYov#XR%Y55x-$lQ+UOYYno_miw5ih!T>_7 zyrp)L@1uKoD-10B6cN&CzVNOCIBzfPm73>zdOvdGHyU<{H#N;fS>{>kDr2a)67|iE zCZn0?TL^&}g``k+xb@mRg&1m*(iy%|A!qpT3|@iWfAxyIn<6q-QTo*eoNHdjbc$+N zKmG+Qin60vikRk7x@HuOYgynC^rvPvS~`!e5lUw`CPZtf&p;T+cu6oF1cf;+h6;;c zov#GWb?Ou8qb}QqCfDl0Z>Y~0k`$1EFV?r{99CZ?a)tHYHreT*sm*Zw8G(i!2x_CSnNJpOsV?kb!AZXVfw}(EfUo3FtcD?uH)-$EoZI zGJe6D0+zkw7*cuF?>GWm1U7!h4(E+^mV!w9j16fzr~IJYFL8a3?w|aVf8xQ*=~nhL z1szf4on|1~1ZQ8!r_{y9j6Agq;qwnG_|!RboiqgKh|U6PqN?A@v(IxRDLN4@WqjE1t>@cC z-3(RMy{&nkOEz+#-t_dElhEUco-I(1yfYJGdK#m~mMC(cNllyq*BHnre%c5`+=*QR z+Vl|h6n;x~Pp41krugm`OOFd(qQ`VY(y0L9@GfmMf%2+h8G!`?XT-0(TPgRz`ef_z z3ptr+!%y!fQtE}Af-l)=Qs^C~^jpAlvX9|0D`OkL?Cfk!?54)3RhYimf#HT_;Z=_z z+4RHLUjk2@op^>a+F%KCHK_`2=xOqtDcg?SaTHjSlxy`=t)V9ye}U`ge(vYcM6H-- zjWU)_PefG3lm$T(%hk`I6?01QVn#^Ez0egWu-%nHeZQf1L{5=Mdr9Th425LVbS?AF zu0*nwl&gaE?CPmE#1W-W_Ay;ZObTcgK^wln=|dFYwU&+AF?~sPcp|dYNO4V>5k9_Q zb{u>q>6=L52+<7vbzN^v;@}$&Zz6_3O^C%*Jj2i?@fxpLq+GgXF9p|-N`-6eT=Ozc zJK7M#*$m+dXrELYP-E*CyNu0FM%U*!clxaxIgTNM1;K}S_a3Na8$$zvR4k5B|QJY|MeB{3cDK)7XlP&zj+2sn1P(Agnm7kgR z$HY8L;6%2(an9JcRQUNh%V=uV&{6AHD=Y)Z>o`_3Tf3|V*CXMZ>0t{ku5&2(#Chb zXFDj}t^Bl-VfZpg6g~J)soYaZ1we64s z$7EPlEt-C}__mN-lD>tFfGSGwSnN(7l88eHttHhQ-M&(xpa ze!5Itgk-%ShJp2NSE~2pNF}zD!cD+1GCPq#vtsD|Z7jW8qe(IZ3eiW~QYKhW=ggAq z`f-LZfZ=*Y`XC-?^*sqmnp%8$arB1uhB%1?CKZ3Q4NoL-{6ac~Oe#*`a@|?Cr4C_4 zV626lfw>-;3z@Ol<||8y;0)lHuN#;if*o-a5o_2qi+a714k+e5`7No(zFTd)kl{(6K{^oGcOEq!3z=sl{0qd%)AbHx%u?Wk2&~ zb7`ex&|Ff6bgsy7c2E4C{{5i|dwZ3nbM{<{AtlxYh&=@DmhYa?5rIQWdMeCLM_(YO zl~;*LQTB~2cU+&a8OG6@OR}|p2yb3W}L*%pO?h|Fy&xLPTUf?pa1NG|JF%4YE zI3bT-f*Nloq5{d%!Niu!;eUkRc7inokqM^ik+a7zkQJe&d7ZdKknkFxMb@Pb@8 z61m>`Fwkcs{#Jxbr4$Q^e1^}6e7yO8{?Gq$qjUwc{w`nN@-5$@_r?c; z@8aouLr*VPB2MX1UtwBay)D(97hzux7cq;hv) z8m4d&p>v@{u3r{I;f7i+!;WsxD-lXjKjw@S>VfC5a6Qd4oDIqASc+0DP&08Z>jTsl zh-|3Oz|0tU_dMPIvd{iP8#$vaar9@>iA-U}T&H0=p8ow*^ap<62V6<}pDdT>xP2w{>|p-)9vBdjAH$7r{k6G8MLkJ-rnmMMkcB&ZkAr1#eSTlAan@N09ea zn2lCcvc*ECIW-23eor=qJlhxLt#neF8VJ@5Ed`M#+t8Pwcw5p(!BWFhU=(XO7mz)L z6Mqzpe5>sKaOsKdH~z-o=&{2?KbIS=$L`+kHl$gi^#d!Tq5hS&dKelhu~o|!s9z$( zR?LlGZTPP!UtD+j8{gDY9-+L3CoXCFz;Jy`J@0*yo{nGz!?ip8c2i72zi>4#sfo-5 zELK#6h9fuBUzp_$9C^kWH8Ap|WQ+z^XTxMGhS$(DsC(Fskma2K06+jqL_t&^_yd37 zOJDku{}SE?c6%Ekal`Frd&aPLj(B}ygRfC*H7cOM*<2K=gzXEclG<|JR!UdMxpWhu zQxnSw*Pr&l3_6>|V__Ha8;4CH+3Ktwwo2IA>JQP^q~I-<0i!AOCUpEbAl7ycyM_h7|yx zomxFmJH6~PU4r$Pb1mv5yb^n%Jq{_?ay@GU9|Iv~I=sLZ|8^Mei=ap70O>Pgo{1*H zcs5tFj>f(W&+sXnS?=ZBx_JvZIm>&B$In&Yy{153Gn(dU4`kG)KV$gRLKHA5c3z0> zC%dZ?nqikIpsnbf1-jw^|J#52Z=ZbfNiPXZ8FJ1DLS%_5Mo%z1*XQX!-4h?6qCR!# z6|G;PlUl8O}PHCUT3pN8J*=*J=LjrgwR5RyS;FrAX(D3Yph@*%31IIMM1yjzC;;hAFg1XF03 z_f#IA0bfb%H7TZsW|Oo}I$mf%97Z&FN0R~M(#}LvV<1o~Has;Uz#7~0y^F%H|MkD# zyPG`^p~dI**yQTK2odG2?snG@AiI{h@-#>n9blRn6|6R1v4-{Gm!2!Hq-}}c`+I-y zum07)8cii$26#bIg$q2B0!yTxOBwG2pk={Kp>T)|z-GYlwPX_+Sj}9um~k%C$@Sho zWz#$pkrKFEZ0Q>2g1Cq6aEsO66?OYeoLy>o^EJG)p8md*%|CtQTvlLj5)~dtdH#z7 z*Yop!StTVZ^)gb+UXMT=Uas&nioL1vR?osek3snoUU}-Nq`c;6c7ggWTAV~SWgDId zPW%Kri03RWc|X%|D^O~~O=O7dG`Sc99l`2>sIa6%l$TxqnM!({ES(RR{Ar-=%=mEa zOlLT$Y~CB`-DQC{gaI5m1HwB(U_-OhF8t3kB-m||yn~qb!!nzC^I#rVrW$3nUm+s2BgM!m#6K!SSs|`|x!#|U8 zo8HkrRsT#X$V95rY}s9IDz2h=wG$C#Ej-wH9N+5TbfrtPeib$}b4`fh8yf{o;(DhUMu!d zz!FvYIZXdd2U(UeXJEEn3tZnSIW8Jb3cwCo`r*}l3JNi-508IxW%W=+3BnSswZWK!>%?FYB6Q!)oWF#XS>9}*_zn;3^oNP;LZ2$xSV-sr47Ko;)=h-*pmgv#Am-Zxbde1DXvVdNY_@NcP;mN$% z0&ByU0>Zwn;Bn=RCWK)M4GT#pvfdCLGeSd1yGX1Bw?u{!AgUD$A&$w0@G{0>YsewP zA-83{SA{SGfs}@DHuyQ59p6w$BI?YWi#Q`2r(!Vs>bd`n<=_09f8+9QSW@8`dToW- z$?PTww}nV)lvfPH(DvTfqA-`5?OmkRmU4swXHPJ3y+pht^mq#Fn@I3NGH$$g+kR4? z$55(PF%05O91?CwKLX>P|LMHr+7^nF0`PoR0OlpaRsE39*{H42WD}H2RShAfYYOFU z59GoJYN~A_dgSrBKDfxo*b=#qlqF^0GKMsfh4Vsc2!>o5#4uaW4vC|$adqXH_nqg9 z`L_b5weps|+D4e6o?KI4Y$@7E$iOiZKPQsHG~aF%cuJM46xn(NO)(BxjU96LJkwPJ z{WC?pWW(9cbT`(9da28p!us(~WOWXlAy*+8ZFps8_@MGv{EA;;F8Dw_Da-_I6=1L4 zxl+&rtB|2pDnv6(Z3TQz(Wg_@5X;JA%^uzS25di^sv(Zev06DaKU*^sp&27 zG+KgI0sfXo?W9$VToTbSY$^4*LKd>{2n`2D6H=5O!dsnp_=ae?T7-rbaCbhPY}GS_ zj&>^NTF*QCV&tkXH9~3)Pq5g?Ren+1u;c4*#rP~0=mzfr4mhRW7@k-2!pBbm7@kx|6Ovb7%Uf*7J?@(h zrDqynr17z}Kgo}8t6)}@zRSew>1;|C&5Fe*tMVa+_vO636@ff{HIUV~$Szk#u4jAv zq8uIn(~YqVGBGkvkY3|{MC&J!z7K?w1>SaAAkHY#4Xm)QD8SscARN4iY?F(M_a)V z%@+E$Z~Hc1Gy8jg@9%jN_i{i_GMz%|FT>@6vm0U>s;~=-7v8q9&3TDUa3N=wFUls~ zlIpdciR=olB=gmrGU1lgf`|p`4dF^!6eHIcRp%}JJ(qea!vSJsE3lG!_0Tvk8F#M55$LjGi+D^Sg;k`>IiyiL5*r{D@%S%ezf@eSyF-#V+ zB4ufp4*5LBhra+&Q_IoPn50_S6~`MvZ?-KBFOH3+^@z}NG)WxfW}fm*;@#=@ndY3- zxh`~8TX;zN+3*?Jl~G^RkSbru@Dn*xzzYE)uQ#voWyzZkdqc=8Z8S!-l(Xk;xWWv% zwD4zpTiHJT=l}el^ZHqpYQ6RASFNJbc{b|3pxD9*cF={`@lF+ACHDri$3)+d1A31T z^HSuQovmT&(|k7jE#JHCbP{BTLl_ER+=)+@`;&Rkb4wJzVRp32IBm0=!or7~hLfTn z2`NJpm>OGE`Vv)}1L2)i;c;^9dI7}me8ll|Rn*c=Sr%t5vKBbbONJ~CjYof9_!i}O z=~r9nNI@`7id{qFRXG<#e;#_S28D23I#rb`6b!?ft8M)vx-}m%aov^aAgl z|N8HwGS=gf(nA&lX=vp={r{%l^qYKp&8wFlYDfEdGOCTZGODCvvP)`-&g>1XlJnFT zT3igR%su*P^B@rEWDh^}PpQvoSENdO+(*h;CU50&mDEsE48$JT?0DhPc!}^PsPEvm zg)T*=Z~;H-utcQf)hecXDv3)AM>zTAQZs@O!@Tu|1?odi*;hw+J@UM>BN)!ebs3HT zPh_)$3`BdjT?#e~X~7pBm;#Vjmfo%S>Q}$&4b%VnU;nFJ-L(Z76UVaa@hy9T46fb; zVH43J+!Egd5z1@GDCy_#pI*3sfKYQ-O_kNKTGI^Cn^+uydm@1Z>JcmrBjr;hPK))H zQt?k(;np;q&M76Y^0c#HgcHwuXMNhmM~i7#B101~WI#ww98#2inpG8^`Z&3ka`?rV z{tSf0&Ym$>>tJ{WUc`C5TJ!%{b^Gb7=WW&tbNY;Gek2T84UxBoEs=EoqR4;v5C6gW z(_2jooFN5~=`@`8(Ws(MKOKG%h#;KiPUnRXI`?unh@8OAt*HD;=ciM_5VE8ls4+$G7cO zi&HMRa4Xh#Jb;pR(p6!5hS%rv5KNqaAh-I zX~;F4vNKMk8X&jg?pp<+bfe|fbH$h1pIqy-t#Yvu>M<+NP{>ZPdy?BCgfqOB^GDpL z%y?{z%4K-rENQr*hGsXqRe#_2ec!iy%eQd#P|I}*|6+%w-mX}+!>tUkZGcwxXgIu9 zECX0nd`#Z>5t90%>X+AwIjkPm{cTOxXD{12;+0ncOU;1fQi--o0=Eb@xvQB!BAnW2 z?=_b9y-Wo4)FRJrJxL9z)Wm`KO3mA87@^BoxIVtQxc)?YOI4qhBa!TSfwLQi%)Ts6 zJ4!5EQ>QEKHrUGGN}(wja@CLXtWngm7bQ?Db@myHtSL#Goe`}N{i0GkYs)2S9Cm>z z54U^WdJH3%G{J!*UyK9;&s-sG5BmYl!d8H7u(JbF!$+2y5jmq7aWQy_z@x2iqrOx6 zbiSCMr+uzZ0FP(On11PGHQop4a(tGVCl3JprPNBl|#O2aUCyId&;VMLh z!uKw32fb=vDu&3y$!aaaOZ+$Dl4_I_FG?;gTiFO3j@%mJZ~+&XU7w2rFGPrmC)H42 zYA!P}%v)bz%1eZ!Sx+wikXEdpbZXHRb6SdoI3T+j}PPC+WB@v}Y52eo$?d zCtYjR%B0K%Z_#!(q1}USuLE6p^;4+q=@{}hj5G2VQaNRN(v$iR|L_m{O!$xf(Ld@d zi%P0I6KM@`s)4b*s~M-V^*|hFz2EKl=AO?#1*&;bkvBYz1W)7@`_wrL$X0o!R!?i# z(jk;cVfJK)rbH0lXbbVFh%?cP$&_Cy>2tcKKO@1aJr{@Fus(iM_IAnL`Sc#8Mc{?l zd-^6g*#hC0jGM>>Z>Yb7j5dS9Y~*E}@`;SFkY{PU5%3FXf1XEBAlHuLMd$59iPE`c zdiQ?KzxhIH=mr>+e@kMZ<8eOs+}ceuC9 zIVCE+^QR>)T+syetvI7;^EQ)_(I72E#;ufl6QwaDm*ti6iWDQ`fe8w~x&nAj|W<2eMoZ##;-3eMZ_3-M4Ga#0i0e?lA3(c`k!4+Q< z;?O^PM3B9^;DZAEGu_25yCYixqAE|U%c!ry`vlExhWFXp0-tqV5?zK>B~Ut>v$;1c z7rTa;F@=WJIdBGV3hU>}KqxjQyWwbtxlSZ|i;3X9H!qDC*1u)MF^scgnwq}n>83NL zVXafxQW!4p>xNup@x^M$ZhQJEAQ*N`t4FCgh3wo%(?~huHrQYLYk%!u|LcD((1V4V z^BTU?sA*^jUY&Z9pd%LeQpoVcmqL+ArKVbK-l8rcK$W8jY^JlDGTh>1i!DW2VxHhh zvp$gnB?_qw3W@M)*`)G@D6C<9mCv4QQ4#LKx1}SZC(@CEL)gI5%_}f++rUti*LV%} z#ljh)h?Gm8NFk1XKPX3wN!IPt0d0u<*b)g(md$uJ~hsAxSCr@3#jonTnMoBgs}Bhf(CE9rW{@%ozGM7V(GXtyYl~< zU-N6e{N*qE@PtcWo#kC&47g*hMM#IORamprJH%xDX^`a_`GRanVfJS-DnP#|E7{V8 zRF$$BTjbT8fyv+~(ZvKfXKW+A}U3_;T}^y!3{<9!Xj z=`=fMk&RrU^(Cqw$T;zf9qm$BA@-I^@T=+~d(n9u(sYBdl~d@LcK9C2yzUCVjNyXb z?ctFVq8Q{VtZcTsvex+{sjUJ$%PzJkeeI*eBxYs9Au0qy0?P=`2-l-&aqtt@I~?f%8Sxpl zm_Rg*0WWqU40uCHJM=h^+Uy0s^L$De2SlymKt`z-sEm>_0vqaQz@4^+&4r;nwtfZ< zabP{ZKYsO_fAeoP3t&<>_@-Z?XM4=ryc<&1hM#sIL^|&>?8-Ls&!2IY$2P$V3)fdw zB~`|K3*4SZ4eccaw#p6J1zNO*ZMLZe>OZ(8Rk-&Z=>PbS|G1xK{nStW6i}TIM!Pg} zGiquItJFAL@taO_5%n`a^E2M@aRm!6-51--Rojdj$_Qag$1v1p zNv8^*^`2n4r~zB6)5xpWWJh2i-(Hb?^Oy1$g)sdIh7_)^c+8$?&$&Rfn4-Ak{l&ld z7rPOr#%ovzq?SH#b;1jWtOdfUInkbi?@ab+2yA_ulkwgr+@pZ~pgd&ZkiBk6%|zxZ zSF_N=^;%3r%d7W`Pk$iDHv#;%*h_-`*AB{DFEw7x1QCS$e=2%uL>&H7)bNv2k-2W7 z_jV51D>&P*eNwt}&#ZuMIju%V;nb1D8YU~GboGg+!)8Pq=K`LGuh^HE@Xf9(@R&17 zr|b?fymTW_;B9t+4q|z^I-m&7-zEs;)o;E+XM<^BHO|NCtK_GKxS^eu*PZA`Yj z8!|S`VpNzq*+RO)cNQB8seM?HuSCf^Z#8U* z)WD@?seN|+(MKO~H5Z8e{7Zl7FTHHtrz-FNi1lw%ZaPc!3t*h{dL`Wo#%bjti|x2@ z4PhXh7nWDV-|M3GN?YDIr`j0>Zx$zE_L$A9qsd0#ipGF+WW#rq8=m?_o%}@f$?6k? zNN0yY^cgLweI9>Fc_E#r`boiYxHM`h>$CO4@02g5`=-0jvy!dCqf6ad^{sBSlV8!m z%`=?>?X4}!qkc&G(|L&oPqXZ|%8uK>Ks5GQ?I{?IH)EQoRQwZpoB|{eP32mAU>BNj zAALer08o2rI6On(CdJ6kNF)TU0*bn4xTQD~HOz%PW=38*NhJ$(8-R>6!_|}hJATLS z@IEeMpWrVLm@SuGLF6pPbEJ23-D!TwFZm^3`N~&ZdCPK!FIUDUb-^EpF(<`H9DZ&^ zcnyw_>m7Exj=+~1{+MWBhMtIL(+U{B;mF=S@}*?&gL?eMH+Q{vD1Ea9XJodfFb$|r z5ZRJ)0U>wRt+S*arWt|y2t}pP<9nR&XM1En1V{c0f8j6umA~>=ybSQ!f+)D`DR*@P zhL=lW2yD&H;cBNDvWx9|Bl=Pop5|FhAvd9WGkv=V!N%(~0|l{X$fGTE^-!poGIHVb zZea^g6LWbX1tJu+o=Z!|ao0`LzJNck!zyVtHpjD6pawZ-)P}KQT;Xxh zfM#LXdM0J4C35QIePds#BNMb4JokH7!ohNgU8zb@>KzmyJ3f){iVs9Eqg;@}4L1e$ z@=iRRG9n0!!#Md1e2Y`Qy;-FL?8bSJRPP2^{3SMr`njO z5p&41%8pA1uBjQyRU7{uaNA(B84dMf!*7TAzA?l`SoR3vapVO;3}@hnw4{aSb={kb zm+j=T#{!qgfyjn4#3F0#`qbDA4UVaub|3`KKHI17;hku95^P~javUx#xf#57_?yz? z6~3B6R?6!`nqpg*vg0#^r(ifF!ukl0LyOgP5Q=Rnfs>lCaKNB= z##M9pJ+inv8m1Fx-k!;T5lX?9Wr+$gjB_$`5wUb?c%|U7r;m)Om59jJgn_d}>4#Jg z+my|S;V#}^@FZopmTbeMy3o`&k<|PNaK``oU;oR+mnK`A&c%4ebzvIHyH?=Y=Q?TZ z$}?r&To6X>41b3`T}0;cLdcKj{P(36WJ~9XAKws|VDHxZ?eQnC*5nyz7&k~8C+ZHB?Yp*phCD5%%vq2e^muO+Y7NnxW4@5FL$8T38at+SH{F` zn~rbyzvpN;WyKV-su*j+ zwT5WJ8Pp)le$rL|u#*K180TctIYBgQrEdeIgWwG9(3I{hgEwV;2Bg|j23Bpk0`;Uu zYbZquT#-ZCWkZ+Im~DfO>A-0EwxVN^L0NbT!bgKE!wNt=`uCrHv_1vG zH8hPOHj##GMuKHk~u<7W5DsTN6A$l#4QJ}s9u_KEQ)S5fy=U zCgu2gG{g2P1%^+sy|%f8`^u$_I)33eA&e$Ps(GE^z=q|Gsn?L_T}TM!U18>W-B>Zv z&SAE`8W2Lt6}Y@JqHVbFkY#CT^~`84c7#i=X;#3zBTr#S#!GnK)EqvG=I}*6Ye1`h zMs*t6Kigo2TC{U>CW1+GoWPJm^d-ePv$s*_Wz?i#timZ*1BFEDF*6cp7r0?k%T9UuLp%rCO7!CE8p`@?m zoyett3qJ9r?vhWkTz0@E#b_SdY`9~-_h*KJ1L<5=*_RhTtolMfB7%)uA~IDbn3Zgh}tX| z?No0q3qdmkYRfWX3Pav&2*J5kf4@AXaOy|6q?nAf%~A9J+WwZ`@>@i`cV4d$HrbPN)^Osl+oXD2^ErU;>|V&t zLh)$XTbZ_>VMs?7Cz`9a*Fui?-~QWw`yc#+f8ZsbCk(FfB`Of;nYBwcuyBTD{2%}0 zfAm7pu+0V(W%$@S`qi1pY$1L{prp>b^6Jl;163l$0w1$nkTWR-Lw%gnNIV3`(v{k* zwbGr+m>OI?k2vI9Np=br+$+-W`mXQV=S!`F6d5!NtfvD>jl$OUmizuOMFcL@S~?5p zuXz2dfAz0a)kR=B>DMDWYTgR@JAdczIQlMkUi|`jwTdw;ixg#nbW$(^-`co_|AD@?BXk@Yn$Ee_)y{IqTxM%eIV4*aZ!n$M7F&Oo?uy!RBSp6y#ALN+za z)rw8LkPS}*(>r3WAo>X-epU&e^jVG>wt!{mYpS4qaCTJzL^ z6pBr1G)6Y27AK@Nx%)ruA}rUZj_0hYJ?Fe(GU_F|C%<*3ENa!VO)VN`(=>c6;OSHx z^(KFB7wUy@@xBXs|G#vSn!*slxl#$A_jJ8C%_;sm+0t~#g|wjf&mO|pb)NNBbH(W8 z(iq{}riO8duyK6j&>va%B$RA?+t-~ExH?5ue7(ajHZqsS=IWU~{MkA&iF0*&3t?9( zTNA6Hv6aocA#j>pZf35PKk_3#@?$^tWA1G(rkDs5VHXlEqeKD`qV}qmwxF|<1PE}vY$HH@SFP4raa6;w#O86Po$6vZb2Z@;frl~KlP(OGvGWuy;h*o4BLE<^rBmzQlGcE}WD9S#5%n>X&H7 zq(YW-{1GlyjTxb#ev@SjDXJbbqjf;gwi@i^8k3P=UcDia47P@o7fxBPrQqne&?Ta; zof!opFFXTn2K{V(W#4mtQ{H6Hkdrz5Dg77v&wZ{>SH)9UQs=VkA)_hmfGz~Sh1wMG zYp2?5AwKC%s_+yRo>9g{5oC|qa9#$assviP3Sj4|pXRBZvISG91D#OCj;sKV4N{uLJueY+@()M!Uq3 z>C_fGFMK8PUJ4lh?M4cE%Azcal}b5;ouS`xo$Kw1#$3i_p_ZVYx2K=+b9qK|SN=19 z=FjL|ble&p+RC=!d{e?>A|rCjC4y+(4%Od=2rQhvTo;3@eX~G>mWYlsVLFCA+tEbO zF0zImMEp3c+j(BHGbooU1D*&24x}uLTcc^3zEx_1Tx*b*+BmNmVlQ6VC&<;OW-uBDmqi4b3rfb2sbe+upDle)^|>8et0<`Tth04EI70c7OG+{#7sV z+!#D$E9SmnPYcL4N3^D_rZO5KKJ;O;t{W42W3#4p#b*Gs~lGO@L@*Q($L| zoJc)a%!(`!l2-^q&1vE#qCcjtY)Dx|$#_l#-omzkR=bbsOoXe2Z84<~PO23sb;>c5 zg`ipF>Rf74*%=kQvMKOJsIRb63)!rRN9L*$d|#=iv%*F7w zM)#vx4`f(v6KAw0`c{KCV<9o;{owT>IL*_j!i(${@Qx8fM*Y(97Cxh4%-M#i2{$`K{MIVYV;|vl z&STVjk;gi#?F>9QY$v-invwb;8vcw{qm=<}ve8auE)COHd|h^2pbV!oqY$o3?2^Vz zidwjZ_1ENn?&p5aF|FWw-fDxdBnVsOlj3!h^%~`m?(dIuZH!Y-jaNe(ZU~<%``LaX zFL|HEFJK~OXUZBcJMdoGZG{Ii0yFeyyIoDrF?*AF*2X_WPVz?C~4ZS9eBfZ$nI=}T&Da|<-)6{GOR{w@lO#nPaAn7XTu}wg?p9a zMn2k>cT&YFULbqDDYF}zW-h~|mf{s7^0S1S?osDC4A)m!2AfW6%V?M@JhIt!IDotw zW43kR{p6ERJZ$yiE$`VEl74IDy5p(zCw}55e)LCwl;QQm$~XgOueKpSABGwnGP|x{ zYTBz!T>Tl6HWx$9R@7IUo@+-urpY$nqJa24H9~|K#xx9}*`7CK3r8qXOpo$jg8u10 z{ilEXZ~yIHr8s4Jw~4nVaE2q|6#M$uzivj3Q6I1Mq+U9Gt%>X9LiUKzhasdQG{L7F zFaJY9Q??#!>ytrh1~1Jf$XJ7yF+(vX_(MPRL*M`X-~W&P(LeH8likvl#2?`?BdcLp zc={FK1%N+<->E7oP>*otHCe+-nl}O_Z?hmChpQ$d!w#v`WOHTfMd>N%mxx^~FTP<> zk=bR;#XCIO+xwKKKjZBRpOv|ctyMdp?eLk@hO30D$!>AV*p$(>H`q0VjB!3|ktcte zzzH(S5)w9w$2SK}(LBUrTT#o+KpZZh3=lA@cKl^9@EWV$&`JAvz?uFk_RHe=s?UDt- z1;_Q=;o~w?;mTk`sx5`v1l|$2FwLU74}vQvHE+VYLPm>|h`y@QZx}L{p+NnN(-r=~ zy}VlQkh&u;MMjC@KwimvJxG^=ZY%V#SCYFh-#Db!%dC~fxF@+C9)@G!_?5IiW%dW; z-OR~m)6pyq*)Fe!TnL5?!*wUOHE`(1-KS?u3Cjt-d2N9bLM2Q85P53RD9k_(r0uC z(42%;VAmbw2zTOpI^@sOQdfhPO^rRxfsmHV6-Q&=gKl%w*br~;;V0!Ik?={KvRnxk zDAutS;_(%C=!_V}I1ewVn+@&}T$$4fP1x zs%lHaBj1B=&p;ZQej-8|)(cdaz;fw7`skz7;M31Q11i|C9@8Ql>PrNtJiZWDEw?TQ z)ax;i&VHucs|8FAK_CC6zVdj@f?ZpRg_rjx`mW(H;aXTx4V&PXzx?G474j12^`q*3 zOVhfEW!zT$eq1nZFt$MaEq;Xwk5;w%Qp0Pq05wB>73zf>wvz0MAwD~fzF9=L3BB7Y z;g!Lj<^p9Lr>KS>)>BYk?ZXt_7_}*zn(bt0hNBr)yk2wB=zD$a%QP|Z&9Nwb1cp}Cwg6|IOdv0!3FYm#VUO>0(+1!GK%0k05^A}SIP1TTkkB2i;3_*#BT?`O{M-t8Iu z=GtqowfA!l_>RKZeYDwR2)zxkVU&9$~xY(ss!tj>`UY!K$p{oK#_p}li>3i57J z3n`bLcP-HH^v2PDWK%W;;=Vxm>8GDsz?L0i5frA#2rCi}F}$RH`VByJU#JChJ?XuW z-!&v+K|La@6o!_po~xVVKvEx*u}ac!^Ex-JNVvN|StM=k-c|pr@YM98);AYJt?bk6 zO8DuY{%L;^1$bTtx{#^S5-4=xY;p;Xb!43Dqr}* z7yQLT&+8+f^F>Rx&3ZC#E?ETPGpvKxQMm0sLmusw`sn)Hk$x8OJ>T;^{$#uPx{%w- zD$iE4=T4tP`$anIW7rC`I9sA>+pt-r)BT~7(1qzdVCqE`BG(WcL#;$zHOPJzjPK*s z0=F6?GrD#Df`^IQs-s3gw5B|g&^GX zym_%lXh_5kMuVi#M1+Wi+*92;i>4LGrLiSStr`N6xz^u;?=5h}jLgZ_bIGpqg(qA1 zt9lCQQ*f18K=0^G_EjRUB}-b~uAPR~M_z674cE7HR`s&Zg|tv?fiYX9QcUEvMUGD) z`(m8R)hxTlwmXap6T84^J{qF53+^#MgOjSI2BB%K^!MX-Tbb&)sXXXR7Ke@@4x8aQ z&yM>`8=dEvNkz_vOQ&BV_G2R66uJZu7?5&F!JvZ%Kf{-$@UuT@C;qwEFBiwObn&}y zsM#=t%TRA>Zl_ii{;o;+K;8Sgzw%f9ia+_~?>+p$5Bz}dE_RbYCsnwfW?6U*>odY* z77NiV?@EHN@-`^JqT){(Q%eegj$N~5CKZmq*BZ10b1je|7Bd}vS(a<~hZ*U}BD+_s zZ~Vq@?C-62OO%>y2akc)kz1|^8#3N}JVy7?Hy-b7#$|ZA-fOE#trS`*Nzc6SE)QT? z)Nj>)Q^_Ie&mu84u=}X~d(t(5f zqISjbY7qVN%N4=xLmBb))i0x&c8f$)Tm55^L&VO`=vqzr;(8oT|4|2dAey#eI!nQ@ zP`+;Q?ce_GF2(=yU;c}i6VF@QCiC)e)*LS)D~Wy?i(2+}bHnj}0S!~I$O`b=DgJi? zh8^MFVS8nfl7M{p@N5Lf=*I^8$6E8A{rP#(>~>VXN|rX$v44!kMLz1&YOqAPTu*QEbdK88(!S z%p0M=WeL1HZ%KuuCQ5^cbTce995Y&KAKW`a{@5;A7xG!&2MfM8u$eY}6OkIy;x}Ik zRbpuF>{g&z<0I>VG2zs-@fl@#Z!ymh2yylbynfZ$W;^TVM#IdCADoa3W;PHe5Qi zDh$yZmR+5g=`gesF-9Jecd|G3=h4!yU(yMm*$2*WIcx4Mu16qp{H@Q1ypNSz1UT~d zec$&nx_{m^KD+C}Yj8~Vq7t`yh3K??B|uA@e2ZKNj&7lzC1WX?{Q|VF1tDIS@S~J@U7Sr z8=43vPE{=`P(PeuI(nN0?@QPWWeY#~n^2{Ketl)y^zMx`uyrC{h0nz&MFdb{GnG<}cV{!0fO*I*)ye$y>Bd#xtk5izB+Uyn$EoELSW=7^kq4`>Y zGab7c>?0!%kRed78H&nl=)GS>LbxWL;%WrYOu7fo8)hF)XUv zKC)r2FNqs2O9pWUyezI_FJ~^~bht{9vc49sS4NugXLD(WWcBSP1Ao&oq~=lrYV4w( z?2$FD=V?#IJ_AnnRm!4T2lLWwuD{i0>8kLOu3e6QrW-Anb?}PsjkmkdHf5a3Ywj13 z!V5uD&&aC>s0_>&KRdNjWY?eikVF_NO#e3Lzec zTYn?uCw}55ynd;Qnldu7Tc|QBqarU!Z%6h5eNBhzuRy0J^YpH~iy^iK?}Hy7&H7|oUU=jTh~bmqMYGBWp5e_U zad=FIhb@!xV@-ecNZ|SGWg?LJXjG5+Zd$K8f^2sND zb|<`a>2Nh%k>RDjfGPtJCPQgw@qxARJ|8XKl{xtoPD-if_np6&z*Tvkdz~ExsH~C7ana2t22Y( zTr<*a-wUsvDI>JAdAG}O1?WlXGb*xT()q}q>=a6&45!^Iox37LJr%DQmGEjps9XrV~E!G8`V6)Ci1{ z#*uyAGAaPrI?i)9Bad%rWm*=2;XulIQ8k1vz{2$zokpCH$ZQMf8#tYxT&2!F z`zLW=jXrV@(~Rnrg|d11l>G;P@CPx!|NFn+d4sphn7}j3qg~AvOtzsYMzJ%BjTr&I zi8Gc6AsrJ}Ta{0Qi%$InEoq>uxLd(!Cb%-lhbv61Iud{{;$-6r#P zyKR{%P=iaM8R|!Xmq>q)`|P%cwtC{%hbLINi7-B_m0hfil~E7ZWK0&Aajp+SV8%GW zx!5g2VB$sT4Kde-5Ah%7_#po;|K-2@XaDS<^)}##e&~lFdzFcna73J1a zHL)vcHiP$h*+uy>{mER4fu#CO&eKfqw&%JSm9!JX(Bg2h9WSd<3ZI$)ArW|88(cuh z%3~)RZs?}mcZY-k4T-4GN2e=KxCY0`F3aqTri))aehU{o<14Pi-AyEmIBW{HLq3O*G|_jpk46_Xo6cz)3M8~Nt4|Q^uc!f z#=C#t-6wfombOP9u!dy&#hdT? zuJ8I&f9g-kZb&>SWjJH@+D`!f;$QrW{(7CeM00<6E(ObipP?^yMy~ZUWVcXOo}r}M z65NlOKvTBhO`MC7xL89iW>O^rDqd89`e-K9%BYu73eB=W z>X)lBhM%chMlKC47ac=(;D!j}aKR0y5MLkP>&PefKiTfTP5e#2={I3Mz6zwz#mE+$ zf-TU9Yq|7Y7ps9`;;S=eeWy_fFPc`h3?NQLN>MoD&U(Y-)k0=e2Jr5D<6YF}Vx(L~ z>DEfRzFeaf^}ql3|Aq@O7YHMMw!Wo^%2h$p zSTTW#OnEvP$Xc@FFo5k~_a6iZ!P|W9wRK$-8()uBo0O33*Y=mdk`kAfRKqke*AN1& z2E&jmWc~Hj=2+x8T=2y^trr>>ol~vS=>*1PZy4e1pHEsKj*x79;TiVK@9D%sz^KSIA`55GRCPE`7!n8kR-W-GA#+=*dan^)HH`mmd#mJsO(zq)vlE z1Wy$nD7h^7NqD)vk{B^D!|aS?BZtUk{naxtd|pO&gw}_qlesByvGwj7?hI^RtJlx;7lKd} zLX8eDyxy=})vV{zRsf`1lkmy1aVUSoH++Mq|HWq9s=uFsXe~7tuvf2phFc0o`elS% zq+U2oN4U>N-0fv#%dVeMQd6Mm$EUn#TZ9)Ty3lBr#k?5VGLmKM;pG(w*Eg&x9K%X- z9)KFe@nEDG0S9u){=q|1w_4xcALy`C%XLu(p6v{5I(i}5GYTCVD{TCo`g z-r=9uE$X!f!88|Gq75@1X>g#qXl%EOZjzQg7q1ynL#Wn^$WCb)k?&%k-B(L}=}VWU z-kvTTlfA0A6k`~ktD%0iC54$Xd^(He>LhSwP)m?2H7>EIKBU#SOif@Z^k=(EUb~a< zLR$@rB&0THteJ|fwh;E2sac=;Z+--MLKk~e4R1*F99}{;jJ#pSS;R?TZ%F^qF0j;h z?rqE0#vhUaZwU;~)Tft?!<(#cllwH$C(}L6#Qd24QrpU?Hp(N+cvrfnueKd}A?M00 z5blB4g`kFv002M$Nkl#7toDH2rxW*WtbYK$?9`8S%HQvn!xy6Y(4%IG77I36(Mm$tF7m) zhG-XsmkeDat}MS2s`qzw3~lA9Mqg?lI&MajJ!u1m~zN^OOy4@kShGf|qyj;RtQY2nTHt({-U*1F&s7_JT>dVXKC2nY~d|>hEr=Pl1 zSINs0J;^K`S8GnTiQJ2Na{v~I%v-BwHn7)t%j>pjXd*G=2tPxn_CjMK@yiHg%++v$ zmm&OQ&W5W~pVW!7O(*ZxWRr@C(7Yz%dh}A_54ZdC*?vanjIE@M_-ExK&`cIW@G^`B z6uXerY#gI%Ll79LRda%EQ{i^Xhdwxk2a1|FgSYV13~MLnw0Y0rd+jIr12Z<9S~G<+ zGUlGcKjRcX$aTE2O@p^6tTj&AY19Y^cB0 z@zjF9=MBIalYNtU@h)OMIlZdCE4?8=T0TZm^)VA^ZtPqe<_&yI2cIjW<-kn5j9gX| zXeoV&>&QATkh6eC@%JK$pR^N^H2n%=w+Ic-sM!n0iKb7ae$2c7b~SpAab)-O79sD* zXkLRmMmsJH^=N4LRs*sE0u58HjLAmRr;w)JYriYU8vwY!731;Ujb2Ml0hXOeiI##< zoBoDrdY>ckE$j9#<~Xy5C@fnUX`YHxgLY|S zJ{p+9;bnXzd;qfS#TvFaW>LDPBk!2u5X^9~46SQtIA%I&B4i9-Afy5i7!C0m+QL&< z6nv}Ar6H&D^4>{yiabyFhL2ycAxP&8e@62e7X{u*N-**nGJA$7WXsr;T_ehKy>x+a zwLTg885D9c0=F`WH{@!MJTO6W0p%6WaIlxokzOTS4H=@~h0lO%n|1Y<6vtz6=I@tE{b_kW3bqIk3Ps)0+*|b&0-ylVAbL8{ z76rUyi3uMwPWS?OsTIq{(c-kfU;N@18GdhJoC%_7a6Rzs<2>0%d(!nJh<^!bc!z)9 zJ+tDgKMvdSs(<7-n+v>YcKmFO>({$7gq9^dwI|C1mXWcf1G(5s*AP;x7%5UBAE&q`M!cA+jUaGe8_742=<=%{XnYm$w4P-%#p%p3lT_ z&MbM`j=83j@x&W|%FUhJ*~1Y|Z}yY@YJK!_<<%$Ht}Hve{adaG8x}t0%W$-lka+2q zmtl=s%~E)w^$;MCOPj)L4Y8X}u|p=yIPKBil`BC7FHp_rLY;vrJE$7Y&N$SF^Qy2E zkft+1%z>LJO?fxW7>zMI7rrL-LaH!)DI^U?qrf{I25%s*hXcb9eLB2rOLQ?X6Tjf^ z9a1hZBfe^v`n?K|gjKt$ntn4HpEn$zT>*@?o}nS+Webeoq1`IM^JZj=iVRFSDLsOg zH$%VJoA2!tEu&c8S2cmou;1$T^zU*|>LvV9Ry`)JIl33}ITX06W~cV3If4)(4W(dc z+m5cC5lV+JgiS$XoaGs|s+eRET2;fyrLY3LY`wqz=g#1*g7tymYmD|()Hc_(U|2DF zoYD<3-0LOK5Z{9WW)E-;edz9%BI9>_$9J&(l#g=NCM9FxQ+|~pFTPnww+h)>I$R7x zhDtpBt1v`gc+8jgq86ARZ3{TdkO10nfWFUU;$%UtZ+9 zm}NQDAXV622#iw?)D#SFF)swJ!pk_j6hzX|4>3o>G+PQoC)4@yqgSsTtyO`^mK4%z zh{{V-18O+z_`K(W-&wbA3iy#B+5}6Kh#rlXltn0o;=Pak?Z5rE!+rC!SB;_|1u`Nt zN;<)DhAiZQJU@I!=g|cTtXEsjtXCc+@9N0~MDyOvB5(nRpZs@KOIElh3P(s$^?}Kz zurjzRlA+y?;q6}0v(0FCz1t#d>B|Mg_YpAA%?Ekj_;$^w6AN#Rh z(F@cInF9M1IqfOG)@TLJb(rj285La3U1oC4C@(LXNj0y?xrnq5dhNA!zJy<$l4|%3 zRq~S9cX5c^w(7oFDMPf&EOM0;c?KHt8CY$lv%mH|j;NLe0j_MrdSqTY$-;3~5*)2z zvieiAK;W5r%$FGI@o43pY6tT9km~nAtlt?>)idPH=;T!7#kJw)l5+Q`Uc(^UYa(Nw zMt0tMGXk<(d?L-UA(z&I%SB(VUf6_qMRbc`0IO%Q1=foaTRJpgbr!O@!%rK>FDLv8m_!JOV8HZfJ5l#cAU1(pp47Eh` z4bfURMz(eEwLLbDD8?zvKJn}}G&{m@i0ucp2tT> zI6{JV?$6_2lt1_3GFE_kDrt!#n0N$3Ml^O?z-Uvt@FVE+-cRWNYiRD6V$WrSKiMjy zl^3O<8A?=keYhJGJbpcVNP%kbzN_zT(gB|Q6+k?%vxh8%PRQx<4Mv}M(md;_Fyxer z(qCFNk8|<9d{}BpwdUxzs=RC&R838OAa5;WHe;77oJR!QHKM`X9elD}!Mo3CgQ2OU4WP0=Mzc zM=i^Rc1wS6!_8e=c^R#|T+#e)bk}qH66j+>ZvgxmAK?~Lpw>*60@7vQZQanTSI-v2 zxkwd)U^@`r)OQrgT(vJTQkawwc0Cun*-a`i++3QYwY2~_Y0F!JaRM~ zM)P94tD)82MR*e5yu`A#%1{p*_+-?3!D7>l+yt8i8_3Re%ldY&85W_1+LR$lRgd*S zD1-tqK}a-#;}^2L4Mic3-*B z@qD>b5T#|OTrZrVk;=evy?1F$;bI^sB{jps_G4ITU-`;cBtmE+X{N@$=)7>a!b;ld zEw)PNmr?5SGD;WWF%d7On}97-|05wJk;tWhpUOlGwHfR zN|fel`>fNBBco}I?gs8f9tm99cJR#D4YLYMlv@3mXQGqvye;ZUg2Gx}`u-;+Z-CvU zJJR?-gvGMCHk{yXk{iqzZwA++9YXrazw9@~8T13PZoYk^QB|T+=(MeXwwsqAkO5(6*qI#e(?_MAZ%dFzs|YBA4h%cV!Ck9pxlYXx$x zq>Q3Yu%(kd*+;>bAXODJTZ)!z;b&FAD$!$VAvnVegtU@Y2~wT-r?}zPHo^K66qtCq zNF|a{E>RiJ^rBAMsTO{EFUVXfk;3=cJRG|OTnmpnt6{Sk!We%b@fw67%FW#ij1;X^ zN%Jn8QB($Rrzz z(L9uEY|JNVtr%CW6oDm$1KIk`m)+XJwTdhSdrXFgkVw@AzP2Y)$c&T?hbX&5r}(^0 zupygK-=vztQknr%l zym#X5s7xf9A$@oXWNW7gg!irI{r_HK-yd)=F0qvjtRXU@tGwY^d0zJfMjOF6iw}>c z--5JsMYkTd&bc)H$e7hB(~w(&D8yS{AOHKiJzxIvm%T6>das$w(eLSB z&Dz3KC=rDj2u&m=m#Dn(*@fuQfK!OMUWjIR5>8vV7A;(_6;;96(}|F#drf~J1-PUU zLh8jbs3kIHBGn%UvP5}PI74FUResLh@C+|w;dAY7cnUt+qJ+FFHT=; zP`{GcckZprdm&?<8)4{9QV8*)3N%c1#bj(4ZDjak4XoQv6l@|2pu`L z%iMp@r^md6m!&?PY*B{iA*_;=NqHyW#k$@E-J8R0;)V<0lwXzl`uzm>0qu9+PcqUWvc)8^6)_y#3Nb(}b^?PM;=(P|$$- zrpBPgTi^779iCF>io=!Ez=nAr!FgX|2wy#qf?TbzBIjMkjFlImq^FBr%XQK&sYjSd z;?|AACD4%9cC>E2YfZ3f+1>!4^}W_K+XA~brU@pVp#Dv{#}!fRTn#s=(}+`+Wam9a zWRE%Ub1}*|sphyLm33hf^f$A&do)0xxSu8HCJ1C&#F%QgR7c| zbkqgOdbGJgu@LU8%WxM3OB)jTt z@wa{3w|(#TelM56!egHNs;aNB(+;`x5;g0SUp*J*2M_5NE0=mKuYQv)JEY~s>@!QW zA;9q;%ec(38I`n*XF3R34Y&&@qZC|Hq^X9v@|Ha>X8Nb=vAiLq2pX1R_+$7Z@<^_R zm~5vPM+$bWIa-G}QumVFjF7`42=S0qVes0LUJTy%A{Rm>4LpbJY9QOLLB?cUh7fb{ zUjlnSY({Es)@MjIHIF066o~7!1WWW7GJ?p8j6j{ zo`Fxfxx|5FY-=QgXri=iQNtPVq(pfSgM-kQ`){@0FF-Rw+l?_DpD7e*$UunCW*~>N zA=#$^mk7-i7zl-*Oo*!V#I;=Yz!Dj9u^ZOsf^AbOk{FA>Ms5dM-j zCL^_XMa`Q4SV5=0vEjS;ABI25Le3&j*<7YP&I~lRW=(cP8HUsntUm?cYYU+%BcuH) zJCL}WgU{d@&R!fiX8ZemI?TuH7w}Y=YerLk6nQU_EFCp#@8!RW(Bca@>wK@mt(fUS)SHe~dO5cx^{_ku*u zyzInV2g8g?DiH$1QuAttTI;ad5T?A?$F+J_dFecQ%lL@D3yH5sSS)eJ@|@sYjN$HC z=4fg_eF{s2Hv4&F57y7qE?!A5h+&l|Mr~W(@cQc4^J=E4DZrzi=M3KkN@0YhKsF>Go4~As){38V*8CL;J$^>?H&)Z?Akn?H+OK;3=;#20jw(Kma*t7V}5iL9p zsVCn6Ysl_}sUr&Sh{go^nYqQ$B24xJ4BIE2yQ{yl zV5cg8tlzfkgE^WC(4%RR>OUaS*ED1+yi!;8=a@I~4-ow9rulrZ#dX2>UE^QzD}IGL z_LW&1ub1JJm&?!;{Ak;M*~=@1Ul`~WI!#Ej z8$v|Y&XUS|A~+4X;uGnR3>hCG1%1Y1Pa~a6j}X$VQ-Bo6fJB%wrec7MXz%S)(2p5n zn26z-dS_!xS4K4aP6cCv(Hfq8E4Fo8_IDX{I$)bDW`%_;V+xmHVEh7CHbcQ&LKqqX zP-Aquq}CwOFq3kxXTVKl-f}(J3qN^gQmeeGM$2p1q-2bs zr{HhHc_!d0e1a1<nFK|63Lf%J((jiEyQP!%5F_A}u7yrad6qv|% z0Syv(OLK35dg&e;axMmC1VfE&vk0(@V*B-g-~GFPx4-r2Mg+u>T5rB*QlMC5&C>uT zgig8YZ#nMDhSkrg0DSfuS_HTn;AktYH}`=eni&p!Q@q@R=r$^XZoz_xp}#W`(J|@-|1qN@bkn zua8}7Gh%2f#t*K2deu-Rm2HJ_4B<6)NXBe}Md{hjM#qDKK;ZwMT9a)?Tb70vgcsO$ zH+OayX{%aDcC-&$oW#4zD3roo4sP)?*zHkO88RSwKP>OpD5~jHp%mv$<7<@hg|bR| z(Hk`@>t5jQ{OPBk{*qtvOZ=VeUdU|*2|I1w8T$5q8zWHTQqVxd5H5KA3oHJ-XlK%j zq0Bh(&m|X46DO;&H_VU%uD5O(@Qk@)5;ubvqZ3=!BDeZ!EsE;@#-K!o^7!HYfnWyv^Yn>aF8 zA-uCQ&X4coI7un0O0)XLq)jA# zF&SKNeYqM!3>8_R@*KsleB~?tV_N5-!w@qCOvaXFRS5(dPPSYlpLpfHs()=bzGA%H z`kwFko-cm!i~r$2{0FP8!gSV~J8+Bt!YnVOofCX-$VpJD;w>qK7SQ2gXlV6ZTkXZ3 z2AX-H=`jtnaWq4l8wh`S6MG?lfXGGRg~O$OGUL1#HRQQEw}u)!9liCjNxz(YesNc=Wanp9|k5YLghh7*?TKtmcLv-MytWz zdRiz<%V>#swF1w2@N+FN+R1se3$aIx_0b@t*vjC_PLsW%f<15fWq^P5kN(l$`+I-S zkB0kcej>bD{6fwe!cU|r$IM&Cl^1zZ87nn14xE&JNX7y$c(OoIg^$_LEKF^Jm~19yy&$kDyJ2;r5#HrpB0^E0KX2 z8$QWVTW6_s9l70|fljzjzPBb2KUwOB00fKT^%Sxn_*TYx3hY1mC;z11xn#WSy9(pa zP&xe#XNOx84cIUPg3oTKFbD!eZ?b+q=4TNO2^yq6gN>#aVg)P(z81#=)}Qzjf5J~z zU0lH4S8&xREGgqH{N~tqCJ9p3$|9s2?8uKsg(aANu}udbq1MKoso_8z8K(@~a0WGj zCdKf&08f)KL5BYX;P3zazweXgrsL5{2#%kqI1--Zq(g%sc&Fz#kqk*y=UGGc^chn3 zKyYS^cGe8R)Z~&eaiEOA@!4GK8LOnEVx2?pXVA=|q=g_j%#Z{rljUl5X&P1u8vaDs z)$n0`vM-61S5upbSbRU3`R?!jZh>z5O_0}6zeOOf4f)7YUsB||IF*!FN!6)$0L-Xk z09RoLtg=n!Di_*B9!j2PxBEUf>1FbsXec`*pD&ZTi7WcSyd|xqFHJ^ zro}mjY)drqDfL1-HTv$SWLc|}=6FCpDfcC|)D3M|HpZaVBwe`YWc@x2}LbSEB+IdfKi<3_Etfw=a9S#XB z%c+(9a-dvczck#{N{LPn>u)wmMf@k<>j5vC&J3!1{ zJDZ3i9Ui?`V)y7zKKaB`Z%=!XJppSJ@QLWr+|z&aZ~o1rEh(DcD|JEjkuZEWnQ@*p zHYo;?hFfZg3Uje3=(Scj99feNn1RO7TS+~d#?T6HXcoWz*Z+Fo5jQW0#jzm2?$`ag zZ~yjh@4o6k6!UF(+bybaw6ZfaU_GOZ)iAIqE>RaS*LsA}FzL9iechIwGUlTQ1b!+f zl5BilQ_ve~T-M4=^~C}g#YT2lbgH+5>03;$is^{5efAGT)@x;{uZE>y;A_@7!CN)= z7W3`swz=8UVF(14g@GeV&y`ni*fAKwFw=IkEPCO3clAH?hyIYCvWn`QizcAOwOy^y(vfDC+S>bLzn1&{z zS=%jahEjnzhG}N#YlX)_)=paX3wwemlN4F!!P`Zbp5fN>qY zT4#&qS>o%E^qCYEM;Sh``s}&q88(;gbPM>wAN)aIaO|CQS6aJ*z_9T>Byri8W}5+t zgW$#QcmB@b`P+WmZ)1>pKJVAgyUH&q-i2Ri?;V=*c^SAXd1&_X?|mw}13^T(7D_L~ zu0TBGSxiP*7#(**w1u2FTYIJ|;%SnSrHdBGpb2EVgi1uJM30&f^}FgY+3}mmsx3T) zxi$& zm1q;a#(&3md1=OA5>@+*(xzL_^ag(kr74P2|b`PQP`e zgcDa!CE>%1iiR_hka=^BMu)8ojZ1^j?=9SD5H7uDjvmem1U6;8*C)Sv>pjsrPf0I&=8^4uC3<4>dzL-5UXvkA>l76GIGq*4k7Do`|+3GYIPIqW{9@A zyd=fPa~=yjHCqtVcN+B?Zy^xR*)5>CFnHq!o;t6xsios8X57=>-q$4pK^O<hX?31J1 zRB<$3`yr$=#PG8Hzy8<%N@R;-_%&@3(O1vcG|R-BSFa@|?u@!!0xgjhW7OD6$`)&g zYz^_*kjqi7etkyigg0bBPFDm7P0jWxiBS!5dFkDg1aeZmc1@I)W;%&O^2X1FnQ`)G z=e^0j70hK=l-|RKS>Gw2eSR>Sj5vl1$ykc%T#WJIvqf20(~mq66Nmqw|MP!Jr1x0n zI{E8={jYmP{3CzlkNE!(ycw9zCD%E`A?119a}2}Emu?G-&_+ngUXv7(6tF9f5olNx z1XIK0Vo+wt#dTJ4+C}NzyWK8*8g1FVx_cb&(|6>;Q!chBHZborLrNWJn$5AQfQC+8 z;d;BQcLz`^!*ujGw+|$a|C*-|ZvcP@hG%Mg;8(x;)xY^S|K^YV*pEdHgg?_Zm(=P7 zR&CSKYXhkjm44p%a1E|+;-$ucWHVB;=FXBYvDid&ZykK0#oyP$#9`Y7b72=7P8nZc zuEhP?vFGDN!j-gKj43=C(aMqU9Qy z2!{9?(BUk^iox@Ged**-Xjuy6O;fC8@rO9!mQ?I=G1@FHO^Ak{IB!jbDeqk2VDc8o zFhp}dmuqjFPEJfcaRgry^K?RoEs8Q+mR5s}N!E~vX2`A{b_nBx8->-M$VIbZQ*%$S z&hC4d-bXoU#p)f=FMa7tCh|}I$v^47fztxEl1<_HI?M7r=jC3NYgH(Ut#^L9lvHIp zwyE;CRNLAv3Q|cop?gD1&}B=3Rp-QdssnLpz^xC8vh|M(yM zB=8^ngMZLjT9(JaUnpc}Rc&1z_jP)+?6a-2wYAhAT&G3Tq|l!_ohympY|XB@y1hoT zy`cEzdr9q=&BFQM0v-dY$@_?(NY(1C032WO`qoORz`RZ-Lp#I2&UWR!?hSy7w+dY_ z)*TXPo*s{I{L+2fhNCrYOrVYF6IO40;q5U_Ymr(Dk!G>wg+z<+0sWKVOY{MP&xc57 zYUXHOn1t5JEmkXuj2xmJ1g8N6o$T6TZI$$k8%e(I-W zm%<%Q7C$oc2H=N&=!bl^-8+-?i7!!_dYX#S*dyfSiV)|t@sXpQWOzjaH6^)jy|f@} zMU|`mQcsCen9jvGQ)DM4T`Rw#K%f+s#bc0H3&j|E3G}&wPnW;xo4(2KJ)^;kD%YjY zRsF(E%F~|bT1LkJL5QAL#*1xO8OfrNQj)FdmSO14iB*c30Pkc8J$KH1SQoQwm1$g9j}YsPnCMs zgGR(dmFAYx`kaJ!*|Ox4rKA-Qo?Q*mPH%-_nnE15mJMuJVE9s4VYHt98F0tUb@zAv z&fmc|7ekjMTuVWZsmZG^DIEvEe-G>jBR}yIKVjl<1WiUgre_F?#+c5d5kFiYW9O>C zg+CgPY9UpQUu!jhx8m-tDO6`(fyhpE20Jf9;Eug$IF9MNWmzlD5LigIUX!K1L#`)< zMg*QteX-pQxC}Kk{0R4|-z@A9hB6#oH}hZptAF*UpML6hq9F3>S1Ku*8a_~#zUiT! z^VqL8v>F6oAVE>pe^>fEJLO5YA}1Ygs0I(Idef{(eTGL>E^h$rg+puBMI~rUp2ZLc%E2DMBF^maeS4;|Kr@8-#f|TWBs+GtRd0qIe zzxB8Ns$cc1{LgU?hM%-lo?MphWAvYoZJN(>=0!gFEt>Ke?lAMVVtR<1PpdkS*BX-* zNV8?H49c2?Y9sXSTAzIK38ohUKlJ16Nv4usmAbsrWu%a8Q|yt6dxUG7PGwA$@b;R8 zEDEThu`lGNaDu;3_UqmNn9nAYVJ1p9c?a6Md&+by{>T6LAKe1tW7Z;DKCk`Wt}%oN zZ#m)^-j?m{75F)>3;jp6uSKG=NY@!Hg~|3&pCba-oU_R9A z*J~F7q|f;O;2->h9$vixaE{%2Y>-@j6cgW(J)al?>+hXu^~(}%AmdEIYdFs7D&+LGbdW%)Q?oBtKCj+>`C6fm|9|vHe-vTj37$Qj zhLvGxRsCP83DUo1ddp#;tpx0*+_FWiQJ!y=u$5$%>V<3ahzd*qhq)&ox1isMSWG5CSZgF+M{}hoJ@> zuUN^{3WKxtvW!q|D-1Kcj6NOk0VI1L4OAP1jy|0Xg#i^)7A|-$nhaj;j@{nsbRmR~ zocF_e;_1wIQfFRwo0dYsRoD}MSCCj>J4IGgum$&)fD5zBycE3e%Jn4vDv-De_2)9O zE8C7JlHjf*c6`Pbe0Cy+)$CSDA^v7@Mq$3TN#WYp`yqgnG)7_kMWODGI>{tbQR)c|ZHJKMS;06?`rQ+;FkK;Ku9T0G#DE z4Kp+)OV$PEImyTWXb|&nwjCOXrO~^b{^>vcr)(?hf~1p_p0^YN8TA9t>>qC@{_#4` zC`e)M=G8gSBb!dQq2|>pVH#=-4cL0JcihDq>IpL92eMnea9|D2prH3<4+oclsc%!* z9qSZ?LvrB&XT#|jzU!V|>PrEMR0f+@uZ0+D&D7$!gk;fx`lUvObXo?cvmPPZiOgp3 zE*3dgxePVll2%(=rX);D^fN#6Gd+pA#V8p66lX&OYDq!ZXT~#xok&qhwY<)T)E-~l zbbjM+{Eb%IH*vkD!U=YSifY&tC@>&;oO0=V`Z0^88%+qNcY~r33Sn1yB~2%gHzaRl zNMQY#aW+J5g(32`=_h!XxveT=OtuR3O@Ep*Fnb6d@?L}57LDDcEWWDPxs+!M+^HZ5 zX2gG2iiN8xzMhSrev5BU8McPkykr?4^T`xdl}nwj1)nB(8_(tfeHw;FIpJBxo*Koep;(e#UAD?YVy$5V zO=JKIyi6pQK#hK1<81$FJ`HB>`oc!28`N=Jyk!Z5Y$frc#tCS#nKdQ6Q` z-i3#Me81XWqFgv0I7vW^4R=tNY#p+faaEBJ9&>7`ZL3=I#TFD&#Vp)CGT6wfNp3{qukR&;1b3 z=b@g6Fg?gxz)3wB`?-J>c}ZmWSUgZk*3*J8dZp-ab_uITRIx2uwxu&hG?MOMV*1_Dl%qW6&$Zn26goT_F--0lT zt;fOFlWGCSTsWlPyXeO4O15$CJ2uWI5N_+FkkJxp2$>;7{k<3`yO7f9GlnZBq`ihW zTsTB4OP`3GNaXqs3>}11q}U6R{@IrWE&WrZ*w(F6I-i4tA2h$&Fh)#{K;G? z&qaZtzOuW42DZGIz*T6d8RBT!U--fo{4fo;1H&e(M@W;c1u|M62Eue0kelw?XY0I% zHZ-!nR_eE-H&JsHIPb+Eh@ips%{1FG_Bp+bTxP^D1q*)CnuvbjV}+NbA25`ces!vu zLRlD1K~)~m72L`rbK!5Di@KynaE5_>7+gpPAXz_f^Snqz0mw^w(K&}D6^^{cL2%hB zuX``f8A4ox3}5U?d*0pzGbW`5h_qy~)Oi`E#+5hmRe}}{p%xB7V>8-NFKvb?XRxiRjrQN6ux&*5)uQyZ zg&0yRMdEs1jZqDo4iMi8z_pl+H=?in=OY6ZL!m8GiBsn`_z(Z#KU9V3d)!wx0-A!o zP4Y7F#V>xbnc4zFUc;vISbMRL_^S|wNzr1Kx{O4Cxfo`FnKDr7);0osMz~%GJR^Nk zHN&LN9>{wlkG3piogZIWalraTiyohz)O=L<7-Z!!qnE{}-qI=Bp0R7iVd-IFE*@T7B2Kb@6!QI}h#xHmO~- z3SB~scCJ%G$NtewP~UpFWseEePR{dnevaN!m;Ga8DVT3+j+CV^JHxZjt4GMS6>Cl4 z)xeltR64wHwzd>2+p~hDc8-0W+E_?KQm*MBhes|8Fg&CF;!2@dHcpe>6hf+#T|M6P zE5LPrs->?77Ot2RJcn?@kW+*$gpvB)rO!g5xW^ps*ImkPj>pH zpll2DYS0v53l!|N83BUNJMxUPBvB^iN5DP}>WdQzdI_P%W#|;S44bmm0A@>ZN}c*0 zhG^lp67PqkDCwTj zR6W~C))@F9Biw}E-U9>m%??vWO=>M~!$gYZdQ?DuZChal(qvq~M@@vX=c>+Z=hn9! zJpD6lN@@w3pbOl6@+W`tC-sIztfaQrnlsmX%ZU6E+J&>?t8hutw7iV%DMUM`c?l^B zc#FTc2%^TE!9V-aDExi|Zf}y^7L?(J@O;#x&Icf+epld=1*pwE#M51$M%~xx&1uJP z(ez!Olv~3mSMptXFL-LMEN5)TCP6Mq<wjPr}r_^3Ak+l*#wUHTx|@|k># zXxa=bt7pU61sb-g3`n?rWw&;P@P3_Jc|SgT)6}bnmUR3birOvh@j`!@JU( zN0aRMdP8%x-Pt9Alr969Y@BY9K*NxW@i-hgzM&N^Ddh@i5lS>ZgI8ZYF8F6|(9xtQ zBoX~IiDc{9@%5Uh5H{}}d(-)1s4Y`%r=qf@C?s;q$?9jMtY-W;%aW@cq=e`L85i3S zA@YUOuyEqqB{srn>UV$iOOvMKdDKy42ywa!qyW*EvH5a+?oCN+#MxT}cIm3E4Gmey z_{HXRQFsmbEx+Zrkn+6P1uoo+hh6Y0Vwyf8O2i!7M_}_!MBl85z^C6(U+P>93xsEI zEd?nh`Ja-%^;^Hy7vd3wqTm8hmA5;d4l*Q8 zvw+wLw;g{?%ioRQ6>DvI8G3#|%cfVXgcMm2vxU*1b#;Ce>(BMn{w zdS1P0nuUESWPuq7KlgJ#=jne_6D34T{1SD^_~dXYp7a7ourCs=5(Y#`8e6ZbkcJT; zUGJpWTAKJ7Xj;m7;m;X7?yR=Q%$*w1$eu*Ke0w zoF=8<86Qx5E9_l|kCLUXW`a_18R`x75Umu|U;o;qOTnP5dBfw4nR`ypPConp+kg9S zds{21drSO!xfsfDEBMd<`9FL5cdx)Xt9orcUpM}nTj7W0Yq5HHqu>(i?q>eh!iqQ? zxBa;{3#YZX@U5LdpdNyNyJ1x|%>LT-vR}k2^#Zv7wN63Yz zRIz#KRE(F~j8kE>j7F40uuunpI_3F)f9@dP+T=4>pp0S9q!Ot^h`}P(#PnMfxsX+-ki%Ex?hCo@0dN&Zy zB#ty!^*k!Ty;pIw)vQ&==3V5+@Lwq8v+r`ewt^O2-%UYyZ~6`04XvHGjQ7R-?3o_N zy68pKZ-Fq=5y(3{Ci2&zN%}daYYI)LS+hG9UIX?xyQETU9X?A@c!<8~Q(zdLQL`Dd zw_K6oUcd%EX>_>DRS)U0qu!U>`VFwkNQ5#Pemz$@?x&*4en}S@h^eO_s*HN?9<5Sp z3ukDj&SuIu@pswLBqCnA>P(hbgKy<4OpgHH@Ll@TiQg28%2i-V^=Wnz*!WWGl@XIs zsgc=mOhb(~SqPC_O@TdcQ4k^e(_lx_v#BL`BfY&s8F@2K!j1H{O#zVRUF^+OT$J8< za1ZrFs_#fEp7%mw$P1E0$FCvp-5V0nfs50~#9sO!hSO`OirDJ%##9?enl_W$-8n7sV z?`^9GldTm}B_Vp^wP+QS4M&J?uPuT+!A0pc-m{0x9;d2!6(Fyr<8>yx2i}6;u7+jz0ihv- zV1y|%G_v}8&}TR8Kt(d7%Uf*_&1KU2io3>}1DXB7{mOo?f&y?(KEyCwuUV^SbS&M) zD`W^e@e`c)V+^t#4|8@; zMBb&bWifpmQC($@y`?Y&BJ*}{FK`)OWl*NpC6G;a3to1D44_1MN6t`@`d8(Oto|lQ zO-8n-d3T-`2dI_n8FA#EvWj|!`HRQ(;a>r=(5}tWWD76;) z?dbNe@IZp&L(=EP$&UQFFW+Ptvp+J6f=QVLo#xe1?tNsRFUHYlTrvs|pMkLQ7`61@ zjy?;ysGYGFmsrcLFx%&PPD-^5=f0lzD_{AF7rch2Li6&iP?I$;1YS~l_2&(QWV9^n zWyj3xC0`9)NP&wITX=noGcV&}H%z2lcgpP!oT>GTA^I zwOmy8~Qy_k7bUMyF&y{jJ~W5bg_kU_b|?l4z2 zKEy|2CgLy0JG6b5#zN)N3)G9+bQp=W=IVDh@V>DJ0(_eEi)Ay;260`K!k6fLes48a z9wtH}#pZ%b8ndNlHw$GH6<=TWKv9hT5MfNiT1>qZ5K*dP7p^~D*+3%fDq#rJ!xMQU z{r3KF0q1?MG37^Qnh3QBKr?N!W^uY66?h-PA@e>jGCpG=CwQ?_vu+)mqAL4brD)C= zqmAIL34xd+kxNEHoInPeb}8T-Ucyh;$Jiexdq!;v%E&&wrgkZhSKG6JS>I2=XFnC^ zg}@iv+Fo>CHKZB;mizt?(wf_sPGGs(DNNa8u7+2o6V()U3xa6wnJa@K`>SOCuOL5n zh~GN_>t3^1$ZZwFN_olQYF8Nd^?Y--y1KZg&)e1l86(FznG5;FZ&bFq%;Ji_ATCnZZOn%3X-?r{ z6dvgG781CsHiTFZpYQsMOe(QrT+v!;3feUHh(85}KpR1)T!xDxrKL%bOEbi*KgA(h zA=Ph~QJ+h1Sbuui@fXF&ZofuQgK7gEvvE>0G#$48mhOvR{363S?ASb+w5aEMffAA3 z)4=#;Z`hiL=nbupS3BQ!LofuYME%|ps0v8As0q&C@|Be`vbC6ubTG?8a8WXbx1qka z?vrnb`n>{G0|m63;@dk09UNXr&%45b8YC$O4iPgdV+ip1hBhfoYE7h4AqdI?LbS+L zD^XQd38wOd`18>Hv2z5VRt?$uY6iA|dM_d#tR-@>xF?uJG|ZAtBq_b5dKqoRzxg-+ z#-Hx>_9OB*MFAn%6&$THAPUG!a|-ZtxQuEmumYCrG`MJ@G3Z!_vxY!?kJo+tQB`oX zn5UXsM{jaY@U=GaGsN&jOuxJgZA^u&o{KBaM2fnrex{uF6bz|fmM4uf>Pf=R2JaS- zEwBw@ERk6x=)kbYUzQAf-bF<_ktZwp8iA`VeN|nMcjZMxZl;TRNi1?_H6xt@(`hN# zQd?x7ZT0B<67eUiC6yWl;xxM%GQ#VN?IVu4R{#J&07*naR5U`6*%LfD1J9C10J2NO zmSwbzF{g7e9tl&pFrOSE*Pir#O~KFB>oLvSsy@5&aZI(wYp6FI?x5IvKQ?u*ce680 zHgZf0-H@S0sJHaO)8{&sBS7r2HbDrxAx^P@hQm)+Qum-YUC8IsOZ}wI%68+{49Bd9 zYGyOcJus=55K$8!;nYb4XU547AA+M9whIJ%MQONK7emd~c%1SfoIS-XkYT^ze#HNm z{?cFiO+u~lgvVYkACB)&CiF}cvRyk~ZZmX}UhoQ-iH zvokzoyEH@@YAfun^q#IndKEezp8ved@~g(qrw4ax$*yNq_QfzSAH917Pp~!d*}O0I z_$ZnboPL6FD6DmQH)N!+s)U%Ao~vyoWvI8OeuV(&sh79?Es^1J6`M%0Avg_N*b^KX zp@#gfhOk`iGG<7<3SIJ_e)_3b<;Z5Infv%cvW1wJUHFk7`4Rt_2E%oMj8L5v*ju-i zmlvNx-zkr2xOy1c@MIx44w7B-nFXEQE^>{33%)me4Xy`dWDAc2WH(GfUt0*H8sHf% zf?*={DVMR>2oUY$q#t;9zUh?Vlse=#Vj*XW(=O&jPP-KKC(d3~Y$AreD6pM@2&QRy z4I>A#;r4FBy!EXNmsvnAo0rf|u72{fz{A-ySul&E*(BM`?V zt9KJ~GKD;v=PHwJL#hK`|~_PFnVb@R7r7zSqi`fVRpq{K=#(%0&>lm(QJ72wTgr z1Y3pSA%^s0l4a;20&C@3F%88+vcJ~7GNh&9Zao7~YLyx}J;|p%kaDJ&B^SR z68X~KG99uF>o3g5VH4VVR!_sorekPx4LAE!oDIieSTQvHbPVBvBZL6gPUW`j!f6;y zRQdylWGr^Z>RCug1x)rFo`mps^_TFIHrI?Z#mRi$R(1;iA9eTgYU{V1^&b2m#6a-4 zf4OMlCGiqaF&-mZ6ANA<#F_EqvsfJt~++ri>T^5{oyc79gqy45Q*j2ZB+I5|tyE;{X zT<|g?$1x<*_Jb7Owabv<>EF*75z2^PqI4egC#?b?_*)G|6;?_5KtnBM!>*Y{o#{dp zmgYU-B!7VYu^#|lOxbIj7hQo7IwV$~Vb%04N-K!j5i*1ijAJ1*pq8DoA)`KoH{;9Q ze+d({OWKZ=-Hb}ne67Q7y6nK4IiCD`!Vuz`8!f?(MLI3JD}`GQ@8AC0fBP@}rN88s z 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≥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 w%hSjZbh=J1+!OLrT7VzqYFP* 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 R#wh>*002ovPDHLkV1kC15n})V literal 131 zcmWN?NfN>!5CFh?Ucmc~{=a?F zp2t(ple4^V8@-6xqO$HFbW}@3Z$i? Date: Mon, 16 Sep 2024 14:47:07 -0700 Subject: [PATCH 1560/2019] [External] [docs] Remove rebind section in roadmap and update condicional conformance (#47205) [External] [docs] Remove rebind section in roadmap and update condicional conformance - Remove the `rebind` builtin from the roadmap, already implemented - Update the conditional conformance support in roadmap, as it's partially implemented --------- Co-authored-by: Manuel Saelices Co-authored-by: Arthur Evans Closes modularml/mojo#3475 MODULAR_ORIG_COMMIT_REV_ID: 3a18b08121fd05faff42e96eb7fae8a51a3d78ea --- docs/manual/parameters/index.ipynb | 102 ++++++++++++++++++++++++----- docs/roadmap.md | 40 +---------- 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index e7a9ff4181..7dd7599fa0 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -1598,7 +1598,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -1619,7 +1619,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1641,7 +1641,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -1659,7 +1659,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -1699,7 +1699,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 58, "metadata": {}, "outputs": [], "source": [ @@ -1719,7 +1719,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -1740,7 +1740,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -1757,7 +1757,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 61, "metadata": {}, "outputs": [ { @@ -1786,27 +1786,40 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 75, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ate Fudge (5,4,7)\n" + ] + } + ], "source": [ "fn nibble(f: Fudge[5]):\n", - " print(\"Ate\", str(f))" + " print(\"Ate\", str(f))\n", + "\n", + "nibble(Fudge[5, 4, 7]())\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here, `Fudge[5]` works like `Fudge[5, *_]` **except** in the handling of parameters with default values. Instead of discarding the default value of\n", - "`chocolate`, `Fudge[5]` binds the default value immediately, making it equivalent to: `Fudge[5, _, 7]`.\n", + "Here, `Fudge[5]` works like `Fudge[5, *_]` **except** in the handling of\n", + "parameters with default values. Instead of discarding the default value of\n", + "`chocolate`, `Fudge[5]` binds the default value immediately, making it\n", + "equivalent to: `Fudge[5, _, 7]`.\n", "\n", - "\n", - "This means that the following code won't compile with the previous definition for `nibble()` function, since it doesn't use the default value for `chocolate`:\n", + "This means that the following code won't compile with the previous definition\n", + "for the `nibble()` function, since it doesn't use the default value for\n", + "`chocolate`:\n", "\n", "```mojo\n", "nibble(Fudge[5, 5, 9]())\n", - "# ERROR: invalid call to 'eat': argument #0 cannot be converted from 'Fudge[5, 5, 9]' to 'Fudge[5, 5, 7]'\n", + "# ERROR: invalid call to 'nibble': argument #0 cannot be converted from 'Fudge[5, 5, 9]' to 'Fudge[5, 5, 7]'\n", "```\n", "\n", ":::note TODO\n", @@ -1816,6 +1829,63 @@ "\n", ":::\n" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `rebind()` builtin\n", + "\n", + "One of the consequences of Mojo not performing function instantiation in the\n", + "parser like C++ is that Mojo cannot always figure out whether some parametric\n", + "types are equal and complain about an invalid conversion. This typically occurs\n", + "in static dispatch patterns. For example, the following code won't compile:\n", + "\n", + "```mojo\n", + "fn take_simd8(x: SIMD[DType.float32, 8]):\n", + " pass\n", + "\n", + "fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]):\n", + " @parameter\n", + " if nelts == 8:\n", + " take_simd8(x)\n", + "```\n", + "\n", + "The parser will complain:\n", + "\n", + "```plaintext\n", + "error: invalid call to 'take_simd8': argument #0 cannot be converted from\n", + "'SIMD[f32, nelts]' to 'SIMD[f32, 8]'\n", + " take_simd8(x)\n", + " ~~~~~~~~~~^~~\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is because the parser fully type-checks the function without instantiation,\n", + "and the type of `x` is still `SIMD[f32, nelts]`, and not `SIMD[f32, 8]`, despite\n", + "the static conditional. The remedy is to manually \"rebind\" the type of `x`,\n", + "using the `rebind` builtin, which inserts a compile-time assert that the input\n", + "and result types resolve to the same type after function instantiation:" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "fn take_simd8(x: SIMD[DType.float32, 8]):\n", + " pass\n", + "\n", + "fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]):\n", + " @parameter\n", + " if nelts == 8:\n", + " take_simd8(rebind[SIMD[DType.float32, 8]](x))" + ] } ], "metadata": { diff --git a/docs/roadmap.md b/docs/roadmap.md index 80c7422f64..f7569721ea 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -210,7 +210,7 @@ We plan to expand traits support in future releases. Planned features include: - Support for a feature like Swift's extensions, allowing you to add a trait to a preexisting type. -- Add support for conditional conformance. +- Improve support for conditional conformance. ## Classes @@ -343,44 +343,6 @@ These are "synchronous co-routines" -- functions with multiple suspend points. Although Mojo has support for async functions with `async fn` and `async def`, Mojo does not yet support the `async for` and `async with` statements. -### The `rebind` builtin - -One of the consequences of Mojo not performing function instantiation in the -parser like C++ is that Mojo cannot always figure out whether some parametric -types are equal and complain about an invalid conversion. This typically occurs -in static dispatch patterns, like: - -```mojo -fn take_simd8(x: SIMD[DType.float32, 8]): pass - -fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]): - @parameter - if nelts == 8: - take_simd8(x) -``` - -The parser will complain, - -```log -error: invalid call to 'take_simd8': argument #0 cannot be converted from -'SIMD[f32, nelts]' to 'SIMD[f32, 8]' - take_simd8(x) - ~~~~~~~~~~^~~ -``` - -This is because the parser fully type-checks the function without instantiation, -and the type of `x` is still `SIMD[f32, nelts]`, and not `SIMD[f32, 8]`, despite -the static conditional. The remedy is to manually "rebind" the type of `x`, -using the `rebind` builtin, which inserts a compile-time assert that the input -and result types resolve to the same type after function instantiation. - -```mojo -fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]): - @parameter - if nelts == 8: - take_simd8(rebind[SIMD[DType.float32, 8]](x)) -``` - ### Scoping and mutability of statement variables Python programmers understand that local variables are implicitly declared and From 79ef0e193c554ee6f42ac4fbc635f09b37d5ebef Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 16 Sep 2024 18:37:23 -0700 Subject: [PATCH 1561/2019] [stdlib] Remove unnecessary lit features Lit has a builtin feature we can use for macOS, and since our tests don't support macOS on Intel at all we don't have to differentiate. MODULAR_ORIG_COMMIT_REV_ID: 0d55cf512ae8eab44ab301e1410d0353778c492b --- stdlib/test/sys/lit.local.cfg | 13 ------------- stdlib/test/sys/test_aarch64_target.mojo | 5 ++--- 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 stdlib/test/sys/lit.local.cfg diff --git a/stdlib/test/sys/lit.local.cfg b/stdlib/test/sys/lit.local.cfg deleted file mode 100644 index 75cc65c739..0000000000 --- a/stdlib/test/sys/lit.local.cfg +++ /dev/null @@ -1,13 +0,0 @@ -import platform -from pathlib import Path - -# Configuration file for the 'lit' test runner. - -if platform.machine() in ("aarch64", "arm64"): - config.available_features.add("aarch64") - -def is_apple_silicon() -> bool: - return platform.system() == "Darwin" and platform.processor() == "arm" - -if is_apple_silicon(): - config.available_features.add("apple-silicon") diff --git a/stdlib/test/sys/test_aarch64_target.mojo b/stdlib/test/sys/test_aarch64_target.mojo index a678d977d9..7fa94b946d 100644 --- a/stdlib/test/sys/test_aarch64_target.mojo +++ b/stdlib/test/sys/test_aarch64_target.mojo @@ -10,10 +10,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: aarch64 # COM: TODO (17471): Not all aarch64 have neon, so we need to guard against that, -# for now just require apple-silicon. -# REQUIRES: apple-silicon +# for now just require Apple Silicon. +# REQUIRES: system-darwin # RUN: %mojo %s from sys import alignof, has_avx512f, has_neon, simdbitwidth From cdb95a4d1c908b42a8e27a40ec1c1cf066a130c5 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 17 Sep 2024 10:51:32 -0700 Subject: [PATCH 1562/2019] [stdlib] Fix build info test This test wasn't running because is_debug was never set as a lit feature. Instead regardless of overall build configuration we can set the related variables. MODULAR_ORIG_COMMIT_REV_ID: 31584f26ade76f6ed4e58ed69d6c0aff60ff1a81 --- stdlib/test/sys/test_build_info_debug.mojo | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stdlib/test/sys/test_build_info_debug.mojo b/stdlib/test/sys/test_build_info_debug.mojo index 575d1deb73..5dc1a1a77d 100644 --- a/stdlib/test/sys/test_build_info_debug.mojo +++ b/stdlib/test/sys/test_build_info_debug.mojo @@ -10,18 +10,17 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: is_debug -# RUN: %mojo %s +# RUN: %mojo -D BUILD_TYPE=debug %s from sys._build import is_debug_build, is_release_build from testing import assert_false, assert_true -fn test_is_debug(): +def test_is_debug(): assert_true(is_debug_build()) assert_false(is_release_build()) -fn main(): +def main(): test_is_debug() From 2ea4163f3b2ab087f461e8deb186b1b2566a2a9d Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 17 Sep 2024 11:09:48 -0700 Subject: [PATCH 1563/2019] [stdlib] Use default lit features for systems Lit provides some defaults for the same things we were doing here MODULAR_ORIG_COMMIT_REV_ID: a87cd5ee47343b57e02189cd9fb0afb299db7bfb --- stdlib/test/builtin/test_stdin.mojo | 2 +- stdlib/test/lit.cfg.py | 3 --- stdlib/test/os/path/test_dirname.mojo | 2 +- stdlib/test/os/path/test_expanduser.mojo | 2 +- stdlib/test/os/test_getenv_setenv.mojo | 2 +- stdlib/test/pathlib/test_pathlib.mojo | 2 +- stdlib/test/pwd/test_pwd.mojo | 2 +- stdlib/test/sys/test_macos_target.mojo | 2 +- stdlib/test/sys/test_windows_target.mojo | 2 +- 9 files changed, 8 insertions(+), 11 deletions(-) diff --git a/stdlib/test/builtin/test_stdin.mojo b/stdlib/test/builtin/test_stdin.mojo index 65f338ac7c..c88619f955 100644 --- a/stdlib/test/builtin/test_stdin.mojo +++ b/stdlib/test/builtin/test_stdin.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !windows +# REQUIRES: !system-windows # RUN: echo "Hello, World" | %mojo-no-debug %s from builtin.io import _fdopen diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 87e08cbbcf..593f609ef4 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -39,9 +39,6 @@ def has_not(): if has_not() or os.getenv("GITHUB_REPOSITORY"): config.available_features.add("has_not") -# This makes the OS name available for `REQUIRE` directives, e.g., `# REQUIRES: darwin`. -config.available_features.add(platform.system().lower()) - # test_utils does not contain tests, just source code # that we run `mojo package` on to be used by other tests config.excludes = ["test_utils"] diff --git a/stdlib/test/os/path/test_dirname.mojo b/stdlib/test/os/path/test_dirname.mojo index 5b60f789ca..2b4a914ad3 100644 --- a/stdlib/test/os/path/test_dirname.mojo +++ b/stdlib/test/os/path/test_dirname.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !windows +# REQUIRES: !system-windows # RUN: %mojo %s diff --git a/stdlib/test/os/path/test_expanduser.mojo b/stdlib/test/os/path/test_expanduser.mojo index c1932bfd5d..7cfe60bfce 100644 --- a/stdlib/test/os/path/test_expanduser.mojo +++ b/stdlib/test/os/path/test_expanduser.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !windows +# REQUIRES: !system-windows # RUN: %mojo %s diff --git a/stdlib/test/os/test_getenv_setenv.mojo b/stdlib/test/os/test_getenv_setenv.mojo index 63fce8683f..5c1ffad832 100644 --- a/stdlib/test/os/test_getenv_setenv.mojo +++ b/stdlib/test/os/test_getenv_setenv.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: linux || darwin +# REQUIRES: system-linux || system-darwin # RUN: TEST_MYVAR=MyValue %mojo %s from os import getenv, setenv diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 010ada600e..43d677fc63 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !windows +# REQUIRES: !system-windows # RUN: %mojo -D TEMP_FILE=%t %s import os diff --git a/stdlib/test/pwd/test_pwd.mojo b/stdlib/test/pwd/test_pwd.mojo index 78178b68a7..f58121a69d 100644 --- a/stdlib/test/pwd/test_pwd.mojo +++ b/stdlib/test/pwd/test_pwd.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !windows +# REQUIRES: !system-windows # RUN: %mojo %s import pwd diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 1d07b467e4..d5101c8eb0 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -14,7 +14,7 @@ # This file is only run on macos targets. # # ===----------------------------------------------------------------------=== # -# REQUIRES: darwin +# REQUIRES: system-darwin # RUN: %mojo %s diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index 99ad6f1b8d..f14807e792 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -14,7 +14,7 @@ # This file is only run on windows targets. # # ===----------------------------------------------------------------------=== # -# REQUIRES: windows +# REQUIRES: system-windows # RUN: mojo.exe %s From 9a7a5cb9c921b6e410f6ae03fda37c6638719d75 Mon Sep 17 00:00:00 2001 From: Ahajha <44127594+Ahajha@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:53:44 -0400 Subject: [PATCH 1564/2019] Update nightly branch to point to nightly conda channel Update Conda channel to new URL Ignore magic cache folders Fix tutorial magic file MODULAR_ORIG_COMMIT_REV_ID: 805d317a0d8d391b9a525ec49f27dbd1d6df70d3 --- examples/mojoproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mojoproject.toml b/examples/mojoproject.toml index e0541dd041..7289b853cd 100644 --- a/examples/mojoproject.toml +++ b/examples/mojoproject.toml @@ -3,7 +3,7 @@ name = "Mojo examples" version = "0.1.0" description = "A collection of Mojo code examples" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] platforms = ["osx-arm64","linux-64","linux-aarch64"] [dependencies] From c8722c2bd0c43ac194a29583f27f633d333ac8b9 Mon Sep 17 00:00:00 2001 From: Ahajha <44127594+Ahajha@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:17:09 -0400 Subject: [PATCH 1565/2019] [examples] Update remaining conda package URLs MODULAR_ORIG_COMMIT_REV_ID: e121015ca0f25c68fa0869b0f8b9fc393ddce746 --- examples/notebooks/pixi.toml | 2 +- pixi.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml index 668fdf4caa..ffddf3150e 100644 --- a/examples/notebooks/pixi.toml +++ b/examples/notebooks/pixi.toml @@ -3,7 +3,7 @@ name = "Mojo notebooks" version = "1.0.0" description = "Environment for running JupyterLab" authors = ["Modular "] -channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max"] +channels = ["conda-forge", "https://conda.modular.com/max"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] diff --git a/pixi.toml b/pixi.toml index af506b0fc5..8dddbc4900 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,7 +1,7 @@ [project] name = "Mojo" authors = ["Modular "] -channels = ["conda-forge", "https://conda.cloudsmith.io/modular/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] platforms = ["linux-64", "linux-aarch64", "osx-arm64"] [tasks] From ce793af18df97f2b6891c915b83492ea01e32f56 Mon Sep 17 00:00:00 2001 From: Ahajha <44127594+Ahajha@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:47:20 -0400 Subject: [PATCH 1566/2019] [docs][examples] Always use trailing slashes in Conda channel URL MODULAR_ORIG_COMMIT_REV_ID: 41ea352e4b898f6a61dca52a1f790daac49ca86c --- CONTRIBUTING.md | 4 ++-- README.md | 4 ++-- examples/mojoproject.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73f374a0a1..a5392781df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -272,13 +272,13 @@ magic init mojo-nightly --format mojoproject \ ``` If you're [using conda](https://docs.modular.com/magic/conda), add the -`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +`https://conda.modular.com/max-nightly/` channel to your `environment.yaml` file. For example: ```yaml [project] name = "Mojo nightly example" -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] diff --git a/README.md b/README.md index a3175c6088..51fa8e08fa 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,13 @@ magic init hello-world-nightly --format mojoproject \ ``` Or, if you're [using conda](https://docs.modular.com/magic/conda), add the -`https://conda.modular.com/max-nightly` channel to your `environment.yaml` +`https://conda.modular.com/max-nightly/` channel to your `environment.yaml` file. For example: ```yaml [project] name = "Mojo nightly example" -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] diff --git a/examples/mojoproject.toml b/examples/mojoproject.toml index 7289b853cd..bd1d365cd9 100644 --- a/examples/mojoproject.toml +++ b/examples/mojoproject.toml @@ -3,7 +3,7 @@ name = "Mojo examples" version = "0.1.0" description = "A collection of Mojo code examples" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] platforms = ["osx-arm64","linux-64","linux-aarch64"] [dependencies] From 861018955cb1cbcb8a40455bdbdcccfc7f6c7e7a Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 18 Sep 2024 11:39:30 -0700 Subject: [PATCH 1567/2019] [stdlib] Enable debug info in test_stdin Seems like the issue we were tracking for this has been fixed MODULAR_ORIG_COMMIT_REV_ID: c78ac775f1707fdd778223814b278f7625175125 --- stdlib/test/builtin/test_stdin.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_stdin.mojo b/stdlib/test/builtin/test_stdin.mojo index c88619f955..ac566def06 100644 --- a/stdlib/test/builtin/test_stdin.mojo +++ b/stdlib/test/builtin/test_stdin.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: !system-windows -# RUN: echo "Hello, World" | %mojo-no-debug %s +# RUN: echo "Hello, World" | %mojo %s from builtin.io import _fdopen from testing import testing From c4f2559a5ab2f08c5dade04058d70d5183e85394 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 18 Sep 2024 12:32:47 -0700 Subject: [PATCH 1568/2019] [stdlib] Fix invalid REQUIRES MODULAR_ORIG_COMMIT_REV_ID: 07ac0ae836bbbd16689396174f2b91c222d9f159 --- stdlib/test/sys/test_linux_target.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/sys/test_linux_target.mojo b/stdlib/test/sys/test_linux_target.mojo index a118683ca3..dd78cb3bb3 100644 --- a/stdlib/test/sys/test_linux_target.mojo +++ b/stdlib/test/sys/test_linux_target.mojo @@ -14,7 +14,7 @@ # This file is only run on linux targets. # # ===----------------------------------------------------------------------=== # -# REQUIRES: linux +# REQUIRES: system-linux # RUN: %mojo %s from sys import os_is_linux, os_is_macos From dd777fdf573857a9564c4effce12dae8aa451981 Mon Sep 17 00:00:00 2001 From: Derek Smith Date: Wed, 18 Sep 2024 13:11:29 -0700 Subject: [PATCH 1569/2019] [External] [stdlib] Make `UInt` `Hashable` again (#47433) [External] [stdlib] Make `UInt` `Hashable` again ### Problem Calling `hash` on a `UInt` value doesn't compile, e.g. ```mojo def main(): var x: UInt = 1234 hash(x) ``` results in ``` error: no matching function in call to 'hash' hash(u) ~~~~^~~ ``` even though it does compile for `UInt8`, `UInt16`, `UInt32`, and `UInt64`. This means using `UInt` as a key to `Dict` doesn't compile either, for example ```mojo from collections import Dict def main(): var d = Dict[UInt, String]() ``` results in ``` error: 'Dict' parameter #0 has 'KeyElement' type, but value has type 'AnyStruct[UInt]' var d = Dict[UInt, String]() ^~~~ ``` ### Fix This _does_ work for `Int8` through `Int64` as well as `Int` itself, so I essentially copied over the definition of `Int.__hash__` with the appropriate change to the underlying `DType` passed to `_hash_simd`, under the assumption it should work the same for `UInt`. Added a (very simple) test similar to that for `String.__hash__` too. Co-authored-by: Derek Smith Closes modularml/mojo#3491 MODULAR_ORIG_COMMIT_REV_ID: c258aaeba7adf7927a5d401e2c2432ef914b8abf --- stdlib/src/builtin/uint.mojo | 12 ++++++++++++ stdlib/test/builtin/test_uint.mojo | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index dee9d73027..5ff6c01972 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import bitwidthof from utils._visualizers import lldb_formatter_wrapping_type +from builtin.hash import _hash_simd @lldb_formatter_wrapping_type @@ -130,6 +131,17 @@ struct UInt(IntLike): """ return "UInt(" + str(self) + ")" + fn __hash__(self) -> UInt: + """Hash the UInt using builtin hash. + + Returns: + A 64-bit hash value. This value is _not_ suitable for cryptographic + uses. Its intended usage is for data structures. See the `hash` + builtin documentation for more details. + """ + # TODO(MOCO-636): switch to DType.index + return _hash_simd(Scalar[DType.uint64](self)) + @always_inline("nodebug") fn __eq__(self, rhs: UInt) -> Bool: """Compare this UInt to the RHS using EQ comparison. diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 1a782c52c0..9e63c45937 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -238,6 +238,12 @@ def test_pos(): assert_equal(UInt(0).__pos__(), UInt(0)) +def test_hash(): + assert_not_equal(UInt.__hash__(123), UInt.__hash__(456)) + assert_equal(UInt.__hash__(123), UInt.__hash__(123)) + assert_equal(UInt.__hash__(456), UInt.__hash__(456)) + + def test_comptime(): alias a: UInt = 32 # Verify that count_trailing_zeros works at comptime. @@ -268,4 +274,5 @@ def main(): test_indexer() test_comparison() test_pos() + test_hash() test_comptime() From 4cb32a034301abbfbf61a19b388a61010ce6fabd Mon Sep 17 00:00:00 2001 From: Derek Smith Date: Wed, 18 Sep 2024 13:26:53 -0700 Subject: [PATCH 1570/2019] [External] [docs] Docs typo fixes (#47439) [External] [docs] Docs typo fixes Couple minor typo fixes in the pointers manual. Co-authored-by: Derek Smith Closes modularml/mojo#3499 MODULAR_ORIG_COMMIT_REV_ID: 34b727a252c6db119e5320049aadd3522e83ddd3 --- docs/manual/pointers.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index 0bda2759bd..d9e2c97284 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -449,7 +449,7 @@ "## Working with foreign pointers\n", "\n", "When exchanging data with other programming languages, you may need to construct\n", - "an `UnsafePointer` from an a foreign pointer. Mojo restricts creating \n", + "an `UnsafePointer` from a foreign pointer. Mojo restricts creating \n", "`UnsafePointer` instances from arbitrary addresses, to avoid users accidentally \n", "creating pointers that _alias_ each other (that is, two pointers that refer to\n", "the same location). However, there are specific methods you can use to get an\n", @@ -711,7 +711,7 @@ "## `UnsafePointer` and `Reference`\n", "\n", "The [`Reference`](/mojo/stdlib/memory/reference/Reference) type is essentially a \n", - "safe pointer type. Like a pointer, you can derferences a `Reference` using the \n", + "safe pointer type. Like a pointer, you can derference a `Reference` using the \n", "dereference operator, `[]`. However, the `Reference` type has several\n", "differences from `UnsafePointer` which make it safer:\n", "\n", From 12571e41449d0ec911be9036596459202c2286f2 Mon Sep 17 00:00:00 2001 From: Derek Smith Date: Wed, 18 Sep 2024 17:07:10 -0700 Subject: [PATCH 1571/2019] [External] [stdlib] Modify `String.__repr__` to handle multi-byte UTF-8 characters (#47447) [External] [stdlib] Modify `String.__repr__` to handle multi-byte UTF-8 characters This PR fixes https://github.com/modularml/mojo/issues/3456 by modifying `String.__repr__` to handle multi-byte UTF-8 characters. The original code iterated over the string's internal bytes buffer and handled each one individually, causing the multi-byte characters to get emitted separately. I took advantage of `String.__iter__` since it already has the logic internally to slice the string up into its UTF-8 characters, but I do have another implementation that still iterates over the byte buffer and pulls out the characters directly, if that solution is preferred for whatever reason. The old definition for the module function `string.ascii` used to just call `String.__repr__` directly, so I copied the old definition of `String.__repr__` to `string.ascii` so that it still works the same. Co-authored-by: Derek Smith Closes modularml/mojo#3495 MODULAR_ORIG_COMMIT_REV_ID: fd61985dd2145a5b341a557fdbdcaa82fa1e4b9c --- stdlib/src/collections/string.mojo | 42 +++++++++++++++++++----- stdlib/test/collections/test_string.mojo | 15 ++++++--- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index b67304e66d..fbedee4e2b 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -202,7 +202,6 @@ fn _repr_ascii(c: UInt8) -> String: return hex(uc, prefix=r"\x") -# TODO: This is currently the same as repr, should change with unicode strings @always_inline fn ascii(value: String) -> String: """Get the ASCII representation of the object. @@ -213,7 +212,19 @@ fn ascii(value: String) -> String: Returns: A string containing the ASCII representation of the object. """ - return value.__repr__() + alias ord_squote = ord("'") + var result = String() + var use_dquote = False + + for idx in range(len(value._buffer) - 1): + var char = value._buffer[idx] + result += _repr_ascii(char) + use_dquote = use_dquote or (char == ord_squote) + + if use_dquote: + return '"' + result + '"' + else: + return "'" + result + "'" # ===----------------------------------------------------------------------=== # @@ -1179,14 +1190,29 @@ struct String( Returns: A new representation of the string. """ - alias ord_squote = ord("'") var result = String() var use_dquote = False - - for idx in range(len(self._buffer) - 1): - var char = self._buffer[idx] - result += _repr_ascii(char) - use_dquote = use_dquote or (char == ord_squote) + for s in self: + use_dquote = use_dquote or (s == "'") + + if s == "\\": + result += r"\\" + elif s == "\t": + result += r"\t" + elif s == "\n": + result += r"\n" + elif s == "\r": + result += r"\r" + else: + var codepoint = ord(s) + if isprintable(codepoint): + result += s + elif codepoint < 0x10: + result += hex(codepoint, prefix=r"\x0") + elif codepoint < 0x20 or codepoint == 0x7F: + result += hex(codepoint, prefix=r"\x") + else: # multi-byte character + result += s if use_dquote: return '"' + result + '"' diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 86c3222260..b857e4fa83 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -45,11 +45,14 @@ def test_stringable(): def test_repr(): - # Usual cases + # Standard single-byte characters assert_equal(String.__repr__("hello"), "'hello'") assert_equal(String.__repr__(str(0)), "'0'") + assert_equal(String.__repr__("A"), "'A'") + assert_equal(String.__repr__(" "), "' '") + assert_equal(String.__repr__("~"), "'~'") - # Escape cases + # Special single-byte characters assert_equal(String.__repr__("\0"), r"'\x00'") assert_equal(String.__repr__("\x06"), r"'\x06'") assert_equal(String.__repr__("\x09"), r"'\t'") @@ -57,13 +60,15 @@ def test_repr(): assert_equal(String.__repr__("\x0d"), r"'\r'") assert_equal(String.__repr__("\x0e"), r"'\x0e'") assert_equal(String.__repr__("\x1f"), r"'\x1f'") - assert_equal(String.__repr__(" "), "' '") assert_equal(String.__repr__("'"), '"\'"') - assert_equal(String.__repr__("A"), "'A'") assert_equal(String.__repr__("\\"), r"'\\'") - assert_equal(String.__repr__("~"), "'~'") assert_equal(String.__repr__("\x7f"), r"'\x7f'") + # Multi-byte characters + assert_equal(String.__repr__("Örnsköldsvik"), "'Örnsköldsvik'") # 2-byte + assert_equal(String.__repr__("你好!"), "'你好!'") # 3-byte + assert_equal(String.__repr__("hello 🔥!"), "'hello 🔥!'") # 4-byte + def test_constructors(): # Default construction From 472e98677ab069b59a163c827666d51852a2f8a0 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 19 Sep 2024 15:30:40 -0500 Subject: [PATCH 1572/2019] [stdlib] Factor out CPython.log helper function (NFC) This is a minor change to reduce some of the boilerplate in the low-level CPython wrapping functions. MODULAR_ORIG_COMMIT_REV_ID: da892046f93bcf193a90723a51ec55d26598667e --- stdlib/src/python/_cpython.mojo | 430 ++++++++++++++++++-------------- 1 file changed, 245 insertions(+), 185 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 73ac413667..255a808a8e 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -401,6 +401,10 @@ struct PyModuleDef(Stringable, Representable, Formattable): struct CPython: + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + var lib: DLHandle var dict_type: PyObjectPtr var logging_enabled: Bool @@ -408,6 +412,10 @@ struct CPython: var total_ref_count: UnsafePointer[Int] var init_error: StringRef + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__(inout self): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: @@ -456,6 +464,17 @@ struct CPython: else: self.version = PythonVersion(0, 0, 0) + fn __del__(owned self): + pass + + fn __copyinit__(inout self, existing: Self): + self.lib = existing.lib + self.dict_type = existing.dict_type + self.logging_enabled = existing.logging_enabled + self.version = existing.version + self.total_ref_count = existing.total_ref_count + self.init_error = existing.init_error + @staticmethod fn destroy(inout existing: CPython): if existing.logging_enabled: @@ -489,6 +508,10 @@ struct CPython: error += "\n https://modul.ar/fix-python\n" raise error + # ===-------------------------------------------------------------------===# + # None + # ===-------------------------------------------------------------------===# + fn Py_None(inout self) -> PyObjectPtr: """Get a None value, of type NoneType.""" @@ -506,16 +529,37 @@ struct CPython: return PyObjectPtr(ptr) - fn __del__(owned self): - pass + # ===-------------------------------------------------------------------===# + # Logging + # ===-------------------------------------------------------------------===# - fn __copyinit__(inout self, existing: Self): - self.lib = existing.lib - self.dict_type = existing.dict_type - self.logging_enabled = existing.logging_enabled - self.version = existing.version - self.total_ref_count = existing.total_ref_count - self.init_error = existing.init_error + @always_inline + fn log[*Ts: Formattable](self, *args: *Ts): + """If logging is enabled, print the given arguments as a log message. + + Parameters: + Ts: The argument types. + + Arguments: + args: The arguments to log. + """ + if not self.logging_enabled: + return + + # TODO(MOCO-358): + # Once Mojo argument splatting is supported, this should just + # be: `print(*args)` + @parameter + fn print_arg[T: Formattable](arg: T): + print(arg, sep="", end="", flush=False) + + args.each[print_arg]() + + print(flush=True) + + # ===-------------------------------------------------------------------===# + # Reference count management + # ===-------------------------------------------------------------------===# fn _inc_total_rc(inout self): var v = self.total_ref_count.take_pointee() @@ -526,18 +570,14 @@ struct CPython: self.total_ref_count.init_pointee_move(v - 1) fn Py_IncRef(inout self, ptr: PyObjectPtr): - if self.logging_enabled: - print( - ptr._get_ptr_as_int(), " INCREF refcnt:", self._Py_REFCNT(ptr) - ) + self.log(ptr._get_ptr_as_int(), " INCREF refcnt:", self._Py_REFCNT(ptr)) + self.lib.get_function[fn (PyObjectPtr) -> None]("Py_IncRef")(ptr) self._inc_total_rc() fn Py_DecRef(inout self, ptr: PyObjectPtr): - if self.logging_enabled: - print( - ptr._get_ptr_as_int(), " DECREF refcnt:", self._Py_REFCNT(ptr) - ) + self.log(ptr._get_ptr_as_int(), " DECREF refcnt:", self._Py_REFCNT(ptr)) + self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) self._dec_total_rc() @@ -565,12 +605,13 @@ struct CPython: fn PyDict_New(inout self) -> PyObjectPtr: var r = self.lib.get_function[fn () -> PyObjectPtr]("PyDict_New")() - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyDict_New, refcnt:", - self._Py_REFCNT(r), - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyDict_New, refcnt:", + self._Py_REFCNT(r), + ) + self._inc_total_rc() return r @@ -580,13 +621,14 @@ struct CPython: var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> Int32 ](StringRef("PyDict_SetItem"))(dict_obj, key, value) - if self.logging_enabled: - print( - "PyDict_SetItem, key: ", - key._get_ptr_as_int(), - " value: ", - value._get_ptr_as_int(), - ) + + self.log( + "PyDict_SetItem, key: ", + key._get_ptr_as_int(), + " value: ", + value._get_ptr_as_int(), + ) + return int(r) fn PyDict_GetItemWithError( @@ -595,8 +637,9 @@ struct CPython: var result = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr ](StringRef("PyDict_GetItemWithError"))(dict_obj, key) - if self.logging_enabled: - print("PyDict_GetItemWithError, key: ", key._get_ptr_as_int()) + + self.log("PyDict_GetItemWithError, key: ", key._get_ptr_as_int()) + return result fn PyEval_GetBuiltins(inout self) -> PyObjectPtr: @@ -611,14 +654,15 @@ struct CPython: var r = self.lib.get_function[fn (UnsafePointer[UInt8]) -> PyObjectPtr]( "PyImport_ImportModule" )(name.data) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyImport_ImportModule, str:", - name, - ", refcnt:", - self._Py_REFCNT(r), - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyImport_ImportModule, str:", + name, + ", refcnt:", + self._Py_REFCNT(r), + ) + self._inc_total_rc() return r @@ -690,16 +734,17 @@ struct CPython: ) -> UnsafePointer[Int8] ]("PyRun_String")(strref.data, Int32(run_mode), globals, locals) ) - if self.logging_enabled: - print( - result._get_ptr_as_int(), - " NEWREF PyRun_String, str:", - strref, - ", ptr: ", - result._get_ptr_as_int(), - ", refcnt:", - self._Py_REFCNT(result), - ) + + self.log( + result._get_ptr_as_int(), + " NEWREF PyRun_String, str:", + strref, + ", ptr: ", + result._get_ptr_as_int(), + ", refcnt:", + self._Py_REFCNT(result), + ) + self._inc_total_rc() return result @@ -741,16 +786,17 @@ struct CPython: var r = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[UInt8]) -> PyObjectPtr ]("PyObject_GetAttrString")(obj, name.data) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyObject_GetAttrString, str:", - name, - ", refcnt:", - self._Py_REFCNT(r), - ", parent obj:", - obj._get_ptr_as_int(), - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyObject_GetAttrString, str:", + name, + ", refcnt:", + self._Py_REFCNT(r), + ", parent obj:", + obj._get_ptr_as_int(), + ) + self._inc_total_rc() return r @@ -760,17 +806,18 @@ struct CPython: var r = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[UInt8], PyObjectPtr) -> Int ]("PyObject_SetAttrString")(obj, name.data, new_value) - if self.logging_enabled: - print( - "PyObject_SetAttrString str:", - name, - ", parent obj:", - obj._get_ptr_as_int(), - ", new value:", - new_value._get_ptr_as_int(), - " new value ref count: ", - self._Py_REFCNT(new_value), - ) + + self.log( + "PyObject_SetAttrString str:", + name, + ", parent obj:", + obj._get_ptr_as_int(), + ", new value:", + new_value._get_ptr_as_int(), + " new value ref count: ", + self._Py_REFCNT(new_value), + ) + return r fn PyObject_CallObject( @@ -781,14 +828,15 @@ struct CPython: var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr ]("PyObject_CallObject")(callable_obj, args) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyObject_CallObject, refcnt:", - self._Py_REFCNT(r), - ", callable obj:", - callable_obj._get_ptr_as_int(), - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyObject_CallObject, refcnt:", + self._Py_REFCNT(r), + ", callable obj:", + callable_obj._get_ptr_as_int(), + ) + self._inc_total_rc() return r @@ -801,14 +849,15 @@ struct CPython: var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr ]("PyObject_Call")(callable_obj, args, kwargs) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyObject_Call, refcnt:", - self._Py_REFCNT(r), - ", callable obj:", - callable_obj._get_ptr_as_int(), - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyObject_Call, refcnt:", + self._Py_REFCNT(r), + ", callable obj:", + callable_obj._get_ptr_as_int(), + ) + self._inc_total_rc() return r @@ -841,14 +890,15 @@ struct CPython: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( StringRef("PyTuple_New") )(count) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyTuple_New, refcnt:", - self._Py_REFCNT(r), - ", tuple size:", - count, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyTuple_New, refcnt:", + self._Py_REFCNT(r), + ", tuple size:", + count, + ) + self._inc_total_rc() return r @@ -875,14 +925,15 @@ struct CPython: ](StringRef("PyUnicode_DecodeUTF8"))( strref.data, strref.length, "strict".unsafe_cstr_ptr() ) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyString_FromStringAndSize, refcnt:", - self._Py_REFCNT(r), - ", str:", - strref, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyString_FromStringAndSize, refcnt:", + self._Py_REFCNT(r), + ", str:", + strref, + ) + self._inc_total_rc() return r @@ -890,14 +941,15 @@ struct CPython: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( "PyLong_FromLong" )(value) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyLong_FromLong, refcnt:", - self._Py_REFCNT(r), - ", value:", - value, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyLong_FromLong, refcnt:", + self._Py_REFCNT(r), + ", value:", + value, + ) + self._inc_total_rc() return r @@ -917,14 +969,15 @@ struct CPython: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( "PyBool_FromLong" )(value) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyBool_FromLong, refcnt:", - self._Py_REFCNT(r), - ", value:", - value, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyBool_FromLong, refcnt:", + self._Py_REFCNT(r), + ", value:", + value, + ) + self._inc_total_rc() return r @@ -932,14 +985,15 @@ struct CPython: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]("PyList_New")( length ) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyList_New, refcnt:", - self._Py_REFCNT(r), - ", list size:", - length, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyList_New, refcnt:", + self._Py_REFCNT(r), + ", list size:", + length, + ) + self._inc_total_rc() return r @@ -983,14 +1037,15 @@ struct CPython: var r = self.lib.get_function[fn (Float64) -> PyObjectPtr]( "PyFloat_FromDouble" )(value) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyFloat_FromDouble, refcnt:", - self._Py_REFCNT(r), - ", value:", - value, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyFloat_FromDouble, refcnt:", + self._Py_REFCNT(r), + ", value:", + value, + ) + self._inc_total_rc() return r @@ -1004,14 +1059,15 @@ struct CPython: var r = self.lib.get_function[fn (Int8) -> PyObjectPtr]( "PyBool_FromLong" )(Int8(long)) - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyBool_FromLong, refcnt:", - self._Py_REFCNT(r), - ", value:", - value, - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyBool_FromLong, refcnt:", + self._Py_REFCNT(r), + ", value:", + value, + ) + self._inc_total_rc() return r @@ -1048,12 +1104,13 @@ struct CPython: ) -> None ]("PyErr_Fetch")(type_ptr, value_ptr, traceback_ptr) var r = PyObjectPtr {value: value} - if self.logging_enabled: - print( - r._get_ptr_as_int(), - " NEWREF PyErr_Fetch, refcnt:", - self._Py_REFCNT(r), - ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyErr_Fetch, refcnt:", + self._Py_REFCNT(r), + ) + self._inc_total_rc() _ = type _ = value @@ -1106,16 +1163,17 @@ struct CPython: var iter = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyObject_GetIter" )(traversablePyObject) - if self.logging_enabled: - print( - iter._get_ptr_as_int(), - " NEWREF PyObject_GetIter, refcnt:", - self._Py_REFCNT(iter), - "referencing ", - traversablePyObject._get_ptr_as_int(), - "refcnt of traversable: ", - self._Py_REFCNT(traversablePyObject), - ) + + self.log( + iter._get_ptr_as_int(), + " NEWREF PyObject_GetIter, refcnt:", + self._Py_REFCNT(iter), + "referencing ", + traversablePyObject._get_ptr_as_int(), + "refcnt of traversable: ", + self._Py_REFCNT(traversablePyObject), + ) + self._inc_total_rc() return iter @@ -1123,16 +1181,17 @@ struct CPython: var next_obj = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyIter_Next" )(iterator) - if self.logging_enabled: - print( - next_obj._get_ptr_as_int(), - " NEWREF PyIter_Next from ", - iterator._get_ptr_as_int(), - ", refcnt(obj):", - self._Py_REFCNT(next_obj), - "refcnt(iter)", - self._Py_REFCNT(iterator), - ) + + self.log( + next_obj._get_ptr_as_int(), + " NEWREF PyIter_Next from ", + iterator._get_ptr_as_int(), + ", refcnt(obj):", + self._Py_REFCNT(next_obj), + "refcnt(iter)", + self._Py_REFCNT(iterator), + ) + if next_obj._get_ptr_as_int() != 0: self._inc_total_rc() return next_obj @@ -1171,22 +1230,23 @@ struct CPython: key_ptr, value_ptr, ) - if self.logging_enabled: - print( - dictionary._get_ptr_as_int(), - " NEWREF PyDict_Next", - dictionary._get_ptr_as_int(), - "refcnt:", - self._Py_REFCNT(dictionary), - " key: ", - PyObjectPtr {value: key}._get_ptr_as_int(), - ", refcnt(key):", - self._Py_REFCNT(key), - "value:", - PyObjectPtr {value: value}._get_ptr_as_int(), - "refcnt(value)", - self._Py_REFCNT(value), - ) + + self.log( + dictionary._get_ptr_as_int(), + " NEWREF PyDict_Next", + dictionary._get_ptr_as_int(), + "refcnt:", + self._Py_REFCNT(dictionary), + " key: ", + PyObjectPtr {value: key}._get_ptr_as_int(), + ", refcnt(key):", + self._Py_REFCNT(key), + "value:", + PyObjectPtr {value: value}._get_ptr_as_int(), + "refcnt(value)", + self._Py_REFCNT(value), + ) + _ = v return PyKeyValuePair { key: key, From ffaedae327da738623241c9f6629c9e767339eeb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 19 Sep 2024 16:07:43 -0500 Subject: [PATCH 1573/2019] cleanup: Reorganize and group CPython methods (NFC) No functions were added, removed, or modified, just reorganized. MODULAR_ORIG_COMMIT_REV_ID: 636acf37014a7b15911a44b3ca12a9560f6c175f --- stdlib/src/python/_cpython.mojo | 390 +++++++++++++++++--------------- 1 file changed, 213 insertions(+), 177 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 255a808a8e..7c31a928c3 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -581,6 +581,20 @@ struct CPython: self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) self._dec_total_rc() + # This function assumes a specific way PyObjectPtr is implemented, namely + # that the refcount has offset 0 in that structure. That generally doesn't + # have to always be the case - but often it is and it's convenient for + # debugging. We shouldn't rely on this function anywhere - its only purpose + # is debugging. + fn _Py_REFCNT(inout self, ptr: PyObjectPtr) -> Int: + if ptr._get_ptr_as_int() == 0: + return -1 + return int(ptr.value.load()) + + # ===-------------------------------------------------------------------===# + # Python GIL and threading + # ===-------------------------------------------------------------------===# + fn PyGILState_Ensure(inout self) -> Bool: return self.lib.get_function[fn () -> Bool]("PyGILState_Ensure")() @@ -593,15 +607,9 @@ struct CPython: fn PyEval_RestoreThread(inout self, state: Int64): self.lib.get_function[fn (Int64) -> None]("PyEval_RestoreThread")(state) - # This function assumes a specific way PyObjectPtr is implemented, namely - # that the refcount has offset 0 in that structure. That generally doesn't - # have to always be the case - but often it is and it's convenient for - # debugging. We shouldn't rely on this function anywhere - its only purpose - # is debugging. - fn _Py_REFCNT(inout self, ptr: PyObjectPtr) -> Int: - if ptr._get_ptr_as_int() == 0: - return -1 - return int(ptr.value.load()) + # ===-------------------------------------------------------------------===# + # Python Dict operations + # ===-------------------------------------------------------------------===# fn PyDict_New(inout self) -> PyObjectPtr: var r = self.lib.get_function[fn () -> PyObjectPtr]("PyDict_New")() @@ -642,10 +650,69 @@ struct CPython: return result - fn PyEval_GetBuiltins(inout self) -> PyObjectPtr: - return self.lib.get_function[fn () -> PyObjectPtr]( - "PyEval_GetBuiltins" - )() + fn PyDict_Check(inout self, maybe_dict: PyObjectPtr) -> Bool: + var my_type = self.PyObject_Type(maybe_dict) + var my_type_as_int = my_type._get_ptr_as_int() + var dict_type = self.PyDict_Type() + var result = my_type_as_int == dict_type._get_ptr_as_int() + self.Py_DecRef(my_type) + return result + + fn PyDict_Type(inout self) -> PyObjectPtr: + if self.dict_type.is_null(): + self.dict_type = self.lib.get_function[PyObjectPtr]("PyDict_Type") + return self.dict_type + + fn PyDict_Next( + inout self, dictionary: PyObjectPtr, p: Int + ) -> PyKeyValuePair: + var key = UnsafePointer[Int8]() + var value = UnsafePointer[Int8]() + var v = p + var position = UnsafePointer[Int].address_of(v) + var value_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(value) + var key_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(key) + var result = self.lib.get_function[ + fn ( + PyObjectPtr, + UnsafePointer[Int], + UnsafePointer[UnsafePointer[Int8]], + UnsafePointer[UnsafePointer[Int8]], + ) -> Int + ]("PyDict_Next")( + dictionary, + position, + key_ptr, + value_ptr, + ) + + self.log( + dictionary._get_ptr_as_int(), + " NEWREF PyDict_Next", + dictionary._get_ptr_as_int(), + "refcnt:", + self._Py_REFCNT(dictionary), + " key: ", + PyObjectPtr {value: key}._get_ptr_as_int(), + ", refcnt(key):", + self._Py_REFCNT(key), + "value:", + PyObjectPtr {value: value}._get_ptr_as_int(), + "refcnt(value)", + self._Py_REFCNT(value), + ) + + _ = v + return PyKeyValuePair { + key: key, + value: value, + position: position.take_pointee(), + success: result == 1, + } + + # ===-------------------------------------------------------------------===# + # Python Module operations + # ===-------------------------------------------------------------------===# fn PyImport_ImportModule( inout self, @@ -666,6 +733,12 @@ struct CPython: self._inc_total_rc() return r + fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: + var value = self.lib.get_function[ + fn (UnsafePointer[UInt8]) -> UnsafePointer[Int8] + ]("PyImport_AddModule")(name.data) + return PyObjectPtr {value: value} + fn PyModule_Create( inout self, name: String, @@ -703,6 +776,16 @@ struct CPython: return add_functions_fn(mod, functions) + fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: + var value = self.lib.get_function[ + fn (PyObjectPtr) -> UnsafePointer[Int8] + ]("PyModule_GetDict")(name.value) + return PyObjectPtr {value: value} + + # ===-------------------------------------------------------------------===# + # Python Evaluation + # ===-------------------------------------------------------------------===# + fn PyRun_SimpleString(inout self, strref: StringRef) -> Bool: """Executes the given Python code. @@ -764,6 +847,11 @@ struct CPython: self._inc_total_rc() return result + fn PyEval_GetBuiltins(inout self) -> PyObjectPtr: + return self.lib.get_function[fn () -> PyObjectPtr]( + "PyEval_GetBuiltins" + )() + fn Py_CompileString( inout self, strref: StringRef, @@ -778,6 +866,37 @@ struct CPython: self._inc_total_rc() return r + # ===-------------------------------------------------------------------===# + # Python Object operations + # ===-------------------------------------------------------------------===# + + fn Py_Is( + inout self, + rhs: PyObjectPtr, + lhs: PyObjectPtr, + ) -> Bool: + if self.version.minor >= 10: + var r = self.lib.get_function[fn (PyObjectPtr, PyObjectPtr) -> Int]( + "Py_Is" + )(rhs, lhs) + return r > 0 + else: + return rhs == lhs + + fn PyObject_Type(inout self, obj: PyObjectPtr) -> PyObjectPtr: + var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( + "PyObject_Type" + ) + self._inc_total_rc() + return f(obj) + + fn PyObject_Str(inout self, obj: PyObjectPtr) -> PyObjectPtr: + var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( + "PyObject_Str" + ) + self._inc_total_rc() + return f(obj) + fn PyObject_GetAttrString( inout self, obj: PyObjectPtr, @@ -886,6 +1005,30 @@ struct CPython: self.lib.get_function[fn (PyObjectPtr) -> Int]("PyObject_Hash")(obj) ) + fn PyObject_GetIter( + inout self, traversablePyObject: PyObjectPtr + ) -> PyObjectPtr: + var iter = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( + "PyObject_GetIter" + )(traversablePyObject) + + self.log( + iter._get_ptr_as_int(), + " NEWREF PyObject_GetIter, refcnt:", + self._Py_REFCNT(iter), + "referencing ", + traversablePyObject._get_ptr_as_int(), + "refcnt of traversable: ", + self._Py_REFCNT(traversablePyObject), + ) + + self._inc_total_rc() + return iter + + # ===-------------------------------------------------------------------===# + # Python Tuple operations + # ===-------------------------------------------------------------------===# + fn PyTuple_New(inout self, count: Int) -> PyObjectPtr: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( StringRef("PyTuple_New") @@ -915,28 +1058,47 @@ struct CPython: StringRef("PyTuple_SetItem") )(tuple_obj, index, element) - fn PyString_FromStringAndSize(inout self, strref: StringRef) -> PyObjectPtr: - var r = self.lib.get_function[ - fn ( - UnsafePointer[UInt8], - Int, - UnsafePointer[C_char], - ) -> PyObjectPtr - ](StringRef("PyUnicode_DecodeUTF8"))( - strref.data, strref.length, "strict".unsafe_cstr_ptr() + # ===-------------------------------------------------------------------===# + # Python List operations + # ===-------------------------------------------------------------------===# + + fn PyList_New(inout self, length: Int) -> PyObjectPtr: + var r = self.lib.get_function[fn (Int) -> PyObjectPtr]("PyList_New")( + length ) self.log( r._get_ptr_as_int(), - " NEWREF PyString_FromStringAndSize, refcnt:", + " NEWREF PyList_New, refcnt:", self._Py_REFCNT(r), - ", str:", - strref, + ", list size:", + length, ) self._inc_total_rc() return r + fn PyList_SetItem( + inout self, list_obj: PyObjectPtr, index: Int, value: PyObjectPtr + ) -> PyObjectPtr: + # PyList_SetItem steals the reference - the element object will be + # destroyed along with the list + self._dec_total_rc() + return self.lib.get_function[ + fn (PyObjectPtr, Int, PyObjectPtr) -> PyObjectPtr + ]("PyList_SetItem")(list_obj, index, value) + + fn PyList_GetItem( + inout self, list_obj: PyObjectPtr, index: Int + ) -> PyObjectPtr: + return self.lib.get_function[fn (PyObjectPtr, Int) -> PyObjectPtr]( + "PyList_GetItem" + )(list_obj, index) + + # ===-------------------------------------------------------------------===# + # Primitive type conversions + # ===-------------------------------------------------------------------===# + fn PyLong_FromLong(inout self, value: Int) -> PyObjectPtr: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( "PyLong_FromLong" @@ -953,18 +1115,6 @@ struct CPython: self._inc_total_rc() return r - fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: - var value = self.lib.get_function[ - fn (PyObjectPtr) -> UnsafePointer[Int8] - ]("PyModule_GetDict")(name.value) - return PyObjectPtr {value: value} - - fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: - var value = self.lib.get_function[ - fn (UnsafePointer[UInt8]) -> UnsafePointer[Int8] - ]("PyImport_AddModule")(name.data) - return PyObjectPtr {value: value} - fn PyBool_FromLong(inout self, value: Int) -> PyObjectPtr: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( "PyBool_FromLong" @@ -981,39 +1131,6 @@ struct CPython: self._inc_total_rc() return r - fn PyList_New(inout self, length: Int) -> PyObjectPtr: - var r = self.lib.get_function[fn (Int) -> PyObjectPtr]("PyList_New")( - length - ) - - self.log( - r._get_ptr_as_int(), - " NEWREF PyList_New, refcnt:", - self._Py_REFCNT(r), - ", list size:", - length, - ) - - self._inc_total_rc() - return r - - fn PyList_SetItem( - inout self, list_obj: PyObjectPtr, index: Int, value: PyObjectPtr - ) -> PyObjectPtr: - # PyList_SetItem steals the reference - the element object will be - # destroyed along with the list - self._dec_total_rc() - return self.lib.get_function[ - fn (PyObjectPtr, Int, PyObjectPtr) -> PyObjectPtr - ]("PyList_SetItem")(list_obj, index, value) - - fn PyList_GetItem( - inout self, list_obj: PyObjectPtr, index: Int - ) -> PyObjectPtr: - return self.lib.get_function[fn (PyObjectPtr, Int) -> PyObjectPtr]( - "PyList_GetItem" - )(list_obj, index) - fn toPython(inout self, litString: StringRef) -> PyObjectPtr: return self.PyString_FromStringAndSize(litString) @@ -1071,12 +1188,38 @@ struct CPython: self._inc_total_rc() return r + fn PyString_FromStringAndSize(inout self, strref: StringRef) -> PyObjectPtr: + var r = self.lib.get_function[ + fn ( + UnsafePointer[UInt8], + Int, + UnsafePointer[C_char], + ) -> PyObjectPtr + ](StringRef("PyUnicode_DecodeUTF8"))( + strref.data, strref.length, "strict".unsafe_cstr_ptr() + ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyString_FromStringAndSize, refcnt:", + self._Py_REFCNT(r), + ", str:", + strref, + ) + + self._inc_total_rc() + return r + fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: var result = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[Int]) -> UnsafePointer[C_char] ]("PyUnicode_AsUTF8AndSize")(py_object, UnsafePointer[Int]()) return StringRef(result) + # ===-------------------------------------------------------------------===# + # Python Error operations + # ===-------------------------------------------------------------------===# + fn PyErr_Clear(inout self): self.lib.get_function[fn () -> None]("PyErr_Clear")() @@ -1117,66 +1260,6 @@ struct CPython: _ = traceback return r - fn Py_Is( - inout self, - rhs: PyObjectPtr, - lhs: PyObjectPtr, - ) -> Bool: - if self.version.minor >= 10: - var r = self.lib.get_function[fn (PyObjectPtr, PyObjectPtr) -> Int]( - "Py_Is" - )(rhs, lhs) - return r > 0 - else: - return rhs == lhs - - fn PyDict_Check(inout self, maybe_dict: PyObjectPtr) -> Bool: - var my_type = self.PyObject_Type(maybe_dict) - var my_type_as_int = my_type._get_ptr_as_int() - var dict_type = self.PyDict_Type() - var result = my_type_as_int == dict_type._get_ptr_as_int() - self.Py_DecRef(my_type) - return result - - fn PyDict_Type(inout self) -> PyObjectPtr: - if self.dict_type.is_null(): - self.dict_type = self.lib.get_function[PyObjectPtr]("PyDict_Type") - return self.dict_type - - fn PyObject_Type(inout self, obj: PyObjectPtr) -> PyObjectPtr: - var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyObject_Type" - ) - self._inc_total_rc() - return f(obj) - - fn PyObject_Str(inout self, obj: PyObjectPtr) -> PyObjectPtr: - var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyObject_Str" - ) - self._inc_total_rc() - return f(obj) - - fn PyObject_GetIter( - inout self, traversablePyObject: PyObjectPtr - ) -> PyObjectPtr: - var iter = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyObject_GetIter" - )(traversablePyObject) - - self.log( - iter._get_ptr_as_int(), - " NEWREF PyObject_GetIter, refcnt:", - self._Py_REFCNT(iter), - "referencing ", - traversablePyObject._get_ptr_as_int(), - "refcnt of traversable: ", - self._Py_REFCNT(traversablePyObject), - ) - - self._inc_total_rc() - return iter - fn PyIter_Next(inout self, iterator: PyObjectPtr) -> PyObjectPtr: var next_obj = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyIter_Next" @@ -1207,50 +1290,3 @@ struct CPython: fn (PyObjectPtr) -> Int ]("PySequence_Check")(obj) return follows_seq_protocol != 0 - - fn PyDict_Next( - inout self, dictionary: PyObjectPtr, p: Int - ) -> PyKeyValuePair: - var key = UnsafePointer[Int8]() - var value = UnsafePointer[Int8]() - var v = p - var position = UnsafePointer[Int].address_of(v) - var value_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(value) - var key_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(key) - var result = self.lib.get_function[ - fn ( - PyObjectPtr, - UnsafePointer[Int], - UnsafePointer[UnsafePointer[Int8]], - UnsafePointer[UnsafePointer[Int8]], - ) -> Int - ]("PyDict_Next")( - dictionary, - position, - key_ptr, - value_ptr, - ) - - self.log( - dictionary._get_ptr_as_int(), - " NEWREF PyDict_Next", - dictionary._get_ptr_as_int(), - "refcnt:", - self._Py_REFCNT(dictionary), - " key: ", - PyObjectPtr {value: key}._get_ptr_as_int(), - ", refcnt(key):", - self._Py_REFCNT(key), - "value:", - PyObjectPtr {value: value}._get_ptr_as_int(), - "refcnt(value)", - self._Py_REFCNT(value), - ) - - _ = v - return PyKeyValuePair { - key: key, - value: value, - position: position.take_pointee(), - success: result == 1, - } From 132ce0d29fa710ce6a88df79dbb88f7e42dc2175 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 19 Sep 2024 16:20:20 -0600 Subject: [PATCH 1574/2019] [stdlib] Remove `StringRef` from `prelude` `StringRef` is not a safe type and should not be exported from the `prelude`. Remove it and import `StringRef` explicitly where it is still used. MODULAR_ORIG_COMMIT_REV_ID: bd24716bb4e92c51d626c4b14d88644b3fc00429 --- docs/changelog.md | 5 +++++ stdlib/src/builtin/error.mojo | 2 ++ stdlib/src/builtin/file.mojo | 2 +- stdlib/src/prelude/__init__.mojo | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6309b54dd3..2de6269ba0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -62,6 +62,11 @@ what we publish. ### 🦋 Changed +- More things have been removed from the auto-exported set of entities in the `prelude` + module from the Mojo standard library. + - `StringRef` has been removed. Please explicitly import it via + `from utils import StringRef`. + - A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method specifies to the compiler that the resultant pointer is a distinct identifiable object that does not alias any other memory in the local scope. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 22353b376e..a819e94d4d 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -20,6 +20,8 @@ from sys import alignof, sizeof from memory import UnsafePointer, memcpy from memory.memory import _free +from utils import StringRef + # ===----------------------------------------------------------------------===# # Error # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 9812896cc0..ed351ddea9 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -33,7 +33,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call, sizeof -from utils import Span +from utils import Span, StringRef from memory import AddressSpace, UnsafePointer diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index e6d2fd6a39..46e24297fd 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -132,4 +132,4 @@ from collections.string import ( isprintable, ) from memory import UnsafePointer, Reference, AddressSpace -from utils import StringRef, Formattable, Formatter +from utils import Formattable, Formatter From 28ca2d6927d14da351081572e898f65c6c467999 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 19 Sep 2024 15:33:42 -0700 Subject: [PATCH 1575/2019] [External] [stdlib] Make utf8 validation ~10-13x faster on neon and sse4 (#47462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Make utf8 validation ~10-13x faster on neon and sse4 ## Description of the changes In the future `_is_valid_utf8()` will be used massively. As such, we need every performance improvement possible, as long as the complexity cost is reasonable. This PR changes the implementation fo the function `_is_valid_utf8()` without changing the signature. It's a drop-in replacement. This implementation is describled in the paper [Validating UTF-8 in less than one instruction per byte](https://arxiv.org/abs/2010.03090) by John Keiser and Daniel Lemire, which is pretty close to the state of the art on the subject. A reference C++ implementation can be found in the repository [lemire/fastvalidate-utf-8](https://github.com/lemire/fastvalidate-utf-8), precisely in [this file](https://github.com/lemire/fastvalidate-utf-8/blob/master/include/simdutf8check.h). Notice how Mojo makes this more generic and readable, as well as portable. Note that the only improvement that I'm aware of to this algorithm is the [is_utf8 library of simdutf](https://github.com/simdutf/is_utf8) which is based on the algorithm made in this PR. It is significantly harder to implement as it's a production grade library full of macros and other things that I have a harder time reading than fastvalidate-utf-8. Two good blog posts have been made one the subject: * [Validating UTF-8 strings using as little as 0.7 cycles per byte](https://lemire.me/blog/2018/05/16/validating-utf-8-strings-using-as-little-as-0-7-cycles-per-byte/) * [Validating UTF-8 bytes using only 0.45 cycles per byte, AVX edition](https://lemire.me/blog/2018/10/19/validating-utf-8-bytes-using-only-0-45-cycles-per-byte-avx-edition/) ## Types of utf-8 errors While I'm not sure that the current implementation can detect all classes of errors, this algorithms checks the following rules: a) **5+ Byte**. The leading byte must have fewer than 5 header bits. b) **Too Short**. The leading byte must be followed by N-1 continuation bytes, where N is the UTF-8 character length. c) **Too Long**. The leading byte must not be a continuation byte. d) **Overlong**. The decoded character must be above U+7F for two-byte characters, U+7FF for three-byte characters, and U+FFFF for four-byte characters. e) **Too Large**. The decoded character must be less than or equal to U+10FFFF. f) **Surrogate**. The decoded character must be not be in U+D800...DFFF. ## Why is this implementation so much faster than the current one? ### The current implementation The current implementation was using simd, but not in an optimal way: 1) It was loading N bytes in an simd vector 2) Was checking with simd instructions if it was ascii (a fast path) and skip the chunk if that's the case. If some bytes are ascii, do a plain for loop on each byte to skip them. 3) If the chunk was not full ascii, look at the first byte and get the number of bytes in the character that is at the start of the simd vector 4) Increment the counter with the number of bytes in the character (2, 3 or 4) and go back to step 1. Since the index can increment by 2,3 or 4 before loading the next chunk of bytes, the following problems were presents: 1) We do a lot of iterations, one per character if it's non-ascii and since each iteration involves a `LOAD`, that's expensive. 2) Doing a for loop on each byte to check ascii require a lot of instructions to be executed per byte and not good branching predictability (if not ascii, continue the loop, it's a hard to predict branch). 3) Since the data isn't aligned anymore (`idx` is not a multiple of the simd size), loading the chunk into an simd vector is quite slow. 4) Overall, many if-statements are present in the loop. ### The new implementation The new algorithm improves on this by reading the data chunk by chunk, keeping into simd vectors information about the previous chunk. You can look at the `_is_valid_utf8` function which has the main loop. Since it's chunk by chunk, the index jump is always the simd size. It has the following properties which usually make the hardware happy: 1) One single LOAD per iteration. 2) LOAD each byte once. 3) Once a chunk is loaded into SIMD, no branching is done. 4) Once a chunk is loaded into simd, no more load from memory is done, we only work with simd vectors in registers. 5) Many simd operations don't depend on each other, which gives flexibility to the cpu with the out-of-order execution. 6) The jump size if constant (simd size) which makes it very easy to know which data will be accessed next and the data to load in simd is always aligned correctly. Basically, you load 32 bytes of data into simd, do a bunch of computation in registers without branching (~60 assembly instructions, look at the `_check_utf8_bytes` function, those instructions don't all depend on each other), and only take the next 32 bytes when you're done with the current chunk. Keep a bunch of vectors here and there to be able to validate characters which spans across two simd vectors. Such type of computation is the one cpus are optimized for. Since we only do simd operations, the number of assembly instructions is the same if the simd vector is of size 8 or size 64, meaning that if the cpu has biggest simd sizes available (AVX512) then a considerable speedup can be achieved. ## Benchmark code We don't make use of the `benchmark` module, because it is not available when an external contributor recompiles the stdlib. ```mojo import sys from testing import assert_true, assert_false from utils.string_slice import _is_valid_utf8 import time @no_inline fn keep(x: Bool) -> Int: return not x fn get_big_string() -> String: var string = str( "안녕하세요,세상 hello mojo! 🔥🔥hopefully this string is complicated enough :p" " éç__çè" ) # The string is 100 bytes long. return string * 100_000 # 10MB def main(): print("Has neon:", sys.has_neon()) print("Has sse4:", sys.has_sse4()) print("SIMD size:", sys.simdbytewidth()) var big_string = get_big_string() @parameter fn utf8_simd_validation_benchmark() raises: # we want to validate ~1gb of data for _ in range(100): var result = _is_valid_utf8(big_string.unsafe_ptr(), len(big_string)) assert_true(result) # warmup for _ in range(3): utf8_simd_validation_benchmark() iterations = 10 t1 = time.now() for _ in range(iterations): utf8_simd_validation_benchmark() t2 = time.now() _ = big_string average_ns = (t2 - t1) / iterations average_s = average_ns / 1_000_000_000 print("Validate 1GB of UTF-8 data in", average_s, "s") print(1.0 / average_s, "GB/s") ``` Put it in a file called `bench.mojo`. Bench the nightly version with ``` MOJO_OVERRIDE_COMPILER_VERSION_CHECK=true mojo build bench.mojo && ./bench ``` Bench this branch by doing a checkout on it and then: ``` MOJO_OVERRIDE_COMPILER_VERSION_CHECK=true MODULAR_MOJO_NIGHTLY_IMPORT_PATH=./build mojo build bench.mojo && ./bench ``` ## Benchmark results: * On AMD Ryzen 9 7945HX with Radeon Graphics we get a **x10.8** speedup. * On Intel(R) Core(TM) i7-10700KF CPU @ 3.80GHz (WSL2, windows 11) I get **x7.3** speedup I don't have the numbers for less capable CPUs, notably the ones that don't have the instruction to do the `dynamic_shuffle()` in one single instruction. If you have other cpus, please run the benchmark and report the numbers here. Thanks! ## Future work in this area To further improve the algorithm, a few paths can be taken: 1) The reference implementation has a "fast path" for ASCII chunks where many instructions are skipped if the chunk is ASCII. This can improve the speed for some common cases. Such cases are when there is a lot of ascii chars in a string. This fast path has not been yet implemented. 2) The original author states in the repo, and we could see the changes made: > NOTE: The fastvalidate-utf-8 library is obsolete as of 2022: [please adopt the simdutf library] (https://github.com/simdutf/). It is much more powerful, faster and better tested. 3) This algorithm could inform the user which precise byte is causing an issue and why. This has not been implemented yet. Note that it will add branching to the loop and will require benchmarking to make sure there is no performance penalty. 4) I looked at the assembly and some instructions that were present in the original implementation were not used here. Meaning there is some room for using specialized instruction that will save a few cycles. A good example is the `_mm_alignr_epi8` function which should in theory be one single instruction. I am not currently working on those improvements. Someone else can investigate. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3401 MODULAR_ORIG_COMMIT_REV_ID: 3dabaa99b60da779630c7be36f01f8d4468eeab9 --- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/utils/_utf8_validation.mojo | 183 +++++++++++++++++++++++ stdlib/src/utils/string_slice.mojo | 146 ------------------ stdlib/test/builtin/test_simd.mojo | 24 +-- stdlib/test/utils/test_string_slice.mojo | 2 +- 5 files changed, 197 insertions(+), 160 deletions(-) create mode 100644 stdlib/src/utils/_utf8_validation.mojo diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index af3fa02f85..d484a9d99b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1956,7 +1956,7 @@ struct SIMD[type: DType, size: Int]( # Not an overload of shuffle because there is ambiguity # with fn shuffle[*mask: Int](self, other: Self) -> Self: - # TODO: move closer to UTF-8 String validation code - see https://github.com/modularml/mojo/issues/3477 + # TODO: move to the utils directory - see https://github.com/modularml/mojo/issues/3477 @always_inline fn _dynamic_shuffle[ mask_size: Int, // diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo new file mode 100644 index 0000000000..88350e70e0 --- /dev/null +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -0,0 +1,183 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +"""Implement fast utf-8 validation using SIMD instructions. + +References for this algorithm: +J. Keiser, D. Lemire, Validating UTF-8 In Less Than One Instruction Per Byte, +Software: Practice and Experience 51 (5), 2021 +https://arxiv.org/abs/2010.03090 + +Blog post: +https://lemire.me/blog/2018/10/19/validating-utf-8-bytes-using-only-0-45-cycles-per-byte-avx-edition/ + +Code adapted from: +https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs +""" + +alias TOO_SHORT: UInt8 = 1 << 0 +alias TOO_LONG: UInt8 = 1 << 1 +alias OVERLONG_3: UInt8 = 1 << 2 +alias SURROGATE: UInt8 = 1 << 4 +alias OVERLONG_2: UInt8 = 1 << 5 +alias TWO_CONTS: UInt8 = 1 << 7 +alias TOO_LARGE: UInt8 = 1 << 3 +alias TOO_LARGE_1000: UInt8 = 1 << 6 +alias OVERLONG_4: UInt8 = 1 << 6 +alias CARRY: UInt8 = TOO_SHORT | TOO_LONG | TWO_CONTS + + +# fmt: off +alias shuf1 = SIMD[DType.uint8, 16]( + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + TOO_SHORT | OVERLONG_2, + TOO_SHORT, + TOO_SHORT | OVERLONG_3 | SURROGATE, + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 +) + +alias shuf2 = SIMD[DType.uint8, 16]( + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + CARRY | OVERLONG_2, + CARRY, + CARRY, + CARRY | TOO_LARGE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 +) +alias shuf3 = SIMD[DType.uint8, 16]( + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT +) +# fmt: on + + +@always_inline +fn extract_vector[ + simd_size: Int, //, offset: Int +](a: SIMD[DType.uint8, simd_size], b: SIMD[DType.uint8, simd_size]) -> SIMD[ + DType.uint8, simd_size +]: + """This can be a single instruction on some architectures.""" + concatenated = a.join(b) + return concatenated.slice[simd_size, offset=offset]() + + +@always_inline +fn _subtract_with_saturation[ + simd_size: Int, //, b: Int +](a: SIMD[DType.uint8, simd_size]) -> SIMD[DType.uint8, simd_size]: + """The equivalent of https://doc.rust-lang.org/core/arch/x86_64/fn._mm_subs_epu8.html . + This can be a single instruction on some architectures. + """ + alias b_as_vector = SIMD[DType.uint8, simd_size](b) + return max(a, b_as_vector) - b_as_vector + + +fn validate_chunk[ + simd_size: Int +]( + current_block: SIMD[DType.uint8, simd_size], + previous_input_block: SIMD[DType.uint8, simd_size], +) -> SIMD[DType.uint8, simd_size]: + alias v0f = SIMD[DType.uint8, simd_size](0x0F) + alias v80 = SIMD[DType.uint8, simd_size](0x80) + alias third_byte = 0b11100000 - 0x80 + alias fourth_byte = 0b11110000 - 0x80 + var prev1 = extract_vector[simd_size - 1]( + previous_input_block, current_block + ) + var byte_1_high = shuf1._dynamic_shuffle(prev1 >> 4) + var byte_1_low = shuf2._dynamic_shuffle(prev1 & v0f) + var byte_2_high = shuf3._dynamic_shuffle(current_block >> 4) + var sc = byte_1_high & byte_1_low & byte_2_high + + var prev2 = extract_vector[simd_size - 2]( + previous_input_block, current_block + ) + var prev3 = extract_vector[simd_size - 3]( + previous_input_block, current_block + ) + var is_third_byte = _subtract_with_saturation[third_byte](prev2) + var is_fourth_byte = _subtract_with_saturation[fourth_byte](prev3) + var must23 = is_third_byte | is_fourth_byte + var must23_as_80 = must23 & v80 + return must23_as_80 ^ sc + + +fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: + """Verify that the bytes are valid UTF-8. + + Args: + ptr: The pointer to the data. + length: The length of the items pointed to. + + Returns: + Whether the data is valid UTF-8. + + #### UTF-8 coding format + [Table 3-7 page 94](http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf). + Well-Formed UTF-8 Byte Sequences + + Code Points | First Byte | Second Byte | Third Byte | Fourth Byte | + :---------- | :--------- | :---------- | :--------- | :---------- | + U+0000..U+007F | 00..7F | | | | + U+0080..U+07FF | C2..DF | 80..BF | | | + U+0800..U+0FFF | E0 | ***A0***..BF| 80..BF | | + U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | | + U+D000..U+D7FF | ED | 80..***9F***| 80..BF | | + U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | | + U+10000..U+3FFFF | F0 | ***90***..BF| 80..BF | 80..BF | + U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | + U+100000..U+10FFFF | F4 | 80..***8F***| 80..BF | 80..BF | + """ + alias simd_size = sys.simdbytewidth() + var i: Int = 0 + var previous = SIMD[DType.uint8, simd_size]() + + while i + simd_size <= length: + var current_bytes = (ptr + i).load[width=simd_size]() + var has_error = validate_chunk(current_bytes, previous) + previous = current_bytes + if any(has_error != 0): + return False + i += simd_size + + var has_error = SIMD[DType.uint8, simd_size]() + # last incomplete chunk + if i != length: + var buffer = SIMD[DType.uint8, simd_size](0) + for j in range(i, length): + buffer[j - i] = (ptr + j)[] + has_error = validate_chunk(buffer, previous) + else: + # Add a chunk of 0s to the end to validate continuations bytes + has_error = validate_chunk(SIMD[DType.uint8, simd_size](), previous) + + return all(has_error == 0) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 031e9b7d75..b271cd121a 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -48,152 +48,6 @@ fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): return count_leading_zeros(~(b & UInt8(0b1111_0000))) -fn _validate_utf8_simd_slice[ - width: Int, remainder: Bool = False -](ptr: UnsafePointer[UInt8], length: Int, owned iter_len: Int) -> Int: - """Internal method to validate utf8, use _is_valid_utf8. - - Parameters: - width: The width of the SIMD vector to build for validation. - remainder: Whether it is computing the remainder that doesn't fit in the - SIMD vector. - - Args: - ptr: Pointer to the data. - length: The length of the items in the pointer. - iter_len: The amount of items to still iterate through. - - Returns: - The new amount of items to iterate through that don't fit in the - specified width of SIMD vector. If -1 then it is invalid. - """ - # TODO: implement a faster algorithm like https://github.com/cyb70289/utf8 - # and benchmark the difference. - var idx = length - iter_len - while iter_len >= width or remainder: - var d: SIMD[DType.uint8, width] # use a vector of the specified width - - @parameter - if not remainder: - d = ptr.load[width=width](idx) - else: - debug_assert(iter_len > -1, "iter_len must be > -1") - d = SIMD[DType.uint8, width](0) - for i in range(iter_len): - d[i] = ptr[idx + i] - - var is_ascii = d < 0b1000_0000 - if is_ascii.reduce_and(): # skip all ASCII bytes - - @parameter - if not remainder: - idx += width - iter_len -= width - continue - else: - return 0 - elif is_ascii[0]: - for i in range(1, width): - if is_ascii[i]: - continue - idx += i - iter_len -= i - break - continue - - var byte_types = _utf8_byte_type(d) - var first_byte_type = byte_types[0] - - # byte_type has to match against the amount of continuation bytes - alias Vec = SIMD[DType.uint8, 4] - alias n4_byte_types = Vec(4, 1, 1, 1) - alias n3_byte_types = Vec(3, 1, 1, 0) - alias n3_mask = Vec(0b111, 0b111, 0b111, 0) - alias n2_byte_types = Vec(2, 1, 0, 0) - alias n2_mask = Vec(0b111, 0b111, 0, 0) - var byte_types_4 = byte_types.slice[4]() - var valid_n4 = (byte_types_4 == n4_byte_types).reduce_and() - var valid_n3 = ((byte_types_4 & n3_mask) == n3_byte_types).reduce_and() - var valid_n2 = ((byte_types_4 & n2_mask) == n2_byte_types).reduce_and() - if not (valid_n4 or valid_n3 or valid_n2): - return -1 - - # special unicode ranges - var b0 = d[0] - var b1 = d[1] - if first_byte_type == 2 and b0 < UInt8(0b1100_0010): - return -1 - elif b0 == 0xE0 and not (UInt8(0xA0) <= b1 <= UInt8(0xBF)): - return -1 - elif b0 == 0xED and not (UInt8(0x80) <= b1 <= UInt8(0x9F)): - return -1 - elif b0 == 0xF0 and not (UInt8(0x90) <= b1 <= UInt8(0xBF)): - return -1 - elif b0 == 0xF4 and not (UInt8(0x80) <= b1 <= UInt8(0x8F)): - return -1 - - # amount of bytes evaluated - idx += int(first_byte_type) - iter_len -= int(first_byte_type) - - @parameter - if remainder: - break - return iter_len - - -fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: - """Verify that the bytes are valid UTF-8. - - Args: - ptr: The pointer to the data. - length: The length of the items pointed to. - - Returns: - Whether the data is valid UTF-8. - - #### UTF-8 coding format - [Table 3-7 page 94](http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf). - Well-Formed UTF-8 Byte Sequences - - Code Points | First Byte | Second Byte | Third Byte | Fourth Byte | - :---------- | :--------- | :---------- | :--------- | :---------- | - U+0000..U+007F | 00..7F | | | | - U+0080..U+07FF | C2..DF | 80..BF | | | - U+0800..U+0FFF | E0 | ***A0***..BF| 80..BF | | - U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | | - U+D000..U+D7FF | ED | 80..***9F***| 80..BF | | - U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | | - U+10000..U+3FFFF | F0 | ***90***..BF| 80..BF | 80..BF | - U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | - U+100000..U+10FFFF | F4 | 80..***8F***| 80..BF | 80..BF | - . - """ - - var iter_len = length - if iter_len >= 64 and simdwidthof[DType.uint8]() >= 64: - iter_len = _validate_utf8_simd_slice[64](ptr, length, iter_len) - if iter_len < 0: - return False - if iter_len >= 32 and simdwidthof[DType.uint8]() >= 32: - iter_len = _validate_utf8_simd_slice[32](ptr, length, iter_len) - if iter_len < 0: - return False - if iter_len >= 16 and simdwidthof[DType.uint8]() >= 16: - iter_len = _validate_utf8_simd_slice[16](ptr, length, iter_len) - if iter_len < 0: - return False - if iter_len >= 8: - iter_len = _validate_utf8_simd_slice[8](ptr, length, iter_len) - if iter_len < 0: - return False - if iter_len >= 4: - iter_len = _validate_utf8_simd_slice[4](ptr, length, iter_len) - if iter_len < 0: - return False - return _validate_utf8_simd_slice[4, True](ptr, length, iter_len) == 0 - - fn _is_newline_start( ptr: UnsafePointer[UInt8], read_ahead: Int = 1 ) -> (Bool, Int): diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 71fbc906df..d1a4e5c03c 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -888,13 +888,13 @@ def test_shuffle_dynamic_size_32_uint8(): ) # fmt: off var indices = SIMD[DType.uint8, 32]( - 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , - 11, 11, 13, 13, 15, 15, 0 , 1 , - 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , + 11, 11, 13, 13, 15, 15, 0 , 1 , + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10, 11, 12, 13, 14, 15, ) result = table_lookup._dynamic_shuffle(indices) - + expected_result = SIMD[DType.uint8, 32]( 30 , 30 , 50 , 50 , 70 , 70 , 90 , 90 , 110, 110, 130, 130, 150, 150, 0 , 10 , @@ -911,13 +911,13 @@ def test_shuffle_dynamic_size_64_uint8(): ) # fmt: off var indices = SIMD[DType.uint8, 32]( - 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , - 11, 11, 13, 13, 15, 15, 0 , 1 , - 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , + 11, 11, 13, 13, 15, 15, 0 , 1 , + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10, 11, 12, 13, 14, 15, ) result = table_lookup._dynamic_shuffle(indices.join(indices)) - + expected_result = SIMD[DType.uint8, 32]( 30 , 30 , 50 , 50 , 70 , 70 , 90 , 90 , 110, 110, 130, 130, 150, 150, 0 , 10 , @@ -935,13 +935,13 @@ def test_shuffle_dynamic_size_32_float(): 80.0, 90.0, 100.0, 110.0, 120.0, 130.0, 140.0, 150.0, ) var indices = SIMD[DType.uint8, 32]( - 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , - 11, 11, 13, 13, 15, 15, 0 , 1 , - 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 3 , 3 , 5 , 5 , 7 , 7 , 9 , 9 , + 11, 11, 13, 13, 15, 15, 0 , 1 , + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10, 11, 12, 13, 14, 15, ) result = table_lookup._dynamic_shuffle(indices) - + expected_result = SIMD[DType.float64, 32]( 30. , 30. , 50. , 50. , 70. , 70. , 90. , 90. , 110., 110., 130., 130., 150., 150., 0. , 10. , diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 1372ded023..6f94838f1e 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -15,7 +15,7 @@ from testing import assert_equal, assert_true, assert_false from utils import Span, StringSlice -from utils.string_slice import _is_valid_utf8 +from utils._utf8_validation import _is_valid_utf8 fn test_string_literal_byte_slice() raises: From 68333f7fc533f3b394e7e58a2c73545a31c8f0d8 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Thu, 19 Sep 2024 15:58:03 -0700 Subject: [PATCH 1576/2019] [External] [stdlib] assertion overloads for `List` (#47210) [External] [stdlib] assertion overloads for `List` Add `assert_equal` and `assert_not_equal` overload for `List` comparisons, a nice-to-have in a few places. Most probably a temporary solution under the current trait system. Co-authored-by: Joshua James Venter Closes modularml/mojo#3372 MODULAR_ORIG_COMMIT_REV_ID: 73743fb7a84f578717998c6359ba6c07cd6709ab --- stdlib/src/testing/testing.mojo | 77 ++++++++++++++++++++++++- stdlib/test/testing/test_assertion.mojo | 62 ++++++++++++++------ 2 files changed, 121 insertions(+), 18 deletions(-) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 4d417a18cb..fa7646fc4a 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -93,6 +93,15 @@ trait Testable(EqualityComparable, Stringable): pass +trait TestableCollectionElement( + EqualityComparableCollectionElement, + RepresentableCollectionElement, +): + """A trait for elements that can be tested in a collection.""" + + pass + + @always_inline fn assert_equal[ T: Testable @@ -124,7 +133,7 @@ fn assert_equal[ ) -# TODO: Remove the String and SIMD overloads once we have more powerful traits. +# TODO: Remove the String, SIMD and List overloads once we have more powerful traits. @always_inline fn assert_equal( lhs: String, @@ -183,6 +192,39 @@ fn assert_equal[ ) +@always_inline +fn assert_equal[ + T: TestableCollectionElement +]( + lhs: List[T], + rhs: List[T], + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: + """Asserts that two lists are equal. + + Parameters: + T: A TestableCollectionElement type. + + Args: + lhs: The left-hand side list. + rhs: The right-hand side list. + msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). + + Raises: + An Error with the provided message if assert fails and `None` otherwise. + """ + if lhs != rhs: + raise _assert_cmp_error["`left == right` comparison"]( + lhs.__str__(), + rhs.__str__(), + msg=msg, + loc=location.or_else(__call_location()), + ) + + @always_inline fn assert_not_equal[ T: Testable @@ -272,6 +314,39 @@ fn assert_not_equal[ ) +@always_inline +fn assert_not_equal[ + T: TestableCollectionElement +]( + lhs: List[T], + rhs: List[T], + msg: String = "", + *, + location: Optional[_SourceLocation] = None, +) raises: + """Asserts that two lists are not equal. + + Parameters: + T: A TestableCollectionElement type. + + Args: + lhs: The left-hand side list. + rhs: The right-hand side list. + msg: The message to be printed if the assertion fails. + location: The location of the error (default to the `__call_location`). + + Raises: + An Error with the provided message if assert fails and `None` otherwise. + """ + if lhs == rhs: + raise _assert_cmp_error["`left != right` comparison"]( + lhs.__str__(), + rhs.__str__(), + msg=msg, + loc=location.or_else(__call_location()), + ) + + @always_inline fn assert_almost_equal[ type: DType, size: Int diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index a19360cc63..6f07f4d3b9 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -28,6 +28,28 @@ from builtin._location import _SourceLocation from python import PythonObject +def test_assert_messages(): + try: + assert_true(False) + except e: + assert_true("test_assertion.mojo:33:20: AssertionError:" in str(e)) + + try: + assert_false(True) + except e: + assert_true("test_assertion.mojo:38:21: AssertionError:" in str(e)) + + try: + assert_equal(1, 0) + except e: + assert_true("test_assertion.mojo:43:21: AssertionError:" in str(e)) + + try: + assert_not_equal(0, 0) + except e: + assert_true("test_assertion.mojo:48:25: AssertionError:" in str(e)) + + @value struct DummyStruct: var value: Int @@ -64,26 +86,30 @@ def test_assert_equal_with_simd(): assert_equal(SIMD[DType.uint8, 2](1, 1), SIMD[DType.uint8, 2](1, 2)) -def test_assert_messages(): - try: - assert_true(False) - except e: - assert_true("test_assertion.mojo:69:20: AssertionError:" in str(e)) +def test_assert_equal_with_list(): + assert_equal( + List(String("This"), String("is"), String("Mojo")), + List(String("This"), String("is"), String("Mojo")), + ) - try: - assert_false(True) - except e: - assert_true("test_assertion.mojo:74:21: AssertionError:" in str(e)) + with assert_raises(): + assert_equal( + List(String("This"), String("is"), String("Mojo")), + List(String("This"), String("is"), String("mojo")), + ) - try: - assert_equal(1, 0) - except e: - assert_true("test_assertion.mojo:79:21: AssertionError:" in str(e)) - try: - assert_not_equal(0, 0) - except e: - assert_true("test_assertion.mojo:84:25: AssertionError:" in str(e)) +def test_assert_not_equal_with_list(): + assert_not_equal( + List(3, 2, 1), + List(3, 1, 0), + ) + + with assert_raises(): + assert_not_equal( + List(3, 2, 1), + List(3, 2, 1), + ) def test_assert_almost_equal(): @@ -215,6 +241,8 @@ def main(): test_assert_equal_is_generic() test_assert_not_equal_is_generic() test_assert_equal_with_simd() + test_assert_equal_with_list() + test_assert_not_equal_with_list() test_assert_messages() test_assert_almost_equal() test_assert_is() From 35ad7ee0d7af2f40f47cca6b3f3beb1947d82695 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 19 Sep 2024 16:00:58 -0700 Subject: [PATCH 1577/2019] [External] [stdlib] Complete the string literals signature to match the `String` one (#47461) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Complete the string literals signature to match the `String` one To match the existing methods in both Mojo and Python strings. This could help bit a little with the transition of Python programmers playing with the REPL and small mojo examples that handle strings, finding it more familiar with Python E.g. it's annoying for them to start playing with Mojo with a file like this one: ```mojo fn main(): s = "Hello" for c in s: print(c) ``` And encounter an error like this one: ``` ❯ mojo t.mojo /tmp/t.mojo:3:12: error: 'StringLiteral' does not implement the '__iter__' method for c in s: ^ mojo: error: failed to parse the provided Mojo source module ``` ### New methods and features implemented in String literals - Implemented `.strip()`, `.lstrip()`, and `.rstrip()` - Implement `.isdigit()` - Implement `.islower()` and `.isupper()` - Implement `.__iter__()` - Implement indexing - Implement `.startswith()` and `.endswith()` - Implement `.rjust()`, `.ljust()` and `.center()` - Implement `.count()` - Implement `.split()` and `.splitlines()` ### Example ```mojo > mojo 1> x = "123".isdigit() (Bool) x = True 2> y = " hello ".strip() (String) y = "hello" 2> z = " hello ".lstrip() (String) z = "hello " 3> t = " hello ".rstrip() (String) t = " hello" 4> b1 = "HELLO".isupper() (Bool) b1 = True 5> b2 = "HELLO".islower() (Bool) b2 = False 6> s = "Hello" (StringLiteral) s = "Hello" │ 7> h = s[0] (String) x = "H" 8> b3 = s.startswith("He") (Bool) b3 = True 9> sl = s.ljust(10) 10> c = s.count("l") (Int) c = 2 11> first = "Hello world".split()[0] (String) first = "Hello" 12> for c in "HELLO": 12. print(c) 13. H E L L O ``` Co-authored-by: Manuel Saelices Closes modularml/mojo#3438 MODULAR_ORIG_COMMIT_REV_ID: 0b1d12be1b0d7ea489c22343055f3b586ad6e0f4 --- stdlib/src/builtin/string_literal.mojo | 266 ++++++++++++++++++- stdlib/test/builtin/test_string_literal.mojo | 253 ++++++++++++++++++ 2 files changed, 518 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index fa698860e0..eeb3c87672 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -23,7 +23,7 @@ from utils import StringRef, Span, StringSlice from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type -from collections.string import _atol +from collections.string import _atol, _StringSliceIter # ===----------------------------------------------------------------------===# # StringLiteral @@ -273,6 +273,30 @@ struct StringLiteral( """ return self.__str__() + fn __iter__(ref [_]self) -> _StringSliceIter[__lifetime_of(self)]: + """Return an iterator over the string literal. + + Returns: + An iterator over the string. + """ + return _StringSliceIter[__lifetime_of(self)]( + unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() + ) + + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> String: + """Gets the character at the specified position. + + Parameters: + IndexerType: The inferred type of an indexer argument. + + Args: + idx: The index value. + + Returns: + A new string containing the character at the specified position. + """ + return str(self)[idx] + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# @@ -417,6 +441,88 @@ struct StringLiteral( return result + fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]: + """Split the string literal by a separator. + + Args: + sep: The string to split on. + maxsplit: The maximum amount of items to split from String. + Defaults to unlimited. + + Returns: + A List of Strings containing the input split by the separator. + + Examples: + + ```mojo + # Splitting a space + _ = "hello world".split(" ") # ["hello", "world"] + # Splitting adjacent separators + _ = "hello,,world".split(",") # ["hello", "", "world"] + # Splitting with maxsplit + _ = "1,2,3".split(",", 1) # ['1', '2,3'] + ``` + . + """ + return str(self).split(sep, maxsplit) + + fn split(self, sep: NoneType = None, maxsplit: Int = -1) -> List[String]: + """Split the string literal by every whitespace separator. + + Args: + sep: None. + maxsplit: The maximum amount of items to split from string. Defaults + to unlimited. + + Returns: + A List of Strings containing the input split by the separator. + + Examples: + + ```mojo + # Splitting an empty string or filled with whitespaces + _ = " ".split() # [] + _ = "".split() # [] + + # Splitting a string with leading, trailing, and middle whitespaces + _ = " hello world ".split() # ["hello", "world"] + # Splitting adjacent universal newlines: + _ = "hello \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029world".split() + # ["hello", "world"] + ``` + . + """ + return str(self).split(sep, maxsplit) + + fn splitlines(self, keepends: Bool = False) -> List[String]: + """Split the string literal at line boundaries. This corresponds to Python's + [universal newlines]( + https://docs.python.org/3/library/stdtypes.html#str.splitlines) + `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + + Args: + keepends: If True, line breaks are kept in the resulting strings. + + Returns: + A List of Strings containing the input split by line boundaries. + """ + return self.as_string_slice().splitlines(keepends) + + fn count(self, substr: String) -> Int: + """Return the number of non-overlapping occurrences of substring + `substr` in the string literal. + + If sub is empty, returns the number of empty strings between characters + which is the length of the string plus one. + + Args: + substr: The substring to count. + + Returns: + The number of occurrences of `substr`. + """ + return str(self).count(substr) + fn lower(self) -> String: """Returns a copy of the string literal with all cased characters converted to lowercase. @@ -436,3 +542,161 @@ struct StringLiteral( """ return str(self).upper() + + fn rjust(self, width: Int, fillchar: StringLiteral = " ") -> String: + """Returns the string right justified in a string literal of specified width. + + Args: + width: The width of the field containing the string. + fillchar: Specifies the padding character. + + Returns: + Returns right justified string, or self if width is not bigger than self length. + """ + return str(self).rjust(width, fillchar) + + fn ljust(self, width: Int, fillchar: StringLiteral = " ") -> String: + """Returns the string left justified in a string literal of specified width. + + Args: + width: The width of the field containing the string. + fillchar: Specifies the padding character. + + Returns: + Returns left justified string, or self if width is not bigger than self length. + """ + return str(self).ljust(width, fillchar) + + fn center(self, width: Int, fillchar: StringLiteral = " ") -> String: + """Returns the string center justified in a string literal of specified width. + + Args: + width: The width of the field containing the string. + fillchar: Specifies the padding character. + + Returns: + Returns center justified string, or self if width is not bigger than self length. + """ + return str(self).center(width, fillchar) + + fn startswith(self, prefix: String, start: Int = 0, end: Int = -1) -> Bool: + """Checks if the string literal starts with the specified prefix between start + and end positions. Returns True if found and False otherwise. + + Args: + prefix: The prefix to check. + start: The start offset from which to check. + end: The end offset from which to check. + + Returns: + True if the self[start:end] is prefixed by the input prefix. + """ + return str(self).startswith(prefix, start, end) + + fn endswith(self, suffix: String, start: Int = 0, end: Int = -1) -> Bool: + """Checks if the string literal end with the specified suffix between start + and end positions. Returns True if found and False otherwise. + + Args: + suffix: The suffix to check. + start: The start offset from which to check. + end: The end offset from which to check. + + Returns: + True if the self[start:end] is suffixed by the input suffix. + """ + return str(self).endswith(suffix, start, end) + + fn isdigit(self) -> Bool: + """Returns True if all characters in the string literal are digits. + + Note that this currently only works with ASCII strings. + + Returns: + True if all characters are digits else False. + """ + return str(self).isdigit() + + fn isupper(self) -> Bool: + """Returns True if all cased characters in the string literal are + uppercase and there is at least one cased character. + + Note that this currently only works with ASCII strings. + + Returns: + True if all cased characters in the string literal are uppercase + and there is at least one cased character, False otherwise. + """ + return str(self).isupper() + + fn islower(self) -> Bool: + """Returns True if all cased characters in the string literal + are lowercase and there is at least one cased character. + + Note that this currently only works with ASCII strings. + + Returns: + True if all cased characters in the string literal are lowercase + and there is at least one cased character, False otherwise. + """ + return str(self).islower() + + fn strip(self) -> String: + """Return a copy of the string literal with leading and trailing whitespaces + removed. + + Returns: + A string with no leading or trailing whitespaces. + """ + return self.lstrip().rstrip() + + fn strip(self, chars: String) -> String: + """Return a copy of the string literal with leading and trailing characters + removed. + + Args: + chars: A set of characters to be removed. Defaults to whitespace. + + Returns: + A string with no leading or trailing characters. + """ + + return self.lstrip(chars).rstrip(chars) + + fn rstrip(self, chars: String) -> String: + """Return a copy of the string literal with trailing characters removed. + + Args: + chars: A set of characters to be removed. Defaults to whitespace. + + Returns: + A string with no trailing characters. + """ + return str(self).rstrip(chars) + + fn rstrip(self) -> String: + """Return a copy of the string with trailing whitespaces removed. + + Returns: + A copy of the string with no trailing whitespaces. + """ + return str(self).rstrip() + + fn lstrip(self, chars: String) -> String: + """Return a copy of the string with leading characters removed. + + Args: + chars: A set of characters to be removed. Defaults to whitespace. + + Returns: + A copy of the string with no leading characters. + """ + return str(self).lstrip(chars) + + fn lstrip(self) -> String: + """Return a copy of the string with leading whitespaces removed. + + Returns: + A copy of the string with no leading whitespaces. + """ + return str(self).lstrip() diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index c4960d31f8..3199aa875d 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -105,6 +105,30 @@ def test_replace(): ) +def test_startswith(): + var str = "Hello world" + + assert_true(str.startswith("Hello")) + assert_false(str.startswith("Bye")) + + assert_true(str.startswith("llo", 2)) + assert_true(str.startswith("llo", 2, -1)) + assert_false(str.startswith("llo", 2, 3)) + + +def test_endswith(): + var str = "Hello world" + + assert_true(str.endswith("")) + assert_true(str.endswith("world")) + assert_true(str.endswith("ld")) + assert_false(str.endswith("universe")) + + assert_true(str.endswith("ld", 2)) + assert_true(str.endswith("llo", 2, 5)) + assert_false(str.endswith("llo", 2, 3)) + + def test_comparison_operators(): # Test less than and greater than assert_true(StringLiteral.__lt__("abc", "def")) @@ -142,6 +166,13 @@ def test_hash(): assert_equal(StringLiteral.__hash__("b"), StringLiteral.__hash__("b")) +def test_indexing(): + var s = "hello" + assert_equal(s[False], "h") + assert_equal(s[int(1)], "e") + assert_equal(s[2], "l") + + def test_intable(): assert_equal(StringLiteral.__int__("123"), 123) @@ -149,6 +180,43 @@ def test_intable(): _ = StringLiteral.__int__("hi") +def test_isdigit(): + assert_true("123".isdigit()) + assert_false("abc".isdigit()) + assert_false("123abc".isdigit()) + # TODO: Uncomment this when PR3439 is merged + # assert_false("".isdigit()) + + +def test_islower(): + assert_true("hello".islower()) + assert_false("Hello".islower()) + assert_false("HELLO".islower()) + assert_false("123".islower()) + assert_false("".islower()) + + +def test_isupper(): + assert_true("HELLO".isupper()) + assert_false("Hello".isupper()) + assert_false("hello".isupper()) + assert_false("123".isupper()) + assert_false("".isupper()) + + +def test_iter(): + # Test iterating over a string + var s = "one" + var i = 0 + for c in s: + if i == 0: + assert_equal(c, "o") + elif i == 1: + assert_equal(c, "n") + elif i == 2: + assert_equal(c, "e") + + def test_layout(): # Test empty StringLiteral contents var empty = "".unsafe_ptr() @@ -196,6 +264,178 @@ def test_repr(): assert_equal(StringLiteral.__repr__("\x7f"), r"'\x7f'") +def test_strip(): + assert_equal("".strip(), "") + assert_equal(" ".strip(), "") + assert_equal(" hello".strip(), "hello") + assert_equal("hello ".strip(), "hello") + assert_equal(" hello ".strip(), "hello") + assert_equal(" hello world ".strip(" "), "hello world") + assert_equal("_wrap_hello world_wrap_".strip("_wrap_"), "hello world") + assert_equal(" hello world ".strip(" "), "hello world") + assert_equal(" hello world ".lstrip(), "hello world ") + assert_equal(" hello world ".rstrip(), " hello world") + assert_equal( + "_wrap_hello world_wrap_".lstrip("_wrap_"), "hello world_wrap_" + ) + assert_equal( + "_wrap_hello world_wrap_".rstrip("_wrap_"), "_wrap_hello world" + ) + + +def test_count(): + var str = "Hello world" + + assert_equal(12, str.count("")) + assert_equal(1, str.count("Hell")) + assert_equal(3, str.count("l")) + assert_equal(1, str.count("ll")) + assert_equal(1, str.count("ld")) + assert_equal(0, str.count("universe")) + + assert_equal(String("aaaaa").count("a"), 5) + assert_equal(String("aaaaaa").count("aa"), 3) + + +def test_rjust(): + assert_equal("hello".rjust(4), "hello") + assert_equal("hello".rjust(8), " hello") + assert_equal("hello".rjust(8, "*"), "***hello") + + +def test_ljust(): + assert_equal("hello".ljust(4), "hello") + assert_equal("hello".ljust(8), "hello ") + assert_equal("hello".ljust(8, "*"), "hello***") + + +def test_center(): + assert_equal("hello".center(4), "hello") + assert_equal("hello".center(8), " hello ") + assert_equal("hello".center(8, "*"), "*hello**") + + +def test_split(): + var d = "hello world".split() + assert_true(len(d) == 2) + assert_true(d[0] == "hello") + assert_true(d[1] == "world") + d = "hello \t\n\n\v\fworld".split("\n") + assert_true(len(d) == 3) + assert_true(d[0] == "hello \t" and d[1] == "" and d[2] == "\v\fworld") + + # should split into empty strings between separators + d = "1,,,3".split(",") + assert_true(len(d) == 4) + assert_true(d[0] == "1" and d[1] == "" and d[2] == "" and d[3] == "3") + d = "abababaaba".split("aba") + assert_true(len(d) == 4) + assert_true(d[0] == "" and d[1] == "b" and d[2] == "" and d[3] == "") + + # should split into maxsplit + 1 items + d = "1,2,3".split(",", 0) + assert_true(len(d) == 1) + assert_true(d[0] == "1,2,3") + d = "1,2,3".split(",", 1) + assert_true(len(d) == 2) + assert_true(d[0] == "1" and d[1] == "2,3") + + assert_true(len("".split()) == 0) + assert_true(len(" ".split()) == 0) + assert_true(len("".split(" ")) == 1) + assert_true(len(" ".split(" ")) == 2) + assert_true(len(" ".split(" ")) == 3) + assert_true(len(" ".split(" ")) == 4) + + with assert_raises(): + _ = "".split("") + + # Matches should be properly split in multiple case + var d2 = " " + var in2 = "modcon is coming soon" + var res2 = in2.split(d2) + assert_equal(len(res2), 4) + assert_equal(res2[0], "modcon") + assert_equal(res2[1], "is") + assert_equal(res2[2], "coming") + assert_equal(res2[3], "soon") + + # No match from the delimiter + var d3 = "x" + var in3 = "hello world" + var res3 = in3.split(d3) + assert_equal(len(res3), 1) + assert_equal(res3[0], "hello world") + + # Multiple character delimiter + var d4 = "ll" + var in4 = "hello" + var res4 = in4.split(d4) + assert_equal(len(res4), 2) + assert_equal(res4[0], "he") + assert_equal(res4[1], "o") + + +def test_splitlines(): + # Test with no line breaks + var in1 = "hello world" + var res1 = in1.splitlines() + assert_equal(len(res1), 1) + assert_equal(res1[0], "hello world") + + # Test with \n line break + var in2 = "hello\nworld" + var res2 = in2.splitlines() + assert_equal(len(res2), 2) + assert_equal(res2[0], "hello") + assert_equal(res2[1], "world") + + # Test with \r\n line break + var in3 = "hello\r\nworld" + var res3 = in3.splitlines() + assert_equal(len(res3), 2) + assert_equal(res3[0], "hello") + assert_equal(res3[1], "world") + + # Test with \r line break + var in4 = "hello\rworld" + var res4 = in4.splitlines() + assert_equal(len(res4), 2) + assert_equal(res4[0], "hello") + assert_equal(res4[1], "world") + + # Test with multiple different line breaks + var in5 = "hello\nworld\r\nmojo\rlanguage" + var res5 = in5.splitlines() + assert_equal(len(res5), 4) + assert_equal(res5[0], "hello") + assert_equal(res5[1], "world") + assert_equal(res5[2], "mojo") + assert_equal(res5[3], "language") + + # Test with keepends=True + var res6 = in5.splitlines(keepends=True) + assert_equal(len(res6), 4) + assert_equal(res6[0], "hello\n") + assert_equal(res6[1], "world\r\n") + assert_equal(res6[2], "mojo\r") + assert_equal(res6[3], "language") + + # Test with an empty string + var in7 = "" + var res7 = in7.splitlines() + assert_equal(len(res7), 0) + + # test with keepends=True + var in8 = String("hello\vworld\fmojo\x1clanguage\x1d") + var res10 = in8.splitlines(keepends=True) + assert_equal(len(res10), 4) + assert_equal(res10[0], "hello\v") + assert_equal(res10[1], "world\f") + assert_equal(res10[2], "mojo\x1c") + assert_equal(res10[3], "language\x1d") + + def test_float_conversion(): assert_equal(("4.5").__float__(), 4.5) assert_equal(float("4.5"), 4.5) @@ -213,9 +453,22 @@ def main(): test_rfind() test_replace() test_comparison_operators() + test_count() test_hash() + test_indexing() test_intable() + test_isdigit() + test_islower() + test_isupper() test_layout() test_lower_upper() test_repr() + test_rjust() + test_ljust() + test_center() + test_startswith() + test_endswith() + test_strip() + test_split() + test_splitlines() test_float_conversion() From d3ecbd4f9cd2ba5d0923fdca6d84c13183c285e9 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:57:20 -0700 Subject: [PATCH 1578/2019] [External] [stdlib] Add `Arc.count()` (#47531) [External] [stdlib] Add `Arc.count()` Add `Arc.count()` which returns the current number of references to the pointee. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3509 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3509 MODULAR_ORIG_COMMIT_REV_ID: e869e67615e2a051e0019b1aa70242926a19629e --- stdlib/src/memory/arc.mojo | 10 +++++++++- stdlib/test/memory/test_arc.mojo | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 6e2c2a64a3..8198237e6e 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -51,7 +51,7 @@ from memory import UnsafePointer, stack_allocation struct _ArcInner[T: Movable]: - var refcount: Atomic[DType.int64] + var refcount: Atomic[DType.uint64] var payload: T fn __init__(inout self, owned value: T): @@ -162,3 +162,11 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew): """ # TODO: consider removing this method. return UnsafePointer.address_of(self._inner[].payload) + + fn count(self) -> UInt64: + """Count the amount of current references. + + Returns: + The current amount of references to the pointee. + """ + return self._inner[].refcount.load() diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index fff670a0ca..24681cdb8c 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -66,7 +66,19 @@ def test_deleter_not_called_until_no_references_explicit_copy(): assert_true(deleted) +def test_count(): + var a = Arc(10) + var b = Arc(other=a) + var c = a + assert_equal(3, a.count()) + _ = b^ + assert_equal(2, a.count()) + _ = c + assert_equal(1, a.count()) + + def main(): test_basic() test_deleter_not_called_until_no_references() test_deleter_not_called_until_no_references_explicit_copy() + test_count() From c57a9e33301f2e68ca36c8ca203bf33cd97942bb Mon Sep 17 00:00:00 2001 From: Fabian Tschopp Date: Fri, 20 Sep 2024 02:41:56 +0200 Subject: [PATCH 1579/2019] [stdlib] Acquire GIL in PyObject Acquire GIL such that __del__ can be called safely for cases where the PyObject is handled in non-python contexts. MODULAR_ORIG_COMMIT_REV_ID: 24e2b2c114c5192782d42b89a0a6690ad1022b1c --- stdlib/src/python/python_object.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index f50f63f9e9..c69d702dde 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -485,7 +485,6 @@ struct PythonObject( The underlying data. """ var ptr = self.py_object - self.py_object = PyObjectPtr() return ptr @@ -496,9 +495,13 @@ struct PythonObject( This decrements the underlying refcount of the pointed-to object. """ var cpython = _get_global_python_itf().cpython() + # Acquire GIL such that __del__ can be called safely for cases where the + # PyObject is handled in non-python contexts. + var state = cpython.PyGILState_Ensure() if not self.py_object.is_null(): cpython.Py_DecRef(self.py_object) self.py_object = PyObjectPtr() + cpython.PyGILState_Release(state) fn __getattr__(self, name: StringLiteral) raises -> PythonObject: """Return the value of the object attribute with the given name. From 82b4ead1cb80e216884884a9593c29d6a04578ea Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 20 Sep 2024 08:51:25 -0600 Subject: [PATCH 1580/2019] [stdlib] Remove `UnsafePointer` from `prelude` Since `UnsafePointer` should be used more sparingly over time and is one of the "unsafe" parts of the language/library, require users to explicitly import the `UnsafePointer` type. Do this by removing it from the exported set of entities in the `prelude` module and update all the users of it to explicitly import `UnsafePointer`. MODULAR_ORIG_COMMIT_REV_ID: 53629156a821cf33d4c16f572e288a5b14f7836f --- docs/changelog.md | 2 ++ examples/mandelbrot.mojo | 1 + examples/matmul.mojo | 2 +- examples/reduce.mojo | 1 + stdlib/benchmarks/builtin/bench_sort.mojo | 1 + stdlib/benchmarks/utils/bench_memmem.mojo | 2 +- stdlib/src/builtin/_hasher.mojo | 2 ++ stdlib/src/builtin/_startup.mojo | 1 + stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 1 + stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/inline_array.mojo | 1 + stdlib/src/os/env.mojo | 1 + stdlib/src/os/os.mojo | 1 + stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/prelude/__init__.mojo | 2 +- stdlib/src/python/python_object.mojo | 1 + stdlib/src/sys/info.mojo | 1 + stdlib/src/utils/_serialize.mojo | 2 +- stdlib/src/utils/_utf8_validation.mojo | 2 ++ stdlib/src/utils/span.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 2 +- stdlib/test/builtin/test_hasher.mojo | 1 + stdlib/test/builtin/test_simd.mojo | 1 + stdlib/test/builtin/test_sort_issue_1018.mojo | 1 + stdlib/test/builtin/test_string_literal.mojo | 1 + stdlib/test/collections/test_inline_array.mojo | 1 + stdlib/test/collections/test_inline_list.mojo | 1 + stdlib/test/collections/test_list.mojo | 1 + stdlib/test/collections/test_string.mojo | 1 + stdlib/test/memory/test_arc.mojo | 2 +- stdlib/test/memory/test_maybe_uninitialized.mojo | 1 + stdlib/test/test_utils/types.mojo | 1 + stdlib/test/utils/test_span.mojo | 1 + stdlib/test/utils/test_tuple.mojo | 1 + stdlib/test/utils/test_variant.mojo | 1 + 38 files changed, 41 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2de6269ba0..d6df81e58c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -64,6 +64,8 @@ what we publish. - More things have been removed from the auto-exported set of entities in the `prelude` module from the Mojo standard library. + - `UnsafePointer` has been removed. Please explicitly import it via + `from memory import UnsafePointer`. - `StringRef` has been removed. Please explicitly import it via `from utils import StringRef`. diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index f14c45a620..c5d8bd18b2 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -14,6 +14,7 @@ # RUN: %mojo %s | FileCheck %s from math import iota +from memory import UnsafePointer from sys import num_physical_cores, simdwidthof import benchmark diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 8eb6dc0944..36eb2a655c 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -23,7 +23,7 @@ from sys import simdwidthof import benchmark from algorithm import Static2DTileUnitFunc as Tile2DFunc from algorithm import parallelize, vectorize -from memory import memset_zero, stack_allocation +from memory import memset_zero, stack_allocation, UnsafePointer from python import Python, PythonObject alias M = 512 # rows of A and C diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 6b39fad01c..69506662cb 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -22,6 +22,7 @@ from time import now from algorithm import sum from benchmark import Unit, benchmark, keep from buffer import Buffer +from memory import UnsafePointer from python import Python # Change these numbers to reduce on different sizes diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index e961f29302..74c7c101b3 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -14,6 +14,7 @@ # RUN: %mojo-no-debug %s -t from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run +from memory import UnsafePointer from random import * from stdlib.builtin.sort import ( sort, diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index a5a81113f3..6d4dea9576 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -17,7 +17,7 @@ from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width -from memory import memcmp, bitcast +from memory import memcmp, bitcast, UnsafePointer from utils.stringref import _align_down, _memchr, _memmem diff --git a/stdlib/src/builtin/_hasher.mojo b/stdlib/src/builtin/_hasher.mojo index c75fecb1ab..f69f080fa7 100644 --- a/stdlib/src/builtin/_hasher.mojo +++ b/stdlib/src/builtin/_hasher.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from memory import UnsafePointer + trait _HashableWithHasher: fn __hash__[H: _Hasher](self, inout hasher: H): diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 039fb16a27..15d0d9f450 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements functionality to start a mojo execution.""" +from memory import UnsafePointer from sys import external_call from sys.ffi import _get_global diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 49fab8bdd2..e28dd50cc5 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -31,7 +31,7 @@ from sys import simdwidthof, bitwidthof from collections import InlineArray from builtin.dtype import _uint_type_of_width -from memory import memcpy, memset_zero, stack_allocation, bitcast +from memory import memcpy, memset_zero, stack_allocation, bitcast, UnsafePointer # ===----------------------------------------------------------------------=== # # Implementation diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 37587ae39f..379b4c9c25 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import Dict, List from sys.intrinsics import _type_is_eq -from memory import Arc, memcmp, memcpy +from memory import Arc, memcmp, memcpy, UnsafePointer from utils import StringRef, Variant diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index eeb3c87672..e46323a1d2 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from sys.ffi import C_char -from memory import memcpy +from memory import memcpy, UnsafePointer from collections import List from utils import StringRef, Span, StringSlice from utils import Formattable, Formatter diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index c7c69b100c..9e9afcb1a0 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from sys.intrinsics import _type_is_eq +from memory import UnsafePointer from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 164d5ebb25..2e7d34ae53 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -35,7 +35,7 @@ from builtin.value import StringableCollectionElement from .optional import Optional from bit import is_power_of_two -from memory import memcpy, bitcast +from memory import memcpy, bitcast, UnsafePointer trait KeyElement(CollectionElement, Hashable, EqualityComparable): diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 31b58f0788..a6d69b8bc8 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -21,6 +21,7 @@ from collections import InlineArray from collections._index_normalization import normalize_index from sys.intrinsics import _type_is_eq +from memory import UnsafePointer from memory.maybe_uninitialized import UnsafeMaybeUninitialized # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index f7016bfa93..aece34c39c 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -21,6 +21,7 @@ from os import setenv from sys import external_call, os_is_linux, os_is_macos +from memory import UnsafePointer from utils import StringRef diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 422ec28c78..1d842a08dd 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,6 +23,7 @@ from collections import List, InlineArray from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call from sys.ffi import C_char +from memory import UnsafePointer from utils import StringRef from .path import isdir, split diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 6c44c798d3..2befd26a21 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -20,7 +20,7 @@ from sys import os_is_windows, external_call from sys.ffi import C_char from builtin._location import __call_location, _SourceLocation -from memory import stack_allocation +from memory import stack_allocation, UnsafePointer from utils import StringRef diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 46e24297fd..d07db976dc 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -131,5 +131,5 @@ from collections.string import ( islower, isprintable, ) -from memory import UnsafePointer, Reference, AddressSpace +from memory import Reference, AddressSpace from utils import Formattable, Formatter diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index c69d702dde..d70ed57459 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -21,6 +21,7 @@ from python import PythonObject from sys.intrinsics import _type_is_eq +from memory import UnsafePointer from collections import Dict from utils import StringRef diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 95567d1020..6db69f2f21 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -20,6 +20,7 @@ from sys import is_x86 """ from .ffi import _external_call_const, external_call +from memory import UnsafePointer @always_inline("nodebug") diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index f7ee606fe1..136de48ad8 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -13,7 +13,7 @@ from pathlib import Path -from memory import AddressSpace, bitcast +from memory import AddressSpace, bitcast, UnsafePointer alias _kStartTensorMarker = "[" alias _kEndTensorMarker = "]" diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index 88350e70e0..a27abbf72f 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -25,6 +25,8 @@ Code adapted from: https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs """ +from memory import UnsafePointer + alias TOO_SHORT: UInt8 = 1 << 0 alias TOO_LONG: UInt8 = 1 << 1 alias OVERLONG_3: UInt8 = 1 << 2 diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 4b5a3955ce..51d377bf6a 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -21,7 +21,7 @@ from utils import Span """ from collections import InlineArray -from memory import Reference +from memory import Reference, UnsafePointer from sys.intrinsics import _type_is_eq from builtin.builtin_list import _lit_mut_cast diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index b271cd121a..fdf37871bd 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -24,7 +24,7 @@ from bit import count_leading_zeros from utils import Span from collections.string import _isspace from collections import List -from memory import memcmp +from memory import memcmp, UnsafePointer from sys import simdwidthof, bitwidthof alias StaticString = StringSlice[ImmutableStaticLifetime] diff --git a/stdlib/test/builtin/test_hasher.mojo b/stdlib/test/builtin/test_hasher.mojo index 964400519b..2fa23271f7 100644 --- a/stdlib/test/builtin/test_hasher.mojo +++ b/stdlib/test/builtin/test_hasher.mojo @@ -14,6 +14,7 @@ from builtin._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher +from memory import UnsafePointer from testing import assert_equal diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index d1a4e5c03c..ca4d5223fd 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from sys import has_neon +from memory import UnsafePointer from collections import InlineArray from builtin.simd import _modf diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index 67849b0f61..c070908826 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -14,6 +14,7 @@ # RUN: %mojo %s | FileCheck %s from random import rand +from memory import UnsafePointer from utils import Span diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 3199aa875d..3a9e382169 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from sys.ffi import C_char +from memory import UnsafePointer from testing import ( assert_equal, diff --git a/stdlib/test/collections/test_inline_array.mojo b/stdlib/test/collections/test_inline_array.mojo index 0d891abf63..b16cb4b5bb 100644 --- a/stdlib/test/collections/test_inline_array.mojo +++ b/stdlib/test/collections/test_inline_array.mojo @@ -15,6 +15,7 @@ from collections import InlineArray from testing import assert_equal, assert_false, assert_true from memory.maybe_uninitialized import UnsafeMaybeUninitialized +from memory import UnsafePointer from test_utils import ValueDestructorRecorder diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index f572304775..b3d21ed3a0 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections import InlineList, Set +from memory import UnsafePointer from test_utils import MoveCounter, ValueDestructorRecorder from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index f03595948d..f416003483 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections import List +from memory import UnsafePointer from sys.info import sizeof from test_utils import CopyCounter, MoveCounter from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index b857e4fa83..a664d321df 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -19,6 +19,7 @@ from collections.string import ( _calc_initial_buffer_size_int64, _isspace, ) +from memory import UnsafePointer from python import Python from testing import ( assert_equal, diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 24681cdb8c..4411f57732 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -14,7 +14,7 @@ from collections import List -from memory import Arc +from memory import Arc, UnsafePointer from testing import assert_equal, assert_false, assert_true diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index f2937881e1..6c02fec9b6 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from os import abort +from memory import UnsafePointer from memory.maybe_uninitialized import UnsafeMaybeUninitialized from test_utils import CopyCounter, MoveCounter, ValueDestructorRecorder diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index a47f4e6351..5167af2659 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -10,6 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +from memory import UnsafePointer struct MoveOnly[T: Movable](Movable): diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index f39d3a0982..e299cd7430 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections import InlineArray, List +from memory import UnsafePointer from testing import assert_equal, assert_true from utils import Span diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 97cd01af54..f754afd9c1 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -14,6 +14,7 @@ from testing import assert_equal, assert_false, assert_true +from memory import UnsafePointer from utils import StaticIntTuple, StaticTuple from test_utils import ValueDestructorRecorder diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index bd00fdcdcb..85436004be 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -14,6 +14,7 @@ from sys.ffi import _get_global +from memory import UnsafePointer from testing import assert_equal, assert_false, assert_true from utils import Variant From c4297a27ac554556b69026e1ff1e0b797ae74cfa Mon Sep 17 00:00:00 2001 From: Ahajha <44127594+Ahajha@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:38:14 -0400 Subject: [PATCH 1581/2019] [examples] Be consistent with conda channels MODULAR_ORIG_COMMIT_REV_ID: 48b325ef38369f1d778a7b3a1ed7d3cf0a416c95 --- examples/notebooks/pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml index ffddf3150e..cf658ff6cf 100644 --- a/examples/notebooks/pixi.toml +++ b/examples/notebooks/pixi.toml @@ -3,7 +3,7 @@ name = "Mojo notebooks" version = "1.0.0" description = "Environment for running JupyterLab" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] From 67335c5b8113a78c1de7cb666e02d3aa5b99906c Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 20 Sep 2024 12:27:34 -0500 Subject: [PATCH 1582/2019] [stdlib] feat: Add PyMethodDef.function to simplify method metadata construction * Also add safe Python.add_functions() * Rename Python.add_methods() to unsafe_add_methods() + doc safety requirements * Change Python.add_functions() to raise instead of return error code * Remove unused METHOD_COUNT constant MODULAR_ORIG_COMMIT_REV_ID: 11b49a80bfed7d8ea4a6705ce043166fffced02b --- stdlib/src/python/_cpython.mojo | 29 ++++++++++++++++++++++++- stdlib/src/python/python.mojo | 38 +++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 7c31a928c3..12a0a85667 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -118,7 +118,7 @@ struct PyMethodDef: var method_name: UnsafePointer[C_char] # called ml_name in CPython - # TODO: Support keyword-argument only methods + # TODO(MSTDL-887): Support keyword-argument only methods # Pointer to the function to call var method_impl: Self._PyCFunction_type @@ -143,6 +143,33 @@ struct PyMethodDef: self.method_flags = 0 self.method_docstring = UnsafePointer[C_char]() + fn __init__(inout self, *, other: Self): + """Explicitly construct a deep copy of the provided value. + + Args: + other: The value to copy. + """ + self = other + + @staticmethod + fn function[ + func: fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr, + func_name: StringLiteral, + docstring: StringLiteral = "", + ]() -> Self: + # TODO(MSTDL-896): + # Support a way to get the name of the function from its parameter + # type, similar to `get_linkage_name()`? + + alias METH_VARARGS = 0x1 + + return PyMethodDef( + func_name.unsafe_cstr_ptr(), + func, + METH_VARARGS, + docstring.unsafe_cstr_ptr(), + ) + fn _null_fn_ptr[T: AnyTrivialRegType]() -> T: return __mlir_op.`pop.pointer.bitcast`[_type=T]( diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index ce986b0789..9967d2fcdc 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -249,28 +249,54 @@ struct Python: ) @staticmethod - fn add_methods( + fn add_functions( + inout module: TypedPythonObject["Module"], + owned functions: List[PyMethodDef], + ) raises: + """Adds functions to a PyModule object. + + Args: + module: The PyModule object. + functions: List of function data. + """ + + # Write a zeroed entry at the end as a terminator. + functions.append(PyMethodDef()) + + # FIXME(MSTDL-910): + # This is an intentional memory leak, because we don't store this + # in a global variable (yet). + var ptr: UnsafePointer[PyMethodDef] = functions.steal_data() + + return Self.unsafe_add_methods(module, ptr) + + @staticmethod + fn unsafe_add_methods( inout module: TypedPythonObject["Module"], functions: UnsafePointer[PyMethodDef], - ) -> Int: + ) raises: """Adds methods to a PyModule object. + Safety: + The provided `functions` pointer must point to data that lives + for the duration of the associated Python interpreter session. + Args: module: The PyModule object. functions: A null terminated pointer to function data. - - Returns: - Status code indicating success or failure. """ var cpython = _get_global_python_itf().cpython() - return cpython.PyModule_AddFunctions( + var result = cpython.PyModule_AddFunctions( # Safety: `module` pointer lives long enough because its reference # argument. module.unsafe_as_py_object_ptr(), functions, ) + if result != 0: + Python.throw_python_exception_if_error_state(cpython) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# From a94b16979008633dfc4d6b2a3d06e03336e5ea76 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 20 Sep 2024 11:54:55 -0600 Subject: [PATCH 1583/2019] [stdlib] Rename `sys.ffi.C_*` to `c_*` Rename `C_int` to `c_int`. Ditto for `C_long`, `C_long_long`, and so on. MODULAR_ORIG_COMMIT_REV_ID: 751ffdd6c78cb3c92132f897f5ff562e35022a11 --- docs/changelog-released.md | 6 ++--- docs/changelog.md | 3 +++ stdlib/src/builtin/string_literal.mojo | 6 ++--- stdlib/src/collections/string.mojo | 6 ++--- stdlib/src/os/os.mojo | 8 +++--- stdlib/src/pathlib/path.mojo | 8 +++--- stdlib/src/pwd/_linux.mojo | 4 +-- stdlib/src/pwd/_macos.mojo | 4 +-- stdlib/src/python/_cpython.mojo | 27 ++++++++++---------- stdlib/src/sys/ffi.mojo | 10 ++++---- stdlib/src/utils/stringref.mojo | 6 ++--- stdlib/test/builtin/test_string_literal.mojo | 4 +-- stdlib/test/sys/test_c_types.mojo | 16 ++++++------ 13 files changed, 55 insertions(+), 53 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 5c7f78aaff..43444b82a3 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -475,7 +475,7 @@ detailed information in the following sections: - Added the [`unsafe_cstr_ptr()`](/mojo/stdlib/collections/string/String#unsafe_cstr_ptr) method to `String` and `StringLiteral`, which returns an - `UnsafePointer[C_char]` for convenient interoperability with C APIs. + `UnsafePointer[c_char]` for convenient interoperability with C APIs. - Added the `byte_length()` method to [`String`](/mojo/stdlib/collections/string/String#byte_length), @@ -500,7 +500,7 @@ detailed information in the following sections: initializer that accepts a `StringLiteral`. - The [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) constructors from - `DTypePointer.int8` have been changed to take a `UnsafePointer[C_char]`, + `DTypePointer.int8` have been changed to take a `UnsafePointer[c_char]`, reflecting their use for compatibility with C APIs. - Continued the transition to `UnsafePointer` and unsigned byte type for @@ -793,7 +793,7 @@ detailed information in the following sections: hash. In general, hashes should be an unsigned integer, and can also lead to improved performance in certain cases. - - Added the [`C_char`](/mojo/stdlib/sys/ffi/#aliases) type alias in `sys.ffi`. + - Added the [`c_char`](/mojo/stdlib/sys/ffi/#aliases) type alias in `sys.ffi`. - [`sort()`](/mojo/stdlib/builtin/sort/sort) now supports a `stable` parameter. It can be called by diff --git a/docs/changelog.md b/docs/changelog.md index d6df81e58c..3985ea80a8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -75,6 +75,9 @@ what we publish. - Restore implicit copyability of `Tuple` and `ListLiteral`. +- The aliases for C FFI have been renamed: `C_int` -> `c_int`, `C_long` -> `c_long` + and so on. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index e46323a1d2..4bedfb78c2 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys.ffi import C_char +from sys.ffi import c_char from memory import memcpy, UnsafePointer from collections import List @@ -327,7 +327,7 @@ struct StringLiteral( # return type. return ptr.bitcast[UInt8]() - fn unsafe_cstr_ptr(self) -> UnsafePointer[C_char]: + fn unsafe_cstr_ptr(self) -> UnsafePointer[c_char]: """Retrieves a C-string-compatible pointer to the underlying memory. The returned pointer is guaranteed to be NUL terminated, and not null. @@ -335,7 +335,7 @@ struct StringLiteral( Returns: The pointer to the underlying memory. """ - return self.unsafe_ptr().bitcast[C_char]() + return self.unsafe_ptr().bitcast[c_char]() @always_inline fn as_string_slice(self) -> StringSlice[ImmutableAnyLifetime]: diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index fbedee4e2b..723d9ef2f0 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic -from sys.ffi import C_char +from sys.ffi import c_char from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy @@ -1358,7 +1358,7 @@ struct String( """ return self._buffer.data - fn unsafe_cstr_ptr(self) -> UnsafePointer[C_char]: + fn unsafe_cstr_ptr(self) -> UnsafePointer[c_char]: """Retrieves a C-string-compatible pointer to the underlying memory. The returned pointer is guaranteed to be null, or NUL terminated. @@ -1366,7 +1366,7 @@ struct String( Returns: The pointer to the underlying memory. """ - return self.unsafe_ptr().bitcast[C_char]() + return self.unsafe_ptr().bitcast[c_char]() fn as_bytes(self) -> Self._buffer_type: """Retrieves the underlying byte sequence encoding the characters in diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 1d842a08dd..e43429fbe7 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -21,7 +21,7 @@ from os import listdir from collections import List, InlineArray from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call -from sys.ffi import C_char +from sys.ffi import c_char from memory import UnsafePointer from utils import StringRef @@ -62,7 +62,7 @@ struct _dirent_linux: """Length of the record.""" var d_type: Int8 """Type of file.""" - var name: InlineArray[C_char, Self.MAX_NAME_SIZE] + var name: InlineArray[c_char, Self.MAX_NAME_SIZE] """Name of entry.""" @@ -79,11 +79,11 @@ struct _dirent_macos: """Length of the name.""" var d_type: Int8 """Type of file.""" - var name: InlineArray[C_char, Self.MAX_NAME_SIZE] + var name: InlineArray[c_char, Self.MAX_NAME_SIZE] """Name of entry.""" -fn _strnlen(ptr: UnsafePointer[C_char], max: Int) -> Int: +fn _strnlen(ptr: UnsafePointer[c_char], max: Int) -> Int: var offset = 0 while offset < max and ptr[offset]: offset += 1 diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 2befd26a21..bbfd67bfe6 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -17,7 +17,7 @@ import os from collections import List from os import PathLike, listdir, stat_result from sys import os_is_windows, external_call -from sys.ffi import C_char +from sys.ffi import c_char from builtin._location import __call_location, _SourceLocation from memory import stack_allocation, UnsafePointer @@ -34,14 +34,14 @@ fn cwd() raises -> Path: The current directory. """ alias MAX_CWD_BUFFER_SIZE = 1024 - var buf = stack_allocation[MAX_CWD_BUFFER_SIZE, C_char]() + var buf = stack_allocation[MAX_CWD_BUFFER_SIZE, c_char]() - var res = external_call["getcwd", UnsafePointer[C_char]]( + var res = external_call["getcwd", UnsafePointer[c_char]]( buf, Int(MAX_CWD_BUFFER_SIZE) ) # If we get a nullptr, then we raise an error. - if res == UnsafePointer[C_char](): + if res == UnsafePointer[c_char](): raise Error("unable to query the current directory") return String(StringRef(buf)) diff --git a/stdlib/src/pwd/_linux.mojo b/stdlib/src/pwd/_linux.mojo index 7635c8afdc..d2ed4175a7 100644 --- a/stdlib/src/pwd/_linux.mojo +++ b/stdlib/src/pwd/_linux.mojo @@ -12,11 +12,11 @@ # ===----------------------------------------------------------------------=== # from .pwd import Passwd from memory import UnsafePointer -from sys.ffi import C_char, external_call +from sys.ffi import c_char, external_call alias uid_t = Int32 alias gid_t = Int32 -alias char = UnsafePointer[C_char] +alias char = UnsafePointer[c_char] @register_passable("trivial") diff --git a/stdlib/src/pwd/_macos.mojo b/stdlib/src/pwd/_macos.mojo index 33917b3d12..9908504d97 100644 --- a/stdlib/src/pwd/_macos.mojo +++ b/stdlib/src/pwd/_macos.mojo @@ -12,12 +12,12 @@ # ===----------------------------------------------------------------------=== # from .pwd import Passwd from memory import UnsafePointer -from sys.ffi import C_char, external_call +from sys.ffi import c_char, external_call alias uid_t = Int32 alias gid_t = Int32 alias time_t = Int -alias char = UnsafePointer[C_char] +alias char = UnsafePointer[c_char] @register_passable("trivial") diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 12a0a85667..61ae3b96b4 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -17,7 +17,7 @@ from os.path import dirname from pathlib import Path from sys import external_call from sys.arg import argv -from sys.ffi import DLHandle, C_char, C_int +from sys.ffi import DLHandle, c_char, c_int from memory import UnsafePointer @@ -91,7 +91,7 @@ struct PythonVersion: fn _py_get_version(lib: DLHandle) -> StringRef: - var version_string = lib.get_function[fn () -> UnsafePointer[C_char]]( + var version_string = lib.get_function[fn () -> UnsafePointer[c_char]]( "Py_GetVersion" )() return StringRef(version_string) @@ -116,7 +116,7 @@ struct PyMethodDef: # Fields # ===-------------------------------------------------------------------===# - var method_name: UnsafePointer[C_char] # called ml_name in CPython + var method_name: UnsafePointer[c_char] # called ml_name in CPython # TODO(MSTDL-887): Support keyword-argument only methods # Pointer to the function to call @@ -124,10 +124,10 @@ struct PyMethodDef: # Flags bits indicating how the call should be constructed. # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef for the various calling conventions - var method_flags: C_int + var method_flags: c_int # Points to the contents of the docstring for the module. - var method_docstring: UnsafePointer[C_char] + var method_docstring: UnsafePointer[c_char] # ===-------------------------------------------------------------------===# # Life cycle methods @@ -138,10 +138,10 @@ struct PyMethodDef: This is suitable for use terminating an array of PyMethodDef values. """ - self.method_name = UnsafePointer[C_char]() + self.method_name = UnsafePointer[c_char]() self.method_impl = _null_fn_ptr[Self._PyCFunction_type]() self.method_flags = 0 - self.method_docstring = UnsafePointer[C_char]() + self.method_docstring = UnsafePointer[c_char]() fn __init__(inout self, *, other: Self): """Explicitly construct a deep copy of the provided value. @@ -325,10 +325,10 @@ struct PyModuleDef(Stringable, Representable, Formattable): var base: PyModuleDef_Base # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef - var name: UnsafePointer[C_char] + var name: UnsafePointer[c_char] # Points to the contents of the docstring for the module. - var docstring: UnsafePointer[C_char] + var docstring: UnsafePointer[c_char] var size: Int @@ -356,8 +356,7 @@ struct PyModuleDef(Stringable, Representable, Formattable): fn __init__(inout self, name: String): self.base = PyModuleDef_Base() self.name = name.unsafe_cstr_ptr() - # self.docstring = UnsafePointer[C_char]() - self.docstring = UnsafePointer[C_char]() + self.docstring = UnsafePointer[c_char]() # means that the module does not support sub-interpreters self.size = -1 self.methods = UnsafePointer[PyMethodDef]() @@ -468,7 +467,7 @@ struct CPython: # and make this initialization a raising function. self.init_error = external_call[ "KGEN_CompilerRT_Python_SetPythonPath", - UnsafePointer[C_char], + UnsafePointer[c_char], ]() var python_lib = getenv("MOJO_PYTHON_LIBRARY") @@ -1220,7 +1219,7 @@ struct CPython: fn ( UnsafePointer[UInt8], Int, - UnsafePointer[C_char], + UnsafePointer[c_char], ) -> PyObjectPtr ](StringRef("PyUnicode_DecodeUTF8"))( strref.data, strref.length, "strict".unsafe_cstr_ptr() @@ -1239,7 +1238,7 @@ struct CPython: fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: var result = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[Int]) -> UnsafePointer[C_char] + fn (PyObjectPtr, UnsafePointer[Int]) -> UnsafePointer[c_char] ]("PyUnicode_AsUTF8AndSize")(py_object, UnsafePointer[Int]()) return StringRef(result) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index e2903a28b6..ff1544f7e0 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -21,23 +21,23 @@ from .info import os_is_linux, os_is_windows, is_64bit, os_is_macos from .intrinsics import _mlirtype_is_eq from builtin.builtin_list import _LITRefPackHelper -alias C_char = Int8 +alias c_char = Int8 """C `char` type.""" -alias C_int = Int32 +alias c_int = Int32 """C `int` type. The C `int` type is typically a signed 32-bit integer on commonly used targets today. """ -alias C_long = Scalar[_c_long_dtype()] +alias c_long = Scalar[_c_long_dtype()] """C `long` type. The C `long` type is typically a signed 64-bit integer on macOS and Linux, and a 32-bit integer on Windows.""" -alias C_long_long = Scalar[_c_long_long_dtype()] +alias c_long_long = Scalar[_c_long_long_dtype()] """C `long long` type. The C `long long` type is typically a signed 64-bit integer on commonly used @@ -199,7 +199,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @always_inline fn _get_function[ result_type: AnyTrivialRegType - ](self, name: UnsafePointer[C_char]) -> result_type: + ](self, name: UnsafePointer[c_char]) -> result_type: """Returns a handle to the function with the given name in the dynamic library. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 4cbd323e97..c810d1865f 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -19,7 +19,7 @@ from collections.string import _atol, _isspace from memory import UnsafePointer, memcmp, bitcast from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice -from sys.ffi import C_char +from sys.ffi import c_char from sys import simdwidthof # ===----------------------------------------------------------------------=== # @@ -90,7 +90,7 @@ struct StringRef( self = StringRef(str.unsafe_ptr(), len(str)) @always_inline - fn __init__(inout self, ptr: UnsafePointer[C_char], len: Int): + fn __init__(inout self, ptr: UnsafePointer[c_char], len: Int): """Construct a StringRef value given a (potentially non-0 terminated string). @@ -123,7 +123,7 @@ struct StringRef( self = StringRef(ptr, len) @always_inline - fn __init__(inout self, ptr: UnsafePointer[C_char]): + fn __init__(inout self, ptr: UnsafePointer[c_char]): """Construct a StringRef value given a null-terminated string. Note that you should use the constructor from `UnsafePointer[UInt8]` instead diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 3a9e382169..f6b3eaf455 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.ffi import C_char +from sys.ffi import c_char from memory import UnsafePointer from testing import ( @@ -227,7 +227,7 @@ def test_layout(): # assert_equal(empty[0], 0) # Test non-empty StringLiteral C string - var ptr: UnsafePointer[C_char] = "hello".unsafe_cstr_ptr() + var ptr: UnsafePointer[c_char] = "hello".unsafe_cstr_ptr() assert_equal(ptr[0], ord("h")) assert_equal(ptr[1], ord("e")) assert_equal(ptr[2], ord("l")) diff --git a/stdlib/test/sys/test_c_types.mojo b/stdlib/test/sys/test_c_types.mojo index a8641d9b50..61029f21c9 100644 --- a/stdlib/test/sys/test_c_types.mojo +++ b/stdlib/test/sys/test_c_types.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from sys.info import os_is_linux, os_is_macos, os_is_windows, is_64bit, is_32bit -from sys.ffi import C_int, C_long, C_long_long +from sys.ffi import c_int, c_long, c_long_long from testing import assert_equal, assert_true @@ -26,27 +26,27 @@ from testing import assert_equal, assert_true def test_c_int_type(): if is_64bit() and (os_is_macos() or os_is_linux() or os_is_windows()): # `int` is always 32 bits on the modern 64-bit OSes. - assert_equal(C_int.type, DType.int32) + assert_equal(c_int.type, DType.int32) else: - assert_true(False, "platform C_int size is untested") + assert_true(False, "platform c_int size is untested") def test_c_long_type(): if is_64bit() and (os_is_macos() or os_is_linux()): # `long` is 64 bits on macOS and Linux. - assert_equal(C_long.type, DType.int64) + assert_equal(c_long.type, DType.int64) elif is_64bit() and os_is_windows(): # `long` is 32 bits only on Windows. - assert_equal(C_long.type, DType.int32) + assert_equal(c_long.type, DType.int32) else: - assert_true(False, "platform C_long size is untested") + assert_true(False, "platform c_long size is untested") def test_c_long_long_type(): if is_64bit() and (os_is_macos() or os_is_linux() or os_is_windows()): - assert_equal(C_long_long.type, DType.int64) + assert_equal(c_long_long.type, DType.int64) else: - assert_true(False, "platform C_long_long size is untested") + assert_true(False, "platform c_long_long size is untested") def main(): From 2ec9be1c16953d2af2dfd3fef101e9d7400852f6 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 20 Sep 2024 16:40:54 -0500 Subject: [PATCH 1584/2019] [stdlib] feat: Add PythonObject.from_borrowed_ptr() There are two kinds of `PyObject*` references: borrowed references, and strong references. The later are more common, but the former do occur, and are important to handle correctly to prevent use-after-free bugs. This new function is intended to make it a little bit cleaner to construct an owned PythonObject when all one has is a borrowed reference. MODULAR_ORIG_COMMIT_REV_ID: dfef9c4b31e269e9b01c266326318fcb8dcb6762 --- docs/changelog.md | 7 +++++ stdlib/src/python/python.mojo | 13 +++++----- stdlib/src/python/python_object.mojo | 39 +++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3985ea80a8..db1ffb9e1f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -57,6 +57,13 @@ what we publish. any side effects that occur in the expression are never evaluated at runtime, eliminating concerns about `__type_of(expensive())` being a problem. +- Add `PythonObject.from_borrowed_ptr()`, to simplify the construction of + `PythonObject` values from CPython 'borrowed reference' pointers. + + The existing `PythonObject.__init__(PyObjectPtr)` should continue to be used + for the more common case of constructing a `PythonObject` from a + 'strong reference' pointer. + - The `rebind` standard library function now works with memory-only types in addition to `@register_passable("trivial")` ones, without requiring a copy. diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 9967d2fcdc..4a88d3de23 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -113,12 +113,13 @@ struct Python: `PythonObject` containing the result of the evaluation. """ var cpython = _get_global_python_itf().cpython() - var module = PythonObject(cpython.PyImport_AddModule(name)) - # PyImport_AddModule returns a borrowed reference - IncRef it to keep it alive. - cpython.Py_IncRef(module.py_object) - var dict_obj = PythonObject(cpython.PyModule_GetDict(module.py_object)) - # PyModule_GetDict returns a borrowed reference - IncRef it to keep it alive. - cpython.Py_IncRef(dict_obj.py_object) + # PyImport_AddModule returns a borrowed reference. + var module = PythonObject.from_borrowed_ptr( + cpython.PyImport_AddModule(name) + ) + var dict_obj = PythonObject.from_borrowed_ptr( + cpython.PyModule_GetDict(module.py_object) + ) if file: # We compile the code as provided and execute in the module # context. Note that this may be an existing module if the provided diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index d70ed57459..b05817733a 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -210,7 +210,8 @@ struct PythonObject( self = other fn __init__(inout self, ptr: PyObjectPtr): - """Initialize the object with a `PyObjectPtr` value. + """Initialize this object from an owned reference-counted Python object + pointer. Ownership of the reference will be assumed by `PythonObject`. @@ -219,6 +220,42 @@ struct PythonObject( """ self.py_object = ptr + @staticmethod + fn from_borrowed_ptr(borrowed_ptr: PyObjectPtr) -> Self: + """Initialize this object from a borrowed reference-counted Python + object pointer. + + The reference count of the pointee object will be incremented, and + ownership of the additional reference count will be assumed by the + initialized `PythonObject`. + + The CPython API documentation indicates the ownership semantics of the + returned object on any function that returns a `PyObject*` value. The + two possible annotations are: + + * "Return value: New reference." + * "Return value: Borrowed reference. + + This function should be used to construct a `PythonObject` from the + pointer returned by 'Borrowed reference'-type objects. + + Args: + borrowed_ptr: A borrowed reference counted pointer to a Python + object. + + Returns: + An owned PythonObject pointer. + """ + var cpython = _get_global_python_itf().cpython() + + # SAFETY: + # We were passed a Python 'borrowed reference', so for it to be + # safe to store this reference, we must increment the reference + # count to convert this to a 'strong reference'. + cpython.Py_IncRef(borrowed_ptr) + + return PythonObject(borrowed_ptr) + fn __init__(inout self, owned typed_obj: TypedPythonObject[_]): """Construct a PythonObject from a typed object, dropping the type hint information. From 0b1ccbaa528f4386fcccfd271b71c3064a5eecba Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 20 Sep 2024 16:36:16 -0600 Subject: [PATCH 1585/2019] [stdlib] Add ffi aliases for `size_t` and `ssize_t` To improve expressiveness and intent when doing FFI, add aliases that correspond to C's `size_t` and `ssize_t` types. They will get applied in future PRs. MODULAR_ORIG_COMMIT_REV_ID: a232668b4d96bc28a8650646f6966f5d57df6d6f --- stdlib/src/sys/ffi.mojo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index ff1544f7e0..7a3b7ea76b 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -43,6 +43,12 @@ alias c_long_long = Scalar[_c_long_long_dtype()] The C `long long` type is typically a signed 64-bit integer on commonly used targets today.""" +alias c_size_t = UInt +"""C `size_t` type.""" + +alias c_ssize_t = Int +"""C `ssize_t` type.""" + fn _c_long_dtype() -> DType: # https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models From d914f06a6244148d6b5c2db7a5034425ac2a14e1 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 20 Sep 2024 18:20:25 -0700 Subject: [PATCH 1586/2019] [mojo-lang] Enable autoparameterization of parameters MODULAR_ORIG_COMMIT_REV_ID: 8829df5375dbac06aaa61bf41b5351340de76452 --- docs/changelog.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index db1ffb9e1f..6c023e1c30 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -67,6 +67,19 @@ what we publish. - The `rebind` standard library function now works with memory-only types in addition to `@register_passable("trivial")` ones, without requiring a copy. +- Autoparameterization of parameters is now supported. Specifying a parameter + type with unbound parameters causes them to be implicitly added to the + function signature as inferred parameters. + + ```mojo + fn foo[value: SIMD[DType.int32, _]](): + pass + + # Equivalent to + fn foo[size: Int, //, value: SIMD[DType.int32, size]](): + pass + ``` + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` From 1590ec226a30ce8f7e01ee4cb8841ea30d68c91f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 20 Sep 2024 21:13:29 -0700 Subject: [PATCH 1587/2019] [mojo-lang] Fix weird issue with parameter autoparameterization (#47625) We want to skip autoparameterized infer-only parameters when checking keyword-passed parameters. This also fixes a loophole that allowed passing a infer-only parameter explicitly by using a keyword. MODULAR_ORIG_COMMIT_REV_ID: 7d9a93f897443513e6390a8468dafaf3c038c4c8 --- stdlib/benchmarks/algorithm/bench_elementwise.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 275bbf4afa..799c9bf888 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -39,7 +39,7 @@ fn bench_elementwise[n: Int](inout b: Bencher) raises: vector[idx[0]] = 42 elementwise[func, 1](Index(n)) - elementwise[func=func, simd_width = simdwidthof[DType.index](), rank=1]( + elementwise[func=func, simd_width = simdwidthof[DType.index]()]( Index(n) ) From d1b9fb909d1b7ba2b9a69acb072e58e8271010be Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Sat, 21 Sep 2024 07:32:48 -0700 Subject: [PATCH 1588/2019] [External] [stdlib] Fix chr impl taking funcs to string_slice and separating (#47532) [External] [stdlib] Fix chr impl taking funcs to string_slice and separating Fix `chr` implementation taking funcs to `string_slice` and separating their respective functionalities. This code will be used elsewhere e.g., PR [#3496](https://github.com/modularml/mojo/pull/3496#discussion_r1765391252) ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3506 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3506 MODULAR_ORIG_COMMIT_REV_ID: dc8c96e58f5e272d01e3c26f6daf3ffe8f7c0b36 --- stdlib/src/collections/string.mojo | 61 +++++++++++++----------------- stdlib/src/utils/string_slice.mojo | 33 ++++++++++++++++ 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 723d9ef2f0..2528b9a79a 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -34,7 +34,12 @@ from utils import ( Formatter, ) from utils.format import ToFormatter -from utils.string_slice import _utf8_byte_type, _StringSliceIter +from utils.string_slice import ( + _utf8_byte_type, + _StringSliceIter, + _unicode_codepoint_utf8_byte_length, + _shift_unicode_to_utf8, +) # ===----------------------------------------------------------------------=== # # ord @@ -107,50 +112,36 @@ fn ord(s: StringSlice) -> Int: fn chr(c: Int) -> String: - """Returns a string based on the given Unicode code point. - - Returns the string representing a character whose code point is the integer - `c`. For example, `chr(97)` returns the string `"a"`. This is the inverse of - the `ord()` function. + """Returns a String based on the given Unicode code point. This is the + inverse of the `ord()` function. Args: c: An integer that represents a code point. Returns: A string containing a single character based on the given code point. + + Examples: + ```mojo + print(chr(97)) # "a" + print(chr(8364)) # "€" + ``` + . """ - # Unicode (represented as UInt32 BE) to UTF-8 conversion : - # 1: 00000000 00000000 00000000 0aaaaaaa -> 0aaaaaaa a - # 2: 00000000 00000000 00000aaa aabbbbbb -> 110aaaaa 10bbbbbb a >> 6 | 0b11000000, b | 0b10000000 - # 3: 00000000 00000000 aaaabbbb bbcccccc -> 1110aaaa 10bbbbbb 10cccccc a >> 12 | 0b11100000, b >> 6 | 0b10000000, c | 0b10000000 - # 4: 00000000 000aaabb bbbbcccc ccdddddd -> 11110aaa 10bbbbbb 10cccccc 10dddddd a >> 18 | 0b11110000, b >> 12 | 0b10000000, c >> 6 | 0b10000000, d | 0b10000000 - if (c >> 7) == 0: # This is 1 byte ASCII char - return _chr_ascii(c) + if c < 0b1000_0000: # 1 byte ASCII char + return String(String._buffer_type(c, 0)) - @always_inline - fn _utf8_len(val: Int) -> Int: - debug_assert( - 0 <= val <= 0x10FFFF, "Value is not a valid Unicode code point" - ) - alias sizes = SIMD[DType.int32, 4]( - 0, 0b1111_111, 0b1111_1111_111, 0b1111_1111_1111_1111 - ) - var values = SIMD[DType.int32, 4](val) - var mask = values > sizes - return int(mask.cast[DType.uint8]().reduce_add()) - - var num_bytes = _utf8_len(c) + var num_bytes = _unicode_codepoint_utf8_byte_length(c) var p = UnsafePointer[UInt8].alloc(num_bytes + 1) - var shift = 6 * (num_bytes - 1) - var mask = UInt8(0xFF) >> (num_bytes + 1) - var num_bytes_marker = UInt8(0xFF) << (8 - num_bytes) - p.store[width=1](((c >> shift) & mask) | num_bytes_marker) - for i in range(1, num_bytes): - shift -= 6 - p.store[width=1](i, ((c >> shift) & 0b00111111) | 0b10000000) - p.store[width=1](num_bytes, 0) - return String(p.bitcast[UInt8](), num_bytes + 1) + _shift_unicode_to_utf8(p, c, num_bytes) + # TODO: decide whether to use replacement char (�) or raise ValueError + # if not _is_valid_utf8(p, num_bytes): + # debug_assert(False, "Invalid Unicode code point") + # p.free() + # return chr(0xFFFD) + p[num_bytes] = 0 + return String(ptr=p, len=num_bytes + 1) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index fdf37871bd..1eb89d6a35 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -31,6 +31,39 @@ alias StaticString = StringSlice[ImmutableStaticLifetime] """An immutable static string slice.""" +fn _unicode_codepoint_utf8_byte_length(c: Int) -> Int: + alias sizes = SIMD[DType.int32, 4](0, 0b0111_1111, 0b0111_1111_1111, 0xFFFF) + return int((sizes < c).cast[DType.uint8]().reduce_add()) + + +fn _shift_unicode_to_utf8(ptr: UnsafePointer[UInt8], c: Int, num_bytes: Int): + """Shift unicode to utf8 representation. + + ### Unicode (represented as UInt32 BE) to UTF-8 conversion: + - 1: 00000000 00000000 00000000 0aaaaaaa -> 0aaaaaaa + - a + - 2: 00000000 00000000 00000aaa aabbbbbb -> 110aaaaa 10bbbbbb + - (a >> 6) | 0b11000000, b | 0b10000000 + - 3: 00000000 00000000 aaaabbbb bbcccccc -> 1110aaaa 10bbbbbb 10cccccc + - (a >> 12) | 0b11100000, (b >> 6) | 0b10000000, c | 0b10000000 + - 4: 00000000 000aaabb bbbbcccc ccdddddd -> 11110aaa 10bbbbbb 10cccccc + 10dddddd + - (a >> 18) | 0b11110000, (b >> 12) | 0b10000000, (c >> 6) | 0b10000000, + d | 0b10000000 + """ + if num_bytes == 1: + ptr[0] = UInt8(c) + return + + var shift = 6 * (num_bytes - 1) + var mask = UInt8(0xFF) >> (num_bytes + 1) + var num_bytes_marker = UInt8(0xFF) << (8 - num_bytes) + ptr[0] = ((c >> shift) & mask) | num_bytes_marker + for i in range(1, num_bytes): + shift -= 6 + ptr[i] = ((c >> shift) & 0b0011_1111) | 0b1000_0000 + + fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): """UTF-8 byte type. From 4e9125e8dfc19a6db503ed7328e7d8b632485fe7 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sat, 21 Sep 2024 09:22:55 -0700 Subject: [PATCH 1589/2019] [mojo-stdlib] Remove extraneous characters in assembly format (NFC) Not sure why MLIR doesn't pick this up and reject it... MODULAR_ORIG_COMMIT_REV_ID: 899d123d375e3dbf6819900bdafe295a15c45df1 --- stdlib/src/builtin/type_aliases.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 69c58fe19e..c008f2c202 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -24,10 +24,10 @@ alias ImmutableLifetime = __mlir_type.`!lit.lifetime<0>` alias MutableLifetime = __mlir_type.`!lit.lifetime<1>` """Mutable lifetime reference type.""" -alias ImmutableAnyLifetime = __mlir_attr.`#lit.any.lifetime<0>: !lit.lifetime<0>` +alias ImmutableAnyLifetime = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<0>` """The immutable lifetime that might access any memory value.""" -alias MutableAnyLifetime = __mlir_attr.`#lit.any.lifetime<1>: !lit.lifetime<1>` +alias MutableAnyLifetime = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<1>` """The mutable lifetime that might access any memory value.""" # TODO: We don't have a "static" lifetime to use yet, so we use Any. From bb4e4ceae8b66f5824674aab4f26cd59ebd60545 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 21 Sep 2024 19:01:23 -0700 Subject: [PATCH 1590/2019] [mojo-stdlib] Remove needless copies This reduces unneeded copies for lifetime extensin. MODULAR_ORIG_COMMIT_REV_ID: 3407615a18ae3b978501d7c6ecd6d71f668825ab --- stdlib/src/builtin/file.mojo | 3 --- 1 file changed, 3 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index ed351ddea9..a2cda5674c 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -85,9 +85,6 @@ struct FileHandle: """ self.__init__(path._strref_dangerous(), mode._strref_dangerous()) - _ = path - _ = mode - fn __init__(inout self, path: StringRef, mode: StringRef) raises: """Construct the FileHandle using the file path and string. From 3506619ea55dc571445f66c93d762a3e9e257b4b Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 22 Sep 2024 14:08:38 -0600 Subject: [PATCH 1591/2019] This reduces unneeded copies for lifetime extension. MODULAR_ORIG_COMMIT_REV_ID: 13de3cf0c0d139c2c5a0de7247799913e72172ea --- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/hash.mojo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 8b5b1dca21..99fdfd9c98 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -331,7 +331,7 @@ fn _try_write_int[ ) fmt.write_str(zero) - _ = zero_buf + _ = zero_buf^ return None diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index e28dd50cc5..644f8ca5b4 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -265,7 +265,7 @@ fn hash(bytes: UnsafePointer[UInt8], n: Int) -> UInt: memset_zero(ptr + r, stride - r) # set the rest to 0 var last_value = ptr.bitcast[Scalar[type]]().load[width=simd_width]() hash_data = _HASH_UPDATE(hash_data, last_value) - _ = remaining # We make sure the array lives long enough. + _ = remaining^ # We make sure the array lives long enough. # Now finally, hash the final SIMD vector state. return _hash_simd(hash_data) From e8bc05d467608a7bfd89882cac7860e223e3da85 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Sun, 22 Sep 2024 23:57:33 -0400 Subject: [PATCH 1592/2019] [vscode] update changelog MODULAR_ORIG_COMMIT_REV_ID: e098f4ffb85f77b8595877ae52b5883c16869baf --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 6c023e1c30..653814cdcb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -98,6 +98,8 @@ what we publish. - The aliases for C FFI have been renamed: `C_int` -> `c_int`, `C_long` -> `c_long` and so on. +- The VS Code extension now allows selecting a default SDK when multiple are available. + ### ❌ Removed ### 🛠️ Fixed @@ -107,3 +109,5 @@ what we publish. - [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init causing use of uninitialized variable + +- The VS Code extension now auto-updates its private copy of the MAX SDK. From 7bc14140dadab312df2f4afb28e64a6f1a9fe566 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 23 Sep 2024 10:08:59 -0600 Subject: [PATCH 1593/2019] [stdlib] Add alias for void* in ffi To improve expressiveness/intent, add an alias for C's `void*` when interacting in FFI places. Apply this in the CPython parts of the stdlib. It can get applied to other parts of the stdlib in a follow-up. MODULAR_ORIG_COMMIT_REV_ID: f90cd1fb352687ec98f74135bf78ab7bb682597e --- stdlib/src/python/_cpython.mojo | 12 +++++------- stdlib/src/python/python.mojo | 6 +++--- stdlib/src/sys/ffi.mojo | 3 +++ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 61ae3b96b4..43bba66415 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -17,7 +17,7 @@ from os.path import dirname from pathlib import Path from sys import external_call from sys.arg import argv -from sys.ffi import DLHandle, c_char, c_int +from sys.ffi import DLHandle, c_char, c_int, OpaquePointer from memory import UnsafePointer @@ -314,7 +314,7 @@ struct PyModuleDef_Base(Stringable, Representable, Formattable): @value struct PyModuleDef_Slot: var slot: Int - var value: UnsafePointer[NoneType] + var value: OpaquePointer # Ref: https://docs.python.org/3/c-api/module.html#c.PyModuleDef @@ -339,18 +339,16 @@ struct PyModuleDef(Stringable, Representable, Formattable): var slots: UnsafePointer[PyModuleDef_Slot] # TODO(MOCO-1138): These are C ABI function pointers, not Mojo functions. - alias _visitproc_fn_type = fn (PyObjectPtr, UnsafePointer[NoneType]) -> Int + alias _visitproc_fn_type = fn (PyObjectPtr, OpaquePointer) -> Int alias _traverse_fn_type = fn ( - PyObjectPtr, Self._visitproc_fn_type, UnsafePointer[NoneType] + PyObjectPtr, Self._visitproc_fn_type, OpaquePointer ) -> Int var traverse_fn: Self._traverse_fn_type alias _clear_fn_type = fn (PyObjectPtr) -> Int var clear_fn: Self._clear_fn_type - alias _free_fn_type = fn (UnsafePointer[NoneType]) -> UnsafePointer[ - NoneType - ] + alias _free_fn_type = fn (OpaquePointer) -> OpaquePointer var free_fn: Self._free_fn_type fn __init__(inout self, name: String): diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 4a88d3de23..e8422df2bb 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -22,7 +22,7 @@ from python import Python from collections import Dict from os import abort, getenv from sys import external_call, sizeof -from sys.ffi import _get_global +from sys.ffi import _get_global, OpaquePointer from memory import UnsafePointer @@ -32,13 +32,13 @@ from ._cpython import CPython, Py_eval_input, Py_file_input, PyMethodDef from .python_object import PythonObject, TypedPythonObject -fn _init_global(ignored: UnsafePointer[NoneType]) -> UnsafePointer[NoneType]: +fn _init_global(ignored: OpaquePointer) -> OpaquePointer: var ptr = UnsafePointer[CPython].alloc(1) ptr[] = CPython() return ptr.bitcast[NoneType]() -fn _destroy_global(python: UnsafePointer[NoneType]): +fn _destroy_global(python: OpaquePointer): var p = python.bitcast[CPython]() CPython.destroy(p[]) python.free() diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 7a3b7ea76b..0a9294c581 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -49,6 +49,9 @@ alias c_size_t = UInt alias c_ssize_t = Int """C `ssize_t` type.""" +alias OpaquePointer = UnsafePointer[NoneType] +"""An opaque pointer, equivalent to the C `void*` type.""" + fn _c_long_dtype() -> DType: # https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models From aba2f8b3e7eb7575f46b92c45927ab9ea6504b6e Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 23 Sep 2024 11:12:20 -0500 Subject: [PATCH 1594/2019] [stdlib] feat: Support `TypedPythonObject["Tuple"].__getitem__` This adds some initial logic needed to work with Python 'tuple' values safely via TypedPythonObject. * Add Python.unsafe_get_python_exception() helper * Add CPython.PyTuple_GetItem * Add Py_ssize_t alias * Implement Sized for TypedPythonObject * Change unsafe_as_py_object_ptr() methods to take `borrowed self` (was `inout self`), for greater flexibility. MODULAR_ORIG_COMMIT_REV_ID: 91e4ba22d187d407911993fa2790de3f47b007d0 --- docs/changelog.md | 6 +++ stdlib/src/python/_cpython.mojo | 80 ++++++++++++++++++++++++---- stdlib/src/python/python.mojo | 30 +++++++++-- stdlib/src/python/python_object.mojo | 50 +++++++++++++++-- 4 files changed, 150 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 653814cdcb..60bf090dd1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -50,6 +50,12 @@ what we publish. values with static type information. This design will likely evolve and change significantly. + - Added `TypedPythonObject["Tuple].__getitem__` for accessing the elements of + a Python tuple. + +- Added `Python.unsafe_get_python_exception()`, as an efficient low-level + utility to get the Mojo `Error` equivalent of the current CPython error state. + - The `__type_of(x)` and `__lifetime_of(x)` operators are much more general now: they allow arbitrary expressions inside of them, allow referring to dynamic values in parameter contexts, and even allow referring to raising functions diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 43bba66415..e1acfeeea4 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -23,12 +23,73 @@ from memory import UnsafePointer from utils import StringRef +# ===-----------------------------------------------------------------------===# +# Bindings Utilities +# ===-----------------------------------------------------------------------===# + + +fn create_wrapper_function[ + user_func: fn (PythonObject, TypedPythonObject["Tuple"]) -> PythonObject +]() -> PyCFunction: + # > When a C function is called from Python, it borrows references to its + # > arguments from the caller. The caller owns a reference to the object, + # > so the borrowed reference’s lifetime is guaranteed until the function + # > returns. Only when such a borrowed reference must be stored or passed + # > on, it must be turned into an owned reference by calling Py_INCREF(). + # > + # > -- https://docs.python.org/3/extending/extending.html#ownership-rules + + fn wrapper(py_self_ptr: PyObjectPtr, args_ptr: PyObjectPtr) -> PyObjectPtr: + # SAFETY: + # Here we illegally (but carefully) construct _owned_ `PythonObject` + # values from the borrowed object reference arguments. We are careful + # down below to prevent the destructor for these objects from running + # so that we do not illegally decrement the reference count of these + # objects we do not own. + # + # This is valid to do, because these are passed using the `borrowed` + # argument convention to `user_func`, so logically they are treated + # as Python borrowed references. + var py_self = PythonObject(py_self_ptr) + var args = TypedPythonObject["Tuple"]( + unsafe_unchecked_from=PythonObject(args_ptr) + ) + + # SAFETY: + # Call the user provided function, and take ownership of the + # PyObjectPtr of the returned PythonObject. + var result = user_func(py_self, args).steal_data() + + # Do not destroy the provided PyObjectPtr arguments, since they + # actually have ownership of the underlying object. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(py_self) + ) + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(args)) + + return result + + return wrapper + + +# ===-----------------------------------------------------------------------===# +# Raw Bindings +# ===-----------------------------------------------------------------------===# + # https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/compile.h#L7 alias Py_single_input = 256 alias Py_file_input = 257 alias Py_eval_input = 258 alias Py_func_type_input = 345 +# TODO(MSTDL-892): Change this to alias ffi.C_ssize_t +alias Py_ssize_t = Int + +# Ref: https://docs.python.org/3/c-api/structures.html#c.PyCFunction +# TODO(MOCO-1138): +# This should be a C ABI function pointer, not a Mojo ABI function. +alias PyCFunction = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr + @value @register_passable("trivial") @@ -104,14 +165,6 @@ fn _py_finalize(lib: DLHandle): # Ref https://docs.python.org/3/c-api/structures.html#c.PyMethodDef @value struct PyMethodDef: - # ===-------------------------------------------------------------------===# - # Aliases - # ===-------------------------------------------------------------------===# - - # Ref: https://docs.python.org/3/c-api/structures.html#c.PyCFunction - # TODO(MOCO-1138): This is a C ABI function pointer, not Mojo a function. - alias _PyCFunction_type = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr - # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# @@ -120,7 +173,7 @@ struct PyMethodDef: # TODO(MSTDL-887): Support keyword-argument only methods # Pointer to the function to call - var method_impl: Self._PyCFunction_type + var method_impl: PyCFunction # Flags bits indicating how the call should be constructed. # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef for the various calling conventions @@ -139,7 +192,7 @@ struct PyMethodDef: This is suitable for use terminating an array of PyMethodDef values. """ self.method_name = UnsafePointer[c_char]() - self.method_impl = _null_fn_ptr[Self._PyCFunction_type]() + self.method_impl = _null_fn_ptr[PyCFunction]() self.method_flags = 0 self.method_docstring = UnsafePointer[c_char]() @@ -1069,6 +1122,13 @@ struct CPython: self._inc_total_rc() return r + fn PyTuple_GetItem( + inout self, tuple: PyObjectPtr, pos: Py_ssize_t + ) -> PyObjectPtr: + return self.lib.get_function[ + fn (PyObjectPtr, Py_ssize_t) -> PyObjectPtr + ]("PyTuple_GetItem")(tuple, pos) + fn PyTuple_SetItem( inout self, tuple_obj: PyObjectPtr, diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index e8422df2bb..d1a3d9a906 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -341,9 +341,33 @@ struct Python: cpython: The cpython instance we wish to error check. """ if cpython.PyErr_Occurred(): - var error: Error = str(PythonObject(cpython.PyErr_Fetch())) - cpython.PyErr_Clear() - raise error + raise Python.unsafe_get_python_exception(cpython) + + @staticmethod + fn unsafe_get_python_exception(inout cpython: CPython) -> Error: + """Get the `Error` object corresponding to the current CPython + interpreter error state. + + Safety: + The caller MUST be sure that the CPython interpreter is in an error + state before calling this function. + + This function will clear the CPython error. + + Args: + cpython: The cpython instance we wish to error check. + + Returns: + `Error` object describing the CPython error. + """ + debug_assert( + cpython.PyErr_Occurred(), + "invalid unchecked conversion of Python error to Mojo error", + ) + + var error: Error = str(PythonObject(cpython.PyErr_Fetch())) + cpython.PyErr_Clear() + return error @staticmethod fn is_type(x: PythonObject, y: PythonObject) -> Bool: diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index b05817733a..33806b02e7 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -114,7 +114,9 @@ struct _PyIter(Sized): @register_passable -struct TypedPythonObject[type_hint: StringLiteral]: +struct TypedPythonObject[type_hint: StringLiteral]( + SizedRaising, +): """A wrapper around `PythonObject` that indicates the type of the contained object. @@ -154,6 +156,18 @@ struct TypedPythonObject[type_hint: StringLiteral]: """ self._obj = other._obj + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + fn __len__(self) raises -> Int: + """Returns the length of the object. + + Returns: + The length of the object. + """ + return len(self._obj) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# @@ -161,7 +175,7 @@ struct TypedPythonObject[type_hint: StringLiteral]: # TODO: # This should have lifetime, or we should do this with a context # manager, to prevent use after ASAP destruction. - fn unsafe_as_py_object_ptr(inout self) -> PyObjectPtr: + fn unsafe_as_py_object_ptr(self) -> PyObjectPtr: """Get the underlying PyObject pointer. Returns: @@ -173,6 +187,36 @@ struct TypedPythonObject[type_hint: StringLiteral]: """ return self._obj.unsafe_as_py_object_ptr() + # ===-------------------------------------------------------------------===# + # 'Tuple' Operations + # ===-------------------------------------------------------------------===# + + fn __getitem__( + self: TypedPythonObject["Tuple"], + pos: Int, + ) raises -> PythonObject: + """Get an element from this tuple. + + Args: + pos: The tuple element position to retrieve. + + Returns: + The value of the tuple element at the specified position. + """ + var cpython = _get_global_python_itf().cpython() + + var item: PyObjectPtr = cpython.PyTuple_GetItem( + self.unsafe_as_py_object_ptr(), + pos, + ) + + if item.is_null(): + raise Python.unsafe_get_python_exception(cpython) + + # TODO(MSTDL-911): Avoid unnecessary owned reference counts when + # returning borrowed PythonObject values. + return PythonObject.from_borrowed_ptr(item) + @register_passable struct PythonObject( @@ -504,7 +548,7 @@ struct PythonObject( # Methods # ===-------------------------------------------------------------------===# - fn unsafe_as_py_object_ptr(inout self) -> PyObjectPtr: + fn unsafe_as_py_object_ptr(self) -> PyObjectPtr: """Get the underlying PyObject pointer. Returns: From 5e6e7e43c38ecc68d6a1d9a456be5962f89741c6 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 23 Sep 2024 09:20:18 -0700 Subject: [PATCH 1595/2019] [mojo] Randomly add more `capturing [_]` (NFC) MODULAR_ORIG_COMMIT_REV_ID: 2c528a032ea8b259c51515183d5aaf0982b75dd6 --- stdlib/src/builtin/sort.mojo | 54 ++++++++++++++++++------------------ stdlib/src/utils/loop.mojo | 16 +++++------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index b8b62c5d7f..c00a91a9d5 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -42,7 +42,7 @@ struct _SortWrapper[type: CollectionElement](CollectionElement): fn _insertion_sort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): """Sort the array[start:end] slice""" var array = span.unsafe_ptr() @@ -67,7 +67,7 @@ fn _insertion_sort[ fn _quicksort_partition_right[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]) -> Int: var array = span.unsafe_ptr() var size = len(span) @@ -96,7 +96,7 @@ fn _quicksort_partition_right[ fn _quicksort_partition_left[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]) -> Int: var array = span.unsafe_ptr() var size = len(span) @@ -122,7 +122,7 @@ fn _quicksort_partition_left[ fn _heap_sort_fix_down[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime], idx: Int): var array = span.unsafe_ptr() var size = len(span) @@ -143,7 +143,7 @@ fn _heap_sort_fix_down[ fn _heap_sort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var array = span.unsafe_ptr() var size = len(span) @@ -172,7 +172,7 @@ fn _estimate_initial_height(size: Int) -> Int: fn _delegate_small_sort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var array = span.unsafe_ptr() var size = len(span) @@ -204,7 +204,7 @@ fn _delegate_small_sort[ fn _quicksort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var array = span.unsafe_ptr() var size = len(span) @@ -266,7 +266,7 @@ fn merge[ type: CollectionElement, span_lifetime: ImmutableLifetime, result_lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ]( span1: Span[type, span_lifetime], span2: Span[type, span_lifetime], @@ -281,7 +281,7 @@ fn merge[ type: Type of the spans. span_lifetime: Lifetime of the input spans. result_lifetime: Lifetime of the result Span. - cmp_fn: Comparison functor of (type, type) capturing -> Bool type. + cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: span1: The first span to be merged. @@ -324,7 +324,7 @@ fn _stable_sort_impl[ type: CollectionElement, span_life: MutableLifetime, tmp_life: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, span_life], temp_buff: Span[type, tmp_life]): var size = len(span) if size <= 1: @@ -354,7 +354,7 @@ fn _stable_sort_impl[ fn _stable_sort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var temp_buff = UnsafePointer[type].alloc(len(span)) var temp_buff_span = Span[type, __lifetime_of(temp_buff)]( @@ -373,7 +373,7 @@ fn _stable_sort[ fn _partition[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]) -> Int: var size = len(span) if size <= 1: @@ -406,7 +406,7 @@ fn _partition[ fn _partition[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](owned span: Span[type, lifetime], owned k: Int): while True: var pivot = _partition[cmp_fn](span) @@ -424,7 +424,7 @@ fn _partition[ fn partition[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (type, type) capturing -> Bool, + cmp_fn: fn (type, type) capturing [_] -> Bool, ](span: Span[type, lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. @@ -433,7 +433,7 @@ fn partition[ Parameters: type: Type of the underlying data. lifetime: Lifetime of span. - cmp_fn: Comparison functor of (type, type) capturing -> Bool type. + cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: span: Input buffer. @@ -449,7 +449,7 @@ fn partition[ fn partition[ lifetime: MutableLifetime, //, - cmp_fn: fn (Int, Int) capturing -> Bool, + cmp_fn: fn (Int, Int) capturing [_] -> Bool, ](span: Span[Int, lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. @@ -457,7 +457,7 @@ fn partition[ Parameters: lifetime: Lifetime of span. - cmp_fn: Comparison functor of (type, type) capturing -> Bool type. + cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: span: Input buffer. @@ -474,7 +474,7 @@ fn partition[ fn partition[ type: DType, lifetime: MutableLifetime, //, - cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, + cmp_fn: fn (Scalar[type], Scalar[type]) capturing [_] -> Bool, ](span: Span[Scalar[type], lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. @@ -483,7 +483,7 @@ fn partition[ Parameters: type: DType of the underlying data. lifetime: Lifetime of span. - cmp_fn: Comparison functor of (type, type) capturing -> Bool type. + cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: span: Input buffer. @@ -508,7 +508,7 @@ fn partition[ fn _sort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, *, stable: Bool = False, ](span: Span[type, lifetime]): @@ -534,7 +534,7 @@ fn _sort[ fn sort[ type: CollectionElement, lifetime: MutableLifetime, //, - cmp_fn: fn (type, type) capturing -> Bool, + cmp_fn: fn (type, type) capturing [_] -> Bool, *, stable: Bool = False, ](span: Span[type, lifetime]): @@ -560,7 +560,7 @@ fn sort[ fn sort[ lifetime: MutableLifetime, //, - cmp_fn: fn (Int, Int) capturing -> Bool, + cmp_fn: fn (Int, Int) capturing [_] -> Bool, *, stable: Bool = False, ](span: Span[Int, lifetime]): @@ -586,7 +586,7 @@ fn sort[ fn sort[ type: DType, lifetime: MutableLifetime, //, - cmp_fn: fn (Scalar[type], Scalar[type]) capturing -> Bool, + cmp_fn: fn (Scalar[type], Scalar[type]) capturing [_] -> Bool, *, stable: Bool = False, ](span: Span[Scalar[type], lifetime]): @@ -692,7 +692,7 @@ fn sort[ @always_inline fn _sort2[ type: CollectionElement, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](array: UnsafePointer[type], offset0: Int, offset1: Int): var a = array[offset0] var b = array[offset1] @@ -704,7 +704,7 @@ fn _sort2[ @always_inline fn _sort3[ type: CollectionElement, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](array: UnsafePointer[type], offset0: Int, offset1: Int, offset2: Int): _sort2[type, cmp_fn](array, offset0, offset1) _sort2[type, cmp_fn](array, offset1, offset2) @@ -714,7 +714,7 @@ fn _sort3[ @always_inline fn _sort_partial_3[ type: CollectionElement, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](array: UnsafePointer[type], offset0: Int, offset1: Int, offset2: Int): var a = array[offset0] var b = array[offset1] @@ -734,7 +734,7 @@ fn _sort_partial_3[ fn _small_sort[ n: Int, type: CollectionElement, - cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](array: UnsafePointer[type]): @parameter if n == 2: diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index d7f0251d53..efef81a73f 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -30,7 +30,7 @@ from utils import unroll @always_inline fn unroll[ - func: fn[idx0: Int, idx1: Int] () capturing -> None, + func: fn[idx0: Int, idx1: Int] () capturing [_] -> None, dim0: Int, dim1: Int, ](): @@ -58,7 +58,7 @@ fn unroll[ @always_inline fn unroll[ - func: fn[idx0: Int, idx1: Int, idx2: Int] () capturing -> None, + func: fn[idx0: Int, idx1: Int, idx2: Int] () capturing [_] -> None, dim0: Int, dim1: Int, dim2: Int, @@ -91,7 +91,7 @@ fn unroll[ @always_inline fn unroll[ - func: fn[idx: Int] () capturing -> None, + func: fn[idx: Int] () capturing [_] -> None, zero_starting_range: _ZeroStartingRange, ](): """Repeatedly evaluates a function `range` times. @@ -109,7 +109,7 @@ fn unroll[ @always_inline fn unroll[ - func: fn[idx: Int] () raises capturing -> None, + func: fn[idx: Int] () raises capturing [_] -> None, zero_starting_range: _ZeroStartingRange, ]() raises: """Repeatedly evaluates a function `range` times. @@ -130,7 +130,7 @@ fn unroll[ # ===----------------------------------------------------------------------===# @always_inline fn unroll[ - func: fn[idx: Int] () capturing -> None, + func: fn[idx: Int] () capturing [_] -> None, sequential_range: _SequentialRange, ](): """Repeatedly evaluates a function `range` times. @@ -148,7 +148,7 @@ fn unroll[ @always_inline fn unroll[ - func: fn[idx: Int] () raises capturing -> None, + func: fn[idx: Int] () raises capturing [_] -> None, sequential_range: _SequentialRange, ]() raises: """Repeatedly evaluates a function `range` times. @@ -169,7 +169,7 @@ fn unroll[ # ===----------------------------------------------------------------------===# @always_inline fn unroll[ - func: fn[idx: Int] () capturing -> None, + func: fn[idx: Int] () capturing [_] -> None, strided_range: _StridedRange, ](): """Repeatedly evaluates a function `range` times. @@ -187,7 +187,7 @@ fn unroll[ @always_inline fn unroll[ - func: fn[idx: Int] () raises capturing -> None, + func: fn[idx: Int] () raises capturing [_] -> None, strided_range: _StridedRange, ]() raises: """Repeatedly evaluates a function `range` times. From 552ed0d907b83016b1b2c65a559490e1d79ac293 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 23 Sep 2024 11:44:02 -0600 Subject: [PATCH 1596/2019] [mojo-stdlib] Improve `StringSlice.__iter__` lifetimes The iter formed over a string slice references the data being sliced, not the `StringSlice` itself. MODULAR_ORIG_COMMIT_REV_ID: a7175ce2896d40b5e0e993b16789876845b96e61 --- stdlib/src/utils/string_slice.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 1eb89d6a35..9fd2841877 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -425,25 +425,25 @@ struct StringSlice[ """ return not self == rhs - fn __iter__(ref [_]self) -> _StringSliceIter[__lifetime_of(self)]: + fn __iter__(ref [_]self) -> _StringSliceIter[lifetime]: """Iterate over elements of the string slice, returning immutable references. Returns: An iterator of references to the string elements. """ - return _StringSliceIter[__lifetime_of(self)]( + return _StringSliceIter[lifetime]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) fn __reversed__( ref [_]self, - ) -> _StringSliceIter[__lifetime_of(self), False]: + ) -> _StringSliceIter[lifetime, False]: """Iterate backwards over the string, returning immutable references. Returns: A reversed iterator of references to the string elements. """ - return _StringSliceIter[__lifetime_of(self), forward=False]( + return _StringSliceIter[lifetime, forward=False]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) From 755449605d5fe1d340d1f8e45eaad9d26d946ec3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 23 Sep 2024 12:17:32 -0600 Subject: [PATCH 1597/2019] [mojo-stdlib] Rename the `AnyLifetime` type to `Lifetime` This helper type (which I hope will eventually go away or be significantly changed) is used to construct a `!lit.lifetime` type, not a `#lit.any.lifetime` value. Remove the `Any` to avoid confusion. MODULAR_ORIG_COMMIT_REV_ID: 62a44b9a0adc8ef1bb170c322e197f06c52f333f --- docs/changelog.md | 3 +++ stdlib/src/builtin/builtin_list.mojo | 14 +++++++------- stdlib/src/builtin/reversed.mojo | 4 ++-- stdlib/src/builtin/type_aliases.mojo | 2 +- stdlib/src/collections/dict.mojo | 6 +++--- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/reference.mojo | 2 +- stdlib/src/prelude/__init__.mojo | 2 +- stdlib/src/utils/span.mojo | 4 ++-- stdlib/src/utils/string_slice.mojo | 4 ++-- 11 files changed, 24 insertions(+), 21 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 60bf090dd1..0b26ab920f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -99,6 +99,9 @@ what we publish. specifies to the compiler that the resultant pointer is a distinct identifiable object that does not alias any other memory in the local scope. +- The `AnyLifetime` type (useful for declaring lifetime types as parameters) has + been renamed to `Lifetime`. + - Restore implicit copyability of `Tuple` and `ListLiteral`. - The aliases for C FFI have been renamed: `C_int` -> `c_int`, `C_long` -> `c_long` diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index c9931caabe..4e8d67809a 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -224,7 +224,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): struct _VariadicListMemIter[ elt_is_mutable: Bool, //, elt_type: AnyType, - elt_lifetime: AnyLifetime[elt_is_mutable].type, + elt_lifetime: Lifetime[elt_is_mutable].type, list_lifetime: ImmutableLifetime, ]: """Iterator for VariadicListMem. @@ -256,8 +256,8 @@ struct _VariadicListMemIter[ # TODO: parametric aliases would be nice. struct _lit_lifetime_union[ is_mutable: Bool, //, - a: AnyLifetime[is_mutable].type, - b: AnyLifetime[is_mutable].type, + a: Lifetime[is_mutable].type, + b: Lifetime[is_mutable].type, ]: alias result = __mlir_attr[ `#lit.lifetime.union<`, @@ -272,7 +272,7 @@ struct _lit_lifetime_union[ struct _lit_mut_cast[ is_mutable: Bool, //, - operand: AnyLifetime[is_mutable].type, + operand: Lifetime[is_mutable].type, result_mutable: Bool, ]: alias result = __mlir_attr[ @@ -287,7 +287,7 @@ struct _lit_mut_cast[ struct VariadicListMem[ elt_is_mutable: Bool, //, element_type: AnyType, - lifetime: AnyLifetime[elt_is_mutable].type, + lifetime: Lifetime[elt_is_mutable].type, ](Sized): """A utility class to access variadic function arguments of memory-only types that may have ownership. It exposes references to the elements in a @@ -470,7 +470,7 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @value struct _LITRefPackHelper[ is_mutable: Bool, //, - lifetime: AnyLifetime[is_mutable].type, + lifetime: Lifetime[is_mutable].type, address_space: __mlir_type.index, element_trait: _AnyTypeMetaType, *element_types: element_trait, @@ -540,7 +540,7 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, //, - lifetime: AnyLifetime[Bool {value: elt_is_mutable}].type, + lifetime: Lifetime[Bool {value: elt_is_mutable}].type, element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 83c496c0d4..3517726d9e 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -122,7 +122,7 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_lifetime: AnyLifetime[dict_mutability].type, + dict_lifetime: Lifetime[dict_mutability].type, ](ref [_]value: _DictValueIter[K, V, dict_lifetime]) -> _DictValueIter[ K, V, dict_lifetime, False ]: @@ -149,7 +149,7 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_lifetime: AnyLifetime[dict_mutability].type, + dict_lifetime: Lifetime[dict_mutability].type, ](ref [_]value: _DictEntryIter[K, V, dict_lifetime]) -> _DictEntryIter[ K, V, dict_lifetime, False ]: diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index c008f2c202..fbd3728bce 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -43,7 +43,7 @@ alias LifetimeSet = __mlir_type.`!lit.lifetime.set` # Helper to build a value of !lit.lifetime type. # TODO: Should be a parametric alias. -struct AnyLifetime[is_mutable: Bool]: +struct Lifetime[is_mutable: Bool]: """This represents a lifetime reference of potentially parametric type. TODO: This should be replaced with a parametric type alias. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 2e7d34ae53..86bdce4042 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -59,7 +59,7 @@ struct _DictEntryIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: AnyLifetime[dict_mutability].type, + dict_lifetime: Lifetime[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable DictEntry references. @@ -107,7 +107,7 @@ struct _DictKeyIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: AnyLifetime[dict_mutability].type, + dict_lifetime: Lifetime[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable Dict key references. @@ -141,7 +141,7 @@ struct _DictValueIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: AnyLifetime[dict_mutability].type, + dict_lifetime: Lifetime[dict_mutability].type, forward: Bool = True, ]: """Iterator over Dict value references. These are mutable if the dict diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index a273d215d6..366ce0bba8 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -31,7 +31,7 @@ struct _InlineListIter[ list_mutability: Bool, //, T: CollectionElementNew, capacity: Int, - list_lifetime: AnyLifetime[list_mutability].type, + list_lifetime: Lifetime[list_mutability].type, forward: Bool = True, ]: """Iterator for InlineList. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index af3ba54e5f..9cbd312e17 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -38,7 +38,7 @@ struct _ListIter[ list_mutability: Bool, //, T: CollectionElement, hint_trivial_type: Bool, - list_lifetime: AnyLifetime[list_mutability].type, + list_lifetime: Lifetime[list_mutability].type, forward: Bool = True, ]: """Iterator for List. diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 2c19f6caa2..e1bc50c50c 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -270,7 +270,7 @@ struct AddressSpace(EqualityComparable): struct Reference[ is_mutable: Bool, //, type: AnyType, - lifetime: AnyLifetime[is_mutable].type, + lifetime: Lifetime[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, ](CollectionElementNew, Stringable): """Defines a non-nullable safe reference. diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index d07db976dc..72c21577be 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -83,7 +83,7 @@ from builtin.type_aliases import ( ImmutableStaticLifetime, MutableStaticLifetime, LifetimeSet, - AnyLifetime, + Lifetime, ) from builtin.uint import UInt from builtin.value import ( diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 51d377bf6a..2534df4323 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -30,7 +30,7 @@ from builtin.builtin_list import _lit_mut_cast struct _SpanIter[ is_mutable: Bool, //, T: CollectionElement, - lifetime: AnyLifetime[is_mutable].type, + lifetime: Lifetime[is_mutable].type, forward: Bool = True, ]: """Iterator for Span. @@ -74,7 +74,7 @@ struct _SpanIter[ struct Span[ is_mutable: Bool, //, T: CollectionElement, - lifetime: AnyLifetime[is_mutable].type, + lifetime: Lifetime[is_mutable].type, ](CollectionElementNew): """A non owning view of contiguous data. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 9fd2841877..c4f23315eb 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -122,7 +122,7 @@ fn _is_newline_start( @value struct _StringSliceIter[ is_mutable: Bool, //, - lifetime: AnyLifetime[is_mutable].type, + lifetime: Lifetime[is_mutable].type, forward: Bool = True, ]: """Iterator for StringSlice @@ -191,7 +191,7 @@ struct _StringSliceIter[ struct StringSlice[ is_mutable: Bool, //, - lifetime: AnyLifetime[is_mutable].type, + lifetime: Lifetime[is_mutable].type, ](Stringable, Sized, Formattable): """ A non-owning view to encoded string data. From ec12cdbecf6f6f2f4e2b2e7bef1301aab482ea7c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 23 Sep 2024 12:29:22 -0600 Subject: [PATCH 1598/2019] [mojo-stdlib] Simplify StringLiteral, rename as_bytes_slice -> as_bytes_span This renames `as_byte_slice` to `as_byte_span` since it returns a `Span`, not a `StringSlice`. This reuses an existing `String` constructor rather that duplicating it. MODULAR_ORIG_COMMIT_REV_ID: 9c5ea758b582b004c3739cc41f6dc7cb38af58a4 --- docs/changelog.md | 3 +++ stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 22 ++++++++++++---------- stdlib/src/collections/string.mojo | 10 +++++----- stdlib/src/utils/inline_string.mojo | 8 ++++---- stdlib/src/utils/string_slice.mojo | 6 +++--- stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 12 ++++++------ 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0b26ab920f..2ae9c011a9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -109,6 +109,9 @@ what we publish. - The VS Code extension now allows selecting a default SDK when multiple are available. +- `String.as_bytes_slice()` is renamed to `String.as_bytes_span()` since it + returns a `Span` and not a `StringSlice`. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d484a9d99b..0ae1b97485 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1389,7 +1389,7 @@ struct SIMD[type: DType, size: Int]( @parameter if size > 1: # TODO: Fix when slice indexing is implemented on StringSlice - values = StringSlice(unsafe_from_utf8=output.as_bytes_slice()[1:-1]) + values = StringSlice(unsafe_from_utf8=output.as_bytes_span()[1:-1]) return ( "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 4bedfb78c2..ae1ccff848 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -19,7 +19,7 @@ from sys.ffi import c_char from memory import memcpy, UnsafePointer from collections import List -from utils import StringRef, Span, StringSlice +from utils import StringRef, Span, StringSlice, StaticString from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type @@ -232,6 +232,10 @@ struct StringLiteral( Returns: A new string. """ + # TODO(MOCO-1224): We should be able to reuse this, but we have to + # inline the string slice constructor to work around an elaborator + # memory leak. + # return self.as_string_slice() var string = String() var length = self.byte_length() var buffer = String._buffer_type() @@ -338,33 +342,31 @@ struct StringLiteral( return self.unsafe_ptr().bitcast[c_char]() @always_inline - fn as_string_slice(self) -> StringSlice[ImmutableAnyLifetime]: + fn as_string_slice(self) -> StaticString: """Returns a string slice of this static string literal. Returns: A string slice pointing to this static string literal. """ - var bytes = self.as_bytes_slice() - # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually # guaranteed to be valid. - return StringSlice[ImmutableAnyLifetime](unsafe_from_utf8=bytes) + return StaticString( + unsafe_from_utf8_ptr=self.unsafe_ptr(), len=self.byte_length() + ) @always_inline - fn as_bytes_slice(self) -> Span[UInt8, ImmutableAnyLifetime]: + fn as_bytes_span(self) -> Span[UInt8, ImmutableAnyLifetime]: """ - Returns a contiguous slice of the bytes owned by this string. + Returns a contiguous Span of the bytes owned by this string. Returns: A contiguous slice pointing to the bytes owned by this string. """ - var ptr = self.unsafe_ptr() - return Span[UInt8, ImmutableAnyLifetime]( - unsafe_ptr=ptr, + unsafe_ptr=self.unsafe_ptr(), len=self.byte_length(), ) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 2528b9a79a..ac165fbc38 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -790,13 +790,13 @@ struct String( """ # Calculate length in bytes - var length: Int = len(str_slice.as_bytes_slice()) + var length: Int = len(str_slice.as_bytes_span()) var buffer = Self._buffer_type() # +1 for null terminator, initialized to 0 buffer.resize(length + 1, 0) memcpy( dest=buffer.data, - src=str_slice.as_bytes_slice().unsafe_ptr(), + src=str_slice.as_bytes_span().unsafe_ptr(), count=length, ) self = Self(buffer^) @@ -1380,7 +1380,7 @@ struct String( return copy @always_inline - fn as_bytes_slice(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes_span(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: @@ -1405,7 +1405,7 @@ struct String( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in String so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) + return StringSlice(unsafe_from_utf8=self.as_bytes_span()) @always_inline fn byte_length(self) -> Int: @@ -2199,7 +2199,7 @@ struct String( debug_assert( len(fillchar) == 1, "fill char needs to be a one byte literal" ) - var fillbyte = fillchar.as_bytes_slice()[0] + var fillbyte = fillchar.as_bytes_span()[0] var buffer = Self._buffer_type(capacity=width + 1) buffer.resize(width, fillbyte) buffer.append(0) diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 032eed819d..2fa4f6d972 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -278,10 +278,10 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): # FIXME(MSTDL-160): # Enforce UTF-8 encoding in _FixedString so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) + return StringSlice(unsafe_from_utf8=self.as_bytes_span()) @always_inline - fn as_bytes_slice(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes_span(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -517,10 +517,10 @@ struct _FixedString[CAP: Int]( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in _FixedString so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self.as_bytes_slice()) + return StringSlice(unsafe_from_utf8=self.as_bytes_span()) @always_inline - fn as_bytes_slice(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes_span(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index c4f23315eb..f5abcf9da4 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -452,7 +452,7 @@ struct StringSlice[ # ===------------------------------------------------------------------===# @always_inline - fn as_bytes_slice(self) -> Span[UInt8, lifetime]: + fn as_bytes_span(self) -> Span[UInt8, lifetime]: """Get the sequence of encoded bytes as a slice of the underlying string. Returns: @@ -478,7 +478,7 @@ struct StringSlice[ The length of this string slice in bytes. """ - return len(self.as_bytes_slice()) + return len(self.as_bytes_span()) @always_inline @deprecated("use byte_length() instead") @@ -489,7 +489,7 @@ struct StringSlice[ The length of this string slice in bytes. """ - return len(self.as_bytes_slice()) + return len(self.as_bytes_span()) fn _strref_dangerous(self) -> StringRef: """Returns an inner pointer to the string as a StringRef. diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index bec5e69800..0abe92062d 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -186,7 +186,7 @@ def test_file_write_span(): var content: String = "The quick brown fox jumps over the lazy dog" var TEMP_FILE = Path(gettempdir().value()) / "test_file_write_span" with open(TEMP_FILE, "w") as f: - f.write(content.as_bytes_slice()) + f.write(content.as_bytes_span()) with open(TEMP_FILE, "r") as read_file: assert_equal(read_file.read(), content) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 6f94838f1e..97c3b17b95 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -18,9 +18,9 @@ from utils import Span, StringSlice from utils._utf8_validation import _is_valid_utf8 -fn test_string_literal_byte_slice() raises: +fn test_string_literal_byte_span() raises: alias string: StringLiteral = "Hello" - alias slc = string.as_bytes_slice() + alias slc = string.as_bytes_span() assert_equal(len(slc), 5) assert_equal(slc[0], ord("H")) @@ -30,9 +30,9 @@ fn test_string_literal_byte_slice() raises: assert_equal(slc[4], ord("o")) -fn test_string_byte_slice() raises: +fn test_string_byte_span() raises: var string = String("Hello") - var str_slice = string.as_bytes_slice() + var str_slice = string.as_bytes_span() assert_equal(len(str_slice), 5) assert_equal(str_slice[0], ord("H")) @@ -386,8 +386,8 @@ def test_combination_10_good_10_bad_utf8_sequences(): fn main() raises: - test_string_literal_byte_slice() - test_string_byte_slice() + test_string_literal_byte_span() + test_string_byte_span() test_heap_string_from_string_slice() test_slice_len() test_slice_eq() From 6155c229fd2f4f757e933643a53fdbf988aa8e96 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 23 Sep 2024 13:21:17 -0600 Subject: [PATCH 1599/2019] [mojo-lang] Put `StringLiteral` into a new global lifetime. This introduces compiler support for a new `#lit.static.lifetime` lifetime to model things that have static storage duration, e.g. string literals and global variables. This adopts it in the standard library in the `StringLiteral` type (and also cleans up some other stuff w.r.t. it), replacing the use of `AnyLifetime` which could point to anything. This is better for alias analysis and subtyping in general, but is important because a subsequent patch will change the behavior of `AnyLifetime` and I don't want every use of `print` to be impacted by it :) MODULAR_ORIG_COMMIT_REV_ID: a1a072ee2e77b827bda304055616bbdbcba4b2a1 --- stdlib/src/builtin/string_literal.mojo | 8 ++++---- stdlib/src/builtin/type_aliases.mojo | 13 +++++++------ stdlib/src/prelude/__init__.mojo | 3 +-- stdlib/src/utils/string_slice.mojo | 6 +++--- stdlib/test/utils/test_string_slice.mojo | 14 ++++++-------- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index ae1ccff848..9252014f25 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -277,13 +277,13 @@ struct StringLiteral( """ return self.__str__() - fn __iter__(ref [_]self) -> _StringSliceIter[__lifetime_of(self)]: + fn __iter__(ref [_]self) -> _StringSliceIter[StaticConstantLifetime]: """Return an iterator over the string literal. Returns: An iterator over the string. """ - return _StringSliceIter[__lifetime_of(self)]( + return _StringSliceIter[StaticConstantLifetime]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) @@ -357,7 +357,7 @@ struct StringLiteral( ) @always_inline - fn as_bytes_span(self) -> Span[UInt8, ImmutableAnyLifetime]: + fn as_bytes_span(self) -> Span[UInt8, StaticConstantLifetime]: """ Returns a contiguous Span of the bytes owned by this string. @@ -365,7 +365,7 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, ImmutableAnyLifetime]( + return Span[UInt8, StaticConstantLifetime]( unsafe_ptr=self.unsafe_ptr(), len=self.byte_length(), ) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index fbd3728bce..80041ab90e 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -30,12 +30,13 @@ alias ImmutableAnyLifetime = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<0>` alias MutableAnyLifetime = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<1>` """The mutable lifetime that might access any memory value.""" -# TODO: We don't have a "static" lifetime to use yet, so we use Any. -alias ImmutableStaticLifetime = ImmutableAnyLifetime -"""The immutable lifetime that lasts for the entire duration of program execution.""" - -alias MutableStaticLifetime = MutableAnyLifetime -"""The mutable lifetime that lasts for the entire duration of program execution.""" +# Static constants are a named subset of the global lifetime. +alias StaticConstantLifetime = __mlir_attr[ + `#lit.lifetime.field<`, + `#lit.static.lifetime : !lit.lifetime<0>`, + `, "__constants__"> : !lit.lifetime<0>`, +] +"""A lifetime for strings and other always-immutable static constants.""" alias LifetimeSet = __mlir_type.`!lit.lifetime.set` """A set of lifetime parameters.""" diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 72c21577be..53c09d50fb 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -80,8 +80,7 @@ from builtin.type_aliases import ( MutableLifetime, ImmutableAnyLifetime, MutableAnyLifetime, - ImmutableStaticLifetime, - MutableStaticLifetime, + StaticConstantLifetime, LifetimeSet, Lifetime, ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index f5abcf9da4..1904633013 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -27,7 +27,7 @@ from collections import List from memory import memcmp, UnsafePointer from sys import simdwidthof, bitwidthof -alias StaticString = StringSlice[ImmutableStaticLifetime] +alias StaticString = StringSlice[StaticConstantLifetime] """An immutable static string slice.""" @@ -212,7 +212,7 @@ struct StringSlice[ @always_inline fn __init__( - inout self: StringSlice[ImmutableAnyLifetime], lit: StringLiteral + inout self: StringSlice[StaticConstantLifetime], lit: StringLiteral ): """Construct a new string slice from a string literal. @@ -231,7 +231,7 @@ struct StringSlice[ # _is_valid_utf8(literal.unsafe_ptr(), literal._byte_length()), # "StringLiteral doesn't have valid UTF-8 encoding", # ) - self = StringSlice[ImmutableStaticLifetime]( + self = StaticString( unsafe_from_utf8_ptr=lit.unsafe_ptr(), len=lit.byte_length() ) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 97c3b17b95..f77e57170b 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -117,9 +117,7 @@ fn test_string_byte_span() raises: fn test_heap_string_from_string_slice() raises: alias string_lit: StringLiteral = "Hello" - alias static_str: StringSlice[ - ImmutableAnyLifetime - ] = string_lit.as_string_slice() + alias static_str = string_lit.as_string_slice() alias heap_string = String(static_str) @@ -133,11 +131,11 @@ fn test_slice_len() raises: alias str4: StringLiteral = "12" alias str5: StringLiteral = "1" - alias slice1: StringSlice[ImmutableAnyLifetime] = str1.as_string_slice() - alias slice2: StringSlice[ImmutableAnyLifetime] = str2.as_string_slice() - alias slice3: StringSlice[ImmutableAnyLifetime] = str3.as_string_slice() - alias slice4: StringSlice[ImmutableAnyLifetime] = str4.as_string_slice() - alias slice5: StringSlice[ImmutableAnyLifetime] = str5.as_string_slice() + alias slice1 = str1.as_string_slice() + alias slice2 = str2.as_string_slice() + alias slice3 = str3.as_string_slice() + alias slice4 = str4.as_string_slice() + alias slice5 = str5.as_string_slice() assert_equal(5, len(slice1)) assert_equal(4, len(slice2)) From 9d9a347c798ef6b92d43274819aae919f349fa59 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 23 Sep 2024 12:31:32 -0700 Subject: [PATCH 1600/2019] [All] Add more `[_]` (NFC) MODULAR_ORIG_COMMIT_REV_ID: 345360ed07441e551e021488877b383a9e5ed663 --- stdlib/benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/builtin/debug_assert.mojo | 2 +- stdlib/src/builtin/dtype.mojo | 10 +++++----- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/time/time.mojo | 4 ++-- stdlib/src/utils/_serialize.mojo | 8 ++++---- stdlib/test/builtin/test_sort.mojo | 12 +++++++----- 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 02c33bdbd6..889cebba89 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -194,7 +194,7 @@ fn test_vectorize[ # TODO: move this function to a common module for benchmarking. @always_inline fn unroll_nested_call[ - func: fn[List[Int]] () raises capturing -> None, + func: fn[List[Int]] () raises capturing [_] -> None, count: List[Int], # TODO: a better datatype to use? e.g., Dim? loop_idx: Int = 0, index_prev: List[Int] = List[Int](), diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 469672616e..8eec66fd9e 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -33,7 +33,7 @@ alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() @always_inline fn debug_assert[ - func: fn () capturing -> Bool, message_type: Stringable + func: fn () capturing [_] -> Bool, message_type: Stringable ](message: message_type): """Asserts that the condition is true. diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 3f2b086bf4..b1e3f7e6c1 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -437,7 +437,7 @@ struct DType( @always_inline fn dispatch_integral[ - func: fn[type: DType] () capturing -> None + func: fn[type: DType] () capturing [_] -> None ](self) raises: """Dispatches an integral function corresponding to the current DType. @@ -474,7 +474,7 @@ struct DType( @always_inline fn dispatch_floating[ - func: fn[type: DType] () capturing -> None + func: fn[type: DType] () capturing [_] -> None ](self) raises: """Dispatches a floating-point function corresponding to the current DType. @@ -498,7 +498,7 @@ struct DType( @always_inline fn _dispatch_bitwidth[ - func: fn[type: DType] () capturing -> None, + func: fn[type: DType] () capturing [_] -> None, ](self) raises: """Dispatches a function corresponding to the current DType's bitwidth. This should only be used if func only depends on the bitwidth of the dtype, @@ -525,7 +525,7 @@ struct DType( @always_inline fn _dispatch_custom[ - func: fn[type: DType] () capturing -> None, *dtypes: DType + func: fn[type: DType] () capturing [_] -> None, *dtypes: DType ](self) raises: """Dispatches a function corresponding to current DType if it matches any type in the dtypes parameter. @@ -552,7 +552,7 @@ struct DType( @always_inline fn dispatch_arithmetic[ - func: fn[type: DType] () capturing -> None + func: fn[type: DType] () capturing [_] -> None ](self) raises: """Dispatches a function corresponding to the current DType. diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 99fdfd9c98..04299a556b 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -360,7 +360,7 @@ fn _try_write_int[ var remaining_int = value @parameter - fn process_digits[get_digit_value: fn () capturing -> Scalar[type]](): + fn process_digits[get_digit_value: fn () capturing [_] -> Scalar[type]](): while remaining_int: var digit_value = get_digit_value() diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 101fc305d7..1f6d6b4a41 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -235,7 +235,7 @@ fn now() -> Int: @always_inline @parameter -fn _time_function_windows[func: fn () capturing -> None]() -> Int: +fn _time_function_windows[func: fn () capturing [_] -> None]() -> Int: """Calculates elapsed time in Windows""" var ticks_per_sec: _WINDOWS_LARGE_INTEGER = 0 @@ -266,7 +266,7 @@ fn _time_function_windows[func: fn () capturing -> None]() -> Int: @always_inline @parameter -fn time_function[func: fn () capturing -> None]() -> Int: +fn time_function[func: fn () capturing [_] -> None]() -> Int: """Measures the time spent in the function. Parameters: diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 136de48ad8..1b7e23be07 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -24,7 +24,7 @@ alias _kCompactElemPerSide = _kCompactMaxElemsToPrint // 2 fn _serialize_elements_compact[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, ](ptr: UnsafePointer[Scalar[type], _], len: Int): serialize_fn(_kStartTensorMarker) if len < _kCompactMaxElemsToPrint: @@ -45,7 +45,7 @@ fn _serialize_elements_compact[ fn _serialize_elements_complete[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, ](ptr: UnsafePointer[Scalar[type], _], len: Int): if len == 0: return @@ -57,7 +57,7 @@ fn _serialize_elements_complete[ fn _serialize_elements[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, compact: Bool = False, ](ptr: UnsafePointer[Scalar[type], _], len: Int): @parameter @@ -69,7 +69,7 @@ fn _serialize_elements[ fn _serialize[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing -> None, + serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, serialize_dtype: Bool = True, serialize_shape: Bool = True, serialize_end_line: Bool = True, diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index d4a2962a35..987dbda3ec 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -167,7 +167,7 @@ fn test_sort3_dupe_elements() raises: alias length = 3 fn test[ - cmp_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing [_] -> Bool, ]() raises: var list = List[Int](capacity=3) list.append(5) @@ -453,8 +453,10 @@ fn test_sort_stress() raises: @__copy_capture(random_seed) @parameter fn test[ - cmp_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing -> Bool, - check_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing -> Bool, + cmp_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing [_] -> Bool, + check_fn: fn (_SortWrapper[Int], _SortWrapper[Int]) capturing [ + _ + ] -> Bool, ](length: Int) raises: var list = List[Int](capacity=length) for _ in range(length): @@ -619,8 +621,8 @@ def test_stable_sort_stress(): @parameter fn test[ - cmp_fn: fn (IntPair, IntPair) capturing -> Bool, - check_fn: fn (IntPair, IntPair) capturing -> Bool, + cmp_fn: fn (IntPair, IntPair) capturing [_] -> Bool, + check_fn: fn (IntPair, IntPair) capturing [_] -> Bool, ](length: Int) raises: var list = List[IntPair](capacity=length) for i in range(length): From 5a7ad9cf4a1d13f2e7e2e6b3b7f6fdbaf9d0ddb9 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 23 Sep 2024 12:31:42 -0700 Subject: [PATCH 1601/2019] [mojo] Update changelog with `[_]` for capturing closures MODULAR_ORIG_COMMIT_REV_ID: e57c86b8c37d69bfa015310716babaacd696b608 --- docs/changelog.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 2ae9c011a9..29abad8a53 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -86,6 +86,30 @@ what we publish. pass ``` +- Function types now accept a lifetime set parameter. This parameter represents + the lifetimes of values captured by a parameter closure. The compiler + automatically tags parameter closures with the right set of lifetimes. This + enables lifetimes and parameter closures to correctly compose. + + ```mojo + fn call_it[f: fn() capturing [_] -> None](): + f() + + fn test(): + var msg = String("hello world") + + @parameter + fn say_hi(): + print(msg) + + call_it[say_hi]() + # no longer need to write `_ = msg^`!! + ``` + + Note that this only works for higher-order functions which have explicitly + added `[_]` as the capture lifetimes. By default, the compiler still assumes + a `capturing` closure does not reference any lifetimes. This will soon change. + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` From 3ec0c59459be1a22b9ceb819f0d3a5838b15e5cb Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 23 Sep 2024 18:48:34 -0400 Subject: [PATCH 1602/2019] [External] [stdlib] Update the error msg to instruct using magic when compiler mismatches (#47671) [External] [stdlib] Update the error msg to instruct using magic when compiler mismatches It's still pointing to the deprecated modular CLI. Update to point at using `modular`. Co-authored-by: Manuel Saelices Closes modularml/mojo#3516 MODULAR_ORIG_COMMIT_REV_ID: 05e45509b554117ebc97024bdd26cf9df7c4df96 --- stdlib/scripts/build-stdlib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/scripts/build-stdlib.sh b/stdlib/scripts/build-stdlib.sh index 8389f2d307..f154ecaae3 100755 --- a/stdlib/scripts/build-stdlib.sh +++ b/stdlib/scripts/build-stdlib.sh @@ -27,7 +27,7 @@ if [ -z "${MOJO_OVERRIDE_COMPILER_VERSION_CHECK:-}" ]; then echo "Mismatch in compiler versions! Cannot build the standard library." echo "Expected compiler version: ${EXPECTED_COMPILER_VERSION}" echo "Current installed compiler version: ${ACTUAL_COMPILER_VERSION}" - echo "Please run modular update nightly/mojo to get the latest compiler." + echo "Please run \`magic update && magic install\` to get the latest compiler." exit 1 fi fi From 26b9bf49f68223a9393a9bc4ae38a770e78099e1 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Mon, 23 Sep 2024 22:41:28 -0400 Subject: [PATCH 1603/2019] [vscode] Add a way to control where to focus after mojo run MODULAR_ORIG_COMMIT_REV_ID: 90ff78a5a8d6e65ed481b2d6c9dae7ca2e4f2c6b --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 29abad8a53..c115172e6c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -110,6 +110,11 @@ what we publish. added `[_]` as the capture lifetimes. By default, the compiler still assumes a `capturing` closure does not reference any lifetimes. This will soon change. +- The VS Code extension now has the `mojo.run.focusOnTerminalAfterLaunch` + setting, which controls whether to focus on the terminal used by the + `Mojo: Run Mojo File` command or on the editor after launch. + [Issue #3532](https://github.com/modularml/mojo/issues/3532). + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` From 0929ddac9f3b7654aa8b00269dd5f5583fd2f511 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Mon, 23 Sep 2024 23:01:53 -0400 Subject: [PATCH 1604/2019] [vscode] Add a setting to support a custom modular root MODULAR_ORIG_COMMIT_REV_ID: 27abbb0b00b5d3703d926786aaa36d28678d8aa2 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c115172e6c..ef4a0413f8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -115,6 +115,11 @@ what we publish. `Mojo: Run Mojo File` command or on the editor after launch. [Issue #3532](https://github.com/modularml/mojo/issues/3532). +- The VS Code extension now has the `mojo.SDK.additionalSDKs` setting, which + allows the user to provide a list of MAX SDKs that the extension can use when + determining a default SDK to use. The user can select the default SDK to use + with the `Mojo: Select the default MAX SDK` command. + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` From 237b14ff44cb9906799e9c583f0b1617289be745 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 24 Sep 2024 09:57:25 -0600 Subject: [PATCH 1605/2019] [mojo-stdlib] Remove some unneeded ref args, NFC. These methods can just pass a borrowed arg, no need for ref. MODULAR_ORIG_COMMIT_REV_ID: d62686fc4a14956068f9a705686ae85d5d2aa14d --- stdlib/src/utils/span.mojo | 4 ++-- stdlib/src/utils/string_slice.mojo | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 2534df4323..635ba406e0 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -263,7 +263,7 @@ struct Span[ fn __eq__[ T: EqualityComparableCollectionElement, // - ](ref [_]self: Span[T, lifetime], ref [_]rhs: Span[T]) -> Bool: + ](self: Span[T, lifetime], rhs: Span[T]) -> Bool: """Verify if span is equal to another span. Parameters: @@ -292,7 +292,7 @@ struct Span[ @always_inline fn __ne__[ T: EqualityComparableCollectionElement, // - ](ref [_]self: Span[T, lifetime], ref [_]rhs: Span[T]) -> Bool: + ](self: Span[T, lifetime], rhs: Span[T]) -> Bool: """Verify if span is not equal to another span. Parameters: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 1904633013..1ff1bfc858 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -425,7 +425,7 @@ struct StringSlice[ """ return not self == rhs - fn __iter__(ref [_]self) -> _StringSliceIter[lifetime]: + fn __iter__(self) -> _StringSliceIter[lifetime]: """Iterate over elements of the string slice, returning immutable references. Returns: @@ -435,9 +435,7 @@ struct StringSlice[ unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) - fn __reversed__( - ref [_]self, - ) -> _StringSliceIter[lifetime, False]: + fn __reversed__(self) -> _StringSliceIter[lifetime, False]: """Iterate backwards over the string, returning immutable references. Returns: From f883925a7041b3624c5c71bbcbb57fbdebf6c6a1 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 24 Sep 2024 11:57:34 -0700 Subject: [PATCH 1606/2019] [stdlib] Remove runtime penalty for `debug_assert` with variadics This allows you to format a `debug_assert` message with no IR bloat, for example: ```mojo from benchmark import keep fn main(): actual = 41 expected = 42 debug_assert( actual == expected, "actual: ", actual, " expected: ", expected ) keep(actual) keep(expected) ``` The generated LLVM IR is exactly the same when assertions are disabled, as when the call to `debug_assert` is removed. MODULAR_ORIG_COMMIT_REV_ID: 927c807df9fd546feca8e3d7774f62bf69858dd8 --- stdlib/docs/style-guide.md | 27 ++++++++++ stdlib/src/builtin/debug_assert.mojo | 54 +++++++++++++++---- stdlib/src/collections/list.mojo | 7 ++- stdlib/src/collections/string.mojo | 5 +- stdlib/src/utils/string_slice.mojo | 3 ++ stdlib/test/builtin/test_debug_assert.mojo | 9 ++++ .../test/builtin/test_debug_assert_error.mojo | 5 +- .../test_list_getitem_invalid_index.mojo | 25 +++++++++ 8 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 stdlib/test/collections/test_list_getitem_invalid_index.mojo diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 2ed8ca349f..f1c135cf4f 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -349,6 +349,33 @@ fn add_param_arg[foo: Int](bar: Int) -> Int: For more detailed style guidelines, see the [Mojo docstring style guide](docstring-style-guide.md). +### Assertions + +You should use assertions liberally when there is a condition you expect for the +following code to behave correctly. Assertions may be present even in +"unsafe" methods and types. You should format the message to be descriptive with +the expected and actual values: + +```mojo +actual = 41 +expected = 42 +debug_assert(actual == expected, "expected: ", expected, " but got: ", actual) +``` + +Make sure that you don't allocate anything in the call to `debug_assert` to +ensure there is no runtime penalty when assertions are disabled. If you must +have a side-effect in the condition such as allocating a `String`, you can pass +a closure as a parameter that only runs when assertions are enabled: + +```mojo +tensor = Tensor[DType.uint8, 1](TensorShape(1), cpu_device()) + +fn _test_cpu() capturing -> Bool: + return "cpu" in str(tensor._device) + +debug_assert[_test_cpu]("This code is only runnable on CPU") +``` + ### Testing #### Unit test filenames diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 8eec66fd9e..b43eff0afb 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -31,10 +31,11 @@ alias _ERROR_ON_ASSERT = is_debug_build() or is_defined[ alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() +# TODO(MOCO-358) Deduplicate and simplify when variadic unpacking is supported @always_inline fn debug_assert[ - func: fn () capturing [_] -> Bool, message_type: Stringable -](message: message_type): + func: fn () capturing [_] -> Bool, *Ts: Stringable +](*messages: *Ts): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -48,19 +49,37 @@ fn debug_assert[ func: The function to invoke to check if the assertion holds. Can be used if the function is side-effecting, in which case a debug_assert taking a Bool will evaluate the expression producing the Bool even in release mode. - message_type: A type conforming to `Stringable` for the message. + Ts: The element types conforming to `Stringable` for the message. Args: - message: The message before displaying it on failure. + messages: Arguments to convert to a `String` message. """ @parameter if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: - debug_assert(func(), message) + if func(): + return + + # Only allocate and build a formatted `String` on CPU + @parameter + if triple_is_nvidia_cuda(): + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]("", __call_location()) + else: + message = String() + + @parameter + fn build_message[i: Int, T: Stringable](value: T): + message += str(value) + + messages.each_idx[build_message]() + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( + message, __call_location() + ) +# TODO(MOCO-358) Deduplicate and simplify when variadic unpacking is supported @always_inline -fn debug_assert[message_type: Stringable](cond: Bool, message: message_type): +fn debug_assert[*Ts: Stringable](cond: Bool, *messages: *Ts): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -71,20 +90,33 @@ fn debug_assert[message_type: Stringable](cond: Bool, message: message_type): for enabling assertions in the library. Parameters: - message_type: A type conforming to `Stringable` for the message. + Ts: The element types conforming to `Stringable` for the message. Args: cond: The bool value to assert. - message: The message before displaying it on failure. + messages: Arguments to convert to a `String` message. """ @parameter if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: if cond: return - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( - message, __call_location() - ) + + # Only allocate and build a formatted `String` on CPU + @parameter + if triple_is_nvidia_cuda(): + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]("", __call_location()) + else: + message = String() + + @parameter + fn build_message[i: Int, T: Stringable](value: T): + message += str(value) + + messages.each_idx[build_message]() + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( + message, __call_location() + ) @no_inline diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9cbd312e17..01b79bfa28 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -794,10 +794,15 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Returns: A reference to the element at the given index. """ + var normalized_idx = idx + debug_assert( -self.size <= normalized_idx < self.size, - "index must be within bounds", + "index: ", + normalized_idx, + " is out of bounds for `List` of size: ", + self.size, ) if normalized_idx < 0: normalized_idx += len(self) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index ac165fbc38..82a24beb24 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -90,7 +90,7 @@ fn ord(s: StringSlice) -> Int: s.byte_length() == int(num_bytes), "input string must be one character" ) debug_assert( - 1 < int(num_bytes) < 5, "invalid UTF-8 byte " + str(b1) + " at index 0" + 1 < int(num_bytes) < 5, "invalid UTF-8 byte ", b1, " at index 0" ) var shift = int((6 * (num_bytes - 1))) var b1_mask = 0b11111111 >> (num_bytes + 1) @@ -98,8 +98,7 @@ fn ord(s: StringSlice) -> Int: for i in range(1, num_bytes): p += 1 debug_assert( - p[] >> 6 == 0b00000010, - "invalid UTF-8 byte " + str(b1) + " at index " + str(i), + p[] >> 6 == 0b00000010, "invalid UTF-8 byte ", b1, " at index ", i ) shift -= 6 result |= int(p[] & 0b00111111) << shift diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 1ff1bfc858..52396b21b0 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -32,6 +32,9 @@ alias StaticString = StringSlice[StaticConstantLifetime] fn _unicode_codepoint_utf8_byte_length(c: Int) -> Int: + debug_assert( + 0 <= c <= 0x10FFFF, "Value: ", c, " is not a valid Unicode code point" + ) alias sizes = SIMD[DType.int32, 4](0, 0b0111_1111, 0b0111_1111_1111, 0xFFFF) return int((sizes < c).cast[DType.uint8]().reduce_add()) diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index 89d51c9639..7bad619fe3 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -19,6 +19,7 @@ def main(): test_debug_assert() + test_debug_assert_multiple_args() # CHECK-OK-LABEL: test_debug_assert @@ -28,3 +29,11 @@ def test_debug_assert(): debug_assert(3, Error("also ok")) # CHECK-OK: is reached print("is reached") + + +# CHECK-OK-LABEL: test_debug_assert_multiple_args +def test_debug_assert_multiple_args(): + print("== test_debug_assert_multiple_args") + debug_assert(True, "passing mutliple args: ", 42, ", ", 4.2) + # CHECK-OK: is reached + print("is reached") diff --git a/stdlib/test/builtin/test_debug_assert_error.mojo b/stdlib/test/builtin/test_debug_assert_error.mojo index 02a68a4cf5..c6f5267717 100644 --- a/stdlib/test/builtin/test_debug_assert_error.mojo +++ b/stdlib/test/builtin/test_debug_assert_error.mojo @@ -21,6 +21,9 @@ # CHECK-FAIL-LABEL: test_fail fn main(): print("== test_fail") - debug_assert(False, "fail") + # CHECK-FAIL: formatted failure message: 2, 4 + debug_assert( + False, "formatted failure message: ", 2, ", ", Scalar[DType.uint8](4) + ) # CHECK-FAIL-NOT: is never reached print("is never reached") diff --git a/stdlib/test/collections/test_list_getitem_invalid_index.mojo b/stdlib/test/collections/test_list_getitem_invalid_index.mojo new file mode 100644 index 0000000000..f5ce83a0e2 --- /dev/null +++ b/stdlib/test/collections/test_list_getitem_invalid_index.mojo @@ -0,0 +1,25 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: has_not +# RUN: not --crash %bare-mojo -D BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL + + +# CHECK-FAIL-LABEL: test_fail_list_index +fn main(): + print("== test_fail_list_index") + # CHECK-FAIL: index: 4 is out of bounds for `List` of size: 3 + nums = List[Int](1, 2, 3) + print(nums[4]) + + # CHECK-FAIL-NOT: is never reached + print("is never reached") From b3c29106467e515226c7583e7459e7803a27c895 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 24 Sep 2024 14:34:11 -0600 Subject: [PATCH 1607/2019] [mojo-lang] Introduce an escape hatch for exclusivity checking. This introduces a new `__unsafe_disable_nested_lifetime_exclusivity` decorator which disables nested lifetime exclusivity checking. This is used on functions that take lifetimes nested in one or more arguments but guarantees they do not actually access anything in that lifetime. As its long and scary name indicates, this is not intended to be generally used, but is an important escape hatch for now. In the future, we're likely to replace this with a safe (checked) mechanism to say that nested lifetimes are not accessed. I haven't implemented such logic yet and don't want to be blocked on it. This also adds this to the methods in `Reference`, `Span` and `StringSlice` that can use these, and re-introduces some unit tests that I removed when switching exclusivity to an error. MODULAR_ORIG_COMMIT_REV_ID: e8783c56545dea0b3dada5756e0815d3d3b1d2cb --- stdlib/src/memory/reference.mojo | 6 ++++++ stdlib/src/utils/span.mojo | 5 +++++ stdlib/src/utils/string_slice.mojo | 6 ++++++ stdlib/test/memory/test_reference.mojo | 2 ++ stdlib/test/utils/test_span.mojo | 2 ++ stdlib/test/utils/test_string_slice.mojo | 4 ++++ 6 files changed, 25 insertions(+) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index e1bc50c50c..8db1604187 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -333,6 +333,11 @@ struct Reference[ """ return __get_litref_as_mvalue(self.value) + # This attribute informs the compiler that indirect address spaces are not + # dereferenced by the method. + # TODO: replace with a safe model that checks the body of the method for + # accesses to the lifetime. + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __eq__(self, rhs: Reference[type, _, address_space]) -> Bool: """Returns True if the two pointers are equal. @@ -347,6 +352,7 @@ struct Reference[ __mlir_op.`lit.ref.to_pointer`(self.value) ) == UnsafePointer(__mlir_op.`lit.ref.to_pointer`(rhs.value)) + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __ne__(self, rhs: Reference[type, _, address_space]) -> Bool: """Returns True if the two pointers are not equal. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 635ba406e0..3a9b125fa3 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -261,6 +261,11 @@ struct Span[ """ return len(self) > 0 + # This attribute informs the compiler that indirect address spaces are not + # dereferenced by the method. + # TODO: replace with a safe model that checks the body of the method for + # accesses to the lifetime. + @__unsafe_disable_nested_lifetime_exclusivity fn __eq__[ T: EqualityComparableCollectionElement, // ](self: Span[T, lifetime], rhs: Span[T]) -> Bool: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 52396b21b0..31ad7d3dd5 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -347,6 +347,11 @@ struct StringSlice[ """ return len(self._slice) > 0 + # This attribute informs the compiler that indirect address spaces are not + # dereferenced by the method. + # TODO: replace with a safe model that checks the body of the method for + # accesses to the lifetime. + @__unsafe_disable_nested_lifetime_exclusivity fn __eq__(self, rhs: StringSlice) -> Bool: """Verify if a string slice is equal to another string slice. @@ -392,6 +397,7 @@ struct StringSlice[ """ return self == rhs.as_string_slice() + @__unsafe_disable_nested_lifetime_exclusivity @always_inline fn __ne__(self, rhs: StringSlice) -> Bool: """Verify if span is not equal to another string slice. diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo index 90be5fd1b4..8d71065bc6 100644 --- a/stdlib/test/memory/test_reference.mojo +++ b/stdlib/test/memory/test_reference.mojo @@ -31,6 +31,8 @@ def test_equality(): var a = List[Int](1, 2, 3) var b = List[Int](4, 5, 6) + assert_true(Reference(a) == Reference(a)) + assert_true(Reference(b) == Reference(b)) assert_true(Reference(a) != Reference(b)) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index e299cd7430..dd1c789671 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -167,6 +167,8 @@ def test_equality(): var sp = Span[String](l) var sp2 = Span[String](l) var sp3 = Span(l2) + # same pointer + assert_true(sp == sp2) # different pointer assert_true(sp == sp3) # different length diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index f77e57170b..35d216adc3 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -154,7 +154,11 @@ fn test_slice_eq() raises: # eq + # FIXME: the lifetime of the StringSlice lifetime should be the data in the + # string, not the string itself. + # assert_true(str1.as_string_slice().__eq__(str1)) assert_true(str1.as_string_slice().__eq__(str2)) + assert_true(str2.as_string_slice().__eq__(str2.as_string_slice())) assert_true(str1.as_string_slice().__eq__(str3)) # ne From 9e712e1b953e5030606518bcd30c00542d0eeeb7 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Sep 2024 17:36:26 -0700 Subject: [PATCH 1608/2019] This change adds `time.monotonic` to the stdlib time module as an eventual replacement for `time.now`. This change replaces all callsites of `time.now` with `time.monotonic` in the max-examples. MODULAR_ORIG_COMMIT_REV_ID: 51d78b355b1644fd61d14e8170be4f3afaeda681 --- stdlib/src/time/__init__.mojo | 9 ++++++++- stdlib/src/time/time.mojo | 19 +++++++++++++++++++ stdlib/test/time/test_time.mojo | 10 +++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/stdlib/src/time/__init__.mojo b/stdlib/src/time/__init__.mojo index 885fa654c8..de8f50f95e 100644 --- a/stdlib/src/time/__init__.mojo +++ b/stdlib/src/time/__init__.mojo @@ -12,4 +12,11 @@ # ===----------------------------------------------------------------------=== # """Implements the time package.""" -from .time import now, perf_counter, perf_counter_ns, sleep, time_function +from .time import ( + now, + perf_counter, + perf_counter_ns, + sleep, + time_function, + monotonic, +) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 1f6d6b4a41..4465be346c 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -228,6 +228,25 @@ fn now() -> Int: return perf_counter_ns() +# ===----------------------------------------------------------------------===# +# monotonic +# ===----------------------------------------------------------------------===# + + +@always_inline +fn monotonic() -> Int: + """ + Returns the current monotonic time time in nanoseconds. This function + queries the current platform's monotonic clock, making it useful for + measuring time differences, but the significance of the returned value + varies depending on the underlying implementation. + + Returns: + The current time in ns. + """ + return perf_counter_ns() + + # ===----------------------------------------------------------------------===# # time_function # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index d30ba26558..6be7e4e60a 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -13,7 +13,14 @@ # RUN: %mojo %s from sys import os_is_windows -from time import now, perf_counter, perf_counter_ns, sleep, time_function +from time import ( + now, + perf_counter, + perf_counter_ns, + sleep, + time_function, + monotonic, +) from testing import assert_true @@ -54,6 +61,7 @@ fn test_time() raises: assert_true(perf_counter() > 0) assert_true(perf_counter_ns() > 0) assert_true(now() > 0) + assert_true(monotonic() > 0) var t1 = time_function[time_me]() assert_true(t1 > 1 * ns_per_sec) From 483228288a348128c93473a7c4dc6d32d1f2ccff Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 25 Sep 2024 15:27:09 -0700 Subject: [PATCH 1609/2019] [******][GPU] Cleanup some of the sm_80 checks in SIMD type, NFC MODULAR_ORIG_COMMIT_REV_ID: c5b9fa7c23e6c654812f8b8132f57e29289e498c --- stdlib/src/builtin/simd.mojo | 19 +++++-------------- stdlib/src/sys/info.mojo | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 0ae1b97485..669fa6fc0d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -27,7 +27,7 @@ from sys import ( triple_is_nvidia_cuda, bitwidthof, ) -from sys.info import _current_arch +from sys.info import _current_arch, _is_sm_8x from sys._assembly import inlined_assembly from os import abort @@ -144,15 +144,6 @@ fn _has_native_bf16_support() -> Bool: return triple_is_nvidia_cuda() -@always_inline("nodebug") -fn _is_sm_80() -> Bool: - return triple_is_nvidia_cuda() and StringLiteral(_current_arch()) in ( - "sm_80", - "sm_86", - "sm_89", - ) - - # ===----------------------------------------------------------------------=== # # SIMD # ===----------------------------------------------------------------------=== # @@ -535,7 +526,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_80() and type.is_half_float(): + if _is_sm_8x() and type.is_half_float(): return self.fma(1, rhs) return __mlir_op.`pop.add`(self.value, rhs.value) @@ -554,7 +545,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_80() and type.is_half_float(): + if _is_sm_8x() and type.is_half_float(): return rhs.fma(-1, self) return __mlir_op.`pop.sub`(self.value, rhs.value) @@ -577,7 +568,7 @@ struct SIMD[type: DType, size: Int]( ]() @parameter - if _is_sm_80() and type.is_half_float(): + if _is_sm_8x() and type.is_half_float(): return self.fma(rhs, -0.0) constrained[type.is_numeric(), "the SIMD type must be numeric"]() @@ -1761,7 +1752,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_80() and type.is_half_float(): + if _is_sm_8x() and type.is_half_float(): alias prefix = "fma.rn.bf16" if type is DType.bfloat16 else "fma.rn.f16" @parameter diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 6db69f2f21..479778a57e 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -400,6 +400,20 @@ fn is_triple[triple: StringLiteral]() -> Bool: ] +@always_inline("nodebug") +fn _is_sm_8x() -> Bool: + return ( + triple_is_nvidia_cuda["sm_80"]() + or triple_is_nvidia_cuda["sm_86"]() + or triple_is_nvidia_cuda["sm_89"]() + ) + + +@always_inline("nodebug") +fn _is_sm_9x() -> Bool: + return triple_is_nvidia_cuda["sm_90"]() or triple_is_nvidia_cuda["sm_9a"]() + + @always_inline("nodebug") fn triple_is_nvidia_cuda() -> Bool: """Returns True if the target triple of the compiler is `nvptx64-nvidia-cuda` @@ -411,6 +425,20 @@ fn triple_is_nvidia_cuda() -> Bool: return is_triple["nvptx64-nvidia-cuda"]() +@always_inline("nodebug") +fn triple_is_nvidia_cuda[subarch: StringLiteral]() -> Bool: + """Returns True if the target triple of the compiler is `nvptx64-nvidia-cuda` + and we are compiling for the specified sub-architecture and False otherwise. + + Parameters: + subarch: The subarchitecture (e.g. sm_80). + + Returns: + True if the triple target is cuda and False otherwise. + """ + return triple_is_nvidia_cuda() and StringLiteral(_current_arch()) == subarch + + @always_inline("nodebug") fn is_little_endian[ target: __mlir_type.`!kgen.target` = _current_target() From efb5c20d92fbc8c8ddc8d969dce7d006d619f473 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 25 Sep 2024 15:40:39 -0700 Subject: [PATCH 1610/2019] [******][KGEN] Make inlined_assembly variadic MODULAR_ORIG_COMMIT_REV_ID: 4efec117bd356874b4af20673ee877f7d967ae4f --- stdlib/src/sys/_assembly.mojo | 647 +--------------------------------- 1 file changed, 10 insertions(+), 637 deletions(-) diff --git a/stdlib/src/sys/_assembly.mojo b/stdlib/src/sys/_assembly.mojo index 6dae67c37c..4429d45c4e 100644 --- a/stdlib/src/sys/_assembly.mojo +++ b/stdlib/src/sys/_assembly.mojo @@ -13,6 +13,7 @@ """This module includes the inlined_assembly function.""" from sys.intrinsics import _mlirtype_is_eq +from builtin.builtin_list import _LITRefPackHelper # ===----------------------------------------------------------------------===# # 0-arg @@ -23,12 +24,12 @@ from sys.intrinsics import _mlirtype_is_eq fn inlined_assembly[ asm: StringLiteral, result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r", + *types: AnyType, + constraints: StringLiteral, has_side_effect: Bool = True, -]() -> result_type: - """Generates assembly via inline for instructions with 0 args.""" +](*arguments: *types) -> result_type: + """Generates assembly via inline assembly.""" + var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() @parameter if _mlirtype_is_eq[result_type, NoneType](): @@ -40,13 +41,13 @@ fn inlined_assembly[ assembly = asm.value, constraints = constraints.value, hasSideEffects = __mlir_attr.unit, - ]() + ](loaded_pack) else: __mlir_op.`pop.inline_asm`[ _type=None, assembly = asm.value, constraints = constraints.value, - ]() + ](loaded_pack) return rebind[result_type](None) else: @@ -57,638 +58,10 @@ fn inlined_assembly[ assembly = asm.value, constraints = constraints.value, hasSideEffects = __mlir_attr.unit, - ]() + ](loaded_pack) else: return __mlir_op.`pop.inline_asm`[ _type=result_type, assembly = asm.value, constraints = constraints.value, - ]() - - -# ===----------------------------------------------------------------------===# -# 1-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r", - has_side_effect: Bool = True, -](arg0: arg0_type) -> result_type: - """Generates assembly via inline for instructions with 1 arg.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0) - - -# ===----------------------------------------------------------------------===# -# 2-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r", - has_side_effect: Bool = True, -](arg0: arg0_type, arg1: arg1_type) -> result_type: - """Generates assembly via inline for instructions with 2 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1) - - -# ===----------------------------------------------------------------------===# -# 3-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r", - has_side_effect: Bool = True, -](arg0: arg0_type, arg1: arg1_type, arg2: arg2_type) -> result_type: - """Generates assembly via inline for instructions with 3 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2) - - -# ===----------------------------------------------------------------------===# -# 4-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, arg1: arg1_type, arg2: arg2_type, arg3: arg3_type -) -> result_type: - """Generates assembly via inline for instructions with 4 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3) - - -# ===----------------------------------------------------------------------===# -# 5-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, - arg1: arg1_type, - arg2: arg2_type, - arg3: arg3_type, - arg4: arg4_type, -) -> result_type: - """Generates assembly via inline for instructions with 5 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4) - - -# ===----------------------------------------------------------------------===# -# 6-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, - arg5_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, - arg1: arg1_type, - arg2: arg2_type, - arg3: arg3_type, - arg4: arg4_type, - arg5: arg5_type, -) -> result_type: - """Generates assembly via inline for instructions with 6 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5) - - -# ===----------------------------------------------------------------------===# -# 7-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, - arg5_type: AnyTrivialRegType, - arg6_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, - arg1: arg1_type, - arg2: arg2_type, - arg3: arg3_type, - arg4: arg4_type, - arg5: arg5_type, - arg6: arg6_type, -) -> result_type: - """Generates assembly via inline for instructions with 7 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - - -# ===----------------------------------------------------------------------===# -# 8-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, - arg5_type: AnyTrivialRegType, - arg6_type: AnyTrivialRegType, - arg7_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r,r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, - arg1: arg1_type, - arg2: arg2_type, - arg3: arg3_type, - arg4: arg4_type, - arg5: arg5_type, - arg6: arg6_type, - arg7: arg7_type, -) -> result_type: - """Generates assembly via inline for instructions with 8 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - - -# ===----------------------------------------------------------------------===# -# 9-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, - arg5_type: AnyTrivialRegType, - arg6_type: AnyTrivialRegType, - arg7_type: AnyTrivialRegType, - arg8_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r,r,r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, - arg1: arg1_type, - arg2: arg2_type, - arg3: arg3_type, - arg4: arg4_type, - arg5: arg5_type, - arg6: arg6_type, - arg7: arg7_type, - arg8: arg8_type, -) -> result_type: - """Generates assembly via inline for instructions with 9 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - - -# ===----------------------------------------------------------------------===# -# 10-arg -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn inlined_assembly[ - arg0_type: AnyTrivialRegType, - arg1_type: AnyTrivialRegType, - arg2_type: AnyTrivialRegType, - arg3_type: AnyTrivialRegType, - arg4_type: AnyTrivialRegType, - arg5_type: AnyTrivialRegType, - arg6_type: AnyTrivialRegType, - arg7_type: AnyTrivialRegType, - arg8_type: AnyTrivialRegType, - arg9_type: AnyTrivialRegType, //, - asm: StringLiteral, - result_type: AnyTrivialRegType, - /, - *, - constraints: StringLiteral = "r,r,r,r,r,r,r,r,r,r", - has_side_effect: Bool = True, -]( - arg0: arg0_type, - arg1: arg1_type, - arg2: arg2_type, - arg3: arg3_type, - arg4: arg4_type, - arg5: arg5_type, - arg6: arg6_type, - arg7: arg7_type, - arg8: arg8_type, - arg9: arg9_type, -) -> result_type: - """Generates assembly via inline for instructions with 10 args.""" - - @parameter - if _mlirtype_is_eq[result_type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - else: - __mlir_op.`pop.inline_asm`[ - _type=None, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - return rebind[result_type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - hasSideEffects = __mlir_attr.unit, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - else: - return __mlir_op.`pop.inline_asm`[ - _type=result_type, - assembly = asm.value, - constraints = constraints.value, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + ](loaded_pack) From 1d8ad0fa55a399eed1eb3fb89477f0d193e8079a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 25 Sep 2024 16:38:58 -0700 Subject: [PATCH 1611/2019] [******][GPU] Continue plumbing F8 support for H100 systems MODULAR_ORIG_COMMIT_REV_ID: 2a02ddabf4afe53629e6c92befb80c5292739eaf --- stdlib/src/builtin/dtype.mojo | 47 ++++++++++++++++++++++++++++++++--- stdlib/src/builtin/simd.mojo | 13 ++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index b1e3f7e6c1..906e216ecf 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -57,6 +57,14 @@ struct DType( """Represents a signed integer type whose bitwidth is 64.""" alias uint64 = DType(__mlir_attr.`#kgen.dtype.constant : !kgen.dtype`) """Represents an unsigned integer type whose bitwidth is 64.""" + alias float8e5m2 = DType( + __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` + ) + """Represents a FP8E5M2 floating point format whose bitwidth is 8.""" + alias float8e4m3 = DType( + __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` + ) + """Represents a FP8E4M3 floating point format whose bitwidth is 8.""" alias bfloat16 = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) @@ -116,6 +124,10 @@ struct DType( return DType.uint64 elif str == String("index"): return DType.index + elif str == String("float8e5m2"): + return DType.float8e5m2 + elif str == String("float8e4m3"): + return DType.float8e4m3 elif str == String("bfloat16"): return DType.bfloat16 elif str == String("float16"): @@ -170,6 +182,10 @@ struct DType( return writer.write_str("uint64") if self == DType.index: return writer.write_str("index") + if self == DType.float8e5m2: + return writer.write_str("float8e5m2") + if self == DType.float8e4m3: + return writer.write_str("float8e4m3") if self == DType.bfloat16: return writer.write_str("bfloat16") if self == DType.float16: @@ -358,6 +374,17 @@ struct DType( ) ) + @always_inline("nodebug") + fn is_float8(self) -> Bool: + """Returns True if the type is a 8bit-precision floating point type, + e.g. either float8e5m2 or float8e4m3. + + Returns: + True if the type is a 8bit-precision float, false otherwise. + """ + + return self in (DType.float8e5m2, DType.float8e4m3) + @always_inline("nodebug") fn is_half_float(self) -> Bool: """Returns True if the type is a half-precision floating point type, @@ -410,6 +437,10 @@ struct DType( return sizeof[DType.bool]() if self == DType.index: return sizeof[DType.index]() + if self == DType.float8e5m2: + return sizeof[DType.float8e5m2]() + if self == DType.float8e4m3: + return sizeof[DType.float8e4m3]() if self == DType.bfloat16: return sizeof[DType.bfloat16]() if self == DType.float16: @@ -581,7 +612,11 @@ fn _integral_type_of[type: DType]() -> DType: return type @parameter - if type is DType.bfloat16 or type is DType.float16: + if type.is_float8(): + return DType.int8 + + @parameter + if type.is_half_float(): return DType.int16 @parameter @@ -610,7 +645,11 @@ fn _unsigned_integral_type_of[type: DType]() -> DType: return _uint_type_of_width[bitwidthof[type]()]() @parameter - if type is DType.bfloat16 or type is DType.float16: + if type.is_float8(): + return DType.uint8 + + @parameter + if type.is_half_float(): return DType.uint16 @parameter @@ -636,7 +675,9 @@ fn _scientific_notation_digits[type: DType]() -> StringLiteral: constrained[type.is_floating_point(), "expected floating point type"]() @parameter - if type is DType.bfloat16 or type is DType.float16: + if type.is_float8(): + return "2" + elif type.is_half_float(): return "4" elif type is DType.float32 or type is DType.tensor_float32: return "8" diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 669fa6fc0d..79fae1ddf9 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -27,7 +27,7 @@ from sys import ( triple_is_nvidia_cuda, bitwidthof, ) -from sys.info import _current_arch, _is_sm_8x +from sys.info import _current_arch, _is_sm_8x, _is_sm_9x from sys._assembly import inlined_assembly from os import abort @@ -117,9 +117,13 @@ fn _simd_construction_checks[type: DType, size: Int](): constrained[size > 0, "simd width must be > 0"]() constrained[size & (size - 1) == 0, "simd width must be power of 2"]() constrained[ - type is not DType.bfloat16 or not has_neon(), + not (type is DType.bfloat16 and has_neon()), "bf16 is not supported for ARM architectures", ]() + constrained[ + not (type.is_float8() and _has_native_f8_support()), + "f8 is not supported on non sm_90 architectures", + ]() @always_inline("nodebug") @@ -144,6 +148,11 @@ fn _has_native_bf16_support() -> Bool: return triple_is_nvidia_cuda() +@always_inline("nodebug") +fn _has_native_f8_support() -> Bool: + return _is_sm_9x() + + # ===----------------------------------------------------------------------=== # # SIMD # ===----------------------------------------------------------------------=== # From b44239ed947c92bf1adbbf21dd3314fe462b4864 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 25 Sep 2024 17:21:50 -0700 Subject: [PATCH 1612/2019] [Docs] Clean up roadmap. Removed references to the modular CLI and known SDK issues section, which documented install issues related to the old CLI. MODULAR_ORIG_COMMIT_REV_ID: 1c0fcf643c29b697fca939ecf5f6604a65e75a60 --- docs/roadmap.md | 110 ++++-------------------------------------------- 1 file changed, 8 insertions(+), 102 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index f7569721ea..fcd5c6fcff 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -33,7 +33,7 @@ If you have encountered any bugs with current Mojo behavior, please If you have ideas about how to improve the core Mojo features, we prefer that you first look for similar topics or start a new conversation about it -in our [GitHub Discussions](https://github.com/modularml/mojo/discussions). +on [Discord](https://discord.gg/modular). We also consider Mojo to be a new member of the Python family, so if you have suggestions to improve the experience with Python, we encourage @@ -66,7 +66,7 @@ subsumed by more general features if time and care were given to broader evaluation. - Third, the Python community should tackle some of these ideas first. It is -important to us that Mojo be a good member of the Python family (a "Python++"), +important to us that Mojo be a good member of the Python family, not just a language with Pythonic syntax. As such, we don't want to needlessly diverge from Python evolution: adding a bunch of features could lead to problems down the road if Python makes incompatible decisions. Such a future @@ -83,83 +83,6 @@ better equipped to evaluate these features, they have mature code bases to evaluate them with, and they have processes and infrastructure for making structured language evolution features. -## Mojo SDK known issues - -The Mojo SDK is still in early development -and currently only available for Ubuntu Linux and macOS (Apple silicon) -systems. Here are some of the notable issues that we plan to fix: - -- Missing native support for Windows, Intel Macs, and Linux distributions - other than Ubuntu. Currently, we support Ubuntu systems with x86-64 - processors and Apple Silicon macOS. Support for more Linux distributions - (including Debian and RHEL) and Windows is in progress. - -- Modular CLI install might fail and require `modular clean` before you - re-install. - - If it asks you to perform auth, run `modular auth ` and use the - `MODULAR_AUTH` value shown for the `curl` command on [the download - page](https://developer.modular.com/download). - -- If you attempt to uninstall Mojo with `modular uninstall`, your subsequent - attempt to install Mojo might fail with an HTTP 500 error code. If so, run - `modular clean` and try again. - -- Mojo REPL might hang (become unresponsive for more than 10 seconds) when - interpreting an expression if your system has 4 GiB or less RAM. If you - encounter this issue, please report it with your system specs. - -Additionally, we're aware of some issues that we might not be able to solve, -but we mention them here with some more information: - -- When installing Mojo, if you receive the error, - `failed to reach URL https://cas.modular.com`, it could be because your - network connection is behind a firewall. Try updating your firewall settings - to allow access to these end points: `https://packages.modular.com` and - `https://cas.modular.com`. Then retry with `modular clean` and - `modular install mojo`. - -- When installing Mojo, if you receive the error, - `gpg: no valid OpenGPG data found`, this is likely because you are located - outside our supported geographies. Due to US export control restrictions, we - are unable to provide access to Mojo to users situated in specific countries. - -- If using Windows Subsystem for Linux (WSL), you might face issues with WSL 1. - We recommend you upgrade to WSL 2. To check the version, run `wsl -l -v`. If - you're running WSL 1, refer to the - [WSL upgrade instructions](https://learn.microsoft.com/en-us/windows/wsl/install#upgrade-version-from-wsl-1-to-wsl-2). - -- When installing on macOS (Apple silicon), the Modular CLI install might fail - with the message: - - ```plaintext - modular: The arm64 architecture is required for this software. - ``` - - This occurs because Apple's Rosetta x86 emulation is active. Check the - following: - - - Right click on the terminal application you use (for example, - `Terminal.app`), click **Get Info**, and make sure the **Open in Rosetta** - checkbox is not selected. - - - Run the following command: - - ```bash - brew config | grep Rosetta - ``` - - If the output shows `Rosetta 2: True`, the x86 version of Homebrew is - installed. - [Uninstall and reinstall Homebrew](https://github.com/homebrew/install#uninstall-homebrew) - before retrying the Modular installation. - - **Note:** Before uninstalling Homebrew, verify that you don't have other - projects specifically depending on the x86 version of Homebrew. - -You can see other [reported issues on -GitHub](https://github.com/modularml/mojo/issues). - ## Small independent features There are a number of features that are missing that are important to round out @@ -188,7 +111,7 @@ to use right now. ## Traits support -As of v0.6.0 Mojo has basic support for +Mojo has basic support for [traits](/mojo/manual/traits). Traits allow you to specify a set of requirements for types to implement. Types can implement those requirements to *conform to* the trait. Traits allow you to write @@ -346,9 +269,11 @@ Mojo does not yet support the `async for` and `async with` statements. ### Scoping and mutability of statement variables Python programmers understand that local variables are implicitly declared and -scoped at the function level. As the programming manual explains, this feature -is supported in Mojo only inside `def` functions. However, there are some -nuances to Python's implicit declaration rules that Mojo does not match 1-to-1. +scoped at the function level. As the Mojo Manual explains, this is supported in +Mojo for +[implicitly-declared variables](/mojo/manual/variables#implicitly-declared-variables). +However, there are some nuances to Python's implicit declaration rules that Mojo +does not match 1-to-1. For example, the scope of `for` loop iteration variables and caught exceptions in `except` statements is limited to the next indentation block, for both `def` @@ -374,25 +299,6 @@ dynamic characteristic of the function. Mojo's lifetime tracker is intentionally simple (so lifetimes are easy to use!), and cannot reason that `i` would be defined even when the loop bounds are constant. -Also stated in the programming manual: in `def` functions, the function -arguments are mutable and re-assignable, whereas in `fn`, function arguments are -rvalues and cannot be re-assigned. The same logic extends to statement -variables, like `for` loop iteration variables or caught exceptions: - -```mojo -def foo(): - try: - bad_function(): - except e: - e = Error() # ok: we can overwrite 'e' - -fn bar(): - try: - bad_function(): - except e: - e = Error() # error: 'e' is not mutable -``` - ### Name scoping of nested function declarations In Python, nested function declarations produce dynamic values. They are From a816fbf710523b1b63914dcf8222f1fcb32790b9 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 25 Sep 2024 19:01:21 -0700 Subject: [PATCH 1613/2019] Remove legacy Quarto markup styles Docsite currently transforms these for us to be compatible with Docusaurus, but we need to migrate and avoid purpetuating these bad styles. MODULAR_ORIG_COMMIT_REV_ID: 5d8a81cddbe43591eb7fe83381e64468f709c961 --- docs/manual/decorators/nonmaterializable.ipynb | 4 ++-- docs/manual/packages.md | 18 +++++++++--------- docs/manual/python/index.ipynb | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index b5a657517b..494a9880a1 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -75,9 +75,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ":::{.callout-note}\n", + ":::note\n", "\n", - "**Note:** A non-materializable struct must have all of its methods annotated\n", + "A non-materializable struct must have all of its methods annotated\n", "as `@always_inline`, and it must be computable in the parameter domain.\n", "\n", ":::" diff --git a/docs/manual/packages.md b/docs/manual/packages.md index f9a146f37e..ec7f1b55ea 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -18,7 +18,7 @@ Mojo module is a single Mojo source file that includes code suitable for use by other files that import it. For example, you can create a module to define a struct such as this one: -```{.mojo filename="mymodule.mojo"} +```mojo title="mymodule.mojo" struct MyPair: var first: Int var second: Int @@ -38,7 +38,7 @@ Notice that this code has no `main()` function, so you can't execute For example, here's how you can import `MyPair` into a file named `main.mojo` that's in the same directory as `mymodule.mojo`: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mymodule import MyPair fn main(): @@ -49,7 +49,7 @@ fn main(): Alternatively, you can import the whole module and then access its members through the module name. For example: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" import mymodule fn main(): @@ -59,7 +59,7 @@ fn main(): You can also create an alias for an imported member with `as`, like this: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" import mymodule as my fn main(): @@ -111,7 +111,7 @@ struct) and `__init__.mojo` is empty. In this case, the `main.mojo` file can now import `MyPair` through the package name like this: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mypackage.mymodule import MyPair fn main(): @@ -148,7 +148,7 @@ mypack.mojopkg Because we named the package file different from the directory, we need to fix the import statement and it all works the same: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mypack.mymodule import MyPair ``` @@ -186,14 +186,14 @@ mypackage/ Let's now add the following line in `__init__.mojo`: -```{.mojo filename="__init__.mojo"} +```mojo title="__init__.mojo" from .mymodule import MyPair ``` That's all that's in there. Now, we can simplify the import statement in `main.mojo` like this: -```{.mojo filename="main.mojo"} +```mojo title="main.mojo" from mypackage import MyPair ``` @@ -210,7 +210,7 @@ from algorithm.functional import map However, the `algorithm/__init__.mojo` file also includes these lines: -```{.mojo filename="algorithm/__init__.mojo"} +```mojo title="algorithm/__init__.mojo" from .functional import * from .reduction import * ``` diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index e9e3caa2b1..0a292153ff 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -148,7 +148,7 @@ "\n", "For example, suppose you have a Python file named `mypython.py`:\n", "\n", - "```{.python filename=\"mypython.py\"}\n", + "```python title=\"mypython.py\"\n", "import numpy as np\n", "\n", "def gen_random_values(size, base):\n", @@ -159,7 +159,7 @@ "\n", "Here's how you can import it and use it in a Mojo file:\n", "\n", - "```{.mojo filename=\"main.mojo\"}\n", + "```mojo title=\"main.mojo\"\n", "from python import Python\n", "\n", "def main():\n", @@ -210,7 +210,7 @@ "First you create a Python module that defines a Tkinter interface, with a window\n", "and single button:\n", "\n", - "```{.python filename=\"myapp.py\"}\n", + "```python title=\"myapp.py\"\n", "import tkinter as tk\n", "\n", "class App:\n", @@ -244,7 +244,7 @@ "source": [ "You can call this module from Mojo like this:\n", "\n", - "```{.mojo filename=\"main.mojo\"}\n", + "```mojo title=\"main.mojo\"\n", "from python import Python\n", "\n", "def button_clicked():\n", From 7097dc52e752bb384e6ca78f27e669a7a2cbdc33 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 25 Sep 2024 20:40:31 -0600 Subject: [PATCH 1614/2019] [mojo-stdlib] Parameterize `UnsafePointer` on a lifetime. This adds a new lifetime parameter to `UnsafePointer`, which specifies the lifetime of the dereferenced pointer. It defaults to `AnyLifetime`. This has two advantages: 1) This can be used by unsafe code that wants to be more specific about lifetimes than using `AnyLifetime`. This is important as we continue to push lifetimes in the stack. 2) This allows the compiler to know that unqualified `UnsafePointer`'s involve `AnyLifetime` which means that CheckLifetimes understand that APIs using `UnsafePointer` may access any live value. This allows it to extend the lifetime of local values, which defines away another class of reasons that people have to use `_ = foo` to extend lifetimes. Similar treatment should be applied to the `Buffer`/`StringRef` and other similar types that are wrappers around `UnsafePointer` MODULAR_ORIG_COMMIT_REV_ID: 9c4ec32a5c57fe1c157ae1ac752c71279ada476e --- docs/changelog.md | 30 ++++++++++++++++++++++ stdlib/src/builtin/builtin_list.mojo | 2 -- stdlib/src/builtin/format_int.mojo | 3 --- stdlib/src/builtin/hash.mojo | 1 - stdlib/src/builtin/io.mojo | 2 -- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/memory/memory.mojo | 4 +-- stdlib/src/memory/reference.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 20 +++++++++++---- stdlib/src/utils/span.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 2 +- stdlib/test/collections/test_list.mojo | 8 +----- stdlib/test/memory/test_unsafepointer.mojo | 9 ++++--- 13 files changed, 57 insertions(+), 30 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ef4a0413f8..37d1b5cd2e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -20,6 +20,36 @@ what we publish. enabling things like `count_leading_zeros` to work at compile time: [Issue #933](https://github.com/modularml/mojo/issues/933). +- The destructor insertion logic in Mojo is now aware that types that take an + `AnyLifetime` as part of their signature could potentially access any live + value that destructor insertion is tracking, eliminating a significant + usability issue with unsafe APIs like `UnsafePointer`. Consider a typical + example working with strings before this change: + + ```mojo + var str = String(...) + var ptr = str.unsafe_ptr() + some_low_level_api(ptr) + _ = str^ # OLD HACK: Explicitly keep string alive until here! + ``` + + The `_ = str^` pattern was formerly required because the Mojo compiler has no + idea what "ptr" might reference. As a consequence, it had no idea that + `some_low_level_api` might access `str` and therefore thought it was ok to + destroy the `String` before the call - this is why the explicit lifetime + extension was required. + + Mojo now knows that `UnsafePointer` may access the `AnyLifetime` lifetime, + and now assumes that any API that uses that lifetime could use live values. + In this case, it assumes that `some_low_level_api` might access `str` and + because it might be using it, it cannot destroy `str` until after the call. + The consequence of this is that the old hack is no longer needed for these + cases! + +- The `UnsafePointer` type now has a `lifetime` parameter that can be used when + the `UnsafePointer` is known to point into some lifetime. This lifetime is + propagated through the `ptr[]` indirection operation. + - The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration setting that can be used in conjunction with `mojoFile` to define the build arguments when compiling the Mojo file. diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 4e8d67809a..4dfd6d8c07 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -347,7 +347,6 @@ struct VariadicListMem[ # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. self.value = UnsafePointer.address_of(tmp).bitcast[Self._mlir_type]()[] - _ = tmp self._is_owned = False # Provide support for variadics of *owned* arguments. The reference will @@ -368,7 +367,6 @@ struct VariadicListMem[ # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. self.value = UnsafePointer.address_of(tmp).bitcast[Self._mlir_type]()[] - _ = tmp self._is_owned = True @always_inline diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 04299a556b..57a60576a2 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -331,8 +331,6 @@ fn _try_write_int[ ) fmt.write_str(zero) - _ = zero_buf^ - return None # Create a buffer to store the formatted value @@ -411,6 +409,5 @@ fn _try_write_int[ ) fmt.write_str(str_slice) - _ = buf^ return None diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 644f8ca5b4..d67e8a8a4a 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -265,7 +265,6 @@ fn hash(bytes: UnsafePointer[UInt8], n: Int) -> UInt: memset_zero(ptr + r, stride - r) # set the rest to 0 var last_value = ptr.bitcast[Scalar[type]]().load[width=simd_width]() hash_data = _HASH_UPDATE(hash_data, last_value) - _ = remaining^ # We make sure the array lives long enough. # Now finally, hash the final SIMD vector state. return _hash_simd(hash_data) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index e061dd5e6e..2befb93aa5 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -183,7 +183,6 @@ fn _printf[ _ = external_call["vprintf", Int32]( fmt.unsafe_cstr_ptr(), Reference(loaded_pack) ) - _ = loaded_pack else: with _fdopen(file) as fd: _ = __mlir_op.`pop.external_call`[ @@ -332,7 +331,6 @@ fn _put[ _ = external_call["vprintf", Int32]( x.unsafe_ptr(), arg_ptr.bitcast[UnsafePointer[NoneType]]() ) - _ = tmp else: alias MAX_STR_LEN = 0x1000_0000 diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 8198237e6e..1180478c5a 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -129,7 +129,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew): references, delete the object and free its memory.""" if self._inner[].drop_ref(): # Call inner destructor, then free the memory. - (self._inner).destroy_pointee() + self._inner.destroy_pointee() self._inner.free() # FIXME: The lifetime returned for this is currently self lifetime, which diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 456d3d6274..5855800ca6 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -50,7 +50,7 @@ fn _align_down(value: Int, alignment: Int) -> Int: @always_inline fn _memcmp_impl_unconstrained[ type: DType -](s1: UnsafePointer[Scalar[type], *_], s2: __type_of(s1), count: Int) -> Int: +](s1: UnsafePointer[Scalar[type], _], s2: __type_of(s1), count: Int) -> Int: alias simd_width = simdwidthof[type]() if count < simd_width: for i in range(count): @@ -94,7 +94,7 @@ fn _memcmp_impl_unconstrained[ @always_inline fn _memcmp_impl[ type: DType -](s1: UnsafePointer[Scalar[type], *_], s2: __type_of(s1), count: Int) -> Int: +](s1: UnsafePointer[Scalar[type], _], s2: __type_of(s1), count: Int) -> Int: constrained[type.is_integral(), "the input dtype must be integral"]() return _memcmp_impl_unconstrained(s1, s2, count) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 8db1604187..1b3f1db752 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -333,7 +333,7 @@ struct Reference[ """ return __get_litref_as_mvalue(self.value) - # This attribute informs the compiler that indirect address spaces are not + # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for # accesses to the lifetime. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f94d79f906..a20a5d24fb 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -44,6 +44,7 @@ struct UnsafePointer[ address_space: AddressSpace = AddressSpace.GENERIC, exclusive: Bool = False, alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, + lifetime: Lifetime[True].type = MutableAnyLifetime, ]( ImplicitlyBoolable, CollectionElement, @@ -60,6 +61,7 @@ struct UnsafePointer[ address_space: The address space associated with the UnsafePointer allocated memory. exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. alignment: The minimum alignment of this pointer known statically. + lifetime: The lifetime of the memory being addressed. """ # ===-------------------------------------------------------------------===# @@ -162,7 +164,7 @@ struct UnsafePointer[ @always_inline fn __getitem__( self, - ) -> ref [MutableAnyLifetime, address_space._value.value] type: + ) -> ref [lifetime, address_space._value.value] type: """Return a reference to the underlying data. Returns: @@ -172,7 +174,7 @@ struct UnsafePointer[ # We're unsafe, so we can have unsafe things. References we make have # an 'any' mutable lifetime, since UnsafePointer is allowed to alias # anything. - alias _ref_type = Reference[type, MutableAnyLifetime, address_space] + alias _ref_type = Reference[type, lifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( UnsafePointer[type, address_space, False](self).address @@ -197,9 +199,7 @@ struct UnsafePointer[ @always_inline fn __getitem__[ IntLike: IntLike, // - ](self, offset: IntLike) -> ref [ - MutableAnyLifetime, address_space._value.value - ] type: + ](self, offset: IntLike) -> ref [lifetime, address_space._value.value] type: """Return a reference to the underlying data, offset by the given index. Parameters: @@ -267,6 +267,11 @@ struct UnsafePointer[ """ self = self - offset + # This decorator informs the compiler that indirect address spaces are not + # dereferenced by the method. + # TODO: replace with a safe model that checks the body of the method for + # accesses to the lifetime. + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: """Returns True if the two pointers are equal. @@ -279,6 +284,7 @@ struct UnsafePointer[ """ return int(self) == int(rhs) + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __ne__(self, rhs: Self) -> Bool: """Returns True if the two pointers are not equal. @@ -291,6 +297,7 @@ struct UnsafePointer[ """ return not (self == rhs) + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __lt__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a lower address than rhs. @@ -303,6 +310,7 @@ struct UnsafePointer[ """ return int(self) < int(rhs) + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __le__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a lower than or equal @@ -316,6 +324,7 @@ struct UnsafePointer[ """ return int(self) <= int(rhs) + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __gt__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a higher address than rhs. @@ -328,6 +337,7 @@ struct UnsafePointer[ """ return int(self) > int(rhs) + @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") fn __ge__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a higher than or equal diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 3a9b125fa3..543b1b7ea4 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -261,7 +261,7 @@ struct Span[ """ return len(self) > 0 - # This attribute informs the compiler that indirect address spaces are not + # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for # accesses to the lifetime. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 31ad7d3dd5..aa24aebec7 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -347,7 +347,7 @@ struct StringSlice[ """ return len(self._slice) > 0 - # This attribute informs the compiler that indirect address spaces are not + # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for # accesses to the lifetime. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index f416003483..a7fe9260a8 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -294,9 +294,6 @@ def test_list_reverse_move_count(): assert_equal(vec.data[3].move_count, 3) assert_equal(vec.data[4].move_count, 3) - # Keep vec alive until after we've done the last `vec.data + N` read. - _ = vec^ - def test_list_insert(): # @@ -486,7 +483,7 @@ def test_list_extend_non_trivial(): v2.append(MoveCounter[String]("Bar")) v2.append(MoveCounter[String]("Baz")) - v1.extend(v2) + v1.extend(v2^) assert_equal(len(v1), 5) assert_equal(v1[0].value, "Hello") @@ -501,9 +498,6 @@ def test_list_extend_non_trivial(): assert_equal(v1.data[3].move_count, 2) assert_equal(v1.data[4].move_count, 2) - # Keep v1 alive until after we've done the last `vec.data + N` read. - _ = v1^ - def test_2d_dynamic_list(): var list = List[List[Int]]() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 48147d9a6f..de9db6c9c3 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -48,10 +48,11 @@ def test_unsafepointer_of_move_only_type(): assert_equal(actions_ptr[0][1], "__moveinit__", msg="emplace_value") assert_equal(ptr[0].value, 42) - var value = ptr.take_pointee() - assert_equal(len(actions_ptr[0]), 3) - assert_equal(actions_ptr[0][2], "__moveinit__") - assert_equal(value.value, 42) + if True: # scope value + var value = ptr.take_pointee() + assert_equal(len(actions_ptr[0]), 3) + assert_equal(actions_ptr[0][2], "__moveinit__") + assert_equal(value.value, 42) ptr.free() assert_equal(len(actions_ptr[0]), 4) From c85d05affbf9e691e6d42794102e5c5e12bcfd19 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 25 Sep 2024 21:43:54 -0700 Subject: [PATCH 1615/2019] [******][GPU] Use specialized intrinsics for sm_90 arch MODULAR_ORIG_COMMIT_REV_ID: ed79de6f973d3288de8aa07c9e64ae3e5f8de9b8 --- stdlib/src/builtin/simd.mojo | 35 ++++++++++++++++++++----- stdlib/src/math/math.mojo | 51 ++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 79fae1ddf9..b316e111a0 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ import math +from math.math import _call_ptx_intrinsic from sys import ( PrefetchOptions, _RegisterPackType, @@ -535,7 +536,14 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_8x() and type.is_half_float(): + if _is_sm_9x() and type is DType.bfloat16: + return _call_ptx_intrinsic[ + scalar_instruction="add.rn.bf16", + vector2_instruction="add.rn.bf16x2", + scalar_constraints="=h,h,h", + vector_constraints="=r,r,r", + ](self, rhs) + elif _is_sm_8x() and type.is_half_float(): return self.fma(1, rhs) return __mlir_op.`pop.add`(self.value, rhs.value) @@ -554,8 +562,16 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_8x() and type.is_half_float(): - return rhs.fma(-1, self) + if _is_sm_9x() and type is DType.bfloat16: + return _call_ptx_intrinsic[ + scalar_instruction="sub.rn.bf16", + vector2_instruction="sub.rn.bf16x2", + scalar_constraints="=h,h,h", + vector_constraints="=r,r,r", + ](self, rhs) + elif _is_sm_8x() and type.is_half_float(): + return self.fma(-1, rhs) + return __mlir_op.`pop.sub`(self.value, rhs.value) @always_inline("nodebug") @@ -575,9 +591,14 @@ struct SIMD[type: DType, size: Int]( return (rebind[Self._Mask](self) & rebind[Self._Mask](rhs)).cast[ type ]() - - @parameter - if _is_sm_8x() and type.is_half_float(): + elif _is_sm_9x() and type is DType.bfloat16: + return _call_ptx_intrinsic[ + scalar_instruction="mul.rn.bf16", + vector2_instruction="mul.rn.bf16x2", + scalar_constraints="=h,h,h", + vector_constraints="=r,r,r", + ](self, rhs) + elif _is_sm_8x() and type.is_half_float(): return self.fma(rhs, -0.0) constrained[type.is_numeric(), "the SIMD type must be numeric"]() @@ -1761,7 +1782,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_8x() and type.is_half_float(): + if (_is_sm_8x() or _is_sm_9x()) and type.is_half_float(): alias prefix = "fma.rn.bf16" if type is DType.bfloat16 else "fma.rn.f16" @parameter diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 3a1634b8cd..45b7253bb3 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -2368,7 +2368,21 @@ fn _call_ptx_intrinsic_scalar[ Scalar[type], constraints=constraints, has_side_effect=False, - ](arg).cast[type]() + ](arg) + + +fn _call_ptx_intrinsic_scalar[ + type: DType, //, + *, + instruction: StringLiteral, + constraints: StringLiteral, +](arg0: Scalar[type], arg1: Scalar[type]) -> Scalar[type]: + return inlined_assembly[ + instruction + " $0, $1, $2;", + Scalar[type], + constraints=constraints, + has_side_effect=False, + ](arg0, arg1) fn _call_ptx_intrinsic[ @@ -2419,7 +2433,40 @@ fn _call_ptx_intrinsic[ SIMD[type, 2], constraints=vector_constraints, has_side_effect=False, - ](arg).cast[type]() + ](arg.slice[2, offset=i]()) + ) + + return res + + +fn _call_ptx_intrinsic[ + type: DType, + simd_width: Int, //, + *, + scalar_instruction: StringLiteral, + vector2_instruction: StringLiteral, + scalar_constraints: StringLiteral, + vector_constraints: StringLiteral, +](arg0: SIMD[type, simd_width], arg1: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + @parameter + if simd_width == 1: + return _call_ptx_intrinsic_scalar[ + instruction=scalar_instruction, constraints=scalar_constraints + ](arg0[0], arg1[0]) + + var res = SIMD[type, simd_width]() + + @parameter + for i in range(0, simd_width, 2): + res = res.insert[offset=i]( + inlined_assembly[ + vector2_instruction + " $0, $1; $2;", + SIMD[type, 2], + constraints=vector_constraints, + has_side_effect=False, + ](arg0.slice[2, offset=i](), arg1.slice[2, offset=i]()) ) return res From 9093717737eacc51d137388168878c983d044482 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 25 Sep 2024 21:55:58 -0700 Subject: [PATCH 1616/2019] [Stdlib][GPU] Add math limits to the fp8 dtypes MODULAR_ORIG_COMMIT_REV_ID: ff6910f47bd3668202edefe4fc5f525da18feafd --- stdlib/src/utils/numerics.mojo | 54 ++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 9263c512c4..68eaac6b72 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -462,7 +462,21 @@ fn nan[type: DType]() -> Scalar[type]: """ @parameter - if type is DType.float16: + if type is DType.float8e5m2: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], + ]() + ) + elif type is DType.float8e4m3: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], + ]() + ) + elif type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], @@ -517,6 +531,8 @@ fn isnan[ True if val is NaN and False otherwise. """ + constrained[not type.is_float8(), "fp8 type is not currently supported"]() + @parameter if not type.is_floating_point(): return False @@ -555,7 +571,21 @@ fn inf[type: DType]() -> Scalar[type]: """ @parameter - if type is DType.float16: + if type is DType.float8e5m2: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type is DType.float8e4m3: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], @@ -608,7 +638,21 @@ fn neg_inf[type: DType]() -> Scalar[type]: """ @parameter - if type is DType.float16: + if type is DType.float8e5m2: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], + ]() + ) + elif type is DType.float8e4m3: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], + ]() + ) + elif type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ _type = __mlir_type[`!pop.scalar`], @@ -679,6 +723,10 @@ fn max_finite[type: DType]() -> Scalar[type]: return 9223372036854775807 elif type is DType.uint64: return 18446744073709551615 + elif type is DType.float8e4m3: + return 448 + elif type is DType.float8e5m2: + return 57344 elif type is DType.float16: return 65504 elif type is DType.bfloat16: From bd48f2fceb0f4adbfccacdeffe7010051686111f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 25 Sep 2024 23:18:08 -0600 Subject: [PATCH 1617/2019] [mojo-lang] Teach optimizer about 'ref' argument mutability This teaches the Mojo optimizer that 'ref' arguments that could be mutable have exclusivity enforced, and are therefore 'noalias'. This enables the LLVM optimizer to be more aggressive. This fixes MOCO-914 and Fixes https://github.com/modularml/mojo/issues/3544 MODULAR_ORIG_COMMIT_REV_ID: 5c01d1241d818cdce96a54ae93b91e4ec0e064b9 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 37d1b5cd2e..5db819342a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -186,4 +186,7 @@ what we publish. - [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init causing use of uninitialized variable +- [Issue #3544](https://github.com/modularml/mojo/issues/3544) - Potentially + mutable `ref` argument are not optimized as `noalias` by LLVM. + - The VS Code extension now auto-updates its private copy of the MAX SDK. From 969095620c36e055af6af2c78f2e7e030e982217 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 26 Sep 2024 09:06:21 -0700 Subject: [PATCH 1618/2019] [Stdlib][GPU] Fix typo in sub for sm_80 MODULAR_ORIG_COMMIT_REV_ID: d2c6284ecaed676db038f3f755ac032769dbdcdc --- stdlib/src/builtin/simd.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b316e111a0..2b791460e5 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -570,7 +570,7 @@ struct SIMD[type: DType, size: Int]( vector_constraints="=r,r,r", ](self, rhs) elif _is_sm_8x() and type.is_half_float(): - return self.fma(-1, rhs) + return rhs.fma(-1, self) return __mlir_op.`pop.sub`(self.value, rhs.value) From 386e5787433303278034c098b878d4c460126d4d Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 26 Sep 2024 09:23:22 -0700 Subject: [PATCH 1619/2019] [mojo] Fix a bunch of parameter binding issues. Chiefly, forwarding a variadic now requires `*Ts`. Note that unpacking syntax only works when forwarding a variadic. MODULAR_ORIG_COMMIT_REV_ID: a182cc5105038344e1e12862c4bef1445f1b5666 --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/simd.mojo | 8 ++++---- stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/python/python_object.mojo | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 4dfd6d8c07..1f9db19af1 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -33,7 +33,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): Ts: The type of the elements. """ - var storage: Tuple[Ts] + var storage: Tuple[*Ts] """The underlying storage for the list.""" # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 379b4c9c25..e385b2e1f3 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -815,7 +815,7 @@ struct object( self._value = impl @always_inline - fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): """Initializes the object from a list literal. Parameters: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2b791460e5..d16cdd0066 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1863,13 +1863,13 @@ struct SIMD[type: DType, size: Int]( return array - alias length = variadic_len[mask]() + alias length = variadic_len[*mask]() constrained[ output_size == length, "size of the mask must match the output SIMD size", ]() return __mlir_op.`pop.simd.shuffle`[ - mask = _convert_variadic_to_pop_array[mask](), + mask = _convert_variadic_to_pop_array[*mask](), _type = __mlir_type[ `!pop.simd<`, output_size.value, `, `, type.value, `>` ], @@ -1922,7 +1922,7 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self)[permutation[i]]`. """ - return self._shuffle_list[mask](self) + return self._shuffle_list[*mask](self) @always_inline("nodebug") fn shuffle[*mask: Int](self, other: Self) -> Self: @@ -1940,7 +1940,7 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self + other)[permutation[i]]`. """ - return self._shuffle_list[mask](other) + return self._shuffle_list[*mask](other) @always_inline("nodebug") fn shuffle[mask: StaticIntTuple[size]](self) -> Self: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 9e9afcb1a0..dd97bc17fc 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -59,7 +59,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): fn __init__( inout self, *, - owned storage: VariadicPack[_, CollectionElement, element_types], + owned storage: VariadicPack[_, CollectionElement, *element_types], ): """Construct the tuple from a low-level internal representation. diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 33806b02e7..c937ae9a5b 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -416,7 +416,7 @@ struct PythonObject( self.py_object = cpython.toPython(string._strref_dangerous()) string._strref_keepalive() - fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): """Initialize the object from a list literal. Parameters: @@ -458,7 +458,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) - fn __init__[*Ts: CollectionElement](inout self, value: Tuple[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: Tuple[*Ts]): """Initialize the object from a tuple literal. Parameters: From b98acdf3a0c3f523ba132509566a2fce72dca7d9 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 26 Sep 2024 13:52:18 -0700 Subject: [PATCH 1620/2019] [stdlib] Change `Stringable` to `Formattable` in `debug_assert` Moves `Stringable` to `Formattable` for the variadic args in `debug_assert` that builds the error message. The IR generated is exactly the same, with no runtime penalty or IR bloat, so long as there are no side-effects in the condition. MODULAR_ORIG_COMMIT_REV_ID: 834ca965d4cced4d49d491779cd9d6a8b3cf8168 --- stdlib/src/builtin/debug_assert.mojo | 59 +++++++------------ stdlib/test/builtin/test_debug_assert.mojo | 17 ++++++ .../builtin/test_debug_assert_warning.mojo | 12 ++-- 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index b43eff0afb..71e16e06d2 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -31,10 +31,9 @@ alias _ERROR_ON_ASSERT = is_debug_build() or is_defined[ alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() -# TODO(MOCO-358) Deduplicate and simplify when variadic unpacking is supported @always_inline fn debug_assert[ - func: fn () capturing [_] -> Bool, *Ts: Stringable + func: fn () capturing [_] -> Bool, *Ts: Formattable ](*messages: *Ts): """Asserts that the condition is true. @@ -60,26 +59,13 @@ fn debug_assert[ if func(): return - # Only allocate and build a formatted `String` on CPU - @parameter - if triple_is_nvidia_cuda(): - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]("", __call_location()) - else: - message = String() - - @parameter - fn build_message[i: Int, T: Stringable](value: T): - message += str(value) + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( + messages, __call_location() + ) - messages.each_idx[build_message]() - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( - message, __call_location() - ) - -# TODO(MOCO-358) Deduplicate and simplify when variadic unpacking is supported @always_inline -fn debug_assert[*Ts: Stringable](cond: Bool, *messages: *Ts): +fn debug_assert[*Ts: Formattable](cond: Bool, *messages: *Ts): """Asserts that the condition is true. The `debug_assert` is similar to `assert` in C++. It is a no-op in release @@ -101,28 +87,15 @@ fn debug_assert[*Ts: Stringable](cond: Bool, *messages: *Ts): if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: if cond: return - - # Only allocate and build a formatted `String` on CPU - @parameter - if triple_is_nvidia_cuda(): - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]("", __call_location()) - else: - message = String() - - @parameter - fn build_message[i: Int, T: Stringable](value: T): - message += str(value) - - messages.each_idx[build_message]() - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( - message, __call_location() - ) + _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( + messages, __call_location() + ) @no_inline fn _debug_assert_msg[ - message_type: Stringable, //, *, is_warning: Bool = False -](msg: message_type, loc: _SourceLocation): + is_warning: Bool = False +](messages: VariadicPack[_, Formattable, *_], loc: _SourceLocation): """Aborts with (or prints) the given message and location. This function is intentionally marked as no_inline to reduce binary size. @@ -142,9 +115,17 @@ fn _debug_assert_msg[ else: abort() else: + var message = String() + var writer = message._unsafe_to_formatter() + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(writer) + + messages.each[write_arg]() @parameter if is_warning: - print(loc.prefix("Assert Warning:"), str(msg)) + print(loc.prefix("Assert Warning:"), message) else: - abort(loc.prefix("Assert Error: " + str(msg))) + abort(loc.prefix("Assert Error: " + message)) diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index 7bad619fe3..d8f6f5603f 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -20,6 +20,7 @@ def main(): test_debug_assert() test_debug_assert_multiple_args() + test_debug_assert_formattable() # CHECK-OK-LABEL: test_debug_assert @@ -37,3 +38,19 @@ def test_debug_assert_multiple_args(): debug_assert(True, "passing mutliple args: ", 42, ", ", 4.2) # CHECK-OK: is reached print("is reached") + + +# CHECK-OK-LABEL: test_debug_assert_formattable +def test_debug_assert_formattable(): + print("== test_debug_assert_formattable") + debug_assert(True, FormattableOnly("failed with Formattable arg")) + # CHECK-OK: is reached + print("is reached") + + +@value +struct FormattableOnly: + var message: String + + fn format_to(self, inout writer: Formatter): + writer.write(self.message) diff --git a/stdlib/test/builtin/test_debug_assert_warning.mojo b/stdlib/test/builtin/test_debug_assert_warning.mojo index 32d9b92310..6d378173e1 100644 --- a/stdlib/test/builtin/test_debug_assert_warning.mojo +++ b/stdlib/test/builtin/test_debug_assert_warning.mojo @@ -14,17 +14,15 @@ # This file only tests the debug_assert function # # ===----------------------------------------------------------------------=== # -# RUN: %bare-mojo -D ASSERT_WARNING -debug-level full %s | FileCheck %s -check-prefix=CHECK-WARN +# RUN: %bare-mojo -D ASSERT_WARNING %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK -# CHECK-WARN: test_ok +# # CHECK-OK-LABEL: test_ok fn main(): print("== test_ok") - # CHECK-WARN: At {{.*}}test_debug_assert_warning.mojo:25:17: - # CHECK-WARN-SAME: Assert Warning: failed, but we don't terminate + # CHECK-OK: test_debug_assert_warning.mojo:24:17: Assert Warning: failed, but we don't terminate debug_assert(False, "failed, but we don't terminate") - # CHECK-WARN: At {{.*}}test_debug_assert_warning.mojo:28:17: - # CHECK-WARN-SAME: Assert Warning: also failed, but in a Boolable + # CHECK-OK: test_debug_assert_warning.mojo:26:17: Assert Warning: also failed, but in a Boolable debug_assert(0, Error("also failed, but in a Boolable")) - # CHECK-WARN: is reached + # CHECK-OK: is reached print("is reached") From 130b74b758d1718f22dec47eb3a3dac9dd55605f Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 26 Sep 2024 16:06:30 -0500 Subject: [PATCH 1621/2019] [stdlib] feat: Add sys._libc module + use it to simplify DLHandle * Make the error checking in get_symbol() more robust MODULAR_ORIG_COMMIT_REV_ID: 29423f94abf1b955be409a26fcc1514cad49f69d --- stdlib/src/sys/_libc.mojo | 53 +++++++++++++++++++++++++++++++ stdlib/src/sys/ffi.mojo | 66 ++++++++++++++++++++++++++------------- 2 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 stdlib/src/sys/_libc.mojo diff --git a/stdlib/src/sys/_libc.mojo b/stdlib/src/sys/_libc.mojo new file mode 100644 index 0000000000..bde93c660f --- /dev/null +++ b/stdlib/src/sys/_libc.mojo @@ -0,0 +1,53 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements low-level bindings to functions from the C standard library. + +The functions in this module are intended to be thin wrappers around their +C standard library counterparts. These are used to implement higher level +functionality in the rest of the Mojo standard library. +""" + +from memory import UnsafePointer +from sys.ffi import c_char, c_int, OpaquePointer + + +# ===----------------------------------------------------------------------===# +# dlfcn.h — dynamic library operations +# ===----------------------------------------------------------------------===# + + +@always_inline +fn dlerror() -> UnsafePointer[c_char]: + return external_call["dlerror", UnsafePointer[c_char]]() + + +@always_inline +fn dlopen(filename: UnsafePointer[c_char], flags: c_int) -> OpaquePointer: + return external_call["dlopen", OpaquePointer](filename, flags) + + +fn dlclose(handle: OpaquePointer) -> c_int: + return external_call["dlclose", c_int](handle) + + +@always_inline +fn dlsym[ + # Default `dlsym` result is an OpaquePointer. + result_type: AnyType = NoneType +]( + handle: OpaquePointer, + name: UnsafePointer[c_char], +) -> UnsafePointer[ + result_type +]: + return external_call["dlsym", UnsafePointer[result_type]](handle, name) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 0a9294c581..8cb52f302f 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -21,6 +21,8 @@ from .info import os_is_linux, os_is_windows, is_64bit, os_is_macos from .intrinsics import _mlirtype_is_eq from builtin.builtin_list import _LITRefPackHelper +from sys._libc import dlerror, dlopen, dlclose, dlsym + alias c_char = Int8 """C `char` type.""" @@ -108,7 +110,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): The library is loaded on initialization and unloaded by `close`. """ - var handle: UnsafePointer[Int8] + var handle: OpaquePointer """The handle to the dynamic library.""" # TODO(#15590): Implement support for windows and remove the always_inline. @@ -124,17 +126,13 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @parameter if not os_is_windows(): - var handle = external_call["dlopen", UnsafePointer[Int8]]( - path.unsafe_cstr_ptr(), flags - ) - if handle == UnsafePointer[Int8](): - var error_message = external_call[ - "dlerror", UnsafePointer[UInt8] - ]() + var handle = dlopen(path.unsafe_cstr_ptr(), flags) + if handle == OpaquePointer(): + var error_message = dlerror() abort("dlopen failed: " + String(error_message)) self.handle = handle else: - self.handle = UnsafePointer[Int8]() + self.handle = OpaquePointer() fn __init__(inout self, *, other: Self): """Copy the object. @@ -158,13 +156,12 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): "Checking dynamic library symbol is not supported on Windows", ]() - var opaque_function_ptr = external_call["dlsym", UnsafePointer[Int8]]( - self.handle.address, name.unsafe_cstr_ptr() + var opaque_function_ptr: OpaquePointer = dlsym( + self.handle, + name.unsafe_cstr_ptr(), ) - if opaque_function_ptr: - return True - return False + return bool(opaque_function_ptr) # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline @@ -174,8 +171,8 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @parameter if not os_is_windows(): - _ = external_call["dlclose", Int](self.handle) - self.handle = UnsafePointer[Int8]() + _ = dlclose(self.handle) + self.handle = OpaquePointer() fn __bool__(self) -> Bool: """Checks if the handle is valid. @@ -281,15 +278,42 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): debug_assert(self.handle, "Dylib handle is null") @parameter - if not os_is_windows(): - return external_call["dlsym", UnsafePointer[result_type]]( - self.handle.address, name - ) - else: + if os_is_windows(): return abort[UnsafePointer[result_type]]( "get_symbol isn't supported on windows" ) + # To check for `dlsym()` results that are _validly_ NULL, we do the + # dance described in https://man7.org/linux/man-pages/man3/dlsym.3.html: + # + # > In unusual cases (see NOTES) the value of the symbol could + # > actually be NULL. Therefore, a NULL return from dlsym() need not + # > indicate an error. The correct way to distinguish an error from + # > a symbol whose value is NULL is to call dlerror(3) to clear any + # > old error conditions, then call dlsym(), and then call dlerror(3) + # > again, saving its return value into a variable, and check whether + # > this saved value is not NULL. + + var res = dlsym[result_type](self.handle, name) + + if not res: + # Clear any potential unrelated error that pre-dates the `dlsym` + # call above. + _ = dlerror() + + # Redo the `dlsym` call + res = dlsym[result_type](self.handle, name) + + debug_assert(not res, "dlsym unexpectedly returned non-NULL result") + + # Check if an error occurred during the 2nd `dlsym` call. + var err = dlerror() + + if err: + abort("dlsym failed: " + String(err)) + + return res + # ===----------------------------------------------------------------------===# # Library Load From fa5a684bd7d29fe2f33194bf09dbbe61f54beca7 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 26 Sep 2024 14:52:36 -0700 Subject: [PATCH 1622/2019] [mojo-lang] Don't mark `ref` args with parametric mutability noalias (#47896) This refines #47892 to be more conservative and align with exclusivity checking. If you have a function that takes an argument as a ref with parametric mutability, it can be instantiated with an immutable ref and exclusivity checking will allow it to alias other immutable reference arguments. As such, we can't mark it noalias. This changes the compiler to only mark `ref` arguments with known mutability as `noalias`. In practice, it won't matter anyway, because parametric mut references cannot be mutated. MODULAR_ORIG_COMMIT_REV_ID: c4a4ab1cf1f2a2979913be07c7c0a799ad492ca1 --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5db819342a..4706256f3e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -186,7 +186,7 @@ what we publish. - [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init causing use of uninitialized variable -- [Issue #3544](https://github.com/modularml/mojo/issues/3544) - Potentially +- [Issue #3544](https://github.com/modularml/mojo/issues/3544) - Known mutable `ref` argument are not optimized as `noalias` by LLVM. - The VS Code extension now auto-updates its private copy of the MAX SDK. From 6ca63cfb6019a11d7c84bc4d3521afdda7263b90 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 26 Sep 2024 14:57:57 -0700 Subject: [PATCH 1623/2019] [mojo-stdlib] Propagate lifetimes through `UnsafePointer` methods Now that `UnsafePointer` has the ability to represent a lifetime, we should make sure it is propagated by default through various methods like `bitcast`. This also eliminates an unused overload of `memcpy`, which can be implemented by giving `memcpy` a default count of 1 if we want this. MODULAR_ORIG_COMMIT_REV_ID: b098f76910fd0660e14a750453f572fbdb951165 --- stdlib/src/memory/memory.mojo | 74 ++++++--------------------- stdlib/src/memory/unsafe_pointer.mojo | 51 ++++++++++++------ 2 files changed, 52 insertions(+), 73 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 5855800ca6..ccc0689bfb 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -142,62 +142,7 @@ fn memcmp[ @always_inline -fn memcpy[count: Int](dest: UnsafePointer, src: __type_of(dest)): - """Copies a memory area. - - Parameters: - count: The number of elements to copy (not bytes!). - - Args: - dest: The destination pointer. - src: The source pointer. - """ - alias n = count * sizeof[dest.type]() - - var dest_data = dest.bitcast[Int8]() - var src_data = src.bitcast[Int8]() - - @parameter - if n < 5: - - @parameter - for i in range(n): - dest_data[i] = src_data[i] - return - - @parameter - if n <= 16: - - @parameter - if n >= 8: - var ui64_size = sizeof[Int64]() - dest_data.bitcast[Int64]()[] = src_data.bitcast[Int64]()[0] - dest_data.offset(n - ui64_size).bitcast[ - Int64 - ]()[] = src_data.offset(n - ui64_size).bitcast[Int64]()[0] - return - - var ui32_size = sizeof[Int32]() - dest_data.bitcast[Int32]()[] = src_data.bitcast[Int32]()[0] - dest_data.offset(n - ui32_size).bitcast[Int32]()[] = src_data.offset( - n - ui32_size - ).bitcast[Int32]()[0] - return - - var dest_ptr = dest_data.bitcast[Int8]() - var src_ptr = src_data.bitcast[Int8]() - - # Copy in 32-byte chunks. - alias chunk_size = 32 - alias vector_end = _align_down(n, chunk_size) - for i in range(0, vector_end, chunk_size): - dest_ptr.store(i, src_ptr.load[width=chunk_size](i)) - for i in range(vector_end, n): - dest_ptr.store(i, src_ptr.load(i)) - - -@always_inline -fn memcpy( +fn _memcpy_impl( dest_data: UnsafePointer[Int8, *_], src_data: __type_of(dest_data), n: Int ): """Copies a memory area. @@ -258,16 +203,29 @@ fn memcpy( @always_inline -fn memcpy(dest: UnsafePointer, src: __type_of(dest), count: Int): +fn memcpy[ + T: AnyType +]( + dest: UnsafePointer[T, AddressSpace.GENERIC, *_], + src: UnsafePointer[T, AddressSpace.GENERIC, *_], + count: Int, +): """Copies a memory area. + Parameters: + T: The element type. + Args: dest: The destination pointer. src: The source pointer. count: The number of elements to copy. """ var n = count * sizeof[dest.type]() - memcpy(dest.bitcast[Int8](), src.bitcast[Int8](), n) + _memcpy_impl( + dest.bitcast[Int8, lifetime=MutableAnyLifetime](), + src.bitcast[Int8, lifetime=MutableAnyLifetime](), + n, + ) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index a20a5d24fb..41090388fb 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -129,7 +129,15 @@ struct UnsafePointer[ @staticmethod @always_inline("nodebug") - fn address_of(ref [_, address_space._value.value]arg: type) -> Self: + fn address_of( + ref [_, address_space._value.value]arg: type + ) -> UnsafePointer[ + type, + address_space, + False, + 1, + # TODO: Propagate lifetime of the argument. + ] as result: """Gets the address of the argument. Args: @@ -138,7 +146,9 @@ struct UnsafePointer[ Returns: An UnsafePointer which contains the address of the argument. """ - return Self(__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg))) + return __type_of(result)( + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(arg)) + ) @staticmethod @always_inline @@ -171,13 +181,13 @@ struct UnsafePointer[ A reference to the value. """ - # We're unsafe, so we can have unsafe things. References we make have - # an 'any' mutable lifetime, since UnsafePointer is allowed to alias - # anything. + # We're unsafe, so we can have unsafe things. alias _ref_type = Reference[type, lifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( - UnsafePointer[type, address_space, False](self).address + UnsafePointer[type, address_space, False, alignment, lifetime]( + self + ).address ) ) @@ -410,7 +420,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn as_noalias_ptr( self, - ) -> UnsafePointer[type, address_space, True, alignment]: + ) -> UnsafePointer[type, address_space, True, alignment, lifetime]: """Cast the pointer to a new pointer that is known not to locally alias any other pointer. In other words, the pointer transitively does not alias any other memory value declared in the local function context. @@ -783,12 +793,16 @@ struct UnsafePointer[ T: AnyType = Self.type, /, address_space: AddressSpace = Self.address_space, - ](self) -> UnsafePointer[T, address_space, alignment=alignment]: + lifetime: Lifetime[True].type = Self.lifetime, + ](self) -> UnsafePointer[ + T, address_space, Self.exclusive, alignment, lifetime + ]: """Bitcasts a UnsafePointer to a different type. Parameters: T: The target type. address_space: The address space of the result. + lifetime: Lifetime of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, @@ -805,12 +819,16 @@ struct UnsafePointer[ T: DType, /, address_space: AddressSpace = Self.address_space, - ](self) -> UnsafePointer[Scalar[T], address_space, alignment=alignment]: + lifetime: Lifetime[True].type = Self.lifetime, + ](self) -> UnsafePointer[ + Scalar[T], address_space, Self.exclusive, alignment, lifetime + ]: """Bitcasts a UnsafePointer to a different type. Parameters: T: The target type. address_space: The address space of the result. + lifetime: Lifetime of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, @@ -819,7 +837,7 @@ struct UnsafePointer[ return self.bitcast[Scalar[T], address_space]() @always_inline - fn destroy_pointee(self: UnsafePointer[type, alignment=alignment]): + fn destroy_pointee(self: UnsafePointer[type, AddressSpace.GENERIC, *_]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed @@ -833,7 +851,7 @@ struct UnsafePointer[ @always_inline fn take_pointee[ T: Movable, //, - ](self: UnsafePointer[T]) -> T: + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_]) -> T: """Move the value at the pointer out, leaving it uninitialized. The pointer must not be null, and the pointer memory location is assumed @@ -856,7 +874,7 @@ struct UnsafePointer[ @always_inline fn init_pointee_move[ T: Movable, //, - ](self: UnsafePointer[T], owned value: T): + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_], owned value: T): """Emplace a new value into the pointer location, moving from `value`. The pointer memory location is assumed to contain uninitialized data, @@ -878,7 +896,7 @@ struct UnsafePointer[ @always_inline fn init_pointee_copy[ T: Copyable, //, - ](self: UnsafePointer[T], value: T): + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_], value: T): """Emplace a copy of `value` into the pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -900,7 +918,7 @@ struct UnsafePointer[ @always_inline fn init_pointee_explicit_copy[ T: ExplicitlyCopyable, // - ](self: UnsafePointer[T], value: T): + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_], value: T): """Emplace a copy of `value` into this pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -923,7 +941,10 @@ struct UnsafePointer[ @always_inline fn move_pointee_into[ T: Movable, //, - ](self: UnsafePointer[T], dst: UnsafePointer[T]): + ]( + self: UnsafePointer[T, AddressSpace.GENERIC, *_], + dst: UnsafePointer[T, AddressSpace.GENERIC, *_], + ): """Moves the value `self` points to into the memory location pointed to by `dst`. From 3ccffa3626c1833a12713bd8814be7fd4095f35d Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 26 Sep 2024 16:23:22 -0700 Subject: [PATCH 1624/2019] [stdlib] Add `assert_mode` to `debug_assert` This enables setting an assert level with `-D ASSERT=[assert-mode]`, the main purpose of this is to enable default CPU safety assertions. Currently the levels are: - none: all assertions off - warn: print assertion errors, previously `-D ASSERT_WARNING` - safe: the default mode for standard CPU safety assetions - all: turn on all assertions, previously `-D MOJO_ENABLE_ASSERTIONS` MODULAR_ORIG_COMMIT_REV_ID: 29c070732066b11f9c7bed0717a271a34f77539a --- stdlib/docs/development.md | 2 +- stdlib/src/builtin/debug_assert.mojo | 198 ++++++++++++++---- ...o => test_debug_assert_default_error.mojo} | 12 +- .../builtin/test_debug_assert_mode_all.mojo | 30 +++ ... => test_debug_assert_mode_all_error.mojo} | 2 +- .../builtin/test_debug_assert_mode_none.mojo | 40 ++++ .../builtin/test_debug_assert_warning.mojo | 2 +- .../builtin/test_range_out_of_bounds.mojo | 2 +- .../test_range_uint_reverse_range_bad.mojo | 2 +- stdlib/test/lit.cfg.py | 2 +- 10 files changed, 237 insertions(+), 55 deletions(-) rename stdlib/test/builtin/{test_debug_assert_mojo_enable_assertions.mojo => test_debug_assert_default_error.mojo} (72%) create mode 100644 stdlib/test/builtin/test_debug_assert_mode_all.mojo rename stdlib/test/builtin/{test_debug_assert_error.mojo => test_debug_assert_mode_all_error.mojo} (91%) create mode 100644 stdlib/test/builtin/test_debug_assert_mode_none.mojo diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 8afdb6ff91..e813810f67 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -89,7 +89,7 @@ run all the tests automatically. ./stdlib/scripts/run-tests.sh ``` -Note that tests are run with `-D MOJO_ENABLE_ASSERTIONS`. +Note that tests are run with `-D ASSERT=all`. If you wish to run the unit tests that are in a specific test file, you can do so with diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 71e16e06d2..53cd7babbd 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -19,83 +19,193 @@ These are Mojo built-ins, so you don't need to import them. from os import abort from sys import is_defined, triple_is_nvidia_cuda from sys._build import is_debug_build +from sys.param_env import env_get_string from builtin._location import __call_location, _SourceLocation -# Print an error and fail. -alias _ERROR_ON_ASSERT = is_debug_build() or is_defined[ - "MOJO_ENABLE_ASSERTIONS" -]() -# Print a warning, but do not fail (useful for testing assert behavior). -alias _WARN_ON_ASSERT = is_defined["ASSERT_WARNING"]() +@no_inline +fn _assert_enabled[assert_mode: StringLiteral]() -> Bool: + alias defined_mode = env_get_string["ASSERT", "safe"]() + constrained[ + defined_mode == "none" + or defined_mode == "warn" + or defined_mode == "safe" + or defined_mode == "all", + "-D ASSERT=" + + defined_mode + + " but must be one of: none, warn, safe, all", + ]() + constrained[ + assert_mode == "none" or assert_mode == "safe", + "assert_mode=" + assert_mode + " but must be one of: none, safe", + ]() + + @parameter + if defined_mode == "none": + return False + elif defined_mode == "all" or defined_mode == "warn" or is_debug_build(): + return True + else: + return defined_mode == assert_mode @always_inline fn debug_assert[ - func: fn () capturing [_] -> Bool, *Ts: Formattable + cond: fn () capturing [_] -> Bool, + assert_mode: StringLiteral = "none", + *Ts: Formattable, ](*messages: *Ts): """Asserts that the condition is true. - The `debug_assert` is similar to `assert` in C++. It is a no-op in release - builds unless MOJO_ENABLE_ASSERTIONS is defined. - - Right now, users of the mojo-sdk must explicitly specify `-D MOJO_ENABLE_ASSERTIONS` - to enable assertions. It is not sufficient to compile programs with `-debug-level full` - for enabling assertions in the library. - Parameters: - func: The function to invoke to check if the assertion holds. Can be used - if the function is side-effecting, in which case a debug_assert taking - a Bool will evaluate the expression producing the Bool even in release mode. - Ts: The element types conforming to `Stringable` for the message. + cond: The function to invoke to check if the assertion holds. + assert_mode: Determines when the assert is turned on. + Ts: The element types conforming to `Formattable` for the message. Args: messages: Arguments to convert to a `String` message. + + + You can pass in multiple args that are `Formattable` to generate a formatted + message, by default this will be a no-op: + + ```mojo + x = 0 + debug_assert(x > 0, "expected x to be more than 0 but got: ", x) + ``` + + You can change the assertion level for example: + + ```sh + mojo -D ASSERT=all main.mojo + ``` + + Assertion modes: + + - none: turn off all assertions for performance at the cost of safety. + - warn: print any errors instead of aborting. + - safe: standard safety checks that are on even in release builds. + - all: turn on all assertions. + + You can set the `assert_mode` to `safe` so the assertion runs even in + release builds: + + ```mojo + debug_assert[assert_mode="safe"]( + x > 0, "expected x to be more than 0 but got: ", x + ) + ``` + + To ensure that you have no runtime penality from your assertion in release + builds, make sure there are no side effects in your message and condition. + Take this example: + + ```mojo + person = "name: john, age: 50" + name = "john" + debug_assert(str("name: ") + name == person, "unexpected name") + ``` + + This will have a runtime penality due to allocating a `String` in the + condition even in release builds, you must put the condition inside a + closure so it only runs when the assertion is turned on: + + ```mojo + fn check_name() capturing -> Bool: + return str("name: ") + name == person + + debug_assert[check_name]("unexpected name") + ``` + . """ @parameter - if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: - if func(): + if _assert_enabled[assert_mode](): + if cond(): return - - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( - messages, __call_location() - ) + _debug_assert_msg(messages, __call_location()) @always_inline -fn debug_assert[*Ts: Formattable](cond: Bool, *messages: *Ts): +fn debug_assert[ + assert_mode: StringLiteral = "none", + *Ts: Formattable, +](cond: Bool, *messages: *Ts): """Asserts that the condition is true. - The `debug_assert` is similar to `assert` in C++. It is a no-op in release - builds unless MOJO_ENABLE_ASSERTIONS is defined. - - Right now, users of the mojo-sdk must explicitly specify `-D MOJO_ENABLE_ASSERTIONS` - to enable assertions. It is not sufficient to compile programs with `-debug-level full` - for enabling assertions in the library. - Parameters: - Ts: The element types conforming to `Stringable` for the message. + assert_mode: Determines when the assert is turned on. + Ts: The element types conforming to `Formattable` for the message. Args: cond: The bool value to assert. messages: Arguments to convert to a `String` message. + + You can pass in multiple args that are `Formattable` to generate a formatted + message, by default this will be a no-op: + + ```mojo + x = 0 + debug_assert(x > 0, "expected x to be more than 0 but got: ", x) + ``` + + You can change the assertion level for example: + + ```sh + mojo -D ASSERT=all main.mojo + ``` + + Assertion modes: + + - none: turn off all assertions for performance at the cost of safety. + - warn: print any errors instead of aborting. + - safe: standard safety checks that are on even in release builds. + - all: turn on all assertions. + + You can set the `assert_mode` to `safe` so the assertion runs even in + release builds: + + ```mojo + debug_assert[assert_mode="safe"]( + x > 0, "expected x to be more than 0 but got: ", x + ) + ``` + + To ensure that you have no runtime penality from your assertion in release + builds, make sure there are no side effects in your message and condition. + Take this example: + + ```mojo + person = "name: john, age: 50" + name = "john" + debug_assert(str("name: ") + name == person, "unexpected name") + ``` + + This will have a runtime penality due to allocating a `String` in the + condition even in release builds, you must put the condition inside a + closure so it only runs when the assertion is turned on: + + ```mojo + fn check_name() capturing -> Bool: + return str("name: ") + name == person + + debug_assert[check_name]("unexpected name") + ``` + . """ @parameter - if _ERROR_ON_ASSERT or _WARN_ON_ASSERT: + if _assert_enabled[assert_mode](): if cond: return - _debug_assert_msg[is_warning=_WARN_ON_ASSERT]( - messages, __call_location() - ) + _debug_assert_msg(messages, __call_location()) @no_inline -fn _debug_assert_msg[ - is_warning: Bool = False -](messages: VariadicPack[_, Formattable, *_], loc: _SourceLocation): +fn _debug_assert_msg( + messages: VariadicPack[_, Formattable, *_], loc: _SourceLocation +): """Aborts with (or prints) the given message and location. This function is intentionally marked as no_inline to reduce binary size. @@ -104,13 +214,13 @@ fn _debug_assert_msg[ an indirect recursion of @always_inline functions is possible (e.g. because abort's implementation could use debug_assert) """ + alias defined_mode = env_get_string["ASSERT", "safe"]() @parameter if triple_is_nvidia_cuda(): # On GPUs, assert shouldn't allocate. - @parameter - if is_warning: + if defined_mode == "warn": print("Assert Warning") else: abort() @@ -125,7 +235,7 @@ fn _debug_assert_msg[ messages.each[write_arg]() @parameter - if is_warning: - print(loc.prefix("Assert Warning:"), message) + if defined_mode == "warn": + print(loc.prefix("Assert Warning: " + message)) else: abort(loc.prefix("Assert Error: " + message)) diff --git a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo b/stdlib/test/builtin/test_debug_assert_default_error.mojo similarity index 72% rename from stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo rename to stdlib/test/builtin/test_debug_assert_default_error.mojo index 8e6ce41119..6b5ef51e0e 100644 --- a/stdlib/test/builtin/test_debug_assert_mojo_enable_assertions.mojo +++ b/stdlib/test/builtin/test_debug_assert_default_error.mojo @@ -15,13 +15,15 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %bare-mojo -D MOJO_ENABLE_ASSERTIONS -debug-level full %s 2>&1 | FileCheck %s +# RUN: not --crash %bare-mojo %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL -# CHECK-LABEL: test_fail +# CHECK-FAIL-LABEL: test_fail fn main(): print("== test_fail") - # CHECK: Assert Error: this fails - debug_assert(False, "this fails") - # CHECK-NOT: is never reached + # CHECK-FAIL: formatted failure message: 2, 4 + debug_assert[assert_mode="safe"]( + False, "formatted failure message: ", 2, ", ", Scalar[DType.uint8](4) + ) + # CHECK-FAIL-NOT: is never reached print("is never reached") diff --git a/stdlib/test/builtin/test_debug_assert_mode_all.mojo b/stdlib/test/builtin/test_debug_assert_mode_all.mojo new file mode 100644 index 0000000000..61e3fdf43e --- /dev/null +++ b/stdlib/test/builtin/test_debug_assert_mode_all.mojo @@ -0,0 +1,30 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# +# This file only tests the debug_assert function +# +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s | FileCheck %s -check-prefix=CHECK-OK + + +def main(): + test_debug_assert_mode_all_true() + + +# CHECK-OK-LABEL: test_debug_assert_mode_all_true +def test_debug_assert_mode_all_true(): + print("== test_debug_assert_mode_all_true") + debug_assert(True, "ok") + debug_assert[assert_mode="safe"](True, "ok") + # CHECK-OK: is reached + print("is reached") diff --git a/stdlib/test/builtin/test_debug_assert_error.mojo b/stdlib/test/builtin/test_debug_assert_mode_all_error.mojo similarity index 91% rename from stdlib/test/builtin/test_debug_assert_error.mojo rename to stdlib/test/builtin/test_debug_assert_mode_all_error.mojo index c6f5267717..88e11c0ad8 100644 --- a/stdlib/test/builtin/test_debug_assert_error.mojo +++ b/stdlib/test/builtin/test_debug_assert_mode_all_error.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %bare-mojo -D BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL +# RUN: not --crash %bare-mojo -D ASSERT=all %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL # CHECK-FAIL-LABEL: test_fail diff --git a/stdlib/test/builtin/test_debug_assert_mode_none.mojo b/stdlib/test/builtin/test_debug_assert_mode_none.mojo new file mode 100644 index 0000000000..c5db5428cd --- /dev/null +++ b/stdlib/test/builtin/test_debug_assert_mode_none.mojo @@ -0,0 +1,40 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# +# This file only tests the debug_assert function +# +# ===----------------------------------------------------------------------=== # +# RUN: %bare-mojo -D ASSERT=none %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK + + +def main(): + test_debug_assert_mode_none_true() + test_debug_assert_mode_none_false() + + +# CHECK-OK-LABEL: test_debug_assert_mode_none_true +def test_debug_assert_mode_none_true(): + print("== test_debug_assert_mode_none_true") + + debug_assert[assert_mode="safe"](True, "ok") + # CHECK-OK: is reached + print("is reached") + + +# CHECK-OK-LABEL: test_debug_assert_mode_none_false +def test_debug_assert_mode_none_false(): + print("== test_debug_assert_mode_none_false") + debug_assert(False, "ok") + debug_assert[assert_mode="safe"](False, "ok") + # CHECK-OK: is reached + print("is reached") diff --git a/stdlib/test/builtin/test_debug_assert_warning.mojo b/stdlib/test/builtin/test_debug_assert_warning.mojo index 6d378173e1..faa4530b38 100644 --- a/stdlib/test/builtin/test_debug_assert_warning.mojo +++ b/stdlib/test/builtin/test_debug_assert_warning.mojo @@ -14,7 +14,7 @@ # This file only tests the debug_assert function # # ===----------------------------------------------------------------------=== # -# RUN: %bare-mojo -D ASSERT_WARNING %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK +# RUN: %bare-mojo -D ASSERT=warn %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK # # CHECK-OK-LABEL: test_ok diff --git a/stdlib/test/builtin/test_range_out_of_bounds.mojo b/stdlib/test/builtin/test_range_out_of_bounds.mojo index 24e15b6f7d..19c0f5d694 100644 --- a/stdlib/test/builtin/test_range_out_of_bounds.mojo +++ b/stdlib/test/builtin/test_range_out_of_bounds.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash mojo -D MOJO_ENABLE_ASSERTIONS %s 2>&1 +# RUN: not --crash mojo -D ASSERT=all %s 2>&1 from testing import assert_equal diff --git a/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo b/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo index 259f00d13e..87f52e2cd3 100644 --- a/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo +++ b/stdlib/test/builtin/test_range_uint_reverse_range_bad.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash mojo -D MOJO_ENABLE_ASSERTIONS %s 2>&1 +# RUN: not --crash mojo -D ASSERT=all %s 2>&1 from testing import assert_equal diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 593f609ef4..ad8ab36e9a 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -70,7 +70,7 @@ def has_not(): # In the future, we can do other fancy things like with sanitizers # and build type. if bool(int(os.environ.get("MOJO_ENABLE_ASSERTIONS_IN_TESTS", 1))): - base_mojo_command = "mojo -D MOJO_ENABLE_ASSERTIONS" + base_mojo_command = "mojo -D ASSERT=all" else: print("Running tests with assertions disabled.") base_mojo_command = "mojo" From f24766154cf5200ed9447d1a5d9f5da17e27f423 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 26 Sep 2024 17:23:49 -0700 Subject: [PATCH 1625/2019] [stdlib] Add `cpu_only` option to `debug_assert` This allows turning off asserts that have allocations in a closure condition on GPU, and enabling cpu-only default asserts. MODULAR_ORIG_COMMIT_REV_ID: 5e0da3c76fbe87e39ad904119d41c936692b3e20 --- stdlib/src/builtin/debug_assert.mojo | 30 +++++++++++++++---- .../builtin/test_debug_assert_mode_all.mojo | 1 + .../builtin/test_debug_assert_mode_none.mojo | 2 ++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 53cd7babbd..c2260e0db9 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -23,10 +23,11 @@ from sys.param_env import env_get_string from builtin._location import __call_location, _SourceLocation +alias defined_mode = env_get_string["ASSERT", "safe"]() + @no_inline -fn _assert_enabled[assert_mode: StringLiteral]() -> Bool: - alias defined_mode = env_get_string["ASSERT", "safe"]() +fn _assert_enabled[assert_mode: StringLiteral, cpu_only: Bool]() -> Bool: constrained[ defined_mode == "none" or defined_mode == "warn" @@ -42,7 +43,7 @@ fn _assert_enabled[assert_mode: StringLiteral]() -> Bool: ]() @parameter - if defined_mode == "none": + if defined_mode == "none" or (triple_is_nvidia_cuda() and cpu_only): return False elif defined_mode == "all" or defined_mode == "warn" or is_debug_build(): return True @@ -54,6 +55,7 @@ fn _assert_enabled[assert_mode: StringLiteral]() -> Bool: fn debug_assert[ cond: fn () capturing [_] -> Bool, assert_mode: StringLiteral = "none", + cpu_only: Bool = False, *Ts: Formattable, ](*messages: *Ts): """Asserts that the condition is true. @@ -61,6 +63,7 @@ fn debug_assert[ Parameters: cond: The function to invoke to check if the assertion holds. assert_mode: Determines when the assert is turned on. + cpu_only: If true, only run the assert on CPU. Ts: The element types conforming to `Formattable` for the message. Args: @@ -117,11 +120,18 @@ fn debug_assert[ debug_assert[check_name]("unexpected name") ``` + + If you need to allocate, and so don't want the assert to ever run on GPU, + you can set it to CPU only: + + ```mojo + debug_assert[check_name, cpu_only=True]("unexpected name") + ``` . """ @parameter - if _assert_enabled[assert_mode](): + if _assert_enabled[assert_mode, cpu_only](): if cond(): return _debug_assert_msg(messages, __call_location()) @@ -130,12 +140,14 @@ fn debug_assert[ @always_inline fn debug_assert[ assert_mode: StringLiteral = "none", + cpu_only: Bool = False, *Ts: Formattable, ](cond: Bool, *messages: *Ts): """Asserts that the condition is true. Parameters: assert_mode: Determines when the assert is turned on. + cpu_only: If true, only run the assert on CPU. Ts: The element types conforming to `Formattable` for the message. Args: @@ -192,11 +204,18 @@ fn debug_assert[ debug_assert[check_name]("unexpected name") ``` + + If you need to allocate, and so don't want the assert to ever run on GPU, + you can set it to CPU only: + + ```mojo + debug_assert[check_name, cpu_only=True]("unexpected name") + ``` . """ @parameter - if _assert_enabled[assert_mode](): + if _assert_enabled[assert_mode, cpu_only](): if cond: return _debug_assert_msg(messages, __call_location()) @@ -214,7 +233,6 @@ fn _debug_assert_msg( an indirect recursion of @always_inline functions is possible (e.g. because abort's implementation could use debug_assert) """ - alias defined_mode = env_get_string["ASSERT", "safe"]() @parameter if triple_is_nvidia_cuda(): diff --git a/stdlib/test/builtin/test_debug_assert_mode_all.mojo b/stdlib/test/builtin/test_debug_assert_mode_all.mojo index 61e3fdf43e..f6eaa9bf09 100644 --- a/stdlib/test/builtin/test_debug_assert_mode_all.mojo +++ b/stdlib/test/builtin/test_debug_assert_mode_all.mojo @@ -26,5 +26,6 @@ def test_debug_assert_mode_all_true(): print("== test_debug_assert_mode_all_true") debug_assert(True, "ok") debug_assert[assert_mode="safe"](True, "ok") + debug_assert[assert_mode="safe", cpu_only=True](True, "ok") # CHECK-OK: is reached print("is reached") diff --git a/stdlib/test/builtin/test_debug_assert_mode_none.mojo b/stdlib/test/builtin/test_debug_assert_mode_none.mojo index c5db5428cd..e94fdca644 100644 --- a/stdlib/test/builtin/test_debug_assert_mode_none.mojo +++ b/stdlib/test/builtin/test_debug_assert_mode_none.mojo @@ -27,6 +27,7 @@ def test_debug_assert_mode_none_true(): print("== test_debug_assert_mode_none_true") debug_assert[assert_mode="safe"](True, "ok") + debug_assert[assert_mode="safe", cpu_only=True](True, "ok") # CHECK-OK: is reached print("is reached") @@ -36,5 +37,6 @@ def test_debug_assert_mode_none_false(): print("== test_debug_assert_mode_none_false") debug_assert(False, "ok") debug_assert[assert_mode="safe"](False, "ok") + debug_assert[assert_mode="safe", cpu_only=True](False, "ok") # CHECK-OK: is reached print("is reached") From 517b70f6e556639b498a44379386dea77d8c63d8 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 26 Sep 2024 18:10:13 -0700 Subject: [PATCH 1626/2019] [stdlib] Overload for `String.format_sequence` to pass variadic pack This allows passing a variadic pack from another function and formatting it directly without running `args.for_each`. MODULAR_ORIG_COMMIT_REV_ID: 81bde0e5b478ded33e54487e4ab4dace7f8b35a4 --- stdlib/src/builtin/debug_assert.mojo | 9 +-------- stdlib/src/collections/string.mojo | 29 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index c2260e0db9..b7472dc93e 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -243,14 +243,7 @@ fn _debug_assert_msg( else: abort() else: - var message = String() - var writer = message._unsafe_to_formatter() - - @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(writer) - - messages.each[write_arg]() + message = String.format_sequence(messages) @parameter if defined_mode == "warn": diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 82a24beb24..40379a24af 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -879,6 +879,35 @@ struct String( . """ + return Self.format_sequence(args) + + @staticmethod + @no_inline + fn format_sequence(args: VariadicPack[_, Formattable, *_]) -> Self: + """ + Construct a string directly from a variadic pack. + + Args: + args: A VariadicPack of formattable arguments. + + Returns: + A string formed by formatting the VariadicPack. + + Examples: + + ```mojo + fn variadic_pack_to_string[ + *Ts: Formattable, + ](*args: *Ts) -> String: + return String.format_sequence(args) + + string = variadic_pack_to_string(1, ", ", 2.0, ", ", "three") + %# from testing import assert_equal + %# assert_equal(string, "1, 2.0, three") + ``` + . + """ + var output = String() var writer = output._unsafe_to_formatter() From 84ed08c24d6f94e8862470b109dc525647595ae1 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 26 Sep 2024 18:19:34 -0700 Subject: [PATCH 1627/2019] [******] Plumb the FP8 limits to the numerics functions MODULAR_ORIG_COMMIT_REV_ID: 28f768ffa9ef5ce25b0e966fa53b9f1640a41fba --- stdlib/src/utils/numerics.mojo | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 68eaac6b72..fdc69804ec 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -64,7 +64,11 @@ struct FPUtils[ """ @parameter - if type is DType.float16: + if type is DType.float8e4m3: + return 3 + elif type is DType.float8e5m2: + return 2 + elif type is DType.float16: return 10 elif type is DType.bfloat16: return 7 @@ -84,7 +88,11 @@ struct FPUtils[ """ @parameter - if type is DType.float16: + if type is DType.float8e4m3: + return 8 + elif type is DType.float8e5m2: + return 16 + elif type is DType.float16: return 16 elif type is DType.float32 or type is DType.bfloat16: return 128 @@ -102,7 +110,11 @@ struct FPUtils[ """ @parameter - if type is DType.float16: + if type is DType.float8e4m3: + return 4 + elif type is DType.float8e5m2: + return 5 + elif type is DType.float16: return 5 elif type is DType.float32 or type is DType.bfloat16: return 8 @@ -537,9 +549,12 @@ fn isnan[ if not type.is_floating_point(): return False + alias int_dtype = _integral_type_of[type]() + @parameter - if type is DType.bfloat16: - alias int_dtype = _integral_type_of[type]() + if type is DType.float8e4m3: + return bitcast[int_dtype, simd_width](abs(val)) == 0x7F + elif type in (DType.float8e5m2, DType.bfloat16): alias x7FFF = SIMD[int_dtype, simd_width](0x7FFF) alias x7F80 = SIMD[int_dtype, simd_width](0x7F80) return bitcast[int_dtype, simd_width](val) & x7FFF > x7F80 From 0ddcd2c033a32ba3ce8dc86c021df3db048c8745 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 26 Sep 2024 18:44:44 -0700 Subject: [PATCH 1628/2019] Fix markup typos that break in MDX MODULAR_ORIG_COMMIT_REV_ID: df65261a7189829b4fee597909368aa63a51e061 --- docs/manual/values/lifetimes.ipynb | 2 +- docs/tools/debugging.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 299323c49c..b66f8828eb 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -298,7 +298,7 @@ "\n", "The syntax for a `ref` argument is:\n", "\n", - "ref [lifetime] argName: argType\n", + "ref [lifetime] argName: argType\n", "\n", "The `lifetime` parameter passed inside the square brackets can be replaced with\n", "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 4f4a62bf31..78dc2e31e7 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -311,7 +311,7 @@ "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", "\n", "For more information, see the VS Code documentation for [Launch \n", - "configurations]](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", + "configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", "\n", ":::note Compilation options\n", "\n", From 9fe3f8946460ed11d395cb947140f82d4c433122 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 26 Sep 2024 19:57:36 -0700 Subject: [PATCH 1629/2019] [******][Numerics] Fix an error in the isnan for float8e5m2 MODULAR_ORIG_COMMIT_REV_ID: 1cf6e7ef35a736325f6ecc65b26cfe05ce4ea641 --- stdlib/src/utils/numerics.mojo | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index fdc69804ec..f880e0eb06 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -543,8 +543,6 @@ fn isnan[ True if val is NaN and False otherwise. """ - constrained[not type.is_float8(), "fp8 type is not currently supported"]() - @parameter if not type.is_floating_point(): return False @@ -553,8 +551,15 @@ fn isnan[ @parameter if type is DType.float8e4m3: - return bitcast[int_dtype, simd_width](abs(val)) == 0x7F - elif type in (DType.float8e5m2, DType.bfloat16): + return (bitcast[int_dtype, simd_width](val) & 0x7F) == 0x7F + elif type is DType.float8e5m2: + # For the float8e5m2 type NaN is limited to 0x7F and 0xFF values. + # 7D, 7E, 7F are positive NaNs; FD, FE, FF are negative NaNs. + return (bitcast[int_dtype, simd_width](val) & 0x7F) > 0x7C + elif type is DType.float16: + var ival = bitcast[int_dtype, simd_width](val) + return (ival & 0x7C00) == 0x7C00 and (ival & 0x03FF) != 0 + elif type is DType.bfloat16: alias x7FFF = SIMD[int_dtype, simd_width](0x7FFF) alias x7F80 = SIMD[int_dtype, simd_width](0x7F80) return bitcast[int_dtype, simd_width](val) & x7FFF > x7F80 @@ -870,6 +875,11 @@ fn isinf[ if not type.is_floating_point(): return False + elif type is DType.float8e5m2: + # For the float8e5m2 both 7C and FC are infinity. + alias int_dtype = _integral_type_of[type]() + return (bitcast[int_dtype, simd_width](val) & 0x7F) == 0x7C + alias negative_infinity_test: UInt32 = 0x0004 alias positive_infinity_test: UInt32 = 0x0200 return llvm_intrinsic["llvm.is.fpclass", SIMD[DType.bool, simd_width]]( From 2ffdc0b50ae647ffff39b94bf17196fc7638cdfc Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 26 Sep 2024 20:52:42 -0700 Subject: [PATCH 1630/2019] [******][GPU] Enable fp8 on Ada subarch MODULAR_ORIG_COMMIT_REV_ID: 44113917b46bc4020da9836aa77f75c85e12aacc --- stdlib/src/builtin/simd.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d16cdd0066..c66b1484b8 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -123,7 +123,7 @@ fn _simd_construction_checks[type: DType, size: Int](): ]() constrained[ not (type.is_float8() and _has_native_f8_support()), - "f8 is not supported on non sm_90 architectures", + "f8 is not supported on non sm_89 and sm_90 architectures", ]() @@ -151,7 +151,7 @@ fn _has_native_bf16_support() -> Bool: @always_inline("nodebug") fn _has_native_f8_support() -> Bool: - return _is_sm_9x() + return _is_sm_9x() or triple_is_nvidia_cuda["sm_89"]() # ===----------------------------------------------------------------------=== # From 867e9aba91cfae908d75c6aa0da2d87964e89fa2 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:09:37 -0400 Subject: [PATCH 1631/2019] [External] [stdlib] Better `Dict` benchmarks (#45496) [External] [stdlib] Better `Dict` benchmarks Better `Dict` benchmarks, one of the steps for https://github.com/modularml/mojo/issues/3254 ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3269 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3269 MODULAR_ORIG_COMMIT_REV_ID: 5a7f21d9c2dcfb8a7ed025644d3d04152d6440d2 --- stdlib/benchmarks/collections/bench_dict.mojo | 187 ++++++++++++------ stdlib/src/collections/dict.mojo | 2 +- 2 files changed, 126 insertions(+), 63 deletions(-) diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index 2a37df1a36..8645b66d7a 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -15,99 +15,107 @@ from random import * from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run -from stdlib.collections import Dict +from sys import sizeof +from bit import bit_ceil +from math import ceil +from collections import Dict, Optional +from collections.dict import DictEntry # ===----------------------------------------------------------------------===# # Benchmark Data # ===----------------------------------------------------------------------===# -fn make_dict(n: Int) -> Dict[Int, Int]: - var dict = Dict[Int, Int]() - for i in range(0, n): - dict[i] = random.random_si64(0, n).value - return dict - - -alias small_n = 10_000 -alias large_n = 500_000 -alias insert_n = small_n // 2 -alias partial_n = small_n // 4 - -var small = make_dict(small_n) -var large = make_dict(large_n) +fn make_dict[size: Int]() -> Dict[Int, Int]: + var d = Dict[Int, Int]() + for i in range(0, size): + d[i] = random.random_si64(0, size).value + return d # ===----------------------------------------------------------------------===# -# Benchmark Dict Ctor +# Benchmark Dict init # ===----------------------------------------------------------------------===# @parameter -fn bench_dict_ctor(inout b: Bencher) raises: +fn bench_dict_init(inout b: Bencher) raises: @always_inline @parameter fn call_fn(): - var _d: Dict[Int, Int] = Dict[Int, Int]() + for _ in range(1000): + var d = Dict[Int, Int]() + keep(d._entries.data) + keep(d._index.data) b.iter[call_fn]() # ===----------------------------------------------------------------------===# -# Benchmark Dict Small Insert +# Benchmark Dict Insert # ===----------------------------------------------------------------------===# @parameter -fn bench_dict_small_insert(inout b: Bencher) raises: +fn bench_dict_insert[size: Int](inout b: Bencher) raises: + """Insert 100 new items.""" + var items = make_dict[size]() + @always_inline @parameter fn call_fn() raises: - for key in range(small_n, small_n + insert_n): - small[key] = random.random_si64(0, small_n).value + for key in range(size, size + 100): + items[key] = random.random_si64(0, size).value b.iter[call_fn]() - keep(bool(small)) + keep(bool(items)) # ===----------------------------------------------------------------------===# -# Benchmark Dict Large Insert +# Benchmark Dict Lookup # ===----------------------------------------------------------------------===# @parameter -fn bench_dict_large_insert(inout b: Bencher) raises: +fn bench_dict_lookup[size: Int](inout b: Bencher) raises: + """Lookup 100 items.""" + var items = make_dict[size]() + var closest_divisor = ceil(100 / size) + @always_inline @parameter fn call_fn() raises: - for key in range(large_n, large_n + insert_n): - large[key] = random.random_si64(0, large_n).value + @parameter + if size < 100: + for _ in range(closest_divisor): + for key in range(100 // closest_divisor): + var res = items[key] + keep(res) + else: + for key in range(100): + var res = items[key] + keep(res) b.iter[call_fn]() - keep(bool(large)) + keep(bool(items)) # ===----------------------------------------------------------------------===# -# Benchmark Dict Small Lookup +# Benchmark Dict Memory Footprint # ===----------------------------------------------------------------------===# -@parameter -fn bench_dict_small_lookup(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for key in range(0, partial_n): - _ = small[key] - b.iter[call_fn]() - keep(bool(small)) +fn total_bytes_used(items: Dict[Int, Int]) -> Int: + # the allocated memory by entries: + var entry_size = sizeof[Optional[DictEntry[Int, Int]]]() + var amnt_bytes = items._entries.capacity * entry_size + amnt_bytes += sizeof[Dict[Int, Int]]() -# ===----------------------------------------------------------------------===# -# Benchmark Dict Large Lookup -# ===----------------------------------------------------------------------===# -@parameter -fn bench_dict_large_lookup(inout b: Bencher) raises: - @always_inline - @parameter - fn call_fn() raises: - for key in range(0, partial_n): - _ = large[key] + # the allocated memory by index table: + var reserved = items._reserved() + if reserved <= 128: + amnt_bytes += sizeof[Int8]() * reserved + elif reserved <= 2**16 - 2: + amnt_bytes += sizeof[Int16]() * reserved + elif reserved <= 2**32 - 2: + amnt_bytes += sizeof[Int32]() * reserved + else: + amnt_bytes += sizeof[Int64]() * reserved - b.iter[call_fn]() - keep(bool(large)) + return amnt_bytes # ===----------------------------------------------------------------------===# @@ -116,17 +124,72 @@ fn bench_dict_large_lookup(inout b: Bencher) raises: def main(): seed() var m = Bench(BenchConfig(num_repetitions=1)) - m.bench_function[bench_dict_ctor](BenchId("bench_dict_ctor")) - m.bench_function[bench_dict_small_insert]( - BenchId("bench_dict_small_insert") - ) - m.bench_function[bench_dict_large_insert]( - BenchId("bench_dict_large_insert") - ) - m.bench_function[bench_dict_small_lookup]( - BenchId("bench_dict_small_lookup") - ) - m.bench_function[bench_dict_large_lookup]( - BenchId("bench_dict_large_lookup") + m.bench_function[bench_dict_init](BenchId("bench_dict_init")) + alias sizes = ( + 10, + 20, + 30, + 40, + 50, + 60, + 70, + 80, + 90, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 1000, + 2000, + 3000, + 4000, + 5000, + 6000, + 7000, + 8000, + 9000, + 10_000, + 20_000, + 30_000, + 40_000, + 50_000, + 60_000, + 70_000, + 80_000, + 90_000, + 100_000, + 200_000, + 300_000, + 400_000, + 500_000, + 600_000, + 700_000, + 800_000, + 900_000, + 1_000_000, ) + + @parameter + for i in range(len(sizes)): + alias size = sizes.get[i, Int]() + m.bench_function[bench_dict_insert[size]]( + BenchId("bench_dict_insert[" + str(size) + "]") + ) + m.bench_function[bench_dict_lookup[size]]( + BenchId("bench_dict_lookup[" + str(size) + "]") + ) + m.dump_report() + + @parameter + for i in range(len(sizes)): + alias size = sizes.get[i, Int]() + var mem_s = total_bytes_used(make_dict[size]()) + print( + '"bench_dict_memory_size[' + str(size) + ']",' + str(mem_s) + ",0" + ) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 86bdce4042..2f4fa4ce03 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -339,7 +339,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( The value type must implement the `CollectionElement` trait. - Usage: + Examples: ```mojo from collections import Dict From 15b5ab708e3243d793b7a839c2213d1bf0dbcbf2 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 27 Sep 2024 09:24:43 -0700 Subject: [PATCH 1632/2019] [******][GPU] Fix the FP8 constraint for SIMD, NFC MODULAR_ORIG_COMMIT_REV_ID: 8c6ab70b5c335a5c92e3852c5838df16d6136645 --- stdlib/src/builtin/simd.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c66b1484b8..807f6afebf 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -122,7 +122,7 @@ fn _simd_construction_checks[type: DType, size: Int](): "bf16 is not supported for ARM architectures", ]() constrained[ - not (type.is_float8() and _has_native_f8_support()), + not (type.is_float8() and not _has_native_f8_support()), "f8 is not supported on non sm_89 and sm_90 architectures", ]() From 998c8d41f6c57b8ed04b7dde68ef2d0cd44568bb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 27 Sep 2024 11:26:42 -0500 Subject: [PATCH 1633/2019] [stdlib] feat: Flesh out the new _lib.c and migrate some external_call[..]'s The migrated functions were essentially arbitrarily chosen. There are at least 1-2 dozen more C stdlib external_call[..] uses in the standard library that we should migrate. MODULAR_ORIG_COMMIT_REV_ID: b7add473be1ef0dbaf44f5091bca4e63ba7d7b9f --- stdlib/src/builtin/io.mojo | 27 ++++---------- stdlib/src/memory/memory.mojo | 3 +- stdlib/src/sys/_libc.mojo | 66 +++++++++++++++++++++++++++++++++++ stdlib/src/sys/terminate.mojo | 5 +-- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 2befb93aa5..544e452b92 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -18,10 +18,11 @@ These are Mojo built-ins, so you don't need to import them. from sys import ( bitwidthof, external_call, - os_is_windows, stdout, triple_is_nvidia_cuda, + _libc as libc, ) +from sys._libc import dup, fclose, fdopen, fflush from builtin.builtin_list import _LITRefPackHelper from builtin.dtype import _get_dtype_printf_format @@ -36,14 +37,6 @@ from utils import Formattable, Formatter # ===----------------------------------------------------------------------=== # -fn _dup(fd: Int32) -> Int32: - @parameter - if os_is_windows(): - return external_call["_dup", Int32](fd) - else: - return external_call["dup", Int32](fd) - - @value @register_passable("trivial") struct _fdopen[mode: StringLiteral = "a"]: @@ -56,15 +49,7 @@ struct _fdopen[mode: StringLiteral = "a"]: stream_id: The stream id """ - @parameter - if os_is_windows(): - self.handle = external_call["_fdopen", UnsafePointer[NoneType]]( - _dup(stream_id.value), mode.unsafe_cstr_ptr() - ) - else: - self.handle = external_call["fdopen", UnsafePointer[NoneType]]( - _dup(stream_id.value), mode.unsafe_cstr_ptr() - ) + self.handle = fdopen(dup(stream_id.value), mode.unsafe_cstr_ptr()) fn __enter__(self) -> Self: """Open the file handle for use within a context manager""" @@ -72,7 +57,7 @@ struct _fdopen[mode: StringLiteral = "a"]: fn __exit__(self): """Closes the file handle.""" - _ = external_call["fclose", Int32](self.handle) + _ = fclose(self.handle) fn readline(self) -> String: """Reads an entire line from stdin or until EOF. Lines are delimited by a newline character. @@ -149,7 +134,7 @@ struct _fdopen[mode: StringLiteral = "a"]: # Copy the buffer (excluding the delimiter itself) into a Mojo String. var s = String(StringRef(buffer, bytes_read - 1)) # Explicitly free the buffer using free() instead of the Mojo allocator. - external_call["free", NoneType](buffer.bitcast[NoneType]()) + libc.free(buffer.bitcast[NoneType]()) return s @@ -161,7 +146,7 @@ struct _fdopen[mode: StringLiteral = "a"]: @no_inline fn _flush(file: FileDescriptor = stdout): with _fdopen(file) as fd: - _ = external_call["fflush", Int32](fd.handle) + _ = fflush(fd.handle) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index ccc0689bfb..dbd24f5d13 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -27,6 +27,7 @@ from sys import ( triple_is_nvidia_cuda, external_call, simdwidthof, + _libc as libc, ) from collections import Optional from builtin.dtype import _integral_type_of @@ -438,6 +439,6 @@ fn _malloc[ fn _free(ptr: UnsafePointer[_, AddressSpace.GENERIC, *_]): @parameter if triple_is_nvidia_cuda(): - external_call["free", NoneType](ptr.bitcast[NoneType]()) + libc.free(ptr.bitcast[NoneType]()) else: __mlir_op.`pop.aligned_free`(ptr.address) diff --git a/stdlib/src/sys/_libc.mojo b/stdlib/src/sys/_libc.mojo index bde93c660f..a76a1e47d1 100644 --- a/stdlib/src/sys/_libc.mojo +++ b/stdlib/src/sys/_libc.mojo @@ -18,9 +18,74 @@ functionality in the rest of the Mojo standard library. """ from memory import UnsafePointer +from sys import os_is_windows from sys.ffi import c_char, c_int, OpaquePointer +# ===----------------------------------------------------------------------===# +# stdlib.h — core C standard library operations +# ===----------------------------------------------------------------------===# + + +@always_inline +fn free(ptr: OpaquePointer): + external_call["free", NoneType](ptr) + + +@always_inline +fn exit(status: c_int): + external_call["exit", NoneType](status) + + +# ===----------------------------------------------------------------------===# +# stdio.h — input/output operations +# ===----------------------------------------------------------------------===# + +alias FILE_ptr = OpaquePointer + + +@always_inline +fn fdopen(fd: c_int, mode: UnsafePointer[c_char]) -> FILE_ptr: + alias name = "_fdopen" if os_is_windows() else "fdopen" + + return external_call[name, FILE_ptr](fd, mode) + + +@always_inline +fn fclose(stream: FILE_ptr) -> c_int: + return external_call["fclose", c_int](stream) + + +@always_inline +fn fflush(stream: FILE_ptr) -> c_int: + return external_call["fflush", c_int](stream) + + +@always_inline +fn popen( + command: UnsafePointer[c_char], + type: UnsafePointer[c_char], +) -> FILE_ptr: + return external_call["popen", FILE_ptr](command, type) + + +@always_inline +fn pclose(stream: FILE_ptr) -> c_int: + return external_call["pclose", c_int](stream) + + +# ===----------------------------------------------------------------------===# +# unistd.h +# ===----------------------------------------------------------------------===# + + +@always_inline +fn dup(oldfd: c_int) -> c_int: + alias name = "_dup" if os_is_windows() else "dup" + + return external_call[name, c_int](oldfd) + + # ===----------------------------------------------------------------------===# # dlfcn.h — dynamic library operations # ===----------------------------------------------------------------------===# @@ -36,6 +101,7 @@ fn dlopen(filename: UnsafePointer[c_char], flags: c_int) -> OpaquePointer: return external_call["dlopen", OpaquePointer](filename, flags) +@always_inline fn dlclose(handle: OpaquePointer) -> c_int: return external_call["dlclose", c_int](handle) diff --git a/stdlib/src/sys/terminate.mojo b/stdlib/src/sys/terminate.mojo index 7b80e91429..48e5fd8455 100644 --- a/stdlib/src/sys/terminate.mojo +++ b/stdlib/src/sys/terminate.mojo @@ -13,7 +13,8 @@ """This module includes the exit functions.""" -from .ffi import external_call +from sys.ffi import c_int +from sys import _libc as libc fn exit(): @@ -33,4 +34,4 @@ fn exit[intable: Intable](code: intable): Args: code: The exit code. """ - external_call["exit", NoneType](Int32(int(code))) + libc.exit(c_int(int(code))) From 0c17bf6a4501843690be749b762bc993cad349eb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 27 Sep 2024 12:12:07 -0500 Subject: [PATCH 1634/2019] [stdlib] cleanup: Consolidate `ObservableDel` duplicates in test_utils/types.mojo This type was copy+pasted across a couple of test files. Instead, lets have one definition in the test_utils library. MODULAR_ORIG_COMMIT_REV_ID: 30296f65efb78cebc84643134a9a27de8e66b423 --- stdlib/test/memory/test_arc.mojo | 12 +-------- stdlib/test/test_utils/__init__.mojo | 1 + stdlib/test/test_utils/types.mojo | 40 ++++++++++++++++++++++++++++ stdlib/test/utils/test_variant.mojo | 12 +-------- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 4411f57732..129bbea32a 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -16,6 +16,7 @@ from collections import List from memory import Arc, UnsafePointer from testing import assert_equal, assert_false, assert_true +from test_utils import ObservableDel def test_basic(): @@ -25,17 +26,6 @@ def test_basic(): assert_equal(3, p[]) -@value -struct ObservableDel(CollectionElement): - var target: UnsafePointer[Bool] - - fn __init__(inout self, *, other: Self): - self = other - - fn __del__(owned self): - self.target.init_pointee_move(True) - - def test_deleter_not_called_until_no_references(): var deleted = False var p = Arc(ObservableDel(UnsafePointer.address_of(deleted))) diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index 50cb4749b9..d94f01d631 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -18,4 +18,5 @@ from .types import ( MoveCounter, MoveOnly, ValueDestructorRecorder, + ObservableDel, ) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 5167af2659..6b8427859e 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -12,6 +12,10 @@ # ===----------------------------------------------------------------------=== # from memory import UnsafePointer +# ===----------------------------------------------------------------------=== # +# MoveOnly +# ===----------------------------------------------------------------------=== # + struct MoveOnly[T: Movable](Movable): """Utility for testing MoveOnly types. @@ -40,6 +44,11 @@ struct MoveOnly[T: Movable](Movable): self.data = other.data^ +# ===----------------------------------------------------------------------=== # +# ExplicitCopyOnly +# ===----------------------------------------------------------------------=== # + + struct ExplicitCopyOnly(ExplicitlyCopyable): var value: Int var copy_count: Int @@ -53,6 +62,11 @@ struct ExplicitCopyOnly(ExplicitlyCopyable): self.copy_count = other.copy_count + 1 +# ===----------------------------------------------------------------------=== # +# CopyCounter +# ===----------------------------------------------------------------------=== # + + struct CopyCounter(CollectionElement, ExplicitlyCopyable): """Counts the number of copies performed on a value.""" @@ -71,6 +85,11 @@ struct CopyCounter(CollectionElement, ExplicitlyCopyable): self.copy_count = existing.copy_count + 1 +# ===----------------------------------------------------------------------=== # +# MoveCounter +# ===----------------------------------------------------------------------=== # + + struct MoveCounter[T: CollectionElementNew]( CollectionElement, CollectionElementNew, @@ -109,6 +128,11 @@ struct MoveCounter[T: CollectionElementNew]( self.move_count = existing.move_count +# ===----------------------------------------------------------------------=== # +# ValueDestructorRecorder +# ===----------------------------------------------------------------------=== # + + @value struct ValueDestructorRecorder(ExplicitlyCopyable): var value: Int @@ -120,3 +144,19 @@ struct ValueDestructorRecorder(ExplicitlyCopyable): fn __del__(owned self): self.destructor_counter[].append(self.value) + + +# ===----------------------------------------------------------------------=== # +# ObservableDel +# ===----------------------------------------------------------------------=== # + + +@value +struct ObservableDel(CollectionElement): + var target: UnsafePointer[Bool] + + fn __init__(inout self, *, other: Self): + self = other + + fn __del__(owned self): + self.target.init_pointee_move(True) diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 85436004be..6e326e0290 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -16,6 +16,7 @@ from sys.ffi import _get_global from memory import UnsafePointer from testing import assert_equal, assert_false, assert_true +from test_utils import ObservableDel from utils import Variant @@ -141,17 +142,6 @@ def test_move(): assert_no_poison() -@value -struct ObservableDel(CollectionElement): - var target: UnsafePointer[Bool] - - fn __init__(inout self, *, other: Self): - self = other - - fn __del__(owned self): - self.target.init_pointee_move(True) - - def test_del(): alias TestDeleterVariant = Variant[ObservableDel, Poison] var deleted: Bool = False From e1e0ad7a083ef1f675e6a18779245f64488fc8da Mon Sep 17 00:00:00 2001 From: Sawyer Bergeron Date: Fri, 27 Sep 2024 14:54:09 -0400 Subject: [PATCH 1635/2019] [External] [stdlib] Create Box[] type as a new smart pointer (#47849) [External] [stdlib] Create Box[] type as a new smart pointer This introduces a new `Box[]` type as an alternative to `UnsafePointer[]` to explicitly heap allocate large or slow to move/slow to copy types. Co-authored-by: Sawyer Bergeron Closes modularml/mojo#3524 MODULAR_ORIG_COMMIT_REV_ID: f47cec18cbc679de5978d7275c3c9ff7c7cd0219 --- docs/changelog.md | 7 ++ stdlib/src/memory/__init__.mojo | 1 + stdlib/src/memory/box.mojo | 134 +++++++++++++++++++++++++++ stdlib/test/memory/test_box.mojo | 121 ++++++++++++++++++++++++ stdlib/test/test_utils/__init__.mojo | 3 +- stdlib/test/test_utils/types.mojo | 18 ++++ 6 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 stdlib/src/memory/box.mojo create mode 100644 stdlib/test/memory/test_box.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 4706256f3e..694a953625 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -150,6 +150,13 @@ what we publish. determining a default SDK to use. The user can select the default SDK to use with the `Mojo: Select the default MAX SDK` command. +- Added a new [`Box`](/mojo/stdlib/memory/box/Box) type as a safe, single-owner, + non-nullable smart pointer with similar semantics to Rust's + [`Box<>`](https://doc.rust-lang.org/std/boxed/struct.Box.html) and C++'s + [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr). + + ([PR #3524](https://github.com/modularml/mojo/pull/3524) by [@szbergeron](https://github.com/szbergeron)) + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index f13370de53..90eee5e7c1 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -13,6 +13,7 @@ """Implements the memory package.""" from .arc import Arc +from .box import Box from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation from .reference import AddressSpace, Reference from .unsafe import bitcast diff --git a/stdlib/src/memory/box.mojo b/stdlib/src/memory/box.mojo new file mode 100644 index 0000000000..d083fcef95 --- /dev/null +++ b/stdlib/src/memory/box.mojo @@ -0,0 +1,134 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +from memory import UnsafePointer, stack_allocation, memcpy + + +struct Box[T: AnyType]: + """A safe, owning, smart pointer. + + This smart pointer is designed for cases where there is clear ownership + of the underlying data, and restricts access to it through the lifetime + system such that no more than one mutable alias for the underlying data + may exist. + + Parameters: + T: The type to be stored in the Box[]. + """ + + var _inner: UnsafePointer[T, AddressSpace.GENERIC] + + fn __init__[T: Movable](inout self: Box[T], owned value: T): + """Construct a new Box[] by moving the passed value into a new backing allocation. + + Parameters: + T: The type of the data to store. It is restricted to `Movable` here to allow efficient move construction. + + Args: + value: The value to move into the Box[]. + """ + self._inner = UnsafePointer[T].alloc(1) + self._inner.init_pointee_move(value^) + + fn __init__[T: ExplicitlyCopyable](inout self: Box[T], *, copy_value: T): + """Construct a new Box[] by explicitly copying the passed value into a new backing allocation. + + Parameters: + T: The type of the data to store. + + Args: + copy_value: The value to explicitly copy into the Box[]. + """ + self._inner = UnsafePointer[T].alloc(1) + self._inner.init_pointee_explicit_copy(copy_value) + + fn __init__[T: Copyable, U: NoneType = None](inout self: Box[T], value: T): + """Construct a new Box[] by copying the passed value into a new backing allocation. + + Parameters: + T: The type of the data to store. + U: A dummy type parameter, to lower the selection priority of this ctor. + + Args: + value: The value to copy into the Box[]. + """ + self._inner = UnsafePointer[T].alloc(1) + self._inner.init_pointee_copy(value) + + fn __init__[ + T: ExplicitlyCopyable + ](inout self: Box[T], *, copy_box: Box[T],): + """Construct a new Box[] by explicitly copying the value from another Box[]. + + Parameters: + T: The type of the data to store. + + Args: + copy_box: The Box[] to copy. + """ + self.__init__(copy_value=copy_box[]) + + fn __moveinit__(inout self, owned existing: Self): + """Move this Box[]. + + Args: + existing: The value to move. + """ + self._inner = existing._inner + existing._inner = UnsafePointer[T]() + + fn __getitem__( + ref [_, AddressSpace.GENERIC._value.value]self + ) -> ref [__lifetime_of(self), AddressSpace.GENERIC._value.value] T: + """Returns a reference to the box's underlying data with parametric mutability. + + Returns: + A reference to the data underlying the Box[]. + """ + # This should have a widening conversion here that allows + # the mutable ref that is always (potentially unsafely) + # returned from UnsafePointer to be guarded behind the + # aliasing guarantees of the lifetime system here. + # All of the magic happens above in the function signature + + return self._inner[] + + fn __del__(owned self: Box[T]): + """Destroy the Box[].""" + self._inner.destroy_pointee() + self._inner.free() + + fn unsafe_ptr(self) -> UnsafePointer[T]: + """UNSAFE: returns the backing pointer for this Box[]. + + Returns: + An UnsafePointer to the backing allocation for this Box[]. + """ + return self._inner + + fn take[T: Movable](owned self: Box[T]) -> T: + """Move the value within the Box[] out of it, consuming the Box[] in the process. + + Parameters: + T: The type of the data backing this Box[]. `take()` only exists for T: Movable + since this consuming operation only makes sense for types that you want to avoid copying. + For types that are Copy or ExplicitlyCopy but are not Movable, you can copy them through + `__getitem__` as in `var v = some_box_var[]`. + + Returns: + The data that is (was) backing the Box[]. + """ + var r = self._inner.take_pointee() + self._inner.free() + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + + return r^ diff --git a/stdlib/test/memory/test_box.mojo b/stdlib/test/memory/test_box.mojo new file mode 100644 index 0000000000..d658efdf8e --- /dev/null +++ b/stdlib/test/memory/test_box.mojo @@ -0,0 +1,121 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal, assert_false, assert_true, assert_not_equal +from test_utils import ( + MoveOnly, + ExplicitCopyOnly, + ImplicitCopyOnly, + ObservableDel, +) +from memory import Box, UnsafePointer + + +def test_basic_ref(): + var b = Box(1) + assert_equal(1, b[]) + + +def test_box_copy_constructor(): + var b = Box(1) + var b2 = Box(copy_box=b) + + assert_equal(1, b[]) + assert_equal(1, b2[]) + + assert_not_equal(b.unsafe_ptr(), b2.unsafe_ptr()) + + +def test_copying_constructor(): + var v = ImplicitCopyOnly(1) + var b = Box(v) + + assert_equal(b[].value, 1) + assert_equal(b[].copy_count, 1) # this should only ever require one copy + + +def test_explicitly_copying_constructor(): + var v = ExplicitCopyOnly(1) + var b = Box(copy_value=v) + + assert_equal(b[].value, 1) + assert_equal(b[].copy_count, 1) # this should only ever require one copy + + +def test_moving_constructor(): + var v = MoveOnly[Int](1) + var b = Box(v^) + + assert_equal(b[].data, 1) + + +def test_basic_ref_mutate(): + var b = Box(1) + assert_equal(1, b[]) + + b[] = 2 + + assert_equal(2, b[]) + + +def test_multiple_refs(): + var b = Box(1) + + var borrow1 = b[] + var borrow2 = b[] + + assert_equal(2, borrow1 + borrow2) + + +def test_basic_del(): + var deleted = False + var b = Box(ObservableDel(UnsafePointer.address_of(deleted))) + + assert_false(deleted) + + _ = b^ + + assert_true(deleted) + + +def test_take(): + var b = Box(1) + var v = b^.take() + assert_equal(1, v) + + +def test_moveinit(): + var deleted = False + var b = Box(ObservableDel(UnsafePointer.address_of(deleted))) + var p1 = b.unsafe_ptr() + + var b2 = b^ + var p2 = b2.unsafe_ptr() + + assert_false(deleted) + assert_equal(p1, p2) # move should reuse the allocation + + _ = b2^ + + +def main(): + test_basic_ref() + test_box_copy_constructor() + test_moving_constructor() + test_copying_constructor() + test_explicitly_copying_constructor() + test_basic_ref_mutate() + test_basic_del() + test_take() + test_moveinit() diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index d94f01d631..3dcecfc83b 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -15,8 +15,9 @@ from .test_utils import libm_call from .types import ( CopyCounter, ExplicitCopyOnly, + ImplicitCopyOnly, MoveCounter, MoveOnly, - ValueDestructorRecorder, ObservableDel, + ValueDestructorRecorder, ) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 6b8427859e..ade9d8c809 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -62,6 +62,24 @@ struct ExplicitCopyOnly(ExplicitlyCopyable): self.copy_count = other.copy_count + 1 +# ===----------------------------------------------------------------------=== # +# ImplicitCopyOnly +# ===----------------------------------------------------------------------=== # + + +struct ImplicitCopyOnly(Copyable): + var value: Int + var copy_count: Int + + fn __init__(inout self, value: Int): + self.value = value + self.copy_count = 0 + + fn __copyinit__(inout self, *, other: Self): + self.value = other.value + self.copy_count = other.copy_count + 1 + + # ===----------------------------------------------------------------------=== # # CopyCounter # ===----------------------------------------------------------------------=== # From 10e4407a675e85a28cbc696e879a52836ffa84e2 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:25:12 -0700 Subject: [PATCH 1636/2019] Created new page on Mojo errors, error handling, and context managers MODULAR_ORIG_COMMIT_REV_ID: 85d56f452bc42d51ab4a5ccb7b5a20dd8d0d05b3 --- docs/manual/errors.mdx | 426 +++++++++++++++++++++++++++++++++++++++ docs/tools/testing.ipynb | 7 +- 2 files changed, 430 insertions(+), 3 deletions(-) create mode 100644 docs/manual/errors.mdx diff --git a/docs/manual/errors.mdx b/docs/manual/errors.mdx new file mode 100644 index 0000000000..c072092858 --- /dev/null +++ b/docs/manual/errors.mdx @@ -0,0 +1,426 @@ +--- +title: Errors, error handling, and context managers +sidebar_label: Errors and context managers +--- + +This page discusses how to raise errors in Mojo programs and how to detect and +handle error conditions. It also discusses how you can use context managers to +allocate and release resources such as files correctly, even when error +conditions occur. Finally, it shows you how to implement context managers for +your own custom resources. + +## Raise an error + +The `raise` statement raises an error condition in your program. You provide the +`raise` statement with an [`Error`](/mojo/stdlib/builtin/error/Error) instance +to indicate the type of error that occurred. For example: + +```mojo +raise Error("integer overflow") +``` + +As a convenience, you can instead provide an error message in the form of a +[`String`](/mojo/stdlib/collections/string/String) or +[`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral) value, and +`raise` automatically uses that to create an `Error` instance. So you can raise +the same error condition as shown above by executing: + +```mojo +raise "integer overflow" +``` + +:::note + +Currently, Mojo does not support typed error conditions. All errors are +instances of `Error`, and the only thing that distinguishes different error +conditions is the error message that you provide. + +::: + +An error interrupts the current execution flow of your program. If you provide +an error handler (as described in [Handle an error](#handle-an-error)) in the +current scope or any calling scope, execution resumes with that handler. If +there is no applicable error handler, your program terminates with a non-zero +exit code and prints the error message. For example: + +```output +Unhandled exception caught during execution: integer overflow +``` + +If a function you define using the `fn` keyword can raise an error, you must +include the `raises` keyword in the function definition. For example: + +```mojo +fn incr(n: Int) raises -> Int: + if n == Int.MAX: + raise "inc: integer overflow" + else: + return n + 1 +``` + +On the other hand, you **cannot** use the `raises` keyword when defining a +function using the `def` keyword, because `def` already implies that the +function may raise an error. So the following is equivalent to the function +defined above: + +```mojo +def incr(n: Int) -> Int: + if n == Int.MAX: + raise "inc: integer overflow" + else: + return n + 1 +``` + +## Handle an error + +Mojo allows you to detect and handle error conditions using the `try-except` +control flow structure, whose full syntax is: + +```mojo +try: + # Code block to execute that might raise an error +except : + # Code block to execute if an error occurs +else: + # Code block to execute if no error occurs +finally: + # Final code block to execute in all circumstances +``` + +You must include one or both of the `except` and `finally` clauses. The `else` +clause is optional. + +The `try` clause contains a code block to execute that might raise an error. If +no error occurs, the entire code block executes. If an error occurs, execution +of the code block stops at the point that the error is raised. + +If the `except` clause is present, its code block executes only if an error +occurred in the `try` clause. You can optionally provide the name of a variable +after the `except` keyword. The `Error` instance is bound to the variable if an +error occurs. The `except` clause "consumes" the error that occurred in the +`try` clause. If desired, you can re-raise an error condition by executing a +`raise` statement from within the `except` clause. + +:::note + +Because Mojo does not currently support typed errors, you can include at most +one `except` clause, which catches any `Error` raised. + +::: + +If the `else` clause is present, its code block executes only if an error does +not occur in the `try` clause. Note that the `else` clause is *skipped* if the +`try` clause executes a `continue`, `break`, or `return` that exits from the +`try` block. + +If the `finally` clause is present, its code block executes after the `try` +clause and the `except` or `else` clause, if applicable. The `finally` clause +executes even if one of the other code blocks exit by executing a `continue`, +`break`, or `return` statement. The `finally` clause is often used to release +resources used by the `try` clause (such as a file handle) regardless of whether +or not an error occurred. + +As an example, consider the following program: + +```mojo +def incr(n: Int) -> Int: + if n == Int.MAX: + raise "inc: integer overflow" + else: + return n + 1 + +def main(): + values = List(0, 1, Int.MAX) + for value in values: + try: + print() + print("try =>", value[]) + if value[] == 1: + continue + result = str("{} incremented is {}").format(value[], incr(value[])) + except e: + print("except =>", e) + else: + print("else =>", result) + finally: + print("finally => ====================") + +``` + +Running this program generates the following output: + +```output +try => 0 +else => 0 incremented is 1 +finally => ==================== + +try => 1 +finally => ==================== + +try => 9223372036854775807 +except => inc: integer overflow +finally => ==================== +``` + +## Use a context manager + +A *context manager* is an object that manages resources such as files, network +connections, and database connections. It provides a way to allocate resources +and release them automatically when they are no longer needed, ensuring proper +cleanup and preventing resource leaks even in the case of error conditions. + +As an example, consider reading data from a file. A naive approach might look +like this: + +```mojo +# Obtain a file handle to read from storage +f = open(input_file, "r") +content = f.read() +# Process the content as needed +# Close the file handle +f.close() +``` + +Calling [`close()`](/mojo/stdlib/builtin/file/FileHandle#close) releases the +operating system resources associated with the opened file. If your program were +to open many files without closing them, you could exhaust the resources +available to your program and cause errors. The problem is even worse if you +were writing to a file instead of reading from it, because the operating system +might buffer the output in memory until the file is closed. If your program were +to crash instead of exiting normally, that buffered data could be lost instead +of being written to storage. The example above actually includes the call to +`close()`, but it ignores the possibility that +[`read()`](/mojo/stdlib/builtin/file/FileHandle#read) could raise an error, +which would result in the program not executing the `close()`. To handle this +scenario you would need to rewrite the code like this: + +```mojo +# Obtain a file handle to read from storage +f = open(input_file, "r") + +try: + content = f.read() + # Process the content as needed +finally: + # Ensure that the file handle is closed even if read() raises an error + f.close() +``` + +However, the [`FileHandle`](/mojo/stdlib/builtin/file/FileHandle) struct +returned by [`open()`](/mojo/stdlib/builtin/file/open) is a context manager. +When used in conjunction with Mojo's `with` statement, a context manager ensures +that the resources it manages are properly released at the end of the block, +even if an error occurs. You could rewrite the example above to take advantage +of the context manager like this: + +```mojo +with open(input_file, "r") as f: + content = f.read() + # Process the content as needed +``` + +The `with` statement also allows you to use multiple context managers within the +same code block. As an example, the following code opens one text file, reads +its entire content, converts it to upper case, and then writes the result to a +different file: + +```mojo + with open(input_file, "r") as f_in, open(output_file, "w") as f_out: + input_text = f_in.read() + output_text = input_text.upper() + f_out.write(output_text) +``` + +`FileHandle` is perhaps the most commonly used context manager. Other examples +of context managers in the Mojo standard library are +[`NamedTemporaryFile`](/mojo/stdlib/tempfile/tempfile/NamedTemporaryFile), +[`TemporaryDirectory`](/mojo/stdlib/tempfile/tempfile/TemporaryDirectory), +[`BlockingScopedLock`](/mojo/stdlib/utils/lock/BlockingScopedLock), and +[`assert_raises`](/mojo/stdlib/testing/testing/assert_raises). You can also +create your own custom context managers, as described in [Write a custom context +manager](#write-a-custom-context-manager). + +## Write a custom context manager + +Writing a custom context manager is a matter of defining a +[struct](/mojo/manual/structs) that implements two special *dunder* methods +("double underscore" methods): `__enter__()` and `__exit__()`: + +- `__enter__()` is called by the `with` statement to enter the runtime context. + The `__enter__()` method should initialize any state necessary for the context + and return the context manager. + +- `__exit__()` is called when the `with` code block completes execution, even if + the `with` code block terminates with a call to `continue`, `break`, or + `return`. The `__exit__()` method should release any resources associated with + the context. After the `__exit__()` method returns, the context manager is + destroyed. + + If the `with` code block raises an error, then the `__exit__()` method runs + before any error processing occurs (that is, before it is caught by a + `try-except` structure or your program terminates). As discussed in [Define a + conditional `__exit__()` method](#define-a-conditional-__exit__-method), there + is an overloaded form of `__exit__()` that you can implement to determine + custom processing for error conditions in a `with` code block. + + For context managers that don't need to release resources or perform other + actions on termination, you are not required to implement an `__exit__()` + method. In that case the context manager is destroyed automatically after the + `with` code block completes execution. + +Here is an example of implementing a `Timer` context manager, which prints the +amount of time spent executing the `with` code block: + +```mojo title="context_mgr.mojo" +import sys +import time + +@value +struct Timer: + var start_time: Int + + fn __init__(inout self): + self.start_time = 0 + + fn __enter__(inout self) -> Self: + self.start_time = time.perf_counter_ns() + return self + + fn __exit__(inout self): + end_time = time.perf_counter_ns() + elapsed_time_ms = round(((end_time - self.start_time) / 1e6), 3) + print("Elapsed time:", elapsed_time_ms, "milliseconds") + +def main(): + with Timer(): + print("Beginning execution") + time.sleep(1) + if len(sys.argv()) > 1: + raise "simulated error" + time.sleep(1) + print("Ending execution") +``` + +Running this example produces output like this: + +```sh +mojo context_mgr.mojo +``` + +```output +Beginning execution +Ending execution +Elapsed time: 2010.0 milliseconds +``` + +```sh +mojo context_mgr.mojo fail +``` + +```output +Beginning execution +Elapsed time: 1002.0 milliseconds +Unhandled exception caught during execution: simulated error +``` + +### Define a conditional `__exit__()` method + +When creating a context manager, you must implement the `__exit__(self)` form of +the `__exit__()` method. However, you also have the option of implementing an +overloaded version that is invoked only when an error occurs in the `with` code +block: + +```mojo +fn __exit__(self, error: Error) raises -> Bool +``` + +Given the `Error` that occurred as an argument, the method can: + +- Return `True` to suppress the error +- Return `False` to re-raise the error +- Raise a new error + +The following is an example of a context manager that suppresses only a certain +type of error condition and propagates all others: + +```mojo title="conditional_context_mgr.mojo" +import sys +import time + +@value +struct ConditionalTimer: + var start_time: Int + + fn __init__(inout self): + self.start_time = 0 + + fn __enter__(inout self) -> Self: + self.start_time = time.perf_counter_ns() + return self + + fn __exit__(inout self): + end_time = time.perf_counter_ns() + elapsed_time_ms = round(((end_time - self.start_time) / 1e6), 3) + print("Elapsed time:", elapsed_time_ms, "milliseconds") + + fn __exit__(inout self, e: Error) raises -> Bool: + if str(e) == "just a warning": + print("Suppressing error:", e) + self.__exit__() + return True + else: + print("Propagating error") + self.__exit__() + return False + +def flaky_identity(n: Int) -> Int: + if (n % 4) == 0: + raise "really bad" + elif (n % 2) == 0: + raise "just a warning" + else: + return n + +def main(): + for i in range(1, 9): + with ConditionalTimer(): + print("\nBeginning execution") + + print("i =", i) + time.sleep(0.1) + + if i == 3: + print("continue executed") + continue + + j = flaky_identity(i) + print("j =", j) + + print("Ending execution") +``` + +Running this example produces this output: + +```output +Beginning execution +i = 1 +j = 1 +Ending execution +Elapsed time: 105.0 milliseconds + +Beginning execution +i = 2 +Suppressing error: just a warning +Elapsed time: 106.0 milliseconds + +Beginning execution +i = 3 +continue executed +Elapsed time: 106.0 milliseconds + +Beginning execution +i = 4 +Propagating error +Elapsed time: 106.0 milliseconds +Unhandled exception caught during execution: really bad +``` diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb index 94b06f1ebf..be8e4a6c16 100644 --- a/docs/tools/testing.ipynb +++ b/docs/tools/testing.ipynb @@ -233,9 +233,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The testing module also defines a context manager,\n", - "[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises),\n", - "to assert that a given code block correctly raises an expected error." + "The testing module also defines a [context\n", + "manager](/mojo/manual/errors#use-a-context-manager),\n", + "[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises), to assert that\n", + "a given code block correctly raises an expected error." ] }, { From ffcbca5ffe1d0ab368eb03d73e3cc14f3bdbd3c6 Mon Sep 17 00:00:00 2001 From: Peiming Liu Date: Fri, 27 Sep 2024 20:27:42 -0700 Subject: [PATCH 1637/2019] [stdlib] rename pop.xor/or/and to pop.simd.xor/or/and (NFC). We will instead use `pop.and/or/xor` to handle non-simd types (i.e., `mlir_type.i1`) directly without type conversion. MODULAR_ORIG_COMMIT_REV_ID: a4b548816c5cb2c8db6580027a5182992152c65e --- stdlib/src/bit/bit.mojo | 2 +- stdlib/src/builtin/bool.mojo | 10 ++++++---- stdlib/src/builtin/dtype.mojo | 10 +++++----- stdlib/src/builtin/simd.mojo | 6 +++--- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 3546244110..d3a48a938e 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -283,7 +283,7 @@ fn bit_not[ """ constrained[type.is_integral(), "must be integral"]() var neg_one = SIMD[type, width](-1) - return __mlir_op.`pop.xor`(val.value, neg_one.value) + return __mlir_op.`pop.simd.xor`(val.value, neg_one.value) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 2506270a3c..0c841090dc 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -377,7 +377,7 @@ struct Bool( _type = __mlir_type.`!pop.scalar`, value = __mlir_attr.`#pop.simd : !pop.scalar`, ]() - return __mlir_op.`pop.xor`(self._as_scalar_bool(), true) + return __mlir_op.`pop.simd.xor`(self._as_scalar_bool(), true) @always_inline("nodebug") fn __and__(self, rhs: Bool) -> Bool: @@ -392,7 +392,7 @@ struct Bool( Returns: `self & rhs`. """ - return __mlir_op.`pop.and`( + return __mlir_op.`pop.simd.and`( self._as_scalar_bool(), rhs._as_scalar_bool() ) @@ -430,7 +430,9 @@ struct Bool( Returns: `self | rhs`. """ - return __mlir_op.`pop.or`(self._as_scalar_bool(), rhs._as_scalar_bool()) + return __mlir_op.`pop.simd.or`( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) @always_inline("nodebug") fn __ior__(inout self, rhs: Bool): @@ -466,7 +468,7 @@ struct Bool( Returns: `self ^ rhs`. """ - return __mlir_op.`pop.xor`( + return __mlir_op.`pop.simd.xor`( self._as_scalar_bool(), rhs._as_scalar_bool() ) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 906e216ecf..f9b121b3f2 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -309,7 +309,7 @@ struct DType( return False return Bool( __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - __mlir_op.`pop.and`(self._as_i8(), _mIsSigned.value), + __mlir_op.`pop.simd.and`(self._as_i8(), _mIsSigned.value), UInt8(0).value, ) ) @@ -327,7 +327,7 @@ struct DType( return False return Bool( __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - __mlir_op.`pop.and`(self._as_i8(), _mIsSigned.value), + __mlir_op.`pop.simd.and`(self._as_i8(), _mIsSigned.value), UInt8(0).value, ) ) @@ -341,7 +341,7 @@ struct DType( """ return Bool( __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - __mlir_op.`pop.and`(self._as_i8(), _mIsInteger.value), + __mlir_op.`pop.simd.and`(self._as_i8(), _mIsInteger.value), UInt8(0).value, ) ) @@ -369,7 +369,7 @@ struct DType( return False return Bool( __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - __mlir_op.`pop.and`(self._as_i8(), _mIsFloat.value), + __mlir_op.`pop.simd.and`(self._as_i8(), _mIsFloat.value), UInt8(0).value, ) ) @@ -422,7 +422,7 @@ struct DType( UInt8(1).value, __mlir_op.`pop.sub`( __mlir_op.`pop.shr`( - __mlir_op.`pop.and`( + __mlir_op.`pop.simd.and`( self._as_i8(), _mIsNotInteger.value ), UInt8(1).value, diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 807f6afebf..23b89d4a43 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -869,7 +869,7 @@ struct SIMD[type: DType, size: Int]( type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() - return __mlir_op.`pop.and`(self.value, rhs.value) + return __mlir_op.`pop.simd.and`(self.value, rhs.value) @always_inline("nodebug") fn __xor__(self, rhs: Self) -> Self: @@ -888,7 +888,7 @@ struct SIMD[type: DType, size: Int]( type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() - return __mlir_op.`pop.xor`(self.value, rhs.value) + return __mlir_op.`pop.simd.xor`(self.value, rhs.value) @always_inline("nodebug") fn __or__(self, rhs: Self) -> Self: @@ -907,7 +907,7 @@ struct SIMD[type: DType, size: Int]( type.is_integral() or type is DType.bool, "must be an integral or bool type", ]() - return __mlir_op.`pop.or`(self.value, rhs.value) + return __mlir_op.`pop.simd.or`(self.value, rhs.value) @always_inline("nodebug") fn __lshift__(self, rhs: Self) -> Self: From 74bbef9d23ad452310d2f14dc36823fbe8b6e034 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 28 Sep 2024 18:43:52 -0700 Subject: [PATCH 1638/2019] [mojo-stdlib] Upgrade `Variant.unsafe_get` to `ref` return. This upgrades `Variant` to use `ref`result for `unsafe_get`, eliminating a bunch of extra `[]`s. MODULAR_ORIG_COMMIT_REV_ID: 9f5006d9e6b184ead98e64e8c6f773bafe70142c --- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/utils/variant.mojo | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 9ba502ba5e..671ebc0623 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -342,7 +342,7 @@ struct Optional[T: CollectionElement]( A reference to the contained data of the option as a Reference[T]. """ debug_assert(self.__bool__(), ".value() on empty Optional") - return self._value.unsafe_get[T]()[] + return self._value.unsafe_get[T]() fn take(inout self) -> T: """Move the value out of the Optional. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index bcda383ca5..d9a334816b 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -215,7 +215,7 @@ struct Variant[*Ts: CollectionElement]( if not self.isa[T](): abort("get: wrong variant type") - return self.unsafe_get[T]()[] + return self.unsafe_get[T]() # ===-------------------------------------------------------------------===# # Methods @@ -366,7 +366,7 @@ struct Variant[*Ts: CollectionElement]( fn unsafe_get[ T: CollectionElement - ](ref [_]self: Self) -> Reference[T, __lifetime_of(self)]: + ](ref [_]self: Self) -> ref [__lifetime_of(self)] T: """Get the value out of the variant as a type-checked type. This doesn't explicitly check that your value is of that type! From f87daf08260b7bff03299d695207e52f02d2351e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 28 Sep 2024 18:44:05 -0700 Subject: [PATCH 1639/2019] [mojo-lang][CheckLifetimes] Make AnyLifetime extend with packs This fixes a bug handling `VariadicPack`, where we need to notice its embedded lifetime parameter. Not doing this defeats lifetime extension of `AnyLifetime` references in functions like `print`. This fixes https://github.com/modularml/mojo/issues/3559 MODULAR_ORIG_COMMIT_REV_ID: c86279a5c2b90787e74bca6d915afe1a07e505b7 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 694a953625..71520068a5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -196,4 +196,7 @@ what we publish. - [Issue #3544](https://github.com/modularml/mojo/issues/3544) - Known mutable `ref` argument are not optimized as `noalias` by LLVM. +- [Issue #3559](https://github.com/modularml/mojo/issues/3559) - VariadicPack + doesn't extend the lifetimes of the values it references. + - The VS Code extension now auto-updates its private copy of the MAX SDK. From 9fb4fb34989fa85379d4807f736da9f77f382064 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 28 Sep 2024 19:40:50 -0700 Subject: [PATCH 1640/2019] [mojo-lang] Allow direct use of mvalues in ref lifetime specifiers This enhances the parser to allow one to directly use an MValue in a `ref` lifetime specifier, instead of forcing the use of `__lifetime_of` which seems quite redundant most of the time. You still need to use `__lifetime_of` in parameter lists for types like `Reference` though. This adopts this in the standard library as well. MODULAR_ORIG_COMMIT_REV_ID: ce40b1f5ecb30d08bc6ca295539d5d5aff6827f9 --- docs/changelog-released.md | 2 +- docs/changelog.md | 11 +++++++++++ docs/manual/parameters/index.ipynb | 2 +- docs/manual/values/lifetimes.ipynb | 6 +++--- proposals/ref-convention.md | 4 ++-- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/rebind.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 4 ++-- stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/inline_array.mojo | 10 +++------- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/list.mojo | 6 ++---- stdlib/src/collections/optional.mojo | 4 ++-- stdlib/src/memory/box.mojo | 2 +- stdlib/src/memory/maybe_uninitialized.mojo | 2 +- stdlib/src/utils/variant.mojo | 6 ++---- 16 files changed, 35 insertions(+), 32 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 43444b82a3..17cefc77bb 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -1059,7 +1059,7 @@ Big themes for this release: var first: Int var second: Int - fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: + fn get_first_ref(inout self) -> ref [self] Int: return self.first fn show_mutation(): diff --git a/docs/changelog.md b/docs/changelog.md index 71520068a5..ec40428439 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -157,6 +157,17 @@ what we publish. ([PR #3524](https://github.com/modularml/mojo/pull/3524) by [@szbergeron](https://github.com/szbergeron)) +- `ref` argument and result specifiers now allow providing a memory value + directly in the lifetime specifier, rather than requiring the use of + `__lifetime_of`. It is still fine to use `__lifetime_of` explicitly though, + and this is required when specifying lifetimes for parameters (e.g. to the + `Reference` type). For example, this is now valid without `__lifetime_of`: + + ```mojo + fn return_ref(a: String) -> ref [a] String: + return a + ``` + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 7dd7599fa0..cbf61f4f7f 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -244,7 +244,7 @@ " (self.data + i).destroy_pointee()\n", " self.data.free()\n", "\n", - " fn __getitem__(self, i: Int) raises -> ref [__lifetime_of(self)] ElementType:\n", + " fn __getitem__(self, i: Int) raises -> ref [self] ElementType:\n", " if (i < self.size):\n", " return self.data[i]\n", " else:\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index b66f8828eb..ddf4086e83 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -220,7 +220,7 @@ "called to return the subscripted item looks like this:\n", "\n", "```mojo\n", - "fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T:\n", + "fn __getitem__(ref [_]self, idx: Int) -> ref [self] T:\n", "```\n", "\n", "The syntax may be unfamiliar—`ref` arguments and `ref` return values are\n", @@ -415,7 +415,7 @@ " self.names.append(name[])\n", "\n", " def __getitem__(ref [_] self: Self, index: Int) ->\n", - " ref [__lifetime_of(self)] String:\n", + " ref [self] String:\n", " if (index >=0 and index < len(self.names)):\n", " return self.names[index]\n", " else:\n", @@ -495,7 +495,7 @@ "\n", "```mojo\n", "def __getitem__(ref [_] self: Self, index: Int) ->\n", - " ref [__lifetime_of(self)] String:\n", + " ref [self] String:\n", "```\n", "\n", "Since the `lifetime` of the return value is tied to the lifetime of `self`, the\n", diff --git a/proposals/ref-convention.md b/proposals/ref-convention.md index 38399090fe..b99a3510aa 100644 --- a/proposals/ref-convention.md +++ b/proposals/ref-convention.md @@ -134,7 +134,7 @@ example: ```mojo fn __refitem__(ref [_] self, index: Int) - -> ref [__lifetime_of(self)] Self.ElementType: + -> ref [self] Self.ElementType: # Hopefully someday we'll have a Lifetime type: fn __refitem__(ref [_] self, index: Int) @@ -174,7 +174,7 @@ becomes: ```mojo fn __getitem__(ref [_] self, index: Int) - -> ref [__lifetime_of(self)] Self.ElementType: + -> ref [self] Self.ElementType: ``` Note: Neither `inout` nor `borrowed` are allowed in a result position. The diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 1f9db19af1..5b180fb584 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -85,7 +85,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](self) -> ref [__lifetime_of(self)] T: + fn get[i: Int, T: CollectionElement](self) -> ref [self] T: """Get a list element at the given index. Parameters: diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index 9d718c903f..cbfea237be 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -46,7 +46,7 @@ fn rebind[ fn rebind[ src_type: AnyType, //, dest_type: AnyType, -](ref [_]src: src_type) -> ref [__lifetime_of(src)] dest_type: +](ref [_]src: src_type) -> ref [src] dest_type: """Statically assert that a parameter input type `src_type` resolves to the same type as a parameter result type `dest_type` after function instantiation and "rebind" the input to the result type, returning a diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index dd97bc17fc..c008de5e33 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -155,7 +155,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn __getitem__[ idx: Int - ](ref [_]self: Self) -> ref [__lifetime_of(self)] element_types[idx.value]: + ](ref [_]self: Self) -> ref [self] element_types[idx.value]: """Get a reference to an element in the tuple. Parameters: @@ -178,7 +178,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](self) -> ref [__lifetime_of(self)] T: + fn get[i: Int, T: CollectionElement](self) -> ref [self] T: """Get a tuple element and rebind to the specified type. Parameters: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 2f4fa4ce03..29eec85392 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -735,7 +735,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MOCO-604): Return Optional[Reference] instead of raising fn _find_ref( ref [_]self: Self, key: K - ) raises -> ref [__lifetime_of(self._entries[0].value().value)] Self.V: + ) raises -> ref [self._entries[0].value().value] Self.V: """Find a value in the dictionary by key. Args: diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index a6d69b8bc8..9362127244 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -222,9 +222,7 @@ struct InlineArray[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__( - ref [_]self: Self, idx: Int - ) -> ref [__lifetime_of(self)] Self.ElementType: + fn __getitem__(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: """Get a `Reference` to the element at the given index. Args: @@ -240,7 +238,7 @@ struct InlineArray[ @always_inline("nodebug") fn __getitem__[ idx: Int, - ](ref [_]self: Self) -> ref [__lifetime_of(self)] Self.ElementType: + ](ref [_]self: Self) -> ref [self] Self.ElementType: """Get a `Reference` to the element at the given index. Parameters: @@ -277,9 +275,7 @@ struct InlineArray[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn unsafe_get( - ref [_]self: Self, idx: Int - ) -> ref [__lifetime_of(self)] Self.ElementType: + fn unsafe_get(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: """Get a reference to an element of self without checking index bounds. Users should opt for `__getitem__` instead of this method as it is diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 366ce0bba8..592072a002 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -129,7 +129,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __getitem__( ref [_]self: Self, owned idx: Int - ) -> ref [__lifetime_of(self._array)] Self.ElementType: + ) -> ref [self._array] Self.ElementType: """Get a `Reference` to the element at the given index. Args: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 01b79bfa28..fb913f742e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -785,7 +785,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return res^ - fn __getitem__(ref [_]self, idx: Int) -> ref [__lifetime_of(self)] T: + fn __getitem__(ref [_]self, idx: Int) -> ref [self] T: """Gets the list element at the given index. Args: @@ -810,9 +810,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return (self.data + normalized_idx)[] @always_inline - fn unsafe_get( - ref [_]self: Self, idx: Int - ) -> ref [__lifetime_of(self)] Self.T: + fn unsafe_get(ref [_]self: Self, idx: Int) -> ref [self] Self.T: """Get a reference to an element of self without checking index bounds. Users should consider using `__getitem__` instead of this method as it diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 671ebc0623..4a857381c2 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -313,7 +313,7 @@ struct Optional[T: CollectionElement]( # ===-------------------------------------------------------------------===# @always_inline - fn value(ref [_]self: Self) -> ref [__lifetime_of(self._value)] T: + fn value(ref [_]self: Self) -> ref [self._value] T: """Retrieve a reference to the value of the Optional. This check to see if the optional contains a value. @@ -330,7 +330,7 @@ struct Optional[T: CollectionElement]( return self.unsafe_value() @always_inline - fn unsafe_value(ref [_]self: Self) -> ref [__lifetime_of(self._value)] T: + fn unsafe_value(ref [_]self: Self) -> ref [self._value] T: """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. diff --git a/stdlib/src/memory/box.mojo b/stdlib/src/memory/box.mojo index d083fcef95..f6f2da49d4 100644 --- a/stdlib/src/memory/box.mojo +++ b/stdlib/src/memory/box.mojo @@ -88,7 +88,7 @@ struct Box[T: AnyType]: fn __getitem__( ref [_, AddressSpace.GENERIC._value.value]self - ) -> ref [__lifetime_of(self), AddressSpace.GENERIC._value.value] T: + ) -> ref [self, AddressSpace.GENERIC._value.value] T: """Returns a reference to the box's underlying data with parametric mutability. Returns: diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index bbb593a24c..894bfeda9e 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -212,7 +212,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): @always_inline fn assume_initialized( ref [_]self: Self, - ) -> ref [__lifetime_of(self)] Self.ElementType: + ) -> ref [self] Self.ElementType: """Returns a reference to the internal value. Calling this method assumes that the memory is initialized. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index d9a334816b..ef6a35be4b 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -194,9 +194,7 @@ struct Variant[*Ts: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - fn __getitem__[ - T: CollectionElement - ](ref [_]self: Self) -> ref [__lifetime_of(self)] T: + fn __getitem__[T: CollectionElement](ref [_]self: Self) -> ref [self] T: """Get the value out of the variant as a type-checked type. This explicitly check that your value is of that type! @@ -232,7 +230,7 @@ struct Variant[*Ts: CollectionElement]( return discr_ptr @always_inline("nodebug") - fn _get_discr(ref [_]self: Self) -> ref [__lifetime_of(self)] UInt8: + fn _get_discr(ref [_]self: Self) -> ref [self] UInt8: var ptr = UnsafePointer.address_of(self._impl).address var discr_ptr = __mlir_op.`pop.variant.discr_gep`[ _type = __mlir_type.`!kgen.pointer>` From bf070dda6315cdbc80379340d0f4c46733d36cee Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 28 Sep 2024 21:18:01 -0700 Subject: [PATCH 1641/2019] [mojo-lang][mojo-stdlib] Fix overloaded getitem's that `ref` return This fixes the compiler to handle `ref` results from getitem implementations when there is also a setitem. This allows us to upgrade `Dict` to return elements by-ref instead of by-copy, which should be a significant performance improvement for code that uses `Dict`. This Fixes MOCO-1244 and Fixes MSTDL-452. MODULAR_ORIG_COMMIT_REV_ID: bac9abec26491e86508356c655a9cc836fb1e93b --- docs/changelog.md | 4 ++++ stdlib/src/collections/dict.mojo | 5 +++-- stdlib/test/collections/test_dict.mojo | 12 +++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ec40428439..ffef44549b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -103,6 +103,10 @@ what we publish. - The `rebind` standard library function now works with memory-only types in addition to `@register_passable("trivial")` ones, without requiring a copy. +- The `Dict.__getitem__` method now returns a reference instead of a copy of + the value (or raises). This improves the performance of common code that + uses `Dict` by allowing borrows from the `Dict` elements. + - Autoparameterization of parameters is now supported. Specifying a parameter type with unbound parameters causes them to be implicitly added to the function signature as inferred parameters. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 29eec85392..6d2407287f 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -557,8 +557,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - # TODO(MSTDL-452): rename to __getitem__ returning a reference - fn __getitem__(self, key: K) raises -> V: + fn __getitem__( + self, key: K + ) raises -> ref [self._entries[0].value().value] Self.V: """Retrieve a value out of the dictionary. Args: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 92972b6215..438ca36ebd 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -279,10 +279,8 @@ def test_dict_copy_calls_copy_constructor(): # test values copied to new Dict var copy = Dict(other=orig) - # I _may_ have thoughts about where our performance issues - # are coming from :) - assert_equal(1, orig["a"].copy_count) - assert_equal(2, copy["a"].copy_count) + assert_equal(0, orig["a"].copy_count) + assert_equal(1, copy["a"].copy_count) assert_equal(0, orig._find_ref("a").copy_count) assert_equal(1, copy._find_ref("a").copy_count) @@ -593,11 +591,11 @@ fn test_dict_setdefault() raises: var a_def = CopyCounter() var b_def = CopyCounter() other_dict["a"] = a^ - assert_equal(1, other_dict["a"].copy_count) + assert_equal(0, other_dict["a"].copy_count) _ = other_dict.setdefault("a", a_def^) _ = other_dict.setdefault("b", b_def^) - assert_equal(1, other_dict["a"].copy_count) - assert_equal(1, other_dict["b"].copy_count) + assert_equal(0, other_dict["a"].copy_count) + assert_equal(0, other_dict["b"].copy_count) def main(): From 265b906673d1d4411989184526b7baedbe747acc Mon Sep 17 00:00:00 2001 From: soraros Date: Sun, 29 Sep 2024 15:08:50 -0400 Subject: [PATCH 1642/2019] [External] [stdlib] Use the new mem type `rebind` in stdlib (#48052) [External] [stdlib] Use the new mem type `rebind` in stdlib Co-authored-by: soraros Closes modularml/mojo#3553 MODULAR_ORIG_COMMIT_REV_ID: 1cc5fe8cb387896fafd25c1cfde22117807afa13 --- stdlib/src/builtin/tuple.mojo | 13 ++++++++----- stdlib/src/collections/inline_array.mojo | 11 ++--------- stdlib/src/collections/inline_list.mojo | 9 ++++----- stdlib/src/utils/index.mojo | 12 +++--------- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index c008de5e33..3d0d42c3b7 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -178,7 +178,9 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](self) -> ref [self] T: + fn get[ + i: Int, T: CollectionElement + ](ref [_]self) -> ref [__lifetime_of(self)] T: """Get a tuple element and rebind to the specified type. Parameters: @@ -188,10 +190,12 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): Returns: The tuple element at the requested index. """ - return rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] + return rebind[T](self[i]) @always_inline("nodebug") - fn __contains__[T: EqualityComparable](self, value: T) -> Bool: + fn __contains__[ + T: EqualityComparableCollectionElement + ](self, value: T) -> Bool: """Return whether the tuple contains the specified value. For example: @@ -217,8 +221,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter if _type_is_eq[element_types[i], T](): - var elt_ptr = UnsafePointer.address_of(self[i]).bitcast[T]() - if elt_ptr[] == value: + if self.get[i, T]() == value: return True return False diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 9362127244..9fdd32d10b 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -322,7 +322,7 @@ struct InlineArray[ @always_inline fn __contains__[ T: EqualityComparableCollectionElement, // - ](self, value: T) -> Bool: + ](self: InlineArray[T, size], value: T) -> Bool: """Verify if a given value is present in the array. ```mojo @@ -341,16 +341,9 @@ struct InlineArray[ Returns: True if the value is contained in the array, False otherwise. """ - constrained[ - _type_is_eq[T, Self.ElementType](), - "T must be equal to Self.ElementType", - ]() @parameter for i in range(size): - if ( - rebind[Reference[T, __lifetime_of(self)]](Reference(self[i]))[] - == value - ): + if self[i] == value: return True return False diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 592072a002..a780e6b7df 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -202,8 +202,8 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): constrained[ _type_is_eq[ElementType, C](), "value type is not self.ElementType" ]() - for i in self: - if value == rebind[Reference[C, __lifetime_of(self)]](i)[]: + for e in self: + if rebind[C](e[]) == value: return True return False @@ -235,9 +235,8 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): ]() var count = 0 - for elem in self: - if value == rebind[Reference[C, __lifetime_of(self)]](elem)[]: - count += 1 + for e in self: + count += int(rebind[C](e[]) == value) return count fn append(inout self, owned value: ElementType): diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 31db793173..8dfee900ce 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -216,9 +216,7 @@ struct StaticIntTuple[size: Int]( @parameter fn fill[idx: Int](): - tup[idx] = rebind[Reference[Int, __lifetime_of(elems)]]( - Reference(elems[idx]) - )[] + tup[idx] = rebind[Int](elems[idx]) unroll[fill, 2]() @@ -243,9 +241,7 @@ struct StaticIntTuple[size: Int]( @parameter fn fill[idx: Int](): - tup[idx] = rebind[Reference[Int, __lifetime_of(elems)]]( - Reference(elems[idx]) - )[] + tup[idx] = rebind[Int](elems[idx]) unroll[fill, 3]() @@ -270,9 +266,7 @@ struct StaticIntTuple[size: Int]( @parameter fn fill[idx: Int](): - tup[idx] = rebind[Reference[Int, __lifetime_of(elems)]]( - Reference(elems[idx]) - )[] + tup[idx] = rebind[Int](elems[idx]) unroll[fill, 4]() From 9312795e9ac7bafe9588d2231ea14f997e210998 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Mon, 30 Sep 2024 12:12:51 -0400 Subject: [PATCH 1643/2019] [External] [stdlib] Prevent crash for non-subscriptable `PythonObject`s (#48051) [External] [stdlib] Prevent crash for non-subscriptable `PythonObject`s Add a null check for `callable_obj` in `PythonObject.__getitem__` method to prevent crashes for non-subscriptable objects. **NOTE:** This results in a different error than Python's `'' is not subscriptable`: - We raise an `AttributeError` at method lookup, while Python raises a `TypeError`. - Due to call of `PyObject_GetAttrString` as apposed to `PyObect_GetItem` We may want to consider handling for non-variadic arguments, leveraging [`PyObject_GetItem`](https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem). It's part of the stable ABI and may improve Python interop, perhaps some performance gain aswell. Co-authored-by: Joshua James Venter Closes modularml/mojo#3557 MODULAR_ORIG_COMMIT_REV_ID: 9f674179fc66b13b9485b3ad5aa5876ba085757f --- stdlib/src/python/python_object.mojo | 5 ++- stdlib/test/python/test_python_object.mojo | 45 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index c937ae9a5b..7c6bb35c4d 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -697,8 +697,8 @@ struct PythonObject( Returns: The value corresponding to the given key for this object. """ - var size = len(args) var cpython = _get_global_python_itf().cpython() + var size = len(args) var tuple_obj = cpython.PyTuple_New(size) for i in range(size): var arg_value = args[i].py_object @@ -710,6 +710,9 @@ struct PythonObject( var callable_obj = cpython.PyObject_GetAttrString( self.py_object, "__getitem__" ) + if callable_obj.is_null(): + cpython.Py_DecRef(tuple_obj) + Python.throw_python_exception_if_error_state(cpython) var result = cpython.PyObject_CallObject(callable_obj, tuple_obj) cpython.Py_DecRef(callable_obj) cpython.Py_DecRef(tuple_obj) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 72f8efc640..ad5397be65 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -409,6 +409,50 @@ fn test_none() raises: assert_true(n is None) +fn test_getitem_raises() raises: + var a = PythonObject(2) + with assert_raises(contains="'int' object has no attribute '__getitem__'"): + _ = a[0] + + var b = PythonObject(2.2) + with assert_raises( + contains="'float' object has no attribute '__getitem__'" + ): + _ = b[0] + + var c = PythonObject(True) + with assert_raises(contains="'bool' object has no attribute '__getitem__'"): + _ = c[0] + + var d = PythonObject(None) + with assert_raises( + contains="'NoneType' object has no attribute '__getitem__'" + ): + _ = d[0] + + var with_get = Python.evaluate( + "type('WithGetItem', (), {'__getitem__': lambda self, key: f\"Key:" + ' {key}"})()' + ) + assert_equal("Key: 0", str(with_get[0])) + + var without_get = Python.evaluate( + "type('WithOutGetItem', (), {'__str__': \"SomeString\"})()" + ) + with assert_raises( + contains="'WithOutGetItem' object has no attribute '__getitem__'" + ): + _ = without_get[0] + + var with_get_exception = Python.evaluate( + "type('WithGetItemException', (), {'__getitem__': lambda self, key: (_" + ' for _ in ()).throw(ValueError("Custom error")),})()' + ) + + with assert_raises(contains="Custom error"): + _ = with_get_exception[1] + + def main(): # initializing Python instance calls init_python var python = Python() @@ -423,3 +467,4 @@ def main(): test_dict() test_none() test_nested_object() + test_getitem_raises() From 1a704d12d9196b8c4e0a79d7303bc697d9c9751a Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 30 Sep 2024 12:09:36 -0700 Subject: [PATCH 1644/2019] [docs] Add changelog entry for `-D ASSERT` flag change Adds a changelog entry describing the new `debug_assert` behavior and `-D ASSERT` flag. MODULAR_ORIG_COMMIT_REV_ID: 870c5d264db441adf5e71c7b4a49994bdb8a6f7b --- docs/changelog.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index ffef44549b..fdeb2a09d3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -198,6 +198,38 @@ what we publish. - `String.as_bytes_slice()` is renamed to `String.as_bytes_span()` since it returns a `Span` and not a `StringSlice`. +- The flag for turning on asserts has changed, e.g. to enable all checks: + + ```bash + mojo -D ASSERT=all main.mojo + ``` + + The levels are: + + - none: all assertions off + - warn: print assertion errors e.g. for multithreaded tests (previously -D + ASSERT_WARNING) + - safe: the default mode for standard CPU safety assertions + - all: turn on all assertions (previously -D MOJO_ENABLE_ASSERTIONS) + + You can now also pass `Stringable` args to format a message, which will have + no runtime penalty or IR bloat cost when assertions are off. Previously you + had to: + + ```mojo + x = -1 + debug_assert( + x > 0, String.format_sequence(“expected x to be more than 0 but got: ”, x) + ) + ``` + + Which can't be optimized away by the compiler in release builds, you can now + pass multiple args for a formatted message at no runtime cost: + + ```mojo + debug_assert(x > 0, “expected x to be more than 0 but got: ”, x) + ``` + ### ❌ Removed ### 🛠️ Fixed From c653ae42ae321c0d88021b44db9bd491149895be Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 30 Sep 2024 12:38:04 -0700 Subject: [PATCH 1645/2019] [stdlib] Clean up assert constrained call Use `in` instead of multiple `if` statements. MODULAR_ORIG_COMMIT_REV_ID: 498f01e6b30186facc132a56486b6b75708b6ca2 --- stdlib/src/builtin/debug_assert.mojo | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index b7472dc93e..6edd91d9a4 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -29,16 +29,13 @@ alias defined_mode = env_get_string["ASSERT", "safe"]() @no_inline fn _assert_enabled[assert_mode: StringLiteral, cpu_only: Bool]() -> Bool: constrained[ - defined_mode == "none" - or defined_mode == "warn" - or defined_mode == "safe" - or defined_mode == "all", + defined_mode in ["none", "warn", "safe", "all"], "-D ASSERT=" + defined_mode + " but must be one of: none, warn, safe, all", ]() constrained[ - assert_mode == "none" or assert_mode == "safe", + assert_mode in ["none", "safe"], "assert_mode=" + assert_mode + " but must be one of: none, safe", ]() From bd49f5248b72d075afe19a95e357170262dc0cba Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 30 Sep 2024 16:06:39 -0500 Subject: [PATCH 1646/2019] [stdlib] feat: Support raising Python errors from Mojo * Add `Error.unsafe_cstr_ptr()` * Add CPython wrappers for PyErr_SetNone and PyErr_SetString. * Add CPython `get_error_global()`, for getting a borrowed reference to one of the CPython error global static objects. MODULAR_ORIG_COMMIT_REV_ID: 14ad1f54a2f627904af9af7f31fd89e4ae6961ba --- stdlib/src/builtin/error.mojo | 27 ++++++++++ stdlib/src/python/_cpython.mojo | 89 +++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index a819e94d4d..00d7ebb439 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from sys import alignof, sizeof +from sys.ffi import c_char from memory import UnsafePointer, memcpy from memory.memory import _free @@ -38,6 +39,10 @@ struct Error( ): """This type represents an Error.""" + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + var data: UnsafePointer[UInt8] """A pointer to the beginning of the string data being referenced.""" @@ -49,6 +54,10 @@ struct Error( ownership and a free is executed in the destructor. """ + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline fn __init__(inout self): """Default constructor.""" @@ -128,6 +137,10 @@ struct Error( self.data = existing.data self.loaded_length = existing.loaded_length + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + fn __bool__(self) -> Bool: """Returns True if the error is set and false otherwise. @@ -166,6 +179,20 @@ struct Error( """ return "Error(" + repr(self._message()) + ")" + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn unsafe_cstr_ptr(self) -> UnsafePointer[c_char]: + """Retrieves a C-string-compatible pointer to the underlying memory. + + The returned pointer is guaranteed to be NUL terminated, and not null. + + Returns: + The pointer to the underlying memory. + """ + return self.data.bitcast[c_char]() + fn _message(self) -> String: """Converts the Error to string representation. diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index e1acfeeea4..60d5901dea 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -19,6 +19,8 @@ from sys import external_call from sys.arg import argv from sys.ffi import DLHandle, c_char, c_int, OpaquePointer +from python.python import _get_global_python_itf + from memory import UnsafePointer from utils import StringRef @@ -72,6 +74,44 @@ fn create_wrapper_function[ return wrapper +# Wrap a `raises` function +fn create_wrapper_function[ + user_func: fn ( + PythonObject, TypedPythonObject["Tuple"] + ) raises -> PythonObject +]() -> PyCFunction: + fn wrapper( + py_self: PythonObject, args: TypedPythonObject["Tuple"] + ) -> PythonObject: + var cpython = _get_global_python_itf().cpython() + + var state = cpython.PyGILState_Ensure() + + try: + var result = user_func(py_self, args) + return result + except e: + # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. + var error_type = cpython.get_error_global("PyExc_Exception") + + cpython.PyErr_SetString( + error_type, + e.unsafe_cstr_ptr(), + ) + + # Return a NULL `PyObject*`. + return PythonObject(PyObjectPtr()) + finally: + cpython.PyGILState_Release(state) + + # TODO: + # Does this lead to multiple levels of indirect function calls for + # `raises` functions? Could we fix that by marking `wrapper` here as + # `@always_inline`? + # Call the non-`raises` overload of `create_wrapper_function`. + return create_wrapper_function[wrapper]() + + # ===-----------------------------------------------------------------------===# # Raw Bindings # ===-----------------------------------------------------------------------===# @@ -1344,6 +1384,55 @@ struct CPython: _ = traceback return r + fn PyErr_SetNone( + inout self, + type: PyObjectPtr, + ): + var func = self.lib.get_function[fn (PyObjectPtr) -> None]( + "PyErr_SetNone" + ) + + return func(type) + + fn PyErr_SetString( + inout self, + type: PyObjectPtr, + message: UnsafePointer[c_char], + ): + self.lib.get_function[fn (PyObjectPtr, UnsafePointer[c_char]) -> None]( + "PyErr_SetString" + )(type, message) + + # ===-------------------------------------------------------------------===# + # Python Error types + # ===-------------------------------------------------------------------===# + + fn get_error_global( + inout self, + global_name: StringLiteral, + ) -> PyObjectPtr: + """Get a Python borrowed reference to the specified global exception object. + """ + + # Get pointer to the immortal `global_name` PyObject struct + # instance. + var ptr: UnsafePointer[PyObjectPtr] = self.lib.get_symbol[PyObjectPtr]( + global_name + ) + + if not ptr: + abort( + "error: unable to get pointer to CPython `" + + global_name + + "` global" + ) + + return ptr[] + + # ===-------------------------------------------------------------------===# + # Python Iterator operations + # ===-------------------------------------------------------------------===# + fn PyIter_Next(inout self, iterator: PyObjectPtr) -> PyObjectPtr: var next_obj = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyIter_Next" From 2b2713a59c2e918db8ad3aec617c2fef9099354d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 30 Sep 2024 21:34:45 -0700 Subject: [PATCH 1647/2019] [Stdlib] Enhance the bitcast operation to include an alignment flag This allows one to override the alignment field within the UnsafePointer. It will be used in subsequent PRs. MODULAR_ORIG_COMMIT_REV_ID: 284eac67aa61a55c72685ec72c5c37bae689d08e --- stdlib/src/memory/unsafe_pointer.mojo | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 41090388fb..980f42aeca 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -793,6 +793,7 @@ struct UnsafePointer[ T: AnyType = Self.type, /, address_space: AddressSpace = Self.address_space, + alignment: Int = Self.alignment, lifetime: Lifetime[True].type = Self.lifetime, ](self) -> UnsafePointer[ T, address_space, Self.exclusive, alignment, lifetime @@ -802,6 +803,7 @@ struct UnsafePointer[ Parameters: T: The target type. address_space: The address space of the result. + alignment: Alignment of the destination pointer. lifetime: Lifetime of the destination pointer. Returns: @@ -819,6 +821,7 @@ struct UnsafePointer[ T: DType, /, address_space: AddressSpace = Self.address_space, + alignment: Int = Self.alignment, lifetime: Lifetime[True].type = Self.lifetime, ](self) -> UnsafePointer[ Scalar[T], address_space, Self.exclusive, alignment, lifetime @@ -828,13 +831,16 @@ struct UnsafePointer[ Parameters: T: The target type. address_space: The address space of the result. + alignment: Alignment of the destination pointer. lifetime: Lifetime of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, as the original UnsafePointer. """ - return self.bitcast[Scalar[T], address_space]() + return self.bitcast[ + Scalar[T], address_space=address_space, alignment=alignment + ]() @always_inline fn destroy_pointee(self: UnsafePointer[type, AddressSpace.GENERIC, *_]): From 5f119e24bfa07c7d4fd1a8eabb560a3483a4692b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 30 Sep 2024 23:08:37 -0700 Subject: [PATCH 1648/2019] [Stdlib] Add test case for the alignment bitcast, NFC MODULAR_ORIG_COMMIT_REV_ID: d53b529773b5590841168753bf5072e025904f06 --- stdlib/test/memory/test_unsafepointer.mojo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index de9db6c9c3..dedda58f4d 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -132,6 +132,9 @@ def test_bitcast(): assert_equal(int(ptr), int(ptr.bitcast[Int]())) assert_equal(int(ptr), int(aliased_ptr)) + + assert_equal(ptr.bitcast[ptr.type, alignment=33]().alignment, 33) + _ = local From d3df5f0888c70aacf3901b62e1424e824ea533e4 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 30 Sep 2024 23:14:35 -0700 Subject: [PATCH 1649/2019] [mojo-stdlib] Simplify some code using `Reference`, NFC This simplifies some code using references, split out of a bigger patch. MODULAR_ORIG_COMMIT_REV_ID: baa95a67692302c1a38bc6160dea977f4a0db3e8 --- stdlib/src/builtin/tuple.mojo | 4 +--- stdlib/src/collections/inline_array.mojo | 8 ++------ stdlib/src/utils/variant.mojo | 4 +--- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 3d0d42c3b7..389c2c0e8e 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -178,9 +178,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[ - i: Int, T: CollectionElement - ](ref [_]self) -> ref [__lifetime_of(self)] T: + fn get[i: Int, T: CollectionElement](ref [_]self) -> ref [self] T: """Get a tuple element and rebind to the specified type. Parameters: diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 9fdd32d10b..48d1c79b4c 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -173,12 +173,8 @@ struct InlineArray[ # Move each element into the array storage. @parameter for i in range(size): - var eltref: Reference[ - Self.ElementType, __lifetime_of(self) - ] = self.unsafe_get(i) - UnsafePointer.address_of(storage[i]).move_pointee_into( - UnsafePointer[Self.ElementType].address_of(eltref[]) - ) + var eltptr = UnsafePointer.address_of(self.unsafe_get(i)) + UnsafePointer.address_of(storage[i]).move_pointee_into(eltptr) # Mark the elements as already destroyed. storage._is_owned = False diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index ef6a35be4b..3ecdc0de4a 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -362,9 +362,7 @@ struct Variant[*Ts: CollectionElement]( alias idx = Self._check[T]() return self._get_discr() == idx - fn unsafe_get[ - T: CollectionElement - ](ref [_]self: Self) -> ref [__lifetime_of(self)] T: + fn unsafe_get[T: CollectionElement](ref [_]self: Self) -> ref [self] T: """Get the value out of the variant as a type-checked type. This doesn't explicitly check that your value is of that type! From a9ce226373e18358c18423e6b823c369ecc5ea1e Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 1 Oct 2024 11:18:59 -0400 Subject: [PATCH 1650/2019] [External] [stdlib] Fix dostrings in `uint` (#48102) [External] [stdlib] Fix dostrings in `uint` Part of https://github.com/modularml/mojo/issues/3572 Co-authored-by: soraros Closes modularml/mojo#3575 MODULAR_ORIG_COMMIT_REV_ID: 6a29cafdba1d34b6314d197d981d31341ccd3be2 --- stdlib/src/builtin/uint.mojo | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 5ff6c01972..8efc3cc4ef 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -98,8 +98,9 @@ struct UInt(IntLike): A small example. ```mojo - var x = UInt(50) - var x_as_string = str(x) # x_as_string = "50" + %# from testing import assert_equal + x = UInt(50) + assert_equal(str(x), "50") ``` Returns: @@ -122,8 +123,9 @@ struct UInt(IntLike): A small example. ```mojo - var x = UInt(50) - var x_as_string = repr(x) # x_as_string = "UInt(50)" + %# from testing import assert_equal + x = UInt(50) + assert_equal(repr(x), "UInt(50)") ``` Returns: From 524f5c7fa59c1c81902f58cecdf8d8c5014e02cb Mon Sep 17 00:00:00 2001 From: Peiming Liu Date: Tue, 1 Oct 2024 09:25:46 -0700 Subject: [PATCH 1651/2019] [stdlib] switch Bool.__and__/__or__/__xor__ to use i1 directly. MODULAR_ORIG_COMMIT_REV_ID: b33c7f6882fdea3007947e1ec36cc02a04f75457 --- stdlib/src/builtin/bool.mojo | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 0c841090dc..3978c7021c 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -373,11 +373,11 @@ struct Bool( Returns: True if the object is false and False otherwise. """ - var true = __mlir_op.`kgen.param.constant`[ - _type = __mlir_type.`!pop.scalar`, - value = __mlir_attr.`#pop.simd : !pop.scalar`, + var true = __mlir_op.`index.bool.constant`[ + _type = __mlir_type.i1, + value = __mlir_attr.`true : i1`, ]() - return __mlir_op.`pop.simd.xor`(self._as_scalar_bool(), true) + return __mlir_op.`pop.xor`(self.value, true) @always_inline("nodebug") fn __and__(self, rhs: Bool) -> Bool: @@ -392,9 +392,7 @@ struct Bool( Returns: `self & rhs`. """ - return __mlir_op.`pop.simd.and`( - self._as_scalar_bool(), rhs._as_scalar_bool() - ) + return __mlir_op.`pop.and`(self.value, rhs.value) @always_inline("nodebug") fn __iand__(inout self, rhs: Bool): @@ -430,9 +428,7 @@ struct Bool( Returns: `self | rhs`. """ - return __mlir_op.`pop.simd.or`( - self._as_scalar_bool(), rhs._as_scalar_bool() - ) + return __mlir_op.`pop.or`(self.value, rhs.value) @always_inline("nodebug") fn __ior__(inout self, rhs: Bool): @@ -468,9 +464,7 @@ struct Bool( Returns: `self ^ rhs`. """ - return __mlir_op.`pop.simd.xor`( - self._as_scalar_bool(), rhs._as_scalar_bool() - ) + return __mlir_op.`pop.xor`(self.value, rhs.value) @always_inline("nodebug") fn __ixor__(inout self, rhs: Bool): From 1b15b9c776cf0ab7f181127f488ad775fe80a01a Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 1 Oct 2024 12:34:21 -0400 Subject: [PATCH 1652/2019] [External] [stdlib] Fix docstring in `builtin._documentation` (#48103) [External] [stdlib] Fix docstring in `builtin._documentation` Part of https://github.com/modularml/mojo/issues/3572 Co-authored-by: soraros Closes modularml/mojo#3573 MODULAR_ORIG_COMMIT_REV_ID: 75ee39440ff6061857c79354b97fffa4e06b7dd3 --- stdlib/src/builtin/_documentation.mojo | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/stdlib/src/builtin/_documentation.mojo b/stdlib/src/builtin/_documentation.mojo index cbe4c23663..7c7f600048 100644 --- a/stdlib/src/builtin/_documentation.mojo +++ b/stdlib/src/builtin/_documentation.mojo @@ -32,15 +32,16 @@ fn doc_private(): For example: ```mojo + %# from builtin._documentation import doc_private struct Foo: - @doc_private - fn __init__(inout self): - "This should not be called directly, prefer Foo.create instead." - return - - @staticmethod - fn create() -> Self: - return Self() + @doc_private + fn __init__(inout self): + "This should not be called directly, use `Foo.create` instead." + return + + @staticmethod + fn create() -> Self: + return Self() ``` """ return From f7d4432c102e07e27ef320e0d28b17c7492ff212 Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 1 Oct 2024 12:42:55 -0400 Subject: [PATCH 1653/2019] [External] [stdlib] Fix docstrings in `int` (#48101) [External] [stdlib] Fix docstrings in `int` Part of https://github.com/modularml/mojo/issues/3572 Co-authored-by: soraros Closes modularml/mojo#3574 MODULAR_ORIG_COMMIT_REV_ID: 28bc120b01373d63f74789f03075f3b008745584 --- stdlib/src/builtin/int.mojo | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 74bf38c133..9ee87ea89d 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -106,12 +106,9 @@ trait Intable: `Int`: ```mojo - var foo = Foo(42) - print(int(foo) == 42) - ``` - - ```plaintext - True + %# from testing import assert_equal + foo = Foo(42) + assert_equal(int(foo), 42) ``` **Note:** If the `__int__()` method can raise an error, use the @@ -153,13 +150,9 @@ trait IntableRaising: `Int`: ```mojo - fn main() raises: - var x = Foo(42) - print(int(x) == 42) - ``` - - ```plaintext - True + %# from testing import assert_equal + foo = Foo(42) + assert_equal(int(foo), 42) ``` """ @@ -1177,9 +1170,9 @@ struct Int( Examples: ```mojo - assert_equal(10._decimal_digit_count(), 2) - - assert_equal(-10._decimal_digit_count(), 2) + %# from testing import assert_equal + assert_equal(Int(10)._decimal_digit_count(), 2) + assert_equal(Int(-10)._decimal_digit_count(), 2) ``` . """ From c653301f3eb1ba11e85aa2ed1f61e9c239c6f694 Mon Sep 17 00:00:00 2001 From: Yi-Chi Lee <55395582+yichi170@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:48:21 -0400 Subject: [PATCH 1654/2019] [External] [stdlib] fix: set constraint in `rotate_bits_*` function to bit size (#48107) [External] [stdlib] fix: set constraint in `rotate_bits_*` function to bit size Set constraint to bit size instead of byte size in `rotate_bits_left` and `rotate_bits_right`. Fixes https://github.com/modularml/mojo/issues/3538. Co-authored-by: Yi-Chi Lee <55395582+yichi170@users.noreply.github.com> Closes modularml/mojo#3569 MODULAR_ORIG_COMMIT_REV_ID: 297bf9a45dd130039f842d794ae63ffb58201f79 --- stdlib/src/bit/bit.mojo | 8 ++++---- stdlib/test/bit/test_bit.mojo | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index d3a48a938e..bd5b8b81a0 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -520,8 +520,8 @@ fn rotate_bits_left[shift: Int](x: Int) -> Int: The input rotated to the left by `shift` elements (with wrap-around). """ constrained[ - shift >= -sizeof[Int]() and shift < sizeof[Int](), - "Constraints: -sizeof[Int]() <= shift < sizeof[Int]()", + shift >= -bitwidthof[Int]() and shift < bitwidthof[Int](), + "Constraints: -bitwidthof[Int]() <= shift < bitwidthof[Int]()", ]() @parameter @@ -598,8 +598,8 @@ fn rotate_bits_right[shift: Int](x: Int) -> Int: The input rotated to the right by `shift` elements (with wrap-around). """ constrained[ - shift >= -sizeof[Int]() and shift < sizeof[Int](), - "Constraints: -sizeof[Int]() <= shift < sizeof[Int]()", + shift >= -bitwidthof[Int]() and shift < bitwidthof[Int](), + "Constraints: -bitwidthof[Int]() <= shift < bitwidthof[Int]()", ]() @parameter diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index c58fc00a26..9fcb5f3871 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -447,10 +447,14 @@ def test_rotate_bits_int(): assert_equal(rotate_bits_left[0](104), 104) assert_equal(rotate_bits_left[2](104), 416) assert_equal(rotate_bits_left[-2](104), 26) + assert_equal(rotate_bits_left[8](104), 26624) + assert_equal(rotate_bits_left[-8](104), 7493989779944505344) assert_equal(rotate_bits_right[0](104), 104) assert_equal(rotate_bits_right[2](104), 26) assert_equal(rotate_bits_right[-2](104), 416) + assert_equal(rotate_bits_right[8](104), 7493989779944505344) + assert_equal(rotate_bits_right[-8](104), 26624) def test_rotate_bits_simd(): From 5151a33913a02b80d79a01841bde69432bdc58f7 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 1 Oct 2024 12:01:59 -0500 Subject: [PATCH 1655/2019] [stdlib] feat: Add `c_uint` + fix misuse of `mark_destroyed` `mark_destroyed` does not prevent the _fields_ of a type from being destroyed, so its not sufficient to only mark the parent struct as destroyed. (In this way, `mark_destroyed` is not exactly equivalent to `std::mem::forget()` from Rust.) MODULAR_ORIG_COMMIT_REV_ID: 640993d8da311e8eccb3e3a7cd10fa219792ffb4 --- stdlib/src/python/_cpython.mojo | 13 +++++++++++++ stdlib/src/sys/ffi.mojo | 3 +++ 2 files changed, 16 insertions(+) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 60d5901dea..29b73d33f5 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -10,6 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +""" +Mojo bindings functions and types from the CPython C API. + +Documentation for these functions can be found online at: + +""" from collections import InlineArray from os import getenv, setenv, abort @@ -67,7 +73,14 @@ fn create_wrapper_function[ __mlir_op.`lit.ownership.mark_destroyed`( __get_mvalue_as_litref(py_self) ) + + # SAFETY: + # Prevent `args` AND `args._obj` from being destroyed, since we don't + # own them. + # TODO: Use a `mem.forget(args^)` function here in the future. __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(args)) + var _obj = args._obj^ + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(_obj)) return result diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 8cb52f302f..99460e3da8 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -33,6 +33,9 @@ The C `int` type is typically a signed 32-bit integer on commonly used targets today. """ +alias c_uint = UInt32 +"""C `unsigned int` type.""" + alias c_long = Scalar[_c_long_dtype()] """C `long` type. From b6c946000eac1682f3c6220a76d1e6e0b3e5f763 Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 1 Oct 2024 13:27:43 -0400 Subject: [PATCH 1656/2019] [External] [stdlib] Remove `_type_is_eq` from `Span` (#48221) [External] [stdlib] Remove `_type_is_eq` from `Span` Co-authored-by: soraros Closes modularml/mojo#3584 MODULAR_ORIG_COMMIT_REV_ID: 06d0e9767af88b301561c9fd750d90fe77bed982 --- stdlib/src/utils/span.mojo | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 543b1b7ea4..46ed1a4e82 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -22,7 +22,6 @@ from utils import Span from collections import InlineArray from memory import Reference, UnsafePointer -from sys.intrinsics import _type_is_eq from builtin.builtin_list import _lit_mut_cast @@ -125,20 +124,17 @@ struct Span[ @always_inline fn __init__[ - T2: CollectionElement, size: Int, // - ](inout self, ref [lifetime]array: InlineArray[T2, size]): + size: Int, // + ](inout self, ref [lifetime]array: InlineArray[T, size]): """Construct a Span from an InlineArray. Parameters: - T2: The type of the elements in the span. size: The size of the InlineArray. Args: array: The array to which the span refers. """ - constrained[_type_is_eq[T, T2](), "array element is not Span.T"]() - self._data = UnsafePointer.address_of(array).bitcast[T]() self._len = size From d45e8cf99bd75c15b4d10cb68806bafed7f10b88 Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 1 Oct 2024 13:28:28 -0400 Subject: [PATCH 1657/2019] [External] [stdlib] Various cleanups to `ListLiteral` (#48220) [External] [stdlib] Various cleanups to `ListLiteral` Co-authored-by: soraros Closes modularml/mojo#3582 MODULAR_ORIG_COMMIT_REV_ID: 1a5fc22dbfff7b49ae1728a5d8f2184fc4c55ab1 --- stdlib/src/builtin/builtin_list.mojo | 33 +++++++--------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 5b180fb584..41235a3361 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -17,7 +17,6 @@ These are Mojo built-ins, so you don't need to import them. from memory import Reference, UnsafePointer -from sys.intrinsics import _type_is_eq # ===----------------------------------------------------------------------===# # ListLiteral @@ -85,7 +84,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](self) -> ref [self] T: + fn get[i: Int, T: CollectionElement](self) -> ref [self.storage] T: """Get a list element at the given index. Parameters: @@ -95,15 +94,16 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): Returns: The element at the given index. """ - # FIXME: Rebinding to a different lifetime. - return UnsafePointer.address_of(self.storage[i]).bitcast[T]()[] + return self.storage.get[i, T]() # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __contains__[T: EqualityComparable](self, value: T) -> Bool: + fn __contains__[ + T: EqualityComparableCollectionElement + ](self, value: T) -> Bool: """Determines if a given value exists in the ListLiteral. Parameters: @@ -116,17 +116,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): Returns: True if the value is found in the ListLiteral, False otherwise. """ - - @parameter - for i in range(len(VariadicList(Ts))): - if _type_is_eq[Ts[i], T](): - var elt_ptr = UnsafePointer.address_of(self.storage[i]).bitcast[ - T - ]() - if elt_ptr[] == value: - return True - - return False + return value in self.storage # ===----------------------------------------------------------------------===# @@ -645,17 +635,10 @@ struct VariadicPack[ A reference to the element. The Reference's mutability follows the mutability of the pack argument convention. """ - var litref_elt = __mlir_op.`lit.ref.pack.extract`[index = index.value]( + litref_elt = __mlir_op.`lit.ref.pack.extract`[index = index.value]( self._value ) - - # Rebind the !lit.ref to agree on the element type. This is needed - # because we're getting a low level rebind to AnyType when the - # element_types[index] expression is erased to AnyType for Reference. - var ref_elt = UnsafePointer.address_of( - __get_litref_as_mvalue(litref_elt) - ) - return ref_elt.bitcast[element_types[index.value]]()[] + return __get_litref_as_mvalue(litref_elt) # ===-------------------------------------------------------------------===# # Methods From d2e419f8bbe42412287f845c27360fe39364e4f5 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:44:39 -0700 Subject: [PATCH 1658/2019] Updated error handling documentation to incorporate reviewer feedback MODULAR_ORIG_COMMIT_REV_ID: 6ad8d88fdd2b73821af7ac585be2e4672af1d548 --- docs/manual/errors.mdx | 86 ++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/docs/manual/errors.mdx b/docs/manual/errors.mdx index c072092858..e94a6d40e4 100644 --- a/docs/manual/errors.mdx +++ b/docs/manual/errors.mdx @@ -92,19 +92,34 @@ clause is optional. The `try` clause contains a code block to execute that might raise an error. If no error occurs, the entire code block executes. If an error occurs, execution -of the code block stops at the point that the error is raised. +of the code block stops at the point that the error is raised. Your program then +continues with the execution of the `except` clause, if provided, or the +`finally` clause. If the `except` clause is present, its code block executes only if an error -occurred in the `try` clause. You can optionally provide the name of a variable -after the `except` keyword. The `Error` instance is bound to the variable if an -error occurs. The `except` clause "consumes" the error that occurred in the -`try` clause. If desired, you can re-raise an error condition by executing a -`raise` statement from within the `except` clause. +occurred in the `try` clause. The `except` clause "consumes" the error that +occurred in the `try` clause. You can then implement any error handling or +recovery that's appropriate for your application. + +If you provide the name of a variable after the `except` keyword, then the +`Error` instance is bound to the variable if an error occurs. The `Error` type +implements the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait, so +you can pass it as an argument to the [`print()`](/mojo/stdlib/builtin/io/print) +function if you'd like to print its error message to the console. It also +implements the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, so you +can pass it to the [`str()`](/mojo/stdlib/builtin/str/str) function if you want +to extract the error message as a `String` for further processing. + +If desired, you can re-raise an error condition from your `except` clause simply +by executing a `raise` statement from within its code block. This can be either +a new `Error` instance or, if you provided a variable name to capture the +`Error` that occurred originally, you can re-raise that error. :::note -Because Mojo does not currently support typed errors, you can include at most -one `except` clause, which catches any `Error` raised. +Because Mojo does not currently support typed errors, a `try-except` control +structure can include at most one `except` clause, which catches any `Error` +raised. ::: @@ -116,9 +131,9 @@ not occur in the `try` clause. Note that the `else` clause is *skipped* if the If the `finally` clause is present, its code block executes after the `try` clause and the `except` or `else` clause, if applicable. The `finally` clause executes even if one of the other code blocks exit by executing a `continue`, -`break`, or `return` statement. The `finally` clause is often used to release -resources used by the `try` clause (such as a file handle) regardless of whether -or not an error occurred. +`break`, or `return` statement or by raising an error. The `finally` clause is +often used to release resources used by the `try` clause (such as a file handle) +regardless of whether or not an error occurred. As an example, consider the following program: @@ -182,17 +197,18 @@ f.close() ``` Calling [`close()`](/mojo/stdlib/builtin/file/FileHandle#close) releases the -operating system resources associated with the opened file. If your program were -to open many files without closing them, you could exhaust the resources -available to your program and cause errors. The problem is even worse if you -were writing to a file instead of reading from it, because the operating system -might buffer the output in memory until the file is closed. If your program were -to crash instead of exiting normally, that buffered data could be lost instead -of being written to storage. The example above actually includes the call to -`close()`, but it ignores the possibility that -[`read()`](/mojo/stdlib/builtin/file/FileHandle#read) could raise an error, -which would result in the program not executing the `close()`. To handle this -scenario you would need to rewrite the code like this: +memory and other operating system resources associated with the opened file. If +your program were to open many files without closing them, you could exhaust the +resources available to your program and cause errors. The problem is even worse +if you were writing to a file instead of reading from it, because the operating +system might buffer the output in memory until the file is closed. If your +program were to crash instead of exiting normally, that buffered data could be +lost instead of being written to storage. + +The example above actually includes the call to `close()`, but it ignores the +possibility that [`read()`](/mojo/stdlib/builtin/file/FileHandle#read) could +raise an error, which would result in the program not executing the `close()`. +To handle this scenario you could rewrite the code to use `try` like this: ```mojo # Obtain a file handle to read from storage @@ -210,8 +226,10 @@ However, the [`FileHandle`](/mojo/stdlib/builtin/file/FileHandle) struct returned by [`open()`](/mojo/stdlib/builtin/file/open) is a context manager. When used in conjunction with Mojo's `with` statement, a context manager ensures that the resources it manages are properly released at the end of the block, -even if an error occurs. You could rewrite the example above to take advantage -of the context manager like this: +even if an error occurs. In the case of a `FileHandle`, that means that the call +to `close()` takes place automatically. So you could rewrite the example above +to take advantage of the context manager—and omit the explicit call to +`close()`—like this: ```mojo with open(input_file, "r") as f: @@ -238,7 +256,7 @@ of context managers in the Mojo standard library are [`BlockingScopedLock`](/mojo/stdlib/utils/lock/BlockingScopedLock), and [`assert_raises`](/mojo/stdlib/testing/testing/assert_raises). You can also create your own custom context managers, as described in [Write a custom context -manager](#write-a-custom-context-manager). +manager](#write-a-custom-context-manager) below. ## Write a custom context manager @@ -258,10 +276,11 @@ Writing a custom context manager is a matter of defining a If the `with` code block raises an error, then the `__exit__()` method runs before any error processing occurs (that is, before it is caught by a - `try-except` structure or your program terminates). As discussed in [Define a - conditional `__exit__()` method](#define-a-conditional-__exit__-method), there - is an overloaded form of `__exit__()` that you can implement to determine - custom processing for error conditions in a `with` code block. + `try-except` structure or your program terminates). If you'd like to define + conditional processing for error conditions in a `with` code block, you can + implement an overloaded version of `__exit__()` that takes an `Error` + argument. For more information, see [Define a conditional `__exit__()` + method](#define-a-conditional-__exit__-method) below. For context managers that don't need to release resources or perform other actions on termination, you are not required to implement an `__exit__()` @@ -325,10 +344,11 @@ Unhandled exception caught during execution: simulated error ### Define a conditional `__exit__()` method -When creating a context manager, you must implement the `__exit__(self)` form of -the `__exit__()` method. However, you also have the option of implementing an -overloaded version that is invoked only when an error occurs in the `with` code -block: +When creating a context manager, you can implement the `__exit__(self)` form of +the `__exit__()` method to handle completion of the `with` statement under all +circumstances including errors. However, you have the option of additionally +implementing an overloaded version that is invoked instead when an error occurs +in the `with` code block: ```mojo fn __exit__(self, error: Error) raises -> Bool From 766a47cc9b1fe56792255337bbefcb333702a8eb Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 1 Oct 2024 13:48:30 -0400 Subject: [PATCH 1659/2019] [External] [stdlib] Fix `bit.is_power_of_two` for `Int.MIN` (#48224) [External] [stdlib] Fix `bit.is_power_of_two` for `Int.MIN` Co-authored-by: soraros Closes modularml/mojo#3570 MODULAR_ORIG_COMMIT_REV_ID: 542172ef3104c01db7d28420a1273889c6d37160 --- stdlib/src/bit/bit.mojo | 8 ++++++-- stdlib/test/bit/test_bit.mojo | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index bd5b8b81a0..5a14fc8f21 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -359,7 +359,7 @@ fn is_power_of_two(val: Int) -> Bool: Returns: True if the input value is a power of 2, False otherwise. """ - return (val != 0) & (val & (val - 1) == 0) + return (val > 0) & (val & (val - 1) == 0) @always_inline @@ -384,7 +384,11 @@ fn is_power_of_two[ """ constrained[type.is_integral(), "must be integral"]() - return (val != 0) & (val & (val - 1) == 0) + @parameter + if type.is_unsigned(): + return pop_count(val) == 1 + else: + return (val > 0) & (val & (val - 1) == 0) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index 9fcb5f3871..dee81507d1 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -276,6 +276,7 @@ def test_bit_not_simd(): def test_is_power_of_two(): + assert_equal(is_power_of_two(Int.MIN), False) assert_equal(is_power_of_two(-(2**59)), False) assert_equal(is_power_of_two(-1), False) assert_equal(is_power_of_two(0), False) @@ -285,6 +286,7 @@ def test_is_power_of_two(): assert_equal(is_power_of_two(4), True) assert_equal(is_power_of_two(5), False) assert_equal(is_power_of_two(2**59), True) + assert_equal(is_power_of_two(Int.MAX), False) def test_is_power_of_two_simd(): @@ -322,6 +324,8 @@ def test_is_power_of_two_simd(): SIMD[DType.bool, simd_width](False, False, False, True), ) + assert_equal(is_power_of_two(Int64.MIN), False) + def test_bit_width(): assert_equal(bit_width(-(2**59)), 59) From 418a04313abe80e8a4621fcc0da642693164323f Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:56:56 -0400 Subject: [PATCH 1660/2019] [External] [stdlib] Add examples for `argv()`, `NamedTemporaryFile` and `assert_true` (#48223) [External] [stdlib] Add examples for `argv()`, `NamedTemporaryFile` and `assert_true` Hello, this commit adds a few examples: - `argv` for app inputs - `NamedTemporayFile` that uses `with` block - `assert_true` in `try:`, `except:` Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3585 MODULAR_ORIG_COMMIT_REV_ID: 795c92b4e9dd5f65d433fbf9d1aa0c2babadea5a --- stdlib/src/sys/arg.mojo | 10 ++++++++++ stdlib/src/tempfile/tempfile.mojo | 20 +++++++++++++++++++- stdlib/src/testing/testing.mojo | 12 ++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/stdlib/src/sys/arg.mojo b/stdlib/src/sys/arg.mojo index 6284910c4d..59b17b2046 100644 --- a/stdlib/src/sys/arg.mojo +++ b/stdlib/src/sys/arg.mojo @@ -17,6 +17,16 @@ You can import these APIs from the `sys` package. For example: ```mojo from sys import argv +def main(): + arguments = argv() + print( + arguments[0], #app.mojo + arguments[1] #Hello world! + ) + for arg in arguments: + print(arg) +# If the program is app.mojo: +# mojo run app.mojo "Hello world!" ``` """ diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 244f1e0e31..4acbc0cffc 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -260,7 +260,25 @@ struct TemporaryDirectory: struct NamedTemporaryFile: - """A handle to a temporary file.""" + """A handle to a temporary file. + + Example: + ```mojo + from tempfile import NamedTemporaryFile + from pathlib import Path + def main(): + var p: Path + with NamedTemporaryFile(mode="rw") as f: + p = f.name + f.write("Hello world!") + f.seek(0) + print( + f.read() == "Hello world!" + ) + print(str(p), p.exists()) #Removed by default + ``` + Note: `NamedTemporaryFile.__init__` document the arguments. + """ var _file_handle: FileHandle """The underlying file handle.""" diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index fa7646fc4a..79369bea7c 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -16,6 +16,18 @@ You can import these APIs from the `testing` package. For example: ```mojo from testing import assert_true + +def main(): + x = 1 + y = 2 + try: + assert_true(x==1) + assert_true(y==2) + assert_true((x+y)==3) + print("All assertions succeeded") + except e: + print("At least one assertion failed:") + print(e) ``` """ from collections import Optional From 9e8ca9561145993bbf2db34646f7db026f925465 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 1 Oct 2024 13:40:18 -0500 Subject: [PATCH 1661/2019] [stdlib] feat: Support adding named PythonObject value to Python modules * Add `Python.add_object()` method, used to add a named value to a Python module. * Add wrappers for CPython `PyModule_AddObjectRef` and `PyType_FromSpec` functions. * Add bindings for CPython `PyType_Spec` and `PyType_Slot` structs. MODULAR_ORIG_COMMIT_REV_ID: a543867656b43172501488d34ec767df4ba2135f --- docs/changelog.md | 3 ++ stdlib/src/python/_cpython.mojo | 63 ++++++++++++++++++++++++++++++--- stdlib/src/python/python.mojo | 31 ++++++++++++++++ 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index fdeb2a09d3..e14c8bac35 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -83,6 +83,9 @@ what we publish. - Added `TypedPythonObject["Tuple].__getitem__` for accessing the elements of a Python tuple. +- Added `Python.add_object()`, to add a named `PythonObject` value to a Python + 'module' object instance. + - Added `Python.unsafe_get_python_exception()`, as an efficient low-level utility to get the Mojo `Error` equivalent of the current CPython error state. diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 29b73d33f5..00678d7e86 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -23,7 +23,7 @@ from os.path import dirname from pathlib import Path from sys import external_call from sys.arg import argv -from sys.ffi import DLHandle, c_char, c_int, OpaquePointer +from sys.ffi import DLHandle, c_char, c_int, c_uint, OpaquePointer from python.python import _get_global_python_itf @@ -135,6 +135,11 @@ alias Py_file_input = 257 alias Py_eval_input = 258 alias Py_func_type_input = 345 +alias Py_tp_dealloc = 52 +alias Py_tp_new = 65 + +alias Py_TPFLAGS_DEFAULT = 0 + # TODO(MSTDL-892): Change this to alias ffi.C_ssize_t alias Py_ssize_t = Int @@ -143,6 +148,8 @@ alias Py_ssize_t = Int # This should be a C ABI function pointer, not a Mojo ABI function. alias PyCFunction = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr +alias destructor = fn (PyObjectPtr) -> None + @value @register_passable("trivial") @@ -290,6 +297,27 @@ struct PyTypeObject: pass +@value +@register_passable("trivial") +struct PyType_Spec: + var name: UnsafePointer[c_char] + var basicsize: c_int + var itemsize: c_int + var flags: c_uint + var slots: UnsafePointer[PyType_Slot] + + +@value +@register_passable("trivial") +struct PyType_Slot: + var slot: c_int + var pfunc: OpaquePointer + + @staticmethod + fn null() -> Self: + return PyType_Slot {slot: 0, pfunc: OpaquePointer()} + + @value struct PyObject(Stringable, Representable, Formattable): var object_ref_count: Int @@ -906,11 +934,36 @@ struct CPython: return add_functions_fn(mod, functions) + fn PyModule_AddObjectRef( + inout self, + module: PyObjectPtr, + name: UnsafePointer[c_char], + value: PyObjectPtr, + ) -> c_int: + var func = self.lib.get_function[ + fn (PyObjectPtr, UnsafePointer[c_char], PyObjectPtr) -> c_int + ]("PyModule_AddObjectRef") + + return func(module, name, value) + fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: - var value = self.lib.get_function[ - fn (PyObjectPtr) -> UnsafePointer[Int8] - ]("PyModule_GetDict")(name.value) - return PyObjectPtr {value: value} + var value = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( + "PyModule_GetDict" + )(name.value) + return value + + # ===-------------------------------------------------------------------===# + # Python Type operations + # ===-------------------------------------------------------------------===# + + fn PyType_FromSpec( + inout self, spec: UnsafePointer[PyType_Spec] + ) -> PyObjectPtr: + var func = self.lib.get_function[ + fn (UnsafePointer[PyType_Spec]) -> PyObjectPtr + ]("PyType_FromSpec") + + return func(spec) # ===-------------------------------------------------------------------===# # Python Evaluation diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index d1a3d9a906..cf29753d42 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -298,6 +298,37 @@ struct Python: if result != 0: Python.throw_python_exception_if_error_state(cpython) + @staticmethod + fn add_object( + inout module: TypedPythonObject["Module"], + name: StringLiteral, + value: PythonObject, + ) raises: + """Add a new object to `module` with the given name and value. + + The provided object can be any type of Python object: an instance, + a type object, a function, etc. + + The added value will be inserted into the `__dict__` of the provided + module. + + Args: + module: The Python module to modify. + name: The name of the new object. + value: The python object value. + """ + + var cpython = _get_global_python_itf().cpython() + + var result = cpython.PyModule_AddObjectRef( + module.unsafe_as_py_object_ptr(), + name.unsafe_cstr_ptr(), + value.unsafe_as_py_object_ptr(), + ) + + if result != 0: + Python.throw_python_exception_if_error_state(cpython) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# From 892466a0cc417ebe24c9cf7d606d11b35238687e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 1 Oct 2024 12:03:45 -0700 Subject: [PATCH 1662/2019] [mojo-stdlib] Upgrade `Reference` to use `address_of` and modernize This removes the implicit constructor in favor of a `Reference.address_of` static method, eliminating bugs due to the implicit conversion. The use of `Reference` in general is dwindling now that we have ref arguments and results working. Even more will go away when we can upgrade `__next__`. This fixes MSTDL-936 MODULAR_ORIG_COMMIT_REV_ID: 5a62e4a8b29e17826c8a31c1ca7102a6c6f233e0 --- stdlib/src/builtin/builtin_list.mojo | 11 ++++++-- stdlib/src/builtin/file.mojo | 18 ++++++------ stdlib/src/builtin/io.mojo | 2 +- stdlib/src/collections/dict.mojo | 25 ++++++++++++----- stdlib/src/collections/inline_array.mojo | 1 - stdlib/src/collections/inline_list.mojo | 6 ++-- stdlib/src/collections/list.mojo | 8 +++--- stdlib/src/memory/reference.mojo | 35 ++++++++++++++++-------- stdlib/src/os/_linux_aarch64.mojo | 4 +-- stdlib/src/os/_linux_x86.mojo | 4 +-- stdlib/src/os/_macos.mojo | 8 ++++-- stdlib/src/sys/arg.mojo | 4 ++- stdlib/src/sys/info.mojo | 2 +- stdlib/src/time/time.mojo | 8 ++++-- stdlib/src/utils/span.mojo | 6 ++-- stdlib/test/collections/test_dict.mojo | 6 ++-- stdlib/test/memory/test_reference.mojo | 11 ++++---- stdlib/test/utils/test_span.mojo | 6 +--- 18 files changed, 100 insertions(+), 65 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 41235a3361..d46db01ba0 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -231,12 +231,19 @@ struct _VariadicListMemIter[ var index: Int var src: Reference[Self.variadic_list_type, list_lifetime] + fn __init__( + inout self, index: Int, ref [list_lifetime]list: Self.variadic_list_type + ): + self.index = index + self.src = Reference.address_of(list) + fn __next__(inout self) -> Self.variadic_list_type.reference_type: self.index += 1 # TODO: Need to make this return a dereferenced reference, not a # reference that must be deref'd by the user. - # NOTE: Using UnsafePointer here to get lifetimes to match. - return UnsafePointer.address_of(self.src[][self.index - 1])[] + return rebind[Self.variadic_list_type.reference_type]( + Reference.address_of(self.src[][self.index - 1]) + ) fn __len__(self) -> Int: return len(self.src[]) - self.index diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index a2cda5674c..9d02063647 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -95,7 +95,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() var handle = external_call[ "KGEN_CompilerRT_IO_FileOpen", UnsafePointer[NoneType] - ](path, mode, Reference(err_msg)) + ](path, mode, Reference.address_of(err_msg)) if err_msg: self.handle = UnsafePointer[NoneType]() @@ -117,7 +117,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() external_call["KGEN_CompilerRT_IO_FileClose", NoneType]( - self.handle, Reference(err_msg) + self.handle, Reference.address_of(err_msg) ) if err_msg: @@ -193,8 +193,8 @@ struct FileHandle: "KGEN_CompilerRT_IO_FileRead", UnsafePointer[UInt8] ]( self.handle, - Reference(size_copy), - Reference(err_msg), + Reference.address_of(size_copy), + Reference.address_of(err_msg), ) if err_msg: @@ -270,7 +270,7 @@ struct FileHandle: self.handle, ptr, size * sizeof[type](), - Reference(err_msg), + Reference.address_of(err_msg), ) if err_msg: @@ -336,8 +336,8 @@ struct FileHandle: "KGEN_CompilerRT_IO_FileReadBytes", UnsafePointer[UInt8] ]( self.handle, - Reference(size_copy), - Reference(err_msg), + Reference.address_of(size_copy), + Reference.address_of(err_msg), ) if err_msg: @@ -394,7 +394,7 @@ struct FileHandle: ) var err_msg = _OwnedStringRef() var pos = external_call["KGEN_CompilerRT_IO_FileSeek", UInt64]( - self.handle, offset, whence, Reference(err_msg) + self.handle, offset, whence, Reference.address_of(err_msg) ) if err_msg: @@ -446,7 +446,7 @@ struct FileHandle: self.handle, ptr.address, len, - Reference(err_msg), + Reference.address_of(err_msg), ) if err_msg: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 544e452b92..a0532243c0 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -166,7 +166,7 @@ fn _printf[ @parameter if triple_is_nvidia_cuda(): _ = external_call["vprintf", Int32]( - fmt.unsafe_cstr_ptr(), Reference(loaded_pack) + fmt.unsafe_cstr_ptr(), Reference.address_of(loaded_pack) ) else: with _fdopen(file) as fd: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 6d2407287f..245d33f0d9 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -76,6 +76,13 @@ struct _DictEntryIter[ var seen: Int var src: Reference[Dict[K, V], dict_lifetime] + fn __init__( + inout self, index: Int, seen: Int, ref [dict_lifetime]dict: Dict[K, V] + ): + self.index = index + self.seen = seen + self.src = Reference.address_of(dict) + fn __iter__(self) -> Self: return self @@ -86,7 +93,9 @@ struct _DictEntryIter[ DictEntry[K, V], __lifetime_of(self.src[]._entries[0].value()) ]: while True: - var opt_entry_ref = Reference(self.src[]._entries[self.index]) + var opt_entry_ref = Reference.address_of( + self.src[]._entries[self.index] + ) @parameter if forward: @@ -96,7 +105,7 @@ struct _DictEntryIter[ if opt_entry_ref[]: self.seen += 1 - return opt_entry_ref[].value() + return Reference.address_of(opt_entry_ref[].value()) fn __len__(self) -> Int: return len(self.src[]) - self.seen @@ -130,7 +139,7 @@ struct _DictKeyIter[ fn __next__( inout self, ) -> Reference[K, __lifetime_of(self.iter.__next__()[].key)]: - return self.iter.__next__()[].key + return Reference.address_of(self.iter.__next__()[].key) fn __len__(self) -> Int: return self.iter.__len__() @@ -174,7 +183,9 @@ struct _DictValueIter[ var entry_ref = self.iter.__next__() # Cast through a pointer to grant additional mutability because # _DictEntryIter.next erases it. - return UnsafePointer.address_of(entry_ref[].value)[] + return Self.ref_type.address_of( + UnsafePointer.address_of(entry_ref[].value)[] + ) fn __len__(self) -> Int: return self.iter.__len__() @@ -752,7 +763,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var index: Int found, slot, index = self._find_index(hash, key) if found: - var entry = Reference(self._entries[index]) + var entry = Reference.address_of(self._entries[index]) debug_assert(entry[].__bool__(), "entry in index must be full") return entry[].value().value raise "KeyError" @@ -818,7 +829,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( found, slot, index = self._find_index(hash, key) if found: self._set_index(slot, Self.REMOVED) - var entry = Reference(self._entries[index]) + var entry = Reference.address_of(self._entries[index]) debug_assert(entry[].__bool__(), "entry in index must be full") var entry_value = entry[].unsafe_take() entry[] = None @@ -905,7 +916,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn setdefault( inout self, key: K, owned default: V - ) raises -> Reference[V, __lifetime_of(self._find_ref(key))]: + ) raises -> ref [self._find_ref(key)] V: """Get a value from the dictionary by key, or set it to a default if it doesn't exist. Args: diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 48d1c79b4c..ccfae9bded 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -190,7 +190,6 @@ struct InlineArray[ for idx in range(size): var ptr = self.unsafe_ptr() + idx - ptr.init_pointee_copy(other[idx]) fn __copyinit__(inout self, other: Self): diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index a780e6b7df..90755b8122 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -58,10 +58,10 @@ struct _InlineListIter[ @parameter if forward: self.index += 1 - return self.src[][self.index - 1] + return Reference.address_of(self.src[][self.index - 1]) else: self.index -= 1 - return self.src[][self.index] + return Reference.address_of(self.src[][self.index]) fn __len__(self) -> Int: @parameter @@ -177,7 +177,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): Returns: An iterator of immutable references to the list elements. """ - return _InlineListIter(0, self) + return _InlineListIter(0, Reference.address_of(self)) fn __contains__[ C: EqualityComparableCollectionElement, // diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index fb913f742e..79e3f4f740 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -66,10 +66,10 @@ struct _ListIter[ @parameter if forward: self.index += 1 - return self.src[][self.index - 1] + return Reference.address_of(self.src[][self.index - 1]) else: self.index -= 1 - return self.src[][self.index] + return Reference.address_of(self.src[][self.index]) fn __len__(self) -> Int: @parameter @@ -353,7 +353,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Returns: An iterator of immutable references to the list elements. """ - return _ListIter(0, self) + return _ListIter(0, Reference.address_of(self)) fn __reversed__( ref [_]self: Self, @@ -363,7 +363,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Returns: A reversed iterator of immutable references to the list elements. """ - return _ListIter[forward=False](len(self), self) + return _ListIter[forward=False](len(self), Reference.address_of(self)) # ===-------------------------------------------------------------------===# # Trait implementations diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 1b3f1db752..11295205c7 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -292,7 +292,7 @@ struct Reference[ `>`, ] - var value: Self._mlir_type + var _value: Self._mlir_type """The underlying MLIR reference.""" # ===------------------------------------------------------------------===# @@ -300,15 +300,28 @@ struct Reference[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__( - inout self, ref [lifetime, address_space._value.value]value: type - ): + fn __init__(inout self, *, _mlir_value: Self._mlir_type): + """Constructs a Reference from its MLIR prepresentation. + + Args: + _mlir_value: The MLIR representation reference. + """ + self._value = _mlir_value + + @staticmethod + @always_inline("nodebug") + fn address_of( + ref [lifetime, address_space._value.value]value: type + ) -> Self: """Constructs a Reference from a value reference. Args: value: The value reference. + + Returns: + The result Reference. """ - self.value = __get_mvalue_as_litref(value) + return Reference(_mlir_value=__get_mvalue_as_litref(value)) fn __init__(inout self, *, other: Self): """Constructs a copy from another Reference. @@ -318,7 +331,7 @@ struct Reference[ Args: other: The `Reference` to copy. """ - self.value = other.value + self._value = other._value # ===------------------------------------------------------------------===# # Operator dunders @@ -331,7 +344,7 @@ struct Reference[ Returns: The MLIR reference for the Mojo compiler to use. """ - return __get_litref_as_mvalue(self.value) + return __get_litref_as_mvalue(self._value) # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. @@ -348,9 +361,9 @@ struct Reference[ Returns: True if the two pointers are equal and False otherwise. """ - return UnsafePointer( - __mlir_op.`lit.ref.to_pointer`(self.value) - ) == UnsafePointer(__mlir_op.`lit.ref.to_pointer`(rhs.value)) + return UnsafePointer.address_of(self[]) == UnsafePointer.address_of( + rhs[] + ) @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") @@ -372,4 +385,4 @@ struct Reference[ Returns: The string representation of the Reference. """ - return str(UnsafePointer(__mlir_op.`lit.ref.to_pointer`(self.value))) + return str(UnsafePointer.address_of(self[])) diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index bebc4bffc9..e9048cdd2b 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -110,7 +110,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference(stat) + Int32(0), path.unsafe_ptr(), Reference.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -121,7 +121,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference(stat) + Int32(0), path.unsafe_ptr(), Reference.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index e11fb84423..ad6693d765 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -108,7 +108,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference(stat) + Int32(0), path.unsafe_ptr(), Reference.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -119,7 +119,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference(stat) + Int32(0), path.unsafe_ptr(), Reference.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 78f3f3abcd..f3f18d4cdc 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -114,7 +114,9 @@ struct _c_stat(Stringable): @always_inline fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() - var err = external_call["stat", Int32](path.unsafe_ptr(), Reference(stat)) + var err = external_call["stat", Int32]( + path.unsafe_ptr(), Reference.address_of(stat) + ) if err == -1: raise "unable to stat '" + path + "'" return stat @@ -123,7 +125,9 @@ fn _stat(path: String) raises -> _c_stat: @always_inline fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() - var err = external_call["lstat", Int32](path.unsafe_ptr(), Reference(stat)) + var err = external_call["lstat", Int32]( + path.unsafe_ptr(), Reference.address_of(stat) + ) if err == -1: raise "unable to lstat '" + path + "'" return stat diff --git a/stdlib/src/sys/arg.mojo b/stdlib/src/sys/arg.mojo index 59b17b2046..7adf8a4865 100644 --- a/stdlib/src/sys/arg.mojo +++ b/stdlib/src/sys/arg.mojo @@ -45,5 +45,7 @@ fn argv() -> VariadicList[StringRef]: The list of command line arguments provided when mojo was invoked. """ var result = VariadicList[StringRef]("") - external_call["KGEN_CompilerRT_GetArgV", NoneType](Reference(result)) + external_call["KGEN_CompilerRT_GetArgV", NoneType]( + Reference.address_of(result) + ) return result diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 479778a57e..f2e77ec0e1 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -779,7 +779,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: var err = external_call["sysctlbyname", Int32]( "kern.osproductversion".unsafe_cstr_ptr(), buf.data, - Reference(buf_len), + Reference.address_of(buf_len), UnsafePointer[NoneType](), Int(0), ) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 4465be346c..fd6f143cbf 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -105,7 +105,9 @@ fn _clock_gettime(clockid: Int) -> _CTimeSpec: var ts = _CTimeSpec() # Call libc's clock_gettime. - _ = external_call["clock_gettime", Int32](Int32(clockid), Reference(ts)) + _ = external_call["clock_gettime", Int32]( + Int32(clockid), Reference.address_of(ts) + ) return ts @@ -134,7 +136,9 @@ fn _monotonic_nanoseconds() -> Int: @parameter if os_is_windows(): var ft = _FILETIME() - external_call["GetSystemTimePreciseAsFileTime", NoneType](Reference(ft)) + external_call["GetSystemTimePreciseAsFileTime", NoneType]( + Reference.address_of(ft) + ) return ft.as_nanoseconds() else: diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 46ed1a4e82..1f504c1714 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -55,10 +55,10 @@ struct _SpanIter[ @parameter if forward: self.index += 1 - return self.src[self.index - 1] + return Reference.address_of(self.src[self.index - 1]) else: self.index -= 1 - return self.src[self.index] + return Reference.address_of(self.src[self.index]) @always_inline fn __len__(self) -> Int: @@ -230,7 +230,7 @@ struct Span[ A Reference pointing at the first element of this slice. """ - return self._data[0] + return Reference[T, lifetime].address_of(self._data[0]) @always_inline fn copy_from[ diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 438ca36ebd..44a7a04d5b 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -580,9 +580,9 @@ fn test_dict_setdefault() raises: var some_dict = Dict[String, Int]() some_dict["key1"] = 1 some_dict["key2"] = 2 - assert_equal(some_dict.setdefault("key1", 0)[], 1) - assert_equal(some_dict.setdefault("key2", 0)[], 2) - assert_equal(some_dict.setdefault("not_key", 0)[], 0) + assert_equal(some_dict.setdefault("key1", 0), 1) + assert_equal(some_dict.setdefault("key2", 0), 2) + assert_equal(some_dict.setdefault("not_key", 0), 0) assert_equal(some_dict["not_key"], 0) # Check that there is no copy of the default value, so it's performant diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo index 8d71065bc6..e91f09aa5a 100644 --- a/stdlib/test/memory/test_reference.mojo +++ b/stdlib/test/memory/test_reference.mojo @@ -17,8 +17,7 @@ from testing import assert_equal, assert_true def test_copy_reference_explicitly(): var a = List[Int](1, 2, 3) - var b = Reference(a) - + var b = Reference.address_of(a) var c = Reference(other=b) c[][0] = 4 @@ -31,14 +30,14 @@ def test_equality(): var a = List[Int](1, 2, 3) var b = List[Int](4, 5, 6) - assert_true(Reference(a) == Reference(a)) - assert_true(Reference(b) == Reference(b)) - assert_true(Reference(a) != Reference(b)) + assert_true(Reference.address_of(a) == Reference.address_of(a)) + assert_true(Reference.address_of(b) == Reference.address_of(b)) + assert_true(Reference.address_of(a) != Reference.address_of(b)) def test_str(): var a = Int(42) - var a_ref = Reference(a) + var a_ref = Reference.address_of(a) assert_true(str(a_ref).startswith("0x")) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index dd1c789671..f1ddbdc150 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -191,11 +191,7 @@ def test_fill(): def test_ref(): var l = InlineArray[Int, 3](1, 2, 3) var s = Span[Int](array=l) - # Would be nice to just use `assert_equal` with `Reference`, but doesn't quite work yet - # even after `Reference` is `Stringable`. So, just compare for pointer equality right now. - var p1 = UnsafePointer(__mlir_op.`lit.ref.to_pointer`(s.as_ref().value)) - var p2 = l.unsafe_ptr() - assert_equal(p1, p2) + assert_true(s.as_ref() == Reference.address_of(l.unsafe_ptr()[])) def main(): From b1cf47ee621363ada88934f744287fb501a4822e Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 1 Oct 2024 14:42:01 -0500 Subject: [PATCH 1663/2019] [stdlib] feat: Add handful of constants for Python type spec fields MODULAR_ORIG_COMMIT_REV_ID: 0f0a331c023f9e705655501666dbd75309ef9ca4 --- stdlib/src/python/_cpython.mojo | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 00678d7e86..58414c1049 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -136,6 +136,8 @@ alias Py_eval_input = 258 alias Py_func_type_input = 345 alias Py_tp_dealloc = 52 +alias Py_tp_init = 60 +alias Py_tp_methods = 64 alias Py_tp_new = 65 alias Py_TPFLAGS_DEFAULT = 0 @@ -148,6 +150,8 @@ alias Py_ssize_t = Int # This should be a C ABI function pointer, not a Mojo ABI function. alias PyCFunction = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr +alias METH_VARARGS = 0x1 + alias destructor = fn (PyObjectPtr) -> None @@ -274,8 +278,6 @@ struct PyMethodDef: # Support a way to get the name of the function from its parameter # type, similar to `get_linkage_name()`? - alias METH_VARARGS = 0x1 - return PyMethodDef( func_name.unsafe_cstr_ptr(), func, From 30bfaf9be02efe42a7936adb33b78ed4b908037e Mon Sep 17 00:00:00 2001 From: Hengjie Wang <86926839+hengjiew@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:56:07 -0400 Subject: [PATCH 1664/2019] [Integrate] Bump LLVM to 32719c4 (#47985) Requires small changes to invocation of the CallIntrinsicOp which now needs an operand bundle --------- Co-authored-by: Abdul Dakkak MODULAR_ORIG_COMMIT_REV_ID: a0f69c78fe59b6768cc98e4b48a0454254c185b0 --- examples/notebooks/Matmul.ipynb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index cc2be3a71b..a552187f06 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -219,7 +219,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.0022564560694965834 GFLOP/s\n" + "0.004532987790119301 GFLOP/s\n" ] } ], @@ -361,7 +361,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.019322323885887414 GFLOP/s, a 8.563128769530282 x speedup over Python\n" + "0.014276454358821751 GFLOP/s, a 3.1494579336702819 x speedup over Python\n" ] } ], @@ -409,6 +409,7 @@ }, "outputs": [], "source": [ + "from memory import memset_zero\n", "alias type = DType.float32\n", "\n", "struct Matrix[rows: Int, cols: Int]:\n", @@ -417,7 +418,7 @@ " # Initialize zeroeing all values\n", " fn __init__(inout self):\n", " self.data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", - " memset_zero(self.data.address, rows * cols)\n", + " memset_zero(self.data, rows * cols)\n", "\n", " # Initialize taking a pointer, don't set any elements\n", " fn __init__(inout self, data: UnsafePointer[Scalar[type]]):\n", @@ -542,7 +543,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.3335685138294373 GFLOP/s, a 2363.6926000599515 x speedup over Python\n" + "5.5584227194331879 GFLOP/s, a 1226.2161242854129 x speedup over Python\n" ] } ], @@ -617,7 +618,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "27.943656514151261 GFLOP/s, a 12383.869064371152 x speedup over Python\n" + "24.522805389188736 GFLOP/s, a 5409.8547193623335 x speedup over Python\n" ] } ], @@ -672,7 +673,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "29.94507372793699 GFLOP/s, a 13270.842775422503 x speedup over Python\n" + "17.823704154967395 GFLOP/s, a 3931.9991538071854 x speedup over Python\n" ] } ], @@ -741,7 +742,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "860.6608444221323 GFLOP/s, a 381421.49366734456 x speedup over Python\n" + "66.225456232955807 GFLOP/s, a 14609.670111468104 x speedup over Python\n" ] } ], @@ -837,7 +838,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "898.79112716147336 GFLOP/s, a 398319.79860436375 x speedup over Python\n" + "66.530139059653536 GFLOP/s, a 14676.884681814368 x speedup over Python\n" ] } ], @@ -900,7 +901,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1026.9730406876936 GFLOP/s, a 455126.53872176283 x speedup over Python\n" + "72.59541968462139 GFLOP/s, a 16014.916219906869 x speedup over Python\n" ] } ], From 73041aeccfb6787a1249f5548f0a3633fefc9edc Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 1 Oct 2024 17:00:00 -0500 Subject: [PATCH 1665/2019] [stdlib] cleanup: Add initial _bindings.mojo MODULAR_ORIG_COMMIT_REV_ID: 1ae40f93212be082d6fec1f0f8061b9909a4d865 --- stdlib/src/python/_bindings.mojo | 109 +++++++++++++++++++++++++++++++ stdlib/src/python/_cpython.mojo | 93 -------------------------- 2 files changed, 109 insertions(+), 93 deletions(-) create mode 100644 stdlib/src/python/_bindings.mojo diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo new file mode 100644 index 0000000000..599b3681ea --- /dev/null +++ b/stdlib/src/python/_bindings.mojo @@ -0,0 +1,109 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from python import PythonObject, TypedPythonObject +from python.python import _get_global_python_itf +from python._cpython import PyObject, PyObjectPtr, PyCFunction + +# ===-----------------------------------------------------------------------===# +# PyCFunction Wrappers +# ===-----------------------------------------------------------------------===# + + +fn create_wrapper_function[ + user_func: fn (PythonObject, TypedPythonObject["Tuple"]) -> PythonObject +]() -> PyCFunction: + # > When a C function is called from Python, it borrows references to its + # > arguments from the caller. The caller owns a reference to the object, + # > so the borrowed reference’s lifetime is guaranteed until the function + # > returns. Only when such a borrowed reference must be stored or passed + # > on, it must be turned into an owned reference by calling Py_INCREF(). + # > + # > -- https://docs.python.org/3/extending/extending.html#ownership-rules + + fn wrapper(py_self_ptr: PyObjectPtr, args_ptr: PyObjectPtr) -> PyObjectPtr: + # SAFETY: + # Here we illegally (but carefully) construct _owned_ `PythonObject` + # values from the borrowed object reference arguments. We are careful + # down below to prevent the destructor for these objects from running + # so that we do not illegally decrement the reference count of these + # objects we do not own. + # + # This is valid to do, because these are passed using the `borrowed` + # argument convention to `user_func`, so logically they are treated + # as Python borrowed references. + var py_self = PythonObject(py_self_ptr) + var args = TypedPythonObject["Tuple"]( + unsafe_unchecked_from=PythonObject(args_ptr) + ) + + # SAFETY: + # Call the user provided function, and take ownership of the + # PyObjectPtr of the returned PythonObject. + var result = user_func(py_self, args).steal_data() + + # Do not destroy the provided PyObjectPtr arguments, since they + # actually have ownership of the underlying object. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(py_self) + ) + + # SAFETY: + # Prevent `args` AND `args._obj` from being destroyed, since we don't + # own them. + # TODO: Use a `mem.forget(args^)` function here in the future. + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(args)) + var _obj = args._obj^ + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(_obj)) + + return result + + return wrapper + + +# Wrap a `raises` function +fn create_wrapper_function[ + user_func: fn ( + PythonObject, TypedPythonObject["Tuple"] + ) raises -> PythonObject +]() -> PyCFunction: + fn wrapper( + py_self: PythonObject, args: TypedPythonObject["Tuple"] + ) -> PythonObject: + var cpython = _get_global_python_itf().cpython() + + var state = cpython.PyGILState_Ensure() + + try: + var result = user_func(py_self, args) + return result + except e: + # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. + var error_type = cpython.get_error_global("PyExc_Exception") + + cpython.PyErr_SetString( + error_type, + e.unsafe_cstr_ptr(), + ) + + # Return a NULL `PyObject*`. + return PythonObject(PyObjectPtr()) + finally: + cpython.PyGILState_Release(state) + + # TODO: + # Does this lead to multiple levels of indirect function calls for + # `raises` functions? Could we fix that by marking `wrapper` here as + # `@always_inline`? + # Call the non-`raises` overload of `create_wrapper_function`. + return create_wrapper_function[wrapper]() diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 58414c1049..198e950db7 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -31,99 +31,6 @@ from memory import UnsafePointer from utils import StringRef -# ===-----------------------------------------------------------------------===# -# Bindings Utilities -# ===-----------------------------------------------------------------------===# - - -fn create_wrapper_function[ - user_func: fn (PythonObject, TypedPythonObject["Tuple"]) -> PythonObject -]() -> PyCFunction: - # > When a C function is called from Python, it borrows references to its - # > arguments from the caller. The caller owns a reference to the object, - # > so the borrowed reference’s lifetime is guaranteed until the function - # > returns. Only when such a borrowed reference must be stored or passed - # > on, it must be turned into an owned reference by calling Py_INCREF(). - # > - # > -- https://docs.python.org/3/extending/extending.html#ownership-rules - - fn wrapper(py_self_ptr: PyObjectPtr, args_ptr: PyObjectPtr) -> PyObjectPtr: - # SAFETY: - # Here we illegally (but carefully) construct _owned_ `PythonObject` - # values from the borrowed object reference arguments. We are careful - # down below to prevent the destructor for these objects from running - # so that we do not illegally decrement the reference count of these - # objects we do not own. - # - # This is valid to do, because these are passed using the `borrowed` - # argument convention to `user_func`, so logically they are treated - # as Python borrowed references. - var py_self = PythonObject(py_self_ptr) - var args = TypedPythonObject["Tuple"]( - unsafe_unchecked_from=PythonObject(args_ptr) - ) - - # SAFETY: - # Call the user provided function, and take ownership of the - # PyObjectPtr of the returned PythonObject. - var result = user_func(py_self, args).steal_data() - - # Do not destroy the provided PyObjectPtr arguments, since they - # actually have ownership of the underlying object. - __mlir_op.`lit.ownership.mark_destroyed`( - __get_mvalue_as_litref(py_self) - ) - - # SAFETY: - # Prevent `args` AND `args._obj` from being destroyed, since we don't - # own them. - # TODO: Use a `mem.forget(args^)` function here in the future. - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(args)) - var _obj = args._obj^ - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(_obj)) - - return result - - return wrapper - - -# Wrap a `raises` function -fn create_wrapper_function[ - user_func: fn ( - PythonObject, TypedPythonObject["Tuple"] - ) raises -> PythonObject -]() -> PyCFunction: - fn wrapper( - py_self: PythonObject, args: TypedPythonObject["Tuple"] - ) -> PythonObject: - var cpython = _get_global_python_itf().cpython() - - var state = cpython.PyGILState_Ensure() - - try: - var result = user_func(py_self, args) - return result - except e: - # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. - var error_type = cpython.get_error_global("PyExc_Exception") - - cpython.PyErr_SetString( - error_type, - e.unsafe_cstr_ptr(), - ) - - # Return a NULL `PyObject*`. - return PythonObject(PyObjectPtr()) - finally: - cpython.PyGILState_Release(state) - - # TODO: - # Does this lead to multiple levels of indirect function calls for - # `raises` functions? Could we fix that by marking `wrapper` here as - # `@always_inline`? - # Call the non-`raises` overload of `create_wrapper_function`. - return create_wrapper_function[wrapper]() - # ===-----------------------------------------------------------------------===# # Raw Bindings From a97f13d38ce9125902bcf8fb36ca9872af76e4c5 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Tue, 1 Oct 2024 19:20:51 -0400 Subject: [PATCH 1666/2019] [External] [stdlib] Speedup (~1.4x) on `PythonObject.__getitem__` (#48239) [External] [stdlib] Speedup (~1.4x) on `PythonObject.__getitem__` Noticing consistent ~1.4x speedup for basic benchmarks on my machine (M1), example: ```mojo for i in range(iterations): var idx_a = random_si64(0, 999) var idx_b = random_si64(0, 9999) var idx_c = random_si64(0, 99999) _ = a[idx_a] _ = b[idx_b] _ = c[idx_c] ``` This essentially free speedup comes from removing any overhead associated with `PyObject_GetAttr`, by using [`PyObject_GetItem`](https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem) directly. Also noticed minimising the use of `PythonObject` wrappers, and working directly with `PyObjectPtr` attributes contributed to the speedup as well. In addition, multi-dimensional indexing into Python objects are now possible, such as below ```mojo var np = Python.import_module("numpy") var a = np.array(PythonObject([1,2,3,1,2,3])).reshape(2,3) print((a[0, 1])) ``` Previously, this would raise an `expected 1 argument, got 2` exception. Lastly, error messages are also now consistent to Python, for non-subscriptable objects (mentioned in https://github.com/modularml/mojo/issues/3557). Co-authored-by: Joshua James Venter Closes modularml/mojo#3583 MODULAR_ORIG_COMMIT_REV_ID: 2886a29cc03cb89efcf6b5919ecfbfc9c3f845df --- docs/changelog.md | 9 +++ stdlib/src/python/_cpython.mojo | 20 +++++++ stdlib/src/python/python_object.mojo | 32 +++++------ stdlib/test/python/test_python_object.mojo | 64 ++++++++++++++++------ 4 files changed, 92 insertions(+), 33 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e14c8bac35..975db26005 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -175,6 +175,15 @@ what we publish. return a ``` +- Support for multi-dimensional indexing for `PythonObject` + ([PR #3583](https://github.com/modularml/mojo/pull/3583) by [@jjvraw](https://github.com/jjvraw)). + + ```mojo + var np = Python.import_module("numpy") + var a = np.array(PythonObject([1,2,3,1,2,3])).reshape(2,3) + print((a[0, 1])) # 2 + ``` + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 198e950db7..48aaaf181f 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -989,6 +989,26 @@ struct CPython: self._inc_total_rc() return f(obj) + fn PyObject_GetItem( + inout self, obj: PyObjectPtr, key: PyObjectPtr + ) -> PyObjectPtr: + var r = self.lib.get_function[ + fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr + ]("PyObject_GetItem")(obj, key) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyObject_GetItem, key:", + key._get_ptr_as_int(), + ", refcnt:", + self._Py_REFCNT(r), + ", parent obj:", + obj._get_ptr_as_int(), + ) + + self._inc_total_rc() + return r + fn PyObject_GetAttrString( inout self, obj: PyObjectPtr, diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 7c6bb35c4d..709c773477 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -699,23 +699,21 @@ struct PythonObject( """ var cpython = _get_global_python_itf().cpython() var size = len(args) - var tuple_obj = cpython.PyTuple_New(size) - for i in range(size): - var arg_value = args[i].py_object - cpython.Py_IncRef(arg_value) - var result = cpython.PyTuple_SetItem(tuple_obj, i, arg_value) - if result != 0: - raise Error("internal error: PyTuple_SetItem failed") - - var callable_obj = cpython.PyObject_GetAttrString( - self.py_object, "__getitem__" - ) - if callable_obj.is_null(): - cpython.Py_DecRef(tuple_obj) - Python.throw_python_exception_if_error_state(cpython) - var result = cpython.PyObject_CallObject(callable_obj, tuple_obj) - cpython.Py_DecRef(callable_obj) - cpython.Py_DecRef(tuple_obj) + var key_obj: PyObjectPtr + if size == 1: + key_obj = args[0].py_object + else: + key_obj = cpython.PyTuple_New(size) + for i in range(size): + var arg_value = args[i].py_object + cpython.Py_IncRef(arg_value) + var result = cpython.PyTuple_SetItem(key_obj, i, arg_value) + if result != 0: + raise Error("internal error: PyTuple_SetItem failed") + + cpython.Py_IncRef(key_obj) + var result = cpython.PyObject_GetItem(self.py_object, key_obj) + cpython.Py_DecRef(key_obj) Python.throw_python_exception_if_error_state(cpython) return PythonObject(result) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index ad5397be65..618da8a703 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -411,39 +411,49 @@ fn test_none() raises: fn test_getitem_raises() raises: var a = PythonObject(2) - with assert_raises(contains="'int' object has no attribute '__getitem__'"): + with assert_raises(contains="'int' object is not subscriptable"): _ = a[0] + with assert_raises(contains="'int' object is not subscriptable"): + _ = a[0, 0] var b = PythonObject(2.2) - with assert_raises( - contains="'float' object has no attribute '__getitem__'" - ): + with assert_raises(contains="'float' object is not subscriptable"): _ = b[0] + with assert_raises(contains="'float' object is not subscriptable"): + _ = b[0, 0] var c = PythonObject(True) - with assert_raises(contains="'bool' object has no attribute '__getitem__'"): + with assert_raises(contains="'bool' object is not subscriptable"): _ = c[0] + with assert_raises(contains="'bool' object is not subscriptable"): + _ = c[0, 0] var d = PythonObject(None) - with assert_raises( - contains="'NoneType' object has no attribute '__getitem__'" - ): + with assert_raises(contains="'NoneType' object is not subscriptable"): _ = d[0] - - var with_get = Python.evaluate( - "type('WithGetItem', (), {'__getitem__': lambda self, key: f\"Key:" - ' {key}"})()' + with assert_raises(contains="'NoneType' object is not subscriptable"): + _ = d[0, 0] + + with_get = Python.evaluate( + """type('WithGetItem', (), { + '__getitem__': lambda self, key: + 'Keys: {0}'.format(", ".join(map(str, key))) if isinstance(key, tuple) + else 'Key: {0}'.format(key) + })()""" ) assert_equal("Key: 0", str(with_get[0])) + assert_equal("Keys: 0, 0", str(with_get[0, 0])) + assert_equal("Keys: 0, 0, 0", str(with_get[0, 0, 0])) var without_get = Python.evaluate( - "type('WithOutGetItem', (), {'__str__': \"SomeString\"})()" + "type('WithOutGetItem', (), {'__str__': lambda self: \"SomeString\"})()" ) - with assert_raises( - contains="'WithOutGetItem' object has no attribute '__getitem__'" - ): + with assert_raises(contains="'WithOutGetItem' object is not subscriptable"): _ = without_get[0] + with assert_raises(contains="'WithOutGetItem' object is not subscriptable"): + _ = without_get[0, 0] + var with_get_exception = Python.evaluate( "type('WithGetItemException', (), {'__getitem__': lambda self, key: (_" ' for _ in ()).throw(ValueError("Custom error")),})()' @@ -452,6 +462,28 @@ fn test_getitem_raises() raises: with assert_raises(contains="Custom error"): _ = with_get_exception[1] + with_2d = Python.evaluate( + """type('With2D', (), { + '__init__': lambda self: setattr(self, 'data', [[1, 2, 3], [4, 5, 6]]), + '__getitem__': lambda self, key: ( + self.data[key[0]][key[1]] if isinstance(key, tuple) + else self.data[key] + ) + })()""" + ) + assert_equal("[1, 2, 3]", str(with_2d[0])) + assert_equal(2, with_2d[0, 1]) + assert_equal(6, with_2d[1, 2]) + + with assert_raises(contains="list index out of range"): + _ = with_2d[0, 4] + + with assert_raises(contains="list index out of range"): + _ = with_2d[2, 0] + + with assert_raises(contains="list index out of range"): + _ = with_2d[2] + def main(): # initializing Python instance calls init_python From 9a6213b65e96717a6ead358b89de94f50f4bfa2a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 1 Oct 2024 17:52:33 -0700 Subject: [PATCH 1667/2019] [Stdlib] Relax the UnsafePointer input types MODULAR_ORIG_COMMIT_REV_ID: c2667ea3007fb8892b64500372c4c732c5c5c024 --- examples/mandelbrot.mojo | 2 +- examples/matmul.mojo | 2 +- examples/notebooks/Mandelbrot.ipynb | 4 +- examples/notebooks/Matmul.ipynb | 8 +- stdlib/src/memory/unsafe_pointer.mojo | 185 ++++++++++++++++----- stdlib/test/builtin/test_simd.mojo | 4 +- stdlib/test/memory/test_unsafepointer.mojo | 2 +- stdlib/test/sys/test_intrinsics.mojo | 2 +- 8 files changed, 152 insertions(+), 57 deletions(-) diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index c5d8bd18b2..a2a7d12a43 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -43,7 +43,7 @@ struct Matrix[type: DType, rows: Int, cols: Int]: self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) fn store[nelts: Int](self, row: Int, col: Int, val: SIMD[type, nelts]): - self.data.store[width=nelts](row * cols + col, val) + self.data.store(row * cols + col, val) fn mandelbrot_kernel_SIMD[ diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 36eb2a655c..4b1a08e7cd 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -77,7 +77,7 @@ struct Matrix[rows: Int, cols: Int]: return self.data.load[width=nelts](y * self.cols + x) fn store[nelts: Int = 1](self, y: Int, x: Int, val: SIMD[type, nelts]): - self.data.store[width=nelts](y * self.cols + x, val) + self.data.store(y * self.cols + x, val) def run_matmul_python() -> Float64: diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 834198f7a9..ad269046ce 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -1,5 +1,5 @@ { - "cells": [ + "cells": [ { "cell_type": "raw", "metadata": {}, @@ -107,7 +107,7 @@ " return self.data.load(row * cols + col)\n", "\n", " fn store[width: Int = 1](self, row: Int, col: Int, val: SIMD[type, width]):\n", - " self.data.store[width=width](row * cols + col, val)" + " self.data.store(row * cols + col, val)" ] }, { diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index a552187f06..71a63127af 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -435,13 +435,13 @@ " return self.load[1](y, x)\n", "\n", " fn __setitem__(self, y: Int, x: Int, val: Scalar[type]):\n", - " self.store[1](y, x, val)\n", + " self.store(y, x, val)\n", "\n", " fn load[nelts: Int](self, y: Int, x: Int) -> SIMD[type, nelts]:\n", " return self.data.load[width=nelts](y * self.cols + x)\n", "\n", - " fn store[nelts: Int](self, y: Int, x: Int, val: SIMD[type, nelts]):\n", - " return self.data.store[width=nelts](y * self.cols + x, val)" + " fn store[nelts: Int, //](self, y: Int, x: Int, val: SIMD[type, nelts]):\n", + " return self.data.store(y * self.cols + x, val)" ] }, { @@ -718,7 +718,7 @@ " for k in range(A.cols):\n", " @parameter\n", " fn dot[nelts : Int](n : Int):\n", - " C.store[nelts](m,n, C.load[nelts](m,n) + A[m,k] * B.load[nelts](k,n))\n", + " C.store(m,n, C.load[nelts](m,n) + A[m,k] * B.load[nelts](k,n))\n", " vectorize[dot, nelts, size = C.cols]()\n", " parallelize[calc_row](C.rows, C.rows)" ] diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 980f42aeca..26735fe7cd 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -38,12 +38,22 @@ from memory.memory import _free, _malloc # ===----------------------------------------------------------------------=== # +@always_inline +fn _default_alignment[type: AnyType]() -> Int: + return alignof[type]() if triple_is_nvidia_cuda() else 1 + + +@always_inline +fn _default_alignment[type: DType, width: Int = 1]() -> Int: + return _default_alignment[Scalar[type]]() + + @register_passable("trivial") struct UnsafePointer[ type: AnyType, address_space: AddressSpace = AddressSpace.GENERIC, exclusive: Bool = False, - alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, + alignment: Int = _default_alignment[type](), lifetime: Lifetime[True].type = MutableAnyLifetime, ]( ImplicitlyBoolable, @@ -104,7 +114,7 @@ struct UnsafePointer[ self.address = value @always_inline - fn __init__(inout self, other: UnsafePointer[type, address_space, *_]): + fn __init__(inout self, other: UnsafePointer[type, address_space, *_, **_]): """Exclusivity parameter cast a pointer. Args: @@ -438,10 +448,8 @@ struct UnsafePointer[ type: DType, //, width: Int = 1, *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_]) -> SIMD[type, width]: + alignment: Int = _default_alignment[type, width](), + ](self: UnsafePointer[Scalar[type], *_, **_]) -> SIMD[type, width]: """Loads the value the pointer points to. Constraints: @@ -485,10 +493,8 @@ struct UnsafePointer[ type: DType, //, width: Int = 1, *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_], offset: Scalar) -> SIMD[ + alignment: Int = _default_alignment[type, width](), + ](self: UnsafePointer[Scalar[type], *_, **_], offset: Scalar) -> SIMD[ type, width ]: """Loads the value the pointer points to with the given offset. @@ -517,10 +523,10 @@ struct UnsafePointer[ type: DType, //, width: Int = 1, *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_], offset: T) -> SIMD[type, width]: + alignment: Int = _default_alignment[type, width](), + ](self: UnsafePointer[Scalar[type], *_, **_], offset: T) -> SIMD[ + type, width + ]: """Loads the value the pointer points to with the given offset. Constraints: @@ -544,13 +550,39 @@ struct UnsafePointer[ fn store[ T: IntLike, type: DType, //, - width: Int = 1, *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, + alignment: Int = _default_alignment[type](), ]( - self: UnsafePointer[Scalar[type], *_], + self: UnsafePointer[Scalar[type], *_, **_], + offset: T, + val: Scalar[type], + ): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + The offset must be integer. + + Parameters: + T: The type of offset, either `Int` or `UInt`. + type: The data type of SIMD vector elements. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to store to. + val: The value to store. + """ + self.offset(offset)._store[alignment=alignment](val) + + @always_inline + fn store[ + T: IntLike, + type: DType, + width: Int, //, + *, + alignment: Int = _default_alignment[type, width](), + ]( + self: UnsafePointer[Scalar[type], *_, **_], offset: T, val: SIMD[type, width], ): @@ -574,15 +606,42 @@ struct UnsafePointer[ @always_inline fn store[ - type: DType, //, - width: Int = 1, + type: DType, + offset_type: DType, //, *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, + alignment: Int = _default_alignment[type](), ]( - self: UnsafePointer[Scalar[type], *_], - offset: Scalar, + self: UnsafePointer[Scalar[type], *_, **_], + offset: Scalar[offset_type], + val: Scalar[type], + ): + """Stores a single element value at the given offset. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + offset_type: The data type of the offset value. + alignment: The minimal alignment of the address. + + Args: + offset: The offset to store to. + val: The value to store. + """ + constrained[offset_type.is_integral(), "offset must be integer"]() + self.offset(int(offset))._store[alignment=alignment](val) + + @always_inline + fn store[ + type: DType, + width: Int, + offset_type: DType, //, + *, + alignment: Int = _default_alignment[type, width](), + ]( + self: UnsafePointer[Scalar[type], *_, **_], + offset: Scalar[offset_type], val: SIMD[type, width], ): """Stores a single element value at the given offset. @@ -593,24 +652,41 @@ struct UnsafePointer[ Parameters: type: The data type of SIMD vector elements. width: The size of the SIMD vector. + offset_type: The data type of the offset value. alignment: The minimal alignment of the address. Args: offset: The offset to store to. val: The value to store. """ - constrained[offset.type.is_integral(), "offset must be integer"]() - self.offset(int(offset)).store[alignment=alignment](val) + constrained[offset_type.is_integral(), "offset must be integer"]() + self.offset(int(offset))._store[alignment=alignment](val) @always_inline("nodebug") fn store[ - type: DType, //, - width: Int = 1, + type: DType, //, *, alignment: Int = _default_alignment[type]() + ](self: UnsafePointer[Scalar[type], *_, **_], val: Scalar[type]): + """Stores a single element value. + + Constraints: + The width and alignment must be positive integer values. + + Parameters: + type: The data type of SIMD vector elements. + alignment: The minimal alignment of the address. + + Args: + val: The value to store. + """ + self._store[alignment=alignment](val) + + @always_inline("nodebug") + fn store[ + type: DType, + width: Int, //, *, - alignment: Int = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1, - ](self: UnsafePointer[Scalar[type], *_], val: SIMD[type, width]): + alignment: Int = _default_alignment[type, width](), + ](self: UnsafePointer[Scalar[type], *_, **_], val: SIMD[type, width]): """Stores a single element value. Constraints: @@ -624,6 +700,15 @@ struct UnsafePointer[ Args: val: The value to store. """ + self._store[alignment=alignment](val) + + @always_inline("nodebug") + fn _store[ + type: DType, + width: Int, + *, + alignment: Int = _default_alignment[type, width](), + ](self: UnsafePointer[Scalar[type], *_, **_], val: SIMD[type, width]): constrained[width > 0, "width must be a positive integer value"]() constrained[ alignment > 0, "alignment must be a positive integer value" @@ -635,7 +720,9 @@ struct UnsafePointer[ @always_inline("nodebug") fn strided_load[ type: DType, T: Intable, //, width: Int - ](self: UnsafePointer[Scalar[type], *_], stride: T) -> SIMD[type, width]: + ](self: UnsafePointer[Scalar[type], *_, **_], stride: T) -> SIMD[ + type, width + ]: """Performs a strided load of the SIMD vector. Parameters: @@ -656,7 +743,11 @@ struct UnsafePointer[ type: DType, T: Intable, //, width: Int, - ](self: UnsafePointer[Scalar[type], *_], val: SIMD[type, width], stride: T): + ]( + self: UnsafePointer[Scalar[type], *_, **_], + val: SIMD[type, width], + stride: T, + ): """Performs a strided store of the SIMD vector. Parameters: @@ -679,7 +770,7 @@ struct UnsafePointer[ SIMD[type, width] ]() if triple_is_nvidia_cuda() else 1, ]( - self: UnsafePointer[Scalar[type], *_], + self: UnsafePointer[Scalar[type], *_, **_], offset: SIMD[_, width], mask: SIMD[DType.bool, width] = True, default: SIMD[type, width] = 0, @@ -736,7 +827,7 @@ struct UnsafePointer[ SIMD[type, width] ]() if triple_is_nvidia_cuda() else 1, ]( - self: UnsafePointer[Scalar[type], *_], + self: UnsafePointer[Scalar[type], *_, **_], offset: SIMD[_, width], val: SIMD[type, width], mask: SIMD[DType.bool, width] = True, @@ -784,7 +875,7 @@ struct UnsafePointer[ scatter(val, base, mask, alignment) @always_inline - fn free(self: UnsafePointer[_, AddressSpace.GENERIC, *_]): + fn free(self: UnsafePointer[_, AddressSpace.GENERIC, *_, **_]): """Free the memory referenced by the pointer.""" _free(self) @@ -843,7 +934,9 @@ struct UnsafePointer[ ]() @always_inline - fn destroy_pointee(self: UnsafePointer[type, AddressSpace.GENERIC, *_]): + fn destroy_pointee( + self: UnsafePointer[type, AddressSpace.GENERIC, *_, **_] + ): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed @@ -857,7 +950,7 @@ struct UnsafePointer[ @always_inline fn take_pointee[ T: Movable, //, - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_]) -> T: + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_]) -> T: """Move the value at the pointer out, leaving it uninitialized. The pointer must not be null, and the pointer memory location is assumed @@ -880,7 +973,7 @@ struct UnsafePointer[ @always_inline fn init_pointee_move[ T: Movable, //, - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_], owned value: T): + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], owned value: T): """Emplace a new value into the pointer location, moving from `value`. The pointer memory location is assumed to contain uninitialized data, @@ -902,7 +995,7 @@ struct UnsafePointer[ @always_inline fn init_pointee_copy[ T: Copyable, //, - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_], value: T): + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], value: T): """Emplace a copy of `value` into the pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -924,7 +1017,7 @@ struct UnsafePointer[ @always_inline fn init_pointee_explicit_copy[ T: ExplicitlyCopyable, // - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_], value: T): + ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], value: T): """Emplace a copy of `value` into this pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -948,8 +1041,8 @@ struct UnsafePointer[ fn move_pointee_into[ T: Movable, //, ]( - self: UnsafePointer[T, AddressSpace.GENERIC, *_], - dst: UnsafePointer[T, AddressSpace.GENERIC, *_], + self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], + dst: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], ): """Moves the value `self` points to into the memory location pointed to by `dst`. diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index ca4d5223fd..ab3a2c5f64 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -160,7 +160,9 @@ def test_issue_20421(): var a = UnsafePointer[UInt8, alignment=64].alloc(count=16 * 64) for i in range(16 * 64): a[i] = i & 255 - var av16 = a.offset(128 + 64 + 4).bitcast[Int32]().load[width=4]() + var av16 = a.offset(128 + 64 + 4).bitcast[Int32]().load[ + width=4, alignment=1 + ]() assert_equal( av16, SIMD[DType.int32, 4](-943274556, -875902520, -808530484, -741158448), diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index dedda58f4d..86b735004f 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -281,7 +281,7 @@ def test_load_and_store_simd(): var ptr2 = UnsafePointer[Int8].alloc(16) for i in range(0, 16, 4): - ptr2.store[width=4](i, i) + ptr2.store(i, SIMD[DType.int8, 4](i)) for i in range(16): assert_equal(ptr2[i], i // 4 * 4) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index bc30c829d4..af63cec115 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -37,7 +37,7 @@ def test_compressed_store(): assert_equal(vector.load[width=4](0), F32x4(2.0, 3.0, 0.0, 0.0)) # Just clear the buffer. - vector.store[width=4](0, 0) + vector.store(0, SIMD[DType.float32, 4](0)) var val = F32x4(0.0, 1.0, 3.0, 0.0) compressed_store(val, vector, val != 0) From fcfda93b16067e847090d56b9512ed88967297b5 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 1 Oct 2024 18:21:12 -0700 Subject: [PATCH 1668/2019] [******] Enable raising function in benchmark code, NFC MODULAR_ORIG_COMMIT_REV_ID: 924f8d77ef61d2a1790ac5f3f442cf8f43fedd67 --- examples/notebooks/Matmul.ipynb | 2 +- .../benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/time/time.mojo | 29 +++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index 71a63127af..a25647594c 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -502,7 +502,7 @@ "\n", "@always_inline\n", "fn bench[\n", - " func: fn (Matrix, Matrix, Matrix) -> None](base_gflops: Float64):\n", + " func: fn (Matrix, Matrix, Matrix) -> None](base_gflops: Float64) raises:\n", " var C = Matrix[M, N]()\n", " var A = Matrix[M, K].rand()\n", " var B = Matrix[K, N].rand()\n", diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 889cebba89..09f210d412 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -234,7 +234,7 @@ fn unroll_nested_call[ func[index]() -fn bench_compare(): +fn bench_compare() raises: alias type = DType.uint8 alias width = simdwidthof[type]() alias unit = Unit.ns diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index fd6f143cbf..458d9822b6 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -19,6 +19,7 @@ from time import now ``` """ +from os import abort from sys import ( external_call, os_is_linux, @@ -258,7 +259,9 @@ fn monotonic() -> Int: @always_inline @parameter -fn _time_function_windows[func: fn () capturing [_] -> None]() -> Int: +fn _time_function_windows[ + func: fn () raises capturing [_] -> None +]() raises -> Int: """Calculates elapsed time in Windows""" var ticks_per_sec: _WINDOWS_LARGE_INTEGER = 0 @@ -289,7 +292,7 @@ fn _time_function_windows[func: fn () capturing [_] -> None]() -> Int: @always_inline @parameter -fn time_function[func: fn () capturing [_] -> None]() -> Int: +fn time_function[func: fn () raises capturing [_] -> None]() raises -> Int: """Measures the time spent in the function. Parameters: @@ -309,6 +312,28 @@ fn time_function[func: fn () capturing [_] -> None]() -> Int: return toc - tic +@always_inline +@parameter +fn time_function[func: fn () capturing [_] -> None]() -> Int: + """Measures the time spent in the function. + + Parameters: + func: The function to time. + + Returns: + The time elapsed in the function in ns. + """ + + @parameter + fn raising_func() raises: + func() + + try: + return time_function[raising_func]() + except err: + return abort[Int](err) + + # ===----------------------------------------------------------------------===# # sleep # ===----------------------------------------------------------------------===# From 8d285e42d2999f735bcf7c3ed1314ecfcc2d3b66 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 1 Oct 2024 21:09:12 -0500 Subject: [PATCH 1669/2019] [stdlib] cleanup: Use `OpaquePointer` instead of `UnsafePointer[NoneType]` `UnsafePointer[NoneType]` appears very often in FFI and similar low-level code. Switching to `OpaquePointer` is both shorter and less syntactically noisy. MODULAR_ORIG_COMMIT_REV_ID: 33e73fcdcb0297307190fe79ed4af82ab208fad2 --- stdlib/src/builtin/_startup.mojo | 15 ++++++------ stdlib/src/builtin/file.mojo | 13 ++++++----- stdlib/src/builtin/hash.mojo | 8 +++---- stdlib/src/builtin/io.mojo | 7 +++--- stdlib/src/builtin/object.mojo | 3 ++- stdlib/src/collections/dict.mojo | 3 ++- stdlib/src/collections/string.mojo | 4 ++-- stdlib/src/os/os.mojo | 6 ++--- stdlib/src/random/random.mojo | 5 ++-- stdlib/src/sys/ffi.mojo | 36 ++++++++++++++--------------- stdlib/src/sys/info.mojo | 4 ++-- stdlib/src/utils/format.mojo | 15 ++++++------ stdlib/src/utils/inline_string.mojo | 3 ++- stdlib/src/utils/lock.mojo | 5 ++-- stdlib/test/utils/test_variant.mojo | 8 +++---- 15 files changed, 70 insertions(+), 65 deletions(-) diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 15d0d9f450..97a815742f 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -14,26 +14,25 @@ from memory import UnsafePointer from sys import external_call -from sys.ffi import _get_global +from sys.ffi import _get_global, OpaquePointer -fn _init_global_runtime( - ignored: UnsafePointer[NoneType], -) -> UnsafePointer[NoneType]: +fn _init_global_runtime(ignored: OpaquePointer) -> OpaquePointer: return external_call[ - "KGEN_CompilerRT_AsyncRT_CreateRuntime", UnsafePointer[NoneType] + "KGEN_CompilerRT_AsyncRT_CreateRuntime", + OpaquePointer, ](0) -fn _destroy_global_runtime(ptr: UnsafePointer[NoneType]): +fn _destroy_global_runtime(ptr: OpaquePointer): """Destroy the global runtime if ever used.""" external_call["KGEN_CompilerRT_AsyncRT_DestroyRuntime", NoneType](ptr) @always_inline -fn _get_current_or_global_runtime() -> UnsafePointer[NoneType]: +fn _get_current_or_global_runtime() -> OpaquePointer: var current_runtime = external_call[ - "KGEN_CompilerRT_AsyncRT_GetCurrentRuntime", UnsafePointer[NoneType] + "KGEN_CompilerRT_AsyncRT_GetCurrentRuntime", OpaquePointer ]() if current_runtime: return current_runtime diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 9d02063647..41aecb651f 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -33,6 +33,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call, sizeof +from sys.ffi import OpaquePointer from utils import Span, StringRef from memory import AddressSpace, UnsafePointer @@ -69,12 +70,12 @@ struct _OwnedStringRef(Boolable): struct FileHandle: """File handle to an opened file.""" - var handle: UnsafePointer[NoneType] + var handle: OpaquePointer """The underlying pointer to the file handle.""" fn __init__(inout self): """Default constructor.""" - self.handle = UnsafePointer[NoneType]() + self.handle = OpaquePointer() fn __init__(inout self, path: String, mode: String) raises: """Construct the FileHandle using the file path and mode. @@ -94,11 +95,11 @@ struct FileHandle: """ var err_msg = _OwnedStringRef() var handle = external_call[ - "KGEN_CompilerRT_IO_FileOpen", UnsafePointer[NoneType] + "KGEN_CompilerRT_IO_FileOpen", OpaquePointer ](path, mode, Reference.address_of(err_msg)) if err_msg: - self.handle = UnsafePointer[NoneType]() + self.handle = OpaquePointer() raise err_msg^.consume_as_error() self.handle = handle @@ -123,7 +124,7 @@ struct FileHandle: if err_msg: raise err_msg^.consume_as_error() - self.handle = UnsafePointer[NoneType]() + self.handle = OpaquePointer() fn __moveinit__(inout self, owned existing: Self): """Moves constructor for the file handle. @@ -132,7 +133,7 @@ struct FileHandle: existing: The existing file handle. """ self.handle = existing.handle - existing.handle = UnsafePointer[NoneType]() + existing.handle = OpaquePointer() fn read(self, size: Int64 = -1) raises -> String: """Reads data from a file and sets the file handle seek position. If diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index d67e8a8a4a..528cf2bd3e 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -26,7 +26,7 @@ There are a few main tools in this module: """ import random -from sys.ffi import _get_global +from sys.ffi import _get_global, OpaquePointer from sys import simdwidthof, bitwidthof from collections import InlineArray @@ -52,15 +52,15 @@ fn _HASH_SECRET() -> UInt: fn _initialize_hash_secret( - payload: UnsafePointer[NoneType], -) -> UnsafePointer[NoneType]: + payload: OpaquePointer, +) -> OpaquePointer: var secret = random.random_ui64(0, UInt64.MAX) var data = UnsafePointer[Int].alloc(1) data[] = int(secret) return data.bitcast[NoneType]() -fn _destroy_hash_secret(p: UnsafePointer[NoneType]): +fn _destroy_hash_secret(p: OpaquePointer): p.free() diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index a0532243c0..84a6bada47 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -23,6 +23,7 @@ from sys import ( _libc as libc, ) from sys._libc import dup, fclose, fdopen, fflush +from sys.ffi import OpaquePointer from builtin.builtin_list import _LITRefPackHelper from builtin.dtype import _get_dtype_printf_format @@ -40,7 +41,7 @@ from utils import Formattable, Formatter @value @register_passable("trivial") struct _fdopen[mode: StringLiteral = "a"]: - var handle: UnsafePointer[NoneType] + var handle: OpaquePointer fn __init__(inout self, stream_id: FileDescriptor): """Creates a file handle to the stdout/stderr stream. @@ -124,7 +125,7 @@ struct _fdopen[mode: StringLiteral = "a"]: UnsafePointer[UnsafePointer[UInt8]], UnsafePointer[UInt64], Int, - UnsafePointer[NoneType], + OpaquePointer, ]( UnsafePointer.address_of(buffer), UnsafePointer.address_of(UInt64(0)), @@ -314,7 +315,7 @@ fn _put[ var tmp = 0 var arg_ptr = UnsafePointer.address_of(tmp) _ = external_call["vprintf", Int32]( - x.unsafe_ptr(), arg_ptr.bitcast[UnsafePointer[NoneType]]() + x.unsafe_ptr(), arg_ptr.bitcast[OpaquePointer]() ) else: alias MAX_STR_LEN = 0x1000_0000 diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index e385b2e1f3..832a3bb3b3 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import Dict, List from sys.intrinsics import _type_is_eq +from sys.ffi import OpaquePointer from memory import Arc, memcmp, memcpy, UnsafePointer @@ -84,7 +85,7 @@ struct _RefCountedList: @register_passable("trivial") struct _RefCountedListRef(CollectionElement, CollectionElementNew): # FIXME(#3335): Use indirection to avoid a recursive struct definition. - var lst: UnsafePointer[NoneType] + var lst: OpaquePointer """The reference to the list.""" @always_inline diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 245d33f0d9..660cfd3bb4 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -35,6 +35,7 @@ from builtin.value import StringableCollectionElement from .optional import Optional from bit import is_power_of_two +from sys.ffi import OpaquePointer from memory import memcpy, bitcast, UnsafePointer @@ -256,7 +257,7 @@ struct _DictIndex: this in the current type system. """ - var data: UnsafePointer[NoneType] + var data: OpaquePointer @always_inline fn __init__(inout self, reserved: Int): diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 40379a24af..98b29cf15e 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic -from sys.ffi import c_char +from sys.ffi import c_char, OpaquePointer from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy @@ -1269,7 +1269,7 @@ struct String( value. This `String` MUST outlive the `Formatter` instance. """ - fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): + fn write_to_string(ptr0: OpaquePointer, strref: StringRef): var ptr: UnsafePointer[String] = ptr0.bitcast[String]() # FIXME: diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index e43429fbe7..2605d23067 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -21,7 +21,7 @@ from os import listdir from collections import List, InlineArray from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call -from sys.ffi import c_char +from sys.ffi import c_char, OpaquePointer from memory import UnsafePointer from utils import StringRef @@ -93,7 +93,7 @@ fn _strnlen(ptr: UnsafePointer[c_char], max: Int) -> Int: struct _DirHandle: """Handle to an open directory descriptor opened via opendir.""" - var _handle: UnsafePointer[NoneType] + var _handle: OpaquePointer fn __init__(inout self, path: String) raises: """Construct the _DirHandle using the path provided. @@ -108,7 +108,7 @@ struct _DirHandle: if not isdir(path): raise "the directory '" + path + "' does not exist" - self._handle = external_call["opendir", UnsafePointer[NoneType]]( + self._handle = external_call["opendir", OpaquePointer]( path.unsafe_ptr() ) diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 58bd5967d1..840e353f7a 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -20,6 +20,7 @@ from random import seed """ from sys import bitwidthof, external_call +from sys.ffi import OpaquePointer from time import perf_counter_ns from collections import Optional @@ -28,10 +29,10 @@ from math import floor import math -fn _get_random_state() -> UnsafePointer[NoneType]: +fn _get_random_state() -> OpaquePointer: return external_call[ "KGEN_CompilerRT_GetRandomState", - UnsafePointer[NoneType], + OpaquePointer, ]() diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 99460e3da8..15e1cb13c2 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -326,29 +326,27 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): @always_inline fn _get_global[ name: StringLiteral, - init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], - destroy_fn: fn (UnsafePointer[NoneType]) -> None, -]( - payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]() -) -> UnsafePointer[NoneType]: - return external_call[ - "KGEN_CompilerRT_GetGlobalOrCreate", UnsafePointer[NoneType] - ](StringRef(name), payload, init_fn, destroy_fn) + init_fn: fn (OpaquePointer) -> OpaquePointer, + destroy_fn: fn (OpaquePointer) -> None, +](payload: OpaquePointer = OpaquePointer()) -> OpaquePointer: + return external_call["KGEN_CompilerRT_GetGlobalOrCreate", OpaquePointer]( + StringRef(name), payload, init_fn, destroy_fn + ) @always_inline -fn _get_global_or_null[name: StringLiteral]() -> UnsafePointer[NoneType]: - return external_call[ - "KGEN_CompilerRT_GetGlobalOrNull", UnsafePointer[NoneType] - ](name.unsafe_ptr(), name.byte_length()) +fn _get_global_or_null[name: StringLiteral]() -> OpaquePointer: + return external_call["KGEN_CompilerRT_GetGlobalOrNull", OpaquePointer]( + name.unsafe_ptr(), name.byte_length() + ) @always_inline fn _get_dylib[ name: StringLiteral, - init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], - destroy_fn: fn (UnsafePointer[NoneType]) -> None, -](payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]()) -> DLHandle: + init_fn: fn (OpaquePointer) -> OpaquePointer, + destroy_fn: fn (OpaquePointer) -> None, +](payload: OpaquePointer = OpaquePointer()) -> DLHandle: var ptr = _get_global[name, init_fn, destroy_fn](payload).bitcast[ DLHandle ]() @@ -359,10 +357,10 @@ fn _get_dylib[ fn _get_dylib_function[ name: StringLiteral, func_name: StringLiteral, - init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], - destroy_fn: fn (UnsafePointer[NoneType]) -> None, + init_fn: fn (OpaquePointer) -> OpaquePointer, + destroy_fn: fn (OpaquePointer) -> None, result_type: AnyTrivialRegType, -](payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]()) -> result_type: +](payload: OpaquePointer = OpaquePointer()) -> result_type: alias func_cache_name = name + "/" + func_name var func_ptr = _get_global_or_null[func_cache_name]() if func_ptr: @@ -374,7 +372,7 @@ fn _get_dylib_function[ var new_func = dylib._get_function[func_name, result_type]() external_call["KGEN_CompilerRT_InsertGlobal", NoneType]( StringRef(func_cache_name), - UnsafePointer.address_of(new_func).bitcast[UnsafePointer[NoneType]]()[], + UnsafePointer.address_of(new_func).bitcast[OpaquePointer]()[], ) return new_func diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index f2e77ec0e1..b404a8c194 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -19,7 +19,7 @@ from sys import is_x86 ``` """ -from .ffi import _external_call_const, external_call +from .ffi import _external_call_const, external_call, OpaquePointer from memory import UnsafePointer @@ -780,7 +780,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: "kern.osproductversion".unsafe_cstr_ptr(), buf.data, Reference.address_of(buf_len), - UnsafePointer[NoneType](), + OpaquePointer(), Int(0), ) diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo index 069d465691..c47de9a83a 100644 --- a/stdlib/src/utils/format.mojo +++ b/stdlib/src/utils/format.mojo @@ -16,6 +16,7 @@ themselves to a string. from builtin.io import _put from memory import UnsafePointer +from sys.ffi import OpaquePointer # ===----------------------------------------------------------------------===# # Interface traits @@ -81,8 +82,8 @@ struct Formatter: # seemingly getting clobbered in between when the closure was constructed # and first called. Once that bug is fixed, this should be replaced with # an `escaping` closure again. - var _write_func: fn (UnsafePointer[NoneType], StringRef) -> None - var _write_func_arg: UnsafePointer[NoneType] + var _write_func: fn (OpaquePointer, StringRef) -> None + var _write_func_arg: OpaquePointer """Closure argument passed to `_write_func`.""" # ===------------------------------------------------------------------===# @@ -109,7 +110,7 @@ struct Formatter: """ @always_inline - fn write_to_fd(ptr: UnsafePointer[NoneType], strref: StringRef): + fn write_to_fd(ptr: OpaquePointer, strref: StringRef): var fd0 = ptr.bitcast[FileDescriptor]()[].value _put(strref, file=fd0) @@ -121,8 +122,8 @@ struct Formatter: fn __init__( inout self, - func: fn (UnsafePointer[NoneType], StringRef) -> None, - arg: UnsafePointer[NoneType], + func: fn (OpaquePointer, StringRef) -> None, + arg: OpaquePointer, ): """Constructs a formatter from any closure that accepts `StringRef`s. @@ -215,7 +216,7 @@ struct Formatter: """ @always_inline - fn write_to_stdout(_data: UnsafePointer[NoneType], strref: StringRef): + fn write_to_stdout(_data: OpaquePointer, strref: StringRef): _put(strref) - return Formatter(write_to_stdout, UnsafePointer[NoneType]()) + return Formatter(write_to_stdout, OpaquePointer()) diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 2fa4f6d972..2c5ce4555f 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -19,6 +19,7 @@ from collections import InlineArray from os import abort from collections import Optional from sys import sizeof +from sys.ffi import OpaquePointer from memory import UnsafePointer, memcpy @@ -474,7 +475,7 @@ struct _FixedString[CAP: Int]( writer.write_str(self.as_string_slice()) fn _unsafe_to_formatter(inout self) -> Formatter: - fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): + fn write_to_string(ptr0: OpaquePointer, strref: StringRef): var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() var str_slice = StringSlice[ImmutableAnyLifetime]( diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 93beea947c..18cdeef46b 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -15,6 +15,7 @@ from memory import UnsafePointer from os import Atomic from time import sleep from sys import external_call +from sys.ffi import OpaquePointer # ===----------------------------------------------------------------------===# @@ -25,14 +26,14 @@ from sys import external_call struct SpinWaiter: """A proxy for the C++ runtime's SpinWaiter type.""" - var storage: UnsafePointer[NoneType] + var storage: OpaquePointer """Pointer to the underlying SpinWaiter instance.""" fn __init__(inout self: Self): """Initializes a SpinWaiter instance.""" self.storage = external_call[ "KGEN_CompilerRT_AsyncRT_InitializeSpinWaiter", - UnsafePointer[NoneType], + OpaquePointer, ]() fn __del__(owned self: Self): diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 6e326e0290..f517433183 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.ffi import _get_global +from sys.ffi import _get_global, OpaquePointer from memory import UnsafePointer from testing import assert_equal, assert_false, assert_true @@ -53,14 +53,14 @@ fn assert_no_poison() raises: fn _initialize_poison( - payload: UnsafePointer[NoneType], -) -> UnsafePointer[NoneType]: + payload: OpaquePointer, +) -> OpaquePointer: var poison = UnsafePointer[Bool].alloc(1) poison.init_pointee_move(False) return poison.bitcast[NoneType]() -fn _destroy_poison(p: UnsafePointer[NoneType]): +fn _destroy_poison(p: OpaquePointer): p.free() From c5d844c70c00f3d3710af19f426c2954a6c59e35 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 1 Oct 2024 19:10:33 -0700 Subject: [PATCH 1670/2019] [mojo-stdlib] Temporarily disable SIMD's explicit copy constructor This constructor is actually ambiguous with the construct from a Scalar when `self` is also a scalar, but this wasn't being picked up by the compiler before. Disable it temporarily, as things are current moving around in the compiler right now. MODULAR_ORIG_COMMIT_REV_ID: 68bb44e75ec9c88ac36c97a74b2b82e6f8330f50 --- stdlib/src/builtin/simd.mojo | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 23b89d4a43..2e78a68f71 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -167,7 +167,7 @@ struct SIMD[type: DType, size: Int]( Ceilable, CeilDivable, CollectionElement, - CollectionElementNew, + # CollectionElementNew, Floatable, Floorable, Formattable, @@ -229,14 +229,14 @@ struct SIMD[type: DType, size: Int]( _simd_construction_checks[type, size]() self = _unchecked_zero[type, size]() - @always_inline("nodebug") - fn __init__(inout self, *, other: SIMD[type, size]): - """Explicitly copy the provided value. + # @always_inline("nodebug") + # fn __init__(inout self, *, other: SIMD[type, size]): + # """Explicitly copy the provided value. - Args: - other: The value to copy. - """ - self.__copyinit__(other) + # Args: + # other: The value to copy. + # """ + # self.__copyinit__(other) @always_inline("nodebug") fn __init__(inout self, value: UInt): From 711e02885b79a19beebd37159ebb59e4b68a139f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 1 Oct 2024 19:55:12 -0700 Subject: [PATCH 1671/2019] [mojo] Add FIXMEs for disabled code (NFC) So it's easy to find this when the issue is fixed. MODULAR_ORIG_COMMIT_REV_ID: f683804f48998c4a014b0c79411c11f21b7a4d87 --- stdlib/src/builtin/simd.mojo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2e78a68f71..b5c4a2aafa 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -167,6 +167,7 @@ struct SIMD[type: DType, size: Int]( Ceilable, CeilDivable, CollectionElement, + # FIXME(MOCO-1291): Can't implement this due to ambiguity. # CollectionElementNew, Floatable, Floorable, @@ -229,6 +230,7 @@ struct SIMD[type: DType, size: Int]( _simd_construction_checks[type, size]() self = _unchecked_zero[type, size]() + # FIXME(MOCO-1291): Can't implement this due to ambiguity. # @always_inline("nodebug") # fn __init__(inout self, *, other: SIMD[type, size]): # """Explicitly copy the provided value. From e2f66f28c66167609e6538b6a741b5807e7fcf75 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 1 Oct 2024 20:13:37 -0700 Subject: [PATCH 1672/2019] [******][GPU] Simplify the abs operation for bf16 MODULAR_ORIG_COMMIT_REV_ID: 72022b697675345ccd818403875a6a571ee3da77 --- stdlib/src/builtin/simd.mojo | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b5c4a2aafa..b9ec3f601f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1465,28 +1465,12 @@ struct SIMD[type: DType, size: Int]( @parameter if type.is_half_float(): alias prefix = "abs.bf16" if type is DType.bfloat16 else "abs.f16" - - @parameter - if size == 1: - return inlined_assembly[ - prefix + " $0, $1;", - Self, - constraints="=h,h", - has_side_effect=False, - ](self) - - var res = Self() - - @parameter - for i in range(0, size, 2): - var val = inlined_assembly[ - prefix + "x2 $0, $1;", - SIMD[type, 2], - constraints="=r,r", - has_side_effect=False, - ](self.slice[2, offset=i]()) - res = res.insert[offset=i](val) - return res + return _call_ptx_intrinsic[ + scalar_instruction=prefix, + vector2_instruction = prefix + "x2", + scalar_constraints="=h,h", + vector_constraints="=r,r", + ](self) return llvm_intrinsic["llvm.fabs", Self, has_side_effect=False]( self ) From 43180baf3db4b0d75095cac0432d8209e11fead4 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:49:38 -0400 Subject: [PATCH 1673/2019] [External] [stdlib] Add `split()` to `StringRef` (#48246) [External] [stdlib] Add `split()` to `StringRef` This PR adds a `split()` function to `StringRef` and delegates `String.split()` to it ORIGINAL_AUTHOR=Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2705 Co-authored-by: Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> Co-authored-by: Jack Clayton Closes modularml/mojo#2705 MODULAR_ORIG_COMMIT_REV_ID: 0ba4c194ce5a533f3a8bfd58e4e34c3c2b8733b9 --- docs/changelog.md | 4 +++ stdlib/src/utils/stringref.mojo | 35 ++++++++++++++++++++++ stdlib/test/utils/test_stringref.mojo | 42 +++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 975db26005..9200c2d884 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -175,6 +175,10 @@ what we publish. return a ``` +- `StringRef` now implements `split()` which can be used to split a + `StringRef` into a `List[StringRef]` by a delimiter. + ([PR #2705](https://github.com/modularml/mojo/pull/2705) by [@fknfilewalker](https://github.com/fknfilewalker)) + - Support for multi-dimensional indexing for `PythonObject` ([PR #3583](https://github.com/modularml/mojo/pull/3583) by [@jjvraw](https://github.com/jjvraw)). diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index c810d1865f..065c820caa 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -574,6 +574,41 @@ struct StringRef( end -= 1 return StringRef(ptr + start, end - start) + fn split(self, delimiter: StringRef) raises -> List[StringRef]: + """Split the StringRef by a delimiter. + + Args: + delimiter: The StringRef to split on. + + Returns: + A List of StringRefs containing the input split by the delimiter. + + Raises: + Error if an empty delimiter is specified. + """ + if not delimiter: + raise Error("empty delimiter not allowed to be passed to split.") + + var output = List[StringRef]() + var ptr = self.unsafe_ptr() + + var current_offset = 0 + while True: + var loc = self.find(delimiter, current_offset) + # delimiter not found, so add the search slice from where we're currently at + if loc == -1: + output.append( + StringRef(ptr + current_offset, len(self) - current_offset) + ) + break + + # We found a delimiter, so add the preceding string slice + output.append(StringRef(ptr + current_offset, loc - current_offset)) + + # Advance our search offset past the delimiter + current_offset = loc + len(delimiter) + return output + fn startswith( self, prefix: StringRef, start: Int = 0, end: Int = -1 ) -> Bool: diff --git a/stdlib/test/utils/test_stringref.mojo b/stdlib/test/utils/test_stringref.mojo index b5f9d35976..33c18c533f 100644 --- a/stdlib/test/utils/test_stringref.mojo +++ b/stdlib/test/utils/test_stringref.mojo @@ -128,8 +128,50 @@ def test_endswith(): assert_true(ab.endswith("ab")) +fn test_stringref_split() raises: + # Reject empty delimiters + with assert_raises( + contains="empty delimiter not allowed to be passed to split." + ): + _ = StringRef("hello").split("") + + # Split in middle + var d1 = StringRef("n") + var in1 = StringRef("faang") + var res1 = in1.split(d1) + assert_equal(len(res1), 2) + assert_equal(res1[0], "faa") + assert_equal(res1[1], "g") + + # Matches should be properly split in multiple case + var d2 = StringRef(" ") + var in2 = StringRef("modcon is coming soon") + var res2 = in2.split(d2) + assert_equal(len(res2), 4) + assert_equal(res2[0], "modcon") + assert_equal(res2[1], "is") + assert_equal(res2[2], "coming") + assert_equal(res2[3], "soon") + + # No match from the delimiter + var d3 = StringRef("x") + var in3 = StringRef("hello world") + var res3 = in3.split(d3) + assert_equal(len(res3), 1) + assert_equal(res3[0], "hello world") + + # Multiple character delimiter + var d4 = StringRef("ll") + var in4 = StringRef("hello") + var res4 = in4.split(d4) + assert_equal(len(res4), 2) + assert_equal(res4[0], "he") + assert_equal(res4[1], "o") + + def main(): test_strref_from_start() + test_stringref_split() test_comparison_operators() test_intable() test_indexing() From 5fcba38724595c9b681ab6d71a52c0d39dea50f7 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Wed, 2 Oct 2024 14:59:40 -0400 Subject: [PATCH 1674/2019] [External] [stdlib] Add a new hash function based on AHash algorithm (#47460) [External] [stdlib] Add a new hash function based on AHash algorithm This PR introduces a new hash function based on [AHash](https://github.com/tkaitchuck/aHash/tree/master) algorithm. The goal is to provide a playground for std lib devs to consider replacing current hash function. Co-authored-by: Maxim Zaks Closes modularml/mojo#3476 MODULAR_ORIG_COMMIT_REV_ID: 6f7f6f7aae37a1e20d589db2274af31db32a984c --- stdlib/benchmarks/builtin/bench_hash.mojo | 716 +++++++++++++++++++ stdlib/src/builtin/_hash.mojo | 180 +++++ stdlib/test/builtin/test__hash.mojo | 806 ++++++++++++++++++++++ 3 files changed, 1702 insertions(+) create mode 100644 stdlib/benchmarks/builtin/bench_hash.mojo create mode 100644 stdlib/src/builtin/_hash.mojo create mode 100644 stdlib/test/builtin/test__hash.mojo diff --git a/stdlib/benchmarks/builtin/bench_hash.mojo b/stdlib/benchmarks/builtin/bench_hash.mojo new file mode 100644 index 0000000000..9cdb6970ff --- /dev/null +++ b/stdlib/benchmarks/builtin/bench_hash.mojo @@ -0,0 +1,716 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo-no-debug %s -t + +from benchmark import Bench, BenchConfig, Bencher, BenchId + +# Source: https://www.101languages.net/arabic/most-common-arabic-words/ +alias words_ar = """ +لا, من, هذا, أن, في, أنا, على, ما, هل, + يا, و, لقد, ذلك, ماذا, أنت, هنا, لم, إلى, نعم, كان, هو, ان, هذه, هناك, عن, فى, كل, ليس, فقط, كنت, الآن, يجب, انا, + لك, مع, شيء, لكن, لن, الذي, حسنا, كيف, سوف, هيا, نحن, إنه, ـ, أجل, لماذا, إذا, عندما, انه, كذلك, لي, الى, بعد, انت, + هي, أين, أنه, كانت, حتى, أي, إنها, أعرف, قد, قبل, تلك, الأمر, بعض, أو, مثل, أريد, رجل, لو, أعتقد, ربما, أيها, بخير, + يكون, عليك, جيد, أنك, شخص, إن, التي, ولكن, أليس, علي, أحد, به, الوقت, يمكن, انها, اليوم, شئ, تعرف, تريد, صحيح, أكثر, + تكون, لست, كما, أستطيع, منذ, جدا, سيدي, يمكنك, لذا, واحد, لديك, يبدو, أوه, كلا, الرجل, لدي, تفعل, غير, عليه, اذا, + آخر, حدث, مرة, شكرا, لدينا, الناس, يوجد, له, مكان, سيد, سيكون, أعلم, رائع, مرحبا, آسف, بهذا, وقت, اللعنة, كم, + ليست, أفضل, بها, معك, أنها, الذى, الكثير, قلت, بك, يحدث, الان, يكن, يوم, وأنا, واحدة, بي, أخرى, ولا, علينا, + أبي, بأن, ثم, تعال, هكذا, يمكنني, هم, ألا, بالطبع, أنني, المكان, بذلك, معي, لهذا, ها, شىء, انك, إلهي, تستطيع, + العمل, العالم, الحقيقة, الليلة, بالتأكيد, حقا, تعلم, أمي, الطريق, حال, لى, لها, الأن, هؤلاء, فعل, توقف, + عمل, حول, لنا, خلال, اعتقد, السيد, انظر, منك, أى, أفعل, فعلت, لأن, إذن, قال, الجميع, تم, الجحيم, هى, فيه, + جيدة, عنه, بشكل, بما, تقول, لديه, ثانية, لذلك, أكون, دعنا, ايها, المال, يمكننا, الذهاب, متى, تعتقد, + اريد, عليها, أذهب, ستكون, فضلك, بدون, أرجوك, التى, شيئا, نذهب, لكي, نفسك, بنا, اين, وأنت, لكم, اي, + بين, إنهم, أرى, المنزل, بحق, كنا, عند, أم, منه, نفس, اذهب, حيث, مجرد, أقول, تبدو, الحياة, أيضا, + تحت, الأشياء, معه, يريد, أننا, أنظر, لما, اعرف, إلي, ثلاثة, انتظر, الرجال, الذين, حصلت, أني, + سعيد, لابد, عزيزتي, الشيء, + فكرة, انهم, الله, الباب, سيدى, دائما, رأيت, مشكلة, استطيع, تكن, تذهب, ليلة, شيئ, أظن, طوال, + جميل, وهو, الشرطة, او, دولار, السيارة, وهذا, كبير, مني, بسرعة, النار, الأمور, سمعت, أشعر, يعرف, + أعني, لدى, بهذه, أحب, سنوات, بأس, الأفضل, بالنسبة, + أنتم, عظيم, يقول, جميلة, جون, جاك, بسبب, الوحيد, أمر, بل, بالفعل, الشخص, الي, دعني, خارج, اجل, الخير, ــ, + حالك, للغاية, فحسب, كانوا, أردت, فتاة, بشأن, يعني, كبيرة, ترى, آسفة, دقيقة, أنهم, يستطيع, احد, بأنك, تعمل, + تريدين, فيها, اليس, رائعة, رجال, نوع, حياتي, الأرض, البيت, قتل, اوه, والآن, مات, بكل, تعرفين, أحتاج, نستطيع, + جديد, صباح, ألم, عيد, منها, يعمل, الموت, إليك, جميع, لأنه, لحظة, لكني, الامر, عشر, لكنه, بحاجة, بأنه, أتمنى, + إليه, عنك, الفتاة, لهم, بالضبط, سأكون, اعلم, اللعين, رقم, طريق, منهم, المدينة, الحب, لنذهب, خذ, أكن, فوق, + عزيزي, دون, الـ, صغيرة, الرئيس, تتحدث, ترجمة, صديقي, فقد, الصغير, ولم, ساعة, يفعل, غرفة, وماذا, المرة, قام, + إلا, عام, هذة, متأكد, دقائق, سيارة, فعله, سعيدة, مما, ومن, معنا, سبب, سأذهب, الطريقة, الأطفال, سنة, بينما, + يرام, السبب, أننى, أول, اى, أريدك, قمت, الأولى, المدرسة, ذهبت, لطيف, نفسي, الا, الجنس, أية, أقصد, غريب, نفعل, + الصباح, حالة, المزيد, أبدا, مهما, اسمع, لأنك, أحاول, وقد, ايضا, أحبك, اكثر, فرصة, رأيك, افعل, الحصول, صغير, + الماء, جيدا, التحدث, يمكننى, الساعة, طريقة, أيتها, كثيرا, سيدة, خمسة, وجدت, قليلا, وانا, اخرى, الليل, تعني, + تماما, نهاية, عرفت, اني, أفكر, معها, الأول, لكنك, تعالي, البعض, أفهم, أخبرك, حياة, أتعرف, نفسه, الواقع, + أيام, انني, تأتي, لديهم, فهمت, لـ, لديها, الحرب, الأقل, أخبرني, إنك, بـ, الصغيرة, تحتاج, بدأت, حياتك, + عني, إذهب, عندي, تقلق, نحتاج, إنتظر, أصبح, مجنون, يكفي, اننا, خطأ, الطفل, نصف, أكبر, الخاص, عليهم, + نريد, لأنني, حان, تعلمين, نعرف, هنالك, رفاق, لكنني, معى, دكتور, جديدة, هلا, افضل, طفل, عنها, أتعلم, تقوم, + أعمل, بد, الهاتف, بالخارج, السيدة, الطعام, ثلاث, أقوم, صديق, أتحدث, فرانك, الجديد, مالذي, للتو, سيدتي, + طويلة, وما, السجن, أشياء, فأنا, أخبرتك, العديد, أعطني, أراك, أخي, سام, قالت, فريق, فيما, جو, يتم, + نكون, وليس, يذهب, ممكن, لمدة, حق, اسف, يجري, تفعله, مثلك, وبعد, تشعر, تحب, اخر, رؤية, طويل, والدك, + ذهب, آه, أقل, حصل, لكى, اللعنه, سأفعل, يعلم, كله, القيام, فتى, الممكن, أخرج, النوم, داخل, جورج, + رجاء, أصبحت, الخاصة, اذن, ذات, جميعا, منا, الموضوع, الفتى, اللقاء, أخر, كي, كلمة, عبر, أود, بيت, + تفهم, تفعلين, علاقة, بى, نيويورك, الآخر, بلا, مايكل, نظرة, ونحن, الخارج, تحاول, المشكلة, بواسطة, كن, + المفترض, قل, يارجل, تظن, يقوم, مليون, أخذ, توم, يمكنه, مباشرة, سيئة, الحال, العودة, حاول, عندك, + تكوني, ميت, الكبير, الفتيات, النساء, رئيس, أسرع, النهاية, قادم, أحضر, جزء, + الهي, ذاهب, العام, لكنها, أتريد, بخصوص, الوغد, حقيقي, إنني, البقاء, حبيبتي, بهم, المساعدة, تصبح, عشرة, أحدهم, + الخروج, قصة, مستحيل, أربعة, وهي, أبى, كلها, ضد, حاولت, القادمة, يأتي, تفضل, أسمع, تمت, توجد, لكل, العشاء, + الغرفة, وانت, وسوف, خمس, تذكر, أصدق, ألف, بنفسك, شباب, الماضي, دعونا, الأسبوع, نتحدث, نسيت, بأنني, منزل, + وضع, ولد, أنتي, جاهز, رسالة, دي, ابن, اكون, حقيقة, مايك, حين, عائلة, أدري, وكان, القائد, للمنزل, مساعدتك, + غدا, ظننت, ولن, المرأة, لهذه, تحرك, يهم, تبقى, الطبيب, اسم, انظري, تبا, أتذكر, فترة, ساعات, تفكر, تحصل, + بأي, النقود, لعبة, زوجتي, الكلام, ستفعل, أسف, فهو, الملك, مدينة, بكم, الوحيدة, أمام, عدد, اخرج, بول, سأعود, + جئت, لأني, تحدث, السلامة, الماضية, أمك, اعتقدت, مره, مساء, بطريقة, الرب, ابدا, أهذا, وفي, وكل, أتيت, منكم, + انتهى, بوب, بعيدا, ضع, وجود, تعود, زلت, اللعينة, نقوم, كلنا, أحصل, يريدون, تأخذ, المحتمل, الشمس, بدأ, + ارجوك, المسيح, جاء, كهذا, سنذهب, تعالى, إثنان, فعلا, حتي, سيحدث, الجيد, وشك, القادم, + معرفة, صورة, أعود, اسمي, طلب, آنسة, الثانية, فقدت, حفلة, تنظر, مثير, اننى, وصلت, أنتظر, السماء, يقولون, الهراء, + معهم, ابي, وعندما, مجموعة, العاهرة, ماري, حسن, الزواج, نحو, دعيني, الجديدة, مهم, أمس, اتصل, ابتعد, هراء, ستة, + الأخرى, يحصل, ولكني, الطائرة, أصدقاء, الحظ, مشاكل, الترجمة, تبدين, لسنا, مستعد, ولكنه, اقول, أولئك, النوع, أثناء, + اسمه, اسمك, مكتب, والدي, ينبغي, منى, كرة, بيتر, عدم, أطفال, الإطلاق, سوى, مضحك, الوضع, جي, الأخيرة, صعب, أحمق, + يحاول, الشئ, حينما, الأشخاص, البحر, إليها, عرض, بأني, يحتاج, سيء, عالم, كثير, الداخل, الكتاب, ذو, الأيام, خلف, + بعضنا, يعود, ام, اللعبة, إني, رأسك, شركة, زال, بشيء, الاشياء, قطعة, خائف, واضح, أمى, موجود, علم, يعد, أبحث, + الدخول, جين, امرأة, متأكدة, هيه, تخبرني, مدى, إلهى, احب, عما, نرى, بيننا, تعيش, قتلت, الأحمق, تشارلي, + بيل, + عليكم, سؤال, طلبت, الهواء, وهذه, صوت, انتم, ميلاد, ماكس, + تعتقدين, الحديث, الجانب, صديقك, ذا, خطر, أطلق, الشارع, عملية, ببعض, تتكلم, مختلف, تحمل, مساعدة, + بضعة, المناسب, المنطقة, قم, بالداخل, البداية, لأجل, زوجتك, مقابل, يحب, هاري, ممتاز, قريبا, سنكون, + فعلته, بتلك, التفكير, أسفل, للعمل, العجوز, امي, الكلب, انتظري, مازال, إننا, اشعر, الجيش, شرطة +""" + +# Source: https://github.com/tkaitchuck/ahash/blob/7d5c661a74b12d5bc5448b0b83fdb429190db1a3/tests/map_tests.rs#L9 +alias words_en: String = """ + a, ability, able, about, above, accept, according, account, across, act, action, + activity, actually, add, address, administration, admit, adult, affect, after, + again, against, age, agency, agent, ago, agree, agreement, ahead, air, all, + allow, almost, alone, along, already, also, although, always, American, among, + amount, analysis, and, animal, another, answer, any, anyone, anything, appear, + apply, approach, area, argue, arm, around, arrive, art, article, artist, as, + ask, assume, at, attack, attention, attorney, audience, author, authority, + available, avoid, away, baby, back, bad, bag, ball, bank, bar, base, be, beat, + beautiful, because, become, bed, before, begin, behavior, behind, believe, + benefit, best, better, between, beyond, big, bill, billion, bit, black, blood, + blue, board, body, book, born, both, box, boy, break, bring, brother, budget, + build, building, business, but, buy, by, call, camera, campaign, can, cancer, + candidate, capital, car, card, care, career, carry, case, catch, cause, cell, + center, central, century, certain, certainly, chair, challenge, chance, change, + character, charge, check, child, choice, choose, church, citizen, city, civil, + claim, class, clear, clearly, close, coach, cold, collection, college, color, + come, commercial, common, community, company, compare, computer, concern, + condition, conference, Congress, consider, consumer, contain, continue, control, + cost, could, country, couple, course, court, cover, create, crime, cultural, + culture, cup, current, customer, cut, dark, data, daughter, day, dead, deal, + death, debate, decade, decide, decision, deep, defense, degree, Democrat, + democratic, describe, design, despite, detail, determine, develop, development, + die, difference, different, difficult, dinner, direction, director, discover, + discuss, discussion, disease, do, doctor, dog, door, down, draw, dream, drive, + drop, drug, during, each, early, east, easy, eat, economic, economy, edge, + education, effect, effort, eight, either, election, else, employee, end, energy, + enjoy, enough, enter, entire, environment, environmental, especially, establish, + even, evening, event, ever, every, everybody, everyone, everything, evidence, + exactly, example, executive, exist, expect, experience, expert, explain, eye, + face, fact, factor, fail, fall, family, far, fast, father, fear, federal, feel, + feeling, few, field, fight, figure, fill, film, final, finally, financial, find, + fine, finger, finish, fire, firm, first, fish, five, floor, fly, focus, follow, + food, foot, for, force, foreign, forget, form, former, forward, four, free, + friend, from, front, full, fund, future, game, garden, gas, general, generation, + get, girl, give, glass, go, goal, good, government, great, green, ground, group, + grow, growth, guess, gun, guy, hair, half, hand, hang, happen, happy, hard, + have, he, head, health, hear, heart, heat, heavy, help, her, here, herself, + high, him, himself, his, history, hit, hold, home, hope, hospital, hot, hotel, + hour, house, how, however, huge, human, hundred, husband, I, idea, identify, if, + image, imagine, impact, important, improve, in, include, including, increase, + indeed, indicate, individual, industry, information, inside, instead, + institution, interest, interesting, international, interview, into, investment, + involve, issue, it, item, its, itself, job, join, just, keep, key, kid, kill, + kind, kitchen, know, knowledge, land, language, large, last, late, later, laugh, + law, lawyer, lay, lead, leader, learn, least, leave, left, leg, legal, less, + let, letter, level, lie, life, light, like, likely, line, list, listen, little, + live, local, long, look, lose, loss, lot, love, low, machine, magazine, main, + maintain, major, majority, make, man, manage, management, manager, many, market, + marriage, material, matter, may, maybe, me, mean, measure, media, medical, meet, + meeting, member, memory, mention, message, method, middle, might, military, + million, mind, minute, miss, mission, model, modern, moment, money, month, more, + morning, most, mother, mouth, move, movement, movie, Mr, Mrs, much, music, must, + my, myself, name, nation, national, natural, nature, near, nearly, necessary, + need, network, never, new, news, newspaper, next, nice, night, no, none, nor, + north, not, note, nothing, notice, now, n't, number, occur, of, off, offer, + office, officer, official, often, oh, oil, ok, old, on, once, one, only, onto, + open, operation, opportunity, option, or, order, organization, other, others, + our, out, outside, over, own, owner, page, pain, painting, paper, parent, part, + participant, particular, particularly, partner, party, pass, past, patient, + pattern, pay, peace, people, per, perform, performance, perhaps, period, person, + personal, phone, physical, pick, picture, piece, place, plan, plant, play, + player, PM, point, police, policy, political, politics, poor, popular, + population, position, positive, possible, power, practice, prepare, present, + president, pressure, pretty, prevent, price, private, probably, problem, + process, produce, product, production, professional, professor, program, + project, property, protect, prove, provide, public, pull, purpose, push, put, + quality, question, quickly, quite, race, radio, raise, range, rate, rather, + reach, read, ready, real, reality, realize, really, reason, receive, recent, + recently, recognize, record, red, reduce, reflect, region, relate, relationship, + religious, remain, remember, remove, report, represent, Republican, require, + research, resource, respond, response, responsibility, rest, result, return, + reveal, rich, right, rise, risk, road, rock, role, room, rule, run, safe, same, + save, say, scene, school, science, scientist, score, sea, season, seat, second, + section, security, see, seek, seem, sell, send, senior, sense, series, serious, + serve, service, set, seven, several, sex, sexual, shake, share, she, shoot, + short, shot, should, shoulder, show, side, sign, significant, similar, simple, + simply, since, sing, single, sister, sit, site, situation, six, size, skill, + skin, small, smile, so, social, society, soldier, some, somebody, someone, + something, sometimes, son, song, soon, sort, sound, source, south, southern, + space, speak, special, specific, speech, spend, sport, spring, staff, stage, + stand, standard, star, start, state, statement, station, stay, step, still, + stock, stop, store, story, strategy, street, strong, structure, student, study, + stuff, style, subject, success, successful, such, suddenly, suffer, suggest, + summer, support, sure, surface, system, table, take, talk, task, tax, teach, + teacher, team, technology, television, tell, ten, tend, term, test, than, thank, + that, the, their, them, themselves, then, theory, there, these, they, thing, + think, third, this, those, though, thought, thousand, threat, three, through, + throughout, throw, thus, time, to, today, together, tonight, too, top, total, + tough, toward, town, trade, traditional, training, travel, treat, treatment, + tree, trial, trip, trouble, true, truth, try, turn, TV, two, type, under, + understand, unit, until, up, upon, us, use, usually, value, various, very, + victim, view, violence, visit, voice, vote, wait, walk, wall, want, war, watch, + water, way, we, weapon, wear, week, weight, well, west, western, what, whatever, + when, where, whether, which, while, white, who, whole, whom, whose, why, wide, + wife, will, win, wind, window, wish, with, within, without, woman, wonder, word, + work, worker, world, worry, would, write, writer, wrong, yard, yeah, year, yes, + yet, you, young, your, yourself""" + +# Source: https://www.101languages.net/hebrew/most-common-hebrew-words/ +alias words_he = """ +לא , את , אני , זה , אתה , + מה , הוא , לי, על, כן, לך, של, יש , בסדר , אבל , כל , שלי , טוב , עם, היא, אם, רוצה, + שלך, היה, אנחנו, הם, אותך, יודע, אז, רק, אותו, יכול, אותי, יותר, הזה, אל, כאן, או, + למה, שאני, כך, אחד, עכשיו, משהו, להיות, היי, תודה, כמו, אין, זאת, איך, נכון, מי, שם, + לו, צריך, לעשות, קדימה, לנו, חושב, כמה, שאתה, זו, גם, יודעת, אותה, עוד, באמת, הייתי, + שהוא, אולי, בבקשה, עושה, פשוט, שזה, דבר, מאוד, כבר, שלא, נראה, לעזאזל, אתם, כדי, ואני, + פה, אלוהים, הנה, פעם, האם, בוא, שלו, איפה, הרבה, כי, יכולה, שלנו, אומר, יהיה, אותם, עד, + קצת, לפני, זמן, הכל, ממש, אבא, הולך, מר, אדוני, לראות, ובכן, מישהו, חייב, עדיין, לה, + אף, בכל, בדיוק, היום, אנשים, ללכת, מצטער, היית, שלום, קרה, שוב, אוהב, אחת, הייתה, אמא, + חשבתי, בן, איזה, יום, לדבר, תמיד, צריכה, לזה, חושבת, להם, היו, שאת, שיש, רואה, אפילו, + בטח, כולם, בגלל, שום, שהיא, אחר, חבר, בשביל, קורה, איתך, הזאת, אמרתי, אדם, תן, צריכים, + הזמן, יכולים, ואז, כלום, רגע, האלה, אחרי, מבין, תראה, בטוח, שהם, לכם, בו, אמר, מדי, + ג, טובה, אותנו, תהיה, אנו, אך, ככה, בזה, יפה, כזה, אוכל, מותק, מספיק, בואו, אפשר, שלה, + דברים, הכי, מזה, מקום, בואי, לכאן, אה, בבית, דרך, איתי, מתכוון, ביותר, הביתה, לומר, אחי, + מת, הזו, ה, הדבר, מדבר, שאנחנו, לעזור, לעולם, זהו, לדעת, כאילו, גדול, אוהבת, שנים, בי, + מכאן, יודעים, לקחת, ראיתי, בלי, נהדר, די, כסף, הו, היתה, מהר, עליי, י, מצטערת, וזה, ילד, + לשם, קשה, חכה, לאן, ואתה, ממני, ו, תגיד, רוצים, שני, הלילה, עליך, כמובן, עליו, נחמד, לכל, + להגיד, סליחה, אמרת, ל, מוכן, מחר, בא, ולא, והוא, אוקיי, אומרת, גברת, בך, נלך, בית, מעולם, + שלהם, אי, הבחור, עבודה, למצוא, נמצא, חייבים, מכיר, מנסה, ב, ואת, מתי, תעשה, בשבילך, מספר, + כדאי, דקות, שלכם, האמת, עושים, אלה, חייבת, דולר, הכסף, כעת, לילה, איש, עלי, לצאת, רציתי, + לתת, בחור, בכלל, איתו, רע, עשית, מרגיש, הכול, בעיה, עבור, אמור, לקבל, עובד, בנאדם, הולכים, + החיים, נוכל, מאמין, סוף, ידעתי, הולכת, לב, בחייך, היכן, שנה, זוכר, ממך, הגיע, קטן, החוצה, + תוכל, בזמן, הן, ילדים, נשמע, חיים, בדרך, אכפת, נעשה, הבית, ש, ידי, בוקר, עשה, לחזור, המקום, + הבא, מקווה, קח, עשיתי, חשוב, הי, אלו, בחיים, גבר, ללא, במקום, משנה, הלו, להרוג, שמעתי, העולם, + ספר, ר, זונה, עצמך, האנשים, למעלה, בני, לגבי, מאוחר, כמעט, תראי, לספר, בקשר, שמח, להתראות, + לבד, הדרך, האלו, שלוש, יופי, לגמרי, מדוע, אליך, מפה, קודם, ראית, למטה, בה, להגיע, חלק, מגיע, + מ, בין, לבוא, אתן, היינו, חרא, הדברים, תפסיק, אחרת, לשמוע, מזל, המפקד, בחוץ, אהיה, הספר, אליי, + ערב, תקשיב, אישה, השני, לחשוב, הערב, מבינה, מיד, בשבילי, למעשה, אוי, הראשון, אלי, תני, א, + חברים, בטוחה, רבה, ומה, מאז, ביום, במשך, בהחלט, עלינו, ון, לעבוד, השם, כולנו, לאחר, הראשונה, + למען, מניח, מוזר, בתוך, איזו, חזק, העבודה, מהם, לפחות, אמרה, האיש, ביחד, כנראה, שיהיה, בת, + אלא, הבעיה, כאשר, ימים, ואם, אימא, קטנה, ברגע, אתכם, אעשה, איני, הדלת, משחק, חדש, בעוד, סתם, + לבית, בחזרה, לאכול, להביא, אתמול, נורא, תראו, אדירים, יחד, גדולה, בעולם, חתיכת, לפעמים, מקרה, + בפנים, נו, עומד, ברור, אבי, הפעם, ממנו, שעות, שלומך, בלילה, אומרים, מתכוונת, אינך, הילד, כרגע, + האחרון, ביי, הא, טובים, העיר, חיי, הילדים, נראית, חכי, יהיו, למות, מצחיק, חוץ, תורגם, שהיה, + חזרה, מאד, הראש, רעיון, הרגע, אהבה, לשאול, להישאר, שאתם, יקרה, מושג, בלתי, איתה, להשיג, ראש, + יכולתי, תחת, עצמי, מכל, קוראים, אליו, שמעת, כ, להיכנס, אמיתי, הבן, תסתכל, כלומר, חברה, עליה, + והיא, ילדה, לשחק, העניין, ועכשיו, קשר, הגדול, לעזוב, החבר, נפלא, האחרונה, חמש, בפעם, עצור, כפי, + לישון, שתי, צודק, וגם, שלושה, ליד, לחיות, קרוב, רב, נגמר, לקרוא, שאין, תא, תדאג, יוצא, האדם, + והם, גמור, בבוקר, קל, מתה, באופן, בשם, מעט, הבאה, יורק, מוכנה, היחיד, לכן, תגידי, חי, חצי, איי, + לוקח, נעים, נהיה, לעצור, גרוע, לפגוש, נשים, שעה, נגד, הטוב, מים, חושבים, ממה, פנימה, מרגישה, + לפה, שתיים, וכל, אסור, פנים, שכל, מתחיל, אשר, אותן, היחידה, שומע, כמוך, בקרוב, לנצח, המכונית, + מחוץ, בחורה, חוזר, להבין, הבנתי, עלייך, לעבור, בערך, פעמים, בחדר, וואו, כאלה, קיבלתי, לכי, חודשים, + אלך, ארוחת, סוג, מדברת, מכירה, מאמינה, כה, זקוק, הקטן, אידיוט, מדהים, מצאתי, הסיבה, אינני, בנות, + בתור, לשמור, החברה, להמשיך, התחת, כשאני, שמי, נתראה, ימי, הסיפור, שכן, סיפור, בצורה, אקח, סלח, + לזוז, רציני, הכבוד, שמישהו, פחות, מדברים, שאלה, סיכוי, מתחת, אחרים, הללו, נכנס, תביא, התכוונתי, + שהייתי, הבוקר, ראשון, באותו, בחורים, בהם, לנסות, להשתמש, אדון, במה, שווה, זוכרת, טיפש, מיליון, + מוכנים, להתחיל, להראות, אנג, אראה, מלא, המשפחה, לפי, מאשר, פרנק, מטורף, לכך, שבוע, מגניב, צא, + ואנחנו, לתוך, לחכות, מאיפה, איתנו, מחדש, דעתך, שונה, חסר, תוך, נותן, לקנות, להכיר, הבחורה, + ינא, מחפש, שבו, ישר, חדשות, חדר, אש, אפשרי, מצוין, הלא, ים, מתוקה, עזרה, שאם, מעולה, כדור, + תירגע, אמרו, באה, החדש, בעיות, שאנו, המצב, הלך, לשתות, מעבר, מעל, טעות, כשאתה, עבר, עליהם, + נשאר, ויש, שב, מייקל, אלף, לקח, ארבע, סיבה, מצב, מן, מסוגל, מידי, להרגיש, בעל, משפחה, שזו, + שוטר, בחיי, מעניין, ההוא, קפה, הזדמנות, כלב, כלל, מקבל, שונא, מחכה, מפני, זין, תחזור, שקט, + באתי, מוצא, אביך, ניסיתי, תקשיבי, חן, מצוות, רוח, מוקדם, קפטן, תהיי, מאיתנו, מבטיח, מושלם, + ידעת, עניין, כוח, המון, פי, חולה, אוהבים, אינו, דם, הנשיא, משם, למרות, גורם, לאט, כבוד, ס, + בעבר, להתקשר, אלייך, משוגע, עשר, ללמוד, שטויות, בנוגע, צוחק, לבדוק, בצד, להאמין, חדשה, עצמו, + לגרום, המשחק, שרה, לעצמך, במיוחד, המשטרה, צוות, אחזור, שאמרתי, גברים, קורא, בראש, רחוק, + למקום, לשלם, להפסיק, מיוחד, הז, שמו, שמחה, כיף, אגיד, למי, ניתן, מאחורי, תמשיך, כיצד, + להוציא, מתים, כולכם, אצל, חבל, האישה, לעצמי, גברתי, תוכלי, רואים, דוד, להציל, שצריך, + בעלי, דוקטור, חג, לעבודה, בוודאי, תעשי, הוד, מילה, ברצינות, הארץ, עשינו, לאנשים, רצה, + עזוב, יצא, נתן, + שניות, בעיר, סי, חשבת, שאלות, אלינו, ידע, תנו, לשים, שאולי, בכך, יכולת, אן, היד, שאוכל, + מין, דקה, לדאוג, שמה, תרצה, ראה, הצילו, נוסף, החרא, אופן, כשהוא, צעיר, הפה, עולה, עובדת, + שמך, לתפוס, נמצאת, כלבה, האקדח, עדיף, הטלפון, טום, פול, חכו, קר, תלך, במקרה, יעשה, שניכם, + הארי, זוז, יקירתי, בהצלחה, לשבת, אנא, דין, מכיוון, יד, הקטנה, לבן, בנו, בעצמי, יין, תוריד, + למישהו, מייק, מול, נזוז, ככל, הלוואי, בעצמך, לרגע, קשור, בשקט, האל, ישנה, מעמד, כזאת, + רד, אחורה, איכפת, איתם, ממנה, חם, מבקש, שש, מידע, השנה, + אכן, אהבתי, בשעה, בסוף, שקרה, לכו, אליה, לבחור, תחשוב, ספק, המים, הפנים, לכולם, תדאגי, + קחי, שתוק, לברוח, מתוק, ארלי, התיק, שים, מישהי, לקרות, לטפל, לחפש, הידיים, ח, במצב, ואל +""" + +# Source: https://www.101languages.net/latvian/most-common-latvian-words/ +alias words_lv = """ + ir, es, un, tu, tas, ka, man, to, vai, ko, ar, kas, par, tā, kā, viņš, uz, no, tev, + mēs, nav, jūs, bet, labi, jā, lai, nē, mani, ja, bija, viņa, esmu, viņu, tevi, esi, + mums, tad, tikai, ne, viņi, kad, jums, arī, viss, nu, kur, pie, jau, tik, tur, te, vēl, + būs, visu, šeit, tagad, kaut, ļoti, pēc, viņam, taču, savu, gan, paldies, būtu, mūsu, + šo, lūdzu, mans, kāpēc, kungs, kāds, varbūt, tās, jūsu, cik, ak, daudz, jo, esam, + zinu, mana, zini, visi, būt, tam, šī, var, līdz, viens, pa, pat, esat, nekad, domāju, + nezinu, vairs, tiešām, tie, vien, kurš, varētu, dievs, neesmu, prom, tieši, kādu, aiziet, + šis, manu, protams, vajag, neko, vienkārši, tāpēc, gribu, varu, nāc, atpakaļ, mūs, + kārtībā, iet, kopā, viņiem, pats, pirms, domā, vienmēr, gribi, nekas, bez, tava, + vienu, ej, viņai, vairāk, notiek, nevaru, pret, tavs, teica, tavu, biju, dēļ, viņas, + laiku, neviens, kādēļ, vari, labāk, patīk, dari, mājās, nebija, cilvēki, ārā, viņus, + ejam, kāda, piedod, laikam, atkal, šķiet, trīs, sevi, ser, laiks, laika, nekā, manis, + iekšā, labs, tāds, darīt, harij, nevar, viena, lieliski, kuru, šīs, sauc, šurp, teicu, + laikā, tos, pagaidi, neesi, tevis, draugs, pārāk, tēvs, šodien, teikt, dienu, visiem, + tātad, notika, hei, zināt, bijis, sveiks, atvainojiet, tika, naudu, varam, savas, citu, + tādu, manas, redzi, šajā, kam, tajā, jābūt, vecīt, tiem, runā, cilvēku, taisnība, saka, + visus, mīlu, lietas, grib, tēt, izskatās, tiek, noteikti, nozīmē, kamēr, divi, it, tāpat, + tāda, ilgi, katru, dēls, noticis, jauki, redzēt, pareizi, lūk, kundze, aiz, iespējams, + pateikt, nebūtu, gandrīz, vīrs, cilvēks, ātri, žēl, pasaules, rokas, liekas, palīdzēt, + līdzi, visas, saki, negribu, vietā, gadus, starp, skaties, tomēr, tūlīt, džek, nevajag, + sev, vajadzētu, būšu, dzīvi, droši, gadu, priekšu, skaidrs, gribēju, nāk, paskaties, mazliet, + tikko, nebūs, augšā, ceru, joprojām, nevis, ātrāk, ļauj, gribētu, liels, zina, vārdu, reizi, + pasaulē, savā, sveiki, dienas, miris, dod, priekšā, galā, klau, cilvēkiem, tavas, patiesībā, + visa, vārds, gatavs, durvis, velns, nedaudz, naudas, redzēju, velna, manā, drīz, pāri, dzīve, + vēlies, nemaz, priekš, bērni, vieta, pāris, darbu, vajadzīgs, tālāk, rīt, roku, klāt, grūti, + beidz, laba, klausies, dara, varat, sveika, biji, vismaz, kopš, redzu, saproti, kura, draugi, + zemes, šovakar, patiešām, kaa, vietu, dieva, vajadzēja, mašīnu, lejā, saku, ceļu, gada, tādēļ, + cauri, runāt, ņem, oh, divas, lieta, tikt, šie, teici, vēlāk, vaļā, nogalināt, redzējis, jāiet, + nespēju, savus, atceries, ūdens, šejienes, labu, diena, mīļā, atvaino, doties, atrast, saprotu, + abi, reiz, jādara, nesaprotu, meitene, darbs, nevari, tai, nedomāju, pilnīgi, nakti, nekādu, + pati, gadiem, vēlos, taa, kādas, cits, ejiet, pirmais, a, būsi, mamma, lietu, slikti, pašu, + acis, diezgan, pasaki, gadā, puiši, asv, sava, nost, cilvēkus, džeks, manuprāt, mājas, o, + bērns, leo, otru, nopietni, vecais, laukā, caur, dzīves, izdarīt, sieviete, vienalga, + nogalināja, dzīvo, kādreiz, čau, sirds, paliec, gribat, vēlreiz, kuras, mazais, vietas, + piedodiet, laipni, palikt, brauc, ei, the, paliek, apkārt, sievietes, tālu, garām, pirmo, + dzīvot, nāciet, runāju, kuri, tiks, jüs, ceļā, nauda, nevienam, māja, vienīgais, īsti, + sapratu, gluži, svarīgi, atvainojos, i, sen, iespēja, tavā, pavisam, nāves, māte, citi, + viegli, zem, notiks, darba, nepatīk, daži, galvu, dienā, hallo, bērnu, neesam, kungi, beidzot, + nedrīkst, vajadzēs, māju, sieva, kādam, puika, kļūst, prieks, esot, iesim, daļa, pasaule, + pietiek, visā, saviem, rīta, pagaidiet, tētis, mājā, mieru, vīru, palīdzību, dzirdēju, + tādas, dzīvs, strādā, tām, vēlas, nakts, īpaši, jūtos, nolādēts, meitenes, pusi, mammu, mees, + aizveries, vispār, dzīvību, kurā, kādā, vārdā, mašīna, būsim, vispirms, vinji, nevienu, šos, + tiksimies, džeik, vinjsh, vaina, turpini, kādi, jaunu, tuvu, atradu, vēlu, varēja, citādi, šim, + satikt, neuztraucies, pārliecināts, liec, diez, liela, doktor, nevaram, palīdzi, uzmanīgi, dažas, + šiem, atgriezies, gribēja, priecājos, parasti, valsts, asinis, tēti, you, mierā, piemēram, + jautājums, atā, bijām, zemē, pasauli, spēlē, blakus, izskaties, pirmā, nomira, paši, šobrīd, + daru, gaida, tādi, iešu, labākais, jauks, maz, pieder, jauns, nezināju, uzmanību, skaista, + prātā, brālis, patiesību, mierīgi, šai, dr, patiesi, jēzus, mārtij, zināju, suns, juus, sievu, + dzirdi, tepat, mamm, tēvu, tēva, frodo, sasodīts, desmit, stundas, tavi, mazā, džon, cita, + vajadzīga, forši, minūtes, mīlestība, nebiju, saprast, izbeidz, šoreiz, labā, dāmas, kurienes, + problēma, šādi, spēj, gadījumā, tiesa, kuģi, pēdējā, tici, esiet, atceros, katrs, nee, palīgā, + mister, liek, likās, domāt, vīri, pēdējo, traks, reizes, vienīgā, tiesības, skolā, turies, beigas, + karš, pīter, uguni, pietiks, vienam, vienā, pakaļ, jauna, zemi, puisis, ziniet, negribi, labrīt, + ap, cilvēka, draugu, atver, nezini, sāra, vēlaties, gadi, dažreiz, rokās, dabūt, nomierinies, + istabā, agrāk, ieroci, savām, meiteni, paņem, meklē, pār, seju, ziņu, dzirdējis, zinām, gatavi, + braukt, sāka, sāk, dievam, neesat, dzirdēt, spēle, bērniem, izdarīja, muļķības, doma, pēdējais, + dīvaini, atdod, ziņas, bankas, darāt, vakar, ceļš, neviena, brāli, otrā, atgriezties, galvas, + pietiekami, gulēt, uzreiz, iespēju, bijusi, karalis, bobij, šrek, tikpat, palīdziet, durvīm, + vecāki, atrodas, smieklīgi, kuģa, bail, godīgi, pēkšņi, nedēļas, māsa, skrien, ceļa, džeims, gars, + lielu, mašīnā, bojā, kurieni, ļaudis, dārgais, vecs, ūdeni, kūper, eju, mašīnas, ideja, kājas, + spēles, galvenais, citiem, jātiek, skaisti, nāvi, vinju, problēmas, vērts, drīkstu, domājat, visur, + bieži, manai, citas, apsolu, zelta, strādāju, dzimšanas, jūtu, naktī, dārgā, atbildi, noticēt, + klājas, izdevās, dok, redzat, gana, divus, ģimene, runa, stāsts, braucam, brīnišķīgi, ģimenes, + kuģis, čārlij, hey, kä, sheit, ved, atrada, mirusi, meita, paklau, nevēlos, bērnus, boss, kaptein, + nekāda, roze, nespēj, vīrietis, brīdi, īsts, dzīvē, tādā, manī, jūras, jaunkundz, iemesls, sakot, + manam, daudzi, varēsi, pateicos, jaunais, policija, pilnībā, nekur, jauka, nedari, kurus, zināms, + jautājumu, seko, re, padomā, pusē, visām, mīļais, dolāru, gadžet, katram, izdarīji, šīm, vienīgi, + mirt, apmēram, spēku, jauno, mr, celies, iepriekš, prātu, vēlētos, četri, lietām, redzēji, nevajadzētu, + donna, jaa, ticu, minūtēm, sievieti, nāve, jūties, nezina, parādi, malā, redz, uh, gredzenu, uzmanies, + kara, drošībā, sapnis, bijāt, grāmatu, slepkava, vinja, paga, pieci, pilsētā, drošs, pateikšu, gāja, + spēli, beigās, hanna, princese, jebkad, dakter, veids, palīdzība, stāstu, izmantot, spēlēt, gaisā, + darīšu, došos, dodas, kreisi, negribēju, mazāk, pastāsti, tak, devās, sirdi, misis, vis, patiesība, + veidā, harijs, cenšos, tuvāk, kurp, klausieties, sāp, ļaujiet, neticami, kungu, sīkais, iedomāties, + daļu, mazs, iedod, mazo, meklēju, parunāt, jādodas, sevis, pārējie, veicas, otra, mīlestību, zēns, + dodies, galam, sem, bīstami, zvēru, iespējas, maza, ellē, virs, nekādas, maniem, skatieties, šonakt, + svēto, kapteinis, iepazīties, pazīstu, turp, gredzens, nepareizi, lieliska, īstais, pagaidām, kājām, + mirklīti, pašlaik, d, poter, saprati, aprunāties, paša, šejieni, interesanti, nevarētu, pašā, paskat, + bailes, skolas, vārdus, aizmirsti, gaismas, kāp, zēni, darīsim, pašam, beidzies, sauca, māti, akmens, + grāmatas, diemžēl, tevī, kļūt, endij, patika, nabaga, tuvojas, tēvoci, dienām, plāns +""" + +# Source: https://www.101languages.net/polish/most-common-polish-words/ +alias words_pl = """ +nie, to, się, w, na, i, z, co, jest, że, do, tak, jak, o, mnie, a, ale, mi, za, ja, ci, tu, ty, czy, +tym, go, tego, tylko, jestem, po, cię, ma, już, mam, jesteś, może, pan, dla, coś, dobrze, wiem, jeśli, +teraz, proszę, od, wszystko, tam, więc, masz, nic, on, być, gdzie, będzie, są, ten, mogę, ciebie, +bardzo, sobie, kiedy, ze, wiesz, no, jej, jeszcze, pani, był, mój, chcę, było, dlaczego, by, przez, +nas, tutaj, chcesz, jego, ją, ich, nigdy, żeby, też, kto, naprawdę, przepraszam, bo, mamy, porządku, +możesz, dobra, mu, dziękuję, ona, domu, panie, muszę, nawet, chyba, hej, właśnie, prawda, zrobić, te, +zawsze, będę, moja, gdy, je, trochę, nam, moje, cześć, bez, nim, była, tej, jesteśmy, dalej, pana, +dzięki, wszyscy, musisz, twój, lat, tobą, więcej, ktoś, czas, ta, który, chce, powiedzieć, chodź, dobry, +mną, niech, sam, razem, chodzi, czego, boże, stało, musimy, raz, albo, prostu, będziesz, dzień, możemy, +was, myślę, czym, daj, lepiej, czemu, ludzie, ok, przed, życie, ludzi, robisz, my, niż, tych, kim, rzeczy, +myślisz, powiedz, przy, twoja, oni, oczywiście, nikt, siebie, stąd, niego, twoje, miał, jeden, mówi, +powiedział, moim, czasu, u, dziś, im, które, musi, wtedy, taki, aby, pod, dwa, temu, pewnie, takie, cóż, +wszystkie, mojego, dużo, cholera, kurwa, wie, znaczy, wygląda, dzieje, mieć, ile, iść, potem, będziemy, +dzieci, dlatego, cały, byłem, moją, skąd, szybko, jako, kochanie, stary, trzeba, miejsce, myśli, można, +sie, jasne, mojej, wam, swoje, zaraz, wiele, nią, rozumiem, nich, wszystkich, jakieś, jakiś, kocham, idź, +tę, mają, mówię, mówisz, dzisiaj, nad, pomóc, takiego, przestań, tobie, jutro, robić, jaki, mamo, kilka, +przykro, wiedzieć, ojciec, widzisz, zbyt, zobaczyć, która, ani, tyle, trzy, tą, sposób, miałem, tato, niej, +później, pieniądze, robi, kogoś, kiedyś, zanim, widzę, pracy, świetnie, pewno, myślałem, będą, bardziej, +życia, długo, och, sir, ponieważ, aż, dni, nocy, każdy, dnia, znowu, oh, chciałem, taka, swoją, twoim, +widziałem, stanie, powiem, imię, wy, żebyś, nadzieję, twojej, panu, spokój, słuchaj, rację, spójrz, razie, +znam, pierwszy, koniec, chciałbym, we, nami, jakie, posłuchaj, problem, przecież, dobre, nasz, dziecko, drzwi, +nasze, miło, czuję, mógł, żyje, jeżeli, człowiek, powiedziałem, gdyby, roku, dom, sama, potrzebuję, +wszystkim, zostać, wciąż, dokładnie, mama, którzy, mówić, zamknij, mów, twoją, chwilę, zrobił, samo, idziemy, +nadal, jesteście, zabić, były, sobą, kogo, lub, lubię, the, podoba, minut, bym, chciał, bądź, czegoś, gdzieś, +mówiłem, chodźmy, znaleźć, poza, spokojnie, wcześniej, został, rozumiesz, mogą, prawie, wydaje, miała, mały, +byłeś, facet, zrobię, macie, żadnych, razy, noc, ciągle, broń, moich, twojego, końcu, pomocy, czekaj, znasz, +oczy, weź, idę, halo, dość, innego, pomysł, jakby, trzymaj, jedno, ojca, porozmawiać, pamiętasz, lata, +powinieneś, którą, powodu, takim, niczego, powinniśmy, oto, napisy, jednak, świat, pokoju, żebym, sprawy, +dwie, samochód, swój, wystarczy, pewien, źle, pozwól, numer, jedną, miejscu, you, drogi, byłam, dokąd, miłość, +panowie, pieniędzy, którego, matka, rano, dwóch, całe, patrz, rzecz, nowy, idzie, wyglądasz, bóg, byś, życiu, +nimi, nikogo, całą, swojego, świecie, sprawa, dziewczyna, prawo, byli, zostaw, wiedziałem, jedna, widzieć, +swoim, kobiety, uważaj, najpierw, właściwie, dam, również, diabła, chcą, którym, zrób, da, jednego, dać, +musiał, ręce, powinienem, których, znów, powiedziała, wczoraj, czujesz, zaczekaj, sądzę, śmierć, mówił, +podczas, której, całkiem, pracę, żona, pójdę, pamiętam, powiedziałeś, mówią, wiemy, jezu, witam, cholery, +swoich, telefon, wielu, także, poważnie, skoro, miejsca, robię, śmierci, słyszałem, wina, zrobiłem, dobranoc, +parę, prawdę, swojej, serce, inaczej, dziewczyny, kobieta, powiesz, martw, rób, pytanie, pięć, innych, one, +gra, natychmiast, wrócić, szybciej, jednym, cokolwiek, wierzę, wcale, wieczór, ważne, człowieka, wielki, nowa, +dopiero, ziemi, gdybym, tata, poznać, stać, jack, myślałam, witaj, słowa, zrobiłeś, gówno, john, dolarów, +sprawę, inne, idziesz, miałam, wiecie, chciałam, zobaczenia, widziałeś, żyć, każdym, nasza, panią, wspaniale, +chwili, każdego, nowego, nieźle, takich, między, dostać, powinien, dawaj, dopóki, naszych, naszej, świata, +chłopaki, chcemy, poczekaj, jaką, człowieku, czasem, żadnego, inny, przynajmniej, nazywa, super, naszego, +szczęście, potrzebuje, godziny, zabrać, powrotem, syn, lecz, słucham, twoich, udało, boga, pokój, działa, +ogóle, naszym, szkoły, możliwe, wiedział, wyjść, wszystkiego, byłoby, daleko, wieczorem, skarbie, jaka, +mógłbym, ostatni, możecie, cztery, doktorze, zrobimy, mąż, przeciwko, zgadza, zrobisz, czasie, czasami, +brzmi, raczej, ciało, należy, miasta, miałeś, taką, brat, cieszę, rozmawiać, cała, czymś, wybacz, twarz, +mała, chcecie, dr, pojęcia, lubisz, głowę, najbardziej, dziwne, głowy, wody, pół, wiadomość, policja, +strony, l, pl, mogłem, mieli, widzenia, pewna, ruszaj, wracaj, ode, popatrz, końca, plan, kiedykolwiek, +wejść, została, rok, syna, uda, wrócę, zewnątrz, droga, uwierzyć, późno, zostało, zostanie, zły, kapitanie, +potrzebujemy, byliśmy, zobaczymy, gotowy, obchodzi, jechać, rodziny, widziałam, drodze, czeka, środku, film, +spać, człowiekiem, zupełnie, taa, pomóż, mieliśmy, pomoc, słowo, innym, ostatnio, and, zna, mogła, pójść, +chłopcy, wziąć, mógłbyś, tłumaczenie, potrzebujesz, słyszysz, blisko, godzin, miłości, góry, zabił, piękna, +napisów, pokaż, moi, lubi, robota, prawa, ciężko, kimś, dół, rękę, nazywam, wielkie, część, wkrótce, naszą, +jedziemy, zapomnij, prosto, radę, robimy, powinnaś, gdybyś, chociaż, zależy, stronie, wypadek, tydzień, byłaś, +nowe, małe, praca, drogę, chłopak, zrobi, widział, mieście, synu, oznacza, krew, mógłby, krwi, górę, joe, wasza, +robią, tędy, wszędzie, temat, pierwsze, zobacz, ponad, kraju, mało, racja, tymi, cicho, chciała, powiedziałam, +leci, powinno, mówiąc, serca, chciałabym, miasto, george, spotkać, mniej, e, przyjaciel, mówiłeś, kłopoty, +miesięcy, jakąś, żaden, zostań, roboty, zatrzymać, frank, nieważne, głupi, pa, koleś, sprawie, spotkanie, ojcze, +pewnego, spróbuj, drugi, znalazłem, pracować, całym, zostały, złe, niemożliwe, jakoś, zdjęcia, stronę, wiedzą, it, +dziewczynę, zaczyna, mogli, samego, sądzisz, rodzina, razu, trudno, samochodu, okay, boję, szkoda, wami, charlie, +dał, środka, ojcem, piękne, dawno, choć, panem, przykład, nagle, bracie, żadnej, drugiej, przyjaciół, otwórz, +myśleć, doktor, chwileczkę, pracuje, najlepszy, brata, czyż, często, http, powinnam, odejść, trzech, chodźcie, +nazwisko, szansę, ciała, policji, szkole, prawdopodobnie, serio, matki, org, wolno, sami, muszą, zabierz, +słyszałeś, siostra, uspokój, wystarczająco, początku, faceta, problemy, szefie, broni, me, zostawić, czuje, +będziecie, przyszedł, wiedziałam, kilku, inni, b, głowie, historia, według, www, wezmę, nowym, czekać, stój, +mężczyzna, mówiłam, pokazać, około, wracam, wieku, jakaś, pierwsza, niczym, zabiję, zdjęcie, zabawne, rodzice, +musiałem, całkowicie, sprawdzić, mike, przyjdzie, sześć, kupić, dobrym, żonę, dasz, pomoże, nogi, obok, ruszać, +trzymać, zadzwonić, panno, godzinę, boli, oraz, spokoju, walczyć, wróci, tom, wspólnego, zmienić, ostatnie, uwagę, +znać, jednej, dłużej, powie, pogadać, łatwo, większość, nikomu, michael, córka, niedługo, powodzenia, tygodniu, +włosy, niestety, górze, kochasz, prawdziwy, historii, ulicy, musicie, gotowi, chwila, samym, grać, zadzwonię, +strasznie, mieszka, kocha, rady, tyłu, jakim, obiecuję, tysięcy, pomyślałem, pracuję, jedynie, pozwolić, uwaga, +proste, zacząć, myśl, wstawaj, rany, prawdziwe, takiej, jakiegoś, umrzeć, złego, okazji +""" + +# Source: https://www.101languages.net/greek/most-common-greek-words/ +alias words_el = """ + να, το, δεν, θα, είναι, και, μου, με, ο, για, την, σου, τα, τον, η, τι, σε, που, του, αυτό, στο, ότι, + από, τη, της, ναι, σας, ένα, εδώ, τους, αν, όχι, μια, μας, είσαι, αλλά, κι, οι, πρέπει, είμαι, ήταν, + πολύ, στην, δε, γιατί, εγώ, τώρα, πως, εντάξει, τις, κάτι, ξέρω, μην, έχει, έχω, εσύ, θέλω, καλά, + έτσι, στη, στον, αυτή, ξέρεις, κάνεις, εκεί, σαν, μόνο, μπορώ, όταν, έχεις, μαζί, πώς, τίποτα, + ευχαριστώ, όλα, κάνω, πάμε, ή, ποτέ, τόσο, πού, αυτά, έλα, στα, μέσα, κάνει, των, μπορεί, κύριε, πιο, + σπίτι, παρακαλώ, λοιπόν, μπορείς, αυτός, υπάρχει, ακόμα, πίσω, λίγο, πάντα, είμαστε, γεια, τότε, + ειναι, μετά, πω, έχουμε, μη, ένας, ποιος, νομίζω, πριν, απλά, δω, δουλειά, παιδιά, οχι, αλήθεια, + όλοι, ίσως, λες, όπως, ας, θέλεις, μα, άλλο, είπε, ζωή, πάω, δύο, ωραία, έναν, καλό, απο, κάνουμε, + έξω, κοίτα, είχε, στις, πάνω, είπα, πες, χρόνια, ούτε, κάτω, είστε, ώρα, θες, σένα, έχουν, γυναίκα, + μένα, μέρα, καλή, φορά, όμως, κανείς, κάθε, ε, οτι, αρέσει, ήμουν, μέχρι, δυο, είχα, μαμά, χωρίς, + καλύτερα, πας, πράγματα, πάει, σήμερα, κάποιος, ήθελα, θέλει, θεέ, έπρεπε, λέει, μία, σωστά, αυτόν, + μπορούμε, συμβαίνει, ακριβώς, έγινε, πόσο, επειδή, λεφτά, πολλά, μόλις, εμένα, λένε, πεις, συγγνώμη, + γρήγορα, ω, έκανε, λυπάμαι, γίνει, παιδί, περίμενε, έκανα, φίλε, βλέπω, μέρος, στιγμή, φαίνεται, + πρόβλημα, άλλη, είπες, φυσικά, κάποιον, όσο, πήγαινε, πάλι, λάθος, ως, έχετε, εσένα, πράγμα, κυρία, + χρόνο, στους, πάρω, μπαμπά, δικό, απ, γίνεται, εσείς, λέω, συγνώμη, όλο, μητέρα, έκανες, πιστεύω, + ήσουν, κάποια, σίγουρα, υπάρχουν, όλη, ενα, αυτο, ξέρει, μωρό, ιδέα, δει, μάλλον, ίδιο, πάρε, είδα, + αύριο, βλέπεις, νέα, κόσμο, νομίζεις, τί, εμείς, σταμάτα, πάρει, αγάπη, πατέρας, όλους, αρκετά, + χρειάζεται, καιρό, φορές, κάνουν, ακόμη, α, πατέρα, προς, αμέσως, πια, ηταν, χαρά, απόψε, όνομα, + μάλιστα, μόνος, μεγάλη, κανένα, ελα, πραγματικά, αυτοί, πει, πότε, εχω, βράδυ, αυτές, θέλετε, κάνετε, + σημαίνει, πρώτη, ποιο, πόλη, μπορούσα, ποια, γαμώτο, ήδη, τελευταία, άνθρωποι, τέλος, απλώς, νόμιζα, + ξέρετε, μέρες, δεις, θέση, αυτούς, καταλαβαίνω, φύγε, χέρια, εκτός, ήξερα, οπότε, λεπτά, μακριά, + κάνε, αμάξι, δική, λεπτό, μεγάλο, μήπως, κορίτσι, μάτια, ελάτε, πρόκειται, πόρτα, δίκιο, βοήθεια, + ήρθε, μιλήσω, δρόμο, εαυτό, καθόλου, ορίστε, βρω, πειράζει, μπορείτε, καλός, πέρα, κοντά, εννοώ, + τέτοιο, μπροστά, έρθει, χρειάζομαι, χέρι, ελπίζω, δώσε, διάολο, φύγω, ιστορία, όπλο, αφού, πρωί, + νύχτα, ωραίο, τύπος, ξανά, θυμάσαι, δούμε, κατά, εννοείς, αγαπώ, κακό, θέμα, εδω, αυτήν, τρόπο, + κεφάλι, είχες, μερικές, μιλάς, φίλος, άνθρωπος, φύγουμε, όλες, σκατά, ανθρώπους, βέβαια, άντρας, + κάποιο, πάνε, αστυνομία, αλλιώς, συνέβη, χαίρομαι, άλλα, περισσότερο, καλύτερο, εκείνη, πάρεις, τo, + νερό, ώρες, σίγουρος, vα, τρεις, εχεις, πρώτα, μπορούσε, σ, οταν, δρ, πιστεύεις, μόνη, ποιός, καμιά, + κανέναν, πέθανε, εχει, ετσι, αγόρι, ανησυχείς, άντρες, δωμάτιο, ομάδα, ίδια, εμπρός, βρούμε, βοηθήσω, + τέτοια, πήρε, τρία, λόγο, μικρό, αντίο, o, πέντε, πήγε, καν, ευκαιρία, είδες, έρχεται, δηλαδή, + αργότερα, ήθελε, πούμε, λέμε, όπου, αλλα, κόρη, κόσμος, γυναίκες, τηλέφωνο, εάν, δώσω, καρδιά, βρήκα, + γραφείο, επίσης, νιώθω, σχέση, θέλουν, ισως, τέλεια, είχαμε, κάπου, μυαλό, ώστε, καλημέρα, σχολείο, + θεός, μικρή, τρέχει, ψέματα, ξέρουμε, οικογένεια, εισαι, θυμάμαι, κ, ενός, φίλοι, πρόσεχε, + καταλαβαίνεις, αργά, ντε, θέλουμε, σύντομα, πήρα, σχεδόν, παιχνίδι, κύριοι, γειά, μήνες, μπαμπάς, + σοβαρά, δολάρια, τουλάχιστον, χρήματα, πείτε, πόδια, αίμα, κοπέλα, φαγητό, ειμαι, ποιον, μερικά, + δύσκολο, μπορούν, βρεις, όμορφη, φύγεις, τύχη, πλάκα, έρθεις, άντρα, κορίτσια, μείνε, αστείο, καμία, + είχαν, χάρη, άλλος, πρεπει, σημασία, φυλακή, νεκρός, συγχωρείτε, φοβάμαι, μπράβο, γύρω, κανένας, μεταξύ, + τ, χθες, πολλές, όνομά, τζακ, ρε, καληνύχτα, πολυ, φύγει, αφήσω, ήθελες, tι, ήρθες, ακούς, πρώτο, γιατι, + ηρέμησε, γι, πάρουμε, πάρα, άλλους, κατάλαβα, έρθω, συνέχεια, έλεγα, γλυκιά, νοιάζει, χριστέ, βιβλίο, + κύριος, μ, χώρα, αρχή, ήρθα, πεθάνει, γη, έτοιμος, εγω, άσχημα, συμβεί, αυτοκίνητο, ζωής, τελικά, φέρω, + τρόπος, κατάσταση, www, περιμένω, σημαντικό, όσα, σκέφτηκα, μιλήσουμε, αφήστε, τωρα, ακούω, γιος, σκοτώσω, + δύναμη, κα, κε, εκείνο, γονείς, μιλάω, σκοτώσει, ολα, μείνει, μείνω, αρέσουν, δεv, υπόθεση, φίλους, όπλα, + υποθέτω, εμάς, ενώ, έξι, σχέδιο, άρεσε, καφέ, σκότωσε, χρειαζόμαστε, φίλο, σωστό, προσπαθώ, κάναμε, + κοιτάξτε, μoυ, κου, ποτό, εσάς, έι, έφυγε, ταινία, μοιάζει, κρεβάτι, εχουμε, περιμένει, νέο, μπορούσες, + μάθω, αφήσεις, περιμένετε, χρειάζεσαι, υπήρχε, μισό, δέκα, αφεντικό, περίπου, άλλοι, λόγος, ξέρουν, κάποτε, + βρήκες, καλύτερη, υπέροχο, τζον, δίπλα, σκάσε, θεού, άκουσα, φύγετε, λέξη, παρά, επόμενη, λέτε, περάσει, + πόσα, γίνεις, σώμα, ν, πήρες, τελείωσε, γιο, ρούχα, σκέφτομαι, εσυ, άλλες, γυρίσω, βάλω, μουσική, ραντεβού, + φωτιά, έδωσε, πάτε, φοβάσαι, βρει, δείξω, γίνω, βοηθήσει, τύπο, σειρά, αξίζει, μείνεις, είπαν, άλλον, + κυρίες, λίγη, πέρασε, κάτσε, πήγα, δείτε, μιας, βδομάδα, έρχομαι, προσοχή, εύκολο, ερώτηση, υπέροχα, + σίγουρη, νοσοκομείο, τρελός, ενας, βάλε, πόλεμο, φέρε, δικά, τιμή, κατάλαβες, ταξίδι, οποίο, δουλεύει, θεό, + μικρέ, μάθεις, βρίσκεται, πολλοί, δες, πάρτε, παντού, πρόσωπο, μήνυμα, αδερφή, μιλάει, παλιά, πουθενά, + κράτα, περίπτωση, φως, επάνω, έλεγε, συμφωνία, οπως, ολοι, πρώτος, δεσποινίς, γιατρός, γνωρίζω, σαμ, + σκέφτεσαι, ει, φίλη, σεξ, έκαναν, προβλήματα, κάπως, ό, τελευταίο, ακούσει, τζο, καλώς, επιλογή, + σταματήστε, τόσα, οτιδήποτε, περισσότερα, άδεια, πάρτι, περίμενα, ακούγεται, gmteam, ήξερες, καιρός, + μαλλιά, καλύτερος, κανεις, φρανκ, μέση, συνέχισε, τίποτε, φωτογραφία, κατι, μεγάλος, περιοχή, άσε, καθώς, + είδε, λόγια, μήνα, μαλακίες, όμορφο, δώρο, στόμα, χάλια, εντελώς, μακάρι, τελειώσει, γνώμη, γιατρέ, ξερω, + πλευρά, μέλλον, θάνατο, νιώθεις, έτοιμοι, κομμάτι, μάθει, μιλάμε, ψηλά, αέρα, ερωτήσεις, αυτού, δώσει, + φεύγω, σημείο, τηλεόραση, κυριε, πραγματικότητα, ανάγκη, βοηθήσεις, προσπάθησε, γύρνα, άφησε, λίγα, κάντε, + είvαι, βλέπετε, αυτη, δείπνο, επιτέλους, κέντρο, περίεργο, ακούστε, πλοίο, κάποιες, δικός, σoυ, οικογένειά, + μιλήσει, πλέον, υπόσχομαι, περιμένεις, ήξερε, σκοτώσεις, ενταξει, δώσεις, εκει, ήμασταν, έρχονται, κώλο, + ρωτήσω, παίρνει, σιγά, σήκω, στοιχεία, αδελφή, βασικά, μένει, άκρη, πηγαίνετε, παίρνεις, tο, περιμένουμε, + συγχωρείς, μικρός, πόδι, δίνει, εκατομμύρια, ξενοδοχείο, αποστολή, ενδιαφέρον, χάρηκα, αεροπλάνο, γάμο, + χιλιάδες, υόρκη, οκ, ευχαριστούμε, καλα, κοιτάς, σα, π, χρόνος, ησυχία, ασφάλεια, εκείνος, a, βρήκε, + τέσσερα, βγάλω, μπες, συχνά, ημέρα, μάνα, εν, αγαπάς, άνθρωπο, γραμμή, φωτογραφίες, προσέχεις, ύπνο, + μυστικό, σχετικά, είδους, σκέψου, χριστούγεννα, κόσμου, τομ, μισώ, σύστημα, δουλειές, τελείως, πεθάνω, + αλλάξει, δεξιά, συνήθως, δουλεύω, μάικλ, εβδομάδα, νούμερο, λείπει, έτοιμη, τμήμα, βγει, ψυχή, έπεσε, + κάθαρμα, ματιά, οποία, πληροφορίες, μονο, κρίμα, τραγούδι, μαγαζί, δουλεύεις, μαζι, τέλειο, κύριο, + λέγεται, τσάρλι, πεθάνεις, σκεφτόμουν, καλησπέρα, συγχαρητήρια, φωνή, εκ, άτομο, παίζεις, σκάφος, + φαίνεσαι, ξαφνικά, παραπάνω, ατύχημα, θελω, ξέχνα, ήρθατε, εναντίον, τραπέζι, γράμμα, μείνετε, αμερική, + βασιλιάς, υπό, μπάνιο, ποτε, ίδιος, προφανώς, μαλάκα, αδερφός, άνδρες, nαι, χρονών, ναί, κλειδί, δις, + γιαγιά, παράξενο, πτώμα, βρήκαμε, μιλήσεις, υποτίθεται, ορκίζομαι, δυνατά, ποιό, θάλασσα, παίρνω, άκουσες, + παρέα, αριστερά, έμαθα, μάχη, μηχανή, σάρα, ζωντανός, όνειρο, παλιό, μπορούσαμε, πάντως, ανάμεσα, έχασα, + νωρίς, κάποιοι, άκου, παίζει, φτάνει, δίνω, βγες, υπέροχη, νόημα, έλεγχο, μέτρα, ξερεις, ζει, δείχνει, + βρες, τού +""" + +# Source: https://www.101languages.net/russian/most-common-russian-words/ +alias words_ru = """ +я, не, что, в, и, ты, это, на, с, он, вы, как, мы, да, а, мне, меня, у, нет, так, но, то, все, тебя, его, +за, о, она, тебе, если, они, бы, же, ну, здесь, к, из, есть, чтобы, для, хорошо, когда, вас, только, по, +вот, просто, был, знаю, нас, всё, было, от, может, кто, вам, очень, их, там, будет, уже, почему, еще, +быть, где, спасибо, ничего, сейчас, или, могу, хочу, нам, чем, мой, до, надо, этого, ее, теперь, давай, +знаешь, нужно, больше, этом, нибудь, раз, со, была, этот, ему, ладно, эй, время, тоже, даже, хочешь, +сказал, ли, себя, думаю, пока, должен, потому, никогда, ни, тут, ещё, её, пожалуйста, сюда, привет, +тогда, конечно, моя, него, сегодня, один, тобой, правда, лучше, об, были, того, можно, мной, всегда, +сказать, день, сэр, без, можешь, чего, эти, дело, значит, лет, много, во, делать, буду, порядке, должны, +такой, ведь, ним, всего, сделать, хотел, твой, жизнь, ей, мистер, потом, через, себе, них, всех, такое, +им, куда, том, мама, после, человек, люди, слишком, иди, зачем, этим, немного, сколько, этой, знаете, +боже, ней, эту, который, отец, свою, деньги, два, под, твоя, мои, никто, моей, думаешь, друг, жизни, +эта, назад, видел, кажется, точно, вместе, люблю, мог, случилось, сам, нравится, черт, какой, людей, +папа, домой, тот, скажи, которые, должна, три, всем, сделал, возможно, прошу, будем, дома, парень, +снова, говорит, место, отсюда, можем, будешь, пошли, делаешь, совсем, говорил, понимаю, завтра, хочет, +простите, разве, давайте, хотите, отлично, сказала, туда, прямо, времени, вами, лишь, своей, хватит, +думал, можете, дом, дела, знать, дай, понял, помочь, говорить, слушай, свои, поэтому, прости, знает, +именно, знал, тем, кого, смотри, каждый, ваш, похоже, найти, моего, наш, мать, одна, имя, про, говорю, +будут, оно, свой, нельзя, извините, стоит, действительно, зовут, поговорить, доктор, перед, несколько, +нужен, происходит, ко, господи, возьми, мою, тех, нами, вижу, должно, наверное, откуда, понимаешь, верно, +скоро, уж, деле, твои, пусть, всю, хотела, при, более, ребята, нее, быстро, подожди, идти, надеюсь, чём, +работу, видеть, такая, этих, уверен, нужна, года, раньше, такие, руки, видишь, какая, посмотри, сын, +самом, ваша, послушай, равно, наши, другой, ага, мир, извини, минут, против, твоей, пор, жить, ж, жаль, +вообще, могли, хотя, человека, пора, ради, говорят, почти, твою, могут, над, весь, первый, чёрт, слышал, +собой, брат, вещи, дня, скажу, говоришь, нормально, своего, мое, ваше, итак, будь, ночь, хоть, ясно, +плохо, дверь, вопрос, господин, давно, денег, ваши, ка, мисс, одну, глаза, пять, будто, между, пойду, +опять, работа, самое, иногда, детей, этому, рад, здорово, бог, одного, ночи, готов, номер, которая, +машину, любовь, дорогая, виду, одно, прекрасно, вон, своих, быстрее, отца, женщина, достаточно, рядом, +убить, таким, пойдем, смерти, дети, такого, правильно, месте, никаких, сказали, здравствуйте, пару, две, +видела, долго, хороший, ах, кроме, алло, нашей, прав, вчера, вечером, жена, миссис, чтоб, друга, нужны, +кем, какие, те, увидеть, утро, смогу, неё, сама, моему, большой, сразу, работать, сердце, стал, своим, +сначала, могла, вроде, ними, говори, голову, дальше, помнишь, либо, ума, одной, вечер, случае, взять, +проблемы, помощь, добрый, год, думала, делает, скорее, слова, капитан, последний, важно, дней, помню, +ночью, утром, моих, произошло, которую, боюсь, также, вашей, ой, стой, твоего, никого, дорогой, убил, +насчет, друзья, самый, проблема, видели, вперед, дерьмо, понятно, чувствую, наша, будете, тому, имею, +вернуться, придется, пришел, спать, стать, столько, говорила, пойти, иначе, работает, девушка, час, +момент, моим, умер, думаете, доброе, слово, новый, часов, мире, знаем, твое, мальчик, однажды, интересно, +конец, играть, a, заткнись, сделали, посмотреть, идет, узнать, свое, права, хорошая, город, джон, +долларов, парни, идем, говорите, уйти, понять, знала, поздно, нашли, работы, скажите, сделаю, увидимся, +какого, другие, идея, пошел, доме, дочь, имеет, приятно, лицо, наших, обо, понимаете, руку, часть, +смотрите, вся, собираюсь, четыре, прежде, хотят, скажешь, чувак, дайте, сделала, кофе, джек, верю, +ждать, затем, большое, сами, неужели, моё, любит, мужчина, дать, господа, таких, осталось, которой, +далеко, вернусь, сильно, ох, сможешь, кому, вашего, посмотрим, машина, подождите, свет, чуть, серьезно, +пришли, оружие, решил, смысле, видите, тихо, нашел, свидания, путь, той, совершенно, следующий, которого, +места, парня, вдруг, пути, мадам, какое, шанс, сестра, нашего, ужасно, минуту, вокруг, другом, иду, +других, хотели, нем, смерть, подумал, фильм, оставь, делаете, уверена, кровь, говорили, внимание, +помогите, идите, держи, получить, оба, взял, спокойно, обычно, мало, забыл, странно, смотреть, поехали, +дал, часа, прекрати, посмотрите, готовы, вернулся, поверить, позже, милая, женщины, любишь, довольно, +обратно, остаться, думать, та, стороны, полиция, тело, тысяч, делал, машины, угодно, муж, году, неплохо, +бога, некоторые, конце, милый, the, рождения, трудно, добро, любви, больно, невозможно, спокойной, +слышишь, типа, получил, которое, приятель, хуже, никому, честь, успокойся, вашу, маленький, выглядит, +чарли, сына, неделю, i, девочка, делаю, шесть, ноги, история, рассказать, послушайте, часто, кстати, +двух, забудь, которых, следует, знают, пришла, семья, станет, матери, ребенок, план, проблем, например, +сделай, воды, немедленно, мира, сэм, телефон, перестань, правду, второй, прощения, ту, наше, уходи, твоих, +помоги, пол, внутри, нему, смог, десять, нашу, около, бывает, самого, большая, леди, сможем, вниз, легко, +делай, единственный, рада, меньше, волнуйся, хотим, полагаю, мам, иметь, своими, мере, наконец, начала, +минутку, работе, пожаловать, другого, двое, никакого, честно, школе, лучший, умереть, дам, насколько, +всей, малыш, оставить, безопасности, ненавижу, школу, осторожно, сынок, джо, таки, пытался, другое, б, +клянусь, машине, недели, стало, истории, пришлось, выглядишь, чему, сможет, купить, слышала, знали, +настоящий, сих, выйти, людям, замечательно, полиции, огонь, пойдём, спросить, дядя, детка, среди, особенно, +твоим, комнате, шоу, выпить, постоянно, делают, позвольте, родители, письмо, городе, случай, месяцев, мужик, +благодарю, o, ребенка, смешно, ответ, города, образом, любой, полностью, увидел, еду, имени, вместо, +абсолютно, обязательно, улице, твоё, убили, ваших, ехать, крови, решение, вина, поможет, своё, секунду, +обещаю, начать, голос, вещь, друзей, показать, нечего, э, месяц, подарок, приехал, самая, молодец, сделаем, +крайней, женщин, собираешься, конца, страшно, новости, идиот, потерял, спасти, вернуть, узнал, слушайте, +хотелось, сон, поняла, прошло, комнату, семь, погоди, главное, рано, корабль, пытаюсь, игра, умерла, +повезло, всему, возьму, таком, моем, глаз, настолько, идём, удачи, готова, семьи, садись, гарри, держись, +звучит, мило, война, человеком, право, такую, вопросы, представить, работаю, имеешь, красивая, идёт, никакой, +профессор, думает, войны, стала, стали, оттуда, известно, слышу, начал, подумать, позвонить, старый, придётся, +историю, вести, твоему, последнее, хочется, миллионов, нашла, способ, отношения, земле, фрэнк, получится, +говоря, связи, многие, пошёл, пистолет, убью, руках, получилось, президент, остановить, тьi, оставил, одним, +you, утра, боль, хорошие, пришёл, открой, брось, вставай, находится, поговорим, кино, людьми, полицию, покажу, +волосы, последние, брата, месяца +""" + + +fn gen_word_pairs[words: String = words_en]() -> List[String]: + var result = List[String]() + try: + var list = words.split(",") + for w in list: + var w1 = w[].strip() + for w in list: + var w2 = w[].strip() + result.append(w1 + " " + w2) + except: + pass + return result + + +from bit import byte_swap, rotate_bits_left +from builtin._hash import hash +from memory import UnsafePointer +from builtin.hash import hash as old_hash +from builtin._hash import ( + AHasher, + hash as ahash, + _folded_multiply, + read_small, + U256, + U128, + MULTIPLE, + ROT, +) + + +# ===----------------------------------------------------------------------===# +# Benchmarks +# ===----------------------------------------------------------------------===# +@parameter +fn bench_small_keys[s: String](inout b: Bencher) raises: + var words = gen_word_pairs[s]() + + @always_inline + @parameter + fn call_fn(): + for w in words: + var h = old_hash(w[]) + benchmark.keep(h) + + b.iter[call_fn]() + + +@parameter +fn bench_small_keys_new_hash_function[s: String](inout b: Bencher) raises: + var words = gen_word_pairs[s]() + + @always_inline + @parameter + fn call_fn(): + for w in words: + var h = ahash(w[].unsafe_ptr(), w[].byte_length()) + benchmark.keep(h) + + b.iter[call_fn]() + + +@parameter +fn bench_long_key[s: String](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var h = old_hash(s) + benchmark.keep(h) + + b.iter[call_fn]() + + +@parameter +fn bench_long_key_new_hash_function[s: String](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + var h = ahash(s.unsafe_ptr(), s.byte_length()) + benchmark.keep(h) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + var m = Bench(BenchConfig(num_repetitions=1)) + m.bench_function[bench_small_keys[words_ar]](BenchId("bench_small_keys_ar")) + m.bench_function[bench_small_keys_new_hash_function[words_ar]]( + BenchId("bench_small_keys_new_ar") + ) + m.bench_function[bench_small_keys[words_el]](BenchId("bench_small_keys_el")) + m.bench_function[bench_small_keys_new_hash_function[words_el]]( + BenchId("bench_small_keys_new_el") + ) + m.bench_function[bench_small_keys[words_en]](BenchId("bench_small_keys_en")) + m.bench_function[bench_small_keys_new_hash_function[words_en]]( + BenchId("bench_small_keys_new_en") + ) + m.bench_function[bench_small_keys[words_he]](BenchId("bench_small_keys_he")) + m.bench_function[bench_small_keys_new_hash_function[words_he]]( + BenchId("bench_small_keys_new_he") + ) + m.bench_function[bench_small_keys[words_lv]](BenchId("bench_small_keys_lv")) + m.bench_function[bench_small_keys_new_hash_function[words_lv]]( + BenchId("bench_small_keys_new_lv") + ) + m.bench_function[bench_small_keys[words_pl]](BenchId("bench_small_keys_pl")) + m.bench_function[bench_small_keys_new_hash_function[words_pl]]( + BenchId("bench_small_keys_new_pl") + ) + m.bench_function[bench_small_keys[words_ru]](BenchId("bench_small_keys_ru")) + m.bench_function[bench_small_keys_new_hash_function[words_ru]]( + BenchId("bench_small_keys_new_ru") + ) + + m.bench_function[bench_long_key[words_ar]](BenchId("bench_long_key_ar")) + m.bench_function[bench_long_key_new_hash_function[words_ar]]( + BenchId("bench_long_key_new_ar") + ) + m.bench_function[bench_long_key[words_el]](BenchId("bench_long_key_el")) + m.bench_function[bench_long_key_new_hash_function[words_el]]( + BenchId("bench_long_key_new_el") + ) + m.bench_function[bench_long_key[words_en]]( + BenchId("bench_long_key_keys_en") + ) + m.bench_function[bench_long_key_new_hash_function[words_en]]( + BenchId("bench_long_key_new_en") + ) + m.bench_function[bench_long_key[words_he]](BenchId("bench_long_key_he")) + m.bench_function[bench_long_key_new_hash_function[words_he]]( + BenchId("bench_long_key_new_he") + ) + m.bench_function[bench_long_key[words_lv]](BenchId("bench_long_key_lv")) + m.bench_function[bench_long_key_new_hash_function[words_lv]]( + BenchId("bench_long_key_new_lv") + ) + m.bench_function[bench_long_key[words_pl]](BenchId("bench_long_key_pl")) + m.bench_function[bench_long_key_new_hash_function[words_pl]]( + BenchId("bench_long_key_new_pl") + ) + m.bench_function[bench_long_key[words_ru]](BenchId("bench_long_key_ru")) + m.bench_function[bench_long_key_new_hash_function[words_ru]]( + BenchId("bench_long_key_new_ru") + ) + m.dump_report() diff --git a/stdlib/src/builtin/_hash.mojo b/stdlib/src/builtin/_hash.mojo new file mode 100644 index 0000000000..ce5943a191 --- /dev/null +++ b/stdlib/src/builtin/_hash.mojo @@ -0,0 +1,180 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from bit import byte_swap +from bit import rotate_bits_left +from memory import UnsafePointer + +alias U256 = SIMD[DType.uint64, 4] +alias U128 = SIMD[DType.uint64, 2] +alias MULTIPLE = 6364136223846793005 +alias ROT = 23 + + +@always_inline +fn _folded_multiply(lhs: UInt64, rhs: UInt64) -> UInt64: + """A fast function to emulate a folded multiply of two 64 bit uints. + Used because we don't have UInt128 type. + + Args: + lhs: 64 bit uint. + rhs: 64 bit uint. + + Returns: + A value which is similar in its bitpattern to result of a folded multply. + """ + var b1 = lhs * byte_swap(rhs) + var b2 = byte_swap(lhs) * (~rhs) + return b1 ^ byte_swap(b2) + + +@always_inline +fn read_small(data: UnsafePointer[UInt8], length: Int) -> U128: + """Produce a `SIMD[DType.uint64, 2]` value from data which is smaller than or equal to `8` bytes. + + Args: + data: Pointer to the byte array. + length: The byte array length. + + Returns: + Returns a SIMD[DType.uint64, 2] value. + """ + if length >= 2: + if length >= 4: + # len 4-8 + var a = data.bitcast[DType.uint32]().load().cast[DType.uint64]() + var b = data.offset(length - 4).bitcast[DType.uint32]().load().cast[ + DType.uint64 + ]() + return U128(a, b) + else: + # len 2-3 + var a = data.bitcast[DType.uint16]().load().cast[DType.uint64]() + var b = data.offset(length - 1).load().cast[DType.uint64]() + return U128(a, b) + else: + # len 0-1 + if length > 0: + var a = data.load().cast[DType.uint64]() + return U128(a, a) + else: + return U128(0, 0) + + +struct AHasher: + var buffer: UInt64 + var pad: UInt64 + var extra_keys: U128 + + fn __init__(inout self, key: U256): + """Initialize the hasher with a key. + + Args: + key: Modifier for the computation of the final hash value. + """ + var pi_key = key ^ U256( + 0x243F_6A88_85A3_08D3, + 0x1319_8A2E_0370_7344, + 0xA409_3822_299F_31D0, + 0x082E_FA98_EC4E_6C89, + ) + self.buffer = pi_key[0] + self.pad = pi_key[1] + self.extra_keys = U128(pi_key[2], pi_key[3]) + + @always_inline + fn large_update(inout self, new_data: U128): + """Update the buffer value with new data. + + Args: + new_data: Value used for update. + """ + var xored = new_data ^ self.extra_keys + var combined = _folded_multiply(xored[0], xored[1]) + self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined) + + @always_inline + fn finish(self) -> UInt64: + """Computes the hash value based on all the previously provided data. + + Returns: + Final hash value. + """ + var rot = self.buffer & 63 + var folded = _folded_multiply(self.buffer, self.pad) + return (folded << rot) | (folded >> (64 - rot)) + + @always_inline + fn write(inout self, data: UnsafePointer[UInt8], length: Int): + """Consume provided data to update the internal buffer. + + Args: + data: Pointer to the byte array. + length: The length of the byte array. + """ + self.buffer = (self.buffer + length) * MULTIPLE + if length > 8: + if length > 16: + var tail = data.offset(length - 16).bitcast[ + DType.uint64 + ]().load[width=2]() + self.large_update(tail) + var offset = 0 + while length - offset > 16: + var block = data.offset(offset).bitcast[ + DType.uint64 + ]().load[width=2]() + self.large_update(block) + offset += 16 + else: + var a = data.bitcast[DType.uint64]().load() + var b = data.offset(length - 8).bitcast[DType.uint64]().load() + self.large_update(U128(a, b)) + else: + var value = read_small(data, length) + self.large_update(value) + + +fn hash[ + key: U256 = U256(0, 0, 0, 0) +](bytes: UnsafePointer[UInt8], n: Int) -> UInt: + """Hash a byte array using an adopted AHash algorithm. + + References: + + - [Reference Implementation in Rust](https://github.com/tkaitchuck/aHash) + + ```mojo + from random import rand + var n = 64 + var rand_bytes = UnsafePointer[UInt8].alloc(n) + rand(rand_bytes, n) + _ = hash(rand_bytes, n) + ``` + + Parameters: + key: A key to modify the result of the hash function, defaults to [0, 0, 0, 0]. + + Args: + bytes: The byte array to hash. + n: The length of the byte array. + + Returns: + A 64-bit integer hash. This hash is _not_ suitable for + cryptographic purposes, but will have good low-bit + hash collision statistical properties for common data structures. + """ + + var hasher = AHasher(key) + hasher.write(bytes, n) + return UInt(int(hasher.finish())) diff --git a/stdlib/test/builtin/test__hash.mojo b/stdlib/test/builtin/test__hash.mojo new file mode 100644 index 0000000000..4228ea60da --- /dev/null +++ b/stdlib/test/builtin/test__hash.mojo @@ -0,0 +1,806 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from bit import pop_count +from builtin._hash import hash +from builtin.hash import hash as old_hash +from testing import assert_equal, assert_not_equal, assert_true +from memory import memset_zero, UnsafePointer +from time import now + +# Source: https://www.101languages.net/arabic/most-common-arabic-words/ +alias words_ar = """ +لا, من, هذا, أن, في, أنا, على, ما, هل, + يا, و, لقد, ذلك, ماذا, أنت, هنا, لم, إلى, نعم, كان, هو, ان, هذه, هناك, عن, فى, كل, ليس, فقط, كنت, الآن, يجب, انا, + لك, مع, شيء, لكن, لن, الذي, حسنا, كيف, سوف, هيا, نحن, إنه, ـ, أجل, لماذا, إذا, عندما, انه, كذلك, لي, الى, بعد, انت, + هي, أين, أنه, كانت, حتى, أي, إنها, أعرف, قد, قبل, تلك, الأمر, بعض, أو, مثل, أريد, رجل, لو, أعتقد, ربما, أيها, بخير, + يكون, عليك, جيد, أنك, شخص, إن, التي, ولكن, أليس, علي, أحد, به, الوقت, يمكن, انها, اليوم, شئ, تعرف, تريد, صحيح, أكثر, + تكون, لست, كما, أستطيع, منذ, جدا, سيدي, يمكنك, لذا, واحد, لديك, يبدو, أوه, كلا, الرجل, لدي, تفعل, غير, عليه, اذا, + آخر, حدث, مرة, شكرا, لدينا, الناس, يوجد, له, مكان, سيد, سيكون, أعلم, رائع, مرحبا, آسف, بهذا, وقت, اللعنة, كم, + ليست, أفضل, بها, معك, أنها, الذى, الكثير, قلت, بك, يحدث, الان, يكن, يوم, وأنا, واحدة, بي, أخرى, ولا, علينا, + أبي, بأن, ثم, تعال, هكذا, يمكنني, هم, ألا, بالطبع, أنني, المكان, بذلك, معي, لهذا, ها, شىء, انك, إلهي, تستطيع, + العمل, العالم, الحقيقة, الليلة, بالتأكيد, حقا, تعلم, أمي, الطريق, حال, لى, لها, الأن, هؤلاء, فعل, توقف, + عمل, حول, لنا, خلال, اعتقد, السيد, انظر, منك, أى, أفعل, فعلت, لأن, إذن, قال, الجميع, تم, الجحيم, هى, فيه, + جيدة, عنه, بشكل, بما, تقول, لديه, ثانية, لذلك, أكون, دعنا, ايها, المال, يمكننا, الذهاب, متى, تعتقد, + اريد, عليها, أذهب, ستكون, فضلك, بدون, أرجوك, التى, شيئا, نذهب, لكي, نفسك, بنا, اين, وأنت, لكم, اي, + بين, إنهم, أرى, المنزل, بحق, كنا, عند, أم, منه, نفس, اذهب, حيث, مجرد, أقول, تبدو, الحياة, أيضا, + تحت, الأشياء, معه, يريد, أننا, أنظر, لما, اعرف, إلي, ثلاثة, انتظر, الرجال, الذين, حصلت, أني, + سعيد, لابد, عزيزتي, الشيء, + فكرة, انهم, الله, الباب, سيدى, دائما, رأيت, مشكلة, استطيع, تكن, تذهب, ليلة, شيئ, أظن, طوال, + جميل, وهو, الشرطة, او, دولار, السيارة, وهذا, كبير, مني, بسرعة, النار, الأمور, سمعت, أشعر, يعرف, + أعني, لدى, بهذه, أحب, سنوات, بأس, الأفضل, بالنسبة, + أنتم, عظيم, يقول, جميلة, جون, جاك, بسبب, الوحيد, أمر, بل, بالفعل, الشخص, الي, دعني, خارج, اجل, الخير, ــ, + حالك, للغاية, فحسب, كانوا, أردت, فتاة, بشأن, يعني, كبيرة, ترى, آسفة, دقيقة, أنهم, يستطيع, احد, بأنك, تعمل, + تريدين, فيها, اليس, رائعة, رجال, نوع, حياتي, الأرض, البيت, قتل, اوه, والآن, مات, بكل, تعرفين, أحتاج, نستطيع, + جديد, صباح, ألم, عيد, منها, يعمل, الموت, إليك, جميع, لأنه, لحظة, لكني, الامر, عشر, لكنه, بحاجة, بأنه, أتمنى, + إليه, عنك, الفتاة, لهم, بالضبط, سأكون, اعلم, اللعين, رقم, طريق, منهم, المدينة, الحب, لنذهب, خذ, أكن, فوق, + عزيزي, دون, الـ, صغيرة, الرئيس, تتحدث, ترجمة, صديقي, فقد, الصغير, ولم, ساعة, يفعل, غرفة, وماذا, المرة, قام, + إلا, عام, هذة, متأكد, دقائق, سيارة, فعله, سعيدة, مما, ومن, معنا, سبب, سأذهب, الطريقة, الأطفال, سنة, بينما, + يرام, السبب, أننى, أول, اى, أريدك, قمت, الأولى, المدرسة, ذهبت, لطيف, نفسي, الا, الجنس, أية, أقصد, غريب, نفعل, + الصباح, حالة, المزيد, أبدا, مهما, اسمع, لأنك, أحاول, وقد, ايضا, أحبك, اكثر, فرصة, رأيك, افعل, الحصول, صغير, + الماء, جيدا, التحدث, يمكننى, الساعة, طريقة, أيتها, كثيرا, سيدة, خمسة, وجدت, قليلا, وانا, اخرى, الليل, تعني, + تماما, نهاية, عرفت, اني, أفكر, معها, الأول, لكنك, تعالي, البعض, أفهم, أخبرك, حياة, أتعرف, نفسه, الواقع, + أيام, انني, تأتي, لديهم, فهمت, لـ, لديها, الحرب, الأقل, أخبرني, إنك, بـ, الصغيرة, تحتاج, بدأت, حياتك, + عني, إذهب, عندي, تقلق, نحتاج, إنتظر, أصبح, مجنون, يكفي, اننا, خطأ, الطفل, نصف, أكبر, الخاص, عليهم, + نريد, لأنني, حان, تعلمين, نعرف, هنالك, رفاق, لكنني, معى, دكتور, جديدة, هلا, افضل, طفل, عنها, أتعلم, تقوم, + أعمل, بد, الهاتف, بالخارج, السيدة, الطعام, ثلاث, أقوم, صديق, أتحدث, فرانك, الجديد, مالذي, للتو, سيدتي, + طويلة, وما, السجن, أشياء, فأنا, أخبرتك, العديد, أعطني, أراك, أخي, سام, قالت, فريق, فيما, جو, يتم, + نكون, وليس, يذهب, ممكن, لمدة, حق, اسف, يجري, تفعله, مثلك, وبعد, تشعر, تحب, اخر, رؤية, طويل, والدك, + ذهب, آه, أقل, حصل, لكى, اللعنه, سأفعل, يعلم, كله, القيام, فتى, الممكن, أخرج, النوم, داخل, جورج, + رجاء, أصبحت, الخاصة, اذن, ذات, جميعا, منا, الموضوع, الفتى, اللقاء, أخر, كي, كلمة, عبر, أود, بيت, + تفهم, تفعلين, علاقة, بى, نيويورك, الآخر, بلا, مايكل, نظرة, ونحن, الخارج, تحاول, المشكلة, بواسطة, كن, + المفترض, قل, يارجل, تظن, يقوم, مليون, أخذ, توم, يمكنه, مباشرة, سيئة, الحال, العودة, حاول, عندك, + تكوني, ميت, الكبير, الفتيات, النساء, رئيس, أسرع, النهاية, قادم, أحضر, جزء, + الهي, ذاهب, العام, لكنها, أتريد, بخصوص, الوغد, حقيقي, إنني, البقاء, حبيبتي, بهم, المساعدة, تصبح, عشرة, أحدهم, + الخروج, قصة, مستحيل, أربعة, وهي, أبى, كلها, ضد, حاولت, القادمة, يأتي, تفضل, أسمع, تمت, توجد, لكل, العشاء, + الغرفة, وانت, وسوف, خمس, تذكر, أصدق, ألف, بنفسك, شباب, الماضي, دعونا, الأسبوع, نتحدث, نسيت, بأنني, منزل, + وضع, ولد, أنتي, جاهز, رسالة, دي, ابن, اكون, حقيقة, مايك, حين, عائلة, أدري, وكان, القائد, للمنزل, مساعدتك, + غدا, ظننت, ولن, المرأة, لهذه, تحرك, يهم, تبقى, الطبيب, اسم, انظري, تبا, أتذكر, فترة, ساعات, تفكر, تحصل, + بأي, النقود, لعبة, زوجتي, الكلام, ستفعل, أسف, فهو, الملك, مدينة, بكم, الوحيدة, أمام, عدد, اخرج, بول, سأعود, + جئت, لأني, تحدث, السلامة, الماضية, أمك, اعتقدت, مره, مساء, بطريقة, الرب, ابدا, أهذا, وفي, وكل, أتيت, منكم, + انتهى, بوب, بعيدا, ضع, وجود, تعود, زلت, اللعينة, نقوم, كلنا, أحصل, يريدون, تأخذ, المحتمل, الشمس, بدأ, + ارجوك, المسيح, جاء, كهذا, سنذهب, تعالى, إثنان, فعلا, حتي, سيحدث, الجيد, وشك, القادم, + معرفة, صورة, أعود, اسمي, طلب, آنسة, الثانية, فقدت, حفلة, تنظر, مثير, اننى, وصلت, أنتظر, السماء, يقولون, الهراء, + معهم, ابي, وعندما, مجموعة, العاهرة, ماري, حسن, الزواج, نحو, دعيني, الجديدة, مهم, أمس, اتصل, ابتعد, هراء, ستة, + الأخرى, يحصل, ولكني, الطائرة, أصدقاء, الحظ, مشاكل, الترجمة, تبدين, لسنا, مستعد, ولكنه, اقول, أولئك, النوع, أثناء, + اسمه, اسمك, مكتب, والدي, ينبغي, منى, كرة, بيتر, عدم, أطفال, الإطلاق, سوى, مضحك, الوضع, جي, الأخيرة, صعب, أحمق, + يحاول, الشئ, حينما, الأشخاص, البحر, إليها, عرض, بأني, يحتاج, سيء, عالم, كثير, الداخل, الكتاب, ذو, الأيام, خلف, + بعضنا, يعود, ام, اللعبة, إني, رأسك, شركة, زال, بشيء, الاشياء, قطعة, خائف, واضح, أمى, موجود, علم, يعد, أبحث, + الدخول, جين, امرأة, متأكدة, هيه, تخبرني, مدى, إلهى, احب, عما, نرى, بيننا, تعيش, قتلت, الأحمق, تشارلي, + بيل, + عليكم, سؤال, طلبت, الهواء, وهذه, صوت, انتم, ميلاد, ماكس, + تعتقدين, الحديث, الجانب, صديقك, ذا, خطر, أطلق, الشارع, عملية, ببعض, تتكلم, مختلف, تحمل, مساعدة, + بضعة, المناسب, المنطقة, قم, بالداخل, البداية, لأجل, زوجتك, مقابل, يحب, هاري, ممتاز, قريبا, سنكون, + فعلته, بتلك, التفكير, أسفل, للعمل, العجوز, امي, الكلب, انتظري, مازال, إننا, اشعر, الجيش, شرطة +""" + +# Source: https://github.com/tkaitchuck/ahash/blob/7d5c661a74b12d5bc5448b0b83fdb429190db1a3/tests/map_tests.rs#L9 +alias words_en: String = """ + a, ability, able, about, above, accept, according, account, across, act, action, + activity, actually, add, address, administration, admit, adult, affect, after, + again, against, age, agency, agent, ago, agree, agreement, ahead, air, all, + allow, almost, alone, along, already, also, although, always, American, among, + amount, analysis, and, animal, another, answer, any, anyone, anything, appear, + apply, approach, area, argue, arm, around, arrive, art, article, artist, as, + ask, assume, at, attack, attention, attorney, audience, author, authority, + available, avoid, away, baby, back, bad, bag, ball, bank, bar, base, be, beat, + beautiful, because, become, bed, before, begin, behavior, behind, believe, + benefit, best, better, between, beyond, big, bill, billion, bit, black, blood, + blue, board, body, book, born, both, box, boy, break, bring, brother, budget, + build, building, business, but, buy, by, call, camera, campaign, can, cancer, + candidate, capital, car, card, care, career, carry, case, catch, cause, cell, + center, central, century, certain, certainly, chair, challenge, chance, change, + character, charge, check, child, choice, choose, church, citizen, city, civil, + claim, class, clear, clearly, close, coach, cold, collection, college, color, + come, commercial, common, community, company, compare, computer, concern, + condition, conference, Congress, consider, consumer, contain, continue, control, + cost, could, country, couple, course, court, cover, create, crime, cultural, + culture, cup, current, customer, cut, dark, data, daughter, day, dead, deal, + death, debate, decade, decide, decision, deep, defense, degree, Democrat, + democratic, describe, design, despite, detail, determine, develop, development, + die, difference, different, difficult, dinner, direction, director, discover, + discuss, discussion, disease, do, doctor, dog, door, down, draw, dream, drive, + drop, drug, during, each, early, east, easy, eat, economic, economy, edge, + education, effect, effort, eight, either, election, else, employee, end, energy, + enjoy, enough, enter, entire, environment, environmental, especially, establish, + even, evening, event, ever, every, everybody, everyone, everything, evidence, + exactly, example, executive, exist, expect, experience, expert, explain, eye, + face, fact, factor, fail, fall, family, far, fast, father, fear, federal, feel, + feeling, few, field, fight, figure, fill, film, final, finally, financial, find, + fine, finger, finish, fire, firm, first, fish, five, floor, fly, focus, follow, + food, foot, for, force, foreign, forget, form, former, forward, four, free, + friend, from, front, full, fund, future, game, garden, gas, general, generation, + get, girl, give, glass, go, goal, good, government, great, green, ground, group, + grow, growth, guess, gun, guy, hair, half, hand, hang, happen, happy, hard, + have, he, head, health, hear, heart, heat, heavy, help, her, here, herself, + high, him, himself, his, history, hit, hold, home, hope, hospital, hot, hotel, + hour, house, how, however, huge, human, hundred, husband, I, idea, identify, if, + image, imagine, impact, important, improve, in, include, including, increase, + indeed, indicate, individual, industry, information, inside, instead, + institution, interest, interesting, international, interview, into, investment, + involve, issue, it, item, its, itself, job, join, just, keep, key, kid, kill, + kind, kitchen, know, knowledge, land, language, large, last, late, later, laugh, + law, lawyer, lay, lead, leader, learn, least, leave, left, leg, legal, less, + let, letter, level, lie, life, light, like, likely, line, list, listen, little, + live, local, long, look, lose, loss, lot, love, low, machine, magazine, main, + maintain, major, majority, make, man, manage, management, manager, many, market, + marriage, material, matter, may, maybe, me, mean, measure, media, medical, meet, + meeting, member, memory, mention, message, method, middle, might, military, + million, mind, minute, miss, mission, model, modern, moment, money, month, more, + morning, most, mother, mouth, move, movement, movie, Mr, Mrs, much, music, must, + my, myself, name, nation, national, natural, nature, near, nearly, necessary, + need, network, never, new, news, newspaper, next, nice, night, no, none, nor, + north, not, note, nothing, notice, now, n't, number, occur, of, off, offer, + office, officer, official, often, oh, oil, ok, old, on, once, one, only, onto, + open, operation, opportunity, option, or, order, organization, other, others, + our, out, outside, over, own, owner, page, pain, painting, paper, parent, part, + participant, particular, particularly, partner, party, pass, past, patient, + pattern, pay, peace, people, per, perform, performance, perhaps, period, person, + personal, phone, physical, pick, picture, piece, place, plan, plant, play, + player, PM, point, police, policy, political, politics, poor, popular, + population, position, positive, possible, power, practice, prepare, present, + president, pressure, pretty, prevent, price, private, probably, problem, + process, produce, product, production, professional, professor, program, + project, property, protect, prove, provide, public, pull, purpose, push, put, + quality, question, quickly, quite, race, radio, raise, range, rate, rather, + reach, read, ready, real, reality, realize, really, reason, receive, recent, + recently, recognize, record, red, reduce, reflect, region, relate, relationship, + religious, remain, remember, remove, report, represent, Republican, require, + research, resource, respond, response, responsibility, rest, result, return, + reveal, rich, right, rise, risk, road, rock, role, room, rule, run, safe, same, + save, say, scene, school, science, scientist, score, sea, season, seat, second, + section, security, see, seek, seem, sell, send, senior, sense, series, serious, + serve, service, set, seven, several, sex, sexual, shake, share, she, shoot, + short, shot, should, shoulder, show, side, sign, significant, similar, simple, + simply, since, sing, single, sister, sit, site, situation, six, size, skill, + skin, small, smile, so, social, society, soldier, some, somebody, someone, + something, sometimes, son, song, soon, sort, sound, source, south, southern, + space, speak, special, specific, speech, spend, sport, spring, staff, stage, + stand, standard, star, start, state, statement, station, stay, step, still, + stock, stop, store, story, strategy, street, strong, structure, student, study, + stuff, style, subject, success, successful, such, suddenly, suffer, suggest, + summer, support, sure, surface, system, table, take, talk, task, tax, teach, + teacher, team, technology, television, tell, ten, tend, term, test, than, thank, + that, the, their, them, themselves, then, theory, there, these, they, thing, + think, third, this, those, though, thought, thousand, threat, three, through, + throughout, throw, thus, time, to, today, together, tonight, too, top, total, + tough, toward, town, trade, traditional, training, travel, treat, treatment, + tree, trial, trip, trouble, true, truth, try, turn, TV, two, type, under, + understand, unit, until, up, upon, us, use, usually, value, various, very, + victim, view, violence, visit, voice, vote, wait, walk, wall, want, war, watch, + water, way, we, weapon, wear, week, weight, well, west, western, what, whatever, + when, where, whether, which, while, white, who, whole, whom, whose, why, wide, + wife, will, win, wind, window, wish, with, within, without, woman, wonder, word, + work, worker, world, worry, would, write, writer, wrong, yard, yeah, year, yes, + yet, you, young, your, yourself""" + +# Source: https://www.101languages.net/hebrew/most-common-hebrew-words/ +alias words_he = """ +לא , את , אני , זה , אתה , + מה , הוא , לי, על, כן, לך, של, יש , בסדר , אבל , כל , שלי , טוב , עם, היא, אם, רוצה, + שלך, היה, אנחנו, הם, אותך, יודע, אז, רק, אותו, יכול, אותי, יותר, הזה, אל, כאן, או, + למה, שאני, כך, אחד, עכשיו, משהו, להיות, היי, תודה, כמו, אין, זאת, איך, נכון, מי, שם, + לו, צריך, לעשות, קדימה, לנו, חושב, כמה, שאתה, זו, גם, יודעת, אותה, עוד, באמת, הייתי, + שהוא, אולי, בבקשה, עושה, פשוט, שזה, דבר, מאוד, כבר, שלא, נראה, לעזאזל, אתם, כדי, ואני, + פה, אלוהים, הנה, פעם, האם, בוא, שלו, איפה, הרבה, כי, יכולה, שלנו, אומר, יהיה, אותם, עד, + קצת, לפני, זמן, הכל, ממש, אבא, הולך, מר, אדוני, לראות, ובכן, מישהו, חייב, עדיין, לה, + אף, בכל, בדיוק, היום, אנשים, ללכת, מצטער, היית, שלום, קרה, שוב, אוהב, אחת, הייתה, אמא, + חשבתי, בן, איזה, יום, לדבר, תמיד, צריכה, לזה, חושבת, להם, היו, שאת, שיש, רואה, אפילו, + בטח, כולם, בגלל, שום, שהיא, אחר, חבר, בשביל, קורה, איתך, הזאת, אמרתי, אדם, תן, צריכים, + הזמן, יכולים, ואז, כלום, רגע, האלה, אחרי, מבין, תראה, בטוח, שהם, לכם, בו, אמר, מדי, + ג, טובה, אותנו, תהיה, אנו, אך, ככה, בזה, יפה, כזה, אוכל, מותק, מספיק, בואו, אפשר, שלה, + דברים, הכי, מזה, מקום, בואי, לכאן, אה, בבית, דרך, איתי, מתכוון, ביותר, הביתה, לומר, אחי, + מת, הזו, ה, הדבר, מדבר, שאנחנו, לעזור, לעולם, זהו, לדעת, כאילו, גדול, אוהבת, שנים, בי, + מכאן, יודעים, לקחת, ראיתי, בלי, נהדר, די, כסף, הו, היתה, מהר, עליי, י, מצטערת, וזה, ילד, + לשם, קשה, חכה, לאן, ואתה, ממני, ו, תגיד, רוצים, שני, הלילה, עליך, כמובן, עליו, נחמד, לכל, + להגיד, סליחה, אמרת, ל, מוכן, מחר, בא, ולא, והוא, אוקיי, אומרת, גברת, בך, נלך, בית, מעולם, + שלהם, אי, הבחור, עבודה, למצוא, נמצא, חייבים, מכיר, מנסה, ב, ואת, מתי, תעשה, בשבילך, מספר, + כדאי, דקות, שלכם, האמת, עושים, אלה, חייבת, דולר, הכסף, כעת, לילה, איש, עלי, לצאת, רציתי, + לתת, בחור, בכלל, איתו, רע, עשית, מרגיש, הכול, בעיה, עבור, אמור, לקבל, עובד, בנאדם, הולכים, + החיים, נוכל, מאמין, סוף, ידעתי, הולכת, לב, בחייך, היכן, שנה, זוכר, ממך, הגיע, קטן, החוצה, + תוכל, בזמן, הן, ילדים, נשמע, חיים, בדרך, אכפת, נעשה, הבית, ש, ידי, בוקר, עשה, לחזור, המקום, + הבא, מקווה, קח, עשיתי, חשוב, הי, אלו, בחיים, גבר, ללא, במקום, משנה, הלו, להרוג, שמעתי, העולם, + ספר, ר, זונה, עצמך, האנשים, למעלה, בני, לגבי, מאוחר, כמעט, תראי, לספר, בקשר, שמח, להתראות, + לבד, הדרך, האלו, שלוש, יופי, לגמרי, מדוע, אליך, מפה, קודם, ראית, למטה, בה, להגיע, חלק, מגיע, + מ, בין, לבוא, אתן, היינו, חרא, הדברים, תפסיק, אחרת, לשמוע, מזל, המפקד, בחוץ, אהיה, הספר, אליי, + ערב, תקשיב, אישה, השני, לחשוב, הערב, מבינה, מיד, בשבילי, למעשה, אוי, הראשון, אלי, תני, א, + חברים, בטוחה, רבה, ומה, מאז, ביום, במשך, בהחלט, עלינו, ון, לעבוד, השם, כולנו, לאחר, הראשונה, + למען, מניח, מוזר, בתוך, איזו, חזק, העבודה, מהם, לפחות, אמרה, האיש, ביחד, כנראה, שיהיה, בת, + אלא, הבעיה, כאשר, ימים, ואם, אימא, קטנה, ברגע, אתכם, אעשה, איני, הדלת, משחק, חדש, בעוד, סתם, + לבית, בחזרה, לאכול, להביא, אתמול, נורא, תראו, אדירים, יחד, גדולה, בעולם, חתיכת, לפעמים, מקרה, + בפנים, נו, עומד, ברור, אבי, הפעם, ממנו, שעות, שלומך, בלילה, אומרים, מתכוונת, אינך, הילד, כרגע, + האחרון, ביי, הא, טובים, העיר, חיי, הילדים, נראית, חכי, יהיו, למות, מצחיק, חוץ, תורגם, שהיה, + חזרה, מאד, הראש, רעיון, הרגע, אהבה, לשאול, להישאר, שאתם, יקרה, מושג, בלתי, איתה, להשיג, ראש, + יכולתי, תחת, עצמי, מכל, קוראים, אליו, שמעת, כ, להיכנס, אמיתי, הבן, תסתכל, כלומר, חברה, עליה, + והיא, ילדה, לשחק, העניין, ועכשיו, קשר, הגדול, לעזוב, החבר, נפלא, האחרונה, חמש, בפעם, עצור, כפי, + לישון, שתי, צודק, וגם, שלושה, ליד, לחיות, קרוב, רב, נגמר, לקרוא, שאין, תא, תדאג, יוצא, האדם, + והם, גמור, בבוקר, קל, מתה, באופן, בשם, מעט, הבאה, יורק, מוכנה, היחיד, לכן, תגידי, חי, חצי, איי, + לוקח, נעים, נהיה, לעצור, גרוע, לפגוש, נשים, שעה, נגד, הטוב, מים, חושבים, ממה, פנימה, מרגישה, + לפה, שתיים, וכל, אסור, פנים, שכל, מתחיל, אשר, אותן, היחידה, שומע, כמוך, בקרוב, לנצח, המכונית, + מחוץ, בחורה, חוזר, להבין, הבנתי, עלייך, לעבור, בערך, פעמים, בחדר, וואו, כאלה, קיבלתי, לכי, חודשים, + אלך, ארוחת, סוג, מדברת, מכירה, מאמינה, כה, זקוק, הקטן, אידיוט, מדהים, מצאתי, הסיבה, אינני, בנות, + בתור, לשמור, החברה, להמשיך, התחת, כשאני, שמי, נתראה, ימי, הסיפור, שכן, סיפור, בצורה, אקח, סלח, + לזוז, רציני, הכבוד, שמישהו, פחות, מדברים, שאלה, סיכוי, מתחת, אחרים, הללו, נכנס, תביא, התכוונתי, + שהייתי, הבוקר, ראשון, באותו, בחורים, בהם, לנסות, להשתמש, אדון, במה, שווה, זוכרת, טיפש, מיליון, + מוכנים, להתחיל, להראות, אנג, אראה, מלא, המשפחה, לפי, מאשר, פרנק, מטורף, לכך, שבוע, מגניב, צא, + ואנחנו, לתוך, לחכות, מאיפה, איתנו, מחדש, דעתך, שונה, חסר, תוך, נותן, לקנות, להכיר, הבחורה, + ינא, מחפש, שבו, ישר, חדשות, חדר, אש, אפשרי, מצוין, הלא, ים, מתוקה, עזרה, שאם, מעולה, כדור, + תירגע, אמרו, באה, החדש, בעיות, שאנו, המצב, הלך, לשתות, מעבר, מעל, טעות, כשאתה, עבר, עליהם, + נשאר, ויש, שב, מייקל, אלף, לקח, ארבע, סיבה, מצב, מן, מסוגל, מידי, להרגיש, בעל, משפחה, שזו, + שוטר, בחיי, מעניין, ההוא, קפה, הזדמנות, כלב, כלל, מקבל, שונא, מחכה, מפני, זין, תחזור, שקט, + באתי, מוצא, אביך, ניסיתי, תקשיבי, חן, מצוות, רוח, מוקדם, קפטן, תהיי, מאיתנו, מבטיח, מושלם, + ידעת, עניין, כוח, המון, פי, חולה, אוהבים, אינו, דם, הנשיא, משם, למרות, גורם, לאט, כבוד, ס, + בעבר, להתקשר, אלייך, משוגע, עשר, ללמוד, שטויות, בנוגע, צוחק, לבדוק, בצד, להאמין, חדשה, עצמו, + לגרום, המשחק, שרה, לעצמך, במיוחד, המשטרה, צוות, אחזור, שאמרתי, גברים, קורא, בראש, רחוק, + למקום, לשלם, להפסיק, מיוחד, הז, שמו, שמחה, כיף, אגיד, למי, ניתן, מאחורי, תמשיך, כיצד, + להוציא, מתים, כולכם, אצל, חבל, האישה, לעצמי, גברתי, תוכלי, רואים, דוד, להציל, שצריך, + בעלי, דוקטור, חג, לעבודה, בוודאי, תעשי, הוד, מילה, ברצינות, הארץ, עשינו, לאנשים, רצה, + עזוב, יצא, נתן, + שניות, בעיר, סי, חשבת, שאלות, אלינו, ידע, תנו, לשים, שאולי, בכך, יכולת, אן, היד, שאוכל, + מין, דקה, לדאוג, שמה, תרצה, ראה, הצילו, נוסף, החרא, אופן, כשהוא, צעיר, הפה, עולה, עובדת, + שמך, לתפוס, נמצאת, כלבה, האקדח, עדיף, הטלפון, טום, פול, חכו, קר, תלך, במקרה, יעשה, שניכם, + הארי, זוז, יקירתי, בהצלחה, לשבת, אנא, דין, מכיוון, יד, הקטנה, לבן, בנו, בעצמי, יין, תוריד, + למישהו, מייק, מול, נזוז, ככל, הלוואי, בעצמך, לרגע, קשור, בשקט, האל, ישנה, מעמד, כזאת, + רד, אחורה, איכפת, איתם, ממנה, חם, מבקש, שש, מידע, השנה, + אכן, אהבתי, בשעה, בסוף, שקרה, לכו, אליה, לבחור, תחשוב, ספק, המים, הפנים, לכולם, תדאגי, + קחי, שתוק, לברוח, מתוק, ארלי, התיק, שים, מישהי, לקרות, לטפל, לחפש, הידיים, ח, במצב, ואל +""" + +# Source: https://www.101languages.net/latvian/most-common-latvian-words/ +alias words_lv = """ + ir, es, un, tu, tas, ka, man, to, vai, ko, ar, kas, par, tā, kā, viņš, uz, no, tev, + mēs, nav, jūs, bet, labi, jā, lai, nē, mani, ja, bija, viņa, esmu, viņu, tevi, esi, + mums, tad, tikai, ne, viņi, kad, jums, arī, viss, nu, kur, pie, jau, tik, tur, te, vēl, + būs, visu, šeit, tagad, kaut, ļoti, pēc, viņam, taču, savu, gan, paldies, būtu, mūsu, + šo, lūdzu, mans, kāpēc, kungs, kāds, varbūt, tās, jūsu, cik, ak, daudz, jo, esam, + zinu, mana, zini, visi, būt, tam, šī, var, līdz, viens, pa, pat, esat, nekad, domāju, + nezinu, vairs, tiešām, tie, vien, kurš, varētu, dievs, neesmu, prom, tieši, kādu, aiziet, + šis, manu, protams, vajag, neko, vienkārši, tāpēc, gribu, varu, nāc, atpakaļ, mūs, + kārtībā, iet, kopā, viņiem, pats, pirms, domā, vienmēr, gribi, nekas, bez, tava, + vienu, ej, viņai, vairāk, notiek, nevaru, pret, tavs, teica, tavu, biju, dēļ, viņas, + laiku, neviens, kādēļ, vari, labāk, patīk, dari, mājās, nebija, cilvēki, ārā, viņus, + ejam, kāda, piedod, laikam, atkal, šķiet, trīs, sevi, ser, laiks, laika, nekā, manis, + iekšā, labs, tāds, darīt, harij, nevar, viena, lieliski, kuru, šīs, sauc, šurp, teicu, + laikā, tos, pagaidi, neesi, tevis, draugs, pārāk, tēvs, šodien, teikt, dienu, visiem, + tātad, notika, hei, zināt, bijis, sveiks, atvainojiet, tika, naudu, varam, savas, citu, + tādu, manas, redzi, šajā, kam, tajā, jābūt, vecīt, tiem, runā, cilvēku, taisnība, saka, + visus, mīlu, lietas, grib, tēt, izskatās, tiek, noteikti, nozīmē, kamēr, divi, it, tāpat, + tāda, ilgi, katru, dēls, noticis, jauki, redzēt, pareizi, lūk, kundze, aiz, iespējams, + pateikt, nebūtu, gandrīz, vīrs, cilvēks, ātri, žēl, pasaules, rokas, liekas, palīdzēt, + līdzi, visas, saki, negribu, vietā, gadus, starp, skaties, tomēr, tūlīt, džek, nevajag, + sev, vajadzētu, būšu, dzīvi, droši, gadu, priekšu, skaidrs, gribēju, nāk, paskaties, mazliet, + tikko, nebūs, augšā, ceru, joprojām, nevis, ātrāk, ļauj, gribētu, liels, zina, vārdu, reizi, + pasaulē, savā, sveiki, dienas, miris, dod, priekšā, galā, klau, cilvēkiem, tavas, patiesībā, + visa, vārds, gatavs, durvis, velns, nedaudz, naudas, redzēju, velna, manā, drīz, pāri, dzīve, + vēlies, nemaz, priekš, bērni, vieta, pāris, darbu, vajadzīgs, tālāk, rīt, roku, klāt, grūti, + beidz, laba, klausies, dara, varat, sveika, biji, vismaz, kopš, redzu, saproti, kura, draugi, + zemes, šovakar, patiešām, kaa, vietu, dieva, vajadzēja, mašīnu, lejā, saku, ceļu, gada, tādēļ, + cauri, runāt, ņem, oh, divas, lieta, tikt, šie, teici, vēlāk, vaļā, nogalināt, redzējis, jāiet, + nespēju, savus, atceries, ūdens, šejienes, labu, diena, mīļā, atvaino, doties, atrast, saprotu, + abi, reiz, jādara, nesaprotu, meitene, darbs, nevari, tai, nedomāju, pilnīgi, nakti, nekādu, + pati, gadiem, vēlos, taa, kādas, cits, ejiet, pirmais, a, būsi, mamma, lietu, slikti, pašu, + acis, diezgan, pasaki, gadā, puiši, asv, sava, nost, cilvēkus, džeks, manuprāt, mājas, o, + bērns, leo, otru, nopietni, vecais, laukā, caur, dzīves, izdarīt, sieviete, vienalga, + nogalināja, dzīvo, kādreiz, čau, sirds, paliec, gribat, vēlreiz, kuras, mazais, vietas, + piedodiet, laipni, palikt, brauc, ei, the, paliek, apkārt, sievietes, tālu, garām, pirmo, + dzīvot, nāciet, runāju, kuri, tiks, jüs, ceļā, nauda, nevienam, māja, vienīgais, īsti, + sapratu, gluži, svarīgi, atvainojos, i, sen, iespēja, tavā, pavisam, nāves, māte, citi, + viegli, zem, notiks, darba, nepatīk, daži, galvu, dienā, hallo, bērnu, neesam, kungi, beidzot, + nedrīkst, vajadzēs, māju, sieva, kādam, puika, kļūst, prieks, esot, iesim, daļa, pasaule, + pietiek, visā, saviem, rīta, pagaidiet, tētis, mājā, mieru, vīru, palīdzību, dzirdēju, + tādas, dzīvs, strādā, tām, vēlas, nakts, īpaši, jūtos, nolādēts, meitenes, pusi, mammu, mees, + aizveries, vispār, dzīvību, kurā, kādā, vārdā, mašīna, būsim, vispirms, vinji, nevienu, šos, + tiksimies, džeik, vinjsh, vaina, turpini, kādi, jaunu, tuvu, atradu, vēlu, varēja, citādi, šim, + satikt, neuztraucies, pārliecināts, liec, diez, liela, doktor, nevaram, palīdzi, uzmanīgi, dažas, + šiem, atgriezies, gribēja, priecājos, parasti, valsts, asinis, tēti, you, mierā, piemēram, + jautājums, atā, bijām, zemē, pasauli, spēlē, blakus, izskaties, pirmā, nomira, paši, šobrīd, + daru, gaida, tādi, iešu, labākais, jauks, maz, pieder, jauns, nezināju, uzmanību, skaista, + prātā, brālis, patiesību, mierīgi, šai, dr, patiesi, jēzus, mārtij, zināju, suns, juus, sievu, + dzirdi, tepat, mamm, tēvu, tēva, frodo, sasodīts, desmit, stundas, tavi, mazā, džon, cita, + vajadzīga, forši, minūtes, mīlestība, nebiju, saprast, izbeidz, šoreiz, labā, dāmas, kurienes, + problēma, šādi, spēj, gadījumā, tiesa, kuģi, pēdējā, tici, esiet, atceros, katrs, nee, palīgā, + mister, liek, likās, domāt, vīri, pēdējo, traks, reizes, vienīgā, tiesības, skolā, turies, beigas, + karš, pīter, uguni, pietiks, vienam, vienā, pakaļ, jauna, zemi, puisis, ziniet, negribi, labrīt, + ap, cilvēka, draugu, atver, nezini, sāra, vēlaties, gadi, dažreiz, rokās, dabūt, nomierinies, + istabā, agrāk, ieroci, savām, meiteni, paņem, meklē, pār, seju, ziņu, dzirdējis, zinām, gatavi, + braukt, sāka, sāk, dievam, neesat, dzirdēt, spēle, bērniem, izdarīja, muļķības, doma, pēdējais, + dīvaini, atdod, ziņas, bankas, darāt, vakar, ceļš, neviena, brāli, otrā, atgriezties, galvas, + pietiekami, gulēt, uzreiz, iespēju, bijusi, karalis, bobij, šrek, tikpat, palīdziet, durvīm, + vecāki, atrodas, smieklīgi, kuģa, bail, godīgi, pēkšņi, nedēļas, māsa, skrien, ceļa, džeims, gars, + lielu, mašīnā, bojā, kurieni, ļaudis, dārgais, vecs, ūdeni, kūper, eju, mašīnas, ideja, kājas, + spēles, galvenais, citiem, jātiek, skaisti, nāvi, vinju, problēmas, vērts, drīkstu, domājat, visur, + bieži, manai, citas, apsolu, zelta, strādāju, dzimšanas, jūtu, naktī, dārgā, atbildi, noticēt, + klājas, izdevās, dok, redzat, gana, divus, ģimene, runa, stāsts, braucam, brīnišķīgi, ģimenes, + kuģis, čārlij, hey, kä, sheit, ved, atrada, mirusi, meita, paklau, nevēlos, bērnus, boss, kaptein, + nekāda, roze, nespēj, vīrietis, brīdi, īsts, dzīvē, tādā, manī, jūras, jaunkundz, iemesls, sakot, + manam, daudzi, varēsi, pateicos, jaunais, policija, pilnībā, nekur, jauka, nedari, kurus, zināms, + jautājumu, seko, re, padomā, pusē, visām, mīļais, dolāru, gadžet, katram, izdarīji, šīm, vienīgi, + mirt, apmēram, spēku, jauno, mr, celies, iepriekš, prātu, vēlētos, četri, lietām, redzēji, nevajadzētu, + donna, jaa, ticu, minūtēm, sievieti, nāve, jūties, nezina, parādi, malā, redz, uh, gredzenu, uzmanies, + kara, drošībā, sapnis, bijāt, grāmatu, slepkava, vinja, paga, pieci, pilsētā, drošs, pateikšu, gāja, + spēli, beigās, hanna, princese, jebkad, dakter, veids, palīdzība, stāstu, izmantot, spēlēt, gaisā, + darīšu, došos, dodas, kreisi, negribēju, mazāk, pastāsti, tak, devās, sirdi, misis, vis, patiesība, + veidā, harijs, cenšos, tuvāk, kurp, klausieties, sāp, ļaujiet, neticami, kungu, sīkais, iedomāties, + daļu, mazs, iedod, mazo, meklēju, parunāt, jādodas, sevis, pārējie, veicas, otra, mīlestību, zēns, + dodies, galam, sem, bīstami, zvēru, iespējas, maza, ellē, virs, nekādas, maniem, skatieties, šonakt, + svēto, kapteinis, iepazīties, pazīstu, turp, gredzens, nepareizi, lieliska, īstais, pagaidām, kājām, + mirklīti, pašlaik, d, poter, saprati, aprunāties, paša, šejieni, interesanti, nevarētu, pašā, paskat, + bailes, skolas, vārdus, aizmirsti, gaismas, kāp, zēni, darīsim, pašam, beidzies, sauca, māti, akmens, + grāmatas, diemžēl, tevī, kļūt, endij, patika, nabaga, tuvojas, tēvoci, dienām, plāns +""" + +# Source: https://www.101languages.net/polish/most-common-polish-words/ +alias words_pl = """ +nie, to, się, w, na, i, z, co, jest, że, do, tak, jak, o, mnie, a, ale, mi, za, ja, ci, tu, ty, czy, +tym, go, tego, tylko, jestem, po, cię, ma, już, mam, jesteś, może, pan, dla, coś, dobrze, wiem, jeśli, +teraz, proszę, od, wszystko, tam, więc, masz, nic, on, być, gdzie, będzie, są, ten, mogę, ciebie, +bardzo, sobie, kiedy, ze, wiesz, no, jej, jeszcze, pani, był, mój, chcę, było, dlaczego, by, przez, +nas, tutaj, chcesz, jego, ją, ich, nigdy, żeby, też, kto, naprawdę, przepraszam, bo, mamy, porządku, +możesz, dobra, mu, dziękuję, ona, domu, panie, muszę, nawet, chyba, hej, właśnie, prawda, zrobić, te, +zawsze, będę, moja, gdy, je, trochę, nam, moje, cześć, bez, nim, była, tej, jesteśmy, dalej, pana, +dzięki, wszyscy, musisz, twój, lat, tobą, więcej, ktoś, czas, ta, który, chce, powiedzieć, chodź, dobry, +mną, niech, sam, razem, chodzi, czego, boże, stało, musimy, raz, albo, prostu, będziesz, dzień, możemy, +was, myślę, czym, daj, lepiej, czemu, ludzie, ok, przed, życie, ludzi, robisz, my, niż, tych, kim, rzeczy, +myślisz, powiedz, przy, twoja, oni, oczywiście, nikt, siebie, stąd, niego, twoje, miał, jeden, mówi, +powiedział, moim, czasu, u, dziś, im, które, musi, wtedy, taki, aby, pod, dwa, temu, pewnie, takie, cóż, +wszystkie, mojego, dużo, cholera, kurwa, wie, znaczy, wygląda, dzieje, mieć, ile, iść, potem, będziemy, +dzieci, dlatego, cały, byłem, moją, skąd, szybko, jako, kochanie, stary, trzeba, miejsce, myśli, można, +sie, jasne, mojej, wam, swoje, zaraz, wiele, nią, rozumiem, nich, wszystkich, jakieś, jakiś, kocham, idź, +tę, mają, mówię, mówisz, dzisiaj, nad, pomóc, takiego, przestań, tobie, jutro, robić, jaki, mamo, kilka, +przykro, wiedzieć, ojciec, widzisz, zbyt, zobaczyć, która, ani, tyle, trzy, tą, sposób, miałem, tato, niej, +później, pieniądze, robi, kogoś, kiedyś, zanim, widzę, pracy, świetnie, pewno, myślałem, będą, bardziej, +życia, długo, och, sir, ponieważ, aż, dni, nocy, każdy, dnia, znowu, oh, chciałem, taka, swoją, twoim, +widziałem, stanie, powiem, imię, wy, żebyś, nadzieję, twojej, panu, spokój, słuchaj, rację, spójrz, razie, +znam, pierwszy, koniec, chciałbym, we, nami, jakie, posłuchaj, problem, przecież, dobre, nasz, dziecko, drzwi, +nasze, miło, czuję, mógł, żyje, jeżeli, człowiek, powiedziałem, gdyby, roku, dom, sama, potrzebuję, +wszystkim, zostać, wciąż, dokładnie, mama, którzy, mówić, zamknij, mów, twoją, chwilę, zrobił, samo, idziemy, +nadal, jesteście, zabić, były, sobą, kogo, lub, lubię, the, podoba, minut, bym, chciał, bądź, czegoś, gdzieś, +mówiłem, chodźmy, znaleźć, poza, spokojnie, wcześniej, został, rozumiesz, mogą, prawie, wydaje, miała, mały, +byłeś, facet, zrobię, macie, żadnych, razy, noc, ciągle, broń, moich, twojego, końcu, pomocy, czekaj, znasz, +oczy, weź, idę, halo, dość, innego, pomysł, jakby, trzymaj, jedno, ojca, porozmawiać, pamiętasz, lata, +powinieneś, którą, powodu, takim, niczego, powinniśmy, oto, napisy, jednak, świat, pokoju, żebym, sprawy, +dwie, samochód, swój, wystarczy, pewien, źle, pozwól, numer, jedną, miejscu, you, drogi, byłam, dokąd, miłość, +panowie, pieniędzy, którego, matka, rano, dwóch, całe, patrz, rzecz, nowy, idzie, wyglądasz, bóg, byś, życiu, +nimi, nikogo, całą, swojego, świecie, sprawa, dziewczyna, prawo, byli, zostaw, wiedziałem, jedna, widzieć, +swoim, kobiety, uważaj, najpierw, właściwie, dam, również, diabła, chcą, którym, zrób, da, jednego, dać, +musiał, ręce, powinienem, których, znów, powiedziała, wczoraj, czujesz, zaczekaj, sądzę, śmierć, mówił, +podczas, której, całkiem, pracę, żona, pójdę, pamiętam, powiedziałeś, mówią, wiemy, jezu, witam, cholery, +swoich, telefon, wielu, także, poważnie, skoro, miejsca, robię, śmierci, słyszałem, wina, zrobiłem, dobranoc, +parę, prawdę, swojej, serce, inaczej, dziewczyny, kobieta, powiesz, martw, rób, pytanie, pięć, innych, one, +gra, natychmiast, wrócić, szybciej, jednym, cokolwiek, wierzę, wcale, wieczór, ważne, człowieka, wielki, nowa, +dopiero, ziemi, gdybym, tata, poznać, stać, jack, myślałam, witaj, słowa, zrobiłeś, gówno, john, dolarów, +sprawę, inne, idziesz, miałam, wiecie, chciałam, zobaczenia, widziałeś, żyć, każdym, nasza, panią, wspaniale, +chwili, każdego, nowego, nieźle, takich, między, dostać, powinien, dawaj, dopóki, naszych, naszej, świata, +chłopaki, chcemy, poczekaj, jaką, człowieku, czasem, żadnego, inny, przynajmniej, nazywa, super, naszego, +szczęście, potrzebuje, godziny, zabrać, powrotem, syn, lecz, słucham, twoich, udało, boga, pokój, działa, +ogóle, naszym, szkoły, możliwe, wiedział, wyjść, wszystkiego, byłoby, daleko, wieczorem, skarbie, jaka, +mógłbym, ostatni, możecie, cztery, doktorze, zrobimy, mąż, przeciwko, zgadza, zrobisz, czasie, czasami, +brzmi, raczej, ciało, należy, miasta, miałeś, taką, brat, cieszę, rozmawiać, cała, czymś, wybacz, twarz, +mała, chcecie, dr, pojęcia, lubisz, głowę, najbardziej, dziwne, głowy, wody, pół, wiadomość, policja, +strony, l, pl, mogłem, mieli, widzenia, pewna, ruszaj, wracaj, ode, popatrz, końca, plan, kiedykolwiek, +wejść, została, rok, syna, uda, wrócę, zewnątrz, droga, uwierzyć, późno, zostało, zostanie, zły, kapitanie, +potrzebujemy, byliśmy, zobaczymy, gotowy, obchodzi, jechać, rodziny, widziałam, drodze, czeka, środku, film, +spać, człowiekiem, zupełnie, taa, pomóż, mieliśmy, pomoc, słowo, innym, ostatnio, and, zna, mogła, pójść, +chłopcy, wziąć, mógłbyś, tłumaczenie, potrzebujesz, słyszysz, blisko, godzin, miłości, góry, zabił, piękna, +napisów, pokaż, moi, lubi, robota, prawa, ciężko, kimś, dół, rękę, nazywam, wielkie, część, wkrótce, naszą, +jedziemy, zapomnij, prosto, radę, robimy, powinnaś, gdybyś, chociaż, zależy, stronie, wypadek, tydzień, byłaś, +nowe, małe, praca, drogę, chłopak, zrobi, widział, mieście, synu, oznacza, krew, mógłby, krwi, górę, joe, wasza, +robią, tędy, wszędzie, temat, pierwsze, zobacz, ponad, kraju, mało, racja, tymi, cicho, chciała, powiedziałam, +leci, powinno, mówiąc, serca, chciałabym, miasto, george, spotkać, mniej, e, przyjaciel, mówiłeś, kłopoty, +miesięcy, jakąś, żaden, zostań, roboty, zatrzymać, frank, nieważne, głupi, pa, koleś, sprawie, spotkanie, ojcze, +pewnego, spróbuj, drugi, znalazłem, pracować, całym, zostały, złe, niemożliwe, jakoś, zdjęcia, stronę, wiedzą, it, +dziewczynę, zaczyna, mogli, samego, sądzisz, rodzina, razu, trudno, samochodu, okay, boję, szkoda, wami, charlie, +dał, środka, ojcem, piękne, dawno, choć, panem, przykład, nagle, bracie, żadnej, drugiej, przyjaciół, otwórz, +myśleć, doktor, chwileczkę, pracuje, najlepszy, brata, czyż, często, http, powinnam, odejść, trzech, chodźcie, +nazwisko, szansę, ciała, policji, szkole, prawdopodobnie, serio, matki, org, wolno, sami, muszą, zabierz, +słyszałeś, siostra, uspokój, wystarczająco, początku, faceta, problemy, szefie, broni, me, zostawić, czuje, +będziecie, przyszedł, wiedziałam, kilku, inni, b, głowie, historia, według, www, wezmę, nowym, czekać, stój, +mężczyzna, mówiłam, pokazać, około, wracam, wieku, jakaś, pierwsza, niczym, zabiję, zdjęcie, zabawne, rodzice, +musiałem, całkowicie, sprawdzić, mike, przyjdzie, sześć, kupić, dobrym, żonę, dasz, pomoże, nogi, obok, ruszać, +trzymać, zadzwonić, panno, godzinę, boli, oraz, spokoju, walczyć, wróci, tom, wspólnego, zmienić, ostatnie, uwagę, +znać, jednej, dłużej, powie, pogadać, łatwo, większość, nikomu, michael, córka, niedługo, powodzenia, tygodniu, +włosy, niestety, górze, kochasz, prawdziwy, historii, ulicy, musicie, gotowi, chwila, samym, grać, zadzwonię, +strasznie, mieszka, kocha, rady, tyłu, jakim, obiecuję, tysięcy, pomyślałem, pracuję, jedynie, pozwolić, uwaga, +proste, zacząć, myśl, wstawaj, rany, prawdziwe, takiej, jakiegoś, umrzeć, złego, okazji +""" + +# Source: https://www.101languages.net/greek/most-common-greek-words/ +alias words_el = """ + να, το, δεν, θα, είναι, και, μου, με, ο, για, την, σου, τα, τον, η, τι, σε, που, του, αυτό, στο, ότι, + από, τη, της, ναι, σας, ένα, εδώ, τους, αν, όχι, μια, μας, είσαι, αλλά, κι, οι, πρέπει, είμαι, ήταν, + πολύ, στην, δε, γιατί, εγώ, τώρα, πως, εντάξει, τις, κάτι, ξέρω, μην, έχει, έχω, εσύ, θέλω, καλά, + έτσι, στη, στον, αυτή, ξέρεις, κάνεις, εκεί, σαν, μόνο, μπορώ, όταν, έχεις, μαζί, πώς, τίποτα, + ευχαριστώ, όλα, κάνω, πάμε, ή, ποτέ, τόσο, πού, αυτά, έλα, στα, μέσα, κάνει, των, μπορεί, κύριε, πιο, + σπίτι, παρακαλώ, λοιπόν, μπορείς, αυτός, υπάρχει, ακόμα, πίσω, λίγο, πάντα, είμαστε, γεια, τότε, + ειναι, μετά, πω, έχουμε, μη, ένας, ποιος, νομίζω, πριν, απλά, δω, δουλειά, παιδιά, οχι, αλήθεια, + όλοι, ίσως, λες, όπως, ας, θέλεις, μα, άλλο, είπε, ζωή, πάω, δύο, ωραία, έναν, καλό, απο, κάνουμε, + έξω, κοίτα, είχε, στις, πάνω, είπα, πες, χρόνια, ούτε, κάτω, είστε, ώρα, θες, σένα, έχουν, γυναίκα, + μένα, μέρα, καλή, φορά, όμως, κανείς, κάθε, ε, οτι, αρέσει, ήμουν, μέχρι, δυο, είχα, μαμά, χωρίς, + καλύτερα, πας, πράγματα, πάει, σήμερα, κάποιος, ήθελα, θέλει, θεέ, έπρεπε, λέει, μία, σωστά, αυτόν, + μπορούμε, συμβαίνει, ακριβώς, έγινε, πόσο, επειδή, λεφτά, πολλά, μόλις, εμένα, λένε, πεις, συγγνώμη, + γρήγορα, ω, έκανε, λυπάμαι, γίνει, παιδί, περίμενε, έκανα, φίλε, βλέπω, μέρος, στιγμή, φαίνεται, + πρόβλημα, άλλη, είπες, φυσικά, κάποιον, όσο, πήγαινε, πάλι, λάθος, ως, έχετε, εσένα, πράγμα, κυρία, + χρόνο, στους, πάρω, μπαμπά, δικό, απ, γίνεται, εσείς, λέω, συγνώμη, όλο, μητέρα, έκανες, πιστεύω, + ήσουν, κάποια, σίγουρα, υπάρχουν, όλη, ενα, αυτο, ξέρει, μωρό, ιδέα, δει, μάλλον, ίδιο, πάρε, είδα, + αύριο, βλέπεις, νέα, κόσμο, νομίζεις, τί, εμείς, σταμάτα, πάρει, αγάπη, πατέρας, όλους, αρκετά, + χρειάζεται, καιρό, φορές, κάνουν, ακόμη, α, πατέρα, προς, αμέσως, πια, ηταν, χαρά, απόψε, όνομα, + μάλιστα, μόνος, μεγάλη, κανένα, ελα, πραγματικά, αυτοί, πει, πότε, εχω, βράδυ, αυτές, θέλετε, κάνετε, + σημαίνει, πρώτη, ποιο, πόλη, μπορούσα, ποια, γαμώτο, ήδη, τελευταία, άνθρωποι, τέλος, απλώς, νόμιζα, + ξέρετε, μέρες, δεις, θέση, αυτούς, καταλαβαίνω, φύγε, χέρια, εκτός, ήξερα, οπότε, λεπτά, μακριά, + κάνε, αμάξι, δική, λεπτό, μεγάλο, μήπως, κορίτσι, μάτια, ελάτε, πρόκειται, πόρτα, δίκιο, βοήθεια, + ήρθε, μιλήσω, δρόμο, εαυτό, καθόλου, ορίστε, βρω, πειράζει, μπορείτε, καλός, πέρα, κοντά, εννοώ, + τέτοιο, μπροστά, έρθει, χρειάζομαι, χέρι, ελπίζω, δώσε, διάολο, φύγω, ιστορία, όπλο, αφού, πρωί, + νύχτα, ωραίο, τύπος, ξανά, θυμάσαι, δούμε, κατά, εννοείς, αγαπώ, κακό, θέμα, εδω, αυτήν, τρόπο, + κεφάλι, είχες, μερικές, μιλάς, φίλος, άνθρωπος, φύγουμε, όλες, σκατά, ανθρώπους, βέβαια, άντρας, + κάποιο, πάνε, αστυνομία, αλλιώς, συνέβη, χαίρομαι, άλλα, περισσότερο, καλύτερο, εκείνη, πάρεις, τo, + νερό, ώρες, σίγουρος, vα, τρεις, εχεις, πρώτα, μπορούσε, σ, οταν, δρ, πιστεύεις, μόνη, ποιός, καμιά, + κανέναν, πέθανε, εχει, ετσι, αγόρι, ανησυχείς, άντρες, δωμάτιο, ομάδα, ίδια, εμπρός, βρούμε, βοηθήσω, + τέτοια, πήρε, τρία, λόγο, μικρό, αντίο, o, πέντε, πήγε, καν, ευκαιρία, είδες, έρχεται, δηλαδή, + αργότερα, ήθελε, πούμε, λέμε, όπου, αλλα, κόρη, κόσμος, γυναίκες, τηλέφωνο, εάν, δώσω, καρδιά, βρήκα, + γραφείο, επίσης, νιώθω, σχέση, θέλουν, ισως, τέλεια, είχαμε, κάπου, μυαλό, ώστε, καλημέρα, σχολείο, + θεός, μικρή, τρέχει, ψέματα, ξέρουμε, οικογένεια, εισαι, θυμάμαι, κ, ενός, φίλοι, πρόσεχε, + καταλαβαίνεις, αργά, ντε, θέλουμε, σύντομα, πήρα, σχεδόν, παιχνίδι, κύριοι, γειά, μήνες, μπαμπάς, + σοβαρά, δολάρια, τουλάχιστον, χρήματα, πείτε, πόδια, αίμα, κοπέλα, φαγητό, ειμαι, ποιον, μερικά, + δύσκολο, μπορούν, βρεις, όμορφη, φύγεις, τύχη, πλάκα, έρθεις, άντρα, κορίτσια, μείνε, αστείο, καμία, + είχαν, χάρη, άλλος, πρεπει, σημασία, φυλακή, νεκρός, συγχωρείτε, φοβάμαι, μπράβο, γύρω, κανένας, μεταξύ, + τ, χθες, πολλές, όνομά, τζακ, ρε, καληνύχτα, πολυ, φύγει, αφήσω, ήθελες, tι, ήρθες, ακούς, πρώτο, γιατι, + ηρέμησε, γι, πάρουμε, πάρα, άλλους, κατάλαβα, έρθω, συνέχεια, έλεγα, γλυκιά, νοιάζει, χριστέ, βιβλίο, + κύριος, μ, χώρα, αρχή, ήρθα, πεθάνει, γη, έτοιμος, εγω, άσχημα, συμβεί, αυτοκίνητο, ζωής, τελικά, φέρω, + τρόπος, κατάσταση, www, περιμένω, σημαντικό, όσα, σκέφτηκα, μιλήσουμε, αφήστε, τωρα, ακούω, γιος, σκοτώσω, + δύναμη, κα, κε, εκείνο, γονείς, μιλάω, σκοτώσει, ολα, μείνει, μείνω, αρέσουν, δεv, υπόθεση, φίλους, όπλα, + υποθέτω, εμάς, ενώ, έξι, σχέδιο, άρεσε, καφέ, σκότωσε, χρειαζόμαστε, φίλο, σωστό, προσπαθώ, κάναμε, + κοιτάξτε, μoυ, κου, ποτό, εσάς, έι, έφυγε, ταινία, μοιάζει, κρεβάτι, εχουμε, περιμένει, νέο, μπορούσες, + μάθω, αφήσεις, περιμένετε, χρειάζεσαι, υπήρχε, μισό, δέκα, αφεντικό, περίπου, άλλοι, λόγος, ξέρουν, κάποτε, + βρήκες, καλύτερη, υπέροχο, τζον, δίπλα, σκάσε, θεού, άκουσα, φύγετε, λέξη, παρά, επόμενη, λέτε, περάσει, + πόσα, γίνεις, σώμα, ν, πήρες, τελείωσε, γιο, ρούχα, σκέφτομαι, εσυ, άλλες, γυρίσω, βάλω, μουσική, ραντεβού, + φωτιά, έδωσε, πάτε, φοβάσαι, βρει, δείξω, γίνω, βοηθήσει, τύπο, σειρά, αξίζει, μείνεις, είπαν, άλλον, + κυρίες, λίγη, πέρασε, κάτσε, πήγα, δείτε, μιας, βδομάδα, έρχομαι, προσοχή, εύκολο, ερώτηση, υπέροχα, + σίγουρη, νοσοκομείο, τρελός, ενας, βάλε, πόλεμο, φέρε, δικά, τιμή, κατάλαβες, ταξίδι, οποίο, δουλεύει, θεό, + μικρέ, μάθεις, βρίσκεται, πολλοί, δες, πάρτε, παντού, πρόσωπο, μήνυμα, αδερφή, μιλάει, παλιά, πουθενά, + κράτα, περίπτωση, φως, επάνω, έλεγε, συμφωνία, οπως, ολοι, πρώτος, δεσποινίς, γιατρός, γνωρίζω, σαμ, + σκέφτεσαι, ει, φίλη, σεξ, έκαναν, προβλήματα, κάπως, ό, τελευταίο, ακούσει, τζο, καλώς, επιλογή, + σταματήστε, τόσα, οτιδήποτε, περισσότερα, άδεια, πάρτι, περίμενα, ακούγεται, gmteam, ήξερες, καιρός, + μαλλιά, καλύτερος, κανεις, φρανκ, μέση, συνέχισε, τίποτε, φωτογραφία, κατι, μεγάλος, περιοχή, άσε, καθώς, + είδε, λόγια, μήνα, μαλακίες, όμορφο, δώρο, στόμα, χάλια, εντελώς, μακάρι, τελειώσει, γνώμη, γιατρέ, ξερω, + πλευρά, μέλλον, θάνατο, νιώθεις, έτοιμοι, κομμάτι, μάθει, μιλάμε, ψηλά, αέρα, ερωτήσεις, αυτού, δώσει, + φεύγω, σημείο, τηλεόραση, κυριε, πραγματικότητα, ανάγκη, βοηθήσεις, προσπάθησε, γύρνα, άφησε, λίγα, κάντε, + είvαι, βλέπετε, αυτη, δείπνο, επιτέλους, κέντρο, περίεργο, ακούστε, πλοίο, κάποιες, δικός, σoυ, οικογένειά, + μιλήσει, πλέον, υπόσχομαι, περιμένεις, ήξερε, σκοτώσεις, ενταξει, δώσεις, εκει, ήμασταν, έρχονται, κώλο, + ρωτήσω, παίρνει, σιγά, σήκω, στοιχεία, αδελφή, βασικά, μένει, άκρη, πηγαίνετε, παίρνεις, tο, περιμένουμε, + συγχωρείς, μικρός, πόδι, δίνει, εκατομμύρια, ξενοδοχείο, αποστολή, ενδιαφέρον, χάρηκα, αεροπλάνο, γάμο, + χιλιάδες, υόρκη, οκ, ευχαριστούμε, καλα, κοιτάς, σα, π, χρόνος, ησυχία, ασφάλεια, εκείνος, a, βρήκε, + τέσσερα, βγάλω, μπες, συχνά, ημέρα, μάνα, εν, αγαπάς, άνθρωπο, γραμμή, φωτογραφίες, προσέχεις, ύπνο, + μυστικό, σχετικά, είδους, σκέψου, χριστούγεννα, κόσμου, τομ, μισώ, σύστημα, δουλειές, τελείως, πεθάνω, + αλλάξει, δεξιά, συνήθως, δουλεύω, μάικλ, εβδομάδα, νούμερο, λείπει, έτοιμη, τμήμα, βγει, ψυχή, έπεσε, + κάθαρμα, ματιά, οποία, πληροφορίες, μονο, κρίμα, τραγούδι, μαγαζί, δουλεύεις, μαζι, τέλειο, κύριο, + λέγεται, τσάρλι, πεθάνεις, σκεφτόμουν, καλησπέρα, συγχαρητήρια, φωνή, εκ, άτομο, παίζεις, σκάφος, + φαίνεσαι, ξαφνικά, παραπάνω, ατύχημα, θελω, ξέχνα, ήρθατε, εναντίον, τραπέζι, γράμμα, μείνετε, αμερική, + βασιλιάς, υπό, μπάνιο, ποτε, ίδιος, προφανώς, μαλάκα, αδερφός, άνδρες, nαι, χρονών, ναί, κλειδί, δις, + γιαγιά, παράξενο, πτώμα, βρήκαμε, μιλήσεις, υποτίθεται, ορκίζομαι, δυνατά, ποιό, θάλασσα, παίρνω, άκουσες, + παρέα, αριστερά, έμαθα, μάχη, μηχανή, σάρα, ζωντανός, όνειρο, παλιό, μπορούσαμε, πάντως, ανάμεσα, έχασα, + νωρίς, κάποιοι, άκου, παίζει, φτάνει, δίνω, βγες, υπέροχη, νόημα, έλεγχο, μέτρα, ξερεις, ζει, δείχνει, + βρες, τού +""" + +# Source: https://www.101languages.net/russian/most-common-russian-words/ +alias words_ru = """ +я, не, что, в, и, ты, это, на, с, он, вы, как, мы, да, а, мне, меня, у, нет, так, но, то, все, тебя, его, +за, о, она, тебе, если, они, бы, же, ну, здесь, к, из, есть, чтобы, для, хорошо, когда, вас, только, по, +вот, просто, был, знаю, нас, всё, было, от, может, кто, вам, очень, их, там, будет, уже, почему, еще, +быть, где, спасибо, ничего, сейчас, или, могу, хочу, нам, чем, мой, до, надо, этого, ее, теперь, давай, +знаешь, нужно, больше, этом, нибудь, раз, со, была, этот, ему, ладно, эй, время, тоже, даже, хочешь, +сказал, ли, себя, думаю, пока, должен, потому, никогда, ни, тут, ещё, её, пожалуйста, сюда, привет, +тогда, конечно, моя, него, сегодня, один, тобой, правда, лучше, об, были, того, можно, мной, всегда, +сказать, день, сэр, без, можешь, чего, эти, дело, значит, лет, много, во, делать, буду, порядке, должны, +такой, ведь, ним, всего, сделать, хотел, твой, жизнь, ей, мистер, потом, через, себе, них, всех, такое, +им, куда, том, мама, после, человек, люди, слишком, иди, зачем, этим, немного, сколько, этой, знаете, +боже, ней, эту, который, отец, свою, деньги, два, под, твоя, мои, никто, моей, думаешь, друг, жизни, +эта, назад, видел, кажется, точно, вместе, люблю, мог, случилось, сам, нравится, черт, какой, людей, +папа, домой, тот, скажи, которые, должна, три, всем, сделал, возможно, прошу, будем, дома, парень, +снова, говорит, место, отсюда, можем, будешь, пошли, делаешь, совсем, говорил, понимаю, завтра, хочет, +простите, разве, давайте, хотите, отлично, сказала, туда, прямо, времени, вами, лишь, своей, хватит, +думал, можете, дом, дела, знать, дай, понял, помочь, говорить, слушай, свои, поэтому, прости, знает, +именно, знал, тем, кого, смотри, каждый, ваш, похоже, найти, моего, наш, мать, одна, имя, про, говорю, +будут, оно, свой, нельзя, извините, стоит, действительно, зовут, поговорить, доктор, перед, несколько, +нужен, происходит, ко, господи, возьми, мою, тех, нами, вижу, должно, наверное, откуда, понимаешь, верно, +скоро, уж, деле, твои, пусть, всю, хотела, при, более, ребята, нее, быстро, подожди, идти, надеюсь, чём, +работу, видеть, такая, этих, уверен, нужна, года, раньше, такие, руки, видишь, какая, посмотри, сын, +самом, ваша, послушай, равно, наши, другой, ага, мир, извини, минут, против, твоей, пор, жить, ж, жаль, +вообще, могли, хотя, человека, пора, ради, говорят, почти, твою, могут, над, весь, первый, чёрт, слышал, +собой, брат, вещи, дня, скажу, говоришь, нормально, своего, мое, ваше, итак, будь, ночь, хоть, ясно, +плохо, дверь, вопрос, господин, давно, денег, ваши, ка, мисс, одну, глаза, пять, будто, между, пойду, +опять, работа, самое, иногда, детей, этому, рад, здорово, бог, одного, ночи, готов, номер, которая, +машину, любовь, дорогая, виду, одно, прекрасно, вон, своих, быстрее, отца, женщина, достаточно, рядом, +убить, таким, пойдем, смерти, дети, такого, правильно, месте, никаких, сказали, здравствуйте, пару, две, +видела, долго, хороший, ах, кроме, алло, нашей, прав, вчера, вечером, жена, миссис, чтоб, друга, нужны, +кем, какие, те, увидеть, утро, смогу, неё, сама, моему, большой, сразу, работать, сердце, стал, своим, +сначала, могла, вроде, ними, говори, голову, дальше, помнишь, либо, ума, одной, вечер, случае, взять, +проблемы, помощь, добрый, год, думала, делает, скорее, слова, капитан, последний, важно, дней, помню, +ночью, утром, моих, произошло, которую, боюсь, также, вашей, ой, стой, твоего, никого, дорогой, убил, +насчет, друзья, самый, проблема, видели, вперед, дерьмо, понятно, чувствую, наша, будете, тому, имею, +вернуться, придется, пришел, спать, стать, столько, говорила, пойти, иначе, работает, девушка, час, +момент, моим, умер, думаете, доброе, слово, новый, часов, мире, знаем, твое, мальчик, однажды, интересно, +конец, играть, a, заткнись, сделали, посмотреть, идет, узнать, свое, права, хорошая, город, джон, +долларов, парни, идем, говорите, уйти, понять, знала, поздно, нашли, работы, скажите, сделаю, увидимся, +какого, другие, идея, пошел, доме, дочь, имеет, приятно, лицо, наших, обо, понимаете, руку, часть, +смотрите, вся, собираюсь, четыре, прежде, хотят, скажешь, чувак, дайте, сделала, кофе, джек, верю, +ждать, затем, большое, сами, неужели, моё, любит, мужчина, дать, господа, таких, осталось, которой, +далеко, вернусь, сильно, ох, сможешь, кому, вашего, посмотрим, машина, подождите, свет, чуть, серьезно, +пришли, оружие, решил, смысле, видите, тихо, нашел, свидания, путь, той, совершенно, следующий, которого, +места, парня, вдруг, пути, мадам, какое, шанс, сестра, нашего, ужасно, минуту, вокруг, другом, иду, +других, хотели, нем, смерть, подумал, фильм, оставь, делаете, уверена, кровь, говорили, внимание, +помогите, идите, держи, получить, оба, взял, спокойно, обычно, мало, забыл, странно, смотреть, поехали, +дал, часа, прекрати, посмотрите, готовы, вернулся, поверить, позже, милая, женщины, любишь, довольно, +обратно, остаться, думать, та, стороны, полиция, тело, тысяч, делал, машины, угодно, муж, году, неплохо, +бога, некоторые, конце, милый, the, рождения, трудно, добро, любви, больно, невозможно, спокойной, +слышишь, типа, получил, которое, приятель, хуже, никому, честь, успокойся, вашу, маленький, выглядит, +чарли, сына, неделю, i, девочка, делаю, шесть, ноги, история, рассказать, послушайте, часто, кстати, +двух, забудь, которых, следует, знают, пришла, семья, станет, матери, ребенок, план, проблем, например, +сделай, воды, немедленно, мира, сэм, телефон, перестань, правду, второй, прощения, ту, наше, уходи, твоих, +помоги, пол, внутри, нему, смог, десять, нашу, около, бывает, самого, большая, леди, сможем, вниз, легко, +делай, единственный, рада, меньше, волнуйся, хотим, полагаю, мам, иметь, своими, мере, наконец, начала, +минутку, работе, пожаловать, другого, двое, никакого, честно, школе, лучший, умереть, дам, насколько, +всей, малыш, оставить, безопасности, ненавижу, школу, осторожно, сынок, джо, таки, пытался, другое, б, +клянусь, машине, недели, стало, истории, пришлось, выглядишь, чему, сможет, купить, слышала, знали, +настоящий, сих, выйти, людям, замечательно, полиции, огонь, пойдём, спросить, дядя, детка, среди, особенно, +твоим, комнате, шоу, выпить, постоянно, делают, позвольте, родители, письмо, городе, случай, месяцев, мужик, +благодарю, o, ребенка, смешно, ответ, города, образом, любой, полностью, увидел, еду, имени, вместо, +абсолютно, обязательно, улице, твоё, убили, ваших, ехать, крови, решение, вина, поможет, своё, секунду, +обещаю, начать, голос, вещь, друзей, показать, нечего, э, месяц, подарок, приехал, самая, молодец, сделаем, +крайней, женщин, собираешься, конца, страшно, новости, идиот, потерял, спасти, вернуть, узнал, слушайте, +хотелось, сон, поняла, прошло, комнату, семь, погоди, главное, рано, корабль, пытаюсь, игра, умерла, +повезло, всему, возьму, таком, моем, глаз, настолько, идём, удачи, готова, семьи, садись, гарри, держись, +звучит, мило, война, человеком, право, такую, вопросы, представить, работаю, имеешь, красивая, идёт, никакой, +профессор, думает, войны, стала, стали, оттуда, известно, слышу, начал, подумать, позвонить, старый, придётся, +историю, вести, твоему, последнее, хочется, миллионов, нашла, способ, отношения, земле, фрэнк, получится, +говоря, связи, многие, пошёл, пистолет, убью, руках, получилось, президент, остановить, тьi, оставил, одним, +you, утра, боль, хорошие, пришёл, открой, брось, вставай, находится, поговорим, кино, людьми, полицию, покажу, +волосы, последние, брата, месяца +""" + + +fn gen_word_pairs[words: String = words_en]() -> List[String]: + var result = List[String]() + try: + var list = words.split(",") + for w in list: + var w1 = w[].strip() + for w in list: + var w2 = w[].strip() + result.append(w1 + " " + w2) + except: + pass + return result + + +def dif_bits(i1: UInt, i2: UInt) -> Int: + return pop_count(i1 ^ i2) + + +def assert_dif_hashes(hashes: List[UInt], upper_bound: Int): + for i in range(len(hashes)): + for j in range(i + 1, len(hashes)): + var diff = dif_bits(hashes[i], hashes[j]) + assert_true( + diff > 14, + str("Index: {}:{}, diff between: {} and {} is: {}").format( + i, j, hashes[i], hashes[j], diff + ), + ) + + +def test_hash_byte_array(): + assert_equal(hash("a".unsafe_ptr(), 1), hash("a".unsafe_ptr(), 1)) + assert_equal( + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("a".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("a".unsafe_ptr(), 1), + ) + assert_not_equal( + hash("a".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("a".unsafe_ptr(), 1), + ) + assert_equal(hash("b".unsafe_ptr(), 1), hash("b".unsafe_ptr(), 1)) + assert_equal( + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("b".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("b".unsafe_ptr(), 1), + ) + assert_not_equal( + hash("b".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("b".unsafe_ptr(), 1), + ) + assert_equal(hash("c".unsafe_ptr(), 1), hash("c".unsafe_ptr(), 1)) + assert_equal( + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("c".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("c".unsafe_ptr(), 1), + ) + assert_not_equal( + hash("c".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("c".unsafe_ptr(), 1), + ) + assert_equal(hash("d".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1)) + assert_equal( + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("d".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("d".unsafe_ptr(), 1), + ) + assert_not_equal( + hash("d".unsafe_ptr(), 1), + hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("d".unsafe_ptr(), 1), + ) + + +def test_avalanche(): + # test that values which differ just in one bit, + # produce significatly different hash values + var data = UnsafePointer[UInt8].alloc(256) + memset_zero(data, 256) + var hashes0 = List[UInt]() + var hashes1 = List[UInt]() + hashes0.append(hash(data, 256)) + hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, 256)) + + for i in range(256): + memset_zero(data, 256) + var v = 1 << (i & 7) + data[i >> 3] = v + hashes0.append(hash(data, 256)) + hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, 256)) + + for i in range(len(hashes0)): + var diff = dif_bits(hashes0[i], hashes1[i]) + assert_true( + diff > 16, + str("Index: {}, diff between: {} and {} is: {}").format( + i, hashes0[i], hashes1[i], diff + ), + ) + + assert_dif_hashes(hashes0, 14) + assert_dif_hashes(hashes1, 15) + + +def test_trailing_zeros(): + # checks that a value with different amount of trailing zeros, + # results in significantly different hash values + var data = UnsafePointer[UInt8].alloc(8) + data[0] = 23 + var hashes0 = List[UInt]() + var hashes1 = List[UInt]() + for i in range(1, 9): + hashes0.append(hash(data, i)) + hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, i)) + + for i in range(len(hashes0)): + var diff = dif_bits(hashes0[i], hashes1[i]) + assert_true( + diff > 19, + str("Index: {}, diff between: {} and {} is: {}").format( + i, hashes0[i], hashes1[i], diff + ), + ) + + assert_dif_hashes(hashes0, 18) + assert_dif_hashes(hashes1, 18) + + +def assert_fill_factor[ + label: String +](words: List[String], num_buckets: Int, lower_bound: Float64): + # A perfect hash function is when the number of buckets is equal to number of words + # and the fill factor results in 1.0 + var buckets = List[Int](0) * num_buckets + for w in words: + var h = hash(w[].unsafe_ptr(), w[].byte_length()) + buckets[h % num_buckets] += 1 + var unfilled = 0 + for v in buckets: + if v[] == 0: + unfilled += 1 + + var fill_factor = 1 - unfilled / num_buckets + assert_true( + fill_factor >= lower_bound, + str("Fill factor for {} is {}, provided lower boound was {}").format( + label, fill_factor, lower_bound + ), + ) + + +def assert_fill_factor_old_hash[ + label: String +](words: List[String], num_buckets: Int, lower_bound: Float64): + # A perfect hash function is when the number of buckets is equal to number of words + # and the fill factor results in 1.0 + var buckets = List[Int](0) * num_buckets + for w in words: + var h = old_hash(w[].unsafe_ptr(), w[].byte_length()) + buckets[h % num_buckets] += 1 + var unfilled = 0 + for v in buckets: + if v[] == 0: + unfilled += 1 + + var fill_factor = 1 - unfilled / num_buckets + assert_true( + fill_factor >= lower_bound, + str("Fill factor for {} is {}, provided lower bound was {}").format( + label, fill_factor, lower_bound + ), + ) + + +def test_fill_factor(): + var words = List[String]() + + words = gen_word_pairs[words_ar]() + assert_fill_factor["AR"](words, len(words), 0.63) + assert_fill_factor["AR"](words, len(words) // 2, 0.86) + assert_fill_factor["AR"](words, len(words) // 4, 0.98) + assert_fill_factor["AR"](words, len(words) // 13, 1.0) + + assert_fill_factor_old_hash["AR"](words, len(words), 0.59) + + words = gen_word_pairs[words_el]() + assert_fill_factor["EL"](words, len(words), 0.63) + assert_fill_factor["EL"](words, len(words) // 2, 0.86) + assert_fill_factor["EL"](words, len(words) // 4, 0.98) + assert_fill_factor["EL"](words, len(words) // 12, 1.0) + + assert_fill_factor_old_hash["EL"](words, len(words), 0.015) + + words = gen_word_pairs[words_en]() + assert_fill_factor["EN"](words, len(words), 0.63) + assert_fill_factor["EN"](words, len(words) // 2, 0.85) + assert_fill_factor["EN"](words, len(words) // 4, 0.98) + assert_fill_factor["EN"](words, len(words) // 14, 1.0) + + assert_fill_factor_old_hash["EN"](words, len(words), 0.015) + + words = gen_word_pairs[words_he]() + assert_fill_factor["HE"](words, len(words), 0.63) + assert_fill_factor["HE"](words, len(words) // 2, 0.86) + assert_fill_factor["HE"](words, len(words) // 4, 0.98) + assert_fill_factor["HE"](words, len(words) // 14, 1.0) + + assert_fill_factor_old_hash["HE"](words, len(words), 0.2) + + words = gen_word_pairs[words_lv]() + assert_fill_factor["LV"](words, len(words), 0.63) + assert_fill_factor["LV"](words, len(words) // 2, 0.86) + assert_fill_factor["LV"](words, len(words) // 4, 0.98) + assert_fill_factor["LV"](words, len(words) // 12, 1.0) + + assert_fill_factor_old_hash["LV"](words, len(words), 0.015) + + words = gen_word_pairs[words_pl]() + assert_fill_factor["PL"](words, len(words), 0.63) + assert_fill_factor["PL"](words, len(words) // 2, 0.86) + assert_fill_factor["PL"](words, len(words) // 4, 0.98) + assert_fill_factor["PL"](words, len(words) // 13, 1.0) + + assert_fill_factor_old_hash["PL"](words, len(words), 0.015) + + words = gen_word_pairs[words_ru]() + assert_fill_factor["RU"](words, len(words), 0.63) + assert_fill_factor["RU"](words, len(words) // 2, 0.86) + assert_fill_factor["RU"](words, len(words) // 4, 0.98) + assert_fill_factor["RU"](words, len(words) // 13, 1.0) + + assert_fill_factor_old_hash["RU"](words, len(words), 0.015) + + +def main(): + test_hash_byte_array() + test_avalanche() + test_trailing_zeros() + test_fill_factor() From 46195f141284746425cc43f396b55a084344a39a Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:02:25 -0400 Subject: [PATCH 1675/2019] [External] [stdlib] Remove `add_to_path` from `hello_interop.mojo` (#48326) [External] [stdlib] Remove `add_to_path` from `hello_interop.mojo` Removes two `Python.add_to_path` from `hello_interop.mojo`. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3592 MODULAR_ORIG_COMMIT_REV_ID: 9a762fef6106e58ac077f899a1cb8dd301d4f0dc --- examples/hello_interop.mojo | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/hello_interop.mojo b/examples/hello_interop.mojo index 9dfbb1c1e7..3dbfbc56b6 100644 --- a/examples/hello_interop.mojo +++ b/examples/hello_interop.mojo @@ -22,7 +22,5 @@ def main(): print("Hello Mojo 🔥!") for x in range(9, 0, -3): print(x) - Python.add_to_path(".") - Python.add_to_path("./examples") var test_module = Python.import_module("simple_interop") test_module.test_interop_func() From fb3ce1fe196344706e1837eac1974972772d35ed Mon Sep 17 00:00:00 2001 From: soraros Date: Wed, 2 Oct 2024 15:05:21 -0400 Subject: [PATCH 1676/2019] [External] [stdlib] Add `UInt` ctor from `scalar` (#48240) [External] [stdlib] Add `UInt` ctor from `scalar` Co-authored-by: soraros Closes modularml/mojo#3571 MODULAR_ORIG_COMMIT_REV_ID: 9c0196f709f718ac857e82d81d4c879840a14cd6 --- stdlib/src/builtin/int.mojo | 16 ++++++++++------ stdlib/src/builtin/uint.mojo | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 9ee87ea89d..ea91a558b1 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement +from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from builtin.io import _snprintf @@ -315,6 +316,7 @@ struct Int( """ self = other + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.index): """Construct Int from the given index value. @@ -324,6 +326,7 @@ struct Int( """ self.value = value + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int16 value. @@ -331,12 +334,13 @@ struct Int( Args: value: The init value. """ - self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self = Self( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int32 value. @@ -344,12 +348,13 @@ struct Int( Args: value: The init value. """ - self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self = Self( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int64 value. @@ -357,12 +362,13 @@ struct Int( Args: value: The init value. """ - self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + self = Self( __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( value ) ) + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Index value. @@ -371,9 +377,7 @@ struct Int( value: The init value. """ self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( - __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( - value - ) + value ) @always_inline("nodebug") diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 8efc3cc4ef..bb9a685206 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -17,6 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import bitwidthof from utils._visualizers import lldb_formatter_wrapping_type +from builtin._documentation import doc_private from builtin.hash import _hash_simd @@ -56,6 +57,7 @@ struct UInt(IntLike): """Default constructor that produces zero.""" self.value = __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.index): """Construct UInt from the given index value. @@ -65,6 +67,18 @@ struct UInt(IntLike): """ self.value = value + @doc_private + @always_inline("nodebug") + fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + """Construct UInt from the given Index value. + + Args: + value: The init value. + """ + self.value = __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.index]( + value + ) + @always_inline("nodebug") fn __init__(inout self, value: Int): """Construct UInt from the given index value. From 2f148d476b15186bfc6796068049c1239c9e00c1 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 2 Oct 2024 13:20:27 -0600 Subject: [PATCH 1677/2019] [stdlib] Fix PyGILState_* signatures `PyGILState_Ensure` returns a `PyGILState_STATE` which is an opaque "handle" to the thread state, not a bool. Adjust the wrapper calls and the dual function: `PyGILState_Release` so it accepts the handle rather than the bool. Note the `PyGILState_STATE` is actually an enum in C and is four bytes (an `int`). So, model it as a struct with a value that is four bytes for ABI compatibility purposes. Getting these right will be important as we aim to fix our "respect the GIL" issues in python interop. MODULAR_ORIG_COMMIT_REV_ID: 542fcf89b830298314ce47629029861b946fa4d6 --- stdlib/src/python/_cpython.mojo | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 48aaaf181f..1c591e078d 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -62,6 +62,16 @@ alias METH_VARARGS = 0x1 alias destructor = fn (PyObjectPtr) -> None +# GIL +@value +@register_passable("trivial") +struct PyGILState_STATE: + var current_state: c_int + + alias PyGILState_LOCKED = c_int(0) + alias PyGILState_UNLOCKED = c_int(1) + + @value @register_passable("trivial") struct PyKeyValuePair: @@ -662,11 +672,15 @@ struct CPython: # Python GIL and threading # ===-------------------------------------------------------------------===# - fn PyGILState_Ensure(inout self) -> Bool: - return self.lib.get_function[fn () -> Bool]("PyGILState_Ensure")() + fn PyGILState_Ensure(inout self) -> PyGILState_STATE: + return self.lib.get_function[fn () -> PyGILState_STATE]( + "PyGILState_Ensure" + )() - fn PyGILState_Release(inout self, state: Bool): - self.lib.get_function[fn (Bool) -> None]("PyGILState_Release")(state) + fn PyGILState_Release(inout self, state: PyGILState_STATE): + self.lib.get_function[fn (PyGILState_STATE) -> None]( + "PyGILState_Release" + )(state) fn PyEval_SaveThread(inout self) -> Int64: return self.lib.get_function[fn () -> Int64]("PyEval_SaveThread")() From 9b1d0892e8947e5bdd7d3848784e06fd3a0a5a40 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 2 Oct 2024 14:47:27 -0600 Subject: [PATCH 1678/2019] [stdlib] Fix some uses of `Int` in `_cpython` wrappers Fix some incorrect uses of `Int` in favor of `c_int`, which is 32-bits on modern platforms, similar to `int` in the corresponding C code. MODULAR_ORIG_COMMIT_REV_ID: 55853f36d6a5e9a4f9a66172f617aa274cd8513a --- stdlib/src/python/_cpython.mojo | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 1c591e078d..06443f3460 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -77,7 +77,7 @@ struct PyGILState_STATE: struct PyKeyValuePair: var key: PyObjectPtr var value: PyObjectPtr - var position: Int + var position: c_int var success: Bool @@ -299,7 +299,7 @@ struct PyModuleDef_Base(Stringable, Representable, Formattable): var init_fn: Self._init_fn_type # The module's index into its interpreter's modules_by_index cache. - var index: Int + var index: Py_ssize_t # A copy of the module's __dict__ after the first time it was loaded. var dict_copy: UnsafePointer[PyObject] @@ -366,7 +366,7 @@ struct PyModuleDef_Base(Stringable, Representable, Formattable): # Ref: https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot @value struct PyModuleDef_Slot: - var slot: Int + var slot: c_int var value: OpaquePointer @@ -383,7 +383,7 @@ struct PyModuleDef(Stringable, Representable, Formattable): # Points to the contents of the docstring for the module. var docstring: UnsafePointer[c_char] - var size: Int + var size: Py_ssize_t # A pointer to a table of module-level functions. Can be null if there # are no functions present. @@ -392,13 +392,13 @@ struct PyModuleDef(Stringable, Representable, Formattable): var slots: UnsafePointer[PyModuleDef_Slot] # TODO(MOCO-1138): These are C ABI function pointers, not Mojo functions. - alias _visitproc_fn_type = fn (PyObjectPtr, OpaquePointer) -> Int + alias _visitproc_fn_type = fn (PyObjectPtr, OpaquePointer) -> c_int alias _traverse_fn_type = fn ( PyObjectPtr, Self._visitproc_fn_type, OpaquePointer - ) -> Int + ) -> c_int var traverse_fn: Self._traverse_fn_type - alias _clear_fn_type = fn (PyObjectPtr) -> Int + alias _clear_fn_type = fn (PyObjectPtr) -> c_int var clear_fn: Self._clear_fn_type alias _free_fn_type = fn (OpaquePointer) -> OpaquePointer From a5a8ff5ed92e1639467727b149fa201b04b4f3d0 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 2 Oct 2024 15:54:11 -0500 Subject: [PATCH 1679/2019] [stdlib] feat: Add `PyMojoObject` + add helper constructors to `PyType_Slot` MODULAR_ORIG_COMMIT_REV_ID: f10c9f1eca132c9c13d441d73c6c92382de41b4c --- stdlib/src/python/_bindings.mojo | 28 ++++++++++++++++++++++++++++ stdlib/src/python/_cpython.mojo | 20 ++++++++++++++++++++ stdlib/src/python/python.mojo | 2 +- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 599b3681ea..91fb8fc042 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -11,10 +11,38 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from memory import UnsafePointer + +from sys.ffi import c_int + from python import PythonObject, TypedPythonObject from python.python import _get_global_python_itf from python._cpython import PyObject, PyObjectPtr, PyCFunction +# ===-----------------------------------------------------------------------===# +# Mojo Object +# ===-----------------------------------------------------------------------===# + +# Must be ABI compatible with `initfunc` +alias Typed_initfunc = fn ( + PyObjectPtr, TypedPythonObject["Tuple"], PythonObject +) -> c_int + + +struct PyMojoObject[T: AnyType]: + """Storage backing a PyObject* wrapping a Mojo value.""" + + var ob_base: PyObject + var mojo_value: T + + @staticmethod + fn unsafe_cast_obj(obj_raw_ptr: PyObjectPtr) -> UnsafePointer[T]: + var mojo_obj_ptr = obj_raw_ptr.value.bitcast[PyMojoObject[T]]() + + # TODO(MSTDL-950): Should use something like `addr_of!` + return UnsafePointer[T].address_of(mojo_obj_ptr[].mojo_value) + + # ===-----------------------------------------------------------------------===# # PyCFunction Wrappers # ===-----------------------------------------------------------------------===# diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 06443f3460..e1cc41c286 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -26,6 +26,7 @@ from sys.arg import argv from sys.ffi import DLHandle, c_char, c_int, c_uint, OpaquePointer from python.python import _get_global_python_itf +from python._bindings import Typed_initfunc from memory import UnsafePointer @@ -61,6 +62,9 @@ alias METH_VARARGS = 0x1 alias destructor = fn (PyObjectPtr) -> None +alias initproc = fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int +alias newfunc = fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr + # GIL @value @@ -232,6 +236,22 @@ struct PyType_Slot: var slot: c_int var pfunc: OpaquePointer + @staticmethod + fn tp_new(func: newfunc) -> Self: + return PyType_Slot(Py_tp_new, rebind[OpaquePointer](func)) + + @staticmethod + fn tp_init(func: Typed_initfunc) -> Self: + return PyType_Slot(Py_tp_init, rebind[OpaquePointer](func)) + + @staticmethod + fn tp_dealloc(func: destructor) -> Self: + return PyType_Slot(Py_tp_dealloc, rebind[OpaquePointer](func)) + + @staticmethod + fn tp_methods(methods: UnsafePointer[PyMethodDef]) -> Self: + return PyType_Slot(Py_tp_methods, rebind[OpaquePointer](methods)) + @staticmethod fn null() -> Self: return PyType_Slot {slot: 0, pfunc: OpaquePointer()} diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index cf29753d42..0044212d74 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -28,8 +28,8 @@ from memory import UnsafePointer from utils import StringRef -from ._cpython import CPython, Py_eval_input, Py_file_input, PyMethodDef from .python_object import PythonObject, TypedPythonObject +from ._cpython import CPython, Py_eval_input, Py_file_input, PyMethodDef fn _init_global(ignored: OpaquePointer) -> OpaquePointer: From a79fbc856f5896cc3b10119b848312b9f129701c Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 2 Oct 2024 16:47:03 -0500 Subject: [PATCH 1680/2019] [stdlib] feat: Add `PyMojoObject.python_type_object()` + `Box.steal_data()` This improves the ergonomics of creating a Python 'type' object instance that describes the operations a Mojo type supports from Python. Also added Box.steal_data() as a convenience for taking ownership of the pointer owned by a Box. MODULAR_ORIG_COMMIT_REV_ID: aee519d7c62496be1533e487bbdeb2fd2b8872f3 --- stdlib/src/memory/box.mojo | 42 ++++++++- stdlib/src/python/_bindings.mojo | 152 ++++++++++++++++++++++++++++++- stdlib/src/python/_cpython.mojo | 4 +- stdlib/test/memory/test_box.mojo | 15 +++ 4 files changed, 202 insertions(+), 11 deletions(-) diff --git a/stdlib/src/memory/box.mojo b/stdlib/src/memory/box.mojo index f6f2da49d4..e6058bb4c8 100644 --- a/stdlib/src/memory/box.mojo +++ b/stdlib/src/memory/box.mojo @@ -27,6 +27,10 @@ struct Box[T: AnyType]: var _inner: UnsafePointer[T, AddressSpace.GENERIC] + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + fn __init__[T: Movable](inout self: Box[T], owned value: T): """Construct a new Box[] by moving the passed value into a new backing allocation. @@ -86,6 +90,15 @@ struct Box[T: AnyType]: self._inner = existing._inner existing._inner = UnsafePointer[T]() + fn __del__(owned self: Box[T]): + """Destroy the Box[].""" + self._inner.destroy_pointee() + self._inner.free() + + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + fn __getitem__( ref [_, AddressSpace.GENERIC._value.value]self ) -> ref [self, AddressSpace.GENERIC._value.value] T: @@ -102,10 +115,9 @@ struct Box[T: AnyType]: return self._inner[] - fn __del__(owned self: Box[T]): - """Destroy the Box[].""" - self._inner.destroy_pointee() - self._inner.free() + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# fn unsafe_ptr(self) -> UnsafePointer[T]: """UNSAFE: returns the backing pointer for this Box[]. @@ -132,3 +144,25 @@ struct Box[T: AnyType]: __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) return r^ + + fn steal_data(owned self) -> UnsafePointer[T]: + """Take ownership over the heap allocated pointer backing this `Box`. + + Safety: + This function is not unsafe to call, as a memory leak is not + considered unsafe. + + However, to avoid a memory leak, callers should ensure that the + returned pointer is eventually deinitialized and deallocated. + Failure to do so will leak memory. + + Returns: + The pointer owned by this box. + """ + + var ptr = self._inner + + # Prevent the destructor from running on `self` + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + + return ptr diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 91fb8fc042..5f40a5af07 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -11,21 +11,37 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer +from memory import UnsafePointer, Box from sys.ffi import c_int +from sys.info import sizeof + +from os import abort from python import PythonObject, TypedPythonObject from python.python import _get_global_python_itf -from python._cpython import PyObject, PyObjectPtr, PyCFunction +from python._cpython import ( + PyObject, + PyObjectPtr, + PyCFunction, + PyType_Spec, + PyType_Slot, + PyMethodDef, + Py_TPFLAGS_DEFAULT, + newfunc, + destructor, +) # ===-----------------------------------------------------------------------===# # Mojo Object # ===-----------------------------------------------------------------------===# -# Must be ABI compatible with `initfunc` -alias Typed_initfunc = fn ( - PyObjectPtr, TypedPythonObject["Tuple"], PythonObject +# Must be ABI compatible with `initproc` +alias Typed_initproc = fn ( + PyObjectPtr, + TypedPythonObject["Tuple"], + # Will be NULL if no keyword arguments were passed. + PyObjectPtr, ) -> c_int @@ -42,6 +58,132 @@ struct PyMojoObject[T: AnyType]: # TODO(MSTDL-950): Should use something like `addr_of!` return UnsafePointer[T].address_of(mojo_obj_ptr[].mojo_value) + @staticmethod + fn python_type_object[ + type_name: StringLiteral, + empty_init_func: fn (UnsafePointer[T]) -> None, + del_func: fn (UnsafePointer[T]) -> None, + ](owned methods: List[PyMethodDef]) raises -> TypedPythonObject["Type"]: + """Construct a Python 'type' describing PyMojoObject[T]. + + Parameters: + type_name: The name of the Mojo type. + empty_init_func: A function that default initializes an instance of `T`. + del_func: A function that deinitializes an instance of `T`. + """ + + var cpython = _get_global_python_itf().cpython() + + var slots = List[PyType_Slot]( + # All wrapped Mojo types are allocated generically. + PyType_Slot.tp_new( + cpython.lib.get_function[newfunc]("PyType_GenericNew") + ), + PyType_Slot.tp_init(create_empty_init_wrapper[empty_init_func]()), + PyType_Slot.tp_dealloc(create_dealloc_wrapper[del_func]()), + # FIXME: Avoid leaking the methods data pointer in this way. + PyType_Slot.tp_methods(methods.steal_data()), + # Zeroed item terminator + PyType_Slot.null(), + ) + + var type_spec = PyType_Spec { + name: type_name.unsafe_cstr_ptr(), + basicsize: sizeof[PyMojoObject[T]](), + itemsize: 0, + flags: Py_TPFLAGS_DEFAULT, + # FIXME: Don't leak this pointer, use globals instead. + slots: slots.steal_data(), + } + + # Construct a Python 'type' object from our type spec. + # FIXME: + # We heap allocate the type specification metadata. + # Who owns this pointer? Or does Python not actually take + # ownership of this pointer? + var type_obj = cpython.PyType_FromSpec(Box(type_spec).steal_data()) + + if not type_obj.value: + Python.throw_python_exception_if_error_state(cpython) + return abort[TypedPythonObject["Type"]]( + "expected to raise after getting NULL type object" + ) + + return TypedPythonObject["Type"]( + unsafe_unchecked_from=PythonObject(type_obj) + ) + + +# Impedance match between: +# +# Mojo: fn(UnsafePointer[T]) -> None +# Python: fn(PyObjectPtr, PyObjectPtr, PyObjectPtr) +# +# The latter is the C function signature that the CPython API expects a +# PyObject initializer function to have. The former is an unsafe form of the +# `fn(inout self)` signature that Mojo types with default constructors provide. +# +# To support CPython calling a Mojo types default constructor, we need to +# provide a wrapper function (around the Mojo constructor) that has the +# signature the C API expects. +# +# This function creates that wrapper function, and returns a pointer pointer to +# it. +fn create_empty_init_wrapper[ + T: AnyType, //, + empty_init_func: fn (UnsafePointer[T]) -> None, +]() -> Typed_initproc: + fn wrapper( + py_self: PyObjectPtr, + args: TypedPythonObject["Tuple"], + keyword_args: PyObjectPtr, + ) -> c_int: + var cpython = _get_global_python_itf().cpython() + + try: + if len(args) != 0 or keyword_args != PyObjectPtr(): + raise "unexpected arguments passed to default initializer function of wrapped Mojo type" + + var obj_ptr: UnsafePointer[T] = PyMojoObject[T].unsafe_cast_obj( + py_self + ) + + # ------------------------------------------------ + # Call the user-provided initialization function. + # ------------------------------------------------ + + # TODO(MOCO-1020): + # If/when Mojo supports an `init` convention, use it here. + # Change this callback to take an `init T` instead of an + # `UnsafePointer[T]`, for more ergonomic code in the caller. + empty_init_func(obj_ptr) + + return 0 + except e: + # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. + var error_type = cpython.get_error_global("PyExc_ValueError") + + cpython.PyErr_SetString( + error_type, + e.unsafe_cstr_ptr(), + ) + + return -1 + + return wrapper + + +fn create_dealloc_wrapper[ + T: AnyType, //, + del_func: fn (UnsafePointer[T]) -> None, +]() -> destructor: + fn wrapper(py_self: PyObjectPtr): + var self: UnsafePointer[T] = PyMojoObject[T].unsafe_cast_obj(py_self) + + del_func(self) + + return wrapper + # ===-----------------------------------------------------------------------===# # PyCFunction Wrappers diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index e1cc41c286..e19a5d5533 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -26,7 +26,7 @@ from sys.arg import argv from sys.ffi import DLHandle, c_char, c_int, c_uint, OpaquePointer from python.python import _get_global_python_itf -from python._bindings import Typed_initfunc +from python._bindings import Typed_initproc from memory import UnsafePointer @@ -241,7 +241,7 @@ struct PyType_Slot: return PyType_Slot(Py_tp_new, rebind[OpaquePointer](func)) @staticmethod - fn tp_init(func: Typed_initfunc) -> Self: + fn tp_init(func: Typed_initproc) -> Self: return PyType_Slot(Py_tp_init, rebind[OpaquePointer](func)) @staticmethod diff --git a/stdlib/test/memory/test_box.mojo b/stdlib/test/memory/test_box.mojo index d658efdf8e..6e7303516a 100644 --- a/stdlib/test/memory/test_box.mojo +++ b/stdlib/test/memory/test_box.mojo @@ -109,6 +109,20 @@ def test_moveinit(): _ = b2^ +def test_steal_data(): + var deleted = False + + var box = Box(ObservableDel(UnsafePointer.address_of(deleted))) + + var ptr = box^.steal_data() + + # Check that `Box` did not deinitialize its pointee. + assert_false(deleted) + + ptr.destroy_pointee() + ptr.free() + + def main(): test_basic_ref() test_box_copy_constructor() @@ -119,3 +133,4 @@ def main(): test_basic_del() test_take() test_moveinit() + test_steal_data() From ef80c5b0fd78279d50c0755342a0ee20f894811a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 2 Oct 2024 15:53:27 -0700 Subject: [PATCH 1681/2019] [mojo-stdlib] Rename `memory.Reference` to `memory.Pointer`. This renames the `Reference` struct to `Pointer`, to reflect this type as being related to `UnsafePointer` (more rationale in the changelog). This patch is the minimal rename of the type itself without updating the rest of the stdlib. It includes an alias to keep existing code working and easier to migrate. This is the first step of MSTDL-948 MODULAR_ORIG_COMMIT_REV_ID: 91ed6a6480a2b07714c14bf408de39cdf0ed17d1 --- docs/changelog.md | 7 +++ stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/memory.mojo | 2 +- .../memory/{reference.mojo => pointer.mojo} | 49 ++++++++++--------- 4 files changed, 35 insertions(+), 25 deletions(-) rename stdlib/src/memory/{reference.mojo => pointer.mojo} (89%) diff --git a/docs/changelog.md b/docs/changelog.md index 9200c2d884..4bbc0a4ca5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -197,6 +197,13 @@ what we publish. - `StringRef` has been removed. Please explicitly import it via `from utils import StringRef`. +- The `Reference` type has been renamed to `Pointer`: a memory safe complement + to `UnsafePointer`. This change is motivated by the fact that `Pointer` + is assignable and requires an explicit dereference with `ptr[]`. Renaming + to `Pointer` clarifies that "references" means `ref` arguments and results, + and gives us a model that is more similar to what the C++ community would + expect. + - A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method specifies to the compiler that the resultant pointer is a distinct identifiable object that does not alias any other memory in the local scope. diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 90eee5e7c1..9d25f91476 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -15,6 +15,6 @@ from .arc import Arc from .box import Box from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation -from .reference import AddressSpace, Reference +from .pointer import AddressSpace, Pointer, Reference from .unsafe import bitcast from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index dbd24f5d13..f8af74202e 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -31,7 +31,7 @@ from sys import ( ) from collections import Optional from builtin.dtype import _integral_type_of -from memory.reference import AddressSpace, _GPUAddressSpace +from memory.pointer import AddressSpace, _GPUAddressSpace # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/pointer.mojo similarity index 89% rename from stdlib/src/memory/reference.mojo rename to stdlib/src/memory/pointer.mojo index 11295205c7..8ecdeb094e 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -10,15 +10,18 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements the Reference type. +"""Implements the Pointer type. You can import these APIs from the `memory` package. For example: ```mojo -from memory import Reference +from memory import Pointer ``` """ +# TODO: This is kept for compatibility, remove this in the future. +alias Reference = Pointer[*_] + # ===----------------------------------------------------------------------===# # AddressSpace # ===----------------------------------------------------------------------===# @@ -261,25 +264,25 @@ struct AddressSpace(EqualityComparable): # ===----------------------------------------------------------------------===# -# Reference +# Pointer # ===----------------------------------------------------------------------===# @value @register_passable("trivial") -struct Reference[ +struct Pointer[ is_mutable: Bool, //, type: AnyType, lifetime: Lifetime[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, ](CollectionElementNew, Stringable): - """Defines a non-nullable safe reference. + """Defines a non-nullable safe pointer. Parameters: - is_mutable: Whether the referenced data may be mutated through this. + is_mutable: Whether the pointee data may be mutated through this. type: Type of the underlying data. - lifetime: The lifetime of the reference. - address_space: The address space of the referenced data. + lifetime: The lifetime of the pointer. + address_space: The address space of the pointee data. """ alias _mlir_type = __mlir_type[ @@ -293,7 +296,7 @@ struct Reference[ ] var _value: Self._mlir_type - """The underlying MLIR reference.""" + """The underlying MLIR representation.""" # ===------------------------------------------------------------------===# # Initializers @@ -301,10 +304,10 @@ struct Reference[ @always_inline("nodebug") fn __init__(inout self, *, _mlir_value: Self._mlir_type): - """Constructs a Reference from its MLIR prepresentation. + """Constructs a Pointer from its MLIR prepresentation. Args: - _mlir_value: The MLIR representation reference. + _mlir_value: The MLIR representation of the pointer. """ self._value = _mlir_value @@ -313,23 +316,23 @@ struct Reference[ fn address_of( ref [lifetime, address_space._value.value]value: type ) -> Self: - """Constructs a Reference from a value reference. + """Constructs a Pointer from a reference to a value. Args: - value: The value reference. + value: The value to get the address of. Returns: - The result Reference. + The result Pointer. """ - return Reference(_mlir_value=__get_mvalue_as_litref(value)) + return Pointer(_mlir_value=__get_mvalue_as_litref(value)) fn __init__(inout self, *, other: Self): - """Constructs a copy from another Reference. + """Constructs a copy from another Pointer. Note that this does **not** copy the underlying data. Args: - other: The `Reference` to copy. + other: The `Pointer` to copy. """ self._value = other._value @@ -339,10 +342,10 @@ struct Reference[ @always_inline("nodebug") fn __getitem__(self) -> ref [lifetime, address_space._value.value] type: - """Enable subscript syntax `ref[]` to access the element. + """Enable subscript syntax `ptr[]` to access the element. Returns: - The MLIR reference for the Mojo compiler to use. + A reference to the underlying value in memory. """ return __get_litref_as_mvalue(self._value) @@ -352,7 +355,7 @@ struct Reference[ # accesses to the lifetime. @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") - fn __eq__(self, rhs: Reference[type, _, address_space]) -> Bool: + fn __eq__(self, rhs: Pointer[type, _, address_space]) -> Bool: """Returns True if the two pointers are equal. Args: @@ -367,7 +370,7 @@ struct Reference[ @__unsafe_disable_nested_lifetime_exclusivity @always_inline("nodebug") - fn __ne__(self, rhs: Reference[type, _, address_space]) -> Bool: + fn __ne__(self, rhs: Pointer[type, _, address_space]) -> Bool: """Returns True if the two pointers are not equal. Args: @@ -380,9 +383,9 @@ struct Reference[ @no_inline fn __str__(self) -> String: - """Gets a string representation of the Reference. + """Gets a string representation of the Pointer. Returns: - The string representation of the Reference. + The string representation of the Pointer. """ return str(UnsafePointer.address_of(self[])) From e96cdf9a2f5d611bda6e05fd6d09baf6d70613e6 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 2 Oct 2024 16:34:03 -0700 Subject: [PATCH 1682/2019] [mojo-stdlib] Rename `Reference` to `Pointer` throughout the stdlib This pushes things over to the new name. MODULAR_ORIG_COMMIT_REV_ID: 2f166b684deb1f85591db18757896a53ac2f1a11 --- stdlib/src/builtin/_hash.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 12 ++++++------ stdlib/src/builtin/file.mojo | 18 +++++++++--------- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/rebind.mojo | 2 +- stdlib/src/collections/dict.mojo | 22 +++++++++++----------- stdlib/src/collections/inline_array.mojo | 4 ++-- stdlib/src/collections/inline_list.mojo | 12 ++++++------ stdlib/src/collections/list.mojo | 14 +++++++------- stdlib/src/collections/optional.mojo | 4 ++-- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/memory/arc.mojo | 10 +++++----- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/src/os/_linux_aarch64.mojo | 4 ++-- stdlib/src/os/_linux_x86.mojo | 4 ++-- stdlib/src/os/_macos.mojo | 4 ++-- stdlib/src/prelude/__init__.mojo | 2 +- stdlib/src/sys/arg.mojo | 2 +- stdlib/src/sys/info.mojo | 2 +- stdlib/src/time/time.mojo | 4 ++-- stdlib/src/utils/span.mojo | 16 ++++++++-------- stdlib/src/utils/variant.mojo | 4 ++-- stdlib/test/memory/test_reference.mojo | 12 ++++++------ stdlib/test/utils/test_span.mojo | 2 +- 24 files changed, 81 insertions(+), 81 deletions(-) diff --git a/stdlib/src/builtin/_hash.mojo b/stdlib/src/builtin/_hash.mojo index ce5943a191..f0fe12140b 100644 --- a/stdlib/src/builtin/_hash.mojo +++ b/stdlib/src/builtin/_hash.mojo @@ -152,7 +152,7 @@ fn hash[ References: - - [Reference Implementation in Rust](https://github.com/tkaitchuck/aHash) + - [Pointer Implementation in Rust](https://github.com/tkaitchuck/aHash) ```mojo from random import rand diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index d46db01ba0..da3334f0b7 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory import Reference, UnsafePointer +from memory import Pointer, UnsafePointer # ===----------------------------------------------------------------------===# @@ -229,20 +229,20 @@ struct _VariadicListMemIter[ alias variadic_list_type = VariadicListMem[elt_type, elt_lifetime] var index: Int - var src: Reference[Self.variadic_list_type, list_lifetime] + var src: Pointer[Self.variadic_list_type, list_lifetime] fn __init__( inout self, index: Int, ref [list_lifetime]list: Self.variadic_list_type ): self.index = index - self.src = Reference.address_of(list) + self.src = Pointer.address_of(list) fn __next__(inout self) -> Self.variadic_list_type.reference_type: self.index += 1 # TODO: Need to make this return a dereferenced reference, not a # reference that must be deref'd by the user. return rebind[Self.variadic_list_type.reference_type]( - Reference.address_of(self.src[][self.index - 1]) + Pointer.address_of(self.src[][self.index - 1]) ) fn __len__(self) -> Int: @@ -297,7 +297,7 @@ struct VariadicListMem[ lifetime: The reference lifetime of the underlying elements. """ - alias reference_type = Reference[element_type, lifetime] + alias reference_type = Pointer[element_type, lifetime] alias _mlir_ref_type = Self.reference_type._mlir_type alias _mlir_type = __mlir_type[ `!kgen.variadic<`, Self._mlir_ref_type, `, borrow_in_mem>` @@ -639,7 +639,7 @@ struct VariadicPack[ index: The element of the pack to return. Returns: - A reference to the element. The Reference's mutability follows the + A reference to the element. The Pointer's mutability follows the mutability of the pack argument convention. """ litref_elt = __mlir_op.`lit.ref.pack.extract`[index = index.value]( diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 41aecb651f..3f95e511b2 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -96,7 +96,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() var handle = external_call[ "KGEN_CompilerRT_IO_FileOpen", OpaquePointer - ](path, mode, Reference.address_of(err_msg)) + ](path, mode, Pointer.address_of(err_msg)) if err_msg: self.handle = OpaquePointer() @@ -118,7 +118,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() external_call["KGEN_CompilerRT_IO_FileClose", NoneType]( - self.handle, Reference.address_of(err_msg) + self.handle, Pointer.address_of(err_msg) ) if err_msg: @@ -194,8 +194,8 @@ struct FileHandle: "KGEN_CompilerRT_IO_FileRead", UnsafePointer[UInt8] ]( self.handle, - Reference.address_of(size_copy), - Reference.address_of(err_msg), + Pointer.address_of(size_copy), + Pointer.address_of(err_msg), ) if err_msg: @@ -271,7 +271,7 @@ struct FileHandle: self.handle, ptr, size * sizeof[type](), - Reference.address_of(err_msg), + Pointer.address_of(err_msg), ) if err_msg: @@ -337,8 +337,8 @@ struct FileHandle: "KGEN_CompilerRT_IO_FileReadBytes", UnsafePointer[UInt8] ]( self.handle, - Reference.address_of(size_copy), - Reference.address_of(err_msg), + Pointer.address_of(size_copy), + Pointer.address_of(err_msg), ) if err_msg: @@ -395,7 +395,7 @@ struct FileHandle: ) var err_msg = _OwnedStringRef() var pos = external_call["KGEN_CompilerRT_IO_FileSeek", UInt64]( - self.handle, offset, whence, Reference.address_of(err_msg) + self.handle, offset, whence, Pointer.address_of(err_msg) ) if err_msg: @@ -447,7 +447,7 @@ struct FileHandle: self.handle, ptr.address, len, - Reference.address_of(err_msg), + Pointer.address_of(err_msg), ) if err_msg: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 84a6bada47..9ee0e84e81 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -167,7 +167,7 @@ fn _printf[ @parameter if triple_is_nvidia_cuda(): _ = external_call["vprintf", Int32]( - fmt.unsafe_cstr_ptr(), Reference.address_of(loaded_pack) + fmt.unsafe_cstr_ptr(), Pointer.address_of(loaded_pack) ) else: with _fdopen(file) as fd: diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index cbfea237be..fb5bd65750 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -67,5 +67,5 @@ fn rebind[ A reference to the value rebound as `dest_type`. """ lit = __get_mvalue_as_litref(src) - rebound = rebind[Reference[dest_type, __lifetime_of(src)]._mlir_type](lit) + rebound = rebind[Pointer[dest_type, __lifetime_of(src)]._mlir_type](lit) return __get_litref_as_mvalue(rebound) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 660cfd3bb4..e0f8b6631a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -75,14 +75,14 @@ struct _DictEntryIter[ var index: Int var seen: Int - var src: Reference[Dict[K, V], dict_lifetime] + var src: Pointer[Dict[K, V], dict_lifetime] fn __init__( inout self, index: Int, seen: Int, ref [dict_lifetime]dict: Dict[K, V] ): self.index = index self.seen = seen - self.src = Reference.address_of(dict) + self.src = Pointer.address_of(dict) fn __iter__(self) -> Self: return self @@ -90,11 +90,11 @@ struct _DictEntryIter[ @always_inline fn __next__( inout self, - ) -> Reference[ + ) -> Pointer[ DictEntry[K, V], __lifetime_of(self.src[]._entries[0].value()) ]: while True: - var opt_entry_ref = Reference.address_of( + var opt_entry_ref = Pointer.address_of( self.src[]._entries[self.index] ) @@ -106,7 +106,7 @@ struct _DictEntryIter[ if opt_entry_ref[]: self.seen += 1 - return Reference.address_of(opt_entry_ref[].value()) + return Pointer.address_of(opt_entry_ref[].value()) fn __len__(self) -> Int: return len(self.src[]) - self.seen @@ -139,8 +139,8 @@ struct _DictKeyIter[ fn __next__( inout self, - ) -> Reference[K, __lifetime_of(self.iter.__next__()[].key)]: - return Reference.address_of(self.iter.__next__()[].key) + ) -> Pointer[K, __lifetime_of(self.iter.__next__()[].key)]: + return Pointer.address_of(self.iter.__next__()[].key) fn __len__(self) -> Int: return self.iter.__len__() @@ -165,7 +165,7 @@ struct _DictValueIter[ forward: The iteration direction. `False` is backwards. """ - alias ref_type = Reference[V, dict_lifetime] + alias ref_type = Pointer[V, dict_lifetime] var iter: _DictEntryIter[K, V, dict_lifetime, forward] @@ -745,7 +745,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( except: return None - # TODO(MOCO-604): Return Optional[Reference] instead of raising + # TODO(MOCO-604): Return Optional[Pointer] instead of raising fn _find_ref( ref [_]self: Self, key: K ) raises -> ref [self._entries[0].value().value] Self.V: @@ -764,7 +764,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var index: Int found, slot, index = self._find_index(hash, key) if found: - var entry = Reference.address_of(self._entries[index]) + var entry = Pointer.address_of(self._entries[index]) debug_assert(entry[].__bool__(), "entry in index must be full") return entry[].value().value raise "KeyError" @@ -830,7 +830,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( found, slot, index = self._find_index(hash, key) if found: self._set_index(slot, Self.REMOVED) - var entry = Reference.address_of(self._entries[index]) + var entry = Pointer.address_of(self._entries[index]) debug_assert(entry[].__bool__(), "entry in index must be full") var entry_value = entry[].unsafe_take() entry[] = None diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index ccfae9bded..0a787cbd03 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -218,7 +218,7 @@ struct InlineArray[ @always_inline("nodebug") fn __getitem__(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: - """Get a `Reference` to the element at the given index. + """Get a `Pointer` to the element at the given index. Args: idx: The index of the item. @@ -234,7 +234,7 @@ struct InlineArray[ fn __getitem__[ idx: Int, ](ref [_]self: Self) -> ref [self] Self.ElementType: - """Get a `Reference` to the element at the given index. + """Get a `Pointer` to the element at the given index. Parameters: idx: The index of the item. diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 90755b8122..6987e3b98c 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -47,21 +47,21 @@ struct _InlineListIter[ alias list_type = InlineList[T, capacity] var index: Int - var src: Reference[Self.list_type, list_lifetime] + var src: Pointer[Self.list_type, list_lifetime] fn __iter__(self) -> Self: return self fn __next__( inout self, - ) -> Reference[T, __lifetime_of(self.src[][0])]: + ) -> Pointer[T, __lifetime_of(self.src[][0])]: @parameter if forward: self.index += 1 - return Reference.address_of(self.src[][self.index - 1]) + return Pointer.address_of(self.src[][self.index - 1]) else: self.index -= 1 - return Reference.address_of(self.src[][self.index]) + return Pointer.address_of(self.src[][self.index]) fn __len__(self) -> Int: @parameter @@ -130,7 +130,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __getitem__( ref [_]self: Self, owned idx: Int ) -> ref [self._array] Self.ElementType: - """Get a `Reference` to the element at the given index. + """Get a `Pointer` to the element at the given index. Args: idx: The index of the item. @@ -177,7 +177,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): Returns: An iterator of immutable references to the list elements. """ - return _InlineListIter(0, Reference.address_of(self)) + return _InlineListIter(0, Pointer.address_of(self)) fn __contains__[ C: EqualityComparableCollectionElement, // diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 79e3f4f740..6233443a36 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -23,7 +23,7 @@ from collections import List from sys.intrinsics import _type_is_eq from sys import sizeof from os import abort -from memory import Reference, UnsafePointer, memcpy +from memory import Pointer, UnsafePointer, memcpy from utils import Span from .optional import Optional @@ -55,21 +55,21 @@ struct _ListIter[ alias list_type = List[T, hint_trivial_type] var index: Int - var src: Reference[Self.list_type, list_lifetime] + var src: Pointer[Self.list_type, list_lifetime] fn __iter__(self) -> Self: return self fn __next__( inout self, - ) -> Reference[T, list_lifetime]: + ) -> Pointer[T, list_lifetime]: @parameter if forward: self.index += 1 - return Reference.address_of(self.src[][self.index - 1]) + return Pointer.address_of(self.src[][self.index - 1]) else: self.index -= 1 - return Reference.address_of(self.src[][self.index]) + return Pointer.address_of(self.src[][self.index]) fn __len__(self) -> Int: @parameter @@ -353,7 +353,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Returns: An iterator of immutable references to the list elements. """ - return _ListIter(0, Reference.address_of(self)) + return _ListIter(0, Pointer.address_of(self)) fn __reversed__( ref [_]self: Self, @@ -363,7 +363,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Returns: A reversed iterator of immutable references to the list elements. """ - return _ListIter[forward=False](len(self), Reference.address_of(self)) + return _ListIter[forward=False](len(self), Pointer.address_of(self)) # ===-------------------------------------------------------------------===# # Trait implementations diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 4a857381c2..5f6ac18f69 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -322,7 +322,7 @@ struct Optional[T: CollectionElement]( value (for instance with `or_else`), the program will abort Returns: - A reference to the contained data of the option as a Reference[T]. + A reference to the contained data of the option as a Pointer[T]. """ if not self.__bool__(): abort(".value() on empty Optional") @@ -339,7 +339,7 @@ struct Optional[T: CollectionElement]( value (for instance with `or_else`), you'll get garbage unsafe data out. Returns: - A reference to the contained data of the option as a Reference[T]. + A reference to the contained data of the option as a Pointer[T]. """ debug_assert(self.__bool__(), ".value() on empty Optional") return self._value.unsafe_get[T]() diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 61dae607b5..4a9636fb2a 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -19,7 +19,7 @@ from collections import InlinedFixedVector ``` """ -from memory import Reference, UnsafePointer, memcpy +from memory import Pointer, UnsafePointer, memcpy from sys import sizeof from utils import StaticTuple diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 1180478c5a..e72602239b 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Reference-counted smart pointers. +"""Pointer-counted smart pointers. Example usage: @@ -22,7 +22,7 @@ p2[]=3 print(3 == p[]) ``` -Subscripting(`[]`) is done by `Reference`, +Subscripting(`[]`) is done by `Pointer`, in order to ensure that the underlying `Arc` outlive the operation. It is highly DISCOURAGED to manipulate an `Arc` through `UnsafePointer`. @@ -36,7 +36,7 @@ print(Arc(String("ok"))._inner[].payload) #........................^ASAP ^already freed ``` -Always use `Reference` subscripting (`[]`): +Always use `Pointer` subscripting (`[]`): ```mojo print(Arc(String("ok"))[]) @@ -144,13 +144,13 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew): ) -> ref [ _lit_mut_cast[self_life, result_mutable=True].result ] T: - """Returns a mutable Reference to the managed value. + """Returns a mutable reference to the managed value. Parameters: self_life: The lifetime of self. Returns: - A Reference to the managed value. + A reference to the managed value. """ return self._inner[].payload diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 26735fe7cd..a1cecd775f 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -192,7 +192,7 @@ struct UnsafePointer[ """ # We're unsafe, so we can have unsafe things. - alias _ref_type = Reference[type, lifetime, address_space] + alias _ref_type = Pointer[type, lifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( UnsafePointer[type, address_space, False, alignment, lifetime]( diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index e9048cdd2b..b8e8740068 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -110,7 +110,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference.address_of(stat) + Int32(0), path.unsafe_ptr(), Pointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -121,7 +121,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference.address_of(stat) + Int32(0), path.unsafe_ptr(), Pointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index ad6693d765..472e16c61d 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -108,7 +108,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference.address_of(stat) + Int32(0), path.unsafe_ptr(), Pointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -119,7 +119,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path.unsafe_ptr(), Reference.address_of(stat) + Int32(0), path.unsafe_ptr(), Pointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index f3f18d4cdc..faeb23f686 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -115,7 +115,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["stat", Int32]( - path.unsafe_ptr(), Reference.address_of(stat) + path.unsafe_ptr(), Pointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -126,7 +126,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["lstat", Int32]( - path.unsafe_ptr(), Reference.address_of(stat) + path.unsafe_ptr(), Pointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 53c09d50fb..9ef17b3eb9 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -130,5 +130,5 @@ from collections.string import ( islower, isprintable, ) -from memory import Reference, AddressSpace +from memory import Pointer, AddressSpace from utils import Formattable, Formatter diff --git a/stdlib/src/sys/arg.mojo b/stdlib/src/sys/arg.mojo index 7adf8a4865..69a0093630 100644 --- a/stdlib/src/sys/arg.mojo +++ b/stdlib/src/sys/arg.mojo @@ -46,6 +46,6 @@ fn argv() -> VariadicList[StringRef]: """ var result = VariadicList[StringRef]("") external_call["KGEN_CompilerRT_GetArgV", NoneType]( - Reference.address_of(result) + Pointer.address_of(result) ) return result diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index b404a8c194..a8aec88aa2 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -779,7 +779,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: var err = external_call["sysctlbyname", Int32]( "kern.osproductversion".unsafe_cstr_ptr(), buf.data, - Reference.address_of(buf_len), + Pointer.address_of(buf_len), OpaquePointer(), Int(0), ) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 458d9822b6..39cca3aff6 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -107,7 +107,7 @@ fn _clock_gettime(clockid: Int) -> _CTimeSpec: # Call libc's clock_gettime. _ = external_call["clock_gettime", Int32]( - Int32(clockid), Reference.address_of(ts) + Int32(clockid), Pointer.address_of(ts) ) return ts @@ -138,7 +138,7 @@ fn _monotonic_nanoseconds() -> Int: if os_is_windows(): var ft = _FILETIME() external_call["GetSystemTimePreciseAsFileTime", NoneType]( - Reference.address_of(ft) + Pointer.address_of(ft) ) return ft.as_nanoseconds() diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 1f504c1714..53838433ad 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -21,7 +21,7 @@ from utils import Span """ from collections import InlineArray -from memory import Reference, UnsafePointer +from memory import Pointer, UnsafePointer from builtin.builtin_list import _lit_mut_cast @@ -51,14 +51,14 @@ struct _SpanIter[ @always_inline fn __next__( inout self, - ) -> Reference[T, lifetime]: + ) -> Pointer[T, lifetime]: @parameter if forward: self.index += 1 - return Reference.address_of(self.src[self.index - 1]) + return Pointer.address_of(self.src[self.index - 1]) else: self.index -= 1 - return Reference.address_of(self.src[self.index]) + return Pointer.address_of(self.src[self.index]) @always_inline fn __len__(self) -> Int: @@ -222,15 +222,15 @@ struct Span[ return self._data - fn as_ref(self) -> Reference[T, lifetime]: + fn as_ref(self) -> Pointer[T, lifetime]: """ - Gets a Reference to the first element of this slice. + Gets a Pointer to the first element of this slice. Returns: - A Reference pointing at the first element of this slice. + A Pointer pointing at the first element of this slice. """ - return Reference[T, lifetime].address_of(self._data[0]) + return Pointer[T, lifetime].address_of(self._data[0]) @always_inline fn copy_from[ diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 3ecdc0de4a..cb60bfb46e 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -208,7 +208,7 @@ struct Variant[*Ts: CollectionElement]( T: The type of the value to get out. Returns: - The internal data represented as a `Reference[T]`. + A reference to the internal data. """ if not self.isa[T](): abort("get: wrong variant type") @@ -377,7 +377,7 @@ struct Variant[*Ts: CollectionElement]( T: The type of the value to get out. Returns: - The internal data represented as a `Reference[T]`. + The internal data represented as a `Pointer[T]`. """ debug_assert(self.isa[T](), "get: wrong variant type") return self._get_ptr[T]()[] diff --git a/stdlib/test/memory/test_reference.mojo b/stdlib/test/memory/test_reference.mojo index e91f09aa5a..44d448e791 100644 --- a/stdlib/test/memory/test_reference.mojo +++ b/stdlib/test/memory/test_reference.mojo @@ -17,8 +17,8 @@ from testing import assert_equal, assert_true def test_copy_reference_explicitly(): var a = List[Int](1, 2, 3) - var b = Reference.address_of(a) - var c = Reference(other=b) + var b = Pointer.address_of(a) + var c = Pointer(other=b) c[][0] = 4 assert_equal(a[0], 4) @@ -30,14 +30,14 @@ def test_equality(): var a = List[Int](1, 2, 3) var b = List[Int](4, 5, 6) - assert_true(Reference.address_of(a) == Reference.address_of(a)) - assert_true(Reference.address_of(b) == Reference.address_of(b)) - assert_true(Reference.address_of(a) != Reference.address_of(b)) + assert_true(Pointer.address_of(a) == Pointer.address_of(a)) + assert_true(Pointer.address_of(b) == Pointer.address_of(b)) + assert_true(Pointer.address_of(a) != Pointer.address_of(b)) def test_str(): var a = Int(42) - var a_ref = Reference.address_of(a) + var a_ref = Pointer.address_of(a) assert_true(str(a_ref).startswith("0x")) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index f1ddbdc150..1e3eb04b5c 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -191,7 +191,7 @@ def test_fill(): def test_ref(): var l = InlineArray[Int, 3](1, 2, 3) var s = Span[Int](array=l) - assert_true(s.as_ref() == Reference.address_of(l.unsafe_ptr()[])) + assert_true(s.as_ref() == Pointer.address_of(l.unsafe_ptr()[])) def main(): From 9625528c2cb155abe0148c44d526b3bc273b280c Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 2 Oct 2024 22:04:40 -0600 Subject: [PATCH 1683/2019] [stdlib] Fix signatures of CPython thread APIs `PyEval_SaveThread` CPython API function returns a `PyThreadState*`, not an `Int64` as it's currently modeled. This lines up fine from an ABI perspective (both types are 8 bytes), but it's less correct conceptually and may provide confusion to future readers of the code. Instead, introduce a `PyThreadState` type and return that from `PyEval_SaveThread`. For its dual, `PyEval_RestoreThread`, change its function argument from `Int64` to this new type. MODULAR_ORIG_COMMIT_REV_ID: 8d2d4a054bde6b1197905bb34701bc9ece4b51a1 --- stdlib/src/python/_cpython.mojo | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index e19a5d5533..a46977f194 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -76,6 +76,12 @@ struct PyGILState_STATE: alias PyGILState_UNLOCKED = c_int(1) +struct PyThreadState: + """Opaque struct.""" + + pass + + @value @register_passable("trivial") struct PyKeyValuePair: @@ -702,11 +708,15 @@ struct CPython: "PyGILState_Release" )(state) - fn PyEval_SaveThread(inout self) -> Int64: - return self.lib.get_function[fn () -> Int64]("PyEval_SaveThread")() + fn PyEval_SaveThread(inout self) -> UnsafePointer[PyThreadState]: + return self.lib.get_function[fn () -> UnsafePointer[PyThreadState]]( + "PyEval_SaveThread" + )() - fn PyEval_RestoreThread(inout self, state: Int64): - self.lib.get_function[fn (Int64) -> None]("PyEval_RestoreThread")(state) + fn PyEval_RestoreThread(inout self, state: UnsafePointer[PyThreadState]): + self.lib.get_function[fn (UnsafePointer[PyThreadState]) -> None]( + "PyEval_RestoreThread" + )(state) # ===-------------------------------------------------------------------===# # Python Dict operations From 0be552c726513cb079cad38e8567469acae279aa Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 3 Oct 2024 11:34:01 -0600 Subject: [PATCH 1684/2019] [stdlib] Introduce `hashlib` module Now that we have an `ahash` algorithm and are starting to explore different hash functions, rehome these from `builtin` into the new top-level `hashlib` module. Relocate their associated tests too. In the near future, we'll rework the generic hasher trait, allow injecting different hash functions into `Dict`, etc. MODULAR_ORIG_COMMIT_REV_ID: 0b15a92c8e29a3b68866749b5e3c1ee493c9956c --- .../{builtin => hashlib}/bench_hash.mojo | 29 +++++++++---------- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/uint.mojo | 2 +- stdlib/src/hashlib/__init__.mojo | 14 +++++++++ .../_hash.mojo => hashlib/_ahash.mojo} | 0 stdlib/src/{builtin => hashlib}/_hasher.mojo | 0 stdlib/src/{builtin => hashlib}/hash.mojo | 0 stdlib/src/prelude/__init__.mojo | 2 +- .../test_ahash.mojo} | 4 +-- .../test/{builtin => hashlib}/test_hash.mojo | 2 +- .../{builtin => hashlib}/test_hasher.mojo | 2 +- 12 files changed, 35 insertions(+), 24 deletions(-) rename stdlib/benchmarks/{builtin => hashlib}/bench_hash.mojo (99%) create mode 100644 stdlib/src/hashlib/__init__.mojo rename stdlib/src/{builtin/_hash.mojo => hashlib/_ahash.mojo} (100%) rename stdlib/src/{builtin => hashlib}/_hasher.mojo (100%) rename stdlib/src/{builtin => hashlib}/hash.mojo (100%) rename stdlib/test/{builtin/test__hash.mojo => hashlib/test_ahash.mojo} (99%) rename stdlib/test/{builtin => hashlib}/test_hash.mojo (99%) rename stdlib/test/{builtin => hashlib}/test_hasher.mojo (98%) diff --git a/stdlib/benchmarks/builtin/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo similarity index 99% rename from stdlib/benchmarks/builtin/bench_hash.mojo rename to stdlib/benchmarks/hashlib/bench_hash.mojo index 9cdb6970ff..99dc115281 100644 --- a/stdlib/benchmarks/builtin/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -14,6 +14,19 @@ # RUN: %mojo-no-debug %s -t from benchmark import Bench, BenchConfig, Bencher, BenchId +from bit import byte_swap, rotate_bits_left +from memory import UnsafePointer +from hashlib.hash import hash as old_hash +from hashlib._ahash import ( + AHasher, + hash as ahash, + _folded_multiply, + read_small, + U256, + U128, + MULTIPLE, + ROT, +) # Source: https://www.101languages.net/arabic/most-common-arabic-words/ alias words_ar = """ @@ -580,22 +593,6 @@ fn gen_word_pairs[words: String = words_en]() -> List[String]: return result -from bit import byte_swap, rotate_bits_left -from builtin._hash import hash -from memory import UnsafePointer -from builtin.hash import hash as old_hash -from builtin._hash import ( - AHasher, - hash as ahash, - _folded_multiply, - read_small, - U256, - U128, - MULTIPLE, - ROT, -) - - # ===----------------------------------------------------------------------===# # Benchmarks # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index ea91a558b1..c866ceef67 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -19,7 +19,7 @@ from collections import KeyElement from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable -from builtin.hash import _hash_simd +from hashlib.hash import _hash_simd from builtin.io import _snprintf from collections.string import ( _calc_initial_buffer_size_int32, diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b9ec3f601f..f12086c554 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -37,7 +37,7 @@ from bit import pop_count from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width -from builtin.hash import _hash_simd +from hashlib.hash import _hash_simd from builtin.format_int import _try_write_int from collections import InlineArray from memory import bitcast, UnsafePointer diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index bb9a685206..d76d9b0eec 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import bitwidthof from utils._visualizers import lldb_formatter_wrapping_type from builtin._documentation import doc_private -from builtin.hash import _hash_simd +from hashlib.hash import _hash_simd @lldb_formatter_wrapping_type diff --git a/stdlib/src/hashlib/__init__.mojo b/stdlib/src/hashlib/__init__.mojo new file mode 100644 index 0000000000..2bdc949799 --- /dev/null +++ b/stdlib/src/hashlib/__init__.mojo @@ -0,0 +1,14 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the hashlib package that provides various hash algorithms.""" +from .hash import hash, Hashable diff --git a/stdlib/src/builtin/_hash.mojo b/stdlib/src/hashlib/_ahash.mojo similarity index 100% rename from stdlib/src/builtin/_hash.mojo rename to stdlib/src/hashlib/_ahash.mojo diff --git a/stdlib/src/builtin/_hasher.mojo b/stdlib/src/hashlib/_hasher.mojo similarity index 100% rename from stdlib/src/builtin/_hasher.mojo rename to stdlib/src/hashlib/_hasher.mojo diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/hashlib/hash.mojo similarity index 100% rename from stdlib/src/builtin/hash.mojo rename to stdlib/src/hashlib/hash.mojo diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 9ef17b3eb9..06ff1443b5 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -36,7 +36,6 @@ from builtin.file_descriptor import FileDescriptor from builtin.float_literal import FloatLiteral from builtin.floatable import Floatable, FloatableRaising, float from builtin.format_int import bin, hex, oct -from builtin.hash import hash, Hashable from builtin.identifiable import Identifiable, StringableIdentifiable from builtin.int import ( Int, @@ -130,5 +129,6 @@ from collections.string import ( islower, isprintable, ) +from hashlib.hash import hash, Hashable from memory import Pointer, AddressSpace from utils import Formattable, Formatter diff --git a/stdlib/test/builtin/test__hash.mojo b/stdlib/test/hashlib/test_ahash.mojo similarity index 99% rename from stdlib/test/builtin/test__hash.mojo rename to stdlib/test/hashlib/test_ahash.mojo index 4228ea60da..49dcc79474 100644 --- a/stdlib/test/builtin/test__hash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -13,8 +13,8 @@ # RUN: %mojo %s from bit import pop_count -from builtin._hash import hash -from builtin.hash import hash as old_hash +from hashlib._ahash import hash +from hashlib.hash import hash as old_hash from testing import assert_equal, assert_not_equal, assert_true from memory import memset_zero, UnsafePointer from time import now diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/hashlib/test_hash.mojo similarity index 99% rename from stdlib/test/builtin/test_hash.mojo rename to stdlib/test/hashlib/test_hash.mojo index 1bf14b85a1..5e7662fbd7 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/hashlib/test_hash.mojo @@ -18,7 +18,7 @@ # These tests aren't _great_. They're platform specific, and implementation # specific. But for now they test behavior and reproducibility. -from builtin.hash import _hash_simd +from hashlib.hash import _hash_simd from testing import assert_equal, assert_not_equal, assert_true diff --git a/stdlib/test/builtin/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo similarity index 98% rename from stdlib/test/builtin/test_hasher.mojo rename to stdlib/test/hashlib/test_hasher.mojo index 2fa23271f7..bd83c2f4b0 100644 --- a/stdlib/test/builtin/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s -from builtin._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher +from hashlib._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher from memory import UnsafePointer from testing import assert_equal From 72f3f8bae1f20aec05a91f9c90b05091c8193e88 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 3 Oct 2024 11:42:21 -0600 Subject: [PATCH 1685/2019] [stdlib] Remove `String._byte_length()` The `String` and `StringSlice._byte_length` function has been deprecated for a while now in favor of the public `byte_length()` function. Just remove them now. MODULAR_ORIG_COMMIT_REV_ID: 2dc24f99b54ac29515a8871882a958129b17ffb1 --- stdlib/src/collections/string.mojo | 13 ------------- stdlib/src/utils/string_slice.mojo | 13 +------------ 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 98b29cf15e..f301a74ee9 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1447,19 +1447,6 @@ struct String( """ return max(len(self._buffer) - 1, 0) - @always_inline - @deprecated("use byte_length() instead") - fn _byte_length(self) -> Int: - """Get the string length in bytes. - - Returns: - The length of this string in bytes, excluding null terminator. - - Notes: - This does not include the trailing null terminator in the count. - """ - return max(len(self._buffer) - 1, 0) - fn _steal_ptr(inout self) -> UnsafePointer[UInt8]: """Transfer ownership of pointer to the underlying memory. The caller is responsible for freeing up the memory. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index aa24aebec7..1f481cc523 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -231,7 +231,7 @@ struct StringSlice[ # Ensure StringLiteral _actually_ always uses UTF-8 encoding. # TODO(#933): use when llvm intrinsics can be used at compile time # debug_assert( - # _is_valid_utf8(literal.unsafe_ptr(), literal._byte_length()), + # _is_valid_utf8(literal.unsafe_ptr(), literal.byte_length()), # "StringLiteral doesn't have valid UTF-8 encoding", # ) self = StaticString( @@ -487,17 +487,6 @@ struct StringSlice[ return len(self.as_bytes_span()) - @always_inline - @deprecated("use byte_length() instead") - fn _byte_length(self) -> Int: - """Get the length of this string slice in bytes. - - Returns: - The length of this string slice in bytes. - """ - - return len(self.as_bytes_span()) - fn _strref_dangerous(self) -> StringRef: """Returns an inner pointer to the string as a StringRef. From 96aa13bae83922de1d5802557e30e58431dfb6fc Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 3 Oct 2024 13:57:28 -0500 Subject: [PATCH 1686/2019] [stdlib][python-bindings] feat: Abstract init/del method wrapping This simplifies the user side of exposing a type to Python. MODULAR_ORIG_COMMIT_REV_ID: a66e26cd6677cdcbee9f918c81ee01baa76f0c4d --- stdlib/src/python/_bindings.mojo | 49 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 5f40a5af07..ae6f76e151 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -45,7 +45,13 @@ alias Typed_initproc = fn ( ) -> c_int -struct PyMojoObject[T: AnyType]: +trait Pythonable(Defaultable): + """Represents a Mojo type that can be used from Python.""" + + pass + + +struct PyMojoObject[T: Pythonable]: """Storage backing a PyObject* wrapping a Mojo value.""" var ob_base: PyObject @@ -61,15 +67,11 @@ struct PyMojoObject[T: AnyType]: @staticmethod fn python_type_object[ type_name: StringLiteral, - empty_init_func: fn (UnsafePointer[T]) -> None, - del_func: fn (UnsafePointer[T]) -> None, ](owned methods: List[PyMethodDef]) raises -> TypedPythonObject["Type"]: """Construct a Python 'type' describing PyMojoObject[T]. Parameters: type_name: The name of the Mojo type. - empty_init_func: A function that default initializes an instance of `T`. - del_func: A function that deinitializes an instance of `T`. """ var cpython = _get_global_python_itf().cpython() @@ -79,8 +81,8 @@ struct PyMojoObject[T: AnyType]: PyType_Slot.tp_new( cpython.lib.get_function[newfunc]("PyType_GenericNew") ), - PyType_Slot.tp_init(create_empty_init_wrapper[empty_init_func]()), - PyType_Slot.tp_dealloc(create_dealloc_wrapper[del_func]()), + PyType_Slot.tp_init(create_empty_init_wrapper[T]()), + PyType_Slot.tp_dealloc(create_dealloc_wrapper[T]()), # FIXME: Avoid leaking the methods data pointer in this way. PyType_Slot.tp_methods(methods.steal_data()), # Zeroed item terminator @@ -129,10 +131,13 @@ struct PyMojoObject[T: AnyType]: # # This function creates that wrapper function, and returns a pointer pointer to # it. -fn create_empty_init_wrapper[ - T: AnyType, //, - empty_init_func: fn (UnsafePointer[T]) -> None, -]() -> Typed_initproc: +fn create_empty_init_wrapper[T: Pythonable]() -> Typed_initproc: + """Create Python-compatible wrapper around a Mojo initializer function. + + Parameters: + T: The wrapped Mojo type. + """ + fn wrapper( py_self: PyObjectPtr, args: TypedPythonObject["Tuple"], @@ -152,11 +157,8 @@ fn create_empty_init_wrapper[ # Call the user-provided initialization function. # ------------------------------------------------ - # TODO(MOCO-1020): - # If/when Mojo supports an `init` convention, use it here. - # Change this callback to take an `init T` instead of an - # `UnsafePointer[T]`, for more ergonomic code in the caller. - empty_init_func(obj_ptr) + # TODO(MSTDL-950): Avoid forming ref through uninit pointee. + T.__init__(obj_ptr[]) return 0 except e: @@ -173,14 +175,17 @@ fn create_empty_init_wrapper[ return wrapper -fn create_dealloc_wrapper[ - T: AnyType, //, - del_func: fn (UnsafePointer[T]) -> None, -]() -> destructor: +fn create_dealloc_wrapper[T: Pythonable]() -> destructor: fn wrapper(py_self: PyObjectPtr): - var self: UnsafePointer[T] = PyMojoObject[T].unsafe_cast_obj(py_self) + var self_ptr: UnsafePointer[T] = PyMojoObject[T].unsafe_cast_obj( + py_self + ) - del_func(self) + # TODO(MSTDL-633): + # Is this always safe? Wrap in GIL, because this could + # evaluate arbitrary code? + # Destroy this `Person` instance. + self_ptr.destroy_pointee() return wrapper From f05927916ad5650273c78c3f311b7cc7146c49ba Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 3 Oct 2024 15:31:48 -0700 Subject: [PATCH 1687/2019] [stdlib] Remove `raises` from `Set.clear` There is no reason for this to be raising, as it will be a no-op 0 iteration loop if there are no elements. MODULAR_ORIG_COMMIT_REV_ID: 91b552403d174ab84fe7a285c2d6855dc5c16f3b --- stdlib/src/collections/set.mojo | 8 ++++++-- stdlib/test/collections/test_set.mojo | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 866382576e..71cf307408 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -583,14 +583,18 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): except: pass - fn clear(inout self) raises: + fn clear(inout self): """Removes all elements from the set. This method modifies the set in-place, removing all of its elements. After calling this method, the set will be empty. """ for _ in range(len(self)): - var a = self.pop() + # Can't fail from an empty set + try: + _ = self.pop() + except: + pass #! This code below (without using range function) won't pass tests #! It leaves set with one remaining item. Is this a bug? diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 3166a7115f..4dceebd59d 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -460,6 +460,11 @@ def test_discard(): def test_clear(): + # Shouldn't fail when clearing a 0 length set + set0 = Set[Int]() + set0.clear() + assert_equal(0, len(set0)) + set1 = Set[Int](1, 2, 3) set1.clear() assert_true(set1 == Set[Int]()) From 23aae5c1766f91fa25a284d7ce1d8a991289867e Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Thu, 3 Oct 2024 19:46:46 -0400 Subject: [PATCH 1688/2019] [External] [stdlib] Integrate `PyObject_SetItem` for speedup and error handling (#48447) [External] [stdlib] Integrate `PyObject_SetItem` for speedup and error handling Following the trend of: - https://github.com/modularml/mojo/issues/3549 - https://github.com/modularml/mojo/issues/3583 Again, noticing ~1.4x speedup on basic benchmarks: ```mojo for _ in range(iterations): var idx_a = random_si64(0, 999) var idx_b = random_si64(0, 9999) var idx_c = random_si64(0, 99999) a[idx_a] = idx_a b[idx_b] = idx_b c[idx_c] = idx_c ``` and improved error handling, preventing crashes. Co-authored-by: Joshua James Venter Closes modularml/mojo#3595 MODULAR_ORIG_COMMIT_REV_ID: 107c0b478171f86869b7cb94e57f5ea59d83e994 --- stdlib/src/python/_cpython.mojo | 20 ++++++++++++ stdlib/src/python/python_object.mojo | 37 +++++++++++----------- stdlib/test/python/test_python_object.mojo | 33 +++++++++++++++++++ 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index a46977f194..02ecb515cd 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -1053,6 +1053,26 @@ struct CPython: self._inc_total_rc() return r + fn PyObject_SetItem( + inout self, obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr + ) -> c_int: + var r = self.lib.get_function[ + fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int + ]("PyObject_SetItem")(obj, key, value) + + self.log( + "PyObject_SetItem result:", + r, + ", key:", + key._get_ptr_as_int(), + ", value:", + value._get_ptr_as_int(), + ", parent obj:", + obj._get_ptr_as_int(), + ) + + return r + fn PyObject_GetAttrString( inout self, obj: PyObjectPtr, diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 709c773477..a662df2684 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -724,29 +724,30 @@ struct PythonObject( args: The key or keys to set on this object. value: The value to set. """ + var cpython = _get_global_python_itf().cpython() var size = len(args) + var key_obj: PyObjectPtr - var cpython = _get_global_python_itf().cpython() - var tuple_obj = cpython.PyTuple_New(size + 1) - for i in range(size): - var arg_value = args[i].py_object - cpython.Py_IncRef(arg_value) - var result = cpython.PyTuple_SetItem(tuple_obj, i, arg_value) - if result != 0: - raise Error("internal error: PyTuple_SetItem failed") + if size == 1: + key_obj = args[0].py_object + else: + key_obj = cpython.PyTuple_New(size) + for i in range(size): + var arg_value = args[i].py_object + cpython.Py_IncRef(arg_value) + var result = cpython.PyTuple_SetItem(key_obj, i, arg_value) + if result != 0: + raise Error("internal error: PyTuple_SetItem failed") + cpython.Py_IncRef(key_obj) cpython.Py_IncRef(value.py_object) - var result2 = cpython.PyTuple_SetItem(tuple_obj, size, value.py_object) - if result2 != 0: - raise Error("internal error: PyTuple_SetItem failed") - - var callable_obj = cpython.PyObject_GetAttrString( - self.py_object, "__setitem__" + var result = cpython.PyObject_SetItem( + self.py_object, key_obj, value.py_object ) - var result = cpython.PyObject_CallObject(callable_obj, tuple_obj) - cpython.Py_DecRef(callable_obj) - cpython.Py_DecRef(tuple_obj) - Python.throw_python_exception_if_error_state(cpython) + if result != 0: + Python.throw_python_exception_if_error_state(cpython) + cpython.Py_DecRef(key_obj) + cpython.Py_DecRef(value.py_object) fn _call_zero_arg_method( self, method_name: StringRef diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 618da8a703..c3738e9412 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -485,6 +485,38 @@ fn test_getitem_raises() raises: _ = with_2d[2] +def test_setitem_raises(): + t = Python.evaluate("(1,2,3)") + with assert_raises( + contains="'tuple' object does not support item assignment" + ): + t[0] = 0 + + lst = Python.evaluate("[1, 2, 3]") + with assert_raises(contains="list assignment index out of range"): + lst[10] = 4 + + s = Python.evaluate('"hello"') + with assert_raises( + contains="'str' object does not support item assignment" + ): + s[3] = "xy" + + custom = Python.evaluate( + """type('Custom', (), { + '__init__': lambda self: None, + })()""" + ) + with assert_raises( + contains="'Custom' object does not support item assignment" + ): + custom[0] = 0 + + d = Python.evaluate("{}") + with assert_raises(contains="unhashable type: 'list'"): + d[[1, 2, 3]] = 5 + + def main(): # initializing Python instance calls init_python var python = Python() @@ -500,3 +532,4 @@ def main(): test_none() test_nested_object() test_getitem_raises() + test_setitem_raises() From 08d30c44829d20e2caad167bf0ef702d94bc45da Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 3 Oct 2024 16:57:07 -0700 Subject: [PATCH 1689/2019] [stdlib] improve `InlineArray` indexing perf Allows all the IR and runtime penalty for the indexing check to be folded away for `InlineArray` on GPU. Allows the check to be turned off using `ASSERT=none` on CPU for improved perf at the cost of safety. Match up the debug_assert error message with others in the stdlib. MODULAR_ORIG_COMMIT_REV_ID: a1e4896459e42f6bf14a4205e87e1d5095263561 --- .../src/collections/_index_normalization.mojo | 65 ++++++------------- stdlib/src/collections/inline_array.mojo | 9 ++- .../collections/test_index_normalization.mojo | 41 ++++-------- 3 files changed, 36 insertions(+), 79 deletions(-) diff --git a/stdlib/src/collections/_index_normalization.mojo b/stdlib/src/collections/_index_normalization.mojo index 6df7c773f7..367a98d306 100644 --- a/stdlib/src/collections/_index_normalization.mojo +++ b/stdlib/src/collections/_index_normalization.mojo @@ -13,37 +13,6 @@ """The utilities provided in this module help normalize the access to data elements in arrays.""" -from sys import triple_is_nvidia_cuda -from os import abort - - -fn get_out_of_bounds_error_message[ - container_name: StringLiteral -](i: Int, container_length: Int) -> String: - if container_length == 0: - return ( - "The " - + container_name - + " has a length of 0. Thus it's not possible to access its values" - " with an index but the index value " - + str(i) - + " was used. Aborting now to avoid an out-of-bounds access." - ) - else: - return ( - "The " - + container_name - + " has a length of " - + str(container_length) - + ". Thus the index provided should be between " - + str(-container_length) - + " (inclusive) and " - + str(container_length) - + " (exclusive) but the index value " - + str(i) - + " was used. Aborting now to avoid an out-of-bounds access." - ) - @always_inline fn normalize_index[ @@ -64,16 +33,24 @@ fn normalize_index[ Returns: The normalized index value. """ - var container_length = len(container) - if not (-container_length <= idx < container_length): - - @parameter - if triple_is_nvidia_cuda(): - abort() - else: - abort( - get_out_of_bounds_error_message[container_name]( - idx, container_length - ) - ) - return idx + int(idx < 0) * container_length + debug_assert[assert_mode="safe", cpu_only=True]( + len(container) > 0, + "indexing into a ", + container_name, + " that has 0 elements", + ) + debug_assert[assert_mode="safe", cpu_only=True]( + -len(container) <= idx < len(container), + container_name, + " has length: ", + len(container), + " index out of bounds: ", + idx, + " should be between ", + -len(container), + " and ", + len(container) - 1, + ) + if idx >= 0: + return idx + return idx + len(container) diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 0a787cbd03..2eb09e11d2 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -227,7 +227,6 @@ struct InlineArray[ A reference to the item at the given index. """ var normalized_index = normalize_index["InlineArray"](idx, self) - return self.unsafe_get(normalized_index) @always_inline("nodebug") @@ -288,10 +287,10 @@ struct InlineArray[ var idx_as_int = index(idx) debug_assert( 0 <= idx_as_int < size, - ( - "Index must be within bounds when using" - " `InlineArray.unsafe_get()`." - ), + " InlineArray.unsafe_get() index out of bounds: ", + idx_as_int, + " should be less than: ", + size, ) var ptr = __mlir_op.`pop.array.gep`( UnsafePointer.address_of(self._array).address, diff --git a/stdlib/test/collections/test_index_normalization.mojo b/stdlib/test/collections/test_index_normalization.mojo index fe74b90622..703ed95f9f 100644 --- a/stdlib/test/collections/test_index_normalization.mojo +++ b/stdlib/test/collections/test_index_normalization.mojo @@ -10,42 +10,23 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s +# RUN: %bare-mojo -D ASSERT=warn %s | FileCheck %s -from collections._index_normalization import ( - get_out_of_bounds_error_message, - normalize_index, -) +from collections._index_normalization import normalize_index from testing import assert_equal def test_out_of_bounds_message(): - assert_equal( - get_out_of_bounds_error_message["List"](5, 2), - ( - "The List has a length of 2. Thus the index provided should be" - " between -2 (inclusive) and 2 (exclusive) but the index value 5" - " was used. Aborting now to avoid an out-of-bounds access." - ), - ) - - assert_equal( - get_out_of_bounds_error_message["List"](0, 0), - ( - "The List has a length of 0. Thus it's not possible to access its" - " values with an index but the index value 0 was used. Aborting now" - " to avoid an out-of-bounds access." - ), - ) - assert_equal( - get_out_of_bounds_error_message["InlineArray"](8, 0), - ( - "The InlineArray has a length of 0. Thus it's not possible to" - " access its values with an index but the index value 8 was used." - " Aborting now to avoid an out-of-bounds access." - ), - ) + l = List[Int](1, 2) + # CHECK: index out of bounds: 2 + _ = normalize_index["List"](2, l) + # CHECK: index out of bounds: -3 + _ = normalize_index["List"](-3, l) + + l2 = List[Int]() + # CHECK: indexing into a List that has 0 elements + _ = normalize_index["List"](2, l2) def test_normalize_index(): From d990226936bed8d0fa82c1c56c2fcbbab884fbb8 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 4 Oct 2024 18:29:17 +0900 Subject: [PATCH 1690/2019] [mojo] Add `__hasmore__` function to all iterator types Iterators are in a bit of a weird state, and this primarily reflects the weird state of Mojo for loops. While the for loop API seems relatively generic, in reality it's always backed by an `Int` comparison. Evidence of this is the required `__len__` function on iterators, which has always been strange. The desired API for iterators, matching Python semantics, would be ```mojo trait Iterator: alias ElementType: AnyType fn __next__(inout self) raises StopIteration -> ElementType: ... ``` Mojo now has associated aliases, but not typed throws. In the meantime, the API will be ````mojo trait Iterator: alias ElementType: AnyType fn __next__(inout self) -> ElementType: ... fn __hasmore__(self) -> Bool: ... ``` Which is slightly more generic than `__len__`. The implementation is ```mojo fn __hasmore__(self) -> Bool: return self.__len__() > 0 ``` Which matches what the compiler was previously generating. This PR just adds it to all the iterators and doesn't actually implement the API change for for loops yet. MODULAR_ORIG_COMMIT_REV_ID: c9490d47189db3a9e127c724b36867d51cd620e6 --- stdlib/src/builtin/builtin_list.mojo | 8 +++++++ stdlib/src/builtin/range.mojo | 28 +++++++++++++++++++++++++ stdlib/src/collections/dict.mojo | 12 +++++++++++ stdlib/src/collections/inline_list.mojo | 4 ++++ stdlib/src/collections/list.mojo | 4 ++++ stdlib/src/collections/vector.mojo | 4 ++++ stdlib/src/python/python_object.mojo | 4 ++++ stdlib/src/utils/span.mojo | 4 ++++ stdlib/src/utils/string_slice.mojo | 4 ++++ 9 files changed, 72 insertions(+) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index da3334f0b7..d2b22c2bb2 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -139,6 +139,10 @@ struct _VariadicListIter[type: AnyTrivialRegType]: self.index += 1 return self.src[self.index - 1] + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: return len(self.src) - self.index @@ -245,6 +249,10 @@ struct _VariadicListMemIter[ Pointer.address_of(self.src[][self.index - 1]) ) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: return len(self.src[]) - self.index diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index fa87b798ee..61ab61a9fc 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -62,6 +62,10 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): self.curr -= 1 return self.end - curr + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @always_inline("nodebug") fn __len__(self) -> Int: return self.curr @@ -92,6 +96,10 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): self.start += 1 return start + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @always_inline("nodebug") fn __len__(self) -> Int: return max(0, self.end - self.start) @@ -128,6 +136,10 @@ struct _StridedRangeIterator(Sized): self.start += self.step return result + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @value @register_passable("trivial") @@ -152,6 +164,10 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): self.start += self.step return result + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @always_inline("nodebug") fn __len__(self) -> Int: # If the step is positive we want to check that the start is smaller @@ -322,6 +338,10 @@ struct _UIntZeroStartingRange(UIntSized): self.curr -= 1 return self.end - curr + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @always_inline fn __len__(self) -> UInt: return self.curr @@ -349,6 +369,10 @@ struct _UIntStridedRangeIterator(UIntSized): self.start += self.step return result + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @value @register_passable("trivial") @@ -385,6 +409,10 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): self.start += self.step return result + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @always_inline fn __len__(self) -> UInt: if self.start >= self.end: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e0f8b6631a..2e5435928a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -108,6 +108,10 @@ struct _DictEntryIter[ self.seen += 1 return Pointer.address_of(opt_entry_ref[].value()) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: return len(self.src[]) - self.seen @@ -142,6 +146,10 @@ struct _DictKeyIter[ ) -> Pointer[K, __lifetime_of(self.iter.__next__()[].key)]: return Pointer.address_of(self.iter.__next__()[].key) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: return self.iter.__len__() @@ -188,6 +196,10 @@ struct _DictValueIter[ UnsafePointer.address_of(entry_ref[].value)[] ) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: return self.iter.__len__() diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 6987e3b98c..1fd52eb6e5 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -63,6 +63,10 @@ struct _InlineListIter[ self.index -= 1 return Pointer.address_of(self.src[][self.index]) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: @parameter if forward: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 6233443a36..69515acac8 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -71,6 +71,10 @@ struct _ListIter[ self.index -= 1 return Pointer.address_of(self.src[][self.index]) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: @parameter if forward: diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 4a9636fb2a..f2452865c3 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -45,6 +45,10 @@ struct _VecIter[ self.i += 1 return deref(self.vec, self.i - 1) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: return self.size - self.i diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index a662df2684..b713712678 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -101,6 +101,10 @@ struct _PyIter(Sized): self.preparedNextItem = PythonObject(maybeNextItem) return current + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: """Return zero to halt iteration. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 53838433ad..140b0df71f 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -60,6 +60,10 @@ struct _SpanIter[ self.index -= 1 return Pointer.address_of(self.src[self.index]) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + @always_inline fn __len__(self) -> Int: @parameter diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 1f481cc523..39166bc6c2 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -184,6 +184,10 @@ struct _StringSliceIter[ unsafe_from_utf8_ptr=self.ptr + self.index, len=byte_len ) + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + fn __len__(self) -> Int: @parameter if forward: From bef19a11cac54e628818549f6bc1806ed3086abb Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Fri, 4 Oct 2024 12:25:46 -0400 Subject: [PATCH 1691/2019] [External] [stdlib] ABI bindings: `Int` to `c_int` (#48486) [External] [stdlib] ABI bindings: `Int` to `c_int` Fix some incorrect uses of `Int` in favor of `c_int`, which is 32-bits on modern platforms, similar to `int` in the corresponding C code. Co-authored-by: Joshua James Venter Closes modularml/mojo#3600 MODULAR_ORIG_COMMIT_REV_ID: 2bbdea279d4dc9b97ef2751012a839b4c4518386 --- stdlib/src/python/_cpython.mojo | 66 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 02ecb515cd..6d699ab8b5 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -734,11 +734,13 @@ struct CPython: self._inc_total_rc() return r + # int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val) + # ref: https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem fn PyDict_SetItem( inout self, dict_obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr - ) -> Int: + ) -> c_int: var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> Int32 + fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int ](StringRef("PyDict_SetItem"))(dict_obj, key, value) self.log( @@ -748,7 +750,7 @@ struct CPython: value._get_ptr_as_int(), ) - return int(r) + return r fn PyDict_GetItemWithError( inout self, dict_obj: PyObjectPtr, key: PyObjectPtr @@ -774,6 +776,8 @@ struct CPython: self.dict_type = self.lib.get_function[PyObjectPtr]("PyDict_Type") return self.dict_type + # int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) + # ref: https://docs.python.org/3/c-api/dict.html#c.PyDict_Next fn PyDict_Next( inout self, dictionary: PyObjectPtr, p: Int ) -> PyKeyValuePair: @@ -789,7 +793,7 @@ struct CPython: UnsafePointer[Int], UnsafePointer[UnsafePointer[Int8]], UnsafePointer[UnsafePointer[Int8]], - ) -> Int + ) -> c_int ]("PyDict_Next")( dictionary, position, @@ -876,13 +880,15 @@ struct CPython: var module_api_version = 1013 return create_module_fn(module_def_ptr, module_api_version) + # int PyModule_AddFunctions(PyObject *module, PyMethodDef *functions) + # ref: https://docs.python.org/3/c-api/module.html#c.PyModule_AddFunctions fn PyModule_AddFunctions( inout self, mod: PyObjectPtr, functions: UnsafePointer[PyMethodDef], - ) -> Int: + ) -> c_int: var add_functions_fn = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[PyMethodDef]) -> Int + fn (PyObjectPtr, UnsafePointer[PyMethodDef]) -> c_int ]("PyModule_AddFunctions") return add_functions_fn(mod, functions) @@ -922,6 +928,8 @@ struct CPython: # Python Evaluation # ===-------------------------------------------------------------------===# + # int PyRun_SimpleString(const char *command) + # ref: https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleString fn PyRun_SimpleString(inout self, strref: StringRef) -> Bool: """Executes the given Python code. @@ -932,7 +940,7 @@ struct CPython: `True` if the code executed successfully or `False` if the code raised an exception. """ - var status = self.lib.get_function[fn (UnsafePointer[UInt8]) -> Int]( + var status = self.lib.get_function[fn (UnsafePointer[UInt8]) -> c_int]( StringRef("PyRun_SimpleString") )(strref.data) # PyRun_SimpleString returns 0 on success and -1 if an exception was @@ -1006,15 +1014,17 @@ struct CPython: # Python Object operations # ===-------------------------------------------------------------------===# + # int Py_Is(PyObject *x, PyObject *y) + # ref: https://docs.python.org/3/c-api/structures.html#c.Py_Is fn Py_Is( inout self, rhs: PyObjectPtr, lhs: PyObjectPtr, ) -> Bool: if self.version.minor >= 10: - var r = self.lib.get_function[fn (PyObjectPtr, PyObjectPtr) -> Int]( - "Py_Is" - )(rhs, lhs) + var r = self.lib.get_function[ + fn (PyObjectPtr, PyObjectPtr) -> c_int + ]("Py_Is")(rhs, lhs) return r > 0 else: return rhs == lhs @@ -1095,11 +1105,13 @@ struct CPython: self._inc_total_rc() return r + # int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v) + # ref: https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString fn PyObject_SetAttrString( inout self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr - ) -> Int: + ) -> c_int: var r = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[UInt8], PyObjectPtr) -> Int + fn (PyObjectPtr, UnsafePointer[UInt8], PyObjectPtr) -> c_int ]("PyObject_SetAttrString")(obj, name.data, new_value) self.log( @@ -1156,15 +1168,15 @@ struct CPython: self._inc_total_rc() return r + # int PyObject_IsTrue(PyObject *o) + # ref: https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue fn PyObject_IsTrue( inout self, obj: PyObjectPtr, - ) -> Int: - return int( - self.lib.get_function[fn (PyObjectPtr) -> Int32]("PyObject_IsTrue")( - obj - ) - ) + ) -> c_int: + return self.lib.get_function[fn (PyObjectPtr) -> c_int]( + "PyObject_IsTrue" + )(obj) fn PyObject_Length( inout self, @@ -1228,18 +1240,20 @@ struct CPython: fn (PyObjectPtr, Py_ssize_t) -> PyObjectPtr ]("PyTuple_GetItem")(tuple, pos) + # int PyTuple_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o) + # ref: https://docs.python.org/3/c-api/tuple.html#c.PyTuple_SetItem fn PyTuple_SetItem( inout self, tuple_obj: PyObjectPtr, index: Int, element: PyObjectPtr, - ) -> Int: + ) -> c_int: # PyTuple_SetItem steals the reference - the element object will be # destroyed along with the tuple self._dec_total_rc() - return self.lib.get_function[fn (PyObjectPtr, Int, PyObjectPtr) -> Int]( - StringRef("PyTuple_SetItem") - )(tuple_obj, index, element) + return self.lib.get_function[ + fn (PyObjectPtr, Int, PyObjectPtr) -> c_int + ](StringRef("PyTuple_SetItem"))(tuple_obj, index, element) # ===-------------------------------------------------------------------===# # Python List operations @@ -1511,14 +1525,18 @@ struct CPython: self._inc_total_rc() return next_obj + # int PyIter_Check(PyObject *o) + # ref: https://docs.python.org/3/c-api/iter.html#c.PyIter_Check fn PyIter_Check(inout self, obj: PyObjectPtr) -> Bool: var follows_iter_protocol = self.lib.get_function[ - fn (PyObjectPtr) -> Int + fn (PyObjectPtr) -> c_int ]("PyIter_Check")(obj) return follows_iter_protocol != 0 + # int PySequence_Check(PyObject *o) + # https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check fn PySequence_Check(inout self, obj: PyObjectPtr) -> Bool: var follows_seq_protocol = self.lib.get_function[ - fn (PyObjectPtr) -> Int + fn (PyObjectPtr) -> c_int ]("PySequence_Check")(obj) return follows_seq_protocol != 0 From dacdf6ad6d153d24eec00250855e7f6c10d1a623 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Fri, 4 Oct 2024 16:45:15 -0400 Subject: [PATCH 1692/2019] [External] [stdlib] Clean up Slice (#48448) [External] [stdlib] Clean up Slice - Prevent warnings in docstring (related to https://github.com/modularml/mojo/issues/3572) - Add comment headers Co-authored-by: Joshua James Venter Closes modularml/mojo#3594 MODULAR_ORIG_COMMIT_REV_ID: d4d8a76518dab86aa8f62c465566b2c6712ab233 --- stdlib/src/builtin/builtin_slice.mojo | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 48958291a1..64fcdf6458 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -40,6 +40,7 @@ struct Slice( ``` """ + # Fields var start: Optional[Int] """The starting index of the slice.""" var end: Optional[Int] @@ -47,6 +48,10 @@ struct Slice( var step: Int """The step increment value of the slice.""" + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self, start: Int, end: Int): """Construct slice given the start and end values. @@ -85,6 +90,10 @@ struct Slice( """ self.__init__(start=other.start, end=other.end, step=other.step) + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + @no_inline fn __str__(self) -> String: """Gets the string representation of the span. @@ -169,20 +178,20 @@ struct Slice( Negative indices are wrapped using the length of the container. ```mojo s = slice(0, -1, 1) - s.indices(5) # returns (0, 4, 1) + i = s.indices(5) # returns (0, 4, 1) ``` None indices are defaulted to the start or the end of the container based on whether `step` is positive or negative. ```mojo s = slice(None, None, 1) - s.indices(5) # returns (0, 5, 1) + i = s.indices(5) # returns (0, 5, 1) ``` Out of bounds indices are clamped using the size of the container. ```mojo s = slice(20) - s.indices(5) # returns (0, 5, 1) + i = s.indices(5) # returns (0, 5, 1) ``` Args: @@ -218,6 +227,11 @@ struct Slice( return (start.value(), end.value(), step) +# ===-----------------------------------------------------------------------===# +# Slice constructor functions +# ===-----------------------------------------------------------------------===# + + @always_inline("nodebug") fn slice(end: Int) -> Slice: """Construct slice given the end value. From 286d552b5369dc43008069048d17aa7b26fecb88 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 4 Oct 2024 15:13:02 -0700 Subject: [PATCH 1693/2019] Fix typos MODULAR_ORIG_COMMIT_REV_ID: afd6bc8e0738edf1981e1bac7559c242a4d53cc4 --- docs/manual/traits.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 60eb9bfb05..6643bb17ce 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -567,13 +567,13 @@ "raise an error. Any type that conforms to `Stringable` or `StringableRaising`\n", "also works with the built-in [`str()`](/mojo/stdlib/builtin/str/str) function to\n", "explicitly return a `String`. These traits also mean that the type can support\n", - "both the `{!s}` and `{}` format specifiers of the `String` class'\n", + "both the `{!s}` and `{}` format specifiers of the `String` class's\n", "[`format()`](/mojo/stdlib/collections/string/String#format) method. These traits\n", "require the type to define the\n", "[`__str__()`](/mojo/stdlib/builtin/str/Stringable#__str__) method.\n", "\n", "In contrast, the [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", - "trait that defines a type that can be used with the built-in\n", + "trait defines a type that can be used with the built-in\n", "[`repr()`](/mojo/stdlib/builtin/repr/repr) function, as well as the `{!r}`\n", "format specifier of the `format()` method. This trait requires the type to\n", "define the [`__repr__()`](/mojo/stdlib/builtin/repr/Representable#__repr__)\n", From 872634d33e728cf5eb05fc568028357d53b11a2f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sat, 5 Oct 2024 08:17:08 +0900 Subject: [PATCH 1694/2019] [mojo] Change for loop codegen to emit `__hasmore__` (#48464) This gets `index` out of the implementation of for loops. Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 7b94509c9ec696395e2958b0e6817382a777dca3 --- stdlib/src/builtin/range.mojo | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 61ab61a9fc..1a072e22c0 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -340,7 +340,8 @@ struct _UIntZeroStartingRange(UIntSized): @always_inline fn __hasmore__(self) -> Bool: - return self.__len__() > 0 + # FIXME(KERN-1024): This should be an unsigned comparison! + return Int(self.__len__().value) > 0 @always_inline fn __len__(self) -> UInt: @@ -371,7 +372,8 @@ struct _UIntStridedRangeIterator(UIntSized): @always_inline fn __hasmore__(self) -> Bool: - return self.__len__() > 0 + # FIXME(KERN-1024): This should be an unsigned comparison! + return Int(self.__len__().value) > 0 @value @@ -411,7 +413,8 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): @always_inline fn __hasmore__(self) -> Bool: - return self.__len__() > 0 + # FIXME(KERN-1024): This should be an unsigned comparison! + return Int(self.__len__().value) > 0 @always_inline fn __len__(self) -> UInt: From 153e41ee17a95a6d4342cb67669d1ab242e7a017 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sat, 5 Oct 2024 08:17:35 +0900 Subject: [PATCH 1695/2019] [mojo-stdlib] Remove some nodebug inline annotations (NFC) `@always_inline("nodebug")` should only be used for extremely low-level functions and basic data types, e.g. `Int`, `Bool`, or functions that wrap inline assembly, direct MLIR operations, or LLVM intrinsics. In practically every other case, this decorator should not be used, since it erases debuginfo from the function and makes debugging hard. This PR removes some obvious misuses of this decorator. Marking non-trivial functions with this decorator also negatively affects compile time. MODULAR_ORIG_COMMIT_REV_ID: 353c0b901861c86d37a9b720a244fd40165f1c5d --- .../benchmarks/algorithm/bench_vectorize.mojo | 2 +- stdlib/src/bit/bit.mojo | 8 ++-- stdlib/src/builtin/builtin_list.mojo | 10 ++--- stdlib/src/builtin/builtin_slice.mojo | 14 +++---- stdlib/src/builtin/range.mojo | 40 +++++++++---------- stdlib/src/builtin/simd.mojo | 4 +- stdlib/src/collections/inline_array.mojo | 10 ++--- stdlib/src/python/_cpython.mojo | 2 +- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo index 09f210d412..6d6f59bf2f 100644 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ b/stdlib/benchmarks/algorithm/bench_vectorize.mojo @@ -48,7 +48,7 @@ struct Op(Stringable): alias ld = 5 alias st = 6 - @always_inline("nodebug") + @always_inline fn __eq__(self, other: Op) -> Bool: return self.op_code == other.op_code diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 5a14fc8f21..5405411ab1 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -261,7 +261,7 @@ fn pop_count[ # ===----------------------------------------------------------------------===# -@always_inline("nodebug") +@always_inline fn bit_not[ type: DType, width: Int, // ](val: SIMD[type, width]) -> SIMD[type, width]: @@ -417,7 +417,7 @@ fn bit_ceil(val: Int) -> Int: return 1 << bit_width(val - 1) -@always_inline("nodebug") +@always_inline fn bit_ceil[ type: DType, width: Int, // ](val: SIMD[type, width]) -> SIMD[type, width]: @@ -453,7 +453,7 @@ fn bit_ceil[ # reference: https://en.cppreference.com/w/cpp/numeric/bit_floor -@always_inline("nodebug") +@always_inline fn bit_floor(val: Int) -> Int: """Computes the largest power of 2 that is less than or equal to the input value. Any integral value less than or equal to 0 will be floored to 0. @@ -470,7 +470,7 @@ fn bit_floor(val: Int) -> Int: return 1 << (bit_width(val) - 1) -@always_inline("nodebug") +@always_inline fn bit_floor[ type: DType, width: Int, // ](val: SIMD[type, width]) -> SIMD[type, width]: diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index d2b22c2bb2..bce38fe79f 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -39,7 +39,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # Life cycle methods # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __init__(inout self, owned *args: *Ts): """Construct the list literal from the given values. @@ -48,7 +48,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): """ self.storage = Tuple(storage=args^) - @always_inline("nodebug") + @always_inline fn __copyinit__(inout self, existing: Self): """Copy construct the tuple. @@ -70,7 +70,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # Trait implementations # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: """Get the list length. @@ -83,7 +83,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # Methods # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn get[i: Int, T: CollectionElement](self) -> ref [self.storage] T: """Get a list element at the given index. @@ -100,7 +100,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # Operator dunders # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __contains__[ T: EqualityComparableCollectionElement ](self, value: T) -> Bool: diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 64fcdf6458..e8989e8cd9 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -52,7 +52,7 @@ struct Slice( # Life cycle methods # ===-------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __init__(inout self, start: Int, end: Int): """Construct slice given the start and end values. @@ -64,7 +64,7 @@ struct Slice( self.end = end self.step = 1 - @always_inline("nodebug") + @always_inline fn __init__( inout self, start: Optional[Int], @@ -138,7 +138,7 @@ struct Slice( writer.write(repr(self.step)) writer.write(")") - @always_inline("nodebug") + @always_inline fn __eq__(self, other: Self) -> Bool: """Compare this slice to the other. @@ -155,7 +155,7 @@ struct Slice( and self.step == other.step ) - @always_inline("nodebug") + @always_inline fn __ne__(self, other: Self) -> Bool: """Compare this slice to the other. @@ -232,7 +232,7 @@ struct Slice( # ===-----------------------------------------------------------------------===# -@always_inline("nodebug") +@always_inline fn slice(end: Int) -> Slice: """Construct slice given the end value. @@ -245,7 +245,7 @@ fn slice(end: Int) -> Slice: return Slice(None, end, None) -@always_inline("nodebug") +@always_inline fn slice(start: Int, end: Int) -> Slice: """Construct slice given the start and end values. @@ -259,7 +259,7 @@ fn slice(start: Int, end: Int) -> Slice: return Slice(start, end) -@always_inline("nodebug") +@always_inline fn slice( start: Optional[Int], end: Optional[Int], step: Optional[Int] ) -> Slice: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 1a072e22c0..9427bf6986 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -29,7 +29,7 @@ from utils._select import _select_register_value as select # ===----------------------------------------------------------------------=== # -@always_inline("nodebug") +@always_inline fn _sign(x: Int) -> Int: var result = 0 result = select(x > 0, 1, result) @@ -47,12 +47,12 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): var curr: Int var end: Int - @always_inline("nodebug") + @always_inline fn __init__(inout self, end: Int): self.curr = max(0, end) self.end = self.curr - @always_inline("nodebug") + @always_inline fn __iter__(self) -> Self: return self @@ -66,16 +66,16 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): fn __hasmore__(self) -> Bool: return self.__len__() > 0 - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: return self.curr - @always_inline("nodebug") + @always_inline fn __getitem__(self, idx: Int) -> Int: debug_assert(idx < self.__len__(), "index out of range") return index(idx) - @always_inline("nodebug") + @always_inline fn __reversed__(self) -> _StridedRange: return range(self.end - 1, -1, -1) @@ -86,7 +86,7 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): var start: Int var end: Int - @always_inline("nodebug") + @always_inline fn __iter__(self) -> Self: return self @@ -100,16 +100,16 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): fn __hasmore__(self) -> Bool: return self.__len__() > 0 - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: return max(0, self.end - self.start) - @always_inline("nodebug") + @always_inline fn __getitem__(self, idx: Int) -> Int: debug_assert(idx < self.__len__(), "index out of range") return self.start + index(idx) - @always_inline("nodebug") + @always_inline fn __reversed__(self) -> _StridedRange: return range(self.end - 1, self.start - 1, -1) @@ -148,17 +148,17 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): var end: Int var step: Int - @always_inline("nodebug") + @always_inline fn __init__(inout self, start: Int, end: Int): self.start = start self.end = end self.step = 1 - @always_inline("nodebug") + @always_inline fn __iter__(self) -> _StridedRangeIterator: return _StridedRangeIterator(self.start, self.end, self.step) - @always_inline("nodebug") + @always_inline fn __next__(inout self) -> Int: var result = self.start self.start += self.step @@ -168,7 +168,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): fn __hasmore__(self) -> Bool: return self.__len__() > 0 - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: # If the step is positive we want to check that the start is smaller # than the end, if the step is negative we want to check the reverse. @@ -185,12 +185,12 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): # return 0 without a branch. return ceildiv(select(cnd, 0, numerator), select(cnd, 1, denominator)) - @always_inline("nodebug") + @always_inline fn __getitem__(self, idx: Int) -> Int: debug_assert(idx < self.__len__(), "index out of range") return self.start + index(idx) * self.step - @always_inline("nodebug") + @always_inline fn __reversed__(self) -> _StridedRange: var shifted_end = self.end - _sign(self.step) var start = shifted_end - ((shifted_end - self.start) % self.step) @@ -199,7 +199,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return range(start, end, step) -@always_inline("nodebug") +@always_inline fn range[type: Intable](end: type) -> _ZeroStartingRange: """Constructs a [0; end) Range. @@ -231,7 +231,7 @@ fn range[type: IntableRaising](end: type) raises -> _ZeroStartingRange: return _ZeroStartingRange(int(end)) -@always_inline("nodebug") +@always_inline fn range[t0: Intable, t1: Intable](start: t0, end: t1) -> _SequentialRange: """Constructs a [start; end) Range. @@ -249,7 +249,7 @@ fn range[t0: Intable, t1: Intable](start: t0, end: t1) -> _SequentialRange: return _SequentialRange(int(start), int(end)) -@always_inline("nodebug") +@always_inline fn range[ t0: IntableRaising, t1: IntableRaising ](start: t0, end: t1) raises -> _SequentialRange: @@ -383,7 +383,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): var end: UInt var step: UInt - @always_inline("nodebug") + @always_inline fn __init__(inout self, start: UInt, end: UInt, step: UInt): self.start = start self.end = end diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f12086c554..d67a0af69f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -384,7 +384,7 @@ struct SIMD[type: DType, size: Int]( for i in range(size): self[i] = elems[i] - @always_inline("nodebug") + @always_inline fn __init__(inout self, value: FloatLiteral): """Initializes the SIMD vector with a float. @@ -2049,7 +2049,7 @@ struct SIMD[type: DType, size: Int]( result[i] = self[int(mask[i])] return result - @always_inline("nodebug") + @always_inline fn slice[ output_width: Int, /, *, offset: Int = 0 ](self) -> SIMD[type, output_width]: diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 2eb09e11d2..201222f15d 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -154,7 +154,7 @@ struct InlineArray[ self = Self(storage=elems^) - @always_inline("nodebug") + @always_inline fn __init__( inout self, *, @@ -216,7 +216,7 @@ struct InlineArray[ # Operator dunders # ===------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn __getitem__(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: """Get a `Pointer` to the element at the given index. @@ -229,7 +229,7 @@ struct InlineArray[ var normalized_index = normalize_index["InlineArray"](idx, self) return self.unsafe_get(normalized_index) - @always_inline("nodebug") + @always_inline fn __getitem__[ idx: Int, ](ref [_]self: Self) -> ref [self] Self.ElementType: @@ -255,7 +255,7 @@ struct InlineArray[ # Trait implementations # ===------------------------------------------------------------------=== # - @always_inline("nodebug") + @always_inline fn __len__(self) -> Int: """Returns the length of the array. This is a known constant value. @@ -268,7 +268,7 @@ struct InlineArray[ # Methods # ===------------------------------------------------------------------===# - @always_inline("nodebug") + @always_inline fn unsafe_get(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: """Get a reference to an element of self without checking index bounds. diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 6d699ab8b5..3652d84c5d 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -96,7 +96,7 @@ struct PyKeyValuePair: struct PyObjectPtr: var value: UnsafePointer[Int8] - @always_inline("nodebug") + @always_inline fn __init__(inout self): self.value = UnsafePointer[Int8]() From ca45cf98b102235a4a8c25f6250aa6e9c645642d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 5 Oct 2024 10:56:46 -0700 Subject: [PATCH 1696/2019] [******] Enable one to specify the integer bitwidth for StaticIntTuple MODULAR_ORIG_COMMIT_REV_ID: 553934c907349bd96eee4981bb43de95b24f2cea --- stdlib/src/builtin/simd.mojo | 32 +++-- stdlib/src/math/math.mojo | 2 +- stdlib/src/utils/index.mojo | 214 ++++++++++++++++++----------- stdlib/src/utils/static_tuple.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 12 +- 5 files changed, 156 insertions(+), 106 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d67a0af69f..02fee40887 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -42,7 +42,7 @@ from builtin.format_int import _try_write_int from collections import InlineArray from memory import bitcast, UnsafePointer -from utils import StringSlice, StaticIntTuple, Span +from utils import StringSlice, StaticTuple, StaticIntTuple, Span from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan @@ -1802,7 +1802,7 @@ struct SIMD[type: DType, size: Int]( ) @always_inline("nodebug") - fn _shuffle_list[ + fn _shuffle_variadic[ *mask: Int, output_size: Int = size ](self, other: Self) -> SIMD[type, output_size]: """Shuffles (also called blend) the values of the current vector with @@ -1835,6 +1835,8 @@ struct SIMD[type: DType, size: Int]( ] ]() + var array_ptr = UnsafePointer.address_of(array) + @parameter for idx in range(output_size): alias val = mask[idx] @@ -1843,7 +1845,7 @@ struct SIMD[type: DType, size: Int]( "invalid index in the shuffle operation", ]() var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(array).address, idx.value + array_ptr.address, idx.value ) __mlir_op.`pop.store`(val, ptr) @@ -1863,14 +1865,14 @@ struct SIMD[type: DType, size: Int]( @always_inline("nodebug") fn _shuffle_list[ - output_size: Int, mask: StaticIntTuple[output_size] + output_size: Int, mask: StaticTuple[Int, output_size] ](self, other: Self) -> SIMD[type, output_size]: """Shuffles (also called blend) the values of the current vector with the `other` value using the specified mask (permutation). The mask values must be within `2 * len(self)`. Parameters: - output_size: The size of the output vector. + output_size: The output SIMD size. mask: The permutation to use in the shuffle. Args: @@ -1889,7 +1891,7 @@ struct SIMD[type: DType, size: Int]( ]() return __mlir_op.`pop.simd.shuffle`[ - mask = mask.data.array, + mask = mask.array, _type = __mlir_type[ `!pop.simd<`, output_size.value, `, `, type.value, `>` ], @@ -1908,7 +1910,7 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self)[permutation[i]]`. """ - return self._shuffle_list[*mask](self) + return self._shuffle_variadic[*mask](self) @always_inline("nodebug") fn shuffle[*mask: Int](self, other: Self) -> Self: @@ -1926,10 +1928,10 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self + other)[permutation[i]]`. """ - return self._shuffle_list[*mask](other) + return self._shuffle_variadic[*mask](other) @always_inline("nodebug") - fn shuffle[mask: StaticIntTuple[size]](self) -> Self: + fn shuffle[mask: StaticIntTuple[size, **_]](self) -> Self: """Shuffles (also called blend) the values of the current vector with the `other` value using the specified mask (permutation). The mask values must be within `2 * len(self)`. @@ -1941,10 +1943,10 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self)[permutation[i]]`. """ - return self._shuffle_list[size, mask](self) + return self._shuffle_list[size, mask._as_index_tuple()](self) @always_inline("nodebug") - fn shuffle[mask: StaticIntTuple[size]](self, other: Self) -> Self: + fn shuffle[mask: StaticIntTuple[size, **_]](self, other: Self) -> Self: """Shuffles (also called blend) the values of the current vector with the `other` value using the specified mask (permutation). The mask values must be within `2 * len(self)`. @@ -1959,7 +1961,7 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self + other)[permutation[i]]`. """ - return self._shuffle_list[size, mask](other) + return self._shuffle_list[size, mask._as_index_tuple()](other) # Not an overload of shuffle because there is ambiguity # with fn shuffle[*mask: Int](self, other: Self) -> Self: @@ -2139,7 +2141,7 @@ struct SIMD[type: DType, size: Int]( ](self, value, Int64(offset)) @always_inline("nodebug") - fn join(self, other: Self) -> SIMD[type, 2 * size]: + fn join(self, other: Self) -> SIMD[type, 2 * size] as result: """Concatenates the two vectors together. Args: @@ -2151,8 +2153,8 @@ struct SIMD[type: DType, size: Int]( @always_inline @parameter - fn build_indices() -> StaticIntTuple[2 * size]: - var indices = StaticIntTuple[2 * size]() + fn build_indices() -> StaticTuple[Int, 2 * size]: + var indices = StaticTuple[Int, 2 * size](0) @parameter for i in range(2 * size): diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 45b7253bb3..7da51fcc4e 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -2217,7 +2217,7 @@ fn factorial(n: Int) -> Int: Returns: The factorial of the input. Results are undefined for negative inputs. """ - alias table = StaticIntTuple[21]( + alias table = StaticTuple[Int, 21]( 1, 1, 2, diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 8dfee900ce..ef23f6afb1 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -20,6 +20,8 @@ from utils import StaticIntTuple ``` """ +from sys import bitwidthof +from builtin.dtype import _int_type_of_width from builtin.io import _get_dtype_printf_format, _snprintf from collections.string import _calc_initial_buffer_size @@ -53,11 +55,8 @@ fn _reduce_and_fn(a: Bool, b: Bool) -> Bool: @always_inline fn _int_tuple_binary_apply[ - size: Int, - binary_fn: fn (Int, Int) -> Int, -](a: StaticTuple[Int, size], b: StaticTuple[Int, size]) -> StaticTuple[ - Int, size -]: + binary_fn: fn[type: DType] (Scalar[type], Scalar[type]) -> Scalar[type], +](a: StaticIntTuple, b: __type_of(a)) -> __type_of(a): """Applies a given element binary function to each pair of corresponding elements in two tuples. @@ -66,10 +65,6 @@ fn _int_tuple_binary_apply[ var b: StaticTuple[Int, size] var c = _int_tuple_binary_apply[size, Int.add](a, b) - Parameters: - size: Static size of the operand and result tuples. - binary_fn: Binary function to apply to tuple elements. - Args: a: Tuple containing lhs operands of the elementwise binary function. b: Tuple containing rhs operands of the elementwise binary function. @@ -78,24 +73,21 @@ fn _int_tuple_binary_apply[ Tuple containing the result. """ - var c = StaticTuple[Int, size]() + var c = __type_of(a)() @parameter - for i in range(size): + for i in range(a.size): var a_elem = a.__getitem__[i]() var b_elem = b.__getitem__[i]() - c.__setitem__[i](binary_fn(a_elem, b_elem)) + c.__setitem__[i](binary_fn[a._int_dtype](a_elem, b_elem)) return c @always_inline fn _int_tuple_compare[ - size: Int, - comp_fn: fn (Int, Int) -> Bool, -](a: StaticTuple[Int, size], b: StaticTuple[Int, size]) -> StaticTuple[ - Bool, size -]: + comp_fn: fn[type: DType] (Scalar[type], Scalar[type]) -> Bool, +](a: StaticIntTuple, b: __type_of(a)) -> StaticTuple[Bool, a.size]: """Applies a given element compare function to each pair of corresponding elements in two tuples and produces a tuple of Bools containing result. @@ -104,10 +96,6 @@ fn _int_tuple_compare[ var b: StaticTuple[Int, size] var c = _int_tuple_compare[size, Int.less_than](a, b) - Parameters: - size: Static size of the operand and result tuples. - comp_fn: Compare function to apply to tuple elements. - Args: a: Tuple containing lhs operands of the elementwise compare function. b: Tuple containing rhs operands of the elementwise compare function. @@ -116,22 +104,21 @@ fn _int_tuple_compare[ Tuple containing the result. """ - var c = StaticTuple[Bool, size]() + var c = StaticTuple[Bool, a.size]() @parameter - for i in range(size): - var a_elem: Int = a.__getitem__[i]() - var b_elem: Int = b.__getitem__[i]() - c.__setitem__[i](comp_fn(a_elem, b_elem)) + for i in range(a.size): + var a_elem = a.__getitem__[i]() + var b_elem = b.__getitem__[i]() + c.__setitem__[i](comp_fn[a._int_dtype](a_elem, b_elem)) return c @always_inline fn _bool_tuple_reduce[ - size: Int, reduce_fn: fn (Bool, Bool) -> Bool, -](a: StaticTuple[Bool, size], init: Bool) -> Bool: +](a: StaticTuple[Bool, _], init: Bool) -> Bool: """Reduces the tuple argument with the given reduce function and initial value. @@ -140,7 +127,6 @@ fn _bool_tuple_reduce[ var c = _bool_tuple_reduce[size, _reduce_and_fn](a, True) Parameters: - size: Static size of the operand and result tuples. reduce_fn: Reduce function to accumulate tuple elements. Args: @@ -154,7 +140,7 @@ fn _bool_tuple_reduce[ var c: Bool = init @parameter - for i in range(size): + for i in range(a.size): c = reduce_fn(c, a.__getitem__[i]()) return c @@ -167,7 +153,7 @@ fn _bool_tuple_reduce[ @value @register_passable("trivial") -struct StaticIntTuple[size: Int]( +struct StaticIntTuple[size: Int, *, integer_bitwidth: Int = bitwidthof[Int]()]( Sized, Stringable, Formattable, @@ -177,9 +163,16 @@ struct StaticIntTuple[size: Int]( Parameters: size: The size of the tuple. + integer_bitwidth: The bitwidth of the underlying integer element type. """ - var data: StaticTuple[Int, size] + alias _int_dtype = _int_type_of_width[integer_bitwidth]() + """The underlying dtype of the integer element value.""" + + alias _int_type = Scalar[Self._int_dtype] + """The underlying storage of the integer element value.""" + + var data: StaticTuple[Self._int_type, size] """The underlying storage of the tuple value.""" @always_inline @@ -304,8 +297,10 @@ struct StaticIntTuple[size: Int]( """ self.data = __mlir_op.`pop.array.repeat`[ - _type = __mlir_type[`!pop.array<`, size.value, `, `, Int, `>`] - ](elem) + _type = __mlir_type[ + `!pop.array<`, size.value, `, `, Self._int_type, `>` + ] + ](Self._int_type(elem)) fn __init__(inout self, *, other: Self): """Copy constructor. @@ -313,7 +308,7 @@ struct StaticIntTuple[size: Int]( Args: other: The other tuple to copy from. """ - self.data = StaticTuple[Int, size](other=other.data) + self.data = other.data @always_inline fn __init__(inout self, values: VariadicList[Int]): @@ -322,8 +317,21 @@ struct StaticIntTuple[size: Int]( Args: values: The list of values. """ - constrained[size > 0]() - self.data = values + + var num_elements = len(values) + + debug_assert( + size == num_elements, + "[StaticIntTuple] mismatch in the number of elements", + ) + + var tup = Self() + + @parameter + for idx in range(size): + tup[idx] = values[idx] + + self = tup @always_inline("nodebug") fn __len__(self) -> Int: @@ -334,6 +342,18 @@ struct StaticIntTuple[size: Int]( """ return size + @always_inline + fn __getitem__[idx: Int](self) -> Int: + """Gets an element from the tuple by index. + + Parameters: + idx: The element index. + + Returns: + The tuple element value. + """ + return int(self.data.__getitem__[idx]()) + @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: """Gets an element from the tuple by index. @@ -344,7 +364,7 @@ struct StaticIntTuple[size: Int]( Returns: The tuple element value. """ - return self.data[idx] + return int(self.data[idx]) @always_inline("nodebug") fn __setitem__[index: Int](inout self, val: Int): @@ -358,6 +378,18 @@ struct StaticIntTuple[size: Int]( """ self.data.__setitem__[index](val) + @always_inline("nodebug") + fn __setitem__[index: Int](inout self, val: Self._int_type): + """Sets an element in the tuple at the given static index. + + Parameters: + index: The element index. + + Args: + val: The value to store. + """ + self.data.__setitem__[index](val) + @always_inline("nodebug") fn __setitem__(inout self, idx: Int, val: Int): """Sets an element in the tuple at the given index. @@ -375,7 +407,12 @@ struct StaticIntTuple[size: Int]( Returns: The corresponding StaticTuple object. """ - return self.data + var res = StaticTuple[Int, size]() + + @parameter + for i in range(size): + res[i] = int(self.__getitem__[i]()) + return res @always_inline fn flattened_length(self) -> Int: @@ -393,7 +430,7 @@ struct StaticIntTuple[size: Int]( return length @always_inline - fn __add__(self, rhs: StaticIntTuple[size]) -> StaticIntTuple[size]: + fn __add__(self, rhs: Self) -> Self: """Performs element-wise integer add. Args: @@ -404,15 +441,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Int: + fn apply_fn[ + type: DType + ](a: Scalar[type], b: Scalar[type]) -> Scalar[type]: return a + b - return Self { - data: _int_tuple_binary_apply[size, apply_fn](self.data, rhs.data) - } + return _int_tuple_binary_apply[apply_fn](self, rhs) @always_inline - fn __sub__(self, rhs: StaticIntTuple[size]) -> StaticIntTuple[size]: + fn __sub__(self, rhs: Self) -> Self: """Performs element-wise integer subtract. Args: @@ -423,15 +460,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Int: + fn apply_fn[ + type: DType + ](a: Scalar[type], b: Scalar[type]) -> Scalar[type]: return a - b - return Self { - data: _int_tuple_binary_apply[size, apply_fn](self.data, rhs.data) - } + return _int_tuple_binary_apply[apply_fn](self, rhs) @always_inline - fn __mul__(self, rhs: StaticIntTuple[size]) -> StaticIntTuple[size]: + fn __mul__(self, rhs: Self) -> Self: """Performs element-wise integer multiply. Args: @@ -442,12 +479,12 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Int: + fn apply_fn[ + type: DType + ](a: Scalar[type], b: Scalar[type]) -> Scalar[type]: return a * b - return Self { - data: _int_tuple_binary_apply[size, apply_fn](self.data, rhs.data) - } + return _int_tuple_binary_apply[apply_fn](self, rhs) @always_inline fn __floordiv__(self, rhs: Self) -> Self: @@ -461,12 +498,12 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Int: + fn apply_fn[ + type: DType + ](a: Scalar[type], b: Scalar[type]) -> Scalar[type]: return a // b - return Self { - data: _int_tuple_binary_apply[size, apply_fn](self.data, rhs.data) - } + return _int_tuple_binary_apply[apply_fn](self, rhs) @always_inline fn __rfloordiv__(self, rhs: Self) -> Self: @@ -481,7 +518,7 @@ struct StaticIntTuple[size: Int]( return rhs // self @always_inline - fn remu(self, rhs: StaticIntTuple[size]) -> StaticIntTuple[size]: + fn remu(self, rhs: Self) -> Self: """Performs element-wise integer unsigned modulo. Args: @@ -492,15 +529,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Int: + fn apply_fn[ + type: DType + ](a: Scalar[type], b: Scalar[type]) -> Scalar[type]: return a % b - return Self { - data: _int_tuple_binary_apply[size, apply_fn](self.data, rhs.data) - } + return _int_tuple_binary_apply[apply_fn](self, rhs) @always_inline - fn __eq__(self, rhs: StaticIntTuple[size]) -> Bool: + fn __eq__(self, rhs: Self) -> Bool: """Compares this tuple to another tuple for equality. The tuples are equal if all corresponding elements are equal. @@ -513,15 +550,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Bool: + fn apply_fn[type: DType](a: Scalar[type], b: Scalar[type]) -> Bool: return a == b - return _bool_tuple_reduce[size, _reduce_and_fn]( - _int_tuple_compare[size, apply_fn](self.data, rhs.data), True + return _bool_tuple_reduce[_reduce_and_fn]( + _int_tuple_compare[apply_fn](self.data, rhs.data), True ) @always_inline - fn __ne__(self, rhs: StaticIntTuple[size]) -> Bool: + fn __ne__(self, rhs: Self) -> Bool: """Compares this tuple to another tuple for non-equality. The tuples are non-equal if at least one element of LHS isn't equal to @@ -536,7 +573,7 @@ struct StaticIntTuple[size: Int]( return not (self == rhs) @always_inline - fn __lt__(self, rhs: StaticIntTuple[size]) -> Bool: + fn __lt__(self, rhs: Self) -> Bool: """Compares this tuple to another tuple using LT comparison. A tuple is less-than another tuple if all corresponding elements of lhs @@ -552,15 +589,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Bool: + fn apply_fn[type: DType](a: Scalar[type], b: Scalar[type]) -> Bool: return a < b - return _bool_tuple_reduce[size, _reduce_and_fn]( - _int_tuple_compare[size, apply_fn](self.data, rhs.data), True + return _bool_tuple_reduce[_reduce_and_fn]( + _int_tuple_compare[apply_fn](self.data, rhs.data), True ) @always_inline - fn __le__(self, rhs: StaticIntTuple[size]) -> Bool: + fn __le__(self, rhs: Self) -> Bool: """Compares this tuple to another tuple using LE comparison. A tuple is less-or-equal than another tuple if all corresponding @@ -576,15 +613,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Bool: + fn apply_fn[type: DType](a: Scalar[type], b: Scalar[type]) -> Bool: return a <= b - return _bool_tuple_reduce[size, _reduce_and_fn]( - _int_tuple_compare[size, apply_fn](self.data, rhs.data), True + return _bool_tuple_reduce[_reduce_and_fn]( + _int_tuple_compare[apply_fn](self.data, rhs.data), True ) @always_inline - fn __gt__(self, rhs: StaticIntTuple[size]) -> Bool: + fn __gt__(self, rhs: Self) -> Bool: """Compares this tuple to another tuple using GT comparison. A tuple is greater-than than another tuple if all corresponding @@ -600,15 +637,15 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Bool: + fn apply_fn[type: DType](a: Scalar[type], b: Scalar[type]) -> Bool: return a > b - return _bool_tuple_reduce[size, _reduce_and_fn]( - _int_tuple_compare[size, apply_fn](self.data, rhs.data), True + return _bool_tuple_reduce[_reduce_and_fn]( + _int_tuple_compare[apply_fn](self.data, rhs.data), True ) @always_inline - fn __ge__(self, rhs: StaticIntTuple[size]) -> Bool: + fn __ge__(self, rhs: Self) -> Bool: """Compares this tuple to another tuple using GE comparison. A tuple is greater-or-equal than another tuple if all corresponding @@ -624,11 +661,11 @@ struct StaticIntTuple[size: Int]( """ @always_inline - fn apply_fn(a: Int, b: Int) -> Bool: + fn apply_fn[type: DType](a: Scalar[type], b: Scalar[type]) -> Bool: return a >= b - return _bool_tuple_reduce[size, _reduce_and_fn]( - _int_tuple_compare[size, apply_fn](self.data, rhs.data), True + return _bool_tuple_reduce[_reduce_and_fn]( + _int_tuple_compare[apply_fn](self.data, rhs.data), True ) @no_inline @@ -676,6 +713,15 @@ struct StaticIntTuple[size: Int]( # TODO: Optimize this to avoid the intermediate String allocation. writer.write(str(self)) + @always_inline + fn _as_index_tuple(self) -> StaticTuple[Int, size]: + var res = StaticTuple[Int, size]() + + @parameter + for i in range(size): + res[i] = self.__getitem__[i]() + return res + # ===----------------------------------------------------------------------===# # Factory functions for creating index. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 88299f6df1..f3d3894c23 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -101,7 +101,7 @@ fn _static_tuple_construction_checks[size: Int](): Parameters: size: The number of elements. """ - constrained[size > 0, "number of elements in `StaticTuple` must be > 0"]() + constrained[size >= 0, "number of elements in `StaticTuple` must be >= 0"]() @value diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index ab3a2c5f64..a97fd88a77 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -24,7 +24,7 @@ from testing import ( assert_not_equal, assert_true, ) -from utils import unroll, StaticIntTuple +from utils import unroll, StaticTuple, StaticIntTuple from utils.numerics import isfinite, isinf, isnan, nan @@ -825,22 +825,24 @@ def test_shuffle(): ) assert_equal( - vec._shuffle_list[7, 6, 5, 4, 3, 2, 1, 0, output_size = 2 * width](vec), + vec._shuffle_variadic[7, 6, 5, 4, 3, 2, 1, 0, output_size = 2 * width]( + vec + ), SIMD[dtype, 2 * width](103, 102, 101, 100, 103, 102, 101, 100), ) assert_equal( - vec.shuffle[StaticIntTuple[width](3, 2, 1, 0)](), + vec._shuffle_list[width, StaticTuple[Int, width](3, 2, 1, 0)](vec), SIMD[dtype, width](103, 102, 101, 100), ) assert_equal( - vec.shuffle[StaticIntTuple[width](0, 2, 4, 6)](vec), + vec._shuffle_list[width, StaticTuple[Int, width](0, 2, 4, 6)](vec), SIMD[dtype, width](100, 102, 100, 102), ) assert_equal( vec._shuffle_list[ - 2 * width, StaticIntTuple[2 * width](7, 6, 5, 4, 3, 2, 1, 0) + 2 * width, StaticTuple[Int, 2 * width](7, 6, 5, 4, 3, 2, 1, 0) ](vec), SIMD[dtype, 2 * width](103, 102, 101, 100, 103, 102, 101, 100), ) From 25ee0cc8ac9e574f981526482bf83e58e5244ccc Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 5 Oct 2024 11:55:02 -0700 Subject: [PATCH 1697/2019] [Stdlib] Add cast operation to StaticIntTuple MODULAR_ORIG_COMMIT_REV_ID: 054afbf83fb7194481333a14057b5af8a537b545 --- stdlib/src/utils/index.mojo | 40 +++++++++++++++++++++++++ stdlib/src/utils/static_tuple.mojo | 30 +++++++++---------- stdlib/test/utils/test_index.mojo | 48 ++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 stdlib/test/utils/test_index.mojo diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index ef23f6afb1..f179d5f697 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -713,6 +713,46 @@ struct StaticIntTuple[size: Int, *, integer_bitwidth: Int = bitwidthof[Int]()]( # TODO: Optimize this to avoid the intermediate String allocation. writer.write(str(self)) + @always_inline + fn cast[ + type: DType + ](self) -> StaticIntTuple[size, integer_bitwidth = bitwidthof[type]()]: + """Casts to the target DType. + + Parameters: + type: The type to cast towards. + + Returns: + The list casted to the target type. + """ + constrained[type.is_integral(), "the target type must be integral"]() + return self.cast[bitwidthof[type]()]() + + @always_inline + fn cast[ + integer_bitwidth: Int + ](self) -> StaticIntTuple[ + size, integer_bitwidth=integer_bitwidth + ] as result: + """Casts to the target DType. + + Parameters: + integer_bitwidth: The bitwidth to cast towards. + + Returns: + The list casted to the target type. + """ + var res = __type_of(result)() + + @parameter + for i in range(size): + res.data[i] = rebind[__type_of(result.data).element_type]( + rebind[Scalar[Self._int_dtype]]( + self.data.__getitem__[i]() + ).cast[result._int_dtype]() + ) + return res + @always_inline fn _as_index_tuple(self) -> StaticTuple[Int, size]: var res = StaticTuple[Int, size]() diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index f3d3894c23..d281c4bcb0 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -180,21 +180,6 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): ](self.array) return val - @always_inline("nodebug") - fn __setitem__[index: Int](inout self, val: Self.element_type): - """Stores a single value into the tuple at the specified index. - - Parameters: - index: The index into the tuple. - - Args: - val: The value to store. - """ - constrained[index < size]() - var tmp = self - _set_array_elem[index, size, Self.element_type](val, tmp.array) - self = tmp - @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Self.element_type: """Returns the value of the tuple at the given dynamic index. @@ -231,3 +216,18 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): ) UnsafePointer(ptr)[] = val self = tmp + + @always_inline("nodebug") + fn __setitem__[index: Int](inout self, val: Self.element_type): + """Stores a single value into the tuple at the specified index. + + Parameters: + index: The index into the tuple. + + Args: + val: The value to store. + """ + constrained[index < size]() + var tmp = self + _set_array_elem[index, size, Self.element_type](val, tmp.array) + self = tmp diff --git a/stdlib/test/utils/test_index.mojo b/stdlib/test/utils/test_index.mojo new file mode 100644 index 0000000000..24c3602779 --- /dev/null +++ b/stdlib/test/utils/test_index.mojo @@ -0,0 +1,48 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo -debug-level full %s + +from testing import assert_equal + +from utils import Index, StaticIntTuple + + +def test_basics(): + assert_equal(StaticIntTuple[2](1, 2), StaticIntTuple[2](1, 2)) + assert_equal(StaticIntTuple[3](1, 2, 3), StaticIntTuple[3](1, 2, 3)) + assert_equal(str(StaticIntTuple[3](1, 2, 3)), "(1, 2, 3)") + assert_equal(StaticIntTuple[3](1, 2, 3)[2], 3) + + +def test_cast(): + assert_equal( + str(StaticIntTuple[2](1, 2).cast[DType.int32]()), + "(1, 2)", + ) + assert_equal( + str(StaticIntTuple[2, integer_bitwidth=64](1, 2).cast[DType.int32]()), + "(1, 2)", + ) + assert_equal( + str(StaticIntTuple[2, integer_bitwidth=32](1, 2).cast[DType.int64]()), + "(1, 2)", + ) + assert_equal( + str(StaticIntTuple[2, integer_bitwidth=32](1, 2).cast[64]()), + "(1, 2)", + ) + + +def main(): + test_basics() + test_cast() From ce50b88cdcde0329f4567bbeaa3a12f418fc79ff Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 5 Oct 2024 12:58:28 -0700 Subject: [PATCH 1698/2019] [Stdlib] Add unsigned to StaticIntTuple MODULAR_ORIG_COMMIT_REV_ID: 426eb7abf55cb9d5dd6fd387da7fe2a7903a16c9 --- stdlib/src/utils/index.mojo | 66 ++++++++++++++++++++++--------- stdlib/test/utils/test_index.mojo | 18 +++++++-- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index f179d5f697..373b225c26 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -21,7 +21,7 @@ from utils import StaticIntTuple """ from sys import bitwidthof -from builtin.dtype import _int_type_of_width +from builtin.dtype import _int_type_of_width, _uint_type_of_width from builtin.io import _get_dtype_printf_format, _snprintf from collections.string import _calc_initial_buffer_size @@ -151,9 +151,26 @@ fn _bool_tuple_reduce[ # ===----------------------------------------------------------------------===# +fn _type_of_width[bitwidth: Int, unsigned: Bool]() -> DType: + @parameter + if unsigned: + return _uint_type_of_width[bitwidth]() + else: + return _int_type_of_width[bitwidth]() + + +fn _is_unsigned[type: DType]() -> Bool: + return type in (DType.uint8, DType.uint16, DType.uint32, DType.uint64) + + @value @register_passable("trivial") -struct StaticIntTuple[size: Int, *, integer_bitwidth: Int = bitwidthof[Int]()]( +struct StaticIntTuple[ + size: Int, + *, + element_bitwidth: Int = bitwidthof[Int](), + unsigned: Bool = False, +]( Sized, Stringable, Formattable, @@ -163,10 +180,11 @@ struct StaticIntTuple[size: Int, *, integer_bitwidth: Int = bitwidthof[Int]()]( Parameters: size: The size of the tuple. - integer_bitwidth: The bitwidth of the underlying integer element type. + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. """ - alias _int_dtype = _int_type_of_width[integer_bitwidth]() + alias _int_dtype = _type_of_width[element_bitwidth, unsigned]() """The underlying dtype of the integer element value.""" alias _int_type = Scalar[Self._int_dtype] @@ -716,7 +734,11 @@ struct StaticIntTuple[size: Int, *, integer_bitwidth: Int = bitwidthof[Int]()]( @always_inline fn cast[ type: DType - ](self) -> StaticIntTuple[size, integer_bitwidth = bitwidthof[type]()]: + ](self) -> StaticIntTuple[ + size, + element_bitwidth = bitwidthof[type](), + unsigned = _is_unsigned[type](), + ] as result: """Casts to the target DType. Parameters: @@ -726,32 +748,38 @@ struct StaticIntTuple[size: Int, *, integer_bitwidth: Int = bitwidthof[Int]()]( The list casted to the target type. """ constrained[type.is_integral(), "the target type must be integral"]() - return self.cast[bitwidthof[type]()]() + + var res = __type_of(result)() + + @parameter + for i in range(size): + res.data[i] = rebind[__type_of(result.data).element_type]( + rebind[Scalar[Self._int_dtype]]( + self.data.__getitem__[i]() + ).cast[result._int_dtype]() + ) + return res @always_inline fn cast[ - integer_bitwidth: Int + *, + element_bitwidth: Int = Self.element_bitwidth, + unsigned: Bool = Self.unsigned, ](self) -> StaticIntTuple[ - size, integer_bitwidth=integer_bitwidth + size, element_bitwidth=element_bitwidth, unsigned=unsigned ] as result: """Casts to the target DType. Parameters: - integer_bitwidth: The bitwidth to cast towards. + element_bitwidth: The bitwidth to cast towards. + unsigned: The signess of the list. Returns: The list casted to the target type. """ - var res = __type_of(result)() - - @parameter - for i in range(size): - res.data[i] = rebind[__type_of(result.data).element_type]( - rebind[Scalar[Self._int_dtype]]( - self.data.__getitem__[i]() - ).cast[result._int_dtype]() - ) - return res + return rebind[__type_of(result)]( + self.cast[_type_of_width[element_bitwidth, unsigned]()]() + ) @always_inline fn _as_index_tuple(self) -> StaticTuple[Int, size]: diff --git a/stdlib/test/utils/test_index.mojo b/stdlib/test/utils/test_index.mojo index 24c3602779..c52dc09d29 100644 --- a/stdlib/test/utils/test_index.mojo +++ b/stdlib/test/utils/test_index.mojo @@ -30,15 +30,27 @@ def test_cast(): "(1, 2)", ) assert_equal( - str(StaticIntTuple[2, integer_bitwidth=64](1, 2).cast[DType.int32]()), + str(StaticIntTuple[2, element_bitwidth=64](1, 2).cast[DType.int32]()), "(1, 2)", ) assert_equal( - str(StaticIntTuple[2, integer_bitwidth=32](1, 2).cast[DType.int64]()), + str(StaticIntTuple[2, element_bitwidth=32](1, 2).cast[DType.int64]()), "(1, 2)", ) assert_equal( - str(StaticIntTuple[2, integer_bitwidth=32](1, 2).cast[64]()), + str( + StaticIntTuple[2, element_bitwidth=32](1, -2).cast[ + element_bitwidth=64 + ]() + ), + "(1, -2)", + ) + assert_equal( + str( + StaticIntTuple[2, element_bitwidth=32](1, 2).cast[ + element_bitwidth=64, unsigned=True + ]() + ), "(1, 2)", ) From 7eff57821229651084d5bd672b79b12d9d56d46c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 5 Oct 2024 13:23:02 -0700 Subject: [PATCH 1699/2019] [mojo-lang][KGEN] Fix problem with `SIMD.__init__` at comptime. This fixes a bug reported by Sora on discord where we failed to compile this: ``` fn f() -> SIMD[DType.float32, 2]: return SIMD[DType.float32, 2](0, 1) fn main(): alias v = f() print(v) ``` The problem is that the relevant SIMD initializer is using a kgen.undef + insertelement, and the comptime interpreter got scared. MODULAR_ORIG_COMMIT_REV_ID: f2958fbaad086940c5ddcd9164f7df3b0881e79e --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 4bbc0a4ca5..55f2021e76 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -270,3 +270,5 @@ what we publish. doesn't extend the lifetimes of the values it references. - The VS Code extension now auto-updates its private copy of the MAX SDK. + +- The variadic initializer for `SIMD` now works in parameter expressions. From fc1b017e3f0f703be007a64daa68c64d83535640 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 5 Oct 2024 13:24:09 -0700 Subject: [PATCH 1700/2019] [Stdlib] Rename StaticIntTuple to IndexList, NFC With the recent changes we have setup StaticIntTuple to be a lot more generic than holding Ints, so we can rename it to the more appropriate IndexList. MODULAR_ORIG_COMMIT_REV_ID: 92db1a0299a5b9b371d87f9897bb856e9166a35e --- docs/changelog-released.md | 4 +- docs/changelog.md | 4 + .../algorithm/bench_elementwise.mojo | 4 +- stdlib/src/builtin/simd.mojo | 6 +- stdlib/src/collections/string.mojo | 2 +- stdlib/src/math/math.mojo | 2 +- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/index.mojo | 74 +++++++++---------- stdlib/test/builtin/test_issue_1505.mojo | 6 +- stdlib/test/builtin/test_print.mojo | 4 +- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/utils/test_index.mojo | 22 +++--- stdlib/test/utils/test_tuple.mojo | 28 +++---- stdlib/test/utils/test_unroll.mojo | 66 ++++++++--------- 14 files changed, 111 insertions(+), 115 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 17cefc77bb..848a884f82 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -1557,7 +1557,7 @@ Big themes for this release: - [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type: - Added [`SIMD.shuffle()`](/mojo/stdlib/builtin/simd/SIMD#shuffle) with - `StaticIntTuple` mask. + `IndexList` mask. ([PR #2315](https://github.com/modularml/mojo/pull/2315)) - [`SIMD.__bool__()`](/mojo/stdlib/builtin/simd/SIMD#__bool__) is constrained @@ -4393,7 +4393,7 @@ the previous "read to EOF" behavior when size is negative. - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape) and [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape) now have constructors that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list/List) - and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to + and [`IndexList`](/mojo/stdlib/utils/index_/IndexList) to initialize shapes. - The [`String`](/mojo/stdlib/collections/string/String) type now has the diff --git a/docs/changelog.md b/docs/changelog.md index 55f2021e76..3544a058ab 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -253,6 +253,10 @@ what we publish. debug_assert(x > 0, “expected x to be more than 0 but got: ”, x) ``` +- The `StaticIntTuple` datastructure in the `utils` package has been renamed to + `IndexList`. The datastructure now allows one to specify the index bitwidth of + the elements along with whether the underlying indices are signed or unsigned. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 799c9bf888..f7e753efbd 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -17,7 +17,7 @@ from algorithm import elementwise from benchmark import Bench, BenchConfig, Bencher, BenchId from buffer import Buffer -from utils.index import Index, StaticIntTuple +from utils.index import Index, IndexList # ===----------------------------------------------------------------------===# @@ -35,7 +35,7 @@ fn bench_elementwise[n: Int](inout b: Bencher) raises: fn call_fn() raises: @always_inline @parameter - fn func[simd_width: Int, rank: Int](idx: StaticIntTuple[rank]): + fn func[simd_width: Int, rank: Int](idx: IndexList[rank]): vector[idx[0]] = 42 elementwise[func, 1](Index(n)) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 02fee40887..d7a304ebfe 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -42,7 +42,7 @@ from builtin.format_int import _try_write_int from collections import InlineArray from memory import bitcast, UnsafePointer -from utils import StringSlice, StaticTuple, StaticIntTuple, Span +from utils import StringSlice, StaticTuple, IndexList, Span from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan @@ -1931,7 +1931,7 @@ struct SIMD[type: DType, size: Int]( return self._shuffle_variadic[*mask](other) @always_inline("nodebug") - fn shuffle[mask: StaticIntTuple[size, **_]](self) -> Self: + fn shuffle[mask: IndexList[size, **_]](self) -> Self: """Shuffles (also called blend) the values of the current vector with the `other` value using the specified mask (permutation). The mask values must be within `2 * len(self)`. @@ -1946,7 +1946,7 @@ struct SIMD[type: DType, size: Int]( return self._shuffle_list[size, mask._as_index_tuple()](self) @always_inline("nodebug") - fn shuffle[mask: StaticIntTuple[size, **_]](self, other: Self) -> Self: + fn shuffle[mask: IndexList[size, **_]](self, other: Self) -> Self: """Shuffles (also called blend) the values of the current vector with the `other` value using the specified mask (permutation). The mask values must be within `2 * len(self)`. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index f301a74ee9..dd1db6aba7 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -26,7 +26,7 @@ from python import PythonObject from utils import ( Span, - StaticIntTuple, + IndexList, StringRef, StringSlice, Variant, diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 7da51fcc4e..ee63e6fd81 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -40,7 +40,7 @@ from builtin.simd import _simd_apply, _modf from sys.info import _current_arch from utils import Span -from utils.index import StaticIntTuple +from utils.index import IndexList from utils.numerics import FPUtils, isnan, nan from utils.static_tuple import StaticTuple diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 4e74bd8022..df544d0711 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the utils package.""" -from .index import Index, StaticIntTuple, product +from .index import Index, IndexList, product from .inline_string import InlineString from .loop import unroll from .span import Span diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 373b225c26..ad9c943a0d 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -10,13 +10,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements `StaticIntTuple` which is commonly used to represent N-D +"""Implements `IndexList` which is commonly used to represent N-D indices. You can import these APIs from the `utils` package. For example: ```mojo -from utils import StaticIntTuple +from utils import IndexList ``` """ @@ -56,7 +56,7 @@ fn _reduce_and_fn(a: Bool, b: Bool) -> Bool: @always_inline fn _int_tuple_binary_apply[ binary_fn: fn[type: DType] (Scalar[type], Scalar[type]) -> Scalar[type], -](a: StaticIntTuple, b: __type_of(a)) -> __type_of(a): +](a: IndexList, b: __type_of(a)) -> __type_of(a): """Applies a given element binary function to each pair of corresponding elements in two tuples. @@ -87,7 +87,7 @@ fn _int_tuple_binary_apply[ @always_inline fn _int_tuple_compare[ comp_fn: fn[type: DType] (Scalar[type], Scalar[type]) -> Bool, -](a: StaticIntTuple, b: __type_of(a)) -> StaticTuple[Bool, a.size]: +](a: IndexList, b: __type_of(a)) -> StaticTuple[Bool, a.size]: """Applies a given element compare function to each pair of corresponding elements in two tuples and produces a tuple of Bools containing result. @@ -147,7 +147,7 @@ fn _bool_tuple_reduce[ # ===----------------------------------------------------------------------===# -# StaticIntTuple: +# IndexList: # ===----------------------------------------------------------------------===# @@ -165,7 +165,7 @@ fn _is_unsigned[type: DType]() -> Bool: @value @register_passable("trivial") -struct StaticIntTuple[ +struct IndexList[ size: Int, *, element_bitwidth: Int = bitwidthof[Int](), @@ -220,7 +220,7 @@ struct StaticIntTuple[ debug_assert( size == num_elements, - "[StaticIntTuple] mismatch in the number of elements", + "[IndexList] mismatch in the number of elements", ) var tup = Self() @@ -245,7 +245,7 @@ struct StaticIntTuple[ debug_assert( size == num_elements, - "[StaticIntTuple] mismatch in the number of elements", + "[IndexList] mismatch in the number of elements", ) var tup = Self() @@ -270,7 +270,7 @@ struct StaticIntTuple[ debug_assert( size == num_elements, - "[StaticIntTuple] mismatch in the number of elements", + "[IndexList] mismatch in the number of elements", ) var tup = Self() @@ -295,7 +295,7 @@ struct StaticIntTuple[ debug_assert( size == num_elements, - "[StaticIntTuple] mismatch in the number of elements", + "[IndexList] mismatch in the number of elements", ) var tup = Self() @@ -340,7 +340,7 @@ struct StaticIntTuple[ debug_assert( size == num_elements, - "[StaticIntTuple] mismatch in the number of elements", + "[IndexList] mismatch in the number of elements", ) var tup = Self() @@ -420,7 +420,7 @@ struct StaticIntTuple[ @always_inline("nodebug") fn as_tuple(self) -> StaticTuple[Int, size]: - """Converts this StaticIntTuple to StaticTuple. + """Converts this IndexList to StaticTuple. Returns: The corresponding StaticTuple object. @@ -734,7 +734,7 @@ struct StaticIntTuple[ @always_inline fn cast[ type: DType - ](self) -> StaticIntTuple[ + ](self) -> IndexList[ size, element_bitwidth = bitwidthof[type](), unsigned = _is_unsigned[type](), @@ -765,7 +765,7 @@ struct StaticIntTuple[ *, element_bitwidth: Int = Self.element_bitwidth, unsigned: Bool = Self.unsigned, - ](self) -> StaticIntTuple[ + ](self) -> IndexList[ size, element_bitwidth=element_bitwidth, unsigned=unsigned ] as result: """Casts to the target DType. @@ -795,7 +795,7 @@ struct StaticIntTuple[ # Factory functions for creating index. # ===----------------------------------------------------------------------===# @always_inline -fn Index[T0: Intable](x: T0) -> StaticIntTuple[1]: +fn Index[T0: Intable](x: T0) -> IndexList[1]: """Constructs a 1-D Index from the given value. Parameters: @@ -805,26 +805,26 @@ fn Index[T0: Intable](x: T0) -> StaticIntTuple[1]: x: The initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[1](int(x)) + return IndexList[1](int(x)) @always_inline -fn Index(x: UInt) -> StaticIntTuple[1]: +fn Index(x: UInt) -> IndexList[1]: """Constructs a 1-D Index from the given value. Args: x: The initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[1](x.value) + return IndexList[1](x.value) @always_inline -fn Index[T0: Intable, T1: Intable](x: T0, y: T1) -> StaticIntTuple[2]: +fn Index[T0: Intable, T1: Intable](x: T0, y: T1) -> IndexList[2]: """Constructs a 2-D Index from the given values. Parameters: @@ -836,13 +836,13 @@ fn Index[T0: Intable, T1: Intable](x: T0, y: T1) -> StaticIntTuple[2]: y: The 2nd initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[2](int(x), int(y)) + return IndexList[2](int(x), int(y)) @always_inline -fn Index(x: UInt, y: UInt) -> StaticIntTuple[2]: +fn Index(x: UInt, y: UInt) -> IndexList[2]: """Constructs a 2-D Index from the given values. Args: @@ -850,15 +850,15 @@ fn Index(x: UInt, y: UInt) -> StaticIntTuple[2]: y: The 2nd initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[2](x.value, y.value) + return IndexList[2](x.value, y.value) @always_inline fn Index[ T0: Intable, T1: Intable, T2: Intable -](x: T0, y: T1, z: T2) -> StaticIntTuple[3]: +](x: T0, y: T1, z: T2) -> IndexList[3]: """Constructs a 3-D Index from the given values. Parameters: @@ -872,15 +872,15 @@ fn Index[ z: The 3rd initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[3](int(x), int(y), int(z)) + return IndexList[3](int(x), int(y), int(z)) @always_inline fn Index[ T0: Intable, T1: Intable, T2: Intable, T3: Intable -](x: T0, y: T1, z: T2, w: T3) -> StaticIntTuple[4]: +](x: T0, y: T1, z: T2, w: T3) -> IndexList[4]: """Constructs a 4-D Index from the given values. Parameters: @@ -896,15 +896,15 @@ fn Index[ w: The 4th initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[4](int(x), int(y), int(z), int(w)) + return IndexList[4](int(x), int(y), int(z), int(w)) @always_inline fn Index[ T0: Intable, T1: Intable, T2: Intable, T3: Intable, T4: Intable -](x: T0, y: T1, z: T2, w: T3, v: T4) -> StaticIntTuple[5]: +](x: T0, y: T1, z: T2, w: T3, v: T4) -> IndexList[5]: """Constructs a 5-D Index from the given values. Parameters: @@ -922,9 +922,9 @@ fn Index[ v: The 5th initial value. Returns: - The constructed StaticIntTuple. + The constructed IndexList. """ - return StaticIntTuple[5](int(x), int(y), int(z), int(w), int(v)) + return IndexList[5](int(x), int(y), int(z), int(w), int(v)) # ===----------------------------------------------------------------------===# @@ -933,7 +933,7 @@ fn Index[ @always_inline -fn product[size: Int](tuple: StaticIntTuple[size], end_idx: Int) -> Int: +fn product[size: Int](tuple: IndexList[size], end_idx: Int) -> Int: """Computes a product of values in the tuple up to the given index. Parameters: @@ -952,7 +952,7 @@ fn product[size: Int](tuple: StaticIntTuple[size], end_idx: Int) -> Int: @always_inline fn product[ size: Int -](tuple: StaticIntTuple[size], start_idx: Int, end_idx: Int) -> Int: +](tuple: IndexList[size], start_idx: Int, end_idx: Int) -> Int: """Computes a product of values in the tuple in the given index range. Parameters: diff --git a/stdlib/test/builtin/test_issue_1505.mojo b/stdlib/test/builtin/test_issue_1505.mojo index e092856f2b..a4f9c4e14f 100644 --- a/stdlib/test/builtin/test_issue_1505.mojo +++ b/stdlib/test/builtin/test_issue_1505.mojo @@ -15,12 +15,12 @@ from random import random_ui64 -from utils import StaticIntTuple +from utils import IndexList from testing import assert_equal -fn gen_perm() -> StaticIntTuple[64]: - var result = StaticIntTuple[64]() +fn gen_perm() -> IndexList[64]: + var result = IndexList[64]() for i in range(64): result[i] = 64 - i - 1 diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index c0c9483014..11ca1936e0 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -19,7 +19,7 @@ from tempfile import NamedTemporaryFile from testing import assert_equal from builtin._location import __call_location, _SourceLocation -from utils import StaticIntTuple, StringRef +from utils import IndexList, StringRef @always_inline @@ -109,7 +109,7 @@ def test_print(): checker.check_line_starts_with("> 99.90000") print(">", float64, file=checker.stream()) checker.check_line_starts_with("> -129.29018") - print(">", StaticIntTuple[3](1, 2, 3), file=checker.stream()) + print(">", IndexList[3](1, 2, 3), file=checker.stream()) checker.check_line_starts_with("> (1, 2, 3)") print(">", 9223372036854775806, file=checker.stream()) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index a97fd88a77..8253e5186f 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -24,7 +24,7 @@ from testing import ( assert_not_equal, assert_true, ) -from utils import unroll, StaticTuple, StaticIntTuple +from utils import unroll, StaticTuple, IndexList from utils.numerics import isfinite, isinf, isnan, nan diff --git a/stdlib/test/utils/test_index.mojo b/stdlib/test/utils/test_index.mojo index c52dc09d29..0cecb3f12a 100644 --- a/stdlib/test/utils/test_index.mojo +++ b/stdlib/test/utils/test_index.mojo @@ -14,40 +14,38 @@ from testing import assert_equal -from utils import Index, StaticIntTuple +from utils import Index, IndexList def test_basics(): - assert_equal(StaticIntTuple[2](1, 2), StaticIntTuple[2](1, 2)) - assert_equal(StaticIntTuple[3](1, 2, 3), StaticIntTuple[3](1, 2, 3)) - assert_equal(str(StaticIntTuple[3](1, 2, 3)), "(1, 2, 3)") - assert_equal(StaticIntTuple[3](1, 2, 3)[2], 3) + assert_equal(IndexList[2](1, 2), IndexList[2](1, 2)) + assert_equal(IndexList[3](1, 2, 3), IndexList[3](1, 2, 3)) + assert_equal(str(IndexList[3](1, 2, 3)), "(1, 2, 3)") + assert_equal(IndexList[3](1, 2, 3)[2], 3) def test_cast(): assert_equal( - str(StaticIntTuple[2](1, 2).cast[DType.int32]()), + str(IndexList[2](1, 2).cast[DType.int32]()), "(1, 2)", ) assert_equal( - str(StaticIntTuple[2, element_bitwidth=64](1, 2).cast[DType.int32]()), + str(IndexList[2, element_bitwidth=64](1, 2).cast[DType.int32]()), "(1, 2)", ) assert_equal( - str(StaticIntTuple[2, element_bitwidth=32](1, 2).cast[DType.int64]()), + str(IndexList[2, element_bitwidth=32](1, 2).cast[DType.int64]()), "(1, 2)", ) assert_equal( str( - StaticIntTuple[2, element_bitwidth=32](1, -2).cast[ - element_bitwidth=64 - ]() + IndexList[2, element_bitwidth=32](1, -2).cast[element_bitwidth=64]() ), "(1, -2)", ) assert_equal( str( - StaticIntTuple[2, element_bitwidth=32](1, 2).cast[ + IndexList[2, element_bitwidth=32](1, 2).cast[ element_bitwidth=64, unsigned=True ]() ), diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index f754afd9c1..b38a4e0e75 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -15,7 +15,7 @@ from testing import assert_equal, assert_false, assert_true from memory import UnsafePointer -from utils import StaticIntTuple, StaticTuple +from utils import IndexList, StaticTuple from test_utils import ValueDestructorRecorder @@ -37,39 +37,33 @@ def test_static_tuple(): def test_static_int_tuple(): - assert_equal(str(StaticIntTuple[1](1)), "(1,)") + assert_equal(str(IndexList[1](1)), "(1,)") - assert_equal(str(StaticIntTuple[3](2)), "(2, 2, 2)") + assert_equal(str(IndexList[3](2)), "(2, 2, 2)") assert_equal( - str(StaticIntTuple[3](1, 2, 3) * StaticIntTuple[3](4, 5, 6)), + str(IndexList[3](1, 2, 3) * IndexList[3](4, 5, 6)), "(4, 10, 18)", ) assert_equal( - str(StaticIntTuple[4](1, 2, 3, 4) - StaticIntTuple[4](4, 5, 6, 7)), + str(IndexList[4](1, 2, 3, 4) - IndexList[4](4, 5, 6, 7)), "(-3, -3, -3, -3)", ) - assert_equal( - str(StaticIntTuple[2](10, 11) // StaticIntTuple[2](3, 4)), "(3, 2)" - ) + assert_equal(str(IndexList[2](10, 11) // IndexList[2](3, 4)), "(3, 2)") # Note: index comparison is intended for access bound checking, which is # usually all-element semantic, i.e. true if true for all positions. - assert_true( - StaticIntTuple[5](1, 2, 3, 4, 5) < StaticIntTuple[5](4, 5, 6, 7, 8) - ) + assert_true(IndexList[5](1, 2, 3, 4, 5) < IndexList[5](4, 5, 6, 7, 8)) - assert_false( - StaticIntTuple[4](3, 5, -1, -2) > StaticIntTuple[4](0, 0, 0, 0) - ) + assert_false(IndexList[4](3, 5, -1, -2) > IndexList[4](0, 0, 0, 0)) - assert_equal(len(StaticIntTuple[4](3, 5, -1, -2)), 4) + assert_equal(len(IndexList[4](3, 5, -1, -2)), 4) - assert_equal(str(StaticIntTuple[2]((1, 2))), "(1, 2)") + assert_equal(str(IndexList[2]((1, 2))), "(1, 2)") - assert_equal(str(StaticIntTuple[4]((1, 2, 3, 4))), "(1, 2, 3, 4)") + assert_equal(str(IndexList[4]((1, 2, 3, 4))), "(1, 2, 3, 4)") def test_tuple_literal(): diff --git a/stdlib/test/utils/test_unroll.mojo b/stdlib/test/utils/test_unroll.mojo index 2929becf98..de1b942b3a 100644 --- a/stdlib/test/utils/test_unroll.mojo +++ b/stdlib/test/utils/test_unroll.mojo @@ -14,7 +14,7 @@ from testing import assert_equal, assert_raises -from utils import StaticIntTuple, unroll +from utils import IndexList, unroll def test_unroll(): @@ -34,53 +34,53 @@ def test_unroll(): def test_unroll2(): - var static_tuples_seen = List[StaticIntTuple[2]]() + var static_tuples_seen = List[IndexList[2]]() @parameter fn func[idx0: Int, idx1: Int](): - static_tuples_seen.append(StaticIntTuple[2](idx0, idx1)) + static_tuples_seen.append(IndexList[2](idx0, idx1)) unroll[func, 2, 2]() - assert_equal(static_tuples_seen[0], StaticIntTuple[2](0, 0)) - assert_equal(static_tuples_seen[1], StaticIntTuple[2](0, 1)) - assert_equal(static_tuples_seen[2], StaticIntTuple[2](1, 0)) - assert_equal(static_tuples_seen[3], StaticIntTuple[2](1, 1)) + assert_equal(static_tuples_seen[0], IndexList[2](0, 0)) + assert_equal(static_tuples_seen[1], IndexList[2](0, 1)) + assert_equal(static_tuples_seen[2], IndexList[2](1, 0)) + assert_equal(static_tuples_seen[3], IndexList[2](1, 1)) assert_equal(len(static_tuples_seen), 4) def test_unroll3(): - var static_tuples_seen = List[StaticIntTuple[3]]() + var static_tuples_seen = List[IndexList[3]]() @parameter fn func[idx0: Int, idx1: Int, idx2: Int](): - static_tuples_seen.append(StaticIntTuple[3](idx0, idx1, idx2)) + static_tuples_seen.append(IndexList[3](idx0, idx1, idx2)) unroll[func, 4, 2, 3]() - assert_equal(static_tuples_seen[0], StaticIntTuple[3](0, 0, 0)) - assert_equal(static_tuples_seen[1], StaticIntTuple[3](0, 0, 1)) - assert_equal(static_tuples_seen[2], StaticIntTuple[3](0, 0, 2)) - assert_equal(static_tuples_seen[3], StaticIntTuple[3](0, 1, 0)) - assert_equal(static_tuples_seen[4], StaticIntTuple[3](0, 1, 1)) - assert_equal(static_tuples_seen[5], StaticIntTuple[3](0, 1, 2)) - assert_equal(static_tuples_seen[6], StaticIntTuple[3](1, 0, 0)) - assert_equal(static_tuples_seen[7], StaticIntTuple[3](1, 0, 1)) - assert_equal(static_tuples_seen[8], StaticIntTuple[3](1, 0, 2)) - assert_equal(static_tuples_seen[9], StaticIntTuple[3](1, 1, 0)) - assert_equal(static_tuples_seen[10], StaticIntTuple[3](1, 1, 1)) - assert_equal(static_tuples_seen[11], StaticIntTuple[3](1, 1, 2)) - assert_equal(static_tuples_seen[12], StaticIntTuple[3](2, 0, 0)) - assert_equal(static_tuples_seen[13], StaticIntTuple[3](2, 0, 1)) - assert_equal(static_tuples_seen[14], StaticIntTuple[3](2, 0, 2)) - assert_equal(static_tuples_seen[15], StaticIntTuple[3](2, 1, 0)) - assert_equal(static_tuples_seen[16], StaticIntTuple[3](2, 1, 1)) - assert_equal(static_tuples_seen[17], StaticIntTuple[3](2, 1, 2)) - assert_equal(static_tuples_seen[18], StaticIntTuple[3](3, 0, 0)) - assert_equal(static_tuples_seen[19], StaticIntTuple[3](3, 0, 1)) - assert_equal(static_tuples_seen[20], StaticIntTuple[3](3, 0, 2)) - assert_equal(static_tuples_seen[21], StaticIntTuple[3](3, 1, 0)) - assert_equal(static_tuples_seen[22], StaticIntTuple[3](3, 1, 1)) - assert_equal(static_tuples_seen[23], StaticIntTuple[3](3, 1, 2)) + assert_equal(static_tuples_seen[0], IndexList[3](0, 0, 0)) + assert_equal(static_tuples_seen[1], IndexList[3](0, 0, 1)) + assert_equal(static_tuples_seen[2], IndexList[3](0, 0, 2)) + assert_equal(static_tuples_seen[3], IndexList[3](0, 1, 0)) + assert_equal(static_tuples_seen[4], IndexList[3](0, 1, 1)) + assert_equal(static_tuples_seen[5], IndexList[3](0, 1, 2)) + assert_equal(static_tuples_seen[6], IndexList[3](1, 0, 0)) + assert_equal(static_tuples_seen[7], IndexList[3](1, 0, 1)) + assert_equal(static_tuples_seen[8], IndexList[3](1, 0, 2)) + assert_equal(static_tuples_seen[9], IndexList[3](1, 1, 0)) + assert_equal(static_tuples_seen[10], IndexList[3](1, 1, 1)) + assert_equal(static_tuples_seen[11], IndexList[3](1, 1, 2)) + assert_equal(static_tuples_seen[12], IndexList[3](2, 0, 0)) + assert_equal(static_tuples_seen[13], IndexList[3](2, 0, 1)) + assert_equal(static_tuples_seen[14], IndexList[3](2, 0, 2)) + assert_equal(static_tuples_seen[15], IndexList[3](2, 1, 0)) + assert_equal(static_tuples_seen[16], IndexList[3](2, 1, 1)) + assert_equal(static_tuples_seen[17], IndexList[3](2, 1, 2)) + assert_equal(static_tuples_seen[18], IndexList[3](3, 0, 0)) + assert_equal(static_tuples_seen[19], IndexList[3](3, 0, 1)) + assert_equal(static_tuples_seen[20], IndexList[3](3, 0, 2)) + assert_equal(static_tuples_seen[21], IndexList[3](3, 1, 0)) + assert_equal(static_tuples_seen[22], IndexList[3](3, 1, 1)) + assert_equal(static_tuples_seen[23], IndexList[3](3, 1, 2)) assert_equal(len(static_tuples_seen), 24) From c301508ba9adb0be76222959af7dd79dc56b3b84 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 5 Oct 2024 17:49:06 -0700 Subject: [PATCH 1701/2019] [mojo-lang][mojo-docs] Update the references syntax proposal. With the references design converging, this proposes a concrete rename of a bunch of argument conventions and other things. MODULAR_ORIG_COMMIT_REV_ID: 10ee785df885c398e4ba0b2ef8cd72192f88f948 --- proposals/lifetimes-keyword-renaming.md | 347 ++++++++++++++++-------- 1 file changed, 230 insertions(+), 117 deletions(-) diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md index e99bf2f77f..13de798ea5 100644 --- a/proposals/lifetimes-keyword-renaming.md +++ b/proposals/lifetimes-keyword-renaming.md @@ -1,147 +1,260 @@ -# Keyword naming and other topics to discuss - -This document is split off the [Provenance Tracking and Lifetimes in -Mojo](lifetimes-and-provenance.md) document to separate general syntactic -bikesheding issues from the core semantic issues in that proposal. - -Assuming that proposal goes through, I think we should consider a few changes to -the current Mojo keyword paint: - -## `borrowed` Keyword => `borrow` or `ref` - -`borrowed` as a keyword doesn’t really make sense in our new world. This is -currently used to indicate an argument that is a borrowed version of an existing -value. Given the introduction of lifetimes, these things can now appear in -arbitrary places (e.g. you can have an array of references) so it makes sense to -use a noun. - -Instead of reading an argument as “this function takes foo which is a borrowed -string”, we would read it as “foo is a borrow/ref of a string”. This makes it -consistent with local borrows on the stack: +# Resyntaxing argument conventions and References + +Date: October 2024 +Previous revision: [[June 2023](https://github.com/modularml/mojo/blob/f8d7cb8ba4c21ec3fbc87e21609b3fd56cab695f/proposals/lifetimes-keyword-renaming.md)] + +The design of the Mojo references subsystem is starting to come together. To +finalize the major points, it helps to come back and re-evaluate several early +decisions in Mojo to make the design more self consistent. This is a proposal +to gain consensus and alignment on naming topics, etc without diving deep into +the overall design (which is settling out). + +I anticipate that this is going to drive significant bikeshed'ing, so let's get +one thing out of the way first: we're not trying to make Mojo similar to Rust. +Our implementation model and the semantics are significantly different (and +better IMO) than Rust's references and lifetimes, so while it is a good idea to +be inspired by Rust and bring in good ideas that fit into Mojo based on first +principle analysis, the argument that "it is similar to Rust" is not seen as +itself a good reason to do something. + +Similarly, the general design of argument conventions in Mojo fits very well +into the ownership model and is scaling very effectively. Changing the +architecture of how things work isn't in scope for this proposal. + +## Rename `Reference` to `Pointer` + +The `Reference` type is an explicitly dereferenced "safe pointer" type that +offers a memory safe subset of the `UnsafePointer` API. This is an important +and useful type for some purposes, but is rarely used in practice, and it has +nothing to do with the behavior of `ref` elsewhere in Mojo. As such, we have +already renamed it to avoid confusion and clarify the model. + +## Renaming Mojo argument conventions + +Let's take a survey of the Mojo language today. As of Oct 2024, we have the +following argument conventions: + +1) `borrowed`: This is the implicit convention that provides an immutable + reference to another value with an inferred lifetime. +2) `inout`: This argument convention is a mutable reference to a value from a + caller with an inferred lifetime. +3) `ref [lifetime]`: this argument convention allows either a mutable or + immutable reference with a specified lifetime. It can be used with `ref [_]` + to infer an arbitrary lifetime. +4) `owned`: This argument convention provides a mutable reference to value that + the callee may need to destroy. I'd like to ignore this convention for the + purposes of this document to keep it focused. +5) `fn __init__(inout self)`: Mojo has a special hack that allows (and requires) + one to write the `self` argument on init functions as `inout`. This doesn't + make sense because the value isn't live-in, and indeed you will see poor + error messages with code like `var x: fn (inout Foo) -> None = Foo.__init__`. + +In addition, Mojo functions have the following return syntax: + +1) `fn f():` means either `-> None` or `-> object` for a `fn` or `def`. +2) `fn f() -> T:` returns an owned instance of T. +3) `fn f() -> ref [life] T:` is a returned reference of some specified lifetime. +4) `fn f() -> T as name:` allows a named return value. The intention of this syntax + was to follow Python pattern syntax, but it is weird and we can't allow other + pattern syntax here. + +I suggest we rename `borrowed` to `ref` (without square brackets), rename +`inout` to `mutref` and introduce a new `out` convention. Such a change will +give us: + +1) `ref`: This is the implicit convention that provides an immutable + reference to another value with an inferred lifetime. +2) `mutref`: This argument convention is a mutable reference to a value from a + callee with an inferred lifetime. +3) `ref [lifetime]`: **No change:** this works as it does today. +4) `owned`: **No change:** unrelated to this proposal, let's stay focused. +5) `fn __init__(out self)`: The `__init__` function takes `self` as + uninitialized memory and returns it initialized (when an error isn't thrown) + which means it's a named output. Let's call it `out`, which will allow one + to write `var x: fn (out Foo) -> None = Foo.__init__` as you'd expect. +6) `mutref [lifetime]`: If there is a good reason, we could allow `mutref` to + be used as a constraint that the lifetime is mutable. This is actually nice + for things like `mutref [_]` which would only infer a mutable lifetime (not + a parametricly mutable lifetime) but is mostly a consistency thing. + +Finally, let's align the result syntax: + +1) `fn f():` **No change**. +2) `fn f() -> T:` **No change**. +3) `fn f() -> ref [life] T:`: **No change**. +4) `fn f() -> (out name: T):` and maybe eventually `fn f(out name: T):`. The + former is a bit ugly because you'd want parens (or something else) to + disambiguate (for humans, not the compiler) the `:` as a terminator of the + function definition from the type specification. The later is very pretty, + would provide a path to "real" multiple return values, and would make the + model consistent with `__init__` but has implementation risk that we'd have + to explore. + +As a benefit of these changes, we'd get rid of the `borrowed` terminology, which +is loaded with Rust semantics that we don't carry, and get rid of the `inout` +keyword which is confusing and technically incorrect: the callee never does copy +in/out. + +### Alternatives considered + +I expect the biggest bikeshed to be around the `mutref` keyword, we might +consider instead: + +- `mut`: This is shorter, and the same length as `ref` but isn't clear to + readers that it is a reference (losing important readability and information) + and I don't see why it is useful for mutable references to be specifically + compact. +- `mut ref`: we could use a space, but this seems like unnecessary verbosity + and I don't see an advantage over `mutref`. + +## Rename "Lifetime" (the type, but also conceptually) + +Mojo uses parameters of types `Lifetime`, `MutableLifetime`, +`ImmutableLifetime` etc to represent and propagate lifetimes into functions. +For example, the standard library `StringSlice` type is declared like this: ```mojo -fn do_stuff[a: Lifetime](x: ref[a] String): ... - -fn usage(): - var str = String("hello") - ref r = str # Defines a local borrow of str. - - do_stuff(str) # Bind a reference to 'str' - do_stuff(r) # Pass on existing reference 'str' +struct StringSlice[ + is_mutable: Bool, //, + lifetime: Lifetime[is_mutable].type, +] + +# ASIDE: We have a path to be able to write this as: +# struct StringSlice[lifetime: Lifetime[_]]: +# but are missing a few things unrelated to this proposal. ``` -## `inout` Keyword => `ref` or `mutref` (??) - -The primary argument for the ‘`inout`’ keyword being named this was that Chris -wanted to get off the former ampersand syntax we used, and that (in an argument -position) there is copy-in and copy-out action that happens with computed -LValues. I think there is a principled argument to switch to something shorter -like `ref` which is used in other languages (e.g. C#), since they can exist in -other places that are not arguments, and those don’t get copy-in/copy-out -behavior. One challenge with the name `ref` is that it doesn't obviously -convey mutability, so we might need something weird like `mutref`. +This is saying that it takes a lifetime of parametric mutability. We chose +the word "Lifetime" for this concept following Rust's lead when the feature +was being prototyped. -Note that copy-in/copy-out syntax is useful in more than function call -arguments, so the `inout` keyword may return in the future. For example, we may -actually want to bind computed values to mutable references: +We have a problem though: the common notion of "the lifetime of a value" is +the region of code where a value may be accessed, and this lifetime may even +have holes: ```mojo -for inout x in some_array_with_getitem_and_setitem: - x += 1 + var s : String + # Not alive here. + s = "hello" + # alive here + use(s) + # destroyed here. + unrelated_stuff() + # ... + s = "goodbye" + # alive here + use(s) + # destroyed here ``` -This requires opening the reference with getitem, and writing it back with -setitem. We may also want to abstract over computed properties, e.g. form -something like `Array[inout Int]` where the elements of the array hold closers -over the get/set pairs. If we had this, this could decay to a classic mutable -reference at call sites providing the existing behavior we have. +Mojo's design is also completely different in Mojo and Rust: Mojo has early +"ASAP" destruction of values instead of being scope driven. Furthermore, +the use of references that might access a value affects where destructor +calls are inserted, changing the actual "lifetime" of the value. These are +all point to the word "lifetime" as being the wrong thing. -Given this possible direction and layering, I think we should go with something -like this: +So what is the right thing? These parameters indicate the "provenance" of a +reference, where the reference might be pointing. We also want a word that +is specific and ideally not overloaded to mean many other things in common +domains. I would recommend we use the word **`Origin`** because this is a +short and specific word that we can define in a uniquely-Mojo way. There is +some overlap in other domains (e.g. the origin of a coordinate space in +graphics) but I don't expect any overlap in the standard library. -1. `ref`: immutable reference, this is spelled “`borrowed`” today +To be specific, this would lead to the following changes: -2. `mutref`: mutable reference, this is spelled “`inout`” today. I’d love a -better keyword suggestion than `mutref`, perhaps just `mut`? +- `Lifetime` => `Origin` +- `MutableLifetime` => `MutableOrigin` +- `ImmutableLifetime` => `ImmutableOrigin` -3. `inout`: abstracted computed mutable reference with getter/setter. +etc. More importantly, this would affect the Mojo manual and how these +concepts are explained. -`inout` can decay to `mutref` and `ref` in an argument position with writeback, -and `mutref` is a subtype of `ref` generally. +### Alternatives considered -## `owned` Keyword => `var` +There are several other words we might choose, here are some thoughts on +them: -People on the forums have pointed out that the “`owned`” keyword in argument -lists is very analogous to the `var` keyword. It defines a new, whole, value -and it is mutable just like `var`. Switching to `var` eliminates a concept and -reduces the number of keywords we are introducing. +- `Memory`: too generic and not tied to provenance, the standard library + already has other things that work on "memory" generically. +- `Region`: this can work, but (to me at least) has connotations of nesting + which aren't appropriate. It is also a very generic word. +- `Target`: very generic. -## Allow `let` in argument lists ... or remove them entirely (!) +## Rename the "transfer operator" (`x^`) -If we replace the `owned` keyword with `var`, then we need to decide what to do -with `let`. There are two different paths with different tradeoffs that I see. +This is a minor thing, but the "transfer operator" could use a rebranding +in the documentation. First, unlike other operators (like `+`) it isn't +tied into a method (like `__add__`): perhaps renaming it to a "sigil" +instead of an "operator" would be warranted. -The easiest to explain and most contiguous would be to allow arguments to be -defined as `let` arguments, just like we define `var` arguments. This would -keep these two declarations symmetrical, and appease people that like to control -mutation tightly. +More importantly though, this operator *makes the specified value able to +be transferred/consumed*, it does not itself transfer the value. I'm not +sure what to call this. -The more extreme direction would be to remove `let` entirely. Some arguments -in favor of this approach: +## Implementation -1. It has been observed on the forum that it adds very little - it doesn't - provide additional performance benefits over `var`, it only prevents - "accidental mutation" of a value. -2. Languages like C++ default to mutability everywhere (very few people bother - marking local variables constant, e.g. with `const int x = foo()`. -3. The more important (and completely necessary) thing that Mojo needs to model - are immutable borrows. Removing `let` would reduce confusion about these two - immutable things. -4. Mojo also has `alias`, which most programmers see as a “different type of - constant” further increasing our chance of confusing people. -5. `let` declarations require additional compiler complexity to check them, Mojo - doesn’t currently support struct fields market `let` for example because the - initialization rules are annoying to check for. Once you have them, it - messes with default values in structs. +All of these changes can be done in a way that is effectively additive: we need +to take `mutref` as a new keyword, but otherwise we can accept the new and the +old syntax in the 24.6 release and then remove the old syntax in subsequent +releases. -In my opinion, I think we are likely to want to remove `let`’s, but we should -only do so after the whole lifetime system is up and working. This will give us -more information about how things feel in practice and whether they are worth -the complexity. +## Future directions -## More alternatives to consider +It is important to consider how this proposal intersects with likely future +work. The most relevant upcoming feature work will be to introduce "patterns" +to Mojo. Patterns are a [core part of Python](https://docs.python.org/3/reference/compound_stmts.html#patterns) +which we need for compatibility with the Python ecosystem, and are the basis +for `match` statements and other things. When we implement them, we will allow +`var` statements to contain irrefutable patterns, and we will introduce a new +`ref` and `mutref` pattern to bind a reference to a value instead of copying +it. -[@sa- -suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6104926) -the keyword `fix` instead of `let`. +This will allow many nice things for example it will eliminate the need for +`elt[]` when foreaching over a list: -[@mojodojodev suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6105688): +```mojo + # Copy the elements of the list into elt (required for Python compat), like + # "for (auto elt : list)" in C++. + for elt in list: + elt += "foo" + + # Bind a mutable reference to elements in the list, like + # `for (auto &elt : list)` in C++. + for mutref elt in list: + elt += "foo" + + # Bind an immutable reference to elements in the list, like + # `for (const auto &elt : list)` in C++. + for ref elt in list: + elt += "foo" +``` -`ref[a]` - immutable reference -`mut[a]` - mutable reference -`let[a]` - immutable owned -`var[a]` - mutable owned +Furthermore, we will allow patterns in `var` statements, so you'll be able to +declare local references on the stack: -Having three letters for all of the keywords will allow the user to understand -"this is related to ownership and mutability". The problem with the proposed -removing let is that code ported from Python to Mojo won't behave the same, -keeping let and var is advantageous in that it says this is a Mojo variable so -you can add all the weird Python dynamic behavior when the keyword is elided. +```mojo + # Copy an element out of the list, like "auto x = list[i]" in C++. + var a = list[i] -[@mzaks -suggests](https://github.com/modularml/mojo/discussions/338#discussioncomment-6134220) -using numbers to identify lifetimes, e.g.: + # Mutref to an element in the list, like "auto &x = list[i]" in C++. + var (mutref b) = list[i] + b += "foo" -```mojo -fn example['1_life](cond: Bool, - x: borrowed'1 String, - y: borrowed'1 String): - # Late initialized local borrow with explicit lifetime - borrowed'1 str_ref : String - - if cond: - str_ref = x - else: - str_ref = y - print(str_ref) + # Mutref to an element in the list, better syntax + mutref c = list[i] + print(len(c)) + + # Immutable ref to an element in the list, like + # "const auto &x = list[i]" in C++. + var (ref d) = list[i] + d += "foo" + + # Immutable ref to an element in the list, better syntax + ref e = list[i] + print(len(e)) ``` + +We believe that this will provide a nice and familiar model to a wide variety +of programmers with ergonomic syntax and full safety. We're excited for what +this means for Mojo. From 271d8787d67d691583e50ecf35d0640eca670459 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 6 Oct 2024 05:49:01 -0700 Subject: [PATCH 1702/2019] [******] Make the shape/stride in NDBuffer be unsigned MODULAR_ORIG_COMMIT_REV_ID: 87a77db8165a7c2091369e88450d8ba6af830fe2 --- stdlib/src/builtin/dtype.mojo | 16 ++++++++++++++-- stdlib/src/builtin/simd.mojo | 4 ++-- stdlib/src/utils/index.mojo | 33 ++++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index f9b121b3f2..68d045c3f4 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -691,7 +691,14 @@ fn _scientific_notation_digits[type: DType]() -> StringLiteral: # ===-------------------------------------------------------------------===# +@parameter +@always_inline fn _int_type_of_width[width: Int]() -> DType: + constrained[ + width == 8 or width == 16 or width == 32 or width == 64, + "width must be either 8, 16, 32, or 64", + ]() + @parameter if width == 8: return DType.int8 @@ -700,7 +707,6 @@ fn _int_type_of_width[width: Int]() -> DType: elif width == 32: return DType.int32 else: - constrained[width == 64]() return DType.int64 @@ -709,7 +715,14 @@ fn _int_type_of_width[width: Int]() -> DType: # ===-------------------------------------------------------------------===# +@parameter +@always_inline fn _uint_type_of_width[width: Int]() -> DType: + constrained[ + width == 8 or width == 16 or width == 32 or width == 64, + "width must be either 8, 16, 32, or 64", + ]() + @parameter if width == 8: return DType.uint8 @@ -718,7 +731,6 @@ fn _uint_type_of_width[width: Int]() -> DType: elif width == 32: return DType.uint32 else: - constrained[width == 64]() return DType.uint64 diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d7a304ebfe..104152895f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1943,7 +1943,7 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self)[permutation[i]]`. """ - return self._shuffle_list[size, mask._as_index_tuple()](self) + return self._shuffle_list[size, mask.as_tuple()](self) @always_inline("nodebug") fn shuffle[mask: IndexList[size, **_]](self, other: Self) -> Self: @@ -1961,7 +1961,7 @@ struct SIMD[type: DType, size: Int]( A new vector with the same length as the mask where the value at position `i` is `(self + other)[permutation[i]]`. """ - return self._shuffle_list[size, mask._as_index_tuple()](other) + return self._shuffle_list[size, mask.as_tuple()](other) # Not an overload of shuffle because there is ambiguity # with fn shuffle[*mask: Int](self, other: Self) -> Self: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index ad9c943a0d..e1d46054f1 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -432,6 +432,22 @@ struct IndexList[ res[i] = int(self.__getitem__[i]()) return res + @always_inline("nodebug") + fn canonicalize( + self, + ) -> IndexList[ + size, element_bitwidth = bitwidthof[Int](), unsigned=False + ] as result: + """Canonicalizes the IndexList. + + Returns: + Canonicalizes the object. + """ + return self.cast[ + element_bitwidth = result.element_bitwidth, + unsigned = result.unsigned, + ]() + @always_inline fn flattened_length(self) -> Int: """Returns the flattened length of the tuple. @@ -777,19 +793,18 @@ struct IndexList[ Returns: The list casted to the target type. """ + + @parameter + if ( + element_bitwidth == Self.element_bitwidth + and unsigned == Self.unsigned + ): + return rebind[__type_of(result)](self) + return rebind[__type_of(result)]( self.cast[_type_of_width[element_bitwidth, unsigned]()]() ) - @always_inline - fn _as_index_tuple(self) -> StaticTuple[Int, size]: - var res = StaticTuple[Int, size]() - - @parameter - for i in range(size): - res[i] = self.__getitem__[i]() - return res - # ===----------------------------------------------------------------------===# # Factory functions for creating index. From b1a27eee139617adbafc9a4707e6eab911b98b2a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 6 Oct 2024 12:36:17 -0700 Subject: [PATCH 1703/2019] [mojo-lang][mojo-docs] Enhance + change resyntax proposal This changes the proposal from mutref -> mut clarifies the discussion and adds information about adding first class types etc. MODULAR_ORIG_COMMIT_REV_ID: 5e8d8c78b4d57f61e69a95ba72847b49fce0b23d --- proposals/lifetimes-keyword-renaming.md | 233 +++++++++++++++++------- 1 file changed, 168 insertions(+), 65 deletions(-) diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md index 13de798ea5..82f05d1790 100644 --- a/proposals/lifetimes-keyword-renaming.md +++ b/proposals/lifetimes-keyword-renaming.md @@ -59,12 +59,12 @@ In addition, Mojo functions have the following return syntax: pattern syntax here. I suggest we rename `borrowed` to `ref` (without square brackets), rename -`inout` to `mutref` and introduce a new `out` convention. Such a change will +`inout` to `mut` and introduce a new `out` convention. Such a change will give us: 1) `ref`: This is the implicit convention that provides an immutable reference to another value with an inferred lifetime. -2) `mutref`: This argument convention is a mutable reference to a value from a +2) `mut`: This argument convention is a mutable reference to a value from a callee with an inferred lifetime. 3) `ref [lifetime]`: **No change:** this works as it does today. 4) `owned`: **No change:** unrelated to this proposal, let's stay focused. @@ -72,10 +72,11 @@ give us: uninitialized memory and returns it initialized (when an error isn't thrown) which means it's a named output. Let's call it `out`, which will allow one to write `var x: fn (out Foo) -> None = Foo.__init__` as you'd expect. -6) `mutref [lifetime]`: If there is a good reason, we could allow `mutref` to - be used as a constraint that the lifetime is mutable. This is actually nice - for things like `mutref [_]` which would only infer a mutable lifetime (not - a parametricly mutable lifetime) but is mostly a consistency thing. + +I don't see a reason to allow `mut [lifetime]`: the only use-case would be if +you'd want to explicitly declare lifetime as a parameter, but I'm not sure why +that is useful (vs inferring it). We can evaluate adding it if there is a +compelling reason to over time. Finally, let's align the result syntax: @@ -97,16 +98,19 @@ in/out. ### Alternatives considered -I expect the biggest bikeshed to be around the `mutref` keyword, we might -consider instead: +I expect the biggest bikeshed to be around the `mut` keyword. The benefits of +its name is that it is short. The major downside of it is that it doesn't +convey that it is a reference. + +We might consider instead: -- `mut`: This is shorter, and the same length as `ref` but isn't clear to - readers that it is a reference (losing important readability and information) - and I don't see why it is useful for mutable references to be specifically - compact. +- `mutref`: This clarifies that this is a reference. - `mut ref`: we could use a space, but this seems like unnecessary verbosity and I don't see an advantage over `mutref`. +I'd love to see discussion, new ideas, and additional positions and rationale: +I'll update the proposal with those changes. + ## Rename "Lifetime" (the type, but also conceptually) Mojo uses parameters of types `Lifetime`, `MutableLifetime`, @@ -133,18 +137,18 @@ the region of code where a value may be accessed, and this lifetime may even have holes: ```mojo - var s : String - # Not alive here. - s = "hello" - # alive here - use(s) - # destroyed here. - unrelated_stuff() - # ... - s = "goodbye" - # alive here - use(s) - # destroyed here + var s : String + # Not alive here. + s = "hello" + # alive here + use(s) + # destroyed here. + unrelated_stuff() + # ... + s = "goodbye" + # alive here + use(s) + # destroyed here ``` Mojo's design is also completely different in Mojo and Rust: Mojo has early @@ -180,38 +184,44 @@ them: - `Region`: this can work, but (to me at least) has connotations of nesting which aren't appropriate. It is also a very generic word. - `Target`: very generic. - -## Rename the "transfer operator" (`x^`) - -This is a minor thing, but the "transfer operator" could use a rebranding -in the documentation. First, unlike other operators (like `+`) it isn't -tied into a method (like `__add__`): perhaps renaming it to a "sigil" -instead of an "operator" would be warranted. - -More importantly though, this operator *makes the specified value able to -be transferred/consumed*, it does not itself transfer the value. I'm not -sure what to call this. +- `Provenance`: verbose and technical. ## Implementation All of these changes can be done in a way that is effectively additive: we need -to take `mutref` as a new keyword, but otherwise we can accept the new and the +to take `mut` as a new keyword, but otherwise we can accept the new and the old syntax in the 24.6 release and then remove the old syntax in subsequent releases. +Lifetimes have been in heavy development, so I don't think we need to "phase in" +the changes to `Lifetime` etc, but we can keep around compatibility aliases for +a release if helpful. + ## Future directions It is important to consider how this proposal intersects with likely future -work. The most relevant upcoming feature work will be to introduce "patterns" -to Mojo. Patterns are a [core part of Python](https://docs.python.org/3/reference/compound_stmts.html#patterns) -which we need for compatibility with the Python ecosystem, and are the basis -for `match` statements and other things. When we implement them, we will allow -`var` statements to contain irrefutable patterns, and we will introduce a new -`ref` and `mutref` pattern to bind a reference to a value instead of copying -it. +work. This section includes a few of them for framing and perspective, but with +the goal of locking in the details above before we tackle these. + +### Patterns + Local reference bindings + +The most relevant upcoming feature work will be to introduce "patterns" +to Mojo. Python supports both [Targets](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-target) +and [Patterns](https://docs.python.org/3/reference/compound_stmts.html#patterns) +(closely related) +which we need for compatibility with the Python ecosystem. These are the basis +for `match` statements, unpack assignment syntax `(a,b) = foo()` and other +things. + +Mojo currently has support for targets, but not patterns. When we implement +patterns, we will extend `var` and `for` statements to work with them and we +will introduce a new +`ref` pattern to bind a reference to a value instead of copying it. Because +there is always an initializer value, we can allow `ref` to always infer the +mutability of the initialized value like the `ref [_]` argument convention does. This will allow many nice things for example it will eliminate the need for -`elt[]` when foreaching over a list: +`elt[]` when for-each-ing over a list: ```mojo # Copy the elements of the list into elt (required for Python compat), like @@ -220,41 +230,134 @@ This will allow many nice things for example it will eliminate the need for elt += "foo" # Bind a mutable reference to elements in the list, like - # `for (auto &elt : list)` in C++. - for mutref elt in list: + # `for (auto &elt : mutlist)` in C++. + # This happens when `mutlist` yields mutable references. + for ref elt in mutlist: elt += "foo" # Bind an immutable reference to elements in the list, like - # `for (const auto &elt : list)` in C++. - for ref elt in list: - elt += "foo" + # `for (const auto &elt : immlist)` in C++ + # This happens when `mutlist` yields immutable references. + for ref elt in immlist: + use(elt.field) # no need to copy elt to access a subfield. + #elt += foo # This would be an error, can't mutate immutable reference. ``` Furthermore, we will allow patterns in `var` statements, so you'll be able to -declare local references on the stack: +declare local references on the stack. ```mojo - # Copy an element out of the list, like "auto x = list[i]" in C++. - var a = list[i] + # Copy an element out of the list, like "auto x = list[i]" in C++. + var a = list[i] + + # Unpack tuple elements with tuple patterns: + var (a1, a2) = list[i] + + # Bind a ref to an element in the list, like "auto &x = mutlist[i]" in C++. + var (ref b) = mutlist[i] + b += "foo" - # Mutref to an element in the list, like "auto &x = list[i]" in C++. - var (mutref b) = list[i] - b += "foo" + # I don't see a reason not to allow `ref` in "target" syntax, so let's do + # that too: + ref c = mutlist[i] + c += "foo" - # Mutref to an element in the list, better syntax - mutref c = list[i] - print(len(c)) + # Parametric mutability automatically infers immutable vs mutable reference + # from the initializer. + ref d = immlist[i] + print(len(d)) +``` + +This should fully dovetail with parametric mutability at the function signature +level as well, an advanced example: - # Immutable ref to an element in the list, like - # "const auto &x = list[i]" in C++. - var (ref d) = list[i] - d += "foo" +```mojo +struct Aggregate: + var field: String - # Immutable ref to an element in the list, better syntax - ref e = list[i] - print(len(e)) +fn complicated(ref [_] agg: Aggregate) -> ref [agg.field] String: + ref field = agg.field # automatically propagates mutability + print(field) + return field ``` +The nice thing about this is that it propagates parametric mutability, so the +result of a call to `complicated` will return a mutable reference if provided a +mutable reference, or return an immutable (or parametric!) reference if provided +that instead. + We believe that this will provide a nice and familiar model to a wide variety of programmers with ergonomic syntax and full safety. We're excited for what this means for Mojo. + +### Making `ref` a first class type + +Right now `ref` is not a first class type - it is an argument and result +specifier as part of argument conventions. After adding pattern support, we +should look to extend ref to conform to specific traits (e.g. `AnyType` and +`Movable` and `Copyable`), which would allow it to be used in collections, and +enable things like: + +```mojo +struct Dict[K: KeyElement, V: CollectionElement]: + ... + fn __getitem__(self, key: K) -> Optional[ref [...] Self.V]: + ... +``` + +Today this isn't possible because `ref` isn't a first class type, so you can't +return an optional reference, or have an array of ref's. The workaround for +today is to use `Pointer` which handles this with a level of abstraction, or +use `raises` in the specific case of `Dict.__getitem__`. + +### Consider renaming `owned` argument convention + +This dovetails into questions about what we should do with the `owned` keyword, +and whether we should rename it. + +One suggestion is to rename it to `var` because a `var` declaration is an owned +mutable value just like an `owned` argument. The major problem that I see with +this approach is that the argument is "like a var" to the *callee*, but the +important part of method signatures are how they communicate to the *caller* +(e.g. when shown in API docs). + +Because of this, I think the use of `var` keyword would be very confusing in +practice, e.g. consider the API docs for `List.append`: + +```mojo +struct List[...]: + ... + fn append(out self, var value: T): # doesn't send a "consuming" signal. + ... +``` + +Furthermore, I don't think it would convey the right intention in `__del__` or +`__moveinit__`: + +```mojo +struct YourType: + fn __del__(var self): + ... + fn __moveinit__(out self, var existing: Self): + ... +``` + +The problem here is that we need some keyword that conveys that the function +"consumes an owned value", which is is the important thing from the caller +perspective. + +Other potential names are something like `consuming` (which is what Swift uses) +or `consumes` or `takes`. I would love suggestions that take into consideration +the above problems. + +### Rename the "transfer operator" (`x^`) + +This is a minor thing, but the "transfer operator" could use a rebranding +in the documentation. First, unlike other operators (like `+`) it isn't +tied into a method (like `__add__`): perhaps renaming it to a "sigil" +instead of an "operator" would be warranted. + +More importantly though, this operator *makes the specified value able to +be transferred/consumed*, it does not itself transfer the value. It seems +highly tied into the discussion about what to do with `owned`, but I'm not +sure what to call this. Perhaps one will flow from the other. From a7fcb93bf3d506cd37ac4996234be304e343d8b4 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Sun, 6 Oct 2024 17:48:56 -0400 Subject: [PATCH 1704/2019] [External] [stdlib] Adopt AHasher to Hasher trait (#48547) [External] [stdlib] Adopt AHasher to Hasher trait Co-authored-by: Maxim Zaks Closes modularml/mojo#3604 MODULAR_ORIG_COMMIT_REV_ID: de7fbcb13dea3efd5bdf10323342210a67c84138 --- stdlib/benchmarks/hashlib/bench_hash.mojo | 2 +- stdlib/src/hashlib/_ahash.mojo | 98 ++++++++++++++++------- stdlib/test/hashlib/test_ahash.mojo | 62 +++++++++++--- stdlib/test/hashlib/test_hasher.mojo | 42 ++++++++++ 4 files changed, 162 insertions(+), 42 deletions(-) diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index 99dc115281..5f779ee1a2 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -21,7 +21,7 @@ from hashlib._ahash import ( AHasher, hash as ahash, _folded_multiply, - read_small, + _read_small, U256, U128, MULTIPLE, diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index f0fe12140b..a5d3839022 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -14,6 +14,7 @@ from bit import byte_swap from bit import rotate_bits_left from memory import UnsafePointer +from ._hasher import _Hasher, _HashableWithHasher alias U256 = SIMD[DType.uint64, 4] alias U128 = SIMD[DType.uint64, 2] @@ -39,7 +40,7 @@ fn _folded_multiply(lhs: UInt64, rhs: UInt64) -> UInt64: @always_inline -fn read_small(data: UnsafePointer[UInt8], length: Int) -> U128: +fn _read_small(data: UnsafePointer[UInt8], length: Int) -> U128: """Produce a `SIMD[DType.uint64, 2]` value from data which is smaller than or equal to `8` bytes. Args: @@ -71,18 +72,14 @@ fn read_small(data: UnsafePointer[UInt8], length: Int) -> U128: return U128(0, 0) -struct AHasher: +struct AHasher[key: U256](_Hasher): var buffer: UInt64 var pad: UInt64 var extra_keys: U128 - fn __init__(inout self, key: U256): - """Initialize the hasher with a key. - - Args: - key: Modifier for the computation of the final hash value. - """ - var pi_key = key ^ U256( + fn __init__(inout self): + """Initialize the hasher.""" + alias pi_key = key ^ U256( 0x243F_6A88_85A3_08D3, 0x1319_8A2E_0370_7344, 0xA409_3822_299F_31D0, @@ -93,29 +90,26 @@ struct AHasher: self.extra_keys = U128(pi_key[2], pi_key[3]) @always_inline - fn large_update(inout self, new_data: U128): + fn _update(inout self, new_data: UInt64): """Update the buffer value with new data. Args: new_data: Value used for update. """ - var xored = new_data ^ self.extra_keys - var combined = _folded_multiply(xored[0], xored[1]) - self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined) + self.buffer = _folded_multiply(new_data ^ self.buffer, MULTIPLE) @always_inline - fn finish(self) -> UInt64: - """Computes the hash value based on all the previously provided data. + fn _large_update(inout self, new_data: U128): + """Update the buffer value with new data. - Returns: - Final hash value. + Args: + new_data: Value used for update. """ - var rot = self.buffer & 63 - var folded = _folded_multiply(self.buffer, self.pad) - return (folded << rot) | (folded >> (64 - rot)) + var xored = new_data ^ self.extra_keys + var combined = _folded_multiply(xored[0], xored[1]) + self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined) - @always_inline - fn write(inout self, data: UnsafePointer[UInt8], length: Int): + fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): """Consume provided data to update the internal buffer. Args: @@ -128,26 +122,68 @@ struct AHasher: var tail = data.offset(length - 16).bitcast[ DType.uint64 ]().load[width=2]() - self.large_update(tail) + self._large_update(tail) var offset = 0 while length - offset > 16: var block = data.offset(offset).bitcast[ DType.uint64 ]().load[width=2]() - self.large_update(block) + self._large_update(block) offset += 16 else: var a = data.bitcast[DType.uint64]().load() var b = data.offset(length - 8).bitcast[DType.uint64]().load() - self.large_update(U128(a, b)) + self._large_update(U128(a, b)) + else: + var value = _read_small(data, length) + self._large_update(value) + + fn _update_with_simd(inout self, new_data: SIMD[_, _]): + """Update the buffer value with new data. + + Args: + new_data: Value used for update. + """ + var v64: SIMD[DType.uint64, new_data.size] + + @parameter + if new_data.type.is_floating_point(): + v64 = new_data._float_to_bits[DType.uint64]() else: - var value = read_small(data, length) - self.large_update(value) + v64 = new_data.cast[DType.uint64]() + + @parameter + if v64.size == 1: + self._update(v64[0]) + else: + + @parameter + for i in range(0, v64.size, 2): + self._large_update(U128(v64[i], v64[i + 1])) + + fn update[T: _HashableWithHasher](inout self, value: T): + """Update the buffer value with new hashable value. + + Args: + value: Value used for update. + """ + value.__hash__(self) + + @always_inline + fn finish(owned self) -> UInt64: + """Computes the hash value based on all the previously provided data. + + Returns: + Final hash value. + """ + var rot = self.buffer & 63 + var folded = _folded_multiply(self.buffer, self.pad) + return (folded << rot) | (folded >> (64 - rot)) fn hash[ key: U256 = U256(0, 0, 0, 0) -](bytes: UnsafePointer[UInt8], n: Int) -> UInt: +](bytes: UnsafePointer[UInt8], n: Int) -> UInt64: """Hash a byte array using an adopted AHash algorithm. References: @@ -175,6 +211,6 @@ fn hash[ hash collision statistical properties for common data structures. """ - var hasher = AHasher(key) - hasher.write(bytes, n) - return UInt(int(hasher.finish())) + var hasher = AHasher[key]() + hasher._update_with_bytes(bytes, n) + return hasher^.finish() diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index 49dcc79474..fad0dd07a1 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s from bit import pop_count -from hashlib._ahash import hash +from hashlib._ahash import hash, AHasher from hashlib.hash import hash as old_hash from testing import assert_equal, assert_not_equal, assert_true from memory import memset_zero, UnsafePointer @@ -584,11 +584,11 @@ fn gen_word_pairs[words: String = words_en]() -> List[String]: return result -def dif_bits(i1: UInt, i2: UInt) -> Int: - return pop_count(i1 ^ i2) +def dif_bits(i1: UInt64, i2: UInt64) -> Int: + return int(pop_count(i1 ^ i2)) -def assert_dif_hashes(hashes: List[UInt], upper_bound: Int): +def assert_dif_hashes(hashes: List[UInt64], upper_bound: Int): for i in range(len(hashes)): for j in range(i + 1, len(hashes)): var diff = dif_bits(hashes[i], hashes[j]) @@ -644,8 +644,8 @@ def test_avalanche(): # produce significatly different hash values var data = UnsafePointer[UInt8].alloc(256) memset_zero(data, 256) - var hashes0 = List[UInt]() - var hashes1 = List[UInt]() + var hashes0 = List[UInt64]() + var hashes1 = List[UInt64]() hashes0.append(hash(data, 256)) hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, 256)) @@ -674,8 +674,8 @@ def test_trailing_zeros(): # results in significantly different hash values var data = UnsafePointer[UInt8].alloc(8) data[0] = 23 - var hashes0 = List[UInt]() - var hashes1 = List[UInt]() + var hashes0 = List[UInt64]() + var hashes1 = List[UInt64]() for i in range(1, 9): hashes0.append(hash(data, i)) hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, i)) @@ -701,7 +701,7 @@ def assert_fill_factor[ var buckets = List[Int](0) * num_buckets for w in words: var h = hash(w[].unsafe_ptr(), w[].byte_length()) - buckets[h % num_buckets] += 1 + buckets[int(h) % num_buckets] += 1 var unfilled = 0 for v in buckets: if v[] == 0: @@ -754,7 +754,7 @@ def test_fill_factor(): assert_fill_factor["EL"](words, len(words), 0.63) assert_fill_factor["EL"](words, len(words) // 2, 0.86) assert_fill_factor["EL"](words, len(words) // 4, 0.98) - assert_fill_factor["EL"](words, len(words) // 12, 1.0) + assert_fill_factor["EL"](words, len(words) // 13, 1.0) assert_fill_factor_old_hash["EL"](words, len(words), 0.015) @@ -799,8 +799,50 @@ def test_fill_factor(): assert_fill_factor_old_hash["RU"](words, len(words), 0.015) +def test_hash_simd_values(): + fn hash(value: SIMD) -> UInt64: + var hasher = AHasher[SIMD[DType.uint64, 4](0)]() + hasher._update_with_simd(value) + return hasher^.finish() + + assert_equal(hash(SIMD[DType.float16, 1](1.5)), 18058966248987367737) + assert_equal(hash(SIMD[DType.float32, 1](1.5)), 13467270117196531127) + assert_equal(hash(SIMD[DType.float64, 1](1.5)), 719560574162820089) + assert_equal(hash(SIMD[DType.float16, 1](1)), 1206414632147291024) + assert_equal(hash(SIMD[DType.float32, 1](1)), 9557262614467209093) + assert_equal(hash(SIMD[DType.float64, 1](1)), 7961842588256067709) + + assert_equal(hash(SIMD[DType.int8, 1](1)), 4759877148789019546) + assert_equal(hash(SIMD[DType.int16, 1](1)), 4759877148789019546) + assert_equal(hash(SIMD[DType.int32, 1](1)), 4759877148789019546) + assert_equal(hash(SIMD[DType.int64, 1](1)), 4759877148789019546) + assert_equal(hash(SIMD[DType.bool, 1](True)), 4759877148789019546) + + assert_equal(hash(SIMD[DType.int8, 1](-1)), 7301741325190448010) + assert_equal(hash(SIMD[DType.int16, 1](-1)), 7301741325190448010) + assert_equal(hash(SIMD[DType.int32, 1](-1)), 7301741325190448010) + assert_equal(hash(SIMD[DType.int64, 1](-1)), 7301741325190448010) + + assert_equal(hash(SIMD[DType.int8, 1](0)), 16659764227661506736) + assert_equal(hash(SIMD[DType.int8, 2](0)), 1562284133626399299) + assert_equal(hash(SIMD[DType.int8, 4](0)), 17902233708981521127) + assert_equal(hash(SIMD[DType.int8, 8](0)), 632562262308536351) + assert_equal(hash(SIMD[DType.int8, 16](0)), 7298276873920245913) + assert_equal(hash(SIMD[DType.int8, 32](0)), 7079057015559465054) + assert_equal(hash(SIMD[DType.int8, 64](0)), 11911213625103275990) + + assert_equal(hash(SIMD[DType.int32, 1](0)), 16659764227661506736) + assert_equal(hash(SIMD[DType.int32, 2](0)), 1562284133626399299) + assert_equal(hash(SIMD[DType.int32, 4](0)), 17902233708981521127) + assert_equal(hash(SIMD[DType.int32, 8](0)), 632562262308536351) + assert_equal(hash(SIMD[DType.int32, 16](0)), 7298276873920245913) + assert_equal(hash(SIMD[DType.int32, 32](0)), 7079057015559465054) + assert_equal(hash(SIMD[DType.int32, 64](0)), 11911213625103275990) + + def main(): test_hash_byte_array() test_avalanche() test_trailing_zeros() test_fill_factor() + test_hash_simd_values() diff --git a/stdlib/test/hashlib/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo index bd83c2f4b0..2052aebbb7 100644 --- a/stdlib/test/hashlib/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -14,6 +14,7 @@ from hashlib._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher +from hashlib._ahash import AHasher from memory import UnsafePointer from testing import assert_equal @@ -102,6 +103,26 @@ struct ComplexHashableStructWithList(_HashableWithHasher): _ = self._value3 +@value +struct ComplexHashableStructWithListAndWideSIMD(_HashableWithHasher): + var _value1: SomeHashableStruct + var _value2: SomeHashableStruct + var _value3: List[UInt8] + var _value4: SIMD[DType.uint32, 4] + + fn __hash__[H: _Hasher](self, inout hasher: H): + hasher.update(self._value1) + hasher.update(self._value2) + # This is okay because self is passed as borrowed so the pointer will + # be valid until at least the end of the function + hasher._update_with_bytes( + data=self._value3.unsafe_ptr(), + length=len(self._value3), + ) + hasher._update_with_simd(self._value4) + _ = self._value3 + + def test_update_with_bytes(): var hasher = DummyHasher() var hashable = ComplexHashableStructWithList( @@ -111,9 +132,30 @@ def test_update_with_bytes(): assert_equal(hasher^.finish(), 58) +def test_with_ahasher(): + var hashable1 = ComplexHashableStructWithList( + SomeHashableStruct(42), SomeHashableStruct(10), List[UInt8](1, 2, 3) + ) + var hash_value = _hash_with_hasher[ + AHasher[SIMD[DType.uint64, 4](0, 0, 0, 0)] + ](hashable1) + assert_equal(hash_value, 12427888534629009331) + var hashable2 = ComplexHashableStructWithListAndWideSIMD( + SomeHashableStruct(42), + SomeHashableStruct(10), + List[UInt8](1, 2, 3), + SIMD[DType.uint32, 4](1, 2, 3, 4), + ) + hash_value = _hash_with_hasher[AHasher[SIMD[DType.uint64, 4](0, 0, 0, 0)]]( + hashable2 + ) + assert_equal(hash_value, 9463003097190363949) + + def main(): test_hasher() test_hash_with_hasher() test_complex_hasher() test_complexe_hash_with_hasher() test_update_with_bytes() + test_with_ahasher() From ee6ba13b948d1df80b4e4819fd613a9c05b8c56f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 7 Oct 2024 11:41:08 -0700 Subject: [PATCH 1705/2019] [mojo-lang] Various updates based on community feedback. This incorporates feedback from @Yinon Burgansky on discord that we should keep `ref x` to consistently mean "infer a lifetime" even in argument lists. This switches the "borrowed replacement" to `immref` which then pushes the "inout replacement" back to `mutref`. While here, also mention that we should consider `ImmOrigin` instead of `ImmutableOrigin`. MODULAR_ORIG_COMMIT_REV_ID: d6df73d15df8fb9f274d9ba43f6b189f2e4056e6 --- proposals/lifetimes-keyword-renaming.md | 49 +++++++++++++++---------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/proposals/lifetimes-keyword-renaming.md b/proposals/lifetimes-keyword-renaming.md index 82f05d1790..e39c39b32b 100644 --- a/proposals/lifetimes-keyword-renaming.md +++ b/proposals/lifetimes-keyword-renaming.md @@ -1,6 +1,7 @@ # Resyntaxing argument conventions and References Date: October 2024 + Previous revision: [[June 2023](https://github.com/modularml/mojo/blob/f8d7cb8ba4c21ec3fbc87e21609b3fd56cab695f/proposals/lifetimes-keyword-renaming.md)] The design of the Mojo references subsystem is starting to come together. To @@ -38,13 +39,14 @@ following argument conventions: reference to another value with an inferred lifetime. 2) `inout`: This argument convention is a mutable reference to a value from a caller with an inferred lifetime. -3) `ref [lifetime]`: this argument convention allows either a mutable or - immutable reference with a specified lifetime. It can be used with `ref [_]` - to infer an arbitrary lifetime. -4) `owned`: This argument convention provides a mutable reference to value that +3) `ref [lifetime]`: this argument convention allows a reference to something of + the specified lifetime, the lifetime specifies the mutability requirements. +4) `ref [_]`: this is a shorthand for binding a reference to an anonymous + lifetime with any mutability. +5) `owned`: This argument convention provides a mutable reference to value that the callee may need to destroy. I'd like to ignore this convention for the purposes of this document to keep it focused. -5) `fn __init__(inout self)`: Mojo has a special hack that allows (and requires) +6) `fn __init__(inout self)`: Mojo has a special hack that allows (and requires) one to write the `self` argument on init functions as `inout`. This doesn't make sense because the value isn't live-in, and indeed you will see poor error messages with code like `var x: fn (inout Foo) -> None = Foo.__init__`. @@ -58,22 +60,26 @@ In addition, Mojo functions have the following return syntax: was to follow Python pattern syntax, but it is weird and we can't allow other pattern syntax here. -I suggest we rename `borrowed` to `ref` (without square brackets), rename -`inout` to `mut` and introduce a new `out` convention. Such a change will +I suggest we rename `borrowed` to `immref` (without square brackets), rename +`inout` to `mutref` and introduce a new `out` convention. Such a change will give us: -1) `ref`: This is the implicit convention that provides an immutable - reference to another value with an inferred lifetime. -2) `mut`: This argument convention is a mutable reference to a value from a +1) `immref`: This is convention provides an immutable reference to another value + with an inferred lifetime. As with `borrowed` it is allowed, but will never + be written in practice. +2) `mutref`: This argument convention is a mutable reference to a value from a callee with an inferred lifetime. 3) `ref [lifetime]`: **No change:** this works as it does today. -4) `owned`: **No change:** unrelated to this proposal, let's stay focused. -5) `fn __init__(out self)`: The `__init__` function takes `self` as +4) `ref`: Bind to an arbitrary reference with inferred lifetime and mutability, + this is the same as `ref [_]`. +5) `owned`: **No change:** unrelated to this proposal, let's stay focused. +6) `fn __init__(out self)`: The `__init__` function takes `self` as uninitialized memory and returns it initialized (when an error isn't thrown) which means it's a named output. Let's call it `out`, which will allow one to write `var x: fn (out Foo) -> None = Foo.__init__` as you'd expect. -I don't see a reason to allow `mut [lifetime]`: the only use-case would be if +I don't see a reason to allow explicit lifetime specifications on `immref` and +`mutref`, e.g. `mutref [lifetime]`. The only use-case would be if you'd want to explicitly declare lifetime as a parameter, but I'm not sure why that is useful (vs inferring it). We can evaluate adding it if there is a compelling reason to over time. @@ -89,7 +95,7 @@ Finally, let's align the result syntax: function definition from the type specification. The later is very pretty, would provide a path to "real" multiple return values, and would make the model consistent with `__init__` but has implementation risk that we'd have - to explore. + to explore. In any case, this isn't really core to the rest of the proposal. As a benefit of these changes, we'd get rid of the `borrowed` terminology, which is loaded with Rust semantics that we don't carry, and get rid of the `inout` @@ -98,13 +104,14 @@ in/out. ### Alternatives considered -I expect the biggest bikeshed to be around the `mut` keyword. The benefits of -its name is that it is short. The major downside of it is that it doesn't -convey that it is a reference. +I expect the biggest bikeshed to be around the `mutref` keyword. The benefits +of its name is that it is clear that this is a reference, keeps in aligned with +`ref` in other parts of the language, and is short. We might consider instead: -- `mutref`: This clarifies that this is a reference. +- `mut`: This is shorter, but loses that this is a reference. Also the name + `imm` for the borrowed replacement would be a bit odd. - `mut ref`: we could use a space, but this seems like unnecessary verbosity and I don't see an advantage over `mutref`. @@ -186,10 +193,14 @@ them: - `Target`: very generic. - `Provenance`: verbose and technical. +Furthermore if we're taking "mut" and "imm" as the root word for references, we +should decide if we're enshrining that as a [term of art](https://www.swift.org/documentation/api-design-guidelines/#use-terminology-well) +in Mojo. If so, we should use names like `MutOrigin` and `ImmOrigin`. + ## Implementation All of these changes can be done in a way that is effectively additive: we need -to take `mut` as a new keyword, but otherwise we can accept the new and the +to take `mutref` as a new keyword, but otherwise we can accept the new and the old syntax in the 24.6 release and then remove the old syntax in subsequent releases. From 0b36842426db102240819cbcbba812d7e539ed74 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 7 Oct 2024 13:23:46 -0700 Subject: [PATCH 1706/2019] [stdlib] `debug_assert` on GPU show block and thread idx Makes it so a failed `debug_assert` in the context of a GPU ****** will stop execution and show an error message with the block and thread idx. This behaves the same way as `assert` when compiling with nvcc. The limitation is that the message currently only works with a single `StringLiteral`. MODULAR_ORIG_COMMIT_REV_ID: 8d934c0afb63f7e354f147644b70e4626af8217f --- stdlib/src/builtin/debug_assert.mojo | 109 +++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 6edd91d9a4..6a8f442191 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -20,6 +20,10 @@ from os import abort from sys import is_defined, triple_is_nvidia_cuda from sys._build import is_debug_build from sys.param_env import env_get_string +from sys.ffi import external_call, c_uint, c_size_t, c_char +from sys.info import sizeof +from memory import UnsafePointer + from builtin._location import __call_location, _SourceLocation @@ -48,6 +52,65 @@ fn _assert_enabled[assert_mode: StringLiteral, cpu_only: Bool]() -> Bool: return defined_mode == assert_mode +@always_inline +fn debug_assert[ + assert_mode: StringLiteral = "none", cpu_only: Bool = False +](cond: Bool, message: StringLiteral): + """Asserts that the condition is true. + + Parameters: + assert_mode: Determines when the assert is turned on. + cpu_only: If true, only run the assert on CPU. + + Args: + cond: The function to invoke to check if the assertion holds. + message: A StringLiteral to print on failure. + + + Pass in a condition and single StringLiteral to stop execution and + show an error on failure: + + ```mojo + x = 0 + debug_assert(x > 0, "x is not more than 0") + ``` + + By default it's a no-op, you can change the assertion level for example: + + ```sh + mojo -D ASSERT=all main.mojo + ``` + + Assertion modes: + + - none: turn off all assertions for performance at the cost of safety. + - warn: print any errors instead of aborting. + - safe: standard safety checks that are on even in release builds. + - all: turn on all assertions. + + You can set the `assert_mode` to `safe` so the assertion runs even in + release builds: + + ```mojo + debug_assert[assert_mode="safe"]( + x > 0, "expected x to be more than 0 but got: ", x + ) + ``` + + To ensure that you have no runtime penality from your assertion in release + builds, make sure there are no side effects in your message and condition. + + On GPU this will also show the the block and thread idx's on failure. + . + """ + + @parameter + if _assert_enabled[assert_mode, cpu_only](): + if cond: + return + _debug_assert_msg_literal(message, __call_location()) + + @always_inline fn debug_assert[ cond: fn () capturing [_] -> Bool, @@ -233,12 +296,16 @@ fn _debug_assert_msg( @parameter if triple_is_nvidia_cuda(): - # On GPUs, assert shouldn't allocate. - @parameter - if defined_mode == "warn": - print("Assert Warning") - else: - abort() + external_call["__assertfail", NoneType]( + "debug_assert message must be a single StringLiteral on GPU" + .unsafe_cstr_ptr(), + loc.file_name.unsafe_cstr_ptr(), + c_uint(loc.line), + # TODO(MSTDL-962) pass through the funciton name here + "kernel".unsafe_cstr_ptr(), + c_size_t(sizeof[Int8]()), + ) + else: message = String.format_sequence(messages) @@ -247,3 +314,33 @@ fn _debug_assert_msg( print(loc.prefix("Assert Warning: " + message)) else: abort(loc.prefix("Assert Error: " + message)) + + +@no_inline +fn _debug_assert_msg_literal(message: StringLiteral, loc: _SourceLocation): + """Aborts with (or prints) the given message and location. + + This function is intentionally marked as no_inline to reduce binary size. + + Note that it's important that this function doesn't get inlined; otherwise, + an indirect recursion of @always_inline functions is possible (e.g. because + abort's implementation could use debug_assert) + """ + + @parameter + if triple_is_nvidia_cuda(): + external_call["__assertfail", NoneType]( + message.unsafe_cstr_ptr(), + loc.file_name.unsafe_cstr_ptr(), + c_uint(loc.line), + # TODO(MSTDL-962) pass through the funciton name here + "kernel".unsafe_cstr_ptr(), + c_size_t(sizeof[Int8]()), + ) + else: + + @parameter + if defined_mode == "warn": + print(loc.prefix(str("Assert Warning: ") + message)) + else: + abort(loc.prefix(str("Assert Error: ") + message)) From ad4cf45d1124e1c702b54a7e8f5119dc367c030c Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 7 Oct 2024 17:31:37 -0600 Subject: [PATCH 1707/2019] [stdlib] Remove `exclusive` from `UnsafePointer` This member is not used for anything, and we intend to fold it into the lifetime parameter instead. MODULAR_ORIG_COMMIT_REV_ID: 61d62d5fe576a97e9b104c57b4b26bd02f7c98d9 --- stdlib/src/memory/unsafe_pointer.mojo | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index a1cecd775f..526387f1fb 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -52,7 +52,6 @@ fn _default_alignment[type: DType, width: Int = 1]() -> Int: struct UnsafePointer[ type: AnyType, address_space: AddressSpace = AddressSpace.GENERIC, - exclusive: Bool = False, alignment: Int = _default_alignment[type](), lifetime: Lifetime[True].type = MutableAnyLifetime, ]( @@ -69,7 +68,6 @@ struct UnsafePointer[ Parameters: type: The type the pointer points to. address_space: The address space associated with the UnsafePointer allocated memory. - exclusive: The underlying memory allocation of the pointer is known only to be accessible through this pointer. alignment: The minimum alignment of this pointer known statically. lifetime: The lifetime of the memory being addressed. """ @@ -144,7 +142,6 @@ struct UnsafePointer[ ) -> UnsafePointer[ type, address_space, - False, 1, # TODO: Propagate lifetime of the argument. ] as result: @@ -164,7 +161,7 @@ struct UnsafePointer[ @always_inline fn alloc( count: Int, - ) -> UnsafePointer[type, AddressSpace.GENERIC, exclusive, alignment]: + ) -> UnsafePointer[type, AddressSpace.GENERIC, alignment]: """Allocate an array with specified or default alignment. Args: @@ -195,7 +192,7 @@ struct UnsafePointer[ alias _ref_type = Pointer[type, lifetime, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( - UnsafePointer[type, address_space, False, alignment, lifetime]( + UnsafePointer[type, address_space, alignment, lifetime]( self ).address ) @@ -430,7 +427,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn as_noalias_ptr( self, - ) -> UnsafePointer[type, address_space, True, alignment, lifetime]: + ) -> UnsafePointer[type, address_space, alignment, lifetime]: """Cast the pointer to a new pointer that is known not to locally alias any other pointer. In other words, the pointer transitively does not alias any other memory value declared in the local function context. @@ -886,9 +883,7 @@ struct UnsafePointer[ address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, lifetime: Lifetime[True].type = Self.lifetime, - ](self) -> UnsafePointer[ - T, address_space, Self.exclusive, alignment, lifetime - ]: + ](self) -> UnsafePointer[T, address_space, alignment, lifetime]: """Bitcasts a UnsafePointer to a different type. Parameters: @@ -914,9 +909,7 @@ struct UnsafePointer[ address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, lifetime: Lifetime[True].type = Self.lifetime, - ](self) -> UnsafePointer[ - Scalar[T], address_space, Self.exclusive, alignment, lifetime - ]: + ](self) -> UnsafePointer[Scalar[T], address_space, alignment, lifetime]: """Bitcasts a UnsafePointer to a different type. Parameters: From 02c897731f93c54c919ce4dfe0cb16b605c15e27 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 7 Oct 2024 20:25:56 -0700 Subject: [PATCH 1708/2019] [Stdlib] Add env_get_bool to the sys package This adds an `env_get_bool` function to the sys package. This allows one to get a True/False param from the param environment variables. MODULAR_ORIG_COMMIT_REV_ID: ec3a3d883759b0b2ab3b4aa460630e49ab4ba6d1 --- stdlib/src/sys/__init__.mojo | 2 +- stdlib/src/sys/param_env.mojo | 45 ++++++++++++++++++++++ stdlib/test/sys/test_invalid_paramenv.mojo | 21 ++++++++++ stdlib/test/sys/test_paramenv.mojo | 10 ++++- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 stdlib/test/sys/test_invalid_paramenv.mojo diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index ff5d750a79..29a6aea26d 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -65,5 +65,5 @@ from .intrinsics import ( strided_load, strided_store, ) -from .param_env import env_get_int, env_get_string, is_defined +from .param_env import env_get_int, env_get_string, env_get_bool, is_defined from .terminate import exit diff --git a/stdlib/src/sys/param_env.mojo b/stdlib/src/sys/param_env.mojo index c9b43c01be..b484fb2f90 100644 --- a/stdlib/src/sys/param_env.mojo +++ b/stdlib/src/sys/param_env.mojo @@ -53,6 +53,51 @@ fn is_defined[name: StringLiteral]() -> Bool: return __mlir_attr[`#kgen.param.expr : i1`] +fn env_get_bool[name: StringLiteral]() -> Bool: + """Try to get an boolean-valued define. Compilation fails if the + name is not defined or the value is neither `True` or `False`. + + Parameters: + name: The name of the define. + + Returns: + An boolean parameter value. + """ + alias val = env_get_string[name]() + + @parameter + if val in ("True", "true"): + return True + + @parameter + if val in ("False", "false"): + return False + + constrained[ + False, "the boolean environment value is neither `True` nor `False`" + ]() + return False + + +fn env_get_bool[name: StringLiteral, default: Bool]() -> Bool: + """Try to get an bool-valued define. If the name is not defined, return + a default value instead. The boolean must be either `True` or `False`. + + Parameters: + name: The name of the define. + default: The default value to use. + + Returns: + An bool parameter value. + """ + + @parameter + if is_defined[name](): + return env_get_bool[name]() + else: + return default + + fn env_get_int[name: StringLiteral]() -> Int: """Try to get an integer-valued define. Compilation fails if the name is not defined. diff --git a/stdlib/test/sys/test_invalid_paramenv.mojo b/stdlib/test/sys/test_invalid_paramenv.mojo new file mode 100644 index 0000000000..005755b999 --- /dev/null +++ b/stdlib/test/sys/test_invalid_paramenv.mojo @@ -0,0 +1,21 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: has_not +# RUN: not %mojo -D my_false=blah %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL + +from sys import env_get_bool + + +# CHECK-FAIL: constraint failed: the boolean environment value is neither `True` nor `False` +fn main(): + _ = env_get_bool["my_false"]() diff --git a/stdlib/test/sys/test_paramenv.mojo b/stdlib/test/sys/test_paramenv.mojo index 840db7c3b9..abdbd3c266 100644 --- a/stdlib/test/sys/test_paramenv.mojo +++ b/stdlib/test/sys/test_paramenv.mojo @@ -10,9 +10,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D bar=99 -D baz=hello -D foo=11 %s +# RUN: %mojo -D bar=99 -D baz=hello -D foo=11 -D my_true=True -D my_false=false %s -from sys import env_get_int, env_get_string, is_defined +from sys import env_get_int, env_get_bool, env_get_string, is_defined from testing import assert_equal, assert_false, assert_true @@ -35,7 +35,13 @@ def test_env_get_int(): assert_equal(env_get_int["boo", 42](), 42) +def test_env_get_bool(): + assert_equal(env_get_bool["my_true"](), True) + assert_equal(env_get_bool["my_false"](), False) + + def main(): test_is_defined() test_get_string() test_env_get_int() + test_env_get_bool() From f81aff0155e65cb5f8ace39ac26bc3ff8b49f9aa Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 8 Oct 2024 18:19:29 +0900 Subject: [PATCH 1709/2019] [mojo-stdlib] Add `Scalar[dtype]` variant of `range` This PR takes advantage of the new for loop API to implement a `range` iterator over a generic `Scalar` value. This enables writing loops over truly lower bitwidth integer types. MODULAR_ORIG_COMMIT_REV_ID: aa98afd94ba592eb05eac9e4b9cdeeae1ddf0a6f --- stdlib/benchmarks/collections/bench_dict.mojo | 2 +- stdlib/src/builtin/range.mojo | 80 +++++++++++++++++++ stdlib/test/builtin/test_range.mojo | 18 +++++ 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index 8645b66d7a..91c18e9aeb 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -81,7 +81,7 @@ fn bench_dict_lookup[size: Int](inout b: Bencher) raises: @parameter if size < 100: for _ in range(closest_divisor): - for key in range(100 // closest_divisor): + for key in range(int(100 // closest_divisor)): var res = items[key] keep(res) else: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 9427bf6986..d3d5dc89f1 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -454,3 +454,83 @@ fn range(start: UInt, end: UInt, step: UInt = 1) -> _UIntStridedRange: The constructed range. """ return _UIntStridedRange(start, end, step) + + +# ===----------------------------------------------------------------------=== # +# Range Scalar +# ===----------------------------------------------------------------------=== # + + +@value +@register_passable("trivial") +struct _StridedScalarRangeIterator[dtype: DType]: + var start: Scalar[dtype] + var end: Scalar[dtype] + var step: Scalar[dtype] + + @always_inline + fn __hasmore__(self) -> Bool: + # If the dtype is unsigned, then 'step' cannot be negative. + @parameter + if dtype.is_unsigned(): + return self.start < self.end + else: + if self.step > 0: + return self.start < self.end + return self.end < self.start + + @always_inline + fn __next__(inout self) -> Scalar[dtype]: + var result = self.start + self.start += self.step + return result + + +@value +@register_passable("trivial") +struct _StridedScalarRange[dtype: DType]: + var start: Scalar[dtype] + var end: Scalar[dtype] + var step: Scalar[dtype] + + @always_inline + fn __iter__(self) -> _StridedScalarRangeIterator[dtype]: + return _StridedScalarRangeIterator(self.start, self.end, self.step) + + +@always_inline +fn range[ + dtype: DType +]( + start: Scalar[dtype], end: Scalar[dtype], step: Scalar[dtype] = 1 +) -> _StridedScalarRange[dtype]: + """Constructs a [start; end) Range with a given step. + + Parameters: + dtype: The range type. + + Args: + start: The start of the range. + end: The end of the range. + step: The step for the range. Defaults to 1. + + Returns: + The constructed range. + """ + return _StridedScalarRange(start, end, step) + + +@always_inline +fn range[dtype: DType](end: Scalar[dtype]) -> _StridedScalarRange[dtype]: + """Constructs a [0; end) Range with a step = 1. + + Parameters: + dtype: The range type. + + Args: + end: The end of the range. + + Returns: + The constructed range. + """ + return _StridedScalarRange(0, end, 1) diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index f0095089cd..b3a1f62be6 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -187,6 +187,24 @@ def test_range_bounds(): assert_equal(r[ln - 1], last_seen) +def test_scalar_range(): + r = range(UInt8(2), 16, 4) + assert_equal(r.start, 2) + assert_equal(r.end, 16) + assert_equal(r.step, 4) + + fn append_many(inout list: List, *values: list.T): + for value in values: + list.append(value[]) + + expected_elements = List[UInt8]() + append_many(expected_elements, 2, 6, 10, 14) + actual_elements = List[UInt8]() + for e in r: + actual_elements.append(e) + assert_equal(actual_elements, expected_elements) + + def main(): test_range_len() test_range_len_uint() From 692aeacd583ae6826d24036c351687bf99d87d3e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 8 Oct 2024 18:26:37 +0900 Subject: [PATCH 1710/2019] [mojo-pybind] Start building scaffolding for compiler binding gen This PR starts building out the scaffolding for compiler binding gen. It adds some helper methods to `builtin/_pybind.mojo`, which should be the interface point between the compiler and the standard library. It builds out some basic function verification, but mostly focuses on gnerating the `PyInit_*` function, with a lot of leftover TODOs to fill. The most important TODO is to figure out how to adapt a Mojo `Error` into a Python exception. MODULAR_ORIG_COMMIT_REV_ID: 4d321152364466d671ba2098258124cfd952919e --- stdlib/src/builtin/_pybind.mojo | 118 +++++++++++++++++++++++++ stdlib/src/builtin/string_literal.mojo | 3 + 2 files changed, 121 insertions(+) create mode 100644 stdlib/src/builtin/_pybind.mojo diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo new file mode 100644 index 0000000000..0cb5e01947 --- /dev/null +++ b/stdlib/src/builtin/_pybind.mojo @@ -0,0 +1,118 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from memory import UnsafePointer + +from sys import sizeof, alignof +from sys.ffi import OpaquePointer + +import python._cpython as cp +from python import TypedPythonObject, Python +from python._cpython import ( + PyObjectPtr, + PyMethodDef, + PyType_Slot, + PyType_Spec, + CPython, +) + + +fn create_pybind_module[ + name: StringLiteral +]() raises -> TypedPythonObject["Module"]: + return Python.create_module(name) + + +@value +@register_passable("trivial") +struct PyTypeDefSizes: + var pymethod_def_size: Int + var pymethod_def_align: Int + + var pytype_slot_size: Int + var pytype_slot_align: Int + + var pytype_spec_size: Int + var pytype_spec_align: Int + + +fn get_pytype_def_sizes() -> PyTypeDefSizes: + return PyTypeDefSizes( + sizeof[PyMethodDef](), + alignof[PyMethodDef](), + sizeof[PyType_Slot](), + alignof[PyType_Slot](), + sizeof[PyType_Spec](), + alignof[PyType_Spec](), + ) + + +fn pyobj_destroy_as[T: AnyType](pyobj: PyObjectPtr): + # TODO(MSTDL-633): Is this always safe? Wrap in GIL, because this could + # evaluate arbitrary code? + pyobj.value.bitcast[T]().destroy_pointee() + + +alias MutableGlobalLifetime = __mlir_attr[ + `#lit.lifetime.field<`, + `#lit.static.lifetime : !lit.lifetime<1>`, + `, "__python_globals__"> : !lit.lifetime<1>`, +] + + +# FIXME(MOCO-1308): Workaround crash by adding explicit `alignment=1`. +alias PyGlobalPtr = UnsafePointer[lifetime=MutableGlobalLifetime, alignment=1] + + +fn gen_pytype_wrapper[ + T: AnyType, name: StringLiteral +]( + inout cpython: CPython, + inout module: TypedPythonObject["Module"], + methods: PyGlobalPtr[PyMethodDef], + slots: PyGlobalPtr[PyType_Slot], + spec: PyGlobalPtr[PyType_Spec], +) raises: + # TODO(MOCO-1301): Add support for member method generation. + # TODO(MOCO-1302): Add support for generating member field as computed properties. + + # Zeroed item as terminator + methods[0] = PyMethodDef() + + # TODO(MOCO-1307): Add support for constructor generation. + slots[0] = PyType_Slot( + cp.Py_tp_new, cpython.lib.get_symbol[NoneType]("PyType_GenericNew") + ) + alias dtor = pyobj_destroy_as[T] + slots[1] = PyType_Slot( + cp.Py_tp_dealloc, + __mlir_op.`pop.pointer.bitcast`[_type = OpaquePointer._mlir_type](dtor), + ) + slots[2] = PyType_Slot.null() + + spec.init_pointee_move( + PyType_Spec { + # FIXME(MOCO-1306): This should be `T.__name__`. + name: name.unsafe_cstr_ptr(), + basicsize: sizeof[T](), + itemsize: 0, + flags: cp.Py_TPFLAGS_DEFAULT, + slots: slots, + } + ) + + type_obj = cpython.PyType_FromSpec(spec) + # FIXME(MSTDL-957): We should have APIs that explicitly take a `CPython` + # instance so that callers can pass it around instead of performing a lookup + # each time. + Python.add_object(module, name, type_obj) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 9252014f25..11943dfe7b 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -318,6 +318,7 @@ struct StringLiteral( return __mlir_op.`pop.string.size`(self.value) @always_inline("nodebug") + # FIXME(MSTDL-956): This should return a pointer with StaticConstantLifetime. fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Get raw pointer to the underlying data. @@ -331,6 +332,8 @@ struct StringLiteral( # return type. return ptr.bitcast[UInt8]() + @always_inline + # FIXME(MSTDL-956): This should return a pointer with StaticConstantLifetime. fn unsafe_cstr_ptr(self) -> UnsafePointer[c_char]: """Retrieves a C-string-compatible pointer to the underlying memory. From 91c839984dd5e826a4efa0cbf69ea7c3adbb0611 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 8 Oct 2024 18:27:41 +0900 Subject: [PATCH 1711/2019] [mojo] Add test for compiler pybind gen and error handling This PR adds error handling to the generated PyInit function and adds a unit test based on the mojo-pybind script. MODULAR_ORIG_COMMIT_REV_ID: 0b272141a20892c5b84853547923c9acb7f99658 --- stdlib/src/builtin/_pybind.mojo | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 0cb5e01947..d3139aa1de 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -17,7 +17,8 @@ from sys import sizeof, alignof from sys.ffi import OpaquePointer import python._cpython as cp -from python import TypedPythonObject, Python +from python import TypedPythonObject, Python, PythonObject +from python.python import _get_global_python_itf from python._cpython import ( PyObjectPtr, PyMethodDef, @@ -27,6 +28,10 @@ from python._cpython import ( ) +fn get_cpython() -> CPython: + return _get_global_python_itf().cpython() + + fn create_pybind_module[ name: StringLiteral ]() raises -> TypedPythonObject["Module"]: @@ -63,6 +68,19 @@ fn pyobj_destroy_as[T: AnyType](pyobj: PyObjectPtr): pyobj.value.bitcast[T]().destroy_pointee() +fn fail_initialization(owned err: Error) -> PythonObject: + # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. + cpython = get_cpython() + error_type = cpython.get_error_global("PyExc_Exception") + + cpython.PyErr_SetString( + error_type, + err.unsafe_cstr_ptr(), + ) + _ = err^ + return PythonObject(PyObjectPtr()) + + alias MutableGlobalLifetime = __mlir_attr[ `#lit.lifetime.field<`, `#lit.static.lifetime : !lit.lifetime<1>`, @@ -77,12 +95,13 @@ alias PyGlobalPtr = UnsafePointer[lifetime=MutableGlobalLifetime, alignment=1] fn gen_pytype_wrapper[ T: AnyType, name: StringLiteral ]( - inout cpython: CPython, inout module: TypedPythonObject["Module"], methods: PyGlobalPtr[PyMethodDef], slots: PyGlobalPtr[PyType_Slot], spec: PyGlobalPtr[PyType_Spec], ) raises: + cpython = get_cpython() + # TODO(MOCO-1301): Add support for member method generation. # TODO(MOCO-1302): Add support for generating member field as computed properties. From 2ee138f25db2bf7b2ffabf756b892c33f3f38b89 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 8 Oct 2024 21:18:11 +0900 Subject: [PATCH 1712/2019] [mojo] Implement basic type binding gen This just teaches the python binding generator to generate simple definitions (no member functions, fields, etc.) for Mojo types, based on the argument types of functions in the module. MODULAR_ORIG_COMMIT_REV_ID: 219d28e07f9a56b7fd019280fc0ec30510e1acc6 --- stdlib/src/builtin/_pybind.mojo | 68 ++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index d3139aa1de..8cf9036056 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -27,41 +27,17 @@ from python._cpython import ( CPython, ) +alias PyModule = TypedPythonObject["Module"] + fn get_cpython() -> CPython: return _get_global_python_itf().cpython() -fn create_pybind_module[ - name: StringLiteral -]() raises -> TypedPythonObject["Module"]: +fn create_pybind_module[name: StringLiteral]() raises -> PyModule: return Python.create_module(name) -@value -@register_passable("trivial") -struct PyTypeDefSizes: - var pymethod_def_size: Int - var pymethod_def_align: Int - - var pytype_slot_size: Int - var pytype_slot_align: Int - - var pytype_spec_size: Int - var pytype_spec_align: Int - - -fn get_pytype_def_sizes() -> PyTypeDefSizes: - return PyTypeDefSizes( - sizeof[PyMethodDef](), - alignof[PyMethodDef](), - sizeof[PyType_Slot](), - alignof[PyType_Slot](), - sizeof[PyType_Spec](), - alignof[PyType_Spec](), - ) - - fn pyobj_destroy_as[T: AnyType](pyobj: PyObjectPtr): # TODO(MSTDL-633): Is this always safe? Wrap in GIL, because this could # evaluate arbitrary code? @@ -92,23 +68,43 @@ alias MutableGlobalLifetime = __mlir_attr[ alias PyGlobalPtr = UnsafePointer[lifetime=MutableGlobalLifetime, alignment=1] +@always_inline +fn global_alloc[T: AnyType, len: Int]() -> PyGlobalPtr[T]: + return __mlir_op.`pop.global_alloc`[ + name = "pyglobal".value, + count = len.value, + _type = __mlir_type[`!kgen.pointer<`, T, `>`], + ]() + + +fn pointer_bitcast[ + To: AnyType +](ptr: Pointer) -> Pointer[To, ptr.lifetime, ptr.address_space, *_, **_] as out: + return __type_of(out)( + _mlir_value=__mlir_op.`lit.ref.from_pointer`[ + _type = __type_of(out)._mlir_type + ]( + UnsafePointer(__mlir_op.`lit.ref.to_pointer`(ptr._value)) + .bitcast[To]() + .address + ) + ) + + fn gen_pytype_wrapper[ T: AnyType, name: StringLiteral -]( - inout module: TypedPythonObject["Module"], - methods: PyGlobalPtr[PyMethodDef], - slots: PyGlobalPtr[PyType_Slot], - spec: PyGlobalPtr[PyType_Spec], -) raises: +](inout module: PythonObject) raises: cpython = get_cpython() # TODO(MOCO-1301): Add support for member method generation. # TODO(MOCO-1302): Add support for generating member field as computed properties. # Zeroed item as terminator + methods = global_alloc[PyMethodDef, 1]() methods[0] = PyMethodDef() # TODO(MOCO-1307): Add support for constructor generation. + slots = global_alloc[PyType_Slot, 3]() slots[0] = PyType_Slot( cp.Py_tp_new, cpython.lib.get_symbol[NoneType]("PyType_GenericNew") ) @@ -119,6 +115,7 @@ fn gen_pytype_wrapper[ ) slots[2] = PyType_Slot.null() + spec = global_alloc[PyType_Spec, 1]() spec.init_pointee_move( PyType_Spec { # FIXME(MOCO-1306): This should be `T.__name__`. @@ -134,4 +131,7 @@ fn gen_pytype_wrapper[ # FIXME(MSTDL-957): We should have APIs that explicitly take a `CPython` # instance so that callers can pass it around instead of performing a lookup # each time. - Python.add_object(module, name, type_obj) + # FIXME(MSTDL-969): Bitcast to `TypedPythonObject["Module"]`. + Python.add_object( + pointer_bitcast[PyModule](Pointer.address_of(module))[], name, type_obj + ) From 5c3d0a68b4e007c1a670c963f10ad2a969cb7f32 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 8 Oct 2024 11:52:28 -0700 Subject: [PATCH 1713/2019] [Stdlib] Change divMod to be in snake case to maintain style, NFC MODULAR_ORIG_COMMIT_REV_ID: 99ddd7e8c0bc35a8a6522d26dd1f7b7afc456f58 --- stdlib/src/builtin/int.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index c866ceef67..2eca4a83ce 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -593,8 +593,8 @@ struct Int( var div: Int = self._positive_div(denominator) var mod = self - div * rhs - var divMod = select(((rhs < 0) ^ (self < 0)) & mod, div - 1, div) - div = select(self > 0 & rhs > 0, div, divMod) + var div_mod = select(((rhs < 0) ^ (self < 0)) & mod, div - 1, div) + div = select(self > 0 & rhs > 0, div, div_mod) div = select(rhs == 0, 0, div) return div @@ -612,9 +612,9 @@ struct Int( var div: Int = self._positive_div(denominator) var mod = self - div * rhs - var divMod = select(((rhs < 0) ^ (self < 0)) & mod, mod + rhs, mod) + var div_mod = select(((rhs < 0) ^ (self < 0)) & mod, mod + rhs, mod) mod = select( - self > 0 & rhs > 0, self._positive_rem(denominator), divMod + self > 0 & rhs > 0, self._positive_rem(denominator), div_mod ) mod = select(rhs == 0, 0, mod) return mod From 9e9423dda5c247880e610a0af45ba6299b69a628 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 8 Oct 2024 14:13:55 -0700 Subject: [PATCH 1714/2019] [Stdlib] Introduce likely/unlikely intrinsics These tell the compiler the probable value of a boolean value. MODULAR_ORIG_COMMIT_REV_ID: b0478b31507013344f430c3b36565691b85db622 --- stdlib/src/sys/intrinsics.mojo | 38 ++++++++++++++++++++++++++++ stdlib/test/sys/test_intrinsics.mojo | 7 +++++ 2 files changed, 45 insertions(+) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 08886ccc77..91087cbc8f 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1502,3 +1502,41 @@ struct _RegisterPackType[*a: AnyTrivialRegType]: The tuple element at the requested index. """ return __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) + + +# ===----------------------------------------------------------------------=== # +# likely +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn likely(val: Bool) -> Bool: + """Provides information that the most probable value of `val` is going to be + `True`. This information can be used by optimizers. + + Args: + val: The input value which is likely to be `True` most of the time. + + Returns: + The input value. + """ + return llvm_intrinsic["llvm.expect", Bool](val, True) + + +# ===----------------------------------------------------------------------=== # +# unlikely +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn unlikely(val: Bool) -> Bool: + """Provides information that the most probable value of `val` is going to be + `False`. This information can be used by optimizers. + + Args: + val: The input value which is likely to be `False` most of the time. + + Returns: + The input value. + """ + return llvm_intrinsic["llvm.expect", Bool](val, False) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index af63cec115..e604e980ee 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -19,6 +19,7 @@ from sys import ( strided_load, strided_store, ) +from sys.intrinsics import likely, unlikely from memory import UnsafePointer, memset_zero from testing import assert_equal @@ -121,9 +122,15 @@ fn test_strided_store() raises: vector.free() +def test_likely_unlikely(): + assert_equal(likely(True), True) + assert_equal(unlikely(True), True) + + def main(): test_compressed_store() test_masked_load() test_masked_store() test_strided_load() test_strided_store() + test_likely_unlikely() From b67142a05809d5196362163f9c276a016763471f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 8 Oct 2024 18:57:07 -0700 Subject: [PATCH 1715/2019] [Stdlib] Add `assume` intrinsic Signals to the optimizer that the condition is always true. This allows the optimizer to optimize the code. MODULAR_ORIG_COMMIT_REV_ID: c6341610f0cd6c9d7a2222a67c253c5c5d952c9f --- stdlib/src/sys/intrinsics.mojo | 16 ++++++++++++++++ stdlib/test/sys/test_intrinsics.mojo | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 91087cbc8f..1fd9b3c1b2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1540,3 +1540,19 @@ fn unlikely(val: Bool) -> Bool: The input value. """ return llvm_intrinsic["llvm.expect", Bool](val, False) + + +# ===----------------------------------------------------------------------=== # +# assume +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn assume(val: Bool): + """Signals to the optimizer that the condition is always true. This allows + the optimizer to optimize the code. + + Args: + val: The input value which is assumed to be `True`. + """ + llvm_intrinsic["llvm.assume", NoneType](val) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index e604e980ee..8aa06bdc6d 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -19,7 +19,7 @@ from sys import ( strided_load, strided_store, ) -from sys.intrinsics import likely, unlikely +from sys.intrinsics import likely, unlikely, assume from memory import UnsafePointer, memset_zero from testing import assert_equal @@ -127,6 +127,10 @@ def test_likely_unlikely(): assert_equal(unlikely(True), True) +def test_assume(): + assume(True) + + def main(): test_compressed_store() test_masked_load() @@ -134,3 +138,4 @@ def main(): test_strided_load() test_strided_store() test_likely_unlikely() + test_assume() From aadfa9f173c651d66e844bc9d64f40bcbef14080 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Wed, 9 Oct 2024 09:35:16 -0500 Subject: [PATCH 1716/2019] [Mojo] Support `{min,max}_finite[DType.bool]` MODULAR_ORIG_COMMIT_REV_ID: 4c185422b1e8e1449920a13845795d309aa10a38 --- stdlib/src/utils/numerics.mojo | 4 ++++ stdlib/test/utils/test_numerics.mojo | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index f880e0eb06..a52594be68 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -755,6 +755,8 @@ fn max_finite[type: DType]() -> Scalar[type]: return 3.40282346638528859812e38 elif type is DType.float64: return 1.79769313486231570815e308 + elif type is DType.bool: + return True else: constrained[False, "max_finite() called on unsupported type"]() return 0 @@ -794,6 +796,8 @@ fn min_finite[type: DType]() -> Scalar[type]: return -9223372036854775808 elif type.is_floating_point(): return -max_finite[type]() + elif type is DType.bool: + return False else: constrained[False, "min_finite() called on unsupported type"]() return 0 diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index 5490746a46..ee85152855 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -156,6 +156,8 @@ def test_max_finite(): assert_almost_equal(max_finite[DType.float32](), 3.4028235e38) assert_almost_equal(max_finite[DType.float64](), 1.7976931348623157e308) + assert_true(max_finite[DType.bool]()) + assert_true(overflow_int[DType.int8]()) assert_true(overflow_int[DType.uint8]()) assert_true(overflow_int[DType.int16]()) @@ -186,6 +188,8 @@ def test_min_finite(): assert_almost_equal(min_finite[DType.float32](), -3.4028235e38) assert_almost_equal(min_finite[DType.float64](), -1.7976931348623157e308) + assert_false(min_finite[DType.bool]()) + assert_true(underflow_int[DType.int8]()) assert_true(underflow_int[DType.uint8]()) assert_true(underflow_int[DType.int16]()) @@ -202,6 +206,7 @@ def test_min_finite(): def test_max_or_inf(): assert_almost_equal(max_or_inf[DType.float32](), inf[DType.float32]()) assert_almost_equal(max_or_inf[DType.float64](), inf[DType.float64]()) + assert_true(max_or_inf[DType.bool]()) def test_min_or_neg_inf(): @@ -211,6 +216,7 @@ def test_min_or_neg_inf(): assert_almost_equal( min_or_neg_inf[DType.float64](), neg_inf[DType.float64]() ) + assert_false(min_or_neg_inf[DType.bool]()) def test_neg_inf(): From 949977c7b7ea86961c852e8f7edbceb0a1dbffb8 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:38:33 -0400 Subject: [PATCH 1717/2019] [External] [stdlib] Add examples for `env_get_int` and `sizeof` (#48622) [External] [stdlib] Add examples for `env_get_int` and `sizeof` Add a few examples: - `env_get_int` for parametrizing - `sizeof` for sizes Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3593 MODULAR_ORIG_COMMIT_REV_ID: 3f8ef10024ff3736cbd4f1ad56038b595ca09aea --- stdlib/src/sys/info.mojo | 16 ++++++++++++++++ stdlib/src/sys/param_env.mojo | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index a8aec88aa2..6d2d86bb9e 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -565,6 +565,22 @@ fn sizeof[ Returns: The size of the type in bytes. + + Example: + ```mojo + from sys.info import sizeof + def main(): + print( + sizeof[UInt8]() == 1, + sizeof[UInt16]() == 2, + sizeof[Int32]() == 4, + sizeof[Float64]() == 8, + sizeof[ + SIMD[DType.uint8, 4] + ]() == 4, + ) + ``` + Note: `align_of` is in same module. """ alias mlir_type = __mlir_attr[ `#kgen.param.expr Int: Returns: An integer parameter value. + + Example: + ```mojo + from sys.param_env import env_get_int + + def main(): + alias number = env_get_int[ + "favorite_number", + 1 # Default value + ]() + parametrized[number]() + + fn parametrized[num: Int](): + print(num) + ``` + + If the program is `app.mojo`: + - `mojo run -D favorite_number=2 app.mojo` + - `mojo run -D app.mojo` + + Note: useful for parameterizing SIMD vector sizes. """ @parameter From 7124caa3e010f2d80bb9a1daa8e76eac349d4209 Mon Sep 17 00:00:00 2001 From: Shouzheng Liu Date: Wed, 9 Oct 2024 12:52:38 -0700 Subject: [PATCH 1718/2019] [stdlib] Add `__rpow__` to SIMD This update introduces support for reverse power operations between the same Dtype. MODULAR_ORIG_COMMIT_REV_ID: 370c4ba4c07e243ea45b2101e8b71f3630c07690 --- stdlib/src/builtin/simd.mojo | 13 +++++++++++++ stdlib/test/builtin/test_simd.mojo | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 104152895f..8f1d48cb8d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1221,6 +1221,19 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the type must be numeric"]() return value % self + @always_inline("nodebug") + fn __rpow__(self, base: Self) -> Self: + """Returns `base ** self`. + + Args: + base: The base value. + + Returns: + `base ** self`. + """ + constrained[type.is_numeric(), "the type must be numeric"]() + return base**self + @always_inline("nodebug") fn __rand__(self, value: Self) -> Self: """Returns `value & self`. diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8253e5186f..6dfca29604 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1535,6 +1535,29 @@ def test_powf(): ) +def test_rpow(): + alias F32x4 = SIMD[DType.float32, 4] + alias I32x4 = SIMD[DType.int32, 4] + + var f32x4_val = F32x4(0, 1, 2, 3) + var i32x4_val = I32x4(0, 1, 2, 3) + + assert_equal(0**i32x4_val, I32x4(1, 0, 0, 0)) + assert_equal(2**i32x4_val, I32x4(1, 2, 4, 8)) + assert_equal((-1) ** i32x4_val, I32x4(1, -1, 1, -1)) + + assert_equal(Int(0) ** i32x4_val, I32x4(1, 0, 0, 0)) + assert_equal(Int(2) ** i32x4_val, I32x4(1, 2, 4, 8)) + assert_equal(Int(-1) ** i32x4_val, I32x4(1, -1, 1, -1)) + + assert_equal(UInt(2) ** i32x4_val, I32x4(1, 2, 4, 8)) + assert_equal(UInt(0) ** i32x4_val, I32x4(1, 0, 0, 0)) + + assert_almost_equal(1.0**f32x4_val, F32x4(1.0, 1.0, 1.0, 1.0)) + assert_almost_equal(2.5**f32x4_val, F32x4(1.0, 2.5, 6.25, 15.625)) + assert_almost_equal(3.0**f32x4_val, F32x4(1.0, 3.0, 9.0, 27.0)) + + def test_modf(): var f32 = _modf(Float32(123.5)) assert_almost_equal(f32[0], 123) @@ -1809,6 +1832,7 @@ def main(): test_mod() test_pow() test_powf() + test_rpow() test_radd() test_reduce() test_reduce_bit_count() From 36517f6944de1618d704e1c34c5996e603580421 Mon Sep 17 00:00:00 2001 From: Spenser Bauman Date: Wed, 9 Oct 2024 20:31:06 +0000 Subject: [PATCH 1719/2019] [mojo-stdlib] Fix alignment issue in memcpy implementation where values would be loaded/stored from the source/destination pointers at invalid alignments. Memcpy will now always generate load/store operations with an alignment of 1. MODULAR_ORIG_COMMIT_REV_ID: 64ef02ed96a38a11f95b29e46db0b4d03326f5ee --- stdlib/src/memory/memory.mojo | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index f8af74202e..7e6dc777a3 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -167,16 +167,27 @@ fn _memcpy_impl( if n <= 16: if n >= 8: var ui64_size = sizeof[Int64]() - dest_data.bitcast[Int64]()[] = src_data.bitcast[Int64]()[0] - dest_data.offset(n - ui64_size).bitcast[ - Int64 - ]()[] = src_data.offset(n - ui64_size).bitcast[Int64]()[0] + dest_data.bitcast[Int64]().store[alignment=1]( + 0, src_data.bitcast[Int64]().load[alignment=1](0) + ) + dest_data.offset(n - ui64_size).bitcast[Int64]().store[alignment=1]( + 0, + src_data.offset(n - ui64_size) + .bitcast[Int64]() + .load[alignment=1](0), + ) return + var ui32_size = sizeof[Int32]() - dest_data.bitcast[Int32]()[] = src_data.bitcast[Int32]()[0] - dest_data.offset(n - ui32_size).bitcast[Int32]()[] = src_data.offset( - n - ui32_size - ).bitcast[Int32]()[0] + dest_data.bitcast[Int32]().store[alignment=1]( + 0, src_data.bitcast[Int32]().load[alignment=1](0) + ) + dest_data.offset(n - ui32_size).bitcast[Int32]().store[alignment=1]( + 0, + src_data.offset(n - ui32_size) + .bitcast[Int32]() + .load[alignment=1](0), + ) return # TODO (#10566): This branch appears to cause a 12% regression in BERT by From 35aa0387b8ba72b174f9057228bd4f4e08b2199f Mon Sep 17 00:00:00 2001 From: Nikolay Panchenko Date: Thu, 10 Oct 2024 08:10:46 -0400 Subject: [PATCH 1720/2019] [stdlib] Use `kgen.param.constant` instead of `kgen.undef` MODULAR_ORIG_COMMIT_REV_ID: 4801aac183208ef4f28482d71b684be6d0633245 --- stdlib/src/builtin/simd.mojo | 20 +++++++++++++---- stdlib/src/collections/inline_array.mojo | 25 +++++++++++++++++----- stdlib/src/memory/maybe_uninitialized.mojo | 5 ++++- stdlib/src/utils/static_tuple.mojo | 14 +++++++++--- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8f1d48cb8d..e7af0753a8 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -376,8 +376,14 @@ struct SIMD[type: DType, size: Int]( ), ) - self = __mlir_op.`kgen.undef`[ - _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] + self = __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `, `, type.value, `>` + ], + value = __mlir_attr[ + `#kgen.unknown : `, + __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], + ], ]() @parameter @@ -1842,10 +1848,16 @@ struct SIMD[type: DType, size: Int]( fn _convert_variadic_to_pop_array[ *mask: Int ]() -> __mlir_type[`!pop.array<`, output_size.value, `, `, Int, `>`]: - var array = __mlir_op.`kgen.undef`[ + var array = __mlir_op.`kgen.param.constant`[ _type = __mlir_type[ `!pop.array<`, output_size.value, `, `, Int, `>` - ] + ], + value = __mlir_attr[ + `#kgen.unknown : `, + __mlir_type[ + `!pop.array<`, output_size.value, `, `, Int, `>` + ], + ], ]() var array_ptr = UnsafePointer.address_of(array) diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 201222f15d..0721f33e6f 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -79,7 +79,10 @@ struct InlineArray[ " 'unsafe_uninitialized'." ), ]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self._array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() @always_inline fn __init__(inout self, *, unsafe_uninitialized: Bool): @@ -102,7 +105,10 @@ struct InlineArray[ Always set to `True` (it's not actually used inside the constructor). """ _inline_array_construction_checks[size]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self._array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() fn __init__( inout self, @@ -122,7 +128,10 @@ struct InlineArray[ unsafe_assume_initialized: The array of `UnsafeMaybeUninitialized` elements. """ - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self._array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() for i in range(Self.size): unsafe_assume_initialized[i].unsafe_ptr().move_pointee_into( @@ -137,7 +146,10 @@ struct InlineArray[ fill: The element to fill each index. """ _inline_array_construction_checks[size]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self._array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() @parameter for i in range(size): @@ -168,7 +180,10 @@ struct InlineArray[ debug_assert(len(storage) == size, "Elements must be of length size") _inline_array_construction_checks[size]() - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self._array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() # Move each element into the array storage. @parameter diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 894bfeda9e..93c0b324c3 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -37,7 +37,10 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): @always_inline fn __init__(inout self): """The memory is now considered uninitialized.""" - self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + self._array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() @doc_private @always_inline diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index d281c4bcb0..5199411cc9 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -77,8 +77,13 @@ fn _create_array[ ](lst[0]) debug_assert(size == len(lst), "mismatch in the number of elements") - var array = __mlir_op.`kgen.undef`[ - _type = __mlir_type[`!pop.array<`, size.value, `, `, type, `>`] + + var array = __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], + value = __mlir_attr[ + `#kgen.unknown : `, + __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], + ], ]() @parameter @@ -124,7 +129,10 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): fn __init__(inout self): """Constructs an empty (undefined) tuple.""" _static_tuple_construction_checks[size]() - self.array = __mlir_op.`kgen.undef`[_type = Self.type]() + self.array = __mlir_op.`kgen.param.constant`[ + _type = Self.type, + value = __mlir_attr[`#kgen.unknown : `, Self.type], + ]() @always_inline fn __init__(inout self, *elems: Self.element_type): From fa1d7bf6e12ab0a386912120e4df0f5985f2b71b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 10 Oct 2024 09:19:38 -0700 Subject: [PATCH 1721/2019] [Stdlib] Ensure we do unsigned comparisons when the range is unsigned MODULAR_ORIG_COMMIT_REV_ID: e5fad271d21aa5f3cf96e8b6f255eb45392517a3 --- stdlib/src/builtin/range.mojo | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index d3d5dc89f1..965ea7dfab 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -340,8 +340,7 @@ struct _UIntZeroStartingRange(UIntSized): @always_inline fn __hasmore__(self) -> Bool: - # FIXME(KERN-1024): This should be an unsigned comparison! - return Int(self.__len__().value) > 0 + return self.__len__() > 0 @always_inline fn __len__(self) -> UInt: @@ -372,8 +371,7 @@ struct _UIntStridedRangeIterator(UIntSized): @always_inline fn __hasmore__(self) -> Bool: - # FIXME(KERN-1024): This should be an unsigned comparison! - return Int(self.__len__().value) > 0 + return self.__len__() > 0 @value @@ -413,8 +411,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): @always_inline fn __hasmore__(self) -> Bool: - # FIXME(KERN-1024): This should be an unsigned comparison! - return Int(self.__len__().value) > 0 + return self.__len__() > 0 @always_inline fn __len__(self) -> UInt: From 6eb7fa7408bb74ad214905686ee8c6306887dd37 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 10 Oct 2024 10:19:55 -0600 Subject: [PATCH 1722/2019] [stdlib] Make `merge` private in sort module `merge` is a bit of a dangerous API since it takes a span as an arguments and expects its arguments to be uninit data. It's currently quite specific to its one caller. So, make it private to avoid confusion that it's more general than it is right now. While in the neighborhood, remove an unused variable. MODULAR_ORIG_COMMIT_REV_ID: 1ad4d1ebaea7f41998f25fa181c14522502af71f --- stdlib/src/builtin/sort.mojo | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index c00a91a9d5..8a2001e846 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -262,7 +262,7 @@ fn _quicksort[ # ===----------------------------------------------------------------------===# -fn merge[ +fn _merge[ type: CollectionElement, span_lifetime: ImmutableLifetime, result_lifetime: MutableLifetime, //, @@ -330,7 +330,6 @@ fn _stable_sort_impl[ if size <= 1: return var i = 0 - var array = span.unsafe_ptr() while i < size: _insertion_sort[cmp_fn]( span[i : min(i + insertion_sort_threshold, size)] @@ -342,7 +341,7 @@ fn _stable_sort_impl[ while j + merge_size < size: var span1 = span[j : j + merge_size] var span2 = span[j + merge_size : min(size, j + 2 * merge_size)] - merge[cmp_fn]( + _merge[cmp_fn]( span1.get_immutable(), span2.get_immutable(), temp_buff ) for i in range(merge_size + len(span2)): From 2844b45bd5b86b986c0ca12e34d6e643100cc36f Mon Sep 17 00:00:00 2001 From: akirchhoff-modular Date: Thu, 10 Oct 2024 09:49:19 -0700 Subject: [PATCH 1723/2019] [mojo-stdlib] Make Arc Identifiable It is useful to be able to tell whether two `Arc`s point at the same object or not. Make `Arc` `Identifiable` so this is easy. MODULAR_ORIG_COMMIT_REV_ID: fe81c5ccbc91e65ccc7e245cbec66016dad9972e --- docs/changelog.md | 4 ++++ stdlib/src/memory/arc.mojo | 24 +++++++++++++++++++++++- stdlib/test/memory/test_arc.mojo | 11 +++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3544a058ab..8ba2d2c751 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -188,6 +188,10 @@ what we publish. print((a[0, 1])) # 2 ``` +- [`Arc`](/mojo/stdlib/memory/arc/Arc) now implements + [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable), and can be + compared for pointer equivalence using `a is b`. + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index e72602239b..7d66110b6e 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -70,7 +70,7 @@ struct _ArcInner[T: Movable]: @register_passable -struct Arc[T: Movable](CollectionElement, CollectionElementNew): +struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): """Atomic reference-counted pointer. This smart pointer owns an instance of `T` indirectly managed on the heap. @@ -170,3 +170,25 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew): The current amount of references to the pointee. """ return self._inner[].refcount.load() + + fn __is__(self, rhs: Self) -> Bool: + """Returns True if the two Arcs point at the same object. + + Args: + rhs: The other Arc. + + Returns: + True if the two Arcs point at the same object and False otherwise. + """ + return self._inner == rhs._inner + + fn __isnot__(self, rhs: Self) -> Bool: + """Returns True if the two Arcs point at different objects. + + Args: + rhs: The other Arc. + + Returns: + True if the two Arcs point at different objects and False otherwise. + """ + return self._inner != rhs._inner diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 129bbea32a..394b1d507f 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -26,6 +26,16 @@ def test_basic(): assert_equal(3, p[]) +def test_is(): + var p = Arc(3) + var p2 = p + var p3 = Arc(3) + assert_true(p is p2) + assert_false(p is not p2) + assert_false(p is p3) + assert_true(p is not p3) + + def test_deleter_not_called_until_no_references(): var deleted = False var p = Arc(ObservableDel(UnsafePointer.address_of(deleted))) @@ -69,6 +79,7 @@ def test_count(): def main(): test_basic() + test_is() test_deleter_not_called_until_no_references() test_deleter_not_called_until_no_references_explicit_copy() test_count() From f91a99de3a4dc656baf25da142e7cfa6d438524c Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:16:28 -0400 Subject: [PATCH 1724/2019] [External] [stdlib] Add example for `constrained` (#48798) [External] [stdlib] Add example for `constrained` Add an example for `constrained`. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3596 MODULAR_ORIG_COMMIT_REV_ID: a62d34c9585bd0172112263c27fa1580bfe9581b --- stdlib/src/builtin/constrained.mojo | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/stdlib/src/builtin/constrained.mojo b/stdlib/src/builtin/constrained.mojo index 4b4a03a990..c7cbb756b4 100644 --- a/stdlib/src/builtin/constrained.mojo +++ b/stdlib/src/builtin/constrained.mojo @@ -28,6 +28,28 @@ fn constrained[cond: Bool, msg: StringLiteral = "param assertion failed"](): Parameters: cond: The bool value to assert. msg: The message to display on failure. + + Example: + + ```mojo + from sys.info import num_physical_cores + + def main(): + alias cores_to_use = 2 + multicore_check[cores_to_use]() + + def multicore_check[cores: Int](): + constrained[ + cores <= num_physical_cores(), + "build failed: not enough cores" + ]() + constrained[ + cores >= 2, + "at least two cores are required" + ]() + + ``` + """ __mlir_op.`kgen.param.assert`[ cond = cond.__mlir_i1__(), message = msg.value From 0532c240a7179f0dc4b1aa555d88ea6a335ff208 Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 10 Oct 2024 13:22:00 -0400 Subject: [PATCH 1725/2019] [External] [stdlib] Update `String.as_bytes` to return a span (#48880) [External] [stdlib] Update `String.as_bytes` to return a span Change `String.as_bytes()` to return a `Span[UInt8]` instead of a `List[Int8]`. The old behavior can be achieved by using `List(s.as_bytes())`. Fixes https://github.com/modularml/mojo/issues/3633 Co-authored-by: soraros Closes modularml/mojo#3636 MODULAR_ORIG_COMMIT_REV_ID: 8200c8dbda52d089d84de94c2d959a1acc0562a8 --- docs/changelog.md | 3 +++ stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/collections/string.mojo | 30 ++++-------------------- stdlib/src/utils/inline_string.mojo | 8 +++---- stdlib/src/utils/string_slice.mojo | 4 ++-- stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 4 ++-- 8 files changed, 19 insertions(+), 36 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 8ba2d2c751..2ab8fade35 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -261,6 +261,9 @@ what we publish. `IndexList`. The datastructure now allows one to specify the index bitwidth of the elements along with whether the underlying indices are signed or unsigned. +- `String.as_bytes()` now returns a `Span[UInt8]` instead of a `List[Int8]`. The + old behavior can be achieved by using `List(s.as_bytes())`. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index e7af0753a8..cb5fc5ae55 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1431,7 +1431,7 @@ struct SIMD[type: DType, size: Int]( @parameter if size > 1: # TODO: Fix when slice indexing is implemented on StringSlice - values = StringSlice(unsafe_from_utf8=output.as_bytes_span()[1:-1]) + values = StringSlice(unsafe_from_utf8=output.as_bytes()[1:-1]) return ( "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 11943dfe7b..e86946496e 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -360,7 +360,7 @@ struct StringLiteral( ) @always_inline - fn as_bytes_span(self) -> Span[UInt8, StaticConstantLifetime]: + fn as_bytes(self) -> Span[UInt8, StaticConstantLifetime]: """ Returns a contiguous Span of the bytes owned by this string. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index dd1db6aba7..75cea7438f 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -789,13 +789,13 @@ struct String( """ # Calculate length in bytes - var length: Int = len(str_slice.as_bytes_span()) + var length: Int = len(str_slice.as_bytes()) var buffer = Self._buffer_type() # +1 for null terminator, initialized to 0 buffer.resize(length + 1, 0) memcpy( dest=buffer.data, - src=str_slice.as_bytes_span().unsafe_ptr(), + src=str_slice.as_bytes().unsafe_ptr(), count=length, ) self = Self(buffer^) @@ -1387,28 +1387,8 @@ struct String( """ return self.unsafe_ptr().bitcast[c_char]() - fn as_bytes(self) -> Self._buffer_type: - """Retrieves the underlying byte sequence encoding the characters in - this string. - - This does not include the trailing null terminator. - - Returns: - A sequence containing the encoded characters stored in this string. - """ - - # TODO(lifetimes): Return a reference rather than a copy - var copy = self._buffer - var last = copy.pop() - debug_assert( - last == 0, - "expected last element of String buffer to be null terminator", - ) - - return copy - @always_inline - fn as_bytes_span(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: @@ -1433,7 +1413,7 @@ struct String( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in String so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self.as_bytes_span()) + return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline fn byte_length(self) -> Int: @@ -2214,7 +2194,7 @@ struct String( debug_assert( len(fillchar) == 1, "fill char needs to be a one byte literal" ) - var fillbyte = fillchar.as_bytes_span()[0] + var fillbyte = fillchar.as_bytes()[0] var buffer = Self._buffer_type(capacity=width + 1) buffer.resize(width, fillbyte) buffer.append(0) diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 2c5ce4555f..d79ee91ffc 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -279,10 +279,10 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): # FIXME(MSTDL-160): # Enforce UTF-8 encoding in _FixedString so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self.as_bytes_span()) + return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes_span(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -518,10 +518,10 @@ struct _FixedString[CAP: Int]( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in _FixedString so this is actually # guaranteed to be valid. - return StringSlice(unsafe_from_utf8=self.as_bytes_span()) + return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes_span(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 39166bc6c2..9bff4e9f5a 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -463,7 +463,7 @@ struct StringSlice[ # ===------------------------------------------------------------------===# @always_inline - fn as_bytes_span(self) -> Span[UInt8, lifetime]: + fn as_bytes(self) -> Span[UInt8, lifetime]: """Get the sequence of encoded bytes as a slice of the underlying string. Returns: @@ -489,7 +489,7 @@ struct StringSlice[ The length of this string slice in bytes. """ - return len(self.as_bytes_span()) + return len(self.as_bytes()) fn _strref_dangerous(self) -> StringRef: """Returns an inner pointer to the string as a StringRef. diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 0abe92062d..67bb8a0e73 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -186,7 +186,7 @@ def test_file_write_span(): var content: String = "The quick brown fox jumps over the lazy dog" var TEMP_FILE = Path(gettempdir().value()) / "test_file_write_span" with open(TEMP_FILE, "w") as f: - f.write(content.as_bytes_span()) + f.write(content.as_bytes()) with open(TEMP_FILE, "r") as read_file: assert_equal(read_file.read(), content) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 35d216adc3..cf21e78a9c 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -20,7 +20,7 @@ from utils._utf8_validation import _is_valid_utf8 fn test_string_literal_byte_span() raises: alias string: StringLiteral = "Hello" - alias slc = string.as_bytes_span() + alias slc = string.as_bytes() assert_equal(len(slc), 5) assert_equal(slc[0], ord("H")) @@ -32,7 +32,7 @@ fn test_string_literal_byte_span() raises: fn test_string_byte_span() raises: var string = String("Hello") - var str_slice = string.as_bytes_span() + var str_slice = string.as_bytes() assert_equal(len(str_slice), 5) assert_equal(str_slice[0], ord("H")) From 1009ac23f1742d526887fa17a19f814a4fae1e62 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 10 Oct 2024 14:11:05 -0400 Subject: [PATCH 1726/2019] [stdlib] Rename files to comply with snake_case convention MODULAR_ORIG_COMMIT_REV_ID: f33f231d54276ad75c7210ecebe4c4e4a5ea6a92 --- stdlib/scripts/{check-licenses.mojo => check_licenses.mojo} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename stdlib/scripts/{check-licenses.mojo => check_licenses.mojo} (100%) diff --git a/stdlib/scripts/check-licenses.mojo b/stdlib/scripts/check_licenses.mojo similarity index 100% rename from stdlib/scripts/check-licenses.mojo rename to stdlib/scripts/check_licenses.mojo From 00c939a906d1e5d3ad47b131cd7334d917240a67 Mon Sep 17 00:00:00 2001 From: Rasool Sharifi Date: Fri, 11 Oct 2024 15:02:46 -0400 Subject: [PATCH 1727/2019] [stdlib] Public title reduce ****** may raise and there functions which uses it should also raise MODULAR_ORIG_COMMIT_REV_ID: 9d876293960508b4352f11ff5372af802dfe6896 --- examples/reduce.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 69506662cb..8e213c6b86 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -36,7 +36,7 @@ alias scalar = Scalar[type] # Use the https://en.wikipedia.org/wiki/Kahan_summation_algorithm # Simple summation of the array elements -fn naive_reduce_sum[size: Int](buffer: Buffer[type, size]) -> scalar: +fn naive_reduce_sum[size: Int](buffer: Buffer[type, size]) raises -> scalar: var my_sum: scalar = 0 var c: scalar = 0 for i in range(buffer.size): @@ -47,7 +47,7 @@ fn naive_reduce_sum[size: Int](buffer: Buffer[type, size]) -> scalar: return my_sum -fn stdlib_reduce_sum[size: Int](array: Buffer[type, size]) -> scalar: +fn stdlib_reduce_sum[size: Int](array: Buffer[type, size]) raises -> scalar: var my_sum = sum(array) return my_sum @@ -62,12 +62,12 @@ def pretty_print(name: String, elements: Int, time: Float64): fn bench[ - func: fn[size: Int] (buffer: Buffer[type, size]) -> scalar, + func: fn[size: Int] (buffer: Buffer[type, size]) raises -> scalar, size: Int, name: String, ](buffer: Buffer[type, size]) raises: @parameter - fn runner(): + fn runner() raises: var result = func[size](buffer) keep(result) From e1eb04d4d6def7c949e7d61fe8d7c9fbd3329f3c Mon Sep 17 00:00:00 2001 From: soraros Date: Fri, 11 Oct 2024 15:30:52 -0600 Subject: [PATCH 1728/2019] [External] [stdlib] Clean up `CPython` primitive type conversion (#48938) [External] [stdlib] Clean up `CPython` primitive type conversion - Align types with the corresponding C API. - Add function signatures and URLs to the CPython API documentation. - Reorder methods in the following order: `bool`, `int`, `float`, `str`. Within each type, place `PyT_FromX` before `PyT_AsX`. - Remove redundant overloads. Co-authored-by: soraros Closes modularml/mojo#3639 MODULAR_ORIG_COMMIT_REV_ID: 611e1ed7dad3e42c26b4efebfee86981aab1068c --- stdlib/src/python/_cpython.mojo | 179 ++++++++++++++++----------- stdlib/src/python/python_object.mojo | 90 +++++++------- 2 files changed, 151 insertions(+), 118 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 3652d84c5d..d16d61ba6f 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -23,7 +23,16 @@ from os.path import dirname from pathlib import Path from sys import external_call from sys.arg import argv -from sys.ffi import DLHandle, c_char, c_int, c_uint, OpaquePointer +from sys.ffi import ( + DLHandle, + OpaquePointer, + c_char, + c_int, + c_long, + c_size_t, + c_ssize_t, + c_uint, +) from python.python import _get_global_python_itf from python._bindings import Typed_initproc @@ -611,27 +620,6 @@ struct CPython: error += "\n https://modul.ar/fix-python\n" raise error - # ===-------------------------------------------------------------------===# - # None - # ===-------------------------------------------------------------------===# - - fn Py_None(inout self) -> PyObjectPtr: - """Get a None value, of type NoneType.""" - - # Get pointer to the immortal `None` PyObject struct instance. - # Note: - # The name of this global is technical a private part of the - # CPython API, but unfortunately the only stable ways to access it are - # macros. - var ptr = self.lib.get_symbol[Int8]( - "_Py_NoneStruct", - ) - - if not ptr: - abort("error: unable to get pointer to CPython `None` struct") - - return PyObjectPtr(ptr) - # ===-------------------------------------------------------------------===# # Logging # ===-------------------------------------------------------------------===# @@ -1293,17 +1281,43 @@ struct CPython: )(list_obj, index) # ===-------------------------------------------------------------------===# - # Primitive type conversions + # Concrete Objects + # ref: https://docs.python.org/3/c-api/concrete.html # ===-------------------------------------------------------------------===# - fn PyLong_FromLong(inout self, value: Int) -> PyObjectPtr: - var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( - "PyLong_FromLong" + # The None Object + # ref: https://docs.python.org/3/c-api/none.html + + # PyObject *Py_None + # https://docs.python.org/3/c-api/none.html#c.Py_None + fn Py_None(inout self) -> PyObjectPtr: + """Get a None value, of type NoneType.""" + + # Get pointer to the immortal `None` PyObject struct instance. + # Note: + # The name of this global is technical a private part of the + # CPython API, but unfortunately the only stable ways to access it are + # macros. + ptr = self.lib.get_symbol[c_char]("_Py_NoneStruct") + + if not ptr: + abort("error: unable to get pointer to CPython `None` struct") + + return PyObjectPtr(ptr) + + # Boolean Objects + # ref: https://docs.python.org/3/c-api/bool.html + + # PyObject *PyBool_FromLong(long v) + # ref: https://docs.python.org/3/c-api/bool.html#c.PyBool_FromLong + fn PyBool_FromLong(inout self, value: c_long) -> PyObjectPtr: + r = self.lib.get_function[fn (c_long) -> PyObjectPtr]( + "PyBool_FromLong" )(value) self.log( r._get_ptr_as_int(), - " NEWREF PyLong_FromLong, refcnt:", + " NEWREF PyBool_FromLong, refcnt:", self._Py_REFCNT(r), ", value:", value, @@ -1312,14 +1326,19 @@ struct CPython: self._inc_total_rc() return r - fn PyBool_FromLong(inout self, value: Int) -> PyObjectPtr: - var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( - "PyBool_FromLong" + # Integer Objects + # ref: https://docs.python.org/3/c-api/long.html + + # PyObject *PyLong_FromSsize_t(Py_ssize_t v) + # ref: https://docs.python.org/3/c-api/long.html#c.PyLong_FromSsize_t + fn PyLong_FromSsize_t(inout self, value: c_ssize_t) -> PyObjectPtr: + r = self.lib.get_function[fn (c_ssize_t) -> PyObjectPtr]( + "PyLong_FromSsize_t" )(value) self.log( r._get_ptr_as_int(), - " NEWREF PyBool_FromLong, refcnt:", + " NEWREF PyLong_FromSsize_t, refcnt:", self._Py_REFCNT(r), ", value:", value, @@ -1328,33 +1347,16 @@ struct CPython: self._inc_total_rc() return r - fn toPython(inout self, litString: StringRef) -> PyObjectPtr: - return self.PyString_FromStringAndSize(litString) - - fn toPython(inout self, litInt: Int) -> PyObjectPtr: - return self.PyLong_FromLong(litInt.value) - - fn toPython(inout self, litBool: Bool) -> PyObjectPtr: - return self.PyBool_FromLong(1 if litBool else 0) - - fn PyLong_AsLong(inout self, py_object: PyObjectPtr) -> Int: - return self.lib.get_function[fn (PyObjectPtr) -> Int]("PyLong_AsLong")( - py_object - ) - - fn PyFloat_AsDouble(inout self, py_object: PyObjectPtr) -> Float64: - return self.lib.get_function[fn (PyObjectPtr) -> Float64]( - "PyFloat_AsDouble" - )(py_object) - - fn PyFloat_FromDouble(inout self, value: Float64) -> PyObjectPtr: - var r = self.lib.get_function[fn (Float64) -> PyObjectPtr]( - "PyFloat_FromDouble" + # PyObject *PyLong_FromSize_t(Py_ssize_t v) + # ref: https://docs.python.org/3/c-api/long.html#c.PyLong_FromSize_t + fn PyLong_FromSize_t(inout self, value: c_size_t) -> PyObjectPtr: + r = self.lib.get_function[fn (c_size_t) -> PyObjectPtr]( + "PyLong_FromSize_t" )(value) self.log( r._get_ptr_as_int(), - " NEWREF PyFloat_FromDouble, refcnt:", + " NEWREF PyLong_FromSize_t, refcnt:", self._Py_REFCNT(r), ", value:", value, @@ -1363,20 +1365,26 @@ struct CPython: self._inc_total_rc() return r - fn PyFloat_FromDouble(inout self, value: Float32) -> PyObjectPtr: - return self.PyFloat_FromDouble(value.cast[DType.float64]()) + # Py_ssize_t PyLong_AsSsize_t(PyObject *pylong) + # ref: https://docs.python.org/3/c-api/long.html#c.PyLong_AsSsize_t + fn PyLong_AsSsize_t(inout self, py_object: PyObjectPtr) -> c_ssize_t: + return self.lib.get_function[fn (PyObjectPtr) -> c_ssize_t]( + "PyLong_AsSsize_t" + )(py_object) - fn PyBool_FromLong(inout self, value: Bool) -> PyObjectPtr: - var long = 0 - if value: - long = 1 - var r = self.lib.get_function[fn (Int8) -> PyObjectPtr]( - "PyBool_FromLong" - )(Int8(long)) + # Floating-Point Objects + # ref: https://docs.python.org/3/c-api/float.html + + # PyObject *PyFloat_FromDouble(double v)¶ + # ref: https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble + fn PyFloat_FromDouble(inout self, value: Float64) -> PyObjectPtr: + r = self.lib.get_function[fn (Float64) -> PyObjectPtr]( + "PyFloat_FromDouble" + )(value) self.log( r._get_ptr_as_int(), - " NEWREF PyBool_FromLong, refcnt:", + " NEWREF PyFloat_FromDouble, refcnt:", self._Py_REFCNT(r), ", value:", value, @@ -1385,20 +1393,34 @@ struct CPython: self._inc_total_rc() return r - fn PyString_FromStringAndSize(inout self, strref: StringRef) -> PyObjectPtr: - var r = self.lib.get_function[ + # double PyFloat_AsDouble(PyObject *pyfloat) + # ref: https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble + fn PyFloat_AsDouble(inout self, py_object: PyObjectPtr) -> Float64: + return self.lib.get_function[fn (PyObjectPtr) -> Float64]( + "PyFloat_AsDouble" + )(py_object) + + # Unicode Objects + # https://docs.python.org/3/c-api/unicode.html + + # PyObject *PyUnicode_DecodeUTF8(const char *str, Py_ssize_t size, const char *errors) + # ref: https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8 + fn PyUnicode_DecodeUTF8(inout self, strref: StringRef) -> PyObjectPtr: + r = self.lib.get_function[ fn ( - UnsafePointer[UInt8], - Int, + UnsafePointer[c_char], + c_ssize_t, UnsafePointer[c_char], ) -> PyObjectPtr - ](StringRef("PyUnicode_DecodeUTF8"))( - strref.data, strref.length, "strict".unsafe_cstr_ptr() + ]("PyUnicode_DecodeUTF8")( + strref.data.bitcast[Int8](), + strref.length, + "strict".unsafe_cstr_ptr(), ) self.log( r._get_ptr_as_int(), - " NEWREF PyString_FromStringAndSize, refcnt:", + " NEWREF PyUnicode_DecodeUTF8, refcnt:", self._Py_REFCNT(r), ", str:", strref, @@ -1407,11 +1429,18 @@ struct CPython: self._inc_total_rc() return r + # const char *PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *size) + # ref: https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: - var result = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[Int]) -> UnsafePointer[c_char] - ]("PyUnicode_AsUTF8AndSize")(py_object, UnsafePointer[Int]()) - return StringRef(result) + s = StringRef() + s.data = self.lib.get_function[ + fn (PyObjectPtr, UnsafePointer[c_ssize_t]) -> UnsafePointer[c_char] + ]("PyUnicode_AsUTF8AndSize")( + py_object, UnsafePointer.address_of(s.length) + ).bitcast[ + UInt8 + ]() + return s # ===-------------------------------------------------------------------===# # Python Error operations diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index b713712678..3867df17ad 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -331,7 +331,7 @@ struct PythonObject( Args: none: None. """ - self = Self(none=NoneType(none)) + self = Self(none=NoneType()) fn __init__(inout self, none: NoneType): """Initialize a none value object from a `None` literal. @@ -339,27 +339,27 @@ struct PythonObject( Args: none: None. """ - var cpython = _get_global_python_itf().cpython() + cpython = _get_global_python_itf().cpython() self.py_object = cpython.Py_None() cpython.Py_IncRef(self.py_object) - fn __init__(inout self, integer: Int): - """Initialize the object with an integer value. + fn __init__(inout self, value: Bool): + """Initialize the object from a bool. Args: - integer: The integer value. + value: The boolean value. """ - var cpython = _get_global_python_itf().cpython() - self.py_object = cpython.toPython(integer) + cpython = _get_global_python_itf().cpython() + self.py_object = cpython.PyBool_FromLong(int(value)) - fn __init__(inout self, float: Float64): - """Initialize the object with an floating-point value. + fn __init__(inout self, integer: Int): + """Initialize the object with an integer value. Args: - float: The float value. + integer: The integer value. """ - var cpython = _get_global_python_itf().cpython() - self.py_object = cpython.PyFloat_FromDouble(float) + cpython = _get_global_python_itf().cpython() + self.py_object = cpython.PyLong_FromSsize_t(integer) fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): """Initialize the object with a generic scalar value. If the scalar @@ -372,27 +372,18 @@ struct PythonObject( Args: value: The scalar value. """ - var cpython = _get_global_python_itf().cpython() + cpython = _get_global_python_itf().cpython() @parameter - if dt == DType.bool: - self.py_object = cpython.toPython(value.__bool__()) + if dt is DType.bool: + self.py_object = cpython.PyBool_FromLong(int(value)) elif dt.is_integral(): - var int_val = value.cast[DType.index]().value - self.py_object = cpython.toPython(int_val) + int_val = value.cast[DType.index]().value + self.py_object = cpython.PyLong_FromSsize_t(int_val) else: - var fp_val = value.cast[DType.float64]() + fp_val = value.cast[DType.float64]() self.py_object = cpython.PyFloat_FromDouble(fp_val) - fn __init__(inout self, value: Bool): - """Initialize the object from a bool. - - Args: - value: The boolean value. - """ - var cpython = _get_global_python_itf().cpython() - self.py_object = cpython.toPython(value) - fn __init__(inout self, value: StringLiteral): """Initialize the object from a string literal. @@ -407,8 +398,8 @@ struct PythonObject( Args: strref: The string reference. """ - var cpython = _get_global_python_itf().cpython() - self.py_object = cpython.toPython(strref) + cpython = _get_global_python_itf().cpython() + self.py_object = cpython.PyUnicode_DecodeUTF8(strref) fn __init__(inout self, string: String): """Initialize the object from a string. @@ -416,8 +407,10 @@ struct PythonObject( Args: string: The string value. """ - var cpython = _get_global_python_itf().cpython() - self.py_object = cpython.toPython(string._strref_dangerous()) + cpython = _get_global_python_itf().cpython() + self.py_object = cpython.PyUnicode_DecodeUTF8( + string._strref_dangerous() + ) string._strref_keepalive() fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): @@ -1364,7 +1357,9 @@ struct PythonObject( var dict_obj = cpython.PyDict_New() for entry in kwargs.items(): - var key = cpython.toPython(entry[].key._strref_dangerous()) + var key = cpython.PyUnicode_DecodeUTF8( + entry[].key._strref_dangerous() + ) var result = cpython.PyDict_SetItem( dict_obj, key, entry[].value.py_object ) @@ -1389,15 +1384,6 @@ struct PythonObject( ) return PythonObject(result) - fn to_float64(self) -> Float64: - """Returns a float representation of the object. - - Returns: - A floating point value that represents this object. - """ - var cpython = _get_global_python_itf().cpython() - return cpython.PyFloat_AsDouble(self.py_object.value) - fn __index__(self) -> Int: """Returns an index representation of the object. @@ -1412,8 +1398,26 @@ struct PythonObject( Returns: An integral value that represents this object. """ - var cpython = _get_global_python_itf().cpython() - return cpython.PyLong_AsLong(self.py_object.value) + cpython = _get_global_python_itf().cpython() + return cpython.PyLong_AsSsize_t(self.py_object) + + fn __float__(self) -> Float64: + """Returns a float representation of the object. + + Returns: + A floating point value that represents this object. + """ + cpython = _get_global_python_itf().cpython() + return cpython.PyFloat_AsDouble(self.py_object) + + @deprecated("Use `float(obj)` instead.") + fn to_float64(self) -> Float64: + """Returns a float representation of the object. + + Returns: + A floating point value that represents this object. + """ + return self.__float__() fn unsafe_get_as_pointer[type: DType](self) -> UnsafePointer[Scalar[type]]: """Convert a Python-owned and managed pointer into a Mojo pointer. From d7ce9c0514ef752b82960028b792accc79fa7cca Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Fri, 11 Oct 2024 16:30:36 -0600 Subject: [PATCH 1729/2019] [External] [stdlib] Implement _HashableWithHasher trait on all types which implement __hash__ method (#48993) [External] [stdlib] Implement _HashableWithHasher trait on all types which implement __hash__ method To allow for wider adoption of `AHash` in the future, implement `_HashableWithHasher` trait on all types in the standard library that currently implement `__hash__`. Co-authored-by: Maxim Zaks Closes modularml/mojo#3615 MODULAR_ORIG_COMMIT_REV_ID: 7145b3230d04ef515c78d7a2b99dc0e1efd58034 --- stdlib/benchmarks/hashlib/bench_hash.mojo | 6 +- stdlib/src/builtin/dtype.mojo | 19 ++++++- stdlib/src/builtin/int.mojo | 13 +++++ stdlib/src/builtin/simd.mojo | 13 +++++ stdlib/src/builtin/string_literal.mojo | 13 +++++ stdlib/src/builtin/uint.mojo | 14 ++++- stdlib/src/collections/string.mojo | 14 +++++ stdlib/src/hashlib/_ahash.mojo | 43 +++----------- stdlib/src/hashlib/_hasher.mojo | 15 ++++- stdlib/src/pathlib/path.mojo | 13 +++++ stdlib/src/python/python_object.mojo | 18 ++++++ stdlib/src/utils/stringref.mojo | 13 +++++ stdlib/test/hashlib/test_ahash.mojo | 68 ++++++++++------------- stdlib/test/hashlib/test_hasher.mojo | 37 ++++++++---- 14 files changed, 210 insertions(+), 89 deletions(-) diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index 5f779ee1a2..d672da085d 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -19,7 +19,6 @@ from memory import UnsafePointer from hashlib.hash import hash as old_hash from hashlib._ahash import ( AHasher, - hash as ahash, _folded_multiply, _read_small, U256, @@ -27,6 +26,7 @@ from hashlib._ahash import ( MULTIPLE, ROT, ) +from hashlib._hasher import _hash_with_hasher # Source: https://www.101languages.net/arabic/most-common-arabic-words/ alias words_ar = """ @@ -618,7 +618,7 @@ fn bench_small_keys_new_hash_function[s: String](inout b: Bencher) raises: @parameter fn call_fn(): for w in words: - var h = ahash(w[].unsafe_ptr(), w[].byte_length()) + var h = _hash_with_hasher(w[].unsafe_ptr(), w[].byte_length()) benchmark.keep(h) b.iter[call_fn]() @@ -640,7 +640,7 @@ fn bench_long_key_new_hash_function[s: String](inout b: Bencher) raises: @always_inline @parameter fn call_fn(): - var h = ahash(s.unsafe_ptr(), s.byte_length()) + var h = _hash_with_hasher(s.unsafe_ptr(), s.byte_length()) benchmark.keep(h) b.iter[call_fn]() diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 68d045c3f4..53150b739b 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import KeyElement +from hashlib._hasher import _HashableWithHasher, _Hasher from sys import sizeof, bitwidthof, os_is_windows alias _mIsSigned = UInt8(1) @@ -27,7 +28,12 @@ alias _mIsFloat = UInt8(1 << 6) @value @register_passable("trivial") struct DType( - Stringable, Formattable, Representable, KeyElement, CollectionElementNew + Stringable, + Formattable, + Representable, + KeyElement, + CollectionElementNew, + _HashableWithHasher, ): """Represents DType and provides methods for working with it.""" @@ -298,6 +304,17 @@ struct DType( """ return hash(UInt8(self._as_i8())) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with this `DType` value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_simd(UInt8(self._as_i8())) + @always_inline("nodebug") fn is_unsigned(self) -> Bool: """Returns True if the type parameter is unsigned and False otherwise. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 2eca4a83ce..2015842335 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -20,6 +20,7 @@ from collections import KeyElement from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from hashlib.hash import _hash_simd +from hashlib._hasher import _HashableWithHasher, _Hasher from builtin.io import _snprintf from collections.string import ( _calc_initial_buffer_size_int32, @@ -286,6 +287,7 @@ struct Int( KeyElement, Roundable, IntLike, + _HashableWithHasher, ): """This type represents an integer value.""" @@ -1112,6 +1114,17 @@ struct Int( # TODO(MOCO-636): switch to DType.index return _hash_simd(Scalar[DType.int64](self)) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with this int value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_simd(Int64(self)) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index cb5fc5ae55..c2f22b3074 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -38,6 +38,7 @@ from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from hashlib.hash import _hash_simd +from hashlib._hasher import _HashableWithHasher, _Hasher from builtin.format_int import _try_write_int from collections import InlineArray from memory import bitcast, UnsafePointer @@ -173,6 +174,7 @@ struct SIMD[type: DType, size: Int]( Floorable, Formattable, Hashable, + _HashableWithHasher, Intable, Powable, Representable, @@ -1536,6 +1538,17 @@ struct SIMD[type: DType, size: Int]( """ return _hash_simd(self) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with this SIMD value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_simd(self) + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index e86946496e..65d05f6e25 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -19,6 +19,7 @@ from sys.ffi import c_char from memory import memcpy, UnsafePointer from collections import List +from hashlib._hasher import _HashableWithHasher, _Hasher from utils import StringRef, Span, StringSlice, StaticString from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type @@ -43,6 +44,7 @@ struct StringLiteral( Sized, Stringable, FloatableRaising, + _HashableWithHasher, ): """This type represents a string literal. @@ -269,6 +271,17 @@ struct StringLiteral( """ return hash(self.unsafe_ptr(), len(self)) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with the underlying bytes. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_bytes(self.unsafe_ptr(), self.byte_length()) + fn __fspath__(self) -> String: """Return the file system path representation of the object. diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index d76d9b0eec..43bbc78433 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -19,12 +19,13 @@ from sys import bitwidthof from utils._visualizers import lldb_formatter_wrapping_type from builtin._documentation import doc_private from hashlib.hash import _hash_simd +from hashlib._hasher import _HashableWithHasher, _Hasher @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct UInt(IntLike): +struct UInt(IntLike, _HashableWithHasher): """This type represents an unsigned integer. An unsigned integer is represents a positive integral number. @@ -158,6 +159,17 @@ struct UInt(IntLike): # TODO(MOCO-636): switch to DType.index return _hash_simd(Scalar[DType.uint64](self)) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with this uint value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_simd(UInt64(self)) + @always_inline("nodebug") fn __eq__(self, rhs: UInt) -> Bool: """Compare this UInt to the RHS using EQ comparison. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 75cea7438f..a89f754bf1 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -24,6 +24,8 @@ from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy from python import PythonObject +from hashlib._hasher import _HashableWithHasher, _Hasher + from utils import ( Span, IndexList, @@ -697,6 +699,7 @@ struct String( ToFormatter, CollectionElementNew, FloatableRaising, + _HashableWithHasher, ): """Represents a mutable string.""" @@ -1819,6 +1822,17 @@ struct String( """ return hash(self._strref_dangerous()) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with the underlying bytes. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_bytes(self.unsafe_ptr(), self.byte_length()) + fn _interleave(self, val: String) -> String: var res = Self._buffer_type() var val_ptr = val.unsafe_ptr() diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index a5d3839022..ad71de0899 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -73,6 +73,14 @@ fn _read_small(data: UnsafePointer[UInt8], length: Int) -> U128: struct AHasher[key: U256](_Hasher): + """Adopted AHash algorithm which produces fast and high quality hash value by + implementing `_Hasher` trait. + + References: + + - [AHasher Implementation in Rust](https://github.com/tkaitchuck/aHash) + """ + var buffer: UInt64 var pad: UInt64 var extra_keys: U128 @@ -179,38 +187,3 @@ struct AHasher[key: U256](_Hasher): var rot = self.buffer & 63 var folded = _folded_multiply(self.buffer, self.pad) return (folded << rot) | (folded >> (64 - rot)) - - -fn hash[ - key: U256 = U256(0, 0, 0, 0) -](bytes: UnsafePointer[UInt8], n: Int) -> UInt64: - """Hash a byte array using an adopted AHash algorithm. - - References: - - - [Pointer Implementation in Rust](https://github.com/tkaitchuck/aHash) - - ```mojo - from random import rand - var n = 64 - var rand_bytes = UnsafePointer[UInt8].alloc(n) - rand(rand_bytes, n) - _ = hash(rand_bytes, n) - ``` - - Parameters: - key: A key to modify the result of the hash function, defaults to [0, 0, 0, 0]. - - Args: - bytes: The byte array to hash. - n: The length of the byte array. - - Returns: - A 64-bit integer hash. This hash is _not_ suitable for - cryptographic purposes, but will have good low-bit - hash collision statistical properties for common data structures. - """ - - var hasher = AHasher[key]() - hasher._update_with_bytes(bytes, n) - return hasher^.finish() diff --git a/stdlib/src/hashlib/_hasher.mojo b/stdlib/src/hashlib/_hasher.mojo index f69f080fa7..505ae4fc0a 100644 --- a/stdlib/src/hashlib/_hasher.mojo +++ b/stdlib/src/hashlib/_hasher.mojo @@ -11,6 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from ._ahash import AHasher from memory import UnsafePointer @@ -36,10 +37,22 @@ trait _Hasher: ... +alias default_hasher = AHasher[SIMD[DType.uint64, 4](0, 0, 0, 0)] + + fn _hash_with_hasher[ - HasherType: _Hasher, HashableType: _HashableWithHasher + HashableType: _HashableWithHasher, HasherType: _Hasher = default_hasher ](hashable: HashableType) -> UInt64: var hasher = HasherType() hasher.update(hashable) var value = hasher^.finish() return value + + +fn _hash_with_hasher[ + HasherType: _Hasher = default_hasher +](data: UnsafePointer[UInt8], len: Int) -> UInt64: + var hasher = HasherType() + hasher._update_with_bytes(data, len) + var value = hasher^.finish() + return value diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index bbfd67bfe6..49faa36f49 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -22,6 +22,8 @@ from sys.ffi import c_char from builtin._location import __call_location, _SourceLocation from memory import stack_allocation, UnsafePointer +from hashlib._hasher import _HashableWithHasher, _Hasher + from utils import StringRef alias DIR_SEPARATOR = "\\" if os_is_windows() else "/" @@ -236,6 +238,17 @@ struct Path( return hash(self.path) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with the path string value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher.update(self.path) + fn stat(self) raises -> stat_result: """Returns the stat information on the path. diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 3867df17ad..fc60daeaed 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -25,6 +25,8 @@ from memory import UnsafePointer from collections import Dict from utils import StringRef +from hashlib._hasher import _HashableWithHasher, _Hasher + from ._cpython import CPython, PyObjectPtr from .python import Python, _get_global_python_itf @@ -231,6 +233,7 @@ struct PythonObject( SizedRaising, Stringable, Formattable, + _HashableWithHasher, ): """A Python object.""" @@ -685,6 +688,21 @@ struct PythonObject( debug_assert(result != -1, "object is not hashable") return result + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with this python object hash value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + var cpython = _get_global_python_itf().cpython() + var result = cpython.PyObject_Hash(self.py_object) + # TODO: make this function raise when we can raise parametrically. + debug_assert(result != -1, "object is not hashable") + hasher.update(result) + fn __getitem__(self, *args: PythonObject) raises -> PythonObject: """Return the value for the given key or keys. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 065c820caa..484b58a12a 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,6 +16,7 @@ from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from collections.string import _atol, _isspace +from hashlib._hasher import _HashableWithHasher, _Hasher from memory import UnsafePointer, memcmp, bitcast from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice @@ -47,6 +48,7 @@ struct StringRef( Stringable, Formattable, Hashable, + _HashableWithHasher, Boolable, Comparable, ): @@ -341,6 +343,17 @@ struct StringRef( """ return hash(self.data, self.length) + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with the underlying bytes. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + hasher._update_with_bytes(self.data, self.length) + fn __int__(self) raises -> Int: """Parses the given string as a base-10 integer and returns that value. diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index fad0dd07a1..27d27a1b1e 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -13,11 +13,13 @@ # RUN: %mojo %s from bit import pop_count -from hashlib._ahash import hash, AHasher +from hashlib._ahash import AHasher from hashlib.hash import hash as old_hash +from hashlib._hasher import _hash_with_hasher as hash from testing import assert_equal, assert_not_equal, assert_true from memory import memset_zero, UnsafePointer from time import now +from utils import Span # Source: https://www.101languages.net/arabic/most-common-arabic-words/ alias words_ar = """ @@ -593,49 +595,38 @@ def assert_dif_hashes(hashes: List[UInt64], upper_bound: Int): for j in range(i + 1, len(hashes)): var diff = dif_bits(hashes[i], hashes[j]) assert_true( - diff > 14, + diff > upper_bound, str("Index: {}:{}, diff between: {} and {} is: {}").format( i, j, hashes[i], hashes[j], diff ), ) +alias hasher0 = AHasher[SIMD[DType.uint64, 4](0, 0, 0, 0)] +alias hasher1 = AHasher[SIMD[DType.uint64, 4](1, 0, 0, 0)] + + def test_hash_byte_array(): - assert_equal(hash("a".unsafe_ptr(), 1), hash("a".unsafe_ptr(), 1)) - assert_equal( - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("a".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("a".unsafe_ptr(), 1), - ) + assert_equal(hash[HasherType=hasher0]("a"), hash[HasherType=hasher0]("a")) + assert_equal(hash[HasherType=hasher1]("a"), hash[HasherType=hasher1]("a")) assert_not_equal( - hash("a".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("a".unsafe_ptr(), 1), - ) - assert_equal(hash("b".unsafe_ptr(), 1), hash("b".unsafe_ptr(), 1)) - assert_equal( - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("b".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("b".unsafe_ptr(), 1), + hash[HasherType=hasher0]("a"), hash[HasherType=hasher1]("a") ) + assert_equal(hash[HasherType=hasher0]("b"), hash[HasherType=hasher0]("b")) + assert_equal(hash[HasherType=hasher1]("b"), hash[HasherType=hasher1]("b")) assert_not_equal( - hash("b".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("b".unsafe_ptr(), 1), - ) - assert_equal(hash("c".unsafe_ptr(), 1), hash("c".unsafe_ptr(), 1)) - assert_equal( - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("c".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("c".unsafe_ptr(), 1), + hash[HasherType=hasher0]("b"), hash[HasherType=hasher1]("b") ) + assert_equal(hash[HasherType=hasher0]("c"), hash[HasherType=hasher0]("c")) + assert_equal(hash[HasherType=hasher1]("c"), hash[HasherType=hasher1]("c")) assert_not_equal( - hash("c".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("c".unsafe_ptr(), 1), - ) - assert_equal(hash("d".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1)) - assert_equal( - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("d".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("d".unsafe_ptr(), 1), + hash[HasherType=hasher0]("c"), hash[HasherType=hasher1]("c") ) + assert_equal(hash[HasherType=hasher0]("d"), hash[HasherType=hasher0]("d")) + assert_equal(hash[HasherType=hasher1]("d"), hash[HasherType=hasher1]("d")) assert_not_equal( - hash("d".unsafe_ptr(), 1), - hash[SIMD[DType.uint64, 4](1, 0, 0, 0)]("d".unsafe_ptr(), 1), + hash[HasherType=hasher0]("d"), + hash[HasherType=hasher1]("d"), ) @@ -646,15 +637,15 @@ def test_avalanche(): memset_zero(data, 256) var hashes0 = List[UInt64]() var hashes1 = List[UInt64]() - hashes0.append(hash(data, 256)) - hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, 256)) + hashes0.append(hash[HasherType=hasher0](data, 256)) + hashes1.append(hash[HasherType=hasher1](data, 256)) for i in range(256): memset_zero(data, 256) var v = 1 << (i & 7) data[i >> 3] = v - hashes0.append(hash(data, 256)) - hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, 256)) + hashes0.append(hash[HasherType=hasher0](data, 256)) + hashes1.append(hash[HasherType=hasher1](data, 256)) for i in range(len(hashes0)): var diff = dif_bits(hashes0[i], hashes1[i]) @@ -673,17 +664,18 @@ def test_trailing_zeros(): # checks that a value with different amount of trailing zeros, # results in significantly different hash values var data = UnsafePointer[UInt8].alloc(8) + memset_zero(data, 8) data[0] = 23 var hashes0 = List[UInt64]() var hashes1 = List[UInt64]() for i in range(1, 9): - hashes0.append(hash(data, i)) - hashes1.append(hash[SIMD[DType.uint64, 4](0, 1, 0, 0)](data, i)) + hashes0.append(hash[HasherType=hasher0](data, i)) + hashes1.append(hash[HasherType=hasher1](data, i)) for i in range(len(hashes0)): var diff = dif_bits(hashes0[i], hashes1[i]) assert_true( - diff > 19, + diff > 18, str("Index: {}, diff between: {} and {} is: {}").format( i, hashes0[i], hashes1[i], diff ), @@ -700,7 +692,7 @@ def assert_fill_factor[ # and the fill factor results in 1.0 var buckets = List[Int](0) * num_buckets for w in words: - var h = hash(w[].unsafe_ptr(), w[].byte_length()) + var h = hash[HasherType=hasher0](w[]) buckets[int(h) % num_buckets] += 1 var unfilled = 0 for v in buckets: diff --git a/stdlib/test/hashlib/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo index 2052aebbb7..0ae9bdb662 100644 --- a/stdlib/test/hashlib/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -16,7 +16,10 @@ from hashlib._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher from hashlib._ahash import AHasher from memory import UnsafePointer -from testing import assert_equal +from pathlib import Path +from python import Python, PythonObject +from testing import assert_equal, assert_true +from utils import StringRef struct DummyHasher(_Hasher): @@ -56,7 +59,7 @@ def test_hasher(): def test_hash_with_hasher(): var hashable = SomeHashableStruct(10) - assert_equal(_hash_with_hasher[DummyHasher](hashable), 10) + assert_equal(_hash_with_hasher[HasherType=DummyHasher](hashable), 10) @value @@ -82,7 +85,7 @@ def test_complexe_hash_with_hasher(): var hashable = ComplexeHashableStruct( SomeHashableStruct(42), SomeHashableStruct(10) ) - assert_equal(_hash_with_hasher[DummyHasher](hashable), 52) + assert_equal(_hash_with_hasher[HasherType=DummyHasher](hashable), 52) @value @@ -119,7 +122,7 @@ struct ComplexHashableStructWithListAndWideSIMD(_HashableWithHasher): data=self._value3.unsafe_ptr(), length=len(self._value3), ) - hasher._update_with_simd(self._value4) + hasher.update(self._value4) _ = self._value3 @@ -136,9 +139,7 @@ def test_with_ahasher(): var hashable1 = ComplexHashableStructWithList( SomeHashableStruct(42), SomeHashableStruct(10), List[UInt8](1, 2, 3) ) - var hash_value = _hash_with_hasher[ - AHasher[SIMD[DType.uint64, 4](0, 0, 0, 0)] - ](hashable1) + var hash_value = _hash_with_hasher(hashable1) assert_equal(hash_value, 12427888534629009331) var hashable2 = ComplexHashableStructWithListAndWideSIMD( SomeHashableStruct(42), @@ -146,12 +147,27 @@ def test_with_ahasher(): List[UInt8](1, 2, 3), SIMD[DType.uint32, 4](1, 2, 3, 4), ) - hash_value = _hash_with_hasher[AHasher[SIMD[DType.uint64, 4](0, 0, 0, 0)]]( - hashable2 - ) + hash_value = _hash_with_hasher(hashable2) assert_equal(hash_value, 9463003097190363949) +def test_hash_hashable_with_hasher_types(): + assert_equal(_hash_with_hasher(DType.uint64), 5919096275431609211) + assert_equal(_hash_with_hasher(""), 12914568033466041247) + assert_equal(_hash_with_hasher(str("")), 12914568033466041247) + assert_equal(_hash_with_hasher(StringRef("")), 12914568033466041247) + assert_equal(_hash_with_hasher(Int(-123)), 7309790389124252133) + assert_equal(_hash_with_hasher(UInt(123)), 11416101997646518198) + assert_equal( + _hash_with_hasher(SIMD[DType.float16, 4](0.1, -0.1, 12, 0)), + 236488340994185196, + ) + assert_equal(_hash_with_hasher(Path("/tmp")), 862170317972693446) + # Hash value of PythonObject is randomized by default + # can be deterministic if env var PYTHONHASHSEED is set + assert_true(_hash_with_hasher(PythonObject("hello")) != 0) + + def main(): test_hasher() test_hash_with_hasher() @@ -159,3 +175,4 @@ def main(): test_complexe_hash_with_hasher() test_update_with_bytes() test_with_ahasher() + test_hash_hashable_with_hasher_types() From 0045298e01b909b26e2cb489d0dedfb6bf9d4be2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 16:25:43 -0700 Subject: [PATCH 1730/2019] [mojo-stdlib] Remove a bunch of unneeded moveinit stuff. This is all noticed by inspection: many moveinits can be removed by adopting the `@value` decorator. Also, there is no need to tidy up the "existing" object since its dtor isn't run. MODULAR_ORIG_COMMIT_REV_ID: b7850438347991127f2d653e441e8be3df5b1ff9 --- stdlib/src/collections/optional.mojo | 17 +---------------- stdlib/src/collections/string.mojo | 19 +------------------ stdlib/src/pathlib/path.mojo | 18 +----------------- 3 files changed, 3 insertions(+), 51 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 5f6ac18f69..1742159cc4 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -47,6 +47,7 @@ struct _NoneType(CollectionElement, CollectionElementNew): # ===----------------------------------------------------------------------===# +@value struct Optional[T: CollectionElement]( CollectionElement, CollectionElementNew, Boolable ): @@ -126,22 +127,6 @@ struct Optional[T: CollectionElement]( """ self.__copyinit__(other) - fn __copyinit__(inout self, other: Self): - """Copy construct an Optional. - - Args: - other: The Optional to copy. - """ - self._value = other._value - - fn __moveinit__(inout self, owned other: Self): - """Move this `Optional`. - - Args: - other: The `Optional` to move from. - """ - self._value = other._value^ - # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index a89f754bf1..ef9cd5940c 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -687,6 +687,7 @@ fn isprintable(c: UInt8) -> Bool: # ===----------------------------------------------------------------------=== # +@value struct String( Sized, Stringable, @@ -831,24 +832,6 @@ struct String( ) ) - @always_inline - fn __copyinit__(inout self, existing: Self): - """Creates a deep copy of an existing string. - - Args: - existing: The string to copy. - """ - self._buffer = existing._buffer - - @always_inline - fn __moveinit__(inout self, owned existing: String): - """Move the value of a string. - - Args: - existing: The string to move. - """ - self._buffer = existing._buffer^ - # ===------------------------------------------------------------------=== # # Factory dunders # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 49faa36f49..7c7f4eb5ba 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -65,6 +65,7 @@ fn _dir_of_current_file_impl(file_name: StringLiteral) raises -> Path: return Path(str(file_name)[0:i]) +@value struct Path( Stringable, Boolable, @@ -99,23 +100,6 @@ struct Path( """ self.path = String(other=other.path) - fn __moveinit__(inout self, owned existing: Self): - """Move data of an existing Path into a new one. - - Args: - existing: The existing Path. - """ - self.path = existing.path^ - - @always_inline - fn __copyinit__(inout self, existing: Self): - """Copy constructor for the path struct. - - Args: - existing: The existing struct to copy from. - """ - self.path = existing.path - fn __truediv__(self, suffix: Self) -> Self: """Joins two paths using the system-defined path separator. From 4aeab74ccd9db600f94d22409d9b6a388d98874b Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 11 Oct 2024 16:42:07 -0700 Subject: [PATCH 1731/2019] Add warning about Python dependencies in Mojo package `mojo build` does not bundle the Python dependencies, so they must be provided where you run the program. MODULAR_ORIG_COMMIT_REV_ID: 44279eec34e52db3a92ce2141908c2be22e6fd58 --- docs/manual/python/index.ipynb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 0a292153ff..3058ef0d98 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -126,12 +126,15 @@ " code than in the Mojo standard library, which \n", " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", "\n", - ":::note\n", "\n", - "Mojo loads the Python interpreter and Python modules at runtime, so\n", - "wherever you run a Mojo program, it must be able to access a compatible Python\n", - "interpreter, and to locate any imported Python modules. For more information,\n", - "see [Create a Python environment](#create-a-python-environment).\n", + ":::caution\n", + "\n", + "[`mojo build`](/mojo/cli/build) doesn't include the Python packages used by\n", + "your Mojo project. Instead, Mojo loads the Python interpreter and Python\n", + "packages at runtime, so they must be provided in the environment where you run\n", + "the Mojo program (such as inside the Magic environment where you built the\n", + "executable). For more information, see the section above to [create a Python\n", + "environment](#create-a-python-environment).\n", "\n", ":::" ] From 55fccec20325c568e974dc67296192bb89ab3f14 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 17:43:20 -0700 Subject: [PATCH 1732/2019] [mojo-lang] Fix exclusivity checking with reference downcasts. This changes the exclusivity checker to look through reference downcasts for function calls, allowing it to notice the original lifetimes of arguments that explicitly pass as AnyLifetime. This fixes https://github.com/modularml/mojo/issues/3627 MODULAR_ORIG_COMMIT_REV_ID: 45251b67f03bf03fb0bf798146481373cf6c9acd --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 2ab8fade35..b627da7964 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -280,6 +280,9 @@ what we publish. - [Issue #3559](https://github.com/modularml/mojo/issues/3559) - VariadicPack doesn't extend the lifetimes of the values it references. +- [Issue #3627](https://github.com/modularml/mojo/issues/3627) - Compiler + overlooked exclusivity violation caused by `ref [MutableAnyLifetime] T` + - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. From f6d0f9c06fb5298c3171bfab4a8956a87edae5a1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 18:55:15 -0700 Subject: [PATCH 1733/2019] [mojo-stdlib] Rename `Lifetime` aliases to `Origin` Per discussion on this thread, the name `Origin` is more clear and specific than the word `Lifetime` for describing the purpose of these types. MODULAR_ORIG_COMMIT_REV_ID: c3b1e2913687820e849c25e6eac828afed5075d6 --- docs/changelog.md | 6 ++ stdlib/src/builtin/builtin_list.mojo | 16 ++--- stdlib/src/builtin/coroutine.mojo | 4 +- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/io.mojo | 4 +- stdlib/src/builtin/reversed.mojo | 4 +- stdlib/src/builtin/sort.mojo | 70 +++++++++---------- stdlib/src/builtin/string_literal.mojo | 12 ++-- stdlib/src/builtin/type_aliases.mojo | 24 +++---- stdlib/src/collections/dict.mojo | 6 +- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/memory/memory.mojo | 4 +- stdlib/src/memory/pointer.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 10 +-- stdlib/src/prelude/__init__.mojo | 14 ++-- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/span.mojo | 8 +-- stdlib/src/utils/string_slice.mojo | 8 +-- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- 22 files changed, 106 insertions(+), 100 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b627da7964..030166912e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -264,6 +264,12 @@ what we publish. - `String.as_bytes()` now returns a `Span[UInt8]` instead of a `List[Int8]`. The old behavior can be achieved by using `List(s.as_bytes())`. +- `Lifetime` and related types has been renamed to `Origin` in the standard + library to better clarify that parameters of this type indicate where a + reference is derived from, not the more complicated notion of where a variable + is initialized and destroyed. Please see [the proposal](https://github.com/modularml/mojo/blob/main/proposals/lifetimes-keyword-renaming.md) + for more information and rationale. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index bce38fe79f..ccaf7a4fe5 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -218,8 +218,8 @@ struct VariadicList[type: AnyTrivialRegType](Sized): struct _VariadicListMemIter[ elt_is_mutable: Bool, //, elt_type: AnyType, - elt_lifetime: Lifetime[elt_is_mutable].type, - list_lifetime: ImmutableLifetime, + elt_lifetime: Origin[elt_is_mutable].type, + list_lifetime: ImmutableOrigin, ]: """Iterator for VariadicListMem. @@ -261,8 +261,8 @@ struct _VariadicListMemIter[ # TODO: parametric aliases would be nice. struct _lit_lifetime_union[ is_mutable: Bool, //, - a: Lifetime[is_mutable].type, - b: Lifetime[is_mutable].type, + a: Origin[is_mutable].type, + b: Origin[is_mutable].type, ]: alias result = __mlir_attr[ `#lit.lifetime.union<`, @@ -277,7 +277,7 @@ struct _lit_lifetime_union[ struct _lit_mut_cast[ is_mutable: Bool, //, - operand: Lifetime[is_mutable].type, + operand: Origin[is_mutable].type, result_mutable: Bool, ]: alias result = __mlir_attr[ @@ -292,7 +292,7 @@ struct _lit_mut_cast[ struct VariadicListMem[ elt_is_mutable: Bool, //, element_type: AnyType, - lifetime: Lifetime[elt_is_mutable].type, + lifetime: Origin[elt_is_mutable].type, ](Sized): """A utility class to access variadic function arguments of memory-only types that may have ownership. It exposes references to the elements in a @@ -473,7 +473,7 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @value struct _LITRefPackHelper[ is_mutable: Bool, //, - lifetime: Lifetime[is_mutable].type, + lifetime: Origin[is_mutable].type, address_space: __mlir_type.index, element_trait: _AnyTypeMetaType, *element_types: element_trait, @@ -543,7 +543,7 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, //, - lifetime: Lifetime[Bool {value: elt_is_mutable}].type, + lifetime: Origin[Bool {value: elt_is_mutable}].type, element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 36be7710d1..d49cd441ed 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -79,7 +79,7 @@ fn _coro_resume_noop_callback(null: AnyCoroutine): @register_passable -struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: +struct Coroutine[type: AnyType, lifetimes: OriginSet]: """Represents a coroutine. Coroutines can pause execution saving the state of the program (including @@ -158,7 +158,7 @@ struct Coroutine[type: AnyType, lifetimes: LifetimeSet]: @register_passable -struct RaisingCoroutine[type: AnyType, lifetimes: LifetimeSet]: +struct RaisingCoroutine[type: AnyType, lifetimes: OriginSet]: """Represents a coroutine that can raise. Coroutines can pause execution saving the state of the program (including diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 57a60576a2..ed4aeb8831 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -321,7 +321,7 @@ fn _try_write_int[ # Construct a null-terminated buffer of single-byte char. var zero_buf = InlineArray[UInt8, 2](zero_char, 0) - var zero = StringSlice[ImmutableAnyLifetime]( + var zero = StringSlice[ImmutableAnyOrigin]( # TODO(MSTDL-720): # Support printing non-null-terminated strings on GPU and switch # back to this code without a workaround. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 9ee0e84e81..33231b7909 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -290,7 +290,7 @@ fn _float_repr[ fn _put(strref: StringRef, file: FileDescriptor = stdout): - var str_slice = StringSlice[ImmutableAnyLifetime]( + var str_slice = StringSlice[ImmutableAnyOrigin]( unsafe_from_utf8_strref=strref ) @@ -299,7 +299,7 @@ fn _put(strref: StringRef, file: FileDescriptor = stdout): @no_inline fn _put[ - lif: ImmutableLifetime, // + lif: ImmutableOrigin, // ](x: StringSlice[lif], file: FileDescriptor = stdout): # Avoid printing "(null)" for an empty/default constructed `String` var str_len = x.byte_length() diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 3517726d9e..0edfc493e9 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -122,7 +122,7 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_lifetime: Lifetime[dict_mutability].type, + dict_lifetime: Origin[dict_mutability].type, ](ref [_]value: _DictValueIter[K, V, dict_lifetime]) -> _DictValueIter[ K, V, dict_lifetime, False ]: @@ -149,7 +149,7 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_lifetime: Lifetime[dict_mutability].type, + dict_lifetime: Origin[dict_mutability].type, ](ref [_]value: _DictEntryIter[K, V, dict_lifetime]) -> _DictEntryIter[ K, V, dict_lifetime, False ]: diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 8a2001e846..813a56ed55 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -41,7 +41,7 @@ struct _SortWrapper[type: CollectionElement](CollectionElement): @always_inline fn _insertion_sort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): """Sort the array[start:end] slice""" @@ -66,7 +66,7 @@ fn _insertion_sort[ @always_inline fn _quicksort_partition_right[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]) -> Int: var array = span.unsafe_ptr() @@ -95,7 +95,7 @@ fn _quicksort_partition_right[ @always_inline fn _quicksort_partition_left[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]) -> Int: var array = span.unsafe_ptr() @@ -121,7 +121,7 @@ fn _quicksort_partition_left[ fn _heap_sort_fix_down[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime], idx: Int): var array = span.unsafe_ptr() @@ -142,7 +142,7 @@ fn _heap_sort_fix_down[ @always_inline fn _heap_sort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var array = span.unsafe_ptr() @@ -171,7 +171,7 @@ fn _estimate_initial_height(size: Int) -> Int: @always_inline fn _delegate_small_sort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var array = span.unsafe_ptr() @@ -203,7 +203,7 @@ fn _delegate_small_sort[ @always_inline fn _quicksort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var array = span.unsafe_ptr() @@ -264,8 +264,8 @@ fn _quicksort[ fn _merge[ type: CollectionElement, - span_lifetime: ImmutableLifetime, - result_lifetime: MutableLifetime, //, + span_lifetime: ImmutableOrigin, + result_lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ]( span1: Span[type, span_lifetime], @@ -279,8 +279,8 @@ fn _merge[ Parameters: type: Type of the spans. - span_lifetime: Lifetime of the input spans. - result_lifetime: Lifetime of the result Span. + span_lifetime: Origin of the input spans. + result_lifetime: Origin of the result Span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -322,8 +322,8 @@ fn _merge[ fn _stable_sort_impl[ type: CollectionElement, - span_life: MutableLifetime, - tmp_life: MutableLifetime, //, + span_life: MutableOrigin, + tmp_life: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, span_life], temp_buff: Span[type, tmp_life]): var size = len(span) @@ -352,7 +352,7 @@ fn _stable_sort_impl[ fn _stable_sort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var temp_buff = UnsafePointer[type].alloc(len(span)) @@ -371,7 +371,7 @@ fn _stable_sort[ @always_inline fn _partition[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]) -> Int: var size = len(span) @@ -404,7 +404,7 @@ fn _partition[ fn _partition[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](owned span: Span[type, lifetime], owned k: Int): while True: @@ -422,7 +422,7 @@ fn _partition[ fn partition[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (type, type) capturing [_] -> Bool, ](span: Span[type, lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the @@ -431,7 +431,7 @@ fn partition[ Parameters: type: Type of the underlying data. - lifetime: Lifetime of span. + lifetime: Origin of span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -447,7 +447,7 @@ fn partition[ fn partition[ - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (Int, Int) capturing [_] -> Bool, ](span: Span[Int, lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the @@ -455,7 +455,7 @@ fn partition[ The ordering of the first k elements is undefined. Parameters: - lifetime: Lifetime of span. + lifetime: Origin of span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -472,7 +472,7 @@ fn partition[ fn partition[ type: DType, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing [_] -> Bool, ](span: Span[Scalar[type], lifetime], k: Int): """Partition the input buffer inplace such that first k elements are the @@ -481,7 +481,7 @@ fn partition[ Parameters: type: DType of the underlying data. - lifetime: Lifetime of span. + lifetime: Origin of span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -506,7 +506,7 @@ fn partition[ # Junction from public to private API fn _sort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, *, stable: Bool = False, @@ -532,7 +532,7 @@ fn _sort[ # optional cmp_fn. fn sort[ type: CollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (type, type) capturing [_] -> Bool, *, stable: Bool = False, @@ -542,7 +542,7 @@ fn sort[ Parameters: type: CollectionElement type of the underlying data. - lifetime: Lifetime of span. + lifetime: Origin of span. cmp_fn: The comparison function. stable: Whether the sort should be stable. @@ -558,7 +558,7 @@ fn sort[ fn sort[ - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (Int, Int) capturing [_] -> Bool, *, stable: Bool = False, @@ -567,7 +567,7 @@ fn sort[ The function doesn't return anything, the list is updated inplace. Parameters: - lifetime: Lifetime of span. + lifetime: Origin of span. cmp_fn: The comparison function. stable: Whether the sort should be stable. @@ -584,7 +584,7 @@ fn sort[ fn sort[ type: DType, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing [_] -> Bool, *, stable: Bool = False, @@ -594,7 +594,7 @@ fn sort[ Parameters: type: DType type of the underlying data. - lifetime: Lifetime of span. + lifetime: Origin of span. cmp_fn: The comparison function. stable: Whether the sort should be stable. @@ -612,7 +612,7 @@ fn sort[ fn sort[ - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, *, stable: Bool = False, ](span: Span[Int, lifetime]): @@ -620,7 +620,7 @@ fn sort[ The function doesn't return anything, the list is updated inplace. Parameters: - lifetime: Lifetime of span. + lifetime: Origin of span. stable: Whether the sort should be stable. Args: @@ -636,7 +636,7 @@ fn sort[ fn sort[ type: DType, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, *, stable: Bool = False, ](span: Span[Scalar[type], lifetime]): @@ -645,7 +645,7 @@ fn sort[ Parameters: type: CollectionElement type of the underlying data. - lifetime: Lifetime of span. + lifetime: Origin of span. stable: Whether the sort should be stable. Args: @@ -661,7 +661,7 @@ fn sort[ fn sort[ type: ComparableCollectionElement, - lifetime: MutableLifetime, //, + lifetime: MutableOrigin, //, *, stable: Bool = False, ](span: Span[type, lifetime]): @@ -669,7 +669,7 @@ fn sort[ Parameters: type: The order comparable collection element type. - lifetime: Lifetime of span. + lifetime: Origin of span. stable: Whether the sort should be stable. Args: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 65d05f6e25..7198bfc473 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -290,13 +290,13 @@ struct StringLiteral( """ return self.__str__() - fn __iter__(ref [_]self) -> _StringSliceIter[StaticConstantLifetime]: + fn __iter__(ref [_]self) -> _StringSliceIter[StaticConstantOrigin]: """Return an iterator over the string literal. Returns: An iterator over the string. """ - return _StringSliceIter[StaticConstantLifetime]( + return _StringSliceIter[StaticConstantOrigin]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) @@ -331,7 +331,7 @@ struct StringLiteral( return __mlir_op.`pop.string.size`(self.value) @always_inline("nodebug") - # FIXME(MSTDL-956): This should return a pointer with StaticConstantLifetime. + # FIXME(MSTDL-956): This should return a pointer with StaticConstantOrigin. fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Get raw pointer to the underlying data. @@ -346,7 +346,7 @@ struct StringLiteral( return ptr.bitcast[UInt8]() @always_inline - # FIXME(MSTDL-956): This should return a pointer with StaticConstantLifetime. + # FIXME(MSTDL-956): This should return a pointer with StaticConstantOrigin. fn unsafe_cstr_ptr(self) -> UnsafePointer[c_char]: """Retrieves a C-string-compatible pointer to the underlying memory. @@ -373,7 +373,7 @@ struct StringLiteral( ) @always_inline - fn as_bytes(self) -> Span[UInt8, StaticConstantLifetime]: + fn as_bytes(self) -> Span[UInt8, StaticConstantOrigin]: """ Returns a contiguous Span of the bytes owned by this string. @@ -381,7 +381,7 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, StaticConstantLifetime]( + return Span[UInt8, StaticConstantOrigin]( unsafe_ptr=self.unsafe_ptr(), len=self.byte_length(), ) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 80041ab90e..299b9320c8 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -18,33 +18,33 @@ These are Mojo built-ins, so you don't need to import them. alias AnyTrivialRegType = __mlir_type.`!kgen.type` """Represents any register passable Mojo data type.""" -alias ImmutableLifetime = __mlir_type.`!lit.lifetime<0>` -"""Immutable lifetime reference type.""" +alias ImmutableOrigin = __mlir_type.`!lit.lifetime<0>` +"""Immutable origin reference type.""" -alias MutableLifetime = __mlir_type.`!lit.lifetime<1>` -"""Mutable lifetime reference type.""" +alias MutableOrigin = __mlir_type.`!lit.lifetime<1>` +"""Mutable origin reference type.""" -alias ImmutableAnyLifetime = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<0>` -"""The immutable lifetime that might access any memory value.""" +alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<0>` +"""The immutable origin that might access any memory value.""" -alias MutableAnyLifetime = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<1>` -"""The mutable lifetime that might access any memory value.""" +alias MutableAnyOrigin = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<1>` +"""The mutable origin that might access any memory value.""" # Static constants are a named subset of the global lifetime. -alias StaticConstantLifetime = __mlir_attr[ +alias StaticConstantOrigin = __mlir_attr[ `#lit.lifetime.field<`, `#lit.static.lifetime : !lit.lifetime<0>`, `, "__constants__"> : !lit.lifetime<0>`, ] -"""A lifetime for strings and other always-immutable static constants.""" +"""An origin for strings and other always-immutable static constants.""" -alias LifetimeSet = __mlir_type.`!lit.lifetime.set` +alias OriginSet = __mlir_type.`!lit.lifetime.set` """A set of lifetime parameters.""" # Helper to build a value of !lit.lifetime type. # TODO: Should be a parametric alias. -struct Lifetime[is_mutable: Bool]: +struct Origin[is_mutable: Bool]: """This represents a lifetime reference of potentially parametric type. TODO: This should be replaced with a parametric type alias. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 2e5435928a..8a8ca8ffce 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -60,7 +60,7 @@ struct _DictEntryIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: Lifetime[dict_mutability].type, + dict_lifetime: Origin[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable DictEntry references. @@ -121,7 +121,7 @@ struct _DictKeyIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: Lifetime[dict_mutability].type, + dict_lifetime: Origin[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable Dict key references. @@ -159,7 +159,7 @@ struct _DictValueIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: Lifetime[dict_mutability].type, + dict_lifetime: Origin[dict_mutability].type, forward: Bool = True, ]: """Iterator over Dict value references. These are mutable if the dict diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 1fd52eb6e5..fb2894e359 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -31,7 +31,7 @@ struct _InlineListIter[ list_mutability: Bool, //, T: CollectionElementNew, capacity: Int, - list_lifetime: Lifetime[list_mutability].type, + list_lifetime: Origin[list_mutability].type, forward: Bool = True, ]: """Iterator for InlineList. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 69515acac8..916990f0ae 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -38,7 +38,7 @@ struct _ListIter[ list_mutability: Bool, //, T: CollectionElement, hint_trivial_type: Bool, - list_lifetime: Lifetime[list_mutability].type, + list_lifetime: Origin[list_mutability].type, forward: Bool = True, ]: """Iterator for List. diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 7d66110b6e..2c84b25040 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -138,7 +138,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): # to model the mutability and invalidation of the returned reference # correctly. fn __getitem__[ - self_life: ImmutableLifetime + self_life: ImmutableOrigin ]( ref [self_life]self: Self, ) -> ref [ diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 7e6dc777a3..731099b5c8 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -234,8 +234,8 @@ fn memcpy[ """ var n = count * sizeof[dest.type]() _memcpy_impl( - dest.bitcast[Int8, lifetime=MutableAnyLifetime](), - src.bitcast[Int8, lifetime=MutableAnyLifetime](), + dest.bitcast[Int8, lifetime=MutableAnyOrigin](), + src.bitcast[Int8, lifetime=MutableAnyOrigin](), n, ) diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 8ecdeb094e..93a72393fd 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -273,7 +273,7 @@ struct AddressSpace(EqualityComparable): struct Pointer[ is_mutable: Bool, //, type: AnyType, - lifetime: Lifetime[is_mutable].type, + lifetime: Origin[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, ](CollectionElementNew, Stringable): """Defines a non-nullable safe pointer. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 526387f1fb..e545408d2a 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -53,7 +53,7 @@ struct UnsafePointer[ type: AnyType, address_space: AddressSpace = AddressSpace.GENERIC, alignment: Int = _default_alignment[type](), - lifetime: Lifetime[True].type = MutableAnyLifetime, + lifetime: Origin[True].type = MutableAnyOrigin, ]( ImplicitlyBoolable, CollectionElement, @@ -882,7 +882,7 @@ struct UnsafePointer[ /, address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, - lifetime: Lifetime[True].type = Self.lifetime, + lifetime: Origin[True].type = Self.lifetime, ](self) -> UnsafePointer[T, address_space, alignment, lifetime]: """Bitcasts a UnsafePointer to a different type. @@ -890,7 +890,7 @@ struct UnsafePointer[ T: The target type. address_space: The address space of the result. alignment: Alignment of the destination pointer. - lifetime: Lifetime of the destination pointer. + lifetime: Origin of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, @@ -908,7 +908,7 @@ struct UnsafePointer[ /, address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, - lifetime: Lifetime[True].type = Self.lifetime, + lifetime: Origin[True].type = Self.lifetime, ](self) -> UnsafePointer[Scalar[T], address_space, alignment, lifetime]: """Bitcasts a UnsafePointer to a different type. @@ -916,7 +916,7 @@ struct UnsafePointer[ T: The target type. address_space: The address space of the result. alignment: Alignment of the destination pointer. - lifetime: Lifetime of the destination pointer. + lifetime: Origin of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 06ff1443b5..5f2b9a5cf9 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -75,13 +75,13 @@ from builtin.tuple import ( ) from builtin.type_aliases import ( AnyTrivialRegType, - ImmutableLifetime, - MutableLifetime, - ImmutableAnyLifetime, - MutableAnyLifetime, - StaticConstantLifetime, - LifetimeSet, - Lifetime, + ImmutableOrigin, + MutableOrigin, + ImmutableAnyOrigin, + MutableAnyOrigin, + StaticConstantOrigin, + OriginSet, + Origin, ) from builtin.uint import UInt from builtin.value import ( diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index d79ee91ffc..949e5ceadc 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -478,7 +478,7 @@ struct _FixedString[CAP: Int]( fn write_to_string(ptr0: OpaquePointer, strref: StringRef): var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() - var str_slice = StringSlice[ImmutableAnyLifetime]( + var str_slice = StringSlice[ImmutableAnyOrigin]( unsafe_from_utf8_strref=strref ) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 140b0df71f..bab05cc62c 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -29,7 +29,7 @@ from builtin.builtin_list import _lit_mut_cast struct _SpanIter[ is_mutable: Bool, //, T: CollectionElement, - lifetime: Lifetime[is_mutable].type, + lifetime: Origin[is_mutable].type, forward: Bool = True, ]: """Iterator for Span. @@ -77,7 +77,7 @@ struct _SpanIter[ struct Span[ is_mutable: Bool, //, T: CollectionElement, - lifetime: Lifetime[is_mutable].type, + lifetime: Origin[is_mutable].type, ](CollectionElementNew): """A non owning view of contiguous data. @@ -238,7 +238,7 @@ struct Span[ @always_inline fn copy_from[ - lifetime: MutableLifetime, // + lifetime: MutableOrigin, // ](self: Span[T, lifetime], other: Span[T, _]): """ Performs an element wise copy from all elements of `other` into all elements of `self`. @@ -312,7 +312,7 @@ struct Span[ """ return not self == rhs - fn fill[lifetime: MutableLifetime, //](self: Span[T, lifetime], value: T): + fn fill[lifetime: MutableOrigin, //](self: Span[T, lifetime], value: T): """ Fill the memory that a span references with a given value. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 9bff4e9f5a..da4f8831af 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -27,7 +27,7 @@ from collections import List from memory import memcmp, UnsafePointer from sys import simdwidthof, bitwidthof -alias StaticString = StringSlice[StaticConstantLifetime] +alias StaticString = StringSlice[StaticConstantOrigin] """An immutable static string slice.""" @@ -125,7 +125,7 @@ fn _is_newline_start( @value struct _StringSliceIter[ is_mutable: Bool, //, - lifetime: Lifetime[is_mutable].type, + lifetime: Origin[is_mutable].type, forward: Bool = True, ]: """Iterator for StringSlice @@ -198,7 +198,7 @@ struct _StringSliceIter[ struct StringSlice[ is_mutable: Bool, //, - lifetime: Lifetime[is_mutable].type, + lifetime: Origin[is_mutable].type, ](Stringable, Sized, Formattable): """ A non-owning view to encoded string data. @@ -219,7 +219,7 @@ struct StringSlice[ @always_inline fn __init__( - inout self: StringSlice[StaticConstantLifetime], lit: StringLiteral + inout self: StringSlice[StaticConstantOrigin], lit: StringLiteral ): """Construct a new string slice from a string literal. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 484b58a12a..65a4e67602 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -395,7 +395,7 @@ struct StringRef( # SAFETY: # Safe because our use of this StringSlice does not outlive `self`. - var str_slice = StringSlice[ImmutableAnyLifetime]( + var str_slice = StringSlice[ImmutableAnyOrigin]( unsafe_from_utf8_strref=self ) diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index c070908826..c35c575680 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -21,7 +21,7 @@ from utils import Span fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = UnsafePointer[SIMD[D, 1]].alloc(size) rand[D](p, size) - sort(Span[Scalar[D], MutableAnyLifetime](unsafe_ptr=p, len=size)) + sort(Span[Scalar[D], MutableAnyOrigin](unsafe_ptr=p, len=size)) for i in range(1, size - 1): if p[i] < p[i - 1]: print(name, "size:", size, "max:", max, "incorrect sort") From 0c470f2d8884cd4753a08ae5c0e35fc1094fe3ea Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 20:10:06 -0700 Subject: [PATCH 1734/2019] [mojo-lang] Rename `__lifetime_of` to `__origin_of`. As part of the great lifetime->origin renaming, we move this builtin function over. MODULAR_ORIG_COMMIT_REV_ID: a205fe4d8e6596ec3c5779d56940cd040b65dc28 --- docs/changelog-released.md | 2 +- docs/changelog.md | 9 +++++---- docs/manual/values/lifetimes.ipynb | 4 ++-- proposals/ref-convention.md | 2 +- stdlib/src/builtin/builtin_list.mojo | 6 +++--- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/rebind.mojo | 2 +- stdlib/src/builtin/reversed.mojo | 4 ++-- stdlib/src/builtin/sort.mojo | 2 +- stdlib/src/collections/counter.mojo | 8 ++++---- stdlib/src/collections/dict.mojo | 24 +++++++++++------------- stdlib/src/collections/inline_list.mojo | 4 ++-- stdlib/src/collections/list.mojo | 4 ++-- stdlib/src/collections/set.mojo | 2 +- stdlib/src/collections/string.mojo | 14 +++++++------- stdlib/src/utils/inline_string.mojo | 12 ++++++------ 16 files changed, 50 insertions(+), 51 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 848a884f82..1bb87df302 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -3137,7 +3137,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. [`Copyable`](/mojo/stdlib/builtin/value/Copyable), and [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement). -- A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory +- A new magic `__origin_of(expr)` call will yield the lifetime of a memory value. We hope and expect that this will eventually be replaced by `Reference(expr).lifetime` as the parameter system evolves, but this is important in the meantime for use in function signatures. diff --git a/docs/changelog.md b/docs/changelog.md index 030166912e..527e4fa0f9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -89,7 +89,7 @@ what we publish. - Added `Python.unsafe_get_python_exception()`, as an efficient low-level utility to get the Mojo `Error` equivalent of the current CPython error state. -- The `__type_of(x)` and `__lifetime_of(x)` operators are much more general now: +- The `__type_of(x)` and `__origin_of(x)` operators are much more general now: they allow arbitrary expressions inside of them, allow referring to dynamic values in parameter contexts, and even allow referring to raising functions in non-raising contexts. These operations never evaluate their expression, so @@ -166,9 +166,9 @@ what we publish. - `ref` argument and result specifiers now allow providing a memory value directly in the lifetime specifier, rather than requiring the use of - `__lifetime_of`. It is still fine to use `__lifetime_of` explicitly though, + `__origin_of`. It is still fine to use `__origin_of` explicitly though, and this is required when specifying lifetimes for parameters (e.g. to the - `Reference` type). For example, this is now valid without `__lifetime_of`: + `Reference` type). For example, this is now valid without `__origin_of`: ```mojo fn return_ref(a: String) -> ref [a] String: @@ -268,7 +268,8 @@ what we publish. library to better clarify that parameters of this type indicate where a reference is derived from, not the more complicated notion of where a variable is initialized and destroyed. Please see [the proposal](https://github.com/modularml/mojo/blob/main/proposals/lifetimes-keyword-renaming.md) - for more information and rationale. + for more information and rationale. As a consequence `__origin_of` is now + named `__origin_of`. ### ❌ Removed diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index ddf4086e83..06fb1154db 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -182,7 +182,7 @@ "\n", "- Static lifetimes. The `ImmutableStaticLifetime` and `MutableStaticLifetime`\n", " aliases are lifetimes that last for the duration of the program. \n", - "- The `__lifetime_of()` magic function, which returns the lifetime associated\n", + "- The `__origin_of()` magic function, which returns the lifetime associated\n", " with the value (or values) passed in.\n", "- Inferred lifetime. You can use inferred parameters to capture the lifetime\n", " of a value passed in to a function.\n", @@ -213,7 +213,7 @@ "\n", "#### Derived lifetimes\n", "\n", - "Use the `__lifetime_of()` magic function to obtain a value's lifetime. This can \n", + "Use the `__origin_of()` magic function to obtain a value's lifetime. This can \n", "be useful, for example, when creating a container type. Consider the `List`\n", "type. Subscripting into a list (`list[4]`) returns a reference to the item at\n", "the specified position. The signature of the `__getitem__()` method that's\n", diff --git a/proposals/ref-convention.md b/proposals/ref-convention.md index b99a3510aa..c67db5b262 100644 --- a/proposals/ref-convention.md +++ b/proposals/ref-convention.md @@ -83,7 +83,7 @@ You would now use an argument convention: ```mojo fn __refitem__(ref [_] self, index: Int) -> Reference[ # This is a bit yuck, but is simplified further below. - Self.ElementType, __lifetime_of(self).is_mutable, __lifetime_of(self) + Self.ElementType, __origin_of(self).is_mutable, __origin_of(self) ]: # Alternatively, name the Lifetime: diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index ccaf7a4fe5..810f17e59b 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -431,7 +431,7 @@ struct VariadicListMem[ # cast mutability of self to match the mutability of the element, # since that is what we want to use in the ultimate reference and # the union overall doesn't matter. - _lit_mut_cast[__lifetime_of(self), elt_is_mutable].result, + _lit_mut_cast[__origin_of(self), elt_is_mutable].result, ].result ] element_type: """Gets a single element on the variadic list. @@ -449,7 +449,7 @@ struct VariadicListMem[ fn __iter__( self, - ) -> _VariadicListMemIter[element_type, lifetime, __lifetime_of(self),]: + ) -> _VariadicListMemIter[element_type, lifetime, __origin_of(self),]: """Iterate over the list. Returns: @@ -458,7 +458,7 @@ struct VariadicListMem[ return _VariadicListMemIter[ element_type, lifetime, - __lifetime_of(self), + __origin_of(self), ](0, self) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index ed4aeb8831..d5d17f0abb 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -403,7 +403,7 @@ fn _try_write_int[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. - var str_slice = StringSlice[__lifetime_of(buf)]( + var str_slice = StringSlice[__origin_of(buf)]( unsafe_from_utf8_ptr=buf_ptr, len=len, ) diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index fb5bd65750..5d9debeeab 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -67,5 +67,5 @@ fn rebind[ A reference to the value rebound as `dest_type`. """ lit = __get_mvalue_as_litref(src) - rebound = rebind[Pointer[dest_type, __lifetime_of(src)]._mlir_type](lit) + rebound = rebind[Pointer[dest_type, __origin_of(src)]._mlir_type](lit) return __get_litref_as_mvalue(rebound) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 0edfc493e9..0c2ebadf00 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -79,7 +79,7 @@ fn reversed[T: ReversibleRange](value: T) -> _StridedRange: fn reversed[ T: CollectionElement ](ref [_]value: List[T, *_]) -> _ListIter[ - T, __type_of(value).hint_trivial_type, __lifetime_of(value), False + T, __type_of(value).hint_trivial_type, __origin_of(value), False ]: """Get a reversed iterator of the input list. @@ -100,7 +100,7 @@ fn reversed[ fn reversed[ K: KeyElement, V: CollectionElement, -](ref [_]value: Dict[K, V],) -> _DictKeyIter[K, V, __lifetime_of(value), False]: +](ref [_]value: Dict[K, V],) -> _DictKeyIter[K, V, __origin_of(value), False]: """Get a reversed iterator of the input dict. **Note**: iterators are currently non-raising. diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 813a56ed55..f840bff519 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -356,7 +356,7 @@ fn _stable_sort[ cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ](span: Span[type, lifetime]): var temp_buff = UnsafePointer[type].alloc(len(span)) - var temp_buff_span = Span[type, __lifetime_of(temp_buff)]( + var temp_buff_span = Span[type, __origin_of(temp_buff)]( unsafe_ptr=temp_buff, len=len(span) ) _stable_sort_impl[cmp_fn](span, temp_buff_span) diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 594dbc1bce..b65076d9e0 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -121,7 +121,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ self._data[value] = count - fn __iter__(self: Self) -> _DictKeyIter[V, Int, __lifetime_of(self._data)]: + fn __iter__(self: Self) -> _DictKeyIter[V, Int, __origin_of(self._data)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -483,7 +483,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): fn keys( ref [_]self: Self, - ) -> _DictKeyIter[V, Int, __lifetime_of(self._data)]: + ) -> _DictKeyIter[V, Int, __origin_of(self._data)]: """Iterate over the Counter's keys as immutable references. Returns: @@ -493,7 +493,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): fn values( ref [_]self: Self, - ) -> _DictValueIter[V, Int, __lifetime_of(self._data)]: + ) -> _DictValueIter[V, Int, __origin_of(self._data)]: """Iterate over the Counter's values as references. Returns: @@ -501,7 +501,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.values() - fn items(self: Self) -> _DictEntryIter[V, Int, __lifetime_of(self._data)]: + fn items(self: Self) -> _DictEntryIter[V, Int, __origin_of(self._data)]: """Iterate over the dict's entries as immutable references. Returns: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 8a8ca8ffce..daca2fa483 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -90,9 +90,7 @@ struct _DictEntryIter[ @always_inline fn __next__( inout self, - ) -> Pointer[ - DictEntry[K, V], __lifetime_of(self.src[]._entries[0].value()) - ]: + ) -> Pointer[DictEntry[K, V], __origin_of(self.src[]._entries[0].value())]: while True: var opt_entry_ref = Pointer.address_of( self.src[]._entries[self.index] @@ -143,7 +141,7 @@ struct _DictKeyIter[ fn __next__( inout self, - ) -> Pointer[K, __lifetime_of(self.iter.__next__()[].key)]: + ) -> Pointer[K, __origin_of(self.iter.__next__()[].key)]: return Pointer.address_of(self.iter.__next__()[].key) @always_inline @@ -619,7 +617,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn __iter__( ref [_]self: Self, - ) -> _DictKeyIter[K, V, __lifetime_of(self)]: + ) -> _DictKeyIter[K, V, __origin_of(self)]: """Iterate over the dict's keys as immutable references. Returns: @@ -629,7 +627,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn __reversed__( ref [_]self: Self, - ) -> _DictKeyIter[K, V, __lifetime_of(self), False]: + ) -> _DictKeyIter[K, V, __origin_of(self), False]: """Iterate backwards over the dict keys, returning immutable references. Returns: @@ -878,7 +876,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( raise "KeyError: popitem(): dictionary is empty" - fn keys(ref [_]self: Self) -> _DictKeyIter[K, V, __lifetime_of(self)]: + fn keys(ref [_]self: Self) -> _DictKeyIter[K, V, __origin_of(self)]: """Iterate over the dict's keys as immutable references. Returns: @@ -886,7 +884,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return Self.__iter__(self) - fn values(ref [_]self: Self) -> _DictValueIter[K, V, __lifetime_of(self)]: + fn values(ref [_]self: Self) -> _DictValueIter[K, V, __origin_of(self)]: """Iterate over the dict's values as references. Returns: @@ -894,7 +892,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return _DictValueIter(_DictEntryIter(0, 0, self)) - fn items(ref [_]self: Self) -> _DictEntryIter[K, V, __lifetime_of(self)]: + fn items(ref [_]self: Self) -> _DictEntryIter[K, V, __origin_of(self)]: """Iterate over the dict's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1208,7 +1206,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn __iter__( ref [_]self: Self, - ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self._dict)]: + ) -> _DictKeyIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1218,7 +1216,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn keys( ref [_]self: Self, - ) -> _DictKeyIter[Self.key_type, V, __lifetime_of(self._dict)]: + ) -> _DictKeyIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dict's keys as immutable references. Returns: @@ -1228,7 +1226,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn values( ref [_]self: Self, - ) -> _DictValueIter[Self.key_type, V, __lifetime_of(self._dict)]: + ) -> _DictValueIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dict's values as references. Returns: @@ -1238,7 +1236,7 @@ struct OwnedKwargsDict[V: CollectionElement]( fn items( ref [_]self: Self, - ) -> _DictEntryIter[Self.key_type, V, __lifetime_of(self._dict)]: + ) -> _DictEntryIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dictionary's entries as immutable references. These can't yet be unpacked like Python dict items, but you can diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index fb2894e359..f2977f0144 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -54,7 +54,7 @@ struct _InlineListIter[ fn __next__( inout self, - ) -> Pointer[T, __lifetime_of(self.src[][0])]: + ) -> Pointer[T, __origin_of(self.src[][0])]: @parameter if forward: self.index += 1 @@ -175,7 +175,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __iter__( ref [_]self: Self, - ) -> _InlineListIter[ElementType, capacity, __lifetime_of(self)]: + ) -> _InlineListIter[ElementType, capacity, __origin_of(self)]: """Iterate over elements of the list, returning immutable references. Returns: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 916990f0ae..c90f2a1ac2 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -351,7 +351,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn __iter__( ref [_]self: Self, - ) -> _ListIter[T, hint_trivial_type, __lifetime_of(self)]: + ) -> _ListIter[T, hint_trivial_type, __origin_of(self)]: """Iterate over elements of the list, returning immutable references. Returns: @@ -361,7 +361,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn __reversed__( ref [_]self: Self, - ) -> _ListIter[T, hint_trivial_type, __lifetime_of(self), False]: + ) -> _ListIter[T, hint_trivial_type, __origin_of(self), False]: """Iterate backwards over the list, returning immutable references. Returns: diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 71cf307408..83f07d4e58 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -355,7 +355,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): fn __iter__( ref [_]self: Self, - ) -> _DictKeyIter[T, NoneType, __lifetime_of(self._data)]: + ) -> _DictKeyIter[T, NoneType, __origin_of(self._data)]: """Iterate over elements of the set, returning immutable references. Returns: diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index ef9cd5940c..f71e881985 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1125,25 +1125,25 @@ struct String( count=other_len + 1, ) - fn __iter__(ref [_]self) -> _StringSliceIter[__lifetime_of(self)]: + fn __iter__(ref [_]self) -> _StringSliceIter[__origin_of(self)]: """Iterate over elements of the string, returning immutable references. Returns: An iterator of references to the string elements. """ - return _StringSliceIter[__lifetime_of(self)]( + return _StringSliceIter[__origin_of(self)]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) fn __reversed__( ref [_]self, - ) -> _StringSliceIter[__lifetime_of(self), False]: + ) -> _StringSliceIter[__origin_of(self), False]: """Iterate backwards over the string, returning immutable references. Returns: A reversed iterator of references to the string elements. """ - return _StringSliceIter[__lifetime_of(self), forward=False]( + return _StringSliceIter[__origin_of(self), forward=False]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) @@ -1374,7 +1374,7 @@ struct String( return self.unsafe_ptr().bitcast[c_char]() @always_inline - fn as_bytes(ref [_]self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes(ref [_]self) -> Span[UInt8, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: @@ -1385,12 +1385,12 @@ struct String( """ # Does NOT include the NUL terminator. - return Span[UInt8, __lifetime_of(self)]( + return Span[UInt8, __origin_of(self)]( unsafe_ptr=self._buffer.unsafe_ptr(), len=self.byte_length() ) @always_inline - fn as_string_slice(ref [_]self) -> StringSlice[__lifetime_of(self)]: + fn as_string_slice(ref [_]self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this string. Returns: diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 949e5ceadc..647264f664 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -269,7 +269,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return self._storage[String].unsafe_ptr() @always_inline - fn as_string_slice(ref [_]self: Self) -> StringSlice[__lifetime_of(self)]: + fn as_string_slice(ref [_]self: Self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this inline string. Returns: @@ -282,7 +282,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes(ref [_]self: Self) -> Span[UInt8, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -292,7 +292,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, __lifetime_of(self)]( + return Span[UInt8, __origin_of(self)]( unsafe_ptr=self.unsafe_ptr(), # Does NOT include the NUL terminator. len=len(self), @@ -508,7 +508,7 @@ struct _FixedString[CAP: Int]( return self.buffer.unsafe_ptr() @always_inline - fn as_string_slice(ref [_]self: Self) -> StringSlice[__lifetime_of(self)]: + fn as_string_slice(ref [_]self: Self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this fixed string. Returns: @@ -521,7 +521,7 @@ struct _FixedString[CAP: Int]( return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref [_]self: Self) -> Span[UInt8, __lifetime_of(self)]: + fn as_bytes(ref [_]self: Self) -> Span[UInt8, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -531,7 +531,7 @@ struct _FixedString[CAP: Int]( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, __lifetime_of(self)]( + return Span[UInt8, __origin_of(self)]( unsafe_ptr=self.unsafe_ptr(), # Does NOT include the NUL terminator. len=self.size, From 8bea734b9090329c84f8ffa69704014ac353a1ec Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 20:42:00 -0700 Subject: [PATCH 1735/2019] [mojo-lang] Rename LifetimeSet -> OriginSet Rename the MLIR LifetimeSet type and attribute to use 'origin' nomenclature. MODULAR_ORIG_COMMIT_REV_ID: 55c7ccc10b905bf1f753084e3c7e7870d5f84c2a --- stdlib/src/builtin/type_aliases.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 299b9320c8..00ab5a8b75 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -38,7 +38,7 @@ alias StaticConstantOrigin = __mlir_attr[ ] """An origin for strings and other always-immutable static constants.""" -alias OriginSet = __mlir_type.`!lit.lifetime.set` +alias OriginSet = __mlir_type.`!lit.origin.set` """A set of lifetime parameters.""" From 5533926b1fa464f1d90593743f3792a9e5c48589 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 21:07:49 -0700 Subject: [PATCH 1736/2019] [mojo-lang] Rename various lifetime attrs -> origin attrs Next step in migration MODULAR_ORIG_COMMIT_REV_ID: ba63b285c06e1844367b6f9c4b6c56c73935ebcc --- stdlib/src/builtin/_pybind.mojo | 4 ++-- stdlib/src/builtin/builtin_list.mojo | 4 ++-- stdlib/src/builtin/type_aliases.mojo | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 8cf9036056..7c18783005 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -58,8 +58,8 @@ fn fail_initialization(owned err: Error) -> PythonObject: alias MutableGlobalLifetime = __mlir_attr[ - `#lit.lifetime.field<`, - `#lit.static.lifetime : !lit.lifetime<1>`, + `#lit.origin.field<`, + `#lit.static.origin : !lit.lifetime<1>`, `, "__python_globals__"> : !lit.lifetime<1>`, ] diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 810f17e59b..3cd0881369 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -265,7 +265,7 @@ struct _lit_lifetime_union[ b: Origin[is_mutable].type, ]: alias result = __mlir_attr[ - `#lit.lifetime.union<`, + `#lit.origin.union<`, a, `,`, b, @@ -281,7 +281,7 @@ struct _lit_mut_cast[ result_mutable: Bool, ]: alias result = __mlir_attr[ - `#lit.lifetime.mutcast<`, + `#lit.origin.mutcast<`, operand, `> : !lit.lifetime<`, +result_mutable.value, diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 00ab5a8b75..f0016648e9 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -24,16 +24,16 @@ alias ImmutableOrigin = __mlir_type.`!lit.lifetime<0>` alias MutableOrigin = __mlir_type.`!lit.lifetime<1>` """Mutable origin reference type.""" -alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<0>` +alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.lifetime<0>` """The immutable origin that might access any memory value.""" -alias MutableAnyOrigin = __mlir_attr.`#lit.any.lifetime : !lit.lifetime<1>` +alias MutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.lifetime<1>` """The mutable origin that might access any memory value.""" # Static constants are a named subset of the global lifetime. alias StaticConstantOrigin = __mlir_attr[ - `#lit.lifetime.field<`, - `#lit.static.lifetime : !lit.lifetime<0>`, + `#lit.origin.field<`, + `#lit.static.origin : !lit.lifetime<0>`, `, "__constants__"> : !lit.lifetime<0>`, ] """An origin for strings and other always-immutable static constants.""" From c0de4260e3b5a39d867990ddb0b515556259ad90 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 11 Oct 2024 22:45:07 -0700 Subject: [PATCH 1737/2019] [mojo-lang] Rename LifetimeType -> OriginType. This renames `!lit.lifetime` -> `!lit.origin`, continuing the migration. MODULAR_ORIG_COMMIT_REV_ID: 3903cc27bf50589016655c73bea95659378ce245 --- docs/manual/values/lifetimes.ipynb | 2 +- stdlib/src/builtin/_pybind.mojo | 4 ++-- stdlib/src/builtin/anytype.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 6 +++--- stdlib/src/builtin/coroutine.mojo | 8 ++++---- stdlib/src/builtin/type_aliases.mojo | 16 ++++++++-------- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/utils/variant.mojo | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 06fb1154db..3c801baf52 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -298,7 +298,7 @@ "\n", "The syntax for a `ref` argument is:\n", "\n", - "ref [lifetime] argName: argType\n", + "ref [origin] argName: argType\n", "\n", "The `lifetime` parameter passed inside the square brackets can be replaced with\n", "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 7c18783005..1bc83b1d5c 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -59,8 +59,8 @@ fn fail_initialization(owned err: Error) -> PythonObject: alias MutableGlobalLifetime = __mlir_attr[ `#lit.origin.field<`, - `#lit.static.origin : !lit.lifetime<1>`, - `, "__python_globals__"> : !lit.lifetime<1>`, + `#lit.static.origin : !lit.origin<1>`, + `, "__python_globals__"> : !lit.origin<1>`, ] diff --git a/stdlib/src/builtin/anytype.mojo b/stdlib/src/builtin/anytype.mojo index 2d34ffc441..c54a4e1745 100644 --- a/stdlib/src/builtin/anytype.mojo +++ b/stdlib/src/builtin/anytype.mojo @@ -28,7 +28,7 @@ trait AnyType: an instance of the object reaches the end of its lifetime. Hence, only non-trivial types may have destructors. - Any composition of types that have lifetimes is also an object with a + Any composition of types that have origins is also an object with a lifetime, and the resultant type receives a destructor regardless of whether the user explicitly defines one. diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 3cd0881369..ea5b6149b4 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -257,7 +257,7 @@ struct _VariadicListMemIter[ return len(self.src[]) - self.index -# Helper to compute the union of two lifetimes: +# Helper to compute the union of two origins: # TODO: parametric aliases would be nice. struct _lit_lifetime_union[ is_mutable: Bool, //, @@ -269,7 +269,7 @@ struct _lit_lifetime_union[ a, `,`, b, - `> : !lit.lifetime<`, + `> : !lit.origin<`, is_mutable.value, `>`, ] @@ -283,7 +283,7 @@ struct _lit_mut_cast[ alias result = __mlir_attr[ `#lit.origin.mutcast<`, operand, - `> : !lit.lifetime<`, + `> : !lit.origin<`, +result_mutable.value, `>`, ] diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index d49cd441ed..eb8374ef2d 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -79,7 +79,7 @@ fn _coro_resume_noop_callback(null: AnyCoroutine): @register_passable -struct Coroutine[type: AnyType, lifetimes: OriginSet]: +struct Coroutine[type: AnyType, origins: OriginSet]: """Represents a coroutine. Coroutines can pause execution saving the state of the program (including @@ -89,7 +89,7 @@ struct Coroutine[type: AnyType, lifetimes: OriginSet]: Parameters: type: Type of value returned upon completion of the coroutine. - lifetimes: The lifetime of the coroutine's captures. + origins: The lifetime of the coroutine's captures. """ var _handle: AnyCoroutine @@ -158,7 +158,7 @@ struct Coroutine[type: AnyType, lifetimes: OriginSet]: @register_passable -struct RaisingCoroutine[type: AnyType, lifetimes: OriginSet]: +struct RaisingCoroutine[type: AnyType, origins: OriginSet]: """Represents a coroutine that can raise. Coroutines can pause execution saving the state of the program (including @@ -168,7 +168,7 @@ struct RaisingCoroutine[type: AnyType, lifetimes: OriginSet]: Parameters: type: Type of value returned upon completion of the coroutine. - lifetimes: The lifetime set of the coroutine's captures. + origins: The lifetime set of the coroutine's captures. """ var _handle: AnyCoroutine diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index f0016648e9..432bddd9ed 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -18,23 +18,23 @@ These are Mojo built-ins, so you don't need to import them. alias AnyTrivialRegType = __mlir_type.`!kgen.type` """Represents any register passable Mojo data type.""" -alias ImmutableOrigin = __mlir_type.`!lit.lifetime<0>` +alias ImmutableOrigin = __mlir_type.`!lit.origin<0>` """Immutable origin reference type.""" -alias MutableOrigin = __mlir_type.`!lit.lifetime<1>` +alias MutableOrigin = __mlir_type.`!lit.origin<1>` """Mutable origin reference type.""" -alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.lifetime<0>` +alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.origin<0>` """The immutable origin that might access any memory value.""" -alias MutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.lifetime<1>` +alias MutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.origin<1>` """The mutable origin that might access any memory value.""" # Static constants are a named subset of the global lifetime. alias StaticConstantOrigin = __mlir_attr[ `#lit.origin.field<`, - `#lit.static.origin : !lit.lifetime<0>`, - `, "__constants__"> : !lit.lifetime<0>`, + `#lit.static.origin : !lit.origin<0>`, + `, "__constants__"> : !lit.origin<0>`, ] """An origin for strings and other always-immutable static constants.""" @@ -42,7 +42,7 @@ alias OriginSet = __mlir_type.`!lit.origin.set` """A set of lifetime parameters.""" -# Helper to build a value of !lit.lifetime type. +# Helper to build a value of !lit.origin type. # TODO: Should be a parametric alias. struct Origin[is_mutable: Bool]: """This represents a lifetime reference of potentially parametric type. @@ -53,7 +53,7 @@ struct Origin[is_mutable: Bool]: """ alias type = __mlir_type[ - `!lit.lifetime<`, + `!lit.origin<`, is_mutable.value, `>`, ] diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 2c84b25040..aaff7b6c0e 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -134,7 +134,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): # FIXME: The lifetime returned for this is currently self lifetime, which # keeps the Arc object alive as long as there are references into it. That - # said, this isn't really the right modeling, we need hierarchical lifetimes + # said, this isn't really the right modeling, we need hierarchical origins # to model the mutability and invalidation of the returned reference # correctly. fn __getitem__[ diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index cb60bfb46e..c79a3be725 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -73,7 +73,7 @@ struct Variant[*Ts: CollectionElement]( - use `isa[T]()` to check what type a variant is - use `unsafe_take[T]()` to take a value from the variant - use `[T]` to get a value out of a variant - - This currently does an extra copy/move until we have lifetimes + - This currently does an extra copy/move until we have origins - It also temporarily requires the value to be mutable - use `set[T](owned new_value: T)` to reset the variant to a new value From b445fd2dac3e2df71479261ea2d91411247e9433 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 12 Oct 2024 15:37:08 -0700 Subject: [PATCH 1738/2019] [mojo-stdlib] Rename many uses of lifetime -> origin This updates a wide range of different names in the stdlib and in other places. MODULAR_ORIG_COMMIT_REV_ID: 7b88a3200447f85fff8e44c8ad6fd4ad2b0b57d7 --- docs/manual/lifecycle/index.ipynb | 4 +- docs/manual/pointers.ipynb | 30 +++--- proposals/value-ownership.md | 4 +- stdlib/src/builtin/_pybind.mojo | 4 +- stdlib/src/builtin/builtin_list.mojo | 42 ++++----- stdlib/src/builtin/coroutine.mojo | 4 +- stdlib/src/builtin/format_int.mojo | 2 +- stdlib/src/builtin/reversed.mojo | 18 ++-- stdlib/src/builtin/sort.mojo | 114 +++++++++++------------ stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/builtin/type_aliases.mojo | 8 +- stdlib/src/collections/dict.mojo | 26 +++--- stdlib/src/collections/inline_list.mojo | 6 +- stdlib/src/collections/list.mojo | 8 +- stdlib/src/memory/arc.mojo | 4 +- stdlib/src/memory/box.mojo | 4 +- stdlib/src/memory/memory.mojo | 4 +- stdlib/src/memory/pointer.mojo | 18 ++-- stdlib/src/memory/unsafe_pointer.mojo | 44 ++++----- stdlib/src/python/python_object.mojo | 2 +- stdlib/src/utils/span.mojo | 46 ++++----- stdlib/src/utils/string_slice.mojo | 46 ++++----- stdlib/test/utils/test_string_slice.mojo | 2 +- 23 files changed, 220 insertions(+), 222 deletions(-) diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 9714beaf5b..61872ba398 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -82,9 +82,9 @@ "\n", ":::note Lifetime type\n", "\n", - "The concept of lifetimes is related to the `lifetime` type, a Mojo primitive\n", + "The concept of lifetimes is related to the `origin` type, a Mojo primitive\n", "used to track ownership. For most Mojo programming, you won't need to work with\n", - "`lifetime` values directly. For information, see [Lifetimes and\n", + "`origin` values directly. For information, see [Lifetimes and\n", "references](/mojo/manual/values/lifetimes).\n", "\n", ":::\n", diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb index d9e2c97284..7b2b495c0f 100644 --- a/docs/manual/pointers.ipynb +++ b/docs/manual/pointers.ipynb @@ -31,8 +31,8 @@ ":::note \n", "\n", "In addition to unsafe pointers, Mojo supports a safe \n", - "[`Reference`](/mojo/stdlib/memory/reference/Reference) type. See\n", - "[`UnsafePointer` and `Reference`](#unsafepointer-and-reference) for a brief\n", + "[`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type. See\n", + "[`UnsafePointer` and `Pointer`](#unsafepointer-and-pointer) for a brief\n", "comparison of the types.\n", "\n", ":::\n", @@ -708,25 +708,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## `UnsafePointer` and `Reference`\n", + "## `UnsafePointer` and `Pointer`\n", "\n", - "The [`Reference`](/mojo/stdlib/memory/reference/Reference) type is essentially a \n", - "safe pointer type. Like a pointer, you can derference a `Reference` using the \n", - "dereference operator, `[]`. However, the `Reference` type has several\n", + "The [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type is essentially a \n", + "safe pointer type. Like a pointer, you can derference a `Pointer` using the \n", + "dereference operator, `[]`. However, the `Pointer` type has several\n", "differences from `UnsafePointer` which make it safer:\n", "\n", - "- A `Reference` is _non-nullable_. A reference always points to something.\n", - "- You can't allocate or free memory using a `Reference`—only point to an\n", + "- A `Pointer` is _non-nullable_: it always points to something.\n", + "- You can't allocate or free memory using a `Pointer`—only point to an\n", " existing value.\n", - "- A `Reference` only refers to a single value. You can't do pointer arithmetic\n", - " with a `Reference`.\n", - "- A `Reference` has an associated _lifetime_, which connects it back to an\n", - " original, owned value. The lifetime ensures that the value won't be destroyed\n", - " while the reference exists.\n", + "- A `Pointer` only refers to a single value. You can't do pointer arithmetic\n", + " with a `Pointer`.\n", + "- A `Pointer` has an associated _origin_, which connects it back to an\n", + " original, owned value. The origin ensures that the value won't be destroyed\n", + " while the pointer exists.\n", "\n", - "The `Reference` type shouldn't be confused with the immutable and mutable\n", + "The `Pointer` type shouldn't be confused with the immutable and mutable\n", "references used with the `borrowed` and `inout` argument conventions. Those\n", - "references do not require explicit dereferencing, unlike a `Reference` or \n", + "references do not require explicit dereferencing, unlike a `Pointer` or \n", "`UnsafePointer`." ] } diff --git a/proposals/value-ownership.md b/proposals/value-ownership.md index 9303d1644e..86812f1d2c 100644 --- a/proposals/value-ownership.md +++ b/proposals/value-ownership.md @@ -638,11 +638,11 @@ and MLIR design. For example, you could imagine things like this: ```mojo # Take a non-copyable SomeTy as a borrow and return owned copy - fn life_ex1['a: lifetime](value: 'a SomeTy) -> SomeTy: + fn life_ex1['a: origin](value: 'a SomeTy) -> SomeTy: return value.copy() # Take a non-copyable SomeTy and return the reference - fn life_ex2['a: lifetime](value: 'a SomeTy) -> borrowed 'a SomeTy: + fn life_ex2['a: origin](value: 'a SomeTy) -> borrowed 'a SomeTy: return value ``` diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 1bc83b1d5c..2a8552d447 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -65,7 +65,7 @@ alias MutableGlobalLifetime = __mlir_attr[ # FIXME(MOCO-1308): Workaround crash by adding explicit `alignment=1`. -alias PyGlobalPtr = UnsafePointer[lifetime=MutableGlobalLifetime, alignment=1] +alias PyGlobalPtr = UnsafePointer[origin=MutableGlobalLifetime, alignment=1] @always_inline @@ -79,7 +79,7 @@ fn global_alloc[T: AnyType, len: Int]() -> PyGlobalPtr[T]: fn pointer_bitcast[ To: AnyType -](ptr: Pointer) -> Pointer[To, ptr.lifetime, ptr.address_space, *_, **_] as out: +](ptr: Pointer) -> Pointer[To, ptr.origin, ptr.address_space, *_, **_] as out: return __type_of(out)( _mlir_value=__mlir_op.`lit.ref.from_pointer`[ _type = __type_of(out)._mlir_type diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index ea5b6149b4..3e8ed9a01d 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -218,25 +218,25 @@ struct VariadicList[type: AnyTrivialRegType](Sized): struct _VariadicListMemIter[ elt_is_mutable: Bool, //, elt_type: AnyType, - elt_lifetime: Origin[elt_is_mutable].type, - list_lifetime: ImmutableOrigin, + elt_origin: Origin[elt_is_mutable].type, + list_origin: ImmutableOrigin, ]: """Iterator for VariadicListMem. Parameters: elt_is_mutable: Whether the elements in the list are mutable. elt_type: The type of the elements in the list. - elt_lifetime: The lifetime of the elements. - list_lifetime: The lifetime of the VariadicListMem. + elt_origin: The origin of the elements. + list_origin: The origin of the VariadicListMem. """ - alias variadic_list_type = VariadicListMem[elt_type, elt_lifetime] + alias variadic_list_type = VariadicListMem[elt_type, elt_origin] var index: Int - var src: Pointer[Self.variadic_list_type, list_lifetime] + var src: Pointer[Self.variadic_list_type, list_origin] fn __init__( - inout self, index: Int, ref [list_lifetime]list: Self.variadic_list_type + inout self, index: Int, ref [list_origin]list: Self.variadic_list_type ): self.index = index self.src = Pointer.address_of(list) @@ -259,7 +259,7 @@ struct _VariadicListMemIter[ # Helper to compute the union of two origins: # TODO: parametric aliases would be nice. -struct _lit_lifetime_union[ +struct _lit_origin_union[ is_mutable: Bool, //, a: Origin[is_mutable].type, b: Origin[is_mutable].type, @@ -292,7 +292,7 @@ struct _lit_mut_cast[ struct VariadicListMem[ elt_is_mutable: Bool, //, element_type: AnyType, - lifetime: Origin[elt_is_mutable].type, + origin: Origin[elt_is_mutable].type, ](Sized): """A utility class to access variadic function arguments of memory-only types that may have ownership. It exposes references to the elements in a @@ -302,10 +302,10 @@ struct VariadicListMem[ elt_is_mutable: True if the elements of the list are mutable for an inout or owned argument. element_type: The type of the elements in the list. - lifetime: The reference lifetime of the underlying elements. + origin: The reference origin of the underlying elements. """ - alias reference_type = Pointer[element_type, lifetime] + alias reference_type = Pointer[element_type, origin] alias _mlir_ref_type = Self.reference_type._mlir_type alias _mlir_type = __mlir_type[ `!kgen.variadic<`, Self._mlir_ref_type, `, borrow_in_mem>` @@ -426,8 +426,8 @@ struct VariadicListMem[ fn __getitem__( self, idx: Int ) -> ref [ - _lit_lifetime_union[ - lifetime, + _lit_origin_union[ + origin, # cast mutability of self to match the mutability of the element, # since that is what we want to use in the ultimate reference and # the union overall doesn't matter. @@ -449,7 +449,7 @@ struct VariadicListMem[ fn __iter__( self, - ) -> _VariadicListMemIter[element_type, lifetime, __origin_of(self),]: + ) -> _VariadicListMemIter[element_type, origin, __origin_of(self),]: """Iterate over the list. Returns: @@ -457,7 +457,7 @@ struct VariadicListMem[ """ return _VariadicListMemIter[ element_type, - lifetime, + origin, __origin_of(self), ](0, self) @@ -473,7 +473,7 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @value struct _LITRefPackHelper[ is_mutable: Bool, //, - lifetime: Origin[is_mutable].type, + origin: Origin[is_mutable].type, address_space: __mlir_type.index, element_trait: _AnyTypeMetaType, *element_types: element_trait, @@ -487,7 +487,7 @@ struct _LITRefPackHelper[ `> `, element_types, `, `, - lifetime, + origin, `, `, address_space, `>`, @@ -543,7 +543,7 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, //, - lifetime: Origin[Bool {value: elt_is_mutable}].type, + origin: Origin[Bool {value: elt_is_mutable}].type, element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): @@ -553,7 +553,7 @@ struct VariadicPack[ Parameters: elt_is_mutable: True if the elements of the list are mutable for an inout or owned argument pack. - lifetime: The reference lifetime of the underlying elements. + origin: The reference origin of the underlying elements. element_trait: The trait that each element of the pack conforms to. element_types: The list of types held by the argument pack. """ @@ -564,7 +564,7 @@ struct VariadicPack[ `> `, element_types, `, `, - lifetime, + origin, `>`, ] @@ -640,7 +640,7 @@ struct VariadicPack[ @always_inline fn __getitem__[ index: Int - ](self) -> ref [Self.lifetime] element_types[index.value]: + ](self) -> ref [Self.origin] element_types[index.value]: """Return a reference to an element of the pack. Parameters: diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index eb8374ef2d..1246e099fa 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -89,7 +89,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: Parameters: type: Type of value returned upon completion of the coroutine. - origins: The lifetime of the coroutine's captures. + origins: The origin of the coroutine's captures. """ var _handle: AnyCoroutine @@ -168,7 +168,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: Parameters: type: Type of value returned upon completion of the coroutine. - origins: The lifetime set of the coroutine's captures. + origins: The origin set of the coroutine's captures. """ var _handle: AnyCoroutine diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index d5d17f0abb..d6c703a0af 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -314,7 +314,7 @@ fn _try_write_int[ if value == 0: # TODO: Replace with safe digit_chars[:1] syntax. # SAFETY: - # This static lifetime is valid as long as we're using a + # This static origin is valid as long as we're using a # `StringLiteral` for `digit_chars`. var zero_char = digit_chars_array[0] diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 0c2ebadf00..0ddb24621b 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -122,9 +122,9 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_lifetime: Origin[dict_mutability].type, -](ref [_]value: _DictValueIter[K, V, dict_lifetime]) -> _DictValueIter[ - K, V, dict_lifetime, False + dict_origin: Origin[dict_mutability].type, +](ref [_]value: _DictValueIter[K, V, dict_origin]) -> _DictValueIter[ + K, V, dict_origin, False ]: """Get a reversed iterator of the input dict values. @@ -134,7 +134,7 @@ fn reversed[ K: The type of the keys in the dict. V: The type of the values in the dict. dict_mutability: Whether the reference to the dict values is mutable. - dict_lifetime: The lifetime of the dict values. + dict_origin: The origin of the dict values. Args: value: The dict values to get the reversed iterator of. @@ -149,9 +149,9 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_lifetime: Origin[dict_mutability].type, -](ref [_]value: _DictEntryIter[K, V, dict_lifetime]) -> _DictEntryIter[ - K, V, dict_lifetime, False + dict_origin: Origin[dict_mutability].type, +](ref [_]value: _DictEntryIter[K, V, dict_origin]) -> _DictEntryIter[ + K, V, dict_origin, False ]: """Get a reversed iterator of the input dict items. @@ -161,7 +161,7 @@ fn reversed[ K: The type of the keys in the dict. V: The type of the values in the dict. dict_mutability: Whether the reference to the dict items is mutable. - dict_lifetime: The lifetime of the dict items. + dict_origin: The origin of the dict items. Args: value: The dict items to get the reversed iterator of. @@ -170,6 +170,6 @@ fn reversed[ The reversed iterator of the dict items. """ var src = value.src - return _DictEntryIter[K, V, dict_lifetime, False]( + return _DictEntryIter[K, V, dict_origin, False]( src[]._reserved() - 1, 0, src ) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index f840bff519..310768304d 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -41,9 +41,9 @@ struct _SortWrapper[type: CollectionElement](CollectionElement): @always_inline fn _insertion_sort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]): +](span: Span[type, origin]): """Sort the array[start:end] slice""" var array = span.unsafe_ptr() var size = len(span) @@ -66,9 +66,9 @@ fn _insertion_sort[ @always_inline fn _quicksort_partition_right[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]) -> Int: +](span: Span[type, origin]) -> Int: var array = span.unsafe_ptr() var size = len(span) @@ -95,9 +95,9 @@ fn _quicksort_partition_right[ @always_inline fn _quicksort_partition_left[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]) -> Int: +](span: Span[type, origin]) -> Int: var array = span.unsafe_ptr() var size = len(span) @@ -121,9 +121,9 @@ fn _quicksort_partition_left[ fn _heap_sort_fix_down[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime], idx: Int): +](span: Span[type, origin], idx: Int): var array = span.unsafe_ptr() var size = len(span) var i = idx @@ -142,9 +142,9 @@ fn _heap_sort_fix_down[ @always_inline fn _heap_sort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]): +](span: Span[type, origin]): var array = span.unsafe_ptr() var size = len(span) # heapify @@ -171,9 +171,9 @@ fn _estimate_initial_height(size: Int) -> Int: @always_inline fn _delegate_small_sort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]): +](span: Span[type, origin]): var array = span.unsafe_ptr() var size = len(span) if size == 2: @@ -203,9 +203,9 @@ fn _delegate_small_sort[ @always_inline fn _quicksort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]): +](span: Span[type, origin]): var array = span.unsafe_ptr() var size = len(span) if size == 0: @@ -222,7 +222,7 @@ fn _quicksort[ var imm_interval = stack.pop() var ptr = imm_interval.unsafe_ptr() var len = len(imm_interval) - var interval = Span[type, lifetime](unsafe_ptr=ptr, len=len) + var interval = Span[type, origin](unsafe_ptr=ptr, len=len) if len <= 5: _delegate_small_sort[cmp_fn](interval) @@ -264,13 +264,13 @@ fn _quicksort[ fn _merge[ type: CollectionElement, - span_lifetime: ImmutableOrigin, - result_lifetime: MutableOrigin, //, + span_origin: ImmutableOrigin, + result_origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, ]( - span1: Span[type, span_lifetime], - span2: Span[type, span_lifetime], - result: Span[type, result_lifetime], + span1: Span[type, span_origin], + span2: Span[type, span_origin], + result: Span[type, result_origin], ): """Merge span1 and span2 into result using the given cmp_fn. The function will crash if result is not large enough to hold both span1 and span2. @@ -279,8 +279,8 @@ fn _merge[ Parameters: type: Type of the spans. - span_lifetime: Origin of the input spans. - result_lifetime: Origin of the result Span. + span_origin: Origin of the input spans. + result_origin: Origin of the result Span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -352,9 +352,9 @@ fn _stable_sort_impl[ fn _stable_sort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]): +](span: Span[type, origin]): var temp_buff = UnsafePointer[type].alloc(len(span)) var temp_buff_span = Span[type, __origin_of(temp_buff)]( unsafe_ptr=temp_buff, len=len(span) @@ -371,9 +371,9 @@ fn _stable_sort[ @always_inline fn _partition[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](span: Span[type, lifetime]) -> Int: +](span: Span[type, origin]) -> Int: var size = len(span) if size <= 1: return 0 @@ -404,9 +404,9 @@ fn _partition[ fn _partition[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, -](owned span: Span[type, lifetime], owned k: Int): +](owned span: Span[type, origin], owned k: Int): while True: var pivot = _partition[cmp_fn](span) if pivot == k: @@ -422,16 +422,16 @@ fn _partition[ fn partition[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (type, type) capturing [_] -> Bool, -](span: Span[type, lifetime], k: Int): +](span: Span[type, origin], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: type: Type of the underlying data. - lifetime: Origin of span. + origin: Origin of span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -447,15 +447,15 @@ fn partition[ fn partition[ - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (Int, Int) capturing [_] -> Bool, -](span: Span[Int, lifetime], k: Int): +](span: Span[Int, origin], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: - lifetime: Origin of span. + origin: Origin of span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -472,16 +472,16 @@ fn partition[ fn partition[ type: DType, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing [_] -> Bool, -](span: Span[Scalar[type], lifetime], k: Int): +](span: Span[Scalar[type], origin], k: Int): """Partition the input buffer inplace such that first k elements are the largest (or smallest if cmp_fn is < operator) elements. The ordering of the first k elements is undefined. Parameters: type: DType of the underlying data. - lifetime: Origin of span. + origin: Origin of span. cmp_fn: Comparison functor of (type, type) capturing [_] -> Bool type. Args: @@ -506,11 +506,11 @@ fn partition[ # Junction from public to private API fn _sort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (_SortWrapper[type], _SortWrapper[type]) capturing [_] -> Bool, *, stable: Bool = False, -](span: Span[type, lifetime]): +](span: Span[type, origin]): if len(span) <= 5: _delegate_small_sort[cmp_fn](span) return @@ -532,17 +532,17 @@ fn _sort[ # optional cmp_fn. fn sort[ type: CollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (type, type) capturing [_] -> Bool, *, stable: Bool = False, -](span: Span[type, lifetime]): +](span: Span[type, origin]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: type: CollectionElement type of the underlying data. - lifetime: Origin of span. + origin: Origin of span. cmp_fn: The comparison function. stable: Whether the sort should be stable. @@ -558,16 +558,16 @@ fn sort[ fn sort[ - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (Int, Int) capturing [_] -> Bool, *, stable: Bool = False, -](span: Span[Int, lifetime]): +](span: Span[Int, origin]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: - lifetime: Origin of span. + origin: Origin of span. cmp_fn: The comparison function. stable: Whether the sort should be stable. @@ -584,17 +584,17 @@ fn sort[ fn sort[ type: DType, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, cmp_fn: fn (Scalar[type], Scalar[type]) capturing [_] -> Bool, *, stable: Bool = False, -](span: Span[Scalar[type], lifetime]): +](span: Span[Scalar[type], origin]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: type: DType type of the underlying data. - lifetime: Origin of span. + origin: Origin of span. cmp_fn: The comparison function. stable: Whether the sort should be stable. @@ -612,15 +612,15 @@ fn sort[ fn sort[ - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, *, stable: Bool = False, -](span: Span[Int, lifetime]): +](span: Span[Int, origin]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: - lifetime: Origin of span. + origin: Origin of span. stable: Whether the sort should be stable. Args: @@ -636,16 +636,16 @@ fn sort[ fn sort[ type: DType, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, *, stable: Bool = False, -](span: Span[Scalar[type], lifetime]): +](span: Span[Scalar[type], origin]): """Sort the list inplace. The function doesn't return anything, the list is updated inplace. Parameters: type: CollectionElement type of the underlying data. - lifetime: Origin of span. + origin: Origin of span. stable: Whether the sort should be stable. Args: @@ -661,15 +661,15 @@ fn sort[ fn sort[ type: ComparableCollectionElement, - lifetime: MutableOrigin, //, + origin: MutableOrigin, //, *, stable: Bool = False, -](span: Span[type, lifetime]): +](span: Span[type, origin]): """Sort list of the order comparable elements in-place. Parameters: type: The order comparable collection element type. - lifetime: Origin of span. + origin: Origin of span. stable: Whether the sort should be stable. Args: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 389c2c0e8e..963b605af4 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -172,7 +172,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( storage_kgen_ptr ) - # Use an immortal mut reference, which converts to self's lifetime. + # Use an immortal mut reference, which converts to self's origin. return UnsafePointer(elt_kgen_ptr)[] # TODO(#38268): Remove this method when references and parameter expressions diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 432bddd9ed..70d70a664e 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -30,7 +30,7 @@ alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.origin<0>` alias MutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.origin<1>` """The mutable origin that might access any memory value.""" -# Static constants are a named subset of the global lifetime. +# Static constants are a named subset of the global origin. alias StaticConstantOrigin = __mlir_attr[ `#lit.origin.field<`, `#lit.static.origin : !lit.origin<0>`, @@ -39,17 +39,17 @@ alias StaticConstantOrigin = __mlir_attr[ """An origin for strings and other always-immutable static constants.""" alias OriginSet = __mlir_type.`!lit.origin.set` -"""A set of lifetime parameters.""" +"""A set of origin parameters.""" # Helper to build a value of !lit.origin type. # TODO: Should be a parametric alias. struct Origin[is_mutable: Bool]: - """This represents a lifetime reference of potentially parametric type. + """This represents a origin reference of potentially parametric type. TODO: This should be replaced with a parametric type alias. Parameters: - is_mutable: Whether the lifetime reference is mutable. + is_mutable: Whether the origin reference is mutable. """ alias type = __mlir_type[ diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index daca2fa483..07ffbfd6ce 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -60,7 +60,7 @@ struct _DictEntryIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable DictEntry references. @@ -69,16 +69,16 @@ struct _DictEntryIter[ dict_mutability: Whether the reference to the dictionary is mutable. K: The key type of the elements in the dictionary. V: The value type of the elements in the dictionary. - dict_lifetime: The lifetime of the List + dict_origin: The origin of the List forward: The iteration direction. `False` is backwards. """ var index: Int var seen: Int - var src: Pointer[Dict[K, V], dict_lifetime] + var src: Pointer[Dict[K, V], dict_origin] fn __init__( - inout self, index: Int, seen: Int, ref [dict_lifetime]dict: Dict[K, V] + inout self, index: Int, seen: Int, ref [dict_origin]dict: Dict[K, V] ): self.index = index self.seen = seen @@ -119,7 +119,7 @@ struct _DictKeyIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability].type, forward: Bool = True, ]: """Iterator over immutable Dict key references. @@ -128,11 +128,11 @@ struct _DictKeyIter[ dict_mutability: Whether the reference to the vector is mutable. K: The key type of the elements in the dictionary. V: The value type of the elements in the dictionary. - dict_lifetime: The lifetime of the List + dict_origin: The origin of the List forward: The iteration direction. `False` is backwards. """ - alias dict_entry_iter = _DictEntryIter[K, V, dict_lifetime, forward] + alias dict_entry_iter = _DictEntryIter[K, V, dict_origin, forward] var iter: Self.dict_entry_iter @@ -157,7 +157,7 @@ struct _DictValueIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_lifetime: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability].type, forward: Bool = True, ]: """Iterator over Dict value references. These are mutable if the dict @@ -167,21 +167,21 @@ struct _DictValueIter[ dict_mutability: Whether the reference to the vector is mutable. K: The key type of the elements in the dictionary. V: The value type of the elements in the dictionary. - dict_lifetime: The lifetime of the List + dict_origin: The origin of the List forward: The iteration direction. `False` is backwards. """ - alias ref_type = Pointer[V, dict_lifetime] + alias ref_type = Pointer[V, dict_origin] - var iter: _DictEntryIter[K, V, dict_lifetime, forward] + var iter: _DictEntryIter[K, V, dict_origin, forward] fn __iter__(self) -> Self: return self - fn __reversed__(self) -> _DictValueIter[K, V, dict_lifetime, False]: + fn __reversed__(self) -> _DictValueIter[K, V, dict_origin, False]: var src = self.iter.src return _DictValueIter( - _DictEntryIter[K, V, dict_lifetime, False]( + _DictEntryIter[K, V, dict_origin, False]( src[]._reserved() - 1, 0, src ) ) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index f2977f0144..97f253fd50 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -31,7 +31,7 @@ struct _InlineListIter[ list_mutability: Bool, //, T: CollectionElementNew, capacity: Int, - list_lifetime: Origin[list_mutability].type, + list_origin: Origin[list_mutability].type, forward: Bool = True, ]: """Iterator for InlineList. @@ -40,14 +40,14 @@ struct _InlineListIter[ list_mutability: Whether the reference to the list is mutable. T: The type of the elements in the list. capacity: The maximum number of elements that the list can hold. - list_lifetime: The lifetime of the List + list_origin: The origin of the List forward: The iteration direction. `False` is backwards. """ alias list_type = InlineList[T, capacity] var index: Int - var src: Pointer[Self.list_type, list_lifetime] + var src: Pointer[Self.list_type, list_origin] fn __iter__(self) -> Self: return self diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index c90f2a1ac2..5ba17d6f16 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -38,7 +38,7 @@ struct _ListIter[ list_mutability: Bool, //, T: CollectionElement, hint_trivial_type: Bool, - list_lifetime: Origin[list_mutability].type, + list_origin: Origin[list_mutability].type, forward: Bool = True, ]: """Iterator for List. @@ -48,21 +48,21 @@ struct _ListIter[ T: The type of the elements in the list. hint_trivial_type: Set to `True` if the type `T` is trivial, this is not mandatory, but it helps performance. Will go away in the future. - list_lifetime: The lifetime of the List + list_origin: The origin of the List forward: The iteration direction. `False` is backwards. """ alias list_type = List[T, hint_trivial_type] var index: Int - var src: Pointer[Self.list_type, list_lifetime] + var src: Pointer[Self.list_type, list_origin] fn __iter__(self) -> Self: return self fn __next__( inout self, - ) -> Pointer[T, list_lifetime]: + ) -> Pointer[T, list_origin]: @parameter if forward: self.index += 1 diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index aaff7b6c0e..d07383f165 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -132,7 +132,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): self._inner.destroy_pointee() self._inner.free() - # FIXME: The lifetime returned for this is currently self lifetime, which + # FIXME: The origin returned for this is currently self origin, which # keeps the Arc object alive as long as there are references into it. That # said, this isn't really the right modeling, we need hierarchical origins # to model the mutability and invalidation of the returned reference @@ -147,7 +147,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): """Returns a mutable reference to the managed value. Parameters: - self_life: The lifetime of self. + self_life: The origin of self. Returns: A reference to the managed value. diff --git a/stdlib/src/memory/box.mojo b/stdlib/src/memory/box.mojo index e6058bb4c8..cefcf5ce1d 100644 --- a/stdlib/src/memory/box.mojo +++ b/stdlib/src/memory/box.mojo @@ -17,7 +17,7 @@ struct Box[T: AnyType]: """A safe, owning, smart pointer. This smart pointer is designed for cases where there is clear ownership - of the underlying data, and restricts access to it through the lifetime + of the underlying data, and restricts access to it through the origin system such that no more than one mutable alias for the underlying data may exist. @@ -110,7 +110,7 @@ struct Box[T: AnyType]: # This should have a widening conversion here that allows # the mutable ref that is always (potentially unsafely) # returned from UnsafePointer to be guarded behind the - # aliasing guarantees of the lifetime system here. + # aliasing guarantees of the origin system here. # All of the magic happens above in the function signature return self._inner[] diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 731099b5c8..b26a61de17 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -234,8 +234,8 @@ fn memcpy[ """ var n = count * sizeof[dest.type]() _memcpy_impl( - dest.bitcast[Int8, lifetime=MutableAnyOrigin](), - src.bitcast[Int8, lifetime=MutableAnyOrigin](), + dest.bitcast[Int8, origin=MutableAnyOrigin](), + src.bitcast[Int8, origin=MutableAnyOrigin](), n, ) diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 93a72393fd..3a3adb9a78 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -273,7 +273,7 @@ struct AddressSpace(EqualityComparable): struct Pointer[ is_mutable: Bool, //, type: AnyType, - lifetime: Origin[is_mutable].type, + origin: Origin[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, ](CollectionElementNew, Stringable): """Defines a non-nullable safe pointer. @@ -281,7 +281,7 @@ struct Pointer[ Parameters: is_mutable: Whether the pointee data may be mutated through this. type: Type of the underlying data. - lifetime: The lifetime of the pointer. + origin: The origin of the pointer. address_space: The address space of the pointee data. """ @@ -289,7 +289,7 @@ struct Pointer[ `!lit.ref<`, type, `, `, - lifetime, + origin, `, `, address_space._value.value, `>`, @@ -313,9 +313,7 @@ struct Pointer[ @staticmethod @always_inline("nodebug") - fn address_of( - ref [lifetime, address_space._value.value]value: type - ) -> Self: + fn address_of(ref [origin, address_space._value.value]value: type) -> Self: """Constructs a Pointer from a reference to a value. Args: @@ -341,7 +339,7 @@ struct Pointer[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__(self) -> ref [lifetime, address_space._value.value] type: + fn __getitem__(self) -> ref [origin, address_space._value.value] type: """Enable subscript syntax `ptr[]` to access the element. Returns: @@ -352,8 +350,8 @@ struct Pointer[ # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for - # accesses to the lifetime. - @__unsafe_disable_nested_lifetime_exclusivity + # accesses to the origin. + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __eq__(self, rhs: Pointer[type, _, address_space]) -> Bool: """Returns True if the two pointers are equal. @@ -368,7 +366,7 @@ struct Pointer[ rhs[] ) - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __ne__(self, rhs: Pointer[type, _, address_space]) -> Bool: """Returns True if the two pointers are not equal. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index e545408d2a..d10cbc6984 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -53,7 +53,7 @@ struct UnsafePointer[ type: AnyType, address_space: AddressSpace = AddressSpace.GENERIC, alignment: Int = _default_alignment[type](), - lifetime: Origin[True].type = MutableAnyOrigin, + origin: Origin[True].type = MutableAnyOrigin, ]( ImplicitlyBoolable, CollectionElement, @@ -69,7 +69,7 @@ struct UnsafePointer[ type: The type the pointer points to. address_space: The address space associated with the UnsafePointer allocated memory. alignment: The minimum alignment of this pointer known statically. - lifetime: The lifetime of the memory being addressed. + origin: The origin of the memory being addressed. """ # ===-------------------------------------------------------------------===# @@ -143,7 +143,7 @@ struct UnsafePointer[ type, address_space, 1, - # TODO: Propagate lifetime of the argument. + # TODO: Propagate origin of the argument. ] as result: """Gets the address of the argument. @@ -181,7 +181,7 @@ struct UnsafePointer[ @always_inline fn __getitem__( self, - ) -> ref [lifetime, address_space._value.value] type: + ) -> ref [origin, address_space._value.value] type: """Return a reference to the underlying data. Returns: @@ -189,10 +189,10 @@ struct UnsafePointer[ """ # We're unsafe, so we can have unsafe things. - alias _ref_type = Pointer[type, lifetime, address_space] + alias _ref_type = Pointer[type, origin, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( - UnsafePointer[type, address_space, alignment, lifetime]( + UnsafePointer[type, address_space, alignment, origin]( self ).address ) @@ -216,7 +216,7 @@ struct UnsafePointer[ @always_inline fn __getitem__[ IntLike: IntLike, // - ](self, offset: IntLike) -> ref [lifetime, address_space._value.value] type: + ](self, offset: IntLike) -> ref [origin, address_space._value.value] type: """Return a reference to the underlying data, offset by the given index. Parameters: @@ -287,8 +287,8 @@ struct UnsafePointer[ # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for - # accesses to the lifetime. - @__unsafe_disable_nested_lifetime_exclusivity + # accesses to the origin. + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: """Returns True if the two pointers are equal. @@ -301,7 +301,7 @@ struct UnsafePointer[ """ return int(self) == int(rhs) - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __ne__(self, rhs: Self) -> Bool: """Returns True if the two pointers are not equal. @@ -314,7 +314,7 @@ struct UnsafePointer[ """ return not (self == rhs) - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __lt__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a lower address than rhs. @@ -327,7 +327,7 @@ struct UnsafePointer[ """ return int(self) < int(rhs) - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __le__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a lower than or equal @@ -341,7 +341,7 @@ struct UnsafePointer[ """ return int(self) <= int(rhs) - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __gt__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a higher address than rhs. @@ -354,7 +354,7 @@ struct UnsafePointer[ """ return int(self) > int(rhs) - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline("nodebug") fn __ge__(self, rhs: Self) -> Bool: """Returns True if this pointer represents a higher than or equal @@ -427,7 +427,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn as_noalias_ptr( self, - ) -> UnsafePointer[type, address_space, alignment, lifetime]: + ) -> UnsafePointer[type, address_space, alignment, origin]: """Cast the pointer to a new pointer that is known not to locally alias any other pointer. In other words, the pointer transitively does not alias any other memory value declared in the local function context. @@ -882,15 +882,15 @@ struct UnsafePointer[ /, address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, - lifetime: Origin[True].type = Self.lifetime, - ](self) -> UnsafePointer[T, address_space, alignment, lifetime]: + origin: Origin[True].type = Self.origin, + ](self) -> UnsafePointer[T, address_space, alignment, origin]: """Bitcasts a UnsafePointer to a different type. Parameters: T: The target type. address_space: The address space of the result. alignment: Alignment of the destination pointer. - lifetime: Origin of the destination pointer. + origin: Origin of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, @@ -908,15 +908,15 @@ struct UnsafePointer[ /, address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, - lifetime: Origin[True].type = Self.lifetime, - ](self) -> UnsafePointer[Scalar[T], address_space, alignment, lifetime]: + origin: Origin[True].type = Self.origin, + ](self) -> UnsafePointer[Scalar[T], address_space, alignment, origin]: """Bitcasts a UnsafePointer to a different type. Parameters: T: The target type. address_space: The address space of the result. alignment: Alignment of the destination pointer. - lifetime: Origin of the destination pointer. + origin: Origin of the destination pointer. Returns: A new UnsafePointer object with the specified type and the same address, @@ -949,7 +949,7 @@ struct UnsafePointer[ The pointer must not be null, and the pointer memory location is assumed to contain a valid initialized instance of `T`. - This performs a _consuming_ move, ending the lifetime of the value stored + This performs a _consuming_ move, ending the origin of the value stored in this pointer memory location. Subsequent reads of this pointer are not valid. If a new valid value is stored using `init_pointee_move()`, then reading from this pointer becomes valid again. diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index fc60daeaed..8973c4d176 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -179,7 +179,7 @@ struct TypedPythonObject[type_hint: StringLiteral]( # ===-------------------------------------------------------------------===# # TODO: - # This should have lifetime, or we should do this with a context + # This should have origin, or we should do this with a context # manager, to prevent use after ASAP destruction. fn unsafe_as_py_object_ptr(self) -> PyObjectPtr: """Get the underlying PyObject pointer. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index bab05cc62c..0941b210d6 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -29,7 +29,7 @@ from builtin.builtin_list import _lit_mut_cast struct _SpanIter[ is_mutable: Bool, //, T: CollectionElement, - lifetime: Origin[is_mutable].type, + origin: Origin[is_mutable].type, forward: Bool = True, ]: """Iterator for Span. @@ -37,12 +37,12 @@ struct _SpanIter[ Parameters: is_mutable: Whether the reference to the span is mutable. T: The type of the elements in the span. - lifetime: The lifetime of the Span. + origin: The origin of the Span. forward: The iteration direction. `False` is backwards. """ var index: Int - var src: Span[T, lifetime] + var src: Span[T, origin] @always_inline fn __iter__(self) -> Self: @@ -51,7 +51,7 @@ struct _SpanIter[ @always_inline fn __next__( inout self, - ) -> Pointer[T, lifetime]: + ) -> Pointer[T, origin]: @parameter if forward: self.index += 1 @@ -77,14 +77,14 @@ struct _SpanIter[ struct Span[ is_mutable: Bool, //, T: CollectionElement, - lifetime: Origin[is_mutable].type, + origin: Origin[is_mutable].type, ](CollectionElementNew): """A non owning view of contiguous data. Parameters: is_mutable: Whether the span is mutable. T: The type of the elements in the span. - lifetime: The lifetime of the Span. + origin: The origin of the Span. """ # Field @@ -117,7 +117,7 @@ struct Span[ self._len = other._len @always_inline - fn __init__(inout self, ref [lifetime]list: List[T, *_]): + fn __init__(inout self, ref [origin]list: List[T, *_]): """Construct a Span from a List. Args: @@ -129,7 +129,7 @@ struct Span[ @always_inline fn __init__[ size: Int, // - ](inout self, ref [lifetime]array: InlineArray[T, size]): + ](inout self, ref [origin]array: InlineArray[T, size]): """Construct a Span from an InlineArray. Parameters: @@ -147,7 +147,7 @@ struct Span[ # ===------------------------------------------------------------------===# @always_inline - fn __getitem__(self, idx: Int) -> ref [lifetime] T: + fn __getitem__(self, idx: Int) -> ref [origin] T: """Get a reference to an element in the span. Args: @@ -191,7 +191,7 @@ struct Span[ return res @always_inline - fn __iter__(self) -> _SpanIter[T, lifetime]: + fn __iter__(self) -> _SpanIter[T, origin]: """Get an iterator over the elements of the span. Returns: @@ -226,7 +226,7 @@ struct Span[ return self._data - fn as_ref(self) -> Pointer[T, lifetime]: + fn as_ref(self) -> Pointer[T, origin]: """ Gets a Pointer to the first element of this slice. @@ -234,17 +234,17 @@ struct Span[ A Pointer pointing at the first element of this slice. """ - return Pointer[T, lifetime].address_of(self._data[0]) + return Pointer[T, origin].address_of(self._data[0]) @always_inline fn copy_from[ - lifetime: MutableOrigin, // - ](self: Span[T, lifetime], other: Span[T, _]): + origin: MutableOrigin, // + ](self: Span[T, origin], other: Span[T, _]): """ Performs an element wise copy from all elements of `other` into all elements of `self`. Parameters: - lifetime: The inferred mutable lifetime of the data within the Span. + origin: The inferred mutable origin of the data within the Span. Args: other: The Span to copy all elements from. @@ -264,11 +264,11 @@ struct Span[ # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for - # accesses to the lifetime. - @__unsafe_disable_nested_lifetime_exclusivity + # accesses to the origin. + @__unsafe_disable_nested_origin_exclusivity fn __eq__[ T: EqualityComparableCollectionElement, // - ](self: Span[T, lifetime], rhs: Span[T]) -> Bool: + ](self: Span[T, origin], rhs: Span[T]) -> Bool: """Verify if span is equal to another span. Parameters: @@ -297,7 +297,7 @@ struct Span[ @always_inline fn __ne__[ T: EqualityComparableCollectionElement, // - ](self: Span[T, lifetime], rhs: Span[T]) -> Bool: + ](self: Span[T, origin], rhs: Span[T]) -> Bool: """Verify if span is not equal to another span. Parameters: @@ -312,12 +312,12 @@ struct Span[ """ return not self == rhs - fn fill[lifetime: MutableOrigin, //](self: Span[T, lifetime], value: T): + fn fill[origin: MutableOrigin, //](self: Span[T, origin], value: T): """ Fill the memory that a span references with a given value. Parameters: - lifetime: The inferred mutable lifetime of the data within the Span. + origin: The inferred mutable origin of the data within the Span. Args: value: The value to assign to each element. @@ -325,13 +325,13 @@ struct Span[ for element in self: element[] = value - fn get_immutable(self) -> Span[T, _lit_mut_cast[lifetime, False].result]: + fn get_immutable(self) -> Span[T, _lit_mut_cast[origin, False].result]: """ Return an immutable version of this span. Returns: A span covering the same elements, but without mutability. """ - return Span[T, _lit_mut_cast[lifetime, False].result]( + return Span[T, _lit_mut_cast[origin, False].result]( unsafe_ptr=self._data, len=self._len ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index da4f8831af..7ed75f46c4 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -125,14 +125,14 @@ fn _is_newline_start( @value struct _StringSliceIter[ is_mutable: Bool, //, - lifetime: Origin[is_mutable].type, + origin: Origin[is_mutable].type, forward: Bool = True, ]: """Iterator for StringSlice Parameters: is_mutable: Whether the slice is mutable. - lifetime: The lifetime of the underlying string data. + origin: The origin of the underlying string data. forward: The iteration direction. `False` is backwards. """ @@ -155,7 +155,7 @@ struct _StringSliceIter[ fn __iter__(self) -> Self: return self - fn __next__(inout self) -> StringSlice[lifetime]: + fn __next__(inout self) -> StringSlice[origin]: @parameter if forward: var byte_len = 1 @@ -165,7 +165,7 @@ struct _StringSliceIter[ byte_len = int(byte_type) self.continuation_bytes -= byte_len - 1 self.index += byte_len - return StringSlice[lifetime]( + return StringSlice[origin]( unsafe_from_utf8_ptr=self.ptr + (self.index - byte_len), len=byte_len, ) @@ -180,7 +180,7 @@ struct _StringSliceIter[ byte_type = _utf8_byte_type(b) self.continuation_bytes -= byte_len - 1 self.index -= byte_len - return StringSlice[lifetime]( + return StringSlice[origin]( unsafe_from_utf8_ptr=self.ptr + self.index, len=byte_len ) @@ -198,7 +198,7 @@ struct _StringSliceIter[ struct StringSlice[ is_mutable: Bool, //, - lifetime: Origin[is_mutable].type, + origin: Origin[is_mutable].type, ](Stringable, Sized, Formattable): """ A non-owning view to encoded string data. @@ -208,10 +208,10 @@ struct StringSlice[ Parameters: is_mutable: Whether the slice is mutable. - lifetime: The lifetime of the underlying string data. + origin: The origin of the underlying string data. """ - var _slice: Span[UInt8, lifetime] + var _slice: Span[UInt8, origin] # ===------------------------------------------------------------------===# # Initializers @@ -226,8 +226,8 @@ struct StringSlice[ Args: lit: The literal to construct this string slice from. """ - # Since a StringLiteral has static lifetime, it will outlive - # whatever arbitrary `lifetime` the user has specified they need this + # Since a StringLiteral has static origin, it will outlive + # whatever arbitrary `origin` the user has specified they need this # slice to live for. # SAFETY: # StringLiteral is guaranteed to use UTF-8 encoding. @@ -243,7 +243,7 @@ struct StringSlice[ ) @always_inline - fn __init__(inout self, *, owned unsafe_from_utf8: Span[UInt8, lifetime]): + fn __init__(inout self, *, owned unsafe_from_utf8: Span[UInt8, origin]): """ Construct a new StringSlice from a sequence of UTF-8 encoded bytes. @@ -263,7 +263,7 @@ struct StringSlice[ Safety: - `unsafe_from_utf8_strref` MUST point to data that is valid for - `lifetime`. + `origin`. - `unsafe_from_utf8_strref` MUST be valid UTF-8 encoded data. Args: @@ -271,7 +271,7 @@ struct StringSlice[ """ var strref = unsafe_from_utf8_strref - var byte_slice = Span[UInt8, lifetime]( + var byte_slice = Span[UInt8, origin]( unsafe_ptr=strref.unsafe_ptr(), len=len(strref), ) @@ -293,14 +293,14 @@ struct StringSlice[ - `unsafe_from_utf8_ptr` MUST point to at least `len` bytes of valid UTF-8 encoded data. - `unsafe_from_utf8_ptr` must point to data that is live for the - duration of `lifetime`. + duration of `origin`. Args: unsafe_from_utf8_ptr: A pointer to a sequence of bytes encoded in UTF-8. len: The number of bytes of encoded data. """ - var byte_slice = Span[UInt8, lifetime]( + var byte_slice = Span[UInt8, origin]( unsafe_ptr=unsafe_from_utf8_ptr, len=len, ) @@ -354,8 +354,8 @@ struct StringSlice[ # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for - # accesses to the lifetime. - @__unsafe_disable_nested_lifetime_exclusivity + # accesses to the origin. + @__unsafe_disable_nested_origin_exclusivity fn __eq__(self, rhs: StringSlice) -> Bool: """Verify if a string slice is equal to another string slice. @@ -401,7 +401,7 @@ struct StringSlice[ """ return self == rhs.as_string_slice() - @__unsafe_disable_nested_lifetime_exclusivity + @__unsafe_disable_nested_origin_exclusivity @always_inline fn __ne__(self, rhs: StringSlice) -> Bool: """Verify if span is not equal to another string slice. @@ -438,23 +438,23 @@ struct StringSlice[ """ return not self == rhs - fn __iter__(self) -> _StringSliceIter[lifetime]: + fn __iter__(self) -> _StringSliceIter[origin]: """Iterate over elements of the string slice, returning immutable references. Returns: An iterator of references to the string elements. """ - return _StringSliceIter[lifetime]( + return _StringSliceIter[origin]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) - fn __reversed__(self) -> _StringSliceIter[lifetime, False]: + fn __reversed__(self) -> _StringSliceIter[origin, False]: """Iterate backwards over the string, returning immutable references. Returns: A reversed iterator of references to the string elements. """ - return _StringSliceIter[lifetime, forward=False]( + return _StringSliceIter[origin, forward=False]( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) @@ -463,7 +463,7 @@ struct StringSlice[ # ===------------------------------------------------------------------===# @always_inline - fn as_bytes(self) -> Span[UInt8, lifetime]: + fn as_bytes(self) -> Span[UInt8, origin]: """Get the sequence of encoded bytes as a slice of the underlying string. Returns: diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index cf21e78a9c..2a911ab6c4 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -154,7 +154,7 @@ fn test_slice_eq() raises: # eq - # FIXME: the lifetime of the StringSlice lifetime should be the data in the + # FIXME: the origin of the StringSlice origin should be the data in the # string, not the string itself. # assert_true(str1.as_string_slice().__eq__(str1)) assert_true(str1.as_string_slice().__eq__(str2)) From 0efca360cfab2cea59e12af7cdfe9ead247c8489 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 12 Oct 2024 16:48:15 -0700 Subject: [PATCH 1739/2019] [******][GPU] Skip the complicated branching for memcpy on GPUs MODULAR_ORIG_COMMIT_REV_ID: c0abd11a4447b4c7ba741c578f352981edd61f8a --- stdlib/src/memory/memory.mojo | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index b26a61de17..aedaca4d96 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -27,6 +27,7 @@ from sys import ( triple_is_nvidia_cuda, external_call, simdwidthof, + simdbitwidth, _libc as libc, ) from collections import Optional @@ -153,6 +154,17 @@ fn _memcpy_impl( src_data: The source pointer. n: The number of bytes to copy. """ + + @parameter + if triple_is_nvidia_cuda(): + alias chunk_size = simdbitwidth() + var vector_end = _align_down(n, chunk_size) + for i in range(0, vector_end, chunk_size): + dest_data.store(i, src_data.load[width=chunk_size](i)) + for i in range(vector_end, n): + dest_data.store(i, src_data.load(i)) + return + if n < 5: if n == 0: return @@ -202,16 +214,13 @@ fn _memcpy_impl( # ) # return - var dest_ptr = dest_data.bitcast[Int8]() - var src_ptr = src_data.bitcast[Int8]() - # Copy in 32-byte chunks. alias chunk_size = 32 var vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): - dest_ptr.store(i, src_ptr.load[width=chunk_size](i)) + dest_data.store(i, src_data.load[width=chunk_size](i)) for i in range(vector_end, n): - dest_ptr.store(i, src_ptr.load(i)) + dest_data.store(i, src_data.load(i)) @always_inline From 87bf96862e6549a2b25dfedf87bc6e12fcad0010 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Sun, 13 Oct 2024 15:59:50 -0600 Subject: [PATCH 1740/2019] [External] [stdlib] add AsBytes trait for a much faster String.join (#49038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] add AsBytes trait for a much faster String.join Calculate the total size of the joined string before joining, to reserve the buffer with the right capacity and prevent many syscalls for allocating memory. ## Benchmark ```mojo from time import now fn main(): l = List[String]() for i in range(100_000): l.append(str(i)) start = now() s = String(",").join(l) end = now() print('Len: ', len(s), 'Time: ', (end - start) / 1_000_000_000, 'seconds') ``` ### Before ``` ❯ mojo bench_join.mojo Len: 588889 Time: 1.792499018 seconds ``` ### After ``` ❯ mojo bench_join.mojo Len: 588889 Time: 0.001283047 seconds ``` Resulting in a ~1300x speedup, roughly as fast as the Python implementation (written in C) --------- Co-authored-by: Manuel Saelices Co-authored-by: Jack Clayton Closes modularml/mojo#3485 MODULAR_ORIG_COMMIT_REV_ID: fe779704e9e9f6fc9550eaf7c3f622cae727a550 --- docs/changelog.md | 9 ++- stdlib/src/builtin/string_literal.mojo | 31 +++++---- stdlib/src/builtin/value.mojo | 12 ++++ stdlib/src/collections/string.mojo | 84 ++++++++++++++++++++---- stdlib/src/prelude/__init__.mojo | 3 +- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/span.mojo | 18 +++++ stdlib/test/collections/test_string.mojo | 3 + 8 files changed, 132 insertions(+), 30 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 527e4fa0f9..80ab99591e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -222,9 +222,6 @@ what we publish. - The VS Code extension now allows selecting a default SDK when multiple are available. -- `String.as_bytes_slice()` is renamed to `String.as_bytes_span()` since it - returns a `Span` and not a `StringSlice`. - - The flag for turning on asserts has changed, e.g. to enable all checks: ```bash @@ -261,8 +258,10 @@ what we publish. `IndexList`. The datastructure now allows one to specify the index bitwidth of the elements along with whether the underlying indices are signed or unsigned. -- `String.as_bytes()` now returns a `Span[UInt8]` instead of a `List[Int8]`. The - old behavior can be achieved by using `List(s.as_bytes())`. +- A new trait has been added `AsBytes` to enable taking a `Span[UInt8]` of a + type with `s.as_bytes()`. `String.as_bytes` and `String.as_bytes_slice` have + been consolidated under `s.as_bytes` to return a `Span[UInt8]`, you can convert + it to a `List` if you require a copy with `List(s.as_bytes())`. - `Lifetime` and related types has been renamed to `Origin` in the standard library to better clarify that parameters of this type indicate where a diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 7198bfc473..025ad518d7 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -41,9 +41,9 @@ struct StringLiteral( IntableRaising, KeyElement, Representable, - Sized, Stringable, FloatableRaising, + BytesCollectionElement, _HashableWithHasher, ): """This type represents a string literal. @@ -386,6 +386,23 @@ struct StringLiteral( len=self.byte_length(), ) + @always_inline + fn as_bytes(ref [_]self) -> Span[UInt8, __origin_of(self)]: + """Returns a contiguous slice of the bytes owned by this string. + + Returns: + A contiguous slice pointing to the bytes owned by this string. + + Notes: + This does not include the trailing null terminator. + """ + + # Does NOT include the NUL terminator. + return Span[UInt8, __origin_of(self)]( + unsafe_ptr=self.unsafe_ptr(), + len=self.byte_length(), + ) + fn format_to(self, inout writer: Formatter): """ Formats this string literal to the provided formatter. @@ -447,17 +464,7 @@ struct StringLiteral( Returns: The joined string. """ - var result: String = "" - var is_first = True - - for e in elems: - if is_first: - is_first = False - else: - result += self - result += str(e[]) - - return result + return str(self).join(elems) fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]: """Split the string literal by a separator. diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 2ead21c4dd..74e033330b 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -221,6 +221,18 @@ trait StringableCollectionElement(CollectionElement, Stringable): pass +trait BytesCollectionElement(CollectionElement, AsBytes): + """The BytesCollectionElement trait denotes a trait composition + of the `CollectionElement` and `AsBytes`. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `CollectionElement & AsBytes` in the parameter. + """ + + pass + + trait EqualityComparableCollectionElement( CollectionElement, EqualityComparable ): diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index f71e881985..b8c89988f8 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -24,6 +24,7 @@ from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy from python import PythonObject +from sys.intrinsics import _type_is_eq from hashlib._hasher import _HashableWithHasher, _Hasher from utils import ( @@ -691,6 +692,7 @@ fn isprintable(c: UInt8) -> Bool: struct String( Sized, Stringable, + AsBytes, Representable, IntableRaising, KeyElement, @@ -1286,7 +1288,7 @@ struct String( curr += self + str(elems[i]) return curr - fn join[*Types: Stringable](self, *elems: *Types) -> String: + fn join[*Types: Formattable](self, *elems: *Types) -> String: """Joins string elements using the current string as a delimiter. Parameters: @@ -1300,15 +1302,16 @@ struct String( """ var result: String = "" + var formatter = result._unsafe_to_formatter() var is_first = True @parameter - fn add_elt[T: Stringable](a: T): + fn add_elt[T: Formattable](a: T): if is_first: is_first = False else: - result += self - result += str(a) + self.format_to(formatter) + a.format_to(formatter) elems.each[add_elt]() _ = is_first @@ -1326,17 +1329,76 @@ struct String( Returns: The joined string. """ - var result: String = "" - var is_first = True - for e in elems: + # TODO(#3403): Simplify this when the linked conditional conformance + # feature is added. Runs a faster algorithm if the concrete types are + # able to be converted to a span of bytes. + @parameter + if _type_is_eq[T, String](): + return self.fast_join(rebind[List[String]](elems)) + elif _type_is_eq[T, StringLiteral](): + return self.fast_join(rebind[List[StringLiteral]](elems)) + # FIXME(#3597): once StringSlice conforms to CollectionElement trait: + # if _type_is_eq[T, StringSlice](): + # return self.fast_join(rebind[List[StringSlice]](elems)) + else: + var result: String = "" + var is_first = True + + for e in elems: + if is_first: + is_first = False + else: + result += self + result += str(e[]) + + return result + + fn fast_join[ + T: BytesCollectionElement, //, + ](self, elems: List[T, *_]) -> String: + """Joins string elements using the current string as a delimiter. + + Parameters: + T: The types of the elements. + + Args: + elems: The input values. + + Returns: + The joined string. + """ + var n_elems = len(elems) + if n_elems == 0: + return String("") + var len_self = self.byte_length() + var len_elems = 0 + # Calculate the total size of the elements to join beforehand + # to prevent alloc syscalls as we know the buffer size. + # This can hugely improve the performance on large lists + for e_ref in elems: + len_elems += len(e_ref[].as_bytes()) + var capacity = len_self * (n_elems - 1) + len_elems + var buf = Self._buffer_type(capacity=capacity) + var self_ptr = self.unsafe_ptr() + var ptr = buf.unsafe_ptr() + var offset = 0 + var i = 0 + var is_first = True + while i < n_elems: if is_first: is_first = False else: - result += self - result += str(e[]) - - return result + memcpy(dest=ptr + offset, src=self_ptr, count=len_self) + offset += len_self + var e = elems[i].as_bytes() + var e_len = len(e) + memcpy(dest=ptr + offset, src=e.unsafe_ptr(), count=e_len) + offset += e_len + i += 1 + buf.size = capacity + buf.append(0) + return String(buf^) fn _strref_dangerous(self) -> StringRef: """ diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 5f2b9a5cf9..b7941d92fb 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -91,6 +91,7 @@ from builtin.value import ( Defaultable, CollectionElement, CollectionElementNew, + BytesCollectionElement, StringableCollectionElement, EqualityComparableCollectionElement, ComparableCollectionElement, @@ -131,4 +132,4 @@ from collections.string import ( ) from hashlib.hash import hash, Hashable from memory import Pointer, AddressSpace -from utils import Formattable, Formatter +from utils import AsBytes, Formattable, Formatter diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index df544d0711..f0290277cf 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -15,7 +15,7 @@ from .index import Index, IndexList, product from .inline_string import InlineString from .loop import unroll -from .span import Span +from .span import AsBytes, Span from .static_tuple import StaticTuple from .stringref import StringRef from .string_slice import StaticString, StringSlice diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 0941b210d6..24ca92b2a7 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -25,6 +25,24 @@ from memory import Pointer, UnsafePointer from builtin.builtin_list import _lit_mut_cast +trait AsBytes: + """ + The `AsBytes` trait denotes a type that can be returned as a immutable byte + span. + """ + + fn as_bytes(ref [_]self) -> Span[UInt8, __origin_of(self)]: + """Returns a contiguous slice of the bytes owned by this string. + + Returns: + A contiguous slice pointing to the bytes owned by this string. + + Notes: + This does not include the trailing null terminator. + """ + ... + + @value struct _SpanIter[ is_mutable: Bool, //, diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index a664d321df..a03dffa9f2 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -215,6 +215,9 @@ def test_string_join(): var s5 = String(",").join(List[UInt8](1)) assert_equal(s5, "1") + var s6 = String(",").join(List[String]("1", "2", "3")) + assert_equal(s6, "1,2,3") + def test_string_literal_join(): var s2 = ",".join(List[UInt8](1, 2, 3)) From 579bd7a69a1439996e6632cca388474b2e63f402 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sun, 13 Oct 2024 18:36:32 -0700 Subject: [PATCH 1741/2019] Accidently removed in prior PR MODULAR_ORIG_COMMIT_REV_ID: 30bbad12f31da9a585944e36255a57898950f240 --- stdlib/src/builtin/string_literal.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 025ad518d7..65199a814f 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -41,6 +41,7 @@ struct StringLiteral( IntableRaising, KeyElement, Representable, + Sized, Stringable, FloatableRaising, BytesCollectionElement, From de9dc5d25af1fbf7ac2b937d1b075bf9114479af Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 14 Oct 2024 11:13:25 -0500 Subject: [PATCH 1742/2019] [stdlib] cleanup: Don't represent `PyObject*` as `UnsafePointer[Int8]` This changes our `PyObjectPtr` struct to store an `UnsafePointer[PyObject]` internally, instead of an `UnsafePointer[Int8]`. It is still unsafe in general to directly interact with a value through a `PyObjectPtr` (see added comments), but this change at least models the type _more_ correctly. * Rename `PyObjectPtr.value` field to `unsized_obj_ptr` to make it more clear that this pointer should typically not be used directly, and to have a more greppable name. * Replace `PyMojoObject[T].unsafe_cast_obj` with `PyObjectPtr.unchecked_cast_to_mojo_object()`. MODULAR_ORIG_COMMIT_REV_ID: 95e48bc46c0371fc6907a04687469e07d979bba1 --- stdlib/src/builtin/_pybind.mojo | 2 +- stdlib/src/python/_bindings.mojo | 21 ++-- stdlib/src/python/_cpython.mojo | 159 ++++++++++++++++++++----------- 3 files changed, 114 insertions(+), 68 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 2a8552d447..b932779893 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -41,7 +41,7 @@ fn create_pybind_module[name: StringLiteral]() raises -> PyModule: fn pyobj_destroy_as[T: AnyType](pyobj: PyObjectPtr): # TODO(MSTDL-633): Is this always safe? Wrap in GIL, because this could # evaluate arbitrary code? - pyobj.value.bitcast[T]().destroy_pointee() + pyobj.unsized_obj_ptr.bitcast[T]().destroy_pointee() fn fail_initialization(owned err: Error) -> PythonObject: diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index ae6f76e151..bd5b60b7c2 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -57,13 +57,6 @@ struct PyMojoObject[T: Pythonable]: var ob_base: PyObject var mojo_value: T - @staticmethod - fn unsafe_cast_obj(obj_raw_ptr: PyObjectPtr) -> UnsafePointer[T]: - var mojo_obj_ptr = obj_raw_ptr.value.bitcast[PyMojoObject[T]]() - - # TODO(MSTDL-950): Should use something like `addr_of!` - return UnsafePointer[T].address_of(mojo_obj_ptr[].mojo_value) - @staticmethod fn python_type_object[ type_name: StringLiteral, @@ -105,7 +98,7 @@ struct PyMojoObject[T: Pythonable]: # ownership of this pointer? var type_obj = cpython.PyType_FromSpec(Box(type_spec).steal_data()) - if not type_obj.value: + if type_obj.is_null(): Python.throw_python_exception_if_error_state(cpython) return abort[TypedPythonObject["Type"]]( "expected to raise after getting NULL type object" @@ -149,9 +142,9 @@ fn create_empty_init_wrapper[T: Pythonable]() -> Typed_initproc: if len(args) != 0 or keyword_args != PyObjectPtr(): raise "unexpected arguments passed to default initializer function of wrapped Mojo type" - var obj_ptr: UnsafePointer[T] = PyMojoObject[T].unsafe_cast_obj( - py_self - ) + var obj_ptr: UnsafePointer[ + T + ] = py_self.unchecked_cast_to_mojo_value[T]() # ------------------------------------------------ # Call the user-provided initialization function. @@ -177,9 +170,9 @@ fn create_empty_init_wrapper[T: Pythonable]() -> Typed_initproc: fn create_dealloc_wrapper[T: Pythonable]() -> destructor: fn wrapper(py_self: PyObjectPtr): - var self_ptr: UnsafePointer[T] = PyMojoObject[T].unsafe_cast_obj( - py_self - ) + var self_ptr: UnsafePointer[T] = py_self.unchecked_cast_to_mojo_value[ + T + ]() # TODO(MSTDL-633): # Is this always safe? Wrap in GIL, because this could diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index d16d61ba6f..425ab247c7 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -35,7 +35,7 @@ from sys.ffi import ( ) from python.python import _get_global_python_itf -from python._bindings import Typed_initproc +from python._bindings import Typed_initproc, PyMojoObject, Pythonable from memory import UnsafePointer @@ -103,24 +103,74 @@ struct PyKeyValuePair: @value @register_passable("trivial") struct PyObjectPtr: - var value: UnsafePointer[Int8] + """Equivalent to `PyObject*` in C. + + It is crucial that this type has the same size and alignment as `PyObject*` + for FFI ABI correctness. + """ + + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var unsized_obj_ptr: UnsafePointer[PyObject] + + """Raw pointer to the underlying PyObject struct instance. + + It is not valid to read or write a `PyObject` directly from this pointer. + + This is because `PyObject` is an "unsized" or "incomplete" type: typically, + any allocation containing a `PyObject` contains additional fields holding + information specific to that Python object instance, e.g. containing its + "true" value. + + The value behind this pointer is only safe to interact with directly when + it has been downcasted to a concrete Python object type backing struct, in + a context where the user has ensured the object value is of that type. + """ + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# @always_inline fn __init__(inout self): - self.value = UnsafePointer[Int8]() + self.unsized_obj_ptr = UnsafePointer[PyObject]() - fn is_null(self) -> Bool: - return int(self.value) == 0 + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# fn __eq__(self, rhs: PyObjectPtr) -> Bool: - return int(self.value) == int(rhs.value) + return int(self.unsized_obj_ptr) == int(rhs.unsized_obj_ptr) fn __ne__(self, rhs: PyObjectPtr) -> Bool: return not (self == rhs) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn unchecked_cast_to_mojo_object[ + T: Pythonable + ](owned self) -> UnsafePointer[PyMojoObject[T]]: + """Assume that this Python object contains a wrapped Mojo value.""" + return self.unsized_obj_ptr.bitcast[PyMojoObject[T]]() + + fn unchecked_cast_to_mojo_value[ + T: Pythonable + ](owned self) -> UnsafePointer[T]: + var mojo_obj_ptr = self.unchecked_cast_to_mojo_object[T]() + + # TODO(MSTDL-950): Should use something like `addr_of!` + return UnsafePointer[T].address_of(mojo_obj_ptr[].mojo_value) + + fn is_null(self) -> Bool: + return int(self.unsized_obj_ptr) == 0 + # TODO: Consider removing this and inlining int(p.value) into callers fn _get_ptr_as_int(self) -> Int: - return int(self.value) + return int(self.unsized_obj_ptr) @value @@ -680,7 +730,20 @@ struct CPython: fn _Py_REFCNT(inout self, ptr: PyObjectPtr) -> Int: if ptr._get_ptr_as_int() == 0: return -1 - return int(ptr.value.load()) + # NOTE: + # The "obvious" way to write this would be: + # return ptr.unsized_obj_ptr[].object_ref_count + # However, that is not valid, because, as the name suggest, a PyObject + # is an "unsized" or "incomplete" type, meaning that a pointer to an + # instance of that type doesn't point at the entire allocation of the + # underlying "concrete" object instance. + # + # To avoid concerns about whether that's UB or not in Mojo, this + # this by just assumes the first field will be the ref count, and + # treats the object pointer "as if" it was a pointer to just the first + # field. + # TODO(MSTDL-950): Should use something like `addr_of!` + return ptr.unsized_obj_ptr.bitcast[Int]()[] # ===-------------------------------------------------------------------===# # Python GIL and threading @@ -769,24 +832,22 @@ struct CPython: fn PyDict_Next( inout self, dictionary: PyObjectPtr, p: Int ) -> PyKeyValuePair: - var key = UnsafePointer[Int8]() - var value = UnsafePointer[Int8]() + var key = PyObjectPtr() + var value = PyObjectPtr() var v = p var position = UnsafePointer[Int].address_of(v) - var value_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(value) - var key_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(key) var result = self.lib.get_function[ fn ( PyObjectPtr, - UnsafePointer[Int], - UnsafePointer[UnsafePointer[Int8]], - UnsafePointer[UnsafePointer[Int8]], + UnsafePointer[Py_ssize_t], + UnsafePointer[PyObjectPtr], + UnsafePointer[PyObjectPtr], ) -> c_int ]("PyDict_Next")( dictionary, position, - key_ptr, - value_ptr, + UnsafePointer.address_of(key), + UnsafePointer.address_of(value), ) self.log( @@ -796,11 +857,11 @@ struct CPython: "refcnt:", self._Py_REFCNT(dictionary), " key: ", - PyObjectPtr {value: key}._get_ptr_as_int(), + key._get_ptr_as_int(), ", refcnt(key):", self._Py_REFCNT(key), "value:", - PyObjectPtr {value: value}._get_ptr_as_int(), + value._get_ptr_as_int(), "refcnt(value)", self._Py_REFCNT(value), ) @@ -837,10 +898,9 @@ struct CPython: return r fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: - var value = self.lib.get_function[ - fn (UnsafePointer[UInt8]) -> UnsafePointer[Int8] - ]("PyImport_AddModule")(name.data) - return PyObjectPtr {value: value} + return self.lib.get_function[fn (UnsafePointer[c_char]) -> PyObjectPtr]( + "PyImport_AddModule" + )(name.unsafe_ptr().bitcast[c_char]()) fn PyModule_Create( inout self, @@ -896,7 +956,7 @@ struct CPython: fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: var value = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyModule_GetDict" - )(name.value) + )(name) return value # ===-------------------------------------------------------------------===# @@ -942,13 +1002,11 @@ struct CPython: locals: PyObjectPtr, run_mode: Int, ) -> PyObjectPtr: - var result = PyObjectPtr( - self.lib.get_function[ - fn ( - UnsafePointer[UInt8], Int32, PyObjectPtr, PyObjectPtr - ) -> UnsafePointer[Int8] - ]("PyRun_String")(strref.data, Int32(run_mode), globals, locals) - ) + var result = self.lib.get_function[ + fn ( + UnsafePointer[UInt8], Int32, PyObjectPtr, PyObjectPtr + ) -> PyObjectPtr + ]("PyRun_String")(strref.data, Int32(run_mode), globals, locals) self.log( result._get_ptr_as_int(), @@ -969,13 +1027,9 @@ struct CPython: globals: PyObjectPtr, locals: PyObjectPtr, ) -> PyObjectPtr: - var result = PyObjectPtr( - self.lib.get_function[ - fn ( - PyObjectPtr, PyObjectPtr, PyObjectPtr - ) -> UnsafePointer[Int8] - ]("PyEval_EvalCode")(co, globals, locals) - ) + var result = self.lib.get_function[ + fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr + ]("PyEval_EvalCode")(co, globals, locals) self._inc_total_rc() return result @@ -1298,7 +1352,7 @@ struct CPython: # The name of this global is technical a private part of the # CPython API, but unfortunately the only stable ways to access it are # macros. - ptr = self.lib.get_symbol[c_char]("_Py_NoneStruct") + ptr = self.lib.get_symbol[PyObject]("_Py_NoneStruct") if not ptr: abort("error: unable to get pointer to CPython `None` struct") @@ -1456,23 +1510,22 @@ struct CPython: return not value.is_null() fn PyErr_Fetch(inout self) -> PyObjectPtr: - var type = UnsafePointer[Int8]() - var value = UnsafePointer[Int8]() - var traceback = UnsafePointer[Int8]() - - var type_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(type) - var value_ptr = UnsafePointer[UnsafePointer[Int8]].address_of(value) - var traceback_ptr = UnsafePointer[UnsafePointer[Int8]].address_of( - traceback - ) + var type = PyObjectPtr() + var value = PyObjectPtr() + var traceback = PyObjectPtr() + var func = self.lib.get_function[ fn ( - UnsafePointer[UnsafePointer[Int8]], - UnsafePointer[UnsafePointer[Int8]], - UnsafePointer[UnsafePointer[Int8]], + UnsafePointer[PyObjectPtr], + UnsafePointer[PyObjectPtr], + UnsafePointer[PyObjectPtr], ) -> None - ]("PyErr_Fetch")(type_ptr, value_ptr, traceback_ptr) - var r = PyObjectPtr {value: value} + ]("PyErr_Fetch")( + UnsafePointer.address_of(type), + UnsafePointer.address_of(value), + UnsafePointer.address_of(traceback), + ) + var r = value self.log( r._get_ptr_as_int(), From b95eaba63e012fe506784e80c33b8a772c233597 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:17:58 -0600 Subject: [PATCH 1743/2019] [External] [stdlib] Make `StringSlice` `CollectionElement` (#49050) [External] [stdlib] Make `StringSlice` `CollectionElement` Make `StringSlice` `CollectionElement` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3597 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3597 MODULAR_ORIG_COMMIT_REV_ID: 4a6d29b9abbe111bd0c5dbf001e69d75729c6750 --- stdlib/src/utils/string_slice.mojo | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 7ed75f46c4..2604d99ba9 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -196,12 +196,12 @@ struct _StringSliceIter[ return self.index - self.continuation_bytes +@value struct StringSlice[ is_mutable: Bool, //, origin: Origin[is_mutable].type, -](Stringable, Sized, Formattable): - """ - A non-owning view to encoded string data. +](Stringable, Sized, Formattable, CollectionElement, CollectionElementNew): + """A non-owning view to encoded string data. TODO: The underlying string data is guaranteed to be encoded using UTF-8. @@ -307,6 +307,15 @@ struct StringSlice[ self._slice = byte_slice + @always_inline + fn __init__(inout self, *, other: Self): + """Explicitly construct a deep copy of the provided `StringSlice`. + + Args: + other: The `StringSlice` to copy. + """ + self._slice = other._slice + # ===------------------------------------------------------------------===# # Trait implementations # ===------------------------------------------------------------------===# From 3f7bd0118f2fbe4cefaf20fb0d9c73c51da7181c Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:21:00 -0600 Subject: [PATCH 1744/2019] [External] [stdlib] Add `_count_utf8_continuation_bytes()` (#49049) [External] [stdlib] Add `_count_utf8_continuation_bytes()` Add `_count_utf8_continuation_bytes()` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3529 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3529 MODULAR_ORIG_COMMIT_REV_ID: 994f648ac650ccd29096946d29b290e855bce057 --- stdlib/src/builtin/string_literal.mojo | 10 +++ stdlib/src/collections/string.mojo | 8 +-- stdlib/src/utils/string_slice.mojo | 80 ++++++++++++++++-------- stdlib/test/collections/test_string.mojo | 13 ++-- stdlib/test/utils/test_string_slice.mojo | 29 +++++++++ 5 files changed, 103 insertions(+), 37 deletions(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 65199a814f..975963b749 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -301,6 +301,16 @@ struct StringLiteral( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) + fn __reversed__(self) -> _StringSliceIter[StaticConstantOrigin, False]: + """Iterate backwards over the string, returning immutable references. + + Returns: + A reversed iterator over the string. + """ + return _StringSliceIter[StaticConstantOrigin, False]( + unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() + ) + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> String: """Gets the character at the specified position. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index b8c89988f8..23b3fd2c12 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1127,8 +1127,8 @@ struct String( count=other_len + 1, ) - fn __iter__(ref [_]self) -> _StringSliceIter[__origin_of(self)]: - """Iterate over elements of the string, returning immutable references. + fn __iter__(self) -> _StringSliceIter[__origin_of(self)]: + """Iterate over the string, returning immutable references. Returns: An iterator of references to the string elements. @@ -1137,9 +1137,7 @@ struct String( unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) - fn __reversed__( - ref [_]self, - ) -> _StringSliceIter[__origin_of(self), False]: + fn __reversed__(self) -> _StringSliceIter[__origin_of(self), False]: """Iterate backwards over the string, returning immutable references. Returns: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 2604d99ba9..a860c5982e 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -31,6 +31,32 @@ alias StaticString = StringSlice[StaticConstantOrigin] """An immutable static string slice.""" +fn _count_utf8_continuation_bytes(span: Span[UInt8]) -> Int: + alias sizes = (256, 128, 64, 32, 16, 8) + var ptr = span.unsafe_ptr() + var num_bytes = len(span) + var amnt: Int = 0 + var processed = 0 + + @parameter + for i in range(len(sizes)): + alias s = sizes.get[i, Int]() + + @parameter + if simdwidthof[DType.uint8]() >= s: + var rest = num_bytes - processed + for _ in range(rest // s): + var vec = (ptr + processed).load[width=s]() + var comp = (vec & 0b1100_0000) == 0b1000_0000 + amnt += int(comp.cast[DType.uint8]().reduce_add()) + processed += s + + for i in range(num_bytes - processed): + amnt += int((ptr[processed + i] & 0b1100_0000) == 0b1000_0000) + + return amnt + + fn _unicode_codepoint_utf8_byte_length(c: Int) -> Int: debug_assert( 0 <= c <= 0x10FFFF, "Value: ", c, " is not a valid Unicode code point" @@ -147,10 +173,9 @@ struct _StringSliceIter[ self.index = 0 if forward else length self.ptr = unsafe_pointer self.length = length - self.continuation_bytes = 0 - for i in range(length): - if _utf8_byte_type(unsafe_pointer[i]) == 1: - self.continuation_bytes += 1 + alias S = Span[UInt8, StaticConstantOrigin] + var s = S(unsafe_ptr=self.ptr, len=self.length) + self.continuation_bytes = _count_utf8_continuation_bytes(s) fn __iter__(self) -> Self: return self @@ -244,8 +269,7 @@ struct StringSlice[ @always_inline fn __init__(inout self, *, owned unsafe_from_utf8: Span[UInt8, origin]): - """ - Construct a new StringSlice from a sequence of UTF-8 encoded bytes. + """Construct a new StringSlice from a sequence of UTF-8 encoded bytes. Safety: `unsafe_from_utf8` MUST be valid UTF-8 encoded data. @@ -257,9 +281,8 @@ struct StringSlice[ self._slice = unsafe_from_utf8^ fn __init__(inout self, *, unsafe_from_utf8_strref: StringRef): - """ - Construct a new StringSlice from a StringRef pointing to UTF-8 encoded - bytes. + """Construct a new StringSlice from a StringRef pointing to UTF-8 + encoded bytes. Safety: - `unsafe_from_utf8_strref` MUST point to data that is valid for @@ -285,8 +308,7 @@ struct StringSlice[ unsafe_from_utf8_ptr: UnsafePointer[UInt8], len: Int, ): - """ - Construct a StringSlice from a pointer to a sequence of UTF-8 encoded + """Construct a StringSlice from a pointer to a sequence of UTF-8 encoded bytes and a length. Safety: @@ -335,13 +357,10 @@ struct StringSlice[ Returns: The length in Unicode codepoints. """ - var unicode_length = self.byte_length() - - for i in range(unicode_length): - if _utf8_byte_type(self._slice[i]) == 1: - unicode_length -= 1 - - return unicode_length + var b_len = self.byte_length() + alias S = Span[UInt8, StaticConstantOrigin] + var s = S(unsafe_ptr=self.unsafe_ptr(), len=b_len) + return b_len - _count_utf8_continuation_bytes(s) fn format_to(self, inout writer: Formatter): """ @@ -372,7 +391,8 @@ struct StringSlice[ rhs: The string slice to compare against. Returns: - True if the string slices are equal in length and contain the same elements, False otherwise. + True if the string slices are equal in length and contain the same + elements, False otherwise. """ if not self and not rhs: return True @@ -394,7 +414,8 @@ struct StringSlice[ rhs: The string to compare against. Returns: - True if the string slice is equal to the input string in length and contain the same bytes, False otherwise. + True if the string slice is equal to the input string in length and + contain the same bytes, False otherwise. """ return self == rhs.as_string_slice() @@ -406,7 +427,8 @@ struct StringSlice[ rhs: The literal to compare against. Returns: - True if the string slice is equal to the input literal in length and contain the same bytes, False otherwise. + True if the string slice is equal to the input literal in length and + contain the same bytes, False otherwise. """ return self == rhs.as_string_slice() @@ -419,7 +441,8 @@ struct StringSlice[ rhs: The string slice to compare against. Returns: - True if the string slices are not equal in length or contents, False otherwise. + True if the string slices are not equal in length or contents, False + otherwise. """ return not self == rhs @@ -431,7 +454,8 @@ struct StringSlice[ rhs: The string slice to compare against. Returns: - True if the string and slice are not equal in length or contents, False otherwise. + True if the string and slice are not equal in length or contents, + False otherwise. """ return not self == rhs @@ -443,12 +467,13 @@ struct StringSlice[ rhs: The string literal to compare against. Returns: - True if the slice is not equal to the literal in length or contents, False otherwise. + True if the slice is not equal to the literal in length or contents, + False otherwise. """ return not self == rhs fn __iter__(self) -> _StringSliceIter[origin]: - """Iterate over elements of the string slice, returning immutable references. + """Iterate over the string, returning immutable references. Returns: An iterator of references to the string elements. @@ -473,7 +498,7 @@ struct StringSlice[ @always_inline fn as_bytes(self) -> Span[UInt8, origin]: - """Get the sequence of encoded bytes as a slice of the underlying string. + """Get the sequence of encoded bytes of the underlying string. Returns: A slice containing the underlying sequence of encoded bytes. @@ -519,7 +544,8 @@ struct StringSlice[ pass fn _from_start(self, start: Int) -> Self: - """Gets the `StringSlice` pointing to the substring after the specified slice start position. + """Gets the `StringSlice` pointing to the substring after the specified + slice start position. If start is negative, it is interpreted as the number of characters from the end of the string to start at. diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index a03dffa9f2..e12cfad5a3 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1338,17 +1338,20 @@ def test_string_iter(): "álO", "етйувтсвардЗ", ) - var utf8_sequence_lengths = List(5, 12, 9, 5, 7, 6, 5, 5, 2, 3, 12) + var items_amount_characters = List(5, 12, 9, 5, 7, 6, 5, 5, 2, 3, 12) for item_idx in range(len(items)): var item = items[item_idx] - var utf8_sequence_len = 0 + var ptr = item.unsafe_ptr() + var amnt_characters = 0 var byte_idx = 0 for v in item: var byte_len = v.byte_length() - assert_equal(item[byte_idx : byte_idx + byte_len], v) + for i in range(byte_len): + assert_equal(ptr[byte_idx + i], v.unsafe_ptr()[i]) byte_idx += byte_len - utf8_sequence_len += 1 - assert_equal(utf8_sequence_len, utf8_sequence_lengths[item_idx]) + amnt_characters += 1 + + assert_equal(amnt_characters, items_amount_characters[item_idx]) var concat = String("") for v in item.__reversed__(): concat += v diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 2a911ab6c4..b753463662 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -16,6 +16,7 @@ from testing import assert_equal, assert_true, assert_false from utils import Span, StringSlice from utils._utf8_validation import _is_valid_utf8 +from utils.string_slice import _count_utf8_continuation_bytes fn test_string_literal_byte_span() raises: @@ -387,6 +388,33 @@ def test_combination_10_good_10_bad_utf8_sequences(): assert_false(validate_utf8(sequence)) +def test_count_utf8_continuation_bytes(): + alias c = UInt8(0b1000_0000) + alias b1 = UInt8(0b0100_0000) + alias b2 = UInt8(0b1100_0000) + alias b3 = UInt8(0b1110_0000) + alias b4 = UInt8(0b1111_0000) + + def _test(amnt: Int, items: List[UInt8]): + p = items.unsafe_ptr() + span = Span[UInt8, StaticConstantOrigin](unsafe_ptr=p, len=len(items)) + assert_equal(amnt, _count_utf8_continuation_bytes(span)) + + _test(5, List[UInt8](c, c, c, c, c)) + _test(2, List[UInt8](b2, c, b2, c, b1)) + _test(2, List[UInt8](b2, c, b1, b2, c)) + _test(2, List[UInt8](b2, c, b2, c, b1)) + _test(2, List[UInt8](b2, c, b1, b2, c)) + _test(2, List[UInt8](b1, b2, c, b2, c)) + _test(2, List[UInt8](b3, c, c, b1, b1)) + _test(2, List[UInt8](b1, b1, b3, c, c)) + _test(2, List[UInt8](b1, b3, c, c, b1)) + _test(3, List[UInt8](b1, b4, c, c, c)) + _test(3, List[UInt8](b4, c, c, c, b1)) + _test(3, List[UInt8](b3, c, c, b2, c)) + _test(3, List[UInt8](b2, c, b3, c, c)) + + fn main() raises: test_string_literal_byte_span() test_string_byte_span() @@ -403,3 +431,4 @@ fn main() raises: test_combination_good_bad_utf8_sequences() test_combination_10_good_utf8_sequences() test_combination_10_good_10_bad_utf8_sequences() + test_count_utf8_continuation_bytes() From a2ae4287037294aade299b4f76042b9c143f31d2 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 14 Oct 2024 13:21:40 -0500 Subject: [PATCH 1745/2019] [stdlib] cleanup: Share code between _pybind.mojo and _bindings.mojo This also cleans up use of leaked malloc()'d pointers, after further investigation in the CPython sources made it apparent that these pointers were only "borrowed" for the duration of the PyType_FromSpec() function call. MODULAR_ORIG_COMMIT_REV_ID: dea3de4d2724902951dfff1d9bda05b34b0ef581 --- stdlib/src/builtin/_pybind.mojo | 62 ++---------------- stdlib/src/python/_bindings.mojo | 106 +++++++++++++++---------------- 2 files changed, 57 insertions(+), 111 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index b932779893..7e39bd775c 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -26,6 +26,7 @@ from python._cpython import ( PyType_Spec, CPython, ) +from python._bindings import Pythonable, PyMojoObject alias PyModule = TypedPythonObject["Module"] @@ -38,12 +39,6 @@ fn create_pybind_module[name: StringLiteral]() raises -> PyModule: return Python.create_module(name) -fn pyobj_destroy_as[T: AnyType](pyobj: PyObjectPtr): - # TODO(MSTDL-633): Is this always safe? Wrap in GIL, because this could - # evaluate arbitrary code? - pyobj.unsized_obj_ptr.bitcast[T]().destroy_pointee() - - fn fail_initialization(owned err: Error) -> PythonObject: # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. cpython = get_cpython() @@ -57,26 +52,6 @@ fn fail_initialization(owned err: Error) -> PythonObject: return PythonObject(PyObjectPtr()) -alias MutableGlobalLifetime = __mlir_attr[ - `#lit.origin.field<`, - `#lit.static.origin : !lit.origin<1>`, - `, "__python_globals__"> : !lit.origin<1>`, -] - - -# FIXME(MOCO-1308): Workaround crash by adding explicit `alignment=1`. -alias PyGlobalPtr = UnsafePointer[origin=MutableGlobalLifetime, alignment=1] - - -@always_inline -fn global_alloc[T: AnyType, len: Int]() -> PyGlobalPtr[T]: - return __mlir_op.`pop.global_alloc`[ - name = "pyglobal".value, - count = len.value, - _type = __mlir_type[`!kgen.pointer<`, T, `>`], - ]() - - fn pointer_bitcast[ To: AnyType ](ptr: Pointer) -> Pointer[To, ptr.origin, ptr.address_space, *_, **_] as out: @@ -92,42 +67,17 @@ fn pointer_bitcast[ fn gen_pytype_wrapper[ - T: AnyType, name: StringLiteral + T: Pythonable, + name: StringLiteral, ](inout module: PythonObject) raises: - cpython = get_cpython() - # TODO(MOCO-1301): Add support for member method generation. # TODO(MOCO-1302): Add support for generating member field as computed properties. - - # Zeroed item as terminator - methods = global_alloc[PyMethodDef, 1]() - methods[0] = PyMethodDef() - # TODO(MOCO-1307): Add support for constructor generation. - slots = global_alloc[PyType_Slot, 3]() - slots[0] = PyType_Slot( - cp.Py_tp_new, cpython.lib.get_symbol[NoneType]("PyType_GenericNew") - ) - alias dtor = pyobj_destroy_as[T] - slots[1] = PyType_Slot( - cp.Py_tp_dealloc, - __mlir_op.`pop.pointer.bitcast`[_type = OpaquePointer._mlir_type](dtor), - ) - slots[2] = PyType_Slot.null() - - spec = global_alloc[PyType_Spec, 1]() - spec.init_pointee_move( - PyType_Spec { - # FIXME(MOCO-1306): This should be `T.__name__`. - name: name.unsafe_cstr_ptr(), - basicsize: sizeof[T](), - itemsize: 0, - flags: cp.Py_TPFLAGS_DEFAULT, - slots: slots, - } + + var type_obj = PyMojoObject[T].python_type_object[name]( + methods=List[PyMethodDef](), ) - type_obj = cpython.PyType_FromSpec(spec) # FIXME(MSTDL-957): We should have APIs that explicitly take a `CPython` # instance so that callers can pass it around instead of performing a lookup # each time. diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index bd5b60b7c2..d4611e76a5 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -74,29 +74,31 @@ struct PyMojoObject[T: Pythonable]: PyType_Slot.tp_new( cpython.lib.get_function[newfunc]("PyType_GenericNew") ), - PyType_Slot.tp_init(create_empty_init_wrapper[T]()), - PyType_Slot.tp_dealloc(create_dealloc_wrapper[T]()), - # FIXME: Avoid leaking the methods data pointer in this way. - PyType_Slot.tp_methods(methods.steal_data()), - # Zeroed item terminator - PyType_Slot.null(), + PyType_Slot.tp_init(empty_tp_init_wrapper[T]), + PyType_Slot.tp_dealloc(tp_dealloc_wrapper[T]), ) + if methods: + # FIXME: Avoid leaking the methods data pointer in this way. + slots.append(PyType_Slot.tp_methods(methods.steal_data())) + + # Zeroed item terminator + slots.append(PyType_Slot.null()) + var type_spec = PyType_Spec { + # FIXME(MOCO-1306): This should be `T.__name__`. name: type_name.unsafe_cstr_ptr(), basicsize: sizeof[PyMojoObject[T]](), itemsize: 0, flags: Py_TPFLAGS_DEFAULT, - # FIXME: Don't leak this pointer, use globals instead. - slots: slots.steal_data(), + # Note: This pointer is only "borrowed" by PyType_FromSpec. + slots: slots.unsafe_ptr(), } # Construct a Python 'type' object from our type spec. - # FIXME: - # We heap allocate the type specification metadata. - # Who owns this pointer? Or does Python not actually take - # ownership of this pointer? - var type_obj = cpython.PyType_FromSpec(Box(type_spec).steal_data()) + var type_obj = cpython.PyType_FromSpec( + UnsafePointer.address_of(type_spec) + ) if type_obj.is_null(): Python.throw_python_exception_if_error_state(cpython) @@ -124,63 +126,57 @@ struct PyMojoObject[T: Pythonable]: # # This function creates that wrapper function, and returns a pointer pointer to # it. -fn create_empty_init_wrapper[T: Pythonable]() -> Typed_initproc: - """Create Python-compatible wrapper around a Mojo initializer function. +fn empty_tp_init_wrapper[ + T: Pythonable +]( + py_self: PyObjectPtr, + args: TypedPythonObject["Tuple"], + keyword_args: PyObjectPtr, +) -> c_int: + """Python-compatible wrapper around a Mojo initializer function. Parameters: T: The wrapped Mojo type. """ - fn wrapper( - py_self: PyObjectPtr, - args: TypedPythonObject["Tuple"], - keyword_args: PyObjectPtr, - ) -> c_int: - var cpython = _get_global_python_itf().cpython() + var cpython = _get_global_python_itf().cpython() - try: - if len(args) != 0 or keyword_args != PyObjectPtr(): - raise "unexpected arguments passed to default initializer function of wrapped Mojo type" + try: + if len(args) != 0 or keyword_args != PyObjectPtr(): + raise "unexpected arguments passed to default initializer function of wrapped Mojo type" - var obj_ptr: UnsafePointer[ - T - ] = py_self.unchecked_cast_to_mojo_value[T]() + var obj_ptr: UnsafePointer[T] = py_self.unchecked_cast_to_mojo_value[ + T + ]() - # ------------------------------------------------ - # Call the user-provided initialization function. - # ------------------------------------------------ + # ------------------------------------------------ + # Call the user-provided initialization function. + # ------------------------------------------------ - # TODO(MSTDL-950): Avoid forming ref through uninit pointee. - T.__init__(obj_ptr[]) + # TODO(MSTDL-950): Avoid forming ref through uninit pointee. + T.__init__(obj_ptr[]) - return 0 - except e: - # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. - var error_type = cpython.get_error_global("PyExc_ValueError") + return 0 + except e: + # TODO(MSTDL-933): Add custom 'MojoError' type, and raise it here. + var error_type = cpython.get_error_global("PyExc_ValueError") - cpython.PyErr_SetString( - error_type, - e.unsafe_cstr_ptr(), - ) - - return -1 - - return wrapper + cpython.PyErr_SetString( + error_type, + e.unsafe_cstr_ptr(), + ) + return -1 -fn create_dealloc_wrapper[T: Pythonable]() -> destructor: - fn wrapper(py_self: PyObjectPtr): - var self_ptr: UnsafePointer[T] = py_self.unchecked_cast_to_mojo_value[ - T - ]() - # TODO(MSTDL-633): - # Is this always safe? Wrap in GIL, because this could - # evaluate arbitrary code? - # Destroy this `Person` instance. - self_ptr.destroy_pointee() +fn tp_dealloc_wrapper[T: Pythonable](py_self: PyObjectPtr): + var self_ptr: UnsafePointer[T] = py_self.unchecked_cast_to_mojo_value[T]() - return wrapper + # TODO(MSTDL-633): + # Is this always safe? Wrap in GIL, because this could + # evaluate arbitrary code? + # Destroy this `Person` instance. + self_ptr.destroy_pointee() # ===-----------------------------------------------------------------------===# From 452311a2d112fca3991124cc505000a3f40990d1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 14 Oct 2024 13:47:27 -0500 Subject: [PATCH 1746/2019] [stdlib] polish: Require `Pythonable` types to implement `Representable` MODULAR_ORIG_COMMIT_REV_ID: 9907b0faa8d58dc44d52241ed2f35eb19941c431 --- stdlib/src/python/_bindings.mojo | 11 ++++++++++- stdlib/src/python/_cpython.mojo | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index d4611e76a5..59489e5c5f 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -45,7 +45,7 @@ alias Typed_initproc = fn ( ) -> c_int -trait Pythonable(Defaultable): +trait Pythonable(Defaultable, Representable): """Represents a Mojo type that can be used from Python.""" pass @@ -76,6 +76,7 @@ struct PyMojoObject[T: Pythonable]: ), PyType_Slot.tp_init(empty_tp_init_wrapper[T]), PyType_Slot.tp_dealloc(tp_dealloc_wrapper[T]), + PyType_Slot.tp_repr(tp_repr_wrapper[T]), ) if methods: @@ -179,6 +180,14 @@ fn tp_dealloc_wrapper[T: Pythonable](py_self: PyObjectPtr): self_ptr.destroy_pointee() +fn tp_repr_wrapper[T: Pythonable](py_self: PyObjectPtr) -> PyObjectPtr: + var self_ptr: UnsafePointer[T] = py_self.unchecked_cast_to_mojo_value[T]() + + var repr_str: String = repr(self_ptr[]) + + return PythonObject(string=repr_str).steal_data() + + # ===-----------------------------------------------------------------------===# # PyCFunction Wrappers # ===-----------------------------------------------------------------------===# diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 425ab247c7..ef73807f3f 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -56,6 +56,7 @@ alias Py_tp_dealloc = 52 alias Py_tp_init = 60 alias Py_tp_methods = 64 alias Py_tp_new = 65 +alias Py_tp_repr = 66 alias Py_TPFLAGS_DEFAULT = 0 @@ -71,6 +72,8 @@ alias METH_VARARGS = 0x1 alias destructor = fn (PyObjectPtr) -> None +alias reprfunc = fn (PyObjectPtr) -> PyObjectPtr + alias initproc = fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int alias newfunc = fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr @@ -317,6 +320,10 @@ struct PyType_Slot: fn tp_methods(methods: UnsafePointer[PyMethodDef]) -> Self: return PyType_Slot(Py_tp_methods, rebind[OpaquePointer](methods)) + @staticmethod + fn tp_repr(func: reprfunc) -> Self: + return PyType_Slot(Py_tp_repr, rebind[OpaquePointer](func)) + @staticmethod fn null() -> Self: return PyType_Slot {slot: 0, pfunc: OpaquePointer()} From 34af7762a4e9e65983dd0728c379997c30ed4a92 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 14 Oct 2024 14:32:02 -0500 Subject: [PATCH 1747/2019] [stdlib] feat: Add wrapped function arity and argument type checking utils * Add PyObjectPtr.try_cast_to_mojo_value() for checked downcasting from a Python object to a Mojo value. * Add check_arguments_arity() and check_argument_type() utilities * Add Py_TYPE and PyType_GetName() wrappers. MODULAR_ORIG_COMMIT_REV_ID: 1cc776ac7329905344e999cf586c07da831e9dbf --- stdlib/src/python/_bindings.mojo | 87 ++++++++++++++++++++++++++++++++ stdlib/src/python/_cpython.mojo | 51 ++++++++++++++++++- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 59489e5c5f..51455e74d0 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -18,6 +18,8 @@ from sys.info import sizeof from os import abort +from collections import Optional + from python import PythonObject, TypedPythonObject from python.python import _get_global_python_itf from python._cpython import ( @@ -280,3 +282,88 @@ fn create_wrapper_function[ # `@always_inline`? # Call the non-`raises` overload of `create_wrapper_function`. return create_wrapper_function[wrapper]() + + +fn check_arguments_arity( + func_name: StringLiteral, + arity: Int, + args: TypedPythonObject["Tuple"], +) raises: + """Raise an error if the provided argument count does not match the expected + function arity. + + If this function returns normally (without raising), then the argument + count is exactly equal to the expected arity. + """ + + var arg_count = len(args) + + # The error messages raised below are intended to be similar to the + # equivalent errors in Python. + if arg_count != arity: + if arg_count < arity: + var missing_arg_count = arity - arg_count + + raise Error( + String.format( + "TypeError: {}() missing {} required positional {}", + func_name, + missing_arg_count, + _pluralize(missing_arg_count, "argument", "arguments"), + ) + ) + else: + raise Error( + String.format( + "TypeError: {}() takes {} positional {} but {} were given", + func_name, + arity, + _pluralize(arity, "argument", "arguments"), + arg_count, + ) + ) + + +fn check_argument_type[ + T: Pythonable, +]( + func_name: StringLiteral, + type_name_id: StringLiteral, + obj: PythonObject, +) raises -> UnsafePointer[T]: + """Raise an error if the provided Python object does not contain a wrapped + instance of the Mojo `T` type. + """ + + var opt: Optional[UnsafePointer[T]] = obj.py_object.try_cast_to_mojo_value[ + T + ](type_name_id) + + if not opt: + var cpython = _get_global_python_itf().cpython() + + var actual_type = cpython.Py_TYPE(obj.unsafe_as_py_object_ptr()) + var actual_type_name = PythonObject(cpython.PyType_GetName(actual_type)) + + raise Error( + String.format( + "TypeError: {}() expected Mojo '{}' type argument, got '{}'", + func_name, + type_name_id, + str(actual_type_name), + ) + ) + + # SAFETY: We just validated that this Optional is not empty. + return opt.unsafe_take() + + +fn _pluralize( + count: Int, + singular: StringLiteral, + plural: StringLiteral, +) -> StringLiteral: + if count == 1: + return singular + else: + return plural diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index ef73807f3f..65d87f835e 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -17,7 +17,7 @@ Documentation for these functions can be found online at: """ -from collections import InlineArray +from collections import InlineArray, Optional from os import getenv, setenv, abort from os.path import dirname from pathlib import Path @@ -154,6 +154,31 @@ struct PyObjectPtr: # Methods # ===-------------------------------------------------------------------===# + fn try_cast_to_mojo_value[ + T: Pythonable, + ]( + owned self, + # TODO: Make this part of the trait bound + expected_type_name: StringLiteral, + ) -> Optional[UnsafePointer[T]]: + var cpython = _get_global_python_itf().cpython() + + var type = cpython.Py_TYPE(self) + + var type_name = PythonObject(cpython.PyType_GetName(type)) + + # FIXME(MSTDL-978): + # Improve this check. We should do something conceptually equivalent + # to: + # type == T.python_type_object + # where: + # trait Pythonable: + # var python_type_object: PyTypeObject + if type_name == PythonObject(expected_type_name): + return self.unchecked_cast_to_mojo_value[T]() + else: + return None + fn unchecked_cast_to_mojo_object[ T: Pythonable ](owned self) -> UnsafePointer[PyMojoObject[T]]: @@ -970,6 +995,28 @@ struct CPython: # Python Type operations # ===-------------------------------------------------------------------===# + fn Py_TYPE(inout self, ob_raw: PyObjectPtr) -> UnsafePointer[PyTypeObject]: + """Get the PyTypeObject field of a Python object.""" + + # Note: + # The `Py_TYPE` function is a `static` function in the C API, so + # we can't call it directly. Instead we reproduce its (trivial) + # behavior here. + # TODO(MSTDL-977): + # Investigate doing this without hard-coding private API details. + + # TODO(MSTDL-950): Should use something like `addr_of!` + return ob_raw.unsized_obj_ptr[].object_type + + fn PyType_GetName( + inout self, type: UnsafePointer[PyTypeObject] + ) -> PyObjectPtr: + var func = self.lib.get_function[ + fn (UnsafePointer[PyTypeObject]) -> PyObjectPtr + ]("PyType_GetName") + + return func(type) + fn PyType_FromSpec( inout self, spec: UnsafePointer[PyType_Spec] ) -> PyObjectPtr: @@ -1359,6 +1406,8 @@ struct CPython: # The name of this global is technical a private part of the # CPython API, but unfortunately the only stable ways to access it are # macros. + # TODO(MSTDL-977): + # Investigate doing this without hard-coding private API details. ptr = self.lib.get_symbol[PyObject]("_Py_NoneStruct") if not ptr: From cd944ac7baffc26ba418eefbf0aaee0258e9d36a Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:52:59 -0600 Subject: [PATCH 1748/2019] [External] [stdlib] Add `String` `__add__` with `StringSlice` and `StringLiteral` & optimize to not use `List.resize()` (#49071) [External] [stdlib] Add `String` `__add__` with `StringSlice` and `StringLiteral` & optimize to not use `List.resize()` Add `String` `__add__` with `StringSlice` and `StringLiteral` & optimize to not use `List.resize()` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3591 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3591 MODULAR_ORIG_COMMIT_REV_ID: a01834b1eb829ffaa3e5bda5bd3fee05b3513774 --- stdlib/src/collections/string.mojo | 153 +++++++++++++++++------ stdlib/test/collections/test_string.mojo | 16 ++- 2 files changed, 131 insertions(+), 38 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 23b3fd2c12..d365365bfc 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1063,6 +1063,30 @@ struct String( """ return not (self < rhs) + @staticmethod + fn _add[rhs_has_null: Bool](lhs: Span[UInt8], rhs: Span[UInt8]) -> String: + var lhs_len = len(lhs) + var rhs_len = len(rhs) + var lhs_ptr = lhs.unsafe_ptr() + var rhs_ptr = rhs.unsafe_ptr() + alias S = StringSlice[ImmutableAnyOrigin] + if lhs_len == 0: + return String(S(unsafe_from_utf8_ptr=rhs_ptr, len=rhs_len)) + elif rhs_len == 0: + return String(S(unsafe_from_utf8_ptr=lhs_ptr, len=lhs_len)) + var sum_len = lhs_len + rhs_len + var buffer = Self._buffer_type(capacity=sum_len + 1) + var ptr = buffer.unsafe_ptr() + memcpy(ptr, lhs_ptr, lhs_len) + memcpy(ptr + lhs_len, rhs_ptr, rhs_len + int(rhs_has_null)) + buffer.size = sum_len + 1 + + @parameter + if not rhs_has_null: + ptr[sum_len] = 0 + return Self(buffer^) + + @always_inline fn __add__(self, other: String) -> String: """Creates a string by appending another string at the end. @@ -1072,26 +1096,31 @@ struct String( Returns: The new constructed string. """ - if not self: - return other - if not other: - return self - var self_len = self.byte_length() - var other_len = other.byte_length() - var total_len = self_len + other_len - var buffer = Self._buffer_type() - buffer.resize(total_len + 1, 0) - memcpy( - buffer.data, - self.unsafe_ptr(), - self_len, - ) - memcpy( - buffer.data + self_len, - other.unsafe_ptr(), - other_len + 1, # Also copy the terminator - ) - return Self(buffer^) + return Self._add[True](self.as_bytes(), other.as_bytes()) + + @always_inline + fn __add__(self, other: StringLiteral) -> String: + """Creates a string by appending a string literal at the end. + + Args: + other: The string literal to append. + + Returns: + The new constructed string. + """ + return Self._add[False](self.as_bytes(), other.as_bytes()) + + @always_inline + fn __add__(self, other: StringSlice) -> String: + """Creates a string by appending a string slice at the end. + + Args: + other: The string slice to append. + + Returns: + The new constructed string. + """ + return Self._add[False](self.as_bytes(), other.as_bytes()) @always_inline fn __radd__(self, other: String) -> String: @@ -1103,29 +1132,78 @@ struct String( Returns: The new constructed string. """ - return other + self + return Self._add[True](other.as_bytes(), self.as_bytes()) + + @always_inline + fn __radd__(self, other: StringLiteral) -> String: + """Creates a string by prepending another string literal to the start. + Args: + other: The string to prepend. + + Returns: + The new constructed string. + """ + return Self._add[True](other.as_bytes(), self.as_bytes()) + + @always_inline + fn __radd__(self, other: StringSlice) -> String: + """Creates a string by prepending another string slice to the start. + + Args: + other: The string to prepend. + + Returns: + The new constructed string. + """ + return Self._add[True](other.as_bytes(), self.as_bytes()) + + fn _iadd[has_null: Bool](inout self, other: Span[UInt8]): + var s_len = self.byte_length() + var o_len = len(other) + var o_ptr = other.unsafe_ptr() + if s_len == 0: + alias S = StringSlice[ImmutableAnyOrigin] + self = String(S(unsafe_from_utf8_ptr=o_ptr, len=o_len)) + return + elif o_len == 0: + return + var sum_len = s_len + o_len + self._buffer.reserve(sum_len + 1) + var s_ptr = self.unsafe_ptr() + memcpy(s_ptr + s_len, o_ptr, o_len + int(has_null)) + self._buffer.size = sum_len + 1 + + @parameter + if not has_null: + s_ptr[sum_len] = 0 + + @always_inline fn __iadd__(inout self, other: String): """Appends another string to this string. Args: other: The string to append. """ - if not self: - self = other - return - if not other: - return - var self_len = self.byte_length() - var other_len = other.byte_length() - var total_len = self_len + other_len - self._buffer.resize(total_len + 1, 0) - # Copy the data alongside the terminator. - memcpy( - dest=self.unsafe_ptr() + self_len, - src=other.unsafe_ptr(), - count=other_len + 1, - ) + self._iadd[True](other.as_bytes()) + + @always_inline + fn __iadd__(inout self, other: StringLiteral): + """Appends another string literal to this string. + + Args: + other: The string to append. + """ + self._iadd[False](other.as_bytes()) + + @always_inline + fn __iadd__(inout self, other: StringSlice): + """Appends another string slice to this string. + + Args: + other: The string to append. + """ + self._iadd[False](other.as_bytes()) fn __iter__(self) -> _StringSliceIter[__origin_of(self)]: """Iterate over the string, returning immutable references. @@ -1471,7 +1549,8 @@ struct String( Notes: This does not include the trailing null terminator in the count. """ - return max(len(self._buffer) - 1, 0) + var length = len(self._buffer) + return length - int(length > 0) fn _steal_ptr(inout self) -> UnsafePointer[UInt8]: """Transfer ownership of pointer to the underlying memory. diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index e12cfad5a3..bbf3768c7f 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -29,7 +29,7 @@ from testing import ( assert_true, ) -from utils import StringRef +from utils import StringRef, StringSlice @value @@ -193,6 +193,19 @@ def test_add(): assert_equal("abc is a string", str(s8) + str(s9)) +def test_add_string_slice(): + var s1 = String("123") + var s2 = StringSlice("abc") + var s3: StringLiteral = "abc" + assert_equal("123abc", s1 + s2) + assert_equal("123abc", s1 + s3) + assert_equal("abc123", s2 + s1) + assert_equal("abc123", s3 + s1) + s1 += s2 + s1 += s3 + assert_equal("123abcabc", s1) + + def test_string_join(): var sep = String(",") var s0 = String("abc") @@ -1626,6 +1639,7 @@ def main(): test_equality_operators() test_comparison_operators() test_add() + test_add_string_slice() test_stringable() test_repr() test_string_join() From 78abfa55b0137b1c4a6d54a8b4e363242beb3d3a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 14 Oct 2024 13:53:12 -0700 Subject: [PATCH 1749/2019] [Stdlib] Specialize the range iter structs based on the arity MODULAR_ORIG_COMMIT_REV_ID: c6a7ea28d356dde0e6543dc2e1192374cc188641 --- stdlib/src/builtin/range.mojo | 140 ++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 965ea7dfab..a7bd3264bf 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -458,18 +458,93 @@ fn range(start: UInt, end: UInt, step: UInt = 1) -> _UIntStridedRange: # ===----------------------------------------------------------------------=== # +@register_passable("trivial") +struct _ZeroStartingScalarRange[type: DType]: + var curr: Scalar[type] + var end: Scalar[type] + + @always_inline + fn __init__(inout self, end: Scalar[type]): + self.curr = max(0, end) + self.end = self.curr + + @always_inline + fn __iter__(self) -> Self: + return self + + @always_inline + fn __next__(inout self) -> Scalar[type]: + var curr = self.curr + self.curr -= 1 + return self.end - curr + + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + + @always_inline + fn __len__(self) -> Scalar[type]: + return self.curr + + @always_inline + fn __getitem__(self, idx: Scalar[type]) -> Scalar[type]: + debug_assert(idx < self.__len__(), "index out of range") + return idx + + @always_inline + fn __reversed__(self) -> _StridedScalarRange[type]: + constrained[ + not type.is_unsigned(), "cannot reverse an unsigned range" + ]() + return range(self.end - 1, Scalar[type](-1), Scalar[type](-1)) + + +@value +@register_passable("trivial") +struct _SequentialScalarRange[type: DType]: + var start: Scalar[type] + var end: Scalar[type] + + @always_inline + fn __iter__(self) -> Self: + return self + + @always_inline + fn __next__(inout self) -> Scalar[type]: + var start = self.start + self.start += 1 + return start + + @always_inline + fn __hasmore__(self) -> Bool: + return self.__len__() > 0 + + @always_inline + fn __len__(self) -> Scalar[type]: + return max(0, self.end - self.start) + + @always_inline + fn __getitem__(self, idx: Scalar[type]) -> Scalar[type]: + debug_assert(idx < self.__len__(), "index out of range") + return self.start + idx + + @always_inline + fn __reversed__(self) -> _StridedRange: + return range(self.end - 1, self.start - 1, -1) + + @value @register_passable("trivial") -struct _StridedScalarRangeIterator[dtype: DType]: - var start: Scalar[dtype] - var end: Scalar[dtype] - var step: Scalar[dtype] +struct _StridedScalarRangeIterator[type: DType]: + var start: Scalar[type] + var end: Scalar[type] + var step: Scalar[type] @always_inline fn __hasmore__(self) -> Bool: - # If the dtype is unsigned, then 'step' cannot be negative. + # If the type is unsigned, then 'step' cannot be negative. @parameter - if dtype.is_unsigned(): + if type.is_unsigned(): return self.start < self.end else: if self.step > 0: @@ -477,7 +552,7 @@ struct _StridedScalarRangeIterator[dtype: DType]: return self.end < self.start @always_inline - fn __next__(inout self) -> Scalar[dtype]: + fn __next__(inout self) -> Scalar[type]: var result = self.start self.start += self.step return result @@ -485,49 +560,68 @@ struct _StridedScalarRangeIterator[dtype: DType]: @value @register_passable("trivial") -struct _StridedScalarRange[dtype: DType]: - var start: Scalar[dtype] - var end: Scalar[dtype] - var step: Scalar[dtype] +struct _StridedScalarRange[type: DType]: + var start: Scalar[type] + var end: Scalar[type] + var step: Scalar[type] @always_inline - fn __iter__(self) -> _StridedScalarRangeIterator[dtype]: + fn __iter__(self) -> _StridedScalarRangeIterator[type]: return _StridedScalarRangeIterator(self.start, self.end, self.step) +@always_inline +fn range[type: DType, //](end: Scalar[type]) -> _ZeroStartingScalarRange[type]: + """Constructs a [start; end) Range with a given step. + + Parameters: + type: The range type. + + Args: + end: The end of the range. + + Returns: + The constructed range. + """ + return _ZeroStartingScalarRange(end) + + @always_inline fn range[ - dtype: DType -]( - start: Scalar[dtype], end: Scalar[dtype], step: Scalar[dtype] = 1 -) -> _StridedScalarRange[dtype]: + type: DType, // +](start: Scalar[type], end: Scalar[type]) -> _SequentialScalarRange[type]: """Constructs a [start; end) Range with a given step. Parameters: - dtype: The range type. + type: The range type. Args: start: The start of the range. end: The end of the range. - step: The step for the range. Defaults to 1. Returns: The constructed range. """ - return _StridedScalarRange(start, end, step) + return _SequentialScalarRange(start, end) @always_inline -fn range[dtype: DType](end: Scalar[dtype]) -> _StridedScalarRange[dtype]: - """Constructs a [0; end) Range with a step = 1. +fn range[ + type: DType, // +]( + start: Scalar[type], end: Scalar[type], step: Scalar[type] +) -> _StridedScalarRange[type]: + """Constructs a [start; end) Range with a given step. Parameters: - dtype: The range type. + type: The range type. Args: + start: The start of the range. end: The end of the range. + step: The step for the range. Defaults to 1. Returns: The constructed range. """ - return _StridedScalarRange(0, end, 1) + return _StridedScalarRange(start, end, step) From cd8eb7280ea9d11621e8978e6b2693b53072b120 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 14 Oct 2024 13:54:41 -0700 Subject: [PATCH 1750/2019] [Stdlib] Allow static_tuple to work with IntLike types, NFC This allows one to pass in both UInt and Int types for get/set element MODULAR_ORIG_COMMIT_REV_ID: cbab23a6b3dae6ea2828fc54723ebe9d5c6eb57c --- stdlib/src/utils/static_tuple.mojo | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 5199411cc9..3f009988f7 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -189,38 +189,52 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): return val @always_inline("nodebug") - fn __getitem__(self, idx: Int) -> Self.element_type: + fn __getitem__[ + IntLike: IntLike, // + ](self, idx: IntLike) -> Self.element_type: """Returns the value of the tuple at the given dynamic index. + Parameters: + IntLike: The type of idx; either `Int` or `UInt`. + Args: idx: The index into the tuple. Returns: The value at the specified position. """ - debug_assert(idx < size, "index must be within bounds") + debug_assert( + int(idx.__mlir_index__()) < size, "index must be within bounds" + ) # Copy the array so we can get its address, because we can't take the # address of 'self' in a non-mutating method. var arrayCopy = self.array var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(arrayCopy).address, idx.value + UnsafePointer.address_of(arrayCopy).address, idx.__mlir_index__() ) var result = UnsafePointer(ptr)[] _ = arrayCopy return result @always_inline("nodebug") - fn __setitem__(inout self, idx: Int, val: Self.element_type): + fn __setitem__[ + IntLike: IntLike, // + ](inout self, idx: IntLike, val: Self.element_type): """Stores a single value into the tuple at the specified dynamic index. + Parameters: + IntLike: The type of idx; either `Int` or `UInt`. + Args: idx: The index into the tuple. val: The value to store. """ - debug_assert(idx < size, "index must be within bounds") + debug_assert( + int(idx.__mlir_index__()) < size, "index must be within bounds" + ) var tmp = self var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(tmp.array).address, idx.value + UnsafePointer.address_of(tmp.array).address, idx.__mlir_index__() ) UnsafePointer(ptr)[] = val self = tmp From 501c668c181e24429f51bdb9bc3006928d0f087b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 14 Oct 2024 14:11:34 -0700 Subject: [PATCH 1751/2019] [KGEN] Add volatile load/store to pop MODULAR_ORIG_COMMIT_REV_ID: b262cf9a0e025be53fd644e3371461bdb60376de --- stdlib/src/memory/unsafe_pointer.mojo | 90 +++++++++++++++++----- stdlib/test/memory/test_unsafepointer.mojo | 16 ++++ 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index d10cbc6984..446410acd0 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -446,6 +446,7 @@ struct UnsafePointer[ width: Int = 1, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_]) -> SIMD[type, width]: """Loads the value the pointer points to. @@ -456,6 +457,7 @@ struct UnsafePointer[ type: The data type of SIMD vector. width: The size of the SIMD vector. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Returns: The loaded value. @@ -476,14 +478,28 @@ struct UnsafePointer[ # intentionally don't unroll, otherwise the compiler vectorizes for i in range(width): - v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( - (self + i).address - ) + + @parameter + if volatile: + v[i] = __mlir_op.`pop.load`[ + alignment = alignment.value, + isVolatile = __mlir_attr.unit, + ]((self + i).address) + else: + v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( + (self + i).address + ) return v - return __mlir_op.`pop.load`[alignment = alignment.value]( - self.bitcast[SIMD[type, width]]().address - ) + @parameter + if volatile: + return __mlir_op.`pop.load`[ + alignment = alignment.value, isVolatile = __mlir_attr.unit + ](self.bitcast[SIMD[type, width]]().address) + else: + return __mlir_op.`pop.load`[alignment = alignment.value]( + self.bitcast[SIMD[type, width]]().address + ) @always_inline fn load[ @@ -491,6 +507,7 @@ struct UnsafePointer[ width: Int = 1, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], offset: Scalar) -> SIMD[ type, width ]: @@ -504,6 +521,7 @@ struct UnsafePointer[ type: The data type of SIMD vector elements. width: The size of the SIMD vector. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: offset: The offset to load from. @@ -512,7 +530,9 @@ struct UnsafePointer[ The loaded value. """ constrained[offset.type.is_integral(), "offset must be integer"]() - return self.offset(int(offset)).load[width=width, alignment=alignment]() + return self.offset(int(offset)).load[ + width=width, alignment=alignment, volatile=volatile + ]() @always_inline("nodebug") fn load[ @@ -521,6 +541,7 @@ struct UnsafePointer[ width: Int = 1, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], offset: T) -> SIMD[ type, width ]: @@ -534,6 +555,7 @@ struct UnsafePointer[ type: The data type of SIMD vector elements. width: The size of the SIMD vector. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: offset: The offset to load from. @@ -541,7 +563,9 @@ struct UnsafePointer[ Returns: The loaded value. """ - return self.offset(offset).load[width=width, alignment=alignment]() + return self.offset(offset).load[ + width=width, alignment=alignment, volatile=volatile + ]() @always_inline fn store[ @@ -549,6 +573,7 @@ struct UnsafePointer[ type: DType, //, *, alignment: Int = _default_alignment[type](), + volatile: Bool = False, ]( self: UnsafePointer[Scalar[type], *_, **_], offset: T, @@ -564,12 +589,13 @@ struct UnsafePointer[ T: The type of offset, either `Int` or `UInt`. type: The data type of SIMD vector elements. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: offset: The offset to store to. val: The value to store. """ - self.offset(offset)._store[alignment=alignment](val) + self.offset(offset)._store[alignment=alignment, volatile=volatile](val) @always_inline fn store[ @@ -578,6 +604,7 @@ struct UnsafePointer[ width: Int, //, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ]( self: UnsafePointer[Scalar[type], *_, **_], offset: T, @@ -594,12 +621,13 @@ struct UnsafePointer[ type: The data type of SIMD vector elements. width: The size of the SIMD vector. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: offset: The offset to store to. val: The value to store. """ - self.offset(offset).store[alignment=alignment](val) + self.offset(offset).store[alignment=alignment, volatile=volatile](val) @always_inline fn store[ @@ -607,6 +635,7 @@ struct UnsafePointer[ offset_type: DType, //, *, alignment: Int = _default_alignment[type](), + volatile: Bool = False, ]( self: UnsafePointer[Scalar[type], *_, **_], offset: Scalar[offset_type], @@ -621,13 +650,16 @@ struct UnsafePointer[ type: The data type of SIMD vector elements. offset_type: The data type of the offset value. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: offset: The offset to store to. val: The value to store. """ constrained[offset_type.is_integral(), "offset must be integer"]() - self.offset(int(offset))._store[alignment=alignment](val) + self.offset(int(offset))._store[alignment=alignment, volatile=volatile]( + val + ) @always_inline fn store[ @@ -636,6 +668,7 @@ struct UnsafePointer[ offset_type: DType, //, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ]( self: UnsafePointer[Scalar[type], *_, **_], offset: Scalar[offset_type], @@ -651,17 +684,23 @@ struct UnsafePointer[ width: The size of the SIMD vector. offset_type: The data type of the offset value. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: offset: The offset to store to. val: The value to store. """ constrained[offset_type.is_integral(), "offset must be integer"]() - self.offset(int(offset))._store[alignment=alignment](val) + self.offset(int(offset))._store[alignment=alignment, volatile=volatile]( + val + ) @always_inline("nodebug") fn store[ - type: DType, //, *, alignment: Int = _default_alignment[type]() + type: DType, //, + *, + alignment: Int = _default_alignment[type](), + volatile: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], val: Scalar[type]): """Stores a single element value. @@ -671,11 +710,12 @@ struct UnsafePointer[ Parameters: type: The data type of SIMD vector elements. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: val: The value to store. """ - self._store[alignment=alignment](val) + self._store[alignment=alignment, volatile=volatile](val) @always_inline("nodebug") fn store[ @@ -683,6 +723,7 @@ struct UnsafePointer[ width: Int, //, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], val: SIMD[type, width]): """Stores a single element value. @@ -693,11 +734,12 @@ struct UnsafePointer[ type: The data type of SIMD vector elements. width: The size of the SIMD vector. alignment: The minimal alignment of the address. + volatile: Whether the operation is volatile or not. Args: val: The value to store. """ - self._store[alignment=alignment](val) + self._store[alignment=alignment, volatile=volatile](val) @always_inline("nodebug") fn _store[ @@ -705,14 +747,22 @@ struct UnsafePointer[ width: Int, *, alignment: Int = _default_alignment[type, width](), + volatile: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], val: SIMD[type, width]): constrained[width > 0, "width must be a positive integer value"]() constrained[ alignment > 0, "alignment must be a positive integer value" ]() - __mlir_op.`pop.store`[alignment = alignment.value]( - val, self.bitcast[SIMD[type, width]]().address - ) + + @parameter + if volatile: + __mlir_op.`pop.store`[ + alignment = alignment.value, isVolatile = __mlir_attr.unit + ](val, self.bitcast[SIMD[type, width]]().address) + else: + __mlir_op.`pop.store`[alignment = alignment.value]( + val, self.bitcast[SIMD[type, width]]().address + ) @always_inline("nodebug") fn strided_load[ @@ -820,9 +870,7 @@ struct UnsafePointer[ type: DType, //, *, width: Int = 1, - alignment: Int = alignof[ - SIMD[type, width] - ]() if triple_is_nvidia_cuda() else 1, + alignment: Int = _default_alignment[type, width](), ]( self: UnsafePointer[Scalar[type], *_, **_], offset: SIMD[_, width], diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 86b735004f..478207ec36 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -286,6 +286,21 @@ def test_load_and_store_simd(): assert_equal(ptr2[i], i // 4 * 4) +def test_volatile_load_and_store_simd(): + var ptr = UnsafePointer[Int8].alloc(16) + for i in range(16): + ptr[i] = i + for i in range(0, 16, 4): + var vec = ptr.load[width=4, volatile=True](i) + assert_equal(vec, SIMD[DType.int8, 4](i, i + 1, i + 2, i + 3)) + + var ptr2 = UnsafePointer[Int8].alloc(16) + for i in range(0, 16, 4): + ptr2.store[volatile=True](i, SIMD[DType.int8, 4](i)) + for i in range(16): + assert_equal(ptr2[i], i // 4 * 4) + + def main(): test_address_of() @@ -308,3 +323,4 @@ def main(): test_alignment() test_offset() test_load_and_store_simd() + test_volatile_load_and_store_simd() From ed8ed6a5cfa8e27e0d4fd1d3e9327c4e8ca021fa Mon Sep 17 00:00:00 2001 From: Taylor Date: Mon, 14 Oct 2024 15:25:49 -0600 Subject: [PATCH 1752/2019] [External] [Docs] Fix `__origin_of` to `__lifetime_of` in changelog.md (#49087) [External] [Docs] Fix `__origin_of` to `__lifetime_of` in changelog.md The search and replace was a little too aggressive in this case. Signed-off-by: Taylor Pool Co-authored-by: Taylor Closes modularml/mojo#3665 MODULAR_ORIG_COMMIT_REV_ID: a7c14b4f91add180bba7dcc348e16c59732a0053 --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 80ab99591e..47d6198be3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -267,7 +267,7 @@ what we publish. library to better clarify that parameters of this type indicate where a reference is derived from, not the more complicated notion of where a variable is initialized and destroyed. Please see [the proposal](https://github.com/modularml/mojo/blob/main/proposals/lifetimes-keyword-renaming.md) - for more information and rationale. As a consequence `__origin_of` is now + for more information and rationale. As a consequence `__lifetime_of` is now named `__origin_of`. ### ❌ Removed From ed5c37b8bed5554187e94e94ba33e69eb3b319b8 Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 14 Oct 2024 15:26:11 -0600 Subject: [PATCH 1753/2019] [External] [stdlib] Fix example in `simd.mojo` (#49086) [External] [stdlib] Fix example in `simd.mojo` Co-authored-by: soraros Closes modularml/mojo#3666 MODULAR_ORIG_COMMIT_REV_ID: d40beb040b1671589fa461ea5d2aa31f6a1118ab --- stdlib/src/builtin/simd.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c2f22b3074..e748710482 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2025,9 +2025,9 @@ struct SIMD[type: DType, size: Int]( which is an unrolled for loop. The pseudocode of this function is: - ```mojo - var result = SIMD[Self.type, mask_size]() - for i in range(0, mask_size): + ``` + result = SIMD[Self.type, mask_size]() + for i in range(mask_size): result[i] = self[int(mask[i])] ``` From 76774707507b6f6dfd26607f965658ef5eaa57fd Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 14 Oct 2024 15:32:22 -0600 Subject: [PATCH 1754/2019] [External] [stdlib] Implement `folded_mul` using MLIR, fix UB (#49090) [External] [stdlib] Implement `folded_mul` using MLIR, fix UB - Reimplement `_folded_multiply` in MLIR to ensure efficient code generation. - Fix a very subtle bug: `a >> a.size` causes undefined behavior. CC @mzaks. - Add `debug_assert` to `SIMD.__lshift__` and `SIMD.__rshift__` to catch similar issues in the future. Now it generates ```asm movq %rsi, %rdx mulxq %rdi, %rcx, %rax xorq %rcx, %rax retq ``` Co-authored-by: soraros Closes modularml/mojo#3652 MODULAR_ORIG_COMMIT_REV_ID: f3b1b1552724726fbd1dd976632f2f0b9be81770 --- stdlib/src/builtin/simd.mojo | 6 +++ stdlib/src/hashlib/_ahash.mojo | 17 ++++-- stdlib/test/hashlib/test_ahash.mojo | 81 +++++++++++++++------------- stdlib/test/hashlib/test_hasher.mojo | 20 +++---- 4 files changed, 72 insertions(+), 52 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index e748710482..7f217bee54 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -934,6 +934,9 @@ struct SIMD[type: DType, size: Int]( """ constrained[type.is_integral(), "must be an integral type"]() debug_assert(all(rhs >= 0), "unhandled negative value") + debug_assert( + all(rhs < bitwidthof[type]()), "unhandled value greater than size" + ) return __mlir_op.`pop.shl`(self.value, rhs.value) @always_inline("nodebug") @@ -951,6 +954,9 @@ struct SIMD[type: DType, size: Int]( """ constrained[type.is_integral(), "must be an integral type"]() debug_assert(all(rhs >= 0), "unhandled negative value") + debug_assert( + all(rhs < bitwidthof[type]()), "unhandled value greater than size" + ) return __mlir_op.`pop.shr`(self.value, rhs.value) @always_inline("nodebug") diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index ad71de0899..9770fe84ac 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -34,9 +34,17 @@ fn _folded_multiply(lhs: UInt64, rhs: UInt64) -> UInt64: Returns: A value which is similar in its bitpattern to result of a folded multply. """ - var b1 = lhs * byte_swap(rhs) - var b2 = byte_swap(lhs) * (~rhs) - return b1 ^ byte_swap(b2) + l = __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( + lhs.value + ) + r = __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( + rhs.value + ) + m = __mlir_op.`pop.mul`(l, r) + res = SIMD[DType.uint64, 2]( + __mlir_op.`pop.bitcast`[_type = __mlir_type.`!pop.simd<2, ui64>`](m) + ) + return res[0] ^ res[1] @always_inline @@ -152,7 +160,6 @@ struct AHasher[key: U256](_Hasher): Args: new_data: Value used for update. """ - var v64: SIMD[DType.uint64, new_data.size] @parameter if new_data.type.is_floating_point(): @@ -186,4 +193,4 @@ struct AHasher[key: U256](_Hasher): """ var rot = self.buffer & 63 var folded = _folded_multiply(self.buffer, self.pad) - return (folded << rot) | (folded >> (64 - rot)) + return (folded << rot) | (folded >> ((64 - rot) & 63)) diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index 27d27a1b1e..1ec984086b 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from bit import pop_count +from builtin._location import __call_location from hashlib._ahash import AHasher from hashlib.hash import hash as old_hash from hashlib._hasher import _hash_with_hasher as hash @@ -590,6 +591,7 @@ def dif_bits(i1: UInt64, i2: UInt64) -> Int: return int(pop_count(i1 ^ i2)) +@always_inline def assert_dif_hashes(hashes: List[UInt64], upper_bound: Int): for i in range(len(hashes)): for j in range(i + 1, len(hashes)): @@ -599,6 +601,7 @@ def assert_dif_hashes(hashes: List[UInt64], upper_bound: Int): str("Index: {}:{}, diff between: {} and {} is: {}").format( i, j, hashes[i], hashes[j], diff ), + location=__call_location(), ) @@ -656,8 +659,8 @@ def test_avalanche(): ), ) - assert_dif_hashes(hashes0, 14) - assert_dif_hashes(hashes1, 15) + assert_dif_hashes(hashes0, 12) + assert_dif_hashes(hashes1, 12) def test_trailing_zeros(): @@ -685,6 +688,7 @@ def test_trailing_zeros(): assert_dif_hashes(hashes1, 18) +@always_inline def assert_fill_factor[ label: String ](words: List[String], num_buckets: Int, lower_bound: Float64): @@ -705,9 +709,11 @@ def assert_fill_factor[ str("Fill factor for {} is {}, provided lower boound was {}").format( label, fill_factor, lower_bound ), + location=__call_location(), ) +@always_inline def assert_fill_factor_old_hash[ label: String ](words: List[String], num_buckets: Int, lower_bound: Float64): @@ -728,6 +734,7 @@ def assert_fill_factor_old_hash[ str("Fill factor for {} is {}, provided lower bound was {}").format( label, fill_factor, lower_bound ), + location=__call_location(), ) @@ -770,7 +777,7 @@ def test_fill_factor(): assert_fill_factor["LV"](words, len(words), 0.63) assert_fill_factor["LV"](words, len(words) // 2, 0.86) assert_fill_factor["LV"](words, len(words) // 4, 0.98) - assert_fill_factor["LV"](words, len(words) // 12, 1.0) + assert_fill_factor["LV"](words, len(words) // 13, 1.0) assert_fill_factor_old_hash["LV"](words, len(words), 0.015) @@ -793,43 +800,43 @@ def test_fill_factor(): def test_hash_simd_values(): fn hash(value: SIMD) -> UInt64: - var hasher = AHasher[SIMD[DType.uint64, 4](0)]() + hasher = hasher0() hasher._update_with_simd(value) return hasher^.finish() - assert_equal(hash(SIMD[DType.float16, 1](1.5)), 18058966248987367737) - assert_equal(hash(SIMD[DType.float32, 1](1.5)), 13467270117196531127) - assert_equal(hash(SIMD[DType.float64, 1](1.5)), 719560574162820089) - assert_equal(hash(SIMD[DType.float16, 1](1)), 1206414632147291024) - assert_equal(hash(SIMD[DType.float32, 1](1)), 9557262614467209093) - assert_equal(hash(SIMD[DType.float64, 1](1)), 7961842588256067709) - - assert_equal(hash(SIMD[DType.int8, 1](1)), 4759877148789019546) - assert_equal(hash(SIMD[DType.int16, 1](1)), 4759877148789019546) - assert_equal(hash(SIMD[DType.int32, 1](1)), 4759877148789019546) - assert_equal(hash(SIMD[DType.int64, 1](1)), 4759877148789019546) - assert_equal(hash(SIMD[DType.bool, 1](True)), 4759877148789019546) - - assert_equal(hash(SIMD[DType.int8, 1](-1)), 7301741325190448010) - assert_equal(hash(SIMD[DType.int16, 1](-1)), 7301741325190448010) - assert_equal(hash(SIMD[DType.int32, 1](-1)), 7301741325190448010) - assert_equal(hash(SIMD[DType.int64, 1](-1)), 7301741325190448010) - - assert_equal(hash(SIMD[DType.int8, 1](0)), 16659764227661506736) - assert_equal(hash(SIMD[DType.int8, 2](0)), 1562284133626399299) - assert_equal(hash(SIMD[DType.int8, 4](0)), 17902233708981521127) - assert_equal(hash(SIMD[DType.int8, 8](0)), 632562262308536351) - assert_equal(hash(SIMD[DType.int8, 16](0)), 7298276873920245913) - assert_equal(hash(SIMD[DType.int8, 32](0)), 7079057015559465054) - assert_equal(hash(SIMD[DType.int8, 64](0)), 11911213625103275990) - - assert_equal(hash(SIMD[DType.int32, 1](0)), 16659764227661506736) - assert_equal(hash(SIMD[DType.int32, 2](0)), 1562284133626399299) - assert_equal(hash(SIMD[DType.int32, 4](0)), 17902233708981521127) - assert_equal(hash(SIMD[DType.int32, 8](0)), 632562262308536351) - assert_equal(hash(SIMD[DType.int32, 16](0)), 7298276873920245913) - assert_equal(hash(SIMD[DType.int32, 32](0)), 7079057015559465054) - assert_equal(hash(SIMD[DType.int32, 64](0)), 11911213625103275990) + assert_equal(hash(SIMD[DType.float16, 1](1.5)), 17170570249477226196) + assert_equal(hash(SIMD[DType.float32, 1](1.5)), 13132675331935815936) + assert_equal(hash(SIMD[DType.float64, 1](1.5)), 5099020174652265565) + assert_equal(hash(SIMD[DType.float16, 1](1)), 2425465491952508383) + assert_equal(hash(SIMD[DType.float32, 1](1)), 7268380206556411294) + assert_equal(hash(SIMD[DType.float64, 1](1)), 1824371972732385641) + + assert_equal(hash(SIMD[DType.int8, 1](1)), 7121024052126637824) + assert_equal(hash(SIMD[DType.int16, 1](1)), 7121024052126637824) + assert_equal(hash(SIMD[DType.int32, 1](1)), 7121024052126637824) + assert_equal(hash(SIMD[DType.int64, 1](1)), 7121024052126637824) + assert_equal(hash(SIMD[DType.bool, 1](True)), 7121024052126637824) + + assert_equal(hash(SIMD[DType.int8, 1](-1)), 3480139124131340807) + assert_equal(hash(SIMD[DType.int16, 1](-1)), 3480139124131340807) + assert_equal(hash(SIMD[DType.int32, 1](-1)), 3480139124131340807) + assert_equal(hash(SIMD[DType.int64, 1](-1)), 3480139124131340807) + + assert_equal(hash(SIMD[DType.int8, 1](0)), 14824966480498192933) + assert_equal(hash(SIMD[DType.int8, 2](0)), 4666323910194780317) + assert_equal(hash(SIMD[DType.int8, 4](0)), 17500360606583578786) + assert_equal(hash(SIMD[DType.int8, 8](0)), 17090405845262319422) + assert_equal(hash(SIMD[DType.int8, 16](0)), 14743323736766031385) + assert_equal(hash(SIMD[DType.int8, 32](0)), 10765559911200264018) + assert_equal(hash(SIMD[DType.int8, 64](0)), 810077408472869726) + + assert_equal(hash(SIMD[DType.int32, 1](0)), 14824966480498192933) + assert_equal(hash(SIMD[DType.int32, 2](0)), 4666323910194780317) + assert_equal(hash(SIMD[DType.int32, 4](0)), 17500360606583578786) + assert_equal(hash(SIMD[DType.int32, 8](0)), 17090405845262319422) + assert_equal(hash(SIMD[DType.int32, 16](0)), 14743323736766031385) + assert_equal(hash(SIMD[DType.int32, 32](0)), 10765559911200264018) + assert_equal(hash(SIMD[DType.int32, 64](0)), 810077408472869726) def main(): diff --git a/stdlib/test/hashlib/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo index 0ae9bdb662..44bf5eed15 100644 --- a/stdlib/test/hashlib/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -140,7 +140,7 @@ def test_with_ahasher(): SomeHashableStruct(42), SomeHashableStruct(10), List[UInt8](1, 2, 3) ) var hash_value = _hash_with_hasher(hashable1) - assert_equal(hash_value, 12427888534629009331) + assert_equal(hash_value, 7948090191592501094) var hashable2 = ComplexHashableStructWithListAndWideSIMD( SomeHashableStruct(42), SomeHashableStruct(10), @@ -148,21 +148,21 @@ def test_with_ahasher(): SIMD[DType.uint32, 4](1, 2, 3, 4), ) hash_value = _hash_with_hasher(hashable2) - assert_equal(hash_value, 9463003097190363949) + assert_equal(hash_value, 1754891767834419861) def test_hash_hashable_with_hasher_types(): - assert_equal(_hash_with_hasher(DType.uint64), 5919096275431609211) - assert_equal(_hash_with_hasher(""), 12914568033466041247) - assert_equal(_hash_with_hasher(str("")), 12914568033466041247) - assert_equal(_hash_with_hasher(StringRef("")), 12914568033466041247) - assert_equal(_hash_with_hasher(Int(-123)), 7309790389124252133) - assert_equal(_hash_with_hasher(UInt(123)), 11416101997646518198) + assert_equal(_hash_with_hasher(DType.uint64), 6529703120343940753) + assert_equal(_hash_with_hasher(""), 11583516797109448887) + assert_equal(_hash_with_hasher(str("")), 11583516797109448887) + assert_equal(_hash_with_hasher(StringRef("")), 11583516797109448887) + assert_equal(_hash_with_hasher(Int(-123)), 4720193641311814362) + assert_equal(_hash_with_hasher(UInt(123)), 4498397628805512285) assert_equal( _hash_with_hasher(SIMD[DType.float16, 4](0.1, -0.1, 12, 0)), - 236488340994185196, + 3806818604433176740, ) - assert_equal(_hash_with_hasher(Path("/tmp")), 862170317972693446) + assert_equal(_hash_with_hasher(Path("/tmp")), 16491058316913697698) # Hash value of PythonObject is randomized by default # can be deterministic if env var PYTHONHASHSEED is set assert_true(_hash_with_hasher(PythonObject("hello")) != 0) From 049fe098a94f8c29c7d08e684fb12edda7d11208 Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 14 Oct 2024 15:36:13 -0600 Subject: [PATCH 1755/2019] [External] [stdlib] Implement `sub_with_sat` using LLVM intrinsic (#49091) [External] [stdlib] Implement `sub_with_sat` using LLVM intrinsic Co-authored-by: soraros Closes modularml/mojo#3654 MODULAR_ORIG_COMMIT_REV_ID: e732d75dcb4dbf37120d107c0a9031309ec2a1cf --- stdlib/src/utils/_utf8_validation.mojo | 43 +++++++++++++------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index a27abbf72f..53c0a257df 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -14,7 +14,7 @@ """Implement fast utf-8 validation using SIMD instructions. References for this algorithm: -J. Keiser, D. Lemire, Validating UTF-8 In Less Than One Instruction Per Byte, +J. Keiser, D. Lemire, Validating UTF-8 In Less Than One Instruction Per Byte, Software: Practice and Experience 51 (5), 2021 https://arxiv.org/abs/2010.03090 @@ -26,6 +26,7 @@ https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs """ from memory import UnsafePointer +from sys.intrinsics import llvm_intrinsic alias TOO_SHORT: UInt8 = 1 << 0 alias TOO_LONG: UInt8 = 1 << 1 @@ -81,25 +82,23 @@ alias shuf3 = SIMD[DType.uint8, 16]( @always_inline -fn extract_vector[ - simd_size: Int, //, offset: Int -](a: SIMD[DType.uint8, simd_size], b: SIMD[DType.uint8, simd_size]) -> SIMD[ - DType.uint8, simd_size +fn _extract_vector[ + width: Int, //, offset: Int +](a: SIMD[DType.uint8, width], b: SIMD[DType.uint8, width]) -> SIMD[ + DType.uint8, width ]: - """This can be a single instruction on some architectures.""" - concatenated = a.join(b) - return concatenated.slice[simd_size, offset=offset]() + # generates a single `vpalignr` on x86 with AVX + return a.join(b).slice[width, offset=offset]() -@always_inline -fn _subtract_with_saturation[ - simd_size: Int, //, b: Int -](a: SIMD[DType.uint8, simd_size]) -> SIMD[DType.uint8, simd_size]: - """The equivalent of https://doc.rust-lang.org/core/arch/x86_64/fn._mm_subs_epu8.html . - This can be a single instruction on some architectures. - """ - alias b_as_vector = SIMD[DType.uint8, simd_size](b) - return max(a, b_as_vector) - b_as_vector +@always_inline("nodebug") +fn _sub_with_saturation[ + width: Int, // +](a: SIMD[DType.uint8, width], b: SIMD[DType.uint8, width]) -> SIMD[ + DType.uint8, width +]: + # generates a single `vpsubusb` on x86 with AVX + return llvm_intrinsic["llvm.usub.sat", __type_of(a)](a, b) fn validate_chunk[ @@ -112,7 +111,7 @@ fn validate_chunk[ alias v80 = SIMD[DType.uint8, simd_size](0x80) alias third_byte = 0b11100000 - 0x80 alias fourth_byte = 0b11110000 - 0x80 - var prev1 = extract_vector[simd_size - 1]( + var prev1 = _extract_vector[simd_size - 1]( previous_input_block, current_block ) var byte_1_high = shuf1._dynamic_shuffle(prev1 >> 4) @@ -120,14 +119,14 @@ fn validate_chunk[ var byte_2_high = shuf3._dynamic_shuffle(current_block >> 4) var sc = byte_1_high & byte_1_low & byte_2_high - var prev2 = extract_vector[simd_size - 2]( + var prev2 = _extract_vector[simd_size - 2]( previous_input_block, current_block ) - var prev3 = extract_vector[simd_size - 3]( + var prev3 = _extract_vector[simd_size - 3]( previous_input_block, current_block ) - var is_third_byte = _subtract_with_saturation[third_byte](prev2) - var is_fourth_byte = _subtract_with_saturation[fourth_byte](prev3) + var is_third_byte = _sub_with_saturation(prev2, third_byte) + var is_fourth_byte = _sub_with_saturation(prev3, fourth_byte) var must23 = is_third_byte | is_fourth_byte var must23_as_80 = must23 & v80 return must23_as_80 ^ sc From 1786071c3286baf228a9d74ab536d078d0166aae Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 14 Oct 2024 16:28:09 -0600 Subject: [PATCH 1756/2019] [External] [stdlib] Introduce `Byte` (#49099) [External] [stdlib] Introduce `Byte` And use it in `memory.mojo`. Related to https://github.com/modularml/mojo/issues/3634. Co-authored-by: soraros Closes modularml/mojo#3670 MODULAR_ORIG_COMMIT_REV_ID: c69c46207857968bb2c4d752c3b5c2952de081ec --- stdlib/src/builtin/simd.mojo | 3 +++ stdlib/src/memory/memory.mojo | 38 +++++++++++++++-------------- stdlib/src/prelude/__init__.mojo | 1 + stdlib/test/memory/test_memory.mojo | 10 ++++---- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7f217bee54..c7e70db460 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -98,6 +98,9 @@ alias Float32 = Scalar[DType.float32] alias Float64 = Scalar[DType.float64] """Represents a 64-bit floating point value.""" +alias Byte = UInt8 +"""Represents a byte (backed by an 8-bit unsigned integer).""" + # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index aedaca4d96..ef8d0ba1bf 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -135,7 +135,7 @@ fn memcmp[ byte_count // sizeof[DType.int32](), ) - return _memcmp_impl(s1.bitcast[Int8](), s2.bitcast[Int8](), byte_count) + return _memcmp_impl(s1.bitcast[Byte](), s2.bitcast[Byte](), byte_count) # ===----------------------------------------------------------------------===# @@ -145,7 +145,7 @@ fn memcmp[ @always_inline fn _memcpy_impl( - dest_data: UnsafePointer[Int8, *_], src_data: __type_of(dest_data), n: Int + dest_data: UnsafePointer[Byte, *_], src_data: __type_of(dest_data), n: Int ): """Copies a memory area. @@ -178,26 +178,28 @@ fn _memcpy_impl( if n <= 16: if n >= 8: - var ui64_size = sizeof[Int64]() - dest_data.bitcast[Int64]().store[alignment=1]( - 0, src_data.bitcast[Int64]().load[alignment=1](0) + var ui64_size = sizeof[UInt64]() + dest_data.bitcast[UInt64]().store[alignment=1]( + 0, src_data.bitcast[UInt64]().load[alignment=1](0) ) - dest_data.offset(n - ui64_size).bitcast[Int64]().store[alignment=1]( + dest_data.offset(n - ui64_size).bitcast[UInt64]().store[ + alignment=1 + ]( 0, src_data.offset(n - ui64_size) - .bitcast[Int64]() + .bitcast[UInt64]() .load[alignment=1](0), ) return - var ui32_size = sizeof[Int32]() - dest_data.bitcast[Int32]().store[alignment=1]( - 0, src_data.bitcast[Int32]().load[alignment=1](0) + var ui32_size = sizeof[UInt32]() + dest_data.bitcast[UInt32]().store[alignment=1]( + 0, src_data.bitcast[UInt32]().load[alignment=1](0) ) - dest_data.offset(n - ui32_size).bitcast[Int32]().store[alignment=1]( + dest_data.offset(n - ui32_size).bitcast[UInt32]().store[alignment=1]( 0, src_data.offset(n - ui32_size) - .bitcast[Int32]() + .bitcast[UInt32]() .load[alignment=1](0), ) return @@ -243,8 +245,8 @@ fn memcpy[ """ var n = count * sizeof[dest.type]() _memcpy_impl( - dest.bitcast[Int8, origin=MutableAnyOrigin](), - src.bitcast[Int8, origin=MutableAnyOrigin](), + dest.bitcast[Byte, origin=MutableAnyOrigin](), + src.bitcast[Byte, origin=MutableAnyOrigin](), n, ) @@ -257,8 +259,8 @@ fn memcpy[ @always_inline("nodebug") fn _memset_impl[ address_space: AddressSpace -](ptr: UnsafePointer[UInt8, address_space], value: UInt8, count: Int): - alias simd_width = simdwidthof[UInt8]() +](ptr: UnsafePointer[Byte, address_space], value: Byte, count: Int): + alias simd_width = simdwidthof[Byte]() var vector_end = _align_down(count, simd_width) for i in range(0, vector_end, simd_width): @@ -271,7 +273,7 @@ fn _memset_impl[ @always_inline fn memset[ type: AnyType, address_space: AddressSpace -](ptr: UnsafePointer[type, address_space], value: UInt8, count: Int): +](ptr: UnsafePointer[type, address_space], value: Byte, count: Int): """Fills memory with the given value. Parameters: @@ -283,7 +285,7 @@ fn memset[ value: The value to fill with. count: Number of elements to fill (in elements, not bytes). """ - _memset_impl(ptr.bitcast[UInt8](), value, count * sizeof[type]()) + _memset_impl(ptr.bitcast[Byte](), value, count * sizeof[type]()) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index b7941d92fb..60f47ba5c7 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -113,6 +113,7 @@ from builtin.simd import ( Float16, Float32, Float64, + Byte, SIMD, ) from builtin.type_aliases import AnyTrivialRegType diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 9deb983caa..90195ba443 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -125,16 +125,16 @@ def test_memcmp(): def test_memcmp_overflow(): - var p1 = UnsafePointer[Int8].alloc(1) - var p2 = UnsafePointer[Int8].alloc(1) + p1 = UnsafePointer[Byte].alloc(1) + p2 = UnsafePointer[Byte].alloc(1) p1.store(-120) p2.store(120) - var c = memcmp(p1, p2, 1) - assert_equal(c, -1, "-120 is smaller than 120") + c = memcmp(p1, p2, 1) + assert_equal(c, 1) c = memcmp(p2, p1, 1) - assert_equal(c, 1, "120 is bigger than -120") + assert_equal(c, -1) def test_memcmp_simd(): From 934f45b54255d12ee494b449f76ffbc0080aa28a Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 14 Oct 2024 16:44:32 -0600 Subject: [PATCH 1757/2019] [External] [stdlib] Document CPython bindings - Round 1 (#49085) - [X] Document PyGILState_STATE - [X] Rename PyKeyValuePair to PyKeysValuePair to match CPython - [X] Add docstrings to all the structs in cpython.mojo - [X] Add single docstring regarding C-API docs to all the functions Related issue: https://github.com/modularml/mojo/issues/3661 Co-authored-by: Manuel Saelices Closes modularml/mojo#3664 MODULAR_ORIG_COMMIT_REV_ID: d464718f686c30d0c223c812b2fa8eb67fb654b6 --- stdlib/src/python/_cpython.mojo | 315 ++++++++++++++++++++++++++++---- 1 file changed, 275 insertions(+), 40 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 65d87f835e..fa400147eb 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -63,10 +63,10 @@ alias Py_TPFLAGS_DEFAULT = 0 # TODO(MSTDL-892): Change this to alias ffi.C_ssize_t alias Py_ssize_t = Int -# Ref: https://docs.python.org/3/c-api/structures.html#c.PyCFunction # TODO(MOCO-1138): # This should be a C ABI function pointer, not a Mojo ABI function. alias PyCFunction = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr +"""See https://docs.python.org/3/c-api/structures.html#c.PyCFunction.""" alias METH_VARARGS = 0x1 @@ -82,7 +82,16 @@ alias newfunc = fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr @value @register_passable("trivial") struct PyGILState_STATE: + """Represents the state of the Python Global Interpreter Lock (GIL). + + This struct is used to store and manage the state of the GIL, + which is crucial for thread-safe operations in Python. + + See https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/pystate.h#L76 + """ + var current_state: c_int + """The current state of the GIL.""" alias PyGILState_LOCKED = c_int(0) alias PyGILState_UNLOCKED = c_int(1) @@ -96,11 +105,21 @@ struct PyThreadState: @value @register_passable("trivial") -struct PyKeyValuePair: +struct PyKeysValuePair: + """Represents a key-value pair in a Python dictionary iteration. + + This struct is used to store the result of iterating over a Python dictionary, + containing the key, value, current position, and success status of the iteration. + """ + var key: PyObjectPtr + """The key of the current dictionary item.""" var value: PyObjectPtr + """The value of the current dictionary item.""" var position: c_int + """The current position in the dictionary iteration.""" var success: Bool + """Indicates whether the iteration was successful.""" @value @@ -110,6 +129,9 @@ struct PyObjectPtr: It is crucial that this type has the same size and alignment as `PyObject*` for FFI ABI correctness. + + This struct provides methods for initialization, null checking, + equality comparison, and conversion to integer representation. """ # ===-------------------------------------------------------------------===# @@ -138,6 +160,7 @@ struct PyObjectPtr: @always_inline fn __init__(inout self): + """Initialize a null PyObjectPtr.""" self.unsized_obj_ptr = UnsafePointer[PyObject]() # ===-------------------------------------------------------------------===# @@ -145,9 +168,25 @@ struct PyObjectPtr: # ===-------------------------------------------------------------------===# fn __eq__(self, rhs: PyObjectPtr) -> Bool: + """Compare two PyObjectPtr for equality. + + Args: + rhs: The right-hand side PyObjectPtr to compare. + + Returns: + Bool: True if the pointers are equal, False otherwise. + """ return int(self.unsized_obj_ptr) == int(rhs.unsized_obj_ptr) fn __ne__(self, rhs: PyObjectPtr) -> Bool: + """Compare two PyObjectPtr for inequality. + + Args: + rhs: The right-hand side PyObjectPtr to compare. + + Returns: + Bool: True if the pointers are not equal, False otherwise. + """ return not (self == rhs) # ===-------------------------------------------------------------------===# @@ -194,21 +233,46 @@ struct PyObjectPtr: return UnsafePointer[T].address_of(mojo_obj_ptr[].mojo_value) fn is_null(self) -> Bool: + """Check if the pointer is null. + + Returns: + Bool: True if the pointer is null, False otherwise. + """ return int(self.unsized_obj_ptr) == 0 # TODO: Consider removing this and inlining int(p.value) into callers fn _get_ptr_as_int(self) -> Int: + """Get the pointer value as an integer. + + Returns: + Int: The integer representation of the pointer. + """ return int(self.unsized_obj_ptr) @value @register_passable struct PythonVersion: + """ + Represents a Python version with major, minor, and patch numbers. + """ + var major: Int + """The major version number.""" var minor: Int + """The minor version number.""" var patch: Int + """The patch version number.""" fn __init__(inout self, version: StringRef): + """Initialize a PythonVersion object from a version string. + + Args: + version: A string representing the Python version (e.g., "3.9.5"). + + The version string is parsed to extract major, minor, and patch numbers. + If parsing fails for any component, it defaults to -1. + """ var version_string = String(version) var components = InlineArray[Int, 3](-1) var start = 0 @@ -243,22 +307,28 @@ fn _py_finalize(lib: DLHandle): # Ref https://docs.python.org/3/c-api/structures.html#c.PyMethodDef @value struct PyMethodDef: + """Represents a Python method definition. + + This struct is used to define methods for Python modules or types. + """ + # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# var method_name: UnsafePointer[c_char] # called ml_name in CPython + """A pointer to the name of the method as a C string.""" # TODO(MSTDL-887): Support keyword-argument only methods - # Pointer to the function to call var method_impl: PyCFunction + """A function pointer to the implementation of the method.""" - # Flags bits indicating how the call should be constructed. # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef for the various calling conventions var method_flags: c_int + """Flags indicating how the method should be called.""" - # Points to the contents of the docstring for the module. var method_docstring: UnsafePointer[c_char] + """A pointer to the docstring for the method as a C string.""" # ===-------------------------------------------------------------------===# # Life cycle methods @@ -288,6 +358,14 @@ struct PyMethodDef: func_name: StringLiteral, docstring: StringLiteral = "", ]() -> Self: + """ + Create a PyMethodDef for a function. + + Parameters: + func: The function to wrap. + func_name: The name of the function. + docstring: The docstring for the function. + """ # TODO(MSTDL-896): # Support a way to get the name of the function from its parameter # type, similar to `get_linkage_name()`? @@ -307,6 +385,12 @@ fn _null_fn_ptr[T: AnyTrivialRegType]() -> T: struct PyTypeObject: + """ + The opaque C structure of the objects used to describe types. + + See https://docs.python.org/3/c-api/type.html#c.PyTypeObject + """ + # TODO(MSTDL-877): # Fill this out based on # https://docs.python.org/3/c-api/typeobj.html#pytypeobject-definition @@ -316,6 +400,12 @@ struct PyTypeObject: @value @register_passable("trivial") struct PyType_Spec: + """ + Structure defining a type’s behavior. + + See https://docs.python.org/3/c-api/type.html#c.PyType_Spec + """ + var name: UnsafePointer[c_char] var basicsize: c_int var itemsize: c_int @@ -326,6 +416,13 @@ struct PyType_Spec: @value @register_passable("trivial") struct PyType_Slot: + """ + Structure defining optional functionality of a type, containing a slot ID + and a value pointer. + + See https://docs.python.org/3/c-api/type.html#c.PyType_Slot + """ + var slot: c_int var pfunc: OpaquePointer @@ -356,6 +453,12 @@ struct PyType_Slot: @value struct PyObject(Stringable, Representable, Formattable): + """ + All object types are extensions of this type. This is a type which contains the information Python needs to treat a pointer to an object as an object. In a normal “release” build, it contains only the object’s reference count and a pointer to the corresponding type object. Nothing is actually declared to be a PyObject, but every pointer to a Python object can be cast to a PyObject*. + + See https://docs.python.org/3/c-api/structures.html#c.PyObject + """ + var object_ref_count: Int # FIXME: should we use `PyObjectPtr`? I don't think so! var object_type: UnsafePointer[PyTypeObject] @@ -480,18 +583,24 @@ struct PyModuleDef_Base(Stringable, Representable, Formattable): writer.write(")") -# Ref: https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot @value struct PyModuleDef_Slot: + """ + See https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot. + """ + var slot: c_int var value: OpaquePointer -# Ref: https://docs.python.org/3/c-api/module.html#c.PyModuleDef struct PyModuleDef(Stringable, Representable, Formattable): - # The Python module definition structs that holds all of the information needed - # to create a module. Typically, there is a 1:1 correspondence beteeen a `PyMethodDef` - # and a module. + """ + The Python module definition structs that holds all of the information needed + to create a module. + + See https://docs.python.org/3/c-api/module.html#c.PyModuleDef + """ + var base: PyModuleDef_Base # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef @@ -595,16 +704,26 @@ struct PyModuleDef(Stringable, Representable, Formattable): struct CPython: + """ + Handle to the CPython interpreter present in the current process. + """ + # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# var lib: DLHandle + """The handle to the CPython shared library.""" var dict_type: PyObjectPtr + """The type object for Python dictionaries.""" var logging_enabled: Bool + """Whether logging is enabled.""" var version: PythonVersion + """The version of the Python runtime.""" var total_ref_count: UnsafePointer[Int] + """The total reference count of all Python objects.""" var init_error: StringRef + """An error message if initialization failed.""" # ===-------------------------------------------------------------------===# # Life cycle methods @@ -743,12 +862,16 @@ struct CPython: self.total_ref_count.init_pointee_move(v - 1) fn Py_IncRef(inout self, ptr: PyObjectPtr): + """See https://docs.python.org/3/c-api/refcounting.html#c.Py_IncRef.""" + self.log(ptr._get_ptr_as_int(), " INCREF refcnt:", self._Py_REFCNT(ptr)) self.lib.get_function[fn (PyObjectPtr) -> None]("Py_IncRef")(ptr) self._inc_total_rc() fn Py_DecRef(inout self, ptr: PyObjectPtr): + """See https://docs.python.org/3/c-api/refcounting.html#c.Py_DecRef.""" + self.log(ptr._get_ptr_as_int(), " DECREF refcnt:", self._Py_REFCNT(ptr)) self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) @@ -782,21 +905,31 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyGILState_Ensure(inout self) -> PyGILState_STATE: + """See https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure.""" + return self.lib.get_function[fn () -> PyGILState_STATE]( "PyGILState_Ensure" )() fn PyGILState_Release(inout self, state: PyGILState_STATE): + """See https://docs.python.org/3/c-api/init.html#c.PyGILState_Release. + """ + self.lib.get_function[fn (PyGILState_STATE) -> None]( "PyGILState_Release" )(state) fn PyEval_SaveThread(inout self) -> UnsafePointer[PyThreadState]: + """See https://docs.python.org/3/c-api/init.html#c.PyEval_SaveThread.""" + return self.lib.get_function[fn () -> UnsafePointer[PyThreadState]]( "PyEval_SaveThread" )() fn PyEval_RestoreThread(inout self, state: UnsafePointer[PyThreadState]): + """See https://docs.python.org/3/c-api/init.html#c.PyEval_RestoreThread. + """ + self.lib.get_function[fn (UnsafePointer[PyThreadState]) -> None]( "PyEval_RestoreThread" )(state) @@ -806,6 +939,8 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyDict_New(inout self) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/dict.html#c.PyDict_New.""" + var r = self.lib.get_function[fn () -> PyObjectPtr]("PyDict_New")() self.log( @@ -818,10 +953,11 @@ struct CPython: return r # int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val) - # ref: https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem fn PyDict_SetItem( inout self, dict_obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr ) -> c_int: + """See https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem.""" + var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int ](StringRef("PyDict_SetItem"))(dict_obj, key, value) @@ -838,6 +974,9 @@ struct CPython: fn PyDict_GetItemWithError( inout self, dict_obj: PyObjectPtr, key: PyObjectPtr ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItemWithError. + """ + var result = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr ](StringRef("PyDict_GetItemWithError"))(dict_obj, key) @@ -847,6 +986,8 @@ struct CPython: return result fn PyDict_Check(inout self, maybe_dict: PyObjectPtr) -> Bool: + """See https://docs.python.org/3/c-api/dict.html#c.PyDict_Check.""" + var my_type = self.PyObject_Type(maybe_dict) var my_type_as_int = my_type._get_ptr_as_int() var dict_type = self.PyDict_Type() @@ -855,15 +996,16 @@ struct CPython: return result fn PyDict_Type(inout self) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/dict.html#c.PyDict_Type.""" if self.dict_type.is_null(): self.dict_type = self.lib.get_function[PyObjectPtr]("PyDict_Type") return self.dict_type # int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) - # ref: https://docs.python.org/3/c-api/dict.html#c.PyDict_Next fn PyDict_Next( inout self, dictionary: PyObjectPtr, p: Int - ) -> PyKeyValuePair: + ) -> PyKeysValuePair: + """See https://docs.python.org/3/c-api/dict.html#c.PyDict_Next.""" var key = PyObjectPtr() var value = PyObjectPtr() var v = p @@ -899,7 +1041,7 @@ struct CPython: ) _ = v - return PyKeyValuePair { + return PyKeysValuePair { key: key, value: value, position: position.take_pointee(), @@ -914,6 +1056,9 @@ struct CPython: inout self, name: StringRef, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/import.html#c.PyImport_ImportModule. + """ + var r = self.lib.get_function[fn (UnsafePointer[UInt8]) -> PyObjectPtr]( "PyImport_ImportModule" )(name.data) @@ -930,6 +1075,8 @@ struct CPython: return r fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/import.html#c.PyImport_AddModule. + """ return self.lib.get_function[fn (UnsafePointer[c_char]) -> PyObjectPtr]( "PyImport_AddModule" )(name.unsafe_ptr().bitcast[c_char]()) @@ -938,6 +1085,8 @@ struct CPython: inout self, name: String, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/module.html#c.PyModule_Create.""" + # TODO: See https://docs.python.org/3/c-api/module.html#c.PyModule_Create # and https://github.com/pybind/pybind11/blob/a1d00916b26b187e583f3bce39cd59c3b0652c32/include/pybind11/pybind11.h#L1326 # for what we want to do essentially here. @@ -960,13 +1109,15 @@ struct CPython: var module_api_version = 1013 return create_module_fn(module_def_ptr, module_api_version) - # int PyModule_AddFunctions(PyObject *module, PyMethodDef *functions) - # ref: https://docs.python.org/3/c-api/module.html#c.PyModule_AddFunctions fn PyModule_AddFunctions( inout self, mod: PyObjectPtr, functions: UnsafePointer[PyMethodDef], ) -> c_int: + """See https://docs.python.org/3/c-api/module.html#c.PyModule_AddFunctions. + """ + + # int PyModule_AddFunctions(PyObject *module, PyMethodDef *functions) var add_functions_fn = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[PyMethodDef]) -> c_int ]("PyModule_AddFunctions") @@ -979,6 +1130,9 @@ struct CPython: name: UnsafePointer[c_char], value: PyObjectPtr, ) -> c_int: + """See https://docs.python.org/3/c-api/module.html#c.PyModule_AddObjectRef. + """ + var func = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[c_char], PyObjectPtr) -> c_int ]("PyModule_AddObjectRef") @@ -986,6 +1140,9 @@ struct CPython: return func(module, name, value) fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/module.html#c.PyModule_GetDict. + """ + var value = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyModule_GetDict" )(name) @@ -1020,6 +1177,8 @@ struct CPython: fn PyType_FromSpec( inout self, spec: UnsafePointer[PyType_Spec] ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/type.html#c.PyType_FromSpec.""" + var func = self.lib.get_function[ fn (UnsafePointer[PyType_Spec]) -> PyObjectPtr ]("PyType_FromSpec") @@ -1030,11 +1189,11 @@ struct CPython: # Python Evaluation # ===-------------------------------------------------------------------===# - # int PyRun_SimpleString(const char *command) - # ref: https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleString fn PyRun_SimpleString(inout self, strref: StringRef) -> Bool: """Executes the given Python code. + See https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleString + Args: strref: The python code to execute. @@ -1042,6 +1201,7 @@ struct CPython: `True` if the code executed successfully or `False` if the code raised an exception. """ + # int PyRun_SimpleString(const char *command) var status = self.lib.get_function[fn (UnsafePointer[UInt8]) -> c_int]( StringRef("PyRun_SimpleString") )(strref.data) @@ -1056,6 +1216,7 @@ struct CPython: locals: PyObjectPtr, run_mode: Int, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_String.""" var result = self.lib.get_function[ fn ( UnsafePointer[UInt8], Int32, PyObjectPtr, PyObjectPtr @@ -1081,6 +1242,8 @@ struct CPython: globals: PyObjectPtr, locals: PyObjectPtr, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/veryhigh.html#c.PyEval_EvalCode. + """ var result = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr ]("PyEval_EvalCode")(co, globals, locals) @@ -1088,6 +1251,9 @@ struct CPython: return result fn PyEval_GetBuiltins(inout self) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetBuiltins. + """ + return self.lib.get_function[fn () -> PyObjectPtr]( "PyEval_GetBuiltins" )() @@ -1098,6 +1264,9 @@ struct CPython: filename: StringRef, compile_mode: Int, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/veryhigh.html#c.Py_CompileString. + """ + var r = self.lib.get_function[ fn ( UnsafePointer[UInt8], UnsafePointer[UInt8], Int32 @@ -1110,14 +1279,15 @@ struct CPython: # Python Object operations # ===-------------------------------------------------------------------===# - # int Py_Is(PyObject *x, PyObject *y) - # ref: https://docs.python.org/3/c-api/structures.html#c.Py_Is fn Py_Is( inout self, rhs: PyObjectPtr, lhs: PyObjectPtr, ) -> Bool: + """See https://docs.python.org/3/c-api/structures.html#c.Py_Is.""" + if self.version.minor >= 10: + # int Py_Is(PyObject *x, PyObject *y) var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr) -> c_int ]("Py_Is")(rhs, lhs) @@ -1126,6 +1296,8 @@ struct CPython: return rhs == lhs fn PyObject_Type(inout self, obj: PyObjectPtr) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_Type.""" + var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyObject_Type" ) @@ -1133,6 +1305,8 @@ struct CPython: return f(obj) fn PyObject_Str(inout self, obj: PyObjectPtr) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_Str.""" + var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyObject_Str" ) @@ -1142,6 +1316,9 @@ struct CPython: fn PyObject_GetItem( inout self, obj: PyObjectPtr, key: PyObjectPtr ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem. + """ + var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr ]("PyObject_GetItem")(obj, key) @@ -1162,6 +1339,9 @@ struct CPython: fn PyObject_SetItem( inout self, obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr ) -> c_int: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_SetItem. + """ + var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int ]("PyObject_SetItem")(obj, key, value) @@ -1184,6 +1364,9 @@ struct CPython: obj: PyObjectPtr, name: StringRef, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttrString. + """ + var r = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[UInt8]) -> PyObjectPtr ]("PyObject_GetAttrString")(obj, name.data) @@ -1201,11 +1384,13 @@ struct CPython: self._inc_total_rc() return r - # int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v) - # ref: https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString fn PyObject_SetAttrString( inout self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr ) -> c_int: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString. + """ + + # int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v) var r = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[UInt8], PyObjectPtr) -> c_int ]("PyObject_SetAttrString")(obj, name.data, new_value) @@ -1228,6 +1413,9 @@ struct CPython: callable_obj: PyObjectPtr, args: PyObjectPtr, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/call.html#c.PyObject_CallObject. + """ + var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr ]("PyObject_CallObject")(callable_obj, args) @@ -1249,6 +1437,8 @@ struct CPython: args: PyObjectPtr, kwargs: PyObjectPtr, ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/call.html#c.PyObject_Call.""" + var r = self.lib.get_function[ fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr ]("PyObject_Call")(callable_obj, args, kwargs) @@ -1264,12 +1454,13 @@ struct CPython: self._inc_total_rc() return r - # int PyObject_IsTrue(PyObject *o) - # ref: https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue fn PyObject_IsTrue( inout self, obj: PyObjectPtr, ) -> c_int: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue.""" + + # int PyObject_IsTrue(PyObject *o) return self.lib.get_function[fn (PyObjectPtr) -> c_int]( "PyObject_IsTrue" )(obj) @@ -1278,6 +1469,8 @@ struct CPython: inout self, obj: PyObjectPtr, ) -> Int: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_Length.""" + return int( self.lib.get_function[fn (PyObjectPtr) -> Int]("PyObject_Length")( obj @@ -1285,6 +1478,8 @@ struct CPython: ) fn PyObject_Hash(inout self, obj: PyObjectPtr) -> Int: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_Hash.""" + return int( self.lib.get_function[fn (PyObjectPtr) -> Int]("PyObject_Hash")(obj) ) @@ -1292,6 +1487,9 @@ struct CPython: fn PyObject_GetIter( inout self, traversablePyObject: PyObjectPtr ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/object.html#c.PyObject_GetIter. + """ + var iter = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyObject_GetIter" )(traversablePyObject) @@ -1314,6 +1512,8 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyTuple_New(inout self, count: Int) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/tuple.html#c.PyTuple_New.""" + var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( StringRef("PyTuple_New") )(count) @@ -1332,21 +1532,24 @@ struct CPython: fn PyTuple_GetItem( inout self, tuple: PyObjectPtr, pos: Py_ssize_t ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GetItem.""" + return self.lib.get_function[ fn (PyObjectPtr, Py_ssize_t) -> PyObjectPtr ]("PyTuple_GetItem")(tuple, pos) - # int PyTuple_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o) - # ref: https://docs.python.org/3/c-api/tuple.html#c.PyTuple_SetItem fn PyTuple_SetItem( inout self, tuple_obj: PyObjectPtr, index: Int, element: PyObjectPtr, ) -> c_int: + """See https://docs.python.org/3/c-api/tuple.html#c.PyTuple_SetItem.""" + # PyTuple_SetItem steals the reference - the element object will be # destroyed along with the tuple self._dec_total_rc() + # int PyTuple_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o) return self.lib.get_function[ fn (PyObjectPtr, Int, PyObjectPtr) -> c_int ](StringRef("PyTuple_SetItem"))(tuple_obj, index, element) @@ -1356,6 +1559,8 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyList_New(inout self, length: Int) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/list.html#c.PyList_New.""" + var r = self.lib.get_function[fn (Int) -> PyObjectPtr]("PyList_New")( length ) @@ -1374,6 +1579,8 @@ struct CPython: fn PyList_SetItem( inout self, list_obj: PyObjectPtr, index: Int, value: PyObjectPtr ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/list.html#c.PyList_SetItem.""" + # PyList_SetItem steals the reference - the element object will be # destroyed along with the list self._dec_total_rc() @@ -1384,6 +1591,8 @@ struct CPython: fn PyList_GetItem( inout self, list_obj: PyObjectPtr, index: Int ) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/list.html#c.PyList_GetItem.""" + return self.lib.get_function[fn (PyObjectPtr, Int) -> PyObjectPtr]( "PyList_GetItem" )(list_obj, index) @@ -1393,9 +1602,6 @@ struct CPython: # ref: https://docs.python.org/3/c-api/concrete.html # ===-------------------------------------------------------------------===# - # The None Object - # ref: https://docs.python.org/3/c-api/none.html - # PyObject *Py_None # https://docs.python.org/3/c-api/none.html#c.Py_None fn Py_None(inout self) -> PyObjectPtr: @@ -1419,8 +1625,9 @@ struct CPython: # ref: https://docs.python.org/3/c-api/bool.html # PyObject *PyBool_FromLong(long v) - # ref: https://docs.python.org/3/c-api/bool.html#c.PyBool_FromLong fn PyBool_FromLong(inout self, value: c_long) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/bool.html#c.PyBool_FromLong.""" + r = self.lib.get_function[fn (c_long) -> PyObjectPtr]( "PyBool_FromLong" )(value) @@ -1440,8 +1647,10 @@ struct CPython: # ref: https://docs.python.org/3/c-api/long.html # PyObject *PyLong_FromSsize_t(Py_ssize_t v) - # ref: https://docs.python.org/3/c-api/long.html#c.PyLong_FromSsize_t fn PyLong_FromSsize_t(inout self, value: c_ssize_t) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/long.html#c.PyLong_FromSsize_t. + """ + r = self.lib.get_function[fn (c_ssize_t) -> PyObjectPtr]( "PyLong_FromSsize_t" )(value) @@ -1458,8 +1667,9 @@ struct CPython: return r # PyObject *PyLong_FromSize_t(Py_ssize_t v) - # ref: https://docs.python.org/3/c-api/long.html#c.PyLong_FromSize_t fn PyLong_FromSize_t(inout self, value: c_size_t) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/long.html#c.PyLong_FromSize_t.""" + r = self.lib.get_function[fn (c_size_t) -> PyObjectPtr]( "PyLong_FromSize_t" )(value) @@ -1476,8 +1686,9 @@ struct CPython: return r # Py_ssize_t PyLong_AsSsize_t(PyObject *pylong) - # ref: https://docs.python.org/3/c-api/long.html#c.PyLong_AsSsize_t fn PyLong_AsSsize_t(inout self, py_object: PyObjectPtr) -> c_ssize_t: + """See https://docs.python.org/3/c-api/long.html#c.PyLong_AsSsize_t.""" + return self.lib.get_function[fn (PyObjectPtr) -> c_ssize_t]( "PyLong_AsSsize_t" )(py_object) @@ -1486,8 +1697,10 @@ struct CPython: # ref: https://docs.python.org/3/c-api/float.html # PyObject *PyFloat_FromDouble(double v)¶ - # ref: https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble fn PyFloat_FromDouble(inout self, value: Float64) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble. + """ + r = self.lib.get_function[fn (Float64) -> PyObjectPtr]( "PyFloat_FromDouble" )(value) @@ -1504,8 +1717,9 @@ struct CPython: return r # double PyFloat_AsDouble(PyObject *pyfloat) - # ref: https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble fn PyFloat_AsDouble(inout self, py_object: PyObjectPtr) -> Float64: + """See https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble.""" + return self.lib.get_function[fn (PyObjectPtr) -> Float64]( "PyFloat_AsDouble" )(py_object) @@ -1514,8 +1728,10 @@ struct CPython: # https://docs.python.org/3/c-api/unicode.html # PyObject *PyUnicode_DecodeUTF8(const char *str, Py_ssize_t size, const char *errors) - # ref: https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8 fn PyUnicode_DecodeUTF8(inout self, strref: StringRef) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8. + """ + r = self.lib.get_function[ fn ( UnsafePointer[c_char], @@ -1540,8 +1756,10 @@ struct CPython: return r # const char *PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *size) - # ref: https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: + """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize. + """ + s = StringRef() s.data = self.lib.get_function[ fn (PyObjectPtr, UnsafePointer[c_ssize_t]) -> UnsafePointer[c_char] @@ -1557,15 +1775,21 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyErr_Clear(inout self): + """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear.""" + self.lib.get_function[fn () -> None]("PyErr_Clear")() fn PyErr_Occurred(inout self) -> Bool: + """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred. + """ + var value = self.lib.get_function[fn () -> PyObjectPtr]( "PyErr_Occurred" )() return not value.is_null() fn PyErr_Fetch(inout self) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Fetch.""" var type = PyObjectPtr() var value = PyObjectPtr() var traceback = PyObjectPtr() @@ -1599,6 +1823,9 @@ struct CPython: inout self, type: PyObjectPtr, ): + """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetNone. + """ + var func = self.lib.get_function[fn (PyObjectPtr) -> None]( "PyErr_SetNone" ) @@ -1610,6 +1837,9 @@ struct CPython: type: PyObjectPtr, message: UnsafePointer[c_char], ): + """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetString. + """ + self.lib.get_function[fn (PyObjectPtr, UnsafePointer[c_char]) -> None]( "PyErr_SetString" )(type, message) @@ -1645,6 +1875,8 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyIter_Next(inout self, iterator: PyObjectPtr) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/iter.html#c.PyIter_Next.""" + var next_obj = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( "PyIter_Next" )(iterator) @@ -1663,17 +1895,20 @@ struct CPython: self._inc_total_rc() return next_obj - # int PyIter_Check(PyObject *o) - # ref: https://docs.python.org/3/c-api/iter.html#c.PyIter_Check fn PyIter_Check(inout self, obj: PyObjectPtr) -> Bool: + """See https://docs.python.org/3/c-api/iter.html#c.PyIter_Check.""" + + # int PyIter_Check(PyObject *o) var follows_iter_protocol = self.lib.get_function[ fn (PyObjectPtr) -> c_int ]("PyIter_Check")(obj) return follows_iter_protocol != 0 # int PySequence_Check(PyObject *o) - # https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check fn PySequence_Check(inout self, obj: PyObjectPtr) -> Bool: + """See https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check. + """ + var follows_seq_protocol = self.lib.get_function[ fn (PyObjectPtr) -> c_int ]("PySequence_Check")(obj) From d58813c37062e260ccc315e74d93e3d5ee0be497 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 14 Oct 2024 18:58:29 -0700 Subject: [PATCH 1758/2019] [******] Mark traits for Index function as infer only, NFC MODULAR_ORIG_COMMIT_REV_ID: 5d0a37f986930b38cb22b00b27de838a20c0b254 --- stdlib/src/utils/index.mojo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index e1d46054f1..0c40efaa57 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -810,7 +810,7 @@ struct IndexList[ # Factory functions for creating index. # ===----------------------------------------------------------------------===# @always_inline -fn Index[T0: Intable](x: T0) -> IndexList[1]: +fn Index[T0: Intable, //](x: T0) -> IndexList[1]: """Constructs a 1-D Index from the given value. Parameters: @@ -839,7 +839,7 @@ fn Index(x: UInt) -> IndexList[1]: @always_inline -fn Index[T0: Intable, T1: Intable](x: T0, y: T1) -> IndexList[2]: +fn Index[T0: Intable, T1: Intable, //](x: T0, y: T1) -> IndexList[2]: """Constructs a 2-D Index from the given values. Parameters: @@ -872,7 +872,7 @@ fn Index(x: UInt, y: UInt) -> IndexList[2]: @always_inline fn Index[ - T0: Intable, T1: Intable, T2: Intable + T0: Intable, T1: Intable, T2: Intable, // ](x: T0, y: T1, z: T2) -> IndexList[3]: """Constructs a 3-D Index from the given values. @@ -894,7 +894,7 @@ fn Index[ @always_inline fn Index[ - T0: Intable, T1: Intable, T2: Intable, T3: Intable + T0: Intable, T1: Intable, T2: Intable, T3: Intable, // ](x: T0, y: T1, z: T2, w: T3) -> IndexList[4]: """Constructs a 4-D Index from the given values. @@ -918,7 +918,7 @@ fn Index[ @always_inline fn Index[ - T0: Intable, T1: Intable, T2: Intable, T3: Intable, T4: Intable + T0: Intable, T1: Intable, T2: Intable, T3: Intable, T4: Intable, // ](x: T0, y: T1, z: T2, w: T3, v: T4) -> IndexList[5]: """Constructs a 5-D Index from the given values. From f56773c317598e159c87ae59fdaeb0a8d307370c Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 14 Oct 2024 19:38:10 -0700 Subject: [PATCH 1759/2019] [Stdlib] Add param flag for specifying bitwidth for Index construction MODULAR_ORIG_COMMIT_REV_ID: 3ece49099d60834bbfab468e18e06bda7c899b91 --- stdlib/src/utils/index.mojo | 99 +++++++++++++++++++++++++------ stdlib/test/utils/test_index.mojo | 9 +++ 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 0c40efaa57..05b8765129 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -810,11 +810,20 @@ struct IndexList[ # Factory functions for creating index. # ===----------------------------------------------------------------------===# @always_inline -fn Index[T0: Intable, //](x: T0) -> IndexList[1]: +fn Index[ + T0: Intable, //, + *, + element_bitwidth: Int = bitwidthof[Int](), + unsigned: Bool = False, +](x: T0) -> IndexList[ + 1, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 1-D Index from the given value. Parameters: T0: The type of the 1st argument. + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. Args: x: The initial value. @@ -822,29 +831,47 @@ fn Index[T0: Intable, //](x: T0) -> IndexList[1]: Returns: The constructed IndexList. """ - return IndexList[1](int(x)) + return __type_of(result)(int(x)) @always_inline -fn Index(x: UInt) -> IndexList[1]: +fn Index[ + *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False +](x: UInt) -> IndexList[ + 1, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 1-D Index from the given value. + Parameters: + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. + Args: x: The initial value. Returns: The constructed IndexList. """ - return IndexList[1](x.value) + return __type_of(result)(int(x)) @always_inline -fn Index[T0: Intable, T1: Intable, //](x: T0, y: T1) -> IndexList[2]: +fn Index[ + T0: Intable, + T1: Intable, //, + *, + element_bitwidth: Int = bitwidthof[Int](), + unsigned: Bool = False, +](x: T0, y: T1) -> IndexList[ + 2, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 2-D Index from the given values. Parameters: T0: The type of the 1st argument. T1: The type of the 2nd argument. + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. Args: x: The 1st initial value. @@ -853,13 +880,21 @@ fn Index[T0: Intable, T1: Intable, //](x: T0, y: T1) -> IndexList[2]: Returns: The constructed IndexList. """ - return IndexList[2](int(x), int(y)) + return __type_of(result)(int(x), int(y)) @always_inline -fn Index(x: UInt, y: UInt) -> IndexList[2]: +fn Index[ + *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False +](x: UInt, y: UInt) -> IndexList[ + 2, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 2-D Index from the given values. + Parameters: + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. + Args: x: The 1st initial value. y: The 2nd initial value. @@ -867,19 +902,28 @@ fn Index(x: UInt, y: UInt) -> IndexList[2]: Returns: The constructed IndexList. """ - return IndexList[2](x.value, y.value) + return __type_of(result)(int(x), int(y)) @always_inline fn Index[ - T0: Intable, T1: Intable, T2: Intable, // -](x: T0, y: T1, z: T2) -> IndexList[3]: + T0: Intable, + T1: Intable, + T2: Intable, //, + *, + element_bitwidth: Int = bitwidthof[Int](), + unsigned: Bool = False, +](x: T0, y: T1, z: T2) -> IndexList[ + 3, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 3-D Index from the given values. Parameters: T0: The type of the 1st argument. T1: The type of the 2nd argument. T2: The type of the 3rd argument. + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. Args: x: The 1st initial value. @@ -889,13 +933,21 @@ fn Index[ Returns: The constructed IndexList. """ - return IndexList[3](int(x), int(y), int(z)) + return __type_of(result)(int(x), int(y), int(z)) @always_inline fn Index[ - T0: Intable, T1: Intable, T2: Intable, T3: Intable, // -](x: T0, y: T1, z: T2, w: T3) -> IndexList[4]: + T0: Intable, + T1: Intable, + T2: Intable, + T3: Intable, //, + *, + element_bitwidth: Int = bitwidthof[Int](), + unsigned: Bool = False, +](x: T0, y: T1, z: T2, w: T3) -> IndexList[ + 4, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 4-D Index from the given values. Parameters: @@ -903,6 +955,8 @@ fn Index[ T1: The type of the 2nd argument. T2: The type of the 3rd argument. T3: The type of the 4th argument. + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. Args: x: The 1st initial value. @@ -913,13 +967,22 @@ fn Index[ Returns: The constructed IndexList. """ - return IndexList[4](int(x), int(y), int(z), int(w)) + return __type_of(result)(int(x), int(y), int(z), int(w)) @always_inline fn Index[ - T0: Intable, T1: Intable, T2: Intable, T3: Intable, T4: Intable, // -](x: T0, y: T1, z: T2, w: T3, v: T4) -> IndexList[5]: + T0: Intable, + T1: Intable, + T2: Intable, + T3: Intable, + T4: Intable, //, + *, + element_bitwidth: Int = bitwidthof[Int](), + unsigned: Bool = False, +](x: T0, y: T1, z: T2, w: T3, v: T4) -> IndexList[ + 5, element_bitwidth=element_bitwidth, unsigned=unsigned +] as result: """Constructs a 5-D Index from the given values. Parameters: @@ -928,6 +991,8 @@ fn Index[ T2: The type of the 3rd argument. T3: The type of the 4th argument. T4: The type of the 5th argument. + element_bitwidth: The bitwidth of the underlying integer element type. + unsigned: Whether the integer is signed or unsigned. Args: x: The 1st initial value. @@ -939,7 +1004,7 @@ fn Index[ Returns: The constructed IndexList. """ - return IndexList[5](int(x), int(y), int(z), int(w), int(v)) + return __type_of(result)(int(x), int(y), int(z), int(w), int(v)) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/utils/test_index.mojo b/stdlib/test/utils/test_index.mojo index 0cecb3f12a..d262c9fdbe 100644 --- a/stdlib/test/utils/test_index.mojo +++ b/stdlib/test/utils/test_index.mojo @@ -53,6 +53,15 @@ def test_cast(): ) +def test_index(): + assert_equal(str(Index[element_bitwidth=64](1, 2, 3)), "(1, 2, 3)") + assert_equal(str(Index[element_bitwidth=32](1, 2, 3)), "(1, 2, 3)") + assert_equal( + str(Index[element_bitwidth=32, unsigned=True](1, 2, 3)), "(1, 2, 3)" + ) + + def main(): test_basics() test_cast() + test_index() From 74eef9b69096a258e813c9de306c89b95d00ec65 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 15 Oct 2024 12:13:38 -0600 Subject: [PATCH 1760/2019] [stdlib] Add `@doc_private` to MLIR initializers The documentation for various initializers that work on MLIR types is pretty verbose and not helpful to users (e.g. on "hover" action in the LSP) since it just shows all the various internal dialects. This isn't super useful for users, so just mark these initializers with the `@doc_private` decorator so there won't be generated docs for these. MODULAR_ORIG_COMMIT_REV_ID: c965afe75242c35af66af4110f9c935e82296e6d --- stdlib/src/builtin/bool.mojo | 3 +++ stdlib/src/builtin/builtin_list.mojo | 4 ++++ stdlib/src/builtin/int_literal.mojo | 2 ++ stdlib/src/collections/optional.mojo | 3 +++ stdlib/src/memory/pointer.mojo | 3 +++ stdlib/src/memory/unsafe_pointer.mojo | 2 ++ stdlib/src/python/python_object.mojo | 2 ++ stdlib/src/utils/index.mojo | 2 ++ 8 files changed, 21 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 3978c7021c..f40e6dec9f 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -15,6 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ +from builtin._documentation import doc_private from collections import Set, List from utils._visualizers import lldb_formatter_wrapping_type @@ -129,6 +130,7 @@ struct Bool( """ self.value = other.value + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.i1): """Construct a Bool value given a __mlir_type.i1 value. @@ -138,6 +140,7 @@ struct Bool( """ self.value = value + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.`!pop.scalar`): """Construct a Bool value given a `!pop.scalar` value. diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 3e8ed9a01d..5b20b83f86 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,6 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ +from builtin._documentation import doc_private from memory import Pointer, UnsafePointer @@ -173,6 +174,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): """ self = value + @doc_private @always_inline fn __init__(inout self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. @@ -324,6 +326,7 @@ struct VariadicListMem[ # ===-------------------------------------------------------------------===# # Provide support for borrowed variadic arguments. + @doc_private @always_inline fn __init__(inout self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. @@ -575,6 +578,7 @@ struct VariadicPack[ # Life cycle methods # ===-------------------------------------------------------------------===# + @doc_private @always_inline fn __init__(inout self, value: Self._mlir_type, is_owned: Bool): """Constructs a VariadicPack from the internal representation. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 4ea94f5c49..5a6493a693 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" +from builtin._documentation import doc_private from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @@ -57,6 +58,7 @@ struct IntLiteral( """Default constructor.""" self.value = __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` + @doc_private @always_inline("nodebug") fn __init__(inout self, value: __mlir_type.`!kgen.int_literal`): """Construct IntLiteral from the given mlir !kgen.int_literal value. diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 1742159cc4..f69e0d97ff 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -31,6 +31,7 @@ print(d) # prints 2 ``` """ +from builtin._documentation import doc_private from os import abort from utils import Variant @@ -103,6 +104,7 @@ struct Optional[T: CollectionElement]( # TODO(MSTDL-715): # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. + @doc_private fn __init__(inout self, value: NoneType._mlir_type): """Construct an empty Optional. @@ -420,6 +422,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): # TODO(MSTDL-715): # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. + @doc_private fn __init__(inout self, value: NoneType._mlir_type): """Construct an empty Optional. diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 3a3adb9a78..ddbfa54ffb 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -19,6 +19,8 @@ from memory import Pointer ``` """ +from builtin._documentation import doc_private + # TODO: This is kept for compatibility, remove this in the future. alias Reference = Pointer[*_] @@ -302,6 +304,7 @@ struct Pointer[ # Initializers # ===------------------------------------------------------------------===# + @doc_private @always_inline("nodebug") fn __init__(inout self, *, _mlir_value: Self._mlir_type): """Constructs a Pointer from its MLIR prepresentation. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 446410acd0..8ef6e9a03b 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,6 +19,7 @@ from memory import UnsafePointer ``` """ +from builtin._documentation import doc_private from sys import alignof, sizeof, triple_is_nvidia_cuda from sys.intrinsics import ( _mlirtype_is_eq, @@ -102,6 +103,7 @@ struct UnsafePointer[ """Create a null pointer.""" self.address = __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] + @doc_private @always_inline fn __init__(inout self, value: Self._mlir_type): """Create a pointer with the input value. diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 8973c4d176..a6f7d9a221 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -19,6 +19,7 @@ from python import PythonObject ``` """ +from builtin._documentation import doc_private from sys.intrinsics import _type_is_eq from memory import UnsafePointer @@ -328,6 +329,7 @@ struct PythonObject( # TODO(MSTDL-715): # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. + @doc_private fn __init__(inout self, none: NoneType._mlir_type): """Initialize a none value object from a `None` literal. diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 05b8765129..af08df0305 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -22,6 +22,7 @@ from utils import IndexList from sys import bitwidthof from builtin.dtype import _int_type_of_width, _uint_type_of_width +from builtin._documentation import doc_private from builtin.io import _get_dtype_printf_format, _snprintf from collections.string import _calc_initial_buffer_size @@ -198,6 +199,7 @@ struct IndexList[ """Constructs a static int tuple of the given size.""" self = 0 + @doc_private @always_inline fn __init__(inout self, value: __mlir_type.index): """Constructs a sized 1 static int tuple of given the element value. From 38679a099a0860db3afbb51a5b03ac88745f124d Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 15 Oct 2024 12:29:50 -0600 Subject: [PATCH 1761/2019] [docs] Add changelog entry for `Byte` alias Promote the use of the new `Byte` alias by documenting it in the changelog. MODULAR_ORIG_COMMIT_REV_ID: 963dabe1c487a6e29c1145d5222e59ddfdb3fb7f --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 47d6198be3..f446b58dc7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -192,6 +192,10 @@ what we publish. [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable), and can be compared for pointer equivalence using `a is b`. +- There is now a [`Byte`](/mojo/stdlib/builtin/simd/Byte) alias to better + express intent when working with a pack of bits. + ([PR #3670](https://github.com/modularml/mojo/pull/3670) by [@soraos](https://github.com/soraros)). + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` From e6bbf05bc404c0f76963941315801dc42c924d54 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:46:31 -0600 Subject: [PATCH 1762/2019] [External] [stdlib] Make `Slice` step `Optional` (#49037) Follow up from https://github.com/modularml/mojo/pull/3064#issuecomment-2195753384 Make `step` member of `Slice` `Optional` in order to better match the behaviour/semantics of the Python slice. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3160 MODULAR_ORIG_COMMIT_REV_ID: dcfe50395856e038b64deb6d88e8d68a69edeae4 --- docs/changelog.md | 5 +++++ stdlib/src/builtin/builtin_slice.mojo | 10 +++++----- stdlib/test/builtin/test_slice.mojo | 8 ++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f446b58dc7..e0f482cdeb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -175,6 +175,11 @@ what we publish. return a ``` +- `Slice.step` is now an `Optional[Int]`, matching the optionality of + `slice.step` in Python. + ([PR #3160](https://github.com/modularml/mojo/pull/3160) by + [@bgreni](https://github.com/bgreni)) + - `StringRef` now implements `split()` which can be used to split a `StringRef` into a `List[StringRef]` by a delimiter. ([PR #2705](https://github.com/modularml/mojo/pull/2705) by [@fknfilewalker](https://github.com/fknfilewalker)) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index e8989e8cd9..d516891281 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -45,7 +45,7 @@ struct Slice( """The starting index of the slice.""" var end: Optional[Int] """The end index of the slice.""" - var step: Int + var step: Optional[Int] """The step increment value of the slice.""" # ===-------------------------------------------------------------------===# @@ -62,7 +62,7 @@ struct Slice( """ self.start = start self.end = end - self.step = 1 + self.step = None @always_inline fn __init__( @@ -80,7 +80,7 @@ struct Slice( """ self.start = start self.end = end - self.step = step.or_else(1) + self.step = step fn __init__(inout self, *, other: Self): """Creates a deep copy of the Slice. @@ -135,7 +135,7 @@ struct Slice( writer.write(", ") write_optional(self.end) writer.write(", ") - writer.write(repr(self.step)) + write_optional(self.step) writer.write(")") @always_inline @@ -200,7 +200,7 @@ struct Slice( Returns: A tuple containing three integers for start, end, and step. """ - var step = self.step + var step = self.step.or_else(1) var start = self.start var end = self.end diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 7736dae236..b552d13f0b 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -19,7 +19,7 @@ def test_none_end_folds(): var all_def_slice = slice(0, None, 1) assert_equal(all_def_slice.start.value(), 0) assert_true(all_def_slice.end is None) - assert_equal(all_def_slice.step, 1) + assert_equal(all_def_slice.step.value(), 1) # This requires parameter inference of StartT. @@ -74,11 +74,11 @@ def test_slice_stringable(): var s = SliceStringable() assert_equal(s[2::-1], "slice(2, None, -1)") assert_equal(s[1:-1:2], "slice(1, -1, 2)") - assert_equal(s[:-1], "slice(None, -1, 1)") - assert_equal(s[::], "slice(None, None, 1)") + assert_equal(s[:-1], "slice(None, -1, None)") + assert_equal(s[::], "slice(None, None, None)") assert_equal(s[::4], "slice(None, None, 4)") assert_equal(repr(slice(None, 2, 3)), "slice(None, 2, 3)") - assert_equal(repr(slice(10)), "slice(None, 10, 1)") + assert_equal(repr(slice(10)), "slice(None, 10, None)") def test_slice_eq(): From 6e448dee1e42519be28fe46987bbb470dd43a47d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 15 Oct 2024 21:36:45 -0700 Subject: [PATCH 1763/2019] [mojo-lang][CheckLifetimes] Notice AnyOrigin uses from arg conventions. (#49211) Operations like lit.ref.load can access an AnyOrigin, and thus need to extend values that those may be derived from. Not doing so can introduce miscompilations. This fixed a bug reported on discord. The removed testcases in check-lifetimes.mlir are too low level to be worth updating because they use anyorigin pervasively and are not realistic testcases. MODULAR_ORIG_COMMIT_REV_ID: 06e18c994832cc53828d5a62f6cc89ea5e0df767 --- stdlib/test/collections/test_list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index a7fe9260a8..96b88b8a83 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -903,7 +903,7 @@ def inner_test_list_dtor(): l.append(DtorCounter()) assert_equal(g_dtor_count, 0) - l.__del__() + l^.__del__() assert_equal(g_dtor_count, 1) From 797877200c1cb89a186c0b7bfff51be3d75b0bda Mon Sep 17 00:00:00 2001 From: soraros Date: Wed, 16 Oct 2024 07:29:08 -0600 Subject: [PATCH 1764/2019] [External] [stdlib] Add `expect` intrinsic (#49219) [External] [stdlib] Add `expect` intrinsic - Add `llvm.expect` intrinsic to stdlib - Refector `likely` and `unlikely` intrinsics to use `expect` - Mark `likely` and `unlikely` intrinsics as side-effect free Co-authored-by: soraros Closes modularml/mojo#3659 MODULAR_ORIG_COMMIT_REV_ID: 66f5725c4a9f3a088acd7ead95295347aaf7ccfb --- stdlib/src/sys/intrinsics.mojo | 40 +++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 1fd9b3c1b2..67d2767694 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1504,6 +1504,34 @@ struct _RegisterPackType[*a: AnyTrivialRegType]: return __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) +# ===----------------------------------------------------------------------=== # +# expect +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn expect[T: AnyTrivialRegType, //, expected_val: T](val: T) -> T: + """Provides information about expected (the most probable) value of `val`, + which can be used by optimizers. + + Constraints: + Only work with integer types. + + Parameters: + T: The type of the input value. + expected_val: The expected value of `val`. + + Args: + val: The input value. + + Returns: + The input value. + """ + return llvm_intrinsic["llvm.expect", T, has_side_effect=False]( + val, expected_val + ) + + # ===----------------------------------------------------------------------=== # # likely # ===----------------------------------------------------------------------=== # @@ -1515,12 +1543,12 @@ fn likely(val: Bool) -> Bool: `True`. This information can be used by optimizers. Args: - val: The input value which is likely to be `True` most of the time. + val: The input value which is likely to be `True` most of the time. Returns: - The input value. + The input value. """ - return llvm_intrinsic["llvm.expect", Bool](val, True) + return expect[True](val) # ===----------------------------------------------------------------------=== # @@ -1534,12 +1562,12 @@ fn unlikely(val: Bool) -> Bool: `False`. This information can be used by optimizers. Args: - val: The input value which is likely to be `False` most of the time. + val: The input value which is likely to be `False` most of the time. Returns: - The input value. + The input value. """ - return llvm_intrinsic["llvm.expect", Bool](val, False) + return expect[False](val) # ===----------------------------------------------------------------------=== # From 53cda826f816b2548b5b457775462e647d14865b Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:10:44 -0600 Subject: [PATCH 1765/2019] [External] [stdlib] Add `SIMD.reversed()` (#48056) [External] [stdlib] Add `SIMD.reversed()` Add `SIMD.reversed()` that reverses by index using shuffle. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3555 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3555 MODULAR_ORIG_COMMIT_REV_ID: c5c0e5fd7961b98ef7781463f9ed66ff1f6d8ae2 --- stdlib/src/builtin/simd.mojo | 26 ++++++++++++++++++++++++++ stdlib/test/builtin/test_simd.mojo | 18 ++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c7e70db460..32ea2a732c 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2776,6 +2776,32 @@ struct SIMD[type: DType, size: Int]( "llvm.vector.splice", Self, has_side_effect=False ](zero_simd, self, Int32(-shift)) + fn reversed(self) -> Self: + """Reverses the SIMD vector by indexes. + + Returns: + The by index reversed vector. + + Examples: + ```mojo + print(SIMD[DType.uint8, 4](1, 2, 3, 4).reversed()) # [4, 3, 2, 1] + ``` + . + """ + + fn build_idx() -> IndexList[size]: + var values = IndexList[size]() + var idx = 0 + + @parameter + for i in reversed(range(size)): + values[idx] = i + idx += 1 + return values + + alias idx = build_idx() + return self.shuffle[mask=idx]() + fn _pshuf_or_tbl1( lookup_table: SIMD[DType.uint8, 16], indices: SIMD[DType.uint8, 16] diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 6dfca29604..1aa89be2a4 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1805,6 +1805,23 @@ def test_float_conversion(): assert_almost_equal(float(UInt64(36)), 36.0) +def test_reversed(): + fn test[D: DType]() raises: + assert_equal(SIMD[D, 4](1, 2, 3, 4).reversed(), SIMD[D, 4](4, 3, 2, 1)) + + test[DType.uint8]() + test[DType.uint16]() + test[DType.uint32]() + test[DType.uint64]() + test[DType.int8]() + test[DType.int16]() + test[DType.int32]() + test[DType.int64]() + test[DType.float16]() + test[DType.float32]() + test[DType.float64]() + + def main(): test_abs() test_add() @@ -1860,4 +1877,5 @@ def main(): test_contains() test_comparison() test_float_conversion() + test_reversed() # TODO: add tests for __and__, __or__, anc comparison operators From fde725cecaf2b26cfb69106232858e36304b8f18 Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Wed, 16 Oct 2024 09:48:25 -0600 Subject: [PATCH 1766/2019] [External] [stdlib] Mojo-Python slice interoperability (#49094) [External] [stdlib] Mojo-Python slice interoperability Leveraging Mojo's `Slice` for more natural interop between `PythonObject`s, enabling something like below: ```mojo a = PythonObject([1, 2, 3, 4]) a_reversed = a[::-1] ``` - We leave C Python to handle slice parameters. - Negative cases such as `b[1.3:10]` or `b["1":10]` are handled by parser which would normally throw a `TypeError` in Python. Co-authored-by: Joshua James Venter Closes modularml/mojo#3549 MODULAR_ORIG_COMMIT_REV_ID: d88cc6a73af06bfb317d6d993611f013c706bce7 --- docs/changelog.md | 11 ++- stdlib/src/python/_cpython.mojo | 55 +++++++++++++ stdlib/src/python/python_object.mojo | 82 ++++++++++++++++++++ stdlib/test/python/test_python_object.mojo | 90 ++++++++++++++++++++++ 4 files changed, 236 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e0f482cdeb..040fdcad35 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -187,11 +187,18 @@ what we publish. - Support for multi-dimensional indexing for `PythonObject` ([PR #3583](https://github.com/modularml/mojo/pull/3583) by [@jjvraw](https://github.com/jjvraw)). +- Support for multi-dimensional indexing and slicing for `PythonObject` + (PRs [#3549](https://github.com/modularml/mojo/pull/3549), + [#3583](https://github.com/modularml/mojo/pull/3583) by [@jjvraw](https://github.com/jjvraw)). + ```mojo var np = Python.import_module("numpy") - var a = np.array(PythonObject([1,2,3,1,2,3])).reshape(2,3) + var a = np.array(PythonObject([1,2,3,4,5,6])).reshape(2,3) print((a[0, 1])) # 2 - ``` + print((a[1][::-1])) # [6 5 4] + ``` + + Note, that the syntax, `a[1, ::-1]`, is currently not supported. - [`Arc`](/mojo/stdlib/memory/arc/Arc) now implements [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable), and can be diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index fa400147eb..516320b288 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -1755,6 +1755,33 @@ struct CPython: self._inc_total_rc() return r + fn PySlice_FromSlice(inout self, slice: Slice) -> PyObjectPtr: + # Convert Mojo Slice to Python slice parameters + # Note: Deliberately avoid using `span.indices()` here and instead pass + # the Slice parameters directly to Python. Python's C implementation + # already handles such conditions, allowing Python to apply its own slice + # handling. + var py_start = self.Py_None() + var py_stop = self.Py_None() + var py_step = self.Py_None() + + if slice.start: + py_start = self.PyLong_FromSsize_t(c_ssize_t(slice.start.value())) + if slice.end: + py_stop = self.PyLong_FromSsize_t(c_ssize_t(slice.end.value())) + if slice.end: + py_step = self.PyLong_FromSsize_t(c_ssize_t(slice.step.value())) + + var py_slice = self.PySlice_New(py_start, py_stop, py_step) + + if py_start != self.Py_None(): + self.Py_DecRef(py_start) + if py_stop != self.Py_None(): + self.Py_DecRef(py_stop) + self.Py_DecRef(py_step) + + return py_slice + # const char *PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *size) fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize. @@ -1913,3 +1940,31 @@ struct CPython: fn (PyObjectPtr) -> c_int ]("PySequence_Check")(obj) return follows_seq_protocol != 0 + + # ===-------------------------------------------------------------------===# + # Python Slice Creation + # ===-------------------------------------------------------------------===# + + # PyObject *PySlice_New(PyObject *start, PyObject *stop, PyObject *step) + # ref: https://docs.python.org/3/c-api/slice.html#c.PySlice_New + fn PySlice_New( + inout self, start: PyObjectPtr, stop: PyObjectPtr, step: PyObjectPtr + ) -> PyObjectPtr: + var r = self.lib.get_function[ + fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr + ]("PySlice_New")(start, stop, step) + + self.log( + r._get_ptr_as_int(), + " NEWREF PySlice_New, refcnt:", + self._Py_REFCNT(r), + ", start:", + start._get_ptr_as_int(), + ", stop:", + stop._get_ptr_as_int(), + ", step:", + step._get_ptr_as_int(), + ) + + self._inc_total_rc() + return r diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index a6f7d9a221..d8db12081f 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -30,6 +30,7 @@ from hashlib._hasher import _HashableWithHasher, _Hasher from ._cpython import CPython, PyObjectPtr from .python import Python, _get_global_python_itf +from sys.ffi import c_ssize_t struct _PyIter(Sized): @@ -503,6 +504,14 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyTuple_SetItem(self.py_object, i, obj.py_object) + fn __init__(inout self, slice: Slice): + """Initialize the object from a Mojo Slice. + + Args: + slice: The dictionary value. + """ + self.py_object = _slice_to_py_object_ptr(slice) + fn __init__(inout self, value: Dict[Self, Self]): """Initialize the object from a dictionary of PythonObjects. @@ -734,6 +743,35 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return PythonObject(result) + fn __getitem__(self, *args: Slice) raises -> PythonObject: + """Return the sliced value for the given Slice or Slices. + + Args: + args: The Slice or Slices to apply to this object. + + Returns: + The sliced value corresponding to the given Slice(s) for this object. + """ + var cpython = _get_global_python_itf().cpython() + var size = len(args) + var key_obj: PyObjectPtr + + if size == 1: + key_obj = _slice_to_py_object_ptr(args[0]) + else: + key_obj = cpython.PyTuple_New(size) + for i in range(size): + var slice_obj = _slice_to_py_object_ptr(args[i]) + var result = cpython.PyTuple_SetItem(key_obj, i, slice_obj) + if result != 0: + raise Error("internal error: PyTuple_SetItem failed") + + cpython.Py_IncRef(key_obj) + var result = cpython.PyObject_GetItem(self.py_object, key_obj) + cpython.Py_DecRef(key_obj) + Python.throw_python_exception_if_error_state(cpython) + return PythonObject(result) + fn __setitem__(inout self, *args: PythonObject, value: PythonObject) raises: """Set the value with the given key or keys. @@ -1487,3 +1525,47 @@ struct PythonObject( # TODO: Avoid this intermediate String allocation, if possible. writer.write(str(self)) + + +# ===-----------------------------------------------------------------------===# +# Helper functions +# ===-----------------------------------------------------------------------===# + + +fn _slice_to_py_object_ptr(slice: Slice) -> PyObjectPtr: + """Convert Mojo Slice to Python slice parameters. + + Deliberately avoids using `span.indices()` here and instead passes + the Slice parameters directly to Python. Python's C implementation + already handles such conditions, allowing Python to apply its own slice + handling and error handling. + + + Args: + slice: A Mojo slice object to be converted. + + Returns: + PyObjectPtr: The pointer to the Python slice. + + """ + cpython = _get_global_python_itf().cpython() + var py_start = cpython.Py_None() + var py_stop = cpython.Py_None() + var py_step = cpython.Py_None() + + if slice.start: + py_start = cpython.PyLong_FromSsize_t(c_ssize_t(slice.start.value())) + if slice.end: + py_stop = cpython.PyLong_FromSsize_t(c_ssize_t(slice.end.value())) + if slice.step: + py_step = cpython.PyLong_FromSsize_t(c_ssize_t(slice.step.value())) + + var py_slice = cpython.PySlice_New(py_start, py_stop, py_step) + + if py_start != cpython.Py_None(): + cpython.Py_DecRef(py_start) + if py_stop != cpython.Py_None(): + cpython.Py_DecRef(py_stop) + cpython.Py_DecRef(py_step) + + return py_slice diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index c3738e9412..264eb958ee 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -517,6 +517,95 @@ def test_setitem_raises(): d[[1, 2, 3]] = 5 +fn test_py_slice() raises: + var a = PythonObject([1, 2, 3, 4, 5]) + assert_equal("[2, 3]", str(a[1:3])) + assert_equal("[1, 2, 3, 4, 5]", str(a[:])) + assert_equal("[1, 2, 3]", str(a[:3])) + assert_equal("[3, 4, 5]", str(a[2:])) + assert_equal("[1, 3, 5]", str(a[::2])) + assert_equal("[2, 4]", str(a[1::2])) + assert_equal("[4, 5]", str(a[-2:])) + assert_equal("[1, 2, 3]", str(a[:-2])) + assert_equal("[5, 4, 3, 2, 1]", str(a[::-1])) + assert_equal("[1, 2, 3, 4, 5]", str(a[-10:10])) # out of bounds + assert_equal("[1, 2, 3, 4, 5]", str(a[::])) + assert_equal("[1, 2, 3, 4, 5]", str(a[:100])) + assert_equal("[]", str(a[5:])) + assert_equal("[5, 4, 3, 2]", str(a[:-5:-1])) + + var b = Python.evaluate("[i for i in range(1000)]") + assert_equal("[0, 250, 500, 750]", str(b[::250])) + with assert_raises(contains="slice step cannot be zero"): + _ = b[::0] + # Negative cases such as `b[1.3:10]` or `b["1":10]` are handled by parser + # which would normally throw a TypeError in Python + + var s = PythonObject("Hello, World!") + assert_equal("Hello", str(s[:5])) + assert_equal("World!", str(s[7:])) + assert_equal("!dlroW ,olleH", str(s[::-1])) + assert_equal("Hello, World!", str(s[:])) + assert_equal("Hlo ol!", str(s[::2])) + assert_equal("Hlo ol!", str(s[None:None:2])) + + var t = PythonObject((1, 2, 3, 4, 5)) + assert_equal("(2, 3, 4)", str(t[1:4])) + assert_equal("(4, 3, 2)", str(t[3:0:-1])) + + var empty = PythonObject([]) + assert_equal("[]", str(empty[:])) + assert_equal("[]", str(empty[1:2:3])) + + # TODO: enable this test. Currently it fails with error: unhashable type: 'slice' + # var d = Python.dict() + # d["a"] = 1 + # d["b"] = 2 + # with assert_raises(contains="slice(1, 3, None)"): + # _ = d[1:3] + + var custom = Python.evaluate( + "type('CustomSliceable', (), {'__getitem__': lambda self, key: key})()" + ) + assert_equal("slice(1, 3, None)", str(custom[1:3])) + + var i = PythonObject(1) + with assert_raises(contains="'int' object is not subscriptable"): + _ = i[0:1] + + with_2d = Python.evaluate( + """type('With2D', (), { + '__init__': lambda self: setattr(self, 'data', [[1, 2, 3], [4, 5, 6], [7, 8, 9]]), + '__getitem__': lambda self, key: ( + [row[key[1]] for row in self.data[key[0]]] if isinstance(key, tuple) and all(isinstance(k, slice) for k in key) + else (self.data[key[0]][key[1]] if isinstance(key, tuple) + else self.data[key]) + ) + })()""" + ) + assert_equal("[1, 2]", str(with_2d[0, PythonObject(Slice(0, 2))])) + assert_equal("[1, 2]", str(with_2d[0][0:2])) + + assert_equal("[4, 5, 6]", str(with_2d[PythonObject(Slice(0, 2)), 1])) + assert_equal("[4, 5, 6]", str(with_2d[0:2][1])) + + assert_equal( + "[[1, 2, 3], [4, 5, 6]]", str(with_2d[PythonObject(Slice(0, 2))]) + ) + assert_equal("[[1, 2, 3], [4, 5, 6]]", str(with_2d[0:2])) + assert_equal("[[1, 3], [4, 6]]", str(with_2d[0:2, ::2])) + + assert_equal( + "[6, 5, 4]", str(with_2d[1, PythonObject(Slice(None, None, -1))]) + ) + assert_equal("[6, 5, 4]", str(with_2d[1][::-1])) + + assert_equal("[7, 9]", str(with_2d[2][::2])) + + with assert_raises(contains="list index out of range"): + _ = with_2d[0:1][4] + + def main(): # initializing Python instance calls init_python var python = Python() @@ -533,3 +622,4 @@ def main(): test_nested_object() test_getitem_raises() test_setitem_raises() + test_py_slice() From 79aaa391a035de94cccdd5758466a76dc37e27bf Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Wed, 16 Oct 2024 14:16:29 -0400 Subject: [PATCH 1767/2019] [******] Implement Matmul for KVCache with packed tensors Implements the ****** wrapper for fused_qkv_matmul with packed tensors. Next step is to expose our numerous custom ****** with all of the supported permutations of head and dim. MODULAR_ORIG_COMMIT_REV_ID: aae8c550b432e45eb0fb1901c6bb40c8facc2251 --- stdlib/src/utils/index.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index af08df0305..e01f68b1a2 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -1015,7 +1015,7 @@ fn Index[ @always_inline -fn product[size: Int](tuple: IndexList[size], end_idx: Int) -> Int: +fn product[size: Int](tuple: IndexList[size, **_], end_idx: Int) -> Int: """Computes a product of values in the tuple up to the given index. Parameters: @@ -1034,7 +1034,7 @@ fn product[size: Int](tuple: IndexList[size], end_idx: Int) -> Int: @always_inline fn product[ size: Int -](tuple: IndexList[size], start_idx: Int, end_idx: Int) -> Int: +](tuple: IndexList[size, **_], start_idx: Int, end_idx: Int) -> Int: """Computes a product of values in the tuple in the given index range. Parameters: From 257bf31821bde2b68d1aa4eb595a17dce2c04e33 Mon Sep 17 00:00:00 2001 From: William G Hatch Date: Wed, 16 Oct 2024 15:24:02 -0600 Subject: [PATCH 1768/2019] [stdlib] fix formatter lifetime bug in string.join The lifetime of the formatter object was not extended to the lifetime of the `@parameter` closure, so in certain configurations it was crashing. Closes https://github.com/modularml/mojo/issues/2751 MODULAR_ORIG_COMMIT_REV_ID: 045a508b51458e01a55f0a36d6a2cc54ca9a0e92 --- stdlib/src/collections/string.mojo | 1 + stdlib/test/collections/test_string.mojo | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index d365365bfc..f69b1f1e5b 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1391,6 +1391,7 @@ struct String( elems.each[add_elt]() _ = is_first + _ = formatter^ return result fn join[T: StringableCollectionElement](self, elems: List[T, *_]) -> String: diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index bbf3768c7f..ed92b61a49 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -10,10 +10,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %bare-mojo %s +# RUN: %mojo %s -# TODO: Replace %bare-mojo with %mojo -# when https://github.com/modularml/mojo/issues/2751 is fixed. from collections.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, From 19097e0edd7230def8646fe1c627c2e2e67b3c87 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 16 Oct 2024 16:05:43 -0600 Subject: [PATCH 1769/2019] [stdlib] Fix missing `benchmark.keep` calls in `bench_math.mojo` There are a few missing `benchmark.keep` calls in the benchmark functions. Add them so the compiler won't optimize away this function. Other files will be audited in a follow-up. MODULAR_ORIG_COMMIT_REV_ID: ec352ca3f588a0f15d709465d5222c4999192cc7 --- stdlib/benchmarks/math/bench_math.mojo | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index 877c2798a2..9a62d9aba2 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -52,7 +52,8 @@ fn bench_math[ @parameter fn call_fn() raises: for input in inputs: - _ = math_f1p(input[]) + var result = math_f1p(input[]) + keep(result) b.iter[call_fn]() @@ -70,7 +71,8 @@ fn bench_math3[ @parameter fn call_fn() raises: for input in inputs: - _ = math_f3p(input[], input[], input[]) + var result = math_f3p(input[], input[], input[]) + keep(result) b.iter[call_fn]() From 376abe157b50c8e8dfd3de6a998d300e02df0f2a Mon Sep 17 00:00:00 2001 From: Helehex Date: Wed, 16 Oct 2024 16:15:05 -0600 Subject: [PATCH 1770/2019] [External] [stdlib] Fix `/_math.mojo` examples. (#49264) [External] [stdlib] Fix `/_math.mojo` examples. FYI: `Truncable` is not in `math`, so that one still fails, not sure what's going on there. Co-authored-by: Helehex Closes modularml/mojo#3681 MODULAR_ORIG_COMMIT_REV_ID: d1fb6af747e4599fe0eefa709dad1f9f4840e5b1 --- stdlib/src/builtin/_math.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 67b818e9f4..22a309e7ed 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -41,7 +41,7 @@ trait Ceilable: var im: Float64 fn __ceil__(self) -> Self: - return Self(ceil(re), ceil(im)) + return Self(ceil(self.re), ceil(self.im)) ``` """ @@ -78,7 +78,7 @@ trait Floorable: var im: Float64 fn __floor__(self) -> Self: - return Self(floor(re), floor(im)) + return Self(floor(self.re), floor(self.im)) ``` """ From 98768053e07c58ffaac08aa4790247e788a0f76b Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 16 Oct 2024 16:55:54 -0600 Subject: [PATCH 1771/2019] [stdlib] Remove `Reference` type alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of the internal code has moved over to using `Pointer` — the new name, instead of `Reference`. Drop the compatibility type alias now. MODULAR_ORIG_COMMIT_REV_ID: cda036aa01a5ec512dbeae1debfaba3a6f181298 --- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/pointer.mojo | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 9d25f91476..cc226348fd 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -15,6 +15,6 @@ from .arc import Arc from .box import Box from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation -from .pointer import AddressSpace, Pointer, Reference +from .pointer import AddressSpace, Pointer from .unsafe import bitcast from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index ddbfa54ffb..167feb5f5a 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -21,9 +21,6 @@ from memory import Pointer from builtin._documentation import doc_private -# TODO: This is kept for compatibility, remove this in the future. -alias Reference = Pointer[*_] - # ===----------------------------------------------------------------------===# # AddressSpace # ===----------------------------------------------------------------------===# From 7d8e819272c7e6c9aa1f3285ce802b19956889a1 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:44:12 -0600 Subject: [PATCH 1772/2019] [External] [stdlib] Fix `String.format()` to use byte indexing, refactor for performance and readability, and prepare for full format spec support (#49225) [External] [stdlib] Fix `String.format()` to use byte indexing, refactor for performance and readability, and prepare for full format spec support Fix `String.format()` to use byte indexing, refactor for performance and readability, and prepare for full format spec support. Closes https://github.com/modularml/mojo/issues/3296. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3539 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3539 MODULAR_ORIG_COMMIT_REV_ID: 2d7ebae6f48b7d29866c089cffd084cb4405ac45 --- stdlib/src/builtin/string_literal.mojo | 48 +- stdlib/src/collections/string.mojo | 340 +---------- stdlib/src/utils/string_slice.mojo | 687 ++++++++++++++++++++++- stdlib/test/collections/test_string.mojo | 54 +- stdlib/test/hashlib/test_ahash.mojo | 8 +- 5 files changed, 768 insertions(+), 369 deletions(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 975963b749..21bdb178b1 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -24,7 +24,11 @@ from utils import StringRef, Span, StringSlice, StaticString from utils import Formattable, Formatter from utils._visualizers import lldb_formatter_wrapping_type -from collections.string import _atol, _StringSliceIter +from utils.string_slice import ( + _StringSliceIter, + _FormatCurlyEntry, + _CurlyEntryFormattable, +) # ===----------------------------------------------------------------------===# # StringLiteral @@ -206,27 +210,25 @@ struct StringLiteral( """ return len(self) != 0 + @always_inline fn __int__(self) raises -> Int: """Parses the given string as a base-10 integer and returns that value. - - For example, `int("19")` returns `19`. If the given string cannot be parsed - as an integer value, an error is raised. For example, `int("hi")` raises an - error. + If the string cannot be parsed as an int, an error is raised. Returns: An integer value that represents the string, or otherwise raises. """ - return _atol(self) + return int(self.as_string_slice()) + @always_inline fn __float__(self) raises -> Float64: - """Parses the string as a float point number and returns that value. - - If the string cannot be parsed as a float, an error is raised. + """Parses the string as a float point number and returns that value. If + the string cannot be parsed as a float, an error is raised. Returns: A float value that represents the string, or otherwise raises. """ - return atof(self) + return float(self.as_string_slice()) @no_inline fn __str__(self) -> String: @@ -414,6 +416,32 @@ struct StringLiteral( len=self.byte_length(), ) + @always_inline + fn format[*Ts: _CurlyEntryFormattable](self, *args: *Ts) raises -> String: + """Format a template with `*args`. + + Args: + args: The substitution values. + + Parameters: + Ts: The types of substitution values that implement `Representable` + and `Stringable` (to be changed and made more flexible). + + Returns: + The template with the given values substituted. + + Examples: + + ```mojo + # Manual indexing: + print("{0} {1} {0}".format("Mojo", 1.125)) # Mojo 1.125 Mojo + # Automatic indexing: + print("{} {}".format(True, "hello world")) # True hello world + ``` + . + """ + return _FormatCurlyEntry.format(self, args) + fn format_to(self, inout writer: Formatter): """ Formats this string literal to the provided formatter. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index f69b1f1e5b..33e68615af 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -42,6 +42,8 @@ from utils.string_slice import ( _StringSliceIter, _unicode_codepoint_utf8_byte_length, _shift_unicode_to_utf8, + _FormatCurlyEntry, + _CurlyEntryFormattable, ) # ===----------------------------------------------------------------------=== # @@ -748,7 +750,7 @@ struct String( impl: The buffer. """ debug_assert( - impl[-1] == 0, + len(impl) > 0 and impl[-1] == 0, "expected last element of String buffer to be null terminator", ) # We make a backup because steal_data() will clear size and capacity. @@ -2095,22 +2097,20 @@ struct String( return self[: -suffix.byte_length()] return self + @always_inline fn __int__(self) raises -> Int: """Parses the given string as a base-10 integer and returns that value. - - For example, `int("19")` returns `19`. If the given string cannot be - parsed as an integer value, an error is raised. For example, `int("hi")` - raises an error. + If the string cannot be parsed as an int, an error is raised. Returns: An integer value that represents the string, or otherwise raises. """ return atol(self) + @always_inline fn __float__(self) raises -> Float64: - """Parses the string as a float point number and returns that value. - - If the string cannot be parsed as a float, an error is raised. + """Parses the string as a float point number and returns that value. If + the string cannot be parsed as a float, an error is raised. Returns: A float value that represents the string, or otherwise raises. @@ -2140,84 +2140,31 @@ struct String( ) return String(buf^) - fn format[*Ts: StringRepresentable](self, *args: *Ts) raises -> String: - """Format a template with *args. - - Example of manual indexing: - - ```mojo - print( - String("{0} {1} {0}").format( - "Mojo", 1.125 - ) - ) #Mojo 1.125 Mojo - ``` - - Example of automatic indexing: - - ```mojo - var x = String("{} {}").format( - True, "hello world" - ) - print(x) #True hello world - ``` + @always_inline + fn format[*Ts: _CurlyEntryFormattable](self, *args: *Ts) raises -> String: + """Format a template with `*args`. Args: args: The substitution values. Parameters: - Ts: The types of the substitution values. - Are required to implement `Stringable`. + Ts: The types of substitution values that implement `Representable` + and `Stringable` (to be changed and made more flexible). Returns: The template with the given values substituted. - """ - alias num_pos_args = len(VariadicList(Ts)) - var entries = _FormatCurlyEntry.create_entries(self, num_pos_args) - - var res: String = "" - var pos_in_self = 0 - - var current_automatic_arg_index = 0 - for e in entries: - debug_assert( - pos_in_self < self.byte_length(), - "pos_in_self >= self.byte_length()", - ) - res += self[pos_in_self : e[].first_curly] - - if e[].is_escaped_brace(): - res += "}" if e[].field[Bool] else "{" - - if e[].is_manual_indexing(): - - @parameter - for i in range(num_pos_args): - if i == e[].field[Int]: - if e[].conversion_flag == "r": - res += repr(args[i]) - else: - res += str(args[i]) - - if e[].is_automatic_indexing(): - - @parameter - for i in range(num_pos_args): - if i == current_automatic_arg_index: - if e[].conversion_flag == "r": - res += repr(args[i]) - else: - res += str(args[i]) - - current_automatic_arg_index += 1 - - pos_in_self = e[].last_curly + 1 - - if pos_in_self < self.byte_length(): - res += self[pos_in_self : self.byte_length()] + Examples: - return res^ + ```mojo + # Manual indexing: + print(String("{0} {1} {0}").format("Mojo", 1.125)) # Mojo 1.125 Mojo + # Automatic indexing: + print(String("{} {}").format(True, "hello world")) # True hello world + ``` + . + """ + return _FormatCurlyEntry.format(self, args) fn isdigit(self) -> Bool: """A string is a digit string if all characters in the string are digits @@ -2461,244 +2408,3 @@ fn _calc_format_buffer_size[type: DType]() -> Int: return 64 + 1 else: return 128 + 1 # Add 1 for the terminator - - -# ===----------------------------------------------------------------------===# -# Format method structures -# ===----------------------------------------------------------------------===# - - -trait StringRepresentable(Stringable, Representable): - """The `StringRepresentable` trait denotes a trait composition of the - `Stringable` and `Representable` traits. - - This trait is used by the `format()` method to support both `{!s}` (or `{}`) - and `{!r}` format specifiers. It allows the method to handle types that - can be formatted using both their string representation and their - more detailed representation. - - Types implementing this trait must provide both `__str__()` and `__repr__()` - methods as defined in `Stringable` and `Representable` traits respectively. - """ - - pass - - -@value -struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): - """ - Internally used by the `format()` method. - - Specifically to structure fields. - - Does not contain any substitution values. - - """ - - var first_curly: Int - """The index of an opening brace around a substitution field.""" - - var last_curly: Int - """The index of an closing brace around a substitution field.""" - - var conversion_flag: String - """Store the format specifier (e.g., 'r' for repr).""" - - alias _FieldVariantType = Variant[ - String, # kwargs indexing (`{field_name}`) - Int, # args manual indexing (`{3}`) - NoneType, # args automatic indexing (`{}`) - Bool, # for escaped curlies ('{{') - ] - var field: Self._FieldVariantType - """Store the substitution field.""" - - fn __init__(inout self, *, other: Self): - self.first_curly = other.first_curly - self.last_curly = other.last_curly - self.conversion_flag = other.conversion_flag - self.field = Self._FieldVariantType(other=other.field) - - fn is_escaped_brace(ref [_]self) -> Bool: - return self.field.isa[Bool]() - - fn is_kwargs_field(ref [_]self) -> Bool: - return self.field.isa[String]() - - fn is_automatic_indexing(ref [_]self) -> Bool: - return self.field.isa[NoneType]() - - fn is_manual_indexing(ref [_]self) -> Bool: - return self.field.isa[Int]() - - @staticmethod - fn create_entries( - format_src: String, len_pos_args: Int - ) raises -> List[Self]: - """Used internally by the `format()` method. - - Args: - format_src: The "format" part provided by the user. - len_pos_args: The len of *args - - Returns: - A `List` of structured field entries. - - Purpose of the `Variant` `Self.field`: - - - `Int` for manual indexing - (value field contains `0`) - - - `NoneType` for automatic indexing - (value field contains `None`) - - - `String` for **kwargs indexing - (value field contains `foo`) - - - `Bool` for escaped curlies - (value field contains False for `{` or True for '}') - """ - var manual_indexing_count = 0 - var automatic_indexing_count = 0 - var raised_manual_index = Optional[Int](None) - var raised_automatic_index = Optional[Int](None) - var raised_kwarg_field = Optional[String](None) - alias supported_conversion_flags = ( - String("s"), # __str__ - String("r"), # __repr__ - ) - - var entries = List[Self]() - var start = Optional[Int](None) - var skip_next = False - for i in range(format_src.byte_length()): - if skip_next: - skip_next = False - continue - if format_src[i] == "{": - if start: - # already one there. - if i - start.value() == 1: - # python escapes double curlies - var current_entry = Self( - first_curly=start.value(), - last_curly=i, - field=False, - conversion_flag="", - ) - entries.append(current_entry^) - start = None - continue - raise ( - "there is a single curly { left unclosed or unescaped" - ) - else: - start = i - continue - if format_src[i] == "}": - if start: - var start_value = start.value() - var current_entry = Self( - first_curly=start_value, - last_curly=i, - field=NoneType(), - conversion_flag="", - ) - - if i - start_value != 1: - var field = format_src[start_value + 1 : i] - var exclamation_index = field.find("!") - - # TODO: Future implementation of format specifiers - # When implementing format specifiers, modify this section to handle: - # replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}" - # this will involve: - # 1. finding a colon ':' after the conversion flag (if present) - # 2. extracting the format_spec if a colon is found - # 3. adjusting the field and conversion_flag parsing accordingly - - if exclamation_index != -1: - if exclamation_index + 1 < len(field): - var conversion_flag: String = field[ - exclamation_index + 1 : - ] - if ( - conversion_flag - not in supported_conversion_flags - ): - raise 'Conversion flag "' + conversion_flag + '" not recognised.' - current_entry.conversion_flag = conversion_flag - else: - raise "Empty conversion flag." - - field = field[:exclamation_index] - - if ( - field == "" - ): # an empty field, so it's automatic indexing - if automatic_indexing_count >= len_pos_args: - raised_automatic_index = ( - automatic_indexing_count - ) - break - automatic_indexing_count += 1 - else: - try: - # field is a number for manual indexing: - var number = int(field) - current_entry.field = number - if number >= len_pos_args or number < 0: - raised_manual_index = number - break - manual_indexing_count += 1 - except e: - debug_assert( - "not convertible to integer" in str(e), - "Not the expected error from atol", - ) - # field is an keyword for **kwargs: - current_entry.field = field - raised_kwarg_field = field - break - - else: - # automatic indexing - # current_entry.field is already None - if automatic_indexing_count >= len_pos_args: - raised_automatic_index = automatic_indexing_count - break - automatic_indexing_count += 1 - entries.append(current_entry^) - start = None - else: - # python escapes double curlies - if (i + 1) < format_src.byte_length(): - if format_src[i + 1] == "}": - var curren_entry = Self( - first_curly=i, - last_curly=i + 1, - field=True, - conversion_flag="", - ) - entries.append(curren_entry^) - skip_next = True - continue - # if it is not an escaped one, it is an error - raise ( - "there is a single curly } left unclosed or unescaped" - ) - - if raised_automatic_index: - raise "Automatic indexing require more args in *args" - if raised_kwarg_field: - raise "Index " + raised_kwarg_field.value() + " not in kwargs" - if manual_indexing_count and automatic_indexing_count: - raise "Cannot both use manual and automatic indexing" - if raised_manual_index: - raise ( - "Index " + str(raised_manual_index.value()) + " not in *args" - ) - if start: - raise "there is a single curly { left unclosed or unescaped" - - return entries^ diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index a860c5982e..9855f99de7 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -22,8 +22,8 @@ from utils import StringSlice from bit import count_leading_zeros from utils import Span -from collections.string import _isspace -from collections import List +from collections.string import _isspace, _atol, _atof +from collections import List, Optional from memory import memcmp, UnsafePointer from sys import simdwidthof, bitwidthof @@ -492,6 +492,26 @@ struct StringSlice[ unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) + @always_inline + fn __int__(self) raises -> Int: + """Parses the given string as a base-10 integer and returns that value. + If the string cannot be parsed as an int, an error is raised. + + Returns: + An integer value that represents the string, or otherwise raises. + """ + return _atol(self._strref_dangerous()) + + @always_inline + fn __float__(self) raises -> Float64: + """Parses the string as a float point number and returns that value. If + the string cannot be parsed as a float, an error is raised. + + Returns: + A float value that represents the string, or otherwise raises. + """ + return _atof(self._strref_dangerous()) + # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# @@ -585,6 +605,32 @@ struct StringSlice[ # and use something smarter. return StringSlice(unsafe_from_utf8=self._slice[abs_start:]) + @always_inline + fn format[*Ts: _CurlyEntryFormattable](self, *args: *Ts) raises -> String: + """Format a template with `*args`. + + Args: + args: The substitution values. + + Parameters: + Ts: The types of substitution values that implement `Representable` + and `Stringable` (to be changed and made more flexible). + + Returns: + The template with the given values substituted. + + Examples: + + ```mojo + # Manual indexing: + print("{0} {1} {0}".format("Mojo", 1.125)) # Mojo 1.125 Mojo + # Automatic indexing: + print("{} {}".format(True, "hello world")) # True hello world + ``` + . + """ + return _FormatCurlyEntry.format(self, args) + fn find(self, substr: StringSlice, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at `start`. If not found, returns -1. @@ -712,3 +758,640 @@ struct StringSlice[ current_offset += eol_location + eol_length return output^ + + +# ===----------------------------------------------------------------------===# +# Utils +# ===----------------------------------------------------------------------===# + + +trait Stringlike: + """Trait intended to be used only with `String`, `StringLiteral` and + `StringSlice`.""" + + fn byte_length(self) -> Int: + """Get the string length in bytes. + + Returns: + The length of this string in bytes. + + Notes: + This does not include the trailing null terminator in the count. + """ + ... + + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: + """Get raw pointer to the underlying data. + + Returns: + The raw pointer to the data. + """ + ... + + +# ===----------------------------------------------------------------------===# +# Format method structures +# ===----------------------------------------------------------------------===# + + +trait _CurlyEntryFormattable(Stringable, Representable): + """This trait is used by the `format()` method to support format specifiers. + Currently, it is a composition of both `Stringable` and `Representable` + traits i.e. a type to be formatted must implement both. In the future this + will be less constrained. + """ + + pass + + +@value +struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): + """The struct that handles string-like formatting by curly braces entries. + This is internal for the types: `String`, `StringLiteral` and `StringSlice`. + """ + + var first_curly: Int + """The index of an opening brace around a substitution field.""" + var last_curly: Int + """The index of a closing brace around a substitution field.""" + # TODO: ord("a") conversion flag not supported yet + var conversion_flag: UInt8 + """The type of conversion for the entry: {ord("s"), ord("r")}.""" + var format_spec: Optional[_FormatSpec] + """The format specifier.""" + # TODO: ord("a") conversion flag not supported yet + alias supported_conversion_flags = SIMD[DType.uint8, 2](ord("s"), ord("r")) + """Currently supported conversion flags: `__str__` and `__repr__`.""" + alias _FieldVariantType = Variant[String, Int, NoneType, Bool] + """Purpose of the `Variant` `Self.field`: + + - `Int` for manual indexing: (value field contains `0`). + - `NoneType` for automatic indexing: (value field contains `None`). + - `String` for **kwargs indexing: (value field contains `foo`). + - `Bool` for escaped curlies: (value field contains False for `{` or True + for `}`). + """ + var field: Self._FieldVariantType + """Store the substitution field. See `Self._FieldVariantType` docstrings for + more details.""" + alias _args_t = VariadicPack[element_trait=_CurlyEntryFormattable, *_] + """Args types that are formattable by curly entry.""" + + fn __init__(inout self, *, other: Self): + self.first_curly = other.first_curly + self.last_curly = other.last_curly + self.conversion_flag = other.conversion_flag + self.field = Self._FieldVariantType(other=other.field) + self.format_spec = other.format_spec + + fn __init__( + inout self, + first_curly: Int, + last_curly: Int, + field: Self._FieldVariantType, + conversion_flag: UInt8 = 0, + format_spec: Optional[_FormatSpec] = None, + ): + self.first_curly = first_curly + self.last_curly = last_curly + self.field = field + self.conversion_flag = conversion_flag + self.format_spec = format_spec + + @always_inline + fn is_escaped_brace(ref [_]self) -> Bool: + return self.field.isa[Bool]() + + @always_inline + fn is_kwargs_field(ref [_]self) -> Bool: + return self.field.isa[String]() + + @always_inline + fn is_automatic_indexing(ref [_]self) -> Bool: + return self.field.isa[NoneType]() + + @always_inline + fn is_manual_indexing(ref [_]self) -> Bool: + return self.field.isa[Int]() + + @staticmethod + fn format[T: Stringlike](fmt_src: T, args: Self._args_t) raises -> String: + alias len_pos_args = __type_of(args).__len__() + entries, size_estimation = Self._create_entries(fmt_src, len_pos_args) + var fmt_len = fmt_src.byte_length() + var buf = String._buffer_type(capacity=fmt_len + size_estimation) + buf.size = 1 + buf.unsafe_set(0, 0) + var res = String(buf^) + var offset = 0 + var ptr = fmt_src.unsafe_ptr() + alias S = StringSlice[StaticConstantOrigin] + + @always_inline("nodebug") + fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: + return S(unsafe_from_utf8_ptr=p + start, len=end - start) + + var auto_arg_index = 0 + for e in entries: + debug_assert(offset < fmt_len, "offset >= fmt_src.byte_length()") + res += _build_slice(ptr, offset, e[].first_curly) + e[]._format_entry[len_pos_args](res, args, auto_arg_index) + offset = e[].last_curly + 1 + + res += _build_slice(ptr, offset, fmt_len) + return res^ + + @staticmethod + fn _create_entries[ + T: Stringlike + ](fmt_src: T, len_pos_args: Int) raises -> (List[Self], Int): + """Returns a list of entries and its total estimated entry byte width. + """ + var manual_indexing_count = 0 + var automatic_indexing_count = 0 + var raised_manual_index = Optional[Int](None) + var raised_automatic_index = Optional[Int](None) + var raised_kwarg_field = Optional[String](None) + alias `}` = UInt8(ord("}")) + alias `{` = UInt8(ord("{")) + alias l_err = "there is a single curly { left unclosed or unescaped" + alias r_err = "there is a single curly } left unclosed or unescaped" + + var entries = List[Self]() + var start = Optional[Int](None) + var skip_next = False + var fmt_ptr = fmt_src.unsafe_ptr() + var fmt_len = fmt_src.byte_length() + var total_estimated_entry_byte_width = 0 + + for i in range(fmt_len): + if skip_next: + skip_next = False + continue + if fmt_ptr[i] == `{`: + if not start: + start = i + continue + if i - start.value() != 1: + raise Error(l_err) + # python escapes double curlies + entries.append(Self(start.value(), i, field=False)) + start = None + continue + elif fmt_ptr[i] == `}`: + if not start and (i + 1) < fmt_len: + # python escapes double curlies + if fmt_ptr[i + 1] == `}`: + entries.append(Self(i, i + 1, field=True)) + total_estimated_entry_byte_width += 2 + skip_next = True + continue + elif not start: # if it is not an escaped one, it is an error + raise Error(r_err) + + var start_value = start.value() + var current_entry = Self(start_value, i, field=NoneType()) + + if i - start_value != 1: + if current_entry._handle_field_and_break( + fmt_src, + len_pos_args, + i, + start_value, + automatic_indexing_count, + raised_automatic_index, + manual_indexing_count, + raised_manual_index, + raised_kwarg_field, + total_estimated_entry_byte_width, + ): + break + else: # automatic indexing + if automatic_indexing_count >= len_pos_args: + raised_automatic_index = automatic_indexing_count + break + automatic_indexing_count += 1 + total_estimated_entry_byte_width += 8 # guessing + entries.append(current_entry^) + start = None + + if raised_automatic_index: + raise Error("Automatic indexing require more args in *args") + elif raised_kwarg_field: + var val = raised_kwarg_field.value() + raise Error("Index " + val + " not in kwargs") + elif manual_indexing_count and automatic_indexing_count: + raise Error("Cannot both use manual and automatic indexing") + elif raised_manual_index: + var val = str(raised_manual_index.value()) + raise Error("Index " + val + " not in *args") + elif start: + raise Error(l_err) + return entries^, total_estimated_entry_byte_width + + fn _handle_field_and_break[ + T: Stringlike + ]( + inout self, + fmt_src: T, + len_pos_args: Int, + i: Int, + start_value: Int, + inout automatic_indexing_count: Int, + inout raised_automatic_index: Optional[Int], + inout manual_indexing_count: Int, + inout raised_manual_index: Optional[Int], + inout raised_kwarg_field: Optional[String], + inout total_estimated_entry_byte_width: Int, + ) raises -> Bool: + alias S = StringSlice[StaticConstantOrigin] + + @always_inline("nodebug") + fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: + return S(unsafe_from_utf8_ptr=p + start, len=end - start) + + var field = _build_slice(fmt_src.unsafe_ptr(), start_value + 1, i) + var field_ptr = field.unsafe_ptr() + var field_len = i - (start_value + 1) + var exclamation_index = -1 + var idx = 0 + while idx < field_len: + if field_ptr[idx] == ord("!"): + exclamation_index = idx + break + idx += 1 + var new_idx = exclamation_index + 1 + if exclamation_index != -1: + if new_idx == field_len: + raise Error("Empty conversion flag.") + var conversion_flag = field_ptr[new_idx] + if field_len - new_idx > 1 or ( + conversion_flag not in Self.supported_conversion_flags + ): + var f = String(_build_slice(field_ptr, new_idx, field_len)) + _ = field^ + raise Error('Conversion flag "' + f + '" not recognised.') + self.conversion_flag = conversion_flag + field = _build_slice(field_ptr, 0, exclamation_index) + else: + new_idx += 1 + + var extra = int(new_idx < field_len) + var fmt_field = _build_slice(field_ptr, new_idx + extra, field_len) + self.format_spec = _FormatSpec.parse(fmt_field) + var w = int(self.format_spec.value().width) if self.format_spec else 0 + # fully guessing the byte width here to be at least 8 bytes per entry + # minus the length of the whole format specification + total_estimated_entry_byte_width += 8 * int(w > 0) + w - (field_len + 2) + + if field.byte_length() == 0: + # an empty field, so it's automatic indexing + if automatic_indexing_count >= len_pos_args: + raised_automatic_index = automatic_indexing_count + return True + automatic_indexing_count += 1 + else: + try: + # field is a number for manual indexing: + var number = int(field) + self.field = number + if number >= len_pos_args or number < 0: + raised_manual_index = number + return True + manual_indexing_count += 1 + except e: + alias unexp = "Not the expected error from atol" + debug_assert("not convertible to integer" in str(e), unexp) + # field is a keyword for **kwargs: + var f = str(field) + self.field = f + raised_kwarg_field = f + return True + return False + + fn _format_entry[ + len_pos_args: Int + ](self, inout res: String, args: Self._args_t, inout auto_idx: Int) raises: + # TODO(#3403 and/or #3252): this function should be able to use + # Formatter syntax when the type implements it, since it will give great + # performance benefits. This also needs to be able to check if the given + # args[i] conforms to the trait needed by the conversion_flag to avoid + # needing to constraint that every type needs to conform to every trait. + alias `r` = UInt8(ord("r")) + alias `s` = UInt8(ord("s")) + # alias `a` = UInt8(ord("a")) # TODO + + @parameter + fn _format(idx: Int) raises: + @parameter + for i in range(len_pos_args): + if i == idx: + var type_impls_repr = True # TODO + var type_impls_str = True # TODO + var type_impls_formatter_repr = True # TODO + var type_impls_formatter_str = True # TODO + var flag = self.conversion_flag + var empty = flag == 0 and not self.format_spec + + var data: String + if empty and type_impls_formatter_str: + data = str(args[i]) # TODO: use formatter and return + elif empty and type_impls_str: + data = str(args[i]) + elif flag == `s` and type_impls_formatter_str: + if empty: + # TODO: use formatter and return + pass + data = str(args[i]) + elif flag == `s` and type_impls_str: + data = str(args[i]) + elif flag == `r` and type_impls_formatter_repr: + if empty: + # TODO: use formatter and return + pass + data = repr(args[i]) + elif flag == `r` and type_impls_repr: + data = repr(args[i]) + elif self.format_spec: + self.format_spec.value().stringify(res, args[i]) + return + else: + alias argnum = "Argument number: " + alias does_not = " does not implement the trait " + alias needed = "needed for conversion_flag: " + var flg = String(List[UInt8](flag, 0)) + raise Error(argnum + str(i) + does_not + needed + flg) + + if self.format_spec: + self.format_spec.value().format_string(res, data) + else: + res += data + + if self.is_escaped_brace(): + res += "}" if self.field[Bool] else "{" + elif self.is_manual_indexing(): + _format(self.field[Int]) + elif self.is_automatic_indexing(): + _format(auto_idx) + auto_idx += 1 + + +@value +@register_passable("trivial") +struct _FormatSpec: + """Store every field of the format specifier in a byte (e.g., ord("+") for + sign). It is stored in a byte because every [format specifier](\ + https://docs.python.org/3/library/string.html#formatspec) is an ASCII + character. + """ + + var fill: UInt8 + """If a valid align value is specified, it can be preceded by a fill + character that can be any character and defaults to a space if omitted. + """ + var align: UInt8 + """The meaning of the various alignment options is as follows: + + | Option | Meaning| + |:-------|:-------| + |'<' | Forces the field to be left-aligned within the available space + (this is the default for most objects).| + |'>' | Forces the field to be right-aligned within the available space + (this is the default for numbers).| + |'=' | Forces the padding to be placed after the sign (if any) but before + the digits. This is used for printing fields in the form `+000000120`. This + alignment option is only valid for numeric types. It becomes the default for + numbers when `0` immediately precedes the field width.| + |'^' | Forces the field to be centered within the available space.| + """ + var sign: UInt8 + """The sign option is only valid for number types, and can be one of the + following: + + | Option | Meaning| + |:-------|:-------| + |'+' | indicates that a sign should be used for both positive as well as + negative numbers.| + |'-' | indicates that a sign should be used only for negative numbers (this + is the default behavior).| + |space | indicates that a leading space should be used on positive numbers, + and a minus sign on negative numbers.| + """ + var coerce_z: Bool + """The 'z' option coerces negative zero floating-point values to positive + zero after rounding to the format precision. This option is only valid for + floating-point presentation types. + """ + var alternate_form: Bool + """The alternate form is defined differently for different types. This + option is only valid for types that implement the trait `# TODO: define + trait`. For integers, when binary, octal, or hexadecimal output is used, + this option adds the respective prefix '0b', '0o', '0x', or '0X' to the + output value. For float and complex the alternate form causes the result of + the conversion to always contain a decimal-point character, even if no + digits follow it. + """ + var width: UInt8 + """A decimal integer defining the minimum total field width, including any + prefixes, separators, and other formatting characters. If not specified, + then the field width will be determined by the content. When no explicit + alignment is given, preceding the width field by a zero ('0') character + enables sign-aware zero-padding for numeric types. This is equivalent to a + fill character of '0' with an alignment type of '='. + """ + var grouping_option: UInt8 + """The ',' option signals the use of a comma for a thousands separator. For + a locale aware separator, use the 'n' integer presentation type instead. The + '_' option signals the use of an underscore for a thousands separator for + floating-point presentation types and for integer presentation type 'd'. For + integer presentation types 'b', 'o', 'x', and 'X', underscores will be + inserted every 4 digits. For other presentation types, specifying this + option is an error. + """ + var precision: UInt8 + """The precision is a decimal integer indicating how many digits should be + displayed after the decimal point for presentation types 'f' and 'F', or + before and after the decimal point for presentation types 'g' or 'G'. For + string presentation types the field indicates the maximum field size - in + other words, how many characters will be used from the field content. The + precision is not allowed for integer presentation types. + """ + var type: UInt8 + """Determines how the data should be presented. + + The available integer presentation types are: + + | Option | Meaning| + |:-------|:-------| + |'b' |Binary format. Outputs the number in base 2.| + |'c' |Character. Converts the integer to the corresponding unicode character + before printing.| + |'d' |Decimal Integer. Outputs the number in base 10.| + |'o' |Octal format. Outputs the number in base 8.| + |'x' |Hex format. Outputs the number in base 16, using lower-case letters + for the digits above 9.| + |'X' |Hex format. Outputs the number in base 16, using upper-case letters + for the digits above 9. In case '#' is specified, the prefix '0x' will be + upper-cased to '0X' as well.| + |'n' |Number. This is the same as 'd', except that it uses the current + locale setting to insert the appropriate number separator characters.| + |None | The same as 'd'.| + + In addition to the above presentation types, integers can be formatted with + the floating-point presentation types listed below (except 'n' and None). + When doing so, float() is used to convert the integer to a floating-point + number before formatting. + + The available presentation types for float and Decimal values are: + + | Option | Meaning| + |:-------|:-------| + |'e' |Scientific notation. For a given precision p, formats the number in + scientific notation with the letter `e` separating the coefficient from the + exponent. The coefficient has one digit before and p digits after the + decimal point, for a total of p + 1 significant digits. With no precision + given, uses a precision of 6 digits after the decimal point for float, and + shows all coefficient digits for Decimal. If no digits follow the decimal + point, the decimal point is also removed unless the # option is used.| + |'E' |Scientific notation. Same as 'e' except it uses an upper case `E` as + the separator character.| + |'f' |Fixed-point notation. For a given precision p, formats the number as a + decimal number with exactly p digits following the decimal point. With no + precision given, uses a precision of 6 digits after the decimal point for + float, and uses a precision large enough to show all coefficient digits for + Decimal. If no digits follow the decimal point, the decimal point is also + removed unless the # option is used.| + |'F' |Fixed-point notation. Same as 'f', but converts nan to NAN and inf to + INF.| + |'g' |General format. For a given precision p >= 1, this rounds the number + to p significant digits and then formats the result in either fixed-point + format or in scientific notation, depending on its magnitude. A precision of + 0 is treated as equivalent to a precision of 1. + The precise rules are as follows: suppose that the result formatted with + presentation type 'e' and precision p-1 would have exponent exp. Then, if + m <= exp < p, where m is -4 for floats and -6 for Decimals, the number is + formatted with presentation type 'f' and precision p-1-exp. Otherwise, the + number is formatted with presentation type 'e' and precision p-1. In both + cases insignificant trailing zeros are removed from the significand, and the + decimal point is also removed if there are no remaining digits following it, + unless the '#' option is used. + With no precision given, uses a precision of 6 significant digits for float. + For Decimal, the coefficient of the result is formed from the coefficient + digits of the value; scientific notation is used for values smaller than + 1e-6 in absolute value and values where the place value of the least + significant digit is larger than 1, and fixed-point notation is used + otherwise. + Positive and negative infinity, positive and negative zero, and nans, are + formatted as inf, -inf, 0, -0 and nan respectively, regardless of the + precision.| + |'G' |General format. Same as 'g' except switches to 'E' if the number gets + too large. The representations of infinity and NaN are uppercased, too.| + |'n' |Number. This is the same as 'g', except that it uses the current + locale setting to insert the appropriate number separator characters.| + |'%' |Percentage. Multiplies the number by 100 and displays in fixed ('f') + format, followed by a percent sign.| + |None |For float this is like the 'g' type, except that when fixed-point + notation is used to format the result, it always includes at least one digit + past the decimal point, and switches to the scientific notation when + exp >= p - 1. When the precision is not specified, the latter will be as + large as needed to represent the given value faithfully. + For Decimal, this is the same as either 'g' or 'G' depending on the value of + context.capitals for the current decimal context. + The overall effect is to match the output of str() as altered by the other + format modifiers.| + """ + + fn __init__( + inout self, + fill: UInt8 = ord(" "), + align: UInt8 = 0, + sign: UInt8 = ord("-"), + coerce_z: Bool = False, + alternate_form: Bool = False, + width: UInt8 = 0, + grouping_option: UInt8 = 0, + precision: UInt8 = 0, + type: UInt8 = 0, + ): + """Construct a FormatSpec instance. + + Args: + fill: Defaults to space. + align: Defaults to 0 which is adjusted to the default for the arg + type. + sign: Defaults to `-`. + coerce_z: Defaults to False. + alternate_form: Defaults to False. + width: Defaults to 0 which is adjusted to the default for the arg + type. + grouping_option: Defaults to 0 which is adjusted to the default for + the arg type. + precision: Defaults to 0 which is adjusted to the default for the + arg type. + type: Defaults to 0 which is adjusted to the default for the arg + type. + """ + self.fill = fill + self.align = align + self.sign = sign + self.coerce_z = coerce_z + self.alternate_form = alternate_form + self.width = width + self.grouping_option = grouping_option + self.precision = precision + self.type = type + + @staticmethod + fn parse(fmt_str: StringSlice) -> Optional[Self]: + """Parses the format spec string. + + Args: + fmt_str: The StringSlice with the format spec. + + Returns: + An instance of FormatSpec. + """ + var f_len = fmt_str.byte_length() + var f_ptr = fmt_str.unsafe_ptr() + var colon_idx = -1 + var idx = 0 + while idx < f_len: + if f_ptr[idx] == ord(":"): + exclamation_index = idx + break + idx += 1 + + if colon_idx == -1: + return None + + # TODO: Future implementation of format specifiers + return None + + fn stringify[ + T: _CurlyEntryFormattable + ](self, inout res: String, item: T) raises: + """Stringify a type according to its format specification. + + Args: + res: The resulting String. + item: The item to stringify. + """ + var type_implements_float = True # TODO + var type_implements_float_raising = True # TODO + var type_implements_int = True # TODO + var type_implements_int_raising = True # TODO + + # TODO: transform to int/float depending on format spec and stringify + # with hex/bin/oct etc. + res += str(item) + + fn format_string(self, inout res: String, item: String) raises: + """Transform a String according to its format specification. + + Args: + res: The resulting String. + item: The item to format. + """ + + # TODO: align, fill, etc. + res += item diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index ed92b61a49..de0a461913 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1396,50 +1396,36 @@ def test_format_args(): with assert_raises(contains="Index first not in kwargs"): _ = String("A {first} B {second}").format(1, 2) - assert_equal( - String(" {} , {} {} !").format( - "Hello", - "Beautiful", - "World", - ), - " Hello , Beautiful World !", - ) + var s = String(" {} , {} {} !").format("Hello", "Beautiful", "World") + assert_equal(s, " Hello , Beautiful World !") - with assert_raises( - contains="there is a single curly { left unclosed or unescaped" - ): + fn curly(c: StringLiteral) -> StringLiteral: + return "there is a single curly " + c + " left unclosed or unescaped" + + with assert_raises(contains=curly("{")): _ = String("{ {}").format(1) - with assert_raises( - contains="there is a single curly { left unclosed or unescaped" - ): + with assert_raises(contains=curly("{")): _ = String("{ {0}").format(1) - with assert_raises( - contains="there is a single curly { left unclosed or unescaped" - ): + with assert_raises(contains=curly("{")): _ = String("{}{").format(1) - with assert_raises( - contains="there is a single curly } left unclosed or unescaped" - ): + with assert_raises(contains=curly("}")): _ = String("{}}").format(1) - with assert_raises( - contains="there is a single curly { left unclosed or unescaped" - ): + with assert_raises(contains=curly("{")): _ = String("{} {").format(1) - with assert_raises( - contains="there is a single curly { left unclosed or unescaped" - ): + with assert_raises(contains=curly("{")): _ = String("{").format(1) - with assert_raises( - contains="there is a single curly } left unclosed or unescaped" - ): + with assert_raises(contains=curly("}")): _ = String("}").format(1) + with assert_raises(contains=""): + _ = String("{}").format() + assert_equal(String("}}").format(), "}") assert_equal(String("{{").format(), "{") @@ -1469,13 +1455,8 @@ def test_format_args(): output = String(vinput).format() assert_equal(len(output), 0) - assert_equal( - String("{0} {1} ❤️‍🔥 {1} {0}").format( - "🔥", - "Mojo", - ), - "🔥 Mojo ❤️‍🔥 Mojo 🔥", - ) + var res = "🔥 Mojo ❤️‍🔥 Mojo 🔥" + assert_equal(String("{0} {1} ❤️‍🔥 {1} {0}").format("🔥", "Mojo"), res) assert_equal(String("{0} {1}").format(True, 1.125), "True 1.125") @@ -1501,6 +1482,7 @@ def test_format_conversion_flags(): assert_equal(String("{} {!r}").format(a, a), "Mojo 'Mojo'") assert_equal(String("{!s} {!r}").format(a, a), "Mojo 'Mojo'") assert_equal(String("{0!s} {0!r}").format(a), "Mojo 'Mojo'") + assert_equal(String("{0!s} {0!r}").format(a, "Mojo2"), "Mojo 'Mojo'") var b = 21.1 assert_true( diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index 1ec984086b..b411b857e5 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -18,7 +18,7 @@ from hashlib._ahash import AHasher from hashlib.hash import hash as old_hash from hashlib._hasher import _hash_with_hasher as hash from testing import assert_equal, assert_not_equal, assert_true -from memory import memset_zero, UnsafePointer +from memory import memset_zero, stack_allocation from time import now from utils import Span @@ -576,7 +576,7 @@ you, утра, боль, хорошие, пришёл, открой, брось, fn gen_word_pairs[words: String = words_en]() -> List[String]: var result = List[String]() try: - var list = words.split(",") + var list = words.split(", ") for w in list: var w1 = w[].strip() for w in list: @@ -636,7 +636,7 @@ def test_hash_byte_array(): def test_avalanche(): # test that values which differ just in one bit, # produce significatly different hash values - var data = UnsafePointer[UInt8].alloc(256) + var data = stack_allocation[256, UInt8]() memset_zero(data, 256) var hashes0 = List[UInt64]() var hashes1 = List[UInt64]() @@ -666,7 +666,7 @@ def test_avalanche(): def test_trailing_zeros(): # checks that a value with different amount of trailing zeros, # results in significantly different hash values - var data = UnsafePointer[UInt8].alloc(8) + var data = stack_allocation[8, UInt8]() memset_zero(data, 8) data[0] = 23 var hashes0 = List[UInt64]() From 3ac1f39e477fee1b46a948b0e9d3730e260e34cc Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Wed, 16 Oct 2024 17:50:50 -0600 Subject: [PATCH 1773/2019] [External] [stdlib] Improve readability of `test_python_object` (#49254) [External] [stdlib] Improve readability of `test_python_object` Instead of raw strings with some Python inline for types and methods, pull them out into a separate Python module: `custom_indexable.py` and use that within the test. This allows for better maintenance and readability by having the code in a separate file. Co-authored-by: Joshua James Venter Closes modularml/mojo#3678 MODULAR_ORIG_COMMIT_REV_ID: c9a63e78e4d2f38401b6d344accd356dca89f9e6 --- stdlib/test/python/custom_indexable.py | 48 ++++++++++++++++ stdlib/test/python/test_python_object.mojo | 65 ++++++---------------- 2 files changed, 65 insertions(+), 48 deletions(-) create mode 100644 stdlib/test/python/custom_indexable.py diff --git a/stdlib/test/python/custom_indexable.py b/stdlib/test/python/custom_indexable.py new file mode 100644 index 0000000000..1164f7f5cb --- /dev/null +++ b/stdlib/test/python/custom_indexable.py @@ -0,0 +1,48 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +class Simple: + def __init__(self): + pass + + +class WithGetItem: + def __getitem__(self, key): + if isinstance(key, tuple): + return "Keys: {0}".format(", ".join(map(str, key))) + else: + return "Key: {0}".format(key) + + +class WithGetItemException: + def __getitem__(self, key): + raise ValueError("Custom error") + + +class With2DGetItem: + def __init__(self): + self.data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + + def __getitem__(self, key): + if isinstance(key, tuple) and all(isinstance(k, slice) for k in key): + return [row[key[1]] for row in self.data[key[0]]] + elif isinstance(key, tuple): + return self.data[key[0]][key[1]] + else: + return self.data[key] + + +class Sliceable: + def __getitem__(self, key): + return key diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 264eb958ee..b6e5fc38c1 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -410,6 +410,8 @@ fn test_none() raises: fn test_getitem_raises() raises: + custom_indexable = Python.import_module("custom_indexable") + var a = PythonObject(2) with assert_raises(contains="'int' object is not subscriptable"): _ = a[0] @@ -434,43 +436,23 @@ fn test_getitem_raises() raises: with assert_raises(contains="'NoneType' object is not subscriptable"): _ = d[0, 0] - with_get = Python.evaluate( - """type('WithGetItem', (), { - '__getitem__': lambda self, key: - 'Keys: {0}'.format(", ".join(map(str, key))) if isinstance(key, tuple) - else 'Key: {0}'.format(key) - })()""" - ) + with_get = custom_indexable.WithGetItem() assert_equal("Key: 0", str(with_get[0])) assert_equal("Keys: 0, 0", str(with_get[0, 0])) assert_equal("Keys: 0, 0, 0", str(with_get[0, 0, 0])) - var without_get = Python.evaluate( - "type('WithOutGetItem', (), {'__str__': lambda self: \"SomeString\"})()" - ) - with assert_raises(contains="'WithOutGetItem' object is not subscriptable"): + var without_get = custom_indexable.Simple() + with assert_raises(contains="'Simple' object is not subscriptable"): _ = without_get[0] - with assert_raises(contains="'WithOutGetItem' object is not subscriptable"): + with assert_raises(contains="'Simple' object is not subscriptable"): _ = without_get[0, 0] - var with_get_exception = Python.evaluate( - "type('WithGetItemException', (), {'__getitem__': lambda self, key: (_" - ' for _ in ()).throw(ValueError("Custom error")),})()' - ) - + var with_get_exception = custom_indexable.WithGetItemException() with assert_raises(contains="Custom error"): _ = with_get_exception[1] - with_2d = Python.evaluate( - """type('With2D', (), { - '__init__': lambda self: setattr(self, 'data', [[1, 2, 3], [4, 5, 6]]), - '__getitem__': lambda self, key: ( - self.data[key[0]][key[1]] if isinstance(key, tuple) - else self.data[key] - ) - })()""" - ) + with_2d = custom_indexable.With2DGetItem() assert_equal("[1, 2, 3]", str(with_2d[0])) assert_equal(2, with_2d[0, 1]) assert_equal(6, with_2d[1, 2]) @@ -479,13 +461,14 @@ fn test_getitem_raises() raises: _ = with_2d[0, 4] with assert_raises(contains="list index out of range"): - _ = with_2d[2, 0] + _ = with_2d[3, 0] with assert_raises(contains="list index out of range"): - _ = with_2d[2] + _ = with_2d[3] def test_setitem_raises(): + custom_indexable = Python.import_module("custom_indexable") t = Python.evaluate("(1,2,3)") with assert_raises( contains="'tuple' object does not support item assignment" @@ -502,15 +485,11 @@ def test_setitem_raises(): ): s[3] = "xy" - custom = Python.evaluate( - """type('Custom', (), { - '__init__': lambda self: None, - })()""" - ) + with_out = custom_indexable.Simple() with assert_raises( - contains="'Custom' object does not support item assignment" + contains="'Simple' object does not support item assignment" ): - custom[0] = 0 + with_out[0] = 0 d = Python.evaluate("{}") with assert_raises(contains="unhashable type: 'list'"): @@ -518,6 +497,7 @@ def test_setitem_raises(): fn test_py_slice() raises: + custom_indexable = Python.import_module("custom_indexable") var a = PythonObject([1, 2, 3, 4, 5]) assert_equal("[2, 3]", str(a[1:3])) assert_equal("[1, 2, 3, 4, 5]", str(a[:])) @@ -564,25 +544,14 @@ fn test_py_slice() raises: # with assert_raises(contains="slice(1, 3, None)"): # _ = d[1:3] - var custom = Python.evaluate( - "type('CustomSliceable', (), {'__getitem__': lambda self, key: key})()" - ) + var custom = custom_indexable.Sliceable() assert_equal("slice(1, 3, None)", str(custom[1:3])) var i = PythonObject(1) with assert_raises(contains="'int' object is not subscriptable"): _ = i[0:1] - with_2d = Python.evaluate( - """type('With2D', (), { - '__init__': lambda self: setattr(self, 'data', [[1, 2, 3], [4, 5, 6], [7, 8, 9]]), - '__getitem__': lambda self, key: ( - [row[key[1]] for row in self.data[key[0]]] if isinstance(key, tuple) and all(isinstance(k, slice) for k in key) - else (self.data[key[0]][key[1]] if isinstance(key, tuple) - else self.data[key]) - ) - })()""" - ) + with_2d = custom_indexable.With2DGetItem() assert_equal("[1, 2]", str(with_2d[0, PythonObject(Slice(0, 2))])) assert_equal("[1, 2]", str(with_2d[0][0:2])) From 2bd13af95ffce2c6ecf303ee042641ea695b8ab6 Mon Sep 17 00:00:00 2001 From: Helehex Date: Wed, 16 Oct 2024 17:51:05 -0600 Subject: [PATCH 1774/2019] [External] [stdlib] Fix example in `/floatable.mojo` (#49279) [External] [stdlib] Fix example in `/floatable.mojo` Co-authored-by: Helehex Closes modularml/mojo#3679 MODULAR_ORIG_COMMIT_REV_ID: 5204d8180f7dfe4ede4afc7392e7c61d603aaa4d --- stdlib/src/builtin/floatable.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/floatable.mojo b/stdlib/src/builtin/floatable.mojo index 340265d291..1510173716 100644 --- a/stdlib/src/builtin/floatable.mojo +++ b/stdlib/src/builtin/floatable.mojo @@ -69,8 +69,10 @@ trait FloatableRaising: For example: ```mojo + from utils import Variant + @value - struct MaybeFloat(FloatableRasing): + struct MaybeFloat(FloatableRaising): var value: Variant[Float64, NoneType] fn __float__(self) raises -> Float64: From ec76a2cabf7d64aedfc36ca1082806f8aec00f12 Mon Sep 17 00:00:00 2001 From: Helehex Date: Thu, 17 Oct 2024 11:11:58 -0600 Subject: [PATCH 1775/2019] [External] [stdlib] Fix `Roundable` example (#49308) [External] [stdlib] Fix `Roundable` example I left the `Absable` one (even though it's broken), since it will probably change with associated aliases. Co-authored-by: Helehex Closes modularml/mojo#3685 MODULAR_ORIG_COMMIT_REV_ID: 686a53086cecc5201a40964de05caf973b65b07a --- stdlib/src/builtin/math.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index e29c3d23a3..2506699f9d 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -359,7 +359,10 @@ trait Roundable: var im: Float64 fn __round__(self) -> Self: - return Self(round(re), round(im)) + return Self(round(self.re), round(self.im)) + + fn __round__(self, ndigits: Int) -> Self: + return Self(round(self.re, ndigits), round(self.im, ndigits)) ``` """ From 89360eb421a816c1f886e8dd075ec2d26b8bb09e Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 17 Oct 2024 14:28:14 -0700 Subject: [PATCH 1776/2019] [stdlib] Change `Formatter` struct to `Writer` trait This is to enable buffering before printing output, where some state needs to be held, which wasn't possible when passing a closure for the write function to a `Formatter`. The next PR in stack improves the print performance by 122x on a 10k loop of 5 args, coming within 1ms of C, and 2.27x faster than Python. MODULAR_ORIG_COMMIT_REV_ID: 00dc54c9e57a3343c3d74a7432db2762a2badc1b --- docs/changelog.md | 4 +- docs/manual/errors.mdx | 2 +- docs/manual/traits.ipynb | 34 +-- docs/roadmap.md | 4 +- stdlib/benchmarks/utils/bench_formatter.mojo | 22 +- stdlib/src/builtin/_location.mojo | 11 +- stdlib/src/builtin/bool.mojo | 11 +- stdlib/src/builtin/builtin_slice.mojo | 14 +- stdlib/src/builtin/debug_assert.mojo | 14 +- stdlib/src/builtin/dtype.mojo | 49 ++-- stdlib/src/builtin/error.mojo | 11 +- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/file_descriptor.mojo | 62 +++++- stdlib/src/builtin/format_int.mojo | 27 +-- stdlib/src/builtin/int.mojo | 32 ++- stdlib/src/builtin/io.mojo | 9 +- stdlib/src/builtin/none.mojo | 11 +- stdlib/src/builtin/object.mojo | 15 +- stdlib/src/builtin/simd.mojo | 59 ++--- stdlib/src/builtin/string_literal.mojo | 24 +- stdlib/src/builtin/uint.mojo | 9 +- stdlib/src/collections/list.mojo | 14 +- stdlib/src/collections/optional.mojo | 21 +- stdlib/src/collections/set.mojo | 13 +- stdlib/src/collections/string.mojo | 138 +++++++----- stdlib/src/memory/unsafe_pointer.mojo | 11 +- stdlib/src/os/os.mojo | 6 +- stdlib/src/pathlib/path.mojo | 11 +- stdlib/src/prelude/__init__.mojo | 2 +- stdlib/src/pwd/pwd.mojo | 9 +- stdlib/src/python/_cpython.mojo | 37 ++-- stdlib/src/python/python_object.mojo | 11 +- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/_serialize.mojo | 8 +- stdlib/src/utils/format.mojo | 222 ------------------- stdlib/src/utils/index.mojo | 11 +- stdlib/src/utils/inline_string.mojo | 88 ++++---- stdlib/src/utils/span.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 44 ++-- stdlib/src/utils/stringref.mojo | 33 ++- stdlib/src/utils/write.mojo | 148 +++++++++++++ stdlib/test/builtin/test_debug_assert.mojo | 14 +- stdlib/test/utils/test_format.mojo | 39 ++-- stdlib/test/utils/test_format_to_stdout.mojo | 9 +- stdlib/test/utils/test_string_slice.mojo | 2 +- 45 files changed, 702 insertions(+), 619 deletions(-) delete mode 100644 stdlib/src/utils/format.mojo create mode 100644 stdlib/src/utils/write.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 040fdcad35..4b20e24e5a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -274,9 +274,9 @@ what we publish. `IndexList`. The datastructure now allows one to specify the index bitwidth of the elements along with whether the underlying indices are signed or unsigned. -- A new trait has been added `AsBytes` to enable taking a `Span[UInt8]` of a +- A new trait has been added `AsBytes` to enable taking a `Span[Byte]` of a type with `s.as_bytes()`. `String.as_bytes` and `String.as_bytes_slice` have - been consolidated under `s.as_bytes` to return a `Span[UInt8]`, you can convert + been consolidated under `s.as_bytes` to return a `Span[Byte]`, you can convert it to a `List` if you require a copy with `List(s.as_bytes())`. - `Lifetime` and related types has been renamed to `Origin` in the standard diff --git a/docs/manual/errors.mdx b/docs/manual/errors.mdx index e94a6d40e4..c87fab7777 100644 --- a/docs/manual/errors.mdx +++ b/docs/manual/errors.mdx @@ -103,7 +103,7 @@ recovery that's appropriate for your application. If you provide the name of a variable after the `except` keyword, then the `Error` instance is bound to the variable if an error occurs. The `Error` type -implements the [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait, so +implements the [`Writable`](/mojo/stdlib/utils/write/Writable) trait, so you can pass it as an argument to the [`print()`](/mojo/stdlib/builtin/io/print) function if you'd like to print its error message to the console. It also implements the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, so you diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 6643bb17ce..98d0435509 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -451,7 +451,7 @@ " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", " - [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable)\n", - " - [`Formattable`](/mojo/stdlib/utils/format/Formattable)\n", + " - [`Writable`](/mojo/stdlib/utils/write/Writable)\n", " - [`Hashable`](/mojo/stdlib/builtin/hash/Hashable)\n", " - [`Indexer`](/mojo/stdlib/builtin/int/Indexer)\n", " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", @@ -469,7 +469,7 @@ " - [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising)\n", " - [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", " - [`Roundable`](/mojo/stdlib/builtin/math/Roundable)\n", - " - [`ToFormatter`](/mojo/stdlib/utils/_format/ToFormatter)\n", + " - [`Writer`](/mojo/stdlib/utils/write/Writer)\n", " - [`Truncable`](/mojo/stdlib/builtin/_math/Truncable)\n", "\n", "The API reference docs linked above include usage examples for each trait. The\n", @@ -557,7 +557,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The `Stringable`, `Representable`, and `Formattable` traits\n", + "### The `Stringable`, `Representable`, and `Writable` traits\n", "\n", "The [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait identifies a type\n", "that can be implicitly converted to\n", @@ -586,27 +586,27 @@ "traits. It requires a type to implement both a `__str__()` and a `__repr__()`\n", "method.\n", "\n", - "The [`Formattable`](/mojo/stdlib/utils/format/Formattable) trait describes a\n", + "The [`Writable`](/mojo/stdlib/utils/write/Writable) trait describes a\n", "type that can be converted to a stream of UTF-8 encoded data by writing to a\n", - "formatter object. The [`print()`](/mojo/stdlib/builtin/io/print) function\n", - "requires that its arguments conform to the `Formattable` trait. This enables\n", + "`Writer` object. The [`print()`](/mojo/stdlib/builtin/io/print) function\n", + "requires that its arguments conform to the `Writable` trait. This enables\n", "efficient stream-based writing by default, avoiding unnecessary intermediate\n", "String heap allocations.\n", "\n", - "The `Formattable` trait requires a type to implement a\n", - "[`format_to()`](/mojo/stdlib/utils/format/Formattable#format_to) method, which\n", - "is provided with an instance of\n", - "[`Formatter`](/mojo/stdlib/utils/format/Formatter) as an argument. You then\n", - "invoke the `Formatter` instance's\n", - "[`write()`](/mojo/stdlib/utils/format/Formatter#write) method to write a\n", - "sequence of `Formattable` arguments constituting the `String` representation of\n", + "The `Writable` trait requires a type to implement a\n", + "[`write_to()`](/mojo/stdlib/utils/write/Writable#write_to) method, which\n", + "is provided with an object that conforms to the\n", + "[`Writer`](/mojo/stdlib/utils/write/Writer) as an argument. You then\n", + "invoke the `Writer` instance's\n", + "[`write()`](/mojo/stdlib/utils/write/Writer#write) method to write a\n", + "sequence of `Writable` arguments constituting the `String` representation of\n", "your type.\n", "\n", "While this might sound complex at first, in practice you can minimize\n", "boilerplate and duplicated code by using the [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) static function to\n", - "implement the type's `Stringable` implementation in terms of its `Formattable`\n", + "implement the type's `Stringable` implementation in terms of its `Writable`\n", "implementation. Here is a simple example of a type that implements all of the\n", - "`Stringable`, `Representable`, and `Formattable` traits:" + "`Stringable`, `Representable`, and `Writable` traits:" ] }, { @@ -627,7 +627,7 @@ ], "source": [ "@value\n", - "struct Dog(Stringable, Representable, Formattable):\n", + "struct Dog(Stringable, Representable, Writable):\n", " var name: String\n", " var age: Int\n", "\n", @@ -637,7 +637,7 @@ " fn __str__(self) -> String:\n", " return String.format_sequence(self)\n", "\n", - " fn format_to(self, inout writer: Formatter) -> None:\n", + " fn write_to[W: Writer](self, inout writer: W) -> None:\n", " writer.write(\"Dog(\", self.name, \", \", self.age, \")\")\n", "\n", "var dog = Dog(\"Rex\", 5)\n", diff --git a/docs/roadmap.md b/docs/roadmap.md index fcd5c6fcff..fff951648f 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -373,8 +373,8 @@ print(One()) # prints '1' ``` Mojo currently supports similar functionality through the -[`Formattable`](/mojo/stdlib/utils/format/Formattable) trait, so that -`print()` works on all `Formattable` types. Similar support exists for the +[`Writable`](/mojo/stdlib/utils/write/Writable) trait, so that +`print()` works on all `Writable` types. Similar support exists for the [`int()`](/mojo/stdlib/builtin/int/int-function) and [`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 43b9e686ed..6797c268b7 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -29,26 +29,24 @@ from utils.stringref import _align_down, _memchr, _memmem # Benchmarks # ===----------------------------------------------------------------------===# @parameter -fn bench_formatter_int[n: Int](inout b: Bencher) raises: +fn bench_writer_int[n: Int](inout b: Bencher) raises: @always_inline @parameter fn call_fn(): var s1 = String() - var s1_fmt = Formatter(s1) - Int(n).format_to(s1_fmt) + s1.write(n) _ = s1^ b.iter[call_fn]() @parameter -fn bench_formatter_simd[n: Int](inout b: Bencher) raises: +fn bench_writer_simd[n: Int](inout b: Bencher) raises: @always_inline @parameter fn call_fn(): var s1 = String() - var s1_fmt = Formatter(s1) - SIMD[DType.int32, simdwidthof[DType.int32]()](n).format_to(s1_fmt) + s1.write(SIMD[DType.int32, simdwidthof[DType.int32]()](n)) _ = s1^ b.iter[call_fn]() @@ -59,12 +57,12 @@ fn bench_formatter_simd[n: Int](inout b: Bencher) raises: # ===----------------------------------------------------------------------===# def main(): var m = Bench(BenchConfig(num_repetitions=1)) - m.bench_function[bench_formatter_int[42]](BenchId("bench_formatter_int_42")) - m.bench_function[bench_formatter_int[2**64]]( - BenchId("bench_formatter_int_2**64") + m.bench_function[bench_writer_int[42]](BenchId("bench_writer_int_42")) + m.bench_function[bench_writer_int[2**64]]( + BenchId("bench_writer_int_2**64") ) - m.bench_function[bench_formatter_simd[42]](BenchId("bench_formatter_simd")) - m.bench_function[bench_formatter_simd[2**16]]( - BenchId("bench_formatter_simd_2**16") + m.bench_function[bench_writer_simd[42]](BenchId("bench_writer_simd")) + m.bench_function[bench_writer_simd[2**16]]( + BenchId("bench_writer_simd_2**16") ) m.dump_report() diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 1b6ddb583d..484c011aa2 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -16,7 +16,7 @@ @value @register_passable("trivial") -struct _SourceLocation(Formattable, Stringable): +struct _SourceLocation(Writable, Stringable): """Type to carry file name, line, and column information.""" var line: Int @@ -42,12 +42,15 @@ struct _SourceLocation(Formattable, Stringable): """ return "At " + str(self) + ": " + str(msg) - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats the source location to the provided formatter. + Formats the source location to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write(self.file_name, ":", self.line, ":", self.col) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index f40e6dec9f..13c167eb3a 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -108,7 +108,7 @@ struct Bool( Intable, Representable, Stringable, - Formattable, + Writable, Floatable, ): """The primitive Bool scalar value used in Mojo.""" @@ -223,12 +223,15 @@ struct Bool( return String.format_sequence(self) @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this boolean to the provided formatter. + Formats this boolean to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("True" if self else "False") diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index d516891281..77d7811c01 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -23,7 +23,7 @@ struct Slice( Stringable, EqualityComparable, Representable, - Formattable, + Writable, CollectionElementNew, ): """Represents a slice expression. @@ -102,8 +102,7 @@ struct Slice( The string representation of the span. """ var output = String() - var writer = output._unsafe_to_formatter() - self.format_to(writer) + self.write_to(output) return output @no_inline @@ -116,11 +115,14 @@ struct Slice( return self.__str__() @no_inline - fn format_to(self, inout writer: Formatter): - """Write Slice string representation to a `Formatter`. + fn write_to[W: Writer](self, inout writer: W): + """Write Slice string representation to a `Writer`. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ @parameter diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 6a8f442191..0dc5c80655 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -116,7 +116,7 @@ fn debug_assert[ cond: fn () capturing [_] -> Bool, assert_mode: StringLiteral = "none", cpu_only: Bool = False, - *Ts: Formattable, + *Ts: Writable, ](*messages: *Ts): """Asserts that the condition is true. @@ -124,13 +124,13 @@ fn debug_assert[ cond: The function to invoke to check if the assertion holds. assert_mode: Determines when the assert is turned on. cpu_only: If true, only run the assert on CPU. - Ts: The element types conforming to `Formattable` for the message. + Ts: The element types conforming to `Writable` for the message. Args: messages: Arguments to convert to a `String` message. - You can pass in multiple args that are `Formattable` to generate a formatted + You can pass in multiple args that are `Writable` to generate a formatted message, by default this will be a no-op: ```mojo @@ -201,20 +201,20 @@ fn debug_assert[ fn debug_assert[ assert_mode: StringLiteral = "none", cpu_only: Bool = False, - *Ts: Formattable, + *Ts: Writable, ](cond: Bool, *messages: *Ts): """Asserts that the condition is true. Parameters: assert_mode: Determines when the assert is turned on. cpu_only: If true, only run the assert on CPU. - Ts: The element types conforming to `Formattable` for the message. + Ts: The element types conforming to `Writable` for the message. Args: cond: The bool value to assert. messages: Arguments to convert to a `String` message. - You can pass in multiple args that are `Formattable` to generate a formatted + You can pass in multiple args that are `Writable` to generate a formatted message, by default this will be a no-op: ```mojo @@ -283,7 +283,7 @@ fn debug_assert[ @no_inline fn _debug_assert_msg( - messages: VariadicPack[_, Formattable, *_], loc: _SourceLocation + messages: VariadicPack[_, Writable, *_], loc: _SourceLocation ): """Aborts with (or prints) the given message and location. diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 53150b739b..3fed068b3b 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -29,7 +29,7 @@ alias _mIsFloat = UInt8(1 << 6) @register_passable("trivial") struct DType( Stringable, - Formattable, + Writable, Representable, KeyElement, CollectionElementNew, @@ -160,52 +160,55 @@ struct DType( return String.format_sequence(self) @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this dtype to the provided formatter. + Formats this dtype to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ if self == DType.bool: - return writer.write_str("bool") + return writer.write("bool") if self == DType.int8: - return writer.write_str("int8") + return writer.write("int8") if self == DType.uint8: - return writer.write_str("uint8") + return writer.write("uint8") if self == DType.int16: - return writer.write_str("int16") + return writer.write("int16") if self == DType.uint16: - return writer.write_str("uint16") + return writer.write("uint16") if self == DType.int32: - return writer.write_str("int32") + return writer.write("int32") if self == DType.uint32: - return writer.write_str("uint32") + return writer.write("uint32") if self == DType.int64: - return writer.write_str("int64") + return writer.write("int64") if self == DType.uint64: - return writer.write_str("uint64") + return writer.write("uint64") if self == DType.index: - return writer.write_str("index") + return writer.write("index") if self == DType.float8e5m2: - return writer.write_str("float8e5m2") + return writer.write("float8e5m2") if self == DType.float8e4m3: - return writer.write_str("float8e4m3") + return writer.write("float8e4m3") if self == DType.bfloat16: - return writer.write_str("bfloat16") + return writer.write("bfloat16") if self == DType.float16: - return writer.write_str("float16") + return writer.write("float16") if self == DType.float32: - return writer.write_str("float32") + return writer.write("float32") if self == DType.tensor_float32: - return writer.write_str("tensor_float32") + return writer.write("tensor_float32") if self == DType.float64: - return writer.write_str("float64") + return writer.write("float64") if self == DType.invalid: - return writer.write_str("invalid") + return writer.write("invalid") - return writer.write_str("<>") + return writer.write("<>") @always_inline("nodebug") fn __repr__(self) -> String: diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 00d7ebb439..89367a8809 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -33,7 +33,7 @@ struct Error( Stringable, Boolable, Representable, - Formattable, + Writable, CollectionElement, CollectionElementNew, ): @@ -159,12 +159,15 @@ struct Error( return String.format_sequence(self) @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this error to the provided formatter. + Formats this error to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ # TODO: Avoid this unnecessary intermediate String allocation. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 3f95e511b2..1599efb1c2 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -411,7 +411,7 @@ struct FileHandle: """ self._write(data.unsafe_ptr(), data.byte_length()) - fn write(self, data: Span[UInt8, _]) raises: + fn write(self, data: Span[Byte, _]) raises: """Write a borrowed sequence of data to the file. Args: diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index ac458d169e..7dee133efa 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -23,26 +23,27 @@ f.close() ``` """ +from utils import Span +from sys.ffi import external_call, OpaquePointer +from sys.info import triple_is_nvidia_cuda +from memory import UnsafePointer @value -struct FileDescriptor: +@register_passable("trivial") +struct FileDescriptor(Writer): """File descriptor of a file.""" var value: Int """The underlying value of the file descriptor.""" - fn __init__(inout self): - """Default constructor to stdout.""" - self.value = 1 - - fn __init__(inout self, x: Int): + fn __init__(inout self, value: Int = 1): """Constructs the file descriptor from an integer. Args: - x: The integer. + value: The file identifier (Default 1 = stdout). """ - self.value = x + self.value = value fn __init__(inout self, f: FileHandle): """Constructs the file descriptor from a file handle. @@ -51,3 +52,48 @@ struct FileDescriptor: f: The file handle. """ self.value = f._get_raw_fd() + + @always_inline + fn write_bytes(inout self, bytes: Span[Byte, _]): + """ + Write a span of bytes to the file. + + Args: + bytes: The byte span to write to this file. + """ + + @parameter + if triple_is_nvidia_cuda(): + var tmp = 0 + var arg_ptr = UnsafePointer.address_of(tmp) + # TODO: Debug assert to check bytes written when GPU print calls are buffered + _ = external_call["vprintf", Int32]( + bytes.unsafe_ptr(), arg_ptr.bitcast[OpaquePointer]() + ) + else: + written = external_call["write", Int32]( + self.value, bytes.unsafe_ptr(), len(bytes) + ) + debug_assert( + written == len(bytes), + "expected amount of bytes not written. expected: ", + len(bytes), + "but got: ", + written, + ) + + fn write[*Ts: Writable](inout self, *args: *Ts): + """Write a sequence of Writable arguments to the provided Writer. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this Writer. + """ + + @parameter + fn write_arg[i: Int, T: Writable](arg: T): + arg.write_to(self) + + args.each_idx[write_arg]() diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index d6c703a0af..0ea4fac700 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -237,18 +237,18 @@ fn _format_int[ digit_chars: StaticString = _DEFAULT_DIGIT_CHARS, prefix: StaticString = "", ) raises -> String: - var string = String() - var fmt = string._unsafe_to_formatter() + var output = String() - _write_int(fmt, value, radix, digit_chars=digit_chars, prefix=prefix) + _write_int(output, value, radix, digit_chars=digit_chars, prefix=prefix) - return string^ + return output^ fn _write_int[ - type: DType, //, + type: DType, + W: Writer, ]( - inout fmt: Formatter, + inout writer: W, value: Scalar[type], /, radix: Int = 10, @@ -257,16 +257,17 @@ fn _write_int[ prefix: StaticString = "", ) raises: var err = _try_write_int( - fmt, value, radix, digit_chars=digit_chars, prefix=prefix + writer, value, radix, digit_chars=digit_chars, prefix=prefix ) if err: raise err.value() fn _try_write_int[ - type: DType, //, + type: DType, + W: Writer, ]( - inout fmt: Formatter, + inout writer: W, value: Scalar[type], /, radix: Int = 10, @@ -305,11 +306,11 @@ fn _try_write_int[ # Prefix a '-' if the original int was negative and make positive. if value < 0: - fmt.write_str("-") + writer.write("-") # Add the custom number prefix, e.g. "0x" commonly used for hex numbers. # This comes *after* the minus sign, if present. - fmt.write_str(prefix) + writer.write(prefix) if value == 0: # TODO: Replace with safe digit_chars[:1] syntax. @@ -329,7 +330,7 @@ fn _try_write_int[ unsafe_from_utf8_ptr=zero_buf.unsafe_ptr(), len=1, ) - fmt.write_str(zero) + writer.write(zero) return None @@ -408,6 +409,6 @@ fn _try_write_int[ len=len, ) - fmt.write_str(str_slice) + writer.write(str_slice) return None diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 2015842335..0c9ea6b275 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -27,7 +27,7 @@ from collections.string import ( _calc_initial_buffer_size_int64, ) -from utils import Formattable, Formatter +from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select from sys import triple_is_nvidia_cuda, bitwidthof @@ -180,7 +180,7 @@ trait IntLike( Ceilable, Comparable, Floorable, - Formattable, + Writable, Powable, Stringable, Truncable, @@ -1129,16 +1129,38 @@ struct Int( # Methods # ===-------------------------------------------------------------------===# - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this integer to the provided formatter. + Formats this integer to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write(Int64(self)) + fn write_padded[W: Writer](self, inout writer: W, width: Int): + """Write the int right-aligned to a set padding. + + Parameters: + W: A type conforming to the Writable trait. + + Args: + writer: The object to write to. + width: The amount to pad to the left. + """ + var int_width = self._decimal_digit_count() + + # TODO: Assumes user wants right-aligned content. + if int_width < width: + for _ in range(width - int_width): + writer.write(" ") + + writer.write(self) + @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: """Convert to index. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 33231b7909..f859133386 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -31,7 +31,6 @@ from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer from utils import StringRef, StaticString, StringSlice -from utils import Formattable, Formatter # ===----------------------------------------------------------------------=== # # _file_handle @@ -341,13 +340,13 @@ fn _put[ @no_inline fn print[ - *Ts: Formattable + *Ts: Writable ]( *values: *Ts, sep: StaticString = " ", end: StaticString = "\n", flush: Bool = False, - file: FileDescriptor = stdout, + owned file: FileDescriptor = stdout, ): """Prints elements to the text stream. Each element is separated by `sep` and followed by `end`. @@ -363,10 +362,10 @@ fn print[ file: The output stream. """ - var writer = Formatter(fd=file) + var writer = file @parameter - fn print_with_separator[i: Int, T: Formattable](value: T): + fn print_with_separator[i: Int, T: Writable](value: T): writer.write(value) @parameter diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo index 6ac97be9d9..a5e85d39a8 100644 --- a/stdlib/src/builtin/none.mojo +++ b/stdlib/src/builtin/none.mojo @@ -21,7 +21,7 @@ These are Mojo built-ins, so you don't need to import them. struct NoneType( CollectionElement, CollectionElementNew, - Formattable, + Writable, Representable, Stringable, ): @@ -65,10 +65,13 @@ struct NoneType( return "None" @no_inline - fn format_to(self, inout writer: Formatter): - """Write `None` to a formatter stream. + fn write_to[W: Writer](self, inout writer: W): + """Write `None` to a writer stream. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("None") diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 832a3bb3b3..144a822edc 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -256,7 +256,7 @@ struct _ObjectImpl( CollectionElementNew, Stringable, Representable, - Formattable, + Writable, ): """This class is the underlying implementation of the value of an `object`. It is a variant of primitive types and pointers to implementations of more @@ -564,7 +564,7 @@ struct _ObjectImpl( else: lhs = lhs.convert_bool_to_int() - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """Performs conversion to string according to Python semantics. """ @@ -693,7 +693,7 @@ struct _ObjectImpl( struct object( - IntableRaising, ImplicitlyBoolable, Stringable, Representable, Formattable + IntableRaising, ImplicitlyBoolable, Stringable, Representable, Writable ): """Represents an object without a concrete type. @@ -972,14 +972,17 @@ struct object( """ return self.__bool__() - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """Performs conversion to string according to Python semantics. + Parameters: + W: A type conforming to the Writable trait. + Args: - writer: The Formatter to write to. + writer: The Writer to write to. """ - self._value.format_to(writer) + self._value.write_to(writer) @no_inline fn __str__(self) -> String: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 32ea2a732c..b2ab25b9e9 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -175,7 +175,7 @@ struct SIMD[type: DType, size: Int]( # CollectionElementNew, Floatable, Floorable, - Formattable, + Writable, Hashable, _HashableWithHasher, Intable, @@ -1434,8 +1434,7 @@ struct SIMD[type: DType, size: Int]( """ var output = String() - var writer = output._unsafe_to_formatter() - self.format_to[use_scientific_notation=True](writer) + self.write_to[use_scientific_notation=True](output) var values = output.as_string_slice() @@ -1627,54 +1626,60 @@ struct SIMD[type: DType, size: Int]( ](self.value) @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this SIMD value to the provided formatter. + Formats this SIMD value to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ - self.format_to[use_scientific_notation=False](writer) + self.write_to[use_scientific_notation=False](writer) - # This overload is required to keep SIMD compliant with the Formattable + # This overload is required to keep SIMD compliant with the Writable # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will # fail to compile. @no_inline - fn format_to[use_scientific_notation: Bool](self, inout writer: Formatter): + fn write_to[ + W: Writer, use_scientific_notation: Bool + ](self, inout writer: W): """ - Formats this SIMD value to the provided formatter. + Formats this SIMD value to the provided Writer. Parameters: - use_scientific_notation: Whether floats should use scientific notation. - This parameter does not apply to integer types. + W: A type conforming to the Writable trait. + use_scientific_notation: Whether floats should use scientific + notation. This parameter does not apply to integer types. Args: - writer: The formatter to write to. + writer: The object to write to. """ # Print an opening `[`. @parameter if size > 1: - writer.write_str("[") + writer.write("[") # Print each element. for i in range(size): var element = self[i] # Print separators between each element. if i != 0: - writer.write_str(", ") + writer.write(", ") @parameter if triple_is_nvidia_cuda(): # FIXME(MSTDL-406): # The uses of `printf` below prints "out of band" with the - # `Formatter` passed in, meaning this will only work if - # `Formatter` is an unbuffered wrapper around printf (which - # Formatter.stdout currently is by default). + # `Writer` passed in, meaning this will only work if + # `Writer` is an unbuffered wrapper around printf (which + # Writer.stdout currently is by default). # # This is a workaround to permit debug formatting of # floating-point values on GPU, where printing to stdout - # is the only way the Formatter framework is currently + # is the only way the Writer framework is currently # used. @parameter @@ -1709,7 +1714,7 @@ struct SIMD[type: DType, size: Int]( # Print a closing `]`. @parameter if size > 1: - writer.write_str("]") + writer.write("]") @always_inline fn _bits_to_float[dest_type: DType](self) -> SIMD[dest_type, size]: @@ -3117,12 +3122,15 @@ fn _simd_apply[ # ===----------------------------------------------------------------------=== # +# Scalar Formatting +# ===----------------------------------------------------------------------=== # fn _format_scalar[ - dtype: DType, //, + dtype: DType, + W: Writer, //, float_format: StringLiteral = "%.17g", -](inout writer: Formatter, value: Scalar[dtype]): +](inout writer: W, value: Scalar[dtype]): # Stack allocate enough bytes to store any formatted Scalar value of any # type. alias size: Int = _calc_format_buffer_size[dtype]() @@ -3137,11 +3145,8 @@ fn _format_scalar[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. - var span = Span[UInt8](buf)[:wrote] - - var str_slice = StringSlice(unsafe_from_utf8=span) - - writer.write_str(str_slice) + var span = Span[Byte](buf)[:wrote] + writer.write_bytes(span) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 21bdb178b1..5edcd74b20 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -21,7 +21,7 @@ from memory import memcpy, UnsafePointer from collections import List from hashlib._hasher import _HashableWithHasher, _Hasher from utils import StringRef, Span, StringSlice, StaticString -from utils import Formattable, Formatter +from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils.string_slice import ( @@ -41,7 +41,7 @@ struct StringLiteral( Boolable, Comparable, CollectionElementNew, - Formattable, + Writable, IntableRaising, KeyElement, Representable, @@ -386,7 +386,7 @@ struct StringLiteral( ) @always_inline - fn as_bytes(self) -> Span[UInt8, StaticConstantOrigin]: + fn as_bytes(self) -> Span[Byte, StaticConstantOrigin]: """ Returns a contiguous Span of the bytes owned by this string. @@ -394,13 +394,13 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, StaticConstantOrigin]( + return Span[Byte, StaticConstantOrigin]( unsafe_ptr=self.unsafe_ptr(), len=self.byte_length(), ) @always_inline - fn as_bytes(ref [_]self) -> Span[UInt8, __origin_of(self)]: + fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: @@ -409,9 +409,8 @@ struct StringLiteral( Notes: This does not include the trailing null terminator. """ - # Does NOT include the NUL terminator. - return Span[UInt8, __origin_of(self)]( + return Span[Byte, __origin_of(self)]( unsafe_ptr=self.unsafe_ptr(), len=self.byte_length(), ) @@ -442,15 +441,18 @@ struct StringLiteral( """ return _FormatCurlyEntry.format(self, args) - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this string literal to the provided formatter. + Formats this string literal to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ - writer.write_str(self.as_string_slice()) + writer.write(self.as_string_slice()) fn find(self, substr: StringLiteral, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 43bbc78433..eb99e27225 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -124,11 +124,14 @@ struct UInt(IntLike, _HashableWithHasher): return String.format_sequence(self) @no_inline - fn format_to(self, inout writer: Formatter): - """Formats this integer to the provided formatter. + fn write_to[W: Writer](self, inout writer: W): + """Formats this integer to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write(UInt64(self)) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 5ba17d6f16..1587298703 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -416,21 +416,21 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( A string representation of the list. """ var output = String() - var writer = output._unsafe_to_formatter() - self.format_to(writer) + self.write_to(output) return output^ @no_inline - fn format_to[ - U: RepresentableCollectionElement, // - ](self: List[U, *_], inout writer: Formatter): - """Write `my_list.__str__()` to a `Formatter`. + fn write_to[ + W: Writer, U: RepresentableCollectionElement, // + ](self: List[U, *_], inout writer: W): + """Write `my_list.__str__()` to a `Writer`. Parameters: + W: A type conforming to the Writable trait. U: The type of the List elements. Must have the trait `RepresentableCollectionElement`. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("[") for i in range(len(self)): diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index f69e0d97ff..c556f0c4ee 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -254,8 +254,7 @@ struct Optional[T: CollectionElement]( A string representation of the Optional. """ var output = String() - var writer = output._unsafe_to_formatter() - self.format_to(writer) + self.write_to(output) return output # TODO: Include the Parameter type in the string as well. @@ -272,23 +271,23 @@ struct Optional[T: CollectionElement]( A verbose string representation of the Optional. """ var output = String() - var writer = output._unsafe_to_formatter() - writer.write("Optional(") - self.format_to(writer) - writer.write(")") + output.write("Optional(") + self.write_to(output) + output.write(")") return output - fn format_to[ - U: RepresentableCollectionElement, // - ](self: Optional[U], inout writer: Formatter): - """Write Optional string representation to a `Formatter`. + fn write_to[ + W: Writer, U: RepresentableCollectionElement, // + ](self: Optional[U], inout writer: W): + """Write Optional string representation to a `Writer`. Parameters: + W: A type conforming to the Writable trait. U: The type of the elements in the list. Must implement the traits `Representable` and `CollectionElement`. Args: - writer: The formatter to write to. + writer: The object to write to. """ if self: writer.write(repr(self.value())) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 83f07d4e58..8d9f6d60f0 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -313,8 +313,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): The string representation of the set. """ var output = String() - var writer = output._unsafe_to_formatter() - self.format_to(writer) + self.write_to(output) return output @no_inline @@ -329,16 +328,18 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return self.__str__() - fn format_to[ + fn write_to[ + W: Writer, U: RepresentableKeyElement, - ](self: Set[U], inout writer: Formatter): - """Write Set string representation to a `Formatter`. + ](self: Set[U], inout writer: W): + """Write Set string representation to a `Writer`. Parameters: + W: A type conforming to the Writable trait. U: The type of the List elements. Must have the trait `RepresentableCollectionElement`. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("{") var written = 0 diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 33e68615af..9b3a34630b 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -19,6 +19,7 @@ from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic from sys.ffi import c_char, OpaquePointer +from utils import StaticString from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy @@ -33,10 +34,9 @@ from utils import ( StringRef, StringSlice, Variant, - Formattable, - Formatter, + Writable, + Writer, ) -from utils.format import ToFormatter from utils.string_slice import ( _utf8_byte_type, _StringSliceIter, @@ -700,8 +700,8 @@ struct String( KeyElement, Comparable, Boolable, - Formattable, - ToFormatter, + Writable, + Writer, CollectionElementNew, FloatableRaising, _HashableWithHasher, @@ -840,25 +840,55 @@ struct String( # Factory dunders # ===------------------------------------------------------------------=== # + fn write_bytes(inout self, bytes: Span[Byte, _]): + """ + Write a byte span to this String. + + Args: + bytes: The byte span to write to this String. Must NOT be + null terminated. + """ + self._iadd[True](bytes) + + fn write[*Ts: Writable](inout self, *args: *Ts): + """Write a sequence of Writable arguments to the provided Writer. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this Writer. + """ + + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() + @staticmethod @no_inline - fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + fn format_sequence[ + *Ts: Writable + ](*args: *Ts, sep: StaticString = "", end: StaticString = "") -> Self: """ - Construct a string by concatenating a sequence of formattable arguments. + Construct a string by concatenating a sequence of Writable arguments. Args: - args: A sequence of formattable arguments. + args: A sequence of Writable arguments. + sep: The separator used between elements. + end: The String to write after printing the elements. Parameters: Ts: The types of the arguments to format. Each type must be satisfy - `Formattable`. + `Writable`. Returns: A string formed by formatting the argument sequence. Examples: - Construct a String from several `Formattable` arguments: + Construct a String from several `Writable` arguments: ```mojo var string = String.format_sequence(1, ", ", 2.0, ", ", "three") @@ -873,12 +903,24 @@ struct String( @staticmethod @no_inline - fn format_sequence(args: VariadicPack[_, Formattable, *_]) -> Self: + fn format_sequence[ + *Ts: Writable + ]( + args: VariadicPack[_, Writable, *Ts], + sep: StaticString = "", + end: StaticString = "", + ) -> Self: """ - Construct a string directly from a variadic pack. + Construct a string by passing a variadic pack. Args: - args: A VariadicPack of formattable arguments. + args: A VariadicPack of Writable arguments. + sep: The separator used between elements. + end: The String to write after printing the elements. + + Parameters: + Ts: The types of the arguments to format. Each type must be satisfy + `Writable`. Returns: A string formed by formatting the VariadicPack. @@ -887,7 +929,7 @@ struct String( ```mojo fn variadic_pack_to_string[ - *Ts: Formattable, + *Ts: Writable, ](*args: *Ts) -> String: return String.format_sequence(args) @@ -899,14 +941,18 @@ struct String( """ var output = String() - var writer = output._unsafe_to_formatter() @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(writer) + fn write_arg[i: Int, T: Writable](arg: T): + output.write(arg) - args.each[write_arg]() - _ = writer^ + @parameter + if i < len(VariadicList(Ts)) - 1: + output.write(sep) + + args.each_idx[write_arg]() + + output.write(end) return output^ @@ -1066,7 +1112,7 @@ struct String( return not (self < rhs) @staticmethod - fn _add[rhs_has_null: Bool](lhs: Span[UInt8], rhs: Span[UInt8]) -> String: + fn _add[rhs_has_null: Bool](lhs: Span[Byte], rhs: Span[Byte]) -> String: var lhs_len = len(lhs) var rhs_len = len(rhs) var lhs_ptr = lhs.unsafe_ptr() @@ -1160,7 +1206,7 @@ struct String( """ return Self._add[True](other.as_bytes(), self.as_bytes()) - fn _iadd[has_null: Bool](inout self, other: Span[UInt8]): + fn _iadd[has_null: Bool](inout self, other: Span[Byte]): var s_len = self.byte_length() var o_len = len(other) var o_ptr = other.unsafe_ptr() @@ -1316,38 +1362,18 @@ struct String( # Methods # ===------------------------------------------------------------------=== # - fn format_to(self, inout writer: Formatter): - """ - Formats this string to the provided formatter. - - Args: - writer: The formatter to write to. + fn write_to[W: Writer](self, inout writer: W): """ + Formats this string to the provided Writer. - writer.write_str(self.as_string_slice()) - - fn _unsafe_to_formatter(inout self) -> Formatter: - """ - Constructs a formatter that will write to this mutable string. + Parameters: + W: A type conforming to the Writable trait. - Safety: - The returned `Formatter` holds a mutable pointer to this `String` - value. This `String` MUST outlive the `Formatter` instance. + Args: + writer: The object to write to. """ - fn write_to_string(ptr0: OpaquePointer, strref: StringRef): - var ptr: UnsafePointer[String] = ptr0.bitcast[String]() - - # FIXME: - # String.__iadd__ currently only accepts a String, meaning this - # RHS will allocate unnecessarily. - ptr[] += strref - - return Formatter( - write_to_string, - # Arg data - UnsafePointer.address_of(self).bitcast[NoneType](), - ) + writer.write_bytes(self.as_bytes()) fn join(self, *elems: Int) -> String: """Joins the elements from the tuple using the current string as a @@ -1366,7 +1392,7 @@ struct String( curr += self + str(elems[i]) return curr - fn join[*Types: Formattable](self, *elems: *Types) -> String: + fn join[*Types: Writable](self, *elems: *Types) -> String: """Joins string elements using the current string as a delimiter. Parameters: @@ -1379,21 +1405,19 @@ struct String( The joined string. """ - var result: String = "" - var formatter = result._unsafe_to_formatter() + var result = String() var is_first = True @parameter - fn add_elt[T: Formattable](a: T): + fn add_elt[T: Writable](a: T): if is_first: is_first = False else: - self.format_to(formatter) - a.format_to(formatter) + result.write(self) + result.write(a) elems.each[add_elt]() _ = is_first - _ = formatter^ return result fn join[T: StringableCollectionElement](self, elems: List[T, *_]) -> String: @@ -1515,7 +1539,7 @@ struct String( return self.unsafe_ptr().bitcast[c_char]() @always_inline - fn as_bytes(ref [_]self) -> Span[UInt8, __origin_of(self)]: + fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: @@ -1526,7 +1550,7 @@ struct String( """ # Does NOT include the NUL terminator. - return Span[UInt8, __origin_of(self)]( + return Span[Byte, __origin_of(self)]( unsafe_ptr=self._buffer.unsafe_ptr(), len=self.byte_length() ) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 8ef6e9a03b..548ea8ccbd 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -60,7 +60,7 @@ struct UnsafePointer[ CollectionElement, CollectionElementNew, Stringable, - Formattable, + Writable, Intable, Comparable, ): @@ -411,12 +411,15 @@ struct UnsafePointer[ return hex(int(self)) @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this pointer address to the provided formatter. + Formats this pointer address to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ # TODO: Avoid intermediate String allocation. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 2605d23067..ba00c1b447 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -249,13 +249,13 @@ fn abort[result: AnyType = NoneType._mlir_type]() -> result: @no_inline fn abort[ - result: AnyType = NoneType._mlir_type, *, formattable: Formattable -](message: formattable) -> result: + result: AnyType = NoneType._mlir_type, *, W: Writable +](message: W) -> result: """Calls a target dependent trap instruction if available. Parameters: result: The result type. - formattable: The Formattable type. + W: The Writable type. Args: message: The message to include when aborting. diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 7c7f4eb5ba..04ca4b43ae 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -69,7 +69,7 @@ fn _dir_of_current_file_impl(file_name: StringLiteral) raises -> Path: struct Path( Stringable, Boolable, - Formattable, + Writable, CollectionElement, CollectionElementNew, PathLike, @@ -153,12 +153,15 @@ struct Path( """ return self.path.byte_length() > 0 - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this path to the provided formatter. + Formats this path to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write(self.path) diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 60f47ba5c7..7d7badc1a2 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -133,4 +133,4 @@ from collections.string import ( ) from hashlib.hash import hash, Hashable from memory import Pointer, AddressSpace -from utils import AsBytes, Formattable, Formatter +from utils import AsBytes, Writable, Writer diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo index bc583cd616..e5ef4e4547 100644 --- a/stdlib/src/pwd/pwd.mojo +++ b/stdlib/src/pwd/pwd.mojo @@ -39,11 +39,14 @@ struct Passwd(Stringable): var pw_shell: String """Shell program.""" - fn format_to(self, inout writer: Formatter): - """Formats this string to the provided formatter. + fn write_to[W: Writer](self, inout writer: W): + """Formats this string to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("pwd.struct_passwd(pw_name='", self.pw_name) writer.write("', pw_passwd='", self.pw_passwd) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 516320b288..0749e0689b 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -452,7 +452,7 @@ struct PyType_Slot: @value -struct PyObject(Stringable, Representable, Formattable): +struct PyObject(Stringable, Representable, Writable): """ All object types are extensions of this type. This is a type which contains the information Python needs to treat a pointer to an object as an object. In a normal “release” build, it contains only the object’s reference count and a pointer to the corresponding type object. Nothing is actually declared to be a PyObject, but every pointer to a Python object can be cast to a PyObject*. @@ -491,12 +491,15 @@ struct PyObject(Stringable, Representable, Formattable): # Methods # ===-------------------------------------------------------------------===# - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats to the provided formatter. + Formats to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("PyObject(") @@ -509,7 +512,7 @@ struct PyObject(Stringable, Representable, Formattable): # Ref2: https://pyo3.rs/main/doc/pyo3/ffi/struct.pymoduledef_base # Mojo doesn't have macros, so we define it here for ease. # Note: `PyModuleDef_HEAD_INIT` defaults all of its members, see https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L60 -struct PyModuleDef_Base(Stringable, Representable, Formattable): +struct PyModuleDef_Base(Stringable, Representable, Writable): # The initial segment of every `PyObject` in CPython var object_base: PyObject @@ -567,12 +570,15 @@ struct PyModuleDef_Base(Stringable, Representable, Formattable): # Methods # ===-------------------------------------------------------------------===# - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats to the provided formatter. + Formats to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("PyModuleDef_Base(") @@ -593,7 +599,7 @@ struct PyModuleDef_Slot: var value: OpaquePointer -struct PyModuleDef(Stringable, Representable, Formattable): +struct PyModuleDef(Stringable, Representable, Writable): """ The Python module definition structs that holds all of the information needed to create a module. @@ -682,12 +688,15 @@ struct PyModuleDef(Stringable, Representable, Formattable): # Methods # ===-------------------------------------------------------------------===# - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats to the provided formatter. + Formats to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ writer.write("PyModuleDef(") @@ -826,7 +835,7 @@ struct CPython: # ===-------------------------------------------------------------------===# @always_inline - fn log[*Ts: Formattable](self, *args: *Ts): + fn log[*Ts: Writable](self, *args: *Ts): """If logging is enabled, print the given arguments as a log message. Parameters: @@ -842,7 +851,7 @@ struct CPython: # Once Mojo argument splatting is supported, this should just # be: `print(*args)` @parameter - fn print_arg[T: Formattable](arg: T): + fn print_arg[T: Writable](arg: T): print(arg, sep="", end="", flush=False) args.each[print_arg]() diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index d8db12081f..13715b01f4 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -234,7 +234,7 @@ struct PythonObject( KeyElement, SizedRaising, Stringable, - Formattable, + Writable, _HashableWithHasher, ): """A Python object.""" @@ -1515,12 +1515,15 @@ struct PythonObject( _ = python_str return mojo_str - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this Python object to the provided formatter. + Formats this Python object to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ # TODO: Avoid this intermediate String allocation, if possible. diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index f0290277cf..6f86e9308a 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -21,4 +21,4 @@ from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock -from .format import Formatter, Formattable +from .write import Writer, Writable diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 1b7e23be07..5eadeb4788 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -24,7 +24,7 @@ alias _kCompactElemPerSide = _kCompactMaxElemsToPrint // 2 fn _serialize_elements_compact[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, + serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, ](ptr: UnsafePointer[Scalar[type], _], len: Int): serialize_fn(_kStartTensorMarker) if len < _kCompactMaxElemsToPrint: @@ -45,7 +45,7 @@ fn _serialize_elements_compact[ fn _serialize_elements_complete[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, + serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, ](ptr: UnsafePointer[Scalar[type], _], len: Int): if len == 0: return @@ -57,7 +57,7 @@ fn _serialize_elements_complete[ fn _serialize_elements[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, + serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, compact: Bool = False, ](ptr: UnsafePointer[Scalar[type], _], len: Int): @parameter @@ -69,7 +69,7 @@ fn _serialize_elements[ fn _serialize[ type: DType, //, - serialize_fn: fn[T: Formattable] (elem: T) capturing [_] -> None, + serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, serialize_dtype: Bool = True, serialize_shape: Bool = True, serialize_end_line: Bool = True, diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo deleted file mode 100644 index c47de9a83a..0000000000 --- a/stdlib/src/utils/format.mojo +++ /dev/null @@ -1,222 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -"""Implements a formatter abstraction for objects that can format -themselves to a string. -""" - -from builtin.io import _put -from memory import UnsafePointer -from sys.ffi import OpaquePointer - -# ===----------------------------------------------------------------------===# -# Interface traits -# ===----------------------------------------------------------------------===# - - -trait Formattable: - """ - The `Formattable` trait describes a type that can be converted to a stream - of UTF-8 encoded data by writing to a formatter object. - - Examples: - - Implement `Formattable` and `Stringable` for a type: - - ```mojo - struct Point(Stringable, Formattable): - var x: Float64 - var y: Float64 - - fn __str__(self) -> String: - return String.format_sequence(self) - - fn format_to(self, inout writer: Formatter): - writer.write("(", self.x, ", ", self.y, ")") - ``` - """ - - fn format_to(self, inout writer: Formatter): - """ - Formats the string representation of this type to the provided formatter. - - Args: - writer: The formatter to write to. - """ - ... - - -trait ToFormatter: - """ - The `ToFormatter` trait describes a type that can be written to by a - `Formatter` object. - """ - - fn _unsafe_to_formatter(inout self) -> Formatter: - ... - - -# ===----------------------------------------------------------------------===# -# Formatter -# ===----------------------------------------------------------------------===# - - -struct Formatter: - """ - A `Formatter` is used by types implementing the `Formattable` trait to write - bytes to the underlying formatter output buffer or stream. - """ - - # FIXME(#37996): - # This manual implementation of a closure function ptr + closure data - # arg is needed to workaround a bug with `escaping` closure capture values - # seemingly getting clobbered in between when the closure was constructed - # and first called. Once that bug is fixed, this should be replaced with - # an `escaping` closure again. - var _write_func: fn (OpaquePointer, StringRef) -> None - var _write_func_arg: OpaquePointer - """Closure argument passed to `_write_func`.""" - - # ===------------------------------------------------------------------===# - # Initializers - # ===------------------------------------------------------------------===# - - fn __init__[F: ToFormatter](inout self, inout output: F): - """Construct a new `Formatter` from a value implementing `ToFormatter`. - - Parameters: - F: The type that supports being used to back a `Formatter`. - - Args: - output: Value to accumulate or process output streamed to the `Formatter`. - """ - self = output._unsafe_to_formatter() - - fn __init__(inout self, *, fd: FileDescriptor): - """ - Constructs a `Formatter` that writes to the given file descriptor. - - Args: - fd: The file descriptor to write to. - """ - - @always_inline - fn write_to_fd(ptr: OpaquePointer, strref: StringRef): - var fd0 = ptr.bitcast[FileDescriptor]()[].value - - _put(strref, file=fd0) - - self = Formatter( - write_to_fd, - UnsafePointer.address_of(fd).bitcast[NoneType](), - ) - - fn __init__( - inout self, - func: fn (OpaquePointer, StringRef) -> None, - arg: OpaquePointer, - ): - """Constructs a formatter from any closure that accepts `StringRef`s. - - This function should only be used by low-level types that wish to - accept streamed formatted data. - - Args: - func: Raw closure function pointer. - arg: Opaque user data argument that is passed to the closure function pointer. - """ - self._write_func = func - self._write_func_arg = arg - - fn __moveinit__(inout self, owned other: Self): - """Move this value. - - Args: - other: The value to move. - """ - self._write_func = other._write_func - self._write_func_arg = other._write_func_arg - - # ===------------------------------------------------------------------=== # - # Methods - # ===------------------------------------------------------------------=== # - - # TODO: Constrain to only require an immutable StringSlice[..]` - @always_inline - fn write_str(inout self, str_slice: StringSlice[_]): - """ - Write a string slice to this formatter. - - Args: - str_slice: The string slice to write to this formatter. Must NOT be - null terminated. - """ - - # SAFETY: - # Safe because `str_slice` is a `borrowed` arg, and so alive at least - # as long as this call. - var strref: StringRef = str_slice._strref_dangerous() - - self._write_func(self._write_func_arg, strref) - - fn write[*Ts: Formattable](inout self: Formatter, *args: *Ts): - """Write a sequence of formattable arguments to the provided formatter. - - Parameters: - Ts: Types of the provided argument sequence. - - Args: - args: Sequence of arguments to write to this formatter. - """ - - @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(self) - - args.each[write_arg]() - - fn _write_int_padded(inout self, value: Int, *, width: Int): - var int_width = value._decimal_digit_count() - - # TODO: Assumes user wants right-aligned content. - if int_width < width: - self._write_repeated( - " ".as_string_slice(), - width - int_width, - ) - - self.write(value) - - fn _write_repeated(inout self, str: StringSlice, count: Int): - for _ in range(count): - self.write_str(str) - - # ===------------------------------------------------------------------=== # - # Factory methods - # ===------------------------------------------------------------------=== # - - @always_inline - @staticmethod - fn stdout() -> Self: - """ - Constructs a formatter that writes directly to stdout. - - Returns: - A formatter that writes provided data to the operating system - standard output stream. - """ - - @always_inline - fn write_to_stdout(_data: OpaquePointer, strref: StringRef): - _put(strref) - - return Formatter(write_to_stdout, OpaquePointer()) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index e01f68b1a2..054f2a85bd 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -174,7 +174,7 @@ struct IndexList[ ]( Sized, Stringable, - Formattable, + Writable, Comparable, ): """A base struct that implements size agnostic index functions. @@ -738,12 +738,15 @@ struct IndexList[ return buf^ @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this int tuple to the provided formatter. + Formats this int tuple to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ # TODO: Optimize this to avoid the intermediate String allocation. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 647264f664..b047b64b07 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -24,7 +24,6 @@ from sys.ffi import OpaquePointer from memory import UnsafePointer, memcpy from utils import StringSlice, Variant -from utils.format import ToFormatter # ===----------------------------------------------------------------------===# # InlineString @@ -282,7 +281,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref [_]self: Self) -> Span[UInt8, __origin_of(self)]: + fn as_bytes(ref [_]self: Self) -> Span[Byte, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -292,7 +291,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, __origin_of(self)]( + return Span[Byte, __origin_of(self)]( unsafe_ptr=self.unsafe_ptr(), # Does NOT include the NUL terminator. len=len(self), @@ -308,8 +307,8 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): struct _FixedString[CAP: Int]( Sized, Stringable, - Formattable, - ToFormatter, + Writable, + Writer, CollectionElement, CollectionElementNew, ): @@ -369,27 +368,26 @@ struct _FixedString[CAP: Int]( # ===------------------------------------------------------------------=== # @staticmethod - fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + fn format_sequence[*Ts: Writable](*args: *Ts) -> Self: """ - Construct a string by concatenating a sequence of formattable arguments. + Construct a string by concatenating a sequence of Writable arguments. Args: - args: A sequence of formattable arguments. + args: A sequence of Writable arguments. Parameters: Ts: The types of the arguments to format. Each type must be satisfy - `Formattable`. + `Writable`. Returns: A string formed by formatting the argument sequence. """ var output = Self() - var writer = output._unsafe_to_formatter() @parameter - fn write_arg[T: Formattable](arg: T): - arg.format_to(writer) + fn write_arg[T: Writable](arg: T): + arg.write_to(output) args.each[write_arg]() @@ -422,7 +420,7 @@ struct _FixedString[CAP: Int]( Args: str_slice: The string to append. """ - var err = self._iadd_non_raising(str_slice) + var err = self._iadd_non_raising(str_slice._slice) if err: raise err.value() @@ -443,16 +441,16 @@ struct _FixedString[CAP: Int]( fn _iadd_non_raising( inout self, - str_slice: StringSlice[_], + bytes: Span[Byte, _], ) -> Optional[Error]: - var total_len = len(self) + str_slice.byte_length() + var total_len = len(self) + len(bytes) # Ensure there is sufficient capacity to append `str_slice` if total_len > CAP: return Optional( Error( "Insufficient capacity to append len=" - + str(str_slice.byte_length()) + + str(len(bytes)) + " string to len=" + str(len(self)) + " FixedString with capacity=" @@ -463,41 +461,43 @@ struct _FixedString[CAP: Int]( # Append the bytes from `str_slice` at the end of the current string memcpy( dest=self.buffer.unsafe_ptr() + len(self), - src=str_slice.unsafe_ptr(), - count=str_slice.byte_length(), + src=bytes.unsafe_ptr(), + count=len(bytes), ) self.size = total_len return None - fn format_to(self, inout writer: Formatter): - writer.write_str(self.as_string_slice()) + fn write_to[W: Writer](self, inout writer: W): + writer.write_bytes(self.as_bytes()) - fn _unsafe_to_formatter(inout self) -> Formatter: - fn write_to_string(ptr0: OpaquePointer, strref: StringRef): - var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() + @always_inline + fn write_bytes(inout self, bytes: Span[Byte, _]): + """ + Write a byte span to this String. - var str_slice = StringSlice[ImmutableAnyOrigin]( - unsafe_from_utf8_strref=strref - ) + Args: + bytes: The byte span to write to this String. Must NOT be + null terminated. + """ + _ = self._iadd_non_raising(bytes) - # FIXME(#37990): - # Use `ptr[] += str_slice` and remove _iadd_non_raising after - # "failed to fold operation lit.try" is fixed. - # try: - # ptr[] += str_slice - # except e: - # abort("error formatting to FixedString: " + str(e)) - var err = ptr[]._iadd_non_raising(str_slice) - if err: - abort("error formatting to FixedString: " + str(err.value())) - - return Formatter( - write_to_string, - # Arg data - UnsafePointer.address_of(self).bitcast[NoneType](), - ) + fn write[*Ts: Writable](inout self, *args: *Ts): + """Write a sequence of Writable arguments to the provided Writer. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this Writer. + """ + + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. @@ -521,7 +521,7 @@ struct _FixedString[CAP: Int]( return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref [_]self: Self) -> Span[UInt8, __origin_of(self)]: + fn as_bytes(ref [_]self: Self) -> Span[Byte, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -531,7 +531,7 @@ struct _FixedString[CAP: Int]( A contiguous slice pointing to the bytes owned by this string. """ - return Span[UInt8, __origin_of(self)]( + return Span[Byte, __origin_of(self)]( unsafe_ptr=self.unsafe_ptr(), # Does NOT include the NUL terminator. len=self.size, diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 24ca92b2a7..305b4d1381 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -31,7 +31,7 @@ trait AsBytes: span. """ - fn as_bytes(ref [_]self) -> Span[UInt8, __origin_of(self)]: + fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 9855f99de7..84a8dc6aa6 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -31,7 +31,7 @@ alias StaticString = StringSlice[StaticConstantOrigin] """An immutable static string slice.""" -fn _count_utf8_continuation_bytes(span: Span[UInt8]) -> Int: +fn _count_utf8_continuation_bytes(span: Span[Byte]) -> Int: alias sizes = (256, 128, 64, 32, 16, 8) var ptr = span.unsafe_ptr() var num_bytes = len(span) @@ -173,7 +173,7 @@ struct _StringSliceIter[ self.index = 0 if forward else length self.ptr = unsafe_pointer self.length = length - alias S = Span[UInt8, StaticConstantOrigin] + alias S = Span[Byte, StaticConstantOrigin] var s = S(unsafe_ptr=self.ptr, len=self.length) self.continuation_bytes = _count_utf8_continuation_bytes(s) @@ -225,8 +225,9 @@ struct _StringSliceIter[ struct StringSlice[ is_mutable: Bool, //, origin: Origin[is_mutable].type, -](Stringable, Sized, Formattable, CollectionElement, CollectionElementNew): - """A non-owning view to encoded string data. +](Stringable, Sized, Writable, CollectionElement, CollectionElementNew): + """ + A non-owning view to encoded string data. TODO: The underlying string data is guaranteed to be encoded using UTF-8. @@ -236,7 +237,7 @@ struct StringSlice[ origin: The origin of the underlying string data. """ - var _slice: Span[UInt8, origin] + var _slice: Span[Byte, origin] # ===------------------------------------------------------------------===# # Initializers @@ -268,7 +269,7 @@ struct StringSlice[ ) @always_inline - fn __init__(inout self, *, owned unsafe_from_utf8: Span[UInt8, origin]): + fn __init__(inout self, *, owned unsafe_from_utf8: Span[Byte, origin]): """Construct a new StringSlice from a sequence of UTF-8 encoded bytes. Safety: @@ -294,7 +295,7 @@ struct StringSlice[ """ var strref = unsafe_from_utf8_strref - var byte_slice = Span[UInt8, origin]( + var byte_slice = Span[Byte, origin]( unsafe_ptr=strref.unsafe_ptr(), len=len(strref), ) @@ -322,7 +323,7 @@ struct StringSlice[ UTF-8. len: The number of bytes of encoded data. """ - var byte_slice = Span[UInt8, origin]( + var byte_slice = Span[Byte, origin]( unsafe_ptr=unsafe_from_utf8_ptr, len=len, ) @@ -358,18 +359,21 @@ struct StringSlice[ The length in Unicode codepoints. """ var b_len = self.byte_length() - alias S = Span[UInt8, StaticConstantOrigin] + alias S = Span[Byte, StaticConstantOrigin] var s = S(unsafe_ptr=self.unsafe_ptr(), len=b_len) return b_len - _count_utf8_continuation_bytes(s) - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this string slice to the provided formatter. + Formats this string slice to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ - writer.write_str(str_slice=self) + writer.write_bytes(self.as_bytes()) fn __bool__(self) -> Bool: """Check if a string slice is non-empty. @@ -517,7 +521,7 @@ struct StringSlice[ # ===------------------------------------------------------------------===# @always_inline - fn as_bytes(self) -> Span[UInt8, origin]: + fn as_bytes(self) -> Span[Byte, origin]: """Get the sequence of encoded bytes of the underlying string. Returns: @@ -1095,19 +1099,19 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): var data: String if empty and type_impls_formatter_str: - data = str(args[i]) # TODO: use formatter and return + data = str(args[i]) # TODO: use writer and return elif empty and type_impls_str: data = str(args[i]) elif flag == `s` and type_impls_formatter_str: if empty: - # TODO: use formatter and return + # TODO: use writer and return pass data = str(args[i]) elif flag == `s` and type_impls_str: data = str(args[i]) elif flag == `r` and type_impls_formatter_repr: if empty: - # TODO: use formatter and return + # TODO: use writer and return pass data = repr(args[i]) elif flag == `r` and type_impls_repr: @@ -1217,8 +1221,8 @@ struct _FormatSpec: precision is not allowed for integer presentation types. """ var type: UInt8 - """Determines how the data should be presented. - + """Determines how the data should be presented. + The available integer presentation types are: | Option | Meaning| @@ -1240,7 +1244,7 @@ struct _FormatSpec: In addition to the above presentation types, integers can be formatted with the floating-point presentation types listed below (except 'n' and None). When doing so, float() is used to convert the integer to a floating-point - number before formatting. + number before formatting. The available presentation types for float and Decimal values are: diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 65a4e67602..c8aab8c5f0 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -46,11 +46,12 @@ struct StringRef( CollectionElement, CollectionElementNew, Stringable, - Formattable, + Writable, Hashable, _HashableWithHasher, Boolable, Comparable, + AsBytes, ): """ Represent a constant reference to a string, i.e. a sequence of characters @@ -211,6 +212,16 @@ struct StringRef( return StringRef() return Self(self.data, self.length - num_bytes) + fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: + """Returns a contiguous Span of the bytes owned by this string. + + Returns: + A contiguous slice pointing to the bytes owned by this string. + """ + return Span[Byte, __origin_of(self)]( + unsafe_ptr=self.data, len=self.length + ) + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# @@ -385,22 +396,24 @@ struct StringRef( return String.format_sequence(self) @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): """ - Formats this StringRef to the provided formatter. + Formats this StringRef to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. Args: - writer: The formatter to write to. + writer: The object to write to. """ - # SAFETY: - # Safe because our use of this StringSlice does not outlive `self`. - var str_slice = StringSlice[ImmutableAnyOrigin]( - unsafe_from_utf8_strref=self + # Safe because our use of this Span does not outlive `self`. + writer.write_bytes( + Span[Byte, ImmutableAnyOrigin]( + unsafe_ptr=self.unsafe_ptr(), len=len(self) + ) ) - writer.write_str(str_slice) - fn __fspath__(self) -> String: """Return the file system path representation of the object. diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo new file mode 100644 index 0000000000..5d495f1b50 --- /dev/null +++ b/stdlib/src/utils/write.mojo @@ -0,0 +1,148 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Establishes the contract between `Writer` and `Writable` types.""" + +# ===----------------------------------------------------------------------===# +# Writer +# ===----------------------------------------------------------------------===# + + +trait Writer: + """Describes a type that can be written to by any type that implements the + `write_to` function. + + This enables you to write one implementation that can be written to a + variety of types such as file descriptors, strings, network locations etc. + The types are written as a `Span[Byte]`, so the `Writer` can avoid + allocations depending on the requirements. There is also a general `write` + that takes multiple args that implement `write_to`. + + Example: + + ```mojo + from utils import Span + + @value + struct NewString(Writer): + var s: String + + # Enable a type to write its data members as a `Byte[Span]` + fn write_bytes(inout self, bytes: Span[Byte, _]): + # If your Writer needs to store the number of bytes being written, + # you can use e.g. `self.bytes_written += len(str_slice)` here. + self.s._iadd[False](bytes) + + # Enable passing multiple args that implement `write_to`, which + # themselves may have calls to `write` with variadic args: + fn write[*Ts: Writable](inout self, *args: *Ts): + # Loop through the args, running all their `write_to` functions: + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + args.each[write_arg]() + + @value + struct Point(Writable): + var x: Int + var y: Int + + fn write_to[W: Writer](self, inout writer: W): + # Write a single `Span[Byte]`: + var string = "Point" + writer.write_bytes(string.as_bytes()) + + # Write the Ints and StringLiterals in a single call, they also + # implement `write_to`, which implements how to write themselves as + # a `Byte[Span]`: + writer.write("(", self.x, ", ", self.y, ")") + + var output = NewString(String()) + var point = Point(2, 4) + + output.write(point) + print(output.s) + ``` + + Output: + + ```plaintext + Point(2, 4) + ``` + """ + + @always_inline + fn write_bytes(inout self, bytes: Span[Byte, _]): + """ + Write a `Span[Byte]` to this `Writer`. + + Args: + bytes: The string slice to write to this Writer. Must NOT be + null-terminated. + """ + ... + + fn write[*Ts: Writable](inout self, *args: *Ts): + """Write a sequence of Writable arguments to the provided Writer. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this Writer. + """ + ... + # TODO: When have default implementations on traits, we can use this: + # @parameter + # fn write_arg[W: Writable](arg: W): + # arg.write_to(self) + # args.each[write_arg]() + # + # To only have to implement `write_str` to make a type a valid Writer + + +# ===----------------------------------------------------------------------===# +# Writable +# ===----------------------------------------------------------------------===# + + +trait Writable: + """The `Writable` trait describes how a type is written into a `Writer`. + + You must implement `write_to` which takes `self` and a type conforming to + `Writer`: + + ```mojo + struct Point(Writable): + var x: Float64 + var y: Float64 + + fn write_to[W: Writer](self, inout writer: W): + var string = "Point" + # Write a single `Span[Byte]`: + writer.write_bytes(string.as_bytes()) + # Pass multiple args that can be converted to a `Span[Byte]`: + writer.write("(", self.x, ", ", self.y, ")") + ``` + """ + + fn write_to[W: Writer](self, inout writer: W): + """ + Formats the string representation of this type to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. + + Args: + writer: The type conforming to `Writable`. + """ + ... diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index d8f6f5603f..9df1b1e1f3 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -20,7 +20,7 @@ def main(): test_debug_assert() test_debug_assert_multiple_args() - test_debug_assert_formattable() + test_debug_assert_writable() # CHECK-OK-LABEL: test_debug_assert @@ -40,17 +40,17 @@ def test_debug_assert_multiple_args(): print("is reached") -# CHECK-OK-LABEL: test_debug_assert_formattable -def test_debug_assert_formattable(): - print("== test_debug_assert_formattable") - debug_assert(True, FormattableOnly("failed with Formattable arg")) +# CHECK-OK-LABEL: test_debug_assert_writable +def test_debug_assert_writable(): + print("== test_debug_assert_writable") + debug_assert(True, WritableOnly("failed with Writable arg")) # CHECK-OK: is reached print("is reached") @value -struct FormattableOnly: +struct WritableOnly: var message: String - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): writer.write(self.message) diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 513f12377f..6cfb32a871 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -14,27 +14,27 @@ from testing import assert_equal -from utils import Formattable, Formatter +from utils import Writable, Writer from utils.inline_string import _FixedString fn main() raises: - test_formatter_of_string() + test_writer_of_string() test_string_format_seq() test_stringable_based_on_format() - test_formatter_of_fixed_string() + test_writer_of_fixed_string() - test_formatter_write_int_padded() + test_write_int_padded() @value -struct Point(Formattable, Stringable): +struct Point(Writable, Stringable): var x: Int var y: Int @no_inline - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): writer.write("Point(", self.x, ", ", self.y, ")") @no_inline @@ -42,21 +42,19 @@ struct Point(Formattable, Stringable): return String.format_sequence(self) -fn test_formatter_of_string() raises: +fn test_writer_of_string() raises: # - # Test format_to(String) + # Test write_to(String) # var s1 = String() - var s1_fmt = Formatter(s1) - Point(2, 7).format_to(s1_fmt) + Point(2, 7).write_to(s1) assert_equal(s1, "Point(2, 7)") # - # Test fmt.write(String, ..) + # Test writer.write(String, ..) # var s2 = String() - var s2_fmt = Formatter(s2) - s2_fmt.write(Point(3, 8)) + s2.write(Point(3, 8)) assert_equal(s2, "Point(3, 8)") @@ -75,22 +73,20 @@ fn test_stringable_based_on_format() raises: assert_equal(str(Point(10, 11)), "Point(10, 11)") -fn test_formatter_of_fixed_string() raises: +fn test_writer_of_fixed_string() raises: var s1 = _FixedString[100]() - var s1_fmt = Formatter(s1) - s1_fmt.write("Hello, World!") + s1.write("Hello, World!") assert_equal(str(s1), "Hello, World!") -fn test_formatter_write_int_padded() raises: +fn test_write_int_padded() raises: var s1 = String() - var s1_fmt = Formatter(s1) - s1_fmt._write_int_padded(5, width=5) + Int(5).write_padded(s1, width=5) assert_equal(s1, " 5") - s1_fmt._write_int_padded(123, width=5) + Int(123).write_padded(s1, width=5) assert_equal(s1, " 5 123") @@ -99,8 +95,7 @@ fn test_formatter_write_int_padded() raises: # ---------------------------------- var s2 = String() - var s2_fmt = Formatter(s2) - s2_fmt._write_int_padded(12345, width=3) + Int(12345).write_padded(s2, width=3) assert_equal(s2, "12345") diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index f132356755..256146aeff 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,7 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils import Formattable, Formatter +from utils import Writable, Writer +import sys fn main() raises: @@ -20,11 +21,11 @@ fn main() raises: @value -struct Point(Formattable): +struct Point(Writable): var x: Int var y: Int - fn format_to(self, inout writer: Formatter): + fn write_to[W: Writer](self, inout writer: W): writer.write("Point(", self.x, ", ", self.y, ")") @@ -32,7 +33,7 @@ struct Point(Formattable): fn test_write_to_stdout(): print("== test_write_to_stdout") - var stdout = Formatter.stdout() + var stdout = sys.stdout # CHECK: Hello, World! stdout.write("Hello, World!") diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index b753463662..21791d7604 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -397,7 +397,7 @@ def test_count_utf8_continuation_bytes(): def _test(amnt: Int, items: List[UInt8]): p = items.unsafe_ptr() - span = Span[UInt8, StaticConstantOrigin](unsafe_ptr=p, len=len(items)) + span = Span[Byte, StaticConstantOrigin](unsafe_ptr=p, len=len(items)) assert_equal(amnt, _count_utf8_continuation_bytes(span)) _test(5, List[UInt8](c, c, c, c, c)) From d49f5c075c7abca1e7535de34ba0b1d7191f9898 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 17 Oct 2024 20:03:50 -0700 Subject: [PATCH 1777/2019] [stdlib] Change `format_sequence` to `write` Change `format_sequence` to align with naming of other methods and traits, can now use `write` to construct a type, or write into an existing one. MODULAR_ORIG_COMMIT_REV_ID: bdba2af6155f806c5cd28fd0514d0b02a8d7b784 --- stdlib/src/builtin/_location.mojo | 2 +- stdlib/src/builtin/bool.mojo | 2 +- stdlib/src/builtin/debug_assert.mojo | 2 +- stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/object.mojo | 4 ++-- stdlib/src/builtin/simd.mojo | 4 ++-- stdlib/src/builtin/uint.mojo | 2 +- stdlib/src/collections/string.mojo | 10 +++++----- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/pwd/pwd.mojo | 4 ++-- stdlib/src/python/_cpython.mojo | 6 +++--- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/builtin/test_none.mojo | 2 +- stdlib/test/utils/test_format.mojo | 8 ++++---- 17 files changed, 29 insertions(+), 29 deletions(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 484c011aa2..a79adfc33d 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -28,7 +28,7 @@ struct _SourceLocation(Writable, Stringable): @no_inline fn __str__(self) -> String: - return String.format_sequence(self) + return String.write(self) @no_inline fn prefix[T: Stringable](self, msg: T) -> String: diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 13c167eb3a..174aa10b7d 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -220,7 +220,7 @@ struct Bool( Returns: A string representation. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn write_to[W: Writer](self, inout writer: W): diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 0dc5c80655..da3567731a 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -307,7 +307,7 @@ fn _debug_assert_msg( ) else: - message = String.format_sequence(messages) + message = String.write(messages) @parameter if defined_mode == "warn": diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 3fed068b3b..1391914b55 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -157,7 +157,7 @@ struct DType( The name of the dtype. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn write_to[W: Writer](self, inout writer: W): diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 89367a8809..e52c7dd804 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -156,7 +156,7 @@ struct Error( Returns: A String of the error message. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn write_to[W: Writer](self, inout writer: W): diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 0c9ea6b275..a4ba643759 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1092,7 +1092,7 @@ struct Int( A string representation. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn __repr__(self) -> String: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 144a822edc..f7587a8ac6 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -639,7 +639,7 @@ struct _ObjectImpl( Returns: The String representation of the object. """ - return String.format_sequence(self) + return String.write(self) # ===------------------------------------------------------------------=== # # List Functions @@ -992,7 +992,7 @@ struct object( Returns: The String representation of the object. """ - return String.format_sequence(self._value) + return String.write(self._value) @no_inline fn __repr__(self) -> String: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b2ab25b9e9..e8eafc1525 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1423,7 +1423,7 @@ struct SIMD[type: DType, size: Int]( A string representation. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn __repr__(self) -> String: @@ -1639,7 +1639,7 @@ struct SIMD[type: DType, size: Int]( self.write_to[use_scientific_notation=False](writer) # This overload is required to keep SIMD compliant with the Writable - # trait, and the call to `String.format_sequence(self)` in SIMD.__str__ will + # trait, and the call to `String.write(self)` in SIMD.__str__ will # fail to compile. @no_inline fn write_to[ diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index eb99e27225..1dac5cad1e 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -121,7 +121,7 @@ struct UInt(IntLike, _HashableWithHasher): Returns: The string representation of this UInt. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn write_to[W: Writer](self, inout writer: W): diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 9b3a34630b..156ea685b8 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -868,7 +868,7 @@ struct String( @staticmethod @no_inline - fn format_sequence[ + fn write[ *Ts: Writable ](*args: *Ts, sep: StaticString = "", end: StaticString = "") -> Self: """ @@ -891,7 +891,7 @@ struct String( Construct a String from several `Writable` arguments: ```mojo - var string = String.format_sequence(1, ", ", 2.0, ", ", "three") + var string = String.write(1, ", ", 2.0, ", ", "three") print(string) # "1, 2.0, three" %# from testing import assert_equal %# assert_equal(string, "1, 2.0, three") @@ -899,11 +899,11 @@ struct String( . """ - return Self.format_sequence(args) + return Self.write(args) @staticmethod @no_inline - fn format_sequence[ + fn write[ *Ts: Writable ]( args: VariadicPack[_, Writable, *Ts], @@ -931,7 +931,7 @@ struct String( fn variadic_pack_to_string[ *Ts: Writable, ](*args: *Ts) -> String: - return String.format_sequence(args) + return String.write(args) string = variadic_pack_to_string(1, ", ", 2.0, ", ", "three") %# from testing import assert_equal diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 04ca4b43ae..419bc93b1f 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -142,7 +142,7 @@ struct Path( Returns: A string representation of the path. """ - return String.format_sequence(self) + return String.write(self) @always_inline fn __bool__(self) -> Bool: diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo index e5ef4e4547..0bc26698d1 100644 --- a/stdlib/src/pwd/pwd.mojo +++ b/stdlib/src/pwd/pwd.mojo @@ -64,7 +64,7 @@ struct Passwd(Stringable): Returns: A compact string of the Passwd struct. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn __repr__(self) -> String: @@ -73,7 +73,7 @@ struct Passwd(Stringable): Returns: A compact string representation of Passwd struct. """ - return String.format_sequence(self) + return String.write(self) fn getpwuid(uid: Int) raises -> Passwd: diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 0749e0689b..00c5787b66 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -476,7 +476,7 @@ struct PyObject(Stringable, Representable, Writable): A string representation. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn __repr__(self) -> String: @@ -555,7 +555,7 @@ struct PyModuleDef_Base(Stringable, Representable, Writable): A string representation. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn __repr__(self) -> String: @@ -673,7 +673,7 @@ struct PyModuleDef(Stringable, Representable, Writable): A string representation. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn __repr__(self) -> String: diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index b047b64b07..3552122489 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -368,7 +368,7 @@ struct _FixedString[CAP: Int]( # ===------------------------------------------------------------------=== # @staticmethod - fn format_sequence[*Ts: Writable](*args: *Ts) -> Self: + fn write[*Ts: Writable](*args: *Ts) -> Self: """ Construct a string by concatenating a sequence of Writable arguments. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index c8aab8c5f0..1bd7cb9dbe 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -393,7 +393,7 @@ struct StringRef( Returns: A new string. """ - return String.format_sequence(self) + return String.write(self) @no_inline fn write_to[W: Writer](self, inout writer: W): diff --git a/stdlib/test/builtin/test_none.mojo b/stdlib/test/builtin/test_none.mojo index 338bd2c386..028471881c 100644 --- a/stdlib/test/builtin/test_none.mojo +++ b/stdlib/test/builtin/test_none.mojo @@ -31,7 +31,7 @@ def test_repr(): def test_format_to(): - assert_equal(String.format_sequence(NoneType()), "None") + assert_equal(String.write(NoneType()), "None") struct FromNone: diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 6cfb32a871..e15d744a88 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -39,7 +39,7 @@ struct Point(Writable, Stringable): @no_inline fn __str__(self) -> String: - return String.format_sequence(self) + return String.write(self) fn test_writer_of_string() raises: @@ -59,13 +59,13 @@ fn test_writer_of_string() raises: fn test_string_format_seq() raises: - var s1 = String.format_sequence("Hello, ", "World!") + var s1 = String.write("Hello, ", "World!") assert_equal(s1, "Hello, World!") - var s2 = String.format_sequence("point = ", Point(2, 7)) + var s2 = String.write("point = ", Point(2, 7)) assert_equal(s2, "point = Point(2, 7)") - var s3 = String.format_sequence() + var s3 = String.write() assert_equal(s3, "") From 4afa8474ad66deca819cb4c2c55e0892ba60cec5 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 18 Oct 2024 08:22:23 -0700 Subject: [PATCH 1778/2019] [stdlib] Removing _strref_dangerous from String - Replacing `_strref_dangerous` with `as_string_slice`. - Replacing `StringRef` overloads in String with `StringSlice`. - Enabled `StringSlice` to support the `Hashable` trait. - `StringSlice` implements less-than comparable. - `StringSlice` implements `__getitem__` enabling subscript expression support. - Implement the following `StringSlice` functions: strip, startswith, endswith, find, and find. MODULAR_ORIG_COMMIT_REV_ID: 80fc97fe0b5efe3ccb9183426084f1491ed2c9f6 --- stdlib/src/collections/string.mojo | 56 ++- stdlib/src/utils/string_slice.mojo | 208 +++++++- stdlib/src/utils/stringref.mojo | 5 +- stdlib/test/collections/test_string.mojo | 8 + stdlib/test/hashlib/test_ahash.mojo | 597 ++++++++++++----------- 5 files changed, 542 insertions(+), 332 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 156ea685b8..2071947411 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -227,7 +227,7 @@ fn ascii(value: String) -> String: # ===----------------------------------------------------------------------=== # -fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: +fn _atol(str_ref: StringSlice[_], base: Int = 10) raises -> Int: """Implementation of `atol` for StringRef inputs. Please see its docstring for details. @@ -354,7 +354,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: return result -fn _atol_error(base: Int, str_ref: StringRef) -> String: +fn _atol_error(base: Int, str_ref: StringSlice[_]) -> String: return ( "String is not convertible to integer with base " + str(base) @@ -364,7 +364,7 @@ fn _atol_error(base: Int, str_ref: StringRef) -> String: ) -fn _identify_base(str_ref: StringRef, start: Int) -> Tuple[Int, Int]: +fn _identify_base(str_ref: StringSlice[_], start: Int) -> Tuple[Int, Int]: var length = len(str_ref) # just 1 digit, assume base 10 if start == (length - 1): @@ -415,14 +415,14 @@ fn atol(str: String, base: Int = 10) raises -> Int: Returns: An integer value that represents the string, or otherwise raises. """ - return _atol(str._strref_dangerous(), base) + return _atol(str.as_string_slice(), base) -fn _atof_error(str_ref: StringRef) -> Error: +fn _atof_error(str_ref: StringSlice[_]) -> Error: return Error("String is not convertible to float: '" + str(str_ref) + "'") -fn _atof(str_ref: StringRef) raises -> Float64: +fn _atof(str_ref: StringSlice[_]) raises -> Float64: """Implementation of `atof` for StringRef inputs. Please see its docstring for details. @@ -532,7 +532,7 @@ fn atof(str: String) raises -> Float64: Returns: An floating point value that represents the string, or otherwise raises. """ - return _atof(str._strref_dangerous()) + return _atof(str.as_string_slice()) # ===----------------------------------------------------------------------=== # @@ -1073,7 +1073,7 @@ struct String( True if this String is strictly less than the RHS String and False otherwise. """ - return self._strref_dangerous() < rhs._strref_dangerous() + return self.as_string_slice() < rhs.as_string_slice() @always_inline fn __le__(self, rhs: String) -> Bool: @@ -1630,7 +1630,7 @@ struct String( Returns: True if the string contains the substring. """ - return substr._strref_dangerous() in self._strref_dangerous() + return substr.as_string_slice() in self.as_string_slice() fn find(self, substr: String, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at @@ -1658,8 +1658,8 @@ struct String( The offset of `substr` relative to the beginning of the string. """ - return self._strref_dangerous().rfind( - substr._strref_dangerous(), start=start + return self.as_string_slice().rfind( + substr.as_string_slice(), start=start ) fn isspace(self) -> Bool: @@ -1969,7 +1969,7 @@ struct String( uses. Its intended usage is for data structures. See the `hash` builtin documentation for more details. """ - return hash(self._strref_dangerous()) + return hash(self.as_string_slice()) fn __hash__[H: _Hasher](self, inout hasher: H): """Updates hasher with the underlying bytes. @@ -2033,7 +2033,9 @@ struct String( return copy - fn startswith(self, prefix: String, start: Int = 0, end: Int = -1) -> Bool: + fn startswith( + ref [_]self, prefix: String, start: Int = 0, end: Int = -1 + ) -> Bool: """Checks if the string starts with the specified prefix between start and end positions. Returns True if found and False otherwise. @@ -2046,13 +2048,14 @@ struct String( True if the self[start:end] is prefixed by the input prefix. """ if end == -1: - return StringRef( - self.unsafe_ptr() + start, self.byte_length() - start - ).startswith(prefix._strref_dangerous()) + return StringSlice[__origin_of(self)]( + unsafe_from_utf8_ptr=self.unsafe_ptr() + start, + len=self.byte_length() - start, + ).startswith(prefix.as_string_slice()) - return StringRef(self.unsafe_ptr() + start, end - start).startswith( - prefix._strref_dangerous() - ) + return StringSlice[__origin_of(self)]( + unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ).startswith(prefix.as_string_slice()) fn endswith(self, suffix: String, start: Int = 0, end: Int = -1) -> Bool: """Checks if the string end with the specified suffix between start @@ -2067,13 +2070,14 @@ struct String( True if the self[start:end] is suffixed by the input suffix. """ if end == -1: - return StringRef( - self.unsafe_ptr() + start, self.byte_length() - start - ).endswith(suffix._strref_dangerous()) - - return StringRef(self.unsafe_ptr() + start, end - start).endswith( - suffix._strref_dangerous() - ) + return StringSlice[__origin_of(self)]( + unsafe_from_utf8_ptr=self.unsafe_ptr() + start, + len=self.byte_length() - start, + ).endswith(suffix.as_string_slice()) + + return StringSlice[__origin_of(self)]( + unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ).endswith(suffix.as_string_slice()) fn removeprefix(self, prefix: String, /) -> String: """Returns a new string with the prefix removed if it was present. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 84a8dc6aa6..c7fbd8a76c 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -26,6 +26,7 @@ from collections.string import _isspace, _atol, _atof from collections import List, Optional from memory import memcmp, UnsafePointer from sys import simdwidthof, bitwidthof +from memory.memory import _memcmp_impl_unconstrained alias StaticString = StringSlice[StaticConstantOrigin] """An immutable static string slice.""" @@ -110,6 +111,43 @@ fn _utf8_byte_type(b: SIMD[DType.uint8, _], /) -> __type_of(b): return count_leading_zeros(~(b & UInt8(0b1111_0000))) +@always_inline +fn _memrchr[ + type: DType +]( + source: UnsafePointer[Scalar[type]], char: Scalar[type], len: Int +) -> UnsafePointer[Scalar[type]]: + if not len: + return UnsafePointer[Scalar[type]]() + for i in reversed(range(len)): + if source[i] == char: + return source + i + return UnsafePointer[Scalar[type]]() + + +@always_inline +fn _memrmem[ + type: DType +]( + haystack: UnsafePointer[Scalar[type]], + haystack_len: Int, + needle: UnsafePointer[Scalar[type]], + needle_len: Int, +) -> UnsafePointer[Scalar[type]]: + if not needle_len: + return haystack + if needle_len > haystack_len: + return UnsafePointer[Scalar[type]]() + if needle_len == 1: + return _memrchr[type](haystack, needle[0], haystack_len) + for i in reversed(range(haystack_len - needle_len + 1)): + if haystack[i] != needle[0]: + continue + if memcmp(haystack + i + 1, needle + 1, needle_len - 1) == 0: + return haystack + i + return UnsafePointer[Scalar[type]]() + + fn _is_newline_start( ptr: UnsafePointer[UInt8], read_ahead: Int = 1 ) -> (Bool, Int): @@ -222,10 +260,14 @@ struct _StringSliceIter[ @value -struct StringSlice[ - is_mutable: Bool, //, - origin: Origin[is_mutable].type, -](Stringable, Sized, Writable, CollectionElement, CollectionElementNew): +struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( + Stringable, + Sized, + Writable, + CollectionElement, + CollectionElementNew, + Hashable, +): """ A non-owning view to encoded string data. @@ -383,6 +425,16 @@ struct StringSlice[ """ return len(self._slice) > 0 + fn __hash__(self) -> UInt: + """Hash the underlying buffer using builtin hash. + + Returns: + A 64-bit hash value. This value is _not_ suitable for cryptographic + uses. Its intended usage is for data structures. See the `hash` + builtin documentation for more details. + """ + return hash(self._slice._data, self._slice._len) + # This decorator informs the compiler that indirect address spaces are not # dereferenced by the method. # TODO: replace with a safe model that checks the body of the method for @@ -476,6 +528,23 @@ struct StringSlice[ """ return not self == rhs + @always_inline + fn __lt__(self, rhs: StringSlice) -> Bool: + """Compare this StringSlice to the RHS using LT comparison. + + Args: + rhs: The other StringSlice to compare against. + + Returns: + True if this string is strictly less than the RHS string and False + otherwise. + """ + var len1 = len(self) + var len2 = len(rhs) + return int(len1 < len2) > _memcmp_impl_unconstrained( + self.unsafe_ptr(), rhs.unsafe_ptr(), min(len1, len2) + ) + fn __iter__(self) -> _StringSliceIter[origin]: """Iterate over the string, returning immutable references. @@ -496,6 +565,35 @@ struct StringSlice[ unsafe_pointer=self.unsafe_ptr(), length=self.byte_length() ) + fn __getitem__[IndexerType: Indexer](self, idx: IndexerType) -> String: + """Gets the character at the specified position. + + Parameters: + IndexerType: The inferred type of an indexer argument. + + Args: + idx: The index value. + + Returns: + A new string containing the character at the specified position. + """ + # TODO(#933): implement this for unicode when we support llvm intrinsic evaluation at compile time + var buf = String._buffer_type(capacity=1) + buf.append(self._slice[idx]) + buf.append(0) + return String(buf^) + + fn __contains__(ref [_]self, substr: StringSlice[_]) -> Bool: + """Returns True if the substring is contained within the current string. + + Args: + substr: The substring to check. + + Returns: + True if the string contains the substring. + """ + return self.find(substr) != -1 + @always_inline fn __int__(self) raises -> Int: """Parses the given string as a base-10 integer and returns that value. @@ -504,7 +602,7 @@ struct StringSlice[ Returns: An integer value that represents the string, or otherwise raises. """ - return _atol(self._strref_dangerous()) + return _atol(self) @always_inline fn __float__(self) raises -> Float64: @@ -514,12 +612,33 @@ struct StringSlice[ Returns: A float value that represents the string, or otherwise raises. """ - return _atof(self._strref_dangerous()) + return _atof(self) # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# + @always_inline + fn strip(self) -> StringSlice[origin]: + """Gets a StringRef with leading and trailing whitespaces removed. + This only takes C spaces into account: " \\t\\n\\r\\f\\v". + + For example, `" mojo "` returns `"mojo"`. + + Returns: + A StringRef with leading and trailing whitespaces removed. + """ + var start: Int = 0 + var end: Int = len(self) + var ptr = self.unsafe_ptr() + while start < end and _isspace(ptr[start]): + start += 1 + while end > start and _isspace(ptr[end - 1]): + end -= 1 + return StringSlice[origin]( + unsafe_from_utf8_ptr=ptr + start, len=end - start + ) + @always_inline fn as_bytes(self) -> Span[Byte, origin]: """Get the sequence of encoded bytes of the underlying string. @@ -567,6 +686,48 @@ struct StringSlice[ """ pass + fn startswith( + self, prefix: StringSlice[_], start: Int = 0, end: Int = -1 + ) -> Bool: + """Checks if the StringRef starts with the specified prefix between start + and end positions. Returns True if found and False otherwise. + + Args: + prefix: The prefix to check. + start: The start offset from which to check. + end: The end offset from which to check. + + Returns: + True if the self[start:end] is prefixed by the input prefix. + """ + if end == -1: + return self.find(prefix, start) == start + return StringSlice[__origin_of(self)]( + unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ).startswith(prefix) + + fn endswith( + self, suffix: StringSlice[_], start: Int = 0, end: Int = -1 + ) -> Bool: + """Checks if the StringRef end with the specified suffix between start + and end positions. Returns True if found and False otherwise. + + Args: + suffix: The suffix to check. + start: The start offset from which to check. + end: The end offset from which to check. + + Returns: + True if the self[start:end] is suffixed by the input suffix. + """ + if len(suffix) > len(self): + return False + if end == -1: + return self.rfind(suffix, start) + len(suffix) == len(self) + return StringSlice[__origin_of(self)]( + unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ).endswith(suffix) + fn _from_start(self, start: Int) -> Self: """Gets the `StringSlice` pointing to the substring after the specified slice start position. @@ -635,7 +796,7 @@ struct StringSlice[ """ return _FormatCurlyEntry.format(self, args) - fn find(self, substr: StringSlice, start: Int = 0) -> Int: + fn find(ref [_]self, substr: StringSlice, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at `start`. If not found, returns -1. @@ -668,6 +829,39 @@ struct StringSlice[ return int(loc) - int(self.unsafe_ptr()) + fn rfind(self, substr: StringSlice, start: Int = 0) -> Int: + """Finds the offset of the last occurrence of `substr` starting at + `start`. If not found, returns -1. + + Args: + substr: The substring to find. + start: The offset from which to find. + + Returns: + The offset of `substr` relative to the beginning of the string. + """ + if not substr: + return len(self) + + if len(self) < len(substr) + start: + return -1 + + # The substring to search within, offset from the beginning if `start` + # is positive, and offset from the end if `start` is negative. + var haystack_str = self._from_start(start) + + var loc = _memrmem( + haystack_str.unsafe_ptr(), + len(haystack_str), + substr.unsafe_ptr(), + len(substr), + ) + + if not loc: + return -1 + + return int(loc) - int(self.unsafe_ptr()) + fn isspace(self) -> Bool: """Determines whether every character in the given StringSlice is a python whitespace String. This corresponds to Python's diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 1bd7cb9dbe..982622a4a0 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -375,7 +375,10 @@ struct StringRef( Returns: An integer value that represents the string, or otherwise raises. """ - return _atol(self) + var str_slice = StringSlice[ImmutableAnyOrigin]( + unsafe_from_utf8_strref=self + ) + return _atol(str_slice) @always_inline fn __len__(self) -> Int: diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index de0a461913..86d524a6c4 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1613,6 +1613,13 @@ def test_float_conversion(): _ = float(String("not a float")) +def test_slice_contains(): + assert_true(String("hello world").as_string_slice().__contains__("world")) + assert_false( + String("hello world").as_string_slice().__contains__("not-found") + ) + + def main(): test_constructors() test_copy() @@ -1668,3 +1675,4 @@ def main(): test_ljust() test_center() test_float_conversion() + test_slice_contains() diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index b411b857e5..87604606ca 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -41,7 +41,7 @@ alias words_ar = """ تحت, الأشياء, معه, يريد, أننا, أنظر, لما, اعرف, إلي, ثلاثة, انتظر, الرجال, الذين, حصلت, أني, سعيد, لابد, عزيزتي, الشيء, فكرة, انهم, الله, الباب, سيدى, دائما, رأيت, مشكلة, استطيع, تكن, تذهب, ليلة, شيئ, أظن, طوال, - جميل, وهو, الشرطة, او, دولار, السيارة, وهذا, كبير, مني, بسرعة, النار, الأمور, سمعت, أشعر, يعرف, + جميل, وهو, الشرطة, او, دولار, السيارة, وهذا, كبير, مني, بسرعة, النار, الأمور, سمعت, أشعر, يعرف, أعني, لدى, بهذه, أحب, سنوات, بأس, الأفضل, بالنسبة, أنتم, عظيم, يقول, جميلة, جون, جاك, بسبب, الوحيد, أمر, بل, بالفعل, الشخص, الي, دعني, خارج, اجل, الخير, ــ, حالك, للغاية, فحسب, كانوا, أردت, فتاة, بشأن, يعني, كبيرة, ترى, آسفة, دقيقة, أنهم, يستطيع, احد, بأنك, تعمل, @@ -72,7 +72,7 @@ alias words_ar = """ غدا, ظننت, ولن, المرأة, لهذه, تحرك, يهم, تبقى, الطبيب, اسم, انظري, تبا, أتذكر, فترة, ساعات, تفكر, تحصل, بأي, النقود, لعبة, زوجتي, الكلام, ستفعل, أسف, فهو, الملك, مدينة, بكم, الوحيدة, أمام, عدد, اخرج, بول, سأعود, جئت, لأني, تحدث, السلامة, الماضية, أمك, اعتقدت, مره, مساء, بطريقة, الرب, ابدا, أهذا, وفي, وكل, أتيت, منكم, - انتهى, بوب, بعيدا, ضع, وجود, تعود, زلت, اللعينة, نقوم, كلنا, أحصل, يريدون, تأخذ, المحتمل, الشمس, بدأ, + انتهى, بوب, بعيدا, ضع, وجود, تعود, زلت, اللعينة, نقوم, كلنا, أحصل, يريدون, تأخذ, المحتمل, الشمس, بدأ, ارجوك, المسيح, جاء, كهذا, سنذهب, تعالى, إثنان, فعلا, حتي, سيحدث, الجيد, وشك, القادم, معرفة, صورة, أعود, اسمي, طلب, آنسة, الثانية, فقدت, حفلة, تنظر, مثير, اننى, وصلت, أنتظر, السماء, يقولون, الهراء, معهم, ابي, وعندما, مجموعة, العاهرة, ماري, حسن, الزواج, نحو, دعيني, الجديدة, مهم, أمس, اتصل, ابتعد, هراء, ستة, @@ -83,7 +83,7 @@ alias words_ar = """ الدخول, جين, امرأة, متأكدة, هيه, تخبرني, مدى, إلهى, احب, عما, نرى, بيننا, تعيش, قتلت, الأحمق, تشارلي, بيل, عليكم, سؤال, طلبت, الهواء, وهذه, صوت, انتم, ميلاد, ماكس, - تعتقدين, الحديث, الجانب, صديقك, ذا, خطر, أطلق, الشارع, عملية, ببعض, تتكلم, مختلف, تحمل, مساعدة, + تعتقدين, الحديث, الجانب, صديقك, ذا, خطر, أطلق, الشارع, عملية, ببعض, تتكلم, مختلف, تحمل, مساعدة, بضعة, المناسب, المنطقة, قم, بالداخل, البداية, لأجل, زوجتك, مقابل, يحب, هاري, ممتاز, قريبا, سنكون, فعلته, بتلك, التفكير, أسفل, للعمل, العجوز, امي, الكلب, انتظري, مازال, إننا, اشعر, الجيش, شرطة """ @@ -248,13 +248,13 @@ alias words_he = """ לגרום, המשחק, שרה, לעצמך, במיוחד, המשטרה, צוות, אחזור, שאמרתי, גברים, קורא, בראש, רחוק, למקום, לשלם, להפסיק, מיוחד, הז, שמו, שמחה, כיף, אגיד, למי, ניתן, מאחורי, תמשיך, כיצד, להוציא, מתים, כולכם, אצל, חבל, האישה, לעצמי, גברתי, תוכלי, רואים, דוד, להציל, שצריך, - בעלי, דוקטור, חג, לעבודה, בוודאי, תעשי, הוד, מילה, ברצינות, הארץ, עשינו, לאנשים, רצה, + בעלי, דוקטור, חג, לעבודה, בוודאי, תעשי, הוד, מילה, ברצינות, הארץ, עשינו, לאנשים, רצה, עזוב, יצא, נתן, שניות, בעיר, סי, חשבת, שאלות, אלינו, ידע, תנו, לשים, שאולי, בכך, יכולת, אן, היד, שאוכל, מין, דקה, לדאוג, שמה, תרצה, ראה, הצילו, נוסף, החרא, אופן, כשהוא, צעיר, הפה, עולה, עובדת, שמך, לתפוס, נמצאת, כלבה, האקדח, עדיף, הטלפון, טום, פול, חכו, קר, תלך, במקרה, יעשה, שניכם, הארי, זוז, יקירתי, בהצלחה, לשבת, אנא, דין, מכיוון, יד, הקטנה, לבן, בנו, בעצמי, יין, תוריד, - למישהו, מייק, מול, נזוז, ככל, הלוואי, בעצמך, לרגע, קשור, בשקט, האל, ישנה, מעמד, כזאת, + למישהו, מייק, מול, נזוז, ככל, הלוואי, בעצמך, לרגע, קשור, בשקט, האל, ישנה, מעמד, כזאת, רד, אחורה, איכפת, איתם, ממנה, חם, מבקש, שש, מידע, השנה, אכן, אהבתי, בשעה, בסוף, שקרה, לכו, אליה, לבחור, תחשוב, ספק, המים, הפנים, לכולם, תדאגי, קחי, שתוק, לברוח, מתוק, ארלי, התיק, שים, מישהי, לקרות, לטפל, לחפש, הידיים, ח, במצב, ואל @@ -262,313 +262,313 @@ alias words_he = """ # Source: https://www.101languages.net/latvian/most-common-latvian-words/ alias words_lv = """ - ir, es, un, tu, tas, ka, man, to, vai, ko, ar, kas, par, tā, kā, viņš, uz, no, tev, - mēs, nav, jūs, bet, labi, jā, lai, nē, mani, ja, bija, viņa, esmu, viņu, tevi, esi, - mums, tad, tikai, ne, viņi, kad, jums, arī, viss, nu, kur, pie, jau, tik, tur, te, vēl, - būs, visu, šeit, tagad, kaut, ļoti, pēc, viņam, taču, savu, gan, paldies, būtu, mūsu, - šo, lūdzu, mans, kāpēc, kungs, kāds, varbūt, tās, jūsu, cik, ak, daudz, jo, esam, - zinu, mana, zini, visi, būt, tam, šī, var, līdz, viens, pa, pat, esat, nekad, domāju, - nezinu, vairs, tiešām, tie, vien, kurš, varētu, dievs, neesmu, prom, tieši, kādu, aiziet, - šis, manu, protams, vajag, neko, vienkārši, tāpēc, gribu, varu, nāc, atpakaļ, mūs, - kārtībā, iet, kopā, viņiem, pats, pirms, domā, vienmēr, gribi, nekas, bez, tava, - vienu, ej, viņai, vairāk, notiek, nevaru, pret, tavs, teica, tavu, biju, dēļ, viņas, - laiku, neviens, kādēļ, vari, labāk, patīk, dari, mājās, nebija, cilvēki, ārā, viņus, - ejam, kāda, piedod, laikam, atkal, šķiet, trīs, sevi, ser, laiks, laika, nekā, manis, - iekšā, labs, tāds, darīt, harij, nevar, viena, lieliski, kuru, šīs, sauc, šurp, teicu, - laikā, tos, pagaidi, neesi, tevis, draugs, pārāk, tēvs, šodien, teikt, dienu, visiem, - tātad, notika, hei, zināt, bijis, sveiks, atvainojiet, tika, naudu, varam, savas, citu, - tādu, manas, redzi, šajā, kam, tajā, jābūt, vecīt, tiem, runā, cilvēku, taisnība, saka, - visus, mīlu, lietas, grib, tēt, izskatās, tiek, noteikti, nozīmē, kamēr, divi, it, tāpat, - tāda, ilgi, katru, dēls, noticis, jauki, redzēt, pareizi, lūk, kundze, aiz, iespējams, - pateikt, nebūtu, gandrīz, vīrs, cilvēks, ātri, žēl, pasaules, rokas, liekas, palīdzēt, - līdzi, visas, saki, negribu, vietā, gadus, starp, skaties, tomēr, tūlīt, džek, nevajag, - sev, vajadzētu, būšu, dzīvi, droši, gadu, priekšu, skaidrs, gribēju, nāk, paskaties, mazliet, - tikko, nebūs, augšā, ceru, joprojām, nevis, ātrāk, ļauj, gribētu, liels, zina, vārdu, reizi, - pasaulē, savā, sveiki, dienas, miris, dod, priekšā, galā, klau, cilvēkiem, tavas, patiesībā, - visa, vārds, gatavs, durvis, velns, nedaudz, naudas, redzēju, velna, manā, drīz, pāri, dzīve, - vēlies, nemaz, priekš, bērni, vieta, pāris, darbu, vajadzīgs, tālāk, rīt, roku, klāt, grūti, - beidz, laba, klausies, dara, varat, sveika, biji, vismaz, kopš, redzu, saproti, kura, draugi, - zemes, šovakar, patiešām, kaa, vietu, dieva, vajadzēja, mašīnu, lejā, saku, ceļu, gada, tādēļ, - cauri, runāt, ņem, oh, divas, lieta, tikt, šie, teici, vēlāk, vaļā, nogalināt, redzējis, jāiet, - nespēju, savus, atceries, ūdens, šejienes, labu, diena, mīļā, atvaino, doties, atrast, saprotu, - abi, reiz, jādara, nesaprotu, meitene, darbs, nevari, tai, nedomāju, pilnīgi, nakti, nekādu, - pati, gadiem, vēlos, taa, kādas, cits, ejiet, pirmais, a, būsi, mamma, lietu, slikti, pašu, - acis, diezgan, pasaki, gadā, puiši, asv, sava, nost, cilvēkus, džeks, manuprāt, mājas, o, - bērns, leo, otru, nopietni, vecais, laukā, caur, dzīves, izdarīt, sieviete, vienalga, - nogalināja, dzīvo, kādreiz, čau, sirds, paliec, gribat, vēlreiz, kuras, mazais, vietas, - piedodiet, laipni, palikt, brauc, ei, the, paliek, apkārt, sievietes, tālu, garām, pirmo, - dzīvot, nāciet, runāju, kuri, tiks, jüs, ceļā, nauda, nevienam, māja, vienīgais, īsti, - sapratu, gluži, svarīgi, atvainojos, i, sen, iespēja, tavā, pavisam, nāves, māte, citi, - viegli, zem, notiks, darba, nepatīk, daži, galvu, dienā, hallo, bērnu, neesam, kungi, beidzot, - nedrīkst, vajadzēs, māju, sieva, kādam, puika, kļūst, prieks, esot, iesim, daļa, pasaule, - pietiek, visā, saviem, rīta, pagaidiet, tētis, mājā, mieru, vīru, palīdzību, dzirdēju, - tādas, dzīvs, strādā, tām, vēlas, nakts, īpaši, jūtos, nolādēts, meitenes, pusi, mammu, mees, - aizveries, vispār, dzīvību, kurā, kādā, vārdā, mašīna, būsim, vispirms, vinji, nevienu, šos, - tiksimies, džeik, vinjsh, vaina, turpini, kādi, jaunu, tuvu, atradu, vēlu, varēja, citādi, šim, - satikt, neuztraucies, pārliecināts, liec, diez, liela, doktor, nevaram, palīdzi, uzmanīgi, dažas, - šiem, atgriezies, gribēja, priecājos, parasti, valsts, asinis, tēti, you, mierā, piemēram, - jautājums, atā, bijām, zemē, pasauli, spēlē, blakus, izskaties, pirmā, nomira, paši, šobrīd, - daru, gaida, tādi, iešu, labākais, jauks, maz, pieder, jauns, nezināju, uzmanību, skaista, - prātā, brālis, patiesību, mierīgi, šai, dr, patiesi, jēzus, mārtij, zināju, suns, juus, sievu, - dzirdi, tepat, mamm, tēvu, tēva, frodo, sasodīts, desmit, stundas, tavi, mazā, džon, cita, - vajadzīga, forši, minūtes, mīlestība, nebiju, saprast, izbeidz, šoreiz, labā, dāmas, kurienes, - problēma, šādi, spēj, gadījumā, tiesa, kuģi, pēdējā, tici, esiet, atceros, katrs, nee, palīgā, - mister, liek, likās, domāt, vīri, pēdējo, traks, reizes, vienīgā, tiesības, skolā, turies, beigas, - karš, pīter, uguni, pietiks, vienam, vienā, pakaļ, jauna, zemi, puisis, ziniet, negribi, labrīt, - ap, cilvēka, draugu, atver, nezini, sāra, vēlaties, gadi, dažreiz, rokās, dabūt, nomierinies, - istabā, agrāk, ieroci, savām, meiteni, paņem, meklē, pār, seju, ziņu, dzirdējis, zinām, gatavi, - braukt, sāka, sāk, dievam, neesat, dzirdēt, spēle, bērniem, izdarīja, muļķības, doma, pēdējais, - dīvaini, atdod, ziņas, bankas, darāt, vakar, ceļš, neviena, brāli, otrā, atgriezties, galvas, - pietiekami, gulēt, uzreiz, iespēju, bijusi, karalis, bobij, šrek, tikpat, palīdziet, durvīm, - vecāki, atrodas, smieklīgi, kuģa, bail, godīgi, pēkšņi, nedēļas, māsa, skrien, ceļa, džeims, gars, - lielu, mašīnā, bojā, kurieni, ļaudis, dārgais, vecs, ūdeni, kūper, eju, mašīnas, ideja, kājas, - spēles, galvenais, citiem, jātiek, skaisti, nāvi, vinju, problēmas, vērts, drīkstu, domājat, visur, - bieži, manai, citas, apsolu, zelta, strādāju, dzimšanas, jūtu, naktī, dārgā, atbildi, noticēt, - klājas, izdevās, dok, redzat, gana, divus, ģimene, runa, stāsts, braucam, brīnišķīgi, ģimenes, - kuģis, čārlij, hey, kä, sheit, ved, atrada, mirusi, meita, paklau, nevēlos, bērnus, boss, kaptein, - nekāda, roze, nespēj, vīrietis, brīdi, īsts, dzīvē, tādā, manī, jūras, jaunkundz, iemesls, sakot, - manam, daudzi, varēsi, pateicos, jaunais, policija, pilnībā, nekur, jauka, nedari, kurus, zināms, - jautājumu, seko, re, padomā, pusē, visām, mīļais, dolāru, gadžet, katram, izdarīji, šīm, vienīgi, - mirt, apmēram, spēku, jauno, mr, celies, iepriekš, prātu, vēlētos, četri, lietām, redzēji, nevajadzētu, - donna, jaa, ticu, minūtēm, sievieti, nāve, jūties, nezina, parādi, malā, redz, uh, gredzenu, uzmanies, - kara, drošībā, sapnis, bijāt, grāmatu, slepkava, vinja, paga, pieci, pilsētā, drošs, pateikšu, gāja, - spēli, beigās, hanna, princese, jebkad, dakter, veids, palīdzība, stāstu, izmantot, spēlēt, gaisā, - darīšu, došos, dodas, kreisi, negribēju, mazāk, pastāsti, tak, devās, sirdi, misis, vis, patiesība, - veidā, harijs, cenšos, tuvāk, kurp, klausieties, sāp, ļaujiet, neticami, kungu, sīkais, iedomāties, - daļu, mazs, iedod, mazo, meklēju, parunāt, jādodas, sevis, pārējie, veicas, otra, mīlestību, zēns, - dodies, galam, sem, bīstami, zvēru, iespējas, maza, ellē, virs, nekādas, maniem, skatieties, šonakt, - svēto, kapteinis, iepazīties, pazīstu, turp, gredzens, nepareizi, lieliska, īstais, pagaidām, kājām, - mirklīti, pašlaik, d, poter, saprati, aprunāties, paša, šejieni, interesanti, nevarētu, pašā, paskat, - bailes, skolas, vārdus, aizmirsti, gaismas, kāp, zēni, darīsim, pašam, beidzies, sauca, māti, akmens, + ir, es, un, tu, tas, ka, man, to, vai, ko, ar, kas, par, tā, kā, viņš, uz, no, tev, + mēs, nav, jūs, bet, labi, jā, lai, nē, mani, ja, bija, viņa, esmu, viņu, tevi, esi, + mums, tad, tikai, ne, viņi, kad, jums, arī, viss, nu, kur, pie, jau, tik, tur, te, vēl, + būs, visu, šeit, tagad, kaut, ļoti, pēc, viņam, taču, savu, gan, paldies, būtu, mūsu, + šo, lūdzu, mans, kāpēc, kungs, kāds, varbūt, tās, jūsu, cik, ak, daudz, jo, esam, + zinu, mana, zini, visi, būt, tam, šī, var, līdz, viens, pa, pat, esat, nekad, domāju, + nezinu, vairs, tiešām, tie, vien, kurš, varētu, dievs, neesmu, prom, tieši, kādu, aiziet, + šis, manu, protams, vajag, neko, vienkārši, tāpēc, gribu, varu, nāc, atpakaļ, mūs, + kārtībā, iet, kopā, viņiem, pats, pirms, domā, vienmēr, gribi, nekas, bez, tava, + vienu, ej, viņai, vairāk, notiek, nevaru, pret, tavs, teica, tavu, biju, dēļ, viņas, + laiku, neviens, kādēļ, vari, labāk, patīk, dari, mājās, nebija, cilvēki, ārā, viņus, + ejam, kāda, piedod, laikam, atkal, šķiet, trīs, sevi, ser, laiks, laika, nekā, manis, + iekšā, labs, tāds, darīt, harij, nevar, viena, lieliski, kuru, šīs, sauc, šurp, teicu, + laikā, tos, pagaidi, neesi, tevis, draugs, pārāk, tēvs, šodien, teikt, dienu, visiem, + tātad, notika, hei, zināt, bijis, sveiks, atvainojiet, tika, naudu, varam, savas, citu, + tādu, manas, redzi, šajā, kam, tajā, jābūt, vecīt, tiem, runā, cilvēku, taisnība, saka, + visus, mīlu, lietas, grib, tēt, izskatās, tiek, noteikti, nozīmē, kamēr, divi, it, tāpat, + tāda, ilgi, katru, dēls, noticis, jauki, redzēt, pareizi, lūk, kundze, aiz, iespējams, + pateikt, nebūtu, gandrīz, vīrs, cilvēks, ātri, žēl, pasaules, rokas, liekas, palīdzēt, + līdzi, visas, saki, negribu, vietā, gadus, starp, skaties, tomēr, tūlīt, džek, nevajag, + sev, vajadzētu, būšu, dzīvi, droši, gadu, priekšu, skaidrs, gribēju, nāk, paskaties, mazliet, + tikko, nebūs, augšā, ceru, joprojām, nevis, ātrāk, ļauj, gribētu, liels, zina, vārdu, reizi, + pasaulē, savā, sveiki, dienas, miris, dod, priekšā, galā, klau, cilvēkiem, tavas, patiesībā, + visa, vārds, gatavs, durvis, velns, nedaudz, naudas, redzēju, velna, manā, drīz, pāri, dzīve, + vēlies, nemaz, priekš, bērni, vieta, pāris, darbu, vajadzīgs, tālāk, rīt, roku, klāt, grūti, + beidz, laba, klausies, dara, varat, sveika, biji, vismaz, kopš, redzu, saproti, kura, draugi, + zemes, šovakar, patiešām, kaa, vietu, dieva, vajadzēja, mašīnu, lejā, saku, ceļu, gada, tādēļ, + cauri, runāt, ņem, oh, divas, lieta, tikt, šie, teici, vēlāk, vaļā, nogalināt, redzējis, jāiet, + nespēju, savus, atceries, ūdens, šejienes, labu, diena, mīļā, atvaino, doties, atrast, saprotu, + abi, reiz, jādara, nesaprotu, meitene, darbs, nevari, tai, nedomāju, pilnīgi, nakti, nekādu, + pati, gadiem, vēlos, taa, kādas, cits, ejiet, pirmais, a, būsi, mamma, lietu, slikti, pašu, + acis, diezgan, pasaki, gadā, puiši, asv, sava, nost, cilvēkus, džeks, manuprāt, mājas, o, + bērns, leo, otru, nopietni, vecais, laukā, caur, dzīves, izdarīt, sieviete, vienalga, + nogalināja, dzīvo, kādreiz, čau, sirds, paliec, gribat, vēlreiz, kuras, mazais, vietas, + piedodiet, laipni, palikt, brauc, ei, the, paliek, apkārt, sievietes, tālu, garām, pirmo, + dzīvot, nāciet, runāju, kuri, tiks, jüs, ceļā, nauda, nevienam, māja, vienīgais, īsti, + sapratu, gluži, svarīgi, atvainojos, i, sen, iespēja, tavā, pavisam, nāves, māte, citi, + viegli, zem, notiks, darba, nepatīk, daži, galvu, dienā, hallo, bērnu, neesam, kungi, beidzot, + nedrīkst, vajadzēs, māju, sieva, kādam, puika, kļūst, prieks, esot, iesim, daļa, pasaule, + pietiek, visā, saviem, rīta, pagaidiet, tētis, mājā, mieru, vīru, palīdzību, dzirdēju, + tādas, dzīvs, strādā, tām, vēlas, nakts, īpaši, jūtos, nolādēts, meitenes, pusi, mammu, mees, + aizveries, vispār, dzīvību, kurā, kādā, vārdā, mašīna, būsim, vispirms, vinji, nevienu, šos, + tiksimies, džeik, vinjsh, vaina, turpini, kādi, jaunu, tuvu, atradu, vēlu, varēja, citādi, šim, + satikt, neuztraucies, pārliecināts, liec, diez, liela, doktor, nevaram, palīdzi, uzmanīgi, dažas, + šiem, atgriezies, gribēja, priecājos, parasti, valsts, asinis, tēti, you, mierā, piemēram, + jautājums, atā, bijām, zemē, pasauli, spēlē, blakus, izskaties, pirmā, nomira, paši, šobrīd, + daru, gaida, tādi, iešu, labākais, jauks, maz, pieder, jauns, nezināju, uzmanību, skaista, + prātā, brālis, patiesību, mierīgi, šai, dr, patiesi, jēzus, mārtij, zināju, suns, juus, sievu, + dzirdi, tepat, mamm, tēvu, tēva, frodo, sasodīts, desmit, stundas, tavi, mazā, džon, cita, + vajadzīga, forši, minūtes, mīlestība, nebiju, saprast, izbeidz, šoreiz, labā, dāmas, kurienes, + problēma, šādi, spēj, gadījumā, tiesa, kuģi, pēdējā, tici, esiet, atceros, katrs, nee, palīgā, + mister, liek, likās, domāt, vīri, pēdējo, traks, reizes, vienīgā, tiesības, skolā, turies, beigas, + karš, pīter, uguni, pietiks, vienam, vienā, pakaļ, jauna, zemi, puisis, ziniet, negribi, labrīt, + ap, cilvēka, draugu, atver, nezini, sāra, vēlaties, gadi, dažreiz, rokās, dabūt, nomierinies, + istabā, agrāk, ieroci, savām, meiteni, paņem, meklē, pār, seju, ziņu, dzirdējis, zinām, gatavi, + braukt, sāka, sāk, dievam, neesat, dzirdēt, spēle, bērniem, izdarīja, muļķības, doma, pēdējais, + dīvaini, atdod, ziņas, bankas, darāt, vakar, ceļš, neviena, brāli, otrā, atgriezties, galvas, + pietiekami, gulēt, uzreiz, iespēju, bijusi, karalis, bobij, šrek, tikpat, palīdziet, durvīm, + vecāki, atrodas, smieklīgi, kuģa, bail, godīgi, pēkšņi, nedēļas, māsa, skrien, ceļa, džeims, gars, + lielu, mašīnā, bojā, kurieni, ļaudis, dārgais, vecs, ūdeni, kūper, eju, mašīnas, ideja, kājas, + spēles, galvenais, citiem, jātiek, skaisti, nāvi, vinju, problēmas, vērts, drīkstu, domājat, visur, + bieži, manai, citas, apsolu, zelta, strādāju, dzimšanas, jūtu, naktī, dārgā, atbildi, noticēt, + klājas, izdevās, dok, redzat, gana, divus, ģimene, runa, stāsts, braucam, brīnišķīgi, ģimenes, + kuģis, čārlij, hey, kä, sheit, ved, atrada, mirusi, meita, paklau, nevēlos, bērnus, boss, kaptein, + nekāda, roze, nespēj, vīrietis, brīdi, īsts, dzīvē, tādā, manī, jūras, jaunkundz, iemesls, sakot, + manam, daudzi, varēsi, pateicos, jaunais, policija, pilnībā, nekur, jauka, nedari, kurus, zināms, + jautājumu, seko, re, padomā, pusē, visām, mīļais, dolāru, gadžet, katram, izdarīji, šīm, vienīgi, + mirt, apmēram, spēku, jauno, mr, celies, iepriekš, prātu, vēlētos, četri, lietām, redzēji, nevajadzētu, + donna, jaa, ticu, minūtēm, sievieti, nāve, jūties, nezina, parādi, malā, redz, uh, gredzenu, uzmanies, + kara, drošībā, sapnis, bijāt, grāmatu, slepkava, vinja, paga, pieci, pilsētā, drošs, pateikšu, gāja, + spēli, beigās, hanna, princese, jebkad, dakter, veids, palīdzība, stāstu, izmantot, spēlēt, gaisā, + darīšu, došos, dodas, kreisi, negribēju, mazāk, pastāsti, tak, devās, sirdi, misis, vis, patiesība, + veidā, harijs, cenšos, tuvāk, kurp, klausieties, sāp, ļaujiet, neticami, kungu, sīkais, iedomāties, + daļu, mazs, iedod, mazo, meklēju, parunāt, jādodas, sevis, pārējie, veicas, otra, mīlestību, zēns, + dodies, galam, sem, bīstami, zvēru, iespējas, maza, ellē, virs, nekādas, maniem, skatieties, šonakt, + svēto, kapteinis, iepazīties, pazīstu, turp, gredzens, nepareizi, lieliska, īstais, pagaidām, kājām, + mirklīti, pašlaik, d, poter, saprati, aprunāties, paša, šejieni, interesanti, nevarētu, pašā, paskat, + bailes, skolas, vārdus, aizmirsti, gaismas, kāp, zēni, darīsim, pašam, beidzies, sauca, māti, akmens, grāmatas, diemžēl, tevī, kļūt, endij, patika, nabaga, tuvojas, tēvoci, dienām, plāns """ # Source: https://www.101languages.net/polish/most-common-polish-words/ alias words_pl = """ -nie, to, się, w, na, i, z, co, jest, że, do, tak, jak, o, mnie, a, ale, mi, za, ja, ci, tu, ty, czy, -tym, go, tego, tylko, jestem, po, cię, ma, już, mam, jesteś, może, pan, dla, coś, dobrze, wiem, jeśli, -teraz, proszę, od, wszystko, tam, więc, masz, nic, on, być, gdzie, będzie, są, ten, mogę, ciebie, -bardzo, sobie, kiedy, ze, wiesz, no, jej, jeszcze, pani, był, mój, chcę, było, dlaczego, by, przez, -nas, tutaj, chcesz, jego, ją, ich, nigdy, żeby, też, kto, naprawdę, przepraszam, bo, mamy, porządku, -możesz, dobra, mu, dziękuję, ona, domu, panie, muszę, nawet, chyba, hej, właśnie, prawda, zrobić, te, -zawsze, będę, moja, gdy, je, trochę, nam, moje, cześć, bez, nim, była, tej, jesteśmy, dalej, pana, -dzięki, wszyscy, musisz, twój, lat, tobą, więcej, ktoś, czas, ta, który, chce, powiedzieć, chodź, dobry, -mną, niech, sam, razem, chodzi, czego, boże, stało, musimy, raz, albo, prostu, będziesz, dzień, możemy, -was, myślę, czym, daj, lepiej, czemu, ludzie, ok, przed, życie, ludzi, robisz, my, niż, tych, kim, rzeczy, -myślisz, powiedz, przy, twoja, oni, oczywiście, nikt, siebie, stąd, niego, twoje, miał, jeden, mówi, -powiedział, moim, czasu, u, dziś, im, które, musi, wtedy, taki, aby, pod, dwa, temu, pewnie, takie, cóż, -wszystkie, mojego, dużo, cholera, kurwa, wie, znaczy, wygląda, dzieje, mieć, ile, iść, potem, będziemy, -dzieci, dlatego, cały, byłem, moją, skąd, szybko, jako, kochanie, stary, trzeba, miejsce, myśli, można, -sie, jasne, mojej, wam, swoje, zaraz, wiele, nią, rozumiem, nich, wszystkich, jakieś, jakiś, kocham, idź, -tę, mają, mówię, mówisz, dzisiaj, nad, pomóc, takiego, przestań, tobie, jutro, robić, jaki, mamo, kilka, -przykro, wiedzieć, ojciec, widzisz, zbyt, zobaczyć, która, ani, tyle, trzy, tą, sposób, miałem, tato, niej, -później, pieniądze, robi, kogoś, kiedyś, zanim, widzę, pracy, świetnie, pewno, myślałem, będą, bardziej, -życia, długo, och, sir, ponieważ, aż, dni, nocy, każdy, dnia, znowu, oh, chciałem, taka, swoją, twoim, -widziałem, stanie, powiem, imię, wy, żebyś, nadzieję, twojej, panu, spokój, słuchaj, rację, spójrz, razie, -znam, pierwszy, koniec, chciałbym, we, nami, jakie, posłuchaj, problem, przecież, dobre, nasz, dziecko, drzwi, -nasze, miło, czuję, mógł, żyje, jeżeli, człowiek, powiedziałem, gdyby, roku, dom, sama, potrzebuję, -wszystkim, zostać, wciąż, dokładnie, mama, którzy, mówić, zamknij, mów, twoją, chwilę, zrobił, samo, idziemy, -nadal, jesteście, zabić, były, sobą, kogo, lub, lubię, the, podoba, minut, bym, chciał, bądź, czegoś, gdzieś, -mówiłem, chodźmy, znaleźć, poza, spokojnie, wcześniej, został, rozumiesz, mogą, prawie, wydaje, miała, mały, -byłeś, facet, zrobię, macie, żadnych, razy, noc, ciągle, broń, moich, twojego, końcu, pomocy, czekaj, znasz, -oczy, weź, idę, halo, dość, innego, pomysł, jakby, trzymaj, jedno, ojca, porozmawiać, pamiętasz, lata, -powinieneś, którą, powodu, takim, niczego, powinniśmy, oto, napisy, jednak, świat, pokoju, żebym, sprawy, -dwie, samochód, swój, wystarczy, pewien, źle, pozwól, numer, jedną, miejscu, you, drogi, byłam, dokąd, miłość, -panowie, pieniędzy, którego, matka, rano, dwóch, całe, patrz, rzecz, nowy, idzie, wyglądasz, bóg, byś, życiu, -nimi, nikogo, całą, swojego, świecie, sprawa, dziewczyna, prawo, byli, zostaw, wiedziałem, jedna, widzieć, -swoim, kobiety, uważaj, najpierw, właściwie, dam, również, diabła, chcą, którym, zrób, da, jednego, dać, -musiał, ręce, powinienem, których, znów, powiedziała, wczoraj, czujesz, zaczekaj, sądzę, śmierć, mówił, -podczas, której, całkiem, pracę, żona, pójdę, pamiętam, powiedziałeś, mówią, wiemy, jezu, witam, cholery, -swoich, telefon, wielu, także, poważnie, skoro, miejsca, robię, śmierci, słyszałem, wina, zrobiłem, dobranoc, -parę, prawdę, swojej, serce, inaczej, dziewczyny, kobieta, powiesz, martw, rób, pytanie, pięć, innych, one, -gra, natychmiast, wrócić, szybciej, jednym, cokolwiek, wierzę, wcale, wieczór, ważne, człowieka, wielki, nowa, -dopiero, ziemi, gdybym, tata, poznać, stać, jack, myślałam, witaj, słowa, zrobiłeś, gówno, john, dolarów, -sprawę, inne, idziesz, miałam, wiecie, chciałam, zobaczenia, widziałeś, żyć, każdym, nasza, panią, wspaniale, -chwili, każdego, nowego, nieźle, takich, między, dostać, powinien, dawaj, dopóki, naszych, naszej, świata, -chłopaki, chcemy, poczekaj, jaką, człowieku, czasem, żadnego, inny, przynajmniej, nazywa, super, naszego, -szczęście, potrzebuje, godziny, zabrać, powrotem, syn, lecz, słucham, twoich, udało, boga, pokój, działa, -ogóle, naszym, szkoły, możliwe, wiedział, wyjść, wszystkiego, byłoby, daleko, wieczorem, skarbie, jaka, -mógłbym, ostatni, możecie, cztery, doktorze, zrobimy, mąż, przeciwko, zgadza, zrobisz, czasie, czasami, -brzmi, raczej, ciało, należy, miasta, miałeś, taką, brat, cieszę, rozmawiać, cała, czymś, wybacz, twarz, -mała, chcecie, dr, pojęcia, lubisz, głowę, najbardziej, dziwne, głowy, wody, pół, wiadomość, policja, -strony, l, pl, mogłem, mieli, widzenia, pewna, ruszaj, wracaj, ode, popatrz, końca, plan, kiedykolwiek, -wejść, została, rok, syna, uda, wrócę, zewnątrz, droga, uwierzyć, późno, zostało, zostanie, zły, kapitanie, -potrzebujemy, byliśmy, zobaczymy, gotowy, obchodzi, jechać, rodziny, widziałam, drodze, czeka, środku, film, -spać, człowiekiem, zupełnie, taa, pomóż, mieliśmy, pomoc, słowo, innym, ostatnio, and, zna, mogła, pójść, -chłopcy, wziąć, mógłbyś, tłumaczenie, potrzebujesz, słyszysz, blisko, godzin, miłości, góry, zabił, piękna, -napisów, pokaż, moi, lubi, robota, prawa, ciężko, kimś, dół, rękę, nazywam, wielkie, część, wkrótce, naszą, -jedziemy, zapomnij, prosto, radę, robimy, powinnaś, gdybyś, chociaż, zależy, stronie, wypadek, tydzień, byłaś, -nowe, małe, praca, drogę, chłopak, zrobi, widział, mieście, synu, oznacza, krew, mógłby, krwi, górę, joe, wasza, -robią, tędy, wszędzie, temat, pierwsze, zobacz, ponad, kraju, mało, racja, tymi, cicho, chciała, powiedziałam, -leci, powinno, mówiąc, serca, chciałabym, miasto, george, spotkać, mniej, e, przyjaciel, mówiłeś, kłopoty, -miesięcy, jakąś, żaden, zostań, roboty, zatrzymać, frank, nieważne, głupi, pa, koleś, sprawie, spotkanie, ojcze, -pewnego, spróbuj, drugi, znalazłem, pracować, całym, zostały, złe, niemożliwe, jakoś, zdjęcia, stronę, wiedzą, it, -dziewczynę, zaczyna, mogli, samego, sądzisz, rodzina, razu, trudno, samochodu, okay, boję, szkoda, wami, charlie, -dał, środka, ojcem, piękne, dawno, choć, panem, przykład, nagle, bracie, żadnej, drugiej, przyjaciół, otwórz, -myśleć, doktor, chwileczkę, pracuje, najlepszy, brata, czyż, często, http, powinnam, odejść, trzech, chodźcie, -nazwisko, szansę, ciała, policji, szkole, prawdopodobnie, serio, matki, org, wolno, sami, muszą, zabierz, -słyszałeś, siostra, uspokój, wystarczająco, początku, faceta, problemy, szefie, broni, me, zostawić, czuje, -będziecie, przyszedł, wiedziałam, kilku, inni, b, głowie, historia, według, www, wezmę, nowym, czekać, stój, -mężczyzna, mówiłam, pokazać, około, wracam, wieku, jakaś, pierwsza, niczym, zabiję, zdjęcie, zabawne, rodzice, -musiałem, całkowicie, sprawdzić, mike, przyjdzie, sześć, kupić, dobrym, żonę, dasz, pomoże, nogi, obok, ruszać, -trzymać, zadzwonić, panno, godzinę, boli, oraz, spokoju, walczyć, wróci, tom, wspólnego, zmienić, ostatnie, uwagę, -znać, jednej, dłużej, powie, pogadać, łatwo, większość, nikomu, michael, córka, niedługo, powodzenia, tygodniu, -włosy, niestety, górze, kochasz, prawdziwy, historii, ulicy, musicie, gotowi, chwila, samym, grać, zadzwonię, -strasznie, mieszka, kocha, rady, tyłu, jakim, obiecuję, tysięcy, pomyślałem, pracuję, jedynie, pozwolić, uwaga, +nie, to, się, w, na, i, z, co, jest, że, do, tak, jak, o, mnie, a, ale, mi, za, ja, ci, tu, ty, czy, +tym, go, tego, tylko, jestem, po, cię, ma, już, mam, jesteś, może, pan, dla, coś, dobrze, wiem, jeśli, +teraz, proszę, od, wszystko, tam, więc, masz, nic, on, być, gdzie, będzie, są, ten, mogę, ciebie, +bardzo, sobie, kiedy, ze, wiesz, no, jej, jeszcze, pani, był, mój, chcę, było, dlaczego, by, przez, +nas, tutaj, chcesz, jego, ją, ich, nigdy, żeby, też, kto, naprawdę, przepraszam, bo, mamy, porządku, +możesz, dobra, mu, dziękuję, ona, domu, panie, muszę, nawet, chyba, hej, właśnie, prawda, zrobić, te, +zawsze, będę, moja, gdy, je, trochę, nam, moje, cześć, bez, nim, była, tej, jesteśmy, dalej, pana, +dzięki, wszyscy, musisz, twój, lat, tobą, więcej, ktoś, czas, ta, który, chce, powiedzieć, chodź, dobry, +mną, niech, sam, razem, chodzi, czego, boże, stało, musimy, raz, albo, prostu, będziesz, dzień, możemy, +was, myślę, czym, daj, lepiej, czemu, ludzie, ok, przed, życie, ludzi, robisz, my, niż, tych, kim, rzeczy, +myślisz, powiedz, przy, twoja, oni, oczywiście, nikt, siebie, stąd, niego, twoje, miał, jeden, mówi, +powiedział, moim, czasu, u, dziś, im, które, musi, wtedy, taki, aby, pod, dwa, temu, pewnie, takie, cóż, +wszystkie, mojego, dużo, cholera, kurwa, wie, znaczy, wygląda, dzieje, mieć, ile, iść, potem, będziemy, +dzieci, dlatego, cały, byłem, moją, skąd, szybko, jako, kochanie, stary, trzeba, miejsce, myśli, można, +sie, jasne, mojej, wam, swoje, zaraz, wiele, nią, rozumiem, nich, wszystkich, jakieś, jakiś, kocham, idź, +tę, mają, mówię, mówisz, dzisiaj, nad, pomóc, takiego, przestań, tobie, jutro, robić, jaki, mamo, kilka, +przykro, wiedzieć, ojciec, widzisz, zbyt, zobaczyć, która, ani, tyle, trzy, tą, sposób, miałem, tato, niej, +później, pieniądze, robi, kogoś, kiedyś, zanim, widzę, pracy, świetnie, pewno, myślałem, będą, bardziej, +życia, długo, och, sir, ponieważ, aż, dni, nocy, każdy, dnia, znowu, oh, chciałem, taka, swoją, twoim, +widziałem, stanie, powiem, imię, wy, żebyś, nadzieję, twojej, panu, spokój, słuchaj, rację, spójrz, razie, +znam, pierwszy, koniec, chciałbym, we, nami, jakie, posłuchaj, problem, przecież, dobre, nasz, dziecko, drzwi, +nasze, miło, czuję, mógł, żyje, jeżeli, człowiek, powiedziałem, gdyby, roku, dom, sama, potrzebuję, +wszystkim, zostać, wciąż, dokładnie, mama, którzy, mówić, zamknij, mów, twoją, chwilę, zrobił, samo, idziemy, +nadal, jesteście, zabić, były, sobą, kogo, lub, lubię, the, podoba, minut, bym, chciał, bądź, czegoś, gdzieś, +mówiłem, chodźmy, znaleźć, poza, spokojnie, wcześniej, został, rozumiesz, mogą, prawie, wydaje, miała, mały, +byłeś, facet, zrobię, macie, żadnych, razy, noc, ciągle, broń, moich, twojego, końcu, pomocy, czekaj, znasz, +oczy, weź, idę, halo, dość, innego, pomysł, jakby, trzymaj, jedno, ojca, porozmawiać, pamiętasz, lata, +powinieneś, którą, powodu, takim, niczego, powinniśmy, oto, napisy, jednak, świat, pokoju, żebym, sprawy, +dwie, samochód, swój, wystarczy, pewien, źle, pozwól, numer, jedną, miejscu, you, drogi, byłam, dokąd, miłość, +panowie, pieniędzy, którego, matka, rano, dwóch, całe, patrz, rzecz, nowy, idzie, wyglądasz, bóg, byś, życiu, +nimi, nikogo, całą, swojego, świecie, sprawa, dziewczyna, prawo, byli, zostaw, wiedziałem, jedna, widzieć, +swoim, kobiety, uważaj, najpierw, właściwie, dam, również, diabła, chcą, którym, zrób, da, jednego, dać, +musiał, ręce, powinienem, których, znów, powiedziała, wczoraj, czujesz, zaczekaj, sądzę, śmierć, mówił, +podczas, której, całkiem, pracę, żona, pójdę, pamiętam, powiedziałeś, mówią, wiemy, jezu, witam, cholery, +swoich, telefon, wielu, także, poważnie, skoro, miejsca, robię, śmierci, słyszałem, wina, zrobiłem, dobranoc, +parę, prawdę, swojej, serce, inaczej, dziewczyny, kobieta, powiesz, martw, rób, pytanie, pięć, innych, one, +gra, natychmiast, wrócić, szybciej, jednym, cokolwiek, wierzę, wcale, wieczór, ważne, człowieka, wielki, nowa, +dopiero, ziemi, gdybym, tata, poznać, stać, jack, myślałam, witaj, słowa, zrobiłeś, gówno, john, dolarów, +sprawę, inne, idziesz, miałam, wiecie, chciałam, zobaczenia, widziałeś, żyć, każdym, nasza, panią, wspaniale, +chwili, każdego, nowego, nieźle, takich, między, dostać, powinien, dawaj, dopóki, naszych, naszej, świata, +chłopaki, chcemy, poczekaj, jaką, człowieku, czasem, żadnego, inny, przynajmniej, nazywa, super, naszego, +szczęście, potrzebuje, godziny, zabrać, powrotem, syn, lecz, słucham, twoich, udało, boga, pokój, działa, +ogóle, naszym, szkoły, możliwe, wiedział, wyjść, wszystkiego, byłoby, daleko, wieczorem, skarbie, jaka, +mógłbym, ostatni, możecie, cztery, doktorze, zrobimy, mąż, przeciwko, zgadza, zrobisz, czasie, czasami, +brzmi, raczej, ciało, należy, miasta, miałeś, taką, brat, cieszę, rozmawiać, cała, czymś, wybacz, twarz, +mała, chcecie, dr, pojęcia, lubisz, głowę, najbardziej, dziwne, głowy, wody, pół, wiadomość, policja, +strony, l, pl, mogłem, mieli, widzenia, pewna, ruszaj, wracaj, ode, popatrz, końca, plan, kiedykolwiek, +wejść, została, rok, syna, uda, wrócę, zewnątrz, droga, uwierzyć, późno, zostało, zostanie, zły, kapitanie, +potrzebujemy, byliśmy, zobaczymy, gotowy, obchodzi, jechać, rodziny, widziałam, drodze, czeka, środku, film, +spać, człowiekiem, zupełnie, taa, pomóż, mieliśmy, pomoc, słowo, innym, ostatnio, and, zna, mogła, pójść, +chłopcy, wziąć, mógłbyś, tłumaczenie, potrzebujesz, słyszysz, blisko, godzin, miłości, góry, zabił, piękna, +napisów, pokaż, moi, lubi, robota, prawa, ciężko, kimś, dół, rękę, nazywam, wielkie, część, wkrótce, naszą, +jedziemy, zapomnij, prosto, radę, robimy, powinnaś, gdybyś, chociaż, zależy, stronie, wypadek, tydzień, byłaś, +nowe, małe, praca, drogę, chłopak, zrobi, widział, mieście, synu, oznacza, krew, mógłby, krwi, górę, joe, wasza, +robią, tędy, wszędzie, temat, pierwsze, zobacz, ponad, kraju, mało, racja, tymi, cicho, chciała, powiedziałam, +leci, powinno, mówiąc, serca, chciałabym, miasto, george, spotkać, mniej, e, przyjaciel, mówiłeś, kłopoty, +miesięcy, jakąś, żaden, zostań, roboty, zatrzymać, frank, nieważne, głupi, pa, koleś, sprawie, spotkanie, ojcze, +pewnego, spróbuj, drugi, znalazłem, pracować, całym, zostały, złe, niemożliwe, jakoś, zdjęcia, stronę, wiedzą, it, +dziewczynę, zaczyna, mogli, samego, sądzisz, rodzina, razu, trudno, samochodu, okay, boję, szkoda, wami, charlie, +dał, środka, ojcem, piękne, dawno, choć, panem, przykład, nagle, bracie, żadnej, drugiej, przyjaciół, otwórz, +myśleć, doktor, chwileczkę, pracuje, najlepszy, brata, czyż, często, http, powinnam, odejść, trzech, chodźcie, +nazwisko, szansę, ciała, policji, szkole, prawdopodobnie, serio, matki, org, wolno, sami, muszą, zabierz, +słyszałeś, siostra, uspokój, wystarczająco, początku, faceta, problemy, szefie, broni, me, zostawić, czuje, +będziecie, przyszedł, wiedziałam, kilku, inni, b, głowie, historia, według, www, wezmę, nowym, czekać, stój, +mężczyzna, mówiłam, pokazać, około, wracam, wieku, jakaś, pierwsza, niczym, zabiję, zdjęcie, zabawne, rodzice, +musiałem, całkowicie, sprawdzić, mike, przyjdzie, sześć, kupić, dobrym, żonę, dasz, pomoże, nogi, obok, ruszać, +trzymać, zadzwonić, panno, godzinę, boli, oraz, spokoju, walczyć, wróci, tom, wspólnego, zmienić, ostatnie, uwagę, +znać, jednej, dłużej, powie, pogadać, łatwo, większość, nikomu, michael, córka, niedługo, powodzenia, tygodniu, +włosy, niestety, górze, kochasz, prawdziwy, historii, ulicy, musicie, gotowi, chwila, samym, grać, zadzwonię, +strasznie, mieszka, kocha, rady, tyłu, jakim, obiecuję, tysięcy, pomyślałem, pracuję, jedynie, pozwolić, uwaga, proste, zacząć, myśl, wstawaj, rany, prawdziwe, takiej, jakiegoś, umrzeć, złego, okazji """ # Source: https://www.101languages.net/greek/most-common-greek-words/ alias words_el = """ - να, το, δεν, θα, είναι, και, μου, με, ο, για, την, σου, τα, τον, η, τι, σε, που, του, αυτό, στο, ότι, - από, τη, της, ναι, σας, ένα, εδώ, τους, αν, όχι, μια, μας, είσαι, αλλά, κι, οι, πρέπει, είμαι, ήταν, - πολύ, στην, δε, γιατί, εγώ, τώρα, πως, εντάξει, τις, κάτι, ξέρω, μην, έχει, έχω, εσύ, θέλω, καλά, - έτσι, στη, στον, αυτή, ξέρεις, κάνεις, εκεί, σαν, μόνο, μπορώ, όταν, έχεις, μαζί, πώς, τίποτα, - ευχαριστώ, όλα, κάνω, πάμε, ή, ποτέ, τόσο, πού, αυτά, έλα, στα, μέσα, κάνει, των, μπορεί, κύριε, πιο, - σπίτι, παρακαλώ, λοιπόν, μπορείς, αυτός, υπάρχει, ακόμα, πίσω, λίγο, πάντα, είμαστε, γεια, τότε, - ειναι, μετά, πω, έχουμε, μη, ένας, ποιος, νομίζω, πριν, απλά, δω, δουλειά, παιδιά, οχι, αλήθεια, - όλοι, ίσως, λες, όπως, ας, θέλεις, μα, άλλο, είπε, ζωή, πάω, δύο, ωραία, έναν, καλό, απο, κάνουμε, - έξω, κοίτα, είχε, στις, πάνω, είπα, πες, χρόνια, ούτε, κάτω, είστε, ώρα, θες, σένα, έχουν, γυναίκα, - μένα, μέρα, καλή, φορά, όμως, κανείς, κάθε, ε, οτι, αρέσει, ήμουν, μέχρι, δυο, είχα, μαμά, χωρίς, - καλύτερα, πας, πράγματα, πάει, σήμερα, κάποιος, ήθελα, θέλει, θεέ, έπρεπε, λέει, μία, σωστά, αυτόν, - μπορούμε, συμβαίνει, ακριβώς, έγινε, πόσο, επειδή, λεφτά, πολλά, μόλις, εμένα, λένε, πεις, συγγνώμη, - γρήγορα, ω, έκανε, λυπάμαι, γίνει, παιδί, περίμενε, έκανα, φίλε, βλέπω, μέρος, στιγμή, φαίνεται, + να, το, δεν, θα, είναι, και, μου, με, ο, για, την, σου, τα, τον, η, τι, σε, που, του, αυτό, στο, ότι, + από, τη, της, ναι, σας, ένα, εδώ, τους, αν, όχι, μια, μας, είσαι, αλλά, κι, οι, πρέπει, είμαι, ήταν, + πολύ, στην, δε, γιατί, εγώ, τώρα, πως, εντάξει, τις, κάτι, ξέρω, μην, έχει, έχω, εσύ, θέλω, καλά, + έτσι, στη, στον, αυτή, ξέρεις, κάνεις, εκεί, σαν, μόνο, μπορώ, όταν, έχεις, μαζί, πώς, τίποτα, + ευχαριστώ, όλα, κάνω, πάμε, ή, ποτέ, τόσο, πού, αυτά, έλα, στα, μέσα, κάνει, των, μπορεί, κύριε, πιο, + σπίτι, παρακαλώ, λοιπόν, μπορείς, αυτός, υπάρχει, ακόμα, πίσω, λίγο, πάντα, είμαστε, γεια, τότε, + ειναι, μετά, πω, έχουμε, μη, ένας, ποιος, νομίζω, πριν, απλά, δω, δουλειά, παιδιά, οχι, αλήθεια, + όλοι, ίσως, λες, όπως, ας, θέλεις, μα, άλλο, είπε, ζωή, πάω, δύο, ωραία, έναν, καλό, απο, κάνουμε, + έξω, κοίτα, είχε, στις, πάνω, είπα, πες, χρόνια, ούτε, κάτω, είστε, ώρα, θες, σένα, έχουν, γυναίκα, + μένα, μέρα, καλή, φορά, όμως, κανείς, κάθε, ε, οτι, αρέσει, ήμουν, μέχρι, δυο, είχα, μαμά, χωρίς, + καλύτερα, πας, πράγματα, πάει, σήμερα, κάποιος, ήθελα, θέλει, θεέ, έπρεπε, λέει, μία, σωστά, αυτόν, + μπορούμε, συμβαίνει, ακριβώς, έγινε, πόσο, επειδή, λεφτά, πολλά, μόλις, εμένα, λένε, πεις, συγγνώμη, + γρήγορα, ω, έκανε, λυπάμαι, γίνει, παιδί, περίμενε, έκανα, φίλε, βλέπω, μέρος, στιγμή, φαίνεται, πρόβλημα, άλλη, είπες, φυσικά, κάποιον, όσο, πήγαινε, πάλι, λάθος, ως, έχετε, εσένα, πράγμα, κυρία, - χρόνο, στους, πάρω, μπαμπά, δικό, απ, γίνεται, εσείς, λέω, συγνώμη, όλο, μητέρα, έκανες, πιστεύω, - ήσουν, κάποια, σίγουρα, υπάρχουν, όλη, ενα, αυτο, ξέρει, μωρό, ιδέα, δει, μάλλον, ίδιο, πάρε, είδα, - αύριο, βλέπεις, νέα, κόσμο, νομίζεις, τί, εμείς, σταμάτα, πάρει, αγάπη, πατέρας, όλους, αρκετά, - χρειάζεται, καιρό, φορές, κάνουν, ακόμη, α, πατέρα, προς, αμέσως, πια, ηταν, χαρά, απόψε, όνομα, - μάλιστα, μόνος, μεγάλη, κανένα, ελα, πραγματικά, αυτοί, πει, πότε, εχω, βράδυ, αυτές, θέλετε, κάνετε, - σημαίνει, πρώτη, ποιο, πόλη, μπορούσα, ποια, γαμώτο, ήδη, τελευταία, άνθρωποι, τέλος, απλώς, νόμιζα, - ξέρετε, μέρες, δεις, θέση, αυτούς, καταλαβαίνω, φύγε, χέρια, εκτός, ήξερα, οπότε, λεπτά, μακριά, - κάνε, αμάξι, δική, λεπτό, μεγάλο, μήπως, κορίτσι, μάτια, ελάτε, πρόκειται, πόρτα, δίκιο, βοήθεια, - ήρθε, μιλήσω, δρόμο, εαυτό, καθόλου, ορίστε, βρω, πειράζει, μπορείτε, καλός, πέρα, κοντά, εννοώ, - τέτοιο, μπροστά, έρθει, χρειάζομαι, χέρι, ελπίζω, δώσε, διάολο, φύγω, ιστορία, όπλο, αφού, πρωί, - νύχτα, ωραίο, τύπος, ξανά, θυμάσαι, δούμε, κατά, εννοείς, αγαπώ, κακό, θέμα, εδω, αυτήν, τρόπο, - κεφάλι, είχες, μερικές, μιλάς, φίλος, άνθρωπος, φύγουμε, όλες, σκατά, ανθρώπους, βέβαια, άντρας, - κάποιο, πάνε, αστυνομία, αλλιώς, συνέβη, χαίρομαι, άλλα, περισσότερο, καλύτερο, εκείνη, πάρεις, τo, - νερό, ώρες, σίγουρος, vα, τρεις, εχεις, πρώτα, μπορούσε, σ, οταν, δρ, πιστεύεις, μόνη, ποιός, καμιά, - κανέναν, πέθανε, εχει, ετσι, αγόρι, ανησυχείς, άντρες, δωμάτιο, ομάδα, ίδια, εμπρός, βρούμε, βοηθήσω, - τέτοια, πήρε, τρία, λόγο, μικρό, αντίο, o, πέντε, πήγε, καν, ευκαιρία, είδες, έρχεται, δηλαδή, - αργότερα, ήθελε, πούμε, λέμε, όπου, αλλα, κόρη, κόσμος, γυναίκες, τηλέφωνο, εάν, δώσω, καρδιά, βρήκα, - γραφείο, επίσης, νιώθω, σχέση, θέλουν, ισως, τέλεια, είχαμε, κάπου, μυαλό, ώστε, καλημέρα, σχολείο, - θεός, μικρή, τρέχει, ψέματα, ξέρουμε, οικογένεια, εισαι, θυμάμαι, κ, ενός, φίλοι, πρόσεχε, - καταλαβαίνεις, αργά, ντε, θέλουμε, σύντομα, πήρα, σχεδόν, παιχνίδι, κύριοι, γειά, μήνες, μπαμπάς, - σοβαρά, δολάρια, τουλάχιστον, χρήματα, πείτε, πόδια, αίμα, κοπέλα, φαγητό, ειμαι, ποιον, μερικά, - δύσκολο, μπορούν, βρεις, όμορφη, φύγεις, τύχη, πλάκα, έρθεις, άντρα, κορίτσια, μείνε, αστείο, καμία, - είχαν, χάρη, άλλος, πρεπει, σημασία, φυλακή, νεκρός, συγχωρείτε, φοβάμαι, μπράβο, γύρω, κανένας, μεταξύ, - τ, χθες, πολλές, όνομά, τζακ, ρε, καληνύχτα, πολυ, φύγει, αφήσω, ήθελες, tι, ήρθες, ακούς, πρώτο, γιατι, - ηρέμησε, γι, πάρουμε, πάρα, άλλους, κατάλαβα, έρθω, συνέχεια, έλεγα, γλυκιά, νοιάζει, χριστέ, βιβλίο, - κύριος, μ, χώρα, αρχή, ήρθα, πεθάνει, γη, έτοιμος, εγω, άσχημα, συμβεί, αυτοκίνητο, ζωής, τελικά, φέρω, - τρόπος, κατάσταση, www, περιμένω, σημαντικό, όσα, σκέφτηκα, μιλήσουμε, αφήστε, τωρα, ακούω, γιος, σκοτώσω, - δύναμη, κα, κε, εκείνο, γονείς, μιλάω, σκοτώσει, ολα, μείνει, μείνω, αρέσουν, δεv, υπόθεση, φίλους, όπλα, - υποθέτω, εμάς, ενώ, έξι, σχέδιο, άρεσε, καφέ, σκότωσε, χρειαζόμαστε, φίλο, σωστό, προσπαθώ, κάναμε, - κοιτάξτε, μoυ, κου, ποτό, εσάς, έι, έφυγε, ταινία, μοιάζει, κρεβάτι, εχουμε, περιμένει, νέο, μπορούσες, - μάθω, αφήσεις, περιμένετε, χρειάζεσαι, υπήρχε, μισό, δέκα, αφεντικό, περίπου, άλλοι, λόγος, ξέρουν, κάποτε, - βρήκες, καλύτερη, υπέροχο, τζον, δίπλα, σκάσε, θεού, άκουσα, φύγετε, λέξη, παρά, επόμενη, λέτε, περάσει, - πόσα, γίνεις, σώμα, ν, πήρες, τελείωσε, γιο, ρούχα, σκέφτομαι, εσυ, άλλες, γυρίσω, βάλω, μουσική, ραντεβού, - φωτιά, έδωσε, πάτε, φοβάσαι, βρει, δείξω, γίνω, βοηθήσει, τύπο, σειρά, αξίζει, μείνεις, είπαν, άλλον, - κυρίες, λίγη, πέρασε, κάτσε, πήγα, δείτε, μιας, βδομάδα, έρχομαι, προσοχή, εύκολο, ερώτηση, υπέροχα, - σίγουρη, νοσοκομείο, τρελός, ενας, βάλε, πόλεμο, φέρε, δικά, τιμή, κατάλαβες, ταξίδι, οποίο, δουλεύει, θεό, - μικρέ, μάθεις, βρίσκεται, πολλοί, δες, πάρτε, παντού, πρόσωπο, μήνυμα, αδερφή, μιλάει, παλιά, πουθενά, - κράτα, περίπτωση, φως, επάνω, έλεγε, συμφωνία, οπως, ολοι, πρώτος, δεσποινίς, γιατρός, γνωρίζω, σαμ, - σκέφτεσαι, ει, φίλη, σεξ, έκαναν, προβλήματα, κάπως, ό, τελευταίο, ακούσει, τζο, καλώς, επιλογή, - σταματήστε, τόσα, οτιδήποτε, περισσότερα, άδεια, πάρτι, περίμενα, ακούγεται, gmteam, ήξερες, καιρός, - μαλλιά, καλύτερος, κανεις, φρανκ, μέση, συνέχισε, τίποτε, φωτογραφία, κατι, μεγάλος, περιοχή, άσε, καθώς, - είδε, λόγια, μήνα, μαλακίες, όμορφο, δώρο, στόμα, χάλια, εντελώς, μακάρι, τελειώσει, γνώμη, γιατρέ, ξερω, - πλευρά, μέλλον, θάνατο, νιώθεις, έτοιμοι, κομμάτι, μάθει, μιλάμε, ψηλά, αέρα, ερωτήσεις, αυτού, δώσει, - φεύγω, σημείο, τηλεόραση, κυριε, πραγματικότητα, ανάγκη, βοηθήσεις, προσπάθησε, γύρνα, άφησε, λίγα, κάντε, - είvαι, βλέπετε, αυτη, δείπνο, επιτέλους, κέντρο, περίεργο, ακούστε, πλοίο, κάποιες, δικός, σoυ, οικογένειά, - μιλήσει, πλέον, υπόσχομαι, περιμένεις, ήξερε, σκοτώσεις, ενταξει, δώσεις, εκει, ήμασταν, έρχονται, κώλο, - ρωτήσω, παίρνει, σιγά, σήκω, στοιχεία, αδελφή, βασικά, μένει, άκρη, πηγαίνετε, παίρνεις, tο, περιμένουμε, - συγχωρείς, μικρός, πόδι, δίνει, εκατομμύρια, ξενοδοχείο, αποστολή, ενδιαφέρον, χάρηκα, αεροπλάνο, γάμο, - χιλιάδες, υόρκη, οκ, ευχαριστούμε, καλα, κοιτάς, σα, π, χρόνος, ησυχία, ασφάλεια, εκείνος, a, βρήκε, - τέσσερα, βγάλω, μπες, συχνά, ημέρα, μάνα, εν, αγαπάς, άνθρωπο, γραμμή, φωτογραφίες, προσέχεις, ύπνο, - μυστικό, σχετικά, είδους, σκέψου, χριστούγεννα, κόσμου, τομ, μισώ, σύστημα, δουλειές, τελείως, πεθάνω, - αλλάξει, δεξιά, συνήθως, δουλεύω, μάικλ, εβδομάδα, νούμερο, λείπει, έτοιμη, τμήμα, βγει, ψυχή, έπεσε, - κάθαρμα, ματιά, οποία, πληροφορίες, μονο, κρίμα, τραγούδι, μαγαζί, δουλεύεις, μαζι, τέλειο, κύριο, - λέγεται, τσάρλι, πεθάνεις, σκεφτόμουν, καλησπέρα, συγχαρητήρια, φωνή, εκ, άτομο, παίζεις, σκάφος, - φαίνεσαι, ξαφνικά, παραπάνω, ατύχημα, θελω, ξέχνα, ήρθατε, εναντίον, τραπέζι, γράμμα, μείνετε, αμερική, - βασιλιάς, υπό, μπάνιο, ποτε, ίδιος, προφανώς, μαλάκα, αδερφός, άνδρες, nαι, χρονών, ναί, κλειδί, δις, - γιαγιά, παράξενο, πτώμα, βρήκαμε, μιλήσεις, υποτίθεται, ορκίζομαι, δυνατά, ποιό, θάλασσα, παίρνω, άκουσες, - παρέα, αριστερά, έμαθα, μάχη, μηχανή, σάρα, ζωντανός, όνειρο, παλιό, μπορούσαμε, πάντως, ανάμεσα, έχασα, - νωρίς, κάποιοι, άκου, παίζει, φτάνει, δίνω, βγες, υπέροχη, νόημα, έλεγχο, μέτρα, ξερεις, ζει, δείχνει, + χρόνο, στους, πάρω, μπαμπά, δικό, απ, γίνεται, εσείς, λέω, συγνώμη, όλο, μητέρα, έκανες, πιστεύω, + ήσουν, κάποια, σίγουρα, υπάρχουν, όλη, ενα, αυτο, ξέρει, μωρό, ιδέα, δει, μάλλον, ίδιο, πάρε, είδα, + αύριο, βλέπεις, νέα, κόσμο, νομίζεις, τί, εμείς, σταμάτα, πάρει, αγάπη, πατέρας, όλους, αρκετά, + χρειάζεται, καιρό, φορές, κάνουν, ακόμη, α, πατέρα, προς, αμέσως, πια, ηταν, χαρά, απόψε, όνομα, + μάλιστα, μόνος, μεγάλη, κανένα, ελα, πραγματικά, αυτοί, πει, πότε, εχω, βράδυ, αυτές, θέλετε, κάνετε, + σημαίνει, πρώτη, ποιο, πόλη, μπορούσα, ποια, γαμώτο, ήδη, τελευταία, άνθρωποι, τέλος, απλώς, νόμιζα, + ξέρετε, μέρες, δεις, θέση, αυτούς, καταλαβαίνω, φύγε, χέρια, εκτός, ήξερα, οπότε, λεπτά, μακριά, + κάνε, αμάξι, δική, λεπτό, μεγάλο, μήπως, κορίτσι, μάτια, ελάτε, πρόκειται, πόρτα, δίκιο, βοήθεια, + ήρθε, μιλήσω, δρόμο, εαυτό, καθόλου, ορίστε, βρω, πειράζει, μπορείτε, καλός, πέρα, κοντά, εννοώ, + τέτοιο, μπροστά, έρθει, χρειάζομαι, χέρι, ελπίζω, δώσε, διάολο, φύγω, ιστορία, όπλο, αφού, πρωί, + νύχτα, ωραίο, τύπος, ξανά, θυμάσαι, δούμε, κατά, εννοείς, αγαπώ, κακό, θέμα, εδω, αυτήν, τρόπο, + κεφάλι, είχες, μερικές, μιλάς, φίλος, άνθρωπος, φύγουμε, όλες, σκατά, ανθρώπους, βέβαια, άντρας, + κάποιο, πάνε, αστυνομία, αλλιώς, συνέβη, χαίρομαι, άλλα, περισσότερο, καλύτερο, εκείνη, πάρεις, τo, + νερό, ώρες, σίγουρος, vα, τρεις, εχεις, πρώτα, μπορούσε, σ, οταν, δρ, πιστεύεις, μόνη, ποιός, καμιά, + κανέναν, πέθανε, εχει, ετσι, αγόρι, ανησυχείς, άντρες, δωμάτιο, ομάδα, ίδια, εμπρός, βρούμε, βοηθήσω, + τέτοια, πήρε, τρία, λόγο, μικρό, αντίο, o, πέντε, πήγε, καν, ευκαιρία, είδες, έρχεται, δηλαδή, + αργότερα, ήθελε, πούμε, λέμε, όπου, αλλα, κόρη, κόσμος, γυναίκες, τηλέφωνο, εάν, δώσω, καρδιά, βρήκα, + γραφείο, επίσης, νιώθω, σχέση, θέλουν, ισως, τέλεια, είχαμε, κάπου, μυαλό, ώστε, καλημέρα, σχολείο, + θεός, μικρή, τρέχει, ψέματα, ξέρουμε, οικογένεια, εισαι, θυμάμαι, κ, ενός, φίλοι, πρόσεχε, + καταλαβαίνεις, αργά, ντε, θέλουμε, σύντομα, πήρα, σχεδόν, παιχνίδι, κύριοι, γειά, μήνες, μπαμπάς, + σοβαρά, δολάρια, τουλάχιστον, χρήματα, πείτε, πόδια, αίμα, κοπέλα, φαγητό, ειμαι, ποιον, μερικά, + δύσκολο, μπορούν, βρεις, όμορφη, φύγεις, τύχη, πλάκα, έρθεις, άντρα, κορίτσια, μείνε, αστείο, καμία, + είχαν, χάρη, άλλος, πρεπει, σημασία, φυλακή, νεκρός, συγχωρείτε, φοβάμαι, μπράβο, γύρω, κανένας, μεταξύ, + τ, χθες, πολλές, όνομά, τζακ, ρε, καληνύχτα, πολυ, φύγει, αφήσω, ήθελες, tι, ήρθες, ακούς, πρώτο, γιατι, + ηρέμησε, γι, πάρουμε, πάρα, άλλους, κατάλαβα, έρθω, συνέχεια, έλεγα, γλυκιά, νοιάζει, χριστέ, βιβλίο, + κύριος, μ, χώρα, αρχή, ήρθα, πεθάνει, γη, έτοιμος, εγω, άσχημα, συμβεί, αυτοκίνητο, ζωής, τελικά, φέρω, + τρόπος, κατάσταση, www, περιμένω, σημαντικό, όσα, σκέφτηκα, μιλήσουμε, αφήστε, τωρα, ακούω, γιος, σκοτώσω, + δύναμη, κα, κε, εκείνο, γονείς, μιλάω, σκοτώσει, ολα, μείνει, μείνω, αρέσουν, δεv, υπόθεση, φίλους, όπλα, + υποθέτω, εμάς, ενώ, έξι, σχέδιο, άρεσε, καφέ, σκότωσε, χρειαζόμαστε, φίλο, σωστό, προσπαθώ, κάναμε, + κοιτάξτε, μoυ, κου, ποτό, εσάς, έι, έφυγε, ταινία, μοιάζει, κρεβάτι, εχουμε, περιμένει, νέο, μπορούσες, + μάθω, αφήσεις, περιμένετε, χρειάζεσαι, υπήρχε, μισό, δέκα, αφεντικό, περίπου, άλλοι, λόγος, ξέρουν, κάποτε, + βρήκες, καλύτερη, υπέροχο, τζον, δίπλα, σκάσε, θεού, άκουσα, φύγετε, λέξη, παρά, επόμενη, λέτε, περάσει, + πόσα, γίνεις, σώμα, ν, πήρες, τελείωσε, γιο, ρούχα, σκέφτομαι, εσυ, άλλες, γυρίσω, βάλω, μουσική, ραντεβού, + φωτιά, έδωσε, πάτε, φοβάσαι, βρει, δείξω, γίνω, βοηθήσει, τύπο, σειρά, αξίζει, μείνεις, είπαν, άλλον, + κυρίες, λίγη, πέρασε, κάτσε, πήγα, δείτε, μιας, βδομάδα, έρχομαι, προσοχή, εύκολο, ερώτηση, υπέροχα, + σίγουρη, νοσοκομείο, τρελός, ενας, βάλε, πόλεμο, φέρε, δικά, τιμή, κατάλαβες, ταξίδι, οποίο, δουλεύει, θεό, + μικρέ, μάθεις, βρίσκεται, πολλοί, δες, πάρτε, παντού, πρόσωπο, μήνυμα, αδερφή, μιλάει, παλιά, πουθενά, + κράτα, περίπτωση, φως, επάνω, έλεγε, συμφωνία, οπως, ολοι, πρώτος, δεσποινίς, γιατρός, γνωρίζω, σαμ, + σκέφτεσαι, ει, φίλη, σεξ, έκαναν, προβλήματα, κάπως, ό, τελευταίο, ακούσει, τζο, καλώς, επιλογή, + σταματήστε, τόσα, οτιδήποτε, περισσότερα, άδεια, πάρτι, περίμενα, ακούγεται, gmteam, ήξερες, καιρός, + μαλλιά, καλύτερος, κανεις, φρανκ, μέση, συνέχισε, τίποτε, φωτογραφία, κατι, μεγάλος, περιοχή, άσε, καθώς, + είδε, λόγια, μήνα, μαλακίες, όμορφο, δώρο, στόμα, χάλια, εντελώς, μακάρι, τελειώσει, γνώμη, γιατρέ, ξερω, + πλευρά, μέλλον, θάνατο, νιώθεις, έτοιμοι, κομμάτι, μάθει, μιλάμε, ψηλά, αέρα, ερωτήσεις, αυτού, δώσει, + φεύγω, σημείο, τηλεόραση, κυριε, πραγματικότητα, ανάγκη, βοηθήσεις, προσπάθησε, γύρνα, άφησε, λίγα, κάντε, + είvαι, βλέπετε, αυτη, δείπνο, επιτέλους, κέντρο, περίεργο, ακούστε, πλοίο, κάποιες, δικός, σoυ, οικογένειά, + μιλήσει, πλέον, υπόσχομαι, περιμένεις, ήξερε, σκοτώσεις, ενταξει, δώσεις, εκει, ήμασταν, έρχονται, κώλο, + ρωτήσω, παίρνει, σιγά, σήκω, στοιχεία, αδελφή, βασικά, μένει, άκρη, πηγαίνετε, παίρνεις, tο, περιμένουμε, + συγχωρείς, μικρός, πόδι, δίνει, εκατομμύρια, ξενοδοχείο, αποστολή, ενδιαφέρον, χάρηκα, αεροπλάνο, γάμο, + χιλιάδες, υόρκη, οκ, ευχαριστούμε, καλα, κοιτάς, σα, π, χρόνος, ησυχία, ασφάλεια, εκείνος, a, βρήκε, + τέσσερα, βγάλω, μπες, συχνά, ημέρα, μάνα, εν, αγαπάς, άνθρωπο, γραμμή, φωτογραφίες, προσέχεις, ύπνο, + μυστικό, σχετικά, είδους, σκέψου, χριστούγεννα, κόσμου, τομ, μισώ, σύστημα, δουλειές, τελείως, πεθάνω, + αλλάξει, δεξιά, συνήθως, δουλεύω, μάικλ, εβδομάδα, νούμερο, λείπει, έτοιμη, τμήμα, βγει, ψυχή, έπεσε, + κάθαρμα, ματιά, οποία, πληροφορίες, μονο, κρίμα, τραγούδι, μαγαζί, δουλεύεις, μαζι, τέλειο, κύριο, + λέγεται, τσάρλι, πεθάνεις, σκεφτόμουν, καλησπέρα, συγχαρητήρια, φωνή, εκ, άτομο, παίζεις, σκάφος, + φαίνεσαι, ξαφνικά, παραπάνω, ατύχημα, θελω, ξέχνα, ήρθατε, εναντίον, τραπέζι, γράμμα, μείνετε, αμερική, + βασιλιάς, υπό, μπάνιο, ποτε, ίδιος, προφανώς, μαλάκα, αδερφός, άνδρες, nαι, χρονών, ναί, κλειδί, δις, + γιαγιά, παράξενο, πτώμα, βρήκαμε, μιλήσεις, υποτίθεται, ορκίζομαι, δυνατά, ποιό, θάλασσα, παίρνω, άκουσες, + παρέα, αριστερά, έμαθα, μάχη, μηχανή, σάρα, ζωντανός, όνειρο, παλιό, μπορούσαμε, πάντως, ανάμεσα, έχασα, + νωρίς, κάποιοι, άκου, παίζει, φτάνει, δίνω, βγες, υπέροχη, νόημα, έλεγχο, μέτρα, ξερεις, ζει, δείχνει, βρες, τού """ # Source: https://www.101languages.net/russian/most-common-russian-words/ alias words_ru = """ -я, не, что, в, и, ты, это, на, с, он, вы, как, мы, да, а, мне, меня, у, нет, так, но, то, все, тебя, его, -за, о, она, тебе, если, они, бы, же, ну, здесь, к, из, есть, чтобы, для, хорошо, когда, вас, только, по, -вот, просто, был, знаю, нас, всё, было, от, может, кто, вам, очень, их, там, будет, уже, почему, еще, -быть, где, спасибо, ничего, сейчас, или, могу, хочу, нам, чем, мой, до, надо, этого, ее, теперь, давай, -знаешь, нужно, больше, этом, нибудь, раз, со, была, этот, ему, ладно, эй, время, тоже, даже, хочешь, -сказал, ли, себя, думаю, пока, должен, потому, никогда, ни, тут, ещё, её, пожалуйста, сюда, привет, -тогда, конечно, моя, него, сегодня, один, тобой, правда, лучше, об, были, того, можно, мной, всегда, -сказать, день, сэр, без, можешь, чего, эти, дело, значит, лет, много, во, делать, буду, порядке, должны, -такой, ведь, ним, всего, сделать, хотел, твой, жизнь, ей, мистер, потом, через, себе, них, всех, такое, -им, куда, том, мама, после, человек, люди, слишком, иди, зачем, этим, немного, сколько, этой, знаете, -боже, ней, эту, который, отец, свою, деньги, два, под, твоя, мои, никто, моей, думаешь, друг, жизни, -эта, назад, видел, кажется, точно, вместе, люблю, мог, случилось, сам, нравится, черт, какой, людей, -папа, домой, тот, скажи, которые, должна, три, всем, сделал, возможно, прошу, будем, дома, парень, -снова, говорит, место, отсюда, можем, будешь, пошли, делаешь, совсем, говорил, понимаю, завтра, хочет, -простите, разве, давайте, хотите, отлично, сказала, туда, прямо, времени, вами, лишь, своей, хватит, -думал, можете, дом, дела, знать, дай, понял, помочь, говорить, слушай, свои, поэтому, прости, знает, -именно, знал, тем, кого, смотри, каждый, ваш, похоже, найти, моего, наш, мать, одна, имя, про, говорю, -будут, оно, свой, нельзя, извините, стоит, действительно, зовут, поговорить, доктор, перед, несколько, -нужен, происходит, ко, господи, возьми, мою, тех, нами, вижу, должно, наверное, откуда, понимаешь, верно, -скоро, уж, деле, твои, пусть, всю, хотела, при, более, ребята, нее, быстро, подожди, идти, надеюсь, чём, -работу, видеть, такая, этих, уверен, нужна, года, раньше, такие, руки, видишь, какая, посмотри, сын, -самом, ваша, послушай, равно, наши, другой, ага, мир, извини, минут, против, твоей, пор, жить, ж, жаль, -вообще, могли, хотя, человека, пора, ради, говорят, почти, твою, могут, над, весь, первый, чёрт, слышал, -собой, брат, вещи, дня, скажу, говоришь, нормально, своего, мое, ваше, итак, будь, ночь, хоть, ясно, -плохо, дверь, вопрос, господин, давно, денег, ваши, ка, мисс, одну, глаза, пять, будто, между, пойду, -опять, работа, самое, иногда, детей, этому, рад, здорово, бог, одного, ночи, готов, номер, которая, -машину, любовь, дорогая, виду, одно, прекрасно, вон, своих, быстрее, отца, женщина, достаточно, рядом, -убить, таким, пойдем, смерти, дети, такого, правильно, месте, никаких, сказали, здравствуйте, пару, две, -видела, долго, хороший, ах, кроме, алло, нашей, прав, вчера, вечером, жена, миссис, чтоб, друга, нужны, -кем, какие, те, увидеть, утро, смогу, неё, сама, моему, большой, сразу, работать, сердце, стал, своим, -сначала, могла, вроде, ними, говори, голову, дальше, помнишь, либо, ума, одной, вечер, случае, взять, -проблемы, помощь, добрый, год, думала, делает, скорее, слова, капитан, последний, важно, дней, помню, -ночью, утром, моих, произошло, которую, боюсь, также, вашей, ой, стой, твоего, никого, дорогой, убил, -насчет, друзья, самый, проблема, видели, вперед, дерьмо, понятно, чувствую, наша, будете, тому, имею, -вернуться, придется, пришел, спать, стать, столько, говорила, пойти, иначе, работает, девушка, час, -момент, моим, умер, думаете, доброе, слово, новый, часов, мире, знаем, твое, мальчик, однажды, интересно, -конец, играть, a, заткнись, сделали, посмотреть, идет, узнать, свое, права, хорошая, город, джон, -долларов, парни, идем, говорите, уйти, понять, знала, поздно, нашли, работы, скажите, сделаю, увидимся, -какого, другие, идея, пошел, доме, дочь, имеет, приятно, лицо, наших, обо, понимаете, руку, часть, -смотрите, вся, собираюсь, четыре, прежде, хотят, скажешь, чувак, дайте, сделала, кофе, джек, верю, -ждать, затем, большое, сами, неужели, моё, любит, мужчина, дать, господа, таких, осталось, которой, -далеко, вернусь, сильно, ох, сможешь, кому, вашего, посмотрим, машина, подождите, свет, чуть, серьезно, -пришли, оружие, решил, смысле, видите, тихо, нашел, свидания, путь, той, совершенно, следующий, которого, -места, парня, вдруг, пути, мадам, какое, шанс, сестра, нашего, ужасно, минуту, вокруг, другом, иду, -других, хотели, нем, смерть, подумал, фильм, оставь, делаете, уверена, кровь, говорили, внимание, -помогите, идите, держи, получить, оба, взял, спокойно, обычно, мало, забыл, странно, смотреть, поехали, -дал, часа, прекрати, посмотрите, готовы, вернулся, поверить, позже, милая, женщины, любишь, довольно, -обратно, остаться, думать, та, стороны, полиция, тело, тысяч, делал, машины, угодно, муж, году, неплохо, -бога, некоторые, конце, милый, the, рождения, трудно, добро, любви, больно, невозможно, спокойной, -слышишь, типа, получил, которое, приятель, хуже, никому, честь, успокойся, вашу, маленький, выглядит, -чарли, сына, неделю, i, девочка, делаю, шесть, ноги, история, рассказать, послушайте, часто, кстати, -двух, забудь, которых, следует, знают, пришла, семья, станет, матери, ребенок, план, проблем, например, -сделай, воды, немедленно, мира, сэм, телефон, перестань, правду, второй, прощения, ту, наше, уходи, твоих, -помоги, пол, внутри, нему, смог, десять, нашу, около, бывает, самого, большая, леди, сможем, вниз, легко, -делай, единственный, рада, меньше, волнуйся, хотим, полагаю, мам, иметь, своими, мере, наконец, начала, -минутку, работе, пожаловать, другого, двое, никакого, честно, школе, лучший, умереть, дам, насколько, -всей, малыш, оставить, безопасности, ненавижу, школу, осторожно, сынок, джо, таки, пытался, другое, б, -клянусь, машине, недели, стало, истории, пришлось, выглядишь, чему, сможет, купить, слышала, знали, -настоящий, сих, выйти, людям, замечательно, полиции, огонь, пойдём, спросить, дядя, детка, среди, особенно, -твоим, комнате, шоу, выпить, постоянно, делают, позвольте, родители, письмо, городе, случай, месяцев, мужик, -благодарю, o, ребенка, смешно, ответ, города, образом, любой, полностью, увидел, еду, имени, вместо, -абсолютно, обязательно, улице, твоё, убили, ваших, ехать, крови, решение, вина, поможет, своё, секунду, -обещаю, начать, голос, вещь, друзей, показать, нечего, э, месяц, подарок, приехал, самая, молодец, сделаем, -крайней, женщин, собираешься, конца, страшно, новости, идиот, потерял, спасти, вернуть, узнал, слушайте, -хотелось, сон, поняла, прошло, комнату, семь, погоди, главное, рано, корабль, пытаюсь, игра, умерла, -повезло, всему, возьму, таком, моем, глаз, настолько, идём, удачи, готова, семьи, садись, гарри, держись, -звучит, мило, война, человеком, право, такую, вопросы, представить, работаю, имеешь, красивая, идёт, никакой, -профессор, думает, войны, стала, стали, оттуда, известно, слышу, начал, подумать, позвонить, старый, придётся, -историю, вести, твоему, последнее, хочется, миллионов, нашла, способ, отношения, земле, фрэнк, получится, -говоря, связи, многие, пошёл, пистолет, убью, руках, получилось, президент, остановить, тьi, оставил, одним, -you, утра, боль, хорошие, пришёл, открой, брось, вставай, находится, поговорим, кино, людьми, полицию, покажу, +я, не, что, в, и, ты, это, на, с, он, вы, как, мы, да, а, мне, меня, у, нет, так, но, то, все, тебя, его, +за, о, она, тебе, если, они, бы, же, ну, здесь, к, из, есть, чтобы, для, хорошо, когда, вас, только, по, +вот, просто, был, знаю, нас, всё, было, от, может, кто, вам, очень, их, там, будет, уже, почему, еще, +быть, где, спасибо, ничего, сейчас, или, могу, хочу, нам, чем, мой, до, надо, этого, ее, теперь, давай, +знаешь, нужно, больше, этом, нибудь, раз, со, была, этот, ему, ладно, эй, время, тоже, даже, хочешь, +сказал, ли, себя, думаю, пока, должен, потому, никогда, ни, тут, ещё, её, пожалуйста, сюда, привет, +тогда, конечно, моя, него, сегодня, один, тобой, правда, лучше, об, были, того, можно, мной, всегда, +сказать, день, сэр, без, можешь, чего, эти, дело, значит, лет, много, во, делать, буду, порядке, должны, +такой, ведь, ним, всего, сделать, хотел, твой, жизнь, ей, мистер, потом, через, себе, них, всех, такое, +им, куда, том, мама, после, человек, люди, слишком, иди, зачем, этим, немного, сколько, этой, знаете, +боже, ней, эту, который, отец, свою, деньги, два, под, твоя, мои, никто, моей, думаешь, друг, жизни, +эта, назад, видел, кажется, точно, вместе, люблю, мог, случилось, сам, нравится, черт, какой, людей, +папа, домой, тот, скажи, которые, должна, три, всем, сделал, возможно, прошу, будем, дома, парень, +снова, говорит, место, отсюда, можем, будешь, пошли, делаешь, совсем, говорил, понимаю, завтра, хочет, +простите, разве, давайте, хотите, отлично, сказала, туда, прямо, времени, вами, лишь, своей, хватит, +думал, можете, дом, дела, знать, дай, понял, помочь, говорить, слушай, свои, поэтому, прости, знает, +именно, знал, тем, кого, смотри, каждый, ваш, похоже, найти, моего, наш, мать, одна, имя, про, говорю, +будут, оно, свой, нельзя, извините, стоит, действительно, зовут, поговорить, доктор, перед, несколько, +нужен, происходит, ко, господи, возьми, мою, тех, нами, вижу, должно, наверное, откуда, понимаешь, верно, +скоро, уж, деле, твои, пусть, всю, хотела, при, более, ребята, нее, быстро, подожди, идти, надеюсь, чём, +работу, видеть, такая, этих, уверен, нужна, года, раньше, такие, руки, видишь, какая, посмотри, сын, +самом, ваша, послушай, равно, наши, другой, ага, мир, извини, минут, против, твоей, пор, жить, ж, жаль, +вообще, могли, хотя, человека, пора, ради, говорят, почти, твою, могут, над, весь, первый, чёрт, слышал, +собой, брат, вещи, дня, скажу, говоришь, нормально, своего, мое, ваше, итак, будь, ночь, хоть, ясно, +плохо, дверь, вопрос, господин, давно, денег, ваши, ка, мисс, одну, глаза, пять, будто, между, пойду, +опять, работа, самое, иногда, детей, этому, рад, здорово, бог, одного, ночи, готов, номер, которая, +машину, любовь, дорогая, виду, одно, прекрасно, вон, своих, быстрее, отца, женщина, достаточно, рядом, +убить, таким, пойдем, смерти, дети, такого, правильно, месте, никаких, сказали, здравствуйте, пару, две, +видела, долго, хороший, ах, кроме, алло, нашей, прав, вчера, вечером, жена, миссис, чтоб, друга, нужны, +кем, какие, те, увидеть, утро, смогу, неё, сама, моему, большой, сразу, работать, сердце, стал, своим, +сначала, могла, вроде, ними, говори, голову, дальше, помнишь, либо, ума, одной, вечер, случае, взять, +проблемы, помощь, добрый, год, думала, делает, скорее, слова, капитан, последний, важно, дней, помню, +ночью, утром, моих, произошло, которую, боюсь, также, вашей, ой, стой, твоего, никого, дорогой, убил, +насчет, друзья, самый, проблема, видели, вперед, дерьмо, понятно, чувствую, наша, будете, тому, имею, +вернуться, придется, пришел, спать, стать, столько, говорила, пойти, иначе, работает, девушка, час, +момент, моим, умер, думаете, доброе, слово, новый, часов, мире, знаем, твое, мальчик, однажды, интересно, +конец, играть, a, заткнись, сделали, посмотреть, идет, узнать, свое, права, хорошая, город, джон, +долларов, парни, идем, говорите, уйти, понять, знала, поздно, нашли, работы, скажите, сделаю, увидимся, +какого, другие, идея, пошел, доме, дочь, имеет, приятно, лицо, наших, обо, понимаете, руку, часть, +смотрите, вся, собираюсь, четыре, прежде, хотят, скажешь, чувак, дайте, сделала, кофе, джек, верю, +ждать, затем, большое, сами, неужели, моё, любит, мужчина, дать, господа, таких, осталось, которой, +далеко, вернусь, сильно, ох, сможешь, кому, вашего, посмотрим, машина, подождите, свет, чуть, серьезно, +пришли, оружие, решил, смысле, видите, тихо, нашел, свидания, путь, той, совершенно, следующий, которого, +места, парня, вдруг, пути, мадам, какое, шанс, сестра, нашего, ужасно, минуту, вокруг, другом, иду, +других, хотели, нем, смерть, подумал, фильм, оставь, делаете, уверена, кровь, говорили, внимание, +помогите, идите, держи, получить, оба, взял, спокойно, обычно, мало, забыл, странно, смотреть, поехали, +дал, часа, прекрати, посмотрите, готовы, вернулся, поверить, позже, милая, женщины, любишь, довольно, +обратно, остаться, думать, та, стороны, полиция, тело, тысяч, делал, машины, угодно, муж, году, неплохо, +бога, некоторые, конце, милый, the, рождения, трудно, добро, любви, больно, невозможно, спокойной, +слышишь, типа, получил, которое, приятель, хуже, никому, честь, успокойся, вашу, маленький, выглядит, +чарли, сына, неделю, i, девочка, делаю, шесть, ноги, история, рассказать, послушайте, часто, кстати, +двух, забудь, которых, следует, знают, пришла, семья, станет, матери, ребенок, план, проблем, например, +сделай, воды, немедленно, мира, сэм, телефон, перестань, правду, второй, прощения, ту, наше, уходи, твоих, +помоги, пол, внутри, нему, смог, десять, нашу, около, бывает, самого, большая, леди, сможем, вниз, легко, +делай, единственный, рада, меньше, волнуйся, хотим, полагаю, мам, иметь, своими, мере, наконец, начала, +минутку, работе, пожаловать, другого, двое, никакого, честно, школе, лучший, умереть, дам, насколько, +всей, малыш, оставить, безопасности, ненавижу, школу, осторожно, сынок, джо, таки, пытался, другое, б, +клянусь, машине, недели, стало, истории, пришлось, выглядишь, чему, сможет, купить, слышала, знали, +настоящий, сих, выйти, людям, замечательно, полиции, огонь, пойдём, спросить, дядя, детка, среди, особенно, +твоим, комнате, шоу, выпить, постоянно, делают, позвольте, родители, письмо, городе, случай, месяцев, мужик, +благодарю, o, ребенка, смешно, ответ, города, образом, любой, полностью, увидел, еду, имени, вместо, +абсолютно, обязательно, улице, твоё, убили, ваших, ехать, крови, решение, вина, поможет, своё, секунду, +обещаю, начать, голос, вещь, друзей, показать, нечего, э, месяц, подарок, приехал, самая, молодец, сделаем, +крайней, женщин, собираешься, конца, страшно, новости, идиот, потерял, спасти, вернуть, узнал, слушайте, +хотелось, сон, поняла, прошло, комнату, семь, погоди, главное, рано, корабль, пытаюсь, игра, умерла, +повезло, всему, возьму, таком, моем, глаз, настолько, идём, удачи, готова, семьи, садись, гарри, держись, +звучит, мило, война, человеком, право, такую, вопросы, представить, работаю, имеешь, красивая, идёт, никакой, +профессор, думает, войны, стала, стали, оттуда, известно, слышу, начал, подумать, позвонить, старый, придётся, +историю, вести, твоему, последнее, хочется, миллионов, нашла, способ, отношения, земле, фрэнк, получится, +говоря, связи, многие, пошёл, пистолет, убью, руках, получилось, президент, остановить, тьi, оставил, одним, +you, утра, боль, хорошие, пришёл, открой, брось, вставай, находится, поговорим, кино, людьми, полицию, покажу, волосы, последние, брата, месяца """ @@ -747,7 +747,8 @@ def test_fill_factor(): assert_fill_factor["AR"](words, len(words) // 4, 0.98) assert_fill_factor["AR"](words, len(words) // 13, 1.0) - assert_fill_factor_old_hash["AR"](words, len(words), 0.59) + # TODO: flaky test + # assert_fill_factor_old_hash["AR"](words, len(words), 0.59) words = gen_word_pairs[words_el]() assert_fill_factor["EL"](words, len(words), 0.63) @@ -777,7 +778,7 @@ def test_fill_factor(): assert_fill_factor["LV"](words, len(words), 0.63) assert_fill_factor["LV"](words, len(words) // 2, 0.86) assert_fill_factor["LV"](words, len(words) // 4, 0.98) - assert_fill_factor["LV"](words, len(words) // 13, 1.0) + assert_fill_factor["LV"](words, len(words) // 13, 0.99) assert_fill_factor_old_hash["LV"](words, len(words), 0.015) From bc09dd31ab1c3ea4f0582bc6f2f404c4abda87c1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 18 Oct 2024 10:59:44 -0500 Subject: [PATCH 1779/2019] [stdlib] feat: Add additional minor binding utilities MODULAR_ORIG_COMMIT_REV_ID: f7cd7f188de7424f9afde75a8df611008ff7c873 --- stdlib/src/builtin/_pybind.mojo | 39 ++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 7e39bd775c..c99db3fe0a 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -26,7 +26,16 @@ from python._cpython import ( PyType_Spec, CPython, ) -from python._bindings import Pythonable, PyMojoObject +from python._bindings import ( + Pythonable, + PyMojoObject, + create_wrapper_function, + check_argument_type, + # Imported for use by the compiler + check_arguments_arity, +) + +from collections import Optional alias PyModule = TypedPythonObject["Module"] @@ -85,3 +94,31 @@ fn gen_pytype_wrapper[ Python.add_object( pointer_bitcast[PyModule](Pointer.address_of(module))[], name, type_obj ) + + +fn add_wrapper_to_module[ + wrapper_func: fn ( + PythonObject, TypedPythonObject["Tuple"] + ) raises -> PythonObject, + func_name: StringLiteral, +](inout module_obj: PythonObject) raises: + var module = TypedPythonObject["Module"](unsafe_unchecked_from=module_obj) + Python.add_functions( + module, + List[PyMethodDef]( + PyMethodDef.function[ + create_wrapper_function[wrapper_func](), func_name + ]() + ), + ) + + +fn check_and_get_arg[ + T: Pythonable +]( + func_name: StringLiteral, + type_name_id: StringLiteral, + py_args: TypedPythonObject["Tuple"], + index: Int, +) raises -> UnsafePointer[T]: + return check_argument_type[T](func_name, type_name_id, py_args[index]) From f52b16b365ac12727fd800ddba8da964081aa074 Mon Sep 17 00:00:00 2001 From: Helehex Date: Fri, 18 Oct 2024 10:21:55 -0600 Subject: [PATCH 1780/2019] [External] [docs] Fix docstring examples in `Variant`. (#49365) [External] [docs] Fix docstring examples in `Variant`. Since `Variant.__getitem__()` uses auto-deref now. Co-authored-by: Helehex Closes modularml/mojo#3689 MODULAR_ORIG_COMMIT_REV_ID: 9bd494c759d5639b0ee16dc092d627867f8e843e --- stdlib/src/utils/variant.mojo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index c79a3be725..34546cba4b 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -20,9 +20,9 @@ from utils import Variant alias IntOrString = Variant[Int, String] fn to_string(inout x: IntOrString) -> String: if x.isa[String](): - return x[String][] + return x[String] # x.isa[Int]() - return str(x[Int])[] + return str(x[Int]) # They have to be mutable for now, and implement CollectionElement var an_int = IntOrString(4) @@ -83,9 +83,9 @@ struct Variant[*Ts: CollectionElement]( alias IntOrString = Variant[Int, String] fn to_string(inout x: IntOrString) -> String: if x.isa[String](): - return x[String][] + return x[String] # x.isa[Int]() - return str(x[Int][]) + return str(x[Int]) # They have to be mutable for now, and implement CollectionElement var an_int = IntOrString(4) From 9a5af9b4b259ba00f96f1219b6d226f96ece7457 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 18 Oct 2024 09:35:55 -0700 Subject: [PATCH 1781/2019] [stdlib] Removing `String._strref_dangerous` usage from stdlib.file MODULAR_ORIG_COMMIT_REV_ID: 668c77a6b9c949e1072749f8d51ed0aa6c07136b --- stdlib/src/builtin/file.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 1599efb1c2..8313f1ed65 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -34,7 +34,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call, sizeof from sys.ffi import OpaquePointer -from utils import Span, StringRef +from utils import Span, StringRef, StringSlice from memory import AddressSpace, UnsafePointer @@ -84,9 +84,9 @@ struct FileHandle: path: The file path. mode: The mode to open the file in (the mode can be "r" or "w" or "rw"). """ - self.__init__(path._strref_dangerous(), mode._strref_dangerous()) + self.__init__(path.as_string_slice(), mode.as_string_slice()) - fn __init__(inout self, path: StringRef, mode: StringRef) raises: + fn __init__(inout self, path: StringSlice, mode: StringSlice) raises: """Construct the FileHandle using the file path and string. Args: From e52dd713e38b240d8eede1d04c25cb5a755df2ad Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 18 Oct 2024 13:48:28 -0700 Subject: [PATCH 1782/2019] [stdlib] Buffer output before printing and writing to file This greatly improves the print performance, minimizing syscalls and multiple variadic pack loads. These are results from printing 5 args in a 10k loop on the fast alacritty terminal emulator: ``` Program : Average : Slowdown C clang -O3 : 110ms : 1.00x rust release : 122ms : 1.11x go 1.23 : 125ms : 1.14x python 3.12 : 250ms : 2.27x mojo current : 13.44s : 122.18x mojo buffered : 111ms : 1.01x ``` Also improves writing to file speed by 500x on a 10k loop with 5 args, from 9.85s to 20ms. And moves sep and end formatting to a `write_args` function to reuse it. MODULAR_ORIG_COMMIT_REV_ID: 0e6d526042eb66ec5e16104e858b81095b4b2980 --- stdlib/src/builtin/file.mojo | 39 +++--- stdlib/src/builtin/file_descriptor.mojo | 15 +-- stdlib/src/builtin/io.mojo | 26 ++-- stdlib/src/collections/string.mojo | 22 +-- stdlib/src/tempfile/tempfile.mojo | 23 +++- stdlib/src/utils/__init__.mojo | 2 +- stdlib/src/utils/write.mojo | 169 +++++++++++++++++++++++- stdlib/test/builtin/test_file.mojo | 2 +- 8 files changed, 230 insertions(+), 68 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 8313f1ed65..15afa6606a 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -31,10 +31,10 @@ with open("my_file.txt", "r") as f: """ -from os import PathLike +from os import PathLike, abort from sys import external_call, sizeof from sys.ffi import OpaquePointer -from utils import Span, StringRef, StringSlice +from utils import Span, StringRef, StringSlice, write_buffered from memory import AddressSpace, UnsafePointer @@ -403,29 +403,36 @@ struct FileHandle: return pos - fn write(self, data: String) raises: - """Write the data to the file. + @always_inline + fn write_bytes(inout self, bytes: Span[Byte, _]): + """ + Write a span of bytes to the file. Args: - data: The data to write to the file. + bytes: The byte span to write to this file. """ - self._write(data.unsafe_ptr(), data.byte_length()) + var err_msg = _OwnedStringRef() + external_call["KGEN_CompilerRT_IO_FileWrite", NoneType]( + self.handle, + bytes.unsafe_ptr(), + len(bytes), + Pointer.address_of(err_msg), + ) - fn write(self, data: Span[Byte, _]) raises: - """Write a borrowed sequence of data to the file. + if err_msg: + abort(err_msg^.consume_as_error()) - Args: - data: The data to write to the file. - """ - self._write(data.unsafe_ptr(), len(data)) + fn write[*Ts: Writable](inout self, *args: *Ts): + """Write a sequence of Writable arguments to the provided Writer. - fn write(self, data: StringRef) raises: - """Write the data to the file. + Parameters: + Ts: Types of the provided argument sequence. Args: - data: The data to write to the file. + args: Sequence of arguments to write to this Writer. """ - self._write(data.unsafe_ptr(), len(data)) + var file = FileDescriptor(self._get_raw_fd()) + write_buffered[buffer_size=4096](file, args) fn _write[ address_space: AddressSpace diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 7dee133efa..0745273a9a 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -18,12 +18,13 @@ For example, here's how to print to a file ```mojo var f = open("my_file.txt", "r") -print("hello", file=f) +print("hello", file=f^) f.close() ``` """ from utils import Span +from builtin.io import _printf from sys.ffi import external_call, OpaquePointer from sys.info import triple_is_nvidia_cuda from memory import UnsafePointer @@ -61,15 +62,11 @@ struct FileDescriptor(Writer): Args: bytes: The byte span to write to this file. """ + var len_bytes = len(bytes) @parameter if triple_is_nvidia_cuda(): - var tmp = 0 - var arg_ptr = UnsafePointer.address_of(tmp) - # TODO: Debug assert to check bytes written when GPU print calls are buffered - _ = external_call["vprintf", Int32]( - bytes.unsafe_ptr(), arg_ptr.bitcast[OpaquePointer]() - ) + _printf["%*s"](len_bytes, bytes.unsafe_ptr()) else: written = external_call["write", Int32]( self.value, bytes.unsafe_ptr(), len(bytes) @@ -93,7 +90,7 @@ struct FileDescriptor(Writer): """ @parameter - fn write_arg[i: Int, T: Writable](arg: T): + fn write_arg[T: Writable](arg: T): arg.write_to(self) - args.each_idx[write_arg]() + args.each[write_arg]() diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index f859133386..c9b781a764 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -25,10 +25,12 @@ from sys import ( from sys._libc import dup, fclose, fdopen, fflush from sys.ffi import OpaquePointer +from utils import Span, write_buffered, write_args +from collections import InlineArray from builtin.builtin_list import _LITRefPackHelper from builtin.dtype import _get_dtype_printf_format from builtin.file_descriptor import FileDescriptor -from memory import UnsafePointer +from memory import UnsafePointer, memcpy from utils import StringRef, StaticString, StringSlice @@ -362,23 +364,13 @@ fn print[ file: The output stream. """ - var writer = file - - @parameter - fn print_with_separator[i: Int, T: Writable](value: T): - writer.write(value) - - @parameter - if i < len(VariadicList(Ts)) - 1: - writer.write(sep) - - values.each_idx[print_with_separator]() - - writer.write(end) - - # TODO: What is a flush function that works on CUDA? + # TODO: Implement a float formatter for GPU to enable buffering to global + # memory. PTX isn't able to call snprintf to format floats. @parameter - if not triple_is_nvidia_cuda(): + if triple_is_nvidia_cuda(): + write_args(file, values, sep=sep, end=end) + else: + write_buffered[buffer_size=4096](file, values, sep=sep, end=end) if flush: _flush(file=file) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 2071947411..4b72ad89a1 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -19,7 +19,7 @@ from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic from sys.ffi import c_char, OpaquePointer -from utils import StaticString +from utils import StaticString, write_args from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy @@ -898,8 +898,9 @@ struct String( ``` . """ - - return Self.write(args) + var output = String() + write_args(output, args, sep=sep, end=end) + return output^ @staticmethod @no_inline @@ -939,21 +940,8 @@ struct String( ``` . """ - var output = String() - - @parameter - fn write_arg[i: Int, T: Writable](arg: T): - output.write(arg) - - @parameter - if i < len(VariadicList(Ts)) - 1: - output.write(sep) - - args.each_idx[write_arg]() - - output.write(end) - + write_args(output, args, sep=sep, end=end) return output^ @staticmethod diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 4acbc0cffc..d33c41f867 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -23,6 +23,7 @@ import os import sys from collections import Optional, List from pathlib import Path +from utils import Span, write_buffered alias TMP_MAX = 10_000 @@ -401,13 +402,27 @@ struct NamedTemporaryFile: """ return self._file_handle.seek(offset, whence) - fn write(self, data: String) raises: - """Write the data to the file. + fn write[*Ts: Writable](inout self, *args: *Ts): + """Write a sequence of Writable arguments to the provided Writer. + + Parameters: + Ts: Types of the provided argument sequence. + + Args: + args: Sequence of arguments to write to this Writer. + """ + var file = FileDescriptor(self._file_handle._get_raw_fd()) + write_buffered[buffer_size=4096](file, args) + + @always_inline + fn write_bytes(inout self, bytes: Span[Byte, _]): + """ + Write a span of bytes to the file. Args: - data: The data to write to the file. + bytes: The byte span to write to this file. """ - self._file_handle.write(data) + self._file_handle.write_bytes(bytes) fn __enter__(owned self) -> Self: """The function to call when entering the context. diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 6f86e9308a..d319cf884a 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -21,4 +21,4 @@ from .stringref import StringRef from .string_slice import StaticString, StringSlice from .variant import Variant from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock -from .write import Writer, Writable +from .write import Writer, Writable, write_args, write_buffered diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 5d495f1b50..4a70cdcc0b 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -12,8 +12,10 @@ # ===----------------------------------------------------------------------=== # """Establishes the contract between `Writer` and `Writable` types.""" -# ===----------------------------------------------------------------------===# -# Writer +from collections import InlineArray +from memory import memcpy, UnsafePointer + + # ===----------------------------------------------------------------------===# @@ -107,7 +109,7 @@ trait Writer: # arg.write_to(self) # args.each[write_arg]() # - # To only have to implement `write_str` to make a type a valid Writer + # To only have to implement `write_bytes` to make a type a valid Writer # ===----------------------------------------------------------------------===# @@ -146,3 +148,164 @@ trait Writable: writer: The type conforming to `Writable`. """ ... + + +# ===----------------------------------------------------------------------===# +# Utils +# ===----------------------------------------------------------------------===# + + +fn write_args[ + W: Writer, *Ts: Writable +]( + inout writer: W, + args: VariadicPack[_, Writable, *Ts], + *, + sep: StaticString = "", + end: StaticString = "", +): + """ + Add seperators and end characters when writing variadics into a `Writer`. + + Parameters: + W: The type of the `Writer` to write to. + Ts: The types of each arg to write. Each type must satisfy `Writable`. + + Args: + writer: The `Writer` to write to. + args: A VariadicPack of Writable arguments. + sep: The separator used between elements. + end: The String to write after printing the elements. + + Example + + ```mojo + import sys + from utils import write_args + + fn variadic_pack_function[*Ts: Writable]( + *args: *Ts, sep: StringLiteral, end: StringLiteral + ): + var stdout = sys.stdout + write_args(stdout, args, sep=sep, end=end) + + variadic_pack_function(3, "total", "args", sep=",", end="[end]") + ``` + + ``` + 3, total, args[end] + ``` + . + """ + + @parameter + fn print_with_separator[i: Int, T: Writable](value: T): + value.write_to(writer) + + @parameter + if i < len(VariadicList(Ts)) - 1: + sep.write_to(writer) + + args.each_idx[print_with_separator]() + if end: + end.write_to(writer) + + +trait MovableWriter(Movable, Writer): + """Allows moving a Writer into a buffer.""" + + ... + + +struct _WriteBuffer[W: MovableWriter, //, capacity: Int](Writer): + var data: InlineArray[UInt8, capacity] + var pos: Int + var writer: W + + fn __init__(inout self, owned writer: W): + self.data = InlineArray[UInt8, capacity](unsafe_uninitialized=True) + self.pos = 0 + self.writer = writer^ + + fn flush(inout self): + self.writer.write_bytes( + Span[Byte, ImmutableAnyOrigin]( + unsafe_ptr=self.data.unsafe_ptr(), len=self.pos + ) + ) + self.pos = 0 + + fn write_bytes(inout self, bytes: Span[Byte, _]): + len_bytes = len(bytes) + # If empty then return + if len_bytes == 0: + return + # If span is too large to fit in buffer, write directly and return + if len_bytes > capacity: + self.flush() + self.writer.write_bytes(bytes) + return + # If buffer would overflow, flush writer and reset pos to 0. + if self.pos + len_bytes > capacity: + self.flush() + # Continue writing to buffer + memcpy(self.data.unsafe_ptr() + self.pos, bytes.unsafe_ptr(), len_bytes) + self.pos += len_bytes + + fn write[*Ts: Writable](inout self, *args: *Ts): + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() + + +fn write_buffered[ + buffer_size: Int, W: MovableWriter, *Ts: Writable +]( + owned writer: W, + args: VariadicPack[_, Writable, *Ts], + *, + sep: StaticString = "", + end: StaticString = "", +): + """ + Use a buffer on the stack to minimize expensive calls to the writer. When + the buffer would overflow it writes to the `writer` passed in. You can also + add seperators between the args, and end characters. + + + Parameters: + buffer_size: How many bytes to write to a buffer before writing out. + W: The type of the `Writer` to write to. + Ts: The types of each arg to write. Each type must satisfy `Writable`. + + Args: + writer: The `Writer` to write to. + args: A VariadicPack of Writable arguments. + sep: The separator used between elements. + end: The String to write after printing the elements. + + Example + + ```mojo + import sys + from utils import write_buffered + + fn print_err_buffered[*Ts: Writable]( + *args: *Ts, sep: StringLiteral, end: StringLiteral + ): + var stdout = sys.stderr + write_buffered[buffer_size=4096](stdout, args, sep=sep, end=end) + + print_err_buffered(3, "total", "args", sep=",", end="[end]") + ``` + + ``` + 3, total, args[end] + ``` + . + """ + var buffer = _WriteBuffer[buffer_size](writer^) + write_args(buffer, args, sep=sep, end=end) + buffer.flush() diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 67bb8a0e73..7c9d3055f4 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -186,7 +186,7 @@ def test_file_write_span(): var content: String = "The quick brown fox jumps over the lazy dog" var TEMP_FILE = Path(gettempdir().value()) / "test_file_write_span" with open(TEMP_FILE, "w") as f: - f.write(content.as_bytes()) + f.write_bytes(content.as_bytes()) with open(TEMP_FILE, "r") as read_file: assert_equal(read_file.read(), content) From a95d346168535521729c24235b812070c3ebbcca Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Fri, 18 Oct 2024 16:54:10 -0400 Subject: [PATCH 1783/2019] [stdlib] Update the __mojo_debugger_raise_hook MODULAR_ORIG_COMMIT_REV_ID: 74cfb5f0e9368b5b1cfd9b3a30723edbd7ccccac --- stdlib/src/builtin/error.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index e52c7dd804..e4980a1af2 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -211,7 +211,6 @@ struct Error( return String(StringRef(self.data, length)) -@export("__mojo_debugger_raise_hook") fn __mojo_debugger_raise_hook(): """This function is used internally by the Mojo Debugger.""" pass From e0df9445416c33a52ab09eca085b480ddf8b1268 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Fri, 18 Oct 2024 18:10:52 -0400 Subject: [PATCH 1784/2019] [docs] Update debugging docs mentioning new supported breakpoints Our debugger now supports data breakpoints and function breakpoints, so let's update the docs. MODULAR_ORIG_COMMIT_REV_ID: c3c54f8c955974c74f7f8b5ffdda5e64bc9d4dbe --- docs/tools/debugging.ipynb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 78dc2e31e7..c84408c1dd 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -394,14 +394,16 @@ "\n", "The Mojo debugger supports setting [standard \n", "breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints),\n", - "[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints), and\n", - "[triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), \n", + "[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints),\n", + "[function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints),\n", + "[data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints),\n", + "and [triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), \n", "as described in the VS Code documentation.\n", "The Mojo debugger also supports _error breakpoints_ (also known as \"break on\n", "raise\"), which break whenever a `raise` statement is executed.\n", "\n", - "When debugging Mojo code, the debugger doesn't support function breakpoints,\n", - "data breakpoints, or conditional breakpoints based on an expression (it does \n", + "When debugging Mojo code, the debugger doesn't support conditional breakpoints\n", + " based on an expression (it does \n", "support hit counts, which VS Code classifies as a kind of conditional \n", "breakpoint).\n", "\n", From 82bf838e5b8705f1952217fb6cf602d674e95984 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 18 Oct 2024 16:18:27 -0700 Subject: [PATCH 1785/2019] [stdlib] Add changelog entry for `Writer` and `Writable` Adds a changelog entry for `Writer` and `Writable`, with an example of how to use the changes. MODULAR_ORIG_COMMIT_REV_ID: 80009738091cb8191dd8e8bc985cbeb3d7703dd5 --- docs/changelog.md | 56 +++++++++++++++++++++++++++++++++++++ docs/manual/traits.ipynb | 4 +-- stdlib/src/utils/write.mojo | 41 ++++++++++++++------------- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4b20e24e5a..67f64e0c83 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -238,6 +238,62 @@ what we publish. - The VS Code extension now allows selecting a default SDK when multiple are available. +- The `Formatter` struct has changed to a `Writer` trait to enable buffered IO, + increasing print and file writing perf to the same speed as C. It's now more + general purpose and can write any `Span[Byte]`. To align with this the + `Formattable` trait is now named `Writable`, and the `String.format_sequence` + static methods to initialize a new `String` have been renamed to + `String.write`. Here's an example of using all the changes: + + ```mojo + from utils import Span + + @value + struct NewString(Writer, Writable): + var s: String + + # Writer requirement to write a Span of Bytes + fn write_bytes(inout self, bytes: Span[Byte, _]): + self.s._iadd[False](bytes) + + # Writer requirement to take multiple args + fn write[*Ts: Writable](inout self, *args: *Ts): + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() + + # Also make it Writable to allow `print` to write the inner String + fn write_to[W: Writer](self, inout writer: W): + writer.write(self.s) + + + @value + struct Point(Writable): + var x: Int + var y: Int + + # Pass multiple args to the Writer. The Int and StringLiteral types call + # `writer.write_bytes` in their own `write_to` implementations. + fn write_to[W: Writer](self, inout writer: W): + writer.write("Point(", self.x, ", ", self.y, ")") + + # Enable conversion to a String using `str(point)` + fn __str__(self) -> String: + return String.write(self) + + + fn main(): + var point = Point(1, 2) + var new_string = NewString(str(point)) + new_string.write("\n", Point(3, 4)) + print(new_string) + ``` + + Point(1, 2) + Point(3, 4) + - The flag for turning on asserts has changed, e.g. to enable all checks: ```bash diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 98d0435509..a0b665410f 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -603,7 +603,7 @@ "your type.\n", "\n", "While this might sound complex at first, in practice you can minimize\n", - "boilerplate and duplicated code by using the [`String.format_sequence()`](/mojo/stdlib/collections/string/String#format_sequence) static function to\n", + "boilerplate and duplicated code by using the [`String.write()`](/mojo/stdlib/collections/string/String#write) static function to\n", "implement the type's `Stringable` implementation in terms of its `Writable`\n", "implementation. Here is a simple example of a type that implements all of the\n", "`Stringable`, `Representable`, and `Writable` traits:" @@ -635,7 +635,7 @@ " return \"Dog(name=\" + repr(self.name) + \", age=\" + repr(self.age) + \")\"\n", "\n", " fn __str__(self) -> String:\n", - " return String.format_sequence(self)\n", + " return String.write(self)\n", "\n", " fn write_to[W: Writer](self, inout writer: W) -> None:\n", " writer.write(\"Dog(\", self.name, \", \", self.age, \")\")\n", diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 4a70cdcc0b..ad19e0fce0 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -35,50 +35,53 @@ trait Writer: from utils import Span @value - struct NewString(Writer): + struct NewString(Writer, Writable): var s: String - # Enable a type to write its data members as a `Byte[Span]` + # Writer requirement to write a Span of Bytes fn write_bytes(inout self, bytes: Span[Byte, _]): - # If your Writer needs to store the number of bytes being written, - # you can use e.g. `self.bytes_written += len(str_slice)` here. self.s._iadd[False](bytes) - # Enable passing multiple args that implement `write_to`, which - # themselves may have calls to `write` with variadic args: + # Writer requirement to take multiple args fn write[*Ts: Writable](inout self, *args: *Ts): - # Loop through the args, running all their `write_to` functions: @parameter fn write_arg[T: Writable](arg: T): arg.write_to(self) + args.each[write_arg]() + # Also make it Writable to allow `print` to write the inner String + fn write_to[W: Writer](self, inout writer: W): + writer.write(self.s) + + @value struct Point(Writable): var x: Int var y: Int + # Pass multiple args to the Writer. The Int and StringLiteral types + # call `writer.write_bytes` in their own `write_to` implementations. fn write_to[W: Writer](self, inout writer: W): - # Write a single `Span[Byte]`: - var string = "Point" - writer.write_bytes(string.as_bytes()) + writer.write("Point(", self.x, ", ", self.y, ")") - # Write the Ints and StringLiterals in a single call, they also - # implement `write_to`, which implements how to write themselves as - # a `Byte[Span]`: - writer.write("(", self.x, ", ", self.y, ")") + # Enable conversion to a String using `str(point)` + fn __str__(self) -> String: + return String.write(self) - var output = NewString(String()) - var point = Point(2, 4) - output.write(point) - print(output.s) + fn main(): + var point = Point(1, 2) + var new_string = NewString(str(point)) + new_string.write("\\n", Point(3, 4)) + print(new_string) ``` Output: ```plaintext - Point(2, 4) + Point(1, 2) + Point(3, 4) ``` """ From 92da6002e45cb0ba9f731c00ed84f6e53530ba70 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Sat, 19 Oct 2024 14:13:29 -0400 Subject: [PATCH 1786/2019] [docs] Update changelog after debugger and vscode changes MODULAR_ORIG_COMMIT_REV_ID: d77d88e20ea8c940c6e186333431f05a65de2760 --- docs/changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 67f64e0c83..21776f1a33 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -208,6 +208,12 @@ what we publish. express intent when working with a pack of bits. ([PR #3670](https://github.com/modularml/mojo/pull/3670) by [@soraos](https://github.com/soraros)). +- The VS Code extension now supports setting [data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints) + as well as [function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints). + +- The Mojo LLDB debugger now supports symbol breakpoints, e.g. `b main` or + `b my_module::main`. + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` @@ -364,3 +370,6 @@ what we publish. - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. + +- The VS Code extension now downloads its private copy of the MAX SDK in a way + that prevents ETXTBSY errors on Linux. From cbd1c3104a845eb18418b97e7a8ced4931a1e238 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 19 Oct 2024 23:18:30 -0700 Subject: [PATCH 1787/2019] [******] Extend llvm_intrinsic to operate on variadic input MODULAR_ORIG_COMMIT_REV_ID: 38d50dea7fe848ca527e8a984e165c3553ec7c5d --- stdlib/src/builtin/builtin_list.mojo | 6 +- stdlib/src/sys/intrinsics.mojo | 764 +-------------------------- stdlib/test/sys/test_intrinsics.mojo | 7 + 3 files changed, 30 insertions(+), 747 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 5b20b83f86..afec4004b6 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -518,6 +518,7 @@ struct _LITRefPackHelper[ ] # This rebinds `in_pack` to the equivalent `!kgen.pack` with kgen pointers. + @always_inline("nodebug") fn get_as_kgen_pack(self) -> Self.kgen_pack_with_pointer_type: return rebind[Self.kgen_pack_with_pointer_type](self.storage) @@ -534,6 +535,7 @@ struct _LITRefPackHelper[ ] # This returns the stored KGEN pack after loading all of the elements. + @always_inline("nodebug") fn get_loaded_kgen_pack(self) -> Self.loaded_kgen_pack_type: return __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) @@ -579,7 +581,7 @@ struct VariadicPack[ # ===-------------------------------------------------------------------===# @doc_private - @always_inline + @always_inline("nodebug") fn __init__(inout self, value: Self._mlir_type, is_owned: Bool): """Constructs a VariadicPack from the internal representation. @@ -590,7 +592,7 @@ struct VariadicPack[ self._value = value self._is_owned = is_owned - @always_inline + @always_inline("nodebug") fn __del__(owned self): """Destructor that releases elements if owned.""" diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 67d2767694..036be0aff6 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -21,6 +21,7 @@ from sys import PrefetchLocality from .info import sizeof, triple_is_nvidia_cuda from ._assembly import inlined_assembly +from builtin.builtin_list import _LITRefPackHelper import math from memory import AddressSpace, UnsafePointer @@ -29,16 +30,14 @@ from memory import AddressSpace, UnsafePointer # llvm_intrinsic # ===----------------------------------------------------------------------===# -# FIXME: Need tuple unpacking to write a single function definition. - @always_inline("nodebug") fn llvm_intrinsic[ intrin: StringLiteral, type: AnyTrivialRegType, - *, + *types: AnyType, has_side_effect: Bool = True, -]() -> type: +](*arguments: *types) -> type: """Calls an LLVM intrinsic with no arguments. Calls an LLVM intrinsic with the name intrin and return type type. @@ -46,331 +45,17 @@ fn llvm_intrinsic[ Parameters: intrin: The name of the llvm intrinsic. type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - Returns: - The result of calling the llvm intrinsic with no arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - ]() - return rebind[type](None) - - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ]() - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - ]() - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ]() - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0) -> type: - """Calls an LLVM intrinsic with one argument. - - Calls the intrinsic with the name intrin and return type type on argument - arg0. - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - Args: - arg0: The argument to call the LLVM intrinsic with. The type of arg0 - must be T0. - - Returns: - The result of calling the llvm intrinsic with arg0 as an argument. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0, arg1: T1) -> type: - """Calls an LLVM intrinsic with two arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0 and arg1. - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of - arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of - arg1 must be T1. - - Returns: - The result of calling the llvm intrinsic with arg0 and arg1 as arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1) - return rebind[type](None) - else: - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1) - - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0, arg1: T1, arg2: T2) -> type: - """Calls an LLVM intrinsic with three arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1 and arg2. - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of - arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of - arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of - arg2 must be T2. - - Returns: - The result of calling the llvm intrinsic with arg0, arg1 and arg2 as - arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1, arg2) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: - """Calls an LLVM intrinsic with four arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, arg2 and arg3. - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. + types: The argument types for the function. has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: - arg0: The first argument to call the LLVM intrinsic with. The type of - arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of - arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of - arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of - arg3 must be T3. + arguments: The arguments to the function. Returns: - The result of calling the llvm intrinsic with arg0, arg1, arg2 and arg3 - as arguments. + The result of calling the llvm intrinsic with no arguments. """ - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1, arg2, arg3) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: - """Calls an LLVM intrinsic with five arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, arg2, arg3 and arg4. - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - T4: The type of the fifth argument to the intrinsic (arg4). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of arg3 must be T3. - arg4: The fifth argument to call the LLVM intrinsic with. The type of arg4 must be T4. - - Returns: - The result of calling the llvm intrinsic with arg0...arg4 as arguments. - """ + var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() @parameter if _mlirtype_is_eq[type, NoneType](): @@ -378,86 +63,17 @@ fn llvm_intrinsic[ @parameter if has_side_effect: __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3, arg4) + intrin = intrin.value, + _type=None, + ](loaded_pack) return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1, arg2, arg3, arg4) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: - """Calls an LLVM intrinsic with six arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, ..., arg5 - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - T4: The type of the fifth argument to the intrinsic (arg4). - T5: The type of the sixth argument to the intrinsic (arg5). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of arg3 must be T3. - arg4: The fifth argument to call the LLVM intrinsic with. The type of arg4 must be T4. - arg5: The sixth argument to call the LLVM intrinsic with. The type of arg5 must be T5. - - Returns: - The result of calling the llvm intrinsic with arg0...arg5 as arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: + else: __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3, arg4, arg5) + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](loaded_pack) return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5) - return rebind[type](None) else: @parameter @@ -465,355 +81,13 @@ fn llvm_intrinsic[ return __mlir_op.`pop.call_llvm_intrinsic`[ intrin = intrin.value, _type=type, - ](arg0, arg1, arg2, arg3, arg4, arg5) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: - """Calls an LLVM intrinsic with seven arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, ..., arg6 - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - T4: The type of the fifth argument to the intrinsic (arg4). - T5: The type of the sixth argument to the intrinsic (arg5). - T6: The type of the seventh argument to the intrinsic (arg6). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of arg3 must be T3. - arg4: The fifth argument to call the LLVM intrinsic with. The type of arg4 must be T4. - arg5: The sixth argument to call the LLVM intrinsic with. The type of arg5 must be T5. - arg6: The seventh argument to call the LLVM intrinsic with. The type of arg6 must be T6. - - Returns: - The result of calling the llvm intrinsic with arg0...arg6 as arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, -) -> type: - """Calls an LLVM intrinsic with eight arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, ..., arg7 - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - T4: The type of the fifth argument to the intrinsic (arg4). - T5: The type of the sixth argument to the intrinsic (arg5). - T6: The type of the seventh argument to the intrinsic (arg6). - T7: The type of the eighth argument to the intrinsic (arg7). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of arg3 must be T3. - arg4: The fifth argument to call the LLVM intrinsic with. The type of arg4 must be T4. - arg5: The sixth argument to call the LLVM intrinsic with. The type of arg5 must be T5. - arg6: The seventh argument to call the LLVM intrinsic with. The type of arg6 must be T6. - arg7: The eighth argument to call the LLVM intrinsic with. The type of arg7 must be T7. - - Returns: - The result of calling the llvm intrinsic with arg0...arg7 as arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, -) -> type: - """Calls an LLVM intrinsic with nine arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, ..., arg8 - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - T4: The type of the fifth argument to the intrinsic (arg4). - T5: The type of the sixth argument to the intrinsic (arg5). - T6: The type of the seventh argument to the intrinsic (arg6). - T7: The type of the eighth argument to the intrinsic (arg7). - T8: The type of the ninth argument to the intrinsic (arg8). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of arg3 must be T3. - arg4: The fifth argument to call the LLVM intrinsic with. The type of arg4 must be T4. - arg5: The sixth argument to call the LLVM intrinsic with. The type of arg5 must be T5. - arg6: The seventh argument to call the LLVM intrinsic with. The type of arg6 must be T6. - arg7: The eighth argument to call the LLVM intrinsic with. The type of arg7 must be T7. - arg8: The ninth argument to call the LLVM intrinsic with. The type of arg8 must be T8. - - Returns: - The result of calling the llvm intrinsic with arg0...arg8 as arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - return rebind[type](None) - else: - - @parameter - if has_side_effect: + ](loaded_pack) + else: return __mlir_op.`pop.call_llvm_intrinsic`[ intrin = intrin.value, _type=type, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - - -@always_inline("nodebug") -fn llvm_intrinsic[ - T0: AnyTrivialRegType, - T1: AnyTrivialRegType, - T2: AnyTrivialRegType, - T3: AnyTrivialRegType, - T4: AnyTrivialRegType, - T5: AnyTrivialRegType, - T6: AnyTrivialRegType, - T7: AnyTrivialRegType, - T8: AnyTrivialRegType, - T9: AnyTrivialRegType, //, - intrin: StringLiteral, - type: AnyTrivialRegType, - *, - has_side_effect: Bool = True, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, - arg7: T7, - arg8: T8, - arg9: T9, -) -> type: - """Calls an LLVM intrinsic with ten arguments. - - Calls the LLVM intrinsic with the name intrin and return type type on - arguments arg0, arg1, ..., arg10 - - Parameters: - T0: The type of the first argument to the intrinsic (arg0). - T1: The type of the second argument to the intrinsic (arg1). - T2: The type of the third argument to the intrinsic (arg2). - T3: The type of the fourth argument to the intrinsic (arg3). - T4: The type of the fifth argument to the intrinsic (arg4). - T5: The type of the sixth argument to the intrinsic (arg5). - T6: The type of the seventh argument to the intrinsic (arg6). - T7: The type of the eighth argument to the intrinsic (arg7). - T8: The type of the ninth argument to the intrinsic (arg8). - T9: The type of the tenth argument to the intrinsic (arg9). - intrin: The name of the llvm intrinsic. - type: The return type of the intrinsic. - has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. - - - Args: - arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. - arg1: The second argument to call the LLVM intrinsic with. The type of arg1 must be T1. - arg2: The third argument to call the LLVM intrinsic with. The type of arg2 must be T2. - arg3: The fourth argument to call the LLVM intrinsic with. The type of arg3 must be T3. - arg4: The fifth argument to call the LLVM intrinsic with. The type of arg4 must be T4. - arg5: The sixth argument to call the LLVM intrinsic with. The type of arg5 must be T5. - arg6: The seventh argument to call the LLVM intrinsic with. The type of arg6 must be T6. - arg7: The eighth argument to call the LLVM intrinsic with. The type of arg7 must be T7. - arg8: The ninth argument to call the LLVM intrinsic with. The type of arg8 must be T8. - arg9: The tenth argument to call the LLVM intrinsic with. The type of arg9 must be T9. - - Returns: - The result of calling the llvm intrinsic with arg0...arg9 as arguments. - """ - - @parameter - if _mlirtype_is_eq[type, NoneType](): - - @parameter - if has_side_effect: - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=None - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - return rebind[type](None) - __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=None, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - return rebind[type](None) - else: - - @parameter - if has_side_effect: - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, - _type=type, - hasSideEffects = __mlir_attr.false, - ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + hasSideEffects = __mlir_attr.false, + ](loaded_pack) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 8aa06bdc6d..417b5bd23b 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -12,6 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from math import gcd from sys import ( compressed_store, masked_load, @@ -30,6 +31,11 @@ alias iota_4 = F32x4(0.0, 1.0, 2.0, 3.0) alias iota_8 = F32x8(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0) +def test_intrinsic_comp_eval(): + alias res = gcd(5, 4) + assert_equal(res, gcd(5, 4)) + + def test_compressed_store(): var vector = UnsafePointer[Float32]().alloc(5) memset_zero(vector, 5) @@ -132,6 +138,7 @@ def test_assume(): def main(): + test_intrinsic_comp_eval() test_compressed_store() test_masked_load() test_masked_store() From 71345cd45b38f37ab3e0d3276337c2ca3b4316e9 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:39:06 -0600 Subject: [PATCH 1788/2019] [External] [stdlib] Fix `string_slice.mojo` docstrings (#49472) [External] [stdlib] Fix `string_slice.mojo` docstrings Fix `string_slice.mojo` docstrings ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3699 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3699 MODULAR_ORIG_COMMIT_REV_ID: 99638231ea74213a63c95e17560a9276cf16c09e --- stdlib/src/utils/string_slice.mojo | 323 +++++++++++++++-------------- 1 file changed, 165 insertions(+), 158 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index c7fbd8a76c..e68495372d 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -13,7 +13,9 @@ """Implements the StringSlice type. -You can import these APIs from the `utils.string_slice` module. For example: +You can import these APIs from the `utils.string_slice` module. + +Examples: ```mojo from utils import StringSlice @@ -192,7 +194,7 @@ struct _StringSliceIter[ origin: Origin[is_mutable].type, forward: Bool = True, ]: - """Iterator for StringSlice + """Iterator for `StringSlice` over unicode characters. Parameters: is_mutable: Whether the slice is mutable. @@ -268,15 +270,15 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( CollectionElementNew, Hashable, ): - """ - A non-owning view to encoded string data. - - TODO: - The underlying string data is guaranteed to be encoded using UTF-8. + """A non-owning view to encoded string data. Parameters: is_mutable: Whether the slice is mutable. origin: The origin of the underlying string data. + + Notes: + TODO: The underlying string data is guaranteed to be encoded using + UTF-8. """ var _slice: Span[Byte, origin] @@ -289,10 +291,10 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn __init__( inout self: StringSlice[StaticConstantOrigin], lit: StringLiteral ): - """Construct a new string slice from a string literal. + """Construct a new `StringSlice` from a `StringLiteral`. Args: - lit: The literal to construct this string slice from. + lit: The literal to construct this `StringSlice` from. """ # Since a StringLiteral has static origin, it will outlive # whatever arbitrary `origin` the user has specified they need this @@ -312,29 +314,30 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( @always_inline fn __init__(inout self, *, owned unsafe_from_utf8: Span[Byte, origin]): - """Construct a new StringSlice from a sequence of UTF-8 encoded bytes. + """Construct a new `StringSlice` from a sequence of UTF-8 encoded bytes. + + Args: + unsafe_from_utf8: A `Span[Byte]` encoded in UTF-8. Safety: `unsafe_from_utf8` MUST be valid UTF-8 encoded data. - - Args: - unsafe_from_utf8: A slice of bytes encoded in UTF-8. """ self._slice = unsafe_from_utf8^ fn __init__(inout self, *, unsafe_from_utf8_strref: StringRef): - """Construct a new StringSlice from a StringRef pointing to UTF-8 + """Construct a new StringSlice from a `StringRef` pointing to UTF-8 encoded bytes. + Args: + unsafe_from_utf8_strref: A `StringRef` of bytes encoded in UTF-8. + Safety: - `unsafe_from_utf8_strref` MUST point to data that is valid for `origin`. - `unsafe_from_utf8_strref` MUST be valid UTF-8 encoded data. - - Args: - unsafe_from_utf8_strref: A StringRef of bytes encoded in UTF-8. """ + var strref = unsafe_from_utf8_strref var byte_slice = Span[Byte, origin]( @@ -351,19 +354,19 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( unsafe_from_utf8_ptr: UnsafePointer[UInt8], len: Int, ): - """Construct a StringSlice from a pointer to a sequence of UTF-8 encoded - bytes and a length. + """Construct a `StringSlice` from a pointer to a sequence of UTF-8 + encoded bytes and a length. + + Args: + unsafe_from_utf8_ptr: A pointer to a sequence of bytes encoded in + UTF-8. + len: The number of bytes of encoded data. Safety: - `unsafe_from_utf8_ptr` MUST point to at least `len` bytes of valid UTF-8 encoded data. - `unsafe_from_utf8_ptr` must point to data that is live for the duration of `origin`. - - Args: - unsafe_from_utf8_ptr: A pointer to a sequence of bytes encoded in - UTF-8. - len: The number of bytes of encoded data. """ var byte_slice = Span[Byte, origin]( unsafe_ptr=unsafe_from_utf8_ptr, @@ -406,11 +409,10 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( return b_len - _count_utf8_continuation_bytes(s) fn write_to[W: Writer](self, inout writer: W): - """ - Formats this string slice to the provided Writer. + """Formats this string slice to the provided `Writer`. Parameters: - W: A type conforming to the Writable trait. + W: A type conforming to the `Writable` trait. Args: writer: The object to write to. @@ -441,14 +443,13 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( # accesses to the origin. @__unsafe_disable_nested_origin_exclusivity fn __eq__(self, rhs: StringSlice) -> Bool: - """Verify if a string slice is equal to another string slice. + """Verify if a `StringSlice` is equal to another `StringSlice`. Args: - rhs: The string slice to compare against. + rhs: The `StringSlice` to compare against. Returns: - True if the string slices are equal in length and contain the same - elements, False otherwise. + If the `StringSlice` is equal to the input in length and contents. """ if not self and not rhs: return True @@ -464,80 +465,79 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( @always_inline fn __eq__(self, rhs: String) -> Bool: - """Verify if a string slice is equal to a string. + """Verify if a `StringSlice` is equal to a string. Args: - rhs: The string to compare against. + rhs: The `String` to compare against. Returns: - True if the string slice is equal to the input string in length and - contain the same bytes, False otherwise. + If the `StringSlice` is equal to the input in length and contents. """ return self == rhs.as_string_slice() @always_inline fn __eq__(self, rhs: StringLiteral) -> Bool: - """Verify if a string slice is equal to a literal. + """Verify if a `StringSlice` is equal to a literal. Args: - rhs: The literal to compare against. + rhs: The `StringLiteral` to compare against. Returns: - True if the string slice is equal to the input literal in length and - contain the same bytes, False otherwise. + If the `StringSlice` is equal to the input in length and contents. """ return self == rhs.as_string_slice() @__unsafe_disable_nested_origin_exclusivity @always_inline fn __ne__(self, rhs: StringSlice) -> Bool: - """Verify if span is not equal to another string slice. + """Verify if span is not equal to another `StringSlice`. Args: - rhs: The string slice to compare against. + rhs: The `StringSlice` to compare against. Returns: - True if the string slices are not equal in length or contents, False - otherwise. + If the `StringSlice` is not equal to the input in length and + contents. """ return not self == rhs @always_inline fn __ne__(self, rhs: String) -> Bool: - """Verify if span is not equal to another string slice. + """Verify if span is not equal to another `StringSlice`. Args: - rhs: The string slice to compare against. + rhs: The `StringSlice` to compare against. Returns: - True if the string and slice are not equal in length or contents, - False otherwise. + If the `StringSlice` is not equal to the input in length and + contents. """ return not self == rhs @always_inline fn __ne__(self, rhs: StringLiteral) -> Bool: - """Verify if span is not equal to a literal. + """Verify if span is not equal to a `StringLiteral`. Args: - rhs: The string literal to compare against. + rhs: The `StringLiteral` to compare against. Returns: - True if the slice is not equal to the literal in length or contents, - False otherwise. + If the `StringSlice` is not equal to the input in length and + contents. """ return not self == rhs @always_inline fn __lt__(self, rhs: StringSlice) -> Bool: - """Compare this StringSlice to the RHS using LT comparison. + """Verify if the `StringSlice` bytes are strictly less than the input in + overlapping content. Args: - rhs: The other StringSlice to compare against. + rhs: The other `StringSlice` to compare against. Returns: - True if this string is strictly less than the RHS string and False - otherwise. + If the `StringSlice` bytes are strictly less than the input in + overlapping content. """ var len1 = len(self) var len2 = len(rhs) @@ -621,13 +621,20 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( @always_inline fn strip(self) -> StringSlice[origin]: """Gets a StringRef with leading and trailing whitespaces removed. - This only takes C spaces into account: " \\t\\n\\r\\f\\v". - - For example, `" mojo "` returns `"mojo"`. + This only takes ASCII whitespace into account: + `" \\t\\n\\v\\f\\r\\x1c\\x1d\\x1e"`. Returns: A StringRef with leading and trailing whitespaces removed. + + Examples: + + ```mojo + print(" mojo ".strip()) # "mojo" + ``` + . """ + # FIXME: this can already do full isspace support with iterator var start: Int = 0 var end: Int = len(self) var ptr = self.unsafe_ptr() @@ -689,16 +696,16 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn startswith( self, prefix: StringSlice[_], start: Int = 0, end: Int = -1 ) -> Bool: - """Checks if the StringRef starts with the specified prefix between start - and end positions. Returns True if found and False otherwise. + """Verify if the `StringSlice` starts with the specified prefix between + start and end positions. Args: - prefix: The prefix to check. - start: The start offset from which to check. - end: The end offset from which to check. + prefix: The prefix to check. + start: The start offset from which to check. + end: The end offset from which to check. Returns: - True if the self[start:end] is prefixed by the input prefix. + True if the `self[start:end]` is prefixed by the input prefix. """ if end == -1: return self.find(prefix, start) == start @@ -709,16 +716,16 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn endswith( self, suffix: StringSlice[_], start: Int = 0, end: Int = -1 ) -> Bool: - """Checks if the StringRef end with the specified suffix between start - and end positions. Returns True if found and False otherwise. + """Verify if the `StringSlice` end with the specified suffix between + start and end positions. Args: - suffix: The suffix to check. - start: The start offset from which to check. - end: The end offset from which to check. + suffix: The suffix to check. + start: The start offset from which to check. + end: The end offset from which to check. Returns: - True if the self[start:end] is suffixed by the input suffix. + True if the `self[start:end]` is suffixed by the input suffix. """ if len(suffix) > len(self): return False @@ -730,10 +737,8 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn _from_start(self, start: Int) -> Self: """Gets the `StringSlice` pointing to the substring after the specified - slice start position. - - If start is negative, it is interpreted as the number of characters - from the end of the string to start at. + slice start position. If start is negative, it is interpreted as the + number of characters from the end of the string to start at. Args: start: Starting index of the slice. @@ -798,14 +803,14 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn find(ref [_]self, substr: StringSlice, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at - `start`. If not found, returns -1. + `start`. If not found, returns `-1`. Args: - substr: The substring to find. - start: The offset from which to find. + substr: The substring to find. + start: The offset from which to find. Returns: - The offset of `substr` relative to the beginning of the string. + The offset of `substr` relative to the beginning of the string. """ if not substr: return 0 @@ -831,14 +836,14 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn rfind(self, substr: StringSlice, start: Int = 0) -> Int: """Finds the offset of the last occurrence of `substr` starting at - `start`. If not found, returns -1. + `start`. If not found, returns `-1`. Args: - substr: The substring to find. - start: The offset from which to find. + substr: The substring to find. + start: The offset from which to find. Returns: - The offset of `substr` relative to the beginning of the string. + The offset of `substr` relative to the beginning of the string. """ if not substr: return len(self) @@ -866,12 +871,12 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """Determines whether every character in the given StringSlice is a python whitespace String. This corresponds to Python's [universal separators]( - https://docs.python.org/3/library/stdtypes.html#str.splitlines) + https://docs.python.org/3/library/stdtypes.html#str.splitlines) `" \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Returns: True if the whole StringSlice is made up of whitespace characters - listed above, otherwise False. + listed above, otherwise False. """ if self.byte_length() == 0: @@ -908,7 +913,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn splitlines(self, keepends: Bool = False) -> List[String]: """Split the string at line boundaries. This corresponds to Python's [universal newlines]( - https://docs.python.org/3/library/stdtypes.html#str.splitlines) + https://docs.python.org/3/library/stdtypes.html#str.splitlines) `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Args: @@ -999,12 +1004,12 @@ trait _CurlyEntryFormattable(Stringable, Representable): will be less constrained. """ - pass + ... @value struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): - """The struct that handles string-like formatting by curly braces entries. + """The struct that handles `Stringlike` formatting by curly braces entries. This is internal for the types: `String`, `StringLiteral` and `StringSlice`. """ @@ -1338,7 +1343,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): @register_passable("trivial") struct _FormatSpec: """Store every field of the format specifier in a byte (e.g., ord("+") for - sign). It is stored in a byte because every [format specifier](\ + sign). It is stored in a byte because every [format specifier]( https://docs.python.org/3/library/string.html#formatspec) is an ASCII character. """ @@ -1351,15 +1356,15 @@ struct _FormatSpec: """The meaning of the various alignment options is as follows: | Option | Meaning| - |:-------|:-------| - |'<' | Forces the field to be left-aligned within the available space + |:------:|:-------| + |'<' | Forces the field to be left-aligned within the available space \ (this is the default for most objects).| - |'>' | Forces the field to be right-aligned within the available space + |'>' | Forces the field to be right-aligned within the available space \ (this is the default for numbers).| - |'=' | Forces the padding to be placed after the sign (if any) but before - the digits. This is used for printing fields in the form `+000000120`. This - alignment option is only valid for numeric types. It becomes the default for - numbers when `0` immediately precedes the field width.| + |'=' | Forces the padding to be placed after the sign (if any) but before \ + the digits. This is used for printing fields in the form `+000000120`. This\ + alignment option is only valid for numeric types. It becomes the default\ + for numbers when `0` immediately precedes the field width.| |'^' | Forces the field to be centered within the available space.| """ var sign: UInt8 @@ -1367,12 +1372,12 @@ struct _FormatSpec: following: | Option | Meaning| - |:-------|:-------| - |'+' | indicates that a sign should be used for both positive as well as + |:------:|:-------| + |'+' | indicates that a sign should be used for both positive as well as\ negative numbers.| - |'-' | indicates that a sign should be used only for negative numbers (this + |'-' | indicates that a sign should be used only for negative numbers (this\ is the default behavior).| - |space | indicates that a leading space should be used on positive numbers, + |space | indicates that a leading space should be used on positive numbers,\ and a minus sign on negative numbers.| """ var coerce_z: Bool @@ -1420,18 +1425,18 @@ struct _FormatSpec: The available integer presentation types are: | Option | Meaning| - |:-------|:-------| + |:------:|:-------| |'b' |Binary format. Outputs the number in base 2.| - |'c' |Character. Converts the integer to the corresponding unicode character - before printing.| + |'c' |Character. Converts the integer to the corresponding unicode\ + character before printing.| |'d' |Decimal Integer. Outputs the number in base 10.| |'o' |Octal format. Outputs the number in base 8.| - |'x' |Hex format. Outputs the number in base 16, using lower-case letters + |'x' |Hex format. Outputs the number in base 16, using lower-case letters\ for the digits above 9.| - |'X' |Hex format. Outputs the number in base 16, using upper-case letters - for the digits above 9. In case '#' is specified, the prefix '0x' will be + |'X' |Hex format. Outputs the number in base 16, using upper-case letters\ + for the digits above 9. In case '#' is specified, the prefix '0x' will be\ upper-cased to '0X' as well.| - |'n' |Number. This is the same as 'd', except that it uses the current + |'n' |Number. This is the same as 'd', except that it uses the current\ locale setting to insert the appropriate number separator characters.| |None | The same as 'd'.| @@ -1443,59 +1448,59 @@ struct _FormatSpec: The available presentation types for float and Decimal values are: | Option | Meaning| - |:-------|:-------| - |'e' |Scientific notation. For a given precision p, formats the number in - scientific notation with the letter `e` separating the coefficient from the - exponent. The coefficient has one digit before and p digits after the - decimal point, for a total of p + 1 significant digits. With no precision - given, uses a precision of 6 digits after the decimal point for float, and - shows all coefficient digits for Decimal. If no digits follow the decimal + |:------:|:-------| + |'e' |Scientific notation. For a given precision p, formats the number in\ + scientific notation with the letter `e` separating the coefficient from the\ + exponent. The coefficient has one digit before and p digits after the\ + decimal point, for a total of p + 1 significant digits. With no precision\ + given, uses a precision of 6 digits after the decimal point for float, and\ + shows all coefficient digits for Decimal. If no digits follow the decimal\ point, the decimal point is also removed unless the # option is used.| - |'E' |Scientific notation. Same as 'e' except it uses an upper case `E` as + |'E' |Scientific notation. Same as 'e' except it uses an upper case `E` as\ the separator character.| - |'f' |Fixed-point notation. For a given precision p, formats the number as a - decimal number with exactly p digits following the decimal point. With no - precision given, uses a precision of 6 digits after the decimal point for - float, and uses a precision large enough to show all coefficient digits for - Decimal. If no digits follow the decimal point, the decimal point is also - removed unless the # option is used.| - |'F' |Fixed-point notation. Same as 'f', but converts nan to NAN and inf to + |'f' |Fixed-point notation. For a given precision p, formats the number as\ + a decimal number with exactly p digits following the decimal point. With no\ + precision given, uses a precision of 6 digits after the decimal point for\ + float, and uses a precision large enough to show all coefficient digits for\ + Decimal. If no digits follow the decimal point, the decimal point is also\ + removed unless the '#' option is used.| + |'F' |Fixed-point notation. Same as 'f', but converts nan to NAN and inf to\ INF.| - |'g' |General format. For a given precision p >= 1, this rounds the number - to p significant digits and then formats the result in either fixed-point - format or in scientific notation, depending on its magnitude. A precision of - 0 is treated as equivalent to a precision of 1. - The precise rules are as follows: suppose that the result formatted with - presentation type 'e' and precision p-1 would have exponent exp. Then, if - m <= exp < p, where m is -4 for floats and -6 for Decimals, the number is - formatted with presentation type 'f' and precision p-1-exp. Otherwise, the - number is formatted with presentation type 'e' and precision p-1. In both - cases insignificant trailing zeros are removed from the significand, and the - decimal point is also removed if there are no remaining digits following it, - unless the '#' option is used. - With no precision given, uses a precision of 6 significant digits for float. - For Decimal, the coefficient of the result is formed from the coefficient - digits of the value; scientific notation is used for values smaller than - 1e-6 in absolute value and values where the place value of the least - significant digit is larger than 1, and fixed-point notation is used - otherwise. - Positive and negative infinity, positive and negative zero, and nans, are - formatted as inf, -inf, 0, -0 and nan respectively, regardless of the + |'g' |General format. For a given precision p >= 1, this rounds the number\ + to p significant digits and then formats the result in either fixed-point\ + format or in scientific notation, depending on its magnitude. A precision\ + of 0 is treated as equivalent to a precision of 1.\ + The precise rules are as follows: suppose that the result formatted with\ + presentation type 'e' and precision p-1 would have exponent exp. Then, if\ + m <= exp < p, where m is -4 for floats and -6 for Decimals, the number is\ + formatted with presentation type 'f' and precision p-1-exp. Otherwise, the\ + number is formatted with presentation type 'e' and precision p-1. In both\ + cases insignificant trailing zeros are removed from the significand, and\ + the decimal point is also removed if there are no remaining digits\ + following it, unless the '#' option is used.\ + With no precision given, uses a precision of 6 significant digits for\ + float. For Decimal, the coefficient of the result is formed from the\ + coefficient digits of the value; scientific notation is used for values\ + smaller than 1e-6 in absolute value and values where the place value of the\ + least significant digit is larger than 1, and fixed-point notation is used\ + otherwise.\ + Positive and negative infinity, positive and negative zero, and nans, are\ + formatted as inf, -inf, 0, -0 and nan respectively, regardless of the\ precision.| - |'G' |General format. Same as 'g' except switches to 'E' if the number gets + |'G' |General format. Same as 'g' except switches to 'E' if the number gets\ too large. The representations of infinity and NaN are uppercased, too.| - |'n' |Number. This is the same as 'g', except that it uses the current + |'n' |Number. This is the same as 'g', except that it uses the current\ locale setting to insert the appropriate number separator characters.| - |'%' |Percentage. Multiplies the number by 100 and displays in fixed ('f') + |'%' |Percentage. Multiplies the number by 100 and displays in fixed ('f')\ format, followed by a percent sign.| - |None |For float this is like the 'g' type, except that when fixed-point - notation is used to format the result, it always includes at least one digit - past the decimal point, and switches to the scientific notation when - exp >= p - 1. When the precision is not specified, the latter will be as - large as needed to represent the given value faithfully. - For Decimal, this is the same as either 'g' or 'G' depending on the value of - context.capitals for the current decimal context. - The overall effect is to match the output of str() as altered by the other + |None |For float this is like the 'g' type, except that when fixed-point\ + notation is used to format the result, it always includes at least one\ + digit past the decimal point, and switches to the scientific notation when\ + exp >= p - 1. When the precision is not specified, the latter will be as\ + large as needed to represent the given value faithfully.\ + For Decimal, this is the same as either 'g' or 'G' depending on the value\ + of context.capitals for the current decimal context.\ + The overall effect is to match the output of str() as altered by the other\ format modifiers.| """ @@ -1515,18 +1520,18 @@ struct _FormatSpec: Args: fill: Defaults to space. - align: Defaults to 0 which is adjusted to the default for the arg + align: Defaults to `0` which is adjusted to the default for the arg type. sign: Defaults to `-`. coerce_z: Defaults to False. alternate_form: Defaults to False. - width: Defaults to 0 which is adjusted to the default for the arg + width: Defaults to `0` which is adjusted to the default for the arg type. - grouping_option: Defaults to 0 which is adjusted to the default for + grouping_option: Defaults to `0` which is adjusted to the default for the arg type. - precision: Defaults to 0 which is adjusted to the default for the + precision: Defaults to `0` which is adjusted to the default for the arg type. - type: Defaults to 0 which is adjusted to the default for the arg + type: Defaults to `0` which is adjusted to the default for the arg type. """ self.fill = fill @@ -1549,12 +1554,14 @@ struct _FormatSpec: Returns: An instance of FormatSpec. """ + + alias `:` = UInt8(ord(":")) var f_len = fmt_str.byte_length() var f_ptr = fmt_str.unsafe_ptr() var colon_idx = -1 var idx = 0 while idx < f_len: - if f_ptr[idx] == ord(":"): + if f_ptr[idx] == `:`: exclamation_index = idx break idx += 1 From de34c9dfa6151c0db5ab5478065657dac5d6a896 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Mon, 21 Oct 2024 23:25:10 -0700 Subject: [PATCH 1789/2019] Removing `_strref_dangerous` from String.__eq__ Refactoring the `String.__eq__` code to remove uses of `_strref_dangerous`. MODULAR_ORIG_COMMIT_REV_ID: 7bfbfed01df3a1140763f828ae5f7004815d6025 --- stdlib/src/collections/string.mojo | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 4b72ad89a1..55ea75c2ad 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1036,7 +1036,17 @@ struct String( Returns: True if the Strings are equal and False otherwise. """ - return not (self != other) + if not self and not other: + return True + if len(self) != len(other): + return False + # same pointer and length, so equal + if self.unsafe_ptr() == other.unsafe_ptr(): + return True + for i in range(len(self)): + if self.unsafe_ptr()[i] != other.unsafe_ptr()[i]: + return False + return True @always_inline fn __ne__(self, other: String) -> Bool: @@ -1048,7 +1058,7 @@ struct String( Returns: True if the Strings are not equal and False otherwise. """ - return self._strref_dangerous() != other._strref_dangerous() + return not (self == other) @always_inline fn __lt__(self, rhs: String) -> Bool: From 484790be8e0a730003287b9abb61c203c14575c7 Mon Sep 17 00:00:00 2001 From: Bill Welense Date: Tue, 22 Oct 2024 08:54:26 -0500 Subject: [PATCH 1790/2019] [docs] Clarify meaning of "superset of Python" Update documentation to provide more context than the word "superset" can communicate alone. Was: Our vision is for Mojo to become a superset of Python. Now, depending on context: - Our vision is for Mojo to become the best way to extend Python. - Our intent is for Mojo to adopt the syntax of Python. MODULAR_ORIG_COMMIT_REV_ID: 8a9a6f3135fd333578b792cb5b82f76b32aaeb76 --- README.md | 2 +- docs/manual/basics.ipynb | 4 ++-- docs/manual/index.md | 2 +- docs/manual/values/value-semantics.ipynb | 2 +- docs/roadmap.md | 4 ++-- docs/why-mojo.md | 22 +++++++++++----------- examples/notebooks/Matmul.ipynb | 2 +- proposals/mojo-and-dynamism.md | 10 +++++----- proposals/ref-convention.md | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 51fa8e08fa..5774ca462a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Mojo is a new programming language that bridges the gap between research and production by combining Python syntax and ecosystem with systems programming and metaprogramming features. Mojo is still young, but it is designed -to become a superset of Python over time. +to become the best way to extend Python over time. This repo includes source code for: diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index d35752c300..c75b22d225 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -25,7 +25,7 @@ "world\"](/mojo/manual/get-started). Now let's talk about how\n", "to write Mojo code.\n", "\n", - "You probably already know that Mojo is designed as a superset of Python. So if\n", + "If\n", "you know Python, then a lot of Mojo code will look familiar. However, Mojo\n", "is—first and foremost—designed for high-performance systems programming, with\n", "features like strong type checking, memory safety, next-generation compiler\n", @@ -643,7 +643,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Mojo is not yet a full superset of Python, but we've built a mechanism to import\n", + "Mojo does not yet adopt the full syntax of Python, but we've built a mechanism to import\n", "Python modules as-is, so you can leverage existing Python code right away.\n", "\n", "For example, here's how you can import and use NumPy (you must have Python\n", diff --git a/docs/manual/index.md b/docs/manual/index.md index 6d3e2bbd3e..ba52bcd6c4 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -10,7 +10,7 @@ Mojo is designed to solve a variety of AI development challenges that no other language can, because Mojo is the first programming language built from the ground-up with [MLIR](https://mlir.llvm.org/) (a compiler infrastructure that's ideal for heterogeneous hardware, from CPUs and GPUs, to various AI ASICs). We -also designed Mojo as a superset of Python because we love Python and its +also designed Mojo as the best way to extend Python because we love Python and its community, but we couldn't realistically enhance Python to do all the things we wanted. For a longer discussion on this topic, read [Why Mojo](/mojo/why-mojo). diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index fa8bf907d2..de3c812464 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -402,7 +402,7 @@ "\n", ":::note TODO\n", "\n", - "Mojo is not a complete superset of Python yet, and there is a lot to\n", + "Mojo does not adopt the full syntax of Python yet, and there is a lot to\n", "do in this department before Mojo supports all of Python's types and behaviors.\n", "As such, this is a topic that also still needs a lot of documentation.\n", "\n", diff --git a/docs/roadmap.md b/docs/roadmap.md index fff951648f..5fb1ec2149 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -77,8 +77,8 @@ For all these reasons, "nice to have" syntactic sugar is not a priority, and we will quickly close such proposals to avoid cluttering the issue tracker. If you'd like to propose a "general goodness" syntactic feature, please do so with the existing [Python PEP process](https://peps.python.org/pep-0000/). If/when -Python adopts a feature, Mojo will also add it, because Mojo's goal is to be a -superset. We are happy with this approach because the Python community is +Python adopts a feature, Mojo may also add it, because Mojo's goal is to adopt +Python's syntax. We are happy with this approach because the Python community is better equipped to evaluate these features, they have mature code bases to evaluate them with, and they have processes and infrastructure for making structured language evolution features. diff --git a/docs/why-mojo.md b/docs/why-mojo.md index 6b89d1e946..5ecea74bb1 100644 --- a/docs/why-mojo.md +++ b/docs/why-mojo.md @@ -80,8 +80,8 @@ features. We also benefit from tremendous lessons learned from other languages migrating developers to new compilers and languages, and we leverage the existing MLIR compiler ecosystem. -Further, we decided that the right _long-term goal_ for Mojo is to provide a -**superset of Python** (that is, to make Mojo compatible with existing Python +Further, we decided that the right _long-term goal_ for Mojo is to adopt the +**syntax of Python** (that is, to make Mojo compatible with existing Python programs) and to embrace the CPython implementation for long-tail ecosystem support. If you're a Python programmer, we hope that Mojo is immediately familiar, while also providing new tools to develop safe and performant @@ -197,8 +197,8 @@ interpreter in Mojo instead of C? 🔥 ## Python's problems -By aiming to make Mojo a superset of Python, we believe we can solve many of -Python's existing problems. +By aiming to make Mojo the best way to extend Python, we believe we can solve +many of Python's existing problems. Python has some well-known problems—most obviously, poor low-level performance and CPython implementation details like the global interpreter lock (GIL), @@ -327,17 +327,17 @@ use-cases of Python, they cannot solve the "two world problem." This approach drives fragmentation, and incompatibility makes _migration_ difficult to impossible—recall how challenging it was to migrate from Python 2 to Python 3. -### Python supersets with C compatibility +### Python family languages with C compatibility -Because Mojo is designed to be a superset of Python with improved systems -programming capabilities, it shares some high-level ideas with other Python -supersets like [Pyrex](https://wiki.python.org/moin/Pyrex) and -[Cython](https://cython.org/). Like Mojo, these projects define their own -language that also support the Python language. They allow you to write more +Because Mojo is designed to adopt the syntax of Python with improved systems +programming capabilities, it shares some high-level ideas with other members of +the Python family of languages like [Pyrex](https://wiki.python.org/moin/Pyrex) +and [Cython](https://cython.org/). Like Mojo, these projects define their own +language while also supporting the Python language. They allow you to write more performant extensions for Python that interoperate with both Python and C libraries. -These Python supersets are great for some kinds of applications, and they've +These Python family languages are great for some kinds of applications, and they've been applied to great effect by some popular Python libraries. However, they don't solve [Python's two-world problem](#the-two-world-problem) and because they rely on CPython for their core semantics, they can't work without it, diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index a25647594c..f7a4a796a4 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -264,7 +264,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Then, we can copy and paste our Python code. Mojo is a superset of Python, so the same Python code will run as Mojo code" + "Then, we can copy and paste our Python code. Mojo adopts the syntax of Python, so the same Python code will run as Mojo code" ] }, { diff --git a/proposals/mojo-and-dynamism.md b/proposals/mojo-and-dynamism.md index f1c48bc5e1..47a9afd38d 100644 --- a/proposals/mojo-and-dynamism.md +++ b/proposals/mojo-and-dynamism.md @@ -2,7 +2,7 @@ Mojo has the lofty goal of being a simple, powerful, and easy-to-use language like Python but with features that allow programmers to reach the performance of -C. One of Mojo's approaches is to start from being a superset of Python and +C. One of Mojo's approaches is to start by adopting the syntax of Python and provide an incremental typing-for-performance story where the performance of Python code can be incrementally improved by adding type annotations, explicit variable declarations and error handling, switching `def` to `fn`, and so on. @@ -47,10 +47,10 @@ would be member functions have their first argument bound to the new class instance through the Python [descriptor mechanism](https://docs.python.org/3/howto/descriptor.html#invocation-from-a-class). -Mojo as a superset of Python has to support the full "hash-table" dynamism in -classes for compatibility with Python, but reference semantic classes are also -important for systems programming and application programming, where this level -of dynamism isn't needed and is actively harmful. We need to decide how to +Mojo adopting the syntax of Python means we have to support the full "hash-table" +dynamism in classes for compatibility with Python, but reference semantic classes +are also important for systems programming and application programming, where this +level of dynamism isn't needed and is actively harmful. We need to decide how to handle this. One approach is to provide a decorator on class definitions (which can be opt-in diff --git a/proposals/ref-convention.md b/proposals/ref-convention.md index c67db5b262..3d69a9f9ce 100644 --- a/proposals/ref-convention.md +++ b/proposals/ref-convention.md @@ -327,7 +327,7 @@ commence! 👏 We have proposed two new conventions: an argument convention and a result convention. For both of these conventions, we have used the keyword `ref`. This terminology has precedent in C++, C#, and many other languages. However, Mojo -aims to be a superset of Python, and in Python, a "reference" is understood to +aims to adopt the syntax of Python, and in Python, a "reference" is understood to be a pointer to a heap-allocated, garbage-collected instance of a class. The argument conventions that we've proposed are entirely unrelated to that feature. Therefore, it seems wise to consider other syntaxes for the conventions. We From 28e3d2a53866f0fbd26885fc3aa7a59daeb02641 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:12:25 -0600 Subject: [PATCH 1791/2019] [External] [stdlib] Add string benchmarking infrastructure and basic benchmarks (#49095) [External] [stdlib] Add string benchmarking infrastructure and basic benchmarks Add string benchmarking infrastructure and basic benchmarks. Added data for benchmarking collections. UN charter: Taken from the official [UN website](https://www.un.org/en/about-us/un-charter/full-text) with the language abbreviations following ISO 639-1 except simplified mandarin chinese (zh-CN) ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3523 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3523 MODULAR_ORIG_COMMIT_REV_ID: d3b154bc993eccf4b80bb3773895bc28e1f41ee6 --- .../benchmarks/collections/bench_string.mojo | 274 ++ stdlib/benchmarks/collections/data/README.md | 9 + .../collections/data/UN_charter_AR.html | 2850 +++++++++++++++++ .../collections/data/UN_charter_AR.txt | 465 +++ .../collections/data/UN_charter_EN.html | 2333 ++++++++++++++ .../collections/data/UN_charter_EN.txt | 463 +++ .../collections/data/UN_charter_ES.html | 2309 +++++++++++++ .../collections/data/UN_charter_ES.txt | 453 +++ .../collections/data/UN_charter_RU.html | 2323 ++++++++++++++ .../collections/data/UN_charter_RU.txt | 468 +++ .../collections/data/UN_charter_zh-CN.html | 2365 ++++++++++++++ .../collections/data/UN_charter_zh-CN.txt | 609 ++++ 12 files changed, 14921 insertions(+) create mode 100644 stdlib/benchmarks/collections/bench_string.mojo create mode 100644 stdlib/benchmarks/collections/data/README.md create mode 100644 stdlib/benchmarks/collections/data/UN_charter_AR.html create mode 100644 stdlib/benchmarks/collections/data/UN_charter_AR.txt create mode 100644 stdlib/benchmarks/collections/data/UN_charter_EN.html create mode 100644 stdlib/benchmarks/collections/data/UN_charter_EN.txt create mode 100644 stdlib/benchmarks/collections/data/UN_charter_ES.html create mode 100644 stdlib/benchmarks/collections/data/UN_charter_ES.txt create mode 100644 stdlib/benchmarks/collections/data/UN_charter_RU.html create mode 100644 stdlib/benchmarks/collections/data/UN_charter_RU.txt create mode 100644 stdlib/benchmarks/collections/data/UN_charter_zh-CN.html create mode 100644 stdlib/benchmarks/collections/data/UN_charter_zh-CN.txt diff --git a/stdlib/benchmarks/collections/bench_string.mojo b/stdlib/benchmarks/collections/bench_string.mojo new file mode 100644 index 0000000000..f824a0d5f4 --- /dev/null +++ b/stdlib/benchmarks/collections/bench_string.mojo @@ -0,0 +1,274 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo-no-debug %s -t + +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run +from random import random_si64, seed +from pathlib import _dir_of_current_file +from collections import Optional +from utils._utf8_validation import _is_valid_utf8 +from os import abort + + +# ===----------------------------------------------------------------------===# +# Benchmark Data +# ===----------------------------------------------------------------------===# +fn make_string[ + length: UInt = 0 +](filename: StringLiteral = "UN_charter_EN.txt") -> String: + """Make a `String` made of items in the `./data` directory or random bytes + (ASCII value range) in case opening the file fails. + + Parameters: + length: The length in bytes of the resulting `String`. If == 0 -> the + whole file content. + + Args: + filename: The name of the file inside the `./data` directory. + """ + + try: + directory = _dir_of_current_file() / "data" + var f = open(directory / filename, "rb") + + @parameter + if length > 0: + var items = f.read_bytes(length) + i = 0 + while length > len(items): + items.append(items[i]) + i = i + 1 if i < len(items) - 1 else 0 + items.append(0) + return String(items^) + else: + return String(f.read_bytes()) + except e: + print(e) + return abort[String]() + + +# ===----------------------------------------------------------------------===# +# Benchmark string init +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_init(inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn(): + for _ in range(1000): + var d = String() + keep(d._buffer.data) + + b.iter[call_fn]() + + +# ===----------------------------------------------------------------------===# +# Benchmark string count +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_count[ + length: UInt = 0, + filename: StringLiteral = "UN_charter_EN", + sequence: StringLiteral = "a", +](inout b: Bencher) raises: + var items = make_string[length](filename + ".txt") + + @always_inline + @parameter + fn call_fn() raises: + var amnt = items.count(sequence) + keep(amnt) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark string split +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_split[ + length: UInt = 0, + filename: StringLiteral = "UN_charter_EN", + sequence: Optional[StringLiteral] = None, +](inout b: Bencher) raises: + var items = make_string[length](filename + ".txt") + + @always_inline + @parameter + fn call_fn() raises: + var res: List[String] + + @parameter + if sequence: + res = items.split(sequence.value()) + else: + res = items.split() + keep(res.data) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark string splitlines +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_splitlines[ + length: UInt = 0, filename: StringLiteral = "UN_charter_EN" +](inout b: Bencher) raises: + var items = make_string[length](filename + ".txt") + + @always_inline + @parameter + fn call_fn() raises: + var res = items.splitlines() + keep(res.data) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark string lower +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_lower[ + length: UInt = 0, filename: StringLiteral = "UN_charter_EN" +](inout b: Bencher) raises: + var items = make_string[length](filename + ".txt") + + @always_inline + @parameter + fn call_fn() raises: + var res = items.lower() + keep(res._buffer.data) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark string upper +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_upper[ + length: UInt = 0, filename: StringLiteral = "UN_charter_EN" +](inout b: Bencher) raises: + var items = make_string[length](filename + ".txt") + + @always_inline + @parameter + fn call_fn() raises: + var res = items.upper() + keep(res._buffer.data) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark string replace +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_replace[ + length: UInt = 0, + filename: StringLiteral = "UN_charter_EN", + old: StringLiteral = "a", + new: StringLiteral = "A", +](inout b: Bencher) raises: + var items = make_string[length](filename + ".txt") + + @always_inline + @parameter + fn call_fn() raises: + var res = items.replace(old, new) + keep(res._buffer.data) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark string _is_valid_utf8 +# ===----------------------------------------------------------------------===# +@parameter +fn bench_string_is_valid_utf8[ + length: UInt = 0, filename: StringLiteral = "UN_charter_EN" +](inout b: Bencher) raises: + var items = make_string[length](filename + ".html") + + @always_inline + @parameter + fn call_fn() raises: + var res = _is_valid_utf8(items.unsafe_ptr(), length) + keep(res) + + b.iter[call_fn]() + keep(bool(items)) + + +# ===----------------------------------------------------------------------===# +# Benchmark Main +# ===----------------------------------------------------------------------===# +def main(): + seed() + var m = Bench(BenchConfig(num_repetitions=1)) + m.bench_function[bench_string_init](BenchId("bench_string_init")) + alias filenames = ( + "UN_charter_EN", + "UN_charter_ES", + "UN_charter_AR", + "UN_charter_RU", + "UN_charter_zh-CN", + ) + alias old_chars = ("a", "ó", "ل", "и", "一") + alias new_chars = ("A", "Ó", "ل", "И", "一") + alias lengths = (10, 30, 50, 100, 1000, 10_000, 100_000, 1_000_000) + + @parameter + for i in range(len(lengths)): + alias length = lengths.get[i, Int]() + + @parameter + for j in range(len(filenames)): + alias fname = filenames.get[j, StringLiteral]() + alias old = old_chars.get[j, StringLiteral]() + alias new = new_chars.get[j, StringLiteral]() + m.bench_function[bench_string_count[length, fname, old]]( + BenchId("bench_string_count[" + str(length) + "]") + ) + m.bench_function[bench_string_split[length, fname, old]]( + BenchId("bench_string_split[" + str(length) + "]") + ) + m.bench_function[bench_string_split[length, fname]]( + BenchId( + "bench_string_split[" + str(length) + ", sequence=None]" + ) + ) + m.bench_function[bench_string_splitlines[length, fname]]( + BenchId("bench_string_splitlines[" + str(length) + "]") + ) + m.bench_function[bench_string_lower[length, fname]]( + BenchId("bench_string_lower[" + str(length) + "]") + ) + m.bench_function[bench_string_upper[length, fname]]( + BenchId("bench_string_upper[" + str(length) + "]") + ) + m.bench_function[bench_string_replace[length, fname, old, new]]( + BenchId("bench_string_replace[" + str(length) + "]") + ) + m.bench_function[bench_string_is_valid_utf8[length, fname]]( + BenchId("bench_string_is_valid_utf8[" + str(length) + "]") + ) + m.dump_report() diff --git a/stdlib/benchmarks/collections/data/README.md b/stdlib/benchmarks/collections/data/README.md new file mode 100644 index 0000000000..ca0d47b3f8 --- /dev/null +++ b/stdlib/benchmarks/collections/data/README.md @@ -0,0 +1,9 @@ +# Data for benchmarking collections + +As realistic as possible datasets. + +## UN charter + +Taken from the official [UN website](https://www.un.org/en/about-us/un-charter/full-text) +with the language abbreviations following ISO 639-1 except simplified mandarin +chinese (zh-CN). diff --git a/stdlib/benchmarks/collections/data/UN_charter_AR.html b/stdlib/benchmarks/collections/data/UN_charter_AR.html new file mode 100644 index 0000000000..b85d4cdb3f --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_AR.html @@ -0,0 +1,2850 @@ + + + + + + + + + + + + + + + + + + + + + ميثاق الأمم المتحدة (النص الكامل) | الأمم المتحدة + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    + + + + +
    +
    + + +
    + + + + + + + + + + +
    +
    +
    + +
    + + +

    ميثاق الأمم المتحدة (النص الكامل)

    + + + + +
    +
    + + +
    + + + + +
    +
    +
    +

    الديباجة

    + +

    نحن شعوب الأمم المتحدة وقد آلينا على أنفسنا

    + +

    أن ننقذ الأجيال المقبلة من ويلات الحرب التي في خلال جيل واحد جلبت على الإنسانية مرتين أحزاناً يعجز عنها الوصف،

    + +

    وأن نؤكد من جديد إيماننا بالحقوق الأساسية للإنسان وبكرامة الفرد وقدره وبما للرجال والنساء والأمم كبيرها وصغيرها من حقوق متساوية،

    + +

    وأن نبيّن الأحوال التي يمكن في ظلها تحقيق العدالة واحترام الالتزامات الناشئة عن المعاهدات وغيرها من مصادر القانون الدولي،

    + +

    وأن ندفع بالرقي الاجتماعي قدماً، وأن نرفع مستوى الحياة في جو من الحرية أفسح.

    + +

    وفي سبيل هذه الغايات اعتزمنا

    + +

    أن نأخذ أنفسنا بالتسامح، وأن نعيش معاً في سلام وحسن جوار ،

    + +

    وأن نضم قوانا كي نحتفظ بالسلم والأمن الدولي،

    + +

    وأن نكفل بقبولنا مبادئ معيّنة ورسم الخطط اللازمة لها ألاّ تستخدم القوة المسلحة في غير المصلحة المشتركة ،

    + +

    وأن نستخدم الأداة الدولية في ترقية الشؤون الاقتصادية والاجتماعية للشعوب جميعها،

    + +

    قد قرّرنا أن نوحّد جهودنا لتحقيق هذه الأغراض

    + +

    ولهذا فإن حكوماتنا المختلفة على يد مندوبيها المجتمعين في مدينة سان فرانسيسكو الذين قدّموا وثائق التفويض المستوفية للشرائط، قد ارتضت ميثاق الأمم المتحدة هذا، وأنشأت بمقتضاه هيئة دولية تُسمّى "الأمم المتحدة".

    + +

    الفصل الأول: مقاصد الهيئة ومبادئها

    + +

    المادة 1

    + +

    مقاصـد الأمـم المتحدة هي:

    + +
      +
    1. +

      حفظ السلم والأمن الدولي، وتحقيقاً لهذه الغاية تتخذ الهيئة التدابير المشتركة الفعّالة لمنع الأسباب التي تهدد السلم ولإزالتها، وتقمع أعمال العدوان وغيرها من وجوه الإخلال بالسلم، وتتذرّع بالوسائل السلمية، وفقاً لمبادئ العدل والقانون الدولي، لحل المنازعات الدولية التي قد تؤدي إلى الإخلال بالسلم أو لتسويتها.

      +
    2. +
    3. +

      إنماء العلاقات الودية بين الأمم على أساس احترام المبدأ الذي يقضي بالتسوية في الحقوق بين الشعوب وبأن يكون لكل منها تقرير مصيرها، وكذلك اتخاذ التدابير الأخرى الملائمة لتعزيز السلم العام.

      +
    4. +
    5. +

      تحقيق التعاون الدولي على حل المسائل الدولية ذات الصبغة الاقتصادية والاجتماعية والثقافية والإنسانية وعلى تعزيز احترام حقوق الإنسان والحريات الأساسية للناس جميعاً والتشجيع على ذلك إطلاقاً بلا تمييز بسبب الجنس أو اللغة أو الدين ولا تفريق بين الرجال والنساء.

      +
    6. +
    7. +

      جعل هذه الهيئة مرجعاً لتنسيق أعمال الأمم وتوجيهها نحو إدراك هذه الغايات المشتركة.

      +
    8. +
    + +

    المادة 2

    + +

    تعمل الهيئة وأعضاؤها في سعيها وراء المقاصد المذكورة في المادة الأولى وفقاً ‏‏ للمبادئ الآتية:

    + +
      +
    1. +

      تقوم الهيئة على مبدأ المساواة في السيادة بين جميع أعضائها.‏

      +
    2. +
    3. +

      لكي يكفل أعضاء الهيئة لأنفسهم جميعاً الحقوق والمزايا المترتبة على صفة العضوية يقومون في حسن نية ‏بالالتزامات التي أخذوها على أنفسهم بهذا الميثاق.

      +
    4. +
    5. +

      يفض جميع أعضاء الهيئة منازعاتهم الدولية بالوسائل السلمية على وجه لا يجعل السلم والأمن والعدل الدولي عرضة للخطر.

      +
    6. +
    7. +

      يمتنع أعضاء الهيئة جميعاً في علاقاتهم الدولية عن التهديد باستعمال القوة أو استخدامها ضد سلامة الأراضي أو الاستقلال السياسي لأية دولة أو على أي وجه آخر لا يتفق ومقاصد "الأمم المتحدة"..

      +
    8. +
    9. +

      يقدّم جميع الأعضاء كل ما في وسعهم من عون إلى "الأمم المتحدة" في أي عمل تتخذه وفق هذا الميثاق، كما يمتنعون عن مساعدة أية دولة تتخذ الأمم المتحدة إزاءها عملاً من أعمال المنع أو القمع.

      +
    10. +
    11. +

      تعمل الهيئة على أن تسير الدول غير الأعضاء فيها على هذه المبادئ بقدر ما تقتضيه ضرورة حفظ السلم ‏والأمن الدولي.‏

      +
    12. +
    13. +

      ليس في هذا الميثاق ما يسوغ ”للأمم المتحدة“ أن تتدخل في الشؤون التي تكون من صميم السلطان الداخلي ‏لدولة ما، وليس فيه ما يقتضي الأعضاء أن يعرضوا مثل هذه المسائل لأن تحل بحكم هذا الميثاق، على أن ‏هذا المبدأ لا يخلّ بتطبيق تدابير القمع الواردة في الفصل السابع.‏

      +
    14. +
    + +

    الفصل الثاني : العضوية

    + +

    المادة 3

    + +

    الأعضاء الأصليون للأمم المتحدة هـم الدول التي اشتركت في مؤتمر الأمم المتحدة لوضع نظام الهيئة الدولية المنعقد في سان فرانسيسكو، والتي توقّع هذا الميثاق وتصدّق عليه طبقاً للمادة 110، وكذلك الدول التي وقّعت من قبل تصريح الأمم المتحدة الصادر في أول كانون الثاني/يناير سنة 1942، وتوقّع هذا الميثاق وتصدّق عليه.

    + +

    المادة 4

    + +
      +
    1. ‏العضوية في "الأمم المتحدة" مباحة لجميع الدول الأخرى المُحبة للسلام، والتي تأخذ نفسها بالالتزامات التي يتضمنها هذا الميثاق، والتي ترى الهيئة أنها قادرة على تنفيذ هذه الالتزامات وراغبة فيه .
    2. +
    3. قبول أية دولة من هذه الدول في عضوية "الأمم المتحدة" يتم بقرار من الجمعية العامة بناءً على توصية مجلس الأمن .
    4. +
    + +

    المادة 5

    + +

    يجوز للجمعية العامة أن توقف أي عضو اتخذ مجلس الأمن قِبَله عملاً من أعمال المنع أو القمع، عن مباشرة حقوق العضوية ومزاياها، ويكون ذلك بناءً على توصية ‏مجلس الأمن، ولمجلس الأمن أن يرد لهذا العضو مباشرة تلك الحقوق والمزايا.

    + +

    المادة 6

    + +

    إذا أمعن عضو من أعضاء "الأمم المتحدة" في انتهاك مبادئ الميثاق جاز للجمعية العامة أن تفصله من الهيئة بناءً على توصية مجلس الأمن.

    + +

    الفصل الثالث : فروع الهيئـة

    + +

    المادة 7

    + +
      +
    1. تنشأ الهيئات الآتية فروعاً رئيسية للأمم المتحدة: الجمعيـة العـامة، ومجلـس الأمـن، والمجلـس الاقتصـادي والاجتمـاعي، و مجلـس وصـاية، و محكمـة عـدل دوليـة و أمـانة
    2. +
    3. +

      ‏يجوز أن ينشأ وفقاً لأحكام هذا الميثاق ما يرى ضرورة إنشائه من فروع ثانوية أخرى .

      +
    4. +
    + +

    المادة 8

    + +

    لا تفرض "الأمم المتحدة" قيوداً تحدّ بها جواز اختيار الرجال والنساء للاشتراك بأية صفة وعلى وجه المساواة في فروعها الرئيسية والثانوية.

    + +

    الفصل الرابع: الجمعيـة العـامة

    + +

    تأليفهـا

    + +

    المادة 9

    + +
      +
    1. تتألف الجمعية العامة من جميع أعضاء "الأمم المتحدة".
    2. +
    3. لا يجوز أن يكون للعضو الواحد أكثر من خمسة مندوبين في الجمعية العامة.
    4. +
    + +

    وظائف الجمعية وسلطاتها

    + +

    المادة 10

    + +

    للجمعية العامة أن تناقش أية مسألة أو أمر يدخل في نطاق هذا الميثاق أو يتصل بسلطات فرع من الفروع المنصوص عليها فيه أو وظائفه. كما أن لها في ما عدا ما نصّ عليه في المادة 12 أن توصي أعضاء الهيئة أو مجلس الأمن أو كليهما بما تراه في تلك المسائل والأمور.

    + +

    المادة 11

    + +
      +
    1. للجمعية العامة أن تنظر في المبادئ العامة للتعاون في حفظ السلم والأمن الدولي ويدخل في ذلك المبادئ المتعلقة بنزع السلاح وتنظيم التسليح، كما أن لها أن تقدّم توصياتها بصدد هذه المبادئ إلى الأعضاء أو إلى مجلس الأمن أو إلى كليهما.
    2. +
    3. للجمعية العامة أن تناقش أية مسألة يكون لها صلة بحفظ السلم والأمن الدولي يرفعها إليها أي عضو من أعضاء الأمم المتحدة ومجلس الأمن أو دولة ليست من أعضائها وفقاً لأحكام الفقرة الثانية من المادة 35، ولها - فيما عدا ما تنصّ عليه المادة الثانية عشرة - أن تقدّم توصياتها بصدد هذه المسائل للدولة أو الدول صاحبة الشأن أو لمجلس الأمن أو لكليهما معاً. وكل مسألة مما تقدّم ذكره يكون من الضروري فيها القيام بعمل ما، ينبغي أن تحيلها الجمعية العامة على مجلس الأمن قبل بحثها أو بعده.
    4. +
    5. للجمعية العامة أن تسترعي نظر مجلس الأمن إلى الأحوال التي يحتمل أن تعرّض السلم والأمن الدولي للخطر.
    6. +
    7. لا تحدّ سلطات الجمعية العامة المبيّنة في هذه المادة من عموم مدى المادة العاشرة.
    8. +
    + +

    المادة 12

    + +
      +
    1. عندما يباشر مجلس الأمن، بصدد نزاع أو موقف ما، الوظائف التي رسمت في الميثاق، فليس للجمعية العامة أن تقدّم أية توصية في شأن هذا النزاع أو الموقف إلاّ إذا طلب ذلك منها مجلس الأمن.
    2. +
    3. يخطر الأمين العام - بموافقة مجلس الأمن - الجمعية العامة في كل دور من أدوار انعقادها بكل المسائل المتصلة بحفظ السلم والأمن الدولي التي تكون محل نظر مجلس الأمن، كذلك يخطرها أو يخطر أعضاء "الأمم المتحدة" إذا لم تكن الجمعية العامة في دور انعقادها، بفراغ مجلس الأمن من نظر تلك المسائل وذلك بمجرد انتهائه منها.
    4. +
    + +

    المادة 13

    + +
      +
    1. تنشئ الجمعية العامة دراسات وتشير بتوصيات بقصد: +
        +
      1. إنماء التعاون الدولي في الميدان السياسي وتشجيع التقدّم المطرد للقانون الدولي وتدوينه
      2. +
      3. ب - إنماء التعاون الدولي في الميادين الاقتصادية والاجتماعية والثقافية والتعليمية والصحية، والإعانة على تحقيق حقوق الإنسان والحريات الأساسية للناس كافة بلا تمييز بينهم في الجنس أو اللغة أو الدين ولا تفريق بين الرجال والنساء.
      4. +
      +
    2. +
    3. تبعات الجمعية العامة ووظائفها وسلطاتها الأخرى في ما يختص بالمسائل الواردة في الفقرة السابقة (ب) بيّنة في الفصلين التاسع والعاشر من هذا الميثاق.
    4. +
    + +

    المادة 14

    + +

    مع مراعاة أحكام المادة الثانية عشرة، للجمعية العامة أن توصي باتخاذ التدابير لتسوية أي موقف، مهما يكن منشؤه، تسوية سلمية متى رأت أن هذا الموقف قد يضر بالرفاهية العامة أو يعكّر صفو العلاقات الودية بين الأمم، ويدخل في ذلك المواقف الناشئة عن انتهاك أحكام هذا الميثاق الموضحة لمقاصد الأمم المتحدة ومبادئها.

    + +

    المادة 15

    + +
      +
    1. تتلقى الجمعية العامة تقارير سنوية وأخرى خاصة من مجلس الأمن وتنظر فيها، وتتضمن هذه التقارير بياناً عن التدابير التي يكون مجلس الأمن قد قرّرها أو اتخذها لحفظ السلم والأمن الدولي.
    2. +
    3. تتلقى الجمعية العامة تقارير من الفروع الأخرى للأمم المتحدة وتنظر فيها.
    4. +
    + +

    المادة 16

    + +

    تباشر الجمعية العامة الوظائف الرسمية التي رسمت لها بمقتضى الفصلين الثاني عشر والثالث عشر في ما يتعلق بنظام الوصاية الدولية، ويدخل في ذلك المصادقة على اتفاقات الوصاية بشأن المواقع التي تعتبر أنها مواقع استراتيجية.

    + +

    المادة 17

    + +
      +
    1. تنظر الجمعية العامة في ميزانية الهيئة وتصدّق عليها.
    2. +
    3. يتحمّل الأعضاء نفقات الهيئة حسب الأنصبة التي تقرّرها الجمعية العامة.
    4. +
    5. تنظر الجمعية العامة في أية ترتيبات مالية أو متعلقة بالميزانية مع الوكالات المتخصصة المشار إليها في المادة 57. وتصدّق عليها وتدرس الميزانيات الإدارية لتلك الوكالات لكي تقدّم لها توصياتها.
    6. +
    + +

    التصـويت

    + +

    المادة 18

    + +
      +
    1. يكون لكل عضو في "الأمم المتحدة" صوت واحد في الجمعية العامة.
    2. +
    3. تصدر الجمعية العامة قراراتها في المسائل العامة بأغلبية ثلثي الأعضاء الحاضرين المشتركين في التصويت. وتشمل هذه المسائل: التوصيات الخاصة بحفظ السلم والأمن الدولي، وانتخاب أعضاء مجلس الأمن غير الدائمين، وانتخاب أعضاء المجلس الاقتصادي والاجتماعي، وانتخاب أعضاء مجلس الوصاية وفقاً لحكم الفقرة الأولى (ج) من المادة 86، وقبول أعضاء جدد في "الأمم المتحدة" ووقف الأعضاء عن مباشرة حقوق العضوية والتمتع بمزاياها، وفصل الأعضاء، والمسائل المتعلقة بسير نظام الوصاية، والمسائل الخاصة بالميزانية.
    4. +
    5. القرارات في المسائل الأخرى - ويدخل في ذلك تحديد طوائف المسائل الإضافية التي تتطلب في إقرارها أغلبية الثلثين - تصدر بأغلبية الأعضاء الحاضرين المشتركين في التصويت.
    6. +
    + +

    المادة 19

    + +

    لا يكون لعضو الأمم المتحدة الذي يتأخر عن تسديد اشتراكاته المالية في الهيئة حق التصويت في الجمعية العامة إذا كان المتأخر عليه مساوياً لقيمة الاشتراكات المستحقة عليه في السنتين الكاملتين السابقتين أو زائداً عنها، وللجمعية العامة مع ذلك أن تسمح لهذا العضو بالتصويت إذا اقتنعت بأن عدم الدفع ناشئ عن أسباب لا قبل للعضو بها.

    + +

    الإجـراءات

    + +

    المادة 20

    + +

    تجتمع الجمعية العامة في أدوار انعقاد عادية وفي أدوار انعقاد سنوية خاصة بحسب ما تدعو إليه الحاجة. ويقوم بالدعوة إلى أدوار الانعقاد الخاصة الأمين العام بناءً على طلب مجلس الأمن أو أغلبية أعضاء "الأمم المتحدة".

    + +

    المادة 21

    + +

    تضع الجمعية العامة لائحة إجراءاتها، وتنتخب رئيسها لكل دور انعقاد.

    + +

    المادة 22

    + +

    للجمعية العامة أن تنشئ من الفروع الثانوية ما تراه ضروريا للقيام بوظائفها.

    + +

    الفصل الخامس: مجلـس الأمـن

    + +

    تأليفـه

    + +

    المادة 23

    + +
      +
    1. يتألف مجلس الأمن من خمسة عشر عضواً من الأمم المتحدة، وتكون جمهورية الصين، وفرنسا، واتحاد الجمهوريات الاشتراكية السوفياتية، والمملكة المتحدة لبريطانيا العظمى وأيرلندا الشمالية، والولايات المتحدة الأمريكية أعضاء دائمين فيه. وتنتخب الجمعية العامة عشرة أعضاء آخرين من الأمم المتحدة ليكونوا أعضاء غير دائمين في المجلس. ويراعى في ذلك بوجه خاص وقبل كل شيء مساهمة أعضاء الأمم المتحدة في حفظ السلم والأمن الدولي وفي مقاصد الهيئة الأخرى، كما يراعى أيضاً التوزيع الجغرافي العادل.
    2. +
    3. ينتخب أعضاء مجلس الأمن غير الدائمين لمدة سنتين، على أنه في أول انتخاب للأعضاء غير الدائمين بعد زيادة عدد أعضاء مجلس الأمن من أحد عشر عضواً إلى خمسة عشر عضواً، يختار اثنان من الأعضاء الأربعة الإضافيين لمدة سنة واحدة والعضو الذي انتهت مدته لا يجوز إعادة انتخابه على الفور.
    4. +
    5. يكون لكل عضو في مجلس الأمن مندوب واحد.
    6. +
    + +

    الوظائف والسلطـات

    + +

    المادة 24

    + +
      +
    1. رغبة في أن يكون العمل الذي تقوم به "الأمم المتحدة" سريعاً فعالاً، يعهد أعضاء تلك الهيئة إلى مجلس الأمن بالتبعات الرئيسية في أمر حفظ السلم والأمن الدولي ويوافقون على أن هذا المجلس يعمل نائباً عنهم في قيامه بواجباته التي تفرضها عليه هذه التبعات.
    2. +
    3. يعمل مجلس الأمن، في أداء هذه الواجبات وفقاً لمقاصد "الأمم المتحدة" ومبادئها والسلطات الخاصة المخوّلة لمجلس الأمن لتمكينه من القيام بهذه الواجبات مبينة في الفصول السادس والسابع والثامن والثاني عشر
    4. +
    5. يرفع مجلس الأمن تقارير سنوية، وأخرى خاصة، إذا اقتضت الحال إلى الجمعية عامة لتنظر فيها.
    6. +
    + +

    المادة 25

    + +

    يتعهد أعضاء "الأمم المتحدة" بقبول قرارات مجلس الأمن وتنفيذها وفق هذا الميثاق.

    + +

    المادة 26

    + +

    رغبة في إقامة السلم والأمن الدولي وتوطيدهما بأقل تحويل لموارد العالم الإنسانية والاقتصادية إلى ناحية التسليح، يكون مجلس الأمن مسؤولاً بمساعدة لجنة أركان الحرب المشار إليها في المادة 47 عن وضع خطط تعرض على أعضاء "الأمم المتحدة" لوضع منهاج لتنظيم التسليح.

    + +

    التصويت

    + +

    المادة 27

    + +
      +
    1. يكون لكل عضو من أعضاء مجلس الأمن صوت واحد.
    2. +
    3. تصدر قرارات مجلس الأمن في المسائل الإجرائية بموافقة تسعة من أعضائه.
    4. +
    5. تصدر قرارات مجلس الأمن في المسائل الأخرى كافة بموافقة أصوات تسعة من أعضائه يكون من بينها أصوات الأعضاء الدائمين متفقة، بشرط أنه في القرارات المتخذة تطبيقاً لأحكام الفصل السادس والفقرة 3 من المادة 52 يمتنع من كان طرفاً في النزاع عن التصويت.
    6. +
    + +

    الإجـراءات

    + +

    المادة 28

    + +
      +
    1. ينظم مجلس الأمن على وجه يستطيع معه العمل باستمرار، ولهذا الغرض يمثل كل عضو من أعضائه تمثيلاً دائماً في مقر الهيئة.
    2. +
    3. يعقد مجلس الأمن اجتماعات دورية يمثل فيها كل عضو من أعضائه - إذا شاء ذلك - بأحد رجال حكومته أو بمندوب آخر يسميه لهذا الغرض خاصة.
    4. +
    5. لمجلس الأمن أن يعقد اجتماعات في غير مقر الهيئة إذا رأى أن ذلك أدنى إلى تسهيل أعماله.
    6. +
    + +

    المادة 29

    + +

    لمجلس الأمن أن ينشئ من الفروع الثانوية ما يرى له ضرورة لأداء وظائفه.

    + +

    المادة 30

    + +

    يضع مجلس الأمن لائحة إجراءاته ويدخل فيها طريقة اختيار رئيسه.

    + +

    المادة 31

    + +

    لكل عضو من أعضاء "الأمم المتحدة" من غير أعضاء مجلس الأمن أن يشترك بدون تصويت في مناقشة أية مسألة تعرض على مجلس الأمن إذا رأى المجلس أن مصالح هذا العضو تتأثر بها بوجه خاص.

    + +

    المادة 32

    + +

    كل عضو من أعضاء "الأمم المتحدة" ليس بعضو في مجلس الأمن، وأية دولة ليست عضواً في "الأمم المتحدة" إذا كان أيهما طرفاً في نزاع معروض على مجلس الأمن لبحثه يدعى إلى الاشتراك في المناقشات المتعلقة بهذا النزاع دون أن يكون له حق في التصويت، ويضع مجلس الأمن الشروط التي يراها عادلة لاشتراك الدولة التي ليست من أعضاء "الأمم المتحدة".

    + +

    الفصل السادس: حل المنازعات حلاً سلمياً

    + +

    المادة 33

    + +
      +
    1. يجب على أطراف أي نزاع من شأن استمراره أن يعرض حفظ السلم والأمن الدولي للخطر أن يلتمسوا حله بادئ ذي بدء بطريق المفاوضة والتحقيق والوساطة والتوفيق والتحكيم والتسوية القضائية، أو أن يلجأوا إلى الوكالات والتنظيمات الإقليمية أو غيرها من الوسائل السلمية التي يقع عليها اختيارها.
    2. +
    3. ويدعو مجلس الأمن أطراف النزاع إلى أن يسووا ما بينهم من النزاع بتلك الطرق إذا رأى ضرورة ذلك.
    4. +
    + +

    المادة 34

    + +

    لمجلس الأمن أن يفحص أي نزاع أو أي موقف قد يؤدي إلى احتكاك دولي أو قد يثير نزاعا لكي يقرر ما إذا كان استمرار هذا النزاع أو الموقف من شأنه أن يعرض للخطر حفظ السلم والأمن الدولي.

    + +

    المادة 35

    + +
      +
    1. لكل عضو من "الأمم المتحدة" أن ينبه مجلس الأمن أو الجمعية العامة إلى أي نزاع أو موقف من النوع المشار إليه في المادة الرابعة والثلاثين.
    2. +
    3. لكل دولة ليست عضواً في "الأمم المتحدة" أن تنبه مجلس الأمن أو الجمعية العامة إلى أي نزاع تكون طرفا فيه إذا كانت تقبل مقدماً في خصوص هذا النزاع التزامات الحل السلمي المنصوص عليها في هذا الميثاق.
    4. +
    5. تجرى أحكام المادتين 11 و12 على الطريقة التي تعالج بها الجمعية العامة المسائل التي تنبه إليها وفقا لهذه المادة.
    6. +
    + +

    المادة 36

    + +
      +
    1. لمجلس الأمن في أية مرحلة من مراحل نزاع من النوع المشار إليه في المادة 33 أو موقف شبيه به أن يوصي بما يراه ملائماً من الإجراءات وطرق التسوية.
    2. +
    3. على مجلس الأمن أن يراعي ما اتخذه المتنازعون من إجراءات سابقة لحل النزاع القائم بينهم.
    4. +
    5. على مجلس الأمن وهو يقدم توصياته وفقا لهذه المادة أن يراعي أيضاً أن المنازعات القانونية يجب على أطراف النزاع - بصفة عامة - أن يعرضوها على محكمة العدل الدولية وفقاً لأحكام النظام الأساسي لهذه المحكمة.
    6. +
    + +

    المادة 37

    + +
      +
    1. إذا أخفقت الدول التي يقوم بينها نزاع من النوع المشار إليه في المادة 33 في حله بالوسائل المبينة في تلك المادة وجب عليها أن تعرضه على مجلس الأمن.
    2. +
    3. إذا رأى مجلس الأمن أن استمرار هذا النزاع من شأنه في الواقع، أن يعرض للخطر حفظ السلم والأمن الدولي قرر ما إذا كان يقوم بعمل وفقاً للمادة 36 أو يوصي بما يراه ملائماً من شروط حل النزاع.
    4. +
    + +

    المادة 38

    + +

    لمجلس الأمن - إذا طلب إليه جميع المتنازعين ذلك - أن يقدم إليهم توصياته بقصد حل النزاع حلاً سلمياً، وذلك بدون إخلال بأحكام المواد من 33 إلى 37.

    + +

    الفصل السابع: فيما يتخذ من الأعمال في حالات تهديد السلم والإخلال به ووقوع العدوان

    + +

    المادة 39

    + +

    يقرر مجلس الأمن ما إذا كان قد وقع تهديد للسلم أو إخلال به أو كان ما وقع عملاًً من أعمال العدوان، ويقدم في ذلك توصياته أو يقرر ما يجب اتخاذه من التدابير طبقاً لأحكام المادتين 41 و42 لحفظ السلم والأمن الدولي أو إعادته إلى نصابه.

    + +

    المادة 40

    + +

    منعاً لتفاقم الموقف، لمجلس الأمن، قبل أن يقوم توصياته أو يتخذ التدابير المنصوص عليها في المادة 39، أن يدعو المتنازعين للأخذ بما يراه ضرورياً أو مستحسناً من تدابير مؤقتة، ولا تخل هذه التدابير المؤقتة بحقوق المتنازعين ومطالبهم أو بمركزهم، وعلى مجلس الأمن أن يحسب لعدم أخذ المتنازعين بهذه التدابير المؤقتة حسابه.

    + +

    المادة 41

    + +

    لمجلس الأمن أن يقرر ما يجب اتخاذه من التدابير التي لا تتطلب استخدام القوات المسلحة لتنفيذ قراراته، وله أن يطلب إلى أعضاء "الأمم المتحدة" تطبيق هذه التدابير، ويجوز أن يكون من بينها وقف الصلات الاقتصادية والمواصلات الحديدية والبحرية والجوية والبريدية والبرقية واللاسلكية وغيرها من وسائل المواصلات وقفا جزئياً أو كليا وقطع العلاقات الدبلوماسية.

    + +

    المادة 42

    + +

    إذا رأى مجلس الأمن أن التدابير المنصوص عليها في المادة 41 لا تفي بالغرض أو ثبت أنها لم تف به، جاز له أن يتخذ بطريق القوات الجوية والبحرية والبرية من الأعمال ما يلزم لحفظ السلم والأمن الدولي أو لإعادته إلى نصابه. ويجوز أن تتناول هذه الأعمال المظاهرات والحصر والعمليات الأخرى بطريق القوات الجوية أو البحرية أو البرية التابعة لأعضاء "الأمم المتحدة".

    + +

    المادة 43

    + +
      +
    1. يتعهد جميع أعضاء "الأمم المتحدة" في سبيل المساهمة في حفظ السلم والأمن الدولي، أن يضعوا تحت تصرف مجلس الأمن بناء على طلبه وطبقاً لاتفاق أو اتفاقات خاصة ما يلزم من القوات المسلحة والمساعدات والتسهيلات الضرورية لحفظ السلم والأمن الدولي ومن ذلك حق المرور.
    2. +
    3. يجب أن يحدد ذلك الاتفاق أو تلك الاتفاقات عدد هذه القوات وأنواعها ومدى استعدادها وأماكنها عموماً ونوع التسهيلات والمساعدات التي تقدم.
    4. +
    5. تجرى المفاوضة في الاتفاق أو الاتفاقات المذكورة بأسرع ما يمكن بناءً على طلب مجلس الأمن، وتبرم بين مجلس الأمن وبين أعضاء "الأمم المتحدة" أو بينه وبين مجموعات من أعضاء "الأمم المتحدة"، وتصدق عليها الدول الموقعة وفق مقتضيات أوضاعها الدستورية.
    6. +
    + +

    المادة 44

    + +

    إذا قرر مجلس الأمن استخدام القوة، فإنه قبل أن يطلب من عضو غير ممثل فيه تقديم القوات المسلحة وفاءً بالالتزامات المنصوص عليها في المادة 43، ينبغي له أن يدعو هذا العضو إلى أن يشترك إذا شاء في القرارات التي يصدرها فيما يختص باستخدام وحدات من قوات هذا العضو المسلحة.

    + +

    المادة 45

    + +

    رغبة في تمكين الأمم المتحدة من اتخاذ التدابير الحربية العاجلة يكون لدى الأعضاء وحدات جوية أهلية يمكن استخدامها فوراً لأعمال القمع الدولية المشتركة. ويحدد مجلس الأمن قوى هذه الوحدات ومدى استعدادها والخطط لأعمالها المشتركة، وذلك بمساعدة لجنة أركان الحرب وفي الحدود الواردة في الاتفاق أو الاتفاقات الخاصة المشار إليها في المادة 43.

    + +

    المادة 46

    + +

    الخطط اللازمة لاستخدام القوة المسلحة يضعها مجلس الأمن بمساعدة لجنة أركان الحرب.

    + +

    المادة 47

    + +
      +
    1. تشكل لجنة من أركان الحرب تكون مهمتها أن تسدي المشورة والمعونة إلى مجلس الأمن وتعاونه في جميع المسائل المتصلة بما يلزمه من حاجات حربية لحفظ السلم والأمن الدولي ولاستخدام القوات الموضوعة تحت تصرفه وقيادتها ولتنظيم التسليح ونزع السلاح بالقدر المستطاع.
    2. +
    3. تشكل لجنة أركان الحرب من رؤساء أركان حرب الأعضاء الدائمين في مجلس الأمن أو من يقوم مقامهم، وعلى اللجنة أن تدعو أي عضو في "الأمم المتحدة" من الأعضاء غير الممثلين فيها بصفة دائمة للإشراف في عملها إذا اقتضى حسن قيام اللجنة بمسؤولياتها أن يساهم هذا العضو في عملها.
    4. +
    5. لجنة أركان الحرب مسؤولة تحت إشراف مجلس الأمن عن التوجيه الاستراتيجي لأية قوات مسلحة موضوعة تحت تصرف المجلس. أما المسائل المرتبطة بقيادة هذه القوات فستبحث فيما بعد.
    6. +
    7. للجنة أركان الحرب أن تنشئ لجاناً فرعية إقليمية إذا خوّلها ذلك مجلس الأمن وبعد التشاور مع الوكالات الإقليمية صاحبة الشأن.
    8. +
    + +

    المادة 48

    + +
      +
    1. الأعمال اللازمة لتنفيذ قرارات مجلس الأمن لحفظ السلم والأمن الدولي يقوم بها جميع أعضاء "الأمم المتحدة" أو بعض هؤلاء الأعضاء وذلك حسبما يقرره المجلس.
    2. +
    3. يقوم أعضاء "الأمم المتحدة" بتنفيذ القرارات المتقدمة مباشرة وبطريق العمل في الوكالات الدولية المتخصصة التي يكونون أعضاء فيها.
    4. +
    + +

    المادة 49

    + +

    يتضافر أعضاء "الأمم المتحدة" على تقديم المعونة المتبادلة لتنفيذ التدابير التي قررها مجلس الأمن.

    + +

    المادة 50

    + +

    إذا اتخذ مجلس الأمن ضد أية دولة تدابير منع أو قمع فإن لكل دولة أخرى - سواء أكانت من أعضاء "الأمم المتحدة" أم لم تكن - تواجه مشاكل اقتصادية خاصة تنشأ عن تنفيذ هذه التدابير، الحق في أن تتذاكر مع مجلس الأمن بصدد حل هذه المشاكل.

    + +

    المادة 51

    + +

    ليس في هذا الميثاق ما يضعف أو ينتقص الحق الطبيعي للدول، فرادى أو جماعات، في الدفاع عن أنفسهم إذا اعتدت قوة مسلحة على أحد أعضاء "الأمم المتحدة" وذلك إلى أن يتخذ مجلس الأمن التدابير اللازمة لحفظ السلم والأمن الدولي، والتدابير التي اتخذها الأعضاء استعمالاً لحق الدفاع عن النفس تبلغ إلى المجلس فورا، ولا تؤثر تلك التدابير بأي حال فيما للمجلس - بمقتضى سلطته ومسؤولياته المستمرة من أحكام هذا الميثاق - من الحق في أن يتخذ في أي وقت ما يرى ضرورة لاتخاذه من الأعمال لحفظ السلم والأمن الدولي أو إعادته إلى نصابه.

    + +

    الفصل الثامـن: التنظيمات الإقليمية

    + +

    المادة 52

    + +
      +
    1. ليس في هذا الميثاق ما يحول دون قيام تنظيمات أو وكالات إقليمية تعالج من الأمور المتعلقة بحفظ السلم والأمن الدولي ما يكون العمل الإقليمي صالحاً فيها ومناسباً ما دامت هذه التنظيمات أو الوكالات الإقليمية ونشاطها متلائمة مع مقاصد "الأمم المتحدة" ومبادئها.
    2. +
    3. يبذل أعضاء "الأمم المتحدة" الداخلون في مثل هذه التنظيمات أو الذين تتألف منهم تلك الوكالات كل جهدهم لتدبير الحل السلمي للمنازعات المحلية عن طريق هذه التنظيمات الإقليمية أو بواسطة هذه الوكالات وذلك قبل عرضها على مجلس الأمن.
    4. +
    5. على مجلس الأمن أن يشجع على الاستكثار من الحل السلمي لهذه المنازعات المحلية بطريق هذه التنظيمات الإقليمية أو بواسطة تلك الوكالات الإقليمية بطلب من الدول التي يعنيها الأمر أو بالإحالة عليها من جانب مجلس الأمن.
    6. +
    7. لا تعطل هذه المادة بحال من الأحوال تطبيق المادتين 34 و 35.
    8. +
    + +

    المادة 53

    + +
      +
    1. يستخدم مجلس الأمن تلك التنظيمات والوكالات الإقليمية في أعمال القمع، كلما رأى ذلك ملائماً ويكون عملها حينئذ تحت مراقبته وإشرافه. أما التنظيمات والوكالات نفسها فإنه لا يجوز بمقتضاها أو على يدها القيام بأي عمل من أعمال القمع بغير إذن المجلس، ويستثنى مما تقدم التدابير التي تتخذ ضد أية دولة من دول الأعداء المعرّفة في الفقرة 2 من هذه المادة مما هو منصوص عليه في المادة 107 أو التدابير التي يكون المقصود بها في التنظيمات الإقليمية منع تجدد سياسة العدوان من جانب دولة من تلك الدول، وذلك إلى أن يحين الوقت الذي قد يعهد فيه إلى الهيئة، بناءً على طلب الحكومات ذات الشأن، بالمسؤولية عن منع كل عدوان آخر من جانب أية دولة من تلك الدول.
    2. +
    3. تنطبق عبارة "الدولة المعادية" المذكورة في الفقرة 1 من هذه المادة على أية دولة كانت في الحرب العالمية الثانية من أعداء أية دولة موقعة على هذا الميثاق.
    4. +
    + +

    المادة 54

    + +

    يجب أن يكون مجلس الأمن على علم تام بما يجري من الأعمال لحفظ السلم والأمن الدولي بمقتضى تنظيمات أو بواسطة وكالات إقليمية أو ما يزمع إجراؤه منها.

    + +

    الفصل التاسع: التعاون الدولي الاقتصادي والاجتماعي

    + +

    المادة 55

    + +

    رغبة في تهيئة دواعي الاستقرار والرفاهية الضروريين لقيام علاقات سليمة ودية بين الأمم المتحدة مؤسسة على احترام المبدأ الذي يقضي بالتسوية في الحقوق بين الشعوب وبأن يكون لكل منها تقرير مصيرها، تعمل الأمم المتحدة على:

    + +
      +
    1. تحقيق مستوى أعلى للمعيشة وتوفير أسباب الاستخدام المتصل لكل فرد والنهوض بعوامل التطور والتقدم الاقتصادي والاجتماعي.
    2. +
    3. تيسير الحلول للمشاكل الدولية الاقتصادية والاجتماعية والصحية وما يتصل بها، وتعزيز التعاون الدولي في أمور الثقافة والتعليم.
    4. +
    5. أن يشيع في العالم احترام حقوق الإنسان والحريات الأساسية للجميع بلا تمييز بسبب الجنس أو اللغة أو الدين، ولا تفريق بين الرجال والنساء، ومراعاة تلك الحقوق والحريات فعلاً.
    6. +
    + +

    المادة 56

    + +

    يتعهد جميع الأعضاء بأن يقوموا، منفردين أو مشتركين، بما يجب عليهم من عمل بالتعاون مع الهيئة لإدراك المقاصد المنصوص عليها في المادة 55.

    + +

    المادة 57

    + +
      +
    1. الوكالات المختلفة التي تنشأ بمقتضى اتفاق بين الحكومات والتي تضطلع بمقتضى نظمها الأساسية بتبعات دولية واسعة في الاقتصاد والاجتماع والثقافة والتعليم والصحة وما يتصل بذلك من الشؤون يوصل بينها وبين "الأمم المتحدة" وفقا لأحكام المادة 63.
    2. +
    3. تسمى هذه الوكالات التي يوصل بينها وبين "الأمم المتحدة" فيما يلي من الأحكام بالوكالات المتخصصة.
    4. +
    + +

    المادة 58

    + +

    تقدم الهيئة توصيات بقصد تنسيق سياسات الوكالات المتخصصة ووجوه نشاطها.

    + +

    المادة 59

    + +

    تدعو الهيئة عند المناسبة إلى أجراء مفاوضات بين الدول ذات الشأن بقصد إنشاء أية وكالة متخصصة جديدة يتطلبها تحقيق المقاصد المبينة في المادة 55.

    + +

    المادة 60

    + +

    مقاصد الهيئة المبينة في هذا الفصل تقع مسؤولية تحقيقها على عاتق الجمعية العامة كما تقع على عاتق المجلس الاقتصادي والاجتماعي تحت إشراف الجمعية العامة، ويكون لهذا المجلس من أجل ذلك السلطات المبينة في الفصل العاشر.

    + +

    الفصل العاشر: المجلس الاقتصادي والاجتماعي

    + +

    التأليف

    + +

    المادة 61

    + +
      +
    1. يتألف المجلس الاقتصادي والاجتماعي من أربعة وخمسين عضواً من الأمم المتحدة تنتخبهم الجمعية العامة.
    2. +
    3. مع مراعاة أحكام الفقرة 3، ينتخب ثمانية عشر عضواً من أعضاء المجلس الاقتصادي والاجتماعي كل سنة لمدة ثلاث سنوات ويحوز أن يعاد انتخاب العضو الذي انتهت مدته مباشرة.
    4. +
    5. في الانتخاب الأول بعد زيادة عدد أعضاء المجلس الاقتصادي والاجتماعي من سبعة وعشرين إلى أربعة وخمسين عضواً، يختار سبعة وعشرون عضواً إضافياً علاوة على الأعضاء المنتخبين محل الأعضاء التسعة الذين تنتهي مدة عضويتهم في نهاية هذا العام. وتنتهي عضوية تسعة من هؤلاء الأعضاء السبعة والعشرين الإضافيين بعد انقضاء سنة واحدة، وتنتهي عضوية تسعة أعضاء آخرين بعد انقضاء سنتين، ويجرى ذلك وفقا للنظام الذي تضعه الجمعية العامة.
    6. +
    7. يكون لكل عضو من أعضاء المجلس الاقتصادي والاجتماعي مندوب واحد.
    8. +
    + +

    الوظائف والسلطـات

    + +

    المادة 62

    + +
      +
    1. للمجلس الاقتصادي والاجتماعي أن يقوم بدراسات ويضع تقارير عن المسائل الدولية في أمور الاقتصاد والاجتماع والثقافة والتعليم والصحة وما يتصل بها، كما أن له أن يوجه إلى مثل تلك الدراسات وإلى وضع مثل تلك التقارير. وله أن يقدم توصياته في أية مسألة من تلك المسائل إلى الجمعية العامة وإلى أعضاء "الأمم المتحدة" وإلى الوكالات المتخصصة ذات الشأن.
    2. +
    3. وله أن يقدم توصيات فيما يختص بإشاعة احترام حقوق الإنسان والحريات الأساسية ومراعاتها.
    4. +
    5. وله أن يعد مشروعات اتفاقات لتعرض على الجمعية العامة عن المسائل التي تدخل في دائرة اختصاصه.
    6. +
    7. وله أن يدعو إلى عقد مؤتمرات دولية لدراسة المسائل التي تدخل في دائرة اختصاصه وفقا للقواعد التي تضعها "الأمم المتحدة".
    8. +
    + +

    المادة 63

    + +
      +
    1. للمجلس الاقتصادي والاجتماعي أن يضع اتفاقات مع أي وكالة من الوكالات المشار إليها في المادة 57 تحدد الشروط التي على مقتضاها يوصل بينها وبين "الأمم المتحدة" وتعرض هذه الاتفاقات على الجمعية العامة للموافقة عليها.
    2. +
    3. وله أن ينسق وجوه نشاط الوكالات المتخصصة بطريق التشاور معها وتقديم توصياته إليها وإلى الجمعية العامة وأعضاء "الأمم المتحدة".
    4. +
    + +

    المادة 64

    + +
      +
    1. للمجلس الاقتصادي والاجتماعي أن يتخذ الخطوات المناسبة للحصول بانتظام على تقارير من الوكالات المتخصصة وله أن يضع مع أعضاء "الأمم المتحدة" ومع الوكالات المتخصصة ما يلزم من الترتيبات كيما تمده بتقارير عن الخطوات التي اتخذتها لتنفيذ توصياته أو لتنفيذ توصيات الجمعية العامة في شأن المسائل الداخلة في اختصاصه.
    2. +
    3. وله أن يبلغ الجمعية العامة ملاحظاته على هذه التقارير.
    4. +
    + +

    المادة 65

    + +

    للمجلس الاقتصادي والاجتماعي أن يمد مجلس الأمن بما يلزم من المعلومات وعليه أن يعاونه متى طلب إليه ذلك.

    + +

    المادة 66

    + +
      +
    1. يقوم المجلس الاقتصادي والاجتماعي في تنفيذ توصيات الجمعية العامة بالوظائف التي تدخل في اختصاصه.
    2. +
    3. وله بعد موافقة الجمعية العامة أن يقوم بالخدمات اللازمة لأعضاء "الأمم المتحدة" أو الوكالات المتخصصة متى طلب إليه ذلك.
    4. +
    5. يقوم المجلس بالوظائف الأخرى المبينة في غير هذا الموضع مع الميثاق وبالوظائف التي قد تعهد بها إليه الجمعية العامة.
    6. +
    + +

    التصويت

    + +

    المادة 67

    + +
      +
    1. يكون لكل عضو من أعضاء المجلس الاقتصادي والاجتماعي صوت واحد.
    2. +
    3. تصدر قرارات المجلس الاقتصادي والاجتماعي بأغلبية أعضائه الحاضرين المشتركين في التصويت.
    4. +
    + +

    الإجـراءات

    + +

    المادة 68

    + +

    ينشئ المجلس الاقتصادي والاجتماعي لجاناً للشؤون الاقتصادية والاجتماعية ولتعزيز حقوق الإنسان، كما ينشئ غير ذلك من اللجان التي قد يحتاج إليها لتأدية وظائفه.

    + +

    المادة 69

    + +

    يدعو المجلس الاقتصادي والاجتماعي أي عضو من "الأمم المتحدة" للاشتراك في مداولاته عند بحث أية مسألة تعني هذا العضو بوجه خاص، على ألا يكون له حق التصويت.

    + +

    المادة 70

    + +

    للمجلس الاقتصادي والاجتماعي أن يعمل على إشراك مندوبي الوكالات المتخصصة في مداولاته أو في مداولات اللجان التي ينشئها دون أن يكون لهم حق التصويت، كما أن له أن يعمل على إشراك مندوبيه في مداولات الوكالة المتخصصة.

    + +

    المادة 71

    + +

    للمجلس الاقتصادي والاجتماعي أن يجرى الترتيبات المناسبة للتشاور مع الهيئات غير الحكومية التي تعني بالمسائل الداخلة في اختصاصه. وهذه الترتيبات قد يجريها المجلس مع هيئات دولية، كما أنه قد يجريها إذا رأى ذلك ملائماً مع هيئات أهلية وبعد التشاور مع عضو "الأمم المتحدة" ذي الشأن.

    + +

    المادة 72

    + +
      +
    1. يضع المجلس الاقتصادي والاجتماعي لائحة إجراءاته ومنها طريقة اختيار رئيسه.
    2. +
    3. يجتمع المجلس الاقتصادي والاجتماعي كلما دعت الحاجة إلى ذلك وفقا للائحة التي يسنها. ويجب أن تتضمن تلك اللائحة النص على دعوته للاجتماع بناءً على طلب يقدم من أغلبية أعضائه.
    4. +
    + +

    الفصل الحادي عشر: تصريح يتعلق بالأقاليم غير المتمتعة بالحكم الذاتي

    + +

    المادة 73

    + +

    يقرر أعضاء الأمم المتحدة - الذين يضطلعون في الحال أو في المستقبل بتبعات عن إدارة أقاليم لم تنل شعوبها قسطاً كاملاً من الحكم الذاتي - المبدأ القاضي بأن مصالح أهل هذه الأقاليم لها المقام الأول، ويقبلون أمانة مقدسة في عنقهم، الالتزام بالعمل على تنمية رفاهية أهل هذه الأقاليم إلى أقصى حد مستطاع في نطاق السلم والأمن الدولي الذي رسمه هذا الميثاق. ولهذا الغرض:

    + +
      +
    1. يكفلون تقدم هذه الشعوب في شؤون السياسة والاقتصاد والاجتماع والتعليم، كما يكفلون معاملتها بإنصاف وحمايتها من ضروب الإساءة - كل ذلك مع مراعاة الاحترام الواجب لثقافة هذه الشعوب.
    2. +
    3. ينمون الحكم الذاتي، ويقدرون الأماني السياسية لهذه الشعوب قدرها، ويعاونونها على إنماء نظمها السياسية الحرة نمواً مطرداً، وفقا للظروف الخاصة لكل إقليم وشعوبه، ومراحل تقدمها المختلفة.
    4. +
    5. يوطدون السلم والأمن الدولي.
    6. +
    7. يعززون التدابير الإنسانية للرقي والتقدم، ويشجعون البحوث، ويتعاونون فيما بينهم لتحقيق المقاصد الاجتماعية والاقتصادية والعلمية المفصّلة في هذه المادة تحقيقاً عملياً، كما يتعاونون أيضاً لهذا الغرض مع الهيئات الدولية المتخصصة كلما تراءت لهم ملاءمة ذلك. ./li>
    8. +
    9. يرسلون إلى الأمين العام بانتظام يحيطونه علماً بالبيانات الإحصائية وغيرها من البيانات الفنية المتعلقة بأمور الاقتصاد والاجتماع والتعليم في الأقاليم التي يكونون مسؤولين عنها، عدا الأقاليم التي تنطبق عليها أحكام الفصلين الثاني عشر والثالث عشر من هذا الميثاق. كل ذلك مع مراعاة القيود التي قد تستدعيها الاعتبارات المتعلقة بالأمن والاعتبارات الدستورية.
    10. +
    + +

    المادة 74

    + +

    يوافق أعضاء الأمم المتحدة أيضاً على أن سياستهم إزاء الأقاليم التي ينطبق عليها هذا الفصل - كسياستهم في بلادهم نفسها - يجب أن تقوم على مبدأ حسن الجوار، وأن تراعي حق المراعاة مصالح بقية أجزاء العالم ورفاهيتها في الشؤون الاجتماعية والاقتصادية والتجارية.

    + +

    الفصل الثاني عشر: نظام الوصاية الدولي

    + +

    المادة 75

    + +

    تنشئ "الأمم المتحدة" تحت إشرافها نظاماً دولياً للوصاية، وذلك لإدارة الأقاليم التي قد تخضع لهذا النظام بمقتضى إتفاقات فردية لاحقة وللإشراف عليها، ويطلق على هذه الأقاليم فيما يلي من الأحكام اسم الأقاليم المشمولة بالوصاية.

    + +

    المادة 76

    + +

    الأهداف الأساسية لنظام الوصاية طبقاً لمقاصد "الأمم المتحدة" المبينة في المادة الأولى من هذا الميثاق هي:

    + +
      +
    1. توطيد السلم والأمن الدولي؛
    2. +
    3. لحكم الذاتي أو الاستقلال حسبما يلائم الظروف الخاصة لكل إقليم وشعوبه، ويتفق مع رغبات هذه الشعوب التي تعرب عنها بملء حريتها وطبقاً لما قد ينص عليه في شروط كل اتفاق من اتفاقات الوصاية؛
    4. +
    5. التشجيع على احترام حقوق الإنسان والحريات الأساسية للجميع بلا تمييز بسبب الجنس أو اللغة أو الدين، ولا تفريق بين الرجال والنساء، والتشجيع على إدراك ما بين شعوب العالم من تقيد بعضهم بالبعض؛
    6. +
    7. كفالة المساواة في المعاملة في الأمور الاجتماعية والاقتصادية والتجارية لجميع أعضاء "الأمم المتحدة" وأهاليها والمساواة بين هؤلاء الأهالي أيضاً فيما يتعلق بإجراء القضاء، وذلك مع عدم الإخلال بتحقيق الأغراض المتقدمة ومع مراعاة أحكام المادة 80.
    8. +
    + +

    المادة 77

    + +
      +
    1. يطبق نظام الوصاية على الأقاليم الداخلة في الفئات الآتية مما قد يوضع تحت حكمها بمقتضى اتفاقات وصاية: +
        +
      1. الأقاليم المشمولة الآن بالانتداب؛
      2. +
      3. الأقاليم التي قد تقتطع من دول الأعداء نتيجة للحرب العالمية الثانية؛
      4. +
      5. الأقاليم التي تضعها في الوصاية بمحض اختيارها دول مسؤولة عن إدارتها.
      6. +
      +
    2. +
    3. أما تعيين أي الأقاليم من الفئات سالفة الذكر يوضع تحت نظام الوصاية وطبقاً لأي شروط، فذلك من شأن ما يعقد بعد من اتفاقات
    4. +
    + +

    المادة 78

    + +

    لا يطبق نظام الوصاية على الأقاليم التي أصبحت أعضاء في هيئة "الأمم المتحدة" إذ العلاقات بين أعضاء هذه الهيئة يجب أن تقوم على احترام مبدأ المساواة في السيادة.

    + +

    المادة 79

    + +

    شروط الوصاية لكل إقليم يوضع تحت ذلك النظام، وكل تغيير أو تعديل يطرآن بعد عليها، ذلك كله يتفق عليه برضا الدول التي يعنيها هذا الأمر بالذات ومنها الدولة المنتدبة في حالة الأقاليم المشمولة بانتداب أحد أعضاء "الأمم المتحدة". وهذا مع مراعاة أحكام المادتين 83 و85 في شأن المصادقة على تلك الشروط وتعديلاتها.

    + +

    المادة 80

    + +
      +
    1. فيما عدا ما قد يتفق عليه في اتفاقات الوصاية الفردية التي تبرم وفق أحكام المواد 77 و 79 و 81 وبمقتضاها توضع الأقاليم تحت الوصاية، وإلى أن تعقد مثل هذه الاتفاقات لا يجوز تأويل نص أي حكم من أحكام هذا الفصل ولا تخريجه تأويلاً أو تخريجاً من شأنه أن يغير بطريقة ما أية حقوق لأية دول أو شعوب، أو يغير شروط الاتفاقات الدولية القائمة التي قد يكون أعضاء "الأمم المتحدة" أطرافاً فيها.
    2. +
    3. لا يجوز أن تؤول الفقرة الأولى من هذه المادة على أنها تهيئ سبباً لتأخير أو تأجيل المفاوضة في الاتفاقات التي ترمي لوضع الأقاليم المشمولة بالانتداب أو غيرها من الأقاليم في نظام الوصاية طبقا للمادة 77 أو تأخير أو تأجيل إبرام مثل تلك الاتفاقات.
    4. +
    + +

    المادة 81

    + +

    يشمل اتفاق الوصاية، في كل حالة، الشروط التي يدار بمقتضاها الإقليم المشمول بالوصاية، ويعين السلطة التي تباشر إدارة ذلك الإقليم، ويجوز أن تكون هذه السلطة التي يطلق عليها فيما يلي من الأحكام "السلطة القائمة بالإدارة" دولة أو أكثر أو هيئة "الأمم المتحدة" ذاتها.

    + +

    المادة 82

    + +

    يجوز أن يحدد في أي اتفاق من اتفاقات الوصاية موقع استراتيجي قد يشمل الإقليم الذي ينطبق عليه نظام الوصاية بعضه أو كله، وذلك دون الإخلال بأي اتفاق أو اتفاقات خاصة معقودة طبقا لنص المادة 43.

    + +

    المادة 83

    + +
      +
    1. يباشر مجلس الأمن جميع وظائف "الأمم المتحدة" المتعلقة بالمواقع الاستراتيجية، ويدخل في ذلك الموافقة على شروط اتفاقات الوصاية وتغييرها أو تعديلها.
    2. +
    3. تراعى جميع الأهداف الأساسية المبينة في المادة 76 بالنسبة لشعب كل موقع استراتيجي.
    4. +
    5. يستعين مجلس الأمن بمجلس الوصاية - مع مراعاة أحكام اتفاقيات الوصاية ودون إخلال بالاعتبارات المتصلة بالأمن - في مباشرة ما كان من وظائف "الأمم المتحدة" في نظام الوصاية خاصاً بالشؤون السياسية والاقتصادية والاجتماعية والتعليمية للمواقع الاستراتيجية.
    6. +
    + +

    المادة 84

    + +

    يكون من واجب السلطة القائمة بالإدارة أن تكفل قيام الإقليم المشمول بالوصاية بنصيبه في حفظ السلم والأمن الدولي. وتحقيقا لهذه الغاية يجوز للسلطة القائمة بالإدارة أن تستخدم قوات متطوعة وتسهيلات ومساعدات من الإقليم المشمول بالوصاية للقيام بالالتزامات التي تعهدت بها تلك السلطة لمجلس الأمن في هذا الشأن، وللقيام أيضاً بالدفاع وبإقرار حكم القانون والنظام داخل الإقليم المشمول بالوصاية.

    + +

    المادة 85

    + +
      +
    1. تباشر الجمعية العامة وظائف "الأمم المتحدة" فيما يختص باتفاقات الوصاية على كل المساحات التي لم ينص على أنها مساحات استراتيجية ويدخل في ذلك إقرار شروط اتفاقات الوصاية وتغييرها أو تعديلها.
    2. +
    3. يساعد مجلس الوصاية الجمعية العامة في القيام بهذه الوظائف عاملاً تحت إشرافها.
    4. +
    + +

    الفصل الثالث عشر: مجلس الوصاية

    + +

    التأليف

    + +

    المادة 86

    + +
      +
    1. يتألف مجلس الوصاية من أعضاء "الأمم المتحدة" الآتي بيانهم: +
        +
      1. الأعضاء الذين يتولون إدارة أقاليم مشمولة بالوصاية؛
      2. +
      3. الأعضاء المذكورون بالاسم في المادة 23 الذين لا يتولون إدارة أقاليم مشمولة بالوصاية؛
      4. +
      5. العدد الذي يلزم من الأعضاء الآخرين لكفالة أن يكون جملة أعضاء مجلس الوصاية فريقين متساويين، أحدهما الأعضاء الذين يقومون بإدارة الأقاليم المشمولة بالوصاية، والآخر الأعضاء الذين خلوا من تلك الإدارة. وتنتخب الجمعية العامة هؤلاء الأعضاء لمدة ثلاث سنوات.
      6. +
      +
    2. +
    3. يعين كل عضو من أعضاء مجلس الوصاية من يراه أهلاً بوجه خاص لتمثيله في هذا المجلس.
    4. +
    + +

    الوظائف والسلطـات

    + +

    المادة 87

    + +

    لكل من الجمعية العامة ومجلس الوصاية عاملاً تحت إشرافها، وهما يقومان بأداء وظائفهما:

    + +
      +
    1. أن ينظر في التقارير التي ترفعها السلطة القائمة بالإدارة.
    2. +
    3. أن يقبل العرائض ويفحصها بالتشاور مع السلطة القائمة بالإدارة.
    4. +
    5. أن يقبل العرائض ويفحصها بالتشاور مع السلطة القائمة بالإدارة.
    6. +
    7. أن يتخذ هذه التدابير وغيرها، وفقا للشروط المبينة في اتفاقات الوصاية.
    8. +
    + +

    المادة 88

    + +

    يضع مجلس الوصاية طائفة من الأسئلة عن تقدم سكان كل إقليم مشمول بالوصاية في الشؤون السياسية والاقتصادية والاجتماعية والتعليمية. وتقدم السلطة القائمة بالإدارة في كل إقليم مشمول بالوصاية داخل اختصاص الجمعية العامة تقريراً سنوياً للجمعية العامة موضوعاً على أساس هذه الأسئلة.

    + +

    التصويت

    + +

    المادة 89

    + +
      +
    1. يكون لك عضو في مجلس الوصاية صوت واحد.
    2. +
    3. تصدر قرارات مجلس الوصاية بأغلبية الأعضاء الحاضرين المشتركين في التصويت.
    4. +
    + +

    الإجـراءات

    + +

    المادة 90

    + +
      +
    1. يضع مجلس الوصاية لائحة إجراءاته ومنها طريقة اختيار رئيسه.
    2. +
    3. يجتمع مجلس الوصاية كلما دعت الحاجة لذلك وفقاً للائحة التي يسنها. ويجب أن تتضمن تلك اللائحة النص على دعوته للاجتماع بناءً على طلب يقدم من أغلبية أعضائه.
    4. +
    + +

    المادة 91

    + +

    يستعين مجلس الوصاية، كلما كان ذلك مناسباً، بالمجلس الاقتصادي والاجتماعي وبالوكالات المتخصصة في كل ما يختص به كل منها من الشؤون.

    + +

    الفصل الرابع عشر: محكمة العدل الدولية

    + +

    المادة 92

    + +

    محكمة العدل الدولية هي الأداة القضائية الرئيسية "للأمم المتحدة"، وتقوم بعملها وفق نظامها الأساسي الملحق بهذا الميثاق وهو مبني على النظام الأساسي للمحكمة الدائمة للعدل الدولي وجزء لا يتجزأ من الميثاق.

    + +

    المادة 93

    + +
      +
    1. يعتبر جميع أعضاء "الأمم المتحدة" بحكم عضويتهم أطرافاً في النظام الأساسي لمحكمة العدل الدولية.
    2. +
    3. يجوز لدولة ليست من "الأمم المتحدة" أن تنضم إلى النظام الأساسي لمحكمة العدل الدولية بشروط تحددها الجمعية العامة لكل حالة بناء على توصية مجلس الأمن.
    4. +
    + +

    المادة 94

    + +
      +
    1. يتعهد كل عضو من أعضاء "الأمم المتحدة" أن ينزل على حكم محكمة العدل الدولية في أية قضية يكون طرفاً فيها.
    2. +
    3. إذا امتنع أحد المتقاضين في قضية ما عن القيام بما يفرضه عليه حكم تصدره المحكمة، فللطرف الآخر أن يلجأ إلى مجلس الأمن، ولهذا المجلس، إذا رأى ضرورة لذلك أن يقدم توصياته أو يصدر قراراً بالتدابير التي يجب اتخاذها لتنفيذ هذا الحكم.
    4. +
    + +

    المادة 95

    + +

    ليس في هذا الميثاق ما يمنع أعضاء "الأمم المتحدة" من أن يعهدوا بحل ما ينشأ بينهم من خلاف إلى محاكم أخرى بمقتضى اتفاقات قائمة من قبل أو يمكن أن تعقد بينهم في المستقبل.

    + +

    المادة 96

    + +
      +
    1. لأي من الجمعية العامة أو مجلس الأمن أن يطلب إلى محكمة العدل الدولية إفتاءه في أية مسألة قانونية.
    2. +
    3. ولسائر فروع الهيئة والوكالات المتخصصة المرتبطة بها، ممن يجوز أن تأذن لها الجمعية العامة بذلك في أي وقت، أن تطلب أيضاً من المحكمة إفتاءها فيما يعرض لها من المسائل القانونية الداخلة في نطاق أعمالها.
    4. +
    + +

    الفصل الخامس عشر: الأمـانة العامة

    + +

    المادة 97

    + +

    يكون للهيئة أمانة تشمل أميناً عاماً ومن تحتاجهم الهيئة من الموظفين. وتعين الجمعية العامة الأمين العام بناءً على توصية مجلس الأمن. والأمين العام هو الموظف الإداري الأكبر في الهيئة.

    + +

    المادة 98

    + +

    يتولى الأمين العام أعماله بصفته هذه في كل اجتماعات الجمعية العامة ومجلس الأمن والمجلس الاقتصادي والاجتماعي، ومجلس الوصاية، ويقوم بالوظائف الأخرى التي تكلها إليه هذه الفروع. ويعد الأمين العام تقريراً سنوياً للجمعية العامة بأعمال الهيئة.

    + +

    المادة 99

    + +

    للأمين العام أن ينبه مجلس الأمن إلى أية مسألة يرى أنها قد تهدد حفظ السلم والآمن الدولي.

    + +

    المادة 100

    + +
      +
    1. ليس للأمين العام ولا للموظفين أن يطلبوا أو أن يتلقوا في تأدية واجبهم تعليمات من أية حكومة أو من أية سلطة خارجة عن الهيئة. وعليهم أن يمتنعوا عن القيام بأي عمل قد يسئ إلى مراكزهم بوصفهم موظفين دوليين مسؤولين أمام الهيئة وحدها.
    2. +
    3. يتعهد كل عضو في "الأمم المتحدة" باحترام الصفة الدولية البحتة لمسؤوليات الأمين العام والموظفين وبألا يسعى إلى التأثير فيهم عند اضطلاعهم بمسؤولياتهم.
    4. +
    + +

    المادة 101

    + +
      +
    1. يعين الأمين العام موظفي الأمانة طبقا للوائح التي تضعها الجمعية العامة.
    2. +
    3. يعين للمجلس الاقتصادي والاجتماعي ولمجلس الوصاية ما يكفيهما من الموظفين على وجه دائم ويعين لغيرهما من فروع "الأمم المتحدة" الأخرى ما هي بحاجة إليه منهم. وتعتبر جملة هؤلاء الموظفين جزءاً من الأمانة.
    4. +
    5. ينبغي في استخدام الموظفين وفي تحديد شروط خدمتهم أن يراعى في المكان الأول ضرورة الحصول على أعلى مستوى من المقدرة والكفاية والنزاهة. كما أن من المهم أن يراعى في اختيارهم أكبر ما يستطاع من معاني التوزيع الجغرافي.
    6. +
    + +

    Chapter XVI: Miscellaneous Provisions

    + +

    المادة 102

    + +
      +
    1. كل معاهدة وكل اتفاق دولي يعقده أي عضو من أعضاء "الأمم المتحدة" بعد العمل بهذا الاتفاق يجب أن يسجل في أمانة الهيئة وأن تقوم بنشره بأسرع ما يمكن.
    2. +
    3. ليس لأي طرف في معاهدة أو اتفاق دولي لم يسجل وفقاً للفقرة الأولى من هذه المادة أن يتمسك بتلك المعاهدة أو ذلك الاتفاق أمام أي فرع من فروع "الأمم المتحدة".
    4. +
    + +

    المادة 103

    + +

    إذا تعارضت الالتزامات التي يرتبط بها أعضاء "الأمم المتحدة" وفقاً لأحكام هذا الميثاق مع أي التزام دولي آخر يرتبطون به فالعبرة بالتزاماتهم المترتبة على هذا الميثاق.

    + +

    المادة 104

    + +

    تتمتع الهيئة في بلاد كل عضو من أعضائها بالأهلية القانونية التي يتطلبها قيامها بأعباء وظائفها وتحقيق مقاصدها.

    + +

    المادة 105

    + +
      +
    1. تتمتع الهيئة في أرض كل عضو من أعضائها بالمزايا والإعفاءات التي يتطلبها تحقيق مقاصدها.
    2. +
    3. وكذلك يتمتع المندوبون عن أعضاء "الأمم المتحدة" وموظفو هذه الهيئة بالمزايا والإعفاءات التي يتطلبها استقلالهم في القيام بمهام وظائفهم المتصلة بالهيئة.
    4. +
    5. للجمعية العامة أن تقدم التوصيات بقصد تحديد التفاصيل الخاصة بتطبيق الفقرتين 1 و 2 من هذه المادة، ولها أن تقترح على أعضاء الهيئة عقد اتفاقات لهذا الغرض.
    6. +
    + +

    الفصل السابع عشر: تدابير حفظ الأمن في فترة الانتقال

    + +

    المادة 106

    + +

    إلى أن تصير الاتفاقات الخاصة المشار إليها في المادة الثالثة والأربعين معمولاً بها على الوجه الذي يرى معه مجلس الأمن أنه أصبح يستطيع البدء في احتمال مسؤولياته وفقاً للمادة 42، تتشاور الدول التي اشتركت في تصريح الدول الأربع الموقع في موسكو في 30 تشرين الأول/أكتوبر سنة 1943 هي وفرنسا وفقاً لأحكام الفقرة 5 من ذلك التصريح، كما تتشاور الدول الخمس مع أعضاء "الأمم المتحدة" الآخرين، كلما اقتضت الحال، للقيام نيابة عن الهيئة بالأعمال المشتركة التي قد تلزم لحفظ السلم والأمن الدولي.

    + +

    المادة 107

    + +

    ليس في هذا الميثاق ما يبطل أو يمنع أي عمل إزاء دولة كانت في أثناء الحرب العالمية الثانية معادية لإحدى الدول الموقعة على هذا الميثاق إذا كان هذا العمل قد اتخذ أو رخص به نتيجة لتلك الحرب من قبل الحكومات المسؤولة عن القيام بهذا العمل.

    + +

    الفصل الثامن عشر: تعديل الميثاق

    + +

    المادة 108

    + +

    التعديلات التي تدخل على هذا الميثاق تسري على جميع أعضاء "الأمم المتحدة" إذا صدرت بموافقة ثلثي أعضاء الجمعية العامة وصدق عليها ثلثا أعضاء "الأمم المتحدة" ومن بينهم جميع أعضاء مجلس الأمن الدائمين، وفقا للأوضاع الدستورية في كل دولة.

    + +

    المادة 109

    + +
      +
    1. يجوز عقد مؤتمر عام من أعضاء "الأمم المتحدة" لإعادة النظر في هذا الميثاق في الزمان والمكان اللذين تحددهما الجمعية العامة بأغلبية ثلثي أعضائها وبموافقة تسعة ما من أعضاء مجلس الأمن, ويكون لكل عضو في "الأمم المتحدة" صوت واحد في المؤتمر.
    2. +
    3. كل تغيير في هذا الميثاق أوصى به المؤتمر بأغلبية ثلثي أعضائه يسري إذا صدق عليه ثلثا أعضاء "الأمم المتحدة" ومن بينهم الأعضاء الدائمون في مجلس الأمن وفقا لأوضاعهم الدستورية.
    4. +
    5. إذا لم يعقد هذا المؤتمر قبل الدورة العادية العاشرة للجمعية العامة، بعد العمل بهذا الميثاق، وجب أن يدرج بجدول أعمال ذلك الدور العاشر اقتراح بالدعوة إلى عقده، وهذا المؤتمر يعقد إذا قررت ذلك أغلبية أعضاء الجمعية العامة وسبعة ما من أعضاء مجلس الأمن.
    6. +
    + +

    الفصل التاسع عشر: التصديق والتوقيع

    + +

    المادة 110

    + +
      +
    1. تصدق على هذا الميثاق الدول الموقعة عليه كل منها حسب أوضاعه الدستورية.
    2. +
    3. تودع التصديقات لدى حكومة الولايات المتحدة الأمريكية التي تخطر الدول الموقعة عليه بكل إيداع يحصل، كما تخطر الأمين العام لهيئة "الأمم المتحدة" بعد تعيينه.
    4. +
    5. يصبح هذا الميثاق معمولاً به متى أودعت تصديقاتها جمهورية الصين وفرنسا واتحاد الجمهوريات الاشتراكية السوفياتية والمملكة المتحدة لبريطانيا العظمى وأيرلندا الشمالية والولايات المتحدة الأمريكية وأغلبية الدول الأخرى الموقعة عليه وتعد حكومة الولايات المتحدة الأمريكية بروتوكولاً خاصاً بالتصديقات المودعة وتبلغ صوراً منه لكل الدول الموقعة على الميثاق.
    6. +
    7. الدول الموقعة على هذا الميثاق التي تصدق عليه بعد العمل به، تعتبر من الأعضاء الأصليين في "الأمم المتحدة" من تاريخ إيداعها لتصديقاتها.
    8. +
    + +

    المادة 111

    + +

    وضع هذا الميثاق بلغات خمس هي الصينية والفرنسية والروسية والإنجليزية والأسبانية، وهي لغاته الرسمية على وجه السواء . ويظل الميثاق مودعاًُ في محفوظات حكومة الولايات المتحدة الأمريكية، وتبلغ هذه الحكومة حكومات الدول الأخرى الموقعة عليه صوراً معتمدة منه.

    + +

    ومصادقا لما تقدم وقع مندوبو حكومات "الأمم المتحدة" على هذا الميثاق. صدر بمدينة سان فرانسيسكو في اليوم السادس والعشرين من شهر حزيران/يونيه 1945.

    +
    +
    +
    +
    + + +
    +
    + + + +
    + +
    +
    + +
    +
    +
    + + + +
    + + + + + + + + + + + +
    + + + + + + + + + + \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_AR.txt b/stdlib/benchmarks/collections/data/UN_charter_AR.txt new file mode 100644 index 0000000000..0d65f19bea --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_AR.txt @@ -0,0 +1,465 @@ +ميثاق الأمم المتحدة (النص الكامل) +الديباجة +نحن شعوب الأمم المتحدة وقد آلينا على أنفسنا +أن ننقذ الأجيال المقبلة من ويلات الحرب التي في خلال جيل واحد جلبت على الإنسانية مرتين أحزاناً يعجز عنها الوصف، + +وأن نؤكد من جديد إيماننا بالحقوق الأساسية للإنسان وبكرامة الفرد وقدره وبما للرجال والنساء والأمم كبيرها وصغيرها من حقوق متساوية، + +وأن نبيّن الأحوال التي يمكن في ظلها تحقيق العدالة واحترام الالتزامات الناشئة عن المعاهدات وغيرها من مصادر القانون الدولي، + +وأن ندفع بالرقي الاجتماعي قدماً، وأن نرفع مستوى الحياة في جو من الحرية أفسح. + +وفي سبيل هذه الغايات اعتزمنا +أن نأخذ أنفسنا بالتسامح، وأن نعيش معاً في سلام وحسن جوار ، + +وأن نضم قوانا كي نحتفظ بالسلم والأمن الدولي، + +وأن نكفل بقبولنا مبادئ معيّنة ورسم الخطط اللازمة لها ألاّ تستخدم القوة المسلحة في غير المصلحة المشتركة ، + +وأن نستخدم الأداة الدولية في ترقية الشؤون الاقتصادية والاجتماعية للشعوب جميعها، + +قد قرّرنا أن نوحّد جهودنا لتحقيق هذه الأغراض +ولهذا فإن حكوماتنا المختلفة على يد مندوبيها المجتمعين في مدينة سان فرانسيسكو الذين قدّموا وثائق التفويض المستوفية للشرائط، قد ارتضت ميثاق الأمم المتحدة هذا، وأنشأت بمقتضاه هيئة دولية تُسمّى "الأمم المتحدة". + +الفصل الأول: مقاصد الهيئة ومبادئها +المادة 1 +مقاصـد الأمـم المتحدة هي: + +حفظ السلم والأمن الدولي، وتحقيقاً لهذه الغاية تتخذ الهيئة التدابير المشتركة الفعّالة لمنع الأسباب التي تهدد السلم ولإزالتها، وتقمع أعمال العدوان وغيرها من وجوه الإخلال بالسلم، وتتذرّع بالوسائل السلمية، وفقاً لمبادئ العدل والقانون الدولي، لحل المنازعات الدولية التي قد تؤدي إلى الإخلال بالسلم أو لتسويتها. + +إنماء العلاقات الودية بين الأمم على أساس احترام المبدأ الذي يقضي بالتسوية في الحقوق بين الشعوب وبأن يكون لكل منها تقرير مصيرها، وكذلك اتخاذ التدابير الأخرى الملائمة لتعزيز السلم العام. + +تحقيق التعاون الدولي على حل المسائل الدولية ذات الصبغة الاقتصادية والاجتماعية والثقافية والإنسانية وعلى تعزيز احترام حقوق الإنسان والحريات الأساسية للناس جميعاً والتشجيع على ذلك إطلاقاً بلا تمييز بسبب الجنس أو اللغة أو الدين ولا تفريق بين الرجال والنساء. + +جعل هذه الهيئة مرجعاً لتنسيق أعمال الأمم وتوجيهها نحو إدراك هذه الغايات المشتركة. + +المادة 2 +تعمل الهيئة وأعضاؤها في سعيها وراء المقاصد المذكورة في المادة الأولى وفقاً ‏‏ للمبادئ الآتية: + +تقوم الهيئة على مبدأ المساواة في السيادة بين جميع أعضائها.‏ + +لكي يكفل أعضاء الهيئة لأنفسهم جميعاً الحقوق والمزايا المترتبة على صفة العضوية يقومون في حسن نية ‏بالالتزامات التي أخذوها على أنفسهم بهذا الميثاق. + +يفض جميع أعضاء الهيئة منازعاتهم الدولية بالوسائل السلمية على وجه لا يجعل السلم والأمن والعدل الدولي عرضة للخطر. + +يمتنع أعضاء الهيئة جميعاً في علاقاتهم الدولية عن التهديد باستعمال القوة أو استخدامها ضد سلامة الأراضي أو الاستقلال السياسي لأية دولة أو على أي وجه آخر لا يتفق ومقاصد "الأمم المتحدة".. + +يقدّم جميع الأعضاء كل ما في وسعهم من عون إلى "الأمم المتحدة" في أي عمل تتخذه وفق هذا الميثاق، كما يمتنعون عن مساعدة أية دولة تتخذ الأمم المتحدة إزاءها عملاً من أعمال المنع أو القمع. + +تعمل الهيئة على أن تسير الدول غير الأعضاء فيها على هذه المبادئ بقدر ما تقتضيه ضرورة حفظ السلم ‏والأمن الدولي.‏ + +ليس في هذا الميثاق ما يسوغ ”للأمم المتحدة“ أن تتدخل في الشؤون التي تكون من صميم السلطان الداخلي ‏لدولة ما، وليس فيه ما يقتضي الأعضاء أن يعرضوا مثل هذه المسائل لأن تحل بحكم هذا الميثاق، على أن ‏هذا المبدأ لا يخلّ بتطبيق تدابير القمع الواردة في الفصل السابع.‏ + +الفصل الثاني : العضوية +المادة 3 +الأعضاء الأصليون للأمم المتحدة هـم الدول التي اشتركت في مؤتمر الأمم المتحدة لوضع نظام الهيئة الدولية المنعقد في سان فرانسيسكو، والتي توقّع هذا الميثاق وتصدّق عليه طبقاً للمادة 110، وكذلك الدول التي وقّعت من قبل تصريح الأمم المتحدة الصادر في أول كانون الثاني/يناير سنة 1942، وتوقّع هذا الميثاق وتصدّق عليه. + +المادة 4 +‏العضوية في "الأمم المتحدة" مباحة لجميع الدول الأخرى المُحبة للسلام، والتي تأخذ نفسها بالالتزامات التي يتضمنها هذا الميثاق، والتي ترى الهيئة أنها قادرة على تنفيذ هذه الالتزامات وراغبة فيه . +قبول أية دولة من هذه الدول في عضوية "الأمم المتحدة" يتم بقرار من الجمعية العامة بناءً على توصية مجلس الأمن . +المادة 5 +يجوز للجمعية العامة أن توقف أي عضو اتخذ مجلس الأمن قِبَله عملاً من أعمال المنع أو القمع، عن مباشرة حقوق العضوية ومزاياها، ويكون ذلك بناءً على توصية ‏مجلس الأمن، ولمجلس الأمن أن يرد لهذا العضو مباشرة تلك الحقوق والمزايا. + +المادة 6 +إذا أمعن عضو من أعضاء "الأمم المتحدة" في انتهاك مبادئ الميثاق جاز للجمعية العامة أن تفصله من الهيئة بناءً على توصية مجلس الأمن. + +الفصل الثالث : فروع الهيئـة +المادة 7 +تنشأ الهيئات الآتية فروعاً رئيسية للأمم المتحدة: الجمعيـة العـامة، ومجلـس الأمـن، والمجلـس الاقتصـادي والاجتمـاعي، و مجلـس وصـاية، و محكمـة عـدل دوليـة و أمـانة +‏يجوز أن ينشأ وفقاً لأحكام هذا الميثاق ما يرى ضرورة إنشائه من فروع ثانوية أخرى . + +المادة 8 +لا تفرض "الأمم المتحدة" قيوداً تحدّ بها جواز اختيار الرجال والنساء للاشتراك بأية صفة وعلى وجه المساواة في فروعها الرئيسية والثانوية. + +الفصل الرابع: الجمعيـة العـامة +تأليفهـا +المادة 9 +تتألف الجمعية العامة من جميع أعضاء "الأمم المتحدة". +لا يجوز أن يكون للعضو الواحد أكثر من خمسة مندوبين في الجمعية العامة. +وظائف الجمعية وسلطاتها +المادة 10 +للجمعية العامة أن تناقش أية مسألة أو أمر يدخل في نطاق هذا الميثاق أو يتصل بسلطات فرع من الفروع المنصوص عليها فيه أو وظائفه. كما أن لها في ما عدا ما نصّ عليه في المادة 12 أن توصي أعضاء الهيئة أو مجلس الأمن أو كليهما بما تراه في تلك المسائل والأمور. + +المادة 11 +للجمعية العامة أن تنظر في المبادئ العامة للتعاون في حفظ السلم والأمن الدولي ويدخل في ذلك المبادئ المتعلقة بنزع السلاح وتنظيم التسليح، كما أن لها أن تقدّم توصياتها بصدد هذه المبادئ إلى الأعضاء أو إلى مجلس الأمن أو إلى كليهما. +للجمعية العامة أن تناقش أية مسألة يكون لها صلة بحفظ السلم والأمن الدولي يرفعها إليها أي عضو من أعضاء الأمم المتحدة ومجلس الأمن أو دولة ليست من أعضائها وفقاً لأحكام الفقرة الثانية من المادة 35، ولها - فيما عدا ما تنصّ عليه المادة الثانية عشرة - أن تقدّم توصياتها بصدد هذه المسائل للدولة أو الدول صاحبة الشأن أو لمجلس الأمن أو لكليهما معاً. وكل مسألة مما تقدّم ذكره يكون من الضروري فيها القيام بعمل ما، ينبغي أن تحيلها الجمعية العامة على مجلس الأمن قبل بحثها أو بعده. +للجمعية العامة أن تسترعي نظر مجلس الأمن إلى الأحوال التي يحتمل أن تعرّض السلم والأمن الدولي للخطر. +لا تحدّ سلطات الجمعية العامة المبيّنة في هذه المادة من عموم مدى المادة العاشرة. +المادة 12 +عندما يباشر مجلس الأمن، بصدد نزاع أو موقف ما، الوظائف التي رسمت في الميثاق، فليس للجمعية العامة أن تقدّم أية توصية في شأن هذا النزاع أو الموقف إلاّ إذا طلب ذلك منها مجلس الأمن. +يخطر الأمين العام - بموافقة مجلس الأمن - الجمعية العامة في كل دور من أدوار انعقادها بكل المسائل المتصلة بحفظ السلم والأمن الدولي التي تكون محل نظر مجلس الأمن، كذلك يخطرها أو يخطر أعضاء "الأمم المتحدة" إذا لم تكن الجمعية العامة في دور انعقادها، بفراغ مجلس الأمن من نظر تلك المسائل وذلك بمجرد انتهائه منها. +المادة 13 +تنشئ الجمعية العامة دراسات وتشير بتوصيات بقصد: +إنماء التعاون الدولي في الميدان السياسي وتشجيع التقدّم المطرد للقانون الدولي وتدوينه +ب - إنماء التعاون الدولي في الميادين الاقتصادية والاجتماعية والثقافية والتعليمية والصحية، والإعانة على تحقيق حقوق الإنسان والحريات الأساسية للناس كافة بلا تمييز بينهم في الجنس أو اللغة أو الدين ولا تفريق بين الرجال والنساء. +تبعات الجمعية العامة ووظائفها وسلطاتها الأخرى في ما يختص بالمسائل الواردة في الفقرة السابقة (ب) بيّنة في الفصلين التاسع والعاشر من هذا الميثاق. +المادة 14 +مع مراعاة أحكام المادة الثانية عشرة، للجمعية العامة أن توصي باتخاذ التدابير لتسوية أي موقف، مهما يكن منشؤه، تسوية سلمية متى رأت أن هذا الموقف قد يضر بالرفاهية العامة أو يعكّر صفو العلاقات الودية بين الأمم، ويدخل في ذلك المواقف الناشئة عن انتهاك أحكام هذا الميثاق الموضحة لمقاصد الأمم المتحدة ومبادئها. + +المادة 15 +تتلقى الجمعية العامة تقارير سنوية وأخرى خاصة من مجلس الأمن وتنظر فيها، وتتضمن هذه التقارير بياناً عن التدابير التي يكون مجلس الأمن قد قرّرها أو اتخذها لحفظ السلم والأمن الدولي. +تتلقى الجمعية العامة تقارير من الفروع الأخرى للأمم المتحدة وتنظر فيها. +المادة 16 +تباشر الجمعية العامة الوظائف الرسمية التي رسمت لها بمقتضى الفصلين الثاني عشر والثالث عشر في ما يتعلق بنظام الوصاية الدولية، ويدخل في ذلك المصادقة على اتفاقات الوصاية بشأن المواقع التي تعتبر أنها مواقع استراتيجية. + +المادة 17 +تنظر الجمعية العامة في ميزانية الهيئة وتصدّق عليها. +يتحمّل الأعضاء نفقات الهيئة حسب الأنصبة التي تقرّرها الجمعية العامة. +تنظر الجمعية العامة في أية ترتيبات مالية أو متعلقة بالميزانية مع الوكالات المتخصصة المشار إليها في المادة 57. وتصدّق عليها وتدرس الميزانيات الإدارية لتلك الوكالات لكي تقدّم لها توصياتها. +التصـويت +المادة 18 +يكون لكل عضو في "الأمم المتحدة" صوت واحد في الجمعية العامة. +تصدر الجمعية العامة قراراتها في المسائل العامة بأغلبية ثلثي الأعضاء الحاضرين المشتركين في التصويت. وتشمل هذه المسائل: التوصيات الخاصة بحفظ السلم والأمن الدولي، وانتخاب أعضاء مجلس الأمن غير الدائمين، وانتخاب أعضاء المجلس الاقتصادي والاجتماعي، وانتخاب أعضاء مجلس الوصاية وفقاً لحكم الفقرة الأولى (ج) من المادة 86، وقبول أعضاء جدد في "الأمم المتحدة" ووقف الأعضاء عن مباشرة حقوق العضوية والتمتع بمزاياها، وفصل الأعضاء، والمسائل المتعلقة بسير نظام الوصاية، والمسائل الخاصة بالميزانية. +القرارات في المسائل الأخرى - ويدخل في ذلك تحديد طوائف المسائل الإضافية التي تتطلب في إقرارها أغلبية الثلثين - تصدر بأغلبية الأعضاء الحاضرين المشتركين في التصويت. +المادة 19 +لا يكون لعضو الأمم المتحدة الذي يتأخر عن تسديد اشتراكاته المالية في الهيئة حق التصويت في الجمعية العامة إذا كان المتأخر عليه مساوياً لقيمة الاشتراكات المستحقة عليه في السنتين الكاملتين السابقتين أو زائداً عنها، وللجمعية العامة مع ذلك أن تسمح لهذا العضو بالتصويت إذا اقتنعت بأن عدم الدفع ناشئ عن أسباب لا قبل للعضو بها. + +الإجـراءات +المادة 20 +تجتمع الجمعية العامة في أدوار انعقاد عادية وفي أدوار انعقاد سنوية خاصة بحسب ما تدعو إليه الحاجة. ويقوم بالدعوة إلى أدوار الانعقاد الخاصة الأمين العام بناءً على طلب مجلس الأمن أو أغلبية أعضاء "الأمم المتحدة". + +المادة 21 +تضع الجمعية العامة لائحة إجراءاتها، وتنتخب رئيسها لكل دور انعقاد. + +المادة 22 +للجمعية العامة أن تنشئ من الفروع الثانوية ما تراه ضروريا للقيام بوظائفها. + +الفصل الخامس: مجلـس الأمـن +تأليفـه +المادة 23 +يتألف مجلس الأمن من خمسة عشر عضواً من الأمم المتحدة، وتكون جمهورية الصين، وفرنسا، واتحاد الجمهوريات الاشتراكية السوفياتية، والمملكة المتحدة لبريطانيا العظمى وأيرلندا الشمالية، والولايات المتحدة الأمريكية أعضاء دائمين فيه. وتنتخب الجمعية العامة عشرة أعضاء آخرين من الأمم المتحدة ليكونوا أعضاء غير دائمين في المجلس. ويراعى في ذلك بوجه خاص وقبل كل شيء مساهمة أعضاء الأمم المتحدة في حفظ السلم والأمن الدولي وفي مقاصد الهيئة الأخرى، كما يراعى أيضاً التوزيع الجغرافي العادل. +ينتخب أعضاء مجلس الأمن غير الدائمين لمدة سنتين، على أنه في أول انتخاب للأعضاء غير الدائمين بعد زيادة عدد أعضاء مجلس الأمن من أحد عشر عضواً إلى خمسة عشر عضواً، يختار اثنان من الأعضاء الأربعة الإضافيين لمدة سنة واحدة والعضو الذي انتهت مدته لا يجوز إعادة انتخابه على الفور. +يكون لكل عضو في مجلس الأمن مندوب واحد. +الوظائف والسلطـات +المادة 24 +رغبة في أن يكون العمل الذي تقوم به "الأمم المتحدة" سريعاً فعالاً، يعهد أعضاء تلك الهيئة إلى مجلس الأمن بالتبعات الرئيسية في أمر حفظ السلم والأمن الدولي ويوافقون على أن هذا المجلس يعمل نائباً عنهم في قيامه بواجباته التي تفرضها عليه هذه التبعات. +يعمل مجلس الأمن، في أداء هذه الواجبات وفقاً لمقاصد "الأمم المتحدة" ومبادئها والسلطات الخاصة المخوّلة لمجلس الأمن لتمكينه من القيام بهذه الواجبات مبينة في الفصول السادس والسابع والثامن والثاني عشر +يرفع مجلس الأمن تقارير سنوية، وأخرى خاصة، إذا اقتضت الحال إلى الجمعية عامة لتنظر فيها. +المادة 25 +يتعهد أعضاء "الأمم المتحدة" بقبول قرارات مجلس الأمن وتنفيذها وفق هذا الميثاق. + +المادة 26 +رغبة في إقامة السلم والأمن الدولي وتوطيدهما بأقل تحويل لموارد العالم الإنسانية والاقتصادية إلى ناحية التسليح، يكون مجلس الأمن مسؤولاً بمساعدة لجنة أركان الحرب المشار إليها في المادة 47 عن وضع خطط تعرض على أعضاء "الأمم المتحدة" لوضع منهاج لتنظيم التسليح. + +التصويت +المادة 27 +يكون لكل عضو من أعضاء مجلس الأمن صوت واحد. +تصدر قرارات مجلس الأمن في المسائل الإجرائية بموافقة تسعة من أعضائه. +تصدر قرارات مجلس الأمن في المسائل الأخرى كافة بموافقة أصوات تسعة من أعضائه يكون من بينها أصوات الأعضاء الدائمين متفقة، بشرط أنه في القرارات المتخذة تطبيقاً لأحكام الفصل السادس والفقرة 3 من المادة 52 يمتنع من كان طرفاً في النزاع عن التصويت. +الإجـراءات +المادة 28 +ينظم مجلس الأمن على وجه يستطيع معه العمل باستمرار، ولهذا الغرض يمثل كل عضو من أعضائه تمثيلاً دائماً في مقر الهيئة. +يعقد مجلس الأمن اجتماعات دورية يمثل فيها كل عضو من أعضائه - إذا شاء ذلك - بأحد رجال حكومته أو بمندوب آخر يسميه لهذا الغرض خاصة. +لمجلس الأمن أن يعقد اجتماعات في غير مقر الهيئة إذا رأى أن ذلك أدنى إلى تسهيل أعماله. +المادة 29 +لمجلس الأمن أن ينشئ من الفروع الثانوية ما يرى له ضرورة لأداء وظائفه. + +المادة 30 +يضع مجلس الأمن لائحة إجراءاته ويدخل فيها طريقة اختيار رئيسه. + +المادة 31 +لكل عضو من أعضاء "الأمم المتحدة" من غير أعضاء مجلس الأمن أن يشترك بدون تصويت في مناقشة أية مسألة تعرض على مجلس الأمن إذا رأى المجلس أن مصالح هذا العضو تتأثر بها بوجه خاص. + +المادة 32 +كل عضو من أعضاء "الأمم المتحدة" ليس بعضو في مجلس الأمن، وأية دولة ليست عضواً في "الأمم المتحدة" إذا كان أيهما طرفاً في نزاع معروض على مجلس الأمن لبحثه يدعى إلى الاشتراك في المناقشات المتعلقة بهذا النزاع دون أن يكون له حق في التصويت، ويضع مجلس الأمن الشروط التي يراها عادلة لاشتراك الدولة التي ليست من أعضاء "الأمم المتحدة". + +الفصل السادس: حل المنازعات حلاً سلمياً +المادة 33 +يجب على أطراف أي نزاع من شأن استمراره أن يعرض حفظ السلم والأمن الدولي للخطر أن يلتمسوا حله بادئ ذي بدء بطريق المفاوضة والتحقيق والوساطة والتوفيق والتحكيم والتسوية القضائية، أو أن يلجأوا إلى الوكالات والتنظيمات الإقليمية أو غيرها من الوسائل السلمية التي يقع عليها اختيارها. +ويدعو مجلس الأمن أطراف النزاع إلى أن يسووا ما بينهم من النزاع بتلك الطرق إذا رأى ضرورة ذلك. +المادة 34 +لمجلس الأمن أن يفحص أي نزاع أو أي موقف قد يؤدي إلى احتكاك دولي أو قد يثير نزاعا لكي يقرر ما إذا كان استمرار هذا النزاع أو الموقف من شأنه أن يعرض للخطر حفظ السلم والأمن الدولي. + +المادة 35 +لكل عضو من "الأمم المتحدة" أن ينبه مجلس الأمن أو الجمعية العامة إلى أي نزاع أو موقف من النوع المشار إليه في المادة الرابعة والثلاثين. +لكل دولة ليست عضواً في "الأمم المتحدة" أن تنبه مجلس الأمن أو الجمعية العامة إلى أي نزاع تكون طرفا فيه إذا كانت تقبل مقدماً في خصوص هذا النزاع التزامات الحل السلمي المنصوص عليها في هذا الميثاق. +تجرى أحكام المادتين 11 و12 على الطريقة التي تعالج بها الجمعية العامة المسائل التي تنبه إليها وفقا لهذه المادة. +المادة 36 +لمجلس الأمن في أية مرحلة من مراحل نزاع من النوع المشار إليه في المادة 33 أو موقف شبيه به أن يوصي بما يراه ملائماً من الإجراءات وطرق التسوية. +على مجلس الأمن أن يراعي ما اتخذه المتنازعون من إجراءات سابقة لحل النزاع القائم بينهم. +على مجلس الأمن وهو يقدم توصياته وفقا لهذه المادة أن يراعي أيضاً أن المنازعات القانونية يجب على أطراف النزاع - بصفة عامة - أن يعرضوها على محكمة العدل الدولية وفقاً لأحكام النظام الأساسي لهذه المحكمة. +المادة 37 +إذا أخفقت الدول التي يقوم بينها نزاع من النوع المشار إليه في المادة 33 في حله بالوسائل المبينة في تلك المادة وجب عليها أن تعرضه على مجلس الأمن. +إذا رأى مجلس الأمن أن استمرار هذا النزاع من شأنه في الواقع، أن يعرض للخطر حفظ السلم والأمن الدولي قرر ما إذا كان يقوم بعمل وفقاً للمادة 36 أو يوصي بما يراه ملائماً من شروط حل النزاع. +المادة 38 +لمجلس الأمن - إذا طلب إليه جميع المتنازعين ذلك - أن يقدم إليهم توصياته بقصد حل النزاع حلاً سلمياً، وذلك بدون إخلال بأحكام المواد من 33 إلى 37. + +الفصل السابع: فيما يتخذ من الأعمال في حالات تهديد السلم والإخلال به ووقوع العدوان +المادة 39 +يقرر مجلس الأمن ما إذا كان قد وقع تهديد للسلم أو إخلال به أو كان ما وقع عملاًً من أعمال العدوان، ويقدم في ذلك توصياته أو يقرر ما يجب اتخاذه من التدابير طبقاً لأحكام المادتين 41 و42 لحفظ السلم والأمن الدولي أو إعادته إلى نصابه. + +المادة 40 +منعاً لتفاقم الموقف، لمجلس الأمن، قبل أن يقوم توصياته أو يتخذ التدابير المنصوص عليها في المادة 39، أن يدعو المتنازعين للأخذ بما يراه ضرورياً أو مستحسناً من تدابير مؤقتة، ولا تخل هذه التدابير المؤقتة بحقوق المتنازعين ومطالبهم أو بمركزهم، وعلى مجلس الأمن أن يحسب لعدم أخذ المتنازعين بهذه التدابير المؤقتة حسابه. + +المادة 41 +لمجلس الأمن أن يقرر ما يجب اتخاذه من التدابير التي لا تتطلب استخدام القوات المسلحة لتنفيذ قراراته، وله أن يطلب إلى أعضاء "الأمم المتحدة" تطبيق هذه التدابير، ويجوز أن يكون من بينها وقف الصلات الاقتصادية والمواصلات الحديدية والبحرية والجوية والبريدية والبرقية واللاسلكية وغيرها من وسائل المواصلات وقفا جزئياً أو كليا وقطع العلاقات الدبلوماسية. + +المادة 42 +إذا رأى مجلس الأمن أن التدابير المنصوص عليها في المادة 41 لا تفي بالغرض أو ثبت أنها لم تف به، جاز له أن يتخذ بطريق القوات الجوية والبحرية والبرية من الأعمال ما يلزم لحفظ السلم والأمن الدولي أو لإعادته إلى نصابه. ويجوز أن تتناول هذه الأعمال المظاهرات والحصر والعمليات الأخرى بطريق القوات الجوية أو البحرية أو البرية التابعة لأعضاء "الأمم المتحدة". + +المادة 43 +يتعهد جميع أعضاء "الأمم المتحدة" في سبيل المساهمة في حفظ السلم والأمن الدولي، أن يضعوا تحت تصرف مجلس الأمن بناء على طلبه وطبقاً لاتفاق أو اتفاقات خاصة ما يلزم من القوات المسلحة والمساعدات والتسهيلات الضرورية لحفظ السلم والأمن الدولي ومن ذلك حق المرور. +يجب أن يحدد ذلك الاتفاق أو تلك الاتفاقات عدد هذه القوات وأنواعها ومدى استعدادها وأماكنها عموماً ونوع التسهيلات والمساعدات التي تقدم. +تجرى المفاوضة في الاتفاق أو الاتفاقات المذكورة بأسرع ما يمكن بناءً على طلب مجلس الأمن، وتبرم بين مجلس الأمن وبين أعضاء "الأمم المتحدة" أو بينه وبين مجموعات من أعضاء "الأمم المتحدة"، وتصدق عليها الدول الموقعة وفق مقتضيات أوضاعها الدستورية. +المادة 44 +إذا قرر مجلس الأمن استخدام القوة، فإنه قبل أن يطلب من عضو غير ممثل فيه تقديم القوات المسلحة وفاءً بالالتزامات المنصوص عليها في المادة 43، ينبغي له أن يدعو هذا العضو إلى أن يشترك إذا شاء في القرارات التي يصدرها فيما يختص باستخدام وحدات من قوات هذا العضو المسلحة. + +المادة 45 +رغبة في تمكين الأمم المتحدة من اتخاذ التدابير الحربية العاجلة يكون لدى الأعضاء وحدات جوية أهلية يمكن استخدامها فوراً لأعمال القمع الدولية المشتركة. ويحدد مجلس الأمن قوى هذه الوحدات ومدى استعدادها والخطط لأعمالها المشتركة، وذلك بمساعدة لجنة أركان الحرب وفي الحدود الواردة في الاتفاق أو الاتفاقات الخاصة المشار إليها في المادة 43. + +المادة 46 +الخطط اللازمة لاستخدام القوة المسلحة يضعها مجلس الأمن بمساعدة لجنة أركان الحرب. + +المادة 47 +تشكل لجنة من أركان الحرب تكون مهمتها أن تسدي المشورة والمعونة إلى مجلس الأمن وتعاونه في جميع المسائل المتصلة بما يلزمه من حاجات حربية لحفظ السلم والأمن الدولي ولاستخدام القوات الموضوعة تحت تصرفه وقيادتها ولتنظيم التسليح ونزع السلاح بالقدر المستطاع. +تشكل لجنة أركان الحرب من رؤساء أركان حرب الأعضاء الدائمين في مجلس الأمن أو من يقوم مقامهم، وعلى اللجنة أن تدعو أي عضو في "الأمم المتحدة" من الأعضاء غير الممثلين فيها بصفة دائمة للإشراف في عملها إذا اقتضى حسن قيام اللجنة بمسؤولياتها أن يساهم هذا العضو في عملها. +لجنة أركان الحرب مسؤولة تحت إشراف مجلس الأمن عن التوجيه الاستراتيجي لأية قوات مسلحة موضوعة تحت تصرف المجلس. أما المسائل المرتبطة بقيادة هذه القوات فستبحث فيما بعد. +للجنة أركان الحرب أن تنشئ لجاناً فرعية إقليمية إذا خوّلها ذلك مجلس الأمن وبعد التشاور مع الوكالات الإقليمية صاحبة الشأن. +المادة 48 +الأعمال اللازمة لتنفيذ قرارات مجلس الأمن لحفظ السلم والأمن الدولي يقوم بها جميع أعضاء "الأمم المتحدة" أو بعض هؤلاء الأعضاء وذلك حسبما يقرره المجلس. +يقوم أعضاء "الأمم المتحدة" بتنفيذ القرارات المتقدمة مباشرة وبطريق العمل في الوكالات الدولية المتخصصة التي يكونون أعضاء فيها. +المادة 49 +يتضافر أعضاء "الأمم المتحدة" على تقديم المعونة المتبادلة لتنفيذ التدابير التي قررها مجلس الأمن. + +المادة 50 +إذا اتخذ مجلس الأمن ضد أية دولة تدابير منع أو قمع فإن لكل دولة أخرى - سواء أكانت من أعضاء "الأمم المتحدة" أم لم تكن - تواجه مشاكل اقتصادية خاصة تنشأ عن تنفيذ هذه التدابير، الحق في أن تتذاكر مع مجلس الأمن بصدد حل هذه المشاكل. + +المادة 51 +ليس في هذا الميثاق ما يضعف أو ينتقص الحق الطبيعي للدول، فرادى أو جماعات، في الدفاع عن أنفسهم إذا اعتدت قوة مسلحة على أحد أعضاء "الأمم المتحدة" وذلك إلى أن يتخذ مجلس الأمن التدابير اللازمة لحفظ السلم والأمن الدولي، والتدابير التي اتخذها الأعضاء استعمالاً لحق الدفاع عن النفس تبلغ إلى المجلس فورا، ولا تؤثر تلك التدابير بأي حال فيما للمجلس - بمقتضى سلطته ومسؤولياته المستمرة من أحكام هذا الميثاق - من الحق في أن يتخذ في أي وقت ما يرى ضرورة لاتخاذه من الأعمال لحفظ السلم والأمن الدولي أو إعادته إلى نصابه. + +الفصل الثامـن: التنظيمات الإقليمية +المادة 52 +ليس في هذا الميثاق ما يحول دون قيام تنظيمات أو وكالات إقليمية تعالج من الأمور المتعلقة بحفظ السلم والأمن الدولي ما يكون العمل الإقليمي صالحاً فيها ومناسباً ما دامت هذه التنظيمات أو الوكالات الإقليمية ونشاطها متلائمة مع مقاصد "الأمم المتحدة" ومبادئها. +يبذل أعضاء "الأمم المتحدة" الداخلون في مثل هذه التنظيمات أو الذين تتألف منهم تلك الوكالات كل جهدهم لتدبير الحل السلمي للمنازعات المحلية عن طريق هذه التنظيمات الإقليمية أو بواسطة هذه الوكالات وذلك قبل عرضها على مجلس الأمن. +على مجلس الأمن أن يشجع على الاستكثار من الحل السلمي لهذه المنازعات المحلية بطريق هذه التنظيمات الإقليمية أو بواسطة تلك الوكالات الإقليمية بطلب من الدول التي يعنيها الأمر أو بالإحالة عليها من جانب مجلس الأمن. +لا تعطل هذه المادة بحال من الأحوال تطبيق المادتين 34 و 35. +المادة 53 +يستخدم مجلس الأمن تلك التنظيمات والوكالات الإقليمية في أعمال القمع، كلما رأى ذلك ملائماً ويكون عملها حينئذ تحت مراقبته وإشرافه. أما التنظيمات والوكالات نفسها فإنه لا يجوز بمقتضاها أو على يدها القيام بأي عمل من أعمال القمع بغير إذن المجلس، ويستثنى مما تقدم التدابير التي تتخذ ضد أية دولة من دول الأعداء المعرّفة في الفقرة 2 من هذه المادة مما هو منصوص عليه في المادة 107 أو التدابير التي يكون المقصود بها في التنظيمات الإقليمية منع تجدد سياسة العدوان من جانب دولة من تلك الدول، وذلك إلى أن يحين الوقت الذي قد يعهد فيه إلى الهيئة، بناءً على طلب الحكومات ذات الشأن، بالمسؤولية عن منع كل عدوان آخر من جانب أية دولة من تلك الدول. +تنطبق عبارة "الدولة المعادية" المذكورة في الفقرة 1 من هذه المادة على أية دولة كانت في الحرب العالمية الثانية من أعداء أية دولة موقعة على هذا الميثاق. +المادة 54 +يجب أن يكون مجلس الأمن على علم تام بما يجري من الأعمال لحفظ السلم والأمن الدولي بمقتضى تنظيمات أو بواسطة وكالات إقليمية أو ما يزمع إجراؤه منها. + +الفصل التاسع: التعاون الدولي الاقتصادي والاجتماعي +المادة 55 +رغبة في تهيئة دواعي الاستقرار والرفاهية الضروريين لقيام علاقات سليمة ودية بين الأمم المتحدة مؤسسة على احترام المبدأ الذي يقضي بالتسوية في الحقوق بين الشعوب وبأن يكون لكل منها تقرير مصيرها، تعمل الأمم المتحدة على: + +تحقيق مستوى أعلى للمعيشة وتوفير أسباب الاستخدام المتصل لكل فرد والنهوض بعوامل التطور والتقدم الاقتصادي والاجتماعي. +تيسير الحلول للمشاكل الدولية الاقتصادية والاجتماعية والصحية وما يتصل بها، وتعزيز التعاون الدولي في أمور الثقافة والتعليم. +أن يشيع في العالم احترام حقوق الإنسان والحريات الأساسية للجميع بلا تمييز بسبب الجنس أو اللغة أو الدين، ولا تفريق بين الرجال والنساء، ومراعاة تلك الحقوق والحريات فعلاً. +المادة 56 +يتعهد جميع الأعضاء بأن يقوموا، منفردين أو مشتركين، بما يجب عليهم من عمل بالتعاون مع الهيئة لإدراك المقاصد المنصوص عليها في المادة 55. + +المادة 57 +الوكالات المختلفة التي تنشأ بمقتضى اتفاق بين الحكومات والتي تضطلع بمقتضى نظمها الأساسية بتبعات دولية واسعة في الاقتصاد والاجتماع والثقافة والتعليم والصحة وما يتصل بذلك من الشؤون يوصل بينها وبين "الأمم المتحدة" وفقا لأحكام المادة 63. +تسمى هذه الوكالات التي يوصل بينها وبين "الأمم المتحدة" فيما يلي من الأحكام بالوكالات المتخصصة. +المادة 58 +تقدم الهيئة توصيات بقصد تنسيق سياسات الوكالات المتخصصة ووجوه نشاطها. + +المادة 59 +تدعو الهيئة عند المناسبة إلى أجراء مفاوضات بين الدول ذات الشأن بقصد إنشاء أية وكالة متخصصة جديدة يتطلبها تحقيق المقاصد المبينة في المادة 55. + +المادة 60 +مقاصد الهيئة المبينة في هذا الفصل تقع مسؤولية تحقيقها على عاتق الجمعية العامة كما تقع على عاتق المجلس الاقتصادي والاجتماعي تحت إشراف الجمعية العامة، ويكون لهذا المجلس من أجل ذلك السلطات المبينة في الفصل العاشر. + +الفصل العاشر: المجلس الاقتصادي والاجتماعي +التأليف +المادة 61 +يتألف المجلس الاقتصادي والاجتماعي من أربعة وخمسين عضواً من الأمم المتحدة تنتخبهم الجمعية العامة. +مع مراعاة أحكام الفقرة 3، ينتخب ثمانية عشر عضواً من أعضاء المجلس الاقتصادي والاجتماعي كل سنة لمدة ثلاث سنوات ويحوز أن يعاد انتخاب العضو الذي انتهت مدته مباشرة. +في الانتخاب الأول بعد زيادة عدد أعضاء المجلس الاقتصادي والاجتماعي من سبعة وعشرين إلى أربعة وخمسين عضواً، يختار سبعة وعشرون عضواً إضافياً علاوة على الأعضاء المنتخبين محل الأعضاء التسعة الذين تنتهي مدة عضويتهم في نهاية هذا العام. وتنتهي عضوية تسعة من هؤلاء الأعضاء السبعة والعشرين الإضافيين بعد انقضاء سنة واحدة، وتنتهي عضوية تسعة أعضاء آخرين بعد انقضاء سنتين، ويجرى ذلك وفقا للنظام الذي تضعه الجمعية العامة. +يكون لكل عضو من أعضاء المجلس الاقتصادي والاجتماعي مندوب واحد. +الوظائف والسلطـات +المادة 62 +للمجلس الاقتصادي والاجتماعي أن يقوم بدراسات ويضع تقارير عن المسائل الدولية في أمور الاقتصاد والاجتماع والثقافة والتعليم والصحة وما يتصل بها، كما أن له أن يوجه إلى مثل تلك الدراسات وإلى وضع مثل تلك التقارير. وله أن يقدم توصياته في أية مسألة من تلك المسائل إلى الجمعية العامة وإلى أعضاء "الأمم المتحدة" وإلى الوكالات المتخصصة ذات الشأن. +وله أن يقدم توصيات فيما يختص بإشاعة احترام حقوق الإنسان والحريات الأساسية ومراعاتها. +وله أن يعد مشروعات اتفاقات لتعرض على الجمعية العامة عن المسائل التي تدخل في دائرة اختصاصه. +وله أن يدعو إلى عقد مؤتمرات دولية لدراسة المسائل التي تدخل في دائرة اختصاصه وفقا للقواعد التي تضعها "الأمم المتحدة". +المادة 63 +للمجلس الاقتصادي والاجتماعي أن يضع اتفاقات مع أي وكالة من الوكالات المشار إليها في المادة 57 تحدد الشروط التي على مقتضاها يوصل بينها وبين "الأمم المتحدة" وتعرض هذه الاتفاقات على الجمعية العامة للموافقة عليها. +وله أن ينسق وجوه نشاط الوكالات المتخصصة بطريق التشاور معها وتقديم توصياته إليها وإلى الجمعية العامة وأعضاء "الأمم المتحدة". +المادة 64 +للمجلس الاقتصادي والاجتماعي أن يتخذ الخطوات المناسبة للحصول بانتظام على تقارير من الوكالات المتخصصة وله أن يضع مع أعضاء "الأمم المتحدة" ومع الوكالات المتخصصة ما يلزم من الترتيبات كيما تمده بتقارير عن الخطوات التي اتخذتها لتنفيذ توصياته أو لتنفيذ توصيات الجمعية العامة في شأن المسائل الداخلة في اختصاصه. +وله أن يبلغ الجمعية العامة ملاحظاته على هذه التقارير. +المادة 65 +للمجلس الاقتصادي والاجتماعي أن يمد مجلس الأمن بما يلزم من المعلومات وعليه أن يعاونه متى طلب إليه ذلك. + +المادة 66 +يقوم المجلس الاقتصادي والاجتماعي في تنفيذ توصيات الجمعية العامة بالوظائف التي تدخل في اختصاصه. +وله بعد موافقة الجمعية العامة أن يقوم بالخدمات اللازمة لأعضاء "الأمم المتحدة" أو الوكالات المتخصصة متى طلب إليه ذلك. +يقوم المجلس بالوظائف الأخرى المبينة في غير هذا الموضع مع الميثاق وبالوظائف التي قد تعهد بها إليه الجمعية العامة. +التصويت +المادة 67 +يكون لكل عضو من أعضاء المجلس الاقتصادي والاجتماعي صوت واحد. +تصدر قرارات المجلس الاقتصادي والاجتماعي بأغلبية أعضائه الحاضرين المشتركين في التصويت. +الإجـراءات +المادة 68 +ينشئ المجلس الاقتصادي والاجتماعي لجاناً للشؤون الاقتصادية والاجتماعية ولتعزيز حقوق الإنسان، كما ينشئ غير ذلك من اللجان التي قد يحتاج إليها لتأدية وظائفه. + +المادة 69 +يدعو المجلس الاقتصادي والاجتماعي أي عضو من "الأمم المتحدة" للاشتراك في مداولاته عند بحث أية مسألة تعني هذا العضو بوجه خاص، على ألا يكون له حق التصويت. + +المادة 70 +للمجلس الاقتصادي والاجتماعي أن يعمل على إشراك مندوبي الوكالات المتخصصة في مداولاته أو في مداولات اللجان التي ينشئها دون أن يكون لهم حق التصويت، كما أن له أن يعمل على إشراك مندوبيه في مداولات الوكالة المتخصصة. + +المادة 71 +للمجلس الاقتصادي والاجتماعي أن يجرى الترتيبات المناسبة للتشاور مع الهيئات غير الحكومية التي تعني بالمسائل الداخلة في اختصاصه. وهذه الترتيبات قد يجريها المجلس مع هيئات دولية، كما أنه قد يجريها إذا رأى ذلك ملائماً مع هيئات أهلية وبعد التشاور مع عضو "الأمم المتحدة" ذي الشأن. + +المادة 72 +يضع المجلس الاقتصادي والاجتماعي لائحة إجراءاته ومنها طريقة اختيار رئيسه. +يجتمع المجلس الاقتصادي والاجتماعي كلما دعت الحاجة إلى ذلك وفقا للائحة التي يسنها. ويجب أن تتضمن تلك اللائحة النص على دعوته للاجتماع بناءً على طلب يقدم من أغلبية أعضائه. +الفصل الحادي عشر: تصريح يتعلق بالأقاليم غير المتمتعة بالحكم الذاتي +المادة 73 +يقرر أعضاء الأمم المتحدة - الذين يضطلعون في الحال أو في المستقبل بتبعات عن إدارة أقاليم لم تنل شعوبها قسطاً كاملاً من الحكم الذاتي - المبدأ القاضي بأن مصالح أهل هذه الأقاليم لها المقام الأول، ويقبلون أمانة مقدسة في عنقهم، الالتزام بالعمل على تنمية رفاهية أهل هذه الأقاليم إلى أقصى حد مستطاع في نطاق السلم والأمن الدولي الذي رسمه هذا الميثاق. ولهذا الغرض: + +يكفلون تقدم هذه الشعوب في شؤون السياسة والاقتصاد والاجتماع والتعليم، كما يكفلون معاملتها بإنصاف وحمايتها من ضروب الإساءة - كل ذلك مع مراعاة الاحترام الواجب لثقافة هذه الشعوب. +ينمون الحكم الذاتي، ويقدرون الأماني السياسية لهذه الشعوب قدرها، ويعاونونها على إنماء نظمها السياسية الحرة نمواً مطرداً، وفقا للظروف الخاصة لكل إقليم وشعوبه، ومراحل تقدمها المختلفة. +يوطدون السلم والأمن الدولي. +يعززون التدابير الإنسانية للرقي والتقدم، ويشجعون البحوث، ويتعاونون فيما بينهم لتحقيق المقاصد الاجتماعية والاقتصادية والعلمية المفصّلة في هذه المادة تحقيقاً عملياً، كما يتعاونون أيضاً لهذا الغرض مع الهيئات الدولية المتخصصة كلما تراءت لهم ملاءمة ذلك. ./li> +يرسلون إلى الأمين العام بانتظام يحيطونه علماً بالبيانات الإحصائية وغيرها من البيانات الفنية المتعلقة بأمور الاقتصاد والاجتماع والتعليم في الأقاليم التي يكونون مسؤولين عنها، عدا الأقاليم التي تنطبق عليها أحكام الفصلين الثاني عشر والثالث عشر من هذا الميثاق. كل ذلك مع مراعاة القيود التي قد تستدعيها الاعتبارات المتعلقة بالأمن والاعتبارات الدستورية. +المادة 74 +يوافق أعضاء الأمم المتحدة أيضاً على أن سياستهم إزاء الأقاليم التي ينطبق عليها هذا الفصل - كسياستهم في بلادهم نفسها - يجب أن تقوم على مبدأ حسن الجوار، وأن تراعي حق المراعاة مصالح بقية أجزاء العالم ورفاهيتها في الشؤون الاجتماعية والاقتصادية والتجارية. + +الفصل الثاني عشر: نظام الوصاية الدولي +المادة 75 +تنشئ "الأمم المتحدة" تحت إشرافها نظاماً دولياً للوصاية، وذلك لإدارة الأقاليم التي قد تخضع لهذا النظام بمقتضى إتفاقات فردية لاحقة وللإشراف عليها، ويطلق على هذه الأقاليم فيما يلي من الأحكام اسم الأقاليم المشمولة بالوصاية. + +المادة 76 +الأهداف الأساسية لنظام الوصاية طبقاً لمقاصد "الأمم المتحدة" المبينة في المادة الأولى من هذا الميثاق هي: + +توطيد السلم والأمن الدولي؛ +لحكم الذاتي أو الاستقلال حسبما يلائم الظروف الخاصة لكل إقليم وشعوبه، ويتفق مع رغبات هذه الشعوب التي تعرب عنها بملء حريتها وطبقاً لما قد ينص عليه في شروط كل اتفاق من اتفاقات الوصاية؛ +التشجيع على احترام حقوق الإنسان والحريات الأساسية للجميع بلا تمييز بسبب الجنس أو اللغة أو الدين، ولا تفريق بين الرجال والنساء، والتشجيع على إدراك ما بين شعوب العالم من تقيد بعضهم بالبعض؛ +كفالة المساواة في المعاملة في الأمور الاجتماعية والاقتصادية والتجارية لجميع أعضاء "الأمم المتحدة" وأهاليها والمساواة بين هؤلاء الأهالي أيضاً فيما يتعلق بإجراء القضاء، وذلك مع عدم الإخلال بتحقيق الأغراض المتقدمة ومع مراعاة أحكام المادة 80. +المادة 77 +يطبق نظام الوصاية على الأقاليم الداخلة في الفئات الآتية مما قد يوضع تحت حكمها بمقتضى اتفاقات وصاية: +الأقاليم المشمولة الآن بالانتداب؛ +الأقاليم التي قد تقتطع من دول الأعداء نتيجة للحرب العالمية الثانية؛ +الأقاليم التي تضعها في الوصاية بمحض اختيارها دول مسؤولة عن إدارتها. +أما تعيين أي الأقاليم من الفئات سالفة الذكر يوضع تحت نظام الوصاية وطبقاً لأي شروط، فذلك من شأن ما يعقد بعد من اتفاقات +المادة 78 +لا يطبق نظام الوصاية على الأقاليم التي أصبحت أعضاء في هيئة "الأمم المتحدة" إذ العلاقات بين أعضاء هذه الهيئة يجب أن تقوم على احترام مبدأ المساواة في السيادة. + +المادة 79 +شروط الوصاية لكل إقليم يوضع تحت ذلك النظام، وكل تغيير أو تعديل يطرآن بعد عليها، ذلك كله يتفق عليه برضا الدول التي يعنيها هذا الأمر بالذات ومنها الدولة المنتدبة في حالة الأقاليم المشمولة بانتداب أحد أعضاء "الأمم المتحدة". وهذا مع مراعاة أحكام المادتين 83 و85 في شأن المصادقة على تلك الشروط وتعديلاتها. + +المادة 80 +فيما عدا ما قد يتفق عليه في اتفاقات الوصاية الفردية التي تبرم وفق أحكام المواد 77 و 79 و 81 وبمقتضاها توضع الأقاليم تحت الوصاية، وإلى أن تعقد مثل هذه الاتفاقات لا يجوز تأويل نص أي حكم من أحكام هذا الفصل ولا تخريجه تأويلاً أو تخريجاً من شأنه أن يغير بطريقة ما أية حقوق لأية دول أو شعوب، أو يغير شروط الاتفاقات الدولية القائمة التي قد يكون أعضاء "الأمم المتحدة" أطرافاً فيها. +لا يجوز أن تؤول الفقرة الأولى من هذه المادة على أنها تهيئ سبباً لتأخير أو تأجيل المفاوضة في الاتفاقات التي ترمي لوضع الأقاليم المشمولة بالانتداب أو غيرها من الأقاليم في نظام الوصاية طبقا للمادة 77 أو تأخير أو تأجيل إبرام مثل تلك الاتفاقات. +المادة 81 +يشمل اتفاق الوصاية، في كل حالة، الشروط التي يدار بمقتضاها الإقليم المشمول بالوصاية، ويعين السلطة التي تباشر إدارة ذلك الإقليم، ويجوز أن تكون هذه السلطة التي يطلق عليها فيما يلي من الأحكام "السلطة القائمة بالإدارة" دولة أو أكثر أو هيئة "الأمم المتحدة" ذاتها. + +المادة 82 +يجوز أن يحدد في أي اتفاق من اتفاقات الوصاية موقع استراتيجي قد يشمل الإقليم الذي ينطبق عليه نظام الوصاية بعضه أو كله، وذلك دون الإخلال بأي اتفاق أو اتفاقات خاصة معقودة طبقا لنص المادة 43. + +المادة 83 +يباشر مجلس الأمن جميع وظائف "الأمم المتحدة" المتعلقة بالمواقع الاستراتيجية، ويدخل في ذلك الموافقة على شروط اتفاقات الوصاية وتغييرها أو تعديلها. +تراعى جميع الأهداف الأساسية المبينة في المادة 76 بالنسبة لشعب كل موقع استراتيجي. +يستعين مجلس الأمن بمجلس الوصاية - مع مراعاة أحكام اتفاقيات الوصاية ودون إخلال بالاعتبارات المتصلة بالأمن - في مباشرة ما كان من وظائف "الأمم المتحدة" في نظام الوصاية خاصاً بالشؤون السياسية والاقتصادية والاجتماعية والتعليمية للمواقع الاستراتيجية. +المادة 84 +يكون من واجب السلطة القائمة بالإدارة أن تكفل قيام الإقليم المشمول بالوصاية بنصيبه في حفظ السلم والأمن الدولي. وتحقيقا لهذه الغاية يجوز للسلطة القائمة بالإدارة أن تستخدم قوات متطوعة وتسهيلات ومساعدات من الإقليم المشمول بالوصاية للقيام بالالتزامات التي تعهدت بها تلك السلطة لمجلس الأمن في هذا الشأن، وللقيام أيضاً بالدفاع وبإقرار حكم القانون والنظام داخل الإقليم المشمول بالوصاية. + +المادة 85 +تباشر الجمعية العامة وظائف "الأمم المتحدة" فيما يختص باتفاقات الوصاية على كل المساحات التي لم ينص على أنها مساحات استراتيجية ويدخل في ذلك إقرار شروط اتفاقات الوصاية وتغييرها أو تعديلها. +يساعد مجلس الوصاية الجمعية العامة في القيام بهذه الوظائف عاملاً تحت إشرافها. +الفصل الثالث عشر: مجلس الوصاية +التأليف +المادة 86 +يتألف مجلس الوصاية من أعضاء "الأمم المتحدة" الآتي بيانهم: +الأعضاء الذين يتولون إدارة أقاليم مشمولة بالوصاية؛ +الأعضاء المذكورون بالاسم في المادة 23 الذين لا يتولون إدارة أقاليم مشمولة بالوصاية؛ +العدد الذي يلزم من الأعضاء الآخرين لكفالة أن يكون جملة أعضاء مجلس الوصاية فريقين متساويين، أحدهما الأعضاء الذين يقومون بإدارة الأقاليم المشمولة بالوصاية، والآخر الأعضاء الذين خلوا من تلك الإدارة. وتنتخب الجمعية العامة هؤلاء الأعضاء لمدة ثلاث سنوات. +يعين كل عضو من أعضاء مجلس الوصاية من يراه أهلاً بوجه خاص لتمثيله في هذا المجلس. +الوظائف والسلطـات +المادة 87 +لكل من الجمعية العامة ومجلس الوصاية عاملاً تحت إشرافها، وهما يقومان بأداء وظائفهما: + +أن ينظر في التقارير التي ترفعها السلطة القائمة بالإدارة. +أن يقبل العرائض ويفحصها بالتشاور مع السلطة القائمة بالإدارة. +أن يقبل العرائض ويفحصها بالتشاور مع السلطة القائمة بالإدارة. +أن يتخذ هذه التدابير وغيرها، وفقا للشروط المبينة في اتفاقات الوصاية. +المادة 88 +يضع مجلس الوصاية طائفة من الأسئلة عن تقدم سكان كل إقليم مشمول بالوصاية في الشؤون السياسية والاقتصادية والاجتماعية والتعليمية. وتقدم السلطة القائمة بالإدارة في كل إقليم مشمول بالوصاية داخل اختصاص الجمعية العامة تقريراً سنوياً للجمعية العامة موضوعاً على أساس هذه الأسئلة. + +التصويت +المادة 89 +يكون لك عضو في مجلس الوصاية صوت واحد. +تصدر قرارات مجلس الوصاية بأغلبية الأعضاء الحاضرين المشتركين في التصويت. +الإجـراءات +المادة 90 +يضع مجلس الوصاية لائحة إجراءاته ومنها طريقة اختيار رئيسه. +يجتمع مجلس الوصاية كلما دعت الحاجة لذلك وفقاً للائحة التي يسنها. ويجب أن تتضمن تلك اللائحة النص على دعوته للاجتماع بناءً على طلب يقدم من أغلبية أعضائه. +المادة 91 +يستعين مجلس الوصاية، كلما كان ذلك مناسباً، بالمجلس الاقتصادي والاجتماعي وبالوكالات المتخصصة في كل ما يختص به كل منها من الشؤون. + +الفصل الرابع عشر: محكمة العدل الدولية +المادة 92 +محكمة العدل الدولية هي الأداة القضائية الرئيسية "للأمم المتحدة"، وتقوم بعملها وفق نظامها الأساسي الملحق بهذا الميثاق وهو مبني على النظام الأساسي للمحكمة الدائمة للعدل الدولي وجزء لا يتجزأ من الميثاق. + +المادة 93 +يعتبر جميع أعضاء "الأمم المتحدة" بحكم عضويتهم أطرافاً في النظام الأساسي لمحكمة العدل الدولية. +يجوز لدولة ليست من "الأمم المتحدة" أن تنضم إلى النظام الأساسي لمحكمة العدل الدولية بشروط تحددها الجمعية العامة لكل حالة بناء على توصية مجلس الأمن. +المادة 94 +يتعهد كل عضو من أعضاء "الأمم المتحدة" أن ينزل على حكم محكمة العدل الدولية في أية قضية يكون طرفاً فيها. +إذا امتنع أحد المتقاضين في قضية ما عن القيام بما يفرضه عليه حكم تصدره المحكمة، فللطرف الآخر أن يلجأ إلى مجلس الأمن، ولهذا المجلس، إذا رأى ضرورة لذلك أن يقدم توصياته أو يصدر قراراً بالتدابير التي يجب اتخاذها لتنفيذ هذا الحكم. +المادة 95 +ليس في هذا الميثاق ما يمنع أعضاء "الأمم المتحدة" من أن يعهدوا بحل ما ينشأ بينهم من خلاف إلى محاكم أخرى بمقتضى اتفاقات قائمة من قبل أو يمكن أن تعقد بينهم في المستقبل. + +المادة 96 +لأي من الجمعية العامة أو مجلس الأمن أن يطلب إلى محكمة العدل الدولية إفتاءه في أية مسألة قانونية. +ولسائر فروع الهيئة والوكالات المتخصصة المرتبطة بها، ممن يجوز أن تأذن لها الجمعية العامة بذلك في أي وقت، أن تطلب أيضاً من المحكمة إفتاءها فيما يعرض لها من المسائل القانونية الداخلة في نطاق أعمالها. +الفصل الخامس عشر: الأمـانة العامة +المادة 97 +يكون للهيئة أمانة تشمل أميناً عاماً ومن تحتاجهم الهيئة من الموظفين. وتعين الجمعية العامة الأمين العام بناءً على توصية مجلس الأمن. والأمين العام هو الموظف الإداري الأكبر في الهيئة. + +المادة 98 +يتولى الأمين العام أعماله بصفته هذه في كل اجتماعات الجمعية العامة ومجلس الأمن والمجلس الاقتصادي والاجتماعي، ومجلس الوصاية، ويقوم بالوظائف الأخرى التي تكلها إليه هذه الفروع. ويعد الأمين العام تقريراً سنوياً للجمعية العامة بأعمال الهيئة. + +المادة 99 +للأمين العام أن ينبه مجلس الأمن إلى أية مسألة يرى أنها قد تهدد حفظ السلم والآمن الدولي. + +المادة 100 +ليس للأمين العام ولا للموظفين أن يطلبوا أو أن يتلقوا في تأدية واجبهم تعليمات من أية حكومة أو من أية سلطة خارجة عن الهيئة. وعليهم أن يمتنعوا عن القيام بأي عمل قد يسئ إلى مراكزهم بوصفهم موظفين دوليين مسؤولين أمام الهيئة وحدها. +يتعهد كل عضو في "الأمم المتحدة" باحترام الصفة الدولية البحتة لمسؤوليات الأمين العام والموظفين وبألا يسعى إلى التأثير فيهم عند اضطلاعهم بمسؤولياتهم. +المادة 101 +يعين الأمين العام موظفي الأمانة طبقا للوائح التي تضعها الجمعية العامة. +يعين للمجلس الاقتصادي والاجتماعي ولمجلس الوصاية ما يكفيهما من الموظفين على وجه دائم ويعين لغيرهما من فروع "الأمم المتحدة" الأخرى ما هي بحاجة إليه منهم. وتعتبر جملة هؤلاء الموظفين جزءاً من الأمانة. +ينبغي في استخدام الموظفين وفي تحديد شروط خدمتهم أن يراعى في المكان الأول ضرورة الحصول على أعلى مستوى من المقدرة والكفاية والنزاهة. كما أن من المهم أن يراعى في اختيارهم أكبر ما يستطاع من معاني التوزيع الجغرافي. +Chapter XVI: Miscellaneous Provisions +المادة 102 +كل معاهدة وكل اتفاق دولي يعقده أي عضو من أعضاء "الأمم المتحدة" بعد العمل بهذا الاتفاق يجب أن يسجل في أمانة الهيئة وأن تقوم بنشره بأسرع ما يمكن. +ليس لأي طرف في معاهدة أو اتفاق دولي لم يسجل وفقاً للفقرة الأولى من هذه المادة أن يتمسك بتلك المعاهدة أو ذلك الاتفاق أمام أي فرع من فروع "الأمم المتحدة". +المادة 103 +إذا تعارضت الالتزامات التي يرتبط بها أعضاء "الأمم المتحدة" وفقاً لأحكام هذا الميثاق مع أي التزام دولي آخر يرتبطون به فالعبرة بالتزاماتهم المترتبة على هذا الميثاق. + +المادة 104 +تتمتع الهيئة في بلاد كل عضو من أعضائها بالأهلية القانونية التي يتطلبها قيامها بأعباء وظائفها وتحقيق مقاصدها. + +المادة 105 +تتمتع الهيئة في أرض كل عضو من أعضائها بالمزايا والإعفاءات التي يتطلبها تحقيق مقاصدها. +وكذلك يتمتع المندوبون عن أعضاء "الأمم المتحدة" وموظفو هذه الهيئة بالمزايا والإعفاءات التي يتطلبها استقلالهم في القيام بمهام وظائفهم المتصلة بالهيئة. +للجمعية العامة أن تقدم التوصيات بقصد تحديد التفاصيل الخاصة بتطبيق الفقرتين 1 و 2 من هذه المادة، ولها أن تقترح على أعضاء الهيئة عقد اتفاقات لهذا الغرض. +الفصل السابع عشر: تدابير حفظ الأمن في فترة الانتقال +المادة 106 +إلى أن تصير الاتفاقات الخاصة المشار إليها في المادة الثالثة والأربعين معمولاً بها على الوجه الذي يرى معه مجلس الأمن أنه أصبح يستطيع البدء في احتمال مسؤولياته وفقاً للمادة 42، تتشاور الدول التي اشتركت في تصريح الدول الأربع الموقع في موسكو في 30 تشرين الأول/أكتوبر سنة 1943 هي وفرنسا وفقاً لأحكام الفقرة 5 من ذلك التصريح، كما تتشاور الدول الخمس مع أعضاء "الأمم المتحدة" الآخرين، كلما اقتضت الحال، للقيام نيابة عن الهيئة بالأعمال المشتركة التي قد تلزم لحفظ السلم والأمن الدولي. + +المادة 107 +ليس في هذا الميثاق ما يبطل أو يمنع أي عمل إزاء دولة كانت في أثناء الحرب العالمية الثانية معادية لإحدى الدول الموقعة على هذا الميثاق إذا كان هذا العمل قد اتخذ أو رخص به نتيجة لتلك الحرب من قبل الحكومات المسؤولة عن القيام بهذا العمل. + +الفصل الثامن عشر: تعديل الميثاق +المادة 108 +التعديلات التي تدخل على هذا الميثاق تسري على جميع أعضاء "الأمم المتحدة" إذا صدرت بموافقة ثلثي أعضاء الجمعية العامة وصدق عليها ثلثا أعضاء "الأمم المتحدة" ومن بينهم جميع أعضاء مجلس الأمن الدائمين، وفقا للأوضاع الدستورية في كل دولة. + +المادة 109 +يجوز عقد مؤتمر عام من أعضاء "الأمم المتحدة" لإعادة النظر في هذا الميثاق في الزمان والمكان اللذين تحددهما الجمعية العامة بأغلبية ثلثي أعضائها وبموافقة تسعة ما من أعضاء مجلس الأمن, ويكون لكل عضو في "الأمم المتحدة" صوت واحد في المؤتمر. +كل تغيير في هذا الميثاق أوصى به المؤتمر بأغلبية ثلثي أعضائه يسري إذا صدق عليه ثلثا أعضاء "الأمم المتحدة" ومن بينهم الأعضاء الدائمون في مجلس الأمن وفقا لأوضاعهم الدستورية. +إذا لم يعقد هذا المؤتمر قبل الدورة العادية العاشرة للجمعية العامة، بعد العمل بهذا الميثاق، وجب أن يدرج بجدول أعمال ذلك الدور العاشر اقتراح بالدعوة إلى عقده، وهذا المؤتمر يعقد إذا قررت ذلك أغلبية أعضاء الجمعية العامة وسبعة ما من أعضاء مجلس الأمن. +الفصل التاسع عشر: التصديق والتوقيع +المادة 110 +تصدق على هذا الميثاق الدول الموقعة عليه كل منها حسب أوضاعه الدستورية. +تودع التصديقات لدى حكومة الولايات المتحدة الأمريكية التي تخطر الدول الموقعة عليه بكل إيداع يحصل، كما تخطر الأمين العام لهيئة "الأمم المتحدة" بعد تعيينه. +يصبح هذا الميثاق معمولاً به متى أودعت تصديقاتها جمهورية الصين وفرنسا واتحاد الجمهوريات الاشتراكية السوفياتية والمملكة المتحدة لبريطانيا العظمى وأيرلندا الشمالية والولايات المتحدة الأمريكية وأغلبية الدول الأخرى الموقعة عليه وتعد حكومة الولايات المتحدة الأمريكية بروتوكولاً خاصاً بالتصديقات المودعة وتبلغ صوراً منه لكل الدول الموقعة على الميثاق. +الدول الموقعة على هذا الميثاق التي تصدق عليه بعد العمل به، تعتبر من الأعضاء الأصليين في "الأمم المتحدة" من تاريخ إيداعها لتصديقاتها. +المادة 111 +وضع هذا الميثاق بلغات خمس هي الصينية والفرنسية والروسية والإنجليزية والأسبانية، وهي لغاته الرسمية على وجه السواء . ويظل الميثاق مودعاًُ في محفوظات حكومة الولايات المتحدة الأمريكية، وتبلغ هذه الحكومة حكومات الدول الأخرى الموقعة عليه صوراً معتمدة منه. + +ومصادقا لما تقدم وقع مندوبو حكومات "الأمم المتحدة" على هذا الميثاق. صدر بمدينة سان فرانسيسكو في اليوم السادس والعشرين من شهر حزيران/يونيه 1945. \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_EN.html b/stdlib/benchmarks/collections/data/UN_charter_EN.html new file mode 100644 index 0000000000..a1ba6dc872 --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_EN.html @@ -0,0 +1,2333 @@ + + + + + + + + + + + + + + + + + + + United Nations Charter (full text) | United Nations + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    + + + + +
    +
    + + +
    + + + + + + + + + + +
    +
    +
    + +
    + + + +

    United Nations Charter (full text)

    + + + + +
    +
    + + +
    + + + + +
    +
    +
    +
    +

    Preamble

    + +

    WE THE PEOPLES OF THE UNITED NATIONS DETERMINED

    + +

    to save succeeding generations from the scourge of war, which twice in our lifetime has brought untold sorrow to mankind, and

    + +

    to reaffirm faith in fundamental human rights, in the dignity and worth of the human person, in the equal rights of men and women and of nations large and small, and

    + +

    to establish conditions under which justice and respect for the obligations arising from treaties and other sources of international law can be maintained, and

    + +

    to promote social progress and better standards of life in larger freedom,

    + +

    AND FOR THESE ENDS

    + +

    to practice tolerance and live together in peace with one another as good neighbours, and

    + +

    to unite our strength to maintain international peace and security, and

    + +

    to ensure, by the acceptance of principles and the institution of methods, that armed force shall not be used, save in the common interest, and

    + +

    to employ international machinery for the promotion of the economic and social advancement of all peoples,

    + +

    HAVE RESOLVED TO COMBINE OUR EFFORTS TO ACCOMPLISH THESE AIMS.

    + +

    Accordingly, our respective Governments, through representatives assembled in the city of San Francisco, who have exhibited their full powers found to be in good and due form, have agreed to the present Charter of the United Nations and do hereby establish an international organization to be known as the United Nations.

    + +

    Chapter I: Purposes and Principles

    + +

    Article 1

    + +

    The Purposes of the United Nations are:

    + +
      +
    1. To maintain international peace and security, and to that end: to take effective collective measures for the prevention and removal of threats to the peace, and for the suppression of acts of aggression or other breaches of the peace, and to bring about by peaceful means, and in conformity with the principles of justice and international law, adjustment or settlement of international disputes or situations which might lead to a breach of the peace;
    2. +
    3. To develop friendly relations among nations based on respect for the principle of equal rights and self-determination of peoples, and to take other appropriate measures to strengthen universal peace;
    4. +
    5. To achieve international co-operation in solving international problems of an economic, social, cultural, or humanitarian character, and in promoting and encouraging respect for human rights and for fundamental freedoms for all without distinction as to race, sex, language, or religion; and
    6. +
    7. To be a centre for harmonizing the actions of nations in the attainment of these common ends.
    8. +
    + +

    Article 2

    + +

    The Organization and its Members, in pursuit of the Purposes stated in Article 1, shall act in accordance with the following Principles.

    + +
      +
    1. The Organization is based on the principle of the sovereign equality of all its Members.
    2. +
    3. All Members, in order to ensure to all of them the rights and benefits resulting from membership, shall fulfill in good faith the obligations assumed by them in accordance with the present Charter.
    4. +
    5. All Members shall settle their international disputes by peaceful means in such a manner that international peace and security, and justice, are not endangered.
    6. +
    7. All Members shall refrain in their international relations from the threat or use of force against the territorial integrity or political independence of any state, or in any other manner inconsistent with the Purposes of the United Nations.
    8. +
    9. All Members shall give the United Nations every assistance in any action it takes in accordance with the present Charter, and shall refrain from giving assistance to any state against which the United Nations is taking preventive or enforcement action.
    10. +
    11. The Organization shall ensure that states which are not Members of the United Nations act in accordance with these Principles so far as may be necessary for the maintenance of international peace and security.
    12. +
    13. Nothing contained in the present Charter shall authorize the United Nations to intervene in matters which are essentially within the domestic jurisdiction of any state or shall require the Members to submit such matters to settlement under the present Charter; but this principle shall not prejudice the application of enforcement measures under Chapter Vll.
    14. +
    + +

    Chapter II: Membership

    + +

    Article 3

    + +

    The original Members of the United Nations shall be the states which, having participated in the United Nations Conference on International Organization at San Francisco, or having previously signed the Declaration by United Nations of 1 January 1942, sign the present Charter and ratify it in accordance with Article 110.

    + +

    Article 4

    + +
      +
    1. Membership in the United Nations is open to all other peace-loving states which accept the obligations contained in the present Charter and, in the judgment of the Organization, are able and willing to carry out these obligations.
    2. +
    3. The admission of any such state to membership in the United Nations will be effected by a decision of the General Assembly upon the recommendation of the Security Council.
    4. +
    + +

    Article 5

    + +

    A Member of the United Nations against which preventive or enforcement action has been taken by the Security Council may be suspended from the exercise of the rights and privileges of membership by the General Assembly upon the recommendation of the Security Council. The exercise of these rights and privileges may be restored by the Security Council.

    + +

    Article 6

    + +

    A Member of the United Nations which has persistently violated the Principles contained in the present Charter may be expelled from the Organization by the General Assembly upon the recommendation of the Security Council.

    + +

    Chapter III: Organs

    + +

    Article 7

    + +
      +
    1. There are established as principal organs of the United Nations: a General Assembly, a Security Council, an Economic and Social Council, a Trusteeship Council, an International Court of Justice and a Secretariat.
    2. +
    3. Such subsidiary organs as may be found necessary may be established in accordance with the present Charter.
    4. +
    + +

    Article 8

    + +

    The United Nations shall place no restrictions on the eligibility of men and women to participate in any capacity and under conditions of equality in its principal and subsidiary organs.

    + +

    Chapter IV: The General Assembly

    + +

    COMPOSITION

    + +

    Article 9

    + +
      +
    1. The General Assembly shall consist of all the Members of the United Nations.
    2. +
    3. Each Member shall have not more than five representatives in the General Assembly.
    4. +
    + +

    FUNCTIONS AND POWERS

    + +

    Article 10

    + +

    The General Assembly may discuss any questions or any matters within the scope of the present Charter or relating to the powers and functions of any organs provided for in the present Charter, and, except as provided in Article 12, may make recommendations to the Members of the United Nations or to the Security Council or to both on any such questions or matters.

    + +

    Article 11

    + +
      +
    1. The General Assembly may consider the general principles of co-operation in the maintenance of international peace and security, including the principles governing disarmament and the regulation of armaments, and may make recommendations with regard to such principles to the Members or to the Security Council or to both.
    2. +
    3. The General Assembly may discuss any questions relating to the maintenance of international peace and security brought before it by any Member of the United Nations, or by the Security Council, or by a state which is not a Member of the United Nations in accordance with Article 35, paragraph 2, and, except as provided in Article 12, may make recommendations with regard to any such questions to the state or states concerned or to the Security Council or to both. Any such question on which action is necessary shall be referred to the Security Council by the General Assembly either before or after discussion.
    4. +
    5. The General Assembly may call the attention of the Security Council to situations which are likely to endanger international peace and security.
    6. +
    7. The powers of the General Assembly set forth in this Article shall not limit the general scope of Article 10.
    8. +
    + +

    Article 12

    + +
      +
    1. While the Security Council is exercising in respect of any dispute or situation the functions assigned to it in the present Charter, the General Assembly shall not make any recommendation with regard to that dispute or situation unless the Security Council so requests.
    2. +
    3. The Secretary-General, with the consent of the Security Council, shall notify the General Assembly at each session of any matters relative to the maintenance of international peace and security which are being dealt with by the Security Council and shall similarly notify the General Assembly, or the Members of the United Nations if the General Assembly is not in session, immediately the Security Council ceases to deal with such matters.
    4. +
    + +

    Article 13

    + +
      +
    1. The General Assembly shall initiate studies and make recommendations for the purpose of: +
        +
      1. promoting international co-operation in the political field and encouraging the progressive development of international law and its codification;
      2. +
      3. promoting international co-operation in the economic, social, cultural, educational, and health fields, and assisting in the realization of human rights and fundamental freedoms for all without distinction as to race, sex, language, or religion.
      4. +
      +
    2. +
    3. The further responsibilities, functions and powers of the General Assembly with respect to matters mentioned in paragraph 1 (b) above are set forth in Chapters IX and X.
    4. +
    + +

    Article 14

    + +

    Subject to the provisions of Article 12, the General Assembly may recommend measures for the peaceful adjustment of any situation, regardless of origin, which it deems likely to impair the general welfare or friendly relations among nations, including situations resulting from a violation of the provisions of the present Charter setting forth the Purposes and Principles of the United Nations.

    + +

    Article 15

    + +
      +
    1. The General Assembly shall receive and consider annual and special reports from the Security Council; these reports shall include an account of the measures that the Security Council has decided upon or taken to maintain international peace and security.
    2. +
    3. The General Assembly shall receive and consider reports from the other organs of the United Nations.
    4. +
    + +

    Article 16

    + +

    The General Assembly shall perform such functions with respect to the international trusteeship system as are assigned to it under Chapters XII and XIII, including the approval of the trusteeship agreements for areas not designated as strategic.

    + +

    Article 17

    + +
      +
    1. The General Assembly shall consider and approve the budget of the Organization.
    2. +
    3. The expenses of the Organization shall be borne by the Members as apportioned by the General Assembly.
    4. +
    5. The General Assembly shall consider and approve any financial and budgetary arrangements with specialized agencies referred to in Article 57 and shall examine the administrative budgets of such specialized agencies with a view to making recommendations to the agencies concerned.
    6. +
    + +

    VOTING

    + +

    Article 18

    + +
      +
    1. Each member of the General Assembly shall have one vote.
    2. +
    3. Decisions of the General Assembly on important questions shall be made by a two-thirds majority of the members present and voting. These questions shall include: recommendations with respect to the maintenance of international peace and security, the election of the non-permanent members of the Security Council, the election of the members of the Economic and Social Council, the election of members of the Trusteeship Council in accordance with paragraph 1 (c) of Article 86, the admission of new Members to the United Nations, the suspension of the rights and privileges of membership, the expulsion of Members, questions relating to the operation of the trusteeship system, and budgetary questions.
    4. +
    5. Decisions on other questions, including the determination of additional categories of questions to be decided by a two-thirds majority, shall be made by a majority of the members present and voting.
    6. +
    + +

    Article 19

    + +

    A Member of the United Nations which is in arrears in the payment of its financial contributions to the Organization shall have no vote in the General Assembly if the amount of its arrears equals or exceeds the amount of the contributions due from it for the preceding two full years. The General Assembly may, nevertheless, permit such a Member to vote if it is satisfied that the failure to pay is due to conditions beyond the control of the Member.

    + +

    PROCEDURE

    + +

    Article 20

    + +

    The General Assembly shall meet in regular annual sessions and in such special sessions as occasion may require. Special sessions shall be convoked by the Secretary-General at the request of the Security Council or of a majority of the Members of the United Nations.

    + +

    Article 21

    + +

    The General Assembly shall adopt its own rules of procedure. It shall elect its President for each session.

    + +

    Article 22

    + +

    The General Assembly may establish such subsidiary organs as it deems necessary for the performance of its functions.

    + +

    Chapter V: The Security Council

    + +

    COMPOSITION

    + +

    Article 23

    + +
      +
    1. The Security Council shall consist of fifteen Members of the United Nations. The Republic of China, France, the Union of Soviet Socialist Republics, the United Kingdom of Great Britain and Northern Ireland, and the United States of America shall be permanent members of the Security Council. The General Assembly shall elect ten other Members of the United Nations to be non-permanent members of the Security Council, due regard being specially paid, in the first instance to the contribution of Members of the United Nations to the maintenance of international peace and security and to the other purposes of the Organization, and also to equitable geographical distribution.
    2. +
    3. The non-permanent members of the Security Council shall be elected for a term of two years. In the first election of the non-permanent members after the increase of the membership of the Security Council from eleven to fifteen, two of the four additional members shall be chosen for a term of one year. A retiring member shall not be eligible for immediate re-election.
    4. +
    5. Each member of the Security Council shall have one representative.
    6. +
    + +

    FUNCTIONS AND POWERS

    + +

    Article 24

    + +
      +
    1. In order to ensure prompt and effective action by the United Nations, its Members confer on the Security Council primary responsibility for the maintenance of international peace and security, and agree that in carrying out its duties under this responsibility the Security Council acts on their behalf.
    2. +
    3. In discharging these duties the Security Council shall act in accordance with the Purposes and Principles of the United Nations. The specific powers granted to the Security Council for the discharge of these duties are laid down in Chapters VI, VII, VIII, and XII.
    4. +
    5. The Security Council shall submit annual and, when necessary, special reports to the General Assembly for its consideration.
    6. +
    + +

    Article 25

    + +

    The Members of the United Nations agree to accept and carry out the decisions of the Security Council in accordance with the present Charter.

    + +

    Article 26

    + +

    In order to promote the establishment and maintenance of international peace and security with the least diversion for armaments of the world's human and economic resources, the Security Council shall be responsible for formulating, with the assistance of the Military Staff Committee referred to in Article 47, plans to be submitted to the Members of the United Nations for the establishment of a system for the regulation of armaments.

    + +

    VOTING

    + +

    Article 27

    + +
      +
    1. Each member of the Security Council shall have one vote.
    2. +
    3. Decisions of the Security Council on procedural matters shall be made by an affirmative vote of nine members.
    4. +
    5. Decisions of the Security Council on all other matters shall be made by an affirmative vote of nine members including the concurring votes of the permanent members; provided that, in decisions under Chapter VI, and under paragraph 3 of Article 52, a party to a dispute shall abstain from voting.
    6. +
    + +

    PROCEDURE

    + +

    Article 28

    + +
      +
    1. The Security Council shall be so organized as to be able to function continuously. Each member of the Security Council shall for this purpose be represented at all times at the seat of the Organization.
    2. +
    3. The Security Council shall hold periodic meetings at which each of its members may, if it so desires, be represented by a member of the government or by some other specially designated representative.
    4. +
    5. The Security Council may hold meetings at such places other than the seat of the Organization as in its judgment will best facilitate its work.
    6. +
    + +

    Article 29

    + +

    The Security Council may establish such subsidiary organs as it deems necessary for the performance of its functions.

    + +

    Article 30

    + +

    The Security Council shall adopt its own rules of procedure, including the method of selecting its President.

    + +

    Article 31

    + +

    Any Member of the United Nations which is not a member of the Security Council may participate, without vote, in the discussion of any question brought before the Security Council whenever the latter considers that the interests of that Member are specially affected.

    + +

    Article 32

    + +

    Any Member of the United Nations which is not a member of the Security Council or any state which is not a Member of the United Nations, if it is a party to a dispute under consideration by the Security Council, shall be invited to participate, without vote, in the discussion relating to the dispute. The Security Council shall lay down such conditions as it deems just for the participation of a state which is not a Member of the United Nations.

    + +

    Chapter VI: Pacific Settlement of Disputes

    + +

    Article 33

    + +
      +
    1. The parties to any dispute, the continuance of which is likely to endanger the maintenance of international peace and security, shall, first of all, seek a solution by negotiation, enquiry, mediation, conciliation, arbitration, judicial settlement, resort to regional agencies or arrangements, or other peaceful means of their own choice.
    2. +
    3. The Security Council shall, when it deems necessary, call upon the parties to settle their dispute by such means.
    4. +
    + +

    Article 34

    + +

    The Security Council may investigate any dispute, or any situation which might lead to international friction or give rise to a dispute, in order to determine whether the continuance of the dispute or situation is likely to endanger the maintenance of international peace and security.

    + +

    Article 35

    + +
      +
    1. Any Member of the United Nations may bring any dispute, or any situation of the nature referred to in Article 34, to the attention of the Security Council or of the General Assembly.
    2. +
    3. A state which is not a Member of the United Nations may bring to the attention of the Security Council or of the General Assembly any dispute to which it is a party if it accepts in advance, for the purposes of the dispute, the obligations of pacific settlement provided in the present Charter.
    4. +
    5. The proceedings of the General Assembly in respect of matters brought to its attention under this Article will be subject to the provisions of Articles 11 and 12.
    6. +
    + +

    Article 36

    + +
      +
    1. The Security Council may, at any stage of a dispute of the nature referred to in Article 33 or of a situation of like nature, recommend appropriate procedures or methods of adjustment.
    2. +
    3. The Security Council should take into consideration any procedures for the settlement of the dispute which have already been adopted by the parties.
    4. +
    5. In making recommendations under this Article the Security Council should also take into consideration that legal disputes should as a general rule be referred by the parties to the International Court of Justice in accordance with the provisions of the Statute of the Court.
    6. +
    + +

    Article 37

    + +
      +
    1. Should the parties to a dispute of the nature referred to in Article 33 fail to settle it by the means indicated in that Article, they shall refer it to the Security Council.
    2. +
    3. If the Security Council deems that the continuance of the dispute is in fact likely to endanger the maintenance of international peace and security, it shall decide whether to take action under Article 36 or to recommend such terms of settlement as it may consider appropriate.
    4. +
    + +

    Article 38

    + +

    Without prejudice to the provisions of Articles 33 to 37, the Security Council may, if all the parties to any dispute so request, make recommendations to the parties with a view to a pacific settlement of the dispute.

    + +

    Chapter VII: Action with Respect to Threats to the Peace, Breaches of the Peace, and Acts of Aggression

    + +

    Article 39

    + +

    The Security Council shall determine the existence of any threat to the peace, breach of the peace, or act of aggression and shall make recommendations, or decide what measures shall be taken in accordance with Articles 41 and 42, to maintain or restore international peace and security.

    + +

    Article 40

    + +

    In order to prevent an aggravation of the situation, the Security Council may, before making the recommendations or deciding upon the measures provided for in Article 39, call upon the parties concerned to comply with such provisional measures as it deems necessary or desirable. Such provisional measures shall be without prejudice to the rights, claims, or position of the parties concerned. The Security Council shall duly take account of failure to comply with such provisional measures.

    + +

    Article 41

    + +

    The Security Council may decide what measures not involving the use of armed force are to be employed to give effect to its decisions, and it may call upon the Members of the United Nations to apply such measures. These may include complete or partial interruption of economic relations and of rail, sea, air, postal, telegraphic, radio, and other means of communication, and the severance of diplomatic relations.

    + +

    Article 42

    + +

    Should the Security Council consider that measures provided for in Article 41 would be inadequate or have proved to be inadequate, it may take such action by air, sea, or land forces as may be necessary to maintain or restore international peace and security. Such action may include demonstrations, blockade, and other operations by air, sea, or land forces of Members of the United Nations.

    + +

    Article 43

    + +
      +
    1. All Members of the United Nations, in order to contribute to the maintenance of international peace and security, undertake to make available to the Security Council, on its call and in accordance with a special agreement or agreements, armed forces, assistance, and facilities, including rights of passage, necessary for the purpose of maintaining international peace and security.
    2. +
    3. Such agreement or agreements shall govern the numbers and types of forces, their degree of readiness and general location, and the nature of the facilities and assistance to be provided.
    4. +
    5. The agreement or agreements shall be negotiated as soon as possible on the initiative of the Security Council. They shall be concluded between the Security Council and Members or between the Security Council and groups of Members and shall be subject to ratification by the signatory states in accordance with their respective constitutional processes.
    6. +
    + +

    Article 44

    + +

    When the Security Council has decided to use force it shall, before calling upon a Member not represented on it to provide armed forces in fulfilment of the obligations assumed under Article 43, invite that Member, if the Member so desires, to participate in the decisions of the Security Council concerning the employment of contingents of that Member's armed forces.

    + +

    Article 45

    + +

    In order to enable the United Nations to take urgent military measures, Members shall hold immediately available national air-force contingents for combined international enforcement action. The strength and degree of readiness of these contingents and plans for their combined action shall be determined within the limits laid down in the special agreement or agreements referred to in Article 43, by the Security Council with the assistance of the Military Staff Committee.

    + +

    Article 46

    + +

    Plans for the application of armed force shall be made by the Security Council with the assistance of the Military Staff Committee.

    + +

    Article 47

    + +
      +
    1. There shall be established a Military Staff Committee to advise and assist the Security Council on all questions relating to the Security Council's military requirements for the maintenance of international peace and security, the employment and command of forces placed at its disposal, the regulation of armaments, and possible disarmament.
    2. +
    3. The Military Staff Committee shall consist of the Chiefs of Staff of the permanent members of the Security Council or their representatives. Any Member of the United Nations not permanently represented on the Committee shall be invited by the Committee to be associated with it when the efficient discharge of the Committee's responsibilities requires the participation of that Member in its work.
    4. +
    5. The Military Staff Committee shall be responsible under the Security Council for the strategic direction of any armed forces placed at the disposal of the Security Council. Questions relating to the command of such forces shall be worked out subsequently.
    6. +
    7. The Military Staff Committee, with the authorization of the Security Council and after consultation with appropriate regional agencies, may establish regional sub-committees.
    8. +
    + +

    Article 48

    + +
      +
    1. The action required to carry out the decisions of the Security Council for the maintenance of international peace and security shall be taken by all the Members of the United Nations or by some of them, as the Security Council may determine.
    2. +
    3. Such decisions shall be carried out by the Members of the United Nations directly and through their action in the appropriate international agencies of which they are members.
    4. +
    + +

    Article 49

    + +

    The Members of the United Nations shall join in affording mutual assistance in carrying out the measures decided upon by the Security Council.

    + +

    Article 50

    + +

    If preventive or enforcement measures against any state are taken by the Security Council, any other state, whether a Member of the United Nations or not, which finds itself confronted with special economic problems arising from the carrying out of those measures shall have the right to consult the Security Council with regard to a solution of those problems.

    + +

    Article 51

    + +

    Nothing in the present Charter shall impair the inherent right of individual or collective self-defence if an armed attack occurs against a Member of the United Nations, until the Security Council has taken measures necessary to maintain international peace and security. Measures taken by Members in the exercise of this right of self-defence shall be immediately reported to the Security Council and shall not in any way affect the authority and responsibility of the Security Council under the present Charter to take at any time such action as it deems necessary in order to maintain or restore international peace and security.

    + +

    Chapter VIII: Regional Arrangements

    + +

    Article 52

    + +
      +
    1. Nothing in the present Charter precludes the existence of regional arrangements or agencies for dealing with such matters relating to the maintenance of international peace and security as are appropriate for regional action provided that such arrangements or agencies and their activities are consistent with the Purposes and Principles of the United Nations.
    2. +
    3. The Members of the United Nations entering into such arrangements or constituting such agencies shall make every effort to achieve pacific settlement of local disputes through such regional arrangements or by such regional agencies before referring them to the Security Council.
    4. +
    5. The Security Council shall encourage the development of pacific settlement of local disputes through such regional arrangements or by such regional agencies either on the initiative of the states concerned or by reference from the Security Council.
    6. +
    7. This Article in no way impairs the application of Articles 34 and 35.
    8. +
    + +

    Article 53

    + +
      +
    1. The Security Council shall, where appropriate, utilize such regional arrangements or agencies for enforcement action under its authority. But no enforcement action shall be taken under regional arrangements or by regional agencies without the authorization of the Security Council, with the exception of measures against any enemy state, as defined in paragraph 2 of this Article, provided for pursuant to Article 107 or in regional arrangements directed against renewal of aggressive policy on the part of any such state, until such time as the Organization may, on request of the Governments concerned, be charged with the responsibility for preventing further aggression by such a state.
    2. +
    3. The term enemy state as used in paragraph 1 of this Article applies to any state which during the Second World War has been an enemy of any signatory of the present Charter.
    4. +
    + +

    Article 54

    + +

    The Security Council shall at all times be kept fully informed of activities undertaken or in contemplation under regional arrangements or by regional agencies for the maintenance of international peace and security.

    + +

    Chapter IX: International Economic and Social Cooperation

    + +

    Article 55

    + +

    With a view to the creation of conditions of stability and well-being which are necessary for peaceful and friendly relations among nations based on respect for the principle of equal rights and self-determination of peoples, the United Nations shall promote:

    + +
      +
    1. higher standards of living, full employment, and conditions of economic and social progress and development;
    2. +
    3. solutions of international economic, social, health, and related problems; and international cultural and educational cooperation; and
    4. +
    5. universal respect for, and observance of, human rights and fundamental freedoms for all without distinction as to race, sex, language, or religion.
    6. +
    + +

    Article 56

    + +

    All Members pledge themselves to take joint and separate action in co-operation with the Organization for the achievement of the purposes set forth in Article 55.

    + +

    Article 57

    + +
      +
    1. The various specialized agencies, established by intergovernmental agreement and having wide international responsibilities, as defined in their basic instruments, in economic, social, cultural, educational, health, and related fields, shall be brought into relationship with the United Nations in accordance with the provisions of Article 63.
    2. +
    3. Such agencies thus brought into relationship with the United Nations are hereinafter referred to as specialized agencies.
    4. +
    + +

    Article 58

    + +

    The Organization shall make recommendations for the co-ordination of the policies and activities of the specialized agencies.

    + +

    Article 59

    + +

    The Organization shall, where appropriate, initiate negotiations among the states concerned for the creation of any new specialized agencies required for the accomplishment of the purposes set forth in Article 55.

    + +

    Article 60

    + +

    Responsibility for the discharge of the functions of the Organization set forth in this Chapter shall be vested in the General Assembly and, under the authority of the General Assembly, in the Economic and Social Council, which shall have for this purpose the powers set forth in Chapter X.

    + +

    Chapter X: The Economic and Social Council

    + +

    COMPOSITION

    + +

    Article 61

    + +
      +
    1. The Economic and Social Council shall consist of fifty-four Members of the United Nations elected by the General Assembly.
    2. +
    3. Subject to the provisions of paragraph 3, eighteen members of the Economic and Social Council shall be elected each year for a term of three years. A retiring member shall be eligible for immediate re-election.
    4. +
    5. At the first election after the increase in the membership of the Economic and Social Council from twenty-seven to fifty-four members, in addition to the members elected in place of the nine members whose term of office expires at the end of that year, twenty-seven additional members shall be elected. Of these twenty-seven additional members, the term of office of nine members so elected shall expire at the end of one year, and of nine other members at the end of two years, in accordance with arrangements made by the General Assembly.
    6. +
    7. Each member of the Economic and Social Council shall have one representative.
    8. +
    + +

    FUNCTIONS AND POWERS

    + +

    Article 62

    + +
      +
    1. The Economic and Social Council may make or initiate studies and reports with respect to international economic, social, cultural, educational, health, and related matters and may make recommendations with respect to any such matters to the General Assembly to the Members of the United Nations, and to the specialized agencies concerned.
    2. +
    3. It may make recommendations for the purpose of promoting respect for, and observance of, human rights and fundamental freedoms for all.
    4. +
    5. It may prepare draft conventions for submission to the General Assembly, with respect to matters falling within its competence.
    6. +
    7. It may call, in accordance with the rules prescribed by the United Nations, international conferences on matters falling within its competence.
    8. +
    + +

    Article 63

    + +
      +
    1. The Economic and Social Council may enter into agreements with any of the agencies referred to in Article 57, defining the terms on which the agency concerned shall be brought into relationship with the United Nations. Such agreements shall be subject to approval by the General Assembly.
    2. +
    3. It may co-ordinate the activities of the specialized agencies through consultation with and recommendations to such agencies and through recommendations to the General Assembly and to the Members of the United Nations.
    4. +
    + +

    Article 64

    + +
      +
    1. The Economic and Social Council may take appropriate steps to obtain regular reports from the specialized agencies. It may make arrangements with the Members of the United Nations and with the specialized agencies to obtain reports on the steps taken to give effect to its own recommendations and to recommendations on matters falling within its competence made by the General Assembly.
    2. +
    3. It may communicate its observations on these reports to the General Assembly.
    4. +
    + +

    Article 65

    + +

    The Economic and Social Council may furnish information to the Security Council and shall assist the Security Council upon its request.

    + +

    Article 66

    + +
      +
    1. The Economic and Social Council shall perform such functions as fall within its competence in connection with the carrying out of the recommendations of the General Assembly.
    2. +
    3. It may, with the approval of the General Assembly, perform services at the request of Members of the United Nations and at the request of specialized agencies.
    4. +
    5. It shall perform such other functions as are specified elsewhere in the present Charter or as may be assigned to it by the General Assembly.
    6. +
    + +

    VOTING

    + +

    Article 67

    + +
      +
    1. Each member of the Economic and Social Council shall have one vote.
    2. +
    3. Decisions of the Economic and Social Council shall be made by a majority of the members present and voting.
    4. +
    + +

    PROCEDURE

    + +

    Article 68

    + +

    The Economic and Social Council shall set up commissions in economic and social fields and for the promotion of human rights, and such other commissions as may be required for the performance of its functions.

    + +

    Article 69

    + +

    The Economic and Social Council shall invite any Member of the United Nations to participate, without vote, in its deliberations on any matter of particular concern to that Member.

    + +

    Article 70

    + +

    The Economic and Social Council may make arrangements for representatives of the specialized agencies to participate, without vote, in its deliberations and in those of the commissions established by it, and for its representatives to participate in the deliberations of the specialized agencies.

    + +

    Article 71

    + +

    The Economic and Social Council may make suitable arrangements for consultation with non-governmental organizations which are concerned with matters within its competence. Such arrangements may be made with international organizations and, where appropriate, with national organizations after consultation with the Member of the United Nations concerned.

    + +

    Article 72

    + +
      +
    1. The Economic and Social Council shall adopt its own rules of procedure, including the method of selecting its President.
    2. +
    3. The Economic and Social Council shall meet as required in accordance with its rules, which shall include provision for the convening of meetings on the request of a majority of its members.
    4. +
    + +

    Chapter XI: Declaration Regarding Non-Self-Governing Territories

    + +

    Article 73

    + +

    Members of the United Nations which have or assume responsibilities for the administration of territories whose peoples have not yet attained a full measure of self-government recognize the principle that the interests of the inhabitants of these territories are paramount, and accept as a sacred trust the obligation to promote to the utmost, within the system of international peace and security established by the present Charter, the well-being of the inhabitants of these territories, and, to this end:

    + +
      +
    1. to ensure, with due respect for the culture of the peoples concerned, their political, economic, social, and educational advancement, their just treatment, and their protection against abuses;
    2. +
    3. to develop self-government, to take due account of the political aspirations of the peoples, and to assist them in the progressive development of their free political institutions, according to the particular circumstances of each territory and its peoples and their varying stages of advancement;
    4. +
    5. to further international peace and security;
    6. +
    7. to promote constructive measures of development, to encourage research, and to co-operate with one another and, when and where appropriate, with specialized international bodies with a view to the practical achievement of the social, economic, and scientific purposes set forth in this Article; and
    8. +
    9. to transmit regularly to the Secretary-General for information purposes, subject to such limitation as security and constitutional considerations may require, statistical and other information of a technical nature relating to economic, social, and educational conditions in the territories for which they are respectively responsible other than those territories to which Chapters XII and XIII apply.
    10. +
    + +

    Article 74

    + +

    Members of the United Nations also agree that their policy in respect of the territories to which this Chapter applies, no less than in respect of their metropolitan areas, must be based on the general principle of good-neighbourliness, due account being taken of the interests and well-being of the rest of the world, in social, economic, and commercial matters.

    + +

    Chapter XII: International Trusteeship System

    + +

    Article 75

    + +

    The United Nations shall establish under its authority an international trusteeship system for the administration and supervision of such territories as may be placed thereunder by subsequent individual agreements. These territories are hereinafter referred to as trust territories.

    + +

    Article 76

    + +

    The basic objectives of the trusteeship system, in accordance with the Purposes of the United Nations laid down in Article 1 of the present Charter, shall be:

    + +
      +
    1. to further international peace and security;
    2. +
    3. to promote the political, economic, social, and educational advancement of the inhabitants of the trust territories, and their progressive development towards self-government or independence as may be appropriate to the particular circumstances of each territory and its peoples and the freely expressed wishes of the peoples concerned, and as may be provided by the terms of each trusteeship agreement;
    4. +
    5. to encourage respect for human rights and for fundamental freedoms for all without distinction as to race, sex, language, or religion, and to encourage recognition of the interdependence of the peoples of the world; and
    6. +
    7. to ensure equal treatment in social, economic, and commercial matters for all Members of the United Nations and their nationals, and also equal treatment for the latter in the administration of justice, without prejudice to the attainment of the foregoing objectives and subject to the provisions of Article 80.
    8. +
    + +

    Article 77

    + +
      +
    1. The trusteeship system shall apply to such territories in the following categories as may be placed thereunder by means of trusteeship agreements: +
        +
      1. territories now held under mandate;
      2. +
      3. territories which may be detached from enemy states as a result of the Second World War; and
      4. +
      5. territories voluntarily placed under the system by states responsible for their administration.
      6. +
      +
    2. +
    3. It will be a matter for subsequent agreement as to which territories in the foregoing categories will be brought under the trusteeship system and upon what terms.
    4. +
    + +

    Article 78

    + +

    The trusteeship system shall not apply to territories which have become Members of the United Nations, relationship among which shall be based on respect for the principle of sovereign equality.

    + +

    Article 79

    + +

    The terms of trusteeship for each territory to be placed under the trusteeship system, including any alteration or amendment, shall be agreed upon by the states directly concerned, including the mandatory power in the case of territories held under mandate by a Member of the United Nations, and shall be approved as provided for in Articles 83 and 85.

    + +

    Article 80

    + +
      +
    1. Except as may be agreed upon in individual trusteeship agreements, made under Articles 77, 79, and 81, placing each territory under the trusteeship system, and until such agreements have been concluded, nothing in this Chapter shall be construed in or of itself to alter in any manner the rights whatsoever of any states or any peoples or the terms of existing international instruments to which Members of the United Nations may respectively be parties.
    2. +
    3. Paragraph 1 of this Article shall not be interpreted as giving grounds for delay or postponement of the negotiation and conclusion of agreements for placing mandated and other territories under the trusteeship system as provided for in Article 77.
    4. +
    + +

    Article 81

    + +

    The trusteeship agreement shall in each case include the terms under which the trust territory will be administered and designate the authority which will exercise the administration of the trust territory. Such authority, hereinafter called the administering authority, may be one or more states or the Organization itself.

    + +

    Article 82

    + +

    There may be designated, in any trusteeship agreement, a strategic area or areas which may include part or all of the trust territory to which the agreement applies, without prejudice to any special agreement or agreements made under Article 43.

    + +

    Article 83

    + +
      +
    1. All functions of the United Nations relating to strategic areas, including the approval of the terms of the trusteeship agreements and of their alteration or amendment shall be exercised by the Security Council.
    2. +
    3. The basic objectives set forth in Article 76 shall be applicable to the people of each strategic area.
    4. +
    5. The Security Council shall, subject to the provisions of the trusteeship agreements and without prejudice to security considerations, avail itself of the assistance of the Trusteeship Council to perform those functions of the United Nations under the trusteeship system relating to political, economic, social, and educational matters in the strategic areas.
    6. +
    + +

    Article 84

    + +

    It shall be the duty of the administering authority to ensure that the trust territory shall play its part in the maintenance of international peace and security. To this end the administering authority may make use of volunteer forces, facilities, and assistance from the trust territory in carrying out the obligations towards the Security Council undertaken in this regard by the administering authority, as well as for local defence and the maintenance of law and order within the trust territory.

    + +

    Article 85

    + +
      +
    1. The functions of the United Nations with regard to trusteeship agreements for all areas not designated as strategic, including the approval of the terms of the trusteeship agreements and of their alteration or amendment, shall be exercised by the General Assembly.
    2. +
    3. The Trusteeship Council, operating under the authority of the General Assembly shall assist the General Assembly in carrying out these functions.
    4. +
    + +

    Chapter XIII: The Trusteeship Council

    + +

    COMPOSITION

    + +

    Article 86

    + +
      +
    1. The Trusteeship Council shall consist of the following Members of the United Nations: +
        +
      1. those Members administering trust territories;
      2. +
      3. such of those Members mentioned by name in Article 23 as are not administering trust territories; and
      4. +
      5. as many other Members elected for three-year terms by the General Assembly as may be necessary to ensure that the total number of members of the Trusteeship Council is equally divided between those Members of the United Nations which administer trust territories and those which do not.
      6. +
      +
    2. +
    3. Each member of the Trusteeship Council shall designate one specially qualified person to represent it therein.
    4. +
    + +

    FUNCTIONS AND POWERS

    + +

    Article 87

    + +

    The General Assembly and, under its authority, the Trusteeship Council, in carrying out their functions, may:

    + +
      +
    1. consider reports submitted by the administering authority;
    2. +
    3. accept petitions and examine them in consultation with the administering authority;
    4. +
    5. provide for periodic visits to the respective trust territories at times agreed upon with the administering authority; and
    6. +
    7. take these and other actions in conformity with the terms of the trusteeship agreements.
    8. +
    + +

    Article 88

    + +

    The Trusteeship Council shall formulate a questionnaire on the political, economic, social, and educational advancement of the inhabitants of each trust territory, and the administering authority for each trust territory within the competence of the General Assembly shall make an annual report to the General Assembly upon the basis of such questionnaire.

    + +

    VOTING

    + +

    Article 89

    + +
      +
    1. Each member of the Trusteeship Council shall have one vote.
    2. +
    3. Decisions of the Trusteeship Council shall be made by a majority of the members present and voting.
    4. +
    + +

    PROCEDURE

    + +

    Article 90

    + +
      +
    1. The Trusteeship Council shall adopt its own rules of procedure, including the method of selecting its President.
    2. +
    3. The Trusteeship Council shall meet as required in accordance with its rules, which shall include provision for the convening of meetings on the request of a majority of its members.
    4. +
    + +

    Article 91

    + +

    The Trusteeship Council shall, when appropriate, avail itself of the assistance of the Economic and Social Council and of the specialized agencies in regard to matters with which they are respectively concerned.

    + +

    Chapter XIV: The International Court of Justice

    + +

    Article 92

    + +

    The International Court of Justice shall be the principal judicial organ of the United Nations. It shall function in accordance with the annexed Statute, which is based upon the Statute of the Permanent Court of International Justice and forms an integral part of the present Charter.

    + +

    Article 93

    + +
      +
    1. All Members of the United Nations are ipso facto parties to the Statute of the International Court of Justice.
    2. +
    3. A state which is not a Member of the United Nations may become a party to the Statute of the International Court of Justice on conditions to be determined in each case by the General Assembly upon the recommendation of the Security Council.
    4. +
    + +

    Article 94

    + +
      +
    1. Each Member of the United Nations undertakes to comply with the decision of the International Court of Justice in any case to which it is a party.
    2. +
    3. If any party to a case fails to perform the obligations incumbent upon it under a judgment rendered by the Court, the other party may have recourse to the Security Council, which may, if it deems necessary, make recommendations or decide upon measures to be taken to give effect to the judgment.
    4. +
    + +

    Article 95

    + +

    Nothing in the present Charter shall prevent Members of the United Nations from entrusting the solution of their differences to other tribunals by virtue of agreements already in existence or which may be concluded in the future.

    + +

    Article 96

    + +
      +
    1. The General Assembly or the Security Council may request the International Court of Justice to give an advisory opinion on any legal question.
    2. +
    3. Other organs of the United Nations and specialized agencies, which may at any time be so authorized by the General Assembly, may also request advisory opinions of the Court on legal questions arising within the scope of their activities.
    4. +
    + +

    Chapter XV: The Secretariat

    + +

    Article 97

    + +

    The Secretariat shall comprise a Secretary-General and such staff as the Organization may require. The Secretary-General shall be appointed by the General Assembly upon the recommendation of the Security Council. He shall be the chief administrative officer of the Organization.

    + +

    Article 98

    + +

    The Secretary-General shall act in that capacity in all meetings of the General Assembly, of the Security Council, of the Economic and Social Council, and of the Trusteeship Council, and shall perform such other functions as are entrusted to him by these organs. The Secretary-General shall make an annual report to the General Assembly on the work of the Organization.

    + +

    Article 99

    + +

    The Secretary-General may bring to the attention of the Security Council any matter which in his opinion may threaten the maintenance of international peace and security.

    + +

    Article 100

    + +
      +
    1. In the performance of their duties the Secretary-General and the staff shall not seek or receive instructions from any government or from any other authority external to the Organization. They shall refrain from any action which might reflect on their position as international officials responsible only to the Organization.
    2. +
    3. Each Member of the United Nations undertakes to respect the exclusively international character of the responsibilities of the Secretary-General and the staff and not to seek to influence them in the discharge of their responsibilities.
    4. +
    + +

    Article 101

    + +
      +
    1. The staff shall be appointed by the Secretary-General under regulations established by the General Assembly.
    2. +
    3. Appropriate staffs shall be permanently assigned to the Economic and Social Council, the Trusteeship Council, and, as required, to other organs of the United Nations. These staffs shall form a part of the Secretariat.
    4. +
    5. The paramount consideration in the employment of the staff and in the determination of the conditions of service shall be the necessity of securing the highest standards of efficiency, competence, and integrity. Due regard shall be paid to the importance of recruiting the staff on as wide a geographical basis as possible.
    6. +
    + +

    Chapter XVI: Miscellaneous Provisions

    + +

    Article 102

    + +
      +
    1. Every treaty and every international agreement entered into by any Member of the United Nations after the present Charter comes into force shall as soon as possible be registered with the Secretariat and published by it.
    2. +
    3. No party to any such treaty or international agreement which has not been registered in accordance with the provisions of paragraph 1 of this Article may invoke that treaty or agreement before any organ of the United Nations.
    4. +
    + +

    Article 103

    + +

    In the event of a conflict between the obligations of the Members of the United Nations under the present Charter and their obligations under any other international agreement, their obligations under the present Charter shall prevail.

    + +

    Article 104

    + +

    The Organization shall enjoy in the territory of each of its Members such legal capacity as may be necessary for the exercise of its functions and the fulfilment of its purposes.

    + +

    Article 105

    + +
      +
    1. The Organization shall enjoy in the territory of each of its Members such privileges and immunities as are necessary for the fulfilment of its purposes.
    2. +
    3. Representatives of the Members of the United Nations and officials of the Organization shall similarly enjoy such privileges and immunities as are necessary for the independent exercise of their functions in connection with the Organization.
    4. +
    5. The General Assembly may make recommendations with a view to determining the details of the application of paragraphs 1 and 2 of this Article or may propose conventions to the Members of the United Nations for this purpose.
    6. +
    + +

    Chapter XVII: Transitional Security Arrangements

    + +

    Article 106

    + +

    Pending the coming into force of such special agreements referred to in Article 43 as in the opinion of the Security Council enable it to begin the exercise of its responsibilities under Article 42, the parties to the Four-Nation Declaration, signed at Moscow, 30 October 1943, and France, shall, in accordance with the provisions of paragraph 5 of that Declaration, consult with one another and as occasion requires with other Members of the United Nations with a view to such joint action on behalf of the Organization as may be necessary for the purpose of maintaining international peace and security.

    + +

    Article 107

    + +

    Nothing in the present Charter shall invalidate or preclude action, in relation to any state which during the Second World War has been an enemy of any signatory to the present Charter, taken or authorized as a result of that war by the Governments having responsibility for such action.

    + +

    Chapter XVIII: Amendments

    + +

    Article 108

    + +

    Amendments to the present Charter shall come into force for all Members of the United Nations when they have been adopted by a vote of two thirds of the members of the General Assembly and ratified in accordance with their respective constitutional processes by two thirds of the Members of the United Nations, including all the permanent members of the Security Council.

    + +

    Article 109

    + +
      +
    1. A General Conference of the Members of the United Nations for the purpose of reviewing the present Charter may be held at a date and place to be fixed by a two-thirds vote of the members of the General Assembly and by a vote of any nine members of the Security Council. Each Member of the United Nations shall have one vote in the conference.
    2. +
    3. Any alteration of the present Charter recommended by a two-thirds vote of the conference shall take effect when ratified in accordance with their respective constitutional processes by two thirds of the Members of the United Nations including all the permanent members of the Security Council.
    4. +
    5. If such a conference has not been held before the tenth annual session of the General Assembly following the coming into force of the present Charter, the proposal to call such a conference shall be placed on the agenda of that session of the General Assembly, and the conference shall be held if so decided by a majority vote of the members of the General Assembly and by a vote of any seven members of the Security Council.
    6. +
    + +

    Chapter XIX: Ratification and Signature

    + +

    Article 110

    + +
      +
    1. The present Charter shall be ratified by the signatory states in accordance with their respective constitutional processes.
    2. +
    3. The ratifications shall be deposited with the Government of the United States of America, which shall notify all the signatory states of each deposit as well as the Secretary-General of the Organization when he has been appointed.
    4. +
    5. The present Charter shall come into force upon the deposit of ratifications by the Republic of China, France, the Union of Soviet Socialist Republics, the United Kingdom of Great Britain and Northern Ireland, and the United States of America, and by a majority of the other signatory states. A protocol of the ratifications deposited shall thereupon be drawn up by the Government of the United States of America which shall communicate copies thereof to all the signatory states.
    6. +
    7. The states signatory to the present Charter which ratify it after it has come into force will become original Members of the United Nations on the date of the deposit of their respective ratifications.
    8. +
    + +

    Article 111

    + +

    The present Charter, of which the Chinese, French, Russian, English, and Spanish texts are equally authentic, shall remain deposited in the archives of the Government of the United States of America. Duly certified copies thereof shall be transmitted by that Government to the Governments of the other signatory states.

    + +

    In Faith Whereof the representatives of the Governments of the United Nations have signed the present Charter. DONE at the city of San Francisco the twenty-sixth day of June, one thousand nine hundred and forty-five.

    + +


    + Note on Amendments to Articles 23, 27, 61, 109

    + +

    Amendments to Articles 23, 27 and 61 of the Charter were adopted by the General Assembly on 17 December 1963 and came into force on 31 August 1965. A further amendment to Article 61 was adopted by the General Assembly on 20 December 1971, and came into force on 24 September 1973. An amendment to Article 109, adopted by the General Assembly on 20 December 1965, came into force on 12 June 1968.

    + +

    The amendment to Article 23 enlarges the membership of the Security Council from eleven to fifteen. The amended Article 27 provides that decisions of the Security Council on procedural matters shall be made by an affirmative vote of nine members (formerly seven) and on all other matters by an affirmative vote of nine members (formerly seven), including the concurring votes of the five permanent members of the Security Council.

    + +

    The amendment to Article 61, which entered into force on 31 August 1965, enlarged the membership of the Economic and Social Council from eighteen to twenty-seven. The subsequent amendment to that Article, which entered into force on 24 September 1973, further increased the membership of the Council from twenty-seven to fifty-four.

    + +

    The amendment to Article 109, which relates to the first paragraph of that Article, provides that a General Conference of Member States for the purpose of reviewing the Charter may be held at a date and place to be fixed by a two-thirds vote of the members of the General Assembly and by a vote of any nine members (formerly seven) of the Security Council. Paragraph 3 of Article 109, which deals with the consideration of a possible review conference during the tenth regular session of the General Assembly, has been retained in its original form in its reference to a "vote, of any seven members of the Security Council", the paragraph having been acted upon in 1955 by the General Assembly, at its tenth regular session, and by the Security Council.

    +
    +
    +
    + + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + + + +
    +
    + + +
    + + + + + + + + + + + +
    + + + + + + + + + +
    \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_EN.txt b/stdlib/benchmarks/collections/data/UN_charter_EN.txt new file mode 100644 index 0000000000..db3e0f7cc1 --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_EN.txt @@ -0,0 +1,463 @@ +United Nations Charter (full text) +Preamble +WE THE PEOPLES OF THE UNITED NATIONS DETERMINED +to save succeeding generations from the scourge of war, which twice in our lifetime has brought untold sorrow to mankind, and + +to reaffirm faith in fundamental human rights, in the dignity and worth of the human person, in the equal rights of men and women and of nations large and small, and + +to establish conditions under which justice and respect for the obligations arising from treaties and other sources of international law can be maintained, and + +to promote social progress and better standards of life in larger freedom, + +AND FOR THESE ENDS +to practice tolerance and live together in peace with one another as good neighbours, and + +to unite our strength to maintain international peace and security, and + +to ensure, by the acceptance of principles and the institution of methods, that armed force shall not be used, save in the common interest, and + +to employ international machinery for the promotion of the economic and social advancement of all peoples, + +HAVE RESOLVED TO COMBINE OUR EFFORTS TO ACCOMPLISH THESE AIMS. +Accordingly, our respective Governments, through representatives assembled in the city of San Francisco, who have exhibited their full powers found to be in good and due form, have agreed to the present Charter of the United Nations and do hereby establish an international organization to be known as the United Nations. + +Chapter I: Purposes and Principles +Article 1 +The Purposes of the United Nations are: + +To maintain international peace and security, and to that end: to take effective collective measures for the prevention and removal of threats to the peace, and for the suppression of acts of aggression or other breaches of the peace, and to bring about by peaceful means, and in conformity with the principles of justice and international law, adjustment or settlement of international disputes or situations which might lead to a breach of the peace; +To develop friendly relations among nations based on respect for the principle of equal rights and self-determination of peoples, and to take other appropriate measures to strengthen universal peace; +To achieve international co-operation in solving international problems of an economic, social, cultural, or humanitarian character, and in promoting and encouraging respect for human rights and for fundamental freedoms for all without distinction as to race, sex, language, or religion; and +To be a centre for harmonizing the actions of nations in the attainment of these common ends. +Article 2 +The Organization and its Members, in pursuit of the Purposes stated in Article 1, shall act in accordance with the following Principles. + +The Organization is based on the principle of the sovereign equality of all its Members. +All Members, in order to ensure to all of them the rights and benefits resulting from membership, shall fulfill in good faith the obligations assumed by them in accordance with the present Charter. +All Members shall settle their international disputes by peaceful means in such a manner that international peace and security, and justice, are not endangered. +All Members shall refrain in their international relations from the threat or use of force against the territorial integrity or political independence of any state, or in any other manner inconsistent with the Purposes of the United Nations. +All Members shall give the United Nations every assistance in any action it takes in accordance with the present Charter, and shall refrain from giving assistance to any state against which the United Nations is taking preventive or enforcement action. +The Organization shall ensure that states which are not Members of the United Nations act in accordance with these Principles so far as may be necessary for the maintenance of international peace and security. +Nothing contained in the present Charter shall authorize the United Nations to intervene in matters which are essentially within the domestic jurisdiction of any state or shall require the Members to submit such matters to settlement under the present Charter; but this principle shall not prejudice the application of enforcement measures under Chapter Vll. +Chapter II: Membership +Article 3 +The original Members of the United Nations shall be the states which, having participated in the United Nations Conference on International Organization at San Francisco, or having previously signed the Declaration by United Nations of 1 January 1942, sign the present Charter and ratify it in accordance with Article 110. + +Article 4 +Membership in the United Nations is open to all other peace-loving states which accept the obligations contained in the present Charter and, in the judgment of the Organization, are able and willing to carry out these obligations. +The admission of any such state to membership in the United Nations will be effected by a decision of the General Assembly upon the recommendation of the Security Council. +Article 5 +A Member of the United Nations against which preventive or enforcement action has been taken by the Security Council may be suspended from the exercise of the rights and privileges of membership by the General Assembly upon the recommendation of the Security Council. The exercise of these rights and privileges may be restored by the Security Council. + +Article 6 +A Member of the United Nations which has persistently violated the Principles contained in the present Charter may be expelled from the Organization by the General Assembly upon the recommendation of the Security Council. + +Chapter III: Organs +Article 7 +There are established as principal organs of the United Nations: a General Assembly, a Security Council, an Economic and Social Council, a Trusteeship Council, an International Court of Justice and a Secretariat. +Such subsidiary organs as may be found necessary may be established in accordance with the present Charter. +Article 8 +The United Nations shall place no restrictions on the eligibility of men and women to participate in any capacity and under conditions of equality in its principal and subsidiary organs. + +Chapter IV: The General Assembly +COMPOSITION +Article 9 +The General Assembly shall consist of all the Members of the United Nations. +Each Member shall have not more than five representatives in the General Assembly. +FUNCTIONS AND POWERS +Article 10 +The General Assembly may discuss any questions or any matters within the scope of the present Charter or relating to the powers and functions of any organs provided for in the present Charter, and, except as provided in Article 12, may make recommendations to the Members of the United Nations or to the Security Council or to both on any such questions or matters. + +Article 11 +The General Assembly may consider the general principles of co-operation in the maintenance of international peace and security, including the principles governing disarmament and the regulation of armaments, and may make recommendations with regard to such principles to the Members or to the Security Council or to both. +The General Assembly may discuss any questions relating to the maintenance of international peace and security brought before it by any Member of the United Nations, or by the Security Council, or by a state which is not a Member of the United Nations in accordance with Article 35, paragraph 2, and, except as provided in Article 12, may make recommendations with regard to any such questions to the state or states concerned or to the Security Council or to both. Any such question on which action is necessary shall be referred to the Security Council by the General Assembly either before or after discussion. +The General Assembly may call the attention of the Security Council to situations which are likely to endanger international peace and security. +The powers of the General Assembly set forth in this Article shall not limit the general scope of Article 10. +Article 12 +While the Security Council is exercising in respect of any dispute or situation the functions assigned to it in the present Charter, the General Assembly shall not make any recommendation with regard to that dispute or situation unless the Security Council so requests. +The Secretary-General, with the consent of the Security Council, shall notify the General Assembly at each session of any matters relative to the maintenance of international peace and security which are being dealt with by the Security Council and shall similarly notify the General Assembly, or the Members of the United Nations if the General Assembly is not in session, immediately the Security Council ceases to deal with such matters. +Article 13 +The General Assembly shall initiate studies and make recommendations for the purpose of: +promoting international co-operation in the political field and encouraging the progressive development of international law and its codification; +promoting international co-operation in the economic, social, cultural, educational, and health fields, and assisting in the realization of human rights and fundamental freedoms for all without distinction as to race, sex, language, or religion. +The further responsibilities, functions and powers of the General Assembly with respect to matters mentioned in paragraph 1 (b) above are set forth in Chapters IX and X. +Article 14 +Subject to the provisions of Article 12, the General Assembly may recommend measures for the peaceful adjustment of any situation, regardless of origin, which it deems likely to impair the general welfare or friendly relations among nations, including situations resulting from a violation of the provisions of the present Charter setting forth the Purposes and Principles of the United Nations. + +Article 15 +The General Assembly shall receive and consider annual and special reports from the Security Council; these reports shall include an account of the measures that the Security Council has decided upon or taken to maintain international peace and security. +The General Assembly shall receive and consider reports from the other organs of the United Nations. +Article 16 +The General Assembly shall perform such functions with respect to the international trusteeship system as are assigned to it under Chapters XII and XIII, including the approval of the trusteeship agreements for areas not designated as strategic. + +Article 17 +The General Assembly shall consider and approve the budget of the Organization. +The expenses of the Organization shall be borne by the Members as apportioned by the General Assembly. +The General Assembly shall consider and approve any financial and budgetary arrangements with specialized agencies referred to in Article 57 and shall examine the administrative budgets of such specialized agencies with a view to making recommendations to the agencies concerned. +VOTING +Article 18 +Each member of the General Assembly shall have one vote. +Decisions of the General Assembly on important questions shall be made by a two-thirds majority of the members present and voting. These questions shall include: recommendations with respect to the maintenance of international peace and security, the election of the non-permanent members of the Security Council, the election of the members of the Economic and Social Council, the election of members of the Trusteeship Council in accordance with paragraph 1 (c) of Article 86, the admission of new Members to the United Nations, the suspension of the rights and privileges of membership, the expulsion of Members, questions relating to the operation of the trusteeship system, and budgetary questions. +Decisions on other questions, including the determination of additional categories of questions to be decided by a two-thirds majority, shall be made by a majority of the members present and voting. +Article 19 +A Member of the United Nations which is in arrears in the payment of its financial contributions to the Organization shall have no vote in the General Assembly if the amount of its arrears equals or exceeds the amount of the contributions due from it for the preceding two full years. The General Assembly may, nevertheless, permit such a Member to vote if it is satisfied that the failure to pay is due to conditions beyond the control of the Member. + +PROCEDURE +Article 20 +The General Assembly shall meet in regular annual sessions and in such special sessions as occasion may require. Special sessions shall be convoked by the Secretary-General at the request of the Security Council or of a majority of the Members of the United Nations. + +Article 21 +The General Assembly shall adopt its own rules of procedure. It shall elect its President for each session. + +Article 22 +The General Assembly may establish such subsidiary organs as it deems necessary for the performance of its functions. + +Chapter V: The Security Council +COMPOSITION +Article 23 +The Security Council shall consist of fifteen Members of the United Nations. The Republic of China, France, the Union of Soviet Socialist Republics, the United Kingdom of Great Britain and Northern Ireland, and the United States of America shall be permanent members of the Security Council. The General Assembly shall elect ten other Members of the United Nations to be non-permanent members of the Security Council, due regard being specially paid, in the first instance to the contribution of Members of the United Nations to the maintenance of international peace and security and to the other purposes of the Organization, and also to equitable geographical distribution. +The non-permanent members of the Security Council shall be elected for a term of two years. In the first election of the non-permanent members after the increase of the membership of the Security Council from eleven to fifteen, two of the four additional members shall be chosen for a term of one year. A retiring member shall not be eligible for immediate re-election. +Each member of the Security Council shall have one representative. +FUNCTIONS AND POWERS +Article 24 +In order to ensure prompt and effective action by the United Nations, its Members confer on the Security Council primary responsibility for the maintenance of international peace and security, and agree that in carrying out its duties under this responsibility the Security Council acts on their behalf. +In discharging these duties the Security Council shall act in accordance with the Purposes and Principles of the United Nations. The specific powers granted to the Security Council for the discharge of these duties are laid down in Chapters VI, VII, VIII, and XII. +The Security Council shall submit annual and, when necessary, special reports to the General Assembly for its consideration. +Article 25 +The Members of the United Nations agree to accept and carry out the decisions of the Security Council in accordance with the present Charter. + +Article 26 +In order to promote the establishment and maintenance of international peace and security with the least diversion for armaments of the world's human and economic resources, the Security Council shall be responsible for formulating, with the assistance of the Military Staff Committee referred to in Article 47, plans to be submitted to the Members of the United Nations for the establishment of a system for the regulation of armaments. + +VOTING +Article 27 +Each member of the Security Council shall have one vote. +Decisions of the Security Council on procedural matters shall be made by an affirmative vote of nine members. +Decisions of the Security Council on all other matters shall be made by an affirmative vote of nine members including the concurring votes of the permanent members; provided that, in decisions under Chapter VI, and under paragraph 3 of Article 52, a party to a dispute shall abstain from voting. +PROCEDURE +Article 28 +The Security Council shall be so organized as to be able to function continuously. Each member of the Security Council shall for this purpose be represented at all times at the seat of the Organization. +The Security Council shall hold periodic meetings at which each of its members may, if it so desires, be represented by a member of the government or by some other specially designated representative. +The Security Council may hold meetings at such places other than the seat of the Organization as in its judgment will best facilitate its work. +Article 29 +The Security Council may establish such subsidiary organs as it deems necessary for the performance of its functions. + +Article 30 +The Security Council shall adopt its own rules of procedure, including the method of selecting its President. + +Article 31 +Any Member of the United Nations which is not a member of the Security Council may participate, without vote, in the discussion of any question brought before the Security Council whenever the latter considers that the interests of that Member are specially affected. + +Article 32 +Any Member of the United Nations which is not a member of the Security Council or any state which is not a Member of the United Nations, if it is a party to a dispute under consideration by the Security Council, shall be invited to participate, without vote, in the discussion relating to the dispute. The Security Council shall lay down such conditions as it deems just for the participation of a state which is not a Member of the United Nations. + +Chapter VI: Pacific Settlement of Disputes +Article 33 +The parties to any dispute, the continuance of which is likely to endanger the maintenance of international peace and security, shall, first of all, seek a solution by negotiation, enquiry, mediation, conciliation, arbitration, judicial settlement, resort to regional agencies or arrangements, or other peaceful means of their own choice. +The Security Council shall, when it deems necessary, call upon the parties to settle their dispute by such means. +Article 34 +The Security Council may investigate any dispute, or any situation which might lead to international friction or give rise to a dispute, in order to determine whether the continuance of the dispute or situation is likely to endanger the maintenance of international peace and security. + +Article 35 +Any Member of the United Nations may bring any dispute, or any situation of the nature referred to in Article 34, to the attention of the Security Council or of the General Assembly. +A state which is not a Member of the United Nations may bring to the attention of the Security Council or of the General Assembly any dispute to which it is a party if it accepts in advance, for the purposes of the dispute, the obligations of pacific settlement provided in the present Charter. +The proceedings of the General Assembly in respect of matters brought to its attention under this Article will be subject to the provisions of Articles 11 and 12. +Article 36 +The Security Council may, at any stage of a dispute of the nature referred to in Article 33 or of a situation of like nature, recommend appropriate procedures or methods of adjustment. +The Security Council should take into consideration any procedures for the settlement of the dispute which have already been adopted by the parties. +In making recommendations under this Article the Security Council should also take into consideration that legal disputes should as a general rule be referred by the parties to the International Court of Justice in accordance with the provisions of the Statute of the Court. +Article 37 +Should the parties to a dispute of the nature referred to in Article 33 fail to settle it by the means indicated in that Article, they shall refer it to the Security Council. +If the Security Council deems that the continuance of the dispute is in fact likely to endanger the maintenance of international peace and security, it shall decide whether to take action under Article 36 or to recommend such terms of settlement as it may consider appropriate. +Article 38 +Without prejudice to the provisions of Articles 33 to 37, the Security Council may, if all the parties to any dispute so request, make recommendations to the parties with a view to a pacific settlement of the dispute. + +Chapter VII: Action with Respect to Threats to the Peace, Breaches of the Peace, and Acts of Aggression +Article 39 +The Security Council shall determine the existence of any threat to the peace, breach of the peace, or act of aggression and shall make recommendations, or decide what measures shall be taken in accordance with Articles 41 and 42, to maintain or restore international peace and security. + +Article 40 +In order to prevent an aggravation of the situation, the Security Council may, before making the recommendations or deciding upon the measures provided for in Article 39, call upon the parties concerned to comply with such provisional measures as it deems necessary or desirable. Such provisional measures shall be without prejudice to the rights, claims, or position of the parties concerned. The Security Council shall duly take account of failure to comply with such provisional measures. + +Article 41 +The Security Council may decide what measures not involving the use of armed force are to be employed to give effect to its decisions, and it may call upon the Members of the United Nations to apply such measures. These may include complete or partial interruption of economic relations and of rail, sea, air, postal, telegraphic, radio, and other means of communication, and the severance of diplomatic relations. + +Article 42 +Should the Security Council consider that measures provided for in Article 41 would be inadequate or have proved to be inadequate, it may take such action by air, sea, or land forces as may be necessary to maintain or restore international peace and security. Such action may include demonstrations, blockade, and other operations by air, sea, or land forces of Members of the United Nations. + +Article 43 +All Members of the United Nations, in order to contribute to the maintenance of international peace and security, undertake to make available to the Security Council, on its call and in accordance with a special agreement or agreements, armed forces, assistance, and facilities, including rights of passage, necessary for the purpose of maintaining international peace and security. +Such agreement or agreements shall govern the numbers and types of forces, their degree of readiness and general location, and the nature of the facilities and assistance to be provided. +The agreement or agreements shall be negotiated as soon as possible on the initiative of the Security Council. They shall be concluded between the Security Council and Members or between the Security Council and groups of Members and shall be subject to ratification by the signatory states in accordance with their respective constitutional processes. +Article 44 +When the Security Council has decided to use force it shall, before calling upon a Member not represented on it to provide armed forces in fulfilment of the obligations assumed under Article 43, invite that Member, if the Member so desires, to participate in the decisions of the Security Council concerning the employment of contingents of that Member's armed forces. + +Article 45 +In order to enable the United Nations to take urgent military measures, Members shall hold immediately available national air-force contingents for combined international enforcement action. The strength and degree of readiness of these contingents and plans for their combined action shall be determined within the limits laid down in the special agreement or agreements referred to in Article 43, by the Security Council with the assistance of the Military Staff Committee. + +Article 46 +Plans for the application of armed force shall be made by the Security Council with the assistance of the Military Staff Committee. + +Article 47 +There shall be established a Military Staff Committee to advise and assist the Security Council on all questions relating to the Security Council's military requirements for the maintenance of international peace and security, the employment and command of forces placed at its disposal, the regulation of armaments, and possible disarmament. +The Military Staff Committee shall consist of the Chiefs of Staff of the permanent members of the Security Council or their representatives. Any Member of the United Nations not permanently represented on the Committee shall be invited by the Committee to be associated with it when the efficient discharge of the Committee's responsibilities requires the participation of that Member in its work. +The Military Staff Committee shall be responsible under the Security Council for the strategic direction of any armed forces placed at the disposal of the Security Council. Questions relating to the command of such forces shall be worked out subsequently. +The Military Staff Committee, with the authorization of the Security Council and after consultation with appropriate regional agencies, may establish regional sub-committees. +Article 48 +The action required to carry out the decisions of the Security Council for the maintenance of international peace and security shall be taken by all the Members of the United Nations or by some of them, as the Security Council may determine. +Such decisions shall be carried out by the Members of the United Nations directly and through their action in the appropriate international agencies of which they are members. +Article 49 +The Members of the United Nations shall join in affording mutual assistance in carrying out the measures decided upon by the Security Council. + +Article 50 +If preventive or enforcement measures against any state are taken by the Security Council, any other state, whether a Member of the United Nations or not, which finds itself confronted with special economic problems arising from the carrying out of those measures shall have the right to consult the Security Council with regard to a solution of those problems. + +Article 51 +Nothing in the present Charter shall impair the inherent right of individual or collective self-defence if an armed attack occurs against a Member of the United Nations, until the Security Council has taken measures necessary to maintain international peace and security. Measures taken by Members in the exercise of this right of self-defence shall be immediately reported to the Security Council and shall not in any way affect the authority and responsibility of the Security Council under the present Charter to take at any time such action as it deems necessary in order to maintain or restore international peace and security. + +Chapter VIII: Regional Arrangements +Article 52 +Nothing in the present Charter precludes the existence of regional arrangements or agencies for dealing with such matters relating to the maintenance of international peace and security as are appropriate for regional action provided that such arrangements or agencies and their activities are consistent with the Purposes and Principles of the United Nations. +The Members of the United Nations entering into such arrangements or constituting such agencies shall make every effort to achieve pacific settlement of local disputes through such regional arrangements or by such regional agencies before referring them to the Security Council. +The Security Council shall encourage the development of pacific settlement of local disputes through such regional arrangements or by such regional agencies either on the initiative of the states concerned or by reference from the Security Council. +This Article in no way impairs the application of Articles 34 and 35. +Article 53 +The Security Council shall, where appropriate, utilize such regional arrangements or agencies for enforcement action under its authority. But no enforcement action shall be taken under regional arrangements or by regional agencies without the authorization of the Security Council, with the exception of measures against any enemy state, as defined in paragraph 2 of this Article, provided for pursuant to Article 107 or in regional arrangements directed against renewal of aggressive policy on the part of any such state, until such time as the Organization may, on request of the Governments concerned, be charged with the responsibility for preventing further aggression by such a state. +The term enemy state as used in paragraph 1 of this Article applies to any state which during the Second World War has been an enemy of any signatory of the present Charter. +Article 54 +The Security Council shall at all times be kept fully informed of activities undertaken or in contemplation under regional arrangements or by regional agencies for the maintenance of international peace and security. + +Chapter IX: International Economic and Social Cooperation +Article 55 +With a view to the creation of conditions of stability and well-being which are necessary for peaceful and friendly relations among nations based on respect for the principle of equal rights and self-determination of peoples, the United Nations shall promote: + +higher standards of living, full employment, and conditions of economic and social progress and development; +solutions of international economic, social, health, and related problems; and international cultural and educational cooperation; and +universal respect for, and observance of, human rights and fundamental freedoms for all without distinction as to race, sex, language, or religion. +Article 56 +All Members pledge themselves to take joint and separate action in co-operation with the Organization for the achievement of the purposes set forth in Article 55. + +Article 57 +The various specialized agencies, established by intergovernmental agreement and having wide international responsibilities, as defined in their basic instruments, in economic, social, cultural, educational, health, and related fields, shall be brought into relationship with the United Nations in accordance with the provisions of Article 63. +Such agencies thus brought into relationship with the United Nations are hereinafter referred to as specialized agencies. +Article 58 +The Organization shall make recommendations for the co-ordination of the policies and activities of the specialized agencies. + +Article 59 +The Organization shall, where appropriate, initiate negotiations among the states concerned for the creation of any new specialized agencies required for the accomplishment of the purposes set forth in Article 55. + +Article 60 +Responsibility for the discharge of the functions of the Organization set forth in this Chapter shall be vested in the General Assembly and, under the authority of the General Assembly, in the Economic and Social Council, which shall have for this purpose the powers set forth in Chapter X. + +Chapter X: The Economic and Social Council +COMPOSITION +Article 61 +The Economic and Social Council shall consist of fifty-four Members of the United Nations elected by the General Assembly. +Subject to the provisions of paragraph 3, eighteen members of the Economic and Social Council shall be elected each year for a term of three years. A retiring member shall be eligible for immediate re-election. +At the first election after the increase in the membership of the Economic and Social Council from twenty-seven to fifty-four members, in addition to the members elected in place of the nine members whose term of office expires at the end of that year, twenty-seven additional members shall be elected. Of these twenty-seven additional members, the term of office of nine members so elected shall expire at the end of one year, and of nine other members at the end of two years, in accordance with arrangements made by the General Assembly. +Each member of the Economic and Social Council shall have one representative. +FUNCTIONS AND POWERS +Article 62 +The Economic and Social Council may make or initiate studies and reports with respect to international economic, social, cultural, educational, health, and related matters and may make recommendations with respect to any such matters to the General Assembly to the Members of the United Nations, and to the specialized agencies concerned. +It may make recommendations for the purpose of promoting respect for, and observance of, human rights and fundamental freedoms for all. +It may prepare draft conventions for submission to the General Assembly, with respect to matters falling within its competence. +It may call, in accordance with the rules prescribed by the United Nations, international conferences on matters falling within its competence. +Article 63 +The Economic and Social Council may enter into agreements with any of the agencies referred to in Article 57, defining the terms on which the agency concerned shall be brought into relationship with the United Nations. Such agreements shall be subject to approval by the General Assembly. +It may co-ordinate the activities of the specialized agencies through consultation with and recommendations to such agencies and through recommendations to the General Assembly and to the Members of the United Nations. +Article 64 +The Economic and Social Council may take appropriate steps to obtain regular reports from the specialized agencies. It may make arrangements with the Members of the United Nations and with the specialized agencies to obtain reports on the steps taken to give effect to its own recommendations and to recommendations on matters falling within its competence made by the General Assembly. +It may communicate its observations on these reports to the General Assembly. +Article 65 +The Economic and Social Council may furnish information to the Security Council and shall assist the Security Council upon its request. + +Article 66 +The Economic and Social Council shall perform such functions as fall within its competence in connection with the carrying out of the recommendations of the General Assembly. +It may, with the approval of the General Assembly, perform services at the request of Members of the United Nations and at the request of specialized agencies. +It shall perform such other functions as are specified elsewhere in the present Charter or as may be assigned to it by the General Assembly. +VOTING +Article 67 +Each member of the Economic and Social Council shall have one vote. +Decisions of the Economic and Social Council shall be made by a majority of the members present and voting. +PROCEDURE +Article 68 +The Economic and Social Council shall set up commissions in economic and social fields and for the promotion of human rights, and such other commissions as may be required for the performance of its functions. + +Article 69 +The Economic and Social Council shall invite any Member of the United Nations to participate, without vote, in its deliberations on any matter of particular concern to that Member. + +Article 70 +The Economic and Social Council may make arrangements for representatives of the specialized agencies to participate, without vote, in its deliberations and in those of the commissions established by it, and for its representatives to participate in the deliberations of the specialized agencies. + +Article 71 +The Economic and Social Council may make suitable arrangements for consultation with non-governmental organizations which are concerned with matters within its competence. Such arrangements may be made with international organizations and, where appropriate, with national organizations after consultation with the Member of the United Nations concerned. + +Article 72 +The Economic and Social Council shall adopt its own rules of procedure, including the method of selecting its President. +The Economic and Social Council shall meet as required in accordance with its rules, which shall include provision for the convening of meetings on the request of a majority of its members. +Chapter XI: Declaration Regarding Non-Self-Governing Territories +Article 73 +Members of the United Nations which have or assume responsibilities for the administration of territories whose peoples have not yet attained a full measure of self-government recognize the principle that the interests of the inhabitants of these territories are paramount, and accept as a sacred trust the obligation to promote to the utmost, within the system of international peace and security established by the present Charter, the well-being of the inhabitants of these territories, and, to this end: + +to ensure, with due respect for the culture of the peoples concerned, their political, economic, social, and educational advancement, their just treatment, and their protection against abuses; +to develop self-government, to take due account of the political aspirations of the peoples, and to assist them in the progressive development of their free political institutions, according to the particular circumstances of each territory and its peoples and their varying stages of advancement; +to further international peace and security; +to promote constructive measures of development, to encourage research, and to co-operate with one another and, when and where appropriate, with specialized international bodies with a view to the practical achievement of the social, economic, and scientific purposes set forth in this Article; and +to transmit regularly to the Secretary-General for information purposes, subject to such limitation as security and constitutional considerations may require, statistical and other information of a technical nature relating to economic, social, and educational conditions in the territories for which they are respectively responsible other than those territories to which Chapters XII and XIII apply. +Article 74 +Members of the United Nations also agree that their policy in respect of the territories to which this Chapter applies, no less than in respect of their metropolitan areas, must be based on the general principle of good-neighbourliness, due account being taken of the interests and well-being of the rest of the world, in social, economic, and commercial matters. + +Chapter XII: International Trusteeship System +Article 75 +The United Nations shall establish under its authority an international trusteeship system for the administration and supervision of such territories as may be placed thereunder by subsequent individual agreements. These territories are hereinafter referred to as trust territories. + +Article 76 +The basic objectives of the trusteeship system, in accordance with the Purposes of the United Nations laid down in Article 1 of the present Charter, shall be: + +to further international peace and security; +to promote the political, economic, social, and educational advancement of the inhabitants of the trust territories, and their progressive development towards self-government or independence as may be appropriate to the particular circumstances of each territory and its peoples and the freely expressed wishes of the peoples concerned, and as may be provided by the terms of each trusteeship agreement; +to encourage respect for human rights and for fundamental freedoms for all without distinction as to race, sex, language, or religion, and to encourage recognition of the interdependence of the peoples of the world; and +to ensure equal treatment in social, economic, and commercial matters for all Members of the United Nations and their nationals, and also equal treatment for the latter in the administration of justice, without prejudice to the attainment of the foregoing objectives and subject to the provisions of Article 80. +Article 77 +The trusteeship system shall apply to such territories in the following categories as may be placed thereunder by means of trusteeship agreements: +territories now held under mandate; +territories which may be detached from enemy states as a result of the Second World War; and +territories voluntarily placed under the system by states responsible for their administration. +It will be a matter for subsequent agreement as to which territories in the foregoing categories will be brought under the trusteeship system and upon what terms. +Article 78 +The trusteeship system shall not apply to territories which have become Members of the United Nations, relationship among which shall be based on respect for the principle of sovereign equality. + +Article 79 +The terms of trusteeship for each territory to be placed under the trusteeship system, including any alteration or amendment, shall be agreed upon by the states directly concerned, including the mandatory power in the case of territories held under mandate by a Member of the United Nations, and shall be approved as provided for in Articles 83 and 85. + +Article 80 +Except as may be agreed upon in individual trusteeship agreements, made under Articles 77, 79, and 81, placing each territory under the trusteeship system, and until such agreements have been concluded, nothing in this Chapter shall be construed in or of itself to alter in any manner the rights whatsoever of any states or any peoples or the terms of existing international instruments to which Members of the United Nations may respectively be parties. +Paragraph 1 of this Article shall not be interpreted as giving grounds for delay or postponement of the negotiation and conclusion of agreements for placing mandated and other territories under the trusteeship system as provided for in Article 77. +Article 81 +The trusteeship agreement shall in each case include the terms under which the trust territory will be administered and designate the authority which will exercise the administration of the trust territory. Such authority, hereinafter called the administering authority, may be one or more states or the Organization itself. + +Article 82 +There may be designated, in any trusteeship agreement, a strategic area or areas which may include part or all of the trust territory to which the agreement applies, without prejudice to any special agreement or agreements made under Article 43. + +Article 83 +All functions of the United Nations relating to strategic areas, including the approval of the terms of the trusteeship agreements and of their alteration or amendment shall be exercised by the Security Council. +The basic objectives set forth in Article 76 shall be applicable to the people of each strategic area. +The Security Council shall, subject to the provisions of the trusteeship agreements and without prejudice to security considerations, avail itself of the assistance of the Trusteeship Council to perform those functions of the United Nations under the trusteeship system relating to political, economic, social, and educational matters in the strategic areas. +Article 84 +It shall be the duty of the administering authority to ensure that the trust territory shall play its part in the maintenance of international peace and security. To this end the administering authority may make use of volunteer forces, facilities, and assistance from the trust territory in carrying out the obligations towards the Security Council undertaken in this regard by the administering authority, as well as for local defence and the maintenance of law and order within the trust territory. + +Article 85 +The functions of the United Nations with regard to trusteeship agreements for all areas not designated as strategic, including the approval of the terms of the trusteeship agreements and of their alteration or amendment, shall be exercised by the General Assembly. +The Trusteeship Council, operating under the authority of the General Assembly shall assist the General Assembly in carrying out these functions. +Chapter XIII: The Trusteeship Council +COMPOSITION +Article 86 +The Trusteeship Council shall consist of the following Members of the United Nations: +those Members administering trust territories; +such of those Members mentioned by name in Article 23 as are not administering trust territories; and +as many other Members elected for three-year terms by the General Assembly as may be necessary to ensure that the total number of members of the Trusteeship Council is equally divided between those Members of the United Nations which administer trust territories and those which do not. +Each member of the Trusteeship Council shall designate one specially qualified person to represent it therein. +FUNCTIONS AND POWERS +Article 87 +The General Assembly and, under its authority, the Trusteeship Council, in carrying out their functions, may: + +consider reports submitted by the administering authority; +accept petitions and examine them in consultation with the administering authority; +provide for periodic visits to the respective trust territories at times agreed upon with the administering authority; and +take these and other actions in conformity with the terms of the trusteeship agreements. +Article 88 +The Trusteeship Council shall formulate a questionnaire on the political, economic, social, and educational advancement of the inhabitants of each trust territory, and the administering authority for each trust territory within the competence of the General Assembly shall make an annual report to the General Assembly upon the basis of such questionnaire. + +VOTING +Article 89 +Each member of the Trusteeship Council shall have one vote. +Decisions of the Trusteeship Council shall be made by a majority of the members present and voting. +PROCEDURE +Article 90 +The Trusteeship Council shall adopt its own rules of procedure, including the method of selecting its President. +The Trusteeship Council shall meet as required in accordance with its rules, which shall include provision for the convening of meetings on the request of a majority of its members. +Article 91 +The Trusteeship Council shall, when appropriate, avail itself of the assistance of the Economic and Social Council and of the specialized agencies in regard to matters with which they are respectively concerned. + +Chapter XIV: The International Court of Justice +Article 92 +The International Court of Justice shall be the principal judicial organ of the United Nations. It shall function in accordance with the annexed Statute, which is based upon the Statute of the Permanent Court of International Justice and forms an integral part of the present Charter. + +Article 93 +All Members of the United Nations are ipso facto parties to the Statute of the International Court of Justice. +A state which is not a Member of the United Nations may become a party to the Statute of the International Court of Justice on conditions to be determined in each case by the General Assembly upon the recommendation of the Security Council. +Article 94 +Each Member of the United Nations undertakes to comply with the decision of the International Court of Justice in any case to which it is a party. +If any party to a case fails to perform the obligations incumbent upon it under a judgment rendered by the Court, the other party may have recourse to the Security Council, which may, if it deems necessary, make recommendations or decide upon measures to be taken to give effect to the judgment. +Article 95 +Nothing in the present Charter shall prevent Members of the United Nations from entrusting the solution of their differences to other tribunals by virtue of agreements already in existence or which may be concluded in the future. + +Article 96 +The General Assembly or the Security Council may request the International Court of Justice to give an advisory opinion on any legal question. +Other organs of the United Nations and specialized agencies, which may at any time be so authorized by the General Assembly, may also request advisory opinions of the Court on legal questions arising within the scope of their activities. +Chapter XV: The Secretariat +Article 97 +The Secretariat shall comprise a Secretary-General and such staff as the Organization may require. The Secretary-General shall be appointed by the General Assembly upon the recommendation of the Security Council. He shall be the chief administrative officer of the Organization. + +Article 98 +The Secretary-General shall act in that capacity in all meetings of the General Assembly, of the Security Council, of the Economic and Social Council, and of the Trusteeship Council, and shall perform such other functions as are entrusted to him by these organs. The Secretary-General shall make an annual report to the General Assembly on the work of the Organization. + +Article 99 +The Secretary-General may bring to the attention of the Security Council any matter which in his opinion may threaten the maintenance of international peace and security. + +Article 100 +In the performance of their duties the Secretary-General and the staff shall not seek or receive instructions from any government or from any other authority external to the Organization. They shall refrain from any action which might reflect on their position as international officials responsible only to the Organization. +Each Member of the United Nations undertakes to respect the exclusively international character of the responsibilities of the Secretary-General and the staff and not to seek to influence them in the discharge of their responsibilities. +Article 101 +The staff shall be appointed by the Secretary-General under regulations established by the General Assembly. +Appropriate staffs shall be permanently assigned to the Economic and Social Council, the Trusteeship Council, and, as required, to other organs of the United Nations. These staffs shall form a part of the Secretariat. +The paramount consideration in the employment of the staff and in the determination of the conditions of service shall be the necessity of securing the highest standards of efficiency, competence, and integrity. Due regard shall be paid to the importance of recruiting the staff on as wide a geographical basis as possible. +Chapter XVI: Miscellaneous Provisions +Article 102 +Every treaty and every international agreement entered into by any Member of the United Nations after the present Charter comes into force shall as soon as possible be registered with the Secretariat and published by it. +No party to any such treaty or international agreement which has not been registered in accordance with the provisions of paragraph 1 of this Article may invoke that treaty or agreement before any organ of the United Nations. +Article 103 +In the event of a conflict between the obligations of the Members of the United Nations under the present Charter and their obligations under any other international agreement, their obligations under the present Charter shall prevail. + +Article 104 +The Organization shall enjoy in the territory of each of its Members such legal capacity as may be necessary for the exercise of its functions and the fulfilment of its purposes. + +Article 105 +The Organization shall enjoy in the territory of each of its Members such privileges and immunities as are necessary for the fulfilment of its purposes. +Representatives of the Members of the United Nations and officials of the Organization shall similarly enjoy such privileges and immunities as are necessary for the independent exercise of their functions in connection with the Organization. +The General Assembly may make recommendations with a view to determining the details of the application of paragraphs 1 and 2 of this Article or may propose conventions to the Members of the United Nations for this purpose. +Chapter XVII: Transitional Security Arrangements +Article 106 +Pending the coming into force of such special agreements referred to in Article 43 as in the opinion of the Security Council enable it to begin the exercise of its responsibilities under Article 42, the parties to the Four-Nation Declaration, signed at Moscow, 30 October 1943, and France, shall, in accordance with the provisions of paragraph 5 of that Declaration, consult with one another and as occasion requires with other Members of the United Nations with a view to such joint action on behalf of the Organization as may be necessary for the purpose of maintaining international peace and security. + +Article 107 +Nothing in the present Charter shall invalidate or preclude action, in relation to any state which during the Second World War has been an enemy of any signatory to the present Charter, taken or authorized as a result of that war by the Governments having responsibility for such action. + +Chapter XVIII: Amendments +Article 108 +Amendments to the present Charter shall come into force for all Members of the United Nations when they have been adopted by a vote of two thirds of the members of the General Assembly and ratified in accordance with their respective constitutional processes by two thirds of the Members of the United Nations, including all the permanent members of the Security Council. + +Article 109 +A General Conference of the Members of the United Nations for the purpose of reviewing the present Charter may be held at a date and place to be fixed by a two-thirds vote of the members of the General Assembly and by a vote of any nine members of the Security Council. Each Member of the United Nations shall have one vote in the conference. +Any alteration of the present Charter recommended by a two-thirds vote of the conference shall take effect when ratified in accordance with their respective constitutional processes by two thirds of the Members of the United Nations including all the permanent members of the Security Council. +If such a conference has not been held before the tenth annual session of the General Assembly following the coming into force of the present Charter, the proposal to call such a conference shall be placed on the agenda of that session of the General Assembly, and the conference shall be held if so decided by a majority vote of the members of the General Assembly and by a vote of any seven members of the Security Council. +Chapter XIX: Ratification and Signature +Article 110 +The present Charter shall be ratified by the signatory states in accordance with their respective constitutional processes. +The ratifications shall be deposited with the Government of the United States of America, which shall notify all the signatory states of each deposit as well as the Secretary-General of the Organization when he has been appointed. +The present Charter shall come into force upon the deposit of ratifications by the Republic of China, France, the Union of Soviet Socialist Republics, the United Kingdom of Great Britain and Northern Ireland, and the United States of America, and by a majority of the other signatory states. A protocol of the ratifications deposited shall thereupon be drawn up by the Government of the United States of America which shall communicate copies thereof to all the signatory states. +The states signatory to the present Charter which ratify it after it has come into force will become original Members of the United Nations on the date of the deposit of their respective ratifications. +Article 111 +The present Charter, of which the Chinese, French, Russian, English, and Spanish texts are equally authentic, shall remain deposited in the archives of the Government of the United States of America. Duly certified copies thereof shall be transmitted by that Government to the Governments of the other signatory states. + +In Faith Whereof the representatives of the Governments of the United Nations have signed the present Charter. DONE at the city of San Francisco the twenty-sixth day of June, one thousand nine hundred and forty-five. + + +Note on Amendments to Articles 23, 27, 61, 109 +Amendments to Articles 23, 27 and 61 of the Charter were adopted by the General Assembly on 17 December 1963 and came into force on 31 August 1965. A further amendment to Article 61 was adopted by the General Assembly on 20 December 1971, and came into force on 24 September 1973. An amendment to Article 109, adopted by the General Assembly on 20 December 1965, came into force on 12 June 1968. + +The amendment to Article 23 enlarges the membership of the Security Council from eleven to fifteen. The amended Article 27 provides that decisions of the Security Council on procedural matters shall be made by an affirmative vote of nine members (formerly seven) and on all other matters by an affirmative vote of nine members (formerly seven), including the concurring votes of the five permanent members of the Security Council. + +The amendment to Article 61, which entered into force on 31 August 1965, enlarged the membership of the Economic and Social Council from eighteen to twenty-seven. The subsequent amendment to that Article, which entered into force on 24 September 1973, further increased the membership of the Council from twenty-seven to fifty-four. + +The amendment to Article 109, which relates to the first paragraph of that Article, provides that a General Conference of Member States for the purpose of reviewing the Charter may be held at a date and place to be fixed by a two-thirds vote of the members of the General Assembly and by a vote of any nine members (formerly seven) of the Security Council. Paragraph 3 of Article 109, which deals with the consideration of a possible review conference during the tenth regular session of the General Assembly, has been retained in its original form in its reference to a "vote, of any seven members of the Security Council", the paragraph having been acted upon in 1955 by the General Assembly, at its tenth regular session, and by the Security Council. \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_ES.html b/stdlib/benchmarks/collections/data/UN_charter_ES.html new file mode 100644 index 0000000000..ad5cdb939c --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_ES.html @@ -0,0 +1,2309 @@ + + + + + + + + + + + + + + + + + + + Carta de las Naciones Unidas (texto completo) | Naciones Unidas + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    + + + + +
    +
    + + +
    + + + + + + + + + + +
    +
    +
    + +
    + + + +

    Carta de las Naciones Unidas (texto completo)

    + + + + +
    +
    + + +
    + + + + +
    +
    + +
    +
    +

    Preámbulo

    + +

    NOSOSTROS LOS PUEBLOS DE LAS NACIONES UNIDAS RESUELTOS

    + +

    a preservar a las generaciones venideras del flagelo de la guerra que dos veces durante nuestra vida ha infligido a la Humanidad sufrimientos indecibles,

    + +

    a reafirmar la fe en los derechos fundamentales del hombre, en la dignidad y el valor de la persona humana, en la igualdad de derechos de hombres y mujeres y de las naciones grandes y pequeñas,

    + +

    a crear condiciones bajo las cuales puedan mantenerse la justicia y el respeto a las obligaciones emanadas de los tratados y de otras fuentes del derecho internacional,

    + +

    a promover el progreso social y a elevar el nivel de vida dentro de un concepto más amplio de la libertad,

    + +

    Y CON TALES FINALIDADES

    + +

    a practicar la tolerancia y a convivir en paz como buenos vecinos,

    + +

    a unir nuestras fuerzas para el mantenimiento de la paz y la seguridad internacionales,

    + +

    a asegurar, mediante la aceptación de principios y la adopción de métodos, que no se usará; la fuerza armada sino en servicio del interés común, y

    + +

    a emplear un mecanismo internacional para promover el progreso económico y social de todos los pueblos,

    + +

    HEMOS DECIDIDO UNIR NUESTROS ESFUERZOS PARA REALIZAR DESIGNIOS.

    + +

    Por lo tanto, nuestros respectivos Gobiernos, por medio de representantes reunidos en la ciudad de San Francisco que han exhibido sus plenos poderes, encontrados en buena y debida forma, han convenido en la presente Carta de las Naciones Unidas, y por este acto establecen una organización internacional que se denominará las Naciones Unidas.

    + +

    Capítulo I: Propósitos y principios

    + +

    Artículo 1

    + +

    Los propósitos de las Naciones Unidas son:

    + +
      +
    1. Mantener la paz y la seguridad internacionales, y con tal fin: tomar medidas colectivas eficaces para prevenir y eliminar amenazas a la paz, y para suprimir actos de agresión u otros quebrantamientos de la paz; y lograr por medios pacíficos, y de conformidad con los principios de la justicia y del derecho internacional, el ajuste o arreglo de controversias o situaciones internacionales susceptibles de conducir a quebrantamientos de la paz;
    2. +
    3. Fomentar entre las naciones relaciones de amistad basadas en el respeto al principio de la igualdad de derechos y al de la libre determinación de los pueblos, y tomar otros medidas adecuadas para fortalecer la paz universal;
    4. +
    5. Realizar la cooperación internacional en la solución de problemas internacionales de carácter económico, social, cultural o humanitario, y en el desarrollo y estímulo del respeto a los derechos humanos y a las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión; y
    6. +
    7. Servir de centro que armonice los esfuerzos de las naciones por alcanzar estos propósitos comunes.
    8. +
    + +

    Artículo 2

    + +

    Para la realización de los Propósitos consignados en el Artículo 1, la Organización y sus Miembros procederán de acuerdo con los siguientes Principios:

    + +
      +
    1. La Organización esta basada en el principio de la igualdad soberana de todos sus Miembros.
    2. +
    3. Los Miembros de la Organización, a fin de asegurarse los derechos y beneficios inherentes a su condición de tales, cumplirán de buena fe las obligaciones contraidas por ellos de conformidad con esta Carta.
    4. +
    5. Los Miembros de la Organización arreglarán sus controversias internacionales por medios pacificos de tal manera que no se pongan en peligro ni la paz y la seguridad internacionales ni la justicia.
    6. +
    7. Los Miembros de la Organización, en sus relaciones internacionales, se abstendrán de recurrir a la amenaza o al uso de la fuerza contra la integridad territorial o la independencia política de cualquier Estado, o en cualquier otra forma incompatible con los Propósitos de las Naciones Unidas.
    8. +
    9. Los Miembros de la Organización prestaron a ésta toda clase de ayuda en cualquier acción que ejerza de conformidad con esta Carta, y se abstendrán de dar ayuda a Estado alguno contra el cual la Organización estuviere ejerciendo acción preventiva o coercitiva.
    10. +
    11. La Organización hará que los Estados que no son Miembros de las Naciones Unidas se conduzcan de acuerdo con estos Principios en la medida que sea necesaria para mantener la paz y la seguridad internacionales.
    12. +
    13. Ninguna disposición de esta Carta autorizará a las Naciones Unidas a intervenir en los asuntos que son esencialmente de la jurisdicción interna de los Estados, ni obligará; a los Miembros a someter dichos asuntos a procedimientos de arreglo conforme a la presente Carta; pero este principio no se opone a la aplicación de las medidas coercitivas prescritas en el Capítulo VII.
    14. +
    + +

    Capítulo II: Miembros

    + +

    Artículo 3

    + +

    Son Miembros originarios de las Naciones Unidas los Estados que habiendo participado en la Conferencia de las Naciones Unidas sobre Organización Internacional celebrada en San Francisco, o que habiendo firmado previamente la Declaración de las Naciones Unidas de 1 de enero de 1942, suscriban esta Carta y la ratifiquen de conformidad con el Artículo 110.

    + +

    Artículo 4

    + +
      +
    1. Podrán ser Miembros de las Naciones Unidas todos los demás Estados amantes de la paz que acepten las obligaciones consignadas en esta Carta, y que, a juicio de la Organización, estén capacitados para cumplir dichas obligaciones y se hallen dispuestos a hacerlo.
    2. +
    3. La admisión de tales Estados como Miembros de las Naciones Unidas se efectuará por decisión de la Asamblea General a recomendación del Consejo de Seguridad.
    4. +
    + +

    Artículo 5

    + +

    Todo Miembro de las Naciones Unidas que haya sido objeto de acción preventiva o coercitiva por parte del Consejo de Seguridad podrá ser suspendido por la Asamblea General, a recomendación del Consejo de Seguridad, del ejercicio de los derechos y privilegios inherentes a su calidad de Miembro. El ejercicio de tales derechos y privilegios podrá ser restituido por el Consejo de Seguridad.

    + +

    Artículo 6

    + +

    Todo Miembro de las Naciones Unidas que haya violado repetidamente los Principios contenidos en esta Carta podrá ser expulsado de la Organización por la Asamblea General a recomendación del Consejo de Seguridad.

    + +

    Capítulo III: Órganos

    + +

    Artículo 7

    + +
      +
    1. Se establecen como órganos principales de las Naciones Unidas: una Asamblea General, un Consejo de Seguridad, un Consejo Económico y Social, un Consejo de Administración Fiduciaria, una Corte Internacional de Justicia, una Secretaría.
    2. +
    3. Se podrán establecer, de acuerdo con las disposiciones de la presente Carta, los órganos subsidiarios que se estimen necesarios.
    4. +
    + +

    Artículo 8

    + +

    La Organización no establecerá restricciones en cuanto a la elegibilidad de hombres y mujeres para participar en condiciones de igualdad y en cualquier caracter en las funciones de sus órganos principales y subsidiarios.

    + +

    Capítulo IV: La Asamblea General

    + +

    COMPOSICIÓN

    + +

    Artículo 9

    + +
      +
    1. La Asamblea General estará integrada por todos los Miembros de las Naciones Unidas.
    2. +
    3. Ningun Miembro podrá tener más de cinco representantes en la Asamblea General.
    4. +
    + +

    FUNCIONES y PODERES

    + +

    Artículo 10

    + +

    La Asamblea General podrá discutir cualesquier asuntos o cuestiones dentro de los límites de esta Carta o que se refieran a los poderes y funciones de cualquiera de los órganos creados por esta Carta, y salvo lo dispuesto en el Artículo 12 podrá hacer recomendaciones sobre tales asuntos o cuestiones a los Miembros de las Naciones Unidas o al Consejo de Seguridad o a éste y a aquéllos.

    + +

    Artículo 11

    + +
      +
    1. La Asamblea General podrá considerar los principios generales de la cooperación en el mantenimiento de la paz y la seguridad internacionales, incluso los principios que rigen el desarme y la regulación de los armamentos, y podrá tambien hacer recomendaciones respecto de tales principios a los Miembros o al Consejo de Seguridad o a éste y a aquéllos.
    2. +
    3. La Asamblea General podrá discutir toda cuestión relativa al mantenimiento de la paz y la seguridad internacionales que presente a su consideración cualquier Miembro de las Naciones Unidas o el Consejo de Seguridad, o que un Estado que no es Miembro de las Naciones Unidas presente de conformidad con el Artículo 35, párrafo 2, y salvo lo dispuesto en el Artículo 12, podrá hacer recomendaciones acerca de tales cuestiones al Estado o Estados interesados o al Consejo de Seguridad o a éste y a aquéllos. Toda cuestión de esta naturaleza con respecto a la cual se requiera acción será referida al Consejo de Seguridad por la Asamblea General antes o después de discutirla.
    4. +
    5. La Asamblea General podrá llamar la atención del Consejo de Seguridad hacia situaciones susceptibles de poner en peligro la paz y la seguridad internacionales.
    6. +
    7. Los poderes de la Asamblea General enumerados en este Artículo no limitarán el alcance general del Artículo 10.
    8. +
    + +

    Artículo 12

    + +
      +
    1. Mientras el Consejo de Seguridad esté desempeñando las funciones que le asigna esta Carta con respecto a una controversia o situación, la Asamblea General no hará recomendación alguna sobre tal controversia o situación, a no ser que lo solicite el Consejo de Seguridad.
    2. +
    3. El Secretario General, con el consentimiento del Consejo de Seguridad, informará a la Asamblea General, en cada periodo de sesiones, sobre todo asunto relativo al mantenimiento de la paz y la seguridad internacionales que estuviere tratando el Consejo de Seguridad, e informará asimismo a la Asamblea General, o a los Miembros de las Naciones Unidas si la Asamblea no estuviere reunida, tan pronto como el Consejo de Seguridad cese de tratar dichos asuntos.
    4. +
    + +

    Artículo 13

    + +
      +
    1. La Asamblea General promoverá estudios y hará recomendaciones para los fines siguientes: +
        +
      1. fomentar la cooperación internacional en el campo político e impulsar el desarrollo progresivo del derecho internacional y su codificación;
      2. +
      3. fomentar la cooperación internacional en materias de carácter económico, social, cultural, educativo y sanitario y ayudar a hacer efectivos los derechos humanos y las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión.
      4. +
      +
    2. +
    3. Los demás poderes, responsabilidades y funciones de la Asamblea General con relación a los asuntos que se mencionan en el inciso b del párrafo 1 precedente quedan enumerados en los Capítulos IX y X.
    4. +
    + +

    Artículo 14

    + +

    Salvo lo dispuesto en el Artículo 12, la Asamblea General podrá recomendar medidas para el arreglo pacífico de cualesquiera situaciones, sea cual fuere su origen, que a juicio de la Asamblea puedan perjudicar el bienestar general o las relaciones amistosas entre naciones, incluso las situaciones resultantes de una violación de las disposiciones de esta Carta que enuncian los Propósitos y Principios de las Naciones Unidas.

    + +

    Artículo 15

    + +
      +
    1. La Asamblea General recibirá y considerará informes anuales y especiales del Consejo de Seguridad. Estos informes comprenderán una relación de las medidas que el Consejo de Seguridad haya decidido aplicar o haya aplicado para mantener la paz y la seguridad internacionales.
    2. +
    3. La Asamblea General recibirá y considerará informes de los demás órganos de las Naciones Unidas.
    4. +
    + +

    Artículo 16

    + +

    La Asamblea General desempeñará, con respecto al régimen internacional de administración fiduciaria, las funciones que se le atribuyen conforme a los Capítulos XII y XIII, incluso la aprobación de los acuerdos de administración fiduciaria de zonas no designadas como estratégicas.

    + +

    Artículo 17

    + +
      +
    1. La Asamblea General examinará y aprobará el presupuesto de la Organización.
    2. +
    3. Los miembros sufragarán los gastos de la Organización en la proporción que determine la Asamblea General.
    4. +
    5. La Asamblea General considerará y aprobará los arreglos financieros y presupuestarios que se celebren con los organismos especializados de que trata el Artículo 57 y examinará los presupuestos administrativos de tales organismos especializados con el fin de hacer recomendaciones a los organismos correspondientes.
    6. +
    + +

    VOTACIÓN

    + +

    Artículo 18

    + +
      +
    1. Cada Miembro de la Asamblea General tendrá un voto.
    2. +
    3. Las decisiones de la Asamblea General en cuestiones importantes se tomarán por el voto de una mayoria de dos tercios de los miembros presentes y votantes. Estas cuestiones comprenderán: las recomendaciones relativas al mantenimiento de la paz y la seguridad internacionales, la elección de los miembros no permanentes del Consejo de Seguridad, la elección de los miembros del Consejo Económico y Social, la elección de los miembros del Consejo de Administración Fiduciaria de conformidad con el inciso c, párrafo 1, del Artículo 86, la admisión de nuevos Miembros a las Naciones Unidas, la suspensión de los derechos y privilegios de los Miembros, la expulsión de Miembros, las cuestiones relativas al funcionamiento del régimen de administración fiduciaria y las cuestiones presupuestarias.
    4. +
    5. Las decisiones sobre otras cuestiones, incluso la determinación de categorías adicionales de cuestiones que deban resolverse por mayoría de dos tercios, se tomarán por la mayoría de los miembros presentes y votantes.
    6. +
    + +

    Artículo 19

    + +

    El Miembro de las Naciones Unidas que esté en mora en el pago de sus cuotas financieras para los gastos de la Organización, no tendra voto en la Asamblea General cuando la suma adeudada sea igual o superior al total de las cuotas adeudadas por los dos años anteriores completos. La Asamblea General podrá, sin embargo, permitir que dicho Miembro vote si llegare a la conclusión de que la mora se debe a circunstancias ajenas a la voluntad de dicho Miembro.

    + +

    PROCEDIMIENTO

    + +

    Artículo 20

    + +

    Las Asamblea General se reunirá anualmente en sesiones ordinarias y, cada vez que las circunstancias lo exijan, en sesiones extraordinarias. El Secretario General convocará a sesiones extraordinarias a solicitud del Consejo de Seguridad o de la mayoría de los Miembros de las Naciones Unidas.

    + +

    Artículo 21

    + +

    La Asamblea General dictará su propio reglamento y elegirá su Presidente para cada periodo de sesiones.

    + +

    Artículo 22

    + +

    La Asamblea General podrá establecer los organismos subsidiarios que estime necesarios para el desempeño de sus funciones.

    + +

    Capítulo V: El Consejo de Seguridad

    + +

    COMPOSICIÓN

    + +

    Artículo 23

    + +
      +
    1. El Consejo de Seguridad se compondrá de quince miembros de las Naciones Unidas. La República de China, Francia, la Unión de las Repúblicas Socialistas Soviéticas, el Reino Unido de la Gran Bretaña e Irlanda del Norte y los Estados Unidos de América, serán miembros permanentes del Consejo de Seguridad. La Asamblea General elegirá otros diez Miembros de las Naciones Unidas que serán miembros no permanentes del Consejo de Seguridad, prestando especial atención, en primer término, a la contribución de los Miembros de las Naciones Unidas al mantenimiento de la paz y la seguridad internacionales y a los démas propósitos de la Organización, como tambien a una distribución geográfica equitativa.
    2. +
    3. Los miembros no permanentes del Consejo de Seguridad serán elegidos por un periodo de dos años. En la primera elección de los miembros no permanentes que se celebre despues de haberse aumentado de once a quince el número de miembros del Consejo de Seguridad, dos de los cuatro miembros nuevos serán elegidos por un periodo de un año. Los miembros salientes no serán reelegibles para el periodo subsiguiente.
    4. +
    5. Cada miembro del Consejo de Seguridad tendrá un representante.
    6. +
    + +

    FUNCIONES y PODERES

    + +

    Artículo 24

    + +
      +
    1. A fin de asegurar acción rápida y eficaz por parte de las Naciones Unidas, sus Miembros confieren al Consejo de Seguridad la responsabilidad primordial de mantener la paz y la seguridad internacionales, y reconocen que el Consejo de Seguridad actuá a nombre de ellos al desempeñar las funciones que le impone aquella responsabilidad.
    2. +
    3. En el desempeño de estas funciones, el Consejo de Seguridad procederá de acuerdo con los Propósitos y Principios de las Naciones Unidas. Los poderes otorgados al Consejo de Seguridad para el desempeño de dichas funciones quedan definidos en los Capítulos VI, VII, VIII y XII.
    4. +
    5. El Consejo de Seguridad presentará a la Asamblea General para su consideración informes anuales y, cuando fuere necesario, informes especiales.
    6. +
    + +

    Artículo 25

    + +

    Los Miembros de las Naciones Unidas convienen en aceptar y cumplir las decisiones del Consejo de Seguridad de acuerdo con esta Carta.

    + +

    Artículo 26

    + +

    A fin de promover el establecimiento y mantenimiento de la paz y la seguridad internacionales con la menor desviación posible de los recursos humanos y económicos del mundo hacia los armamentos, el Consejo de Seguridad tendrá a su cargo, con la ayuda del Comité de Estado Mayor a que se refiere e1 Artículo 47, la elaboración de planes que se someterán a los Miembros de las Naciones Unidas para el establecimiento de un sistema de regulación de los armamentos.

    + +

    VOTACIÓN

    + +

    Artículo 27

    + +
      +
    1. Cada miembro del Consejo de Seguridad tendrá un voto.
    2. +
    3. Las decisiones del Consejo de Seguridad sobre cuestiones de procedimiento serán tomadas por el voto afirmativo de nueve miembros.
    4. +
    5. Las decisiones del Consejo de Seguridad sobre todas las demás cuestiones serán tomadas por el voto afirmativo de nueve miembros, incluso los votos afirmativos de todos los miembros permanentes; pero en las decisiones tomadas en virtud del Capítulo VI y del párrafo 3 del Artículo 52, la parte en una controversia se abstendrá de votar.
    6. +
    + +

    PROCEDIMIENTO

    + +

    Artículo 28

    + +
      +
    1. El Consejo de Seguridad será organizado de modo que pueda funcionar continuamente. Con tal fin, cada miembro del Consejo de Seguridad tendra en todo momento su representante en la sede de la Organización.
    2. +
    3. El Consejo de Seguridad celebrará reuniones periódicas en las cuales cada uno de sus miembros podrá, si lo desea, hacerse representar por un miembro de su Gobierno o por otro representante especialmente designado.
    4. +
    5. El Consejo de Seguridad podrá celebrar reuniones en cualesquiera lugares, fuera de la sede de la Organización, que juzgue más apropiados para facilitar sus labores.
    6. +
    + +

    Artículo 29

    + +

    El Consejo de Seguridad podrá establecer los organismos subsidiarios que estime necesarios para el desempeño de sus funciones.

    + +

    Artículo 30

    + +

    El Consejo de Seguridad dictará su propio reglamento, el cual establecerá el método de elegir su Presidente.

    + +

    Artículo 31

    + +

    Cualquier Miembro de las Naciones Unidas que no sea miembro del Consejo de Seguridad podra participar sin derecho a voto en la discusión de toda cuestión llevada ante el Consejo de Seguridad cuando éste considere que los intereses de ese Miembro están afectados de manera especial.

    + +

    Artículo 32

    + +

    El Miembro de las Naciones Unidas que no tenga asiento en el Consejo de Seguridad o el Estado que no sea Miembro de las Naciones Unidas, si fuere parte en una controversia que esté considerando el Consejo de Seguridad, será invitado a participar sin derecho a voto en las discusiones relativas a dicha controversia. El Consejo de Seguridad establecerá las condiciones que estime justas para la participación de los Estados que no sean Miembros de las Naciones Unidas.

    + +

    Capítulo VI: Arreglo pacífico de controversias

    + +

    Artículo 33

    + +
      +
    1. Las partes en una controversia cuya continuación sea susceptible de poner en peligro el mantenimiento de la paz y la seguridad internacionales tratarán de buscarle solución, ante todo, mediante la negociación, la investigación, la mediación, la conciliación, el arbitraje, el arreglo judicial, el recurso a organismos o acuerdos regionales u otros medios pacíficos de su elección.
    2. +
    3. El Consejo de Seguridad, si lo estimare necesario, instará a las partes a que arreglen sus controversias por dichos medios.
    4. +
    + +

    Artículo 34

    + +

    El Consejo de Seguridad podrá investigar toda controversia, o toda situación susceptible de conducir a fricción internacional o dar origen a una controversia, a fin de determinar si la prolongación de tal controversia o situación puede poner en peligro el mantenimiento de la paz y la seguridad internacionales.

    + +

    Artículo 35

    + +
      +
    1. Todo Miembro de las Naciones Unidas podrá llevar cualquiera controversia, o cualquiera situación de la naturaleza expresada en el Artículo 34, a la atención del Consejo de Seguridad o de la Asamblea General.
    2. +
    3. Un Estado que no es Miembro de las Naciones Unidas podrá llevar a la atención del Consejo de Seguridad o de la Asamblea General toda controversia en que sea parte, si acepta de antemano, en lo relativo a la controversia, las obligaciones de arreglo pacífico establecidas en esta Carta.
    4. +
    5. El procedimiento que siga la Asamblea General con respecto a asuntos que le sean presentados de acuerdo con este Artículo quedará sujeto a las disposiciones de los Artículos 11 y 12.
    6. +
    + +

    Artículo 36

    + +
      +
    1. El Consejo de Seguridad podrá, en cualquier estado en que se encuentre una controversia de la naturaleza de que trata el Artículo 33 o una situación de indole semejante, recomendar los procedimientos o métodos de ajuste que sean apropiados.
    2. +
    3. El Consejo de Seguridad debera tomar en consideración todo procedimiento que las partes hayan adoptado para el arreglo de la controversia.
    4. +
    5. Al hacer recomendaciones de acuerdo con este Artículo, el Consejo de Seguridad deberá tomar tambien en consideración que las controversias de orden jurídico, por regla general, deben ser sometidas por las partes a la Corte Internacional de Justicia, de conformidad con las disposiciones del Estatuto de la Corte.
    6. +
    + +

    Artículo 37

    + +
      +
    1. Si las partes en una controversia de la naturaleza definida en el Artículo 33 no lograren arreglarla por los medios indicados en dicho Artículo, la someterán al Consejo de Seguridad.
    2. +
    3. Si el Consejo de Seguridad estimare que la continuación de la controversia es realmente susceptible de poner en peligro el mantenimiento de la paz y la seguridad internacionales, el Consejo decidirá si ha de proceder de conformidad con el Artículo 36 o si ha de recomendar los términos de arreglo que considere apropiados.
    4. +
    + +

    Artículo 38

    + +

    Sin perjuicio de lo dispuesto en los Artículos 33 a 37, el Consejo de Seguridad podrá, si así lo solicitan todas las partes en una controversia, hacerles recomendaciones a efecto de que se llegue a un arreglo pacífico.

    + +

    Capítulo VII: Acción en caso de amenazas a la paz, quebrantamientos de la paz o actos de agresión

    + +

    Artículo 39

    + +

    El Consejo de Seguridad determinará la existencia de toda amenaza a la paz, quebrantamiento de la paz o acto de agresion y hará recomendaciones o decidirá que medidas seran tomadas de conformidad con los Artículos 41 y 42 para mantener o restablecer 1a paz y la seguridad internacionales.

    + +

    Artículo 40

    + +

    A fin de evitar que la situación se agrave, el Consejo de Seguridad, antes de hacer las recomendaciones o decidir las medidas de que trata el Artículo 39, podrá instar a las partes interesadas a que cumplan con las medidas provisionales que juzgue necesarias o aconsejables. Dichas medidas provisionales no perjudicarán los derechos, las reclamaciones o la posición de las partes interesadas. El Consejo de Seguridad tomará debida nota del incumplimiento de dichas medidas provisionales.

    + +

    Artículo 41

    + +

    El Consejo de Seguridad podrá decidir qué medidas que no impliquen el uso de la fuerza armada han de emplearse para hacer efectivas sus decisiones, y podrá instar a los Miembros de las Naciones Unidas a que apliquen dichas medidas, que podrán comprender la interrupción total o parcial de las relaciones económicas y de las comunicaciones ferroviarias, marítimas, aéreas, postales, telegráficas, radioeléctricas, y otros medios de comunicación, así como la ruptura de relaciones diplomáticas.

    + +

    Artículo 42

    + +

    Si el Consejo de Seguridad estimare que las medidas de que trata el Artículo 41 pueden ser inadecuadas o han demostrado serlo, podrá ejercer, por medio de fuerzas aéreas, navales o terrestres, la acción que sea necesaria para mantener o restablecer la paz y la seguridad internacionales. Tal acción podrá comprender demostraciones, bloqueos y otras operaciones ejecutadas por fuerzas aéreas, navales o terrestres de Miembros de las Naciones Unidas.

    + +

    Artículo 43

    + +
      +
    1. Todos los Miembros de las Naciones Unidas, con e1 fin de contribuir al mantenimiento de la paz y la seguridad internacionales, se compremeten a poner a disposición del Consejo de Seguridad, cuando éste lo solicite, y de conformidad con un convenio especial o con convenios especiales, las fuerzas armadas, la ayuda y las facilidades, incluso el derecho de paso, que sean necesarias para el propósito de mantener la paz y la seguridad internacionales.
    2. +
    3. Dicho convenio o convenios fijarán el número y clase de las fuerzas, su grado de preparación y su ublicación general, como también la naturaleza de las facilidades y de la ayuda que habrán de darse.
    4. +
    5. El convenio o convenios serán negociados a iniciativa del Consejo de Seguridad tan pronto como sea posible; serán concertados entre el Consejo de Seguridad y Miembros individuales o entre el Consejo de Seguridad y grupos de Miembros, y estarán sujetos a ratificación por los Estados signatarios de acuerdo con sus respectivos procedimientos constitucionales.
    6. +
    + +

    Artículo 44

    + +

    Cuando el Consejo de Seguridad haya decidido hacer uso de la fuerza, antes de requerir a un Miembro que no éste representado en él a que provea fuerzas armadas en cumplimiento de las obligaciones contraídas en virtud del Artículo 43, invitará a dicho Miembro, si éste así lo deseare, a participar en las decisiones del Consejo de Seguridad relativas al empleo de contingentes de fuerzas armadas de dicho Miembro.

    + +

    Artículo 45

    + +

    A fin de que la Organización pueda tomar medidas militares urgentes, sus Miembros mantendrán contingentes de fuerzas aéreas nacionales inmediatamente disponibles para la ejecución combinada de una acción coercitiva internacional. La potencia y el grado de preparación de estos contingentes y los planes para su acción combinada seran determinados, dentro de los límites establecidos en el convenio o convenios especiales de que trata el Artículo 43, por el Consejo de Seguridad con la ayuda del Comité de Estado Mayor.

    + +

    Artículo 46

    + +

    Los planes para el empleo de la fuerza armada serán hechos por el Consejo de Seguridad con la ayuda del Comité de Estado Mayor.

    + +

    Artículo 47

    + +
      +
    1. Se establecerá un Comité de Estado Mayor para asesorar y asistir al Consejo de Seguridad en todas las cuestiones relativas a las necesidades militares del Consejo para el mantenimiento de la paz y la seguridad internacionales, al empleo y comando de las fuerzas puestas a su disposición, a la regulación de los armamentos y al posible desarme.
    2. +
    3. El Comité de Estado Mayor estará integrado por los Jefes de Estado Mayor de los miembros permanentes del Consejo de Seguridad o sus representantes. Todo Miembro de las Naciones Unidas que no éste permanentemente representado en el Comite será invitado por éste a asociarse a sus labores cuando el desempeño eficiente de las funciones del Comité requiera la participación de dicho Miembro.
    4. +
    5. El Comité de Estado Mayor tendrá a su cargo, bajo la autoridad del Consejo de Seguridad, la dirección estratégica de todas las fuerzas armadas puestas a disposición del Consejo. Las cuestiones relativas al comando de dichas fuerzas serán resueltas posteriormente.
    6. +
    7. El Comite de Estado Mayor, con autorización del Consejo de Seguridad y después de consultar con los organismos regionales apropiados, podrá establecer subcomités regionales.
    8. +
    + +

    Artículo 48

    + +
      +
    1. La acción requerida para llevar a cabo las decisiones del Consejo de Seguridad para el mantenimiento de la paz y la seguridad internacionales será ejercida por todos los Miembros de las Naciones Unidas o por algunos de ellos, según lo determine el Consejo de Seguridad.
    2. +
    3. Dichas decisiones serán llevadas a cabo por los Miembros de las Naciones Unidas directamente y mediante su acción en los organismos internacionales apropiados de que formen parte.
    4. +
    + +

    Artículo 49

    + +

    Los Miembros de las Naciones Unidas deberán prestarse ayuda mutua para llevar a cabo las medidas dispuestas por el Consejo de Seguridad.

    + +

    Artículo 50

    + +

    Si el Consejo de Seguridad tomare medidas preventivas o coercitivas contra un Estado, cualquier otro Estado, sea o no Miembro de las Naciones Unidas, que confrontare problemas económicos especiales originados por la ejecución de dichas medidas, tendrá el derecho de consultar al Consejo de Seguridad acerca de la solución de esos problemas.

    + +

    Artículo 51

    + +

    Ninguna disposición de esta Carta menoscabará el derecho inmanente de legítima defensa, individual o colectiva, en caso de ataque armado contra un Miembro de las Naciones Unidas, hasta tanto que el Consejo de Seguridad haya tomado las medidas necesarias para mantener la paz y la seguridad internacionales. Las medidas tomadas por los Miembros en ejercicio del derecho de legítima defensa serán comunicadas inmediatamente al Consejo de Seguridad, y no afectarán en manera alguna la autoridad y responsabilidad del Consejo conforme a la presente Carta para ejercer en cualquier momento la acción que estime necesaria con el fin de mantener o restablecer la paz y la seguridad internacionales.

    + +

    Capítulo VIII: Acuerdos regionales

    + +

    Artículo 52

    + +
      +
    1. Ninguna disposición de esta Carta se opone a la existencia de acuerdos u organismos regionales cuyo fin sea entender en los asuntos relativos al mantenimiento de la paz y la seguridad internacionales y susceptibles de acción regional, siempre que dichos acuerdos u organismos, y sus actividades, sean compatibles con los Propósitos y Principios de las Naciones Unidas.
    2. +
    3. Los Miembros de las Naciones Unidas que sean partes en dichos acuerdos o que constituyan dichos organismos, harán todos los esfuerzos posibles para lograr el arreglo pacífico de las controversias de caracter local por medio de tales acuerdos u organismos regionales antes de someterlas al Consejo de Seguridad.
    4. +
    5. El Consejo de Seguridad promoverá el desarrollo del arreglo pacífico de las controversias de carácter local por medio de dichos acuerdos u organismos regionales, procediendo, bien a iniciativa de los Estados interesados, bien a instancia del Consejo de Seguridad.
    6. +
    7. Este Artículo no afecta en manera a1guna la aplicación de los Artículos 34 y 35.
    8. +
    + +

    Artículo 53

    + +
      +
    1. El Consejo de Seguridad utilizará dichos acuerdos u organismos regionales, si a ello hubiere lugar, para aplicar medidas coercitivas bajo su autoridad. Sin embargo, no se aplicarán medidas coercitivas en virtud de acuerdos regionales o por organismos regionales sin autorización del Consejo de Seguridad, salvo que contra Estados enemigos, según se les define en el párrafo 2 de este Artículo, se tomen las medidas dispuestas en virtud del Artículo 107 o en acuerdos regionales dirigidos contra la renovación de una política de agresión de parte de dichos Estados, hasta tanto que a solicitud de los gobiernos interesados quede a cargo de la Organización la responsabi1idad de prevenir nuevas agresiones de parte de aquellos Estados.
    2. +
    3. El término "Estados enemigos" empleado en el párrafo 1 de este Artículo se aplica a todo Estado que durante la segunda guerra mundial haya sido enemigo de cualquiera de los signatarios de esta Carta.
    4. +
    + +

    Artículo 54

    + +

    Se deberá mantener en todo tiempo al Consejo de Seguridad plenamente informado de las actividades emprendidas o proyectadas de conformidad con acuerdos regionales o por organismos regionales con el propósito de mantener la paz y la seguridad internacionales.

    + +

    Capítulo IX: Cooperación internacional económica y social

    + +

    Artículo 55

    + +

    Con el propósito de crear las condiciones de estabilidad y bienestar necesarias para las relaciones pacíficas y amistosas entre las naciones, basadas en el respeto al principio de la igualdad de derechos y al de la libre determinación de los pueblos, la Organización promoverá:

    + +
      +
    1. niveles de vida más elevados, trabajo permanente para todos, y condiciones de progreso y desarrollo económico y social;
    2. +
    3. la solución de problemas internacionales de carácter económico, social y sanitario, y de otros problemas conexos; y la cooperación internacional en el orden cultural y educativo; y
    4. +
    5. el respeto universal a los derechos humanos y a las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión, y la efectividad de tales derechos y libertades.
    6. +
    + +

    Artículo 56

    + +

    Todos los Miembros se comprometen a tomar medidas conjunta o separadamente, en cooperación con la Organización, para la realización de los propósitos consignados en el Artículo 55.

    + +

    Artículo 57

    + +
      +
    1. Los distintos organismos especializados establecidos por acuerdos intergubernamentales, que tengan amplias atribuciones internacionales definidas en sus estatutos, y relativas a materias de carácter económico, social, cultural, educativo, sanitario, y otras conexas, serán vinculados con la Organización de acuerdo con las disposiciones del Artículo 63.
    2. +
    3. Tales organismos especializados así vinculados con la Organización se denominarán en adelante "los organismos especializados".
    4. +
    + +

    Artículo 58

    + +

    La Organización hará recomendaciones con el objeto de coordinar las normas de acción y las actividades de los organismos especializados.

    + +

    Artículo 59

    + +

    La Organización iniciará, cuando hubiere lugar, negociaciones entre los Estados interesados para crear los nuevos organismos especializados que fueren necesarios para la realización de los propósitos enunciados en el Artículo 55.

    + +

    Artículo 60

    + +

    La responsabilidad por el desempeño de las funciones de la Organización señaladas en este Capítulo corresponderá a la Asamblea General y, bajo la autoridad de ésta, al Consejo Económico y Social, que dispondrá a este efecto de las facultades expresadas en el Capítulo X.

    + +

    Capítulo X: El Consejo Económico y Social

    + +

    COMPOSICIÓN

    + +

    Artículo 61

    + +
      +
    1. El Consejo Económico y Social estará integrado por cincuenta y cuatro Miembros de las Naciones Unidas elegidos por la Asamblea General.
    2. +
    3. Salvo lo prescrito en el párrafo 3, dieciocho miembros del Consejo Económico y Social serán elegidos cada año por un periodo de tres años. Los miembros salientes serán reelegibles para el periodo subsiguiente.
    4. +
    5. En la primera elección que se celebre después de haberse aumentado de veintisiete a cincuenta y cuatro el número de miembros del Consejo Económico y Social, además de los miembros que se elijan para sustituir a los nueve miembros cuyo mandato expire al final de ese año, se elegirán veintisiete miembros más. El mandato de nueve de estos veintisiete miembros adicionales asi elegidos expirara al cabo de un año y el de otros nueve miembros una vez transcurridos dos años, conforme a las disposiciones que dicte la Asamblea General.
    6. +
    7. Cada miembro del Consejo Económico y Social tendrá un representante.
    8. +
    + +

    FUNCIONES y PODERES

    + +

    Artículo 62

    + +
      +
    1. El Consejo Econó:mico y Social podrá hacer o iniciar estudios e informes con respecto a asuntos internacionales de carár económico, social, cultural, educativo y sanitario, y otros asuntos conexos, y hacer recomendaciones sobre tales asuntos a la Asamblea General, a los Miembros de las Naciones Unidas y a los organismos especializados interados.
    2. +
    3. El Consejo Económico y Social podrá hacer recomendaciones con el objeto de promover el respeto a los derechos humanos y a las libertades fundamentales de todos, y la efectividad de tales derechos y libertades.
    4. +
    5. El Consejo Económico y Social podrá formular proyectos de convención con respecto a cuestiones de su competencia para someterlos a la Asamblea General.
    6. +
    7. El Consejo Económico y Social podrá convocar, conforme a las reglas que prescriba la Organización, conferencias internacionales sobre asuntos de su competencia.
    8. +
    + +

    Artículo 63

    + +
      +
    1. El Consejo Económico y Social podrá concertar con cualquiera de los organismos especializados de que trata el Artículo 57, acuerdos por medio de los cuales se establezcan las condiciones en que dichos organismos habrán de vincularse con la Organización. Tales acuerdos estarán sujetos a la aprobación de la Asamblea General.
    2. +
    3. El Consejo Económico y Social podrá coordinar las actividades de los organismos especializados mediante consultas con ellos y haciéndoles recomendaciones, como también mediante recomendaciones a la Asamblea General y a los Miembros de las Naciones Unidas.
    4. +
    + +

    Artículo 64

    + +
      +
    1. El Consejo Económico y Social podrá tomar las medidas apropiadas para obtener informes periódicos de los organismos especializados. También podrá hacer arreglos con los Miembros de las Naciones Unidas y con los organismos especializados para obtener informes con respecto a los medidas tomadas para hacer efectivas sus propias recomendaciones y las que haga la Asamblea General acerca de materias de la competencia del Consejo.
    2. +
    3. El Consejo Económico y Social podrá comunicar a la Asamblea General sus observaciones sobre dichos informes.
    4. +
    + +

    Artículo 65

    + +

    El Consejo Económico y Social podrá suministrar información a1 Consejo de Seguridad y deberá darle la ayuda que éste le solicite.

    + +

    Artículo 66

    + +
      +
    1. El Consejo Económico y Social desempeñará las funciones que caigan dentro de su competencia en relación con el cumplimiento de las recomendaciones de la Asamblea General.
    2. +
    3. El Consejo Económico y Social podrá prestar, con aprobación de la Asamblea General, los servicios que le soliciten los Miembros de las Naciones Unidas y los organismos especializados.
    4. +
    5. El Consejo Económico y Social desempeñará las demás funciones prescritas en otras partes de esta Carta o que le asignare la Asamblea General.
    6. +
    + +

    VOTACIÓN

    + +

    Artículo 67

    + +
      +
    1. Cada miembro del Consejo Económico y Social tendrá un voto.
    2. +
    3. Las decisiones del Consejo Económico y Social se tomarán por la mayoría de los miembros presentes y votantes.
    4. +
    + +

    PROCEDIMIENTO

    + +

    Artículo 68

    + +

    El Consejo Económico y Social establecerá comisiones de orden económico y social y para la promoción de los derechos humanos, así como las demás comisiones necesarias para el desempeño de sus funciones.

    + +

    Artículo 69

    + +

    El Consejo Económico y Social invitará a cualquier Miembro de las Naciones Unidas a participar, sin derecho a voto, en sus deliberaciones sobre cualquier asunto de particular interés para dicho Miembro.

    + +

    Artículo 70

    + +

    El Consejo Económico y Social podrá hacer arreglos para que representantes de los organismos especializados participen, sin derecho a voto, en sus deliberaciones y en las de las comisiones que establezca, y para que sus propios representantes participen en las deliberaciones de aquellos organismos.

    + +

    Artículo 71

    + +

    El Consejo Económico y Social podrá hacer arreglos adecuados para celebrar consultas con organizaciones no gubernamentales que se ocupen en asuntos de la competencia del Consejo. Podrán hacerse dichos arreglos con organizaciones internacionales y, si a ello hubiere lugar, con organizaciones nacionales, previa consulta con el respectivo Miembro de las Naciones Unidas.

    + +

    Artículo 72

    + +
      +
    1. El Consejo Económico y Social dictará su propio reglamento, el cual establecerá el método de elegir su Presidente.
    2. +
    3. El Consejo Económico y Social se reunirá cuando sea necesario de acuerdo con su reglamento, el cual incluirá disposiciones para la convocación a sesiones cuando lo solicite una mayoría de sus miembros.
    4. +
    + +

    Capítulo XI: Declaración relativa a territorios no autónomos

    + +

    Artículo 73

    + +

    Los Miembros de las Naciones Unidas que tengan o asuman la responsabilidad de administrar territorios cuyos pueblos no hayan alcanzado todavía la plenitud del gobierno propio, reconocen el principio de que los intereses de los habitantes de esos territorios están por encima de todo, aceptan como un encargo sagrado la obligación de promover en todo lo posible, dentro del sistema de paz y de seguridad internacionales establecido por esta Carta, el bienestar de los habitantes de esos territorios, y asimismo se obligan:

    + +
      +
    1. a asegurar, con el debido respeto a la cultura de los pueblos respectivos, su adelanto político, económico, social y educativo, el justo tratamiento de dichos pueblos y su protección contra todo abuso;
    2. +
    3. a desarrollar el gobierno propio, a tener debidamente en cuenta las aspiraciones políticas de los pueblos, y a ayudarlos en el desenvolvimiento progresivo de sus libres instituciones políticas, de acuerdo con las circunstancias especiales de cada territorio, de sus pueblos y de sus distintos grados de adelanto;
    4. +
    5. a promover la paz y la seguridad internacionales;
    6. +
    7. a promover medidas constructivas de desarrollo, estimular la investigación, y cooperar unos con otros y, cuando y donde fuere del caso, con organismos internacionales especializados, para conseguir la realización práctica de los propósitos de carácter social, económico y científico expresados en este Artículo; y
    8. +
    9. a transmitir regularmente al Secretario General, a título informativo y dentro de los límites que la seguridad y consideraciones de orden constitucional requieran, la información estadística y de cualquier otra naturaleza técnica que verse sobre las condiciones económicas, sociales y educativas de los territorios por los cuales son respectivamente responsables, que no sean de los territorios a que se refieren los Capítulos XII y XIII de esta Carta.
    10. +
    + +

    Artículo 74

    + +

    Los Miembros de las Naciones Unidas convienen igualmente en que su política con respecto a los territorios a que se refiere este Capitulo, no menos que con respecto a sus territorios metropolitanos, debera fundarse en el principio general de la buena vecindad, teniendo debidamente en cuenta los intereses y el bienestar del resto del mundo en cuestiones de carácter social, económico y comercial.

    + +

    Capítulo XII: Régimen internacional de administración fiduciaria

    + +

    Artículo 75

    + +

    La Organización establecerá bajo su autoridad un régimen internacional de administración fiduciaria para la administración y vigilancia de los territorios que puedan colocarse bajo dicho régimen en virtud de acuerdos especiales posteriores. A dichos territorios se les denominará territorios fideicometidos.

    + +

    Artículo 76

    + +

    Los objetivos básicos del régimen de administración fiduciaria, de acuerdo con los Propósitos de las Naciones Unidas enunciados en el Artículo 1 de esta Carta, serán:

    + +
      +
    1. fomentar la paz y la seguridad internacionales;
    2. +
    3. promover el adelanto político, económico, social y educativo de los habitantes de los territorios fideicometidos, y su desarrollo progresivo hacia el gobierno propio o la independencia, teniéndose en cuenta las circunstancias particulares de cada territorio y de sus pueblos y los deseos libremente expresados de los pueblos interesados, y según se dispusiere en cada acuerdo sobre administración fiduciaria;
    4. +
    5. promover el respeto a los derechos humanos y a las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión, así como el reconocimiento de la interdependencia de los pueblos del mundo; y
    6. +
    7. asegurar tratamiento igual para todos los Miembros de las Naciones Unidas y sus nacionales en materias de carácter social, económico y comercial, así como tratamiento igual para dichos nacionales en la administración de la justicia, sin perjuicio de la realización de los objetivos arriba expuestos y con sujeción a las disposiciones del Artículo 80.
    8. +
    + +

    Artículo 77

    + +
      +
    1. El régimen de administración fiduciaria se aplicará a los territorios de las siguientes categorías que se colocaren bajo dicho régimen por medio de los correspondientes acuerdos: +
        +
      1. territorios actualmente bajo mandato;
      2. +
      3. territorios que, como resultado de la segunda guerra mundial, fueren segregados de Estados enemigos, y
      4. +
      5. territorios voluntariamente colocados bajo este régimen por los Estados responsables de su administración.
      6. +
      +
    2. +
    3. Será objeto de acuerdo posterior el determinar cuáles territorios de las categorías anteriormente mencionadas seran colocados bajo el régimen de administración fiduciaria y en qué condiciones.
    4. +
    + +

    Artículo 78

    + +

    El régimen de administración fiduciaria no se aplicará a territorios que hayan adquirido la calidad de Miembros de las Naciones Unidas, cuyas relaciones entre sí se basarán en el respeto al principio de la igualdad soberana.

    + +

    Artículo 79

    + +

    Los términos de la administración fiduciaria para cada territorio que haya de colocarse bajo el régimen expresado, y cualquier modificación o reforma, deberán ser acordados por los Estados directamente interesados, incluso la potencia mandataria en el caso de territorios bajo mandato de un Miembro de las Naciones Unidas, y serán aprobados según se dispone en los Artículos 83 y 85.

    + +

    Artículo 80

    + +
      +
    1. Salvo lo que se conviniere en los acuerdos especiales sobre administración fiduciaria concertados de conformidad con los Artículos 77, 79 y 81 y mediante los cuales se coloque cada territorio bajo el régimen de administración fiduciaria, y hasta tanto se coIlcierten tales acuerdos, ninguna disposición de este Capítulo será interpretada en el sentido de que modifica en manera alguna los derechos de cualesquiera Estados o pueblos, o los términos de los instrumentos internacionales vigentes en que sean partes Miembros de los Naciones Unidas.
    2. +
    3. El párrafo 1 de este Artículo no será interpretado en el sentido de que da motivo para demorar o diferir la negociación y celebración de acuerdos para aplicar el régimen de administración fiduciaria a territorios bajo mandato y otros territorios, conforme al Artículo 77.
    4. +
    + +

    Artículo 81

    + +

    El acuerdo sobre administración fiduciaria contendrá en cada caso las condiciones en que se administrará el territorio fideicometido, y designará la autoridad que ha de ejercer la administración. Dicha autoridad, que en lo sucesivo se denominará la "autoridad administradora", podrá ser uno o más Estados o la misma Organización.

    + +

    Artículo 82

    + +

    Podrán designarse en cualquier acuerdo sobre administración fiduciaria, una o varias zonas estratégicas que comprendan parte o la totalidad del territorio fideicometido a que se refiera el acuerdo, sin perjuicio de los acuerdos especiales celebrados con arreglo al Artículo 43.

    + +

    Artículo 83

    + +
      +
    1. Todas las funciones de las Naciones Unidas relativas a zonas estratégicas, incluso la de aprobar los términos de los acuerdos sobre administración fiduciaria y de las modificaciones o reformas de los mismos, serán ejercidas por el Consejo de Seguridad.
    2. +
    3. Los objetivos básicos enunciados en el Artículo 76 serán aplicables a la población de cada zona estratégica.
    4. +
    5. Salvo las disposiciones de los acuerdos sobre administración fiduciaria y sin perjuicio de las exigencias de la seguridad, el Consejo de Seguridad aprovechará la ayuda del Consejo de Administración Fiduciaria para desempeñar, en las zonas estratégicas, aquellas funciones de la Organización relativas a materias políticas, económicas, sociales y educativas que correspondan al régimen de administración fiduciaria.
    6. +
    + +

    Artículo 84

    + +

    La autoridad administradora tendrá el deber de velar por que el territorio fideicometido contribuya al mantenimiento de la paz y la seguridad internacionales. Con tal fin, la autoridad administradora podrá hacer uso de las fuerzas voluntarias, de las facilidades y de la ayuda del citado territorio, a efecto de cumplir con las obligaciones por ella contraídas a este respecto ante el Consejo de Seguridad, como también para la defensa local y el mantenimiento de la ley y del orden dentro del territorio fideicometido.

    + +

    Artículo 85

    + +
      +
    1. Las funciones de la Organización en lo que respecta a los acuerdos sobre administración fiduciaria relativos a todas las zonas no designadas como estratégicas, incluso la de aprobar los términos de los acuerdos y las modificaciones o reformas de los mismos serán ejercidas por la Asamblea General.
    2. +
    3. El Consejo de Administración Fiduciaria, bajo la autoridad de la Asamblea General, ayudará a ésta en el desempeño de las funciones aquí enumeradas.
    4. +
    + +

    Capítulo XIII: El Consejo de Administración Fiduciaria

    + +

    COMPOSICIÓN

    + +

    Artículo 86

    + +
      +
    1. El Consejo de Administración Fiduciaria estará integrado por los siguientes Miembros de las Naciones Unidas: +
        +
      1. los Miembros que administren territorios fideicometidos;
      2. +
      3. los Miembros mencionados por su nombre en el Artículo 23 que no estén administrando territorios fideicometidos; y
      4. +
      5. tantos otros Miembros elegidos por periodos de tres años por la Asamblea General cuantos sean necesarios para asegurar que el número total de miembros del Consejo de Administración Fiduciaria se divida por igual entre los Miembros de las Naciones Unidas administradores de tales territorios y los no administradores.
      6. +
      +
    2. +
    3. Cada miembro del Consejo de Administración Fiduciaria designará a una persona especialmente calificada para que lo represente en el Consejo.
    4. +
    + +

    FUNCIONES Y PODERES

    + +

    Artículo 87

    + +

    En el desempeño de sus funciones, la Asamblea General y, bajo su autoridad, el Consejo de Administración Fiduciaria, podrán:

    + +
      +
    1. considerar informes que les haya rendido la autoridad administradora;
    2. +
    3. aceptar peticiones y examinarlas en consulta con la autoridad administradora;
    4. +
    5. disponer visitas periódicas a los territorios fideicometidos en fechas convenidas con la autoridad administradora; y
    6. +
    7. tomar estas y otras medidas de conformidad con los términos de los acuerdos sobre administración fiduciaria.
    8. +
    + +

    Artículo 88

    + +

    El Consejo de Administración Fiduciaria formulará un cuestionario sobre el adelanto político, económico, social y educativo de los habitantes de cada territorio fideicometido; y la autoridad administradora de cada territorio fideicometido dentro de la competencia de la Asamblea General, rendirá a ésta un informe anual sobre 1a base de dicho cuestionario.

    + +

    VOTACIÓN

    + +

    Artículo 89

    + +
      +
    1. Cada miembro del Consejo de Administración Fiduciaria tendra un voto.
    2. +
    3. Las decisiones del Consejo de Administración Fiduciaria serán tomadas por el voto de la mayoría de los miembros presentes y votantes.
    4. +
    + +

    PROCEDIMIENTO

    + +

    Artículo 90

    + +
      +
    1. El Consejo de Administración Fiduciaria dictará su propio reglamento, el cual establecerá el método de elegir su Presidente.
    2. +
    3. El Consejo de Administración Fiduciaria se reunirá cuando sea necesario, según su reglamento. Este contendrá disposiciones sobre convocación del Consejo a solicitud de la mayoría de sus miembros.
    4. +
    + +

    Artículo 91

    + +

    El Consejo de Administración Fiduciaria, cuando lo estime conveniente, se valdrá de la ayuda del Consejo Económico y Social y de la de los organismos especializados con respecto a los asuntos de la respectiva competencia de los mismos.

    + +

    Capítulo XIV: La Corte Internacional de Justicia

    + +

    Artículo 92

    + +

    La Corte Internacional de Justicia será el órgano judicial principal de las Naciones Unidas; funcionará de conformidad con el Estatuto anexo, que está basado en el de la Corte Permanente de Justicia Internacional, y que forma parte integrante de esta Carta.

    + +

    Artículo 93

    + +
      +
    1. Todos los Miembros de las Naciones Unidas son ipso facto partes en el Estatuto de la Corte Internacional de Justicia.
    2. +
    3. Un Estado que no sea Miembro de las Naciones Unidas podrá llegar a ser parte en el Estatuto de la Corte Internacional de Justicia, de acuerdo con las condiciones que determine en cada caso la Asamblea General a recomendación del Consejo de Seguridad.
    4. +
    + +

    Artículo 94

    + +
      +
    1. Cada Miembro de las Naciones Unidas compromete a cumplir la decisión de la Corte Internacional de Justicia en todo litigio en que sea parte.
    2. +
    3. Si una de las partes en un litigio dejare de cumplir las obligaciones que le imponga un fallo de la Corte, la otra parte podrá recurrir al Consejo de Seguridad, el cual podrá, si lo cree necesario, hacer recomendaciones o dictar medidas con el objeto de que se lleve a efecto la ejecución del fallo.
    4. +
    + +

    Artículo 95

    + +

    Ninguna de las disposiciones de esta Carta impedirá a los Miembros de las Naciones Unidas encomendar la solución de sus diferencias a otros tribunales en virtud de acuerdos ya existentes o que puedan concertarse en el futuro.

    + +

    Artículo 96

    + +
      +
    1. La Asamblea General o el Consejo de Seguridad podrán solicitar de la Corte Internacional de Justicia que emita una opinión consultiva sobre cualquier cuestión jurídica.
    2. +
    3. Los otros órganos de las Naciones Unidas y los organismos especializados que en cualquier momento sean autorizados para ello por la Asamblea General, podrán igualmente solicitar de la Corte opiniones consultivas sobre cuestiones jurídicas que surjan dentro de la esfera de sus actividades.
    4. +
    + +

    Capítulo XV: La Secretaría

    + +

    Artículo 97

    + +

    La Secretaría se compondrá de un Secretario General y del personal que requiera la Organización. El Secretario General será nombrado por la Asamblea General a recomendación del Consejo de Seguridad. El Secretario General sera el más alto funcionario administrativo de la Organización.

    + +

    Artículo 98

    + +

    El Secretario General actuará como tal en todas las sesiones de la Asamblea General, del Consejo de Seguridad, del Consejo Económico y Social y del Consejo de Administración Fiduciaria, y desempeñara las demas funciones que le encomienden dichos órganos. El Secretario General rendirá a la Asamblea General un informe anual sobre las actividades de la Organización.

    + +

    Artículo 99

    + +

    El Secretario General podrá llamar la atención del Consejo de Seguridad hacia cualquier asunto que en su opinión pueda poner en peligro el mantenimiento de la paz y la seguridad internacionales.

    + +

    Artículo 100

    + +
      +
    1. En el cumplimiento de sus deberes, el Secretario General y el personal de la Secretaría no solicitarán ni recibirán instrucciones de ningún gobierno ni de ninguna autoridad ajena a la Organización, y se abstendrán de actuar en forma alguna que sea incompatible con su condición de funcionarios internacionales responsables únicamente ante la Organización.
    2. +
    3. Cada uno de los Miembros de las Naciones Unidas se compromete a respetar el carácter exclusivamente internacional de las funciones del Secretario General y del personal de la Secretaría, y a no tratar de influir sobre ellos en el desempeño de sus funciones.
    4. +
    + +

    Artículo 101

    + +
      +
    1. El personal de la Secretaría será nombrado por el Secretario General de acuerdo con las reglas establecidas por la Asamblea General.
    2. +
    3. Se asignará permanentemente personal adecuado al Consejo Económico y Social, al Consejo de Administración Fiduciaria y, según se requiera, a otros órganos de las Naciones Unidas. Este personal formará parte de la Secretaría.
    4. +
    5. La consideración primordial que se tendrá en cuenta al nombrar el personal de la Secretaría y al determinar las condiciones del servicio, es la necesidad de asegurar el más alto grado de eficiencia, competencia e integridad. Se dará debida consideración también a la importancia de contratar el personal en forma de que haya la más amplia representación geográfica posible.
    6. +
    + +

    Capítulo XVI: Disposiciones varias

    + +

    Artículo 102

    + +
      +
    1. Todo tratado y todo acuerdo internacional concertados por cualesquiera Miembros de las Naciones Unidas después de entrar en vigor esta Carta, serán registrados en la Secretaría y publicados por ésta a la mayor brevedad posible.
    2. +
    3. Ninguna de las partes en un tratado o acuerdo internacional que no haya sido registrado conforme a las disposiciones del párrafo 1 de este Artículo, podrá invocar dicho tratado o acuerdo ante órgano alguno de las Naciones Unidas.
    4. +
    + +

    Artículo 103

    + +

    En caso de conflicto entre las obligaciones contraídas por los Miembros de las Naciones Unidas en virtud de la presente Carta y sus obligaciones contraídas en virtud de cualquier otro convenio internacional, prevalecerán las obligaciones impuestas por la presente Carta.

    + +

    Artículo 104

    + +

    La Organización gozará, en el territorio de cada uno de sus Miembros, de la capacidad jurídica que sea necesaria para el ejercicio de sus funciones y la realización de sus propósitos.

    + +

    Artículo 105

    + +
      +
    1. La Organización gozará, en el territorio de cada uno de sus Miembros, de los privilegios e inmunidades necesarios para la realización de sus propósitos.
    2. +
    3. Los representantes de los Miembros de la Organización y los funcionarios de ésta, gozarán asimismo de los privilegios e inmunidades necesarios para desempeñar con independencia sus funciones en relación con la Organización.
    4. +
    5. La Asamblea General podrá hacer recomendaciones con el objeto de determinar los pormenores de la aplicación de los párrafos 1 y 2 de este Artículo, o proponer convenciones a los Miembros de las Naciones Unidas con el mismo objeto.
    6. +
    + +

    Capítulo XVII: Acuerdos transitorios sobre seguridad

    + +

    Artículo 106

    + +

    Mientras entran en vigor los convenios especiales previstos en el Artículo 43, que a juicio del Consejo de Seguridad lo capaciten para ejercer las atribuciones a que se refiere el Artículo 42, las partes en la Declaración de las Cuatro Potencias firmada en Moscú el 30 de octubre de 1943, y Francia, deberán, conforme a las disposiciones del párrafo 5 de esa Declaración, celebrar consultas entre sí, y cuando a ello hubiere lugar, con otros miembros de la Organización, a fin de acordar en nombre de ésta la acción conjunta que fuere necesaria para mantener la paz y la seguridad internacionales.

    + +

    Artículo 107

    + +

    Ninguna de las disposiciones de esta Carta invalidará o impedirá cualquier acción ejercida o autorizada como resultado de la segunda guerra mundial con respecto a un Estado enemigo de cualquiera de los signatarios de esta Carta durante la citada guerra, por los gobiernos responsables de dicha acción.

    + +

    Capítulo XVIII: Reformas

    + +

    Artículo 108

    + +

    Las reformas a la presente Carta entrarán en vigor para todos los Miembros de las Naciones Unidas cuando hayan sido adoptadas por el voto de las dos terceras partes de los miembros de la Asamblea General y ratificadas, de conformidad con sus respectivos procedimientos constitucionales, por las dos terceras partes de los Miembros de las Naciones Unidas, incluyendo a todos los miembros permanentes del Consejo de Seguridad.

    + +

    Artículo 109

    + +
      +
    1. Se podrá celebrar una Conferencia General de los Miembros de las Naciones Unidas con el propósito de revisar esta Carta, en la fecha y lugar que se determinen por el voto de las dos terceras partes de los miembros de la Asamblea General y por el voto de cualesquiera nueve miembros del Consejo de Seguridad. Cada Miembro de las Naciones Unidas tendrá un voto en la Conferencia.
    2. +
    3. Toda modificación de esta Carta recomendada por el voto de las dos terceras partes de la Conferencia entrará en vigor al ser ratificada de acuerdo con sus respectivos procedimientos constitucionales, por las dos terceras partes de los Miembros de las Naciones Unidas, incluyendo a todos los miembros permanentes del Consejo de Seguridad.
    4. +
    5. Si no se hubiere celebrado tal Conferencia antes de la décima reunión anual de la Asamblea General despues de entrar en vigor esta Carta, la proposición de convocar tal Conferencia será puesta en la agenda de dicha reunión de la Asamblea General, y la Conferencia será celebrada si así lo decidieren la mayoría de los miembros de la Asamblea General y siete miembros cualesquiera del Consejo de Seguridad.
    6. +
    + +

    Capítulo XIX: Ratificación y firma

    + +

    Artículo 110

    + +
      +
    1. La presente Carta será ratificada por los Estados signatorios de acuerdo con sus respectivos procedimientos constitucionales.
    2. +
    3. Las ratificaciones serán entregadas para su depósito al Gobierno de los Estados Unidos de América, el cual notificará cada depósito a todos los Estados signatarios así como al Secretario General de la Organización cuando haya sido designado.
    4. +
    5. La presente Carta entrará en vigor tan pronto como hayan sido depositadas las ratificaciones de la República de China, Francia, la Unión de las Repúblicas Socialistas Soviéticas, el Reino Unido de la Gran Bretaña e Irlanda del Norte y los Estados Unidos de América, y por la mayoría de los demás Estados signatarios. Acto seguido se dejará constancia de las ratificaciones depositadas en un protocolo que extenderá el Gobierno de los Estados Unidos de América, y del cual transmitirá copias a todos los Estados signatarios.
    6. +
    7. Los Estados signatarios de esta Carta que la ratifiquen después que haya entrado en vigor adquirirán la calidad de miembros originarios de las Naciones Unidas en la fecha del depósito de sus respectivas ratificaciones.
    8. +
    + +

    Artículo 111

    + +

    La presente Carta, cuyos textos en chino, francés, ruso, inglés y español son igualmente auténticos, será depositada en los archivos del Gobierno de los Estados Unidos de América. Dicho Gobierno enviará copias debidamente certificadas de la misma a los Gobiernos de los demás Estados signatarios.

    + +

    En fe de lo cual los Representantes de los Gobiernos de las Naciones Unidas han suscrito esta Carta. Firmada en la ciudad de San Francisco, a los veintiséis días del mes de junio de mil novecientos cuarenta y cinco.

    +
    +
    +
    + + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + + + +
    +
    + + +
    + + + + + + + + + + + +
    + + + + + + + + + +
    \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_ES.txt b/stdlib/benchmarks/collections/data/UN_charter_ES.txt new file mode 100644 index 0000000000..98337dec21 --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_ES.txt @@ -0,0 +1,453 @@ +Carta de las Naciones Unidas (texto completo) +Preámbulo +NOSOSTROS LOS PUEBLOS DE LAS NACIONES UNIDAS RESUELTOS +a preservar a las generaciones venideras del flagelo de la guerra que dos veces durante nuestra vida ha infligido a la Humanidad sufrimientos indecibles, + +a reafirmar la fe en los derechos fundamentales del hombre, en la dignidad y el valor de la persona humana, en la igualdad de derechos de hombres y mujeres y de las naciones grandes y pequeñas, + +a crear condiciones bajo las cuales puedan mantenerse la justicia y el respeto a las obligaciones emanadas de los tratados y de otras fuentes del derecho internacional, + +a promover el progreso social y a elevar el nivel de vida dentro de un concepto más amplio de la libertad, + +Y CON TALES FINALIDADES +a practicar la tolerancia y a convivir en paz como buenos vecinos, + +a unir nuestras fuerzas para el mantenimiento de la paz y la seguridad internacionales, + +a asegurar, mediante la aceptación de principios y la adopción de métodos, que no se usará; la fuerza armada sino en servicio del interés común, y + +a emplear un mecanismo internacional para promover el progreso económico y social de todos los pueblos, + +HEMOS DECIDIDO UNIR NUESTROS ESFUERZOS PARA REALIZAR DESIGNIOS. +Por lo tanto, nuestros respectivos Gobiernos, por medio de representantes reunidos en la ciudad de San Francisco que han exhibido sus plenos poderes, encontrados en buena y debida forma, han convenido en la presente Carta de las Naciones Unidas, y por este acto establecen una organización internacional que se denominará las Naciones Unidas. + +Capítulo I: Propósitos y principios +Artículo 1 +Los propósitos de las Naciones Unidas son: + +Mantener la paz y la seguridad internacionales, y con tal fin: tomar medidas colectivas eficaces para prevenir y eliminar amenazas a la paz, y para suprimir actos de agresión u otros quebrantamientos de la paz; y lograr por medios pacíficos, y de conformidad con los principios de la justicia y del derecho internacional, el ajuste o arreglo de controversias o situaciones internacionales susceptibles de conducir a quebrantamientos de la paz; +Fomentar entre las naciones relaciones de amistad basadas en el respeto al principio de la igualdad de derechos y al de la libre determinación de los pueblos, y tomar otros medidas adecuadas para fortalecer la paz universal; +Realizar la cooperación internacional en la solución de problemas internacionales de carácter económico, social, cultural o humanitario, y en el desarrollo y estímulo del respeto a los derechos humanos y a las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión; y +Servir de centro que armonice los esfuerzos de las naciones por alcanzar estos propósitos comunes. +Artículo 2 +Para la realización de los Propósitos consignados en el Artículo 1, la Organización y sus Miembros procederán de acuerdo con los siguientes Principios: + +La Organización esta basada en el principio de la igualdad soberana de todos sus Miembros. +Los Miembros de la Organización, a fin de asegurarse los derechos y beneficios inherentes a su condición de tales, cumplirán de buena fe las obligaciones contraidas por ellos de conformidad con esta Carta. +Los Miembros de la Organización arreglarán sus controversias internacionales por medios pacificos de tal manera que no se pongan en peligro ni la paz y la seguridad internacionales ni la justicia. +Los Miembros de la Organización, en sus relaciones internacionales, se abstendrán de recurrir a la amenaza o al uso de la fuerza contra la integridad territorial o la independencia política de cualquier Estado, o en cualquier otra forma incompatible con los Propósitos de las Naciones Unidas. +Los Miembros de la Organización prestaron a ésta toda clase de ayuda en cualquier acción que ejerza de conformidad con esta Carta, y se abstendrán de dar ayuda a Estado alguno contra el cual la Organización estuviere ejerciendo acción preventiva o coercitiva. +La Organización hará que los Estados que no son Miembros de las Naciones Unidas se conduzcan de acuerdo con estos Principios en la medida que sea necesaria para mantener la paz y la seguridad internacionales. +Ninguna disposición de esta Carta autorizará a las Naciones Unidas a intervenir en los asuntos que son esencialmente de la jurisdicción interna de los Estados, ni obligará; a los Miembros a someter dichos asuntos a procedimientos de arreglo conforme a la presente Carta; pero este principio no se opone a la aplicación de las medidas coercitivas prescritas en el Capítulo VII. +Capítulo II: Miembros +Artículo 3 +Son Miembros originarios de las Naciones Unidas los Estados que habiendo participado en la Conferencia de las Naciones Unidas sobre Organización Internacional celebrada en San Francisco, o que habiendo firmado previamente la Declaración de las Naciones Unidas de 1 de enero de 1942, suscriban esta Carta y la ratifiquen de conformidad con el Artículo 110. + +Artículo 4 +Podrán ser Miembros de las Naciones Unidas todos los demás Estados amantes de la paz que acepten las obligaciones consignadas en esta Carta, y que, a juicio de la Organización, estén capacitados para cumplir dichas obligaciones y se hallen dispuestos a hacerlo. +La admisión de tales Estados como Miembros de las Naciones Unidas se efectuará por decisión de la Asamblea General a recomendación del Consejo de Seguridad. +Artículo 5 +Todo Miembro de las Naciones Unidas que haya sido objeto de acción preventiva o coercitiva por parte del Consejo de Seguridad podrá ser suspendido por la Asamblea General, a recomendación del Consejo de Seguridad, del ejercicio de los derechos y privilegios inherentes a su calidad de Miembro. El ejercicio de tales derechos y privilegios podrá ser restituido por el Consejo de Seguridad. + +Artículo 6 +Todo Miembro de las Naciones Unidas que haya violado repetidamente los Principios contenidos en esta Carta podrá ser expulsado de la Organización por la Asamblea General a recomendación del Consejo de Seguridad. + +Capítulo III: Órganos +Artículo 7 +Se establecen como órganos principales de las Naciones Unidas: una Asamblea General, un Consejo de Seguridad, un Consejo Económico y Social, un Consejo de Administración Fiduciaria, una Corte Internacional de Justicia, una Secretaría. +Se podrán establecer, de acuerdo con las disposiciones de la presente Carta, los órganos subsidiarios que se estimen necesarios. +Artículo 8 +La Organización no establecerá restricciones en cuanto a la elegibilidad de hombres y mujeres para participar en condiciones de igualdad y en cualquier caracter en las funciones de sus órganos principales y subsidiarios. + +Capítulo IV: La Asamblea General +COMPOSICIÓN +Artículo 9 +La Asamblea General estará integrada por todos los Miembros de las Naciones Unidas. +Ningun Miembro podrá tener más de cinco representantes en la Asamblea General. +FUNCIONES y PODERES +Artículo 10 +La Asamblea General podrá discutir cualesquier asuntos o cuestiones dentro de los límites de esta Carta o que se refieran a los poderes y funciones de cualquiera de los órganos creados por esta Carta, y salvo lo dispuesto en el Artículo 12 podrá hacer recomendaciones sobre tales asuntos o cuestiones a los Miembros de las Naciones Unidas o al Consejo de Seguridad o a éste y a aquéllos. + +Artículo 11 +La Asamblea General podrá considerar los principios generales de la cooperación en el mantenimiento de la paz y la seguridad internacionales, incluso los principios que rigen el desarme y la regulación de los armamentos, y podrá tambien hacer recomendaciones respecto de tales principios a los Miembros o al Consejo de Seguridad o a éste y a aquéllos. +La Asamblea General podrá discutir toda cuestión relativa al mantenimiento de la paz y la seguridad internacionales que presente a su consideración cualquier Miembro de las Naciones Unidas o el Consejo de Seguridad, o que un Estado que no es Miembro de las Naciones Unidas presente de conformidad con el Artículo 35, párrafo 2, y salvo lo dispuesto en el Artículo 12, podrá hacer recomendaciones acerca de tales cuestiones al Estado o Estados interesados o al Consejo de Seguridad o a éste y a aquéllos. Toda cuestión de esta naturaleza con respecto a la cual se requiera acción será referida al Consejo de Seguridad por la Asamblea General antes o después de discutirla. +La Asamblea General podrá llamar la atención del Consejo de Seguridad hacia situaciones susceptibles de poner en peligro la paz y la seguridad internacionales. +Los poderes de la Asamblea General enumerados en este Artículo no limitarán el alcance general del Artículo 10. +Artículo 12 +Mientras el Consejo de Seguridad esté desempeñando las funciones que le asigna esta Carta con respecto a una controversia o situación, la Asamblea General no hará recomendación alguna sobre tal controversia o situación, a no ser que lo solicite el Consejo de Seguridad. +El Secretario General, con el consentimiento del Consejo de Seguridad, informará a la Asamblea General, en cada periodo de sesiones, sobre todo asunto relativo al mantenimiento de la paz y la seguridad internacionales que estuviere tratando el Consejo de Seguridad, e informará asimismo a la Asamblea General, o a los Miembros de las Naciones Unidas si la Asamblea no estuviere reunida, tan pronto como el Consejo de Seguridad cese de tratar dichos asuntos. +Artículo 13 +La Asamblea General promoverá estudios y hará recomendaciones para los fines siguientes: +fomentar la cooperación internacional en el campo político e impulsar el desarrollo progresivo del derecho internacional y su codificación; +fomentar la cooperación internacional en materias de carácter económico, social, cultural, educativo y sanitario y ayudar a hacer efectivos los derechos humanos y las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión. +Los demás poderes, responsabilidades y funciones de la Asamblea General con relación a los asuntos que se mencionan en el inciso b del párrafo 1 precedente quedan enumerados en los Capítulos IX y X. +Artículo 14 +Salvo lo dispuesto en el Artículo 12, la Asamblea General podrá recomendar medidas para el arreglo pacífico de cualesquiera situaciones, sea cual fuere su origen, que a juicio de la Asamblea puedan perjudicar el bienestar general o las relaciones amistosas entre naciones, incluso las situaciones resultantes de una violación de las disposiciones de esta Carta que enuncian los Propósitos y Principios de las Naciones Unidas. + +Artículo 15 +La Asamblea General recibirá y considerará informes anuales y especiales del Consejo de Seguridad. Estos informes comprenderán una relación de las medidas que el Consejo de Seguridad haya decidido aplicar o haya aplicado para mantener la paz y la seguridad internacionales. +La Asamblea General recibirá y considerará informes de los demás órganos de las Naciones Unidas. +Artículo 16 +La Asamblea General desempeñará, con respecto al régimen internacional de administración fiduciaria, las funciones que se le atribuyen conforme a los Capítulos XII y XIII, incluso la aprobación de los acuerdos de administración fiduciaria de zonas no designadas como estratégicas. + +Artículo 17 +La Asamblea General examinará y aprobará el presupuesto de la Organización. +Los miembros sufragarán los gastos de la Organización en la proporción que determine la Asamblea General. +La Asamblea General considerará y aprobará los arreglos financieros y presupuestarios que se celebren con los organismos especializados de que trata el Artículo 57 y examinará los presupuestos administrativos de tales organismos especializados con el fin de hacer recomendaciones a los organismos correspondientes. +VOTACIÓN +Artículo 18 +Cada Miembro de la Asamblea General tendrá un voto. +Las decisiones de la Asamblea General en cuestiones importantes se tomarán por el voto de una mayoria de dos tercios de los miembros presentes y votantes. Estas cuestiones comprenderán: las recomendaciones relativas al mantenimiento de la paz y la seguridad internacionales, la elección de los miembros no permanentes del Consejo de Seguridad, la elección de los miembros del Consejo Económico y Social, la elección de los miembros del Consejo de Administración Fiduciaria de conformidad con el inciso c, párrafo 1, del Artículo 86, la admisión de nuevos Miembros a las Naciones Unidas, la suspensión de los derechos y privilegios de los Miembros, la expulsión de Miembros, las cuestiones relativas al funcionamiento del régimen de administración fiduciaria y las cuestiones presupuestarias. +Las decisiones sobre otras cuestiones, incluso la determinación de categorías adicionales de cuestiones que deban resolverse por mayoría de dos tercios, se tomarán por la mayoría de los miembros presentes y votantes. +Artículo 19 +El Miembro de las Naciones Unidas que esté en mora en el pago de sus cuotas financieras para los gastos de la Organización, no tendra voto en la Asamblea General cuando la suma adeudada sea igual o superior al total de las cuotas adeudadas por los dos años anteriores completos. La Asamblea General podrá, sin embargo, permitir que dicho Miembro vote si llegare a la conclusión de que la mora se debe a circunstancias ajenas a la voluntad de dicho Miembro. + +PROCEDIMIENTO +Artículo 20 +Las Asamblea General se reunirá anualmente en sesiones ordinarias y, cada vez que las circunstancias lo exijan, en sesiones extraordinarias. El Secretario General convocará a sesiones extraordinarias a solicitud del Consejo de Seguridad o de la mayoría de los Miembros de las Naciones Unidas. + +Artículo 21 +La Asamblea General dictará su propio reglamento y elegirá su Presidente para cada periodo de sesiones. + +Artículo 22 +La Asamblea General podrá establecer los organismos subsidiarios que estime necesarios para el desempeño de sus funciones. + +Capítulo V: El Consejo de Seguridad +COMPOSICIÓN +Artículo 23 +El Consejo de Seguridad se compondrá de quince miembros de las Naciones Unidas. La República de China, Francia, la Unión de las Repúblicas Socialistas Soviéticas, el Reino Unido de la Gran Bretaña e Irlanda del Norte y los Estados Unidos de América, serán miembros permanentes del Consejo de Seguridad. La Asamblea General elegirá otros diez Miembros de las Naciones Unidas que serán miembros no permanentes del Consejo de Seguridad, prestando especial atención, en primer término, a la contribución de los Miembros de las Naciones Unidas al mantenimiento de la paz y la seguridad internacionales y a los démas propósitos de la Organización, como tambien a una distribución geográfica equitativa. +Los miembros no permanentes del Consejo de Seguridad serán elegidos por un periodo de dos años. En la primera elección de los miembros no permanentes que se celebre despues de haberse aumentado de once a quince el número de miembros del Consejo de Seguridad, dos de los cuatro miembros nuevos serán elegidos por un periodo de un año. Los miembros salientes no serán reelegibles para el periodo subsiguiente. +Cada miembro del Consejo de Seguridad tendrá un representante. +FUNCIONES y PODERES +Artículo 24 +A fin de asegurar acción rápida y eficaz por parte de las Naciones Unidas, sus Miembros confieren al Consejo de Seguridad la responsabilidad primordial de mantener la paz y la seguridad internacionales, y reconocen que el Consejo de Seguridad actuá a nombre de ellos al desempeñar las funciones que le impone aquella responsabilidad. +En el desempeño de estas funciones, el Consejo de Seguridad procederá de acuerdo con los Propósitos y Principios de las Naciones Unidas. Los poderes otorgados al Consejo de Seguridad para el desempeño de dichas funciones quedan definidos en los Capítulos VI, VII, VIII y XII. +El Consejo de Seguridad presentará a la Asamblea General para su consideración informes anuales y, cuando fuere necesario, informes especiales. +Artículo 25 +Los Miembros de las Naciones Unidas convienen en aceptar y cumplir las decisiones del Consejo de Seguridad de acuerdo con esta Carta. + +Artículo 26 +A fin de promover el establecimiento y mantenimiento de la paz y la seguridad internacionales con la menor desviación posible de los recursos humanos y económicos del mundo hacia los armamentos, el Consejo de Seguridad tendrá a su cargo, con la ayuda del Comité de Estado Mayor a que se refiere e1 Artículo 47, la elaboración de planes que se someterán a los Miembros de las Naciones Unidas para el establecimiento de un sistema de regulación de los armamentos. + +VOTACIÓN +Artículo 27 +Cada miembro del Consejo de Seguridad tendrá un voto. +Las decisiones del Consejo de Seguridad sobre cuestiones de procedimiento serán tomadas por el voto afirmativo de nueve miembros. +Las decisiones del Consejo de Seguridad sobre todas las demás cuestiones serán tomadas por el voto afirmativo de nueve miembros, incluso los votos afirmativos de todos los miembros permanentes; pero en las decisiones tomadas en virtud del Capítulo VI y del párrafo 3 del Artículo 52, la parte en una controversia se abstendrá de votar. +PROCEDIMIENTO +Artículo 28 +El Consejo de Seguridad será organizado de modo que pueda funcionar continuamente. Con tal fin, cada miembro del Consejo de Seguridad tendra en todo momento su representante en la sede de la Organización. +El Consejo de Seguridad celebrará reuniones periódicas en las cuales cada uno de sus miembros podrá, si lo desea, hacerse representar por un miembro de su Gobierno o por otro representante especialmente designado. +El Consejo de Seguridad podrá celebrar reuniones en cualesquiera lugares, fuera de la sede de la Organización, que juzgue más apropiados para facilitar sus labores. +Artículo 29 +El Consejo de Seguridad podrá establecer los organismos subsidiarios que estime necesarios para el desempeño de sus funciones. + +Artículo 30 +El Consejo de Seguridad dictará su propio reglamento, el cual establecerá el método de elegir su Presidente. + +Artículo 31 +Cualquier Miembro de las Naciones Unidas que no sea miembro del Consejo de Seguridad podra participar sin derecho a voto en la discusión de toda cuestión llevada ante el Consejo de Seguridad cuando éste considere que los intereses de ese Miembro están afectados de manera especial. + +Artículo 32 +El Miembro de las Naciones Unidas que no tenga asiento en el Consejo de Seguridad o el Estado que no sea Miembro de las Naciones Unidas, si fuere parte en una controversia que esté considerando el Consejo de Seguridad, será invitado a participar sin derecho a voto en las discusiones relativas a dicha controversia. El Consejo de Seguridad establecerá las condiciones que estime justas para la participación de los Estados que no sean Miembros de las Naciones Unidas. + +Capítulo VI: Arreglo pacífico de controversias +Artículo 33 +Las partes en una controversia cuya continuación sea susceptible de poner en peligro el mantenimiento de la paz y la seguridad internacionales tratarán de buscarle solución, ante todo, mediante la negociación, la investigación, la mediación, la conciliación, el arbitraje, el arreglo judicial, el recurso a organismos o acuerdos regionales u otros medios pacíficos de su elección. +El Consejo de Seguridad, si lo estimare necesario, instará a las partes a que arreglen sus controversias por dichos medios. +Artículo 34 +El Consejo de Seguridad podrá investigar toda controversia, o toda situación susceptible de conducir a fricción internacional o dar origen a una controversia, a fin de determinar si la prolongación de tal controversia o situación puede poner en peligro el mantenimiento de la paz y la seguridad internacionales. + +Artículo 35 +Todo Miembro de las Naciones Unidas podrá llevar cualquiera controversia, o cualquiera situación de la naturaleza expresada en el Artículo 34, a la atención del Consejo de Seguridad o de la Asamblea General. +Un Estado que no es Miembro de las Naciones Unidas podrá llevar a la atención del Consejo de Seguridad o de la Asamblea General toda controversia en que sea parte, si acepta de antemano, en lo relativo a la controversia, las obligaciones de arreglo pacífico establecidas en esta Carta. +El procedimiento que siga la Asamblea General con respecto a asuntos que le sean presentados de acuerdo con este Artículo quedará sujeto a las disposiciones de los Artículos 11 y 12. +Artículo 36 +El Consejo de Seguridad podrá, en cualquier estado en que se encuentre una controversia de la naturaleza de que trata el Artículo 33 o una situación de indole semejante, recomendar los procedimientos o métodos de ajuste que sean apropiados. +El Consejo de Seguridad debera tomar en consideración todo procedimiento que las partes hayan adoptado para el arreglo de la controversia. +Al hacer recomendaciones de acuerdo con este Artículo, el Consejo de Seguridad deberá tomar tambien en consideración que las controversias de orden jurídico, por regla general, deben ser sometidas por las partes a la Corte Internacional de Justicia, de conformidad con las disposiciones del Estatuto de la Corte. +Artículo 37 +Si las partes en una controversia de la naturaleza definida en el Artículo 33 no lograren arreglarla por los medios indicados en dicho Artículo, la someterán al Consejo de Seguridad. +Si el Consejo de Seguridad estimare que la continuación de la controversia es realmente susceptible de poner en peligro el mantenimiento de la paz y la seguridad internacionales, el Consejo decidirá si ha de proceder de conformidad con el Artículo 36 o si ha de recomendar los términos de arreglo que considere apropiados. +Artículo 38 +Sin perjuicio de lo dispuesto en los Artículos 33 a 37, el Consejo de Seguridad podrá, si así lo solicitan todas las partes en una controversia, hacerles recomendaciones a efecto de que se llegue a un arreglo pacífico. + +Capítulo VII: Acción en caso de amenazas a la paz, quebrantamientos de la paz o actos de agresión +Artículo 39 +El Consejo de Seguridad determinará la existencia de toda amenaza a la paz, quebrantamiento de la paz o acto de agresion y hará recomendaciones o decidirá que medidas seran tomadas de conformidad con los Artículos 41 y 42 para mantener o restablecer 1a paz y la seguridad internacionales. + +Artículo 40 +A fin de evitar que la situación se agrave, el Consejo de Seguridad, antes de hacer las recomendaciones o decidir las medidas de que trata el Artículo 39, podrá instar a las partes interesadas a que cumplan con las medidas provisionales que juzgue necesarias o aconsejables. Dichas medidas provisionales no perjudicarán los derechos, las reclamaciones o la posición de las partes interesadas. El Consejo de Seguridad tomará debida nota del incumplimiento de dichas medidas provisionales. + +Artículo 41 +El Consejo de Seguridad podrá decidir qué medidas que no impliquen el uso de la fuerza armada han de emplearse para hacer efectivas sus decisiones, y podrá instar a los Miembros de las Naciones Unidas a que apliquen dichas medidas, que podrán comprender la interrupción total o parcial de las relaciones económicas y de las comunicaciones ferroviarias, marítimas, aéreas, postales, telegráficas, radioeléctricas, y otros medios de comunicación, así como la ruptura de relaciones diplomáticas. + +Artículo 42 +Si el Consejo de Seguridad estimare que las medidas de que trata el Artículo 41 pueden ser inadecuadas o han demostrado serlo, podrá ejercer, por medio de fuerzas aéreas, navales o terrestres, la acción que sea necesaria para mantener o restablecer la paz y la seguridad internacionales. Tal acción podrá comprender demostraciones, bloqueos y otras operaciones ejecutadas por fuerzas aéreas, navales o terrestres de Miembros de las Naciones Unidas. + +Artículo 43 +Todos los Miembros de las Naciones Unidas, con e1 fin de contribuir al mantenimiento de la paz y la seguridad internacionales, se compremeten a poner a disposición del Consejo de Seguridad, cuando éste lo solicite, y de conformidad con un convenio especial o con convenios especiales, las fuerzas armadas, la ayuda y las facilidades, incluso el derecho de paso, que sean necesarias para el propósito de mantener la paz y la seguridad internacionales. +Dicho convenio o convenios fijarán el número y clase de las fuerzas, su grado de preparación y su ublicación general, como también la naturaleza de las facilidades y de la ayuda que habrán de darse. +El convenio o convenios serán negociados a iniciativa del Consejo de Seguridad tan pronto como sea posible; serán concertados entre el Consejo de Seguridad y Miembros individuales o entre el Consejo de Seguridad y grupos de Miembros, y estarán sujetos a ratificación por los Estados signatarios de acuerdo con sus respectivos procedimientos constitucionales. +Artículo 44 +Cuando el Consejo de Seguridad haya decidido hacer uso de la fuerza, antes de requerir a un Miembro que no éste representado en él a que provea fuerzas armadas en cumplimiento de las obligaciones contraídas en virtud del Artículo 43, invitará a dicho Miembro, si éste así lo deseare, a participar en las decisiones del Consejo de Seguridad relativas al empleo de contingentes de fuerzas armadas de dicho Miembro. + +Artículo 45 +A fin de que la Organización pueda tomar medidas militares urgentes, sus Miembros mantendrán contingentes de fuerzas aéreas nacionales inmediatamente disponibles para la ejecución combinada de una acción coercitiva internacional. La potencia y el grado de preparación de estos contingentes y los planes para su acción combinada seran determinados, dentro de los límites establecidos en el convenio o convenios especiales de que trata el Artículo 43, por el Consejo de Seguridad con la ayuda del Comité de Estado Mayor. + +Artículo 46 +Los planes para el empleo de la fuerza armada serán hechos por el Consejo de Seguridad con la ayuda del Comité de Estado Mayor. + +Artículo 47 +Se establecerá un Comité de Estado Mayor para asesorar y asistir al Consejo de Seguridad en todas las cuestiones relativas a las necesidades militares del Consejo para el mantenimiento de la paz y la seguridad internacionales, al empleo y comando de las fuerzas puestas a su disposición, a la regulación de los armamentos y al posible desarme. +El Comité de Estado Mayor estará integrado por los Jefes de Estado Mayor de los miembros permanentes del Consejo de Seguridad o sus representantes. Todo Miembro de las Naciones Unidas que no éste permanentemente representado en el Comite será invitado por éste a asociarse a sus labores cuando el desempeño eficiente de las funciones del Comité requiera la participación de dicho Miembro. +El Comité de Estado Mayor tendrá a su cargo, bajo la autoridad del Consejo de Seguridad, la dirección estratégica de todas las fuerzas armadas puestas a disposición del Consejo. Las cuestiones relativas al comando de dichas fuerzas serán resueltas posteriormente. +El Comite de Estado Mayor, con autorización del Consejo de Seguridad y después de consultar con los organismos regionales apropiados, podrá establecer subcomités regionales. +Artículo 48 +La acción requerida para llevar a cabo las decisiones del Consejo de Seguridad para el mantenimiento de la paz y la seguridad internacionales será ejercida por todos los Miembros de las Naciones Unidas o por algunos de ellos, según lo determine el Consejo de Seguridad. +Dichas decisiones serán llevadas a cabo por los Miembros de las Naciones Unidas directamente y mediante su acción en los organismos internacionales apropiados de que formen parte. +Artículo 49 +Los Miembros de las Naciones Unidas deberán prestarse ayuda mutua para llevar a cabo las medidas dispuestas por el Consejo de Seguridad. + +Artículo 50 +Si el Consejo de Seguridad tomare medidas preventivas o coercitivas contra un Estado, cualquier otro Estado, sea o no Miembro de las Naciones Unidas, que confrontare problemas económicos especiales originados por la ejecución de dichas medidas, tendrá el derecho de consultar al Consejo de Seguridad acerca de la solución de esos problemas. + +Artículo 51 +Ninguna disposición de esta Carta menoscabará el derecho inmanente de legítima defensa, individual o colectiva, en caso de ataque armado contra un Miembro de las Naciones Unidas, hasta tanto que el Consejo de Seguridad haya tomado las medidas necesarias para mantener la paz y la seguridad internacionales. Las medidas tomadas por los Miembros en ejercicio del derecho de legítima defensa serán comunicadas inmediatamente al Consejo de Seguridad, y no afectarán en manera alguna la autoridad y responsabilidad del Consejo conforme a la presente Carta para ejercer en cualquier momento la acción que estime necesaria con el fin de mantener o restablecer la paz y la seguridad internacionales. + +Capítulo VIII: Acuerdos regionales +Artículo 52 +Ninguna disposición de esta Carta se opone a la existencia de acuerdos u organismos regionales cuyo fin sea entender en los asuntos relativos al mantenimiento de la paz y la seguridad internacionales y susceptibles de acción regional, siempre que dichos acuerdos u organismos, y sus actividades, sean compatibles con los Propósitos y Principios de las Naciones Unidas. +Los Miembros de las Naciones Unidas que sean partes en dichos acuerdos o que constituyan dichos organismos, harán todos los esfuerzos posibles para lograr el arreglo pacífico de las controversias de caracter local por medio de tales acuerdos u organismos regionales antes de someterlas al Consejo de Seguridad. +El Consejo de Seguridad promoverá el desarrollo del arreglo pacífico de las controversias de carácter local por medio de dichos acuerdos u organismos regionales, procediendo, bien a iniciativa de los Estados interesados, bien a instancia del Consejo de Seguridad. +Este Artículo no afecta en manera a1guna la aplicación de los Artículos 34 y 35. +Artículo 53 +El Consejo de Seguridad utilizará dichos acuerdos u organismos regionales, si a ello hubiere lugar, para aplicar medidas coercitivas bajo su autoridad. Sin embargo, no se aplicarán medidas coercitivas en virtud de acuerdos regionales o por organismos regionales sin autorización del Consejo de Seguridad, salvo que contra Estados enemigos, según se les define en el párrafo 2 de este Artículo, se tomen las medidas dispuestas en virtud del Artículo 107 o en acuerdos regionales dirigidos contra la renovación de una política de agresión de parte de dichos Estados, hasta tanto que a solicitud de los gobiernos interesados quede a cargo de la Organización la responsabi1idad de prevenir nuevas agresiones de parte de aquellos Estados. +El término "Estados enemigos" empleado en el párrafo 1 de este Artículo se aplica a todo Estado que durante la segunda guerra mundial haya sido enemigo de cualquiera de los signatarios de esta Carta. +Artículo 54 +Se deberá mantener en todo tiempo al Consejo de Seguridad plenamente informado de las actividades emprendidas o proyectadas de conformidad con acuerdos regionales o por organismos regionales con el propósito de mantener la paz y la seguridad internacionales. + +Capítulo IX: Cooperación internacional económica y social +Artículo 55 +Con el propósito de crear las condiciones de estabilidad y bienestar necesarias para las relaciones pacíficas y amistosas entre las naciones, basadas en el respeto al principio de la igualdad de derechos y al de la libre determinación de los pueblos, la Organización promoverá: + +niveles de vida más elevados, trabajo permanente para todos, y condiciones de progreso y desarrollo económico y social; +la solución de problemas internacionales de carácter económico, social y sanitario, y de otros problemas conexos; y la cooperación internacional en el orden cultural y educativo; y +el respeto universal a los derechos humanos y a las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión, y la efectividad de tales derechos y libertades. +Artículo 56 +Todos los Miembros se comprometen a tomar medidas conjunta o separadamente, en cooperación con la Organización, para la realización de los propósitos consignados en el Artículo 55. + +Artículo 57 +Los distintos organismos especializados establecidos por acuerdos intergubernamentales, que tengan amplias atribuciones internacionales definidas en sus estatutos, y relativas a materias de carácter económico, social, cultural, educativo, sanitario, y otras conexas, serán vinculados con la Organización de acuerdo con las disposiciones del Artículo 63. +Tales organismos especializados así vinculados con la Organización se denominarán en adelante "los organismos especializados". +Artículo 58 +La Organización hará recomendaciones con el objeto de coordinar las normas de acción y las actividades de los organismos especializados. + +Artículo 59 +La Organización iniciará, cuando hubiere lugar, negociaciones entre los Estados interesados para crear los nuevos organismos especializados que fueren necesarios para la realización de los propósitos enunciados en el Artículo 55. + +Artículo 60 +La responsabilidad por el desempeño de las funciones de la Organización señaladas en este Capítulo corresponderá a la Asamblea General y, bajo la autoridad de ésta, al Consejo Económico y Social, que dispondrá a este efecto de las facultades expresadas en el Capítulo X. + +Capítulo X: El Consejo Económico y Social +COMPOSICIÓN +Artículo 61 +El Consejo Económico y Social estará integrado por cincuenta y cuatro Miembros de las Naciones Unidas elegidos por la Asamblea General. +Salvo lo prescrito en el párrafo 3, dieciocho miembros del Consejo Económico y Social serán elegidos cada año por un periodo de tres años. Los miembros salientes serán reelegibles para el periodo subsiguiente. +En la primera elección que se celebre después de haberse aumentado de veintisiete a cincuenta y cuatro el número de miembros del Consejo Económico y Social, además de los miembros que se elijan para sustituir a los nueve miembros cuyo mandato expire al final de ese año, se elegirán veintisiete miembros más. El mandato de nueve de estos veintisiete miembros adicionales asi elegidos expirara al cabo de un año y el de otros nueve miembros una vez transcurridos dos años, conforme a las disposiciones que dicte la Asamblea General. +Cada miembro del Consejo Económico y Social tendrá un representante. +FUNCIONES y PODERES +Artículo 62 +El Consejo Econó:mico y Social podrá hacer o iniciar estudios e informes con respecto a asuntos internacionales de carár económico, social, cultural, educativo y sanitario, y otros asuntos conexos, y hacer recomendaciones sobre tales asuntos a la Asamblea General, a los Miembros de las Naciones Unidas y a los organismos especializados interados. +El Consejo Económico y Social podrá hacer recomendaciones con el objeto de promover el respeto a los derechos humanos y a las libertades fundamentales de todos, y la efectividad de tales derechos y libertades. +El Consejo Económico y Social podrá formular proyectos de convención con respecto a cuestiones de su competencia para someterlos a la Asamblea General. +El Consejo Económico y Social podrá convocar, conforme a las reglas que prescriba la Organización, conferencias internacionales sobre asuntos de su competencia. +Artículo 63 +El Consejo Económico y Social podrá concertar con cualquiera de los organismos especializados de que trata el Artículo 57, acuerdos por medio de los cuales se establezcan las condiciones en que dichos organismos habrán de vincularse con la Organización. Tales acuerdos estarán sujetos a la aprobación de la Asamblea General. +El Consejo Económico y Social podrá coordinar las actividades de los organismos especializados mediante consultas con ellos y haciéndoles recomendaciones, como también mediante recomendaciones a la Asamblea General y a los Miembros de las Naciones Unidas. +Artículo 64 +El Consejo Económico y Social podrá tomar las medidas apropiadas para obtener informes periódicos de los organismos especializados. También podrá hacer arreglos con los Miembros de las Naciones Unidas y con los organismos especializados para obtener informes con respecto a los medidas tomadas para hacer efectivas sus propias recomendaciones y las que haga la Asamblea General acerca de materias de la competencia del Consejo. +El Consejo Económico y Social podrá comunicar a la Asamblea General sus observaciones sobre dichos informes. +Artículo 65 +El Consejo Económico y Social podrá suministrar información a1 Consejo de Seguridad y deberá darle la ayuda que éste le solicite. + +Artículo 66 +El Consejo Económico y Social desempeñará las funciones que caigan dentro de su competencia en relación con el cumplimiento de las recomendaciones de la Asamblea General. +El Consejo Económico y Social podrá prestar, con aprobación de la Asamblea General, los servicios que le soliciten los Miembros de las Naciones Unidas y los organismos especializados. +El Consejo Económico y Social desempeñará las demás funciones prescritas en otras partes de esta Carta o que le asignare la Asamblea General. +VOTACIÓN +Artículo 67 +Cada miembro del Consejo Económico y Social tendrá un voto. +Las decisiones del Consejo Económico y Social se tomarán por la mayoría de los miembros presentes y votantes. +PROCEDIMIENTO +Artículo 68 +El Consejo Económico y Social establecerá comisiones de orden económico y social y para la promoción de los derechos humanos, así como las demás comisiones necesarias para el desempeño de sus funciones. + +Artículo 69 +El Consejo Económico y Social invitará a cualquier Miembro de las Naciones Unidas a participar, sin derecho a voto, en sus deliberaciones sobre cualquier asunto de particular interés para dicho Miembro. + +Artículo 70 +El Consejo Económico y Social podrá hacer arreglos para que representantes de los organismos especializados participen, sin derecho a voto, en sus deliberaciones y en las de las comisiones que establezca, y para que sus propios representantes participen en las deliberaciones de aquellos organismos. + +Artículo 71 +El Consejo Económico y Social podrá hacer arreglos adecuados para celebrar consultas con organizaciones no gubernamentales que se ocupen en asuntos de la competencia del Consejo. Podrán hacerse dichos arreglos con organizaciones internacionales y, si a ello hubiere lugar, con organizaciones nacionales, previa consulta con el respectivo Miembro de las Naciones Unidas. + +Artículo 72 +El Consejo Económico y Social dictará su propio reglamento, el cual establecerá el método de elegir su Presidente. +El Consejo Económico y Social se reunirá cuando sea necesario de acuerdo con su reglamento, el cual incluirá disposiciones para la convocación a sesiones cuando lo solicite una mayoría de sus miembros. +Capítulo XI: Declaración relativa a territorios no autónomos +Artículo 73 +Los Miembros de las Naciones Unidas que tengan o asuman la responsabilidad de administrar territorios cuyos pueblos no hayan alcanzado todavía la plenitud del gobierno propio, reconocen el principio de que los intereses de los habitantes de esos territorios están por encima de todo, aceptan como un encargo sagrado la obligación de promover en todo lo posible, dentro del sistema de paz y de seguridad internacionales establecido por esta Carta, el bienestar de los habitantes de esos territorios, y asimismo se obligan: + +a asegurar, con el debido respeto a la cultura de los pueblos respectivos, su adelanto político, económico, social y educativo, el justo tratamiento de dichos pueblos y su protección contra todo abuso; +a desarrollar el gobierno propio, a tener debidamente en cuenta las aspiraciones políticas de los pueblos, y a ayudarlos en el desenvolvimiento progresivo de sus libres instituciones políticas, de acuerdo con las circunstancias especiales de cada territorio, de sus pueblos y de sus distintos grados de adelanto; +a promover la paz y la seguridad internacionales; +a promover medidas constructivas de desarrollo, estimular la investigación, y cooperar unos con otros y, cuando y donde fuere del caso, con organismos internacionales especializados, para conseguir la realización práctica de los propósitos de carácter social, económico y científico expresados en este Artículo; y +a transmitir regularmente al Secretario General, a título informativo y dentro de los límites que la seguridad y consideraciones de orden constitucional requieran, la información estadística y de cualquier otra naturaleza técnica que verse sobre las condiciones económicas, sociales y educativas de los territorios por los cuales son respectivamente responsables, que no sean de los territorios a que se refieren los Capítulos XII y XIII de esta Carta. +Artículo 74 +Los Miembros de las Naciones Unidas convienen igualmente en que su política con respecto a los territorios a que se refiere este Capitulo, no menos que con respecto a sus territorios metropolitanos, debera fundarse en el principio general de la buena vecindad, teniendo debidamente en cuenta los intereses y el bienestar del resto del mundo en cuestiones de carácter social, económico y comercial. + +Capítulo XII: Régimen internacional de administración fiduciaria +Artículo 75 +La Organización establecerá bajo su autoridad un régimen internacional de administración fiduciaria para la administración y vigilancia de los territorios que puedan colocarse bajo dicho régimen en virtud de acuerdos especiales posteriores. A dichos territorios se les denominará territorios fideicometidos. + +Artículo 76 +Los objetivos básicos del régimen de administración fiduciaria, de acuerdo con los Propósitos de las Naciones Unidas enunciados en el Artículo 1 de esta Carta, serán: + +fomentar la paz y la seguridad internacionales; +promover el adelanto político, económico, social y educativo de los habitantes de los territorios fideicometidos, y su desarrollo progresivo hacia el gobierno propio o la independencia, teniéndose en cuenta las circunstancias particulares de cada territorio y de sus pueblos y los deseos libremente expresados de los pueblos interesados, y según se dispusiere en cada acuerdo sobre administración fiduciaria; +promover el respeto a los derechos humanos y a las libertades fundamentales de todos, sin hacer distinción por motivos de raza, sexo, idioma o religión, así como el reconocimiento de la interdependencia de los pueblos del mundo; y +asegurar tratamiento igual para todos los Miembros de las Naciones Unidas y sus nacionales en materias de carácter social, económico y comercial, así como tratamiento igual para dichos nacionales en la administración de la justicia, sin perjuicio de la realización de los objetivos arriba expuestos y con sujeción a las disposiciones del Artículo 80. +Artículo 77 +El régimen de administración fiduciaria se aplicará a los territorios de las siguientes categorías que se colocaren bajo dicho régimen por medio de los correspondientes acuerdos: +territorios actualmente bajo mandato; +territorios que, como resultado de la segunda guerra mundial, fueren segregados de Estados enemigos, y +territorios voluntariamente colocados bajo este régimen por los Estados responsables de su administración. +Será objeto de acuerdo posterior el determinar cuáles territorios de las categorías anteriormente mencionadas seran colocados bajo el régimen de administración fiduciaria y en qué condiciones. +Artículo 78 +El régimen de administración fiduciaria no se aplicará a territorios que hayan adquirido la calidad de Miembros de las Naciones Unidas, cuyas relaciones entre sí se basarán en el respeto al principio de la igualdad soberana. + +Artículo 79 +Los términos de la administración fiduciaria para cada territorio que haya de colocarse bajo el régimen expresado, y cualquier modificación o reforma, deberán ser acordados por los Estados directamente interesados, incluso la potencia mandataria en el caso de territorios bajo mandato de un Miembro de las Naciones Unidas, y serán aprobados según se dispone en los Artículos 83 y 85. + +Artículo 80 +Salvo lo que se conviniere en los acuerdos especiales sobre administración fiduciaria concertados de conformidad con los Artículos 77, 79 y 81 y mediante los cuales se coloque cada territorio bajo el régimen de administración fiduciaria, y hasta tanto se coIlcierten tales acuerdos, ninguna disposición de este Capítulo será interpretada en el sentido de que modifica en manera alguna los derechos de cualesquiera Estados o pueblos, o los términos de los instrumentos internacionales vigentes en que sean partes Miembros de los Naciones Unidas. +El párrafo 1 de este Artículo no será interpretado en el sentido de que da motivo para demorar o diferir la negociación y celebración de acuerdos para aplicar el régimen de administración fiduciaria a territorios bajo mandato y otros territorios, conforme al Artículo 77. +Artículo 81 +El acuerdo sobre administración fiduciaria contendrá en cada caso las condiciones en que se administrará el territorio fideicometido, y designará la autoridad que ha de ejercer la administración. Dicha autoridad, que en lo sucesivo se denominará la "autoridad administradora", podrá ser uno o más Estados o la misma Organización. + +Artículo 82 +Podrán designarse en cualquier acuerdo sobre administración fiduciaria, una o varias zonas estratégicas que comprendan parte o la totalidad del territorio fideicometido a que se refiera el acuerdo, sin perjuicio de los acuerdos especiales celebrados con arreglo al Artículo 43. + +Artículo 83 +Todas las funciones de las Naciones Unidas relativas a zonas estratégicas, incluso la de aprobar los términos de los acuerdos sobre administración fiduciaria y de las modificaciones o reformas de los mismos, serán ejercidas por el Consejo de Seguridad. +Los objetivos básicos enunciados en el Artículo 76 serán aplicables a la población de cada zona estratégica. +Salvo las disposiciones de los acuerdos sobre administración fiduciaria y sin perjuicio de las exigencias de la seguridad, el Consejo de Seguridad aprovechará la ayuda del Consejo de Administración Fiduciaria para desempeñar, en las zonas estratégicas, aquellas funciones de la Organización relativas a materias políticas, económicas, sociales y educativas que correspondan al régimen de administración fiduciaria. +Artículo 84 +La autoridad administradora tendrá el deber de velar por que el territorio fideicometido contribuya al mantenimiento de la paz y la seguridad internacionales. Con tal fin, la autoridad administradora podrá hacer uso de las fuerzas voluntarias, de las facilidades y de la ayuda del citado territorio, a efecto de cumplir con las obligaciones por ella contraídas a este respecto ante el Consejo de Seguridad, como también para la defensa local y el mantenimiento de la ley y del orden dentro del territorio fideicometido. + +Artículo 85 +Las funciones de la Organización en lo que respecta a los acuerdos sobre administración fiduciaria relativos a todas las zonas no designadas como estratégicas, incluso la de aprobar los términos de los acuerdos y las modificaciones o reformas de los mismos serán ejercidas por la Asamblea General. +El Consejo de Administración Fiduciaria, bajo la autoridad de la Asamblea General, ayudará a ésta en el desempeño de las funciones aquí enumeradas. +Capítulo XIII: El Consejo de Administración Fiduciaria +COMPOSICIÓN +Artículo 86 +El Consejo de Administración Fiduciaria estará integrado por los siguientes Miembros de las Naciones Unidas: +los Miembros que administren territorios fideicometidos; +los Miembros mencionados por su nombre en el Artículo 23 que no estén administrando territorios fideicometidos; y +tantos otros Miembros elegidos por periodos de tres años por la Asamblea General cuantos sean necesarios para asegurar que el número total de miembros del Consejo de Administración Fiduciaria se divida por igual entre los Miembros de las Naciones Unidas administradores de tales territorios y los no administradores. +Cada miembro del Consejo de Administración Fiduciaria designará a una persona especialmente calificada para que lo represente en el Consejo. +FUNCIONES Y PODERES +Artículo 87 +En el desempeño de sus funciones, la Asamblea General y, bajo su autoridad, el Consejo de Administración Fiduciaria, podrán: + +considerar informes que les haya rendido la autoridad administradora; +aceptar peticiones y examinarlas en consulta con la autoridad administradora; +disponer visitas periódicas a los territorios fideicometidos en fechas convenidas con la autoridad administradora; y +tomar estas y otras medidas de conformidad con los términos de los acuerdos sobre administración fiduciaria. +Artículo 88 +El Consejo de Administración Fiduciaria formulará un cuestionario sobre el adelanto político, económico, social y educativo de los habitantes de cada territorio fideicometido; y la autoridad administradora de cada territorio fideicometido dentro de la competencia de la Asamblea General, rendirá a ésta un informe anual sobre 1a base de dicho cuestionario. + +VOTACIÓN +Artículo 89 +Cada miembro del Consejo de Administración Fiduciaria tendra un voto. +Las decisiones del Consejo de Administración Fiduciaria serán tomadas por el voto de la mayoría de los miembros presentes y votantes. +PROCEDIMIENTO +Artículo 90 +El Consejo de Administración Fiduciaria dictará su propio reglamento, el cual establecerá el método de elegir su Presidente. +El Consejo de Administración Fiduciaria se reunirá cuando sea necesario, según su reglamento. Este contendrá disposiciones sobre convocación del Consejo a solicitud de la mayoría de sus miembros. +Artículo 91 +El Consejo de Administración Fiduciaria, cuando lo estime conveniente, se valdrá de la ayuda del Consejo Económico y Social y de la de los organismos especializados con respecto a los asuntos de la respectiva competencia de los mismos. + +Capítulo XIV: La Corte Internacional de Justicia +Artículo 92 +La Corte Internacional de Justicia será el órgano judicial principal de las Naciones Unidas; funcionará de conformidad con el Estatuto anexo, que está basado en el de la Corte Permanente de Justicia Internacional, y que forma parte integrante de esta Carta. + +Artículo 93 +Todos los Miembros de las Naciones Unidas son ipso facto partes en el Estatuto de la Corte Internacional de Justicia. +Un Estado que no sea Miembro de las Naciones Unidas podrá llegar a ser parte en el Estatuto de la Corte Internacional de Justicia, de acuerdo con las condiciones que determine en cada caso la Asamblea General a recomendación del Consejo de Seguridad. +Artículo 94 +Cada Miembro de las Naciones Unidas compromete a cumplir la decisión de la Corte Internacional de Justicia en todo litigio en que sea parte. +Si una de las partes en un litigio dejare de cumplir las obligaciones que le imponga un fallo de la Corte, la otra parte podrá recurrir al Consejo de Seguridad, el cual podrá, si lo cree necesario, hacer recomendaciones o dictar medidas con el objeto de que se lleve a efecto la ejecución del fallo. +Artículo 95 +Ninguna de las disposiciones de esta Carta impedirá a los Miembros de las Naciones Unidas encomendar la solución de sus diferencias a otros tribunales en virtud de acuerdos ya existentes o que puedan concertarse en el futuro. + +Artículo 96 +La Asamblea General o el Consejo de Seguridad podrán solicitar de la Corte Internacional de Justicia que emita una opinión consultiva sobre cualquier cuestión jurídica. +Los otros órganos de las Naciones Unidas y los organismos especializados que en cualquier momento sean autorizados para ello por la Asamblea General, podrán igualmente solicitar de la Corte opiniones consultivas sobre cuestiones jurídicas que surjan dentro de la esfera de sus actividades. +Capítulo XV: La Secretaría +Artículo 97 +La Secretaría se compondrá de un Secretario General y del personal que requiera la Organización. El Secretario General será nombrado por la Asamblea General a recomendación del Consejo de Seguridad. El Secretario General sera el más alto funcionario administrativo de la Organización. + +Artículo 98 +El Secretario General actuará como tal en todas las sesiones de la Asamblea General, del Consejo de Seguridad, del Consejo Económico y Social y del Consejo de Administración Fiduciaria, y desempeñara las demas funciones que le encomienden dichos órganos. El Secretario General rendirá a la Asamblea General un informe anual sobre las actividades de la Organización. + +Artículo 99 +El Secretario General podrá llamar la atención del Consejo de Seguridad hacia cualquier asunto que en su opinión pueda poner en peligro el mantenimiento de la paz y la seguridad internacionales. + +Artículo 100 +En el cumplimiento de sus deberes, el Secretario General y el personal de la Secretaría no solicitarán ni recibirán instrucciones de ningún gobierno ni de ninguna autoridad ajena a la Organización, y se abstendrán de actuar en forma alguna que sea incompatible con su condición de funcionarios internacionales responsables únicamente ante la Organización. +Cada uno de los Miembros de las Naciones Unidas se compromete a respetar el carácter exclusivamente internacional de las funciones del Secretario General y del personal de la Secretaría, y a no tratar de influir sobre ellos en el desempeño de sus funciones. +Artículo 101 +El personal de la Secretaría será nombrado por el Secretario General de acuerdo con las reglas establecidas por la Asamblea General. +Se asignará permanentemente personal adecuado al Consejo Económico y Social, al Consejo de Administración Fiduciaria y, según se requiera, a otros órganos de las Naciones Unidas. Este personal formará parte de la Secretaría. +La consideración primordial que se tendrá en cuenta al nombrar el personal de la Secretaría y al determinar las condiciones del servicio, es la necesidad de asegurar el más alto grado de eficiencia, competencia e integridad. Se dará debida consideración también a la importancia de contratar el personal en forma de que haya la más amplia representación geográfica posible. +Capítulo XVI: Disposiciones varias +Artículo 102 +Todo tratado y todo acuerdo internacional concertados por cualesquiera Miembros de las Naciones Unidas después de entrar en vigor esta Carta, serán registrados en la Secretaría y publicados por ésta a la mayor brevedad posible. +Ninguna de las partes en un tratado o acuerdo internacional que no haya sido registrado conforme a las disposiciones del párrafo 1 de este Artículo, podrá invocar dicho tratado o acuerdo ante órgano alguno de las Naciones Unidas. +Artículo 103 +En caso de conflicto entre las obligaciones contraídas por los Miembros de las Naciones Unidas en virtud de la presente Carta y sus obligaciones contraídas en virtud de cualquier otro convenio internacional, prevalecerán las obligaciones impuestas por la presente Carta. + +Artículo 104 +La Organización gozará, en el territorio de cada uno de sus Miembros, de la capacidad jurídica que sea necesaria para el ejercicio de sus funciones y la realización de sus propósitos. + +Artículo 105 +La Organización gozará, en el territorio de cada uno de sus Miembros, de los privilegios e inmunidades necesarios para la realización de sus propósitos. +Los representantes de los Miembros de la Organización y los funcionarios de ésta, gozarán asimismo de los privilegios e inmunidades necesarios para desempeñar con independencia sus funciones en relación con la Organización. +La Asamblea General podrá hacer recomendaciones con el objeto de determinar los pormenores de la aplicación de los párrafos 1 y 2 de este Artículo, o proponer convenciones a los Miembros de las Naciones Unidas con el mismo objeto. +Capítulo XVII: Acuerdos transitorios sobre seguridad +Artículo 106 +Mientras entran en vigor los convenios especiales previstos en el Artículo 43, que a juicio del Consejo de Seguridad lo capaciten para ejercer las atribuciones a que se refiere el Artículo 42, las partes en la Declaración de las Cuatro Potencias firmada en Moscú el 30 de octubre de 1943, y Francia, deberán, conforme a las disposiciones del párrafo 5 de esa Declaración, celebrar consultas entre sí, y cuando a ello hubiere lugar, con otros miembros de la Organización, a fin de acordar en nombre de ésta la acción conjunta que fuere necesaria para mantener la paz y la seguridad internacionales. + +Artículo 107 +Ninguna de las disposiciones de esta Carta invalidará o impedirá cualquier acción ejercida o autorizada como resultado de la segunda guerra mundial con respecto a un Estado enemigo de cualquiera de los signatarios de esta Carta durante la citada guerra, por los gobiernos responsables de dicha acción. + +Capítulo XVIII: Reformas +Artículo 108 +Las reformas a la presente Carta entrarán en vigor para todos los Miembros de las Naciones Unidas cuando hayan sido adoptadas por el voto de las dos terceras partes de los miembros de la Asamblea General y ratificadas, de conformidad con sus respectivos procedimientos constitucionales, por las dos terceras partes de los Miembros de las Naciones Unidas, incluyendo a todos los miembros permanentes del Consejo de Seguridad. + +Artículo 109 +Se podrá celebrar una Conferencia General de los Miembros de las Naciones Unidas con el propósito de revisar esta Carta, en la fecha y lugar que se determinen por el voto de las dos terceras partes de los miembros de la Asamblea General y por el voto de cualesquiera nueve miembros del Consejo de Seguridad. Cada Miembro de las Naciones Unidas tendrá un voto en la Conferencia. +Toda modificación de esta Carta recomendada por el voto de las dos terceras partes de la Conferencia entrará en vigor al ser ratificada de acuerdo con sus respectivos procedimientos constitucionales, por las dos terceras partes de los Miembros de las Naciones Unidas, incluyendo a todos los miembros permanentes del Consejo de Seguridad. +Si no se hubiere celebrado tal Conferencia antes de la décima reunión anual de la Asamblea General despues de entrar en vigor esta Carta, la proposición de convocar tal Conferencia será puesta en la agenda de dicha reunión de la Asamblea General, y la Conferencia será celebrada si así lo decidieren la mayoría de los miembros de la Asamblea General y siete miembros cualesquiera del Consejo de Seguridad. +Capítulo XIX: Ratificación y firma +Artículo 110 +La presente Carta será ratificada por los Estados signatorios de acuerdo con sus respectivos procedimientos constitucionales. +Las ratificaciones serán entregadas para su depósito al Gobierno de los Estados Unidos de América, el cual notificará cada depósito a todos los Estados signatarios así como al Secretario General de la Organización cuando haya sido designado. +La presente Carta entrará en vigor tan pronto como hayan sido depositadas las ratificaciones de la República de China, Francia, la Unión de las Repúblicas Socialistas Soviéticas, el Reino Unido de la Gran Bretaña e Irlanda del Norte y los Estados Unidos de América, y por la mayoría de los demás Estados signatarios. Acto seguido se dejará constancia de las ratificaciones depositadas en un protocolo que extenderá el Gobierno de los Estados Unidos de América, y del cual transmitirá copias a todos los Estados signatarios. +Los Estados signatarios de esta Carta que la ratifiquen después que haya entrado en vigor adquirirán la calidad de miembros originarios de las Naciones Unidas en la fecha del depósito de sus respectivas ratificaciones. +Artículo 111 +La presente Carta, cuyos textos en chino, francés, ruso, inglés y español son igualmente auténticos, será depositada en los archivos del Gobierno de los Estados Unidos de América. Dicho Gobierno enviará copias debidamente certificadas de la misma a los Gobiernos de los demás Estados signatarios. + +En fe de lo cual los Representantes de los Gobiernos de las Naciones Unidas han suscrito esta Carta. Firmada en la ciudad de San Francisco, a los veintiséis días del mes de junio de mil novecientos cuarenta y cinco. \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_RU.html b/stdlib/benchmarks/collections/data/UN_charter_RU.html new file mode 100644 index 0000000000..65617aadfe --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_RU.html @@ -0,0 +1,2323 @@ + + + + + + + + + + + + + + + + + + + Устав ООН (полный текст) | Организация Объединенных Наций + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    + + + + +
    +
    + + +
    + + + + + + + + + + +
    +
    +
    + +
    + + + +

    Устав ООН (полный текст)

    + + + + +
    +
    + + +
    + + + + +
    +
    +
    +
    +

    Преамбула

    + +

    МЫ, НАРОДЫ ОБЪЕДИНЕННЫХ НАЦИЙ, ПРЕИСПОЛНЕННЫЕ РЕШИМОСТИ

    + +

    избавить грядущие поколения от бедствий войны, дважды в нашей жизни принесшей человечеству невыразимое горе, и

    + +

    вновь утвердить веру в основные права человека, в достоинство и ценность человеческой личности, в равноправие мужчин и женщин и в равенство прав больших и малых наций, и

    + +

    создать условия, при которых могут соблюдаться справедливость и уважение к обязательствам, вытекающим из договоров и других источников международного права, и

    + +

    содействовать социальному прогрессу и улучшению условий жизни при большей свободе,

    + +

    И В ЭТИХ ЦЕЛЯХ

    + +

    проявлять терпимость и жить вместе, в мире друг с другом, как добрые соседи, и

    + +

    объединить наши силы для поддержания международного мира и безопасности, и 

    + +

    обеспечить принятием принципов и установлением методов, чтобы вооруженные силы применялись не иначе, как в общих интересах, и 

    + +

    использовать международный аппарат для содействия экономическому и социальному прогрессу всех народов, 

    + +

    РЕШИЛИ ОБЪЕДИНИТЬ НАШИ УСИЛИЯ ДЛЯ ДОСТИЖЕНИЯ ЭТИХ ЦЕЛЕЙ.

    + +

    Согласно этому наши соответственные правительства через представителей, собравшихся в городе Сан-Франциско, предъявивших свои полномочия, найденные в надлежащей форме, согласились принять настоящий Устав Организации Объединенных Наций и настоящим учреждают международную организацию под названием «Объединенные Нации».

    + +

    Глава I: Цели и Принципы

    + +

    Статья 1

    + +

    Организация Объединенных Наций преследует Цели:

    + +
      +
    1. Поддерживать международный мир и безопасность и с этой целью принимать эффективные коллективные меры для предотвращения и устранения угрозы миру и подавления актов агрессии или других нарушений мира и проводить мирными средствами, в согласии с принципами справедливости и международного права, улаживание или разрешение международных споров или ситуаций, которые могут привести к нарушению мира;
    2. +
    3. Развивать дружественные отношения между нациями на основе уважения принципа равноправия и самоопределения народов, а также принимать другие соответствующие меры для укрепления всеобщего мира;
    4. +
    5. Осуществлять международное сотрудничество в разрешении международных проблем экономического, социального, культурного и гуманитарного характера и в поощрении и развитии уважения к правам человека и основным свободам для всех, без различия расы, пола, языка и религии, и
    6. +
    7. Быть центром для согласования действий наций в достижении этих общих целей.
    8. +
    + +

    Статья 2

    + +

    Для достижения целей, указанных в статье 1, Организация и ее Члены действуют в соответствии со следующими Принципами:

    + +
      +
    1. Организация основана на принципе суверенного равенства всех ее Членов;
    2. +
    3. Все Члены Организации Объединенных Наций добросовестно выполняют принятые на себя по настоящему Уставу обязательства, чтобы обеспечить им всем в совокупности права и преимущества, вытекающие из принадлежности к составу Членов Организации;
    4. +
    5. Все Члены Организации Объединенных Наций разрешают свои международные споры мирными средствами таким образом, чтобы не подвергать угрозе международный мир и безопасность и справедливость;
    6. +
    7. Все Члены Организации Объединенных Наций воздерживаются в их международных отношениях от угрозы силой или ее применения как против территориальной неприкосновенности или политической независимости любого государства, так и каким-либо другим образом, несовместимым с Целями Объединенных Наций;
    8. +
    9. Все Члены Организации Объединенных Наций оказывают ей всемерную помощь во всех действиях, предпринимаемых ею в соответствии с настоящим Уставом, и воздерживаются от оказания помощи любому государству, против которого Организация Объединенных Наций предпринимает действия превентивного или принудительного характера;
    10. +
    11. Организация обеспечивает, чтобы государства, которые не являются ее Членами, действовали в соответствии с этими Принципами, поскольку это может оказаться необходимым для поддержания международного мира и безопасности;
    12. +
    13. Настоящий Устав ни в коей мере не дает Организации Объединенных Наций права на вмешательство в дела, по существу входящие во внутреннюю компетенцию любого государства, и не требует от Членов Организации Объединенных Наций представлять такие дела на разрешение в порядке настоящего Устава; однако этот принцип не затрагивает применения принудительных мер на основании Главы VII.
    14. +
    + +

    Глава II: Члены Организации

    + +

    Статья 3

    + +

    Первоначальными Членами Организации Объединенных Наций являются государства, которые, приняв участие в Конференции в Сан-Франциско по созданию Международной Организации или ранее подписав Декларацию Объединенных Наций от 1 января 1942 года, подписали и ратифицировали настоящий Устав в соответствии со статьей 110.

    + +

    Статья 4

    + +
      +
    1. Прием в Члены Организации открыт для всех других миролюбивых государств, которые примут на себя содержащиеся в настоящем Уставе обязательства и которые, по суждению Организации, могут и желают эти обязательства выполнять.
    2. +
    3. Прием любого такого государства в Члены Организации производится постановлением Генеральной Ассамблеи по рекомендации Совета Безопасности.
    4. +
    + +

    Статья 5

    + +

    Если против какого-либо Члена Организации были предприняты Советом Безопасности действия превентивного или принудительного характера, Генеральная Ассамблея имеет право, по рекомендации Совета Безопасности, приостанавливать осуществление прав и привилегий, принадлежащих ему как Члену Организации. Осуществление этих прав и привилегий может быть восстановлено Советом Безопасности.

    + +

    Статья 6

    + +

    Член Организации, систематически нарушающий принципы, содержащиеся в настоящем Уставе, может быть исключен из Организации Генеральной Ассамблеей по рекомендации Совета Безопасности.

    + +

    Глава III: Органы

    + +

    Статья 7

    + +
      +
    1. В качестве главных органов Организации Объединенных Наций учреждаются: Генеральная Ассамблея, Совет Безопасности, Экономический и Социальный Совет, Совет по Опеке, Международный Суд и Секретариат.
    2. +
    3. Вспомогательные органы, которые окажутся необходимыми, могут учреждаться в соответствии с настоящим Уставом.
    4. +
    + +

    Статья 8

    + +

    Организация Объединенных Наций не устанавливает никаких ограничений в отношении права мужчин и женщин участвовать в любом качестве и на равных условиях в ее главных и вспомогательных органах.

    + +

    Глава IV: Генеральная Ассамблея

    + +

    СОСТАВ

    + +

    Статья 9

    + +
      +
    1. Генеральная Ассамблея состоит из всех Членов Организации.
    2. +
    3. Каждый Член Организации имеет не более пяти представителей в Генеральной Ассамблее.
    4. +
    + +

    ФУНКЦИИ и ПОЛНОМОЧИЯ

    + +

    Статья 10

    + +

    Генеральная Ассамблея уполномочивается обсуждать любые вопросы или дела в пределах настоящего Устава или относящиеся к полномочиям и функциям любого из органов, предусмотренных настоящим Уставом, и, за исключениями, предусмотренными статьей 12, делать рекомендации Членам Организации Объединенных Наций или Совету Безопасности или и Членам Организации и Совету Безопасности по любым таким вопросам или делам.

    + +

    Статья 11

    + +
      +
    1. Генеральная Ассамблея уполномочивается рассматривать общие принципы сотрудничества в деле поддержания международного мира и безопасности, в том числе принципы, определяющие разоружение и регулирование вооружений, и делать в отношении этих принципов рекомендации Членам Организации или Совету Безопасности или и Членам Организации и Совету Безопасности.
    2. +
    3. Генеральная Ассамблея уполномочивается обсуждать любые вопросы, относящиеся к поддержанию международного мира и безопасности, поставленные перед нею любым Членом Организации или Советом Безопасности или государством, которое не является Членом Организации, в соответствии с пунктом 2 статьи 35, и за исключениями, предусмотренными статьей 12, делать в отношении любых таких вопросов рекомендации заинтересованному государству или государствам или Совету Безопасности или и Совету Безопасности и заинтересованному государству или государствам. Любой такой вопрос, по которому необходимо предпринять действие, передается Генеральной Ассамблеей Совету Безопасности до или после обсуждения.
    4. +
    5. Генеральная Ассамблея может обращать внимание Совета Безопасности на ситуации, которые могли бы угрожать международному миру и безопасности.
    6. +
    7. Полномочия Генеральной Ассамблеи, изложенные в настоящей статье, не должны ограничивать общего смысла статьи 10.
    8. +
    + +

    Статья 12

    + +
      +
    1. Когда Совет Безопасности выполняет возложенные на него настоящим Уставом функции по отношению к какому-либо спору или ситуации, Генеральная Ассамблея не может делать какие-либо рекомендации, касающиеся данного спора или ситуации, если Совет Безопасности не запросит об этом.
    2. +
    3. Генеральный Секретарь, с согласия Совета Безопасности, уведомляет Генеральную Ассамблею на каждой ее сессии о всех вопросах, относящихся к поддержанию международного мира и безопасности, находящихся на рассмотрении Совета Безопасности, и таким же образом уведомляет Генеральную Ассамблею, а если Генеральная Ассамблея не заседает, то Членов Организации, немедленно, как только Совет Безопасности прекратит рассмотрение таких вопросов.
    4. +
    + +

    Статья 13

    + +
      +
    1. Генеральная Ассамблея организует исследования и делает рекомендации в целях: +
        +
      1. Содействия международному сотрудничеству в политической области и поощрения прогрессивного развития международного права и его кодификации;
      2. +
      3. Содействия международному сотрудничеству в области экономической, социальной, культуры, образования, здравоохранения и содействия осуществлению прав человека и основных свобод для всех, без различия расы, пола, языка и религии.
      4. +
      +
    2. +
    3. Дальнейшие обязанности, функции и полномочия Генеральной Ассамблеи в отношении вопросов, упомянутых выше в пункте 1b, изложены в Главах IX и X.
    4. +
    + +

    Статья 14

    + +

    С соблюдением положений статьи 12, Генеральная Ассамблея уполномочивается рекомендовать меры мирного улаживания любой ситуации, независимо от ее происхождения, которая, по мнению Ассамблеи, могла бы нарушить общее благополучие или дружественные отношения между нациями, включая ситуации, возникающие в результате нарушения положений настоящего Устава, излагающих Цели и Принципы Объединенных Наций.

    + +

    Статья 15

    + +
      +
    1. Генеральная Ассамблея получает и рассматривает ежегодные и специальные доклады Совета Безопасности; эти доклады должны включать отчет о мерах по поддержанию международного мира и безопасности, которые Совет Безопасности решил предпринять или предпринял.
    2. +
    3. Генеральная Ассамблея получает и рассматривает доклады других органов Организации.
    4. +
    + +

    Статья 16

    + +

    Генеральная Ассамблея выполняет в отношении международной системы опеки такие функции, которые возложены на нее на основании Глав XII и XIII, включая утверждение соглашений по опеке для территорий, не относящихся к числу стратегических.

    + +

    Статья 17

    + +
      +
    1. Генеральная Ассамблея рассматривает и утверждает бюджет Организации.
    2. +
    3. Члены Организации несут ее расходы по распределению, установленному Генеральной Ассамблеей.
    4. +
    5. Генеральная Ассамблея рассматривает и утверждает любые финансовые и бюджетные соглашения со специализированными учреждениями, упомянутыми в статье 57, и проверяет административные бюджеты таких специализированных учреждений с той целью, чтобы сделать рекомендации заинтересованным учреждениям.
    6. +
    + +

    ГОЛОСОВАНИЕ

    + +

    Статья 18

    + +
      +
    1. Каждый Член Генеральной Ассамблеи имеет один голос.
    2. +
    3. Решения Генеральной Ассамблеи по важным вопросам принимаются большинством в две трети присутствующих и участвующих в голосовании членов Ассамблеи. Эти вопросы включают: рекомендации в отношении поддержания международного мира и безопасности, выборы непостоянных членов Совета Безопасности, выборы членов Экономического и Социального Совета, выборы членов Совета по Опеке, в соответствии с пунктом 1с статьи 86, прием новых Членов в Организацию Объединенных Наций, приостановление прав и привилегий Членов Организации, исключение из Организации ее Членов, вопросы, относящиеся к функционированию системы опеки, и бюджетные вопросы.
    4. +
    5. Решения по другим вопросам, включая определение дополнительных категорий вопросов, которые подлежат решению большинством в две трети голосов, принимаются простым большинством присутствующих и участвующих в голосовании.
    6. +
    + +

    Статья 19

    + +

    Член Организации, за которым числится задолженность по уплате Организации денежных взносов, лишается права голоса в Генеральной Ассамблее, если сумма его задолженности равняется или превышает сумму взносов, причитающихся с него за два полных предыдущих года. Генеральная Ассамблея может, однако, разрешить такому Члену Организации участвовать в голосовании, если она признает, что просрочка платежа произошла по не зависящим от него обстоятельствам.

    + +

    ПРОЦЕДУРА

    + +

    Статья 20

    + +

    Генеральная Ассамблея собирается на очередные ежегодные сессии и на такие специальные сессии, которых могут потребовать обстоятельства. Специальные сессии созываются Генеральным Секретарем по требованию Совета Безопасности или большинства Членов Организации.

    + +

    Статья 21

    + +

    Генеральная Ассамблея устанавливает свои собственные правила процедуры. Она избирает своего Председателя на каждую сессию.

    + +

    Статья 22

    + +

    Генеральная Ассамблея уполномочивается учреждать такие вспомогательные органы, которые она сочтет необходимыми для осуществления своих функций.

    + +

    Глава V: Совет Безопасности

    + +

    СОСТАВ

    + +

    Статья 23

    + +
      +
    1. Совет Безопасности состоит из пятнадцати Членов Организации. Китайская Республика, Франция, Союз Советских Социалистических Республик, Соединенное Королевство Великобритании и Северной Ирландии и Соединенные Штаты Америки являются постоянными членами Совета Безопасности. Генеральная Ассамблея избирает десять других Членов Организации в качестве непостоянных членов Совета Безопасности, уделяя, в особенности, должное внимание, в первую очередь, степени участия Членов Организации в поддержании международного мира и безопасности и в достижении других целей Организации, а также справедливому географическому распределению.
    2. +
    3. Непостоянные члены Совета Безопасности избираются на двухгодичный срок. При первых выборах непостоянных членов, после увеличения Совета Безопасности с одиннадцати до пятнадцати, два из четырех дополнительных членов избираются на срок в один год. Выбывающий член Совета Безопасности не подлежит немедленному переизбранию.
    4. +
    5. Каждый член Совета Безопасности имеет одного представителя.
    6. +
    + +

    ФУНКЦИИ И ПОЛНОМОЧИЯ

    + +

    Статья 24

    + +
      +
    1. Для обеспечения быстрых и эффективных действий Организации Объединенных Наций ее Члены возлагают на Совет Безопасности главную ответственность за поддержание международного мира и безопасности и соглашаются в том, что при исполнении его обязанностей, вытекающих из этой ответственности, Совет Безопасности действует от их имени.
    2. +
    3. При исполнении этих обязанностей Совет Безопасности действует в соответствии с Целями и Принципами Объединенных Наций. Определенные полномочия, предоставленные Совету Безопасности для выполнения этих обязанностей, изложены в Главах VI, VII, VIII и XII.
    4. +
    5. Совет Безопасности представляет на рассмотрение Генеральной Ассамблее ежегодные доклады и, по мере надобности, специальные доклады.
    6. +
    + +

    Статья 25

    + +

    Члены Организации соглашаются, в соответствии с настоящим Уставом, подчиняться с решениями Совета Безопасности и выполнять их.

    + +

    Статья 26

    + +

    В целях содействия установлению и поддержанию международного мира и безопасности с наименьшим отвлечением мировых людских сил и экономических ресурсов для дела вооружения, Совет Безопасности несет ответственность за формулирование, при помощи Военно-Штабного Комитета, указанного в статье 47, планов создания системы регулирования вооружений для представления их Членам Организации.

    + +

    ГОЛОСОВАНИЕ

    + +

    Статья 27

    + +
      +
    1. Каждый член Совета Безопасности имеет один голос.
    2. +
    3. Решения Совета Безопасности по вопросам процедуры считаются принятыми, когда за них поданы голоса девяти членов Совета.
    4. +
    5. Решения Совета Безопасности по всем другим вопросам считаются принятыми, когда за них поданы голоса девяти членов Совета, включая совпадающие голоса всех постоянных членов Совета, причем сторона, участвующая в споре, должна воздержаться от голосования при принятии решения на основании Главы VI и на основании пункта 3 статьи 52.
    6. +
    + +

    ПРОЦЕДУРА

    + +

    Статья 28

    + +
      +
    1. Совет Безопасности организуется таким образом, чтобы он мог функционировать непрерывно. Для этой цели каждый член Совета Безопасности должен быть всегда представлен в месте пребывания Организации Объединенных Наций.
    2. +
    3. Совет Безопасности собирается на периодические заседания, на которых каждый из его членов может, по своему желанию, быть представлен или членом правительства или каким-либо другим особо назначенным представителем.
    4. +
    5. Заседания Совета Безопасности могут происходить не только в месте пребывания Организации, но и во всяком другом месте, которое, по мнению Совета, более способствует его работе.
    6. +
    + +

    Статья 29

    + +

    Совет Безопасности может учреждать такие вспомогательные органы, какие он найдет необходимыми для выполнения своих функций.

    + +

    Статья 30

    + +

    Совет Безопасности устанавливает свои правила процедуры, включая порядок избрания своего Председателя.

    + +

    Статья 31

    + +

    Любой Член Организации, который не является членом Совета Безопасности, может принять участие, без права голоса, в обсуждении любого вопроса, внесенного в Совет Безопасности, во всех тех случаях, когда Совет Безопасности находит, что интересы этого Члена Организации специально затронуты.

    + +

    Статья 32

    + +

    Любой Член Организации, который не состоит членом Совета Безопасности, или любое государство, не состоящее Членом Организации, если они являются сторонами в споре, рассматриваемом Советом Безопасности, приглашаются принять участие, без права голоса, в обсуждении, относящемся к этому спору. Совет Безопасности ставит такие условия для участия государства, не состоящего Членом Организации, какие он найдет справедливыми.

    + +

    Глава VI: Мирное разрешение споров

    + +

    Статья 33

    + +
      +
    1. Стороны, участвующие в любом споре, продолжение которого могло бы угрожать поддержанию международного мира и безопасности, должны прежде всего стараться разрешить спор путем переговоров, обследования, посредничества, примирения, арбитража, судебного разбирательства, обращения к региональным органам или соглашениям или иными мирными средствами по своему выбору.
    2. +
    3. Совет Безопасности, когда он считает это необходимым, требует от сторон разрешения их спора при помощи таких средств.
    4. +
    + +

    Статья 34

    + +

    Совет Безопасности уполномочивается расследовать любой спор или любую ситуацию, которая может привести к международным трениям или вызвать спор, для определения того, не может ли продолжение этого спора или ситуации угрожать поддержанию международного мира и безопасности.

    + +

    Статья 35

    + +
      +
    1. Любой Член Организации может довести о любом споре или ситуации, имеющей характер, указанный в статье 34, до сведения Совета Безопасности или Генеральной Ассамблеи.
    2. +
    3. Государство, которое не является Членом Организации, может довести до сведения Совета Безопасности или Генеральной Ассамблеи о любом споре, в котором оно является стороной, если оно примет на себя заранее в отношении этого спора обязательства мирного разрешения споров, предусмотренные в настоящем Уставе.
    4. +
    5. Разрешение Генеральной Ассамблеей дел, о которых доведено до ее сведения на основании настоящей статьи, производится с учетом положений статей 11 и 12.
    6. +
    + +

    Статья 36

    + +
      +
    1. Совет Безопасности уполномочивается в любой стадии спора, имеющего характер, указанный в статье 33, или ситуации подобного же характера рекомендовать надлежащую процедуру или методы урегулирования.
    2. +
    3. Совет Безопасности принимает во внимание любую процедуру для разрешения этого спора, которая уже была принята сторонами.
    4. +
    5. Делая рекомендации на основании настоящей статьи, Совет Безопасности принимает также во внимание, что споры юридического характера должны, как общее правило, передаваться сторонами в Международный Суд в соответствии с положениями Статута Суда.
    6. +
    + +

    Статья 37

    + +
      +
    1. Если стороны в споре, имеющем характер, указанный в статье 33, не разрешат его при помощи указанных в этой статье средств, они передают его в Совет Безопасности.
    2. +
    3. Если Совет Безопасности считает, что продолжение данного спора в действительности могло бы угрожать поддержанию международного мира и безопасности, он решает, действовать ли ему на основании статьи 36 или рекомендовать такие условия разрешения спора, какие он найдет подходящими.
    4. +
    + +

    Статья 38

    + +

    Без ущерба для положений статей 33–37 Совет Безопасности уполномочивается, если все стороны, участвующие в любом споре, об этом просят, делать сторонам рекомендации с целью мирного разрешения этого спора.

    + +

    Глава VII: Действия в отношении угрозы миру, нарушений мира и актов агрессии

    + +

    Статья 39

    + +

    Совет Безопасности определяет существование любой угрозы миру, любого нарушения мира или акта агрессии и делает рекомендации или решает о том, какие меры следует предпринять в соответствии со статьями 41 и 42 для поддержания или восстановления международного мира и безопасности.

    + +

    Статья 40

    + +

    Чтобы предотвратить ухудшение ситуации, Совет Безопасности уполномочивается, прежде чем сделать рекомендации или решить о принятии мер, предусмотренных статьей 39, потребовать от заинтересованных сторон выполнения тех временных мер, которые он найдет необходимыми или желательными. Такие временные меры не должны наносить ущерба правам, притязаниям или положению заинтересованных сторон. Совет Безопасности должным образом учитывает невыполнение этих временных мер.

    + +

    Статья 41

    + +

    Совет Безопасности уполномочивается решать, какие меры, не связанные с использованием вооруженных сил, должны применяться для осуществления его решений, и он может потребовать от Членов Организации применения этих мер. Эти меры могут включать полный или частичный перерыв экономических отношений, железнодорожных, морских, воздушных, почтовых, телеграфных, радио или других средств сообщения, а также разрыв дипломатических отношений.

    + +

    Статья 42

    + +

    Если Совет Безопасности сочтет, что меры, предусмотренные в статье 41, могут оказаться недостаточными или уже оказались недостаточными, он уполномочивается предпринимать такие действия воздушными, морскими или сухопутными силами, какие окажутся необходимыми для поддержания или восстановления международного мира и безопасности. Такие действия могут включать демонстрации, блокаду и другие операции воздушных, морских или сухопутных сил Членов Организации.

    + +

    Статья 43

    + +
      +
    1. Все Члены Организации для того, чтобы внести свой вклад в дело поддержания международного мира и безопасности, обязуются предоставлять в распоряжение Совета Безопасности по его требованию и в соответствии с особым соглашением или соглашениями необходимые для поддержания международного мира и безопасности вооруженные силы, помощь и соответствующие средства обслуживания, включая право прохода.
    2. +
    3. Такое соглашение или соглашения определяют численность и род войск, степень их готовности и их общее расположение и характер предоставляемых средств обслуживания и помощи.
    4. +
    5. Переговоры о заключении соглашения или соглашений предпринимаются в возможно кратчайший срок по инициативе Совета Безопасности. Они заключаются между Советом Безопасности и Членами Организации или между Советом Безопасности и группами Членов Организации и подлежат ратификации подписавшими их государствами, в соответствии с их конституционной процедурой.
    6. +
    + +

    Статья 44

    + +

    Когда Совет Безопасности решил применить силу, то, прежде чем потребовать от Члена Организации, не представленного в Совете, предоставления вооруженных сил во исполнение обязательств, принятых им на основании статьи 43, Совет Безопасности приглашает этого Члена Организации, если последний этого пожелает, принять участие в решениях Совета Безопасности относительно использования контингентов вооруженных сил данного Члена Организации.

    + +

    Статья 45

    + +

    В целях обеспечения для Организации Объединенных Наций возможности предпринимать срочные военные мероприятия, Члены Организации должны держать в состоянии немедленной готовности контингенты национальных военно-воздушных сил для совместных международных принудительных действий. Численность и степень готовности этих контингентов и планы их совместных действий определяются Советом Безопасности с помощью Военно-Штабного Комитета в пределах, указанных в особом соглашении или соглашениях, упомянутых в статье 43.

    + +

    Статья 46

    + +

    Планы применения вооруженных сил составляются Советом Безопасности с помощью Военно-Штабного Комитета.

    + +

    Статья 47

    + +
      +
    1. Создается Военно-Штабной Комитет для того, чтобы давать советы и оказывать помощь Совету Безопасности по всем вопросам, относящимся к военным потребностям Совета Безопасности в деле поддержания международного мира и безопасности, к использованию войск, предоставленных в его распоряжение, и к командованию ими, а также к регулированию вооружений и к возможному разоружению.
    2. +
    3. Военно-Штабной Комитет состоит из Начальников Штабов постоянных членов Совета Безопасности или их представителей. Любой Член Организации, не представленный постоянно в Комитете, приглашается Комитетом сотрудничать с ним, если эффективное осуществление обязанностей Комитета требует участия этого Члена Организации в работе Комитета.
    4. +
    5. Военно-Штабной Комитет, находясь в подчинении Совета Безопасности, несет ответственность за стратегическое руководство любыми вооруженными силами, предоставленными в распоряжение Совета Безопасности. Вопросы, относящиеся к командованию такими силами, должны быть разработаны позднее.
    6. +
    7. Военно-Штабной Комитет может, с разрешения Совета Безопасности и после консультации с надлежащими региональными органами, учреждать свои региональные подкомитеты.
    8. +
    + +

    Статья 48

    + +
      +
    1. Действия, которые требуются для выполнения решений Совета Безопасности в целях поддержания международного мира и безопасности, предпринимаются всеми Членами Организации или некоторыми из них, в зависимости от того, как это определит Совет Безопасности.
    2. +
    3. Такие решения выполняются Членами Организации непосредственно, а также путем их действий в соответствующих международных учреждениях, членами которых они являются.
    4. +
    + +

    Статья 49

    + +

    Члены Организации должны объединяться для оказания взаимной помощи в проведении мер, о которых принято решение Советом Безопасности.

    + +

    Статья 50

    + +

    Если Советом Безопасности принимаются превентивные или принудительные меры против какого-либо государства, всякое другое государство, независимо от того, состоит ли оно Членом Организации, перед которым встанут специальные экономические проблемы, возникшие из проведения вышеупомянутых мер, имеет право консультироваться с Советом Безопасности на предмет разрешения таких проблем.

    + +

    Статья 51

    + +

    Настоящий Устав ни в коей мере не затрагивает неотъемлемого права на индивидуальную или коллективную самооборону, если произойдет вооруженное нападение на Члена Организации, до тех пор пока Совет Безопасности не примет мер, необходимых для поддержания международного мира и безопасности. Меры, принятые Членами Организации при осуществлении этого права на самооборону, должны быть немедленно сообщены Совету Безопасности и никоим образом не должны затрагивать полномочий и ответственности Совета Безопасности, в соответствии с настоящим Уставом, в отношении предпринятия в любое время таких действий, какие он сочтет необходимыми для поддержания или восстановления международного мира и безопасности.

    + +

    Глава VIII: Региональные соглашения

    + +

    Статья 52

    + +
      +
    1. Настоящий Устав ни в коей мере не препятствует существованию региональных соглашений или органов для разрешения таких вопросов, относящихся к поддержанию международного мира и безопасности, которые являются подходящими для региональных действий, при условии, что такие соглашения или органы и их деятельность совместимы с Целями и Принципами Организации.
    2. +
    3. Члены Организации, заключившие такие соглашения или составляющие такие органы, должны приложить все свои усилия для достижения мирного разрешения местных споров при помощи таких региональных соглашений или таких региональных органов до передачи этих споров в Совет Безопасности.
    4. +
    5. Совет Безопасности должен поощрять развитие применения мирного разрешения местных споров при помощи таких региональных соглашений или таких региональных органов либо по инициативе заинтересованных государств, либо по своей собственной инициативе.
    6. +
    7. Настоящая статья ни в коей мере не затрагивает применения статей 34 и 35.
    8. +
    + +

    Статья 53

    + +
      +
    1. Совет Безопасности использует, где это уместно, такие региональные соглашения или органы для принудительных действий под его руководством. Однако никакие принудительные действия не предпринимаются, в силу этих региональных соглашений или региональными органами, без полномочий от Совета Безопасности, за исключением мер, предусмотренных статьей 107, против любого вражеского государства, как оно определено в пункте 2 настоящей статьи, или мер, предусмотренных в региональных соглашениях, направленных против возобновления агрессивной политики со стороны любого такого государства до того времени, когда на Организацию, по просьбе заинтересованных Правительств, может быть возложена ответственность за предупреждение дальнейшей агрессии со стороны такого государства.
    2. +
    3. Термин «вражеское государство», как он применен в пункте 1 настоящей статьи, относится к любому государству, которое в течение второй мировой войны являлось врагом любого из государств, подписавших настоящий Устав.
    4. +
    + +

    Статья 54

    + +

    Совет Безопасности должен быть всегда полностью информирован о действиях, предпринятых или намечаемых в силу региональных соглашений или региональными органами, для поддержания международного мира и безопасности.

    + +

    ГЛАВА IX: Международное экономическое и социальное сотрудничество

    + +

    Статья 55

    + +

    С целью создания условий стабильности и благополучия, необходимых для мирных и дружеских отношений между нациями, основанных на уважении принципа равноправия и самоопределения народов, Организация Объединенных Наций содействует:

    + +
      +
    1. Повышению уровня жизни, полной занятости населения и условиям экономического и социального прогресса и развития;
    2. +
    3. Разрешению международных проблем в области экономической, социальной, здравоохранения и подобных проблем; международному сотрудничеству в области культуры и образования;
    4. +
    5. Всеобщему уважению и соблюдению прав человека и основных свобод для всех, без различия расы, пола, языка и религии.
    6. +
    + +

    Статья 56

    + +

    Все Члены Организации обязуются предпринимать совместные и самостоятельные действия в сотрудничестве с Организацией для достижения целей, указанных в статье 55.

    + +

    Статья 57

    + +
      +
    1. Различные специализированные учреждения, созданные межправительственными соглашениями и облеченные широко международной, определенной в их учредительных актах, ответственностью в области экономической, социальной, культуры, образования, здравоохранения и подобных областях, будут поставлены в связь с Организацией в соответствии с положениями статьи 63.
    2. +
    3. Такие учреждения, которые будут поставлены указанным образом в связь с Организацией, именуются в последующих статьях «специализированные учреждения».
    4. +
    + +

    Статья 58

    + +

    Организация делает рекомендации по согласованию политики и деятельности специализированных учреждений.

    + +

    Статья 59

    + +

    Организация, в случае надобности, проявляет инициативу в том, чтобы заинтересованные государства приступили к переговорам о создании любых новых специализированных учреждений, которые потребуются для выполнения целей, указанных в статье 55.

    + +

    Статья 60

    + +

    Ответственность за выполнение функций Организации, указанных в настоящей Главе, возлагается на Генеральную Ассамблею и, под руководством Генеральной Ассамблеи, на Экономический и Социальный Совет, которому для этой цели предоставляются полномочия, указанные в Главе X.

    + +

    Глава X: Экономический и Социальный Совет

    + +

    СОСТАВ

    + +

    Статья 61

    + +
      +
    1. Экономический и Социальный Совет состоит из пятидесяти четырех Членов Организации, избираемых Генеральной Ассамблеей.
    2. +
    3. С соблюдением положений, изложенных в пункте 3, восемнадцать членов Экономического и Социального Совета избираются ежегодно сроком на три года. Выбывающий член Совета может быть переизбран немедленно.
    4. +
    5. При первых выборах после увеличения числа членов Экономического и Социального Совета с двадцати семи до пятидесяти четырех, в дополнение к членам, избираемым вместо девяти членов, срок полномочий которых истекает в конце данного года, избираются двадцать семь дополнительных членов. Срок полномочий девяти из двадцати семи дополнительных членов, избранных таким образом, истекает в конце первого года, а срок полномочий других девяти членов — в конце второго года, в соответствии с постановлением Генеральной Ассамблеи.
    6. +
    7. Каждый член Экономического и Социального Совета имеет одного представителя.
    8. +
    + +

    ФУНКЦИИ И ПОЛНОМОЧИЯ

    + +

    Статья 62

    + +
      +
    1. Экономический и Социальный Совет уполномочивается предпринимать исследования и составлять доклады по международным вопросам в области экономической, социальной, культуры, образования, здравоохранения и подобным вопросам или побуждать к этому других, а также делать по любому из этих вопросов рекомендации Генеральной Ассамблее, Членам Организации и заинтересованным специализированным учреждениям.
    2. +
    3. Совет уполномочивается делать рекомендации в целях поощрения уважения и соблюдения прав человека и основных свобод для всех.
    4. +
    5. Совет уполномочивается подготавливать для представления Генеральной Ассамблее проекты конвенций по вопросам, входящим в его компетенцию.
    6. +
    7. Совет уполномочивается созывать, в соответствии с правилами, предписанными Организацией, международные конференции по вопросам, входящим в его компетенцию.
    8. +
    + +

    Статья 63

    + +
      +
    1. Экономический и Социальный Совет уполномочивается вступать с любым из учреждений, указанных в статье 57, в соглашения, определяющие условия, на которых соответствующие учреждения будут поставлены в связь с Организацией. Такие соглашения подлежат утверждению Генеральной Ассамблеей.
    2. +
    3. Совет уполномочивается согласовывать деятельность специализированных учреждений посредством консультаций с ними и рекомендаций таким учреждениям и посредством рекомендаций Генеральной Ассамблее и Членам Организации.
    4. +
    + +

    Статья 64

    + +
      +
    1. Экономический и Социальный Совет уполномочивается принимать надлежащие меры для получения от специализированных учреждений регулярных докладов. Совет уполномочивается заключать соглашения с Членами Организации и со специализированными учреждениями с целью получения от них докладов о мерах, предпринятых ими во исполнение его собственных рекомендаций и рекомендаций Генеральной Ассамблеи по вопросам, входящим в его компетенцию.
    2. +
    3. Совет уполномочивается сообщать Генеральной Ассамблее свои замечания по этим докладам.
    4. +
    + +

    Статья 65

    + +

    Экономический и Социальный Совет уполномочивается представлять Совету Безопасности информацию и, по предложению Совета Безопасности, обязан ему помогать.

    + +

    Статья 66

    + +
      +
    1. Экономический и Социальный Совет осуществляет такие функции, какие входят в его компетенцию, в связи с выполнением рекомендаций Генеральной Ассамблеи.
    2. +
    3. Совет, с одобрения Генеральной Ассамблеи, уполномочивается выполнять работы по просьбе Членов Организации и по просьбе специализированных учреждений.
    4. +
    5. Совет должен выполнять такие другие функции, какие перечислены в других частях настоящего Устава или какие могут быть возложены на него Генеральной Ассамблеей.
    6. +
    + +

    ГОЛОСОВАНИЕ

    + +

    Статья 67

    + +
      +
    1. Каждый член Экономического и Социального Совета имеет один голос.
    2. +
    3. Решения Экономического и Социального Совета принимаются большинством голосов членов Совета, присутствующих и участвующих в голосовании.
    4. +
    + +

    ПРОЦЕДУРА

    + +

    Статья 68

    + +

    Экономический и Социальный Совет создает комиссии в экономической и социальной области и по поощрению прав человека, а также такие другие комиссии, которые могут потребоваться для выполнения его функций.

    + +

    Статья 69

    + +

    Экономический и Социальный Совет приглашает любого Члена Организации участвовать без права голоса в обсуждении им любого вопроса, представляющего особый интерес для данного Члена Организации.

    + +

    Статья 70

    + +

    Экономический и Социальный Совет уполномочивается проводить мероприятия для участия без права голоса представителей специализированных учреждений в обсуждении вопросов в Совете или в созданных им комиссиях, а также для участия представителей Совета в обсуждении вопросов в специализированных учреждениях.

    + +

    Статья 71

    + +

    Экономический и Социальный Совет уполномочивается проводить надлежащие мероприятия для консультации с неправительственными организациями, заинтересованными в вопросах, входящих в его компетенцию. Такие мероприятия могут быть условлены с международными организациями, а в случае надобности, с национальными организациями после консультации с заинтересованным Членом Организации.

    + +

    Статья 72

    + +
      +
    1. Экономический и Социальный Совет устанавливает свои собственные правила процедуры, включая порядок избрания своего Председателя.
    2. +
    3. Экономический и Социальный Совет созывается по мере надобности, в соответствии со своими правилами, которые должны включать положения о созыве заседаний по требованию большинства его членов.
    4. +
    + +

    Глава XI: Декларация в отношении несамоуправляющихся территорий

    + +

    Статья 73

    + +

    Члены Организации Объединенных Наций, которые несут или принимают на себя ответственность за управление территориями, народы которых не достигли еще полного самоуправления, признают тот принцип, что интересы населения этих территорий являются первостепенными, и, как священный долг, принимают обязательство максимально способствовать благополучию населения этих территорий в рамках системы международного мира и безопасности, установленной настоящим Уставом, и с этой целью:

    + +
      +
    1. Обеспечивать, соблюдая должное уважение к культуре указанных народов, их политический, экономический и социальный прогресс, прогресс в области образования, справедливое обращение с ними и защиту их от злоупотреблений;
    2. +
    3. Развивать самоуправление, учитывать должным образом политические стремления этих народов и помогать им в прогрессивном развитии их свободных политических институтов в соответствии со специфическими обстоятельствами, присущими каждой территории и ее народам, и с их разными ступенями развития;
    4. +
    5. Укреплять международный мир и безопасность;
    6. +
    7. Способствовать развитию созидательных мероприятий, поощрять исследования и сотрудничать друг с другом и, где и когда это уместно, со специализированными международными организациями ради практического достижения изложенных в настоящей статье социальных, экономических и научных целей, и
    8. +
    9. Передавать регулярно Генеральному Секретарю для информации и с таким ограничением, какое может потребоваться по соображениям безопасности и конституционного порядка, статистическую и другую информацию специального характера, относящуюся к экономическим и социальным условиям, а также условиям образования на территориях, за которые они соответственно несут ответственность, кроме тех территорий, на которые распространяется действие Глав XII и XIII.
    10. +
    + +

    Статья 74

    + +

    Члены Организации также соглашаются, что их политика в отношении территорий, на которые распространяется действие настоящей Главы, должна быть основана не менее, чем в отношении их метрополий, на общем принципе добрососедства, с надлежащим учетом интересов и благополучия остального мира в делах социальных, экономических и торговли.

    + +

    Глава XII: Международная система опеки

    + +

    Статья 75

    + +

    Организация Объединенных Наций создает под своим руководством международную систему опеки для управления теми территориями, которые могут быть включены в нее последующими индивидуальными соглашениями, и для наблюдения за этими территориями. Эти территории именуются далее «территории под опекой».

    + +

    Статья 76

    + +

    Основные задачи системы опеки, в соответствии с Целями Организации Объединенных Наций, изложенными в статье 1 настоящего Устава, состоят в том, чтобы:

    + +
      +
    1. Укреплять международный мир и безопасность;
    2. +
    3. Способствовать политическому, экономическому и социальному прогрессу населения территорий под опекой, его прогрессу в области образования и его прогрессивному развитию в направлении к самоуправлению или независимости, как это может оказаться подходящим для специфических условий каждой территории и ее народов и имея в виду свободно выраженное желание этих народов, и как это может быть предусмотрено условиями каждого соглашения об опеке;
    4. +
    5. Поощрять уважение прав человека и основных свобод для всех, без различия расы, пола, языка, религии, и поощрять признание взаимозависимости народов мира;
    6. +
    7. Обеспечивать равное отношение к Членам Организации и их гражданам в области социальной, экономической и торговой, а также равное отношение к ним в отправлении правосудия без ущерба для достижения выше изложенных задач и при условии соблюдения положений статьи 80.
    8. +
    + +

    Статья 77

    + +
      +
    1. Система опеки распространяется на такие территории из ниже перечисленных категорий, которые могут быть включены в нее соглашениями об опеке: +
        +
      1. Территории, ныне находящиеся под мандатом;
      2. +
      3. Территории, которые могут быть отторгнуты от вражеских государств в результате второй мировой войны, и
      4. +
      5. Территории, добровольно включенные в систему опеки государствами, ответственными за их управление.
      6. +
      +
    2. +
    3. Вопрос о том, какие из территорий выше перечисленных категорий должны быть включены в систему опеки и на каких условиях, будет предметом последующего соглашения.
    4. +
    + +

    Статья 78

    + +

    Система опеки не распространяется на страны, ставшие Членами Организации, отношения между которыми должны основываться на уважении принципа суверенного равенства.

    + +

    Статья 79

    + +

    Условия опеки для каждой территории, подлежащей включению в систему опеки, в том числе все изменения и поправки, определяются соглашениями непосредственно заинтересованных государств, включая страны-мандатарии, в том случае, если территории находятся под мандатом одного из Членов Организации, и утверждаются, как предусмотрено в статьях 83 и 85.

    + +

    Статья 80

    + +
      +
    1. За исключением случаев, которые могут быть согласованы в индивидуальных соглашениях об опеке, заключенных согласно статьям 77, 79 и 81, включающих каждую территорию в систему опеки, и впредь до заключения таких соглашений, ничто в настоящей Главе не должно толковаться как изменение каким-либо образом каких бы то ни было прав любых государств или любых народов или условий существующих международных соглашений, участниками которых могут быть соответственно Члены Организации.
    2. +
    3. Пункт 1 настоящей статьи не должен толковаться как дающий основания для задержки или отсрочки переговоров и заключения соглашений о включении под мандатных и других территорий в систему опеки, как это предусмотрено в статье 77.
    4. +
    + +

    Статья 81

    + +

    Соглашение об опеке в каждом случае должно включать условия, на которых будет управляться территория под опекой, а также определять власть, которая будет осуществлять управление территорией под опекой. Такая власть, называемая далее управляющей властью, может представлять собою одно или более государств или Организацию Объединенных Наций, как таковую.

    + +

    Статья 82

    + +

    В любом соглашении об опеке может определяться стратегический район или районы, которые могут включать часть или всю территорию под опекой, на которую распространяется соглашение, без ущерба для какого бы то ни было особого соглашения или соглашений, заключенных на основании статьи 43.

    + +

    Статья 83

    + +
      +
    1. Все функции Организации Объединенных Наций, относящиеся к стратегическим районам, включая утверждение условий соглашений об опеке и их изменений или поправок к ним, осуществляются Советом Безопасности.
    2. +
    3. Основные цели, изложенные в статье 76, относятся к народу каждого из стратегических районов.
    4. +
    5. Совет Безопасности, соблюдая условия соглашений об опеке и без ущерба для требований безопасности, пользуется помощью Совета по Опеке для выполнения тех функций Организации Объединенных Наций, в соответствии с системой опеки, которые относятся к политическим, экономическим и социальным вопросам, а также к вопросам в области образования в стратегических районах.
    6. +
    + +

    Статья 84

    + +

    Обязанностью управляющей власти является обеспечение того, чтобы территория под опекой играла свою роль в поддержании международного мира и безопасности. С этой целью управляющая власть уполномочивается использовать добровольные вооруженные силы, средства обслуживания и помощь территории под опекой при выполнении обязательств, принятых в этом отношении управляющей властью перед Советом Безопасности, а равно и для местной обороны и поддержания закона и порядка в пределах территории под опекой.

    + +

    Статья 85

    + +
      +
    1. Функции Организации Объединенных Наций в отношении соглашений об опеке для всех районов, не отнесенных к числу стратегических, включая утверждение условий соглашений об опеке и их изменений или поправок к ним, осуществляются Генеральной Ассамблеей.
    2. +
    3. Совет по Опеке, действующий под руководством Генеральной Ассамблеи, помогает Генеральной Ассамблее в выполнении этих функций.
    4. +
    + +

    Глава XIII: Совет по Опеке

    + +

    СОСТАВ

    + +

    Статья 86

    + +
      +
    1. Совет по Опеке состоит из следующих Членов Организации Объединенных Наций: +
        +
      1. Тех Членов Организации, которые управляют территориями под опекой;
      2. +
      3. Тех Членов Организации, поименованных в статье 23, которые не управляют территориями под опекой;
      4. +
      5. Такого числа других Членов Организации, избранных Генеральной Ассамблеей на трехгодичный срок, какое может оказаться необходимым для обеспечения того, чтобы общее число членов Совета по Опеке распределялось поровну между Членами Организации, управляющими и не управляющими территориями под опекой.
      6. +
      +
    2. +
    3. Каждый Член Совета по Опеке назначит одно особо квалифицированное лицо, которое будет его представителем в Совете по Опеке.
    4. +
    + +

    ФУНКЦИИ И ПОЛНОМОЧИЯ

    + +

    Статья 87

    + +

    Генеральная Ассамблея и находящийся под ее руководством Совет по Опеке при выполнении своих функций уполномочиваются:

    + +
      +
    1. Рассматривать отчеты, представляемые управляющей властью;
    2. +
    3. Принимать петиции и рассматривать их, консультируясь с управляющей властью;
    4. +
    5. Устраивать периодические посещения соответствующих территорий под опекой в согласованные с управляющей властью сроки; и
    6. +
    7. Предпринимать упомянутые и другие действия в соответствии с условиями соглашений об опеке.
    8. +
    + +

    Статья 88

    + +

    Совет по Опеке разрабатывает анкету относительно политического, экономического и социального прогресса населения каждой территории под опекой, а также его прогресса в области образования, а управляющая власть каждой территории под опекой, входящей в компетенцию Генеральной Ассамблеи, представляет последней ежегодные доклады на основе этой анкеты.

    + +

    ГОЛОСОВАНИЕ

    + +

    Статья 89

    + +
      +
    1. Каждый член Совета по Опеке имеет один голос.
    2. +
    3. Решения Совета по Опеке принимаются большинством голосов присутствующих и участвующих в голосовании членов Совета.
    4. +
    + +

    ПРОЦЕДУРА

    + +

    Статья 90

    + +
      +
    1. Совет по Опеке принимает свои собственные правила процедуры, включая порядок избрания своего Председателя.
    2. +
    3. Заседания Совета по Опеке созываются по мере надобности в соответствии с его правилами процедуры, которые должны предусматривать созыв заседаний по требованию большинства членов Совета.
    4. +
    + +

    Статья 91

    + +

    Совет по Опеке пользуется в соответствующих случаях помощью Экономического и Социального Совета и специализированных учреждений в отношении вопросов, в которых они соответственно заинтересованы.

    + +

    Глава XIV: Международный Суд

    + +

    Статья 92

    + +

    Международный Суд является главным судебным органом Организации Объединенных Наций. Он действует в соответствии с прилагаемым Статутом, который основан на Статуте Постоянной Палаты Международного Правосудия и образует неотъемлемую часть настоящего Устава.

    + +

    Статья 93

    + +
      +
    1. Все Члены Организации являются ipso facto участниками Статута Международного Суда.
    2. +
    3. Государство, не являющееся Членом Организации, может стать участником Статута Международного Суда на условиях, которые определяются, в каждом отдельном случае, Генеральной Ассамблеей по рекомендации Совета Безопасности.
    4. +
    + +

    Статья 94

    + +
      +
    1. Каждый Член Организации обязуется выполнить решение Международного Суда по тому делу, в котором он является стороной.
    2. +
    3. В случае, если какая-либо сторона в деле не выполнит обязательства, возложенного на нее решением Суда, другая сторона может обратиться в Совет Безопасности, который может, если признает это необходимым, сделать рекомендации или решить о принятии мер для приведения решения в исполнение.
    4. +
    + +

    Статья 95

    + +

    Настоящий Устав ни в коей мере не препятствует Членам Организации поручать разрешение своих разногласий другим судам в силу уже существующих соглашений или таких, которые могут быть заключены в будущем.

    + +

    Статья 96

    + +
      +
    1. Генеральная Ассамблея или Совет Безопасности могут запрашивать от Международного Суда консультативные заключения по любому юридическому вопросу.
    2. +
    3. Другие органы Организации Объединенных Наций и специализированные учреждения, которым Генеральная Ассамблея может дать в любое время разрешение на это, также могут запрашивать консультативные заключения Суда по юридическим вопросам, возникающим в пределах их круга деятельности.
    4. +
    + +

    Глава XV: Секретариат

    + +

    Статья 97

    + +

    Секретариат состоит из Генерального Секретаря и такого персонала, который может потребоваться для Организации. Генеральный Секретарь назначается Генеральной Ассамблеей по рекомендации Совета Безопасности. Генеральный Секретарь является главным административным должностным лицом Организации.

    + +

    Статья 98

    + +

    Генеральный Секретарь действует в этом качестве на всех заседаниях Генеральной Ассамблеи, Совета Безопасности, Экономического и Социального Совета и Совета по Опеке и выполняет такие другие функции, какие возлагаются на него этими органами. Генеральный Секретарь представляет Генеральной Ассамблее ежегодный отчет о работе Организации.

    + +

    Статья 99

    + +

    Генеральный Секретарь имеет право доводить до сведения Совета Безопасности о любых вопросах, которые, по его мнению, могут угрожать поддержанию международного мира и безопасности.

    + +

    Статья 100

    + +
      +
    1. При исполнении своих обязанностей Генеральный Секретарь и персонал Секретариата не должны запрашивать или получать указания от какого бы то ни было правительства или власти, посторонней для Организации. Они должны воздерживаться от любых действий, которые могли бы отразиться на их положении как международных должностных лиц, ответственных только перед Организацией.
    2. +
    3. Каждый Член Организации обязуется уважать строго международный характер обязанностей Генерального Секретаря и персонала Секретариата и не пытаться оказывать на них влияние при исполнении ими своих обязанностей.
    4. +
    + +

    Статья 101

    + +
      +
    1. Персонал Секретариата назначается Генеральным Секретарем, согласно правилам, устанавливаемым Генеральной Ассамблеей.
    2. +
    3. Надлежащий персонал выделяется для постоянной работы в Экономический и Социальный Совет, в Совет по Опеке и, по мере надобности, в другие органы Организации. Этот персонал составляет часть Секретариата.
    4. +
    5. При приеме на службу и определении условий службы следует руководствоваться, главным образом, необходимостью обеспечить высокий уровень работоспособности, компетентности и добросовестности. Должное внимание следует уделять важности подбора персонала на возможно более широкой географической основе.
    6. +
    + +

    Глава XVI: Разные постановления

    + +

    Статья 102

    + +
      +
    1. Всякий договор и всякое международное соглашение, заключенные любым Членом Организации после вступления в силу настоящего Устава, должны быть, при первой возможности, зарегистрированы в Секретариате и им опубликованы.
    2. +
    3. Ни одна из сторон в любом таком договоре или международном соглашении, не зарегистрированных в соответствии с пунктом 1 настоящей статьи, не может ссылаться на такой договор или соглашение ни в одном из органов Организации Объединенных Наций.
    4. +
    + +

    Статья 103

    + +

    В том случае, когда обязательства Членов Организации по настоящему Уставу окажутся в противоречии с их обязательствами по какому-либо другому международному соглашению, преимущественную силу имеют обязательства по настоящему Уставу.

    + +

    Статья 104

    + +

    Организация Объединенных Наций пользуется на территории каждого из своих Членов такой правоспособностью, которая может оказаться необходимой для выполнения ее функций и достижения ее целей.

    + +

    Статья 105

    + +
      +
    1. Организация Объединенных Наций пользуется на территории каждого из своих Членов такими привилегиями и иммунитетами, которые необходимы для достижения ее целей.
    2. +
    3. Представители Членов Организации и ее должностные лица также пользуются привилегиями и иммунитетами, которые необходимы для самостоятельного выполнения ими своих функций, связанных с деятельностью Организации.
    4. +
    5. Генеральная Ассамблея может делать рекомендации для определения деталей применения пунктов 1 и 2 настоящей статьи, а также может предлагать Членам Организации конвенции для этой цели.
    6. +
    + +

    Глава XVII: Мероприятия по безопасности в переходный период

    + +

    Статья 106

    + +

    Впредь до вступления в силу таких упомянутых в статье 43 особых соглашений, какие, по мнению Совета Безопасности, дают ему возможность начать осуществление своих обязанностей, согласно статье 42, участники Декларации Четырех Держав, подписанной в Москве 30 октября 1943 г., и Франция будут, в соответствии с положениями пункта 5 этой Декларации, консультироваться друг с другом и, в случае необходимости, с другими Членами Организации с целью таких совместных действий от имени Организации, какие могут оказаться необходимыми для поддержания международного мира и безопасности.

    + +

    Статья 107

    + +

    Настоящий Устав ни в коей мере не лишает юридической силы действия, предпринятые или санкционированные в результате второй мировой войны несущими ответственность за такие действия правительствами, в отношении любого государства, которое в течение второй мировой войны было врагом любого из государств, подписавших настоящий Устав, а также не препятствует таким действиям.

    + +

    Глава XVIII: Поправки

    + +

    Статья 108

    + +

    Поправки к настоящему Уставу вступают в силу для всех Членов Организации, после того как они приняты двумя третями голосов членов Генеральной Ассамблеи и ратифицированы, в соответствии с их конституционной процедурой, двумя третями Членов Организации, включая всех постоянных членов Совета Безопасности.

    + +

    Статья 109

    + +
      +
    1. С целью пересмотра настоящего Устава может быть созвана Генеральная конференция Членов Организации Объединенных Наций в срок и в месте, которые должны быть определены двумя третями голосов членов Генеральной Ассамблеи и голосами любых девяти членов Совета Безопасности. Каждый Член Организации будет иметь на Конференции один голос.
    2. +
    3. Любое изменение настоящего Устава, рекомендованное двумя третями голосов участников Конференции, вступит в силу по ратификации, в соответствии с их конституционной процедурой, двумя третями Членов Организации, включая всех постоянных членов Совета Безопасности.
    4. +
    5. Если такая Конференция не состоится до десятой ежегодной сессии Генеральной Ассамблеи, считая со вступления настоящего Устава в силу, предложение созвать такую Конференцию включается в повестку дня этой сессии Генеральной Ассамблеи, и Конференция созывается, если это будет решено простым большинством голосов членов Генеральной Ассамблеи и голосами любых семи членов Совета Безопасности.
    6. +
    + +

    Глава XIX: Ратификация и подписание

    + +

    Статья 110

    + +
      +
    1. Настоящий Устав подлежит ратификации подписавшими его государствами, в соответствии с их конституционной процедурой.
    2. +
    3. Ратификационные грамоты должны сдаваться на хранение Правительству Соединенных Штатов Америки, которое будет извещать о сдаче на хранение каждой грамоты все государства, подписавшие Устав, также как и Генерального Секретаря Организации, когда он будет назначен.
    4. +
    5. Настоящий Устав вступит в силу по сдаче на хранение ратификационных грамот Китайской Республикой, Францией, Союзом Советских Социалистических Республик, Соединенным Королевством Великобритании и Северной Ирландии и Соединенными Штатами Америки и большинством других государств, подписавших Устав. После этого Правительством Соединенных Штатов Америки будет составлен протокол о сдаче на хранение ратификационных грамот, копии с которого будут разосланы всем подписавшим Устав государствам.
    6. +
    7. Государства, подписавшие настоящий Устав, которые ратифицируют его после того, как он вступит в силу, станут Первоначальными Членами Организации Объединенных Наций со дня сдачи ими на хранение своих соответствующих ратификационных грамот.
    8. +
    + +

    Статья 111

    + +

    Настоящий Устав, китайский, французский, русский, английский и испанский тексты которого являются равно аутентичными, будет храниться в архиве Правительства Соединенных Штатов Америки. Это Правительство препровождает копии Устава, должным образом заверенные, Правительствам всех других подписавших его государств.

    + +

    В УДОСТОВЕРЕНИЕ ЧЕГО представители Правительств Объединенных Наций подписали настоящий Устав.

    + +

    СОСТАВЛЕНО в городе Сан-Франциско, июня двадцать шестого дня, тысяча девятьсот сорок пятого года.

    + +

     

    + +

    Поправки к статьям 23, 27, 61, 109

    + +

    Поправки к статьям 23, 27 и 61 Устава были приняты Генеральной Ассамблеей 17 декабря 1963 года и вступили в силу 31 августа 1965 года. Поправка к статье 109, принятая Генеральной Ассамблеей 20 декабря 1965 года, вступила в силу 12 июня 1968 года. 

    + +

    Поправка к статье 23 Устава увеличивает число членов Совета Безопасности с одиннадцати до пятнадцати. 

    + +

    Исправленная статья 27 предусматривает, что решения Совета Безопасности по процедурным вопросам считаются принятыми, когда за них поданы голоса девяти членов (раньше — семи), и по всем другим вопросам — когда за них поданы голоса девяти членов (раньше — семи), включая совпадающие голоса пяти постоянных членов Совета Безопасности. 

    + +

    Поправка к статье 61 увеличивает число членов Экономического и Социального Совета с восемнадцати до двадцати семи. Последующая поправка к этой статье, вступившая в силу 24 сентября 1973 года, увеличивает число членов Совета с двадцати семи до пятидесяти четырех.

    + +

    Поправка к первому пункту статьи 109 предусматривает, что время и место проведения Генеральной конференции государств-членов с целью пересмотра Устава определяются двумя третями голосов членов Генеральной Ассамблеи и голосами любых девяти (раньше — семи) членов Совета Безопасности.  Пункт 3 статьи 109 , который предусматривает возможность созыва конференции по пересмотру Устава, был рассмотрен Генеральной Ассамблеей и Советом Безопасности на десятой очередной сессии Генеральной Ассамблеи в 1955 году и оставлен в его первоначальной формулировке: «голосами любых семи членов Совета Безопасности». 

    +
    +
    +
    + + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + + + +
    +
    + + +
    + + + + + + + + + + + +
    + + + + + + + + + +
    \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_RU.txt b/stdlib/benchmarks/collections/data/UN_charter_RU.txt new file mode 100644 index 0000000000..576465aa8f --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_RU.txt @@ -0,0 +1,468 @@ +Устав ООН (полный текст) +Преамбула +МЫ, НАРОДЫ ОБЪЕДИНЕННЫХ НАЦИЙ, ПРЕИСПОЛНЕННЫЕ РЕШИМОСТИ +избавить грядущие поколения от бедствий войны, дважды в нашей жизни принесшей человечеству невыразимое горе, и + +вновь утвердить веру в основные права человека, в достоинство и ценность человеческой личности, в равноправие мужчин и женщин и в равенство прав больших и малых наций, и + +создать условия, при которых могут соблюдаться справедливость и уважение к обязательствам, вытекающим из договоров и других источников международного права, и + +содействовать социальному прогрессу и улучшению условий жизни при большей свободе, + +И В ЭТИХ ЦЕЛЯХ +проявлять терпимость и жить вместе, в мире друг с другом, как добрые соседи, и + +объединить наши силы для поддержания международного мира и безопасности, и + +обеспечить принятием принципов и установлением методов, чтобы вооруженные силы применялись не иначе, как в общих интересах, и + +использовать международный аппарат для содействия экономическому и социальному прогрессу всех народов, + +РЕШИЛИ ОБЪЕДИНИТЬ НАШИ УСИЛИЯ ДЛЯ ДОСТИЖЕНИЯ ЭТИХ ЦЕЛЕЙ. +Согласно этому наши соответственные правительства через представителей, собравшихся в городе Сан-Франциско, предъявивших свои полномочия, найденные в надлежащей форме, согласились принять настоящий Устав Организации Объединенных Наций и настоящим учреждают международную организацию под названием «Объединенные Нации». + +Глава I: Цели и Принципы +Статья 1 +Организация Объединенных Наций преследует Цели: + +Поддерживать международный мир и безопасность и с этой целью принимать эффективные коллективные меры для предотвращения и устранения угрозы миру и подавления актов агрессии или других нарушений мира и проводить мирными средствами, в согласии с принципами справедливости и международного права, улаживание или разрешение международных споров или ситуаций, которые могут привести к нарушению мира; +Развивать дружественные отношения между нациями на основе уважения принципа равноправия и самоопределения народов, а также принимать другие соответствующие меры для укрепления всеобщего мира; +Осуществлять международное сотрудничество в разрешении международных проблем экономического, социального, культурного и гуманитарного характера и в поощрении и развитии уважения к правам человека и основным свободам для всех, без различия расы, пола, языка и религии, и +Быть центром для согласования действий наций в достижении этих общих целей. +Статья 2 +Для достижения целей, указанных в статье 1, Организация и ее Члены действуют в соответствии со следующими Принципами: + +Организация основана на принципе суверенного равенства всех ее Членов; +Все Члены Организации Объединенных Наций добросовестно выполняют принятые на себя по настоящему Уставу обязательства, чтобы обеспечить им всем в совокупности права и преимущества, вытекающие из принадлежности к составу Членов Организации; +Все Члены Организации Объединенных Наций разрешают свои международные споры мирными средствами таким образом, чтобы не подвергать угрозе международный мир и безопасность и справедливость; +Все Члены Организации Объединенных Наций воздерживаются в их международных отношениях от угрозы силой или ее применения как против территориальной неприкосновенности или политической независимости любого государства, так и каким-либо другим образом, несовместимым с Целями Объединенных Наций; +Все Члены Организации Объединенных Наций оказывают ей всемерную помощь во всех действиях, предпринимаемых ею в соответствии с настоящим Уставом, и воздерживаются от оказания помощи любому государству, против которого Организация Объединенных Наций предпринимает действия превентивного или принудительного характера; +Организация обеспечивает, чтобы государства, которые не являются ее Членами, действовали в соответствии с этими Принципами, поскольку это может оказаться необходимым для поддержания международного мира и безопасности; +Настоящий Устав ни в коей мере не дает Организации Объединенных Наций права на вмешательство в дела, по существу входящие во внутреннюю компетенцию любого государства, и не требует от Членов Организации Объединенных Наций представлять такие дела на разрешение в порядке настоящего Устава; однако этот принцип не затрагивает применения принудительных мер на основании Главы VII. +Глава II: Члены Организации +Статья 3 +Первоначальными Членами Организации Объединенных Наций являются государства, которые, приняв участие в Конференции в Сан-Франциско по созданию Международной Организации или ранее подписав Декларацию Объединенных Наций от 1 января 1942 года, подписали и ратифицировали настоящий Устав в соответствии со статьей 110. + +Статья 4 +Прием в Члены Организации открыт для всех других миролюбивых государств, которые примут на себя содержащиеся в настоящем Уставе обязательства и которые, по суждению Организации, могут и желают эти обязательства выполнять. +Прием любого такого государства в Члены Организации производится постановлением Генеральной Ассамблеи по рекомендации Совета Безопасности. +Статья 5 +Если против какого-либо Члена Организации были предприняты Советом Безопасности действия превентивного или принудительного характера, Генеральная Ассамблея имеет право, по рекомендации Совета Безопасности, приостанавливать осуществление прав и привилегий, принадлежащих ему как Члену Организации. Осуществление этих прав и привилегий может быть восстановлено Советом Безопасности. + +Статья 6 +Член Организации, систематически нарушающий принципы, содержащиеся в настоящем Уставе, может быть исключен из Организации Генеральной Ассамблеей по рекомендации Совета Безопасности. + +Глава III: Органы +Статья 7 +В качестве главных органов Организации Объединенных Наций учреждаются: Генеральная Ассамблея, Совет Безопасности, Экономический и Социальный Совет, Совет по Опеке, Международный Суд и Секретариат. +Вспомогательные органы, которые окажутся необходимыми, могут учреждаться в соответствии с настоящим Уставом. +Статья 8 +Организация Объединенных Наций не устанавливает никаких ограничений в отношении права мужчин и женщин участвовать в любом качестве и на равных условиях в ее главных и вспомогательных органах. + +Глава IV: Генеральная Ассамблея +СОСТАВ +Статья 9 +Генеральная Ассамблея состоит из всех Членов Организации. +Каждый Член Организации имеет не более пяти представителей в Генеральной Ассамблее. +ФУНКЦИИ и ПОЛНОМОЧИЯ +Статья 10 +Генеральная Ассамблея уполномочивается обсуждать любые вопросы или дела в пределах настоящего Устава или относящиеся к полномочиям и функциям любого из органов, предусмотренных настоящим Уставом, и, за исключениями, предусмотренными статьей 12, делать рекомендации Членам Организации Объединенных Наций или Совету Безопасности или и Членам Организации и Совету Безопасности по любым таким вопросам или делам. + +Статья 11 +Генеральная Ассамблея уполномочивается рассматривать общие принципы сотрудничества в деле поддержания международного мира и безопасности, в том числе принципы, определяющие разоружение и регулирование вооружений, и делать в отношении этих принципов рекомендации Членам Организации или Совету Безопасности или и Членам Организации и Совету Безопасности. +Генеральная Ассамблея уполномочивается обсуждать любые вопросы, относящиеся к поддержанию международного мира и безопасности, поставленные перед нею любым Членом Организации или Советом Безопасности или государством, которое не является Членом Организации, в соответствии с пунктом 2 статьи 35, и за исключениями, предусмотренными статьей 12, делать в отношении любых таких вопросов рекомендации заинтересованному государству или государствам или Совету Безопасности или и Совету Безопасности и заинтересованному государству или государствам. Любой такой вопрос, по которому необходимо предпринять действие, передается Генеральной Ассамблеей Совету Безопасности до или после обсуждения. +Генеральная Ассамблея может обращать внимание Совета Безопасности на ситуации, которые могли бы угрожать международному миру и безопасности. +Полномочия Генеральной Ассамблеи, изложенные в настоящей статье, не должны ограничивать общего смысла статьи 10. +Статья 12 +Когда Совет Безопасности выполняет возложенные на него настоящим Уставом функции по отношению к какому-либо спору или ситуации, Генеральная Ассамблея не может делать какие-либо рекомендации, касающиеся данного спора или ситуации, если Совет Безопасности не запросит об этом. +Генеральный Секретарь, с согласия Совета Безопасности, уведомляет Генеральную Ассамблею на каждой ее сессии о всех вопросах, относящихся к поддержанию международного мира и безопасности, находящихся на рассмотрении Совета Безопасности, и таким же образом уведомляет Генеральную Ассамблею, а если Генеральная Ассамблея не заседает, то Членов Организации, немедленно, как только Совет Безопасности прекратит рассмотрение таких вопросов. +Статья 13 +Генеральная Ассамблея организует исследования и делает рекомендации в целях: +Содействия международному сотрудничеству в политической области и поощрения прогрессивного развития международного права и его кодификации; +Содействия международному сотрудничеству в области экономической, социальной, культуры, образования, здравоохранения и содействия осуществлению прав человека и основных свобод для всех, без различия расы, пола, языка и религии. +Дальнейшие обязанности, функции и полномочия Генеральной Ассамблеи в отношении вопросов, упомянутых выше в пункте 1b, изложены в Главах IX и X. +Статья 14 +С соблюдением положений статьи 12, Генеральная Ассамблея уполномочивается рекомендовать меры мирного улаживания любой ситуации, независимо от ее происхождения, которая, по мнению Ассамблеи, могла бы нарушить общее благополучие или дружественные отношения между нациями, включая ситуации, возникающие в результате нарушения положений настоящего Устава, излагающих Цели и Принципы Объединенных Наций. + +Статья 15 +Генеральная Ассамблея получает и рассматривает ежегодные и специальные доклады Совета Безопасности; эти доклады должны включать отчет о мерах по поддержанию международного мира и безопасности, которые Совет Безопасности решил предпринять или предпринял. +Генеральная Ассамблея получает и рассматривает доклады других органов Организации. +Статья 16 +Генеральная Ассамблея выполняет в отношении международной системы опеки такие функции, которые возложены на нее на основании Глав XII и XIII, включая утверждение соглашений по опеке для территорий, не относящихся к числу стратегических. + +Статья 17 +Генеральная Ассамблея рассматривает и утверждает бюджет Организации. +Члены Организации несут ее расходы по распределению, установленному Генеральной Ассамблеей. +Генеральная Ассамблея рассматривает и утверждает любые финансовые и бюджетные соглашения со специализированными учреждениями, упомянутыми в статье 57, и проверяет административные бюджеты таких специализированных учреждений с той целью, чтобы сделать рекомендации заинтересованным учреждениям. +ГОЛОСОВАНИЕ +Статья 18 +Каждый Член Генеральной Ассамблеи имеет один голос. +Решения Генеральной Ассамблеи по важным вопросам принимаются большинством в две трети присутствующих и участвующих в голосовании членов Ассамблеи. Эти вопросы включают: рекомендации в отношении поддержания международного мира и безопасности, выборы непостоянных членов Совета Безопасности, выборы членов Экономического и Социального Совета, выборы членов Совета по Опеке, в соответствии с пунктом 1с статьи 86, прием новых Членов в Организацию Объединенных Наций, приостановление прав и привилегий Членов Организации, исключение из Организации ее Членов, вопросы, относящиеся к функционированию системы опеки, и бюджетные вопросы. +Решения по другим вопросам, включая определение дополнительных категорий вопросов, которые подлежат решению большинством в две трети голосов, принимаются простым большинством присутствующих и участвующих в голосовании. +Статья 19 +Член Организации, за которым числится задолженность по уплате Организации денежных взносов, лишается права голоса в Генеральной Ассамблее, если сумма его задолженности равняется или превышает сумму взносов, причитающихся с него за два полных предыдущих года. Генеральная Ассамблея может, однако, разрешить такому Члену Организации участвовать в голосовании, если она признает, что просрочка платежа произошла по не зависящим от него обстоятельствам. + +ПРОЦЕДУРА +Статья 20 +Генеральная Ассамблея собирается на очередные ежегодные сессии и на такие специальные сессии, которых могут потребовать обстоятельства. Специальные сессии созываются Генеральным Секретарем по требованию Совета Безопасности или большинства Членов Организации. + +Статья 21 +Генеральная Ассамблея устанавливает свои собственные правила процедуры. Она избирает своего Председателя на каждую сессию. + +Статья 22 +Генеральная Ассамблея уполномочивается учреждать такие вспомогательные органы, которые она сочтет необходимыми для осуществления своих функций. + +Глава V: Совет Безопасности +СОСТАВ +Статья 23 +Совет Безопасности состоит из пятнадцати Членов Организации. Китайская Республика, Франция, Союз Советских Социалистических Республик, Соединенное Королевство Великобритании и Северной Ирландии и Соединенные Штаты Америки являются постоянными членами Совета Безопасности. Генеральная Ассамблея избирает десять других Членов Организации в качестве непостоянных членов Совета Безопасности, уделяя, в особенности, должное внимание, в первую очередь, степени участия Членов Организации в поддержании международного мира и безопасности и в достижении других целей Организации, а также справедливому географическому распределению. +Непостоянные члены Совета Безопасности избираются на двухгодичный срок. При первых выборах непостоянных членов, после увеличения Совета Безопасности с одиннадцати до пятнадцати, два из четырех дополнительных членов избираются на срок в один год. Выбывающий член Совета Безопасности не подлежит немедленному переизбранию. +Каждый член Совета Безопасности имеет одного представителя. +ФУНКЦИИ И ПОЛНОМОЧИЯ +Статья 24 +Для обеспечения быстрых и эффективных действий Организации Объединенных Наций ее Члены возлагают на Совет Безопасности главную ответственность за поддержание международного мира и безопасности и соглашаются в том, что при исполнении его обязанностей, вытекающих из этой ответственности, Совет Безопасности действует от их имени. +При исполнении этих обязанностей Совет Безопасности действует в соответствии с Целями и Принципами Объединенных Наций. Определенные полномочия, предоставленные Совету Безопасности для выполнения этих обязанностей, изложены в Главах VI, VII, VIII и XII. +Совет Безопасности представляет на рассмотрение Генеральной Ассамблее ежегодные доклады и, по мере надобности, специальные доклады. +Статья 25 +Члены Организации соглашаются, в соответствии с настоящим Уставом, подчиняться с решениями Совета Безопасности и выполнять их. + +Статья 26 +В целях содействия установлению и поддержанию международного мира и безопасности с наименьшим отвлечением мировых людских сил и экономических ресурсов для дела вооружения, Совет Безопасности несет ответственность за формулирование, при помощи Военно-Штабного Комитета, указанного в статье 47, планов создания системы регулирования вооружений для представления их Членам Организации. + +ГОЛОСОВАНИЕ +Статья 27 +Каждый член Совета Безопасности имеет один голос. +Решения Совета Безопасности по вопросам процедуры считаются принятыми, когда за них поданы голоса девяти членов Совета. +Решения Совета Безопасности по всем другим вопросам считаются принятыми, когда за них поданы голоса девяти членов Совета, включая совпадающие голоса всех постоянных членов Совета, причем сторона, участвующая в споре, должна воздержаться от голосования при принятии решения на основании Главы VI и на основании пункта 3 статьи 52. +ПРОЦЕДУРА +Статья 28 +Совет Безопасности организуется таким образом, чтобы он мог функционировать непрерывно. Для этой цели каждый член Совета Безопасности должен быть всегда представлен в месте пребывания Организации Объединенных Наций. +Совет Безопасности собирается на периодические заседания, на которых каждый из его членов может, по своему желанию, быть представлен или членом правительства или каким-либо другим особо назначенным представителем. +Заседания Совета Безопасности могут происходить не только в месте пребывания Организации, но и во всяком другом месте, которое, по мнению Совета, более способствует его работе. +Статья 29 +Совет Безопасности может учреждать такие вспомогательные органы, какие он найдет необходимыми для выполнения своих функций. + +Статья 30 +Совет Безопасности устанавливает свои правила процедуры, включая порядок избрания своего Председателя. + +Статья 31 +Любой Член Организации, который не является членом Совета Безопасности, может принять участие, без права голоса, в обсуждении любого вопроса, внесенного в Совет Безопасности, во всех тех случаях, когда Совет Безопасности находит, что интересы этого Члена Организации специально затронуты. + +Статья 32 +Любой Член Организации, который не состоит членом Совета Безопасности, или любое государство, не состоящее Членом Организации, если они являются сторонами в споре, рассматриваемом Советом Безопасности, приглашаются принять участие, без права голоса, в обсуждении, относящемся к этому спору. Совет Безопасности ставит такие условия для участия государства, не состоящего Членом Организации, какие он найдет справедливыми. + +Глава VI: Мирное разрешение споров +Статья 33 +Стороны, участвующие в любом споре, продолжение которого могло бы угрожать поддержанию международного мира и безопасности, должны прежде всего стараться разрешить спор путем переговоров, обследования, посредничества, примирения, арбитража, судебного разбирательства, обращения к региональным органам или соглашениям или иными мирными средствами по своему выбору. +Совет Безопасности, когда он считает это необходимым, требует от сторон разрешения их спора при помощи таких средств. +Статья 34 +Совет Безопасности уполномочивается расследовать любой спор или любую ситуацию, которая может привести к международным трениям или вызвать спор, для определения того, не может ли продолжение этого спора или ситуации угрожать поддержанию международного мира и безопасности. + +Статья 35 +Любой Член Организации может довести о любом споре или ситуации, имеющей характер, указанный в статье 34, до сведения Совета Безопасности или Генеральной Ассамблеи. +Государство, которое не является Членом Организации, может довести до сведения Совета Безопасности или Генеральной Ассамблеи о любом споре, в котором оно является стороной, если оно примет на себя заранее в отношении этого спора обязательства мирного разрешения споров, предусмотренные в настоящем Уставе. +Разрешение Генеральной Ассамблеей дел, о которых доведено до ее сведения на основании настоящей статьи, производится с учетом положений статей 11 и 12. +Статья 36 +Совет Безопасности уполномочивается в любой стадии спора, имеющего характер, указанный в статье 33, или ситуации подобного же характера рекомендовать надлежащую процедуру или методы урегулирования. +Совет Безопасности принимает во внимание любую процедуру для разрешения этого спора, которая уже была принята сторонами. +Делая рекомендации на основании настоящей статьи, Совет Безопасности принимает также во внимание, что споры юридического характера должны, как общее правило, передаваться сторонами в Международный Суд в соответствии с положениями Статута Суда. +Статья 37 +Если стороны в споре, имеющем характер, указанный в статье 33, не разрешат его при помощи указанных в этой статье средств, они передают его в Совет Безопасности. +Если Совет Безопасности считает, что продолжение данного спора в действительности могло бы угрожать поддержанию международного мира и безопасности, он решает, действовать ли ему на основании статьи 36 или рекомендовать такие условия разрешения спора, какие он найдет подходящими. +Статья 38 +Без ущерба для положений статей 33–37 Совет Безопасности уполномочивается, если все стороны, участвующие в любом споре, об этом просят, делать сторонам рекомендации с целью мирного разрешения этого спора. + +Глава VII: Действия в отношении угрозы миру, нарушений мира и актов агрессии +Статья 39 +Совет Безопасности определяет существование любой угрозы миру, любого нарушения мира или акта агрессии и делает рекомендации или решает о том, какие меры следует предпринять в соответствии со статьями 41 и 42 для поддержания или восстановления международного мира и безопасности. + +Статья 40 +Чтобы предотвратить ухудшение ситуации, Совет Безопасности уполномочивается, прежде чем сделать рекомендации или решить о принятии мер, предусмотренных статьей 39, потребовать от заинтересованных сторон выполнения тех временных мер, которые он найдет необходимыми или желательными. Такие временные меры не должны наносить ущерба правам, притязаниям или положению заинтересованных сторон. Совет Безопасности должным образом учитывает невыполнение этих временных мер. + +Статья 41 +Совет Безопасности уполномочивается решать, какие меры, не связанные с использованием вооруженных сил, должны применяться для осуществления его решений, и он может потребовать от Членов Организации применения этих мер. Эти меры могут включать полный или частичный перерыв экономических отношений, железнодорожных, морских, воздушных, почтовых, телеграфных, радио или других средств сообщения, а также разрыв дипломатических отношений. + +Статья 42 +Если Совет Безопасности сочтет, что меры, предусмотренные в статье 41, могут оказаться недостаточными или уже оказались недостаточными, он уполномочивается предпринимать такие действия воздушными, морскими или сухопутными силами, какие окажутся необходимыми для поддержания или восстановления международного мира и безопасности. Такие действия могут включать демонстрации, блокаду и другие операции воздушных, морских или сухопутных сил Членов Организации. + +Статья 43 +Все Члены Организации для того, чтобы внести свой вклад в дело поддержания международного мира и безопасности, обязуются предоставлять в распоряжение Совета Безопасности по его требованию и в соответствии с особым соглашением или соглашениями необходимые для поддержания международного мира и безопасности вооруженные силы, помощь и соответствующие средства обслуживания, включая право прохода. +Такое соглашение или соглашения определяют численность и род войск, степень их готовности и их общее расположение и характер предоставляемых средств обслуживания и помощи. +Переговоры о заключении соглашения или соглашений предпринимаются в возможно кратчайший срок по инициативе Совета Безопасности. Они заключаются между Советом Безопасности и Членами Организации или между Советом Безопасности и группами Членов Организации и подлежат ратификации подписавшими их государствами, в соответствии с их конституционной процедурой. +Статья 44 +Когда Совет Безопасности решил применить силу, то, прежде чем потребовать от Члена Организации, не представленного в Совете, предоставления вооруженных сил во исполнение обязательств, принятых им на основании статьи 43, Совет Безопасности приглашает этого Члена Организации, если последний этого пожелает, принять участие в решениях Совета Безопасности относительно использования контингентов вооруженных сил данного Члена Организации. + +Статья 45 +В целях обеспечения для Организации Объединенных Наций возможности предпринимать срочные военные мероприятия, Члены Организации должны держать в состоянии немедленной готовности контингенты национальных военно-воздушных сил для совместных международных принудительных действий. Численность и степень готовности этих контингентов и планы их совместных действий определяются Советом Безопасности с помощью Военно-Штабного Комитета в пределах, указанных в особом соглашении или соглашениях, упомянутых в статье 43. + +Статья 46 +Планы применения вооруженных сил составляются Советом Безопасности с помощью Военно-Штабного Комитета. + +Статья 47 +Создается Военно-Штабной Комитет для того, чтобы давать советы и оказывать помощь Совету Безопасности по всем вопросам, относящимся к военным потребностям Совета Безопасности в деле поддержания международного мира и безопасности, к использованию войск, предоставленных в его распоряжение, и к командованию ими, а также к регулированию вооружений и к возможному разоружению. +Военно-Штабной Комитет состоит из Начальников Штабов постоянных членов Совета Безопасности или их представителей. Любой Член Организации, не представленный постоянно в Комитете, приглашается Комитетом сотрудничать с ним, если эффективное осуществление обязанностей Комитета требует участия этого Члена Организации в работе Комитета. +Военно-Штабной Комитет, находясь в подчинении Совета Безопасности, несет ответственность за стратегическое руководство любыми вооруженными силами, предоставленными в распоряжение Совета Безопасности. Вопросы, относящиеся к командованию такими силами, должны быть разработаны позднее. +Военно-Штабной Комитет может, с разрешения Совета Безопасности и после консультации с надлежащими региональными органами, учреждать свои региональные подкомитеты. +Статья 48 +Действия, которые требуются для выполнения решений Совета Безопасности в целях поддержания международного мира и безопасности, предпринимаются всеми Членами Организации или некоторыми из них, в зависимости от того, как это определит Совет Безопасности. +Такие решения выполняются Членами Организации непосредственно, а также путем их действий в соответствующих международных учреждениях, членами которых они являются. +Статья 49 +Члены Организации должны объединяться для оказания взаимной помощи в проведении мер, о которых принято решение Советом Безопасности. + +Статья 50 +Если Советом Безопасности принимаются превентивные или принудительные меры против какого-либо государства, всякое другое государство, независимо от того, состоит ли оно Членом Организации, перед которым встанут специальные экономические проблемы, возникшие из проведения вышеупомянутых мер, имеет право консультироваться с Советом Безопасности на предмет разрешения таких проблем. + +Статья 51 +Настоящий Устав ни в коей мере не затрагивает неотъемлемого права на индивидуальную или коллективную самооборону, если произойдет вооруженное нападение на Члена Организации, до тех пор пока Совет Безопасности не примет мер, необходимых для поддержания международного мира и безопасности. Меры, принятые Членами Организации при осуществлении этого права на самооборону, должны быть немедленно сообщены Совету Безопасности и никоим образом не должны затрагивать полномочий и ответственности Совета Безопасности, в соответствии с настоящим Уставом, в отношении предпринятия в любое время таких действий, какие он сочтет необходимыми для поддержания или восстановления международного мира и безопасности. + +Глава VIII: Региональные соглашения +Статья 52 +Настоящий Устав ни в коей мере не препятствует существованию региональных соглашений или органов для разрешения таких вопросов, относящихся к поддержанию международного мира и безопасности, которые являются подходящими для региональных действий, при условии, что такие соглашения или органы и их деятельность совместимы с Целями и Принципами Организации. +Члены Организации, заключившие такие соглашения или составляющие такие органы, должны приложить все свои усилия для достижения мирного разрешения местных споров при помощи таких региональных соглашений или таких региональных органов до передачи этих споров в Совет Безопасности. +Совет Безопасности должен поощрять развитие применения мирного разрешения местных споров при помощи таких региональных соглашений или таких региональных органов либо по инициативе заинтересованных государств, либо по своей собственной инициативе. +Настоящая статья ни в коей мере не затрагивает применения статей 34 и 35. +Статья 53 +Совет Безопасности использует, где это уместно, такие региональные соглашения или органы для принудительных действий под его руководством. Однако никакие принудительные действия не предпринимаются, в силу этих региональных соглашений или региональными органами, без полномочий от Совета Безопасности, за исключением мер, предусмотренных статьей 107, против любого вражеского государства, как оно определено в пункте 2 настоящей статьи, или мер, предусмотренных в региональных соглашениях, направленных против возобновления агрессивной политики со стороны любого такого государства до того времени, когда на Организацию, по просьбе заинтересованных Правительств, может быть возложена ответственность за предупреждение дальнейшей агрессии со стороны такого государства. +Термин «вражеское государство», как он применен в пункте 1 настоящей статьи, относится к любому государству, которое в течение второй мировой войны являлось врагом любого из государств, подписавших настоящий Устав. +Статья 54 +Совет Безопасности должен быть всегда полностью информирован о действиях, предпринятых или намечаемых в силу региональных соглашений или региональными органами, для поддержания международного мира и безопасности. + +ГЛАВА IX: Международное экономическое и социальное сотрудничество +Статья 55 +С целью создания условий стабильности и благополучия, необходимых для мирных и дружеских отношений между нациями, основанных на уважении принципа равноправия и самоопределения народов, Организация Объединенных Наций содействует: + +Повышению уровня жизни, полной занятости населения и условиям экономического и социального прогресса и развития; +Разрешению международных проблем в области экономической, социальной, здравоохранения и подобных проблем; международному сотрудничеству в области культуры и образования; +Всеобщему уважению и соблюдению прав человека и основных свобод для всех, без различия расы, пола, языка и религии. +Статья 56 +Все Члены Организации обязуются предпринимать совместные и самостоятельные действия в сотрудничестве с Организацией для достижения целей, указанных в статье 55. + +Статья 57 +Различные специализированные учреждения, созданные межправительственными соглашениями и облеченные широко международной, определенной в их учредительных актах, ответственностью в области экономической, социальной, культуры, образования, здравоохранения и подобных областях, будут поставлены в связь с Организацией в соответствии с положениями статьи 63. +Такие учреждения, которые будут поставлены указанным образом в связь с Организацией, именуются в последующих статьях «специализированные учреждения». +Статья 58 +Организация делает рекомендации по согласованию политики и деятельности специализированных учреждений. + +Статья 59 +Организация, в случае надобности, проявляет инициативу в том, чтобы заинтересованные государства приступили к переговорам о создании любых новых специализированных учреждений, которые потребуются для выполнения целей, указанных в статье 55. + +Статья 60 +Ответственность за выполнение функций Организации, указанных в настоящей Главе, возлагается на Генеральную Ассамблею и, под руководством Генеральной Ассамблеи, на Экономический и Социальный Совет, которому для этой цели предоставляются полномочия, указанные в Главе X. + +Глава X: Экономический и Социальный Совет +СОСТАВ +Статья 61 +Экономический и Социальный Совет состоит из пятидесяти четырех Членов Организации, избираемых Генеральной Ассамблеей. +С соблюдением положений, изложенных в пункте 3, восемнадцать членов Экономического и Социального Совета избираются ежегодно сроком на три года. Выбывающий член Совета может быть переизбран немедленно. +При первых выборах после увеличения числа членов Экономического и Социального Совета с двадцати семи до пятидесяти четырех, в дополнение к членам, избираемым вместо девяти членов, срок полномочий которых истекает в конце данного года, избираются двадцать семь дополнительных членов. Срок полномочий девяти из двадцати семи дополнительных членов, избранных таким образом, истекает в конце первого года, а срок полномочий других девяти членов — в конце второго года, в соответствии с постановлением Генеральной Ассамблеи. +Каждый член Экономического и Социального Совета имеет одного представителя. +ФУНКЦИИ И ПОЛНОМОЧИЯ +Статья 62 +Экономический и Социальный Совет уполномочивается предпринимать исследования и составлять доклады по международным вопросам в области экономической, социальной, культуры, образования, здравоохранения и подобным вопросам или побуждать к этому других, а также делать по любому из этих вопросов рекомендации Генеральной Ассамблее, Членам Организации и заинтересованным специализированным учреждениям. +Совет уполномочивается делать рекомендации в целях поощрения уважения и соблюдения прав человека и основных свобод для всех. +Совет уполномочивается подготавливать для представления Генеральной Ассамблее проекты конвенций по вопросам, входящим в его компетенцию. +Совет уполномочивается созывать, в соответствии с правилами, предписанными Организацией, международные конференции по вопросам, входящим в его компетенцию. +Статья 63 +Экономический и Социальный Совет уполномочивается вступать с любым из учреждений, указанных в статье 57, в соглашения, определяющие условия, на которых соответствующие учреждения будут поставлены в связь с Организацией. Такие соглашения подлежат утверждению Генеральной Ассамблеей. +Совет уполномочивается согласовывать деятельность специализированных учреждений посредством консультаций с ними и рекомендаций таким учреждениям и посредством рекомендаций Генеральной Ассамблее и Членам Организации. +Статья 64 +Экономический и Социальный Совет уполномочивается принимать надлежащие меры для получения от специализированных учреждений регулярных докладов. Совет уполномочивается заключать соглашения с Членами Организации и со специализированными учреждениями с целью получения от них докладов о мерах, предпринятых ими во исполнение его собственных рекомендаций и рекомендаций Генеральной Ассамблеи по вопросам, входящим в его компетенцию. +Совет уполномочивается сообщать Генеральной Ассамблее свои замечания по этим докладам. +Статья 65 +Экономический и Социальный Совет уполномочивается представлять Совету Безопасности информацию и, по предложению Совета Безопасности, обязан ему помогать. + +Статья 66 +Экономический и Социальный Совет осуществляет такие функции, какие входят в его компетенцию, в связи с выполнением рекомендаций Генеральной Ассамблеи. +Совет, с одобрения Генеральной Ассамблеи, уполномочивается выполнять работы по просьбе Членов Организации и по просьбе специализированных учреждений. +Совет должен выполнять такие другие функции, какие перечислены в других частях настоящего Устава или какие могут быть возложены на него Генеральной Ассамблеей. +ГОЛОСОВАНИЕ +Статья 67 +Каждый член Экономического и Социального Совета имеет один голос. +Решения Экономического и Социального Совета принимаются большинством голосов членов Совета, присутствующих и участвующих в голосовании. +ПРОЦЕДУРА +Статья 68 +Экономический и Социальный Совет создает комиссии в экономической и социальной области и по поощрению прав человека, а также такие другие комиссии, которые могут потребоваться для выполнения его функций. + +Статья 69 +Экономический и Социальный Совет приглашает любого Члена Организации участвовать без права голоса в обсуждении им любого вопроса, представляющего особый интерес для данного Члена Организации. + +Статья 70 +Экономический и Социальный Совет уполномочивается проводить мероприятия для участия без права голоса представителей специализированных учреждений в обсуждении вопросов в Совете или в созданных им комиссиях, а также для участия представителей Совета в обсуждении вопросов в специализированных учреждениях. + +Статья 71 +Экономический и Социальный Совет уполномочивается проводить надлежащие мероприятия для консультации с неправительственными организациями, заинтересованными в вопросах, входящих в его компетенцию. Такие мероприятия могут быть условлены с международными организациями, а в случае надобности, с национальными организациями после консультации с заинтересованным Членом Организации. + +Статья 72 +Экономический и Социальный Совет устанавливает свои собственные правила процедуры, включая порядок избрания своего Председателя. +Экономический и Социальный Совет созывается по мере надобности, в соответствии со своими правилами, которые должны включать положения о созыве заседаний по требованию большинства его членов. +Глава XI: Декларация в отношении несамоуправляющихся территорий +Статья 73 +Члены Организации Объединенных Наций, которые несут или принимают на себя ответственность за управление территориями, народы которых не достигли еще полного самоуправления, признают тот принцип, что интересы населения этих территорий являются первостепенными, и, как священный долг, принимают обязательство максимально способствовать благополучию населения этих территорий в рамках системы международного мира и безопасности, установленной настоящим Уставом, и с этой целью: + +Обеспечивать, соблюдая должное уважение к культуре указанных народов, их политический, экономический и социальный прогресс, прогресс в области образования, справедливое обращение с ними и защиту их от злоупотреблений; +Развивать самоуправление, учитывать должным образом политические стремления этих народов и помогать им в прогрессивном развитии их свободных политических институтов в соответствии со специфическими обстоятельствами, присущими каждой территории и ее народам, и с их разными ступенями развития; +Укреплять международный мир и безопасность; +Способствовать развитию созидательных мероприятий, поощрять исследования и сотрудничать друг с другом и, где и когда это уместно, со специализированными международными организациями ради практического достижения изложенных в настоящей статье социальных, экономических и научных целей, и +Передавать регулярно Генеральному Секретарю для информации и с таким ограничением, какое может потребоваться по соображениям безопасности и конституционного порядка, статистическую и другую информацию специального характера, относящуюся к экономическим и социальным условиям, а также условиям образования на территориях, за которые они соответственно несут ответственность, кроме тех территорий, на которые распространяется действие Глав XII и XIII. +Статья 74 +Члены Организации также соглашаются, что их политика в отношении территорий, на которые распространяется действие настоящей Главы, должна быть основана не менее, чем в отношении их метрополий, на общем принципе добрососедства, с надлежащим учетом интересов и благополучия остального мира в делах социальных, экономических и торговли. + +Глава XII: Международная система опеки +Статья 75 +Организация Объединенных Наций создает под своим руководством международную систему опеки для управления теми территориями, которые могут быть включены в нее последующими индивидуальными соглашениями, и для наблюдения за этими территориями. Эти территории именуются далее «территории под опекой». + +Статья 76 +Основные задачи системы опеки, в соответствии с Целями Организации Объединенных Наций, изложенными в статье 1 настоящего Устава, состоят в том, чтобы: + +Укреплять международный мир и безопасность; +Способствовать политическому, экономическому и социальному прогрессу населения территорий под опекой, его прогрессу в области образования и его прогрессивному развитию в направлении к самоуправлению или независимости, как это может оказаться подходящим для специфических условий каждой территории и ее народов и имея в виду свободно выраженное желание этих народов, и как это может быть предусмотрено условиями каждого соглашения об опеке; +Поощрять уважение прав человека и основных свобод для всех, без различия расы, пола, языка, религии, и поощрять признание взаимозависимости народов мира; +Обеспечивать равное отношение к Членам Организации и их гражданам в области социальной, экономической и торговой, а также равное отношение к ним в отправлении правосудия без ущерба для достижения выше изложенных задач и при условии соблюдения положений статьи 80. +Статья 77 +Система опеки распространяется на такие территории из ниже перечисленных категорий, которые могут быть включены в нее соглашениями об опеке: +Территории, ныне находящиеся под мандатом; +Территории, которые могут быть отторгнуты от вражеских государств в результате второй мировой войны, и +Территории, добровольно включенные в систему опеки государствами, ответственными за их управление. +Вопрос о том, какие из территорий выше перечисленных категорий должны быть включены в систему опеки и на каких условиях, будет предметом последующего соглашения. +Статья 78 +Система опеки не распространяется на страны, ставшие Членами Организации, отношения между которыми должны основываться на уважении принципа суверенного равенства. + +Статья 79 +Условия опеки для каждой территории, подлежащей включению в систему опеки, в том числе все изменения и поправки, определяются соглашениями непосредственно заинтересованных государств, включая страны-мандатарии, в том случае, если территории находятся под мандатом одного из Членов Организации, и утверждаются, как предусмотрено в статьях 83 и 85. + +Статья 80 +За исключением случаев, которые могут быть согласованы в индивидуальных соглашениях об опеке, заключенных согласно статьям 77, 79 и 81, включающих каждую территорию в систему опеки, и впредь до заключения таких соглашений, ничто в настоящей Главе не должно толковаться как изменение каким-либо образом каких бы то ни было прав любых государств или любых народов или условий существующих международных соглашений, участниками которых могут быть соответственно Члены Организации. +Пункт 1 настоящей статьи не должен толковаться как дающий основания для задержки или отсрочки переговоров и заключения соглашений о включении под мандатных и других территорий в систему опеки, как это предусмотрено в статье 77. +Статья 81 +Соглашение об опеке в каждом случае должно включать условия, на которых будет управляться территория под опекой, а также определять власть, которая будет осуществлять управление территорией под опекой. Такая власть, называемая далее управляющей властью, может представлять собою одно или более государств или Организацию Объединенных Наций, как таковую. + +Статья 82 +В любом соглашении об опеке может определяться стратегический район или районы, которые могут включать часть или всю территорию под опекой, на которую распространяется соглашение, без ущерба для какого бы то ни было особого соглашения или соглашений, заключенных на основании статьи 43. + +Статья 83 +Все функции Организации Объединенных Наций, относящиеся к стратегическим районам, включая утверждение условий соглашений об опеке и их изменений или поправок к ним, осуществляются Советом Безопасности. +Основные цели, изложенные в статье 76, относятся к народу каждого из стратегических районов. +Совет Безопасности, соблюдая условия соглашений об опеке и без ущерба для требований безопасности, пользуется помощью Совета по Опеке для выполнения тех функций Организации Объединенных Наций, в соответствии с системой опеки, которые относятся к политическим, экономическим и социальным вопросам, а также к вопросам в области образования в стратегических районах. +Статья 84 +Обязанностью управляющей власти является обеспечение того, чтобы территория под опекой играла свою роль в поддержании международного мира и безопасности. С этой целью управляющая власть уполномочивается использовать добровольные вооруженные силы, средства обслуживания и помощь территории под опекой при выполнении обязательств, принятых в этом отношении управляющей властью перед Советом Безопасности, а равно и для местной обороны и поддержания закона и порядка в пределах территории под опекой. + +Статья 85 +Функции Организации Объединенных Наций в отношении соглашений об опеке для всех районов, не отнесенных к числу стратегических, включая утверждение условий соглашений об опеке и их изменений или поправок к ним, осуществляются Генеральной Ассамблеей. +Совет по Опеке, действующий под руководством Генеральной Ассамблеи, помогает Генеральной Ассамблее в выполнении этих функций. +Глава XIII: Совет по Опеке +СОСТАВ +Статья 86 +Совет по Опеке состоит из следующих Членов Организации Объединенных Наций: +Тех Членов Организации, которые управляют территориями под опекой; +Тех Членов Организации, поименованных в статье 23, которые не управляют территориями под опекой; +Такого числа других Членов Организации, избранных Генеральной Ассамблеей на трехгодичный срок, какое может оказаться необходимым для обеспечения того, чтобы общее число членов Совета по Опеке распределялось поровну между Членами Организации, управляющими и не управляющими территориями под опекой. +Каждый Член Совета по Опеке назначит одно особо квалифицированное лицо, которое будет его представителем в Совете по Опеке. +ФУНКЦИИ И ПОЛНОМОЧИЯ +Статья 87 +Генеральная Ассамблея и находящийся под ее руководством Совет по Опеке при выполнении своих функций уполномочиваются: + +Рассматривать отчеты, представляемые управляющей властью; +Принимать петиции и рассматривать их, консультируясь с управляющей властью; +Устраивать периодические посещения соответствующих территорий под опекой в согласованные с управляющей властью сроки; и +Предпринимать упомянутые и другие действия в соответствии с условиями соглашений об опеке. +Статья 88 +Совет по Опеке разрабатывает анкету относительно политического, экономического и социального прогресса населения каждой территории под опекой, а также его прогресса в области образования, а управляющая власть каждой территории под опекой, входящей в компетенцию Генеральной Ассамблеи, представляет последней ежегодные доклады на основе этой анкеты. + +ГОЛОСОВАНИЕ +Статья 89 +Каждый член Совета по Опеке имеет один голос. +Решения Совета по Опеке принимаются большинством голосов присутствующих и участвующих в голосовании членов Совета. +ПРОЦЕДУРА +Статья 90 +Совет по Опеке принимает свои собственные правила процедуры, включая порядок избрания своего Председателя. +Заседания Совета по Опеке созываются по мере надобности в соответствии с его правилами процедуры, которые должны предусматривать созыв заседаний по требованию большинства членов Совета. +Статья 91 +Совет по Опеке пользуется в соответствующих случаях помощью Экономического и Социального Совета и специализированных учреждений в отношении вопросов, в которых они соответственно заинтересованы. + +Глава XIV: Международный Суд +Статья 92 +Международный Суд является главным судебным органом Организации Объединенных Наций. Он действует в соответствии с прилагаемым Статутом, который основан на Статуте Постоянной Палаты Международного Правосудия и образует неотъемлемую часть настоящего Устава. + +Статья 93 +Все Члены Организации являются ipso facto участниками Статута Международного Суда. +Государство, не являющееся Членом Организации, может стать участником Статута Международного Суда на условиях, которые определяются, в каждом отдельном случае, Генеральной Ассамблеей по рекомендации Совета Безопасности. +Статья 94 +Каждый Член Организации обязуется выполнить решение Международного Суда по тому делу, в котором он является стороной. +В случае, если какая-либо сторона в деле не выполнит обязательства, возложенного на нее решением Суда, другая сторона может обратиться в Совет Безопасности, который может, если признает это необходимым, сделать рекомендации или решить о принятии мер для приведения решения в исполнение. +Статья 95 +Настоящий Устав ни в коей мере не препятствует Членам Организации поручать разрешение своих разногласий другим судам в силу уже существующих соглашений или таких, которые могут быть заключены в будущем. + +Статья 96 +Генеральная Ассамблея или Совет Безопасности могут запрашивать от Международного Суда консультативные заключения по любому юридическому вопросу. +Другие органы Организации Объединенных Наций и специализированные учреждения, которым Генеральная Ассамблея может дать в любое время разрешение на это, также могут запрашивать консультативные заключения Суда по юридическим вопросам, возникающим в пределах их круга деятельности. +Глава XV: Секретариат +Статья 97 +Секретариат состоит из Генерального Секретаря и такого персонала, который может потребоваться для Организации. Генеральный Секретарь назначается Генеральной Ассамблеей по рекомендации Совета Безопасности. Генеральный Секретарь является главным административным должностным лицом Организации. + +Статья 98 +Генеральный Секретарь действует в этом качестве на всех заседаниях Генеральной Ассамблеи, Совета Безопасности, Экономического и Социального Совета и Совета по Опеке и выполняет такие другие функции, какие возлагаются на него этими органами. Генеральный Секретарь представляет Генеральной Ассамблее ежегодный отчет о работе Организации. + +Статья 99 +Генеральный Секретарь имеет право доводить до сведения Совета Безопасности о любых вопросах, которые, по его мнению, могут угрожать поддержанию международного мира и безопасности. + +Статья 100 +При исполнении своих обязанностей Генеральный Секретарь и персонал Секретариата не должны запрашивать или получать указания от какого бы то ни было правительства или власти, посторонней для Организации. Они должны воздерживаться от любых действий, которые могли бы отразиться на их положении как международных должностных лиц, ответственных только перед Организацией. +Каждый Член Организации обязуется уважать строго международный характер обязанностей Генерального Секретаря и персонала Секретариата и не пытаться оказывать на них влияние при исполнении ими своих обязанностей. +Статья 101 +Персонал Секретариата назначается Генеральным Секретарем, согласно правилам, устанавливаемым Генеральной Ассамблеей. +Надлежащий персонал выделяется для постоянной работы в Экономический и Социальный Совет, в Совет по Опеке и, по мере надобности, в другие органы Организации. Этот персонал составляет часть Секретариата. +При приеме на службу и определении условий службы следует руководствоваться, главным образом, необходимостью обеспечить высокий уровень работоспособности, компетентности и добросовестности. Должное внимание следует уделять важности подбора персонала на возможно более широкой географической основе. +Глава XVI: Разные постановления +Статья 102 +Всякий договор и всякое международное соглашение, заключенные любым Членом Организации после вступления в силу настоящего Устава, должны быть, при первой возможности, зарегистрированы в Секретариате и им опубликованы. +Ни одна из сторон в любом таком договоре или международном соглашении, не зарегистрированных в соответствии с пунктом 1 настоящей статьи, не может ссылаться на такой договор или соглашение ни в одном из органов Организации Объединенных Наций. +Статья 103 +В том случае, когда обязательства Членов Организации по настоящему Уставу окажутся в противоречии с их обязательствами по какому-либо другому международному соглашению, преимущественную силу имеют обязательства по настоящему Уставу. + +Статья 104 +Организация Объединенных Наций пользуется на территории каждого из своих Членов такой правоспособностью, которая может оказаться необходимой для выполнения ее функций и достижения ее целей. + +Статья 105 +Организация Объединенных Наций пользуется на территории каждого из своих Членов такими привилегиями и иммунитетами, которые необходимы для достижения ее целей. +Представители Членов Организации и ее должностные лица также пользуются привилегиями и иммунитетами, которые необходимы для самостоятельного выполнения ими своих функций, связанных с деятельностью Организации. +Генеральная Ассамблея может делать рекомендации для определения деталей применения пунктов 1 и 2 настоящей статьи, а также может предлагать Членам Организации конвенции для этой цели. +Глава XVII: Мероприятия по безопасности в переходный период +Статья 106 +Впредь до вступления в силу таких упомянутых в статье 43 особых соглашений, какие, по мнению Совета Безопасности, дают ему возможность начать осуществление своих обязанностей, согласно статье 42, участники Декларации Четырех Держав, подписанной в Москве 30 октября 1943 г., и Франция будут, в соответствии с положениями пункта 5 этой Декларации, консультироваться друг с другом и, в случае необходимости, с другими Членами Организации с целью таких совместных действий от имени Организации, какие могут оказаться необходимыми для поддержания международного мира и безопасности. + +Статья 107 +Настоящий Устав ни в коей мере не лишает юридической силы действия, предпринятые или санкционированные в результате второй мировой войны несущими ответственность за такие действия правительствами, в отношении любого государства, которое в течение второй мировой войны было врагом любого из государств, подписавших настоящий Устав, а также не препятствует таким действиям. + +Глава XVIII: Поправки +Статья 108 +Поправки к настоящему Уставу вступают в силу для всех Членов Организации, после того как они приняты двумя третями голосов членов Генеральной Ассамблеи и ратифицированы, в соответствии с их конституционной процедурой, двумя третями Членов Организации, включая всех постоянных членов Совета Безопасности. + +Статья 109 +С целью пересмотра настоящего Устава может быть созвана Генеральная конференция Членов Организации Объединенных Наций в срок и в месте, которые должны быть определены двумя третями голосов членов Генеральной Ассамблеи и голосами любых девяти членов Совета Безопасности. Каждый Член Организации будет иметь на Конференции один голос. +Любое изменение настоящего Устава, рекомендованное двумя третями голосов участников Конференции, вступит в силу по ратификации, в соответствии с их конституционной процедурой, двумя третями Членов Организации, включая всех постоянных членов Совета Безопасности. +Если такая Конференция не состоится до десятой ежегодной сессии Генеральной Ассамблеи, считая со вступления настоящего Устава в силу, предложение созвать такую Конференцию включается в повестку дня этой сессии Генеральной Ассамблеи, и Конференция созывается, если это будет решено простым большинством голосов членов Генеральной Ассамблеи и голосами любых семи членов Совета Безопасности. +Глава XIX: Ратификация и подписание +Статья 110 +Настоящий Устав подлежит ратификации подписавшими его государствами, в соответствии с их конституционной процедурой. +Ратификационные грамоты должны сдаваться на хранение Правительству Соединенных Штатов Америки, которое будет извещать о сдаче на хранение каждой грамоты все государства, подписавшие Устав, также как и Генерального Секретаря Организации, когда он будет назначен. +Настоящий Устав вступит в силу по сдаче на хранение ратификационных грамот Китайской Республикой, Францией, Союзом Советских Социалистических Республик, Соединенным Королевством Великобритании и Северной Ирландии и Соединенными Штатами Америки и большинством других государств, подписавших Устав. После этого Правительством Соединенных Штатов Америки будет составлен протокол о сдаче на хранение ратификационных грамот, копии с которого будут разосланы всем подписавшим Устав государствам. +Государства, подписавшие настоящий Устав, которые ратифицируют его после того, как он вступит в силу, станут Первоначальными Членами Организации Объединенных Наций со дня сдачи ими на хранение своих соответствующих ратификационных грамот. +Статья 111 +Настоящий Устав, китайский, французский, русский, английский и испанский тексты которого являются равно аутентичными, будет храниться в архиве Правительства Соединенных Штатов Америки. Это Правительство препровождает копии Устава, должным образом заверенные, Правительствам всех других подписавших его государств. + +В УДОСТОВЕРЕНИЕ ЧЕГО представители Правительств Объединенных Наций подписали настоящий Устав. + +СОСТАВЛЕНО в городе Сан-Франциско, июня двадцать шестого дня, тысяча девятьсот сорок пятого года. + + + +Поправки к статьям 23, 27, 61, 109 +Поправки к статьям 23, 27 и 61 Устава были приняты Генеральной Ассамблеей 17 декабря 1963 года и вступили в силу 31 августа 1965 года. Поправка к статье 109, принятая Генеральной Ассамблеей 20 декабря 1965 года, вступила в силу 12 июня 1968 года. + +Поправка к статье 23 Устава увеличивает число членов Совета Безопасности с одиннадцати до пятнадцати. + +Исправленная статья 27 предусматривает, что решения Совета Безопасности по процедурным вопросам считаются принятыми, когда за них поданы голоса девяти членов (раньше — семи), и по всем другим вопросам — когда за них поданы голоса девяти членов (раньше — семи), включая совпадающие голоса пяти постоянных членов Совета Безопасности. + +Поправка к статье 61 увеличивает число членов Экономического и Социального Совета с восемнадцати до двадцати семи. Последующая поправка к этой статье, вступившая в силу 24 сентября 1973 года, увеличивает число членов Совета с двадцати семи до пятидесяти четырех. + +Поправка к первому пункту статьи 109 предусматривает, что время и место проведения Генеральной конференции государств-членов с целью пересмотра Устава определяются двумя третями голосов членов Генеральной Ассамблеи и голосами любых девяти (раньше — семи) членов Совета Безопасности. Пункт 3 статьи 109 , который предусматривает возможность созыва конференции по пересмотру Устава, был рассмотрен Генеральной Ассамблеей и Советом Безопасности на десятой очередной сессии Генеральной Ассамблеи в 1955 году и оставлен в его первоначальной формулировке: «голосами любых семи членов Совета Безопасности». \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_zh-CN.html b/stdlib/benchmarks/collections/data/UN_charter_zh-CN.html new file mode 100644 index 0000000000..a60d456724 --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_zh-CN.html @@ -0,0 +1,2365 @@ + + + + + + + + + + + + + + + + + + + 联合国宪章(全文) | 联合国 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    + + + + +
    +
    + + + + +
    +
    + + +
    + + + + + + + + + + +
    +
    +
    + +
    + + + +

    联合国宪章(全文)

    + + + + +
    +
    + + +
    + + + + +
    +
    +
    +
    +

    序言

    + +

    我联合国人民同兹决心

    + +

      欲免后世再遭今代人类两度身历惨不堪言之战祸,

    + +

      重申基本人权,人格尊严与价值,以及男女与大小各国平等权利之信念,

    + +

      创造适当环境,俾克维持正义,尊重由条约与国际法其他渊源而起之义务,久而弗懈,

    + +

      促成大自由中之社会进步及较善之民生,

    + +

    并为达此目的

    + +

      力行容恕,彼此以善邻之道,和睦相处,

    + +

      集中力量,以维持国际和平及安全,

    + +

      接受原则,确立方法,以保证非为公共利益,不得使用武力,

    + +

      运用国际机构,以促成全球人民经济及社会之进展,

    + +

    用是发愤立志,务当同心协力,以竟厥功

    + +

      爰由我各本国政府,经齐集金山市之代表各将所奉全权证书,互相校阅,均属妥善,议定本联合国宪章,并设立国际组织,定名联合国。

    + +

    第一章:宗旨及原则

    + +

    第一条

    + +

      联合国之宗旨为:

    + +

      一、维持国际和平及安全;并为此目的:采取有效集体办法,以防止且消除对于和平之威胁,制止侵略行为或其他和平之破坏;并以和平方法且依正义及国际法之原则,调整或解决足以破坏和平之国际争端或情势。

    + +

      二、发展国际间以尊重人民平等权利及自决原则为根据之友好关系,并采取其他适当办法,以增强普遍和平。

    + +

      三、促成国际合作,以解决国际间属于经济、社会、文化及人类福利性质之国际问题,且不分种族、性别、语言或宗教,增进并激励对于全体人类之人权及基本自由之尊重。

    + +

      四、构成一协调各国行动之中心,以达成上述共同目的。

    + +

    第二条

    + +

      为求实现第一条所述各宗旨起见,本组织及其会员国应遵行下列原则:

    + +

      一、本组织系基于各会员国主权平等之原则。

    + +

      二、各会员国应一秉善意,履行其依本宪章所担负之义务,以保证全体会员国由加入本组织而发生之权益。

    + +

      三、各会员国应以和平方法解决其国际争端,俾免危及国际和平、安全及正义。

    + +

      四、各会员国在其国际关系上不得使用威胁或武力,或以与联合国宗旨不符之任何其他方法,侵害任何会员国或国家之领土完整或政治独立。

    + +

      五、各会员国对于联合国依本宪章规定而采取之行动,应尽力予以协助,联合国对于任何国家正在采取防止或执行行动时,各会员国对该国不得给予协助。

    + +

      六、本组织在维持国际和平及安全之必要范围内,应保证非联合国会员国遵行上述原则。

    + +

      七、本宪章不得认为授权联合国干涉在本质上属于任何国家国内管辖之事件,且并不要求会员国将该项事件依本宪章提请解决;但此项原则不妨碍第七章内执行办法之适用。

    + +

    第二章:会员

    + +

    第三条

    + +

      凡曾经参加金山联合国国际组织会议或前此曾签字于一九四二年一月一日联合国宣言之国家,签订本宪章,且依宪章第一百一十条规定而予以批准者,均为联合国之创始会员国。

    + +

    第四条

    + +

      一、凡其他爱好和平之国家,接受本宪章所载之义务,经本组织认为确能并愿意履行该项义务者,得为联合国会员国。

    + +

      二、准许上述国家为联合国会员国,将由大会经安全理事会之推荐以决议行之。

    + +

    第五条

    + +

      联合国会员国,业经安全理事会对其采取防止或执行行动者,大会经安全理事会之建议,得停止其会员权利及特权之行使。此项权利及特权之行使,得由安全理事会恢复之。

    + +

    第六条

    + +

      联合国之会员国中,有屡次违犯本宪章所载之原则者,大会经安全理事会之建议,得将其由本组织除名。

    + +

    第三章:机关

    + +

    第七条

    + +

      一、兹设联合国之主要机关如下:大会、安全理事会、经济及社会理事会、托管理事会、国际法院、及秘书处。

    + +

      二、联合国得依本宪章设立认为必需之辅助机关。

    + +

    第八条

    + +

      联合国对于男女均得在其主要及辅助机关在平等条件之下,充任任何职务,不得加以限制。

    + +

    第四章:大会

    + +

    组织

    + +

    第九条

    + +

      一、大会由联合国所有会员国组织之。

    + +

      二、每一会员国在大会之代表,不得超过五人。

    + +

    职权

    + +

    第十条

    + +

      大会得讨论本宪章范围内之任何问题或事项,或关于本宪章所规定任何机关之职权;并除第十二条所规定外,得向联合国会员国或安全理事会或兼向两者,提出对各该问题或事项之建议。

    + +

    第十一条

    + +

      一、大会得考虑关于维持国际和平及安全之合作之普通原则,包括军缩及军备管制之原则;并得向会员国或安全理事会或兼向两者提出对于该项原则之建议。

    + +

      二、大会得讨论联合国任何会员国或安全理事会或非联合国会员国依第三十五条第二项之规定向大会所提关于维持国际和平及安全之任何问题;除第十二条所规定外,并得向会员国或安全理事会或兼向两者提出对于各该项问题之建议。凡对于需要行动之各该项问题,应由大会于讨论前或讨论后提交安全理事会。

    + +

      三、大会对于足以危及国际和平与安全之情势,得提请安全理事会注意。

    + +

      四、本条所载之大会权力并不限制第十条之概括范围。

    + +

    第十二条

    + +

      一、当安全理事会对于任何争端或情势,正在执行本宪章所授予该会之职务时,大会非经安全理事会请求,对于该项争端或情势,不得提出任何建议。

    + +

      二、秘书长经安全理事会之同意,应于大会每次会议时,将安全理事会正在处理中关于维持国际和平及安全之任何事件,通知大会;于安全理事会停止处理该项事件时,亦应立即通知大会,或在大会闭会期内通知联合国会员国。

    + +

    第十三条

    + +

      一、大会应发动研究,并作成建议:

    + +

        (子) 以促进政治上之国际合作,并提倡国际法之逐渐发展与编纂。

    + +

        (丑) 以促进经济、社会、文化、教育及卫生各部门之国际合作,且不分种族、性别、语言或宗教,助成全体人类之人权及基本自由之实现。

    + +

      二、大会关于本条第一项(丑)款所列事项之其他责任及职权,于第九章及第十章中规定之。

    + +

    第十四条

    + +

      大会对于其所认为足以妨害国际间公共福利或友好关系之任何情势,不论其起原如何,包括由违反本宪章所载联合国之宗旨及原则而起之情势,得建议和平调整办法,但以不违背第十二条之规定为限。

    + +

    第十五条

    + +

      一、大会应收受并审查安全理事会所送之常年及特别报告;该项报告应载有安全理事会对于维持国际和平及安全所已决定或施行之办法之陈述。

    + +

      二、大会应收受并审查联合国其他机关所送之报告。

    + +

    第十六条

    + +

      大会应执行第十二章及第十三章所授予关于国际托管制度之职务,包括关于非战略防区托管协定之核准。

    + +

    第十七条

    + +

      一、大会应审核本组织之预算。

    + +

      二、本组织之经费应由各会员国依照大会分配限额担负之。

    + +

      三、大会应审核经与第五十七条所指各种专门机关订定之任何财政及预算办法,并应审查该项专门机关之行政预算,以便向关系机关提出建议。

    + +

    投票

    + +

    第十八条

    + +

      一、大会之每一会员国,应有一个投票权。

    + +

      二、大会对于重要问题之决议应以到会及投票之会员国三分之二多数决定之。此项问题应包括:关于维持国际和平及安全之建议,安全理事会非常任理事国之选举,经济及社会理事会理事国之选举,依第八十六条第一项(寅)款所规定托管理事会理事国之选举,对于新会员国加入联合国之准许,会员国权利及特权之停止,会员国之除名,关于施行托管制度之问题,以及预算问题。

    + +

      三、关于其他问题之决议,包括另有何种事项应以三分之二多数决定之问题,应以到会及投票之会员国过半数决定之。

    + +

    第十九条

    + +

      凡拖欠本组织财政款项之会员国,其拖欠数目如等于或超过前两年所应缴纳之数目时,即丧失其在大会投票权。大会如认拖欠原因,确由于该会员国无法控制之情形者,得准许该会员国投票。

    + +

    程序

    + +

    第二十条

    + +

      大会每年应举行常会,并于必要时,举行特别会议。特别会议应由秘书长经安全理事会或联合国会员国过半数之请求召集之。

    + +

    第二十一条

    + +

      大会应自行制定其议事规则。大会应选举每次会议之主席。

    + +

    第二十二条

    + +

      大会得设立其认为于行使职务所必需之辅助大会。

    + +

    第五章:安全理事会

    + +

    组织

    + +

    第二十三条

    + +

      一、安全理事会以联合国十五会员国组织之。中华民国、法兰西、苏维埃社会主义共和国联邦、大不列颠及北爱尔兰联合王国及美利坚合众国应为安全理事会常任理事国。大会应选举联合国其他十会员国为安全理事会非常任理事国,选举时首宜充分斟酌联合国各会员国于维持国际和平与安全及本组织其余各宗旨上之贡献,并宜充分斟酌地域上之公匀分配。

    + +

      二、安全理事会非常任理事国任期定为二年。安全理事会理事国自十一国增至十五国后第一次选举非常任理事国时,所增四国中两国之任期应为一年。任满之理事国不得即行连选。

    + +

      三、安全理事会每一理事国应有代表一人。

    + +

    职权

    + +

    第二十四条

    + +

      一、为保证联合国行动迅速有效起见,各会员国将维持国际和平及安全之主要责任,授予安全理事会,并同意安全理事会于履行此项责任下之职务时,即系代表各会员国。

    + +

      二、安全理事会于履行此项职务时,应遵照联合国之宗旨及原则。为履行此项职务而授予安全理事会之特定权力,于本宪章第六章、第七章、第八章及第十二章内规定之。

    + +

      三、安全理事会应将常年报告、并于必要时将特别报告,提送大会审查。

    + +

    第二十五条

    + +

      联合国会员国同意依宪章之规定接受并履行安全理事会之决议。

    + +

    第二十六条

    + +

      为促进国际和平及安全之建立及维持,以尽量减少世界人力及经济资源之消耗于军备起见,安全理事会借第四十七条所指之军事参谋团之协助,应负责拟具方案,提交联合国会员国,以建立军备管制制度。

    + +

    投票

    + +

    第二十七条

    + +

      一、安全理事会每一理事国应有一个投票权。

    + +

      二、安全理事会关于程序事项之决议,应以九理事国之可决票表决之。

    + +

      三、安全理事会对于其他一切事项之决议,应以九理事国之可决票包括全体常任理事国之同意票表决之;但对于第六章及第五十二条第三项内各事项之决议,争端当事国不得投票。

    + +

    程序

    + +

    第二十八条

    + +

      一、安全理事会之组织,应以使其能继续不断行使职务为要件。为此目的,安全理事会之各理事国应有常驻本组织会所之代表。

    + +

      二、安全理事会应举行定期会议,每一理事国认为合宜时得派政府大员或其他特别指定之代表出席。

    + +

      三、在本组织会所以外,安全理事会得在认为最能便利其工作之其他地点举行会议。

    + +

    第二十九条

    + +

      安全理事会得设立其认为于行使职务所必需之辅助机关。

    + +

    第三十条

    + +

      安全理事会应自行制定其议事规则,包括其推选主席之方法。

    + +

    第三十一条

    + +

      在安全理事会提出之任何问题,经其认为对于非安全理事会理事国之联合国任何会员国之利益有特别关系时,该会员国得参加讨论,但无投票权。

    + +

    第三十二条

    + +

      联合国会员国而非为安全理事会之理事国,或非联合国会员国之国家,如于安全理事会考虑中之争端为当事国者,应被邀参加关于该项争端之讨论,但无投票权。安全理事会应规定其所认为公平之条件,以便非联合国会员国之国家参加。

    + +

    第六章:争端之和平解决

    + +

     

    + +

    第三十三条

    + +

      一、任何争端之当事国,于争端之继续存在足以危及国际和平与安全之维持时,应尽先以谈判、调查、调停、和解、公断、司法解决、区域机关或区域办法之利用,或各该国自行选择之其他和平方法,求得解决。

    + +

      二、安全理事会认为必要时,应促请各当事国以此项方法,解决其争端。

    + +

    第三十四条

    + +

      安全理事会得调查任何争端或可能引起国际磨擦或惹起争端之任何情势,以断定该项争端或情势之继续存在是否足以危及国际和平与安全之维持。

    + +

    第三十五条

    + +

      一、联合国任何会员国得将属于第三十四条所指之性质之任何争端或情势,提请安全理事会或大会注意。

    + +

      二、非联合国会员国之国家如为任何争端之当事国时,经预先声明就该争端而言接受本宪章所规定和平解决之义务后,得将该项争端,提请大会或安全理事会注意。

    + +

      三、大会关于按照本条所提请注意事项之进行步骤,应遵守第十一条及第十二条之规定。

    + +

    第三十六条

    + +

      一、属于第三十三条所指之性质之争端或相似之情势,安全理事会在任何阶段,得建议适当程序或调整方法。

    + +

      二、安全理事会对于当事国为解决争端业经采取之任何程序,理应予以考虑。

    + +

      三、安全理事会按照本条作成建议时,同时理应注意凡具有法律性质之争端,在原则上,理应由当事国依国际法院规约之规定提交国际法院。

    + +

    第三十七条

    + +

      一、属于第三十三条所指之性质之争端,当事国如未能依该条所示方法解决时,应将该项争端提交安全理事会。

    + +

      二、安全理事会如认为该项争端之继续存在,在事实上足以危及国际和平与安全之维持时,应决定是否当依第三十六条采取行动或建议其所认为适当之解决条件。

    + +

    第三十八条

    + +

      安全理事会如经所有争端当事国之请求,得向各当事国作成建议,以求争端之和平解决,但以不妨碍第三十三条至第三十七条之规定为限。

    + +

    第七章:对于和平之威胁、和平之破坏及侵略行为之应付办法

    + +

    第三十九条

    + +

      安全理事会应断定任何和平之威胁、和平之破坏或侵略行为之是否存在,并应作成建议或抉择依第四十一条及第四十二条规定之办法,以维持或恢复国际和平及安全。

    + +

    第四十条

    + +

      为防止情势之恶化,安全理事会在依第三十九条规定作成建议或决定办法以前,得促请关系当事国遵行安全理事会所认为必要或合宜之临时办法。此项临时办法并不妨碍关系当事国之权利、要求或立场。安全理事会对于不遵行此项临时办法之情形,应予适当注意。

    + +

    第四十一条

    + +

      安全理事会得决定所应采武力以外之办法,以实施其决议,并得促请联合国会员国执行此项办法。此项办法得包括经济关系、铁路、海运、航空、邮、电、无线电及其他交通工具之局部或全部停止,以及外交关系之断绝。

    + +

    第四十二条

    + +

      安全理事会如认第四十一条所规定之办法为不足或已经证明为不足时,得采取必要之空海陆军行动,以维持或恢复国际和平及安全。此项行动得包括联合国会员国之空海陆军示威、封锁及其他军事举动。

    + +

    第四十三条

    + +

      一、联合国各会员国为求对于维持国际和平及安全有所贡献起见,担任于安全理事会发令时,并依特别协定,供给为维持国际和平及安全所必需之军队、协助及便利,包括过境权。

    + +

      二、此项特别协定应规定军队之数目及种类,其准备程度及一般驻扎地点,以及所供便利及协助之性质。

    + +

      三、此项特别协定应以安全理事会之主动,尽速议订。此项协定应由安全理事会与会员国或由安全理事会与若干会员国之集团缔结之,并由签字国各依其宪法程序批准之。

    + +

    第四十四条

    + +

      安全理事会决定使用武力时,于要求非安全理事会会员国依第四十三条供给军队以履行其义务之前,如经该会员国请求,应请其遣派代表,参加安全理事会关于使用其军事部队之决议。

    + +

    第四十五条

    + +

      为使联合国能采取紧急军事办法起见,会员国应将其本国空军部队为国际共同执行行动随时供给调遣。此项部队之实力与准备之程度,及其共同行动之计划,应由安全理事会以军事参谋团之协助,在第四十三条所指之特别协定范围内决定之。

    + +

    第四十六条

    + +

      武力使用之计划应由安全理事会以军事参谋团之协助决定之。

    + +

    第四十七条

    + +

      一、兹设立军事参谋团,以便对于安全理事会维持国际和平及安全之军事需要问题,对于受该会所支配军队之使用及统率问题,对于军备之管制及可能之军缩问题,向该会贡献意见并予以协助。

    + +

      二、军事参谋团应由安全理事会各常任理事国之参谋总长或其代表组织之。联合国任何会员国在该团未有常任代表者,如于该团责任之履行在效率上必需该国参加其工作时,应由该团邀请参加。

    + +

      三、军事参谋团在安全理事会权力之下,对于受该会所支配之任何军队,负战略上之指挥责任;关于该项军队之统率问题,应待以后处理。

    + +

      四、军事参谋团,经安全理事会之授权,并与区域内有关机关商议后、得设立区域分团。

    + +

    第四十八条

    + +

      一、执行安全理事会为维持国际和平及安全之决议所必要之行动,应由联合国全体会员国或由若干会员国担任之,一依安全理事会之决定。

    + +

      二、此项决议应由联合国会员国以其直接行动及经其加入为会员之有关国际机关之行动履行之。

    + +

    第四十九条

    + +

      联合国会员国应通力合作,彼此协助,以执行安全理事会所决定之办法。

    + +

    第五十条

    + +

      安全理事会对于任何国家采取防止或执行办法时,其他国家,不论其是否为联合国会员国,遇有因此项办法之执行而引起之特殊经济问题者,应有权与安全理事会会商解决此项问题。

    + +

    第五十一条

    + +

      联合国任何会员国受武力攻击时,在安全理事会采取必要办法,以维持国际和平及安全以前,本宪章不得认为禁止行使单独或集体自卫之自然权利。会员国因行使此项自卫权而采取之办法,应立向安全理事会报告,此项办法于任何方面不得影响该会按照本宪章随时采取其所认为必要行动之权责,以维持或恢复国际和平及安全。

    + +

    第八章:区域办法

    + +

    第五十二条

    + +

      一、本宪章不得认为排除区域办法或区域机关、用以应付关于维持国际和平及安全而宜于区域行动之事件者;但以此项办法或机关及其工作与联合国之宗旨及原则符合者为限。

    + +

      二、缔结此项办法或设立此项机关之联合国会员国,将地方争端提交安全理事会以前,应依该项区域办法,或由该项区域机关,力求和平解决。

    + +

      三、安全理事会对于依区域办法或由区域机关而求地方争端之和平解决,不论其系由关系国主动,或由安全理事会提交者,应鼓励其发展。

    + +

      四、本条绝不妨碍第三十四条及第三十五条之适用。

    + +

    第五十三条

    + +

      一、安全理事会对于职权内之执行行动,在适当情形下,应利用此项区域办法或区域机关。如无安全理事会之授权,不得依区域办法或由区域机关采取任何执行行动;但关于依第一百零七条之规定对付本条第二项所指之任何敌国之步骤,或在区域办法内所取防备此等国家再施其侵略政策之步骤,截至本组织经各关系政府之请求,对于此等国家之再次侵略,能担负防止责任时为止,不在此限。

    + +

      二、本条第一项所称敌国系指第二次世界大战中为本宪章任何签字国之敌国而言。

    + +

    第五十四条

    + +

      关于为维持国际和平及安全起见,依区域办法或由区域机关所已采取或正在考虑之行动,不论何时应向安全理事会充分报告之。

    + +

    第九章:国际经济及社会

    + +

    第五十五条

    + +

      为造成国际间以尊重人民平等权利及自决原则为根据之和平友好关系所必要之安定及福利条件起见,联合国应促进:

    + +

      (子) 较高之生活程度,全民就业,及经济与社会进展。

    + +

      (丑) 国际间经济、社会、卫生及有关问题之解决;国际间文化及教育合作。

    + +

      (寅) 全体人类之人权及基本自由之普遍尊重与遵守,不分种族、性别、语言或宗教。

    + +

    第五十六条

    + +

      各会员国担允采取共同及个别行动与本组织合作,以达成第五十五条所载之宗旨。

    + +

    第五十七条

    + +

      一、由各国政府间协定所成立之各种专门机关,依其组织约章之规定,于经济、社会、文化、教育、卫生及其他有关部门负有广大国际责任者,应依第六十三条之规定使与联合国发生关系。

    + +

      二、上述与联合国发生关系之各专门机关,以下简称专门机关。

    + +

    第五十八条

    + +

      本组织应作成建议,以调整各专门机关之政策及工作。

    + +

    第五十九条

    + +

      本组织应于适当情形下,发动各关系国间之谈判,以创设为达成第五十五条规定宗旨所必要之新专门机关。

    + +

    第六十条

    + +

      履行本章所载本组织职务之责任,属于大会及大会权力下之经济及社会理事会。为此目的,该理事会应有第十章所载之权力。

    + +

    第十章:经济及社会理事会

    + +

    组织

    + +

    第六十一条

    + +

      一、经济及社会理事会由大会选举联合国五十四会员国组织之。

    + +

      二、除第三项所规定外,经济及社会理事会每年选举理事十八国,任期三年。任满之理事国得即行连选。

    + +

      三、经济及社会理事会理事国自二十七国增至五十四国后第一次选举时,除选举理事九国接替任期在该年年终届满之理事国外,应另增选理事二十七国。增选之理事二十七国中,九国任期一年,另九国任期二年,一依大会所定办法。

    + +

      四、经济及社会理事会之每一理事国应有代表一人。

    + +

    职权

    + +

    第六十二条

    + +

      一、经济及社会理事会得作成或发动关于国际经济、社会、文化、教育、卫生及其他有关事项之研究及报告;并得向大会、联合国会员国及关系专门机关提出关于此种事项之建议案。

    + +

      二、本理事会为增进全体人类之人权及基本自由之尊重及维护起见,得作成建议案。

    + +

      三、本理事会得拟具关于其职权范围内事项之协约草案,提交大会。

    + +

      四、本理事会得依联合国所定之规则召集本理事会职务范围以内事项之国际会议。

    + +

    第六十三条

    + +

      一、经济及社会理事会得与第五十七条所指之任何专门机关订立协定,订明关系专门机关与联合国发生关系之条件。该项协定须经大会之核准。

    + +

      二、本理事会,为调整各种专门机关之工作,得与此种机关会商并得向其提出建议,并得向大会及联合国会员国建议。

    + +

    第六十四条

    + +

      一、经济及社会理事会得取适当步骤,以取得专门机关之经常报告。本理事会得与联合国会员国及专门机关,商定办法俾就实施本理事会之建议及大会对于本理事会职权范围内事项之建议所采之步骤,取得报告。

    + +

      二、本理事会得将对于此项报告之意见提送大会。

    + +

    第六十五条

    + +

      经济及社会理事会得向安全理事会供给情报,并因安全理事会之邀请,予以协助。

    + +

    第六十六条

    + +

      一、经济及社会理事会应履行其职权范围内关于执行大会建议之职务。

    + +

      二、经大会之许可,本理事会得应联合国会员国或专门机关之请求,供其服务。

    + +

      三、本理事会应履行本宪章他章所特定之其他职务,以及大会所授予之职务。

    + +

    投票

    + +

    第六十七条

    + +

      一、经济及社会理事会每一理事国应有一个投票权。

    + +

      二、本理事会之决议,应以到会及投票之理事国过半数表决之。

    + +

    程序

    + +

    第六十八条

    + +

      经济及社会理事会应设立经济与社会部门及以提倡人权为目的之各种委员会,并得设立于行使职务所必需之其他委员会。

    + +

    第六十九条

    + +

      经济及社会理事会应请联合国会员国参加讨论本理事会对于该国有特别关系之任何事件,但无投票权。

    + +

    第七十条

    + +

      经济及社会理事会得商定办法使专门机关之代表无投票权而参加本理事会及本理事会所设各委员会之讨论,或使本理事会之代表参加此项专门机关之讨论。

    + +

    第七十一条

    + +

      经济及社会理事会得采取适当办法,俾与各种非政府组织会商有关于本理事会职权范围内之事件。此项办法得与国际组织商定之,并于适当情形下,经与关系联合国会员国会商后,得与该国国内组织商定之。

    + +

    第七十二条

    + +

      一、经济及社会理事会应自行制定其议事规则,包括其推选主席之方法。

    + +

      二、经济及社会理事会应依其规则举行必要之会议。此项规则应包括因理事国过半数之请求而召集会议之条款。

    + +

    第十一章:关于非自治领土之宣言

    + +

    第七十三条

    + +

      联合国各会员国,于其所负有或承担管理责任之领土,其人民尚未臻自治之充分程度者,承认以领土居民之福利为至上之原则,并接受在本宪章所建立之国际和平及安全制度下,以充分增进领土居民福利之义务为神圣之信托,且为此目的:

    + +

      (子) 于充分尊重关系人民之文化下,保证其政治、经济、社会及教育之进展,予以公平待遇,且保障其不受虐待。

    + +

      (丑) 按各领土及其人民特殊之环境、及其进化之阶段,发展自治;对各该人民之政治愿望,予以适当之注意;并助其自由政治制度之逐渐发展。

    + +

      (寅) 促进国际和平及安全。

    + +

      (卯) 提倡建设计划,以求进步;奖励研究;各国彼此合作,并于适当之时间及场合与专门国际团体合作,以求本条所载社会、经济及科学目的之实现。

    + +

      (辰) 在不违背安全及宪法之限制下,按时将关于各会员国分别负责管理领土内之经济、社会及教育情形之统计及具有专门性质之情报,递送秘书长,以供参考。本宪章第十二章及第十三章所规定之领土,不在此限。

    + +

    第七十四条

    + +

      联合国各会员国公同承诺对于本章规定之领土,一如对于本国区域,其政策必须以善邻之道奉为圭臬;并于社会、经济及商业上,对世界各国之利益及幸福,予以充分之注意。

    + +

    第十二章:国际托管制度

    + +

    第七十五条

    + +

      联合国在其权力下,应设立国际托管制度,以管理并监督凭此后个别协定而置于该制度下之领土。此项领土以下简称托管领土。

    + +

    第七十六条

    + +

      按据本宪章第一条所载联合国之宗旨,托管制度之基本目的应为:

    + +

      (子) 促进国际和平及安全。

    + +

      (丑) 增进托管领土居民之政治、经济、社会及教育之进展;并以适合各领土及其人民之特殊情形及关系人民自由表示之愿望为原则,且按照各托管协定之条款,增进其趋向自治或独立之逐渐发展。

    + +

      (寅) 不分种族、性别、语言或宗教,提倡全体人类之人权及基本自由之尊重,并激发世界人民互相维系之意识。

    + +

      (卯) 于社会、经济及商业事件上,保证联合国全体会员国及其国民之平等待遇,及各该国民于司法裁判上之平等待遇,但以不妨碍上述目的之达成,且不违背第八十条之规定为限。

    + +

    第七十七条

    + +

      一、托管制度适用于依托管协定所置于该制度下之下列各种类之领土:

    + +

        (子) 现在委任统治下之领土。

    + +

        (丑) 因第二次世界大战结果或将自敌国割离之领土。

    + +

        (寅) 负管理责任之国家自愿置于该制度下之领土。

    + +

      二、关于上列种类中之何种领土将置于托管制度之下,及其条件,为此后协定所当规定之事项。

    + +

    第七十八条

    + +

      凡领土已成为联合国之会员国者,不适用托管制度;联合国会员国间之关系,应基于尊重主权平等之原则。

    + +

    第七十九条

    + +

      置于托管制度下之每一领土之托管条款,及其更改或修正,应由直接关系各国、包括联合国之会员国而为委任统治地之受托国者,予以议定,其核准应依第八十三条及第八十五条之规定。

    + +

    第八十条

    + +

      一、除依第七十七条、第七十九条及第八十一条所订置各领土于托管制度下之个别托管协定另有议定外,并在该项协定未经缔结以前,本章任何规定绝对不得解释为以任何方式变更任何国家或人民之权利、或联合国会员国个别签订之现有国际约章之条款。

    + +

      二、本条第一项不得解释为对于依第七十七条之规定而订置委任统治地或其他领土于托管制度下之协定,授以延展商订之理由。

    + +

    第八十一条

    + +

      凡托管协定均应载有管理领土之条款,并指定管理托管领土之当局。该项当局,以下简称管理当局,得为一个或数个国家,或为联合国本身。

    + +

    第八十二条

    + +

      于任何托管协定内,得指定一个或数个战略防区,包括该项协定下之托管领土之一部或全部,但该项协定并不妨碍依第四十三条而订立之任何特别协定。

    + +

    第八十三条

    + +

      一、联合国关于战略防区之各项职务,包括此项托管协定条款之核准、及其更改或修正,应由安全理事会行使之。

    + +

      二、第七十六条所规定之基本目的,适用于每一战略防区之人民。

    + +

      三、安全理事会以不违背托管协定之规定且不妨碍安全之考虑为限,应利用托管理事会之协助,以履行联合国托管制度下关于战略防区内之政治、经济、社会及教育事件之职务。

    + +

    第八十四条

    + +

      管理当局有保证托管领土对于维持国际和平及安全尽其本分之义务。该当局为此目的得利用托管领土之志愿军、便利及协助,以履行该当局对于安全理事会所负关于此点之义务,并以实行地方自卫,且在托管领土内维持法律与秩序。

    + +

    第八十五条

    + +

      一、联合国关于一切非战略防区托管协定之职务,包括此项托管协定条款之核准及其更改或修正,应由大会行使之。

    + +

      二、托管理事会于大会权力下,应协助大会履行上述之职务。

    + +

    第十三章:托管理事会

    + +

    组织

    + +

    第八十六条

    + +

      一、托管理事会应由下列联合国会员国组织之:

    + +

        (子) 管理托管领土之会员国。

    + +

        (丑) 第二十三条所列名之国家而现非管理托管领土者。

    + +

        (寅) 大会选举必要数额之其他会员国,任期三年,俾使托管理事会理事国之总数,于联合国会员国中之管理托管领土者及不管理者之间,得以平均分配。

    + +

      二、托管理事会之每一理事国应指定一特别合格之人员,以代表之。

    + +

    职权

    + +

    第八十七条

    + +

      大会及在其权力下之托管理事会于履行职务时得:

    + +

      (子) 审查管理当局所送之报告。

    + +

      (丑) 会同管理当局接受并审查请愿书。

    + +

      (寅) 与管理当局商定时间,按期视察各托管领土。

    + +

      (卯) 依托管协定之条款,采取上述其他行动。

    + +

    第八十八条

    + +

      托管理事会应拟定关于各托管领土居民之政治、经济、社会及教育进展之问题单;就大会职权范围内,各托管领土之管理当局应根据该项问题单向大会提出常年报告。

    + +

    投票

    + +

    第八十九条

    + +

      一、托管理事会之每一理事国应有一个投票权。

    + +

      二、托管理事会之决议应以到会及投票之理事国过半数表决之。

    + +

    程序

    + +

    第九十条

    + +

      一、托管理事会应自行制定其议事规则,包括其推选主席之方法。

    + +

      二、托管理事会应依其所定规则,举行必要之会议。此项规则应包括关于经该会理事国过半数之请求而召集会议之规定。

    + +

    第九十一条

    + +

      托管理事会于适当时,应利用经济及社会理事会之协助,并对于各关系事项,利用专门机关之协助。

    + +

    第十四章:国际法院

    + +

    第九十二条

    + +

      国际法院为联合国之主要司法机关,应依所附规约执行其职务。该项规约系以国际常设法院之规约为根据并为本宪章之构成部分。

    + +

    第九十三条

    + +

      一、联合国各会员国为国际法院规约之当然当事国

    + +

      二、非联合国会员国之国家得为国际法院规约当事国之条件,应由大会经安全理事会之建议就各别情形决定之。

    + +

    第九十四条

    + +

      一、联合国每一会员国为任何案件之当事国者,承诺遵行国际法院之判决。

    + +

      二、遇有一造不履行依法院判决应负之义务时,他造得向于安全理事会申诉。安全理事会如认为必要时,得作成建议或决定应采办法,以执行判决。

    + +

    第九十五条

    + +

      本宪章不得认为禁止联合国会员国依据现有或以后缔结之协定,将其争端托付其他法院解决。

    + +

    第九十六条

    + +

      一、大会或安全理事会对于任何法律问题得请国际法院发表咨询意见。

    + +

      二、联合国其他机关及各种专门机关,对于其工作范围内之任何法律问题,得随时以大会之授权,请求国际法院发表咨询意见。

    + +

    第十五章:秘书处

    + +

    第九十七条

    + +

      秘书处置秘书长一人及本组织所需之办事人员若干人。秘书长应由大会经安全理事会之推荐委派之。秘书长为本组织之行政首长。

    + +

    第九十八条

    + +

      秘书长在大会、安全理事会、经济及社会理事会、及托管理事会之一切会议,应以秘书长资格行使职务,并应执行各该机关所托付之其他职务。秘书长应向大会提送关于本组织工作之常年报告。

    + +

    第九十九条

    + +

      秘书长得将其所认为可能威胁国际和平及安全之任何事件,提请安全理事会注意。

    + +

    第一百条

    + +

      一、秘书长及办事人员于执行职务时,不得请求或接受本组织以外任何政府或其他当局之训示,并应避免足以妨碍其国际官员地位之行动。秘书长及办事人员专对本组织负责。

    + +

      二、联合国各会员国承诺尊重秘书长及办事人员责任之专属国际性,决不设法影响其责任之履行。

    + +

    第一百零一条

    + +

      一、办事人员由秘书长依大会所定章程委派之。

    + +

      二、适当之办事人员应长期分配于经济及社会理事会、托管理事会,并于必要时,分配于联合国其他之机关。此项办事人员构成秘书处之一部。

    + +

      三、办事人员之雇用及其服务条件之决定,应以求达效率、才干及忠诚之最高标准为首要考虑。征聘办事人员时,于可能范围内,应充分注意地域上之普及。

    + +

    第十六章:杂项条款

    + +

    第一百零二条

    + +

      一、本宪章发生效力后,联合国任何会员国所缔结之一切条约及国际协定应尽速在秘书处登记,并由秘书处公布之。

    + +

      二、当事国对于未经依本条第一项规定登记之条约或国际协定,不得向联合国任何机关援引之。

    + +

    第一百零三条

    + +

      联合国会员国在本宪章下之义务与其依任何其他国际协定所负之义务有冲突时,其在本宪章下之义务应居优先。

    + +

    第一百零四条

    + +

      本组织于每一会员国之领土内,应享受于执行其职务及达成其宗旨所必需之法律行为能力。

    + +

    第一百零五条

    + +

      一、本组织于每一会员国之领土内,应享受于达成其宗旨所必需之特权及豁免。

    + +

      二、联合国会员国之代表及本组织之职员,亦应同样享受于其独立行使关于本组织之职务所必需之特权及豁免。

    + +

      三、为明定本条第一项及第二项之施行细则起见,大会得作成建议,或为此目的向联合国会员国提议协约。

    + +

    第十七章:过渡安全办法

    + +

    第一百零六条

    + +

      在第四十三条所称之特别协定尚未生效,因而安全理事会认为尚不得开始履行第四十二条所规定之责任前,一九四三年十月三十日在莫斯科签订四国宣言之当事国及法兰西应依该宣言第五项之规定,互相洽商,并于必要时,与联合国其他会员国洽商,以代表本组织采取为维持国际和平及安全宗旨所必要之联合行动。

    + +

    第一百零七条

    + +

      本宪章并不取消或禁止负行动责任之政府对于在第二次世界大战中本宪章任何签字国之敌国因该次战争而采取或受权执行之行动。

    + +

    第十八章:修正

    + +

    第一百零八条

    + +

      本宪章之修正案经大会会员国三分之二表决并由联合国会员国三分之二、包括安全理事会全体常任理事国,各依其宪法程序批准后,对于联合国所有会员国发生效力。

    + +

    第一百零九条

    + +

      一、联合国会员国,为检讨本宪章,得以大会会员国三分之二表决,经安全理事会任何九理事国之表决,确定日期及地点举行全体会议。联合国每一会员国在全体会议中应有一个投票权。

    + +

      二、全体会议以三分之二表决所建议对于宪章之任何更改,应经联合国会员国三分之二、包括安全理事会全体常任理事国,各依其宪法程序批准后,发生效力。

    + +

      三、如于本宪章生效后大会第十届年会前,此项全体会议尚未举行时,应将召集全体会议之提议列入大会该届年会之议事日程;如得大会会员国过半数及安全理事会任何七理事国之表决,此项会议应即举行。

    + +

    第十九章:批准及签字

    + +

    第一百一十条

    + +

      一、本宪章应由签字国各依其宪法程序批准之。

    + +

      二、批准书应交存美利坚合众国政府。该国政府应于每一批准书交存时通知各签字国,如本组织秘书长业经委派时,并应通知秘书长。

    + +

      三、一俟美利坚合众国政府通知已有中华民国、法兰西、苏维埃社会主义共和国联邦、大不列颠及北爱尔兰联合王国、与美利坚合众国、以及其他签字国之过半数将批准书交存时,本宪章即发生效力。美利坚合众国政府应拟就此项交存批准之议定书并将副本分送所有签字国。

    + +

      四、本宪章签字国于宪章发生效力后批准者,应自其各将批准书交存之日起为联合国之创始会员国。

    + +

    第一百一十一条

    + +

      本宪章应留存美利坚合众国政府之档库,其中、法、俄、英、及西文各本同一作准。该国政府应将正式副本分送其他签字国政府。

    + +

      为此联合国各会员国政府之代表谨签字于本宪章,以昭信守。

    + +

      公历一千九百四十五年六月二十六日签订于金山市。

    +
    +
    +
    + + + +
    + +
    +
    + +
    +
    +
    + + + + + +
    + + + + + + + + + + + +
    + + + + + + + + + +
    \ No newline at end of file diff --git a/stdlib/benchmarks/collections/data/UN_charter_zh-CN.txt b/stdlib/benchmarks/collections/data/UN_charter_zh-CN.txt new file mode 100644 index 0000000000..6be192a83c --- /dev/null +++ b/stdlib/benchmarks/collections/data/UN_charter_zh-CN.txt @@ -0,0 +1,609 @@ +联合国宪章(全文) +序言 +我联合国人民同兹决心 +  欲免后世再遭今代人类两度身历惨不堪言之战祸, + +  重申基本人权,人格尊严与价值,以及男女与大小各国平等权利之信念, + +  创造适当环境,俾克维持正义,尊重由条约与国际法其他渊源而起之义务,久而弗懈, + +  促成大自由中之社会进步及较善之民生, + +并为达此目的 +  力行容恕,彼此以善邻之道,和睦相处, + +  集中力量,以维持国际和平及安全, + +  接受原则,确立方法,以保证非为公共利益,不得使用武力, + +  运用国际机构,以促成全球人民经济及社会之进展, + +用是发愤立志,务当同心协力,以竟厥功 +  爰由我各本国政府,经齐集金山市之代表各将所奉全权证书,互相校阅,均属妥善,议定本联合国宪章,并设立国际组织,定名联合国。 + +第一章:宗旨及原则 +第一条 +  联合国之宗旨为: + +  一、维持国际和平及安全;并为此目的:采取有效集体办法,以防止且消除对于和平之威胁,制止侵略行为或其他和平之破坏;并以和平方法且依正义及国际法之原则,调整或解决足以破坏和平之国际争端或情势。 + +  二、发展国际间以尊重人民平等权利及自决原则为根据之友好关系,并采取其他适当办法,以增强普遍和平。 + +  三、促成国际合作,以解决国际间属于经济、社会、文化及人类福利性质之国际问题,且不分种族、性别、语言或宗教,增进并激励对于全体人类之人权及基本自由之尊重。 + +  四、构成一协调各国行动之中心,以达成上述共同目的。 + +第二条 +  为求实现第一条所述各宗旨起见,本组织及其会员国应遵行下列原则: + +  一、本组织系基于各会员国主权平等之原则。 + +  二、各会员国应一秉善意,履行其依本宪章所担负之义务,以保证全体会员国由加入本组织而发生之权益。 + +  三、各会员国应以和平方法解决其国际争端,俾免危及国际和平、安全及正义。 + +  四、各会员国在其国际关系上不得使用威胁或武力,或以与联合国宗旨不符之任何其他方法,侵害任何会员国或国家之领土完整或政治独立。 + +  五、各会员国对于联合国依本宪章规定而采取之行动,应尽力予以协助,联合国对于任何国家正在采取防止或执行行动时,各会员国对该国不得给予协助。 + +  六、本组织在维持国际和平及安全之必要范围内,应保证非联合国会员国遵行上述原则。 + +  七、本宪章不得认为授权联合国干涉在本质上属于任何国家国内管辖之事件,且并不要求会员国将该项事件依本宪章提请解决;但此项原则不妨碍第七章内执行办法之适用。 + +第二章:会员 +第三条 +  凡曾经参加金山联合国国际组织会议或前此曾签字于一九四二年一月一日联合国宣言之国家,签订本宪章,且依宪章第一百一十条规定而予以批准者,均为联合国之创始会员国。 + +第四条 +  一、凡其他爱好和平之国家,接受本宪章所载之义务,经本组织认为确能并愿意履行该项义务者,得为联合国会员国。 + +  二、准许上述国家为联合国会员国,将由大会经安全理事会之推荐以决议行之。 + +第五条 +  联合国会员国,业经安全理事会对其采取防止或执行行动者,大会经安全理事会之建议,得停止其会员权利及特权之行使。此项权利及特权之行使,得由安全理事会恢复之。 + +第六条 +  联合国之会员国中,有屡次违犯本宪章所载之原则者,大会经安全理事会之建议,得将其由本组织除名。 + +第三章:机关 +第七条 +  一、兹设联合国之主要机关如下:大会、安全理事会、经济及社会理事会、托管理事会、国际法院、及秘书处。 + +  二、联合国得依本宪章设立认为必需之辅助机关。 + +第八条 +  联合国对于男女均得在其主要及辅助机关在平等条件之下,充任任何职务,不得加以限制。 + +第四章:大会 +组织 +第九条 +  一、大会由联合国所有会员国组织之。 + +  二、每一会员国在大会之代表,不得超过五人。 + +职权 +第十条 +  大会得讨论本宪章范围内之任何问题或事项,或关于本宪章所规定任何机关之职权;并除第十二条所规定外,得向联合国会员国或安全理事会或兼向两者,提出对各该问题或事项之建议。 + +第十一条 +  一、大会得考虑关于维持国际和平及安全之合作之普通原则,包括军缩及军备管制之原则;并得向会员国或安全理事会或兼向两者提出对于该项原则之建议。 + +  二、大会得讨论联合国任何会员国或安全理事会或非联合国会员国依第三十五条第二项之规定向大会所提关于维持国际和平及安全之任何问题;除第十二条所规定外,并得向会员国或安全理事会或兼向两者提出对于各该项问题之建议。凡对于需要行动之各该项问题,应由大会于讨论前或讨论后提交安全理事会。 + +  三、大会对于足以危及国际和平与安全之情势,得提请安全理事会注意。 + +  四、本条所载之大会权力并不限制第十条之概括范围。 + +第十二条 +  一、当安全理事会对于任何争端或情势,正在执行本宪章所授予该会之职务时,大会非经安全理事会请求,对于该项争端或情势,不得提出任何建议。 + +  二、秘书长经安全理事会之同意,应于大会每次会议时,将安全理事会正在处理中关于维持国际和平及安全之任何事件,通知大会;于安全理事会停止处理该项事件时,亦应立即通知大会,或在大会闭会期内通知联合国会员国。 + +第十三条 +  一、大会应发动研究,并作成建议: + +    (子) 以促进政治上之国际合作,并提倡国际法之逐渐发展与编纂。 + +    (丑) 以促进经济、社会、文化、教育及卫生各部门之国际合作,且不分种族、性别、语言或宗教,助成全体人类之人权及基本自由之实现。 + +  二、大会关于本条第一项(丑)款所列事项之其他责任及职权,于第九章及第十章中规定之。 + +第十四条 +  大会对于其所认为足以妨害国际间公共福利或友好关系之任何情势,不论其起原如何,包括由违反本宪章所载联合国之宗旨及原则而起之情势,得建议和平调整办法,但以不违背第十二条之规定为限。 + +第十五条 +  一、大会应收受并审查安全理事会所送之常年及特别报告;该项报告应载有安全理事会对于维持国际和平及安全所已决定或施行之办法之陈述。 + +  二、大会应收受并审查联合国其他机关所送之报告。 + +第十六条 +  大会应执行第十二章及第十三章所授予关于国际托管制度之职务,包括关于非战略防区托管协定之核准。 + +第十七条 +  一、大会应审核本组织之预算。 + +  二、本组织之经费应由各会员国依照大会分配限额担负之。 + +  三、大会应审核经与第五十七条所指各种专门机关订定之任何财政及预算办法,并应审查该项专门机关之行政预算,以便向关系机关提出建议。 + +投票 +第十八条 +  一、大会之每一会员国,应有一个投票权。 + +  二、大会对于重要问题之决议应以到会及投票之会员国三分之二多数决定之。此项问题应包括:关于维持国际和平及安全之建议,安全理事会非常任理事国之选举,经济及社会理事会理事国之选举,依第八十六条第一项(寅)款所规定托管理事会理事国之选举,对于新会员国加入联合国之准许,会员国权利及特权之停止,会员国之除名,关于施行托管制度之问题,以及预算问题。 + +  三、关于其他问题之决议,包括另有何种事项应以三分之二多数决定之问题,应以到会及投票之会员国过半数决定之。 + +第十九条 +  凡拖欠本组织财政款项之会员国,其拖欠数目如等于或超过前两年所应缴纳之数目时,即丧失其在大会投票权。大会如认拖欠原因,确由于该会员国无法控制之情形者,得准许该会员国投票。 + +程序 +第二十条 +  大会每年应举行常会,并于必要时,举行特别会议。特别会议应由秘书长经安全理事会或联合国会员国过半数之请求召集之。 + +第二十一条 +  大会应自行制定其议事规则。大会应选举每次会议之主席。 + +第二十二条 +  大会得设立其认为于行使职务所必需之辅助大会。 + +第五章:安全理事会 +组织 +第二十三条 +  一、安全理事会以联合国十五会员国组织之。中华民国、法兰西、苏维埃社会主义共和国联邦、大不列颠及北爱尔兰联合王国及美利坚合众国应为安全理事会常任理事国。大会应选举联合国其他十会员国为安全理事会非常任理事国,选举时首宜充分斟酌联合国各会员国于维持国际和平与安全及本组织其余各宗旨上之贡献,并宜充分斟酌地域上之公匀分配。 + +  二、安全理事会非常任理事国任期定为二年。安全理事会理事国自十一国增至十五国后第一次选举非常任理事国时,所增四国中两国之任期应为一年。任满之理事国不得即行连选。 + +  三、安全理事会每一理事国应有代表一人。 + +职权 +第二十四条 +  一、为保证联合国行动迅速有效起见,各会员国将维持国际和平及安全之主要责任,授予安全理事会,并同意安全理事会于履行此项责任下之职务时,即系代表各会员国。 + +  二、安全理事会于履行此项职务时,应遵照联合国之宗旨及原则。为履行此项职务而授予安全理事会之特定权力,于本宪章第六章、第七章、第八章及第十二章内规定之。 + +  三、安全理事会应将常年报告、并于必要时将特别报告,提送大会审查。 + +第二十五条 +  联合国会员国同意依宪章之规定接受并履行安全理事会之决议。 + +第二十六条 +  为促进国际和平及安全之建立及维持,以尽量减少世界人力及经济资源之消耗于军备起见,安全理事会借第四十七条所指之军事参谋团之协助,应负责拟具方案,提交联合国会员国,以建立军备管制制度。 + +投票 +第二十七条 +  一、安全理事会每一理事国应有一个投票权。 + +  二、安全理事会关于程序事项之决议,应以九理事国之可决票表决之。 + +  三、安全理事会对于其他一切事项之决议,应以九理事国之可决票包括全体常任理事国之同意票表决之;但对于第六章及第五十二条第三项内各事项之决议,争端当事国不得投票。 + +程序 +第二十八条 +  一、安全理事会之组织,应以使其能继续不断行使职务为要件。为此目的,安全理事会之各理事国应有常驻本组织会所之代表。 + +  二、安全理事会应举行定期会议,每一理事国认为合宜时得派政府大员或其他特别指定之代表出席。 + +  三、在本组织会所以外,安全理事会得在认为最能便利其工作之其他地点举行会议。 + +第二十九条 +  安全理事会得设立其认为于行使职务所必需之辅助机关。 + +第三十条 +  安全理事会应自行制定其议事规则,包括其推选主席之方法。 + +第三十一条 +  在安全理事会提出之任何问题,经其认为对于非安全理事会理事国之联合国任何会员国之利益有特别关系时,该会员国得参加讨论,但无投票权。 + +第三十二条 +  联合国会员国而非为安全理事会之理事国,或非联合国会员国之国家,如于安全理事会考虑中之争端为当事国者,应被邀参加关于该项争端之讨论,但无投票权。安全理事会应规定其所认为公平之条件,以便非联合国会员国之国家参加。 + +第六章:争端之和平解决 + +第三十三条 +  一、任何争端之当事国,于争端之继续存在足以危及国际和平与安全之维持时,应尽先以谈判、调查、调停、和解、公断、司法解决、区域机关或区域办法之利用,或各该国自行选择之其他和平方法,求得解决。 + +  二、安全理事会认为必要时,应促请各当事国以此项方法,解决其争端。 + +第三十四条 +  安全理事会得调查任何争端或可能引起国际磨擦或惹起争端之任何情势,以断定该项争端或情势之继续存在是否足以危及国际和平与安全之维持。 + +第三十五条 +  一、联合国任何会员国得将属于第三十四条所指之性质之任何争端或情势,提请安全理事会或大会注意。 + +  二、非联合国会员国之国家如为任何争端之当事国时,经预先声明就该争端而言接受本宪章所规定和平解决之义务后,得将该项争端,提请大会或安全理事会注意。 + +  三、大会关于按照本条所提请注意事项之进行步骤,应遵守第十一条及第十二条之规定。 + +第三十六条 +  一、属于第三十三条所指之性质之争端或相似之情势,安全理事会在任何阶段,得建议适当程序或调整方法。 + +  二、安全理事会对于当事国为解决争端业经采取之任何程序,理应予以考虑。 + +  三、安全理事会按照本条作成建议时,同时理应注意凡具有法律性质之争端,在原则上,理应由当事国依国际法院规约之规定提交国际法院。 + +第三十七条 +  一、属于第三十三条所指之性质之争端,当事国如未能依该条所示方法解决时,应将该项争端提交安全理事会。 + +  二、安全理事会如认为该项争端之继续存在,在事实上足以危及国际和平与安全之维持时,应决定是否当依第三十六条采取行动或建议其所认为适当之解决条件。 + +第三十八条 +  安全理事会如经所有争端当事国之请求,得向各当事国作成建议,以求争端之和平解决,但以不妨碍第三十三条至第三十七条之规定为限。 + +第七章:对于和平之威胁、和平之破坏及侵略行为之应付办法 +第三十九条 +  安全理事会应断定任何和平之威胁、和平之破坏或侵略行为之是否存在,并应作成建议或抉择依第四十一条及第四十二条规定之办法,以维持或恢复国际和平及安全。 + +第四十条 +  为防止情势之恶化,安全理事会在依第三十九条规定作成建议或决定办法以前,得促请关系当事国遵行安全理事会所认为必要或合宜之临时办法。此项临时办法并不妨碍关系当事国之权利、要求或立场。安全理事会对于不遵行此项临时办法之情形,应予适当注意。 + +第四十一条 +  安全理事会得决定所应采武力以外之办法,以实施其决议,并得促请联合国会员国执行此项办法。此项办法得包括经济关系、铁路、海运、航空、邮、电、无线电及其他交通工具之局部或全部停止,以及外交关系之断绝。 + +第四十二条 +  安全理事会如认第四十一条所规定之办法为不足或已经证明为不足时,得采取必要之空海陆军行动,以维持或恢复国际和平及安全。此项行动得包括联合国会员国之空海陆军示威、封锁及其他军事举动。 + +第四十三条 +  一、联合国各会员国为求对于维持国际和平及安全有所贡献起见,担任于安全理事会发令时,并依特别协定,供给为维持国际和平及安全所必需之军队、协助及便利,包括过境权。 + +  二、此项特别协定应规定军队之数目及种类,其准备程度及一般驻扎地点,以及所供便利及协助之性质。 + +  三、此项特别协定应以安全理事会之主动,尽速议订。此项协定应由安全理事会与会员国或由安全理事会与若干会员国之集团缔结之,并由签字国各依其宪法程序批准之。 + +第四十四条 +  安全理事会决定使用武力时,于要求非安全理事会会员国依第四十三条供给军队以履行其义务之前,如经该会员国请求,应请其遣派代表,参加安全理事会关于使用其军事部队之决议。 + +第四十五条 +  为使联合国能采取紧急军事办法起见,会员国应将其本国空军部队为国际共同执行行动随时供给调遣。此项部队之实力与准备之程度,及其共同行动之计划,应由安全理事会以军事参谋团之协助,在第四十三条所指之特别协定范围内决定之。 + +第四十六条 +  武力使用之计划应由安全理事会以军事参谋团之协助决定之。 + +第四十七条 +  一、兹设立军事参谋团,以便对于安全理事会维持国际和平及安全之军事需要问题,对于受该会所支配军队之使用及统率问题,对于军备之管制及可能之军缩问题,向该会贡献意见并予以协助。 + +  二、军事参谋团应由安全理事会各常任理事国之参谋总长或其代表组织之。联合国任何会员国在该团未有常任代表者,如于该团责任之履行在效率上必需该国参加其工作时,应由该团邀请参加。 + +  三、军事参谋团在安全理事会权力之下,对于受该会所支配之任何军队,负战略上之指挥责任;关于该项军队之统率问题,应待以后处理。 + +  四、军事参谋团,经安全理事会之授权,并与区域内有关机关商议后、得设立区域分团。 + +第四十八条 +  一、执行安全理事会为维持国际和平及安全之决议所必要之行动,应由联合国全体会员国或由若干会员国担任之,一依安全理事会之决定。 + +  二、此项决议应由联合国会员国以其直接行动及经其加入为会员之有关国际机关之行动履行之。 + +第四十九条 +  联合国会员国应通力合作,彼此协助,以执行安全理事会所决定之办法。 + +第五十条 +  安全理事会对于任何国家采取防止或执行办法时,其他国家,不论其是否为联合国会员国,遇有因此项办法之执行而引起之特殊经济问题者,应有权与安全理事会会商解决此项问题。 + +第五十一条 +  联合国任何会员国受武力攻击时,在安全理事会采取必要办法,以维持国际和平及安全以前,本宪章不得认为禁止行使单独或集体自卫之自然权利。会员国因行使此项自卫权而采取之办法,应立向安全理事会报告,此项办法于任何方面不得影响该会按照本宪章随时采取其所认为必要行动之权责,以维持或恢复国际和平及安全。 + +第八章:区域办法 +第五十二条 +  一、本宪章不得认为排除区域办法或区域机关、用以应付关于维持国际和平及安全而宜于区域行动之事件者;但以此项办法或机关及其工作与联合国之宗旨及原则符合者为限。 + +  二、缔结此项办法或设立此项机关之联合国会员国,将地方争端提交安全理事会以前,应依该项区域办法,或由该项区域机关,力求和平解决。 + +  三、安全理事会对于依区域办法或由区域机关而求地方争端之和平解决,不论其系由关系国主动,或由安全理事会提交者,应鼓励其发展。 + +  四、本条绝不妨碍第三十四条及第三十五条之适用。 + +第五十三条 +  一、安全理事会对于职权内之执行行动,在适当情形下,应利用此项区域办法或区域机关。如无安全理事会之授权,不得依区域办法或由区域机关采取任何执行行动;但关于依第一百零七条之规定对付本条第二项所指之任何敌国之步骤,或在区域办法内所取防备此等国家再施其侵略政策之步骤,截至本组织经各关系政府之请求,对于此等国家之再次侵略,能担负防止责任时为止,不在此限。 + +  二、本条第一项所称敌国系指第二次世界大战中为本宪章任何签字国之敌国而言。 + +第五十四条 +  关于为维持国际和平及安全起见,依区域办法或由区域机关所已采取或正在考虑之行动,不论何时应向安全理事会充分报告之。 + +第九章:国际经济及社会 +第五十五条 +  为造成国际间以尊重人民平等权利及自决原则为根据之和平友好关系所必要之安定及福利条件起见,联合国应促进: + +  (子) 较高之生活程度,全民就业,及经济与社会进展。 + +  (丑) 国际间经济、社会、卫生及有关问题之解决;国际间文化及教育合作。 + +  (寅) 全体人类之人权及基本自由之普遍尊重与遵守,不分种族、性别、语言或宗教。 + +第五十六条 +  各会员国担允采取共同及个别行动与本组织合作,以达成第五十五条所载之宗旨。 + +第五十七条 +  一、由各国政府间协定所成立之各种专门机关,依其组织约章之规定,于经济、社会、文化、教育、卫生及其他有关部门负有广大国际责任者,应依第六十三条之规定使与联合国发生关系。 + +  二、上述与联合国发生关系之各专门机关,以下简称专门机关。 + +第五十八条 +  本组织应作成建议,以调整各专门机关之政策及工作。 + +第五十九条 +  本组织应于适当情形下,发动各关系国间之谈判,以创设为达成第五十五条规定宗旨所必要之新专门机关。 + +第六十条 +  履行本章所载本组织职务之责任,属于大会及大会权力下之经济及社会理事会。为此目的,该理事会应有第十章所载之权力。 + +第十章:经济及社会理事会 +组织 +第六十一条 +  一、经济及社会理事会由大会选举联合国五十四会员国组织之。 + +  二、除第三项所规定外,经济及社会理事会每年选举理事十八国,任期三年。任满之理事国得即行连选。 + +  三、经济及社会理事会理事国自二十七国增至五十四国后第一次选举时,除选举理事九国接替任期在该年年终届满之理事国外,应另增选理事二十七国。增选之理事二十七国中,九国任期一年,另九国任期二年,一依大会所定办法。 + +  四、经济及社会理事会之每一理事国应有代表一人。 + +职权 +第六十二条 +  一、经济及社会理事会得作成或发动关于国际经济、社会、文化、教育、卫生及其他有关事项之研究及报告;并得向大会、联合国会员国及关系专门机关提出关于此种事项之建议案。 + +  二、本理事会为增进全体人类之人权及基本自由之尊重及维护起见,得作成建议案。 + +  三、本理事会得拟具关于其职权范围内事项之协约草案,提交大会。 + +  四、本理事会得依联合国所定之规则召集本理事会职务范围以内事项之国际会议。 + +第六十三条 +  一、经济及社会理事会得与第五十七条所指之任何专门机关订立协定,订明关系专门机关与联合国发生关系之条件。该项协定须经大会之核准。 + +  二、本理事会,为调整各种专门机关之工作,得与此种机关会商并得向其提出建议,并得向大会及联合国会员国建议。 + +第六十四条 +  一、经济及社会理事会得取适当步骤,以取得专门机关之经常报告。本理事会得与联合国会员国及专门机关,商定办法俾就实施本理事会之建议及大会对于本理事会职权范围内事项之建议所采之步骤,取得报告。 + +  二、本理事会得将对于此项报告之意见提送大会。 + +第六十五条 +  经济及社会理事会得向安全理事会供给情报,并因安全理事会之邀请,予以协助。 + +第六十六条 +  一、经济及社会理事会应履行其职权范围内关于执行大会建议之职务。 + +  二、经大会之许可,本理事会得应联合国会员国或专门机关之请求,供其服务。 + +  三、本理事会应履行本宪章他章所特定之其他职务,以及大会所授予之职务。 + +投票 +第六十七条 +  一、经济及社会理事会每一理事国应有一个投票权。 + +  二、本理事会之决议,应以到会及投票之理事国过半数表决之。 + +程序 +第六十八条 +  经济及社会理事会应设立经济与社会部门及以提倡人权为目的之各种委员会,并得设立于行使职务所必需之其他委员会。 + +第六十九条 +  经济及社会理事会应请联合国会员国参加讨论本理事会对于该国有特别关系之任何事件,但无投票权。 + +第七十条 +  经济及社会理事会得商定办法使专门机关之代表无投票权而参加本理事会及本理事会所设各委员会之讨论,或使本理事会之代表参加此项专门机关之讨论。 + +第七十一条 +  经济及社会理事会得采取适当办法,俾与各种非政府组织会商有关于本理事会职权范围内之事件。此项办法得与国际组织商定之,并于适当情形下,经与关系联合国会员国会商后,得与该国国内组织商定之。 + +第七十二条 +  一、经济及社会理事会应自行制定其议事规则,包括其推选主席之方法。 + +  二、经济及社会理事会应依其规则举行必要之会议。此项规则应包括因理事国过半数之请求而召集会议之条款。 + +第十一章:关于非自治领土之宣言 +第七十三条 +  联合国各会员国,于其所负有或承担管理责任之领土,其人民尚未臻自治之充分程度者,承认以领土居民之福利为至上之原则,并接受在本宪章所建立之国际和平及安全制度下,以充分增进领土居民福利之义务为神圣之信托,且为此目的: + +  (子) 于充分尊重关系人民之文化下,保证其政治、经济、社会及教育之进展,予以公平待遇,且保障其不受虐待。 + +  (丑) 按各领土及其人民特殊之环境、及其进化之阶段,发展自治;对各该人民之政治愿望,予以适当之注意;并助其自由政治制度之逐渐发展。 + +  (寅) 促进国际和平及安全。 + +  (卯) 提倡建设计划,以求进步;奖励研究;各国彼此合作,并于适当之时间及场合与专门国际团体合作,以求本条所载社会、经济及科学目的之实现。 + +  (辰) 在不违背安全及宪法之限制下,按时将关于各会员国分别负责管理领土内之经济、社会及教育情形之统计及具有专门性质之情报,递送秘书长,以供参考。本宪章第十二章及第十三章所规定之领土,不在此限。 + +第七十四条 +  联合国各会员国公同承诺对于本章规定之领土,一如对于本国区域,其政策必须以善邻之道奉为圭臬;并于社会、经济及商业上,对世界各国之利益及幸福,予以充分之注意。 + +第十二章:国际托管制度 +第七十五条 +  联合国在其权力下,应设立国际托管制度,以管理并监督凭此后个别协定而置于该制度下之领土。此项领土以下简称托管领土。 + +第七十六条 +  按据本宪章第一条所载联合国之宗旨,托管制度之基本目的应为: + +  (子) 促进国际和平及安全。 + +  (丑) 增进托管领土居民之政治、经济、社会及教育之进展;并以适合各领土及其人民之特殊情形及关系人民自由表示之愿望为原则,且按照各托管协定之条款,增进其趋向自治或独立之逐渐发展。 + +  (寅) 不分种族、性别、语言或宗教,提倡全体人类之人权及基本自由之尊重,并激发世界人民互相维系之意识。 + +  (卯) 于社会、经济及商业事件上,保证联合国全体会员国及其国民之平等待遇,及各该国民于司法裁判上之平等待遇,但以不妨碍上述目的之达成,且不违背第八十条之规定为限。 + +第七十七条 +  一、托管制度适用于依托管协定所置于该制度下之下列各种类之领土: + +    (子) 现在委任统治下之领土。 + +    (丑) 因第二次世界大战结果或将自敌国割离之领土。 + +    (寅) 负管理责任之国家自愿置于该制度下之领土。 + +  二、关于上列种类中之何种领土将置于托管制度之下,及其条件,为此后协定所当规定之事项。 + +第七十八条 +  凡领土已成为联合国之会员国者,不适用托管制度;联合国会员国间之关系,应基于尊重主权平等之原则。 + +第七十九条 +  置于托管制度下之每一领土之托管条款,及其更改或修正,应由直接关系各国、包括联合国之会员国而为委任统治地之受托国者,予以议定,其核准应依第八十三条及第八十五条之规定。 + +第八十条 +  一、除依第七十七条、第七十九条及第八十一条所订置各领土于托管制度下之个别托管协定另有议定外,并在该项协定未经缔结以前,本章任何规定绝对不得解释为以任何方式变更任何国家或人民之权利、或联合国会员国个别签订之现有国际约章之条款。 + +  二、本条第一项不得解释为对于依第七十七条之规定而订置委任统治地或其他领土于托管制度下之协定,授以延展商订之理由。 + +第八十一条 +  凡托管协定均应载有管理领土之条款,并指定管理托管领土之当局。该项当局,以下简称管理当局,得为一个或数个国家,或为联合国本身。 + +第八十二条 +  于任何托管协定内,得指定一个或数个战略防区,包括该项协定下之托管领土之一部或全部,但该项协定并不妨碍依第四十三条而订立之任何特别协定。 + +第八十三条 +  一、联合国关于战略防区之各项职务,包括此项托管协定条款之核准、及其更改或修正,应由安全理事会行使之。 + +  二、第七十六条所规定之基本目的,适用于每一战略防区之人民。 + +  三、安全理事会以不违背托管协定之规定且不妨碍安全之考虑为限,应利用托管理事会之协助,以履行联合国托管制度下关于战略防区内之政治、经济、社会及教育事件之职务。 + +第八十四条 +  管理当局有保证托管领土对于维持国际和平及安全尽其本分之义务。该当局为此目的得利用托管领土之志愿军、便利及协助,以履行该当局对于安全理事会所负关于此点之义务,并以实行地方自卫,且在托管领土内维持法律与秩序。 + +第八十五条 +  一、联合国关于一切非战略防区托管协定之职务,包括此项托管协定条款之核准及其更改或修正,应由大会行使之。 + +  二、托管理事会于大会权力下,应协助大会履行上述之职务。 + +第十三章:托管理事会 +组织 +第八十六条 +  一、托管理事会应由下列联合国会员国组织之: + +    (子) 管理托管领土之会员国。 + +    (丑) 第二十三条所列名之国家而现非管理托管领土者。 + +    (寅) 大会选举必要数额之其他会员国,任期三年,俾使托管理事会理事国之总数,于联合国会员国中之管理托管领土者及不管理者之间,得以平均分配。 + +  二、托管理事会之每一理事国应指定一特别合格之人员,以代表之。 + +职权 +第八十七条 +  大会及在其权力下之托管理事会于履行职务时得: + +  (子) 审查管理当局所送之报告。 + +  (丑) 会同管理当局接受并审查请愿书。 + +  (寅) 与管理当局商定时间,按期视察各托管领土。 + +  (卯) 依托管协定之条款,采取上述其他行动。 + +第八十八条 +  托管理事会应拟定关于各托管领土居民之政治、经济、社会及教育进展之问题单;就大会职权范围内,各托管领土之管理当局应根据该项问题单向大会提出常年报告。 + +投票 +第八十九条 +  一、托管理事会之每一理事国应有一个投票权。 + +  二、托管理事会之决议应以到会及投票之理事国过半数表决之。 + +程序 +第九十条 +  一、托管理事会应自行制定其议事规则,包括其推选主席之方法。 + +  二、托管理事会应依其所定规则,举行必要之会议。此项规则应包括关于经该会理事国过半数之请求而召集会议之规定。 + +第九十一条 +  托管理事会于适当时,应利用经济及社会理事会之协助,并对于各关系事项,利用专门机关之协助。 + +第十四章:国际法院 +第九十二条 +  国际法院为联合国之主要司法机关,应依所附规约执行其职务。该项规约系以国际常设法院之规约为根据并为本宪章之构成部分。 + +第九十三条 +  一、联合国各会员国为国际法院规约之当然当事国 + +  二、非联合国会员国之国家得为国际法院规约当事国之条件,应由大会经安全理事会之建议就各别情形决定之。 + +第九十四条 +  一、联合国每一会员国为任何案件之当事国者,承诺遵行国际法院之判决。 + +  二、遇有一造不履行依法院判决应负之义务时,他造得向于安全理事会申诉。安全理事会如认为必要时,得作成建议或决定应采办法,以执行判决。 + +第九十五条 +  本宪章不得认为禁止联合国会员国依据现有或以后缔结之协定,将其争端托付其他法院解决。 + +第九十六条 +  一、大会或安全理事会对于任何法律问题得请国际法院发表咨询意见。 + +  二、联合国其他机关及各种专门机关,对于其工作范围内之任何法律问题,得随时以大会之授权,请求国际法院发表咨询意见。 + +第十五章:秘书处 +第九十七条 +  秘书处置秘书长一人及本组织所需之办事人员若干人。秘书长应由大会经安全理事会之推荐委派之。秘书长为本组织之行政首长。 + +第九十八条 +  秘书长在大会、安全理事会、经济及社会理事会、及托管理事会之一切会议,应以秘书长资格行使职务,并应执行各该机关所托付之其他职务。秘书长应向大会提送关于本组织工作之常年报告。 + +第九十九条 +  秘书长得将其所认为可能威胁国际和平及安全之任何事件,提请安全理事会注意。 + +第一百条 +  一、秘书长及办事人员于执行职务时,不得请求或接受本组织以外任何政府或其他当局之训示,并应避免足以妨碍其国际官员地位之行动。秘书长及办事人员专对本组织负责。 + +  二、联合国各会员国承诺尊重秘书长及办事人员责任之专属国际性,决不设法影响其责任之履行。 + +第一百零一条 +  一、办事人员由秘书长依大会所定章程委派之。 + +  二、适当之办事人员应长期分配于经济及社会理事会、托管理事会,并于必要时,分配于联合国其他之机关。此项办事人员构成秘书处之一部。 + +  三、办事人员之雇用及其服务条件之决定,应以求达效率、才干及忠诚之最高标准为首要考虑。征聘办事人员时,于可能范围内,应充分注意地域上之普及。 + +第十六章:杂项条款 +第一百零二条 +  一、本宪章发生效力后,联合国任何会员国所缔结之一切条约及国际协定应尽速在秘书处登记,并由秘书处公布之。 + +  二、当事国对于未经依本条第一项规定登记之条约或国际协定,不得向联合国任何机关援引之。 + +第一百零三条 +  联合国会员国在本宪章下之义务与其依任何其他国际协定所负之义务有冲突时,其在本宪章下之义务应居优先。 + +第一百零四条 +  本组织于每一会员国之领土内,应享受于执行其职务及达成其宗旨所必需之法律行为能力。 + +第一百零五条 +  一、本组织于每一会员国之领土内,应享受于达成其宗旨所必需之特权及豁免。 + +  二、联合国会员国之代表及本组织之职员,亦应同样享受于其独立行使关于本组织之职务所必需之特权及豁免。 + +  三、为明定本条第一项及第二项之施行细则起见,大会得作成建议,或为此目的向联合国会员国提议协约。 + +第十七章:过渡安全办法 +第一百零六条 +  在第四十三条所称之特别协定尚未生效,因而安全理事会认为尚不得开始履行第四十二条所规定之责任前,一九四三年十月三十日在莫斯科签订四国宣言之当事国及法兰西应依该宣言第五项之规定,互相洽商,并于必要时,与联合国其他会员国洽商,以代表本组织采取为维持国际和平及安全宗旨所必要之联合行动。 + +第一百零七条 +  本宪章并不取消或禁止负行动责任之政府对于在第二次世界大战中本宪章任何签字国之敌国因该次战争而采取或受权执行之行动。 + +第十八章:修正 +第一百零八条 +  本宪章之修正案经大会会员国三分之二表决并由联合国会员国三分之二、包括安全理事会全体常任理事国,各依其宪法程序批准后,对于联合国所有会员国发生效力。 + +第一百零九条 +  一、联合国会员国,为检讨本宪章,得以大会会员国三分之二表决,经安全理事会任何九理事国之表决,确定日期及地点举行全体会议。联合国每一会员国在全体会议中应有一个投票权。 + +  二、全体会议以三分之二表决所建议对于宪章之任何更改,应经联合国会员国三分之二、包括安全理事会全体常任理事国,各依其宪法程序批准后,发生效力。 + +  三、如于本宪章生效后大会第十届年会前,此项全体会议尚未举行时,应将召集全体会议之提议列入大会该届年会之议事日程;如得大会会员国过半数及安全理事会任何七理事国之表决,此项会议应即举行。 + +第十九章:批准及签字 +第一百一十条 +  一、本宪章应由签字国各依其宪法程序批准之。 + +  二、批准书应交存美利坚合众国政府。该国政府应于每一批准书交存时通知各签字国,如本组织秘书长业经委派时,并应通知秘书长。 + +  三、一俟美利坚合众国政府通知已有中华民国、法兰西、苏维埃社会主义共和国联邦、大不列颠及北爱尔兰联合王国、与美利坚合众国、以及其他签字国之过半数将批准书交存时,本宪章即发生效力。美利坚合众国政府应拟就此项交存批准之议定书并将副本分送所有签字国。 + +  四、本宪章签字国于宪章发生效力后批准者,应自其各将批准书交存之日起为联合国之创始会员国。 + +第一百一十一条 +  本宪章应留存美利坚合众国政府之档库,其中、法、俄、英、及西文各本同一作准。该国政府应将正式副本分送其他签字国政府。 + +  为此联合国各会员国政府之代表谨签字于本宪章,以昭信守。 + +  公历一千九百四十五年六月二十六日签订于金山市。 \ No newline at end of file From 0fb7c318e7463da267450412abe3425ec84d297c Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 22 Oct 2024 09:15:00 -0700 Subject: [PATCH 1792/2019] [stdlib] Removing '_strref_dangerous' from 'python_object' and '_cpython.mojo' Removing `String._strref_dangerous` from `python_object` and from the `_cpython.mojo` implementation. MODULAR_ORIG_COMMIT_REV_ID: 86ce43868974118d0ea18be90e0a5abbc240d58d --- stdlib/src/python/_cpython.mojo | 29 +++++++++++++++++++++++++++- stdlib/src/python/python_object.mojo | 7 ++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 00c5787b66..2f61e8812a 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -39,7 +39,7 @@ from python._bindings import Typed_initproc, PyMojoObject, Pythonable from memory import UnsafePointer -from utils import StringRef +from utils import StringRef, StringSlice # ===-----------------------------------------------------------------------===# @@ -1764,6 +1764,33 @@ struct CPython: self._inc_total_rc() return r + fn PyUnicode_DecodeUTF8(inout self, strslice: StringSlice) -> PyObjectPtr: + """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8. + """ + # return self.PyUnicode_DecodeUTF8(StringRef(strslice.unsafe_ptr(), strslice.byte_length())) + r = self.lib.get_function[ + fn ( + UnsafePointer[c_char], + c_ssize_t, + UnsafePointer[c_char], + ) -> PyObjectPtr + ]("PyUnicode_DecodeUTF8")( + strslice.unsafe_ptr().bitcast[Int8](), + strslice.byte_length(), + "strict".unsafe_cstr_ptr(), + ) + + self.log( + r._get_ptr_as_int(), + " NEWREF PyUnicode_DecodeUTF8, refcnt:", + self._Py_REFCNT(r), + ", str:", + strslice, + ) + + self._inc_total_rc() + return r + fn PySlice_FromSlice(inout self, slice: Slice) -> PyObjectPtr: # Convert Mojo Slice to Python slice parameters # Note: Deliberately avoid using `span.indices()` here and instead pass diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 13715b01f4..5d60c96185 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -414,10 +414,7 @@ struct PythonObject( string: The string value. """ cpython = _get_global_python_itf().cpython() - self.py_object = cpython.PyUnicode_DecodeUTF8( - string._strref_dangerous() - ) - string._strref_keepalive() + self.py_object = cpython.PyUnicode_DecodeUTF8(string.as_string_slice()) fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): """Initialize the object from a list literal. @@ -1416,7 +1413,7 @@ struct PythonObject( var dict_obj = cpython.PyDict_New() for entry in kwargs.items(): var key = cpython.PyUnicode_DecodeUTF8( - entry[].key._strref_dangerous() + entry[].key.as_string_slice() ) var result = cpython.PyDict_SetItem( dict_obj, key, entry[].value.py_object From 2a026a767d648571f1a93cbbfb82c781cd20bf1a Mon Sep 17 00:00:00 2001 From: Helehex Date: Tue, 22 Oct 2024 13:43:09 -0600 Subject: [PATCH 1793/2019] [External] [stdlib] Fix docstring examples in `Dict` (#49584) [External] [stdlib] Fix docstring examples in `Dict` Co-authored-by: Helehex Closes modularml/mojo#3688 MODULAR_ORIG_COMMIT_REV_ID: ca648f169c31242b3aced62a798490914fbbc6e4 --- stdlib/src/collections/dict.mojo | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 07ffbfd6ce..c3121e9ab9 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -489,7 +489,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( Example usage: ```mojo - var x = Dict[Int,Int](power_of_two_initial_capacity = 1024) + from collections import Dict + + var x = Dict[Int, Int](power_of_two_initial_capacity = 1024) # Insert (2/3 of 1024) entries without reallocation. ``` @@ -533,10 +535,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( Returns: The new dictionary. """ - var dict = Dict[K, V]() + var my_dict = Dict[K, V]() for key in keys: - dict[key[]] = value - return dict + my_dict[key[]] = value + return my_dict @staticmethod fn fromkeys( @@ -688,6 +690,8 @@ struct Dict[K: KeyElement, V: CollectionElement]( the way to call this method is a bit special. Here is an example below: ```mojo + from collections import Dict + var my_dict = Dict[Int, Float64]() my_dict[1] = 1.1 my_dict[2] = 2.2 @@ -899,7 +903,13 @@ struct Dict[K: KeyElement, V: CollectionElement]( access the key and value as attributes ie. ```mojo - for e in dict.items(): + from collections import Dict + + var my_dict = Dict[String, Int]() + my_dict["a"] = 1 + my_dict["b"] = 2 + + for e in my_dict.items(): print(e[].key, e[].value) ``` @@ -1243,7 +1253,13 @@ struct OwnedKwargsDict[V: CollectionElement]( access the key and value as attributes ie. ```mojo - for e in dict.items(): + from collections import Dict + + var my_dict = Dict[String, Int]() + my_dict["a"] = 1 + my_dict["b"] = 2 + + for e in my_dict.items(): print(e[].key, e[].value) ``` From 91d952ec84f327c17d33c81cff5e31c941d9d6d7 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Tue, 22 Oct 2024 18:03:49 -0400 Subject: [PATCH 1794/2019] [vscode] Allow cancelling the installation of the internal sdk MODULAR_ORIG_COMMIT_REV_ID: 0c5d82026a82f674ce0d1a72e8322265ee646ce9 --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 21776f1a33..4a21528c87 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -214,6 +214,9 @@ what we publish. - The Mojo LLDB debugger now supports symbol breakpoints, e.g. `b main` or `b my_module::main`. +- The VS Code extension now allows cancelling the installation of its private + MAX SDK. + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` @@ -373,3 +376,6 @@ what we publish. - The VS Code extension now downloads its private copy of the MAX SDK in a way that prevents ETXTBSY errors on Linux. + +- The VS Code extension now allows invoking a mojo formatter from SDK + installations that contain white spaces in their path. From 1374bfe3c3e947bcc6e41430cd0998a79e437d0a Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 22 Oct 2024 16:22:24 -0700 Subject: [PATCH 1795/2019] [stdlib] Remove system-windows from lit MODULAR_ORIG_COMMIT_REV_ID: e372890116ec45b133713100d510f59c9eb31710 --- stdlib/test/builtin/test_stdin.mojo | 1 - stdlib/test/os/path/test_dirname.mojo | 1 - stdlib/test/os/path/test_expanduser.mojo | 1 - stdlib/test/pathlib/test_pathlib.mojo | 1 - stdlib/test/pwd/test_pwd.mojo | 1 - 5 files changed, 5 deletions(-) diff --git a/stdlib/test/builtin/test_stdin.mojo b/stdlib/test/builtin/test_stdin.mojo index ac566def06..8c6b2d7f6f 100644 --- a/stdlib/test/builtin/test_stdin.mojo +++ b/stdlib/test/builtin/test_stdin.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !system-windows # RUN: echo "Hello, World" | %mojo %s from builtin.io import _fdopen diff --git a/stdlib/test/os/path/test_dirname.mojo b/stdlib/test/os/path/test_dirname.mojo index 2b4a914ad3..daa6e64762 100644 --- a/stdlib/test/os/path/test_dirname.mojo +++ b/stdlib/test/os/path/test_dirname.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !system-windows # RUN: %mojo %s diff --git a/stdlib/test/os/path/test_expanduser.mojo b/stdlib/test/os/path/test_expanduser.mojo index 7cfe60bfce..78764e6bf1 100644 --- a/stdlib/test/os/path/test_expanduser.mojo +++ b/stdlib/test/os/path/test_expanduser.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !system-windows # RUN: %mojo %s diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 43d677fc63..249d5b2133 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !system-windows # RUN: %mojo -D TEMP_FILE=%t %s import os diff --git a/stdlib/test/pwd/test_pwd.mojo b/stdlib/test/pwd/test_pwd.mojo index f58121a69d..0cab2ff11f 100644 --- a/stdlib/test/pwd/test_pwd.mojo +++ b/stdlib/test/pwd/test_pwd.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# REQUIRES: !system-windows # RUN: %mojo %s import pwd From 0dcfe97f4da908724cb4a9a599ce033c29e783e8 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 23 Oct 2024 14:51:58 -0700 Subject: [PATCH 1796/2019] [******][sdk] Removing 'String._strref_dangerous' in ****** and MAX graph. These are some of the final changes before we can remove the 'String._strref_dangerous' function completely from the code base. MODULAR_ORIG_COMMIT_REV_ID: 7a82694f203b98a4919599a0c61e89de7b86d544 --- stdlib/src/collections/string.mojo | 17 ----------------- stdlib/src/utils/string_slice.mojo | 18 ------------------ 2 files changed, 35 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 55ea75c2ad..756a97d8a6 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1501,23 +1501,6 @@ struct String( buf.append(0) return String(buf^) - fn _strref_dangerous(self) -> StringRef: - """ - Returns an inner pointer to the string as a StringRef. - This functionality is extremely dangerous because Mojo eagerly releases - strings. Using this requires the use of the _strref_keepalive() method - to keep the underlying string alive long enough. - """ - return StringRef(self.unsafe_ptr(), self.byte_length()) - - fn _strref_keepalive(self): - """ - A noop that keeps `self` alive through the call. This - can be carefully used with `_strref_dangerous()` to wield inner pointers - without the string getting deallocated early. - """ - pass - fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index e68495372d..0456bdeb3a 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -675,24 +675,6 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( return len(self.as_bytes()) - fn _strref_dangerous(self) -> StringRef: - """Returns an inner pointer to the string as a StringRef. - - Safety: - This functionality is extremely dangerous because Mojo eagerly - releases strings. Using this requires the use of the - _strref_keepalive() method to keep the underlying string alive long - enough. - """ - return StringRef(self.unsafe_ptr(), self.byte_length()) - - fn _strref_keepalive(self): - """A no-op that keeps `self` alive through the call. This - can be carefully used with `_strref_dangerous()` to wield inner pointers - without the string getting deallocated early. - """ - pass - fn startswith( self, prefix: StringSlice[_], start: Int = 0, end: Int = -1 ) -> Bool: From 7f8b8ae0b661ba05cf8294376fe01625486a1092 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 23 Oct 2024 20:55:29 -0400 Subject: [PATCH 1797/2019] [stdlib] Various clean-ups in CPython Remove a TODO and some extraneous `_ = <...>` patterns. While in the neighborhood, mark `CPython` with `@value` decorator since the copyinit is the same as the one that the compiler would generate. MODULAR_ORIG_COMMIT_REV_ID: 808b19875d62a9bf9679a2122434eba26b3ee369 --- stdlib/src/python/_cpython.mojo | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 2f61e8812a..409f9587eb 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -60,8 +60,7 @@ alias Py_tp_repr = 66 alias Py_TPFLAGS_DEFAULT = 0 -# TODO(MSTDL-892): Change this to alias ffi.C_ssize_t -alias Py_ssize_t = Int +alias Py_ssize_t = c_ssize_t # TODO(MOCO-1138): # This should be a C ABI function pointer, not a Mojo ABI function. @@ -460,9 +459,7 @@ struct PyObject(Stringable, Representable, Writable): """ var object_ref_count: Int - # FIXME: should we use `PyObjectPtr`? I don't think so! var object_type: UnsafePointer[PyTypeObject] - # var object_type: PyObjectPtr fn __init__(inout self): self.object_ref_count = 0 @@ -712,6 +709,7 @@ struct PyModuleDef(Stringable, Representable, Writable): writer.write(")") +@value struct CPython: """ Handle to the CPython interpreter present in the current process. @@ -781,22 +779,12 @@ struct CPython: self.init_error = "compatible Python library not found" self.lib.get_function[fn () -> None]("Py_Initialize")() self.version = PythonVersion(_py_get_version(self.lib)) - _ = self.Py_None() - _ = self.PyDict_Type() else: self.version = PythonVersion(0, 0, 0) fn __del__(owned self): pass - fn __copyinit__(inout self, existing: Self): - self.lib = existing.lib - self.dict_type = existing.dict_type - self.logging_enabled = existing.logging_enabled - self.version = existing.version - self.total_ref_count = existing.total_ref_count - self.init_error = existing.init_error - @staticmethod fn destroy(inout existing: CPython): if existing.logging_enabled: From 10d6f498abb7734125b6ee128bb74e2ae290cf0f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 23 Oct 2024 21:49:37 -0400 Subject: [PATCH 1798/2019] [stdlib] Consolidate `builtin/_math.mojo` Now that the `math` module is open-source, move the contents of `builtin/_math.mojo` into `math/math.mojo`. Explicitly re-export `Truncable` from `math/__init__.mojo`. In a follow-up, we should be able to consolidate `builtin/math.mojo` into `math/math.mojo` and just re-export the necessary entities now that we have a a `prelude` module. MODULAR_ORIG_COMMIT_REV_ID: 56ddc5b0e33490532b3908a9eeb1dd2c01d13b33 --- stdlib/src/builtin/_math.mojo | 214 -------------------------- stdlib/src/builtin/float_literal.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/int_literal.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/math/__init__.mojo | 1 + stdlib/src/math/math.mojo | 203 +++++++++++++++++++++++- 7 files changed, 207 insertions(+), 219 deletions(-) delete mode 100644 stdlib/src/builtin/_math.mojo diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo deleted file mode 100644 index 22a309e7ed..0000000000 --- a/stdlib/src/builtin/_math.mojo +++ /dev/null @@ -1,214 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -"""Module to contain some components of the future math module. - -This is needed to work around some circular dependencies; all elements of this -module should be exposed by the current `math` module. The contents of this -module should be eventually moved to the `math` module when it's open sourced. -""" - -from bit import count_trailing_zeros - -# ===----------------------------------------------------------------------=== # -# Ceilable -# ===----------------------------------------------------------------------=== # - - -trait Ceilable: - """ - The `Ceilable` trait describes a type that defines a ceiling operation. - - Types that conform to `Ceilable` will work with the builtin `ceil` - function. The ceiling operation always returns the same type as the input. - - For example: - ```mojo - from math import Ceilable, ceil - - @value - struct Complex(Ceilable): - var re: Float64 - var im: Float64 - - fn __ceil__(self) -> Self: - return Self(ceil(self.re), ceil(self.im)) - ``` - """ - - # TODO(MOCO-333): Reconsider the signature when we have parametric traits or - # associated types. - fn __ceil__(self) -> Self: - """Return the ceiling of the Int value, which is itself. - - Returns: - The Int value itself. - """ - ... - - -# ===----------------------------------------------------------------------=== # -# Floorable -# ===----------------------------------------------------------------------=== # - - -trait Floorable: - """ - The `Floorable` trait describes a type that defines a floor operation. - - Types that conform to `Floorable` will work with the builtin `floor` - function. The floor operation always returns the same type as the input. - - For example: - ```mojo - from math import Floorable, floor - - @value - struct Complex(Floorable): - var re: Float64 - var im: Float64 - - fn __floor__(self) -> Self: - return Self(floor(self.re), floor(self.im)) - ``` - """ - - # TODO(MOCO-333): Reconsider the signature when we have parametric traits or - # associated types. - fn __floor__(self) -> Self: - """Return the floor of the Int value, which is itself. - - Returns: - The Int value itself. - """ - ... - - -# ===----------------------------------------------------------------------=== # -# CeilDivable -# ===----------------------------------------------------------------------=== # - - -trait CeilDivable: - """ - The `CeilDivable` trait describes a type that defines a ceil division - operation. - - Types that conform to `CeilDivable` will work with the `math.ceildiv` - function. - - For example: - ```mojo - from math import CeilDivable - - @value - struct Foo(CeilDivable): - var x: Float64 - - fn __floordiv__(self, other: Self) -> Self: - return self.x // other.x - - fn __rfloordiv__(self, other: Self) -> Self: - return other // self - - fn __neg__(self) -> Self: - return -self.x - ``` - """ - - # TODO(MOCO-333): Reconsider these signatures when we have parametric traits - # or associated types. - fn __floordiv__(self, other: Self) -> Self: - ... - - fn __rfloordiv__(self, other: Self) -> Self: - ... - - fn __neg__(self) -> Self: - ... - - -trait CeilDivableRaising: - """ - The `CeilDivable` trait describes a type that define a floor division and - negation operation that can raise. - - Types that conform to `CeilDivableRaising` will work with the `//` operator - as well as the `math.ceildiv` function. - - For example: - ```mojo - from math import CeilDivableRaising - - @value - struct Foo(CeilDivableRaising): - var x: object - - fn __floordiv__(self, other: Self) raises -> Self: - return self.x // other.x - - fn __rfloordiv__(self, other: Self) raises -> Self: - return other // self - - fn __neg__(self) raises -> Self: - return -self.x - ``` - """ - - # TODO(MOCO-333): Reconsider these signatures when we have parametric traits - # or associated types. - fn __floordiv__(self, other: Self) raises -> Self: - ... - - fn __rfloordiv__(self, other: Self) raises -> Self: - ... - - fn __neg__(self) raises -> Self: - ... - - -# ===----------------------------------------------------------------------=== # -# Truncable -# ===----------------------------------------------------------------------=== # - - -trait Truncable: - """ - The `Truncable` trait describes a type that defines a truncation operation. - - Types that conform to `Truncable` will work with the builtin `trunc` - function. The truncation operation always returns the same type as the - input. - - For example: - ```mojo - from math import Truncable, trunc - - @value - struct Complex(Truncable): - var re: Float64 - var im: Float64 - - fn __trunc__(self) -> Self: - return Self(trunc(re), trunc(im)) - ``` - """ - - # TODO(MOCO-333): Reconsider the signature when we have parametric traits or - # associated types. - fn __trunc__(self) -> Self: - """Return the truncated Int value, which is itself. - - Returns: - The Int value itself. - """ - ... diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index b8ce2b6b29..d6a19e9595 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin._math import Ceilable, CeilDivable, Floorable, Truncable +from math import Ceilable, CeilDivable, Floorable, Truncable # ===----------------------------------------------------------------------===# # FloatLiteral diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index a4ba643759..15337ae474 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement from builtin._documentation import doc_private -from builtin._math import Ceilable, CeilDivable, Floorable, Truncable +from math import Ceilable, CeilDivable, Floorable, Truncable from hashlib.hash import _hash_simd from hashlib._hasher import _HashableWithHasher, _Hasher from builtin.io import _snprintf diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 5a6493a693..b52146b2df 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -13,7 +13,7 @@ """Implements the IntLiteral class.""" from builtin._documentation import doc_private -from builtin._math import Ceilable, CeilDivable, Floorable, Truncable +from math import Ceilable, CeilDivable, Floorable, Truncable @value diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index e8eafc1525..1f5384baa1 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -35,7 +35,7 @@ from os import abort from bit import pop_count from builtin._documentation import doc_private -from builtin._math import Ceilable, CeilDivable, Floorable, Truncable +from math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from hashlib.hash import _hash_simd from hashlib._hasher import _HashableWithHasher, _Hasher diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 066cad8edc..3a9e67e7c7 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -23,6 +23,7 @@ from .math import ( CeilDivable, CeilDivableRaising, Floorable, + Truncable, acos, acosh, align_down, diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index ee63e6fd81..5e9b759c43 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -19,6 +19,7 @@ from math import floor ``` """ +from builtin._documentation import doc_private from collections import List from sys._assembly import inlined_assembly from sys.ffi import _external_call_const @@ -34,7 +35,6 @@ from sys import ( from memory import UnsafePointer from bit import count_trailing_zeros -from builtin._math import * from builtin.dtype import _integral_type_of from builtin.simd import _simd_apply, _modf from sys.info import _current_arch @@ -2470,3 +2470,204 @@ fn _call_ptx_intrinsic[ ) return res + + +# ===----------------------------------------------------------------------=== # +# Ceilable +# ===----------------------------------------------------------------------=== # + + +trait Ceilable: + """ + The `Ceilable` trait describes a type that defines a ceiling operation. + + Types that conform to `Ceilable` will work with the builtin `ceil` + function. The ceiling operation always returns the same type as the input. + + For example: + ```mojo + from math import Ceilable, ceil + + @value + struct Complex(Ceilable): + var re: Float64 + var im: Float64 + + fn __ceil__(self) -> Self: + return Self(ceil(self.re), ceil(self.im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __ceil__(self) -> Self: + """Return the ceiling of the Int value, which is itself. + + Returns: + The Int value itself. + """ + ... + + +# ===----------------------------------------------------------------------=== # +# Floorable +# ===----------------------------------------------------------------------=== # + + +trait Floorable: + """ + The `Floorable` trait describes a type that defines a floor operation. + + Types that conform to `Floorable` will work with the builtin `floor` + function. The floor operation always returns the same type as the input. + + For example: + ```mojo + from math import Floorable, floor + + @value + struct Complex(Floorable): + var re: Float64 + var im: Float64 + + fn __floor__(self) -> Self: + return Self(floor(self.re), floor(self.im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __floor__(self) -> Self: + """Return the floor of the Int value, which is itself. + + Returns: + The Int value itself. + """ + ... + + +# ===----------------------------------------------------------------------=== # +# CeilDivable +# ===----------------------------------------------------------------------=== # + + +trait CeilDivable: + """ + The `CeilDivable` trait describes a type that defines a ceil division + operation. + + Types that conform to `CeilDivable` will work with the `math.ceildiv` + function. + + For example: + ```mojo + from math import CeilDivable + + @value + struct Foo(CeilDivable): + var x: Float64 + + fn __floordiv__(self, other: Self) -> Self: + return self.x // other.x + + fn __rfloordiv__(self, other: Self) -> Self: + return other // self + + fn __neg__(self) -> Self: + return -self.x + ``` + """ + + # TODO(MOCO-333): Reconsider these signatures when we have parametric traits + # or associated types. + @doc_private + fn __floordiv__(self, other: Self) -> Self: + ... + + @doc_private + fn __rfloordiv__(self, other: Self) -> Self: + ... + + @doc_private + fn __neg__(self) -> Self: + ... + + +trait CeilDivableRaising: + """ + The `CeilDivable` trait describes a type that define a floor division and + negation operation that can raise. + + Types that conform to `CeilDivableRaising` will work with the `//` operator + as well as the `math.ceildiv` function. + + For example: + ```mojo + from math import CeilDivableRaising + + @value + struct Foo(CeilDivableRaising): + var x: object + + fn __floordiv__(self, other: Self) raises -> Self: + return self.x // other.x + + fn __rfloordiv__(self, other: Self) raises -> Self: + return other // self + + fn __neg__(self) raises -> Self: + return -self.x + ``` + """ + + # TODO(MOCO-333): Reconsider these signatures when we have parametric traits + # or associated types. + @doc_private + fn __floordiv__(self, other: Self) raises -> Self: + ... + + @doc_private + fn __rfloordiv__(self, other: Self) raises -> Self: + ... + + @doc_private + fn __neg__(self) raises -> Self: + ... + + +# ===----------------------------------------------------------------------=== # +# Truncable +# ===----------------------------------------------------------------------=== # + + +trait Truncable: + """ + The `Truncable` trait describes a type that defines a truncation operation. + + Types that conform to `Truncable` will work with the builtin `trunc` + function. The truncation operation always returns the same type as the + input. + + For example: + ```mojo + from math import Truncable, trunc + + @value + struct Complex(Truncable): + var re: Float64 + var im: Float64 + + fn __trunc__(self) -> Self: + return Self(trunc(re), trunc(im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __trunc__(self) -> Self: + """Return the truncated Int value, which is itself. + + Returns: + The Int value itself. + """ + ... From 6d92b21f351a9dfcdaa44eb263de4e543086cd4a Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Thu, 24 Oct 2024 15:43:54 -0400 Subject: [PATCH 1799/2019] [docs] Remove __mojo_debugger_raise_hook from docs (#49751) Fixes MOTO-821 This is an internal function and shouldn't appear in https://docs.modular.com/mojo/stdlib/builtin/error/ MODULAR_ORIG_COMMIT_REV_ID: a3fd1c63e437308cad2316c20f2b4320410892c9 --- stdlib/src/builtin/error.mojo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index e4980a1af2..4261725a80 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -20,6 +20,7 @@ from sys.ffi import c_char from memory import UnsafePointer, memcpy from memory.memory import _free +from builtin._documentation import doc_private from utils import StringRef @@ -211,6 +212,7 @@ struct Error( return String(StringRef(self.data, length)) +@doc_private fn __mojo_debugger_raise_hook(): """This function is used internally by the Mojo Debugger.""" pass From 1afe160180e4d5535f0038cff3164ca02f08d1cd Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Thu, 24 Oct 2024 23:48:54 -0400 Subject: [PATCH 1800/2019] [docs] Make doc_private public in the prebuilt libs This is a sort of fundamental decorator and it's better to simply make it available everywhere. MODULAR_ORIG_COMMIT_REV_ID: e970f2fd5903acad23badf52445420542a7044e8 --- stdlib/src/builtin/bool.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/int_literal.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/uint.mojo | 2 +- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/documentation/__init__.mojo | 15 +++++++++++++++ .../documentation.mojo} | 3 +-- stdlib/src/math/math.mojo | 2 +- stdlib/src/memory/maybe_uninitialized.mojo | 2 +- stdlib/src/memory/pointer.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/src/prelude/__init__.mojo | 1 + stdlib/src/python/python_object.mojo | 2 +- stdlib/src/utils/index.mojo | 2 +- 17 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 stdlib/src/documentation/__init__.mojo rename stdlib/src/{builtin/_documentation.mojo => documentation/documentation.mojo} (93%) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 174aa10b7d..c8ca50febd 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin._documentation import doc_private +from documentation import doc_private from collections import Set, List from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index afec4004b6..25b487dbd3 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin._documentation import doc_private +from documentation import doc_private from memory import Pointer, UnsafePointer diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 4261725a80..43bc106ba3 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -20,7 +20,7 @@ from sys.ffi import c_char from memory import UnsafePointer, memcpy from memory.memory import _free -from builtin._documentation import doc_private +from documentation import doc_private from utils import StringRef diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 15337ae474..4aa42044fb 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement -from builtin._documentation import doc_private +from documentation import doc_private from math import Ceilable, CeilDivable, Floorable, Truncable from hashlib.hash import _hash_simd from hashlib._hasher import _HashableWithHasher, _Hasher diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index b52146b2df..7aff828a61 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" -from builtin._documentation import doc_private +from documentation import doc_private from math import Ceilable, CeilDivable, Floorable, Truncable diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 1f5384baa1..83e12c56ee 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -34,7 +34,7 @@ from sys._assembly import inlined_assembly from os import abort from bit import pop_count -from builtin._documentation import doc_private +from documentation import doc_private from math import Ceilable, CeilDivable, Floorable, Truncable from builtin.dtype import _uint_type_of_width from hashlib.hash import _hash_simd diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 1dac5cad1e..31fa561dc7 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import bitwidthof from utils._visualizers import lldb_formatter_wrapping_type -from builtin._documentation import doc_private +from documentation import doc_private from hashlib.hash import _hash_simd from hashlib._hasher import _HashableWithHasher, _Hasher diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index c556f0c4ee..ac50c7b4a1 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -31,7 +31,7 @@ print(d) # prints 2 ``` """ -from builtin._documentation import doc_private +from documentation import doc_private from os import abort from utils import Variant diff --git a/stdlib/src/documentation/__init__.mojo b/stdlib/src/documentation/__init__.mojo new file mode 100644 index 0000000000..f307558aa3 --- /dev/null +++ b/stdlib/src/documentation/__init__.mojo @@ -0,0 +1,15 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the documentation package.""" + +from .documentation import doc_private diff --git a/stdlib/src/builtin/_documentation.mojo b/stdlib/src/documentation/documentation.mojo similarity index 93% rename from stdlib/src/builtin/_documentation.mojo rename to stdlib/src/documentation/documentation.mojo index 7c7f600048..76892d3c6c 100644 --- a/stdlib/src/builtin/_documentation.mojo +++ b/stdlib/src/documentation/documentation.mojo @@ -27,12 +27,11 @@ fn doc_private(): This decorator allows for hiding the documentation for a declaration during generation. This is often used to hide `__init__`, and other special - methods, that are intended for internal consumption. + methods, that are not intended to be part of a library's documentation. For example: ```mojo - %# from builtin._documentation import doc_private struct Foo: @doc_private fn __init__(inout self): diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 5e9b759c43..23933e7812 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -19,7 +19,7 @@ from math import floor ``` """ -from builtin._documentation import doc_private +from documentation import doc_private from collections import List from sys._assembly import inlined_assembly from sys.ffi import _external_call_const diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 93c0b324c3..e2ab9d8035 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # from os import abort -from builtin._documentation import doc_private +from documentation import doc_private struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 167feb5f5a..e40f50ecfa 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -19,7 +19,7 @@ from memory import Pointer ``` """ -from builtin._documentation import doc_private +from documentation import doc_private # ===----------------------------------------------------------------------===# # AddressSpace diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 548ea8ccbd..643f478a67 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,7 +19,7 @@ from memory import UnsafePointer ``` """ -from builtin._documentation import doc_private +from documentation import doc_private from sys import alignof, sizeof, triple_is_nvidia_cuda from sys.intrinsics import ( _mlirtype_is_eq, diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 7d7badc1a2..874d79cbab 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -134,3 +134,4 @@ from collections.string import ( from hashlib.hash import hash, Hashable from memory import Pointer, AddressSpace from utils import AsBytes, Writable, Writer +from documentation import doc_private diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 5d60c96185..7411043432 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -19,7 +19,7 @@ from python import PythonObject ``` """ -from builtin._documentation import doc_private +from documentation import doc_private from sys.intrinsics import _type_is_eq from memory import UnsafePointer diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 054f2a85bd..bca9b80af4 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -22,7 +22,7 @@ from utils import IndexList from sys import bitwidthof from builtin.dtype import _int_type_of_width, _uint_type_of_width -from builtin._documentation import doc_private +from documentation import doc_private from builtin.io import _get_dtype_printf_format, _snprintf from collections.string import _calc_initial_buffer_size From ec48f1dd2f47a0d30801af1cabe9bb3f72a0cf4c Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Fri, 25 Oct 2024 01:22:48 -0400 Subject: [PATCH 1801/2019] [changelog] More debugger updates MODULAR_ORIG_COMMIT_REV_ID: 98fafafd0ae8ebd2db9c60f927f5228f13534252 --- docs/changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 4a21528c87..87aa2d95d7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -217,6 +217,16 @@ what we publish. - The VS Code extension now allows cancelling the installation of its private MAX SDK. +- The VS Code extension now opens the Run and Debug tab automatically whenever + a debug session starts. + +- The `mojo debug --vscode` command now support the `--init-command` and + `--stop-on-entry` flags. Execute `mojo debug --help` for more information. + +- The Mojo LLDB debugger on VS Code now supports inspecting the raw attributes + of variables that are handled as synthetic types, e.g. `List` from Mojo or + `std::vector` from C++. + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` From 0fc37a5ca289939e2bc4784ec9163e4a723d2b41 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 25 Oct 2024 11:41:56 -0500 Subject: [PATCH 1802/2019] [stdlib] cleanup: Rename create_wrapper_function => py_c_function_wrapper Also removed an extra level of indirection. MODULAR_ORIG_COMMIT_REV_ID: 11b318bf118b0da87c179009bc835af5c5e46234 --- stdlib/src/builtin/_pybind.mojo | 4 +- stdlib/src/python/_bindings.mojo | 87 ++++++++++++++++---------------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index c99db3fe0a..31856e9b80 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -29,7 +29,7 @@ from python._cpython import ( from python._bindings import ( Pythonable, PyMojoObject, - create_wrapper_function, + py_c_function_wrapper, check_argument_type, # Imported for use by the compiler check_arguments_arity, @@ -107,7 +107,7 @@ fn add_wrapper_to_module[ module, List[PyMethodDef]( PyMethodDef.function[ - create_wrapper_function[wrapper_func](), func_name + py_c_function_wrapper[wrapper_func], func_name ]() ), ) diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 51455e74d0..e03125a4ac 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -195,9 +195,13 @@ fn tp_repr_wrapper[T: Pythonable](py_self: PyObjectPtr) -> PyObjectPtr: # ===-----------------------------------------------------------------------===# -fn create_wrapper_function[ +fn py_c_function_wrapper[ user_func: fn (PythonObject, TypedPythonObject["Tuple"]) -> PythonObject -]() -> PyCFunction: +](py_self_ptr: PyObjectPtr, args_ptr: PyObjectPtr,) -> PyObjectPtr: + """The instantiated type of this generic function is a `PyCFunction`, + suitable for being called from Python. + """ + # > When a C function is called from Python, it borrows references to its # > arguments from the caller. The caller owns a reference to the object, # > so the borrowed reference’s lifetime is guaranteed until the function @@ -206,52 +210,47 @@ fn create_wrapper_function[ # > # > -- https://docs.python.org/3/extending/extending.html#ownership-rules - fn wrapper(py_self_ptr: PyObjectPtr, args_ptr: PyObjectPtr) -> PyObjectPtr: - # SAFETY: - # Here we illegally (but carefully) construct _owned_ `PythonObject` - # values from the borrowed object reference arguments. We are careful - # down below to prevent the destructor for these objects from running - # so that we do not illegally decrement the reference count of these - # objects we do not own. - # - # This is valid to do, because these are passed using the `borrowed` - # argument convention to `user_func`, so logically they are treated - # as Python borrowed references. - var py_self = PythonObject(py_self_ptr) - var args = TypedPythonObject["Tuple"]( - unsafe_unchecked_from=PythonObject(args_ptr) - ) - - # SAFETY: - # Call the user provided function, and take ownership of the - # PyObjectPtr of the returned PythonObject. - var result = user_func(py_self, args).steal_data() - - # Do not destroy the provided PyObjectPtr arguments, since they - # actually have ownership of the underlying object. - __mlir_op.`lit.ownership.mark_destroyed`( - __get_mvalue_as_litref(py_self) - ) - - # SAFETY: - # Prevent `args` AND `args._obj` from being destroyed, since we don't - # own them. - # TODO: Use a `mem.forget(args^)` function here in the future. - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(args)) - var _obj = args._obj^ - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(_obj)) - - return result - - return wrapper + # SAFETY: + # Here we illegally (but carefully) construct _owned_ `PythonObject` + # values from the borrowed object reference arguments. We are careful + # down below to prevent the destructor for these objects from running + # so that we do not illegally decrement the reference count of these + # objects we do not own. + # + # This is valid to do, because these are passed using the `borrowed` + # argument convention to `user_func`, so logically they are treated + # as Python borrowed references. + var py_self = PythonObject(py_self_ptr) + var args = TypedPythonObject["Tuple"]( + unsafe_unchecked_from=PythonObject(args_ptr) + ) + + # SAFETY: + # Call the user provided function, and take ownership of the + # PyObjectPtr of the returned PythonObject. + var result = user_func(py_self, args).steal_data() + + # Do not destroy the provided PyObjectPtr arguments, since they + # actually have ownership of the underlying object. + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(py_self)) + + # SAFETY: + # Prevent `args` AND `args._obj` from being destroyed, since we don't + # own them. + # TODO: Use a `mem.forget(args^)` function here in the future. + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(args)) + var _obj = args._obj^ + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(_obj)) + + return result # Wrap a `raises` function -fn create_wrapper_function[ +fn py_c_function_wrapper[ user_func: fn ( PythonObject, TypedPythonObject["Tuple"] ) raises -> PythonObject -]() -> PyCFunction: +](py_self_ptr: PyObjectPtr, py_args_ptr: PyObjectPtr) -> PyObjectPtr: fn wrapper( py_self: PythonObject, args: TypedPythonObject["Tuple"] ) -> PythonObject: @@ -280,8 +279,8 @@ fn create_wrapper_function[ # Does this lead to multiple levels of indirect function calls for # `raises` functions? Could we fix that by marking `wrapper` here as # `@always_inline`? - # Call the non-`raises` overload of `create_wrapper_function`. - return create_wrapper_function[wrapper]() + # Call the non-`raises` overload of `py_c_function_wrapper`. + return py_c_function_wrapper[wrapper](py_self_ptr, py_args_ptr) fn check_arguments_arity( From 4879c578075ac87538590edfa50af2401daa46c4 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 25 Oct 2024 17:17:52 -0500 Subject: [PATCH 1803/2019] [stdlib] cleanup: Follow style guide method order on PythonObject MODULAR_ORIG_COMMIT_REV_ID: a9171d46a997d665ca4c212bf19ad9a8d119a47d --- stdlib/src/python/python_object.mojo | 214 ++++++++++++++------------- 1 file changed, 109 insertions(+), 105 deletions(-) diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 7411043432..5cb15ecdf0 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -534,6 +534,20 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() cpython.Py_IncRef(self.py_object) + fn __del__(owned self): + """Destroy the object. + + This decrements the underlying refcount of the pointed-to object. + """ + var cpython = _get_global_python_itf().cpython() + # Acquire GIL such that __del__ can be called safely for cases where the + # PyObject is handled in non-python contexts. + var state = cpython.PyGILState_Ensure() + if not self.py_object.is_null(): + cpython.Py_DecRef(self.py_object) + self.py_object = PyObjectPtr() + cpython.PyGILState_Release(state) + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# @@ -552,47 +566,6 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return _PyIter(PythonObject(iter)) - # ===-------------------------------------------------------------------===# - # Methods - # ===-------------------------------------------------------------------===# - - fn unsafe_as_py_object_ptr(self) -> PyObjectPtr: - """Get the underlying PyObject pointer. - - Returns: - The underlying PyObject pointer. - - Safety: - Use-after-free: The caller must take care that `self` outlives the - usage of the pointer returned by this function. - """ - return self.py_object - - fn steal_data(owned self) -> PyObjectPtr: - """Take ownership of the underlying pointer from the Python object. - - Returns: - The underlying data. - """ - var ptr = self.py_object - self.py_object = PyObjectPtr() - - return ptr - - fn __del__(owned self): - """Destroy the object. - - This decrements the underlying refcount of the pointed-to object. - """ - var cpython = _get_global_python_itf().cpython() - # Acquire GIL such that __del__ can be called safely for cases where the - # PyObject is handled in non-python contexts. - var state = cpython.PyGILState_Ensure() - if not self.py_object.is_null(): - cpython.Py_DecRef(self.py_object) - self.py_object = PyObjectPtr() - cpython.PyGILState_Release(state) - fn __getattr__(self, name: StringLiteral) raises -> PythonObject: """Return the value of the object attribute with the given name. @@ -670,47 +643,6 @@ struct PythonObject( """ return not (self is other) - fn __len__(self) raises -> Int: - """Returns the length of the object. - - Returns: - The length of the object. - """ - var cpython = _get_global_python_itf().cpython() - var result = cpython.PyObject_Length(self.py_object) - if result == -1: - # TODO: Improve error message so we say - # "object of type 'int' has no len()" function to match Python - raise Error("object has no len()") - return result - - fn __hash__(self) -> UInt: - """Returns the length of the object. - - Returns: - The length of the object. - """ - var cpython = _get_global_python_itf().cpython() - var result = cpython.PyObject_Length(self.py_object) - # TODO: make this function raise when we can raise parametrically. - debug_assert(result != -1, "object is not hashable") - return result - - fn __hash__[H: _Hasher](self, inout hasher: H): - """Updates hasher with this python object hash value. - - Parameters: - H: The hasher type. - - Args: - hasher: The hasher instance. - """ - var cpython = _get_global_python_itf().cpython() - var result = cpython.PyObject_Hash(self.py_object) - # TODO: make this function raise when we can raise parametrically. - debug_assert(result != -1, "object is not hashable") - hasher.update(result) - fn __getitem__(self, *args: PythonObject) raises -> PythonObject: """Return the value for the given key or keys. @@ -1379,9 +1311,6 @@ struct PythonObject( """ return self._call_zero_arg_method("__invert__") - fn _get_ptr_as_int(self) -> Int: - return self.py_object._get_ptr_as_int() - # see https://github.com/python/cpython/blob/main/Objects/call.c # for decrement rules fn __call__( @@ -1439,6 +1368,51 @@ struct PythonObject( ) return PythonObject(result) + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + fn __len__(self) raises -> Int: + """Returns the length of the object. + + Returns: + The length of the object. + """ + var cpython = _get_global_python_itf().cpython() + var result = cpython.PyObject_Length(self.py_object) + if result == -1: + # TODO: Improve error message so we say + # "object of type 'int' has no len()" function to match Python + raise Error("object has no len()") + return result + + fn __hash__(self) -> UInt: + """Returns the length of the object. + + Returns: + The length of the object. + """ + var cpython = _get_global_python_itf().cpython() + var result = cpython.PyObject_Length(self.py_object) + # TODO: make this function raise when we can raise parametrically. + debug_assert(result != -1, "object is not hashable") + return result + + fn __hash__[H: _Hasher](self, inout hasher: H): + """Updates hasher with this python object hash value. + + Parameters: + H: The hasher type. + + Args: + hasher: The hasher instance. + """ + var cpython = _get_global_python_itf().cpython() + var result = cpython.PyObject_Hash(self.py_object) + # TODO: make this function raise when we can raise parametrically. + debug_assert(result != -1, "object is not hashable") + hasher.update(result) + fn __index__(self) -> Int: """Returns an index representation of the object. @@ -1474,26 +1448,6 @@ struct PythonObject( """ return self.__float__() - fn unsafe_get_as_pointer[type: DType](self) -> UnsafePointer[Scalar[type]]: - """Convert a Python-owned and managed pointer into a Mojo pointer. - - Warning: converting from an integer to a pointer is unsafe! The - compiler assumes the resulting pointer DOES NOT alias any Mojo-derived - pointer. This is OK because the pointer originates from Python. - - Parameters: - type: The desired DType of the pointer. - - Returns: - An `UnsafePointer` for the underlying Python data. - """ - var tmp = int(self) - var result = UnsafePointer.address_of(tmp).bitcast[ - UnsafePointer[Scalar[type]] - ]()[] - _ = tmp - return result - fn __str__(self) -> String: """Returns a string representation of the object. @@ -1526,6 +1480,56 @@ struct PythonObject( # TODO: Avoid this intermediate String allocation, if possible. writer.write(str(self)) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn unsafe_as_py_object_ptr(self) -> PyObjectPtr: + """Get the underlying PyObject pointer. + + Returns: + The underlying PyObject pointer. + + Safety: + Use-after-free: The caller must take care that `self` outlives the + usage of the pointer returned by this function. + """ + return self.py_object + + fn steal_data(owned self) -> PyObjectPtr: + """Take ownership of the underlying pointer from the Python object. + + Returns: + The underlying data. + """ + var ptr = self.py_object + self.py_object = PyObjectPtr() + + return ptr + + fn unsafe_get_as_pointer[type: DType](self) -> UnsafePointer[Scalar[type]]: + """Convert a Python-owned and managed pointer into a Mojo pointer. + + Warning: converting from an integer to a pointer is unsafe! The + compiler assumes the resulting pointer DOES NOT alias any Mojo-derived + pointer. This is OK because the pointer originates from Python. + + Parameters: + type: The desired DType of the pointer. + + Returns: + An `UnsafePointer` for the underlying Python data. + """ + var tmp = int(self) + var result = UnsafePointer.address_of(tmp).bitcast[ + UnsafePointer[Scalar[type]] + ]()[] + _ = tmp + return result + + fn _get_ptr_as_int(self) -> Int: + return self.py_object._get_ptr_as_int() + # ===-----------------------------------------------------------------------===# # Helper functions From 8c2fbb10a741fcaff459451076fbb8b3645388a9 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 25 Oct 2024 21:04:07 -0700 Subject: [PATCH 1804/2019] [******] Delete unuseful broken vectorize benchmark (#49886) MODULAR_ORIG_COMMIT_REV_ID: 17aa3b2e69c56e6807c10f2263232c6d178adbe7 --- .../benchmarks/algorithm/bench_vectorize.mojo | 375 ------------------ 1 file changed, 375 deletions(-) delete mode 100644 stdlib/benchmarks/algorithm/bench_vectorize.mojo diff --git a/stdlib/benchmarks/algorithm/bench_vectorize.mojo b/stdlib/benchmarks/algorithm/bench_vectorize.mojo deleted file mode 100644 index 6d6f59bf2f..0000000000 --- a/stdlib/benchmarks/algorithm/bench_vectorize.mojo +++ /dev/null @@ -1,375 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# ===----------------------------------------------------------------------=== # -# -# Benchmarking performance of vectorize over basic operations -# -# ===----------------------------------------------------------------------=== # - -# RUN: %mojo-no-debug %s -t | FileCheck %s -# CHECK: Benchmark results - -from random import rand -from sys import simdwidthof - -from algorithm import vectorize -from benchmark import ( - Bench, - Bencher, - BenchId, - BenchMetric, - ThroughputMeasure, - Unit, - keep, - run, -) -from buffer import Buffer -from memory import UnsafePointer, memset_zero - - -@value -struct Op(Stringable): - var op_code: Int - alias add = 0 - alias sub = 1 - alias mul = 2 - alias div = 3 - alias fma = 4 - alias ld = 5 - alias st = 6 - - @always_inline - fn __eq__(self, other: Op) -> Bool: - return self.op_code == other.op_code - - @always_inline - fn __str__(self) -> String: - var op_list = List[String]( - "add", "sub", "mul", "div", "fma", "ld", "st" - ) - return "op." + op_list[self.op_code] - - -fn test_vectorize[ - N: Int, - simd_width: Int = 1, - op: Op = Op.add, - const_operand: Bool = True, - dtype: DType = DType.float32, - unroll_factor: Int = 1, -](inout m: Bench) raises: - constrained[(N % simd_width) == 0]() - # Create a mem of size N - alias buffer_align = 64 - var vector = UnsafePointer[Scalar[dtype], alignment=buffer_align].alloc(N) - var result = UnsafePointer[Scalar[dtype], alignment=buffer_align].alloc(N) - - @always_inline - @parameter - fn ld_vector[simd_width: Int](idx: Int): - vector.store(idx + 1, SIMD[dtype, simd_width](idx)) - - @always_inline - @parameter - fn st_vector[simd_width: Int](idx: Int): - result.store(idx, vector.load[width=simd_width](idx)) - - @__copy_capture(vector) - @always_inline - @parameter - fn arithmetic_const[simd_width: Int](idx: Int): - alias x: Scalar[dtype] = 2 - alias y: Scalar[dtype] = 3 - - @parameter - if op == Op.add: - vector.store(idx, vector.load[width=simd_width](idx) + x) - elif op == Op.sub: - vector.store(idx, vector.load[width=simd_width](idx) - x) - elif op == Op.mul: - vector.store(idx, vector.load[width=simd_width](idx) * x) - elif op == Op.div: - vector.store(idx, vector.load[width=simd_width](idx) / x) - elif op == Op.fma: - vector.store(idx, vector.load[width=simd_width](idx) * x + y) - - @__copy_capture(vector) - @always_inline - @parameter - fn arithmetic_vector[simd_width: Int](idx: Int): - @parameter - if op == Op.add: - vector.store( - idx, - vector.load[width=simd_width](idx) - + vector.load[width=simd_width](idx), - ) - elif op == Op.sub: - vector.store( - idx, - vector.load[width=simd_width](idx) - - vector.load[width=simd_width](idx), - ) - elif op == Op.mul: - vector.store( - idx, - vector.load[width=simd_width](idx) - * vector.load[width=simd_width](idx), - ) - elif op == Op.div: - vector.store( - idx, - vector.load[width=simd_width](idx) - / vector.load[width=simd_width](idx), - ) - elif op == Op.fma: - vector.store( - idx, - vector.load[width=simd_width](idx) - * vector.load[width=simd_width](idx) - + vector.load[width=simd_width](idx), - ) - - @always_inline - @parameter - fn bench_(inout b: Bencher): - @always_inline - @parameter - fn call_fn(): - @parameter - if op == Op.ld: - vectorize[ld_vector, simd_width, unroll_factor=unroll_factor](N) - elif op == Op.st: - vectorize[st_vector, simd_width, unroll_factor=unroll_factor](N) - else: - - @parameter - if const_operand: - vectorize[ - arithmetic_const, - simd_width, - unroll_factor=unroll_factor, - ](N) - else: - vectorize[ - arithmetic_vector, - simd_width, - unroll_factor=unroll_factor, - ](N) - keep(vector) - - b.iter[call_fn]() - - var bench_id = BenchId( - str(op) - + ", N=" - + str(N) - + ", simd=" - + str(simd_width) - + ", const_operand=" - + str(const_operand) - + ", dtype=" - + str(dtype) - + ", unroll_factor=" - + str(unroll_factor) - ) - - m.bench_function[bench_]( - bench_id, ThroughputMeasure(BenchMetric.elements, N) - ) - vector.free() - result.free() - - -# TODO: move this function to a common module for benchmarking. -@always_inline -fn unroll_nested_call[ - func: fn[List[Int]] () raises capturing [_] -> None, - count: List[Int], # TODO: a better datatype to use? e.g., Dim? - loop_idx: Int = 0, - index_prev: List[Int] = List[Int](), -]() raises: - """Fully unroll a nested loop of depth `depth` and call function `func` - at the innermost loop with a List of indices from all levels as arguments. - - for loop_idx0 in range(count[0]): - for loop_idx1 in range(count[1]): - for loop_idx2 in range(count[2]): - func(List[loop_idx0, loop_idx1, loop_idx2]) - - Params: - - func: function to call at the innermost loop. - - count: List[Int] contains the total count of iterations for each loop, - outmost loop is at index=0, inner-most loop at index=depth-1 - - loop_idx: index of the current loop - - index_prev: List[Int] of all indices from outer loops. - """ - alias depth = len(count) - - @always_inline - @parameter - fn append_index(x: List[Int], y: Int) -> List[Int]: - var z = x - z.append(y) - return z - - @parameter - for i in range(count[loop_idx]): - alias index = append_index(index_prev, i) - - @parameter - if loop_idx < depth - 1: - unroll_nested_call[func, count, loop_idx + 1, index]() - else: - func[index]() - - -fn bench_compare() raises: - alias type = DType.uint8 - alias width = simdwidthof[type]() - alias unit = Unit.ns - # increasing will reduce the benefit of passing the size as a parameter - alias multiplier = 2 - # Add .5 of the elements that fit into a simd register - alias size: Int = int(multiplier * width + (width * 0.5)) - alias unroll_factor = 2 - alias its = 1000 - - var p1 = UnsafePointer[Scalar[type]].alloc(size) - var p2 = UnsafePointer[Scalar[type]].alloc(size) - print("Benchmark results") - rand(p1, size) - - @parameter - fn arg_size(): - @parameter - fn closure[width: Int](i: Int): - p2.store( - i, - p1.load[width=width](i) + p2.load[width=width](i), - ) - - for i in range(its): - vectorize[closure, width](size) - - @parameter - fn param_size(): - @parameter - fn closure[width: Int](i: Int): - p2.store( - i, - p1.load[width=width](i) + p2.load[width=width](i), - ) - - for i in range(its): - vectorize[closure, width, size=size]() - - @parameter - fn arg_size_unroll(): - @parameter - fn closure[width: Int](i: Int): - p2.store( - i, - p1.load[width=width](i) + p2.load[width=width](i), - ) - - for i in range(its): - vectorize[closure, width, unroll_factor=unroll_factor](size) - - @parameter - fn param_size_unroll(): - @parameter - fn closure[width: Int](i: Int): - p2.store( - i, - p1.load[width=width](i) + p2.load[width=width](i), - ) - - for i in range(its): - vectorize[closure, width, size=size, unroll_factor=unroll_factor]() - - var arg = run[arg_size](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=width]()) - memset_zero(p2, size) - - var param = run[param_size](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=width]()) - memset_zero(p2, size) - - var arg_unroll = run[arg_size_unroll](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=width]()) - memset_zero(p2, size) - - var param_unroll = run[param_size_unroll](max_runtime_secs=0.5).mean(unit) - print(p2.load[width=width]()) - - print( - "calculating", - size, - "elements,", - width, - "elements fit into the SIMD register\n", - ) - - print(" size as argument:", arg, unit) - print(" unrolled:", arg_unroll, unit) - print() - print("size as parameter:", param, unit) - print(" unrolled:", param_unroll, unit) - print( - "\nPassing size as a parameter and unrolling is", - arg_unroll / param_unroll, - "x faster", - ) - p1.free() - p2.free() - - -fn main() raises: - alias vec_size_list = List[Int](512, 2048) - alias simd_width_list = List[Int](1, 2, 4, 8) - alias op_list = List[Op](Op.add, Op.mul, Op.fma, Op.ld) - alias const_operand_list = List[Bool](True) - alias dtype_list = List[DType](DType.float32) - alias unroll_factor_list = List[Int](1, 2, 4) - - var m = Bench() - - @always_inline - @parameter - fn callback[index: List[Int]]() raises: - @parameter - if len(index) == 6: - alias vec_size = vec_size_list[index[0]] - alias simd_width = simd_width_list[index[1]] - alias op_code = op_list[index[2]] - alias const_op = const_operand_list[index[3]] - alias dtype = dtype_list[index[4]] - alias unroll_factor = unroll_factor_list[index[5]] - test_vectorize[ - vec_size, simd_width, op_code, const_op, dtype, unroll_factor - ](m) - - unroll_nested_call[ - callback, - List[Int]( - len(vec_size_list), - len(simd_width_list), - len(op_list), - len(const_operand_list), - len(dtype_list), - len(unroll_factor_list), - ), - ]() - - m.dump_report() From 94f9623b71a76a19b599f349bf21fb2e46c860be Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 25 Oct 2024 22:00:08 -0700 Subject: [PATCH 1805/2019] [******][GPU] Expose the element_type in IndexList, NFC (#49814) MODULAR_ORIG_COMMIT_REV_ID: c25c7be059ec42d8017660b41d8420fb895787c2 --- stdlib/src/utils/index.mojo | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index bca9b80af4..5fc3d05740 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -80,7 +80,7 @@ fn _int_tuple_binary_apply[ for i in range(a.size): var a_elem = a.__getitem__[i]() var b_elem = b.__getitem__[i]() - c.__setitem__[i](binary_fn[a._int_dtype](a_elem, b_elem)) + c.__setitem__[i](binary_fn[a.element_type](a_elem, b_elem)) return c @@ -111,7 +111,7 @@ fn _int_tuple_compare[ for i in range(a.size): var a_elem = a.__getitem__[i]() var b_elem = b.__getitem__[i]() - c.__setitem__[i](comp_fn[a._int_dtype](a_elem, b_elem)) + c.__setitem__[i](comp_fn[a.element_type](a_elem, b_elem)) return c @@ -185,10 +185,10 @@ struct IndexList[ unsigned: Whether the integer is signed or unsigned. """ - alias _int_dtype = _type_of_width[element_bitwidth, unsigned]() + alias element_type = _type_of_width[element_bitwidth, unsigned]() """The underlying dtype of the integer element value.""" - alias _int_type = Scalar[Self._int_dtype] + alias _int_type = Scalar[Self.element_type] """The underlying storage of the integer element value.""" var data: StaticTuple[Self._int_type, size] @@ -775,9 +775,9 @@ struct IndexList[ @parameter for i in range(size): res.data[i] = rebind[__type_of(result.data).element_type]( - rebind[Scalar[Self._int_dtype]]( + rebind[Scalar[Self.element_type]]( self.data.__getitem__[i]() - ).cast[result._int_dtype]() + ).cast[result.element_type]() ) return res From e52483224bf0d1ebc5e2f37d1a79b408e62867fe Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 28 Oct 2024 12:05:59 -0700 Subject: [PATCH 1806/2019] This avoids having to convert `IntLike` SIMD types to an `Int` before using them as an index. This doesn't add any overhead compared to using an Int or UInt, and performs the same as the equivalent C using types such as `uint8_t`. MODULAR_ORIG_COMMIT_REV_ID: bda32f87058bf7dd808438ae5c60c485b5d41de9 --- stdlib/src/builtin/int.mojo | 3 +-- stdlib/src/builtin/simd.mojo | 16 ++++++++++++--- stdlib/test/memory/test_unsafepointer.mojo | 24 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 4aa42044fb..62b8e3436a 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -85,7 +85,7 @@ fn index[T: Indexer](idx: T, /) -> Int: # ===----------------------------------------------------------------------=== # -trait Intable: +trait Intable(CollectionElement): """The `Intable` trait describes a type that can be converted to an Int. Any type that conforms to `Intable` or @@ -178,7 +178,6 @@ trait IntableRaising: trait IntLike( Absable, Ceilable, - Comparable, Floorable, Writable, Powable, diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 83e12c56ee..f67ba542be 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -179,12 +179,10 @@ struct SIMD[type: DType, size: Int]( Hashable, _HashableWithHasher, Intable, - Powable, + IntLike, Representable, Roundable, Sized, - Stringable, - Truncable, ): """Represents a small vector that is backed by a hardware vector element. @@ -1400,6 +1398,18 @@ struct SIMD[type: DType, size: Int]( _type = __mlir_type.`!pop.scalar` ](rebind[Scalar[type]](self).value) + @always_inline("nodebug") + fn __mlir_index__(self) -> __mlir_type.index: + """Convert to index. + + Returns: + The corresponding __mlir_type.index value. + """ + constrained[ + type.is_integral(), "cannot index using a floating point type" + ]() + return int(self).value + @always_inline("nodebug") fn __float__(self) -> Float64: """Casts the value to a float. diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 478207ec36..f7573c1941 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -227,6 +227,29 @@ def test_indexing(): assert_equal(ptr[3], 3) +def test_indexing_simd(): + var ptr = UnsafePointer[Int].alloc(4) + for i in range(4): + ptr[UInt8(i)] = i + + assert_equal(ptr[UInt8(1)], 1) + assert_equal(ptr[UInt8(3)], 3) + assert_equal(ptr[UInt16(1)], 1) + assert_equal(ptr[UInt16(3)], 3) + assert_equal(ptr[UInt32(1)], 1) + assert_equal(ptr[UInt32(3)], 3) + assert_equal(ptr[UInt64(1)], 1) + assert_equal(ptr[UInt64(3)], 3) + assert_equal(ptr[Int8(1)], 1) + assert_equal(ptr[Int8(3)], 3) + assert_equal(ptr[Int16(1)], 1) + assert_equal(ptr[Int16(3)], 3) + assert_equal(ptr[Int32(1)], 1) + assert_equal(ptr[Int32(3)], 3) + assert_equal(ptr[Int64(1)], 1) + assert_equal(ptr[Int64(3)], 3) + + def test_bool(): var nullptr = UnsafePointer[Int]() var ptr = UnsafePointer[Int].alloc(1) @@ -319,6 +342,7 @@ def main(): test_unsafepointer_address_space() test_indexing() + test_indexing_simd() test_bool() test_alignment() test_offset() From 97ec941260e4473d65531b206643ca9ba910a689 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 28 Oct 2024 13:34:42 -0700 Subject: [PATCH 1807/2019] [stdlib] Add `__mul__` and `__iadd__` to StringLiteral Allows you to multiply and in-place add a StringLiteral, but only at compile time using the `alias` keyword. It's not possible to generate a `StringLiteral` at runtime as it must be written into the binary. MODULAR_ORIG_COMMIT_REV_ID: 1edabbc526af734d7878bf7a21ecb6e5c85a8828 --- docs/changelog.md | 29 +++++++++ stdlib/src/builtin/string_literal.mojo | 62 ++++++++++++++++++++ stdlib/test/builtin/test_string_literal.mojo | 22 +++++++ 3 files changed, 113 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 87aa2d95d7..750638216f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -361,6 +361,35 @@ what we publish. for more information and rationale. As a consequence `__lifetime_of` is now named `__origin_of`. +- You can now use the `+=` and `*` operators on a `StringLiteral` at compile + time using the `alias` keyword: + + ```mojo + alias original = "mojo" + alias concat = original * 3 + assert_equal("mojomojomojo", concat) + ``` + + Or inside a `fn` that is being evaluated at compile time: + + ```mojo + fn add_literal( + owned original: StringLiteral, add: StringLiteral, n: Int + ) -> StringLiteral: + for _ in range(n): + original += add + return original + + + fn main(): + alias original = "mojo" + alias concat = add_literal(original, "!", 4) + assert_equal("mojo!!!!", concat) + ``` + + These operators can't be evaluated at runtime, as a `StringLiteral` must be + written into the binary during compilation. + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 5edcd74b20..86e6ff7c5a 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -102,6 +102,68 @@ struct StringLiteral( """ return __mlir_op.`pop.string.concat`(self.value, rhs.value) + @always_inline("nodebug") + fn __iadd__(inout self, rhs: StringLiteral): + """Concatenate a string literal to an existing one. Can only be + evaluated at compile time using the `alias` keyword, which will write + the result into the binary. + + Args: + rhs: The string to concat. + + Example: + + ```mojo + fn add_literal( + owned original: StringLiteral, add: StringLiteral, n: Int + ) -> StringLiteral: + for _ in range(n): + original += add + return original + + + fn main(): + alias original = "mojo" + alias concat = add_literal(original, "!", 4) + print(concat) + ``` + + Result: + + ``` + mojo!!!! + ``` + """ + self = self + rhs + + @always_inline("nodebug") + fn __mul__(self, n: Int) -> StringLiteral: + """Concatenates the string literal `n` times. Can only be evaluated at + compile time using the `alias` keyword, which will write the result into + The binary. + + Args: + n : The number of times to concatenate the string literal. + + Returns: + The string concatenated `n` times. + + Example: + + ```mojo + alias original = "mojo" + alias concat = original * 3 + print(concat) + ``` + + `concat` now points to the StringLiteral "mojomojomojo", which is + written into the binary. + """ + var concat = "" + for _ in range(n): + concat += self + return concat + @always_inline("nodebug") fn __eq__(self, rhs: StringLiteral) -> Bool: """Compare two string literals for equality. diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index f6b3eaf455..471486b5d3 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -30,6 +30,26 @@ def test_add(): assert_equal("fivesix", StringLiteral.__add__("five", "six")) +fn add_literal( + owned original: StringLiteral, add: StringLiteral, n: Int +) -> StringLiteral: + for _ in range(n): + original += add + return original + + +def test_iadd(): + alias original = "mojo" + alias concat = add_literal(original, "!", 4) + assert_equal("mojo!!!!", concat) + + +def test_mul(): + alias original = "mojo" + alias concat = original * 3 + assert_equal("mojomojomojo", concat) + + def test_equality(): assert_false(StringLiteral.__eq__("five", "six")) assert_true(StringLiteral.__eq__("six", "six")) @@ -446,6 +466,8 @@ def test_float_conversion(): def main(): test_add() + test_iadd() + test_mul() test_equality() test_len() test_bool() From f1b1e1a72e79040e59ef9e8506ec9eaeb6218327 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 28 Oct 2024 18:25:30 -0700 Subject: [PATCH 1808/2019] [docs] Update changelog for indexing into UnsafePointer with SIMD MODULAR_ORIG_COMMIT_REV_ID: 2c96fdee5868aa23c9e68de135c380e1f72ada48 --- docs/changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 750638216f..9e30a7839f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -390,6 +390,15 @@ what we publish. These operators can't be evaluated at runtime, as a `StringLiteral` must be written into the binary during compilation. + - You can now index into `UnsafePointer` using SIMD scalar integral types: + + ```mojo + p = UnsafePointer[Int].alloc(1) + i = UInt8(1) + p[i] = 42 + print(p[i]) + ``` + ### ❌ Removed ### 🛠️ Fixed From 783ad3118628d6073dc28c939479e6e459e61bc7 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:39:54 -0600 Subject: [PATCH 1809/2019] [External] [stdlib] Refactor and optimize `String.splitlines()` (#49663) [External] [stdlib] Refactor and optimize `String.splitlines()` Refactor and optimize `String.splitlines()`, add method `isnewline()` to `StringSlice` only since it is not a Python API but very necessary when going over string views to get better perf. Added a method `_to_string_list()` which is very fast in allocating list of strings from either `List[Span[Byte]]` or `List[StringSlice]`. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3697 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3697 MODULAR_ORIG_COMMIT_REV_ID: cb7604feb033289021057eaf14f5a2a2ee62bd84 --- stdlib/benchmarks/README.md | 20 +- .../algorithm/bench_elementwise.mojo | 2 + stdlib/benchmarks/builtin/bench_int.mojo | 2 + stdlib/benchmarks/builtin/bench_sort.mojo | 3 +- stdlib/benchmarks/collections/bench_dict.mojo | 51 +-- .../benchmarks/collections/bench_string.mojo | 61 ++-- stdlib/benchmarks/hashlib/bench_hash.mojo | 3 +- stdlib/benchmarks/lit.cfg.py | 32 +- stdlib/benchmarks/math/bench_math.mojo | 2 + stdlib/benchmarks/utils/bench_formatter.mojo | 3 +- stdlib/benchmarks/utils/bench_memmem.mojo | 3 +- stdlib/scripts/run-benchmarks.sh | 4 +- stdlib/src/builtin/string_literal.mojo | 9 +- stdlib/src/collections/string.mojo | 15 +- stdlib/src/utils/_utf8_validation.mojo | 8 +- stdlib/src/utils/string_slice.mojo | 302 +++++++++++++----- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/builtin/test_string_literal.mojo | 82 ++--- stdlib/test/collections/test_string.mojo | 117 +++---- stdlib/test/utils/test_string_slice.mojo | 87 ++++- 20 files changed, 470 insertions(+), 338 deletions(-) diff --git a/stdlib/benchmarks/README.md b/stdlib/benchmarks/README.md index dbae23362f..54c94807f5 100644 --- a/stdlib/benchmarks/README.md +++ b/stdlib/benchmarks/README.md @@ -20,16 +20,16 @@ internally. If you want to just compile and run all of the benchmarks as-is, there is a script provided [here](../../stdlib/scripts/run-benchmarks.sh). This script builds the open source `stdlib.mojopkg` and then executes -all of the benchmarks. Note that, by default, the executor (`llvm-lit`) -will run all of these benchmarks in parallel. That is not terribly useful -in practice for doing performance comparisons. When doing A-B comparisons, -I recommend just rebuilding the `stdlib.mojopkg` if needed using -the [build-stdlib script](../../stdlib/scripts/build-stdlib.sh) and then -just running `mojo` directly for the individual benchmark file. For example, -`mojo stdlib/benchmarks/collections/bench_dict.mojo` as run from the root -of the repo. As part of the benchmark configs, there is a mechanism -for specifying the number of repetitions to run the benchmark, warmup iterations, -and other things. +all of the benchmarks sequentially. The script also allows specifying a +subdirectory or a file to run. + +Running e.g. `magic run mojo run stdlib/benchmarks/collections/bench_dict.mojo` +makes the linker use the existing branch that the compiler is on. If you wish to +test changes you are making on the current branch, remove the `-t` flag on top +of the benchmark file (`# RUN: %mojo-no-debug %s -t`) then run: +`magic run stdlib/scripts/run-benchmarks.sh +stdlib/benchmarks/collections/bench_dict.mojo`. +Remember to replace the `-t` flag again before pushing any code. ## How to write effective benchmarks diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index f7e753efbd..42ab4732f0 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from sys import simdwidthof from algorithm import elementwise diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index 39aee76006..5a469936f8 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from benchmark import Bench, BenchConfig, Bencher, BenchId diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 74c7c101b3..a2a5ebc4cf 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -10,8 +10,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # - # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run from memory import UnsafePointer diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index 91c18e9aeb..d0d977d63e 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from random import * @@ -125,54 +127,7 @@ def main(): seed() var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_dict_init](BenchId("bench_dict_init")) - alias sizes = ( - 10, - 20, - 30, - 40, - 50, - 60, - 70, - 80, - 90, - 100, - 200, - 300, - 400, - 500, - 600, - 700, - 800, - 900, - 1000, - 2000, - 3000, - 4000, - 5000, - 6000, - 7000, - 8000, - 9000, - 10_000, - 20_000, - 30_000, - 40_000, - 50_000, - 60_000, - 70_000, - 80_000, - 90_000, - 100_000, - 200_000, - 300_000, - 400_000, - 500_000, - 600_000, - 700_000, - 800_000, - 900_000, - 1_000_000, - ) + alias sizes = (10, 30, 50, 100, 1000, 10_000, 100_000, 1_000_000) @parameter for i in range(len(sizes)): diff --git a/stdlib/benchmarks/collections/bench_string.mojo b/stdlib/benchmarks/collections/bench_string.mojo index f824a0d5f4..be397e082e 100644 --- a/stdlib/benchmarks/collections/bench_string.mojo +++ b/stdlib/benchmarks/collections/bench_string.mojo @@ -11,13 +11,16 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from random import random_si64, seed from pathlib import _dir_of_current_file -from collections import Optional -from utils._utf8_validation import _is_valid_utf8 +from collections import Optional, Dict from os import abort +from collections.string import String +from utils._utf8_validation import _is_valid_utf8 # ===----------------------------------------------------------------------===# @@ -26,8 +29,7 @@ from os import abort fn make_string[ length: UInt = 0 ](filename: StringLiteral = "UN_charter_EN.txt") -> String: - """Make a `String` made of items in the `./data` directory or random bytes - (ASCII value range) in case opening the file fails. + """Make a `String` made of items in the `./data` directory. Parameters: length: The length in bytes of the resulting `String`. If == 0 -> the @@ -53,7 +55,7 @@ fn make_string[ else: return String(f.read_bytes()) except e: - print(e) + print(e, file=2) return abort[String]() @@ -211,7 +213,7 @@ fn bench_string_is_valid_utf8[ @always_inline @parameter fn call_fn() raises: - var res = _is_valid_utf8(items.unsafe_ptr(), length) + var res = _is_valid_utf8(items.as_bytes()) keep(res) b.iter[call_fn]() @@ -223,8 +225,7 @@ fn bench_string_is_valid_utf8[ # ===----------------------------------------------------------------------===# def main(): seed() - var m = Bench(BenchConfig(num_repetitions=1)) - m.bench_function[bench_string_init](BenchId("bench_string_init")) + var m = Bench(BenchConfig(num_repetitions=5)) alias filenames = ( "UN_charter_EN", "UN_charter_ES", @@ -235,6 +236,20 @@ def main(): alias old_chars = ("a", "ó", "ل", "и", "一") alias new_chars = ("A", "Ó", "ل", "И", "一") alias lengths = (10, 30, 50, 100, 1000, 10_000, 100_000, 1_000_000) + """At an average 5 letters per word and 300 words per page + (in the English language): + + - 10: 2 words + - 30: 6 words + - 50: 10 words + - 100: 20 words + - 1000: ~ 1/2 page (200 words) + - 10_000: ~ 7 pages (2k words) + - 100_000: ~ 67 pages (20k words) + - 1_000_000: ~ 667 pages (200k words) + """ + + m.bench_function[bench_string_init](BenchId("bench_string_init")) @parameter for i in range(len(lengths)): @@ -245,30 +260,38 @@ def main(): alias fname = filenames.get[j, StringLiteral]() alias old = old_chars.get[j, StringLiteral]() alias new = new_chars.get[j, StringLiteral]() + suffix = "[" + str(length) + "]" # "(" + fname + ")" m.bench_function[bench_string_count[length, fname, old]]( - BenchId("bench_string_count[" + str(length) + "]") + BenchId("bench_string_count" + suffix) ) m.bench_function[bench_string_split[length, fname, old]]( - BenchId("bench_string_split[" + str(length) + "]") + BenchId("bench_string_split" + suffix) ) m.bench_function[bench_string_split[length, fname]]( - BenchId( - "bench_string_split[" + str(length) + ", sequence=None]" - ) + BenchId("bench_string_split_none" + suffix) ) m.bench_function[bench_string_splitlines[length, fname]]( - BenchId("bench_string_splitlines[" + str(length) + "]") + BenchId("bench_string_splitlines" + suffix) ) m.bench_function[bench_string_lower[length, fname]]( - BenchId("bench_string_lower[" + str(length) + "]") + BenchId("bench_string_lower" + suffix) ) m.bench_function[bench_string_upper[length, fname]]( - BenchId("bench_string_upper[" + str(length) + "]") + BenchId("bench_string_upper" + suffix) ) m.bench_function[bench_string_replace[length, fname, old, new]]( - BenchId("bench_string_replace[" + str(length) + "]") + BenchId("bench_string_replace" + suffix) ) m.bench_function[bench_string_is_valid_utf8[length, fname]]( - BenchId("bench_string_is_valid_utf8[" + str(length) + "]") + BenchId("bench_string_is_valid_utf8" + suffix) ) - m.dump_report() + + results = Dict[String, (Float64, Int)]() + for info in m.info_vec: + n = info[].name + time = info[].result.mean("ms") + avg, amnt = results.get(n, (Float64(0), 0)) + results[n] = ((avg * amnt + time) / (amnt + 1), amnt + 1) + print("") + for k_v in results.items(): + print(k_v[].key, k_v[].value[0], sep=",") diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index d672da085d..1f4f93c0cc 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -10,8 +10,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # - # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from benchmark import Bench, BenchConfig, Bencher, BenchId from bit import byte_swap, rotate_bits_left diff --git a/stdlib/benchmarks/lit.cfg.py b/stdlib/benchmarks/lit.cfg.py index 90dd84838f..304a5521d9 100644 --- a/stdlib/benchmarks/lit.cfg.py +++ b/stdlib/benchmarks/lit.cfg.py @@ -39,38 +39,38 @@ config.modular_obj_root, "open-source", "mojo", "stdlib", "benchmarks" ) else: + # test_source_root: The root path where tests are located. + config.test_source_root = Path(__file__).parent.resolve() + + repo_root = Path(__file__).parent.parent.parent + # This is important since `benchmark` is closed source # still right now and is always used by the benchmarks. pre_built_packages_path = os.environ.get( "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", - Path(os.environ["MODULAR_HOME"]) - / "pkg" - / "packages.modular.com_nightly_mojo" - / "lib" - / "mojo", + os.environ.get( + "MODULAR_MOJO_IMPORT_PATH", + repo_root / ".magic" / "envs" / "default" / "lib" / "mojo", + ), ) - # test_source_root: The root path where tests are located. - config.test_source_root = Path(__file__).parent.resolve() - # The `run-tests.sh` script creates the build directory for you. - build_root = Path(__file__).parent.parent.parent / "build" + build_root = repo_root / "build" # The tests are executed inside this build directory to avoid # polluting the source tree. - config.test_exec_root = build_root / "stdlib" / "benchmarks" + config.test_exec_root = (build_root / "stdlib" / "benchmarks").resolve() # Add both the open source, locally built `stdlib.mojopkg` # along with the closed source, pre-built packages shipped # with the Mojo SDK to the appropriate environment variables. # These environment variables are interpreted by the mojo parser # when resolving imports. - os.environ[ - "MODULAR_MOJO_NIGHTLY_IMPORT_PATH" - ] = f"{build_root},{pre_built_packages_path}" - os.environ[ - "MODULAR_MOJO_MAX_NIGHTLY_IMPORT_PATH" - ] = f"{build_root},{pre_built_packages_path}" + joint_path = f"{build_root.resolve()},{pre_built_packages_path.resolve()}" + os.environ["MODULAR_MOJO_NIGHTLY_IMPORT_PATH"] = joint_path + os.environ["MODULAR_MOJO_MAX_NIGHTLY_IMPORT_PATH"] = joint_path + os.environ["MODULAR_MOJO_IMPORT_PATH"] = joint_path + os.environ["MODULAR_MOJO_MAX_IMPORT_PATH"] = joint_path # Pass through several environment variables # to the underlying subprocesses that run the tests. diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index 9a62d9aba2..d2e5de20c0 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -11,6 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from math import * from random import * diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 6797c268b7..83c4c44e82 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -10,8 +10,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # - # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 6d4dea9576..d72a4bddb1 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -10,8 +10,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # - # RUN: %mojo-no-debug %s -t +# NOTE: to test changes on the current branch using run-benchmarks.sh, remove +# the -t flag. Remember to replace it again before pushing any code. from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run diff --git a/stdlib/scripts/run-benchmarks.sh b/stdlib/scripts/run-benchmarks.sh index b6525af2d6..cdf265ce21 100755 --- a/stdlib/scripts/run-benchmarks.sh +++ b/stdlib/scripts/run-benchmarks.sh @@ -31,5 +31,5 @@ if [[ $# -gt 0 ]]; then BENCHMARK_PATH=$1 fi -# Run the benchmarks -lit -sv "${BENCHMARK_PATH}" +# Run the benchmarks sequentially +lit --succinct --show-all --workers 1 ${BENCHMARK_PATH} \ No newline at end of file diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 86e6ff7c5a..4c9974e6a4 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -28,6 +28,7 @@ from utils.string_slice import ( _StringSliceIter, _FormatCurlyEntry, _CurlyEntryFormattable, + _to_string_list, ) # ===----------------------------------------------------------------------===# @@ -615,7 +616,7 @@ struct StringLiteral( # Splitting a string with leading, trailing, and middle whitespaces _ = " hello world ".split() # ["hello", "world"] # Splitting adjacent universal newlines: - _ = "hello \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029world".split() + _ = "hello \\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029world".split() # ["hello", "world"] ``` . @@ -624,9 +625,9 @@ struct StringLiteral( fn splitlines(self, keepends: Bool = False) -> List[String]: """Split the string literal at line boundaries. This corresponds to Python's - [universal newlines]( + [universal newlines:]( https://docs.python.org/3/library/stdtypes.html#str.splitlines) - `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + `"\\r\\n"` and `"\\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Args: keepends: If True, line breaks are kept in the resulting strings. @@ -634,7 +635,7 @@ struct StringLiteral( Returns: A List of Strings containing the input split by line boundaries. """ - return self.as_string_slice().splitlines(keepends) + return _to_string_list(self.as_string_slice().splitlines(keepends)) fn count(self, substr: String) -> Int: """Return the number of non-overlapping occurrences of substring diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 756a97d8a6..80792e2129 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -44,6 +44,7 @@ from utils.string_slice import ( _shift_unicode_to_utf8, _FormatCurlyEntry, _CurlyEntryFormattable, + _to_string_list, ) # ===----------------------------------------------------------------------=== # @@ -615,7 +616,7 @@ fn _isspace(c: String) -> Bool: """Determines whether the given character is a whitespace character. This only respects the default "C" locale, i.e. returns True only if the - character specified is one of " \\t\\n\\r\\f\\v". For semantics similar + character specified is one of " \\t\\n\\v\\f\\r". For semantics similar to Python, use `String.isspace()`. Args: @@ -631,7 +632,7 @@ fn _isspace(c: UInt8) -> Bool: """Determines whether the given character is a whitespace character. This only respects the default "C" locale, i.e. returns True only if the - character specified is one of " \\t\\n\\r\\f\\v". For semantics similar + character specified is one of " \\t\\n\\v\\f\\r". For semantics similar to Python, use `String.isspace()`. Args: @@ -1648,7 +1649,7 @@ struct String( python whitespace String. This corresponds to Python's [universal separators]( https://docs.python.org/3/library/stdtypes.html#str.splitlines) - `" \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + `" \\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Returns: True if the whole String is made up of whitespace characters @@ -1735,7 +1736,7 @@ struct String( _ = String(" hello world ").split() # ["hello", "world"] # Splitting adjacent universal newlines: _ = String( - "hello \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029world" + "hello \\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029world" ).split() # ["hello", "world"] ``` . @@ -1785,9 +1786,9 @@ struct String( fn splitlines(self, keepends: Bool = False) -> List[String]: """Split the string at line boundaries. This corresponds to Python's - [universal newlines]( + [universal newlines:]( https://docs.python.org/3/library/stdtypes.html#str.splitlines) - `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + `"\\r\\n"` and `"\\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Args: keepends: If True, line breaks are kept in the resulting strings. @@ -1795,7 +1796,7 @@ struct String( Returns: A List of Strings containing the input split by line boundaries. """ - return self.as_string_slice().splitlines(keepends) + return _to_string_list(self.as_string_slice().splitlines(keepends)) fn replace(self, old: String, new: String) -> String: """Return a copy of the string with all occurrences of substring `old` diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index 53c0a257df..a57521d7d7 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -132,12 +132,11 @@ fn validate_chunk[ return must23_as_80 ^ sc -fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: +fn _is_valid_utf8(span: Span[Byte]) -> Bool: """Verify that the bytes are valid UTF-8. Args: - ptr: The pointer to the data. - length: The length of the items pointed to. + span: The Span of bytes. Returns: Whether the data is valid UTF-8. @@ -158,6 +157,9 @@ fn _is_valid_utf8(ptr: UnsafePointer[UInt8], length: Int) -> Bool: U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF | U+100000..U+10FFFF | F4 | 80..***8F***| 80..BF | 80..BF | """ + + ptr = span.unsafe_ptr() + length = len(span) alias simd_size = sys.simdbytewidth() var i: Int = 0 var previous = SIMD[DType.uint8, simd_size]() diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 0456bdeb3a..54d72b4bd7 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -26,9 +26,11 @@ from bit import count_leading_zeros from utils import Span from collections.string import _isspace, _atol, _atof from collections import List, Optional -from memory import memcmp, UnsafePointer +from memory import memcmp, UnsafePointer, memcpy from sys import simdwidthof, bitwidthof +from sys.intrinsics import unlikely from memory.memory import _memcmp_impl_unconstrained +from ._utf8_validation import _is_valid_utf8 alias StaticString = StringSlice[StaticConstantOrigin] """An immutable static string slice.""" @@ -68,6 +70,22 @@ fn _unicode_codepoint_utf8_byte_length(c: Int) -> Int: return int((sizes < c).cast[DType.uint8]().reduce_add()) +@always_inline +fn _utf8_first_byte_sequence_length(b: Byte) -> Int: + """Get the length of the sequence starting with given byte. Do note that + this does not work correctly if given a continuation byte.""" + + debug_assert( + (b & 0b1100_0000) != 0b1000_0000, + ( + "Function `_utf8_first_byte_sequence_length()` does not work" + " correctly if given a continuation byte." + ), + ) + var flipped = ~b + return int(count_leading_zeros(flipped) + (flipped >> 7)) + + fn _shift_unicode_to_utf8(ptr: UnsafePointer[UInt8], c: Int, num_bytes: Int): """Shift unicode to utf8 representation. @@ -150,44 +168,6 @@ fn _memrmem[ return UnsafePointer[Scalar[type]]() -fn _is_newline_start( - ptr: UnsafePointer[UInt8], read_ahead: Int = 1 -) -> (Bool, Int): - """Returns if the first item in the pointer is the start of - a newline sequence, and its length. - """ - # TODO add line and paragraph separator as StringLiteral - # once Unicode escape sequences are accepted - alias ` ` = UInt8(ord(" ")) - var rn = "\r\n" - var next_line = List[UInt8](0xC2, 0x85) - """TODO: \\x85""" - var unicode_line_sep = List[UInt8](0xE2, 0x80, 0xA8) - """TODO: \\u2028""" - var unicode_paragraph_sep = List[UInt8](0xE2, 0x80, 0xA9) - """TODO: \\u2029""" - - var val = _utf8_byte_type(ptr[0]) - if val == 0: - if read_ahead > 1: - if memcmp(ptr, rn.unsafe_ptr(), 2) == 0: - return True, 2 - _ = rn - return ptr[0] != ` ` and _isspace(ptr[0]), 1 - elif val == 2 and read_ahead > 1: - var comp = memcmp(ptr, next_line.unsafe_ptr(), 2) == 0 - _ = next_line - return comp, 2 - elif val == 3 and read_ahead > 2: - var comp = ( - memcmp(ptr, unicode_line_sep.unsafe_ptr(), 3) == 0 - or memcmp(ptr, unicode_paragraph_sep.unsafe_ptr(), 3) == 0 - ) - _ = unicode_line_sep, unicode_paragraph_sep - return comp, 3 - return False, 1 - - @value struct _StringSliceIter[ is_mutable: Bool, //, @@ -288,9 +268,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( # ===------------------------------------------------------------------===# @always_inline - fn __init__( - inout self: StringSlice[StaticConstantOrigin], lit: StringLiteral - ): + fn __init__(inout self: StaticString, lit: StringLiteral): """Construct a new `StringSlice` from a `StringLiteral`. Args: @@ -303,14 +281,12 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( # StringLiteral is guaranteed to use UTF-8 encoding. # FIXME(MSTDL-160): # Ensure StringLiteral _actually_ always uses UTF-8 encoding. - # TODO(#933): use when llvm intrinsics can be used at compile time + # FIXME: this gets practically stuck at compile time # debug_assert( - # _is_valid_utf8(literal.unsafe_ptr(), literal.byte_length()), + # _is_valid_utf8(lit.as_bytes()), # "StringLiteral doesn't have valid UTF-8 encoding", # ) - self = StaticString( - unsafe_from_utf8_ptr=lit.unsafe_ptr(), len=lit.byte_length() - ) + self = StaticString(unsafe_from_utf8=lit.as_bytes()) @always_inline fn __init__(inout self, *, owned unsafe_from_utf8: Span[Byte, origin]): @@ -384,6 +360,23 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """ self._slice = other._slice + fn __init__[ + O: ImmutableOrigin, // + ](inout self: StringSlice[O], ref [O]value: String): + """Construct an immutable StringSlice. + + Parameters: + O: The immutable origin. + + Args: + value: The string value. + """ + + debug_assert( + _is_valid_utf8(value.as_bytes()), "value is not valid utf8" + ) + self = StringSlice[O](unsafe_from_utf8=value.as_bytes()) + # ===------------------------------------------------------------------===# # Trait implementations # ===------------------------------------------------------------------===# @@ -852,9 +845,9 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( fn isspace(self) -> Bool: """Determines whether every character in the given StringSlice is a python whitespace String. This corresponds to Python's - [universal separators]( + [universal separators:]( https://docs.python.org/3/library/stdtypes.html#str.splitlines) - `" \\t\\n\\r\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + `" \\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. Returns: True if the whole StringSlice is made up of whitespace characters @@ -892,11 +885,67 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( _ = next_line, unicode_line_sep, unicode_paragraph_sep return True - fn splitlines(self, keepends: Bool = False) -> List[String]: + fn isnewline[single_character: Bool = False](self) -> Bool: + """Determines whether every character in the given StringSlice is a + python newline character. This corresponds to Python's + [universal newlines:]( + https://docs.python.org/3/library/stdtypes.html#str.splitlines) + `"\\r\\n"` and `"\\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + + Parameters: + single_character: Whether to evaluate the stringslice as a single + unicode character (avoids overhead when already iterating). + + Returns: + True if the whole StringSlice is made up of whitespace characters + listed above, otherwise False. + """ + + fn _is_newline_char(s: StringSlice) -> Bool: + # sorry for readability, but this has less overhead than memcmp + # highly performance sensitive code, benchmark before touching + alias `\t` = UInt8(ord("\t")) + alias `\r` = UInt8(ord("\r")) + alias `\n` = UInt8(ord("\n")) + alias `\x1c` = UInt8(ord("\x1c")) + alias `\x1e` = UInt8(ord("\x1e")) + no_null_len = s.byte_length() + ptr = s.unsafe_ptr() + if no_null_len == 1: + v = ptr[0] + return `\t` <= v <= `\x1e` and not (`\r` < v < `\x1c`) + elif no_null_len == 2: + v0 = ptr[0] + v1 = ptr[1] + next_line = v0 == 0xC2 and v1 == 0x85 # next line: \x85 + r_n = v0 == `\r` and v1 == `\n` + return next_line or r_n + elif no_null_len == 3: + # unicode line sep or paragraph sep: \u2028 , \u2029 + v2 = ptr[2] + lastbyte = v2 == 0xA8 or v2 == 0xA9 + return ptr[0] == 0xE2 and ptr[1] == 0x80 and lastbyte + return False + + @parameter + if single_character: + return _is_newline_char(self) + else: + for s in self: + if not _is_newline_char(s): + return False + return self.byte_length() != 0 + + fn splitlines[ + O: ImmutableOrigin, // + ](self: StringSlice[O], keepends: Bool = False) -> List[StringSlice[O]]: """Split the string at line boundaries. This corresponds to Python's - [universal newlines]( + [universal newlines:]( https://docs.python.org/3/library/stdtypes.html#str.splitlines) - `"\\t\\n\\r\\r\\n\\f\\v\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + `"\\r\\n"` and `"\\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x85\\u2028\\u2029"`. + + Parameters: + O: The immutable origin. Args: keepends: If True, line breaks are kept in the resulting strings. @@ -904,43 +953,57 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( Returns: A List of Strings containing the input split by line boundaries. """ - var output = List[String]() - var length = self.byte_length() - var current_offset = 0 - var ptr = self.unsafe_ptr() - while current_offset < length: - var eol_location = length - current_offset - var eol_length = 0 - var curr_ptr = ptr.offset(current_offset) + alias `\r` = UInt8(ord("\r")) + alias `\n` = UInt8(ord("\n")) + alias `\t` = UInt8(ord("\t")) + alias `\x1c` = UInt8(ord("\x1c")) + alias `\x1e` = UInt8(ord("\x1e")) + output = List[StringSlice[O]](capacity=128) # guessing + ptr = self.unsafe_ptr() + length = self.byte_length() + offset = 0 + + @always_inline + @parameter + fn _is_newline_char(p: UnsafePointer[Byte], l: Int, b0: Byte) -> Bool: + # sorry for readability, but this has less overhead than memcmp + # highly performance sensitive code, benchmark before touching + if l == 1: + return `\t` <= b0 <= `\x1e` and not (`\r` < b0 < `\x1c`) + elif l == 2: + return b0 == 0xC2 and p[1] == 0x85 # next line: \x85 + elif l == 3: + # unicode line sep or paragraph sep: \u2028 , \u2029 + v2 = p[2] + lastbyte = v2 == 0xA8 or v2 == 0xA9 + return b0 == 0xE2 and p[1] == 0x80 and lastbyte + return False - for i in range(current_offset, length): - var read_ahead = 3 if i < length - 2 else ( - 2 if i < length - 1 else 1 + while offset < length: + eol_start = offset + eol_length = 0 + + while eol_start < length: + b0 = ptr[eol_start] + char_len = _utf8_first_byte_sequence_length(b0) + debug_assert( + eol_start + char_len <= length, + "corrupted sequence causing unsafe memory access", ) - var res = _is_newline_start(ptr.offset(i), read_ahead) - if res[0]: - eol_location = i - current_offset - eol_length = res[1] + isnewline = int(_is_newline_char(ptr + eol_start, char_len, b0)) + char_end = isnewline * (eol_start + char_len) + next_idx = char_end * int(char_end < length) + is_r_n = b0 == `\r` and next_idx != 0 and ptr[next_idx] == `\n` + eol_length = isnewline * char_len + int(is_r_n) + if unlikely(isnewline == 1): break + eol_start += char_len - var str_len: Int - var end_of_string = False - if current_offset >= length: - end_of_string = True - str_len = 0 - elif keepends: - str_len = eol_location + eol_length - else: - str_len = eol_location - - output.append( - String(Self(unsafe_from_utf8_ptr=curr_ptr, len=str_len)) - ) - - if end_of_string: - break - current_offset += eol_location + eol_length + str_len = eol_start - offset + int(keepends) * eol_length + s = StringSlice[O](unsafe_from_utf8_ptr=ptr + offset, len=str_len) + output.append(s^) + offset = eol_start + eol_length return output^ @@ -974,6 +1037,77 @@ trait Stringlike: ... +fn _to_string_list[ + T: CollectionElement, //, + len_fn: fn (T) -> Int, + unsafe_ptr_fn: fn (T) -> UnsafePointer[Byte], +](items: List[T]) -> List[String]: + i_len = len(items) + i_ptr = items.unsafe_ptr() + out_ptr = UnsafePointer[String].alloc(i_len) + + for i in range(i_len): + og_len = len_fn(i_ptr[i]) + f_len = og_len + 1 # null terminator + p = UnsafePointer[Byte].alloc(f_len) + og_ptr = unsafe_ptr_fn(i_ptr[i]) + memcpy(p, og_ptr, og_len) + p[og_len] = 0 # null terminator + buf = String._buffer_type(unsafe_pointer=p, size=f_len, capacity=f_len) + (out_ptr + i).init_pointee_move(String(buf^)) + return List[String](unsafe_pointer=out_ptr, size=i_len, capacity=i_len) + + +@always_inline +fn _to_string_list[ + O: ImmutableOrigin, // +](items: List[StringSlice[O]]) -> List[String]: + """Create a list of Strings **copying** the existing data. + + Parameters: + O: The origin of the data. + + Args: + items: The List of string slices. + + Returns: + The list of created strings. + """ + + fn unsafe_ptr_fn(v: StringSlice[O]) -> UnsafePointer[Byte]: + return v.unsafe_ptr() + + fn len_fn(v: StringSlice[O]) -> Int: + return v.byte_length() + + return _to_string_list[len_fn, unsafe_ptr_fn](items) + + +@always_inline +fn _to_string_list[ + O: ImmutableOrigin, // +](items: List[Span[Byte, O]]) -> List[String]: + """Create a list of Strings **copying** the existing data. + + Parameters: + O: The origin of the data. + + Args: + items: The List of Bytes. + + Returns: + The list of created strings. + """ + + fn unsafe_ptr_fn(v: Span[Byte, O]) -> UnsafePointer[Byte]: + return v.unsafe_ptr() + + fn len_fn(v: Span[Byte, O]) -> Int: + return len(v) + + return _to_string_list[len_fn, unsafe_ptr_fn](items) + + # ===----------------------------------------------------------------------===# # Format method structures # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 982622a4a0..cdadffbc1a 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -587,7 +587,7 @@ struct StringRef( fn strip(self) -> StringRef: """Gets a StringRef with leading and trailing whitespaces removed. - This only takes C spaces into account: " \\t\\n\\r\\f\\v". + This only takes C spaces into account: " \\t\\n\\v\\f\\r". For example, `" mojo "` returns `"mojo"`. diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 471486b5d3..77a7b802d9 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -398,63 +398,41 @@ def test_split(): def test_splitlines(): + alias L = List[String] # Test with no line breaks - var in1 = "hello world" - var res1 = in1.splitlines() - assert_equal(len(res1), 1) - assert_equal(res1[0], "hello world") - - # Test with \n line break - var in2 = "hello\nworld" - var res2 = in2.splitlines() - assert_equal(len(res2), 2) - assert_equal(res2[0], "hello") - assert_equal(res2[1], "world") - - # Test with \r\n line break - var in3 = "hello\r\nworld" - var res3 = in3.splitlines() - assert_equal(len(res3), 2) - assert_equal(res3[0], "hello") - assert_equal(res3[1], "world") - - # Test with \r line break - var in4 = "hello\rworld" - var res4 = in4.splitlines() - assert_equal(len(res4), 2) - assert_equal(res4[0], "hello") - assert_equal(res4[1], "world") + assert_equal("hello world".splitlines(), L("hello world")) + + # Test with line breaks + assert_equal("hello\nworld".splitlines(), L("hello", "world")) + assert_equal("hello\rworld".splitlines(), L("hello", "world")) + assert_equal("hello\r\nworld".splitlines(), L("hello", "world")) # Test with multiple different line breaks - var in5 = "hello\nworld\r\nmojo\rlanguage" - var res5 = in5.splitlines() - assert_equal(len(res5), 4) - assert_equal(res5[0], "hello") - assert_equal(res5[1], "world") - assert_equal(res5[2], "mojo") - assert_equal(res5[3], "language") - - # Test with keepends=True - var res6 = in5.splitlines(keepends=True) - assert_equal(len(res6), 4) - assert_equal(res6[0], "hello\n") - assert_equal(res6[1], "world\r\n") - assert_equal(res6[2], "mojo\r") - assert_equal(res6[3], "language") + s1 = "hello\nworld\r\nmojo\rlanguage\r\n" + hello_mojo = L("hello", "world", "mojo", "language") + assert_equal(s1.splitlines(), hello_mojo) + assert_equal( + s1.splitlines(keepends=True), + L("hello\n", "world\r\n", "mojo\r", "language\r\n"), + ) # Test with an empty string - var in7 = "" - var res7 = in7.splitlines() - assert_equal(len(res7), 0) - - # test with keepends=True - var in8 = String("hello\vworld\fmojo\x1clanguage\x1d") - var res10 = in8.splitlines(keepends=True) - assert_equal(len(res10), 4) - assert_equal(res10[0], "hello\v") - assert_equal(res10[1], "world\f") - assert_equal(res10[2], "mojo\x1c") - assert_equal(res10[3], "language\x1d") + assert_equal("".splitlines(), L()) + # test \v \f \x1c \x1d + s2 = "hello\vworld\fmojo\x1clanguage\x1d" + assert_equal(s2.splitlines(), hello_mojo) + assert_equal( + s2.splitlines(keepends=True), + L("hello\v", "world\f", "mojo\x1c", "language\x1d"), + ) + + # test \x1c \x1d \x1e + s3 = "hello\x1cworld\x1dmojo\x1elanguage\x1e" + assert_equal(s3.splitlines(), hello_mojo) + assert_equal( + s3.splitlines(keepends=True), + L("hello\x1c", "world\x1d", "mojo\x1e", "language\x1e"), + ) def test_float_conversion(): diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 86d524a6c4..aba5058605 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -844,71 +844,41 @@ def test_split(): def test_splitlines(): + alias L = List[String] # Test with no line breaks - var in1 = String("hello world") - var res1 = in1.splitlines() - assert_equal(len(res1), 1) - assert_equal(res1[0], "hello world") - - # Test with \n line break - var in2 = String("hello\nworld") - var res2 = in2.splitlines() - assert_equal(len(res2), 2) - assert_equal(res2[0], "hello") - assert_equal(res2[1], "world") - - # Test with \r\n line break - var in3 = String("hello\r\nworld") - var res3 = in3.splitlines() - assert_equal(len(res3), 2) - assert_equal(res3[0], "hello") - assert_equal(res3[1], "world") - - # Test with \r line break - var in4 = String("hello\rworld") - var res4 = in4.splitlines() - assert_equal(len(res4), 2) - assert_equal(res4[0], "hello") - assert_equal(res4[1], "world") + assert_equal(String("hello world").splitlines(), L("hello world")) + + # Test with line breaks + assert_equal(String("hello\nworld").splitlines(), L("hello", "world")) + assert_equal(String("hello\rworld").splitlines(), L("hello", "world")) + assert_equal(String("hello\r\nworld").splitlines(), L("hello", "world")) # Test with multiple different line breaks - var in5 = String("hello\nworld\r\nmojo\rlanguage") - var res5 = in5.splitlines() - assert_equal(len(res5), 4) - assert_equal(res5[0], "hello") - assert_equal(res5[1], "world") - assert_equal(res5[2], "mojo") - assert_equal(res5[3], "language") - - # Test with keepends=True - var res6 = in5.splitlines(keepends=True) - assert_equal(len(res6), 4) - assert_equal(res6[0], "hello\n") - assert_equal(res6[1], "world\r\n") - assert_equal(res6[2], "mojo\r") - assert_equal(res6[3], "language") + s1 = String("hello\nworld\r\nmojo\rlanguage\r\n") + hello_mojo = L("hello", "world", "mojo", "language") + assert_equal(s1.splitlines(), hello_mojo) + assert_equal( + s1.splitlines(keepends=True), + L("hello\n", "world\r\n", "mojo\r", "language\r\n"), + ) # Test with an empty string - var in7 = String("") - var res7 = in7.splitlines() - assert_equal(len(res7), 0) - + assert_equal(String("").splitlines(), L()) # test \v \f \x1c \x1d - var in8 = String("hello\vworld\fmojo\x1clanguage\x1d") - var res8 = in8.splitlines() - assert_equal(len(res8), 4) - assert_equal(res8[0], "hello") - assert_equal(res8[1], "world") - assert_equal(res8[2], "mojo") - assert_equal(res8[3], "language") - - # test \x1e \x1d - var in9 = String("hello\x1eworld\x1dmojo") - var res9 = in9.splitlines() - assert_equal(len(res9), 3) - assert_equal(res9[0], "hello") - assert_equal(res9[1], "world") - assert_equal(res9[2], "mojo") + s2 = String("hello\vworld\fmojo\x1clanguage\x1d") + assert_equal(s2.splitlines(), hello_mojo) + assert_equal( + s2.splitlines(keepends=True), + L("hello\v", "world\f", "mojo\x1c", "language\x1d"), + ) + + # test \x1c \x1d \x1e + s3 = String("hello\x1cworld\x1dmojo\x1elanguage\x1e") + assert_equal(s3.splitlines(), hello_mojo) + assert_equal( + s3.splitlines(keepends=True), + L("hello\x1c", "world\x1d", "mojo\x1e", "language\x1e"), + ) # test \x85 \u2028 \u2029 var next_line = List[UInt8](0xC2, 0x85, 0) @@ -919,28 +889,13 @@ def test_splitlines(): """TODO: \\u2029""" for i in List(next_line, unicode_line_sep, unicode_paragraph_sep): - var in9 = "hello\x1eworld" + String(i[]) + "mojo" - var res9 = in9.splitlines() - assert_equal(len(res9), 3) - assert_equal(res9[0], "hello") - assert_equal(res9[1], "world") - assert_equal(res9[2], "mojo") - - # test with keepends=True - var res10 = in8.splitlines(keepends=True) - assert_equal(len(res10), 4) - assert_equal(res10[0], "hello\v") - assert_equal(res10[1], "world\f") - assert_equal(res10[2], "mojo\x1c") - assert_equal(res10[3], "language\x1d") - - var res11 = ("hello\x1eworld" + String(next_line) + "mojo").splitlines( - keepends=True - ) - assert_equal(len(res11), 3) - assert_equal(res11[0], "hello\x1e") - assert_equal(res11[1], "world" + String(next_line)) - assert_equal(res11[2], "mojo") + u = String(i[]) + item = String("").join("hello", u, "world", u, "mojo", u, "language", u) + assert_equal(item.splitlines(), hello_mojo) + assert_equal( + item.splitlines(keepends=True), + L("hello" + u, "world" + u, "mojo" + u, "language" + u), + ) def test_isupper(): diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 21791d7604..eb03a1eaf3 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -205,8 +205,8 @@ fn test_utf8_validation() raises: ظهرت نسخ جديدة ومختلفة من نص لوريم إيبسوم، أحياناً عن طريق الصدفة، وأحياناً عن عمد كإدخال بعض العبارات الفكاهية إليها. """ - assert_true(_is_valid_utf8(text.unsafe_ptr(), text.byte_length())) - assert_true(_is_valid_utf8(text.unsafe_ptr(), text.byte_length())) + assert_true(_is_valid_utf8(text.as_bytes())) + assert_true(_is_valid_utf8(text.as_bytes())) var positive = List[List[UInt8]]( List[UInt8](0x0), @@ -226,8 +226,8 @@ fn test_utf8_validation() raises: List[UInt8](0xF4, 0x8F, 0x88, 0xAA), ) for item in positive: - assert_true(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) - assert_true(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) + assert_true(_is_valid_utf8(Span(item[]))) + assert_true(_is_valid_utf8(Span(item[]))) var negative = List[List[UInt8]]( List[UInt8](0x80), List[UInt8](0xBF), @@ -256,8 +256,8 @@ fn test_utf8_validation() raises: List[UInt8](0x00, 0x00, 0xF0, 0x80, 0x80, 0x80), ) for item in negative: - assert_false(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) - assert_false(_is_valid_utf8(item[].unsafe_ptr(), len(item[]))) + assert_false(_is_valid_utf8(Span(item[]))) + assert_false(_is_valid_utf8(Span(item[]))) def test_find(): @@ -335,7 +335,7 @@ alias BAD_SEQUENCES = List[String]( fn validate_utf8(slice: String) -> Bool: - return _is_valid_utf8(slice.unsafe_ptr(), slice.byte_length()) + return _is_valid_utf8(slice.as_bytes()) def test_good_utf8_sequences(): @@ -415,6 +415,78 @@ def test_count_utf8_continuation_bytes(): _test(3, List[UInt8](b2, c, b3, c, c)) +def test_splitlines(): + alias S = StringSlice[StaticConstantOrigin] + alias L = List[StringSlice[StaticConstantOrigin]] + + # FIXME: remove once StringSlice conforms to TestableCollectionElement + fn _assert_equal[ + O1: ImmutableOrigin, O2: ImmutableOrigin + ](l1: List[StringSlice[O1]], l2: List[StringSlice[O2]]) raises: + assert_equal(len(l1), len(l2)) + for i in range(len(l1)): + assert_equal(str(l1[i]), str(l2[i])) + + # FIXME: remove once StringSlice conforms to TestableCollectionElement + fn _assert_equal[ + O1: ImmutableOrigin + ](l1: List[StringSlice[O1]], l2: List[String]) raises: + assert_equal(len(l1), len(l2)) + for i in range(len(l1)): + assert_equal(str(l1[i]), l2[i]) + + # Test with no line breaks + _assert_equal(S("hello world").splitlines(), L("hello world")) + + # Test with line breaks + _assert_equal(S("hello\nworld").splitlines(), L("hello", "world")) + _assert_equal(S("hello\rworld").splitlines(), L("hello", "world")) + _assert_equal(S("hello\r\nworld").splitlines(), L("hello", "world")) + + # Test with multiple different line breaks + s1 = S("hello\nworld\r\nmojo\rlanguage\r\n") + hello_mojo = L("hello", "world", "mojo", "language") + _assert_equal(s1.splitlines(), hello_mojo) + _assert_equal( + s1.splitlines(keepends=True), + L("hello\n", "world\r\n", "mojo\r", "language\r\n"), + ) + + # Test with an empty string + _assert_equal(S("").splitlines(), L()) + # test \v \f \x1c \x1d + s2 = S("hello\vworld\fmojo\x1clanguage\x1d") + _assert_equal(s2.splitlines(), hello_mojo) + _assert_equal( + s2.splitlines(keepends=True), + L("hello\v", "world\f", "mojo\x1c", "language\x1d"), + ) + + # test \x1c \x1d \x1e + s3 = S("hello\x1cworld\x1dmojo\x1elanguage\x1e") + _assert_equal(s3.splitlines(), hello_mojo) + _assert_equal( + s3.splitlines(keepends=True), + L("hello\x1c", "world\x1d", "mojo\x1e", "language\x1e"), + ) + + # test \x85 \u2028 \u2029 + var next_line = String(List[UInt8](0xC2, 0x85, 0)) + """TODO: \\x85""" + var unicode_line_sep = String(List[UInt8](0xE2, 0x80, 0xA8, 0)) + """TODO: \\u2028""" + var unicode_paragraph_sep = String(List[UInt8](0xE2, 0x80, 0xA9, 0)) + """TODO: \\u2029""" + + for i in List(next_line, unicode_line_sep, unicode_paragraph_sep): + u = i[] + item = String("").join("hello", u, "world", u, "mojo", u, "language", u) + s = StringSlice(item) + _assert_equal(s.splitlines(), hello_mojo) + items = List("hello" + u, "world" + u, "mojo" + u, "language" + u) + _assert_equal(s.splitlines(keepends=True), items) + + fn main() raises: test_string_literal_byte_span() test_string_byte_span() @@ -432,3 +504,4 @@ fn main() raises: test_combination_10_good_utf8_sequences() test_combination_10_good_10_bad_utf8_sequences() test_count_utf8_continuation_bytes() + test_splitlines() From f221e6834cb44fe7ac66eea352086660e4ba74f3 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 29 Oct 2024 11:18:46 -0600 Subject: [PATCH 1810/2019] [External] [stdlib] Use SIMD to make `b64encode` 4.7x faster (#49573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Use SIMD to make `b64encode` 4.7x faster ## Dependencies The following PR should be merged first: * https://github.com/modularml/mojo/pull/3397 ## Description of the changes `b64encode` is the function that encode bytes to base 64. Base 64 encoding is massively used across the industry, being to write secrets as text or to send data across the internet. Since it's going to be used a lot, we should make sure it is fast. As such, this PR provides a new implementation of `b64encode` around 5 times faster than the current one. This implementation was taken from the following papers: Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the speed of a memory copy, Software: Practice and Experience 50 (2), 2020. https://arxiv.org/abs/1910.05109 Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 Instructions, ACM Transactions on the Web 12 (3), 2018. https://arxiv.org/abs/1704.00605 Note that there are substancial differences between the papers and this implementation. There are two reasons for this: * We want to avoid using assembly/llvm intrinsics directly and try to use the functions provided by the stdlib * We want to keep the complexity low, so we don't make a slightly different algorithm for each simd sizes and each cpu architecture. In a nutshell, we decide on a simd size, let's say 32. So at each iteration, we load 32 bytes, reshuffle the 24 first bytes, convert them to base 64, it then becomes 32 bytes, and then we store those 32 bytes in the output buffer. We have a final iteration with the last incomplete chunks where we shouldn't load everything at once, otherwise we would get out of bounds errors. We then use partial loads and store and masking, but the main SIMD algorithm is used. The reasons for the speedups are simlar to the ones provided in https://github.com/modularml/mojo/pull/3401 ## API changes The existing api is ```mojo fn b64encode(str: String) -> String: ``` and has several limitations: 1) The input of the function is raw bytes. It doesn't have to represent text. Requirering the user to provide a `String` forces the user to handle null termination on its bytes and whatever other requirement `String` might have to use bytes. 2) It is not possible to write the produced bytes in an existing buffer. 3) It is hard to benchmark as the signature implies that the function allocates memory on the heap. 4) It supposes that the input value owns the underlying data, meaning that it's not possible to use the function if the data is not owned. `Span` would be a better choice here. We keep in this PR the existing signature for backward compatibility and add new overloads. Now the signatures are: ```mojo fn b64encode(input_bytes: List[UInt8, _], inout result: List[UInt8, _]) fn b64encode(input_bytes: List[UInt8, _]) -> String fn b64encode(input_string: String) -> String ``` Note that it could be further improved in future PRs as currently `Span` is not easy to use but would be a right fit for the input value. We could also in the future remove `fn b64encode(input_string: String) -> String`. Note that the python api takes `bytes` as input and returns `bytes`. ## Benchmarking Benchmarking is harder than usual here because the base function does memory allocation. To avoid having the alloc in the benchmark, we must modify the original function to add the overloads described above. In this case we can benchmark and on my system ``` WSL2 windows 11 Intel(R) Core(TM) i7-10700KF CPU @ 3.80GHz Base speed: 3,80 GHz Sockets: 1 Cores: 8 Logical processors: 16 Virtualization: Enabled L1 cache: 512 KB L2 cache: 2,0 MB L3 cache: 16,0 MB ``` We get around 5x speedup. I don't provide the benchmark script here because it won't work out of the box (see the issue mentionned above), but if that's really necessary to get this merged, I'll provide the diff + the benchmark script. ## Future work As said before, this PR is not an exact re-implementation of the papers and the state of the art implementation that comes with it, the [simdutf](https://github.com/simdutf/simdutf) library. This is to keep this implementation simple and portable as it will work on any CPU that has an simd size of at least 4 bytes, and below or equal 64 bytes. In future PRs, we could provide futher speedups by using simd algorithms that are specific to each architecture. This will greatly increase the complexity of the code. I'll leave this decision to the maintainers. We can also re-write `b64decode` using simd and it's also expected that we'll get speedups. This can be the topic of another PR too. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#3443 MODULAR_ORIG_COMMIT_REV_ID: 0cd01a091ba8cfdaac49dcf43280de22d9c8b299 --- stdlib/src/base64/_b64encode.mojo | 388 +++++++++++++++++++++++++ stdlib/src/base64/base64.mojo | 68 +++-- stdlib/src/builtin/simd.mojo | 10 + stdlib/src/math/math.mojo | 12 + stdlib/src/utils/_utf8_validation.mojo | 11 +- stdlib/test/base64/test_base64.mojo | 8 + 6 files changed, 452 insertions(+), 45 deletions(-) create mode 100644 stdlib/src/base64/_b64encode.mojo diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo new file mode 100644 index 0000000000..0af12e0069 --- /dev/null +++ b/stdlib/src/base64/_b64encode.mojo @@ -0,0 +1,388 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +""" +We make use of the following papers for the implementation, note that there +are some small differences. +Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the +speed of a memory copy, Software: Practice and Experience 50 (2), 2020. +https://arxiv.org/abs/1910.05109 + +Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 +Instructions, ACM Transactions on the Web 12 (3), 2018. +https://arxiv.org/abs/1704.00605 +""" + +from collections import InlineArray +from memory import memcpy, bitcast, UnsafePointer +from memory.maybe_uninitialized import UnsafeMaybeUninitialized +from builtin.simd import _sub_with_saturation +from math.math import _compile_time_iota + + +""" +| 6-bit Value | ASCII Range | Target index | Offset (6-bit to ASCII) | +|-------------|-------------|--------------|-------------------------| +| 0 ... 25 | A ... Z | 13 | 65 | +| 26 ... 51 | a ... z | 0 | 71 | +| 52 ... 61 | 0 ... 9 | 1 ... 10 | -4 | +| 62 | + | 11 | -19 | +| 63 | / | 12 | -16 | +""" +alias UNUSED = 0 +# fmt: off +alias TABLE_BASE64_OFFSETS = SIMD[DType.uint8, 16]( + 71, # a ... z + -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, # 0 ... 9 + -19, # + + -16, # / + 65, # A ... Z + UNUSED, UNUSED +) +# fmt: on +alias END_FIRST_RANGE = 25 +alias END_SECOND_RANGE = 51 + + +fn _base64_simd_mask[ + simd_width: Int +](nb_value_to_load: Int) -> SIMD[DType.bool, simd_width]: + alias mask = _compile_time_iota[DType.uint8, simd_width]() + return mask < UInt8(nb_value_to_load) + + +fn _repeat_until[ + dtype: DType, input_size: Int, //, target_size: Int +](vector: SIMD[dtype, input_size]) -> SIMD[dtype, target_size]: + @parameter + if target_size == input_size: + var same_vector = rebind[SIMD[dtype, target_size]](vector) + return same_vector + else: + return _repeat_until[target_size](vector.join(vector)) + + +fn _move_first_group_of_6_bits[ + simd_width: Int +](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ + DType.uint8, simd_width +]: + alias mask_1 = _repeat_until[simd_width]( + SIMD[DType.uint8, 4](0b11111100, 0, 0, 0) + ) + var masked_1 = shuffled_vector & mask_1 + var result = masked_1 >> 2 + return result + + +fn _move_second_group_of_6_bits[ + simd_width: Int +](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ + DType.uint8, simd_width +]: + alias mask_2 = _repeat_until[simd_width]( + SIMD[DType.uint8, 4](0b00000011, 0b11110000, 0, 0) + ) + var masked_2 = shuffled_vector & mask_2 + var masked_2_as_uint16 = bitcast[DType.uint16, simd_width // 2](masked_2) + var rotated_2 = bit.rotate_bits_right[4](masked_2_as_uint16) + var result = bitcast[DType.uint8, simd_width](rotated_2) + return result + + +fn _move_third_group_of_6_bits[ + simd_width: Int +](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ + DType.uint8, simd_width +]: + alias mask_3 = _repeat_until[simd_width]( + SIMD[DType.uint8, 4]( + 0, + 0, + 0b00001111, + 0b11000000, + ) + ) + var masked_3 = shuffled_vector & mask_3 + var masked_3_as_uint16 = bitcast[DType.uint16, simd_width // 2](masked_3) + var rotated_3 = bit.rotate_bits_left[2](masked_3_as_uint16) + var result = bitcast[DType.uint8, simd_width](rotated_3) + return result + + +fn _move_fourth_group_of_6_bits[ + simd_width: Int +](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ + DType.uint8, simd_width +]: + alias mask_4 = _repeat_until[simd_width]( + SIMD[DType.uint8, 4]( + 0, + 0, + 0, + 0b00111111, + ) + ) + result = shuffled_vector & mask_4 + return result + + +fn _shuffle_input_vector[ + simd_width: Int +](input_vector: SIMD[DType.uint8, simd_width]) -> SIMD[DType.uint8, simd_width]: + # We reorder the bytes to fall in their correct 4 bytes chunks + # When Mojo is a bit more flexible with compile-time programming, we should be + # able to make this less verbose. + @parameter + if simd_width < 4: + constrained[False, msg="simd_width must be at least 4"]() + return SIMD[DType.uint8, simd_width]() # dummy, unreachable + elif simd_width == 4: + return input_vector.shuffle[0, 1, 1, 2]() + elif simd_width == 8: + return input_vector.shuffle[0, 1, 1, 2, 3, 4, 4, 5]() + elif simd_width == 16: + return input_vector.shuffle[ + 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11 + ]() + elif simd_width == 32: + # fmt: off + return input_vector.shuffle[ + 0, 1, 1, 2, + 3, 4, 4, 5, + 6, 7, 7, 8, + 9, 10, 10, 11, + 12, 13, 13, 14, + 15, 16, 16, 17, + 18, 19, 19, 20, + 21, 22, 22, 23 + ]() + # fmt: on + elif simd_width == 64: + # fmt: off + return input_vector.shuffle[ + 0, 1, 1, 2, + 3, 4, 4, 5, + 6, 7, 7, 8, + 9, 10, 10, 11, + 12, 13, 13, 14, + 15, 16, 16, 17, + 18, 19, 19, 20, + 21, 22, 22, 23, + 24, 25, 25, 26, + 27, 28, 28, 29, + 30, 31, 31, 32, + 33, 34, 34, 35, + 36, 37, 37, 38, + 39, 40, 40, 41, + 42, 43, 43, 44, + 45, 46, 46, 47, + ]() + # fmt: on + else: + constrained[False, msg="simd_width must be at most 64"]() + return SIMD[DType.uint8, simd_width]() # dummy, unreachable + + +fn _to_b64_ascii[ + simd_width: Int +](input_vector: SIMD[DType.uint8, simd_width]) -> SIMD[DType.uint8, simd_width]: + alias constant_13 = SIMD[DType.uint8, simd_width](13) + + # We reorder the bytes to fall in their correct 4 bytes chunks + var shuffled_vector = _shuffle_input_vector(input_vector) + + # We have 4 different masks to extract each group of 6 bits from the 4 bytes + var ready_to_encode_per_byte = ( + _move_first_group_of_6_bits(shuffled_vector) + | _move_second_group_of_6_bits(shuffled_vector) + | _move_third_group_of_6_bits(shuffled_vector) + | _move_fourth_group_of_6_bits(shuffled_vector) + ) + + # See the table above for the offsets, we try to go from 6-bits values to target indexes. + # The two first ranges go to 0, the other ranges are just 1...12. + var saturated = _sub_with_saturation( + ready_to_encode_per_byte, + SIMD[DType.uint8, simd_width](END_SECOND_RANGE), + ) + + var mask_in_first_range = ready_to_encode_per_byte <= END_FIRST_RANGE + + # Now are have the target indexes + # The first range goes to 13 + var indices = mask_in_first_range.select(constant_13, saturated) + + var offsets = TABLE_BASE64_OFFSETS._dynamic_shuffle(indices) + + return ready_to_encode_per_byte + offsets + + +fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ + simd_width: Int +]() -> SIMD[DType.uint8, simd_width]: + """This is a lookup table to know how many bytes we need to store in the output buffer + for a given number of bytes to encode in base64. Including the '=' sign. + + This table lookup is smaller than the simd size, because we only use it for the last chunk. + This should be called at compile time, otherwise it's quite slow. + """ + var result = SIMD[DType.uint8, simd_width](0) + for i in range(1, simd_width): + # We have "i" bytes to encode in base64, how many bytes do + # we need to store in the output buffer? Including the '=' sign. + + # math.ceil cannot be called at compile time, this is a workaround + var group_of_3_bytes = i // 3 + if i % 3 != 0: + group_of_3_bytes += 1 + + result[i] = group_of_3_bytes * 4 + return result + + +fn _get_number_of_bytes_to_store_from_number_of_bytes_to_load[ + max_size: Int +](nb_of_elements_to_load: Int) -> Int: + alias table = _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ + max_size + ]() + return int(table[nb_of_elements_to_load]) + + +fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ + simd_width: Int +]() -> SIMD[DType.uint8, simd_width]: + """This is a lookup table to know how many bytes we need to store in the output buffer + for a given number of bytes to encode in base64. This is **not** including the '=' sign. + + This table lookup is smaller than the simd size, because we only use it for the last chunk. + This should be called at compile time, otherwise it's quite slow. + """ + var result = SIMD[DType.uint8, simd_width]() + for i in range(simd_width): + # We have "i" bytes to encode in base64, how many bytes do + # we need to store in the output buffer? NOT including the '=' sign. + # We count the number of groups of 6 bits and we add 1 byte if there is an incomplete group. + var number_of_bits = i * 8 + var complete_groups_of_6_bits = number_of_bits // 6 + var incomplete_groups_of_6_bits: Int + if i * 8 % 6 == 0: + incomplete_groups_of_6_bits = 0 + else: + incomplete_groups_of_6_bits = 1 + + result[i] = complete_groups_of_6_bits + incomplete_groups_of_6_bits + return result + + +fn _get_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ + max_size: Int +](nb_of_elements_to_load: Int) -> Int: + alias table = _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ + max_size + ]() + return int(table[nb_of_elements_to_load]) + + +fn load_incomplete_simd[ + simd_width: Int +](pointer: UnsafePointer[UInt8], nb_of_elements_to_load: Int) -> SIMD[ + DType.uint8, simd_width +]: + var result = SIMD[DType.uint8, simd_width](0) + var tmp_buffer_pointer = UnsafePointer.address_of(result).bitcast[UInt8]() + memcpy(dest=tmp_buffer_pointer, src=pointer, count=nb_of_elements_to_load) + return result + + +fn store_incomplete_simd[ + simd_width: Int +]( + pointer: UnsafePointer[UInt8], + owned simd_vector: SIMD[DType.uint8, simd_width], + nb_of_elements_to_store: Int, +): + var tmp_buffer_pointer = UnsafePointer.address_of(simd_vector).bitcast[ + UInt8 + ]() + + memcpy(dest=pointer, src=tmp_buffer_pointer, count=nb_of_elements_to_store) + _ = simd_vector # We make it live long enough + + +# TODO: Use Span instead of List as input when Span is easier to use +@no_inline +fn b64encode_with_buffers( + input_bytes: List[UInt8, _], inout result: List[UInt8, _] +): + alias simd_width = sys.simdbytewidth() + alias input_simd_width = simd_width * 3 // 4 + alias equal_vector = SIMD[DType.uint8, simd_width](ord("=")) + + var input_bytes_len = len(input_bytes) + + var input_index = 0 + + # Main loop + while input_index + simd_width <= input_bytes_len: + var start_of_input_chunk = input_bytes.unsafe_ptr() + input_index + + var input_vector = start_of_input_chunk.load[width=simd_width]() + + result_vector = _to_b64_ascii(input_vector) + + (result.unsafe_ptr() + len(result)).store(result_vector) + + result.size += simd_width + input_index += input_simd_width + + # We handle the last 0, 1 or 2 chunks + while input_index < input_bytes_len: + var start_of_input_chunk = input_bytes.unsafe_ptr() + input_index + var nb_of_elements_to_load = min( + input_simd_width, input_bytes_len - input_index + ) + + # We don't want to read past the input buffer + var input_vector = load_incomplete_simd[simd_width]( + start_of_input_chunk, + nb_of_elements_to_load=nb_of_elements_to_load, + ) + + result_vector = _to_b64_ascii(input_vector) + + # We place the '=' where needed + var non_equal_chars_number = _get_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ + simd_width + ]( + nb_of_elements_to_load + ) + var equal_mask = _base64_simd_mask[simd_width](non_equal_chars_number) + + var result_vector_with_equals = equal_mask.select( + result_vector, equal_vector + ) + + var nb_of_elements_to_store = _get_number_of_bytes_to_store_from_number_of_bytes_to_load[ + simd_width + ]( + nb_of_elements_to_load + ) + store_incomplete_simd( + result.unsafe_ptr() + len(result), + result_vector_with_equals, + nb_of_elements_to_store, + ) + result.size += nb_of_elements_to_store + input_index += input_simd_width diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 62e021d38f..0bf4f2a3b9 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -21,6 +21,8 @@ from base64 import b64encode from collections import List from sys import simdwidthof +import bit +from ._b64encode import b64encode_with_buffers as _b64encode_with_buffers # ===----------------------------------------------------------------------===# # Utilities @@ -60,50 +62,46 @@ fn _ascii_to_value(char: String) -> Int: # ===----------------------------------------------------------------------===# -fn b64encode(str: String) -> String: +# TODO: Use Span instead of List as input when Span is easier to use +fn b64encode(input_bytes: List[UInt8, _], inout result: List[UInt8, _]): """Performs base64 encoding on the input string. Args: - str: The input string. + input_bytes: The input string buffer. Assumed to be null-terminated. + result: The buffer in which to store the values. + """ + _b64encode_with_buffers(input_bytes, result) + + +# For a nicer API, we provide those overloads: +fn b64encode(input_string: String) -> String: + """Performs base64 encoding on the input string. + + Args: + input_string: The input string buffer. Assumed to be null-terminated. Returns: - Base64 encoding of the input string. + The ASCII base64 encoded string. """ - alias lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - var b64chars = lookup.unsafe_ptr() + # Slicing triggers a copy, but it should work with Span later on. + return b64encode(input_string._buffer[:-1]) - var length = str.byte_length() - var out = String._buffer_type(capacity=length + 1) - @parameter - @always_inline - fn s(idx: Int) -> Int: - return int(str.unsafe_ptr()[idx]) +fn b64encode(input_bytes: List[UInt8, _]) -> String: + """Performs base64 encoding on the input string. - # This algorithm is based on https://arxiv.org/abs/1704.00605 - var end = length - (length % 3) - for i in range(0, end, 3): - var si = s(i) - var si_1 = s(i + 1) - var si_2 = s(i + 2) - out.append(b64chars[si // 4]) - out.append(b64chars[((si * 16) % 64) + si_1 // 16]) - out.append(b64chars[((si_1 * 4) % 64) + si_2 // 64]) - out.append(b64chars[si_2 % 64]) - - if end < length: - var si = s(end) - out.append(b64chars[si // 4]) - if end == length - 1: - out.append(b64chars[(si * 16) % 64]) - out.append(ord("=")) - elif end == length - 2: - var si_1 = s(end + 1) - out.append(b64chars[((si * 16) % 64) + si_1 // 16]) - out.append(b64chars[(si_1 * 4) % 64]) - out.append(ord("=")) - out.append(0) - return String(out^) + Args: + input_bytes: The input string buffer. Assumed to be null-terminated. + + Returns: + The ASCII base64 encoded string. + """ + # +1 for the null terminator and +1 to be sure + var result = List[UInt8, True](capacity=int(len(input_bytes) * (4 / 3)) + 2) + b64encode(input_bytes, result) + # null-terminate the result + result.append(0) + return String(result^) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f67ba542be..1febc71219 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3197,6 +3197,16 @@ fn _modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: return (result_int, result_frac) +@always_inline("nodebug") +fn _sub_with_saturation[ + width: Int, // +](a: SIMD[DType.uint8, width], b: SIMD[DType.uint8, width]) -> SIMD[ + DType.uint8, width +]: + # generates a single `vpsubusb` on x86 with AVX + return llvm_intrinsic["llvm.usub.sat", __type_of(a)](a, b) + + # ===----------------------------------------------------------------------=== # # floor # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 23933e7812..d6d0a5bb51 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1057,6 +1057,18 @@ fn isclose[ # ===----------------------------------------------------------------------=== # +# TODO: Remove this when `iota` works at compile-time +fn _compile_time_iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: + constrained[ + type.is_integral(), + "_compile_time_iota can only be used with integer types.", + ]() + var a = SIMD[type, simd_width](0) + for i in range(simd_width): + a[i] = i + return a + + @always_inline fn iota[ type: DType, simd_width: Int diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index a57521d7d7..800ed5626b 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -27,6 +27,7 @@ https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs from memory import UnsafePointer from sys.intrinsics import llvm_intrinsic +from builtin.simd import _sub_with_saturation alias TOO_SHORT: UInt8 = 1 << 0 alias TOO_LONG: UInt8 = 1 << 1 @@ -91,16 +92,6 @@ fn _extract_vector[ return a.join(b).slice[width, offset=offset]() -@always_inline("nodebug") -fn _sub_with_saturation[ - width: Int, // -](a: SIMD[DType.uint8, width], b: SIMD[DType.uint8, width]) -> SIMD[ - DType.uint8, width -]: - # generates a single `vpsubusb` on x86 with AVX - return llvm_intrinsic["llvm.usub.sat", __type_of(a)](a, b) - - fn validate_chunk[ simd_size: Int ]( diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 0e9abc7f12..6512844905 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -32,6 +32,14 @@ def test_b64encode(): ) assert_equal(b64encode("ABCDEFabcdef"), "QUJDREVGYWJjZGVm") + assert_equal(b64encode("\x00\n\x14\x1e(2 Date: Tue, 29 Oct 2024 16:27:29 -0400 Subject: [PATCH 1811/2019] [tests] Remove COMPATIBLE_COMPILER_VERSION check MODULAR_ORIG_COMMIT_REV_ID: e37f767f0c6c21f2c562bd2d9372c909a5af42f5 --- stdlib/scripts/build-stdlib.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/stdlib/scripts/build-stdlib.sh b/stdlib/scripts/build-stdlib.sh index f154ecaae3..db9c00b5a5 100755 --- a/stdlib/scripts/build-stdlib.sh +++ b/stdlib/scripts/build-stdlib.sh @@ -19,19 +19,6 @@ REPO_ROOT=$(realpath "${SCRIPT_DIR}/../..") BUILD_DIR="${REPO_ROOT}"/build mkdir -p "${BUILD_DIR}" -ACTUAL_COMPILER_VERSION=$(mojo --version | tr " " "\n" | sed -n 2p) -EXPECTED_COMPILER_VERSION=$(<"${REPO_ROOT}"/stdlib/COMPATIBLE_COMPILER_VERSION) - -if [ -z "${MOJO_OVERRIDE_COMPILER_VERSION_CHECK:-}" ]; then - if [ "${EXPECTED_COMPILER_VERSION}" != "${ACTUAL_COMPILER_VERSION}" ]; then - echo "Mismatch in compiler versions! Cannot build the standard library." - echo "Expected compiler version: ${EXPECTED_COMPILER_VERSION}" - echo "Current installed compiler version: ${ACTUAL_COMPILER_VERSION}" - echo "Please run \`magic update && magic install\` to get the latest compiler." - exit 1 - fi -fi - STDLIB_PATH="${REPO_ROOT}/stdlib/src" echo "Packaging up the Standard Library." From c8716fccd55fc5f45fc98d5f4054e0fbed043abc Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 29 Oct 2024 15:20:43 -0600 Subject: [PATCH 1812/2019] [stdlib] Fix `run-benchmarks` lit config cb7604feb033289021057eaf14f5a2a2ee62bd84 broke the OSS benchmarks when not run internally. This is due to a simple type-checking issue: `pre_built_packages_path` is a string type which won't have a `resolve()` function. Explicitly construct it to a `Path` type instead which has the needed `resolve()` functionality. MODULAR_ORIG_COMMIT_REV_ID: a48a48db3428d7fc431a9f5403ad813b59484e79 --- stdlib/benchmarks/lit.cfg.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/stdlib/benchmarks/lit.cfg.py b/stdlib/benchmarks/lit.cfg.py index 304a5521d9..a06446ab34 100644 --- a/stdlib/benchmarks/lit.cfg.py +++ b/stdlib/benchmarks/lit.cfg.py @@ -46,12 +46,14 @@ # This is important since `benchmark` is closed source # still right now and is always used by the benchmarks. - pre_built_packages_path = os.environ.get( - "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + pre_built_packages_path = Path( os.environ.get( - "MODULAR_MOJO_IMPORT_PATH", - repo_root / ".magic" / "envs" / "default" / "lib" / "mojo", - ), + "MODULAR_MOJO_NIGHTLY_IMPORT_PATH", + os.environ.get( + "MODULAR_MOJO_IMPORT_PATH", + repo_root / ".magic" / "envs" / "default" / "lib" / "mojo", + ), + ) ) # The `run-tests.sh` script creates the build directory for you. From 30a2c3865a71aab4cda9504de5b5811acc48a5c3 Mon Sep 17 00:00:00 2001 From: Judy Heflin Date: Tue, 29 Oct 2024 14:29:08 -0700 Subject: [PATCH 1813/2019] [mojo-stdlib][docs] Updates to Mojo type manual (StringLiteral, UInt, and Tuple) Added clarification/updates for StringLiteral and UInt. Added a subsection and example for Tuple. MODULAR_ORIG_COMMIT_REV_ID: 8649f79626b0583f00ed13a1f9cdd09034be67a0 --- docs/manual/types.ipynb | 157 ++++++++++++++++++++++++++++++----- stdlib/src/builtin/uint.mojo | 2 +- 2 files changed, 135 insertions(+), 24 deletions(-) diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 94690bd6d1..1cd9762b51 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -50,8 +50,8 @@ "Mojo's most basic numeric type is `Int`, which represents a signed integer of\n", "the largest size supported by the system—typically 64 bits or 32 bits.\n", "\n", - "Mojo also has built-in types for integer and floating-point values of various\n", - "precisions:\n", + "Mojo also has built-in types for integer, unsigned integer, and floating-point \n", + "values of various precisions:\n", "\n", "
    \n", "\n", @@ -79,10 +79,41 @@ "functions.\n", "\n", "You may wonder when to use `Int` and when to use the other integer \n", - "types. In general, `Int` is good safe default when you need an integer type and\n", - "you don't require a specific bit width. Using `Int` as the default integer type\n", - "for APIs makes APIs more consistent and predictable.\n", - "\n", + "types. In general, `Int` is a good safe default when you need an integer type \n", + "and you don't require a specific bit width. Using `Int` as the default integer \n", + "type for APIs makes APIs more consistent and predictable.\n", + "\n", + "### Signed and unsigned integers\n", + "\n", + "Mojo supports both signed (`Int`) and unsigned (`UInt`) integers. You can use \n", + "the general `Int` or `UInt` types when you do not require a specific bit width.\n", + "Note that any alias to a fixed-precision type will be of type \n", + "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD).\n", + "\n", + "You might prefer to use unsigned integers over signed integers in conditions \n", + "where you don't need negative numbers, are not writing for a public API, or need \n", + "additional range.\n", + "\n", + "Mojo's `UInt` type represents an unsigned integer of the \n", + "[word size](https://en.wikipedia.org/wiki/Word_(computer_architecture)) of the \n", + "CPU, which is 64 bits on 64-bit CPUs and 32 bits on 32-bit CPUs. If you wish to \n", + "use a fixed size unsigned integer, you can use `UInt8`, `UInt16`, `UInt32`, or \n", + "`UInt64`, which are aliases to the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) \n", + "type. \n", + "\n", + "Signed and unsigned integers of the same bit width can represent the same number \n", + "of values, but have different ranges. For example, an `Int8` can represent 256 \n", + "values ranging from -128 to 127. A `UInt8` can also represent 256 values, but \n", + "represents a range of 0 to 255. \n", + "\n", + "Signed and unsigned integers also have different overflow behavior. When a \n", + "signed integer overflows outside the range of values that its type can \n", + "represent, the value overflows to negative numbers. For example, adding `1` to \n", + "`var si: Int8 = 127` results in `-128`. \n", + "\n", + "When an unsigned integer overflows outside the range of values that its type can \n", + "represent, the value overflows to zero. So, adding `1` to `var ui: UInt8 = 255` \n", + "is equal to `0`.\n", "\n", "### Floating-point numbers\n", "\n", @@ -461,30 +492,23 @@ "Unlike `IntLiteral` and `FloatLiteral`, `StringLiteral` doesn't automatically\n", "materialize to a runtime type. In some cases, you may need to manually convert\n", "`StringLiteral` values to `String` using the built-in \n", - "[`str()`](/mojo/stdlib/builtin/str/str) method. \n", - "\n", - "For example, if you want to concatenate string literals to other types, you need \n", - "to first convert `StringLiteral` to `String` values. This is because many types\n", - "can be implicitly converted to `String`, but not to `StringLiteral`." + "[`str()`](/mojo/stdlib/builtin/str/str) method. " ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Strings play nicely with others: True\n" - ] - } - ], + "outputs": [], "source": [ - "# print(\"Strings play nicely with others: \" + True)\n", - "# Error: ... right hand side cannot be converted from Bool to StringLiteral\n", - "print(str(\"Strings play nicely with others: \") + str(True))" + "# Variable is type `StringLiteral`\n", + "var s1 = \"Example\"\n", + "\n", + "# Variable is type `String`\n", + "var s2: String = \"Example\"\n", + "\n", + "# Variable is type `String`\n", + "var s3 = str(\"Example\")" ] }, { @@ -531,6 +555,93 @@ "have a non-zero length." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples\n", + "\n", + "Mojo's `Tuple` type represents an immutable tuple consisting of zero or more \n", + "values, separated by commas. Tuples can consist of multiple types and you can \n", + "index into tuples in multiple ways. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 Example\n", + "Example\n" + ] + } + ], + "source": [ + "# Tuples are immutable and can hold multiple types\n", + "example_tuple = Tuple[Int, String](1, \"Example\")\n", + "\n", + "# Assign multiple variables at once\n", + "x, y = example_tuple\n", + "print(x, y)\n", + "\n", + "# Get individual values with an index\n", + "s = example_tuple.get[1, String]()\n", + "print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also create a tuple without explicit typing. Note that if we declare the \n", + "same tuple from the previous example with implicit typing instead of explicit, \n", + "we must also convert `\"Example\"` from type `StringLiteral` to type `String`. " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Example\n" + ] + } + ], + "source": [ + "example_tuple = (1, str(\"Example\"))\n", + "s = example_tuple.get[1, String]()\n", + "print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When defining a function, you can explicitly declare the type of tuple elements \n", + "in one of two ways: " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "def return_tuple_1() -> Tuple[Int, Int]:\n", + " return Tuple[Int, Int](1, 1)\n", + "\n", + "def return_tuple_2() -> (Int, Int):\n", + " return (2, 2)" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 31fa561dc7..9c744052df 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -28,7 +28,7 @@ from hashlib._hasher import _HashableWithHasher, _Hasher struct UInt(IntLike, _HashableWithHasher): """This type represents an unsigned integer. - An unsigned integer is represents a positive integral number. + An unsigned integer represents a positive integral number. The size of this unsigned integer is platform-dependent. From ef488e084717beee50acf1ef92006b73898cf1fd Mon Sep 17 00:00:00 2001 From: soraros Date: Tue, 29 Oct 2024 16:17:51 -0600 Subject: [PATCH 1814/2019] [External] [stdlib] Make `StringRef` ctor from `UnsafePointer[Byte]` keyword only (#49581) [External] [stdlib] Make `StringRef` ctor from `UnsafePointer[Byte]` keyword only This issues was reported by `@aurelian`(@diocletiann) on Discord. This prevents accidental `UnsafePointer[Byte]` to `String` implicit conversion through `String` ctor from `StringRef`. Make this explicit by explicitly constructing the `StringRef` with the keyword-only constructor. Co-authored-by: soraros Closes modularml/mojo#3698 MODULAR_ORIG_COMMIT_REV_ID: 44c1e061db833fff3cf261f2b6eabfcbef6e1591 --- stdlib/src/collections/string.mojo | 2 +- stdlib/src/os/env.mojo | 2 +- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/utils/stringref.mojo | 2 +- stdlib/test/collections/test_string.mojo | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 80792e2129..3f9e28aa47 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -957,7 +957,7 @@ struct String( buff: The buffer. This should have an existing terminator. """ - return String(buff, len(StringRef(buff)) + 1) + return String(buff, len(StringRef(ptr=buff)) + 1) @staticmethod fn _from_bytes(owned buff: Self._buffer_type) -> String: diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index aece34c39c..6feb36e79b 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -74,4 +74,4 @@ fn getenv(name: String, default: String = "") -> String: var ptr = external_call["getenv", UnsafePointer[UInt8]](name.unsafe_ptr()) if not ptr: return default - return String(StringRef(ptr)) + return String(StringRef(ptr=ptr)) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 419bc93b1f..45a8c9aacf 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -46,7 +46,7 @@ fn cwd() raises -> Path: if res == UnsafePointer[c_char](): raise Error("unable to query the current directory") - return String(StringRef(buf)) + return String(StringRef(ptr=buf)) @always_inline diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 409f9587eb..f6c92b609a 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -296,7 +296,7 @@ fn _py_get_version(lib: DLHandle) -> StringRef: var version_string = lib.get_function[fn () -> UnsafePointer[c_char]]( "Py_GetVersion" )() - return StringRef(version_string) + return StringRef(ptr=version_string) fn _py_finalize(lib: DLHandle): diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index cdadffbc1a..87732fc351 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -112,7 +112,7 @@ struct StringRef( self.length = len @always_inline - fn __init__(inout self, ptr: UnsafePointer[UInt8]): + fn __init__(inout self, *, ptr: UnsafePointer[UInt8]): """Construct a StringRef value given a null-terminated string. Args: diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index aba5058605..b5a1d3007c 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -267,7 +267,7 @@ def test_stringref(): def test_stringref_from_dtypepointer(): var a = StringRef("AAA") - var b = StringRef(a.data) + var b = StringRef(ptr=a.data) assert_equal(3, len(a)) assert_equal(3, len(b)) assert_equal(a, b) From 85d204cb69b37d620f2e17b2fba8947b0c5bef26 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:42:56 -0600 Subject: [PATCH 1815/2019] [External] [stdlib] Add benchmark for `lcm` and `gcd` (#41811) [External] [stdlib] Add benchmark for `lcm` and `gcd` Add benchmarks for `lcm` and `gcd` functions. Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#3041 MODULAR_ORIG_COMMIT_REV_ID: 3fe9e4a6321832878a49ad79d90593ef2969076b --- stdlib/benchmarks/math/bench_math.mojo | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index d2e5de20c0..1ba4175a74 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -39,7 +39,20 @@ fn make_inputs( return result +fn make_int_inputs(begin: Int, end: Int, num: Int) -> List[Int]: + if num == 1: + return List[Int](begin) + + var step = (end - begin) // (num - 1) + + var result: List[Int] = List[Int]() + for i in range(num): + result.append(begin + step * i) + return result + + var inputs = make_inputs(0, 10_000, 1_000_000) +var int_inputs = make_int_inputs(0, 10_000_000, 1_000_000) # ===----------------------------------------------------------------------===# # Benchmark math_func @@ -79,6 +92,21 @@ fn bench_math3[ b.iter[call_fn]() +# ===----------------------------------------------------------------------===# +# Benchmark lcm/gcd +# ===----------------------------------------------------------------------===# +@parameter +fn bench_math2[math_f2p: fn (Int, Int, /) -> Int](inout b: Bencher) raises: + @always_inline + @parameter + fn call_fn() raises: + for i in range(len(int_inputs) // 2): + var result = keep(math_f2p(int_inputs[i], int_inputs[-(i + 1)])) + keep(result) + + b.iter[call_fn]() + + # ===----------------------------------------------------------------------===# # Benchmark Main # ===----------------------------------------------------------------------===# @@ -98,4 +126,6 @@ def main(): m.bench_function[bench_math[exp]](BenchId("bench_math_exp")) m.bench_function[bench_math[erf]](BenchId("bench_math_erf")) m.bench_function[bench_math3[fma]](BenchId("bench_math_fma")) + m.bench_function[bench_math2[lcm]](BenchId("bench_math_lcm")) + m.bench_function[bench_math2[gcd]](BenchId("bench_math_gcd")) m.dump_report() From d3c353afc7b0b570968fb5385546ab5bca5bdbbe Mon Sep 17 00:00:00 2001 From: Alex Trotta <44127594+Ahajha@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:55:45 -0400 Subject: [PATCH 1816/2019] [CI] Un-ignore lockfiles again MODULAR_ORIG_COMMIT_REV_ID: b510830590117b3eb28db46074cfbe75ad7989bd --- examples/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/.gitignore b/examples/.gitignore index 79d5db1e7f..aa90b40800 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -4,4 +4,3 @@ pixi.lock # Magic env .magic/ -magic.lock From 7c2b61a5d85eeeda0b07cf2da2d30cf881991db8 Mon Sep 17 00:00:00 2001 From: Caroline Frasca <42614552+carolinefrasca@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:15:19 -0400 Subject: [PATCH 1817/2019] [Docs] Remove mentions of (now closed) GitHub Discussions from the MAX Graph tutorial and a few README files. MODULAR_ORIG_COMMIT_REV_ID: 8d212fe410e8fa53695a627a3f3444101025c2a7 --- proposals/project-manifest-and-build-tool.md | 6 ++---- stdlib/docs/development.md | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/proposals/project-manifest-and-build-tool.md b/proposals/project-manifest-and-build-tool.md index 1899b0ae3d..c2d5b5c661 100644 --- a/proposals/project-manifest-and-build-tool.md +++ b/proposals/project-manifest-and-build-tool.md @@ -98,7 +98,5 @@ Below are some topics that we would love to hear community members’ opinions o favor of doing so, but on the other hand, we see tradeoffs as well, and a purely declarative form could be used. - Any other thoughts you wish to contribute — we are build systems and language - tooling nerds! Send us your thoughts on [the GitHub Discussion thread - associated with this - proposal](https://github.com/modularml/mojo/discussions/1785), and let’s geek - out. + tooling nerds! Share your thoughts in [our Discord server](https://modul.ar/discord), + and let’s geek out. diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index e813810f67..024dc87b6c 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -376,6 +376,5 @@ did have a worthwhile change you wanted to raise, follow the steps to Congratulations! You've now got an idea on how to contribute to the standard library, test your changes, and raise a PR. -If you're still having troubles make sure to reach out on -[GitHub](https://github.com/modularml/mojo/discussions/new?category=general) or -[Discord](https://modul.ar/discord)! +If you're still having issues, reach out on +[Discord](https://modul.ar/discord). From ad832a8de32c0ddde3ccccc1598c391c6e6d9009 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 30 Oct 2024 18:31:36 -0700 Subject: [PATCH 1818/2019] [******][GPU] Fix the SM90 arch detection MODULAR_ORIG_COMMIT_REV_ID: c877b952dd7136fe53e11990bc207eed7539ecd7 --- stdlib/src/sys/info.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 6d2d86bb9e..004c77a7ef 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -411,7 +411,7 @@ fn _is_sm_8x() -> Bool: @always_inline("nodebug") fn _is_sm_9x() -> Bool: - return triple_is_nvidia_cuda["sm_90"]() or triple_is_nvidia_cuda["sm_9a"]() + return triple_is_nvidia_cuda["sm_90"]() or triple_is_nvidia_cuda["sm_90a"]() @always_inline("nodebug") From 896032be8da086aefd9f0541f5dd7f3dccd43c4f Mon Sep 17 00:00:00 2001 From: Alvydas Vitkauskas Date: Thu, 31 Oct 2024 10:52:44 -0600 Subject: [PATCH 1819/2019] [External] [stdlib] Deque implementation: part 1 - life cycle methods (#49998) [External] [stdlib] Deque implementation: part 1 - life cycle methods This PR provides a first part of the Deque data type implementation as agreed in https://github.com/modularml/mojo/issues/2659. It includes only the life cycle methods, some methods required by the life cycle methods, and related tests. Co-authored-by: Alvydas Vitkauskas Closes modularml/mojo#3705 MODULAR_ORIG_COMMIT_REV_ID: 1dee15bed50ffbdd399127a80120801dc61ed1c5 --- stdlib/src/collections/__init__.mojo | 1 + stdlib/src/collections/deque.mojo | 420 ++++++++++++++++++++++++ stdlib/test/collections/test_deque.mojo | 381 +++++++++++++++++++++ 3 files changed, 802 insertions(+) create mode 100644 stdlib/src/collections/deque.mojo create mode 100644 stdlib/test/collections/test_deque.mojo diff --git a/stdlib/src/collections/__init__.mojo b/stdlib/src/collections/__init__.mojo index 6a220b91b4..97f58c9c88 100644 --- a/stdlib/src/collections/__init__.mojo +++ b/stdlib/src/collections/__init__.mojo @@ -13,6 +13,7 @@ """Implements the collections package.""" from .counter import Counter +from .deque import Deque from .dict import Dict, KeyElement from .inline_array import InlineArray from .inline_list import InlineList diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo new file mode 100644 index 0000000000..b065265479 --- /dev/null +++ b/stdlib/src/collections/deque.mojo @@ -0,0 +1,420 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines the Deque type. + +You can import these APIs from the `collections` package. + +Examples: + +```mojo +from collections import Deque +``` +""" + +from bit import bit_ceil +from collections import Optional +from memory import UnsafePointer + + +# ===----------------------------------------------------------------------===# +# Deque +# ===----------------------------------------------------------------------===# + + +struct Deque[ElementType: CollectionElement]( + Movable, ExplicitlyCopyable, Sized, Boolable +): + """Implements a double-ended queue. + + It supports pushing and popping from both ends in O(1) time resizing the + underlying storage as needed. + + Parameters: + ElementType: The type of the elements in the deque. + Must implement the trait `CollectionElement`. + """ + + # ===-------------------------------------------------------------------===# + # Aliases + # ===-------------------------------------------------------------------===# + + alias default_capacity: Int = 64 + """The default capacity of the deque: must be the power of 2.""" + + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var _data: UnsafePointer[ElementType] + """The underlying storage for the deque.""" + + var _head: Int + """The index of the head: points the first element of the deque.""" + + var _tail: Int + """The index of the tail: points behind the last element of the deque.""" + + var _capacity: Int + """The amount of elements that can fit in the deque without resizing it.""" + + var _min_capacity: Int + """The minimum required capacity in the number of elements of the deque.""" + + var _maxlen: Int + """The maximum number of elements allowed in the deque. + + If more elements are pushed, causing the total to exceed this limit, + items will be popped from the opposite end to maintain the maximum length. + """ + + var _shrink: Bool + """ Indicates whether the deque's storage is re-allocated to a smaller size when possible.""" + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + + fn __init__( + inout self, + *, + owned elements: Optional[List[ElementType]] = None, + capacity: Int = self.default_capacity, + min_capacity: Int = self.default_capacity, + maxlen: Int = -1, + shrink: Bool = True, + ): + """Constructs a deque. + + Args: + elements: The optional list of initial deque elements. + capacity: The initial capacity of the deque. + min_capacity: The minimum allowed capacity of the deque when shrinking. + maxlen: The maximum allowed capacity of the deque when growing. + shrink: Should storage be de-allocated when not needed. + """ + if capacity <= 0: + deque_capacity = self.default_capacity + else: + deque_capacity = bit_ceil(capacity) + + if min_capacity <= 0: + min_deque_capacity = self.default_capacity + else: + min_deque_capacity = bit_ceil(min_capacity) + + if maxlen <= 0: + max_deque_len = -1 + else: + max_deque_len = maxlen + max_deque_capacity = bit_ceil(maxlen) + if max_deque_capacity == maxlen: + max_deque_capacity <<= 1 + deque_capacity = min(deque_capacity, max_deque_capacity) + + self._capacity = deque_capacity + self._data = UnsafePointer[ElementType].alloc(deque_capacity) + self._head = 0 + self._tail = 0 + self._min_capacity = min_deque_capacity + self._maxlen = max_deque_len + self._shrink = shrink + + if elements is not None: + self.extend(elements.value()) + + fn __init__(inout self, owned *values: ElementType): + """Constructs a deque from the given values. + + Args: + values: The values to populate the deque with. + """ + self = Self(variadic_list=values^) + + fn __init__( + inout self, *, owned variadic_list: VariadicListMem[ElementType, _] + ): + """Constructs a deque from the given values. + + Args: + variadic_list: The values to populate the deque with. + """ + args_length = len(variadic_list) + + if args_length < self.default_capacity: + capacity = self.default_capacity + else: + capacity = args_length + + self = Self(capacity=capacity) + + for i in range(args_length): + src = UnsafePointer.address_of(variadic_list[i]) + dst = self._data + i + src.move_pointee_into(dst) + + # Mark the elements as unowned to avoid del'ing uninitialized objects. + variadic_list._is_owned = False + + self._tail = args_length + + fn __init__(inout self, other: Self): + """Creates a deepcopy of the given deque. + + Args: + other: The deque to copy. + """ + self = Self( + capacity=other._capacity, + min_capacity=other._min_capacity, + maxlen=other._maxlen, + shrink=other._shrink, + ) + for i in range(len(other)): + offset = other._physical_index(other._head + i) + (self._data + i).init_pointee_copy((other._data + offset)[]) + + self._tail = len(other) + + fn __moveinit__(inout self, owned existing: Self): + """Moves data of an existing deque into a new one. + + Args: + existing: The existing deque. + """ + self._data = existing._data + self._capacity = existing._capacity + self._head = existing._head + self._tail = existing._tail + self._min_capacity = existing._min_capacity + self._maxlen = existing._maxlen + self._shrink = existing._shrink + + fn __del__(owned self): + """Destroys all elements in the deque and free its memory.""" + for i in range(len(self)): + offset = self._physical_index(self._head + i) + (self._data + offset).destroy_pointee() + self._data.free() + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + @always_inline + fn __bool__(self) -> Bool: + """Checks whether the deque has any elements or not. + + Returns: + `False` if the deque is empty, `True` if there is at least one element. + """ + return self._head != self._tail + + @always_inline + fn __len__(self) -> Int: + """Gets the number of elements in the deque. + + Returns: + The number of elements in the deque. + """ + return (self._tail - self._head) & (self._capacity - 1) + + fn __getitem__(ref [_]self, idx: Int) -> ref [self] ElementType: + """Gets the deque element at the given index. + + Args: + idx: The index of the element. + + Returns: + A reference to the element at the given index. + """ + normalized_idx = idx + + debug_assert( + -len(self) <= normalized_idx < len(self), + "index: ", + normalized_idx, + " is out of bounds for `Deque` of size: ", + len(self), + ) + + if normalized_idx < 0: + normalized_idx += len(self) + + offset = self._physical_index(self._head + normalized_idx) + return (self._data + offset)[] + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + fn append(inout self, owned value: ElementType): + """Appends a value to the right side of the deque. + + Args: + value: The value to append. + """ + # checking for positive _maxlen first is important for speed + if self._maxlen > 0 and len(self) == self._maxlen: + (self._data + self._head).destroy_pointee() + self._head = self._physical_index(self._head + 1) + + (self._data + self._tail).init_pointee_move(value^) + self._tail = self._physical_index(self._tail + 1) + + if self._head == self._tail: + self._realloc(self._capacity << 1) + + fn extend(inout self, owned values: List[ElementType]): + """Extends the right side of the deque by consuming elements of the list argument. + + Args: + values: List whose elements will be added at the right side of the deque. + """ + n_move_total, n_move_self, n_move_values, n_pop_self, n_pop_values = ( + self._compute_pop_and_move_counts(len(self), len(values)) + ) + + # pop excess `self` elements + for _ in range(n_pop_self): + (self._data + self._head).destroy_pointee() + self._head = self._physical_index(self._head + 1) + + # move from `self` to new location if we have to re-allocate + if n_move_total >= self._capacity: + self._prepare_for_new_elements(n_move_total, n_move_self) + + # we will consume all elements of `values` + values_data = values.steal_data() + + # pop excess elements from `values` + for i in range(n_pop_values): + (values_data + i).destroy_pointee() + + # move remaining elements from `values` + src = values_data + n_pop_values + for i in range(n_move_values): + (src + i).move_pointee_into(self._data + self._tail) + self._tail = self._physical_index(self._tail + 1) + + fn _compute_pop_and_move_counts( + self, len_self: Int, len_values: Int + ) -> (Int, Int, Int, Int, Int): + """ + Calculates the number of elements to retain, move or discard in the deque and + in the list of the new values based on the current length of the deque, + the length of new values to add, and the maximum length constraint `_maxlen`. + + Args: + len_self: The current number of elements in the deque. + len_values: The number of new elements to add to the deque. + + Returns: + A tuple: (n_move_total, n_move_self, n_move_values, n_pop_self, n_pop_values) + n_move_total: Total final number of elements in the deque. + n_move_self: Number of existing elements to retain in the deque. + n_move_values: Number of new elements to add from `values`. + n_pop_self: Number of existing elements to remove from the deque. + n_pop_values: Number of new elements that don't fit and will be discarded. + """ + len_total = len_self + len_values + + n_move_total = ( + min(len_total, self._maxlen) if self._maxlen > 0 else len_total + ) + n_move_values = min(len_values, n_move_total) + n_move_self = n_move_total - n_move_values + + n_pop_self = len_self - n_move_self + n_pop_values = len_values - n_move_values + + return ( + n_move_total, + n_move_self, + n_move_values, + n_pop_self, + n_pop_values, + ) + + @always_inline + fn _physical_index(self, logical_index: Int) -> Int: + """Calculates the physical index in the circular buffer. + + Args: + logical_index: The logical index, which may fall outside the physical bounds + of the buffer and needs to be wrapped around. + + The size of the underlying buffer is always a power of two, allowing the use of + the more efficient bitwise `&` operation instead of the modulo `%` operator. + """ + return logical_index & (self._capacity - 1) + + fn _prepare_for_new_elements(inout self, n_total: Int, n_retain: Int): + """Prepares the deque’s internal buffer for adding new elements by + reallocating memory and retaining the specified number of existing elements. + + Args: + n_total: The total number of elements the new buffer should support. + n_retain: The number of existing elements to keep in the deque. + """ + new_capacity = bit_ceil(n_total) + if new_capacity == n_total: + new_capacity <<= 1 + + new_data = UnsafePointer[ElementType].alloc(new_capacity) + + for i in range(n_retain): + offset = self._physical_index(self._head + i) + (self._data + offset).move_pointee_into(new_data + i) + + if self._data: + self._data.free() + + self._data = new_data + self._capacity = new_capacity + self._head = 0 + self._tail = n_retain + + fn _realloc(inout self, new_capacity: Int): + """Relocates data to a new storage buffer. + + Args: + new_capacity: The new capacity of the buffer. + """ + deque_len = len(self) if self else self._capacity + + tail_len = self._tail + head_len = self._capacity - self._head + + if head_len > deque_len: + head_len = deque_len + tail_len = 0 + + new_data = UnsafePointer[ElementType].alloc(new_capacity) + + src = self._data + self._head + dsc = new_data + for i in range(head_len): + (src + i).move_pointee_into(dsc + i) + + src = self._data + dsc = new_data + head_len + for i in range(tail_len): + (src + i).move_pointee_into(dsc + i) + + self._head = 0 + self._tail = deque_len + + if self._data: + self._data.free() + self._data = new_data + self._capacity = new_capacity diff --git a/stdlib/test/collections/test_deque.mojo b/stdlib/test/collections/test_deque.mojo new file mode 100644 index 0000000000..8130b865f0 --- /dev/null +++ b/stdlib/test/collections/test_deque.mojo @@ -0,0 +1,381 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal, assert_false, assert_true, assert_raises + +from collections import Deque + +# ===----------------------------------------------------------------------===# +# Implementation tests +# ===----------------------------------------------------------------------===# + + +fn test_impl_init_default() raises: + q = Deque[Int]() + + assert_equal(q._capacity, q.default_capacity) + assert_equal(q._min_capacity, q.default_capacity) + assert_equal(q._maxlen, -1) + assert_equal(q._head, 0) + assert_equal(q._tail, 0) + assert_equal(q._shrink, True) + + +fn test_impl_init_capacity() raises: + q = Deque[Int](capacity=-10) + assert_equal(q._capacity, q.default_capacity) + assert_equal(q._min_capacity, q.default_capacity) + + q = Deque[Int](capacity=0) + assert_equal(q._capacity, q.default_capacity) + assert_equal(q._min_capacity, q.default_capacity) + + q = Deque[Int](capacity=10) + assert_equal(q._capacity, 16) + assert_equal(q._min_capacity, q.default_capacity) + + q = Deque[Int](capacity=100) + assert_equal(q._capacity, 128) + assert_equal(q._min_capacity, q.default_capacity) + + +fn test_impl_init_min_capacity() raises: + q = Deque[Int](min_capacity=-10) + assert_equal(q._min_capacity, q.default_capacity) + assert_equal(q._capacity, q.default_capacity) + + q = Deque[Int](min_capacity=0) + assert_equal(q._min_capacity, q.default_capacity) + assert_equal(q._capacity, q.default_capacity) + + q = Deque[Int](min_capacity=10) + assert_equal(q._min_capacity, 16) + assert_equal(q._capacity, q.default_capacity) + + q = Deque[Int](min_capacity=100) + assert_equal(q._min_capacity, 128) + assert_equal(q._capacity, q.default_capacity) + + +fn test_impl_init_maxlen() raises: + q = Deque[Int](maxlen=-10) + assert_equal(q._maxlen, -1) + assert_equal(q._capacity, q.default_capacity) + + q = Deque[Int](maxlen=0) + assert_equal(q._maxlen, -1) + assert_equal(q._capacity, q.default_capacity) + + q = Deque[Int](maxlen=10) + assert_equal(q._maxlen, 10) + assert_equal(q._capacity, 16) + + # has to allocate two times more capacity + # when `maxlen` in a power of 2 because + # tail should always point into a free space + q = Deque[Int](maxlen=16) + assert_equal(q._maxlen, 16) + assert_equal(q._capacity, 32) + + q = Deque[Int](maxlen=100) + assert_equal(q._maxlen, 100) + assert_equal(q._capacity, q.default_capacity) + + +fn test_impl_init_shrink() raises: + q = Deque[Int](shrink=False) + assert_equal(q._shrink, False) + assert_equal(q._capacity, q.default_capacity) + + +fn test_impl_init_list() raises: + q = Deque(elements=List(0, 1, 2)) + assert_equal(q._head, 0) + assert_equal(q._tail, 3) + assert_equal(q._capacity, q.default_capacity) + assert_equal((q._data + 0)[], 0) + assert_equal((q._data + 1)[], 1) + assert_equal((q._data + 2)[], 2) + + +fn test_impl_init_list_args() raises: + q = Deque(elements=List(0, 1, 2), maxlen=2, capacity=10) + assert_equal(q._head, 0) + assert_equal(q._tail, 2) + assert_equal(q._capacity, 4) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 2) + + +fn test_impl_init_variadic() raises: + q = Deque(0, 1, 2) + + assert_equal(q._head, 0) + assert_equal(q._tail, 3) + assert_equal(q._capacity, q.default_capacity) + assert_equal((q._data + 0)[], 0) + assert_equal((q._data + 1)[], 1) + assert_equal((q._data + 2)[], 2) + + +fn test_impl_len() raises: + q = Deque[Int]() + + q._head = 0 + q._tail = 10 + assert_equal(len(q), 10) + + q._head = q.default_capacity - 5 + q._tail = 5 + assert_equal(len(q), 10) + + +fn test_impl_bool() raises: + q = Deque[Int]() + assert_false(q) + + q._tail = 1 + assert_true(q) + + +fn test_impl_append() raises: + q = Deque[Int](capacity=2) + + q.append(0) + assert_equal(q._head, 0) + assert_equal(q._tail, 1) + assert_equal(q._capacity, 2) + assert_equal((q._data + 0)[], 0) + + q.append(1) + assert_equal(q._head, 0) + assert_equal(q._tail, 2) + assert_equal(q._capacity, 4) + assert_equal((q._data + 0)[], 0) + assert_equal((q._data + 1)[], 1) + + q.append(2) + assert_equal(q._head, 0) + assert_equal(q._tail, 3) + assert_equal(q._capacity, 4) + assert_equal((q._data + 0)[], 0) + assert_equal((q._data + 1)[], 1) + assert_equal((q._data + 2)[], 2) + + # simulate popleft() + q._head += 1 + q.append(3) + assert_equal(q._head, 1) + # tail wrapped to the front + assert_equal(q._tail, 0) + assert_equal(q._capacity, 4) + assert_equal((q._data + 1)[], 1) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 3) + + q.append(4) + # re-allocated buffer and moved all elements + assert_equal(q._head, 0) + assert_equal(q._tail, 4) + assert_equal(q._capacity, 8) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 2) + assert_equal((q._data + 2)[], 3) + assert_equal((q._data + 3)[], 4) + + +fn test_impl_append_with_maxlen() raises: + q = Deque[Int](maxlen=3) + + assert_equal(q._maxlen, 3) + assert_equal(q._capacity, 4) + + q.append(0) + q.append(1) + q.append(2) + assert_equal(q._head, 0) + assert_equal(q._tail, 3) + + q.append(3) + # first popped the leftmost element + # so there was no re-allocation of buffer + assert_equal(q._head, 1) + assert_equal(q._tail, 0) + assert_equal(q._capacity, 4) + assert_equal((q._data + 1)[], 1) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 3) + + +fn test_impl_extend() raises: + q = Deque[Int](maxlen=4) + lst = List[Int](0, 1, 2) + + q.extend(lst) + assert_equal(q._head, 0) + assert_equal(q._tail, 3) + assert_equal(q._capacity, 8) + assert_equal((q._data + 0)[], 0) + assert_equal((q._data + 1)[], 1) + assert_equal((q._data + 2)[], 2) + + q.extend(lst) + # has to popleft the first 2 elements + assert_equal(q._capacity, 8) + assert_equal(q._head, 2) + assert_equal(q._tail, 6) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 0) + assert_equal((q._data + 4)[], 1) + assert_equal((q._data + 5)[], 2) + + # turn off `maxlen` restriction + q._maxlen = -1 + q.extend(lst) + assert_equal(q._capacity, 8) + assert_equal(q._head, 2) + assert_equal(q._tail, 1) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 0) + assert_equal((q._data + 4)[], 1) + assert_equal((q._data + 5)[], 2) + assert_equal((q._data + 6)[], 0) + assert_equal((q._data + 7)[], 1) + assert_equal((q._data + 0)[], 2) + + # turn on `maxlen` and force to re-allocate + q._maxlen = 8 + q.extend(lst) + assert_equal(q._capacity, 16) + assert_equal(q._head, 0) + assert_equal(q._tail, 8) + # has to popleft the first 2 elements + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 2) + assert_equal((q._data + 6)[], 1) + assert_equal((q._data + 7)[], 2) + + # extend with the list that is longer than `maxlen` + # has to pop all deque elements and some initial + # elements from the list as well + lst = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + q.extend(lst) + assert_equal(q._capacity, 16) + assert_equal(q._head, 8) + assert_equal(q._tail, 0) + assert_equal((q._data + 8)[], 2) + assert_equal((q._data + 9)[], 3) + assert_equal((q._data + 14)[], 8) + assert_equal((q._data + 15)[], 9) + + +# ===----------------------------------------------------------------------===# +# API Interface tests +# ===----------------------------------------------------------------------===# + + +fn test_init_variadic_list() raises: + lst1 = List(0, 1) + lst2 = List(2, 3) + + q = Deque(lst1, lst2) + assert_equal(q[0], lst1) + assert_equal(q[1], lst2) + + lst1[0] = 4 + assert_equal(q[0], List(0, 1)) + + p = Deque(lst1^, lst2^) + assert_equal(p[0], List(4, 1)) + assert_equal(p[1], List(2, 3)) + + +fn test_copy_trivial() raises: + q = Deque(1, 2, 3) + + p = Deque(q) + assert_equal(p[0], q[0]) + + p[0] = 3 + assert_equal(p[0], 3) + assert_equal(q[0], 1) + + +fn test_copy_list() raises: + q = Deque[List[Int]]() + lst1 = List(1, 2, 3) + lst2 = List(4, 5, 6) + q.append(lst1) + q.append(lst2) + assert_equal(q[0], lst1) + + lst1[0] = 7 + assert_equal(q[0], List(1, 2, 3)) + + p = Deque(q) + assert_equal(p[0], q[0]) + + p[0][0] = 7 + assert_equal(p[0], List(7, 2, 3)) + assert_equal(q[0], List(1, 2, 3)) + + +fn test_move_list() raises: + q = Deque[List[Int]]() + lst1 = List(1, 2, 3) + lst2 = List(4, 5, 6) + q.append(lst1) + q.append(lst2) + assert_equal(q[0], lst1) + + p = q^ + assert_equal(p[0], lst1) + + lst1[0] = 7 + assert_equal(lst1[0], 7) + assert_equal(p[0], List(1, 2, 3)) + + +fn test_getitem() raises: + q = Deque(1, 2) + assert_equal(q[0], 1) + assert_equal(q[1], 2) + assert_equal(q[-1], 2) + assert_equal(q[-2], 1) + + +# ===-------------------------------------------------------------------===# +# main +# ===-------------------------------------------------------------------===# + + +def main(): + test_impl_init_default() + test_impl_init_capacity() + test_impl_init_min_capacity() + test_impl_init_maxlen() + test_impl_init_shrink() + test_impl_init_list() + test_impl_init_list_args() + test_impl_init_variadic() + test_impl_len() + test_impl_bool() + test_impl_append() + test_impl_append_with_maxlen() + test_impl_extend() + test_init_variadic_list() + test_copy_trivial() + test_copy_list() + test_move_list() + test_getitem() From 777c9ff34defed12a780551b40cd97076e96d247 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 31 Oct 2024 13:19:22 -0700 Subject: [PATCH 1820/2019] [Docs] Update lifetime->origin, related changes. Updates docs related to lifetimes to reflect the renaming of lifetime (the type) to origin. Includes some other lifetime-related changes, including removing the caution about unsafe parametric closures now that we can track lifetimes for parametric closure captures. MODULAR_ORIG_COMMIT_REV_ID: de92010eb90db365c7f2a39f6c474c851993e534 --- docs/changelog.md | 53 ++-- docs/manual/decorators/parameter.ipynb | 23 +- docs/manual/lifecycle/index.ipynb | 17 +- docs/manual/values/lifetimes.ipynb | 338 ++++++++++++++----------- docs/manual/values/ownership.ipynb | 175 ++++++------- docs/roadmap.md | 70 +---- 6 files changed, 314 insertions(+), 362 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9e30a7839f..ebd5e3e49b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -21,10 +21,11 @@ what we publish. [Issue #933](https://github.com/modularml/mojo/issues/933). - The destructor insertion logic in Mojo is now aware that types that take an - `AnyLifetime` as part of their signature could potentially access any live - value that destructor insertion is tracking, eliminating a significant - usability issue with unsafe APIs like `UnsafePointer`. Consider a typical - example working with strings before this change: + `MutableAnyOrigin` or `ImmutableAnyOrigin` as part of their signature could + potentially access any live value that destructor insertion is tracking, + eliminating a significant usability issue with unsafe APIs like + `UnsafePointer`. Consider a typical example working with strings before this + change: ```mojo var str = String(...) @@ -35,20 +36,20 @@ what we publish. The `_ = str^` pattern was formerly required because the Mojo compiler has no idea what "ptr" might reference. As a consequence, it had no idea that - `some_low_level_api` might access `str` and therefore thought it was ok to + `some_low_level_api()` might access `str` and therefore thought it was ok to destroy the `String` before the call - this is why the explicit lifetime extension was required. - Mojo now knows that `UnsafePointer` may access the `AnyLifetime` lifetime, - and now assumes that any API that uses that lifetime could use live values. - In this case, it assumes that `some_low_level_api` might access `str` and + Mojo now knows that `UnsafePointer` may access the `MutableAnyOrigin` origin, + and now assumes that any API that uses that origin could use live values. + In this case, it assumes that `some_low_level_api()` might access `str` and because it might be using it, it cannot destroy `str` until after the call. The consequence of this is that the old hack is no longer needed for these cases! -- The `UnsafePointer` type now has a `lifetime` parameter that can be used when - the `UnsafePointer` is known to point into some lifetime. This lifetime is - propagated through the `ptr[]` indirection operation. +- The `UnsafePointer` type now has an `origin` parameter that can be used when + the `UnsafePointer` is known to point to a value with a known origin. This + origin is propagated through the `ptr[]` indirection operation. - The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration setting that can be used in conjunction with `mojoFile` to define the build @@ -123,9 +124,9 @@ what we publish. pass ``` -- Function types now accept a lifetime set parameter. This parameter represents - the lifetimes of values captured by a parameter closure. The compiler - automatically tags parameter closures with the right set of lifetimes. This +- Function types now accept an origin set parameter. This parameter represents + the origins of values captured by a parameter closure. The compiler + automatically tags parameter closures with the right set of origins. This enables lifetimes and parameter closures to correctly compose. ```mojo @@ -144,8 +145,8 @@ what we publish. ``` Note that this only works for higher-order functions which have explicitly - added `[_]` as the capture lifetimes. By default, the compiler still assumes - a `capturing` closure does not reference any lifetimes. This will soon change. + added `[_]` as the capture origins. By default, the compiler still assumes + a `capturing` closure does not reference any origins. This will soon change. - The VS Code extension now has the `mojo.run.focusOnTerminalAfterLaunch` setting, which controls whether to focus on the terminal used by the @@ -165,10 +166,10 @@ what we publish. ([PR #3524](https://github.com/modularml/mojo/pull/3524) by [@szbergeron](https://github.com/szbergeron)) - `ref` argument and result specifiers now allow providing a memory value - directly in the lifetime specifier, rather than requiring the use of - `__origin_of`. It is still fine to use `__origin_of` explicitly though, - and this is required when specifying lifetimes for parameters (e.g. to the - `Reference` type). For example, this is now valid without `__origin_of`: + directly in the origin specifier, rather than requiring the use of + `__origin_of()`. It is still fine to use `__origin_of()` explicitly though, + and this is required when specifying origins for parameters (e.g. to the + `Pointer` type). For example, this is now valid without `__origin_of()`: ```mojo fn return_ref(a: String) -> ref [a] String: @@ -247,8 +248,8 @@ what we publish. specifies to the compiler that the resultant pointer is a distinct identifiable object that does not alias any other memory in the local scope. -- The `AnyLifetime` type (useful for declaring lifetime types as parameters) has - been renamed to `Lifetime`. +- The `AnyLifetime` type (useful for declaring origin types as parameters) has + been renamed to `Origin`. - Restore implicit copyability of `Tuple` and `ListLiteral`. @@ -354,12 +355,12 @@ what we publish. been consolidated under `s.as_bytes` to return a `Span[Byte]`, you can convert it to a `List` if you require a copy with `List(s.as_bytes())`. -- `Lifetime` and related types has been renamed to `Origin` in the standard +- `Lifetime` and related types have been renamed to `Origin` in the standard library to better clarify that parameters of this type indicate where a reference is derived from, not the more complicated notion of where a variable is initialized and destroyed. Please see [the proposal](https://github.com/modularml/mojo/blob/main/proposals/lifetimes-keyword-renaming.md) - for more information and rationale. As a consequence `__lifetime_of` is now - named `__origin_of`. + for more information and rationale. As a consequence the `__lifetime_of()` + operator is now named `__origin_of()`. - You can now use the `+=` and `*` operators on a `StringLiteral` at compile time using the `alias` keyword: @@ -416,7 +417,7 @@ what we publish. doesn't extend the lifetimes of the values it references. - [Issue #3627](https://github.com/modularml/mojo/issues/3627) - Compiler - overlooked exclusivity violation caused by `ref [MutableAnyLifetime] T` + overlooked exclusivity violation caused by `ref [MutableAnyOrigin] T` - The VS Code extension now auto-updates its private copy of the MAX SDK. diff --git a/docs/manual/decorators/parameter.ipynb b/docs/manual/decorators/parameter.ipynb index c74b25e22f..1cb7334442 100644 --- a/docs/manual/decorators/parameter.ipynb +++ b/docs/manual/decorators/parameter.ipynb @@ -127,7 +127,7 @@ } ], "source": [ - "fn use_closure[func: fn(Int) capturing -> Int](num: Int) -> Int:\n", + "fn use_closure[func: fn(Int) capturing [_] -> Int](num: Int) -> Int:\n", " return func(num)\n", "\n", "fn create_closure():\n", @@ -150,20 +150,19 @@ "source": [ "Without the `@parameter` decorator, you'll get a compiler error that says you\n", "\"cannot use a dynamic value in call parameter\"—referring to the\n", - "`use_closure[add](2)` call—because the `add()` closure would still be dynamic." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "`use_closure[add](2)` call—because the `add()` closure would still be dynamic.\n", "\n", - ":::caution\n", + "Note the `[_]` in the function type:\n", "\n", - "This is an unsafe feature because we currently do not model the lifetimes of\n", - "capture-by-reference.\n", + "```mojo\n", + "fn use_closure[func: fn(Int) capturing [_] -> Int](num: Int) -> Int:\n", + "```\n", "\n", - ":::" + "This origin specifier represents the set of origins for the values captured by\n", + "the parametric closure. This allows the compiler to correctly extend the\n", + "lifetimes of those values. For more information on lifetimes and origins, see\n", + "[Lifetimes, origins and references](/mojo/manual/values/lifetimes).\n", + "\n" ] }, { diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 61872ba398..f4e3118941 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -71,20 +71,21 @@ "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", "All values that are declared with the same type have the same lifecycle.\n", "\n", - "- The \"lifetime\" of a value is defined by the span of time during \n", - "program execution in which each value is considered valid. The life of a value \n", - "begins when it is initialized (via `__init__()`, `__copyinit__()` or \n", - "`__moveinit__()`) and ends when it is destroyed (`__del__()`), or consumed in\n", - "some other way (for example, as part of a `__moveinit__()` call). \n", + "- The \"lifetime\" of a variable is defined by the span of time during \n", + "program execution in which the variable is considered valid. The life of a \n", + "variable begins when its value is initialized (via `__init__()`, \n", + "`__copyinit__()` or `__moveinit__()`) and ends when the value is destroyed \n", + "(`__del__()`), or consumed in some other way (for example, as part of a \n", + "`__moveinit__()` call). \n", "\n", - "No two values have the exact same life span, because every value is created and \n", + "No two values have the exact same lifetime, because every value is created and \n", "destroyed at a different point in time (even if the difference is imperceptible).\n", "\n", - ":::note Lifetime type\n", + ":::note Origin type\n", "\n", "The concept of lifetimes is related to the `origin` type, a Mojo primitive\n", "used to track ownership. For most Mojo programming, you won't need to work with\n", - "`origin` values directly. For information, see [Lifetimes and\n", + "`origin` values directly. For information, see [Lifetimes, origins and\n", "references](/mojo/manual/values/lifetimes).\n", "\n", ":::\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 3c801baf52..9f525cd30a 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -9,9 +9,9 @@ }, "source": [ "---\n", - "title: Lifetimes and references\n", + "title: Lifetimes, origins, and references\n", "sidebar_position: 4\n", - "description: Working with lifetimes and references.\n", + "description: Working with origins and references.\n", "---" ] }, @@ -19,30 +19,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ":::note Work in progress\n", - "\n", - "Both lifetimes and references are a work in progress and subject to change in\n", - "future releases. \n", - "\n", - ":::\n", - "\n", - "In Mojo, _lifetime_ has two meanings: \n", - "\n", - "- In general terms, a value's lifetime refers to the span of time when the \n", - " value is valid. \n", - "\n", - "- It also refers to a specific type of parameter value used to help track the \n", - " lifetimes of values and references to values. For clarity, we'll use\n", - " `lifetime` in code font to refer to the type.\n", - "\n", "The Mojo compiler includes a lifetime checker, a compiler pass that analyzes\n", "dataflow through your program. It identifies when variables are valid and \n", - "inserts destructor calls when a value's lifetime ends.\n", + "inserts destructor calls when a variable's lifetime ends.\n", "\n", - "The Mojo compiler uses `lifetime` values to track the validity of references.\n", - "Specifically, a `lifetime` value answers two questions:\n", + "The Mojo compiler uses a special value called an _origin_ to track the lifetime\n", + "of variables and the validity of references.\n", "\n", - "- What logical storage location \"owns\" this value?\n", + "Specifically, an origin answers two questions:\n", + "\n", + "- What variable \"owns\" this value?\n", "- Can the value be mutated using this reference?\n", "\n", "For example, consider the following code:\n" @@ -50,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -77,64 +63,64 @@ "and logical storage space for a `String` value. When you pass `name` into the\n", "`print_str()` function, the function gets an immutable reference to the value. \n", "So both `name` and `s` refer to the same logical storage space, and have\n", - "associated `lifetime` values that lets the Mojo compiler reason about them. \n", + "associated origin values that lets the Mojo compiler reason about them. \n", "\n", - "Most of the time, `lifetime` values are handled automatically by the compiler. \n", - "However, in some cases you'll need to interact with `lifetime` values directly:\n", + "Most of the time, origins are handled automatically by the compiler. \n", + "However, in some cases you'll need to interact with origins directly:\n", "\n", "- When working with references—specifically `ref` arguments and `ref` return\n", " values. \n", "\n", "- When working with types like \n", - " [`Reference`](/mojo/stdlib/memory/reference/Reference) or \n", + " [`Pointer`](/mojo/stdlib/memory/reference/Pointer) or \n", " [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the \n", - " `lifetime` of the data they refer to.\n", + " origin of the data they refer to.\n", "\n", - "This section covers [`ref` arguments](#ref-arguments) and \n", + "This section also covers [`ref` arguments](#ref-arguments) and \n", "[`ref` return values](#ref-return-values), which let functions\n", "take arguments and provide return values as references with parametric\n", - "lifetimes." + "origins." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Working with lifetimes\n", + "## Working with origins\n", + "\n", + "Mojo's origin values are unlike most other values in the language, because\n", + "they're primitive values, not Mojo structs.\n", "\n", - "Mojo's `lifetime` values are unlike most other values in the language, because\n", - "they're primitive values, not Mojo structs. Specifying a parameter that takes a \n", - "`lifetime` value, you can't just say, `l: Lifetime`, because there's no \n", - "`Lifetime` type. Likewise, because these values are mostly created by the \n", - "compiler, you can't just create your own `lifetime` value—you usually need to \n", - "derive a `lifetime` from an existing value.\n", + "Likewise, because these values are mostly created by the \n", + "compiler, you can't just create your own origin value—you usually need to \n", + "derive an origin from an existing value.\n", "\n", - "### Lifetime types\n", + "### Origin types\n", "\n", "Mojo supplies a struct and a set of aliases that you can use to specify \n", - "`lifetime` types. As the names suggest, the `ImmutableLifetime` and \n", - "`MutableLifetime` aliases represent immutable and mutable lifetimes, \n", + "origin types. As the names suggest, the `ImmutableOrigin` and \n", + "`MutableOrigin` aliases represent immutable and mutable origins, \n", "respectively:\n", "\n", "```mojo\n", - "struct ImmutableRef[lifetime: ImmutableLifetime]:\n", + "struct ImmutableRef[origin: ImmutableOrigin]:\n", " pass\n", "```\n", "\n", - "Or you can use the [`AnyLifetime`](mojo/stdlib/builtin/type_aliases/AnyLifetime)\n", - "struct to specify a lifetime with parametric mutability:" + "Or you can use the [`Origin`](mojo/stdlib/builtin/type_aliases/Origin)\n", + "struct to specify an origin with parametric mutability:" ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "struct ParametricRef[\n", " is_mutable: Bool,\n", " //,\n", - " lifetime: AnyLifetime[is_mutable].type\n", + " origin: Origin[is_mutable].type\n", "]:\n", " pass" ] @@ -143,31 +129,39 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note that `AnyLifetime` _isn't a lifetime value_, it's a helper for specifying a \n", - "`lifetime` **type**. Lifetime types carry the mutability of a reference as a \n", - "boolean parameter value, indicating whether the lifetime is mutable, immutable,\n", + "Note that `Origin` _isn't an origin value_, it's a helper for specifying a \n", + "origin **type**. Origin types carry the mutability of a reference as a \n", + "boolean parameter value, indicating whether the origin is mutable, immutable,\n", "or even with mutability depending on a parameter specified by the enclosing API.\n", "\n", "The `is_mutable` parameter here is an [infer-only\n", "parameter](/mojo/manual/parameters/#infer-only-parameters). It's never\n", "specified directly by the user, but always inferred from context. The\n", - "`lifetime` value is often inferred, as well. For example, the following code\n", - "creates a [`Reference`](/mojo/stdlib/memory/reference/Reference) to an existing\n", - "value, but doesn't need to specify a lifetime—the `lifetime` is inferred from\n", - "the variable passed in to the reference." + "`origin` value is often inferred, as well. For example, the following code\n", + "creates a [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an existing\n", + "value, but doesn't need to specify an origin—the `origin` is inferred from\n", + "the variable passed in to the `address_of()` method." ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ - "from memory import Reference\n", + "from memory import Pointer\n", "\n", - "def use_reference():\n", + "def use_pointer():\n", " a = 10\n", - " r = Reference(a)" + " ptr = Pointer.address_of(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A final type of origin value is an `OriginSet`. As the name suggests, an \n", + "`OriginSet` represents a group of origins. " ] }, { @@ -175,89 +169,97 @@ "metadata": {}, "source": [ "\n", - "### Lifetime values\n", + "### Origin values\n", "\n", - "Most `lifetime` values are created by the compiler. As a developer, there are a\n", - "few ways to specify `lifetime` values:\n", + "Most origin values are created by the compiler. As a developer, there are a\n", + "few ways to specify origin values:\n", "\n", - "- Static lifetimes. The `ImmutableStaticLifetime` and `MutableStaticLifetime`\n", - " aliases are lifetimes that last for the duration of the program. \n", - "- The `__origin_of()` magic function, which returns the lifetime associated\n", + "- Static origin. The `StaticConstantOrigin`\n", + " alias is an origin value representing immutable values that that last for the\n", + " duration of the program. String literal values have a `StaticConstantOrigin`.\n", + "- The `__origin_of()` magic function, which returns the origin associated\n", " with the value (or values) passed in.\n", - "- Inferred lifetime. You can use inferred parameters to capture the lifetime\n", + "- Inferred origin. You can use inferred parameters to capture the origin\n", " of a value passed in to a function.\n", + "- Wildcard origins. The `ImmutableAnyOrigin` and `MutableAnyOrigin` aliases\n", + " are special cases indicating a reference that might access any live value.\n", "\n", - "#### Static lifetimes\n", + "#### Static origins\n", "\n", - "You can use the static lifetimes `ImmutableStaticLifetime` and \n", - "`MutableStaticLifetime` when you have a value that should never be destroyed;\n", - "or when there's no way to construct a meaningful `lifetime` for a value.\n", + "You can use the static origin `StaticConstantOrigin` when you have a \n", + "value that exists for the entire duration of the program.\n", "\n", - "For an example of the first case, the `StringLiteral` method\n", + "For example, the `StringLiteral` method\n", "[`as_string_slice()`](/mojo/stdlib/builtin/string_literal/StringLiteral#as_string_slice)\n", "returns a [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) pointing\n", "to the original string literal. String literals are static—they're allocated at\n", "compile time and never destroyed—so the slice is created with an immutable,\n", - "static lifetime.\n", - "\n", - "Converting an\n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) into a\n", - "`Reference` is an example of the second case: the `UnsafePointer`'s data\n", - "doesn't carry a `lifetime`—one reason that it's considered unsafe—but you need\n", - "to specify a `lifetime` when creating a `Reference`. In this case, there's no\n", - "way to construct a meaningful `lifetime` value, so the new `Reference` is\n", - "constructed with a `MutableStaticLifetime`. Mojo won't destroy this value\n", - "automatically. As with any value stored using a pointer, it's up to the user to\n", - "explicitly [destroy the\n", - "value](/mojo/manual/pointers#destroying-or-removing-values).\n", - "\n", - "#### Derived lifetimes\n", - "\n", - "Use the `__origin_of()` magic function to obtain a value's lifetime. This can \n", - "be useful, for example, when creating a container type. Consider the `List`\n", - "type. Subscripting into a list (`list[4]`) returns a reference to the item at\n", - "the specified position. The signature of the `__getitem__()` method that's\n", - "called to return the subscripted item looks like this:\n", + "static origin.\n", + "\n", + "#### Derived origins\n", + "\n", + "Use the `__origin_of(value)` operator to obtain a value's origin. The\n", + "argument to `__origin_of()` can take an arbitrary expression:\n", "\n", "```mojo\n", - "fn __getitem__(ref [_]self, idx: Int) -> ref [self] T:\n", + "__origin_of(self)\n", + "__origin_of(x.y)\n", + "__origin_of(foo())\n", "```\n", "\n", - "The syntax may be unfamiliar—`ref` arguments and `ref` return values are\n", - "described in the following sections. For now it's enough to know that \n", - "the return value is a reference of type `T` (where `T` is the element type\n", - "stored in the list), and the reference has the same lifetime as the list itself.\n", - "This means that as long as you hold the reference, the underlying list won't be\n", - "destroyed.\n", + "The `__origin_of()` operator is analyzed statically at compile time;\n", + "The expression passed to `__origin_of()` is never evaluated. (For example, \n", + "when the compiler analyzes `__origin_of(foo())`, it doesn't run the `foo()`\n", + "function.)\n", "\n", - ":::note\n", + "The following struct stores a string value using a \n", + "[`Box`](/mojo/stdlib/memory/box/Box): a smart pointer that \n", + "holds an owned value. The `as_ptr()` method returns a `Pointer` to the stored\n", + "string, using the same origin as the original `Box`." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "from memory import Box, Pointer\n", "\n", - "Ideally the returned reference's `lifetime` would be linked to the individual\n", - "list item, rather than the list itself. Mojo doesn't yet have a mechanism to \n", - "express this relationship.\n", + "struct BoxedString:\n", + " var box: Box[String]\n", "\n", - ":::\n", + " fn __init__(inout self, value: String):\n", + " self.box = Box(value)\n", "\n", - "#### Inferred lifetimes\n", + " fn as_ptr(self) -> Pointer[String, __origin_of(self.box)]:\n", + " return Pointer.address_of(self.box[])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Inferred origins\n", "\n", - "The other common way to access a lifetime value is to _infer_ it from the\n", + "The other common way to access an origin value is to _infer_ it from the\n", "the arguments passed to a function or method. For example, the `Span` type\n", - "has an associated `lifetime`:\n", + "has an associated `origin`:\n", "\n", "```mojo\n", "struct Span[\n", " is_mutable: Bool, //,\n", " T: CollectionElement,\n", - " lifetime: AnyLifetime[is_mutable].type,\n", + " origin: Origin[is_mutable].type,\n", "](CollectionElementNew):\n", " \"\"\"A non owning view of contiguous data.\n", "```\n", "\n", "One of its constructors creates a `Span` from an existing `List`, and infers\n", - "its `lifetime` value from the list:\n", + "its `origin` value from the list:\n", "\n", "```mojo\n", - " fn __init__(inout self, ref [lifetime]list: List[T, *_]):\n", + " fn __init__(inout self, ref [origin]list: List[T, *_]):\n", " \"\"\"Construct a Span from a List.\n", "\n", " Args:\n", @@ -265,7 +267,13 @@ " \"\"\"\n", " self._data = list.data\n", " self._len = len(list)\n", - "```\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "## Working with references\n", "\n", @@ -273,12 +281,11 @@ "reference with parametric mutability. That is, they can be either mutable or \n", "immutable.\n", "\n", - "These references shouldn't be confused with the `Reference` type, which is\n", - "basically a safe pointer type. A `Reference` needs to be dereferenced, like a \n", - "pointer, to access the underlying value. A `ref` argument, on the other hand,\n", - "looks like a `borrowed` or `inout` argument inside the function. A `ref` return\n", - "value looks like any other return value to the calling function, but it is a\n", - "_reference_ to an existing value, not a copy.\n", + "From inside the called function, a `ref` argument looks like a `borrowed` or\n", + "`inout` argument. \n", + "\n", + "A `ref` return value looks like any other return value to the calling function,\n", + "but it is a _reference_ to an existing value, not a copy.\n", "\n", "### `ref` arguments\n", "\n", @@ -298,35 +305,41 @@ "\n", "The syntax for a `ref` argument is:\n", "\n", - "ref [origin] argName: argType\n", + "ref [origin_specifier] arg_name: arg_type\n", "\n", - "The `lifetime` parameter passed inside the square brackets can be replaced with\n", - "an underscore character (`_`) to indicate that the parameter is _unbound_. Think\n", - "of it as a wildcard that will accept any lifetime:" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [], - "source": [ - "def add_ref(ref [_] a: Int, b: Int) -> Int:\n", - " return a+b" + "The origin specifier passed inside the square brackets can be either:\n", + "\n", + "- An origin value.\n", + "- An arbitrary expression, which is treated as shorthand for \n", + " `__origin_of(expression)`. In other words, the following declarations are\n", + " equivalent:\n", + "\n", + " ```mojo\n", + " ref [__origin_of(self)]\n", + " ref [self]\n", + " ```\n", + " \n", + "- An underscore character (`_`) to indicate that the origin is _unbound_. You\n", + " can think of the underscore as a wildcard that will accept any origin:\n", + "\n", + " ```mojo\n", + " def add_ref(ref [_] a: Int, b: Int) -> Int:\n", + " return a+b\n", + " ```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "You can also name the lifetime explicitly. This is useful if you want to specify\n", - "an `ImmutableLifetime` or `MutableLifetime`, or if you want to bind to\n", + "You can also name the origin explicitly. This is useful if you want to specify\n", + "an `ImmutableOrigin` or `MutableLOrigin`, or if you want to bind to\n", "the `is_mutable` parameter." ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -341,8 +354,8 @@ "source": [ "def take_str_ref[\n", " is_mutable: Bool, //,\n", - " life: AnyLifetime[is_mutable].type\n", - " ](ref [life] s: String):\n", + " origin: Origin[is_mutable].type\n", + " ](ref [origin] s: String):\n", " @parameter\n", " if is_mutable:\n", " print(\"Mutable: \" + s)\n", @@ -383,17 +396,17 @@ "- The mutable reference is more efficient—a single update isn't broken up across\n", " two methods. However, the referenced value must be in memory.\n", " \n", - "- A `__getitem__()`/`__setitem__()` pair allows for arbitrary to be run when\n", - " values are retrieved and set. For example, `__setitem__()` can validate or \n", - " constrain input values.\n", + "- A `__getitem__()`/`__setitem__()` pair allows for arbitrary code to be run \n", + " when values are retrieved and set. For example, `__setitem__()` can validate\n", + " or constrain input values.\n", "\n", - "For example, in the following example, `NameList` has a `get()` method\n", + "For example, in the following example, `NameList` has a `__getitem__()` method\n", "that returns a reference: " ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -415,7 +428,7 @@ " self.names.append(name[])\n", "\n", " def __getitem__(ref [_] self: Self, index: Int) ->\n", - " ref [self] String:\n", + " ref [self.names] String:\n", " if (index >=0 and index < len(self.names)):\n", " return self.names[index]\n", " else:\n", @@ -446,31 +459,43 @@ "\n", "```mojo\n", "name = list[2]\n", + "name += \"?\"\n", "```\n", "\n", "Since a variable needs to own its value, `name` would end up with an owned \n", - "_copy_ of the value that `list[2]` returns. Mojo doesn't currently have \n", + "_copy_ of the referenced value. Mojo doesn't currently have \n", "syntax to express that you want to keep the original reference in `name`. This\n", "will be added in a future release.\n", "\n", - "In cases where you need to be able to assign the return value to a variable—for\n", - "example, an iterator which will be used in a `for..in` loop—you might consider \n", - "returning a `Reference` instead of a `ref` return value. For example, see the \n", - "[iterator for the `List` \n", - "type](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L60).\n", - "You can assign a `Reference` to a variable, but you need to use the dereference\n", - "operator (`[]`) to access the underlying value." + "If you're working with an API that returns a reference, and you want to avoid\n", + "copying the referenced value, you can use a\n", + "[`Pointer`](/mojo/stdlib/memory/reference/Pointer) to hold an indirect reference.\n", + "You can assign a `Pointer` to a variable, but you need to use the dereference\n", + "operator (`[]`) to access the underlying value.\n", + "\n", + "```mojo\n", + "name_ptr = Pointer.address_of(list[2])\n", + "name_ptr[] += \"?\"\n", + "```\n", + "\n", + "Similarly, when designing an API you might want to return a `Pointer` instead of\n", + "a `ref` to allow users to assign the return value to a variable. For example, \n", + "iterators for the standard library collections return pointers, so they can be\n", + "used in `for..in` loops:" ] }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "1\n", + "2\n", + "3\n", "1\n", "2\n", "3\n" @@ -479,8 +504,19 @@ ], "source": [ "nums = List(1, 2, 3)\n", - "for item in nums: # List iterator returns a Reference\n", - " print(item[])\n" + "for item in nums: # List iterator returns a Pointer, which must be dereferenced\n", + " print(item[])\n", + "for i in range(len(nums)):\n", + " print(nums[i]) # List __getitem__() returns a ref" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(You can find the code for the \n", + "`List` iterator in the [Mojo\n", + "repo](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L63).)\n" ] }, { @@ -498,7 +534,7 @@ " ref [self] String:\n", "```\n", "\n", - "Since the `lifetime` of the return value is tied to the lifetime of `self`, the\n", + "Since the `origin` of the return value is tied to the origin of `self`, the\n", "returned reference will be mutable if the method was called using a\n", "mutable reference. The method still works if you have an immutable reference\n", "to the `NameList`, but it returns an immutable reference:" @@ -506,7 +542,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 34, "metadata": {}, "outputs": [ { diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 4d9ffd533c..9601db72d2 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -39,6 +39,31 @@ "functions." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ownership summary\n", + "\n", + "The fundamental rules that make Mojo's ownership model work are the following:\n", + "\n", + "- Every value has only one owner at a time.\n", + "- When the lifetime of the owner ends, Mojo destroys the value.\n", + "- If there are outstanding references to a value, Mojo extends the lifetime of\n", + " the owner.\n", + "\n", + "### Variables and references \n", + "\n", + "A variable _owns_ its value. A struct owns its fields. \n", + "\n", + "A _reference_ allows you to access a value owned by another variable. A\n", + "reference can have either mutable access or immutable access to that value.\n", + "\n", + "Mojo references are created when you call a function: function arguments can be\n", + "passed as mutable or immutable references. A function can also return a\n", + "reference instead of returning a value." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -73,16 +98,17 @@ "- `inout`: The function receives a **mutable reference**. This means the\n", " function can read and mutate the original value (it is *not* a copy).\n", " \n", - "- `owned`: The function takes **ownership**. This means the function has\n", - " exclusive ownership of the argument. Often, this also implies that the caller\n", - " should transfer ownership to this function, but that's not always what\n", - " happens and this might instead be a copy (as you'll learn below).\n", - "\n", - "- `ref`: The function gets a reference with an associated lifetime. The\n", - " reference can be either mutable or immutable. You can think of `ref` arguments\n", - " as a generalization of the `borrowed` and `inout` conventions. `ref` arguments\n", - " are an advanced topic, and they're described in more detail in [Lifetimes and \n", - " references](/mojo/manual/values/lifetimes).\n", + "- `owned`: The function takes **ownership** of a value. This means the function\n", + " has exclusive ownership of the argument. The caller might choose to transfer\n", + " ownership of an existing value to this function, but that's not always what\n", + " happens. The callee might receive a newly-created value, or a copy of an\n", + " existing value. \n", + "\n", + "- `ref`: The function gets a reference with an parametric mutability: that is,\n", + " the reference can be either mutable or immutable. You can think of `ref` \n", + " arguments as a generalization of the `borrowed` and `inout` conventions. \n", + " `ref` arguments are an advanced topic, and they're described in more detail in\n", + " [Lifetimes and references](/mojo/manual/values/lifetimes).\n", "\n", "For example, this function has one argument that's a mutable\n", "reference and one that's immutable:" @@ -109,39 +135,7 @@ "metadata": {}, "source": [ "You've probably already seen some function arguments that don't declare a\n", - "convention. by default, all arguments are `borrowed`. But `def` and `fn` \n", - "functions treat `borrowed` arguments somewhat differently:\n", - "\n", - "\n", - "- In an [`fn` function](/mojo/manual/functions#fn-functions), the function\n", - " always receives an immutable reference. If you want a mutable copy, you can\n", - " assign it to a local variable:\n", - "\n", - " ```mojo\n", - " var my_copy = borrowed_arg\n", - " ```\n", - "\n", - "- In a [`def` function](/mojo/manual/functions#def-functions), if the \n", - " function mutates the value, the function receives a mutable copy of the \n", - " argument. Otherwise, it receives an immutable reference. This allows you to\n", - " treat arguments as mutable, but avoid the overhead of making extra copies when\n", - " they're not needed.\n", - "\n", - "The difference between `borrowed` and `owned` in a `def` function may be a\n", - "little subtle: \n", - "\n", - "- In a `def` function, a `borrowed` argument is received as an immutable\n", - " reference, unless it's mutated in the body of the function. This eliminates\n", - " unneeded copies, but maintains the Python expectation that arguments are \n", - " mutable.\n", - "\n", - "- The `borrowed` argument always gets an immutable reference or a local copy.\n", - " You can't transfer a value into a `borrowed` argument.\n", - "\n", - "- The `owned` argument always gets a uniquely owned value, which may have been\n", - " copied or transferred from the callee. Using `owned` arguments without the \n", - " transfer sigil (`^`) usually results in values being copied.\n", - "\n", + "convention. by default, all arguments are `borrowed`. \n", "In the following sections, we'll explain each of these argument conventions in\n", "more detail." ] @@ -150,51 +144,27 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Ownership summary\n", - "\n", - "The fundamental rules that make Mojo's ownership model work are the following:\n", - "\n", - "- Every value has only one owner at a time.\n", - "- When the lifetime of the owner ends, Mojo destroys the value.\n", - "- If there are outstanding references to a value, Mojo keeps the value alive.\n", - "\n", - "In the future, the Mojo lifetime checker will enforce reference exclusivity, so\n", - "that only one mutable reference to a value can exist at a time. **This is not\n", - "currently enforced.**\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Borrowed arguments (`borrowed`)\n", + "- In a [`def` function](/mojo/manual/functions#def-functions), if you mutate\n", + " the value in the body of the function, the function receives a mutable copy of\n", + " the argument. Otherwise, it receives an immutable reference. This allows you\n", + " to treat arguments as mutable, but avoid the overhead of making extra copies when\n", + " they're not needed.\n", "\n", - "The `borrowed` convention is the default for all arguments.\n", + "- In an [`fn` function](/mojo/manual/functions#fn-functions), the function\n", + " always receives an immutable reference. If you want a mutable copy, you can\n", + " assign it to a local variable:\n", "\n", - "In `fn` functions, a `borrowed` argument is received as an immutable reference.\n", + " ```mojo\n", + " var my_copy = borrowed_arg\n", + " ```\n", "\n", - "In `def` functions, you can treat a `borrowed` argument as mutable or immutable.\n", - "If you mutate the argument in the body of the function, you get a mutable copy\n", - "of the original value. If you don't mutate the argument, you get an immutable\n", - "reference, as in an `fn` function.\n", + "In both cases, the original value on the caller side can't be changed by the\n", + "callee.\n", "\n", "For example:" ] @@ -234,9 +204,6 @@ "when handling large or expensive-to-copy values, because the copy constructor\n", "and destructor are not invoked for a borrow.\n", "\n", - "To avoid expensive copies, types should only be implicitly copyable if the copy\n", - "operation is inexpensive.\n", - "\n", "### Compared to C++ and Rust\n", "\n", "Mojo's borrowed argument convention is similar in some ways to passing an\n", @@ -255,8 +222,6 @@ "when compared to languages like C++ and Rust, and moves this optimization from\n", "every call site to a declaration on the type definition.\n", "\n", - "In the future, Mojo's lifetime checker will enforce the exclusivity of\n", - "mutable references, similar to Rust.\n", "The major difference between Rust and Mojo is that Mojo does not require a\n", "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", "passing small values, and Rust defaults to moving values instead of passing\n", @@ -378,7 +343,7 @@ "fn invalid_access():\n", " var my_string = str(\"o\")\n", "\n", - " # warning: passing `my_string` inout is invalid since it is also passed\n", + " # error: passing `my_string` inout is invalid since it is also passed\n", " # borrowed.\n", " append_twice(my_string, my_string)\n", " print(my_string)\n", @@ -400,12 +365,10 @@ " print(my_string)\n", "```\n", "\n", - ":::note Only a warning\n", - "\n", - "Aliasing a mutable reference produces a warning in v24.5. This will change to an\n", - "error in a subsequent release.\n", - "\n", - ":::" + "Note that argument exclusivity isn't enforced for register-passable trivial\n", + "types (like `Int` and `Bool`), because they are always passed by copy. When\n", + "passing the same value into two `Int` arguments, the callee will receive two\n", + "copies of the value." ] }, { @@ -443,7 +406,7 @@ " def take(owned s: String):\n", " pass\n", "\n", - " take(str(\"A brand-new String!\"))\n", + " take(String(\"A brand-new String!\"))\n", " ```\n", "\n" ] @@ -573,6 +536,24 @@ ":::" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Borrowed versus owned in `def` functions\n", + "\n", + "The difference between `borrowed` and `owned` in a `def` function may be a\n", + "little subtle. In both cases, you can end up with a uniquely-owned value that's\n", + "a copy of the original value.\n", + "\n", + "- The `borrowed` argument always gets an immutable reference or a local copy.\n", + " You can't transfer a value into a `borrowed` argument.\n", + "\n", + "- The `owned` argument always gets a uniquely owned value, which may have been\n", + " copied or transferred from the callee. Using `owned` arguments without the \n", + " transfer sigil (`^`) usually results in values being copied." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -589,7 +570,7 @@ " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", " transferred into a function as an `owned` argument, _and_ the original\n", " variable's lifetime ends at the same point (with or without use of the `^`\n", - " transfer operator).\n", + " transfer sigil).\n", "\n", "- If a type implements the [copy \n", " constructor](/mojo/manual/lifecycle/life#move-constructor), `__copyinit__()`\n", diff --git a/docs/roadmap.md b/docs/roadmap.md index 5fb1ec2149..52a20d257b 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -104,8 +104,9 @@ like: - Capture declarations in closures. - Lifetime checker: complain about invalid mutable references. +- Lifetime checker: enforce argument exclusivity for mutable references. -Mojo has support for a safe `Reference` type, and it is used in the standard +Mojo has support for a safe `Pointer` type, and it is used in the standard library, but it is still under active development and not very pretty or nice to use right now. @@ -379,73 +380,6 @@ Mojo currently supports similar functionality through the [`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. -### Parameter closure captures are unsafe references - -You may have seen nested functions, or "closures", annotated with the -`@parameter` decorator. This creates a "parameter closure", which behaves -differently than a normal "stateful" closure. A parameter closure declares a -compile-time value, similar to an `alias` declaration. That means parameter -closures can be passed as parameters: - -```mojo -fn take_func[f: fn() capturing -> Int](): - pass - -fn call_it(a: Int): - @parameter - fn inner() -> Int: - return a # capture 'a' - - take_func[inner]() # pass 'inner' as a parameter -``` - -Parameter closures can even be parametric and capturing: - -```mojo -fn take_func[f: fn[a: Int]() capturing -> Int](): - pass - -fn call_it(a: Int): - @parameter - fn inner[b: Int]() -> Int: - return a + b # capture 'a' - - take_func[inner]() # pass 'inner' as a parameter -``` - -However, note that parameter closures are always capture by *unsafe* reference. -Mojo's lifetime tracking is not yet sophisticated enough to form safe references -to objects (see above section). This means that variable lifetimes need to be -manually extended according to the lifetime of the parametric closure: - -```mojo -fn print_it[f: fn() capturing -> String](): - print(f()) - -fn call_it(): - var s: String = "hello world" - @parameter - fn inner() -> String: - return s # 's' captured by reference, so a copy is made here - # lifetime tracker destroys 's' here - - print_it[inner]() # crash! 's' has been destroyed -``` - -The lifetime of the variable can be manually extended by discarding it -explicitly. - -```mojo -fn call_it(): - var s: String = "hello world" - @parameter - fn inner() -> String: - return s - - print_it[inner]() - _ = s^ # discard 's' explicitly -``` - ### The standard library has limited exceptions use For historic and performance reasons, core standard library types typically do From f79202e8b8efb3b1772afde00774cd13dddddc8d Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 31 Oct 2024 15:14:34 -0600 Subject: [PATCH 1821/2019] [stdlib] Remove explicit imports of `doc_private` As of recently, the `doc_private` decorator is auto-imported via the `prelude` module, so there's no need to explicitly import this. Remove the explicit imports. MODULAR_ORIG_COMMIT_REV_ID: 1a47e9f0475dc48326f8d15ddf2e0e147ae84950 --- stdlib/src/builtin/bool.mojo | 1 - stdlib/src/builtin/builtin_list.mojo | 1 - stdlib/src/builtin/error.mojo | 1 - stdlib/src/builtin/int.mojo | 1 - stdlib/src/builtin/int_literal.mojo | 1 - stdlib/src/collections/optional.mojo | 1 - stdlib/src/math/math.mojo | 1 - stdlib/src/memory/maybe_uninitialized.mojo | 1 - stdlib/src/memory/pointer.mojo | 2 -- stdlib/src/memory/unsafe_pointer.mojo | 1 - stdlib/src/python/python_object.mojo | 1 - stdlib/src/utils/index.mojo | 1 - 12 files changed, 13 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index c8ca50febd..aebc43a7a8 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -15,7 +15,6 @@ These are Mojo built-ins, so you don't need to import them. """ -from documentation import doc_private from collections import Set, List from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 25b487dbd3..28a3eed902 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,6 @@ These are Mojo built-ins, so you don't need to import them. """ -from documentation import doc_private from memory import Pointer, UnsafePointer diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 43bc106ba3..a974d6a8c2 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -20,7 +20,6 @@ from sys.ffi import c_char from memory import UnsafePointer, memcpy from memory.memory import _free -from documentation import doc_private from utils import StringRef diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 62b8e3436a..ca485958a5 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,7 +17,6 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement -from documentation import doc_private from math import Ceilable, CeilDivable, Floorable, Truncable from hashlib.hash import _hash_simd from hashlib._hasher import _HashableWithHasher, _Hasher diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 7aff828a61..90b3850ddb 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,7 +12,6 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" -from documentation import doc_private from math import Ceilable, CeilDivable, Floorable, Truncable diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index ac50c7b4a1..b4a1b00c74 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -31,7 +31,6 @@ print(d) # prints 2 ``` """ -from documentation import doc_private from os import abort from utils import Variant diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index d6d0a5bb51..11f5cb955c 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -19,7 +19,6 @@ from math import floor ``` """ -from documentation import doc_private from collections import List from sys._assembly import inlined_assembly from sys.ffi import _external_call_const diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index e2ab9d8035..33689c6260 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -12,7 +12,6 @@ # ===----------------------------------------------------------------------=== # from os import abort -from documentation import doc_private struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index e40f50ecfa..6a4db76cac 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -19,8 +19,6 @@ from memory import Pointer ``` """ -from documentation import doc_private - # ===----------------------------------------------------------------------===# # AddressSpace # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 643f478a67..ae7384a1d5 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,7 +19,6 @@ from memory import UnsafePointer ``` """ -from documentation import doc_private from sys import alignof, sizeof, triple_is_nvidia_cuda from sys.intrinsics import ( _mlirtype_is_eq, diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 5cb15ecdf0..0c05cc950c 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -19,7 +19,6 @@ from python import PythonObject ``` """ -from documentation import doc_private from sys.intrinsics import _type_is_eq from memory import UnsafePointer diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 5fc3d05740..93eb5dbc8d 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -22,7 +22,6 @@ from utils import IndexList from sys import bitwidthof from builtin.dtype import _int_type_of_width, _uint_type_of_width -from documentation import doc_private from builtin.io import _get_dtype_printf_format, _snprintf from collections.string import _calc_initial_buffer_size From 6bf3cda4cf1d5990a91684944170d50f0ecc395c Mon Sep 17 00:00:00 2001 From: soraros Date: Fri, 1 Nov 2024 10:25:33 -0600 Subject: [PATCH 1822/2019] [External] [stdlib] Add `__init__(*, from_bits)` and `to_bits()` to `SIMD` (#50172) [External] [stdlib] Add `__init__(*, from_bits)` and `to_bits()` to `SIMD` Co-authored-by: soraros Closes modularml/mojo#3680 MODULAR_ORIG_COMMIT_REV_ID: d36f1f1fd14105f89cff09fb77c6bcf5be662a03 --- stdlib/src/builtin/simd.mojo | 72 ++++++++++++++++++++-------------- stdlib/src/hashlib/_ahash.mojo | 2 +- stdlib/src/math/math.mojo | 42 ++++++++------------ stdlib/src/memory/memory.mojo | 1 - stdlib/src/utils/numerics.mojo | 5 ++- 5 files changed, 63 insertions(+), 59 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 1febc71219..9678701dcd 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -57,6 +57,7 @@ from sys import sizeof, alignof from .dtype import ( _get_dtype_printf_format, _integral_type_of, + _unsigned_integral_type_of, _scientific_notation_digits, ) from .io import _printf, _snprintf_scalar @@ -492,6 +493,20 @@ struct SIMD[type: DType, size: Int]( ) ) + fn __init__[ + int_type: DType, // + ](inout self, *, from_bits: SIMD[int_type, size]): + """Initializes the SIMD vector from the bits of an integral SIMD vector. + + Parameters: + int_type: The integral type of the input SIMD vector. + + Args: + from_bits: The SIMD vector to copy the bits from. + """ + constrained[int_type.is_integral(), "the SIMD type must be integral"]() + self = bitcast[type, size](from_bits) + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# @@ -776,9 +791,7 @@ struct SIMD[type: DType, size: Int]( # As a workaround, we roll our own implementation @parameter if has_neon() and type is DType.bfloat16: - var int_self = bitcast[_integral_type_of[type](), size](self) - var int_rhs = bitcast[_integral_type_of[type](), size](rhs) - return int_self == int_rhs + return self.to_bits() == rhs.to_bits() else: return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( self.value, rhs.value @@ -801,9 +814,7 @@ struct SIMD[type: DType, size: Int]( # As a workaround, we roll our own implementation. @parameter if has_neon() and type is DType.bfloat16: - var int_self = bitcast[_integral_type_of[type](), size](self) - var int_rhs = bitcast[_integral_type_of[type](), size](rhs) - return int_self != int_rhs + return self.to_bits() != rhs.to_bits() else: return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( self.value, rhs.value @@ -1514,9 +1525,9 @@ struct SIMD[type: DType, size: Int]( self ) - alias integral_type = FPUtils[type].integral_type - var m = self._float_to_bits[integral_type]() - return (m & (FPUtils[type].sign_mask() - 1))._bits_to_float[type]() + # FIXME: This should be an alias + var mask = FPUtils[type].exponent_mantissa_mask() + return Self(from_bits=self.to_bits() & mask) else: return (self < 0).select(-self, self) @@ -1726,32 +1737,31 @@ struct SIMD[type: DType, size: Int]( if size > 1: writer.write("]") + # FIXME: `_integral_type_of` doesn't work with `DType.bool`. @always_inline - fn _bits_to_float[dest_type: DType](self) -> SIMD[dest_type, size]: - """Bitcasts the integer value to a floating-point value. - - Parameters: - dest_type: DType to bitcast the input SIMD vector to. - - Returns: - A floating-point representation of the integer value. - """ - alias integral_type = FPUtils[type].integral_type - return bitcast[dest_type, size](self.cast[integral_type]()) - - @always_inline - fn _float_to_bits[dest_type: DType](self) -> SIMD[dest_type, size]: - """Bitcasts the floating-point value to an integer value. + fn to_bits[ + int_dtype: DType = _integral_type_of[type]() + ](self) -> SIMD[int_dtype, size]: + """Bitcasts the SIMD vector to an integer SIMD vector. Parameters: - dest_type: DType to bitcast the input SIMD vector to. + int_dtype: The integer type to cast to. Returns: An integer representation of the floating-point value. """ - alias integral_type = FPUtils[type].integral_type - var v = bitcast[integral_type, size](self) - return v.cast[dest_type]() + constrained[ + int_dtype.is_integral(), "the target type must be integral" + ]() + constrained[ + bitwidthof[int_dtype]() >= bitwidthof[type](), + ( + "the target integer type must be at least as wide as the source" + " type" + ), + ]() + + return bitcast[_integral_type_of[type](), size](self).cast[int_dtype]() fn _floor_ceil_trunc_impl[intrinsic: StringLiteral](self) -> Self: constrained[ @@ -3221,14 +3231,16 @@ fn _floor(x: SIMD) -> __type_of(x): alias bitwidth = bitwidthof[x.type]() alias exponent_width = FPUtils[x.type].exponent_width() alias mantissa_width = FPUtils[x.type].mantissa_width() + # FIXME: GH issue #3613 + # alias mask = FPUtils[x.type].exponent_mask() alias mask = (1 << exponent_width) - 1 alias bias = FPUtils[x.type].exponent_bias() alias shift_factor = bitwidth - exponent_width - 1 - var bits = bitcast[integral_type, x.size](x) + bits = x.to_bits() var e = ((bits >> mantissa_width) & mask) - bias bits = (e < shift_factor).select( bits & ~((1 << (shift_factor - e)) - 1), bits, ) - return bitcast[x.type, x.size](bits) + return __type_of(x)(from_bits=bits) diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index 9770fe84ac..d54036d0fe 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -163,7 +163,7 @@ struct AHasher[key: U256](_Hasher): @parameter if new_data.type.is_floating_point(): - v64 = new_data._float_to_bits[DType.uint64]() + v64 = new_data.to_bits().cast[DType.uint64]() else: v64 = new_data.cast[DType.uint64]() diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 11f5cb955c..9e2d918eb7 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -418,11 +418,9 @@ fn exp2[ if type not in (DType.float32, DType.float64): return exp2(x.cast[DType.float32]()).cast[type]() - alias integral_type = FPUtils[type].integral_type - var xc = x.clamp(-126, 126) - var m = xc.cast[integral_type]() + var m = xc.cast[__type_of(x.to_bits()).type]() xc -= m.cast[type]() @@ -436,11 +434,9 @@ fn exp2[ 1.33336498402e-3, ), ](xc) - - return ( - r._float_to_bits[integral_type]() - + (m << FPUtils[type].mantissa_width()) - )._bits_to_float[type]() + return __type_of(r)( + from_bits=(r.to_bits() + (m << FPUtils[type].mantissa_width())) + ) # ===----------------------------------------------------------------------=== # @@ -504,11 +500,9 @@ fn _ldexp_impl[ return res alias integral_type = FPUtils[type].integral_type - var m: SIMD[integral_type, simd_width] = ( - exp.cast[integral_type]() + FPUtils[type].exponent_bias() - ) + var m = exp.cast[integral_type]() + FPUtils[type].exponent_bias() - return x * (m << FPUtils[type].mantissa_width())._bits_to_float[type]() + return x * __type_of(x)(from_bits=m << FPUtils[type].mantissa_width()) @always_inline @@ -629,8 +623,8 @@ fn exp[ @always_inline fn _frexp_mask1[ - simd_width: Int, type: DType, integral_type: DType -]() -> SIMD[integral_type, simd_width]: + simd_width: Int, type: DType +]() -> SIMD[_integral_type_of[type](), simd_width]: @parameter if type is DType.float16: return 0x7C00 @@ -645,8 +639,8 @@ fn _frexp_mask1[ @always_inline fn _frexp_mask2[ - simd_width: Int, type: DType, integral_type: DType -]() -> SIMD[integral_type, simd_width]: + simd_width: Int, type: DType +]() -> SIMD[_integral_type_of[type](), simd_width]: @parameter if type is DType.float16: return 0x3800 @@ -681,22 +675,20 @@ fn frexp[ """ # Based on the implementation in boost/simd/arch/common/simd/function/ifrexp.hpp constrained[type.is_floating_point(), "must be a floating point value"]() - alias integral_type = _integral_type_of[type]() - alias zero = SIMD[type, simd_width](0) + alias T = SIMD[type, simd_width] + alias zero = T(0) alias max_exponent = FPUtils[type].max_exponent() - 2 alias mantissa_width = FPUtils[type].mantissa_width() - var mask1 = _frexp_mask1[simd_width, type, integral_type]() - var mask2 = _frexp_mask2[simd_width, type, integral_type]() - var x_int = x._float_to_bits[integral_type]() + var mask1 = _frexp_mask1[simd_width, type]() + var mask2 = _frexp_mask2[simd_width, type]() + var x_int = x.to_bits() var selector = x != zero var exp = selector.select( (((mask1 & x_int) >> mantissa_width) - max_exponent).cast[type](), zero, ) - var frac = selector.select( - ((x_int & ~mask1) | mask2)._bits_to_float[type](), zero - ) - return StaticTuple[SIMD[type, simd_width], 2](frac, exp) + var frac = selector.select(T(from_bits=x_int & ~mask1 | mask2), zero) + return StaticTuple[size=2](frac, exp) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index ef8d0ba1bf..633025a5d6 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -31,7 +31,6 @@ from sys import ( _libc as libc, ) from collections import Optional -from builtin.dtype import _integral_type_of from memory.pointer import AddressSpace, _GPUAddressSpace # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index a52594be68..6c7133008a 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -152,6 +152,7 @@ struct FPUtils[ Returns: The sign mask. """ + # convert to `Int` first to bypass overflow check return 1 << int(Self.exponent_width() + Self.mantissa_width()) @staticmethod @@ -171,12 +172,12 @@ struct FPUtils[ fn exponent_mantissa_mask() -> Int: """Returns the exponent and mantissa mask of a floating point type. - It is computed by `exponent_mask + mantissa_mask`. + It is computed by `exponent_mask | mantissa_mask`. Returns: The exponent and mantissa mask. """ - return Self.exponent_mask() + Self.mantissa_mask() + return Self.exponent_mask() | Self.mantissa_mask() @staticmethod @always_inline From 7d993ac931296bb52c1b43ba0dae26cd18c819ac Mon Sep 17 00:00:00 2001 From: soraros Date: Fri, 1 Nov 2024 12:39:32 -0600 Subject: [PATCH 1823/2019] [External] [stdlib] Clean up `memory.unsafe` (#48797) [External] [stdlib] Clean up `memory.unsafe` - Make more things infer-only - Remove unnecessary overload - Rename the `bitcast` overload that performs the 'movemask' operation to `pack_mask`. This change is intended to prevent issues where the wrong overload might be selected, and since there is an implicit conversion from scalar to simd at return, the user won't get a type mismatch error to warn them about that. Co-authored-by: soraros Closes modularml/mojo#3588 MODULAR_ORIG_COMMIT_REV_ID: 18e72968cbdadf593e6bc9bba0794e326bada362 --- stdlib/benchmarks/utils/bench_memmem.mojo | 4 +- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 68 ++++++++++------------- stdlib/src/utils/stringref.mojo | 6 +- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index d72a4bddb1..6fd0b16a89 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -18,7 +18,7 @@ from sys import simdwidthof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width -from memory import memcmp, bitcast, UnsafePointer +from memory import memcmp, bitcast, UnsafePointer, pack_bits from utils.stringref import _align_down, _memchr, _memmem @@ -168,7 +168,7 @@ fn _memmem_baseline[ ) for i in range(0, vectorized_end, bool_mask_width): var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle - var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) + var mask = pack_bits(bool_mask) while mask: var offset = int(i + count_trailing_zeros(mask)) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index cc226348fd..1074e5983a 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -16,5 +16,5 @@ from .arc import Arc from .box import Box from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation from .pointer import AddressSpace, Pointer -from .unsafe import bitcast +from .unsafe import bitcast, pack_bits from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 93d7e2266b..82e995de53 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -28,18 +28,21 @@ from sys import bitwidthof @always_inline("nodebug") fn bitcast[ - new_type: DType, new_width: Int, src_type: DType, src_width: Int -](val: SIMD[src_type, src_width]) -> SIMD[new_type, new_width]: + type: DType, + width: Int, //, + new_type: DType, + new_width: Int = width, +](val: SIMD[type, width]) -> SIMD[new_type, new_width]: """Bitcasts a SIMD value to another SIMD value. Constraints: The bitwidth of the two types must be the same. Parameters: + type: The source type. + width: The source width. new_type: The target type. new_width: The target width. - src_type: The source type. - src_width: The source width. Args: val: The source value. @@ -49,13 +52,13 @@ fn bitcast[ source SIMD value. """ constrained[ - bitwidthof[SIMD[src_type, src_width]]() + bitwidthof[SIMD[type, width]]() == bitwidthof[SIMD[new_type, new_width]](), "the source and destination types must have the same bitwidth", ]() @parameter - if new_type == src_type: + if new_type == type: return rebind[SIMD[new_type, new_width]](val) return __mlir_op.`pop.bitcast`[ _type = __mlir_type[ @@ -65,45 +68,31 @@ fn bitcast[ @always_inline("nodebug") -fn bitcast[ - new_type: DType, src_type: DType -](val: SIMD[src_type, 1]) -> SIMD[new_type, 1]: - """Bitcasts a SIMD value to another SIMD value. - - Constraints: - The bitwidth of the two types must be the same. - - Parameters: - new_type: The target type. - src_type: The source type. - - Args: - val: The source value. - - Returns: - A new SIMD value with the specified type and width with a bitcopy of the - source SIMD value. - """ - constrained[ - bitwidthof[SIMD[src_type, 1]]() == bitwidthof[SIMD[new_type, 1]](), - "the source and destination types must have the same bitwidth", - ]() - - return bitcast[new_type, 1, src_type, 1](val) +fn _uint(n: Int) -> DType: + if n == 8: + return DType.uint8 + elif n == 16: + return DType.uint16 + elif n == 32: + return DType.uint32 + else: + return DType.uint64 @always_inline("nodebug") -fn bitcast[ - new_type: DType, src_width: Int -](val: SIMD[DType.bool, src_width]) -> Scalar[new_type]: +fn pack_bits[ + width: Int, //, + new_type: DType = _uint(width), +](val: SIMD[DType.bool, width]) -> Scalar[new_type]: """Packs a SIMD bool into an integer. Constraints: - The bitwidth of the two types must be the same. + The width of the bool vector must be the same as the bitwidth of the + target type. Parameters: + width: The source width. new_type: The target type. - src_width: The source width. Args: val: The source value. @@ -112,8 +101,11 @@ fn bitcast[ A new integer scalar which has the same bitwidth as the bool vector. """ constrained[ - src_width == bitwidthof[Scalar[new_type]](), - "the source and destination types must have the same bitwidth", + width == bitwidthof[Scalar[new_type]](), + ( + "the width of the bool vector must be the same as the bitwidth of" + " the target type" + ), ]() return __mlir_op.`pop.bitcast`[ diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 87732fc351..89c0bb2711 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -17,7 +17,7 @@ from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width from collections.string import _atol, _isspace from hashlib._hasher import _HashableWithHasher, _Hasher -from memory import UnsafePointer, memcmp, bitcast +from memory import UnsafePointer, memcmp, pack_bits from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice from sys.ffi import c_char @@ -698,7 +698,7 @@ fn _memchr[ for i in range(0, vectorized_end, bool_mask_width): var bool_mask = source.load[width=bool_mask_width](i) == first_needle - var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) + var mask = pack_bits(bool_mask) if mask: return source + int(i + count_trailing_zeros(mask)) @@ -742,7 +742,7 @@ fn _memmem[ var eq_last = last_needle == last_block var bool_mask = eq_first & eq_last - var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) + var mask = pack_bits(bool_mask) while mask: var offset = int(i + count_trailing_zeros(mask)) From 308461ac895967cb43bfb6099ae2ef02f0bfc2de Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 1 Nov 2024 13:49:06 -0500 Subject: [PATCH 1824/2019] [stdlib] feat: Add trait for conversion of Python to Mojo values * Add ConvertibleFromPython trait, that types can implement to define logic for constructing an owned Mojo value from a borrowed PythonObject. * Add Python.py_long_as_ssize_t() helper * Add PythonObject._get_type_name() helper, for use in error messages MODULAR_ORIG_COMMIT_REV_ID: 6ed12f789b41912a2fd02788190a5082a7c24824 --- stdlib/src/builtin/_pybind.mojo | 28 ++++++++++++++++++++ stdlib/src/builtin/int.mojo | 15 +++++++++++ stdlib/src/python/_bindings.mojo | 29 ++++++++++++++++----- stdlib/src/python/python.mojo | 39 +++++++++++++++++++++++++++- stdlib/src/python/python_object.mojo | 8 ++++++ stdlib/test/builtin/test_int.mojo | 18 ++++++++++++- 6 files changed, 128 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 31856e9b80..a7f9a1f724 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -28,6 +28,7 @@ from python._cpython import ( ) from python._bindings import ( Pythonable, + ConvertibleFromPython, PyMojoObject, py_c_function_wrapper, check_argument_type, @@ -122,3 +123,30 @@ fn check_and_get_arg[ index: Int, ) raises -> UnsafePointer[T]: return check_argument_type[T](func_name, type_name_id, py_args[index]) + + +fn try_convert_arg[ + T: ConvertibleFromPython +]( + func_name: StringLiteral, + type_name_id: StringLiteral, + py_args: TypedPythonObject["Tuple"], + argidx: Int, +) raises -> T as result: + try: + result = T.try_from_python(py_args[argidx]) + except convert_err: + raise Error( + String.format( + ( + "TypeError: {}() expected argument at position {} to be" + " instance of (or convertible to) Mojo '{}'; got '{}'." + " (Note: attempted conversion failed due to: {})" + ), + func_name, + argidx, + type_name_id, + py_args[argidx]._get_type_name(), + convert_err, + ) + ) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index ca485958a5..9c97d71359 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -25,11 +25,15 @@ from collections.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, ) +from python import Python, PythonObject +from python._cpython import Py_ssize_t +from memory import UnsafePointer from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select from sys import triple_is_nvidia_cuda, bitwidthof +from sys.ffi import OpaquePointer # ===----------------------------------------------------------------------=== # # Indexer @@ -1123,6 +1127,17 @@ struct Int( """ hasher._update_with_simd(Int64(self)) + @doc_private + @staticmethod + fn try_from_python(obj: PythonObject) raises -> Self as result: + """Construct an `Int` from a Python integer value. + + Raises: + An error if conversion failed. + """ + + result = Python.py_long_as_ssize_t(obj) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index e03125a4ac..24d874f9ef 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -13,7 +13,7 @@ from memory import UnsafePointer, Box -from sys.ffi import c_int +from sys.ffi import c_int, OpaquePointer from sys.info import sizeof from os import abort @@ -34,6 +34,26 @@ from python._cpython import ( destructor, ) + +trait ConvertibleFromPython(CollectionElement): + """Denotes a type that can attempt construction from a borrowed Python + object. + """ + + @staticmethod + fn try_from_python(obj: PythonObject) raises -> Self: + """Attempt to construct an instance of this object from a borrowed + Python value. + + Args: + obj: The Python object to convert from. + + Raises: + If conversion was not successful. + """ + ... + + # ===-----------------------------------------------------------------------===# # Mojo Object # ===-----------------------------------------------------------------------===# @@ -339,17 +359,12 @@ fn check_argument_type[ ](type_name_id) if not opt: - var cpython = _get_global_python_itf().cpython() - - var actual_type = cpython.Py_TYPE(obj.unsafe_as_py_object_ptr()) - var actual_type_name = PythonObject(cpython.PyType_GetName(actual_type)) - raise Error( String.format( "TypeError: {}() expected Mojo '{}' type argument, got '{}'", func_name, type_name_id, - str(actual_type_name), + obj._get_type_name(), ) ) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 0044212d74..a0c0878228 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -29,7 +29,13 @@ from memory import UnsafePointer from utils import StringRef from .python_object import PythonObject, TypedPythonObject -from ._cpython import CPython, Py_eval_input, Py_file_input, PyMethodDef +from ._cpython import ( + CPython, + Py_eval_input, + Py_file_input, + PyMethodDef, + Py_ssize_t, +) fn _init_global(ignored: OpaquePointer) -> OpaquePointer: @@ -436,3 +442,34 @@ struct Python: `PythonObject` representing `None`. """ return PythonObject(None) + + # ===-------------------------------------------------------------------===# + # Checked Conversions + # ===-------------------------------------------------------------------===# + + @staticmethod + fn py_long_as_ssize_t(obj: PythonObject) raises -> Py_ssize_t: + """Get the value of a Python `long` object. + + Args: + obj: The Python `long` object. + + Raises: + If `obj` is not a Python `long` object, or if the `long` object + value overflows `Py_ssize_t`. + + Returns: + The value of the `long` object as a `Py_ssize_t`. + """ + var cpython = Python().impl.cpython() + + var long: Py_ssize_t = cpython.PyLong_AsSsize_t( + obj.unsafe_as_py_object_ptr() + ) + + # Disambiguate if this is an error return setinel, or a legitimate + # value. + if long == -1: + Python.throw_python_exception_if_error_state(cpython) + + return long diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 0c05cc950c..cc86926452 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -1529,6 +1529,14 @@ struct PythonObject( fn _get_ptr_as_int(self) -> Int: return self.py_object._get_ptr_as_int() + fn _get_type_name(self) -> String: + var cpython = Python().impl.cpython() + + var actual_type = cpython.Py_TYPE(self.unsafe_as_py_object_ptr()) + var actual_type_name = PythonObject(cpython.PyType_GetName(actual_type)) + + return str(actual_type_name) + # ===-----------------------------------------------------------------------===# # Helper functions diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index ccb1f87da8..8d898798da 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,7 +14,10 @@ from sys.info import bitwidthof -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal, assert_true, assert_false, assert_raises + +from python import PythonObject +from memory import UnsafePointer def test_properties(): @@ -231,6 +234,18 @@ def test_float_conversion(): assert_equal(float(Int(45)), Float64(45)) +def test_conversion_from_python(): + # Test conversion from Python '5' + assert_equal(Int.try_from_python(PythonObject(5)), 5) + + # Test error trying conversion from Python '"str"' + with assert_raises(contains="an integer is required"): + _ = Int.try_from_python(PythonObject("str")) + + # Test conversion from Python '-1' + assert_equal(Int.try_from_python(PythonObject(-1)), -1) + + def main(): test_properties() test_add() @@ -253,3 +268,4 @@ def main(): test_comparison() test_int_uint() test_float_conversion() + test_conversion_from_python() From 175336b9173d4a92a826d835ea5dac8ccb4cae23 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 4 Nov 2024 08:15:30 -0800 Subject: [PATCH 1825/2019] [KGEN][******] Introduce an invariant load into the pop.load op MODULAR_ORIG_COMMIT_REV_ID: f6feec4127b930928b59ae4fa324d4d36319768c --- stdlib/src/memory/unsafe_pointer.mojo | 37 ++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index ae7384a1d5..4b128170bd 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -451,6 +451,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type, width](), volatile: Bool = False, + invariant: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_]) -> SIMD[type, width]: """Loads the value the pointer points to. @@ -462,6 +463,7 @@ struct UnsafePointer[ width: The size of the SIMD vector. alignment: The minimal alignment of the address. volatile: Whether the operation is volatile or not. + invariant: Whether the memory is load invariant. Returns: The loaded value. @@ -470,6 +472,10 @@ struct UnsafePointer[ constrained[ alignment > 0, "alignment must be a positive integer value" ]() + constrained[ + not volatile or volatile ^ invariant, + "both volatile and invariant cannot be set at the same time", + ]() @parameter if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: @@ -489,21 +495,30 @@ struct UnsafePointer[ alignment = alignment.value, isVolatile = __mlir_attr.unit, ]((self + i).address) + elif invariant: + v[i] = __mlir_op.`pop.load`[ + alignment = alignment.value, + isInvariant = __mlir_attr.unit, + ]((self + i).address) else: v[i] = __mlir_op.`pop.load`[alignment = alignment.value]( (self + i).address ) return v + var address = self.bitcast[SIMD[type, width]]().address + @parameter if volatile: return __mlir_op.`pop.load`[ alignment = alignment.value, isVolatile = __mlir_attr.unit - ](self.bitcast[SIMD[type, width]]().address) + ](address) + elif invariant: + return __mlir_op.`pop.load`[ + alignment = alignment.value, isInvariant = __mlir_attr.unit + ](address) else: - return __mlir_op.`pop.load`[alignment = alignment.value]( - self.bitcast[SIMD[type, width]]().address - ) + return __mlir_op.`pop.load`[alignment = alignment.value](address) @always_inline fn load[ @@ -512,6 +527,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type, width](), volatile: Bool = False, + invariant: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], offset: Scalar) -> SIMD[ type, width ]: @@ -526,6 +542,7 @@ struct UnsafePointer[ width: The size of the SIMD vector. alignment: The minimal alignment of the address. volatile: Whether the operation is volatile or not. + invariant: Whether the memory is load invariant. Args: offset: The offset to load from. @@ -535,7 +552,10 @@ struct UnsafePointer[ """ constrained[offset.type.is_integral(), "offset must be integer"]() return self.offset(int(offset)).load[ - width=width, alignment=alignment, volatile=volatile + width=width, + alignment=alignment, + volatile=volatile, + invariant=invariant, ]() @always_inline("nodebug") @@ -546,6 +566,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type, width](), volatile: Bool = False, + invariant: Bool = False, ](self: UnsafePointer[Scalar[type], *_, **_], offset: T) -> SIMD[ type, width ]: @@ -560,6 +581,7 @@ struct UnsafePointer[ width: The size of the SIMD vector. alignment: The minimal alignment of the address. volatile: Whether the operation is volatile or not. + invariant: Whether the memory is load invariant. Args: offset: The offset to load from. @@ -568,7 +590,10 @@ struct UnsafePointer[ The loaded value. """ return self.offset(offset).load[ - width=width, alignment=alignment, volatile=volatile + width=width, + alignment=alignment, + volatile=volatile, + invariant=invariant, ]() @always_inline From 7df16d41d7fc55c8a21fc0b72d845711937970f5 Mon Sep 17 00:00:00 2001 From: soraros Date: Mon, 4 Nov 2024 10:23:35 -0600 Subject: [PATCH 1826/2019] [External] [stdlib] Clean up `b64encode` (1/N) (#50262) [External] [stdlib] Clean up `b64encode` (1/N) - Revised `b64encode` to more closely align with the 2020 paper - Made trivial code look trivial - As a added bonus, this generates better assembly Co-authored-by: soraros Closes modularml/mojo#3737 MODULAR_ORIG_COMMIT_REV_ID: 5a8c10a6ef4f1155e4464ed4e349c0747d600e93 --- stdlib/src/base64/_b64encode.mojo | 281 ++++++++++-------------------- 1 file changed, 93 insertions(+), 188 deletions(-) diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo index 0af12e0069..d867a91be5 100644 --- a/stdlib/src/base64/_b64encode.mojo +++ b/stdlib/src/base64/_b64encode.mojo @@ -14,6 +14,7 @@ """ We make use of the following papers for the implementation, note that there are some small differences. + Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the speed of a memory copy, Software: Practice and Experience 50 (2), 2020. https://arxiv.org/abs/1910.05109 @@ -23,209 +24,95 @@ Instructions, ACM Transactions on the Web 12 (3), 2018. https://arxiv.org/abs/1704.00605 """ -from collections import InlineArray -from memory import memcpy, bitcast, UnsafePointer -from memory.maybe_uninitialized import UnsafeMaybeUninitialized from builtin.simd import _sub_with_saturation +from collections import InlineArray from math.math import _compile_time_iota +from memory import memcpy, bitcast, UnsafePointer +from utils import IndexList +alias Bytes = SIMD[DType.uint8, _] -""" -| 6-bit Value | ASCII Range | Target index | Offset (6-bit to ASCII) | -|-------------|-------------|--------------|-------------------------| -| 0 ... 25 | A ... Z | 13 | 65 | -| 26 ... 51 | a ... z | 0 | 71 | -| 52 ... 61 | 0 ... 9 | 1 ... 10 | -4 | -| 62 | + | 11 | -19 | -| 63 | / | 12 | -16 | -""" -alias UNUSED = 0 + +fn _base64_simd_mask[ + simd_width: Int +](nb_value_to_load: Int) -> SIMD[DType.bool, simd_width]: + alias mask = _compile_time_iota[DType.uint8, simd_width]() + return mask < UInt8(nb_value_to_load) + + +# | |---- byte 2 ----|---- byte 1 ----|---- byte 0 ----| +# | |c₁c₀d₅d₄d₃d₂d₁d₀|b₃b₂b₁b₀c₅c₄c₃c₂|a₅a₄a₃a₂a₁a₀b₅b₄| +# <----------------|----------------|----------------|----------------| +# |31 . . . . . .24|23 . . . . . .16|15 . . . . . .08| 7 6 5 4 3 2 1 0| +# | | +# |---- byte 1 ----|---- byte 2 ----|---- byte 0 ----|---- byte 1 ----| +# |b₃b₂b₁b₀c₅c₄c₃c₂|c₁c₀d₅d₄d₃d₂d₁d₀|a₅a₄a₃a₂a₁a₀b₅b₄|b₃b₂b₁b₀c₅c₄c₃c₂| +# | -------------____________ ------------_____________ | +# | [ C ][ D ] [ A ][ B ] | +# | | +# |--- ascii(d) ---|--- ascii(c) ---|--- ascii(b) ---|--- ascii(a) ---| +# |. . d₅d₄d₃d₂d₁d₀|. . c₅c₄c₃c₂c₁c₀|. . b₅b₄b₃b₂b₁b₀|. . a₅a₄a₃a₂a₁a₀| +fn _6bit_to_byte[width: Int](input: Bytes[width]) -> Bytes[width]: + constrained[width in [4, 8, 16, 32, 64], "width must be between 4 and 64"]() + + fn indices() -> IndexList[width]: + alias perm = List(1, 0, 2, 1) + var res = IndexList[width]() + for i in range(width // 4): + for j in range(4): + res[4 * i + j] = 3 * i + perm[j] + return res + + @always_inline + fn combine[ + mask: Bytes[4], shift: Int + ](shuffled: Bytes[width]) -> Bytes[width]: + var `6bit` = shuffled & _repeat_until[width](mask) + return _rshift_bits_in_u16[shift](`6bit`) + + var shuffled = input.shuffle[mask = indices()]() + var a = combine[ + Bytes[4](0b0000_0000, 0b1111_1100, 0b0000_0000, 0b0000_0000), 10 + ](shuffled) + var b = combine[ + Bytes[4](0b1111_0000, 0b0000_0011, 0b0000_0000, 0b0000_0000), -4 + ](shuffled) + var c = combine[ + Bytes[4](0b0000_0000, 0b0000_0000, 0b1100_0000, 0b0000_1111), 6 + ](shuffled) + var d = combine[ + Bytes[4](0b0000_0000, 0b0000_0000, 0b0011_1111, 0b0000_0000), 8 + ](shuffled) + return a | b | c | d + + +# | 6-bit Value | ASCII Range | Target index | Offset (6-bit to ASCII) | +# |-------------|-------------|--------------|-------------------------| +# | 0 ... 25 | A ... Z | 13 | 65 | +# | 26 ... 51 | a ... z | 0 | 71 | +# | 52 ... 61 | 0 ... 9 | 1 ... 10 | -4 | +# | 62 | + | 11 | -19 | +# | 63 | / | 12 | -16 | # fmt: off -alias TABLE_BASE64_OFFSETS = SIMD[DType.uint8, 16]( +alias UNUSED = 0 +alias OFFSETS = Bytes[16]( 71, # a ... z - -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, # 0 ... 9 + -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, # 0 ... 9 -19, # + -16, # / 65, # A ... Z UNUSED, UNUSED ) -# fmt: on alias END_FIRST_RANGE = 25 alias END_SECOND_RANGE = 51 +# fmt: on -fn _base64_simd_mask[ - simd_width: Int -](nb_value_to_load: Int) -> SIMD[DType.bool, simd_width]: - alias mask = _compile_time_iota[DType.uint8, simd_width]() - return mask < UInt8(nb_value_to_load) - - -fn _repeat_until[ - dtype: DType, input_size: Int, //, target_size: Int -](vector: SIMD[dtype, input_size]) -> SIMD[dtype, target_size]: - @parameter - if target_size == input_size: - var same_vector = rebind[SIMD[dtype, target_size]](vector) - return same_vector - else: - return _repeat_until[target_size](vector.join(vector)) - - -fn _move_first_group_of_6_bits[ - simd_width: Int -](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ - DType.uint8, simd_width -]: - alias mask_1 = _repeat_until[simd_width]( - SIMD[DType.uint8, 4](0b11111100, 0, 0, 0) - ) - var masked_1 = shuffled_vector & mask_1 - var result = masked_1 >> 2 - return result - - -fn _move_second_group_of_6_bits[ - simd_width: Int -](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ - DType.uint8, simd_width -]: - alias mask_2 = _repeat_until[simd_width]( - SIMD[DType.uint8, 4](0b00000011, 0b11110000, 0, 0) - ) - var masked_2 = shuffled_vector & mask_2 - var masked_2_as_uint16 = bitcast[DType.uint16, simd_width // 2](masked_2) - var rotated_2 = bit.rotate_bits_right[4](masked_2_as_uint16) - var result = bitcast[DType.uint8, simd_width](rotated_2) - return result - - -fn _move_third_group_of_6_bits[ - simd_width: Int -](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ - DType.uint8, simd_width -]: - alias mask_3 = _repeat_until[simd_width]( - SIMD[DType.uint8, 4]( - 0, - 0, - 0b00001111, - 0b11000000, - ) - ) - var masked_3 = shuffled_vector & mask_3 - var masked_3_as_uint16 = bitcast[DType.uint16, simd_width // 2](masked_3) - var rotated_3 = bit.rotate_bits_left[2](masked_3_as_uint16) - var result = bitcast[DType.uint8, simd_width](rotated_3) - return result - - -fn _move_fourth_group_of_6_bits[ - simd_width: Int -](shuffled_vector: SIMD[DType.uint8, simd_width]) -> SIMD[ - DType.uint8, simd_width -]: - alias mask_4 = _repeat_until[simd_width]( - SIMD[DType.uint8, 4]( - 0, - 0, - 0, - 0b00111111, - ) - ) - result = shuffled_vector & mask_4 - return result - - -fn _shuffle_input_vector[ - simd_width: Int -](input_vector: SIMD[DType.uint8, simd_width]) -> SIMD[DType.uint8, simd_width]: - # We reorder the bytes to fall in their correct 4 bytes chunks - # When Mojo is a bit more flexible with compile-time programming, we should be - # able to make this less verbose. - @parameter - if simd_width < 4: - constrained[False, msg="simd_width must be at least 4"]() - return SIMD[DType.uint8, simd_width]() # dummy, unreachable - elif simd_width == 4: - return input_vector.shuffle[0, 1, 1, 2]() - elif simd_width == 8: - return input_vector.shuffle[0, 1, 1, 2, 3, 4, 4, 5]() - elif simd_width == 16: - return input_vector.shuffle[ - 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11 - ]() - elif simd_width == 32: - # fmt: off - return input_vector.shuffle[ - 0, 1, 1, 2, - 3, 4, 4, 5, - 6, 7, 7, 8, - 9, 10, 10, 11, - 12, 13, 13, 14, - 15, 16, 16, 17, - 18, 19, 19, 20, - 21, 22, 22, 23 - ]() - # fmt: on - elif simd_width == 64: - # fmt: off - return input_vector.shuffle[ - 0, 1, 1, 2, - 3, 4, 4, 5, - 6, 7, 7, 8, - 9, 10, 10, 11, - 12, 13, 13, 14, - 15, 16, 16, 17, - 18, 19, 19, 20, - 21, 22, 22, 23, - 24, 25, 25, 26, - 27, 28, 28, 29, - 30, 31, 31, 32, - 33, 34, 34, 35, - 36, 37, 37, 38, - 39, 40, 40, 41, - 42, 43, 43, 44, - 45, 46, 46, 47, - ]() - # fmt: on - else: - constrained[False, msg="simd_width must be at most 64"]() - return SIMD[DType.uint8, simd_width]() # dummy, unreachable - - -fn _to_b64_ascii[ - simd_width: Int -](input_vector: SIMD[DType.uint8, simd_width]) -> SIMD[DType.uint8, simd_width]: - alias constant_13 = SIMD[DType.uint8, simd_width](13) - - # We reorder the bytes to fall in their correct 4 bytes chunks - var shuffled_vector = _shuffle_input_vector(input_vector) - - # We have 4 different masks to extract each group of 6 bits from the 4 bytes - var ready_to_encode_per_byte = ( - _move_first_group_of_6_bits(shuffled_vector) - | _move_second_group_of_6_bits(shuffled_vector) - | _move_third_group_of_6_bits(shuffled_vector) - | _move_fourth_group_of_6_bits(shuffled_vector) - ) - - # See the table above for the offsets, we try to go from 6-bits values to target indexes. - # The two first ranges go to 0, the other ranges are just 1...12. - var saturated = _sub_with_saturation( - ready_to_encode_per_byte, - SIMD[DType.uint8, simd_width](END_SECOND_RANGE), - ) - - var mask_in_first_range = ready_to_encode_per_byte <= END_FIRST_RANGE - - # Now are have the target indexes - # The first range goes to 13 - var indices = mask_in_first_range.select(constant_13, saturated) - - var offsets = TABLE_BASE64_OFFSETS._dynamic_shuffle(indices) - - return ready_to_encode_per_byte + offsets +fn _to_b64_ascii[width: Int, //](input: Bytes[width]) -> Bytes[width]: + var abcd = _6bit_to_byte(input) + var target_indices = _sub_with_saturation(abcd, END_SECOND_RANGE) + var offset_indices = (abcd <= END_FIRST_RANGE).select(13, target_indices) + return abcd + OFFSETS._dynamic_shuffle(offset_indices) fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ @@ -386,3 +273,21 @@ fn b64encode_with_buffers( ) result.size += nb_of_elements_to_store input_index += input_simd_width + + +# Utility functions + + +fn _repeat_until[width: Int](v: SIMD) -> SIMD[v.type, width]: + constrained[width >= v.size, "width must be at least v.size"]() + + @parameter + if width == v.size: + return rebind[SIMD[v.type, width]](v) + return _repeat_until[width](v.join(v)) + + +fn _rshift_bits_in_u16[shift: Int](input: Bytes) -> __type_of(input): + var u16 = bitcast[DType.uint16, input.size // 2](input) + var res = bit.rotate_bits_right[shift](u16) + return bitcast[DType.uint8, input.size](res) From 1c9b77bc3ab21b95ccc3ff64365d4dc44b1638aa Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 4 Nov 2024 12:14:26 -0600 Subject: [PATCH 1827/2019] [stdlib][KGEN] cleanup: Change `VariadicPack.elt_is_mutable` to `Bool` (was `i1`) MODULAR_ORIG_COMMIT_REV_ID: 4b6f84381fbf40670115da33b3d40a304f3306bd --- stdlib/src/builtin/builtin_list.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 28a3eed902..6fca53e509 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -546,8 +546,8 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ - elt_is_mutable: __mlir_type.i1, //, - origin: Origin[Bool {value: elt_is_mutable}].type, + elt_is_mutable: Bool, //, + origin: Origin[elt_is_mutable].type, element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): From 6190674fa09c60bda22beae43eb53fd5a11f2423 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 4 Nov 2024 12:15:18 -0600 Subject: [PATCH 1828/2019] [stdlib] cleanup: Add combined argument check and conversion functions MODULAR_ORIG_COMMIT_REV_ID: 4e52f290326a64b0bf680f900f5e4186eb7efee4 --- stdlib/src/builtin/_pybind.mojo | 26 +++++++++++++++++++++++++- stdlib/src/python/_bindings.mojo | 4 ++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index a7f9a1f724..9cf24ea083 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -29,6 +29,7 @@ from python._cpython import ( from python._bindings import ( Pythonable, ConvertibleFromPython, + PythonableAndConvertibleFromPython, PyMojoObject, py_c_function_wrapper, check_argument_type, @@ -125,7 +126,30 @@ fn check_and_get_arg[ return check_argument_type[T](func_name, type_name_id, py_args[index]) -fn try_convert_arg[ +fn check_and_get_or_convert_arg[ + T: PythonableAndConvertibleFromPython +]( + func_name: StringLiteral, + type_name_id: StringLiteral, + py_args: TypedPythonObject["Tuple"], + index: Int, + converted_arg_ptr: UnsafePointer[T], +) raises -> UnsafePointer[T]: + try: + return check_and_get_arg[T](func_name, type_name_id, py_args, index) + except e: + converted_arg_ptr.init_pointee_move( + _try_convert_arg[T]( + func_name, + type_name_id, + py_args, + index, + ) + ) + return converted_arg_ptr + + +fn _try_convert_arg[ T: ConvertibleFromPython ]( func_name: StringLiteral, diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 24d874f9ef..aca723459f 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -54,6 +54,10 @@ trait ConvertibleFromPython(CollectionElement): ... +trait PythonableAndConvertibleFromPython(Pythonable, ConvertibleFromPython): + pass + + # ===-----------------------------------------------------------------------===# # Mojo Object # ===-----------------------------------------------------------------------===# From 21f60e29f56c371a5232159d32694321322ebaaa Mon Sep 17 00:00:00 2001 From: Spenser Bauman Date: Tue, 5 Nov 2024 00:55:51 +0000 Subject: [PATCH 1829/2019] [stdlib] Add Stringable and Writeable traits to AddressSpace Add string/printing methods to make inspecting the address space type easier. MODULAR_ORIG_COMMIT_REV_ID: 1efc2b03c806734e93989c603b1f442786fc383f --- stdlib/src/memory/pointer.mojo | 27 ++++++++++++++++++++++++++- stdlib/test/memory/test_memory.mojo | 8 ++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 6a4db76cac..62529382a1 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -158,7 +158,7 @@ struct _GPUAddressSpace(EqualityComparable): @value @register_passable("trivial") -struct AddressSpace(EqualityComparable): +struct AddressSpace(EqualityComparable, Stringable, Writable): """Address space of the pointer.""" var _value: Int @@ -259,6 +259,31 @@ struct AddressSpace(EqualityComparable): """ return self.value() != other.value() + @always_inline("nodebug") + fn __str__(self) -> String: + """Gets a string representation of the AddressSpace. + + Returns: + The string representation of the AddressSpace. + """ + return String.write(self) + + @always_inline("nodebug") + fn write_to[W: Writer](self, inout writer: W): + """ + Formats the address space to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. + + Args: + writer: The object to write to. + """ + if self is AddressSpace.GENERIC: + writer.write("AddressSpace.GENERIC") + else: + writer.write("AddressSpace(", self.value(), ")") + # ===----------------------------------------------------------------------===# # Pointer diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 90195ba443..52cbe98ae4 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -15,6 +15,7 @@ from sys import sizeof, simdwidthof from memory import ( + AddressSpace, UnsafePointer, memcmp, memcpy, @@ -365,6 +366,11 @@ def test_pointer_refitem_pair(): ptr.free() +def test_address_space_str(): + assert_equal(str(AddressSpace.GENERIC), "AddressSpace.GENERIC") + assert_equal(str(AddressSpace(17)), "AddressSpace(17)") + + def test_dtypepointer_gather(): var ptr = UnsafePointer[Float32].alloc(4) ptr.store(0, SIMD[ptr.type.type, 4](0.0, 1.0, 2.0, 3.0)) @@ -504,6 +510,8 @@ def main(): test_pointer_refitem_pair() test_pointer_string() + test_address_space_str() + test_dtypepointer_gather() test_dtypepointer_scatter() test_indexing() From 2b4bfb5e45a947ad5c03ca49c483aad2c90fcf97 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Mon, 4 Nov 2024 19:40:00 -0600 Subject: [PATCH 1830/2019] [External] [stdlib] Add `DLHandle.call` and cleanup `_cpython.mojo` (#50272) [External] [stdlib] Add `DLHandle.call` and cleanup `_cpython.mojo` Add `DLHandle.call` and cleanup `_cpython.mojo`. Fixes https://github.com/modularml/mojo/issues/3712. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3718 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3718 MODULAR_ORIG_COMMIT_REV_ID: aab5b603245991102cee5274c63acdf2e8a73daf --- stdlib/src/python/_cpython.mojo | 792 +++++++++++++++----------------- stdlib/src/sys/ffi.mojo | 41 ++ 2 files changed, 404 insertions(+), 429 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index f6c92b609a..7fb8070ee6 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -65,7 +65,8 @@ alias Py_ssize_t = c_ssize_t # TODO(MOCO-1138): # This should be a C ABI function pointer, not a Mojo ABI function. alias PyCFunction = fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr -"""See https://docs.python.org/3/c-api/structures.html#c.PyCFunction.""" +"""[Reference](https://docs.python.org/3/c-api/structures.html#c.PyCFunction). +""" alias METH_VARARGS = 0x1 @@ -83,10 +84,11 @@ alias newfunc = fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr struct PyGILState_STATE: """Represents the state of the Python Global Interpreter Lock (GIL). - This struct is used to store and manage the state of the GIL, - which is crucial for thread-safe operations in Python. - - See https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/pystate.h#L76 + Notes: + This struct is used to store and manage the state of the GIL, which is + crucial for thread-safe operations in Python. [Reference]( + https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/pystate.h#L76 + ). """ var current_state: c_int @@ -200,9 +202,7 @@ struct PyObjectPtr: expected_type_name: StringLiteral, ) -> Optional[UnsafePointer[T]]: var cpython = _get_global_python_itf().cpython() - var type = cpython.Py_TYPE(self) - var type_name = PythonObject(cpython.PyType_GetName(type)) # FIXME(MSTDL-978): @@ -252,9 +252,7 @@ struct PyObjectPtr: @value @register_passable struct PythonVersion: - """ - Represents a Python version with major, minor, and patch numbers. - """ + """Represents a Python version with major, minor, and patch numbers.""" var major: Int """The major version number.""" @@ -293,38 +291,42 @@ struct PythonVersion: fn _py_get_version(lib: DLHandle) -> StringRef: - var version_string = lib.get_function[fn () -> UnsafePointer[c_char]]( - "Py_GetVersion" - )() - return StringRef(ptr=version_string) + return StringRef(ptr=lib.call["Py_GetVersion", UnsafePointer[c_char]]()) fn _py_finalize(lib: DLHandle): - lib.get_function[fn () -> None]("Py_Finalize")() + lib.call["Py_Finalize"]() -# Ref https://docs.python.org/3/c-api/structures.html#c.PyMethodDef @value struct PyMethodDef: - """Represents a Python method definition. + """Represents a Python method definition. This struct is used to define + methods for Python modules or types. - This struct is used to define methods for Python modules or types. + Notes: + [Reference]( + https://docs.python.org/3/c-api/structures.html#c.PyMethodDef + ). """ # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# - var method_name: UnsafePointer[c_char] # called ml_name in CPython - """A pointer to the name of the method as a C string.""" + var method_name: UnsafePointer[c_char] + """A pointer to the name of the method as a C string. + + Notes: + called `ml_name` in CPython. + """ # TODO(MSTDL-887): Support keyword-argument only methods var method_impl: PyCFunction """A function pointer to the implementation of the method.""" - # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef for the various calling conventions var method_flags: c_int - """Flags indicating how the method should be called.""" + """Flags indicating how the method should be called. [Reference]( + https://docs.python.org/3/c-api/structures.html#c.PyMethodDef).""" var method_docstring: UnsafePointer[c_char] """A pointer to the docstring for the method as a C string.""" @@ -357,8 +359,7 @@ struct PyMethodDef: func_name: StringLiteral, docstring: StringLiteral = "", ]() -> Self: - """ - Create a PyMethodDef for a function. + """Create a PyMethodDef for a function. Parameters: func: The function to wrap. @@ -384,10 +385,10 @@ fn _null_fn_ptr[T: AnyTrivialRegType]() -> T: struct PyTypeObject: - """ - The opaque C structure of the objects used to describe types. + """The opaque C structure of the objects used to describe types. - See https://docs.python.org/3/c-api/type.html#c.PyTypeObject + Notes: + [Reference](https://docs.python.org/3/c-api/type.html#c.PyTypeObject). """ # TODO(MSTDL-877): @@ -399,10 +400,10 @@ struct PyTypeObject: @value @register_passable("trivial") struct PyType_Spec: - """ - Structure defining a type’s behavior. + """Structure defining a type's behavior. - See https://docs.python.org/3/c-api/type.html#c.PyType_Spec + Notes: + [Reference](https://docs.python.org/3/c-api/type.html#c.PyType_Spec). """ var name: UnsafePointer[c_char] @@ -415,11 +416,11 @@ struct PyType_Spec: @value @register_passable("trivial") struct PyType_Slot: - """ - Structure defining optional functionality of a type, containing a slot ID + """Structure defining optional functionality of a type, containing a slot ID and a value pointer. - See https://docs.python.org/3/c-api/type.html#c.PyType_Slot + Notes: + [Reference](https://docs.python.org/3/c-api/type.html#c.PyType_Slot). """ var slot: c_int @@ -452,10 +453,15 @@ struct PyType_Slot: @value struct PyObject(Stringable, Representable, Writable): - """ - All object types are extensions of this type. This is a type which contains the information Python needs to treat a pointer to an object as an object. In a normal “release” build, it contains only the object’s reference count and a pointer to the corresponding type object. Nothing is actually declared to be a PyObject, but every pointer to a Python object can be cast to a PyObject*. - - See https://docs.python.org/3/c-api/structures.html#c.PyObject + """All object types are extensions of this type. This is a type which + contains the information Python needs to treat a pointer to an object as an + object. In a normal “release” build, it contains only the object's reference + count and a pointer to the corresponding type object. Nothing is actually + declared to be a PyObject, but every pointer to a Python object can be cast + to a PyObject. + + Notes: + [Reference](https://docs.python.org/3/c-api/structures.html#c.PyObject). """ var object_ref_count: Int @@ -477,7 +483,8 @@ struct PyObject(Stringable, Representable, Writable): @no_inline fn __repr__(self) -> String: - """Get the `PyObject` as a string. Returns the same `String` as `__str__`. + """Get the `PyObject` as a string. Returns the same `String` as + `__str__`. Returns: A string representation. @@ -489,8 +496,7 @@ struct PyObject(Stringable, Representable, Writable): # ===-------------------------------------------------------------------===# fn write_to[W: Writer](self, inout writer: W): - """ - Formats to the provided Writer. + """Formats to the provided Writer. Parameters: W: A type conforming to the Writable trait. @@ -505,24 +511,33 @@ struct PyObject(Stringable, Representable, Writable): writer.write(")") -# Ref: https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L39 -# Ref2: https://pyo3.rs/main/doc/pyo3/ffi/struct.pymoduledef_base # Mojo doesn't have macros, so we define it here for ease. -# Note: `PyModuleDef_HEAD_INIT` defaults all of its members, see https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L60 struct PyModuleDef_Base(Stringable, Representable, Writable): - # The initial segment of every `PyObject` in CPython + """PyModuleDef_Base. + + Notes: + [Reference 1]( + https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L39 + ). [Reference 2]( + https://pyo3.rs/main/doc/pyo3/ffi/struct.pymoduledef_base + ). `PyModuleDef_HEAD_INIT` defaults all of its members, [Reference 3]( + https://github.com/python/cpython/blob/833c58b81ebec84dc24ef0507f8c75fe723d9f66/Include/moduleobject.h#L60 + ). + """ + var object_base: PyObject + """The initial segment of every `PyObject` in CPython.""" - # The function used to re-initialize the module. # TODO(MOCO-1138): This is a C ABI function pointer, not Mojo a function. alias _init_fn_type = fn () -> UnsafePointer[PyObject] + """The function used to re-initialize the module.""" var init_fn: Self._init_fn_type - # The module's index into its interpreter's modules_by_index cache. var index: Py_ssize_t + """The module's index into its interpreter's modules_by_index cache.""" - # A copy of the module's __dict__ after the first time it was loaded. var dict_copy: UnsafePointer[PyObject] + """A copy of the module's __dict__ after the first time it was loaded.""" # ===------------------------------------------------------------------=== # # Life cycle methods @@ -556,7 +571,8 @@ struct PyModuleDef_Base(Stringable, Representable, Writable): @no_inline fn __repr__(self) -> String: - """Get the PyMdouleDef_Base as a string. Returns the same `String` as `__str__`. + """Get the PyMdouleDef_Base as a string. Returns the same `String` as + `__str__`. Returns: A string representation. @@ -568,8 +584,7 @@ struct PyModuleDef_Base(Stringable, Representable, Writable): # ===-------------------------------------------------------------------===# fn write_to[W: Writer](self, inout writer: W): - """ - Formats to the provided Writer. + """Formats to the provided Writer. Parameters: W: A type conforming to the Writable trait. @@ -588,8 +603,8 @@ struct PyModuleDef_Base(Stringable, Representable, Writable): @value struct PyModuleDef_Slot: - """ - See https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot. + """[Reference]( + https://docs.python.org/3/c-api/module.html#c.PyModuleDef_Slot). """ var slot: c_int @@ -597,26 +612,27 @@ struct PyModuleDef_Slot: struct PyModuleDef(Stringable, Representable, Writable): - """ - The Python module definition structs that holds all of the information needed - to create a module. + """The Python module definition structs that holds all of the information + needed to create a module. - See https://docs.python.org/3/c-api/module.html#c.PyModuleDef + Notes: + [Reference](https://docs.python.org/3/c-api/module.html#c.PyModuleDef). """ var base: PyModuleDef_Base - # See https://docs.python.org/3/c-api/structures.html#c.PyMethodDef var name: UnsafePointer[c_char] + """[Reference](https://docs.python.org/3/c-api/structures.html#c.PyMethodDef + ).""" - # Points to the contents of the docstring for the module. var docstring: UnsafePointer[c_char] + """Points to the contents of the docstring for the module.""" var size: Py_ssize_t - # A pointer to a table of module-level functions. Can be null if there - # are no functions present. var methods: UnsafePointer[PyMethodDef] + """A pointer to a table of module-level functions. Can be null if there + are no functions present.""" var slots: UnsafePointer[PyModuleDef_Slot] @@ -674,7 +690,8 @@ struct PyModuleDef(Stringable, Representable, Writable): @no_inline fn __repr__(self) -> String: - """Get the PyMdouleDef as a string. Returns the same `String` as `__str__`. + """Get the PyMdouleDef as a string. Returns the same `String` as + `__str__`. Returns: A string representation. @@ -686,8 +703,7 @@ struct PyModuleDef(Stringable, Representable, Writable): # ===-------------------------------------------------------------------===# fn write_to[W: Writer](self, inout writer: W): - """ - Formats to the provided Writer. + """Formats to the provided Writer. Parameters: W: A type conforming to the Writable trait. @@ -711,9 +727,7 @@ struct PyModuleDef(Stringable, Representable, Writable): @value struct CPython: - """ - Handle to the CPython interpreter present in the current process. - """ + """Handle to the CPython interpreter present in the current process.""" # ===-------------------------------------------------------------------===# # Fields @@ -760,8 +774,7 @@ struct CPython: # TODO(MOCO-772) Allow raises to propagate through function pointers # and make this initialization a raising function. self.init_error = external_call[ - "KGEN_CompilerRT_Python_SetPythonPath", - UnsafePointer[c_char], + "KGEN_CompilerRT_Python_SetPythonPath", UnsafePointer[c_char] ]() var python_lib = getenv("MOJO_PYTHON_LIBRARY") @@ -777,7 +790,7 @@ struct CPython: if not self.init_error: if not self.lib.check_symbol("Py_Initialize"): self.init_error = "compatible Python library not found" - self.lib.get_function[fn () -> None]("Py_Initialize")() + self.lib.call["Py_Initialize"]() self.version = PythonVersion(_py_get_version(self.lib)) else: self.version = PythonVersion(0, 0, 0) @@ -804,7 +817,7 @@ struct CPython: raise an error if one occurred when initializing the global CPython. """ if self.init_error: - var error: String = self.init_error + var error = String(self.init_error) var mojo_python = getenv("MOJO_PYTHON") var python_lib = getenv("MOJO_PYTHON_LIBRARY") var python_exe = getenv("PYTHONEXECUTABLE") @@ -859,19 +872,22 @@ struct CPython: self.total_ref_count.init_pointee_move(v - 1) fn Py_IncRef(inout self, ptr: PyObjectPtr): - """See https://docs.python.org/3/c-api/refcounting.html#c.Py_IncRef.""" + """[Reference]( + https://docs.python.org/3/c-api/refcounting.html#c.Py_IncRef). + """ self.log(ptr._get_ptr_as_int(), " INCREF refcnt:", self._Py_REFCNT(ptr)) - self.lib.get_function[fn (PyObjectPtr) -> None]("Py_IncRef")(ptr) + self.lib.call["Py_IncRef"](ptr) self._inc_total_rc() fn Py_DecRef(inout self, ptr: PyObjectPtr): - """See https://docs.python.org/3/c-api/refcounting.html#c.Py_DecRef.""" + """[Reference]( + https://docs.python.org/3/c-api/refcounting.html#c.Py_DecRef). + """ self.log(ptr._get_ptr_as_int(), " DECREF refcnt:", self._Py_REFCNT(ptr)) - - self.lib.get_function[fn (PyObjectPtr) -> None]("Py_DecRef")(ptr) + self.lib.call["Py_DecRef"](ptr) self._dec_total_rc() # This function assumes a specific way PyObjectPtr is implemented, namely @@ -902,43 +918,42 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyGILState_Ensure(inout self) -> PyGILState_STATE: - """See https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure.""" - - return self.lib.get_function[fn () -> PyGILState_STATE]( - "PyGILState_Ensure" - )() + """[Reference]( + https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure). + """ + return self.lib.call["PyGILState_Ensure", PyGILState_STATE]() fn PyGILState_Release(inout self, state: PyGILState_STATE): - """See https://docs.python.org/3/c-api/init.html#c.PyGILState_Release. + """[Reference]( + https://docs.python.org/3/c-api/init.html#c.PyGILState_Release). """ - - self.lib.get_function[fn (PyGILState_STATE) -> None]( - "PyGILState_Release" - )(state) + self.lib.call["PyGILState_Release"](state) fn PyEval_SaveThread(inout self) -> UnsafePointer[PyThreadState]: - """See https://docs.python.org/3/c-api/init.html#c.PyEval_SaveThread.""" + """[Reference]( + https://docs.python.org/3/c-api/init.html#c.PyEval_SaveThread). + """ - return self.lib.get_function[fn () -> UnsafePointer[PyThreadState]]( - "PyEval_SaveThread" - )() + return self.lib.call[ + "PyEval_SaveThread", UnsafePointer[PyThreadState] + ]() fn PyEval_RestoreThread(inout self, state: UnsafePointer[PyThreadState]): - """See https://docs.python.org/3/c-api/init.html#c.PyEval_RestoreThread. + """[Reference]( + https://docs.python.org/3/c-api/init.html#c.PyEval_RestoreThread). """ - - self.lib.get_function[fn (UnsafePointer[PyThreadState]) -> None]( - "PyEval_RestoreThread" - )(state) + self.lib.call["PyEval_RestoreThread"](state) # ===-------------------------------------------------------------------===# # Python Dict operations # ===-------------------------------------------------------------------===# fn PyDict_New(inout self) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/dict.html#c.PyDict_New.""" + """[Reference]( + https://docs.python.org/3/c-api/dict.html#c.PyDict_New). + """ - var r = self.lib.get_function[fn () -> PyObjectPtr]("PyDict_New")() + var r = self.lib.call["PyDict_New", PyObjectPtr]() self.log( r._get_ptr_as_int(), @@ -953,11 +968,11 @@ struct CPython: fn PyDict_SetItem( inout self, dict_obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr ) -> c_int: - """See https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem.""" + """[Reference]( + https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem). + """ - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int - ](StringRef("PyDict_SetItem"))(dict_obj, key, value) + var r = self.lib.call["PyDict_SetItem", c_int](dict_obj, key, value) self.log( "PyDict_SetItem, key: ", @@ -971,19 +986,20 @@ struct CPython: fn PyDict_GetItemWithError( inout self, dict_obj: PyObjectPtr, key: PyObjectPtr ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItemWithError. + """[Reference]( + https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItemWithError). """ - var result = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr - ](StringRef("PyDict_GetItemWithError"))(dict_obj, key) - + var r = self.lib.call["PyDict_GetItemWithError", PyObjectPtr]( + dict_obj, key + ) self.log("PyDict_GetItemWithError, key: ", key._get_ptr_as_int()) - - return result + return r fn PyDict_Check(inout self, maybe_dict: PyObjectPtr) -> Bool: - """See https://docs.python.org/3/c-api/dict.html#c.PyDict_Check.""" + """[Reference]( + https://docs.python.org/3/c-api/dict.html#c.PyDict_Check). + """ var my_type = self.PyObject_Type(maybe_dict) var my_type_as_int = my_type._get_ptr_as_int() @@ -993,28 +1009,25 @@ struct CPython: return result fn PyDict_Type(inout self) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/dict.html#c.PyDict_Type.""" + """[Reference]( + https://docs.python.org/3/c-api/dict.html#c.PyDict_Type). + """ if self.dict_type.is_null(): - self.dict_type = self.lib.get_function[PyObjectPtr]("PyDict_Type") + self.dict_type = self.lib.call["PyDict_Type", PyObjectPtr]() return self.dict_type # int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) fn PyDict_Next( inout self, dictionary: PyObjectPtr, p: Int ) -> PyKeysValuePair: - """See https://docs.python.org/3/c-api/dict.html#c.PyDict_Next.""" + """[Reference]( + https://docs.python.org/3/c-api/dict.html#c.PyDict_Next). + """ var key = PyObjectPtr() var value = PyObjectPtr() var v = p var position = UnsafePointer[Int].address_of(v) - var result = self.lib.get_function[ - fn ( - PyObjectPtr, - UnsafePointer[Py_ssize_t], - UnsafePointer[PyObjectPtr], - UnsafePointer[PyObjectPtr], - ) -> c_int - ]("PyDict_Next")( + var result = self.lib.call["PyDict_Next", c_int]( dictionary, position, UnsafePointer.address_of(key), @@ -1053,12 +1066,11 @@ struct CPython: inout self, name: StringRef, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/import.html#c.PyImport_ImportModule. + """[Reference]( + https://docs.python.org/3/c-api/import.html#c.PyImport_ImportModule). """ - var r = self.lib.get_function[fn (UnsafePointer[UInt8]) -> PyObjectPtr]( - "PyImport_ImportModule" - )(name.data) + var r = self.lib.call["PyImport_ImportModule", PyObjectPtr](name.data) self.log( r._get_ptr_as_int(), @@ -1072,17 +1084,20 @@ struct CPython: return r fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/import.html#c.PyImport_AddModule. + """[Reference]( + https://docs.python.org/3/c-api/import.html#c.PyImport_AddModule). """ - return self.lib.get_function[fn (UnsafePointer[c_char]) -> PyObjectPtr]( - "PyImport_AddModule" - )(name.unsafe_ptr().bitcast[c_char]()) + return self.lib.call["PyImport_AddModule", PyObjectPtr]( + name.unsafe_ptr().bitcast[c_char]() + ) fn PyModule_Create( inout self, name: String, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/module.html#c.PyModule_Create.""" + """[Reference]( + https://docs.python.org/3/c-api/module.html#c.PyModule_Create). + """ # TODO: See https://docs.python.org/3/c-api/module.html#c.PyModule_Create # and https://github.com/pybind/pybind11/blob/a1d00916b26b187e583f3bce39cd59c3b0652c32/include/pybind11/pybind11.h#L1326 @@ -1091,10 +1106,6 @@ struct CPython: var module_def = PyModuleDef(name) module_def_ptr.init_pointee_move(module_def^) - var create_module_fn = self.lib.get_function[ - fn (UnsafePointer[PyModuleDef], Int) -> PyObjectPtr - ]("PyModule_Create2") - # TODO: set gil stuff # Note: Python automatically calls https://docs.python.org/3/c-api/module.html#c.PyState_AddModule # after the caller imports said module. @@ -1104,22 +1115,19 @@ struct CPython: # if this mismatches with the user's Python, then a `RuntimeWarning` is emitted according to the # docs. var module_api_version = 1013 - return create_module_fn(module_def_ptr, module_api_version) + return self.lib.call["PyModule_Create2", PyObjectPtr]( + module_def_ptr, module_api_version + ) fn PyModule_AddFunctions( inout self, mod: PyObjectPtr, functions: UnsafePointer[PyMethodDef], ) -> c_int: - """See https://docs.python.org/3/c-api/module.html#c.PyModule_AddFunctions. + """[Reference]( + https://docs.python.org/3/c-api/module.html#c.PyModule_AddFunctions). """ - - # int PyModule_AddFunctions(PyObject *module, PyMethodDef *functions) - var add_functions_fn = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[PyMethodDef]) -> c_int - ]("PyModule_AddFunctions") - - return add_functions_fn(mod, functions) + return self.lib.call["PyModule_AddFunctions", c_int](mod, functions) fn PyModule_AddObjectRef( inout self, @@ -1127,23 +1135,19 @@ struct CPython: name: UnsafePointer[c_char], value: PyObjectPtr, ) -> c_int: - """See https://docs.python.org/3/c-api/module.html#c.PyModule_AddObjectRef. + """[Reference]( + https://docs.python.org/3/c-api/module.html#c.PyModule_AddObjectRef). """ - var func = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[c_char], PyObjectPtr) -> c_int - ]("PyModule_AddObjectRef") - - return func(module, name, value) + return self.lib.call["PyModule_AddObjectRef", c_int]( + module, name, value + ) fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/module.html#c.PyModule_GetDict. + """[Reference]( + https://docs.python.org/3/c-api/module.html#c.PyModule_GetDict). """ - - var value = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyModule_GetDict" - )(name) - return value + return self.lib.call["PyModule_GetDict", PyObjectPtr](name) # ===-------------------------------------------------------------------===# # Python Type operations @@ -1165,22 +1169,15 @@ struct CPython: fn PyType_GetName( inout self, type: UnsafePointer[PyTypeObject] ) -> PyObjectPtr: - var func = self.lib.get_function[ - fn (UnsafePointer[PyTypeObject]) -> PyObjectPtr - ]("PyType_GetName") - - return func(type) + return self.lib.call["PyType_GetName", PyObjectPtr](type) fn PyType_FromSpec( inout self, spec: UnsafePointer[PyType_Spec] ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/type.html#c.PyType_FromSpec.""" - - var func = self.lib.get_function[ - fn (UnsafePointer[PyType_Spec]) -> PyObjectPtr - ]("PyType_FromSpec") - - return func(spec) + """[Reference]( + https://docs.python.org/3/c-api/type.html#c.PyType_FromSpec). + """ + return self.lib.call["PyType_FromSpec", PyObjectPtr](spec) # ===-------------------------------------------------------------------===# # Python Evaluation @@ -1189,22 +1186,20 @@ struct CPython: fn PyRun_SimpleString(inout self, strref: StringRef) -> Bool: """Executes the given Python code. - See https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleString - Args: strref: The python code to execute. Returns: `True` if the code executed successfully or `False` if the code raised an exception. + + Notes: + [Reference]( + https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleString). """ - # int PyRun_SimpleString(const char *command) - var status = self.lib.get_function[fn (UnsafePointer[UInt8]) -> c_int]( - StringRef("PyRun_SimpleString") - )(strref.data) - # PyRun_SimpleString returns 0 on success and -1 if an exception was - # raised. - return status == 0 + return ( + self.lib.call["PyRun_SimpleString", c_int](strref.unsafe_ptr()) == 0 + ) fn PyRun_String( inout self, @@ -1213,12 +1208,12 @@ struct CPython: locals: PyObjectPtr, run_mode: Int, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_String.""" - var result = self.lib.get_function[ - fn ( - UnsafePointer[UInt8], Int32, PyObjectPtr, PyObjectPtr - ) -> PyObjectPtr - ]("PyRun_String")(strref.data, Int32(run_mode), globals, locals) + """[Reference]( + https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_String). + """ + var result = self.lib.call["PyRun_String", PyObjectPtr]( + strref.unsafe_ptr(), Int32(run_mode), globals, locals + ) self.log( result._get_ptr_as_int(), @@ -1239,21 +1234,20 @@ struct CPython: globals: PyObjectPtr, locals: PyObjectPtr, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/veryhigh.html#c.PyEval_EvalCode. + """[Reference]( + https://docs.python.org/3/c-api/veryhigh.html#c.PyEval_EvalCode). """ - var result = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr - ]("PyEval_EvalCode")(co, globals, locals) + var result = self.lib.call["PyEval_EvalCode", PyObjectPtr]( + co, globals, locals + ) self._inc_total_rc() return result fn PyEval_GetBuiltins(inout self) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetBuiltins. + """[Reference]( + https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetBuiltins). """ - - return self.lib.get_function[fn () -> PyObjectPtr]( - "PyEval_GetBuiltins" - )() + return self.lib.call["PyEval_GetBuiltins", PyObjectPtr]() fn Py_CompileString( inout self, @@ -1261,14 +1255,13 @@ struct CPython: filename: StringRef, compile_mode: Int, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/veryhigh.html#c.Py_CompileString. + """[Reference]( + https://docs.python.org/3/c-api/veryhigh.html#c.Py_CompileString). """ - var r = self.lib.get_function[ - fn ( - UnsafePointer[UInt8], UnsafePointer[UInt8], Int32 - ) -> PyObjectPtr - ]("Py_CompileString")(strref.data, filename.data, Int32(compile_mode)) + var r = self.lib.call["Py_CompileString", PyObjectPtr]( + strref.unsafe_ptr(), filename.unsafe_ptr(), Int32(compile_mode) + ) self._inc_total_rc() return r @@ -1281,44 +1274,42 @@ struct CPython: rhs: PyObjectPtr, lhs: PyObjectPtr, ) -> Bool: - """See https://docs.python.org/3/c-api/structures.html#c.Py_Is.""" + """[Reference]( + https://docs.python.org/3/c-api/structures.html#c.Py_Is). + """ if self.version.minor >= 10: # int Py_Is(PyObject *x, PyObject *y) - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr) -> c_int - ]("Py_Is")(rhs, lhs) - return r > 0 + return self.lib.call["Py_Is", c_int](rhs, lhs) > 0 else: return rhs == lhs fn PyObject_Type(inout self, obj: PyObjectPtr) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_Type.""" + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_Type). + """ - var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyObject_Type" - ) + var p = self.lib.call["PyObject_Type", PyObjectPtr](obj) self._inc_total_rc() - return f(obj) + return p fn PyObject_Str(inout self, obj: PyObjectPtr) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_Str.""" + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_Str). + """ - var f = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyObject_Str" - ) + var p = self.lib.call["PyObject_Str", PyObjectPtr](obj) self._inc_total_rc() - return f(obj) + return p fn PyObject_GetItem( inout self, obj: PyObjectPtr, key: PyObjectPtr ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem. + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem). """ - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr - ]("PyObject_GetItem")(obj, key) + var r = self.lib.call["PyObject_GetItem", PyObjectPtr](obj, key) self.log( r._get_ptr_as_int(), @@ -1336,12 +1327,11 @@ struct CPython: fn PyObject_SetItem( inout self, obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr ) -> c_int: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_SetItem. + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_SetItem). """ - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> c_int - ]("PyObject_SetItem")(obj, key, value) + var r = self.lib.call["PyObject_SetItem", c_int](obj, key, value) self.log( "PyObject_SetItem result:", @@ -1361,12 +1351,13 @@ struct CPython: obj: PyObjectPtr, name: StringRef, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttrString. + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttrString). """ - var r = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[UInt8]) -> PyObjectPtr - ]("PyObject_GetAttrString")(obj, name.data) + var r = self.lib.call["PyObject_GetAttrString", PyObjectPtr]( + obj, name.data + ) self.log( r._get_ptr_as_int(), @@ -1384,13 +1375,13 @@ struct CPython: fn PyObject_SetAttrString( inout self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr ) -> c_int: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString. + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString). """ - # int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v) - var r = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[UInt8], PyObjectPtr) -> c_int - ]("PyObject_SetAttrString")(obj, name.data, new_value) + var r = self.lib.call["PyObject_SetAttrString", c_int]( + obj, name.data, new_value + ) self.log( "PyObject_SetAttrString str:", @@ -1410,12 +1401,13 @@ struct CPython: callable_obj: PyObjectPtr, args: PyObjectPtr, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/call.html#c.PyObject_CallObject. + """[Reference]( + https://docs.python.org/3/c-api/call.html#c.PyObject_CallObject). """ - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr - ]("PyObject_CallObject")(callable_obj, args) + var r = self.lib.call["PyObject_CallObject", PyObjectPtr]( + callable_obj, args + ) self.log( r._get_ptr_as_int(), @@ -1434,11 +1426,13 @@ struct CPython: args: PyObjectPtr, kwargs: PyObjectPtr, ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/call.html#c.PyObject_Call.""" + """[Reference]( + https://docs.python.org/3/c-api/call.html#c.PyObject_Call). + """ - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr - ]("PyObject_Call")(callable_obj, args, kwargs) + var r = self.lib.call["PyObject_Call", PyObjectPtr]( + callable_obj, args, kwargs + ) self.log( r._get_ptr_as_int(), @@ -1451,50 +1445,39 @@ struct CPython: self._inc_total_rc() return r - fn PyObject_IsTrue( - inout self, - obj: PyObjectPtr, - ) -> c_int: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue.""" - - # int PyObject_IsTrue(PyObject *o) - return self.lib.get_function[fn (PyObjectPtr) -> c_int]( - "PyObject_IsTrue" - )(obj) - - fn PyObject_Length( - inout self, - obj: PyObjectPtr, - ) -> Int: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_Length.""" + fn PyObject_IsTrue(inout self, obj: PyObjectPtr) -> c_int: + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue). + """ + return self.lib.call["PyObject_IsTrue", c_int](obj) - return int( - self.lib.get_function[fn (PyObjectPtr) -> Int]("PyObject_Length")( - obj - ) - ) + fn PyObject_Length(inout self, obj: PyObjectPtr) -> Int: + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_Length). + """ + return int(self.lib.call["PyObject_Length", Int](obj)) fn PyObject_Hash(inout self, obj: PyObjectPtr) -> Int: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_Hash.""" - - return int( - self.lib.get_function[fn (PyObjectPtr) -> Int]("PyObject_Hash")(obj) - ) + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_Hash). + """ + return int(self.lib.call["PyObject_Hash", Int](obj)) fn PyObject_GetIter( inout self, traversablePyObject: PyObjectPtr ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/object.html#c.PyObject_GetIter. + """[Reference]( + https://docs.python.org/3/c-api/object.html#c.PyObject_GetIter). """ - var iter = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyObject_GetIter" - )(traversablePyObject) + var iterator = self.lib.call["PyObject_GetIter", PyObjectPtr]( + traversablePyObject + ) self.log( - iter._get_ptr_as_int(), + iterator._get_ptr_as_int(), " NEWREF PyObject_GetIter, refcnt:", - self._Py_REFCNT(iter), + self._Py_REFCNT(iterator), "referencing ", traversablePyObject._get_ptr_as_int(), "refcnt of traversable: ", @@ -1502,18 +1485,18 @@ struct CPython: ) self._inc_total_rc() - return iter + return iterator # ===-------------------------------------------------------------------===# # Python Tuple operations # ===-------------------------------------------------------------------===# fn PyTuple_New(inout self, count: Int) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/tuple.html#c.PyTuple_New.""" + """[Reference]( + https://docs.python.org/3/c-api/tuple.html#c.PyTuple_New). + """ - var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( - StringRef("PyTuple_New") - )(count) + var r = self.lib.call["PyTuple_New", PyObjectPtr](count) self.log( r._get_ptr_as_int(), @@ -1529,38 +1512,35 @@ struct CPython: fn PyTuple_GetItem( inout self, tuple: PyObjectPtr, pos: Py_ssize_t ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GetItem.""" - - return self.lib.get_function[ - fn (PyObjectPtr, Py_ssize_t) -> PyObjectPtr - ]("PyTuple_GetItem")(tuple, pos) + """[Reference]( + https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GetItem). + """ + return self.lib.call["PyTuple_GetItem", PyObjectPtr](tuple, pos) fn PyTuple_SetItem( - inout self, - tuple_obj: PyObjectPtr, - index: Int, - element: PyObjectPtr, + inout self, tuple_obj: PyObjectPtr, index: Int, element: PyObjectPtr ) -> c_int: - """See https://docs.python.org/3/c-api/tuple.html#c.PyTuple_SetItem.""" + """[Reference]( + https://docs.python.org/3/c-api/tuple.html#c.PyTuple_SetItem). + """ # PyTuple_SetItem steals the reference - the element object will be # destroyed along with the tuple self._dec_total_rc() - # int PyTuple_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o) - return self.lib.get_function[ - fn (PyObjectPtr, Int, PyObjectPtr) -> c_int - ](StringRef("PyTuple_SetItem"))(tuple_obj, index, element) + return self.lib.call["PyTuple_SetItem", c_int]( + tuple_obj, index, element + ) # ===-------------------------------------------------------------------===# # Python List operations # ===-------------------------------------------------------------------===# fn PyList_New(inout self, length: Int) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/list.html#c.PyList_New.""" + """[Reference]( + https://docs.python.org/3/c-api/list.html#c.PyList_New). + """ - var r = self.lib.get_function[fn (Int) -> PyObjectPtr]("PyList_New")( - length - ) + var r = self.lib.call["PyList_New", PyObjectPtr](length) self.log( r._get_ptr_as_int(), @@ -1576,33 +1556,33 @@ struct CPython: fn PyList_SetItem( inout self, list_obj: PyObjectPtr, index: Int, value: PyObjectPtr ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/list.html#c.PyList_SetItem.""" + """[Reference]( + https://docs.python.org/3/c-api/list.html#c.PyList_SetItem). + """ # PyList_SetItem steals the reference - the element object will be # destroyed along with the list self._dec_total_rc() - return self.lib.get_function[ - fn (PyObjectPtr, Int, PyObjectPtr) -> PyObjectPtr - ]("PyList_SetItem")(list_obj, index, value) + return self.lib.call["PyList_SetItem", PyObjectPtr]( + list_obj, index, value + ) fn PyList_GetItem( inout self, list_obj: PyObjectPtr, index: Int ) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/list.html#c.PyList_GetItem.""" - - return self.lib.get_function[fn (PyObjectPtr, Int) -> PyObjectPtr]( - "PyList_GetItem" - )(list_obj, index) + """[Reference]( + https://docs.python.org/3/c-api/list.html#c.PyList_GetItem). + """ + return self.lib.call["PyList_GetItem", PyObjectPtr](list_obj, index) # ===-------------------------------------------------------------------===# # Concrete Objects # ref: https://docs.python.org/3/c-api/concrete.html # ===-------------------------------------------------------------------===# - # PyObject *Py_None - # https://docs.python.org/3/c-api/none.html#c.Py_None fn Py_None(inout self) -> PyObjectPtr: - """Get a None value, of type NoneType.""" + """Get a None value, of type NoneType. [Reference]( + https://docs.python.org/3/c-api/none.html#c.Py_None).""" # Get pointer to the immortal `None` PyObject struct instance. # Note: @@ -1611,23 +1591,23 @@ struct CPython: # macros. # TODO(MSTDL-977): # Investigate doing this without hard-coding private API details. - ptr = self.lib.get_symbol[PyObject]("_Py_NoneStruct") + var ptr = self.lib.get_symbol[PyObject]("_Py_NoneStruct") if not ptr: abort("error: unable to get pointer to CPython `None` struct") return PyObjectPtr(ptr) + # ===-------------------------------------------------------------------===# # Boolean Objects - # ref: https://docs.python.org/3/c-api/bool.html + # ===-------------------------------------------------------------------===# - # PyObject *PyBool_FromLong(long v) fn PyBool_FromLong(inout self, value: c_long) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/bool.html#c.PyBool_FromLong.""" + """[Reference]( + https://docs.python.org/3/c-api/bool.html#c.PyBool_FromLong). + """ - r = self.lib.get_function[fn (c_long) -> PyObjectPtr]( - "PyBool_FromLong" - )(value) + var r = self.lib.call["PyBool_FromLong", PyObjectPtr](value) self.log( r._get_ptr_as_int(), @@ -1640,17 +1620,16 @@ struct CPython: self._inc_total_rc() return r + # ===-------------------------------------------------------------------===# # Integer Objects - # ref: https://docs.python.org/3/c-api/long.html + # ===-------------------------------------------------------------------===# - # PyObject *PyLong_FromSsize_t(Py_ssize_t v) fn PyLong_FromSsize_t(inout self, value: c_ssize_t) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/long.html#c.PyLong_FromSsize_t. + """[Reference]( + https://docs.python.org/3/c-api/long.html#c.PyLong_FromSsize_t). """ - r = self.lib.get_function[fn (c_ssize_t) -> PyObjectPtr]( - "PyLong_FromSsize_t" - )(value) + var r = self.lib.call["PyLong_FromSsize_t", PyObjectPtr](value) self.log( r._get_ptr_as_int(), @@ -1663,13 +1642,12 @@ struct CPython: self._inc_total_rc() return r - # PyObject *PyLong_FromSize_t(Py_ssize_t v) fn PyLong_FromSize_t(inout self, value: c_size_t) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/long.html#c.PyLong_FromSize_t.""" + """[Reference]( + https://docs.python.org/3/c-api/long.html#c.PyLong_FromSize_t). + """ - r = self.lib.get_function[fn (c_size_t) -> PyObjectPtr]( - "PyLong_FromSize_t" - )(value) + var r = self.lib.call["PyLong_FromSize_t", PyObjectPtr](value) self.log( r._get_ptr_as_int(), @@ -1682,25 +1660,22 @@ struct CPython: self._inc_total_rc() return r - # Py_ssize_t PyLong_AsSsize_t(PyObject *pylong) fn PyLong_AsSsize_t(inout self, py_object: PyObjectPtr) -> c_ssize_t: - """See https://docs.python.org/3/c-api/long.html#c.PyLong_AsSsize_t.""" - - return self.lib.get_function[fn (PyObjectPtr) -> c_ssize_t]( - "PyLong_AsSsize_t" - )(py_object) + """[Reference]( + https://docs.python.org/3/c-api/long.html#c.PyLong_AsSsize_t). + """ + return self.lib.call["PyLong_AsSsize_t", c_ssize_t](py_object) + # ===-------------------------------------------------------------------===# # Floating-Point Objects - # ref: https://docs.python.org/3/c-api/float.html + # ===-------------------------------------------------------------------===# - # PyObject *PyFloat_FromDouble(double v)¶ fn PyFloat_FromDouble(inout self, value: Float64) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble. + """[Reference]( + https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble). """ - r = self.lib.get_function[fn (Float64) -> PyObjectPtr]( - "PyFloat_FromDouble" - )(value) + var r = self.lib.call["PyFloat_FromDouble", PyObjectPtr](value) self.log( r._get_ptr_as_int(), @@ -1713,30 +1688,23 @@ struct CPython: self._inc_total_rc() return r - # double PyFloat_AsDouble(PyObject *pyfloat) fn PyFloat_AsDouble(inout self, py_object: PyObjectPtr) -> Float64: - """See https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble.""" - - return self.lib.get_function[fn (PyObjectPtr) -> Float64]( - "PyFloat_AsDouble" - )(py_object) + """[Reference]( + https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble). + """ + return self.lib.call["PyFloat_AsDouble", Float64](py_object) + # ===-------------------------------------------------------------------===# # Unicode Objects - # https://docs.python.org/3/c-api/unicode.html + # ===-------------------------------------------------------------------===# - # PyObject *PyUnicode_DecodeUTF8(const char *str, Py_ssize_t size, const char *errors) fn PyUnicode_DecodeUTF8(inout self, strref: StringRef) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8. + """[Reference]( + https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8). """ - r = self.lib.get_function[ - fn ( - UnsafePointer[c_char], - c_ssize_t, - UnsafePointer[c_char], - ) -> PyObjectPtr - ]("PyUnicode_DecodeUTF8")( - strref.data.bitcast[Int8](), + var r = self.lib.call["PyUnicode_DecodeUTF8", PyObjectPtr]( + strref.unsafe_ptr().bitcast[Int8](), strref.length, "strict".unsafe_cstr_ptr(), ) @@ -1753,16 +1721,10 @@ struct CPython: return r fn PyUnicode_DecodeUTF8(inout self, strslice: StringSlice) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8. - """ - # return self.PyUnicode_DecodeUTF8(StringRef(strslice.unsafe_ptr(), strslice.byte_length())) - r = self.lib.get_function[ - fn ( - UnsafePointer[c_char], - c_ssize_t, - UnsafePointer[c_char], - ) -> PyObjectPtr - ]("PyUnicode_DecodeUTF8")( + """[Reference]( + https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8). + """ + var r = self.lib.call["PyUnicode_DecodeUTF8", PyObjectPtr]( strslice.unsafe_ptr().bitcast[Int8](), strslice.byte_length(), "strict".unsafe_cstr_ptr(), @@ -1806,19 +1768,15 @@ struct CPython: return py_slice - # const char *PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *size) fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: - """See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize. + """[Reference]( + https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize). """ - s = StringRef() - s.data = self.lib.get_function[ - fn (PyObjectPtr, UnsafePointer[c_ssize_t]) -> UnsafePointer[c_char] - ]("PyUnicode_AsUTF8AndSize")( - py_object, UnsafePointer.address_of(s.length) - ).bitcast[ - UInt8 - ]() + var s = StringRef() + s.data = self.lib.call[ + "PyUnicode_AsUTF8AndSize", UnsafePointer[c_char] + ](py_object, UnsafePointer.address_of(s.length)).bitcast[UInt8]() return s # ===-------------------------------------------------------------------===# @@ -1826,32 +1784,26 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyErr_Clear(inout self): - """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear.""" - - self.lib.get_function[fn () -> None]("PyErr_Clear")() + """[Reference]( + https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear). + """ + self.lib.call["PyErr_Clear"]() fn PyErr_Occurred(inout self) -> Bool: - """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred. + """[Reference]( + https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred). """ - - var value = self.lib.get_function[fn () -> PyObjectPtr]( - "PyErr_Occurred" - )() - return not value.is_null() + return not self.lib.call["PyErr_Occurred", PyObjectPtr]().is_null() fn PyErr_Fetch(inout self) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Fetch.""" + """[Reference]( + https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Fetch). + """ var type = PyObjectPtr() var value = PyObjectPtr() var traceback = PyObjectPtr() - var func = self.lib.get_function[ - fn ( - UnsafePointer[PyObjectPtr], - UnsafePointer[PyObjectPtr], - UnsafePointer[PyObjectPtr], - ) -> None - ]("PyErr_Fetch")( + self.lib.call["PyErr_Fetch"]( UnsafePointer.address_of(type), UnsafePointer.address_of(value), UnsafePointer.address_of(traceback), @@ -1870,30 +1822,21 @@ struct CPython: _ = traceback return r - fn PyErr_SetNone( - inout self, - type: PyObjectPtr, - ): - """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetNone. + fn PyErr_SetNone(inout self, type: PyObjectPtr): + """[Reference]( + https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetNone). """ - - var func = self.lib.get_function[fn (PyObjectPtr) -> None]( - "PyErr_SetNone" - ) - - return func(type) + self.lib.call["PyErr_SetNone"](type) fn PyErr_SetString( inout self, type: PyObjectPtr, message: UnsafePointer[c_char], ): - """See https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetString. + """[Reference]( + https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetString). """ - - self.lib.get_function[fn (PyObjectPtr, UnsafePointer[c_char]) -> None]( - "PyErr_SetString" - )(type, message) + self.lib.call["PyErr_SetString"](type, message) # ===-------------------------------------------------------------------===# # Python Error types @@ -1903,14 +1846,13 @@ struct CPython: inout self, global_name: StringLiteral, ) -> PyObjectPtr: - """Get a Python borrowed reference to the specified global exception object. + """Get a Python borrowed reference to the specified global exception + object. """ # Get pointer to the immortal `global_name` PyObject struct # instance. - var ptr: UnsafePointer[PyObjectPtr] = self.lib.get_symbol[PyObjectPtr]( - global_name - ) + var ptr = self.lib.get_symbol[PyObjectPtr](global_name) if not ptr: abort( @@ -1926,11 +1868,11 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyIter_Next(inout self, iterator: PyObjectPtr) -> PyObjectPtr: - """See https://docs.python.org/3/c-api/iter.html#c.PyIter_Next.""" + """[Reference]( + https://docs.python.org/3/c-api/iter.html#c.PyIter_Next). + """ - var next_obj = self.lib.get_function[fn (PyObjectPtr) -> PyObjectPtr]( - "PyIter_Next" - )(iterator) + var next_obj = self.lib.call["PyIter_Next", PyObjectPtr](iterator) self.log( next_obj._get_ptr_as_int(), @@ -1947,36 +1889,28 @@ struct CPython: return next_obj fn PyIter_Check(inout self, obj: PyObjectPtr) -> Bool: - """See https://docs.python.org/3/c-api/iter.html#c.PyIter_Check.""" - - # int PyIter_Check(PyObject *o) - var follows_iter_protocol = self.lib.get_function[ - fn (PyObjectPtr) -> c_int - ]("PyIter_Check")(obj) - return follows_iter_protocol != 0 + """[Reference]( + https://docs.python.org/3/c-api/iter.html#c.PyIter_Check). + """ + return self.lib.call["PyIter_Check", c_int](obj) != 0 - # int PySequence_Check(PyObject *o) fn PySequence_Check(inout self, obj: PyObjectPtr) -> Bool: - """See https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check. + """[Reference]( + https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check). """ - - var follows_seq_protocol = self.lib.get_function[ - fn (PyObjectPtr) -> c_int - ]("PySequence_Check")(obj) - return follows_seq_protocol != 0 + return self.lib.call["PySequence_Check", c_int](obj) != 0 # ===-------------------------------------------------------------------===# # Python Slice Creation # ===-------------------------------------------------------------------===# - # PyObject *PySlice_New(PyObject *start, PyObject *stop, PyObject *step) - # ref: https://docs.python.org/3/c-api/slice.html#c.PySlice_New fn PySlice_New( inout self, start: PyObjectPtr, stop: PyObjectPtr, step: PyObjectPtr ) -> PyObjectPtr: - var r = self.lib.get_function[ - fn (PyObjectPtr, PyObjectPtr, PyObjectPtr) -> PyObjectPtr - ]("PySlice_New")(start, stop, step) + """[Reference]( + https://docs.python.org/3/c-api/slice.html#c.PySlice_New). + """ + var r = self.lib.call["PySlice_New", PyObjectPtr](start, stop, step) self.log( r._get_ptr_as_int(), diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 15e1cb13c2..3b90946f2a 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -317,6 +317,47 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): return res + @always_inline + fn call[ + name: StringLiteral, + return_type: AnyTrivialRegType = NoneType, + *T: AnyType, + ](self, *args: *T) -> return_type: + """Call a function with any amount of arguments. + + Parameters: + name: The name of the function. + return_type: The return type of the function. + T: The types of `args`. + + Args: + args: The arguments. + + Returns: + The result. + """ + return self.call[name, return_type](args) + + fn call[ + name: StringLiteral, return_type: AnyTrivialRegType = NoneType + ](self, args: VariadicPack[element_trait=AnyType]) -> return_type: + """Call a function with any amount of arguments. + + Parameters: + name: The name of the function. + return_type: The return type of the function. + + Args: + args: The arguments. + + Returns: + The result. + """ + + debug_assert(self.check_symbol(name), "symbol not found: " + name) + var v = _LITRefPackHelper(args._value).get_loaded_kgen_pack() + return self.get_function[fn (__type_of(v)) -> return_type](name)(v) + # ===----------------------------------------------------------------------===# # Library Load From 71b2ee1d2a47d00bdc954e942d661f55ab2c4b3c Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 5 Nov 2024 07:35:06 -0800 Subject: [PATCH 1831/2019] [******][GPU] Emit llvm code during compilation for AMD MODULAR_ORIG_COMMIT_REV_ID: c989efe46e826b2b803dd03d5f94b23d37e99c88 --- stdlib/src/sys/info.mojo | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 004c77a7ef..c7e168a33a 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -28,6 +28,15 @@ fn _current_target() -> __mlir_type.`!kgen.target`: return __mlir_attr.`#kgen.param.expr : !kgen.target` +fn _get_arch[target: __mlir_type.`!kgen.target`]() -> String: + return __mlir_attr[ + `#kgen.param.expr : !kgen.string`, + ] + + @always_inline("nodebug") fn _current_arch() -> __mlir_type.`!kgen.string`: return __mlir_attr[ From 3f64646be8843967a5c0179fb9c002515ecb1045 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 5 Nov 2024 11:08:48 -0600 Subject: [PATCH 1832/2019] [stdlib] cleanup: Combine `_LITRefPackHelper` functionality into `VariadicPack` `_LITRefPackHelper` and `VariadicPack` are both wrappers around the low-level `lit.ref.pack` type. `_LITRefPackHelper` was a utility to construct packed argument lists compatible with C functions taking variadic argument lists. That functionality is now part of the private API of VariadicPack. This minimizes duplicated code needed for working with `lit.ref.pack`. MODULAR_ORIG_COMMIT_REV_ID: a0c03a56fc49676d4d7467780f151ad6598b1c10 --- stdlib/src/builtin/builtin_list.mojo | 121 +++++++++++---------------- stdlib/src/builtin/io.mojo | 5 +- stdlib/src/sys/_assembly.mojo | 3 +- stdlib/src/sys/ffi.mojo | 5 +- stdlib/src/sys/intrinsics.mojo | 3 +- 5 files changed, 54 insertions(+), 83 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 6fca53e509..6a366739ae 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -465,85 +465,13 @@ struct VariadicListMem[ # ===----------------------------------------------------------------------===# -# _LITRefPackHelper +# VariadicPack # ===----------------------------------------------------------------------===# alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] -@value -struct _LITRefPackHelper[ - is_mutable: Bool, //, - origin: Origin[is_mutable].type, - address_space: __mlir_type.index, - element_trait: _AnyTypeMetaType, - *element_types: element_trait, -]: - """This struct mirrors the !lit.ref.pack type, and provides aliases and - methods that are useful for working with it.""" - - alias _mlir_type = __mlir_type[ - `!lit.ref.pack<:variadic<`, - element_trait, - `> `, - element_types, - `, `, - origin, - `, `, - address_space, - `>`, - ] - - var storage: Self._mlir_type - - # This is the element_types list lowered to `variadic` type for kgen. - alias _kgen_element_types = rebind[ - __mlir_type.`!kgen.variadic` - ](Self.element_types) - - # Use variadic_ptr_map to construct the type list of the !kgen.pack that the - # !lit.ref.pack will lower to. It exposes the pointers introduced by the - # references. - alias _variadic_pointer_types = __mlir_attr[ - `#kgen.param.expr: !kgen.variadic`, - ] - - # This is the !kgen.pack type with pointer elements. - alias kgen_pack_with_pointer_type = __mlir_type[ - `!kgen.pack<:variadic `, Self._variadic_pointer_types, `>` - ] - - # This rebinds `in_pack` to the equivalent `!kgen.pack` with kgen pointers. - @always_inline("nodebug") - fn get_as_kgen_pack(self) -> Self.kgen_pack_with_pointer_type: - return rebind[Self.kgen_pack_with_pointer_type](self.storage) - - alias _variadic_with_pointers_removed = __mlir_attr[ - `#kgen.param.expr: !kgen.variadic`, - ] - - # This is the `!kgen.pack` type that happens if one loads all the elements - # of the pack. - alias loaded_kgen_pack_type = __mlir_type[ - `!kgen.pack<:variadic `, Self._variadic_with_pointers_removed, `>` - ] - - # This returns the stored KGEN pack after loading all of the elements. - @always_inline("nodebug") - fn get_loaded_kgen_pack(self) -> Self.loaded_kgen_pack_type: - return __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) - - -# ===----------------------------------------------------------------------===# -# VariadicPack -# ===----------------------------------------------------------------------===# - - @register_passable struct VariadicPack[ elt_is_mutable: Bool, //, @@ -695,3 +623,50 @@ struct VariadicPack[ @parameter for i in range(Self.__len__()): func[i](self[i]) + + # ===-------------------------------------------------------------------===# + # C Pack Utilities + # ===-------------------------------------------------------------------===# + + # This is the element_types list lowered to `variadic` type for kgen. + alias _kgen_element_types = rebind[ + __mlir_type.`!kgen.variadic` + ](Self.element_types) + + # Use variadic_ptr_map to construct the type list of the !kgen.pack that the + # !lit.ref.pack will lower to. It exposes the pointers introduced by the + # references. + alias _variadic_pointer_types = __mlir_attr[ + `#kgen.param.expr: !kgen.variadic`, + ] + + # This is the !kgen.pack type with pointer elements. + alias _kgen_pack_with_pointer_type = __mlir_type[ + `!kgen.pack<:variadic `, Self._variadic_pointer_types, `>` + ] + + # This rebinds `in_pack` to the equivalent `!kgen.pack` with kgen pointers. + @doc_private + @always_inline("nodebug") + fn get_as_kgen_pack(self) -> Self._kgen_pack_with_pointer_type: + return rebind[Self._kgen_pack_with_pointer_type](self._value) + + alias _variadic_with_pointers_removed = __mlir_attr[ + `#kgen.param.expr: !kgen.variadic`, + ] + + # This is the `!kgen.pack` type that happens if one loads all the elements + # of the pack. + alias _loaded_kgen_pack_type = __mlir_type[ + `!kgen.pack<:variadic `, Self._variadic_with_pointers_removed, `>` + ] + + # This returns the stored KGEN pack after loading all of the elements. + @doc_private + @always_inline("nodebug") + fn get_loaded_kgen_pack(self) -> Self._loaded_kgen_pack_type: + return __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index c9b781a764..52d9b5cb7d 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -27,7 +27,6 @@ from sys.ffi import OpaquePointer from utils import Span, write_buffered, write_args from collections import InlineArray -from builtin.builtin_list import _LITRefPackHelper from builtin.dtype import _get_dtype_printf_format from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer, memcpy @@ -163,7 +162,7 @@ fn _printf[ # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C printf call. Load # all the members of the pack. - var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() + var loaded_pack = arguments.get_loaded_kgen_pack() @parameter if triple_is_nvidia_cuda(): @@ -210,7 +209,7 @@ fn _snprintf[ # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C snprintf call. Load # all the members of the pack. - var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() + var loaded_pack = arguments.get_loaded_kgen_pack() return int( __mlir_op.`pop.external_call`[ diff --git a/stdlib/src/sys/_assembly.mojo b/stdlib/src/sys/_assembly.mojo index 4429d45c4e..6cb2123393 100644 --- a/stdlib/src/sys/_assembly.mojo +++ b/stdlib/src/sys/_assembly.mojo @@ -13,7 +13,6 @@ """This module includes the inlined_assembly function.""" from sys.intrinsics import _mlirtype_is_eq -from builtin.builtin_list import _LITRefPackHelper # ===----------------------------------------------------------------------===# # 0-arg @@ -29,7 +28,7 @@ fn inlined_assembly[ has_side_effect: Bool = True, ](*arguments: *types) -> result_type: """Generates assembly via inline assembly.""" - var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() + var loaded_pack = arguments.get_loaded_kgen_pack() @parameter if _mlirtype_is_eq[result_type, NoneType](): diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 3b90946f2a..6104d7f5e3 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -19,7 +19,6 @@ from utils import StringRef from .info import os_is_linux, os_is_windows, is_64bit, os_is_macos from .intrinsics import _mlirtype_is_eq -from builtin.builtin_list import _LITRefPackHelper from sys._libc import dlerror, dlopen, dlclose, dlsym @@ -445,7 +444,7 @@ fn external_call[ # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C printf call. Load # all the members of the pack. - var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() + var loaded_pack = arguments.get_loaded_kgen_pack() @parameter if _mlirtype_is_eq[type, NoneType](): @@ -487,7 +486,7 @@ fn _external_call_const[ # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C printf call. Load # all the members of the pack. - var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() + var loaded_pack = arguments.get_loaded_kgen_pack() return __mlir_op.`pop.external_call`[ func = callee.value, diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 036be0aff6..d5ceafdfb0 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -21,7 +21,6 @@ from sys import PrefetchLocality from .info import sizeof, triple_is_nvidia_cuda from ._assembly import inlined_assembly -from builtin.builtin_list import _LITRefPackHelper import math from memory import AddressSpace, UnsafePointer @@ -55,7 +54,7 @@ fn llvm_intrinsic[ The result of calling the llvm intrinsic with no arguments. """ - var loaded_pack = _LITRefPackHelper(arguments._value).get_loaded_kgen_pack() + var loaded_pack = arguments.get_loaded_kgen_pack() @parameter if _mlirtype_is_eq[type, NoneType](): From 3d0d3e4dda11b4a96abcceae2f6090fc6f387958 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 5 Nov 2024 11:41:28 -0600 Subject: [PATCH 1833/2019] [stdlib] hotfix: Fix broken use of `_LITRefPackHelper` due to conflicting PRs MODULAR_ORIG_COMMIT_REV_ID: 0b8f5c4087f4aa6654efa7427abd81f58e73d331 --- stdlib/src/sys/ffi.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 6104d7f5e3..ad4058b1e5 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -354,7 +354,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): """ debug_assert(self.check_symbol(name), "symbol not found: " + name) - var v = _LITRefPackHelper(args._value).get_loaded_kgen_pack() + var v = args.get_loaded_kgen_pack() return self.get_function[fn (__type_of(v)) -> return_type](name)(v) From 49fba7372c288ce21dd8e01fd9fb76781d571fef Mon Sep 17 00:00:00 2001 From: Peiming Liu Date: Tue, 5 Nov 2024 10:37:05 -0800 Subject: [PATCH 1834/2019] [stdlib] Mitigate lifetime issue by capturing value instead of ref. MODULAR_ORIG_COMMIT_REV_ID: 7d75b8362b1a61366e49804fbbd74e6061939f93 --- stdlib/benchmarks/collections/bench_dict.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index d0d977d63e..d8c782846e 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -77,6 +77,7 @@ fn bench_dict_lookup[size: Int](inout b: Bencher) raises: var items = make_dict[size]() var closest_divisor = ceil(100 / size) + @__copy_capture(closest_divisor) @always_inline @parameter fn call_fn() raises: From 03d3bc46ddeb2ca4cbcfdfae2de10f401fc51d96 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 5 Nov 2024 13:35:26 -0600 Subject: [PATCH 1835/2019] This change is done to make this pointer type naming consistent with the other standard library pointer types, including `Pointer` and `UnsafePointer`. Other names were considered, namely `UniquePointer`. Briefly, some arguments considered were (pro and cons): 1. `UniquePointer` is more familiar to users coming from C++ 2. `Pointer` with a mutable origin is also a "unique" pointer, so the name UniquePointer might be confusing in that sense. 3. The defining characteristic of `UniquePointer`, then, is that it has _ownership_ of its pointee, not that it's a unique pointer to the value. (In other words, the uniqueness is a consequence of ownership.) 4. `UniquePointer` and `UnsafePointer` are visually nearly identical, particularly at-a-glance, where they only differ by 3 characters that occur in the middle of the identifier: ```mojo var ptr: UniquePointer[Int] = ... var ptr: UnsafePointer[Int] = ... ``` We're open to revisiting this naming in the future after getting some experience with how this name feels in practice, and after getting feedback from the community. MODULAR_ORIG_COMMIT_REV_ID: 8c808afda982ffa1fe91e7d79bcd9678d80e8811 --- docs/changelog.md | 5 +- docs/manual/values/lifetimes.ipynb | 12 ++-- stdlib/src/memory/__init__.mojo | 2 +- .../memory/{box.mojo => owned_pointer.mojo} | 64 ++++++++++--------- stdlib/src/python/_bindings.mojo | 2 +- ...{test_box.mojo => test_owned_pointer.mojo} | 34 +++++----- 6 files changed, 64 insertions(+), 55 deletions(-) rename stdlib/src/memory/{box.mojo => owned_pointer.mojo} (69%) rename stdlib/test/memory/{test_box.mojo => test_owned_pointer.mojo} (78%) diff --git a/docs/changelog.md b/docs/changelog.md index ebd5e3e49b..85f46f7ac9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -158,8 +158,9 @@ what we publish. determining a default SDK to use. The user can select the default SDK to use with the `Mojo: Select the default MAX SDK` command. -- Added a new [`Box`](/mojo/stdlib/memory/box/Box) type as a safe, single-owner, - non-nullable smart pointer with similar semantics to Rust's +- Added a new [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) + type as a safe, single-owner, non-nullable smart pointer with similar + semantics to Rust's [`Box<>`](https://doc.rust-lang.org/std/boxed/struct.Box.html) and C++'s [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr). diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 9f525cd30a..0c350dac66 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -213,9 +213,9 @@ "function.)\n", "\n", "The following struct stores a string value using a \n", - "[`Box`](/mojo/stdlib/memory/box/Box): a smart pointer that \n", - "holds an owned value. The `as_ptr()` method returns a `Pointer` to the stored\n", - "string, using the same origin as the original `Box`." + "[`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer): a smart\n", + "pointer that holds an owned value. The `as_ptr()` method returns a `Pointer` to\n", + "the stored string, using the same origin as the original `OwnedPointer`." ] }, { @@ -224,13 +224,13 @@ "metadata": {}, "outputs": [], "source": [ - "from memory import Box, Pointer\n", + "from memory import OwnedPointer, Pointer\n", "\n", "struct BoxedString:\n", - " var box: Box[String]\n", + " var box: OwnedPointer[String]\n", "\n", " fn __init__(inout self, value: String):\n", - " self.box = Box(value)\n", + " self.box = OwnedPointer(value)\n", "\n", " fn as_ptr(self) -> Pointer[String, __origin_of(self.box)]:\n", " return Pointer.address_of(self.box[])" diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 1074e5983a..06684698a7 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -13,8 +13,8 @@ """Implements the memory package.""" from .arc import Arc -from .box import Box from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation +from .owned_pointer import OwnedPointer from .pointer import AddressSpace, Pointer from .unsafe import bitcast, pack_bits from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/memory/box.mojo b/stdlib/src/memory/owned_pointer.mojo similarity index 69% rename from stdlib/src/memory/box.mojo rename to stdlib/src/memory/owned_pointer.mojo index cefcf5ce1d..3976fc8c6d 100644 --- a/stdlib/src/memory/box.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -13,7 +13,7 @@ from memory import UnsafePointer, stack_allocation, memcpy -struct Box[T: AnyType]: +struct OwnedPointer[T: AnyType]: """A safe, owning, smart pointer. This smart pointer is designed for cases where there is clear ownership @@ -22,7 +22,7 @@ struct Box[T: AnyType]: may exist. Parameters: - T: The type to be stored in the Box[]. + T: The type to be stored in the OwnedPointer[]. """ var _inner: UnsafePointer[T, AddressSpace.GENERIC] @@ -31,58 +31,62 @@ struct Box[T: AnyType]: # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__[T: Movable](inout self: Box[T], owned value: T): - """Construct a new Box[] by moving the passed value into a new backing allocation. + fn __init__[T: Movable](inout self: OwnedPointer[T], owned value: T): + """Construct a new OwnedPointer[] by moving the passed value into a new backing allocation. Parameters: T: The type of the data to store. It is restricted to `Movable` here to allow efficient move construction. Args: - value: The value to move into the Box[]. + value: The value to move into the OwnedPointer[]. """ self._inner = UnsafePointer[T].alloc(1) self._inner.init_pointee_move(value^) - fn __init__[T: ExplicitlyCopyable](inout self: Box[T], *, copy_value: T): - """Construct a new Box[] by explicitly copying the passed value into a new backing allocation. + fn __init__[ + T: ExplicitlyCopyable + ](inout self: OwnedPointer[T], *, copy_value: T): + """Construct a new OwnedPointer[] by explicitly copying the passed value into a new backing allocation. Parameters: T: The type of the data to store. Args: - copy_value: The value to explicitly copy into the Box[]. + copy_value: The value to explicitly copy into the OwnedPointer[]. """ self._inner = UnsafePointer[T].alloc(1) self._inner.init_pointee_explicit_copy(copy_value) - fn __init__[T: Copyable, U: NoneType = None](inout self: Box[T], value: T): - """Construct a new Box[] by copying the passed value into a new backing allocation. + fn __init__[ + T: Copyable, U: NoneType = None + ](inout self: OwnedPointer[T], value: T): + """Construct a new OwnedPointer[] by copying the passed value into a new backing allocation. Parameters: T: The type of the data to store. U: A dummy type parameter, to lower the selection priority of this ctor. Args: - value: The value to copy into the Box[]. + value: The value to copy into the OwnedPointer[]. """ self._inner = UnsafePointer[T].alloc(1) self._inner.init_pointee_copy(value) fn __init__[ T: ExplicitlyCopyable - ](inout self: Box[T], *, copy_box: Box[T],): - """Construct a new Box[] by explicitly copying the value from another Box[]. + ](inout self: OwnedPointer[T], *, other: OwnedPointer[T],): + """Construct a new OwnedPointer[] by explicitly copying the value from another OwnedPointer[]. Parameters: T: The type of the data to store. Args: - copy_box: The Box[] to copy. + other: The OwnedPointer[] to copy. """ - self.__init__(copy_value=copy_box[]) + self.__init__(copy_value=other[]) fn __moveinit__(inout self, owned existing: Self): - """Move this Box[]. + """Move this OwnedPointer[]. Args: existing: The value to move. @@ -90,8 +94,8 @@ struct Box[T: AnyType]: self._inner = existing._inner existing._inner = UnsafePointer[T]() - fn __del__(owned self: Box[T]): - """Destroy the Box[].""" + fn __del__(owned self: OwnedPointer[T]): + """Destroy the OwnedPointer[].""" self._inner.destroy_pointee() self._inner.free() @@ -102,10 +106,10 @@ struct Box[T: AnyType]: fn __getitem__( ref [_, AddressSpace.GENERIC._value.value]self ) -> ref [self, AddressSpace.GENERIC._value.value] T: - """Returns a reference to the box's underlying data with parametric mutability. + """Returns a reference to the pointers's underlying data with parametric mutability. Returns: - A reference to the data underlying the Box[]. + A reference to the data underlying the OwnedPointer[]. """ # This should have a widening conversion here that allows # the mutable ref that is always (potentially unsafely) @@ -120,24 +124,25 @@ struct Box[T: AnyType]: # ===-------------------------------------------------------------------===# fn unsafe_ptr(self) -> UnsafePointer[T]: - """UNSAFE: returns the backing pointer for this Box[]. + """UNSAFE: returns the backing pointer for this OwnedPointer[]. Returns: - An UnsafePointer to the backing allocation for this Box[]. + An UnsafePointer to the backing allocation for this OwnedPointer[]. """ return self._inner - fn take[T: Movable](owned self: Box[T]) -> T: - """Move the value within the Box[] out of it, consuming the Box[] in the process. + fn take[T: Movable](owned self: OwnedPointer[T]) -> T: + """Move the value within the OwnedPointer[] out of it, consuming the + OwnedPointer[] in the process. Parameters: - T: The type of the data backing this Box[]. `take()` only exists for T: Movable + T: The type of the data backing this OwnedPointer[]. `take()` only exists for T: Movable since this consuming operation only makes sense for types that you want to avoid copying. For types that are Copy or ExplicitlyCopy but are not Movable, you can copy them through - `__getitem__` as in `var v = some_box_var[]`. + `__getitem__` as in `var v = some_ptr_var[]`. Returns: - The data that is (was) backing the Box[]. + The data that is (was) backing the OwnedPointer[]. """ var r = self._inner.take_pointee() self._inner.free() @@ -146,7 +151,8 @@ struct Box[T: AnyType]: return r^ fn steal_data(owned self) -> UnsafePointer[T]: - """Take ownership over the heap allocated pointer backing this `Box`. + """Take ownership over the heap allocated pointer backing this + `OwnedPointer`. Safety: This function is not unsafe to call, as a memory leak is not @@ -157,7 +163,7 @@ struct Box[T: AnyType]: Failure to do so will leak memory. Returns: - The pointer owned by this box. + The pointer owned by this instance. """ var ptr = self._inner diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index aca723459f..67aaf1f467 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer, Box +from memory import UnsafePointer from sys.ffi import c_int, OpaquePointer from sys.info import sizeof diff --git a/stdlib/test/memory/test_box.mojo b/stdlib/test/memory/test_owned_pointer.mojo similarity index 78% rename from stdlib/test/memory/test_box.mojo rename to stdlib/test/memory/test_owned_pointer.mojo index 6e7303516a..a09b8fa59b 100644 --- a/stdlib/test/memory/test_box.mojo +++ b/stdlib/test/memory/test_owned_pointer.mojo @@ -19,17 +19,17 @@ from test_utils import ( ImplicitCopyOnly, ObservableDel, ) -from memory import Box, UnsafePointer +from memory import OwnedPointer, UnsafePointer def test_basic_ref(): - var b = Box(1) + var b = OwnedPointer(1) assert_equal(1, b[]) -def test_box_copy_constructor(): - var b = Box(1) - var b2 = Box(copy_box=b) +def test_owned_pointer_copy_constructor(): + var b = OwnedPointer(1) + var b2 = OwnedPointer(other=b) assert_equal(1, b[]) assert_equal(1, b2[]) @@ -39,7 +39,7 @@ def test_box_copy_constructor(): def test_copying_constructor(): var v = ImplicitCopyOnly(1) - var b = Box(v) + var b = OwnedPointer(v) assert_equal(b[].value, 1) assert_equal(b[].copy_count, 1) # this should only ever require one copy @@ -47,7 +47,7 @@ def test_copying_constructor(): def test_explicitly_copying_constructor(): var v = ExplicitCopyOnly(1) - var b = Box(copy_value=v) + var b = OwnedPointer(copy_value=v) assert_equal(b[].value, 1) assert_equal(b[].copy_count, 1) # this should only ever require one copy @@ -55,13 +55,13 @@ def test_explicitly_copying_constructor(): def test_moving_constructor(): var v = MoveOnly[Int](1) - var b = Box(v^) + var b = OwnedPointer(v^) assert_equal(b[].data, 1) def test_basic_ref_mutate(): - var b = Box(1) + var b = OwnedPointer(1) assert_equal(1, b[]) b[] = 2 @@ -70,7 +70,7 @@ def test_basic_ref_mutate(): def test_multiple_refs(): - var b = Box(1) + var b = OwnedPointer(1) var borrow1 = b[] var borrow2 = b[] @@ -80,7 +80,7 @@ def test_multiple_refs(): def test_basic_del(): var deleted = False - var b = Box(ObservableDel(UnsafePointer.address_of(deleted))) + var b = OwnedPointer(ObservableDel(UnsafePointer.address_of(deleted))) assert_false(deleted) @@ -90,14 +90,14 @@ def test_basic_del(): def test_take(): - var b = Box(1) + var b = OwnedPointer(1) var v = b^.take() assert_equal(1, v) def test_moveinit(): var deleted = False - var b = Box(ObservableDel(UnsafePointer.address_of(deleted))) + var b = OwnedPointer(ObservableDel(UnsafePointer.address_of(deleted))) var p1 = b.unsafe_ptr() var b2 = b^ @@ -112,9 +112,11 @@ def test_moveinit(): def test_steal_data(): var deleted = False - var box = Box(ObservableDel(UnsafePointer.address_of(deleted))) + var owned_ptr = OwnedPointer( + ObservableDel(UnsafePointer.address_of(deleted)) + ) - var ptr = box^.steal_data() + var ptr = owned_ptr^.steal_data() # Check that `Box` did not deinitialize its pointee. assert_false(deleted) @@ -125,7 +127,7 @@ def test_steal_data(): def main(): test_basic_ref() - test_box_copy_constructor() + test_owned_pointer_copy_constructor() test_moving_constructor() test_copying_constructor() test_explicitly_copying_constructor() From 65f5686e3efc3dd5852995a6a8d89efaccbecf54 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 5 Nov 2024 19:00:56 -0800 Subject: [PATCH 1836/2019] [KGEN] Rename hasmore with has_next for iterators, NFC MODULAR_ORIG_COMMIT_REV_ID: 155fbb02f7ae393b42c18c80353a20e09c71b8bf --- stdlib/src/builtin/builtin_list.mojo | 4 ++-- stdlib/src/builtin/range.mojo | 20 ++++++++++---------- stdlib/src/collections/dict.mojo | 6 +++--- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/python/python_object.mojo | 2 +- stdlib/src/utils/span.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 6a366739ae..9ea758583d 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -140,7 +140,7 @@ struct _VariadicListIter[type: AnyTrivialRegType]: return self.src[self.index - 1] @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: @@ -251,7 +251,7 @@ struct _VariadicListMemIter[ ) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index a7bd3264bf..ccc01436db 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -63,7 +63,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): return self.end - curr @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -97,7 +97,7 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): return start @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -137,7 +137,7 @@ struct _StridedRangeIterator(Sized): return result @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @@ -165,7 +165,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return result @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -339,7 +339,7 @@ struct _UIntZeroStartingRange(UIntSized): return self.end - curr @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -370,7 +370,7 @@ struct _UIntStridedRangeIterator(UIntSized): return result @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @@ -410,7 +410,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): return result @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -479,7 +479,7 @@ struct _ZeroStartingScalarRange[type: DType]: return self.end - curr @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -516,7 +516,7 @@ struct _SequentialScalarRange[type: DType]: return start @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline @@ -541,7 +541,7 @@ struct _StridedScalarRangeIterator[type: DType]: var step: Scalar[type] @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: # If the type is unsigned, then 'step' cannot be negative. @parameter if type.is_unsigned(): diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index c3121e9ab9..8cbd4b6357 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -107,7 +107,7 @@ struct _DictEntryIter[ return Pointer.address_of(opt_entry_ref[].value()) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: @@ -145,7 +145,7 @@ struct _DictKeyIter[ return Pointer.address_of(self.iter.__next__()[].key) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: @@ -195,7 +195,7 @@ struct _DictValueIter[ ) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 97f253fd50..0922999700 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -64,7 +64,7 @@ struct _InlineListIter[ return Pointer.address_of(self.src[][self.index]) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1587298703..b849766a9e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -72,7 +72,7 @@ struct _ListIter[ return Pointer.address_of(self.src[][self.index]) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index f2452865c3..b371c7ec05 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -46,7 +46,7 @@ struct _VecIter[ return deref(self.vec, self.i - 1) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index cc86926452..05397a1c75 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -105,7 +105,7 @@ struct _PyIter(Sized): return current @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 305b4d1381..af417ee09f 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -79,7 +79,7 @@ struct _SpanIter[ return Pointer.address_of(self.src[self.index]) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 @always_inline diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 54d72b4bd7..49fe2fab06 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -230,7 +230,7 @@ struct _StringSliceIter[ ) @always_inline - fn __hasmore__(self) -> Bool: + fn __has_next__(self) -> Bool: return self.__len__() > 0 fn __len__(self) -> Int: From 5197b9fcc9a8f3b1a868352c0ab4a4ae1a02dddb Mon Sep 17 00:00:00 2001 From: "Ahmed S. Taei" Date: Wed, 6 Nov 2024 13:45:10 -0800 Subject: [PATCH 1837/2019] [stdlib] Remove SM_90 SIMD specialization NVPTX backend already handles generating mul.rn.bf16x2. MODULAR_ORIG_COMMIT_REV_ID: f47ef03edbc18583d7332ec9b1bc8beb4bbb5075 --- stdlib/src/builtin/simd.mojo | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9678701dcd..360d812b2b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -562,14 +562,7 @@ struct SIMD[type: DType, size: Int]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() @parameter - if _is_sm_9x() and type is DType.bfloat16: - return _call_ptx_intrinsic[ - scalar_instruction="add.rn.bf16", - vector2_instruction="add.rn.bf16x2", - scalar_constraints="=h,h,h", - vector_constraints="=r,r,r", - ](self, rhs) - elif _is_sm_8x() and type.is_half_float(): + if _is_sm_8x() and type.is_half_float(): return self.fma(1, rhs) return __mlir_op.`pop.add`(self.value, rhs.value) @@ -617,13 +610,6 @@ struct SIMD[type: DType, size: Int]( return (rebind[Self._Mask](self) & rebind[Self._Mask](rhs)).cast[ type ]() - elif _is_sm_9x() and type is DType.bfloat16: - return _call_ptx_intrinsic[ - scalar_instruction="mul.rn.bf16", - vector2_instruction="mul.rn.bf16x2", - scalar_constraints="=h,h,h", - vector_constraints="=r,r,r", - ](self, rhs) elif _is_sm_8x() and type.is_half_float(): return self.fma(rhs, -0.0) From 17531f086e39295989a2f19514541302aa8c9a13 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 6 Nov 2024 19:08:18 -0800 Subject: [PATCH 1838/2019] [******][GPU] Optimize the casting for bfloat16 and flaot16 to/from f32 MODULAR_ORIG_COMMIT_REV_ID: 213d811a6655cc5d4a7c976c1179fa2bccae8fbe --- stdlib/src/builtin/simd.mojo | 108 ++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 41 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 360d812b2b..1d48359a97 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1583,54 +1583,73 @@ struct SIMD[type: DType, size: Int]( @parameter if type == target: return rebind[SIMD[target, size]](self) - elif ( - triple_is_nvidia_cuda() - and type is DType.float32 - and target is DType.bfloat16 - and size >= 2 - ): - var res = SIMD[target, size]() + + @parameter + if triple_is_nvidia_cuda(): @parameter - for i in range(0, size, 2): - var bf16x2_as_uint32 = inlined_assembly[ - "cvt.rn.bf16x2.f32 $0, $1, $2;", - UInt32, - constraints="=r,f,f", - has_side_effect=False, - ](rebind[Float32](self[i + 1]), rebind[Float32](self[i])) - res = res.insert[offset=i](bitcast[target, 2](bf16x2_as_uint32)) + if size > 1 and type is DType.float32 and target.is_half_float(): + # For size == 1, the LLVM backend generates the correct `cvt.rn.f16.f32` + # instruction. This is why we do not handle it here. + alias vector_asm_prefix = "cvt.rn.f16x2.f32" if target is DType.float16 else "cvt.rn.bf16x2.f32" + var res = SIMD[target, size]() - return res + @parameter + for i in range(0, size, 2): + var bf16x2_as_uint32 = inlined_assembly[ + vector_asm_prefix + " $0, $1, $2;", + UInt32, + constraints="=r,f,f", + has_side_effect=False, + ]( + rebind[Float32](self[i + 1]), + rebind[Float32](self[i]), + ) + res = res.insert[offset=i]( + bitcast[target, 2](bf16x2_as_uint32) + ) - elif has_neon() and ( - type is DType.bfloat16 or target == DType.bfloat16 - ): + return res + + elif type is DType.bfloat16 and target is DType.float64: + # Convert to F64 via a Float32 pathway. This would allow us to + # use the optimizations defined above. + return self.cast[DType.float32]().cast[target]() + + @parameter + if has_neon() and (type is DType.bfloat16 or target == DType.bfloat16): # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[target, size]() - elif type is DType.bool: + + @parameter + if type is DType.bool: return self.select(SIMD[target, size](1), SIMD[target, size](0)) - elif target == DType.bool: + + @parameter + if target == DType.bool: return rebind[SIMD[target, size]](self != 0) - elif type is DType.bfloat16 and not _has_native_bf16_support(): - var cast_result = _bfloat16_to_f32( - rebind[SIMD[DType.bfloat16, size]](self) - ).cast[target]() - return rebind[SIMD[target, size]](cast_result) - elif target == DType.bfloat16 and not _has_native_bf16_support(): + + @parameter + if type is DType.bfloat16 and target is DType.float32: + return rebind[SIMD[target, size]]( + _bfloat16_to_f32(rebind[SIMD[DType.bfloat16, size]](self)) + ) + + @parameter + if type is DType.float32 and target == DType.bfloat16: return rebind[SIMD[target, size]]( _f32_to_bfloat16(self.cast[DType.float32]()) ) - else: - return __mlir_op.`pop.cast`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - target.value, - `>`, - ] - ](self.value) + + return __mlir_op.`pop.cast`[ + _type = __mlir_type[ + `!pop.simd<`, + size.value, + `, `, + target.value, + `>`, + ] + ](self.value) @no_inline fn write_to[W: Writer](self, inout writer: W): @@ -2982,10 +3001,17 @@ fn _bfloat16_to_f32_scalar( # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[DType.float32, 1]() - var bfloat_bits = FPUtils[DType.bfloat16].bitcast_to_integer(val) - return FPUtils[DType.float32].bitcast_from_integer( - bfloat_bits << _fp32_bf16_mantissa_diff - ) + # For bfloat16, we can just do a memcpy to perform the cast to float32. + @parameter + if triple_is_nvidia_cuda(): + return inlined_assembly[ + "cvt.f32.bf16 $0, $1;" if _is_sm_9x() else "mov.b32 $0, {0, $1};", + Scalar[DType.float32], + constraints="=f,h", + has_side_effect=False, + ](bitcast[DType.int16](val)) + + return bitcast[DType.float32, 1](SIMD[DType.bfloat16, 2](0, val)) @always_inline From 3e0bf3d567b6a1b68a8e8d67906d8b6d74175276 Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Wed, 6 Nov 2024 21:33:55 -0600 Subject: [PATCH 1839/2019] [External] [stdlib] Add `os.path.expandvars` and respective tests (#50283) [External] [stdlib] Add `os.path.expandvars` and respective tests This PR adds the `expandvars` function to the `os.path` module. The documentation for contributing references installing `lit` into our local Python environment, but with `magic` we have a Python environment already defined, so I added it as a dependency in the `pixi.toml` file. On a similar note, I added `magic run` to pre-commit hooks so `magic` is used for the Python and Mojo environments. Co-authored-by: Mikhail Tavarez Closes modularml/mojo#3735 MODULAR_ORIG_COMMIT_REV_ID: 2d8c7e8c2b2cadf773fcb36cdc9a33d27306c488 --- docs/changelog.md | 3 + pixi.toml | 3 +- stdlib/src/os/__init__.mojo | 2 +- stdlib/src/os/env.mojo | 19 ++- stdlib/src/os/path/__init__.mojo | 1 + stdlib/src/os/path/path.mojo | 151 +++++++++++++++++- stdlib/test/os/path/test_expandvars.mojo | 104 ++++++++++++ ...{test_getenv_setenv.mojo => test_env.mojo} | 10 +- 8 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 stdlib/test/os/path/test_expandvars.mojo rename stdlib/test/os/{test_getenv_setenv.mojo => test_env.mojo} (84%) diff --git a/docs/changelog.md b/docs/changelog.md index 85f46f7ac9..beaa98db82 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -229,6 +229,9 @@ what we publish. of variables that are handled as synthetic types, e.g. `List` from Mojo or `std::vector` from C++. +- Added `os.path.expandvars` to expand environment variables in a string. + ([PR #3735](https://github.com/modularml/mojo/pull/3735) by [@thatstoasty](https://github.com/thatstoasty)). + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` diff --git a/pixi.toml b/pixi.toml index 8dddbc4900..0067a5d6c1 100644 --- a/pixi.toml +++ b/pixi.toml @@ -11,4 +11,5 @@ benchmarks = { cmd = ["./stdlib/scripts/run-benchmarks.sh"], env = { MODULAR_MOJ [dependencies] python = ">=3.9,<3.13" -max = "*" +lit = "*" +max = "*" \ No newline at end of file diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index fe813bb7d9..b55400cad3 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -13,7 +13,7 @@ """Implements the os package.""" from .atomic import Atomic -from .env import getenv, setenv +from .env import getenv, setenv, unsetenv from .fstat import lstat, stat, stat_result from .os import ( SEEK_CUR, diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index 6feb36e79b..0556389882 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -19,7 +19,8 @@ from os import setenv ``` """ -from sys import external_call, os_is_linux, os_is_macos +from sys import external_call, os_is_linux, os_is_macos, os_is_windows +from sys.ffi import c_int from memory import UnsafePointer from utils import StringRef @@ -51,6 +52,22 @@ fn setenv(name: String, value: String, overwrite: Bool = True) -> Bool: return status == 0 +fn unsetenv(name: String) -> Bool: + """Unsets an environment variable. + + Args: + name: The name of the environment variable. + + Returns: + True if unsetting the variable succeeded. Otherwise, False is returned. + """ + constrained[ + not os_is_windows(), "operating system must be Linux or macOS" + ]() + + return external_call["unsetenv", c_int](name.unsafe_ptr()) == 0 + + fn getenv(name: String, default: String = "") -> String: """Returns the value of the given environment variable. diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index ac7d4aa8ec..68097ee4db 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -15,6 +15,7 @@ from .path import ( dirname, exists, expanduser, + expandvars, getsize, isdir, isfile, diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 8cacd9af54..4bd3930a26 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -19,9 +19,10 @@ from os.path import isdir ``` """ -from collections import List +from collections import List, InlineArray from stat import S_ISDIR, S_ISLNK, S_ISREG from sys import has_neon, os_is_linux, os_is_macos, os_is_windows +from utils import Span, StringSlice from .. import PathLike from .._linux_aarch64 import _lstat as _lstat_linux_arm @@ -390,3 +391,151 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): # paths_str.append(cur_path[].__fspath__()) # return join(path.__fspath__(), *paths_str) + + +# ===----------------------------------------------------------------------=== # +# expandvars +# ===----------------------------------------------------------------------=== # + + +fn _is_shell_special_variable(byte: Byte) -> Bool: + """Checks if `$` + `byte` identifies a special shell variable, such as `$@`. + + Args: + byte: The byte to check. + + Returns: + True if the byte is a special shell variable and False otherwise. + """ + alias shell_variables = InlineArray[Int, 17]( + ord("*"), + ord("#"), + ord("$"), + ord("@"), + ord("!"), + ord("?"), + ord("-"), + ord("0"), + ord("1"), + ord("2"), + ord("3"), + ord("4"), + ord("5"), + ord("6"), + ord("7"), + ord("8"), + ord("9"), + ) + return int(byte) in shell_variables + + +fn _is_alphanumeric(byte: Byte) -> Bool: + """Checks if `byte` is an ASCII letter, number, or underscore. + + Args: + byte: The byte to check. + + Returns: + True if the byte is an ASCII letter, number, or underscore and False otherwise. + """ + var b = int(byte) + return ( + b == ord("_") + or ord("0") <= b + and b <= ord("9") + or ord("a") <= b + and b <= ord("z") + or ord("A") <= b + and b <= ord("Z") + ) + + +fn _parse_variable_name[ + immutable: ImmutableOrigin +](bytes: Span[Byte, immutable]) -> Tuple[StringSlice[immutable], Int]: + """Returns the environment variable name and the byte count required to extract it. + For `${}` expansions, two additional bytes are added to the byte count to account for the braces. + + Args: + bytes: The bytes to extract the environment variable name from. + + Returns: + The environment variable name and the byte count required to extract it. + """ + if bytes[0] == ord("{"): + if ( + len(bytes) > 2 + and _is_shell_special_variable(bytes[1]) + and bytes[2] == ord("}") + ): + return StringSlice(unsafe_from_utf8=bytes[1:2]), 3 + + # Scan until the closing brace or the end of the bytes. + var i = 1 + while i < len(bytes): + if bytes[i] == ord("}"): + return StringSlice(unsafe_from_utf8=bytes[1:i]), i + 1 + i += 1 + return StringSlice(unsafe_from_utf8=bytes[1:i]), i + 1 + elif _is_shell_special_variable(bytes[0]): + return StringSlice(unsafe_from_utf8=bytes[0:1]), 1 + + # Scan until we hit an invalid character in environment variable names. + var i = 0 + while i < len(bytes) and _is_alphanumeric(bytes[i]): + i += 1 + + return StringSlice(unsafe_from_utf8=bytes[:i]), i + + +fn expandvars[PathLike: os.PathLike, //](path: PathLike) -> String: + """Replaces `${var}` or `$var` in the path with values from the current environment variables. + Malformed variable names and references to non-existing variables are left unchanged. + + Parameters: + PathLike: The type conforming to the os.PathLike trait. + + Args: + path: The path that is being expanded. + + Returns: + The expanded path. + """ + var path_str = path.__fspath__() + var bytes = path_str.as_bytes().get_immutable() + var buf = String() + + # Byte scanning should be fine, ${} is ASCII. + i = 0 + j = 0 + while j < len(bytes): + if bytes[j] == ord("$") and j + 1 < len(bytes): + if not buf: + buf._buffer.reserve(new_capacity=2 * len(bytes)) + buf.write_bytes(bytes[i:j]) + + name, length = _parse_variable_name(bytes[j + 1 :]) + + # Invalid syntax (`${}` or `${`) or $ was not followed by a name; write as is. + if name.startswith("{") or name == "": + buf.write_bytes(bytes[j : j + length + 1]) + # Shell variable (eg `$@` or `$*`); write as is. + elif _is_shell_special_variable(name.as_bytes()[0]): + buf.write_bytes(bytes[j : j + 2]) + # Environment variable; expand it. If no value, write as is. + else: + value = os.getenv(String(name)) + if value != "": + buf.write(value) + else: + buf.write_bytes(bytes[j : j + length + 1]) + + j += length + i = j + 1 + j += 1 + + if not buf: + return path_str + + buf.write_bytes(bytes[i:]) + return buf diff --git a/stdlib/test/os/path/test_expandvars.mojo b/stdlib/test/os/path/test_expandvars.mojo new file mode 100644 index 0000000000..662558bbfb --- /dev/null +++ b/stdlib/test/os/path/test_expandvars.mojo @@ -0,0 +1,104 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +import os +from os.path import expandvars +from testing import assert_equal + + +@value +struct EnvVar: + var name: String + + fn __init__(inout self, name: String, value: String) -> None: + self.name = name + _ = os.setenv(name, value) + + fn __enter__(self) -> Self: + return self + + fn __exit__(self) -> None: + _ = os.unsetenv(self.name) + + +def test_expansion(): + with EnvVar("TEST_VAR", "World"): + assert_equal(expandvars("Hello $TEST_VAR!"), "Hello World!") + assert_equal(expandvars("漢字 $TEST_VAR🔥!"), "漢字 World🔥!") + assert_equal(expandvars("$TEST_VAR/path/to/file"), "World/path/to/file") + + with EnvVar("UNICODE_TEST_VAR", "漢字🔥"): + assert_equal(expandvars("Hello $UNICODE_TEST_VAR!"), "Hello 漢字🔥!") + assert_equal(expandvars("漢字 $UNICODE_TEST_VAR🔥!"), "漢字 漢字🔥🔥!") + assert_equal( + expandvars("$UNICODE_TEST_VAR/path/to/file"), "漢字🔥/path/to/file" + ) + + +def test_braced_expansion(): + with EnvVar("BRACE_VAR", "World"): + assert_equal(expandvars("Hello ${BRACE_VAR}!"), "Hello World!") + assert_equal(expandvars("漢字 ${BRACE_VAR}🔥!"), "漢字 World🔥!") + assert_equal( + expandvars("${BRACE_VAR}/path/to/file"), "World/path/to/file" + ) + + with EnvVar("UNICODE_BRACE_VAR", "漢字🔥"): + assert_equal(expandvars("Hello ${UNICODE_BRACE_VAR}!"), "Hello 漢字🔥!") + assert_equal(expandvars("漢字 ${UNICODE_BRACE_VAR}🔥!"), "漢字 漢字🔥🔥!") + assert_equal( + expandvars("${UNICODE_BRACE_VAR}/path/to/file"), "漢字🔥/path/to/file" + ) + + +def test_unset_expansion(): + # Unset variables should not be expanded. + assert_equal( + expandvars("Hello $NONEXISTENT_VAR!"), "Hello $NONEXISTENT_VAR!" + ) + assert_equal( + expandvars("漢字 ${NONEXISTENT_VAR}🔥!"), "漢字 ${NONEXISTENT_VAR}🔥!" + ) + + +def test_dollar_sign(): + # A lone `$` should not be expanded. + assert_equal(expandvars("A lone $ sign"), "A lone $ sign") + + # Special shell variables should not be expanded. + assert_equal( + expandvars("$@ $* $1 $2 $3 $NONEXISTENT_VAR."), + "$@ $* $1 $2 $3 $NONEXISTENT_VAR.", + ) + + +def test_short_variable(): + with EnvVar("a", "World"): + assert_equal(expandvars("$a"), "World") + assert_equal(expandvars("${a}"), "World") + + +def test_invalid_syntax(): + # Invalid syntax should be written as is. + assert_equal(expandvars("${}"), "${}") + assert_equal(expandvars("${"), "${") + + +def main(): + test_expansion() + test_braced_expansion() + test_unset_expansion() + test_dollar_sign() + test_short_variable() + test_invalid_syntax() diff --git a/stdlib/test/os/test_getenv_setenv.mojo b/stdlib/test/os/test_env.mojo similarity index 84% rename from stdlib/test/os/test_getenv_setenv.mojo rename to stdlib/test/os/test_env.mojo index 5c1ffad832..0d4bd0b6ec 100644 --- a/stdlib/test/os/test_getenv_setenv.mojo +++ b/stdlib/test/os/test_env.mojo @@ -13,7 +13,7 @@ # REQUIRES: system-linux || system-darwin # RUN: TEST_MYVAR=MyValue %mojo %s -from os import getenv, setenv +from os import getenv, setenv, unsetenv from testing import assert_equal @@ -40,6 +40,14 @@ def test_setenv(): assert_equal(setenv("=", "INVALID", True), False) +def test_unsetenv(): + assert_equal(setenv("NEW_VAR", "FOO", True), True) + assert_equal(getenv("NEW_VAR"), "FOO") + assert_equal(unsetenv("NEW_VAR"), True) + assert_equal(getenv("NEW_VAR"), "") + + def main(): test_getenv() test_setenv() + test_unsetenv() From 6b7d3783d5782843d09da72e2e8f54a5eef71613 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 6 Nov 2024 20:36:50 -0800 Subject: [PATCH 1840/2019] [******][GPU] Query the warpsize from the target attr MODULAR_ORIG_COMMIT_REV_ID: 7e076e926262e2624383338f1cf0b52097eb0d80 --- stdlib/src/sys/__init__.mojo | 1 + stdlib/src/sys/info.mojo | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 29a6aea26d..57394f0ed6 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -46,6 +46,7 @@ from .info import ( simdbitwidth, simdbytewidth, simdwidthof, + warpsize, sizeof, triple_is_nvidia_cuda, ) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index c7e168a33a..90af173dbe 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -530,13 +530,13 @@ fn is_64bit[target: __mlir_type.`!kgen.target` = _current_target()]() -> Bool: fn simdbitwidth[ target: __mlir_type.`!kgen.target` = _current_target() ]() -> IntLiteral: - """Returns the vector size (in bits) of the host system. + """Returns the vector size (in bits) of the specified target. Parameters: target: The target architecture. Returns: - The vector size (in bits) of the host system. + The vector size (in bits) of the specified target. """ return __mlir_attr[ `#kgen.param.expr IntLiteral: - """Returns the vector size (in bytes) of the host system. + """Returns the vector size (in bytes) of the specified target. Parameters: target: The target architecture. @@ -562,6 +562,26 @@ fn simdbytewidth[ return simdbitwidth[target]() // CHAR_BIT +@always_inline("nodebug") +fn warpsize[ + target: __mlir_type.`!kgen.target` = _current_target() +]() -> IntLiteral: + """Returns the warp size of the specified target. + + Parameters: + target: The target architecture. + + Returns: + The warp size of the specified target. + """ + return __mlir_attr[ + `#kgen.param.expr : !kgen.int_literal`, + ] + + @always_inline("nodebug") fn sizeof[ type: AnyType, target: __mlir_type.`!kgen.target` = _current_target() From ac242117b900f2f80ed614512a883dafe3a02bd1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 7 Nov 2024 17:52:43 -0600 Subject: [PATCH 1841/2019] [stdlib] cleanup: Remove `T: Pythonable` requirement on `PyMojoObject` This also moves `python_type_object()` to be a free function, as the `T: Pythonable` requirement is still needed there for the time being. MODULAR_ORIG_COMMIT_REV_ID: c8a527bf95a2776939ebbf12ad478b7d842b966b --- stdlib/src/builtin/_pybind.mojo | 3 +- stdlib/src/python/_bindings.mojo | 96 ++++++++++++++++---------------- stdlib/src/python/_cpython.mojo | 10 ++-- 3 files changed, 54 insertions(+), 55 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 9cf24ea083..c7e1fd3703 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -31,6 +31,7 @@ from python._bindings import ( ConvertibleFromPython, PythonableAndConvertibleFromPython, PyMojoObject, + python_type_object, py_c_function_wrapper, check_argument_type, # Imported for use by the compiler @@ -85,7 +86,7 @@ fn gen_pytype_wrapper[ # TODO(MOCO-1302): Add support for generating member field as computed properties. # TODO(MOCO-1307): Add support for constructor generation. - var type_obj = PyMojoObject[T].python_type_object[name]( + var type_obj = python_type_object[T, name]( methods=List[PyMethodDef](), ) diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 67aaf1f467..e46e3159d3 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -77,66 +77,66 @@ trait Pythonable(Defaultable, Representable): pass -struct PyMojoObject[T: Pythonable]: +struct PyMojoObject[T: AnyType]: """Storage backing a PyObject* wrapping a Mojo value.""" var ob_base: PyObject var mojo_value: T - @staticmethod - fn python_type_object[ - type_name: StringLiteral, - ](owned methods: List[PyMethodDef]) raises -> TypedPythonObject["Type"]: - """Construct a Python 'type' describing PyMojoObject[T]. - - Parameters: - type_name: The name of the Mojo type. - """ - var cpython = _get_global_python_itf().cpython() +fn python_type_object[ + T: Pythonable, + type_name: StringLiteral, +](owned methods: List[PyMethodDef]) raises -> TypedPythonObject["Type"]: + """Construct a Python 'type' describing PyMojoObject[T]. - var slots = List[PyType_Slot]( - # All wrapped Mojo types are allocated generically. - PyType_Slot.tp_new( - cpython.lib.get_function[newfunc]("PyType_GenericNew") - ), - PyType_Slot.tp_init(empty_tp_init_wrapper[T]), - PyType_Slot.tp_dealloc(tp_dealloc_wrapper[T]), - PyType_Slot.tp_repr(tp_repr_wrapper[T]), - ) + Parameters: + T: The mojo type to wrap. + type_name: The name of the Mojo type. + """ - if methods: - # FIXME: Avoid leaking the methods data pointer in this way. - slots.append(PyType_Slot.tp_methods(methods.steal_data())) - - # Zeroed item terminator - slots.append(PyType_Slot.null()) - - var type_spec = PyType_Spec { - # FIXME(MOCO-1306): This should be `T.__name__`. - name: type_name.unsafe_cstr_ptr(), - basicsize: sizeof[PyMojoObject[T]](), - itemsize: 0, - flags: Py_TPFLAGS_DEFAULT, - # Note: This pointer is only "borrowed" by PyType_FromSpec. - slots: slots.unsafe_ptr(), - } - - # Construct a Python 'type' object from our type spec. - var type_obj = cpython.PyType_FromSpec( - UnsafePointer.address_of(type_spec) - ) + var cpython = _get_global_python_itf().cpython() - if type_obj.is_null(): - Python.throw_python_exception_if_error_state(cpython) - return abort[TypedPythonObject["Type"]]( - "expected to raise after getting NULL type object" - ) + var slots = List[PyType_Slot]( + # All wrapped Mojo types are allocated generically. + PyType_Slot.tp_new( + cpython.lib.get_function[newfunc]("PyType_GenericNew") + ), + PyType_Slot.tp_init(empty_tp_init_wrapper[T]), + PyType_Slot.tp_dealloc(tp_dealloc_wrapper[T]), + PyType_Slot.tp_repr(tp_repr_wrapper[T]), + ) - return TypedPythonObject["Type"]( - unsafe_unchecked_from=PythonObject(type_obj) + if methods: + # FIXME: Avoid leaking the methods data pointer in this way. + slots.append(PyType_Slot.tp_methods(methods.steal_data())) + + # Zeroed item terminator + slots.append(PyType_Slot.null()) + + var type_spec = PyType_Spec { + # FIXME(MOCO-1306): This should be `T.__name__`. + name: type_name.unsafe_cstr_ptr(), + basicsize: sizeof[PyMojoObject[T]](), + itemsize: 0, + flags: Py_TPFLAGS_DEFAULT, + # Note: This pointer is only "borrowed" by PyType_FromSpec. + slots: slots.unsafe_ptr(), + } + + # Construct a Python 'type' object from our type spec. + var type_obj = cpython.PyType_FromSpec(UnsafePointer.address_of(type_spec)) + + if type_obj.is_null(): + Python.throw_python_exception_if_error_state(cpython) + return abort[TypedPythonObject["Type"]]( + "expected to raise after getting NULL type object" ) + return TypedPythonObject["Type"]( + unsafe_unchecked_from=PythonObject(type_obj) + ) + # Impedance match between: # diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 7fb8070ee6..b908306ddf 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -195,7 +195,7 @@ struct PyObjectPtr: # ===-------------------------------------------------------------------===# fn try_cast_to_mojo_value[ - T: Pythonable, + T: AnyType, ]( owned self, # TODO: Make this part of the trait bound @@ -218,14 +218,12 @@ struct PyObjectPtr: return None fn unchecked_cast_to_mojo_object[ - T: Pythonable + T: AnyType ](owned self) -> UnsafePointer[PyMojoObject[T]]: """Assume that this Python object contains a wrapped Mojo value.""" return self.unsized_obj_ptr.bitcast[PyMojoObject[T]]() - fn unchecked_cast_to_mojo_value[ - T: Pythonable - ](owned self) -> UnsafePointer[T]: + fn unchecked_cast_to_mojo_value[T: AnyType](owned self) -> UnsafePointer[T]: var mojo_obj_ptr = self.unchecked_cast_to_mojo_object[T]() # TODO(MSTDL-950): Should use something like `addr_of!` @@ -315,7 +313,7 @@ struct PyMethodDef: var method_name: UnsafePointer[c_char] """A pointer to the name of the method as a C string. - + Notes: called `ml_name` in CPython. """ From 45ae56fac4a6369a73fe41fa3bc93d836cf78096 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:16:57 -0600 Subject: [PATCH 1842/2019] [External] [stdlib] [NFC] Rename `StringSlice` `UnsafePointer` constructor args (#50485) [External] [stdlib] [NFC] Rename `StringSlice` `UnsafePointer` constructor args Rename `StringSlice` `UnsafePointer` constructor args. Part of https://github.com/modularml/mojo/issues/3704. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3740 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3740 MODULAR_ORIG_COMMIT_REV_ID: 66fcc1d96c46dcd4b8d1b872b4da38843b014995 --- stdlib/src/builtin/format_int.mojo | 16 ++++----- stdlib/src/builtin/string_literal.mojo | 4 +-- stdlib/src/collections/string.mojo | 18 +++++----- stdlib/src/utils/string_slice.mojo | 46 +++++++++----------------- 4 files changed, 32 insertions(+), 52 deletions(-) diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 0ea4fac700..940ac43683 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -322,13 +322,12 @@ fn _try_write_int[ # Construct a null-terminated buffer of single-byte char. var zero_buf = InlineArray[UInt8, 2](zero_char, 0) + # TODO(MSTDL-720): + # Support printing non-null-terminated strings on GPU and switch + # back to this code without a workaround. + # ptr=digit_chars_array, var zero = StringSlice[ImmutableAnyOrigin]( - # TODO(MSTDL-720): - # Support printing non-null-terminated strings on GPU and switch - # back to this code without a workaround. - # unsafe_from_utf8_ptr=digit_chars_array, - unsafe_from_utf8_ptr=zero_buf.unsafe_ptr(), - len=1, + ptr=zero_buf.unsafe_ptr(), length=1 ) writer.write(zero) @@ -404,10 +403,7 @@ fn _try_write_int[ # SAFETY: # Create a slice to only those bytes in `buf` that have been initialized. - var str_slice = StringSlice[__origin_of(buf)]( - unsafe_from_utf8_ptr=buf_ptr, - len=len, - ) + var str_slice = StringSlice[__origin_of(buf)](ptr=buf_ptr, length=len) writer.write(str_slice) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 4c9974e6a4..0a6835f4e8 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -444,9 +444,7 @@ struct StringLiteral( # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually # guaranteed to be valid. - return StaticString( - unsafe_from_utf8_ptr=self.unsafe_ptr(), len=self.byte_length() - ) + return StaticString(ptr=self.unsafe_ptr(), length=self.byte_length()) @always_inline fn as_bytes(self) -> Span[Byte, StaticConstantOrigin]: diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 3f9e28aa47..6b6d3cbf31 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1118,9 +1118,9 @@ struct String( var rhs_ptr = rhs.unsafe_ptr() alias S = StringSlice[ImmutableAnyOrigin] if lhs_len == 0: - return String(S(unsafe_from_utf8_ptr=rhs_ptr, len=rhs_len)) + return String(S(ptr=rhs_ptr, length=rhs_len)) elif rhs_len == 0: - return String(S(unsafe_from_utf8_ptr=lhs_ptr, len=lhs_len)) + return String(S(ptr=lhs_ptr, length=lhs_len)) var sum_len = lhs_len + rhs_len var buffer = Self._buffer_type(capacity=sum_len + 1) var ptr = buffer.unsafe_ptr() @@ -1211,7 +1211,7 @@ struct String( var o_ptr = other.unsafe_ptr() if s_len == 0: alias S = StringSlice[ImmutableAnyOrigin] - self = String(S(unsafe_from_utf8_ptr=o_ptr, len=o_len)) + self = String(S(ptr=o_ptr, length=o_len)) return elif o_len == 0: return @@ -2031,12 +2031,12 @@ struct String( """ if end == -1: return StringSlice[__origin_of(self)]( - unsafe_from_utf8_ptr=self.unsafe_ptr() + start, - len=self.byte_length() - start, + ptr=self.unsafe_ptr() + start, + length=self.byte_length() - start, ).startswith(prefix.as_string_slice()) return StringSlice[__origin_of(self)]( - unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ptr=self.unsafe_ptr() + start, length=end - start ).startswith(prefix.as_string_slice()) fn endswith(self, suffix: String, start: Int = 0, end: Int = -1) -> Bool: @@ -2053,12 +2053,12 @@ struct String( """ if end == -1: return StringSlice[__origin_of(self)]( - unsafe_from_utf8_ptr=self.unsafe_ptr() + start, - len=self.byte_length() - start, + ptr=self.unsafe_ptr() + start, + length=self.byte_length() - start, ).endswith(suffix.as_string_slice()) return StringSlice[__origin_of(self)]( - unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ptr=self.unsafe_ptr() + start, length=end - start ).endswith(suffix.as_string_slice()) fn removeprefix(self, prefix: String, /) -> String: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 49fe2fab06..c5c02967c2 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -211,8 +211,7 @@ struct _StringSliceIter[ self.continuation_bytes -= byte_len - 1 self.index += byte_len return StringSlice[origin]( - unsafe_from_utf8_ptr=self.ptr + (self.index - byte_len), - len=byte_len, + ptr=self.ptr + (self.index - byte_len), length=byte_len ) else: var byte_len = 1 @@ -226,7 +225,7 @@ struct _StringSliceIter[ self.continuation_bytes -= byte_len - 1 self.index -= byte_len return StringSlice[origin]( - unsafe_from_utf8_ptr=self.ptr + self.index, len=byte_len + ptr=self.ptr + self.index, length=byte_len ) @always_inline @@ -324,32 +323,21 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( self = Self(unsafe_from_utf8=byte_slice) @always_inline - fn __init__( - inout self, - *, - unsafe_from_utf8_ptr: UnsafePointer[UInt8], - len: Int, - ): + fn __init__(inout self, *, ptr: UnsafePointer[Byte], length: Int): """Construct a `StringSlice` from a pointer to a sequence of UTF-8 encoded bytes and a length. Args: - unsafe_from_utf8_ptr: A pointer to a sequence of bytes encoded in - UTF-8. - len: The number of bytes of encoded data. + ptr: A pointer to a sequence of bytes encoded in UTF-8. + length: The number of bytes of encoded data. Safety: - - `unsafe_from_utf8_ptr` MUST point to at least `len` bytes of valid - UTF-8 encoded data. - - `unsafe_from_utf8_ptr` must point to data that is live for the - duration of `origin`. + - `ptr` MUST point to at least `length` bytes of valid UTF-8 encoded + data. + - `ptr` must point to data that is live for the duration of + `origin`. """ - var byte_slice = Span[Byte, origin]( - unsafe_ptr=unsafe_from_utf8_ptr, - len=len, - ) - - self._slice = byte_slice + self._slice = Span[Byte, origin](unsafe_ptr=ptr, len=length) @always_inline fn __init__(inout self, *, other: Self): @@ -635,9 +623,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( start += 1 while end > start and _isspace(ptr[end - 1]): end -= 1 - return StringSlice[origin]( - unsafe_from_utf8_ptr=ptr + start, len=end - start - ) + return StringSlice[origin](ptr=ptr + start, length=end - start) @always_inline fn as_bytes(self) -> Span[Byte, origin]: @@ -685,7 +671,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( if end == -1: return self.find(prefix, start) == start return StringSlice[__origin_of(self)]( - unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ptr=self.unsafe_ptr() + start, length=end - start ).startswith(prefix) fn endswith( @@ -707,7 +693,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( if end == -1: return self.rfind(suffix, start) + len(suffix) == len(self) return StringSlice[__origin_of(self)]( - unsafe_from_utf8_ptr=self.unsafe_ptr() + start, len=end - start + ptr=self.unsafe_ptr() + start, length=end - start ).endswith(suffix) fn _from_start(self, start: Int) -> Self: @@ -1001,7 +987,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( eol_start += char_len str_len = eol_start - offset + int(keepends) * eol_length - s = StringSlice[O](unsafe_from_utf8_ptr=ptr + offset, len=str_len) + s = StringSlice[O](ptr=ptr + offset, length=str_len) output.append(s^) offset = eol_start + eol_length @@ -1208,7 +1194,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): @always_inline("nodebug") fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: - return S(unsafe_from_utf8_ptr=p + start, len=end - start) + return S(ptr=p + start, length=end - start) var auto_arg_index = 0 for e in entries: @@ -1327,7 +1313,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): @always_inline("nodebug") fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: - return S(unsafe_from_utf8_ptr=p + start, len=end - start) + return S(ptr=p + start, length=end - start) var field = _build_slice(fmt_src.unsafe_ptr(), start_value + 1, i) var field_ptr = field.unsafe_ptr() From 1bf9284f301ee4bef8a12498be1c4f69dc867977 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:00:28 -0600 Subject: [PATCH 1843/2019] [External] [stdlib] Add `StringLiteral` dynamic `__mul__` (#50564) [External] [stdlib] Add `StringLiteral` dynamic `__mul__` Add `StringLiteral` dynamic `__mul__` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3749 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3749 MODULAR_ORIG_COMMIT_REV_ID: 15001dfcbefd3fb2f3d0901207e7e1dfd2176e11 --- stdlib/src/builtin/string_literal.mojo | 24 +++++++++++++------- stdlib/src/collections/string.mojo | 14 +----------- stdlib/src/utils/string_slice.mojo | 20 ++++++++++++++++ stdlib/test/builtin/test_string_literal.mojo | 15 +++++++++--- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 0a6835f4e8..9e95ed7b90 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -138,7 +138,7 @@ struct StringLiteral( self = self + rhs @always_inline("nodebug") - fn __mul__(self, n: Int) -> StringLiteral: + fn __mul__(self, n: IntLiteral) -> StringLiteral: """Concatenates the string literal `n` times. Can only be evaluated at compile time using the `alias` keyword, which will write the result into The binary. @@ -149,22 +149,30 @@ struct StringLiteral( Returns: The string concatenated `n` times. - Example: + Examples: ```mojo - alias original = "mojo" - alias concat = original * 3 - print(concat) + alias concat = "mojo" * 3 + print(concat) # mojomojomojo ``` - - `concat` now points to the StringLiteral "mojomojomojo", which is - written into the binary. + . """ var concat = "" for _ in range(n): concat += self return concat + fn __mul__(self, n: Int) -> String: + """Concatenates the string `n` times. + + Args: + n : The number of times to concatenate the string. + + Returns: + The string concatenated `n` times. + """ + return self.as_string_slice() * n + @always_inline("nodebug") fn __eq__(self, rhs: StringLiteral) -> Bool: """Compare two string literals for equality. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 6b6d3cbf31..b4e7cbdf0b 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -2136,19 +2136,7 @@ struct String( Returns: The string concatenated `n` times. """ - if n <= 0: - return "" - var len_self = self.byte_length() - var count = len_self * n + 1 - var buf = Self._buffer_type(capacity=count) - buf.resize(count, 0) - for i in range(n): - memcpy( - dest=buf.data + len_self * i, - src=self.unsafe_ptr(), - count=len_self, - ) - return String(buf^) + return self.as_string_slice() * n @always_inline fn format[*Ts: _CurlyEntryFormattable](self, *args: *Ts) raises -> String: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index c5c02967c2..bab95a8547 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -595,6 +595,26 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """ return _atof(self) + fn __mul__(self, n: Int) -> String: + """Concatenates the string `n` times. + + Args: + n : The number of times to concatenate the string. + + Returns: + The string concatenated `n` times. + """ + + var len_self = self.byte_length() + var count = len_self * n + 1 + var buf = String._buffer_type(capacity=count) + buf.size = count + var b_ptr = buf.unsafe_ptr() + for i in range(n): + memcpy(b_ptr + len_self * i, self.unsafe_ptr(), len_self) + b_ptr[count - 1] = 0 + return String(buf^) + # ===------------------------------------------------------------------===# # Methods # ===------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 77a7b802d9..f8fb2825e5 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -45,9 +45,18 @@ def test_iadd(): def test_mul(): - alias original = "mojo" - alias concat = original * 3 - assert_equal("mojomojomojo", concat) + alias `3`: Int = 3 + alias `u3`: UInt = 3 + alias static_concat_0 = "mojo" * 3 + alias static_concat_1 = "mojo" * `3` + alias static_concat_2 = "mojo" * `u3` + assert_equal(static_concat_0, static_concat_1) + assert_equal(static_concat_1, static_concat_2) + assert_equal("mojomojomojo", static_concat_0) + assert_equal(static_concat_0, String("mojo") * 3) + var dynamic_concat = "mojo" * 3 + assert_equal("mojomojomojo", dynamic_concat) + assert_equal(static_concat_0, dynamic_concat) def test_equality(): From 87bf22d28554762e041cb4f490571fdfca71d7f2 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 8 Nov 2024 13:50:31 -0800 Subject: [PATCH 1844/2019] [stdlib] Format floats using dragonbox algorithm Uses the dragonbox algorithm from C++ std::format to separate a float into significand and exponent with roundtrip guarantee, and shortest representation. The float formatting itself now matches Python logic where < 0.0001 or positive exponent > 15 will use scientific form. - [Paper](https://github.com/jk-jeon/dragonbox/blob/master/other_files/Dragonbox.pdf) - [Reference Implementation](https://github.com/jk-jeon/dragonbox) This is required for synching GPU printing of floats across threads. It also greatly improves the representation of floats, for example: - Float64(5.6) used to be represented as 5.5999999999999996 even though this is the equivalent of 5.6 when represented in binary. - Float32(5.6) used to be represented as 5.5999999046325684 due to upcasting to Float64, but is now correctly represented as 5.6 when printed or converted to a `String`. MODULAR_ORIG_COMMIT_REV_ID: 969350a8b13bdeee0aa89757aa896ed21e021cfc --- stdlib/src/builtin/_format_float.mojo | 1428 ++++++++++++++++++++ stdlib/src/builtin/dtype.mojo | 32 + stdlib/src/builtin/io.mojo | 108 -- stdlib/src/builtin/simd.mojo | 162 +-- stdlib/src/math/math.mojo | 2 +- stdlib/src/utils/numerics.mojo | 75 +- stdlib/src/utils/write.mojo | 6 +- stdlib/test/builtin/test_format_float.mojo | 207 +++ stdlib/test/builtin/test_print.mojo | 8 +- stdlib/test/builtin/test_simd.mojo | 8 +- stdlib/test/collections/test_string.mojo | 12 +- 11 files changed, 1804 insertions(+), 244 deletions(-) create mode 100644 stdlib/src/builtin/_format_float.mojo create mode 100644 stdlib/test/builtin/test_format_float.mojo diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo new file mode 100644 index 0000000000..31a48829d5 --- /dev/null +++ b/stdlib/src/builtin/_format_float.mojo @@ -0,0 +1,1428 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# This file is derived from the dragonbox reference implementation +# (https://github.com/jk-jeon/dragonbox). Dragonbox contains the following +# attribution notice: +# +# Copyright 2020-2024 Junekey Jeon +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, this software +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. +# ===----------------------------------------------------------------------=== # +from collections import InlineArray +from math import log2 +from memory import bitcast +from sys.info import sizeof +from utils import StaticTuple, Span +from builtin.io import _printf +from utils.numerics import FPUtils, isinf, isnan + + +@value +@register_passable("trivial") +struct _UInt128: + var high: UInt64 + var low: UInt64 + + fn __iadd__(inout self, n: UInt64): + var sum = (self.low + n) & UInt64.MAX + self.high += 1 if sum < self.low else 0 + self.low = sum + + +@value +@register_passable("trivial") +struct _MulParity: + var parity: Bool + var is_integer: Bool + + fn __init__(inout self, parity: Bool, is_integer: Bool): + self.parity = parity + self.is_integer = is_integer + + +@value +@register_passable("trivial") +struct _MulResult[CarrierDType: DType]: + var integer_part: Scalar[CarrierDType] + var is_integer: Bool + + +@value +struct FP[type: DType, CarrierDType: DType = FPUtils[type].uint_type]: + alias CarrierType = Scalar[Self.CarrierDType] + alias total_bits = sizeof[type]() * 8 + alias carrier_bits = sizeof[Self.CarrierDType]() * 8 + alias sig_bits = FPUtils[type].mantissa_width() + alias exp_bits = FPUtils[type].exponent_width() + alias min_exponent = FPUtils[type].min_exponent() + alias max_exponent = FPUtils[type].max_exponent() + alias exp_bias = -Self.max_exponent + alias cache_bits = 64 if Self.CarrierDType == DType.uint32 else 128 + alias min_k = -31 if Self.CarrierDType == DType.uint32 else -292 + alias max_k = 46 if Self.CarrierDType == DType.uint32 else 326 + alias divide_magic_number = StaticTuple[UInt32, 2](6554, 656) + alias n_max = ( + (Scalar[Self.CarrierDType](2) << Self.sig_bits) + 1 + ) // 3 + 1 * 20 + alias n_max_larger = ( + Scalar[Self.CarrierDType](2) << Self.sig_bits + ) * Self.big_divisor - 1 + alias kappa = _floor_log10_pow2(Self.carrier_bits - Self.sig_bits - 2) - 1 + alias big_divisor = pow(10, Self.kappa + 1) + alias small_divisor = pow(10, Self.kappa) + + +fn _write_float[ + W: Writer, type: DType, // +](inout writer: W, value: Scalar[type]): + """Write a SIMD float type into a Writer, using the dragonbox algorithm for + perfect roundtrip, shortest representable format, and high performance. + Paper: https://github.com/jk-jeon/dragonbox/blob/master/other_files/Dragonbox.pdf + Reference Implementation: https://github.com/jk-jeon/dragonbox. + + Parameters: + W: The type of the Writer. + type: The type of the float being passed in. + + Args: + writer: The Writer to write the float to. + value: The float to write into the Writer. + """ + constrained[type.is_floating_point()]() + + # Currently only specialized for float32 and float64, upcast anything else + # to float32 + casted = value.cast[ + DType.float64 if type == DType.float64 else DType.float32 + ]() + + # Bitcast the float and separate the sig and exp, to enable manipulating + # bits as a UInt64 and Int: + # - The significand (sig) is the raw binary fraction + # - The exponent (exp) is still in biased form + var sig = FPUtils.get_mantissa_uint(casted) + var exp = FPUtils.get_exponent_without_bias(casted) + var sign = FPUtils.get_sign(casted) + + if isinf(value): + if sign: + writer.write("-") + writer.write("inf") + return + + if isnan(value): + writer.write("nan") + return + + if sign: + writer.write("-") + + if not sig and not exp: + writer.write("0.0") + return + + # Convert the binary components to a decimal representation: + # - The raw binary sig into a decimal sig + # - The biased binary exp into a decimal power of 10 exp + # This does all the heavy lifting for perfect roundtrip, shortest + # representable format, bankers rounding etc. + _to_decimal[casted.type](sig, exp) + + # This is a custom routine for writing the decimal following python behavior. + # it can be further optimized with a lookup table, there is overhead here + # compared to snprintf. + var orig_sig = sig + var abs_exp = abs(exp) + var digits = StaticTuple[Byte, 21]() + var idx = 0 + while sig > 0: + digits[idx] = (sig % 10).cast[DType.uint8]() + sig //= 10 + idx += 1 + if sig > 0: + exp += 1 + var leading_zeroes = abs_exp - idx + + # Write in scientific notation if < 0.0001 or exp > 15 + if (exp < 0 and leading_zeroes > 3) or exp > 15: + # Handle single digit case + if orig_sig < 10: + writer.write(orig_sig) + else: + # Write digit before decimal point + writer.write(digits[idx - 1]) + writer.write(".") + # Write digits after decimal point + for i in reversed(range(idx - 1)): + writer.write(digits[i]) + # Write exponent + if exp < 0: + writer.write("e-") + exp = -exp + else: + writer.write("e+") + # Pad exponent with a 0 if less than two digits + if exp < 10: + writer.write("0") + var exp_digits = StaticTuple[Byte, 10]() + var exp_idx = 0 + while exp > 0: + exp_digits[exp_idx] = exp % 10 + exp //= 10 + exp_idx += 1 + for i in reversed(range(exp_idx)): + writer.write(exp_digits[i]) + # If between 0 and 0.0001 + elif exp < 0 and leading_zeroes > 0: + writer.write("0.") + for _ in range(leading_zeroes): + writer.write("0") + for i in reversed(range(idx)): + writer.write(digits[i]) + # All other floats > 0.0001 with an exponent <= 15 + else: + var point_written = False + for i in reversed(range(idx)): + if leading_zeroes < 1 and exp == idx - i - 2: + # No integer part so write leading 0 + if i == idx - 1: + writer.write("0") + writer.write(".") + point_written = True + writer.write(digits[i]) + + # If exp - idx + 1 > 0 it's a positive number with more 0's than the sig + for _ in range(exp - idx + 1): + writer.write("0") + if not point_written: + writer.write(".0") + + +fn _to_decimal[ + CarrierDType: DType, //, type: DType +](inout sig: Scalar[CarrierDType], inout exp: Int): + """Transform the raw binary significand to decimal significand, + and biased binary exponent into a decimal power of 10 exponent. + """ + var two_fc = sig * 2 + var binary_exp = exp + + # For normal numbers + if binary_exp != 0: + binary_exp += FP[type].exp_bias - FP[type].sig_bits + if two_fc == 0: + var minus_k = (binary_exp * 631305 - 261663) >> 21 + var beta = binary_exp + _floor_log2_pow10(-minus_k) + var cache_index = -minus_k - FP[type].min_k + + var xi = _compute_endpoint[ + CarrierDType, + FP[type].sig_bits, + FP[type].total_bits, + FP[type].cache_bits, + ](cache_index, beta, left_endpoint=True) + + var zi = _compute_endpoint[ + CarrierDType, + FP[type].sig_bits, + FP[type].total_bits, + FP[type].cache_bits, + ](cache_index, beta, left_endpoint=False) + + # If we don't accept the left endpoint or if the left endpoint is + # not an integer, increase it. + if not _is_left_endpoint_integer_shorter_interval[ + CarrierDType, FP[type].sig_bits + ](binary_exp): + xi += 1 + + # Try bigger divisor. + # zi is at most floor((f_c + 1/2) * 2^e * 10^k0). + # Substituting f_c = 2^p and k0 = -floor(log10(3 * 2^(e-2))), we get + # zi <= floor((2^(p+1) + 1) * 20/3) <= ceil((2^(p+1) + 1)/3) * 20. + sig = _divide_by_pow10[1, FP[type, CarrierDType].n_max](zi) + + # On success, remove trailing zeros and return. + if sig * 10 >= xi: + exp = minus_k + 1 + return _remove_trailing_zeros(sig, exp) + + # Otherwise, compute the round-up of y. + sig = _compute_round_up_for_shorter_interval_case[ + CarrierDType, + FP[type].total_bits, + FP[type].sig_bits, + FP[type].cache_bits, + ](cache_index, beta) + + # When tie occurs + if sig < xi: + sig += 1 + + # No trailing zeroes, so set exp and return + exp = minus_k + return + + # Normal interval case + two_fc |= Scalar[CarrierDType](1) << (FP[type].sig_bits + 1) + else: + # For subnormal numbers + binary_exp = FP[type].min_exponent - FP[type].sig_bits + + ########################################## + # Step 1: Schubfach multiplier calculation + ########################################## + var minus_k = _floor_log10_pow2(binary_exp) - FP[type].kappa + var beta = binary_exp + _floor_log2_pow10(-minus_k) + var cache_index = -minus_k - FP[type].min_k + var deltai = _compute_delta[ + CarrierDType, FP[type].total_bits, FP[type].cache_bits + ](cache_index, beta) + var z_result = _compute_mul[CarrierDType]( + Scalar[CarrierDType]((two_fc | 1) << beta), cache_index + ) + + ################################################################ + # Step 2: Try larger divisor, remove trailing zeros if necessary + ################################################################ + sig = _divide_by_pow10[ + FP[type, CarrierDType].kappa + 1, FP[type, CarrierDType].n_max_larger + ](z_result.integer_part) + var r = (z_result.integer_part - FP[type].big_divisor * sig) + + while True: + if r < deltai: + # Exclude the right endpoint if necessary + if ( + r + | Scalar[CarrierDType](not z_result.is_integer) + | Scalar[CarrierDType](1) + ) == 0: + sig -= 1 + r = FP[type].big_divisor + break + elif r > deltai: + break + else: + # r == deltai, compare fractional parts + var x_result = _compute_mul_parity( + (two_fc - 1).cast[DType.uint64](), cache_index, beta + ) + if not (x_result.parity | x_result.is_integer): + break + # If no break conditions were met + exp = minus_k + FP[type].kappa + 1 + return _remove_trailing_zeros(sig, exp) + + ####################################################### + # Step 3: Find the significand with the smaller divisor + ####################################################### + sig *= 10 + + # delta is equal to 10^(kappa + elog10(2) - floor(elog10(2))), so dist cannot + # be larger than r. + var dist = r - (deltai // 2) + (FP[type].small_divisor // 2) + var approx_y_parity = ((dist ^ (FP[type].small_divisor // 2)) & 1) != 0 + + # Is dist divisible by 10^kappa + var divisible_by_small_divisor = _check_divisibility_and_divide_by_pow10[ + FP[type].carrier_bits, FP[type].divide_magic_number + ](dist, FP[type].kappa) + + # Add dist / 10^kappa to the significand. + sig += dist + + if divisible_by_small_divisor: + # Check z^(f) >= epsilon^(f). + # We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + # where yi == zi - epsilon if and only if z^(f) >= epsilon^(f). + # Since there are only 2 possibilities, we only need to care about the + # parity. Also, zi and r should have the same parity since the divisor + # is an even number. + var y_result = _compute_mul_parity( + two_fc.cast[DType.uint64](), cache_index, beta + ) + if y_result.parity != approx_y_parity: + sig -= 1 + + # No trailing zeroes on this branch, so set exp as the final step + exp = minus_k + FP[type].kappa + + +fn _compute_endpoint[ + CarrierDType: DType, sig_bits: Int, total_bits: Int, cache_bits: Int +](cache_index: Int, beta: Int, left_endpoint: Bool) -> Scalar[CarrierDType]: + @parameter + if CarrierDType == DType.uint64: + var cache = cache_f64[cache_index] + if left_endpoint: + return ( + (cache.high - (cache.high >> (sig_bits + 2))) + >> (total_bits - sig_bits - 1 - beta) + ).cast[CarrierDType]() + else: + return ( + (cache.high + (cache.high >> (sig_bits + 1))) + >> (total_bits - sig_bits - 1 - beta) + ).cast[CarrierDType]() + else: + var cache = cache_f32[cache_index] + if left_endpoint: + return ( + (cache - (cache >> (sig_bits + 2))) + >> (cache_bits - sig_bits - 1 - beta) + ).cast[CarrierDType]() + else: + return ( + (cache + (cache >> (sig_bits + 1))) + >> (cache_bits - sig_bits - 1 - beta) + ).cast[CarrierDType]() + + +fn _print_bits[type: DType](x: Scalar[type]) -> String: + alias total_bits = sizeof[type]() * 8 + var output = String() + + @parameter + if not type.is_floating_point(): + for i in reversed(range(total_bits)): + output.write((x >> i) & 1) + if i % 8 == 0: + output.write(" ") + else: + alias sig_bits = 23 if type == DType.float32 else 52 + alias exp_bits = 8 if type == DType.float32 else 11 + alias cast_type = DType.uint32 if type == DType.float32 else DType.uint64 + var casted = bitcast[cast_type](x) + for i in reversed(range(total_bits)): + output.write((casted >> i) & 1) + if i == total_bits - 1: + output.write(" ") + if i == sig_bits: + if total_bits == 64: + output.write(" ") + else: + output.write(" ") + if i < sig_bits: + if ( + total_bits == 32 + and (total_bits - sig_bits - 2 - i) % 8 == 0 + ): + output.write(" ") + if total_bits == 64 and (total_bits - sig_bits - i) % 8 == 0: + output.write(" ") + return output + + +fn _rotr[ + CarrierDType: DType +](n: Scalar[CarrierDType], r: Scalar[CarrierDType]) -> Scalar[CarrierDType]: + @parameter + if CarrierDType == DType.uint32: + var r_masked = r & 31 + return (n >> r_masked) | (n << ((32 - r_masked) & 31)) + else: + var r_masked = r & 63 + return (n >> r_masked) | (n << ((64 - r_masked) & 63)) + + +fn _floor_log2(n: UInt64) -> Int: + var count = -1 + var num = n + while num != 0: + count += 1 + num >>= 1 + return count + + +fn _floor_log10_pow2(e: Int) -> Int: + return (e * 315653) >> 20 + + +fn _floor_log2_pow10(e: Int) -> Int: + return (e * 1741647) >> 19 + + +fn _umul64(x: UInt32, y: UInt32) -> UInt64: + return x.cast[DType.uint64]() * y.cast[DType.uint64]() + + +fn _umul128[ + CarrierDType: DType +](x: Scalar[CarrierDType], y: UInt64) -> _UInt128: + var a = (x >> 32).cast[DType.uint32]() + var b = x.cast[DType.uint32]() + var c = (y >> 32).cast[DType.uint32]() + var d = y.cast[DType.uint32]() + + var ac = _umul64(a, c) + var bc = _umul64(b, c) + var ad = _umul64(a, d) + var bd = _umul64(b, d) + + var intermediate = (bd >> 32) + _truncate[DType.uint32](ad) + _truncate[ + DType.uint32 + ](bc) + + return _UInt128( + ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + _truncate[DType.uint32](bd), + ) + + +fn _remove_trailing_zeros[ + CarrierDType: DType +](inout sig: Scalar[CarrierDType], inout exp: Int): + """Fastest alg for removing trailing zeroes: + https://github.com/jk-jeon/rtz_benchmark. + """ + + @parameter + if CarrierDType == DType.uint64: + var r = _rotr(sig * 28999941890838049, 8) + var b = r < 184467440738 + var s = int(b) + if b: + sig = r + + r = _rotr(sig * 182622766329724561, 4) + b = r < 1844674407370956 + s = s * 2 + int(b) + if b: + sig = r + + r = _rotr(sig * 10330176681277348905, 2) + b = r < 184467440737095517 + s = s * 2 + int(b) + if b: + sig = r + + r = _rotr(sig * 14757395258967641293, 1) + b = r < 1844674407370955162 + s = s * 2 + int(b) + if b: + sig = r + + exp += s + else: + var r = _rotr(sig * 184254097, 4) + var b = r < 429497 + var s = int(b) + if b: + sig = r + + r = _rotr(sig * 42949673, 2) + b = r < 42949673 + s = s * 2 + int(b) + if b: + sig = r + + r = _rotr(sig * 1288490189, 1) + b = r < 429496730 + s = s * 2 + int(b) + if b: + sig = r + + exp += s + + +fn _divide_by_pow10[ + CarrierDType: DType, //, N: Int, n_max: Scalar[CarrierDType] +](n: Scalar[CarrierDType]) -> Scalar[CarrierDType]: + @parameter + if CarrierDType == DType.uint64: + + @parameter + if N == 1 and bool(n_max <= 4611686018427387908): + return _umul128_upper64(n, 1844674407370955162) + elif N == 3 and bool(n_max <= 15534100272597517998): + return _umul128_upper64(n, 4722366482869645214) >> 8 + else: + return n / pow(10, N) + else: + + @parameter + if N == 1 and bool(n_max <= 1073741828): + return (_umul64(n.cast[DType.uint32](), 429496730) >> 32).cast[ + CarrierDType + ]() + elif N == 2: + return (_umul64(n.cast[DType.uint32](), 1374389535) >> 37).cast[ + CarrierDType + ]() + else: + return n / pow(10, N) + + +fn _umul192_lower128(x: UInt64, y: _UInt128) -> _UInt128: + """Get lower 128-bits of multiplication of a 64-bit unsigned integer and a + 128-bit unsigned integer. + """ + var high = x * y.high + var high_low = _umul128(x, y.low) + return _UInt128((high + high_low.high) & UInt64.MAX, high_low.low) + + +fn _compute_mul_parity[ + CarrierDType: DType +](two_f: Scalar[CarrierDType], cache_index: Int, beta: Int) -> _MulParity: + if CarrierDType == DType.uint64: + debug_assert(1 <= beta < 64, "beta must be between 1 and 64") + var r = _umul192_lower128( + two_f.cast[DType.uint64](), cache_f64[cache_index] + ) + return _MulParity( + ((r.high >> (64 - beta)) & 1) != 0, + ( + ((r.high << beta) & UInt64(0xFFFFFFFFFFFFFFFF)) + | (r.low >> (64 - beta)) + ) + == 0, + ) + else: + debug_assert( + 1 <= beta < 32, + "beta for float types 32bits must be between 1 and 32", + ) + var r = _umul96_lower64( + two_f.cast[DType.uint32](), cache_f32[cache_index] + ) + return _MulParity( + ((r >> (64 - beta)) & 1) != 0, + (UInt32(0xFFFFFFFF).cast[DType.uint64]() & (r >> (32 - beta))) == 0, + ) + + +fn _umul96_lower64(x: UInt32, y: UInt64) -> UInt64: + return (x.cast[DType.uint64]() * y) & UInt64(0xFFFFFFFFFFFFFFFF) + + +fn _check_divisibility_and_divide_by_pow10[ + CarrierDType: DType, //, + carrier_bits: Int, + divide_magic_number: StaticTuple[UInt32, 2], +](inout n: Scalar[CarrierDType], N: Int) -> Bool: + # Make sure the computation for max_n does not overflow. + debug_assert(N + 1 <= _floor_log10_pow2(carrier_bits)) + + var magic_number = divide_magic_number[N - 1] + var prod = (n * magic_number.cast[CarrierDType]()).cast[DType.uint32]() + + var mask = UInt32((UInt32(1) << 16) - 1) + var result = ((prod & mask) < magic_number) + + n = (prod >> 16).cast[CarrierDType]() + return result + + +fn _truncate[ + D: DType, S: Int, //, TruncateType: DType +](u: SIMD[D, S]) -> SIMD[D, S]: + """Cast to DType to truncate to the width of that type, then cast back to + original DType. + """ + return u.cast[TruncateType]().cast[D]() + + +fn _umul96_upper64[ + CarrierDType: DType +](x: Scalar[CarrierDType], y: UInt64) -> UInt64: + var yh = (y >> 32).cast[DType.uint32]() + var yl = y.cast[DType.uint32]() + + var xyh = _umul64(x.cast[DType.uint32](), yh) + var xyl = _umul64(x.cast[DType.uint32](), yl) + return xyh + (xyl >> 32) + + +fn _compute_mul[ + CarrierDType: DType +](u: Scalar[CarrierDType], cache_index: Int) -> _MulResult[CarrierDType]: + if CarrierDType == DType.uint64: + var r = _umul192_upper128(u, cache_f64[cache_index]) + return _MulResult[CarrierDType](r.high.cast[CarrierDType](), r.low == 0) + else: + var cache_value = cache_f32[cache_index] + var r = _umul96_upper64(u, cache_value) + return _MulResult[CarrierDType]( + (r >> 32).cast[CarrierDType](), r.cast[CarrierDType]() == 0 + ) + + +fn _compute_delta[ + CarrierDType: DType, total_bits: Int, cache_bits: Int +](cache_index: Int, beta: Int) -> Scalar[CarrierDType]: + if CarrierDType == DType.uint64: + var cache = cache_f64[cache_index] + return (cache.high >> (total_bits - 1 - beta)).cast[CarrierDType]() + else: + var cache = cache_f32[cache_index] + return (cache >> (cache_bits - 1 - beta)).cast[CarrierDType]() + + +fn _umul192_upper128[ + CarrierDType: DType +](x: Scalar[CarrierDType], y: _UInt128) -> _UInt128: + var r = _umul128(x, y.high) + r += _umul128_upper64(x, y.low).cast[DType.uint64]() + return r + + +fn _umul128_upper64[ + CarrierDType: DType +](x: Scalar[CarrierDType], y: UInt64) -> Scalar[CarrierDType]: + var a = (x >> 32).cast[DType.uint32]() + var b = x.cast[DType.uint32]() + var c = (y >> 32).cast[DType.uint32]() + var d = y.cast[DType.uint32]() + + var ac = _umul64(a, c) + var bc = _umul64(b, c) + var ad = _umul64(a, d) + var bd = _umul64(b, d) + + var intermediate = (bd >> 32) + _truncate[DType.uint32](ad) + _truncate[ + DType.uint32 + ](bc) + return (ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32)).cast[ + CarrierDType + ]() + + +fn _is_finite[exp_bits: Int](exponent: Int) -> Bool: + return exponent != (1 << exp_bits) - 1 + + +fn _count_factors[ + CarrierDType: DType +](owned n: Scalar[CarrierDType], a: Int) -> Int: + debug_assert(a > 1) + var c = 0 + while n % a == 0: + n /= a + c += 1 + return c + + +fn _compute_round_up_for_shorter_interval_case[ + CarrierDType: DType, total_bits: Int, sig_bits: Int, cache_bits: Int +](cache_index: Int, beta: Int) -> Scalar[CarrierDType]: + if CarrierDType == DType.uint64: + var cache = cache_f64[cache_index] + return ( + ( + (cache.high >> (total_bits - sig_bits - 2 - beta)).cast[ + CarrierDType + ]() + ) + + 1 + ) / 2 + else: + var cache = cache_f32[cache_index] + return ( + (cache >> (cache_bits - sig_bits - 2 - beta)).cast[CarrierDType]() + + 1 + ) / 2 + + +fn _case_shorter_interval_left_endpoint_upper_threshold[ + CarrierDType: DType, sig_bits: Int +]() -> Int: + var k = _count_factors( + (Scalar[CarrierDType](1) << (sig_bits + 2)) - 1, 5 + ) + 1 + return 2 + _floor_log2(pow(10, k)) // 3 + + +fn _is_left_endpoint_integer_shorter_interval[ + CarrierDType: DType, sig_bits: Int +](binary_exp: Int) -> Bool: + return ( + binary_exp >= 2 + and binary_exp + <= _case_shorter_interval_left_endpoint_upper_threshold[ + CarrierDType, sig_bits + ]() + ) + + +# fmt: off +alias cache_f32 = StaticTuple[UInt64, 78]( + 0x81CEB32C4B43FCF5, 0xA2425FF75E14FC32, + 0xCAD2F7F5359A3B3F, 0xFD87B5F28300CA0E, + 0x9E74D1B791E07E49, 0xC612062576589DDB, + 0xF79687AED3EEC552, 0x9ABE14CD44753B53, + 0xC16D9A0095928A28, 0xF1C90080BAF72CB2, + 0x971DA05074DA7BEF, 0xBCE5086492111AEB, + 0xEC1E4A7DB69561A6, 0x9392EE8E921D5D08, + 0xB877AA3236A4B44A, 0xE69594BEC44DE15C, + 0x901D7CF73AB0ACDA, 0xB424DC35095CD810, + 0xE12E13424BB40E14, 0x8CBCCC096F5088CC, + 0xAFEBFF0BCB24AAFF, 0xDBE6FECEBDEDD5BF, + 0x89705F4136B4A598, 0xABCC77118461CEFD, + 0xD6BF94D5E57A42BD, 0x8637BD05AF6C69B6, + 0xA7C5AC471B478424, 0xD1B71758E219652C, + 0x83126E978D4FDF3C, 0xA3D70A3D70A3D70B, + 0xCCCCCCCCCCCCCCCD, 0x8000000000000000, + 0xA000000000000000, 0xC800000000000000, + 0xFA00000000000000, 0x9C40000000000000, + 0xC350000000000000, 0xF424000000000000, + 0x9896800000000000, 0xBEBC200000000000, + 0xEE6B280000000000, 0x9502F90000000000, + 0xBA43B74000000000, 0xE8D4A51000000000, + 0x9184E72A00000000, 0xB5E620F480000000, + 0xE35FA931A0000000, 0x8E1BC9BF04000000, + 0xB1A2BC2EC5000000, 0xDE0B6B3A76400000, + 0x8AC7230489E80000, 0xAD78EBC5AC620000, + 0xD8D726B7177A8000, 0x878678326EAC9000, + 0xA968163F0A57B400, 0xD3C21BCECCEDA100, + 0x84595161401484A0, 0xA56FA5B99019A5C8, + 0xCECB8F27F4200F3A, 0x813F3978F8940985, + 0xA18F07D736B90BE6, 0xC9F2C9CD04674EDF, + 0xFC6F7C4045812297, 0x9DC5ADA82B70B59E, + 0xC5371912364CE306, 0xF684DF56C3E01BC7, + 0x9A130B963A6C115D, 0xC097CE7BC90715B4, + 0xF0BDC21ABB48DB21, 0x96769950B50D88F5, + 0xBC143FA4E250EB32, 0xEB194F8E1AE525FE, + 0x92EFD1B8D0CF37BF, 0xB7ABC627050305AE, + 0xE596B7B0C643C71A, 0x8F7E32CE7BEA5C70, + 0xB35DBF821AE4F38C, 0xE0352F62A19E306F, +) +# fmt: on + +alias cache_f64 = StaticTuple[_UInt128, 619]( + _UInt128(0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B), + _UInt128(0x9FAACF3DF73609B1, 0x77B191618C54E9AD), + _UInt128(0xC795830D75038C1D, 0xD59DF5B9EF6A2418), + _UInt128(0xF97AE3D0D2446F25, 0x4B0573286B44AD1E), + _UInt128(0x9BECCE62836AC577, 0x4EE367F9430AEC33), + _UInt128(0xC2E801FB244576D5, 0x229C41F793CDA740), + _UInt128(0xF3A20279ED56D48A, 0x6B43527578C11110), + _UInt128(0x9845418C345644D6, 0x830A13896B78AAAA), + _UInt128(0xBE5691EF416BD60C, 0x23CC986BC656D554), + _UInt128(0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9), + _UInt128(0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA), + _UInt128(0xB9E08A83A5E34F07, 0xDAF5CCD93FB0CC54), + _UInt128(0xE858AD248F5C22C9, 0xD1B3400F8F9CFF69), + _UInt128(0x91376C36D99995BE, 0x23100809B9C21FA2), + _UInt128(0xB58547448FFFFB2D, 0xABD40A0C2832A78B), + _UInt128(0xE2E69915B3FFF9F9, 0x16C90C8F323F516D), + _UInt128(0x8DD01FAD907FFC3B, 0xAE3DA7D97F6792E4), + _UInt128(0xB1442798F49FFB4A, 0x99CD11CFDF41779D), + _UInt128(0xDD95317F31C7FA1D, 0x40405643D711D584), + _UInt128(0x8A7D3EEF7F1CFC52, 0x482835EA666B2573), + _UInt128(0xAD1C8EAB5EE43B66, 0xDA3243650005EED0), + _UInt128(0xD863B256369D4A40, 0x90BED43E40076A83), + _UInt128(0x873E4F75E2224E68, 0x5A7744A6E804A292), + _UInt128(0xA90DE3535AAAE202, 0x711515D0A205CB37), + _UInt128(0xD3515C2831559A83, 0x0D5A5B44CA873E04), + _UInt128(0x8412D9991ED58091, 0xE858790AFE9486C3), + _UInt128(0xA5178FFF668AE0B6, 0x626E974DBE39A873), + _UInt128(0xCE5D73FF402D98E3, 0xFB0A3D212DC81290), + _UInt128(0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A), + _UInt128(0xA139029F6A239F72, 0x1C1FFFC1EBC44E81), + _UInt128(0xC987434744AC874E, 0xA327FFB266B56221), + _UInt128(0xFBE9141915D7A922, 0x4BF1FF9F0062BAA9), + _UInt128(0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4AA), + _UInt128(0xC4CE17B399107C22, 0xCB550FB4384D21D4), + _UInt128(0xF6019DA07F549B2B, 0x7E2A53A146606A49), + _UInt128(0x99C102844F94E0FB, 0x2EDA7444CBFC426E), + _UInt128(0xC0314325637A1939, 0xFA911155FEFB5309), + _UInt128(0xF03D93EEBC589F88, 0x793555AB7EBA27CB), + _UInt128(0x96267C7535B763B5, 0x4BC1558B2F3458DF), + _UInt128(0xBBB01B9283253CA2, 0x9EB1AAEDFB016F17), + _UInt128(0xEA9C227723EE8BCB, 0x465E15A979C1CADD), + _UInt128(0x92A1958A7675175F, 0x0BFACD89EC191ECA), + _UInt128(0xB749FAED14125D36, 0xCEF980EC671F667C), + _UInt128(0xE51C79A85916F484, 0x82B7E12780E7401B), + _UInt128(0x8F31CC0937AE58D2, 0xD1B2ECB8B0908811), + _UInt128(0xB2FE3F0B8599EF07, 0x861FA7E6DCB4AA16), + _UInt128(0xDFBDCECE67006AC9, 0x67A791E093E1D49B), + _UInt128(0x8BD6A141006042BD, 0xE0C8BB2C5C6D24E1), + _UInt128(0xAECC49914078536D, 0x58FAE9F773886E19), + _UInt128(0xDA7F5BF590966848, 0xAF39A475506A899F), + _UInt128(0x888F99797A5E012D, 0x6D8406C952429604), + _UInt128(0xAAB37FD7D8F58178, 0xC8E5087BA6D33B84), + _UInt128(0xD5605FCDCF32E1D6, 0xFB1E4A9A90880A65), + _UInt128(0x855C3BE0A17FCD26, 0x5CF2EEA09A550680), + _UInt128(0xA6B34AD8C9DFC06F, 0xF42FAA48C0EA481F), + _UInt128(0xD0601D8EFC57B08B, 0xF13B94DAF124DA27), + _UInt128(0x823C12795DB6CE57, 0x76C53D08D6B70859), + _UInt128(0xA2CB1717B52481ED, 0x54768C4B0C64CA6F), + _UInt128(0xCB7DDCDDA26DA268, 0xA9942F5DCF7DFD0A), + _UInt128(0xFE5D54150B090B02, 0xD3F93B35435D7C4D), + _UInt128(0x9EFA548D26E5A6E1, 0xC47BC5014A1A6DB0), + _UInt128(0xC6B8E9B0709F109A, 0x359AB6419CA1091C), + _UInt128(0xF867241C8CC6D4C0, 0xC30163D203C94B63), + _UInt128(0x9B407691D7FC44F8, 0x79E0DE63425DCF1E), + _UInt128(0xC21094364DFB5636, 0x985915FC12F542E5), + _UInt128(0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E), + _UInt128(0x979CF3CA6CEC5B5A, 0xA705992CEECF9C43), + _UInt128(0xBD8430BD08277231, 0x50C6FF782A838354), + _UInt128(0xECE53CEC4A314EBD, 0xA4F8BF5635246429), + _UInt128(0x940F4613AE5ED136, 0x871B7795E136BE9A), + _UInt128(0xB913179899F68584, 0x28E2557B59846E40), + _UInt128(0xE757DD7EC07426E5, 0x331AEADA2FE589D0), + _UInt128(0x9096EA6F3848984F, 0x3FF0D2C85DEF7622), + _UInt128(0xB4BCA50B065ABE63, 0x0FED077A756B53AA), + _UInt128(0xE1EBCE4DC7F16DFB, 0xD3E8495912C62895), + _UInt128(0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D), + _UInt128(0xB080392CC4349DEC, 0xBD8D794D96AACFB4), + _UInt128(0xDCA04777F541C567, 0xECF0D7A0FC5583A1), + _UInt128(0x89E42CAAF9491B60, 0xF41686C49DB57245), + _UInt128(0xAC5D37D5B79B6239, 0x311C2875C522CED6), + _UInt128(0xD77485CB25823AC7, 0x7D633293366B828C), + _UInt128(0x86A8D39EF77164BC, 0xAE5DFF9C02033198), + _UInt128(0xA8530886B54DBDEB, 0xD9F57F830283FDFD), + _UInt128(0xD267CAA862A12D66, 0xD072DF63C324FD7C), + _UInt128(0x8380DEA93DA4BC60, 0x4247CB9E59F71E6E), + _UInt128(0xA46116538D0DEB78, 0x52D9BE85F074E609), + _UInt128(0xCD795BE870516656, 0x67902E276C921F8C), + _UInt128(0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7), + _UInt128(0xA086CFCD97BF97F3, 0x80E8A40ECCD228A5), + _UInt128(0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE), + _UInt128(0xFAD2A4B13D1B5D6C, 0x796B805720085F82), + _UInt128(0x9CC3A6EEC6311A63, 0xCBE3303674053BB1), + _UInt128(0xC3F490AA77BD60FC, 0xBEDBFC4411068A9D), + _UInt128(0xF4F1B4D515ACB93B, 0xEE92FB5515482D45), + _UInt128(0x991711052D8BF3C5, 0x751BDD152D4D1C4B), + _UInt128(0xBF5CD54678EEF0B6, 0xD262D45A78A0635E), + _UInt128(0xEF340A98172AACE4, 0x86FB897116C87C35), + _UInt128(0x9580869F0E7AAC0E, 0xD45D35E6AE3D4DA1), + _UInt128(0xBAE0A846D2195712, 0x8974836059CCA10A), + _UInt128(0xE998D258869FACD7, 0x2BD1A438703FC94C), + _UInt128(0x91FF83775423CC06, 0x7B6306A34627DDD0), + _UInt128(0xB67F6455292CBF08, 0x1A3BC84C17B1D543), + _UInt128(0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94), + _UInt128(0x8E938662882AF53E, 0x547EB47B7282EE9D), + _UInt128(0xB23867FB2A35B28D, 0xE99E619A4F23AA44), + _UInt128(0xDEC681F9F4C31F31, 0x6405FA00E2EC94D5), + _UInt128(0x8B3C113C38F9F37E, 0xDE83BC408DD3DD05), + _UInt128(0xAE0B158B4738705E, 0x9624AB50B148D446), + _UInt128(0xD98DDAEE19068C76, 0x3BADD624DD9B0958), + _UInt128(0x87F8A8D4CFA417C9, 0xE54CA5D70A80E5D7), + _UInt128(0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4D), + _UInt128(0xD47487CC8470652B, 0x7647C32000696720), + _UInt128(0x84C8D4DFD2C63F3B, 0x29ECD9F40041E074), + _UInt128(0xA5FB0A17C777CF09, 0xF468107100525891), + _UInt128(0xCF79CC9DB955C2CC, 0x7182148D4066EEB5), + _UInt128(0x81AC1FE293D599BF, 0xC6F14CD848405531), + _UInt128(0xA21727DB38CB002F, 0xB8ADA00E5A506A7D), + _UInt128(0xCA9CF1D206FDC03B, 0xA6D90811F0E4851D), + _UInt128(0xFD442E4688BD304A, 0x908F4A166D1DA664), + _UInt128(0x9E4A9CEC15763E2E, 0x9A598E4E043287FF), + _UInt128(0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE), + _UInt128(0xF7549530E188C128, 0xD12BEE59E68EF47D), + _UInt128(0x9A94DD3E8CF578B9, 0x82BB74F8301958CF), + _UInt128(0xC13A148E3032D6E7, 0xE36A52363C1FAF02), + _UInt128(0xF18899B1BC3F8CA1, 0xDC44E6C3CB279AC2), + _UInt128(0x96F5600F15A7B7E5, 0x29AB103A5EF8C0BA), + _UInt128(0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8), + _UInt128(0xEBDF661791D60F56, 0x111B495B3464AD22), + _UInt128(0x936B9FCEBB25C995, 0xCAB10DD900BEEC35), + _UInt128(0xB84687C269EF3BFB, 0x3D5D514F40EEA743), + _UInt128(0xE65829B3046B0AFA, 0x0CB4A5A3112A5113), + _UInt128(0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC), + _UInt128(0xB3F4E093DB73A093, 0x59ED216765690F57), + _UInt128(0xE0F218B8D25088B8, 0x306869C13EC3532D), + _UInt128(0x8C974F7383725573, 0x1E414218C73A13FC), + _UInt128(0xAFBD2350644EEACF, 0xE5D1929EF90898FB), + _UInt128(0xDBAC6C247D62A583, 0xDF45F746B74ABF3A), + _UInt128(0x894BC396CE5DA772, 0x6B8BBA8C328EB784), + _UInt128(0xAB9EB47C81F5114F, 0x066EA92F3F326565), + _UInt128(0xD686619BA27255A2, 0xC80A537B0EFEFEBE), + _UInt128(0x8613FD0145877585, 0xBD06742CE95F5F37), + _UInt128(0xA798FC4196E952E7, 0x2C48113823B73705), + _UInt128(0xD17F3B51FCA3A7A0, 0xF75A15862CA504C6), + _UInt128(0x82EF85133DE648C4, 0x9A984D73DBE722FC), + _UInt128(0xA3AB66580D5FDAF5, 0xC13E60D0D2E0EBBB), + _UInt128(0xCC963FEE10B7D1B3, 0x318DF905079926A9), + _UInt128(0xFFBBCFE994E5C61F, 0xFDF17746497F7053), + _UInt128(0x9FD561F1FD0F9BD3, 0xFEB6EA8BEDEFA634), + _UInt128(0xC7CABA6E7C5382C8, 0xFE64A52EE96B8FC1), + _UInt128(0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1), + _UInt128(0x9C1661A651213E2D, 0x06BEA10CA65C084F), + _UInt128(0xC31BFA0FE5698DB8, 0x486E494FCFF30A63), + _UInt128(0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB), + _UInt128(0x986DDB5C6B3A76B7, 0xF89629465A75E01D), + _UInt128(0xBE89523386091465, 0xF6BBB397F1135824), + _UInt128(0xEE2BA6C0678B597F, 0x746AA07DED582E2D), + _UInt128(0x94DB483840B717EF, 0xA8C2A44EB4571CDD), + _UInt128(0xBA121A4650E4DDEB, 0x92F34D62616CE414), + _UInt128(0xE896A0D7E51E1566, 0x77B020BAF9C81D18), + _UInt128(0x915E2486EF32CD60, 0x0ACE1474DC1D122F), + _UInt128(0xB5B5ADA8AAFF80B8, 0x0D819992132456BB), + _UInt128(0xE3231912D5BF60E6, 0x10E1FFF697ED6C6A), + _UInt128(0x8DF5EFABC5979C8F, 0xCA8D3FFA1EF463C2), + _UInt128(0xB1736B96B6FD83B3, 0xBD308FF8A6B17CB3), + _UInt128(0xDDD0467C64BCE4A0, 0xAC7CB3F6D05DDBDF), + _UInt128(0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96C), + _UInt128(0xAD4AB7112EB3929D, 0x86C16C98D2C953C7), + _UInt128(0xD89D64D57A607744, 0xE871C7BF077BA8B8), + _UInt128(0x87625F056C7C4A8B, 0x11471CD764AD4973), + _UInt128(0xA93AF6C6C79B5D2D, 0xD598E40D3DD89BD0), + _UInt128(0xD389B47879823479, 0x4AFF1D108D4EC2C4), + _UInt128(0x843610CB4BF160CB, 0xCEDF722A585139BB), + _UInt128(0xA54394FE1EEDB8FE, 0xC2974EB4EE658829), + _UInt128(0xCE947A3DA6A9273E, 0x733D226229FEEA33), + _UInt128(0x811CCC668829B887, 0x0806357D5A3F5260), + _UInt128(0xA163FF802A3426A8, 0xCA07C2DCB0CF26F8), + _UInt128(0xC9BCFF6034C13052, 0xFC89B393DD02F0B6), + _UInt128(0xFC2C3F3841F17C67, 0xBBAC2078D443ACE3), + _UInt128(0x9D9BA7832936EDC0, 0xD54B944B84AA4C0E), + _UInt128(0xC5029163F384A931, 0x0A9E795E65D4DF12), + _UInt128(0xF64335BCF065D37D, 0x4D4617B5FF4A16D6), + _UInt128(0x99EA0196163FA42E, 0x504BCED1BF8E4E46), + _UInt128(0xC06481FB9BCF8D39, 0xE45EC2862F71E1D7), + _UInt128(0xF07DA27A82C37088, 0x5D767327BB4E5A4D), + _UInt128(0x964E858C91BA2655, 0x3A6A07F8D510F870), + _UInt128(0xBBE226EFB628AFEA, 0x890489F70A55368C), + _UInt128(0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F), + _UInt128(0x92C8AE6B464FC96F, 0x3B0B8BC90012929E), + _UInt128(0xB77ADA0617E3BBCB, 0x09CE6EBB40173745), + _UInt128(0xE55990879DDCAABD, 0xCC420A6A101D0516), + _UInt128(0x8F57FA54C2A9EAB6, 0x9FA946824A12232E), + _UInt128(0xB32DF8E9F3546564, 0x47939822DC96ABFA), + _UInt128(0xDFF9772470297EBD, 0x59787E2B93BC56F8), + _UInt128(0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B), + _UInt128(0xAEFAE51477A06B03, 0xEDE622920B6B23F2), + _UInt128(0xDAB99E59958885C4, 0xE95FAB368E45ECEE), + _UInt128(0x88B402F7FD75539B, 0x11DBCB0218EBB415), + _UInt128(0xAAE103B5FCD2A881, 0xD652BDC29F26A11A), + _UInt128(0xD59944A37C0752A2, 0x4BE76D3346F04960), + _UInt128(0x857FCAE62D8493A5, 0x6F70A4400C562DDC), + _UInt128(0xA6DFBD9FB8E5B88E, 0xCB4CCD500F6BB953), + _UInt128(0xD097AD07A71F26B2, 0x7E2000A41346A7A8), + _UInt128(0x825ECC24C873782F, 0x8ED400668C0C28C9), + _UInt128(0xA2F67F2DFA90563B, 0x728900802F0F32FB), + _UInt128(0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA), + _UInt128(0xFEA126B7D78186BC, 0xE2F610C84987BFA9), + _UInt128(0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7CA), + _UInt128(0xC6EDE63FA05D3143, 0x91503D1C79720DBC), + _UInt128(0xF8A95FCF88747D94, 0x75A44C6397CE912B), + _UInt128(0x9B69DBE1B548CE7C, 0xC986AFBE3EE11ABB), + _UInt128(0xC24452DA229B021B, 0xFBE85BADCE996169), + _UInt128(0xF2D56790AB41C2A2, 0xFAE27299423FB9C4), + _UInt128(0x97C560BA6B0919A5, 0xDCCD879FC967D41B), + _UInt128(0xBDB6B8E905CB600F, 0x5400E987BBC1C921), + _UInt128(0xED246723473E3813, 0x290123E9AAB23B69), + _UInt128(0x9436C0760C86E30B, 0xF9A0B6720AAF6522), + _UInt128(0xB94470938FA89BCE, 0xF808E40E8D5B3E6A), + _UInt128(0xE7958CB87392C2C2, 0xB60B1D1230B20E05), + _UInt128(0x90BD77F3483BB9B9, 0xB1C6F22B5E6F48C3), + _UInt128(0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF4), + _UInt128(0xE2280B6C20DD5232, 0x25C6DA63C38DE1B1), + _UInt128(0x8D590723948A535F, 0x579C487E5A38AD0F), + _UInt128(0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852), + _UInt128(0xDCDB1B2798182244, 0xF8E431456CF88E66), + _UInt128(0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900), + _UInt128(0xAC8B2D36EED2DAC5, 0xE272467E3D222F40), + _UInt128(0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10), + _UInt128(0x86CCBB52EA94BAEA, 0x98E947129FC2B4EA), + _UInt128(0xA87FEA27A539E9A5, 0x3F2398D747B36225), + _UInt128(0xD29FE4B18E88640E, 0x8EEC7F0D19A03AAE), + _UInt128(0x83A3EEEEF9153E89, 0x1953CF68300424AD), + _UInt128(0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD8), + _UInt128(0xCDB02555653131B6, 0x3792F412CB06794E), + _UInt128(0x808E17555F3EBF11, 0xE2BBD88BBEE40BD1), + _UInt128(0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC5), + _UInt128(0xC8DE047564D20A8B, 0xF245825A5A445276), + _UInt128(0xFB158592BE068D2E, 0xEED6E2F0F0D56713), + _UInt128(0x9CED737BB6C4183D, 0x55464DD69685606C), + _UInt128(0xC428D05AA4751E4C, 0xAA97E14C3C26B887), + _UInt128(0xF53304714D9265DF, 0xD53DD99F4B3066A9), + _UInt128(0x993FE2C6D07B7FAB, 0xE546A8038EFE402A), + _UInt128(0xBF8FDB78849A5F96, 0xDE98520472BDD034), + _UInt128(0xEF73D256A5C0F77C, 0x963E66858F6D4441), + _UInt128(0x95A8637627989AAD, 0xDDE7001379A44AA9), + _UInt128(0xBB127C53B17EC159, 0x5560C018580D5D53), + _UInt128(0xE9D71B689DDE71AF, 0xAAB8F01E6E10B4A7), + _UInt128(0x9226712162AB070D, 0xCAB3961304CA70E9), + _UInt128(0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D23), + _UInt128(0xE45C10C42A2B3B05, 0x8CB89A7DB77C506B), + _UInt128(0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243), + _UInt128(0xB267ED1940F1C61C, 0x55F038B237591ED4), + _UInt128(0xDF01E85F912E37A3, 0x6B6C46DEC52F6689), + _UInt128(0x8B61313BBABCE2C6, 0x2323AC4B3B3DA016), + _UInt128(0xAE397D8AA96C1B77, 0xABEC975E0A0D081B), + _UInt128(0xD9C7DCED53C72255, 0x96E7BD358C904A22), + _UInt128(0x881CEA14545C7575, 0x7E50D64177DA2E55), + _UInt128(0xAA242499697392D2, 0xDDE50BD1D5D0B9EA), + _UInt128(0xD4AD2DBFC3D07787, 0x955E4EC64B44E865), + _UInt128(0x84EC3C97DA624AB4, 0xBD5AF13BEF0B113F), + _UInt128(0xA6274BBDD0FADD61, 0xECB1AD8AEACDD58F), + _UInt128(0xCFB11EAD453994BA, 0x67DE18EDA5814AF3), + _UInt128(0x81CEB32C4B43FCF4, 0x80EACF948770CED8), + _UInt128(0xA2425FF75E14FC31, 0xA1258379A94D028E), + _UInt128(0xCAD2F7F5359A3B3E, 0x096EE45813A04331), + _UInt128(0xFD87B5F28300CA0D, 0x8BCA9D6E188853FD), + _UInt128(0x9E74D1B791E07E48, 0x775EA264CF55347E), + _UInt128(0xC612062576589DDA, 0x95364AFE032A819E), + _UInt128(0xF79687AED3EEC551, 0x3A83DDBD83F52205), + _UInt128(0x9ABE14CD44753B52, 0xC4926A9672793543), + _UInt128(0xC16D9A0095928A27, 0x75B7053C0F178294), + _UInt128(0xF1C90080BAF72CB1, 0x5324C68B12DD6339), + _UInt128(0x971DA05074DA7BEE, 0xD3F6FC16EBCA5E04), + _UInt128(0xBCE5086492111AEA, 0x88F4BB1CA6BCF585), + _UInt128(0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E6), + _UInt128(0x9392EE8E921D5D07, 0x3AFF322E62439FD0), + _UInt128(0xB877AA3236A4B449, 0x09BEFEB9FAD487C3), + _UInt128(0xE69594BEC44DE15B, 0x4C2EBE687989A9B4), + _UInt128(0x901D7CF73AB0ACD9, 0x0F9D37014BF60A11), + _UInt128(0xB424DC35095CD80F, 0x538484C19EF38C95), + _UInt128(0xE12E13424BB40E13, 0x2865A5F206B06FBA), + _UInt128(0x8CBCCC096F5088CB, 0xF93F87B7442E45D4), + _UInt128(0xAFEBFF0BCB24AAFE, 0xF78F69A51539D749), + _UInt128(0xDBE6FECEBDEDD5BE, 0xB573440E5A884D1C), + _UInt128(0x89705F4136B4A597, 0x31680A88F8953031), + _UInt128(0xABCC77118461CEFC, 0xFDC20D2B36BA7C3E), + _UInt128(0xD6BF94D5E57A42BC, 0x3D32907604691B4D), + _UInt128(0x8637BD05AF6C69B5, 0xA63F9A49C2C1B110), + _UInt128(0xA7C5AC471B478423, 0x0FCF80DC33721D54), + _UInt128(0xD1B71758E219652B, 0xD3C36113404EA4A9), + _UInt128(0x83126E978D4FDF3B, 0x645A1CAC083126EA), + _UInt128(0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4), + _UInt128(0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCD), + _UInt128(0x8000000000000000, 0x0000000000000000), + _UInt128(0xA000000000000000, 0x0000000000000000), + _UInt128(0xC800000000000000, 0x0000000000000000), + _UInt128(0xFA00000000000000, 0x0000000000000000), + _UInt128(0x9C40000000000000, 0x0000000000000000), + _UInt128(0xC350000000000000, 0x0000000000000000), + _UInt128(0xF424000000000000, 0x0000000000000000), + _UInt128(0x9896800000000000, 0x0000000000000000), + _UInt128(0xBEBC200000000000, 0x0000000000000000), + _UInt128(0xEE6B280000000000, 0x0000000000000000), + _UInt128(0x9502F90000000000, 0x0000000000000000), + _UInt128(0xBA43B74000000000, 0x0000000000000000), + _UInt128(0xE8D4A51000000000, 0x0000000000000000), + _UInt128(0x9184E72A00000000, 0x0000000000000000), + _UInt128(0xB5E620F480000000, 0x0000000000000000), + _UInt128(0xE35FA931A0000000, 0x0000000000000000), + _UInt128(0x8E1BC9BF04000000, 0x0000000000000000), + _UInt128(0xB1A2BC2EC5000000, 0x0000000000000000), + _UInt128(0xDE0B6B3A76400000, 0x0000000000000000), + _UInt128(0x8AC7230489E80000, 0x0000000000000000), + _UInt128(0xAD78EBC5AC620000, 0x0000000000000000), + _UInt128(0xD8D726B7177A8000, 0x0000000000000000), + _UInt128(0x878678326EAC9000, 0x0000000000000000), + _UInt128(0xA968163F0A57B400, 0x0000000000000000), + _UInt128(0xD3C21BCECCEDA100, 0x0000000000000000), + _UInt128(0x84595161401484A0, 0x0000000000000000), + _UInt128(0xA56FA5B99019A5C8, 0x0000000000000000), + _UInt128(0xCECB8F27F4200F3A, 0x0000000000000000), + _UInt128(0x813F3978F8940984, 0x4000000000000000), + _UInt128(0xA18F07D736B90BE5, 0x5000000000000000), + _UInt128(0xC9F2C9CD04674EDE, 0xA400000000000000), + _UInt128(0xFC6F7C4045812296, 0x4D00000000000000), + _UInt128(0x9DC5ADA82B70B59D, 0xF020000000000000), + _UInt128(0xC5371912364CE305, 0x6C28000000000000), + _UInt128(0xF684DF56C3E01BC6, 0xC732000000000000), + _UInt128(0x9A130B963A6C115C, 0x3C7F400000000000), + _UInt128(0xC097CE7BC90715B3, 0x4B9F100000000000), + _UInt128(0xF0BDC21ABB48DB20, 0x1E86D40000000000), + _UInt128(0x96769950B50D88F4, 0x1314448000000000), + _UInt128(0xBC143FA4E250EB31, 0x17D955A000000000), + _UInt128(0xEB194F8E1AE525FD, 0x5DCFAB0800000000), + _UInt128(0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000), + _UInt128(0xB7ABC627050305AD, 0xF14A3D9E40000000), + _UInt128(0xE596B7B0C643C719, 0x6D9CCD05D0000000), + _UInt128(0x8F7E32CE7BEA5C6F, 0xE4820023A2000000), + _UInt128(0xB35DBF821AE4F38B, 0xDDA2802C8A800000), + _UInt128(0xE0352F62A19E306E, 0xD50B2037AD200000), + _UInt128(0x8C213D9DA502DE45, 0x4526F422CC340000), + _UInt128(0xAF298D050E4395D6, 0x9670B12B7F410000), + _UInt128(0xDAF3F04651D47B4C, 0x3C0CDD765F114000), + _UInt128(0x88D8762BF324CD0F, 0xA5880A69FB6AC800), + _UInt128(0xAB0E93B6EFEE0053, 0x8EEA0D047A457A00), + _UInt128(0xD5D238A4ABE98068, 0x72A4904598D6D880), + _UInt128(0x85A36366EB71F041, 0x47A6DA2B7F864750), + _UInt128(0xA70C3C40A64E6C51, 0x999090B65F67D924), + _UInt128(0xD0CF4B50CFE20765, 0xFFF4B4E3F741CF6D), + _UInt128(0x82818F1281ED449F, 0xBFF8F10E7A8921A5), + _UInt128(0xA321F2D7226895C7, 0xAFF72D52192B6A0E), + _UInt128(0xCBEA6F8CEB02BB39, 0x9BF4F8A69F764491), + _UInt128(0xFEE50B7025C36A08, 0x02F236D04753D5B5), + _UInt128(0x9F4F2726179A2245, 0x01D762422C946591), + _UInt128(0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF6), + _UInt128(0xF8EBAD2B84E0D58B, 0xD2E0898765A7DEB3), + _UInt128(0x9B934C3B330C8577, 0x63CC55F49F88EB30), + _UInt128(0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FC), + _UInt128(0xF316271C7FC3908A, 0x8BEF464E3945EF7B), + _UInt128(0x97EDD871CFDA3A56, 0x97758BF0E3CBB5AD), + _UInt128(0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA318), + _UInt128(0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDE), + _UInt128(0x945E455F24FB1CF8, 0x8FE8CAA93E74EF6B), + _UInt128(0xB975D6B6EE39E436, 0xB3E2FD538E122B45), + _UInt128(0xE7D34C64A9C85D44, 0x60DBBCA87196B617), + _UInt128(0x90E40FBEEA1D3A4A, 0xBC8955E946FE31CE), + _UInt128(0xB51D13AEA4A488DD, 0x6BABAB6398BDBE42), + _UInt128(0xE264589A4DCDAB14, 0xC696963C7EED2DD2), + _UInt128(0x8D7EB76070A08AEC, 0xFC1E1DE5CF543CA3), + _UInt128(0xB0DE65388CC8ADA8, 0x3B25A55F43294BCC), + _UInt128(0xDD15FE86AFFAD912, 0x49EF0EB713F39EBF), + _UInt128(0x8A2DBF142DFCC7AB, 0x6E3569326C784338), + _UInt128(0xACB92ED9397BF996, 0x49C2C37F07965405), + _UInt128(0xD7E77A8F87DAF7FB, 0xDC33745EC97BE907), + _UInt128(0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A4), + _UInt128(0xA8ACD7C0222311BC, 0xC40832EA0D68CE0D), + _UInt128(0xD2D80DB02AABD62B, 0xF50A3FA490C30191), + _UInt128(0x83C7088E1AAB65DB, 0x792667C6DA79E0FB), + _UInt128(0xA4B8CAB1A1563F52, 0x577001B891185939), + _UInt128(0xCDE6FD5E09ABCF26, 0xED4C0226B55E6F87), + _UInt128(0x80B05E5AC60B6178, 0x544F8158315B05B5), + _UInt128(0xA0DC75F1778E39D6, 0x696361AE3DB1C722), + _UInt128(0xC913936DD571C84C, 0x03BC3A19CD1E38EA), + _UInt128(0xFB5878494ACE3A5F, 0x04AB48A04065C724), + _UInt128(0x9D174B2DCEC0E47B, 0x62EB0D64283F9C77), + _UInt128(0xC45D1DF942711D9A, 0x3BA5D0BD324F8395), + _UInt128(0xF5746577930D6500, 0xCA8F44EC7EE3647A), + _UInt128(0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECC), + _UInt128(0xBFC2EF456AE276E8, 0x9E3FEDD8C321A67F), + _UInt128(0xEFB3AB16C59B14A2, 0xC5CFE94EF3EA101F), + _UInt128(0x95D04AEE3B80ECE5, 0xBBA1F1D158724A13), + _UInt128(0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC98), + _UInt128(0xEA1575143CF97226, 0xF52D09D71A3293BE), + _UInt128(0x924D692CA61BE758, 0x593C2626705F9C57), + _UInt128(0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836D), + _UInt128(0xE498F455C38B997A, 0x0B6DFB9C0F956448), + _UInt128(0x8EDF98B59A373FEC, 0x4724BD4189BD5EAD), + _UInt128(0xB2977EE300C50FE7, 0x58EDEC91EC2CB658), + _UInt128(0xDF3D5E9BC0F653E1, 0x2F2967B66737E3EE), + _UInt128(0x8B865B215899F46C, 0xBD79E0D20082EE75), + _UInt128(0xAE67F1E9AEC07187, 0xECD8590680A3AA12), + _UInt128(0xDA01EE641A708DE9, 0xE80E6F4820CC9496), + _UInt128(0x884134FE908658B2, 0x3109058D147FDCDE), + _UInt128(0xAA51823E34A7EEDE, 0xBD4B46F0599FD416), + _UInt128(0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91B), + _UInt128(0x850FADC09923329E, 0x03E2CF6BC604DDB1), + _UInt128(0xA6539930BF6BFF45, 0x84DB8346B786151D), + _UInt128(0xCFE87F7CEF46FF16, 0xE612641865679A64), + _UInt128(0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07F), + _UInt128(0xA26DA3999AEF7749, 0xE3BE5E330F38F09E), + _UInt128(0xCB090C8001AB551C, 0x5CADF5BFD3072CC6), + _UInt128(0xFDCB4FA002162A63, 0x73D9732FC7C8F7F7), + _UInt128(0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFB), + _UInt128(0xC646D63501A1511D, 0xB281E1FD541501B9), + _UInt128(0xF7D88BC24209A565, 0x1F225A7CA91A4227), + _UInt128(0x9AE757596946075F, 0x3375788DE9B06959), + _UInt128(0xC1A12D2FC3978937, 0x0052D6B1641C83AF), + _UInt128(0xF209787BB47D6B84, 0xC0678C5DBD23A49B), + _UInt128(0x9745EB4D50CE6332, 0xF840B7BA963646E1), + _UInt128(0xBD176620A501FBFF, 0xB650E5A93BC3D899), + _UInt128(0xEC5D3FA8CE427AFF, 0xA3E51F138AB4CEBF), + _UInt128(0x93BA47C980E98CDF, 0xC66F336C36B10138), + _UInt128(0xB8A8D9BBE123F017, 0xB80B0047445D4185), + _UInt128(0xE6D3102AD96CEC1D, 0xA60DC059157491E6), + _UInt128(0x9043EA1AC7E41392, 0x87C89837AD68DB30), + _UInt128(0xB454E4A179DD1877, 0x29BABE4598C311FC), + _UInt128(0xE16A1DC9D8545E94, 0xF4296DD6FEF3D67B), + _UInt128(0x8CE2529E2734BB1D, 0x1899E4A65F58660D), + _UInt128(0xB01AE745B101E9E4, 0x5EC05DCFF72E7F90), + _UInt128(0xDC21A1171D42645D, 0x76707543F4FA1F74), + _UInt128(0x899504AE72497EBA, 0x6A06494A791C53A9), + _UInt128(0xABFA45DA0EDBDE69, 0x0487DB9D17636893), + _UInt128(0xD6F8D7509292D603, 0x45A9D2845D3C42B7), + _UInt128(0x865B86925B9BC5C2, 0x0B8A2392BA45A9B3), + _UInt128(0xA7F26836F282B732, 0x8E6CAC7768D7141F), + _UInt128(0xD1EF0244AF2364FF, 0x3207D795430CD927), + _UInt128(0x8335616AED761F1F, 0x7F44E6BD49E807B9), + _UInt128(0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A7), + _UInt128(0xCD036837130890A1, 0x36DBA887C37A8C10), + _UInt128(0x802221226BE55A64, 0xC2494954DA2C978A), + _UInt128(0xA02AA96B06DEB0FD, 0xF2DB9BAA10B7BD6D), + _UInt128(0xC83553C5C8965D3D, 0x6F92829494E5ACC8), + _UInt128(0xFA42A8B73ABBF48C, 0xCB772339BA1F17FA), + _UInt128(0x9C69A97284B578D7, 0xFF2A760414536EFC), + _UInt128(0xC38413CF25E2D70D, 0xFEF5138519684ABB), + _UInt128(0xF46518C2EF5B8CD1, 0x7EB258665FC25D6A), + _UInt128(0x98BF2F79D5993802, 0xEF2F773FFBD97A62), + _UInt128(0xBEEEFB584AFF8603, 0xAAFB550FFACFD8FB), + _UInt128(0xEEAABA2E5DBF6784, 0x95BA2A53F983CF39), + _UInt128(0x952AB45CFA97A0B2, 0xDD945A747BF26184), + _UInt128(0xBA756174393D88DF, 0x94F971119AEEF9E5), + _UInt128(0xE912B9D1478CEB17, 0x7A37CD5601AAB85E), + _UInt128(0x91ABB422CCB812EE, 0xAC62E055C10AB33B), + _UInt128(0xB616A12B7FE617AA, 0x577B986B314D600A), + _UInt128(0xE39C49765FDF9D94, 0xED5A7E85FDA0B80C), + _UInt128(0x8E41ADE9FBEBC27D, 0x14588F13BE847308), + _UInt128(0xB1D219647AE6B31C, 0x596EB2D8AE258FC9), + _UInt128(0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BC), + _UInt128(0x8AEC23D680043BEE, 0x25DE7BB9480D5855), + _UInt128(0xADA72CCC20054AE9, 0xAF561AA79A10AE6B), + _UInt128(0xD910F7FF28069DA4, 0x1B2BA1518094DA05), + _UInt128(0x87AA9AFF79042286, 0x90FB44D2F05D0843), + _UInt128(0xA99541BF57452B28, 0x353A1607AC744A54), + _UInt128(0xD3FA922F2D1675F2, 0x42889B8997915CE9), + _UInt128(0x847C9B5D7C2E09B7, 0x69956135FEBADA12), + _UInt128(0xA59BC234DB398C25, 0x43FAB9837E699096), + _UInt128(0xCF02B2C21207EF2E, 0x94F967E45E03F4BC), + _UInt128(0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F6), + _UInt128(0xA1BA1BA79E1632DC, 0x6462D92A69731733), + _UInt128(0xCA28A291859BBF93, 0x7D7B8F7503CFDCFF), + _UInt128(0xFCB2CB35E702AF78, 0x5CDA735244C3D43F), + _UInt128(0x9DEFBF01B061ADAB, 0x3A0888136AFA64A8), + _UInt128(0xC56BAEC21C7A1916, 0x088AAA1845B8FDD1), + _UInt128(0xF6C69A72A3989F5B, 0x8AAD549E57273D46), + _UInt128(0x9A3C2087A63F6399, 0x36AC54E2F678864C), + _UInt128(0xC0CB28A98FCF3C7F, 0x84576A1BB416A7DE), + _UInt128(0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D6), + _UInt128(0x969EB7C47859E743, 0x9F644AE5A4B1B326), + _UInt128(0xBC4665B596706114, 0x873D5D9F0DDE1FEF), + _UInt128(0xEB57FF22FC0C7959, 0xA90CB506D155A7EB), + _UInt128(0x9316FF75DD87CBD8, 0x09A7F12442D588F3), + _UInt128(0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB30), + _UInt128(0xE5D3EF282A242E81, 0x8F1668C8A86DA5FB), + _UInt128(0x8FA475791A569D10, 0xF96E017D694487BD), + _UInt128(0xB38D92D760EC4455, 0x37C981DCC395A9AD), + _UInt128(0xE070F78D3927556A, 0x85BBE253F47B1418), + _UInt128(0x8C469AB843B89562, 0x93956D7478CCEC8F), + _UInt128(0xAF58416654A6BABB, 0x387AC8D1970027B3), + _UInt128(0xDB2E51BFE9D0696A, 0x06997B05FCC0319F), + _UInt128(0x88FCF317F22241E2, 0x441FECE3BDF81F04), + _UInt128(0xAB3C2FDDEEAAD25A, 0xD527E81CAD7626C4), + _UInt128(0xD60B3BD56A5586F1, 0x8A71E223D8D3B075), + _UInt128(0x85C7056562757456, 0xF6872D5667844E4A), + _UInt128(0xA738C6BEBB12D16C, 0xB428F8AC016561DC), + _UInt128(0xD106F86E69D785C7, 0xE13336D701BEBA53), + _UInt128(0x82A45B450226B39C, 0xECC0024661173474), + _UInt128(0xA34D721642B06084, 0x27F002D7F95D0191), + _UInt128(0xCC20CE9BD35C78A5, 0x31EC038DF7B441F5), + _UInt128(0xFF290242C83396CE, 0x7E67047175A15272), + _UInt128(0x9F79A169BD203E41, 0x0F0062C6E984D387), + _UInt128(0xC75809C42C684DD1, 0x52C07B78A3E60869), + _UInt128(0xF92E0C3537826145, 0xA7709A56CCDF8A83), + _UInt128(0x9BBCC7A142B17CCB, 0x88A66076400BB692), + _UInt128(0xC2ABF989935DDBFE, 0x6ACFF893D00EA436), + _UInt128(0xF356F7EBF83552FE, 0x0583F6B8C4124D44), + _UInt128(0x98165AF37B2153DE, 0xC3727A337A8B704B), + _UInt128(0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5D), + _UInt128(0xEDA2EE1C7064130C, 0x1162DEF06F79DF74), + _UInt128(0x9485D4D1C63E8BE7, 0x8ADDCB5645AC2BA9), + _UInt128(0xB9A74A0637CE2EE1, 0x6D953E2BD7173693), + _UInt128(0xE8111C87C5C1BA99, 0xC8FA8DB6CCDD0438), + _UInt128(0x910AB1D4DB9914A0, 0x1D9C9892400A22A3), + _UInt128(0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4C), + _UInt128(0xE2A0B5DC971F303A, 0x2E44AE64840FD61E), + _UInt128(0x8DA471A9DE737E24, 0x5CEAECFED289E5D3), + _UInt128(0xB10D8E1456105DAD, 0x7425A83E872C5F48), + _UInt128(0xDD50F1996B947518, 0xD12F124E28F7771A), + _UInt128(0x8A5296FFE33CC92F, 0x82BD6B70D99AAA70), + _UInt128(0xACE73CBFDC0BFB7B, 0x636CC64D1001550C), + _UInt128(0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4F), + _UInt128(0x8714A775E3E95C78, 0x65ACFAEC34810A72), + _UInt128(0xA8D9D1535CE3B396, 0x7F1839A741A14D0E), + _UInt128(0xD31045A8341CA07C, 0x1EDE48111209A051), + _UInt128(0x83EA2B892091E44D, 0x934AED0AAB460433), + _UInt128(0xA4E4B66B68B65D60, 0xF81DA84D56178540), + _UInt128(0xCE1DE40642E3F4B9, 0x36251260AB9D668F), + _UInt128(0x80D2AE83E9CE78F3, 0xC1D72B7C6B42601A), + _UInt128(0xA1075A24E4421730, 0xB24CF65B8612F820), + _UInt128(0xC94930AE1D529CFC, 0xDEE033F26797B628), + _UInt128(0xFB9B7CD9A4A7443C, 0x169840EF017DA3B2), + _UInt128(0x9D412E0806E88AA5, 0x8E1F289560EE864F), + _UInt128(0xC491798A08A2AD4E, 0xF1A6F2BAB92A27E3), + _UInt128(0xF5B5D7EC8ACB58A2, 0xAE10AF696774B1DC), + _UInt128(0x9991A6F3D6BF1765, 0xACCA6DA1E0A8EF2A), + _UInt128(0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF4), + _UInt128(0xEFF394DCFF8A948E, 0xDDFC4B4CEF07F5B1), + _UInt128(0x95F83D0A1FB69CD9, 0x4ABDAF101564F98F), + _UInt128(0xBB764C4CA7A4440F, 0x9D6D1AD41ABE37F2), + _UInt128(0xEA53DF5FD18D5513, 0x84C86189216DC5EE), + _UInt128(0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB5), + _UInt128(0xB7118682DBB66A77, 0x3FBC8C33221DC2A2), + _UInt128(0xE4D5E82392A40515, 0x0FABAF3FEAA5334B), + _UInt128(0x8F05B1163BA6832D, 0x29CB4D87F2A7400F), + _UInt128(0xB2C71D5BCA9023F8, 0x743E20E9EF511013), + _UInt128(0xDF78E4B2BD342CF6, 0x914DA9246B255417), + _UInt128(0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548F), + _UInt128(0xAE9672ABA3D0C320, 0xA184AC2473B529B2), + _UInt128(0xDA3C0F568CC4F3E8, 0xC9E5D72D90A2741F), + _UInt128(0x8865899617FB1871, 0x7E2FA67C7A658893), + _UInt128(0xAA7EEBFB9DF9DE8D, 0xDDBB901B98FEEAB8), + _UInt128(0xD51EA6FA85785631, 0x552A74227F3EA566), + _UInt128(0x8533285C936B35DE, 0xD53A88958F872760), + _UInt128(0xA67FF273B8460356, 0x8A892ABAF368F138), + _UInt128(0xD01FEF10A657842C, 0x2D2B7569B0432D86), + _UInt128(0x8213F56A67F6B29B, 0x9C3B29620E29FC74), + _UInt128(0xA298F2C501F45F42, 0x8349F3BA91B47B90), + _UInt128(0xCB3F2F7642717713, 0x241C70A936219A74), + _UInt128(0xFE0EFB53D30DD4D7, 0xED238CD383AA0111), + _UInt128(0x9EC95D1463E8A506, 0xF4363804324A40AB), + _UInt128(0xC67BB4597CE2CE48, 0xB143C6053EDCD0D6), + _UInt128(0xF81AA16FDC1B81DA, 0xDD94B7868E94050B), + _UInt128(0x9B10A4E5E9913128, 0xCA7CF2B4191C8327), + _UInt128(0xC1D4CE1F63F57D72, 0xFD1C2F611F63A3F1), + _UInt128(0xF24A01A73CF2DCCF, 0xBC633B39673C8CED), + _UInt128(0x976E41088617CA01, 0xD5BE0503E085D814), + _UInt128(0xBD49D14AA79DBC82, 0x4B2D8644D8A74E19), + _UInt128(0xEC9C459D51852BA2, 0xDDF8E7D60ED1219F), + _UInt128(0x93E1AB8252F33B45, 0xCABB90E5C942B504), + _UInt128(0xB8DA1662E7B00A17, 0x3D6A751F3B936244), + _UInt128(0xE7109BFBA19C0C9D, 0x0CC512670A783AD5), + _UInt128(0x906A617D450187E2, 0x27FB2B80668B24C6), + _UInt128(0xB484F9DC9641E9DA, 0xB1F9F660802DEDF7), + _UInt128(0xE1A63853BBD26451, 0x5E7873F8A0396974), + _UInt128(0x8D07E33455637EB2, 0xDB0B487B6423E1E9), + _UInt128(0xB049DC016ABC5E5F, 0x91CE1A9A3D2CDA63), + _UInt128(0xDC5C5301C56B75F7, 0x7641A140CC7810FC), + _UInt128(0x89B9B3E11B6329BA, 0xA9E904C87FCB0A9E), + _UInt128(0xAC2820D9623BF429, 0x546345FA9FBDCD45), + _UInt128(0xD732290FBACAF133, 0xA97C177947AD4096), + _UInt128(0x867F59A9D4BED6C0, 0x49ED8EABCCCC485E), + _UInt128(0xA81F301449EE8C70, 0x5C68F256BFFF5A75), + _UInt128(0xD226FC195C6A2F8C, 0x73832EEC6FFF3112), + _UInt128(0x83585D8FD9C25DB7, 0xC831FD53C5FF7EAC), + _UInt128(0xA42E74F3D032F525, 0xBA3E7CA8B77F5E56), + _UInt128(0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EC), + _UInt128(0x80444B5E7AA7CF85, 0x7980D163CF5B81B4), + _UInt128(0xA0555E361951C366, 0xD7E105BCC3326220), + _UInt128(0xC86AB5C39FA63440, 0x8DD9472BF3FEFAA8), + _UInt128(0xFA856334878FC150, 0xB14F98F6F0FEB952), + _UInt128(0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D4), + _UInt128(0xC3B8358109E84F07, 0x0A862F80EC4700C9), + _UInt128(0xF4A642E14C6262C8, 0xCD27BB612758C0FB), + _UInt128(0x98E7E9CCCFBD7DBD, 0x8038D51CB897789D), + _UInt128(0xBF21E44003ACDD2C, 0xE0470A63E6BD56C4), + _UInt128(0xEEEA5D5004981478, 0x1858CCFCE06CAC75), + _UInt128(0x95527A5202DF0CCB, 0x0F37801E0C43EBC9), + _UInt128(0xBAA718E68396CFFD, 0xD30560258F54E6BB), + _UInt128(0xE950DF20247C83FD, 0x47C6B82EF32A206A), + _UInt128(0x91D28B7416CDD27E, 0x4CDC331D57FA5442), + _UInt128(0xB6472E511C81471D, 0xE0133FE4ADF8E953), + _UInt128(0xE3D8F9E563A198E5, 0x58180FDDD97723A7), + _UInt128(0x8E679C2F5E44FF8F, 0x570F09EAA7EA7649), + _UInt128(0xB201833B35D63F73, 0x2CD2CC6551E513DB), + _UInt128(0xDE81E40A034BCF4F, 0xF8077F7EA65E58D2), + _UInt128(0x8B112E86420F6191, 0xFB04AFAF27FAF783), + _UInt128(0xADD57A27D29339F6, 0x79C5DB9AF1F9B564), + _UInt128(0xD94AD8B1C7380874, 0x18375281AE7822BD), + _UInt128(0x87CEC76F1C830548, 0x8F2293910D0B15B6), + _UInt128(0xA9C2794AE3A3C69A, 0xB2EB3875504DDB23), + _UInt128(0xD433179D9C8CB841, 0x5FA60692A46151EC), + _UInt128(0x849FEEC281D7F328, 0xDBC7C41BA6BCD334), + _UInt128(0xA5C7EA73224DEFF3, 0x12B9B522906C0801), + _UInt128(0xCF39E50FEAE16BEF, 0xD768226B34870A01), + _UInt128(0x81842F29F2CCE375, 0xE6A1158300D46641), + _UInt128(0xA1E53AF46F801C53, 0x60495AE3C1097FD1), + _UInt128(0xCA5E89B18B602368, 0x385BB19CB14BDFC5), + _UInt128(0xFCF62C1DEE382C42, 0x46729E03DD9ED7B6), + _UInt128(0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D2), + _UInt128(0xC5A05277621BE293, 0xC7098B7305241886), + _UInt128(0xF70867153AA2DB38, 0xB8CBEE4FC66D1EA8), +) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 1391914b55..26ae8cd003 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -650,6 +650,38 @@ fn _integral_type_of[type: DType]() -> DType: return type.invalid +@always_inline("nodebug") +fn _uint_type_of[type: DType]() -> DType: + """Gets the unsigned integral type which has the same bitwidth as the input + type.""" + + @parameter + if type.is_integral() and type.is_unsigned(): + return type + + @parameter + if type.is_float8() or type is DType.int8: + return DType.uint8 + + @parameter + if type.is_half_float() or type is DType.int16: + return DType.uint16 + + @parameter + if ( + type is DType.float32 + or type is DType.tensor_float32 + or type is DType.int32 + ): + return DType.uint32 + + @parameter + if type is DType.float64 or type is DType.int64: + return DType.uint64 + + return type.invalid + + # ===-------------------------------------------------------------------===# # _unsigned_integral_type_of # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 52d9b5cb7d..a1384e053b 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -226,114 +226,6 @@ fn _snprintf[ ) -@no_inline -fn _snprintf_scalar[ - type: DType, - float_format: StringLiteral = "%.17g", -](buffer: UnsafePointer[UInt8], size: Int, x: Scalar[type]) -> Int: - @parameter - if type is DType.bool: - if x: - return _snprintf["True"](buffer, size) - else: - return _snprintf["False"](buffer, size) - elif type.is_integral(): - return _snprintf[_get_dtype_printf_format[type]()](buffer, size, x) - elif ( - type is DType.float16 or type is DType.bfloat16 or type is DType.float32 - ): - # We need to cast the value to float64 to print it. - return _float_repr[float_format](buffer, size, x.cast[DType.float64]()) - elif type is DType.float64: - return _float_repr[float_format](buffer, size, rebind[Float64](x)) - return 0 - - -# ===----------------------------------------------------------------------=== # -# Helper functions to print a single pop scalar without spacing or new line. -# ===----------------------------------------------------------------------=== # - - -@no_inline -fn _float_repr[ - fmt: StringLiteral = "%.17g" -](buffer: UnsafePointer[UInt8], size: Int, x: Float64) -> Int: - # Using `%.17g` with decimal check is equivalent to CPython's fallback path - # when its more complex dtoa library (forked from - # https://github.com/dtolnay/dtoa) is not available. - var n = _snprintf[fmt](buffer, size, x.value) - # If the buffer isn't big enough to add anything, then just return. - if n + 2 >= size: - return n - # Don't do anything fancy. Just insert ".0" if there is no decimal and this - # is not in exponent form. - var p = buffer - alias minus = ord("-") - alias dot = ord(".") - if p[] == minus: - p += 1 - while p[] != 0 and isdigit(p[]): - p += 1 - if p[]: - return n - p[] = dot - p += 1 - p[] = ord("0") - p += 1 - p[] = 0 - return n + 2 - - -# ===----------------------------------------------------------------------=== # -# _put -# ===----------------------------------------------------------------------=== # - - -fn _put(strref: StringRef, file: FileDescriptor = stdout): - var str_slice = StringSlice[ImmutableAnyOrigin]( - unsafe_from_utf8_strref=strref - ) - - _put(str_slice, file=file) - - -@no_inline -fn _put[ - lif: ImmutableOrigin, // -](x: StringSlice[lif], file: FileDescriptor = stdout): - # Avoid printing "(null)" for an empty/default constructed `String` - var str_len = x.byte_length() - - if not str_len: - return - - @parameter - if triple_is_nvidia_cuda(): - # Note: - # This assumes that the `StringSlice` that was passed in is NUL - # terminated. - var tmp = 0 - var arg_ptr = UnsafePointer.address_of(tmp) - _ = external_call["vprintf", Int32]( - x.unsafe_ptr(), arg_ptr.bitcast[OpaquePointer]() - ) - else: - alias MAX_STR_LEN = 0x1000_0000 - - # The string can be printed, so that's fine. - if str_len < MAX_STR_LEN: - _printf["%.*s"](x.byte_length(), x.unsafe_ptr(), file=file) - return - - # The string is large, then we need to chunk it. - var p = x.unsafe_ptr() - while str_len: - var ll = min(str_len, MAX_STR_LEN) - _printf["%.*s"](ll, p, file=file) - str_len -= ll - p += ll - - # ===----------------------------------------------------------------------=== # # print # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 1d48359a97..db493c1e58 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -40,6 +40,8 @@ from builtin.dtype import _uint_type_of_width from hashlib.hash import _hash_simd from hashlib._hasher import _HashableWithHasher, _Hasher from builtin.format_int import _try_write_int +from builtin._format_float import _write_float +from builtin.io import _snprintf from collections import InlineArray from memory import bitcast, UnsafePointer @@ -60,7 +62,6 @@ from .dtype import ( _unsigned_integral_type_of, _scientific_notation_digits, ) -from .io import _printf, _snprintf_scalar from collections.string import ( _calc_format_buffer_size, _calc_initial_buffer_size, @@ -1439,20 +1440,17 @@ struct SIMD[type: DType, size: Int]( Returns: The representation of the SIMD value. """ - var output = String() - self.write_to[use_scientific_notation=True](output) - - var values = output.as_string_slice() - - @parameter - if size > 1: - # TODO: Fix when slice indexing is implemented on StringSlice - values = StringSlice(unsafe_from_utf8=output.as_bytes()[1:-1]) - - return ( - "SIMD[" + type.__repr__() + ", " + str(size) + "](" + values + ")" - ) + output.write("SIMD[" + type.__repr__() + ", ", size, "](") + # Write each element. + for i in range(size): + var element = self[i] + # Write separators between each element. + if i != 0: + output.write(", ") + _write_scalar(output, element) + output.write(")") + return output @always_inline("nodebug") fn __floor__(self) -> Self: @@ -1662,82 +1660,21 @@ struct SIMD[type: DType, size: Int]( Args: writer: The object to write to. """ - self.write_to[use_scientific_notation=False](writer) - - # This overload is required to keep SIMD compliant with the Writable - # trait, and the call to `String.write(self)` in SIMD.__str__ will - # fail to compile. - @no_inline - fn write_to[ - W: Writer, use_scientific_notation: Bool - ](self, inout writer: W): - """ - Formats this SIMD value to the provided Writer. - Parameters: - W: A type conforming to the Writable trait. - use_scientific_notation: Whether floats should use scientific - notation. This parameter does not apply to integer types. - - Args: - writer: The object to write to. - """ - - # Print an opening `[`. + # Write an opening `[`. @parameter if size > 1: writer.write("[") - # Print each element. + # Write each element. for i in range(size): var element = self[i] - # Print separators between each element. + # Write separators between each element. if i != 0: writer.write(", ") + _write_scalar(writer, element) - @parameter - if triple_is_nvidia_cuda(): - # FIXME(MSTDL-406): - # The uses of `printf` below prints "out of band" with the - # `Writer` passed in, meaning this will only work if - # `Writer` is an unbuffered wrapper around printf (which - # Writer.stdout currently is by default). - # - # This is a workaround to permit debug formatting of - # floating-point values on GPU, where printing to stdout - # is the only way the Writer framework is currently - # used. - - @parameter - if type is DType.float64: - # get_dtype_printf_format hardcodes 17 digits of precision. - _printf["%g"](element) - elif type.is_floating_point(): - # We need to cast the value to float64 to print it, to avoid - # an ABI mismatch. - _printf["%g"](element.cast[DType.float64]()) - elif type.is_integral(): - var err = _try_write_int(writer, element) - if err: - abort( - "unreachable: unexpected write int failure" - " condition: " - + str(err.value()) - ) - else: - _printf[_get_dtype_printf_format[type]()](element) - else: - - @parameter - if use_scientific_notation and type.is_floating_point(): - alias float_format = "%." + _scientific_notation_digits[ - type - ]() + "e" - _format_scalar[float_format](writer, element) - else: - _format_scalar(writer, element) - - # Print a closing `]`. + # Write a closing `]`. @parameter if size > 1: writer.write("]") @@ -3153,34 +3090,6 @@ fn _simd_apply[ return result -# ===----------------------------------------------------------------------=== # -# Scalar Formatting -# ===----------------------------------------------------------------------=== # - - -fn _format_scalar[ - dtype: DType, - W: Writer, //, - float_format: StringLiteral = "%.17g", -](inout writer: W, value: Scalar[dtype]): - # Stack allocate enough bytes to store any formatted Scalar value of any - # type. - alias size: Int = _calc_format_buffer_size[dtype]() - - var buf = InlineArray[UInt8, size](fill=0) - - var wrote = _snprintf_scalar[dtype, float_format]( - buf.unsafe_ptr(), - size, - value, - ) - - # SAFETY: - # Create a slice to only those bytes in `buf` that have been initialized. - var span = Span[Byte](buf)[:wrote] - writer.write_bytes(span) - - # ===----------------------------------------------------------------------=== # # modf # ===----------------------------------------------------------------------=== # @@ -3256,3 +3165,40 @@ fn _floor(x: SIMD) -> __type_of(x): bits, ) return __type_of(x)(from_bits=bits) + + +fn _write_scalar[ + dtype: DType, + W: Writer, //, +](inout writer: W, value: Scalar[dtype]): + @parameter + if dtype == DType.bool: + if value: + writer.write("True") + else: + writer.write("False") + + elif dtype.is_floating_point(): + _write_float(writer, value) + + # TODO: bring in modern int formatter and remove GPU specific code + elif dtype.is_integral(): + + @parameter + if triple_is_nvidia_cuda(): + var err = _try_write_int(writer, value) + if err: + abort( + "unreachable: unexpected write int failure condition: " + + str(err.value()) + ) + else: + # Stack allocate enough bytes to store any formatted Scalar value. + alias size: Int = _calc_format_buffer_size[dtype]() + var buf = InlineArray[UInt8, size](fill=0) + var wrote = _snprintf[_get_dtype_printf_format[dtype]()]( + buf.unsafe_ptr(), size, value + ) + # SAFETY: + # Create a slice to only those bytes in `buf` that have been initialized. + writer.write_bytes(Span[Byte](buf)[:wrote]) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 9e2d918eb7..2cf83b725c 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -677,7 +677,7 @@ fn frexp[ constrained[type.is_floating_point(), "must be a floating point value"]() alias T = SIMD[type, simd_width] alias zero = T(0) - alias max_exponent = FPUtils[type].max_exponent() - 2 + alias max_exponent = FPUtils[type].max_exponent() - 1 alias mantissa_width = FPUtils[type].mantissa_width() var mask1 = _frexp_mask1[simd_width, type]() var mask2 = _frexp_mask2[simd_width, type]() diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 6c7133008a..b5c006aef4 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -23,7 +23,7 @@ from sys import bitwidthof, has_neon, has_sse4, llvm_intrinsic from sys._assembly import inlined_assembly from sys.ffi import _external_call_const -from builtin.dtype import _integral_type_of +from builtin.dtype import _integral_type_of, _uint_type_of from builtin.simd import _simd_apply from memory import UnsafePointer, bitcast @@ -54,6 +54,9 @@ struct FPUtils[ alias integral_type = _integral_type_of[type]() """The equivalent integer type of the float type.""" + alias uint_type = _uint_type_of[type]() + """The equivalent uint type of the float type.""" + @staticmethod @always_inline("nodebug") fn mantissa_width() -> IntLiteral: @@ -81,7 +84,8 @@ struct FPUtils[ @staticmethod @always_inline("nodebug") fn max_exponent() -> IntLiteral: - """Returns the max exponent of a floating point type. + """Returns the max exponent of a floating point type, taking into + account special reserved cases such infinity and nan. Returns: The max exponent. @@ -89,16 +93,39 @@ struct FPUtils[ @parameter if type is DType.float8e4m3: - return 8 + return 7 + elif type is DType.float8e5m2: + return 15 + elif type is DType.float16: + return 15 + elif type is DType.float32 or type is DType.bfloat16: + return 127 + else: + constrained[type is DType.float64, "unsupported float type"]() + return 1023 + + @staticmethod + @always_inline("nodebug") + fn min_exponent() -> IntLiteral: + """Returns the min exponent of a floating point type, taking into + account special reserved cases such as infinity and nan. + + Returns: + The min exponent. + """ + + @parameter + if type is DType.float8e4m3: + return -6 elif type is DType.float8e5m2: - return 16 + return -14 elif type is DType.float16: - return 16 + return -14 elif type is DType.float32 or type is DType.bfloat16: - return 128 + return -126 else: constrained[type is DType.float64, "unsupported float type"]() - return 1024 + return -1022 @staticmethod @always_inline("nodebug") @@ -140,7 +167,7 @@ struct FPUtils[ Returns: The exponent bias. """ - return Self.max_exponent() - 1 + return Self.max_exponent() @staticmethod @always_inline @@ -211,6 +238,19 @@ struct FPUtils[ """ return int(bitcast[Self.integral_type, 1](value)) + @staticmethod + @always_inline + fn bitcast_to_uint(value: Scalar[type]) -> Scalar[Self.uint_type]: + """Bitcasts the floating-point value to an integer. + + Args: + value: The floating-point type. + + Returns: + An integer representation of the floating-point value. + """ + return bitcast[Self.uint_type, 1](value) + @staticmethod @always_inline fn bitcast_from_integer(value: Int) -> Scalar[type]: @@ -282,8 +322,10 @@ struct FPUtils[ Returns: Returns the exponent bits. """ - alias bias = int(Self.exponent_bias()) - return Self.get_exponent(value) - bias + return int( + Self.bitcast_to_uint(value) >> Self.mantissa_width() + & ((1 << Self.exponent_width()) - 1) + ) @staticmethod @always_inline @@ -315,6 +357,19 @@ struct FPUtils[ """ return Self.bitcast_to_integer(value) & Self.mantissa_mask() + @staticmethod + @always_inline + fn get_mantissa_uint(value: Scalar[type]) -> Scalar[Self.uint_type]: + """Gets the mantissa bits of the floating-point value. + + Args: + value: The floating-point value. + + Returns: + The mantissa bits. + """ + return Self.bitcast_to_uint(value) & Self.mantissa_mask() + @staticmethod @always_inline fn set_mantissa(value: Scalar[type], mantissa: Int) -> Scalar[type]: diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index ad19e0fce0..3f8f375541 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -264,7 +264,9 @@ struct _WriteBuffer[W: MovableWriter, //, capacity: Int](Writer): fn write_buffered[ - buffer_size: Int, W: MovableWriter, *Ts: Writable + W: MovableWriter, //, + *Ts: Writable, + buffer_size: Int, ]( owned writer: W, args: VariadicPack[_, Writable, *Ts], @@ -279,9 +281,9 @@ fn write_buffered[ Parameters: - buffer_size: How many bytes to write to a buffer before writing out. W: The type of the `Writer` to write to. Ts: The types of each arg to write. Each type must satisfy `Writable`. + buffer_size: How many bytes to write to a buffer before writing out. Args: writer: The `Writer` to write to. diff --git a/stdlib/test/builtin/test_format_float.mojo b/stdlib/test/builtin/test_format_float.mojo new file mode 100644 index 0000000000..1544f185ff --- /dev/null +++ b/stdlib/test/builtin/test_format_float.mojo @@ -0,0 +1,207 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from builtin._format_float import _write_float +from testing import assert_equal +from python import Python, PythonObject +from random import random_float64 + + +def test_float64(): + var test_floats = List[Float64]( + # Zero values + 0.0, + -0.0, + # Integer-like values + 1.0, + -1.0, + 42.0, + # Simple decimals + 0.5, + -0.5, + 1.23, + -1.23, + # Very small numbers + 0.000123, + -0.000123, + 1e-10, + -1e-10, + # Very large numbers + 1e10, + -1e10, + 1.23e15, + -1.23e15, + # Numbers requiring scientific notation + 1.23e-15, + -1.23e-15, + 1.23e20, + -1.23e20, + # Numbers near scientific notation threshold (typically e±16) + 9.9999e14, + -9.9999e14, + 1e15, + -1e15, + # Repeating decimals + 1 / 3, # 0.3333... + -1 / 3, # -0.3333... + 2 / 3, # 0.6666... + # Numbers with many decimal places + 3.141592653589793, + -3.141592653589793, + # Numbers that might trigger rounding + 1.999999999999999, + -1.999999999999999, + 2.0000000000000004, + -2.0000000000000004, + # Subnormal numbers + 2.2250738585072014e-308, # Near minimum subnormal + -2.2250738585072014e-308, + # Numbers near system limits + 1.7976931348623157e308, # Near maximum float + -1.7976931348623157e308, + 2.2250738585072014e-308, # Near minimum normal float + -2.2250738585072014e-308, + # Numbers that might trigger special formatting + 1000000.0, # Could be formatted as 1e6 or 1000000 + 0.0000001, # Could be formatted as 1e-7 or 0.0000001 + # Numbers with trailing zeros + 1.100, + -1.100, + 1.0010, + -1.0010, + # Numbers that might affect alignment + 999999.999999, + -999999.999999, + 0.000000999999, + -0.000000999999, + ) + + for f in test_floats: + # Float64 + var mojo_f64_str = String() + _write_float(mojo_f64_str, f[]) + + var py_f64_str = str(PythonObject(f[])) + + assert_equal(py_f64_str, mojo_f64_str) + + +def test_float32(): + var test_floats = List[Float32]( + # Zero values + Float32(0.0), + Float32(-0.0), + # Integer-like values + Float32(1.0), + Float32(-1.0), + Float32(42.0), + # Simple decimals + Float32(0.5), + Float32(-0.5), + Float32(1.23), + Float32(-1.23), + # Very small numbers + Float32(1.18e-38), # Near minimum normal float32 + Float32(-1.18e-38), + Float32(1e-35), + Float32(-1e-35), + # Very large numbers + Float32(1e35), + Float32(-1e35), + Float32(3.4e38), # Near maximum float32 + Float32(-3.4e38), + # Numbers requiring scientific notation + Float32(1.23e-35), + Float32(-1.23e-35), + Float32(1.23e35), + Float32(-1.23e35), + # Numbers near scientific notation threshold + Float32(9.9999e14), + Float32(-9.9999e14), + Float32(1e15), + Float32(-1e15), + # Repeating decimals + Float32(0.3333), + Float32(-0.3333), + Float32(0.6666), + # Numbers with precision near float32 limit (~7 decimal digits) + Float32(3.141593), # Pi + Float32(-3.141593), + # Numbers that might trigger rounding + Float32(1.9999999), + Float32(-1.9999999), + Float32(2.0000002), + Float32(-2.0000002), + # Subnormal numbers for float32 + Float32(1.4e-45), # Near minimum subnormal float32 + Float32(-1.4e-45), + # Numbers near system limits for float32 + Float32(3.4028234e38), # Max float32 + Float32(-3.4028234e38), + Float32(1.1754944e-38), # Min normal float32 + Float32(-1.1754944e-38), + # Numbers that might trigger special formatting + Float32(100000.0), + Float32(0.000001), + # Numbers with trailing zeros + Float32(1.100), + Float32(-1.100), + Float32(1.001), + Float32(-1.001), + # Numbers that might affect alignment + Float32(99999.99), + Float32(-99999.99), + Float32(0.0000999), + Float32(-0.0000999), + # Powers of 2 (important for binary floating-point) + Float32(2.0), + Float32(4.0), + Float32(8.0), + Float32(16.0), + Float32(32.0), + Float32(64.0), + Float32(128.0), + # Numbers that demonstrate float32 precision limits + Float32( + 16777216.0 + ), # 2^24, last integer that can be represented exactly + Float32(16777217.0), # 2^24 + 1, demonstrates precision loss + # Numbers that demonstrate mantissa precision + Float32(1.000000119), # Smallest number > 1.0 in float32 + Float32(0.999999881), # Largest number < 1.0 in float32 + ) + + for f in test_floats: + var np = Python.import_module("numpy") + var mojo_f32_str = String() + _write_float(mojo_f32_str, f[]) + + var py_f32_str = str(np.float32(f[])) + + assert_equal(py_f32_str, mojo_f32_str) + + +def test_random_floats(): + for _ in range(10000): + var f64 = random_float64() + var mojo_f64_str = String() + _write_float(mojo_f64_str, f64) + var py_f64_str = str(PythonObject(f64)) + assert_equal(py_f64_str, mojo_f64_str) + + +def main(): + test_float64() + test_float32() + test_random_floats() diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 11ca1936e0..d17765bf88 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -104,11 +104,11 @@ def test_print(): var float32: Float32 = 99.9 var float64: Float64 = -129.2901823 print(">", 3.14, file=checker.stream()) - checker.check_line_starts_with("> 3.14000") + checker.check_line("> 3.14") print(">", float32, file=checker.stream()) - checker.check_line_starts_with("> 99.90000") + checker.check_line("> 99.9") print(">", float64, file=checker.stream()) - checker.check_line_starts_with("> -129.29018") + checker.check_line("> -129.2901823") print(">", IndexList[3](1, 2, 3), file=checker.stream()) checker.check_line_starts_with("> (1, 2, 3)") @@ -117,7 +117,7 @@ def test_print(): var pi = 3.1415916535897743 print(">", pi, file=checker.stream()) - checker.check_line_starts_with("> 3.1415916535") + checker.check_line("> 3.1415916535897743") var x = (pi - 3.141591) * 1e6 print(">", x, file=checker.stream()) checker.check_line_starts_with("> 0.6535") diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 1aa89be2a4..53851a5f76 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -120,17 +120,17 @@ def test_simd_repr(): assert_equal(Int32(4).__repr__(), "SIMD[DType.int32, 1](4)") assert_equal( Float64(235234523.3452).__repr__(), - "SIMD[DType.float64, 1](2.3523452334520000e+08)", + "SIMD[DType.float64, 1](235234523.3452)", ) assert_equal( - Float32(2897239).__repr__(), "SIMD[DType.float32, 1](2.89723900e+06)" + Float32(2897239).__repr__(), "SIMD[DType.float32, 1](2897239.0)" ) - assert_equal(Float16(324).__repr__(), "SIMD[DType.float16, 1](3.2400e+02)") + assert_equal(Float16(324).__repr__(), "SIMD[DType.float16, 1](324.0)") assert_equal( SIMD[DType.float32, 4]( Float32.MAX, Float32.MIN, -0.0, nan[DType.float32]() ).__repr__(), - "SIMD[DType.float32, 4](inf, -inf, -0.00000000e+00, nan)", + "SIMD[DType.float32, 4](inf, -inf, -0.0, nan)", ) diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index b5a1d3007c..bd259dd639 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1441,22 +1441,20 @@ def test_format_conversion_flags(): var b = 21.1 assert_true( - "21.100000000000001 SIMD[DType.float64, 1](2" - in String("{} {!r}").format(b, b), + "21.1 SIMD[DType.float64, 1](2" in String("{} {!r}").format(b, b), ) assert_true( - "21.100000000000001 SIMD[DType.float64, 1](2" - in String("{!s} {!r}").format(b, b), + "21.1 SIMD[DType.float64, 1](2" in String("{!s} {!r}").format(b, b), ) var c = 1e100 assert_equal( String("{} {!r}").format(c, c), - "1e+100 SIMD[DType.float64, 1](1.0000000000000000e+100)", + "1e+100 SIMD[DType.float64, 1](1e+100)", ) assert_equal( String("{!s} {!r}").format(c, c), - "1e+100 SIMD[DType.float64, 1](1.0000000000000000e+100)", + "1e+100 SIMD[DType.float64, 1](1e+100)", ) var d = 42 @@ -1486,7 +1484,7 @@ def test_format_conversion_flags(): assert_equal( String("{3} {2} {1} {0}").format(a, d, c, b), - "21.100000000000001 1e+100 42 Mojo", + "21.1 1e+100 42 Mojo", ) assert_true( From 0e29dcda5bc356c0d7a468584f6245a405777341 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 8 Nov 2024 14:15:30 -0800 Subject: [PATCH 1845/2019] [stdlib] Buffer GPU printing to heap to synch output across threads This enables synched printing across GPU threads. For example `print("string", 42)` used to print like something like this: ``` stringstringstring string 42424242 ``` The arguments and newline characters were all jumbled up due to multiple calls to `vnprintf`, which is the only way to write to stdout from PTX. Because stack space on GPUs is very limited due to thousands of threads, we need to write into the heap in order to buffer the output before writing. So now the output looks like this: ``` string 42 string 42 string 42 string 42 ``` MODULAR_ORIG_COMMIT_REV_ID: 99a3c7c7674a70c07bd03d6b05f1fb4d74bff1d9 --- stdlib/src/builtin/io.mojo | 8 ++--- stdlib/src/utils/write.mojo | 64 ++++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index a1384e053b..ac2fb202ba 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -254,14 +254,12 @@ fn print[ flush: If set to true, then the stream is forcibly flushed. file: The output stream. """ - # TODO: Implement a float formatter for GPU to enable buffering to global # memory. PTX isn't able to call snprintf to format floats. + write_buffered[buffer_size=4096](file, values, sep=sep, end=end) + @parameter - if triple_is_nvidia_cuda(): - write_args(file, values, sep=sep, end=end) - else: - write_buffered[buffer_size=4096](file, values, sep=sep, end=end) + if not triple_is_nvidia_cuda(): if flush: _flush(file=file) diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 3f8f375541..fd2c8e4443 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -14,6 +14,9 @@ from collections import InlineArray from memory import memcpy, UnsafePointer +from utils import Span, StaticString +from sys.info import triple_is_nvidia_cuda +from builtin.io import _printf # ===----------------------------------------------------------------------===# @@ -220,7 +223,54 @@ trait MovableWriter(Movable, Writer): ... -struct _WriteBuffer[W: MovableWriter, //, capacity: Int](Writer): +struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): + var data: UnsafePointer[UInt8] + var pos: Int + var writer: W + + fn __init__(inout self, owned writer: W): + self.data = UnsafePointer[ + UInt8, + address_space = AddressSpace.GENERIC, + ].alloc(capacity) + self.pos = 0 + self.writer = writer^ + + fn flush(inout self): + self.writer.write_bytes( + Span[Byte, ImmutableAnyOrigin](unsafe_ptr=self.data, len=self.pos) + ) + self.pos = 0 + + @always_inline + fn write_bytes(inout self, bytes: Span[UInt8, _]): + len_bytes = len(bytes) + # If empty then return + if len_bytes == 0: + return + # If span is too large to fit in buffer, write directly and return + if len_bytes > capacity: + self.flush() + self.writer.write_bytes(bytes) + return + # If buffer would overflow, flush writer and reset pos to 0. + if self.pos + len_bytes > capacity: + self.flush() + ptr = bytes.unsafe_ptr() + # Continue writing to buffer + for i in range(len_bytes): + self.data[i + self.pos] = ptr[i] + self.pos += len_bytes + + fn write[*Ts: Writable](inout self, *args: *Ts): + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() + + +struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): var data: InlineArray[UInt8, capacity] var pos: Int var writer: W @@ -311,6 +361,12 @@ fn write_buffered[ ``` . """ - var buffer = _WriteBuffer[buffer_size](writer^) - write_args(buffer, args, sep=sep, end=end) - buffer.flush() + if triple_is_nvidia_cuda(): + # Stack space is very small on GPU due to many threads, so use heap + var buffer = _WriteBufferHeap[buffer_size](writer^) + write_args(buffer, args, sep=sep, end=end) + buffer.flush() + else: + var buffer = _WriteBufferStack[buffer_size](writer^) + write_args(buffer, args, sep=sep, end=end) + buffer.flush() From f8953e4ba03820b56c52d8d6db44b0200e9cded9 Mon Sep 17 00:00:00 2001 From: Alvydas Vitkauskas Date: Fri, 8 Nov 2024 17:42:59 -0600 Subject: [PATCH 1846/2019] [External] [stdlib] Deque - part 2 - add operator dunders and remaining traits (#50553) [External] [stdlib] Deque - part 2 - add operator dunders and remaining traits This is a second part of Deque implementation adding the operator dunders and all remaining traits. Co-authored-by: Alvydas Vitkauskas Closes modularml/mojo#3729 MODULAR_ORIG_COMMIT_REV_ID: b57e61d106fc1622eb92192e8133ddfe63fa5666 --- stdlib/src/collections/deque.mojo | 342 ++++++++++++++++++++++++ stdlib/test/collections/test_deque.mojo | 318 ++++++++++++++++++++++ 2 files changed, 660 insertions(+) diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index b065265479..c52991fa85 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -206,6 +206,157 @@ struct Deque[ElementType: CollectionElement]( (self._data + offset).destroy_pointee() self._data.free() + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + + fn __add__(self, other: Self) -> Self: + """Concatenates self with other and returns the result as a new deque. + + Args: + other: Deque whose elements will be appended to the elements of self. + + Returns: + The newly created deque with the properties of `self`. + """ + new = Self(other=self) + for element in other: + new.append(element[]) + return new^ + + fn __iadd__(inout self, other: Self): + """Appends the elements of other deque into self. + + Args: + other: Deque whose elements will be appended to self. + """ + for element in other: + self.append(element[]) + + fn __mul__(self, n: Int) -> Self: + """Concatenates `n` deques of `self` and returns a new deque. + + Args: + n: The multiplier number. + + Returns: + The new deque. + """ + if n <= 0: + return Self( + capacity=self._min_capacity, + min_capacity=self._min_capacity, + maxlen=self._maxlen, + shrink=self._shrink, + ) + new = Self(other=self) + for _ in range(n - 1): + for element in self: + new.append(element[]) + return new^ + + fn __imul__(inout self, n: Int): + """Concatenates self `n` times in place. + + Args: + n: The multiplier number. + """ + if n <= 0: + self.clear() + return + + orig = Self(other=self) + for _ in range(n - 1): + for element in orig: + self.append(element[]) + + fn __eq__[ + EqualityElementType: EqualityComparableCollectionElement, // + ]( + self: Deque[EqualityElementType], other: Deque[EqualityElementType] + ) -> Bool: + """Checks if two deques are equal. + + Parameters: + EqualityElementType: The type of the elements in the deque. + Must implement the trait `EqualityComparableCollectionElement`. + + Args: + other: The deque to compare with. + + Returns: + `True` if the deques are equal, `False` otherwise. + """ + if len(self) != len(other): + return False + + for i in range(len(self)): + offset_self = self._physical_index(self._head + i) + offset_other = other._physical_index(other._head + i) + if (self._data + offset_self)[] != (other._data + offset_other)[]: + return False + return True + + fn __ne__[ + EqualityElementType: EqualityComparableCollectionElement, // + ]( + self: Deque[EqualityElementType], other: Deque[EqualityElementType] + ) -> Bool: + """Checks if two deques are not equal. + + Parameters: + EqualityElementType: The type of the elements in the deque. + Must implement the trait `EqualityComparableCollectionElement`. + + Args: + other: The deque to compare with. + + Returns: + `True` if the deques are not equal, `False` otherwise. + """ + return not (self == other) + + fn __contains__[ + EqualityElementType: EqualityComparableCollectionElement, // + ](self: Deque[EqualityElementType], value: EqualityElementType) -> Bool: + """Verify if a given value is present in the deque. + + Parameters: + EqualityElementType: The type of the elements in the deque. + Must implement the trait `EqualityComparableCollectionElement`. + + Args: + value: The value to find. + + Returns: + True if the value is contained in the deque, False otherwise. + """ + for i in range(len(self)): + offset = self._physical_index(self._head + i) + if (self._data + offset)[] == value: + return True + return False + + fn __iter__( + ref [_]self, + ) -> _DequeIter[ElementType, __origin_of(self)]: + """Iterates over elements of the deque, returning the references. + + Returns: + An iterator of the references to the deque elements. + """ + return _DequeIter(0, Pointer.address_of(self)) + + fn __reversed__( + ref [_]self, + ) -> _DequeIter[ElementType, __origin_of(self), False]: + """Iterate backwards over the deque, returning the references. + + Returns: + A reversed iterator of the references to the deque elements. + """ + return _DequeIter[forward=False](len(self), Pointer.address_of(self)) + # ===-------------------------------------------------------------------===# # Trait implementations # ===-------------------------------------------------------------------===# @@ -253,6 +404,87 @@ struct Deque[ElementType: CollectionElement]( offset = self._physical_index(self._head + normalized_idx) return (self._data + offset)[] + @no_inline + fn write_to[ + RepresentableElementType: RepresentableCollectionElement, + WriterType: Writer, //, + ](self: Deque[RepresentableElementType], inout writer: WriterType): + """Writes `my_deque.__str__()` to a `Writer`. + + Parameters: + RepresentableElementType: The type of the Deque elements. + Must implement the trait `RepresentableCollectionElement`. + WriterType: A type conforming to the Writable trait. + + Args: + writer: The object to write to. + """ + writer.write("Deque(") + for i in range(len(self)): + offset = self._physical_index(self._head + i) + writer.write(repr((self._data + offset)[])) + if i < len(self) - 1: + writer.write(", ") + writer.write(")") + + @no_inline + fn __str__[ + RepresentableElementType: RepresentableCollectionElement, // + ](self: Deque[RepresentableElementType]) -> String: + """Returns a string representation of a `Deque`. + + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + my_deque = Deque[Int](1, 2, 3) + print(my_deque.__str__()) + ``` + + When the compiler supports conditional methods, then a simple `str(my_deque)` will + be enough. + + The elements' type must implement the `__repr__()` method for this to work. + + Parameters: + RepresentableElementType: The type of the elements in the deque. + Must implement the trait `RepresentableCollectionElement`. + + Returns: + A string representation of the deque. + """ + output = String() + self.write_to(output) + return output^ + + @no_inline + fn __repr__[ + RepresentableElementType: RepresentableCollectionElement, // + ](self: Deque[RepresentableElementType]) -> String: + """Returns a string representation of a `Deque`. + + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + my_deque = Deque[Int](1, 2, 3) + print(my_deque.__repr__()) + ``` + + When the compiler supports conditional methods, then a simple `repr(my_deque)` will + be enough. + + The elements' type must implement the `__repr__()` for this to work. + + Parameters: + RepresentableElementType: The type of the elements in the deque. + Must implement the trait `RepresentableCollectionElement`. + + Returns: + A string representation of the deque. + """ + return self.__str__() + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# @@ -274,6 +506,37 @@ struct Deque[ElementType: CollectionElement]( if self._head == self._tail: self._realloc(self._capacity << 1) + fn appendleft(inout self, owned value: ElementType): + """Appends a value to the left side of the deque. + + Args: + value: The value to append. + """ + # checking for positive _maxlen first is important for speed + if self._maxlen > 0 and len(self) == self._maxlen: + self._tail = self._physical_index(self._tail - 1) + (self._data + self._tail).destroy_pointee() + + self._head = self._physical_index(self._head - 1) + (self._data + self._head).init_pointee_move(value^) + + if self._head == self._tail: + self._realloc(self._capacity << 1) + + fn clear(inout self): + """Removes all elements from the deque leaving it with length 0. + + Resets the underlying storage capacity to `_min_capacity`. + """ + for i in range(len(self)): + offset = self._physical_index(self._head + i) + (self._data + offset).destroy_pointee() + self._data.free() + self._capacity = self._min_capacity + self._data = UnsafePointer[ElementType].alloc(self._capacity) + self._head = 0 + self._tail = 0 + fn extend(inout self, owned values: List[ElementType]): """Extends the right side of the deque by consuming elements of the list argument. @@ -306,6 +569,40 @@ struct Deque[ElementType: CollectionElement]( (src + i).move_pointee_into(self._data + self._tail) self._tail = self._physical_index(self._tail + 1) + fn extendleft(inout self, owned values: List[ElementType]): + """Extends the left side of the deque by consuming elements from the list argument. + + Acts as series of left appends resulting in reversed order of elements in the list argument. + + Args: + values: List whose elements will be added at the left side of the deque. + """ + n_move_total, n_move_self, n_move_values, n_pop_self, n_pop_values = ( + self._compute_pop_and_move_counts(len(self), len(values)) + ) + + # pop excess `self` elements + for _ in range(n_pop_self): + self._tail = self._physical_index(self._tail - 1) + (self._data + self._tail).destroy_pointee() + + # move from `self` to new location if we have to re-allocate + if n_move_total >= self._capacity: + self._prepare_for_new_elements(n_move_total, n_move_self) + + # we will consume all elements of `values` + values_data = values.steal_data() + + # pop excess elements from `values` + for i in range(n_pop_values): + (values_data + i).destroy_pointee() + + # move remaining elements from `values` + src = values_data + n_pop_values + for i in range(n_move_values): + self._head = self._physical_index(self._head - 1) + (src + i).move_pointee_into(self._data + self._head) + fn _compute_pop_and_move_counts( self, len_self: Int, len_values: Int ) -> (Int, Int, Int, Int, Int): @@ -418,3 +715,48 @@ struct Deque[ElementType: CollectionElement]( self._data.free() self._data = new_data self._capacity = new_capacity + + +@value +struct _DequeIter[ + deque_mutability: Bool, //, + ElementType: CollectionElement, + deque_lifetime: Origin[deque_mutability].type, + forward: Bool = True, +]: + """Iterator for Deque. + + Parameters: + deque_mutability: Whether the reference to the deque is mutable. + ElementType: The type of the elements in the deque. + deque_lifetime: The lifetime of the Deque. + forward: The iteration direction. `False` is backwards. + """ + + alias deque_type = Deque[ElementType] + + var index: Int + var src: Pointer[Self.deque_type, deque_lifetime] + + fn __iter__(self) -> Self: + return self + + fn __next__(inout self) -> Pointer[ElementType, deque_lifetime]: + @parameter + if forward: + self.index += 1 + return Pointer.address_of(self.src[][self.index - 1]) + else: + self.index -= 1 + return Pointer.address_of(self.src[][self.index]) + + fn __len__(self) -> Int: + @parameter + if forward: + return len(self.src[]) - self.index + else: + return self.index + + @always_inline + fn __has_next__(self) -> Bool: + return self.__len__() > 0 diff --git a/stdlib/test/collections/test_deque.mojo b/stdlib/test/collections/test_deque.mojo index 8130b865f0..16746021e7 100644 --- a/stdlib/test/collections/test_deque.mojo +++ b/stdlib/test/collections/test_deque.mojo @@ -280,6 +280,205 @@ fn test_impl_extend() raises: assert_equal((q._data + 15)[], 9) +fn test_impl_clear() raises: + q = Deque[Int](capacity=2) + q.append(1) + assert_equal(q._tail, 1) + + q.clear() + assert_equal(q._head, 0) + assert_equal(q._tail, 0) + assert_equal(q._capacity, q._min_capacity) + + +fn test_impl_add() raises: + l1 = List(1, 2, 3, 4, 5, 6, 7, 8) + l2 = List(9, 10, 11, 12, 13, 14, 15, 16) + q1 = Deque(elements=l1, capacity=20, maxlen=30) + q2 = Deque(elements=l2, min_capacity=200, shrink=False) + + assert_equal(q1._capacity, 32) + assert_equal(q1._maxlen, 30) + assert_equal(q2._capacity, 64) + assert_equal(q2._min_capacity, 256) + + q3 = q1 + q2 + # has to inherit q1 properties + assert_equal(q3._capacity, 32) + assert_equal(q3._min_capacity, 64) + assert_equal(q3._maxlen, 30) + assert_equal(q3._shrink, True) + assert_equal(q3._head, 0) + assert_equal(q3._tail, 16) + for i in range(len(q3)): + assert_equal((q3._data + i)[], 1 + i) + + q4 = q2 + q1 + # has to inherit q2 properties + assert_equal(q4._capacity, 64) + assert_equal(q4._min_capacity, 256) + assert_equal(q4._maxlen, -1) + assert_equal(q4._shrink, False) + assert_equal(q4._head, 0) + assert_equal(q4._tail, 16) + mid_len = len(q4) // 2 + for i in range(mid_len): + assert_equal((q4._data + i)[], 9 + i) + for i in range(mid_len, len(q4)): + assert_equal((q4._data + i)[], i - 7) + + q5 = q3 + q4 + # has to inherit q3 properties + assert_equal(q5._capacity, 32) + assert_equal(q5._min_capacity, 64) + assert_equal(q5._maxlen, 30) + assert_equal(q5._shrink, True) + # has to obey to maxlen + assert_equal(len(q5), 30) + assert_equal(q5._head, 2) + assert_equal(q5._tail, 0) + assert_equal((q5._data + 2)[], 3) + assert_equal((q5._data + 31)[], 8) + + q6 = q4 + q3 + # has to inherit q4 properties + assert_equal(q6._capacity, 64) + assert_equal(q6._min_capacity, 256) + assert_equal(q6._maxlen, -1) + assert_equal(q6._shrink, False) + # has to obey to maxlen + assert_equal(len(q6), 32) + assert_equal(q6._head, 0) + assert_equal(q6._tail, 32) + assert_equal((q6._data + 0)[], 9) + assert_equal((q6._data + 31)[], 16) + + +fn test_impl_iadd() raises: + l1 = List(1, 2, 3, 4, 5, 6, 7, 8) + l2 = List(9, 10, 11, 12, 13, 14, 15, 16) + q1 = Deque(elements=l1, maxlen=10) + q2 = Deque(elements=l2, min_capacity=200, shrink=False) + + q1 += q2 + # has to keep q1 properties + assert_equal(q1._capacity, 16) + assert_equal(q1._min_capacity, 64) + assert_equal(q1._maxlen, 10) + assert_equal(q1._shrink, True) + # has to obey maxlen + assert_equal(len(q1), 10) + assert_equal(q1._head, 6) + assert_equal(q1._tail, 0) + for i in range(len(q1)): + assert_equal(q1[i], 7 + i) + + q2 += q1 + # has to keep q2 properties + assert_equal(q2._capacity, 64) + assert_equal(q2._min_capacity, 256) + assert_equal(q2._maxlen, -1) + assert_equal(q2._shrink, False) + assert_equal(len(q2), 18) + assert_equal(q2._head, 0) + assert_equal(q2._tail, 18) + assert_equal((q2._data + 0)[], 9) + assert_equal((q2._data + 17)[], 16) + + +fn test_impl_mul() raises: + l = List(1, 2, 3) + q = Deque(elements=l, capacity=3, min_capacity=2, maxlen=7, shrink=False) + + q1 = q * 0 + assert_equal(q1._head, 0) + assert_equal(q1._tail, 0) + assert_equal(q1._capacity, q._min_capacity) + assert_equal(q1._min_capacity, q._min_capacity) + assert_equal(q1._maxlen, q._maxlen) + assert_equal(q1._shrink, q._shrink) + + q2 = q * 1 + assert_equal(q2._head, 0) + assert_equal(q2._tail, len(q)) + assert_equal(q2._capacity, q._capacity) + assert_equal(q2._min_capacity, q._min_capacity) + assert_equal(q2._maxlen, q._maxlen) + assert_equal(q2._shrink, q._shrink) + assert_equal((q2._data + 0)[], (q._data + 0)[]) + assert_equal((q2._data + 1)[], (q._data + 1)[]) + assert_equal((q2._data + 2)[], (q._data + 2)[]) + + q3 = q * 2 + assert_equal(q3._head, 0) + assert_equal(q3._tail, 2 * len(q)) + assert_equal(q3._min_capacity, q._min_capacity) + assert_equal(q3._maxlen, q._maxlen) + assert_equal(q3._shrink, q._shrink) + assert_equal((q3._data + 0)[], (q._data + 0)[]) + assert_equal((q3._data + 5)[], (q._data + 2)[]) + + q4 = q * 3 + # should obey maxlen + assert_equal(q4._head, 2) + assert_equal(q4._tail, 1) + assert_equal(q4._capacity, 8) + assert_equal(q4._min_capacity, q._min_capacity) + assert_equal(q4._maxlen, q._maxlen) + assert_equal(q4._shrink, q._shrink) + assert_equal((q4._data + 2)[], 3) + assert_equal((q4._data + 0)[], 3) + + +fn test_impl_imul() raises: + l = List(1, 2, 3) + + q = Deque(elements=l, capacity=3, min_capacity=2, maxlen=7, shrink=False) + q *= 0 + assert_equal(q._head, 0) + assert_equal(q._tail, 0) + # resets capacity to min_capacity + assert_equal(q._capacity, 2) + assert_equal(q._min_capacity, 2) + assert_equal(q._maxlen, 7) + assert_equal(q._shrink, False) + + q = Deque(elements=l, capacity=3, min_capacity=2, maxlen=7, shrink=False) + q *= 1 + assert_equal(q._head, 0) + assert_equal(q._tail, len(q)) + assert_equal(q._capacity, 4) + assert_equal(q._min_capacity, 2) + assert_equal(q._maxlen, 7) + assert_equal(q._shrink, False) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 2) + assert_equal((q._data + 2)[], 3) + + q = Deque(elements=l, capacity=3, min_capacity=2, maxlen=7, shrink=False) + q *= 2 + assert_equal(q._head, 0) + assert_equal(q._tail, 6) + assert_equal(q._capacity, 8) + assert_equal(q._min_capacity, 2) + assert_equal(q._maxlen, 7) + assert_equal(q._shrink, False) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 5)[], 3) + + q = Deque(elements=l, capacity=3, min_capacity=2, maxlen=7, shrink=False) + q *= 3 + # should obey maxlen + assert_equal(q._head, 2) + assert_equal(q._tail, 1) + assert_equal(q._capacity, 8) + assert_equal(q._min_capacity, 2) + assert_equal(q._maxlen, 7) + assert_equal(q._shrink, False) + assert_equal((q._data + 2)[], 3) + assert_equal((q._data + 0)[], 3) + + # ===----------------------------------------------------------------------===# # API Interface tests # ===----------------------------------------------------------------------===# @@ -355,6 +554,112 @@ fn test_getitem() raises: assert_equal(q[-2], 1) +fn test_setitem() raises: + q = Deque(1, 2) + assert_equal(q[0], 1) + + q[0] = 3 + assert_equal(q[0], 3) + + q[-1] = 4 + assert_equal(q[1], 4) + + +fn test_eq() raises: + q = Deque[Int](1, 2, 3) + p = Deque[Int](1, 2, 3) + + assert_true(q == p) + + r = Deque[Int](0, 1, 2, 3) + q.appendleft(0) + assert_true(q == r) + + +fn test_ne() raises: + q = Deque[Int](1, 2, 3) + p = Deque[Int](3, 2, 1) + + assert_true(q != p) + + q.appendleft(0) + p.append(0) + assert_true(q != p) + + +fn test_contains() raises: + q = Deque[Int](1, 2, 3) + + assert_true(1 in q) + assert_false(4 in q) + + +fn test_iter() raises: + q = Deque(1, 2, 3) + + i = 0 + for e in q: + assert_equal(e[], q[i]) + i += 1 + assert_equal(i, len(q)) + + for e in q: + if e[] == 1: + e[] = 4 + assert_equal(e[], 4) + assert_equal(q[0], 4) + + +fn test_iter_with_list() raises: + q = Deque[List[Int]]() + lst1 = List(1, 2, 3) + lst2 = List(4, 5, 6) + q.append(lst1) + q.append(lst2) + assert_equal(len(q), 2) + + i = 0 + for e in q: + assert_equal(e[], q[i]) + i += 1 + assert_equal(i, len(q)) + + for e in q: + if e[] == lst1: + e[][0] = 7 + assert_equal(e[], List(7, 2, 3)) + assert_equal(q[0], List(7, 2, 3)) + + for e in q: + if e[] == lst2: + e[] = List(1, 2, 3) + assert_equal(e[], List(1, 2, 3)) + assert_equal(q[1], List(1, 2, 3)) + + +fn test_reversed_iter() raises: + q = Deque(1, 2, 3) + + i = 0 + # change to reversed(q) when implemented in builtin for Deque + for e in q.__reversed__(): + i -= 1 + assert_equal(e[], q[i]) + assert_equal(-i, len(q)) + + +fn test_str_and_repr() raises: + q = Deque(1, 2, 3) + + assert_equal(q.__str__(), "Deque(1, 2, 3)") + assert_equal(q.__repr__(), "Deque(1, 2, 3)") + + s = Deque("a", "b", "c") + + assert_equal(s.__str__(), "Deque('a', 'b', 'c')") + assert_equal(s.__repr__(), "Deque('a', 'b', 'c')") + + # ===-------------------------------------------------------------------===# # main # ===-------------------------------------------------------------------===# @@ -374,8 +679,21 @@ def main(): test_impl_append() test_impl_append_with_maxlen() test_impl_extend() + test_impl_clear() + test_impl_add() + test_impl_iadd() + test_impl_mul() + test_impl_imul() test_init_variadic_list() test_copy_trivial() test_copy_list() test_move_list() test_getitem() + test_setitem() + test_eq() + test_ne() + test_contains() + test_iter() + test_iter_with_list() + test_reversed_iter() + test_str_and_repr() From be155752e355c47298ac5be4846beef38c7045fe Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Fri, 8 Nov 2024 18:06:48 -0600 Subject: [PATCH 1847/2019] [External] [stdlib] Addition of a new constructor and reserve method to the String struct (#50557) [External] [stdlib] Addition of a new constructor and reserve method to the String struct Adds a public API for users to allocate capacity through String, instead of having to reach into the private internal `._buffer` attribute. Satisfies: https://github.com/modularml/mojo/issues/3738 Co-authored-by: Mikhail Tavarez Closes modularml/mojo#3755 MODULAR_ORIG_COMMIT_REV_ID: 3d3c3c4fd49acaf3f8f204e453039bacfe57a8ac --- docs/changelog.md | 4 ++++ stdlib/src/collections/string.mojo | 21 +++++++++++++++++++++ stdlib/test/collections/test_string.mojo | 11 +++++++++++ 3 files changed, 36 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index beaa98db82..a4e936999d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -232,6 +232,10 @@ what we publish. - Added `os.path.expandvars` to expand environment variables in a string. ([PR #3735](https://github.com/modularml/mojo/pull/3735) by [@thatstoasty](https://github.com/thatstoasty)). +- Added a `reserve` method and new constructor to the `String` struct to + allocate additional capacity. + ([PR #3755](https://github.com/modularml/mojo/pull/3755) by [@thatstoasty](https://github.com/thatstoasty)). + ### 🦋 Changed - More things have been removed from the auto-exported set of entities in the `prelude` diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index b4e7cbdf0b..b19134a7cc 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -766,6 +766,15 @@ struct String( """Construct an uninitialized string.""" self._buffer = Self._buffer_type() + @always_inline + fn __init__(inout self, *, capacity: Int): + """Construct an uninitialized string with the given capacity. + + Args: + capacity: The capacity of the string. + """ + self._buffer = Self._buffer_type(capacity=capacity) + fn __init__(inout self, *, other: Self): """Explicitly copy the provided value. @@ -2284,6 +2293,18 @@ struct String( var result = String(buffer) return result^ + fn reserve(inout self, new_capacity: Int): + """Reserves the requested capacity. + + Args: + new_capacity: The new capacity. + + Notes: + If the current capacity is greater or equal, this is a no-op. + Otherwise, the storage is reallocated and the data is moved. + """ + self._buffer.reserve(new_capacity) + # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index bd259dd639..63727f080d 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -97,6 +97,10 @@ def test_constructors(): var s3 = String(ptr, 4) assert_equal(s3, "abc") + # Construction with capacity + var s4 = String(capacity=1) + assert_equal(s4._buffer.capacity, 1) + def test_copy(): var s0 = String("find") @@ -1573,6 +1577,13 @@ def test_slice_contains(): ) +def test_reserve(): + var s = String() + assert_equal(s._buffer.capacity, 0) + s.reserve(1) + assert_equal(s._buffer.capacity, 1) + + def main(): test_constructors() test_copy() From 0b19358d8a37943b96b69ac47830c087d9b4a8ca Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 8 Nov 2024 17:42:27 -0800 Subject: [PATCH 1848/2019] [docs] Add changelog entry for float changes Adds an entry to the changelog showing how floats will be printed and converted to strings with shortest representation MODULAR_ORIG_COMMIT_REV_ID: fbdfc69eabdeb45041cd844238f7897433bdd1ff --- docs/changelog.md | 17 +++++++++++++++++ stdlib/src/builtin/io.mojo | 2 -- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a4e936999d..b0e0a3dee4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -408,6 +408,23 @@ what we publish. print(p[i]) ``` + - Float32 and Float64 are now printed and converted to strings with roundtrip + guarantee and shortest representation: + + ```plaintext + Value Old New + Float64(0.3) 0.29999999999999999 0.3 + Float32(0.3) 0.30000001192092896 0.3 + Float64(0.0001) 0.0001 0.0001 + Float32(0.0001) 9.9999997473787516e-05 0.0001 + Float64(-0.00001) -1.0000000000000001e-05 -1e-05 + Float32(-0.00001) -9.9999997473787516e-06 -1e-05 + Float32(0.00001234) 1.2339999557298142e-05 1.234e-05 + Float32(-0.00000123456) -1.2345600453045336e-06 -1.23456e-06 + Float64(1.1234567e-320) 1.1235052786429946e-320 1.1235e-320 + Float64(1.234 * 10**16) 12340000000000000.0 1.234e+16 + ``` + ### ❌ Removed ### 🛠️ Fixed diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index ac2fb202ba..696730e94d 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -254,8 +254,6 @@ fn print[ flush: If set to true, then the stream is forcibly flushed. file: The output stream. """ - # TODO: Implement a float formatter for GPU to enable buffering to global - # memory. PTX isn't able to call snprintf to format floats. write_buffered[buffer_size=4096](file, values, sep=sep, end=end) @parameter From 3057347e880f4b47d27cd4db3a6ee29e55cbdbf2 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 8 Nov 2024 22:02:31 -0600 Subject: [PATCH 1849/2019] [External] [stdlib] [NFC] Rename `String` `UnsafePointer` constructor args (#50486) [External] [stdlib] [NFC] Rename `String` `UnsafePointer` constructor args Rename `String` `UnsafePointer` constructor args. Part of https://github.com/modularml/mojo/issues/3704. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3741 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3741 MODULAR_ORIG_COMMIT_REV_ID: 7c043cc298711e9d981a61a80693ca4b7278f4c0 --- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/collections/string.mojo | 12 +++++------- stdlib/src/sys/info.mojo | 2 +- stdlib/test/collections/test_string.mojo | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 15afa6606a..f725e05bd0 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -201,7 +201,7 @@ struct FileHandle: if err_msg: raise err_msg^.consume_as_error() - return String(buf, int(size_copy) + 1) + return String(ptr=buf, length=int(size_copy) + 1) fn read[ type: DType diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index b19134a7cc..1d82303fbe 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -146,7 +146,7 @@ fn chr(c: Int) -> String: # p.free() # return chr(0xFFFD) p[num_bytes] = 0 - return String(ptr=p, len=num_bytes + 1) + return String(ptr=p, length=num_bytes + 1) # ===----------------------------------------------------------------------=== # @@ -828,7 +828,7 @@ struct String( self = literal.__str__() @always_inline - fn __init__(inout self, ptr: UnsafePointer[UInt8], len: Int): + fn __init__(inout self, *, ptr: UnsafePointer[Byte], length: Int): """Creates a string from the buffer. Note that the string now owns the buffer. @@ -836,14 +836,12 @@ struct String( Args: ptr: The pointer to the buffer. - len: The length of the buffer, including the null terminator. + length: The length of the buffer, including the null terminator. """ # we don't know the capacity of ptr, but we'll assume it's the same or # larger than len self = Self( - Self._buffer_type( - unsafe_pointer=ptr.bitcast[UInt8](), size=len, capacity=len - ) + Self._buffer_type(unsafe_pointer=ptr, size=length, capacity=length) ) # ===------------------------------------------------------------------=== # @@ -966,7 +964,7 @@ struct String( buff: The buffer. This should have an existing terminator. """ - return String(buff, len(StringRef(ptr=buff)) + 1) + return String(ptr=buff, length=len(StringRef(ptr=buff)) + 1) @staticmethod fn _from_bytes(owned buff: Self._buffer_type) -> String: diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 90af173dbe..339c695778 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -832,7 +832,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: if err: raise "Unable to query macOS version" - var osver = String(buf.steal_data(), buf_len) + var osver = String(ptr=buf.steal_data(), length=buf_len) var major = 0 var minor = 0 diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 63727f080d..dc925e41f2 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -94,7 +94,7 @@ def test_constructors(): ptr[1] = ord("b") ptr[2] = ord("c") ptr[3] = 0 - var s3 = String(ptr, 4) + var s3 = String(ptr=ptr, length=4) assert_equal(s3, "abc") # Construction with capacity From ea6fa59165cf0590faf0e4bba26a3222c973b0d5 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 8 Nov 2024 22:32:53 -0600 Subject: [PATCH 1850/2019] [External] [stdlib] [NFC] Rename `Span` `UnsafePointer` constructor args (#50488) [External] [stdlib] [NFC] Rename `Span` `UnsafePointer` constructor args Rename `Span` `UnsafePointer` constructor args. Part of https://github.com/modularml/mojo/issues/3704. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3743 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3743 MODULAR_ORIG_COMMIT_REV_ID: 4845b40b59eaa55827b4ee42240b7f9361d4a608 --- stdlib/src/builtin/sort.mojo | 12 +++++------- stdlib/src/builtin/string_literal.mojo | 6 ++---- stdlib/src/collections/string.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 8 ++------ stdlib/src/utils/span.mojo | 15 +++++++-------- stdlib/src/utils/string_slice.mojo | 10 +++++----- stdlib/src/utils/stringref.mojo | 6 ++---- stdlib/src/utils/write.mojo | 4 ++-- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 2 +- 10 files changed, 28 insertions(+), 39 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 310768304d..df93331427 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -222,7 +222,7 @@ fn _quicksort[ var imm_interval = stack.pop() var ptr = imm_interval.unsafe_ptr() var len = len(imm_interval) - var interval = Span[type, origin](unsafe_ptr=ptr, len=len) + var interval = Span[type, origin](ptr=ptr, length=len) if len <= 5: _delegate_small_sort[cmp_fn](interval) @@ -242,19 +242,17 @@ fn _quicksort[ var pivot = _quicksort_partition_left[cmp_fn](interval) if len > pivot + 2: stack.append( - ImmSpan(unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1) + ImmSpan(ptr=ptr + pivot + 1, length=len - pivot - 1) ) continue var pivot = _quicksort_partition_right[cmp_fn](interval) if len > pivot + 2: - stack.append( - ImmSpan(unsafe_ptr=ptr + pivot + 1, len=len - pivot - 1) - ) + stack.append(ImmSpan(ptr=ptr + pivot + 1, length=len - pivot - 1)) if pivot > 1: - stack.append(ImmSpan(unsafe_ptr=ptr, len=pivot)) + stack.append(ImmSpan(ptr=ptr, length=pivot)) # ===----------------------------------------------------------------------===# @@ -357,7 +355,7 @@ fn _stable_sort[ ](span: Span[type, origin]): var temp_buff = UnsafePointer[type].alloc(len(span)) var temp_buff_span = Span[type, __origin_of(temp_buff)]( - unsafe_ptr=temp_buff, len=len(span) + ptr=temp_buff, length=len(span) ) _stable_sort_impl[cmp_fn](span, temp_buff_span) temp_buff.free() diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 9e95ed7b90..7f358cdd6c 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -464,8 +464,7 @@ struct StringLiteral( """ return Span[Byte, StaticConstantOrigin]( - unsafe_ptr=self.unsafe_ptr(), - len=self.byte_length(), + ptr=self.unsafe_ptr(), length=self.byte_length() ) @always_inline @@ -480,8 +479,7 @@ struct StringLiteral( """ # Does NOT include the NUL terminator. return Span[Byte, __origin_of(self)]( - unsafe_ptr=self.unsafe_ptr(), - len=self.byte_length(), + ptr=self.unsafe_ptr(), length=self.byte_length() ) @always_inline diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 1d82303fbe..2a266537c8 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1540,7 +1540,7 @@ struct String( # Does NOT include the NUL terminator. return Span[Byte, __origin_of(self)]( - unsafe_ptr=self._buffer.unsafe_ptr(), len=self.byte_length() + ptr=self._buffer.unsafe_ptr(), length=self.byte_length() ) @always_inline diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 3552122489..42d04f19d4 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -292,9 +292,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): """ return Span[Byte, __origin_of(self)]( - unsafe_ptr=self.unsafe_ptr(), - # Does NOT include the NUL terminator. - len=len(self), + ptr=self.unsafe_ptr(), length=len(self) ) @@ -532,7 +530,5 @@ struct _FixedString[CAP: Int]( """ return Span[Byte, __origin_of(self)]( - unsafe_ptr=self.unsafe_ptr(), - # Does NOT include the NUL terminator. - len=self.size, + ptr=self.unsafe_ptr(), length=self.size ) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index af417ee09f..4b95ac54c2 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -114,15 +114,15 @@ struct Span[ # ===------------------------------------------------------------------===# @always_inline - fn __init__(inout self, *, unsafe_ptr: UnsafePointer[T], len: Int): + fn __init__(inout self, *, ptr: UnsafePointer[T], length: Int): """Unsafe construction from a pointer and length. Args: - unsafe_ptr: The underlying pointer of the span. - len: The length of the view. + ptr: The underlying pointer of the span. + length: The length of the view. """ - self._data = unsafe_ptr - self._len = len + self._data = ptr + self._len = length @always_inline fn __init__(inout self, *, other: Self): @@ -202,8 +202,7 @@ struct Span[ step == 1, "Slice must be within bounds and step must be 1" ) var res = Self( - unsafe_ptr=(self._data + start), - len=len(range(start, end, step)), + ptr=(self._data + start), length=len(range(start, end, step)) ) return res @@ -351,5 +350,5 @@ struct Span[ A span covering the same elements, but without mutability. """ return Span[T, _lit_mut_cast[origin, False].result]( - unsafe_ptr=self._data, len=self._len + ptr=self._data, length=self._len ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index bab95a8547..50de4ee5ea 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -194,7 +194,7 @@ struct _StringSliceIter[ self.ptr = unsafe_pointer self.length = length alias S = Span[Byte, StaticConstantOrigin] - var s = S(unsafe_ptr=self.ptr, len=self.length) + var s = S(ptr=self.ptr, length=self.length) self.continuation_bytes = _count_utf8_continuation_bytes(s) fn __iter__(self) -> Self: @@ -316,8 +316,8 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( var strref = unsafe_from_utf8_strref var byte_slice = Span[Byte, origin]( - unsafe_ptr=strref.unsafe_ptr(), - len=len(strref), + ptr=strref.unsafe_ptr(), + length=len(strref), ) self = Self(unsafe_from_utf8=byte_slice) @@ -337,7 +337,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( - `ptr` must point to data that is live for the duration of `origin`. """ - self._slice = Span[Byte, origin](unsafe_ptr=ptr, len=length) + self._slice = Span[Byte, origin](ptr=ptr, length=length) @always_inline fn __init__(inout self, *, other: Self): @@ -386,7 +386,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """ var b_len = self.byte_length() alias S = Span[Byte, StaticConstantOrigin] - var s = S(unsafe_ptr=self.unsafe_ptr(), len=b_len) + var s = S(ptr=self.unsafe_ptr(), length=b_len) return b_len - _count_utf8_continuation_bytes(s) fn write_to[W: Writer](self, inout writer: W): diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 89c0bb2711..4d9e427580 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -218,9 +218,7 @@ struct StringRef( Returns: A contiguous slice pointing to the bytes owned by this string. """ - return Span[Byte, __origin_of(self)]( - unsafe_ptr=self.data, len=self.length - ) + return Span[Byte, __origin_of(self)](ptr=self.data, length=self.length) # ===-------------------------------------------------------------------===# # Operator dunders @@ -413,7 +411,7 @@ struct StringRef( # Safe because our use of this Span does not outlive `self`. writer.write_bytes( Span[Byte, ImmutableAnyOrigin]( - unsafe_ptr=self.unsafe_ptr(), len=len(self) + ptr=self.unsafe_ptr(), length=len(self) ) ) diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index fd2c8e4443..5329ab296e 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -238,7 +238,7 @@ struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): fn flush(inout self): self.writer.write_bytes( - Span[Byte, ImmutableAnyOrigin](unsafe_ptr=self.data, len=self.pos) + Span[Byte, ImmutableAnyOrigin](ptr=self.data, length=self.pos) ) self.pos = 0 @@ -283,7 +283,7 @@ struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): fn flush(inout self): self.writer.write_bytes( Span[Byte, ImmutableAnyOrigin]( - unsafe_ptr=self.data.unsafe_ptr(), len=self.pos + ptr=self.data.unsafe_ptr(), length=self.pos ) ) self.pos = 0 diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index c35c575680..855b0a8036 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -21,7 +21,7 @@ from utils import Span fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: var p = UnsafePointer[SIMD[D, 1]].alloc(size) rand[D](p, size) - sort(Span[Scalar[D], MutableAnyOrigin](unsafe_ptr=p, len=size)) + sort(Span[Scalar[D], MutableAnyOrigin](ptr=p, length=size)) for i in range(1, size - 1): if p[i] < p[i - 1]: print(name, "size:", size, "max:", max, "incorrect sort") diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index eb03a1eaf3..19efdc1e0e 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -397,7 +397,7 @@ def test_count_utf8_continuation_bytes(): def _test(amnt: Int, items: List[UInt8]): p = items.unsafe_ptr() - span = Span[Byte, StaticConstantOrigin](unsafe_ptr=p, len=len(items)) + span = Span[Byte, StaticConstantOrigin](ptr=p, length=len(items)) assert_equal(amnt, _count_utf8_continuation_bytes(span)) _test(5, List[UInt8](c, c, c, c, c)) From 18d584a8fc3eeec01970654fa15673606d9093c5 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 8 Nov 2024 22:39:24 -0600 Subject: [PATCH 1851/2019] [External] [stdlib] [NFC] Rename `List` `UnsafePointer` constructor args (#50487) [External] [stdlib] [NFC] Rename `List` `UnsafePointer` constructor args Rename `List` `UnsafePointer` constructor args. Part of https://github.com/modularml/mojo/issues/3704. ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3742 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3742 MODULAR_ORIG_COMMIT_REV_ID: f4e85d1d2a78c7cc81860b9757897bd5cf230f06 --- stdlib/benchmarks/builtin/bench_sort.mojo | 32 +++++++---------------- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/collections/list.mojo | 16 +++++------- stdlib/src/collections/string.mojo | 6 ++--- stdlib/src/utils/string_slice.mojo | 4 +-- stdlib/test/collections/test_list.mojo | 4 +-- 6 files changed, 22 insertions(+), 42 deletions(-) diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index a2a5ebc4cf..57bfcc9e5b 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -92,9 +92,7 @@ fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: fn bench_sort_list(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -113,9 +111,7 @@ fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: fn bench_small_sort(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -134,9 +130,7 @@ fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: fn bench_insertion_sort(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -172,9 +166,7 @@ fn bench_small_list_sort[type: DType](inout m: Bench, count: Int) raises: fn bench_sort_list(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -193,9 +185,7 @@ fn bench_small_list_sort[type: DType](inout m: Bench, count: Int) raises: fn bench_insertion_sort(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -228,9 +218,7 @@ fn bench_large_list_sort[type: DType](inout m: Bench, count: Int) raises: fn bench_sort_list(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -249,9 +237,7 @@ fn bench_large_list_sort[type: DType](inout m: Bench, count: Int) raises: fn bench_heap_sort(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) - var list = List[Scalar[type]]( - unsafe_pointer=ptr, size=count, capacity=count - ) + var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -287,7 +273,7 @@ fn bench_low_cardinality_list_sort( fn bench_sort_list(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[UInt8].alloc(count) - var list = List[UInt8](unsafe_pointer=ptr, size=count, capacity=count) + var list = List[UInt8](ptr=ptr, length=count, capacity=count) @always_inline @parameter @@ -306,7 +292,7 @@ fn bench_low_cardinality_list_sort( fn bench_heap_sort(inout b: Bencher) raises: seed(1) var ptr = UnsafePointer[UInt8].alloc(count) - var list = List[UInt8](unsafe_pointer=ptr, size=count, capacity=count) + var list = List[UInt8](ptr=ptr, length=count, capacity=count) @always_inline @parameter diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index f725e05bd0..6fd4aac2c8 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -345,7 +345,7 @@ struct FileHandle: raise (err_msg^).consume_as_error() var list = List[UInt8]( - unsafe_pointer=buf, size=int(size_copy), capacity=int(size_copy) + ptr=buf, length=int(size_copy), capacity=int(size_copy) ) return list diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b849766a9e..6fa162fd2e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -175,21 +175,17 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.append(value[]) fn __init__( - inout self, - *, - unsafe_pointer: UnsafePointer[T], - size: Int, - capacity: Int, + inout self, *, ptr: UnsafePointer[T], length: Int, capacity: Int ): - """Constructs a list from a pointer, its size, and its capacity. + """Constructs a list from a pointer, its length, and its capacity. Args: - unsafe_pointer: The pointer to the data. - size: The number of elements in the list. + ptr: The pointer to the data. + length: The number of elements in the list. capacity: The capacity of the list. """ - self.data = unsafe_pointer - self.size = size + self.data = ptr + self.size = length self.capacity = capacity fn __moveinit__(inout self, owned existing: Self): diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 2a266537c8..0cf86f7a00 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -758,7 +758,7 @@ struct String( var size = impl.size var capacity = impl.capacity self._buffer = Self._buffer_type( - unsafe_pointer=impl.steal_data(), size=size, capacity=capacity + ptr=impl.steal_data(), length=size, capacity=capacity ) @always_inline @@ -840,9 +840,7 @@ struct String( """ # we don't know the capacity of ptr, but we'll assume it's the same or # larger than len - self = Self( - Self._buffer_type(unsafe_pointer=ptr, size=length, capacity=length) - ) + self = Self(Self._buffer_type(ptr=ptr, length=length, capacity=length)) # ===------------------------------------------------------------------=== # # Factory dunders diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 50de4ee5ea..1c270d6983 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -1059,9 +1059,9 @@ fn _to_string_list[ og_ptr = unsafe_ptr_fn(i_ptr[i]) memcpy(p, og_ptr, og_len) p[og_len] = 0 # null terminator - buf = String._buffer_type(unsafe_pointer=p, size=f_len, capacity=f_len) + buf = String._buffer_type(ptr=p, length=f_len, capacity=f_len) (out_ptr + i).init_pointee_move(String(buf^)) - return List[String](unsafe_pointer=out_ptr, size=i_len, capacity=i_len) + return List[String](ptr=out_ptr, length=i_len, capacity=i_len) @always_inline diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 96b88b8a83..35365af488 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -716,7 +716,7 @@ def test_constructor_from_pointer(): new_pointer[2] = 2 # rest is not initialized - var some_list = List[Int8](unsafe_pointer=new_pointer, size=3, capacity=5) + var some_list = List[Int8](ptr=new_pointer, length=3, capacity=5) assert_equal(some_list[0], 0) assert_equal(some_list[1], 1) assert_equal(some_list[2], 2) @@ -731,7 +731,7 @@ def test_constructor_from_other_list_through_pointer(): var size = len(initial_list) var capacity = initial_list.capacity var some_list = List[Int8]( - unsafe_pointer=initial_list.steal_data(), size=size, capacity=capacity + ptr=initial_list.steal_data(), length=size, capacity=capacity ) assert_equal(some_list[0], 0) assert_equal(some_list[1], 1) From 821b39f4997fa735db7d3d869c6d17a7da4e5a68 Mon Sep 17 00:00:00 2001 From: Hengjie Wang <86926839+hengjiew@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:37:04 -0500 Subject: [PATCH 1852/2019] [stdlib] Disable the `mov` cast of bf16 to fp32. The cast will be performed with `cvt.f32.bf16` by default. MODULAR_ORIG_COMMIT_REV_ID: c4ff54d679480f0efacb0205391b0c01f520a1fc --- stdlib/src/builtin/simd.mojo | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index db493c1e58..af019b3695 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1627,12 +1627,6 @@ struct SIMD[type: DType, size: Int]( if target == DType.bool: return rebind[SIMD[target, size]](self != 0) - @parameter - if type is DType.bfloat16 and target is DType.float32: - return rebind[SIMD[target, size]]( - _bfloat16_to_f32(rebind[SIMD[DType.bfloat16, size]](self)) - ) - @parameter if type is DType.float32 and target == DType.bfloat16: return rebind[SIMD[target, size]]( From dac2b3e950a75045832142528d40301ea009bbb9 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Sat, 9 Nov 2024 13:35:07 -0600 Subject: [PATCH 1853/2019] [External] [stdlib] Span slicing with negative step (#50334) [External] [stdlib] Span slicing with negative step Fixes https://github.com/modularml/mojo/issues/3649 ```mojo 1> s = String("01234") (String) s = "01234" 2> for i in s.as_bytes_span()[::-1]: 3. print(chr(int(i[]))) 4. 4 3 2 1 0 5> ``` Co-authored-by: Manuel Saelices Closes modularml/mojo#3650 MODULAR_ORIG_COMMIT_REV_ID: 1a2158df6c73222e68493c161f8cbab8c5c4460d --- stdlib/src/utils/span.mojo | 15 ++++++++++++--- stdlib/test/utils/test_span.mojo | 14 ++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 4b95ac54c2..e413dc4e42 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -198,9 +198,18 @@ struct Span[ var end: Int var step: Int start, end, step = slc.indices(len(self)) - debug_assert( - step == 1, "Slice must be within bounds and step must be 1" - ) + + if step < 0: + step = -step + var new_len = (start - end + step - 1) // step + var buff = UnsafePointer[T].alloc(new_len) + i = 0 + while start > end: + buff[i] = self._data[start] + start -= step + i += 1 + return Span[T, origin](ptr=buff, length=new_len) + var res = Self( ptr=(self._data + start), length=len(range(start, end, step)) ) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 1e3eb04b5c..b963ad048f 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -137,10 +137,16 @@ def test_span_slice(): assert_equal(res[0], 2) assert_equal(res[1], 3) assert_equal(res[2], 4) - # TODO: Fix Span slicing - # res = s[1::-1] - # assert_equal(res[0], 2) - # assert_equal(res[1], 1) + # Test slicing with negative step + res = s[1::-1] + assert_equal(res[0], 2) + assert_equal(res[1], 1) + res = s[2:1:-1] + assert_equal(res[0], 3) + assert_equal(len(res), 1) + res = s[5:1:-2] + assert_equal(res[0], 5) + assert_equal(res[1], 3) def test_copy_from(): From 9bf287ac111bc220bf002f10544a0e6a24c69129 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 9 Nov 2024 17:23:48 -0800 Subject: [PATCH 1854/2019] [mojo-lang][QoI] Fix error message with inout argument. (#50635) This fixes MOCO-56 and Fixes https://github.com/modularml/mojo/issues/1632 Basically, don't fail parameter inference just because of a mutability problem. Allow it to succeed and fail later with mutability check. MODULAR_ORIG_COMMIT_REV_ID: ea5f73cffba0453bfa9b59ee00a499321a7563ff --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b0e0a3dee4..dd57f3ecfd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -432,6 +432,9 @@ what we publish. - Lifetime tracking is now fully field sensitive, which makes the uninitialized variable checker more precise. +- [Issue #1632](https://github.com/modularml/mojo/issues/1632) - Mojo produces + weird error when inout function is used in non mutating function + - [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init causing use of uninitialized variable From 78042da1cd0b394756b7e3f670cf3890026d5bbb Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 10 Nov 2024 08:16:18 -0800 Subject: [PATCH 1855/2019] [Stdlib] Fixup the casts to f16 types MODULAR_ORIG_COMMIT_REV_ID: f3b0a97f0fbc5cdf14bca0233453504338d4b6ef --- stdlib/src/builtin/simd.mojo | 12 +++++++++--- stdlib/test/builtin/test_simd.mojo | 12 ++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index af019b3695..fb4fc32fca 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1615,7 +1615,7 @@ struct SIMD[type: DType, size: Int]( return self.cast[DType.float32]().cast[target]() @parameter - if has_neon() and (type is DType.bfloat16 or target == DType.bfloat16): + if has_neon() and (type is DType.bfloat16 or target is DType.bfloat16): # TODO(KERN-228): support BF16 on neon systems. return _unchecked_zero[target, size]() @@ -1624,11 +1624,17 @@ struct SIMD[type: DType, size: Int]( return self.select(SIMD[target, size](1), SIMD[target, size](0)) @parameter - if target == DType.bool: + if target is DType.bool: return rebind[SIMD[target, size]](self != 0) @parameter - if type is DType.float32 and target == DType.bfloat16: + if type is DType.bfloat16 and not _has_native_bf16_support(): + return _bfloat16_to_f32( + rebind[SIMD[DType.bfloat16, size]](self) + ).cast[target]() + + @parameter + if target is DType.bfloat16 and not _has_native_bf16_support(): return rebind[SIMD[target, size]]( _f32_to_bfloat16(self.cast[DType.float32]()) ) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 53851a5f76..8b8698c9e7 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -55,6 +55,18 @@ def test_cast(): assert_equal(int(b.cast[DType.int8]()), -128) assert_equal(int(b.cast[DType.int16]()), 128) + @parameter + if not has_neon(): + assert_equal( + BFloat16(33.0).cast[DType.float32]().cast[DType.bfloat16](), 33 + ) + assert_equal( + Float16(33.0).cast[DType.float32]().cast[DType.float16](), 33 + ) + assert_equal( + Float64(33.0).cast[DType.float32]().cast[DType.float16](), 33 + ) + def test_simd_variadic(): assert_equal(str(SIMD[DType.index, 4](52, 12, 43, 5)), "[52, 12, 43, 5]") From 4755b2653db2de6d870bd1893bb8866644dc0bb9 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 10 Nov 2024 09:19:47 -0800 Subject: [PATCH 1856/2019] Now that we have the right information, we can suppress default parameter values in type names. Before: ``` invalid call to 'example': argument #0 cannot be converted from 'UnsafePointer[0, UInt, 0, _default_alignment[::AnyType](), MutableAnyOrigin]' to 'UnsafePointer[Int, 0, _default_alignment[::AnyType](), MutableAnyOrigin]' ``` after: ``` invalid call to 'example': argument #0 cannot be converted from 'UnsafePointer[UInt]' to 'UnsafePointer[Int]' ``` MODULAR_ORIG_COMMIT_REV_ID: 92b40fe77fe6918b31ca53848151bd122316eeb6 --- docs/changelog.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index dd57f3ecfd..852fe40158 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -456,3 +456,17 @@ what we publish. - The VS Code extension now allows invoking a mojo formatter from SDK installations that contain white spaces in their path. + +- Error messages that include type names no longer include inferred or defaulted + parameters when they aren't needed. For example, previously Mojo complained + about things like: + + ```plaintext + ... cannot be converted from 'UnsafePointer[UInt, 0, _default_alignment::AnyType](), MutableAnyOrigin]' to 'UnsafePointer[Int, 0, _default_alignment[::AnyType](), MutableAnyOrigin]' + ``` + + it now complains more helpfully that: + + ```plaintext + ... cannot be converted from 'UnsafePointer[UInt]' to 'UnsafePointer[Int]' + ``` From d5a7082173ea85ad1d42c99d151c7a3271de755f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 10 Nov 2024 09:53:23 -0800 Subject: [PATCH 1857/2019] [mojo-lang][CheckLifetimes] Fix AnyOrigin handling in loops. This fixes an oversight with anyorigin handling in the dtor insertion logic due to the iterative nature of loop processing. This fixes https://github.com/modularml/mojo/issues/3710 MODULAR_ORIG_COMMIT_REV_ID: 2c0567d5e7c7605ef174f433182e042e5580374e --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 852fe40158..8847ec6541 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -447,6 +447,9 @@ what we publish. - [Issue #3627](https://github.com/modularml/mojo/issues/3627) - Compiler overlooked exclusivity violation caused by `ref [MutableAnyOrigin] T` +- [Issue #3710](https://github.com/modularml/mojo/issues/3710) - Mojo frees + memory while reference to it is still in use. + - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. From 829c2d888dbdfd16ac76386d5a52596b60e666f3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 10 Nov 2024 22:30:14 -0800 Subject: [PATCH 1858/2019] [mojo-lang] Enable `__init__(out self)` syntax. As discussed in [this public thread](https://github.com/modularml/mojo/issues/3623), the use of `inout self` in initializers is incorrect. Initializers don't actually read the argument, they only write it. Per discussion on that thread, we're moving to 'out' as the keyword used in function declarations, which allows us to spell the function type correctly as well. This keeps support for the old syntax for migration support, but we should eventually remove it. MODULAR_ORIG_COMMIT_REV_ID: 115768e0c6df069f3cf2e4b3d806db1cc2e6d8a2 --- docs/changelog.md | 21 +++++++++++++++++++++ stdlib/src/collections/string.mojo | 16 ++++++++-------- stdlib/src/memory/pointer.mojo | 10 +++++----- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 8847ec6541..935b6f5680 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -238,6 +238,27 @@ what we publish. ### 🦋 Changed +- The argument convention for `__init__` methods has been changed from `inout` + to `out`, reflecting that an `__init__` method initializes its `self` without + reading from it. This also enables spelling the type of an initializer + correctly, which was not supported before: + + ```mojo + struct Foo: + fn __init__(out self): pass + + fn test(): + # This works now + var fnPtr : fn(out x: Foo)->None = Foo.__init__ + + var someFoo : Foo + fnPtr(someFoo) # initializes someFoo. + ``` + + The previous `fn __init__(inout self)` syntax is still supported in this + release of Mojo, but will be removed in the future. Please migrate to the + new syntax. + - More things have been removed from the auto-exported set of entities in the `prelude` module from the Mojo standard library. - `UnsafePointer` has been removed. Please explicitly import it via diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 0cf86f7a00..917b56e99b 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -734,7 +734,7 @@ struct String( # ===------------------------------------------------------------------=== # @always_inline - fn __init__(inout self, owned impl: List[UInt8, *_]): + fn __init__(out self, owned impl: List[UInt8, *_]): """Construct a string from a buffer of bytes. The buffer must be terminated with a null byte: @@ -762,12 +762,12 @@ struct String( ) @always_inline - fn __init__(inout self): + fn __init__(out self): """Construct an uninitialized string.""" self._buffer = Self._buffer_type() @always_inline - fn __init__(inout self, *, capacity: Int): + fn __init__(out self, *, capacity: Int): """Construct an uninitialized string with the given capacity. Args: @@ -775,7 +775,7 @@ struct String( """ self._buffer = Self._buffer_type(capacity=capacity) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly copy the provided value. Args: @@ -783,7 +783,7 @@ struct String( """ self.__copyinit__(other) - fn __init__(inout self, str: StringRef): + fn __init__(out self, str: StringRef): """Construct a string from a StringRef object. Args: @@ -796,7 +796,7 @@ struct String( memcpy(dest=buffer.data, src=str.data, count=length) self = Self(buffer^) - fn __init__(inout self, str_slice: StringSlice): + fn __init__(out self, str_slice: StringSlice): """Construct a string from a string slice. This will allocate a new string that copies the string contents from @@ -819,7 +819,7 @@ struct String( self = Self(buffer^) @always_inline - fn __init__(inout self, literal: StringLiteral): + fn __init__(out self, literal: StringLiteral): """Constructs a String value given a constant string. Args: @@ -828,7 +828,7 @@ struct String( self = literal.__str__() @always_inline - fn __init__(inout self, *, ptr: UnsafePointer[Byte], length: Int): + fn __init__(out self, *, ptr: UnsafePointer[Byte], length: Int): """Creates a string from the buffer. Note that the string now owns the buffer. diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 62529382a1..da1252d8aa 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -44,7 +44,7 @@ struct _GPUAddressSpace(EqualityComparable): """Local address space.""" @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): self._value = value @always_inline("nodebug") @@ -167,7 +167,7 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): """Generic address space.""" @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Initializes the address space from the underlying integral value. Args: @@ -176,7 +176,7 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): self._value = value @always_inline("nodebug") - fn __init__(inout self, value: _GPUAddressSpace): + fn __init__(out self, value: _GPUAddressSpace): """Initializes the address space from the underlying integral value. Args: @@ -326,7 +326,7 @@ struct Pointer[ @doc_private @always_inline("nodebug") - fn __init__(inout self, *, _mlir_value: Self._mlir_type): + fn __init__(out self, *, _mlir_value: Self._mlir_type): """Constructs a Pointer from its MLIR prepresentation. Args: @@ -347,7 +347,7 @@ struct Pointer[ """ return Pointer(_mlir_value=__get_mvalue_as_litref(value)) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Constructs a copy from another Pointer. Note that this does **not** copy the underlying data. From d3fdfae15ecf1fe2134703ba8478829a3c5d9ba7 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 11 Nov 2024 09:21:06 -0800 Subject: [PATCH 1859/2019] [mojo-stdlib][******] Migrate to `__init__(out self)` This adopts the recent changes that allow the use of the `out` argument convention. This argument convention more correctly models the nature of `__init__` which initializes self but never reads from it. This was discussed on this public thread: https://github.com/modularml/mojo/issues/3623 MODULAR_ORIG_COMMIT_REV_ID: d05be1ccb28b254aeccd5807858546aeb1777991 --- docs/manual/basics.ipynb | 2 +- .../manual/decorators/nonmaterializable.ipynb | 4 +- .../manual/decorators/register-passable.ipynb | 4 +- docs/manual/decorators/staticmethod.ipynb | 4 +- docs/manual/decorators/value.ipynb | 6 +- docs/manual/functions.ipynb | 2 +- docs/manual/lifecycle/death.ipynb | 12 ++-- docs/manual/lifecycle/life.ipynb | 44 ++++++------ docs/manual/parameters/index.ipynb | 16 ++--- docs/manual/structs.ipynb | 12 ++-- docs/manual/traits.ipynb | 8 +-- docs/manual/values/lifetimes.ipynb | 6 +- docs/manual/variables.ipynb | 2 +- examples/mandelbrot.mojo | 2 +- examples/matmul.mojo | 4 +- examples/notebooks/BoolMLIR.ipynb | 16 ++--- examples/notebooks/Mandelbrot.ipynb | 2 +- examples/notebooks/Matmul.ipynb | 4 +- examples/notebooks/RayTracing.ipynb | 10 +-- stdlib/src/builtin/_closure.mojo | 4 +- stdlib/src/builtin/_format_float.mojo | 2 +- stdlib/src/builtin/_location.mojo | 2 +- stdlib/src/builtin/_stubs.mojo | 2 +- stdlib/src/builtin/anytype.mojo | 2 +- stdlib/src/builtin/bool.mojo | 10 +-- stdlib/src/builtin/builtin_list.mojo | 20 +++--- stdlib/src/builtin/builtin_slice.mojo | 4 +- stdlib/src/builtin/coroutine.mojo | 4 +- stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/error.mojo | 12 ++-- stdlib/src/builtin/file.mojo | 10 +-- stdlib/src/builtin/file_descriptor.mojo | 4 +- stdlib/src/builtin/float_literal.mojo | 4 +- stdlib/src/builtin/int.mojo | 18 ++--- stdlib/src/builtin/int_literal.mojo | 4 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/math.mojo | 2 +- stdlib/src/builtin/none.mojo | 4 +- stdlib/src/builtin/object.mojo | 72 +++++++++---------- stdlib/src/builtin/range.mojo | 10 +-- stdlib/src/builtin/simd.mojo | 20 +++--- stdlib/src/builtin/sort.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 4 +- stdlib/src/builtin/tuple.mojo | 6 +- stdlib/src/builtin/uint.mojo | 10 +-- stdlib/src/builtin/value.mojo | 22 +++--- stdlib/src/collections/counter.mojo | 12 ++-- stdlib/src/collections/deque.mojo | 6 +- stdlib/src/collections/dict.mojo | 26 +++---- stdlib/src/collections/inline_array.mojo | 12 ++-- stdlib/src/collections/inline_list.mojo | 4 +- stdlib/src/collections/list.mojo | 16 ++--- stdlib/src/collections/optional.mojo | 20 +++--- stdlib/src/collections/set.mojo | 8 +-- stdlib/src/collections/vector.mojo | 6 +- stdlib/src/documentation/documentation.mojo | 2 +- stdlib/src/hashlib/_ahash.mojo | 2 +- stdlib/src/hashlib/_hasher.mojo | 2 +- stdlib/src/memory/arc.mojo | 8 +-- stdlib/src/memory/maybe_uninitialized.mojo | 8 +-- stdlib/src/memory/owned_pointer.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 8 +-- stdlib/src/os/_linux_aarch64.mojo | 2 +- stdlib/src/os/_linux_x86.mojo | 2 +- stdlib/src/os/_macos.mojo | 2 +- stdlib/src/os/atomic.mojo | 2 +- stdlib/src/os/os.mojo | 2 +- stdlib/src/pathlib/path.mojo | 6 +- stdlib/src/python/_cpython.mojo | 20 +++--- stdlib/src/python/python.mojo | 8 +-- stdlib/src/python/python_object.mojo | 38 +++++----- stdlib/src/sys/ffi.mojo | 4 +- stdlib/src/sys/intrinsics.mojo | 8 +-- stdlib/src/tempfile/tempfile.mojo | 2 +- stdlib/src/testing/testing.mojo | 2 +- stdlib/src/time/time.mojo | 4 +- stdlib/src/utils/index.mojo | 18 ++--- stdlib/src/utils/inline_string.mojo | 14 ++-- stdlib/src/utils/lock.mojo | 4 +- stdlib/src/utils/numerics.mojo | 2 +- stdlib/src/utils/span.mojo | 6 +- stdlib/src/utils/static_tuple.mojo | 8 +-- stdlib/src/utils/string_slice.mojo | 12 ++-- stdlib/src/utils/stringref.mojo | 12 ++-- stdlib/src/utils/variant.mojo | 8 +-- stdlib/src/utils/write.mojo | 4 +- stdlib/test/builtin/test_none.mojo | 4 +- stdlib/test/builtin/test_print.mojo | 4 +- stdlib/test/builtin/test_rebind.mojo | 2 +- stdlib/test/builtin/test_slice.mojo | 4 +- stdlib/test/builtin/test_sort.mojo | 4 +- stdlib/test/collections/test_dict.mojo | 2 +- stdlib/test/collections/test_list.mojo | 12 ++-- stdlib/test/hashlib/test_hasher.mojo | 2 +- stdlib/test/memory/test_unsafepointer.mojo | 4 +- stdlib/test/os/path/test_expandvars.mojo | 2 +- stdlib/test/test_utils/types.mojo | 32 ++++----- stdlib/test/utils/test_variant.mojo | 16 ++--- 98 files changed, 419 insertions(+), 419 deletions(-) diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index c75b22d225..05d218396d 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -272,7 +272,7 @@ " var first: Int\n", " var second: Int\n", "\n", - " fn __init__(inout self, first: Int, second: Int):\n", + " fn __init__(out self, first: Int, second: Int):\n", " self.first = first\n", " self.second = second\n", "\n", diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index 494a9880a1..4e88c205ae 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -48,11 +48,11 @@ "struct HasBool:\n", " var x: Bool\n", "\n", - " fn __init__(inout self, x: Bool):\n", + " fn __init__(out self, x: Bool):\n", " self.x = x\n", "\n", " @always_inline(\"nodebug\")\n", - " fn __init__(inout self, nms: NmStruct):\n", + " fn __init__(out self, nms: NmStruct):\n", " self.x = True if (nms.x == 77) else False\n", "\n", "@value\n", diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb index 31c31d8e4d..174418abaa 100644 --- a/docs/manual/decorators/register-passable.ipynb +++ b/docs/manual/decorators/register-passable.ipynb @@ -42,11 +42,11 @@ " var a: Int\n", " var b: Int\n", "\n", - " fn __init__(inout self, one: Int, two: Int):\n", + " fn __init__(out self, one: Int, two: Int):\n", " self.a = one\n", " self.b = two\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " self.a = existing.a\n", " self.b = existing.b\n", "\n", diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb index 84ff19d90d..bcd9a4d2e5 100644 --- a/docs/manual/decorators/staticmethod.ipynb +++ b/docs/manual/decorators/staticmethod.ipynb @@ -38,10 +38,10 @@ "struct MyStruct:\n", " var data: List[UInt8]\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.data = List[UInt8]()\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " self.data = existing.data ^\n", "\n", " @staticmethod\n", diff --git a/docs/manual/decorators/value.ipynb b/docs/manual/decorators/value.ipynb index 807bd962cb..f82a54a21b 100644 --- a/docs/manual/decorators/value.ipynb +++ b/docs/manual/decorators/value.ipynb @@ -58,15 +58,15 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, owned name: String, age: Int):\n", + " fn __init__(out self, owned name: String, age: Int):\n", " self.name = name^\n", " self.age = age\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " self.name = existing.name\n", " self.age = existing.age\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " self.name = existing.name^\n", " self.age = existing.age" ] diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 420d13aa46..6f24f70e0d 100755 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -794,7 +794,7 @@ "source": [ "@value\n", "struct MyString:\n", - " fn __init__(inout self, string: StringLiteral):\n", + " fn __init__(out self, string: StringLiteral):\n", " pass\n", "\n", "fn foo(name: String):\n", diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 74cfb1e86e..9329b7aebb 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -186,7 +186,7 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, name: String, age: Int):\n", + " fn __init__(out self, name: String, age: Int):\n", " self.name = name\n", " self.age = age" ] @@ -216,7 +216,7 @@ " var data: UnsafePointer[Int]\n", " var size: Int\n", "\n", - " fn __init__(inout self, size: Int, val: Int):\n", + " fn __init__(out self, size: Int, val: Int):\n", " self.size = size\n", " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", @@ -272,7 +272,7 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, name: String, age: Int):\n", + " fn __init__(out self, name: String, age: Int):\n", " self.name = name\n", " self.age = age\n", "\n", @@ -398,7 +398,7 @@ "\n", "```mojo\n", "struct TwoStrings:\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " # Initializes a new `self` by consuming the contents of `existing`\n", " fn __del__(owned self):\n", " # Destroys all resources in `self`\n", @@ -448,11 +448,11 @@ " var str1: String\n", " var str2: String\n", "\n", - " fn __init__(inout self, one: String):\n", + " fn __init__(out self, one: String):\n", " self.str1 = one\n", " self.str2 = String(\"bar\")\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " self.str1 = existing.str1\n", " self.str2 = existing.str2\n", "\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index ef262ef54b..6c64745fdc 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -104,7 +104,7 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, name: String, age: Int):\n", + " fn __init__(out self, name: String, age: Int):\n", " self.name = name\n", " self.age = age" ] @@ -181,11 +181,11 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.name = \"\"\n", " self.age = 0\n", "\n", - " fn __init__(inout self, name: String):\n", + " fn __init__(out self, name: String):\n", " self = MyPet()\n", " self.name = name" ] @@ -218,7 +218,7 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, name: String, age: Int, cond: Bool):\n", + " fn __init__(out self, name: String, age: Int, cond: Bool):\n", " self.name = name\n", " if cond:\n", " self.age = age\n", @@ -254,7 +254,7 @@ "\n", "```mojo\n", "struct Target:\n", - " fn __init__(inout self, s: Source): ...\n", + " fn __init__(out self, s: Source): ...\n", "```\n", "\n", "With implicit conversion, the assignment above is essentially identical to:\n", @@ -269,7 +269,7 @@ "\n", "```mojo\n", "struct Target:\n", - " fn __init__(inout self, s: Source, reverse: Bool = False): ...\n", + " fn __init__(out self, s: Source, reverse: Bool = False): ...\n", "```\n", "\n", "Implicit conversion also occurs if the type doesn't declare its own constructor,\n", @@ -293,14 +293,14 @@ "\n", "```mojo\n", "struct A: \n", - " fn __init__(inout self, s: Source): ...\n", + " fn __init__(out self, s: Source): ...\n", "\n", "struct B: \n", - " fn __init__(inout self, s: Source): ...\n", + " fn __init__(out self, s: Source): ...\n", "\n", "struct Target:\n", - " fn __init__(inout self, a: A): ...\n", - " fn __init__(inout self, b: B): ...\n", + " fn __init__(out self, a: A): ...\n", + " fn __init__(out self, b: B): ...\n", "\n", "# Fails\n", "var t = Target(Source())\n", @@ -316,7 +316,7 @@ "```mojo\n", "struct Target:\n", " # does not support implicit conversion\n", - " fn __init__(inout self, *, source: Source): ...\n", + " fn __init__(out self, *, source: Source): ...\n", "\n", "# the constructor must be called with a keyword\n", "var t = Target(source=a)\n", @@ -363,11 +363,11 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, name: String, age: Int):\n", + " fn __init__(out self, name: String, age: Int):\n", " self.name = name\n", " self.age = age\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " self.name = existing.name\n", " self.age = existing.age" ] @@ -433,14 +433,14 @@ " var size: Int\n", " var cap: Int\n", "\n", - " fn __init__(inout self, size: Int, val: Int):\n", + " fn __init__(out self, size: Int, val: Int):\n", " self.size = size\n", " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.cap)\n", " for i in range(self.size):\n", " (self.data + i).init_pointee_copy(val)\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " # Deep-copy the existing value\n", " self.size = existing.size\n", " self.cap = existing.cap\n", @@ -600,14 +600,14 @@ " var cap: Int\n", "\n", "\n", - " fn __init__(inout self, size: Int, val: Int):\n", + " fn __init__(out self, size: Int, val: Int):\n", " self.size = size\n", " self.cap = size * 2\n", " self.data = UnsafePointer[Int].alloc(self.size)\n", " for i in range(self.size):\n", " (self.data + i).init_pointee_copy(val)\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " # Deep-copy the existing value\n", " self.size = existing.size\n", " self.cap = existing.cap\n", @@ -616,7 +616,7 @@ " (self.data + i).init_pointee_copy(existing.data[i])\n", " # The lifetime of `existing` continues unchanged\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " print(\"move\")\n", " # Shallow copy the existing value\n", " self.size = existing.size\n", @@ -771,15 +771,15 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, owned name: String, age: Int):\n", + " fn __init__(out self, owned name: String, age: Int):\n", " self.name = name^\n", " self.age = age\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " self.name = existing.name\n", " self.age = existing.age\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " self.name = existing.name^\n", " self.age = existing.age" ] @@ -809,7 +809,7 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, owned name: String):\n", + " fn __init__(out self, owned name: String):\n", " self.name = name^\n", " self.age = 0\n" ] diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index cbf61f4f7f..358f846ff3 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -233,7 +233,7 @@ " var data: UnsafePointer[ElementType]\n", " var size: Int\n", "\n", - " fn __init__(inout self, *elements: ElementType):\n", + " fn __init__(out self, *elements: ElementType):\n", " self.size = len(elements)\n", " self.data = UnsafePointer[ElementType].alloc(self.size)\n", " for i in range(self.size):\n", @@ -438,7 +438,7 @@ " var value: … # Some low-level MLIR stuff here\n", "\n", " # Create a new SIMD from a number of scalars\n", - " fn __init__(inout self, *elems: SIMD[type, 1]): ...\n", + " fn __init__(out self, *elems: SIMD[type, 1]): ...\n", "\n", " # Fill a SIMD with a duplicated scalar value.\n", " @staticmethod\n", @@ -536,7 +536,7 @@ " var value: Int\n", "\n", " @always_inline(\"nodebug\")\n", - " fn __init__(inout self, _a: Int):\n", + " fn __init__(out self, _a: Int):\n", " self.value = _a\n", "\n", "fn foo[x: MyInt, a: Int]():\n", @@ -569,7 +569,7 @@ "parameter_overloads[1, 2, MyInt(3)]()\n", "\n", "struct MyStruct:\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " pass\n", "\n", " fn foo(inout self):\n", @@ -730,7 +730,7 @@ "struct One[Type: StringableCollectionElement]:\n", " var value: Type\n", "\n", - " fn __init__(inout self, value: Type):\n", + " fn __init__(out self, value: Type):\n", " self.value = value\n", "\n", "def use_one():\n", @@ -768,7 +768,7 @@ " var val1: Type\n", " var val2: Type\n", "\n", - " fn __init__(inout self, one: One[Type], another: One[Type]):\n", + " fn __init__(out self, one: One[Type], another: One[Type]):\n", " self.val1 = one.value\n", " self.val2 = another.value\n", " print(str(self.val1), str(self.val2))\n", @@ -886,7 +886,7 @@ "outputs": [], "source": [ "struct KwParamStruct[greeting: String = \"Hello\", name: String = \"🔥mojo🔥\"]:\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " print(greeting, name)\n", "\n", "fn use_kw_params():\n", @@ -1193,7 +1193,7 @@ " var data: UnsafePointer[T]\n", " var size: Int\n", "\n", - " fn __init__(inout self, size: Int, value: T):\n", + " fn __init__(out self, size: Int, value: T):\n", " self.size = size\n", " self.data = UnsafePointer[T].alloc(self.size)\n", " for i in range(self.size):\n", diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index ba46521ccd..aff69827b1 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -81,7 +81,7 @@ " var first: Int\n", " var second: Int\n", "\n", - " fn __init__(inout self, first: Int, second: Int):\n", + " fn __init__(out self, first: Int, second: Int):\n", " self.first = first\n", " self.second = second" ] @@ -153,7 +153,7 @@ " var first: Int\n", " var second: Int\n", "\n", - " fn __init__(inout self, first: Int, second: Int):\n", + " fn __init__(out self, first: Int, second: Int):\n", " self.first = first\n", " self.second = second\n", "\n", @@ -223,7 +223,7 @@ "source": [ "struct Logger:\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " pass\n", "\n", " @staticmethod\n", @@ -408,15 +408,15 @@ " var name: String\n", " var age: Int\n", "\n", - " fn __init__(inout self, owned name: String, age: Int):\n", + " fn __init__(out self, owned name: String, age: Int):\n", " self.name = name^\n", " self.age = age\n", "\n", - " fn __copyinit__(inout self, existing: Self):\n", + " fn __copyinit__(out self, existing: Self):\n", " self.name = existing.name\n", " self.age = existing.age\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " self.name = existing.name^\n", " self.age = existing.age" ] diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index a0b665410f..ef9fbd824e 100755 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -391,7 +391,7 @@ "outputs": [], "source": [ "trait DefaultConstructible:\n", - " fn __init__(inout self): ...\n", + " fn __init__(out self): ...\n", "\n", "trait MassProducible(DefaultConstructible, Movable):\n", " pass\n", @@ -402,10 +402,10 @@ "struct Thing(MassProducible):\n", " var id: Int\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.id = 0\n", "\n", - " fn __moveinit__(inout self, owned existing: Self):\n", + " fn __moveinit__(out self, owned existing: Self):\n", " self.id = existing.id\n", "\n", "var thing = factory[Thing]()" @@ -504,7 +504,7 @@ " var size: Int\n", " # ...\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.size = 0\n", "\n", " fn __len__(self) -> Int:\n", diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 0c350dac66..314e186c81 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -229,7 +229,7 @@ "struct BoxedString:\n", " var box: OwnedPointer[String]\n", "\n", - " fn __init__(inout self, value: String):\n", + " fn __init__(out self, value: String):\n", " self.box = OwnedPointer(value)\n", "\n", " fn as_ptr(self) -> Pointer[String, __origin_of(self.box)]:\n", @@ -259,7 +259,7 @@ "its `origin` value from the list:\n", "\n", "```mojo\n", - " fn __init__(inout self, ref [origin]list: List[T, *_]):\n", + " fn __init__(out self, ref [origin]list: List[T, *_]):\n", " \"\"\"Construct a Span from a List.\n", "\n", " Args:\n", @@ -422,7 +422,7 @@ "struct NameList:\n", " var names: List[String]\n", "\n", - " def __init__(inout self, *names: String):\n", + " def __init__(out self, *names: String):\n", " self.names = List[String]()\n", " for name in names:\n", " self.names.append(name[])\n", diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index e4f676b0cd..68b87ce68e 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -312,7 +312,7 @@ "As shown above, value assignment can be converted into a constructor call if the \n", "target type has a constructor that takes a single argument that matches the\n", "value being assigned. So, this code uses the `Float64` constructor that takes an\n", - "integer: `__init__(inout self, value: Int)`.\n", + "integer: `__init__(out self, value: Int)`.\n", "\n", "In general, implicit conversions should only be supported where the conversion\n", "is lossless.\n", diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index a2a7d12a43..9603e31c70 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -39,7 +39,7 @@ alias max_y = 1.5 struct Matrix[type: DType, rows: Int, cols: Int]: var data: UnsafePointer[Scalar[type]] - fn __init__(inout self): + fn __init__(out self): self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) fn store[nelts: Int](self, row: Int, col: Int, val: SIMD[type, nelts]): diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 4b1a08e7cd..44cc91f097 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -52,12 +52,12 @@ struct Matrix[rows: Int, cols: Int]: var data: UnsafePointer[Scalar[type]] # Initialize zeroing all values - fn __init__(inout self): + fn __init__(out self): self.data = UnsafePointer[Scalar[type]].alloc(rows * cols) memset_zero(self.data, rows * cols) # Initialize taking a pointer, don't set any elements - fn __init__(inout self, data: UnsafePointer[Scalar[type]]): + fn __init__(out self, data: UnsafePointer[Scalar[type]]): self.data = data ## Initialize with random values diff --git a/examples/notebooks/BoolMLIR.ipynb b/examples/notebooks/BoolMLIR.ipynb index 2e4a6a09b3..355dc77909 100644 --- a/examples/notebooks/BoolMLIR.ipynb +++ b/examples/notebooks/BoolMLIR.ipynb @@ -133,7 +133,7 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.value = __mlir_op.`index.bool.constant`[\n", " value=__mlir_attr.false,\n", " ]()" @@ -191,7 +191,7 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.value = __mlir_op.`index.bool.constant`[\n", " value=__mlir_attr.false,\n", " ]()" @@ -239,7 +239,7 @@ "\n", " # ...\n", "\n", - " fn __init__(inout self, value: __mlir_type.i1):\n", + " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value" ] }, @@ -303,10 +303,10 @@ " var value: __mlir_type.i1\n", "\n", " # We can simplify our no-argument constructor:\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self = OurFalse\n", "\n", - " fn __init__(inout self, value: __mlir_type.i1):\n", + " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value" ] }, @@ -369,7 +369,7 @@ "\n", " # ...\n", "\n", - " fn __init__(inout self, value: __mlir_type.i1):\n", + " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value\n", "\n", " # Our new method converts `OurBool` to `Bool`:\n", @@ -441,7 +441,7 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__(inout self, value: __mlir_type.i1):\n", + " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value\n", "\n", " # Our new method converts `OurBool` to `i1`:\n", @@ -507,7 +507,7 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", - " fn __init__(inout self, value: __mlir_type.i1):\n", + " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value\n", "\n", " # ...\n", diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index ad269046ce..4dcec04c6a 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -100,7 +100,7 @@ "struct Matrix[type: DType, rows: Int, cols: Int]:\n", " var data: UnsafePointer[Scalar[type]]\n", "\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", "\n", " fn __getitem__(self, row: Int, col: Int) -> Scalar[type]:\n", diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index f7a4a796a4..239654bf5e 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -416,12 +416,12 @@ " var data: UnsafePointer[Scalar[type]]\n", "\n", " # Initialize zeroeing all values\n", - " fn __init__(inout self):\n", + " fn __init__(out self):\n", " self.data = UnsafePointer[Scalar[type]].alloc(rows * cols)\n", " memset_zero(self.data, rows * cols)\n", "\n", " # Initialize taking a pointer, don't set any elements\n", - " fn __init__(inout self, data: UnsafePointer[Scalar[type]]):\n", + " fn __init__(out self, data: UnsafePointer[Scalar[type]]):\n", " self.data = data\n", "\n", " # Initialize with random values\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index a6d9d221ba..1ce972b6b4 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -81,11 +81,11 @@ " var data: SIMD[DType.float32, 4]\n", "\n", " @always_inline\n", - " fn __init__(inout self, x: Float32, y: Float32, z: Float32):\n", + " fn __init__(out self, x: Float32, y: Float32, z: Float32):\n", " self.data = SIMD[DType.float32, 4](x, y, z, 0)\n", "\n", " @always_inline\n", - " fn __init__(inout self, data: SIMD[DType.float32, 4]):\n", + " fn __init__(out self, data: SIMD[DType.float32, 4]):\n", " self.data = data\n", "\n", " @always_inline\n", @@ -207,14 +207,14 @@ " var height: Int\n", " var width: Int\n", "\n", - " fn __init__(inout self, height: Int, width: Int):\n", + " fn __init__(out self, height: Int, width: Int):\n", " self.height = height\n", " self.width = width\n", " self.pixels = UnsafePointer[Vec3f].alloc(self.height * self.width)\n", " self.rc = UnsafePointer[Int].alloc(1)\n", " self.rc[] = 1\n", "\n", - " fn __copyinit__(inout self, other: Self):\n", + " fn __copyinit__(out self, other: Self):\n", " other._inc_rc()\n", " self.pixels = other.pixels\n", " self.rc = other.rc\n", @@ -401,7 +401,7 @@ " var albedo: Vec3f\n", " var specular_component: Float32\n", "\n", - " fn __init__(inout self, color: Vec3f, albedo: Vec3f = Vec3f(0, 0, 0),\n", + " fn __init__(out self, color: Vec3f, albedo: Vec3f = Vec3f(0, 0, 0),\n", " specular_component: Float32 = 0):\n", " self.color = color\n", " self.albedo = albedo\n", diff --git a/stdlib/src/builtin/_closure.mojo b/stdlib/src/builtin/_closure.mojo index b456d1405a..c5465bdf09 100644 --- a/stdlib/src/builtin/_closure.mojo +++ b/stdlib/src/builtin/_closure.mojo @@ -21,11 +21,11 @@ struct __ParameterClosureCaptureList[ # Parameter closure invariant requires this function be marked 'capturing'. @parameter @always_inline - fn __init__(inout self): + fn __init__(out self): self.value = __mlir_op.`kgen.capture_list.create`[callee=fn_ref]() @always_inline - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): self.value = __mlir_op.`kgen.capture_list.copy`[callee=fn_ref]( existing.value ) diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index 31a48829d5..2686917119 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -50,7 +50,7 @@ struct _MulParity: var parity: Bool var is_integer: Bool - fn __init__(inout self, parity: Bool, is_integer: Bool): + fn __init__(out self, parity: Bool, is_integer: Bool): self.parity = parity self.is_integer = is_integer diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index a79adfc33d..0940e49f8e 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -23,7 +23,7 @@ struct _SourceLocation(Writable, Stringable): var col: Int var file_name: StringLiteral - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self = other @no_inline diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index bdb830bece..92e661dc00 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -68,7 +68,7 @@ struct _ParamForIterator[IteratorT: Copyable]: var value: Int var stop: Bool - fn __init__(inout self, next_it: IteratorT, value: Int, stop: Bool): + fn __init__(out self, next_it: IteratorT, value: Int, stop: Bool): self.next_it = next_it self.value = value self.stop = stop diff --git a/stdlib/src/builtin/anytype.mojo b/stdlib/src/builtin/anytype.mojo index c54a4e1745..03da7a124d 100644 --- a/stdlib/src/builtin/anytype.mojo +++ b/stdlib/src/builtin/anytype.mojo @@ -46,7 +46,7 @@ trait AnyType: var p: UnsafePointer[Int] var size: Int - fn __init__(inout self, size: Int): + fn __init__(out self, size: Int): self.p = UnsafePointer[Int].alloc(size) self.size = size diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index aebc43a7a8..06ccff6578 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -116,12 +116,12 @@ struct Bool( """The underlying storage of the boolean value.""" @always_inline("nodebug") - fn __init__(inout self): + fn __init__(out self): """Construct a default, `False` Bool.""" self = False @always_inline("nodebug") - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly construct a deep copy of the provided value. Args: @@ -131,7 +131,7 @@ struct Bool( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.i1): + fn __init__(out self, value: __mlir_type.i1): """Construct a Bool value given a __mlir_type.i1 value. Args: @@ -141,7 +141,7 @@ struct Bool( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct a Bool value given a `!pop.scalar` value. Args: @@ -164,7 +164,7 @@ struct Bool( self = value.__bool__() @always_inline("nodebug") - fn __init__(inout self, value: SIMD[DType.bool, 1]): + fn __init__(out self, value: SIMD[DType.bool, 1]): """Convert a scalar SIMD value to a Bool. Args: diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 9ea758583d..7541b4f9c4 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -40,7 +40,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self, owned *args: *Ts): + fn __init__(out self, owned *args: *Ts): """Construct the list literal from the given values. Args: @@ -49,7 +49,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): self.storage = Tuple(storage=args^) @always_inline - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy construct the tuple. Args: @@ -57,7 +57,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): """ self.storage = existing.storage - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Move construct the list. Args: @@ -164,7 +164,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): alias IterType = _VariadicListIter[type] @always_inline - fn __init__(inout self, *value: type): + fn __init__(out self, *value: type): """Constructs a VariadicList from a variadic list of arguments. Args: @@ -175,7 +175,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): @doc_private @always_inline - fn __init__(inout self, value: Self._mlir_type): + fn __init__(out self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -327,7 +327,7 @@ struct VariadicListMem[ # Provide support for borrowed variadic arguments. @doc_private @always_inline - fn __init__(inout self, value: Self._mlir_type): + fn __init__(out self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -344,7 +344,7 @@ struct VariadicListMem[ ] @always_inline - fn __init__(inout self, value: Self._inout_variadic_type): + fn __init__(out self, value: Self._inout_variadic_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -364,7 +364,7 @@ struct VariadicListMem[ ] @always_inline - fn __init__(inout self, value: Self._owned_variadic_type): + fn __init__(out self, value: Self._owned_variadic_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -377,7 +377,7 @@ struct VariadicListMem[ self._is_owned = True @always_inline - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Moves constructor. Args: @@ -509,7 +509,7 @@ struct VariadicPack[ @doc_private @always_inline("nodebug") - fn __init__(inout self, value: Self._mlir_type, is_owned: Bool): + fn __init__(out self, value: Self._mlir_type, is_owned: Bool): """Constructs a VariadicPack from the internal representation. Args: diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 77d7811c01..cc5a6d2f1a 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -53,7 +53,7 @@ struct Slice( # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self, start: Int, end: Int): + fn __init__(out self, start: Int, end: Int): """Construct slice given the start and end values. Args: @@ -82,7 +82,7 @@ struct Slice( self.end = end self.step = step - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Creates a deep copy of the Slice. Args: diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 1246e099fa..4c8517d687 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -120,7 +120,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: ) @always_inline - fn __init__(inout self, handle: AnyCoroutine): + fn __init__(out self, handle: AnyCoroutine): """Construct a coroutine object from a handle. Args: @@ -200,7 +200,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: ) @always_inline - fn __init__(inout self, handle: AnyCoroutine): + fn __init__(out self, handle: AnyCoroutine): """Construct a coroutine object from a handle. Args: diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 26ae8cd003..e0b81dadc7 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -93,7 +93,7 @@ struct DType( on the system.""" @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy this DType. Args: diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index a974d6a8c2..482f4aa454 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -59,13 +59,13 @@ struct Error( # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """Default constructor.""" self.data = UnsafePointer[UInt8]() self.loaded_length = 0 @always_inline - fn __init__(inout self, value: StringLiteral): + fn __init__(out self, value: StringLiteral): """Construct an Error object with a given string literal. Args: @@ -74,7 +74,7 @@ struct Error( self.data = value.unsafe_ptr() self.loaded_length = len(value) - fn __init__(inout self, src: String): + fn __init__(out self, src: String): """Construct an Error object with a given string. Args: @@ -91,7 +91,7 @@ struct Error( self.data = dest self.loaded_length = -length - fn __init__(inout self, src: StringRef): + fn __init__(out self, src: StringRef): """Construct an Error object with a given string ref. Args: @@ -108,7 +108,7 @@ struct Error( self.data = dest self.loaded_length = -length - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -121,7 +121,7 @@ struct Error( if self.loaded_length < 0: self.data.free() - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Creates a deep copy of an existing error. Args: diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 6fd4aac2c8..0803d4f3e0 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -44,7 +44,7 @@ struct _OwnedStringRef(Boolable): var data: UnsafePointer[UInt8] var length: Int - fn __init__(inout self): + fn __init__(out self): self.data = UnsafePointer[UInt8]() self.length = 0 @@ -73,11 +73,11 @@ struct FileHandle: var handle: OpaquePointer """The underlying pointer to the file handle.""" - fn __init__(inout self): + fn __init__(out self): """Default constructor.""" self.handle = OpaquePointer() - fn __init__(inout self, path: String, mode: String) raises: + fn __init__(out self, path: String, mode: String) raises: """Construct the FileHandle using the file path and mode. Args: @@ -86,7 +86,7 @@ struct FileHandle: """ self.__init__(path.as_string_slice(), mode.as_string_slice()) - fn __init__(inout self, path: StringSlice, mode: StringSlice) raises: + fn __init__(out self, path: StringSlice, mode: StringSlice) raises: """Construct the FileHandle using the file path and string. Args: @@ -126,7 +126,7 @@ struct FileHandle: self.handle = OpaquePointer() - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Moves constructor for the file handle. Args: diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 0745273a9a..e4132603bc 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -38,7 +38,7 @@ struct FileDescriptor(Writer): var value: Int """The underlying value of the file descriptor.""" - fn __init__(inout self, value: Int = 1): + fn __init__(out self, value: Int = 1): """Constructs the file descriptor from an integer. Args: @@ -46,7 +46,7 @@ struct FileDescriptor(Writer): """ self.value = value - fn __init__(inout self, f: FileHandle): + fn __init__(out self, f: FileHandle): """Constructs the file descriptor from a file handle. Args: diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index d6a19e9595..c3da23fcbc 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -49,7 +49,7 @@ struct FloatLiteral( # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__(inout self, value: Self.fp_type): + fn __init__(out self, value: Self.fp_type): """Create a FloatLiteral value from a kgen.float_literal value. Args: @@ -58,7 +58,7 @@ struct FloatLiteral( self.value = value @always_inline("nodebug") - fn __init__(inout self, value: IntLiteral): + fn __init__(out self, value: IntLiteral): """Convert an IntLiteral to a FloatLiteral value. Args: diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 9c97d71359..aa6bd7de8b 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -308,11 +308,11 @@ struct Int( # ===------------------------------------------------------------------=== # @always_inline("nodebug") - fn __init__(inout self): + fn __init__(out self): """Default constructor that produces zero.""" self.value = __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly copy the provided value. Args: @@ -322,7 +322,7 @@ struct Int( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.index): + fn __init__(out self, value: __mlir_type.index): """Construct Int from the given index value. Args: @@ -332,7 +332,7 @@ struct Int( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int16 value. Args: @@ -346,7 +346,7 @@ struct Int( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int32 value. Args: @@ -360,7 +360,7 @@ struct Int( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int64 value. Args: @@ -374,7 +374,7 @@ struct Int( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Index value. Args: @@ -385,7 +385,7 @@ struct Int( ) @always_inline("nodebug") - fn __init__(inout self, value: IntLiteral): + fn __init__(out self, value: IntLiteral): """Construct Int from the given IntLiteral value. Args: @@ -406,7 +406,7 @@ struct Int( self = value.__index__() @always_inline("nodebug") - fn __init__(inout self, value: UInt): + fn __init__(out self, value: UInt): """Construct Int from the given UInt value. Args: diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 90b3850ddb..326759e115 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -53,13 +53,13 @@ struct IntLiteral( # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__(inout self): + fn __init__(out self): """Default constructor.""" self.value = __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!kgen.int_literal`): + fn __init__(out self, value: __mlir_type.`!kgen.int_literal`): """Construct IntLiteral from the given mlir !kgen.int_literal value. Args: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 696730e94d..d605324b1c 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -43,7 +43,7 @@ from utils import StringRef, StaticString, StringSlice struct _fdopen[mode: StringLiteral = "a"]: var handle: OpaquePointer - fn __init__(inout self, stream_id: FileDescriptor): + fn __init__(out self, stream_id: FileDescriptor): """Creates a file handle to the stdout/stderr stream. Args: diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 2506699f9d..eb1b088242 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -269,7 +269,7 @@ trait Powable: var numerator: Float64 var denominator: Float64 - fn __init__(inout self, numerator: Float64, denominator: Float64): + fn __init__(out self, numerator: Float64, denominator: Float64): self.numerator = numerator self.denominator = denominator diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo index a5e85d39a8..966525ef95 100644 --- a/stdlib/src/builtin/none.mojo +++ b/stdlib/src/builtin/none.mojo @@ -33,12 +33,12 @@ struct NoneType( var _value: Self._mlir_type @always_inline - fn __init__(inout self): + fn __init__(out self): """Construct an instance of the `None` type.""" self._value = None @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicit copy constructor. Args: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f7587a8ac6..2d0cef2c92 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -32,7 +32,7 @@ from utils import StringRef, Variant struct _NoneMarker(CollectionElementNew): """This is a trivial class to indicate that an object is `None`.""" - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): pass @@ -50,12 +50,12 @@ struct _ImmutableString(CollectionElement, CollectionElementNew): """The length of the string.""" @always_inline - fn __init__(inout self, data: UnsafePointer[UInt8], length: Int): + fn __init__(out self, data: UnsafePointer[UInt8], length: Int): self.data = data self.length = length @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self = other @always_inline @@ -78,7 +78,7 @@ struct _RefCountedList: var impl: Arc[List[_ObjectImpl]] """The list value.""" - fn __init__(inout self): + fn __init__(out self): self.impl = Arc[List[_ObjectImpl]](List[_ObjectImpl]()) @@ -89,13 +89,13 @@ struct _RefCountedListRef(CollectionElement, CollectionElementNew): """The reference to the list.""" @always_inline - fn __init__(inout self): + fn __init__(out self): var ptr = UnsafePointer[_RefCountedList].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = _RefCountedList() self.lst = ptr.bitcast[NoneType]() @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.lst = other.lst @always_inline @@ -118,7 +118,7 @@ struct _RefCountedAttrsDict: var impl: Arc[Dict[StringLiteral, _ObjectImpl]] """The implementation of the map.""" - fn __init__(inout self): + fn __init__(out self): self.impl = Arc[Dict[StringLiteral, _ObjectImpl]]( Dict[StringLiteral, _ObjectImpl]() ) @@ -159,7 +159,7 @@ struct Attr: """The value of the attribute.""" @always_inline - fn __init__(inout self, key: StringLiteral, owned value: object): + fn __init__(out self, key: StringLiteral, owned value: object): """Initializes the attribute with a key and value. Args: @@ -178,7 +178,7 @@ struct _RefCountedAttrsDictRef(CollectionElement, CollectionElementNew): """The reference to the dictionary.""" @always_inline - fn __init__(inout self, values: VariadicListMem[Attr, _]): + fn __init__(out self, values: VariadicListMem[Attr, _]): var ptr = UnsafePointer[_RefCountedAttrsDict].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() # Elements can only be added on construction. @@ -188,7 +188,7 @@ struct _RefCountedAttrsDictRef(CollectionElement, CollectionElementNew): self.attrs = ptr.bitcast[Int8]() @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self = other @always_inline @@ -216,7 +216,7 @@ struct _Function(CollectionElement, CollectionElementNew): self.value = f @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.value = other.value alias fn0 = fn () raises -> object @@ -305,15 +305,15 @@ struct _ObjectImpl( # ===------------------------------------------------------------------=== # @always_inline - fn __init__(inout self, value: Self.type): + fn __init__(out self, value: Self.type): self.value = value @always_inline - fn __init__(inout self): + fn __init__(out self): self.value = Self.type(_NoneMarker {}) @always_inline - fn __init__(inout self, value: Bool): + fn __init__(out self, value: Bool): self.value = Self.type(value) @always_inline @@ -325,23 +325,23 @@ struct _ObjectImpl( self.value = Self.type(value) @always_inline - fn __init__(inout self, value: _ImmutableString): + fn __init__(out self, value: _ImmutableString): self.value = Self.type(value) @always_inline - fn __init__(inout self, value: _RefCountedListRef): + fn __init__(out self, value: _RefCountedListRef): self.value = Self.type(value) @always_inline - fn __init__(inout self, value: _Function): + fn __init__(out self, value: _Function): self.value = Self.type(value) @always_inline - fn __init__(inout self, value: _RefCountedAttrsDictRef): + fn __init__(out self, value: _RefCountedAttrsDictRef): self.value = Self.type(value) @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -350,11 +350,11 @@ struct _ObjectImpl( self = other.value @always_inline - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): self = existing.value @always_inline - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): self = other.value^ @always_inline @@ -720,12 +720,12 @@ struct object( # ===------------------------------------------------------------------=== # @always_inline - fn __init__(inout self): + fn __init__(out self): """Initializes the object with a `None` value.""" self._value = _ObjectImpl() @always_inline - fn __init__(inout self, impl: _ObjectImpl): + fn __init__(out self, impl: _ObjectImpl): """Initializes the object with an implementation value. This is meant for internal use only. @@ -735,7 +735,7 @@ struct object( self._value = impl @always_inline - fn __init__(inout self, none: NoneType): + fn __init__(out self, none: NoneType): """Initializes a none value object from a `None` literal. Args: @@ -744,7 +744,7 @@ struct object( self._value = _ObjectImpl() @always_inline - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Initializes the object with an integer value. Args: @@ -753,7 +753,7 @@ struct object( self._value = Int64(value) @always_inline - fn __init__(inout self, value: Float64): + fn __init__(out self, value: Float64): """Initializes the object with an floating-point value. Args: @@ -781,7 +781,7 @@ struct object( self._value = value @always_inline - fn __init__(inout self, value: Bool): + fn __init__(out self, value: Bool): """Initializes the object from a bool. Args: @@ -790,7 +790,7 @@ struct object( self._value = value @always_inline - fn __init__(inout self, value: StringLiteral): + fn __init__(out self, value: StringLiteral): """Initializes the object from a string literal. Args: @@ -799,7 +799,7 @@ struct object( self = object(StringRef(value)) @always_inline - fn __init__(inout self, value: StringRef): + fn __init__(out self, value: StringRef): """Initializes the object from a string reference. Args: @@ -850,7 +850,7 @@ struct object( ]() @always_inline - fn __init__(inout self, func: Self.nullary_function): + fn __init__(out self, func: Self.nullary_function): """Initializes an object from a function that takes no arguments. Args: @@ -859,7 +859,7 @@ struct object( self._value = _Function(func) @always_inline - fn __init__(inout self, func: Self.unary_function): + fn __init__(out self, func: Self.unary_function): """Initializes an object from a function that takes one argument. Args: @@ -868,7 +868,7 @@ struct object( self._value = _Function(func) @always_inline - fn __init__(inout self, func: Self.binary_function): + fn __init__(out self, func: Self.binary_function): """Initializes an object from a function that takes two arguments. Args: @@ -877,7 +877,7 @@ struct object( self._value = _Function(func) @always_inline - fn __init__(inout self, func: Self.ternary_function): + fn __init__(out self, func: Self.ternary_function): """Initializes an object from a function that takes three arguments. Args: @@ -886,7 +886,7 @@ struct object( self._value = _Function(func) @always_inline - fn __init__(inout self, *attrs: Attr): + fn __init__(out self, *attrs: Attr): """Initializes the object with a sequence of zero or more attributes. Args: @@ -895,7 +895,7 @@ struct object( self._value = _RefCountedAttrsDictRef(attrs) @always_inline - fn __moveinit__(inout self, owned existing: object): + fn __moveinit__(out self, owned existing: object): """Move the value of an object. Args: @@ -905,7 +905,7 @@ struct object( existing._value = _ObjectImpl() @always_inline - fn __copyinit__(inout self, existing: object): + fn __copyinit__(out self, existing: object): """Copies the object. This clones the underlying string value and increases the refcount of lists or dictionaries. diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index ccc01436db..2b9e691e42 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -48,7 +48,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): var end: Int @always_inline - fn __init__(inout self, end: Int): + fn __init__(out self, end: Int): self.curr = max(0, end) self.end = self.curr @@ -149,7 +149,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): var step: Int @always_inline - fn __init__(inout self, start: Int, end: Int): + fn __init__(out self, start: Int, end: Int): self.start = start self.end = end self.step = 1 @@ -324,7 +324,7 @@ struct _UIntZeroStartingRange(UIntSized): var end: UInt @always_inline - fn __init__(inout self, end: UInt): + fn __init__(out self, end: UInt): self.curr = max(0, end) self.end = self.curr @@ -382,7 +382,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): var step: UInt @always_inline - fn __init__(inout self, start: UInt, end: UInt, step: UInt): + fn __init__(out self, start: UInt, end: UInt, step: UInt): self.start = start self.end = end debug_assert( @@ -464,7 +464,7 @@ struct _ZeroStartingScalarRange[type: DType]: var end: Scalar[type] @always_inline - fn __init__(inout self, end: Scalar[type]): + fn __init__(out self, end: Scalar[type]): self.curr = max(0, end) self.end = self.curr diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index fb4fc32fca..3ae36ad548 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -227,7 +227,7 @@ struct SIMD[type: DType, size: Int]( # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__(inout self): + fn __init__(out self): """Default initializer of the SIMD vector. By default the SIMD vectors are initialized to all zeros. @@ -237,7 +237,7 @@ struct SIMD[type: DType, size: Int]( # FIXME(MOCO-1291): Can't implement this due to ambiguity. # @always_inline("nodebug") - # fn __init__(inout self, *, other: SIMD[type, size]): + # fn __init__(out self, *, other: SIMD[type, size]): # """Explicitly copy the provided value. # Args: @@ -246,7 +246,7 @@ struct SIMD[type: DType, size: Int]( # self.__copyinit__(other) @always_inline("nodebug") - fn __init__(inout self, value: UInt): + fn __init__(out self, value: UInt): """Initializes the SIMD vector with an unsigned integer. The unsigned integer value is splatted across all the elements of the SIMD @@ -258,7 +258,7 @@ struct SIMD[type: DType, size: Int]( self = Self(value.value) @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Initializes the SIMD vector with a signed integer. The signed integer value is splatted across all the elements of the SIMD @@ -271,7 +271,7 @@ struct SIMD[type: DType, size: Int]( @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.index): + fn __init__(out self, value: __mlir_type.index): _simd_construction_checks[type, size]() var t0 = __mlir_op.`pop.cast_from_builtin`[ @@ -285,7 +285,7 @@ struct SIMD[type: DType, size: Int]( ](casted) @always_inline("nodebug") - fn __init__(inout self, value: IntLiteral): + fn __init__(out self, value: IntLiteral): """Initializes the SIMD vector with an integer. The integer value is splatted across all the elements of the SIMD @@ -310,7 +310,7 @@ struct SIMD[type: DType, size: Int]( ](casted) @always_inline("nodebug") - fn __init__(inout self: SIMD[DType.bool, size], value: Bool, /): + fn __init__(out self: SIMD[DType.bool, size], value: Bool, /): """Initializes the SIMD vector with a bool value. The bool value is splatted across all elements of the SIMD vector. @@ -341,7 +341,7 @@ struct SIMD[type: DType, size: Int]( self.value = value @always_inline("nodebug") - fn __init__(inout self, value: Scalar[type], /): + fn __init__(out self, value: Scalar[type], /): """Constructs a SIMD vector by splatting a scalar value. The input value is splatted across all elements of the SIMD vector. @@ -357,7 +357,7 @@ struct SIMD[type: DType, size: Int]( ](value.value) @always_inline("nodebug") - fn __init__(inout self, *elems: Scalar[type]): + fn __init__(out self, *elems: Scalar[type]): """Constructs a SIMD vector via a variadic list of elements. The input values are assigned to the corresponding elements of the SIMD @@ -396,7 +396,7 @@ struct SIMD[type: DType, size: Int]( self[i] = elems[i] @always_inline - fn __init__(inout self, value: FloatLiteral): + fn __init__(out self, value: FloatLiteral): """Initializes the SIMD vector with a float. The value is splatted across all the elements of the SIMD diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index df93331427..77d00ecc3e 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -34,7 +34,7 @@ alias insertion_sort_threshold = 32 struct _SortWrapper[type: CollectionElement](CollectionElement): var data: type - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.data = other.data diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 7f358cdd6c..2974eadbb7 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -70,7 +70,7 @@ struct StringLiteral( # ===-------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__(inout self, value: Self.type): + fn __init__(out self, value: Self.type): """Create a string literal from a builtin string type. Args: @@ -79,7 +79,7 @@ struct StringLiteral( self.value = value @always_inline("nodebug") - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy constructor. Args: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 963b605af4..8984289bb6 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -47,7 +47,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): """The underlying storage for the tuple.""" @always_inline("nodebug") - fn __init__(inout self, owned *args: *element_types): + fn __init__(out self, owned *args: *element_types): """Construct the tuple. Args: @@ -92,7 +92,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): UnsafePointer.address_of(self[i]).destroy_pointee() @always_inline("nodebug") - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy construct the tuple. Args: @@ -108,7 +108,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): UnsafePointer.address_of(self[i]).init_pointee_copy(existing[i]) @always_inline("nodebug") - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Move construct the tuple. Args: diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 9c744052df..cc9f9b8d22 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -54,13 +54,13 @@ struct UInt(IntLike, _HashableWithHasher): """ @always_inline("nodebug") - fn __init__(inout self): + fn __init__(out self): """Default constructor that produces zero.""" self.value = __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.index): + fn __init__(out self, value: __mlir_type.index): """Construct UInt from the given index value. Args: @@ -70,7 +70,7 @@ struct UInt(IntLike, _HashableWithHasher): @doc_private @always_inline("nodebug") - fn __init__(inout self, value: __mlir_type.`!pop.scalar`): + fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct UInt from the given Index value. Args: @@ -81,7 +81,7 @@ struct UInt(IntLike, _HashableWithHasher): ) @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Construct UInt from the given index value. Args: @@ -90,7 +90,7 @@ struct UInt(IntLike, _HashableWithHasher): self.value = value.value @always_inline("nodebug") - fn __init__(inout self, value: IntLiteral): + fn __init__(out self, value: IntLiteral): """Construct UInt from the given IntLiteral value. Args: diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 74e033330b..d8e6d8e5ea 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -24,10 +24,10 @@ trait Movable: ```mojo struct Foo(Movable): - fn __init__(inout self): + fn __init__(out self): pass - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): print("moving") ``` @@ -47,7 +47,7 @@ trait Movable: ``` """ - fn __moveinit__(inout self, owned existing: Self, /): + fn __moveinit__(out self, owned existing: Self, /): """Create a new instance of the value by moving the value of another. Args: @@ -66,10 +66,10 @@ trait Copyable: struct Foo(Copyable): var s: String - fn __init__(inout self, s: String): + fn __init__(out self, s: String): self.s = s - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): print("copying value") self.s = other.s ``` @@ -90,7 +90,7 @@ trait Copyable: ``` """ - fn __copyinit__(inout self, existing: Self, /): + fn __copyinit__(out self, existing: Self, /): """Create a new instance of the value by copying an existing one. Args: @@ -117,10 +117,10 @@ trait ExplicitlyCopyable: struct Foo(ExplicitlyCopyable): var s: String - fn __init__(inout self, s: String): + fn __init__(out self, s: String): self.s = s - fn __init__(inout self, copy: Self): + fn __init__(out self, copy: Self): print("explicitly copying value") self.s = copy.s ``` @@ -145,7 +145,7 @@ trait ExplicitlyCopyable: # `other` is a required named argument for the time being to minimize # implicit conversion overload ambiguity errors, particularly # with SIMD and Int. - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly construct a deep copy of the provided value. Args: @@ -164,7 +164,7 @@ trait Defaultable: struct Foo(Defaultable): var s: String - fn __init__(inout self): + fn __init__(out self): self.s = "default" ``` @@ -183,7 +183,7 @@ trait Defaultable: ``` """ - fn __init__(inout self): + fn __init__(out self): """Create a default instance of the value.""" ... diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index b65076d9e0..64db6a205a 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -51,12 +51,12 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): # Life cycle methods # ===------------------------------------------------------------------=== # - fn __init__(inout self): + fn __init__(out self): """Create a new, empty Counter object.""" self._data = Dict[V, Int]() # TODO: Change List to Iterable when it is supported in Mojo - fn __init__(inout self, items: List[V, *_]): + fn __init__(out self, items: List[V, *_]): """Create a from an input iterable. Args: @@ -68,7 +68,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): self._data[item] = self._data.get(item, 0) + 1 @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Create a new Counter by copying another Counter. Args: @@ -616,7 +616,7 @@ struct CountTuple[V: KeyElement]( # Life cycle methods # ===------------------------------------------------------------------=== # - fn __init__(inout self, value: V, count: Int): + fn __init__(out self, value: V, count: Int): """Create a new CountTuple. Args: @@ -626,7 +626,7 @@ struct CountTuple[V: KeyElement]( self._value = value self._count = count - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): """Create a new CountTuple by copying another CountTuple. Args: @@ -635,7 +635,7 @@ struct CountTuple[V: KeyElement]( self._value = other._value self._count = other._count - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): """Create a new CountTuple by moving another CountTuple. Args: diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index c52991fa85..b619c4bb5a 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -132,7 +132,7 @@ struct Deque[ElementType: CollectionElement]( if elements is not None: self.extend(elements.value()) - fn __init__(inout self, owned *values: ElementType): + fn __init__(out self, owned *values: ElementType): """Constructs a deque from the given values. Args: @@ -167,7 +167,7 @@ struct Deque[ElementType: CollectionElement]( self._tail = args_length - fn __init__(inout self, other: Self): + fn __init__(out self, other: Self): """Creates a deepcopy of the given deque. Args: @@ -185,7 +185,7 @@ struct Deque[ElementType: CollectionElement]( self._tail = len(other) - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Moves data of an existing deque into a new one. Args: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 8cbd4b6357..bd9cdb5687 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -220,7 +220,7 @@ struct DictEntry[K: KeyElement, V: CollectionElement]( var value: V """The value associated with the key.""" - fn __init__(inout self, owned key: K, owned value: V): + fn __init__(out self, owned key: K, owned value: V): """Create an entry from a key and value, computing the hash. Args: @@ -231,7 +231,7 @@ struct DictEntry[K: KeyElement, V: CollectionElement]( self.key = key^ self.value = value^ - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy an existing entry. Args: @@ -270,7 +270,7 @@ struct _DictIndex: var data: OpaquePointer @always_inline - fn __init__(inout self, reserved: Int): + fn __init__(out self, reserved: Int): if reserved <= 128: var data = UnsafePointer[Int8].alloc(reserved) for i in range(reserved): @@ -312,7 +312,7 @@ struct _DictIndex: memcpy(new_data, data, reserved) return index^ - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.data = existing.data fn get_index(self, reserved: Int, slot: Int) -> Int: @@ -472,7 +472,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """Initialize an empty dictiontary.""" self.size = 0 self._n_entries = 0 @@ -480,7 +480,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = _DictIndex(len(self._entries)) @always_inline - fn __init__(inout self, *, power_of_two_initial_capacity: Int): + fn __init__(out self, *, power_of_two_initial_capacity: Int): """Initialize an empty dictiontary with a pre-reserved initial capacity. Args: @@ -513,7 +513,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( return len(self._entries) @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy an existing dictiontary. Args: @@ -555,7 +555,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return Dict[K, Optional[V]].fromkeys(keys, value) - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy an existing dictiontary. Args: @@ -566,7 +566,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = existing._index.copy(existing._reserved()) self._entries = existing._entries - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Move data of an existing dict into a new one. Args: @@ -1083,11 +1083,11 @@ struct OwnedKwargsDict[V: CollectionElement]( # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Initialize an empty keyword dictionary.""" self._dict = Dict[Self.key_type, V]() - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy an existing keyword dictionary. Args: @@ -1095,7 +1095,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ self._dict = other._dict - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy an existing keyword dictionary. Args: @@ -1103,7 +1103,7 @@ struct OwnedKwargsDict[V: CollectionElement]( """ self._dict = existing._dict - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Move data of an existing keyword dictionary into a new one. Args: diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 0721f33e6f..7aab9d4618 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -67,7 +67,7 @@ struct InlineArray[ # ===------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """This constructor will always cause a compile time error if used. It is used to steer users away from uninitialized memory. """ @@ -85,7 +85,7 @@ struct InlineArray[ ]() @always_inline - fn __init__(inout self, *, unsafe_uninitialized: Bool): + fn __init__(out self, *, unsafe_uninitialized: Bool): """Create an InlineArray with uninitialized memory. Note that this is highly unsafe and should be used with caution. @@ -139,7 +139,7 @@ struct InlineArray[ ) @always_inline - fn __init__(inout self, fill: Self.ElementType): + fn __init__(out self, fill: Self.ElementType): """Constructs an empty array where each element is the supplied `fill`. Args: @@ -157,7 +157,7 @@ struct InlineArray[ ptr.init_pointee_copy(fill) @always_inline - fn __init__(inout self, owned *elems: Self.ElementType): + fn __init__(out self, owned *elems: Self.ElementType): """Constructs an array given a set of arguments. Args: @@ -194,7 +194,7 @@ struct InlineArray[ # Mark the elements as already destroyed. storage._is_owned = False - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly copy the provided value. Args: @@ -207,7 +207,7 @@ struct InlineArray[ var ptr = self.unsafe_ptr() + idx ptr.init_pointee_copy(other[idx]) - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): """Copy construct the array. Args: diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 0922999700..792635f427 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -100,7 +100,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """This constructor creates an empty InlineList.""" self._array = InlineArray[ UnsafeMaybeUninitialized[ElementType], capacity @@ -109,7 +109,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # TODO: Avoid copying elements in once owned varargs # allow transfers. - fn __init__(inout self, *values: ElementType): + fn __init__(out self, *values: ElementType): """Constructs a list from the given values. Args: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 6fa162fd2e..301825d0a7 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -109,13 +109,13 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Constructs an empty list.""" self.data = UnsafePointer[T]() self.size = 0 self.capacity = 0 - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Creates a deep copy of the given list. Args: @@ -125,7 +125,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( for e in other: self.append(e[]) - fn __init__(inout self, *, capacity: Int): + fn __init__(out self, *, capacity: Int): """Constructs a list with the given capacity. Args: @@ -135,7 +135,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = 0 self.capacity = capacity - fn __init__(inout self, owned *values: T): + fn __init__(out self, owned *values: T): """Constructs a list from the given values. Args: @@ -143,7 +143,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( """ self = Self(variadic_list=values^) - fn __init__(inout self, *, owned variadic_list: VariadicListMem[T, _]): + fn __init__(out self, *, owned variadic_list: VariadicListMem[T, _]): """Constructs a list from the given values. Args: @@ -164,7 +164,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = length - fn __init__(inout self, span: Span[T]): + fn __init__(out self, span: Span[T]): """Constructs a list from the a Span of values. Args: @@ -188,7 +188,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = length self.capacity = capacity - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Move data of an existing list into a new one. Args: @@ -198,7 +198,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = existing.size self.capacity = existing.capacity - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Creates a deepcopy of the given list. Args: diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index b4a1b00c74..d9326aa4cc 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -38,7 +38,7 @@ from utils import Variant # TODO(27780): NoneType can't currently conform to traits @value struct _NoneType(CollectionElement, CollectionElementNew): - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): pass @@ -88,11 +88,11 @@ struct Optional[T: CollectionElement]( # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Construct an empty Optional.""" self._value = Self._type(_NoneType()) - fn __init__(inout self, owned value: T): + fn __init__(out self, owned value: T): """Construct an Optional containing a value. Args: @@ -104,7 +104,7 @@ struct Optional[T: CollectionElement]( # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @doc_private - fn __init__(inout self, value: NoneType._mlir_type): + fn __init__(out self, value: NoneType._mlir_type): """Construct an empty Optional. Args: @@ -112,7 +112,7 @@ struct Optional[T: CollectionElement]( """ self = Self(value=NoneType(value)) - fn __init__(inout self, value: NoneType): + fn __init__(out self, value: NoneType): """Construct an empty Optional. Args: @@ -120,7 +120,7 @@ struct Optional[T: CollectionElement]( """ self = Self() - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy construct an Optional. Args: @@ -403,11 +403,11 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Create an optional with a value of None.""" self = Self(None) - fn __init__(inout self, value: T): + fn __init__(out self, value: T): """Create an optional with a value. Args: @@ -421,7 +421,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @doc_private - fn __init__(inout self, value: NoneType._mlir_type): + fn __init__(out self, value: NoneType._mlir_type): """Construct an empty Optional. Args: @@ -429,7 +429,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): """ self = Self(value=NoneType(value)) - fn __init__(inout self, value: NoneType): + fn __init__(out self, value: NoneType): """Create an optional without a value from a None literal. Args: diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 8d9f6d60f0..809d838a55 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -54,7 +54,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self, *ts: T): + fn __init__(out self, *ts: T): """Construct a set from initial elements. Args: @@ -64,7 +64,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for t in ts: self.add(t[]) - fn __init__(inout self, elements: Self): + fn __init__(out self, elements: Self): """Explicitly copy another Set instance. Args: @@ -74,7 +74,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for e in elements: self.add(e[]) - fn __init__(inout self, elements: List[T, *_]): + fn __init__(out self, elements: List[T, *_]): """Construct a set from a List of elements. Args: @@ -84,7 +84,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for e in elements: self.add(e[]) - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): """Move constructor. Args: diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index b371c7ec05..6fd87dd51e 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -114,7 +114,7 @@ struct InlinedFixedVector[ """The maximum number of elements that can fit in the vector.""" @always_inline - fn __init__(inout self, capacity: Int): + fn __init__(out self, capacity: Int): """Constructs `InlinedFixedVector` with the given capacity. The dynamically allocated portion is `capacity - size`. @@ -130,7 +130,7 @@ struct InlinedFixedVector[ self.capacity = capacity @always_inline - fn __init__(inout self, existing: Self): + fn __init__(out self, existing: Self): """ Copy constructor. @@ -148,7 +148,7 @@ struct InlinedFixedVector[ self.capacity = existing.capacity @always_inline - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """ Move constructor. diff --git a/stdlib/src/documentation/documentation.mojo b/stdlib/src/documentation/documentation.mojo index 76892d3c6c..69ea07675c 100644 --- a/stdlib/src/documentation/documentation.mojo +++ b/stdlib/src/documentation/documentation.mojo @@ -34,7 +34,7 @@ fn doc_private(): ```mojo struct Foo: @doc_private - fn __init__(inout self): + fn __init__(out self): "This should not be called directly, use `Foo.create` instead." return diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index d54036d0fe..7dccb4dd9d 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -93,7 +93,7 @@ struct AHasher[key: U256](_Hasher): var pad: UInt64 var extra_keys: U128 - fn __init__(inout self): + fn __init__(out self): """Initialize the hasher.""" alias pi_key = key ^ U256( 0x243F_6A88_85A3_08D3, diff --git a/stdlib/src/hashlib/_hasher.mojo b/stdlib/src/hashlib/_hasher.mojo index 505ae4fc0a..7bd278d5e9 100644 --- a/stdlib/src/hashlib/_hasher.mojo +++ b/stdlib/src/hashlib/_hasher.mojo @@ -21,7 +21,7 @@ trait _HashableWithHasher: trait _Hasher: - fn __init__(inout self): + fn __init__(out self): ... fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index d07383f165..9863d700b0 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -54,7 +54,7 @@ struct _ArcInner[T: Movable]: var refcount: Atomic[DType.uint64] var payload: T - fn __init__(inout self, owned value: T): + fn __init__(out self, owned value: T): """Create an initialized instance of this with a refcount of 1.""" self.refcount = 1 self.payload = value^ @@ -88,7 +88,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): alias _inner_type = _ArcInner[T] var _inner: UnsafePointer[Self._inner_type] - fn __init__(inout self, owned value: T): + fn __init__(out self, owned value: T): """Construct a new thread-safe, reference-counted smart pointer, and move the value into heap memory managed by the new pointer. @@ -101,7 +101,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): value^ ) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -110,7 +110,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): other._inner[].add_ref() self._inner = other._inner - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy an existing reference. Increment the refcount to the object. Args: diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 33689c6260..010822a602 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -34,7 +34,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): var _array: Self.type @always_inline - fn __init__(inout self): + fn __init__(out self): """The memory is now considered uninitialized.""" self._array = __mlir_op.`kgen.param.constant`[ _type = Self.type, @@ -43,7 +43,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): @doc_private @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """It is not possible to call this method. Trying to call this method will abort. @@ -77,7 +77,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): self.write(value^) @always_inline - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): """Copy another object. This method should never be called as implicit copy should not @@ -131,7 +131,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): self.unsafe_ptr().init_pointee_explicit_copy(other) @always_inline - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): """Move another object. This method should never be called as implicit moves should not diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 3976fc8c6d..4f00050e89 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -85,7 +85,7 @@ struct OwnedPointer[T: AnyType]: """ self.__init__(copy_value=other[]) - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Move this OwnedPointer[]. Args: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 4b128170bd..772199ed5f 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -98,13 +98,13 @@ struct UnsafePointer[ # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """Create a null pointer.""" self.address = __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] @doc_private @always_inline - fn __init__(inout self, value: Self._mlir_type): + fn __init__(out self, value: Self._mlir_type): """Create a pointer with the input value. Args: @@ -113,7 +113,7 @@ struct UnsafePointer[ self.address = value @always_inline - fn __init__(inout self, other: UnsafePointer[type, address_space, *_, **_]): + fn __init__(out self, other: UnsafePointer[type, address_space, *_, **_]): """Exclusivity parameter cast a pointer. Args: @@ -124,7 +124,7 @@ struct UnsafePointer[ ) @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index b8e8740068..9647be1218 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -48,7 +48,7 @@ struct _c_stat(Stringable): var st_birthtimespec: _CTimeSpec # time of file creation(birth) var unused: InlineArray[Int64, 2] # RESERVED: DO NOT USE! - fn __init__(inout self): + fn __init__(out self): self.st_dev = 0 self.st_mode = 0 self.st_nlink = 0 diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index 472e16c61d..d2872d8a1a 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -47,7 +47,7 @@ struct _c_stat(Stringable): var st_birthtimespec: _CTimeSpec # time of file creation(birth) var unused: InlineArray[Int64, 3] # RESERVED: DO NOT USE! - fn __init__(inout self): + fn __init__(out self): self.st_dev = 0 self.st_mode = 0 self.st_nlink = 0 diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index faeb23f686..1d1a7d11d3 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -50,7 +50,7 @@ struct _c_stat(Stringable): var st_lspare: Int32 # RESERVED: DO NOT USE! var st_qspare: InlineArray[Int64, 2] # RESERVED: DO NOT USE! - fn __init__(inout self): + fn __init__(out self): self.st_dev = 0 self.st_mode = 0 self.st_nlink = 0 diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 6b7bd43d90..f7902c1cf4 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -41,7 +41,7 @@ struct Atomic[type: DType]: """ @always_inline - fn __init__(inout self, value: Scalar[type]): + fn __init__(out self, value: Scalar[type]): """Constructs a new atomic value. Args: diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index ba00c1b447..c00532ff9a 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -95,7 +95,7 @@ struct _DirHandle: var _handle: OpaquePointer - fn __init__(inout self, path: String) raises: + fn __init__(out self, path: String) raises: """Construct the _DirHandle using the path provided. Args: diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 45a8c9aacf..e77c305081 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -80,11 +80,11 @@ struct Path( var path: String """The underlying path string representation.""" - fn __init__(inout self) raises: + fn __init__(out self) raises: """Initializes a path with the current directory.""" self = cwd() - fn __init__(inout self, path: String): + fn __init__(out self, path: String): """Initializes a path with the provided path. Args: @@ -92,7 +92,7 @@ struct Path( """ self.path = path - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index b908306ddf..9e7983cfbe 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -160,7 +160,7 @@ struct PyObjectPtr: # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """Initialize a null PyObjectPtr.""" self.unsized_obj_ptr = UnsafePointer[PyObject]() @@ -259,7 +259,7 @@ struct PythonVersion: var patch: Int """The patch version number.""" - fn __init__(inout self, version: StringRef): + fn __init__(out self, version: StringRef): """Initialize a PythonVersion object from a version string. Args: @@ -333,7 +333,7 @@ struct PyMethodDef: # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Constructs a zero initialized PyModuleDef. This is suitable for use terminating an array of PyMethodDef values. @@ -343,7 +343,7 @@ struct PyMethodDef: self.method_flags = 0 self.method_docstring = UnsafePointer[c_char]() - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly construct a deep copy of the provided value. Args: @@ -465,7 +465,7 @@ struct PyObject(Stringable, Representable, Writable): var object_ref_count: Int var object_type: UnsafePointer[PyTypeObject] - fn __init__(inout self): + fn __init__(out self): self.object_ref_count = 0 self.object_type = UnsafePointer[PyTypeObject]() @@ -541,13 +541,13 @@ struct PyModuleDef_Base(Stringable, Representable, Writable): # Life cycle methods # ===------------------------------------------------------------------=== # - fn __init__(inout self): + fn __init__(out self): self.object_base = PyObject() self.init_fn = _null_fn_ptr[Self._init_fn_type]() self.index = 0 self.dict_copy = UnsafePointer[PyObject]() - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.object_base = existing.object_base self.init_fn = existing.init_fn self.index = existing.index @@ -647,7 +647,7 @@ struct PyModuleDef(Stringable, Representable, Writable): alias _free_fn_type = fn (OpaquePointer) -> OpaquePointer var free_fn: Self._free_fn_type - fn __init__(inout self, name: String): + fn __init__(out self, name: String): self.base = PyModuleDef_Base() self.name = name.unsafe_cstr_ptr() self.docstring = UnsafePointer[c_char]() @@ -661,7 +661,7 @@ struct PyModuleDef(Stringable, Representable, Writable): self.clear_fn = _null_fn_ptr[Self._clear_fn_type]() self.free_fn = _null_fn_ptr[Self._free_fn_type]() - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.base = existing.base^ self.name = existing.name self.docstring = existing.docstring @@ -748,7 +748,7 @@ struct CPython: # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" if logging_enabled: print("CPython init") diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index a0c0878228..f2fb0237b9 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -59,10 +59,10 @@ fn _get_global_python_itf() -> _PythonInterfaceImpl: struct _PythonInterfaceImpl: var _cpython: UnsafePointer[CPython] - fn __init__(inout self, cpython: UnsafePointer[CPython]): + fn __init__(out self, cpython: UnsafePointer[CPython]): self._cpython = cpython - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): self._cpython = existing._cpython fn cpython(self) -> CPython: @@ -79,11 +79,11 @@ struct Python: # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Default constructor.""" self.impl = _get_global_python_itf() - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy constructor. Args: diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 05397a1c75..a7089fc8f5 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -50,7 +50,7 @@ struct _PyIter(Sized): # Life cycle methods # ===-------------------------------------------------------------------===# - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy another iterator. Args: @@ -60,7 +60,7 @@ struct _PyIter(Sized): self.preparedNextItem = existing.preparedNextItem self.isDone = existing.isDone - fn __init__(inout self, iter: PythonObject): + fn __init__(out self, iter: PythonObject): """Initialize an iterator. Args: @@ -76,7 +76,7 @@ struct _PyIter(Sized): self.preparedNextItem = PythonObject(maybeNextItem) self.isDone = False - fn __init__(inout self): + fn __init__(out self): """Initialize an empty iterator.""" self.iterator = PythonObject(PyObjectPtr()) self.isDone = True @@ -145,7 +145,7 @@ struct TypedPythonObject[type_hint: StringLiteral]( # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self, *, owned unsafe_unchecked_from: PythonObject): + fn __init__(out self, *, owned unsafe_unchecked_from: PythonObject): """Construct a TypedPythonObject without any validation that the given object is of the specified hinted type. @@ -155,7 +155,7 @@ struct TypedPythonObject[type_hint: StringLiteral]( """ self._obj = unsafe_unchecked_from^ - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): """Copy an instance of this type. Args: @@ -249,11 +249,11 @@ struct PythonObject( # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Initialize the object with a `None` value.""" self.__init__(None) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -261,7 +261,7 @@ struct PythonObject( """ self = other - fn __init__(inout self, ptr: PyObjectPtr): + fn __init__(out self, ptr: PyObjectPtr): """Initialize this object from an owned reference-counted Python object pointer. @@ -308,7 +308,7 @@ struct PythonObject( return PythonObject(borrowed_ptr) - fn __init__(inout self, owned typed_obj: TypedPythonObject[_]): + fn __init__(out self, owned typed_obj: TypedPythonObject[_]): """Construct a PythonObject from a typed object, dropping the type hint information. @@ -330,7 +330,7 @@ struct PythonObject( # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @doc_private - fn __init__(inout self, none: NoneType._mlir_type): + fn __init__(out self, none: NoneType._mlir_type): """Initialize a none value object from a `None` literal. Args: @@ -338,7 +338,7 @@ struct PythonObject( """ self = Self(none=NoneType()) - fn __init__(inout self, none: NoneType): + fn __init__(out self, none: NoneType): """Initialize a none value object from a `None` literal. Args: @@ -348,7 +348,7 @@ struct PythonObject( self.py_object = cpython.Py_None() cpython.Py_IncRef(self.py_object) - fn __init__(inout self, value: Bool): + fn __init__(out self, value: Bool): """Initialize the object from a bool. Args: @@ -357,7 +357,7 @@ struct PythonObject( cpython = _get_global_python_itf().cpython() self.py_object = cpython.PyBool_FromLong(int(value)) - fn __init__(inout self, integer: Int): + fn __init__(out self, integer: Int): """Initialize the object with an integer value. Args: @@ -389,7 +389,7 @@ struct PythonObject( fp_val = value.cast[DType.float64]() self.py_object = cpython.PyFloat_FromDouble(fp_val) - fn __init__(inout self, value: StringLiteral): + fn __init__(out self, value: StringLiteral): """Initialize the object from a string literal. Args: @@ -397,7 +397,7 @@ struct PythonObject( """ self = PythonObject(str(value)) - fn __init__(inout self, strref: StringRef): + fn __init__(out self, strref: StringRef): """Initialize the object from a string reference. Args: @@ -406,7 +406,7 @@ struct PythonObject( cpython = _get_global_python_itf().cpython() self.py_object = cpython.PyUnicode_DecodeUTF8(strref) - fn __init__(inout self, string: String): + fn __init__(out self, string: String): """Initialize the object from a string. Args: @@ -500,7 +500,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyTuple_SetItem(self.py_object, i, obj.py_object) - fn __init__(inout self, slice: Slice): + fn __init__(out self, slice: Slice): """Initialize the object from a Mojo Slice. Args: @@ -508,7 +508,7 @@ struct PythonObject( """ self.py_object = _slice_to_py_object_ptr(slice) - fn __init__(inout self, value: Dict[Self, Self]): + fn __init__(out self, value: Dict[Self, Self]): """Initialize the object from a dictionary of PythonObjects. Args: @@ -521,7 +521,7 @@ struct PythonObject( self.py_object, entry[].key.py_object, entry[].value.py_object ) - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): """Copy the object. This increments the underlying refcount of the existing object. diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index ad4058b1e5..b3c86e6373 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -117,7 +117,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline - fn __init__(inout self, path: String, flags: Int = DEFAULT_RTLD): + fn __init__(out self, path: String, flags: Int = DEFAULT_RTLD): """Initialize a DLHandle object by loading the dynamic library at the given path. @@ -136,7 +136,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): else: self.handle = OpaquePointer() - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index d5ceafdfb0..b60eecbbc5 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -279,7 +279,7 @@ struct PrefetchLocality: """Extremely local locality (keep in cache).""" @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Constructs a prefetch locality option. Args: @@ -301,7 +301,7 @@ struct PrefetchRW: """Write prefetch.""" @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Constructs a prefetch read-write option. Args: @@ -324,7 +324,7 @@ struct PrefetchCache: """The data prefetching option.""" @always_inline("nodebug") - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): """Constructs a prefetch option. Args: @@ -359,7 +359,7 @@ struct PrefetchOptions: """Indicates i-cache or d-cache prefetching.""" @always_inline("nodebug") - fn __init__(inout self): + fn __init__(out self): """Constructs an instance of PrefetchOptions with default params.""" self.rw = PrefetchRW.READ self.locality = PrefetchLocality.HIGH diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index d33c41f867..7f3c2dd09d 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -350,7 +350,7 @@ struct NamedTemporaryFile: if self._delete: os.remove(self.name) - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): """Moves constructor for the file handle. Args: diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 79369bea7c..5dff04c458 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -526,7 +526,7 @@ struct assert_raises: """Assigned the value returned by __call_locations() at Self.__init__.""" @always_inline - fn __init__(inout self, *, location: Optional[_SourceLocation] = None): + fn __init__(out self, *, location: Optional[_SourceLocation] = None): """Construct a context manager with no message pattern. Args: diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 39cca3aff6..b964a3f85c 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -63,7 +63,7 @@ struct _CTimeSpec(Stringable): var tv_sec: Int # Seconds var tv_subsec: Int # subsecond (nanoseconds on linux and usec on mac) - fn __init__(inout self): + fn __init__(out self): self.tv_sec = 0 self.tv_subsec = 0 @@ -85,7 +85,7 @@ struct _FILETIME: var dwLowDateTime: UInt32 var dwHighDateTime: UInt32 - fn __init__(inout self): + fn __init__(out self): self.dwLowDateTime = 0 self.dwHighDateTime = 0 diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 93eb5dbc8d..26c8620f26 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -194,13 +194,13 @@ struct IndexList[ """The underlying storage of the tuple value.""" @always_inline - fn __init__(inout self): + fn __init__(out self): """Constructs a static int tuple of the given size.""" self = 0 @doc_private @always_inline - fn __init__(inout self, value: __mlir_type.index): + fn __init__(out self, value: __mlir_type.index): """Constructs a sized 1 static int tuple of given the element value. Args: @@ -210,7 +210,7 @@ struct IndexList[ self = Int(value) @always_inline - fn __init__(inout self, elems: (Int, Int)): + fn __init__(out self, elems: (Int, Int)): """Constructs a static int tuple given a tuple of integers. Args: @@ -235,7 +235,7 @@ struct IndexList[ self = tup @always_inline - fn __init__(inout self, elems: (Int, Int, Int)): + fn __init__(out self, elems: (Int, Int, Int)): """Constructs a static int tuple given a tuple of integers. Args: @@ -260,7 +260,7 @@ struct IndexList[ self = tup @always_inline - fn __init__(inout self, elems: (Int, Int, Int, Int)): + fn __init__(out self, elems: (Int, Int, Int, Int)): """Constructs a static int tuple given a tuple of integers. Args: @@ -285,7 +285,7 @@ struct IndexList[ self = tup @always_inline - fn __init__(inout self, *elems: Int): + fn __init__(out self, *elems: Int): """Constructs a static int tuple given a set of arguments. Args: @@ -308,7 +308,7 @@ struct IndexList[ self = tup @always_inline - fn __init__(inout self, elem: Int): + fn __init__(out self, elem: Int): """Constructs a static int tuple given a set of arguments. Args: @@ -321,7 +321,7 @@ struct IndexList[ ] ](Self._int_type(elem)) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy constructor. Args: @@ -330,7 +330,7 @@ struct IndexList[ self.data = other.data @always_inline - fn __init__(inout self, values: VariadicList[Int]): + fn __init__(out self, values: VariadicList[Int]): """Creates a tuple constant using the specified values. Args: diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 42d04f19d4..e163727f51 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -53,12 +53,12 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): # Life cycle methods # ===------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Constructs a new empty string.""" var fixed = _FixedString[Self.SMALL_CAP]() self._storage = Self.Layout(fixed^) - fn __init__(inout self, literal: StringLiteral): + fn __init__(out self, literal: StringLiteral): """Constructs a InlineString value given a string literal. Args: @@ -82,7 +82,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): var heap = String(literal) self._storage = Self.Layout(heap^) - fn __init__(inout self, owned heap_string: String): + fn __init__(out self, owned heap_string: String): """Construct a new small string by taking ownership of an existing heap-allocated String. @@ -91,7 +91,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): """ self._storage = Self.Layout(heap_string^) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -328,12 +328,12 @@ struct _FixedString[CAP: Int]( # Life cycle methods # ===------------------------------------------------------------------===# - fn __init__(inout self): + fn __init__(out self): """Constructs a new empty string.""" self.buffer = InlineArray[UInt8, CAP](unsafe_uninitialized=True) self.size = 0 - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -341,7 +341,7 @@ struct _FixedString[CAP: Int]( """ self = other - fn __init__(inout self, literal: StringLiteral) raises: + fn __init__(out self, literal: StringLiteral) raises: """Constructs a FixedString value given a string literal. Args: diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 18cdeef46b..e7529401d7 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -29,7 +29,7 @@ struct SpinWaiter: var storage: OpaquePointer """Pointer to the underlying SpinWaiter instance.""" - fn __init__(inout self: Self): + fn __init__(out self: Self): """Initializes a SpinWaiter instance.""" self.storage = external_call[ "KGEN_CompilerRT_AsyncRT_InitializeSpinWaiter", @@ -60,7 +60,7 @@ struct BlockingSpinLock: var counter: Atomic[DType.int64] """The atomic counter implementing the spin lock.""" - fn __init__(inout self: Self): + fn __init__(out self: Self): """Default constructor.""" self.counter = Atomic[DType.int64](Self.UNLOCKED) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index b5c006aef4..72dea362b3 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -421,7 +421,7 @@ struct FlushDenormals: """The current state.""" @always_inline - fn __init__(inout self): + fn __init__(out self): """Initializes the FlushDenormals.""" self.state = Self._current_state() diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index e413dc4e42..c2a5fee6c0 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -114,7 +114,7 @@ struct Span[ # ===------------------------------------------------------------------===# @always_inline - fn __init__(inout self, *, ptr: UnsafePointer[T], length: Int): + fn __init__(out self, *, ptr: UnsafePointer[T], length: Int): """Unsafe construction from a pointer and length. Args: @@ -125,7 +125,7 @@ struct Span[ self._len = length @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly construct a deep copy of the provided Span. Args: @@ -135,7 +135,7 @@ struct Span[ self._len = other._len @always_inline - fn __init__(inout self, ref [origin]list: List[T, *_]): + fn __init__(out self, ref [origin]list: List[T, *_]): """Construct a Span from a List. Args: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 3f009988f7..f4066c9a4e 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -126,7 +126,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): """The underlying storage for the static tuple.""" @always_inline - fn __init__(inout self): + fn __init__(out self): """Constructs an empty (undefined) tuple.""" _static_tuple_construction_checks[size]() self.array = __mlir_op.`kgen.param.constant`[ @@ -135,7 +135,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): ]() @always_inline - fn __init__(inout self, *elems: Self.element_type): + fn __init__(out self, *elems: Self.element_type): """Constructs a static tuple given a set of arguments. Args: @@ -145,7 +145,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): self.array = _create_array[size](elems) @always_inline - fn __init__(inout self, values: VariadicList[Self.element_type]): + fn __init__(out self, values: VariadicList[Self.element_type]): """Creates a tuple constant using the specified values. Args: @@ -154,7 +154,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): _static_tuple_construction_checks[size]() self.array = _create_array[size, Self.element_type](values) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly copy the provided StaticTuple. Args: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 1c270d6983..c0e38d5f42 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -267,7 +267,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( # ===------------------------------------------------------------------===# @always_inline - fn __init__(inout self: StaticString, lit: StringLiteral): + fn __init__(out self: StaticString, lit: StringLiteral): """Construct a new `StringSlice` from a `StringLiteral`. Args: @@ -288,7 +288,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( self = StaticString(unsafe_from_utf8=lit.as_bytes()) @always_inline - fn __init__(inout self, *, owned unsafe_from_utf8: Span[Byte, origin]): + fn __init__(out self, *, owned unsafe_from_utf8: Span[Byte, origin]): """Construct a new `StringSlice` from a sequence of UTF-8 encoded bytes. Args: @@ -300,7 +300,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( self._slice = unsafe_from_utf8^ - fn __init__(inout self, *, unsafe_from_utf8_strref: StringRef): + fn __init__(out self, *, unsafe_from_utf8_strref: StringRef): """Construct a new StringSlice from a `StringRef` pointing to UTF-8 encoded bytes. @@ -323,7 +323,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( self = Self(unsafe_from_utf8=byte_slice) @always_inline - fn __init__(inout self, *, ptr: UnsafePointer[Byte], length: Int): + fn __init__(out self, *, ptr: UnsafePointer[Byte], length: Int): """Construct a `StringSlice` from a pointer to a sequence of UTF-8 encoded bytes and a length. @@ -340,7 +340,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( self._slice = Span[Byte, origin](ptr=ptr, length=length) @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly construct a deep copy of the provided `StringSlice`. Args: @@ -1162,7 +1162,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): alias _args_t = VariadicPack[element_trait=_CurlyEntryFormattable, *_] """Args types that are formattable by curly entry.""" - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.first_curly = other.first_curly self.last_curly = other.last_curly self.conversion_flag = other.conversion_flag diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 4d9e427580..8406c7451e 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -69,12 +69,12 @@ struct StringRef( # ===-------------------------------------------------------------------===# @always_inline - fn __init__(inout self): + fn __init__(out self): """Construct a StringRef value with length zero.""" self = StringRef(UnsafePointer[UInt8](), 0) @always_inline - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Copy the object. Args: @@ -84,7 +84,7 @@ struct StringRef( self.length = other.length @always_inline - fn __init__(inout self, str: StringLiteral): + fn __init__(out self, str: StringLiteral): """Construct a StringRef value given a constant string. Args: @@ -93,7 +93,7 @@ struct StringRef( self = StringRef(str.unsafe_ptr(), len(str)) @always_inline - fn __init__(inout self, ptr: UnsafePointer[c_char], len: Int): + fn __init__(out self, ptr: UnsafePointer[c_char], len: Int): """Construct a StringRef value given a (potentially non-0 terminated string). @@ -112,7 +112,7 @@ struct StringRef( self.length = len @always_inline - fn __init__(inout self, *, ptr: UnsafePointer[UInt8]): + fn __init__(out self, *, ptr: UnsafePointer[UInt8]): """Construct a StringRef value given a null-terminated string. Args: @@ -126,7 +126,7 @@ struct StringRef( self = StringRef(ptr, len) @always_inline - fn __init__(inout self, ptr: UnsafePointer[c_char]): + fn __init__(out self, ptr: UnsafePointer[c_char]): """Construct a StringRef value given a null-terminated string. Note that you should use the constructor from `UnsafePointer[UInt8]` instead diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 34546cba4b..e26cd537fc 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -115,7 +115,7 @@ struct Variant[*Ts: CollectionElement]( # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__(inout self, *, unsafe_uninitialized: ()): + fn __init__(out self, *, unsafe_uninitialized: ()): """Unsafely create an uninitialized Variant. Args: @@ -138,7 +138,7 @@ struct Variant[*Ts: CollectionElement]( self._get_discr() = idx self._get_ptr[T]().init_pointee_move(value^) - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly creates a deep copy of an existing variant. Args: @@ -154,7 +154,7 @@ struct Variant[*Ts: CollectionElement]( self._get_ptr[T]().init_pointee_move(other._get_ptr[T]()[]) return - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): """Creates a deep copy of an existing variant. Args: @@ -164,7 +164,7 @@ struct Variant[*Ts: CollectionElement]( # Delegate to explicit copy initializer. self = Self(other=other) - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): """Move initializer for the variant. Args: diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 5329ab296e..8e3e55579d 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -228,7 +228,7 @@ struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): var pos: Int var writer: W - fn __init__(inout self, owned writer: W): + fn __init__(out self, owned writer: W): self.data = UnsafePointer[ UInt8, address_space = AddressSpace.GENERIC, @@ -275,7 +275,7 @@ struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): var pos: Int var writer: W - fn __init__(inout self, owned writer: W): + fn __init__(out self, owned writer: W): self.data = InlineArray[UInt8, capacity](unsafe_uninitialized=True) self.pos = 0 self.writer = writer^ diff --git a/stdlib/test/builtin/test_none.mojo b/stdlib/test/builtin/test_none.mojo index 028471881c..9d588bb7d4 100644 --- a/stdlib/test/builtin/test_none.mojo +++ b/stdlib/test/builtin/test_none.mojo @@ -37,10 +37,10 @@ def test_format_to(): struct FromNone: var value: Int - fn __init__(inout self, none: NoneType): + fn __init__(out self, none: NoneType): self.value = -1 - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): self.value = value diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index d17765bf88..ca3b03afcc 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -47,7 +47,7 @@ struct PrintChecker: var call_location: _SourceLocation @always_inline - fn __init__(inout self) raises: + fn __init__(out self) raises: self.tmp = NamedTemporaryFile("rw") self.call_location = __call_location() self.cursor = 0 @@ -55,7 +55,7 @@ struct PrintChecker: fn __enter__(owned self) -> Self: return self^ - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.tmp = existing.tmp^ self.cursor = existing.cursor self.call_location = existing.call_location diff --git a/stdlib/test/builtin/test_rebind.mojo b/stdlib/test/builtin/test_rebind.mojo index f23ae595e4..0d73aee2af 100644 --- a/stdlib/test/builtin/test_rebind.mojo +++ b/stdlib/test/builtin/test_rebind.mojo @@ -51,7 +51,7 @@ def test_rebind_register(): struct MyMemStruct[size: Int]: var value: Int - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): # Make sure no copy is made due to the rebind. print("Should not copy this!") self.value = existing.value diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index b552d13f0b..2208a5bf3a 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -38,7 +38,7 @@ struct BoringSlice: struct Sliceable: - fn __init__(inout self): + fn __init__(out self): pass fn __getitem__(self, a: FunnySlice) -> FunnySlice: @@ -63,7 +63,7 @@ def test_slicable(): struct SliceStringable: - fn __init__(inout self): + fn __init__(out self): pass fn __getitem__(self, a: Slice) -> String: diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 987dbda3ec..7ff2834d46 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -497,7 +497,7 @@ fn test_sort_stress() raises: struct MyStruct(CollectionElement): var val: Int - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.val = other.val @@ -550,7 +550,7 @@ struct Person(ComparableCollectionElement): var name: String var age: Int - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.name = String(other=other.name) self.age = other.age diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 44a7a04d5b..d9807ea83b 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -391,7 +391,7 @@ def test_dict_update_empty_new(): struct DummyKey(KeyElement): var value: Int - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self = other fn __hash__(self) -> UInt: diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 35365af488..49bd72a1bb 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -551,11 +551,11 @@ struct CopyCountedStruct(CollectionElement): var counter: CopyCounter var value: String - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.counter = CopyCounter(other=other.counter) self.value = String(other=other.value) - fn __init__(inout self, value: String): + fn __init__(out self, value: String): self.counter = CopyCounter() self.value = value @@ -876,16 +876,16 @@ struct DtorCounter(CollectionElement): # NOTE: payload is required because List does not support zero sized structs. var payload: Int - fn __init__(inout self): + fn __init__(out self): self.payload = 0 - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.payload = other.payload - fn __copyinit__(inout self, existing: Self, /): + fn __copyinit__(out self, existing: Self, /): self.payload = existing.payload - fn __moveinit__(inout self, owned existing: Self, /): + fn __moveinit__(out self, owned existing: Self, /): self.payload = existing.payload existing.payload = 0 diff --git a/stdlib/test/hashlib/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo index 44bf5eed15..f41685b553 100644 --- a/stdlib/test/hashlib/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -25,7 +25,7 @@ from utils import StringRef struct DummyHasher(_Hasher): var _dummy_value: UInt64 - fn __init__(inout self): + fn __init__(out self): self._dummy_value = 0 fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index f7573c1941..657fc02b81 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -23,12 +23,12 @@ struct MoveOnlyType(Movable): var actions: UnsafePointer[List[String]] var value: Int - fn __init__(inout self, value: Int, actions: UnsafePointer[List[String]]): + fn __init__(out self, value: Int, actions: UnsafePointer[List[String]]): self.actions = actions self.value = value self.actions[0].append("__init__") - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.actions = existing.actions self.value = existing.value self.actions[0].append("__moveinit__") diff --git a/stdlib/test/os/path/test_expandvars.mojo b/stdlib/test/os/path/test_expandvars.mojo index 662558bbfb..d81746d88c 100644 --- a/stdlib/test/os/path/test_expandvars.mojo +++ b/stdlib/test/os/path/test_expandvars.mojo @@ -21,7 +21,7 @@ from testing import assert_equal struct EnvVar: var name: String - fn __init__(inout self, name: String, value: String) -> None: + fn __init__(out self, name: String, value: String) -> None: self.name = name _ = os.setenv(name, value) diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index ade9d8c809..b9dd9cd91a 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -27,7 +27,7 @@ struct MoveOnly[T: Movable](Movable): var data: T """Test data payload.""" - fn __init__(inout self, owned i: T): + fn __init__(out self, owned i: T): """Construct a MoveOnly providing the payload data. Args: @@ -35,7 +35,7 @@ struct MoveOnly[T: Movable](Movable): """ self.data = i^ - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): """Move construct a MoveOnly from an existing variable. Args: @@ -53,11 +53,11 @@ struct ExplicitCopyOnly(ExplicitlyCopyable): var value: Int var copy_count: Int - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): self.value = value self.copy_count = 0 - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.value = other.value self.copy_count = other.copy_count + 1 @@ -71,11 +71,11 @@ struct ImplicitCopyOnly(Copyable): var value: Int var copy_count: Int - fn __init__(inout self, value: Int): + fn __init__(out self, value: Int): self.value = value self.copy_count = 0 - fn __copyinit__(inout self, *, other: Self): + fn __copyinit__(out self, *, other: Self): self.value = other.value self.copy_count = other.copy_count + 1 @@ -90,16 +90,16 @@ struct CopyCounter(CollectionElement, ExplicitlyCopyable): var copy_count: Int - fn __init__(inout self): + fn __init__(out self): self.copy_count = 0 - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.copy_count = other.copy_count + 1 - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.copy_count = existing.copy_count - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): self.copy_count = existing.copy_count + 1 @@ -117,7 +117,7 @@ struct MoveCounter[T: CollectionElementNew]( var value: T var move_count: Int - fn __init__(inout self, owned value: T): + fn __init__(out self, owned value: T): """Construct a new instance of this type. This initial move is not counted. """ self.value = value^ @@ -125,7 +125,7 @@ struct MoveCounter[T: CollectionElementNew]( # TODO: This type should not be ExplicitlyCopyable, but has to be to satisfy # CollectionElementNew at the moment. - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): """Explicitly copy the provided value. Args: @@ -134,13 +134,13 @@ struct MoveCounter[T: CollectionElementNew]( self.value = T(other=other.value) self.move_count = other.move_count - fn __moveinit__(inout self, owned existing: Self): + fn __moveinit__(out self, owned existing: Self): self.value = existing.value^ self.move_count = existing.move_count + 1 # TODO: This type should not be Copyable, but has to be to satisfy # CollectionElement at the moment. - fn __copyinit__(inout self, existing: Self): + fn __copyinit__(out self, existing: Self): # print("ERROR: _MoveCounter copy constructor called unexpectedly!") self.value = T(other=existing.value) self.move_count = existing.move_count @@ -156,7 +156,7 @@ struct ValueDestructorRecorder(ExplicitlyCopyable): var value: Int var destructor_counter: UnsafePointer[List[Int]] - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self.value = other.value self.destructor_counter = other.destructor_counter @@ -173,7 +173,7 @@ struct ValueDestructorRecorder(ExplicitlyCopyable): struct ObservableDel(CollectionElement): var target: UnsafePointer[Bool] - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self = other fn __del__(owned self): diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index f517433183..bafac3221f 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -25,18 +25,18 @@ struct TestCounter(CollectionElement): var copied: Int var moved: Int - fn __init__(inout self): + fn __init__(out self): self.copied = 0 self.moved = 0 - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): self = other - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): self.copied = other.copied + 1 self.moved = other.moved - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): self.copied = other.copied self.moved = other.moved + 1 @@ -65,16 +65,16 @@ fn _destroy_poison(p: OpaquePointer): struct Poison(CollectionElement): - fn __init__(inout self): + fn __init__(out self): pass - fn __init__(inout self, *, other: Self): + fn __init__(out self, *, other: Self): _poison_ptr().init_pointee_move(True) - fn __copyinit__(inout self, other: Self): + fn __copyinit__(out self, other: Self): _poison_ptr().init_pointee_move(True) - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(out self, owned other: Self): _poison_ptr().init_pointee_move(True) fn __del__(owned self): From 27c64218289d18a36342a7f0d06267764672cae0 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 11 Nov 2024 11:22:42 -0800 Subject: [PATCH 1860/2019] [KGEN] Remove warp_size from targetattr MODULAR_ORIG_COMMIT_REV_ID: 9d76529781a917818075a7e9575a269a68e2dc9e --- stdlib/src/sys/info.mojo | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 339c695778..2620939782 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -574,12 +574,7 @@ fn warpsize[ Returns: The warp size of the specified target. """ - return __mlir_attr[ - `#kgen.param.expr : !kgen.int_literal`, - ] + return 32 @always_inline("nodebug") From c89a60af030ad7296f42607cf70be08acf42a947 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 11 Nov 2024 18:50:14 -0800 Subject: [PATCH 1861/2019] [******][GPU] Rename triple_is_nvidia_cuda to is_nvidia_gpu, NFC MODULAR_ORIG_COMMIT_REV_ID: d1f87b0e1933bfa06f37c830410ac1cedcbb6e26 --- stdlib/src/builtin/debug_assert.mojo | 8 ++++---- stdlib/src/builtin/file_descriptor.mojo | 4 ++-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/io.mojo | 6 +++--- stdlib/src/builtin/simd.mojo | 18 ++++++++--------- stdlib/src/math/math.mojo | 26 ++++++++++++------------- stdlib/src/memory/memory.mojo | 16 +++++++-------- stdlib/src/memory/unsafe_pointer.mojo | 10 ++++------ stdlib/src/os/atomic.mojo | 6 +++--- stdlib/src/os/os.mojo | 4 ++-- stdlib/src/sys/__init__.mojo | 2 +- stdlib/src/sys/info.mojo | 14 ++++++------- stdlib/src/sys/intrinsics.mojo | 4 ++-- stdlib/src/time/time.mojo | 8 ++++---- stdlib/src/utils/write.mojo | 4 ++-- 15 files changed, 64 insertions(+), 68 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index da3567731a..d0d33d4899 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from os import abort -from sys import is_defined, triple_is_nvidia_cuda +from sys import is_defined, is_nvidia_gpu from sys._build import is_debug_build from sys.param_env import env_get_string from sys.ffi import external_call, c_uint, c_size_t, c_char @@ -44,7 +44,7 @@ fn _assert_enabled[assert_mode: StringLiteral, cpu_only: Bool]() -> Bool: ]() @parameter - if defined_mode == "none" or (triple_is_nvidia_cuda() and cpu_only): + if defined_mode == "none" or (is_nvidia_gpu() and cpu_only): return False elif defined_mode == "all" or defined_mode == "warn" or is_debug_build(): return True @@ -295,7 +295,7 @@ fn _debug_assert_msg( """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): external_call["__assertfail", NoneType]( "debug_assert message must be a single StringLiteral on GPU" .unsafe_cstr_ptr(), @@ -328,7 +328,7 @@ fn _debug_assert_msg_literal(message: StringLiteral, loc: _SourceLocation): """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): external_call["__assertfail", NoneType]( message.unsafe_cstr_ptr(), loc.file_name.unsafe_cstr_ptr(), diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index e4132603bc..6974b8d116 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -26,7 +26,7 @@ f.close() from utils import Span from builtin.io import _printf from sys.ffi import external_call, OpaquePointer -from sys.info import triple_is_nvidia_cuda +from sys.info import is_nvidia_gpu from memory import UnsafePointer @@ -65,7 +65,7 @@ struct FileDescriptor(Writer): var len_bytes = len(bytes) @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): _printf["%*s"](len_bytes, bytes.unsafe_ptr()) else: written = external_call["write", Int32]( diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index aa6bd7de8b..6883fbcd17 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -32,7 +32,7 @@ from memory import UnsafePointer from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select -from sys import triple_is_nvidia_cuda, bitwidthof +from sys import is_nvidia_gpu, bitwidthof from sys.ffi import OpaquePointer # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index d605324b1c..f02b5b1a80 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -19,7 +19,7 @@ from sys import ( bitwidthof, external_call, stdout, - triple_is_nvidia_cuda, + is_nvidia_gpu, _libc as libc, ) from sys._libc import dup, fclose, fdopen, fflush @@ -165,7 +165,7 @@ fn _printf[ var loaded_pack = arguments.get_loaded_kgen_pack() @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): _ = external_call["vprintf", Int32]( fmt.unsafe_cstr_ptr(), Pointer.address_of(loaded_pack) ) @@ -257,7 +257,7 @@ fn print[ write_buffered[buffer_size=4096](file, values, sep=sep, end=end) @parameter - if not triple_is_nvidia_cuda(): + if not is_nvidia_gpu(): if flush: _flush(file=file) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 3ae36ad548..560c88937e 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -25,7 +25,7 @@ from sys import ( llvm_intrinsic, prefetch, simdwidthof, - triple_is_nvidia_cuda, + is_nvidia_gpu, bitwidthof, ) from sys.info import _current_arch, _is_sm_8x, _is_sm_9x @@ -152,12 +152,12 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @always_inline("nodebug") fn _has_native_bf16_support() -> Bool: - return triple_is_nvidia_cuda() + return is_nvidia_gpu() @always_inline("nodebug") fn _has_native_f8_support() -> Bool: - return _is_sm_9x() or triple_is_nvidia_cuda["sm_89"]() + return _is_sm_9x() or is_nvidia_gpu["sm_89"]() # ===----------------------------------------------------------------------=== # @@ -218,9 +218,7 @@ struct SIMD[type: DType, size: Int]( alias MIN_FINITE = Self(_min_finite[type]()) """Returns the minimum (lowest) finite value of SIMD value.""" - alias _default_alignment = alignof[ - Scalar[type] - ]() if triple_is_nvidia_cuda() else 1 + alias _default_alignment = alignof[Scalar[type]]() if is_nvidia_gpu() else 1 # ===-------------------------------------------------------------------===# # Life cycle methods @@ -1494,7 +1492,7 @@ struct SIMD[type: DType, size: Int]( elif type.is_floating_point(): @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if type.is_half_float(): @@ -1583,7 +1581,7 @@ struct SIMD[type: DType, size: Int]( return rebind[SIMD[target, size]](self) @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if size > 1 and type is DType.float32 and target.is_half_float(): @@ -2940,7 +2938,7 @@ fn _bfloat16_to_f32_scalar( # For bfloat16, we can just do a memcpy to perform the cast to float32. @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): return inlined_assembly[ "cvt.f32.bf16 $0, $1;" if _is_sm_9x() else "mov.b32 $0, {0, $1};", Scalar[DType.float32], @@ -3185,7 +3183,7 @@ fn _write_scalar[ elif dtype.is_integral(): @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): var err = _try_write_int(writer, value) if err: abort( diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 2cf83b725c..6083f4de83 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -27,7 +27,7 @@ from sys import ( bitwidthof, has_avx512f, simdwidthof, - triple_is_nvidia_cuda, + is_nvidia_gpu, sizeof, ) @@ -258,7 +258,7 @@ fn sqrt[ for i in range(simd_width): res[i] = sqrt(int(x[i])) return res - elif triple_is_nvidia_cuda(): + elif is_nvidia_gpu(): @parameter if x.type in (DType.float16, DType.bfloat16): @@ -303,7 +303,7 @@ fn isqrt(x: SIMD) -> __type_of(x): constrained[x.type.is_floating_point(), "type must be floating point"]() @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if x.type in (DType.float16, DType.bfloat16): @@ -349,7 +349,7 @@ fn recip(x: SIMD) -> __type_of(x): constrained[x.type.is_floating_point(), "type must be floating point"]() @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if x.type in (DType.float16, DType.bfloat16): @@ -385,7 +385,7 @@ fn exp2[ """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if type is DType.float16: @@ -589,7 +589,7 @@ fn exp[ alias inv_lg2 = 1.442695040888963407359924681001892137426646 @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if type in (DType.float16, DType.float32): @@ -768,7 +768,7 @@ fn log(x: SIMD) -> __type_of(x): """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): alias ln2 = 0.69314718055966295651160180568695068359375 @parameter @@ -802,7 +802,7 @@ fn log2(x: SIMD) -> __type_of(x): """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): @parameter if sizeof[x.type]() < sizeof[DType.float32](): @@ -936,7 +936,7 @@ fn tanh[ ]() @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): alias instruction = "tanh.approx.f32" @parameter @@ -1408,7 +1408,7 @@ fn cos[ """ @parameter - if triple_is_nvidia_cuda() and sizeof[type]() <= sizeof[DType.float32](): + if is_nvidia_gpu() and sizeof[type]() <= sizeof[DType.float32](): return _call_ptx_intrinsic[ instruction="cos.approx.ftz.f32", constraints="=f,f" ](x) @@ -1441,7 +1441,7 @@ fn sin[ """ @parameter - if triple_is_nvidia_cuda() and sizeof[type]() <= sizeof[DType.float32](): + if is_nvidia_gpu() and sizeof[type]() <= sizeof[DType.float32](): return _call_ptx_intrinsic[ instruction="sin.approx.ftz.f32", constraints="=f,f" ](x) @@ -1609,7 +1609,7 @@ fn log10(x: SIMD) -> __type_of(x): """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): alias log10_2 = 0.301029995663981195213738894724493027 @parameter @@ -2328,7 +2328,7 @@ fn _call_libm[ arg_type == result_type, "the argument type must match the result type" ]() constrained[ - not triple_is_nvidia_cuda(), + not is_nvidia_gpu(), "the libm operation is not available on the CUDA target", ]() diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 633025a5d6..8384279c65 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -24,7 +24,7 @@ from sys import ( alignof, llvm_intrinsic, sizeof, - triple_is_nvidia_cuda, + is_nvidia_gpu, external_call, simdwidthof, simdbitwidth, @@ -155,7 +155,7 @@ fn _memcpy_impl( """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): alias chunk_size = simdbitwidth() var vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): @@ -349,7 +349,7 @@ fn stack_allocation[ count: Int, type: DType, /, - alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, + alignment: Int = alignof[type]() if is_nvidia_gpu() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> UnsafePointer[Scalar[type], address_space]: """Allocates data buffer space on the stack given a data type and number of @@ -376,7 +376,7 @@ fn stack_allocation[ type: AnyType, /, name: Optional[StringLiteral] = None, - alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, + alignment: Int = alignof[type]() if is_nvidia_gpu() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> UnsafePointer[type, address_space]: """Allocates data buffer space on the stack given a data type and number of @@ -394,7 +394,7 @@ fn stack_allocation[ """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): # On NVGPU, SHARED and PARAM address spaces lower to global memory. @parameter if address_space in (_GPUAddressSpace.SHARED, _GPUAddressSpace.PARAM): @@ -436,12 +436,12 @@ fn _malloc[ type: AnyType, /, *, - alignment: Int = alignof[type]() if triple_is_nvidia_cuda() else 1, + alignment: Int = alignof[type]() if is_nvidia_gpu() else 1, ](size: Int, /) -> UnsafePointer[ type, AddressSpace.GENERIC, alignment=alignment ]: @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): return external_call[ "malloc", UnsafePointer[NoneType, AddressSpace.GENERIC] ](size).bitcast[type]() @@ -459,7 +459,7 @@ fn _malloc[ @always_inline fn _free(ptr: UnsafePointer[_, AddressSpace.GENERIC, *_]): @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): libc.free(ptr.bitcast[NoneType]()) else: __mlir_op.`pop.aligned_free`(ptr.address) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 772199ed5f..1c5676870c 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,7 +19,7 @@ from memory import UnsafePointer ``` """ -from sys import alignof, sizeof, triple_is_nvidia_cuda +from sys import alignof, sizeof, is_nvidia_gpu from sys.intrinsics import ( _mlirtype_is_eq, _type_is_eq, @@ -40,7 +40,7 @@ from memory.memory import _free, _malloc @always_inline fn _default_alignment[type: AnyType]() -> Int: - return alignof[type]() if triple_is_nvidia_cuda() else 1 + return alignof[type]() if is_nvidia_gpu() else 1 @always_inline @@ -478,7 +478,7 @@ struct UnsafePointer[ ]() @parameter - if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: + if is_nvidia_gpu() and sizeof[type]() == 1 and alignment == 1: # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types # regardless of the alignment that is passed. This causes issues if # this method is called on an unaligned pointer. @@ -842,9 +842,7 @@ struct UnsafePointer[ type: DType, //, *, width: Int = 1, - alignment: Int = alignof[ - SIMD[type, width] - ]() if triple_is_nvidia_cuda() else 1, + alignment: Int = alignof[SIMD[type, width]]() if is_nvidia_gpu() else 1, ]( self: UnsafePointer[Scalar[type], *_, **_], offset: SIMD[_, width], diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index f7902c1cf4..2932659f93 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -21,7 +21,7 @@ from os import Atomic from builtin.dtype import _integral_type_of, _unsigned_integral_type_of from memory import UnsafePointer, bitcast -from sys.info import triple_is_nvidia_cuda +from sys.info import is_nvidia_gpu struct Atomic[type: DType]: @@ -343,7 +343,7 @@ fn _max_impl[ type: DType, // ](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): @parameter - if triple_is_nvidia_cuda() and type.is_floating_point(): + if is_nvidia_gpu() and type.is_floating_point(): alias integral_type = _integral_type_of[type]() alias unsigned_integral_type = _unsigned_integral_type_of[type]() if rhs >= 0: @@ -365,7 +365,7 @@ fn _min_impl[ type: DType, // ](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): @parameter - if triple_is_nvidia_cuda() and type.is_floating_point(): + if is_nvidia_gpu() and type.is_floating_point(): alias integral_type = _integral_type_of[type]() alias unsigned_integral_type = _unsigned_integral_type_of[type]() if rhs >= 0: diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index c00532ff9a..d66008b196 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -20,7 +20,7 @@ from os import listdir """ from collections import List, InlineArray -from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda, external_call +from sys import os_is_linux, os_is_windows, is_nvidia_gpu, external_call from sys.ffi import c_char, OpaquePointer from memory import UnsafePointer @@ -265,7 +265,7 @@ fn abort[ """ @parameter - if not triple_is_nvidia_cuda(): + if not is_nvidia_gpu(): print(message, flush=True) return abort[result]() diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 57394f0ed6..0101272996 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -48,7 +48,7 @@ from .info import ( simdwidthof, warpsize, sizeof, - triple_is_nvidia_cuda, + is_nvidia_gpu, ) from .intrinsics import ( PrefetchCache, diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 2620939782..d1aa1837de 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -412,19 +412,19 @@ fn is_triple[triple: StringLiteral]() -> Bool: @always_inline("nodebug") fn _is_sm_8x() -> Bool: return ( - triple_is_nvidia_cuda["sm_80"]() - or triple_is_nvidia_cuda["sm_86"]() - or triple_is_nvidia_cuda["sm_89"]() + is_nvidia_gpu["sm_80"]() + or is_nvidia_gpu["sm_86"]() + or is_nvidia_gpu["sm_89"]() ) @always_inline("nodebug") fn _is_sm_9x() -> Bool: - return triple_is_nvidia_cuda["sm_90"]() or triple_is_nvidia_cuda["sm_90a"]() + return is_nvidia_gpu["sm_90"]() or is_nvidia_gpu["sm_90a"]() @always_inline("nodebug") -fn triple_is_nvidia_cuda() -> Bool: +fn is_nvidia_gpu() -> Bool: """Returns True if the target triple of the compiler is `nvptx64-nvidia-cuda` False otherwise. @@ -435,7 +435,7 @@ fn triple_is_nvidia_cuda() -> Bool: @always_inline("nodebug") -fn triple_is_nvidia_cuda[subarch: StringLiteral]() -> Bool: +fn is_nvidia_gpu[subarch: StringLiteral]() -> Bool: """Returns True if the target triple of the compiler is `nvptx64-nvidia-cuda` and we are compiling for the specified sub-architecture and False otherwise. @@ -445,7 +445,7 @@ fn triple_is_nvidia_cuda[subarch: StringLiteral]() -> Bool: Returns: True if the triple target is cuda and False otherwise. """ - return triple_is_nvidia_cuda() and StringLiteral(_current_arch()) == subarch + return is_nvidia_gpu() and StringLiteral(_current_arch()) == subarch @always_inline("nodebug") diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index b60eecbbc5..5b54419691 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -19,7 +19,7 @@ from sys import PrefetchLocality ``` """ -from .info import sizeof, triple_is_nvidia_cuda +from .info import sizeof, is_nvidia_gpu from ._assembly import inlined_assembly import math @@ -480,7 +480,7 @@ fn prefetch[ """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): inlined_assembly[ "prefetch.global.L2 [$0];", NoneType, diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index b964a3f85c..4002648a9a 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -24,7 +24,7 @@ from sys import ( external_call, os_is_linux, os_is_windows, - triple_is_nvidia_cuda, + is_nvidia_gpu, llvm_intrinsic, ) from sys._assembly import inlined_assembly @@ -204,7 +204,7 @@ fn perf_counter_ns() -> Int: """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): return int( inlined_assembly[ "mov.u64 $0, %globaltimer;", UInt64, constraints="=l" @@ -347,7 +347,7 @@ fn sleep(sec: Float64): """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): var nsec = sec * 1.0e9 llvm_intrinsic["llvm.nvvm.nanosleep", NoneType]( nsec.cast[DType.int32]() @@ -376,7 +376,7 @@ fn sleep(sec: Int): """ @parameter - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): return sleep(Float64(sec)) @parameter diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 8e3e55579d..c82e4afa1b 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -15,7 +15,7 @@ from collections import InlineArray from memory import memcpy, UnsafePointer from utils import Span, StaticString -from sys.info import triple_is_nvidia_cuda +from sys.info import is_nvidia_gpu from builtin.io import _printf @@ -361,7 +361,7 @@ fn write_buffered[ ``` . """ - if triple_is_nvidia_cuda(): + if is_nvidia_gpu(): # Stack space is very small on GPU due to many threads, so use heap var buffer = _WriteBufferHeap[buffer_size](writer^) write_args(buffer, args, sep=sep, end=end) From 9e9f82141b536e8159724ce88b19297fa47a4fc2 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 12 Nov 2024 15:15:13 -0600 Subject: [PATCH 1862/2019] [stdlib] feat: Drop `Pythonable` requirement from argument check functions Also change to use `stack_allocation()` inside of `check_and_get_or_convert_arg()` -- this simplifies the code needed on the caller side. MODULAR_ORIG_COMMIT_REV_ID: c9ec14f33bac7d1c464058dd78ce11f448afb527 --- stdlib/src/builtin/_pybind.mojo | 19 ++++++++++++++----- stdlib/src/python/_bindings.mojo | 6 +----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index c7e1fd3703..61ca83d8c2 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer +from memory import UnsafePointer, stack_allocation from sys import sizeof, alignof from sys.ffi import OpaquePointer @@ -29,7 +29,6 @@ from python._cpython import ( from python._bindings import ( Pythonable, ConvertibleFromPython, - PythonableAndConvertibleFromPython, PyMojoObject, python_type_object, py_c_function_wrapper, @@ -117,7 +116,7 @@ fn add_wrapper_to_module[ fn check_and_get_arg[ - T: Pythonable + T: AnyType ]( func_name: StringLiteral, type_name_id: StringLiteral, @@ -127,15 +126,23 @@ fn check_and_get_arg[ return check_argument_type[T](func_name, type_name_id, py_args[index]) +# NOTE: +# @always_inline is needed so that the stack_allocation() that appears in +# the definition below is valid in the _callers_ stack frame, effectively +# allowing us to "return" a pointer to stack-allocated data from this +# function. +@always_inline fn check_and_get_or_convert_arg[ - T: PythonableAndConvertibleFromPython + T: ConvertibleFromPython ]( func_name: StringLiteral, type_name_id: StringLiteral, py_args: TypedPythonObject["Tuple"], index: Int, - converted_arg_ptr: UnsafePointer[T], ) raises -> UnsafePointer[T]: + # Stack space to hold a converted value for this argument, if needed. + var converted_arg_ptr: UnsafePointer[T] = stack_allocation[1, T]() + try: return check_and_get_arg[T](func_name, type_name_id, py_args, index) except e: @@ -147,6 +154,8 @@ fn check_and_get_or_convert_arg[ index, ) ) + # Return a pointer to stack data. Only valid because this function is + # @always_inline. return converted_arg_ptr diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index e46e3159d3..f83ff6d3a3 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -54,10 +54,6 @@ trait ConvertibleFromPython(CollectionElement): ... -trait PythonableAndConvertibleFromPython(Pythonable, ConvertibleFromPython): - pass - - # ===-----------------------------------------------------------------------===# # Mojo Object # ===-----------------------------------------------------------------------===# @@ -348,7 +344,7 @@ fn check_arguments_arity( fn check_argument_type[ - T: Pythonable, + T: AnyType ]( func_name: StringLiteral, type_name_id: StringLiteral, From 93a95216010d7b38ce80a9af5c380dd8b2a46485 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:09:32 -0800 Subject: [PATCH 1863/2019] New Mojo manual page on operators and dunder methods * New Mojo manual page documenting operators and dunder methods * An accompanying tutorial showing how to operators for a custom Mojo type * Complete example code for the tutorial MODULAR_ORIG_COMMIT_REV_ID: 40762247b51da9d5b2b958738c846a504d538248 --- docs/manual/operators.mdx | 1508 +++++++++++++++++++++++ examples/operators/README.md | 27 + examples/operators/main.mojo | 106 ++ examples/operators/mojoproject.toml | 14 + examples/operators/my_complex.mojo | 200 +++ examples/operators/test_my_complex.mojo | 242 ++++ 6 files changed, 2097 insertions(+) create mode 100644 docs/manual/operators.mdx create mode 100644 examples/operators/README.md create mode 100644 examples/operators/main.mojo create mode 100644 examples/operators/mojoproject.toml create mode 100644 examples/operators/my_complex.mojo create mode 100644 examples/operators/test_my_complex.mojo diff --git a/docs/manual/operators.mdx b/docs/manual/operators.mdx new file mode 100644 index 0000000000..e8a72e4a8c --- /dev/null +++ b/docs/manual/operators.mdx @@ -0,0 +1,1508 @@ +--- +title: Operators, expressions, and dunder methods +sidebar_label: Operators and expressions +--- + +Mojo includes a variety of operators for manipulating values of different types. +Generally, the operators are equivalent to those found in Python, though many +operators also work with additional Mojo types such as `SIMD` vectors. +Additionally, Mojo allows you to define the behavior of most of these operators +for your own custom types by implementing special *dunder* (double underscore) +methods. + +This document contains the following three sections: + +- [Operators and expressions](#operators-and-expressions) discusses Mojo's + built-in operators and how they work with commonly used Mojo types. +- [Implement operators for custom types](#implement-operators-for-custom-types) + describes the dunder methods that you can implement to support using operators + with custom structs that you create. +- [An example of implementing operators for a custom + type](#an-example-of-implementing-operators-for-a-custom-type) shows a + progressive example of writing a custom struct with support for several + operators. + +## Operators and expressions + +This section lists the operators that Mojo supports, their order or precedence +and associativity, and describes how these operators behave with several +commonly used built-in types. + +### Operator precedence and associativity + +The table below lists the various Mojo operators, along with their order of +precedence and associativity (also referred to as grouping). This table lists +operators from the highest precedence to the lowest precedence. + +| **Operators** | **Description** | **Associativity (Grouping)** | +| ------------- | --------------- | ----------------- | +| `()` | Parenthesized expression | Left to right | +| `x[index]`, `x[index:index]` | Subscripting, slicing | Left to right | +| `**` | Exponentiation | Right to left | +| `+x`, `-x`, `~x` | Positive, negative, bitwise NOT | Right to left | +| `*`, `@`, `/`, `//`, `%` | Multiplication, matrix, division, floor division, remainder | Left to right | +| `+`, `–` | Addition and subtraction | Left to right | +| `<<`, `>>` | Shifts | Left to right | +| `&` | Bitwise AND | Left to right | +| `^` | Bitwise XOR | Left to right | +| `\|` | Bitwise OR | Left to right | +| `in`, `not in`, `is`, `is not`, `<`, `<=`, `>`, `>=`, `!=`, `==` | Comparisons, membership tests, identity tests | Left to Right | +| `not x` | Boolean NOT | Right to left | +| `x and y` | Boolean AND | Left to right | +| `x or y` | Boolean OR | Left to right | +| `if-else` | Conditional expression | Right to left | +| `:=` | Assignment expression (walrus operator) | Right to left | + +Mojo supports the same operators as Python (plus a few extensions), and they +have the same precedence levels. For example, the following arithmetic +expression evaluates to 40: + +```mojo +5 + 4 * 3 ** 2 - 1 +``` + +It is equivalent to the following parenthesized expression to explicitly control +the order of evaluation: + +```mojo +(5 + (4 * (3 ** 2))) - 1 +``` + +Associativity defines how operators of the same precedence level are grouped +into expressions. The table indicates whether operators of a given level are +left- or right-associative. For example, multiplication and division are left +associative, so the following expression results in a value of 3: + +```mojo +3 * 4 / 2 / 2 +``` + +It is equivalent to the following parenthesized expression to explicitly control +the order of evaluation: + +```mojo +((3 * 4) / 2) / 2 +``` + +Whereas in the following, exponentiation operators are right associative +resulting in a value of 264,144: + +```mojo +4 ** 3 ** 2 +``` + +It is equivalent to the following parenthesized expression to explicitly control +the order of evaluation: + +```mojo +4 ** (3 ** 2) +``` + +:::note + +Mojo also uses the caret (`^`) as the [*transfer +sigil*](/mojo/manual/values/ownership#transfer-arguments-owned-and-). In +expressions where its use might be ambiguous, Mojo treats the character as the +bitwise XOR operator. For example, `x^+1` is treated as `(x)^(+1)`. + +::: + +### Arithmetic and bitwise operators + +[Numeric types](/mojo/manual/types#numeric-types) describes the different +numeric types provided by the Mojo standard library. The arithmetic and bitwise +operators have slightly different behavior depending on the types of values +provided. + +#### `Int` and `UInt` values + +The [`Int`](/mojo/stdlib/builtin/int/Int) and +[`UInt`](/mojo/stdlib/builtin/uint/UInt) types represent signed and unsigned +integers of the [word +size](https://en.wikipedia.org/wiki/Word_(computer_architecture)) of the CPU, +typically 64 bits or 32 bits. + +The `Int` and `UInt` types support all arithmetic operators except matrix +multiplication (`@`), as well as all bitwise and shift operators. If both +operands to a binary operator are `Int` values the result is an `Int`, if both +operands are `UInt` values the result is a `UInt`, and if one operand is `Int` +and the other `UInt` the result is an `Int`. The one exception for these types +is true division, `/`, which always returns a `Float64` type value. + +```mojo +var a_int: Int = -7 +var b_int: Int = 4 +sum_int = a_int + b_int # Result is type Int +print("Int sum:", sum_int) + +var i_uint: UInt = 9 +var j_uint: UInt = 8 +sum_uint = i_uint + j_uint # Result is type UInt +print("UInt sum:", sum_uint) + +sum_mixed = a_int + i_uint # Result is type Int +print("Mixed sum:", sum_mixed) + +quotient_int = a_int / b_int # Result is type Float64 +print("Int quotient:", quotient_int) +quotient_uint = i_uint / j_uint # Result is type Float64 +print("UInt quotient:", quotient_uint) +``` + +```output +Int sum: -3 +UInt sum: 17 +Mixed sum: 2 +Int quotient: -1.75 +UInt quotient: 1.125 +``` + +#### `SIMD` values + +The Mojo standard library defines the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) +type to represent a fixed-size array of values that can fit into a processor's +register. This allows you to take advantage of [single instruction, multiple +data](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) +operations in hardware to efficiently process multiple values in parallel. +`SIMD` values of a numeric [`DType`](/mojo/stdlib/builtin/dtype/DType) support +all arithmetic operators except for matrix multiplication (`@`), though the left +shift (`<<`) and right shift (`>>`) operators support only integral types. +Additionally, `SIMD` values of an integral or boolean type support all bitwise +operators. `SIMD` values apply the operators in an *elementwise* fashion, as +shown in the following example: + +```mojo +simd1 = SIMD[DType.int32, 4](2, 3, 4, 5) +simd2 = SIMD[DType.int32, 4](-1, 2, -3, 4) +simd3 = simd1 * simd2 +print(simd3) +``` + +```output +[-2, 6, -12, 20] +``` + +[`Scalar`](/mojo/stdlib/builtin/simd/) values are simply aliases for +single-element `SIMD` vectors, so `Float16` is just an alias for +`SIMD[DType.float16, 1]`. Therefore `Scalar` values support the same set of +arithmetic and bitwise operators. + +```mojo +var f1: Float16 = 2.5 +var f2: Float16 = -4.0 +var f3 = f1 * f2 # Implicitly of type Float16 +print(f3) +``` + +```output +-10.0 +``` + +When using these operators on `SIMD` values, Mojo requires both to have the same +size and `DType`, and the result is a `SIMD` of the same size and `DType`. The +operators do *not* automatically widen lower precision `SIMD` values to higher +precision. This means that the `DType` of each value must be the same or else +the result is a compilation error. + +```mojo +var i8: Int8 = 8 +var f64: Float64 = 64.0 +result = i8 * f64 +``` + +```output +error: invalid call to '__mul__': could not deduce parameter 'type' of parent struct 'SIMD' + result = i8 * f64 + ~~~^~~~~ +``` + +If you need to perform an arithmetic or bitwise operator on two `SIMD` values of +different types, you can explicitly +[`cast()`](/mojo/stdlib/builtin/simd/SIMD#cast) a `SIMD` so that they have the +same type. + +```mojo +simd1 = SIMD[DType.float32, 4](2.2, 3.3, 4.4, 5.5) +simd2 = SIMD[DType.int16, 4](-1, 2, -3, 4) +simd3 = simd1 * simd2.cast[DType.float32]() # Result is SIMD[DType.float32, 4] +print(simd3) +``` + +```output +[-2.2000000476837158, 6.5999999046325684, -13.200000762939453, 22.0] +``` + +One exception is that the exponentiation operator, `**`, is overloaded so that +you can specify an `Int` type exponent. All values in the `SIMD` are +exponentiated to the same power. + +```mojo +base_simd = SIMD[DType.float64, 4](1.1, 2.2, 3.3, 4.4) +var power: Int = 2 +pow_simd = base_simd ** power # Result is SIMD[DType.float64, 4] +print(pow_simd) +``` + +```output +[1.2100000000000002, 4.8400000000000007, 10.889999999999999, 19.360000000000003] +``` + +There are three operators related to division: + +- `/`, the "true division" operator, performs floating point division for `SIMD` + values with a floating point `DType`. For `SIMD` values with an integral + `DType`, true division *truncates* the quotient to an integral result. + + ```mojo + num_float16 = SIMD[DType.float16, 4](3.5, -3.5, 3.5, -3.5) + denom_float16 = SIMD[DType.float16, 4](2.5, 2.5, -2.5, -2.5) + + num_int32 = SIMD[DType.int32, 4](5, -6, 7, -8) + denom_int32 = SIMD[DType.int32, 4](2, 3, -4, -5) + + # Result is SIMD[DType.float16, 4] + true_quotient_float16 = num_float16 / denom_float16 + print("True float16 division:", true_quotient_float16) + + # Result is SIMD[DType.int32, 4] + true_quotient_int32 = num_int32 / denom_int32 + print("True int32 division:", true_quotient_int32) + ``` + + ```output + True float16 division: [1.400390625, -1.400390625, -1.400390625, 1.400390625] + True int32 division: [2, -2, -1, 1] + ``` + +- `//`, the "floor division" operator, performs division and *rounds down* the + result to the nearest integer. The resulting `SIMD` is still the same type as + the original operands. For example: + + ```mojo + # Result is SIMD[DType.float16, 4] + var floor_quotient_float16 = num_float16 // denom_float16 + print("Floor float16 division:", floor_quotient_float16) + + # Result is SIMD[DType.int32, 4] + var floor_quotient_int32 = num_int32 // denom_int32 + print("Floor int32 division:", floor_quotient_int32) + ``` + + ```output + Floor float16 division: [1.0, -2.0, -2.0, 1.0] + Floor int32 division: [2, -2, -2, 1] + ``` + +- `%`, the modulo operator, returns the remainder after dividing the numerator + by the denominator an integral number of times. The relationship between the + `//` and `%` operators can be defined as `num == denom * (num // denom) + (num + % denom)`. For example: + + ```mojo + # Result is SIMD[DType.float16, 4] + var remainder_float16 = num_float16 % denom_float16 + print("Modulo float16:", remainder_float16) + + # Result is SIMD[DType.int32, 4] + var remainder_int32 = num_int32 % denom_int32 + print("Modulo int32:", remainder_int32) + + print() + + # Result is SIMD[DType.float16, 4] + var result_float16 = denom_float16 * floor_quotient_float16 + remainder_float16 + print("Result float16:", result_float16) + + # Result is SIMD[DType.int32, 4] + var result_int32 = denom_int32 * floor_quotient_int32 + remainder_int32 + print("Result int32:", result_int32) + ``` + + ```output + Modulo float16: [1.0, 1.5, -1.5, -1.0] + Modulo int32: [1, 0, -1, -3] + + Result float16: [3.5, -3.5, 3.5, -3.5] + Result int32: [5, -6, 7, -8] + ``` + + +#### `IntLiteral` and `FloatLiteral` values + +[`IntLiteral`](/mojo/stdlib/builtin/int_literal/IntLiteral) and +[`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral) are +compile-time, numeric values. When they are used in a compile-time context, they +are arbitrary-precision values. When they are used in a run-time context, they +are materialized as `Int` and `Float64` type values, respectively. + +As an example, the following code causes a compile-time error because the +calculated `IntLiteral` value is too large to store in an `Int` variable: + +```mojo +alias big_int = (1 << 65) + 123456789 # IntLiteral +var too_big_int: Int = big_int +print("Result:", too_big_int) +``` + +```output +note: integer value 36893488147542560021 requires 67 bits to store, but the destination bit width is only 64 bits wide +``` + +However in the following example, taking that same `IntLiteral` value, dividing +by the `IntLiteral` 10 and then assigning the result to an `Int` variable +compiles and runs successfully, because the final `IntLiteral` quotient can fit +in a 64-bit `Int`. + +```mojo +alias big_int = (1 << 65) + 123456789 # IntLiteral +var not_too_big_int: Int = big_int // 10 +print("Result:", not_too_big_int) +``` + +```output +Result: 3689348814754256002 +``` + +In a compile-time context, `IntLiteral` and `FloatLiteral` values support all +arithmetic operators *except* exponentiation (`**`), and `IntLiteral` values +support all bitwise and shift operators. In a run-time context, materialized +`IntLiteral` values are `Int` values and therefore support the same operators as +`Int`, and materialized `FloatLiteral` values are `Float64` values and therefore +support the same operators as `Float64`. + +### Comparison operators + +Mojo supports a standard set of comparison operators: `==`, `!=`, `<`, `<=`, +`>`, and `>=`. However their behavior depends on the type of values being +compared. + +- `Int`, `UInt`, `IntLiteral`, and any type that can be implicitly converted to + `Int` or `UInt` do standard numerical comparison with a `Bool` result. +- Two `SIMD` values can be compared only if they are the same `DType` and size. + (If you need to compare two `SIMD` values of different types, you can + explicitly [`cast()`](/mojo/stdlib/builtin/simd/SIMD#cast) a `SIMD` so that + they have the same type.) Mojo performs elementwise comparison with a + `SIMD[DType.bool]` result. For example: + + ```mojo + simd1 = SIMD[DType.int16, 4](-1, 2, -3, 4) + simd2 = SIMD[DType.int16, 4](0, 1, 2, 3) + simd3 = simd1 > simd2 # SIMD[DType.bool, 4] + print(simd3) + ``` + + ```output + [False, True, False, True] + ``` + +- An integral type `SIMD` can be compared to an `IntLiteral`, `Int`, `UInt`, or + any type that can be implicitly converted to `Int` or `UInt`. Mojo performs + elementwise comparison against the value provided and produces a + `SIMD[DType.bool]` result. For example: + + ```mojo + simd1 = SIMD[DType.int16, 4](-1, 2, -3, 4) + simd2 = simd1 > 2 # SIMD[DType.bool, 4] + print(simd2) + ``` + + ```output + [False, False, False, True] + ``` + +- A floating point type `SIMD` can be compared to a `FloatLiteral`, + `IntLiteral`, `Int`, `UInt`, or any type that can be implicitly converted to + `Int` or `UInt`. Mojo performs elementwise comparison against the value + provided and produces a `SIMD[DType.bool]` result. For example: + + ```mojo + simd1 = SIMD[DType.float32, 4](1.1, -2.2, 3.3, -4.4) + simd2 = simd1 > 0.5 # SIMD[DType.bool, 4] + print(simd2) + ``` + + ```output + [True, False, True, False] + ``` + +- `Scalar` values are simply aliases for single-element `SIMD` vectors. + Therefore, the same restrictions apply against comparing different types. In + other words, you can't compare a `Float16` value to a `Float32` value unless + you cast the values to the same type. For example: + + ```mojo + var float1: Float16 = 12.345 # SIMD[DType.float16, 1] + var float2: Float32 = 0.5 # SIMD[DType.float32, 1] + result = float1.cast[DType.float32]() > float2 # SIMD[DType.bool, 1] + print(result) + ``` + + ```output + True + ``` + + :::note + + Note that the result of comparing a `Scalar` value is a `SIMD[DType.bool, 1]`, + which is not the same as a `Bool` value. However, `SIMD` values of size + 1 implement the `Boolable` trait, which provides for implicit conversion to + a `Bool` value when used in a boolean expression. + + ::: + +- `String` and `StringLiteral` values can be compared using standard + lexicographical ordering, producing a `Bool`. (For example, "Zebra" is treated + as less than "ant" because upper case letters occur before lower case letters + in the character encoding.) String comparisons are discussed further in the + [String operators](#string-operators) section below. + +Several other types in the Mojo standard library support various comparison +operators, in particular the equality and inequality comparisons. Consult the +[API documentation](/mojo/lib) for a type to determine whether any comparison +operators are supported. + +### String operators + +As discussed in [Strings](/mojo/manual/types#strings), the +[`String`](/mojo/stdlib/collections/string/String) type represents a mutable +string value. In contrast, the +[`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral) type +represents a literal string that is embedded into your compiled program. At +run-time a `StringLiteral` is loaded into memory as a constant that persists for +the duration of your program's execution. + +The `String` type has a constructor that accepts a `StringLiteral` value, which +means that a `StringLiteral` can be implicitly converted to a `String` at +run-time if you pass it as an argument to a function or assign it to a `String` +type variable. You also can use the [`String` +constructor](/mojo/stdlib/collections/string/String#__init__) to explicitly +convert the `StringLiteral` to a `String` value at run-time. + +#### String concatenation + +The `+` operator performs string concatenation. The `StringLiteral` type +supports compile-time string concatenation. + +```mojo +alias last_name = "Curie" + +# Compile-time StringLiteral alias +alias marie = "Marie " + last_name +print(marie) + +# Compile-time concatenation assigned to a run-time StringLiteral type variable +pierre = "Pierre " + last_name +print(pierre) +``` + +```output +Marie Curie +Pierre Curie +``` + +With the `String` type the `+` operator performs run-time string concatenation +to produce a new `String` value. You can also concatenate a `String` and a +`StringLiteral` to produce a new `String` result. + +```mojo +var first_name: String = "Grace" +var last_name: String = " Hopper" + +# String type result +programmer = first_name + last_name +print(programmer) + +# String type result +singer = first_name + " Slick" +print(singer) +``` + +```output +Grace Hopper +Grace Slick +``` + +#### String replication + +The `*` operator replicates a `String` a specified number of times. For example: + +```mojo +var str1: String = "la" +str2 = str1 * 5 +print(str2) +``` + +```output +lalalalala +``` + +`StringLiteral` supports the `*` operator for string replication *only* at +compile time. So the following examples compile and run successfully: + +```mojo +alias divider1 = "=" * 40 +alias symbol = "#" +alias divider2 = symbol * 40 + +# You must define the following function using `fn` because an alias +# initializer cannot call a function that can potentially raise an error. +fn generate_divider(char: StringLiteral, repeat: Int) -> StringLiteral: + return char * repeat + +alias divider3 = generate_divider("~", 40) # Evaluated at compile-time + +print(divider1) +print(divider2) +print(divider3) +``` + +```output +======================================== +######################################## +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``` + +However, the following examples fail to compile because the replication +operators would be evaluated at run-time: + +```mojo +# Both of these statements would fail to compile +var div1 = "^" * 40 +print("_" * 40) +``` + +#### String comparison + +`String` values can be compared using standard lexicographical ordering, +producing a `Bool`. (For example, "Zebra" is treated as less than "ant" because +upper case letters occur before lower case letters in the character encoding.) +`StringLiteral` values can be directly compared against each other as well. +However when comparing a `String` to a `StringLiteral` value, the `String` value +**must** be the left-hand operand to avoid a compilation error. For example: + +```mojo +var str: String = "bird" +# Compilation error: +# result = "cat" >= str +# You could explicitly convert the StringLiteral to a String: +# result = String("cat") >= str +result = str < "cat" # Successful comparison +print(result) +``` + +```output +True +``` + +#### Substring testing + +Both `String` and `StringLiteral` support using the `in` operator to produce a +`Bool` result indicating whether a given substring appears within another +string. The operator is overloaded so that you can use any combination of +`String` and `StringLiteral` for both the substring and the string to test. + +```mojo +var food: String = "peanut butter" + +if "nut" in food: + print("It contains a nut") +else: + print("It doesn't contain a nut") +``` + +```output +It contains a nut +``` + +#### String indexing and slicing + +Both the `String` and `StringLiteral` types allow you to use indexing to return +a single character. Character positions are identified with a zero-based index +starting from the first character. You can also specify a negative index to +count backwards from the end of the string, with the last character identified +by index -1. Specifying an index beyond the bounds of the string results in a +run-time error. + +```mojo +alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # StringLiteral type value +print(alphabet[0], alphabet[-1]) + +# The following would produce a run-time error +# print(alphabet[45]) +``` + +```output +A Z +``` + +The `String` type—but *not* the `StringLiteral` type—also supports slices to +return a substring from the original `String`. Providing a slice in the form +`[start:end]` returns a substring starting with the character index specified by +`start` and continuing up to but not including the character at index `end`. You +can use positive or negative indexing for both the start and end values. +Omitting `start` is the same as specifying `0`, and omitting `end` is the same +as specifying 1 plus the length of the string. + +```mojo +var alphabet: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +print(alphabet[1:4]) # The 2nd through 4th characters +print(alphabet[:6]) # The first 6 characters +print(alphabet[-6:]) # The last 6 characters +``` + +```output +BCD +ABCDEF +UVWXYZ +``` + +You can also specify a slice with a `step` value, as in `[start:end:step]` +indicating the increment between subsequent indices of the slide. (This is also +sometimes referred to as a "stride.") If you provide a negative value for +`step`, characters are selected in reverse order starting with `start` but then +with *decreasing* index values up to but not including `end`. + +```mojo +print(alphabet[1:6:2]) # The 2nd, 4th, and 6th characters +print(alphabet[-1:-4:-1]) # The last 3 characters in reverse order +print(alphabet[::-1]) # The entire string reversed +``` + +```output +BDF +ZYX +ZYXWVUTSRQPONMLKJIHGFEDCBA +``` + +### In-place assignment operators + +Mutable types that support binary arithmetic, bitwise, and shift operators +typically support equivalent in-place assignment operators. That means that for +a type that supports the `+` operator, the following two statements are +essentially equivalent: + +```mojo +a = a + b +a += b +``` + +However there is a subtle difference between the two. In the first example, the +expression `a + b` produces a new value, which is then assigned to `a`. In +contrast, the second example does an in-place modification of the value +currently assigned to `a`. For register-passable types, the compiled results +might be equivalent at run-time. But for a memory-only type, the first example +allocates storage for the result of `a + b` and then assigns the value to the +variable, whereas the second example can do an in-place modification of the +existing value. + +:::note + +A type must explicitly implement in-place assignment methods, so you might +encounter some types where in-place equivalents are not supported. + +::: + +### Assignment expressions + +The "walrus" operator, `:=`, allows you to assign a value to a variable within +an expression. The value provided is both assigned to the variable and becomes +the result of the expression. This often can simplify conditional or looping +logic. For example, consider the following prompting loop: + +```mojo +while True: + name = input("Enter a name or 'quit' to exit: ") + if name == "quit": + break + print("Hello,", name) +``` + +```output +Enter a name or 'quit' to exit: Coco +Hello, Coco +Enter a name or 'quit' to exit: Vivienne +Hello, Vivienne +Enter a name or 'quit' to exit: quit +``` + +Using the walrus operator, you can implement the same behavior like this: + +```mojo +while (name := input("Enter a name or 'quit' to exit: ")) != "quit": + print("Hello,", name) +``` + +```output +Enter a name or 'quit' to exit: Donna +Hello, Donna +Enter a name or 'quit' to exit: Vera +Hello, Vera +Enter a name or 'quit' to exit: quit +``` + +## Implement operators for custom types + +When you create a custom struct, Mojo allows you to define the behavior of many +of the built-in operators for that type by implementing special *dunder* (double +underscore) methods. This section lists the dunder methods associated with the +operators and briefly describes the requirements for implementing them. + +:::note + +Currently, Mojo doesn't support defining arbitrary custom operators (for +example, `-^-`). You can define behaviors for only the operators listed in the +following subsections. + +::: + +### Unary operator dunder methods + +A unary operator invokes an associated dunder method on the value to which it +applies. The supported unary operators and their corresponding methods are shown +in the table below. + +| **Operator** | **Dunder method** | +| --------------- | ----------------- | +| `+` positive | `__pos__()` | +| `-` negative | `__neg__()` | +| `~` bitwise NOT | `__invert__()` | + +For each of these methods that you decide to implement, you should return either +the original value if unchanged, or a new value representing the result of the +operator. For example, you could implement the `-` negative operator for a +`MyInt` struct like this: + +```mojo +@value +struct MyInt: + var value: Int + + def __neg__(self) -> Self: + return Self(-self.value) +``` + +### Binary arithmetic, shift, and bitwise operator dunder methods + +When you have a binary expression like `a + b`, there are two possible dunder +methods that could be invoked. + +Mojo first determines whether the left-hand side value (`a` in this example) has +a "normal" version of the `+` operator's dunder method defined that accepts a +value of the right-hand side's type. If so, it then invokes that method on the +left-hand side value and passes the right-hand side value as an argument. + +If Mojo doesn't find a matching "normal" dunder method on the left-hand side +value, it then checks whether the right-hand side value has a "reflected" +(sometimes referred to as "reversed") version of the `+` operator's dunder +method defined that accepts a value of the left-hand side's type. If so, it then +invokes that method on the right-hand side value and passes the left-hand side +value as an argument. + +For both the normal and the reflected versions, the dunder method should return +a new value representing the result of the operator. + +Additionally, there are dunder methods corresponding to the in-place assignment +versions of the operators. These methods receive the right-hand side value as an +argument and the methods should modify the existing left-hand side value to +reflect the result of the operator. + +The table below lists the various binary arithmetic, shift, and bitwise +operators and their corresponding normal, reflected, and in-place dunder +methods. + +| **Operator** | **Normal** | **Reflected** | **In-place** | +| ------------ | ---------- | ------------- | ------------ | +| `+` addition | `__add__()` | `__radd__()` | `__iadd__()` | +| `-` subtraction | `__sub__()` | `__rsub__()` | `__isub__()` | +| `*` multiplication | `__mul__()` | `__rmul__()` | `__imul__()` | +| `/` division | `__truediv__()` | `__rtruediv__()` | `__itruediv__()` | +| `//` floor division | `__floordiv__()` | `__rfloordiv__()` | `__ifloordiv__()` | +| `%` modulus/remainder | `__mod__()` | `__rmod__()` | `__imod__()` | +| `**` exponentiation | `__pow__()` | `__rpow__()` | `__ipow__()` | +| `@` matrix multiplication | `__matmul__()` | `__rmatmul__()` | `__imatmul__()` | +| `<<` left shift | `__lshift__()` | `__rlshift__()` | `__ilshift__()` | +| `>>` right shift | `__rshift__()` | `__rrshift__()` | `__irshift__()` | +| `&` bitwise AND | `__and__()` | `__rand__()` | `__iand__()` | +| `\|` bitwise OR | `__or__()` | `__ror__()` | `__ior__()` | +| `^` bitwise XOR | `__xor__()` | `__rxor__()` | `__ixor__()` | + +As an example, consider implementing support for all of the `+` operator dunder +methods for a custom `MyInt` struct. This shows supporting adding two `MyInt` +instances as well as adding a `MyInt` and an `Int`. We can support the case of +having the `Int` as the right-hand side argument by overloaded the definition of +`__add__()`. But to support the case of having the `Int` as the left-hand side +argument, we need to implement an `__radd__()` method, because the built-in +`Int` type doesn't have an `__add__()` method that supports our custom `MyInt` +type. + +```mojo +@value +struct MyInt: + var value: Int + + def __add__(self, rhs: MyInt) -> Self: + return MyInt(self.value + rhs.value) + + def __add__(self, rhs: Int) -> Self: + return MyInt(self.value + rhs) + + def __radd__(self, lhs: Int) -> Self: + return MyInt(self.value + lhs) + + def __iadd__(inout self, rhs: MyInt) -> None: + self.value += rhs.value + + def __iadd__(inout self, rhs: Int) -> None: + self.value += rhs +``` + +### Comparison operator dunder methods + +When you have a comparison expression like `a < b`, Mojo invokes as associated +dunder method on the left-hand side value and passes the right-hand side value +as an argument. Mojo doesn't support "reflected" versions of these dunder +methods because you should only compare values of the same type. The comparison +dunder methods must return a `Bool` result representing the result of the +comparison. + +There are two traits associated with the comparison dunder methods. A type that +implements the [`Comparable`](/mojo/stdlib/builtin/comparable/Comparable) trait +must define all of the comparison methods. However, some types don't have a +natural ordering (for example, complex numbers). For those types you can decide +to implement the +[`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable/EqualityComparable) +trait, which requires defining only the equality and inequality comparison +methods. + +The supported comparison operators and their corresponding methods are shown in +the table below. + +| **Operator** | **Dunder method** | +| ------------ | ----------------- | +| `==` equality | `__eq__()` | +| `!=` inequality | `__ne__()` | +| `<` less than | `__lt__()` | +| `<=` less than or equal | `__le__()` | +| `>` greater than | `__gt__()` | +| `>=` greater than or equal | `__ge__()` | + +:::note + +The `Comparable` and `EqualityComparable` traits don't allow the comparison +dunder methods to raise errors. Because using `def` to define a method implies +that it can raise an error, you must use `fn` to implement the comparison +methods declared by these traits. See [Functions](/mojo/manual/functions) for +more information on the differences between defining functions with `def` and +`fn`. + +::: + +As an example, consider implementing support for all of the comparison operator +dunder methods for a custom `MyInt` struct. + +```mojo +@value +struct MyInt( + Comparable +): + var value: Int + + fn __eq__(self, rhs: MyInt) -> Bool: + return self.value == rhs.value + + fn __ne__(self, rhs: MyInt) -> Bool: + return self.value != rhs.value + + fn __lt__(self, rhs: MyInt) -> Bool: + return self.value < rhs.value + + fn __le__(self, rhs: MyInt) -> Bool: + return self.value <= rhs.value + + fn __gt__(self, rhs: MyInt) -> Bool: + return self.value > rhs.value + + fn __ge__(self, rhs: MyInt) -> Bool: + return self.value >= rhs.value +``` + +### Membership operator dunder methods + +The `in` and `not in` operators depend on a type implementing the +`__contains__()` dunder method. Typically only collection types (such as `List`, +`Dict`, and `Set`) implement this method. It should accept the right-hand side +value as an argument and return a `Bool` indicating whether the value is present +in the collection or not. + +### Subscript and slicing dunder methods + +Subscripting and slicing typically apply only to sequential collection types, +like `List` and `String`. Subscripting references a single element of a +collection or a dimension of a multi-dimensional container, whereas slicing +refers to a range of values. A type supports both subscripting and slicing by +implementing the `__getitem__()` method for retrieving values and the +`__setitem__()` method for setting values. + +#### Subscripting + +In the simple case of a one-dimensional sequence, the `__getitem__()` and +`__setitem__()` methods should have signatures similar to this: + +```mojo +struct MySeq[type: CollectionElement]: + fn __getitem__(self, idx: Int) -> type: + # Return element at the given index + ... + fn __setitem__(inout self, idx: Int, value: type): + # Assign the element at the given index the provided value +``` + +It's also possible to support multi-dimensional collections, in which case you +can implement both `__getitem__()` and `__setitem__()` methods to accept +multiple index arguments—or even variadic index arguments for +arbitrary—dimension collections. + +```mojo +struct MySeq[type: CollectionElement]: + # 2-dimension support + fn __getitem__(self, x_idx: Int, y_idx: Int) -> type: + ... + # Arbitrary-dimension support + fn __getitem__(self, *indices: Int) -> type: + ... +``` + +#### Slicing + +You provide slicing support for a collection type also by implementing +`__getitem__()` and `__setitem__()` methods. But for slicing, instead of +accepting an `Int` index (or indices, in the case of a multi-dimensional +collection) you implement to methods to accept a +[`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice) (or multiple `Slice`s in +the case of a multi-dimensional collection). + +```mojo +struct MySeq[type: CollectionElement]: + # Return a new MySeq with a subset of elements + fn __getitem__(self, span: Slice) -> Self: + ... + +``` + +A `Slice` contains three fields: + +- `start` (`Optional[Int]`): The starting index of the slice +- `end` (`Optional[Int]`): The ending index of the slice +- `step` (`Optional[Int]`): The step increment value of the slice. + +Because the start, end, and step values are all optional when using slice +syntax, they are represented as `Optional[Int]` values in the `Slice`. And if +present, the index values might be negative representing a relative position +from the end of the sequence. As a convenience, `Slice` provides an `indices()` +method that accepts a `length` value and returns a 3-tuple of "normalized" +start, end, and step values for the given length, all represented as +non-negative values. You can then use these normalized values to determine the +corresponding elements of your collection being referenced. + +```mojo +struct MySeq[type: CollectionElement]: + var size: Int + + # Return a new MySeq with a subset of elements + fn __getitem__(self, span: Slice) -> Self: + var start: Int + var end: Int + var step: Int + start, end, step = span.indices(self.size) + ... + +``` + +## An example of implementing operators for a custom type + +As an example of implementing operators for a custom Mojo type, let's create a +`Complex` struct to represent a single complex number, with both the real and +imaginary components stored as `Float64` values. We'll implement most of the +arithmetic operators, the associated in-place assignment operators, the equality +comparison operators, and a few additional convenience methods to support +operations like printing complex values. We'll also allow mixing `Complex` and +`Float64` values in arithmetic expressions to produce a `Complex` result. + +This example builds our `Complex` struct incrementally. You can also find the +[complete example in the public Mojo GitHub +repo](https://github.com/modularml/mojo/tree/main/examples/operators). + +:::note + +Note that the Mojo standard library implements a parameterized +[`ComplexSIMD`](/mojo/stdlib/complex/complex/ComplexSIMD) struct that provides +support for a basic set of arithmetic operators. However, our `Complex` type +will not be based on the `ComplexSIMD` struct or be compatible with it. + +::: + +### Implement lifecycle methods + +Our `Complex` struct is an example of a simple value type consisting of trivial +numeric fields and requiring no special constructor or destructor behaviors. +This means that we can take advantage of Mojo's +[`@value`](/mojo/manual/decorators/value) decorator, which is described in +[Simple value types](/mojo/manual/lifecycle/life#value-decorator), to +automatically implement a member-wise initializer (a constructor with arguments +for each field), a copy constructor, a move constructor, and a destructor. + +```mojo +@value +struct Complex(): + var re: Float64 + var im: Float64 +``` + +This definition is enough for us to create `Complex` instances and access their +real and imaginary fields. + +```mojo +c1 = Complex(-1.2, 6.5) +print(String("c1: Real: {}; Imaginary: {}").format(c1.re, c1.im)) +``` + +```output +c1: Real: -1.2; Imaginary: 6.5 +``` + +As a convenience, let's add an explicit constructor to handle the case of +creating a `Complex` instance with an imaginary component of 0. + +```mojo +@value +struct Complex(): + var re: Float64 + var im: Float64 + + fn __init__(out self, re: Float64, im: Float64 = 0.0): + self.re = re + self.im = im +``` + +Now we can create a `Complex` instance and provide just a real component. + +```mojo +c2 = Complex(3.14159) +print(String("c2: Real: {}; Imaginary: {}").format(c2.re, c2.im)) +``` + +```output +c2: Real: 3.1415899999999999; Imaginary: 0.0 +``` + +### Implement the `Writable` and `Stringable` traits + +To make it simpler to print `Complex` values, let's implement the +[Writable](/mojo/stdlib/utils/write/Writable) trait. While we're at it, let's +also implement the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait so +that we can use the `str()` function to generate a `String` representation of a +`Complex` value. You can find out more about these traits and their associated +methods in [The `Stringable`, `Representable`, and `Writable` +traits](/mojo/manual/traits#the-stringable-representable-and-writable-traits). + +```mojo +@value +struct Complex( + Writable, + Stringable, +): + # ... + + fn __str__(self) -> String: + return String.write(self) + + fn write_to[W: Writer](self, inout writer: W): + writer.write("(", self.re) + if self.im < 0: + writer.write(" - ", -self.im) + else: + writer.write(" + ", self.im) + writer.write("i)") +``` + +:::note + +The `Writable` trait doesn't allow the `write_to()` method to raise an error and +the `Stringable` trait doesn't allow the `__str__()` method to raise an error. +Because defining a method with `def` implies that it can raise an error, we +instead have to define these methods with `fn`. See +[Functions](/mojo/manual/functions) for more information on the differences +between defining functions with `def` and `fn`. + +::: + +Now we can print a `Complex` value directly, and we can explicitly generate a +`String` representation by passing a `Complex` value to `str()`. + +```mojo +c3 = Complex(3.14159, -2.71828) +print("c3 =", c3) + +var msg: String = "The value is: " + str(c3) +print(msg) +``` + +```output +c3 = (3.1415899999999999 - 2.71828i) +The value is: (3.1415899999999999 - 2.71828i) +``` + +### Implement basic indexing + +Indexing usually is supported only by collection types. But as an example, let's +implement support for accessing the real component as index 0 and the imaginary +component as index 1. We'll not implement slicing or variadic assignment for +this example. + +```mojo + # ... + def __getitem__(self, idx: Int) -> Float64: + if idx == 0: + return self.re + elif idx == 1: + return self.im + else: + raise "index out of bounds" + + def __setitem__(inout self, idx: Int, value: Float64) -> None: + if idx == 0: + self.re = value + elif idx == 1: + self.im = value + else: + raise "index out of bounds" +``` + +Now let's try getting and setting the real and imaginary components of a +`Complex` value using indexing. + +```mojo +c2 = Complex(3.14159) +print(String("c2[0]: {}; c2[1]: {}").format(c2[0], c2[1])) +c2[0] = 2.71828 +c2[1] = 42 +print("c2[0] = 2.71828; c2[1] = 42; c2:", c2) +``` + +```output +c2[0]: 3.1415899999999999; c2[1]: 0.0 +c2[0] = 2.71828; c2[1] = 42; c2: (2.71828 + 42.0i) +``` + +### Implement arithmetic operators + +Now let's implement the dunder methods that allow us to perform arithmetic +operations on `Complex` values. (Refer to the [Wikipedia +page](https://en.wikipedia.org/wiki/Complex_number) on complex numbers for a +more in-depth explanation of the formulas for these operators.) + +#### Implement basic operators for `Complex` values + +The unary `+` operator simply returns the original value, whereas the unary `-` +operator returns a new `Complex` value with the real and imaginary components +negated. + +```mojo + # ... + def __pos__(self) -> Self: + return self + + def __neg__(self) -> Self: + return Self(-self.re, -self.im) +``` + +Let's test these out by printing the result of applying each operator. + +```mojo +c1 = Complex(-1.2, 6.5) +print("+c1:", +c1) +print("-c1:", -c1) +``` + +```output ++c1: (-1.2 + 6.5i) +-c1: (1.2 - 6.5i) +``` + +Next we'll implement the basic binary operators: `+`, `-`, `*`, and `/`. +Dividing complex numbers is a bit tricky, so we'll also define a helper method +called `norm()` to calculate the [Euclidean +norm](https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm_of_complex_numbers) +of a `Complex` instance, which can also be useful for other types of analysis +with complex numbers. + +For all of these dunder methods, the left-hand side operand is `self` and the +right-hand side operand is passed as an argument. We return a new `Complex` +value representing the result. + +```mojo +from math import sqrt + +# ... + + def __add__(self, rhs: Self) -> Self: + return Self(self.re + rhs.re, self.im + rhs.im) + + def __sub__(self, rhs: Self) -> Self: + return Self(self.re - rhs.re, self.im - rhs.im) + + def __mul__(self, rhs: Self) -> Self: + return Self( + self.re * rhs.re - self.im * rhs.im, + self.re * rhs.im + self.im * rhs.re + ) + + def __truediv__(self, rhs: Self) -> Self: + denom = rhs.squared_norm() + return Self( + (self.re * rhs.re + self.im * rhs.im) / denom, + (self.im * rhs.re - self.re * rhs.im) / denom + ) + + def squared_norm(self) -> Float64: + return self.re * self.re + self.im * self.im + + def norm(self) -> Float64: + return sqrt(self.squared_norm()) +``` + +Now we can try them out. + +```mojo +c1 = Complex(-1.2, 6.5) +c3 = Complex(3.14159, -2.71828) +print("c1 + c3 =", c1 + c3) +print("c1 - c3 =", c1 - c3) +print("c1 * c3 =", c1 * c3) +print("c1 / c3 =", c1 / c3) +``` + +```output +c1 + c3 = (1.9415899999999999 + 3.78172i) +c1 - c3 = (-4.3415900000000001 + 9.21828i) +c1 * c3 = (13.898912000000001 + 23.682270999999997i) +c1 / c3 = (-1.2422030701265261 + 0.99419218883955773i) +``` + +#### Implement overloaded arithmetic operators for `Float64` values + +Our initial set of binary arithmetic operators work fine if both operands are +`Complex` instances. But if we have a `Float64` value representing just a real +value, we'd first need to use it to create a `Complex` value before we could +add, subtract, multiply, or divide it with another `Complex` value. If we think +that this will be a common use case, it makes sense to overload our arithmetic +methods to accept a `Float64` as the second operand. + +For the case where we have `complex1 + float1`, we can just create an overloaded +definition of `__add__()`. But what about the case of `float1 + complex1`? By +default, when Mojo encounters a `+` operator it tries to invoke the `__add__()` +method of the left-hand operand, but the built-in `Float64` type doesn't +implement support for addition with a `Complex` value. This is an example where +we need to implement the `__radd__()` method on the `Complex` type. When Mojo +can't find an `__add__(self, rhs: Complex) -> Complex` method defined on +`Float64`, it uses the `__radd__(self, lhs: Float64) -> Complex` method defined +on `Complex`. + +So we can support arithmetic operations on `Complex` and `Float64` values by +implementing the following eight methods. + +```mojo + # ... + def __add__(self, rhs: Float64) -> Self: + return Self(self.re + rhs, self.im) + + def __radd__(self, lhs: Float64) -> Self: + return Self(self.re + lhs, self.im) + + def __sub__(self, rhs: Float64) -> Self: + return Self(self.re - rhs, self.im) + + def __rsub__(self, lhs: Float64) -> Self: + return Self(lhs - self.re, -self.im) + + def __mul__(self, rhs: Float64) -> Self: + return Self(self.re * rhs, self.im * rhs) + + def __rmul__(self, lhs: Float64) -> Self: + return Self(lhs * self.re, lhs * self.im) + + def __truediv__(self, rhs: Float64) -> Self: + return Self(self.re / rhs, self.im / rhs) + + def __rtruediv__(self, lhs: Float64) -> Self: + denom = self.squared_norm() + return Self( + (lhs * self.re) / denom, + (-lhs * self.im) / denom + ) +``` + +Let's see them in action. + +```mojo +c1 = Complex(-1.2, 6.5) +f1 = 2.5 +print("c1 + f1 =", c1 + f1) +print("f1 + c1 =", f1 + c1) +print("c1 - f1 =", c1 - f1) +print("f1 - c1 =", f1 - c1) +print("c1 * f1 =", c1 * f1) +print("f1 * c1 =", f1 * c1) +print("c1 / f1 =", c1 / f1) +print("f1 / c1 =", f1 / c1) +``` + +```output +c1 + f1 = (1.3 + 6.5i) +f1 + c1 = (1.3 + 6.5i) +c1 - f1 = (-3.7000000000000002 + 6.5i) +f1 - c1 = (3.7000000000000002 - 6.5i) +c1 * f1 = (-3.0 + 16.25i) +f1 * c1 = (-3.0 + 16.25i) +c1 / f1 = (-0.47999999999999998 + 2.6000000000000001i) +f1 / c1 = (-0.068665598535133904 - 0.37193865873197529i) +``` + +#### Implement in-place assignment operators + +Now let's implement support for the in-place assignment operators: `+=`, `-=`, +`*=`, and `/=`. These modify the original value, so we need to mark `self` as +being an `inout` argument and update the `re` and `im` fields instead of +returning a new `Complex` instance. And once again, we'll overload the +definitions to support both a `Complex` and a `Float64` operand. + +```mojo + # ... + def __iadd__(inout self, rhs: Self) -> None: + self.re += rhs.re + self.im += rhs.im + + def __iadd__(inout self, rhs: Float64) -> None: + self.re += rhs + + def __isub__(inout self, rhs: Self) -> None: + self.re -= rhs.re + self.im -= rhs.im + + def __isub__(inout self, rhs: Float64) -> None: + self.re -= rhs + + def __imul__(inout self, rhs: Self) -> None: + new_re = self.re * rhs.re - self.im * rhs.im + new_im = self.re * rhs.im + self.im * rhs.re + self.re = new_re + self.im = new_im + + def __imul__(inout self, rhs: Float64) -> None: + self.re *= rhs + self.im *= rhs + + def __itruediv__(inout self, rhs: Self) -> None: + denom = rhs.squared_norm() + new_re = (self.re * rhs.re + self.im * rhs.im) / denom + new_im = (self.im * rhs.re - self.re * rhs.im) / denom + self.re = new_re + self.im = new_im + + def __itruediv__(inout self, rhs: Float64) -> None: + self.re /= rhs + self.im /= rhs +``` + +And now to try them out. + +```mojo +c4 = Complex(-1, -1) +print("c4 =", c4) +c4 += Complex(0.5, -0.5) +print("c4 += Complex(0.5, -0.5) =>", c4) +c4 += 2.75 +print("c4 += 2.75 =>", c4) +c4 -= Complex(0.25, 1.5) +print("c4 -= Complex(0.25, 1.5) =>", c4) +c4 -= 3 +print("c4 -= 3 =>", c4) +c4 *= Complex(-3.0, 2.0) +print("c4 *= Complex(-3.0, 2.0) =>", c4) +c4 *= 0.75 +print("c4 *= 0.75 =>", c4) +c4 /= Complex(1.25, 2.0) +print("c4 /= Complex(1.25, 2.0) =>", c4) +c4 /= 2.0 +print("c4 /= 2.0 =>", c4) +``` + +```output +c4 = (-1.0 - 1.0i) +c4 += Complex(0.5, -0.5) => (-0.5 - 1.5i) +c4 += 2.75 => (2.25 - 1.5i) +c4 -= Complex(0.25, 1.5) => (2.0 - 3.0i) +c4 -= 3 => (-1.0 - 3.0i) +c4 *= Complex(-3.0, 2.0) => (9.0 + 7.0i) +c4 *= 0.75 => (6.75 + 5.25i) +c4 /= Complex(1.25, 2.0) => (3.404494382022472 - 1.247191011235955i) +c4 /= 2.0 => (1.702247191011236 - 0.6235955056179775i) +``` + +### Implement equality operators + +The field of complex numbers is not an ordered field, so it doesn't make sense +for us to implement the `Comparable` trait and the `>`, `>=`, `<`, and `<=` +operators. However, we can implement the `EqualityComparable` trait and the `==` +and `!=` operators. (Of course, this suffers the same limitation of comparing +floating point numbers for equality because of the limited precision of +representing floating point numbers when performing arithmetic operations. But +we'll go ahead and implement the operators for completeness.) + +```mojo +@value +struct Complex( + EqualityComparable, + Formattable, + Stringable, +): + # ... + fn __eq__(self, other: Self) -> Bool: + return self.re == other.re and self.im == other.im + + fn __ne__(self, other: Self) -> Bool: + return self.re != other.re and self.im != other.im +``` + +:::note + +The `EqualityComparable` trait doesn't allow the `__eq__()` and `__ne__()` +methods to raise errors. Because defining a method with `def` implies that it +can raise an error, we instead have to define these methods with `fn`. See +[Functions](/mojo/manual/functions) for more information on the differences +between defining functions with `def` and `fn`. + +::: + +And now to try them out. + +```mojo +c1 = Complex(-1.2, 6.5) +c3 = Complex(3.14159, -2.71828) +c5 = Complex(-1.2, 6.5) + +if c1 == c5: + print("c1 is equal to c5") +else: + print("c1 is not equal to c5") + +if c1 != c3: + print("c1 is not equal to c3") +else: + print("c1 is equal to c3") +``` + +```output +c1 is equal to c5 +c1 is not equal to c3 +``` diff --git a/examples/operators/README.md b/examples/operators/README.md new file mode 100644 index 0000000000..928b8b20ef --- /dev/null +++ b/examples/operators/README.md @@ -0,0 +1,27 @@ +# Implementing operators for a custom Mojo type + +This directory contains an example of implementing operators for a custom Mojo +struct named `Complex`, which represents a single complex number. The +`my_complex.mojo` module defines the `Complex` struct, and `main.mojo` is a +program that imports the module and shows examples of applying the operators to +instances of the `Complex` struct. The `test_my_complex.mojo` file is a set of +unit tests using the [Mojo testing +framework](https://docs.modular.com/mojo/tools/testing). + +Refer to [An example of implementing operators for a custom +type](https://docs.modular.com/mojo/manual/operators#an-example-of-implementing-operators-for-a-custom-type) +in the [Mojo manual](https://docs.modular.com/mojo/manual/) for a complete +explanation of the implementation of the `Complex` type. + +If you have [`magic`](https://docs.modular.com/magic) installed, you can +execute the example by running the following command: + +```bash +magic run mojo main.mojo +``` + +You can run the unit tests by running the following command: + +```bash +magic run test +``` diff --git a/examples/operators/main.mojo b/examples/operators/main.mojo new file mode 100644 index 0000000000..413b68cd10 --- /dev/null +++ b/examples/operators/main.mojo @@ -0,0 +1,106 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from my_complex import Complex + + +def main(): + # Examples of creating Complex instances + c1 = Complex(-1.2, 6.5) + print(String("c1: Real: {}; Imaginary: {}").format(c1.re, c1.im)) + + c2 = Complex(3.14159) + print(String("c2: Real: {}; Imaginary: {}").format(c2.re, c2.im)) + + print() + + # Examples of using Complex values with str(), repr(), and print() + c3 = Complex(3.14159, -2.71828) + print("c3 =", c3) + var msg: String = "The value is: " + str(c3) + print(msg) + print(String("{!r}").format(c3)) + + print() + + # Examples of using Complex indexing + print(String("c2[0]: {}; c2[1]: {}").format(c2[0], c2[1])) + c2[0] = 2.71828 + c2[1] = 42 + print("c2[0] = 2.71828; c2[1] = 42; c2:", c2) + + print() + + # Examples of unary arithmetic operators + print("+c1:", +c1) + print("-c1:", -c1) + + print() + + # Examples of binary arithmetic operators + print("c1 + c3 =", c1 + c3) + print("c1 - c3 =", c1 - c3) + print("c1 * c3 =", c1 * c3) + print("c1 / c3 =", c1 / c3) + + print() + + # Examples of binary arithmetic operators mixing Complex and Float64 values + f1 = 2.5 + print("c1 + f1 =", c1 + f1) + print("f1 + c1 =", f1 + c1) + print("c1 - f1 =", c1 - f1) + print("f1 - c1 =", f1 - c1) + print("c1 * f1 =", c1 * f1) + print("f1 * c1 =", f1 * c1) + print("c1 / f1 =", c1 / f1) + print("f1 / c1 =", f1 / c1) + + print() + + # Examples of in-place arithmetic operators + c4 = Complex(-1, -1) + print("c4 =", c4) + c4 += Complex(0.5, -0.5) + print("c4 += Complex(0.5, -0.5) =>", c4) + c4 += 2.75 + print("c4 += 2.75 =>", c4) + c4 -= Complex(0.25, 1.5) + print("c4 -= Complex(0.25, 1.5) =>", c4) + c4 -= 3 + print("c4 -= 3 =>", c4) + c4 *= Complex(-3.0, 2.0) + print("c4 *= Complex(-3.0, 2.0) =>", c4) + c4 *= 0.75 + print("c4 *= 0.75 =>", c4) + c4 /= Complex(1.25, 2.0) + print("c4 /= Complex(1.25, 2.0) =>", c4) + c4 /= 2.0 + print("c4 /= 2.0 =>", c4) + + print() + + # Examples of equality and inequality comparison operators + c1 = Complex(-1.2, 6.5) + c3 = Complex(3.14159, -2.71828) + c5 = Complex(-1.2, 6.5) + + if c1 == c5: + print("c1 is equal to c5") + else: + print("c1 is not equal to c5") + + if c1 != c3: + print("c1 is not equal to c3") + else: + print("c1 is equal to c3") diff --git a/examples/operators/mojoproject.toml b/examples/operators/mojoproject.toml new file mode 100644 index 0000000000..a44e6ccc05 --- /dev/null +++ b/examples/operators/mojoproject.toml @@ -0,0 +1,14 @@ +[project] +name = "Operators for custom Mojo struct" +version = "0.1.0" +description = "An example of implementing operators for a custom Mojo struct" +authors = ["Modular "] +channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +platforms = ["osx-arm64", "linux-64", "linux-aarch64"] + +[dependencies] +max = "*" + +[tasks] +main = "mojo run main.mojo" +tests = "mojo test test_*.mojo" diff --git a/examples/operators/my_complex.mojo b/examples/operators/my_complex.mojo new file mode 100644 index 0000000000..f13462dc26 --- /dev/null +++ b/examples/operators/my_complex.mojo @@ -0,0 +1,200 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from math import sqrt + + +@value +struct Complex( + Boolable, + EqualityComparable, + Writable, + Representable, + Stringable, +): + """Represents a complex value. + + The struct provides basic methods for manipulating complex values. + """ + + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var re: Float64 + var im: Float64 + + # ===-------------------------------------------------------------------===# + # Initializers + # ===-------------------------------------------------------------------===# + + fn __init__(out self, re: Float64, im: Float64 = 0.0): + self.re = re + self.im = im + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + fn __repr__(self) -> String: + result = ( + "Complex(re = " + repr(self.re) + ", im = " + repr(self.im) + ")" + ) + return result + + fn __str__(self) -> String: + return String.write(self) + + fn write_to[W: Writer](self, inout writer: W): + writer.write("(", self.re) + if self.im < 0: + writer.write(" - ", -self.im) + else: + writer.write(" + ", self.im) + writer.write("i)") + + fn __bool__(self) -> Bool: + return self.re != 0 and self.im != 0 + + # ===-------------------------------------------------------------------===# + # Indexing + # ===-------------------------------------------------------------------===# + + fn __getitem__(self, idx: Int) raises -> Float64: + if idx == 0: + return self.re + elif idx == 1: + return self.im + else: + raise "index out of bounds" + + fn __setitem__(inout self, idx: Int, value: Float64) raises: + if idx == 0: + self.re = value + elif idx == 1: + self.im = value + else: + raise "index out of bounds" + + # ===-------------------------------------------------------------------===# + # Unary arithmetic operator dunders + # ===-------------------------------------------------------------------===# + + def __neg__(self) -> Self: + return Self(-self.re, -self.im) + + def __pos__(self) -> Self: + return self + + # ===-------------------------------------------------------------------===# + # Binary arithmetic operator dunders + # ===-------------------------------------------------------------------===# + + def __add__(self, rhs: Self) -> Self: + return Self(self.re + rhs.re, self.im + rhs.im) + + def __add__(self, rhs: Float64) -> Self: + return Self(self.re + rhs, self.im) + + def __radd__(self, lhs: Float64) -> Self: + return Self(self.re + lhs, self.im) + + def __iadd__(inout self, rhs: Self): + self.re += rhs.re + self.im += rhs.im + + def __iadd__(inout self, rhs: Float64): + self.re += rhs + + def __sub__(self, rhs: Self) -> Self: + return Self(self.re - rhs.re, self.im - rhs.im) + + def __sub__(self, rhs: Float64) -> Self: + return Self(self.re - rhs, self.im) + + def __rsub__(self, lhs: Float64) -> Self: + return Self(lhs - self.re, -self.im) + + def __isub__(inout self, rhs: Self): + self.re -= rhs.re + self.im -= rhs.im + + def __isub__(inout self, rhs: Float64): + self.re -= rhs + + def __mul__(self, rhs: Self) -> Self: + return Self( + self.re * rhs.re - self.im * rhs.im, + self.re * rhs.im + self.im * rhs.re, + ) + + def __mul__(self, rhs: Float64) -> Self: + return Self(self.re * rhs, self.im * rhs) + + def __rmul__(self, lhs: Float64) -> Self: + return Self(lhs * self.re, lhs * self.im) + + def __imul__(inout self, rhs: Self): + new_re = self.re * rhs.re - self.im * rhs.im + new_im = self.re * rhs.im + self.im * rhs.re + self.re = new_re + self.im = new_im + + def __imul__(inout self, rhs: Float64): + self.re *= rhs + self.im *= rhs + + def __truediv__(self, rhs: Self) -> Self: + denom = rhs.squared_norm() + return Self( + (self.re * rhs.re + self.im * rhs.im) / denom, + (self.im * rhs.re - self.re * rhs.im) / denom, + ) + + def __truediv__(self, rhs: Float64) -> Self: + return Self(self.re / rhs, self.im / rhs) + + def __rtruediv__(self, lhs: Float64) -> Self: + denom = self.squared_norm() + return Self((lhs * self.re) / denom, (-lhs * self.im) / denom) + + def __itruediv__(inout self, rhs: Self): + denom = rhs.squared_norm() + new_re = (self.re * rhs.re + self.im * rhs.im) / denom + new_im = (self.im * rhs.re - self.re * rhs.im) / denom + self.re = new_re + self.im = new_im + + def __itruediv__(inout self, rhs: Float64): + self.re /= rhs + self.im /= rhs + + # ===-------------------------------------------------------------------===# + # Equality comparison operator dunders + # ===-------------------------------------------------------------------===# + + fn __eq__(self, other: Self) -> Bool: + return self.re == other.re and self.im == other.im + + fn __ne__(self, other: Self) -> Bool: + return self.re != other.re and self.im != other.im + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + def squared_norm(self) -> Float64: + return self.re * self.re + self.im * self.im + + def norm(self) -> Float64: + return sqrt(self.squared_norm()) diff --git a/examples/operators/test_my_complex.mojo b/examples/operators/test_my_complex.mojo new file mode 100644 index 0000000000..1e6481aef7 --- /dev/null +++ b/examples/operators/test_my_complex.mojo @@ -0,0 +1,242 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from my_complex import Complex +from testing import * + + +def test_init(): + re1 = -1.2 + im1 = 6.5 + c1 = Complex(re1, im1) + re2 = 3.14159 + im2 = 0.0 + c2 = Complex(re2) + + assert_equal(re1, c1.re) + assert_equal(im1, c1.im) + assert_equal(re2, c2.re) + assert_equal(im2, c2.im) + + +def test_str(): + re1 = 3.3 + im1 = 5.1 + str1 = "({} + {}i)".format(re1, im1) + out_str1 = String() + c1 = Complex(re1, im1) + assert_equal(str1, str(c1)) + + c1.write_to(out_str1) + assert_equal(str1, out_str1) + + re2 = -1.2 + im2 = -3.4 + str2 = "({} - {}i)".format(re2, abs(im2)) + out_str2 = String() + c2 = Complex(re2, im2) + assert_equal(str2, str(c2)) + + c2.write_to(out_str2) + assert_equal(str2, out_str2) + + +def test_indexing(): + err_msg = "index out of bounds" + re1 = -1.2 + im1 = 6.5 + c1 = Complex(re1, im1) + assert_equal(re1, c1[0]) + assert_equal(im1, c1[1]) + + re2 = 4.5 + im2 = 7.8 + c1[0] = re2 + c1[1] = im2 + assert_equal(re2, c1[0]) + assert_equal(im2, c1[1]) + + with assert_raises(contains=err_msg): + _ = c1[-1] + + with assert_raises(contains=err_msg): + _ = c1[2] + + with assert_raises(contains=err_msg): + c1[-1] = 1.0 + + with assert_raises(contains=err_msg): + c1[2] = 1.0 + + +def test_unary(): + re1 = -1.2 + im1 = 6.5 + c1 = Complex(re1, im1) + re2 = 4.5 + im2 = -7.8 + c2 = Complex(re2, im2) + + c1_pos = +c1 + assert_equal(c1.re, c1_pos.re) + assert_equal(c1.im, c1_pos.im) + + c2_pos = +c2 + assert_equal(c2.re, c2_pos.re) + assert_equal(c2.im, c2_pos.im) + + c1_neg = -c1 + assert_equal(-c1.re, c1_neg.re) + assert_equal(-c1.im, c1_neg.im) + + c2_neg = -c2 + assert_equal(-c2.re, c2_neg.re) + assert_equal(-c2.im, c2_neg.im) + + +def test_binary_complex(): + c1 = Complex(-1.2, 6.5) + c2 = Complex(3.14159, -2.71828) + + sum = c1 + c2 + diff = c1 - c2 + prod = c1 * c2 + quot = c1 / c2 + + sum_re = 1.94159 + sum_im = 3.78172 + diff_re = -4.34159 + diff_im = 9.21828 + prod_re = 13.898912 + prod_im = 23.682271 + quot_re = -1.242203 + quot_im = 0.994192 + + assert_almost_equal(sum_re, sum.re, atol=0.00001) + assert_almost_equal(sum_im, sum.im, atol=0.00001) + assert_almost_equal(diff_re, diff.re, atol=0.00001) + assert_almost_equal(diff_im, diff.im, atol=0.00001) + assert_almost_equal(prod_re, prod.re, atol=0.000001) + assert_almost_equal(prod_im, prod.im, atol=0.000001) + assert_almost_equal(quot_re, quot.re, atol=0.000001) + assert_almost_equal(quot_im, quot.im, atol=0.000001) + + +def test_binary_float(): + c1 = Complex(-1.2, 6.5) + f1 = 2.5 + + sum = c1 + f1 + diff = c1 - f1 + prod = c1 * f1 + quot = c1 / f1 + + sum_re = 1.3 + sum_im = 6.5 + diff_re = -3.7 + diff_im = 6.5 + prod_re = -3.0 + prod_im = 16.25 + quot_re = -0.48 + quot_im = 2.6 + + assert_almost_equal(sum_re, sum.re, atol=0.00001) + assert_almost_equal(sum_im, sum.im, atol=0.00001) + assert_almost_equal(diff_re, diff.re, atol=0.00001) + assert_almost_equal(diff_im, diff.im, atol=0.00001) + assert_almost_equal(prod_re, prod.re, atol=0.000001) + assert_almost_equal(prod_im, prod.im, atol=0.000001) + assert_almost_equal(quot_re, quot.re, atol=0.000001) + assert_almost_equal(quot_im, quot.im, atol=0.000001) + + +def test_binary_rfloat(): + c1 = Complex(-1.2, 6.5) + f1 = 2.5 + + sum = f1 + c1 + diff = f1 - c1 + prod = f1 * c1 + quot = f1 / c1 + + sum_re = 1.3 + sum_im = 6.5 + diff_re = 3.7 + diff_im = -6.5 + prod_re = -3.0 + prod_im = 16.25 + quot_re = -0.068666 + quot_im = -0.371939 + + assert_almost_equal(sum_re, sum.re, atol=0.00001) + assert_almost_equal(sum_im, sum.im, atol=0.00001) + assert_almost_equal(diff_re, diff.re, atol=0.00001) + assert_almost_equal(diff_im, diff.im, atol=0.00001) + assert_almost_equal(prod_re, prod.re, atol=0.000001) + assert_almost_equal(prod_im, prod.im, atol=0.000001) + assert_almost_equal(quot_re, quot.re, atol=0.000001) + assert_almost_equal(quot_im, quot.im, atol=0.000001) + + +def test_complex_inplace(): + c1 = Complex(-1, -1) + c1 += Complex(0.5, -0.5) + assert_almost_equal(-0.5, c1.re, atol=0.000001) + assert_almost_equal(-1.5, c1.im, atol=0.000001) + + c1 = Complex(-0.5, -1.5) + c1 += 2.75 + assert_almost_equal(2.25, c1.re, atol=0.000001) + assert_almost_equal(-1.5, c1.im, atol=0.000001) + + c1 = Complex(2.25, -1.5) + c1 -= Complex(0.25, 1.5) + assert_almost_equal(2.0, c1.re, atol=0.000001) + assert_almost_equal(-3.0, c1.im, atol=0.000001) + + c1 = Complex(2.0, -3.0) + c1 -= 3 + assert_almost_equal(-1.0, c1.re, atol=0.000001) + assert_almost_equal(-3.0, c1.im, atol=0.000001) + + c1 = Complex(-1.0, -3.0) + c1 *= Complex(-3.0, 2.0) + assert_almost_equal(9.0, c1.re, atol=0.000001) + assert_almost_equal(7.0, c1.im, atol=0.000001) + + c1 = Complex(9.0, 7.0) + c1 *= 0.75 + assert_almost_equal(6.75, c1.re, atol=0.000001) + assert_almost_equal(5.25, c1.im, atol=0.000001) + + c1 = Complex(6.75, 5.25) + c1 /= Complex(1.25, 2.0) + assert_almost_equal(3.404494, c1.re, atol=0.000001) + assert_almost_equal(-1.247191, c1.im, atol=0.000001) + + c1 = Complex(-9.0, 7.0) + c1 /= 2.0 + assert_almost_equal(-4.5, c1.re, atol=0.000001) + assert_almost_equal(3.5, c1.im, atol=0.000001) + + +def test_equality(): + c1 = Complex(-1.2, 6.5) + c2 = Complex(3.14159, -2.71828) + c3 = Complex(-1.2, 6.5) + + assert_false(c1 == c2) + assert_true(c1 != c2) + + assert_true(c1 == c3) + assert_false(c1 != c3) From 9026293f06e6c5fd170f908f4720a71d68910a1c Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 13 Nov 2024 12:42:24 -0800 Subject: [PATCH 1864/2019] [******][GPU] Extend the address spaces to be GPU aware MODULAR_ORIG_COMMIT_REV_ID: 86e3e1b9f8ba59d1012f88d5da528ca6f165f30c --- stdlib/src/memory/pointer.mojo | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index da1252d8aa..8655b58bcf 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -19,6 +19,8 @@ from memory import Pointer ``` """ +from sys import is_nvidia_gpu + # ===----------------------------------------------------------------------===# # AddressSpace # ===----------------------------------------------------------------------===# @@ -30,17 +32,18 @@ struct _GPUAddressSpace(EqualityComparable): var _value: Int # See https://docs.nvidia.com/cuda/nvvm-ir-spec/#address-space + # And https://llvm.org/docs/AMDGPUUsage.html#address-spaces alias GENERIC = AddressSpace(0) """Generic address space.""" alias GLOBAL = AddressSpace(1) """Global address space.""" - alias CONSTANT = AddressSpace(2) + alias CONSTANT = AddressSpace(2) if is_nvidia_gpu() else AddressSpace(4) """Constant address space.""" alias SHARED = AddressSpace(3) """Shared address space.""" alias PARAM = AddressSpace(4) """Param address space.""" - alias LOCAL = AddressSpace(5) + alias LOCAL = AddressSpace(5) if is_nvidia_gpu() else AddressSpace(3) """Local address space.""" @always_inline("nodebug") From ea851cf703573fc0769847c598483cab3dba0f8e Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 13 Nov 2024 18:48:56 -0600 Subject: [PATCH 1865/2019] [stdlib] feat: Add CPython wrapper for `PyType_GenericAlloc()` MODULAR_ORIG_COMMIT_REV_ID: c3553f83d8ff65680949a3119a312aeed7c3fa24 --- stdlib/src/python/_cpython.mojo | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 9e7983cfbe..b8c8c8266a 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -1177,6 +1177,13 @@ struct CPython: """ return self.lib.call["PyType_FromSpec", PyObjectPtr](spec) + fn PyType_GenericAlloc( + inout self, + type: UnsafePointer[PyTypeObject], + nitems: Py_ssize_t, + ) -> PyObjectPtr: + return self.lib.call["PyType_GenericAlloc", PyObjectPtr](type, nitems) + # ===-------------------------------------------------------------------===# # Python Evaluation # ===-------------------------------------------------------------------===# From 594eb2191955f87aae43d4b3cb4f376c9af0b6d1 Mon Sep 17 00:00:00 2001 From: Alex Trotta <44127594+Ahajha@users.noreply.github.com> Date: Wed, 13 Nov 2024 20:40:30 -0500 Subject: [PATCH 1866/2019] [examples] Add trailing slash to max-nightly conda channel for consistency MODULAR_ORIG_COMMIT_REV_ID: 48e5472978812d786082d4c5906daf844e8a5da0 --- examples/operators/mojoproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/operators/mojoproject.toml b/examples/operators/mojoproject.toml index a44e6ccc05..961e83f6b6 100644 --- a/examples/operators/mojoproject.toml +++ b/examples/operators/mojoproject.toml @@ -3,7 +3,7 @@ name = "Operators for custom Mojo struct" version = "0.1.0" description = "An example of implementing operators for a custom Mojo struct" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly"] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] platforms = ["osx-arm64", "linux-64", "linux-aarch64"] [dependencies] From 24bb9d7a7a7e3b6c176439538e0c699f69f50b76 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 13 Nov 2024 18:06:15 -0800 Subject: [PATCH 1867/2019] [******][GPU] Add a check if the target is AMD GPU MODULAR_ORIG_COMMIT_REV_ID: e6882d6be8b161bf493fd1dafe0c727f4f4cb428 --- stdlib/src/sys/info.mojo | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index d1aa1837de..8d404bbdcc 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -448,6 +448,27 @@ fn is_nvidia_gpu[subarch: StringLiteral]() -> Bool: return is_nvidia_gpu() and StringLiteral(_current_arch()) == subarch +@always_inline("nodebug") +fn is_amd_gpu() -> Bool: + """Returns True if the target triple of the compiler is `amdgcn-amd-amdhsa` + False otherwise. + + Returns: + True if the triple target is amdgpu and False otherwise. + """ + return is_triple["amdgcn-amd-amdhsa"]() + + +@always_inline("nodebug") +fn is_gpu() -> Bool: + """Returns True if the target triple is GPU and False otherwise. + + Returns: + True if the triple target is GPU and False otherwise. + """ + return is_nvidia_gpu() or is_amd_gpu() + + @always_inline("nodebug") fn is_little_endian[ target: __mlir_type.`!kgen.target` = _current_target() From 3a5e96f30df5d558cdd149861d6d0c2d0245311a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 13 Nov 2024 18:09:45 -0800 Subject: [PATCH 1868/2019] [******][GPU] Add the Float8e5m2 and Float8e4m3 scalar types MODULAR_ORIG_COMMIT_REV_ID: cb3ebf296399f5166893aa5d6e7d5c8560bdfb49 --- stdlib/src/builtin/simd.mojo | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 560c88937e..9238ca42f7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -91,6 +91,10 @@ alias Int64 = Scalar[DType.int64] alias UInt64 = Scalar[DType.uint64] """Represents a 64-bit unsigned scalar integer.""" +alias Float8e5m2 = Scalar[DType.float8e5m2] +"""Represents a FP8E5M2 floating point format whose bitwidth is 8.""" +alias Float8e4m3 = Scalar[DType.float8e4m3] +"""Represents a FP8E4M3 floating point format whose bitwidth is 8.""" alias BFloat16 = Scalar[DType.bfloat16] """Represents a 16-bit brain floating point value.""" alias Float16 = Scalar[DType.float16] From cc83560d35dc656f07eb2b1844f3add5cf1960cc Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 13 Nov 2024 18:38:10 -0800 Subject: [PATCH 1869/2019] [******][GPU] Make unsafe_pointer GPU agnostic MODULAR_ORIG_COMMIT_REV_ID: 85797bf247f55c80748fc56d468a9d8c2e0e2767 --- stdlib/src/memory/unsafe_pointer.mojo | 6 +++--- stdlib/src/sys/__init__.mojo | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 1c5676870c..4d46ff1f68 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,7 +19,7 @@ from memory import UnsafePointer ``` """ -from sys import alignof, sizeof, is_nvidia_gpu +from sys import alignof, sizeof, is_nvidia_gpu, is_gpu from sys.intrinsics import ( _mlirtype_is_eq, _type_is_eq, @@ -40,7 +40,7 @@ from memory.memory import _free, _malloc @always_inline fn _default_alignment[type: AnyType]() -> Int: - return alignof[type]() if is_nvidia_gpu() else 1 + return alignof[type]() if is_gpu() else 1 @always_inline @@ -842,7 +842,7 @@ struct UnsafePointer[ type: DType, //, *, width: Int = 1, - alignment: Int = alignof[SIMD[type, width]]() if is_nvidia_gpu() else 1, + alignment: Int = _default_alignment[type, width](), ]( self: UnsafePointer[Scalar[type], *_, **_], offset: SIMD[_, width], diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 0101272996..1a36b9f69d 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -49,6 +49,7 @@ from .info import ( warpsize, sizeof, is_nvidia_gpu, + is_gpu, ) from .intrinsics import ( PrefetchCache, From 9e87e64724845eab7e2bbecda8bc3b319baa23d0 Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 14 Nov 2024 10:26:40 -0600 Subject: [PATCH 1870/2019] [External] [stdlib] Fix `math.math.mojo` examples (#50830) [External] [stdlib] Fix `math.math.mojo` examples Part of https://github.com/modularml/mojo/issues/3572. Co-authored-by: soraros Closes modularml/mojo#3763 MODULAR_ORIG_COMMIT_REV_ID: 9b499c7e23f0fc7cccc3ab3ff75326364d032fe8 --- stdlib/src/math/math.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 6083f4de83..6b3876a989 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -2661,7 +2661,7 @@ trait Truncable: var im: Float64 fn __trunc__(self) -> Self: - return Self(trunc(re), trunc(im)) + return Self(trunc(self.re), trunc(self.im)) ``` """ From c656c31430568f803c720a264be21fcadeb62c53 Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 14 Nov 2024 11:14:57 -0600 Subject: [PATCH 1871/2019] [External] [stdlib] Clean up `b64encode` (2/N) (#50831) [External] [stdlib] Clean up `b64encode` (2/N) Co-authored-by: soraros Closes modularml/mojo#3746 MODULAR_ORIG_COMMIT_REV_ID: e5bf916a6cc953c18bcb23b482de4a86778d5f52 --- stdlib/src/base64/_b64encode.mojo | 120 +++++++----------------------- stdlib/src/math/math.mojo | 4 +- 2 files changed, 29 insertions(+), 95 deletions(-) diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo index d867a91be5..8dbe9b2a46 100644 --- a/stdlib/src/base64/_b64encode.mojo +++ b/stdlib/src/base64/_b64encode.mojo @@ -26,20 +26,13 @@ https://arxiv.org/abs/1704.00605 from builtin.simd import _sub_with_saturation from collections import InlineArray -from math.math import _compile_time_iota +from math.math import _iota from memory import memcpy, bitcast, UnsafePointer from utils import IndexList alias Bytes = SIMD[DType.uint8, _] -fn _base64_simd_mask[ - simd_width: Int -](nb_value_to_load: Int) -> SIMD[DType.bool, simd_width]: - alias mask = _compile_time_iota[DType.uint8, simd_width]() - return mask < UInt8(nb_value_to_load) - - # | |---- byte 2 ----|---- byte 1 ----|---- byte 0 ----| # | |c₁c₀d₅d₄d₃d₂d₁d₀|b₃b₂b₁b₀c₅c₄c₃c₂|a₅a₄a₃a₂a₁a₀b₅b₄| # <----------------|----------------|----------------|----------------| @@ -115,99 +108,20 @@ fn _to_b64_ascii[width: Int, //](input: Bytes[width]) -> Bytes[width]: return abcd + OFFSETS._dynamic_shuffle(offset_indices) -fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ - simd_width: Int -]() -> SIMD[DType.uint8, simd_width]: - """This is a lookup table to know how many bytes we need to store in the output buffer - for a given number of bytes to encode in base64. Including the '=' sign. - - This table lookup is smaller than the simd size, because we only use it for the last chunk. - This should be called at compile time, otherwise it's quite slow. - """ - var result = SIMD[DType.uint8, simd_width](0) - for i in range(1, simd_width): - # We have "i" bytes to encode in base64, how many bytes do - # we need to store in the output buffer? Including the '=' sign. - - # math.ceil cannot be called at compile time, this is a workaround - var group_of_3_bytes = i // 3 - if i % 3 != 0: - group_of_3_bytes += 1 - - result[i] = group_of_3_bytes * 4 - return result - - fn _get_number_of_bytes_to_store_from_number_of_bytes_to_load[ max_size: Int ](nb_of_elements_to_load: Int) -> Int: - alias table = _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ - max_size - ]() + alias table = _ceildiv_u8(_iota[DType.uint8, max_size](), 3) * 4 return int(table[nb_of_elements_to_load]) -fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ - simd_width: Int -]() -> SIMD[DType.uint8, simd_width]: - """This is a lookup table to know how many bytes we need to store in the output buffer - for a given number of bytes to encode in base64. This is **not** including the '=' sign. - - This table lookup is smaller than the simd size, because we only use it for the last chunk. - This should be called at compile time, otherwise it's quite slow. - """ - var result = SIMD[DType.uint8, simd_width]() - for i in range(simd_width): - # We have "i" bytes to encode in base64, how many bytes do - # we need to store in the output buffer? NOT including the '=' sign. - # We count the number of groups of 6 bits and we add 1 byte if there is an incomplete group. - var number_of_bits = i * 8 - var complete_groups_of_6_bits = number_of_bits // 6 - var incomplete_groups_of_6_bits: Int - if i * 8 % 6 == 0: - incomplete_groups_of_6_bits = 0 - else: - incomplete_groups_of_6_bits = 1 - - result[i] = complete_groups_of_6_bits + incomplete_groups_of_6_bits - return result - - fn _get_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ max_size: Int ](nb_of_elements_to_load: Int) -> Int: - alias table = _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ - max_size - ]() + alias table = _ceildiv_u8(_iota[DType.uint8, max_size]() * 8, 6) return int(table[nb_of_elements_to_load]) -fn load_incomplete_simd[ - simd_width: Int -](pointer: UnsafePointer[UInt8], nb_of_elements_to_load: Int) -> SIMD[ - DType.uint8, simd_width -]: - var result = SIMD[DType.uint8, simd_width](0) - var tmp_buffer_pointer = UnsafePointer.address_of(result).bitcast[UInt8]() - memcpy(dest=tmp_buffer_pointer, src=pointer, count=nb_of_elements_to_load) - return result - - -fn store_incomplete_simd[ - simd_width: Int -]( - pointer: UnsafePointer[UInt8], - owned simd_vector: SIMD[DType.uint8, simd_width], - nb_of_elements_to_store: Int, -): - var tmp_buffer_pointer = UnsafePointer.address_of(simd_vector).bitcast[ - UInt8 - ]() - - memcpy(dest=pointer, src=tmp_buffer_pointer, count=nb_of_elements_to_store) - _ = simd_vector # We make it live long enough - - # TODO: Use Span instead of List as input when Span is easier to use @no_inline fn b64encode_with_buffers( @@ -242,9 +156,9 @@ fn b64encode_with_buffers( ) # We don't want to read past the input buffer - var input_vector = load_incomplete_simd[simd_width]( + var input_vector = load_simd[simd_width]( start_of_input_chunk, - nb_of_elements_to_load=nb_of_elements_to_load, + nb_of_elements_to_load, ) result_vector = _to_b64_ascii(input_vector) @@ -255,7 +169,9 @@ fn b64encode_with_buffers( ]( nb_of_elements_to_load ) - var equal_mask = _base64_simd_mask[simd_width](non_equal_chars_number) + var equal_mask = _iota[ + DType.uint8, simd_width + ]() < non_equal_chars_number var result_vector_with_equals = equal_mask.select( result_vector, equal_vector @@ -266,7 +182,7 @@ fn b64encode_with_buffers( ]( nb_of_elements_to_load ) - store_incomplete_simd( + store_simd( result.unsafe_ptr() + len(result), result_vector_with_equals, nb_of_elements_to_store, @@ -278,6 +194,10 @@ fn b64encode_with_buffers( # Utility functions +fn _ceildiv_u8(a: Bytes, b: __type_of(a)) -> __type_of(a): + return (a + b - 1) / b + + fn _repeat_until[width: Int](v: SIMD) -> SIMD[v.type, width]: constrained[width >= v.size, "width must be at least v.size"]() @@ -291,3 +211,17 @@ fn _rshift_bits_in_u16[shift: Int](input: Bytes) -> __type_of(input): var u16 = bitcast[DType.uint16, input.size // 2](input) var res = bit.rotate_bits_right[shift](u16) return bitcast[DType.uint8, input.size](res) + + +fn load_simd[ + width: Int +](pointer: UnsafePointer[Byte], len: Int) -> Bytes[width]: + var result = Bytes[width]() + var buffer_ptr = UnsafePointer.address_of(result).bitcast[Byte]() + memcpy(dest=buffer_ptr, src=pointer, count=len) + return result + + +fn store_simd(ptr: UnsafePointer[Byte], owned v: Bytes, len: Int): + var buffer_ptr = UnsafePointer.address_of(v).bitcast[Byte]() + memcpy(dest=ptr, src=buffer_ptr, count=len) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 6b3876a989..76c4c462bb 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1049,10 +1049,10 @@ fn isclose[ # TODO: Remove this when `iota` works at compile-time -fn _compile_time_iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: +fn _iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: constrained[ type.is_integral(), - "_compile_time_iota can only be used with integer types.", + "_iota can only be used with integer types.", ]() var a = SIMD[type, simd_width](0) for i in range(simd_width): From 31990b89d6892cd6da075883c11815f1054a841e Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 14 Nov 2024 11:17:07 -0600 Subject: [PATCH 1872/2019] [External] [stdlib] Make `StringRef` Representable (#50840) [External] [stdlib] Make `StringRef` Representable - [X] Implement `StringRef.__repr__()` - [X] Implement `Writable` for `os.stat_result` Co-authored-by: Manuel Saelices Closes modularml/mojo#3736 MODULAR_ORIG_COMMIT_REV_ID: f8fe27aa7b0274fa27665b8cd0b19af22f3cac3b --- docs/changelog.md | 3 ++ stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/uint.mojo | 2 +- stdlib/src/os/fstat.mojo | 49 +++++++++++++++++---------- stdlib/src/utils/stringref.mojo | 22 ++++++++---- stdlib/test/pathlib/test_pathlib.mojo | 29 ++++++++++++++++ stdlib/test/utils/test_stringref.mojo | 20 +++++++++++ 8 files changed, 102 insertions(+), 27 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 935b6f5680..c232260e01 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,9 @@ what we publish. ### ⭐️ New +- `StringRef` is now representable so `repr(StringRef("hello"))` will return + `StringRef('hello')`. + - Mojo can now interpret simple LLVM intrinsics in parameter expressions, enabling things like `count_leading_zeros` to work at compile time: [Issue #933](https://github.com/modularml/mojo/issues/933). diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index e0b81dadc7..fa7a222492 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -217,7 +217,7 @@ struct DType( Returns: The representation of the dtype. """ - return "DType." + str(self) + return String.write("DType.", self) @always_inline("nodebug") fn get_value(self) -> __mlir_type.`!kgen.dtype`: diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 482f4aa454..5e74bdff77 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -180,7 +180,7 @@ struct Error( Returns: A printable representation of the error message. """ - return "Error(" + repr(self._message()) + ")" + return String.write("Error(", repr(self._message()), ")") # ===-------------------------------------------------------------------===# # Methods diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index cc9f9b8d22..03bb691300 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -149,7 +149,7 @@ struct UInt(IntLike, _HashableWithHasher): Returns: The string representation of this UInt. """ - return "UInt(" + str(self) + ")" + return String.write("UInt(", str(self), ")") fn __hash__(self) -> UInt: """Hash the UInt using builtin hash. diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index f3d367944e..ba298916d4 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -46,7 +46,7 @@ fn _constrain_unix(): @value -struct stat_result(Stringable): +struct stat_result(Stringable, Writable): """Object whose fields correspond to the members of the stat structure.""" var st_mode: Int @@ -150,6 +150,35 @@ struct stat_result(Stringable): self.st_rdev = st_rdev self.st_flags = st_flags + @no_inline + fn write_to[W: Writer](self, inout writer: W): + """ + Formats this path to the provided Writer. + + Parameters: + W: A type conforming to the Writable trait. + + Args: + writer: The object to write to. + """ + writer.write("os.stat_result(") + writer.write("st_mode=", self.st_mode) + writer.write(", st_ino=", self.st_ino) + writer.write(", st_dev=", self.st_dev) + writer.write(", st_nlink=", self.st_nlink) + writer.write(", st_uid=", self.st_uid) + writer.write(", st_gid=", self.st_gid) + writer.write(", st_size=", self.st_size) + writer.write(", st_atime=", str(self.st_atimespec)) + writer.write(", st_mtime=", str(self.st_mtimespec)) + writer.write(", st_ctime=", str(self.st_ctimespec)) + writer.write(", st_birthtime=", str(self.st_birthtimespec)) + writer.write(", st_blocks=", self.st_blocks) + writer.write(", st_blksize=", self.st_blksize) + writer.write(", st_rdev=", self.st_rdev) + writer.write(", st_flags=", self.st_flags) + writer.write(")") + @no_inline fn __str__(self) -> String: """Constructs a string representation of stat_result. @@ -157,23 +186,7 @@ struct stat_result(Stringable): Returns: A string representation of stat_result. """ - var res = String("os.stat_result(") - res += "st_mode=" + str(self.st_mode) - res += ", st_ino=" + str(self.st_ino) - res += ", st_dev=" + str(self.st_dev) - res += ", st_nlink=" + str(self.st_nlink) - res += ", st_uid=" + str(self.st_uid) - res += ", st_gid=" + str(self.st_gid) - res += ", st_size=" + str(self.st_size) - res += ", st_atime=" + str(self.st_atimespec) - res += ", st_mtime=" + str(self.st_mtimespec) - res += ", st_ctime=" + str(self.st_ctimespec) - res += ", st_birthtime=" + str(self.st_birthtimespec) - res += ", st_blocks=" + str(self.st_blocks) - res += ", st_blksize=" + str(self.st_blksize) - res += ", st_rdev=" + str(self.st_rdev) - res += ", st_flags=" + str(self.st_flags) - return res + ")" + return String.write(self) fn __repr__(self) -> String: """Constructs a representation of stat_result. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 8406c7451e..0d1ede078e 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -41,17 +41,18 @@ fn _align_down(value: Int, alignment: Int) -> Int: @value @register_passable("trivial") struct StringRef( - Sized, - IntableRaising, + AsBytes, + Boolable, CollectionElement, CollectionElementNew, + Comparable, + Hashable, + IntableRaising, + Representable, + Sized, Stringable, Writable, - Hashable, _HashableWithHasher, - Boolable, - Comparable, - AsBytes, ): """ Represent a constant reference to a string, i.e. a sequence of characters @@ -396,6 +397,15 @@ struct StringRef( """ return String.write(self) + @no_inline + fn __repr__(self) -> String: + """Convert the string reference to a string. + + Returns: + The String representation of the StringRef. + """ + return String.write("StringRef(", repr(str(self)), ")") + @no_inline fn write_to[W: Writer](self, inout writer: W): """ diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 249d5b2133..fc54c8f8d4 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -148,6 +148,34 @@ def test_home(): set_home(original_home) +def test_stat(): + var path = Path(__source_location().file_name) + var stat = path.stat() + assert_equal( + str(stat), + "os.stat_result(st_mode={}, st_ino={}, st_dev={}, st_nlink={}," + " st_uid={}, st_gid={}, st_size={}, st_atime={}, st_mtime={}," + " st_ctime={}, st_birthtime={}, st_blocks={}, st_blksize={}," + " st_rdev={}, st_flags={})".format( + stat.st_mode, + stat.st_ino, + stat.st_dev, + stat.st_nlink, + stat.st_uid, + stat.st_gid, + stat.st_size, + str(stat.st_atimespec), + str(stat.st_mtimespec), + str(stat.st_ctimespec), + str(stat.st_birthtimespec), + stat.st_blocks, + stat.st_blksize, + stat.st_rdev, + stat.st_flags, + ), + ) + + def main(): test_cwd() test_path() @@ -159,3 +187,4 @@ def main(): test_read_write() test_expand_user() test_home() + test_stat() diff --git a/stdlib/test/utils/test_stringref.mojo b/stdlib/test/utils/test_stringref.mojo index 33c18c533f..0dc7686197 100644 --- a/stdlib/test/utils/test_stringref.mojo +++ b/stdlib/test/utils/test_stringref.mojo @@ -169,6 +169,25 @@ fn test_stringref_split() raises: assert_equal(res4[1], "o") +def test_str_and_ref(): + assert_equal(StringRef("abc").__str__(), "abc") + assert_equal(StringRef("abc").__repr__(), "StringRef('abc')") + assert_equal(StringRef("\0").__repr__(), r"StringRef('\x00')") + assert_equal(StringRef("\x09").__repr__(), r"StringRef('\t')") + assert_equal(StringRef("\n").__repr__(), r"StringRef('\n')") + assert_equal(StringRef("\x0d").__repr__(), r"StringRef('\r')") + assert_equal(StringRef("'").__repr__(), 'StringRef("\'")') + + # Multi-byte characters.__repr__() + assert_equal( + StringRef("Örnsköldsvik").__repr__(), "StringRef('Örnsköldsvik')" + ) # 2-byte + assert_equal(StringRef("你好!").__repr__(), "StringRef('你好!')") # 3-byte + assert_equal( + StringRef("hello 🔥!").__repr__(), "StringRef('hello 🔥!')" + ) # 4-byte + + def main(): test_strref_from_start() test_stringref_split() @@ -177,3 +196,4 @@ def main(): test_indexing() test_find() test_endswith() + test_str_and_ref() From 7969c3ec5e51981e72575516c78177cf50e0081c Mon Sep 17 00:00:00 2001 From: Alex Trotta <44127594+Ahajha@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:17:59 -0500 Subject: [PATCH 1873/2019] [stdlib] Revert "Clean up b64encode (2/N)" This change passed tests initially but caused issues specifically on linux + Intel after being merged internally. Reverting fixes the issue for now while we investigate. MODULAR_ORIG_COMMIT_REV_ID: 0aa612d4d89883645c0649593f8f306d31d35268 --- stdlib/src/base64/_b64encode.mojo | 120 +++++++++++++++++++++++------- stdlib/src/math/math.mojo | 4 +- 2 files changed, 95 insertions(+), 29 deletions(-) diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo index 8dbe9b2a46..d867a91be5 100644 --- a/stdlib/src/base64/_b64encode.mojo +++ b/stdlib/src/base64/_b64encode.mojo @@ -26,13 +26,20 @@ https://arxiv.org/abs/1704.00605 from builtin.simd import _sub_with_saturation from collections import InlineArray -from math.math import _iota +from math.math import _compile_time_iota from memory import memcpy, bitcast, UnsafePointer from utils import IndexList alias Bytes = SIMD[DType.uint8, _] +fn _base64_simd_mask[ + simd_width: Int +](nb_value_to_load: Int) -> SIMD[DType.bool, simd_width]: + alias mask = _compile_time_iota[DType.uint8, simd_width]() + return mask < UInt8(nb_value_to_load) + + # | |---- byte 2 ----|---- byte 1 ----|---- byte 0 ----| # | |c₁c₀d₅d₄d₃d₂d₁d₀|b₃b₂b₁b₀c₅c₄c₃c₂|a₅a₄a₃a₂a₁a₀b₅b₄| # <----------------|----------------|----------------|----------------| @@ -108,20 +115,99 @@ fn _to_b64_ascii[width: Int, //](input: Bytes[width]) -> Bytes[width]: return abcd + OFFSETS._dynamic_shuffle(offset_indices) +fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ + simd_width: Int +]() -> SIMD[DType.uint8, simd_width]: + """This is a lookup table to know how many bytes we need to store in the output buffer + for a given number of bytes to encode in base64. Including the '=' sign. + + This table lookup is smaller than the simd size, because we only use it for the last chunk. + This should be called at compile time, otherwise it's quite slow. + """ + var result = SIMD[DType.uint8, simd_width](0) + for i in range(1, simd_width): + # We have "i" bytes to encode in base64, how many bytes do + # we need to store in the output buffer? Including the '=' sign. + + # math.ceil cannot be called at compile time, this is a workaround + var group_of_3_bytes = i // 3 + if i % 3 != 0: + group_of_3_bytes += 1 + + result[i] = group_of_3_bytes * 4 + return result + + fn _get_number_of_bytes_to_store_from_number_of_bytes_to_load[ max_size: Int ](nb_of_elements_to_load: Int) -> Int: - alias table = _ceildiv_u8(_iota[DType.uint8, max_size](), 3) * 4 + alias table = _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load[ + max_size + ]() return int(table[nb_of_elements_to_load]) +fn _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ + simd_width: Int +]() -> SIMD[DType.uint8, simd_width]: + """This is a lookup table to know how many bytes we need to store in the output buffer + for a given number of bytes to encode in base64. This is **not** including the '=' sign. + + This table lookup is smaller than the simd size, because we only use it for the last chunk. + This should be called at compile time, otherwise it's quite slow. + """ + var result = SIMD[DType.uint8, simd_width]() + for i in range(simd_width): + # We have "i" bytes to encode in base64, how many bytes do + # we need to store in the output buffer? NOT including the '=' sign. + # We count the number of groups of 6 bits and we add 1 byte if there is an incomplete group. + var number_of_bits = i * 8 + var complete_groups_of_6_bits = number_of_bits // 6 + var incomplete_groups_of_6_bits: Int + if i * 8 % 6 == 0: + incomplete_groups_of_6_bits = 0 + else: + incomplete_groups_of_6_bits = 1 + + result[i] = complete_groups_of_6_bits + incomplete_groups_of_6_bits + return result + + fn _get_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ max_size: Int ](nb_of_elements_to_load: Int) -> Int: - alias table = _ceildiv_u8(_iota[DType.uint8, max_size]() * 8, 6) + alias table = _get_table_number_of_bytes_to_store_from_number_of_bytes_to_load_without_equal_sign[ + max_size + ]() return int(table[nb_of_elements_to_load]) +fn load_incomplete_simd[ + simd_width: Int +](pointer: UnsafePointer[UInt8], nb_of_elements_to_load: Int) -> SIMD[ + DType.uint8, simd_width +]: + var result = SIMD[DType.uint8, simd_width](0) + var tmp_buffer_pointer = UnsafePointer.address_of(result).bitcast[UInt8]() + memcpy(dest=tmp_buffer_pointer, src=pointer, count=nb_of_elements_to_load) + return result + + +fn store_incomplete_simd[ + simd_width: Int +]( + pointer: UnsafePointer[UInt8], + owned simd_vector: SIMD[DType.uint8, simd_width], + nb_of_elements_to_store: Int, +): + var tmp_buffer_pointer = UnsafePointer.address_of(simd_vector).bitcast[ + UInt8 + ]() + + memcpy(dest=pointer, src=tmp_buffer_pointer, count=nb_of_elements_to_store) + _ = simd_vector # We make it live long enough + + # TODO: Use Span instead of List as input when Span is easier to use @no_inline fn b64encode_with_buffers( @@ -156,9 +242,9 @@ fn b64encode_with_buffers( ) # We don't want to read past the input buffer - var input_vector = load_simd[simd_width]( + var input_vector = load_incomplete_simd[simd_width]( start_of_input_chunk, - nb_of_elements_to_load, + nb_of_elements_to_load=nb_of_elements_to_load, ) result_vector = _to_b64_ascii(input_vector) @@ -169,9 +255,7 @@ fn b64encode_with_buffers( ]( nb_of_elements_to_load ) - var equal_mask = _iota[ - DType.uint8, simd_width - ]() < non_equal_chars_number + var equal_mask = _base64_simd_mask[simd_width](non_equal_chars_number) var result_vector_with_equals = equal_mask.select( result_vector, equal_vector @@ -182,7 +266,7 @@ fn b64encode_with_buffers( ]( nb_of_elements_to_load ) - store_simd( + store_incomplete_simd( result.unsafe_ptr() + len(result), result_vector_with_equals, nb_of_elements_to_store, @@ -194,10 +278,6 @@ fn b64encode_with_buffers( # Utility functions -fn _ceildiv_u8(a: Bytes, b: __type_of(a)) -> __type_of(a): - return (a + b - 1) / b - - fn _repeat_until[width: Int](v: SIMD) -> SIMD[v.type, width]: constrained[width >= v.size, "width must be at least v.size"]() @@ -211,17 +291,3 @@ fn _rshift_bits_in_u16[shift: Int](input: Bytes) -> __type_of(input): var u16 = bitcast[DType.uint16, input.size // 2](input) var res = bit.rotate_bits_right[shift](u16) return bitcast[DType.uint8, input.size](res) - - -fn load_simd[ - width: Int -](pointer: UnsafePointer[Byte], len: Int) -> Bytes[width]: - var result = Bytes[width]() - var buffer_ptr = UnsafePointer.address_of(result).bitcast[Byte]() - memcpy(dest=buffer_ptr, src=pointer, count=len) - return result - - -fn store_simd(ptr: UnsafePointer[Byte], owned v: Bytes, len: Int): - var buffer_ptr = UnsafePointer.address_of(v).bitcast[Byte]() - memcpy(dest=ptr, src=buffer_ptr, count=len) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 76c4c462bb..6b3876a989 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1049,10 +1049,10 @@ fn isclose[ # TODO: Remove this when `iota` works at compile-time -fn _iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: +fn _compile_time_iota[type: DType, simd_width: Int]() -> SIMD[type, simd_width]: constrained[ type.is_integral(), - "_iota can only be used with integer types.", + "_compile_time_iota can only be used with integer types.", ]() var a = SIMD[type, simd_width](0) for i in range(simd_width): From 3d4b14cfc334a3a5cc69a76665dbec7d34add706 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 14 Nov 2024 19:07:52 -0800 Subject: [PATCH 1874/2019] [KGEN] Add a new POC for the accelerator arch MODULAR_ORIG_COMMIT_REV_ID: 157fb1e14ce62552ea978397e3bc164925127ce4 --- stdlib/src/sys/info.mojo | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 8d404bbdcc..26a480ce17 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -28,6 +28,14 @@ fn _current_target() -> __mlir_type.`!kgen.target`: return __mlir_attr.`#kgen.param.expr : !kgen.target` +@always_inline("nodebug") +fn _accelerator_arch() -> String: + alias arch = String( + __mlir_attr.`#kgen.param.expr : !kgen.string` + ) + return arch + + fn _get_arch[target: __mlir_type.`!kgen.target`]() -> String: return __mlir_attr[ `#kgen.param.expr Date: Thu, 14 Nov 2024 19:45:00 -0800 Subject: [PATCH 1875/2019] [******][GPU] Revert address space changes introduced in #50730 MODULAR_ORIG_COMMIT_REV_ID: 151283eee2ad3d2b32a9a99c318fce87f2fb33f2 --- stdlib/src/memory/pointer.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 8655b58bcf..fcea6422e0 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -37,13 +37,13 @@ struct _GPUAddressSpace(EqualityComparable): """Generic address space.""" alias GLOBAL = AddressSpace(1) """Global address space.""" - alias CONSTANT = AddressSpace(2) if is_nvidia_gpu() else AddressSpace(4) + alias CONSTANT = AddressSpace(2) """Constant address space.""" alias SHARED = AddressSpace(3) """Shared address space.""" alias PARAM = AddressSpace(4) """Param address space.""" - alias LOCAL = AddressSpace(5) if is_nvidia_gpu() else AddressSpace(3) + alias LOCAL = AddressSpace(5) """Local address space.""" @always_inline("nodebug") From 83e5da1f54464c9f74e1cddd66886950d073cab7 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 15 Nov 2024 13:08:24 -0600 Subject: [PATCH 1876/2019] [stdlib] feat: Add `_Global` interface for easier to use global variables * Also removed various unused imports of related types. The Mojo standard library currently contains a pseudo-global-variables mechanism based on a global hashmap. This allows one to use functionality that behaves like global variables, despite the fact that Mojo does not currently have complete language-level support for global variables (i.e. `var` statements at the top level scope). The current interface in the standard library is very low-level, error prone, and requires juggling `OpaquePointer` values. With this new interface, declaring a global variable containing e.g. a String looks like: ```mojo alias MY_GLOBAL = _Global["MY_GLOBAL", String, _init_my_global] fn _init_my_global() -> String: return "default value" ``` This new interface adds the following constraints that make it easier to reason about and use: * The data type stored by the global variable must be declared. * The initializer function cannot take any arguments (no "payload" data) * The global variable is de-initialized by calling the data type's `__del__()` method (not an arbitrary function). MODULAR_ORIG_COMMIT_REV_ID: 40575002cd16e7ecb931a0143102f3956dfb8ef7 --- stdlib/src/builtin/_pybind.mojo | 1 - stdlib/src/builtin/file_descriptor.mojo | 2 +- stdlib/src/builtin/int.mojo | 1 - stdlib/src/collections/string.mojo | 2 +- stdlib/src/hashlib/hash.mojo | 22 ++---- stdlib/src/python/_bindings.mojo | 2 +- stdlib/src/python/python.mojo | 27 +++++--- stdlib/src/sys/ffi.mojo | 90 ++++++++++++++++++------- stdlib/src/utils/inline_string.mojo | 1 - stdlib/test/utils/test_variant.mojo | 26 +++---- 10 files changed, 104 insertions(+), 70 deletions(-) diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 61ca83d8c2..2644bceba2 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -14,7 +14,6 @@ from memory import UnsafePointer, stack_allocation from sys import sizeof, alignof -from sys.ffi import OpaquePointer import python._cpython as cp from python import TypedPythonObject, Python, PythonObject diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 6974b8d116..9edbc8c58c 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -25,7 +25,7 @@ f.close() """ from utils import Span from builtin.io import _printf -from sys.ffi import external_call, OpaquePointer +from sys.ffi import external_call from sys.info import is_nvidia_gpu from memory import UnsafePointer diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 6883fbcd17..0cc7a6e1b3 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -33,7 +33,6 @@ from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select from sys import is_nvidia_gpu, bitwidthof -from sys.ffi import OpaquePointer # ===----------------------------------------------------------------------=== # # Indexer diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 917b56e99b..b76e93e94c 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index from sys import bitwidthof, llvm_intrinsic -from sys.ffi import c_char, OpaquePointer +from sys.ffi import c_char from utils import StaticString, write_args from bit import count_leading_zeros diff --git a/stdlib/src/hashlib/hash.mojo b/stdlib/src/hashlib/hash.mojo index 528cf2bd3e..84f3a82a58 100644 --- a/stdlib/src/hashlib/hash.mojo +++ b/stdlib/src/hashlib/hash.mojo @@ -26,7 +26,8 @@ There are a few main tools in this module: """ import random -from sys.ffi import _get_global, OpaquePointer + +from sys.ffi import _Global from sys import simdwidthof, bitwidthof from collections import InlineArray @@ -44,24 +45,15 @@ from memory import memcpy, memset_zero, stack_allocation, bitcast, UnsafePointer # var HASH_SECRET = int(random.random_ui64(0, UInt64.MAX) -fn _HASH_SECRET() -> UInt: - var ptr = _get_global[ - "HASH_SECRET", _initialize_hash_secret, _destroy_hash_secret - ]() - return ptr.bitcast[UInt]()[0] +fn _init_hash_secret() -> Int: + return int(random.random_ui64(0, UInt64.MAX)) -fn _initialize_hash_secret( - payload: OpaquePointer, -) -> OpaquePointer: - var secret = random.random_ui64(0, UInt64.MAX) - var data = UnsafePointer[Int].alloc(1) - data[] = int(secret) - return data.bitcast[NoneType]() +alias _HASH_SECRET_VALUE = _Global["HASH_SECRET", Int, _init_hash_secret] -fn _destroy_hash_secret(p: OpaquePointer): - p.free() +fn _HASH_SECRET() -> UInt: + return UInt(_HASH_SECRET_VALUE.get_or_create_ptr()[]) trait Hashable: diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index f83ff6d3a3..4046ca5f50 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -13,7 +13,7 @@ from memory import UnsafePointer -from sys.ffi import c_int, OpaquePointer +from sys.ffi import c_int from sys.info import sizeof from os import abort diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index f2fb0237b9..937f727915 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -22,7 +22,7 @@ from python import Python from collections import Dict from os import abort, getenv from sys import external_call, sizeof -from sys.ffi import _get_global, OpaquePointer +from sys.ffi import _Global from memory import UnsafePointer @@ -37,22 +37,29 @@ from ._cpython import ( Py_ssize_t, ) +alias _PYTHON_GLOBAL = _Global["Python", _PythonGlobal, _init_python_global] -fn _init_global(ignored: OpaquePointer) -> OpaquePointer: - var ptr = UnsafePointer[CPython].alloc(1) - ptr[] = CPython() - return ptr.bitcast[NoneType]() +fn _init_python_global() -> _PythonGlobal: + return _PythonGlobal() -fn _destroy_global(python: OpaquePointer): - var p = python.bitcast[CPython]() - CPython.destroy(p[]) - python.free() + +struct _PythonGlobal: + var cpython: CPython + + fn __moveinit__(inout self, owned other: Self): + self.cpython = other.cpython^ + + fn __init__(inout self): + self.cpython = CPython() + + fn __del__(owned self): + CPython.destroy(self.cpython) @always_inline fn _get_global_python_itf() -> _PythonInterfaceImpl: - var ptr = _get_global["Python", _init_global, _destroy_global]() + var ptr = _PYTHON_GLOBAL.get_or_create_ptr() return ptr.bitcast[CPython]() diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index b3c86e6373..cdfbcfb18f 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -22,6 +22,10 @@ from .intrinsics import _mlirtype_is_eq from sys._libc import dlerror, dlopen, dlclose, dlsym +# ===-----------------------------------------------------------------------===# +# Primitive C type aliases +# ===-----------------------------------------------------------------------===# + alias c_char = Int8 """C `char` type.""" @@ -85,6 +89,11 @@ fn _c_long_long_dtype() -> DType: return abort[DType]() +# ===-----------------------------------------------------------------------===# +# Dynamic Library Loading +# ===-----------------------------------------------------------------------===# + + struct RTLD: """Enumeration of the RTLD flags used during dynamic library loading.""" @@ -358,29 +367,6 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): return self.get_function[fn (__type_of(v)) -> return_type](name)(v) -# ===----------------------------------------------------------------------===# -# Library Load -# ===----------------------------------------------------------------------===# - - -@always_inline -fn _get_global[ - name: StringLiteral, - init_fn: fn (OpaquePointer) -> OpaquePointer, - destroy_fn: fn (OpaquePointer) -> None, -](payload: OpaquePointer = OpaquePointer()) -> OpaquePointer: - return external_call["KGEN_CompilerRT_GetGlobalOrCreate", OpaquePointer]( - StringRef(name), payload, init_fn, destroy_fn - ) - - -@always_inline -fn _get_global_or_null[name: StringLiteral]() -> OpaquePointer: - return external_call["KGEN_CompilerRT_GetGlobalOrNull", OpaquePointer]( - name.unsafe_ptr(), name.byte_length() - ) - - @always_inline fn _get_dylib[ name: StringLiteral, @@ -418,6 +404,64 @@ fn _get_dylib_function[ return new_func +# ===----------------------------------------------------------------------===# +# Globals +# ===----------------------------------------------------------------------===# + + +struct _Global[ + name: StringLiteral, + storage_type: Movable, + init_fn: fn () -> storage_type, +]: + @staticmethod + fn _init_wrapper(payload: OpaquePointer) -> OpaquePointer: + # Struct-based globals don't get to take arguments to their initializer. + debug_assert(not payload) + + # Heap allocate space to store this "global" + var ptr = UnsafePointer[storage_type].alloc(1) + + # TODO: + # Any way to avoid the move, e.g. by calling this function + # with the ABI destination result pointer already set to `ptr`? + ptr.init_pointee_move(init_fn()) + + return ptr.bitcast[NoneType]() + + @staticmethod + fn _deinit_wrapper(self_: OpaquePointer): + var ptr: UnsafePointer[storage_type] = self_.bitcast[storage_type]() + + # Deinitialize and deallocate the global + ptr.destroy_pointee() + ptr.free() + + @staticmethod + fn get_or_create_ptr() -> UnsafePointer[storage_type]: + return _get_global[ + name, Self._init_wrapper, Self._deinit_wrapper + ]().bitcast[storage_type]() + + +@always_inline +fn _get_global[ + name: StringLiteral, + init_fn: fn (OpaquePointer) -> OpaquePointer, + destroy_fn: fn (OpaquePointer) -> None, +](payload: OpaquePointer = OpaquePointer()) -> OpaquePointer: + return external_call["KGEN_CompilerRT_GetGlobalOrCreate", OpaquePointer]( + StringRef(name), payload, init_fn, destroy_fn + ) + + +@always_inline +fn _get_global_or_null[name: StringLiteral]() -> OpaquePointer: + return external_call["KGEN_CompilerRT_GetGlobalOrNull", OpaquePointer]( + name.unsafe_ptr(), name.byte_length() + ) + + # ===----------------------------------------------------------------------===# # external_call # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index e163727f51..18ee49465f 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -19,7 +19,6 @@ from collections import InlineArray from os import abort from collections import Optional from sys import sizeof -from sys.ffi import OpaquePointer from memory import UnsafePointer, memcpy diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index bafac3221f..2428ac18a7 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.ffi import _get_global, OpaquePointer +from sys.ffi import _Global from memory import UnsafePointer from testing import assert_equal, assert_false, assert_true @@ -41,27 +41,21 @@ struct TestCounter(CollectionElement): self.moved = other.moved + 1 -fn _poison_ptr() -> UnsafePointer[Bool]: - var ptr = _get_global[ - "TEST_VARIANT_POISON", _initialize_poison, _destroy_poison - ]() - return ptr.bitcast[Bool]() +alias TEST_VARIANT_POISON = _Global[ + "TEST_VARIANT_POISON", Bool, _initialize_poison +] -fn assert_no_poison() raises: - assert_false(_poison_ptr().take_pointee()) +fn _initialize_poison() -> Bool: + return False -fn _initialize_poison( - payload: OpaquePointer, -) -> OpaquePointer: - var poison = UnsafePointer[Bool].alloc(1) - poison.init_pointee_move(False) - return poison.bitcast[NoneType]() +fn _poison_ptr() -> UnsafePointer[Bool]: + return TEST_VARIANT_POISON.get_or_create_ptr() -fn _destroy_poison(p: OpaquePointer): - p.free() +fn assert_no_poison() raises: + assert_false(_poison_ptr().take_pointee()) struct Poison(CollectionElement): From 1cf7172984e4fbd39020fcacef7f8d08b0f1c504 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:02:51 -0600 Subject: [PATCH 1877/2019] [External] [stdlib] [NFC] Add docstring comment for `Span` slicing's allocation (#50938) [External] [stdlib] [NFC] Add docstring comment for `Span` slicing's allocation Add docstring comment for `Span` slicing's allocation regarding PR https://github.com/modularml/mojo/pull/3650 ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#3774 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3774 MODULAR_ORIG_COMMIT_REV_ID: bc4bd40542e608c6f8b542af0f2f8258735d9600 --- stdlib/src/utils/span.mojo | 4 ++++ stdlib/test/utils/test_span.mojo | 3 +++ 2 files changed, 7 insertions(+) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index c2a5fee6c0..97dc5b6aae 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -193,6 +193,10 @@ struct Span[ Returns: A new span that points to the same data as the current span. + + Allocation: + This function allocates when the step is negative, to avoid a memory + leak, take ownership of the value. """ var start: Int var end: Int diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index b963ad048f..79cd780401 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -141,12 +141,15 @@ def test_span_slice(): res = s[1::-1] assert_equal(res[0], 2) assert_equal(res[1], 1) + res.unsafe_ptr().free() res = s[2:1:-1] assert_equal(res[0], 3) assert_equal(len(res), 1) + res.unsafe_ptr().free() res = s[5:1:-2] assert_equal(res[0], 5) assert_equal(res[1], 3) + res.unsafe_ptr().free() def test_copy_from(): From ac6478ae52eecbc1d25e5c00fc8adb8e30075a05 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 15 Nov 2024 12:12:59 -0800 Subject: [PATCH 1878/2019] [******][GPU][AMD] Make WARP_SIZE accelerator agnostic MODULAR_ORIG_COMMIT_REV_ID: 3c06a462bf9b844bd8b598052689acf8bfc00b03 --- stdlib/src/sys/__init__.mojo | 1 - stdlib/src/sys/info.mojo | 22 ++-------------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 1a36b9f69d..099f406f06 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -46,7 +46,6 @@ from .info import ( simdbitwidth, simdbytewidth, simdwidthof, - warpsize, sizeof, is_nvidia_gpu, is_gpu, diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 26a480ce17..682f4f2d78 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -29,11 +29,8 @@ fn _current_target() -> __mlir_type.`!kgen.target`: @always_inline("nodebug") -fn _accelerator_arch() -> String: - alias arch = String( - __mlir_attr.`#kgen.param.expr : !kgen.string` - ) - return arch +fn _accelerator_arch() -> StringLiteral: + return __mlir_attr.`#kgen.param.expr : !kgen.string` fn _get_arch[target: __mlir_type.`!kgen.target`]() -> String: @@ -591,21 +588,6 @@ fn simdbytewidth[ return simdbitwidth[target]() // CHAR_BIT -@always_inline("nodebug") -fn warpsize[ - target: __mlir_type.`!kgen.target` = _current_target() -]() -> IntLiteral: - """Returns the warp size of the specified target. - - Parameters: - target: The target architecture. - - Returns: - The warp size of the specified target. - """ - return 32 - - @always_inline("nodebug") fn sizeof[ type: AnyType, target: __mlir_type.`!kgen.target` = _current_target() From 363da904a7e3f59351bcd8600a3bf67a371bb5e6 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 15 Nov 2024 14:06:47 -0700 Subject: [PATCH 1879/2019] [stdlib] Remove `UnsafePointer.bitcast` overload for `DType` Instead of special-casing `DType` in the `UnsafePointer.bitcast` function, remove the `bitcast` overload for `DType`. Instead, just adjust the callers to specify `Scalar[T]` when using `T` is a `DType`. MODULAR_ORIG_COMMIT_REV_ID: d8efa320831598a6af622e69280a6b65317cfe1d --- docs/changelog.md | 3 +++ stdlib/src/hashlib/_ahash.mojo | 20 +++++++++++++------- stdlib/src/memory/unsafe_pointer.mojo | 24 ------------------------ stdlib/src/os/atomic.mojo | 10 ++++++---- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c232260e01..2d168dbe6a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -451,6 +451,9 @@ what we publish. ### ❌ Removed +- The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your + `DType` in a `Scalar[my_dtype]` to call the only overload of `bitcast` now. + ### 🛠️ Fixed - Lifetime tracking is now fully field sensitive, which makes the uninitialized diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index 7dccb4dd9d..0845dc8d58 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -61,14 +61,18 @@ fn _read_small(data: UnsafePointer[UInt8], length: Int) -> U128: if length >= 2: if length >= 4: # len 4-8 - var a = data.bitcast[DType.uint32]().load().cast[DType.uint64]() - var b = data.offset(length - 4).bitcast[DType.uint32]().load().cast[ + var a = data.bitcast[Scalar[DType.uint32]]().load().cast[ DType.uint64 ]() + var b = data.offset(length - 4).bitcast[ + Scalar[DType.uint32] + ]().load().cast[DType.uint64]() return U128(a, b) else: # len 2-3 - var a = data.bitcast[DType.uint16]().load().cast[DType.uint64]() + var a = data.bitcast[Scalar[DType.uint16]]().load().cast[ + DType.uint64 + ]() var b = data.offset(length - 1).load().cast[DType.uint64]() return U128(a, b) else: @@ -136,19 +140,21 @@ struct AHasher[key: U256](_Hasher): if length > 8: if length > 16: var tail = data.offset(length - 16).bitcast[ - DType.uint64 + Scalar[DType.uint64] ]().load[width=2]() self._large_update(tail) var offset = 0 while length - offset > 16: var block = data.offset(offset).bitcast[ - DType.uint64 + Scalar[DType.uint64] ]().load[width=2]() self._large_update(block) offset += 16 else: - var a = data.bitcast[DType.uint64]().load() - var b = data.offset(length - 8).bitcast[DType.uint64]().load() + var a = data.bitcast[Scalar[DType.uint64]]().load() + var b = data.offset(length - 8).bitcast[ + Scalar[DType.uint64] + ]().load() self._large_update(U128(a, b)) else: var value = _read_small(data, length) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 4d46ff1f68..cd4066c8cb 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -977,30 +977,6 @@ struct UnsafePointer[ ]._mlir_type, ](self.address) - @always_inline("nodebug") - fn bitcast[ - T: DType, - /, - address_space: AddressSpace = Self.address_space, - alignment: Int = Self.alignment, - origin: Origin[True].type = Self.origin, - ](self) -> UnsafePointer[Scalar[T], address_space, alignment, origin]: - """Bitcasts a UnsafePointer to a different type. - - Parameters: - T: The target type. - address_space: The address space of the result. - alignment: Alignment of the destination pointer. - origin: Origin of the destination pointer. - - Returns: - A new UnsafePointer object with the specified type and the same address, - as the original UnsafePointer. - """ - return self.bitcast[ - Scalar[T], address_space=address_space, alignment=alignment - ]() - @always_inline fn destroy_pointee( self: UnsafePointer[type, AddressSpace.GENERIC, *_, **_] diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 2932659f93..94c7047187 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -348,11 +348,12 @@ fn _max_impl[ alias unsigned_integral_type = _unsigned_integral_type_of[type]() if rhs >= 0: _max_impl_base( - ptr.bitcast[integral_type](), bitcast[integral_type](rhs) + ptr.bitcast[Scalar[integral_type]](), + bitcast[integral_type](rhs), ) return _min_impl_base( - ptr.bitcast[unsigned_integral_type](), + ptr.bitcast[Scalar[unsigned_integral_type]](), bitcast[unsigned_integral_type](rhs), ) return @@ -370,11 +371,12 @@ fn _min_impl[ alias unsigned_integral_type = _unsigned_integral_type_of[type]() if rhs >= 0: _min_impl_base( - ptr.bitcast[integral_type](), bitcast[integral_type](rhs) + ptr.bitcast[Scalar[integral_type]](), + bitcast[integral_type](rhs), ) return _max_impl_base( - ptr.bitcast[unsigned_integral_type](), + ptr.bitcast[Scalar[unsigned_integral_type]](), bitcast[unsigned_integral_type](rhs), ) return From eccdc290fbe6033e6c19d1584ef31c6d4d4a274f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 16 Nov 2024 07:49:11 -0800 Subject: [PATCH 1880/2019] [mojo-lang] Refactor some implicit conversion handling. This refactors some implicit conversion emission logic in the compiler's OverloadSet processing logic to follow the `CallSyntax` enum instead of using a redundant `allowImplicitConversions` boolean. Beyond unifying the code this exposes a problem where we allowed implicit conversions from implicit conversions (invalid "a->b->c" conversion) in one case. This fixes that, which requires tightening up some logic in the standard library. MODULAR_ORIG_COMMIT_REV_ID: fe26e7bfdb8d36b36f9537d5370a142076a58feb --- stdlib/src/builtin/object.mojo | 11 +++++++++++ stdlib/src/memory/arc.mojo | 2 +- stdlib/src/python/python.mojo | 2 +- stdlib/src/sys/info.mojo | 14 ++++++++------ stdlib/src/utils/numerics.mojo | 4 ++-- stdlib/test/builtin/test_none.mojo | 5 +++++ stdlib/test/os/test_atomic.mojo | 4 ++-- stdlib/test/pathlib/test_pathlib.mojo | 2 +- 8 files changed, 31 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 2d0cef2c92..5b1f41298f 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -743,6 +743,17 @@ struct object( """ self._value = _ObjectImpl() + # FIXME: None literal should be of NoneType not !kgen.none. + @doc_private + @always_inline + fn __init__(out self, none: __mlir_type.`!kgen.none`): + """Initializes a none value object from a `None` literal. + + Args: + none: None. + """ + self = NoneType() + @always_inline fn __init__(out self, value: Int): """Initializes the object with an integer value. diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 9863d700b0..4ff31e4900 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -56,7 +56,7 @@ struct _ArcInner[T: Movable]: fn __init__(out self, owned value: T): """Create an initialized instance of this with a refcount of 1.""" - self.refcount = 1 + self.refcount = Scalar[DType.uint64](1) self.payload = value^ fn add_ref(inout self): diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 937f727915..fbd1825c26 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -60,7 +60,7 @@ struct _PythonGlobal: @always_inline fn _get_global_python_itf() -> _PythonInterfaceImpl: var ptr = _PYTHON_GLOBAL.get_or_create_ptr() - return ptr.bitcast[CPython]() + return _PythonInterfaceImpl(ptr.bitcast[CPython]()) struct _PythonInterfaceImpl: diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 682f4f2d78..661fc0b94f 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -34,12 +34,14 @@ fn _accelerator_arch() -> StringLiteral: fn _get_arch[target: __mlir_type.`!kgen.target`]() -> String: - return __mlir_attr[ - `#kgen.param.expr : !kgen.string`, - ] + return String( + __mlir_attr[ + `#kgen.param.expr : !kgen.string`, + ] + ) @always_inline("nodebug") diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 72dea362b3..05db78d5f5 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -812,7 +812,7 @@ fn max_finite[type: DType]() -> Scalar[type]: elif type is DType.float64: return 1.79769313486231570815e308 elif type is DType.bool: - return True + return rebind[Scalar[type]](Scalar(True)) else: constrained[False, "max_finite() called on unsupported type"]() return 0 @@ -853,7 +853,7 @@ fn min_finite[type: DType]() -> Scalar[type]: elif type.is_floating_point(): return -max_finite[type]() elif type is DType.bool: - return False + return rebind[Scalar[type]](Scalar(False)) else: constrained[False, "min_finite() called on unsupported type"]() return 0 diff --git a/stdlib/test/builtin/test_none.mojo b/stdlib/test/builtin/test_none.mojo index 9d588bb7d4..07404bb8ae 100644 --- a/stdlib/test/builtin/test_none.mojo +++ b/stdlib/test/builtin/test_none.mojo @@ -40,6 +40,11 @@ struct FromNone: fn __init__(out self, none: NoneType): self.value = -1 + # FIXME: None literal should be of NoneType not !kgen.none. + @always_inline + fn __init__(out self, none: __mlir_type.`!kgen.none`): + self = NoneType() + fn __init__(out self, value: Int): self.value = value diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index ab536d9e41..d8433fd194 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -18,7 +18,7 @@ from testing import assert_equal, assert_false, assert_true fn test_atomic() raises: - var atom: Atomic[DType.index] = 3 + var atom = Atomic[DType.index](3) assert_equal(atom.load(), 3) @@ -68,7 +68,7 @@ fn test_atomic_floating_point() raises: def test_compare_exchange_weak(): - var atom: Atomic[DType.int64] = 3 + var atom = Atomic[DType.int64](3) var expected = Int64(3) var desired = Int64(3) var ok = atom.compare_exchange_weak(expected, desired) diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index fc54c8f8d4..d57c149e0f 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -91,7 +91,7 @@ fn get_user_path() -> Path: @parameter if os_is_windows(): return Path("C:") / "Users" / "user" - return "/home/user" + return Path("/home/user") fn get_current_home() -> String: From ca81c0cced3c904e50577d155f6b42c0351aeac4 Mon Sep 17 00:00:00 2001 From: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Date: Sat, 16 Nov 2024 09:50:20 -0600 Subject: [PATCH 1881/2019] [External] [stdlib] Fix unsafe `String.write_bytes()` (#50972) [External] [stdlib] Fix unsafe `String.write_bytes()` Fix an unsafe assumption that the span is null terminated. Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3773 MODULAR_ORIG_COMMIT_REV_ID: 4b07e6d189015a2053b6f8f3a5a0249ab00b3b1f --- stdlib/src/collections/string.mojo | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index b76e93e94c..4cbebc3028 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -847,14 +847,13 @@ struct String( # ===------------------------------------------------------------------=== # fn write_bytes(inout self, bytes: Span[Byte, _]): - """ - Write a byte span to this String. + """Write a byte span to this String. Args: bytes: The byte span to write to this String. Must NOT be - null terminated. + null terminated. """ - self._iadd[True](bytes) + self._iadd[False](bytes) fn write[*Ts: Writable](inout self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. From 833f81d153ffa12bbc29d6a3d1f130f77b97d925 Mon Sep 17 00:00:00 2001 From: Alvydas Vitkauskas Date: Sat, 16 Nov 2024 09:54:16 -0600 Subject: [PATCH 1882/2019] [External] [stdlib] Deque - add remaining methods and tests (#50973) [External] [stdlib] Deque - add remaining methods and tests This is a final part of the Deque implementation. Resolves https://github.com/modularml/mojo/issues/2659. Co-authored-by: Alvydas Vitkauskas Closes modularml/mojo#3759 MODULAR_ORIG_COMMIT_REV_ID: 61c06ed2438fa18d7ee7136d3e4aad84b3795f4c --- docs/changelog.md | 13 + stdlib/src/builtin/reversed.mojo | 22 +- stdlib/src/collections/deque.mojo | 277 ++++++++++++++++- stdlib/test/builtin/test_reversed.mojo | 11 +- stdlib/test/collections/test_deque.mojo | 390 +++++++++++++++++++++++- 5 files changed, 708 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2d168dbe6a..d18b597ce4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -239,6 +239,19 @@ what we publish. allocate additional capacity. ([PR #3755](https://github.com/modularml/mojo/pull/3755) by [@thatstoasty](https://github.com/thatstoasty)). +- Introduced a new `Deque` (double-ended queue) collection type, based on a + dynamically resizing circular buffer for efficient O(1) additions and removals + at both ends as well as O(1) direct access to all elements. + + The `Deque` supports the full Python `collections.deque` API, ensuring that all + expected deque operations perform as in Python. + + Enhancements to the standard Python API include `peek()` and `peekleft()` + methods for non-destructive access to the last and first elements, and advanced + constructor options (`capacity`, `min_capacity`, and `shrink`) for customizing + memory allocation and performance. These options allow for optimized memory usage + and reduced buffer reallocations, providing flexibility based on application requirements. + ### 🦋 Changed - The argument convention for `__init__` methods has been changed from `inout` diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 0ddb24621b..20f4fa4db3 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -15,7 +15,8 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import Dict +from collections import Deque, Dict +from collections.deque import _DequeIter from collections.dict import _DictEntryIter, _DictKeyIter, _DictValueIter from collections.list import _ListIter @@ -97,6 +98,25 @@ fn reversed[ return value.__reversed__() +fn reversed[ + T: CollectionElement +](ref [_]value: Deque[T]) -> _DequeIter[T, __origin_of(value), False]: + """Get a reversed iterator of the deque. + + **Note**: iterators are currently non-raising. + + Parameters: + T: The type of the elements in the deque. + + Args: + value: The deque to get the reversed iterator of. + + Returns: + The reversed iterator of the deque. + """ + return value.__reversed__() + + fn reversed[ K: KeyElement, V: CollectionElement, diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index b619c4bb5a..c7a02c219a 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -85,7 +85,7 @@ struct Deque[ElementType: CollectionElement]( # ===-------------------------------------------------------------------===# fn __init__( - inout self, + out self, *, owned elements: Optional[List[ElementType]] = None, capacity: Int = self.default_capacity, @@ -537,6 +537,28 @@ struct Deque[ElementType: CollectionElement]( self._head = 0 self._tail = 0 + fn count[ + EqualityElementType: EqualityComparableCollectionElement, // + ](self: Deque[EqualityElementType], value: EqualityElementType) -> Int: + """Counts the number of occurrences of a `value` in the deque. + + Parameters: + EqualityElementType: The type of the elements in the deque. + Must implement the trait `EqualityComparableCollectionElement`. + + Args: + value: The value to count. + + Returns: + The number of occurrences of the value in the deque. + """ + count = 0 + for i in range(len(self)): + offset = self._physical_index(self._head + i) + if (self._data + offset)[] == value: + count += 1 + return count + fn extend(inout self, owned values: List[ElementType]): """Extends the right side of the deque by consuming elements of the list argument. @@ -603,6 +625,259 @@ struct Deque[ElementType: CollectionElement]( self._head = self._physical_index(self._head - 1) (src + i).move_pointee_into(self._data + self._head) + fn index[ + EqualityElementType: EqualityComparableCollectionElement, // + ]( + self: Deque[EqualityElementType], + value: EqualityElementType, + start: Int = 0, + stop: Optional[Int] = None, + ) raises -> Int: + """Returns the index of the first occurrence of a `value` in a deque + restricted by the range given the `start` and `stop` bounds. + + Parameters: + EqualityElementType: The type of the elements in the deque. + Must implement the `EqualityComparableCollectionElement` trait. + + Args: + value: The value to search for. + start: The starting index of the search, treated as a slice index + (defaults to 0). + stop: The ending index of the search, treated as a slice index + (defaults to None, which means the end of the deque). + + Returns: + The index of the first occurrence of the value in the deque. + + Raises: + ValueError: If the value is not found in the deque. + """ + start_normalized = start + + if stop is None: + stop_normalized = len(self) + else: + stop_normalized = stop.value() + + if start_normalized < 0: + start_normalized += len(self) + if stop_normalized < 0: + stop_normalized += len(self) + + start_normalized = max(0, min(start_normalized, len(self))) + stop_normalized = max(0, min(stop_normalized, len(self))) + + for idx in range(start_normalized, stop_normalized): + offset = self._physical_index(self._head + idx) + if (self._data + offset)[] == value: + return idx + raise "ValueError: Given element is not in deque" + + fn insert(inout self, idx: Int, owned value: ElementType) raises: + """Inserts the `value` into the deque at position `idx`. + + Args: + idx: The position to insert the value into. + value: The value to insert. + + Raises: + IndexError: If deque is already at its maximum size. + """ + deque_len = len(self) + + if deque_len == self._maxlen: + raise "IndexError: Deque is already at its maximum size" + + normalized_idx = idx + + if normalized_idx < -deque_len: + normalized_idx = 0 + + if normalized_idx > deque_len: + normalized_idx = deque_len + + if normalized_idx < 0: + normalized_idx += deque_len + + if normalized_idx <= deque_len // 2: + for i in range(normalized_idx): + src = self._physical_index(self._head + i) + dst = self._physical_index(src - 1) + (self._data + src).move_pointee_into(self._data + dst) + self._head = self._physical_index(self._head - 1) + else: + for i in range(deque_len - normalized_idx): + dst = self._physical_index(self._tail - i) + src = self._physical_index(dst - 1) + (self._data + src).move_pointee_into(self._data + dst) + self._tail = self._physical_index(self._tail + 1) + + offset = self._physical_index(self._head + normalized_idx) + (self._data + offset).init_pointee_move(value^) + + if self._head == self._tail: + self._realloc(self._capacity << 1) + + fn remove[ + EqualityElementType: EqualityComparableCollectionElement, // + ]( + inout self: Deque[EqualityElementType], + value: EqualityElementType, + ) raises: + """Removes the first occurrence of the `value`. + + Parameters: + EqualityElementType: The type of the elements in the deque. + Must implement the `EqualityComparableCollectionElement` trait. + + Args: + value: The value to remove. + + Raises: + ValueError: If the value is not found in the deque. + """ + deque_len = len(self) + for idx in range(deque_len): + offset = self._physical_index(self._head + idx) + if (self._data + offset)[] == value: + (self._data + offset).destroy_pointee() + + if idx < deque_len // 2: + for i in reversed(range(idx)): + src = self._physical_index(self._head + i) + dst = self._physical_index(src + 1) + (self._data + src).move_pointee_into(self._data + dst) + self._head = self._physical_index(self._head + 1) + else: + for i in range(idx + 1, deque_len): + src = self._physical_index(self._head + i) + dst = self._physical_index(src - 1) + (self._data + src).move_pointee_into(self._data + dst) + self._tail = self._physical_index(self._tail - 1) + + if ( + self._shrink + and self._capacity > self._min_capacity + and self._capacity // 4 >= len(self) + ): + self._realloc(self._capacity >> 1) + + return + + raise "ValueError: Given element is not in deque" + + fn peek(self) raises -> ElementType: + """Inspect the last (rightmost) element of the deque without removing it. + + Returns: + The the last (rightmost) element of the deque. + + Raises: + IndexError: If the deque is empty. + """ + if self._head == self._tail: + raise "IndexError: Deque is empty" + + return (self._data + self._physical_index(self._tail - 1))[] + + fn peekleft(self) raises -> ElementType: + """Inspect the first (leftmost) element of the deque without removing it. + + Returns: + The the first (leftmost) element of the deque. + + Raises: + IndexError: If the deque is empty. + """ + if self._head == self._tail: + raise "IndexError: Deque is empty" + + return (self._data + self._head)[] + + fn pop(inout self) raises -> ElementType as element: + """Removes and returns the element from the right side of the deque. + + Returns: + The popped value. + + Raises: + IndexError: If the deque is empty. + """ + if self._head == self._tail: + raise "IndexError: Deque is empty" + + self._tail = self._physical_index(self._tail - 1) + element = (self._data + self._tail).take_pointee() + + if ( + self._shrink + and self._capacity > self._min_capacity + and self._capacity // 4 >= len(self) + ): + self._realloc(self._capacity >> 1) + + return + + fn popleft(inout self) raises -> ElementType as element: + """Removes and returns the element from the left side of the deque. + + Returns: + The popped value. + + Raises: + IndexError: If the deque is empty. + """ + if self._head == self._tail: + raise "IndexError: Deque is empty" + + element = (self._data + self._head).take_pointee() + self._head = self._physical_index(self._head + 1) + + if ( + self._shrink + and self._capacity > self._min_capacity + and self._capacity // 4 >= len(self) + ): + self._realloc(self._capacity >> 1) + + return + + fn reverse(inout self): + """Reverses the elements of the deque in-place.""" + last = self._head + len(self) - 1 + for i in range(len(self) // 2): + src = self._physical_index(self._head + i) + dst = self._physical_index(last - i) + tmp = (self._data + dst).take_pointee() + (self._data + src).move_pointee_into(self._data + dst) + (self._data + src).init_pointee_move(tmp^) + + fn rotate(inout self, n: Int = 1): + """Rotates the deque by `n` steps. + + If `n` is positive, rotates to the right. + If `n` is negative, rotates to the left. + + Args: + n: Number of steps to rotate the deque + (defaults to 1). + """ + if n < 0: + for _ in range(-n): + (self._data + self._head).move_pointee_into( + self._data + self._tail + ) + self._tail = self._physical_index(self._tail + 1) + self._head = self._physical_index(self._head + 1) + else: + for _ in range(n): + self._tail = self._physical_index(self._tail - 1) + self._head = self._physical_index(self._head - 1) + (self._data + self._tail).move_pointee_into( + self._data + self._head + ) + fn _compute_pop_and_move_counts( self, len_self: Int, len_values: Int ) -> (Int, Int, Int, Int, Int): diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index 59b466da98..d6027d5429 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from collections import Dict +from collections import Deque, Dict from testing import assert_equal @@ -25,6 +25,15 @@ def test_reversed_list(): check -= 1 +def test_reversed_deque(): + var deque = Deque[Int](1, 2, 3, 4, 5, 6) + var check: Int = 6 + + for item in reversed(deque): + assert_equal(item[], check, "item[], check") + check -= 1 + + def test_reversed_dict(): var dict = Dict[String, Int]() dict["a"] = 1 diff --git a/stdlib/test/collections/test_deque.mojo b/stdlib/test/collections/test_deque.mojo index 16746021e7..a9350c2180 100644 --- a/stdlib/test/collections/test_deque.mojo +++ b/stdlib/test/collections/test_deque.mojo @@ -218,6 +218,77 @@ fn test_impl_append_with_maxlen() raises: assert_equal((q._data + 3)[], 3) +fn test_impl_appendleft() raises: + q = Deque[Int](capacity=2) + + q.appendleft(0) + # head wrapped to the end of the buffer + assert_equal(q._head, 1) + assert_equal(q._tail, 0) + assert_equal(q._capacity, 2) + assert_equal((q._data + 1)[], 0) + + q.appendleft(1) + # re-allocated buffer and moved all elements + assert_equal(q._head, 0) + assert_equal(q._tail, 2) + assert_equal(q._capacity, 4) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 0) + + q.appendleft(2) + # head wrapped to the end of the buffer + assert_equal(q._head, 3) + assert_equal(q._tail, 2) + assert_equal(q._capacity, 4) + assert_equal((q._data + 3)[], 2) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 0) + + # simulate pop() + q._tail -= 1 + q.appendleft(3) + assert_equal(q._head, 2) + assert_equal(q._tail, 1) + assert_equal(q._capacity, 4) + assert_equal((q._data + 2)[], 3) + assert_equal((q._data + 3)[], 2) + assert_equal((q._data + 0)[], 1) + + q.appendleft(4) + # re-allocated buffer and moved all elements + assert_equal(q._head, 0) + assert_equal(q._tail, 4) + assert_equal(q._capacity, 8) + assert_equal((q._data + 0)[], 4) + assert_equal((q._data + 1)[], 3) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 1) + + +fn test_impl_appendleft_with_maxlen() raises: + q = Deque[Int](maxlen=3) + + assert_equal(q._maxlen, 3) + assert_equal(q._capacity, 4) + + q.appendleft(0) + q.appendleft(1) + q.appendleft(2) + assert_equal(q._head, 1) + assert_equal(q._tail, 0) + + q.appendleft(3) + # first popped the rightmost element + # so there was no re-allocation of buffer + assert_equal(q._head, 0) + assert_equal(q._tail, 3) + assert_equal(q._capacity, 4) + assert_equal((q._data + 0)[], 3) + assert_equal((q._data + 1)[], 2) + assert_equal((q._data + 2)[], 1) + + fn test_impl_extend() raises: q = Deque[Int](maxlen=4) lst = List[Int](0, 1, 2) @@ -280,6 +351,122 @@ fn test_impl_extend() raises: assert_equal((q._data + 15)[], 9) +fn test_impl_extendleft() raises: + q = Deque[Int](maxlen=4) + lst = List[Int](0, 1, 2) + + q.extendleft(lst) + # head wrapped to the end of the buffer + assert_equal(q._capacity, 8) + assert_equal(q._head, 5) + assert_equal(q._tail, 0) + assert_equal((q._data + 5)[], 2) + assert_equal((q._data + 6)[], 1) + assert_equal((q._data + 7)[], 0) + + q.extendleft(lst) + # popped the last 2 elements + assert_equal(q._capacity, 8) + assert_equal(q._head, 2) + assert_equal(q._tail, 6) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 1) + assert_equal((q._data + 4)[], 0) + assert_equal((q._data + 5)[], 2) + + # turn off `maxlen` restriction + q._maxlen = -1 + q.extendleft(lst) + assert_equal(q._capacity, 8) + assert_equal(q._head, 7) + assert_equal(q._tail, 6) + assert_equal((q._data + 7)[], 2) + assert_equal((q._data + 0)[], 1) + assert_equal((q._data + 1)[], 0) + assert_equal((q._data + 2)[], 2) + assert_equal((q._data + 3)[], 1) + assert_equal((q._data + 4)[], 0) + assert_equal((q._data + 5)[], 2) + + # turn on `maxlen` and force to re-allocate + q._maxlen = 8 + q.extendleft(lst) + assert_equal(q._capacity, 16) + assert_equal(q._head, 13) + assert_equal(q._tail, 5) + # has to popleft the last 2 elements + assert_equal((q._data + 13)[], 2) + assert_equal((q._data + 14)[], 1) + assert_equal((q._data + 3)[], 2) + assert_equal((q._data + 4)[], 1) + + # extend with the list that is longer than `maxlen` + # has to pop all deque elements and some initial + # elements from the list as well + lst = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + q.extendleft(lst) + assert_equal(q._capacity, 16) + assert_equal(q._head, 5) + assert_equal(q._tail, 13) + assert_equal((q._data + 5)[], 9) + assert_equal((q._data + 6)[], 8) + assert_equal((q._data + 11)[], 3) + assert_equal((q._data + 12)[], 2) + + +fn test_impl_insert() raises: + q = Deque[Int](0, 1, 2, 3, 4, 5) + + q.insert(0, 6) + assert_equal(q._head, q.default_capacity - 1) + assert_equal((q._data + q._head)[], 6) + assert_equal((q._data + 0)[], 0) + + q.insert(1, 7) + assert_equal(q._head, q.default_capacity - 2) + assert_equal((q._data + q._head + 0)[], 6) + assert_equal((q._data + q._head + 1)[], 7) + + q.insert(8, 8) + assert_equal(q._tail, 7) + assert_equal((q._data + q._tail - 1)[], 8) + assert_equal((q._data + q._tail - 2)[], 5) + + q.insert(8, 9) + assert_equal(q._tail, 8) + assert_equal((q._data + q._tail - 1)[], 8) + assert_equal((q._data + q._tail - 2)[], 9) + + +fn test_impl_pop() raises: + q = Deque[Int](capacity=2, min_capacity=2) + with assert_raises(): + _ = q.pop() + + q.append(1) + q.appendleft(2) + assert_equal(q._capacity, 4) + assert_equal(q.pop(), 1) + assert_equal(len(q), 1) + assert_equal(q[0], 2) + assert_equal(q._capacity, 2) + + +fn test_popleft() raises: + q = Deque[Int](capacity=2, min_capacity=2) + assert_equal(q._capacity, 2) + with assert_raises(): + _ = q.popleft() + + q.appendleft(1) + q.append(2) + assert_equal(q._capacity, 4) + assert_equal(q.popleft(), 1) + assert_equal(len(q), 1) + assert_equal(q[0], 2) + assert_equal(q._capacity, 2) + + fn test_impl_clear() raises: q = Deque[Int](capacity=2) q.append(1) @@ -587,6 +774,18 @@ fn test_ne() raises: assert_true(q != p) +fn test_count() raises: + q = Deque(1, 2, 1, 2, 3, 1) + + assert_equal(q.count(1), 3) + assert_equal(q.count(2), 2) + assert_equal(q.count(3), 1) + assert_equal(q.count(4), 0) + + q.appendleft(2) + assert_equal(q.count(2), 3) + + fn test_contains() raises: q = Deque[Int](1, 2, 3) @@ -594,6 +793,181 @@ fn test_contains() raises: assert_false(4 in q) +fn test_index() raises: + q = Deque(1, 2, 1, 2, 3, 1) + + assert_equal(q.index(2), 1) + assert_equal(q.index(2, 1), 1) + assert_equal(q.index(2, 1, 3), 1) + assert_equal(q.index(2, stop=4), 1) + assert_equal(q.index(1, -12, 10), 0) + assert_equal(q.index(1, -4), 2) + assert_equal(q.index(1, -3), 5) + with assert_raises(): + _ = q.index(4) + + +fn test_insert() raises: + q = Deque[Int](capacity=4, maxlen=7) + + # negative index outbound + q.insert(-10, 0) + # Deque(0) + assert_equal(q[0], 0) + assert_equal(len(q), 1) + + # zero index + q.insert(0, 1) + # Deque(1, 0) + assert_equal(q[0], 1) + assert_equal(q[1], 0) + assert_equal(len(q), 2) + + # # positive index eq length + q.insert(2, 2) + # Deque(1, 0, 2) + assert_equal(q[2], 2) + assert_equal(q[1], 0) + + # # positive index outbound + q.insert(10, 3) + # Deque(1, 0, 2, 3) + assert_equal(q[3], 3) + assert_equal(q[2], 2) + + # assert deque buffer reallocated + assert_equal(len(q), 4) + assert_equal(q._capacity, 8) + + # # positive index inbound + q.insert(1, 4) + # Deque(1, 4, 0, 2, 3) + assert_equal(q[1], 4) + assert_equal(q[0], 1) + assert_equal(q[2], 0) + + # # positive index inbound + q.insert(3, 5) + # Deque(1, 4, 0, 5, 2, 3) + assert_equal(q[3], 5) + assert_equal(q[2], 0) + assert_equal(q[4], 2) + + # # negative index inbound + q.insert(-3, 6) + # Deque(1, 4, 0, 6, 5, 2, 3) + assert_equal(q[3], 6) + assert_equal(q[2], 0) + assert_equal(q[4], 5) + + # deque is at its maxlen + assert_equal(len(q), 7) + with assert_raises(): + q.insert(3, 7) + + +fn test_remove() raises: + q = Deque[Int](min_capacity=32) + q.extend(List(0, 1, 0, 2, 3, 0, 4, 5)) + assert_equal(len(q), 8) + assert_equal(q._capacity, 64) + + # remove first + q.remove(0) + # Deque(1, 0, 2, 3, 0, 4, 5) + assert_equal(len(q), 7) + assert_equal(q[0], 1) + # had to shrink its capacity + assert_equal(q._capacity, 32) + + # remove last + q.remove(5) + # Deque(1, 0, 2, 3, 0, 4) + assert_equal(len(q), 6) + assert_equal(q[5], 4) + # should not shrink further + assert_equal(q._capacity, 32) + + # remove in the first half + q.remove(0) + # Deque(1, 2, 3, 0, 4) + assert_equal(len(q), 5) + assert_equal(q[1], 2) + + # remove in the last half + q.remove(0) + # Deque(1, 2, 3, 4) + assert_equal(len(q), 4) + assert_equal(q[3], 4) + + # assert raises when not found + with assert_raises(): + q.remove(5) + + +fn test_peek_and_peekleft() raises: + q = Deque[Int](capacity=4) + assert_equal(q._capacity, 4) + + with assert_raises(): + _ = q.peek() + with assert_raises(): + _ = q.peekleft() + + q.extend(List(1, 2, 3)) + assert_equal(q.peekleft(), 1) + assert_equal(q.peek(), 3) + + _ = q.popleft() + assert_equal(q.peekleft(), 2) + assert_equal(q.peek(), 3) + + q.append(4) + assert_equal(q._capacity, 4) + assert_equal(q.peekleft(), 2) + assert_equal(q.peek(), 4) + + q.append(5) + assert_equal(q._capacity, 8) + assert_equal(q.peekleft(), 2) + assert_equal(q.peek(), 5) + + +fn test_reverse() raises: + q = Deque(0, 1, 2, 3) + + q.reverse() + assert_equal(q[0], 3) + assert_equal(q[1], 2) + assert_equal(q[2], 1) + assert_equal(q[3], 0) + + q.appendleft(4) + q.reverse() + assert_equal(q[0], 0) + assert_equal(q[4], 4) + + +fn test_rotate() raises: + q = Deque(0, 1, 2, 3) + + q.rotate() + assert_equal(q[0], 3) + assert_equal(q[3], 2) + + q.rotate(-1) + assert_equal(q[0], 0) + assert_equal(q[3], 3) + + q.rotate(3) + assert_equal(q[0], 1) + assert_equal(q[3], 0) + + q.rotate(-3) + assert_equal(q[0], 0) + assert_equal(q[3], 3) + + fn test_iter() raises: q = Deque(1, 2, 3) @@ -641,8 +1015,7 @@ fn test_reversed_iter() raises: q = Deque(1, 2, 3) i = 0 - # change to reversed(q) when implemented in builtin for Deque - for e in q.__reversed__(): + for e in reversed(q): i -= 1 assert_equal(e[], q[i]) assert_equal(-i, len(q)) @@ -678,7 +1051,13 @@ def main(): test_impl_bool() test_impl_append() test_impl_append_with_maxlen() + test_impl_appendleft() + test_impl_appendleft_with_maxlen() test_impl_extend() + test_impl_extendleft() + test_impl_insert() + test_impl_pop() + test_popleft() test_impl_clear() test_impl_add() test_impl_iadd() @@ -692,7 +1071,14 @@ def main(): test_setitem() test_eq() test_ne() + test_count() test_contains() + test_index() + test_insert() + test_remove() + test_peek_and_peekleft() + test_reverse() + test_rotate() test_iter() test_iter_with_list() test_reversed_iter() From b32e9d05daf6b74eb9abed14ef1cbb87f9ab8a16 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 16 Nov 2024 09:05:38 -0800 Subject: [PATCH 1883/2019] [mojo-stdlib] Start phasing in some markers for `@implicit` Explicitly-opted-in implicit conversions are coming, this starts adding some markers to some core types, which is necessary prior to landing the compiler support. There is more to do. MODULAR_ORIG_COMMIT_REV_ID: e393705561f37a557cd17dbe1fce573d9c86633a --- stdlib/src/builtin/bool.mojo | 8 ++++++-- stdlib/src/builtin/int.mojo | 8 ++++++++ stdlib/src/builtin/int_literal.mojo | 1 + stdlib/src/builtin/simd.mojo | 7 +++++++ stdlib/src/builtin/uint.mojo | 4 ++++ stdlib/src/collections/string.mojo | 4 ++++ stdlib/src/utils/string_slice.mojo | 2 ++ 7 files changed, 32 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 06ccff6578..c9073c3558 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -131,6 +131,7 @@ struct Bool( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.i1): """Construct a Bool value given a __mlir_type.i1 value. @@ -141,6 +142,7 @@ struct Bool( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct a Bool value given a `!pop.scalar` value. @@ -152,6 +154,7 @@ struct Bool( ) @always_inline("nodebug") + @implicit fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): """Convert an ImplicitlyBoolable value to a Bool. @@ -164,6 +167,7 @@ struct Bool( self = value.__bool__() @always_inline("nodebug") + @implicit fn __init__(out self, value: SIMD[DType.bool, 1]): """Convert a scalar SIMD value to a Bool. @@ -252,7 +256,7 @@ struct Bool( Returns: 1 if the Bool is True, 0 otherwise. """ - return _select_register_value(self.value, Int(1), Int(0)) + return _select_register_value(self, Int(1), Int(0)) @always_inline("nodebug") fn __float__(self) -> Float64: @@ -261,7 +265,7 @@ struct Bool( Returns: 1.0 if True else 0.0 otherwise. """ - return _select_register_value(self.value, Float64(1.0), Float64(0.0)) + return _select_register_value(self, Float64(1.0), Float64(0.0)) @always_inline("nodebug") fn __index__(self) -> Int: diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 0cc7a6e1b3..f68e1d0c0d 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -321,6 +321,7 @@ struct Int( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.index): """Construct Int from the given index value. @@ -331,6 +332,7 @@ struct Int( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int16 value. @@ -345,6 +347,7 @@ struct Int( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int32 value. @@ -359,6 +362,7 @@ struct Int( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Int64 value. @@ -373,6 +377,7 @@ struct Int( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct Int from the given Index value. @@ -384,6 +389,7 @@ struct Int( ) @always_inline("nodebug") + @implicit fn __init__(out self, value: IntLiteral): """Construct Int from the given IntLiteral value. @@ -393,6 +399,7 @@ struct Int( self = value.__int__() @always_inline("nodebug") + @implicit fn __init__[IndexerTy: Indexer](inout self, value: IndexerTy): """Construct Int from the given Indexer value. @@ -405,6 +412,7 @@ struct Int( self = value.__index__() @always_inline("nodebug") + @implicit fn __init__(out self, value: UInt): """Construct Int from the given UInt value. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 326759e115..1b17508a3b 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -59,6 +59,7 @@ struct IntLiteral( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!kgen.int_literal`): """Construct IntLiteral from the given mlir !kgen.int_literal value. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9238ca42f7..8f9e350580 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -248,6 +248,7 @@ struct SIMD[type: DType, size: Int]( # self.__copyinit__(other) @always_inline("nodebug") + @implicit fn __init__(out self, value: UInt): """Initializes the SIMD vector with an unsigned integer. @@ -260,6 +261,7 @@ struct SIMD[type: DType, size: Int]( self = Self(value.value) @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): """Initializes the SIMD vector with a signed integer. @@ -273,6 +275,7 @@ struct SIMD[type: DType, size: Int]( @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.index): _simd_construction_checks[type, size]() @@ -287,6 +290,7 @@ struct SIMD[type: DType, size: Int]( ](casted) @always_inline("nodebug") + @implicit fn __init__(out self, value: IntLiteral): """Initializes the SIMD vector with an integer. @@ -312,6 +316,7 @@ struct SIMD[type: DType, size: Int]( ](casted) @always_inline("nodebug") + @implicit fn __init__(out self: SIMD[DType.bool, size], value: Bool, /): """Initializes the SIMD vector with a bool value. @@ -330,6 +335,7 @@ struct SIMD[type: DType, size: Int]( ](casted) @always_inline("nodebug") + @implicit fn __init__( inout self, value: __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], @@ -343,6 +349,7 @@ struct SIMD[type: DType, size: Int]( self.value = value @always_inline("nodebug") + @implicit fn __init__(out self, value: Scalar[type], /): """Constructs a SIMD vector by splatting a scalar value. diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 03bb691300..5a914e8da1 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -60,6 +60,7 @@ struct UInt(IntLike, _HashableWithHasher): @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.index): """Construct UInt from the given index value. @@ -70,6 +71,7 @@ struct UInt(IntLike, _HashableWithHasher): @doc_private @always_inline("nodebug") + @implicit fn __init__(out self, value: __mlir_type.`!pop.scalar`): """Construct UInt from the given Index value. @@ -81,6 +83,7 @@ struct UInt(IntLike, _HashableWithHasher): ) @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): """Construct UInt from the given index value. @@ -90,6 +93,7 @@ struct UInt(IntLike, _HashableWithHasher): self.value = value.value @always_inline("nodebug") + @implicit fn __init__(out self, value: IntLiteral): """Construct UInt from the given IntLiteral value. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 4cbebc3028..09112037b9 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -734,6 +734,7 @@ struct String( # ===------------------------------------------------------------------=== # @always_inline + @implicit fn __init__(out self, owned impl: List[UInt8, *_]): """Construct a string from a buffer of bytes. @@ -783,6 +784,7 @@ struct String( """ self.__copyinit__(other) + @implicit fn __init__(out self, str: StringRef): """Construct a string from a StringRef object. @@ -796,6 +798,7 @@ struct String( memcpy(dest=buffer.data, src=str.data, count=length) self = Self(buffer^) + @implicit fn __init__(out self, str_slice: StringSlice): """Construct a string from a string slice. @@ -819,6 +822,7 @@ struct String( self = Self(buffer^) @always_inline + @implicit fn __init__(out self, literal: StringLiteral): """Constructs a String value given a constant string. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index c0e38d5f42..effc8ec5c9 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -267,6 +267,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( # ===------------------------------------------------------------------===# @always_inline + @implicit fn __init__(out self: StaticString, lit: StringLiteral): """Construct a new `StringSlice` from a `StringLiteral`. @@ -348,6 +349,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """ self._slice = other._slice + @implicit fn __init__[ O: ImmutableOrigin, // ](inout self: StringSlice[O], ref [O]value: String): From af4bdc6c46122ea2959e985791a90388ff90eb86 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 16 Nov 2024 12:24:54 -0800 Subject: [PATCH 1884/2019] [******][GPU] Make compile_function aware of the target accelerator MODULAR_ORIG_COMMIT_REV_ID: d233c78fd439804bb9f7036b768e3a3b8007cc35 --- stdlib/src/sys/info.mojo | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 661fc0b94f..9930fe79a0 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -387,31 +387,36 @@ fn os_is_windows() -> Bool: @always_inline("nodebug") -fn _triple_attr() -> __mlir_type.`!kgen.string`: +fn _triple_attr[ + triple: __mlir_type.`!kgen.target` = _current_target() +]() -> __mlir_type.`!kgen.string`: return __mlir_attr[ `#kgen.param.expr : !kgen.string`, ] @always_inline("nodebug") -fn is_triple[triple: StringLiteral]() -> Bool: +fn is_triple[ + name: StringLiteral, target: __mlir_type.`!kgen.target` = _current_target() +]() -> Bool: """Returns True if the target triple of the compiler matches the input and False otherwise. Parameters: - triple: The triple value to be checked against. + name: The name of the triple value. + target: The triple value to be checked against. Returns: True if the triple matches and False otherwise. """ return __mlir_attr[ `#kgen.param.expr : i1`, ] From ef300aa237dd3822bf257e36f3cb8e3b14124847 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sat, 16 Nov 2024 12:58:48 -0800 Subject: [PATCH 1885/2019] [stdlib] Allow passing variables to GPU `debug_assert` This buffers the message for debug_assert which allows passing multiple args with variables on GPU for the message. It also removes all allocations for debug_assert on CPU. MODULAR_ORIG_COMMIT_REV_ID: d3699bcc8e5aa0b6cc56027fbee5559600c13769 --- stdlib/src/builtin/debug_assert.mojo | 162 +++++++++++---------------- stdlib/src/utils/write.mojo | 2 + 2 files changed, 68 insertions(+), 96 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index d0d33d4899..6d33f5f5e4 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -17,12 +17,12 @@ These are Mojo built-ins, so you don't need to import them. from os import abort -from sys import is_defined, is_nvidia_gpu +from sys import is_nvidia_gpu, llvm_intrinsic from sys._build import is_debug_build from sys.param_env import env_get_string from sys.ffi import external_call, c_uint, c_size_t, c_char -from sys.info import sizeof from memory import UnsafePointer +from utils.write import _WriteBufferHeap, _WriteBufferStack, write_args from builtin._location import __call_location, _SourceLocation @@ -52,65 +52,6 @@ fn _assert_enabled[assert_mode: StringLiteral, cpu_only: Bool]() -> Bool: return defined_mode == assert_mode -@always_inline -fn debug_assert[ - assert_mode: StringLiteral = "none", cpu_only: Bool = False -](cond: Bool, message: StringLiteral): - """Asserts that the condition is true. - - Parameters: - assert_mode: Determines when the assert is turned on. - cpu_only: If true, only run the assert on CPU. - - Args: - cond: The function to invoke to check if the assertion holds. - message: A StringLiteral to print on failure. - - - Pass in a condition and single StringLiteral to stop execution and - show an error on failure: - - ```mojo - x = 0 - debug_assert(x > 0, "x is not more than 0") - ``` - - By default it's a no-op, you can change the assertion level for example: - - ```sh - mojo -D ASSERT=all main.mojo - ``` - - Assertion modes: - - - none: turn off all assertions for performance at the cost of safety. - - warn: print any errors instead of aborting. - - safe: standard safety checks that are on even in release builds. - - all: turn on all assertions. - - You can set the `assert_mode` to `safe` so the assertion runs even in - release builds: - - ```mojo - debug_assert[assert_mode="safe"]( - x > 0, "expected x to be more than 0 but got: ", x - ) - ``` - - To ensure that you have no runtime penality from your assertion in release - builds, make sure there are no side effects in your message and condition. - - On GPU this will also show the the block and thread idx's on failure. - . - """ - - @parameter - if _assert_enabled[assert_mode, cpu_only](): - if cond: - return - _debug_assert_msg_literal(message, __call_location()) - - @always_inline fn debug_assert[ cond: fn () capturing [_] -> Bool, @@ -187,7 +128,6 @@ fn debug_assert[ ```mojo debug_assert[check_name, cpu_only=True]("unexpected name") ``` - . """ @parameter @@ -271,7 +211,6 @@ fn debug_assert[ ```mojo debug_assert[check_name, cpu_only=True]("unexpected name") ``` - . """ @parameter @@ -294,53 +233,84 @@ fn _debug_assert_msg( abort's implementation could use debug_assert) """ + var stdout = sys.stdout + @parameter if is_nvidia_gpu(): - external_call["__assertfail", NoneType]( - "debug_assert message must be a single StringLiteral on GPU" - .unsafe_cstr_ptr(), - loc.file_name.unsafe_cstr_ptr(), - c_uint(loc.line), - # TODO(MSTDL-962) pass through the funciton name here - "kernel".unsafe_cstr_ptr(), - c_size_t(sizeof[Int8]()), - ) - - else: - message = String.write(messages) + var buffer = _WriteBufferHeap[4096](stdout) + buffer.write("At ", loc, ": ") + _write_gpu_thread_context(buffer) @parameter if defined_mode == "warn": - print(loc.prefix("Assert Warning: " + message)) + buffer.write(" Assert Warning: ") else: - abort(loc.prefix("Assert Error: " + message)) + buffer.write(" Assert Error: ") + write_args(buffer, messages, end="\n") + buffer.flush() -@no_inline -fn _debug_assert_msg_literal(message: StringLiteral, loc: _SourceLocation): - """Aborts with (or prints) the given message and location. + @parameter + if defined_mode != "warn": + abort() - This function is intentionally marked as no_inline to reduce binary size. + else: + var buffer = _WriteBufferStack[4096](stdout) + buffer.write("At ", loc, ": ") - Note that it's important that this function doesn't get inlined; otherwise, - an indirect recursion of @always_inline functions is possible (e.g. because - abort's implementation could use debug_assert) - """ + @parameter + if defined_mode == "warn": + buffer.write(" Assert Warning: ") + else: + buffer.write(" Assert Error: ") + write_args(buffer, messages, end="\n") + buffer.flush() + + @parameter + if defined_mode != "warn": + abort() + + +# Can't import gpu module at this stage in compilation for thread/block idx +fn _write_gpu_thread_context[W: Writer](inout writer: W): + writer.write("block: [") + _write_id["block", "x"](writer) + writer.write(",") + _write_id["block", "y"](writer) + writer.write(",") + _write_id["block", "z"](writer) + writer.write("] thread: [") + _write_id["thread", "x"](writer) + writer.write(",") + _write_id["thread", "y"](writer) + writer.write(",") + _write_id["thread", "z"](writer) + writer.write("]") + + +fn _write_id[ + W: Writer, //, type: StringLiteral, dim: StringLiteral +](inout writer: W): + alias intrinsic_name = _get_intrinsic_name[type, dim]() + writer.write(llvm_intrinsic[intrinsic_name, Int32, has_side_effect=False]()) + + +fn _get_intrinsic_name[ + type: StringLiteral, dim: StringLiteral +]() -> StringLiteral: @parameter if is_nvidia_gpu(): - external_call["__assertfail", NoneType]( - message.unsafe_cstr_ptr(), - loc.file_name.unsafe_cstr_ptr(), - c_uint(loc.line), - # TODO(MSTDL-962) pass through the funciton name here - "kernel".unsafe_cstr_ptr(), - c_size_t(sizeof[Int8]()), - ) + + @parameter + if type == "thread": + return "llvm.nvvm.read.ptx.sreg.tid." + dim + else: + return "llvm.nvvm.read.ptx.sreg.ctaid." + dim else: @parameter - if defined_mode == "warn": - print(loc.prefix(str("Assert Warning: ") + message)) + if type == "thread": + return "llvm.amdgcn.workitem.id." + dim else: - abort(loc.prefix(str("Assert Error: ") + message)) + return "llvm.amdgcn.workgroup.id." + dim diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index c82e4afa1b..ed667bfa96 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -361,6 +361,8 @@ fn write_buffered[ ``` . """ + + @parameter if is_nvidia_gpu(): # Stack space is very small on GPU due to many threads, so use heap var buffer = _WriteBufferHeap[buffer_size](writer^) From 575640ba9a74dd00ed04da25040d81c4db8ad9d2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 17 Nov 2024 12:19:14 -0800 Subject: [PATCH 1886/2019] [mojo-lang] Allow `ref x: Type` in argument list. This treats `ref x: Foo` the same as `ref [_] x: Foo`, inferring the origin from the call site. This was discussed in the arg convention resyntaxing thread. MODULAR_ORIG_COMMIT_REV_ID: 6a9d97b8ef01b87dab972a3e783315ac3db8ff12 --- docs/changelog.md | 8 ++++++++ docs/manual/values/lifetimes.ipynb | 6 +++--- stdlib/src/builtin/rebind.mojo | 2 +- stdlib/src/builtin/reversed.mojo | 10 ++++----- stdlib/src/builtin/string_literal.mojo | 4 ++-- stdlib/src/builtin/tuple.mojo | 6 ++---- stdlib/src/collections/counter.mojo | 10 +++------ stdlib/src/collections/deque.mojo | 6 +++--- stdlib/src/collections/dict.mojo | 24 +++++++++------------- stdlib/src/collections/inline_array.mojo | 8 +++----- stdlib/src/collections/inline_list.mojo | 10 ++++----- stdlib/src/collections/list.mojo | 12 +++++------ stdlib/src/collections/optional.mojo | 4 ++-- stdlib/src/collections/set.mojo | 4 +--- stdlib/src/collections/string.mojo | 6 +++--- stdlib/src/memory/maybe_uninitialized.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 8 ++++---- stdlib/src/utils/span.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 12 +++++------ stdlib/src/utils/stringref.mojo | 2 +- stdlib/src/utils/variant.mojo | 6 +++--- 22 files changed, 72 insertions(+), 82 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d18b597ce4..bb06947757 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -180,6 +180,14 @@ what we publish. return a ``` +- `ref` function arguments without an origin clause are now treated as + `ref [_]`, which is more syntactically convenient and consistent: + + ```mojo + fn takes_and_return_ref(ref a: String) -> ref [a] String: + return a + ``` + - `Slice.step` is now an `Optional[Int]`, matching the optionality of `slice.step` in Python. ([PR #3160](https://github.com/modularml/mojo/pull/3160) by diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb index 314e186c81..f218538d67 100644 --- a/docs/manual/values/lifetimes.ipynb +++ b/docs/manual/values/lifetimes.ipynb @@ -323,7 +323,7 @@ " can think of the underscore as a wildcard that will accept any origin:\n", "\n", " ```mojo\n", - " def add_ref(ref [_] a: Int, b: Int) -> Int:\n", + " def add_ref(ref a: Int, b: Int) -> Int:\n", " return a+b\n", " ```" ] @@ -427,7 +427,7 @@ " for name in names:\n", " self.names.append(name[])\n", "\n", - " def __getitem__(ref [_] self: Self, index: Int) ->\n", + " def __getitem__(ref self, index: Int) ->\n", " ref [self.names] String:\n", " if (index >=0 and index < len(self.names)):\n", " return self.names[index]\n", @@ -530,7 +530,7 @@ "above:\n", "\n", "```mojo\n", - "def __getitem__(ref [_] self: Self, index: Int) ->\n", + "def __getitem__(ref self, index: Int) ->\n", " ref [self] String:\n", "```\n", "\n", diff --git a/stdlib/src/builtin/rebind.mojo b/stdlib/src/builtin/rebind.mojo index 5d9debeeab..baa60c0233 100644 --- a/stdlib/src/builtin/rebind.mojo +++ b/stdlib/src/builtin/rebind.mojo @@ -46,7 +46,7 @@ fn rebind[ fn rebind[ src_type: AnyType, //, dest_type: AnyType, -](ref [_]src: src_type) -> ref [src] dest_type: +](ref src: src_type) -> ref [src] dest_type: """Statically assert that a parameter input type `src_type` resolves to the same type as a parameter result type `dest_type` after function instantiation and "rebind" the input to the result type, returning a diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 20f4fa4db3..352f6d10b4 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -79,7 +79,7 @@ fn reversed[T: ReversibleRange](value: T) -> _StridedRange: fn reversed[ T: CollectionElement -](ref [_]value: List[T, *_]) -> _ListIter[ +](ref value: List[T, *_]) -> _ListIter[ T, __type_of(value).hint_trivial_type, __origin_of(value), False ]: """Get a reversed iterator of the input list. @@ -100,7 +100,7 @@ fn reversed[ fn reversed[ T: CollectionElement -](ref [_]value: Deque[T]) -> _DequeIter[T, __origin_of(value), False]: +](ref value: Deque[T]) -> _DequeIter[T, __origin_of(value), False]: """Get a reversed iterator of the deque. **Note**: iterators are currently non-raising. @@ -120,7 +120,7 @@ fn reversed[ fn reversed[ K: KeyElement, V: CollectionElement, -](ref [_]value: Dict[K, V],) -> _DictKeyIter[K, V, __origin_of(value), False]: +](ref value: Dict[K, V],) -> _DictKeyIter[K, V, __origin_of(value), False]: """Get a reversed iterator of the input dict. **Note**: iterators are currently non-raising. @@ -143,7 +143,7 @@ fn reversed[ V: CollectionElement, dict_mutability: Bool, dict_origin: Origin[dict_mutability].type, -](ref [_]value: _DictValueIter[K, V, dict_origin]) -> _DictValueIter[ +](ref value: _DictValueIter[K, V, dict_origin]) -> _DictValueIter[ K, V, dict_origin, False ]: """Get a reversed iterator of the input dict values. @@ -170,7 +170,7 @@ fn reversed[ V: CollectionElement, dict_mutability: Bool, dict_origin: Origin[dict_mutability].type, -](ref [_]value: _DictEntryIter[K, V, dict_origin]) -> _DictEntryIter[ +](ref value: _DictEntryIter[K, V, dict_origin]) -> _DictEntryIter[ K, V, dict_origin, False ]: """Get a reversed iterator of the input dict items. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 2974eadbb7..a298ae5189 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -364,7 +364,7 @@ struct StringLiteral( """ return self.__str__() - fn __iter__(ref [_]self) -> _StringSliceIter[StaticConstantOrigin]: + fn __iter__(ref self) -> _StringSliceIter[StaticConstantOrigin]: """Return an iterator over the string literal. Returns: @@ -468,7 +468,7 @@ struct StringLiteral( ) @always_inline - fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 8984289bb6..efad7a2226 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -153,9 +153,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): return Self.__len__() @always_inline("nodebug") - fn __getitem__[ - idx: Int - ](ref [_]self: Self) -> ref [self] element_types[idx.value]: + fn __getitem__[idx: Int](ref self) -> ref [self] element_types[idx.value]: """Get a reference to an element in the tuple. Parameters: @@ -178,7 +176,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](ref [_]self) -> ref [self] T: + fn get[i: Int, T: CollectionElement](ref self) -> ref [self] T: """Get a tuple element and rebind to the specified type. Parameters: diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index 64db6a205a..a02f2136ad 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -481,9 +481,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.pop(value, default) - fn keys( - ref [_]self: Self, - ) -> _DictKeyIter[V, Int, __origin_of(self._data)]: + fn keys(ref self) -> _DictKeyIter[V, Int, __origin_of(self._data)]: """Iterate over the Counter's keys as immutable references. Returns: @@ -491,9 +489,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.keys() - fn values( - ref [_]self: Self, - ) -> _DictValueIter[V, Int, __origin_of(self._data)]: + fn values(ref self) -> _DictValueIter[V, Int, __origin_of(self._data)]: """Iterate over the Counter's values as references. Returns: @@ -501,7 +497,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.values() - fn items(self: Self) -> _DictEntryIter[V, Int, __origin_of(self._data)]: + fn items(self) -> _DictEntryIter[V, Int, __origin_of(self._data)]: """Iterate over the dict's entries as immutable references. Returns: diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index c7a02c219a..84bc48bec4 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -338,7 +338,7 @@ struct Deque[ElementType: CollectionElement]( return False fn __iter__( - ref [_]self, + ref self, ) -> _DequeIter[ElementType, __origin_of(self)]: """Iterates over elements of the deque, returning the references. @@ -348,7 +348,7 @@ struct Deque[ElementType: CollectionElement]( return _DequeIter(0, Pointer.address_of(self)) fn __reversed__( - ref [_]self, + ref self, ) -> _DequeIter[ElementType, __origin_of(self), False]: """Iterate backwards over the deque, returning the references. @@ -379,7 +379,7 @@ struct Deque[ElementType: CollectionElement]( """ return (self._tail - self._head) & (self._capacity - 1) - fn __getitem__(ref [_]self, idx: Int) -> ref [self] ElementType: + fn __getitem__(ref self, idx: Int) -> ref [self] ElementType: """Gets the deque element at the given index. Args: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index bd9cdb5687..2ecba20f32 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -617,9 +617,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return self.find(key).__bool__() - fn __iter__( - ref [_]self: Self, - ) -> _DictKeyIter[K, V, __origin_of(self)]: + fn __iter__(ref self) -> _DictKeyIter[K, V, __origin_of(self)]: """Iterate over the dict's keys as immutable references. Returns: @@ -627,9 +625,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return _DictKeyIter(_DictEntryIter(0, 0, self)) - fn __reversed__( - ref [_]self: Self, - ) -> _DictKeyIter[K, V, __origin_of(self), False]: + fn __reversed__(ref self) -> _DictKeyIter[K, V, __origin_of(self), False]: """Iterate backwards over the dict keys, returning immutable references. Returns: @@ -761,7 +757,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( # TODO(MOCO-604): Return Optional[Pointer] instead of raising fn _find_ref( - ref [_]self: Self, key: K + ref self, key: K ) raises -> ref [self._entries[0].value().value] Self.V: """Find a value in the dictionary by key. @@ -880,7 +876,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( raise "KeyError: popitem(): dictionary is empty" - fn keys(ref [_]self: Self) -> _DictKeyIter[K, V, __origin_of(self)]: + fn keys(ref self) -> _DictKeyIter[K, V, __origin_of(self)]: """Iterate over the dict's keys as immutable references. Returns: @@ -888,7 +884,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return Self.__iter__(self) - fn values(ref [_]self: Self) -> _DictValueIter[K, V, __origin_of(self)]: + fn values(ref self) -> _DictValueIter[K, V, __origin_of(self)]: """Iterate over the dict's values as references. Returns: @@ -896,7 +892,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return _DictValueIter(_DictEntryIter(0, 0, self)) - fn items(ref [_]self: Self) -> _DictEntryIter[K, V, __origin_of(self)]: + fn items(ref self) -> _DictEntryIter[K, V, __origin_of(self)]: """Iterate over the dict's entries as immutable references. These can't yet be unpacked like Python dict items, but you can @@ -1215,7 +1211,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict.pop(key) fn __iter__( - ref [_]self: Self, + ref self, ) -> _DictKeyIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dict's keys as immutable references. @@ -1225,7 +1221,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict.keys() fn keys( - ref [_]self: Self, + ref self, ) -> _DictKeyIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dict's keys as immutable references. @@ -1235,7 +1231,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict.keys() fn values( - ref [_]self: Self, + ref self, ) -> _DictValueIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dict's values as references. @@ -1245,7 +1241,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict.values() fn items( - ref [_]self: Self, + ref self, ) -> _DictEntryIter[Self.key_type, V, __origin_of(self._dict)]: """Iterate over the keyword dictionary's entries as immutable references. diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 7aab9d4618..642bc72104 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -232,7 +232,7 @@ struct InlineArray[ # ===------------------------------------------------------------------===# @always_inline - fn __getitem__(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: + fn __getitem__(ref self, idx: Int) -> ref [self] Self.ElementType: """Get a `Pointer` to the element at the given index. Args: @@ -245,9 +245,7 @@ struct InlineArray[ return self.unsafe_get(normalized_index) @always_inline - fn __getitem__[ - idx: Int, - ](ref [_]self: Self) -> ref [self] Self.ElementType: + fn __getitem__[idx: Int](ref self) -> ref [self] Self.ElementType: """Get a `Pointer` to the element at the given index. Parameters: @@ -284,7 +282,7 @@ struct InlineArray[ # ===------------------------------------------------------------------===# @always_inline - fn unsafe_get(ref [_]self: Self, idx: Int) -> ref [self] Self.ElementType: + fn unsafe_get(ref self, idx: Int) -> ref [self] Self.ElementType: """Get a reference to an element of self without checking index bounds. Users should opt for `__getitem__` instead of this method as it is diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 792635f427..285944f10d 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -132,7 +132,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): @always_inline fn __getitem__( - ref [_]self: Self, owned idx: Int + ref self, owned idx: Int ) -> ref [self._array] Self.ElementType: """Get a `Pointer` to the element at the given index. @@ -174,7 +174,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): return len(self) > 0 fn __iter__( - ref [_]self: Self, + ref self, ) -> _InlineListIter[ElementType, capacity, __origin_of(self)]: """Iterate over elements of the list, returning immutable references. @@ -185,7 +185,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): fn __contains__[ C: EqualityComparableCollectionElement, // - ](self: Self, value: C) -> Bool: + ](self, value: C) -> Bool: """Verify if a given value is present in the list. ```mojo @@ -215,9 +215,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # Methods # ===-------------------------------------------------------------------===# - fn count[ - C: EqualityComparableCollectionElement, // - ](self: Self, value: C) -> Int: + fn count[C: EqualityComparableCollectionElement, //](self, value: C) -> Int: """Counts the number of occurrences of a value in the list. ```mojo diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 301825d0a7..d63b8d6aa8 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -345,9 +345,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( """ self.extend(other^) - fn __iter__( - ref [_]self: Self, - ) -> _ListIter[T, hint_trivial_type, __origin_of(self)]: + fn __iter__(ref self) -> _ListIter[T, hint_trivial_type, __origin_of(self)]: """Iterate over elements of the list, returning immutable references. Returns: @@ -356,7 +354,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return _ListIter(0, Pointer.address_of(self)) fn __reversed__( - ref [_]self: Self, + ref self, ) -> _ListIter[T, hint_trivial_type, __origin_of(self), False]: """Iterate backwards over the list, returning immutable references. @@ -689,7 +687,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn index[ C: EqualityComparableCollectionElement, // ]( - ref [_]self: List[C, *_], + ref self: List[C, *_], value: C, start: Int = 0, stop: Optional[Int] = None, @@ -785,7 +783,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return res^ - fn __getitem__(ref [_]self, idx: Int) -> ref [self] T: + fn __getitem__(ref self, idx: Int) -> ref [self] T: """Gets the list element at the given index. Args: @@ -810,7 +808,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return (self.data + normalized_idx)[] @always_inline - fn unsafe_get(ref [_]self: Self, idx: Int) -> ref [self] Self.T: + fn unsafe_get(ref self, idx: Int) -> ref [self] Self.T: """Get a reference to an element of self without checking index bounds. Users should consider using `__getitem__` instead of this method as it diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index d9326aa4cc..82eab48450 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -298,7 +298,7 @@ struct Optional[T: CollectionElement]( # ===-------------------------------------------------------------------===# @always_inline - fn value(ref [_]self: Self) -> ref [self._value] T: + fn value(ref self) -> ref [self._value] T: """Retrieve a reference to the value of the Optional. This check to see if the optional contains a value. @@ -315,7 +315,7 @@ struct Optional[T: CollectionElement]( return self.unsafe_value() @always_inline - fn unsafe_value(ref [_]self: Self) -> ref [self._value] T: + fn unsafe_value(ref self) -> ref [self._value] T: """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 809d838a55..bd37074c32 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -354,9 +354,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # Methods # ===-------------------------------------------------------------------===# - fn __iter__( - ref [_]self: Self, - ) -> _DictKeyIter[T, NoneType, __origin_of(self._data)]: + fn __iter__(ref self) -> _DictKeyIter[T, NoneType, __origin_of(self._data)]: """Iterate over elements of the set, returning immutable references. Returns: diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 09112037b9..144e7cc493 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1529,7 +1529,7 @@ struct String( return self.unsafe_ptr().bitcast[c_char]() @always_inline - fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: @@ -1545,7 +1545,7 @@ struct String( ) @always_inline - fn as_string_slice(ref [_]self) -> StringSlice[__origin_of(self)]: + fn as_string_slice(ref self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this string. Returns: @@ -2024,7 +2024,7 @@ struct String( return copy fn startswith( - ref [_]self, prefix: String, start: Int = 0, end: Int = -1 + ref self, prefix: String, start: Int = 0, end: Int = -1 ) -> Bool: """Checks if the string starts with the specified prefix between start and end positions. Returns True if found and False otherwise. diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 010822a602..b25dbda51d 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -213,7 +213,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): @always_inline fn assume_initialized( - ref [_]self: Self, + ref self: Self, ) -> ref [self] Self.ElementType: """Returns a reference to the internal value. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 18ee49465f..21cb2a5881 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -267,7 +267,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return self._storage[String].unsafe_ptr() @always_inline - fn as_string_slice(ref [_]self: Self) -> StringSlice[__origin_of(self)]: + fn as_string_slice(ref self: Self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this inline string. Returns: @@ -280,7 +280,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref [_]self: Self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self: Self) -> Span[Byte, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -505,7 +505,7 @@ struct _FixedString[CAP: Int]( return self.buffer.unsafe_ptr() @always_inline - fn as_string_slice(ref [_]self: Self) -> StringSlice[__origin_of(self)]: + fn as_string_slice(ref self: Self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this fixed string. Returns: @@ -518,7 +518,7 @@ struct _FixedString[CAP: Int]( return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref [_]self: Self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self: Self) -> Span[Byte, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 97dc5b6aae..4eb0679cd7 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -31,7 +31,7 @@ trait AsBytes: span. """ - fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous slice of the bytes owned by this string. Returns: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index f4066c9a4e..8248fae4f0 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -33,7 +33,7 @@ fn _set_array_elem[ type: AnyTrivialRegType, ]( val: type, - ref [_]array: __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], + ref array: __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], ): """Sets the array element at position `index` with the value `val`. diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index effc8ec5c9..50619e7978 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -566,7 +566,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( buf.append(0) return String(buf^) - fn __contains__(ref [_]self, substr: StringSlice[_]) -> Bool: + fn __contains__(ref self, substr: StringSlice[_]) -> Bool: """Returns True if the substring is contained within the current string. Args: @@ -784,7 +784,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """ return _FormatCurlyEntry.format(self, args) - fn find(ref [_]self, substr: StringSlice, start: Int = 0) -> Int: + fn find(ref self, substr: StringSlice, start: Int = 0) -> Int: """Finds the offset of the first occurrence of `substr` starting at `start`. If not found, returns `-1`. @@ -1186,19 +1186,19 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): self.format_spec = format_spec @always_inline - fn is_escaped_brace(ref [_]self) -> Bool: + fn is_escaped_brace(ref self) -> Bool: return self.field.isa[Bool]() @always_inline - fn is_kwargs_field(ref [_]self) -> Bool: + fn is_kwargs_field(ref self) -> Bool: return self.field.isa[String]() @always_inline - fn is_automatic_indexing(ref [_]self) -> Bool: + fn is_automatic_indexing(ref self) -> Bool: return self.field.isa[NoneType]() @always_inline - fn is_manual_indexing(ref [_]self) -> Bool: + fn is_manual_indexing(ref self) -> Bool: return self.field.isa[Int]() @staticmethod diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0d1ede078e..391440abd7 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -213,7 +213,7 @@ struct StringRef( return StringRef() return Self(self.data, self.length - num_bytes) - fn as_bytes(ref [_]self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self) -> Span[Byte, __origin_of(self)]: """Returns a contiguous Span of the bytes owned by this string. Returns: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index e26cd537fc..4b36fab203 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -194,7 +194,7 @@ struct Variant[*Ts: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - fn __getitem__[T: CollectionElement](ref [_]self: Self) -> ref [self] T: + fn __getitem__[T: CollectionElement](ref self: Self) -> ref [self] T: """Get the value out of the variant as a type-checked type. This explicitly check that your value is of that type! @@ -230,7 +230,7 @@ struct Variant[*Ts: CollectionElement]( return discr_ptr @always_inline("nodebug") - fn _get_discr(ref [_]self: Self) -> ref [self] UInt8: + fn _get_discr(ref self: Self) -> ref [self] UInt8: var ptr = UnsafePointer.address_of(self._impl).address var discr_ptr = __mlir_op.`pop.variant.discr_gep`[ _type = __mlir_type.`!kgen.pointer>` @@ -362,7 +362,7 @@ struct Variant[*Ts: CollectionElement]( alias idx = Self._check[T]() return self._get_discr() == idx - fn unsafe_get[T: CollectionElement](ref [_]self: Self) -> ref [self] T: + fn unsafe_get[T: CollectionElement](ref self: Self) -> ref [self] T: """Get the value out of the variant as a type-checked type. This doesn't explicitly check that your value is of that type! From 9f8253d3b3c08652b9d89492244bf1f92d4b7afb Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 17 Nov 2024 13:03:21 -0800 Subject: [PATCH 1887/2019] [mojo-stdlib] Use implicit `Self`, NFC. This moves from `self: Self` to just `self` to clean things up. MODULAR_ORIG_COMMIT_REV_ID: 1a26d34dcb9cc22b9d8a9da0e94402a2730e786c --- docs/manual/decorators/nonmaterializable.ipynb | 2 +- stdlib/src/collections/counter.mojo | 2 +- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/memory/maybe_uninitialized.mojo | 4 +--- stdlib/src/utils/inline_string.mojo | 8 ++++---- stdlib/src/utils/lock.mojo | 12 ++++++------ stdlib/src/utils/variant.mojo | 6 +++--- stdlib/test/collections/test_string.mojo | 2 +- 8 files changed, 18 insertions(+), 20 deletions(-) diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb index 4e88c205ae..49d6b312fc 100644 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ b/docs/manual/decorators/nonmaterializable.ipynb @@ -62,7 +62,7 @@ " var x: Int\n", "\n", " @always_inline(\"nodebug\")\n", - " fn __add__(self: Self, rhs: Self) -> Self:\n", + " fn __add__(self, rhs: Self) -> Self:\n", " return NmStruct(self.x + rhs.x)\n", "\n", "alias still_nm_struct = NmStruct(1) + NmStruct(2)\n", diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index a02f2136ad..ef99934506 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -121,7 +121,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ self._data[value] = count - fn __iter__(self: Self) -> _DictKeyIter[V, Int, __origin_of(self._data)]: + fn __iter__(self) -> _DictKeyIter[V, Int, __origin_of(self._data)]: """Iterate over the keyword dict's keys as immutable references. Returns: diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 4ff31e4900..f8fde04682 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -140,7 +140,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): fn __getitem__[ self_life: ImmutableOrigin ]( - ref [self_life]self: Self, + ref [self_life]self, ) -> ref [ _lit_mut_cast[self_life, result_mutable=True].result ] T: diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index b25dbda51d..20901a6fff 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -212,9 +212,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): self.unsafe_ptr().init_pointee_move(value^) @always_inline - fn assume_initialized( - ref self: Self, - ) -> ref [self] Self.ElementType: + fn assume_initialized(ref self) -> ref [self] Self.ElementType: """Returns a reference to the internal value. Calling this method assumes that the memory is initialized. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 21cb2a5881..d7b543a704 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -267,7 +267,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return self._storage[String].unsafe_ptr() @always_inline - fn as_string_slice(ref self: Self) -> StringSlice[__origin_of(self)]: + fn as_string_slice(ref self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this inline string. Returns: @@ -280,7 +280,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref self: Self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self) -> Span[Byte, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. @@ -505,7 +505,7 @@ struct _FixedString[CAP: Int]( return self.buffer.unsafe_ptr() @always_inline - fn as_string_slice(ref self: Self) -> StringSlice[__origin_of(self)]: + fn as_string_slice(ref self) -> StringSlice[__origin_of(self)]: """Returns a string slice of the data owned by this fixed string. Returns: @@ -518,7 +518,7 @@ struct _FixedString[CAP: Int]( return StringSlice(unsafe_from_utf8=self.as_bytes()) @always_inline - fn as_bytes(ref self: Self) -> Span[Byte, __origin_of(self)]: + fn as_bytes(ref self) -> Span[Byte, __origin_of(self)]: """ Returns a contiguous slice of the bytes owned by this string. diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index e7529401d7..9737dc2178 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -29,20 +29,20 @@ struct SpinWaiter: var storage: OpaquePointer """Pointer to the underlying SpinWaiter instance.""" - fn __init__(out self: Self): + fn __init__(out self): """Initializes a SpinWaiter instance.""" self.storage = external_call[ "KGEN_CompilerRT_AsyncRT_InitializeSpinWaiter", OpaquePointer, ]() - fn __del__(owned self: Self): + fn __del__(owned self): """Destroys the SpinWaiter instance.""" external_call["KGEN_CompilerRT_AsyncRT_DestroySpinWaiter", NoneType]( self.storage ) - fn wait(self: Self): + fn wait(self): """Blocks the current task for a duration determined by the underlying policy.""" external_call["KGEN_CompilerRT_AsyncRT_SpinWaiter_Wait", NoneType]( @@ -60,12 +60,12 @@ struct BlockingSpinLock: var counter: Atomic[DType.int64] """The atomic counter implementing the spin lock.""" - fn __init__(out self: Self): + fn __init__(out self): """Default constructor.""" self.counter = Atomic[DType.int64](Self.UNLOCKED) - fn lock(inout self: Self, owner: Int): + fn lock(inout self, owner: Int): """Acquires the lock. Args: @@ -79,7 +79,7 @@ struct BlockingSpinLock: waiter.wait() expected = Self.UNLOCKED - fn unlock(inout self: Self, owner: Int) -> Bool: + fn unlock(inout self, owner: Int) -> Bool: """Releases the lock. Args: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 4b36fab203..308e0761e8 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -194,7 +194,7 @@ struct Variant[*Ts: CollectionElement]( # Operator dunders # ===-------------------------------------------------------------------===# - fn __getitem__[T: CollectionElement](ref self: Self) -> ref [self] T: + fn __getitem__[T: CollectionElement](ref self) -> ref [self] T: """Get the value out of the variant as a type-checked type. This explicitly check that your value is of that type! @@ -230,7 +230,7 @@ struct Variant[*Ts: CollectionElement]( return discr_ptr @always_inline("nodebug") - fn _get_discr(ref self: Self) -> ref [self] UInt8: + fn _get_discr(ref self) -> ref [self] UInt8: var ptr = UnsafePointer.address_of(self._impl).address var discr_ptr = __mlir_op.`pop.variant.discr_gep`[ _type = __mlir_type.`!kgen.pointer>` @@ -362,7 +362,7 @@ struct Variant[*Ts: CollectionElement]( alias idx = Self._check[T]() return self._get_discr() == idx - fn unsafe_get[T: CollectionElement](ref self: Self) -> ref [self] T: + fn unsafe_get[T: CollectionElement](ref self) -> ref [self] T: """Get the value out of the variant as a type-checked type. This doesn't explicitly check that your value is of that type! diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index dc925e41f2..9f81629b8a 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -32,7 +32,7 @@ from utils import StringRef, StringSlice @value struct AString(Stringable): - fn __str__(self: Self) -> String: + fn __str__(self) -> String: return "a string" From ca86d7574f51d2e17c25f359f8b4b3872548200a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 17 Nov 2024 13:34:07 -0800 Subject: [PATCH 1888/2019] [mojo-tools] Fix mojo-doc printing of ref args and results. (#51016) THis makes us print implicit origins correctly. MODULAR_ORIG_COMMIT_REV_ID: 94ecbc049d3c60b3208437aad0d5099adfbb91d7 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index bb06947757..1da385ce63 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -521,3 +521,6 @@ what we publish. ```plaintext ... cannot be converted from 'UnsafePointer[UInt]' to 'UnsafePointer[Int]' ``` + +- Tooling now prints the origins of `ref` arguments and results correctly, and + prints `self` instead of `self: Self` in methods. From 722fc566d22073534eae3b7b143a8d24171f38bf Mon Sep 17 00:00:00 2001 From: Lukas Hermann <1734032+lsh@users.noreply.github.com> Date: Sun, 17 Nov 2024 16:27:13 -0800 Subject: [PATCH 1889/2019] [stdlib] Add comparison to StringSlice for StringLiteral This commit adds the necessary equality functions to allow `StringLiteral` to compare to `StringSlice` such that the literal is on the left hand side. Note that since `String` can coerce to `StringLiteral`, this overload should handle both cases. MODULAR_ORIG_COMMIT_REV_ID: 82abd25fc36ee6534887f037137743ba36a3819d --- stdlib/src/builtin/string_literal.mojo | 24 ++++++++++++++++++++ stdlib/test/builtin/test_string_literal.mojo | 9 ++++++++ 2 files changed, 33 insertions(+) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index a298ae5189..7aa426d5d9 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -197,6 +197,30 @@ struct StringLiteral( """ return StringRef(self) != StringRef(rhs) + @always_inline("nodebug") + fn __eq__(self, rhs: StringSlice) -> Bool: + """Compare two string literals for equality. + + Args: + rhs: The string to compare. + + Returns: + True if they are equal. + """ + return not (self != rhs) + + @always_inline("nodebug") + fn __ne__(self, rhs: StringSlice) -> Bool: + """Compare two string literals for inequality. + + Args: + rhs: The string to compare. + + Returns: + True if they are not equal. + """ + return self.as_string_slice() != rhs + @always_inline("nodebug") fn __lt__(self, rhs: StringLiteral) -> Bool: """Compare this StringLiteral to the RHS using LT comparison. diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index f8fb2825e5..1e4ca44832 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -66,6 +66,15 @@ def test_equality(): assert_true(StringLiteral.__ne__("five", "six")) assert_false(StringLiteral.__ne__("six", "six")) + var hello = str("hello") + var hello_ref = hello.as_string_slice() + + assert_false(StringLiteral.__eq__("goodbye", hello)) + assert_true(StringLiteral.__eq__("hello", hello)) + + assert_false(StringLiteral.__eq__("goodbye", hello_ref)) + assert_true(StringLiteral.__eq__("hello", hello_ref)) + def test_len(): assert_equal(0, StringLiteral.__len__("")) From 21563ab744ceb8d7ad16d1a7c1a44897117e2513 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sun, 17 Nov 2024 18:36:17 -0800 Subject: [PATCH 1890/2019] [stdlib] Add implicit decorators to constructors, NFC This adds `@implicit` to all single-argument constructors to enable explicit by default construction. Implicit conversions are used heavily throughout the codebase, and so this is a required step to get it to compile. This allows us to take off `@implicit` constructors where they don't make sense, and fix the compiler errors one step at a time. MODULAR_ORIG_COMMIT_REV_ID: 931a2f38a0f9fe87007e334158f1fdf399f04bb2 --- examples/matmul.mojo | 1 + stdlib/src/builtin/anytype.mojo | 1 + stdlib/src/builtin/builtin_list.mojo | 6 ++++++ stdlib/src/builtin/coroutine.mojo | 2 ++ stdlib/src/builtin/error.mojo | 3 +++ stdlib/src/builtin/file_descriptor.mojo | 2 ++ stdlib/src/builtin/float_literal.mojo | 2 ++ stdlib/src/builtin/io.mojo | 1 + stdlib/src/builtin/none.mojo | 10 ++++++++++ stdlib/src/builtin/object.mojo | 20 ++++++++++++++++++++ stdlib/src/builtin/range.mojo | 3 +++ stdlib/src/builtin/simd.mojo | 2 ++ stdlib/src/builtin/sort.mojo | 4 ++++ stdlib/src/builtin/string_literal.mojo | 1 + stdlib/src/builtin/tuple.mojo | 1 + stdlib/src/builtin/value.mojo | 3 +++ stdlib/src/collections/counter.mojo | 1 + stdlib/src/collections/deque.mojo | 2 ++ stdlib/src/collections/dict.mojo | 1 + stdlib/src/collections/inline_array.mojo | 2 ++ stdlib/src/collections/inline_list.mojo | 1 + stdlib/src/collections/list.mojo | 2 ++ stdlib/src/collections/optional.mojo | 6 ++++++ stdlib/src/collections/set.mojo | 3 +++ stdlib/src/collections/vector.mojo | 2 ++ stdlib/src/memory/arc.mojo | 2 ++ stdlib/src/memory/pointer.mojo | 3 +++ stdlib/src/memory/unsafe_pointer.mojo | 2 ++ stdlib/src/os/atomic.mojo | 1 + stdlib/src/pathlib/path.mojo | 1 + stdlib/src/python/_cpython.mojo | 2 ++ stdlib/src/python/python.mojo | 1 + stdlib/src/python/python_object.mojo | 15 +++++++++++++++ stdlib/src/sys/intrinsics.mojo | 3 +++ stdlib/src/utils/index.mojo | 17 +++++++++++++++++ stdlib/src/utils/inline_string.mojo | 2 ++ stdlib/src/utils/span.mojo | 1 + stdlib/src/utils/static_tuple.mojo | 12 ++++++++++++ stdlib/src/utils/stringref.mojo | 2 ++ stdlib/src/utils/variant.mojo | 1 + stdlib/src/utils/write.mojo | 2 ++ stdlib/test/builtin/test_none.mojo | 3 +++ stdlib/test/collections/test_dict.mojo | 4 ++++ stdlib/test/collections/test_list.mojo | 1 + stdlib/test/test_utils/types.mojo | 4 ++++ 45 files changed, 161 insertions(+) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 44cc91f097..77eb9c447d 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -57,6 +57,7 @@ struct Matrix[rows: Int, cols: Int]: memset_zero(self.data, rows * cols) # Initialize taking a pointer, don't set any elements + @implicit fn __init__(out self, data: UnsafePointer[Scalar[type]]): self.data = data diff --git a/stdlib/src/builtin/anytype.mojo b/stdlib/src/builtin/anytype.mojo index 03da7a124d..8c7a061ba5 100644 --- a/stdlib/src/builtin/anytype.mojo +++ b/stdlib/src/builtin/anytype.mojo @@ -46,6 +46,7 @@ trait AnyType: var p: UnsafePointer[Int] var size: Int + @implicit fn __init__(out self, size: Int): self.p = UnsafePointer[Int].alloc(size) self.size = size diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 7541b4f9c4..80e4b61185 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -40,6 +40,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): # ===-------------------------------------------------------------------===# @always_inline + @implicit fn __init__(out self, owned *args: *Ts): """Construct the list literal from the given values. @@ -164,6 +165,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): alias IterType = _VariadicListIter[type] @always_inline + @implicit fn __init__(out self, *value: type): """Constructs a VariadicList from a variadic list of arguments. @@ -175,6 +177,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): @doc_private @always_inline + @implicit fn __init__(out self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. @@ -327,6 +330,7 @@ struct VariadicListMem[ # Provide support for borrowed variadic arguments. @doc_private @always_inline + @implicit fn __init__(out self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. @@ -344,6 +348,7 @@ struct VariadicListMem[ ] @always_inline + @implicit fn __init__(out self, value: Self._inout_variadic_type): """Constructs a VariadicList from a variadic argument type. @@ -364,6 +369,7 @@ struct VariadicListMem[ ] @always_inline + @implicit fn __init__(out self, value: Self._owned_variadic_type): """Constructs a VariadicList from a variadic argument type. diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 4c8517d687..d33445e100 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -120,6 +120,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: ) @always_inline + @implicit fn __init__(out self, handle: AnyCoroutine): """Construct a coroutine object from a handle. @@ -200,6 +201,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: ) @always_inline + @implicit fn __init__(out self, handle: AnyCoroutine): """Construct a coroutine object from a handle. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 5e74bdff77..c319e708ea 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -65,6 +65,7 @@ struct Error( self.loaded_length = 0 @always_inline + @implicit fn __init__(out self, value: StringLiteral): """Construct an Error object with a given string literal. @@ -74,6 +75,7 @@ struct Error( self.data = value.unsafe_ptr() self.loaded_length = len(value) + @implicit fn __init__(out self, src: String): """Construct an Error object with a given string. @@ -91,6 +93,7 @@ struct Error( self.data = dest self.loaded_length = -length + @implicit fn __init__(out self, src: StringRef): """Construct an Error object with a given string ref. diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 9edbc8c58c..241fa10bf3 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -38,6 +38,7 @@ struct FileDescriptor(Writer): var value: Int """The underlying value of the file descriptor.""" + @implicit fn __init__(out self, value: Int = 1): """Constructs the file descriptor from an integer. @@ -46,6 +47,7 @@ struct FileDescriptor(Writer): """ self.value = value + @implicit fn __init__(out self, f: FileHandle): """Constructs the file descriptor from a file handle. diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index c3da23fcbc..b454ba6d94 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -49,6 +49,7 @@ struct FloatLiteral( # ===------------------------------------------------------------------===# @always_inline("nodebug") + @implicit fn __init__(out self, value: Self.fp_type): """Create a FloatLiteral value from a kgen.float_literal value. @@ -58,6 +59,7 @@ struct FloatLiteral( self.value = value @always_inline("nodebug") + @implicit fn __init__(out self, value: IntLiteral): """Convert an IntLiteral to a FloatLiteral value. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index f02b5b1a80..c2d3f82a33 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -43,6 +43,7 @@ from utils import StringRef, StaticString, StringSlice struct _fdopen[mode: StringLiteral = "a"]: var handle: OpaquePointer + @implicit fn __init__(out self, stream_id: FileDescriptor): """Creates a file handle to the stdout/stderr stream. diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo index 966525ef95..3cfc856b32 100644 --- a/stdlib/src/builtin/none.mojo +++ b/stdlib/src/builtin/none.mojo @@ -37,6 +37,16 @@ struct NoneType( """Construct an instance of the `None` type.""" self._value = None + @always_inline + @implicit + fn __init__(out self, value: Self._mlir_type): + """Construct an instance of the `None` type. + + Args: + value: The MLIR none type to construct from. + """ + self._value = value + @always_inline fn __init__(out self, *, other: Self): """Explicit copy constructor. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 5b1f41298f..c54d99fc18 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -178,6 +178,7 @@ struct _RefCountedAttrsDictRef(CollectionElement, CollectionElementNew): """The reference to the dictionary.""" @always_inline + @implicit fn __init__(out self, values: VariadicListMem[Attr, _]): var ptr = UnsafePointer[_RefCountedAttrsDict].alloc(1) __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() @@ -305,6 +306,7 @@ struct _ObjectImpl( # ===------------------------------------------------------------------=== # @always_inline + @implicit fn __init__(out self, value: Self.type): self.value = value @@ -313,6 +315,7 @@ struct _ObjectImpl( self.value = Self.type(_NoneMarker {}) @always_inline + @implicit fn __init__(out self, value: Bool): self.value = Self.type(value) @@ -325,18 +328,22 @@ struct _ObjectImpl( self.value = Self.type(value) @always_inline + @implicit fn __init__(out self, value: _ImmutableString): self.value = Self.type(value) @always_inline + @implicit fn __init__(out self, value: _RefCountedListRef): self.value = Self.type(value) @always_inline + @implicit fn __init__(out self, value: _Function): self.value = Self.type(value) @always_inline + @implicit fn __init__(out self, value: _RefCountedAttrsDictRef): self.value = Self.type(value) @@ -725,6 +732,7 @@ struct object( self._value = _ObjectImpl() @always_inline + @implicit fn __init__(out self, impl: _ObjectImpl): """Initializes the object with an implementation value. This is meant for internal use only. @@ -735,6 +743,7 @@ struct object( self._value = impl @always_inline + @implicit fn __init__(out self, none: NoneType): """Initializes a none value object from a `None` literal. @@ -746,6 +755,7 @@ struct object( # FIXME: None literal should be of NoneType not !kgen.none. @doc_private @always_inline + @implicit fn __init__(out self, none: __mlir_type.`!kgen.none`): """Initializes a none value object from a `None` literal. @@ -755,6 +765,7 @@ struct object( self = NoneType() @always_inline + @implicit fn __init__(out self, value: Int): """Initializes the object with an integer value. @@ -764,6 +775,7 @@ struct object( self._value = Int64(value) @always_inline + @implicit fn __init__(out self, value: Float64): """Initializes the object with an floating-point value. @@ -792,6 +804,7 @@ struct object( self._value = value @always_inline + @implicit fn __init__(out self, value: Bool): """Initializes the object from a bool. @@ -801,6 +814,7 @@ struct object( self._value = value @always_inline + @implicit fn __init__(out self, value: StringLiteral): """Initializes the object from a string literal. @@ -810,6 +824,7 @@ struct object( self = object(StringRef(value)) @always_inline + @implicit fn __init__(out self, value: StringRef): """Initializes the object from a string reference. @@ -861,6 +876,7 @@ struct object( ]() @always_inline + @implicit fn __init__(out self, func: Self.nullary_function): """Initializes an object from a function that takes no arguments. @@ -870,6 +886,7 @@ struct object( self._value = _Function(func) @always_inline + @implicit fn __init__(out self, func: Self.unary_function): """Initializes an object from a function that takes one argument. @@ -879,6 +896,7 @@ struct object( self._value = _Function(func) @always_inline + @implicit fn __init__(out self, func: Self.binary_function): """Initializes an object from a function that takes two arguments. @@ -888,6 +906,7 @@ struct object( self._value = _Function(func) @always_inline + @implicit fn __init__(out self, func: Self.ternary_function): """Initializes an object from a function that takes three arguments. @@ -897,6 +916,7 @@ struct object( self._value = _Function(func) @always_inline + @implicit fn __init__(out self, *attrs: Attr): """Initializes the object with a sequence of zero or more attributes. diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 2b9e691e42..345c2124a4 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -48,6 +48,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): var end: Int @always_inline + @implicit fn __init__(out self, end: Int): self.curr = max(0, end) self.end = self.curr @@ -324,6 +325,7 @@ struct _UIntZeroStartingRange(UIntSized): var end: UInt @always_inline + @implicit fn __init__(out self, end: UInt): self.curr = max(0, end) self.end = self.curr @@ -464,6 +466,7 @@ struct _ZeroStartingScalarRange[type: DType]: var end: Scalar[type] @always_inline + @implicit fn __init__(out self, end: Scalar[type]): self.curr = max(0, end) self.end = self.curr diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8f9e350580..a299140a57 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -366,6 +366,7 @@ struct SIMD[type: DType, size: Int]( ](value.value) @always_inline("nodebug") + @implicit fn __init__(out self, *elems: Scalar[type]): """Constructs a SIMD vector via a variadic list of elements. @@ -405,6 +406,7 @@ struct SIMD[type: DType, size: Int]( self[i] = elems[i] @always_inline + @implicit fn __init__(out self, value: FloatLiteral): """Initializes the SIMD vector with a float. diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 77d00ecc3e..8e2f0aadbe 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -34,6 +34,10 @@ alias insertion_sort_threshold = 32 struct _SortWrapper[type: CollectionElement](CollectionElement): var data: type + @implicit + fn __init__(out self, data: type): + self.data = data + fn __init__(out self, *, other: Self): self.data = other.data diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 7aa426d5d9..c363f1b7bd 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -70,6 +70,7 @@ struct StringLiteral( # ===-------------------------------------------------------------------===# @always_inline("nodebug") + @implicit fn __init__(out self, value: Self.type): """Create a string literal from a builtin string type. diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index efad7a2226..698a73286e 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -47,6 +47,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): """The underlying storage for the tuple.""" @always_inline("nodebug") + @implicit fn __init__(out self, owned *args: *element_types): """Construct the tuple. diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index d8e6d8e5ea..fab16a7b44 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -66,6 +66,7 @@ trait Copyable: struct Foo(Copyable): var s: String + @implicit fn __init__(out self, s: String): self.s = s @@ -117,9 +118,11 @@ trait ExplicitlyCopyable: struct Foo(ExplicitlyCopyable): var s: String + @implicit fn __init__(out self, s: String): self.s = s + @implicit fn __init__(out self, copy: Self): print("explicitly copying value") self.s = copy.s diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index ef99934506..e6000ce5fb 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -56,6 +56,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): self._data = Dict[V, Int]() # TODO: Change List to Iterable when it is supported in Mojo + @implicit fn __init__(out self, items: List[V, *_]): """Create a from an input iterable. diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index 84bc48bec4..5af332c273 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -132,6 +132,7 @@ struct Deque[ElementType: CollectionElement]( if elements is not None: self.extend(elements.value()) + @implicit fn __init__(out self, owned *values: ElementType): """Constructs a deque from the given values. @@ -167,6 +168,7 @@ struct Deque[ElementType: CollectionElement]( self._tail = args_length + @implicit fn __init__(out self, other: Self): """Creates a deepcopy of the given deque. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 2ecba20f32..e6c8c2a821 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -270,6 +270,7 @@ struct _DictIndex: var data: OpaquePointer @always_inline + @implicit fn __init__(out self, reserved: Int): if reserved <= 128: var data = UnsafePointer[Int8].alloc(reserved) diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 642bc72104..b1c833b75c 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -139,6 +139,7 @@ struct InlineArray[ ) @always_inline + @implicit fn __init__(out self, fill: Self.ElementType): """Constructs an empty array where each element is the supplied `fill`. @@ -157,6 +158,7 @@ struct InlineArray[ ptr.init_pointee_copy(fill) @always_inline + @implicit fn __init__(out self, owned *elems: Self.ElementType): """Constructs an array given a set of arguments. diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 285944f10d..9d5610ff36 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -109,6 +109,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): # TODO: Avoid copying elements in once owned varargs # allow transfers. + @implicit fn __init__(out self, *values: ElementType): """Constructs a list from the given values. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index d63b8d6aa8..1ed439c040 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -135,6 +135,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = 0 self.capacity = capacity + @implicit fn __init__(out self, owned *values: T): """Constructs a list from the given values. @@ -164,6 +165,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = length + @implicit fn __init__(out self, span: Span[T]): """Constructs a list from the a Span of values. diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 82eab48450..2bcaf9ea8e 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -92,6 +92,7 @@ struct Optional[T: CollectionElement]( """Construct an empty Optional.""" self._value = Self._type(_NoneType()) + @implicit fn __init__(out self, owned value: T): """Construct an Optional containing a value. @@ -104,6 +105,7 @@ struct Optional[T: CollectionElement]( # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @doc_private + @implicit fn __init__(out self, value: NoneType._mlir_type): """Construct an empty Optional. @@ -112,6 +114,7 @@ struct Optional[T: CollectionElement]( """ self = Self(value=NoneType(value)) + @implicit fn __init__(out self, value: NoneType): """Construct an empty Optional. @@ -407,6 +410,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): """Create an optional with a value of None.""" self = Self(None) + @implicit fn __init__(out self, value: T): """Create an optional with a value. @@ -421,6 +425,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @doc_private + @implicit fn __init__(out self, value: NoneType._mlir_type): """Construct an empty Optional. @@ -429,6 +434,7 @@ struct OptionalReg[T: AnyTrivialRegType](Boolable): """ self = Self(value=NoneType(value)) + @implicit fn __init__(out self, value: NoneType): """Create an optional without a value from a None literal. diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index bd37074c32..10451bc126 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -54,6 +54,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # Life cycle methods # ===-------------------------------------------------------------------===# + @implicit fn __init__(out self, *ts: T): """Construct a set from initial elements. @@ -64,6 +65,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for t in ts: self.add(t[]) + @implicit fn __init__(out self, elements: Self): """Explicitly copy another Set instance. @@ -74,6 +76,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for e in elements: self.add(e[]) + @implicit fn __init__(out self, elements: List[T, *_]): """Construct a set from a List of elements. diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 6fd87dd51e..8812b4b61c 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -114,6 +114,7 @@ struct InlinedFixedVector[ """The maximum number of elements that can fit in the vector.""" @always_inline + @implicit fn __init__(out self, capacity: Int): """Constructs `InlinedFixedVector` with the given capacity. @@ -130,6 +131,7 @@ struct InlinedFixedVector[ self.capacity = capacity @always_inline + @implicit fn __init__(out self, existing: Self): """ Copy constructor. diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index f8fde04682..fa904d53e8 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -54,6 +54,7 @@ struct _ArcInner[T: Movable]: var refcount: Atomic[DType.uint64] var payload: T + @implicit fn __init__(out self, owned value: T): """Create an initialized instance of this with a refcount of 1.""" self.refcount = Scalar[DType.uint64](1) @@ -88,6 +89,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): alias _inner_type = _ArcInner[T] var _inner: UnsafePointer[Self._inner_type] + @implicit fn __init__(out self, owned value: T): """Construct a new thread-safe, reference-counted smart pointer, and move the value into heap memory managed by the new pointer. diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index fcea6422e0..dfccdc524c 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -47,6 +47,7 @@ struct _GPUAddressSpace(EqualityComparable): """Local address space.""" @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): self._value = value @@ -170,6 +171,7 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): """Generic address space.""" @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): """Initializes the address space from the underlying integral value. @@ -179,6 +181,7 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): self._value = value @always_inline("nodebug") + @implicit fn __init__(out self, value: _GPUAddressSpace): """Initializes the address space from the underlying integral value. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index cd4066c8cb..9c75cc59a6 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -104,6 +104,7 @@ struct UnsafePointer[ @doc_private @always_inline + @implicit fn __init__(out self, value: Self._mlir_type): """Create a pointer with the input value. @@ -113,6 +114,7 @@ struct UnsafePointer[ self.address = value @always_inline + @implicit fn __init__(out self, other: UnsafePointer[type, address_space, *_, **_]): """Exclusivity parameter cast a pointer. diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 94c7047187..73a97c09ea 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -41,6 +41,7 @@ struct Atomic[type: DType]: """ @always_inline + @implicit fn __init__(out self, value: Scalar[type]): """Constructs a new atomic value. diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index e77c305081..0d4449d405 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -84,6 +84,7 @@ struct Path( """Initializes a path with the current directory.""" self = cwd() + @implicit fn __init__(out self, path: String): """Initializes a path with the provided path. diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index b8c8c8266a..ef437a3d06 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -259,6 +259,7 @@ struct PythonVersion: var patch: Int """The patch version number.""" + @implicit fn __init__(out self, version: StringRef): """Initialize a PythonVersion object from a version string. @@ -647,6 +648,7 @@ struct PyModuleDef(Stringable, Representable, Writable): alias _free_fn_type = fn (OpaquePointer) -> OpaquePointer var free_fn: Self._free_fn_type + @implicit fn __init__(out self, name: String): self.base = PyModuleDef_Base() self.name = name.unsafe_cstr_ptr() diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index fbd1825c26..990daaf583 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -66,6 +66,7 @@ fn _get_global_python_itf() -> _PythonInterfaceImpl: struct _PythonInterfaceImpl: var _cpython: UnsafePointer[CPython] + @implicit fn __init__(out self, cpython: UnsafePointer[CPython]): self._cpython = cpython diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index a7089fc8f5..151336f510 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -60,6 +60,7 @@ struct _PyIter(Sized): self.preparedNextItem = existing.preparedNextItem self.isDone = existing.isDone + @implicit fn __init__(out self, iter: PythonObject): """Initialize an iterator. @@ -261,6 +262,7 @@ struct PythonObject( """ self = other + @implicit fn __init__(out self, ptr: PyObjectPtr): """Initialize this object from an owned reference-counted Python object pointer. @@ -308,6 +310,7 @@ struct PythonObject( return PythonObject(borrowed_ptr) + @implicit fn __init__(out self, owned typed_obj: TypedPythonObject[_]): """Construct a PythonObject from a typed object, dropping the type hint information. @@ -330,6 +333,7 @@ struct PythonObject( # This initializer should not be necessary, we should need # only the initilaizer from a `NoneType`. @doc_private + @implicit fn __init__(out self, none: NoneType._mlir_type): """Initialize a none value object from a `None` literal. @@ -338,6 +342,7 @@ struct PythonObject( """ self = Self(none=NoneType()) + @implicit fn __init__(out self, none: NoneType): """Initialize a none value object from a `None` literal. @@ -348,6 +353,7 @@ struct PythonObject( self.py_object = cpython.Py_None() cpython.Py_IncRef(self.py_object) + @implicit fn __init__(out self, value: Bool): """Initialize the object from a bool. @@ -357,6 +363,7 @@ struct PythonObject( cpython = _get_global_python_itf().cpython() self.py_object = cpython.PyBool_FromLong(int(value)) + @implicit fn __init__(out self, integer: Int): """Initialize the object with an integer value. @@ -366,6 +373,7 @@ struct PythonObject( cpython = _get_global_python_itf().cpython() self.py_object = cpython.PyLong_FromSsize_t(integer) + @implicit fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): """Initialize the object with a generic scalar value. If the scalar value type is bool, it is converted to a boolean. Otherwise, it is @@ -389,6 +397,7 @@ struct PythonObject( fp_val = value.cast[DType.float64]() self.py_object = cpython.PyFloat_FromDouble(fp_val) + @implicit fn __init__(out self, value: StringLiteral): """Initialize the object from a string literal. @@ -397,6 +406,7 @@ struct PythonObject( """ self = PythonObject(str(value)) + @implicit fn __init__(out self, strref: StringRef): """Initialize the object from a string reference. @@ -406,6 +416,7 @@ struct PythonObject( cpython = _get_global_python_itf().cpython() self.py_object = cpython.PyUnicode_DecodeUTF8(strref) + @implicit fn __init__(out self, string: String): """Initialize the object from a string. @@ -415,6 +426,7 @@ struct PythonObject( cpython = _get_global_python_itf().cpython() self.py_object = cpython.PyUnicode_DecodeUTF8(string.as_string_slice()) + @implicit fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): """Initialize the object from a list literal. @@ -457,6 +469,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) + @implicit fn __init__[*Ts: CollectionElement](inout self, value: Tuple[*Ts]): """Initialize the object from a tuple literal. @@ -500,6 +513,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyTuple_SetItem(self.py_object, i, obj.py_object) + @implicit fn __init__(out self, slice: Slice): """Initialize the object from a Mojo Slice. @@ -508,6 +522,7 @@ struct PythonObject( """ self.py_object = _slice_to_py_object_ptr(slice) + @implicit fn __init__(out self, value: Dict[Self, Self]): """Initialize the object from a dictionary of PythonObjects. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 5b54419691..38f6ae8e5b 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -279,6 +279,7 @@ struct PrefetchLocality: """Extremely local locality (keep in cache).""" @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): """Constructs a prefetch locality option. @@ -301,6 +302,7 @@ struct PrefetchRW: """Write prefetch.""" @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): """Constructs a prefetch read-write option. @@ -324,6 +326,7 @@ struct PrefetchCache: """The data prefetching option.""" @always_inline("nodebug") + @implicit fn __init__(out self, value: Int): """Constructs a prefetch option. diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 26c8620f26..ff6ada51d1 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -198,8 +198,19 @@ struct IndexList[ """Constructs a static int tuple of the given size.""" self = 0 + @always_inline + @implicit + fn __init__(out self, data: StaticTuple[Self._int_type, size]): + """Constructs a static int tuple of the given size. + + Args: + data: The StaticTuple to construct the IndexList from. + """ + self.data = data + @doc_private @always_inline + @implicit fn __init__(out self, value: __mlir_type.index): """Constructs a sized 1 static int tuple of given the element value. @@ -210,6 +221,7 @@ struct IndexList[ self = Int(value) @always_inline + @implicit fn __init__(out self, elems: (Int, Int)): """Constructs a static int tuple given a tuple of integers. @@ -235,6 +247,7 @@ struct IndexList[ self = tup @always_inline + @implicit fn __init__(out self, elems: (Int, Int, Int)): """Constructs a static int tuple given a tuple of integers. @@ -260,6 +273,7 @@ struct IndexList[ self = tup @always_inline + @implicit fn __init__(out self, elems: (Int, Int, Int, Int)): """Constructs a static int tuple given a tuple of integers. @@ -285,6 +299,7 @@ struct IndexList[ self = tup @always_inline + @implicit fn __init__(out self, *elems: Int): """Constructs a static int tuple given a set of arguments. @@ -308,6 +323,7 @@ struct IndexList[ self = tup @always_inline + @implicit fn __init__(out self, elem: Int): """Constructs a static int tuple given a set of arguments. @@ -330,6 +346,7 @@ struct IndexList[ self.data = other.data @always_inline + @implicit fn __init__(out self, values: VariadicList[Int]): """Creates a tuple constant using the specified values. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index d7b543a704..3f8d5d52f0 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -57,6 +57,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): var fixed = _FixedString[Self.SMALL_CAP]() self._storage = Self.Layout(fixed^) + @implicit fn __init__(out self, literal: StringLiteral): """Constructs a InlineString value given a string literal. @@ -81,6 +82,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): var heap = String(literal) self._storage = Self.Layout(heap^) + @implicit fn __init__(out self, owned heap_string: String): """Construct a new small string by taking ownership of an existing heap-allocated String. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 4eb0679cd7..40098ecafc 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -135,6 +135,7 @@ struct Span[ self._len = other._len @always_inline + @implicit fn __init__(out self, ref [origin]list: List[T, *_]): """Construct a Span from a List. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 8248fae4f0..9a14c36e24 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -135,6 +135,17 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): ]() @always_inline + @implicit + fn __init__(out self, array: Self.type): + """Constructs from an array type. + + Args: + array: Underlying MLIR array type. + """ + self.array = array + + @always_inline + @implicit fn __init__(out self, *elems: Self.element_type): """Constructs a static tuple given a set of arguments. @@ -145,6 +156,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): self.array = _create_array[size](elems) @always_inline + @implicit fn __init__(out self, values: VariadicList[Self.element_type]): """Creates a tuple constant using the specified values. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 391440abd7..729999be24 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -85,6 +85,7 @@ struct StringRef( self.length = other.length @always_inline + @implicit fn __init__(out self, str: StringLiteral): """Construct a StringRef value given a constant string. @@ -127,6 +128,7 @@ struct StringRef( self = StringRef(ptr, len) @always_inline + @implicit fn __init__(out self, ptr: UnsafePointer[c_char]): """Construct a StringRef value given a null-terminated string. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 308e0761e8..07dc22da63 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -123,6 +123,7 @@ struct Variant[*Ts: CollectionElement]( """ self._impl = __mlir_attr[`#kgen.unknown : `, Self._mlir_type] + @implicit fn __init__[T: CollectionElement](inout self, owned value: T): """Create a variant with one of the types. diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index ed667bfa96..5cf8ba1d3e 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -228,6 +228,7 @@ struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): var pos: Int var writer: W + @implicit fn __init__(out self, owned writer: W): self.data = UnsafePointer[ UInt8, @@ -275,6 +276,7 @@ struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): var pos: Int var writer: W + @implicit fn __init__(out self, owned writer: W): self.data = InlineArray[UInt8, capacity](unsafe_uninitialized=True) self.pos = 0 diff --git a/stdlib/test/builtin/test_none.mojo b/stdlib/test/builtin/test_none.mojo index 07404bb8ae..8c153ad329 100644 --- a/stdlib/test/builtin/test_none.mojo +++ b/stdlib/test/builtin/test_none.mojo @@ -37,14 +37,17 @@ def test_format_to(): struct FromNone: var value: Int + @implicit fn __init__(out self, none: NoneType): self.value = -1 # FIXME: None literal should be of NoneType not !kgen.none. @always_inline + @implicit fn __init__(out self, none: __mlir_type.`!kgen.none`): self = NoneType() + @implicit fn __init__(out self, value: Int): self.value = value diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index d9807ea83b..4f8b1e965c 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -391,6 +391,10 @@ def test_dict_update_empty_new(): struct DummyKey(KeyElement): var value: Int + @implicit + fn __init__(out self, value: Int): + self.value = value + fn __init__(out self, *, other: Self): self = other diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 49bd72a1bb..873baf32e6 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -555,6 +555,7 @@ struct CopyCountedStruct(CollectionElement): self.counter = CopyCounter(other=other.counter) self.value = String(other=other.value) + @implicit fn __init__(out self, value: String): self.counter = CopyCounter() self.value = value diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index b9dd9cd91a..0e83d44dca 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -27,6 +27,7 @@ struct MoveOnly[T: Movable](Movable): var data: T """Test data payload.""" + @implicit fn __init__(out self, owned i: T): """Construct a MoveOnly providing the payload data. @@ -53,6 +54,7 @@ struct ExplicitCopyOnly(ExplicitlyCopyable): var value: Int var copy_count: Int + @implicit fn __init__(out self, value: Int): self.value = value self.copy_count = 0 @@ -71,6 +73,7 @@ struct ImplicitCopyOnly(Copyable): var value: Int var copy_count: Int + @implicit fn __init__(out self, value: Int): self.value = value self.copy_count = 0 @@ -117,6 +120,7 @@ struct MoveCounter[T: CollectionElementNew]( var value: T var move_count: Int + @implicit fn __init__(out self, owned value: T): """Construct a new instance of this type. This initial move is not counted. """ From 77d26e87c32924afd71d8c0a933d845d63bd19d4 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sun, 17 Nov 2024 19:12:21 -0800 Subject: [PATCH 1891/2019] [mojo-lang] Turn on explicit by default constructors Require `@implicit` decorator above single argument `__init__` function to allow it to be implicitly constructed. See changelog entry in this commit for an example and more details. This fixes https://github.com/modularml/mojo/issues/1310 MODULAR_ORIG_COMMIT_REV_ID: ace1a1da978fd7acbd9a8ea60d807448f8889fcf --- docs/changelog.md | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1da385ce63..d8261b9ad9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -444,7 +444,7 @@ what we publish. These operators can't be evaluated at runtime, as a `StringLiteral` must be written into the binary during compilation. - - You can now index into `UnsafePointer` using SIMD scalar integral types: +- You can now index into `UnsafePointer` using SIMD scalar integral types: ```mojo p = UnsafePointer[Int].alloc(1) @@ -453,7 +453,7 @@ what we publish. print(p[i]) ``` - - Float32 and Float64 are now printed and converted to strings with roundtrip +- Float32 and Float64 are now printed and converted to strings with roundtrip guarantee and shortest representation: ```plaintext @@ -470,6 +470,49 @@ what we publish. Float64(1.234 * 10**16) 12340000000000000.0 1.234e+16 ``` +- Single argument constructors now require a `@implicit` decorator to allow + for implicit conversions. Previously you could define an `__init__` that + takes a single argument: + + ```mojo + struct Foo: + var value: Int + + fn __init__(out self, value: Int): + self.value = value + ``` + + And this would allow you to pass an `Int` in the position of `Foo`: + + ```mojo + fn func(foo: Foo): + print("implicitly converted Int to Foo:", foo.value) + + fn main(): + func(Int(42)) + ``` + + This can result in difficult to reason about behaviour as the chain of + implicit conversions grow. By default this implicit behavior is now turned + off so you have to explicitly construct `Foo`: + + ```mojo + fn main(): + func(Foo(42)) + ``` + + But you can still opt into implicit conversions by adding the `@implicit` + decorator: + + ```mojo + struct Foo: + var value: Int + + @implicit + fn __init__(out self, value: Int): + self.value = value + ``` + ### ❌ Removed - The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your From a933b4d16f29805dcf724d6aa37d833e47b688f6 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sun, 17 Nov 2024 22:19:01 -0800 Subject: [PATCH 1892/2019] [docs] Add changelog fix for implicit conversions And slight rewording. MODULAR_ORIG_COMMIT_REV_ID: cc40b7c83b539274076c83197a704c8367fc5fac --- docs/changelog.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d8261b9ad9..d6c831da5b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -482,7 +482,7 @@ what we publish. self.value = value ``` - And this would allow you to pass an `Int` in the position of `Foo`: + And this would allow you to pass an `Int` in the position of a `Foo`: ```mojo fn func(foo: Foo): @@ -492,17 +492,17 @@ what we publish. func(Int(42)) ``` - This can result in difficult to reason about behaviour as the chain of - implicit conversions grow. By default this implicit behavior is now turned - off so you have to explicitly construct `Foo`: + This can result in complicated errors that are difficult to debug. By default + this implicit behavior is now turned off, so you have to explicitly construct + `Foo`: ```mojo fn main(): func(Foo(42)) ``` - But you can still opt into implicit conversions by adding the `@implicit` - decorator: + You can still opt into implicit conversions by adding the `@implicit` + decorator. For example, to enable implicit conversions from `Int` to `Foo`: ```mojo struct Foo: @@ -523,6 +523,9 @@ what we publish. - Lifetime tracking is now fully field sensitive, which makes the uninitialized variable checker more precise. +- [Issue #1310](https://github.com/modularml/mojo/issues/1310) - Mojo permits + the use of any constructor for implicit conversions + - [Issue #1632](https://github.com/modularml/mojo/issues/1632) - Mojo produces weird error when inout function is used in non mutating function From 12336a3796c8efccd1ec6d28456aa5142eaf020c Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Mon, 18 Nov 2024 12:24:24 -0600 Subject: [PATCH 1893/2019] [External] [stdlib] Add full unicode support for character casing functions (#47479) [External] [stdlib] Add full unicode support for character casing functions The code I used to generate the lookup tables can be found here https://gist.github.com/mzaks/bbadaeebcf81a5200021af041568b26b Co-authored-by: Maxim Zaks Closes modularml/mojo#3496 MODULAR_ORIG_COMMIT_REV_ID: 971d447e40e3221b84df526393f984b7fe22fcba --- stdlib/src/collections/string.mojo | 65 +- stdlib/src/utils/_unicode.mojo | 265 + stdlib/src/utils/_unicode_lookups.mojo | 5876 ++++++++++++++++++++++ stdlib/test/collections/test_string.mojo | 10 +- 4 files changed, 6168 insertions(+), 48 deletions(-) create mode 100644 stdlib/src/utils/_unicode.mojo create mode 100644 stdlib/src/utils/_unicode_lookups.mojo diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 144e7cc493..f5046bfc74 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -47,6 +47,13 @@ from utils.string_slice import ( _to_string_list, ) +from utils._unicode import ( + is_lowercase, + is_uppercase, + to_lowercase, + to_uppercase, +) + # ===----------------------------------------------------------------------=== # # ord # ===----------------------------------------------------------------------=== # @@ -86,7 +93,7 @@ fn ord(s: StringSlice) -> Int: # 2: 110aaaaa 10bbbbbb -> 00000000 00000000 00000aaa aabbbbbb a << 6 | b # 3: 1110aaaa 10bbbbbb 10cccccc -> 00000000 00000000 aaaabbbb bbcccccc a << 12 | b << 6 | c # 4: 11110aaa 10bbbbbb 10cccccc 10dddddd -> 00000000 000aaabb bbbbcccc ccdddddd a << 18 | b << 12 | c << 6 | d - var p = s.unsafe_ptr().bitcast[UInt8]() + var p = s.unsafe_ptr() var b1 = p[] if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(s.byte_length() == 1, "input string length must be 1") @@ -757,6 +764,10 @@ struct String( ) # We make a backup because steal_data() will clear size and capacity. var size = impl.size + debug_assert( + impl[size - 1] == 0, + "expected last element of String buffer to be null terminator", + ) var capacity = impl.capacity self._buffer = Self._buffer_type( ptr=impl.steal_data(), length=size, capacity=capacity @@ -1985,43 +1996,26 @@ struct String( return String(res^) fn lower(self) -> String: - """Returns a copy of the string with all ASCII cased characters + """Returns a copy of the string with all cased characters converted to lowercase. Returns: A new string where cased letters have been converted to lowercase. """ - # TODO(#26444): - # Support the Unicode standard casing behavior to handle cased letters - # outside of the standard ASCII letters. - return self._toggle_ascii_case[_is_ascii_uppercase]() + # TODO: the _unicode module does not support locale sensitive conversions yet. + return to_lowercase(self) fn upper(self) -> String: - """Returns a copy of the string with all ASCII cased characters + """Returns a copy of the string with all cased characters converted to uppercase. Returns: A new string where cased letters have been converted to uppercase. """ - # TODO(#26444): - # Support the Unicode standard casing behavior to handle cased letters - # outside of the standard ASCII letters. - return self._toggle_ascii_case[_is_ascii_lowercase]() - - fn _toggle_ascii_case[check_case: fn (UInt8) -> Bool](self) -> String: - var copy: String = self - - var char_ptr = copy.unsafe_ptr() - - for i in range(self.byte_length()): - var char: UInt8 = char_ptr[i] - if check_case(char): - var lower = _toggle_ascii_case(char) - char_ptr[i] = lower - - return copy + # TODO: the _unicode module does not support locale sensitive conversions yet. + return to_uppercase(self) fn startswith( ref self, prefix: String, start: Int = 0, end: Int = -1 @@ -2188,44 +2182,25 @@ struct String( return False return True - fn _isupper_islower[*, upper: Bool](self) -> Bool: - fn is_ascii_cased(c: UInt8) -> Bool: - return _is_ascii_uppercase(c) or _is_ascii_lowercase(c) - - for c in self: - debug_assert(c.byte_length() == 1, "only implemented for ASCII") - if is_ascii_cased(ord(c)): - - @parameter - if upper: - return self == self.upper() - else: - return self == self.lower() - return False - fn isupper(self) -> Bool: """Returns True if all cased characters in the string are uppercase and there is at least one cased character. - Note that this currently only works with ASCII strings. - Returns: True if all cased characters in the string are uppercase and there is at least one cased character, False otherwise. """ - return self._isupper_islower[upper=True]() + return len(self) > 0 and is_uppercase(self) fn islower(self) -> Bool: """Returns True if all cased characters in the string are lowercase and there is at least one cased character. - Note that this currently only works with ASCII strings. - Returns: True if all cased characters in the string are lowercase and there is at least one cased character, False otherwise. """ - return self._isupper_islower[upper=False]() + return len(self) > 0 and is_lowercase(self) fn isprintable(self) -> Bool: """Returns True if all characters in the string are ASCII printable. diff --git a/stdlib/src/utils/_unicode.mojo b/stdlib/src/utils/_unicode.mojo new file mode 100644 index 0000000000..079bd7e180 --- /dev/null +++ b/stdlib/src/utils/_unicode.mojo @@ -0,0 +1,265 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +from bit import count_leading_zeros +from memory import memcpy, UnsafePointer +from ._unicode_lookups import * + + +fn _uppercase_mapping_index(rune: Int) -> Int: + """Return index for upper case mapping or -1 if no mapping is given.""" + return _to_index[has_uppercase_mapping](rune) + + +fn _uppercase_mapping2_index(rune: Int) -> Int: + """Return index for upper case mapping converting the rune to 2 runes, or -1 if no mapping is given. + """ + return _to_index[has_uppercase_mapping2](rune) + + +fn _uppercase_mapping3_index(rune: Int) -> Int: + """Return index for upper case mapping converting the rune to 3 runes, or -1 if no mapping is given. + """ + return _to_index[has_uppercase_mapping3](rune) + + +fn _lowercase_mapping_index(rune: Int) -> Int: + """Return index for lower case mapping or -1 if no mapping is given.""" + return _to_index[has_lowercase_mapping](rune) + + +@always_inline +fn _to_index[lookup: List[UInt32, **_]](rune: Int) -> Int: + """Find index of rune in lookup with binary search. + Returns -1 if not found.""" + var cursor = 0 + var x = UInt32(rune) + var b = lookup.data + var length = len(lookup) + while length > 1: + var half = length >> 1 + length -= half + cursor += int(b.load(cursor + half - 1) < x) * half + + return cursor if b.load(cursor) == x else -1 + + +fn is_uppercase(s: String) -> Bool: + """Returns True if all characters in the string are uppercase, and + there is at least one cased character. + + Args: + s: The string to examine. + + Returns: + True if all characters in the string are uppercaseand + there is at least one cased character, False otherwise. + """ + var found = False + for c in s: + var rune = ord(c) + var index = _lowercase_mapping_index(rune) + if index != -1: + found = True + continue + index = _uppercase_mapping_index(rune) + if index != -1: + return False + index = _uppercase_mapping2_index(rune) + if index != -1: + return False + index = _uppercase_mapping3_index(rune) + if index != -1: + return False + return found + + +fn is_lowercase(s: String) -> Bool: + """Returns True if all characters in the string are lowercase, and + there is at least one cased character. + + Args: + s: The string to examine. + + Returns: + True if all characters in the string are lowercase and + there is at least one cased character, False otherwise. + """ + var found = False + for c in s: + var rune = ord(c) + var index = _uppercase_mapping_index(rune) + if index != -1: + found = True + continue + index = _uppercase_mapping2_index(rune) + if index != -1: + found = True + continue + index = _uppercase_mapping3_index(rune) + if index != -1: + found = True + continue + index = _lowercase_mapping_index(rune) + if index != -1: + return False + return found + + +fn _ord(_p: UnsafePointer[UInt8]) -> (Int, Int): + """Return the rune and number of bytes to be consumed, for given UTF-8 string pointer + """ + var p = _p + var b1 = p[] + if (b1 >> 7) == 0: # This is 1 byte ASCII char + return int(b1), 1 + var num_bytes = count_leading_zeros(~b1) + var shift = int((6 * (num_bytes - 1))) + var b1_mask = 0b11111111 >> (num_bytes + 1) + var result = int(b1 & b1_mask) << shift + for _ in range(1, num_bytes): + p += 1 + shift -= 6 + result |= int(p[] & 0b00111111) << shift + return result, int(num_bytes) + + +fn _write_rune(rune: UInt32, p: UnsafePointer[UInt8]) -> Int: + """Write rune as UTF-8 into provided pointer. Return number of added bytes. + """ + if (rune >> 7) == 0: # This is 1 byte ASCII char + p[0] = rune.cast[DType.uint8]() + return 1 + + @always_inline + fn _utf8_len(val: UInt32) -> Int: + alias sizes = SIMD[DType.uint32, 4]( + 0, 0b1111_111, 0b1111_1111_111, 0b1111_1111_1111_1111 + ) + var values = SIMD[DType.uint32, 4](val) + var mask = values > sizes + return int(mask.cast[DType.uint8]().reduce_add()) + + var num_bytes = _utf8_len(rune) + var shift = 6 * (num_bytes - 1) + var mask = UInt32(0xFF) >> (num_bytes + 1) + var num_bytes_marker = UInt32(0xFF) << (8 - num_bytes) + p[0] = (((rune >> shift) & mask) | num_bytes_marker).cast[DType.uint8]() + for i in range(1, num_bytes): + shift -= 6 + p[i] = (((rune >> shift) & 0b00111111) | 0b10000000).cast[DType.uint8]() + return num_bytes + + +fn to_lowercase(s: String) -> String: + """Returns a new string with all characters converted to uppercase. + + Args: + s: Input string. + + Returns: + A new string where cased letters have been converted to lowercase. + """ + var input = s.unsafe_ptr() + var capacity = (s.byte_length() >> 1) * 3 + 1 + var output = UnsafePointer[UInt8].alloc(capacity) + var input_offset = 0 + var output_offset = 0 + while input_offset < s.byte_length(): + var rune_and_size = _ord(input + input_offset) + var index = _lowercase_mapping_index(rune_and_size[0]) + if index == -1: + memcpy( + output + output_offset, input + input_offset, rune_and_size[1] + ) + output_offset += rune_and_size[1] + else: + output_offset += _write_rune( + lowercase_mapping[index], output + output_offset + ) + + input_offset += rune_and_size[1] + + if output_offset >= ( + capacity - 5 + ): # check if we need to resize the ouput + capacity += ((s.byte_length() - input_offset) >> 1) * 3 + 1 + var new_output = UnsafePointer[UInt8].alloc(capacity) + memcpy(new_output, output, output_offset) + output.free() + output = new_output + + output[output_offset] = 0 + var list = List[UInt8]( + ptr=output, length=(output_offset + 1), capacity=capacity + ) + return String(list) + + +fn to_uppercase(s: String) -> String: + """Returns a new string with all characters converted to uppercase. + + Args: + s: Input string. + + Returns: + A new string where cased letters have been converted to uppercase. + """ + var input = s.unsafe_ptr() + var capacity = (s.byte_length() >> 1) * 3 + 1 + var output = UnsafePointer[UInt8].alloc(capacity) + var input_offset = 0 + var output_offset = 0 + while input_offset < s.byte_length(): + var rune_and_size = _ord(input + input_offset) + var index = _uppercase_mapping_index(rune_and_size[0]) + var index2 = _uppercase_mapping2_index( + rune_and_size[0] + ) if index == -1 else -1 + var index3 = _uppercase_mapping3_index( + rune_and_size[0] + ) if index == -1 and index2 == -1 else -1 + if index != -1: + output_offset += _write_rune( + uppercase_mapping[index], output + output_offset + ) + elif index2 != -1: + var runes = uppercase_mapping2[index2] + output_offset += _write_rune(runes[0], output + output_offset) + output_offset += _write_rune(runes[1], output + output_offset) + elif index3 != -1: + var runes = uppercase_mapping3[index3] + output_offset += _write_rune(runes[0], output + output_offset) + output_offset += _write_rune(runes[1], output + output_offset) + output_offset += _write_rune(runes[2], output + output_offset) + else: + memcpy( + output + output_offset, input + input_offset, rune_and_size[1] + ) + output_offset += rune_and_size[1] + + input_offset += rune_and_size[1] + + if output_offset >= ( + capacity - 5 + ): # check if we need to resize the ouput + capacity += ((s.byte_length() - input_offset) >> 1) * 3 + 1 + var new_output = UnsafePointer[UInt8].alloc(capacity) + memcpy(new_output, output, output_offset) + output.free() + output = new_output + + output[output_offset] = 0 + var list = List[UInt8]( + ptr=output, length=(output_offset + 1), capacity=capacity + ) + return String(list) diff --git a/stdlib/src/utils/_unicode_lookups.mojo b/stdlib/src/utils/_unicode_lookups.mojo new file mode 100644 index 0000000000..bb05d05582 --- /dev/null +++ b/stdlib/src/utils/_unicode_lookups.mojo @@ -0,0 +1,5876 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""This file contains lookup tables for Unicode conversions generated from +UnicodeData.txt and SpecialCasing.txt files, which can be found at +https://www.unicode.org/Public/16.0.0/""" + +alias has_uppercase_mapping = List[UInt32, hint_trivial_type=True]( + 0x0061, # LATIN SMALL LETTER A a + 0x0062, # LATIN SMALL LETTER B b + 0x0063, # LATIN SMALL LETTER C c + 0x0064, # LATIN SMALL LETTER D d + 0x0065, # LATIN SMALL LETTER E e + 0x0066, # LATIN SMALL LETTER F f + 0x0067, # LATIN SMALL LETTER G g + 0x0068, # LATIN SMALL LETTER H h + 0x0069, # LATIN SMALL LETTER I i + 0x006A, # LATIN SMALL LETTER J j + 0x006B, # LATIN SMALL LETTER K k + 0x006C, # LATIN SMALL LETTER L l + 0x006D, # LATIN SMALL LETTER M m + 0x006E, # LATIN SMALL LETTER N n + 0x006F, # LATIN SMALL LETTER O o + 0x0070, # LATIN SMALL LETTER P p + 0x0071, # LATIN SMALL LETTER Q q + 0x0072, # LATIN SMALL LETTER R r + 0x0073, # LATIN SMALL LETTER S s + 0x0074, # LATIN SMALL LETTER T t + 0x0075, # LATIN SMALL LETTER U u + 0x0076, # LATIN SMALL LETTER V v + 0x0077, # LATIN SMALL LETTER W w + 0x0078, # LATIN SMALL LETTER X x + 0x0079, # LATIN SMALL LETTER Y y + 0x007A, # LATIN SMALL LETTER Z z + 0x00B5, # MICRO SIGN µ + 0x00E0, # LATIN SMALL LETTER A WITH GRAVE à + 0x00E1, # LATIN SMALL LETTER A WITH ACUTE á + 0x00E2, # LATIN SMALL LETTER A WITH CIRCUMFLEX â + 0x00E3, # LATIN SMALL LETTER A WITH TILDE ã + 0x00E4, # LATIN SMALL LETTER A WITH DIAERESIS ä + 0x00E5, # LATIN SMALL LETTER A WITH RING ABOVE å + 0x00E6, # LATIN SMALL LETTER AE æ + 0x00E7, # LATIN SMALL LETTER C WITH CEDILLA ç + 0x00E8, # LATIN SMALL LETTER E WITH GRAVE è + 0x00E9, # LATIN SMALL LETTER E WITH ACUTE é + 0x00EA, # LATIN SMALL LETTER E WITH CIRCUMFLEX ê + 0x00EB, # LATIN SMALL LETTER E WITH DIAERESIS ë + 0x00EC, # LATIN SMALL LETTER I WITH GRAVE ì + 0x00ED, # LATIN SMALL LETTER I WITH ACUTE í + 0x00EE, # LATIN SMALL LETTER I WITH CIRCUMFLEX î + 0x00EF, # LATIN SMALL LETTER I WITH DIAERESIS ï + 0x00F0, # LATIN SMALL LETTER ETH ð + 0x00F1, # LATIN SMALL LETTER N WITH TILDE ñ + 0x00F2, # LATIN SMALL LETTER O WITH GRAVE ò + 0x00F3, # LATIN SMALL LETTER O WITH ACUTE ó + 0x00F4, # LATIN SMALL LETTER O WITH CIRCUMFLEX ô + 0x00F5, # LATIN SMALL LETTER O WITH TILDE õ + 0x00F6, # LATIN SMALL LETTER O WITH DIAERESIS ö + 0x00F8, # LATIN SMALL LETTER O WITH STROKE ø + 0x00F9, # LATIN SMALL LETTER U WITH GRAVE ù + 0x00FA, # LATIN SMALL LETTER U WITH ACUTE ú + 0x00FB, # LATIN SMALL LETTER U WITH CIRCUMFLEX û + 0x00FC, # LATIN SMALL LETTER U WITH DIAERESIS ü + 0x00FD, # LATIN SMALL LETTER Y WITH ACUTE ý + 0x00FE, # LATIN SMALL LETTER THORN þ + 0x00FF, # LATIN SMALL LETTER Y WITH DIAERESIS ÿ + 0x0101, # LATIN SMALL LETTER A WITH MACRON ā + 0x0103, # LATIN SMALL LETTER A WITH BREVE ă + 0x0105, # LATIN SMALL LETTER A WITH OGONEK ą + 0x0107, # LATIN SMALL LETTER C WITH ACUTE ć + 0x0109, # LATIN SMALL LETTER C WITH CIRCUMFLEX ĉ + 0x010B, # LATIN SMALL LETTER C WITH DOT ABOVE ċ + 0x010D, # LATIN SMALL LETTER C WITH CARON č + 0x010F, # LATIN SMALL LETTER D WITH CARON ď + 0x0111, # LATIN SMALL LETTER D WITH STROKE đ + 0x0113, # LATIN SMALL LETTER E WITH MACRON ē + 0x0115, # LATIN SMALL LETTER E WITH BREVE ĕ + 0x0117, # LATIN SMALL LETTER E WITH DOT ABOVE ė + 0x0119, # LATIN SMALL LETTER E WITH OGONEK ę + 0x011B, # LATIN SMALL LETTER E WITH CARON ě + 0x011D, # LATIN SMALL LETTER G WITH CIRCUMFLEX ĝ + 0x011F, # LATIN SMALL LETTER G WITH BREVE ğ + 0x0121, # LATIN SMALL LETTER G WITH DOT ABOVE ġ + 0x0123, # LATIN SMALL LETTER G WITH CEDILLA ģ + 0x0125, # LATIN SMALL LETTER H WITH CIRCUMFLEX ĥ + 0x0127, # LATIN SMALL LETTER H WITH STROKE ħ + 0x0129, # LATIN SMALL LETTER I WITH TILDE ĩ + 0x012B, # LATIN SMALL LETTER I WITH MACRON ī + 0x012D, # LATIN SMALL LETTER I WITH BREVE ĭ + 0x012F, # LATIN SMALL LETTER I WITH OGONEK į + 0x0131, # LATIN SMALL LETTER DOTLESS I ı + 0x0133, # LATIN SMALL LIGATURE IJ ij + 0x0135, # LATIN SMALL LETTER J WITH CIRCUMFLEX ĵ + 0x0137, # LATIN SMALL LETTER K WITH CEDILLA ķ + 0x013A, # LATIN SMALL LETTER L WITH ACUTE ĺ + 0x013C, # LATIN SMALL LETTER L WITH CEDILLA ļ + 0x013E, # LATIN SMALL LETTER L WITH CARON ľ + 0x0140, # LATIN SMALL LETTER L WITH MIDDLE DOT ŀ + 0x0142, # LATIN SMALL LETTER L WITH STROKE ł + 0x0144, # LATIN SMALL LETTER N WITH ACUTE ń + 0x0146, # LATIN SMALL LETTER N WITH CEDILLA ņ + 0x0148, # LATIN SMALL LETTER N WITH CARON ň + 0x014B, # LATIN SMALL LETTER ENG ŋ + 0x014D, # LATIN SMALL LETTER O WITH MACRON ō + 0x014F, # LATIN SMALL LETTER O WITH BREVE ŏ + 0x0151, # LATIN SMALL LETTER O WITH DOUBLE ACUTE ő + 0x0153, # LATIN SMALL LIGATURE OE œ + 0x0155, # LATIN SMALL LETTER R WITH ACUTE ŕ + 0x0157, # LATIN SMALL LETTER R WITH CEDILLA ŗ + 0x0159, # LATIN SMALL LETTER R WITH CARON ř + 0x015B, # LATIN SMALL LETTER S WITH ACUTE ś + 0x015D, # LATIN SMALL LETTER S WITH CIRCUMFLEX ŝ + 0x015F, # LATIN SMALL LETTER S WITH CEDILLA ş + 0x0161, # LATIN SMALL LETTER S WITH CARON š + 0x0163, # LATIN SMALL LETTER T WITH CEDILLA ţ + 0x0165, # LATIN SMALL LETTER T WITH CARON ť + 0x0167, # LATIN SMALL LETTER T WITH STROKE ŧ + 0x0169, # LATIN SMALL LETTER U WITH TILDE ũ + 0x016B, # LATIN SMALL LETTER U WITH MACRON ū + 0x016D, # LATIN SMALL LETTER U WITH BREVE ŭ + 0x016F, # LATIN SMALL LETTER U WITH RING ABOVE ů + 0x0171, # LATIN SMALL LETTER U WITH DOUBLE ACUTE ű + 0x0173, # LATIN SMALL LETTER U WITH OGONEK ų + 0x0175, # LATIN SMALL LETTER W WITH CIRCUMFLEX ŵ + 0x0177, # LATIN SMALL LETTER Y WITH CIRCUMFLEX ŷ + 0x017A, # LATIN SMALL LETTER Z WITH ACUTE ź + 0x017C, # LATIN SMALL LETTER Z WITH DOT ABOVE ż + 0x017E, # LATIN SMALL LETTER Z WITH CARON ž + 0x017F, # LATIN SMALL LETTER LONG S ſ + 0x0180, # LATIN SMALL LETTER B WITH STROKE ƀ + 0x0183, # LATIN SMALL LETTER B WITH TOPBAR ƃ + 0x0185, # LATIN SMALL LETTER TONE SIX ƅ + 0x0188, # LATIN SMALL LETTER C WITH HOOK ƈ + 0x018C, # LATIN SMALL LETTER D WITH TOPBAR ƌ + 0x0192, # LATIN SMALL LETTER F WITH HOOK ƒ + 0x0195, # LATIN SMALL LETTER HV ƕ + 0x0199, # LATIN SMALL LETTER K WITH HOOK ƙ + 0x019A, # LATIN SMALL LETTER L WITH BAR ƚ + 0x019E, # LATIN SMALL LETTER N WITH LONG RIGHT LEG ƞ + 0x01A1, # LATIN SMALL LETTER O WITH HORN ơ + 0x01A3, # LATIN SMALL LETTER OI ƣ + 0x01A5, # LATIN SMALL LETTER P WITH HOOK ƥ + 0x01A8, # LATIN SMALL LETTER TONE TWO ƨ + 0x01AD, # LATIN SMALL LETTER T WITH HOOK ƭ + 0x01B0, # LATIN SMALL LETTER U WITH HORN ư + 0x01B4, # LATIN SMALL LETTER Y WITH HOOK ƴ + 0x01B6, # LATIN SMALL LETTER Z WITH STROKE ƶ + 0x01B9, # LATIN SMALL LETTER EZH REVERSED ƹ + 0x01BD, # LATIN SMALL LETTER TONE FIVE ƽ + 0x01BF, # LATIN LETTER WYNN ƿ + 0x01C5, # LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON Dž + 0x01C6, # LATIN SMALL LETTER DZ WITH CARON dž + 0x01C8, # LATIN CAPITAL LETTER L WITH SMALL LETTER J Lj + 0x01C9, # LATIN SMALL LETTER LJ lj + 0x01CB, # LATIN CAPITAL LETTER N WITH SMALL LETTER J Nj + 0x01CC, # LATIN SMALL LETTER NJ nj + 0x01CE, # LATIN SMALL LETTER A WITH CARON ǎ + 0x01D0, # LATIN SMALL LETTER I WITH CARON ǐ + 0x01D2, # LATIN SMALL LETTER O WITH CARON ǒ + 0x01D4, # LATIN SMALL LETTER U WITH CARON ǔ + 0x01D6, # LATIN SMALL LETTER U WITH DIAERESIS AND MACRON ǖ + 0x01D8, # LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE ǘ + 0x01DA, # LATIN SMALL LETTER U WITH DIAERESIS AND CARON ǚ + 0x01DC, # LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE ǜ + 0x01DD, # LATIN SMALL LETTER TURNED E ǝ + 0x01DF, # LATIN SMALL LETTER A WITH DIAERESIS AND MACRON ǟ + 0x01E1, # LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON ǡ + 0x01E3, # LATIN SMALL LETTER AE WITH MACRON ǣ + 0x01E5, # LATIN SMALL LETTER G WITH STROKE ǥ + 0x01E7, # LATIN SMALL LETTER G WITH CARON ǧ + 0x01E9, # LATIN SMALL LETTER K WITH CARON ǩ + 0x01EB, # LATIN SMALL LETTER O WITH OGONEK ǫ + 0x01ED, # LATIN SMALL LETTER O WITH OGONEK AND MACRON ǭ + 0x01EF, # LATIN SMALL LETTER EZH WITH CARON ǯ + 0x01F2, # LATIN CAPITAL LETTER D WITH SMALL LETTER Z Dz + 0x01F3, # LATIN SMALL LETTER DZ dz + 0x01F5, # LATIN SMALL LETTER G WITH ACUTE ǵ + 0x01F9, # LATIN SMALL LETTER N WITH GRAVE ǹ + 0x01FB, # LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE ǻ + 0x01FD, # LATIN SMALL LETTER AE WITH ACUTE ǽ + 0x01FF, # LATIN SMALL LETTER O WITH STROKE AND ACUTE ǿ + 0x0201, # LATIN SMALL LETTER A WITH DOUBLE GRAVE ȁ + 0x0203, # LATIN SMALL LETTER A WITH INVERTED BREVE ȃ + 0x0205, # LATIN SMALL LETTER E WITH DOUBLE GRAVE ȅ + 0x0207, # LATIN SMALL LETTER E WITH INVERTED BREVE ȇ + 0x0209, # LATIN SMALL LETTER I WITH DOUBLE GRAVE ȉ + 0x020B, # LATIN SMALL LETTER I WITH INVERTED BREVE ȋ + 0x020D, # LATIN SMALL LETTER O WITH DOUBLE GRAVE ȍ + 0x020F, # LATIN SMALL LETTER O WITH INVERTED BREVE ȏ + 0x0211, # LATIN SMALL LETTER R WITH DOUBLE GRAVE ȑ + 0x0213, # LATIN SMALL LETTER R WITH INVERTED BREVE ȓ + 0x0215, # LATIN SMALL LETTER U WITH DOUBLE GRAVE ȕ + 0x0217, # LATIN SMALL LETTER U WITH INVERTED BREVE ȗ + 0x0219, # LATIN SMALL LETTER S WITH COMMA BELOW ș + 0x021B, # LATIN SMALL LETTER T WITH COMMA BELOW ț + 0x021D, # LATIN SMALL LETTER YOGH ȝ + 0x021F, # LATIN SMALL LETTER H WITH CARON ȟ + 0x0223, # LATIN SMALL LETTER OU ȣ + 0x0225, # LATIN SMALL LETTER Z WITH HOOK ȥ + 0x0227, # LATIN SMALL LETTER A WITH DOT ABOVE ȧ + 0x0229, # LATIN SMALL LETTER E WITH CEDILLA ȩ + 0x022B, # LATIN SMALL LETTER O WITH DIAERESIS AND MACRON ȫ + 0x022D, # LATIN SMALL LETTER O WITH TILDE AND MACRON ȭ + 0x022F, # LATIN SMALL LETTER O WITH DOT ABOVE ȯ + 0x0231, # LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON ȱ + 0x0233, # LATIN SMALL LETTER Y WITH MACRON ȳ + 0x023C, # LATIN SMALL LETTER C WITH STROKE ȼ + 0x023F, # LATIN SMALL LETTER S WITH SWASH TAIL ȿ + 0x0240, # LATIN SMALL LETTER Z WITH SWASH TAIL ɀ + 0x0242, # LATIN SMALL LETTER GLOTTAL STOP ɂ + 0x0247, # LATIN SMALL LETTER E WITH STROKE ɇ + 0x0249, # LATIN SMALL LETTER J WITH STROKE ɉ + 0x024B, # LATIN SMALL LETTER Q WITH HOOK TAIL ɋ + 0x024D, # LATIN SMALL LETTER R WITH STROKE ɍ + 0x024F, # LATIN SMALL LETTER Y WITH STROKE ɏ + 0x0250, # LATIN SMALL LETTER TURNED A ɐ + 0x0251, # LATIN SMALL LETTER ALPHA ɑ + 0x0252, # LATIN SMALL LETTER TURNED ALPHA ɒ + 0x0253, # LATIN SMALL LETTER B WITH HOOK ɓ + 0x0254, # LATIN SMALL LETTER OPEN O ɔ + 0x0256, # LATIN SMALL LETTER D WITH TAIL ɖ + 0x0257, # LATIN SMALL LETTER D WITH HOOK ɗ + 0x0259, # LATIN SMALL LETTER SCHWA ə + 0x025B, # LATIN SMALL LETTER OPEN E ɛ + 0x025C, # LATIN SMALL LETTER REVERSED OPEN E ɜ + 0x0260, # LATIN SMALL LETTER G WITH HOOK ɠ + 0x0261, # LATIN SMALL LETTER SCRIPT G ɡ + 0x0263, # LATIN SMALL LETTER GAMMA ɣ + 0x0265, # LATIN SMALL LETTER TURNED H ɥ + 0x0266, # LATIN SMALL LETTER H WITH HOOK ɦ + 0x0268, # LATIN SMALL LETTER I WITH STROKE ɨ + 0x0269, # LATIN SMALL LETTER IOTA ɩ + 0x026A, # LATIN LETTER SMALL CAPITAL I ɪ + 0x026B, # LATIN SMALL LETTER L WITH MIDDLE TILDE ɫ + 0x026C, # LATIN SMALL LETTER L WITH BELT ɬ + 0x026F, # LATIN SMALL LETTER TURNED M ɯ + 0x0271, # LATIN SMALL LETTER M WITH HOOK ɱ + 0x0272, # LATIN SMALL LETTER N WITH LEFT HOOK ɲ + 0x0275, # LATIN SMALL LETTER BARRED O ɵ + 0x027D, # LATIN SMALL LETTER R WITH TAIL ɽ + 0x0280, # LATIN LETTER SMALL CAPITAL R ʀ + 0x0282, # LATIN SMALL LETTER S WITH HOOK ʂ + 0x0283, # LATIN SMALL LETTER ESH ʃ + 0x0287, # LATIN SMALL LETTER TURNED T ʇ + 0x0288, # LATIN SMALL LETTER T WITH RETROFLEX HOOK ʈ + 0x0289, # LATIN SMALL LETTER U BAR ʉ + 0x028A, # LATIN SMALL LETTER UPSILON ʊ + 0x028B, # LATIN SMALL LETTER V WITH HOOK ʋ + 0x028C, # LATIN SMALL LETTER TURNED V ʌ + 0x0292, # LATIN SMALL LETTER EZH ʒ + 0x029D, # LATIN SMALL LETTER J WITH CROSSED-TAIL ʝ + 0x029E, # LATIN SMALL LETTER TURNED K ʞ + 0x0345, # COMBINING GREEK YPOGEGRAMMENI ͅ + 0x0371, # GREEK SMALL LETTER HETA ͱ + 0x0373, # GREEK SMALL LETTER ARCHAIC SAMPI ͳ + 0x0377, # GREEK SMALL LETTER PAMPHYLIAN DIGAMMA ͷ + 0x037B, # GREEK SMALL REVERSED LUNATE SIGMA SYMBOL ͻ + 0x037C, # GREEK SMALL DOTTED LUNATE SIGMA SYMBOL ͼ + 0x037D, # GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL ͽ + 0x03AC, # GREEK SMALL LETTER ALPHA WITH TONOS ά + 0x03AD, # GREEK SMALL LETTER EPSILON WITH TONOS έ + 0x03AE, # GREEK SMALL LETTER ETA WITH TONOS ή + 0x03AF, # GREEK SMALL LETTER IOTA WITH TONOS ί + 0x03B1, # GREEK SMALL LETTER ALPHA α + 0x03B2, # GREEK SMALL LETTER BETA β + 0x03B3, # GREEK SMALL LETTER GAMMA γ + 0x03B4, # GREEK SMALL LETTER DELTA δ + 0x03B5, # GREEK SMALL LETTER EPSILON ε + 0x03B6, # GREEK SMALL LETTER ZETA ζ + 0x03B7, # GREEK SMALL LETTER ETA η + 0x03B8, # GREEK SMALL LETTER THETA θ + 0x03B9, # GREEK SMALL LETTER IOTA ι + 0x03BA, # GREEK SMALL LETTER KAPPA κ + 0x03BB, # GREEK SMALL LETTER LAMDA λ + 0x03BC, # GREEK SMALL LETTER MU μ + 0x03BD, # GREEK SMALL LETTER NU ν + 0x03BE, # GREEK SMALL LETTER XI ξ + 0x03BF, # GREEK SMALL LETTER OMICRON ο + 0x03C0, # GREEK SMALL LETTER PI π + 0x03C1, # GREEK SMALL LETTER RHO ρ + 0x03C2, # GREEK SMALL LETTER FINAL SIGMA ς + 0x03C3, # GREEK SMALL LETTER SIGMA σ + 0x03C4, # GREEK SMALL LETTER TAU τ + 0x03C5, # GREEK SMALL LETTER UPSILON υ + 0x03C6, # GREEK SMALL LETTER PHI φ + 0x03C7, # GREEK SMALL LETTER CHI χ + 0x03C8, # GREEK SMALL LETTER PSI ψ + 0x03C9, # GREEK SMALL LETTER OMEGA ω + 0x03CA, # GREEK SMALL LETTER IOTA WITH DIALYTIKA ϊ + 0x03CB, # GREEK SMALL LETTER UPSILON WITH DIALYTIKA ϋ + 0x03CC, # GREEK SMALL LETTER OMICRON WITH TONOS ό + 0x03CD, # GREEK SMALL LETTER UPSILON WITH TONOS ύ + 0x03CE, # GREEK SMALL LETTER OMEGA WITH TONOS ώ + 0x03D0, # GREEK BETA SYMBOL ϐ + 0x03D1, # GREEK THETA SYMBOL ϑ + 0x03D5, # GREEK PHI SYMBOL ϕ + 0x03D6, # GREEK PI SYMBOL ϖ + 0x03D7, # GREEK KAI SYMBOL ϗ + 0x03D9, # GREEK SMALL LETTER ARCHAIC KOPPA ϙ + 0x03DB, # GREEK SMALL LETTER STIGMA ϛ + 0x03DD, # GREEK SMALL LETTER DIGAMMA ϝ + 0x03DF, # GREEK SMALL LETTER KOPPA ϟ + 0x03E1, # GREEK SMALL LETTER SAMPI ϡ + 0x03E3, # COPTIC SMALL LETTER SHEI ϣ + 0x03E5, # COPTIC SMALL LETTER FEI ϥ + 0x03E7, # COPTIC SMALL LETTER KHEI ϧ + 0x03E9, # COPTIC SMALL LETTER HORI ϩ + 0x03EB, # COPTIC SMALL LETTER GANGIA ϫ + 0x03ED, # COPTIC SMALL LETTER SHIMA ϭ + 0x03EF, # COPTIC SMALL LETTER DEI ϯ + 0x03F0, # GREEK KAPPA SYMBOL ϰ + 0x03F1, # GREEK RHO SYMBOL ϱ + 0x03F2, # GREEK LUNATE SIGMA SYMBOL ϲ + 0x03F3, # GREEK LETTER YOT ϳ + 0x03F5, # GREEK LUNATE EPSILON SYMBOL ϵ + 0x03F8, # GREEK SMALL LETTER SHO ϸ + 0x03FB, # GREEK SMALL LETTER SAN ϻ + 0x0430, # CYRILLIC SMALL LETTER A а + 0x0431, # CYRILLIC SMALL LETTER BE б + 0x0432, # CYRILLIC SMALL LETTER VE в + 0x0433, # CYRILLIC SMALL LETTER GHE г + 0x0434, # CYRILLIC SMALL LETTER DE д + 0x0435, # CYRILLIC SMALL LETTER IE е + 0x0436, # CYRILLIC SMALL LETTER ZHE ж + 0x0437, # CYRILLIC SMALL LETTER ZE з + 0x0438, # CYRILLIC SMALL LETTER I и + 0x0439, # CYRILLIC SMALL LETTER SHORT I й + 0x043A, # CYRILLIC SMALL LETTER KA к + 0x043B, # CYRILLIC SMALL LETTER EL л + 0x043C, # CYRILLIC SMALL LETTER EM м + 0x043D, # CYRILLIC SMALL LETTER EN н + 0x043E, # CYRILLIC SMALL LETTER O о + 0x043F, # CYRILLIC SMALL LETTER PE п + 0x0440, # CYRILLIC SMALL LETTER ER р + 0x0441, # CYRILLIC SMALL LETTER ES с + 0x0442, # CYRILLIC SMALL LETTER TE т + 0x0443, # CYRILLIC SMALL LETTER U у + 0x0444, # CYRILLIC SMALL LETTER EF ф + 0x0445, # CYRILLIC SMALL LETTER HA х + 0x0446, # CYRILLIC SMALL LETTER TSE ц + 0x0447, # CYRILLIC SMALL LETTER CHE ч + 0x0448, # CYRILLIC SMALL LETTER SHA ш + 0x0449, # CYRILLIC SMALL LETTER SHCHA щ + 0x044A, # CYRILLIC SMALL LETTER HARD SIGN ъ + 0x044B, # CYRILLIC SMALL LETTER YERU ы + 0x044C, # CYRILLIC SMALL LETTER SOFT SIGN ь + 0x044D, # CYRILLIC SMALL LETTER E э + 0x044E, # CYRILLIC SMALL LETTER YU ю + 0x044F, # CYRILLIC SMALL LETTER YA я + 0x0450, # CYRILLIC SMALL LETTER IE WITH GRAVE ѐ + 0x0451, # CYRILLIC SMALL LETTER IO ё + 0x0452, # CYRILLIC SMALL LETTER DJE ђ + 0x0453, # CYRILLIC SMALL LETTER GJE ѓ + 0x0454, # CYRILLIC SMALL LETTER UKRAINIAN IE є + 0x0455, # CYRILLIC SMALL LETTER DZE ѕ + 0x0456, # CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I і + 0x0457, # CYRILLIC SMALL LETTER YI ї + 0x0458, # CYRILLIC SMALL LETTER JE ј + 0x0459, # CYRILLIC SMALL LETTER LJE љ + 0x045A, # CYRILLIC SMALL LETTER NJE њ + 0x045B, # CYRILLIC SMALL LETTER TSHE ћ + 0x045C, # CYRILLIC SMALL LETTER KJE ќ + 0x045D, # CYRILLIC SMALL LETTER I WITH GRAVE ѝ + 0x045E, # CYRILLIC SMALL LETTER SHORT U ў + 0x045F, # CYRILLIC SMALL LETTER DZHE џ + 0x0461, # CYRILLIC SMALL LETTER OMEGA ѡ + 0x0463, # CYRILLIC SMALL LETTER YAT ѣ + 0x0465, # CYRILLIC SMALL LETTER IOTIFIED E ѥ + 0x0467, # CYRILLIC SMALL LETTER LITTLE YUS ѧ + 0x0469, # CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS ѩ + 0x046B, # CYRILLIC SMALL LETTER BIG YUS ѫ + 0x046D, # CYRILLIC SMALL LETTER IOTIFIED BIG YUS ѭ + 0x046F, # CYRILLIC SMALL LETTER KSI ѯ + 0x0471, # CYRILLIC SMALL LETTER PSI ѱ + 0x0473, # CYRILLIC SMALL LETTER FITA ѳ + 0x0475, # CYRILLIC SMALL LETTER IZHITSA ѵ + 0x0477, # CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT ѷ + 0x0479, # CYRILLIC SMALL LETTER UK ѹ + 0x047B, # CYRILLIC SMALL LETTER ROUND OMEGA ѻ + 0x047D, # CYRILLIC SMALL LETTER OMEGA WITH TITLO ѽ + 0x047F, # CYRILLIC SMALL LETTER OT ѿ + 0x0481, # CYRILLIC SMALL LETTER KOPPA ҁ + 0x048B, # CYRILLIC SMALL LETTER SHORT I WITH TAIL ҋ + 0x048D, # CYRILLIC SMALL LETTER SEMISOFT SIGN ҍ + 0x048F, # CYRILLIC SMALL LETTER ER WITH TICK ҏ + 0x0491, # CYRILLIC SMALL LETTER GHE WITH UPTURN ґ + 0x0493, # CYRILLIC SMALL LETTER GHE WITH STROKE ғ + 0x0495, # CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK ҕ + 0x0497, # CYRILLIC SMALL LETTER ZHE WITH DESCENDER җ + 0x0499, # CYRILLIC SMALL LETTER ZE WITH DESCENDER ҙ + 0x049B, # CYRILLIC SMALL LETTER KA WITH DESCENDER қ + 0x049D, # CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE ҝ + 0x049F, # CYRILLIC SMALL LETTER KA WITH STROKE ҟ + 0x04A1, # CYRILLIC SMALL LETTER BASHKIR KA ҡ + 0x04A3, # CYRILLIC SMALL LETTER EN WITH DESCENDER ң + 0x04A5, # CYRILLIC SMALL LIGATURE EN GHE ҥ + 0x04A7, # CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK ҧ + 0x04A9, # CYRILLIC SMALL LETTER ABKHASIAN HA ҩ + 0x04AB, # CYRILLIC SMALL LETTER ES WITH DESCENDER ҫ + 0x04AD, # CYRILLIC SMALL LETTER TE WITH DESCENDER ҭ + 0x04AF, # CYRILLIC SMALL LETTER STRAIGHT U ү + 0x04B1, # CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE ұ + 0x04B3, # CYRILLIC SMALL LETTER HA WITH DESCENDER ҳ + 0x04B5, # CYRILLIC SMALL LIGATURE TE TSE ҵ + 0x04B7, # CYRILLIC SMALL LETTER CHE WITH DESCENDER ҷ + 0x04B9, # CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE ҹ + 0x04BB, # CYRILLIC SMALL LETTER SHHA һ + 0x04BD, # CYRILLIC SMALL LETTER ABKHASIAN CHE ҽ + 0x04BF, # CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER ҿ + 0x04C2, # CYRILLIC SMALL LETTER ZHE WITH BREVE ӂ + 0x04C4, # CYRILLIC SMALL LETTER KA WITH HOOK ӄ + 0x04C6, # CYRILLIC SMALL LETTER EL WITH TAIL ӆ + 0x04C8, # CYRILLIC SMALL LETTER EN WITH HOOK ӈ + 0x04CA, # CYRILLIC SMALL LETTER EN WITH TAIL ӊ + 0x04CC, # CYRILLIC SMALL LETTER KHAKASSIAN CHE ӌ + 0x04CE, # CYRILLIC SMALL LETTER EM WITH TAIL ӎ + 0x04CF, # CYRILLIC SMALL LETTER PALOCHKA ӏ + 0x04D1, # CYRILLIC SMALL LETTER A WITH BREVE ӑ + 0x04D3, # CYRILLIC SMALL LETTER A WITH DIAERESIS ӓ + 0x04D5, # CYRILLIC SMALL LIGATURE A IE ӕ + 0x04D7, # CYRILLIC SMALL LETTER IE WITH BREVE ӗ + 0x04D9, # CYRILLIC SMALL LETTER SCHWA ә + 0x04DB, # CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS ӛ + 0x04DD, # CYRILLIC SMALL LETTER ZHE WITH DIAERESIS ӝ + 0x04DF, # CYRILLIC SMALL LETTER ZE WITH DIAERESIS ӟ + 0x04E1, # CYRILLIC SMALL LETTER ABKHASIAN DZE ӡ + 0x04E3, # CYRILLIC SMALL LETTER I WITH MACRON ӣ + 0x04E5, # CYRILLIC SMALL LETTER I WITH DIAERESIS ӥ + 0x04E7, # CYRILLIC SMALL LETTER O WITH DIAERESIS ӧ + 0x04E9, # CYRILLIC SMALL LETTER BARRED O ө + 0x04EB, # CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS ӫ + 0x04ED, # CYRILLIC SMALL LETTER E WITH DIAERESIS ӭ + 0x04EF, # CYRILLIC SMALL LETTER U WITH MACRON ӯ + 0x04F1, # CYRILLIC SMALL LETTER U WITH DIAERESIS ӱ + 0x04F3, # CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE ӳ + 0x04F5, # CYRILLIC SMALL LETTER CHE WITH DIAERESIS ӵ + 0x04F7, # CYRILLIC SMALL LETTER GHE WITH DESCENDER ӷ + 0x04F9, # CYRILLIC SMALL LETTER YERU WITH DIAERESIS ӹ + 0x04FB, # CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK ӻ + 0x04FD, # CYRILLIC SMALL LETTER HA WITH HOOK ӽ + 0x04FF, # CYRILLIC SMALL LETTER HA WITH STROKE ӿ + 0x0501, # CYRILLIC SMALL LETTER KOMI DE ԁ + 0x0503, # CYRILLIC SMALL LETTER KOMI DJE ԃ + 0x0505, # CYRILLIC SMALL LETTER KOMI ZJE ԅ + 0x0507, # CYRILLIC SMALL LETTER KOMI DZJE ԇ + 0x0509, # CYRILLIC SMALL LETTER KOMI LJE ԉ + 0x050B, # CYRILLIC SMALL LETTER KOMI NJE ԋ + 0x050D, # CYRILLIC SMALL LETTER KOMI SJE ԍ + 0x050F, # CYRILLIC SMALL LETTER KOMI TJE ԏ + 0x0511, # CYRILLIC SMALL LETTER REVERSED ZE ԑ + 0x0513, # CYRILLIC SMALL LETTER EL WITH HOOK ԓ + 0x0515, # CYRILLIC SMALL LETTER LHA ԕ + 0x0517, # CYRILLIC SMALL LETTER RHA ԗ + 0x0519, # CYRILLIC SMALL LETTER YAE ԙ + 0x051B, # CYRILLIC SMALL LETTER QA ԛ + 0x051D, # CYRILLIC SMALL LETTER WE ԝ + 0x051F, # CYRILLIC SMALL LETTER ALEUT KA ԟ + 0x0521, # CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK ԡ + 0x0523, # CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK ԣ + 0x0525, # CYRILLIC SMALL LETTER PE WITH DESCENDER ԥ + 0x0527, # CYRILLIC SMALL LETTER SHHA WITH DESCENDER ԧ + 0x0529, # CYRILLIC SMALL LETTER EN WITH LEFT HOOK ԩ + 0x052B, # CYRILLIC SMALL LETTER DZZHE ԫ + 0x052D, # CYRILLIC SMALL LETTER DCHE ԭ + 0x052F, # CYRILLIC SMALL LETTER EL WITH DESCENDER ԯ + 0x0561, # ARMENIAN SMALL LETTER AYB ա + 0x0562, # ARMENIAN SMALL LETTER BEN բ + 0x0563, # ARMENIAN SMALL LETTER GIM գ + 0x0564, # ARMENIAN SMALL LETTER DA դ + 0x0565, # ARMENIAN SMALL LETTER ECH ե + 0x0566, # ARMENIAN SMALL LETTER ZA զ + 0x0567, # ARMENIAN SMALL LETTER EH է + 0x0568, # ARMENIAN SMALL LETTER ET ը + 0x0569, # ARMENIAN SMALL LETTER TO թ + 0x056A, # ARMENIAN SMALL LETTER ZHE ժ + 0x056B, # ARMENIAN SMALL LETTER INI ի + 0x056C, # ARMENIAN SMALL LETTER LIWN լ + 0x056D, # ARMENIAN SMALL LETTER XEH խ + 0x056E, # ARMENIAN SMALL LETTER CA ծ + 0x056F, # ARMENIAN SMALL LETTER KEN կ + 0x0570, # ARMENIAN SMALL LETTER HO հ + 0x0571, # ARMENIAN SMALL LETTER JA ձ + 0x0572, # ARMENIAN SMALL LETTER GHAD ղ + 0x0573, # ARMENIAN SMALL LETTER CHEH ճ + 0x0574, # ARMENIAN SMALL LETTER MEN մ + 0x0575, # ARMENIAN SMALL LETTER YI յ + 0x0576, # ARMENIAN SMALL LETTER NOW ն + 0x0577, # ARMENIAN SMALL LETTER SHA շ + 0x0578, # ARMENIAN SMALL LETTER VO ո + 0x0579, # ARMENIAN SMALL LETTER CHA չ + 0x057A, # ARMENIAN SMALL LETTER PEH պ + 0x057B, # ARMENIAN SMALL LETTER JHEH ջ + 0x057C, # ARMENIAN SMALL LETTER RA ռ + 0x057D, # ARMENIAN SMALL LETTER SEH ս + 0x057E, # ARMENIAN SMALL LETTER VEW վ + 0x057F, # ARMENIAN SMALL LETTER TIWN տ + 0x0580, # ARMENIAN SMALL LETTER REH ր + 0x0581, # ARMENIAN SMALL LETTER CO ց + 0x0582, # ARMENIAN SMALL LETTER YIWN ւ + 0x0583, # ARMENIAN SMALL LETTER PIWR փ + 0x0584, # ARMENIAN SMALL LETTER KEH ք + 0x0585, # ARMENIAN SMALL LETTER OH օ + 0x0586, # ARMENIAN SMALL LETTER FEH ֆ + 0x10D0, # GEORGIAN LETTER AN ა + 0x10D1, # GEORGIAN LETTER BAN ბ + 0x10D2, # GEORGIAN LETTER GAN გ + 0x10D3, # GEORGIAN LETTER DON დ + 0x10D4, # GEORGIAN LETTER EN ე + 0x10D5, # GEORGIAN LETTER VIN ვ + 0x10D6, # GEORGIAN LETTER ZEN ზ + 0x10D7, # GEORGIAN LETTER TAN თ + 0x10D8, # GEORGIAN LETTER IN ი + 0x10D9, # GEORGIAN LETTER KAN კ + 0x10DA, # GEORGIAN LETTER LAS ლ + 0x10DB, # GEORGIAN LETTER MAN მ + 0x10DC, # GEORGIAN LETTER NAR ნ + 0x10DD, # GEORGIAN LETTER ON ო + 0x10DE, # GEORGIAN LETTER PAR პ + 0x10DF, # GEORGIAN LETTER ZHAR ჟ + 0x10E0, # GEORGIAN LETTER RAE რ + 0x10E1, # GEORGIAN LETTER SAN ს + 0x10E2, # GEORGIAN LETTER TAR ტ + 0x10E3, # GEORGIAN LETTER UN უ + 0x10E4, # GEORGIAN LETTER PHAR ფ + 0x10E5, # GEORGIAN LETTER KHAR ქ + 0x10E6, # GEORGIAN LETTER GHAN ღ + 0x10E7, # GEORGIAN LETTER QAR ყ + 0x10E8, # GEORGIAN LETTER SHIN შ + 0x10E9, # GEORGIAN LETTER CHIN ჩ + 0x10EA, # GEORGIAN LETTER CAN ც + 0x10EB, # GEORGIAN LETTER JIL ძ + 0x10EC, # GEORGIAN LETTER CIL წ + 0x10ED, # GEORGIAN LETTER CHAR ჭ + 0x10EE, # GEORGIAN LETTER XAN ხ + 0x10EF, # GEORGIAN LETTER JHAN ჯ + 0x10F0, # GEORGIAN LETTER HAE ჰ + 0x10F1, # GEORGIAN LETTER HE ჱ + 0x10F2, # GEORGIAN LETTER HIE ჲ + 0x10F3, # GEORGIAN LETTER WE ჳ + 0x10F4, # GEORGIAN LETTER HAR ჴ + 0x10F5, # GEORGIAN LETTER HOE ჵ + 0x10F6, # GEORGIAN LETTER FI ჶ + 0x10F7, # GEORGIAN LETTER YN ჷ + 0x10F8, # GEORGIAN LETTER ELIFI ჸ + 0x10F9, # GEORGIAN LETTER TURNED GAN ჹ + 0x10FA, # GEORGIAN LETTER AIN ჺ + 0x10FD, # GEORGIAN LETTER AEN ჽ + 0x10FE, # GEORGIAN LETTER HARD SIGN ჾ + 0x10FF, # GEORGIAN LETTER LABIAL SIGN ჿ + 0x13F8, # CHEROKEE SMALL LETTER YE ᏸ + 0x13F9, # CHEROKEE SMALL LETTER YI ᏹ + 0x13FA, # CHEROKEE SMALL LETTER YO ᏺ + 0x13FB, # CHEROKEE SMALL LETTER YU ᏻ + 0x13FC, # CHEROKEE SMALL LETTER YV ᏼ + 0x13FD, # CHEROKEE SMALL LETTER MV ᏽ + 0x1C80, # CYRILLIC SMALL LETTER ROUNDED VE ᲀ + 0x1C81, # CYRILLIC SMALL LETTER LONG-LEGGED DE ᲁ + 0x1C82, # CYRILLIC SMALL LETTER NARROW O ᲂ + 0x1C83, # CYRILLIC SMALL LETTER WIDE ES ᲃ + 0x1C84, # CYRILLIC SMALL LETTER TALL TE ᲄ + 0x1C85, # CYRILLIC SMALL LETTER THREE-LEGGED TE ᲅ + 0x1C86, # CYRILLIC SMALL LETTER TALL HARD SIGN ᲆ + 0x1C87, # CYRILLIC SMALL LETTER TALL YAT ᲇ + 0x1C88, # CYRILLIC SMALL LETTER UNBLENDED UK ᲈ + 0x1D79, # LATIN SMALL LETTER INSULAR G ᵹ + 0x1D7D, # LATIN SMALL LETTER P WITH STROKE ᵽ + 0x1D8E, # LATIN SMALL LETTER Z WITH PALATAL HOOK ᶎ + 0x1E01, # LATIN SMALL LETTER A WITH RING BELOW ḁ + 0x1E03, # LATIN SMALL LETTER B WITH DOT ABOVE ḃ + 0x1E05, # LATIN SMALL LETTER B WITH DOT BELOW ḅ + 0x1E07, # LATIN SMALL LETTER B WITH LINE BELOW ḇ + 0x1E09, # LATIN SMALL LETTER C WITH CEDILLA AND ACUTE ḉ + 0x1E0B, # LATIN SMALL LETTER D WITH DOT ABOVE ḋ + 0x1E0D, # LATIN SMALL LETTER D WITH DOT BELOW ḍ + 0x1E0F, # LATIN SMALL LETTER D WITH LINE BELOW ḏ + 0x1E11, # LATIN SMALL LETTER D WITH CEDILLA ḑ + 0x1E13, # LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW ḓ + 0x1E15, # LATIN SMALL LETTER E WITH MACRON AND GRAVE ḕ + 0x1E17, # LATIN SMALL LETTER E WITH MACRON AND ACUTE ḗ + 0x1E19, # LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW ḙ + 0x1E1B, # LATIN SMALL LETTER E WITH TILDE BELOW ḛ + 0x1E1D, # LATIN SMALL LETTER E WITH CEDILLA AND BREVE ḝ + 0x1E1F, # LATIN SMALL LETTER F WITH DOT ABOVE ḟ + 0x1E21, # LATIN SMALL LETTER G WITH MACRON ḡ + 0x1E23, # LATIN SMALL LETTER H WITH DOT ABOVE ḣ + 0x1E25, # LATIN SMALL LETTER H WITH DOT BELOW ḥ + 0x1E27, # LATIN SMALL LETTER H WITH DIAERESIS ḧ + 0x1E29, # LATIN SMALL LETTER H WITH CEDILLA ḩ + 0x1E2B, # LATIN SMALL LETTER H WITH BREVE BELOW ḫ + 0x1E2D, # LATIN SMALL LETTER I WITH TILDE BELOW ḭ + 0x1E2F, # LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE ḯ + 0x1E31, # LATIN SMALL LETTER K WITH ACUTE ḱ + 0x1E33, # LATIN SMALL LETTER K WITH DOT BELOW ḳ + 0x1E35, # LATIN SMALL LETTER K WITH LINE BELOW ḵ + 0x1E37, # LATIN SMALL LETTER L WITH DOT BELOW ḷ + 0x1E39, # LATIN SMALL LETTER L WITH DOT BELOW AND MACRON ḹ + 0x1E3B, # LATIN SMALL LETTER L WITH LINE BELOW ḻ + 0x1E3D, # LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW ḽ + 0x1E3F, # LATIN SMALL LETTER M WITH ACUTE ḿ + 0x1E41, # LATIN SMALL LETTER M WITH DOT ABOVE ṁ + 0x1E43, # LATIN SMALL LETTER M WITH DOT BELOW ṃ + 0x1E45, # LATIN SMALL LETTER N WITH DOT ABOVE ṅ + 0x1E47, # LATIN SMALL LETTER N WITH DOT BELOW ṇ + 0x1E49, # LATIN SMALL LETTER N WITH LINE BELOW ṉ + 0x1E4B, # LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW ṋ + 0x1E4D, # LATIN SMALL LETTER O WITH TILDE AND ACUTE ṍ + 0x1E4F, # LATIN SMALL LETTER O WITH TILDE AND DIAERESIS ṏ + 0x1E51, # LATIN SMALL LETTER O WITH MACRON AND GRAVE ṑ + 0x1E53, # LATIN SMALL LETTER O WITH MACRON AND ACUTE ṓ + 0x1E55, # LATIN SMALL LETTER P WITH ACUTE ṕ + 0x1E57, # LATIN SMALL LETTER P WITH DOT ABOVE ṗ + 0x1E59, # LATIN SMALL LETTER R WITH DOT ABOVE ṙ + 0x1E5B, # LATIN SMALL LETTER R WITH DOT BELOW ṛ + 0x1E5D, # LATIN SMALL LETTER R WITH DOT BELOW AND MACRON ṝ + 0x1E5F, # LATIN SMALL LETTER R WITH LINE BELOW ṟ + 0x1E61, # LATIN SMALL LETTER S WITH DOT ABOVE ṡ + 0x1E63, # LATIN SMALL LETTER S WITH DOT BELOW ṣ + 0x1E65, # LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE ṥ + 0x1E67, # LATIN SMALL LETTER S WITH CARON AND DOT ABOVE ṧ + 0x1E69, # LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE ṩ + 0x1E6B, # LATIN SMALL LETTER T WITH DOT ABOVE ṫ + 0x1E6D, # LATIN SMALL LETTER T WITH DOT BELOW ṭ + 0x1E6F, # LATIN SMALL LETTER T WITH LINE BELOW ṯ + 0x1E71, # LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW ṱ + 0x1E73, # LATIN SMALL LETTER U WITH DIAERESIS BELOW ṳ + 0x1E75, # LATIN SMALL LETTER U WITH TILDE BELOW ṵ + 0x1E77, # LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW ṷ + 0x1E79, # LATIN SMALL LETTER U WITH TILDE AND ACUTE ṹ + 0x1E7B, # LATIN SMALL LETTER U WITH MACRON AND DIAERESIS ṻ + 0x1E7D, # LATIN SMALL LETTER V WITH TILDE ṽ + 0x1E7F, # LATIN SMALL LETTER V WITH DOT BELOW ṿ + 0x1E81, # LATIN SMALL LETTER W WITH GRAVE ẁ + 0x1E83, # LATIN SMALL LETTER W WITH ACUTE ẃ + 0x1E85, # LATIN SMALL LETTER W WITH DIAERESIS ẅ + 0x1E87, # LATIN SMALL LETTER W WITH DOT ABOVE ẇ + 0x1E89, # LATIN SMALL LETTER W WITH DOT BELOW ẉ + 0x1E8B, # LATIN SMALL LETTER X WITH DOT ABOVE ẋ + 0x1E8D, # LATIN SMALL LETTER X WITH DIAERESIS ẍ + 0x1E8F, # LATIN SMALL LETTER Y WITH DOT ABOVE ẏ + 0x1E91, # LATIN SMALL LETTER Z WITH CIRCUMFLEX ẑ + 0x1E93, # LATIN SMALL LETTER Z WITH DOT BELOW ẓ + 0x1E95, # LATIN SMALL LETTER Z WITH LINE BELOW ẕ + 0x1E9B, # LATIN SMALL LETTER LONG S WITH DOT ABOVE ẛ + 0x1EA1, # LATIN SMALL LETTER A WITH DOT BELOW ạ + 0x1EA3, # LATIN SMALL LETTER A WITH HOOK ABOVE ả + 0x1EA5, # LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE ấ + 0x1EA7, # LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE ầ + 0x1EA9, # LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE ẩ + 0x1EAB, # LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE ẫ + 0x1EAD, # LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW ậ + 0x1EAF, # LATIN SMALL LETTER A WITH BREVE AND ACUTE ắ + 0x1EB1, # LATIN SMALL LETTER A WITH BREVE AND GRAVE ằ + 0x1EB3, # LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE ẳ + 0x1EB5, # LATIN SMALL LETTER A WITH BREVE AND TILDE ẵ + 0x1EB7, # LATIN SMALL LETTER A WITH BREVE AND DOT BELOW ặ + 0x1EB9, # LATIN SMALL LETTER E WITH DOT BELOW ẹ + 0x1EBB, # LATIN SMALL LETTER E WITH HOOK ABOVE ẻ + 0x1EBD, # LATIN SMALL LETTER E WITH TILDE ẽ + 0x1EBF, # LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE ế + 0x1EC1, # LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE ề + 0x1EC3, # LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE ể + 0x1EC5, # LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE ễ + 0x1EC7, # LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW ệ + 0x1EC9, # LATIN SMALL LETTER I WITH HOOK ABOVE ỉ + 0x1ECB, # LATIN SMALL LETTER I WITH DOT BELOW ị + 0x1ECD, # LATIN SMALL LETTER O WITH DOT BELOW ọ + 0x1ECF, # LATIN SMALL LETTER O WITH HOOK ABOVE ỏ + 0x1ED1, # LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE ố + 0x1ED3, # LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE ồ + 0x1ED5, # LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE ổ + 0x1ED7, # LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE ỗ + 0x1ED9, # LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW ộ + 0x1EDB, # LATIN SMALL LETTER O WITH HORN AND ACUTE ớ + 0x1EDD, # LATIN SMALL LETTER O WITH HORN AND GRAVE ờ + 0x1EDF, # LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE ở + 0x1EE1, # LATIN SMALL LETTER O WITH HORN AND TILDE ỡ + 0x1EE3, # LATIN SMALL LETTER O WITH HORN AND DOT BELOW ợ + 0x1EE5, # LATIN SMALL LETTER U WITH DOT BELOW ụ + 0x1EE7, # LATIN SMALL LETTER U WITH HOOK ABOVE ủ + 0x1EE9, # LATIN SMALL LETTER U WITH HORN AND ACUTE ứ + 0x1EEB, # LATIN SMALL LETTER U WITH HORN AND GRAVE ừ + 0x1EED, # LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE ử + 0x1EEF, # LATIN SMALL LETTER U WITH HORN AND TILDE ữ + 0x1EF1, # LATIN SMALL LETTER U WITH HORN AND DOT BELOW ự + 0x1EF3, # LATIN SMALL LETTER Y WITH GRAVE ỳ + 0x1EF5, # LATIN SMALL LETTER Y WITH DOT BELOW ỵ + 0x1EF7, # LATIN SMALL LETTER Y WITH HOOK ABOVE ỷ + 0x1EF9, # LATIN SMALL LETTER Y WITH TILDE ỹ + 0x1EFB, # LATIN SMALL LETTER MIDDLE-WELSH LL ỻ + 0x1EFD, # LATIN SMALL LETTER MIDDLE-WELSH V ỽ + 0x1EFF, # LATIN SMALL LETTER Y WITH LOOP ỿ + 0x1F00, # GREEK SMALL LETTER ALPHA WITH PSILI ἀ + 0x1F01, # GREEK SMALL LETTER ALPHA WITH DASIA ἁ + 0x1F02, # GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA ἂ + 0x1F03, # GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA ἃ + 0x1F04, # GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA ἄ + 0x1F05, # GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA ἅ + 0x1F06, # GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI ἆ + 0x1F07, # GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI ἇ + 0x1F10, # GREEK SMALL LETTER EPSILON WITH PSILI ἐ + 0x1F11, # GREEK SMALL LETTER EPSILON WITH DASIA ἑ + 0x1F12, # GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA ἒ + 0x1F13, # GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA ἓ + 0x1F14, # GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA ἔ + 0x1F15, # GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA ἕ + 0x1F20, # GREEK SMALL LETTER ETA WITH PSILI ἠ + 0x1F21, # GREEK SMALL LETTER ETA WITH DASIA ἡ + 0x1F22, # GREEK SMALL LETTER ETA WITH PSILI AND VARIA ἢ + 0x1F23, # GREEK SMALL LETTER ETA WITH DASIA AND VARIA ἣ + 0x1F24, # GREEK SMALL LETTER ETA WITH PSILI AND OXIA ἤ + 0x1F25, # GREEK SMALL LETTER ETA WITH DASIA AND OXIA ἥ + 0x1F26, # GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI ἦ + 0x1F27, # GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI ἧ + 0x1F30, # GREEK SMALL LETTER IOTA WITH PSILI ἰ + 0x1F31, # GREEK SMALL LETTER IOTA WITH DASIA ἱ + 0x1F32, # GREEK SMALL LETTER IOTA WITH PSILI AND VARIA ἲ + 0x1F33, # GREEK SMALL LETTER IOTA WITH DASIA AND VARIA ἳ + 0x1F34, # GREEK SMALL LETTER IOTA WITH PSILI AND OXIA ἴ + 0x1F35, # GREEK SMALL LETTER IOTA WITH DASIA AND OXIA ἵ + 0x1F36, # GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI ἶ + 0x1F37, # GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI ἷ + 0x1F40, # GREEK SMALL LETTER OMICRON WITH PSILI ὀ + 0x1F41, # GREEK SMALL LETTER OMICRON WITH DASIA ὁ + 0x1F42, # GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA ὂ + 0x1F43, # GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA ὃ + 0x1F44, # GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA ὄ + 0x1F45, # GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA ὅ + 0x1F51, # GREEK SMALL LETTER UPSILON WITH DASIA ὑ + 0x1F53, # GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA ὓ + 0x1F55, # GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA ὕ + 0x1F57, # GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI ὗ + 0x1F60, # GREEK SMALL LETTER OMEGA WITH PSILI ὠ + 0x1F61, # GREEK SMALL LETTER OMEGA WITH DASIA ὡ + 0x1F62, # GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA ὢ + 0x1F63, # GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA ὣ + 0x1F64, # GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA ὤ + 0x1F65, # GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA ὥ + 0x1F66, # GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI ὦ + 0x1F67, # GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI ὧ + 0x1F70, # GREEK SMALL LETTER ALPHA WITH VARIA ὰ + 0x1F71, # GREEK SMALL LETTER ALPHA WITH OXIA ά + 0x1F72, # GREEK SMALL LETTER EPSILON WITH VARIA ὲ + 0x1F73, # GREEK SMALL LETTER EPSILON WITH OXIA έ + 0x1F74, # GREEK SMALL LETTER ETA WITH VARIA ὴ + 0x1F75, # GREEK SMALL LETTER ETA WITH OXIA ή + 0x1F76, # GREEK SMALL LETTER IOTA WITH VARIA ὶ + 0x1F77, # GREEK SMALL LETTER IOTA WITH OXIA ί + 0x1F78, # GREEK SMALL LETTER OMICRON WITH VARIA ὸ + 0x1F79, # GREEK SMALL LETTER OMICRON WITH OXIA ό + 0x1F7A, # GREEK SMALL LETTER UPSILON WITH VARIA ὺ + 0x1F7B, # GREEK SMALL LETTER UPSILON WITH OXIA ύ + 0x1F7C, # GREEK SMALL LETTER OMEGA WITH VARIA ὼ + 0x1F7D, # GREEK SMALL LETTER OMEGA WITH OXIA ώ + 0x1F80, # GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI ᾀ + 0x1F81, # GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI ᾁ + 0x1F82, # GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI ᾂ + 0x1F83, # GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI ᾃ + 0x1F84, # GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI ᾄ + 0x1F85, # GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI ᾅ + 0x1F86, # GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI ᾆ + 0x1F87, # GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI ᾇ + 0x1F90, # GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI ᾐ + 0x1F91, # GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI ᾑ + 0x1F92, # GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI ᾒ + 0x1F93, # GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI ᾓ + 0x1F94, # GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI ᾔ + 0x1F95, # GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI ᾕ + 0x1F96, # GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI ᾖ + 0x1F97, # GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI ᾗ + 0x1FA0, # GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI ᾠ + 0x1FA1, # GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI ᾡ + 0x1FA2, # GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI ᾢ + 0x1FA3, # GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI ᾣ + 0x1FA4, # GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI ᾤ + 0x1FA5, # GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI ᾥ + 0x1FA6, # GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI ᾦ + 0x1FA7, # GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI ᾧ + 0x1FB0, # GREEK SMALL LETTER ALPHA WITH VRACHY ᾰ + 0x1FB1, # GREEK SMALL LETTER ALPHA WITH MACRON ᾱ + 0x1FB3, # GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI ᾳ + 0x1FBE, # GREEK PROSGEGRAMMENI ι + 0x1FC3, # GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI ῃ + 0x1FD0, # GREEK SMALL LETTER IOTA WITH VRACHY ῐ + 0x1FD1, # GREEK SMALL LETTER IOTA WITH MACRON ῑ + 0x1FE0, # GREEK SMALL LETTER UPSILON WITH VRACHY ῠ + 0x1FE1, # GREEK SMALL LETTER UPSILON WITH MACRON ῡ + 0x1FE5, # GREEK SMALL LETTER RHO WITH DASIA ῥ + 0x1FF3, # GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI ῳ + 0x214E, # TURNED SMALL F ⅎ + 0x2170, # SMALL ROMAN NUMERAL ONE ⅰ + 0x2171, # SMALL ROMAN NUMERAL TWO ⅱ + 0x2172, # SMALL ROMAN NUMERAL THREE ⅲ + 0x2173, # SMALL ROMAN NUMERAL FOUR ⅳ + 0x2174, # SMALL ROMAN NUMERAL FIVE ⅴ + 0x2175, # SMALL ROMAN NUMERAL SIX ⅵ + 0x2176, # SMALL ROMAN NUMERAL SEVEN ⅶ + 0x2177, # SMALL ROMAN NUMERAL EIGHT ⅷ + 0x2178, # SMALL ROMAN NUMERAL NINE ⅸ + 0x2179, # SMALL ROMAN NUMERAL TEN ⅹ + 0x217A, # SMALL ROMAN NUMERAL ELEVEN ⅺ + 0x217B, # SMALL ROMAN NUMERAL TWELVE ⅻ + 0x217C, # SMALL ROMAN NUMERAL FIFTY ⅼ + 0x217D, # SMALL ROMAN NUMERAL ONE HUNDRED ⅽ + 0x217E, # SMALL ROMAN NUMERAL FIVE HUNDRED ⅾ + 0x217F, # SMALL ROMAN NUMERAL ONE THOUSAND ⅿ + 0x2184, # LATIN SMALL LETTER REVERSED C ↄ + 0x24D0, # CIRCLED LATIN SMALL LETTER A ⓐ + 0x24D1, # CIRCLED LATIN SMALL LETTER B ⓑ + 0x24D2, # CIRCLED LATIN SMALL LETTER C ⓒ + 0x24D3, # CIRCLED LATIN SMALL LETTER D ⓓ + 0x24D4, # CIRCLED LATIN SMALL LETTER E ⓔ + 0x24D5, # CIRCLED LATIN SMALL LETTER F ⓕ + 0x24D6, # CIRCLED LATIN SMALL LETTER G ⓖ + 0x24D7, # CIRCLED LATIN SMALL LETTER H ⓗ + 0x24D8, # CIRCLED LATIN SMALL LETTER I ⓘ + 0x24D9, # CIRCLED LATIN SMALL LETTER J ⓙ + 0x24DA, # CIRCLED LATIN SMALL LETTER K ⓚ + 0x24DB, # CIRCLED LATIN SMALL LETTER L ⓛ + 0x24DC, # CIRCLED LATIN SMALL LETTER M ⓜ + 0x24DD, # CIRCLED LATIN SMALL LETTER N ⓝ + 0x24DE, # CIRCLED LATIN SMALL LETTER O ⓞ + 0x24DF, # CIRCLED LATIN SMALL LETTER P ⓟ + 0x24E0, # CIRCLED LATIN SMALL LETTER Q ⓠ + 0x24E1, # CIRCLED LATIN SMALL LETTER R ⓡ + 0x24E2, # CIRCLED LATIN SMALL LETTER S ⓢ + 0x24E3, # CIRCLED LATIN SMALL LETTER T ⓣ + 0x24E4, # CIRCLED LATIN SMALL LETTER U ⓤ + 0x24E5, # CIRCLED LATIN SMALL LETTER V ⓥ + 0x24E6, # CIRCLED LATIN SMALL LETTER W ⓦ + 0x24E7, # CIRCLED LATIN SMALL LETTER X ⓧ + 0x24E8, # CIRCLED LATIN SMALL LETTER Y ⓨ + 0x24E9, # CIRCLED LATIN SMALL LETTER Z ⓩ + 0x2C30, # GLAGOLITIC SMALL LETTER AZU ⰰ + 0x2C31, # GLAGOLITIC SMALL LETTER BUKY ⰱ + 0x2C32, # GLAGOLITIC SMALL LETTER VEDE ⰲ + 0x2C33, # GLAGOLITIC SMALL LETTER GLAGOLI ⰳ + 0x2C34, # GLAGOLITIC SMALL LETTER DOBRO ⰴ + 0x2C35, # GLAGOLITIC SMALL LETTER YESTU ⰵ + 0x2C36, # GLAGOLITIC SMALL LETTER ZHIVETE ⰶ + 0x2C37, # GLAGOLITIC SMALL LETTER DZELO ⰷ + 0x2C38, # GLAGOLITIC SMALL LETTER ZEMLJA ⰸ + 0x2C39, # GLAGOLITIC SMALL LETTER IZHE ⰹ + 0x2C3A, # GLAGOLITIC SMALL LETTER INITIAL IZHE ⰺ + 0x2C3B, # GLAGOLITIC SMALL LETTER I ⰻ + 0x2C3C, # GLAGOLITIC SMALL LETTER DJERVI ⰼ + 0x2C3D, # GLAGOLITIC SMALL LETTER KAKO ⰽ + 0x2C3E, # GLAGOLITIC SMALL LETTER LJUDIJE ⰾ + 0x2C3F, # GLAGOLITIC SMALL LETTER MYSLITE ⰿ + 0x2C40, # GLAGOLITIC SMALL LETTER NASHI ⱀ + 0x2C41, # GLAGOLITIC SMALL LETTER ONU ⱁ + 0x2C42, # GLAGOLITIC SMALL LETTER POKOJI ⱂ + 0x2C43, # GLAGOLITIC SMALL LETTER RITSI ⱃ + 0x2C44, # GLAGOLITIC SMALL LETTER SLOVO ⱄ + 0x2C45, # GLAGOLITIC SMALL LETTER TVRIDO ⱅ + 0x2C46, # GLAGOLITIC SMALL LETTER UKU ⱆ + 0x2C47, # GLAGOLITIC SMALL LETTER FRITU ⱇ + 0x2C48, # GLAGOLITIC SMALL LETTER HERU ⱈ + 0x2C49, # GLAGOLITIC SMALL LETTER OTU ⱉ + 0x2C4A, # GLAGOLITIC SMALL LETTER PE ⱊ + 0x2C4B, # GLAGOLITIC SMALL LETTER SHTA ⱋ + 0x2C4C, # GLAGOLITIC SMALL LETTER TSI ⱌ + 0x2C4D, # GLAGOLITIC SMALL LETTER CHRIVI ⱍ + 0x2C4E, # GLAGOLITIC SMALL LETTER SHA ⱎ + 0x2C4F, # GLAGOLITIC SMALL LETTER YERU ⱏ + 0x2C50, # GLAGOLITIC SMALL LETTER YERI ⱐ + 0x2C51, # GLAGOLITIC SMALL LETTER YATI ⱑ + 0x2C52, # GLAGOLITIC SMALL LETTER SPIDERY HA ⱒ + 0x2C53, # GLAGOLITIC SMALL LETTER YU ⱓ + 0x2C54, # GLAGOLITIC SMALL LETTER SMALL YUS ⱔ + 0x2C55, # GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL ⱕ + 0x2C56, # GLAGOLITIC SMALL LETTER YO ⱖ + 0x2C57, # GLAGOLITIC SMALL LETTER IOTATED SMALL YUS ⱗ + 0x2C58, # GLAGOLITIC SMALL LETTER BIG YUS ⱘ + 0x2C59, # GLAGOLITIC SMALL LETTER IOTATED BIG YUS ⱙ + 0x2C5A, # GLAGOLITIC SMALL LETTER FITA ⱚ + 0x2C5B, # GLAGOLITIC SMALL LETTER IZHITSA ⱛ + 0x2C5C, # GLAGOLITIC SMALL LETTER SHTAPIC ⱜ + 0x2C5D, # GLAGOLITIC SMALL LETTER TROKUTASTI A ⱝ + 0x2C5E, # GLAGOLITIC SMALL LETTER LATINATE MYSLITE ⱞ + 0x2C5F, # GLAGOLITIC SMALL LETTER CAUDATE CHRIVI ⱟ + 0x2C61, # LATIN SMALL LETTER L WITH DOUBLE BAR ⱡ + 0x2C65, # LATIN SMALL LETTER A WITH STROKE ⱥ + 0x2C66, # LATIN SMALL LETTER T WITH DIAGONAL STROKE ⱦ + 0x2C68, # LATIN SMALL LETTER H WITH DESCENDER ⱨ + 0x2C6A, # LATIN SMALL LETTER K WITH DESCENDER ⱪ + 0x2C6C, # LATIN SMALL LETTER Z WITH DESCENDER ⱬ + 0x2C73, # LATIN SMALL LETTER W WITH HOOK ⱳ + 0x2C76, # LATIN SMALL LETTER HALF H ⱶ + 0x2C81, # COPTIC SMALL LETTER ALFA ⲁ + 0x2C83, # COPTIC SMALL LETTER VIDA ⲃ + 0x2C85, # COPTIC SMALL LETTER GAMMA ⲅ + 0x2C87, # COPTIC SMALL LETTER DALDA ⲇ + 0x2C89, # COPTIC SMALL LETTER EIE ⲉ + 0x2C8B, # COPTIC SMALL LETTER SOU ⲋ + 0x2C8D, # COPTIC SMALL LETTER ZATA ⲍ + 0x2C8F, # COPTIC SMALL LETTER HATE ⲏ + 0x2C91, # COPTIC SMALL LETTER THETHE ⲑ + 0x2C93, # COPTIC SMALL LETTER IAUDA ⲓ + 0x2C95, # COPTIC SMALL LETTER KAPA ⲕ + 0x2C97, # COPTIC SMALL LETTER LAULA ⲗ + 0x2C99, # COPTIC SMALL LETTER MI ⲙ + 0x2C9B, # COPTIC SMALL LETTER NI ⲛ + 0x2C9D, # COPTIC SMALL LETTER KSI ⲝ + 0x2C9F, # COPTIC SMALL LETTER O ⲟ + 0x2CA1, # COPTIC SMALL LETTER PI ⲡ + 0x2CA3, # COPTIC SMALL LETTER RO ⲣ + 0x2CA5, # COPTIC SMALL LETTER SIMA ⲥ + 0x2CA7, # COPTIC SMALL LETTER TAU ⲧ + 0x2CA9, # COPTIC SMALL LETTER UA ⲩ + 0x2CAB, # COPTIC SMALL LETTER FI ⲫ + 0x2CAD, # COPTIC SMALL LETTER KHI ⲭ + 0x2CAF, # COPTIC SMALL LETTER PSI ⲯ + 0x2CB1, # COPTIC SMALL LETTER OOU ⲱ + 0x2CB3, # COPTIC SMALL LETTER DIALECT-P ALEF ⲳ + 0x2CB5, # COPTIC SMALL LETTER OLD COPTIC AIN ⲵ + 0x2CB7, # COPTIC SMALL LETTER CRYPTOGRAMMIC EIE ⲷ + 0x2CB9, # COPTIC SMALL LETTER DIALECT-P KAPA ⲹ + 0x2CBB, # COPTIC SMALL LETTER DIALECT-P NI ⲻ + 0x2CBD, # COPTIC SMALL LETTER CRYPTOGRAMMIC NI ⲽ + 0x2CBF, # COPTIC SMALL LETTER OLD COPTIC OOU ⲿ + 0x2CC1, # COPTIC SMALL LETTER SAMPI ⳁ + 0x2CC3, # COPTIC SMALL LETTER CROSSED SHEI ⳃ + 0x2CC5, # COPTIC SMALL LETTER OLD COPTIC SHEI ⳅ + 0x2CC7, # COPTIC SMALL LETTER OLD COPTIC ESH ⳇ + 0x2CC9, # COPTIC SMALL LETTER AKHMIMIC KHEI ⳉ + 0x2CCB, # COPTIC SMALL LETTER DIALECT-P HORI ⳋ + 0x2CCD, # COPTIC SMALL LETTER OLD COPTIC HORI ⳍ + 0x2CCF, # COPTIC SMALL LETTER OLD COPTIC HA ⳏ + 0x2CD1, # COPTIC SMALL LETTER L-SHAPED HA ⳑ + 0x2CD3, # COPTIC SMALL LETTER OLD COPTIC HEI ⳓ + 0x2CD5, # COPTIC SMALL LETTER OLD COPTIC HAT ⳕ + 0x2CD7, # COPTIC SMALL LETTER OLD COPTIC GANGIA ⳗ + 0x2CD9, # COPTIC SMALL LETTER OLD COPTIC DJA ⳙ + 0x2CDB, # COPTIC SMALL LETTER OLD COPTIC SHIMA ⳛ + 0x2CDD, # COPTIC SMALL LETTER OLD NUBIAN SHIMA ⳝ + 0x2CDF, # COPTIC SMALL LETTER OLD NUBIAN NGI ⳟ + 0x2CE1, # COPTIC SMALL LETTER OLD NUBIAN NYI ⳡ + 0x2CE3, # COPTIC SMALL LETTER OLD NUBIAN WAU ⳣ + 0x2CEC, # COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI ⳬ + 0x2CEE, # COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA ⳮ + 0x2CF3, # COPTIC SMALL LETTER BOHAIRIC KHEI ⳳ + 0x2D00, # GEORGIAN SMALL LETTER AN ⴀ + 0x2D01, # GEORGIAN SMALL LETTER BAN ⴁ + 0x2D02, # GEORGIAN SMALL LETTER GAN ⴂ + 0x2D03, # GEORGIAN SMALL LETTER DON ⴃ + 0x2D04, # GEORGIAN SMALL LETTER EN ⴄ + 0x2D05, # GEORGIAN SMALL LETTER VIN ⴅ + 0x2D06, # GEORGIAN SMALL LETTER ZEN ⴆ + 0x2D07, # GEORGIAN SMALL LETTER TAN ⴇ + 0x2D08, # GEORGIAN SMALL LETTER IN ⴈ + 0x2D09, # GEORGIAN SMALL LETTER KAN ⴉ + 0x2D0A, # GEORGIAN SMALL LETTER LAS ⴊ + 0x2D0B, # GEORGIAN SMALL LETTER MAN ⴋ + 0x2D0C, # GEORGIAN SMALL LETTER NAR ⴌ + 0x2D0D, # GEORGIAN SMALL LETTER ON ⴍ + 0x2D0E, # GEORGIAN SMALL LETTER PAR ⴎ + 0x2D0F, # GEORGIAN SMALL LETTER ZHAR ⴏ + 0x2D10, # GEORGIAN SMALL LETTER RAE ⴐ + 0x2D11, # GEORGIAN SMALL LETTER SAN ⴑ + 0x2D12, # GEORGIAN SMALL LETTER TAR ⴒ + 0x2D13, # GEORGIAN SMALL LETTER UN ⴓ + 0x2D14, # GEORGIAN SMALL LETTER PHAR ⴔ + 0x2D15, # GEORGIAN SMALL LETTER KHAR ⴕ + 0x2D16, # GEORGIAN SMALL LETTER GHAN ⴖ + 0x2D17, # GEORGIAN SMALL LETTER QAR ⴗ + 0x2D18, # GEORGIAN SMALL LETTER SHIN ⴘ + 0x2D19, # GEORGIAN SMALL LETTER CHIN ⴙ + 0x2D1A, # GEORGIAN SMALL LETTER CAN ⴚ + 0x2D1B, # GEORGIAN SMALL LETTER JIL ⴛ + 0x2D1C, # GEORGIAN SMALL LETTER CIL ⴜ + 0x2D1D, # GEORGIAN SMALL LETTER CHAR ⴝ + 0x2D1E, # GEORGIAN SMALL LETTER XAN ⴞ + 0x2D1F, # GEORGIAN SMALL LETTER JHAN ⴟ + 0x2D20, # GEORGIAN SMALL LETTER HAE ⴠ + 0x2D21, # GEORGIAN SMALL LETTER HE ⴡ + 0x2D22, # GEORGIAN SMALL LETTER HIE ⴢ + 0x2D23, # GEORGIAN SMALL LETTER WE ⴣ + 0x2D24, # GEORGIAN SMALL LETTER HAR ⴤ + 0x2D25, # GEORGIAN SMALL LETTER HOE ⴥ + 0x2D27, # GEORGIAN SMALL LETTER YN ⴧ + 0x2D2D, # GEORGIAN SMALL LETTER AEN ⴭ + 0xA641, # CYRILLIC SMALL LETTER ZEMLYA ꙁ + 0xA643, # CYRILLIC SMALL LETTER DZELO ꙃ + 0xA645, # CYRILLIC SMALL LETTER REVERSED DZE ꙅ + 0xA647, # CYRILLIC SMALL LETTER IOTA ꙇ + 0xA649, # CYRILLIC SMALL LETTER DJERV ꙉ + 0xA64B, # CYRILLIC SMALL LETTER MONOGRAPH UK ꙋ + 0xA64D, # CYRILLIC SMALL LETTER BROAD OMEGA ꙍ + 0xA64F, # CYRILLIC SMALL LETTER NEUTRAL YER ꙏ + 0xA651, # CYRILLIC SMALL LETTER YERU WITH BACK YER ꙑ + 0xA653, # CYRILLIC SMALL LETTER IOTIFIED YAT ꙓ + 0xA655, # CYRILLIC SMALL LETTER REVERSED YU ꙕ + 0xA657, # CYRILLIC SMALL LETTER IOTIFIED A ꙗ + 0xA659, # CYRILLIC SMALL LETTER CLOSED LITTLE YUS ꙙ + 0xA65B, # CYRILLIC SMALL LETTER BLENDED YUS ꙛ + 0xA65D, # CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS ꙝ + 0xA65F, # CYRILLIC SMALL LETTER YN ꙟ + 0xA661, # CYRILLIC SMALL LETTER REVERSED TSE ꙡ + 0xA663, # CYRILLIC SMALL LETTER SOFT DE ꙣ + 0xA665, # CYRILLIC SMALL LETTER SOFT EL ꙥ + 0xA667, # CYRILLIC SMALL LETTER SOFT EM ꙧ + 0xA669, # CYRILLIC SMALL LETTER MONOCULAR O ꙩ + 0xA66B, # CYRILLIC SMALL LETTER BINOCULAR O ꙫ + 0xA66D, # CYRILLIC SMALL LETTER DOUBLE MONOCULAR O ꙭ + 0xA681, # CYRILLIC SMALL LETTER DWE ꚁ + 0xA683, # CYRILLIC SMALL LETTER DZWE ꚃ + 0xA685, # CYRILLIC SMALL LETTER ZHWE ꚅ + 0xA687, # CYRILLIC SMALL LETTER CCHE ꚇ + 0xA689, # CYRILLIC SMALL LETTER DZZE ꚉ + 0xA68B, # CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK ꚋ + 0xA68D, # CYRILLIC SMALL LETTER TWE ꚍ + 0xA68F, # CYRILLIC SMALL LETTER TSWE ꚏ + 0xA691, # CYRILLIC SMALL LETTER TSSE ꚑ + 0xA693, # CYRILLIC SMALL LETTER TCHE ꚓ + 0xA695, # CYRILLIC SMALL LETTER HWE ꚕ + 0xA697, # CYRILLIC SMALL LETTER SHWE ꚗ + 0xA699, # CYRILLIC SMALL LETTER DOUBLE O ꚙ + 0xA69B, # CYRILLIC SMALL LETTER CROSSED O ꚛ + 0xA723, # LATIN SMALL LETTER EGYPTOLOGICAL ALEF ꜣ + 0xA725, # LATIN SMALL LETTER EGYPTOLOGICAL AIN ꜥ + 0xA727, # LATIN SMALL LETTER HENG ꜧ + 0xA729, # LATIN SMALL LETTER TZ ꜩ + 0xA72B, # LATIN SMALL LETTER TRESILLO ꜫ + 0xA72D, # LATIN SMALL LETTER CUATRILLO ꜭ + 0xA72F, # LATIN SMALL LETTER CUATRILLO WITH COMMA ꜯ + 0xA733, # LATIN SMALL LETTER AA ꜳ + 0xA735, # LATIN SMALL LETTER AO ꜵ + 0xA737, # LATIN SMALL LETTER AU ꜷ + 0xA739, # LATIN SMALL LETTER AV ꜹ + 0xA73B, # LATIN SMALL LETTER AV WITH HORIZONTAL BAR ꜻ + 0xA73D, # LATIN SMALL LETTER AY ꜽ + 0xA73F, # LATIN SMALL LETTER REVERSED C WITH DOT ꜿ + 0xA741, # LATIN SMALL LETTER K WITH STROKE ꝁ + 0xA743, # LATIN SMALL LETTER K WITH DIAGONAL STROKE ꝃ + 0xA745, # LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE ꝅ + 0xA747, # LATIN SMALL LETTER BROKEN L ꝇ + 0xA749, # LATIN SMALL LETTER L WITH HIGH STROKE ꝉ + 0xA74B, # LATIN SMALL LETTER O WITH LONG STROKE OVERLAY ꝋ + 0xA74D, # LATIN SMALL LETTER O WITH LOOP ꝍ + 0xA74F, # LATIN SMALL LETTER OO ꝏ + 0xA751, # LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER ꝑ + 0xA753, # LATIN SMALL LETTER P WITH FLOURISH ꝓ + 0xA755, # LATIN SMALL LETTER P WITH SQUIRREL TAIL ꝕ + 0xA757, # LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER ꝗ + 0xA759, # LATIN SMALL LETTER Q WITH DIAGONAL STROKE ꝙ + 0xA75B, # LATIN SMALL LETTER R ROTUNDA ꝛ + 0xA75D, # LATIN SMALL LETTER RUM ROTUNDA ꝝ + 0xA75F, # LATIN SMALL LETTER V WITH DIAGONAL STROKE ꝟ + 0xA761, # LATIN SMALL LETTER VY ꝡ + 0xA763, # LATIN SMALL LETTER VISIGOTHIC Z ꝣ + 0xA765, # LATIN SMALL LETTER THORN WITH STROKE ꝥ + 0xA767, # LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER ꝧ + 0xA769, # LATIN SMALL LETTER VEND ꝩ + 0xA76B, # LATIN SMALL LETTER ET ꝫ + 0xA76D, # LATIN SMALL LETTER IS ꝭ + 0xA76F, # LATIN SMALL LETTER CON ꝯ + 0xA77A, # LATIN SMALL LETTER INSULAR D ꝺ + 0xA77C, # LATIN SMALL LETTER INSULAR F ꝼ + 0xA77F, # LATIN SMALL LETTER TURNED INSULAR G ꝿ + 0xA781, # LATIN SMALL LETTER TURNED L ꞁ + 0xA783, # LATIN SMALL LETTER INSULAR R ꞃ + 0xA785, # LATIN SMALL LETTER INSULAR S ꞅ + 0xA787, # LATIN SMALL LETTER INSULAR T ꞇ + 0xA78C, # LATIN SMALL LETTER SALTILLO ꞌ + 0xA791, # LATIN SMALL LETTER N WITH DESCENDER ꞑ + 0xA793, # LATIN SMALL LETTER C WITH BAR ꞓ + 0xA794, # LATIN SMALL LETTER C WITH PALATAL HOOK ꞔ + 0xA797, # LATIN SMALL LETTER B WITH FLOURISH ꞗ + 0xA799, # LATIN SMALL LETTER F WITH STROKE ꞙ + 0xA79B, # LATIN SMALL LETTER VOLAPUK AE ꞛ + 0xA79D, # LATIN SMALL LETTER VOLAPUK OE ꞝ + 0xA79F, # LATIN SMALL LETTER VOLAPUK UE ꞟ + 0xA7A1, # LATIN SMALL LETTER G WITH OBLIQUE STROKE ꞡ + 0xA7A3, # LATIN SMALL LETTER K WITH OBLIQUE STROKE ꞣ + 0xA7A5, # LATIN SMALL LETTER N WITH OBLIQUE STROKE ꞥ + 0xA7A7, # LATIN SMALL LETTER R WITH OBLIQUE STROKE ꞧ + 0xA7A9, # LATIN SMALL LETTER S WITH OBLIQUE STROKE ꞩ + 0xA7B5, # LATIN SMALL LETTER BETA ꞵ + 0xA7B7, # LATIN SMALL LETTER OMEGA ꞷ + 0xA7B9, # LATIN SMALL LETTER U WITH STROKE ꞹ + 0xA7BB, # LATIN SMALL LETTER GLOTTAL A ꞻ + 0xA7BD, # LATIN SMALL LETTER GLOTTAL I ꞽ + 0xA7BF, # LATIN SMALL LETTER GLOTTAL U ꞿ + 0xA7C1, # LATIN SMALL LETTER OLD POLISH O ꟁ + 0xA7C3, # LATIN SMALL LETTER ANGLICANA W ꟃ + 0xA7C8, # LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY ꟈ + 0xA7CA, # LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY ꟊ + 0xA7D1, # LATIN SMALL LETTER CLOSED INSULAR G ꟑ + 0xA7D7, # LATIN SMALL LETTER MIDDLE SCOTS S ꟗ + 0xA7D9, # LATIN SMALL LETTER SIGMOID S ꟙ + 0xA7F6, # LATIN SMALL LETTER REVERSED HALF H ꟶ + 0xAB53, # LATIN SMALL LETTER CHI ꭓ + 0xAB70, # CHEROKEE SMALL LETTER A ꭰ + 0xAB71, # CHEROKEE SMALL LETTER E ꭱ + 0xAB72, # CHEROKEE SMALL LETTER I ꭲ + 0xAB73, # CHEROKEE SMALL LETTER O ꭳ + 0xAB74, # CHEROKEE SMALL LETTER U ꭴ + 0xAB75, # CHEROKEE SMALL LETTER V ꭵ + 0xAB76, # CHEROKEE SMALL LETTER GA ꭶ + 0xAB77, # CHEROKEE SMALL LETTER KA ꭷ + 0xAB78, # CHEROKEE SMALL LETTER GE ꭸ + 0xAB79, # CHEROKEE SMALL LETTER GI ꭹ + 0xAB7A, # CHEROKEE SMALL LETTER GO ꭺ + 0xAB7B, # CHEROKEE SMALL LETTER GU ꭻ + 0xAB7C, # CHEROKEE SMALL LETTER GV ꭼ + 0xAB7D, # CHEROKEE SMALL LETTER HA ꭽ + 0xAB7E, # CHEROKEE SMALL LETTER HE ꭾ + 0xAB7F, # CHEROKEE SMALL LETTER HI ꭿ + 0xAB80, # CHEROKEE SMALL LETTER HO ꮀ + 0xAB81, # CHEROKEE SMALL LETTER HU ꮁ + 0xAB82, # CHEROKEE SMALL LETTER HV ꮂ + 0xAB83, # CHEROKEE SMALL LETTER LA ꮃ + 0xAB84, # CHEROKEE SMALL LETTER LE ꮄ + 0xAB85, # CHEROKEE SMALL LETTER LI ꮅ + 0xAB86, # CHEROKEE SMALL LETTER LO ꮆ + 0xAB87, # CHEROKEE SMALL LETTER LU ꮇ + 0xAB88, # CHEROKEE SMALL LETTER LV ꮈ + 0xAB89, # CHEROKEE SMALL LETTER MA ꮉ + 0xAB8A, # CHEROKEE SMALL LETTER ME ꮊ + 0xAB8B, # CHEROKEE SMALL LETTER MI ꮋ + 0xAB8C, # CHEROKEE SMALL LETTER MO ꮌ + 0xAB8D, # CHEROKEE SMALL LETTER MU ꮍ + 0xAB8E, # CHEROKEE SMALL LETTER NA ꮎ + 0xAB8F, # CHEROKEE SMALL LETTER HNA ꮏ + 0xAB90, # CHEROKEE SMALL LETTER NAH ꮐ + 0xAB91, # CHEROKEE SMALL LETTER NE ꮑ + 0xAB92, # CHEROKEE SMALL LETTER NI ꮒ + 0xAB93, # CHEROKEE SMALL LETTER NO ꮓ + 0xAB94, # CHEROKEE SMALL LETTER NU ꮔ + 0xAB95, # CHEROKEE SMALL LETTER NV ꮕ + 0xAB96, # CHEROKEE SMALL LETTER QUA ꮖ + 0xAB97, # CHEROKEE SMALL LETTER QUE ꮗ + 0xAB98, # CHEROKEE SMALL LETTER QUI ꮘ + 0xAB99, # CHEROKEE SMALL LETTER QUO ꮙ + 0xAB9A, # CHEROKEE SMALL LETTER QUU ꮚ + 0xAB9B, # CHEROKEE SMALL LETTER QUV ꮛ + 0xAB9C, # CHEROKEE SMALL LETTER SA ꮜ + 0xAB9D, # CHEROKEE SMALL LETTER S ꮝ + 0xAB9E, # CHEROKEE SMALL LETTER SE ꮞ + 0xAB9F, # CHEROKEE SMALL LETTER SI ꮟ + 0xABA0, # CHEROKEE SMALL LETTER SO ꮠ + 0xABA1, # CHEROKEE SMALL LETTER SU ꮡ + 0xABA2, # CHEROKEE SMALL LETTER SV ꮢ + 0xABA3, # CHEROKEE SMALL LETTER DA ꮣ + 0xABA4, # CHEROKEE SMALL LETTER TA ꮤ + 0xABA5, # CHEROKEE SMALL LETTER DE ꮥ + 0xABA6, # CHEROKEE SMALL LETTER TE ꮦ + 0xABA7, # CHEROKEE SMALL LETTER DI ꮧ + 0xABA8, # CHEROKEE SMALL LETTER TI ꮨ + 0xABA9, # CHEROKEE SMALL LETTER DO ꮩ + 0xABAA, # CHEROKEE SMALL LETTER DU ꮪ + 0xABAB, # CHEROKEE SMALL LETTER DV ꮫ + 0xABAC, # CHEROKEE SMALL LETTER DLA ꮬ + 0xABAD, # CHEROKEE SMALL LETTER TLA ꮭ + 0xABAE, # CHEROKEE SMALL LETTER TLE ꮮ + 0xABAF, # CHEROKEE SMALL LETTER TLI ꮯ + 0xABB0, # CHEROKEE SMALL LETTER TLO ꮰ + 0xABB1, # CHEROKEE SMALL LETTER TLU ꮱ + 0xABB2, # CHEROKEE SMALL LETTER TLV ꮲ + 0xABB3, # CHEROKEE SMALL LETTER TSA ꮳ + 0xABB4, # CHEROKEE SMALL LETTER TSE ꮴ + 0xABB5, # CHEROKEE SMALL LETTER TSI ꮵ + 0xABB6, # CHEROKEE SMALL LETTER TSO ꮶ + 0xABB7, # CHEROKEE SMALL LETTER TSU ꮷ + 0xABB8, # CHEROKEE SMALL LETTER TSV ꮸ + 0xABB9, # CHEROKEE SMALL LETTER WA ꮹ + 0xABBA, # CHEROKEE SMALL LETTER WE ꮺ + 0xABBB, # CHEROKEE SMALL LETTER WI ꮻ + 0xABBC, # CHEROKEE SMALL LETTER WO ꮼ + 0xABBD, # CHEROKEE SMALL LETTER WU ꮽ + 0xABBE, # CHEROKEE SMALL LETTER WV ꮾ + 0xABBF, # CHEROKEE SMALL LETTER YA ꮿ + 0xFF41, # FULLWIDTH LATIN SMALL LETTER A a + 0xFF42, # FULLWIDTH LATIN SMALL LETTER B b + 0xFF43, # FULLWIDTH LATIN SMALL LETTER C c + 0xFF44, # FULLWIDTH LATIN SMALL LETTER D d + 0xFF45, # FULLWIDTH LATIN SMALL LETTER E e + 0xFF46, # FULLWIDTH LATIN SMALL LETTER F f + 0xFF47, # FULLWIDTH LATIN SMALL LETTER G g + 0xFF48, # FULLWIDTH LATIN SMALL LETTER H h + 0xFF49, # FULLWIDTH LATIN SMALL LETTER I i + 0xFF4A, # FULLWIDTH LATIN SMALL LETTER J j + 0xFF4B, # FULLWIDTH LATIN SMALL LETTER K k + 0xFF4C, # FULLWIDTH LATIN SMALL LETTER L l + 0xFF4D, # FULLWIDTH LATIN SMALL LETTER M m + 0xFF4E, # FULLWIDTH LATIN SMALL LETTER N n + 0xFF4F, # FULLWIDTH LATIN SMALL LETTER O o + 0xFF50, # FULLWIDTH LATIN SMALL LETTER P p + 0xFF51, # FULLWIDTH LATIN SMALL LETTER Q q + 0xFF52, # FULLWIDTH LATIN SMALL LETTER R r + 0xFF53, # FULLWIDTH LATIN SMALL LETTER S s + 0xFF54, # FULLWIDTH LATIN SMALL LETTER T t + 0xFF55, # FULLWIDTH LATIN SMALL LETTER U u + 0xFF56, # FULLWIDTH LATIN SMALL LETTER V v + 0xFF57, # FULLWIDTH LATIN SMALL LETTER W w + 0xFF58, # FULLWIDTH LATIN SMALL LETTER X x + 0xFF59, # FULLWIDTH LATIN SMALL LETTER Y y + 0xFF5A, # FULLWIDTH LATIN SMALL LETTER Z z + 0x10428, # DESERET SMALL LETTER LONG I 𐐨 + 0x10429, # DESERET SMALL LETTER LONG E 𐐩 + 0x1042A, # DESERET SMALL LETTER LONG A 𐐪 + 0x1042B, # DESERET SMALL LETTER LONG AH 𐐫 + 0x1042C, # DESERET SMALL LETTER LONG O 𐐬 + 0x1042D, # DESERET SMALL LETTER LONG OO 𐐭 + 0x1042E, # DESERET SMALL LETTER SHORT I 𐐮 + 0x1042F, # DESERET SMALL LETTER SHORT E 𐐯 + 0x10430, # DESERET SMALL LETTER SHORT A 𐐰 + 0x10431, # DESERET SMALL LETTER SHORT AH 𐐱 + 0x10432, # DESERET SMALL LETTER SHORT O 𐐲 + 0x10433, # DESERET SMALL LETTER SHORT OO 𐐳 + 0x10434, # DESERET SMALL LETTER AY 𐐴 + 0x10435, # DESERET SMALL LETTER OW 𐐵 + 0x10436, # DESERET SMALL LETTER WU 𐐶 + 0x10437, # DESERET SMALL LETTER YEE 𐐷 + 0x10438, # DESERET SMALL LETTER H 𐐸 + 0x10439, # DESERET SMALL LETTER PEE 𐐹 + 0x1043A, # DESERET SMALL LETTER BEE 𐐺 + 0x1043B, # DESERET SMALL LETTER TEE 𐐻 + 0x1043C, # DESERET SMALL LETTER DEE 𐐼 + 0x1043D, # DESERET SMALL LETTER CHEE 𐐽 + 0x1043E, # DESERET SMALL LETTER JEE 𐐾 + 0x1043F, # DESERET SMALL LETTER KAY 𐐿 + 0x10440, # DESERET SMALL LETTER GAY 𐑀 + 0x10441, # DESERET SMALL LETTER EF 𐑁 + 0x10442, # DESERET SMALL LETTER VEE 𐑂 + 0x10443, # DESERET SMALL LETTER ETH 𐑃 + 0x10444, # DESERET SMALL LETTER THEE 𐑄 + 0x10445, # DESERET SMALL LETTER ES 𐑅 + 0x10446, # DESERET SMALL LETTER ZEE 𐑆 + 0x10447, # DESERET SMALL LETTER ESH 𐑇 + 0x10448, # DESERET SMALL LETTER ZHEE 𐑈 + 0x10449, # DESERET SMALL LETTER ER 𐑉 + 0x1044A, # DESERET SMALL LETTER EL 𐑊 + 0x1044B, # DESERET SMALL LETTER EM 𐑋 + 0x1044C, # DESERET SMALL LETTER EN 𐑌 + 0x1044D, # DESERET SMALL LETTER ENG 𐑍 + 0x1044E, # DESERET SMALL LETTER OI 𐑎 + 0x1044F, # DESERET SMALL LETTER EW 𐑏 + 0x104D8, # OSAGE SMALL LETTER A 𐓘 + 0x104D9, # OSAGE SMALL LETTER AI 𐓙 + 0x104DA, # OSAGE SMALL LETTER AIN 𐓚 + 0x104DB, # OSAGE SMALL LETTER AH 𐓛 + 0x104DC, # OSAGE SMALL LETTER BRA 𐓜 + 0x104DD, # OSAGE SMALL LETTER CHA 𐓝 + 0x104DE, # OSAGE SMALL LETTER EHCHA 𐓞 + 0x104DF, # OSAGE SMALL LETTER E 𐓟 + 0x104E0, # OSAGE SMALL LETTER EIN 𐓠 + 0x104E1, # OSAGE SMALL LETTER HA 𐓡 + 0x104E2, # OSAGE SMALL LETTER HYA 𐓢 + 0x104E3, # OSAGE SMALL LETTER I 𐓣 + 0x104E4, # OSAGE SMALL LETTER KA 𐓤 + 0x104E5, # OSAGE SMALL LETTER EHKA 𐓥 + 0x104E6, # OSAGE SMALL LETTER KYA 𐓦 + 0x104E7, # OSAGE SMALL LETTER LA 𐓧 + 0x104E8, # OSAGE SMALL LETTER MA 𐓨 + 0x104E9, # OSAGE SMALL LETTER NA 𐓩 + 0x104EA, # OSAGE SMALL LETTER O 𐓪 + 0x104EB, # OSAGE SMALL LETTER OIN 𐓫 + 0x104EC, # OSAGE SMALL LETTER PA 𐓬 + 0x104ED, # OSAGE SMALL LETTER EHPA 𐓭 + 0x104EE, # OSAGE SMALL LETTER SA 𐓮 + 0x104EF, # OSAGE SMALL LETTER SHA 𐓯 + 0x104F0, # OSAGE SMALL LETTER TA 𐓰 + 0x104F1, # OSAGE SMALL LETTER EHTA 𐓱 + 0x104F2, # OSAGE SMALL LETTER TSA 𐓲 + 0x104F3, # OSAGE SMALL LETTER EHTSA 𐓳 + 0x104F4, # OSAGE SMALL LETTER TSHA 𐓴 + 0x104F5, # OSAGE SMALL LETTER DHA 𐓵 + 0x104F6, # OSAGE SMALL LETTER U 𐓶 + 0x104F7, # OSAGE SMALL LETTER WA 𐓷 + 0x104F8, # OSAGE SMALL LETTER KHA 𐓸 + 0x104F9, # OSAGE SMALL LETTER GHA 𐓹 + 0x104FA, # OSAGE SMALL LETTER ZA 𐓺 + 0x104FB, # OSAGE SMALL LETTER ZHA 𐓻 + 0x10597, # VITHKUQI SMALL LETTER A 𐖗 + 0x10598, # VITHKUQI SMALL LETTER BBE 𐖘 + 0x10599, # VITHKUQI SMALL LETTER BE 𐖙 + 0x1059A, # VITHKUQI SMALL LETTER CE 𐖚 + 0x1059B, # VITHKUQI SMALL LETTER CHE 𐖛 + 0x1059C, # VITHKUQI SMALL LETTER DE 𐖜 + 0x1059D, # VITHKUQI SMALL LETTER DHE 𐖝 + 0x1059E, # VITHKUQI SMALL LETTER EI 𐖞 + 0x1059F, # VITHKUQI SMALL LETTER E 𐖟 + 0x105A0, # VITHKUQI SMALL LETTER FE 𐖠 + 0x105A1, # VITHKUQI SMALL LETTER GA 𐖡 + 0x105A3, # VITHKUQI SMALL LETTER HA 𐖣 + 0x105A4, # VITHKUQI SMALL LETTER HHA 𐖤 + 0x105A5, # VITHKUQI SMALL LETTER I 𐖥 + 0x105A6, # VITHKUQI SMALL LETTER IJE 𐖦 + 0x105A7, # VITHKUQI SMALL LETTER JE 𐖧 + 0x105A8, # VITHKUQI SMALL LETTER KA 𐖨 + 0x105A9, # VITHKUQI SMALL LETTER LA 𐖩 + 0x105AA, # VITHKUQI SMALL LETTER LLA 𐖪 + 0x105AB, # VITHKUQI SMALL LETTER ME 𐖫 + 0x105AC, # VITHKUQI SMALL LETTER NE 𐖬 + 0x105AD, # VITHKUQI SMALL LETTER NJE 𐖭 + 0x105AE, # VITHKUQI SMALL LETTER O 𐖮 + 0x105AF, # VITHKUQI SMALL LETTER PE 𐖯 + 0x105B0, # VITHKUQI SMALL LETTER QA 𐖰 + 0x105B1, # VITHKUQI SMALL LETTER RE 𐖱 + 0x105B3, # VITHKUQI SMALL LETTER SE 𐖳 + 0x105B4, # VITHKUQI SMALL LETTER SHE 𐖴 + 0x105B5, # VITHKUQI SMALL LETTER TE 𐖵 + 0x105B6, # VITHKUQI SMALL LETTER THE 𐖶 + 0x105B7, # VITHKUQI SMALL LETTER U 𐖷 + 0x105B8, # VITHKUQI SMALL LETTER VE 𐖸 + 0x105B9, # VITHKUQI SMALL LETTER XE 𐖹 + 0x105BB, # VITHKUQI SMALL LETTER Y 𐖻 + 0x105BC, # VITHKUQI SMALL LETTER ZE 𐖼 + 0x10CC0, # OLD HUNGARIAN SMALL LETTER A 𐳀 + 0x10CC1, # OLD HUNGARIAN SMALL LETTER AA 𐳁 + 0x10CC2, # OLD HUNGARIAN SMALL LETTER EB 𐳂 + 0x10CC3, # OLD HUNGARIAN SMALL LETTER AMB 𐳃 + 0x10CC4, # OLD HUNGARIAN SMALL LETTER EC 𐳄 + 0x10CC5, # OLD HUNGARIAN SMALL LETTER ENC 𐳅 + 0x10CC6, # OLD HUNGARIAN SMALL LETTER ECS 𐳆 + 0x10CC7, # OLD HUNGARIAN SMALL LETTER ED 𐳇 + 0x10CC8, # OLD HUNGARIAN SMALL LETTER AND 𐳈 + 0x10CC9, # OLD HUNGARIAN SMALL LETTER E 𐳉 + 0x10CCA, # OLD HUNGARIAN SMALL LETTER CLOSE E 𐳊 + 0x10CCB, # OLD HUNGARIAN SMALL LETTER EE 𐳋 + 0x10CCC, # OLD HUNGARIAN SMALL LETTER EF 𐳌 + 0x10CCD, # OLD HUNGARIAN SMALL LETTER EG 𐳍 + 0x10CCE, # OLD HUNGARIAN SMALL LETTER EGY 𐳎 + 0x10CCF, # OLD HUNGARIAN SMALL LETTER EH 𐳏 + 0x10CD0, # OLD HUNGARIAN SMALL LETTER I 𐳐 + 0x10CD1, # OLD HUNGARIAN SMALL LETTER II 𐳑 + 0x10CD2, # OLD HUNGARIAN SMALL LETTER EJ 𐳒 + 0x10CD3, # OLD HUNGARIAN SMALL LETTER EK 𐳓 + 0x10CD4, # OLD HUNGARIAN SMALL LETTER AK 𐳔 + 0x10CD5, # OLD HUNGARIAN SMALL LETTER UNK 𐳕 + 0x10CD6, # OLD HUNGARIAN SMALL LETTER EL 𐳖 + 0x10CD7, # OLD HUNGARIAN SMALL LETTER ELY 𐳗 + 0x10CD8, # OLD HUNGARIAN SMALL LETTER EM 𐳘 + 0x10CD9, # OLD HUNGARIAN SMALL LETTER EN 𐳙 + 0x10CDA, # OLD HUNGARIAN SMALL LETTER ENY 𐳚 + 0x10CDB, # OLD HUNGARIAN SMALL LETTER O 𐳛 + 0x10CDC, # OLD HUNGARIAN SMALL LETTER OO 𐳜 + 0x10CDD, # OLD HUNGARIAN SMALL LETTER NIKOLSBURG OE 𐳝 + 0x10CDE, # OLD HUNGARIAN SMALL LETTER RUDIMENTA OE 𐳞 + 0x10CDF, # OLD HUNGARIAN SMALL LETTER OEE 𐳟 + 0x10CE0, # OLD HUNGARIAN SMALL LETTER EP 𐳠 + 0x10CE1, # OLD HUNGARIAN SMALL LETTER EMP 𐳡 + 0x10CE2, # OLD HUNGARIAN SMALL LETTER ER 𐳢 + 0x10CE3, # OLD HUNGARIAN SMALL LETTER SHORT ER 𐳣 + 0x10CE4, # OLD HUNGARIAN SMALL LETTER ES 𐳤 + 0x10CE5, # OLD HUNGARIAN SMALL LETTER ESZ 𐳥 + 0x10CE6, # OLD HUNGARIAN SMALL LETTER ET 𐳦 + 0x10CE7, # OLD HUNGARIAN SMALL LETTER ENT 𐳧 + 0x10CE8, # OLD HUNGARIAN SMALL LETTER ETY 𐳨 + 0x10CE9, # OLD HUNGARIAN SMALL LETTER ECH 𐳩 + 0x10CEA, # OLD HUNGARIAN SMALL LETTER U 𐳪 + 0x10CEB, # OLD HUNGARIAN SMALL LETTER UU 𐳫 + 0x10CEC, # OLD HUNGARIAN SMALL LETTER NIKOLSBURG UE 𐳬 + 0x10CED, # OLD HUNGARIAN SMALL LETTER RUDIMENTA UE 𐳭 + 0x10CEE, # OLD HUNGARIAN SMALL LETTER EV 𐳮 + 0x10CEF, # OLD HUNGARIAN SMALL LETTER EZ 𐳯 + 0x10CF0, # OLD HUNGARIAN SMALL LETTER EZS 𐳰 + 0x10CF1, # OLD HUNGARIAN SMALL LETTER ENT-SHAPED SIGN 𐳱 + 0x10CF2, # OLD HUNGARIAN SMALL LETTER US 𐳲 + 0x118C0, # WARANG CITI SMALL LETTER NGAA 𑣀 + 0x118C1, # WARANG CITI SMALL LETTER A 𑣁 + 0x118C2, # WARANG CITI SMALL LETTER WI 𑣂 + 0x118C3, # WARANG CITI SMALL LETTER YU 𑣃 + 0x118C4, # WARANG CITI SMALL LETTER YA 𑣄 + 0x118C5, # WARANG CITI SMALL LETTER YO 𑣅 + 0x118C6, # WARANG CITI SMALL LETTER II 𑣆 + 0x118C7, # WARANG CITI SMALL LETTER UU 𑣇 + 0x118C8, # WARANG CITI SMALL LETTER E 𑣈 + 0x118C9, # WARANG CITI SMALL LETTER O 𑣉 + 0x118CA, # WARANG CITI SMALL LETTER ANG 𑣊 + 0x118CB, # WARANG CITI SMALL LETTER GA 𑣋 + 0x118CC, # WARANG CITI SMALL LETTER KO 𑣌 + 0x118CD, # WARANG CITI SMALL LETTER ENY 𑣍 + 0x118CE, # WARANG CITI SMALL LETTER YUJ 𑣎 + 0x118CF, # WARANG CITI SMALL LETTER UC 𑣏 + 0x118D0, # WARANG CITI SMALL LETTER ENN 𑣐 + 0x118D1, # WARANG CITI SMALL LETTER ODD 𑣑 + 0x118D2, # WARANG CITI SMALL LETTER TTE 𑣒 + 0x118D3, # WARANG CITI SMALL LETTER NUNG 𑣓 + 0x118D4, # WARANG CITI SMALL LETTER DA 𑣔 + 0x118D5, # WARANG CITI SMALL LETTER AT 𑣕 + 0x118D6, # WARANG CITI SMALL LETTER AM 𑣖 + 0x118D7, # WARANG CITI SMALL LETTER BU 𑣗 + 0x118D8, # WARANG CITI SMALL LETTER PU 𑣘 + 0x118D9, # WARANG CITI SMALL LETTER HIYO 𑣙 + 0x118DA, # WARANG CITI SMALL LETTER HOLO 𑣚 + 0x118DB, # WARANG CITI SMALL LETTER HORR 𑣛 + 0x118DC, # WARANG CITI SMALL LETTER HAR 𑣜 + 0x118DD, # WARANG CITI SMALL LETTER SSUU 𑣝 + 0x118DE, # WARANG CITI SMALL LETTER SII 𑣞 + 0x118DF, # WARANG CITI SMALL LETTER VIYO 𑣟 + 0x16E60, # MEDEFAIDRIN SMALL LETTER M 𖹠 + 0x16E61, # MEDEFAIDRIN SMALL LETTER S 𖹡 + 0x16E62, # MEDEFAIDRIN SMALL LETTER V 𖹢 + 0x16E63, # MEDEFAIDRIN SMALL LETTER W 𖹣 + 0x16E64, # MEDEFAIDRIN SMALL LETTER ATIU 𖹤 + 0x16E65, # MEDEFAIDRIN SMALL LETTER Z 𖹥 + 0x16E66, # MEDEFAIDRIN SMALL LETTER KP 𖹦 + 0x16E67, # MEDEFAIDRIN SMALL LETTER P 𖹧 + 0x16E68, # MEDEFAIDRIN SMALL LETTER T 𖹨 + 0x16E69, # MEDEFAIDRIN SMALL LETTER G 𖹩 + 0x16E6A, # MEDEFAIDRIN SMALL LETTER F 𖹪 + 0x16E6B, # MEDEFAIDRIN SMALL LETTER I 𖹫 + 0x16E6C, # MEDEFAIDRIN SMALL LETTER K 𖹬 + 0x16E6D, # MEDEFAIDRIN SMALL LETTER A 𖹭 + 0x16E6E, # MEDEFAIDRIN SMALL LETTER J 𖹮 + 0x16E6F, # MEDEFAIDRIN SMALL LETTER E 𖹯 + 0x16E70, # MEDEFAIDRIN SMALL LETTER B 𖹰 + 0x16E71, # MEDEFAIDRIN SMALL LETTER C 𖹱 + 0x16E72, # MEDEFAIDRIN SMALL LETTER U 𖹲 + 0x16E73, # MEDEFAIDRIN SMALL LETTER YU 𖹳 + 0x16E74, # MEDEFAIDRIN SMALL LETTER L 𖹴 + 0x16E75, # MEDEFAIDRIN SMALL LETTER Q 𖹵 + 0x16E76, # MEDEFAIDRIN SMALL LETTER HP 𖹶 + 0x16E77, # MEDEFAIDRIN SMALL LETTER NY 𖹷 + 0x16E78, # MEDEFAIDRIN SMALL LETTER X 𖹸 + 0x16E79, # MEDEFAIDRIN SMALL LETTER D 𖹹 + 0x16E7A, # MEDEFAIDRIN SMALL LETTER OE 𖹺 + 0x16E7B, # MEDEFAIDRIN SMALL LETTER N 𖹻 + 0x16E7C, # MEDEFAIDRIN SMALL LETTER R 𖹼 + 0x16E7D, # MEDEFAIDRIN SMALL LETTER O 𖹽 + 0x16E7E, # MEDEFAIDRIN SMALL LETTER AI 𖹾 + 0x16E7F, # MEDEFAIDRIN SMALL LETTER Y 𖹿 + 0x1E922, # ADLAM SMALL LETTER ALIF 𞤢 + 0x1E923, # ADLAM SMALL LETTER DAALI 𞤣 + 0x1E924, # ADLAM SMALL LETTER LAAM 𞤤 + 0x1E925, # ADLAM SMALL LETTER MIIM 𞤥 + 0x1E926, # ADLAM SMALL LETTER BA 𞤦 + 0x1E927, # ADLAM SMALL LETTER SINNYIIYHE 𞤧 + 0x1E928, # ADLAM SMALL LETTER PE 𞤨 + 0x1E929, # ADLAM SMALL LETTER BHE 𞤩 + 0x1E92A, # ADLAM SMALL LETTER RA 𞤪 + 0x1E92B, # ADLAM SMALL LETTER E 𞤫 + 0x1E92C, # ADLAM SMALL LETTER FA 𞤬 + 0x1E92D, # ADLAM SMALL LETTER I 𞤭 + 0x1E92E, # ADLAM SMALL LETTER O 𞤮 + 0x1E92F, # ADLAM SMALL LETTER DHA 𞤯 + 0x1E930, # ADLAM SMALL LETTER YHE 𞤰 + 0x1E931, # ADLAM SMALL LETTER WAW 𞤱 + 0x1E932, # ADLAM SMALL LETTER NUN 𞤲 + 0x1E933, # ADLAM SMALL LETTER KAF 𞤳 + 0x1E934, # ADLAM SMALL LETTER YA 𞤴 + 0x1E935, # ADLAM SMALL LETTER U 𞤵 + 0x1E936, # ADLAM SMALL LETTER JIIM 𞤶 + 0x1E937, # ADLAM SMALL LETTER CHI 𞤷 + 0x1E938, # ADLAM SMALL LETTER HA 𞤸 + 0x1E939, # ADLAM SMALL LETTER QAAF 𞤹 + 0x1E93A, # ADLAM SMALL LETTER GA 𞤺 + 0x1E93B, # ADLAM SMALL LETTER NYA 𞤻 + 0x1E93C, # ADLAM SMALL LETTER TU 𞤼 + 0x1E93D, # ADLAM SMALL LETTER NHA 𞤽 + 0x1E93E, # ADLAM SMALL LETTER VA 𞤾 + 0x1E93F, # ADLAM SMALL LETTER KHA 𞤿 + 0x1E940, # ADLAM SMALL LETTER GBE 𞥀 + 0x1E941, # ADLAM SMALL LETTER ZAL 𞥁 + 0x1E942, # ADLAM SMALL LETTER KPO 𞥂 + 0x1E943, # ADLAM SMALL LETTER SHA 𞥃 +) +alias has_lowercase_mapping = List[UInt32, hint_trivial_type=True]( + 0x0041, # LATIN CAPITAL LETTER A A + 0x0042, # LATIN CAPITAL LETTER B B + 0x0043, # LATIN CAPITAL LETTER C C + 0x0044, # LATIN CAPITAL LETTER D D + 0x0045, # LATIN CAPITAL LETTER E E + 0x0046, # LATIN CAPITAL LETTER F F + 0x0047, # LATIN CAPITAL LETTER G G + 0x0048, # LATIN CAPITAL LETTER H H + 0x0049, # LATIN CAPITAL LETTER I I + 0x004A, # LATIN CAPITAL LETTER J J + 0x004B, # LATIN CAPITAL LETTER K K + 0x004C, # LATIN CAPITAL LETTER L L + 0x004D, # LATIN CAPITAL LETTER M M + 0x004E, # LATIN CAPITAL LETTER N N + 0x004F, # LATIN CAPITAL LETTER O O + 0x0050, # LATIN CAPITAL LETTER P P + 0x0051, # LATIN CAPITAL LETTER Q Q + 0x0052, # LATIN CAPITAL LETTER R R + 0x0053, # LATIN CAPITAL LETTER S S + 0x0054, # LATIN CAPITAL LETTER T T + 0x0055, # LATIN CAPITAL LETTER U U + 0x0056, # LATIN CAPITAL LETTER V V + 0x0057, # LATIN CAPITAL LETTER W W + 0x0058, # LATIN CAPITAL LETTER X X + 0x0059, # LATIN CAPITAL LETTER Y Y + 0x005A, # LATIN CAPITAL LETTER Z Z + 0x00C0, # LATIN CAPITAL LETTER A WITH GRAVE À + 0x00C1, # LATIN CAPITAL LETTER A WITH ACUTE Á + 0x00C2, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX  + 0x00C3, # LATIN CAPITAL LETTER A WITH TILDE à + 0x00C4, # LATIN CAPITAL LETTER A WITH DIAERESIS Ä + 0x00C5, # LATIN CAPITAL LETTER A WITH RING ABOVE Å + 0x00C6, # LATIN CAPITAL LETTER AE Æ + 0x00C7, # LATIN CAPITAL LETTER C WITH CEDILLA Ç + 0x00C8, # LATIN CAPITAL LETTER E WITH GRAVE È + 0x00C9, # LATIN CAPITAL LETTER E WITH ACUTE É + 0x00CA, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX Ê + 0x00CB, # LATIN CAPITAL LETTER E WITH DIAERESIS Ë + 0x00CC, # LATIN CAPITAL LETTER I WITH GRAVE Ì + 0x00CD, # LATIN CAPITAL LETTER I WITH ACUTE Í + 0x00CE, # LATIN CAPITAL LETTER I WITH CIRCUMFLEX Î + 0x00CF, # LATIN CAPITAL LETTER I WITH DIAERESIS Ï + 0x00D0, # LATIN CAPITAL LETTER ETH Ð + 0x00D1, # LATIN CAPITAL LETTER N WITH TILDE Ñ + 0x00D2, # LATIN CAPITAL LETTER O WITH GRAVE Ò + 0x00D3, # LATIN CAPITAL LETTER O WITH ACUTE Ó + 0x00D4, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX Ô + 0x00D5, # LATIN CAPITAL LETTER O WITH TILDE Õ + 0x00D6, # LATIN CAPITAL LETTER O WITH DIAERESIS Ö + 0x00D8, # LATIN CAPITAL LETTER O WITH STROKE Ø + 0x00D9, # LATIN CAPITAL LETTER U WITH GRAVE Ù + 0x00DA, # LATIN CAPITAL LETTER U WITH ACUTE Ú + 0x00DB, # LATIN CAPITAL LETTER U WITH CIRCUMFLEX Û + 0x00DC, # LATIN CAPITAL LETTER U WITH DIAERESIS Ü + 0x00DD, # LATIN CAPITAL LETTER Y WITH ACUTE Ý + 0x00DE, # LATIN CAPITAL LETTER THORN Þ + 0x0100, # LATIN CAPITAL LETTER A WITH MACRON Ā + 0x0102, # LATIN CAPITAL LETTER A WITH BREVE Ă + 0x0104, # LATIN CAPITAL LETTER A WITH OGONEK Ą + 0x0106, # LATIN CAPITAL LETTER C WITH ACUTE Ć + 0x0108, # LATIN CAPITAL LETTER C WITH CIRCUMFLEX Ĉ + 0x010A, # LATIN CAPITAL LETTER C WITH DOT ABOVE Ċ + 0x010C, # LATIN CAPITAL LETTER C WITH CARON Č + 0x010E, # LATIN CAPITAL LETTER D WITH CARON Ď + 0x0110, # LATIN CAPITAL LETTER D WITH STROKE Đ + 0x0112, # LATIN CAPITAL LETTER E WITH MACRON Ē + 0x0114, # LATIN CAPITAL LETTER E WITH BREVE Ĕ + 0x0116, # LATIN CAPITAL LETTER E WITH DOT ABOVE Ė + 0x0118, # LATIN CAPITAL LETTER E WITH OGONEK Ę + 0x011A, # LATIN CAPITAL LETTER E WITH CARON Ě + 0x011C, # LATIN CAPITAL LETTER G WITH CIRCUMFLEX Ĝ + 0x011E, # LATIN CAPITAL LETTER G WITH BREVE Ğ + 0x0120, # LATIN CAPITAL LETTER G WITH DOT ABOVE Ġ + 0x0122, # LATIN CAPITAL LETTER G WITH CEDILLA Ģ + 0x0124, # LATIN CAPITAL LETTER H WITH CIRCUMFLEX Ĥ + 0x0126, # LATIN CAPITAL LETTER H WITH STROKE Ħ + 0x0128, # LATIN CAPITAL LETTER I WITH TILDE Ĩ + 0x012A, # LATIN CAPITAL LETTER I WITH MACRON Ī + 0x012C, # LATIN CAPITAL LETTER I WITH BREVE Ĭ + 0x012E, # LATIN CAPITAL LETTER I WITH OGONEK Į + 0x0130, # LATIN CAPITAL LETTER I WITH DOT ABOVE İ + 0x0132, # LATIN CAPITAL LIGATURE IJ IJ + 0x0134, # LATIN CAPITAL LETTER J WITH CIRCUMFLEX Ĵ + 0x0136, # LATIN CAPITAL LETTER K WITH CEDILLA Ķ + 0x0139, # LATIN CAPITAL LETTER L WITH ACUTE Ĺ + 0x013B, # LATIN CAPITAL LETTER L WITH CEDILLA Ļ + 0x013D, # LATIN CAPITAL LETTER L WITH CARON Ľ + 0x013F, # LATIN CAPITAL LETTER L WITH MIDDLE DOT Ŀ + 0x0141, # LATIN CAPITAL LETTER L WITH STROKE Ł + 0x0143, # LATIN CAPITAL LETTER N WITH ACUTE Ń + 0x0145, # LATIN CAPITAL LETTER N WITH CEDILLA Ņ + 0x0147, # LATIN CAPITAL LETTER N WITH CARON Ň + 0x014A, # LATIN CAPITAL LETTER ENG Ŋ + 0x014C, # LATIN CAPITAL LETTER O WITH MACRON Ō + 0x014E, # LATIN CAPITAL LETTER O WITH BREVE Ŏ + 0x0150, # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE Ő + 0x0152, # LATIN CAPITAL LIGATURE OE Œ + 0x0154, # LATIN CAPITAL LETTER R WITH ACUTE Ŕ + 0x0156, # LATIN CAPITAL LETTER R WITH CEDILLA Ŗ + 0x0158, # LATIN CAPITAL LETTER R WITH CARON Ř + 0x015A, # LATIN CAPITAL LETTER S WITH ACUTE Ś + 0x015C, # LATIN CAPITAL LETTER S WITH CIRCUMFLEX Ŝ + 0x015E, # LATIN CAPITAL LETTER S WITH CEDILLA Ş + 0x0160, # LATIN CAPITAL LETTER S WITH CARON Š + 0x0162, # LATIN CAPITAL LETTER T WITH CEDILLA Ţ + 0x0164, # LATIN CAPITAL LETTER T WITH CARON Ť + 0x0166, # LATIN CAPITAL LETTER T WITH STROKE Ŧ + 0x0168, # LATIN CAPITAL LETTER U WITH TILDE Ũ + 0x016A, # LATIN CAPITAL LETTER U WITH MACRON Ū + 0x016C, # LATIN CAPITAL LETTER U WITH BREVE Ŭ + 0x016E, # LATIN CAPITAL LETTER U WITH RING ABOVE Ů + 0x0170, # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE Ű + 0x0172, # LATIN CAPITAL LETTER U WITH OGONEK Ų + 0x0174, # LATIN CAPITAL LETTER W WITH CIRCUMFLEX Ŵ + 0x0176, # LATIN CAPITAL LETTER Y WITH CIRCUMFLEX Ŷ + 0x0178, # LATIN CAPITAL LETTER Y WITH DIAERESIS Ÿ + 0x0179, # LATIN CAPITAL LETTER Z WITH ACUTE Ź + 0x017B, # LATIN CAPITAL LETTER Z WITH DOT ABOVE Ż + 0x017D, # LATIN CAPITAL LETTER Z WITH CARON Ž + 0x0181, # LATIN CAPITAL LETTER B WITH HOOK Ɓ + 0x0182, # LATIN CAPITAL LETTER B WITH TOPBAR Ƃ + 0x0184, # LATIN CAPITAL LETTER TONE SIX Ƅ + 0x0186, # LATIN CAPITAL LETTER OPEN O Ɔ + 0x0187, # LATIN CAPITAL LETTER C WITH HOOK Ƈ + 0x0189, # LATIN CAPITAL LETTER AFRICAN D Ɖ + 0x018A, # LATIN CAPITAL LETTER D WITH HOOK Ɗ + 0x018B, # LATIN CAPITAL LETTER D WITH TOPBAR Ƌ + 0x018E, # LATIN CAPITAL LETTER REVERSED E Ǝ + 0x018F, # LATIN CAPITAL LETTER SCHWA Ə + 0x0190, # LATIN CAPITAL LETTER OPEN E Ɛ + 0x0191, # LATIN CAPITAL LETTER F WITH HOOK Ƒ + 0x0193, # LATIN CAPITAL LETTER G WITH HOOK Ɠ + 0x0194, # LATIN CAPITAL LETTER GAMMA Ɣ + 0x0196, # LATIN CAPITAL LETTER IOTA Ɩ + 0x0197, # LATIN CAPITAL LETTER I WITH STROKE Ɨ + 0x0198, # LATIN CAPITAL LETTER K WITH HOOK Ƙ + 0x019C, # LATIN CAPITAL LETTER TURNED M Ɯ + 0x019D, # LATIN CAPITAL LETTER N WITH LEFT HOOK Ɲ + 0x019F, # LATIN CAPITAL LETTER O WITH MIDDLE TILDE Ɵ + 0x01A0, # LATIN CAPITAL LETTER O WITH HORN Ơ + 0x01A2, # LATIN CAPITAL LETTER OI Ƣ + 0x01A4, # LATIN CAPITAL LETTER P WITH HOOK Ƥ + 0x01A6, # LATIN LETTER YR Ʀ + 0x01A7, # LATIN CAPITAL LETTER TONE TWO Ƨ + 0x01A9, # LATIN CAPITAL LETTER ESH Ʃ + 0x01AC, # LATIN CAPITAL LETTER T WITH HOOK Ƭ + 0x01AE, # LATIN CAPITAL LETTER T WITH RETROFLEX HOOK Ʈ + 0x01AF, # LATIN CAPITAL LETTER U WITH HORN Ư + 0x01B1, # LATIN CAPITAL LETTER UPSILON Ʊ + 0x01B2, # LATIN CAPITAL LETTER V WITH HOOK Ʋ + 0x01B3, # LATIN CAPITAL LETTER Y WITH HOOK Ƴ + 0x01B5, # LATIN CAPITAL LETTER Z WITH STROKE Ƶ + 0x01B7, # LATIN CAPITAL LETTER EZH Ʒ + 0x01B8, # LATIN CAPITAL LETTER EZH REVERSED Ƹ + 0x01BC, # LATIN CAPITAL LETTER TONE FIVE Ƽ + 0x01C4, # LATIN CAPITAL LETTER DZ WITH CARON DŽ + 0x01C5, # LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON Dž + 0x01C7, # LATIN CAPITAL LETTER LJ LJ + 0x01C8, # LATIN CAPITAL LETTER L WITH SMALL LETTER J Lj + 0x01CA, # LATIN CAPITAL LETTER NJ NJ + 0x01CB, # LATIN CAPITAL LETTER N WITH SMALL LETTER J Nj + 0x01CD, # LATIN CAPITAL LETTER A WITH CARON Ǎ + 0x01CF, # LATIN CAPITAL LETTER I WITH CARON Ǐ + 0x01D1, # LATIN CAPITAL LETTER O WITH CARON Ǒ + 0x01D3, # LATIN CAPITAL LETTER U WITH CARON Ǔ + 0x01D5, # LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON Ǖ + 0x01D7, # LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE Ǘ + 0x01D9, # LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON Ǚ + 0x01DB, # LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE Ǜ + 0x01DE, # LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON Ǟ + 0x01E0, # LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON Ǡ + 0x01E2, # LATIN CAPITAL LETTER AE WITH MACRON Ǣ + 0x01E4, # LATIN CAPITAL LETTER G WITH STROKE Ǥ + 0x01E6, # LATIN CAPITAL LETTER G WITH CARON Ǧ + 0x01E8, # LATIN CAPITAL LETTER K WITH CARON Ǩ + 0x01EA, # LATIN CAPITAL LETTER O WITH OGONEK Ǫ + 0x01EC, # LATIN CAPITAL LETTER O WITH OGONEK AND MACRON Ǭ + 0x01EE, # LATIN CAPITAL LETTER EZH WITH CARON Ǯ + 0x01F1, # LATIN CAPITAL LETTER DZ DZ + 0x01F2, # LATIN CAPITAL LETTER D WITH SMALL LETTER Z Dz + 0x01F4, # LATIN CAPITAL LETTER G WITH ACUTE Ǵ + 0x01F6, # LATIN CAPITAL LETTER HWAIR Ƕ + 0x01F7, # LATIN CAPITAL LETTER WYNN Ƿ + 0x01F8, # LATIN CAPITAL LETTER N WITH GRAVE Ǹ + 0x01FA, # LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE Ǻ + 0x01FC, # LATIN CAPITAL LETTER AE WITH ACUTE Ǽ + 0x01FE, # LATIN CAPITAL LETTER O WITH STROKE AND ACUTE Ǿ + 0x0200, # LATIN CAPITAL LETTER A WITH DOUBLE GRAVE Ȁ + 0x0202, # LATIN CAPITAL LETTER A WITH INVERTED BREVE Ȃ + 0x0204, # LATIN CAPITAL LETTER E WITH DOUBLE GRAVE Ȅ + 0x0206, # LATIN CAPITAL LETTER E WITH INVERTED BREVE Ȇ + 0x0208, # LATIN CAPITAL LETTER I WITH DOUBLE GRAVE Ȉ + 0x020A, # LATIN CAPITAL LETTER I WITH INVERTED BREVE Ȋ + 0x020C, # LATIN CAPITAL LETTER O WITH DOUBLE GRAVE Ȍ + 0x020E, # LATIN CAPITAL LETTER O WITH INVERTED BREVE Ȏ + 0x0210, # LATIN CAPITAL LETTER R WITH DOUBLE GRAVE Ȑ + 0x0212, # LATIN CAPITAL LETTER R WITH INVERTED BREVE Ȓ + 0x0214, # LATIN CAPITAL LETTER U WITH DOUBLE GRAVE Ȕ + 0x0216, # LATIN CAPITAL LETTER U WITH INVERTED BREVE Ȗ + 0x0218, # LATIN CAPITAL LETTER S WITH COMMA BELOW Ș + 0x021A, # LATIN CAPITAL LETTER T WITH COMMA BELOW Ț + 0x021C, # LATIN CAPITAL LETTER YOGH Ȝ + 0x021E, # LATIN CAPITAL LETTER H WITH CARON Ȟ + 0x0220, # LATIN CAPITAL LETTER N WITH LONG RIGHT LEG Ƞ + 0x0222, # LATIN CAPITAL LETTER OU Ȣ + 0x0224, # LATIN CAPITAL LETTER Z WITH HOOK Ȥ + 0x0226, # LATIN CAPITAL LETTER A WITH DOT ABOVE Ȧ + 0x0228, # LATIN CAPITAL LETTER E WITH CEDILLA Ȩ + 0x022A, # LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON Ȫ + 0x022C, # LATIN CAPITAL LETTER O WITH TILDE AND MACRON Ȭ + 0x022E, # LATIN CAPITAL LETTER O WITH DOT ABOVE Ȯ + 0x0230, # LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON Ȱ + 0x0232, # LATIN CAPITAL LETTER Y WITH MACRON Ȳ + 0x023A, # LATIN CAPITAL LETTER A WITH STROKE Ⱥ + 0x023B, # LATIN CAPITAL LETTER C WITH STROKE Ȼ + 0x023D, # LATIN CAPITAL LETTER L WITH BAR Ƚ + 0x023E, # LATIN CAPITAL LETTER T WITH DIAGONAL STROKE Ⱦ + 0x0241, # LATIN CAPITAL LETTER GLOTTAL STOP Ɂ + 0x0243, # LATIN CAPITAL LETTER B WITH STROKE Ƀ + 0x0244, # LATIN CAPITAL LETTER U BAR Ʉ + 0x0245, # LATIN CAPITAL LETTER TURNED V Ʌ + 0x0246, # LATIN CAPITAL LETTER E WITH STROKE Ɇ + 0x0248, # LATIN CAPITAL LETTER J WITH STROKE Ɉ + 0x024A, # LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL Ɋ + 0x024C, # LATIN CAPITAL LETTER R WITH STROKE Ɍ + 0x024E, # LATIN CAPITAL LETTER Y WITH STROKE Ɏ + 0x0370, # GREEK CAPITAL LETTER HETA Ͱ + 0x0372, # GREEK CAPITAL LETTER ARCHAIC SAMPI Ͳ + 0x0376, # GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA Ͷ + 0x037F, # GREEK CAPITAL LETTER YOT Ϳ + 0x0386, # GREEK CAPITAL LETTER ALPHA WITH TONOS Ά + 0x0388, # GREEK CAPITAL LETTER EPSILON WITH TONOS Έ + 0x0389, # GREEK CAPITAL LETTER ETA WITH TONOS Ή + 0x038A, # GREEK CAPITAL LETTER IOTA WITH TONOS Ί + 0x038C, # GREEK CAPITAL LETTER OMICRON WITH TONOS Ό + 0x038E, # GREEK CAPITAL LETTER UPSILON WITH TONOS Ύ + 0x038F, # GREEK CAPITAL LETTER OMEGA WITH TONOS Ώ + 0x0391, # GREEK CAPITAL LETTER ALPHA Α + 0x0392, # GREEK CAPITAL LETTER BETA Β + 0x0393, # GREEK CAPITAL LETTER GAMMA Γ + 0x0394, # GREEK CAPITAL LETTER DELTA Δ + 0x0395, # GREEK CAPITAL LETTER EPSILON Ε + 0x0396, # GREEK CAPITAL LETTER ZETA Ζ + 0x0397, # GREEK CAPITAL LETTER ETA Η + 0x0398, # GREEK CAPITAL LETTER THETA Θ + 0x0399, # GREEK CAPITAL LETTER IOTA Ι + 0x039A, # GREEK CAPITAL LETTER KAPPA Κ + 0x039B, # GREEK CAPITAL LETTER LAMDA Λ + 0x039C, # GREEK CAPITAL LETTER MU Μ + 0x039D, # GREEK CAPITAL LETTER NU Ν + 0x039E, # GREEK CAPITAL LETTER XI Ξ + 0x039F, # GREEK CAPITAL LETTER OMICRON Ο + 0x03A0, # GREEK CAPITAL LETTER PI Π + 0x03A1, # GREEK CAPITAL LETTER RHO Ρ + 0x03A3, # GREEK CAPITAL LETTER SIGMA Σ + 0x03A4, # GREEK CAPITAL LETTER TAU Τ + 0x03A5, # GREEK CAPITAL LETTER UPSILON Υ + 0x03A6, # GREEK CAPITAL LETTER PHI Φ + 0x03A7, # GREEK CAPITAL LETTER CHI Χ + 0x03A8, # GREEK CAPITAL LETTER PSI Ψ + 0x03A9, # GREEK CAPITAL LETTER OMEGA Ω + 0x03AA, # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA Ϊ + 0x03AB, # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA Ϋ + 0x03CF, # GREEK CAPITAL KAI SYMBOL Ϗ + 0x03D8, # GREEK LETTER ARCHAIC KOPPA Ϙ + 0x03DA, # GREEK LETTER STIGMA Ϛ + 0x03DC, # GREEK LETTER DIGAMMA Ϝ + 0x03DE, # GREEK LETTER KOPPA Ϟ + 0x03E0, # GREEK LETTER SAMPI Ϡ + 0x03E2, # COPTIC CAPITAL LETTER SHEI Ϣ + 0x03E4, # COPTIC CAPITAL LETTER FEI Ϥ + 0x03E6, # COPTIC CAPITAL LETTER KHEI Ϧ + 0x03E8, # COPTIC CAPITAL LETTER HORI Ϩ + 0x03EA, # COPTIC CAPITAL LETTER GANGIA Ϫ + 0x03EC, # COPTIC CAPITAL LETTER SHIMA Ϭ + 0x03EE, # COPTIC CAPITAL LETTER DEI Ϯ + 0x03F4, # GREEK CAPITAL THETA SYMBOL ϴ + 0x03F7, # GREEK CAPITAL LETTER SHO Ϸ + 0x03F9, # GREEK CAPITAL LUNATE SIGMA SYMBOL Ϲ + 0x03FA, # GREEK CAPITAL LETTER SAN Ϻ + 0x03FD, # GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL Ͻ + 0x03FE, # GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL Ͼ + 0x03FF, # GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL Ͽ + 0x0400, # CYRILLIC CAPITAL LETTER IE WITH GRAVE Ѐ + 0x0401, # CYRILLIC CAPITAL LETTER IO Ё + 0x0402, # CYRILLIC CAPITAL LETTER DJE Ђ + 0x0403, # CYRILLIC CAPITAL LETTER GJE Ѓ + 0x0404, # CYRILLIC CAPITAL LETTER UKRAINIAN IE Є + 0x0405, # CYRILLIC CAPITAL LETTER DZE Ѕ + 0x0406, # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I І + 0x0407, # CYRILLIC CAPITAL LETTER YI Ї + 0x0408, # CYRILLIC CAPITAL LETTER JE Ј + 0x0409, # CYRILLIC CAPITAL LETTER LJE Љ + 0x040A, # CYRILLIC CAPITAL LETTER NJE Њ + 0x040B, # CYRILLIC CAPITAL LETTER TSHE Ћ + 0x040C, # CYRILLIC CAPITAL LETTER KJE Ќ + 0x040D, # CYRILLIC CAPITAL LETTER I WITH GRAVE Ѝ + 0x040E, # CYRILLIC CAPITAL LETTER SHORT U Ў + 0x040F, # CYRILLIC CAPITAL LETTER DZHE Џ + 0x0410, # CYRILLIC CAPITAL LETTER A А + 0x0411, # CYRILLIC CAPITAL LETTER BE Б + 0x0412, # CYRILLIC CAPITAL LETTER VE В + 0x0413, # CYRILLIC CAPITAL LETTER GHE Г + 0x0414, # CYRILLIC CAPITAL LETTER DE Д + 0x0415, # CYRILLIC CAPITAL LETTER IE Е + 0x0416, # CYRILLIC CAPITAL LETTER ZHE Ж + 0x0417, # CYRILLIC CAPITAL LETTER ZE З + 0x0418, # CYRILLIC CAPITAL LETTER I И + 0x0419, # CYRILLIC CAPITAL LETTER SHORT I Й + 0x041A, # CYRILLIC CAPITAL LETTER KA К + 0x041B, # CYRILLIC CAPITAL LETTER EL Л + 0x041C, # CYRILLIC CAPITAL LETTER EM М + 0x041D, # CYRILLIC CAPITAL LETTER EN Н + 0x041E, # CYRILLIC CAPITAL LETTER O О + 0x041F, # CYRILLIC CAPITAL LETTER PE П + 0x0420, # CYRILLIC CAPITAL LETTER ER Р + 0x0421, # CYRILLIC CAPITAL LETTER ES С + 0x0422, # CYRILLIC CAPITAL LETTER TE Т + 0x0423, # CYRILLIC CAPITAL LETTER U У + 0x0424, # CYRILLIC CAPITAL LETTER EF Ф + 0x0425, # CYRILLIC CAPITAL LETTER HA Х + 0x0426, # CYRILLIC CAPITAL LETTER TSE Ц + 0x0427, # CYRILLIC CAPITAL LETTER CHE Ч + 0x0428, # CYRILLIC CAPITAL LETTER SHA Ш + 0x0429, # CYRILLIC CAPITAL LETTER SHCHA Щ + 0x042A, # CYRILLIC CAPITAL LETTER HARD SIGN Ъ + 0x042B, # CYRILLIC CAPITAL LETTER YERU Ы + 0x042C, # CYRILLIC CAPITAL LETTER SOFT SIGN Ь + 0x042D, # CYRILLIC CAPITAL LETTER E Э + 0x042E, # CYRILLIC CAPITAL LETTER YU Ю + 0x042F, # CYRILLIC CAPITAL LETTER YA Я + 0x0460, # CYRILLIC CAPITAL LETTER OMEGA Ѡ + 0x0462, # CYRILLIC CAPITAL LETTER YAT Ѣ + 0x0464, # CYRILLIC CAPITAL LETTER IOTIFIED E Ѥ + 0x0466, # CYRILLIC CAPITAL LETTER LITTLE YUS Ѧ + 0x0468, # CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS Ѩ + 0x046A, # CYRILLIC CAPITAL LETTER BIG YUS Ѫ + 0x046C, # CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS Ѭ + 0x046E, # CYRILLIC CAPITAL LETTER KSI Ѯ + 0x0470, # CYRILLIC CAPITAL LETTER PSI Ѱ + 0x0472, # CYRILLIC CAPITAL LETTER FITA Ѳ + 0x0474, # CYRILLIC CAPITAL LETTER IZHITSA Ѵ + 0x0476, # CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT Ѷ + 0x0478, # CYRILLIC CAPITAL LETTER UK Ѹ + 0x047A, # CYRILLIC CAPITAL LETTER ROUND OMEGA Ѻ + 0x047C, # CYRILLIC CAPITAL LETTER OMEGA WITH TITLO Ѽ + 0x047E, # CYRILLIC CAPITAL LETTER OT Ѿ + 0x0480, # CYRILLIC CAPITAL LETTER KOPPA Ҁ + 0x048A, # CYRILLIC CAPITAL LETTER SHORT I WITH TAIL Ҋ + 0x048C, # CYRILLIC CAPITAL LETTER SEMISOFT SIGN Ҍ + 0x048E, # CYRILLIC CAPITAL LETTER ER WITH TICK Ҏ + 0x0490, # CYRILLIC CAPITAL LETTER GHE WITH UPTURN Ґ + 0x0492, # CYRILLIC CAPITAL LETTER GHE WITH STROKE Ғ + 0x0494, # CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK Ҕ + 0x0496, # CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER Җ + 0x0498, # CYRILLIC CAPITAL LETTER ZE WITH DESCENDER Ҙ + 0x049A, # CYRILLIC CAPITAL LETTER KA WITH DESCENDER Қ + 0x049C, # CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE Ҝ + 0x049E, # CYRILLIC CAPITAL LETTER KA WITH STROKE Ҟ + 0x04A0, # CYRILLIC CAPITAL LETTER BASHKIR KA Ҡ + 0x04A2, # CYRILLIC CAPITAL LETTER EN WITH DESCENDER Ң + 0x04A4, # CYRILLIC CAPITAL LIGATURE EN GHE Ҥ + 0x04A6, # CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK Ҧ + 0x04A8, # CYRILLIC CAPITAL LETTER ABKHASIAN HA Ҩ + 0x04AA, # CYRILLIC CAPITAL LETTER ES WITH DESCENDER Ҫ + 0x04AC, # CYRILLIC CAPITAL LETTER TE WITH DESCENDER Ҭ + 0x04AE, # CYRILLIC CAPITAL LETTER STRAIGHT U Ү + 0x04B0, # CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE Ұ + 0x04B2, # CYRILLIC CAPITAL LETTER HA WITH DESCENDER Ҳ + 0x04B4, # CYRILLIC CAPITAL LIGATURE TE TSE Ҵ + 0x04B6, # CYRILLIC CAPITAL LETTER CHE WITH DESCENDER Ҷ + 0x04B8, # CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE Ҹ + 0x04BA, # CYRILLIC CAPITAL LETTER SHHA Һ + 0x04BC, # CYRILLIC CAPITAL LETTER ABKHASIAN CHE Ҽ + 0x04BE, # CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER Ҿ + 0x04C0, # CYRILLIC LETTER PALOCHKA Ӏ + 0x04C1, # CYRILLIC CAPITAL LETTER ZHE WITH BREVE Ӂ + 0x04C3, # CYRILLIC CAPITAL LETTER KA WITH HOOK Ӄ + 0x04C5, # CYRILLIC CAPITAL LETTER EL WITH TAIL Ӆ + 0x04C7, # CYRILLIC CAPITAL LETTER EN WITH HOOK Ӈ + 0x04C9, # CYRILLIC CAPITAL LETTER EN WITH TAIL Ӊ + 0x04CB, # CYRILLIC CAPITAL LETTER KHAKASSIAN CHE Ӌ + 0x04CD, # CYRILLIC CAPITAL LETTER EM WITH TAIL Ӎ + 0x04D0, # CYRILLIC CAPITAL LETTER A WITH BREVE Ӑ + 0x04D2, # CYRILLIC CAPITAL LETTER A WITH DIAERESIS Ӓ + 0x04D4, # CYRILLIC CAPITAL LIGATURE A IE Ӕ + 0x04D6, # CYRILLIC CAPITAL LETTER IE WITH BREVE Ӗ + 0x04D8, # CYRILLIC CAPITAL LETTER SCHWA Ә + 0x04DA, # CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS Ӛ + 0x04DC, # CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS Ӝ + 0x04DE, # CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS Ӟ + 0x04E0, # CYRILLIC CAPITAL LETTER ABKHASIAN DZE Ӡ + 0x04E2, # CYRILLIC CAPITAL LETTER I WITH MACRON Ӣ + 0x04E4, # CYRILLIC CAPITAL LETTER I WITH DIAERESIS Ӥ + 0x04E6, # CYRILLIC CAPITAL LETTER O WITH DIAERESIS Ӧ + 0x04E8, # CYRILLIC CAPITAL LETTER BARRED O Ө + 0x04EA, # CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS Ӫ + 0x04EC, # CYRILLIC CAPITAL LETTER E WITH DIAERESIS Ӭ + 0x04EE, # CYRILLIC CAPITAL LETTER U WITH MACRON Ӯ + 0x04F0, # CYRILLIC CAPITAL LETTER U WITH DIAERESIS Ӱ + 0x04F2, # CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE Ӳ + 0x04F4, # CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS Ӵ + 0x04F6, # CYRILLIC CAPITAL LETTER GHE WITH DESCENDER Ӷ + 0x04F8, # CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS Ӹ + 0x04FA, # CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK Ӻ + 0x04FC, # CYRILLIC CAPITAL LETTER HA WITH HOOK Ӽ + 0x04FE, # CYRILLIC CAPITAL LETTER HA WITH STROKE Ӿ + 0x0500, # CYRILLIC CAPITAL LETTER KOMI DE Ԁ + 0x0502, # CYRILLIC CAPITAL LETTER KOMI DJE Ԃ + 0x0504, # CYRILLIC CAPITAL LETTER KOMI ZJE Ԅ + 0x0506, # CYRILLIC CAPITAL LETTER KOMI DZJE Ԇ + 0x0508, # CYRILLIC CAPITAL LETTER KOMI LJE Ԉ + 0x050A, # CYRILLIC CAPITAL LETTER KOMI NJE Ԋ + 0x050C, # CYRILLIC CAPITAL LETTER KOMI SJE Ԍ + 0x050E, # CYRILLIC CAPITAL LETTER KOMI TJE Ԏ + 0x0510, # CYRILLIC CAPITAL LETTER REVERSED ZE Ԑ + 0x0512, # CYRILLIC CAPITAL LETTER EL WITH HOOK Ԓ + 0x0514, # CYRILLIC CAPITAL LETTER LHA Ԕ + 0x0516, # CYRILLIC CAPITAL LETTER RHA Ԗ + 0x0518, # CYRILLIC CAPITAL LETTER YAE Ԙ + 0x051A, # CYRILLIC CAPITAL LETTER QA Ԛ + 0x051C, # CYRILLIC CAPITAL LETTER WE Ԝ + 0x051E, # CYRILLIC CAPITAL LETTER ALEUT KA Ԟ + 0x0520, # CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK Ԡ + 0x0522, # CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK Ԣ + 0x0524, # CYRILLIC CAPITAL LETTER PE WITH DESCENDER Ԥ + 0x0526, # CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER Ԧ + 0x0528, # CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK Ԩ + 0x052A, # CYRILLIC CAPITAL LETTER DZZHE Ԫ + 0x052C, # CYRILLIC CAPITAL LETTER DCHE Ԭ + 0x052E, # CYRILLIC CAPITAL LETTER EL WITH DESCENDER Ԯ + 0x0531, # ARMENIAN CAPITAL LETTER AYB Ա + 0x0532, # ARMENIAN CAPITAL LETTER BEN Բ + 0x0533, # ARMENIAN CAPITAL LETTER GIM Գ + 0x0534, # ARMENIAN CAPITAL LETTER DA Դ + 0x0535, # ARMENIAN CAPITAL LETTER ECH Ե + 0x0536, # ARMENIAN CAPITAL LETTER ZA Զ + 0x0537, # ARMENIAN CAPITAL LETTER EH Է + 0x0538, # ARMENIAN CAPITAL LETTER ET Ը + 0x0539, # ARMENIAN CAPITAL LETTER TO Թ + 0x053A, # ARMENIAN CAPITAL LETTER ZHE Ժ + 0x053B, # ARMENIAN CAPITAL LETTER INI Ի + 0x053C, # ARMENIAN CAPITAL LETTER LIWN Լ + 0x053D, # ARMENIAN CAPITAL LETTER XEH Խ + 0x053E, # ARMENIAN CAPITAL LETTER CA Ծ + 0x053F, # ARMENIAN CAPITAL LETTER KEN Կ + 0x0540, # ARMENIAN CAPITAL LETTER HO Հ + 0x0541, # ARMENIAN CAPITAL LETTER JA Ձ + 0x0542, # ARMENIAN CAPITAL LETTER GHAD Ղ + 0x0543, # ARMENIAN CAPITAL LETTER CHEH Ճ + 0x0544, # ARMENIAN CAPITAL LETTER MEN Մ + 0x0545, # ARMENIAN CAPITAL LETTER YI Յ + 0x0546, # ARMENIAN CAPITAL LETTER NOW Ն + 0x0547, # ARMENIAN CAPITAL LETTER SHA Շ + 0x0548, # ARMENIAN CAPITAL LETTER VO Ո + 0x0549, # ARMENIAN CAPITAL LETTER CHA Չ + 0x054A, # ARMENIAN CAPITAL LETTER PEH Պ + 0x054B, # ARMENIAN CAPITAL LETTER JHEH Ջ + 0x054C, # ARMENIAN CAPITAL LETTER RA Ռ + 0x054D, # ARMENIAN CAPITAL LETTER SEH Ս + 0x054E, # ARMENIAN CAPITAL LETTER VEW Վ + 0x054F, # ARMENIAN CAPITAL LETTER TIWN Տ + 0x0550, # ARMENIAN CAPITAL LETTER REH Ր + 0x0551, # ARMENIAN CAPITAL LETTER CO Ց + 0x0552, # ARMENIAN CAPITAL LETTER YIWN Ւ + 0x0553, # ARMENIAN CAPITAL LETTER PIWR Փ + 0x0554, # ARMENIAN CAPITAL LETTER KEH Ք + 0x0555, # ARMENIAN CAPITAL LETTER OH Օ + 0x0556, # ARMENIAN CAPITAL LETTER FEH Ֆ + 0x10A0, # GEORGIAN CAPITAL LETTER AN Ⴀ + 0x10A1, # GEORGIAN CAPITAL LETTER BAN Ⴁ + 0x10A2, # GEORGIAN CAPITAL LETTER GAN Ⴂ + 0x10A3, # GEORGIAN CAPITAL LETTER DON Ⴃ + 0x10A4, # GEORGIAN CAPITAL LETTER EN Ⴄ + 0x10A5, # GEORGIAN CAPITAL LETTER VIN Ⴅ + 0x10A6, # GEORGIAN CAPITAL LETTER ZEN Ⴆ + 0x10A7, # GEORGIAN CAPITAL LETTER TAN Ⴇ + 0x10A8, # GEORGIAN CAPITAL LETTER IN Ⴈ + 0x10A9, # GEORGIAN CAPITAL LETTER KAN Ⴉ + 0x10AA, # GEORGIAN CAPITAL LETTER LAS Ⴊ + 0x10AB, # GEORGIAN CAPITAL LETTER MAN Ⴋ + 0x10AC, # GEORGIAN CAPITAL LETTER NAR Ⴌ + 0x10AD, # GEORGIAN CAPITAL LETTER ON Ⴍ + 0x10AE, # GEORGIAN CAPITAL LETTER PAR Ⴎ + 0x10AF, # GEORGIAN CAPITAL LETTER ZHAR Ⴏ + 0x10B0, # GEORGIAN CAPITAL LETTER RAE Ⴐ + 0x10B1, # GEORGIAN CAPITAL LETTER SAN Ⴑ + 0x10B2, # GEORGIAN CAPITAL LETTER TAR Ⴒ + 0x10B3, # GEORGIAN CAPITAL LETTER UN Ⴓ + 0x10B4, # GEORGIAN CAPITAL LETTER PHAR Ⴔ + 0x10B5, # GEORGIAN CAPITAL LETTER KHAR Ⴕ + 0x10B6, # GEORGIAN CAPITAL LETTER GHAN Ⴖ + 0x10B7, # GEORGIAN CAPITAL LETTER QAR Ⴗ + 0x10B8, # GEORGIAN CAPITAL LETTER SHIN Ⴘ + 0x10B9, # GEORGIAN CAPITAL LETTER CHIN Ⴙ + 0x10BA, # GEORGIAN CAPITAL LETTER CAN Ⴚ + 0x10BB, # GEORGIAN CAPITAL LETTER JIL Ⴛ + 0x10BC, # GEORGIAN CAPITAL LETTER CIL Ⴜ + 0x10BD, # GEORGIAN CAPITAL LETTER CHAR Ⴝ + 0x10BE, # GEORGIAN CAPITAL LETTER XAN Ⴞ + 0x10BF, # GEORGIAN CAPITAL LETTER JHAN Ⴟ + 0x10C0, # GEORGIAN CAPITAL LETTER HAE Ⴠ + 0x10C1, # GEORGIAN CAPITAL LETTER HE Ⴡ + 0x10C2, # GEORGIAN CAPITAL LETTER HIE Ⴢ + 0x10C3, # GEORGIAN CAPITAL LETTER WE Ⴣ + 0x10C4, # GEORGIAN CAPITAL LETTER HAR Ⴤ + 0x10C5, # GEORGIAN CAPITAL LETTER HOE Ⴥ + 0x10C7, # GEORGIAN CAPITAL LETTER YN Ⴧ + 0x10CD, # GEORGIAN CAPITAL LETTER AEN Ⴭ + 0x13A0, # CHEROKEE LETTER A Ꭰ + 0x13A1, # CHEROKEE LETTER E Ꭱ + 0x13A2, # CHEROKEE LETTER I Ꭲ + 0x13A3, # CHEROKEE LETTER O Ꭳ + 0x13A4, # CHEROKEE LETTER U Ꭴ + 0x13A5, # CHEROKEE LETTER V Ꭵ + 0x13A6, # CHEROKEE LETTER GA Ꭶ + 0x13A7, # CHEROKEE LETTER KA Ꭷ + 0x13A8, # CHEROKEE LETTER GE Ꭸ + 0x13A9, # CHEROKEE LETTER GI Ꭹ + 0x13AA, # CHEROKEE LETTER GO Ꭺ + 0x13AB, # CHEROKEE LETTER GU Ꭻ + 0x13AC, # CHEROKEE LETTER GV Ꭼ + 0x13AD, # CHEROKEE LETTER HA Ꭽ + 0x13AE, # CHEROKEE LETTER HE Ꭾ + 0x13AF, # CHEROKEE LETTER HI Ꭿ + 0x13B0, # CHEROKEE LETTER HO Ꮀ + 0x13B1, # CHEROKEE LETTER HU Ꮁ + 0x13B2, # CHEROKEE LETTER HV Ꮂ + 0x13B3, # CHEROKEE LETTER LA Ꮃ + 0x13B4, # CHEROKEE LETTER LE Ꮄ + 0x13B5, # CHEROKEE LETTER LI Ꮅ + 0x13B6, # CHEROKEE LETTER LO Ꮆ + 0x13B7, # CHEROKEE LETTER LU Ꮇ + 0x13B8, # CHEROKEE LETTER LV Ꮈ + 0x13B9, # CHEROKEE LETTER MA Ꮉ + 0x13BA, # CHEROKEE LETTER ME Ꮊ + 0x13BB, # CHEROKEE LETTER MI Ꮋ + 0x13BC, # CHEROKEE LETTER MO Ꮌ + 0x13BD, # CHEROKEE LETTER MU Ꮍ + 0x13BE, # CHEROKEE LETTER NA Ꮎ + 0x13BF, # CHEROKEE LETTER HNA Ꮏ + 0x13C0, # CHEROKEE LETTER NAH Ꮐ + 0x13C1, # CHEROKEE LETTER NE Ꮑ + 0x13C2, # CHEROKEE LETTER NI Ꮒ + 0x13C3, # CHEROKEE LETTER NO Ꮓ + 0x13C4, # CHEROKEE LETTER NU Ꮔ + 0x13C5, # CHEROKEE LETTER NV Ꮕ + 0x13C6, # CHEROKEE LETTER QUA Ꮖ + 0x13C7, # CHEROKEE LETTER QUE Ꮗ + 0x13C8, # CHEROKEE LETTER QUI Ꮘ + 0x13C9, # CHEROKEE LETTER QUO Ꮙ + 0x13CA, # CHEROKEE LETTER QUU Ꮚ + 0x13CB, # CHEROKEE LETTER QUV Ꮛ + 0x13CC, # CHEROKEE LETTER SA Ꮜ + 0x13CD, # CHEROKEE LETTER S Ꮝ + 0x13CE, # CHEROKEE LETTER SE Ꮞ + 0x13CF, # CHEROKEE LETTER SI Ꮟ + 0x13D0, # CHEROKEE LETTER SO Ꮠ + 0x13D1, # CHEROKEE LETTER SU Ꮡ + 0x13D2, # CHEROKEE LETTER SV Ꮢ + 0x13D3, # CHEROKEE LETTER DA Ꮣ + 0x13D4, # CHEROKEE LETTER TA Ꮤ + 0x13D5, # CHEROKEE LETTER DE Ꮥ + 0x13D6, # CHEROKEE LETTER TE Ꮦ + 0x13D7, # CHEROKEE LETTER DI Ꮧ + 0x13D8, # CHEROKEE LETTER TI Ꮨ + 0x13D9, # CHEROKEE LETTER DO Ꮩ + 0x13DA, # CHEROKEE LETTER DU Ꮪ + 0x13DB, # CHEROKEE LETTER DV Ꮫ + 0x13DC, # CHEROKEE LETTER DLA Ꮬ + 0x13DD, # CHEROKEE LETTER TLA Ꮭ + 0x13DE, # CHEROKEE LETTER TLE Ꮮ + 0x13DF, # CHEROKEE LETTER TLI Ꮯ + 0x13E0, # CHEROKEE LETTER TLO Ꮰ + 0x13E1, # CHEROKEE LETTER TLU Ꮱ + 0x13E2, # CHEROKEE LETTER TLV Ꮲ + 0x13E3, # CHEROKEE LETTER TSA Ꮳ + 0x13E4, # CHEROKEE LETTER TSE Ꮴ + 0x13E5, # CHEROKEE LETTER TSI Ꮵ + 0x13E6, # CHEROKEE LETTER TSO Ꮶ + 0x13E7, # CHEROKEE LETTER TSU Ꮷ + 0x13E8, # CHEROKEE LETTER TSV Ꮸ + 0x13E9, # CHEROKEE LETTER WA Ꮹ + 0x13EA, # CHEROKEE LETTER WE Ꮺ + 0x13EB, # CHEROKEE LETTER WI Ꮻ + 0x13EC, # CHEROKEE LETTER WO Ꮼ + 0x13ED, # CHEROKEE LETTER WU Ꮽ + 0x13EE, # CHEROKEE LETTER WV Ꮾ + 0x13EF, # CHEROKEE LETTER YA Ꮿ + 0x13F0, # CHEROKEE LETTER YE Ᏸ + 0x13F1, # CHEROKEE LETTER YI Ᏹ + 0x13F2, # CHEROKEE LETTER YO Ᏺ + 0x13F3, # CHEROKEE LETTER YU Ᏻ + 0x13F4, # CHEROKEE LETTER YV Ᏼ + 0x13F5, # CHEROKEE LETTER MV Ᏽ + 0x1C90, # GEORGIAN MTAVRULI CAPITAL LETTER AN Ა + 0x1C91, # GEORGIAN MTAVRULI CAPITAL LETTER BAN Ბ + 0x1C92, # GEORGIAN MTAVRULI CAPITAL LETTER GAN Გ + 0x1C93, # GEORGIAN MTAVRULI CAPITAL LETTER DON Დ + 0x1C94, # GEORGIAN MTAVRULI CAPITAL LETTER EN Ე + 0x1C95, # GEORGIAN MTAVRULI CAPITAL LETTER VIN Ვ + 0x1C96, # GEORGIAN MTAVRULI CAPITAL LETTER ZEN Ზ + 0x1C97, # GEORGIAN MTAVRULI CAPITAL LETTER TAN Თ + 0x1C98, # GEORGIAN MTAVRULI CAPITAL LETTER IN Ი + 0x1C99, # GEORGIAN MTAVRULI CAPITAL LETTER KAN Კ + 0x1C9A, # GEORGIAN MTAVRULI CAPITAL LETTER LAS Ლ + 0x1C9B, # GEORGIAN MTAVRULI CAPITAL LETTER MAN Მ + 0x1C9C, # GEORGIAN MTAVRULI CAPITAL LETTER NAR Ნ + 0x1C9D, # GEORGIAN MTAVRULI CAPITAL LETTER ON Ო + 0x1C9E, # GEORGIAN MTAVRULI CAPITAL LETTER PAR Პ + 0x1C9F, # GEORGIAN MTAVRULI CAPITAL LETTER ZHAR Ჟ + 0x1CA0, # GEORGIAN MTAVRULI CAPITAL LETTER RAE Რ + 0x1CA1, # GEORGIAN MTAVRULI CAPITAL LETTER SAN Ს + 0x1CA2, # GEORGIAN MTAVRULI CAPITAL LETTER TAR Ტ + 0x1CA3, # GEORGIAN MTAVRULI CAPITAL LETTER UN Უ + 0x1CA4, # GEORGIAN MTAVRULI CAPITAL LETTER PHAR Ფ + 0x1CA5, # GEORGIAN MTAVRULI CAPITAL LETTER KHAR Ქ + 0x1CA6, # GEORGIAN MTAVRULI CAPITAL LETTER GHAN Ღ + 0x1CA7, # GEORGIAN MTAVRULI CAPITAL LETTER QAR Ყ + 0x1CA8, # GEORGIAN MTAVRULI CAPITAL LETTER SHIN Შ + 0x1CA9, # GEORGIAN MTAVRULI CAPITAL LETTER CHIN Ჩ + 0x1CAA, # GEORGIAN MTAVRULI CAPITAL LETTER CAN Ც + 0x1CAB, # GEORGIAN MTAVRULI CAPITAL LETTER JIL Ძ + 0x1CAC, # GEORGIAN MTAVRULI CAPITAL LETTER CIL Წ + 0x1CAD, # GEORGIAN MTAVRULI CAPITAL LETTER CHAR Ჭ + 0x1CAE, # GEORGIAN MTAVRULI CAPITAL LETTER XAN Ხ + 0x1CAF, # GEORGIAN MTAVRULI CAPITAL LETTER JHAN Ჯ + 0x1CB0, # GEORGIAN MTAVRULI CAPITAL LETTER HAE Ჰ + 0x1CB1, # GEORGIAN MTAVRULI CAPITAL LETTER HE Ჱ + 0x1CB2, # GEORGIAN MTAVRULI CAPITAL LETTER HIE Ჲ + 0x1CB3, # GEORGIAN MTAVRULI CAPITAL LETTER WE Ჳ + 0x1CB4, # GEORGIAN MTAVRULI CAPITAL LETTER HAR Ჴ + 0x1CB5, # GEORGIAN MTAVRULI CAPITAL LETTER HOE Ჵ + 0x1CB6, # GEORGIAN MTAVRULI CAPITAL LETTER FI Ჶ + 0x1CB7, # GEORGIAN MTAVRULI CAPITAL LETTER YN Ჷ + 0x1CB8, # GEORGIAN MTAVRULI CAPITAL LETTER ELIFI Ჸ + 0x1CB9, # GEORGIAN MTAVRULI CAPITAL LETTER TURNED GAN Ჹ + 0x1CBA, # GEORGIAN MTAVRULI CAPITAL LETTER AIN Ჺ + 0x1CBD, # GEORGIAN MTAVRULI CAPITAL LETTER AEN Ჽ + 0x1CBE, # GEORGIAN MTAVRULI CAPITAL LETTER HARD SIGN Ჾ + 0x1CBF, # GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN Ჿ + 0x1E00, # LATIN CAPITAL LETTER A WITH RING BELOW Ḁ + 0x1E02, # LATIN CAPITAL LETTER B WITH DOT ABOVE Ḃ + 0x1E04, # LATIN CAPITAL LETTER B WITH DOT BELOW Ḅ + 0x1E06, # LATIN CAPITAL LETTER B WITH LINE BELOW Ḇ + 0x1E08, # LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE Ḉ + 0x1E0A, # LATIN CAPITAL LETTER D WITH DOT ABOVE Ḋ + 0x1E0C, # LATIN CAPITAL LETTER D WITH DOT BELOW Ḍ + 0x1E0E, # LATIN CAPITAL LETTER D WITH LINE BELOW Ḏ + 0x1E10, # LATIN CAPITAL LETTER D WITH CEDILLA Ḑ + 0x1E12, # LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW Ḓ + 0x1E14, # LATIN CAPITAL LETTER E WITH MACRON AND GRAVE Ḕ + 0x1E16, # LATIN CAPITAL LETTER E WITH MACRON AND ACUTE Ḗ + 0x1E18, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW Ḙ + 0x1E1A, # LATIN CAPITAL LETTER E WITH TILDE BELOW Ḛ + 0x1E1C, # LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE Ḝ + 0x1E1E, # LATIN CAPITAL LETTER F WITH DOT ABOVE Ḟ + 0x1E20, # LATIN CAPITAL LETTER G WITH MACRON Ḡ + 0x1E22, # LATIN CAPITAL LETTER H WITH DOT ABOVE Ḣ + 0x1E24, # LATIN CAPITAL LETTER H WITH DOT BELOW Ḥ + 0x1E26, # LATIN CAPITAL LETTER H WITH DIAERESIS Ḧ + 0x1E28, # LATIN CAPITAL LETTER H WITH CEDILLA Ḩ + 0x1E2A, # LATIN CAPITAL LETTER H WITH BREVE BELOW Ḫ + 0x1E2C, # LATIN CAPITAL LETTER I WITH TILDE BELOW Ḭ + 0x1E2E, # LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE Ḯ + 0x1E30, # LATIN CAPITAL LETTER K WITH ACUTE Ḱ + 0x1E32, # LATIN CAPITAL LETTER K WITH DOT BELOW Ḳ + 0x1E34, # LATIN CAPITAL LETTER K WITH LINE BELOW Ḵ + 0x1E36, # LATIN CAPITAL LETTER L WITH DOT BELOW Ḷ + 0x1E38, # LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON Ḹ + 0x1E3A, # LATIN CAPITAL LETTER L WITH LINE BELOW Ḻ + 0x1E3C, # LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW Ḽ + 0x1E3E, # LATIN CAPITAL LETTER M WITH ACUTE Ḿ + 0x1E40, # LATIN CAPITAL LETTER M WITH DOT ABOVE Ṁ + 0x1E42, # LATIN CAPITAL LETTER M WITH DOT BELOW Ṃ + 0x1E44, # LATIN CAPITAL LETTER N WITH DOT ABOVE Ṅ + 0x1E46, # LATIN CAPITAL LETTER N WITH DOT BELOW Ṇ + 0x1E48, # LATIN CAPITAL LETTER N WITH LINE BELOW Ṉ + 0x1E4A, # LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW Ṋ + 0x1E4C, # LATIN CAPITAL LETTER O WITH TILDE AND ACUTE Ṍ + 0x1E4E, # LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS Ṏ + 0x1E50, # LATIN CAPITAL LETTER O WITH MACRON AND GRAVE Ṑ + 0x1E52, # LATIN CAPITAL LETTER O WITH MACRON AND ACUTE Ṓ + 0x1E54, # LATIN CAPITAL LETTER P WITH ACUTE Ṕ + 0x1E56, # LATIN CAPITAL LETTER P WITH DOT ABOVE Ṗ + 0x1E58, # LATIN CAPITAL LETTER R WITH DOT ABOVE Ṙ + 0x1E5A, # LATIN CAPITAL LETTER R WITH DOT BELOW Ṛ + 0x1E5C, # LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON Ṝ + 0x1E5E, # LATIN CAPITAL LETTER R WITH LINE BELOW Ṟ + 0x1E60, # LATIN CAPITAL LETTER S WITH DOT ABOVE Ṡ + 0x1E62, # LATIN CAPITAL LETTER S WITH DOT BELOW Ṣ + 0x1E64, # LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE Ṥ + 0x1E66, # LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE Ṧ + 0x1E68, # LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE Ṩ + 0x1E6A, # LATIN CAPITAL LETTER T WITH DOT ABOVE Ṫ + 0x1E6C, # LATIN CAPITAL LETTER T WITH DOT BELOW Ṭ + 0x1E6E, # LATIN CAPITAL LETTER T WITH LINE BELOW Ṯ + 0x1E70, # LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW Ṱ + 0x1E72, # LATIN CAPITAL LETTER U WITH DIAERESIS BELOW Ṳ + 0x1E74, # LATIN CAPITAL LETTER U WITH TILDE BELOW Ṵ + 0x1E76, # LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW Ṷ + 0x1E78, # LATIN CAPITAL LETTER U WITH TILDE AND ACUTE Ṹ + 0x1E7A, # LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS Ṻ + 0x1E7C, # LATIN CAPITAL LETTER V WITH TILDE Ṽ + 0x1E7E, # LATIN CAPITAL LETTER V WITH DOT BELOW Ṿ + 0x1E80, # LATIN CAPITAL LETTER W WITH GRAVE Ẁ + 0x1E82, # LATIN CAPITAL LETTER W WITH ACUTE Ẃ + 0x1E84, # LATIN CAPITAL LETTER W WITH DIAERESIS Ẅ + 0x1E86, # LATIN CAPITAL LETTER W WITH DOT ABOVE Ẇ + 0x1E88, # LATIN CAPITAL LETTER W WITH DOT BELOW Ẉ + 0x1E8A, # LATIN CAPITAL LETTER X WITH DOT ABOVE Ẋ + 0x1E8C, # LATIN CAPITAL LETTER X WITH DIAERESIS Ẍ + 0x1E8E, # LATIN CAPITAL LETTER Y WITH DOT ABOVE Ẏ + 0x1E90, # LATIN CAPITAL LETTER Z WITH CIRCUMFLEX Ẑ + 0x1E92, # LATIN CAPITAL LETTER Z WITH DOT BELOW Ẓ + 0x1E94, # LATIN CAPITAL LETTER Z WITH LINE BELOW Ẕ + 0x1E9E, # LATIN CAPITAL LETTER SHARP S ẞ + 0x1EA0, # LATIN CAPITAL LETTER A WITH DOT BELOW Ạ + 0x1EA2, # LATIN CAPITAL LETTER A WITH HOOK ABOVE Ả + 0x1EA4, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE Ấ + 0x1EA6, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE Ầ + 0x1EA8, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE Ẩ + 0x1EAA, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE Ẫ + 0x1EAC, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW Ậ + 0x1EAE, # LATIN CAPITAL LETTER A WITH BREVE AND ACUTE Ắ + 0x1EB0, # LATIN CAPITAL LETTER A WITH BREVE AND GRAVE Ằ + 0x1EB2, # LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE Ẳ + 0x1EB4, # LATIN CAPITAL LETTER A WITH BREVE AND TILDE Ẵ + 0x1EB6, # LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW Ặ + 0x1EB8, # LATIN CAPITAL LETTER E WITH DOT BELOW Ẹ + 0x1EBA, # LATIN CAPITAL LETTER E WITH HOOK ABOVE Ẻ + 0x1EBC, # LATIN CAPITAL LETTER E WITH TILDE Ẽ + 0x1EBE, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE Ế + 0x1EC0, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE Ề + 0x1EC2, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE Ể + 0x1EC4, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE Ễ + 0x1EC6, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW Ệ + 0x1EC8, # LATIN CAPITAL LETTER I WITH HOOK ABOVE Ỉ + 0x1ECA, # LATIN CAPITAL LETTER I WITH DOT BELOW Ị + 0x1ECC, # LATIN CAPITAL LETTER O WITH DOT BELOW Ọ + 0x1ECE, # LATIN CAPITAL LETTER O WITH HOOK ABOVE Ỏ + 0x1ED0, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE Ố + 0x1ED2, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE Ồ + 0x1ED4, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE Ổ + 0x1ED6, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE Ỗ + 0x1ED8, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW Ộ + 0x1EDA, # LATIN CAPITAL LETTER O WITH HORN AND ACUTE Ớ + 0x1EDC, # LATIN CAPITAL LETTER O WITH HORN AND GRAVE Ờ + 0x1EDE, # LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE Ở + 0x1EE0, # LATIN CAPITAL LETTER O WITH HORN AND TILDE Ỡ + 0x1EE2, # LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW Ợ + 0x1EE4, # LATIN CAPITAL LETTER U WITH DOT BELOW Ụ + 0x1EE6, # LATIN CAPITAL LETTER U WITH HOOK ABOVE Ủ + 0x1EE8, # LATIN CAPITAL LETTER U WITH HORN AND ACUTE Ứ + 0x1EEA, # LATIN CAPITAL LETTER U WITH HORN AND GRAVE Ừ + 0x1EEC, # LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE Ử + 0x1EEE, # LATIN CAPITAL LETTER U WITH HORN AND TILDE Ữ + 0x1EF0, # LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW Ự + 0x1EF2, # LATIN CAPITAL LETTER Y WITH GRAVE Ỳ + 0x1EF4, # LATIN CAPITAL LETTER Y WITH DOT BELOW Ỵ + 0x1EF6, # LATIN CAPITAL LETTER Y WITH HOOK ABOVE Ỷ + 0x1EF8, # LATIN CAPITAL LETTER Y WITH TILDE Ỹ + 0x1EFA, # LATIN CAPITAL LETTER MIDDLE-WELSH LL Ỻ + 0x1EFC, # LATIN CAPITAL LETTER MIDDLE-WELSH V Ỽ + 0x1EFE, # LATIN CAPITAL LETTER Y WITH LOOP Ỿ + 0x1F08, # GREEK CAPITAL LETTER ALPHA WITH PSILI Ἀ + 0x1F09, # GREEK CAPITAL LETTER ALPHA WITH DASIA Ἁ + 0x1F0A, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA Ἂ + 0x1F0B, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA Ἃ + 0x1F0C, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA Ἄ + 0x1F0D, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA Ἅ + 0x1F0E, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI Ἆ + 0x1F0F, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI Ἇ + 0x1F18, # GREEK CAPITAL LETTER EPSILON WITH PSILI Ἐ + 0x1F19, # GREEK CAPITAL LETTER EPSILON WITH DASIA Ἑ + 0x1F1A, # GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA Ἒ + 0x1F1B, # GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA Ἓ + 0x1F1C, # GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA Ἔ + 0x1F1D, # GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA Ἕ + 0x1F28, # GREEK CAPITAL LETTER ETA WITH PSILI Ἠ + 0x1F29, # GREEK CAPITAL LETTER ETA WITH DASIA Ἡ + 0x1F2A, # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA Ἢ + 0x1F2B, # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA Ἣ + 0x1F2C, # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA Ἤ + 0x1F2D, # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA Ἥ + 0x1F2E, # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI Ἦ + 0x1F2F, # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI Ἧ + 0x1F38, # GREEK CAPITAL LETTER IOTA WITH PSILI Ἰ + 0x1F39, # GREEK CAPITAL LETTER IOTA WITH DASIA Ἱ + 0x1F3A, # GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA Ἲ + 0x1F3B, # GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA Ἳ + 0x1F3C, # GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA Ἴ + 0x1F3D, # GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA Ἵ + 0x1F3E, # GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI Ἶ + 0x1F3F, # GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI Ἷ + 0x1F48, # GREEK CAPITAL LETTER OMICRON WITH PSILI Ὀ + 0x1F49, # GREEK CAPITAL LETTER OMICRON WITH DASIA Ὁ + 0x1F4A, # GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA Ὂ + 0x1F4B, # GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA Ὃ + 0x1F4C, # GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA Ὄ + 0x1F4D, # GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA Ὅ + 0x1F59, # GREEK CAPITAL LETTER UPSILON WITH DASIA Ὑ + 0x1F5B, # GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA Ὓ + 0x1F5D, # GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA Ὕ + 0x1F5F, # GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI Ὗ + 0x1F68, # GREEK CAPITAL LETTER OMEGA WITH PSILI Ὠ + 0x1F69, # GREEK CAPITAL LETTER OMEGA WITH DASIA Ὡ + 0x1F6A, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA Ὢ + 0x1F6B, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA Ὣ + 0x1F6C, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA Ὤ + 0x1F6D, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA Ὥ + 0x1F6E, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI Ὦ + 0x1F6F, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI Ὧ + 0x1F88, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI ᾈ + 0x1F89, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI ᾉ + 0x1F8A, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI ᾊ + 0x1F8B, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI ᾋ + 0x1F8C, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI ᾌ + 0x1F8D, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI ᾍ + 0x1F8E, # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI ᾎ + 0x1F8F, # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI ᾏ + 0x1F98, # GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI ᾘ + 0x1F99, # GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI ᾙ + 0x1F9A, # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI ᾚ + 0x1F9B, # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI ᾛ + 0x1F9C, # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI ᾜ + 0x1F9D, # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI ᾝ + 0x1F9E, # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI ᾞ + 0x1F9F, # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI ᾟ + 0x1FA8, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI ᾨ + 0x1FA9, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI ᾩ + 0x1FAA, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI ᾪ + 0x1FAB, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI ᾫ + 0x1FAC, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI ᾬ + 0x1FAD, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI ᾭ + 0x1FAE, # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI ᾮ + 0x1FAF, # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI ᾯ + 0x1FB8, # GREEK CAPITAL LETTER ALPHA WITH VRACHY Ᾰ + 0x1FB9, # GREEK CAPITAL LETTER ALPHA WITH MACRON Ᾱ + 0x1FBA, # GREEK CAPITAL LETTER ALPHA WITH VARIA Ὰ + 0x1FBB, # GREEK CAPITAL LETTER ALPHA WITH OXIA Ά + 0x1FBC, # GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI ᾼ + 0x1FC8, # GREEK CAPITAL LETTER EPSILON WITH VARIA Ὲ + 0x1FC9, # GREEK CAPITAL LETTER EPSILON WITH OXIA Έ + 0x1FCA, # GREEK CAPITAL LETTER ETA WITH VARIA Ὴ + 0x1FCB, # GREEK CAPITAL LETTER ETA WITH OXIA Ή + 0x1FCC, # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI ῌ + 0x1FD8, # GREEK CAPITAL LETTER IOTA WITH VRACHY Ῐ + 0x1FD9, # GREEK CAPITAL LETTER IOTA WITH MACRON Ῑ + 0x1FDA, # GREEK CAPITAL LETTER IOTA WITH VARIA Ὶ + 0x1FDB, # GREEK CAPITAL LETTER IOTA WITH OXIA Ί + 0x1FE8, # GREEK CAPITAL LETTER UPSILON WITH VRACHY Ῠ + 0x1FE9, # GREEK CAPITAL LETTER UPSILON WITH MACRON Ῡ + 0x1FEA, # GREEK CAPITAL LETTER UPSILON WITH VARIA Ὺ + 0x1FEB, # GREEK CAPITAL LETTER UPSILON WITH OXIA Ύ + 0x1FEC, # GREEK CAPITAL LETTER RHO WITH DASIA Ῥ + 0x1FF8, # GREEK CAPITAL LETTER OMICRON WITH VARIA Ὸ + 0x1FF9, # GREEK CAPITAL LETTER OMICRON WITH OXIA Ό + 0x1FFA, # GREEK CAPITAL LETTER OMEGA WITH VARIA Ὼ + 0x1FFB, # GREEK CAPITAL LETTER OMEGA WITH OXIA Ώ + 0x1FFC, # GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI ῼ + 0x2126, # OHM SIGN Ω + 0x212A, # KELVIN SIGN K + 0x212B, # ANGSTROM SIGN Å + 0x2132, # TURNED CAPITAL F Ⅎ + 0x2160, # ROMAN NUMERAL ONE Ⅰ + 0x2161, # ROMAN NUMERAL TWO Ⅱ + 0x2162, # ROMAN NUMERAL THREE Ⅲ + 0x2163, # ROMAN NUMERAL FOUR Ⅳ + 0x2164, # ROMAN NUMERAL FIVE Ⅴ + 0x2165, # ROMAN NUMERAL SIX Ⅵ + 0x2166, # ROMAN NUMERAL SEVEN Ⅶ + 0x2167, # ROMAN NUMERAL EIGHT Ⅷ + 0x2168, # ROMAN NUMERAL NINE Ⅸ + 0x2169, # ROMAN NUMERAL TEN Ⅹ + 0x216A, # ROMAN NUMERAL ELEVEN Ⅺ + 0x216B, # ROMAN NUMERAL TWELVE Ⅻ + 0x216C, # ROMAN NUMERAL FIFTY Ⅼ + 0x216D, # ROMAN NUMERAL ONE HUNDRED Ⅽ + 0x216E, # ROMAN NUMERAL FIVE HUNDRED Ⅾ + 0x216F, # ROMAN NUMERAL ONE THOUSAND Ⅿ + 0x2183, # ROMAN NUMERAL REVERSED ONE HUNDRED Ↄ + 0x24B6, # CIRCLED LATIN CAPITAL LETTER A Ⓐ + 0x24B7, # CIRCLED LATIN CAPITAL LETTER B Ⓑ + 0x24B8, # CIRCLED LATIN CAPITAL LETTER C Ⓒ + 0x24B9, # CIRCLED LATIN CAPITAL LETTER D Ⓓ + 0x24BA, # CIRCLED LATIN CAPITAL LETTER E Ⓔ + 0x24BB, # CIRCLED LATIN CAPITAL LETTER F Ⓕ + 0x24BC, # CIRCLED LATIN CAPITAL LETTER G Ⓖ + 0x24BD, # CIRCLED LATIN CAPITAL LETTER H Ⓗ + 0x24BE, # CIRCLED LATIN CAPITAL LETTER I Ⓘ + 0x24BF, # CIRCLED LATIN CAPITAL LETTER J Ⓙ + 0x24C0, # CIRCLED LATIN CAPITAL LETTER K Ⓚ + 0x24C1, # CIRCLED LATIN CAPITAL LETTER L Ⓛ + 0x24C2, # CIRCLED LATIN CAPITAL LETTER M Ⓜ + 0x24C3, # CIRCLED LATIN CAPITAL LETTER N Ⓝ + 0x24C4, # CIRCLED LATIN CAPITAL LETTER O Ⓞ + 0x24C5, # CIRCLED LATIN CAPITAL LETTER P Ⓟ + 0x24C6, # CIRCLED LATIN CAPITAL LETTER Q Ⓠ + 0x24C7, # CIRCLED LATIN CAPITAL LETTER R Ⓡ + 0x24C8, # CIRCLED LATIN CAPITAL LETTER S Ⓢ + 0x24C9, # CIRCLED LATIN CAPITAL LETTER T Ⓣ + 0x24CA, # CIRCLED LATIN CAPITAL LETTER U Ⓤ + 0x24CB, # CIRCLED LATIN CAPITAL LETTER V Ⓥ + 0x24CC, # CIRCLED LATIN CAPITAL LETTER W Ⓦ + 0x24CD, # CIRCLED LATIN CAPITAL LETTER X Ⓧ + 0x24CE, # CIRCLED LATIN CAPITAL LETTER Y Ⓨ + 0x24CF, # CIRCLED LATIN CAPITAL LETTER Z Ⓩ + 0x2C00, # GLAGOLITIC CAPITAL LETTER AZU Ⰰ + 0x2C01, # GLAGOLITIC CAPITAL LETTER BUKY Ⰱ + 0x2C02, # GLAGOLITIC CAPITAL LETTER VEDE Ⰲ + 0x2C03, # GLAGOLITIC CAPITAL LETTER GLAGOLI Ⰳ + 0x2C04, # GLAGOLITIC CAPITAL LETTER DOBRO Ⰴ + 0x2C05, # GLAGOLITIC CAPITAL LETTER YESTU Ⰵ + 0x2C06, # GLAGOLITIC CAPITAL LETTER ZHIVETE Ⰶ + 0x2C07, # GLAGOLITIC CAPITAL LETTER DZELO Ⰷ + 0x2C08, # GLAGOLITIC CAPITAL LETTER ZEMLJA Ⰸ + 0x2C09, # GLAGOLITIC CAPITAL LETTER IZHE Ⰹ + 0x2C0A, # GLAGOLITIC CAPITAL LETTER INITIAL IZHE Ⰺ + 0x2C0B, # GLAGOLITIC CAPITAL LETTER I Ⰻ + 0x2C0C, # GLAGOLITIC CAPITAL LETTER DJERVI Ⰼ + 0x2C0D, # GLAGOLITIC CAPITAL LETTER KAKO Ⰽ + 0x2C0E, # GLAGOLITIC CAPITAL LETTER LJUDIJE Ⰾ + 0x2C0F, # GLAGOLITIC CAPITAL LETTER MYSLITE Ⰿ + 0x2C10, # GLAGOLITIC CAPITAL LETTER NASHI Ⱀ + 0x2C11, # GLAGOLITIC CAPITAL LETTER ONU Ⱁ + 0x2C12, # GLAGOLITIC CAPITAL LETTER POKOJI Ⱂ + 0x2C13, # GLAGOLITIC CAPITAL LETTER RITSI Ⱃ + 0x2C14, # GLAGOLITIC CAPITAL LETTER SLOVO Ⱄ + 0x2C15, # GLAGOLITIC CAPITAL LETTER TVRIDO Ⱅ + 0x2C16, # GLAGOLITIC CAPITAL LETTER UKU Ⱆ + 0x2C17, # GLAGOLITIC CAPITAL LETTER FRITU Ⱇ + 0x2C18, # GLAGOLITIC CAPITAL LETTER HERU Ⱈ + 0x2C19, # GLAGOLITIC CAPITAL LETTER OTU Ⱉ + 0x2C1A, # GLAGOLITIC CAPITAL LETTER PE Ⱊ + 0x2C1B, # GLAGOLITIC CAPITAL LETTER SHTA Ⱋ + 0x2C1C, # GLAGOLITIC CAPITAL LETTER TSI Ⱌ + 0x2C1D, # GLAGOLITIC CAPITAL LETTER CHRIVI Ⱍ + 0x2C1E, # GLAGOLITIC CAPITAL LETTER SHA Ⱎ + 0x2C1F, # GLAGOLITIC CAPITAL LETTER YERU Ⱏ + 0x2C20, # GLAGOLITIC CAPITAL LETTER YERI Ⱐ + 0x2C21, # GLAGOLITIC CAPITAL LETTER YATI Ⱑ + 0x2C22, # GLAGOLITIC CAPITAL LETTER SPIDERY HA Ⱒ + 0x2C23, # GLAGOLITIC CAPITAL LETTER YU Ⱓ + 0x2C24, # GLAGOLITIC CAPITAL LETTER SMALL YUS Ⱔ + 0x2C25, # GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL Ⱕ + 0x2C26, # GLAGOLITIC CAPITAL LETTER YO Ⱖ + 0x2C27, # GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS Ⱗ + 0x2C28, # GLAGOLITIC CAPITAL LETTER BIG YUS Ⱘ + 0x2C29, # GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS Ⱙ + 0x2C2A, # GLAGOLITIC CAPITAL LETTER FITA Ⱚ + 0x2C2B, # GLAGOLITIC CAPITAL LETTER IZHITSA Ⱛ + 0x2C2C, # GLAGOLITIC CAPITAL LETTER SHTAPIC Ⱜ + 0x2C2D, # GLAGOLITIC CAPITAL LETTER TROKUTASTI A Ⱝ + 0x2C2E, # GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE Ⱞ + 0x2C2F, # GLAGOLITIC CAPITAL LETTER CAUDATE CHRIVI Ⱟ + 0x2C60, # LATIN CAPITAL LETTER L WITH DOUBLE BAR Ⱡ + 0x2C62, # LATIN CAPITAL LETTER L WITH MIDDLE TILDE Ɫ + 0x2C63, # LATIN CAPITAL LETTER P WITH STROKE Ᵽ + 0x2C64, # LATIN CAPITAL LETTER R WITH TAIL Ɽ + 0x2C67, # LATIN CAPITAL LETTER H WITH DESCENDER Ⱨ + 0x2C69, # LATIN CAPITAL LETTER K WITH DESCENDER Ⱪ + 0x2C6B, # LATIN CAPITAL LETTER Z WITH DESCENDER Ⱬ + 0x2C6D, # LATIN CAPITAL LETTER ALPHA Ɑ + 0x2C6E, # LATIN CAPITAL LETTER M WITH HOOK Ɱ + 0x2C6F, # LATIN CAPITAL LETTER TURNED A Ɐ + 0x2C70, # LATIN CAPITAL LETTER TURNED ALPHA Ɒ + 0x2C72, # LATIN CAPITAL LETTER W WITH HOOK Ⱳ + 0x2C75, # LATIN CAPITAL LETTER HALF H Ⱶ + 0x2C7E, # LATIN CAPITAL LETTER S WITH SWASH TAIL Ȿ + 0x2C7F, # LATIN CAPITAL LETTER Z WITH SWASH TAIL Ɀ + 0x2C80, # COPTIC CAPITAL LETTER ALFA Ⲁ + 0x2C82, # COPTIC CAPITAL LETTER VIDA Ⲃ + 0x2C84, # COPTIC CAPITAL LETTER GAMMA Ⲅ + 0x2C86, # COPTIC CAPITAL LETTER DALDA Ⲇ + 0x2C88, # COPTIC CAPITAL LETTER EIE Ⲉ + 0x2C8A, # COPTIC CAPITAL LETTER SOU Ⲋ + 0x2C8C, # COPTIC CAPITAL LETTER ZATA Ⲍ + 0x2C8E, # COPTIC CAPITAL LETTER HATE Ⲏ + 0x2C90, # COPTIC CAPITAL LETTER THETHE Ⲑ + 0x2C92, # COPTIC CAPITAL LETTER IAUDA Ⲓ + 0x2C94, # COPTIC CAPITAL LETTER KAPA Ⲕ + 0x2C96, # COPTIC CAPITAL LETTER LAULA Ⲗ + 0x2C98, # COPTIC CAPITAL LETTER MI Ⲙ + 0x2C9A, # COPTIC CAPITAL LETTER NI Ⲛ + 0x2C9C, # COPTIC CAPITAL LETTER KSI Ⲝ + 0x2C9E, # COPTIC CAPITAL LETTER O Ⲟ + 0x2CA0, # COPTIC CAPITAL LETTER PI Ⲡ + 0x2CA2, # COPTIC CAPITAL LETTER RO Ⲣ + 0x2CA4, # COPTIC CAPITAL LETTER SIMA Ⲥ + 0x2CA6, # COPTIC CAPITAL LETTER TAU Ⲧ + 0x2CA8, # COPTIC CAPITAL LETTER UA Ⲩ + 0x2CAA, # COPTIC CAPITAL LETTER FI Ⲫ + 0x2CAC, # COPTIC CAPITAL LETTER KHI Ⲭ + 0x2CAE, # COPTIC CAPITAL LETTER PSI Ⲯ + 0x2CB0, # COPTIC CAPITAL LETTER OOU Ⲱ + 0x2CB2, # COPTIC CAPITAL LETTER DIALECT-P ALEF Ⲳ + 0x2CB4, # COPTIC CAPITAL LETTER OLD COPTIC AIN Ⲵ + 0x2CB6, # COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE Ⲷ + 0x2CB8, # COPTIC CAPITAL LETTER DIALECT-P KAPA Ⲹ + 0x2CBA, # COPTIC CAPITAL LETTER DIALECT-P NI Ⲻ + 0x2CBC, # COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI Ⲽ + 0x2CBE, # COPTIC CAPITAL LETTER OLD COPTIC OOU Ⲿ + 0x2CC0, # COPTIC CAPITAL LETTER SAMPI Ⳁ + 0x2CC2, # COPTIC CAPITAL LETTER CROSSED SHEI Ⳃ + 0x2CC4, # COPTIC CAPITAL LETTER OLD COPTIC SHEI Ⳅ + 0x2CC6, # COPTIC CAPITAL LETTER OLD COPTIC ESH Ⳇ + 0x2CC8, # COPTIC CAPITAL LETTER AKHMIMIC KHEI Ⳉ + 0x2CCA, # COPTIC CAPITAL LETTER DIALECT-P HORI Ⳋ + 0x2CCC, # COPTIC CAPITAL LETTER OLD COPTIC HORI Ⳍ + 0x2CCE, # COPTIC CAPITAL LETTER OLD COPTIC HA Ⳏ + 0x2CD0, # COPTIC CAPITAL LETTER L-SHAPED HA Ⳑ + 0x2CD2, # COPTIC CAPITAL LETTER OLD COPTIC HEI Ⳓ + 0x2CD4, # COPTIC CAPITAL LETTER OLD COPTIC HAT Ⳕ + 0x2CD6, # COPTIC CAPITAL LETTER OLD COPTIC GANGIA Ⳗ + 0x2CD8, # COPTIC CAPITAL LETTER OLD COPTIC DJA Ⳙ + 0x2CDA, # COPTIC CAPITAL LETTER OLD COPTIC SHIMA Ⳛ + 0x2CDC, # COPTIC CAPITAL LETTER OLD NUBIAN SHIMA Ⳝ + 0x2CDE, # COPTIC CAPITAL LETTER OLD NUBIAN NGI Ⳟ + 0x2CE0, # COPTIC CAPITAL LETTER OLD NUBIAN NYI Ⳡ + 0x2CE2, # COPTIC CAPITAL LETTER OLD NUBIAN WAU Ⳣ + 0x2CEB, # COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI Ⳬ + 0x2CED, # COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA Ⳮ + 0x2CF2, # COPTIC CAPITAL LETTER BOHAIRIC KHEI Ⳳ + 0xA640, # CYRILLIC CAPITAL LETTER ZEMLYA Ꙁ + 0xA642, # CYRILLIC CAPITAL LETTER DZELO Ꙃ + 0xA644, # CYRILLIC CAPITAL LETTER REVERSED DZE Ꙅ + 0xA646, # CYRILLIC CAPITAL LETTER IOTA Ꙇ + 0xA648, # CYRILLIC CAPITAL LETTER DJERV Ꙉ + 0xA64A, # CYRILLIC CAPITAL LETTER MONOGRAPH UK Ꙋ + 0xA64C, # CYRILLIC CAPITAL LETTER BROAD OMEGA Ꙍ + 0xA64E, # CYRILLIC CAPITAL LETTER NEUTRAL YER Ꙏ + 0xA650, # CYRILLIC CAPITAL LETTER YERU WITH BACK YER Ꙑ + 0xA652, # CYRILLIC CAPITAL LETTER IOTIFIED YAT Ꙓ + 0xA654, # CYRILLIC CAPITAL LETTER REVERSED YU Ꙕ + 0xA656, # CYRILLIC CAPITAL LETTER IOTIFIED A Ꙗ + 0xA658, # CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS Ꙙ + 0xA65A, # CYRILLIC CAPITAL LETTER BLENDED YUS Ꙛ + 0xA65C, # CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS Ꙝ + 0xA65E, # CYRILLIC CAPITAL LETTER YN Ꙟ + 0xA660, # CYRILLIC CAPITAL LETTER REVERSED TSE Ꙡ + 0xA662, # CYRILLIC CAPITAL LETTER SOFT DE Ꙣ + 0xA664, # CYRILLIC CAPITAL LETTER SOFT EL Ꙥ + 0xA666, # CYRILLIC CAPITAL LETTER SOFT EM Ꙧ + 0xA668, # CYRILLIC CAPITAL LETTER MONOCULAR O Ꙩ + 0xA66A, # CYRILLIC CAPITAL LETTER BINOCULAR O Ꙫ + 0xA66C, # CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O Ꙭ + 0xA680, # CYRILLIC CAPITAL LETTER DWE Ꚁ + 0xA682, # CYRILLIC CAPITAL LETTER DZWE Ꚃ + 0xA684, # CYRILLIC CAPITAL LETTER ZHWE Ꚅ + 0xA686, # CYRILLIC CAPITAL LETTER CCHE Ꚇ + 0xA688, # CYRILLIC CAPITAL LETTER DZZE Ꚉ + 0xA68A, # CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK Ꚋ + 0xA68C, # CYRILLIC CAPITAL LETTER TWE Ꚍ + 0xA68E, # CYRILLIC CAPITAL LETTER TSWE Ꚏ + 0xA690, # CYRILLIC CAPITAL LETTER TSSE Ꚑ + 0xA692, # CYRILLIC CAPITAL LETTER TCHE Ꚓ + 0xA694, # CYRILLIC CAPITAL LETTER HWE Ꚕ + 0xA696, # CYRILLIC CAPITAL LETTER SHWE Ꚗ + 0xA698, # CYRILLIC CAPITAL LETTER DOUBLE O Ꚙ + 0xA69A, # CYRILLIC CAPITAL LETTER CROSSED O Ꚛ + 0xA722, # LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF Ꜣ + 0xA724, # LATIN CAPITAL LETTER EGYPTOLOGICAL AIN Ꜥ + 0xA726, # LATIN CAPITAL LETTER HENG Ꜧ + 0xA728, # LATIN CAPITAL LETTER TZ Ꜩ + 0xA72A, # LATIN CAPITAL LETTER TRESILLO Ꜫ + 0xA72C, # LATIN CAPITAL LETTER CUATRILLO Ꜭ + 0xA72E, # LATIN CAPITAL LETTER CUATRILLO WITH COMMA Ꜯ + 0xA732, # LATIN CAPITAL LETTER AA Ꜳ + 0xA734, # LATIN CAPITAL LETTER AO Ꜵ + 0xA736, # LATIN CAPITAL LETTER AU Ꜷ + 0xA738, # LATIN CAPITAL LETTER AV Ꜹ + 0xA73A, # LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR Ꜻ + 0xA73C, # LATIN CAPITAL LETTER AY Ꜽ + 0xA73E, # LATIN CAPITAL LETTER REVERSED C WITH DOT Ꜿ + 0xA740, # LATIN CAPITAL LETTER K WITH STROKE Ꝁ + 0xA742, # LATIN CAPITAL LETTER K WITH DIAGONAL STROKE Ꝃ + 0xA744, # LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE Ꝅ + 0xA746, # LATIN CAPITAL LETTER BROKEN L Ꝇ + 0xA748, # LATIN CAPITAL LETTER L WITH HIGH STROKE Ꝉ + 0xA74A, # LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY Ꝋ + 0xA74C, # LATIN CAPITAL LETTER O WITH LOOP Ꝍ + 0xA74E, # LATIN CAPITAL LETTER OO Ꝏ + 0xA750, # LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER Ꝑ + 0xA752, # LATIN CAPITAL LETTER P WITH FLOURISH Ꝓ + 0xA754, # LATIN CAPITAL LETTER P WITH SQUIRREL TAIL Ꝕ + 0xA756, # LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER Ꝗ + 0xA758, # LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE Ꝙ + 0xA75A, # LATIN CAPITAL LETTER R ROTUNDA Ꝛ + 0xA75C, # LATIN CAPITAL LETTER RUM ROTUNDA Ꝝ + 0xA75E, # LATIN CAPITAL LETTER V WITH DIAGONAL STROKE Ꝟ + 0xA760, # LATIN CAPITAL LETTER VY Ꝡ + 0xA762, # LATIN CAPITAL LETTER VISIGOTHIC Z Ꝣ + 0xA764, # LATIN CAPITAL LETTER THORN WITH STROKE Ꝥ + 0xA766, # LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER Ꝧ + 0xA768, # LATIN CAPITAL LETTER VEND Ꝩ + 0xA76A, # LATIN CAPITAL LETTER ET Ꝫ + 0xA76C, # LATIN CAPITAL LETTER IS Ꝭ + 0xA76E, # LATIN CAPITAL LETTER CON Ꝯ + 0xA779, # LATIN CAPITAL LETTER INSULAR D Ꝺ + 0xA77B, # LATIN CAPITAL LETTER INSULAR F Ꝼ + 0xA77D, # LATIN CAPITAL LETTER INSULAR G Ᵹ + 0xA77E, # LATIN CAPITAL LETTER TURNED INSULAR G Ꝿ + 0xA780, # LATIN CAPITAL LETTER TURNED L Ꞁ + 0xA782, # LATIN CAPITAL LETTER INSULAR R Ꞃ + 0xA784, # LATIN CAPITAL LETTER INSULAR S Ꞅ + 0xA786, # LATIN CAPITAL LETTER INSULAR T Ꞇ + 0xA78B, # LATIN CAPITAL LETTER SALTILLO Ꞌ + 0xA78D, # LATIN CAPITAL LETTER TURNED H Ɥ + 0xA790, # LATIN CAPITAL LETTER N WITH DESCENDER Ꞑ + 0xA792, # LATIN CAPITAL LETTER C WITH BAR Ꞓ + 0xA796, # LATIN CAPITAL LETTER B WITH FLOURISH Ꞗ + 0xA798, # LATIN CAPITAL LETTER F WITH STROKE Ꞙ + 0xA79A, # LATIN CAPITAL LETTER VOLAPUK AE Ꞛ + 0xA79C, # LATIN CAPITAL LETTER VOLAPUK OE Ꞝ + 0xA79E, # LATIN CAPITAL LETTER VOLAPUK UE Ꞟ + 0xA7A0, # LATIN CAPITAL LETTER G WITH OBLIQUE STROKE Ꞡ + 0xA7A2, # LATIN CAPITAL LETTER K WITH OBLIQUE STROKE Ꞣ + 0xA7A4, # LATIN CAPITAL LETTER N WITH OBLIQUE STROKE Ꞥ + 0xA7A6, # LATIN CAPITAL LETTER R WITH OBLIQUE STROKE Ꞧ + 0xA7A8, # LATIN CAPITAL LETTER S WITH OBLIQUE STROKE Ꞩ + 0xA7AA, # LATIN CAPITAL LETTER H WITH HOOK Ɦ + 0xA7AB, # LATIN CAPITAL LETTER REVERSED OPEN E Ɜ + 0xA7AC, # LATIN CAPITAL LETTER SCRIPT G Ɡ + 0xA7AD, # LATIN CAPITAL LETTER L WITH BELT Ɬ + 0xA7AE, # LATIN CAPITAL LETTER SMALL CAPITAL I Ɪ + 0xA7B0, # LATIN CAPITAL LETTER TURNED K Ʞ + 0xA7B1, # LATIN CAPITAL LETTER TURNED T Ʇ + 0xA7B2, # LATIN CAPITAL LETTER J WITH CROSSED-TAIL Ʝ + 0xA7B3, # LATIN CAPITAL LETTER CHI Ꭓ + 0xA7B4, # LATIN CAPITAL LETTER BETA Ꞵ + 0xA7B6, # LATIN CAPITAL LETTER OMEGA Ꞷ + 0xA7B8, # LATIN CAPITAL LETTER U WITH STROKE Ꞹ + 0xA7BA, # LATIN CAPITAL LETTER GLOTTAL A Ꞻ + 0xA7BC, # LATIN CAPITAL LETTER GLOTTAL I Ꞽ + 0xA7BE, # LATIN CAPITAL LETTER GLOTTAL U Ꞿ + 0xA7C0, # LATIN CAPITAL LETTER OLD POLISH O Ꟁ + 0xA7C2, # LATIN CAPITAL LETTER ANGLICANA W Ꟃ + 0xA7C4, # LATIN CAPITAL LETTER C WITH PALATAL HOOK Ꞔ + 0xA7C5, # LATIN CAPITAL LETTER S WITH HOOK Ʂ + 0xA7C6, # LATIN CAPITAL LETTER Z WITH PALATAL HOOK Ᶎ + 0xA7C7, # LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY Ꟈ + 0xA7C9, # LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY Ꟊ + 0xA7D0, # LATIN CAPITAL LETTER CLOSED INSULAR G Ꟑ + 0xA7D6, # LATIN CAPITAL LETTER MIDDLE SCOTS S Ꟗ + 0xA7D8, # LATIN CAPITAL LETTER SIGMOID S Ꟙ + 0xA7F5, # LATIN CAPITAL LETTER REVERSED HALF H Ꟶ + 0xFF21, # FULLWIDTH LATIN CAPITAL LETTER A A + 0xFF22, # FULLWIDTH LATIN CAPITAL LETTER B B + 0xFF23, # FULLWIDTH LATIN CAPITAL LETTER C C + 0xFF24, # FULLWIDTH LATIN CAPITAL LETTER D D + 0xFF25, # FULLWIDTH LATIN CAPITAL LETTER E E + 0xFF26, # FULLWIDTH LATIN CAPITAL LETTER F F + 0xFF27, # FULLWIDTH LATIN CAPITAL LETTER G G + 0xFF28, # FULLWIDTH LATIN CAPITAL LETTER H H + 0xFF29, # FULLWIDTH LATIN CAPITAL LETTER I I + 0xFF2A, # FULLWIDTH LATIN CAPITAL LETTER J J + 0xFF2B, # FULLWIDTH LATIN CAPITAL LETTER K K + 0xFF2C, # FULLWIDTH LATIN CAPITAL LETTER L L + 0xFF2D, # FULLWIDTH LATIN CAPITAL LETTER M M + 0xFF2E, # FULLWIDTH LATIN CAPITAL LETTER N N + 0xFF2F, # FULLWIDTH LATIN CAPITAL LETTER O O + 0xFF30, # FULLWIDTH LATIN CAPITAL LETTER P P + 0xFF31, # FULLWIDTH LATIN CAPITAL LETTER Q Q + 0xFF32, # FULLWIDTH LATIN CAPITAL LETTER R R + 0xFF33, # FULLWIDTH LATIN CAPITAL LETTER S S + 0xFF34, # FULLWIDTH LATIN CAPITAL LETTER T T + 0xFF35, # FULLWIDTH LATIN CAPITAL LETTER U U + 0xFF36, # FULLWIDTH LATIN CAPITAL LETTER V V + 0xFF37, # FULLWIDTH LATIN CAPITAL LETTER W W + 0xFF38, # FULLWIDTH LATIN CAPITAL LETTER X X + 0xFF39, # FULLWIDTH LATIN CAPITAL LETTER Y Y + 0xFF3A, # FULLWIDTH LATIN CAPITAL LETTER Z Z + 0x10400, # DESERET CAPITAL LETTER LONG I 𐐀 + 0x10401, # DESERET CAPITAL LETTER LONG E 𐐁 + 0x10402, # DESERET CAPITAL LETTER LONG A 𐐂 + 0x10403, # DESERET CAPITAL LETTER LONG AH 𐐃 + 0x10404, # DESERET CAPITAL LETTER LONG O 𐐄 + 0x10405, # DESERET CAPITAL LETTER LONG OO 𐐅 + 0x10406, # DESERET CAPITAL LETTER SHORT I 𐐆 + 0x10407, # DESERET CAPITAL LETTER SHORT E 𐐇 + 0x10408, # DESERET CAPITAL LETTER SHORT A 𐐈 + 0x10409, # DESERET CAPITAL LETTER SHORT AH 𐐉 + 0x1040A, # DESERET CAPITAL LETTER SHORT O 𐐊 + 0x1040B, # DESERET CAPITAL LETTER SHORT OO 𐐋 + 0x1040C, # DESERET CAPITAL LETTER AY 𐐌 + 0x1040D, # DESERET CAPITAL LETTER OW 𐐍 + 0x1040E, # DESERET CAPITAL LETTER WU 𐐎 + 0x1040F, # DESERET CAPITAL LETTER YEE 𐐏 + 0x10410, # DESERET CAPITAL LETTER H 𐐐 + 0x10411, # DESERET CAPITAL LETTER PEE 𐐑 + 0x10412, # DESERET CAPITAL LETTER BEE 𐐒 + 0x10413, # DESERET CAPITAL LETTER TEE 𐐓 + 0x10414, # DESERET CAPITAL LETTER DEE 𐐔 + 0x10415, # DESERET CAPITAL LETTER CHEE 𐐕 + 0x10416, # DESERET CAPITAL LETTER JEE 𐐖 + 0x10417, # DESERET CAPITAL LETTER KAY 𐐗 + 0x10418, # DESERET CAPITAL LETTER GAY 𐐘 + 0x10419, # DESERET CAPITAL LETTER EF 𐐙 + 0x1041A, # DESERET CAPITAL LETTER VEE 𐐚 + 0x1041B, # DESERET CAPITAL LETTER ETH 𐐛 + 0x1041C, # DESERET CAPITAL LETTER THEE 𐐜 + 0x1041D, # DESERET CAPITAL LETTER ES 𐐝 + 0x1041E, # DESERET CAPITAL LETTER ZEE 𐐞 + 0x1041F, # DESERET CAPITAL LETTER ESH 𐐟 + 0x10420, # DESERET CAPITAL LETTER ZHEE 𐐠 + 0x10421, # DESERET CAPITAL LETTER ER 𐐡 + 0x10422, # DESERET CAPITAL LETTER EL 𐐢 + 0x10423, # DESERET CAPITAL LETTER EM 𐐣 + 0x10424, # DESERET CAPITAL LETTER EN 𐐤 + 0x10425, # DESERET CAPITAL LETTER ENG 𐐥 + 0x10426, # DESERET CAPITAL LETTER OI 𐐦 + 0x10427, # DESERET CAPITAL LETTER EW 𐐧 + 0x104B0, # OSAGE CAPITAL LETTER A 𐒰 + 0x104B1, # OSAGE CAPITAL LETTER AI 𐒱 + 0x104B2, # OSAGE CAPITAL LETTER AIN 𐒲 + 0x104B3, # OSAGE CAPITAL LETTER AH 𐒳 + 0x104B4, # OSAGE CAPITAL LETTER BRA 𐒴 + 0x104B5, # OSAGE CAPITAL LETTER CHA 𐒵 + 0x104B6, # OSAGE CAPITAL LETTER EHCHA 𐒶 + 0x104B7, # OSAGE CAPITAL LETTER E 𐒷 + 0x104B8, # OSAGE CAPITAL LETTER EIN 𐒸 + 0x104B9, # OSAGE CAPITAL LETTER HA 𐒹 + 0x104BA, # OSAGE CAPITAL LETTER HYA 𐒺 + 0x104BB, # OSAGE CAPITAL LETTER I 𐒻 + 0x104BC, # OSAGE CAPITAL LETTER KA 𐒼 + 0x104BD, # OSAGE CAPITAL LETTER EHKA 𐒽 + 0x104BE, # OSAGE CAPITAL LETTER KYA 𐒾 + 0x104BF, # OSAGE CAPITAL LETTER LA 𐒿 + 0x104C0, # OSAGE CAPITAL LETTER MA 𐓀 + 0x104C1, # OSAGE CAPITAL LETTER NA 𐓁 + 0x104C2, # OSAGE CAPITAL LETTER O 𐓂 + 0x104C3, # OSAGE CAPITAL LETTER OIN 𐓃 + 0x104C4, # OSAGE CAPITAL LETTER PA 𐓄 + 0x104C5, # OSAGE CAPITAL LETTER EHPA 𐓅 + 0x104C6, # OSAGE CAPITAL LETTER SA 𐓆 + 0x104C7, # OSAGE CAPITAL LETTER SHA 𐓇 + 0x104C8, # OSAGE CAPITAL LETTER TA 𐓈 + 0x104C9, # OSAGE CAPITAL LETTER EHTA 𐓉 + 0x104CA, # OSAGE CAPITAL LETTER TSA 𐓊 + 0x104CB, # OSAGE CAPITAL LETTER EHTSA 𐓋 + 0x104CC, # OSAGE CAPITAL LETTER TSHA 𐓌 + 0x104CD, # OSAGE CAPITAL LETTER DHA 𐓍 + 0x104CE, # OSAGE CAPITAL LETTER U 𐓎 + 0x104CF, # OSAGE CAPITAL LETTER WA 𐓏 + 0x104D0, # OSAGE CAPITAL LETTER KHA 𐓐 + 0x104D1, # OSAGE CAPITAL LETTER GHA 𐓑 + 0x104D2, # OSAGE CAPITAL LETTER ZA 𐓒 + 0x104D3, # OSAGE CAPITAL LETTER ZHA 𐓓 + 0x10570, # VITHKUQI CAPITAL LETTER A 𐕰 + 0x10571, # VITHKUQI CAPITAL LETTER BBE 𐕱 + 0x10572, # VITHKUQI CAPITAL LETTER BE 𐕲 + 0x10573, # VITHKUQI CAPITAL LETTER CE 𐕳 + 0x10574, # VITHKUQI CAPITAL LETTER CHE 𐕴 + 0x10575, # VITHKUQI CAPITAL LETTER DE 𐕵 + 0x10576, # VITHKUQI CAPITAL LETTER DHE 𐕶 + 0x10577, # VITHKUQI CAPITAL LETTER EI 𐕷 + 0x10578, # VITHKUQI CAPITAL LETTER E 𐕸 + 0x10579, # VITHKUQI CAPITAL LETTER FE 𐕹 + 0x1057A, # VITHKUQI CAPITAL LETTER GA 𐕺 + 0x1057C, # VITHKUQI CAPITAL LETTER HA 𐕼 + 0x1057D, # VITHKUQI CAPITAL LETTER HHA 𐕽 + 0x1057E, # VITHKUQI CAPITAL LETTER I 𐕾 + 0x1057F, # VITHKUQI CAPITAL LETTER IJE 𐕿 + 0x10580, # VITHKUQI CAPITAL LETTER JE 𐖀 + 0x10581, # VITHKUQI CAPITAL LETTER KA 𐖁 + 0x10582, # VITHKUQI CAPITAL LETTER LA 𐖂 + 0x10583, # VITHKUQI CAPITAL LETTER LLA 𐖃 + 0x10584, # VITHKUQI CAPITAL LETTER ME 𐖄 + 0x10585, # VITHKUQI CAPITAL LETTER NE 𐖅 + 0x10586, # VITHKUQI CAPITAL LETTER NJE 𐖆 + 0x10587, # VITHKUQI CAPITAL LETTER O 𐖇 + 0x10588, # VITHKUQI CAPITAL LETTER PE 𐖈 + 0x10589, # VITHKUQI CAPITAL LETTER QA 𐖉 + 0x1058A, # VITHKUQI CAPITAL LETTER RE 𐖊 + 0x1058C, # VITHKUQI CAPITAL LETTER SE 𐖌 + 0x1058D, # VITHKUQI CAPITAL LETTER SHE 𐖍 + 0x1058E, # VITHKUQI CAPITAL LETTER TE 𐖎 + 0x1058F, # VITHKUQI CAPITAL LETTER THE 𐖏 + 0x10590, # VITHKUQI CAPITAL LETTER U 𐖐 + 0x10591, # VITHKUQI CAPITAL LETTER VE 𐖑 + 0x10592, # VITHKUQI CAPITAL LETTER XE 𐖒 + 0x10594, # VITHKUQI CAPITAL LETTER Y 𐖔 + 0x10595, # VITHKUQI CAPITAL LETTER ZE 𐖕 + 0x10C80, # OLD HUNGARIAN CAPITAL LETTER A 𐲀 + 0x10C81, # OLD HUNGARIAN CAPITAL LETTER AA 𐲁 + 0x10C82, # OLD HUNGARIAN CAPITAL LETTER EB 𐲂 + 0x10C83, # OLD HUNGARIAN CAPITAL LETTER AMB 𐲃 + 0x10C84, # OLD HUNGARIAN CAPITAL LETTER EC 𐲄 + 0x10C85, # OLD HUNGARIAN CAPITAL LETTER ENC 𐲅 + 0x10C86, # OLD HUNGARIAN CAPITAL LETTER ECS 𐲆 + 0x10C87, # OLD HUNGARIAN CAPITAL LETTER ED 𐲇 + 0x10C88, # OLD HUNGARIAN CAPITAL LETTER AND 𐲈 + 0x10C89, # OLD HUNGARIAN CAPITAL LETTER E 𐲉 + 0x10C8A, # OLD HUNGARIAN CAPITAL LETTER CLOSE E 𐲊 + 0x10C8B, # OLD HUNGARIAN CAPITAL LETTER EE 𐲋 + 0x10C8C, # OLD HUNGARIAN CAPITAL LETTER EF 𐲌 + 0x10C8D, # OLD HUNGARIAN CAPITAL LETTER EG 𐲍 + 0x10C8E, # OLD HUNGARIAN CAPITAL LETTER EGY 𐲎 + 0x10C8F, # OLD HUNGARIAN CAPITAL LETTER EH 𐲏 + 0x10C90, # OLD HUNGARIAN CAPITAL LETTER I 𐲐 + 0x10C91, # OLD HUNGARIAN CAPITAL LETTER II 𐲑 + 0x10C92, # OLD HUNGARIAN CAPITAL LETTER EJ 𐲒 + 0x10C93, # OLD HUNGARIAN CAPITAL LETTER EK 𐲓 + 0x10C94, # OLD HUNGARIAN CAPITAL LETTER AK 𐲔 + 0x10C95, # OLD HUNGARIAN CAPITAL LETTER UNK 𐲕 + 0x10C96, # OLD HUNGARIAN CAPITAL LETTER EL 𐲖 + 0x10C97, # OLD HUNGARIAN CAPITAL LETTER ELY 𐲗 + 0x10C98, # OLD HUNGARIAN CAPITAL LETTER EM 𐲘 + 0x10C99, # OLD HUNGARIAN CAPITAL LETTER EN 𐲙 + 0x10C9A, # OLD HUNGARIAN CAPITAL LETTER ENY 𐲚 + 0x10C9B, # OLD HUNGARIAN CAPITAL LETTER O 𐲛 + 0x10C9C, # OLD HUNGARIAN CAPITAL LETTER OO 𐲜 + 0x10C9D, # OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE 𐲝 + 0x10C9E, # OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE 𐲞 + 0x10C9F, # OLD HUNGARIAN CAPITAL LETTER OEE 𐲟 + 0x10CA0, # OLD HUNGARIAN CAPITAL LETTER EP 𐲠 + 0x10CA1, # OLD HUNGARIAN CAPITAL LETTER EMP 𐲡 + 0x10CA2, # OLD HUNGARIAN CAPITAL LETTER ER 𐲢 + 0x10CA3, # OLD HUNGARIAN CAPITAL LETTER SHORT ER 𐲣 + 0x10CA4, # OLD HUNGARIAN CAPITAL LETTER ES 𐲤 + 0x10CA5, # OLD HUNGARIAN CAPITAL LETTER ESZ 𐲥 + 0x10CA6, # OLD HUNGARIAN CAPITAL LETTER ET 𐲦 + 0x10CA7, # OLD HUNGARIAN CAPITAL LETTER ENT 𐲧 + 0x10CA8, # OLD HUNGARIAN CAPITAL LETTER ETY 𐲨 + 0x10CA9, # OLD HUNGARIAN CAPITAL LETTER ECH 𐲩 + 0x10CAA, # OLD HUNGARIAN CAPITAL LETTER U 𐲪 + 0x10CAB, # OLD HUNGARIAN CAPITAL LETTER UU 𐲫 + 0x10CAC, # OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE 𐲬 + 0x10CAD, # OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE 𐲭 + 0x10CAE, # OLD HUNGARIAN CAPITAL LETTER EV 𐲮 + 0x10CAF, # OLD HUNGARIAN CAPITAL LETTER EZ 𐲯 + 0x10CB0, # OLD HUNGARIAN CAPITAL LETTER EZS 𐲰 + 0x10CB1, # OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN 𐲱 + 0x10CB2, # OLD HUNGARIAN CAPITAL LETTER US 𐲲 + 0x118A0, # WARANG CITI CAPITAL LETTER NGAA 𑢠 + 0x118A1, # WARANG CITI CAPITAL LETTER A 𑢡 + 0x118A2, # WARANG CITI CAPITAL LETTER WI 𑢢 + 0x118A3, # WARANG CITI CAPITAL LETTER YU 𑢣 + 0x118A4, # WARANG CITI CAPITAL LETTER YA 𑢤 + 0x118A5, # WARANG CITI CAPITAL LETTER YO 𑢥 + 0x118A6, # WARANG CITI CAPITAL LETTER II 𑢦 + 0x118A7, # WARANG CITI CAPITAL LETTER UU 𑢧 + 0x118A8, # WARANG CITI CAPITAL LETTER E 𑢨 + 0x118A9, # WARANG CITI CAPITAL LETTER O 𑢩 + 0x118AA, # WARANG CITI CAPITAL LETTER ANG 𑢪 + 0x118AB, # WARANG CITI CAPITAL LETTER GA 𑢫 + 0x118AC, # WARANG CITI CAPITAL LETTER KO 𑢬 + 0x118AD, # WARANG CITI CAPITAL LETTER ENY 𑢭 + 0x118AE, # WARANG CITI CAPITAL LETTER YUJ 𑢮 + 0x118AF, # WARANG CITI CAPITAL LETTER UC 𑢯 + 0x118B0, # WARANG CITI CAPITAL LETTER ENN 𑢰 + 0x118B1, # WARANG CITI CAPITAL LETTER ODD 𑢱 + 0x118B2, # WARANG CITI CAPITAL LETTER TTE 𑢲 + 0x118B3, # WARANG CITI CAPITAL LETTER NUNG 𑢳 + 0x118B4, # WARANG CITI CAPITAL LETTER DA 𑢴 + 0x118B5, # WARANG CITI CAPITAL LETTER AT 𑢵 + 0x118B6, # WARANG CITI CAPITAL LETTER AM 𑢶 + 0x118B7, # WARANG CITI CAPITAL LETTER BU 𑢷 + 0x118B8, # WARANG CITI CAPITAL LETTER PU 𑢸 + 0x118B9, # WARANG CITI CAPITAL LETTER HIYO 𑢹 + 0x118BA, # WARANG CITI CAPITAL LETTER HOLO 𑢺 + 0x118BB, # WARANG CITI CAPITAL LETTER HORR 𑢻 + 0x118BC, # WARANG CITI CAPITAL LETTER HAR 𑢼 + 0x118BD, # WARANG CITI CAPITAL LETTER SSUU 𑢽 + 0x118BE, # WARANG CITI CAPITAL LETTER SII 𑢾 + 0x118BF, # WARANG CITI CAPITAL LETTER VIYO 𑢿 + 0x16E40, # MEDEFAIDRIN CAPITAL LETTER M 𖹀 + 0x16E41, # MEDEFAIDRIN CAPITAL LETTER S 𖹁 + 0x16E42, # MEDEFAIDRIN CAPITAL LETTER V 𖹂 + 0x16E43, # MEDEFAIDRIN CAPITAL LETTER W 𖹃 + 0x16E44, # MEDEFAIDRIN CAPITAL LETTER ATIU 𖹄 + 0x16E45, # MEDEFAIDRIN CAPITAL LETTER Z 𖹅 + 0x16E46, # MEDEFAIDRIN CAPITAL LETTER KP 𖹆 + 0x16E47, # MEDEFAIDRIN CAPITAL LETTER P 𖹇 + 0x16E48, # MEDEFAIDRIN CAPITAL LETTER T 𖹈 + 0x16E49, # MEDEFAIDRIN CAPITAL LETTER G 𖹉 + 0x16E4A, # MEDEFAIDRIN CAPITAL LETTER F 𖹊 + 0x16E4B, # MEDEFAIDRIN CAPITAL LETTER I 𖹋 + 0x16E4C, # MEDEFAIDRIN CAPITAL LETTER K 𖹌 + 0x16E4D, # MEDEFAIDRIN CAPITAL LETTER A 𖹍 + 0x16E4E, # MEDEFAIDRIN CAPITAL LETTER J 𖹎 + 0x16E4F, # MEDEFAIDRIN CAPITAL LETTER E 𖹏 + 0x16E50, # MEDEFAIDRIN CAPITAL LETTER B 𖹐 + 0x16E51, # MEDEFAIDRIN CAPITAL LETTER C 𖹑 + 0x16E52, # MEDEFAIDRIN CAPITAL LETTER U 𖹒 + 0x16E53, # MEDEFAIDRIN CAPITAL LETTER YU 𖹓 + 0x16E54, # MEDEFAIDRIN CAPITAL LETTER L 𖹔 + 0x16E55, # MEDEFAIDRIN CAPITAL LETTER Q 𖹕 + 0x16E56, # MEDEFAIDRIN CAPITAL LETTER HP 𖹖 + 0x16E57, # MEDEFAIDRIN CAPITAL LETTER NY 𖹗 + 0x16E58, # MEDEFAIDRIN CAPITAL LETTER X 𖹘 + 0x16E59, # MEDEFAIDRIN CAPITAL LETTER D 𖹙 + 0x16E5A, # MEDEFAIDRIN CAPITAL LETTER OE 𖹚 + 0x16E5B, # MEDEFAIDRIN CAPITAL LETTER N 𖹛 + 0x16E5C, # MEDEFAIDRIN CAPITAL LETTER R 𖹜 + 0x16E5D, # MEDEFAIDRIN CAPITAL LETTER O 𖹝 + 0x16E5E, # MEDEFAIDRIN CAPITAL LETTER AI 𖹞 + 0x16E5F, # MEDEFAIDRIN CAPITAL LETTER Y 𖹟 + 0x1E900, # ADLAM CAPITAL LETTER ALIF 𞤀 + 0x1E901, # ADLAM CAPITAL LETTER DAALI 𞤁 + 0x1E902, # ADLAM CAPITAL LETTER LAAM 𞤂 + 0x1E903, # ADLAM CAPITAL LETTER MIIM 𞤃 + 0x1E904, # ADLAM CAPITAL LETTER BA 𞤄 + 0x1E905, # ADLAM CAPITAL LETTER SINNYIIYHE 𞤅 + 0x1E906, # ADLAM CAPITAL LETTER PE 𞤆 + 0x1E907, # ADLAM CAPITAL LETTER BHE 𞤇 + 0x1E908, # ADLAM CAPITAL LETTER RA 𞤈 + 0x1E909, # ADLAM CAPITAL LETTER E 𞤉 + 0x1E90A, # ADLAM CAPITAL LETTER FA 𞤊 + 0x1E90B, # ADLAM CAPITAL LETTER I 𞤋 + 0x1E90C, # ADLAM CAPITAL LETTER O 𞤌 + 0x1E90D, # ADLAM CAPITAL LETTER DHA 𞤍 + 0x1E90E, # ADLAM CAPITAL LETTER YHE 𞤎 + 0x1E90F, # ADLAM CAPITAL LETTER WAW 𞤏 + 0x1E910, # ADLAM CAPITAL LETTER NUN 𞤐 + 0x1E911, # ADLAM CAPITAL LETTER KAF 𞤑 + 0x1E912, # ADLAM CAPITAL LETTER YA 𞤒 + 0x1E913, # ADLAM CAPITAL LETTER U 𞤓 + 0x1E914, # ADLAM CAPITAL LETTER JIIM 𞤔 + 0x1E915, # ADLAM CAPITAL LETTER CHI 𞤕 + 0x1E916, # ADLAM CAPITAL LETTER HA 𞤖 + 0x1E917, # ADLAM CAPITAL LETTER QAAF 𞤗 + 0x1E918, # ADLAM CAPITAL LETTER GA 𞤘 + 0x1E919, # ADLAM CAPITAL LETTER NYA 𞤙 + 0x1E91A, # ADLAM CAPITAL LETTER TU 𞤚 + 0x1E91B, # ADLAM CAPITAL LETTER NHA 𞤛 + 0x1E91C, # ADLAM CAPITAL LETTER VA 𞤜 + 0x1E91D, # ADLAM CAPITAL LETTER KHA 𞤝 + 0x1E91E, # ADLAM CAPITAL LETTER GBE 𞤞 + 0x1E91F, # ADLAM CAPITAL LETTER ZAL 𞤟 + 0x1E920, # ADLAM CAPITAL LETTER KPO 𞤠 + 0x1E921, # ADLAM CAPITAL LETTER SHA 𞤡 +) +alias uppercase_mapping = List[UInt32, hint_trivial_type=True]( + 0x0041, # a -> A + 0x0042, # b -> B + 0x0043, # c -> C + 0x0044, # d -> D + 0x0045, # e -> E + 0x0046, # f -> F + 0x0047, # g -> G + 0x0048, # h -> H + 0x0049, # i -> I + 0x004A, # j -> J + 0x004B, # k -> K + 0x004C, # l -> L + 0x004D, # m -> M + 0x004E, # n -> N + 0x004F, # o -> O + 0x0050, # p -> P + 0x0051, # q -> Q + 0x0052, # r -> R + 0x0053, # s -> S + 0x0054, # t -> T + 0x0055, # u -> U + 0x0056, # v -> V + 0x0057, # w -> W + 0x0058, # x -> X + 0x0059, # y -> Y + 0x005A, # z -> Z + 0x039C, # µ -> Μ + 0x00C0, # à -> À + 0x00C1, # á -> Á + 0x00C2, # â ->  + 0x00C3, # ã -> à + 0x00C4, # ä -> Ä + 0x00C5, # å -> Å + 0x00C6, # æ -> Æ + 0x00C7, # ç -> Ç + 0x00C8, # è -> È + 0x00C9, # é -> É + 0x00CA, # ê -> Ê + 0x00CB, # ë -> Ë + 0x00CC, # ì -> Ì + 0x00CD, # í -> Í + 0x00CE, # î -> Î + 0x00CF, # ï -> Ï + 0x00D0, # ð -> Ð + 0x00D1, # ñ -> Ñ + 0x00D2, # ò -> Ò + 0x00D3, # ó -> Ó + 0x00D4, # ô -> Ô + 0x00D5, # õ -> Õ + 0x00D6, # ö -> Ö + 0x00D8, # ø -> Ø + 0x00D9, # ù -> Ù + 0x00DA, # ú -> Ú + 0x00DB, # û -> Û + 0x00DC, # ü -> Ü + 0x00DD, # ý -> Ý + 0x00DE, # þ -> Þ + 0x0178, # ÿ -> Ÿ + 0x0100, # ā -> Ā + 0x0102, # ă -> Ă + 0x0104, # ą -> Ą + 0x0106, # ć -> Ć + 0x0108, # ĉ -> Ĉ + 0x010A, # ċ -> Ċ + 0x010C, # č -> Č + 0x010E, # ď -> Ď + 0x0110, # đ -> Đ + 0x0112, # ē -> Ē + 0x0114, # ĕ -> Ĕ + 0x0116, # ė -> Ė + 0x0118, # ę -> Ę + 0x011A, # ě -> Ě + 0x011C, # ĝ -> Ĝ + 0x011E, # ğ -> Ğ + 0x0120, # ġ -> Ġ + 0x0122, # ģ -> Ģ + 0x0124, # ĥ -> Ĥ + 0x0126, # ħ -> Ħ + 0x0128, # ĩ -> Ĩ + 0x012A, # ī -> Ī + 0x012C, # ĭ -> Ĭ + 0x012E, # į -> Į + 0x0049, # ı -> I + 0x0132, # ij -> IJ + 0x0134, # ĵ -> Ĵ + 0x0136, # ķ -> Ķ + 0x0139, # ĺ -> Ĺ + 0x013B, # ļ -> Ļ + 0x013D, # ľ -> Ľ + 0x013F, # ŀ -> Ŀ + 0x0141, # ł -> Ł + 0x0143, # ń -> Ń + 0x0145, # ņ -> Ņ + 0x0147, # ň -> Ň + 0x014A, # ŋ -> Ŋ + 0x014C, # ō -> Ō + 0x014E, # ŏ -> Ŏ + 0x0150, # ő -> Ő + 0x0152, # œ -> Œ + 0x0154, # ŕ -> Ŕ + 0x0156, # ŗ -> Ŗ + 0x0158, # ř -> Ř + 0x015A, # ś -> Ś + 0x015C, # ŝ -> Ŝ + 0x015E, # ş -> Ş + 0x0160, # š -> Š + 0x0162, # ţ -> Ţ + 0x0164, # ť -> Ť + 0x0166, # ŧ -> Ŧ + 0x0168, # ũ -> Ũ + 0x016A, # ū -> Ū + 0x016C, # ŭ -> Ŭ + 0x016E, # ů -> Ů + 0x0170, # ű -> Ű + 0x0172, # ų -> Ų + 0x0174, # ŵ -> Ŵ + 0x0176, # ŷ -> Ŷ + 0x0179, # ź -> Ź + 0x017B, # ż -> Ż + 0x017D, # ž -> Ž + 0x0053, # ſ -> S + 0x0243, # ƀ -> Ƀ + 0x0182, # ƃ -> Ƃ + 0x0184, # ƅ -> Ƅ + 0x0187, # ƈ -> Ƈ + 0x018B, # ƌ -> Ƌ + 0x0191, # ƒ -> Ƒ + 0x01F6, # ƕ -> Ƕ + 0x0198, # ƙ -> Ƙ + 0x023D, # ƚ -> Ƚ + 0x0220, # ƞ -> Ƞ + 0x01A0, # ơ -> Ơ + 0x01A2, # ƣ -> Ƣ + 0x01A4, # ƥ -> Ƥ + 0x01A7, # ƨ -> Ƨ + 0x01AC, # ƭ -> Ƭ + 0x01AF, # ư -> Ư + 0x01B3, # ƴ -> Ƴ + 0x01B5, # ƶ -> Ƶ + 0x01B8, # ƹ -> Ƹ + 0x01BC, # ƽ -> Ƽ + 0x01F7, # ƿ -> Ƿ + 0x01C4, # Dž -> DŽ + 0x01C4, # dž -> DŽ + 0x01C7, # Lj -> LJ + 0x01C7, # lj -> LJ + 0x01CA, # Nj -> NJ + 0x01CA, # nj -> NJ + 0x01CD, # ǎ -> Ǎ + 0x01CF, # ǐ -> Ǐ + 0x01D1, # ǒ -> Ǒ + 0x01D3, # ǔ -> Ǔ + 0x01D5, # ǖ -> Ǖ + 0x01D7, # ǘ -> Ǘ + 0x01D9, # ǚ -> Ǚ + 0x01DB, # ǜ -> Ǜ + 0x018E, # ǝ -> Ǝ + 0x01DE, # ǟ -> Ǟ + 0x01E0, # ǡ -> Ǡ + 0x01E2, # ǣ -> Ǣ + 0x01E4, # ǥ -> Ǥ + 0x01E6, # ǧ -> Ǧ + 0x01E8, # ǩ -> Ǩ + 0x01EA, # ǫ -> Ǫ + 0x01EC, # ǭ -> Ǭ + 0x01EE, # ǯ -> Ǯ + 0x01F1, # Dz -> DZ + 0x01F1, # dz -> DZ + 0x01F4, # ǵ -> Ǵ + 0x01F8, # ǹ -> Ǹ + 0x01FA, # ǻ -> Ǻ + 0x01FC, # ǽ -> Ǽ + 0x01FE, # ǿ -> Ǿ + 0x0200, # ȁ -> Ȁ + 0x0202, # ȃ -> Ȃ + 0x0204, # ȅ -> Ȅ + 0x0206, # ȇ -> Ȇ + 0x0208, # ȉ -> Ȉ + 0x020A, # ȋ -> Ȋ + 0x020C, # ȍ -> Ȍ + 0x020E, # ȏ -> Ȏ + 0x0210, # ȑ -> Ȑ + 0x0212, # ȓ -> Ȓ + 0x0214, # ȕ -> Ȕ + 0x0216, # ȗ -> Ȗ + 0x0218, # ș -> Ș + 0x021A, # ț -> Ț + 0x021C, # ȝ -> Ȝ + 0x021E, # ȟ -> Ȟ + 0x0222, # ȣ -> Ȣ + 0x0224, # ȥ -> Ȥ + 0x0226, # ȧ -> Ȧ + 0x0228, # ȩ -> Ȩ + 0x022A, # ȫ -> Ȫ + 0x022C, # ȭ -> Ȭ + 0x022E, # ȯ -> Ȯ + 0x0230, # ȱ -> Ȱ + 0x0232, # ȳ -> Ȳ + 0x023B, # ȼ -> Ȼ + 0x2C7E, # ȿ -> Ȿ + 0x2C7F, # ɀ -> Ɀ + 0x0241, # ɂ -> Ɂ + 0x0246, # ɇ -> Ɇ + 0x0248, # ɉ -> Ɉ + 0x024A, # ɋ -> Ɋ + 0x024C, # ɍ -> Ɍ + 0x024E, # ɏ -> Ɏ + 0x2C6F, # ɐ -> Ɐ + 0x2C6D, # ɑ -> Ɑ + 0x2C70, # ɒ -> Ɒ + 0x0181, # ɓ -> Ɓ + 0x0186, # ɔ -> Ɔ + 0x0189, # ɖ -> Ɖ + 0x018A, # ɗ -> Ɗ + 0x018F, # ə -> Ə + 0x0190, # ɛ -> Ɛ + 0xA7AB, # ɜ -> Ɜ + 0x0193, # ɠ -> Ɠ + 0xA7AC, # ɡ -> Ɡ + 0x0194, # ɣ -> Ɣ + 0xA78D, # ɥ -> Ɥ + 0xA7AA, # ɦ -> Ɦ + 0x0197, # ɨ -> Ɨ + 0x0196, # ɩ -> Ɩ + 0xA7AE, # ɪ -> Ɪ + 0x2C62, # ɫ -> Ɫ + 0xA7AD, # ɬ -> Ɬ + 0x019C, # ɯ -> Ɯ + 0x2C6E, # ɱ -> Ɱ + 0x019D, # ɲ -> Ɲ + 0x019F, # ɵ -> Ɵ + 0x2C64, # ɽ -> Ɽ + 0x01A6, # ʀ -> Ʀ + 0xA7C5, # ʂ -> Ʂ + 0x01A9, # ʃ -> Ʃ + 0xA7B1, # ʇ -> Ʇ + 0x01AE, # ʈ -> Ʈ + 0x0244, # ʉ -> Ʉ + 0x01B1, # ʊ -> Ʊ + 0x01B2, # ʋ -> Ʋ + 0x0245, # ʌ -> Ʌ + 0x01B7, # ʒ -> Ʒ + 0xA7B2, # ʝ -> Ʝ + 0xA7B0, # ʞ -> Ʞ + 0x0399, # ͅ -> Ι + 0x0370, # ͱ -> Ͱ + 0x0372, # ͳ -> Ͳ + 0x0376, # ͷ -> Ͷ + 0x03FD, # ͻ -> Ͻ + 0x03FE, # ͼ -> Ͼ + 0x03FF, # ͽ -> Ͽ + 0x0386, # ά -> Ά + 0x0388, # έ -> Έ + 0x0389, # ή -> Ή + 0x038A, # ί -> Ί + 0x0391, # α -> Α + 0x0392, # β -> Β + 0x0393, # γ -> Γ + 0x0394, # δ -> Δ + 0x0395, # ε -> Ε + 0x0396, # ζ -> Ζ + 0x0397, # η -> Η + 0x0398, # θ -> Θ + 0x0399, # ι -> Ι + 0x039A, # κ -> Κ + 0x039B, # λ -> Λ + 0x039C, # μ -> Μ + 0x039D, # ν -> Ν + 0x039E, # ξ -> Ξ + 0x039F, # ο -> Ο + 0x03A0, # π -> Π + 0x03A1, # ρ -> Ρ + 0x03A3, # ς -> Σ + 0x03A3, # σ -> Σ + 0x03A4, # τ -> Τ + 0x03A5, # υ -> Υ + 0x03A6, # φ -> Φ + 0x03A7, # χ -> Χ + 0x03A8, # ψ -> Ψ + 0x03A9, # ω -> Ω + 0x03AA, # ϊ -> Ϊ + 0x03AB, # ϋ -> Ϋ + 0x038C, # ό -> Ό + 0x038E, # ύ -> Ύ + 0x038F, # ώ -> Ώ + 0x0392, # ϐ -> Β + 0x0398, # ϑ -> Θ + 0x03A6, # ϕ -> Φ + 0x03A0, # ϖ -> Π + 0x03CF, # ϗ -> Ϗ + 0x03D8, # ϙ -> Ϙ + 0x03DA, # ϛ -> Ϛ + 0x03DC, # ϝ -> Ϝ + 0x03DE, # ϟ -> Ϟ + 0x03E0, # ϡ -> Ϡ + 0x03E2, # ϣ -> Ϣ + 0x03E4, # ϥ -> Ϥ + 0x03E6, # ϧ -> Ϧ + 0x03E8, # ϩ -> Ϩ + 0x03EA, # ϫ -> Ϫ + 0x03EC, # ϭ -> Ϭ + 0x03EE, # ϯ -> Ϯ + 0x039A, # ϰ -> Κ + 0x03A1, # ϱ -> Ρ + 0x03F9, # ϲ -> Ϲ + 0x037F, # ϳ -> Ϳ + 0x0395, # ϵ -> Ε + 0x03F7, # ϸ -> Ϸ + 0x03FA, # ϻ -> Ϻ + 0x0410, # а -> А + 0x0411, # б -> Б + 0x0412, # в -> В + 0x0413, # г -> Г + 0x0414, # д -> Д + 0x0415, # е -> Е + 0x0416, # ж -> Ж + 0x0417, # з -> З + 0x0418, # и -> И + 0x0419, # й -> Й + 0x041A, # к -> К + 0x041B, # л -> Л + 0x041C, # м -> М + 0x041D, # н -> Н + 0x041E, # о -> О + 0x041F, # п -> П + 0x0420, # р -> Р + 0x0421, # с -> С + 0x0422, # т -> Т + 0x0423, # у -> У + 0x0424, # ф -> Ф + 0x0425, # х -> Х + 0x0426, # ц -> Ц + 0x0427, # ч -> Ч + 0x0428, # ш -> Ш + 0x0429, # щ -> Щ + 0x042A, # ъ -> Ъ + 0x042B, # ы -> Ы + 0x042C, # ь -> Ь + 0x042D, # э -> Э + 0x042E, # ю -> Ю + 0x042F, # я -> Я + 0x0400, # ѐ -> Ѐ + 0x0401, # ё -> Ё + 0x0402, # ђ -> Ђ + 0x0403, # ѓ -> Ѓ + 0x0404, # є -> Є + 0x0405, # ѕ -> Ѕ + 0x0406, # і -> І + 0x0407, # ї -> Ї + 0x0408, # ј -> Ј + 0x0409, # љ -> Љ + 0x040A, # њ -> Њ + 0x040B, # ћ -> Ћ + 0x040C, # ќ -> Ќ + 0x040D, # ѝ -> Ѝ + 0x040E, # ў -> Ў + 0x040F, # џ -> Џ + 0x0460, # ѡ -> Ѡ + 0x0462, # ѣ -> Ѣ + 0x0464, # ѥ -> Ѥ + 0x0466, # ѧ -> Ѧ + 0x0468, # ѩ -> Ѩ + 0x046A, # ѫ -> Ѫ + 0x046C, # ѭ -> Ѭ + 0x046E, # ѯ -> Ѯ + 0x0470, # ѱ -> Ѱ + 0x0472, # ѳ -> Ѳ + 0x0474, # ѵ -> Ѵ + 0x0476, # ѷ -> Ѷ + 0x0478, # ѹ -> Ѹ + 0x047A, # ѻ -> Ѻ + 0x047C, # ѽ -> Ѽ + 0x047E, # ѿ -> Ѿ + 0x0480, # ҁ -> Ҁ + 0x048A, # ҋ -> Ҋ + 0x048C, # ҍ -> Ҍ + 0x048E, # ҏ -> Ҏ + 0x0490, # ґ -> Ґ + 0x0492, # ғ -> Ғ + 0x0494, # ҕ -> Ҕ + 0x0496, # җ -> Җ + 0x0498, # ҙ -> Ҙ + 0x049A, # қ -> Қ + 0x049C, # ҝ -> Ҝ + 0x049E, # ҟ -> Ҟ + 0x04A0, # ҡ -> Ҡ + 0x04A2, # ң -> Ң + 0x04A4, # ҥ -> Ҥ + 0x04A6, # ҧ -> Ҧ + 0x04A8, # ҩ -> Ҩ + 0x04AA, # ҫ -> Ҫ + 0x04AC, # ҭ -> Ҭ + 0x04AE, # ү -> Ү + 0x04B0, # ұ -> Ұ + 0x04B2, # ҳ -> Ҳ + 0x04B4, # ҵ -> Ҵ + 0x04B6, # ҷ -> Ҷ + 0x04B8, # ҹ -> Ҹ + 0x04BA, # һ -> Һ + 0x04BC, # ҽ -> Ҽ + 0x04BE, # ҿ -> Ҿ + 0x04C1, # ӂ -> Ӂ + 0x04C3, # ӄ -> Ӄ + 0x04C5, # ӆ -> Ӆ + 0x04C7, # ӈ -> Ӈ + 0x04C9, # ӊ -> Ӊ + 0x04CB, # ӌ -> Ӌ + 0x04CD, # ӎ -> Ӎ + 0x04C0, # ӏ -> Ӏ + 0x04D0, # ӑ -> Ӑ + 0x04D2, # ӓ -> Ӓ + 0x04D4, # ӕ -> Ӕ + 0x04D6, # ӗ -> Ӗ + 0x04D8, # ә -> Ә + 0x04DA, # ӛ -> Ӛ + 0x04DC, # ӝ -> Ӝ + 0x04DE, # ӟ -> Ӟ + 0x04E0, # ӡ -> Ӡ + 0x04E2, # ӣ -> Ӣ + 0x04E4, # ӥ -> Ӥ + 0x04E6, # ӧ -> Ӧ + 0x04E8, # ө -> Ө + 0x04EA, # ӫ -> Ӫ + 0x04EC, # ӭ -> Ӭ + 0x04EE, # ӯ -> Ӯ + 0x04F0, # ӱ -> Ӱ + 0x04F2, # ӳ -> Ӳ + 0x04F4, # ӵ -> Ӵ + 0x04F6, # ӷ -> Ӷ + 0x04F8, # ӹ -> Ӹ + 0x04FA, # ӻ -> Ӻ + 0x04FC, # ӽ -> Ӽ + 0x04FE, # ӿ -> Ӿ + 0x0500, # ԁ -> Ԁ + 0x0502, # ԃ -> Ԃ + 0x0504, # ԅ -> Ԅ + 0x0506, # ԇ -> Ԇ + 0x0508, # ԉ -> Ԉ + 0x050A, # ԋ -> Ԋ + 0x050C, # ԍ -> Ԍ + 0x050E, # ԏ -> Ԏ + 0x0510, # ԑ -> Ԑ + 0x0512, # ԓ -> Ԓ + 0x0514, # ԕ -> Ԕ + 0x0516, # ԗ -> Ԗ + 0x0518, # ԙ -> Ԙ + 0x051A, # ԛ -> Ԛ + 0x051C, # ԝ -> Ԝ + 0x051E, # ԟ -> Ԟ + 0x0520, # ԡ -> Ԡ + 0x0522, # ԣ -> Ԣ + 0x0524, # ԥ -> Ԥ + 0x0526, # ԧ -> Ԧ + 0x0528, # ԩ -> Ԩ + 0x052A, # ԫ -> Ԫ + 0x052C, # ԭ -> Ԭ + 0x052E, # ԯ -> Ԯ + 0x0531, # ա -> Ա + 0x0532, # բ -> Բ + 0x0533, # գ -> Գ + 0x0534, # դ -> Դ + 0x0535, # ե -> Ե + 0x0536, # զ -> Զ + 0x0537, # է -> Է + 0x0538, # ը -> Ը + 0x0539, # թ -> Թ + 0x053A, # ժ -> Ժ + 0x053B, # ի -> Ի + 0x053C, # լ -> Լ + 0x053D, # խ -> Խ + 0x053E, # ծ -> Ծ + 0x053F, # կ -> Կ + 0x0540, # հ -> Հ + 0x0541, # ձ -> Ձ + 0x0542, # ղ -> Ղ + 0x0543, # ճ -> Ճ + 0x0544, # մ -> Մ + 0x0545, # յ -> Յ + 0x0546, # ն -> Ն + 0x0547, # շ -> Շ + 0x0548, # ո -> Ո + 0x0549, # չ -> Չ + 0x054A, # պ -> Պ + 0x054B, # ջ -> Ջ + 0x054C, # ռ -> Ռ + 0x054D, # ս -> Ս + 0x054E, # վ -> Վ + 0x054F, # տ -> Տ + 0x0550, # ր -> Ր + 0x0551, # ց -> Ց + 0x0552, # ւ -> Ւ + 0x0553, # փ -> Փ + 0x0554, # ք -> Ք + 0x0555, # օ -> Օ + 0x0556, # ֆ -> Ֆ + 0x1C90, # ა -> Ა + 0x1C91, # ბ -> Ბ + 0x1C92, # გ -> Გ + 0x1C93, # დ -> Დ + 0x1C94, # ე -> Ე + 0x1C95, # ვ -> Ვ + 0x1C96, # ზ -> Ზ + 0x1C97, # თ -> Თ + 0x1C98, # ი -> Ი + 0x1C99, # კ -> Კ + 0x1C9A, # ლ -> Ლ + 0x1C9B, # მ -> Მ + 0x1C9C, # ნ -> Ნ + 0x1C9D, # ო -> Ო + 0x1C9E, # პ -> Პ + 0x1C9F, # ჟ -> Ჟ + 0x1CA0, # რ -> Რ + 0x1CA1, # ს -> Ს + 0x1CA2, # ტ -> Ტ + 0x1CA3, # უ -> Უ + 0x1CA4, # ფ -> Ფ + 0x1CA5, # ქ -> Ქ + 0x1CA6, # ღ -> Ღ + 0x1CA7, # ყ -> Ყ + 0x1CA8, # შ -> Შ + 0x1CA9, # ჩ -> Ჩ + 0x1CAA, # ც -> Ც + 0x1CAB, # ძ -> Ძ + 0x1CAC, # წ -> Წ + 0x1CAD, # ჭ -> Ჭ + 0x1CAE, # ხ -> Ხ + 0x1CAF, # ჯ -> Ჯ + 0x1CB0, # ჰ -> Ჰ + 0x1CB1, # ჱ -> Ჱ + 0x1CB2, # ჲ -> Ჲ + 0x1CB3, # ჳ -> Ჳ + 0x1CB4, # ჴ -> Ჴ + 0x1CB5, # ჵ -> Ჵ + 0x1CB6, # ჶ -> Ჶ + 0x1CB7, # ჷ -> Ჷ + 0x1CB8, # ჸ -> Ჸ + 0x1CB9, # ჹ -> Ჹ + 0x1CBA, # ჺ -> Ჺ + 0x1CBD, # ჽ -> Ჽ + 0x1CBE, # ჾ -> Ჾ + 0x1CBF, # ჿ -> Ჿ + 0x13F0, # ᏸ -> Ᏸ + 0x13F1, # ᏹ -> Ᏹ + 0x13F2, # ᏺ -> Ᏺ + 0x13F3, # ᏻ -> Ᏻ + 0x13F4, # ᏼ -> Ᏼ + 0x13F5, # ᏽ -> Ᏽ + 0x0412, # ᲀ -> В + 0x0414, # ᲁ -> Д + 0x041E, # ᲂ -> О + 0x0421, # ᲃ -> С + 0x0422, # ᲄ -> Т + 0x0422, # ᲅ -> Т + 0x042A, # ᲆ -> Ъ + 0x0462, # ᲇ -> Ѣ + 0xA64A, # ᲈ -> Ꙋ + 0xA77D, # ᵹ -> Ᵹ + 0x2C63, # ᵽ -> Ᵽ + 0xA7C6, # ᶎ -> Ᶎ + 0x1E00, # ḁ -> Ḁ + 0x1E02, # ḃ -> Ḃ + 0x1E04, # ḅ -> Ḅ + 0x1E06, # ḇ -> Ḇ + 0x1E08, # ḉ -> Ḉ + 0x1E0A, # ḋ -> Ḋ + 0x1E0C, # ḍ -> Ḍ + 0x1E0E, # ḏ -> Ḏ + 0x1E10, # ḑ -> Ḑ + 0x1E12, # ḓ -> Ḓ + 0x1E14, # ḕ -> Ḕ + 0x1E16, # ḗ -> Ḗ + 0x1E18, # ḙ -> Ḙ + 0x1E1A, # ḛ -> Ḛ + 0x1E1C, # ḝ -> Ḝ + 0x1E1E, # ḟ -> Ḟ + 0x1E20, # ḡ -> Ḡ + 0x1E22, # ḣ -> Ḣ + 0x1E24, # ḥ -> Ḥ + 0x1E26, # ḧ -> Ḧ + 0x1E28, # ḩ -> Ḩ + 0x1E2A, # ḫ -> Ḫ + 0x1E2C, # ḭ -> Ḭ + 0x1E2E, # ḯ -> Ḯ + 0x1E30, # ḱ -> Ḱ + 0x1E32, # ḳ -> Ḳ + 0x1E34, # ḵ -> Ḵ + 0x1E36, # ḷ -> Ḷ + 0x1E38, # ḹ -> Ḹ + 0x1E3A, # ḻ -> Ḻ + 0x1E3C, # ḽ -> Ḽ + 0x1E3E, # ḿ -> Ḿ + 0x1E40, # ṁ -> Ṁ + 0x1E42, # ṃ -> Ṃ + 0x1E44, # ṅ -> Ṅ + 0x1E46, # ṇ -> Ṇ + 0x1E48, # ṉ -> Ṉ + 0x1E4A, # ṋ -> Ṋ + 0x1E4C, # ṍ -> Ṍ + 0x1E4E, # ṏ -> Ṏ + 0x1E50, # ṑ -> Ṑ + 0x1E52, # ṓ -> Ṓ + 0x1E54, # ṕ -> Ṕ + 0x1E56, # ṗ -> Ṗ + 0x1E58, # ṙ -> Ṙ + 0x1E5A, # ṛ -> Ṛ + 0x1E5C, # ṝ -> Ṝ + 0x1E5E, # ṟ -> Ṟ + 0x1E60, # ṡ -> Ṡ + 0x1E62, # ṣ -> Ṣ + 0x1E64, # ṥ -> Ṥ + 0x1E66, # ṧ -> Ṧ + 0x1E68, # ṩ -> Ṩ + 0x1E6A, # ṫ -> Ṫ + 0x1E6C, # ṭ -> Ṭ + 0x1E6E, # ṯ -> Ṯ + 0x1E70, # ṱ -> Ṱ + 0x1E72, # ṳ -> Ṳ + 0x1E74, # ṵ -> Ṵ + 0x1E76, # ṷ -> Ṷ + 0x1E78, # ṹ -> Ṹ + 0x1E7A, # ṻ -> Ṻ + 0x1E7C, # ṽ -> Ṽ + 0x1E7E, # ṿ -> Ṿ + 0x1E80, # ẁ -> Ẁ + 0x1E82, # ẃ -> Ẃ + 0x1E84, # ẅ -> Ẅ + 0x1E86, # ẇ -> Ẇ + 0x1E88, # ẉ -> Ẉ + 0x1E8A, # ẋ -> Ẋ + 0x1E8C, # ẍ -> Ẍ + 0x1E8E, # ẏ -> Ẏ + 0x1E90, # ẑ -> Ẑ + 0x1E92, # ẓ -> Ẓ + 0x1E94, # ẕ -> Ẕ + 0x1E60, # ẛ -> Ṡ + 0x1EA0, # ạ -> Ạ + 0x1EA2, # ả -> Ả + 0x1EA4, # ấ -> Ấ + 0x1EA6, # ầ -> Ầ + 0x1EA8, # ẩ -> Ẩ + 0x1EAA, # ẫ -> Ẫ + 0x1EAC, # ậ -> Ậ + 0x1EAE, # ắ -> Ắ + 0x1EB0, # ằ -> Ằ + 0x1EB2, # ẳ -> Ẳ + 0x1EB4, # ẵ -> Ẵ + 0x1EB6, # ặ -> Ặ + 0x1EB8, # ẹ -> Ẹ + 0x1EBA, # ẻ -> Ẻ + 0x1EBC, # ẽ -> Ẽ + 0x1EBE, # ế -> Ế + 0x1EC0, # ề -> Ề + 0x1EC2, # ể -> Ể + 0x1EC4, # ễ -> Ễ + 0x1EC6, # ệ -> Ệ + 0x1EC8, # ỉ -> Ỉ + 0x1ECA, # ị -> Ị + 0x1ECC, # ọ -> Ọ + 0x1ECE, # ỏ -> Ỏ + 0x1ED0, # ố -> Ố + 0x1ED2, # ồ -> Ồ + 0x1ED4, # ổ -> Ổ + 0x1ED6, # ỗ -> Ỗ + 0x1ED8, # ộ -> Ộ + 0x1EDA, # ớ -> Ớ + 0x1EDC, # ờ -> Ờ + 0x1EDE, # ở -> Ở + 0x1EE0, # ỡ -> Ỡ + 0x1EE2, # ợ -> Ợ + 0x1EE4, # ụ -> Ụ + 0x1EE6, # ủ -> Ủ + 0x1EE8, # ứ -> Ứ + 0x1EEA, # ừ -> Ừ + 0x1EEC, # ử -> Ử + 0x1EEE, # ữ -> Ữ + 0x1EF0, # ự -> Ự + 0x1EF2, # ỳ -> Ỳ + 0x1EF4, # ỵ -> Ỵ + 0x1EF6, # ỷ -> Ỷ + 0x1EF8, # ỹ -> Ỹ + 0x1EFA, # ỻ -> Ỻ + 0x1EFC, # ỽ -> Ỽ + 0x1EFE, # ỿ -> Ỿ + 0x1F08, # ἀ -> Ἀ + 0x1F09, # ἁ -> Ἁ + 0x1F0A, # ἂ -> Ἂ + 0x1F0B, # ἃ -> Ἃ + 0x1F0C, # ἄ -> Ἄ + 0x1F0D, # ἅ -> Ἅ + 0x1F0E, # ἆ -> Ἆ + 0x1F0F, # ἇ -> Ἇ + 0x1F18, # ἐ -> Ἐ + 0x1F19, # ἑ -> Ἑ + 0x1F1A, # ἒ -> Ἒ + 0x1F1B, # ἓ -> Ἓ + 0x1F1C, # ἔ -> Ἔ + 0x1F1D, # ἕ -> Ἕ + 0x1F28, # ἠ -> Ἠ + 0x1F29, # ἡ -> Ἡ + 0x1F2A, # ἢ -> Ἢ + 0x1F2B, # ἣ -> Ἣ + 0x1F2C, # ἤ -> Ἤ + 0x1F2D, # ἥ -> Ἥ + 0x1F2E, # ἦ -> Ἦ + 0x1F2F, # ἧ -> Ἧ + 0x1F38, # ἰ -> Ἰ + 0x1F39, # ἱ -> Ἱ + 0x1F3A, # ἲ -> Ἲ + 0x1F3B, # ἳ -> Ἳ + 0x1F3C, # ἴ -> Ἴ + 0x1F3D, # ἵ -> Ἵ + 0x1F3E, # ἶ -> Ἶ + 0x1F3F, # ἷ -> Ἷ + 0x1F48, # ὀ -> Ὀ + 0x1F49, # ὁ -> Ὁ + 0x1F4A, # ὂ -> Ὂ + 0x1F4B, # ὃ -> Ὃ + 0x1F4C, # ὄ -> Ὄ + 0x1F4D, # ὅ -> Ὅ + 0x1F59, # ὑ -> Ὑ + 0x1F5B, # ὓ -> Ὓ + 0x1F5D, # ὕ -> Ὕ + 0x1F5F, # ὗ -> Ὗ + 0x1F68, # ὠ -> Ὠ + 0x1F69, # ὡ -> Ὡ + 0x1F6A, # ὢ -> Ὢ + 0x1F6B, # ὣ -> Ὣ + 0x1F6C, # ὤ -> Ὤ + 0x1F6D, # ὥ -> Ὥ + 0x1F6E, # ὦ -> Ὦ + 0x1F6F, # ὧ -> Ὧ + 0x1FBA, # ὰ -> Ὰ + 0x1FBB, # ά -> Ά + 0x1FC8, # ὲ -> Ὲ + 0x1FC9, # έ -> Έ + 0x1FCA, # ὴ -> Ὴ + 0x1FCB, # ή -> Ή + 0x1FDA, # ὶ -> Ὶ + 0x1FDB, # ί -> Ί + 0x1FF8, # ὸ -> Ὸ + 0x1FF9, # ό -> Ό + 0x1FEA, # ὺ -> Ὺ + 0x1FEB, # ύ -> Ύ + 0x1FFA, # ὼ -> Ὼ + 0x1FFB, # ώ -> Ώ + 0x1F88, # ᾀ -> ᾈ + 0x1F89, # ᾁ -> ᾉ + 0x1F8A, # ᾂ -> ᾊ + 0x1F8B, # ᾃ -> ᾋ + 0x1F8C, # ᾄ -> ᾌ + 0x1F8D, # ᾅ -> ᾍ + 0x1F8E, # ᾆ -> ᾎ + 0x1F8F, # ᾇ -> ᾏ + 0x1F98, # ᾐ -> ᾘ + 0x1F99, # ᾑ -> ᾙ + 0x1F9A, # ᾒ -> ᾚ + 0x1F9B, # ᾓ -> ᾛ + 0x1F9C, # ᾔ -> ᾜ + 0x1F9D, # ᾕ -> ᾝ + 0x1F9E, # ᾖ -> ᾞ + 0x1F9F, # ᾗ -> ᾟ + 0x1FA8, # ᾠ -> ᾨ + 0x1FA9, # ᾡ -> ᾩ + 0x1FAA, # ᾢ -> ᾪ + 0x1FAB, # ᾣ -> ᾫ + 0x1FAC, # ᾤ -> ᾬ + 0x1FAD, # ᾥ -> ᾭ + 0x1FAE, # ᾦ -> ᾮ + 0x1FAF, # ᾧ -> ᾯ + 0x1FB8, # ᾰ -> Ᾰ + 0x1FB9, # ᾱ -> Ᾱ + 0x1FBC, # ᾳ -> ᾼ + 0x0399, # ι -> Ι + 0x1FCC, # ῃ -> ῌ + 0x1FD8, # ῐ -> Ῐ + 0x1FD9, # ῑ -> Ῑ + 0x1FE8, # ῠ -> Ῠ + 0x1FE9, # ῡ -> Ῡ + 0x1FEC, # ῥ -> Ῥ + 0x1FFC, # ῳ -> ῼ + 0x2132, # ⅎ -> Ⅎ + 0x2160, # ⅰ -> Ⅰ + 0x2161, # ⅱ -> Ⅱ + 0x2162, # ⅲ -> Ⅲ + 0x2163, # ⅳ -> Ⅳ + 0x2164, # ⅴ -> Ⅴ + 0x2165, # ⅵ -> Ⅵ + 0x2166, # ⅶ -> Ⅶ + 0x2167, # ⅷ -> Ⅷ + 0x2168, # ⅸ -> Ⅸ + 0x2169, # ⅹ -> Ⅹ + 0x216A, # ⅺ -> Ⅺ + 0x216B, # ⅻ -> Ⅻ + 0x216C, # ⅼ -> Ⅼ + 0x216D, # ⅽ -> Ⅽ + 0x216E, # ⅾ -> Ⅾ + 0x216F, # ⅿ -> Ⅿ + 0x2183, # ↄ -> Ↄ + 0x24B6, # ⓐ -> Ⓐ + 0x24B7, # ⓑ -> Ⓑ + 0x24B8, # ⓒ -> Ⓒ + 0x24B9, # ⓓ -> Ⓓ + 0x24BA, # ⓔ -> Ⓔ + 0x24BB, # ⓕ -> Ⓕ + 0x24BC, # ⓖ -> Ⓖ + 0x24BD, # ⓗ -> Ⓗ + 0x24BE, # ⓘ -> Ⓘ + 0x24BF, # ⓙ -> Ⓙ + 0x24C0, # ⓚ -> Ⓚ + 0x24C1, # ⓛ -> Ⓛ + 0x24C2, # ⓜ -> Ⓜ + 0x24C3, # ⓝ -> Ⓝ + 0x24C4, # ⓞ -> Ⓞ + 0x24C5, # ⓟ -> Ⓟ + 0x24C6, # ⓠ -> Ⓠ + 0x24C7, # ⓡ -> Ⓡ + 0x24C8, # ⓢ -> Ⓢ + 0x24C9, # ⓣ -> Ⓣ + 0x24CA, # ⓤ -> Ⓤ + 0x24CB, # ⓥ -> Ⓥ + 0x24CC, # ⓦ -> Ⓦ + 0x24CD, # ⓧ -> Ⓧ + 0x24CE, # ⓨ -> Ⓨ + 0x24CF, # ⓩ -> Ⓩ + 0x2C00, # ⰰ -> Ⰰ + 0x2C01, # ⰱ -> Ⰱ + 0x2C02, # ⰲ -> Ⰲ + 0x2C03, # ⰳ -> Ⰳ + 0x2C04, # ⰴ -> Ⰴ + 0x2C05, # ⰵ -> Ⰵ + 0x2C06, # ⰶ -> Ⰶ + 0x2C07, # ⰷ -> Ⰷ + 0x2C08, # ⰸ -> Ⰸ + 0x2C09, # ⰹ -> Ⰹ + 0x2C0A, # ⰺ -> Ⰺ + 0x2C0B, # ⰻ -> Ⰻ + 0x2C0C, # ⰼ -> Ⰼ + 0x2C0D, # ⰽ -> Ⰽ + 0x2C0E, # ⰾ -> Ⰾ + 0x2C0F, # ⰿ -> Ⰿ + 0x2C10, # ⱀ -> Ⱀ + 0x2C11, # ⱁ -> Ⱁ + 0x2C12, # ⱂ -> Ⱂ + 0x2C13, # ⱃ -> Ⱃ + 0x2C14, # ⱄ -> Ⱄ + 0x2C15, # ⱅ -> Ⱅ + 0x2C16, # ⱆ -> Ⱆ + 0x2C17, # ⱇ -> Ⱇ + 0x2C18, # ⱈ -> Ⱈ + 0x2C19, # ⱉ -> Ⱉ + 0x2C1A, # ⱊ -> Ⱊ + 0x2C1B, # ⱋ -> Ⱋ + 0x2C1C, # ⱌ -> Ⱌ + 0x2C1D, # ⱍ -> Ⱍ + 0x2C1E, # ⱎ -> Ⱎ + 0x2C1F, # ⱏ -> Ⱏ + 0x2C20, # ⱐ -> Ⱐ + 0x2C21, # ⱑ -> Ⱑ + 0x2C22, # ⱒ -> Ⱒ + 0x2C23, # ⱓ -> Ⱓ + 0x2C24, # ⱔ -> Ⱔ + 0x2C25, # ⱕ -> Ⱕ + 0x2C26, # ⱖ -> Ⱖ + 0x2C27, # ⱗ -> Ⱗ + 0x2C28, # ⱘ -> Ⱘ + 0x2C29, # ⱙ -> Ⱙ + 0x2C2A, # ⱚ -> Ⱚ + 0x2C2B, # ⱛ -> Ⱛ + 0x2C2C, # ⱜ -> Ⱜ + 0x2C2D, # ⱝ -> Ⱝ + 0x2C2E, # ⱞ -> Ⱞ + 0x2C2F, # ⱟ -> Ⱟ + 0x2C60, # ⱡ -> Ⱡ + 0x023A, # ⱥ -> Ⱥ + 0x023E, # ⱦ -> Ⱦ + 0x2C67, # ⱨ -> Ⱨ + 0x2C69, # ⱪ -> Ⱪ + 0x2C6B, # ⱬ -> Ⱬ + 0x2C72, # ⱳ -> Ⱳ + 0x2C75, # ⱶ -> Ⱶ + 0x2C80, # ⲁ -> Ⲁ + 0x2C82, # ⲃ -> Ⲃ + 0x2C84, # ⲅ -> Ⲅ + 0x2C86, # ⲇ -> Ⲇ + 0x2C88, # ⲉ -> Ⲉ + 0x2C8A, # ⲋ -> Ⲋ + 0x2C8C, # ⲍ -> Ⲍ + 0x2C8E, # ⲏ -> Ⲏ + 0x2C90, # ⲑ -> Ⲑ + 0x2C92, # ⲓ -> Ⲓ + 0x2C94, # ⲕ -> Ⲕ + 0x2C96, # ⲗ -> Ⲗ + 0x2C98, # ⲙ -> Ⲙ + 0x2C9A, # ⲛ -> Ⲛ + 0x2C9C, # ⲝ -> Ⲝ + 0x2C9E, # ⲟ -> Ⲟ + 0x2CA0, # ⲡ -> Ⲡ + 0x2CA2, # ⲣ -> Ⲣ + 0x2CA4, # ⲥ -> Ⲥ + 0x2CA6, # ⲧ -> Ⲧ + 0x2CA8, # ⲩ -> Ⲩ + 0x2CAA, # ⲫ -> Ⲫ + 0x2CAC, # ⲭ -> Ⲭ + 0x2CAE, # ⲯ -> Ⲯ + 0x2CB0, # ⲱ -> Ⲱ + 0x2CB2, # ⲳ -> Ⲳ + 0x2CB4, # ⲵ -> Ⲵ + 0x2CB6, # ⲷ -> Ⲷ + 0x2CB8, # ⲹ -> Ⲹ + 0x2CBA, # ⲻ -> Ⲻ + 0x2CBC, # ⲽ -> Ⲽ + 0x2CBE, # ⲿ -> Ⲿ + 0x2CC0, # ⳁ -> Ⳁ + 0x2CC2, # ⳃ -> Ⳃ + 0x2CC4, # ⳅ -> Ⳅ + 0x2CC6, # ⳇ -> Ⳇ + 0x2CC8, # ⳉ -> Ⳉ + 0x2CCA, # ⳋ -> Ⳋ + 0x2CCC, # ⳍ -> Ⳍ + 0x2CCE, # ⳏ -> Ⳏ + 0x2CD0, # ⳑ -> Ⳑ + 0x2CD2, # ⳓ -> Ⳓ + 0x2CD4, # ⳕ -> Ⳕ + 0x2CD6, # ⳗ -> Ⳗ + 0x2CD8, # ⳙ -> Ⳙ + 0x2CDA, # ⳛ -> Ⳛ + 0x2CDC, # ⳝ -> Ⳝ + 0x2CDE, # ⳟ -> Ⳟ + 0x2CE0, # ⳡ -> Ⳡ + 0x2CE2, # ⳣ -> Ⳣ + 0x2CEB, # ⳬ -> Ⳬ + 0x2CED, # ⳮ -> Ⳮ + 0x2CF2, # ⳳ -> Ⳳ + 0x10A0, # ⴀ -> Ⴀ + 0x10A1, # ⴁ -> Ⴁ + 0x10A2, # ⴂ -> Ⴂ + 0x10A3, # ⴃ -> Ⴃ + 0x10A4, # ⴄ -> Ⴄ + 0x10A5, # ⴅ -> Ⴅ + 0x10A6, # ⴆ -> Ⴆ + 0x10A7, # ⴇ -> Ⴇ + 0x10A8, # ⴈ -> Ⴈ + 0x10A9, # ⴉ -> Ⴉ + 0x10AA, # ⴊ -> Ⴊ + 0x10AB, # ⴋ -> Ⴋ + 0x10AC, # ⴌ -> Ⴌ + 0x10AD, # ⴍ -> Ⴍ + 0x10AE, # ⴎ -> Ⴎ + 0x10AF, # ⴏ -> Ⴏ + 0x10B0, # ⴐ -> Ⴐ + 0x10B1, # ⴑ -> Ⴑ + 0x10B2, # ⴒ -> Ⴒ + 0x10B3, # ⴓ -> Ⴓ + 0x10B4, # ⴔ -> Ⴔ + 0x10B5, # ⴕ -> Ⴕ + 0x10B6, # ⴖ -> Ⴖ + 0x10B7, # ⴗ -> Ⴗ + 0x10B8, # ⴘ -> Ⴘ + 0x10B9, # ⴙ -> Ⴙ + 0x10BA, # ⴚ -> Ⴚ + 0x10BB, # ⴛ -> Ⴛ + 0x10BC, # ⴜ -> Ⴜ + 0x10BD, # ⴝ -> Ⴝ + 0x10BE, # ⴞ -> Ⴞ + 0x10BF, # ⴟ -> Ⴟ + 0x10C0, # ⴠ -> Ⴠ + 0x10C1, # ⴡ -> Ⴡ + 0x10C2, # ⴢ -> Ⴢ + 0x10C3, # ⴣ -> Ⴣ + 0x10C4, # ⴤ -> Ⴤ + 0x10C5, # ⴥ -> Ⴥ + 0x10C7, # ⴧ -> Ⴧ + 0x10CD, # ⴭ -> Ⴭ + 0xA640, # ꙁ -> Ꙁ + 0xA642, # ꙃ -> Ꙃ + 0xA644, # ꙅ -> Ꙅ + 0xA646, # ꙇ -> Ꙇ + 0xA648, # ꙉ -> Ꙉ + 0xA64A, # ꙋ -> Ꙋ + 0xA64C, # ꙍ -> Ꙍ + 0xA64E, # ꙏ -> Ꙏ + 0xA650, # ꙑ -> Ꙑ + 0xA652, # ꙓ -> Ꙓ + 0xA654, # ꙕ -> Ꙕ + 0xA656, # ꙗ -> Ꙗ + 0xA658, # ꙙ -> Ꙙ + 0xA65A, # ꙛ -> Ꙛ + 0xA65C, # ꙝ -> Ꙝ + 0xA65E, # ꙟ -> Ꙟ + 0xA660, # ꙡ -> Ꙡ + 0xA662, # ꙣ -> Ꙣ + 0xA664, # ꙥ -> Ꙥ + 0xA666, # ꙧ -> Ꙧ + 0xA668, # ꙩ -> Ꙩ + 0xA66A, # ꙫ -> Ꙫ + 0xA66C, # ꙭ -> Ꙭ + 0xA680, # ꚁ -> Ꚁ + 0xA682, # ꚃ -> Ꚃ + 0xA684, # ꚅ -> Ꚅ + 0xA686, # ꚇ -> Ꚇ + 0xA688, # ꚉ -> Ꚉ + 0xA68A, # ꚋ -> Ꚋ + 0xA68C, # ꚍ -> Ꚍ + 0xA68E, # ꚏ -> Ꚏ + 0xA690, # ꚑ -> Ꚑ + 0xA692, # ꚓ -> Ꚓ + 0xA694, # ꚕ -> Ꚕ + 0xA696, # ꚗ -> Ꚗ + 0xA698, # ꚙ -> Ꚙ + 0xA69A, # ꚛ -> Ꚛ + 0xA722, # ꜣ -> Ꜣ + 0xA724, # ꜥ -> Ꜥ + 0xA726, # ꜧ -> Ꜧ + 0xA728, # ꜩ -> Ꜩ + 0xA72A, # ꜫ -> Ꜫ + 0xA72C, # ꜭ -> Ꜭ + 0xA72E, # ꜯ -> Ꜯ + 0xA732, # ꜳ -> Ꜳ + 0xA734, # ꜵ -> Ꜵ + 0xA736, # ꜷ -> Ꜷ + 0xA738, # ꜹ -> Ꜹ + 0xA73A, # ꜻ -> Ꜻ + 0xA73C, # ꜽ -> Ꜽ + 0xA73E, # ꜿ -> Ꜿ + 0xA740, # ꝁ -> Ꝁ + 0xA742, # ꝃ -> Ꝃ + 0xA744, # ꝅ -> Ꝅ + 0xA746, # ꝇ -> Ꝇ + 0xA748, # ꝉ -> Ꝉ + 0xA74A, # ꝋ -> Ꝋ + 0xA74C, # ꝍ -> Ꝍ + 0xA74E, # ꝏ -> Ꝏ + 0xA750, # ꝑ -> Ꝑ + 0xA752, # ꝓ -> Ꝓ + 0xA754, # ꝕ -> Ꝕ + 0xA756, # ꝗ -> Ꝗ + 0xA758, # ꝙ -> Ꝙ + 0xA75A, # ꝛ -> Ꝛ + 0xA75C, # ꝝ -> Ꝝ + 0xA75E, # ꝟ -> Ꝟ + 0xA760, # ꝡ -> Ꝡ + 0xA762, # ꝣ -> Ꝣ + 0xA764, # ꝥ -> Ꝥ + 0xA766, # ꝧ -> Ꝧ + 0xA768, # ꝩ -> Ꝩ + 0xA76A, # ꝫ -> Ꝫ + 0xA76C, # ꝭ -> Ꝭ + 0xA76E, # ꝯ -> Ꝯ + 0xA779, # ꝺ -> Ꝺ + 0xA77B, # ꝼ -> Ꝼ + 0xA77E, # ꝿ -> Ꝿ + 0xA780, # ꞁ -> Ꞁ + 0xA782, # ꞃ -> Ꞃ + 0xA784, # ꞅ -> Ꞅ + 0xA786, # ꞇ -> Ꞇ + 0xA78B, # ꞌ -> Ꞌ + 0xA790, # ꞑ -> Ꞑ + 0xA792, # ꞓ -> Ꞓ + 0xA7C4, # ꞔ -> Ꞔ + 0xA796, # ꞗ -> Ꞗ + 0xA798, # ꞙ -> Ꞙ + 0xA79A, # ꞛ -> Ꞛ + 0xA79C, # ꞝ -> Ꞝ + 0xA79E, # ꞟ -> Ꞟ + 0xA7A0, # ꞡ -> Ꞡ + 0xA7A2, # ꞣ -> Ꞣ + 0xA7A4, # ꞥ -> Ꞥ + 0xA7A6, # ꞧ -> Ꞧ + 0xA7A8, # ꞩ -> Ꞩ + 0xA7B4, # ꞵ -> Ꞵ + 0xA7B6, # ꞷ -> Ꞷ + 0xA7B8, # ꞹ -> Ꞹ + 0xA7BA, # ꞻ -> Ꞻ + 0xA7BC, # ꞽ -> Ꞽ + 0xA7BE, # ꞿ -> Ꞿ + 0xA7C0, # ꟁ -> Ꟁ + 0xA7C2, # ꟃ -> Ꟃ + 0xA7C7, # ꟈ -> Ꟈ + 0xA7C9, # ꟊ -> Ꟊ + 0xA7D0, # ꟑ -> Ꟑ + 0xA7D6, # ꟗ -> Ꟗ + 0xA7D8, # ꟙ -> Ꟙ + 0xA7F5, # ꟶ -> Ꟶ + 0xA7B3, # ꭓ -> Ꭓ + 0x13A0, # ꭰ -> Ꭰ + 0x13A1, # ꭱ -> Ꭱ + 0x13A2, # ꭲ -> Ꭲ + 0x13A3, # ꭳ -> Ꭳ + 0x13A4, # ꭴ -> Ꭴ + 0x13A5, # ꭵ -> Ꭵ + 0x13A6, # ꭶ -> Ꭶ + 0x13A7, # ꭷ -> Ꭷ + 0x13A8, # ꭸ -> Ꭸ + 0x13A9, # ꭹ -> Ꭹ + 0x13AA, # ꭺ -> Ꭺ + 0x13AB, # ꭻ -> Ꭻ + 0x13AC, # ꭼ -> Ꭼ + 0x13AD, # ꭽ -> Ꭽ + 0x13AE, # ꭾ -> Ꭾ + 0x13AF, # ꭿ -> Ꭿ + 0x13B0, # ꮀ -> Ꮀ + 0x13B1, # ꮁ -> Ꮁ + 0x13B2, # ꮂ -> Ꮂ + 0x13B3, # ꮃ -> Ꮃ + 0x13B4, # ꮄ -> Ꮄ + 0x13B5, # ꮅ -> Ꮅ + 0x13B6, # ꮆ -> Ꮆ + 0x13B7, # ꮇ -> Ꮇ + 0x13B8, # ꮈ -> Ꮈ + 0x13B9, # ꮉ -> Ꮉ + 0x13BA, # ꮊ -> Ꮊ + 0x13BB, # ꮋ -> Ꮋ + 0x13BC, # ꮌ -> Ꮌ + 0x13BD, # ꮍ -> Ꮍ + 0x13BE, # ꮎ -> Ꮎ + 0x13BF, # ꮏ -> Ꮏ + 0x13C0, # ꮐ -> Ꮐ + 0x13C1, # ꮑ -> Ꮑ + 0x13C2, # ꮒ -> Ꮒ + 0x13C3, # ꮓ -> Ꮓ + 0x13C4, # ꮔ -> Ꮔ + 0x13C5, # ꮕ -> Ꮕ + 0x13C6, # ꮖ -> Ꮖ + 0x13C7, # ꮗ -> Ꮗ + 0x13C8, # ꮘ -> Ꮘ + 0x13C9, # ꮙ -> Ꮙ + 0x13CA, # ꮚ -> Ꮚ + 0x13CB, # ꮛ -> Ꮛ + 0x13CC, # ꮜ -> Ꮜ + 0x13CD, # ꮝ -> Ꮝ + 0x13CE, # ꮞ -> Ꮞ + 0x13CF, # ꮟ -> Ꮟ + 0x13D0, # ꮠ -> Ꮠ + 0x13D1, # ꮡ -> Ꮡ + 0x13D2, # ꮢ -> Ꮢ + 0x13D3, # ꮣ -> Ꮣ + 0x13D4, # ꮤ -> Ꮤ + 0x13D5, # ꮥ -> Ꮥ + 0x13D6, # ꮦ -> Ꮦ + 0x13D7, # ꮧ -> Ꮧ + 0x13D8, # ꮨ -> Ꮨ + 0x13D9, # ꮩ -> Ꮩ + 0x13DA, # ꮪ -> Ꮪ + 0x13DB, # ꮫ -> Ꮫ + 0x13DC, # ꮬ -> Ꮬ + 0x13DD, # ꮭ -> Ꮭ + 0x13DE, # ꮮ -> Ꮮ + 0x13DF, # ꮯ -> Ꮯ + 0x13E0, # ꮰ -> Ꮰ + 0x13E1, # ꮱ -> Ꮱ + 0x13E2, # ꮲ -> Ꮲ + 0x13E3, # ꮳ -> Ꮳ + 0x13E4, # ꮴ -> Ꮴ + 0x13E5, # ꮵ -> Ꮵ + 0x13E6, # ꮶ -> Ꮶ + 0x13E7, # ꮷ -> Ꮷ + 0x13E8, # ꮸ -> Ꮸ + 0x13E9, # ꮹ -> Ꮹ + 0x13EA, # ꮺ -> Ꮺ + 0x13EB, # ꮻ -> Ꮻ + 0x13EC, # ꮼ -> Ꮼ + 0x13ED, # ꮽ -> Ꮽ + 0x13EE, # ꮾ -> Ꮾ + 0x13EF, # ꮿ -> Ꮿ + 0xFF21, # a -> A + 0xFF22, # b -> B + 0xFF23, # c -> C + 0xFF24, # d -> D + 0xFF25, # e -> E + 0xFF26, # f -> F + 0xFF27, # g -> G + 0xFF28, # h -> H + 0xFF29, # i -> I + 0xFF2A, # j -> J + 0xFF2B, # k -> K + 0xFF2C, # l -> L + 0xFF2D, # m -> M + 0xFF2E, # n -> N + 0xFF2F, # o -> O + 0xFF30, # p -> P + 0xFF31, # q -> Q + 0xFF32, # r -> R + 0xFF33, # s -> S + 0xFF34, # t -> T + 0xFF35, # u -> U + 0xFF36, # v -> V + 0xFF37, # w -> W + 0xFF38, # x -> X + 0xFF39, # y -> Y + 0xFF3A, # z -> Z + 0x10400, # 𐐨 -> 𐐀 + 0x10401, # 𐐩 -> 𐐁 + 0x10402, # 𐐪 -> 𐐂 + 0x10403, # 𐐫 -> 𐐃 + 0x10404, # 𐐬 -> 𐐄 + 0x10405, # 𐐭 -> 𐐅 + 0x10406, # 𐐮 -> 𐐆 + 0x10407, # 𐐯 -> 𐐇 + 0x10408, # 𐐰 -> 𐐈 + 0x10409, # 𐐱 -> 𐐉 + 0x1040A, # 𐐲 -> 𐐊 + 0x1040B, # 𐐳 -> 𐐋 + 0x1040C, # 𐐴 -> 𐐌 + 0x1040D, # 𐐵 -> 𐐍 + 0x1040E, # 𐐶 -> 𐐎 + 0x1040F, # 𐐷 -> 𐐏 + 0x10410, # 𐐸 -> 𐐐 + 0x10411, # 𐐹 -> 𐐑 + 0x10412, # 𐐺 -> 𐐒 + 0x10413, # 𐐻 -> 𐐓 + 0x10414, # 𐐼 -> 𐐔 + 0x10415, # 𐐽 -> 𐐕 + 0x10416, # 𐐾 -> 𐐖 + 0x10417, # 𐐿 -> 𐐗 + 0x10418, # 𐑀 -> 𐐘 + 0x10419, # 𐑁 -> 𐐙 + 0x1041A, # 𐑂 -> 𐐚 + 0x1041B, # 𐑃 -> 𐐛 + 0x1041C, # 𐑄 -> 𐐜 + 0x1041D, # 𐑅 -> 𐐝 + 0x1041E, # 𐑆 -> 𐐞 + 0x1041F, # 𐑇 -> 𐐟 + 0x10420, # 𐑈 -> 𐐠 + 0x10421, # 𐑉 -> 𐐡 + 0x10422, # 𐑊 -> 𐐢 + 0x10423, # 𐑋 -> 𐐣 + 0x10424, # 𐑌 -> 𐐤 + 0x10425, # 𐑍 -> 𐐥 + 0x10426, # 𐑎 -> 𐐦 + 0x10427, # 𐑏 -> 𐐧 + 0x104B0, # 𐓘 -> 𐒰 + 0x104B1, # 𐓙 -> 𐒱 + 0x104B2, # 𐓚 -> 𐒲 + 0x104B3, # 𐓛 -> 𐒳 + 0x104B4, # 𐓜 -> 𐒴 + 0x104B5, # 𐓝 -> 𐒵 + 0x104B6, # 𐓞 -> 𐒶 + 0x104B7, # 𐓟 -> 𐒷 + 0x104B8, # 𐓠 -> 𐒸 + 0x104B9, # 𐓡 -> 𐒹 + 0x104BA, # 𐓢 -> 𐒺 + 0x104BB, # 𐓣 -> 𐒻 + 0x104BC, # 𐓤 -> 𐒼 + 0x104BD, # 𐓥 -> 𐒽 + 0x104BE, # 𐓦 -> 𐒾 + 0x104BF, # 𐓧 -> 𐒿 + 0x104C0, # 𐓨 -> 𐓀 + 0x104C1, # 𐓩 -> 𐓁 + 0x104C2, # 𐓪 -> 𐓂 + 0x104C3, # 𐓫 -> 𐓃 + 0x104C4, # 𐓬 -> 𐓄 + 0x104C5, # 𐓭 -> 𐓅 + 0x104C6, # 𐓮 -> 𐓆 + 0x104C7, # 𐓯 -> 𐓇 + 0x104C8, # 𐓰 -> 𐓈 + 0x104C9, # 𐓱 -> 𐓉 + 0x104CA, # 𐓲 -> 𐓊 + 0x104CB, # 𐓳 -> 𐓋 + 0x104CC, # 𐓴 -> 𐓌 + 0x104CD, # 𐓵 -> 𐓍 + 0x104CE, # 𐓶 -> 𐓎 + 0x104CF, # 𐓷 -> 𐓏 + 0x104D0, # 𐓸 -> 𐓐 + 0x104D1, # 𐓹 -> 𐓑 + 0x104D2, # 𐓺 -> 𐓒 + 0x104D3, # 𐓻 -> 𐓓 + 0x10570, # 𐖗 -> 𐕰 + 0x10571, # 𐖘 -> 𐕱 + 0x10572, # 𐖙 -> 𐕲 + 0x10573, # 𐖚 -> 𐕳 + 0x10574, # 𐖛 -> 𐕴 + 0x10575, # 𐖜 -> 𐕵 + 0x10576, # 𐖝 -> 𐕶 + 0x10577, # 𐖞 -> 𐕷 + 0x10578, # 𐖟 -> 𐕸 + 0x10579, # 𐖠 -> 𐕹 + 0x1057A, # 𐖡 -> 𐕺 + 0x1057C, # 𐖣 -> 𐕼 + 0x1057D, # 𐖤 -> 𐕽 + 0x1057E, # 𐖥 -> 𐕾 + 0x1057F, # 𐖦 -> 𐕿 + 0x10580, # 𐖧 -> 𐖀 + 0x10581, # 𐖨 -> 𐖁 + 0x10582, # 𐖩 -> 𐖂 + 0x10583, # 𐖪 -> 𐖃 + 0x10584, # 𐖫 -> 𐖄 + 0x10585, # 𐖬 -> 𐖅 + 0x10586, # 𐖭 -> 𐖆 + 0x10587, # 𐖮 -> 𐖇 + 0x10588, # 𐖯 -> 𐖈 + 0x10589, # 𐖰 -> 𐖉 + 0x1058A, # 𐖱 -> 𐖊 + 0x1058C, # 𐖳 -> 𐖌 + 0x1058D, # 𐖴 -> 𐖍 + 0x1058E, # 𐖵 -> 𐖎 + 0x1058F, # 𐖶 -> 𐖏 + 0x10590, # 𐖷 -> 𐖐 + 0x10591, # 𐖸 -> 𐖑 + 0x10592, # 𐖹 -> 𐖒 + 0x10594, # 𐖻 -> 𐖔 + 0x10595, # 𐖼 -> 𐖕 + 0x10C80, # 𐳀 -> 𐲀 + 0x10C81, # 𐳁 -> 𐲁 + 0x10C82, # 𐳂 -> 𐲂 + 0x10C83, # 𐳃 -> 𐲃 + 0x10C84, # 𐳄 -> 𐲄 + 0x10C85, # 𐳅 -> 𐲅 + 0x10C86, # 𐳆 -> 𐲆 + 0x10C87, # 𐳇 -> 𐲇 + 0x10C88, # 𐳈 -> 𐲈 + 0x10C89, # 𐳉 -> 𐲉 + 0x10C8A, # 𐳊 -> 𐲊 + 0x10C8B, # 𐳋 -> 𐲋 + 0x10C8C, # 𐳌 -> 𐲌 + 0x10C8D, # 𐳍 -> 𐲍 + 0x10C8E, # 𐳎 -> 𐲎 + 0x10C8F, # 𐳏 -> 𐲏 + 0x10C90, # 𐳐 -> 𐲐 + 0x10C91, # 𐳑 -> 𐲑 + 0x10C92, # 𐳒 -> 𐲒 + 0x10C93, # 𐳓 -> 𐲓 + 0x10C94, # 𐳔 -> 𐲔 + 0x10C95, # 𐳕 -> 𐲕 + 0x10C96, # 𐳖 -> 𐲖 + 0x10C97, # 𐳗 -> 𐲗 + 0x10C98, # 𐳘 -> 𐲘 + 0x10C99, # 𐳙 -> 𐲙 + 0x10C9A, # 𐳚 -> 𐲚 + 0x10C9B, # 𐳛 -> 𐲛 + 0x10C9C, # 𐳜 -> 𐲜 + 0x10C9D, # 𐳝 -> 𐲝 + 0x10C9E, # 𐳞 -> 𐲞 + 0x10C9F, # 𐳟 -> 𐲟 + 0x10CA0, # 𐳠 -> 𐲠 + 0x10CA1, # 𐳡 -> 𐲡 + 0x10CA2, # 𐳢 -> 𐲢 + 0x10CA3, # 𐳣 -> 𐲣 + 0x10CA4, # 𐳤 -> 𐲤 + 0x10CA5, # 𐳥 -> 𐲥 + 0x10CA6, # 𐳦 -> 𐲦 + 0x10CA7, # 𐳧 -> 𐲧 + 0x10CA8, # 𐳨 -> 𐲨 + 0x10CA9, # 𐳩 -> 𐲩 + 0x10CAA, # 𐳪 -> 𐲪 + 0x10CAB, # 𐳫 -> 𐲫 + 0x10CAC, # 𐳬 -> 𐲬 + 0x10CAD, # 𐳭 -> 𐲭 + 0x10CAE, # 𐳮 -> 𐲮 + 0x10CAF, # 𐳯 -> 𐲯 + 0x10CB0, # 𐳰 -> 𐲰 + 0x10CB1, # 𐳱 -> 𐲱 + 0x10CB2, # 𐳲 -> 𐲲 + 0x118A0, # 𑣀 -> 𑢠 + 0x118A1, # 𑣁 -> 𑢡 + 0x118A2, # 𑣂 -> 𑢢 + 0x118A3, # 𑣃 -> 𑢣 + 0x118A4, # 𑣄 -> 𑢤 + 0x118A5, # 𑣅 -> 𑢥 + 0x118A6, # 𑣆 -> 𑢦 + 0x118A7, # 𑣇 -> 𑢧 + 0x118A8, # 𑣈 -> 𑢨 + 0x118A9, # 𑣉 -> 𑢩 + 0x118AA, # 𑣊 -> 𑢪 + 0x118AB, # 𑣋 -> 𑢫 + 0x118AC, # 𑣌 -> 𑢬 + 0x118AD, # 𑣍 -> 𑢭 + 0x118AE, # 𑣎 -> 𑢮 + 0x118AF, # 𑣏 -> 𑢯 + 0x118B0, # 𑣐 -> 𑢰 + 0x118B1, # 𑣑 -> 𑢱 + 0x118B2, # 𑣒 -> 𑢲 + 0x118B3, # 𑣓 -> 𑢳 + 0x118B4, # 𑣔 -> 𑢴 + 0x118B5, # 𑣕 -> 𑢵 + 0x118B6, # 𑣖 -> 𑢶 + 0x118B7, # 𑣗 -> 𑢷 + 0x118B8, # 𑣘 -> 𑢸 + 0x118B9, # 𑣙 -> 𑢹 + 0x118BA, # 𑣚 -> 𑢺 + 0x118BB, # 𑣛 -> 𑢻 + 0x118BC, # 𑣜 -> 𑢼 + 0x118BD, # 𑣝 -> 𑢽 + 0x118BE, # 𑣞 -> 𑢾 + 0x118BF, # 𑣟 -> 𑢿 + 0x16E40, # 𖹠 -> 𖹀 + 0x16E41, # 𖹡 -> 𖹁 + 0x16E42, # 𖹢 -> 𖹂 + 0x16E43, # 𖹣 -> 𖹃 + 0x16E44, # 𖹤 -> 𖹄 + 0x16E45, # 𖹥 -> 𖹅 + 0x16E46, # 𖹦 -> 𖹆 + 0x16E47, # 𖹧 -> 𖹇 + 0x16E48, # 𖹨 -> 𖹈 + 0x16E49, # 𖹩 -> 𖹉 + 0x16E4A, # 𖹪 -> 𖹊 + 0x16E4B, # 𖹫 -> 𖹋 + 0x16E4C, # 𖹬 -> 𖹌 + 0x16E4D, # 𖹭 -> 𖹍 + 0x16E4E, # 𖹮 -> 𖹎 + 0x16E4F, # 𖹯 -> 𖹏 + 0x16E50, # 𖹰 -> 𖹐 + 0x16E51, # 𖹱 -> 𖹑 + 0x16E52, # 𖹲 -> 𖹒 + 0x16E53, # 𖹳 -> 𖹓 + 0x16E54, # 𖹴 -> 𖹔 + 0x16E55, # 𖹵 -> 𖹕 + 0x16E56, # 𖹶 -> 𖹖 + 0x16E57, # 𖹷 -> 𖹗 + 0x16E58, # 𖹸 -> 𖹘 + 0x16E59, # 𖹹 -> 𖹙 + 0x16E5A, # 𖹺 -> 𖹚 + 0x16E5B, # 𖹻 -> 𖹛 + 0x16E5C, # 𖹼 -> 𖹜 + 0x16E5D, # 𖹽 -> 𖹝 + 0x16E5E, # 𖹾 -> 𖹞 + 0x16E5F, # 𖹿 -> 𖹟 + 0x1E900, # 𞤢 -> 𞤀 + 0x1E901, # 𞤣 -> 𞤁 + 0x1E902, # 𞤤 -> 𞤂 + 0x1E903, # 𞤥 -> 𞤃 + 0x1E904, # 𞤦 -> 𞤄 + 0x1E905, # 𞤧 -> 𞤅 + 0x1E906, # 𞤨 -> 𞤆 + 0x1E907, # 𞤩 -> 𞤇 + 0x1E908, # 𞤪 -> 𞤈 + 0x1E909, # 𞤫 -> 𞤉 + 0x1E90A, # 𞤬 -> 𞤊 + 0x1E90B, # 𞤭 -> 𞤋 + 0x1E90C, # 𞤮 -> 𞤌 + 0x1E90D, # 𞤯 -> 𞤍 + 0x1E90E, # 𞤰 -> 𞤎 + 0x1E90F, # 𞤱 -> 𞤏 + 0x1E910, # 𞤲 -> 𞤐 + 0x1E911, # 𞤳 -> 𞤑 + 0x1E912, # 𞤴 -> 𞤒 + 0x1E913, # 𞤵 -> 𞤓 + 0x1E914, # 𞤶 -> 𞤔 + 0x1E915, # 𞤷 -> 𞤕 + 0x1E916, # 𞤸 -> 𞤖 + 0x1E917, # 𞤹 -> 𞤗 + 0x1E918, # 𞤺 -> 𞤘 + 0x1E919, # 𞤻 -> 𞤙 + 0x1E91A, # 𞤼 -> 𞤚 + 0x1E91B, # 𞤽 -> 𞤛 + 0x1E91C, # 𞤾 -> 𞤜 + 0x1E91D, # 𞤿 -> 𞤝 + 0x1E91E, # 𞥀 -> 𞤞 + 0x1E91F, # 𞥁 -> 𞤟 + 0x1E920, # 𞥂 -> 𞤠 + 0x1E921, # 𞥃 -> 𞤡 +) +alias lowercase_mapping = List[UInt32, hint_trivial_type=True]( + 0x0061, # A -> a + 0x0062, # B -> b + 0x0063, # C -> c + 0x0064, # D -> d + 0x0065, # E -> e + 0x0066, # F -> f + 0x0067, # G -> g + 0x0068, # H -> h + 0x0069, # I -> i + 0x006A, # J -> j + 0x006B, # K -> k + 0x006C, # L -> l + 0x006D, # M -> m + 0x006E, # N -> n + 0x006F, # O -> o + 0x0070, # P -> p + 0x0071, # Q -> q + 0x0072, # R -> r + 0x0073, # S -> s + 0x0074, # T -> t + 0x0075, # U -> u + 0x0076, # V -> v + 0x0077, # W -> w + 0x0078, # X -> x + 0x0079, # Y -> y + 0x007A, # Z -> z + 0x00E0, # À -> à + 0x00E1, # Á -> á + 0x00E2, #  -> â + 0x00E3, # à -> ã + 0x00E4, # Ä -> ä + 0x00E5, # Å -> å + 0x00E6, # Æ -> æ + 0x00E7, # Ç -> ç + 0x00E8, # È -> è + 0x00E9, # É -> é + 0x00EA, # Ê -> ê + 0x00EB, # Ë -> ë + 0x00EC, # Ì -> ì + 0x00ED, # Í -> í + 0x00EE, # Î -> î + 0x00EF, # Ï -> ï + 0x00F0, # Ð -> ð + 0x00F1, # Ñ -> ñ + 0x00F2, # Ò -> ò + 0x00F3, # Ó -> ó + 0x00F4, # Ô -> ô + 0x00F5, # Õ -> õ + 0x00F6, # Ö -> ö + 0x00F8, # Ø -> ø + 0x00F9, # Ù -> ù + 0x00FA, # Ú -> ú + 0x00FB, # Û -> û + 0x00FC, # Ü -> ü + 0x00FD, # Ý -> ý + 0x00FE, # Þ -> þ + 0x0101, # Ā -> ā + 0x0103, # Ă -> ă + 0x0105, # Ą -> ą + 0x0107, # Ć -> ć + 0x0109, # Ĉ -> ĉ + 0x010B, # Ċ -> ċ + 0x010D, # Č -> č + 0x010F, # Ď -> ď + 0x0111, # Đ -> đ + 0x0113, # Ē -> ē + 0x0115, # Ĕ -> ĕ + 0x0117, # Ė -> ė + 0x0119, # Ę -> ę + 0x011B, # Ě -> ě + 0x011D, # Ĝ -> ĝ + 0x011F, # Ğ -> ğ + 0x0121, # Ġ -> ġ + 0x0123, # Ģ -> ģ + 0x0125, # Ĥ -> ĥ + 0x0127, # Ħ -> ħ + 0x0129, # Ĩ -> ĩ + 0x012B, # Ī -> ī + 0x012D, # Ĭ -> ĭ + 0x012F, # Į -> į + 0x0069, # İ -> i + 0x0133, # IJ -> ij + 0x0135, # Ĵ -> ĵ + 0x0137, # Ķ -> ķ + 0x013A, # Ĺ -> ĺ + 0x013C, # Ļ -> ļ + 0x013E, # Ľ -> ľ + 0x0140, # Ŀ -> ŀ + 0x0142, # Ł -> ł + 0x0144, # Ń -> ń + 0x0146, # Ņ -> ņ + 0x0148, # Ň -> ň + 0x014B, # Ŋ -> ŋ + 0x014D, # Ō -> ō + 0x014F, # Ŏ -> ŏ + 0x0151, # Ő -> ő + 0x0153, # Œ -> œ + 0x0155, # Ŕ -> ŕ + 0x0157, # Ŗ -> ŗ + 0x0159, # Ř -> ř + 0x015B, # Ś -> ś + 0x015D, # Ŝ -> ŝ + 0x015F, # Ş -> ş + 0x0161, # Š -> š + 0x0163, # Ţ -> ţ + 0x0165, # Ť -> ť + 0x0167, # Ŧ -> ŧ + 0x0169, # Ũ -> ũ + 0x016B, # Ū -> ū + 0x016D, # Ŭ -> ŭ + 0x016F, # Ů -> ů + 0x0171, # Ű -> ű + 0x0173, # Ų -> ų + 0x0175, # Ŵ -> ŵ + 0x0177, # Ŷ -> ŷ + 0x00FF, # Ÿ -> ÿ + 0x017A, # Ź -> ź + 0x017C, # Ż -> ż + 0x017E, # Ž -> ž + 0x0253, # Ɓ -> ɓ + 0x0183, # Ƃ -> ƃ + 0x0185, # Ƅ -> ƅ + 0x0254, # Ɔ -> ɔ + 0x0188, # Ƈ -> ƈ + 0x0256, # Ɖ -> ɖ + 0x0257, # Ɗ -> ɗ + 0x018C, # Ƌ -> ƌ + 0x01DD, # Ǝ -> ǝ + 0x0259, # Ə -> ə + 0x025B, # Ɛ -> ɛ + 0x0192, # Ƒ -> ƒ + 0x0260, # Ɠ -> ɠ + 0x0263, # Ɣ -> ɣ + 0x0269, # Ɩ -> ɩ + 0x0268, # Ɨ -> ɨ + 0x0199, # Ƙ -> ƙ + 0x026F, # Ɯ -> ɯ + 0x0272, # Ɲ -> ɲ + 0x0275, # Ɵ -> ɵ + 0x01A1, # Ơ -> ơ + 0x01A3, # Ƣ -> ƣ + 0x01A5, # Ƥ -> ƥ + 0x0280, # Ʀ -> ʀ + 0x01A8, # Ƨ -> ƨ + 0x0283, # Ʃ -> ʃ + 0x01AD, # Ƭ -> ƭ + 0x0288, # Ʈ -> ʈ + 0x01B0, # Ư -> ư + 0x028A, # Ʊ -> ʊ + 0x028B, # Ʋ -> ʋ + 0x01B4, # Ƴ -> ƴ + 0x01B6, # Ƶ -> ƶ + 0x0292, # Ʒ -> ʒ + 0x01B9, # Ƹ -> ƹ + 0x01BD, # Ƽ -> ƽ + 0x01C6, # DŽ -> dž + 0x01C6, # Dž -> dž + 0x01C9, # LJ -> lj + 0x01C9, # Lj -> lj + 0x01CC, # NJ -> nj + 0x01CC, # Nj -> nj + 0x01CE, # Ǎ -> ǎ + 0x01D0, # Ǐ -> ǐ + 0x01D2, # Ǒ -> ǒ + 0x01D4, # Ǔ -> ǔ + 0x01D6, # Ǖ -> ǖ + 0x01D8, # Ǘ -> ǘ + 0x01DA, # Ǚ -> ǚ + 0x01DC, # Ǜ -> ǜ + 0x01DF, # Ǟ -> ǟ + 0x01E1, # Ǡ -> ǡ + 0x01E3, # Ǣ -> ǣ + 0x01E5, # Ǥ -> ǥ + 0x01E7, # Ǧ -> ǧ + 0x01E9, # Ǩ -> ǩ + 0x01EB, # Ǫ -> ǫ + 0x01ED, # Ǭ -> ǭ + 0x01EF, # Ǯ -> ǯ + 0x01F3, # DZ -> dz + 0x01F3, # Dz -> dz + 0x01F5, # Ǵ -> ǵ + 0x0195, # Ƕ -> ƕ + 0x01BF, # Ƿ -> ƿ + 0x01F9, # Ǹ -> ǹ + 0x01FB, # Ǻ -> ǻ + 0x01FD, # Ǽ -> ǽ + 0x01FF, # Ǿ -> ǿ + 0x0201, # Ȁ -> ȁ + 0x0203, # Ȃ -> ȃ + 0x0205, # Ȅ -> ȅ + 0x0207, # Ȇ -> ȇ + 0x0209, # Ȉ -> ȉ + 0x020B, # Ȋ -> ȋ + 0x020D, # Ȍ -> ȍ + 0x020F, # Ȏ -> ȏ + 0x0211, # Ȑ -> ȑ + 0x0213, # Ȓ -> ȓ + 0x0215, # Ȕ -> ȕ + 0x0217, # Ȗ -> ȗ + 0x0219, # Ș -> ș + 0x021B, # Ț -> ț + 0x021D, # Ȝ -> ȝ + 0x021F, # Ȟ -> ȟ + 0x019E, # Ƞ -> ƞ + 0x0223, # Ȣ -> ȣ + 0x0225, # Ȥ -> ȥ + 0x0227, # Ȧ -> ȧ + 0x0229, # Ȩ -> ȩ + 0x022B, # Ȫ -> ȫ + 0x022D, # Ȭ -> ȭ + 0x022F, # Ȯ -> ȯ + 0x0231, # Ȱ -> ȱ + 0x0233, # Ȳ -> ȳ + 0x2C65, # Ⱥ -> ⱥ + 0x023C, # Ȼ -> ȼ + 0x019A, # Ƚ -> ƚ + 0x2C66, # Ⱦ -> ⱦ + 0x0242, # Ɂ -> ɂ + 0x0180, # Ƀ -> ƀ + 0x0289, # Ʉ -> ʉ + 0x028C, # Ʌ -> ʌ + 0x0247, # Ɇ -> ɇ + 0x0249, # Ɉ -> ɉ + 0x024B, # Ɋ -> ɋ + 0x024D, # Ɍ -> ɍ + 0x024F, # Ɏ -> ɏ + 0x0371, # Ͱ -> ͱ + 0x0373, # Ͳ -> ͳ + 0x0377, # Ͷ -> ͷ + 0x03F3, # Ϳ -> ϳ + 0x03AC, # Ά -> ά + 0x03AD, # Έ -> έ + 0x03AE, # Ή -> ή + 0x03AF, # Ί -> ί + 0x03CC, # Ό -> ό + 0x03CD, # Ύ -> ύ + 0x03CE, # Ώ -> ώ + 0x03B1, # Α -> α + 0x03B2, # Β -> β + 0x03B3, # Γ -> γ + 0x03B4, # Δ -> δ + 0x03B5, # Ε -> ε + 0x03B6, # Ζ -> ζ + 0x03B7, # Η -> η + 0x03B8, # Θ -> θ + 0x03B9, # Ι -> ι + 0x03BA, # Κ -> κ + 0x03BB, # Λ -> λ + 0x03BC, # Μ -> μ + 0x03BD, # Ν -> ν + 0x03BE, # Ξ -> ξ + 0x03BF, # Ο -> ο + 0x03C0, # Π -> π + 0x03C1, # Ρ -> ρ + 0x03C3, # Σ -> σ + 0x03C4, # Τ -> τ + 0x03C5, # Υ -> υ + 0x03C6, # Φ -> φ + 0x03C7, # Χ -> χ + 0x03C8, # Ψ -> ψ + 0x03C9, # Ω -> ω + 0x03CA, # Ϊ -> ϊ + 0x03CB, # Ϋ -> ϋ + 0x03D7, # Ϗ -> ϗ + 0x03D9, # Ϙ -> ϙ + 0x03DB, # Ϛ -> ϛ + 0x03DD, # Ϝ -> ϝ + 0x03DF, # Ϟ -> ϟ + 0x03E1, # Ϡ -> ϡ + 0x03E3, # Ϣ -> ϣ + 0x03E5, # Ϥ -> ϥ + 0x03E7, # Ϧ -> ϧ + 0x03E9, # Ϩ -> ϩ + 0x03EB, # Ϫ -> ϫ + 0x03ED, # Ϭ -> ϭ + 0x03EF, # Ϯ -> ϯ + 0x03B8, # ϴ -> θ + 0x03F8, # Ϸ -> ϸ + 0x03F2, # Ϲ -> ϲ + 0x03FB, # Ϻ -> ϻ + 0x037B, # Ͻ -> ͻ + 0x037C, # Ͼ -> ͼ + 0x037D, # Ͽ -> ͽ + 0x0450, # Ѐ -> ѐ + 0x0451, # Ё -> ё + 0x0452, # Ђ -> ђ + 0x0453, # Ѓ -> ѓ + 0x0454, # Є -> є + 0x0455, # Ѕ -> ѕ + 0x0456, # І -> і + 0x0457, # Ї -> ї + 0x0458, # Ј -> ј + 0x0459, # Љ -> љ + 0x045A, # Њ -> њ + 0x045B, # Ћ -> ћ + 0x045C, # Ќ -> ќ + 0x045D, # Ѝ -> ѝ + 0x045E, # Ў -> ў + 0x045F, # Џ -> џ + 0x0430, # А -> а + 0x0431, # Б -> б + 0x0432, # В -> в + 0x0433, # Г -> г + 0x0434, # Д -> д + 0x0435, # Е -> е + 0x0436, # Ж -> ж + 0x0437, # З -> з + 0x0438, # И -> и + 0x0439, # Й -> й + 0x043A, # К -> к + 0x043B, # Л -> л + 0x043C, # М -> м + 0x043D, # Н -> н + 0x043E, # О -> о + 0x043F, # П -> п + 0x0440, # Р -> р + 0x0441, # С -> с + 0x0442, # Т -> т + 0x0443, # У -> у + 0x0444, # Ф -> ф + 0x0445, # Х -> х + 0x0446, # Ц -> ц + 0x0447, # Ч -> ч + 0x0448, # Ш -> ш + 0x0449, # Щ -> щ + 0x044A, # Ъ -> ъ + 0x044B, # Ы -> ы + 0x044C, # Ь -> ь + 0x044D, # Э -> э + 0x044E, # Ю -> ю + 0x044F, # Я -> я + 0x0461, # Ѡ -> ѡ + 0x0463, # Ѣ -> ѣ + 0x0465, # Ѥ -> ѥ + 0x0467, # Ѧ -> ѧ + 0x0469, # Ѩ -> ѩ + 0x046B, # Ѫ -> ѫ + 0x046D, # Ѭ -> ѭ + 0x046F, # Ѯ -> ѯ + 0x0471, # Ѱ -> ѱ + 0x0473, # Ѳ -> ѳ + 0x0475, # Ѵ -> ѵ + 0x0477, # Ѷ -> ѷ + 0x0479, # Ѹ -> ѹ + 0x047B, # Ѻ -> ѻ + 0x047D, # Ѽ -> ѽ + 0x047F, # Ѿ -> ѿ + 0x0481, # Ҁ -> ҁ + 0x048B, # Ҋ -> ҋ + 0x048D, # Ҍ -> ҍ + 0x048F, # Ҏ -> ҏ + 0x0491, # Ґ -> ґ + 0x0493, # Ғ -> ғ + 0x0495, # Ҕ -> ҕ + 0x0497, # Җ -> җ + 0x0499, # Ҙ -> ҙ + 0x049B, # Қ -> қ + 0x049D, # Ҝ -> ҝ + 0x049F, # Ҟ -> ҟ + 0x04A1, # Ҡ -> ҡ + 0x04A3, # Ң -> ң + 0x04A5, # Ҥ -> ҥ + 0x04A7, # Ҧ -> ҧ + 0x04A9, # Ҩ -> ҩ + 0x04AB, # Ҫ -> ҫ + 0x04AD, # Ҭ -> ҭ + 0x04AF, # Ү -> ү + 0x04B1, # Ұ -> ұ + 0x04B3, # Ҳ -> ҳ + 0x04B5, # Ҵ -> ҵ + 0x04B7, # Ҷ -> ҷ + 0x04B9, # Ҹ -> ҹ + 0x04BB, # Һ -> һ + 0x04BD, # Ҽ -> ҽ + 0x04BF, # Ҿ -> ҿ + 0x04CF, # Ӏ -> ӏ + 0x04C2, # Ӂ -> ӂ + 0x04C4, # Ӄ -> ӄ + 0x04C6, # Ӆ -> ӆ + 0x04C8, # Ӈ -> ӈ + 0x04CA, # Ӊ -> ӊ + 0x04CC, # Ӌ -> ӌ + 0x04CE, # Ӎ -> ӎ + 0x04D1, # Ӑ -> ӑ + 0x04D3, # Ӓ -> ӓ + 0x04D5, # Ӕ -> ӕ + 0x04D7, # Ӗ -> ӗ + 0x04D9, # Ә -> ә + 0x04DB, # Ӛ -> ӛ + 0x04DD, # Ӝ -> ӝ + 0x04DF, # Ӟ -> ӟ + 0x04E1, # Ӡ -> ӡ + 0x04E3, # Ӣ -> ӣ + 0x04E5, # Ӥ -> ӥ + 0x04E7, # Ӧ -> ӧ + 0x04E9, # Ө -> ө + 0x04EB, # Ӫ -> ӫ + 0x04ED, # Ӭ -> ӭ + 0x04EF, # Ӯ -> ӯ + 0x04F1, # Ӱ -> ӱ + 0x04F3, # Ӳ -> ӳ + 0x04F5, # Ӵ -> ӵ + 0x04F7, # Ӷ -> ӷ + 0x04F9, # Ӹ -> ӹ + 0x04FB, # Ӻ -> ӻ + 0x04FD, # Ӽ -> ӽ + 0x04FF, # Ӿ -> ӿ + 0x0501, # Ԁ -> ԁ + 0x0503, # Ԃ -> ԃ + 0x0505, # Ԅ -> ԅ + 0x0507, # Ԇ -> ԇ + 0x0509, # Ԉ -> ԉ + 0x050B, # Ԋ -> ԋ + 0x050D, # Ԍ -> ԍ + 0x050F, # Ԏ -> ԏ + 0x0511, # Ԑ -> ԑ + 0x0513, # Ԓ -> ԓ + 0x0515, # Ԕ -> ԕ + 0x0517, # Ԗ -> ԗ + 0x0519, # Ԙ -> ԙ + 0x051B, # Ԛ -> ԛ + 0x051D, # Ԝ -> ԝ + 0x051F, # Ԟ -> ԟ + 0x0521, # Ԡ -> ԡ + 0x0523, # Ԣ -> ԣ + 0x0525, # Ԥ -> ԥ + 0x0527, # Ԧ -> ԧ + 0x0529, # Ԩ -> ԩ + 0x052B, # Ԫ -> ԫ + 0x052D, # Ԭ -> ԭ + 0x052F, # Ԯ -> ԯ + 0x0561, # Ա -> ա + 0x0562, # Բ -> բ + 0x0563, # Գ -> գ + 0x0564, # Դ -> դ + 0x0565, # Ե -> ե + 0x0566, # Զ -> զ + 0x0567, # Է -> է + 0x0568, # Ը -> ը + 0x0569, # Թ -> թ + 0x056A, # Ժ -> ժ + 0x056B, # Ի -> ի + 0x056C, # Լ -> լ + 0x056D, # Խ -> խ + 0x056E, # Ծ -> ծ + 0x056F, # Կ -> կ + 0x0570, # Հ -> հ + 0x0571, # Ձ -> ձ + 0x0572, # Ղ -> ղ + 0x0573, # Ճ -> ճ + 0x0574, # Մ -> մ + 0x0575, # Յ -> յ + 0x0576, # Ն -> ն + 0x0577, # Շ -> շ + 0x0578, # Ո -> ո + 0x0579, # Չ -> չ + 0x057A, # Պ -> պ + 0x057B, # Ջ -> ջ + 0x057C, # Ռ -> ռ + 0x057D, # Ս -> ս + 0x057E, # Վ -> վ + 0x057F, # Տ -> տ + 0x0580, # Ր -> ր + 0x0581, # Ց -> ց + 0x0582, # Ւ -> ւ + 0x0583, # Փ -> փ + 0x0584, # Ք -> ք + 0x0585, # Օ -> օ + 0x0586, # Ֆ -> ֆ + 0x2D00, # Ⴀ -> ⴀ + 0x2D01, # Ⴁ -> ⴁ + 0x2D02, # Ⴂ -> ⴂ + 0x2D03, # Ⴃ -> ⴃ + 0x2D04, # Ⴄ -> ⴄ + 0x2D05, # Ⴅ -> ⴅ + 0x2D06, # Ⴆ -> ⴆ + 0x2D07, # Ⴇ -> ⴇ + 0x2D08, # Ⴈ -> ⴈ + 0x2D09, # Ⴉ -> ⴉ + 0x2D0A, # Ⴊ -> ⴊ + 0x2D0B, # Ⴋ -> ⴋ + 0x2D0C, # Ⴌ -> ⴌ + 0x2D0D, # Ⴍ -> ⴍ + 0x2D0E, # Ⴎ -> ⴎ + 0x2D0F, # Ⴏ -> ⴏ + 0x2D10, # Ⴐ -> ⴐ + 0x2D11, # Ⴑ -> ⴑ + 0x2D12, # Ⴒ -> ⴒ + 0x2D13, # Ⴓ -> ⴓ + 0x2D14, # Ⴔ -> ⴔ + 0x2D15, # Ⴕ -> ⴕ + 0x2D16, # Ⴖ -> ⴖ + 0x2D17, # Ⴗ -> ⴗ + 0x2D18, # Ⴘ -> ⴘ + 0x2D19, # Ⴙ -> ⴙ + 0x2D1A, # Ⴚ -> ⴚ + 0x2D1B, # Ⴛ -> ⴛ + 0x2D1C, # Ⴜ -> ⴜ + 0x2D1D, # Ⴝ -> ⴝ + 0x2D1E, # Ⴞ -> ⴞ + 0x2D1F, # Ⴟ -> ⴟ + 0x2D20, # Ⴠ -> ⴠ + 0x2D21, # Ⴡ -> ⴡ + 0x2D22, # Ⴢ -> ⴢ + 0x2D23, # Ⴣ -> ⴣ + 0x2D24, # Ⴤ -> ⴤ + 0x2D25, # Ⴥ -> ⴥ + 0x2D27, # Ⴧ -> ⴧ + 0x2D2D, # Ⴭ -> ⴭ + 0xAB70, # Ꭰ -> ꭰ + 0xAB71, # Ꭱ -> ꭱ + 0xAB72, # Ꭲ -> ꭲ + 0xAB73, # Ꭳ -> ꭳ + 0xAB74, # Ꭴ -> ꭴ + 0xAB75, # Ꭵ -> ꭵ + 0xAB76, # Ꭶ -> ꭶ + 0xAB77, # Ꭷ -> ꭷ + 0xAB78, # Ꭸ -> ꭸ + 0xAB79, # Ꭹ -> ꭹ + 0xAB7A, # Ꭺ -> ꭺ + 0xAB7B, # Ꭻ -> ꭻ + 0xAB7C, # Ꭼ -> ꭼ + 0xAB7D, # Ꭽ -> ꭽ + 0xAB7E, # Ꭾ -> ꭾ + 0xAB7F, # Ꭿ -> ꭿ + 0xAB80, # Ꮀ -> ꮀ + 0xAB81, # Ꮁ -> ꮁ + 0xAB82, # Ꮂ -> ꮂ + 0xAB83, # Ꮃ -> ꮃ + 0xAB84, # Ꮄ -> ꮄ + 0xAB85, # Ꮅ -> ꮅ + 0xAB86, # Ꮆ -> ꮆ + 0xAB87, # Ꮇ -> ꮇ + 0xAB88, # Ꮈ -> ꮈ + 0xAB89, # Ꮉ -> ꮉ + 0xAB8A, # Ꮊ -> ꮊ + 0xAB8B, # Ꮋ -> ꮋ + 0xAB8C, # Ꮌ -> ꮌ + 0xAB8D, # Ꮍ -> ꮍ + 0xAB8E, # Ꮎ -> ꮎ + 0xAB8F, # Ꮏ -> ꮏ + 0xAB90, # Ꮐ -> ꮐ + 0xAB91, # Ꮑ -> ꮑ + 0xAB92, # Ꮒ -> ꮒ + 0xAB93, # Ꮓ -> ꮓ + 0xAB94, # Ꮔ -> ꮔ + 0xAB95, # Ꮕ -> ꮕ + 0xAB96, # Ꮖ -> ꮖ + 0xAB97, # Ꮗ -> ꮗ + 0xAB98, # Ꮘ -> ꮘ + 0xAB99, # Ꮙ -> ꮙ + 0xAB9A, # Ꮚ -> ꮚ + 0xAB9B, # Ꮛ -> ꮛ + 0xAB9C, # Ꮜ -> ꮜ + 0xAB9D, # Ꮝ -> ꮝ + 0xAB9E, # Ꮞ -> ꮞ + 0xAB9F, # Ꮟ -> ꮟ + 0xABA0, # Ꮠ -> ꮠ + 0xABA1, # Ꮡ -> ꮡ + 0xABA2, # Ꮢ -> ꮢ + 0xABA3, # Ꮣ -> ꮣ + 0xABA4, # Ꮤ -> ꮤ + 0xABA5, # Ꮥ -> ꮥ + 0xABA6, # Ꮦ -> ꮦ + 0xABA7, # Ꮧ -> ꮧ + 0xABA8, # Ꮨ -> ꮨ + 0xABA9, # Ꮩ -> ꮩ + 0xABAA, # Ꮪ -> ꮪ + 0xABAB, # Ꮫ -> ꮫ + 0xABAC, # Ꮬ -> ꮬ + 0xABAD, # Ꮭ -> ꮭ + 0xABAE, # Ꮮ -> ꮮ + 0xABAF, # Ꮯ -> ꮯ + 0xABB0, # Ꮰ -> ꮰ + 0xABB1, # Ꮱ -> ꮱ + 0xABB2, # Ꮲ -> ꮲ + 0xABB3, # Ꮳ -> ꮳ + 0xABB4, # Ꮴ -> ꮴ + 0xABB5, # Ꮵ -> ꮵ + 0xABB6, # Ꮶ -> ꮶ + 0xABB7, # Ꮷ -> ꮷ + 0xABB8, # Ꮸ -> ꮸ + 0xABB9, # Ꮹ -> ꮹ + 0xABBA, # Ꮺ -> ꮺ + 0xABBB, # Ꮻ -> ꮻ + 0xABBC, # Ꮼ -> ꮼ + 0xABBD, # Ꮽ -> ꮽ + 0xABBE, # Ꮾ -> ꮾ + 0xABBF, # Ꮿ -> ꮿ + 0x13F8, # Ᏸ -> ᏸ + 0x13F9, # Ᏹ -> ᏹ + 0x13FA, # Ᏺ -> ᏺ + 0x13FB, # Ᏻ -> ᏻ + 0x13FC, # Ᏼ -> ᏼ + 0x13FD, # Ᏽ -> ᏽ + 0x10D0, # Ა -> ა + 0x10D1, # Ბ -> ბ + 0x10D2, # Გ -> გ + 0x10D3, # Დ -> დ + 0x10D4, # Ე -> ე + 0x10D5, # Ვ -> ვ + 0x10D6, # Ზ -> ზ + 0x10D7, # Თ -> თ + 0x10D8, # Ი -> ი + 0x10D9, # Კ -> კ + 0x10DA, # Ლ -> ლ + 0x10DB, # Მ -> მ + 0x10DC, # Ნ -> ნ + 0x10DD, # Ო -> ო + 0x10DE, # Პ -> პ + 0x10DF, # Ჟ -> ჟ + 0x10E0, # Რ -> რ + 0x10E1, # Ს -> ს + 0x10E2, # Ტ -> ტ + 0x10E3, # Უ -> უ + 0x10E4, # Ფ -> ფ + 0x10E5, # Ქ -> ქ + 0x10E6, # Ღ -> ღ + 0x10E7, # Ყ -> ყ + 0x10E8, # Შ -> შ + 0x10E9, # Ჩ -> ჩ + 0x10EA, # Ც -> ც + 0x10EB, # Ძ -> ძ + 0x10EC, # Წ -> წ + 0x10ED, # Ჭ -> ჭ + 0x10EE, # Ხ -> ხ + 0x10EF, # Ჯ -> ჯ + 0x10F0, # Ჰ -> ჰ + 0x10F1, # Ჱ -> ჱ + 0x10F2, # Ჲ -> ჲ + 0x10F3, # Ჳ -> ჳ + 0x10F4, # Ჴ -> ჴ + 0x10F5, # Ჵ -> ჵ + 0x10F6, # Ჶ -> ჶ + 0x10F7, # Ჷ -> ჷ + 0x10F8, # Ჸ -> ჸ + 0x10F9, # Ჹ -> ჹ + 0x10FA, # Ჺ -> ჺ + 0x10FD, # Ჽ -> ჽ + 0x10FE, # Ჾ -> ჾ + 0x10FF, # Ჿ -> ჿ + 0x1E01, # Ḁ -> ḁ + 0x1E03, # Ḃ -> ḃ + 0x1E05, # Ḅ -> ḅ + 0x1E07, # Ḇ -> ḇ + 0x1E09, # Ḉ -> ḉ + 0x1E0B, # Ḋ -> ḋ + 0x1E0D, # Ḍ -> ḍ + 0x1E0F, # Ḏ -> ḏ + 0x1E11, # Ḑ -> ḑ + 0x1E13, # Ḓ -> ḓ + 0x1E15, # Ḕ -> ḕ + 0x1E17, # Ḗ -> ḗ + 0x1E19, # Ḙ -> ḙ + 0x1E1B, # Ḛ -> ḛ + 0x1E1D, # Ḝ -> ḝ + 0x1E1F, # Ḟ -> ḟ + 0x1E21, # Ḡ -> ḡ + 0x1E23, # Ḣ -> ḣ + 0x1E25, # Ḥ -> ḥ + 0x1E27, # Ḧ -> ḧ + 0x1E29, # Ḩ -> ḩ + 0x1E2B, # Ḫ -> ḫ + 0x1E2D, # Ḭ -> ḭ + 0x1E2F, # Ḯ -> ḯ + 0x1E31, # Ḱ -> ḱ + 0x1E33, # Ḳ -> ḳ + 0x1E35, # Ḵ -> ḵ + 0x1E37, # Ḷ -> ḷ + 0x1E39, # Ḹ -> ḹ + 0x1E3B, # Ḻ -> ḻ + 0x1E3D, # Ḽ -> ḽ + 0x1E3F, # Ḿ -> ḿ + 0x1E41, # Ṁ -> ṁ + 0x1E43, # Ṃ -> ṃ + 0x1E45, # Ṅ -> ṅ + 0x1E47, # Ṇ -> ṇ + 0x1E49, # Ṉ -> ṉ + 0x1E4B, # Ṋ -> ṋ + 0x1E4D, # Ṍ -> ṍ + 0x1E4F, # Ṏ -> ṏ + 0x1E51, # Ṑ -> ṑ + 0x1E53, # Ṓ -> ṓ + 0x1E55, # Ṕ -> ṕ + 0x1E57, # Ṗ -> ṗ + 0x1E59, # Ṙ -> ṙ + 0x1E5B, # Ṛ -> ṛ + 0x1E5D, # Ṝ -> ṝ + 0x1E5F, # Ṟ -> ṟ + 0x1E61, # Ṡ -> ṡ + 0x1E63, # Ṣ -> ṣ + 0x1E65, # Ṥ -> ṥ + 0x1E67, # Ṧ -> ṧ + 0x1E69, # Ṩ -> ṩ + 0x1E6B, # Ṫ -> ṫ + 0x1E6D, # Ṭ -> ṭ + 0x1E6F, # Ṯ -> ṯ + 0x1E71, # Ṱ -> ṱ + 0x1E73, # Ṳ -> ṳ + 0x1E75, # Ṵ -> ṵ + 0x1E77, # Ṷ -> ṷ + 0x1E79, # Ṹ -> ṹ + 0x1E7B, # Ṻ -> ṻ + 0x1E7D, # Ṽ -> ṽ + 0x1E7F, # Ṿ -> ṿ + 0x1E81, # Ẁ -> ẁ + 0x1E83, # Ẃ -> ẃ + 0x1E85, # Ẅ -> ẅ + 0x1E87, # Ẇ -> ẇ + 0x1E89, # Ẉ -> ẉ + 0x1E8B, # Ẋ -> ẋ + 0x1E8D, # Ẍ -> ẍ + 0x1E8F, # Ẏ -> ẏ + 0x1E91, # Ẑ -> ẑ + 0x1E93, # Ẓ -> ẓ + 0x1E95, # Ẕ -> ẕ + 0x00DF, # ẞ -> ß + 0x1EA1, # Ạ -> ạ + 0x1EA3, # Ả -> ả + 0x1EA5, # Ấ -> ấ + 0x1EA7, # Ầ -> ầ + 0x1EA9, # Ẩ -> ẩ + 0x1EAB, # Ẫ -> ẫ + 0x1EAD, # Ậ -> ậ + 0x1EAF, # Ắ -> ắ + 0x1EB1, # Ằ -> ằ + 0x1EB3, # Ẳ -> ẳ + 0x1EB5, # Ẵ -> ẵ + 0x1EB7, # Ặ -> ặ + 0x1EB9, # Ẹ -> ẹ + 0x1EBB, # Ẻ -> ẻ + 0x1EBD, # Ẽ -> ẽ + 0x1EBF, # Ế -> ế + 0x1EC1, # Ề -> ề + 0x1EC3, # Ể -> ể + 0x1EC5, # Ễ -> ễ + 0x1EC7, # Ệ -> ệ + 0x1EC9, # Ỉ -> ỉ + 0x1ECB, # Ị -> ị + 0x1ECD, # Ọ -> ọ + 0x1ECF, # Ỏ -> ỏ + 0x1ED1, # Ố -> ố + 0x1ED3, # Ồ -> ồ + 0x1ED5, # Ổ -> ổ + 0x1ED7, # Ỗ -> ỗ + 0x1ED9, # Ộ -> ộ + 0x1EDB, # Ớ -> ớ + 0x1EDD, # Ờ -> ờ + 0x1EDF, # Ở -> ở + 0x1EE1, # Ỡ -> ỡ + 0x1EE3, # Ợ -> ợ + 0x1EE5, # Ụ -> ụ + 0x1EE7, # Ủ -> ủ + 0x1EE9, # Ứ -> ứ + 0x1EEB, # Ừ -> ừ + 0x1EED, # Ử -> ử + 0x1EEF, # Ữ -> ữ + 0x1EF1, # Ự -> ự + 0x1EF3, # Ỳ -> ỳ + 0x1EF5, # Ỵ -> ỵ + 0x1EF7, # Ỷ -> ỷ + 0x1EF9, # Ỹ -> ỹ + 0x1EFB, # Ỻ -> ỻ + 0x1EFD, # Ỽ -> ỽ + 0x1EFF, # Ỿ -> ỿ + 0x1F00, # Ἀ -> ἀ + 0x1F01, # Ἁ -> ἁ + 0x1F02, # Ἂ -> ἂ + 0x1F03, # Ἃ -> ἃ + 0x1F04, # Ἄ -> ἄ + 0x1F05, # Ἅ -> ἅ + 0x1F06, # Ἆ -> ἆ + 0x1F07, # Ἇ -> ἇ + 0x1F10, # Ἐ -> ἐ + 0x1F11, # Ἑ -> ἑ + 0x1F12, # Ἒ -> ἒ + 0x1F13, # Ἓ -> ἓ + 0x1F14, # Ἔ -> ἔ + 0x1F15, # Ἕ -> ἕ + 0x1F20, # Ἠ -> ἠ + 0x1F21, # Ἡ -> ἡ + 0x1F22, # Ἢ -> ἢ + 0x1F23, # Ἣ -> ἣ + 0x1F24, # Ἤ -> ἤ + 0x1F25, # Ἥ -> ἥ + 0x1F26, # Ἦ -> ἦ + 0x1F27, # Ἧ -> ἧ + 0x1F30, # Ἰ -> ἰ + 0x1F31, # Ἱ -> ἱ + 0x1F32, # Ἲ -> ἲ + 0x1F33, # Ἳ -> ἳ + 0x1F34, # Ἴ -> ἴ + 0x1F35, # Ἵ -> ἵ + 0x1F36, # Ἶ -> ἶ + 0x1F37, # Ἷ -> ἷ + 0x1F40, # Ὀ -> ὀ + 0x1F41, # Ὁ -> ὁ + 0x1F42, # Ὂ -> ὂ + 0x1F43, # Ὃ -> ὃ + 0x1F44, # Ὄ -> ὄ + 0x1F45, # Ὅ -> ὅ + 0x1F51, # Ὑ -> ὑ + 0x1F53, # Ὓ -> ὓ + 0x1F55, # Ὕ -> ὕ + 0x1F57, # Ὗ -> ὗ + 0x1F60, # Ὠ -> ὠ + 0x1F61, # Ὡ -> ὡ + 0x1F62, # Ὢ -> ὢ + 0x1F63, # Ὣ -> ὣ + 0x1F64, # Ὤ -> ὤ + 0x1F65, # Ὥ -> ὥ + 0x1F66, # Ὦ -> ὦ + 0x1F67, # Ὧ -> ὧ + 0x1F80, # ᾈ -> ᾀ + 0x1F81, # ᾉ -> ᾁ + 0x1F82, # ᾊ -> ᾂ + 0x1F83, # ᾋ -> ᾃ + 0x1F84, # ᾌ -> ᾄ + 0x1F85, # ᾍ -> ᾅ + 0x1F86, # ᾎ -> ᾆ + 0x1F87, # ᾏ -> ᾇ + 0x1F90, # ᾘ -> ᾐ + 0x1F91, # ᾙ -> ᾑ + 0x1F92, # ᾚ -> ᾒ + 0x1F93, # ᾛ -> ᾓ + 0x1F94, # ᾜ -> ᾔ + 0x1F95, # ᾝ -> ᾕ + 0x1F96, # ᾞ -> ᾖ + 0x1F97, # ᾟ -> ᾗ + 0x1FA0, # ᾨ -> ᾠ + 0x1FA1, # ᾩ -> ᾡ + 0x1FA2, # ᾪ -> ᾢ + 0x1FA3, # ᾫ -> ᾣ + 0x1FA4, # ᾬ -> ᾤ + 0x1FA5, # ᾭ -> ᾥ + 0x1FA6, # ᾮ -> ᾦ + 0x1FA7, # ᾯ -> ᾧ + 0x1FB0, # Ᾰ -> ᾰ + 0x1FB1, # Ᾱ -> ᾱ + 0x1F70, # Ὰ -> ὰ + 0x1F71, # Ά -> ά + 0x1FB3, # ᾼ -> ᾳ + 0x1F72, # Ὲ -> ὲ + 0x1F73, # Έ -> έ + 0x1F74, # Ὴ -> ὴ + 0x1F75, # Ή -> ή + 0x1FC3, # ῌ -> ῃ + 0x1FD0, # Ῐ -> ῐ + 0x1FD1, # Ῑ -> ῑ + 0x1F76, # Ὶ -> ὶ + 0x1F77, # Ί -> ί + 0x1FE0, # Ῠ -> ῠ + 0x1FE1, # Ῡ -> ῡ + 0x1F7A, # Ὺ -> ὺ + 0x1F7B, # Ύ -> ύ + 0x1FE5, # Ῥ -> ῥ + 0x1F78, # Ὸ -> ὸ + 0x1F79, # Ό -> ό + 0x1F7C, # Ὼ -> ὼ + 0x1F7D, # Ώ -> ώ + 0x1FF3, # ῼ -> ῳ + 0x03C9, # Ω -> ω + 0x006B, # K -> k + 0x00E5, # Å -> å + 0x214E, # Ⅎ -> ⅎ + 0x2170, # Ⅰ -> ⅰ + 0x2171, # Ⅱ -> ⅱ + 0x2172, # Ⅲ -> ⅲ + 0x2173, # Ⅳ -> ⅳ + 0x2174, # Ⅴ -> ⅴ + 0x2175, # Ⅵ -> ⅵ + 0x2176, # Ⅶ -> ⅶ + 0x2177, # Ⅷ -> ⅷ + 0x2178, # Ⅸ -> ⅸ + 0x2179, # Ⅹ -> ⅹ + 0x217A, # Ⅺ -> ⅺ + 0x217B, # Ⅻ -> ⅻ + 0x217C, # Ⅼ -> ⅼ + 0x217D, # Ⅽ -> ⅽ + 0x217E, # Ⅾ -> ⅾ + 0x217F, # Ⅿ -> ⅿ + 0x2184, # Ↄ -> ↄ + 0x24D0, # Ⓐ -> ⓐ + 0x24D1, # Ⓑ -> ⓑ + 0x24D2, # Ⓒ -> ⓒ + 0x24D3, # Ⓓ -> ⓓ + 0x24D4, # Ⓔ -> ⓔ + 0x24D5, # Ⓕ -> ⓕ + 0x24D6, # Ⓖ -> ⓖ + 0x24D7, # Ⓗ -> ⓗ + 0x24D8, # Ⓘ -> ⓘ + 0x24D9, # Ⓙ -> ⓙ + 0x24DA, # Ⓚ -> ⓚ + 0x24DB, # Ⓛ -> ⓛ + 0x24DC, # Ⓜ -> ⓜ + 0x24DD, # Ⓝ -> ⓝ + 0x24DE, # Ⓞ -> ⓞ + 0x24DF, # Ⓟ -> ⓟ + 0x24E0, # Ⓠ -> ⓠ + 0x24E1, # Ⓡ -> ⓡ + 0x24E2, # Ⓢ -> ⓢ + 0x24E3, # Ⓣ -> ⓣ + 0x24E4, # Ⓤ -> ⓤ + 0x24E5, # Ⓥ -> ⓥ + 0x24E6, # Ⓦ -> ⓦ + 0x24E7, # Ⓧ -> ⓧ + 0x24E8, # Ⓨ -> ⓨ + 0x24E9, # Ⓩ -> ⓩ + 0x2C30, # Ⰰ -> ⰰ + 0x2C31, # Ⰱ -> ⰱ + 0x2C32, # Ⰲ -> ⰲ + 0x2C33, # Ⰳ -> ⰳ + 0x2C34, # Ⰴ -> ⰴ + 0x2C35, # Ⰵ -> ⰵ + 0x2C36, # Ⰶ -> ⰶ + 0x2C37, # Ⰷ -> ⰷ + 0x2C38, # Ⰸ -> ⰸ + 0x2C39, # Ⰹ -> ⰹ + 0x2C3A, # Ⰺ -> ⰺ + 0x2C3B, # Ⰻ -> ⰻ + 0x2C3C, # Ⰼ -> ⰼ + 0x2C3D, # Ⰽ -> ⰽ + 0x2C3E, # Ⰾ -> ⰾ + 0x2C3F, # Ⰿ -> ⰿ + 0x2C40, # Ⱀ -> ⱀ + 0x2C41, # Ⱁ -> ⱁ + 0x2C42, # Ⱂ -> ⱂ + 0x2C43, # Ⱃ -> ⱃ + 0x2C44, # Ⱄ -> ⱄ + 0x2C45, # Ⱅ -> ⱅ + 0x2C46, # Ⱆ -> ⱆ + 0x2C47, # Ⱇ -> ⱇ + 0x2C48, # Ⱈ -> ⱈ + 0x2C49, # Ⱉ -> ⱉ + 0x2C4A, # Ⱊ -> ⱊ + 0x2C4B, # Ⱋ -> ⱋ + 0x2C4C, # Ⱌ -> ⱌ + 0x2C4D, # Ⱍ -> ⱍ + 0x2C4E, # Ⱎ -> ⱎ + 0x2C4F, # Ⱏ -> ⱏ + 0x2C50, # Ⱐ -> ⱐ + 0x2C51, # Ⱑ -> ⱑ + 0x2C52, # Ⱒ -> ⱒ + 0x2C53, # Ⱓ -> ⱓ + 0x2C54, # Ⱔ -> ⱔ + 0x2C55, # Ⱕ -> ⱕ + 0x2C56, # Ⱖ -> ⱖ + 0x2C57, # Ⱗ -> ⱗ + 0x2C58, # Ⱘ -> ⱘ + 0x2C59, # Ⱙ -> ⱙ + 0x2C5A, # Ⱚ -> ⱚ + 0x2C5B, # Ⱛ -> ⱛ + 0x2C5C, # Ⱜ -> ⱜ + 0x2C5D, # Ⱝ -> ⱝ + 0x2C5E, # Ⱞ -> ⱞ + 0x2C5F, # Ⱟ -> ⱟ + 0x2C61, # Ⱡ -> ⱡ + 0x026B, # Ɫ -> ɫ + 0x1D7D, # Ᵽ -> ᵽ + 0x027D, # Ɽ -> ɽ + 0x2C68, # Ⱨ -> ⱨ + 0x2C6A, # Ⱪ -> ⱪ + 0x2C6C, # Ⱬ -> ⱬ + 0x0251, # Ɑ -> ɑ + 0x0271, # Ɱ -> ɱ + 0x0250, # Ɐ -> ɐ + 0x0252, # Ɒ -> ɒ + 0x2C73, # Ⱳ -> ⱳ + 0x2C76, # Ⱶ -> ⱶ + 0x023F, # Ȿ -> ȿ + 0x0240, # Ɀ -> ɀ + 0x2C81, # Ⲁ -> ⲁ + 0x2C83, # Ⲃ -> ⲃ + 0x2C85, # Ⲅ -> ⲅ + 0x2C87, # Ⲇ -> ⲇ + 0x2C89, # Ⲉ -> ⲉ + 0x2C8B, # Ⲋ -> ⲋ + 0x2C8D, # Ⲍ -> ⲍ + 0x2C8F, # Ⲏ -> ⲏ + 0x2C91, # Ⲑ -> ⲑ + 0x2C93, # Ⲓ -> ⲓ + 0x2C95, # Ⲕ -> ⲕ + 0x2C97, # Ⲗ -> ⲗ + 0x2C99, # Ⲙ -> ⲙ + 0x2C9B, # Ⲛ -> ⲛ + 0x2C9D, # Ⲝ -> ⲝ + 0x2C9F, # Ⲟ -> ⲟ + 0x2CA1, # Ⲡ -> ⲡ + 0x2CA3, # Ⲣ -> ⲣ + 0x2CA5, # Ⲥ -> ⲥ + 0x2CA7, # Ⲧ -> ⲧ + 0x2CA9, # Ⲩ -> ⲩ + 0x2CAB, # Ⲫ -> ⲫ + 0x2CAD, # Ⲭ -> ⲭ + 0x2CAF, # Ⲯ -> ⲯ + 0x2CB1, # Ⲱ -> ⲱ + 0x2CB3, # Ⲳ -> ⲳ + 0x2CB5, # Ⲵ -> ⲵ + 0x2CB7, # Ⲷ -> ⲷ + 0x2CB9, # Ⲹ -> ⲹ + 0x2CBB, # Ⲻ -> ⲻ + 0x2CBD, # Ⲽ -> ⲽ + 0x2CBF, # Ⲿ -> ⲿ + 0x2CC1, # Ⳁ -> ⳁ + 0x2CC3, # Ⳃ -> ⳃ + 0x2CC5, # Ⳅ -> ⳅ + 0x2CC7, # Ⳇ -> ⳇ + 0x2CC9, # Ⳉ -> ⳉ + 0x2CCB, # Ⳋ -> ⳋ + 0x2CCD, # Ⳍ -> ⳍ + 0x2CCF, # Ⳏ -> ⳏ + 0x2CD1, # Ⳑ -> ⳑ + 0x2CD3, # Ⳓ -> ⳓ + 0x2CD5, # Ⳕ -> ⳕ + 0x2CD7, # Ⳗ -> ⳗ + 0x2CD9, # Ⳙ -> ⳙ + 0x2CDB, # Ⳛ -> ⳛ + 0x2CDD, # Ⳝ -> ⳝ + 0x2CDF, # Ⳟ -> ⳟ + 0x2CE1, # Ⳡ -> ⳡ + 0x2CE3, # Ⳣ -> ⳣ + 0x2CEC, # Ⳬ -> ⳬ + 0x2CEE, # Ⳮ -> ⳮ + 0x2CF3, # Ⳳ -> ⳳ + 0xA641, # Ꙁ -> ꙁ + 0xA643, # Ꙃ -> ꙃ + 0xA645, # Ꙅ -> ꙅ + 0xA647, # Ꙇ -> ꙇ + 0xA649, # Ꙉ -> ꙉ + 0xA64B, # Ꙋ -> ꙋ + 0xA64D, # Ꙍ -> ꙍ + 0xA64F, # Ꙏ -> ꙏ + 0xA651, # Ꙑ -> ꙑ + 0xA653, # Ꙓ -> ꙓ + 0xA655, # Ꙕ -> ꙕ + 0xA657, # Ꙗ -> ꙗ + 0xA659, # Ꙙ -> ꙙ + 0xA65B, # Ꙛ -> ꙛ + 0xA65D, # Ꙝ -> ꙝ + 0xA65F, # Ꙟ -> ꙟ + 0xA661, # Ꙡ -> ꙡ + 0xA663, # Ꙣ -> ꙣ + 0xA665, # Ꙥ -> ꙥ + 0xA667, # Ꙧ -> ꙧ + 0xA669, # Ꙩ -> ꙩ + 0xA66B, # Ꙫ -> ꙫ + 0xA66D, # Ꙭ -> ꙭ + 0xA681, # Ꚁ -> ꚁ + 0xA683, # Ꚃ -> ꚃ + 0xA685, # Ꚅ -> ꚅ + 0xA687, # Ꚇ -> ꚇ + 0xA689, # Ꚉ -> ꚉ + 0xA68B, # Ꚋ -> ꚋ + 0xA68D, # Ꚍ -> ꚍ + 0xA68F, # Ꚏ -> ꚏ + 0xA691, # Ꚑ -> ꚑ + 0xA693, # Ꚓ -> ꚓ + 0xA695, # Ꚕ -> ꚕ + 0xA697, # Ꚗ -> ꚗ + 0xA699, # Ꚙ -> ꚙ + 0xA69B, # Ꚛ -> ꚛ + 0xA723, # Ꜣ -> ꜣ + 0xA725, # Ꜥ -> ꜥ + 0xA727, # Ꜧ -> ꜧ + 0xA729, # Ꜩ -> ꜩ + 0xA72B, # Ꜫ -> ꜫ + 0xA72D, # Ꜭ -> ꜭ + 0xA72F, # Ꜯ -> ꜯ + 0xA733, # Ꜳ -> ꜳ + 0xA735, # Ꜵ -> ꜵ + 0xA737, # Ꜷ -> ꜷ + 0xA739, # Ꜹ -> ꜹ + 0xA73B, # Ꜻ -> ꜻ + 0xA73D, # Ꜽ -> ꜽ + 0xA73F, # Ꜿ -> ꜿ + 0xA741, # Ꝁ -> ꝁ + 0xA743, # Ꝃ -> ꝃ + 0xA745, # Ꝅ -> ꝅ + 0xA747, # Ꝇ -> ꝇ + 0xA749, # Ꝉ -> ꝉ + 0xA74B, # Ꝋ -> ꝋ + 0xA74D, # Ꝍ -> ꝍ + 0xA74F, # Ꝏ -> ꝏ + 0xA751, # Ꝑ -> ꝑ + 0xA753, # Ꝓ -> ꝓ + 0xA755, # Ꝕ -> ꝕ + 0xA757, # Ꝗ -> ꝗ + 0xA759, # Ꝙ -> ꝙ + 0xA75B, # Ꝛ -> ꝛ + 0xA75D, # Ꝝ -> ꝝ + 0xA75F, # Ꝟ -> ꝟ + 0xA761, # Ꝡ -> ꝡ + 0xA763, # Ꝣ -> ꝣ + 0xA765, # Ꝥ -> ꝥ + 0xA767, # Ꝧ -> ꝧ + 0xA769, # Ꝩ -> ꝩ + 0xA76B, # Ꝫ -> ꝫ + 0xA76D, # Ꝭ -> ꝭ + 0xA76F, # Ꝯ -> ꝯ + 0xA77A, # Ꝺ -> ꝺ + 0xA77C, # Ꝼ -> ꝼ + 0x1D79, # Ᵹ -> ᵹ + 0xA77F, # Ꝿ -> ꝿ + 0xA781, # Ꞁ -> ꞁ + 0xA783, # Ꞃ -> ꞃ + 0xA785, # Ꞅ -> ꞅ + 0xA787, # Ꞇ -> ꞇ + 0xA78C, # Ꞌ -> ꞌ + 0x0265, # Ɥ -> ɥ + 0xA791, # Ꞑ -> ꞑ + 0xA793, # Ꞓ -> ꞓ + 0xA797, # Ꞗ -> ꞗ + 0xA799, # Ꞙ -> ꞙ + 0xA79B, # Ꞛ -> ꞛ + 0xA79D, # Ꞝ -> ꞝ + 0xA79F, # Ꞟ -> ꞟ + 0xA7A1, # Ꞡ -> ꞡ + 0xA7A3, # Ꞣ -> ꞣ + 0xA7A5, # Ꞥ -> ꞥ + 0xA7A7, # Ꞧ -> ꞧ + 0xA7A9, # Ꞩ -> ꞩ + 0x0266, # Ɦ -> ɦ + 0x025C, # Ɜ -> ɜ + 0x0261, # Ɡ -> ɡ + 0x026C, # Ɬ -> ɬ + 0x026A, # Ɪ -> ɪ + 0x029E, # Ʞ -> ʞ + 0x0287, # Ʇ -> ʇ + 0x029D, # Ʝ -> ʝ + 0xAB53, # Ꭓ -> ꭓ + 0xA7B5, # Ꞵ -> ꞵ + 0xA7B7, # Ꞷ -> ꞷ + 0xA7B9, # Ꞹ -> ꞹ + 0xA7BB, # Ꞻ -> ꞻ + 0xA7BD, # Ꞽ -> ꞽ + 0xA7BF, # Ꞿ -> ꞿ + 0xA7C1, # Ꟁ -> ꟁ + 0xA7C3, # Ꟃ -> ꟃ + 0xA794, # Ꞔ -> ꞔ + 0x0282, # Ʂ -> ʂ + 0x1D8E, # Ᶎ -> ᶎ + 0xA7C8, # Ꟈ -> ꟈ + 0xA7CA, # Ꟊ -> ꟊ + 0xA7D1, # Ꟑ -> ꟑ + 0xA7D7, # Ꟗ -> ꟗ + 0xA7D9, # Ꟙ -> ꟙ + 0xA7F6, # Ꟶ -> ꟶ + 0xFF41, # A -> a + 0xFF42, # B -> b + 0xFF43, # C -> c + 0xFF44, # D -> d + 0xFF45, # E -> e + 0xFF46, # F -> f + 0xFF47, # G -> g + 0xFF48, # H -> h + 0xFF49, # I -> i + 0xFF4A, # J -> j + 0xFF4B, # K -> k + 0xFF4C, # L -> l + 0xFF4D, # M -> m + 0xFF4E, # N -> n + 0xFF4F, # O -> o + 0xFF50, # P -> p + 0xFF51, # Q -> q + 0xFF52, # R -> r + 0xFF53, # S -> s + 0xFF54, # T -> t + 0xFF55, # U -> u + 0xFF56, # V -> v + 0xFF57, # W -> w + 0xFF58, # X -> x + 0xFF59, # Y -> y + 0xFF5A, # Z -> z + 0x10428, # 𐐀 -> 𐐨 + 0x10429, # 𐐁 -> 𐐩 + 0x1042A, # 𐐂 -> 𐐪 + 0x1042B, # 𐐃 -> 𐐫 + 0x1042C, # 𐐄 -> 𐐬 + 0x1042D, # 𐐅 -> 𐐭 + 0x1042E, # 𐐆 -> 𐐮 + 0x1042F, # 𐐇 -> 𐐯 + 0x10430, # 𐐈 -> 𐐰 + 0x10431, # 𐐉 -> 𐐱 + 0x10432, # 𐐊 -> 𐐲 + 0x10433, # 𐐋 -> 𐐳 + 0x10434, # 𐐌 -> 𐐴 + 0x10435, # 𐐍 -> 𐐵 + 0x10436, # 𐐎 -> 𐐶 + 0x10437, # 𐐏 -> 𐐷 + 0x10438, # 𐐐 -> 𐐸 + 0x10439, # 𐐑 -> 𐐹 + 0x1043A, # 𐐒 -> 𐐺 + 0x1043B, # 𐐓 -> 𐐻 + 0x1043C, # 𐐔 -> 𐐼 + 0x1043D, # 𐐕 -> 𐐽 + 0x1043E, # 𐐖 -> 𐐾 + 0x1043F, # 𐐗 -> 𐐿 + 0x10440, # 𐐘 -> 𐑀 + 0x10441, # 𐐙 -> 𐑁 + 0x10442, # 𐐚 -> 𐑂 + 0x10443, # 𐐛 -> 𐑃 + 0x10444, # 𐐜 -> 𐑄 + 0x10445, # 𐐝 -> 𐑅 + 0x10446, # 𐐞 -> 𐑆 + 0x10447, # 𐐟 -> 𐑇 + 0x10448, # 𐐠 -> 𐑈 + 0x10449, # 𐐡 -> 𐑉 + 0x1044A, # 𐐢 -> 𐑊 + 0x1044B, # 𐐣 -> 𐑋 + 0x1044C, # 𐐤 -> 𐑌 + 0x1044D, # 𐐥 -> 𐑍 + 0x1044E, # 𐐦 -> 𐑎 + 0x1044F, # 𐐧 -> 𐑏 + 0x104D8, # 𐒰 -> 𐓘 + 0x104D9, # 𐒱 -> 𐓙 + 0x104DA, # 𐒲 -> 𐓚 + 0x104DB, # 𐒳 -> 𐓛 + 0x104DC, # 𐒴 -> 𐓜 + 0x104DD, # 𐒵 -> 𐓝 + 0x104DE, # 𐒶 -> 𐓞 + 0x104DF, # 𐒷 -> 𐓟 + 0x104E0, # 𐒸 -> 𐓠 + 0x104E1, # 𐒹 -> 𐓡 + 0x104E2, # 𐒺 -> 𐓢 + 0x104E3, # 𐒻 -> 𐓣 + 0x104E4, # 𐒼 -> 𐓤 + 0x104E5, # 𐒽 -> 𐓥 + 0x104E6, # 𐒾 -> 𐓦 + 0x104E7, # 𐒿 -> 𐓧 + 0x104E8, # 𐓀 -> 𐓨 + 0x104E9, # 𐓁 -> 𐓩 + 0x104EA, # 𐓂 -> 𐓪 + 0x104EB, # 𐓃 -> 𐓫 + 0x104EC, # 𐓄 -> 𐓬 + 0x104ED, # 𐓅 -> 𐓭 + 0x104EE, # 𐓆 -> 𐓮 + 0x104EF, # 𐓇 -> 𐓯 + 0x104F0, # 𐓈 -> 𐓰 + 0x104F1, # 𐓉 -> 𐓱 + 0x104F2, # 𐓊 -> 𐓲 + 0x104F3, # 𐓋 -> 𐓳 + 0x104F4, # 𐓌 -> 𐓴 + 0x104F5, # 𐓍 -> 𐓵 + 0x104F6, # 𐓎 -> 𐓶 + 0x104F7, # 𐓏 -> 𐓷 + 0x104F8, # 𐓐 -> 𐓸 + 0x104F9, # 𐓑 -> 𐓹 + 0x104FA, # 𐓒 -> 𐓺 + 0x104FB, # 𐓓 -> 𐓻 + 0x10597, # 𐕰 -> 𐖗 + 0x10598, # 𐕱 -> 𐖘 + 0x10599, # 𐕲 -> 𐖙 + 0x1059A, # 𐕳 -> 𐖚 + 0x1059B, # 𐕴 -> 𐖛 + 0x1059C, # 𐕵 -> 𐖜 + 0x1059D, # 𐕶 -> 𐖝 + 0x1059E, # 𐕷 -> 𐖞 + 0x1059F, # 𐕸 -> 𐖟 + 0x105A0, # 𐕹 -> 𐖠 + 0x105A1, # 𐕺 -> 𐖡 + 0x105A3, # 𐕼 -> 𐖣 + 0x105A4, # 𐕽 -> 𐖤 + 0x105A5, # 𐕾 -> 𐖥 + 0x105A6, # 𐕿 -> 𐖦 + 0x105A7, # 𐖀 -> 𐖧 + 0x105A8, # 𐖁 -> 𐖨 + 0x105A9, # 𐖂 -> 𐖩 + 0x105AA, # 𐖃 -> 𐖪 + 0x105AB, # 𐖄 -> 𐖫 + 0x105AC, # 𐖅 -> 𐖬 + 0x105AD, # 𐖆 -> 𐖭 + 0x105AE, # 𐖇 -> 𐖮 + 0x105AF, # 𐖈 -> 𐖯 + 0x105B0, # 𐖉 -> 𐖰 + 0x105B1, # 𐖊 -> 𐖱 + 0x105B3, # 𐖌 -> 𐖳 + 0x105B4, # 𐖍 -> 𐖴 + 0x105B5, # 𐖎 -> 𐖵 + 0x105B6, # 𐖏 -> 𐖶 + 0x105B7, # 𐖐 -> 𐖷 + 0x105B8, # 𐖑 -> 𐖸 + 0x105B9, # 𐖒 -> 𐖹 + 0x105BB, # 𐖔 -> 𐖻 + 0x105BC, # 𐖕 -> 𐖼 + 0x10CC0, # 𐲀 -> 𐳀 + 0x10CC1, # 𐲁 -> 𐳁 + 0x10CC2, # 𐲂 -> 𐳂 + 0x10CC3, # 𐲃 -> 𐳃 + 0x10CC4, # 𐲄 -> 𐳄 + 0x10CC5, # 𐲅 -> 𐳅 + 0x10CC6, # 𐲆 -> 𐳆 + 0x10CC7, # 𐲇 -> 𐳇 + 0x10CC8, # 𐲈 -> 𐳈 + 0x10CC9, # 𐲉 -> 𐳉 + 0x10CCA, # 𐲊 -> 𐳊 + 0x10CCB, # 𐲋 -> 𐳋 + 0x10CCC, # 𐲌 -> 𐳌 + 0x10CCD, # 𐲍 -> 𐳍 + 0x10CCE, # 𐲎 -> 𐳎 + 0x10CCF, # 𐲏 -> 𐳏 + 0x10CD0, # 𐲐 -> 𐳐 + 0x10CD1, # 𐲑 -> 𐳑 + 0x10CD2, # 𐲒 -> 𐳒 + 0x10CD3, # 𐲓 -> 𐳓 + 0x10CD4, # 𐲔 -> 𐳔 + 0x10CD5, # 𐲕 -> 𐳕 + 0x10CD6, # 𐲖 -> 𐳖 + 0x10CD7, # 𐲗 -> 𐳗 + 0x10CD8, # 𐲘 -> 𐳘 + 0x10CD9, # 𐲙 -> 𐳙 + 0x10CDA, # 𐲚 -> 𐳚 + 0x10CDB, # 𐲛 -> 𐳛 + 0x10CDC, # 𐲜 -> 𐳜 + 0x10CDD, # 𐲝 -> 𐳝 + 0x10CDE, # 𐲞 -> 𐳞 + 0x10CDF, # 𐲟 -> 𐳟 + 0x10CE0, # 𐲠 -> 𐳠 + 0x10CE1, # 𐲡 -> 𐳡 + 0x10CE2, # 𐲢 -> 𐳢 + 0x10CE3, # 𐲣 -> 𐳣 + 0x10CE4, # 𐲤 -> 𐳤 + 0x10CE5, # 𐲥 -> 𐳥 + 0x10CE6, # 𐲦 -> 𐳦 + 0x10CE7, # 𐲧 -> 𐳧 + 0x10CE8, # 𐲨 -> 𐳨 + 0x10CE9, # 𐲩 -> 𐳩 + 0x10CEA, # 𐲪 -> 𐳪 + 0x10CEB, # 𐲫 -> 𐳫 + 0x10CEC, # 𐲬 -> 𐳬 + 0x10CED, # 𐲭 -> 𐳭 + 0x10CEE, # 𐲮 -> 𐳮 + 0x10CEF, # 𐲯 -> 𐳯 + 0x10CF0, # 𐲰 -> 𐳰 + 0x10CF1, # 𐲱 -> 𐳱 + 0x10CF2, # 𐲲 -> 𐳲 + 0x118C0, # 𑢠 -> 𑣀 + 0x118C1, # 𑢡 -> 𑣁 + 0x118C2, # 𑢢 -> 𑣂 + 0x118C3, # 𑢣 -> 𑣃 + 0x118C4, # 𑢤 -> 𑣄 + 0x118C5, # 𑢥 -> 𑣅 + 0x118C6, # 𑢦 -> 𑣆 + 0x118C7, # 𑢧 -> 𑣇 + 0x118C8, # 𑢨 -> 𑣈 + 0x118C9, # 𑢩 -> 𑣉 + 0x118CA, # 𑢪 -> 𑣊 + 0x118CB, # 𑢫 -> 𑣋 + 0x118CC, # 𑢬 -> 𑣌 + 0x118CD, # 𑢭 -> 𑣍 + 0x118CE, # 𑢮 -> 𑣎 + 0x118CF, # 𑢯 -> 𑣏 + 0x118D0, # 𑢰 -> 𑣐 + 0x118D1, # 𑢱 -> 𑣑 + 0x118D2, # 𑢲 -> 𑣒 + 0x118D3, # 𑢳 -> 𑣓 + 0x118D4, # 𑢴 -> 𑣔 + 0x118D5, # 𑢵 -> 𑣕 + 0x118D6, # 𑢶 -> 𑣖 + 0x118D7, # 𑢷 -> 𑣗 + 0x118D8, # 𑢸 -> 𑣘 + 0x118D9, # 𑢹 -> 𑣙 + 0x118DA, # 𑢺 -> 𑣚 + 0x118DB, # 𑢻 -> 𑣛 + 0x118DC, # 𑢼 -> 𑣜 + 0x118DD, # 𑢽 -> 𑣝 + 0x118DE, # 𑢾 -> 𑣞 + 0x118DF, # 𑢿 -> 𑣟 + 0x16E60, # 𖹀 -> 𖹠 + 0x16E61, # 𖹁 -> 𖹡 + 0x16E62, # 𖹂 -> 𖹢 + 0x16E63, # 𖹃 -> 𖹣 + 0x16E64, # 𖹄 -> 𖹤 + 0x16E65, # 𖹅 -> 𖹥 + 0x16E66, # 𖹆 -> 𖹦 + 0x16E67, # 𖹇 -> 𖹧 + 0x16E68, # 𖹈 -> 𖹨 + 0x16E69, # 𖹉 -> 𖹩 + 0x16E6A, # 𖹊 -> 𖹪 + 0x16E6B, # 𖹋 -> 𖹫 + 0x16E6C, # 𖹌 -> 𖹬 + 0x16E6D, # 𖹍 -> 𖹭 + 0x16E6E, # 𖹎 -> 𖹮 + 0x16E6F, # 𖹏 -> 𖹯 + 0x16E70, # 𖹐 -> 𖹰 + 0x16E71, # 𖹑 -> 𖹱 + 0x16E72, # 𖹒 -> 𖹲 + 0x16E73, # 𖹓 -> 𖹳 + 0x16E74, # 𖹔 -> 𖹴 + 0x16E75, # 𖹕 -> 𖹵 + 0x16E76, # 𖹖 -> 𖹶 + 0x16E77, # 𖹗 -> 𖹷 + 0x16E78, # 𖹘 -> 𖹸 + 0x16E79, # 𖹙 -> 𖹹 + 0x16E7A, # 𖹚 -> 𖹺 + 0x16E7B, # 𖹛 -> 𖹻 + 0x16E7C, # 𖹜 -> 𖹼 + 0x16E7D, # 𖹝 -> 𖹽 + 0x16E7E, # 𖹞 -> 𖹾 + 0x16E7F, # 𖹟 -> 𖹿 + 0x1E922, # 𞤀 -> 𞤢 + 0x1E923, # 𞤁 -> 𞤣 + 0x1E924, # 𞤂 -> 𞤤 + 0x1E925, # 𞤃 -> 𞤥 + 0x1E926, # 𞤄 -> 𞤦 + 0x1E927, # 𞤅 -> 𞤧 + 0x1E928, # 𞤆 -> 𞤨 + 0x1E929, # 𞤇 -> 𞤩 + 0x1E92A, # 𞤈 -> 𞤪 + 0x1E92B, # 𞤉 -> 𞤫 + 0x1E92C, # 𞤊 -> 𞤬 + 0x1E92D, # 𞤋 -> 𞤭 + 0x1E92E, # 𞤌 -> 𞤮 + 0x1E92F, # 𞤍 -> 𞤯 + 0x1E930, # 𞤎 -> 𞤰 + 0x1E931, # 𞤏 -> 𞤱 + 0x1E932, # 𞤐 -> 𞤲 + 0x1E933, # 𞤑 -> 𞤳 + 0x1E934, # 𞤒 -> 𞤴 + 0x1E935, # 𞤓 -> 𞤵 + 0x1E936, # 𞤔 -> 𞤶 + 0x1E937, # 𞤕 -> 𞤷 + 0x1E938, # 𞤖 -> 𞤸 + 0x1E939, # 𞤗 -> 𞤹 + 0x1E93A, # 𞤘 -> 𞤺 + 0x1E93B, # 𞤙 -> 𞤻 + 0x1E93C, # 𞤚 -> 𞤼 + 0x1E93D, # 𞤛 -> 𞤽 + 0x1E93E, # 𞤜 -> 𞤾 + 0x1E93F, # 𞤝 -> 𞤿 + 0x1E940, # 𞤞 -> 𞥀 + 0x1E941, # 𞤟 -> 𞥁 + 0x1E942, # 𞤠 -> 𞥂 + 0x1E943, # 𞤡 -> 𞥃 +) +alias has_uppercase_mapping2 = List[UInt32, hint_trivial_type=True]( + 0xDF, # # LATIN SMALL LETTER SHARP S ß + 0x149, # # LATIN SMALL LETTER N PRECEDED BY APOSTROPHE ʼn + 0x1F0, # # LATIN SMALL LETTER J WITH CARON ǰ + 0x587, # # ARMENIAN SMALL LIGATURE ECH YIWN և + 0x1E96, # # LATIN SMALL LETTER H WITH LINE BELOW ẖ + 0x1E97, # # LATIN SMALL LETTER T WITH DIAERESIS ẗ + 0x1E98, # # LATIN SMALL LETTER W WITH RING ABOVE ẘ + 0x1E99, # # LATIN SMALL LETTER Y WITH RING ABOVE ẙ + 0x1E9A, # # LATIN SMALL LETTER A WITH RIGHT HALF RING ẚ + 0x1F50, # # GREEK SMALL LETTER UPSILON WITH PSILI ὐ + 0x1FB6, # # GREEK SMALL LETTER ALPHA WITH PERISPOMENI ᾶ + 0x1FC6, # # GREEK SMALL LETTER ETA WITH PERISPOMENI ῆ + 0x1FD6, # # GREEK SMALL LETTER IOTA WITH PERISPOMENI ῖ + 0x1FE4, # # GREEK SMALL LETTER RHO WITH PSILI ῤ + 0x1FE6, # # GREEK SMALL LETTER UPSILON WITH PERISPOMENI ῦ + 0x1FF6, # # GREEK SMALL LETTER OMEGA WITH PERISPOMENI ῶ + 0xFB00, # # LATIN SMALL LIGATURE FF ff + 0xFB01, # # LATIN SMALL LIGATURE FI fi + 0xFB02, # # LATIN SMALL LIGATURE FL fl + 0xFB05, # # LATIN SMALL LIGATURE LONG S T ſt + 0xFB06, # # LATIN SMALL LIGATURE ST st + 0xFB13, # # ARMENIAN SMALL LIGATURE MEN NOW ﬓ + 0xFB14, # # ARMENIAN SMALL LIGATURE MEN ECH ﬔ + 0xFB15, # # ARMENIAN SMALL LIGATURE MEN INI ﬕ + 0xFB16, # # ARMENIAN SMALL LIGATURE VEW NOW ﬖ + 0xFB17, # # ARMENIAN SMALL LIGATURE MEN XEH ﬗ +) +alias uppercase_mapping2 = List[SIMD[DType.uint32, 2], hint_trivial_type=True]( + SIMD[DType.uint32, 2](0x0053, 0x0053), # ß -> SS + SIMD[DType.uint32, 2](0x02BC, 0x004E), # ʼn -> ʼN + SIMD[DType.uint32, 2](0x004A, 0x030C), # ǰ -> J̌ + SIMD[DType.uint32, 2](0x0535, 0x0552), # և -> ԵՒ + SIMD[DType.uint32, 2](0x0048, 0x0331), # ẖ -> H̱ + SIMD[DType.uint32, 2](0x0054, 0x0308), # ẗ -> T̈ + SIMD[DType.uint32, 2](0x0057, 0x030A), # ẘ -> W̊ + SIMD[DType.uint32, 2](0x0059, 0x030A), # ẙ -> Y̊ + SIMD[DType.uint32, 2](0x0041, 0x02BE), # ẚ -> Aʾ + SIMD[DType.uint32, 2](0x03A5, 0x0313), # ὐ -> Υ̓ + SIMD[DType.uint32, 2](0x0391, 0x0342), # ᾶ -> Α͂ + SIMD[DType.uint32, 2](0x0397, 0x0342), # ῆ -> Η͂ + SIMD[DType.uint32, 2](0x0399, 0x0342), # ῖ -> Ι͂ + SIMD[DType.uint32, 2](0x03A1, 0x0313), # ῤ -> Ρ̓ + SIMD[DType.uint32, 2](0x03A5, 0x0342), # ῦ -> Υ͂ + SIMD[DType.uint32, 2](0x03A9, 0x0342), # ῶ -> Ω͂ + SIMD[DType.uint32, 2](0x0046, 0x0046), # ff -> FF + SIMD[DType.uint32, 2](0x0046, 0x0049), # fi -> FI + SIMD[DType.uint32, 2](0x0046, 0x004C), # fl -> FL + SIMD[DType.uint32, 2](0x0053, 0x0054), # ſt -> ST + SIMD[DType.uint32, 2](0x0053, 0x0054), # st -> ST + SIMD[DType.uint32, 2](0x0544, 0x0546), # ﬓ -> ՄՆ + SIMD[DType.uint32, 2](0x0544, 0x0535), # ﬔ -> ՄԵ + SIMD[DType.uint32, 2](0x0544, 0x053B), # ﬕ -> ՄԻ + SIMD[DType.uint32, 2](0x054E, 0x0546), # ﬖ -> ՎՆ + SIMD[DType.uint32, 2](0x0544, 0x053D), # ﬗ -> ՄԽ +) +alias has_uppercase_mapping3 = List[UInt32, hint_trivial_type=True]( + 0x390, # # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS ΐ + 0x3B0, # # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS ΰ + 0x1F52, # # GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA ὒ + 0x1F54, # # GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA ὔ + 0x1F56, # # GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI ὖ + 0x1FD2, # # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA ῒ + 0x1FD3, # # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA ΐ + 0x1FD7, # # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI ῗ + 0x1FE2, # # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA ῢ + 0x1FE3, # # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA ΰ + 0x1FE7, # # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI ῧ + 0xFB03, # # LATIN SMALL LIGATURE FFI ffi + 0xFB04, # # LATIN SMALL LIGATURE FFL ffl +) +alias uppercase_mapping3 = List[SIMD[DType.uint32, 4], hint_trivial_type=True]( + SIMD[DType.uint32, 4](0x0399, 0x0308, 0x0301, 0), # ΐ -> Ϊ́ + SIMD[DType.uint32, 4](0x03A5, 0x0308, 0x0301, 0), # ΰ -> Ϋ́ + SIMD[DType.uint32, 4](0x03A5, 0x0313, 0x0300, 0), # ὒ -> Υ̓̀ + SIMD[DType.uint32, 4](0x03A5, 0x0313, 0x0301, 0), # ὔ -> Υ̓́ + SIMD[DType.uint32, 4](0x03A5, 0x0313, 0x0342, 0), # ὖ -> Υ̓͂ + SIMD[DType.uint32, 4](0x0399, 0x0308, 0x0300, 0), # ῒ -> Ϊ̀ + SIMD[DType.uint32, 4](0x0399, 0x0308, 0x0301, 0), # ΐ -> Ϊ́ + SIMD[DType.uint32, 4](0x0399, 0x0308, 0x0342, 0), # ῗ -> Ϊ͂ + SIMD[DType.uint32, 4](0x03A5, 0x0308, 0x0300, 0), # ῢ -> Ϋ̀ + SIMD[DType.uint32, 4](0x03A5, 0x0308, 0x0301, 0), # ΰ -> Ϋ́ + SIMD[DType.uint32, 4](0x03A5, 0x0308, 0x0342, 0), # ῧ -> Ϋ͂ + SIMD[DType.uint32, 4](0x0046, 0x0046, 0x0049, 0), # ffi -> FFI + SIMD[DType.uint32, 4](0x0046, 0x0046, 0x004C, 0), # ffl -> FFL +) diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index 9f81629b8a..d02ad51563 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -918,6 +918,8 @@ def test_isupper(): assert_false(String("AsDG").isupper()) assert_true(String("ABC123").isupper()) assert_false(String("1!").isupper()) + assert_true(String("É").isupper()) + assert_false(String("é").isupper()) def test_islower(): @@ -936,6 +938,8 @@ def test_islower(): assert_false(String("asdFDg").islower()) assert_true(String("abc123").islower()) assert_false(String("1!").islower()) + assert_true(String("é").islower()) + assert_false(String("É").islower()) def test_lower(): @@ -945,8 +949,8 @@ def test_lower(): assert_equal(String("MOJO🔥").lower(), "mojo🔥") - # TODO(#26444): Non-ASCII not supported yet - assert_equal(String("É").lower(), "É") + assert_equal(String("É").lower(), "é") + assert_equal(String("é").lower(), "é") def test_upper(): @@ -956,8 +960,8 @@ def test_upper(): assert_equal(String("mojo🔥").upper(), "MOJO🔥") - # TODO(#26444): Non-ASCII not supported yet assert_equal(String("É").upper(), "É") + assert_equal(String("é").upper(), "É") def test_isspace(): From 902d83712adb59937867898d607ef3406457683d Mon Sep 17 00:00:00 2001 From: Lukas Hermann <1734032+lsh@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:24:30 -0800 Subject: [PATCH 1894/2019] [stdlib] Mark `Span` and `StringSlice` as register_passable trivial Since `Span` is just a pointer and length, it's a trivial type, and `StringSlice` is just a wrapper around `Span`. This should provide a small speedup. MODULAR_ORIG_COMMIT_REV_ID: 6ce887ad907e0851f353a9ee9f3cb84eaa53c2cb --- stdlib/src/utils/span.mojo | 1 + stdlib/src/utils/string_slice.mojo | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 40098ecafc..3ebb316f2d 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -92,6 +92,7 @@ struct _SpanIter[ @value +@register_passable("trivial") struct Span[ is_mutable: Bool, //, T: CollectionElement, diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 50619e7978..b7fe463728 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -241,6 +241,7 @@ struct _StringSliceIter[ @value +@register_passable("trivial") struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( Stringable, Sized, @@ -299,7 +300,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( `unsafe_from_utf8` MUST be valid UTF-8 encoded data. """ - self._slice = unsafe_from_utf8^ + self._slice = unsafe_from_utf8 fn __init__(out self, *, unsafe_from_utf8_strref: StringRef): """Construct a new StringSlice from a `StringRef` pointing to UTF-8 @@ -692,7 +693,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( """ if end == -1: return self.find(prefix, start) == start - return StringSlice[__origin_of(self)]( + return StringSlice[origin]( ptr=self.unsafe_ptr() + start, length=end - start ).startswith(prefix) @@ -714,7 +715,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( return False if end == -1: return self.rfind(suffix, start) + len(suffix) == len(self) - return StringSlice[__origin_of(self)]( + return StringSlice[origin]( ptr=self.unsafe_ptr() + start, length=end - start ).endswith(suffix) @@ -1010,7 +1011,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( str_len = eol_start - offset + int(keepends) * eol_length s = StringSlice[O](ptr=ptr + offset, length=str_len) - output.append(s^) + output.append(s) offset = eol_start + eol_length return output^ @@ -1046,7 +1047,7 @@ trait Stringlike: fn _to_string_list[ - T: CollectionElement, //, + T: CollectionElement, # TODO(MOCO-1446): Make `T` parameter inferred len_fn: fn (T) -> Int, unsafe_ptr_fn: fn (T) -> UnsafePointer[Byte], ](items: List[T]) -> List[String]: @@ -1088,7 +1089,7 @@ fn _to_string_list[ fn len_fn(v: StringSlice[O]) -> Int: return v.byte_length() - return _to_string_list[len_fn, unsafe_ptr_fn](items) + return _to_string_list[items.T, len_fn, unsafe_ptr_fn](items) @always_inline @@ -1113,7 +1114,7 @@ fn _to_string_list[ fn len_fn(v: Span[Byte, O]) -> Int: return len(v) - return _to_string_list[len_fn, unsafe_ptr_fn](items) + return _to_string_list[items.T, len_fn, unsafe_ptr_fn](items) # ===----------------------------------------------------------------------===# @@ -1356,7 +1357,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): conversion_flag not in Self.supported_conversion_flags ): var f = String(_build_slice(field_ptr, new_idx, field_len)) - _ = field^ + _ = field raise Error('Conversion flag "' + f + '" not recognised.') self.conversion_flag = conversion_flag field = _build_slice(field_ptr, 0, exclamation_index) From 65329a205d0ca807e21ef2124d6d3392ec20788f Mon Sep 17 00:00:00 2001 From: Rasool Sharifi Date: Tue, 19 Nov 2024 15:16:51 -0800 Subject: [PATCH 1895/2019] [stdlib] Public title Added conversion from float8 to float32 MODULAR_ORIG_COMMIT_REV_ID: 441ab0aaac7dbf2235f26dc14f0c38bc60f12c64 --- stdlib/src/builtin/simd.mojo | 153 +++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a299140a57..c2429c5309 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1625,6 +1625,29 @@ struct SIMD[type: DType, size: Int]( # use the optimizations defined above. return self.cast[DType.float32]().cast[target]() + @parameter + if type in (DType.float8e4m3, DType.float8e5m2): + constrained[ + target in (DType.float32, DType.float16, DType.bfloat16), + ( + "Only FP8->F32, FP8->F16, and FP8->BF16 castings are" + " implemented." + ), + ]() + + @parameter + if target is DType.float32: + return _convert_float8_to_f32( + rebind[SIMD[type, size]](self) + ).cast[target]() + elif target is DType.float16: + return _convert_float8_to_f16( + rebind[SIMD[type, size]](self) + ).cast[target]() + return rebind[SIMD[target, size]]( + self.cast[DType.float32]().cast[DType.bfloat16]() + ) + @parameter if has_neon() and (type is DType.bfloat16 or target is DType.bfloat16): # TODO(KERN-228): support BF16 on neon systems. @@ -2931,6 +2954,136 @@ fn _powi[type: DType](base: Scalar[type], exp: Int32) -> __type_of(base): return res +# ===----------------------------------------------------------------------=== # +# float8 +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn _convert_float8_to_f32_scaler[ + type: DType, +](x: Scalar[type]) -> Scalar[DType.float32]: + var kF32_NaN: UInt32 = 0x7FFFFFFF + var FP8_NUM_BITS = 8 + var IS_E4M3 = type is DType.float8e4m3 + var FP8_NUM_MANTISSA_BITS = FPUtils[type].mantissa_width() + var FP8_NUM_EXPONENT_BITS = FPUtils[type].exponent_width() + var FP32_NUM_BITS = 32 + var FP8_EXPONENT_MASK: UInt8 = (1 << FP8_NUM_EXPONENT_BITS) - 1 + var FP8_MANTISSA_MASK: UInt8 = (1 << FP8_NUM_MANTISSA_BITS) - 1 + var FP8_MAX_EXPONENT = 7 if IS_E4M3 else 15 + var FP8_EXPONENT_BIAS = 7 if IS_E4M3 else 15 + var FP32_EXPONENT_BIAS = 127 + var FP32_NUM_MANTISSA_BITS = 23 + + var f8: UInt8 = bitcast[DType.uint8](x) + + var sign: UInt32 = (f8.cast[DType.uint32]() >> (FP8_NUM_BITS - 1)) & 1 + var exp: UInt32 = ((f8 >> FP8_NUM_MANTISSA_BITS) & FP8_EXPONENT_MASK).cast[ + DType.uint32 + ]() + var mantissa: UInt32 = (f8 & FP8_MANTISSA_MASK).cast[DType.uint32]() + + var f: UInt32 = (sign << (FP32_NUM_BITS - 1)) + + if IS_E4M3 and exp == 15 and mantissa == 0x7: + f = kF32_NaN + elif exp > 0 and ( + IS_E4M3 or exp < (FP8_MAX_EXPONENT + FP8_EXPONENT_BIAS + 1) + ): + # normal + exp += FP32_EXPONENT_BIAS - FP8_EXPONENT_BIAS + f |= exp << FP32_NUM_MANTISSA_BITS + f |= mantissa << (FP32_NUM_MANTISSA_BITS - FP8_NUM_MANTISSA_BITS) + elif exp == 0: + if mantissa: + # subnormal + exp += (FP32_EXPONENT_BIAS - FP8_EXPONENT_BIAS) + 1 + while (mantissa & (1 << FP8_NUM_MANTISSA_BITS)) == 0: + mantissa <<= 1 + exp -= 1 + mantissa = mantissa & FP8_MANTISSA_MASK.cast[DType.uint32]() + f |= exp << FP32_NUM_MANTISSA_BITS + f |= mantissa << (FP32_NUM_MANTISSA_BITS - FP8_NUM_MANTISSA_BITS) + else: + # sign-preserving zero + pass + else: + if mantissa == 0: + # Sign-preserving infinity + f |= 0x7F800000 + else: + # Canonical NaN + f = kF32_NaN + + return bitcast[DType.float32](f) + + +@always_inline +fn _convert_float8_to_f32[ + type: DType, + size: Int, +](val: SIMD[type, size]) -> SIMD[DType.float32, size]: + @parameter + if is_nvidia_gpu() and _is_sm_9x(): + return _convert_float8_to_f16(rebind[SIMD[type, size]](val)).cast[ + DType.float32 + ]() + else: + + @always_inline + @parameter + fn wrapper_fn[ + input_type: DType, result_type: DType + ](val: Scalar[input_type]) capturing -> Scalar[result_type]: + return rebind[Scalar[result_type]]( + _convert_float8_to_f32_scaler(rebind[Scalar[type]](val)) + ) + + return _simd_apply[wrapper_fn, DType.float32, size](val) + + +@always_inline +fn _convert_float8_to_f16[ + type: DType, + size: Int, +](val: SIMD[type, size],) -> SIMD[DType.float16, size]: + @parameter + if is_nvidia_gpu() and _is_sm_9x(): + alias asm_prefix = "cvt.rn.f16x2.e4m3x2" if type is DType.float8e4m3 else "cvt.rn.f16x2.e5m2x2" + var val_uint8 = bitcast[DType.uint8](val) + + @parameter + if size > 1: + var res = SIMD[DType.uint16, size]() + + @parameter + for i in range(0, size, 2): + var f8x2 = val_uint8.slice[2, offset=i]() + var f16x2_f8x2 = inlined_assembly[ + asm_prefix + " $0, $1;", + Scalar[DType.uint32], + constraints="=r,h", + has_side_effect=False, + ](bitcast[DType.uint16, 1](f8x2)) + var ui16x2 = bitcast[DType.uint16, 2](f16x2_f8x2) + res = res.insert[offset=i](ui16x2) + return bitcast[DType.float16](res) + else: + var f16x2_f8x2 = inlined_assembly[ + asm_prefix + " $0, $1;", + Scalar[DType.uint32], + constraints="=r,h", + has_side_effect=False, + ](val_uint8.cast[DType.uint16]()) + var ui16x2 = bitcast[DType.uint16, 2](f16x2_f8x2) + return bitcast[DType.float16](ui16x2[0]) + else: + return _convert_float8_to_f32(rebind[SIMD[type, size]](val)).cast[ + DType.float16 + ]() + + # ===----------------------------------------------------------------------=== # # bfloat16 # ===----------------------------------------------------------------------=== # From f5e83bd067a7dada6deeb2fd0e13acf66b2f06fd Mon Sep 17 00:00:00 2001 From: Konstantinos Krommydas Date: Tue, 19 Nov 2024 20:06:54 -0500 Subject: [PATCH 1896/2019] [stdlib] Fixes memory.mojo Makes mem-ops in memory.mojo more generic. MODULAR_ORIG_COMMIT_REV_ID: 9ac40b0fb895905026ff386e194954bfb130d498 --- stdlib/src/memory/memory.mojo | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 8384279c65..ab215ab5b0 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -24,7 +24,7 @@ from sys import ( alignof, llvm_intrinsic, sizeof, - is_nvidia_gpu, + is_gpu, external_call, simdwidthof, simdbitwidth, @@ -155,7 +155,7 @@ fn _memcpy_impl( """ @parameter - if is_nvidia_gpu(): + if is_gpu(): alias chunk_size = simdbitwidth() var vector_end = _align_down(n, chunk_size) for i in range(0, vector_end, chunk_size): @@ -349,7 +349,7 @@ fn stack_allocation[ count: Int, type: DType, /, - alignment: Int = alignof[type]() if is_nvidia_gpu() else 1, + alignment: Int = alignof[type]() if is_gpu() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> UnsafePointer[Scalar[type], address_space]: """Allocates data buffer space on the stack given a data type and number of @@ -376,7 +376,7 @@ fn stack_allocation[ type: AnyType, /, name: Optional[StringLiteral] = None, - alignment: Int = alignof[type]() if is_nvidia_gpu() else 1, + alignment: Int = alignof[type]() if is_gpu() else 1, address_space: AddressSpace = AddressSpace.GENERIC, ]() -> UnsafePointer[type, address_space]: """Allocates data buffer space on the stack given a data type and number of @@ -394,7 +394,7 @@ fn stack_allocation[ """ @parameter - if is_nvidia_gpu(): + if is_gpu(): # On NVGPU, SHARED and PARAM address spaces lower to global memory. @parameter if address_space in (_GPUAddressSpace.SHARED, _GPUAddressSpace.PARAM): @@ -436,12 +436,12 @@ fn _malloc[ type: AnyType, /, *, - alignment: Int = alignof[type]() if is_nvidia_gpu() else 1, + alignment: Int = alignof[type]() if is_gpu() else 1, ](size: Int, /) -> UnsafePointer[ type, AddressSpace.GENERIC, alignment=alignment ]: @parameter - if is_nvidia_gpu(): + if is_gpu(): return external_call[ "malloc", UnsafePointer[NoneType, AddressSpace.GENERIC] ](size).bitcast[type]() @@ -459,7 +459,7 @@ fn _malloc[ @always_inline fn _free(ptr: UnsafePointer[_, AddressSpace.GENERIC, *_]): @parameter - if is_nvidia_gpu(): + if is_gpu(): libc.free(ptr.bitcast[NoneType]()) else: __mlir_op.`pop.aligned_free`(ptr.address) From bfea890986991f9b0804a8083aa95b61ab8664d1 Mon Sep 17 00:00:00 2001 From: Fabio Riccardi Date: Tue, 19 Nov 2024 17:27:06 -0800 Subject: [PATCH 1897/2019] [stdlib] Added support for sin and cos on AMD GPUs. using llvm_intrinsic instead of _call_libm on AMD GPUs MODULAR_ORIG_COMMIT_REV_ID: bbd8e506d72f335a62425dbfb62f94b72ec339f9 --- stdlib/src/math/math.mojo | 7 +++++++ stdlib/src/sys/__init__.mojo | 1 + 2 files changed, 8 insertions(+) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 6b3876a989..e8cc3c1899 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -28,6 +28,7 @@ from sys import ( has_avx512f, simdwidthof, is_nvidia_gpu, + is_amd_gpu, sizeof, ) @@ -1412,6 +1413,8 @@ fn cos[ return _call_ptx_intrinsic[ instruction="cos.approx.ftz.f32", constraints="=f,f" ](x) + elif is_amd_gpu(): + return llvm_intrinsic["llvm.cos", __type_of(x)](x) else: return _call_libm["cos"](x) @@ -1445,6 +1448,8 @@ fn sin[ return _call_ptx_intrinsic[ instruction="sin.approx.ftz.f32", constraints="=f,f" ](x) + elif is_amd_gpu(): + return llvm_intrinsic["llvm.sin", __type_of(x)](x) else: return _call_libm["sin"](x) @@ -1622,6 +1627,8 @@ fn log10(x: SIMD) -> __type_of(x): ](x) * log10_2 ) + elif is_amd_gpu(): + return llvm_intrinsic["llvm.log10", __type_of(x)](x) return _call_libm["log10"](x) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 099f406f06..632dde283e 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -48,6 +48,7 @@ from .info import ( simdwidthof, sizeof, is_nvidia_gpu, + is_amd_gpu, is_gpu, ) from .intrinsics import ( From 9ff9502beb5c1a00b615908fa6b7c1430025d1aa Mon Sep 17 00:00:00 2001 From: Lukas Hermann <1734032+lsh@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:46:11 -0800 Subject: [PATCH 1898/2019] [stdlib] Change `CeilDivable` and `CeilDivableRaises` to just require `__ceildiv__`. The traits currently require `__floordiv__`, `__rfloordiv__`, and `__neg__`. Modelling `CieldDivable{Raising}` using those traits ignores unsigned numeric types where `__neg__` doesn't make sense. Having the types implement `__ceildiv__` directly reduces the change of implementing it wrong. MODULAR_ORIG_COMMIT_REV_ID: f043dc093244082d03eb643c8080341c009b8855 --- stdlib/src/builtin/float_literal.mojo | 12 ++++ stdlib/src/builtin/int.mojo | 13 ++++ stdlib/src/builtin/int_literal.mojo | 13 ++++ stdlib/src/builtin/simd.mojo | 17 ++++++ stdlib/src/builtin/uint.mojo | 13 ++++ stdlib/src/math/math.mojo | 87 +++++++++------------------ 6 files changed, 96 insertions(+), 59 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index b454ba6d94..81308401b5 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -447,6 +447,18 @@ struct FloatLiteral( """ return rhs // self + @always_inline + fn __ceildiv__(self, denominator: Self) -> Self: + """Return the rounded-up result of dividing self by denominator. + + Args: + denominator: The denominator. + + Returns: + The ceiling of dividing numerator by denominator. + """ + return -(self // -denominator) + # TODO - maybe __pow__? # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index f68e1d0c0d..960ac5e00b 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1145,6 +1145,19 @@ struct Int( result = Python.py_long_as_ssize_t(obj) + @always_inline + fn __ceildiv__(self, denominator: Self) -> Self: + """Return the rounded-up result of dividing self by denominator. + + + Args: + denominator: The denominator. + + Returns: + The ceiling of dividing numerator by denominator. + """ + return -(self // -denominator) + # ===-------------------------------------------------------------------===# # Methods # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 1b17508a3b..0432c933d3 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -704,6 +704,19 @@ struct IntLiteral( """ return str(Int(self)) + @always_inline + fn __ceildiv__(self, denominator: Self) -> Self: + """Return the rounded-up result of dividing self by denominator. + + + Args: + denominator: The denominator. + + Returns: + The ceiling of dividing numerator by denominator. + """ + return -(self // -denominator) + # ===----------------------------------------------------------------------===# # Methods # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c2429c5309..9d9fe69a00 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1573,6 +1573,23 @@ struct SIMD[type: DType, size: Int]( """ hasher._update_with_simd(self) + @always_inline + fn __ceildiv__(self, denominator: Self) -> Self: + """Return the rounded-up result of dividing self by denominator. + + + Args: + denominator: The denominator. + + Returns: + The ceiling of dividing numerator by denominator. + """ + + @parameter + if type.is_signed(): + return -(self // -denominator) + return (self + denominator - 1) // denominator + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 5a914e8da1..2c0f3f7de9 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -383,6 +383,19 @@ struct UInt(IntLike, _HashableWithHasher): """ return __mlir_op.`index.or`(self.value, rhs.value) + @always_inline + fn __ceildiv__(self, denominator: Self) -> Self: + """Return the rounded-up result of dividing self by denominator. + + + Args: + denominator: The denominator. + + Returns: + The ceiling of dividing numerator by denominator. + """ + return __mlir_op.`index.ceildivu`(self.value, denominator.value) + # ===----------------------------------------------------------------------===# # In place operations. # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index e8cc3c1899..7371d2a3df 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -95,7 +95,7 @@ fn ceil[T: Ceilable, //](value: T) -> T: @always_inline fn ceildiv[T: CeilDivable, //](numerator: T, denominator: T) -> T: - """Return the rounded-up result of dividing x by y. + """Return the rounded-up result of dividing numerator by denominator. Parameters: T: A type that support floor division. @@ -105,14 +105,15 @@ fn ceildiv[T: CeilDivable, //](numerator: T, denominator: T) -> T: denominator: The denominator. Returns: - The ceiling of dividing x by y. + The ceiling of dividing numerator by denominator. """ - return -(numerator // -denominator) + # return -(numerator // -denominator) + return numerator.__ceildiv__(denominator) @always_inline fn ceildiv[T: CeilDivableRaising, //](numerator: T, denominator: T) raises -> T: - """Return the rounded-up result of dividing x by y, potentially raising. + """Return the rounded-up result of dividing numerator by denominator, potentially raising. Parameters: T: A type that support floor division. @@ -122,39 +123,25 @@ fn ceildiv[T: CeilDivableRaising, //](numerator: T, denominator: T) raises -> T: denominator: The denominator. Returns: - The ceiling of dividing x by y. + The ceiling of dividing numerator by denominator. """ - return -(numerator // -denominator) + return numerator.__ceildiv__(denominator) # NOTE: this overload is needed because of overload precedence; without it the # Int overload would be preferred, and ceildiv wouldn't work on IntLiteral. @always_inline fn ceildiv(numerator: IntLiteral, denominator: IntLiteral) -> IntLiteral: - """Return the rounded-up result of dividing x by y. + """Return the rounded-up result of dividing numerator by denominator. Args: numerator: The numerator. denominator: The denominator. Returns: - The ceiling of dividing x by y. + The ceiling of dividing numerator by denominator. """ - return -(numerator // -denominator) - - -@always_inline("nodebug") -fn ceildiv(numerator: UInt, denominator: UInt) -> UInt: - """Return the rounded-up result of dividing x by y. - - Args: - numerator: The numerator. - denominator: The denominator. - - Returns: - The ceiling of dividing x by y. - """ - return __mlir_op.`index.ceildivu`(numerator.value, denominator.value) + return numerator.__ceildiv__(denominator) # ===----------------------------------------------------------------------=== # @@ -2577,29 +2564,20 @@ trait CeilDivable: struct Foo(CeilDivable): var x: Float64 - fn __floordiv__(self, other: Self) -> Self: - return self.x // other.x - - fn __rfloordiv__(self, other: Self) -> Self: - return other // self - - fn __neg__(self) -> Self: - return -self.x + fn __ceildiv__(self, denominator: Self) -> Self: + return -(self.x // -denominator.x) ``` """ - # TODO(MOCO-333): Reconsider these signatures when we have parametric traits - # or associated types. - @doc_private - fn __floordiv__(self, other: Self) -> Self: - ... + fn __ceildiv__(self, denominator: Self) -> Self: + """Return the rounded-up result of dividing self by denominator. - @doc_private - fn __rfloordiv__(self, other: Self) -> Self: - ... + Args: + denominator: The denominator. - @doc_private - fn __neg__(self) -> Self: + Returns: + The ceiling of dividing numerator by denominator. + """ ... @@ -2619,29 +2597,20 @@ trait CeilDivableRaising: struct Foo(CeilDivableRaising): var x: object - fn __floordiv__(self, other: Self) raises -> Self: - return self.x // other.x - - fn __rfloordiv__(self, other: Self) raises -> Self: - return other // self - - fn __neg__(self) raises -> Self: - return -self.x + fn __ceildiv__(self, denominator: Self) raises -> Self: + return -(self.x // -denominator.x) ``` """ - # TODO(MOCO-333): Reconsider these signatures when we have parametric traits - # or associated types. - @doc_private - fn __floordiv__(self, other: Self) raises -> Self: - ... + fn __ceildiv__(self, denominator: Self) raises -> Self: + """Return the rounded-up result of dividing self by denominator. - @doc_private - fn __rfloordiv__(self, other: Self) raises -> Self: - ... + Args: + denominator: The denominator. - @doc_private - fn __neg__(self) raises -> Self: + Returns: + The ceiling of dividing numerator by denominator. + """ ... From 793968ab7e9c56aa7a9665f55af369ae57dbc55a Mon Sep 17 00:00:00 2001 From: Konstantinos Krommydas Date: Wed, 20 Nov 2024 12:26:54 -0800 Subject: [PATCH 1899/2019] [stdlib] Makes sleep function more generic for GPUs. Adds support for more GPUs to the sleep function. MODULAR_ORIG_COMMIT_REV_ID: c1e1f113c7f51e1386f3bbce5860c4e778e8cdf5 --- stdlib/src/time/time.mojo | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 4002648a9a..a8a38b5841 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -24,6 +24,7 @@ from sys import ( external_call, os_is_linux, os_is_windows, + is_amd_gpu, is_nvidia_gpu, llvm_intrinsic, ) @@ -353,6 +354,12 @@ fn sleep(sec: Float64): nsec.cast[DType.int32]() ) return + elif is_amd_gpu(): + var nsec = sec * 1.0e9 + llvm_intrinsic["llvm.amdgcn.s.sleep", NoneType]( + nsec.cast[DType.int32]() + ) + return alias NANOSECONDS_IN_SECOND = 1_000_000_000 var total_secs = floor(sec) @@ -376,7 +383,7 @@ fn sleep(sec: Int): """ @parameter - if is_nvidia_gpu(): + if is_nvidia_gpu() or is_amd_gpu(): return sleep(Float64(sec)) @parameter From 5d231bac776688fe5c8f417821fba1b331d80756 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 20 Nov 2024 16:23:47 -0800 Subject: [PATCH 1900/2019] [******][GPU] Generalize the GPU operations to be vendor agnostic MODULAR_ORIG_COMMIT_REV_ID: 6c4bb72ec076c976073ef01266dc1025bd12c090 --- stdlib/src/builtin/debug_assert.mojo | 6 +++--- stdlib/src/builtin/file_descriptor.mojo | 4 ++-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/io.mojo | 7 ++++++- stdlib/src/builtin/simd.mojo | 10 ++++++---- stdlib/src/memory/pointer.mojo | 1 - stdlib/src/os/os.mojo | 4 ++-- stdlib/src/utils/write.mojo | 4 ++-- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 6d33f5f5e4..14a2a258f1 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from os import abort -from sys import is_nvidia_gpu, llvm_intrinsic +from sys import is_nvidia_gpu, is_gpu, llvm_intrinsic from sys._build import is_debug_build from sys.param_env import env_get_string from sys.ffi import external_call, c_uint, c_size_t, c_char @@ -44,7 +44,7 @@ fn _assert_enabled[assert_mode: StringLiteral, cpu_only: Bool]() -> Bool: ]() @parameter - if defined_mode == "none" or (is_nvidia_gpu() and cpu_only): + if defined_mode == "none" or (is_gpu() and cpu_only): return False elif defined_mode == "all" or defined_mode == "warn" or is_debug_build(): return True @@ -236,7 +236,7 @@ fn _debug_assert_msg( var stdout = sys.stdout @parameter - if is_nvidia_gpu(): + if is_gpu(): var buffer = _WriteBufferHeap[4096](stdout) buffer.write("At ", loc, ": ") _write_gpu_thread_context(buffer) diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 241fa10bf3..76c2512dad 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -26,7 +26,7 @@ f.close() from utils import Span from builtin.io import _printf from sys.ffi import external_call -from sys.info import is_nvidia_gpu +from sys.info import is_gpu from memory import UnsafePointer @@ -67,7 +67,7 @@ struct FileDescriptor(Writer): var len_bytes = len(bytes) @parameter - if is_nvidia_gpu(): + if is_gpu(): _printf["%*s"](len_bytes, bytes.unsafe_ptr()) else: written = external_call["write", Int32]( diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 960ac5e00b..a2a8d7a150 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -32,7 +32,7 @@ from memory import UnsafePointer from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select -from sys import is_nvidia_gpu, bitwidthof +from sys import bitwidthof # ===----------------------------------------------------------------------=== # # Indexer diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index c2d3f82a33..9efa7ae54a 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -20,6 +20,8 @@ from sys import ( external_call, stdout, is_nvidia_gpu, + is_amd_gpu, + is_gpu, _libc as libc, ) from sys._libc import dup, fclose, fdopen, fflush @@ -170,6 +172,9 @@ fn _printf[ _ = external_call["vprintf", Int32]( fmt.unsafe_cstr_ptr(), Pointer.address_of(loaded_pack) ) + elif is_amd_gpu(): + # constrained[False, "_printf on AMDGPU is not implemented"]() + pass else: with _fdopen(file) as fd: _ = __mlir_op.`pop.external_call`[ @@ -258,7 +263,7 @@ fn print[ write_buffered[buffer_size=4096](file, values, sep=sep, end=end) @parameter - if not is_nvidia_gpu(): + if not is_gpu(): if flush: _flush(file=file) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9d9fe69a00..a0c68f35f7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -26,6 +26,8 @@ from sys import ( prefetch, simdwidthof, is_nvidia_gpu, + is_gpu, + is_amd_gpu, bitwidthof, ) from sys.info import _current_arch, _is_sm_8x, _is_sm_9x @@ -156,12 +158,12 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: @always_inline("nodebug") fn _has_native_bf16_support() -> Bool: - return is_nvidia_gpu() + return is_gpu() @always_inline("nodebug") fn _has_native_f8_support() -> Bool: - return _is_sm_9x() or is_nvidia_gpu["sm_89"]() + return _is_sm_9x() or is_nvidia_gpu["sm_89"]() or is_amd_gpu() # ===----------------------------------------------------------------------=== # @@ -222,7 +224,7 @@ struct SIMD[type: DType, size: Int]( alias MIN_FINITE = Self(_min_finite[type]()) """Returns the minimum (lowest) finite value of SIMD value.""" - alias _default_alignment = alignof[Scalar[type]]() if is_nvidia_gpu() else 1 + alias _default_alignment = alignof[Scalar[type]]() if is_gpu() else 1 # ===-------------------------------------------------------------------===# # Life cycle methods @@ -3366,7 +3368,7 @@ fn _write_scalar[ elif dtype.is_integral(): @parameter - if is_nvidia_gpu(): + if is_gpu(): var err = _try_write_int(writer, value) if err: abort( diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index dfccdc524c..06ddb0f6f6 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -19,7 +19,6 @@ from memory import Pointer ``` """ -from sys import is_nvidia_gpu # ===----------------------------------------------------------------------===# # AddressSpace diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index d66008b196..1678cc89e6 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -20,7 +20,7 @@ from os import listdir """ from collections import List, InlineArray -from sys import os_is_linux, os_is_windows, is_nvidia_gpu, external_call +from sys import os_is_linux, os_is_windows, is_gpu, external_call from sys.ffi import c_char, OpaquePointer from memory import UnsafePointer @@ -265,7 +265,7 @@ fn abort[ """ @parameter - if not is_nvidia_gpu(): + if not is_gpu(): print(message, flush=True) return abort[result]() diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 5cf8ba1d3e..93270dc2b5 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -15,7 +15,7 @@ from collections import InlineArray from memory import memcpy, UnsafePointer from utils import Span, StaticString -from sys.info import is_nvidia_gpu +from sys.info import is_gpu from builtin.io import _printf @@ -365,7 +365,7 @@ fn write_buffered[ """ @parameter - if is_nvidia_gpu(): + if is_gpu(): # Stack space is very small on GPU due to many threads, so use heap var buffer = _WriteBufferHeap[buffer_size](writer^) write_args(buffer, args, sep=sep, end=end) From 16f137c8fdbce979c8ff691ef548b3017cb531e5 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 21 Nov 2024 11:35:07 -0700 Subject: [PATCH 1901/2019] [stdlib] Rename `Arc` to `ArcPointer` Rename `Arc` to `ArcPointer` for consistency with `OwnedPointer` and to help signal intent. MODULAR_ORIG_COMMIT_REV_ID: deef3eac08537bb8dda1ebbc8761ac347729d9c3 --- docs/changelog.md | 6 +++-- stdlib/src/builtin/object.mojo | 12 +++++----- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/arc.mojo | 38 +++++++++++++++++--------------- stdlib/test/memory/test_arc.mojo | 24 ++++++++++---------- 5 files changed, 43 insertions(+), 39 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d6c831da5b..a9f924952b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -250,7 +250,7 @@ what we publish. - Introduced a new `Deque` (double-ended queue) collection type, based on a dynamically resizing circular buffer for efficient O(1) additions and removals at both ends as well as O(1) direct access to all elements. - + The `Deque` supports the full Python `collections.deque` API, ensuring that all expected deque operations perform as in Python. @@ -513,6 +513,8 @@ what we publish. self.value = value ``` +- `Arc` has been renamed to `ArcPointer`, for consistency with `OwnedPointer`. + ### ❌ Removed - The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your @@ -557,7 +559,7 @@ what we publish. - Error messages that include type names no longer include inferred or defaulted parameters when they aren't needed. For example, previously Mojo complained about things like: - + ```plaintext ... cannot be converted from 'UnsafePointer[UInt, 0, _default_alignment::AnyType](), MutableAnyOrigin]' to 'UnsafePointer[Int, 0, _default_alignment[::AnyType](), MutableAnyOrigin]' ``` diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index c54d99fc18..0936da0cb1 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -19,7 +19,7 @@ from collections import Dict, List from sys.intrinsics import _type_is_eq from sys.ffi import OpaquePointer -from memory import Arc, memcmp, memcpy, UnsafePointer +from memory import ArcPointer, memcmp, memcpy, UnsafePointer from utils import StringRef, Variant @@ -75,11 +75,11 @@ struct _RefCountedList: ref-counted data types. """ - var impl: Arc[List[_ObjectImpl]] + var impl: ArcPointer[List[_ObjectImpl]] """The list value.""" fn __init__(out self): - self.impl = Arc[List[_ObjectImpl]](List[_ObjectImpl]()) + self.impl = ArcPointer[List[_ObjectImpl]](List[_ObjectImpl]()) @register_passable("trivial") @@ -115,11 +115,11 @@ struct _RefCountedAttrsDict: directly with `x.attr`, the key will always be a `StringLiteral`. """ - var impl: Arc[Dict[StringLiteral, _ObjectImpl]] + var impl: ArcPointer[Dict[StringLiteral, _ObjectImpl]] """The implementation of the map.""" fn __init__(out self): - self.impl = Arc[Dict[StringLiteral, _ObjectImpl]]( + self.impl = ArcPointer[Dict[StringLiteral, _ObjectImpl]]( Dict[StringLiteral, _ObjectImpl]() ) @@ -653,7 +653,7 @@ struct _ObjectImpl( # ===------------------------------------------------------------------=== # @always_inline - fn get_list_ptr(self) -> Arc[List[_ObjectImpl]]: + fn get_list_ptr(self) -> ArcPointer[List[_ObjectImpl]]: return self.get_as_list().lst.bitcast[_RefCountedList]()[].impl @always_inline diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 06684698a7..b2faf4890a 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the memory package.""" -from .arc import Arc +from .arc import ArcPointer from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation from .owned_pointer import OwnedPointer from .pointer import AddressSpace, Pointer diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index fa904d53e8..37c03786f7 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -15,31 +15,31 @@ Example usage: ```mojo -from memory import Arc -var p = Arc(4) +from memory import ArcPointer +var p = ArcPointer(4) var p2 = p p2[]=3 print(3 == p[]) ``` Subscripting(`[]`) is done by `Pointer`, -in order to ensure that the underlying `Arc` outlive the operation. +in order to ensure that the underlying `ArcPointer` outlive the operation. -It is highly DISCOURAGED to manipulate an `Arc` through `UnsafePointer`. +It is highly DISCOURAGED to manipulate an `ArcPointer` through `UnsafePointer`. Mojo's ASAP deletion policy ensure values are destroyed at last use. -Do not unsafely dereference the `Arc` inner `UnsafePointer` field. +Do not unsafely dereference the `ArcPointer` inner `UnsafePointer` field. See [Lifecycle](https://docs.modular.com/mojo/manual/lifecycle/). ```mojo # Illustration of what NOT to do, in order to understand: -print(Arc(String("ok"))._inner[].payload) +print(ArcPointer(String("ok"))._inner[].payload) #........................^ASAP ^already freed ``` Always use `Pointer` subscripting (`[]`): ```mojo -print(Arc(String("ok"))[]) +print(ArcPointer(String("ok"))[]) ``` """ @@ -50,7 +50,7 @@ from builtin.builtin_list import _lit_mut_cast from memory import UnsafePointer, stack_allocation -struct _ArcInner[T: Movable]: +struct _ArcPointerInner[T: Movable]: var refcount: Atomic[DType.uint64] var payload: T @@ -71,7 +71,9 @@ struct _ArcInner[T: Movable]: @register_passable -struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): +struct ArcPointer[T: Movable]( + CollectionElement, CollectionElementNew, Identifiable +): """Atomic reference-counted pointer. This smart pointer owns an instance of `T` indirectly managed on the heap. @@ -86,7 +88,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): T: The type of the stored value. """ - alias _inner_type = _ArcInner[T] + alias _inner_type = _ArcPointerInner[T] var _inner: UnsafePointer[Self._inner_type] @implicit @@ -98,7 +100,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): value: The value to manage. """ self._inner = UnsafePointer[Self._inner_type].alloc(1) - # Cannot use init_pointee_move as _ArcInner isn't movable. + # Cannot use init_pointee_move as _ArcPointerInner isn't movable. __get_address_as_uninit_lvalue(self._inner.address) = Self._inner_type( value^ ) @@ -135,7 +137,7 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): self._inner.free() # FIXME: The origin returned for this is currently self origin, which - # keeps the Arc object alive as long as there are references into it. That + # keeps the ArcPointer object alive as long as there are references into it. That # said, this isn't really the right modeling, we need hierarchical origins # to model the mutability and invalidation of the returned reference # correctly. @@ -174,23 +176,23 @@ struct Arc[T: Movable](CollectionElement, CollectionElementNew, Identifiable): return self._inner[].refcount.load() fn __is__(self, rhs: Self) -> Bool: - """Returns True if the two Arcs point at the same object. + """Returns True if the two ArcPointers point at the same object. Args: - rhs: The other Arc. + rhs: The other ArcPointer. Returns: - True if the two Arcs point at the same object and False otherwise. + True if the two ArcPointers point at the same object and False otherwise. """ return self._inner == rhs._inner fn __isnot__(self, rhs: Self) -> Bool: - """Returns True if the two Arcs point at different objects. + """Returns True if the two ArcPointers point at different objects. Args: - rhs: The other Arc. + rhs: The other ArcPointer. Returns: - True if the two Arcs point at different objects and False otherwise. + True if the two ArcPointers point at different objects and False otherwise. """ return self._inner != rhs._inner diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 394b1d507f..e5c2ba8fd2 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -14,22 +14,22 @@ from collections import List -from memory import Arc, UnsafePointer +from memory import ArcPointer, UnsafePointer from testing import assert_equal, assert_false, assert_true from test_utils import ObservableDel def test_basic(): - var p = Arc(4) + var p = ArcPointer(4) var p2 = p p2[] = 3 assert_equal(3, p[]) def test_is(): - var p = Arc(3) + var p = ArcPointer(3) var p2 = p - var p3 = Arc(3) + var p3 = ArcPointer(3) assert_true(p is p2) assert_false(p is not p2) assert_false(p is p3) @@ -38,12 +38,12 @@ def test_is(): def test_deleter_not_called_until_no_references(): var deleted = False - var p = Arc(ObservableDel(UnsafePointer.address_of(deleted))) + var p = ArcPointer(ObservableDel(UnsafePointer.address_of(deleted))) var p2 = p _ = p^ assert_false(deleted) - var vec = List[Arc[ObservableDel]]() + var vec = List[ArcPointer[ObservableDel]]() vec.append(p2) _ = p2^ assert_false(deleted) @@ -53,13 +53,13 @@ def test_deleter_not_called_until_no_references(): def test_deleter_not_called_until_no_references_explicit_copy(): var deleted = False - var p = Arc(ObservableDel(UnsafePointer.address_of(deleted))) - var p2 = Arc(other=p) + var p = ArcPointer(ObservableDel(UnsafePointer.address_of(deleted))) + var p2 = ArcPointer(other=p) _ = p^ assert_false(deleted) - var vec = List[Arc[ObservableDel]]() - vec.append(Arc(other=p2)^) + var vec = List[ArcPointer[ObservableDel]]() + vec.append(ArcPointer(other=p2)^) _ = p2^ assert_false(deleted) _ = vec^ @@ -67,8 +67,8 @@ def test_deleter_not_called_until_no_references_explicit_copy(): def test_count(): - var a = Arc(10) - var b = Arc(other=a) + var a = ArcPointer(10) + var b = ArcPointer(other=a) var c = a assert_equal(3, a.count()) _ = b^ From 6095b4a777efd752a87d11a697297ac8e8363388 Mon Sep 17 00:00:00 2001 From: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:27:04 -0600 Subject: [PATCH 1902/2019] [External] [stdlib] [NFC] Move Formatter structs to their own module. Add `TODO`s and observations (#51324) [External] [stdlib] [NFC] Move Formatter structs to their own module Move Formatter structs to their own module. Add `TODO`s and observations. Motivation: These implementations will get more and more complex as time goes on. They are extensive and specific enough that they should be in their own module. Eventually they should become public structs, but that remains in the future until they are feature complete. Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3782 MODULAR_ORIG_COMMIT_REV_ID: 62ab937be19baa549c5bd7fa88faf901485d2bd0 --- stdlib/src/builtin/string_literal.mojo | 4 +- stdlib/src/collections/string.mojo | 3 +- stdlib/src/utils/format.mojo | 719 +++++++++++++++++++++++++ stdlib/src/utils/string_slice.mojo | 612 +-------------------- 4 files changed, 722 insertions(+), 616 deletions(-) create mode 100644 stdlib/src/utils/format.mojo diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c363f1b7bd..0373dd2f43 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -23,11 +23,9 @@ from hashlib._hasher import _HashableWithHasher, _Hasher from utils import StringRef, Span, StringSlice, StaticString from utils import Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type - +from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry from utils.string_slice import ( _StringSliceIter, - _FormatCurlyEntry, - _CurlyEntryFormattable, _to_string_list, ) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index f5046bfc74..9f47e618e7 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -37,13 +37,12 @@ from utils import ( Writable, Writer, ) +from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry from utils.string_slice import ( _utf8_byte_type, _StringSliceIter, _unicode_codepoint_utf8_byte_length, _shift_unicode_to_utf8, - _FormatCurlyEntry, - _CurlyEntryFormattable, _to_string_list, ) diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo new file mode 100644 index 0000000000..cac20f191d --- /dev/null +++ b/stdlib/src/utils/format.mojo @@ -0,0 +1,719 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements Formatting utilities.""" + +from collections import Optional +from memory import UnsafePointer +from utils.string_slice import Stringlike + +# TODO: _FormatCurlyEntry and _FormatSpec should be public in the future for +# people who want to write their own templating engines. This is not yet done +# because the implementation is incomplete and we are missing crucial features. + +# ===----------------------------------------------------------------------===# +# Formatter +# ===----------------------------------------------------------------------===# + + +# NOTE(#3765): an interesting idea would be to allow custom start and end +# characters for formatting (passed as parameters to Formatter), this would be +# useful for people developing custom templating engines as it would allow +# detemining e.g. `` [...] `` html tags. +# And going a step further it might even be worth it adding custom format +# specification start character, and custom format specs themselves (by defining +# a trait that all format specifications conform to) +@value +struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): + """The struct that handles `Stringlike` formatting by curly braces entries. + This is internal for the types: `String`, `StringLiteral` and `StringSlice`. + """ + + var first_curly: Int + """The index of an opening brace around a substitution field.""" + var last_curly: Int + """The index of a closing brace around a substitution field.""" + # TODO: ord("a") conversion flag not supported yet + var conversion_flag: UInt8 + """The type of conversion for the entry: {ord("s"), ord("r")}.""" + var format_spec: Optional[_FormatSpec] + """The format specifier.""" + # TODO: ord("a") conversion flag not supported yet + alias supported_conversion_flags = SIMD[DType.uint8, 2](ord("s"), ord("r")) + """Currently supported conversion flags: `__str__` and `__repr__`.""" + alias _FieldVariantType = Variant[String, Int, NoneType, Bool] + """Purpose of the `Variant` `Self.field`: + + - `Int` for manual indexing: (value field contains `0`). + - `NoneType` for automatic indexing: (value field contains `None`). + - `String` for **kwargs indexing: (value field contains `foo`). + - `Bool` for escaped curlies: (value field contains False for `{` or True + for `}`). + """ + var field: Self._FieldVariantType + """Store the substitution field. See `Self._FieldVariantType` docstrings for + more details.""" + alias _args_t = VariadicPack[element_trait=_CurlyEntryFormattable, *_] + """Args types that are formattable by curly entry.""" + + fn __init__(out self, *, other: Self): + """Construct a format entry by copying another. + + Args: + other: The other format entry. + """ + self.first_curly = other.first_curly + self.last_curly = other.last_curly + self.conversion_flag = other.conversion_flag + self.field = Self._FieldVariantType(other=other.field) + self.format_spec = other.format_spec + + fn __init__( + inout self, + first_curly: Int, + last_curly: Int, + field: Self._FieldVariantType, + conversion_flag: UInt8 = 0, + format_spec: Optional[_FormatSpec] = None, + ): + """Construct a format entry. + + Args: + first_curly: The index of an opening brace around a substitution + field. + last_curly: The index of a closing brace around a substitution + field. + field: Store the substitution field. + conversion_flag: The type of conversion for the entry. + format_spec: The format specifier. + """ + self.first_curly = first_curly + self.last_curly = last_curly + self.field = field + self.conversion_flag = conversion_flag + self.format_spec = format_spec + + @always_inline + fn is_escaped_brace(ref self) -> Bool: + """Whether the field is escaped_brace. + + Returns: + The result. + """ + return self.field.isa[Bool]() + + @always_inline + fn is_kwargs_field(ref self) -> Bool: + """Whether the field is kwargs_field. + + Returns: + The result. + """ + return self.field.isa[String]() + + @always_inline + fn is_automatic_indexing(ref self) -> Bool: + """Whether the field is automatic_indexing. + + Returns: + The result. + """ + return self.field.isa[NoneType]() + + @always_inline + fn is_manual_indexing(ref self) -> Bool: + """Whether the field is manual_indexing. + + Returns: + The result. + """ + return self.field.isa[Int]() + + @staticmethod + fn format[T: Stringlike](fmt_src: T, args: Self._args_t) raises -> String: + """Format the entries. + + Args: + fmt_src: The format source. + args: The arguments. + + Returns: + The result. + """ + alias len_pos_args = __type_of(args).__len__() + entries, size_estimation = Self._create_entries(fmt_src, len_pos_args) + var fmt_len = fmt_src.byte_length() + var buf = String._buffer_type(capacity=fmt_len + size_estimation) + buf.size = 1 + buf.unsafe_set(0, 0) + var res = String(buf^) + var offset = 0 + var ptr = fmt_src.unsafe_ptr() + alias S = StringSlice[StaticConstantOrigin] + + @always_inline("nodebug") + fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: + return S(ptr=p + start, length=end - start) + + var auto_arg_index = 0 + for e in entries: + debug_assert(offset < fmt_len, "offset >= fmt_src.byte_length()") + res += _build_slice(ptr, offset, e[].first_curly) + e[]._format_entry[len_pos_args](res, args, auto_arg_index) + offset = e[].last_curly + 1 + + res += _build_slice(ptr, offset, fmt_len) + return res^ + + @staticmethod + fn _create_entries[ + T: Stringlike + ](fmt_src: T, len_pos_args: Int) raises -> (List[Self], Int): + """Returns a list of entries and its total estimated entry byte width. + """ + var manual_indexing_count = 0 + var automatic_indexing_count = 0 + var raised_manual_index = Optional[Int](None) + var raised_automatic_index = Optional[Int](None) + var raised_kwarg_field = Optional[String](None) + alias `}` = UInt8(ord("}")) + alias `{` = UInt8(ord("{")) + alias l_err = "there is a single curly { left unclosed or unescaped" + alias r_err = "there is a single curly } left unclosed or unescaped" + + var entries = List[Self]() + var start = Optional[Int](None) + var skip_next = False + var fmt_ptr = fmt_src.unsafe_ptr() + var fmt_len = fmt_src.byte_length() + var total_estimated_entry_byte_width = 0 + + for i in range(fmt_len): + if skip_next: + skip_next = False + continue + if fmt_ptr[i] == `{`: + if not start: + start = i + continue + if i - start.value() != 1: + raise Error(l_err) + # python escapes double curlies + entries.append(Self(start.value(), i, field=False)) + start = None + continue + elif fmt_ptr[i] == `}`: + if not start and (i + 1) < fmt_len: + # python escapes double curlies + if fmt_ptr[i + 1] == `}`: + entries.append(Self(i, i + 1, field=True)) + total_estimated_entry_byte_width += 2 + skip_next = True + continue + elif not start: # if it is not an escaped one, it is an error + raise Error(r_err) + + var start_value = start.value() + var current_entry = Self(start_value, i, field=NoneType()) + + if i - start_value != 1: + if current_entry._handle_field_and_break( + fmt_src, + len_pos_args, + i, + start_value, + automatic_indexing_count, + raised_automatic_index, + manual_indexing_count, + raised_manual_index, + raised_kwarg_field, + total_estimated_entry_byte_width, + ): + break + else: # automatic indexing + if automatic_indexing_count >= len_pos_args: + raised_automatic_index = automatic_indexing_count + break + automatic_indexing_count += 1 + total_estimated_entry_byte_width += 8 # guessing + entries.append(current_entry^) + start = None + + if raised_automatic_index: + raise Error("Automatic indexing require more args in *args") + elif raised_kwarg_field: + var val = raised_kwarg_field.value() + raise Error("Index " + val + " not in kwargs") + elif manual_indexing_count and automatic_indexing_count: + raise Error("Cannot both use manual and automatic indexing") + elif raised_manual_index: + var val = str(raised_manual_index.value()) + raise Error("Index " + val + " not in *args") + elif start: + raise Error(l_err) + return entries^, total_estimated_entry_byte_width + + fn _handle_field_and_break[ + T: Stringlike + ]( + inout self, + fmt_src: T, + len_pos_args: Int, + i: Int, + start_value: Int, + inout automatic_indexing_count: Int, + inout raised_automatic_index: Optional[Int], + inout manual_indexing_count: Int, + inout raised_manual_index: Optional[Int], + inout raised_kwarg_field: Optional[String], + inout total_estimated_entry_byte_width: Int, + ) raises -> Bool: + alias S = StringSlice[StaticConstantOrigin] + + @always_inline("nodebug") + fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: + return S(ptr=p + start, length=end - start) + + var field = _build_slice(fmt_src.unsafe_ptr(), start_value + 1, i) + var field_ptr = field.unsafe_ptr() + var field_len = i - (start_value + 1) + var exclamation_index = -1 + var idx = 0 + while idx < field_len: + if field_ptr[idx] == ord("!"): + exclamation_index = idx + break + idx += 1 + var new_idx = exclamation_index + 1 + if exclamation_index != -1: + if new_idx == field_len: + raise Error("Empty conversion flag.") + var conversion_flag = field_ptr[new_idx] + if field_len - new_idx > 1 or ( + conversion_flag not in Self.supported_conversion_flags + ): + var f = String(_build_slice(field_ptr, new_idx, field_len)) + _ = field + raise Error('Conversion flag "' + f + '" not recognised.') + self.conversion_flag = conversion_flag + field = _build_slice(field_ptr, 0, exclamation_index) + else: + new_idx += 1 + + var extra = int(new_idx < field_len) + var fmt_field = _build_slice(field_ptr, new_idx + extra, field_len) + self.format_spec = _FormatSpec.parse(fmt_field) + var w = int(self.format_spec.value().width) if self.format_spec else 0 + # fully guessing the byte width here to be at least 8 bytes per entry + # minus the length of the whole format specification + total_estimated_entry_byte_width += 8 * int(w > 0) + w - (field_len + 2) + + if field.byte_length() == 0: + # an empty field, so it's automatic indexing + if automatic_indexing_count >= len_pos_args: + raised_automatic_index = automatic_indexing_count + return True + automatic_indexing_count += 1 + else: + try: + # field is a number for manual indexing: + # TODO: add support for "My name is {0.name}".format(Person(name="Fred")) + # TODO: add support for "My name is {0[name]}".format({"name": "Fred"}) + var number = int(field) + self.field = number + if number >= len_pos_args or number < 0: + raised_manual_index = number + return True + manual_indexing_count += 1 + except e: + alias unexp = "Not the expected error from atol" + debug_assert("not convertible to integer" in str(e), unexp) + # field is a keyword for **kwargs: + # TODO: add support for "My name is {person.name}".format(person=Person(name="Fred")) + # TODO: add support for "My name is {person[name]}".format(person={"name": "Fred"}) + var f = str(field) + self.field = f + raised_kwarg_field = f + return True + return False + + fn _format_entry[ + len_pos_args: Int + ](self, inout res: String, args: Self._args_t, inout auto_idx: Int) raises: + # TODO(#3403 and/or #3252): this function should be able to use + # Writer syntax when the type implements it, since it will give great + # performance benefits. This also needs to be able to check if the given + # args[i] conforms to the trait needed by the conversion_flag to avoid + # needing to constraint that every type needs to conform to every trait. + alias `r` = UInt8(ord("r")) + alias `s` = UInt8(ord("s")) + # alias `a` = UInt8(ord("a")) # TODO + + @parameter + fn _format(idx: Int) raises: + @parameter + for i in range(len_pos_args): + if i == idx: + var type_impls_repr = True # TODO + var type_impls_str = True # TODO + var type_impls_write_repr = True # TODO + var type_impls_write_str = True # TODO + var flag = self.conversion_flag + var empty = flag == 0 and not self.format_spec + + var data: String + if empty and type_impls_write_str: + data = str(args[i]) # TODO: use writer and return + elif empty and type_impls_str: + data = str(args[i]) + elif flag == `s` and type_impls_write_str: + if empty: + # TODO: use writer and return + pass + data = str(args[i]) + elif flag == `s` and type_impls_str: + data = str(args[i]) + elif flag == `r` and type_impls_write_repr: + if empty: + # TODO: use writer and return + pass + data = repr(args[i]) + elif flag == `r` and type_impls_repr: + data = repr(args[i]) + elif self.format_spec: + self.format_spec.value().format(res, args[i]) + return + else: + alias argnum = "Argument number: " + alias does_not = " does not implement the trait " + alias needed = "needed for conversion_flag: " + var flg = String(List[UInt8](flag, 0)) + raise Error(argnum + str(i) + does_not + needed + flg) + + if self.format_spec: + self.format_spec.value().format( + res, data.as_string_slice() + ) + else: + res += data + + if self.is_escaped_brace(): + res += "}" if self.field[Bool] else "{" + elif self.is_manual_indexing(): + _format(self.field[Int]) + elif self.is_automatic_indexing(): + _format(auto_idx) + auto_idx += 1 + + +# ===----------------------------------------------------------------------===# +# Format Specification +# ===----------------------------------------------------------------------===# + + +trait _CurlyEntryFormattable(Stringable, Representable): + """This trait is used by the `format()` method to support format specifiers. + Currently, it is a composition of both `Stringable` and `Representable` + traits i.e. a type to be formatted must implement both. In the future this + will be less constrained. + """ + + ... + + +# TODO: trait _FormattableStr: fn __format__(self, spec: FormatSpec) -> String: +# TODO: trait _FormattableWrite: fn __format__(self, spec: FormatSpec, *, writer: Writer): +# TODO: add usage of these traits before trying to coerce to repr/str/int/float + + +@value +@register_passable("trivial") +struct _FormatSpec: + """Store every field of the format specifier in a byte (e.g., ord("+") for + sign). It is stored in a byte because every [format specifier]( + https://docs.python.org/3/library/string.html#formatspec) is an ASCII + character. + """ + + var fill: UInt8 + """If a valid align value is specified, it can be preceded by a fill + character that can be any character and defaults to a space if omitted. + """ + var align: UInt8 + """The meaning of the various alignment options is as follows: + + | Option | Meaning| + |:------:|:-------| + |'<' | Forces the field to be left-aligned within the available space \ + (this is the default for most objects).| + |'>' | Forces the field to be right-aligned within the available space \ + (this is the default for numbers).| + |'=' | Forces the padding to be placed after the sign (if any) but before \ + the digits. This is used for printing fields in the form `+000000120`. This\ + alignment option is only valid for numeric types. It becomes the default\ + for numbers when `0` immediately precedes the field width.| + |'^' | Forces the field to be centered within the available space.| + """ + var sign: UInt8 + """The sign option is only valid for number types, and can be one of the + following: + + | Option | Meaning| + |:------:|:-------| + |'+' | indicates that a sign should be used for both positive as well as\ + negative numbers.| + |'-' | indicates that a sign should be used only for negative numbers (this\ + is the default behavior).| + |space | indicates that a leading space should be used on positive numbers,\ + and a minus sign on negative numbers.| + """ + var coerce_z: Bool + """The 'z' option coerces negative zero floating-point values to positive + zero after rounding to the format precision. This option is only valid for + floating-point presentation types. + """ + var alternate_form: Bool + """The alternate form is defined differently for different types. This + option is only valid for types that implement the trait `# TODO: define + trait`. For integers, when binary, octal, or hexadecimal output is used, + this option adds the respective prefix '0b', '0o', '0x', or '0X' to the + output value. For float and complex the alternate form causes the result of + the conversion to always contain a decimal-point character, even if no + digits follow it. + """ + var width: UInt8 + """A decimal integer defining the minimum total field width, including any + prefixes, separators, and other formatting characters. If not specified, + then the field width will be determined by the content. When no explicit + alignment is given, preceding the width field by a zero ('0') character + enables sign-aware zero-padding for numeric types. This is equivalent to a + fill character of '0' with an alignment type of '='. + """ + var grouping_option: UInt8 + """The ',' option signals the use of a comma for a thousands separator. For + a locale aware separator, use the 'n' integer presentation type instead. The + '_' option signals the use of an underscore for a thousands separator for + floating-point presentation types and for integer presentation type 'd'. For + integer presentation types 'b', 'o', 'x', and 'X', underscores will be + inserted every 4 digits. For other presentation types, specifying this + option is an error. + """ + var precision: UInt8 + """The precision is a decimal integer indicating how many digits should be + displayed after the decimal point for presentation types 'f' and 'F', or + before and after the decimal point for presentation types 'g' or 'G'. For + string presentation types the field indicates the maximum field size - in + other words, how many characters will be used from the field content. The + precision is not allowed for integer presentation types. + """ + var type: UInt8 + """Determines how the data should be presented. + + The available integer presentation types are: + + | Option | Meaning| + |:------:|:-------| + |'b' |Binary format. Outputs the number in base 2.| + |'c' |Character. Converts the integer to the corresponding unicode\ + character before printing.| + |'d' |Decimal Integer. Outputs the number in base 10.| + |'o' |Octal format. Outputs the number in base 8.| + |'x' |Hex format. Outputs the number in base 16, using lower-case letters\ + for the digits above 9.| + |'X' |Hex format. Outputs the number in base 16, using upper-case letters\ + for the digits above 9. In case '#' is specified, the prefix '0x' will be\ + upper-cased to '0X' as well.| + |'n' |Number. This is the same as 'd', except that it uses the current\ + locale setting to insert the appropriate number separator characters.| + |None | The same as 'd'.| + + In addition to the above presentation types, integers can be formatted with + the floating-point presentation types listed below (except 'n' and None). + When doing so, float() is used to convert the integer to a floating-point + number before formatting. + + The available presentation types for float and Decimal values are: + + | Option | Meaning| + |:------:|:-------| + |'e' |Scientific notation. For a given precision p, formats the number in\ + scientific notation with the letter `e` separating the coefficient from the\ + exponent. The coefficient has one digit before and p digits after the\ + decimal point, for a total of p + 1 significant digits. With no precision\ + given, uses a precision of 6 digits after the decimal point for float, and\ + shows all coefficient digits for Decimal. If no digits follow the decimal\ + point, the decimal point is also removed unless the # option is used.| + |'E' |Scientific notation. Same as 'e' except it uses an upper case `E` as\ + the separator character.| + |'f' |Fixed-point notation. For a given precision p, formats the number as\ + a decimal number with exactly p digits following the decimal point. With no\ + precision given, uses a precision of 6 digits after the decimal point for\ + float, and uses a precision large enough to show all coefficient digits for\ + Decimal. If no digits follow the decimal point, the decimal point is also\ + removed unless the '#' option is used.| + |'F' |Fixed-point notation. Same as 'f', but converts nan to NAN and inf to\ + INF.| + |'g' |General format. For a given precision p >= 1, this rounds the number\ + to p significant digits and then formats the result in either fixed-point\ + format or in scientific notation, depending on its magnitude. A precision\ + of 0 is treated as equivalent to a precision of 1.\ + The precise rules are as follows: suppose that the result formatted with\ + presentation type 'e' and precision p-1 would have exponent exp. Then, if\ + m <= exp < p, where m is -4 for floats and -6 for Decimals, the number is\ + formatted with presentation type 'f' and precision p-1-exp. Otherwise, the\ + number is formatted with presentation type 'e' and precision p-1. In both\ + cases insignificant trailing zeros are removed from the significand, and\ + the decimal point is also removed if there are no remaining digits\ + following it, unless the '#' option is used.\ + With no precision given, uses a precision of 6 significant digits for\ + float. For Decimal, the coefficient of the result is formed from the\ + coefficient digits of the value; scientific notation is used for values\ + smaller than 1e-6 in absolute value and values where the place value of the\ + least significant digit is larger than 1, and fixed-point notation is used\ + otherwise.\ + Positive and negative infinity, positive and negative zero, and nans, are\ + formatted as inf, -inf, 0, -0 and nan respectively, regardless of the\ + precision.| + |'G' |General format. Same as 'g' except switches to 'E' if the number gets\ + too large. The representations of infinity and NaN are uppercased, too.| + |'n' |Number. This is the same as 'g', except that it uses the current\ + locale setting to insert the appropriate number separator characters.| + |'%' |Percentage. Multiplies the number by 100 and displays in fixed ('f')\ + format, followed by a percent sign.| + |None |For float this is like the 'g' type, except that when fixed-point\ + notation is used to format the result, it always includes at least one\ + digit past the decimal point, and switches to the scientific notation when\ + exp >= p - 1. When the precision is not specified, the latter will be as\ + large as needed to represent the given value faithfully.\ + For Decimal, this is the same as either 'g' or 'G' depending on the value\ + of context.capitals for the current decimal context.\ + The overall effect is to match the output of str() as altered by the other\ + format modifiers.| + """ + + fn __init__( + inout self, + fill: UInt8 = ord(" "), + align: UInt8 = 0, + sign: UInt8 = ord("-"), + coerce_z: Bool = False, + alternate_form: Bool = False, + width: UInt8 = 0, + grouping_option: UInt8 = 0, + precision: UInt8 = 0, + type: UInt8 = 0, + ): + """Construct a FormatSpec instance. + + Args: + fill: Defaults to space. + align: Defaults to `0` which is adjusted to the default for the arg + type. + sign: Defaults to `-`. + coerce_z: Defaults to False. + alternate_form: Defaults to False. + width: Defaults to `0` which is adjusted to the default for the arg + type. + grouping_option: Defaults to `0` which is adjusted to the default for + the arg type. + precision: Defaults to `0` which is adjusted to the default for the + arg type. + type: Defaults to `0` which is adjusted to the default for the arg + type. + """ + self.fill = fill + self.align = align + self.sign = sign + self.coerce_z = coerce_z + self.alternate_form = alternate_form + self.width = width + self.grouping_option = grouping_option + self.precision = precision + self.type = type + + @staticmethod + fn parse(fmt_str: StringSlice) -> Optional[Self]: + """Parses the format spec string. + + Args: + fmt_str: The StringSlice with the format spec. + + Returns: + An instance of FormatSpec. + """ + + # FIXME: the need for the following dynamic characteristics will + # probably mean the parse method will have to be called at the + # formatting stage in cases where it's dynamic. + # TODO: add support for "{0:{1}}".format(123, "10") + # TODO: add support for more complex cases as well + # >>> width = 10 + # >>> precision = 4 + # >>> value = decimal.Decimal('12.34567') + # >>> 'result: {value:{width}.{precision}}'.format(...) + alias `:` = UInt8(ord(":")) + var f_len = fmt_str.byte_length() + var f_ptr = fmt_str.unsafe_ptr() + var colon_idx = -1 + var idx = 0 + while idx < f_len: + if f_ptr[idx] == `:`: + exclamation_index = idx + break + idx += 1 + + if colon_idx == -1: + return None + + # TODO: Future implementation of format specifiers + return None + + # TODO: this should be in StringSlice.__format__(self, spec: FormatSpec, *, writer: Writer): + fn format(self, inout res: String, item: StringSlice) raises: + """Transform a String according to its format specification. + + Args: + res: The resulting String. + item: The item to format. + """ + + # TODO: align, fill, etc. + res += item + + fn format[ + T: _CurlyEntryFormattable + ](self, inout res: String, item: T) raises: + """Stringify a type according to its format specification. + + Args: + res: The resulting String. + item: The item to stringify. + """ + var type_implements_format_write = True # TODO + var type_implements_format_write_raising = True # TODO + var type_implements_format = True # TODO + var type_implements_format_raising = True # TODO + var type_implements_float = True # TODO + var type_implements_float_raising = True # TODO + var type_implements_int = True # TODO + var type_implements_int_raising = True # TODO + + # TODO: send to the type's __format__ method if it has one + # TODO: transform to int/float depending on format spec + # TODO: send to float/int 's __format__ method + # their methods should stringify as hex/bin/oct etc. + res += str(item) + + +# ===----------------------------------------------------------------------===# +# Utils +# ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index b7fe463728..3b6c8bb4fd 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # - """Implements the StringSlice type. You can import these APIs from the `utils.string_slice` module. @@ -30,6 +29,7 @@ from memory import memcmp, UnsafePointer, memcpy from sys import simdwidthof, bitwidthof from sys.intrinsics import unlikely from memory.memory import _memcmp_impl_unconstrained +from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry from ._utf8_validation import _is_valid_utf8 alias StaticString = StringSlice[StaticConstantOrigin] @@ -1115,613 +1115,3 @@ fn _to_string_list[ return len(v) return _to_string_list[items.T, len_fn, unsafe_ptr_fn](items) - - -# ===----------------------------------------------------------------------===# -# Format method structures -# ===----------------------------------------------------------------------===# - - -trait _CurlyEntryFormattable(Stringable, Representable): - """This trait is used by the `format()` method to support format specifiers. - Currently, it is a composition of both `Stringable` and `Representable` - traits i.e. a type to be formatted must implement both. In the future this - will be less constrained. - """ - - ... - - -@value -struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): - """The struct that handles `Stringlike` formatting by curly braces entries. - This is internal for the types: `String`, `StringLiteral` and `StringSlice`. - """ - - var first_curly: Int - """The index of an opening brace around a substitution field.""" - var last_curly: Int - """The index of a closing brace around a substitution field.""" - # TODO: ord("a") conversion flag not supported yet - var conversion_flag: UInt8 - """The type of conversion for the entry: {ord("s"), ord("r")}.""" - var format_spec: Optional[_FormatSpec] - """The format specifier.""" - # TODO: ord("a") conversion flag not supported yet - alias supported_conversion_flags = SIMD[DType.uint8, 2](ord("s"), ord("r")) - """Currently supported conversion flags: `__str__` and `__repr__`.""" - alias _FieldVariantType = Variant[String, Int, NoneType, Bool] - """Purpose of the `Variant` `Self.field`: - - - `Int` for manual indexing: (value field contains `0`). - - `NoneType` for automatic indexing: (value field contains `None`). - - `String` for **kwargs indexing: (value field contains `foo`). - - `Bool` for escaped curlies: (value field contains False for `{` or True - for `}`). - """ - var field: Self._FieldVariantType - """Store the substitution field. See `Self._FieldVariantType` docstrings for - more details.""" - alias _args_t = VariadicPack[element_trait=_CurlyEntryFormattable, *_] - """Args types that are formattable by curly entry.""" - - fn __init__(out self, *, other: Self): - self.first_curly = other.first_curly - self.last_curly = other.last_curly - self.conversion_flag = other.conversion_flag - self.field = Self._FieldVariantType(other=other.field) - self.format_spec = other.format_spec - - fn __init__( - inout self, - first_curly: Int, - last_curly: Int, - field: Self._FieldVariantType, - conversion_flag: UInt8 = 0, - format_spec: Optional[_FormatSpec] = None, - ): - self.first_curly = first_curly - self.last_curly = last_curly - self.field = field - self.conversion_flag = conversion_flag - self.format_spec = format_spec - - @always_inline - fn is_escaped_brace(ref self) -> Bool: - return self.field.isa[Bool]() - - @always_inline - fn is_kwargs_field(ref self) -> Bool: - return self.field.isa[String]() - - @always_inline - fn is_automatic_indexing(ref self) -> Bool: - return self.field.isa[NoneType]() - - @always_inline - fn is_manual_indexing(ref self) -> Bool: - return self.field.isa[Int]() - - @staticmethod - fn format[T: Stringlike](fmt_src: T, args: Self._args_t) raises -> String: - alias len_pos_args = __type_of(args).__len__() - entries, size_estimation = Self._create_entries(fmt_src, len_pos_args) - var fmt_len = fmt_src.byte_length() - var buf = String._buffer_type(capacity=fmt_len + size_estimation) - buf.size = 1 - buf.unsafe_set(0, 0) - var res = String(buf^) - var offset = 0 - var ptr = fmt_src.unsafe_ptr() - alias S = StringSlice[StaticConstantOrigin] - - @always_inline("nodebug") - fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: - return S(ptr=p + start, length=end - start) - - var auto_arg_index = 0 - for e in entries: - debug_assert(offset < fmt_len, "offset >= fmt_src.byte_length()") - res += _build_slice(ptr, offset, e[].first_curly) - e[]._format_entry[len_pos_args](res, args, auto_arg_index) - offset = e[].last_curly + 1 - - res += _build_slice(ptr, offset, fmt_len) - return res^ - - @staticmethod - fn _create_entries[ - T: Stringlike - ](fmt_src: T, len_pos_args: Int) raises -> (List[Self], Int): - """Returns a list of entries and its total estimated entry byte width. - """ - var manual_indexing_count = 0 - var automatic_indexing_count = 0 - var raised_manual_index = Optional[Int](None) - var raised_automatic_index = Optional[Int](None) - var raised_kwarg_field = Optional[String](None) - alias `}` = UInt8(ord("}")) - alias `{` = UInt8(ord("{")) - alias l_err = "there is a single curly { left unclosed or unescaped" - alias r_err = "there is a single curly } left unclosed or unescaped" - - var entries = List[Self]() - var start = Optional[Int](None) - var skip_next = False - var fmt_ptr = fmt_src.unsafe_ptr() - var fmt_len = fmt_src.byte_length() - var total_estimated_entry_byte_width = 0 - - for i in range(fmt_len): - if skip_next: - skip_next = False - continue - if fmt_ptr[i] == `{`: - if not start: - start = i - continue - if i - start.value() != 1: - raise Error(l_err) - # python escapes double curlies - entries.append(Self(start.value(), i, field=False)) - start = None - continue - elif fmt_ptr[i] == `}`: - if not start and (i + 1) < fmt_len: - # python escapes double curlies - if fmt_ptr[i + 1] == `}`: - entries.append(Self(i, i + 1, field=True)) - total_estimated_entry_byte_width += 2 - skip_next = True - continue - elif not start: # if it is not an escaped one, it is an error - raise Error(r_err) - - var start_value = start.value() - var current_entry = Self(start_value, i, field=NoneType()) - - if i - start_value != 1: - if current_entry._handle_field_and_break( - fmt_src, - len_pos_args, - i, - start_value, - automatic_indexing_count, - raised_automatic_index, - manual_indexing_count, - raised_manual_index, - raised_kwarg_field, - total_estimated_entry_byte_width, - ): - break - else: # automatic indexing - if automatic_indexing_count >= len_pos_args: - raised_automatic_index = automatic_indexing_count - break - automatic_indexing_count += 1 - total_estimated_entry_byte_width += 8 # guessing - entries.append(current_entry^) - start = None - - if raised_automatic_index: - raise Error("Automatic indexing require more args in *args") - elif raised_kwarg_field: - var val = raised_kwarg_field.value() - raise Error("Index " + val + " not in kwargs") - elif manual_indexing_count and automatic_indexing_count: - raise Error("Cannot both use manual and automatic indexing") - elif raised_manual_index: - var val = str(raised_manual_index.value()) - raise Error("Index " + val + " not in *args") - elif start: - raise Error(l_err) - return entries^, total_estimated_entry_byte_width - - fn _handle_field_and_break[ - T: Stringlike - ]( - inout self, - fmt_src: T, - len_pos_args: Int, - i: Int, - start_value: Int, - inout automatic_indexing_count: Int, - inout raised_automatic_index: Optional[Int], - inout manual_indexing_count: Int, - inout raised_manual_index: Optional[Int], - inout raised_kwarg_field: Optional[String], - inout total_estimated_entry_byte_width: Int, - ) raises -> Bool: - alias S = StringSlice[StaticConstantOrigin] - - @always_inline("nodebug") - fn _build_slice(p: UnsafePointer[UInt8], start: Int, end: Int) -> S: - return S(ptr=p + start, length=end - start) - - var field = _build_slice(fmt_src.unsafe_ptr(), start_value + 1, i) - var field_ptr = field.unsafe_ptr() - var field_len = i - (start_value + 1) - var exclamation_index = -1 - var idx = 0 - while idx < field_len: - if field_ptr[idx] == ord("!"): - exclamation_index = idx - break - idx += 1 - var new_idx = exclamation_index + 1 - if exclamation_index != -1: - if new_idx == field_len: - raise Error("Empty conversion flag.") - var conversion_flag = field_ptr[new_idx] - if field_len - new_idx > 1 or ( - conversion_flag not in Self.supported_conversion_flags - ): - var f = String(_build_slice(field_ptr, new_idx, field_len)) - _ = field - raise Error('Conversion flag "' + f + '" not recognised.') - self.conversion_flag = conversion_flag - field = _build_slice(field_ptr, 0, exclamation_index) - else: - new_idx += 1 - - var extra = int(new_idx < field_len) - var fmt_field = _build_slice(field_ptr, new_idx + extra, field_len) - self.format_spec = _FormatSpec.parse(fmt_field) - var w = int(self.format_spec.value().width) if self.format_spec else 0 - # fully guessing the byte width here to be at least 8 bytes per entry - # minus the length of the whole format specification - total_estimated_entry_byte_width += 8 * int(w > 0) + w - (field_len + 2) - - if field.byte_length() == 0: - # an empty field, so it's automatic indexing - if automatic_indexing_count >= len_pos_args: - raised_automatic_index = automatic_indexing_count - return True - automatic_indexing_count += 1 - else: - try: - # field is a number for manual indexing: - var number = int(field) - self.field = number - if number >= len_pos_args or number < 0: - raised_manual_index = number - return True - manual_indexing_count += 1 - except e: - alias unexp = "Not the expected error from atol" - debug_assert("not convertible to integer" in str(e), unexp) - # field is a keyword for **kwargs: - var f = str(field) - self.field = f - raised_kwarg_field = f - return True - return False - - fn _format_entry[ - len_pos_args: Int - ](self, inout res: String, args: Self._args_t, inout auto_idx: Int) raises: - # TODO(#3403 and/or #3252): this function should be able to use - # Formatter syntax when the type implements it, since it will give great - # performance benefits. This also needs to be able to check if the given - # args[i] conforms to the trait needed by the conversion_flag to avoid - # needing to constraint that every type needs to conform to every trait. - alias `r` = UInt8(ord("r")) - alias `s` = UInt8(ord("s")) - # alias `a` = UInt8(ord("a")) # TODO - - @parameter - fn _format(idx: Int) raises: - @parameter - for i in range(len_pos_args): - if i == idx: - var type_impls_repr = True # TODO - var type_impls_str = True # TODO - var type_impls_formatter_repr = True # TODO - var type_impls_formatter_str = True # TODO - var flag = self.conversion_flag - var empty = flag == 0 and not self.format_spec - - var data: String - if empty and type_impls_formatter_str: - data = str(args[i]) # TODO: use writer and return - elif empty and type_impls_str: - data = str(args[i]) - elif flag == `s` and type_impls_formatter_str: - if empty: - # TODO: use writer and return - pass - data = str(args[i]) - elif flag == `s` and type_impls_str: - data = str(args[i]) - elif flag == `r` and type_impls_formatter_repr: - if empty: - # TODO: use writer and return - pass - data = repr(args[i]) - elif flag == `r` and type_impls_repr: - data = repr(args[i]) - elif self.format_spec: - self.format_spec.value().stringify(res, args[i]) - return - else: - alias argnum = "Argument number: " - alias does_not = " does not implement the trait " - alias needed = "needed for conversion_flag: " - var flg = String(List[UInt8](flag, 0)) - raise Error(argnum + str(i) + does_not + needed + flg) - - if self.format_spec: - self.format_spec.value().format_string(res, data) - else: - res += data - - if self.is_escaped_brace(): - res += "}" if self.field[Bool] else "{" - elif self.is_manual_indexing(): - _format(self.field[Int]) - elif self.is_automatic_indexing(): - _format(auto_idx) - auto_idx += 1 - - -@value -@register_passable("trivial") -struct _FormatSpec: - """Store every field of the format specifier in a byte (e.g., ord("+") for - sign). It is stored in a byte because every [format specifier]( - https://docs.python.org/3/library/string.html#formatspec) is an ASCII - character. - """ - - var fill: UInt8 - """If a valid align value is specified, it can be preceded by a fill - character that can be any character and defaults to a space if omitted. - """ - var align: UInt8 - """The meaning of the various alignment options is as follows: - - | Option | Meaning| - |:------:|:-------| - |'<' | Forces the field to be left-aligned within the available space \ - (this is the default for most objects).| - |'>' | Forces the field to be right-aligned within the available space \ - (this is the default for numbers).| - |'=' | Forces the padding to be placed after the sign (if any) but before \ - the digits. This is used for printing fields in the form `+000000120`. This\ - alignment option is only valid for numeric types. It becomes the default\ - for numbers when `0` immediately precedes the field width.| - |'^' | Forces the field to be centered within the available space.| - """ - var sign: UInt8 - """The sign option is only valid for number types, and can be one of the - following: - - | Option | Meaning| - |:------:|:-------| - |'+' | indicates that a sign should be used for both positive as well as\ - negative numbers.| - |'-' | indicates that a sign should be used only for negative numbers (this\ - is the default behavior).| - |space | indicates that a leading space should be used on positive numbers,\ - and a minus sign on negative numbers.| - """ - var coerce_z: Bool - """The 'z' option coerces negative zero floating-point values to positive - zero after rounding to the format precision. This option is only valid for - floating-point presentation types. - """ - var alternate_form: Bool - """The alternate form is defined differently for different types. This - option is only valid for types that implement the trait `# TODO: define - trait`. For integers, when binary, octal, or hexadecimal output is used, - this option adds the respective prefix '0b', '0o', '0x', or '0X' to the - output value. For float and complex the alternate form causes the result of - the conversion to always contain a decimal-point character, even if no - digits follow it. - """ - var width: UInt8 - """A decimal integer defining the minimum total field width, including any - prefixes, separators, and other formatting characters. If not specified, - then the field width will be determined by the content. When no explicit - alignment is given, preceding the width field by a zero ('0') character - enables sign-aware zero-padding for numeric types. This is equivalent to a - fill character of '0' with an alignment type of '='. - """ - var grouping_option: UInt8 - """The ',' option signals the use of a comma for a thousands separator. For - a locale aware separator, use the 'n' integer presentation type instead. The - '_' option signals the use of an underscore for a thousands separator for - floating-point presentation types and for integer presentation type 'd'. For - integer presentation types 'b', 'o', 'x', and 'X', underscores will be - inserted every 4 digits. For other presentation types, specifying this - option is an error. - """ - var precision: UInt8 - """The precision is a decimal integer indicating how many digits should be - displayed after the decimal point for presentation types 'f' and 'F', or - before and after the decimal point for presentation types 'g' or 'G'. For - string presentation types the field indicates the maximum field size - in - other words, how many characters will be used from the field content. The - precision is not allowed for integer presentation types. - """ - var type: UInt8 - """Determines how the data should be presented. - - The available integer presentation types are: - - | Option | Meaning| - |:------:|:-------| - |'b' |Binary format. Outputs the number in base 2.| - |'c' |Character. Converts the integer to the corresponding unicode\ - character before printing.| - |'d' |Decimal Integer. Outputs the number in base 10.| - |'o' |Octal format. Outputs the number in base 8.| - |'x' |Hex format. Outputs the number in base 16, using lower-case letters\ - for the digits above 9.| - |'X' |Hex format. Outputs the number in base 16, using upper-case letters\ - for the digits above 9. In case '#' is specified, the prefix '0x' will be\ - upper-cased to '0X' as well.| - |'n' |Number. This is the same as 'd', except that it uses the current\ - locale setting to insert the appropriate number separator characters.| - |None | The same as 'd'.| - - In addition to the above presentation types, integers can be formatted with - the floating-point presentation types listed below (except 'n' and None). - When doing so, float() is used to convert the integer to a floating-point - number before formatting. - - The available presentation types for float and Decimal values are: - - | Option | Meaning| - |:------:|:-------| - |'e' |Scientific notation. For a given precision p, formats the number in\ - scientific notation with the letter `e` separating the coefficient from the\ - exponent. The coefficient has one digit before and p digits after the\ - decimal point, for a total of p + 1 significant digits. With no precision\ - given, uses a precision of 6 digits after the decimal point for float, and\ - shows all coefficient digits for Decimal. If no digits follow the decimal\ - point, the decimal point is also removed unless the # option is used.| - |'E' |Scientific notation. Same as 'e' except it uses an upper case `E` as\ - the separator character.| - |'f' |Fixed-point notation. For a given precision p, formats the number as\ - a decimal number with exactly p digits following the decimal point. With no\ - precision given, uses a precision of 6 digits after the decimal point for\ - float, and uses a precision large enough to show all coefficient digits for\ - Decimal. If no digits follow the decimal point, the decimal point is also\ - removed unless the '#' option is used.| - |'F' |Fixed-point notation. Same as 'f', but converts nan to NAN and inf to\ - INF.| - |'g' |General format. For a given precision p >= 1, this rounds the number\ - to p significant digits and then formats the result in either fixed-point\ - format or in scientific notation, depending on its magnitude. A precision\ - of 0 is treated as equivalent to a precision of 1.\ - The precise rules are as follows: suppose that the result formatted with\ - presentation type 'e' and precision p-1 would have exponent exp. Then, if\ - m <= exp < p, where m is -4 for floats and -6 for Decimals, the number is\ - formatted with presentation type 'f' and precision p-1-exp. Otherwise, the\ - number is formatted with presentation type 'e' and precision p-1. In both\ - cases insignificant trailing zeros are removed from the significand, and\ - the decimal point is also removed if there are no remaining digits\ - following it, unless the '#' option is used.\ - With no precision given, uses a precision of 6 significant digits for\ - float. For Decimal, the coefficient of the result is formed from the\ - coefficient digits of the value; scientific notation is used for values\ - smaller than 1e-6 in absolute value and values where the place value of the\ - least significant digit is larger than 1, and fixed-point notation is used\ - otherwise.\ - Positive and negative infinity, positive and negative zero, and nans, are\ - formatted as inf, -inf, 0, -0 and nan respectively, regardless of the\ - precision.| - |'G' |General format. Same as 'g' except switches to 'E' if the number gets\ - too large. The representations of infinity and NaN are uppercased, too.| - |'n' |Number. This is the same as 'g', except that it uses the current\ - locale setting to insert the appropriate number separator characters.| - |'%' |Percentage. Multiplies the number by 100 and displays in fixed ('f')\ - format, followed by a percent sign.| - |None |For float this is like the 'g' type, except that when fixed-point\ - notation is used to format the result, it always includes at least one\ - digit past the decimal point, and switches to the scientific notation when\ - exp >= p - 1. When the precision is not specified, the latter will be as\ - large as needed to represent the given value faithfully.\ - For Decimal, this is the same as either 'g' or 'G' depending on the value\ - of context.capitals for the current decimal context.\ - The overall effect is to match the output of str() as altered by the other\ - format modifiers.| - """ - - fn __init__( - inout self, - fill: UInt8 = ord(" "), - align: UInt8 = 0, - sign: UInt8 = ord("-"), - coerce_z: Bool = False, - alternate_form: Bool = False, - width: UInt8 = 0, - grouping_option: UInt8 = 0, - precision: UInt8 = 0, - type: UInt8 = 0, - ): - """Construct a FormatSpec instance. - - Args: - fill: Defaults to space. - align: Defaults to `0` which is adjusted to the default for the arg - type. - sign: Defaults to `-`. - coerce_z: Defaults to False. - alternate_form: Defaults to False. - width: Defaults to `0` which is adjusted to the default for the arg - type. - grouping_option: Defaults to `0` which is adjusted to the default for - the arg type. - precision: Defaults to `0` which is adjusted to the default for the - arg type. - type: Defaults to `0` which is adjusted to the default for the arg - type. - """ - self.fill = fill - self.align = align - self.sign = sign - self.coerce_z = coerce_z - self.alternate_form = alternate_form - self.width = width - self.grouping_option = grouping_option - self.precision = precision - self.type = type - - @staticmethod - fn parse(fmt_str: StringSlice) -> Optional[Self]: - """Parses the format spec string. - - Args: - fmt_str: The StringSlice with the format spec. - - Returns: - An instance of FormatSpec. - """ - - alias `:` = UInt8(ord(":")) - var f_len = fmt_str.byte_length() - var f_ptr = fmt_str.unsafe_ptr() - var colon_idx = -1 - var idx = 0 - while idx < f_len: - if f_ptr[idx] == `:`: - exclamation_index = idx - break - idx += 1 - - if colon_idx == -1: - return None - - # TODO: Future implementation of format specifiers - return None - - fn stringify[ - T: _CurlyEntryFormattable - ](self, inout res: String, item: T) raises: - """Stringify a type according to its format specification. - - Args: - res: The resulting String. - item: The item to stringify. - """ - var type_implements_float = True # TODO - var type_implements_float_raising = True # TODO - var type_implements_int = True # TODO - var type_implements_int_raising = True # TODO - - # TODO: transform to int/float depending on format spec and stringify - # with hex/bin/oct etc. - res += str(item) - - fn format_string(self, inout res: String, item: String) raises: - """Transform a String according to its format specification. - - Args: - res: The resulting String. - item: The item to format. - """ - - # TODO: align, fill, etc. - res += item From 7396d5bb3ca155f5a143c98b9fa5d3b207eb635b Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Thu, 21 Nov 2024 13:31:25 -0600 Subject: [PATCH 1903/2019] [External] [stdlib] Add `os.path.splitroot` (#51325) [External] [stdlib] Add `os.path.splitroot` Also cleaned up a previous changelog entry so it aligns with the function's intent. Co-authored-by: Mikhail Tavarez Closes modularml/mojo#3780 MODULAR_ORIG_COMMIT_REV_ID: 081959627df8f25d7f6e04856ca03ab992ecca6c --- docs/changelog.md | 6 +- stdlib/src/os/path/__init__.mojo | 1 + stdlib/src/os/path/path.mojo | 35 ++++++++++ stdlib/test/os/path/test_splitroot.mojo | 87 +++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 stdlib/test/os/path/test_splitroot.mojo diff --git a/docs/changelog.md b/docs/changelog.md index a9f924952b..2c2529ff0f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -240,8 +240,10 @@ what we publish. of variables that are handled as synthetic types, e.g. `List` from Mojo or `std::vector` from C++. -- Added `os.path.expandvars` to expand environment variables in a string. - ([PR #3735](https://github.com/modularml/mojo/pull/3735) by [@thatstoasty](https://github.com/thatstoasty)). +- Expanded `os.path` with new functions (by [@thatstoasty](https://github.com/thatstoasty)): + - `os.path.expandvars`: Expands environment variables in a path ([PR #3735](https://github.com/modularml/mojo/pull/3735)). + - `os.path.splitroot`: Split a path into drive, root and tail. + ([PR #3780](https://github.com/modularml/mojo/pull/3780)). - Added a `reserve` method and new constructor to the `String` struct to allocate additional capacity. diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index 68097ee4db..7ced0ce7eb 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -22,5 +22,6 @@ from .path import ( islink, join, split, + splitroot, lexists, ) diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 4bd3930a26..e103da839b 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -392,6 +392,41 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): # return join(path.__fspath__(), *paths_str) +# ===----------------------------------------------------------------------=== # +# splitroot +# ===----------------------------------------------------------------------=== # + + +fn splitroot[ + PathLike: os.PathLike, // +](path: PathLike) -> Tuple[String, String, String]: + """Splits `path` into drive, root and tail. The tail contains anything after the root. + + Parameters: + PathLike: The type conforming to the os.PathLike trait. + + Args: + path: The path to be split. + + Returns: + A tuple containing three strings: (drive, root, tail). + """ + var p = path.__fspath__() + alias empty = String("") + + # Relative path, e.g.: 'foo' + if p[:1] != sep: + return empty, empty, p + + # Absolute path, e.g.: '/foo', '///foo', '////foo', etc. + elif p[1:2] != sep or p[2:3] == sep: + return empty, String(sep), p[1:] + + # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see + # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + else: + return empty, p[:2], p[2:] + # ===----------------------------------------------------------------------=== # # expandvars diff --git a/stdlib/test/os/path/test_splitroot.mojo b/stdlib/test/os/path/test_splitroot.mojo new file mode 100644 index 0000000000..fc52a959dd --- /dev/null +++ b/stdlib/test/os/path/test_splitroot.mojo @@ -0,0 +1,87 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +import os +from os.path import splitroot +from testing import assert_equal + + +def test_absolute_path(): + drive, root, tail = splitroot("/usr/lib/file.txt") + assert_equal(drive, "") + assert_equal(root, "/") + assert_equal(tail, "usr/lib/file.txt") + + drive, root, tail = splitroot("//usr/lib/file.txt") + assert_equal(drive, "") + assert_equal(root, "//") + assert_equal(tail, "usr/lib/file.txt") + + drive, root, tail = splitroot("///usr/lib/file.txt") + assert_equal(drive, "") + assert_equal(root, "/") + assert_equal(tail, "//usr/lib/file.txt") + + +def test_relative_path(): + drive, root, tail = splitroot("usr/lib/file.txt") + assert_equal(drive, "") + assert_equal(root, "") + assert_equal(tail, "usr/lib/file.txt") + + drive, root, tail = splitroot(".") + assert_equal(drive, "") + assert_equal(root, "") + assert_equal(tail, ".") + + drive, root, tail = splitroot("..") + assert_equal(drive, "") + assert_equal(root, "") + assert_equal(tail, "..") + + drive, root, tail = splitroot("entire/.//.tail/..//captured////") + assert_equal(drive, "") + assert_equal(root, "") + assert_equal(tail, "entire/.//.tail/..//captured////") + + +def test_root_directory(): + drive, root, tail = splitroot("/") + assert_equal(drive, "") + assert_equal(root, "/") + assert_equal(tail, "") + + drive, root, tail = splitroot("//") + assert_equal(drive, "") + assert_equal(root, "//") + assert_equal(tail, "") + + drive, root, tail = splitroot("///") + assert_equal(drive, "") + assert_equal(root, "/") + assert_equal(tail, "//") + + +def test_empty_path(): + drive, root, tail = splitroot("") + assert_equal(drive, "") + assert_equal(root, "") + assert_equal(tail, "") + + +def main(): + test_absolute_path() + test_relative_path() + test_root_directory() + test_empty_path() From 728be898827cab996035dd6d2aff986a9c8c4155 Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:51:23 -0600 Subject: [PATCH 1904/2019] [External] [stdlib] Implement `os.path.basename` (#51331) [External] [stdlib] Implement `os.path.basename` Implement `os.path.basename`. Fixes [#2941](https://github.com/modularml/mojo/issues/2941). Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2974 MODULAR_ORIG_COMMIT_REV_ID: e5ba09e0fc57c7bbcaca873c3dadcd1ec80db729 --- stdlib/src/os/path/__init__.mojo | 1 + stdlib/src/os/path/path.mojo | 25 ++++++++ stdlib/test/os/path/test_basename.mojo | 84 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 stdlib/test/os/path/test_basename.mojo diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index 7ced0ce7eb..edf3b394af 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -13,6 +13,7 @@ from .path import ( dirname, + basename, exists, expanduser, expandvars, diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index e103da839b..839e66f890 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -368,6 +368,31 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): return head, tail +fn basename[PathLike: os.PathLike, //](path: PathLike) -> String: + """Returns the tail section of a path. + + ```mojo + basename("a/path/foo.txt") # returns "foo.txt" + ``` + + Parameters: + PathLike: The type conforming to the os.PathLike trait. + + Args: + path: The path to retrieve the basename from. + + Returns: + The basename from the path. + """ + var fspath = path.__fspath__() + alias sep = str(os.sep) + var i = fspath.rfind(sep) + 1 + var head = fspath[i:] + if head and head != sep * len(head): + return head.rstrip(sep) + return head + + # TODO uncomment this when unpacking is supported # fn join[PathLike: os.PathLike](path: PathLike, *paths: PathLike) -> String: # """Join two or more pathname components, inserting '/' as needed. diff --git a/stdlib/test/os/path/test_basename.mojo b/stdlib/test/os/path/test_basename.mojo new file mode 100644 index 0000000000..24713367f8 --- /dev/null +++ b/stdlib/test/os/path/test_basename.mojo @@ -0,0 +1,84 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from os.path import basename +from pathlib import Path +from builtin._location import __source_location +from testing import assert_equal + + +def main(): + # Root directories + assert_equal("", basename("/")) + + # Empty strings + assert_equal("", basename("")) + + # Current directory (matching behavior of python, doesn't resolve `..` etc.) + assert_equal(".", basename(".")) + + # Parent directory + assert_equal("..", basename("..")) + + # Absolute paths + assert_equal("file", basename("/file")) + assert_equal("file.txt", basename("/file.txt")) + assert_equal("file", basename("/dir/file")) + assert_equal("file", basename("/dir/subdir/file")) + + # Relative paths + assert_equal("file", basename("dir/file")) + assert_equal("file", basename("dir/subdir/file")) + assert_equal("file", basename("file")) + + # Trailing slashes + assert_equal("", basename("/path/to/")) + assert_equal("", basename("/path/to/dir/")) + + # Multiple slashes + assert_equal("file", basename("/path/to//file")) + assert_equal("to", basename("/path//to")) + + # Paths with spaces + assert_equal("file", basename("/path to/file")) + assert_equal("file", basename("/path to/dir/file")) + + # Paths with special characters + assert_equal("file", basename("/path-to/file")) + assert_equal("file", basename("/path_to/dir/file")) + + # Paths with dots + assert_equal("file", basename("/path/./to/file")) + assert_equal("file", basename("/path/../to/file")) + + # Paths with double dots + assert_equal("file", basename("/path/../file")) + assert_equal("file", basename("/path/to/../file")) + + # Root and relative mixed + assert_equal("file", basename("/dir/./file")) + assert_equal("file", basename("/dir/subdir/../file")) + + # Edge cases + assert_equal("file", basename("/./file")) + assert_equal("file", basename("/../file")) + + # Unix hidden files + assert_equal(".hiddenfile", basename("/path/to/.hiddenfile")) + assert_equal(".hiddenfile", basename("/path/to/dir/.hiddenfile")) + + assert_equal("test_basename.mojo", basename(__source_location().file_name)) + assert_equal( + "some_file.txt", basename(Path.home() / "dir" / "some_file.txt") + ) From e95621f0065c56043b8b7e7f737ffc9f5b90951d Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Thu, 21 Nov 2024 14:39:56 -0800 Subject: [PATCH 1905/2019] [External] [stdlib] Refactor `atol`: `StringRef` to `StringSlice` and reusability (#51354) [External] [stdlib] Refactor `atol`: `StringRef` to `StringSlice` and reusability This pull request modularises previously inline code, assisting in addressing issue https://github.com/modularml/mojo/issues/2639 to allow `stol` function to reuse some of the functionality. These changes, initially part of https://github.com/modularml/mojo/pull/3178, have been separated for easier review. The refactoring extracts `_handle_base_prefix` & `_trim_and_handle_sign`. Additionally, the docstring has been revised for clarity. Alongside these changes, the respective function signatures have been updated to move away from `StringRef` to `StringSlice`. There was also a small `FIXME` to convert from `var` to `alias`, which is now seems possible. Co-authored-by: Joshua James Venter Closes modularml/mojo#3207 MODULAR_ORIG_COMMIT_REV_ID: e733ed473f086abe6fc5acccae711e0114c1093d --- stdlib/src/builtin/int.mojo | 34 ++++-- stdlib/src/collections/string.mojo | 177 ++++++++++++++++++----------- 2 files changed, 137 insertions(+), 74 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index a2a8d7a150..2e50208ea7 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -241,21 +241,39 @@ fn int[T: IntableRaising](value: T) raises -> Int: fn int(value: String, base: Int = 10) raises -> Int: - """Parses the given string as an integer in the given base and returns that value. + """Parses and returns the given string as an integer in the given base. - For example, `atol("19")` returns `19`. If the given string cannot be parsed - as an integer value, an error is raised. For example, `atol("hi")` raises an - error. - - If base is 0 the the string is parsed as an Integer literal, - see: https://docs.python.org/3/reference/lexical_analysis.html#integers + If base is set to 0, the string is parsed as an Integer literal, with the + following considerations: + - '0b' or '0B' prefix indicates binary (base 2) + - '0o' or '0O' prefix indicates octal (base 8) + - '0x' or '0X' prefix indicates hexadecimal (base 16) + - Without a prefix, it's treated as decimal (base 10) Args: value: A string to be parsed as an integer in the given base. base: Base used for conversion, value must be between 2 and 36, or 0. Returns: - An integer value that represents the string, or otherwise raises. + An integer value that represents the string. + + Raises: + If the given string cannot be parsed as an integer value or if an + incorrect base is provided. + + Examples: + >>> int("32") + 32 + >>> int("FF", 16) + 255 + >>> int("0xFF", 0) + 255 + >>> int("0b1010", 0) + 10 + + Notes: + This follows [Python's integer literals]( + https://docs.python.org/3/reference/lexical_analysis.html#integers). """ return atol(value, base) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 9f47e618e7..a7c5444688 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -234,15 +234,15 @@ fn ascii(value: String) -> String: # ===----------------------------------------------------------------------=== # -fn _atol(str_ref: StringSlice[_], base: Int = 10) raises -> Int: - """Implementation of `atol` for StringRef inputs. +fn _atol(str_slice: StringSlice, base: Int = 10) raises -> Int: + """Implementation of `atol` for StringSlice inputs. Please see its docstring for details. """ if (base != 0) and (base < 2 or base > 36): raise Error("Base must be >= 2 and <= 36, or 0.") - if not str_ref: - raise Error(_atol_error(base, str_ref)) + if not str_slice: + raise Error(_str_to_base_error(base, str_slice)) var real_base: Int var ord_num_max: Int @@ -252,53 +252,23 @@ fn _atol(str_ref: StringSlice[_], base: Int = 10) raises -> Int: var is_negative: Bool = False var has_prefix: Bool = False var start: Int = 0 - var str_len = len(str_ref) - var buff = str_ref.unsafe_ptr() - - for pos in range(start, str_len): - if _isspace(buff[pos]): - continue + var str_len = str_slice.byte_length() - if str_ref[pos] == "-": - is_negative = True - start = pos + 1 - elif str_ref[pos] == "+": - start = pos + 1 - else: - start = pos - break - - if str_ref[start] == "0" and start + 1 < str_len: - if base == 2 and ( - str_ref[start + 1] == "b" or str_ref[start + 1] == "B" - ): - start += 2 - has_prefix = True - elif base == 8 and ( - str_ref[start + 1] == "o" or str_ref[start + 1] == "O" - ): - start += 2 - has_prefix = True - elif base == 16 and ( - str_ref[start + 1] == "x" or str_ref[start + 1] == "X" - ): - start += 2 - has_prefix = True + start, is_negative = _trim_and_handle_sign(str_slice, str_len) alias ord_0 = ord("0") - # FIXME: - # Change this to `alias` after fixing support for __getitem__ of alias. - var ord_letter_min = (ord("a"), ord("A")) + alias ord_letter_min = (ord("a"), ord("A")) alias ord_underscore = ord("_") if base == 0: - var real_base_new_start = _identify_base(str_ref, start) + var real_base_new_start = _identify_base(str_slice, start) real_base = real_base_new_start[0] start = real_base_new_start[1] has_prefix = real_base != 10 if real_base == -1: - raise Error(_atol_error(base, str_ref)) + raise Error(_str_to_base_error(base, str_slice)) else: + start, has_prefix = _handle_base_prefix(start, str_slice, str_len, base) real_base = base if real_base <= 10: @@ -310,21 +280,23 @@ fn _atol(str_ref: StringSlice[_], base: Int = 10) raises -> Int: ord("A") + (real_base - 11), ) + var buff = str_slice.unsafe_ptr() var found_valid_chars_after_start = False var has_space_after_number = False + # Prefixed integer literals with real_base 2, 8, 16 may begin with leading # underscores under the conditions they have a prefix - var was_last_digit_undescore = not (real_base in (2, 8, 16) and has_prefix) + var was_last_digit_underscore = not (real_base in (2, 8, 16) and has_prefix) for pos in range(start, str_len): var ord_current = int(buff[pos]) if ord_current == ord_underscore: - if was_last_digit_undescore: - raise Error(_atol_error(base, str_ref)) + if was_last_digit_underscore: + raise Error(_str_to_base_error(base, str_slice)) else: - was_last_digit_undescore = True + was_last_digit_underscore = True continue else: - was_last_digit_undescore = False + was_last_digit_underscore = False if ord_0 <= ord_current <= ord_num_max: result += ord_current - ord_0 found_valid_chars_after_start = True @@ -339,45 +311,100 @@ fn _atol(str_ref: StringSlice[_], base: Int = 10) raises -> Int: start = pos + 1 break else: - raise Error(_atol_error(base, str_ref)) + raise Error(_str_to_base_error(base, str_slice)) if pos + 1 < str_len and not _isspace(buff[pos + 1]): var nextresult = result * real_base if nextresult < result: raise Error( - _atol_error(base, str_ref) + _str_to_base_error(base, str_slice) + " String expresses an integer too large to store in Int." ) result = nextresult - if was_last_digit_undescore or (not found_valid_chars_after_start): - raise Error(_atol_error(base, str_ref)) + if was_last_digit_underscore or (not found_valid_chars_after_start): + raise Error(_str_to_base_error(base, str_slice)) if has_space_after_number: for pos in range(start, str_len): if not _isspace(buff[pos]): - raise Error(_atol_error(base, str_ref)) + raise Error(_str_to_base_error(base, str_slice)) if is_negative: result = -result return result -fn _atol_error(base: Int, str_ref: StringSlice[_]) -> String: +@always_inline +fn _trim_and_handle_sign(str_slice: StringSlice, str_len: Int) -> (Int, Bool): + """Trims leading whitespace, handles the sign of the number in the string. + + Args: + str_slice: A StringSlice containing the number to parse. + str_len: The length of the string. + + Returns: + A tuple containing: + - The starting index of the number after whitespace and sign. + - A boolean indicating whether the number is negative. + """ + var buff = str_slice.unsafe_ptr() + var start: Int = 0 + while start < str_len and _isspace(buff[start]): + start += 1 + var p: Bool = buff[start] == ord("+") + var n: Bool = buff[start] == ord("-") + return start + (p or n), n + + +@always_inline +fn _handle_base_prefix( + pos: Int, str_slice: StringSlice, str_len: Int, base: Int +) -> (Int, Bool): + """Adjusts the starting position if a valid base prefix is present. + + Handles "0b"/"0B" for base 2, "0o"/"0O" for base 8, and "0x"/"0X" for base + 16. Only adjusts if the base matches the prefix. + + Args: + pos: Current position in the string. + str_slice: The input StringSlice. + str_len: Length of the input string. + base: The specified base. + + Returns: + A tuple containing: + - Updated position after the prefix, if applicable. + - A boolean indicating if the prefix was valid for the given base. + """ + var start = pos + var buff = str_slice.unsafe_ptr() + if start + 1 < str_len: + var prefix_char = chr(int(buff[start + 1])) + if buff[start] == ord("0") and ( + (base == 2 and (prefix_char == "b" or prefix_char == "B")) + or (base == 8 and (prefix_char == "o" or prefix_char == "O")) + or (base == 16 and (prefix_char == "x" or prefix_char == "X")) + ): + start += 2 + return start, start != pos + + +fn _str_to_base_error(base: Int, str_slice: StringSlice) -> String: return ( "String is not convertible to integer with base " + str(base) + ": '" - + str(str_ref) + + str(str_slice) + "'" ) -fn _identify_base(str_ref: StringSlice[_], start: Int) -> Tuple[Int, Int]: - var length = len(str_ref) +fn _identify_base(str_slice: StringSlice[_], start: Int) -> Tuple[Int, Int]: + var length = str_slice.byte_length() # just 1 digit, assume base 10 if start == (length - 1): return 10, start - if str_ref[start] == "0": - var second_digit = str_ref[start + 1] + if str_slice[start] == "0": + var second_digit = str_slice[start + 1] if second_digit == "b" or second_digit == "B": return 2, start + 2 if second_digit == "o" or second_digit == "O": @@ -387,7 +414,7 @@ fn _identify_base(str_ref: StringSlice[_], start: Int) -> Tuple[Int, Int]: # checking for special case of all "0", "_" are also allowed var was_last_character_underscore = False for i in range(start + 1, length): - if str_ref[i] == "_": + if str_slice[i] == "_": if was_last_character_underscore: return -1, -1 else: @@ -395,9 +422,9 @@ fn _identify_base(str_ref: StringSlice[_], start: Int) -> Tuple[Int, Int]: continue else: was_last_character_underscore = False - if str_ref[i] != "0": + if str_slice[i] != "0": return -1, -1 - elif ord("1") <= ord(str_ref[start]) <= ord("9"): + elif ord("1") <= ord(str_slice[start]) <= ord("9"): return 10, start else: return -1, -1 @@ -408,19 +435,37 @@ fn _identify_base(str_ref: StringSlice[_], start: Int) -> Tuple[Int, Int]: fn atol(str: String, base: Int = 10) raises -> Int: """Parses and returns the given string as an integer in the given base. - For example, `atol("19")` returns `19`. If base is 0 the the string is - parsed as an Integer literal, see: https://docs.python.org/3/reference/lexical_analysis.html#integers. - - Raises: - If the given string cannot be parsed as an integer value. For example in - `atol("hi")`. + If base is set to 0, the string is parsed as an Integer literal, with the + following considerations: + - '0b' or '0B' prefix indicates binary (base 2) + - '0o' or '0O' prefix indicates octal (base 8) + - '0x' or '0X' prefix indicates hexadecimal (base 16) + - Without a prefix, it's treated as decimal (base 10) Args: str: A string to be parsed as an integer in the given base. base: Base used for conversion, value must be between 2 and 36, or 0. Returns: - An integer value that represents the string, or otherwise raises. + An integer value that represents the string. + + Raises: + If the given string cannot be parsed as an integer value or if an + incorrect base is provided. + + Examples: + >>> atol("32") + 32 + >>> atol("FF", 16) + 255 + >>> atol("0xFF", 0) + 255 + >>> atol("0b1010", 0) + 10 + + Notes: + This follows [Python's integer literals]( + https://docs.python.org/3/reference/lexical_analysis.html#integers). """ return _atol(str.as_string_slice(), base) From 1f8fa81a71b255c35ba262abb57bb029675f56e5 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 21 Nov 2024 15:42:44 -0800 Subject: [PATCH 1906/2019] [KGEN] Add ability to construct a string literal from integers and simd types MODULAR_ORIG_COMMIT_REV_ID: ae903bfda5f77887f9fba94bf0cc91b004450416 --- stdlib/src/builtin/string_literal.mojo | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 0373dd2f43..c81ce99ec5 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -856,3 +856,11 @@ struct StringLiteral( A copy of the string with no leading whitespaces. """ return str(self).lstrip() + + +fn _to_string_literal(i: Int) -> StringLiteral: + return __mlir_op.`pop.string.create`(i) + + +fn _to_string_literal(i: SIMD) -> StringLiteral: + return __mlir_op.`pop.string.create`(i) From 6238d953b29652e07c8b423faae342c26ae31f80 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 21 Nov 2024 19:01:10 -0800 Subject: [PATCH 1907/2019] [External] [stdlib] Implement `join()` with variadic params in string literals (#49036) [External] [stdlib] Implement `join()` with variadic params in string literals To be consistent with the `String.join()` signatures Co-authored-by: Manuel Saelices Closes modularml/mojo#3426 MODULAR_ORIG_COMMIT_REV_ID: 2c95774ee92a6fab2782320335edfa43debbe82b --- stdlib/src/builtin/string_literal.mojo | 44 ++++++++++++++++++++ stdlib/src/collections/string.mojo | 1 - stdlib/test/builtin/test_string_literal.mojo | 15 +++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c81ce99ec5..b4f01993b3 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -597,6 +597,50 @@ struct StringLiteral( """ return str(self).join(elems) + fn join(self, *elems: Int) -> String: + """Joins the elements from the tuple using the current string literal as a + delimiter. + + Args: + elems: The input tuple. + + Returns: + The joined string. + """ + if len(elems) == 0: + return "" + var curr = str(elems[0]) + for i in range(1, len(elems)): + curr += self + str(elems[i]) + return curr + + fn join[*Types: Stringable](self, *elems: *Types) -> String: + """Joins string elements using the current string as a delimiter. + + Parameters: + Types: The types of the elements. + + Args: + elems: The input values. + + Returns: + The joined string. + """ + + var result: String = "" + var is_first = True + + @parameter + fn add_elt[T: Stringable](a: T): + if is_first: + is_first = False + else: + result += self + result += str(a) + + elems.each[add_elt]() + return result + fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]: """Split the string literal by a separator. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index a7c5444688..55c4da1b0e 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1479,7 +1479,6 @@ struct String( result.write(a) elems.each[add_elt]() - _ = is_first return result fn join[T: StringableCollectionElement](self, elems: List[T, *_]) -> String: diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 1e4ca44832..429378fbd4 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -219,6 +219,20 @@ def test_intable(): _ = StringLiteral.__int__("hi") +def test_join(): + assert_equal("".join(), "") + assert_equal("".join("a", "b", "c"), "abc") + assert_equal(" ".join("a", "b", "c"), "a b c") + assert_equal(" ".join("a", "b", "c", ""), "a b c ") + assert_equal(" ".join("a", "b", "c", " "), "a b c ") + + var sep = "," + var s = String("abc") + assert_equal(sep.join(s, s, s, s), "abc,abc,abc,abc") + assert_equal(sep.join(1, 2, 3), "1,2,3") + assert_equal(sep.join(1, "abc", 3), "1,abc,3") + + def test_isdigit(): assert_true("123".isdigit()) assert_false("abc".isdigit()) @@ -469,6 +483,7 @@ def main(): test_bool() test_contains() test_find() + test_join() test_rfind() test_replace() test_comparison_operators() From eb2757337eb4f8e97d7b684cd268137dbfd3bb0b Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Thu, 21 Nov 2024 19:32:11 -0800 Subject: [PATCH 1908/2019] [External] [stdlib] Introduce `random.shuffle` for `List` (#51416) [External] [stdlib] Introduce `random.shuffle` for `List` Introduce `random.shuffle` for `List`. Implementation follows the Fisher-Yates shuffle. Co-authored-by: Joshua James Venter Closes modularml/mojo#3327 MODULAR_ORIG_COMMIT_REV_ID: 911eadee8d34911f653e8c69f85fcd89f5cae344 --- docs/changelog.md | 12 ++++ stdlib/src/random/__init__.mojo | 1 + stdlib/src/random/random.mojo | 19 +++++- stdlib/test/random/test_random.mojo | 97 ++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2c2529ff0f..4fb30d7c4a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -110,6 +110,18 @@ what we publish. - The `rebind` standard library function now works with memory-only types in addition to `@register_passable("trivial")` ones, without requiring a copy. +- Introduce `random.shuffle` for `List`. + ([PR #3327](https://github.com/modularml/mojo/pull/3327) by [@jjvraw](https://github.com/jjvraw)) + + Example: + + ```mojo + from random import shuffle + + var l = List[Int](1, 2, 3, 4, 5) + shuffle(l) + ``` + - The `Dict.__getitem__` method now returns a reference instead of a copy of the value (or raises). This improves the performance of common code that uses `Dict` by allowing borrows from the `Dict` elements. diff --git a/stdlib/src/random/__init__.mojo b/stdlib/src/random/__init__.mojo index d71e458454..231b4a115f 100644 --- a/stdlib/src/random/__init__.mojo +++ b/stdlib/src/random/__init__.mojo @@ -21,4 +21,5 @@ from .random import ( random_si64, random_ui64, seed, + shuffle, ) diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 840e353f7a..f0b8dee2ee 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -22,8 +22,7 @@ from random import seed from sys import bitwidthof, external_call from sys.ffi import OpaquePointer from time import perf_counter_ns -from collections import Optional - +from collections import Optional, List from memory import UnsafePointer from math import floor import math @@ -222,3 +221,19 @@ fn randn[ for i in range(size): ptr[i] = randn_float64(mean, variance).cast[type]() return + + +fn shuffle[T: CollectionElement, //](inout list: List[T]): + """Shuffles the elements of the list randomly. + + Performs an in-place Fisher-Yates shuffle on the provided list. + + Args: + list: The list to modify. + + Parameters: + T: The type of element in the List. + """ + for i in reversed(range(len(list))): + var j = int(random_ui64(0, i)) + list.swap_elements(i, j) diff --git a/stdlib/test/random/test_random.mojo b/stdlib/test/random/test_random.mojo index f1f1f6ac73..0a90b5ae89 100644 --- a/stdlib/test/random/test_random.mojo +++ b/stdlib/test/random/test_random.mojo @@ -12,7 +12,14 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from random import randn_float64, random_float64, random_si64, random_ui64, seed +from random import ( + randn_float64, + random_float64, + random_si64, + random_ui64, + seed, + shuffle, +) from testing import assert_equal, assert_true @@ -73,6 +80,94 @@ def test_seed(): assert_equal(some_unsigned_integer, random_ui64(0, 255)) +def test_shuffle(): + # TODO: Clean up with list comprehension when possible. + + # Property tests + alias L_i = List[Int] + alias L_s = List[String] + var a = L_i(1, 2, 3, 4) + var b = L_i(1, 2, 3, 4) + var c = L_s("Random", "shuffle", "in", "Mojo") + var d = L_s("Random", "shuffle", "in", "Mojo") + + shuffle(b) + assert_equal(len(a), len(b)) + assert_true(a != b) + for i in range(len(b)): + assert_true(b[i] in a) + + shuffle(d) + assert_equal(len(c), len(d)) + assert_true(c != d) + for i in range(len(d)): + assert_true(d[i] in c) + + var e = L_i(21) + shuffle(e) + assert_true(e == L_i(21)) + var f = L_s("Mojo") + shuffle(f) + assert_true(f == L_s("Mojo")) + + alias L_l = List[List[Int]] + var g = L_l() + var h = L_l() + for i in range(10): + g.append(L_i(i, i + 1, i + 3)) + h.append(L_i(i, i + 1, i + 3)) + shuffle(g) + # TODO: Uncomment when possible + # assert_true(g != h) + assert_equal(len(g), len(h)) + for i in range(10): + # Currently, the below does not compile. + # assert_true(g.__contains__(L_i(i, i + 1, i + 3))) + var target: List[Int] = L_i(i, i + 1, i + 3) + var found = False + for j in range(len(g)): + if g[j] == target: + found = True + break + assert_true(found) + + alias L_l_s = List[List[String]] + var i = L_l_s() + var j = L_l_s() + for x in range(10): + i.append(L_s(str(x), str(x + 1), str(x + 3))) + j.append(L_s(str(x), str(x + 1), str(x + 3))) + shuffle(i) + # TODO: Uncomment when possible + # assert_true(g != h) + assert_equal(len(i), len(j)) + for x in range(10): + var target: List[String] = L_s(str(x), str(x + 1), str(x + 3)) + var found = False + for y in range(len(i)): + if j[y] == target: + found = True + break + assert_true(found) + + # Given the number of permutations of size 1000 is 1000!, + # we rely on the assertion that a truly random shuffle should not + # result in the same order as the to pre-shuffle list with extremely + # high probability. + var l = L_i() + var m = L_i() + for i in range(1000): + l.append(i) + m.append(i) + shuffle(l) + assert_equal(len(l), len(m)) + assert_true(l != m) + shuffle(m) + assert_equal(len(l), len(m)) + assert_true(l != m) + + def main(): test_random() test_seed() + test_shuffle() From f60b737f220b56c21447513ad64b78941c7018ef Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 21 Nov 2024 23:41:39 -0800 Subject: [PATCH 1909/2019] [stdlib] Buffer GPU print using dynamic allocations For GPU calculate total bytes of arguments before allocating. There is limited space for allocations, so need to use the smallest size possible when calling `print` across thousands of threads to avoid running out of memory. MODULAR_ORIG_COMMIT_REV_ID: 2904debda2eb0d2c897a8fed52fc99bd4ea2bdf1 --- stdlib/src/builtin/debug_assert.mojo | 101 ++++++++++++++++++--------- stdlib/src/utils/write.mojo | 59 +++++++++------- 2 files changed, 101 insertions(+), 59 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 14a2a258f1..86f58ecb44 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -22,7 +22,13 @@ from sys._build import is_debug_build from sys.param_env import env_get_string from sys.ffi import external_call, c_uint, c_size_t, c_char from memory import UnsafePointer -from utils.write import _WriteBufferHeap, _WriteBufferStack, write_args +from utils.write import ( + _WriteBufferHeap, + _WriteBufferStack, + _ArgBytes, + write_args, +) +from utils import Span from builtin._location import __call_location, _SourceLocation @@ -237,18 +243,32 @@ fn _debug_assert_msg( @parameter if is_gpu(): - var buffer = _WriteBufferHeap[4096](stdout) - buffer.write("At ", loc, ": ") - _write_gpu_thread_context(buffer) - - @parameter - if defined_mode == "warn": - buffer.write(" Assert Warning: ") - else: - buffer.write(" Assert Error: ") - + # Count the total length of bytes to allocate only once + var arg_bytes = _ArgBytes() + arg_bytes.write( + "At ", + loc, + ": ", + _ThreadContext(), + " Assert ", + "Warning: " if defined_mode == "warn" else " Error: ", + ) + write_args(arg_bytes, messages, end="\n") + + var buffer = _WriteBufferHeap(arg_bytes.size + 1) + buffer.write( + "At ", + loc, + ": ", + _ThreadContext(), + " Assert ", + "Warning: " if defined_mode == "warn" else "Error: ", + ) write_args(buffer, messages, end="\n") - buffer.flush() + buffer.data[buffer.pos] = 0 + stdout.write_bytes( + Span[Byte, ImmutableAnyOrigin](ptr=buffer.data, length=buffer.pos) + ) @parameter if defined_mode != "warn": @@ -272,28 +292,43 @@ fn _debug_assert_msg( abort() -# Can't import gpu module at this stage in compilation for thread/block idx -fn _write_gpu_thread_context[W: Writer](inout writer: W): - writer.write("block: [") - _write_id["block", "x"](writer) - writer.write(",") - _write_id["block", "y"](writer) - writer.write(",") - _write_id["block", "z"](writer) - writer.write("] thread: [") - _write_id["thread", "x"](writer) - writer.write(",") - _write_id["thread", "y"](writer) - writer.write(",") - _write_id["thread", "z"](writer) - writer.write("]") - - -fn _write_id[ - W: Writer, //, type: StringLiteral, dim: StringLiteral -](inout writer: W): +struct _ThreadContext(Writable): + var block_x: Int32 + var block_y: Int32 + var block_z: Int32 + var thread_x: Int32 + var thread_y: Int32 + var thread_z: Int32 + + fn __init__(out self): + self.block_x = _get_id["block", "x"]() + self.block_y = _get_id["block", "y"]() + self.block_z = _get_id["block", "z"]() + self.thread_x = _get_id["thread", "x"]() + self.thread_y = _get_id["thread", "y"]() + self.thread_z = _get_id["thread", "z"]() + + fn write_to[W: Writer](self, inout writer: W): + writer.write( + "block: [", + self.block_x, + ",", + self.block_y, + ",", + self.block_z, + "] thread: [", + self.thread_x, + ",", + self.thread_y, + ",", + self.thread_z, + "]", + ) + + +fn _get_id[type: StringLiteral, dim: StringLiteral]() -> Int32: alias intrinsic_name = _get_intrinsic_name[type, dim]() - writer.write(llvm_intrinsic[intrinsic_name, Int32, has_side_effect=False]()) + return llvm_intrinsic[intrinsic_name, Int32, has_side_effect=False]() fn _get_intrinsic_name[ diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 93270dc2b5..1b4d8da6d2 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -18,7 +18,6 @@ from utils import Span, StaticString from sys.info import is_gpu from builtin.io import _printf - # ===----------------------------------------------------------------------===# @@ -223,25 +222,18 @@ trait MovableWriter(Movable, Writer): ... -struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): +struct _WriteBufferHeap(Writer): var data: UnsafePointer[UInt8] var pos: Int - var writer: W - @implicit - fn __init__(out self, owned writer: W): + fn __init__(out self, size: Int): self.data = UnsafePointer[ - UInt8, - address_space = AddressSpace.GENERIC, - ].alloc(capacity) + UInt8, address_space = AddressSpace.GENERIC + ].alloc(size) self.pos = 0 - self.writer = writer^ - fn flush(inout self): - self.writer.write_bytes( - Span[Byte, ImmutableAnyOrigin](ptr=self.data, length=self.pos) - ) - self.pos = 0 + fn __del__(owned self): + self.data.free() @always_inline fn write_bytes(inout self, bytes: Span[UInt8, _]): @@ -249,16 +241,7 @@ struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): # If empty then return if len_bytes == 0: return - # If span is too large to fit in buffer, write directly and return - if len_bytes > capacity: - self.flush() - self.writer.write_bytes(bytes) - return - # If buffer would overflow, flush writer and reset pos to 0. - if self.pos + len_bytes > capacity: - self.flush() - ptr = bytes.unsafe_ptr() - # Continue writing to buffer + var ptr = bytes.unsafe_ptr() for i in range(len_bytes): self.data[i + self.pos] = ptr[i] self.pos += len_bytes @@ -271,6 +254,23 @@ struct _WriteBufferHeap[W: MovableWriter, //, capacity: Int](Writer): args.each[write_arg]() +struct _ArgBytes(Writer): + var size: Int + + fn __init__(out self): + self.size = 0 + + fn write_bytes(inout self, bytes: Span[UInt8, _]): + self.size += len(bytes) + + fn write[*Ts: Writable](inout self, *args: *Ts): + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() + + struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): var data: InlineArray[UInt8, capacity] var pos: Int @@ -367,9 +367,16 @@ fn write_buffered[ @parameter if is_gpu(): # Stack space is very small on GPU due to many threads, so use heap - var buffer = _WriteBufferHeap[buffer_size](writer^) + # Count the total length of bytes to allocate only once + var arg_bytes = _ArgBytes() + write_args(arg_bytes, args, sep=sep, end=end) + + var buffer = _WriteBufferHeap(arg_bytes.size + 1) write_args(buffer, args, sep=sep, end=end) - buffer.flush() + buffer.data[buffer.pos] = 0 + writer.write_bytes( + Span[Byte, ImmutableAnyOrigin](ptr=buffer.data, length=buffer.pos) + ) else: var buffer = _WriteBufferStack[buffer_size](writer^) write_args(buffer, args, sep=sep, end=end) From b1ad1e705229a924856400e78a93ecf1d0cc480a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 22 Nov 2024 13:20:42 -0800 Subject: [PATCH 1910/2019] [******][GPU] Ensure to mark math function as non-side effecting MODULAR_ORIG_COMMIT_REV_ID: df27bf28339991200ddd00d171140af6a769c397 --- stdlib/src/math/math.mojo | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 7371d2a3df..0ada40bdfe 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1401,7 +1401,9 @@ fn cos[ instruction="cos.approx.ftz.f32", constraints="=f,f" ](x) elif is_amd_gpu(): - return llvm_intrinsic["llvm.cos", __type_of(x)](x) + return llvm_intrinsic["llvm.cos", __type_of(x), has_side_effect=False]( + x + ) else: return _call_libm["cos"](x) @@ -1436,7 +1438,9 @@ fn sin[ instruction="sin.approx.ftz.f32", constraints="=f,f" ](x) elif is_amd_gpu(): - return llvm_intrinsic["llvm.sin", __type_of(x)](x) + return llvm_intrinsic["llvm.sin", __type_of(x), has_side_effect=False]( + x + ) else: return _call_libm["sin"](x) @@ -1615,7 +1619,9 @@ fn log10(x: SIMD) -> __type_of(x): * log10_2 ) elif is_amd_gpu(): - return llvm_intrinsic["llvm.log10", __type_of(x)](x) + return llvm_intrinsic[ + "llvm.log10", __type_of(x), has_side_effect=False + ](x) return _call_libm["log10"](x) From 9d5feebc13dc0aa0ca8ed1a41a870ee9d46cd180 Mon Sep 17 00:00:00 2001 From: Rasool Sharifi Date: Fri, 22 Nov 2024 22:28:58 -0800 Subject: [PATCH 1911/2019] [stdlib] Public title Added test case for fp8 `ldmatrix` MODULAR_ORIG_COMMIT_REV_ID: 372c4c4113a837ec218d689943fdfe08995647c1 --- stdlib/src/utils/numerics.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index 05db78d5f5..f9a52a9972 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -1001,7 +1001,9 @@ fn get_accum_type[type: DType]() -> DType: DType.float32 if type is a half-precision float, type otherwise. """ - return DType.float32 if type.is_half_float() else type + return DType.float32 if ( + type.is_half_float() or type in (DType.float8e4m3, DType.float8e5m2) + ) else type # ===----------------------------------------------------------------------=== # From 7fc731170da0fa79db1ba741842486b8a2dfbc1e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 23 Nov 2024 22:36:25 -0800 Subject: [PATCH 1912/2019] [mojo-stdlib][KGEN] Expand constrained to work with a `String` parameter This introduces a new `kgen.param.assert.ex` operation that takes a pointer+length for data as a parameter, and uses that to introduce an overload of `constrained` that takes a general `String` instead of just a `StringLiteral`. This allows more interesting expressions, but at the cost of compile time. The existing overload of `constrained` is retained so the majority of callers that pass in a string literal directly will not see any compile time impact. MODULAR_ORIG_COMMIT_REV_ID: 724d791274d08a582b6c738d445fdac90e9fef72 --- stdlib/src/builtin/constrained.mojo | 40 ++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/constrained.mojo b/stdlib/src/builtin/constrained.mojo index c7cbb756b4..b0898c3447 100644 --- a/stdlib/src/builtin/constrained.mojo +++ b/stdlib/src/builtin/constrained.mojo @@ -54,4 +54,42 @@ fn constrained[cond: Bool, msg: StringLiteral = "param assertion failed"](): __mlir_op.`kgen.param.assert`[ cond = cond.__mlir_i1__(), message = msg.value ]() - return + + +@always_inline("nodebug") +fn constrained[cond: Bool, msg: String](): + """Compile time checks that the condition is true. + + The `constrained` is similar to `static_assert` in C++ and is used to + introduce constraints on the enclosing function. In Mojo, the assert places + a constraint on the function. The message is displayed when the assertion + fails, and takes a generalized string. + + Parameters: + cond: The bool value to assert. + msg: The message to display on failure. + + Example: + + ```mojo + from sys.info import num_physical_cores + + def main(): + alias cores_to_use = 2 + multicore_check[cores_to_use]() + + def multicore_check[cores: Int](): + constrained[ + cores <= num_physical_cores(), + "build failed: not enough cores" + ]() + constrained[ + cores >= 2, + "at least two cores are required" + ]() + """ + __mlir_op.`kgen.param.assert.ex`[ + cond = cond.__mlir_i1__(), + messageStart = msg.unsafe_ptr().address, + messageLength = msg.byte_length().value, + ]() From b7b9394bdc5bc7096ea8a74daafc8064f7e57b08 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 24 Nov 2024 10:52:13 -0800 Subject: [PATCH 1913/2019] [mojo-lang] Fix AST doc printing of result types. (#51498) This fixes result type printing to use pretty symbolic names for parameters instead of $1 etc. This is done by using the result type from the function type, instead of the structural signaturetype. This fixes MOTO-418 MODULAR_ORIG_COMMIT_REV_ID: 401f480c7dee0b4e190b4f4074f09c5d18919806 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 4fb30d7c4a..5dc1d9d472 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -586,3 +586,6 @@ what we publish. - Tooling now prints the origins of `ref` arguments and results correctly, and prints `self` instead of `self: Self` in methods. + +- The LSP and generated documentation now print parametric result types + correctly, e.g. showing `SIMD[type, simd_width]` instead of `SIMD[$0, $1]`. From 781ce0c3af701b8682ef0697b9d8255b357f607c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 24 Nov 2024 12:02:50 -0800 Subject: [PATCH 1914/2019] [mojo-stdlib] Introduce a new comptime String -> StringLiteral ctor This introduces a new `StringLiteral.from_string[someString]()` method that converts a comptime String value into a StringLiteral. This allows us to bridge the gap between dynamic-at-comptime strings and static-at-runtime strings in a nice and general way. This replaces the use of `kgen.param.assert.ex` I introduced in a previous patch, but doesn't remove the machinery for that. I will do so in a later patch. MODULAR_ORIG_COMMIT_REV_ID: eed76a05871ed19f015afd7229976fb52a8e2a97 --- docs/changelog.md | 4 ++++ stdlib/src/builtin/constrained.mojo | 6 +----- stdlib/src/builtin/string_literal.mojo | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5dc1d9d472..c8ec0e274b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -274,6 +274,10 @@ what we publish. memory allocation and performance. These options allow for optimized memory usage and reduced buffer reallocations, providing flexibility based on application requirements. +- A new `StringLiteral.from_string[someString]()` method is available. It + allows forming a runtime-constant StringLiteral from a compile-time-dynamic + `String` value. + ### 🦋 Changed - The argument convention for `__init__` methods has been changed from `inout` diff --git a/stdlib/src/builtin/constrained.mojo b/stdlib/src/builtin/constrained.mojo index b0898c3447..09d934ff1c 100644 --- a/stdlib/src/builtin/constrained.mojo +++ b/stdlib/src/builtin/constrained.mojo @@ -88,8 +88,4 @@ fn constrained[cond: Bool, msg: String](): "at least two cores are required" ]() """ - __mlir_op.`kgen.param.assert.ex`[ - cond = cond.__mlir_i1__(), - messageStart = msg.unsafe_ptr().address, - messageLength = msg.byte_length().value, - ]() + constrained[cond, StringLiteral.from_string[msg]()]() diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index b4f01993b3..85f4f544df 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -86,6 +86,29 @@ struct StringLiteral( """ self = other + # TODO(MOCO-1460): This should be: fn __init__[*, value: String](out self): + # but Mojo tries to bind the parameter in `StringLiteral["foo"]()` to the + # type instead of the initializer. Use a static method to work around this + # for now. + @always_inline("nodebug") + @staticmethod + fn from_string[value: String]() -> StringLiteral: + """Form a string literal from an arbitrary compile-time String value. + + Parameters: + value: The string value to use. + + Returns: + The string value as a StringLiteral. + """ + return __mlir_attr[ + `#kgen.param.expr : !kgen.string`, + ] + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# From dc5fe0e49a8f1ac5390da2c3411d30b691deba4c Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Sun, 24 Nov 2024 14:34:58 -0600 Subject: [PATCH 1915/2019] [stdlib] feat: Make `Origin` wrap an MLIR origin value Up until this point, `Origin` has effectively been used only as a parametric alias around a `!lit.origin`. With this change, `Origin` becomes a first-class type, with the intention that we migrate uses of the `Origin[..].type` parameter "alias" usage pattern to just `Origin[..]`. Actually doing that migration will require fixing several compiler bugs along the way. MODULAR_ORIG_COMMIT_REV_ID: 5860ed9f5719eb952cc88d674c25cab9721fe699 --- stdlib/src/builtin/type_aliases.mojo | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 70d70a664e..f255368e96 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -44,6 +44,7 @@ alias OriginSet = __mlir_type.`!lit.origin.set` # Helper to build a value of !lit.origin type. # TODO: Should be a parametric alias. +@value struct Origin[is_mutable: Bool]: """This represents a origin reference of potentially parametric type. TODO: This should be replaced with a parametric type alias. @@ -57,3 +58,26 @@ struct Origin[is_mutable: Bool]: is_mutable.value, `>`, ] + + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var _mlir_origin: Self.type + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + + # NOTE: + # Needs to be @implicit convertible for the time being so that + # `__origin_of(..)` can implicilty convert to `Origin` in use cases like: + # Span[Byte, __origin_of(self)] + @implicit + @always_inline("nodebug") + fn __init__(out self, mlir_origin: Self.type): + """Initialize an Origin from a raw MLIR `!lit.origin` value. + + Args: + mlir_origin: The raw MLIR origin value.""" + self._mlir_origin = mlir_origin From 47857246f9c9b2cf9062164b8cbd183cda9a6b7e Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 24 Nov 2024 14:43:13 -0800 Subject: [PATCH 1916/2019] [KGEN] Remove pop.string.create in favor of StringLiteral.from_string MODULAR_ORIG_COMMIT_REV_ID: 5519577fce3c3f2eb137c79cc87cd2445492ec9a --- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a0c68f35f7..6b8c9edbdf 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3368,7 +3368,7 @@ fn _write_scalar[ elif dtype.is_integral(): @parameter - if is_gpu(): + if is_gpu() or dtype.is_integral(): var err = _try_write_int(writer, value) if err: abort( diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 85f4f544df..c6859b4f04 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -925,9 +925,12 @@ struct StringLiteral( return str(self).lstrip() -fn _to_string_literal(i: Int) -> StringLiteral: - return __mlir_op.`pop.string.create`(i) +fn _to_string_literal[val: Int]() -> StringLiteral: + alias s = StringLiteral.from_string[str(val)]() + return s -fn _to_string_literal(i: SIMD) -> StringLiteral: - return __mlir_op.`pop.string.create`(i) +fn _to_string_literal[val: SIMD]() -> StringLiteral: + constrained[val.type.is_integral(), "input type must be integral"]() + alias s = StringLiteral.from_string[str(val)]() + return s From 008a78169c9e8c903fdec53d7c4efad09c35956b Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 24 Nov 2024 15:03:47 -0800 Subject: [PATCH 1917/2019] [mojo-stdlib] Mark Origin register passable. This marks the type as register_passable and tidies it up a bit. Doing so allows it to be used with the legacy `T{..}` initializer syntax, which is handy for various workarounds. MODULAR_ORIG_COMMIT_REV_ID: 9ee5247be330d992e5207eca041532bb10c34600 --- stdlib/src/builtin/type_aliases.mojo | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index f255368e96..5bbee8d763 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -42,15 +42,13 @@ alias OriginSet = __mlir_type.`!lit.origin.set` """A set of origin parameters.""" -# Helper to build a value of !lit.origin type. -# TODO: Should be a parametric alias. @value +@register_passable("trivial") struct Origin[is_mutable: Bool]: - """This represents a origin reference of potentially parametric type. - TODO: This should be replaced with a parametric type alias. + """This represents a origin reference for a memory value. Parameters: - is_mutable: Whether the origin reference is mutable. + is_mutable: Whether the origin is mutable. """ alias type = __mlir_type[ From b964808ca67b13c8041116e0161fe3bd015f190d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 24 Nov 2024 21:35:25 -0800 Subject: [PATCH 1918/2019] [Stdlib] Move the _sub_with_saturation to the b64 module Moves the helper function which is used only in the b64 module outside the SIMD file. MODULAR_ORIG_COMMIT_REV_ID: c7071970e04d72c0337b5bccda044219b64a5abf --- stdlib/src/base64/_b64encode.mojo | 12 +++++++++++- stdlib/src/builtin/simd.mojo | 10 ---------- stdlib/src/utils/_utf8_validation.mojo | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo index d867a91be5..03aa0a56ab 100644 --- a/stdlib/src/base64/_b64encode.mojo +++ b/stdlib/src/base64/_b64encode.mojo @@ -24,11 +24,11 @@ Instructions, ACM Transactions on the Web 12 (3), 2018. https://arxiv.org/abs/1704.00605 """ -from builtin.simd import _sub_with_saturation from collections import InlineArray from math.math import _compile_time_iota from memory import memcpy, bitcast, UnsafePointer from utils import IndexList +from sys import llvm_intrinsic alias Bytes = SIMD[DType.uint8, _] @@ -291,3 +291,13 @@ fn _rshift_bits_in_u16[shift: Int](input: Bytes) -> __type_of(input): var u16 = bitcast[DType.uint16, input.size // 2](input) var res = bit.rotate_bits_right[shift](u16) return bitcast[DType.uint8, input.size](res) + + +@always_inline +fn _sub_with_saturation[ + width: Int, // +](a: SIMD[DType.uint8, width], b: SIMD[DType.uint8, width]) -> SIMD[ + DType.uint8, width +]: + # generates a single `vpsubusb` on x86 with AVX + return llvm_intrinsic["llvm.usub.sat", __type_of(a)](a, b) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6b8c9edbdf..8146759a4b 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3311,16 +3311,6 @@ fn _modf(x: SIMD) -> Tuple[__type_of(x), __type_of(x)]: return (result_int, result_frac) -@always_inline("nodebug") -fn _sub_with_saturation[ - width: Int, // -](a: SIMD[DType.uint8, width], b: SIMD[DType.uint8, width]) -> SIMD[ - DType.uint8, width -]: - # generates a single `vpsubusb` on x86 with AVX - return llvm_intrinsic["llvm.usub.sat", __type_of(a)](a, b) - - # ===----------------------------------------------------------------------=== # # floor # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index 800ed5626b..3cc522d33d 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -27,7 +27,7 @@ https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs from memory import UnsafePointer from sys.intrinsics import llvm_intrinsic -from builtin.simd import _sub_with_saturation +from base64._b64encode import _sub_with_saturation alias TOO_SHORT: UInt8 = 1 << 0 alias TOO_LONG: UInt8 = 1 << 1 From 993c411f1a1682323b91737b167ad2e6d3b259ed Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 24 Nov 2024 21:42:50 -0800 Subject: [PATCH 1919/2019] [******][GPU] Move the host side check for the gpu target to sys module MODULAR_ORIG_COMMIT_REV_ID: b208657c08af3a11d4c2ddd0ef74e88bfb5c0a55 --- stdlib/src/sys/__init__.mojo | 2 ++ stdlib/src/sys/info.mojo | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 632dde283e..1323b94f86 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -50,6 +50,8 @@ from .info import ( is_nvidia_gpu, is_amd_gpu, is_gpu, + has_amd_gpu, + has_nvidia_gpu, ) from .intrinsics import ( PrefetchCache, diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 9930fe79a0..840e5de7f6 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -863,3 +863,28 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: patch = int(osver[: osver.find(".")]) return (major, minor, patch) + + +# ===----------------------------------------------------------------------===# +# Detect GPU on host side +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn has_amd_gpu() -> Bool: + """Returns True if the host system has an AMD GPU and False otherwise. + + Returns: + True if the host system has an AMD GPU. + """ + return "amd" in _accelerator_arch() + + +@always_inline("nodebug") +fn has_nvidia_gpu() -> Bool: + """Returns True if the host system has an NVIDIA GPU and False otherwise. + + Returns: + True if the host system has an NVIDIA GPU. + """ + return "nvidia" in _accelerator_arch() From 600e5089a389af1de28a655612f808c326000761 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 25 Nov 2024 12:36:00 -0700 Subject: [PATCH 1920/2019] [stdlib][docs] Clarify SLA review time Add some more color on the SLA review time and adjust expectations based on current contributor volume and team size. MODULAR_ORIG_COMMIT_REV_ID: 482ba59d73ab9528d1733e210dd8aef23886a8c6 --- CONTRIBUTING.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5392781df..72f230da1b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -386,7 +386,64 @@ By making a contribution to this project, I certify that: this project or the open source license(s) involved. ``` -### Review time SLA +### Guidelines for Review Time -The team commits to reviewing submitted pull requests within a week of -submission. +1. Pull Request (PR) Review Timeline + +- Initial Review: + - Maintainers will provide an initial review or feedback within 3 weeks of + the PR submission. At times, it may be significantly quicker, but it + depends on a variety of factors. +- Subsequent Reviews: + - Once a contributor addresses feedback, maintainers will review updates as + soon as they can, typically within 5 business days. + +1. Issue Triage Timeline + +- New Issues: + - Maintainers will label and acknowledge new issues within 10 days of the + issue submission. + +1. Proposals + +- Proposals take more time for the team to review, discuss, and make sure this + is in line with the overall strategy and vision for the standard library. + These will get discussed in the team's weekly design meetings internally and + feedback will be communicated back on the relevant proposal. As a team, we'll + ensure these get reviewed and discussed within 6 weeks of submission. + +#### Exceptions + +While we strive our best to adhere to these timelines, there may be occasional +delays due any of the following: + +- High volume of contributions. +- Maintainers' availability (e.g. holidays, team events). +- Complex issues or PRs requiring extended discussion (these may get deferred to + the team's weekly design discussion meetings). + +Note that just because a pull request has been reviewed does not necessarily +mean it will be able to merged internally immediately. This could be due to a +variety of reasons, such as: + +- Mojo compiler bugs. These take time to find a minimal reproducer, file an + issue with the compiler team, and then get prioritized and fixed. +- Internal bugs that get exposed due to a changeset. +- Massive refactorings due to an external changeset. These also take time to + fix - remember, we have the largest Mojo codebase in the world internally. + +If delays occur, we'll provide status updates in the relevant thread (pull +request or GitHub issue). Please bare with us as Mojo is an early language. +We look forward to working together with you in making Mojo better for everyone! + +#### How You Can Help + +To ensure quicker reviews: + +- **Ensure your PR is small and focused.** See the [pull request size section](#about-pull-request-sizes) + for more info. +- Write a good commit message/PR summary outlining the motivation and describing + the changes. In the near future, we'll provide a pull request template to + clarify this further. +- Use descriptive titles and comments for clarity. +- Code-review other contributor pull requests and help each other. From 7ecc9737cf00e7d67b7307240428906a0d1e9d87 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:28:00 -0800 Subject: [PATCH 1921/2019] Reference solution for the new Intro to Mojo tutorial, an implementation of Conway's Game of Life MODULAR_ORIG_COMMIT_REV_ID: 51d3816123bfcef47eca4d24c31d3f6daf2ce858 --- examples/life/README.md | 77 +++++++++++++++++ examples/life/benchmark.mojo | 61 ++++++++++++++ examples/life/gridv1.mojo | 124 ++++++++++++++++++++++++++++ examples/life/gridv2.mojo | 121 +++++++++++++++++++++++++++ examples/life/lifev1.mojo | 85 +++++++++++++++++++ examples/life/lifev2.mojo | 85 +++++++++++++++++++ examples/life/mojoproject.toml | 19 +++++ examples/life/test/test_gridv1.mojo | 75 +++++++++++++++++ examples/life/test/test_gridv2.mojo | 84 +++++++++++++++++++ 9 files changed, 731 insertions(+) create mode 100644 examples/life/README.md create mode 100644 examples/life/benchmark.mojo create mode 100644 examples/life/gridv1.mojo create mode 100644 examples/life/gridv2.mojo create mode 100644 examples/life/lifev1.mojo create mode 100644 examples/life/lifev2.mojo create mode 100644 examples/life/mojoproject.toml create mode 100644 examples/life/test/test_gridv1.mojo create mode 100644 examples/life/test/test_gridv2.mojo diff --git a/examples/life/README.md b/examples/life/README.md new file mode 100644 index 0000000000..49a1884991 --- /dev/null +++ b/examples/life/README.md @@ -0,0 +1,77 @@ +# Introduction to Mojo tutorial solution + +This directory contains a complete solution for the [Introduction to +Mojo](https://docs.modular.com/mojo/manual/basics) tutorial project, which is an +implementation of [Conway's Game of +Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) cellular +automation. + +## Files + +This directory contains the following files: + +- The source files `lifev1.mojo` and `gridv1.mojo` provide an initial version of + the project, with a `Grid` struct representing the grid of cells as a + `List[List[Int]]`. + +- The source files `lifev2.mojo` and `gridv2.mojo` provide a subsequent version + of the project, with a `Grid` struct representing the grid of cells as a block + of memory managed by `UnsafePointer`. + +- The `benchmark.mojo` file performs a simple performance benchmark of the two + versions by running 1,000 evolutions of each `Grid` implementation using a + 1,024 x 1,024 grid. + +- The `test` directory contains unit tests for each `Grid` implementation using + the [Mojo testing framework](https://docs.modular.com/mojo/tools/testing). + +- The `mojoproject.toml` file is a [Magic](https://docs.modular.com/magic/) + project file containing the project dependencies and task definitions. + +## Run the code + +If you have [`magic`](https://docs.modular.com/magic) installed, you can +execute version 1 of the program by running the following command: + +```bash +magic run lifev1 +``` + +This displays a window that shows an initial random state for the grid and then +automatically updates it with subsequent generations. Quit the program by +pressing the `q` or `` key or by closing the window. + +You can execute version 2 of the program by running the following command: + +```bash +magic run lifev2 +``` + +Just like for version 1, this displays a window that shows an initial random +state for the grid and then automatically updates it with subsequent +generations. Quit the program by pressing the `q` or `` key or by +closing the window. + +You can execute the benchmark program by running the following command: + +```bash +magic run benchmark +``` + +You can run the unit tests by running the following command: + +```bash +magic run test +``` + +## Dependencies + +This project includes an example of using a Python package, +[pygame](https://www.pygame.org/wiki/about), from Mojo. Building the program +does *not* embed pygame or a Python runtime in the resulting executable. +Therefore, to run this program your environment must have both a compatible +Python runtime (Python 3.12) and the pygame package installed. + +The easiest way to ensure that the runtime dependencies are met is to run the +program with [`magic`](https://docs.modular.com/magic/), which manages a virtual +environment for the project as defined by the `mojoproject.toml` file. diff --git a/examples/life/benchmark.mojo b/examples/life/benchmark.mojo new file mode 100644 index 0000000000..268e9496ff --- /dev/null +++ b/examples/life/benchmark.mojo @@ -0,0 +1,61 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +import gridv1 +import gridv2 +from time import perf_counter_ns + + +def main(): + alias warmup_iterations = 10 + alias benchmark_iterations = 1000 + alias rows = 1024 + alias cols = 1024 + + # Initial state + gridv1 = gridv1.Grid.random(rows, cols, seed=42) + gridv2 = gridv2.Grid[rows, cols].random(seed=42) + + # Warm up + warmv1 = gridv1 + for i in range(warmup_iterations): + warmv1 = warmv1.evolve() + + warmv2 = gridv2 + for i in range(warmup_iterations): + warmv2 = warmv2.evolve() + + # Benchmark + start_time = perf_counter_ns() + for i in range(benchmark_iterations): + gridv1 = gridv1.evolve() + stop_time = perf_counter_ns() + elapsed = round((stop_time - start_time) / 1e6, 3) + print( + benchmark_iterations, + "evolutions of gridv1.Grid elapsed time: ", + elapsed, + "ms", + ) + + start_time = perf_counter_ns() + for i in range(benchmark_iterations): + gridv2 = gridv2.evolve() + stop_time = perf_counter_ns() + elapsed = round((stop_time - start_time) / 1e6, 3) + print( + benchmark_iterations, + "evolutions of gridv2.Grid elapsed time: ", + elapsed, + "ms", + ) diff --git a/examples/life/gridv1.mojo b/examples/life/gridv1.mojo new file mode 100644 index 0000000000..e416860928 --- /dev/null +++ b/examples/life/gridv1.mojo @@ -0,0 +1,124 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from collections import Optional +import random + + +@value +struct Grid(StringableRaising): + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + var rows: Int + var cols: Int + var data: List[List[Int]] + + # ===-------------------------------------------------------------------===# + # Indexing + # ===-------------------------------------------------------------------===# + + def __getitem__(self, row: Int, col: Int) -> Int: + return self.data[row][col] + + def __setitem__(inout self, row: Int, col: Int, value: Int) -> None: + self.data[row][col] = value + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + def __str__(self) -> String: + str = String() + for row in range(self.rows): + for col in range(self.cols): + if self[row, col] == 1: + str += "*" + else: + str += " " + if row != self.rows - 1: + str += "\n" + return str + + # ===-------------------------------------------------------------------===# + # Factory methods + # ===-------------------------------------------------------------------===# + + @staticmethod + def glider() -> Self: + var glider = List( + List(0, 1, 0, 0, 0, 0, 0, 0), + List(0, 0, 1, 0, 0, 0, 0, 0), + List(1, 1, 1, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + ) + return Grid(8, 8, glider) + + @staticmethod + def random(rows: Int, cols: Int, seed: Optional[Int] = None) -> Self: + if seed: + random.seed(seed.value()) + else: + random.seed() + + data = List[List[Int]]() + + for row in range(rows): + row_data = List[Int]() + for col in range(cols): + row_data.append(int(random.random_si64(0, 1))) + data.append(row_data) + + return Self(rows, cols, data) + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + def evolve(self) -> Self: + next_generation = List[List[Int]]() + + for row in range(self.rows): + row_data = List[Int]() + + row_above = (row - 1) % self.rows + row_below = (row + 1) % self.rows + + for col in range(self.cols): + col_left = (col - 1) % self.cols + col_right = (col + 1) % self.cols + + num_neighbors = ( + self[row_above, col_left] + + self[row_above, col] + + self[row_above, col_right] + + self[row, col_left] + + self[row, col_right] + + self[row_below, col_left] + + self[row_below, col] + + self[row_below, col_right] + ) + + if num_neighbors | self[row, col] == 3: + row_data.append(1) + else: + row_data.append(0) + + next_generation.append(row_data) + + return Self(self.rows, self.cols, next_generation) diff --git a/examples/life/gridv2.mojo b/examples/life/gridv2.mojo new file mode 100644 index 0000000000..8f937a2046 --- /dev/null +++ b/examples/life/gridv2.mojo @@ -0,0 +1,121 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from collections import Optional +from memory import memcpy, memset_zero, UnsafePointer +import random + + +struct Grid[rows: Int, cols: Int](StringableRaising): + # ===-------------------------------------------------------------------===# + # Fields + # ===-------------------------------------------------------------------===# + + alias num_cells = rows * cols + var data: UnsafePointer[Int8] + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + + def __init__(out self): + self.data = UnsafePointer[Int8].alloc(self.num_cells) + memset_zero(self.data, self.num_cells) + + fn __copyinit__(out self, existing: Self): + self.data = UnsafePointer[Int8].alloc(self.num_cells) + memcpy(dest=self.data, src=existing.data, count=self.num_cells) + # The lifetime of `existing` continues unchanged + + fn __moveinit__(out self, owned existing: Self): + self.data = existing.data + # Then the lifetime of `existing` ends here, but + # Mojo does NOT call its destructor + + fn __del__(owned self): + for i in range(self.num_cells): + (self.data + i).destroy_pointee() + self.data.free() + + # ===-------------------------------------------------------------------===# + # Factory methods + # ===-------------------------------------------------------------------===# + + @staticmethod + def random(seed: Optional[Int] = None) -> Self: + if seed: + random.seed(seed.value()) + else: + random.seed() + + grid = Self() + random.randint(grid.data, grid.num_cells, 0, 1) + + return grid + + # ===-------------------------------------------------------------------===# + # Indexing + # ===-------------------------------------------------------------------===# + + def __getitem__(self, row: Int, col: Int) -> Int8: + return (self.data + row * cols + col)[] + + def __setitem__(inout self, row: Int, col: Int, value: Int8) -> None: + (self.data + row * cols + col)[] = value + + # ===-------------------------------------------------------------------===# + # Trait implementations + # ===-------------------------------------------------------------------===# + + def __str__(self) -> String: + str = String() + for row in range(rows): + for col in range(cols): + if self[row, col] == 1: + str += "*" + else: + str += " " + if row != rows - 1: + str += "\n" + return str + + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + + def evolve(self) -> Self: + next_generation = Self() + + for row in range(rows): + row_above = (row - 1) % rows + row_below = (row + 1) % rows + + for col in range(cols): + col_left = (col - 1) % cols + col_right = (col + 1) % cols + + num_neighbors = ( + self[row_above, col_left] + + self[row_above, col] + + self[row_above, col_right] + + self[row, col_left] + + self[row, col_right] + + self[row_below, col_left] + + self[row_below, col] + + self[row_below, col_right] + ) + + if num_neighbors | self[row, col] == 3: + next_generation[row, col] = 1 + + return next_generation diff --git a/examples/life/lifev1.mojo b/examples/life/lifev1.mojo new file mode 100644 index 0000000000..c87ca0eeb0 --- /dev/null +++ b/examples/life/lifev1.mojo @@ -0,0 +1,85 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from gridv1 import Grid +from python import Python, PythonObject +import time + + +def run_display( + owned grid: Grid, + window_height: Int = 600, + window_width: Int = 600, + background_color: String = "black", + cell_color: String = "green", + pause: Float64 = 0.1, +) -> None: + # Import the pygame Python package + pygame = Python.import_module("pygame") + + # Initialize pygame modules + pygame.init() + + # Create a window and set its title + window = pygame.display.set_mode((window_height, window_width)) + pygame.display.set_caption("Conway's Game of Life") + + cell_height = window_height / grid.rows + cell_width = window_width / grid.cols + border_size = 1 + cell_fill_color = pygame.Color(cell_color) + background_fill_color = pygame.Color(background_color) + + running = True + while running: + # Poll for events + event = pygame.event.poll() + if event.type == pygame.QUIT: + # Quit if the window is closed + running = False + elif event.type == pygame.KEYDOWN: + # Also quit if the user presses or 'q' + if event.key == pygame.K_ESCAPE or event.key == pygame.K_q: + running = False + + # Clear the window by painting with the background color + window.fill(background_fill_color) + + # Draw each live cell in the grid + for row in range(grid.rows): + for col in range(grid.cols): + if grid.data[row][col]: + x = col * cell_width + border_size + y = row * cell_height + border_size + width = cell_width - border_size + height = cell_height - border_size + pygame.draw.rect( + window, cell_fill_color, (x, y, width, height) + ) + + # Update the display + pygame.display.flip() + + # Pause to let the user appreciate the scene + time.sleep(pause) + + # Next generation + grid = grid.evolve() + + # Shut down pygame cleanly + pygame.quit() + + +def main(): + start = Grid.random(128, 128) + run_display(start) diff --git a/examples/life/lifev2.mojo b/examples/life/lifev2.mojo new file mode 100644 index 0000000000..c399d2eb9b --- /dev/null +++ b/examples/life/lifev2.mojo @@ -0,0 +1,85 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from gridv2 import Grid +from python import Python, PythonObject +import time + + +def run_display( + owned grid: Grid, + window_height: Int = 600, + window_width: Int = 600, + background_color: String = "black", + cell_color: String = "green", + pause: Float64 = 0.1, +) -> None: + # Import the pygame Python package + pygame = Python.import_module("pygame") + + # Initialize pygame modules + pygame.init() + + # Create a window and set its title + window = pygame.display.set_mode((window_height, window_width)) + pygame.display.set_caption("Conway's Game of Life") + + cell_height = window_height / grid.rows + cell_width = window_width / grid.cols + border_size = 1 + cell_fill_color = pygame.Color(cell_color) + background_fill_color = pygame.Color(background_color) + + running = True + while running: + # Poll for events + event = pygame.event.poll() + if event.type == pygame.QUIT: + # Quit if the window is closed + running = False + elif event.type == pygame.KEYDOWN: + # Also quit if the user presses or 'q' + if event.key == pygame.K_ESCAPE or event.key == pygame.K_q: + running = False + + # Clear the window by painting with the background color + window.fill(background_fill_color) + + # Draw each live cell in the grid + for row in range(grid.rows): + for col in range(grid.cols): + if grid[row, col]: + x = col * cell_width + border_size + y = row * cell_height + border_size + width = cell_width - border_size + height = cell_height - border_size + pygame.draw.rect( + window, cell_fill_color, (x, y, width, height) + ) + + # Update the display + pygame.display.flip() + + # Pause to let the user appreciate the scene + time.sleep(pause) + + # Next generation + grid = grid.evolve() + + # Shut down pygame cleanly + pygame.quit() + + +def main(): + start = Grid[128, 128].random() + run_display(start, window_height=600, window_width=600) diff --git a/examples/life/mojoproject.toml b/examples/life/mojoproject.toml new file mode 100644 index 0000000000..2d38853796 --- /dev/null +++ b/examples/life/mojoproject.toml @@ -0,0 +1,19 @@ +[project] +authors = ["Modular "] +channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] +description = "Introduction to Mojo Tutorial: Conway's Game of Life" +name = "life" +platforms = ["osx-arm64", "linux-64", "linux-aarch64"] +version = "0.1.0" + +[dependencies] +max = "*" +python = "3.12.*" +pygame = ">=2.6.1,<3" + +[tasks] +lifev1 = "mojo run lifev1.mojo" +lifev2 = "mojo run lifev2.mojo" +main = "mojo run benchmark.mojo" +benchmark = "mojo run benchmark.mojo" +tests = "mojo test -I . test" diff --git a/examples/life/test/test_gridv1.mojo b/examples/life/test/test_gridv1.mojo new file mode 100644 index 0000000000..bded4cc2f5 --- /dev/null +++ b/examples/life/test/test_gridv1.mojo @@ -0,0 +1,75 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from gridv1 import Grid +from testing import * + +var data4x4 = List( + List(0, 1, 1, 0), + List(1, 1, 0, 0), + List(0, 0, 1, 1), + List(1, 0, 0, 1), +) +var str4x4 = " ** \n** \n **\n* *" + + +def test_gridv1_init(): + grid = Grid(4, 4, data4x4) + assert_equal(4, grid.rows) + assert_equal(4, grid.cols) + for row in range(4): + assert_equal(data4x4[row], grid.data[row]) + + +def test_gridv1_index(): + grid = Grid(4, 4, data4x4) + for row in range(4): + for col in range(4): + assert_equal(data4x4[row][col], grid[row, col]) + grid[row, col] = 1 + assert_equal(1, grid[row, col]) + grid[row, col] = 0 + assert_equal(0, grid[row, col]) + + +def test_gridv1_str(): + grid = Grid(4, 4, data4x4) + grid_str = str(grid) + assert_equal(str4x4, grid_str) + + +def test_gridv1_evolve(): + data_gen2 = List( + List(0, 0, 1, 0), + List(1, 0, 0, 0), + List(0, 0, 1, 0), + List(1, 0, 0, 0), + ) + data_gen3 = List( + List(0, 1, 0, 1), + List(0, 1, 0, 1), + List(0, 1, 0, 1), + List(0, 1, 0, 1), + ) + + grid_gen1 = Grid(4, 4, data4x4) + + grid_gen2 = grid_gen1.evolve() + for row in range(4): + for col in range(4): + assert_equal(data_gen2[row][col], grid_gen2[row, col]) + + grid_gen3 = grid_gen2.evolve() + for row in range(4): + for col in range(4): + assert_equal(data_gen3[row][col], grid_gen3[row, col]) diff --git a/examples/life/test/test_gridv2.mojo b/examples/life/test/test_gridv2.mojo new file mode 100644 index 0000000000..1987272743 --- /dev/null +++ b/examples/life/test/test_gridv2.mojo @@ -0,0 +1,84 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +from gridv2 import Grid +from testing import * + +var data4x4 = List( + List(0, 1, 1, 0), + List(1, 1, 0, 0), + List(0, 0, 1, 1), + List(1, 0, 0, 1), +) +var str4x4 = " ** \n** \n **\n* *" + + +def grid4x4() -> Grid[4, 4]: + grid = Grid[4, 4]() + for row in range(4): + for col in range(4): + grid[row, col] = data4x4[row][col] + return grid + + +def test_gridv2_init(): + grid = Grid[4, 4]() + assert_equal(4, grid.rows) + assert_equal(4, grid.cols) + for row in range(4): + for col in range(4): + assert_equal(0, grid[row, col]) + + +def test_gridv2_index(): + grid = grid4x4() + for row in range(4): + for col in range(4): + assert_equal(data4x4[row][col], grid[row, col]) + grid[row, col] = 1 + assert_equal(1, grid[row, col]) + grid[row, col] = 0 + assert_equal(0, grid[row, col]) + + +def test_gridv2_str(): + grid = grid4x4() + grid_str = str(grid) + assert_equal(str4x4, grid_str) + + +def test_gridv2_evolve(): + data_gen2 = List( + List(0, 0, 1, 0), + List(1, 0, 0, 0), + List(0, 0, 1, 0), + List(1, 0, 0, 0), + ) + data_gen3 = List( + List(0, 1, 0, 1), + List(0, 1, 0, 1), + List(0, 1, 0, 1), + List(0, 1, 0, 1), + ) + + grid_gen1 = grid4x4() + + grid_gen2 = grid_gen1.evolve() + for row in range(4): + for col in range(4): + assert_equal(data_gen2[row][col], grid_gen2[row, col]) + + grid_gen3 = grid_gen2.evolve() + for row in range(4): + for col in range(4): + assert_equal(data_gen3[row][col], grid_gen3[row, col]) From 46955d3ac1298cb0779b723635f10c99b97d6079 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 25 Nov 2024 14:41:06 -0800 Subject: [PATCH 1922/2019] Convert Mojo docs from notebooks to MDX This PR is purely a file format change; there are no copy changes. We started writing the docs as notebooks because it was convenient to write code snippets that we could execute in situ and print outputs for the docs. We also thought this would allow us to test all the code in CI, but that has proven much more difficult than we had hoped. Although it's still nice to run code in situ, this does not outweigh the problems. The primary problems are: Editing the docs is slightly more cumbersome compared to markdown because it's done in a GUI, and GitHub reviews are very challenging because we can't comment on the rendered view (you must instead open the JSON source and make comments there, which is thoroughly confusing). Switching to markdown should make editing and reviewing documents much faster. For code testing, we will rely more on GitHub code examples that _can_ be tested in CI (and copy them into the markdown). We're using `.mdx` instead of `.md` because MDX is more reliable and extensible with Docusaurus. The CommonMark processor for `.md` files has some bugs, such as an inability to faithfully parse some types of HTML inside markdown. MODULAR_ORIG_COMMIT_REV_ID: 595d2f763197aa502263eb76ce388b0781738366 --- docs/manual/basics.ipynb | 741 ------- docs/manual/basics.mdx | 406 ++++ docs/manual/control-flow.ipynb | 887 -------- docs/manual/control-flow.mdx | 500 +++++ docs/manual/decorators/always-inline.ipynb | 102 - docs/manual/decorators/always-inline.md | 46 + docs/manual/decorators/copy-capture.ipynb | 64 - docs/manual/decorators/copy-capture.md | 27 + docs/manual/decorators/index.mdx | 14 +- .../manual/decorators/nonmaterializable.ipynb | 112 - docs/manual/decorators/nonmaterializable.md | 59 + docs/manual/decorators/parameter.ipynb | 195 -- docs/manual/decorators/parameter.md | 115 + .../manual/decorators/register-passable.ipynb | 205 -- docs/manual/decorators/register-passable.md | 105 + docs/manual/decorators/staticmethod.ipynb | 85 - docs/manual/decorators/staticmethod.md | 39 + docs/manual/decorators/value.ipynb | 111 - docs/manual/decorators/value.md | 49 + docs/manual/functions.ipynb | 855 -------- docs/manual/functions.mdx | 598 ++++++ docs/manual/lifecycle/death.ipynb | 548 ----- docs/manual/lifecycle/death.mdx | 411 ++++ docs/manual/lifecycle/index.ipynb | 128 -- docs/manual/lifecycle/index.mdx | 86 + docs/manual/lifecycle/life.ipynb | 926 -------- docs/manual/lifecycle/life.mdx | 691 ++++++ docs/manual/parameters/index.ipynb | 1908 ----------------- docs/manual/parameters/index.mdx | 1270 +++++++++++ docs/manual/pointers.ipynb | 751 ------- docs/manual/pointers.mdx | 530 +++++ docs/manual/python/index.ipynb | 297 --- docs/manual/python/index.mdx | 236 ++ docs/manual/python/types.ipynb | 292 --- docs/manual/python/types.mdx | 172 ++ docs/manual/structs.ipynb | 485 ----- docs/manual/structs.mdx | 317 +++ docs/manual/traits.ipynb | 750 ------- docs/manual/traits.mdx | 515 +++++ docs/manual/types.ipynb | 1124 ---------- docs/manual/types.mdx | 763 +++++++ docs/manual/values/index.ipynb | 158 -- docs/manual/values/index.mdx | 111 + docs/manual/values/lifetimes.ipynb | 595 ----- docs/manual/values/lifetimes.mdx | 434 ++++ docs/manual/values/ownership.ipynb | 653 ------ docs/manual/values/ownership.mdx | 465 ++++ docs/manual/values/value-semantics.ipynb | 431 ---- docs/manual/values/value-semantics.mdx | 270 +++ docs/manual/variables.ipynb | 493 ----- docs/manual/variables.mdx | 299 +++ docs/tools/debugging.ipynb | 637 ------ docs/tools/debugging.mdx | 543 +++++ docs/tools/testing.ipynb | 792 ------- docs/tools/testing.mdx | 648 ++++++ 55 files changed, 9712 insertions(+), 14332 deletions(-) delete mode 100644 docs/manual/basics.ipynb create mode 100644 docs/manual/basics.mdx delete mode 100644 docs/manual/control-flow.ipynb create mode 100644 docs/manual/control-flow.mdx delete mode 100644 docs/manual/decorators/always-inline.ipynb create mode 100644 docs/manual/decorators/always-inline.md delete mode 100644 docs/manual/decorators/copy-capture.ipynb create mode 100644 docs/manual/decorators/copy-capture.md delete mode 100644 docs/manual/decorators/nonmaterializable.ipynb create mode 100644 docs/manual/decorators/nonmaterializable.md delete mode 100644 docs/manual/decorators/parameter.ipynb create mode 100644 docs/manual/decorators/parameter.md delete mode 100644 docs/manual/decorators/register-passable.ipynb create mode 100644 docs/manual/decorators/register-passable.md delete mode 100644 docs/manual/decorators/staticmethod.ipynb create mode 100644 docs/manual/decorators/staticmethod.md delete mode 100644 docs/manual/decorators/value.ipynb create mode 100644 docs/manual/decorators/value.md delete mode 100755 docs/manual/functions.ipynb create mode 100644 docs/manual/functions.mdx delete mode 100644 docs/manual/lifecycle/death.ipynb create mode 100644 docs/manual/lifecycle/death.mdx delete mode 100644 docs/manual/lifecycle/index.ipynb create mode 100644 docs/manual/lifecycle/index.mdx delete mode 100644 docs/manual/lifecycle/life.ipynb create mode 100644 docs/manual/lifecycle/life.mdx delete mode 100644 docs/manual/parameters/index.ipynb create mode 100644 docs/manual/parameters/index.mdx delete mode 100644 docs/manual/pointers.ipynb create mode 100644 docs/manual/pointers.mdx delete mode 100644 docs/manual/python/index.ipynb create mode 100644 docs/manual/python/index.mdx delete mode 100644 docs/manual/python/types.ipynb create mode 100644 docs/manual/python/types.mdx delete mode 100644 docs/manual/structs.ipynb create mode 100644 docs/manual/structs.mdx delete mode 100755 docs/manual/traits.ipynb create mode 100644 docs/manual/traits.mdx delete mode 100644 docs/manual/types.ipynb create mode 100644 docs/manual/types.mdx delete mode 100644 docs/manual/values/index.ipynb create mode 100644 docs/manual/values/index.mdx delete mode 100644 docs/manual/values/lifetimes.ipynb create mode 100644 docs/manual/values/lifetimes.mdx delete mode 100644 docs/manual/values/ownership.ipynb create mode 100644 docs/manual/values/ownership.mdx delete mode 100644 docs/manual/values/value-semantics.ipynb create mode 100644 docs/manual/values/value-semantics.mdx delete mode 100644 docs/manual/variables.ipynb create mode 100644 docs/manual/variables.mdx delete mode 100644 docs/tools/debugging.ipynb create mode 100644 docs/tools/debugging.mdx delete mode 100644 docs/tools/testing.ipynb create mode 100644 docs/tools/testing.mdx diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb deleted file mode 100644 index 05d218396d..0000000000 --- a/docs/manual/basics.ipynb +++ /dev/null @@ -1,741 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Introduction to Mojo\n", - "sidebar_position: 1\n", - "description: Introduction to Mojo's basic language features.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At this point, you should have already set up the Mojo\n", - "SDK and run [\"Hello\n", - "world\"](/mojo/manual/get-started). Now let's talk about how\n", - "to write Mojo code.\n", - "\n", - "If\n", - "you know Python, then a lot of Mojo code will look familiar. However, Mojo\n", - "is—first and foremost—designed for high-performance systems programming, with\n", - "features like strong type checking, memory safety, next-generation compiler\n", - "technologies, and more. As such, Mojo also has a lot in common with languages\n", - "like C++ and Rust.\n", - "\n", - "Yet, we've designed Mojo to be flexible, so you can incrementally adopt\n", - "systems-programming features like strong type checking as you see fit—Mojo does\n", - "not *require* strong type checking.\n", - "\n", - "On this page, we'll introduce the essential Mojo syntax, so you can start\n", - "coding quickly and understand other Mojo code you encounter. Subsequent\n", - "sections in the Mojo Manual dive deeper into these topics, and links are\n", - "provided below as appropriate.\n", - "\n", - "Let's get started! 🔥\n", - "\n", - ":::note\n", - "\n", - "Mojo is a young language and there are many [features still\n", - "missing](/mojo/roadmap). As such, Mojo is currently **not** meant for\n", - "beginners. Even this basics section assumes some programming experience.\n", - "However, throughout the Mojo Manual, we try not to assume experience with any\n", - "particular language.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo functions can be declared with either `fn` or `def`.\n", - "\n", - "The `fn` declaration enforces type-checking and memory-safe behaviors (Rust\n", - "style), while `def` allows no type declarations and dynamic behaviors (Python\n", - "style).\n", - "\n", - "For example, this `def` function doesn't require declaration of argument types\n", - "or the return type:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def greet(name):\n", - " return \"Hello, \" + name + \"!\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "While the same thing as an `fn` function requires that you specify the\n", - "argument type and the return type like this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn greet2(name: String) -> String:\n", - " return \"Hello, \" + name + \"!\"" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Both functions have the same result, but the `fn` function provides\n", - "compile-time checks to ensure the function receives and returns the correct\n", - "types. Whereas, the `def` function might fail at runtime if it receives the\n", - "wrong type.\n", - "\n", - "Currently, Mojo doesn't support top-level code in a `.mojo` (or `.🔥`) file, so\n", - "every program must include a function named `main()` as the entry point.\n", - "You can declare it with either `def` or `fn`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def main():\n", - " print(\"Hello, world!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "You don't need a `main()` function when coding in the\n", - "[REPL](/mojo/manual/get-started#2-run-code-in-the-repl) or in a\n", - "[Jupyter\n", - "notebook](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more details, see the page about\n", - "[functions](/mojo/manual/functions)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Value ownership and argument mutability" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you're wondering whether function arguments are passed by value or\n", - "passed by reference, the short answer is: `def` functions receive arguments\n", - "\"by value\" and `fn` functions receive arguments \"by immutable reference.\"\n", - "\n", - "The longer short answer is that Mojo allows you to specify for each argument\n", - "whether it should be passed by value (as `owned`), or whether it should be\n", - "passed by reference (as `borrowed` for an immutable reference, or as `inout`\n", - "for a mutable reference).\n", - "\n", - "This feature is entwined with Mojo's value ownership model, which protects you\n", - "from memory errors by ensuring that only one variable \"owns\" a value at any\n", - "given time (but allowing other variables to receive a reference to it).\n", - "Ownership then ensures that the value is destroyed when the lifetime of the\n", - "owner ends (and there are no outstanding references).\n", - "\n", - "But that's still a short answer, because going much further is a slippery slope\n", - "into complexity that is out of scope for this section. For the complete\n", - "answer, see the section about [value ownership](/mojo/manual/values/).\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Variables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can declare variables with the `var` keyword. Or, if your code is in a\n", - "`def` function, you can omit the `var` (in an `fn` function, you must include\n", - "the `var` keyword).\n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "def do_math(x):\n", - " var y = x + x\n", - " y = y * y\n", - " print(y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, you can also declare a variable type like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def add_one(x):\n", - " var y: Int = 1\n", - " print(x + y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Even in an `fn` function, declaring the variable type is optional\n", - "(only the argument and return types must be declared in `fn` functions).\n", - "\n", - "For more details, see the page about\n", - "[variables](/mojo/manual/variables)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Structs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can build high-level abstractions for types (or \"objects\") as a `struct`. \n", - "\n", - "A `struct` in Mojo is similar to a `class` in Python: they both support\n", - "methods, fields, operator overloading, decorators for metaprogramming, and so\n", - "on. However, Mojo structs are completely static—they are bound at compile-time,\n", - "so they do not allow dynamic dispatch or any runtime changes to the structure.\n", - "(Mojo will also support Python-style classes in the future.)\n", - "\n", - "For example, here's a basic struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPair:\n", - " var first: Int\n", - " var second: Int\n", - "\n", - " fn __init__(out self, first: Int, second: Int):\n", - " self.first = first\n", - " self.second = second\n", - "\n", - " fn dump(self):\n", - " print(self.first, self.second)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how you can use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "fn use_mypair():\n", - " var mine = MyPair(2, 4)\n", - " mine.dump()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more details, see the page about\n", - "[structs](/mojo/manual/structs)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Traits\n", - "\n", - "A trait is like a template of characteristics for a struct. If you want to\n", - "create a struct with the characteristics defined in a trait, you must implement\n", - "each characteristic (such as each method). Each characteristic in a trait is a\n", - "\"requirement\" for the struct, and when your struct implements each requirement,\n", - "it's said to \"conform\" to the trait.\n", - "\n", - "Currently, the only characteristics that traits can define are method signatures. Also, traits\n", - "currently cannot implement default behaviors for methods.\n", - "\n", - "Using traits allows you to write generic functions that can accept any type\n", - "that conforms to a trait, rather than accept only specific types.\n", - "\n", - "For example, here's how you can create a trait (notice the function is not\n", - "implemented):" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "trait SomeTrait:\n", - " fn required_method(self, x: Int): ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how to create a struct that conforms to the trait:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct SomeStruct(SomeTrait):\n", - " fn required_method(self, x: Int):\n", - " print(\"hello traits\", x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then, here's a function that uses the trait as an argument type (instead of the\n", - "struct type):" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "fn fun_with_traits[T: SomeTrait](x: T):\n", - " x.required_method(42)\n", - "\n", - "fn use_trait_function():\n", - " var thing = SomeStruct()\n", - " fun_with_traits(thing)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "You're probably wondering about the square brackets on `fun_with_traits()`.\n", - "These aren't function _arguments_ (which go in parentheses); these are function\n", - "_parameters_, which we'll explain next.\n", - "\n", - ":::\n", - "\n", - "Without traits, the `x` argument in `fun_with_traits()` would have to declare a\n", - "specific type that implements `required_method()`, such as `SomeStruct`\n", - "(but then the function would accept only that type). With traits, the function\n", - "can accept any type for `x` as long as it conforms to (it \"implements\")\n", - "`SomeTrait`. Thus, `fun_with_traits()` is known as a \"generic function\" because\n", - "it accepts a _generalized_ type instead of a specific type.\n", - "\n", - "For more details, see the page about [traits](/mojo/manual/traits)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In Mojo, a parameter is a compile-time variable that becomes a runtime\n", - "constant, and it's declared in square brackets on a function or struct.\n", - "Parameters allow for compile-time metaprogramming, which means you can generate\n", - "or modify code at compile time.\n", - "\n", - "Many other languages use \"parameter\" and \"argument\" interchangeably, so be\n", - "aware that when we say things like \"parameter\" and \"parametric function,\" we're\n", - "talking about these compile-time parameters. Whereas, a function \"argument\" is\n", - "a runtime value that's declared in parentheses.\n", - "\n", - "Parameterization is a complex topic that's covered in much more detail in the\n", - "[Metaprogramming](/mojo/manual/parameters/) section, but we want to break the\n", - "ice just a little bit here. To get you started, let's look at a parametric\n", - "function:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "fn repeat[count: Int](msg: String):\n", - " for i in range(count):\n", - " print(msg)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function has one parameter of type `Int` and one argument of type\n", - "`String`. To call the function, you need to specify both the parameter and the\n", - "argument:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "fn call_repeat():\n", - " repeat[3](\"Hello\")\n", - " # Prints \"Hello\" 3 times" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By specifying `count` as a parameter, the Mojo compiler is able to optimize the\n", - "function because this value is guaranteed to not change at runtime. The\n", - "compiler effectively generates a unique version of the `repeat()` function that\n", - "repeats the message only 3 times. This makes the code more performant because\n", - "there's less to compute at runtime.\n", - "\n", - "Similarly, you can define a struct with parameters, which effectively allows\n", - "you to define variants of that type at compile-time, depending on the parameter\n", - "values.\n", - "\n", - "For more detail on parameters, see the section on\n", - "[Metaprogramming](/mojo/manual/parameters/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Blocks and statements" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Code blocks such as functions, conditions, and loops are defined\n", - "with a colon followed by indented lines. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def loop():\n", - " for x in range(5):\n", - " if x % 2 == 0:\n", - " print(x)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can use any number of spaces or tabs for your indentation (we prefer 4\n", - "spaces).\n", - "\n", - "All code statements in Mojo end with a newline. However, statements can span\n", - "multiple lines if you indent the following lines. For example, this long string\n", - "spans two lines:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def print_line():\n", - " long_text = \"This is a long line of text that is a lot easier to read if\"\n", - " \" it is broken up across two lines instead of one long line.\"\n", - " print(long_text)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And you can chain function calls across lines:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def print_hello():\n", - " text = String(\",\")\n", - " .join(\"Hello\", \" world!\")\n", - " print(text)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Code comments\n", - "\n", - "You can create a one-line comment using the hash `#` symbol:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# This is a comment. The Mojo compiler ignores this line." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Comments may also follow some code:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "var message = \"Hello, World!\" # This is also a valid comment" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can instead write longer comments across many lines using triple quotes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "This is also a comment, but it's easier to write across\n", - "many lines, because each line doesn't need the # symbol.\n", - "\"\"\"" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Triple quotes is the preferred method of writing API documentation. For example:\n", - "\n", - "```mojo\n", - "fn print(x: String):\n", - " \"\"\"Prints a string.\n", - "\n", - " Args:\n", - " x: The string to print.\n", - " \"\"\"\n", - " ...\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Documenting your code with these kinds of comments (known as \"docstrings\")\n", - "is a topic we've yet to fully specify, but you can generate an API reference\n", - "from docstrings using the [`mojo doc` command](/mojo/cli/doc)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Python integration" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo does not yet adopt the full syntax of Python, but we've built a mechanism to import\n", - "Python modules as-is, so you can leverage existing Python code right away.\n", - "\n", - "For example, here's how you can import and use NumPy (you must have Python\n", - "`numpy` installed):" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from python import Python\n", - "\n", - "def main():\n", - " var np = Python.import_module(\"numpy\")\n", - " var ar = np.arange(15).reshape(3, 5)\n", - " print(ar)\n", - " print(ar.shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "**Note:** You must have the Python module (such as `numpy`) installed already.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more details, see the page about\n", - "[Python integration](/mojo/manual/python/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next steps" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Hopefully this page has given you enough information to start experimenting with\n", - "Mojo, but this is only touching the surface of what's available in Mojo.\n", - "\n", - "If you're in the mood to read more, continue through each page of this\n", - "Mojo Manual using the buttons at the bottom of each page—the next page from\n", - "here is [Functions](/mojo/manual/functions).\n", - "\n", - "Otherwise, here are some other resources to check out:\n", - "\n", - "- If you want to experiment with some code, clone [the Mojo\n", - "repo](https://github.com/modularml/mojo/) to try our code examples:\n", - "\n", - " ```sh\n", - " git clone https://github.com/modularml/mojo.git\n", - " ```\n", - "\n", - " In addition to several `.mojo` examples, the repo includes [Jupyter\n", - " notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme)\n", - " that teach advanced Mojo features.\n", - "\n", - "- To see all the available Mojo APIs, check out the [Mojo standard library\n", - " reference](/mojo/lib)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/basics.mdx b/docs/manual/basics.mdx new file mode 100644 index 0000000000..2253f8c7c9 --- /dev/null +++ b/docs/manual/basics.mdx @@ -0,0 +1,406 @@ +--- +title: Introduction to Mojo +sidebar_position: 1 +description: Introduction to Mojo's basic language features. +--- + +At this point, you should have already set up the Mojo +SDK and run ["Hello +world"](/mojo/manual/get-started). Now let's talk about how +to write Mojo code. + +If +you know Python, then a lot of Mojo code will look familiar. However, Mojo +is—first and foremost—designed for high-performance systems programming, with +features like strong type checking, memory safety, next-generation compiler +technologies, and more. As such, Mojo also has a lot in common with languages +like C++ and Rust. + +Yet, we've designed Mojo to be flexible, so you can incrementally adopt +systems-programming features like strong type checking as you see fit—Mojo does +not *require* strong type checking. + +On this page, we'll introduce the essential Mojo syntax, so you can start +coding quickly and understand other Mojo code you encounter. Subsequent +sections in the Mojo Manual dive deeper into these topics, and links are +provided below as appropriate. + +Let's get started! 🔥 + +:::note + +Mojo is a young language and there are many [features still +missing](/mojo/roadmap). As such, Mojo is currently **not** meant for +beginners. Even this basics section assumes some programming experience. +However, throughout the Mojo Manual, we try not to assume experience with any +particular language. + +::: + +## Functions + +Mojo functions can be declared with either `fn` or `def`. + +The `fn` declaration enforces type-checking and memory-safe behaviors (Rust +style), while `def` allows no type declarations and dynamic behaviors (Python +style). + +For example, this `def` function doesn't require declaration of argument types +or the return type: + +```mojo +def greet(name): + return "Hello, " + name + "!" +``` + +While the same thing as an `fn` function requires that you specify the +argument type and the return type like this: + +```mojo +fn greet2(name: String) -> String: + return "Hello, " + name + "!" +``` + +Both functions have the same result, but the `fn` function provides +compile-time checks to ensure the function receives and returns the correct +types. Whereas, the `def` function might fail at runtime if it receives the +wrong type. + +Currently, Mojo doesn't support top-level code in a `.mojo` (or `.🔥`) file, so +every program must include a function named `main()` as the entry point. +You can declare it with either `def` or `fn`: + +```mojo +def main(): + print("Hello, world!") +``` + +:::note + +You don't need a `main()` function when coding in the +[REPL](/mojo/manual/get-started#2-run-code-in-the-repl) or in a +[Jupyter +notebook](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme). + +::: + +For more details, see the page about +[functions](/mojo/manual/functions). + +### Value ownership and argument mutability + +If you're wondering whether function arguments are passed by value or +passed by reference, the short answer is: `def` functions receive arguments +"by value" and `fn` functions receive arguments "by immutable reference." + +The longer short answer is that Mojo allows you to specify for each argument +whether it should be passed by value (as `owned`), or whether it should be +passed by reference (as `borrowed` for an immutable reference, or as `inout` +for a mutable reference). + +This feature is entwined with Mojo's value ownership model, which protects you +from memory errors by ensuring that only one variable "owns" a value at any +given time (but allowing other variables to receive a reference to it). +Ownership then ensures that the value is destroyed when the lifetime of the +owner ends (and there are no outstanding references). + +But that's still a short answer, because going much further is a slippery slope +into complexity that is out of scope for this section. For the complete +answer, see the section about [value ownership](/mojo/manual/values/). + +## Variables + +You can declare variables with the `var` keyword. Or, if your code is in a +`def` function, you can omit the `var` (in an `fn` function, you must include +the `var` keyword). + +For example: + +```mojo +def do_math(x): + var y = x + x + y = y * y + print(y) +``` + +Optionally, you can also declare a variable type like this: + +```mojo +def add_one(x): + var y: Int = 1 + print(x + y) +``` + +Even in an `fn` function, declaring the variable type is optional +(only the argument and return types must be declared in `fn` functions). + +For more details, see the page about +[variables](/mojo/manual/variables). + +## Structs + +You can build high-level abstractions for types (or "objects") as a `struct`. + +A `struct` in Mojo is similar to a `class` in Python: they both support +methods, fields, operator overloading, decorators for metaprogramming, and so +on. However, Mojo structs are completely static—they are bound at compile-time, +so they do not allow dynamic dispatch or any runtime changes to the structure. +(Mojo will also support Python-style classes in the future.) + +For example, here's a basic struct: + +```mojo +struct MyPair: + var first: Int + var second: Int + + fn __init__(out self, first: Int, second: Int): + self.first = first + self.second = second + + fn dump(self): + print(self.first, self.second) +``` + +And here's how you can use it: + +```mojo +fn use_mypair(): + var mine = MyPair(2, 4) + mine.dump() +``` + +For more details, see the page about +[structs](/mojo/manual/structs). + +### Traits + +A trait is like a template of characteristics for a struct. If you want to +create a struct with the characteristics defined in a trait, you must implement +each characteristic (such as each method). Each characteristic in a trait is a +"requirement" for the struct, and when your struct implements each requirement, +it's said to "conform" to the trait. + +Currently, the only characteristics that traits can define are method signatures. Also, traits +currently cannot implement default behaviors for methods. + +Using traits allows you to write generic functions that can accept any type +that conforms to a trait, rather than accept only specific types. + +For example, here's how you can create a trait (notice the function is not +implemented): + +```mojo +trait SomeTrait: + fn required_method(self, x: Int): ... +``` + +And here's how to create a struct that conforms to the trait: + +```mojo +@value +struct SomeStruct(SomeTrait): + fn required_method(self, x: Int): + print("hello traits", x) +``` + +Then, here's a function that uses the trait as an argument type (instead of the +struct type): + +```mojo +fn fun_with_traits[T: SomeTrait](x: T): + x.required_method(42) + +fn use_trait_function(): + var thing = SomeStruct() + fun_with_traits(thing) +``` + +:::note + +You're probably wondering about the square brackets on `fun_with_traits()`. +These aren't function *arguments* (which go in parentheses); these are function +*parameters*, which we'll explain next. + +::: + +Without traits, the `x` argument in `fun_with_traits()` would have to declare a +specific type that implements `required_method()`, such as `SomeStruct` +(but then the function would accept only that type). With traits, the function +can accept any type for `x` as long as it conforms to (it "implements") +`SomeTrait`. Thus, `fun_with_traits()` is known as a "generic function" because +it accepts a *generalized* type instead of a specific type. + +For more details, see the page about [traits](/mojo/manual/traits). + +## Parameterization + +In Mojo, a parameter is a compile-time variable that becomes a runtime +constant, and it's declared in square brackets on a function or struct. +Parameters allow for compile-time metaprogramming, which means you can generate +or modify code at compile time. + +Many other languages use "parameter" and "argument" interchangeably, so be +aware that when we say things like "parameter" and "parametric function," we're +talking about these compile-time parameters. Whereas, a function "argument" is +a runtime value that's declared in parentheses. + +Parameterization is a complex topic that's covered in much more detail in the +[Metaprogramming](/mojo/manual/parameters/) section, but we want to break the +ice just a little bit here. To get you started, let's look at a parametric +function: + +```mojo +fn repeat[count: Int](msg: String): + for i in range(count): + print(msg) +``` + +This function has one parameter of type `Int` and one argument of type +`String`. To call the function, you need to specify both the parameter and the +argument: + +```mojo +fn call_repeat(): + repeat[3]("Hello") + # Prints "Hello" 3 times +``` + +By specifying `count` as a parameter, the Mojo compiler is able to optimize the +function because this value is guaranteed to not change at runtime. The +compiler effectively generates a unique version of the `repeat()` function that +repeats the message only 3 times. This makes the code more performant because +there's less to compute at runtime. + +Similarly, you can define a struct with parameters, which effectively allows +you to define variants of that type at compile-time, depending on the parameter +values. + +For more detail on parameters, see the section on +[Metaprogramming](/mojo/manual/parameters/). + +## Blocks and statements + +Code blocks such as functions, conditions, and loops are defined +with a colon followed by indented lines. For example: + +```mojo +def loop(): + for x in range(5): + if x % 2 == 0: + print(x) +``` + +You can use any number of spaces or tabs for your indentation (we prefer 4 +spaces). + +All code statements in Mojo end with a newline. However, statements can span +multiple lines if you indent the following lines. For example, this long string +spans two lines: + +```mojo +def print_line(): + long_text = "This is a long line of text that is a lot easier to read if" + " it is broken up across two lines instead of one long line." + print(long_text) +``` + +And you can chain function calls across lines: + +```mojo +def print_hello(): + text = String(",") + .join("Hello", " world!") + print(text) +``` + +## Code comments + +You can create a one-line comment using the hash `#` symbol: + +```mojo +# This is a comment. The Mojo compiler ignores this line. +``` + +Comments may also follow some code: + +```mojo +var message = "Hello, World!" # This is also a valid comment +``` + +You can instead write longer comments across many lines using triple quotes: + +```mojo +""" +This is also a comment, but it's easier to write across +many lines, because each line doesn't need the # symbol. +""" +``` + +Triple quotes is the preferred method of writing API documentation. For example: + +```mojo +fn print(x: String): + """Prints a string. + + Args: + x: The string to print. + """ + ... +``` + +Documenting your code with these kinds of comments (known as "docstrings") +is a topic we've yet to fully specify, but you can generate an API reference +from docstrings using the [`mojo doc` command](/mojo/cli/doc). + +## Python integration + +Mojo does not yet adopt the full syntax of Python, but we've built a mechanism to import +Python modules as-is, so you can leverage existing Python code right away. + +For example, here's how you can import and use NumPy (you must have Python +`numpy` installed): + +```mojo +from python import Python + +def main(): + var np = Python.import_module("numpy") + var ar = np.arange(15).reshape(3, 5) + print(ar) + print(ar.shape) +``` + +:::note + +**Note:** You must have the Python module (such as `numpy`) installed already. + +::: + +For more details, see the page about +[Python integration](/mojo/manual/python/). + +## Next steps + +Hopefully this page has given you enough information to start experimenting with +Mojo, but this is only touching the surface of what's available in Mojo. + +If you're in the mood to read more, continue through each page of this +Mojo Manual using the buttons at the bottom of each page—the next page from +here is [Functions](/mojo/manual/functions). + +Otherwise, here are some other resources to check out: + +* If you want to experiment with some code, clone [the Mojo + repo](https://github.com/modularml/mojo/) to try our code examples: + + ```sh + git clone https://github.com/modularml/mojo.git + ``` + + In addition to several `.mojo` examples, the repo includes [Jupyter + notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) + that teach advanced Mojo features. + +* To see all the available Mojo APIs, check out the [Mojo standard library + reference](/mojo/lib). diff --git a/docs/manual/control-flow.ipynb b/docs/manual/control-flow.ipynb deleted file mode 100644 index 8ee4dab294..0000000000 --- a/docs/manual/control-flow.ipynb +++ /dev/null @@ -1,887 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Control flow\n", - "description: Mojo control flow statements.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo includes several traditional control flow structures for conditional and\n", - "repeated execution of code blocks.\n", - "\n", - "## The `if` statement\n", - "\n", - "Mojo supports the `if` statement for conditional code execution. With it you can\n", - "conditionally execute an indented code block if a given\n", - "[boolean](/mojo/manual/types#booleans) expression evaluates to `True`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "It is warm.\n", - "The temperature is 77.0 Fahrenheit.\n" - ] - } - ], - "source": [ - "temp_celsius = 25\n", - "if temp_celsius > 20:\n", - " print(\"It is warm.\")\n", - " print(\"The temperature is\", temp_celsius * 9 / 5 + 32, \"Fahrenheit.\" )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can write the entire `if` statement as a single line if all you need to\n", - "execute conditionally is a single, short statement." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "It is warm.\n" - ] - } - ], - "source": [ - "temp_celsius = 22\n", - "if temp_celsius < 15: print(\"It is cool.\") # Skipped because condition is False\n", - "if temp_celsius > 20: print(\"It is warm.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, an `if` statement can include any number of additional `elif`\n", - "clauses, each specifying a boolean condition and associated code block to\n", - "execute if `True`. The conditions are tested in the order given. When a\n", - "condition evaluates to `True`, the associated code block is executed and no\n", - "further conditions are tested.\n", - "\n", - "Additionally, an `if` statement can include an optional `else` clause providing\n", - "a code block to execute if all conditions evaluate to `False`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "It is warm.\n" - ] - } - ], - "source": [ - "temp_celsius = 25\n", - "if temp_celsius <= 0:\n", - " print(\"It is freezing.\")\n", - "elif temp_celsius < 20:\n", - " print(\"It is cool.\")\n", - "elif temp_celsius < 30:\n", - " print(\"It is warm.\")\n", - "else:\n", - " print(\"It is hot.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note TODO\n", - "\n", - "Mojo currently does not support the equivalent of a Python `match` or C `switch`\n", - "statement for pattern matching and conditional execution.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Short-circuit evaluation\n", - "\n", - "Mojo follows [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation)\n", - "semantics for boolean operators. If the first argument to an `or` operator\n", - "evaluates to `True`, the second argument is not evaluated.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Short-circuit \"or\" evaluation\n", - "Executing true_func\n", - "True result\n" - ] - } - ], - "source": [ - "def true_func() -> Bool:\n", - " print(\"Executing true_func\")\n", - " return True\n", - "\n", - "def false_func() -> Bool:\n", - " print(\"Executing false_func\")\n", - " return False\n", - "\n", - "print('Short-circuit \"or\" evaluation')\n", - "if true_func() or false_func():\n", - " print(\"True result\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If the first argument to an `and` operator evaluates to `False`, the second\n", - "argument is not evaluated." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Short-circuit \"and\" evaluation\n", - "Executing false_func\n" - ] - } - ], - "source": [ - "print('Short-circuit \"and\" evaluation')\n", - "if false_func() and true_func():\n", - " print(\"True result\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conditional expressions\n", - "\n", - "Mojo also supports conditional expressions (or what is sometimes called a\n", - "[_ternary conditional operator_](https://en.wikipedia.org/wiki/Ternary_conditional_operator))\n", - "using the syntaxtrue_result if boolean_expression else false_result, just as\n", - "in Python. This is most often used as a concise way to assign one of two\n", - "different values to a variable, based on a boolean condition." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The forecast for today is cool\n" - ] - } - ], - "source": [ - "temp_celsius = 15\n", - "forecast = \"warm\" if temp_celsius > 20 else \"cool\"\n", - "print(\"The forecast for today is\", forecast)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The alternative, written as a multi-line `if` statement, is more verbose." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The forecast for today is cool\n" - ] - } - ], - "source": [ - "if temp_celsius > 20:\n", - " forecast = \"warm\"\n", - "else:\n", - " forecast = \"cool\"\n", - "print(\"The forecast for today is\", forecast)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `while` statement\n", - "\n", - "The `while` loop repeatedly executes a code block while a given boolean\n", - "expression evaluates to `True`. For example, the following loop prints values\n", - "from the Fibonacci series that are less than 50." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 1, 2, 3, 5, 8, 13, 21, 34" - ] - } - ], - "source": [ - "fib_prev = 0\n", - "fib_curr = 1\n", - "\n", - "print(fib_prev, end=\"\")\n", - "while fib_curr < 50:\n", - " print(\",\", fib_curr, end=\"\")\n", - " fib_prev, fib_curr = fib_curr, fib_prev + fib_curr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `continue` statement skips execution of the rest of the code block and\n", - "resumes with the loop test expression." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, 4, 5, " - ] - } - ], - "source": [ - "n = 0\n", - "while n < 5:\n", - " n += 1\n", - " if n == 3:\n", - " continue\n", - " print(n, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `break` statement terminates execution of the loop." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, " - ] - } - ], - "source": [ - "n = 0\n", - "while n < 5:\n", - " n += 1\n", - " if n == 3:\n", - " break\n", - " print(n, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, a `while` loop can include an `else` clause. The body of the `else`\n", - "clause executes when the loop's boolean condition evaluates to `False`, even if\n", - "it occurs the first time tested." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loop completed\n" - ] - } - ], - "source": [ - "n = 5\n", - "\n", - "while n < 4:\n", - " print(n)\n", - " n += 1\n", - "else:\n", - " print(\"Loop completed\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "The `else` clause does _not_ execute if a `break` or `return` statement\n", - "exits the `while` loop.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n" - ] - } - ], - "source": [ - "n = 0\n", - "while n < 5:\n", - " n += 1\n", - " if n == 3:\n", - " break\n", - " print(n)\n", - "else:\n", - " print(\"Executing else clause\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `for` statement\n", - "\n", - "The `for` loop iterates over a sequence, executing a code block for each\n", - "element in the sequence.\n", - "The Mojo `for` loop can iterate over any type that implements an `__iter__()`\n", - "method that returns a type that defines `__next__()` and `__len__()` methods.\n", - "\n", - "### Iterating over Mojo collections\n", - "\n", - "All of the collection types in the [`collections`](/mojo/stdlib/collections)\n", - "module support `for` loop iteration. See the\n", - "[Collection types](/mojo/manual/types#collection-types) documentation for more\n", - "information on Mojo collection types.\n", - "\n", - ":::caution TODO\n", - "\n", - "Iterating over Mojo native collections currently assigns the loop index variable\n", - "a [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", - "item itself. You can access the item using the dereference operator, `[]`, as\n", - "shown in the examples below. This may change in a future version of Mojo.\n", - "\n", - ":::\n", - "\n", - "The following shows an example of iterating over a Mojo\n", - "[`List`](/mojo/stdlib/collections/list/List)." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "California\n", - "Hawaii\n", - "Oregon\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "states = List[String](\"California\", \"Hawaii\", \"Oregon\")\n", - "for state in states:\n", - " print(state[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The same technique works for iterating over a Mojo\n", - "[`Set`](/mojo/stdlib/collections/set/Set)." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "0\n" - ] - } - ], - "source": [ - "from collections import Set\n", - "\n", - "values = Set[Int](42, 0)\n", - "for item in values:\n", - " print(item[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are two techniques for iterating over a Mojo\n", - "[`Dict`](/mojo/stdlib/collections/dict/Dict). The first is to iterate directly\n", - "using the `Dict`, which produces a sequence of the dictionary's keys." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sacramento, California\n", - "Honolulu, Hawaii\n", - "Salem, Oregon\n" - ] - } - ], - "source": [ - "from collections import Dict\n", - "\n", - "capitals = Dict[String, String]()\n", - "capitals[\"California\"] = \"Sacramento\"\n", - "capitals[\"Hawaii\"] = \"Honolulu\"\n", - "capitals[\"Oregon\"] = \"Salem\"\n", - "\n", - "for state in capitals:\n", - " print(capitals[state[]] + \", \" + state[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The second approach to iterating over a Mojo `Dict` is to invoke its\n", - "[`items()`](/mojo/stdlib/collections/dict/Dict#items) method, which produces a\n", - "sequence of [`DictEntry`](/mojo/stdlib/collections/dict/DictEntry) objects.\n", - "Within the loop body, you can then access the `key` and `value` fields of the\n", - "entry." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sacramento, California\n", - "Honolulu, Hawaii\n", - "Salem, Oregon\n" - ] - } - ], - "source": [ - "for item in capitals.items():\n", - " print(item[].value + \", \" + item[].key)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another type of iterable provided by the Mojo standard library is a _range_,\n", - "which is a sequence of integers generated by the\n", - "[`range()`](/mojo/stdlib/builtin/range/range) function. It differs from the\n", - "collection types shown above in that it's implemented as a\n", - "[generator](https://en.wikipedia.org/wiki/Generator_(computer_programming)),\n", - "producing each value as needed rather than materializing the entire sequence\n", - "in memory. Additionally, each value assigned to the loop index variable is\n", - "simply the `Int` value rather than a `Reference` to the value, so you should\n", - "not use the dereference operator on it within the loop. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, 3, 4, " - ] - } - ], - "source": [ - "for i in range(5):\n", - " print(i, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `for` loop control statements\n", - "\n", - "A `continue` statement skips execution of the rest of the code block and\n", - "resumes the loop with the next element of the collection." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, 4, " - ] - } - ], - "source": [ - "for i in range(5):\n", - " if i == 3:\n", - " continue\n", - " print(i, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `break` statement terminates execution of the loop." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, " - ] - } - ], - "source": [ - "for i in range(5):\n", - " if i == 3:\n", - " break\n", - " print(i, end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, a `for` loop can include an `else` clause. The body of the `else`\n", - "clause executes after iterating over all of the elements in a collection." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0, 1, 2, 3, 4, \n", - "Finished executing 'for' loop\n" - ] - } - ], - "source": [ - "for i in range(5):\n", - " print(i, end=\", \")\n", - "else:\n", - " print(\"\\nFinished executing 'for' loop\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `else` clause executes even if the collection is empty." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finished executing 'for' loop\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "empty = List[Int]()\n", - "for i in empty:\n", - " print(i[])\n", - "else:\n", - " print(\"Finished executing 'for' loop\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "The `else` clause does _not_ execute if a `break` or `return` statement\n", - "terminates the `for` loop.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found a dog\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "animals = List[String](\"cat\", \"aardvark\", \"hippopotamus\", \"dog\")\n", - "for animal in animals:\n", - " if animal[] == \"dog\":\n", - " print(\"Found a dog\")\n", - " break\n", - "else:\n", - " print(\"No dog found\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Iterating over Python collections\n", - "\n", - "The Mojo `for` loop supports iterating over Python collection types. Each item\n", - "retrieved by the loop is a\n", - "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", - "the Python object. Refer to the [Python types](/mojo/manual/python/types)\n", - "documentation for more information on manipulating Python objects from Mojo.\n", - "\n", - "The following is a simple example of iterating over a mixed-type Python list." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "cat\n", - "3.14159\n" - ] - } - ], - "source": [ - "from python import Python\n", - "\n", - "# Create a mixed-type Python list\n", - "py_list = Python.evaluate(\"[42, 'cat', 3.14159]\")\n", - "for py_obj in py_list: # Each element is of type \"PythonObject\"\n", - " print(py_obj)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note TODO\n", - "\n", - "Iterating over a Mojo collection currently assigns the loop index variable a\n", - "`Reference` to each element, which then requires you to use the dereference\n", - "operator within the loop body. In contrast, iterating over a Python collection\n", - "assigns a `PythonObject` wrapper for the element, which does _not_ require you\n", - "to use the dereference operator.\n", - "\n", - ":::\n", - "\n", - "\n", - "There are two techniques for iterating over a Python dictionary. The first is to\n", - "iterate directly using the dictionary, which produces a sequence of its keys." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "a 1\n", - "b 2.71828\n", - "c sushi\n" - ] - } - ], - "source": [ - "from python import Python\n", - "\n", - "# Create a mixed-type Python dictionary\n", - "py_dict = Python.evaluate(\"{'a': 1, 'b': 2.71828, 'c': 'sushi'}\")\n", - "for py_key in py_dict: # Each element is of type \"PythonObject\"\n", - " print(py_key, py_dict[py_key])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The second approach to iterating over a Python dictionary is to invoke its\n", - "`items()` method, which produces a sequence of 2-tuple objects.\n", - "Within the loop body, you can then access the key and value by index." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "a 1\n", - "b 2.71828\n", - "c sushi\n" - ] - } - ], - "source": [ - "for py_tuple in py_dict.items(): # Each element is of type \"PythonObject\"\n", - " print(py_tuple[0], py_tuple[1])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/control-flow.mdx b/docs/manual/control-flow.mdx new file mode 100644 index 0000000000..330abc2ed0 --- /dev/null +++ b/docs/manual/control-flow.mdx @@ -0,0 +1,500 @@ +--- +title: Control flow +description: Mojo control flow statements. +--- + +Mojo includes several traditional control flow structures for conditional and +repeated execution of code blocks. + +## The `if` statement + +Mojo supports the `if` statement for conditional code execution. With it you can +conditionally execute an indented code block if a given +[boolean](/mojo/manual/types#booleans) expression evaluates to `True`. + +```mojo +temp_celsius = 25 +if temp_celsius > 20: + print("It is warm.") + print("The temperature is", temp_celsius * 9 / 5 + 32, "Fahrenheit." ) +``` + +```output +It is warm. +The temperature is 77.0 Fahrenheit. +``` + +You can write the entire `if` statement as a single line if all you need to +execute conditionally is a single, short statement. + +```mojo +temp_celsius = 22 +if temp_celsius < 15: print("It is cool.") # Skipped because condition is False +if temp_celsius > 20: print("It is warm.") +``` + +```output +It is warm. +``` + +Optionally, an `if` statement can include any number of additional `elif` +clauses, each specifying a boolean condition and associated code block to +execute if `True`. The conditions are tested in the order given. When a +condition evaluates to `True`, the associated code block is executed and no +further conditions are tested. + +Additionally, an `if` statement can include an optional `else` clause providing +a code block to execute if all conditions evaluate to `False`. + +```mojo +temp_celsius = 25 +if temp_celsius <= 0: + print("It is freezing.") +elif temp_celsius < 20: + print("It is cool.") +elif temp_celsius < 30: + print("It is warm.") +else: + print("It is hot.") +``` + +```output +It is warm. +``` + +:::note TODO + +Mojo currently does not support the equivalent of a Python `match` or C `switch` +statement for pattern matching and conditional execution. + +::: + +### Short-circuit evaluation + +Mojo follows [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation) +semantics for boolean operators. If the first argument to an `or` operator +evaluates to `True`, the second argument is not evaluated. + +```mojo +def true_func() -> Bool: + print("Executing true_func") + return True + +def false_func() -> Bool: + print("Executing false_func") + return False + +print('Short-circuit "or" evaluation') +if true_func() or false_func(): + print("True result") +``` + +```output +Short-circuit "or" evaluation +Executing true_func +True result +``` + +If the first argument to an `and` operator evaluates to `False`, the second +argument is not evaluated. + +```mojo +print('Short-circuit "and" evaluation') +if false_func() and true_func(): + print("True result") +``` + +```output +Short-circuit "and" evaluation +Executing false_func +``` + +### Conditional expressions + +Mojo also supports conditional expressions (or what is sometimes called a +[*ternary conditional operator*](https://en.wikipedia.org/wiki/Ternary_conditional_operator)) +using the syntaxtrue_result if boolean_expression else false_result, just as +in Python. This is most often used as a concise way to assign one of two +different values to a variable, based on a boolean condition. + +```mojo +temp_celsius = 15 +forecast = "warm" if temp_celsius > 20 else "cool" +print("The forecast for today is", forecast) +``` + +```output +The forecast for today is cool +``` + +The alternative, written as a multi-line `if` statement, is more verbose. + +```mojo +if temp_celsius > 20: + forecast = "warm" +else: + forecast = "cool" +print("The forecast for today is", forecast) +``` + +```output +The forecast for today is cool +``` + +## The `while` statement + +The `while` loop repeatedly executes a code block while a given boolean +expression evaluates to `True`. For example, the following loop prints values +from the Fibonacci series that are less than 50. + +```mojo +fib_prev = 0 +fib_curr = 1 + +print(fib_prev, end="") +while fib_curr < 50: + print(",", fib_curr, end="") + fib_prev, fib_curr = fib_curr, fib_prev + fib_curr +``` + +```output +0, 1, 1, 2, 3, 5, 8, 13, 21, 34 +``` + +A `continue` statement skips execution of the rest of the code block and +resumes with the loop test expression. + +```mojo +n = 0 +while n < 5: + n += 1 + if n == 3: + continue + print(n, end=", ") +``` + +```output +1, 2, 4, 5, +``` + +A `break` statement terminates execution of the loop. + +```mojo +n = 0 +while n < 5: + n += 1 + if n == 3: + break + print(n, end=", ") +``` + +```output +1, 2, +``` + +Optionally, a `while` loop can include an `else` clause. The body of the `else` +clause executes when the loop's boolean condition evaluates to `False`, even if +it occurs the first time tested. + +```mojo +n = 5 + +while n < 4: + print(n) + n += 1 +else: + print("Loop completed") + +``` + +```output +Loop completed +``` + +:::note + +The `else` clause does *not* execute if a `break` or `return` statement +exits the `while` loop. + +::: + +```mojo +n = 0 +while n < 5: + n += 1 + if n == 3: + break + print(n) +else: + print("Executing else clause") +``` + +```output +1 +2 +``` + +## The `for` statement + +The `for` loop iterates over a sequence, executing a code block for each +element in the sequence. +The Mojo `for` loop can iterate over any type that implements an `__iter__()` +method that returns a type that defines `__next__()` and `__len__()` methods. + +### Iterating over Mojo collections + +All of the collection types in the [`collections`](/mojo/stdlib/collections) +module support `for` loop iteration. See the +[Collection types](/mojo/manual/types#collection-types) documentation for more +information on Mojo collection types. + +:::caution TODO + +Iterating over Mojo native collections currently assigns the loop index variable +a [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the +item itself. You can access the item using the dereference operator, `[]`, as +shown in the examples below. This may change in a future version of Mojo. + +::: + +The following shows an example of iterating over a Mojo +[`List`](/mojo/stdlib/collections/list/List). + +```mojo +from collections import List + +states = List[String]("California", "Hawaii", "Oregon") +for state in states: + print(state[]) +``` + +```output +California +Hawaii +Oregon +``` + +The same technique works for iterating over a Mojo +[`Set`](/mojo/stdlib/collections/set/Set). + +```mojo +from collections import Set + +values = Set[Int](42, 0) +for item in values: + print(item[]) +``` + +```output +42 +0 +``` + +There are two techniques for iterating over a Mojo +[`Dict`](/mojo/stdlib/collections/dict/Dict). The first is to iterate directly +using the `Dict`, which produces a sequence of the dictionary's keys. + +```mojo +from collections import Dict + +capitals = Dict[String, String]() +capitals["California"] = "Sacramento" +capitals["Hawaii"] = "Honolulu" +capitals["Oregon"] = "Salem" + +for state in capitals: + print(capitals[state[]] + ", " + state[]) +``` + +```output +Sacramento, California +Honolulu, Hawaii +Salem, Oregon +``` + +The second approach to iterating over a Mojo `Dict` is to invoke its +[`items()`](/mojo/stdlib/collections/dict/Dict#items) method, which produces a +sequence of [`DictEntry`](/mojo/stdlib/collections/dict/DictEntry) objects. +Within the loop body, you can then access the `key` and `value` fields of the +entry. + +```mojo +for item in capitals.items(): + print(item[].value + ", " + item[].key) +``` + +```output +Sacramento, California +Honolulu, Hawaii +Salem, Oregon +``` + +Another type of iterable provided by the Mojo standard library is a *range*, +which is a sequence of integers generated by the +[`range()`](/mojo/stdlib/builtin/range/range) function. It differs from the +collection types shown above in that it's implemented as a +[generator](https://en.wikipedia.org/wiki/Generator_\(computer_programming\)), +producing each value as needed rather than materializing the entire sequence +in memory. Additionally, each value assigned to the loop index variable is +simply the `Int` value rather than a `Reference` to the value, so you should +not use the dereference operator on it within the loop. For example: + +```mojo +for i in range(5): + print(i, end=", ") +``` + +```output +0, 1, 2, 3, 4, +``` + +### `for` loop control statements + +A `continue` statement skips execution of the rest of the code block and +resumes the loop with the next element of the collection. + +```mojo +for i in range(5): + if i == 3: + continue + print(i, end=", ") +``` + +```output +0, 1, 2, 4, +``` + +A `break` statement terminates execution of the loop. + +```mojo +for i in range(5): + if i == 3: + break + print(i, end=", ") +``` + +```output +0, 1, 2, +``` + +Optionally, a `for` loop can include an `else` clause. The body of the `else` +clause executes after iterating over all of the elements in a collection. + +```mojo +for i in range(5): + print(i, end=", ") +else: + print("\nFinished executing 'for' loop") +``` + +```output +0, 1, 2, 3, 4, +Finished executing 'for' loop +``` + +The `else` clause executes even if the collection is empty. + +```mojo +from collections import List + +empty = List[Int]() +for i in empty: + print(i[]) +else: + print("Finished executing 'for' loop") +``` + +```output +Finished executing 'for' loop +``` + +:::note + +The `else` clause does *not* execute if a `break` or `return` statement +terminates the `for` loop. + +::: + +```mojo +from collections import List + +animals = List[String]("cat", "aardvark", "hippopotamus", "dog") +for animal in animals: + if animal[] == "dog": + print("Found a dog") + break +else: + print("No dog found") +``` + +```output +Found a dog +``` + +### Iterating over Python collections + +The Mojo `for` loop supports iterating over Python collection types. Each item +retrieved by the loop is a +[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around +the Python object. Refer to the [Python types](/mojo/manual/python/types) +documentation for more information on manipulating Python objects from Mojo. + +The following is a simple example of iterating over a mixed-type Python list. + +```mojo +from python import Python + +# Create a mixed-type Python list +py_list = Python.evaluate("[42, 'cat', 3.14159]") +for py_obj in py_list: # Each element is of type "PythonObject" + print(py_obj) +``` + +```output +42 +cat +3.14159 +``` + +:::note TODO + +Iterating over a Mojo collection currently assigns the loop index variable a +`Reference` to each element, which then requires you to use the dereference +operator within the loop body. In contrast, iterating over a Python collection +assigns a `PythonObject` wrapper for the element, which does *not* require you +to use the dereference operator. + +::: + +There are two techniques for iterating over a Python dictionary. The first is to +iterate directly using the dictionary, which produces a sequence of its keys. + +```mojo +from python import Python + +# Create a mixed-type Python dictionary +py_dict = Python.evaluate("{'a': 1, 'b': 2.71828, 'c': 'sushi'}") +for py_key in py_dict: # Each element is of type "PythonObject" + print(py_key, py_dict[py_key]) +``` + +```output +a 1 +b 2.71828 +c sushi +``` + +The second approach to iterating over a Python dictionary is to invoke its +`items()` method, which produces a sequence of 2-tuple objects. +Within the loop body, you can then access the key and value by index. + +```mojo +for py_tuple in py_dict.items(): # Each element is of type "PythonObject" + print(py_tuple[0], py_tuple[1]) +``` + +```output +a 1 +b 2.71828 +c sushi +``` diff --git a/docs/manual/decorators/always-inline.ipynb b/docs/manual/decorators/always-inline.ipynb deleted file mode 100644 index f272691b9b..0000000000 --- a/docs/manual/decorators/always-inline.ipynb +++ /dev/null @@ -1,102 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@always_inline`'\n", - "description: Copies the body of a function directly into the body of the calling function.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@always_inline` decorator on any function to make the Mojo\n", - "compiler \"inline\" the body of the function (copy it) directly into the body of\n", - "the calling function.\n", - "\n", - "This eliminates potential performance costs associated with function calls\n", - "jumping to a new point in code. Normally, the compiler will do this\n", - "automatically where it can improve performance, but this decorator forces it to\n", - "do so. The downside is that it can increase the binary size by duplicating the\n", - "function at every call site.\n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@always_inline\n", - "fn add(a: Int, b: Int) -> Int:\n", - " return a + b\n", - "\n", - "print(add(1, 2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because `add()` is decorated with `@always_inline`, Mojo compiles this program\n", - "without adding the `add()` function to the call stack, and it instead performs\n", - "the addition directly at the `print()` call site, as if it were written like\n", - "this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(1 + 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `@always_inline(\"nodebug\")`\n", - "\n", - "You can also use the decorator with the `\"nodebug\"` argument, which has the\n", - "same effect to inline the function, but without debug information. This means\n", - "that you can't step into the function when debugging.\n", - "\n", - "This decorator is intended to be used on the lowest-level functions in a\n", - "library, which may wrap primitive functions, MLIR operations, or inline\n", - "assembly. Marking these functions as \"nodebug\" prevents users from accidentally\n", - "stepping into low-level non-Mojo code when debugging." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/always-inline.md b/docs/manual/decorators/always-inline.md new file mode 100644 index 0000000000..d84ae98b63 --- /dev/null +++ b/docs/manual/decorators/always-inline.md @@ -0,0 +1,46 @@ +--- +title: '@always_inline' +description: Copies the body of a function directly into the body of the calling function. +codeTitle: true + +--- + +You can add the `@always_inline` decorator on any function to make the Mojo +compiler "inline" the body of the function (copy it) directly into the body of +the calling function. + +This eliminates potential performance costs associated with function calls +jumping to a new point in code. Normally, the compiler will do this +automatically where it can improve performance, but this decorator forces it to +do so. The downside is that it can increase the binary size by duplicating the +function at every call site. + +For example: + +```mojo +@always_inline +fn add(a: Int, b: Int) -> Int: + return a + b + +print(add(1, 2)) +``` + +Because `add()` is decorated with `@always_inline`, Mojo compiles this program +without adding the `add()` function to the call stack, and it instead performs +the addition directly at the `print()` call site, as if it were written like +this: + +```mojo +print(1 + 2) +``` + +## `@always_inline("nodebug")` + +You can also use the decorator with the `"nodebug"` argument, which has the +same effect to inline the function, but without debug information. This means +that you can't step into the function when debugging. + +This decorator is intended to be used on the lowest-level functions in a +library, which may wrap primitive functions, MLIR operations, or inline +assembly. Marking these functions as "nodebug" prevents users from accidentally +stepping into low-level non-Mojo code when debugging. diff --git a/docs/manual/decorators/copy-capture.ipynb b/docs/manual/decorators/copy-capture.ipynb deleted file mode 100644 index e5e59dfe87..0000000000 --- a/docs/manual/decorators/copy-capture.ipynb +++ /dev/null @@ -1,64 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@__copy_capture`'\n", - "description: Captures register-passable typed values by copy.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `__copy_capture` decorator on a parametric closure to capture register-passable values by copy. This decorator causes a nested function to copy the value of the indicated variable into the closure object at the point of formation instead of capturing that variable by reference. This allows the closure to be passed as an escaping function, without lifetime concerns." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - " fn foo(x: Int):\n", - " var z = x\n", - "\n", - " @__copy_capture(z)\n", - " @parameter\n", - " fn formatter() -> Int:\n", - " return z\n", - " z = 2\n", - " print(formatter())\n", - "\n", - " fn main():\n", - " foo(5)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/copy-capture.md b/docs/manual/decorators/copy-capture.md new file mode 100644 index 0000000000..394e9d6a17 --- /dev/null +++ b/docs/manual/decorators/copy-capture.md @@ -0,0 +1,27 @@ +--- +title: '@__copy_capture' +description: Captures register-passable typed values by copy. +codeTitle: true + +--- + +You can add the `__copy_capture` decorator on a parametric closure to capture +register-passable values by copy. This decorator causes a nested function to +copy the value of the indicated variable into the closure object at the point +of formation instead of capturing that variable by reference. This allows the +closure to be passed as an escaping function, without lifetime concerns. + +```mojo + fn foo(x: Int): + var z = x + + @__copy_capture(z) + @parameter + fn formatter() -> Int: + return z + z = 2 + print(formatter()) + + fn main(): + foo(5) +``` diff --git a/docs/manual/decorators/index.mdx b/docs/manual/decorators/index.mdx index f7c7d1a6af..2a65a9f213 100644 --- a/docs/manual/decorators/index.mdx +++ b/docs/manual/decorators/index.mdx @@ -7,13 +7,13 @@ hide_table_of_contents: true listing: - id: docs contents: - - always-inline.ipynb - - copy-capture.ipynb - - nonmaterializable.ipynb - - parameter.ipynb - - register-passable.ipynb - - staticmethod.ipynb - - value.ipynb + - always-inline.md + - copy-capture.md + - nonmaterializable.md + - parameter.md + - register-passable.md + - staticmethod.md + - value.md type: grid page-size: 99 --- diff --git a/docs/manual/decorators/nonmaterializable.ipynb b/docs/manual/decorators/nonmaterializable.ipynb deleted file mode 100644 index 49d6b312fc..0000000000 --- a/docs/manual/decorators/nonmaterializable.ipynb +++ /dev/null @@ -1,112 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@nonmaterializable`'\n", - "description: Declares that a type should exist only in the parameter domain.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@nonmaterializable` decorator on a struct to declare that the\n", - "type can exist only in the parameter domain (it can be used for metaprogramming\n", - "only, and not as a runtime type). And, if an instance of this type does\n", - "transition into the runtime domain, this decorator declares what type it\n", - "becomes there.\n", - "\n", - "To use it, declare your type with `@nonmaterializable(TargetType)`, where\n", - "`TargetType` is the type that the object should convert to if it becomes a\n", - "runtime value (you must declare the `TargetType`). For example, if a struct is\n", - "marked as `@nonmaterializable(Foo)`, then anywhere that it goes from a\n", - "parameter value to a runtime value, it automatically converts into the `Foo`\n", - "type.\n", - "\n", - "For example, the following `NmStruct` type can be used in the parameter domain,\n", - "but the `converted_to_has_bool` instance of it is converted to `HasBool` when it's\n", - "materialized as a runtime value:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "@register_passable(\"trivial\")\n", - "struct HasBool:\n", - " var x: Bool\n", - "\n", - " fn __init__(out self, x: Bool):\n", - " self.x = x\n", - "\n", - " @always_inline(\"nodebug\")\n", - " fn __init__(out self, nms: NmStruct):\n", - " self.x = True if (nms.x == 77) else False\n", - "\n", - "@value\n", - "@nonmaterializable(HasBool)\n", - "@register_passable(\"trivial\")\n", - "struct NmStruct:\n", - " var x: Int\n", - "\n", - " @always_inline(\"nodebug\")\n", - " fn __add__(self, rhs: Self) -> Self:\n", - " return NmStruct(self.x + rhs.x)\n", - "\n", - "alias still_nm_struct = NmStruct(1) + NmStruct(2)\n", - "# When materializing to a run-time variable, it is automatically converted,\n", - "# even without a type annotation.\n", - "var converted_to_has_bool = still_nm_struct" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "A non-materializable struct must have all of its methods annotated\n", - "as `@always_inline`, and it must be computable in the parameter domain.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/nonmaterializable.md b/docs/manual/decorators/nonmaterializable.md new file mode 100644 index 0000000000..d24847c0dc --- /dev/null +++ b/docs/manual/decorators/nonmaterializable.md @@ -0,0 +1,59 @@ +--- +title: '@nonmaterializable' +description: Declares that a type should exist only in the parameter domain. +codeTitle: true + +--- + +You can add the `@nonmaterializable` decorator on a struct to declare that the +type can exist only in the parameter domain (it can be used for metaprogramming +only, and not as a runtime type). And, if an instance of this type does +transition into the runtime domain, this decorator declares what type it +becomes there. + +To use it, declare your type with `@nonmaterializable(TargetType)`, where +`TargetType` is the type that the object should convert to if it becomes a +runtime value (you must declare the `TargetType`). For example, if a struct is +marked as `@nonmaterializable(Foo)`, then anywhere that it goes from a +parameter value to a runtime value, it automatically converts into the `Foo` +type. + +For example, the following `NmStruct` type can be used in the parameter domain, +but the `converted_to_has_bool` instance of it is converted to `HasBool` when it's +materialized as a runtime value: + +```mojo +@value +@register_passable("trivial") +struct HasBool: + var x: Bool + + fn __init__(out self, x: Bool): + self.x = x + + @always_inline("nodebug") + fn __init__(out self, nms: NmStruct): + self.x = True if (nms.x == 77) else False + +@value +@nonmaterializable(HasBool) +@register_passable("trivial") +struct NmStruct: + var x: Int + + @always_inline("nodebug") + fn __add__(self, rhs: Self) -> Self: + return NmStruct(self.x + rhs.x) + +alias still_nm_struct = NmStruct(1) + NmStruct(2) +# When materializing to a run-time variable, it is automatically converted, +# even without a type annotation. +var converted_to_has_bool = still_nm_struct +``` + +:::note + +A non-materializable struct must have all of its methods annotated +as `@always_inline`, and it must be computable in the parameter domain. + +::: diff --git a/docs/manual/decorators/parameter.ipynb b/docs/manual/decorators/parameter.ipynb deleted file mode 100644 index 1cb7334442..0000000000 --- a/docs/manual/decorators/parameter.ipynb +++ /dev/null @@ -1,195 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@parameter`'\n", - "description: Executes a function or if statement at compile time.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@parameter` decorator on an `if` statement or on a nested\n", - "function to run that code at compile time.\n", - "\n", - "## Parametric if statement\n", - "\n", - "You can add `@parameter` to any `if` condition that's based on a valid\n", - "parameter expression (it's an expression that evaluates at compile time). This\n", - "ensures that only the live branch of the `if` statement is compiled into the\n", - "program, which can reduce your final binary size. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "this will be included in the binary\n" - ] - } - ], - "source": [ - "@parameter\n", - "if True:\n", - " print(\"this will be included in the binary\")\n", - "else:\n", - " print(\"this will be eliminated at compile time\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parametric for statement\n", - "\n", - "You can add the `@parameter` decorator to an `for` loop to create a loop that's\n", - "evaluated at compile time. The loop sequence and induction values must be\n", - "a valid parameter expressions (that is, an expressions that evaluate at compile\n", - "time).\n", - "\n", - "This has the effect of \"unrolling\" the loop.\n", - "\n", - " ```mojo\n", - " fn parameter_for[max: Int]():\n", - " @parameter\n", - " for i in range(max)\n", - " @parameter\n", - " if i == 10:\n", - " print(\"found 10!\")\n", - " ```\n", - "\n", - " Currently, `@parameter for` requires the sequence's `__iter__` method to\n", - " return a `_StridedRangeIterator`, meaning the induction variables must be\n", - " `Int`. The intention is to lift these restrictions in the future.\n", - "\n", - "### Compared to `unroll()`\n", - "\n", - "The Mojo standard library also includes a function called\n", - "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", - "given function that you want to call repeatedly, but has some important\n", - "differences when compared to the parametric `for` statement:\n", - "\n", - "- The `@parameter` decorator operates on `for` loop expressions. The \n", - " `unroll()` function is a higher-order function that takes a parametric closure\n", - " (see below) and executes it a specified number of times.\n", - "\n", - "- The parametric `for` statement is more versatile, since you can do anything \n", - " you can do in a `for` statement: including using arbitrary sequences, \n", - " early-exiting from the loop, skipping iterations with `continue` and so on.\n", - " \n", - " By contrast, `unroll()` simply takes a function and a count, and executes\n", - " the function the specified number of times.\n", - "\n", - "Both `unroll()` and `@parameter for` unroll at the beginning of compilation, \n", - "which might explode the size of the program that still needs to be compiled,\n", - "depending on the amount of code that's unrolled." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parametric closure\n", - "\n", - "You can add `@parameter` on a nested function to create a “parametric”\n", - "capturing closure. This means you can create a closure function that captures\n", - "values from the outer scope (regardless of whether they are variables or\n", - "parameters), and then use that closure as a parameter. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n" - ] - } - ], - "source": [ - "fn use_closure[func: fn(Int) capturing [_] -> Int](num: Int) -> Int:\n", - " return func(num)\n", - "\n", - "fn create_closure():\n", - " var x = 1\n", - "\n", - " @parameter\n", - " fn add(i: Int) -> Int:\n", - " return x + i\n", - "\n", - " var y = use_closure[add](2)\n", - " print(y)\n", - "\n", - "create_closure()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Without the `@parameter` decorator, you'll get a compiler error that says you\n", - "\"cannot use a dynamic value in call parameter\"—referring to the\n", - "`use_closure[add](2)` call—because the `add()` closure would still be dynamic.\n", - "\n", - "Note the `[_]` in the function type:\n", - "\n", - "```mojo\n", - "fn use_closure[func: fn(Int) capturing [_] -> Int](num: Int) -> Int:\n", - "```\n", - "\n", - "This origin specifier represents the set of origins for the values captured by\n", - "the parametric closure. This allows the compiler to correctly extend the\n", - "lifetimes of those values. For more information on lifetimes and origins, see\n", - "[Lifetimes, origins and references](/mojo/manual/values/lifetimes).\n", - "\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/parameter.md b/docs/manual/decorators/parameter.md new file mode 100644 index 0000000000..c82d9c6783 --- /dev/null +++ b/docs/manual/decorators/parameter.md @@ -0,0 +1,115 @@ +--- +title: '@parameter' +description: Executes a function or if statement at compile time. +codeTitle: true + +--- + +You can add the `@parameter` decorator on an `if` statement or on a nested +function to run that code at compile time. + +## Parametric if statement + +You can add `@parameter` to any `if` condition that's based on a valid +parameter expression (it's an expression that evaluates at compile time). This +ensures that only the live branch of the `if` statement is compiled into the +program, which can reduce your final binary size. For example: + +```mojo +@parameter +if True: + print("this will be included in the binary") +else: + print("this will be eliminated at compile time") +``` + +```output +this will be included in the binary +``` + +## Parametric for statement + +You can add the `@parameter` decorator to an `for` loop to create a loop that's +evaluated at compile time. The loop sequence and induction values must be +a valid parameter expressions (that is, an expressions that evaluate at compile +time). + +This has the effect of "unrolling" the loop. + +```mojo +fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") +``` + +Currently, `@parameter for` requires the sequence's `__iter__` method to +return a `_StridedRangeIterator`, meaning the induction variables must be +`Int`. The intention is to lift these restrictions in the future. + +### Compared to `unroll()` + +The Mojo standard library also includes a function called +[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a +given function that you want to call repeatedly, but has some important +differences when compared to the parametric `for` statement: + +- The `@parameter` decorator operates on `for` loop expressions. The + `unroll()` function is a higher-order function that takes a parametric closure + (see below) and executes it a specified number of times. + +- The parametric `for` statement is more versatile, since you can do anything + you can do in a `for` statement: including using arbitrary sequences, + early-exiting from the loop, skipping iterations with `continue` and so on. + + By contrast, `unroll()` simply takes a function and a count, and executes + the function the specified number of times. + +Both `unroll()` and `@parameter for` unroll at the beginning of compilation, +which might explode the size of the program that still needs to be compiled, +depending on the amount of code that's unrolled. + +## Parametric closure + +You can add `@parameter` on a nested function to create a “parametric” +capturing closure. This means you can create a closure function that captures +values from the outer scope (regardless of whether they are variables or +parameters), and then use that closure as a parameter. For example: + +```mojo +fn use_closure[func: fn(Int) capturing [_] -> Int](num: Int) -> Int: + return func(num) + +fn create_closure(): + var x = 1 + + @parameter + fn add(i: Int) -> Int: + return x + i + + var y = use_closure[add](2) + print(y) + +create_closure() +``` + +```output +3 +``` + +Without the `@parameter` decorator, you'll get a compiler error that says you +"cannot use a dynamic value in call parameter"—referring to the +`use_closure[add](2)` call—because the `add()` closure would still be dynamic. + +Note the `[_]` in the function type: + +```mojo +fn use_closure[func: fn(Int) capturing [_] -> Int](num: Int) -> Int: +``` + +This origin specifier represents the set of origins for the values captured by +the parametric closure. This allows the compiler to correctly extend the +lifetimes of those values. For more information on lifetimes and origins, see +[Lifetimes, origins and references](/mojo/manual/values/lifetimes). diff --git a/docs/manual/decorators/register-passable.ipynb b/docs/manual/decorators/register-passable.ipynb deleted file mode 100644 index 174418abaa..0000000000 --- a/docs/manual/decorators/register-passable.ipynb +++ /dev/null @@ -1,205 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@register_passable`'\n", - "description: Declares that a type should be passed in machine registers.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@register_passable` decorator on a struct to tell Mojo that\n", - "the type should be passed in machine registers (such as a CPU register; subject\n", - "to the details of the underlying architecture). For tiny data types like an\n", - "integer or floating-point number, this is much more efficient than storing\n", - "values in stack memory. This means the type is always passed by value and\n", - "cannot be passed by reference.\n", - "\n", - "The basic `@register_passable` decorator does not change the fundamental\n", - "behavior of a type: it still needs an `__init__()` and `__copyinit__()` method\n", - "to be copyable (and it may have a `__del__()` method, if necessary). For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "@register_passable\n", - "struct Pair:\n", - " var a: Int\n", - " var b: Int\n", - "\n", - " fn __init__(out self, one: Int, two: Int):\n", - " self.a = one\n", - " self.b = two\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " self.a = existing.a\n", - " self.b = existing.b\n", - "\n", - "fn test_pair():\n", - " var x = Pair(5, 10)\n", - " var y = x\n", - "\n", - " print(y.a, y.b)\n", - " y.a = 10\n", - " y.b = 20\n", - " print(y.a, y.b)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5 10\n", - "10 20\n" - ] - } - ], - "source": [ - "test_pair()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This behavior is what we expect from `Pair`, with or without the decorator." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You should be aware of a few other observable effects:\n", - "\n", - "1. `@register_passable` types cannot hold instances of types\n", - "that are not also `@register_passable`.\n", - "\n", - "1. `@register_passable` types do not have a predictable identity,\n", - "and so the `self` pointer is not stable/predictable (e.g. in hash tables).\n", - "\n", - "1. `@register_passable` arguments and result are exposed to C and C++ directly,\n", - "instead of being passed by-pointer.\n", - "\n", - "1. `@register_passable` types cannot have a [`__moveinit__()`\n", - "constructor](/mojo/manual/lifecycle/life#move-constructor), because\n", - "values passed in a register cannot be passed by reference.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `@register_passable(\"trivial\")`" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Most types that use `@register_passable` are just \"bags of bits,\" which we call\n", - "\"trivial\" types. These trivial types are simple and should be copied, moved,\n", - "and destroyed without any custom constructors or a destructor. For these types,\n", - "you can add the `\"trivial\"` argument, and Mojo synthesizes all the lifecycle\n", - "methods as appropriate for a trivial register-passable type:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "@register_passable(\"trivial\")\n", - "struct Pair:\n", - " var a: Int\n", - " var b: Int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is similar to the [`@value`](/mojo/manual/decorators/value) decorator,\n", - "except when using `@register_passable(\"trivial\")` the only lifecycle method\n", - "you're allowed to define is the `__init__()` constructor (but you don't have\n", - "to)—you _cannot_ define any copy or move constructors or a destructor.\n", - "\n", - "Examples of trivial types include:\n", - "\n", - "- Arithmetic types such as `Int`, `Bool`, `Float64` etc.\n", - "- Pointers (the address value is trivial, not the data being pointed to).\n", - "- Arrays of other trivial types, including SIMD." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more information about lifecycle methods (constructors and destructors)\n", - "see the section about [Value lifecycle](/mojo/manual/lifecycle/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note TODO\n", - "\n", - "This decorator is due for reconsideration. Lack of custom\n", - "copy/move/destroy logic and \"passability in a register\" are orthogonal concerns\n", - "and should be split. This former logic should be subsumed into a more general\n", - "decorator, which is orthogonal to `@register_passable`.\n", - "\n", - ":::" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/register-passable.md b/docs/manual/decorators/register-passable.md new file mode 100644 index 0000000000..16e3ec0587 --- /dev/null +++ b/docs/manual/decorators/register-passable.md @@ -0,0 +1,105 @@ +--- +title: '@register_passable' +description: Declares that a type should be passed in machine registers. +codeTitle: true + +--- + +You can add the `@register_passable` decorator on a struct to tell Mojo that +the type should be passed in machine registers (such as a CPU register; subject +to the details of the underlying architecture). For tiny data types like an +integer or floating-point number, this is much more efficient than storing +values in stack memory. This means the type is always passed by value and +cannot be passed by reference. + +The basic `@register_passable` decorator does not change the fundamental +behavior of a type: it still needs an `__init__()` and `__copyinit__()` method +to be copyable (and it may have a `__del__()` method, if necessary). For example: + +```mojo +@register_passable +struct Pair: + var a: Int + var b: Int + + fn __init__(out self, one: Int, two: Int): + self.a = one + self.b = two + + fn __copyinit__(out self, existing: Self): + self.a = existing.a + self.b = existing.b + +fn test_pair(): + var x = Pair(5, 10) + var y = x + + print(y.a, y.b) + y.a = 10 + y.b = 20 + print(y.a, y.b) +``` + +```mojo +test_pair() +``` + +```output +5 10 +10 20 +``` + +This behavior is what we expect from `Pair`, with or without the decorator. + +You should be aware of a few other observable effects: + +1. `@register_passable` types cannot hold instances of types + that are not also `@register_passable`. + +2. `@register_passable` types do not have a predictable identity, + and so the `self` pointer is not stable/predictable (e.g. in hash tables). + +3. `@register_passable` arguments and result are exposed to C and C++ directly, + instead of being passed by-pointer. + +4. `@register_passable` types cannot have a [`__moveinit__()` + constructor](/mojo/manual/lifecycle/life#move-constructor), because + values passed in a register cannot be passed by reference. + +## `@register_passable("trivial")` + +Most types that use `@register_passable` are just "bags of bits," which we call +"trivial" types. These trivial types are simple and should be copied, moved, +and destroyed without any custom constructors or a destructor. For these types, +you can add the `"trivial"` argument, and Mojo synthesizes all the lifecycle +methods as appropriate for a trivial register-passable type: + +```mojo +@register_passable("trivial") +struct Pair: + var a: Int + var b: Int +``` + +This is similar to the [`@value`](/mojo/manual/decorators/value) decorator, +except when using `@register_passable("trivial")` the only lifecycle method +you're allowed to define is the `__init__()` constructor (but you don't have +to)—you *cannot* define any copy or move constructors or a destructor. + +Examples of trivial types include: + +- Arithmetic types such as `Int`, `Bool`, `Float64` etc. +- Pointers (the address value is trivial, not the data being pointed to). +- Arrays of other trivial types, including SIMD. + +For more information about lifecycle methods (constructors and destructors) +see the section about [Value lifecycle](/mojo/manual/lifecycle/). + +:::note TODO + +This decorator is due for reconsideration. Lack of custom +copy/move/destroy logic and "passability in a register" are orthogonal concerns +and should be split. This former logic should be subsumed into a more general +decorator, which is orthogonal to `@register_passable`. + +::: diff --git a/docs/manual/decorators/staticmethod.ipynb b/docs/manual/decorators/staticmethod.ipynb deleted file mode 100644 index bcd9a4d2e5..0000000000 --- a/docs/manual/decorators/staticmethod.ipynb +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@staticmethod`'\n", - "description: Declares a struct method as static.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@staticmethod` decorator on a struct method to declare a static\n", - "method. \n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from collections import List\n", - "from pathlib import Path\n", - "\n", - "\n", - "struct MyStruct:\n", - " var data: List[UInt8]\n", - "\n", - " fn __init__(out self):\n", - " self.data = List[UInt8]()\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " self.data = existing.data ^\n", - "\n", - " @staticmethod\n", - " fn load_from_file(file_path: Path) raises -> Self:\n", - " var new_struct = MyStruct()\n", - " new_struct.data = file_path.read_bytes()\n", - " return new_struct ^" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Unlike an instance method, a static method doesn't take an implicit `self`\n", - "argument. It's not attached to a specific instance of a struct, so it can't\n", - "access instance data.\n", - "\n", - "For more information see the documentation on\n", - "[static methods](/mojo/manual/structs#static-methods)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/staticmethod.md b/docs/manual/decorators/staticmethod.md new file mode 100644 index 0000000000..686c86cd4c --- /dev/null +++ b/docs/manual/decorators/staticmethod.md @@ -0,0 +1,39 @@ +--- +title: '@staticmethod' +description: Declares a struct method as static. +codeTitle: true + +--- + +You can add the `@staticmethod` decorator on a struct method to declare a static +method. + +For example: + +```mojo +from collections import List +from pathlib import Path + + +struct MyStruct: + var data: List[UInt8] + + fn __init__(out self): + self.data = List[UInt8]() + + fn __moveinit__(out self, owned existing: Self): + self.data = existing.data ^ + + @staticmethod + fn load_from_file(file_path: Path) raises -> Self: + var new_struct = MyStruct() + new_struct.data = file_path.read_bytes() + return new_struct ^ +``` + +Unlike an instance method, a static method doesn't take an implicit `self` +argument. It's not attached to a specific instance of a struct, so it can't +access instance data. + +For more information see the documentation on +[static methods](/mojo/manual/structs#static-methods). diff --git a/docs/manual/decorators/value.ipynb b/docs/manual/decorators/value.ipynb deleted file mode 100644 index f82a54a21b..0000000000 --- a/docs/manual/decorators/value.ipynb +++ /dev/null @@ -1,111 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@value`'\n", - "description: Generates boilerplate lifecycle methods for a struct.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@value` decorator on a struct to generate boilerplate\n", - "lifecycle methods, including the member-wise `__init__()` constructor,\n", - "`__copyinit__()` copy constructor, and `__moveinit__()` move constructor.\n", - "\n", - "For example, consider a simple struct like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo sees the `@value` decorator and notices that you don't have any constructors\n", - "and it synthesizes them for you, the result being as if you had actually\n", - "written this:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, owned name: String, age: Int):\n", - " self.name = name^\n", - " self.age = age\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " self.name = existing.name\n", - " self.age = existing.age\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " self.name = existing.name^\n", - " self.age = existing.age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo synthesizes each lifecycle method only when it doesn't exist, so\n", - "you can use `@value` and still define your own versions to override the default\n", - "behavior. For example, it is fairly common to use the default member-wise and\n", - "move constructor, but create a custom copy constructor." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more information about these lifecycle methods, read\n", - "[Life of a value](/mojo/manual/lifecycle/life)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/decorators/value.md b/docs/manual/decorators/value.md new file mode 100644 index 0000000000..f60f03259b --- /dev/null +++ b/docs/manual/decorators/value.md @@ -0,0 +1,49 @@ +--- +title: '@value' +description: Generates boilerplate lifecycle methods for a struct. +codeTitle: true + +--- + +You can add the `@value` decorator on a struct to generate boilerplate +lifecycle methods, including the member-wise `__init__()` constructor, +`__copyinit__()` copy constructor, and `__moveinit__()` move constructor. + +For example, consider a simple struct like this: + +```mojo +@value +struct MyPet: + var name: String + var age: Int +``` + +Mojo sees the `@value` decorator and notices that you don't have any constructors +and it synthesizes them for you, the result being as if you had actually +written this: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, owned name: String, age: Int): + self.name = name^ + self.age = age + + fn __copyinit__(out self, existing: Self): + self.name = existing.name + self.age = existing.age + + fn __moveinit__(out self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age +``` + +Mojo synthesizes each lifecycle method only when it doesn't exist, so +you can use `@value` and still define your own versions to override the default +behavior. For example, it is fairly common to use the default member-wise and +move constructor, but create a custom copy constructor. + +For more information about these lifecycle methods, read +[Life of a value](/mojo/manual/lifecycle/life). diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb deleted file mode 100755 index 6f24f70e0d..0000000000 --- a/docs/manual/functions.ipynb +++ /dev/null @@ -1,855 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Functions\n", - "sidebar_position: 2\n", - "description: Introduction to Mojo `fn` and `def` functions.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As mentioned in [Language basics](/mojo/manual/basics), Mojo supports two\n", - "types of functions: `def` and `fn` functions. You can use either declaration\n", - "with any function, including the `main()` function, but they have different\n", - "default behaviors, as described on this page.\n", - "\n", - "We believe both `def` and `fn` have good use cases and don't consider either to\n", - "be better than the other. Deciding which to use is a matter of personal taste as\n", - "to which style best fits a given task.\n", - "\n", - "We believe Mojo's flexibility in this regard is a superpower that allows you to\n", - "write code in the manner that's best for your project.\n", - "\n", - ":::note\n", - "\n", - "Functions declared inside a [`struct`](/mojo/manual/structs) are called\n", - "\"methods,\" but they have all the same qualities as \"functions\" described here.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `fn` functions\n", - "\n", - "The `fn` function has somewhat stricter rules than the `def` function.\n", - "\n", - "Here's an example of an `fn` function:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn greet(name: String) -> String:\n", - " var greeting = \"Hello, \" + name + \"!\"\n", - " return greeting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As far as a function caller is concerned, `def` and `fn` functions are\n", - "interchangeable. That is, there's nothing a `def` can do that an `fn` can't\n", - "(and vice versa). The difference is that, compared to a `def` function, an `fn`\n", - "function is more strict on the inside.\n", - "\n", - "Here's everything to know about `fn`:\n", - "\n", - "- Arguments must specify a type (except for the\n", - " `self` argument in [struct methods](/mojo/manual/structs#methods)).\n", - "\n", - "- Return values must specify a type, unless the function doesn't return a value.\n", - " \n", - " If you don't specify a return type, it defaults to `None` (meaning no return\n", - " value).\n", - "\n", - "- By default, arguments are received as an immutable reference (values are\n", - " read-only, using the `borrowed` [argument\n", - " convention](/mojo/manual/values/ownership#argument-conventions)).\n", - " \n", - " This prevents accidental mutations, and permits the use of non-copyable types\n", - " as arguments.\n", - " \n", - " If you want a local copy, you can simply assign the value to a local\n", - " variable. Or, you can get a mutable reference to the value by declaring the\n", - " `inout` [argument\n", - " convention](/mojo/manual/values/ownership#argument-conventions)).\n", - "\n", - "- If the function raises an exception, it must be explicitly declared with the\n", - " `raises` keyword. (A `def` function does not need to declare exceptions.)\n", - "\n", - "By enforcing these type checks, using the `fn` function helps avoid a variety\n", - "of runtime errors." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `def` functions\n", - "\n", - "Compared to an `fn` function, a `def` function has fewer restrictions.\n", - "The `def` function works more like a Python\n", - "`def` function. For example, this function works the same in Python and Mojo:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def greet(name):\n", - " greeting = \"Hello, \" + name + \"!\"\n", - " return greeting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In a Mojo `def` function, you have the option to specify the argument type and\n", - "the return type. You can also declare variables with `var`, with or without\n", - "explicit typing. So you can write a `def` function that looks almost exactly\n", - "like the `fn` function shown earlier:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def greet(name: String) -> String:\n", - " var greeting = \"Hello, \" + name + \"!\"\n", - " return greeting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This way, the compiler ensures that `name` is a string, and the return type is a\n", - "string.\n", - "\n", - "Here's everything to know about `def`:\n", - "\n", - "- Arguments don't require a declared type.\n", - "\n", - " Undeclared arguments are actually passed as an\n", - " [`object`](/mojo/stdlib/builtin/object/object), which allows the\n", - " function to receive any type (Mojo infers the type at runtime).\n", - "\n", - "- Return types don't need to be declared, and also default to `object`. (If a \n", - " `def` function doesn't declare a return type of `None`, it's considered to\n", - " return an `object` by default.)\n", - "\n", - "- Arguments are mutable. Arguments default to using the `borrowed` \n", - " [argument convention](/mojo/manual/values/ownership#argument-conventions)\n", - " like an `fn` function, with a special addition: if the function mutates the\n", - " argument, it makes a mutable copy. \n", - "\n", - " If an argument is an `object` type, it's received as a reference, following\n", - " [object reference\n", - " semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", - " \n", - " If an argument is any other declared type, it's received as a value." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `object` type\n", - "\n", - "If you don't declare the type for an argument or return value in a `def`, it\n", - "becomes an [`object`](/mojo/stdlib/builtin/object/object), which is unlike\n", - "any other type in the standard library.\n", - "\n", - "The `object` type allows for dynamic typing because it can actually represent\n", - "any type in the Mojo standard library, and the actual type is inferred at\n", - "runtime. (Actually, there's still more to do before it can represent all Mojo\n", - "types.) This is great for compatibility with Python and all of the flexibility\n", - "that it provides with dynamic types. However, this lack of type enforcement can\n", - "lead to runtime errors when a function receives or returns an unexpected type.\n", - "\n", - "For compatibility with Python, `object` values are passed using [object\n", - "reference\n", - "semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics).\n", - "As such, the `object` type is not compatible with the [argument\n", - "conventions](/mojo/manual/values/ownership#argument-conventions) that\n", - "enforce value semantics. So, be careful if using `object` values alongside other\n", - "strongly-typed values—their behavior might be inconsistent because `object` is \n", - "the only type in the standard library that does not conform to [full value\n", - "semantics](/mojo/manual/values/value-semantics#intro-to-value-semantics).\n", - "\n", - ":::note TODO\n", - "\n", - "The `object` type is still a work in progress. It doesn't support all of the\n", - "possible underlying types, for example.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Function arguments\n", - "\n", - "As noted in the previous sections, there are a few differences between how `def`\n", - "and `fn` functions treat arguments. But most of the time they are the same.\n", - "\n", - "As noted, there are some differences in _argument conventions_. \n", - "Argument conventions are discussed in much more detail in the page on\n", - "[Ownership](/mojo/manual/values/ownership#argument-conventions).\n", - "\n", - "The other difference is that `def` functions don't need to specify an argument's\n", - "type. If no type is specified, the argument is passed as an \n", - "[`object`](/mojo/stdlib/builtin/object/object).\n", - "\n", - "The remaining rules for arguments described in this section apply to both `def`\n", - "and `fn` functions.\n", - "\n", - "### Optional arguments\n", - "\n", - "An optional argument is one that includes a default value, such as the `exp`\n", - "argument here:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn my_pow(base: Int, exp: Int = 2) -> Int:\n", - " return base ** exp\n", - "\n", - "fn use_defaults():\n", - " # Uses the default value for `exp`\n", - " var z = my_pow(3)\n", - " print(z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, you cannot define a default value for an argument that's declared as\n", - "[`inout`](/mojo/manual/values/ownership#mutable-arguments-inout).\n", - "\n", - "Any optional arguments must appear after any required arguments. [Keyword-only\n", - "arguments](#positional-only-and-keyword-only-arguments), discussed later, can\n", - "also be either required or optional." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Keyword arguments\n", - "\n", - "You can also use keyword arguments when calling a function. Keyword arguments\n", - "are specified using the format argument_name =\n", - "argument_value. You can pass keyword arguments in any order:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn my_pow(base: Int, exp: Int = 2) -> Int:\n", - " return base ** exp\n", - "\n", - "fn use_keywords():\n", - " # Uses keyword argument names (with order reversed)\n", - " var z = my_pow(exp=3, base=2)\n", - " print(z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Variadic arguments\n", - "\n", - "Variadic arguments let a function accept a variable number of arguments. To\n", - "define a function that takes a variadic argument, use the variadic argument\n", - "syntax *argument_name:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn sum(*values: Int) -> Int:\n", - " var sum: Int = 0\n", - " for value in values:\n", - " sum = sum + value\n", - " return sum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The variadic argument `values` here is a placeholder that accepts any number of \n", - "passed positional arguments.\n", - "\n", - "You can define zero or more arguments before the variadic argument. When calling\n", - "the function, any remaining positional arguments are assigned to the variadic\n", - "argument, so any arguments declared **after** the variadic argument can only be\n", - "specified by keyword (see \n", - "[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)).\n", - "\n", - "Variadic arguments can be divided into two categories:\n", - "\n", - "- Homogeneous variadic arguments, where all of the passed arguments are the same\n", - " type—all `Int`, or all `String`, for example. \n", - "- Heterogeneous variadic arguments, which can accept a set of different argument\n", - " types.\n", - "\n", - "The following sections describe how to work with homogeneous and heterogeneous\n", - "variadic arguments.\n", - "\n", - ":::note Variadic parameters\n", - "\n", - "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", - "(parameters are used for compile-time metaprogramming). Variadic parameters\n", - "are supported, but with some limitations—for details see \n", - "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", - "\n", - ":::\n", - "\n", - "\n", - "#### Homogeneous variadic arguments\n", - "\n", - "When defining a homogeneous variadic argument, use \n", - "*argument_name: argument_type:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def greet(*names: String):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Inside the function body, the variadic argument is available as an iterable list\n", - "for ease of use. Currently there are some differences in handling the list \n", - "depending on whether the arguments are register-passable types (such as `Int`)\n", - "or memory-only types (such as `String`). TODO: We hope to remove these\n", - "differences in the future.\n", - "\n", - "Register-passable types, such as `Int`, are available as a \n", - "[`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) type. As\n", - "shown in the previous example, you can iterate over the values using a `for..in`\n", - "loop." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fn sum(*values: Int) -> Int:\n", - " var sum: Int = 0\n", - " for value in values:\n", - " sum = sum+value\n", - " return sum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Memory-only types, such as `String`, are available as a \n", - "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem).\n", - "Iterating over this list directly with a `for..in` loop currently produces a\n", - "[`Reference`](/mojo/stdlib/memory/reference/Reference) for each value instead\n", - "of the value itself. You must add an empty subscript operator `[]` to\n", - "dereference the reference and retrieve the value:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def make_worldly(inout *strs: String):\n", - " # Requires extra [] to dereference the reference for now.\n", - " for i in strs:\n", - " i[] += \" world\"\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Alternately, subscripting into a `VariadicListMem` returns the argument value,\n", - "and doesn't require any dereferencing:\n", - "\n", - " ```mojo\n", - " fn make_worldly(inout *strs: String):\n", - " # This \"just works\" as you'd expect!\n", - " for i in range(len(strs)):\n", - " strs[i] += \" world\"\n", - " ```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Heterogeneous variadic arguments\n", - "\n", - "Implementing heterogeneous variadic arguments is somewhat more complicated than\n", - "homogeneous variadic arguments. Writing generic code to handle multiple argument\n", - "types requires [traits](/mojo/manual/traits) and \n", - "[parameters](/mojo/manual/parameters/). So the syntax may look a little\n", - "unfamiliar if you haven't worked with those features. The signature for a\n", - "function with a heterogeneous variadic argument looks like this:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```mojo\n", - "def count_many_things[*ArgTypes: Intable](*args: *ArgTypes):\n", - " ...\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an\n", - "`ArgTypes` parameter, which is a list of types, all of which conform to the \n", - "[`Intable`](/mojo/stdlib/builtin/int/Intable) trait. The argument list, \n", - "`(*args: *ArgTypes)` has the familiar `*args` for the variadic argument, but \n", - "instead of a single type, its type is defined as _list_ of types, `*ArgTypes`.\n", - "\n", - "This means that each argument in `args` has a corresponding type in `ArgTypes`, \n", - "so args[n] is of type \n", - "ArgTypes[n].\n", - "\n", - "Inside the function, `args` is available as a\n", - "[`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack). The easiest\n", - "way to work with the arguments is to use the `each()` method to iterate through\n", - "the `VariadicPack`:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "28\n" - ] - } - ], - "source": [ - "fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:\n", - " var total = 0\n", - "\n", - " @parameter\n", - " fn add[Type: Intable](value: Type):\n", - " total += int(value)\n", - "\n", - " args.each[add]()\n", - " return total\n", - "\n", - "print(count_many_things(5, 11.7, 12))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the example above, the `add()` function is called for each argument in turn,\n", - "with the appropriate `value` and `Type` values. For instance, `add()` is first\n", - "called with `value=5` and `Type=Int`, then with `value=11.7` and `Type=Float64`.\n", - "\n", - "Also, note that when calling `count_many_things()`, you don't actually pass in\n", - "a list of argument types. You only need to pass in the arguments, and Mojo\n", - "generates the `ArgTypes` list itself.\n", - "\n", - "As a small optimization, if your function is likely to be called with a single\n", - "argument frequently, you can define your function with a single argument\n", - "followed by a variadic argument. This lets the simple case bypass populating and\n", - "iterating through the `VariadicPack`.\n", - "\n", - "For example, given a `print_string()` function that prints a single string, you\n", - "could re-implement the variadic `print()` function with code like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Bob\n" - ] - } - ], - "source": [ - "fn print_string(s: String):\n", - " print(s, end=\"\")\n", - "\n", - "fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts):\n", - " print_string(str(first))\n", - "\n", - " @parameter\n", - " fn print_elt[T: Stringable](a: T):\n", - " print_string(\" \")\n", - " print_string(str(a))\n", - " rest.each[print_elt]()\n", - "print_many(\"Bob\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you call `print_many()` with a single argument, it calls `print_string()`\n", - "directly. The `VariadicPack` is empty, so `each()` returns immediately without\n", - "calling the `print_elt()` function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Variadic keyword arguments\n", - "\n", - "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", - "keyword arguments allow the user to pass an arbitrary number of keyword\n", - "arguments. To define a function that takes a variadic keyword argument, use the\n", - "variadic keyword argument syntax **kw_argument_name:\n", - "\n", - " ```mojo\n", - " fn print_nicely(**kwargs: Int) raises:\n", - " for key in kwargs.keys():\n", - " print(key[], \"=\", kwargs[key[]])\n", - "\n", - " # prints:\n", - " # `a = 7`\n", - " # `y = 8`\n", - " print_nicely(a=7, y=8)\n", - " ```\n", - "\n", - " In this example, the argument name `kwargs` is a placeholder that accepts any\n", - " number of keyword arguments. Inside the body of the function, you can access\n", - " the arguments as a dictionary of keywords and argument values (specifically,\n", - " an instance of\n", - " [`OwnedKwargsDict`](/mojo/stdlib/collections/dict/OwnedKwargsDict)).\n", - " \n", - " \n", - " There are currently a few limitations:\n", - "\n", - " - Variadic keyword arguments are always implicitly treated as if they\n", - " were declared with the `owned` [argument \n", - " convention](/mojo/manual/values/ownership#argument-conventions), and\n", - " can't be declared otherwise:\n", - "\n", - " ```mojo\n", - " # Not supported yet.\n", - " fn borrowed_var_kwargs(borrowed **kwargs: Int): ...\n", - " ```\n", - "\n", - " - All the variadic keyword arguments must have the same type, and this\n", - " determines the type of the argument dictionary. For example, if the argument\n", - " is `**kwargs: Float64` then the argument dictionary will be a \n", - " `OwnedKwargsDict[Float64]`.\n", - "\n", - " - The argument type must conform to the \n", - " [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", - " That is, the type must be both [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", - " and [`Copyable`](/mojo/stdlib/builtin/value/Copyable).\n", - "\n", - " - Dictionary unpacking is not supported yet:\n", - "\n", - " ```mojo\n", - " fn takes_dict(d: Dict[String, Int]):\n", - " print_nicely(**d) # Not supported yet.\n", - " ```\n", - "\n", - " - Variadic keyword _parameters_ are not supported yet:\n", - "\n", - " ```mojo\n", - " # Not supported yet.\n", - " fn var_kwparams[**kwparams: Int](): ...\n", - " ```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Positional-only and keyword-only arguments\n", - "\n", - "When defining a function, you can restrict some arguments so that they can only\n", - "be passed as positional arguments, or they can only be passed as keyword \n", - "arguments.\n", - "\n", - "To define positional-only arguments, add a slash character (`/`) to the\n", - "argument list. Any arguments before the `/` are positional-only: they can't be\n", - "passed as keyword arguments. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "fn min(a: Int, b: Int, /) -> Int:\n", - " return a if a < b else b" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This `min()` function can be called with `min(1, 2)` but can't be called using\n", - "keywords, like `min(a=1, b=2)`.\n", - "\n", - "There are several reasons you might want to write a function with\n", - "positional-only arguments:\n", - "\n", - "- The argument names aren't meaningful for the the caller.\n", - "- You want the freedom to change the argument names later on without breaking\n", - " backward compatibility.\n", - "\n", - "For example, in the `min()` function, the argument names don't add any real\n", - "information, and there's no reason to specify arguments by keyword. \n", - "\n", - "For more information on positional-only arguments, see [PEP 570 – Python\n", - "Positional-Only Parameters](https://peps.python.org/pep-0570/).\n", - "\n", - "Keyword-only arguments are the inverse of positional-only arguments: they can\n", - "only be specified by keyword. If a function accepts variadic arguments, any \n", - "arguments defined _after_ the variadic arguments are treated as keyword-only.\n", - "For example:\n", - "\n", - "```mojo\n", - "fn sort(*values: Float64, ascending: Bool = True): ...\n", - "```\n", - "\n", - "In this example, the user can pass any number of `Float64` values, optionally\n", - "followed by the keyword `ascending` argument:\n", - "\n", - "```mojo\n", - "var a = sort(1.1, 6.5, 4.3, ascending=False)\n", - "```\n", - "\n", - "If the function doesn't accept variadic arguments, you can add a single star\n", - "(`*`) to the argument list to separate the keyword-only arguments:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "fn kw_only_args(a1: Int, a2: Int, *, double: Bool) -> Int:\n", - " var product = a1 * a2\n", - " if double:\n", - " return product * 2\n", - " else:\n", - " return product" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Keyword-only arguments often have default values, but this is not required. If a\n", - "keyword-only argument doesn't have a default value, it is a _required \n", - "keyword-only argument_. It must be specified, and it must be specified by \n", - "keyword. \n", - "\n", - "Any required keyword-only arguments must appear in the signature before\n", - "any optional keyword-only arguments. That is, arguments appear in the following\n", - "sequence a function signature:\n", - "\n", - "* Required positional arguments.\n", - "* Optional positional arguments.\n", - "* Variadic arguments.\n", - "* Required keyword-only arguments.\n", - "* Optional keyword-only arguments.\n", - "* Variadic keyword arguments.\n", - "\n", - "For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only\n", - "Arguments](https://peps.python.org/pep-3102/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Overloaded functions\n", - "\n", - "If a `def` function does not specify argument types, then it can accept any\n", - "data type and decide how to handle each type internally. This is nice when you\n", - "want expressive APIs that just work by accepting arbitrary inputs, so there's\n", - "usually no need to write function overloads for a `def` function.\n", - "\n", - "On the other hand, all `fn` functions must specify argument types, so if you\n", - "want a function to work with different data types, you need to implement\n", - "separate versions of the function that each specify different argument types.\n", - "This is called \"overloading\" a function.\n", - "\n", - "For example, here's an overloaded `add()` function that can accept either\n", - "`Int` or `String` types:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "fn add(x: Int, y: Int) -> Int:\n", - " return x + y\n", - "\n", - "fn add(x: String, y: String) -> String:\n", - " return x + y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you pass anything other than `Int` or `String` to the `add()` function,\n", - "you'll get a compiler error. That is, unless `Int` or `String` can implicitly\n", - "cast the type into their own type. For example, `String` includes an overloaded\n", - "version of its constructor (`__init__()`) that accepts a `StringLiteral` value.\n", - "Thus, you can also pass a `StringLiteral` to a function that expects a `String`.\n", - "\n", - "When resolving an overloaded function call, the Mojo compiler tries each\n", - "candidate function and uses the one that works (if only one version works), or\n", - "it picks the closest match (if it can determine a close match), or it reports\n", - "that the call is ambiguous (if it can’t figure out which one to pick).\n", - "\n", - "If the compiler can't figure out which function to use, you can resolve the\n", - "ambiguity by explicitly casting your value to a supported argument type. For\n", - "example, in the following code, we want to call the overloaded `foo()`\n", - "function, but both implementations accept an argument that supports [implicit\n", - "conversion](/mojo/manual/variables#implicit-type-conversion) from\n", - "`StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a\n", - "compiler error. We can fix it by casting the value to the type we really want:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyString:\n", - " fn __init__(out self, string: StringLiteral):\n", - " pass\n", - "\n", - "fn foo(name: String):\n", - " print(\"String\")\n", - "\n", - "fn foo(name: MyString):\n", - " print(\"MyString\")\n", - "\n", - "fn call_foo():\n", - " alias string: StringLiteral = \"Hello\"\n", - " # foo(string) # This call is ambiguous because two `foo` functions match it\n", - " foo(MyString(string))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "When resolving an overloaded function, Mojo does not consider the return type\n", - "or other contextual information at the call site—only the argument types affect\n", - "which function is selected.\n", - "\n", - "Overloading also works with combinations of both `fn` and `def` functions.\n", - "For example, you could define multiple `fn` function overloads and then one\n", - "or more `def` versions that don't specify all argument types, as a fallback.\n", - "\n", - ":::note\n", - "\n", - "Although we haven't discussed\n", - "[parameters](/mojo/manual/parameters/) yet (they're\n", - "different from function arguments, and used for compile-time metaprogramming),\n", - "you can also overload functions based on parameter types.\n", - "\n", - ":::" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/functions.mdx b/docs/manual/functions.mdx new file mode 100644 index 0000000000..d35c3cad30 --- /dev/null +++ b/docs/manual/functions.mdx @@ -0,0 +1,598 @@ +--- +title: Functions +sidebar_position: 2 +description: Introduction to Mojo `fn` and `def` functions. +--- + +As mentioned in [Language basics](/mojo/manual/basics), Mojo supports two +types of functions: `def` and `fn` functions. You can use either declaration +with any function, including the `main()` function, but they have different +default behaviors, as described on this page. + +We believe both `def` and `fn` have good use cases and don't consider either to +be better than the other. Deciding which to use is a matter of personal taste as +to which style best fits a given task. + +We believe Mojo's flexibility in this regard is a superpower that allows you to +write code in the manner that's best for your project. + +:::note + +Functions declared inside a [`struct`](/mojo/manual/structs) are called +"methods," but they have all the same qualities as "functions" described here. + +::: + +## `fn` functions + +The `fn` function has somewhat stricter rules than the `def` function. + +Here's an example of an `fn` function: + +```mojo +fn greet(name: String) -> String: + var greeting = "Hello, " + name + "!" + return greeting +``` + +As far as a function caller is concerned, `def` and `fn` functions are +interchangeable. That is, there's nothing a `def` can do that an `fn` can't +(and vice versa). The difference is that, compared to a `def` function, an `fn` +function is more strict on the inside. + +Here's everything to know about `fn`: + +* Arguments must specify a type (except for the + `self` argument in [struct methods](/mojo/manual/structs#methods)). + +* Return values must specify a type, unless the function doesn't return a value. + + If you don't specify a return type, it defaults to `None` (meaning no return + value). + +* By default, arguments are received as an immutable reference (values are + read-only, using the `borrowed` [argument + convention](/mojo/manual/values/ownership#argument-conventions)). + + This prevents accidental mutations, and permits the use of non-copyable types + as arguments. + + If you want a local copy, you can simply assign the value to a local + variable. Or, you can get a mutable reference to the value by declaring the + `inout` [argument + convention](/mojo/manual/values/ownership#argument-conventions)). + +* If the function raises an exception, it must be explicitly declared with the + `raises` keyword. (A `def` function does not need to declare exceptions.) + +By enforcing these type checks, using the `fn` function helps avoid a variety +of runtime errors. + +## `def` functions + +Compared to an `fn` function, a `def` function has fewer restrictions. +The `def` function works more like a Python +`def` function. For example, this function works the same in Python and Mojo: + +```mojo +def greet(name): + greeting = "Hello, " + name + "!" + return greeting +``` + +In a Mojo `def` function, you have the option to specify the argument type and +the return type. You can also declare variables with `var`, with or without +explicit typing. So you can write a `def` function that looks almost exactly +like the `fn` function shown earlier: + +```mojo +def greet(name: String) -> String: + var greeting = "Hello, " + name + "!" + return greeting +``` + +This way, the compiler ensures that `name` is a string, and the return type is a +string. + +Here's everything to know about `def`: + +* Arguments don't require a declared type. + + Undeclared arguments are actually passed as an + [`object`](/mojo/stdlib/builtin/object/object), which allows the + function to receive any type (Mojo infers the type at runtime). + +* Return types don't need to be declared, and also default to `object`. (If a + `def` function doesn't declare a return type of `None`, it's considered to + return an `object` by default.) + +* Arguments are mutable. Arguments default to using the `borrowed` + [argument convention](/mojo/manual/values/ownership#argument-conventions) + like an `fn` function, with a special addition: if the function mutates the + argument, it makes a mutable copy. + + If an argument is an `object` type, it's received as a reference, following + [object reference + semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics). + + If an argument is any other declared type, it's received as a value. + +### The `object` type + +If you don't declare the type for an argument or return value in a `def`, it +becomes an [`object`](/mojo/stdlib/builtin/object/object), which is unlike +any other type in the standard library. + +The `object` type allows for dynamic typing because it can actually represent +any type in the Mojo standard library, and the actual type is inferred at +runtime. (Actually, there's still more to do before it can represent all Mojo +types.) This is great for compatibility with Python and all of the flexibility +that it provides with dynamic types. However, this lack of type enforcement can +lead to runtime errors when a function receives or returns an unexpected type. + +For compatibility with Python, `object` values are passed using [object +reference +semantics](/mojo/manual/values/value-semantics#python-style-reference-semantics). +As such, the `object` type is not compatible with the [argument +conventions](/mojo/manual/values/ownership#argument-conventions) that +enforce value semantics. So, be careful if using `object` values alongside other +strongly-typed values—their behavior might be inconsistent because `object` is +the only type in the standard library that does not conform to [full value +semantics](/mojo/manual/values/value-semantics#intro-to-value-semantics). + +:::note TODO + +The `object` type is still a work in progress. It doesn't support all of the +possible underlying types, for example. + +::: + +## Function arguments + +As noted in the previous sections, there are a few differences between how `def` +and `fn` functions treat arguments. But most of the time they are the same. + +As noted, there are some differences in *argument conventions*. +Argument conventions are discussed in much more detail in the page on +[Ownership](/mojo/manual/values/ownership#argument-conventions). + +The other difference is that `def` functions don't need to specify an argument's +type. If no type is specified, the argument is passed as an +[`object`](/mojo/stdlib/builtin/object/object). + +The remaining rules for arguments described in this section apply to both `def` +and `fn` functions. + +### Optional arguments + +An optional argument is one that includes a default value, such as the `exp` +argument here: + +```mojo +fn my_pow(base: Int, exp: Int = 2) -> Int: + return base ** exp + +fn use_defaults(): + # Uses the default value for `exp` + var z = my_pow(3) + print(z) +``` + +However, you cannot define a default value for an argument that's declared as +[`inout`](/mojo/manual/values/ownership#mutable-arguments-inout). + +Any optional arguments must appear after any required arguments. [Keyword-only +arguments](#positional-only-and-keyword-only-arguments), discussed later, can +also be either required or optional. + +### Keyword arguments + +You can also use keyword arguments when calling a function. Keyword arguments +are specified using the format +argument_name = argument_value. +You can pass keyword arguments in any order: + +```mojo +fn my_pow(base: Int, exp: Int = 2) -> Int: + return base ** exp + +fn use_keywords(): + # Uses keyword argument names (with order reversed) + var z = my_pow(exp=3, base=2) + print(z) +``` + +### Variadic arguments + +Variadic arguments let a function accept a variable number of arguments. To +define a function that takes a variadic argument, use the variadic argument +syntax *argument_name: + +```mojo +fn sum(*values: Int) -> Int: + var sum: Int = 0 + for value in values: + sum = sum + value + return sum +``` + +The variadic argument `values` here is a placeholder that accepts any number of +passed positional arguments. + +You can define zero or more arguments before the variadic argument. When calling +the function, any remaining positional arguments are assigned to the variadic +argument, so any arguments declared **after** the variadic argument can only be +specified by keyword (see +[Positional-only and keyword-only arguments](#positional-only-and-keyword-only-arguments)). + +Variadic arguments can be divided into two categories: + +* Homogeneous variadic arguments, where all of the passed arguments are the same + type—all `Int`, or all `String`, for example. +* Heterogeneous variadic arguments, which can accept a set of different argument + types. + +The following sections describe how to work with homogeneous and heterogeneous +variadic arguments. + +:::note Variadic parameters + +Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments +(parameters are used for compile-time metaprogramming). Variadic parameters +are supported, but with some limitations—for details see +[variadic parameters](/mojo/manual/parameters/#variadic-parameters). + +::: + +#### Homogeneous variadic arguments + +When defining a homogeneous variadic argument, use *argument_name: argument_type: + +```mojo +def greet(*names: String): + ... +``` + +Inside the function body, the variadic argument is available as an iterable list +for ease of use. Currently there are some differences in handling the list +depending on whether the arguments are register-passable types (such as `Int`) +or memory-only types (such as `String`). TODO: We hope to remove these +differences in the future. + +Register-passable types, such as `Int`, are available as a +[`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) type. As +shown in the previous example, you can iterate over the values using a `for..in` +loop. + +```mojo +fn sum(*values: Int) -> Int: + var sum: Int = 0 + for value in values: + sum = sum+value + return sum +``` + +Memory-only types, such as `String`, are available as a +[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem). +Iterating over this list directly with a `for..in` loop currently produces a +[`Reference`](/mojo/stdlib/memory/reference/Reference) for each value instead +of the value itself. You must add an empty subscript operator `[]` to +dereference the reference and retrieve the value: + +```mojo +def make_worldly(inout *strs: String): + # Requires extra [] to dereference the reference for now. + for i in strs: + i[] += " world" + +``` + +Alternately, subscripting into a `VariadicListMem` returns the argument value, +and doesn't require any dereferencing: + +```mojo +fn make_worldly(inout *strs: String): + # This "just works" as you'd expect! + for i in range(len(strs)): + strs[i] += " world" +``` + +#### Heterogeneous variadic arguments + +Implementing heterogeneous variadic arguments is somewhat more complicated than +homogeneous variadic arguments. Writing generic code to handle multiple argument +types requires [traits](/mojo/manual/traits) and +[parameters](/mojo/manual/parameters/). So the syntax may look a little +unfamiliar if you haven't worked with those features. The signature for a +function with a heterogeneous variadic argument looks like this: + +```mojo +def count_many_things[*ArgTypes: Intable](*args: *ArgTypes): + ... +``` + +The parameter list, `[*ArgTypes: Intable]` specifies that the function takes an +`ArgTypes` parameter, which is a list of types, all of which conform to the +[`Intable`](/mojo/stdlib/builtin/int/Intable) trait. The argument list, +`(*args: *ArgTypes)` has the familiar `*args` for the variadic argument, but +instead of a single type, its type is defined as *list* of types, `*ArgTypes`. + +This means that each argument in `args` has a corresponding type in `ArgTypes`, +so args[n] is of type ArgTypes[n]. + +Inside the function, `args` is available as a +[`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack). The easiest +way to work with the arguments is to use the `each()` method to iterate through +the `VariadicPack`: + +```mojo +fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int: + var total = 0 + + @parameter + fn add[Type: Intable](value: Type): + total += int(value) + + args.each[add]() + return total + +print(count_many_things(5, 11.7, 12)) +``` + +```output +28 +``` + +In the example above, the `add()` function is called for each argument in turn, +with the appropriate `value` and `Type` values. For instance, `add()` is first +called with `value=5` and `Type=Int`, then with `value=11.7` and `Type=Float64`. + +Also, note that when calling `count_many_things()`, you don't actually pass in +a list of argument types. You only need to pass in the arguments, and Mojo +generates the `ArgTypes` list itself. + +As a small optimization, if your function is likely to be called with a single +argument frequently, you can define your function with a single argument +followed by a variadic argument. This lets the simple case bypass populating and +iterating through the `VariadicPack`. + +For example, given a `print_string()` function that prints a single string, you +could re-implement the variadic `print()` function with code like this: + +```mojo +fn print_string(s: String): + print(s, end="") + +fn print_many[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): + print_string(str(first)) + + @parameter + fn print_elt[T: Stringable](a: T): + print_string(" ") + print_string(str(a)) + rest.each[print_elt]() +print_many("Bob") +``` + +```output +Bob +``` + +If you call `print_many()` with a single argument, it calls `print_string()` +directly. The `VariadicPack` is empty, so `each()` returns immediately without +calling the `print_elt()` function. + +#### Variadic keyword arguments + +Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic +keyword arguments allow the user to pass an arbitrary number of keyword +arguments. To define a function that takes a variadic keyword argument, use the +variadic keyword argument syntax **kw_argument_name: + +```mojo +fn print_nicely(**kwargs: Int) raises: + for key in kwargs.keys(): + print(key[], "=", kwargs[key[]]) + + # prints: + # `a = 7` + # `y = 8` +print_nicely(a=7, y=8) +``` + +In this example, the argument name `kwargs` is a placeholder that accepts any +number of keyword arguments. Inside the body of the function, you can access +the arguments as a dictionary of keywords and argument values (specifically, +an instance of +[`OwnedKwargsDict`](/mojo/stdlib/collections/dict/OwnedKwargsDict)). + +There are currently a few limitations: + +* Variadic keyword arguments are always implicitly treated as if they + were declared with the `owned` [argument + convention](/mojo/manual/values/ownership#argument-conventions), and + can't be declared otherwise: + + ```mojo + # Not supported yet. + fn borrowed_var_kwargs(borrowed **kwargs: Int): ... + ``` + +* All the variadic keyword arguments must have the same type, and this + determines the type of the argument dictionary. For example, if the argument + is `**kwargs: Float64` then the argument dictionary will be a + `OwnedKwargsDict[Float64]`. + +* The argument type must conform to the + [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. + That is, the type must be both [`Movable`](/mojo/stdlib/builtin/value/Movable) + and [`Copyable`](/mojo/stdlib/builtin/value/Copyable). + +* Dictionary unpacking is not supported yet: + + ```mojo + fn takes_dict(d: Dict[String, Int]): + print_nicely(**d) # Not supported yet. + ``` + +* Variadic keyword *parameters* are not supported yet: + + ```mojo + # Not supported yet. + fn var_kwparams[**kwparams: Int](): ... + ``` + +### Positional-only and keyword-only arguments + +When defining a function, you can restrict some arguments so that they can only +be passed as positional arguments, or they can only be passed as keyword +arguments. + +To define positional-only arguments, add a slash character (`/`) to the +argument list. Any arguments before the `/` are positional-only: they can't be +passed as keyword arguments. For example: + +```mojo +fn min(a: Int, b: Int, /) -> Int: + return a if a < b else b +``` + +This `min()` function can be called with `min(1, 2)` but can't be called using +keywords, like `min(a=1, b=2)`. + +There are several reasons you might want to write a function with +positional-only arguments: + +* The argument names aren't meaningful for the the caller. +* You want the freedom to change the argument names later on without breaking + backward compatibility. + +For example, in the `min()` function, the argument names don't add any real +information, and there's no reason to specify arguments by keyword. + +For more information on positional-only arguments, see [PEP 570 – Python +Positional-Only Parameters](https://peps.python.org/pep-0570/). + +Keyword-only arguments are the inverse of positional-only arguments: they can +only be specified by keyword. If a function accepts variadic arguments, any +arguments defined *after* the variadic arguments are treated as keyword-only. +For example: + +```mojo +fn sort(*values: Float64, ascending: Bool = True): ... +``` + +In this example, the user can pass any number of `Float64` values, optionally +followed by the keyword `ascending` argument: + +```mojo +var a = sort(1.1, 6.5, 4.3, ascending=False) +``` + +If the function doesn't accept variadic arguments, you can add a single star +(`*`) to the argument list to separate the keyword-only arguments: + +```mojo +fn kw_only_args(a1: Int, a2: Int, *, double: Bool) -> Int: + var product = a1 * a2 + if double: + return product * 2 + else: + return product +``` + +Keyword-only arguments often have default values, but this is not required. If a +keyword-only argument doesn't have a default value, it is a *required +keyword-only argument*. It must be specified, and it must be specified by +keyword. + +Any required keyword-only arguments must appear in the signature before +any optional keyword-only arguments. That is, arguments appear in the following +sequence a function signature: + +* Required positional arguments. +* Optional positional arguments. +* Variadic arguments. +* Required keyword-only arguments. +* Optional keyword-only arguments. +* Variadic keyword arguments. + +For more information on keyword-only arguments, see [PEP 3102 – Keyword-Only +Arguments](https://peps.python.org/pep-3102/). + +## Overloaded functions + +If a `def` function does not specify argument types, then it can accept any +data type and decide how to handle each type internally. This is nice when you +want expressive APIs that just work by accepting arbitrary inputs, so there's +usually no need to write function overloads for a `def` function. + +On the other hand, all `fn` functions must specify argument types, so if you +want a function to work with different data types, you need to implement +separate versions of the function that each specify different argument types. +This is called "overloading" a function. + +For example, here's an overloaded `add()` function that can accept either +`Int` or `String` types: + +```mojo +fn add(x: Int, y: Int) -> Int: + return x + y + +fn add(x: String, y: String) -> String: + return x + y +``` + +If you pass anything other than `Int` or `String` to the `add()` function, +you'll get a compiler error. That is, unless `Int` or `String` can implicitly +cast the type into their own type. For example, `String` includes an overloaded +version of its constructor (`__init__()`) that accepts a `StringLiteral` value. +Thus, you can also pass a `StringLiteral` to a function that expects a `String`. + +When resolving an overloaded function call, the Mojo compiler tries each +candidate function and uses the one that works (if only one version works), or +it picks the closest match (if it can determine a close match), or it reports +that the call is ambiguous (if it can’t figure out which one to pick). + +If the compiler can't figure out which function to use, you can resolve the +ambiguity by explicitly casting your value to a supported argument type. For +example, in the following code, we want to call the overloaded `foo()` +function, but both implementations accept an argument that supports [implicit +conversion](/mojo/manual/variables#implicit-type-conversion) from +`StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a +compiler error. We can fix it by casting the value to the type we really want: + +```mojo +@value +struct MyString: + fn __init__(out self, string: StringLiteral): + pass + +fn foo(name: String): + print("String") + +fn foo(name: MyString): + print("MyString") + +fn call_foo(): + alias string: StringLiteral = "Hello" + # foo(string) # This call is ambiguous because two `foo` functions match it + foo(MyString(string)) +``` + +When resolving an overloaded function, Mojo does not consider the return type +or other contextual information at the call site—only the argument types affect +which function is selected. + +Overloading also works with combinations of both `fn` and `def` functions. +For example, you could define multiple `fn` function overloads and then one +or more `def` versions that don't specify all argument types, as a fallback. + +:::note + +Although we haven't discussed +[parameters](/mojo/manual/parameters/) yet (they're +different from function arguments, and used for compile-time metaprogramming), +you can also overload functions based on parameter types. + +::: diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb deleted file mode 100644 index 9329b7aebb..0000000000 --- a/docs/manual/lifecycle/death.ipynb +++ /dev/null @@ -1,548 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Death of a value\n", - "sidebar_position: 3\n", - "description: An explanation of when and how Mojo destroys values.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As soon as a value/object is no longer used, Mojo destroys it. Mojo does _not_\n", - "wait until the end of a code block—or even until the end of an expression—to\n", - "destroy an unused value. It destroys values using an “as soon as possible”\n", - "(ASAP) destruction policy that runs after every sub-expression. Even within an\n", - "expression like `a+b+c+d`, Mojo destroys the intermediate values as soon as\n", - "they're no longer needed.\n", - "\n", - "Mojo uses static compiler analysis to find the point where a value is last used.\n", - "Then, Mojo immediately ends the value's lifetime and calls the `__del__()`\n", - "destructor to perform any necessary cleanup for the type. \n", - "\n", - "For example, notice when the `__del__()` destructor is called for each instance\n", - "of `MyPet`:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loki\n", - "Destruct Loki\n", - "Destruct Charlie\n", - "Sylvie\n", - "Destruct Sylvie\n" - ] - } - ], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __del__(owned self):\n", - " print(\"Destruct\", self.name)\n", - "\n", - "fn pets():\n", - " var a = MyPet(\"Loki\", 4)\n", - " var b = MyPet(\"Sylvie\", 2)\n", - " print(a.name)\n", - " # a.__del__() runs here for \"Loki\"\n", - "\n", - " a = MyPet(\"Charlie\", 8)\n", - " # a.__del__() runs immediately because \"Charlie\" is never used\n", - "\n", - " print(b.name)\n", - " # b.__del__() runs here\n", - "\n", - "pets()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that each initialization of a value is matched with a call to the\n", - "destructor, and `a` is actually destroyed multiple times—once for each time it receives\n", - "a new value.\n", - "\n", - "Also notice that this `__del__()` implementation doesn't actually do\n", - "anything. Most structs don't require a custom destructor, and Mojo automatically\n", - "adds a no-op destructor if you don't define one.\n", - "\n", - "### Default destruction behavior\n", - "\n", - "You may be wondering how Mojo can destroy a type without a custom destructor, or\n", - "why a no-op destructor is useful. If a type is simply a collection of fields,\n", - "like the `MyPet` example, Mojo only needs to destroy the fields: `MyPet` doesn't\n", - "dynamically allocate memory or use any long-lived resources (like file handles).\n", - "There's no special action to take when a `MyPet` value is destroyed.\n", - "\n", - "Looking at the individual fields, `MyPet` includes an `Int` and a `String`. The\n", - "`Int` is what Mojo calls a _trivial type_. It's a statically-sized bundle of \n", - "bits. Mojo knows exactly how big it is, so those bits can be reused to store\n", - "something else.\n", - "\n", - "The `String` value is a little more complicated. Mojo strings are mutable. The\n", - "`String` object has an internal buffer—a\n", - "[`List`](/mojo/stdlib/collections/list/List) field,\n", - "which holds the characters that make up the string. A `List` stores\n", - "its contents in dynamically allocated memory on the heap, so the string can\n", - "grow or shrink. The string itself doesn't have any special destructor logic,\n", - "but when Mojo destroys a string, it calls the destructor for the\n", - "`List` field, which de-allocates the memory.\n", - "\n", - "Since `String` and `Int` don't require any custom destructor logic, they both\n", - "have no-op destructors: literally, `__del__()` methods that don't do anything.\n", - "This may seem pointless, but it means that Mojo can call the destructor on any\n", - "value when its lifetime ends. This makes it easier to write generic containers\n", - "and algorithms.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Benefits of ASAP destruction\n", - "\n", - "Similar to other languages, Mojo follows the principle that objects/values\n", - "acquire resources in a constructor (`__init__()`) and release resources in a\n", - "destructor (`__del__()`). However, Mojo's ASAP destruction has some advantages\n", - "over scope-based destruction (such as the C++ [RAII\n", - "pattern](https://en.cppreference.com/w/cpp/language/raii), which waits until\n", - "the end of the code scope to destroy values):\n", - "\n", - "- Destroying values immediately at last-use composes nicely with the \"move\"\n", - " optimization, which transforms a \"copy+del\" pair into a \"move\" operation.\n", - "\n", - "- Destroying values at end-of-scope in C++ is problematic for some common\n", - " patterns like tail recursion, because the destructor call happens after the\n", - " tail call. This can be a significant performance and memory problem for\n", - " certain functional programming patterns, which is not a problem in Mojo,\n", - " because the destructor call always happens before the tail call.\n", - "\n", - "Additionally, Mojo's ASAP destruction works great within Python-style `def`\n", - "functions. That's because Python doesn’t really provide scopes beyond a\n", - "function scope, so the Python garbage collector cleans up resources more often\n", - "than a scope-based destruction policy would. However, Mojo does not use a\n", - "garbage collector, so the ASAP destruction policy provides destruction\n", - "guarantees that are even more fine-grained than in Python.\n", - "\n", - "The Mojo destruction policy is more similar to how Rust and Swift work, because\n", - "they both have strong value ownership tracking and provide memory safety. One\n", - "difference is that Rust and Swift require the use of a [dynamic \"drop\n", - "flag\"](https://doc.rust-lang.org/nomicon/drop-flags.html)—they maintain hidden\n", - "shadow variables to keep track of the state of your values to provide safety.\n", - "These are often optimized away, but the Mojo approach eliminates this overhead\n", - "entirely, making the generated code faster and avoiding ambiguity." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Destructor\n", - "\n", - "Mojo calls a value's destructor (`__del__()` method) when the value's lifetime\n", - "ends (typically the point at which the value is last used). As we mentioned\n", - "earlier, Mojo provides a default, no-op destructor for all types, so in most\n", - "cases you don't need to define the `__del__()` method.\n", - "\n", - "You should define the `__del__()` method to perform any kind of cleanup the\n", - "type requires. Usually, that includes freeing memory for any fields where you\n", - "dynamically allocated memory (for example, via `UnsafePointer`) and\n", - "closing any long-lived resources such as file handles.\n", - "\n", - "However, any struct that is just a simple collection of other types does not\n", - "need to implement the destructor.\n", - "\n", - "For example, consider this simple struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, name: String, age: Int):\n", - " self.name = name\n", - " self.age = age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There's no need to define the `__del__()` destructor for this, because it's a\n", - "simple collection of other types (`String` and `Int`), and it doesn't \n", - "dynamically allocate memory. \n", - "\n", - "Whereas, the following struct must define the `__del__()` method to free the\n", - "memory allocated by its\n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer):" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import UnsafePointer\n", - "\n", - "struct HeapArray:\n", - " var data: UnsafePointer[Int]\n", - " var size: Int\n", - "\n", - " fn __init__(out self, size: Int, val: Int):\n", - " self.size = size\n", - " self.data = UnsafePointer[Int].alloc(self.size)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(val)\n", - "\n", - " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", - " self.data.free()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that a pointer doesn't _own_ any values in the memory it points to, so\n", - "when a pointer is destroyed, Mojo doesn't call the destructors on those values.\n", - "\n", - "So in the `HeapArray` example above, calling `free()` on the pointer releases\n", - "the memory, but doesn't call the destructors on the stored values. To invoke\n", - "the destructors, use the `destroy_pointee()` method provided by the \n", - "`UnsafePointer` type.\n", - "\n", - ":::note\n", - "\n", - "You can't just call the destructor explicitly. Because `__del__()`\n", - "takes `self` as an `owned` value, and owned arguments are copied by default,\n", - "`foo.__del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n", - "destroys a value, however, it passes in the original value as `self`, not a\n", - "copy.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "It's important to notice that the `__del__()` method is an \"extra\" cleanup\n", - "event, and your implementation does not override any default destruction\n", - "behaviors. For example, Mojo still destroys all the fields in `MyPet` even\n", - "if you implement `__del__()` to do nothing:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, name: String, age: Int):\n", - " self.name = name\n", - " self.age = age\n", - "\n", - " fn __del__(owned self):\n", - " # Mojo destroys all the fields when they're last used\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, the `self` value inside the `__del__()` destructor is still whole (so\n", - "all fields are still usable) until the destructor returns, as we'll discuss\n", - "more in the following section." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Field lifetimes\n", - "\n", - "In addition to tracking the lifetime of all objects in a program, Mojo also\n", - "tracks each field of a structure independently. That is, Mojo keeps track of\n", - "whether a \"whole object\" is fully or partially initialized/destroyed, and it\n", - "destroys each field independently with its ASAP destruction policy.\n", - "\n", - "For example, consider this code that changes the value of a field:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - "fn use_two_strings():\n", - " var pet = MyPet(\"Po\", 8)\n", - " print(pet.name)\n", - " # pet.name.__del__() runs here, because this instance is\n", - " # no longer used; it's replaced below\n", - "\n", - " pet.name = String(\"Lola\") # Overwrite pet.name\n", - " print(pet.name)\n", - " # pet.__del__() runs here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `pet.name` field is destroyed after the first `print()`, because Mojo knows\n", - "that it will be overwritten below. You can also see this behavior when using the\n", - "transfer sigil:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "fn consume(owned arg: String):\n", - " pass\n", - "\n", - "fn use(arg: MyPet):\n", - " print(arg.name)\n", - "\n", - "fn consume_and_use():\n", - " var pet = MyPet(\"Selma\", 5)\n", - " consume(pet.name^)\n", - " # pet.name.__moveinit__() runs here, which destroys pet.name\n", - " # Now pet is only partially initialized\n", - "\n", - " # use(pet) # This fails because pet.name is uninitialized\n", - "\n", - " pet.name = String(\"Jasper\") # All together now\n", - " use(pet) # This is ok\n", - " # pet.__del__() runs here (and only if the object is whole)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the code transfers ownership of the `name` field to `consume()`.\n", - "For a period of time after that, the `name` field is uninitialized.\n", - "Then `name` is reinitialized before it is passed to the `use()` function. If you\n", - "try calling `use()` before `name` is re-initialized, Mojo rejects the code\n", - "with an uninitialized field error.\n", - "\n", - "Also, if you don't re-initialize the name by the end of the `pet` lifetime, the\n", - "compiler complains because it's unable to destroy a partially initialized\n", - "object.\n", - "\n", - "Mojo's policy here is powerful and intentionally straight-forward: fields can\n", - "be temporarily transferred, but the \"whole object\" must be constructed with the\n", - "aggregate type’s initializer and destroyed with the aggregate destructor. This\n", - "means it's impossible to create an object by initializing only its fields, and\n", - "it's likewise impossible to destroy an object by destroying only its fields." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field lifetimes during destruct and move\n", - "\n", - "The consuming-move constructor and destructor face an interesting situation\n", - "with field lifetimes, because, unlike other lifecycle methods, they both take\n", - "an instance of their own type as an `owned` argument, which is about to be\n", - "destroyed. You don't really need to worry about this detail when implementing\n", - "these methods, but it might help you better understand field lifetimes.\n", - "\n", - "Just to recap, the move constructor and destructor method signatures\n", - "look like this:\n", - "\n", - "```mojo\n", - "struct TwoStrings:\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " # Initializes a new `self` by consuming the contents of `existing`\n", - " fn __del__(owned self):\n", - " # Destroys all resources in `self`\n", - "```\n", - "\n", - ":::note\n", - "\n", - "There are two kinds of \"self\" here: capitalized `Self` is an alias\n", - "for the current type name (used as a type specifier for the `existing`\n", - "argument), whereas lowercase `self` is the argument name for the\n", - "implicitly-passed reference to the current instance (also called \"this\" in\n", - "other languages, and also implicitly a `Self` type).\n", - "\n", - ":::\n", - "\n", - "Both of these methods face an interesting but obscure problem: they both must\n", - "dismantle the `existing`/`self` value that's `owned`. That is, `__moveinit__()`\n", - "implicitly destroys sub-elements of `existing` in order to transfer ownership\n", - "to a new instance (read more about the [move\n", - "constructor](/mojo/manual/lifecycle/life#move-constructor)),\n", - "while `__del__()` implements the deletion logic for its `self`. As such, they\n", - "both need to own and transform elements of the `owned` value, and they\n", - "definitely don’t want the original `owned` value's destructor to also run—that\n", - "could result in a double-free error, and in the case of the `__del__()` method,\n", - "it would become an infinite loop.\n", - "\n", - "To solve this problem, Mojo handles these two methods specially by assuming\n", - "that their whole values are destroyed upon reaching any return from the method.\n", - "This means that the whole object may be used as usual, up until the field\n", - "values are transferred or the method returns.\n", - "\n", - "For example, the following code works as you would expect (within the\n", - "destructor, we can still pass ownership of a field value to another function,\n", - "and there's no infinite loop to destroy `self`):" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "fn consume(owned str: String):\n", - " print('Consumed', str)\n", - "\n", - "struct TwoStrings:\n", - " var str1: String\n", - " var str2: String\n", - "\n", - " fn __init__(out self, one: String):\n", - " self.str1 = one\n", - " self.str2 = String(\"bar\")\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " self.str1 = existing.str1\n", - " self.str2 = existing.str2\n", - "\n", - " fn __del__(owned self):\n", - " self.dump() # Self is still whole here\n", - " # Mojo calls self.str2.__del__() since str2 isn't used anymore\n", - "\n", - " consume(self.str1^)\n", - " # self.str1 has been transferred so it is also destroyed now;\n", - " # `self.__del__()` is not called (avoiding an infinite loop).\n", - "\n", - " fn dump(inout self):\n", - " print('str1:', self.str1)\n", - " print('str2:', self.str2)\n", - "\n", - "fn use_two_strings():\n", - " var two_strings = TwoStrings(\"foo\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Explicit lifetimes\n", - "\n", - "So far, we've described how Mojo destroys a value at the point it's last used,\n", - "and this works great in almost all situations. However, there are very rare\n", - "situations in which Mojo simply cannot predict this correctly and will destroy\n", - "a value that is still referenced through some other means.\n", - "\n", - "For instance, perhaps you're building a type with a field that carries a pointer\n", - "to another field. The Mojo compiler won't be able to reason about the pointer,\n", - "so it might destroy a field (`obj1`) when that field is technically no longer\n", - "used, even though another field (`obj2`) still holds a pointer to part of it.\n", - "So, you might need to keep `obj1` alive until you can execute some special\n", - "logic in the destructor or move initializer.\n", - "\n", - "You can force Mojo to keep a value alive up to a certain point by assigning the\n", - "value to the `_` discard pattern at the point where it's okay to destroy it.\n", - "For example:\n", - "\n", - "```mojo\n", - "fn __del__(owned self):\n", - " self.dump() # Self is still whole here\n", - "\n", - " consume(self.obj2^)\n", - " _ = self.obj1\n", - " # Mojo keeps `obj1` alive until here, after its \"last use\"\n", - "```\n", - "\n", - "In this case, if `consume()` refers to some value in `obj1` somehow, this\n", - "ensures that Mojo does not destroy `obj1` until after the call to `consume()`,\n", - "because assignment to the discard variable `_` is actually the last use.\n", - "\n", - "For other situations, you can also scope the lifetime of a value using the\n", - "Python-style [`with`\n", - "statement](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement).\n", - "That is, for any value defined at the entrance to a `with` statement, Mojo will\n", - "keep that value alive until the end of the `with` statement. For example:\n", - "\n", - "```mojo\n", - "with open(\"my_file.txt\", \"r\") as file:\n", - " print(file.read())\n", - "\n", - " # Other stuff happens here (whether using `file` or not)...\n", - " foo()\n", - " # `file` is alive up to the end of the `with` statement.\n", - "\n", - "# `file` is destroyed when the statement ends.\n", - "bar()\n", - "```" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/lifecycle/death.mdx b/docs/manual/lifecycle/death.mdx new file mode 100644 index 0000000000..4494e8b446 --- /dev/null +++ b/docs/manual/lifecycle/death.mdx @@ -0,0 +1,411 @@ +--- +title: Death of a value +sidebar_position: 3 +description: An explanation of when and how Mojo destroys values. +--- + +As soon as a value/object is no longer used, Mojo destroys it. Mojo does *not* +wait until the end of a code block—or even until the end of an expression—to +destroy an unused value. It destroys values using an “as soon as possible” +(ASAP) destruction policy that runs after every sub-expression. Even within an +expression like `a+b+c+d`, Mojo destroys the intermediate values as soon as +they're no longer needed. + +Mojo uses static compiler analysis to find the point where a value is last used. +Then, Mojo immediately ends the value's lifetime and calls the `__del__()` +destructor to perform any necessary cleanup for the type. + +For example, notice when the `__del__()` destructor is called for each instance +of `MyPet`: + +```mojo +@value +struct MyPet: + var name: String + var age: Int + + fn __del__(owned self): + print("Destruct", self.name) + +fn pets(): + var a = MyPet("Loki", 4) + var b = MyPet("Sylvie", 2) + print(a.name) + # a.__del__() runs here for "Loki" + + a = MyPet("Charlie", 8) + # a.__del__() runs immediately because "Charlie" is never used + + print(b.name) + # b.__del__() runs here + +pets() +``` + +```output +Loki +Destruct Loki +Destruct Charlie +Sylvie +Destruct Sylvie +``` + +Notice that each initialization of a value is matched with a call to the +destructor, and `a` is actually destroyed multiple times—once for each time it receives +a new value. + +Also notice that this `__del__()` implementation doesn't actually do +anything. Most structs don't require a custom destructor, and Mojo automatically +adds a no-op destructor if you don't define one. + +### Default destruction behavior + +You may be wondering how Mojo can destroy a type without a custom destructor, or +why a no-op destructor is useful. If a type is simply a collection of fields, +like the `MyPet` example, Mojo only needs to destroy the fields: `MyPet` doesn't +dynamically allocate memory or use any long-lived resources (like file handles). +There's no special action to take when a `MyPet` value is destroyed. + +Looking at the individual fields, `MyPet` includes an `Int` and a `String`. The +`Int` is what Mojo calls a *trivial type*. It's a statically-sized bundle of +bits. Mojo knows exactly how big it is, so those bits can be reused to store +something else. + +The `String` value is a little more complicated. Mojo strings are mutable. The +`String` object has an internal buffer—a +[`List`](/mojo/stdlib/collections/list/List) field, +which holds the characters that make up the string. A `List` stores +its contents in dynamically allocated memory on the heap, so the string can +grow or shrink. The string itself doesn't have any special destructor logic, +but when Mojo destroys a string, it calls the destructor for the +`List` field, which de-allocates the memory. + +Since `String` and `Int` don't require any custom destructor logic, they both +have no-op destructors: literally, `__del__()` methods that don't do anything. +This may seem pointless, but it means that Mojo can call the destructor on any +value when its lifetime ends. This makes it easier to write generic containers +and algorithms. + +### Benefits of ASAP destruction + +Similar to other languages, Mojo follows the principle that objects/values +acquire resources in a constructor (`__init__()`) and release resources in a +destructor (`__del__()`). However, Mojo's ASAP destruction has some advantages +over scope-based destruction (such as the C++ [RAII +pattern](https://en.cppreference.com/w/cpp/language/raii), which waits until +the end of the code scope to destroy values): + +* Destroying values immediately at last-use composes nicely with the "move" + optimization, which transforms a "copy+del" pair into a "move" operation. + +* Destroying values at end-of-scope in C++ is problematic for some common + patterns like tail recursion, because the destructor call happens after the + tail call. This can be a significant performance and memory problem for + certain functional programming patterns, which is not a problem in Mojo, + because the destructor call always happens before the tail call. + +Additionally, Mojo's ASAP destruction works great within Python-style `def` +functions. That's because Python doesn’t really provide scopes beyond a +function scope, so the Python garbage collector cleans up resources more often +than a scope-based destruction policy would. However, Mojo does not use a +garbage collector, so the ASAP destruction policy provides destruction +guarantees that are even more fine-grained than in Python. + +The Mojo destruction policy is more similar to how Rust and Swift work, because +they both have strong value ownership tracking and provide memory safety. One +difference is that Rust and Swift require the use of a [dynamic "drop +flag"](https://doc.rust-lang.org/nomicon/drop-flags.html)—they maintain hidden +shadow variables to keep track of the state of your values to provide safety. +These are often optimized away, but the Mojo approach eliminates this overhead +entirely, making the generated code faster and avoiding ambiguity. + +## Destructor + +Mojo calls a value's destructor (`__del__()` method) when the value's lifetime +ends (typically the point at which the value is last used). As we mentioned +earlier, Mojo provides a default, no-op destructor for all types, so in most +cases you don't need to define the `__del__()` method. + +You should define the `__del__()` method to perform any kind of cleanup the +type requires. Usually, that includes freeing memory for any fields where you +dynamically allocated memory (for example, via `UnsafePointer`) and +closing any long-lived resources such as file handles. + +However, any struct that is just a simple collection of other types does not +need to implement the destructor. + +For example, consider this simple struct: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, name: String, age: Int): + self.name = name + self.age = age +``` + +There's no need to define the `__del__()` destructor for this, because it's a +simple collection of other types (`String` and `Int`), and it doesn't +dynamically allocate memory. + +Whereas, the following struct must define the `__del__()` method to free the +memory allocated by its +[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer): + +```mojo +from memory import UnsafePointer + +struct HeapArray: + var data: UnsafePointer[Int] + var size: Int + + fn __init__(out self, size: Int, val: Int): + self.size = size + self.data = UnsafePointer[Int].alloc(self.size) + for i in range(self.size): + (self.data + i).init_pointee_copy(val) + + fn __del__(owned self): + for i in range(self.size): + (self.data + i).destroy_pointee() + self.data.free() +``` + +Note that a pointer doesn't *own* any values in the memory it points to, so +when a pointer is destroyed, Mojo doesn't call the destructors on those values. + +So in the `HeapArray` example above, calling `free()` on the pointer releases +the memory, but doesn't call the destructors on the stored values. To invoke +the destructors, use the `destroy_pointee()` method provided by the +`UnsafePointer` type. + +:::note + +You can't just call the destructor explicitly. Because `__del__()` +takes `self` as an `owned` value, and owned arguments are copied by default, +`foo.__del__()` actually creates and destroys a *copy* of `foo`. When Mojo +destroys a value, however, it passes in the original value as `self`, not a +copy. + +::: + +It's important to notice that the `__del__()` method is an "extra" cleanup +event, and your implementation does not override any default destruction +behaviors. For example, Mojo still destroys all the fields in `MyPet` even +if you implement `__del__()` to do nothing: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, name: String, age: Int): + self.name = name + self.age = age + + fn __del__(owned self): + # Mojo destroys all the fields when they're last used + pass +``` + +However, the `self` value inside the `__del__()` destructor is still whole (so +all fields are still usable) until the destructor returns, as we'll discuss +more in the following section. + +## Field lifetimes + +In addition to tracking the lifetime of all objects in a program, Mojo also +tracks each field of a structure independently. That is, Mojo keeps track of +whether a "whole object" is fully or partially initialized/destroyed, and it +destroys each field independently with its ASAP destruction policy. + +For example, consider this code that changes the value of a field: + +```mojo +@value +struct MyPet: + var name: String + var age: Int + +fn use_two_strings(): + var pet = MyPet("Po", 8) + print(pet.name) + # pet.name.__del__() runs here, because this instance is + # no longer used; it's replaced below + + pet.name = String("Lola") # Overwrite pet.name + print(pet.name) + # pet.__del__() runs here +``` + +The `pet.name` field is destroyed after the first `print()`, because Mojo knows +that it will be overwritten below. You can also see this behavior when using the +transfer sigil: + +```mojo +fn consume(owned arg: String): + pass + +fn use(arg: MyPet): + print(arg.name) + +fn consume_and_use(): + var pet = MyPet("Selma", 5) + consume(pet.name^) + # pet.name.__moveinit__() runs here, which destroys pet.name + # Now pet is only partially initialized + + # use(pet) # This fails because pet.name is uninitialized + + pet.name = String("Jasper") # All together now + use(pet) # This is ok + # pet.__del__() runs here (and only if the object is whole) +``` + +Notice that the code transfers ownership of the `name` field to `consume()`. +For a period of time after that, the `name` field is uninitialized. +Then `name` is reinitialized before it is passed to the `use()` function. If you +try calling `use()` before `name` is re-initialized, Mojo rejects the code +with an uninitialized field error. + +Also, if you don't re-initialize the name by the end of the `pet` lifetime, the +compiler complains because it's unable to destroy a partially initialized +object. + +Mojo's policy here is powerful and intentionally straight-forward: fields can +be temporarily transferred, but the "whole object" must be constructed with the +aggregate type’s initializer and destroyed with the aggregate destructor. This +means it's impossible to create an object by initializing only its fields, and +it's likewise impossible to destroy an object by destroying only its fields. + +### Field lifetimes during destruct and move + +The consuming-move constructor and destructor face an interesting situation +with field lifetimes, because, unlike other lifecycle methods, they both take +an instance of their own type as an `owned` argument, which is about to be +destroyed. You don't really need to worry about this detail when implementing +these methods, but it might help you better understand field lifetimes. + +Just to recap, the move constructor and destructor method signatures +look like this: + +```mojo +struct TwoStrings: + fn __moveinit__(out self, owned existing: Self): + # Initializes a new `self` by consuming the contents of `existing` + fn __del__(owned self): + # Destroys all resources in `self` +``` + +:::note + +There are two kinds of "self" here: capitalized `Self` is an alias +for the current type name (used as a type specifier for the `existing` +argument), whereas lowercase `self` is the argument name for the +implicitly-passed reference to the current instance (also called "this" in +other languages, and also implicitly a `Self` type). + +::: + +Both of these methods face an interesting but obscure problem: they both must +dismantle the `existing`/`self` value that's `owned`. That is, `__moveinit__()` +implicitly destroys sub-elements of `existing` in order to transfer ownership +to a new instance (read more about the [move +constructor](/mojo/manual/lifecycle/life#move-constructor)), +while `__del__()` implements the deletion logic for its `self`. As such, they +both need to own and transform elements of the `owned` value, and they +definitely don’t want the original `owned` value's destructor to also run—that +could result in a double-free error, and in the case of the `__del__()` method, +it would become an infinite loop. + +To solve this problem, Mojo handles these two methods specially by assuming +that their whole values are destroyed upon reaching any return from the method. +This means that the whole object may be used as usual, up until the field +values are transferred or the method returns. + +For example, the following code works as you would expect (within the +destructor, we can still pass ownership of a field value to another function, +and there's no infinite loop to destroy `self`): + +```mojo +fn consume(owned str: String): + print('Consumed', str) + +struct TwoStrings: + var str1: String + var str2: String + + fn __init__(out self, one: String): + self.str1 = one + self.str2 = String("bar") + + fn __moveinit__(out self, owned existing: Self): + self.str1 = existing.str1 + self.str2 = existing.str2 + + fn __del__(owned self): + self.dump() # Self is still whole here + # Mojo calls self.str2.__del__() since str2 isn't used anymore + + consume(self.str1^) + # self.str1 has been transferred so it is also destroyed now; + # `self.__del__()` is not called (avoiding an infinite loop). + + fn dump(inout self): + print('str1:', self.str1) + print('str2:', self.str2) + +fn use_two_strings(): + var two_strings = TwoStrings("foo") +``` + +## Explicit lifetimes + +So far, we've described how Mojo destroys a value at the point it's last used, +and this works great in almost all situations. However, there are very rare +situations in which Mojo simply cannot predict this correctly and will destroy +a value that is still referenced through some other means. + +For instance, perhaps you're building a type with a field that carries a pointer +to another field. The Mojo compiler won't be able to reason about the pointer, +so it might destroy a field (`obj1`) when that field is technically no longer +used, even though another field (`obj2`) still holds a pointer to part of it. +So, you might need to keep `obj1` alive until you can execute some special +logic in the destructor or move initializer. + +You can force Mojo to keep a value alive up to a certain point by assigning the +value to the `_` discard pattern at the point where it's okay to destroy it. +For example: + +```mojo +fn __del__(owned self): + self.dump() # Self is still whole here + + consume(self.obj2^) + _ = self.obj1 + # Mojo keeps `obj1` alive until here, after its "last use" +``` + +In this case, if `consume()` refers to some value in `obj1` somehow, this +ensures that Mojo does not destroy `obj1` until after the call to `consume()`, +because assignment to the discard variable `_` is actually the last use. + +For other situations, you can also scope the lifetime of a value using the +Python-style [`with` +statement](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement). +That is, for any value defined at the entrance to a `with` statement, Mojo will +keep that value alive until the end of the `with` statement. For example: + +```mojo +with open("my_file.txt", "r") as file: + print(file.read()) + + # Other stuff happens here (whether using `file` or not)... + foo() + # `file` is alive up to the end of the `with` statement. + +# `file` is destroyed when the statement ends. +bar() +``` diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb deleted file mode 100644 index f4e3118941..0000000000 --- a/docs/manual/lifecycle/index.ipynb +++ /dev/null @@ -1,128 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Intro to value lifecycle\n", - "sidebar_position: 1\n", - "description: An introduction to the value lifecycle.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So far, we've explained how Mojo allows you to build high-performance code that\n", - "is memory safe _without_ manually managing memory, using Mojo's [ownership\n", - "model](/mojo/manual/values/ownership). However, Mojo is designed for\n", - "[systems programming](https://en.wikipedia.org/wiki/Systems_programming), which\n", - "often requires manual memory management for custom data types. So, Mojo lets\n", - "you do that as you see fit. To be clear, Mojo has no reference counter and no\n", - "garbage collector.\n", - "\n", - "Mojo also has no built-in data types with special privileges. All data types\n", - "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", - "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/collections/string/String)) are implemented as\n", - "[structs](/mojo/manual/structs). You can actually write your own\n", - "replacements for these types by using low-level primitives provided by\n", - "[MLIR dialects](/mojo/notebooks/BoolMLIR).\n", - "\n", - "What's great about the Mojo language is that it provides you these low-level\n", - "tools for systems programming, but within a framework that helps you build\n", - "things that are safe and easy to use from higher-level programs. That is, you\n", - "can get under the hood and write all the \"unsafe\" code you want, but as long as\n", - "you do so in accordance with Mojo's [value\n", - "semantics](/mojo/manual/values/value-semantics), the programmer instantiating\n", - "your type/object doesn't need to think about memory management at all, and the\n", - "behavior will be safe and predictable, thanks to [value\n", - "ownership](/mojo/manual/values/ownership).\n", - "\n", - "In summary, it's the responsibility of the type author to manage the memory and\n", - "resources for each value type, by implementing specific lifecycle methods, such\n", - "as the constructor, copy constructor, move constructor, and destructor, as\n", - "necessary. Mojo doesn't create any constructors by default, although it does\n", - "add a trivial, no-op destructor for types that don't define their own.\n", - "\n", - "In the following pages, we'll explain exactly how to define these lifecycle\n", - "methods in accordance with value semantics so your types play nicely with value\n", - "ownership." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Lifecycles and lifetimes\n", - "\n", - "First, let's clarify some terminology:\n", - "\n", - "- The \"lifecycle\" of a value is defined by various [dunder\n", - "methods](/mojo/manual/structs#special-methods) in a struct.\n", - "Each lifecycle event is handled by a different method,\n", - "such as the constructor (`__init__()`), the destructor (`__del__()`), the copy\n", - "constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`).\n", - "All values that are declared with the same type have the same lifecycle.\n", - "\n", - "- The \"lifetime\" of a variable is defined by the span of time during \n", - "program execution in which the variable is considered valid. The life of a \n", - "variable begins when its value is initialized (via `__init__()`, \n", - "`__copyinit__()` or `__moveinit__()`) and ends when the value is destroyed \n", - "(`__del__()`), or consumed in some other way (for example, as part of a \n", - "`__moveinit__()` call). \n", - "\n", - "No two values have the exact same lifetime, because every value is created and \n", - "destroyed at a different point in time (even if the difference is imperceptible).\n", - "\n", - ":::note Origin type\n", - "\n", - "The concept of lifetimes is related to the `origin` type, a Mojo primitive\n", - "used to track ownership. For most Mojo programming, you won't need to work with\n", - "`origin` values directly. For information, see [Lifetimes, origins and\n", - "references](/mojo/manual/values/lifetimes).\n", - "\n", - ":::\n", - "\n", - "The life of a value in Mojo begins when a variable is initialized and continues\n", - "up until the value is last used, at which point Mojo destroys it. Mojo destroys\n", - "every value/object as soon as it's no longer used, using an “as soon as\n", - "possible” (ASAP) destruction policy that runs after every sub-expression. The \n", - "Mojo compiler takes care of releasing resources after last use when needed.\n", - "\n", - "As you might imagine, keeping track of a value's life can be difficult if a\n", - "value is shared across functions many times during the life of a program.\n", - "However, Mojo makes this predictable partly through its [value\n", - "semantics](/mojo/manual/values/value-semantics) and [value\n", - "ownership](/mojo/manual/values/ownership) (both prerequisite readings for\n", - "the following sections). The final piece of the puzzle for lifetime management\n", - "is the value lifecycle: every value (defined in a struct) needs to implement\n", - "key lifecycle methods that define how a value is created and destroyed." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/lifecycle/index.mdx b/docs/manual/lifecycle/index.mdx new file mode 100644 index 0000000000..466d792b18 --- /dev/null +++ b/docs/manual/lifecycle/index.mdx @@ -0,0 +1,86 @@ +--- +title: Intro to value lifecycle +sidebar_position: 1 +description: An introduction to the value lifecycle. +--- + +So far, we've explained how Mojo allows you to build high-performance code that +is memory safe *without* manually managing memory, using Mojo's [ownership +model](/mojo/manual/values/ownership). However, Mojo is designed for +[systems programming](https://en.wikipedia.org/wiki/Systems_programming), which +often requires manual memory management for custom data types. So, Mojo lets +you do that as you see fit. To be clear, Mojo has no reference counter and no +garbage collector. + +Mojo also has no built-in data types with special privileges. All data types +in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool), +[`Int`](/mojo/stdlib/builtin/int/Int), and +[`String`](/mojo/stdlib/collections/string/String)) are implemented as +[structs](/mojo/manual/structs). You can actually write your own +replacements for these types by using low-level primitives provided by +[MLIR dialects](/mojo/notebooks/BoolMLIR). + +What's great about the Mojo language is that it provides you these low-level +tools for systems programming, but within a framework that helps you build +things that are safe and easy to use from higher-level programs. That is, you +can get under the hood and write all the "unsafe" code you want, but as long as +you do so in accordance with Mojo's [value +semantics](/mojo/manual/values/value-semantics), the programmer instantiating +your type/object doesn't need to think about memory management at all, and the +behavior will be safe and predictable, thanks to [value +ownership](/mojo/manual/values/ownership). + +In summary, it's the responsibility of the type author to manage the memory and +resources for each value type, by implementing specific lifecycle methods, such +as the constructor, copy constructor, move constructor, and destructor, as +necessary. Mojo doesn't create any constructors by default, although it does +add a trivial, no-op destructor for types that don't define their own. + +In the following pages, we'll explain exactly how to define these lifecycle +methods in accordance with value semantics so your types play nicely with value +ownership. + +## Lifecycles and lifetimes + +First, let's clarify some terminology: + +* The "lifecycle" of a value is defined by various [dunder + methods](/mojo/manual/structs#special-methods) in a struct. + Each lifecycle event is handled by a different method, + such as the constructor (`__init__()`), the destructor (`__del__()`), the copy + constructor (`__copyinit__()`), and the move constructor (`__moveinit__()`). + All values that are declared with the same type have the same lifecycle. + +* The "lifetime" of a variable is defined by the span of time during + program execution in which the variable is considered valid. The life of a + variable begins when its value is initialized (via `__init__()`, + `__copyinit__()` or `__moveinit__()`) and ends when the value is destroyed + (`__del__()`), or consumed in some other way (for example, as part of a + `__moveinit__()` call). + +No two values have the exact same lifetime, because every value is created and +destroyed at a different point in time (even if the difference is imperceptible). + +:::note Origin type + +The concept of lifetimes is related to the `origin` type, a Mojo primitive +used to track ownership. For most Mojo programming, you won't need to work with +`origin` values directly. For information, see [Lifetimes, origins and +references](/mojo/manual/values/lifetimes). + +::: + +The life of a value in Mojo begins when a variable is initialized and continues +up until the value is last used, at which point Mojo destroys it. Mojo destroys +every value/object as soon as it's no longer used, using an “as soon as +possible” (ASAP) destruction policy that runs after every sub-expression. The +Mojo compiler takes care of releasing resources after last use when needed. + +As you might imagine, keeping track of a value's life can be difficult if a +value is shared across functions many times during the life of a program. +However, Mojo makes this predictable partly through its [value +semantics](/mojo/manual/values/value-semantics) and [value +ownership](/mojo/manual/values/ownership) (both prerequisite readings for +the following sections). The final piece of the puzzle for lifetime management +is the value lifecycle: every value (defined in a struct) needs to implement +key lifecycle methods that define how a value is created and destroyed. diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb deleted file mode 100644 index 6c64745fdc..0000000000 --- a/docs/manual/lifecycle/life.ipynb +++ /dev/null @@ -1,926 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Life of a value\n", - "sidebar_position: 2\n", - "description: An explanation of when and how Mojo creates values.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The life of a value in Mojo begins when a variable is initialized and continues\n", - "up until the value is last used, at which point Mojo destroys it. This page\n", - "describes how every value in Mojo is created, copied, and moved. (The next\n", - "page describes [how values are\n", - "destroyed](/mojo/manual/lifecycle/death).)\n", - "\n", - "All data types in Mojo—including basic types in the standard library such as\n", - "[`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", - "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", - "[`String`](/mojo/stdlib/collections/string/String), up to complex types such\n", - "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", - "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", - "[struct](/mojo/manual/structs). This means the creation and\n", - "destruction of any piece of data follows the same lifecycle rules, and you can\n", - "define your own data types that work exactly the same way.\n", - "\n", - "Mojo structs don't get any default lifecycle methods, such as a\n", - "constructor, copy constructor, or move constructor. That means you can create\n", - "a struct without a constructor, but then you can't instantiate it, and it\n", - "would be useful only as a sort of namespace for static methods. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "struct NoInstances:\n", - " var state: Int\n", - "\n", - " @staticmethod\n", - " fn print_hello():\n", - " print(\"Hello world!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Without a constructor, this cannot be instantiated, so it has no lifecycle. The\n", - "`state` field is also useless because it cannot be initialized (Mojo structs do\n", - "not support default field values—you must initialize them in a constructor).\n", - "\n", - "So the only thing you can do is call the static method:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello world!\n" - ] - } - ], - "source": [ - "NoInstances.print_hello()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Constructor\n", - "\n", - "To create an instance of a Mojo type, it needs the `__init__()` constructor\n", - "method. The main responsibility of the constructor is to initialize all fields.\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, name: String, age: Int):\n", - " self.name = name\n", - " self.age = age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can create an instance:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "var mine = MyPet(\"Loki\", 4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An instance of `MyPet` can also be\n", - "[borrowed](/mojo/manual/values/ownership#borrowed-arguments-borrowed)\n", - "and destroyed, but it currently can't be copied or moved.\n", - "\n", - "We believe this is a good default starting point, because there are no built-in\n", - "lifecycle events and no surprise behaviors. You—the type author—must\n", - "explicitly decide whether and how the type can be copied or moved, by\n", - "implementing the copy and move constructors.\n", - "\n", - ":::note\n", - "\n", - "Mojo does not require a destructor to destroy an object. As long as\n", - "all fields in the struct are destructible (every type in the standard library\n", - "is destructible, except for\n", - "[pointers](/mojo/stdlib/memory/unsafe)), then Mojo knows how to destroy\n", - "the type when its lifetime ends. We'll discuss that more in [Death of a\n", - "value](/mojo/manual/lifecycle/death).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Overloading the constructor\n", - "\n", - "Like any other function/method, you can\n", - "[overload](/mojo/manual/functions#overloaded-functions) the\n", - "`__init__()` constructor to initialize the object with different arguments. For\n", - "example, you might want a default constructor that sets some default values and\n", - "takes no arguments, and then additional constructors that accept more arguments.\n", - "\n", - "Just be aware that, in order to modify any fields, each constructor must\n", - "declare the `self` argument with the [`inout`\n", - "convention](/mojo/manual/values/ownership#mutable-arguments-inout). If you\n", - "want to call one constructor from another, you simply call upon that\n", - "constructor as you would externally (you don't need to pass `self`).\n", - "\n", - "For example, here's how you can delegate work from an overloaded constructor:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self):\n", - " self.name = \"\"\n", - " self.age = 0\n", - "\n", - " fn __init__(out self, name: String):\n", - " self = MyPet()\n", - " self.name = name" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field initialization\n", - "\n", - "Notice in the previous example that, by the end of each constructor, all fields\n", - "must be initialized. That's the only requirement in the constructor.\n", - "\n", - "In fact, the `__init__()` constructor is smart enough to treat the `self`\n", - "object as fully initialized even before the constructor is finished, as long\n", - "as all fields are initialized. For example, this constructor can pass around\n", - "`self` as soon as all fields are initialized:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "fn use(arg: MyPet):\n", - " pass\n", - "\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, name: String, age: Int, cond: Bool):\n", - " self.name = name\n", - " if cond:\n", - " self.age = age\n", - " use(self) # Safe to use immediately!\n", - "\n", - " self.age = age\n", - " use(self) # Safe to use immediately!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Constructors and implicit conversion\n", - "\n", - "Mojo supports implicit conversion from one type to another. Implicit conversion\n", - "can happen when one of the following occurs:\n", - "\n", - "- You assign a value of one type to a variable with a different type.\n", - "- You pass a value of one type to a function that requires a different type.\n", - "\n", - "In both cases, implicit conversion is supported when the target type\n", - "defines a constructor that takes a single required, non-keyword argument of the\n", - "source type. For example:\n", - "\n", - "```mojo\n", - "var a = Source()\n", - "var b: Target = a\n", - "```\n", - "\n", - "Mojo implicitly converts the `Source` value in `a` to a `Target` value if \n", - "`Target` defines a matching constructor like this:\n", - "\n", - "```mojo\n", - "struct Target:\n", - " fn __init__(out self, s: Source): ...\n", - "```\n", - "\n", - "With implicit conversion, the assignment above is essentially identical to:\n", - "\n", - "```mojo\n", - "var b = Target(a)\n", - "```\n", - "\n", - "The constructor used for implicit conversion can take optional arguments, so\n", - "the following constructor would also support implicit conversion from `Source`\n", - "to `Target`:\n", - "\n", - "```mojo\n", - "struct Target:\n", - " fn __init__(out self, s: Source, reverse: Bool = False): ...\n", - "```\n", - "\n", - "Implicit conversion also occurs if the type doesn't declare its own constructor,\n", - "but instead uses the [`@value` decorator](#value-decorator), _and_ the type\n", - "has only one field. That's because Mojo automatically creates a member-wise\n", - "constructor for each field, and when there is only one field, that synthesized\n", - "constructor works exactly like a conversion constructor. For example, this\n", - "type also can convert a `Source` value to a `Target` value:\n", - "\n", - "```mojo\n", - "@value\n", - "struct Target:\n", - " var s: Source\n", - "```\n", - "\n", - "Implicit conversion can fail if Mojo can't unambiguously match the conversion to\n", - "a constructor. For example, if the target type has two overloaded constructors\n", - "that take different types, and each of those types supports an implicit\n", - "conversion from the source type, the compiler has two equally-valid paths to \n", - "convert the values:\n", - "\n", - "```mojo\n", - "struct A: \n", - " fn __init__(out self, s: Source): ...\n", - "\n", - "struct B: \n", - " fn __init__(out self, s: Source): ...\n", - "\n", - "struct Target:\n", - " fn __init__(out self, a: A): ...\n", - " fn __init__(out self, b: B): ...\n", - "\n", - "# Fails\n", - "var t = Target(Source())\n", - "```\n", - "\n", - "In this case, removing either one of the target type's constructors will fix the\n", - "problem.\n", - "\n", - "If you want to define a single-argument constructor, but you **don't** want\n", - "the types to implicitly convert, you can define the constructor with a \n", - "[keyword-only argument](/mojo/manual/functions#positional-only-and-keyword-only-arguments):\n", - "\n", - "```mojo\n", - "struct Target:\n", - " # does not support implicit conversion\n", - " fn __init__(out self, *, source: Source): ...\n", - "\n", - "# the constructor must be called with a keyword\n", - "var t = Target(source=a)\n", - "```\n", - "\n", - ":::note\n", - "\n", - "In the future we intend to provide a more explicit method of declaring whether\n", - "a constructor should support implicit conversion.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Copy constructor\n", - "\n", - "When Mojo encounters an assignment operator (`=`), it tries to make a copy of\n", - "the right-side value by calling upon that type's copy constructor: the\n", - "`__copyinit__()` method. Thus, it's the responsibility of the type author to\n", - "implement `__copyinit__()` so it returns a copy of the value.\n", - "\n", - "For example, the `MyPet` type above does not have a copy constructor,\n", - "so this code fails to compile:\n", - "\n", - "```mojo\n", - "var mine = MyPet(\"Loki\", 4)\n", - "var yours = mine # This requires a copy, but MyPet has no copy constructor\n", - "```\n", - "\n", - "To make it work, we need to add the copy constructor, like\n", - "this:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, name: String, age: Int):\n", - " self.name = name\n", - " self.age = age\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " self.name = existing.name\n", - " self.age = existing.age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "`Self` (capital \"S\") is an alias for the current type name\n", - "(`MyPet`, in this example). Using this alias is a best practice to avoid any\n", - "mistakes when referring to the current struct name.\n", - "\n", - "Also, notice that the `existing` argument in `__copyinit__()` is immutable\n", - "because the default [argument\n", - "convention](/mojo/manual/values/ownership#argument-conventions) in an `fn`\n", - "function is `borrowed`—this is a good thing because this function should not\n", - "modify the contents of the value being copied.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now this code works to make a copy:" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "var mine = MyPet(\"Loki\", 4)\n", - "var yours = mine" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What makes Mojo's copy behavior different, compared to other languages, is that\n", - "`__copyinit__()` is designed to perform a deep copy of all fields in the type\n", - "(as per [value semantics](/mojo/manual/values/value-semantics)). That is,\n", - "it copies heap-allocated values, rather than just copying the pointer.\n", - "\n", - "However, the Mojo compiler doesn't enforce this, so it's the type author's\n", - "responsibility to implement `__copyinit__()` with value semantics. For example,\n", - "here's a new `HeapArray` type that performs a deep copy in the copy constructor:" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "struct HeapArray:\n", - " var data: UnsafePointer[Int]\n", - " var size: Int\n", - " var cap: Int\n", - "\n", - " fn __init__(out self, size: Int, val: Int):\n", - " self.size = size\n", - " self.cap = size * 2\n", - " self.data = UnsafePointer[Int].alloc(self.cap)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(val)\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " # Deep-copy the existing value\n", - " self.size = existing.size\n", - " self.cap = existing.cap\n", - " self.data = UnsafePointer[Int].alloc(self.cap)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(existing.data[i])\n", - " # The lifetime of `existing` continues unchanged\n", - "\n", - " fn __del__(owned self):\n", - " # We must free the heap-allocated data, but\n", - " # Mojo knows how to destroy the other fields\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", - " self.data.free()\n", - "\n", - " fn append(inout self, val: Int):\n", - " # Update the array for demo purposes\n", - " if self.size < self.cap:\n", - " (self.data + self.size).init_pointee_copy(val)\n", - " self.size += 1\n", - " else:\n", - " print(\"Out of bounds\")\n", - "\n", - " fn dump(self):\n", - " # Print the array contents for demo purposes\n", - " print(\"[\", end=\"\")\n", - " for i in range(self.size):\n", - " if i > 0:\n", - " print(\", \", end=\"\")\n", - " print(self.data[i], end=\"\")\n", - " print(\"]\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that `__copyinit__()` does not copy the `UnsafePointer` value (doing so would\n", - "make the copied value refer to the same `data` memory address as the original\n", - "value, which is a shallow copy). Instead, we initialize a new `UnsafePointer` to\n", - "allocate a new block of memory, and then copy over all the heap-allocated\n", - "values (this is a deep copy).\n", - "\n", - "Thus, when we copy an instance of `HeapArray`, each copy has its own value on\n", - "the heap, so changes to one value do not affect the other, as shown here:" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "fn copies():\n", - " var a = HeapArray(2, 1)\n", - " var b = a # Calls the copy constructor\n", - " a.dump() # Prints [1, 1]\n", - " b.dump() # Prints [1, 1]\n", - "\n", - " b.append(2) # Changes the copied data\n", - " b.dump() # Prints [1, 1, 2]\n", - " a.dump() # Prints [1, 1] (the original did not change)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "In `HeapArray`, we must use the `__del__()` destructor to free the\n", - "heap-allocated data when the `HeapArray` lifetime ends, but Mojo automatically\n", - "destroys all other fields when their respective lifetimes end. We'll discuss\n", - "this destructor more in [Death of a value](/mojo/manual/lifecycle/death).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If your type doesn't use any pointers for heap-allocated data, then writing the\n", - "constructor and copy constructor is all boilerplate code that you shouldn't\n", - "have to write. For most structs that don't manage memory explicitly, you can \n", - "just add the [`@value` decorator](/mojo/manual/decorators/value) to your\n", - "struct definition and Mojo will synthesize the `__init__()`, `__copyinit__()`,\n", - "and `__moveinit__()` methods.\n", - "\n", - ":::note\n", - "\n", - "Mojo also calls upon the copy constructor when a value is passed to a\n", - "function that takes the argument as\n", - "[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", - "_and_ when the lifetime of the given value does _not_ end at that point. If the\n", - "lifetime of the value does end there (usually indicated with the transfer\n", - "sigil `^`), then Mojo instead invokes the move constructor.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Move constructor\n", - "\n", - "Although copying values provides predictable behavior that matches Mojo's\n", - "[value semantics](/mojo/manual/values/value-semantics), copying some data\n", - "types can be a significant hit on performance. If you're familiar with\n", - "reference semantics, then the solution here might seem clear: instead of making\n", - "a copy when passing a value, share the value as a reference. And if the\n", - "original variable is no longer needed, nullify the original to avoid any\n", - "double-free or use-after-free errors. That's generally known as a move\n", - "operation: the memory block holding the data remains the same (the memory does\n", - "not actually move), but the pointer to that memory moves to a new variable.\n", - "\n", - "To support moving a value, implement the `__moveinit__()` method. The \n", - "`__moveinit__()` method performs a consuming move: it [transfers\n", - "ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and-)\n", - "of a value from one variable to another when the original variable's lifetime\n", - "ends (also called a \"destructive move\").\n", - "\n", - ":::note\n", - "\n", - "A move constructor is **not required** to transfer ownership of a\n", - "value. Unlike in Rust, transferring ownership is not always a move operation;\n", - "the move constructors are only part of the implementation for how Mojo\n", - "transfers ownership of a value. You can learn more in the section about\n", - "[ownership\n", - "transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and-).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When a move occurs, Mojo immediately invalidates the original\n", - "variable, preventing any access to it and disabling its destructor. Invalidating\n", - "the original variable is important to avoid memory errors on heap-allocated\n", - "data, such as use-after-free and double-free errors.\n", - "\n", - "Here's how to add the move constructor to the `HeapArray` example:" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "struct HeapArray:\n", - " var data: UnsafePointer[Int]\n", - " var size: Int\n", - " var cap: Int\n", - "\n", - "\n", - " fn __init__(out self, size: Int, val: Int):\n", - " self.size = size\n", - " self.cap = size * 2\n", - " self.data = UnsafePointer[Int].alloc(self.size)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(val)\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " # Deep-copy the existing value\n", - " self.size = existing.size\n", - " self.cap = existing.cap\n", - " self.data = UnsafePointer[Int].alloc(self.cap)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(existing.data[i])\n", - " # The lifetime of `existing` continues unchanged\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " print(\"move\")\n", - " # Shallow copy the existing value\n", - " self.size = existing.size\n", - " self.cap = existing.cap\n", - " self.data = existing.data\n", - " # Then the lifetime of `existing` ends here, but\n", - " # Mojo does NOT call its destructor\n", - "\n", - " fn __del__(owned self):\n", - " # We must free the heap-allocated data, but\n", - " # Mojo knows how to destroy the other fields\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", - " self.data.free()\n", - "\n", - " fn append(inout self, val: Int):\n", - " # Update the array for demo purposes\n", - " if self.size < self.cap:\n", - " (self.data + self.size).init_pointee_copy(val)\n", - " self.size += 1\n", - " else:\n", - " print(\"Out of bounds\")\n", - "\n", - " fn dump(self):\n", - " # Print the array contents for demo purposes\n", - " print(\"[\", end=\"\")\n", - " for i in range(self.size):\n", - " if i > 0:\n", - " print(\", \", end=\"\")\n", - " print(self.data[i], end=\"\")\n", - " print(\"]\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The critical feature of `__moveinit__()` is that it takes the incoming value as\n", - "`owned`, meaning this method gets unique ownership of the value. Moreover,\n", - "because this is a dunder method that Mojo calls only when performing a move \n", - "(during ownership transfer), the `existing` argument is guaranteed to be a \n", - "mutable reference to the original value, _not a copy_ (unlike other methods that\n", - "may declare an argument as `owned`, but might receive the value as a copy if the\n", - "method is called without the [`^` transfer\n", - "sigil](/mojo/manual/values/ownership#transfer-arguments-owned-and-)).\n", - "That is, Mojo calls this move constructor _only_ when the original variable's\n", - "lifetime actually ends at the point of transfer.\n", - "\n", - "Here's an example showing how to invoke the move constructor for `HeapArray`:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "fn moves():\n", - " var a = HeapArray(3, 1)\n", - "\n", - " a.dump() # Prints [1, 1, 1]\n", - "\n", - " var b = a^ # Prints \"move\"; the lifetime of `a` ends here\n", - "\n", - " b.dump() # Prints [1, 1, 1]\n", - " #a.dump() # ERROR: use of uninitialized value 'a'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that `__moveinit__()` performs a shallow copy of the\n", - "existing field values (it copies the pointer, instead of allocating new memory\n", - "on the heap), which is what makes it useful for types with heap-allocated\n", - "values that are expensive to copy.\n", - "\n", - "To go further and ensure your type can never be copied, you can make it\n", - "\"move-only\" by implementing `__moveinit__()` and _excluding_ `__copyinit__()`.\n", - "A move-only type can be passed to other variables and passed into functions\n", - "with any argument convention (`borrowed`, `inout`, and `owned`)—the only catch\n", - "is that you must use the `^` transfer sigil to end the lifetime of a\n", - "move-only type when assigning it to a new variable or when passing it as an\n", - "`owned` argument." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "For types without heap-allocated fields, you get no real benefit from\n", - "the move constructor. Making copies of simple data types on the stack, like\n", - "integers, floats, and booleans, is very cheap. Yet, if you allow your type to\n", - "be copied, then there's generally no reason to disallow moves, so you can\n", - "synthesize both constructors by adding the [`@value`\n", - "decorator](/mojo/manual/decorators/value).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simple value types {#value-decorator}\n", - "\n", - "Because copy and move constructors are opt-in, Mojo provides great control for\n", - "exotic use cases (such as for atomic values that should never be copied or\n", - "moved), but most structs are simple aggregations of other types that should be\n", - "easily copied and moved, and we don't want to write a lot of boilerplate\n", - "constructors for those simple value types.\n", - "\n", - "To solve this, Mojo provides the [`@value`\n", - "decorator](/mojo/manual/decorators/value), which synthesizes the\n", - "boilerplate code for the `__init__()`, `__copyinit__()`, and `__moveinit__()`\n", - "methods.\n", - "\n", - "For example, consider a simple struct like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo sees the `@value` decorator and notices that you don't have a member-wise\n", - "initializer (a constructor with arguments for each field), a copy constructor,\n", - "or a move constructor, so it synthesizes them for you. The result is as if you\n", - "had actually written this:" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, owned name: String, age: Int):\n", - " self.name = name^\n", - " self.age = age\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " self.name = existing.name\n", - " self.age = existing.age\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " self.name = existing.name^\n", - " self.age = existing.age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo synthesizes each lifecycle method only when it doesn't exist, so\n", - "you can use `@value` and still define your own versions to override the default\n", - "behavior. For example, it is fairly common to use the default member-wise and\n", - "move constructor, but create a custom copy constructor. Another common pattern\n", - "is to use `@value` to create a member-wise constructor, and add overloads that\n", - "take different sets of arguments. For example, if you want to create\n", - "a `MyPet` struct without specifying an age, you could add an overloaded\n", - "constructor:" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, owned name: String):\n", - " self.name = name^\n", - " self.age = 0\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that this overloaded constructor **doesn't** prevent the `@value` decorator\n", - "from synthesizing the member-wise constructor. To override this default\n", - "constructor, you'd need to add a constructor with the same signature as the\n", - "default member-wise constructor.\n", - "\n", - "Something you can see in this code that we didn't mention yet is that the\n", - "`__init__()` method takes all arguments as `owned`, because the constructor\n", - "must take ownership to store each value. This is a useful micro-optimization\n", - "and enables the use of move-only types. Trivial types like `Int` are also\n", - "passed as `owned`, but because ownership doesn't mean anything for integers, we\n", - "can elide that declaration and the transfer sigil (`^`) for simplicity. The\n", - "transfer operator is also just a formality in this case, because, even if it's\n", - "not used with `self.name = name^`, the Mojo compiler will notice that `name` is\n", - "last used here and convert this assignment into a move, instead of a\n", - "copy+delete.\n", - "\n", - ":::note\n", - "\n", - "If your type contains any move-only fields, Mojo will not generate\n", - "the copy constructor because it cannot copy those fields. Further, the `@value`\n", - "decorator won't work at all if any of your members are neither copyable nor\n", - "movable. For example, if you have something like `Atomic` in your struct, then\n", - "it probably isn't a true value type, and you don't want the copy/move\n", - "constructors anyway.\n", - "\n", - "Also notice that the `MyPet` struct above doesn't include the `__del__()`\n", - "destructor (the `@value` decorator does not synthesize this), because Mojo\n", - "doesn't need it to destroy fields, as discussed in [Death of a\n", - "value](/mojo/manual/lifecycle/death)\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Trivial types\n", - "\n", - "So far, we've talked about values that live in memory, which means they have an\n", - "identity (an address) that can be passed around among functions (passed \"by\n", - "reference\"). This is great for most types, and it's a safe default for large\n", - "objects with expensive copy operations. However, it's inefficient for tiny\n", - "things like a single integer or floating point number. We call these types\n", - "\"trivial\" because they are just \"bags of bits\" that should be copied, moved,\n", - "and destroyed without invoking any custom lifecycle methods.\n", - "\n", - "Trivial types are the most common types that surround us, and from a language\n", - "perspective, Mojo doesn’t need special support for these written in a struct.\n", - "Usually, these values are so tiny that they should be passed around in CPU\n", - "registers, not indirectly through memory.\n", - "\n", - "As such, Mojo provides a struct decorator to declare these types of values:\n", - "`@register_passable(\"trivial\")`. This decorator tells Mojo that the type should\n", - "be copyable and movable but that it has no user-defined logic (no lifecycle\n", - "methods) for doing this. It also tells Mojo to pass the value in CPU registers\n", - "whenever possible, which has clear performance benefits.\n", - "\n", - "You'll see this decorator on types like `Int` in the standard library:\n", - "\n", - "```mojo\n", - "@register_passable(\"trivial\")\n", - "struct Int:\n", - " var value: __mlir_type.index\n", - "\n", - " fn __init__(value: __mlir_type.index) -> Int:\n", - " return Self {value: value}\n", - " ...\n", - "```\n", - "\n", - "We expect to use this decorator pervasively on Mojo standard library types, but\n", - "it is safe to ignore for general application-level code.\n", - "\n", - "For more information, see the [`@register_passable`\n", - "documentation](/mojo/manual/decorators/register-passable).\n", - "\n", - ":::note TODO\n", - "\n", - "This decorator is due for reconsideration. Lack of custom\n", - "copy/move/destroy logic and \"passability in a register\" are orthogonal concerns\n", - "and should be split. This former logic should be subsumed into a more general\n", - "`@value(\"trivial\")` decorator, which is orthogonal from `@register_passable`.\n", - "\n", - ":::" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/lifecycle/life.mdx b/docs/manual/lifecycle/life.mdx new file mode 100644 index 0000000000..95c4367593 --- /dev/null +++ b/docs/manual/lifecycle/life.mdx @@ -0,0 +1,691 @@ +--- +title: Life of a value +sidebar_position: 2 +description: An explanation of when and how Mojo creates values. +--- + +The life of a value in Mojo begins when a variable is initialized and continues +up until the value is last used, at which point Mojo destroys it. This page +describes how every value in Mojo is created, copied, and moved. (The next +page describes [how values are +destroyed](/mojo/manual/lifecycle/death).) + +All data types in Mojo—including basic types in the standard library such as +[`Bool`](/mojo/stdlib/builtin/bool/Bool), +[`Int`](/mojo/stdlib/builtin/int/Int), and +[`String`](/mojo/stdlib/collections/string/String), up to complex types such +as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and +[`object`](/mojo/stdlib/builtin/object/object)—are defined as a +[struct](/mojo/manual/structs). This means the creation and +destruction of any piece of data follows the same lifecycle rules, and you can +define your own data types that work exactly the same way. + +Mojo structs don't get any default lifecycle methods, such as a +constructor, copy constructor, or move constructor. That means you can create +a struct without a constructor, but then you can't instantiate it, and it +would be useful only as a sort of namespace for static methods. For example: + +```mojo +struct NoInstances: + var state: Int + + @staticmethod + fn print_hello(): + print("Hello world!") +``` + +Without a constructor, this cannot be instantiated, so it has no lifecycle. The +`state` field is also useless because it cannot be initialized (Mojo structs do +not support default field values—you must initialize them in a constructor). + +So the only thing you can do is call the static method: + +```mojo +NoInstances.print_hello() +``` + +```output +Hello world! +``` + +## Constructor + +To create an instance of a Mojo type, it needs the `__init__()` constructor +method. The main responsibility of the constructor is to initialize all fields. +For example: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, name: String, age: Int): + self.name = name + self.age = age +``` + +Now we can create an instance: + +```mojo +var mine = MyPet("Loki", 4) +``` + +An instance of `MyPet` can also be +[borrowed](/mojo/manual/values/ownership#borrowed-arguments-borrowed) +and destroyed, but it currently can't be copied or moved. + +We believe this is a good default starting point, because there are no built-in +lifecycle events and no surprise behaviors. You—the type author—must +explicitly decide whether and how the type can be copied or moved, by +implementing the copy and move constructors. + +:::note + +Mojo does not require a destructor to destroy an object. As long as +all fields in the struct are destructible (every type in the standard library +is destructible, except for +[pointers](/mojo/stdlib/memory/unsafe)), then Mojo knows how to destroy +the type when its lifetime ends. We'll discuss that more in [Death of a +value](/mojo/manual/lifecycle/death). + +::: + +### Overloading the constructor + +Like any other function/method, you can +[overload](/mojo/manual/functions#overloaded-functions) the +`__init__()` constructor to initialize the object with different arguments. For +example, you might want a default constructor that sets some default values and +takes no arguments, and then additional constructors that accept more arguments. + +Just be aware that, in order to modify any fields, each constructor must +declare the `self` argument with the [`inout` +convention](/mojo/manual/values/ownership#mutable-arguments-inout). If you +want to call one constructor from another, you simply call upon that +constructor as you would externally (you don't need to pass `self`). + +For example, here's how you can delegate work from an overloaded constructor: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self): + self.name = "" + self.age = 0 + + fn __init__(out self, name: String): + self = MyPet() + self.name = name +``` + +### Field initialization + +Notice in the previous example that, by the end of each constructor, all fields +must be initialized. That's the only requirement in the constructor. + +In fact, the `__init__()` constructor is smart enough to treat the `self` +object as fully initialized even before the constructor is finished, as long +as all fields are initialized. For example, this constructor can pass around +`self` as soon as all fields are initialized: + +```mojo +fn use(arg: MyPet): + pass + +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, name: String, age: Int, cond: Bool): + self.name = name + if cond: + self.age = age + use(self) # Safe to use immediately! + + self.age = age + use(self) # Safe to use immediately! +``` + +### Constructors and implicit conversion + +Mojo supports implicit conversion from one type to another. Implicit conversion +can happen when one of the following occurs: + +* You assign a value of one type to a variable with a different type. +* You pass a value of one type to a function that requires a different type. + +In both cases, implicit conversion is supported when the target type +defines a constructor that takes a single required, non-keyword argument of the +source type. For example: + +```mojo +var a = Source() +var b: Target = a +``` + +Mojo implicitly converts the `Source` value in `a` to a `Target` value if +`Target` defines a matching constructor like this: + +```mojo +struct Target: + fn __init__(out self, s: Source): ... +``` + +With implicit conversion, the assignment above is essentially identical to: + +```mojo +var b = Target(a) +``` + +The constructor used for implicit conversion can take optional arguments, so +the following constructor would also support implicit conversion from `Source` +to `Target`: + +```mojo +struct Target: + fn __init__(out self, s: Source, reverse: Bool = False): ... +``` + +Implicit conversion also occurs if the type doesn't declare its own constructor, +but instead uses the [`@value` decorator](#value-decorator), *and* the type +has only one field. That's because Mojo automatically creates a member-wise +constructor for each field, and when there is only one field, that synthesized +constructor works exactly like a conversion constructor. For example, this +type also can convert a `Source` value to a `Target` value: + +```mojo +@value +struct Target: + var s: Source +``` + +Implicit conversion can fail if Mojo can't unambiguously match the conversion to +a constructor. For example, if the target type has two overloaded constructors +that take different types, and each of those types supports an implicit +conversion from the source type, the compiler has two equally-valid paths to +convert the values: + +```mojo +struct A: + fn __init__(out self, s: Source): ... + +struct B: + fn __init__(out self, s: Source): ... + +struct Target: + fn __init__(out self, a: A): ... + fn __init__(out self, b: B): ... + +# Fails +var t = Target(Source()) +``` + +In this case, removing either one of the target type's constructors will fix the +problem. + +If you want to define a single-argument constructor, but you **don't** want +the types to implicitly convert, you can define the constructor with a +[keyword-only argument](/mojo/manual/functions#positional-only-and-keyword-only-arguments): + +```mojo +struct Target: + # does not support implicit conversion + fn __init__(out self, *, source: Source): ... + +# the constructor must be called with a keyword +var t = Target(source=a) +``` + +:::note + +In the future we intend to provide a more explicit method of declaring whether +a constructor should support implicit conversion. + +::: + +## Copy constructor + +When Mojo encounters an assignment operator (`=`), it tries to make a copy of +the right-side value by calling upon that type's copy constructor: the +`__copyinit__()` method. Thus, it's the responsibility of the type author to +implement `__copyinit__()` so it returns a copy of the value. + +For example, the `MyPet` type above does not have a copy constructor, +so this code fails to compile: + +```mojo +var mine = MyPet("Loki", 4) +var yours = mine # This requires a copy, but MyPet has no copy constructor +``` + +To make it work, we need to add the copy constructor, like +this: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, name: String, age: Int): + self.name = name + self.age = age + + fn __copyinit__(out self, existing: Self): + self.name = existing.name + self.age = existing.age +``` + +:::note + +`Self` (capital "S") is an alias for the current type name +(`MyPet`, in this example). Using this alias is a best practice to avoid any +mistakes when referring to the current struct name. + +Also, notice that the `existing` argument in `__copyinit__()` is immutable +because the default [argument +convention](/mojo/manual/values/ownership#argument-conventions) in an `fn` +function is `borrowed`—this is a good thing because this function should not +modify the contents of the value being copied. + +::: + +Now this code works to make a copy: + +```mojo +var mine = MyPet("Loki", 4) +var yours = mine +``` + +What makes Mojo's copy behavior different, compared to other languages, is that +`__copyinit__()` is designed to perform a deep copy of all fields in the type +(as per [value semantics](/mojo/manual/values/value-semantics)). That is, +it copies heap-allocated values, rather than just copying the pointer. + +However, the Mojo compiler doesn't enforce this, so it's the type author's +responsibility to implement `__copyinit__()` with value semantics. For example, +here's a new `HeapArray` type that performs a deep copy in the copy constructor: + +```mojo +struct HeapArray: + var data: UnsafePointer[Int] + var size: Int + var cap: Int + + fn __init__(out self, size: Int, val: Int): + self.size = size + self.cap = size * 2 + self.data = UnsafePointer[Int].alloc(self.cap) + for i in range(self.size): + (self.data + i).init_pointee_copy(val) + + fn __copyinit__(out self, existing: Self): + # Deep-copy the existing value + self.size = existing.size + self.cap = existing.cap + self.data = UnsafePointer[Int].alloc(self.cap) + for i in range(self.size): + (self.data + i).init_pointee_copy(existing.data[i]) + # The lifetime of `existing` continues unchanged + + fn __del__(owned self): + # We must free the heap-allocated data, but + # Mojo knows how to destroy the other fields + for i in range(self.size): + (self.data + i).destroy_pointee() + self.data.free() + + fn append(inout self, val: Int): + # Update the array for demo purposes + if self.size < self.cap: + (self.data + self.size).init_pointee_copy(val) + self.size += 1 + else: + print("Out of bounds") + + fn dump(self): + # Print the array contents for demo purposes + print("[", end="") + for i in range(self.size): + if i > 0: + print(", ", end="") + print(self.data[i], end="") + print("]") +``` + +Notice that `__copyinit__()` does not copy the `UnsafePointer` value (doing so would +make the copied value refer to the same `data` memory address as the original +value, which is a shallow copy). Instead, we initialize a new `UnsafePointer` to +allocate a new block of memory, and then copy over all the heap-allocated +values (this is a deep copy). + +Thus, when we copy an instance of `HeapArray`, each copy has its own value on +the heap, so changes to one value do not affect the other, as shown here: + +```mojo +fn copies(): + var a = HeapArray(2, 1) + var b = a # Calls the copy constructor + a.dump() # Prints [1, 1] + b.dump() # Prints [1, 1] + + b.append(2) # Changes the copied data + b.dump() # Prints [1, 1, 2] + a.dump() # Prints [1, 1] (the original did not change) +``` + +:::note + +In `HeapArray`, we must use the `__del__()` destructor to free the +heap-allocated data when the `HeapArray` lifetime ends, but Mojo automatically +destroys all other fields when their respective lifetimes end. We'll discuss +this destructor more in [Death of a value](/mojo/manual/lifecycle/death). + +::: + +If your type doesn't use any pointers for heap-allocated data, then writing the +constructor and copy constructor is all boilerplate code that you shouldn't +have to write. For most structs that don't manage memory explicitly, you can +just add the [`@value` decorator](/mojo/manual/decorators/value) to your +struct definition and Mojo will synthesize the `__init__()`, `__copyinit__()`, +and `__moveinit__()` methods. + +:::note + +Mojo also calls upon the copy constructor when a value is passed to a +function that takes the argument as +[`owned`](/mojo/manual/values/ownership#transfer-arguments-owned-and-) +*and* when the lifetime of the given value does *not* end at that point. If the +lifetime of the value does end there (usually indicated with the transfer +sigil `^`), then Mojo instead invokes the move constructor. + +::: + +## Move constructor + +Although copying values provides predictable behavior that matches Mojo's +[value semantics](/mojo/manual/values/value-semantics), copying some data +types can be a significant hit on performance. If you're familiar with +reference semantics, then the solution here might seem clear: instead of making +a copy when passing a value, share the value as a reference. And if the +original variable is no longer needed, nullify the original to avoid any +double-free or use-after-free errors. That's generally known as a move +operation: the memory block holding the data remains the same (the memory does +not actually move), but the pointer to that memory moves to a new variable. + +To support moving a value, implement the `__moveinit__()` method. The +`__moveinit__()` method performs a consuming move: it [transfers +ownership](/mojo/manual/values/ownership#transfer-arguments-owned-and-) +of a value from one variable to another when the original variable's lifetime +ends (also called a "destructive move"). + +:::note + +A move constructor is **not required** to transfer ownership of a +value. Unlike in Rust, transferring ownership is not always a move operation; +the move constructors are only part of the implementation for how Mojo +transfers ownership of a value. You can learn more in the section about +[ownership +transfer](/mojo/manual/values/ownership#transfer-arguments-owned-and-). + +::: + +When a move occurs, Mojo immediately invalidates the original +variable, preventing any access to it and disabling its destructor. Invalidating +the original variable is important to avoid memory errors on heap-allocated +data, such as use-after-free and double-free errors. + +Here's how to add the move constructor to the `HeapArray` example: + +```mojo +struct HeapArray: + var data: UnsafePointer[Int] + var size: Int + var cap: Int + + + fn __init__(out self, size: Int, val: Int): + self.size = size + self.cap = size * 2 + self.data = UnsafePointer[Int].alloc(self.size) + for i in range(self.size): + (self.data + i).init_pointee_copy(val) + + fn __copyinit__(out self, existing: Self): + # Deep-copy the existing value + self.size = existing.size + self.cap = existing.cap + self.data = UnsafePointer[Int].alloc(self.cap) + for i in range(self.size): + (self.data + i).init_pointee_copy(existing.data[i]) + # The lifetime of `existing` continues unchanged + + fn __moveinit__(out self, owned existing: Self): + print("move") + # Shallow copy the existing value + self.size = existing.size + self.cap = existing.cap + self.data = existing.data + # Then the lifetime of `existing` ends here, but + # Mojo does NOT call its destructor + + fn __del__(owned self): + # We must free the heap-allocated data, but + # Mojo knows how to destroy the other fields + for i in range(self.size): + (self.data + i).destroy_pointee() + self.data.free() + + fn append(inout self, val: Int): + # Update the array for demo purposes + if self.size < self.cap: + (self.data + self.size).init_pointee_copy(val) + self.size += 1 + else: + print("Out of bounds") + + fn dump(self): + # Print the array contents for demo purposes + print("[", end="") + for i in range(self.size): + if i > 0: + print(", ", end="") + print(self.data[i], end="") + print("]") +``` + +The critical feature of `__moveinit__()` is that it takes the incoming value as +`owned`, meaning this method gets unique ownership of the value. Moreover, +because this is a dunder method that Mojo calls only when performing a move +(during ownership transfer), the `existing` argument is guaranteed to be a +mutable reference to the original value, *not a copy* (unlike other methods that +may declare an argument as `owned`, but might receive the value as a copy if the +method is called without the [`^` transfer +sigil](/mojo/manual/values/ownership#transfer-arguments-owned-and-)). +That is, Mojo calls this move constructor *only* when the original variable's +lifetime actually ends at the point of transfer. + +Here's an example showing how to invoke the move constructor for `HeapArray`: + +```mojo +fn moves(): + var a = HeapArray(3, 1) + + a.dump() # Prints [1, 1, 1] + + var b = a^ # Prints "move"; the lifetime of `a` ends here + + b.dump() # Prints [1, 1, 1] + #a.dump() # ERROR: use of uninitialized value 'a' +``` + +Notice that `__moveinit__()` performs a shallow copy of the +existing field values (it copies the pointer, instead of allocating new memory +on the heap), which is what makes it useful for types with heap-allocated +values that are expensive to copy. + +To go further and ensure your type can never be copied, you can make it +"move-only" by implementing `__moveinit__()` and *excluding* `__copyinit__()`. +A move-only type can be passed to other variables and passed into functions +with any argument convention (`borrowed`, `inout`, and `owned`)—the only catch +is that you must use the `^` transfer sigil to end the lifetime of a +move-only type when assigning it to a new variable or when passing it as an +`owned` argument. + +:::note + +For types without heap-allocated fields, you get no real benefit from +the move constructor. Making copies of simple data types on the stack, like +integers, floats, and booleans, is very cheap. Yet, if you allow your type to +be copied, then there's generally no reason to disallow moves, so you can +synthesize both constructors by adding the [`@value` +decorator](/mojo/manual/decorators/value). + +::: + +## Simple value types {#value-decorator} + +Because copy and move constructors are opt-in, Mojo provides great control for +exotic use cases (such as for atomic values that should never be copied or +moved), but most structs are simple aggregations of other types that should be +easily copied and moved, and we don't want to write a lot of boilerplate +constructors for those simple value types. + +To solve this, Mojo provides the [`@value` +decorator](/mojo/manual/decorators/value), which synthesizes the +boilerplate code for the `__init__()`, `__copyinit__()`, and `__moveinit__()` +methods. + +For example, consider a simple struct like this: + +```mojo +@value +struct MyPet: + var name: String + var age: Int +``` + +Mojo sees the `@value` decorator and notices that you don't have a member-wise +initializer (a constructor with arguments for each field), a copy constructor, +or a move constructor, so it synthesizes them for you. The result is as if you +had actually written this: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, owned name: String, age: Int): + self.name = name^ + self.age = age + + fn __copyinit__(out self, existing: Self): + self.name = existing.name + self.age = existing.age + + fn __moveinit__(out self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age +``` + +Mojo synthesizes each lifecycle method only when it doesn't exist, so +you can use `@value` and still define your own versions to override the default +behavior. For example, it is fairly common to use the default member-wise and +move constructor, but create a custom copy constructor. Another common pattern +is to use `@value` to create a member-wise constructor, and add overloads that +take different sets of arguments. For example, if you want to create +a `MyPet` struct without specifying an age, you could add an overloaded +constructor: + +```mojo +@value +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, owned name: String): + self.name = name^ + self.age = 0 + +``` + +Note that this overloaded constructor **doesn't** prevent the `@value` decorator +from synthesizing the member-wise constructor. To override this default +constructor, you'd need to add a constructor with the same signature as the +default member-wise constructor. + +Something you can see in this code that we didn't mention yet is that the +`__init__()` method takes all arguments as `owned`, because the constructor +must take ownership to store each value. This is a useful micro-optimization +and enables the use of move-only types. Trivial types like `Int` are also +passed as `owned`, but because ownership doesn't mean anything for integers, we +can elide that declaration and the transfer sigil (`^`) for simplicity. The +transfer operator is also just a formality in this case, because, even if it's +not used with `self.name = name^`, the Mojo compiler will notice that `name` is +last used here and convert this assignment into a move, instead of a +copy+delete. + +:::note + +If your type contains any move-only fields, Mojo will not generate +the copy constructor because it cannot copy those fields. Further, the `@value` +decorator won't work at all if any of your members are neither copyable nor +movable. For example, if you have something like `Atomic` in your struct, then +it probably isn't a true value type, and you don't want the copy/move +constructors anyway. + +Also notice that the `MyPet` struct above doesn't include the `__del__()` +destructor (the `@value` decorator does not synthesize this), because Mojo +doesn't need it to destroy fields, as discussed in [Death of a +value](/mojo/manual/lifecycle/death) + +::: + +## Trivial types + +So far, we've talked about values that live in memory, which means they have an +identity (an address) that can be passed around among functions (passed "by +reference"). This is great for most types, and it's a safe default for large +objects with expensive copy operations. However, it's inefficient for tiny +things like a single integer or floating point number. We call these types +"trivial" because they are just "bags of bits" that should be copied, moved, +and destroyed without invoking any custom lifecycle methods. + +Trivial types are the most common types that surround us, and from a language +perspective, Mojo doesn’t need special support for these written in a struct. +Usually, these values are so tiny that they should be passed around in CPU +registers, not indirectly through memory. + +As such, Mojo provides a struct decorator to declare these types of values: +`@register_passable("trivial")`. This decorator tells Mojo that the type should +be copyable and movable but that it has no user-defined logic (no lifecycle +methods) for doing this. It also tells Mojo to pass the value in CPU registers +whenever possible, which has clear performance benefits. + +You'll see this decorator on types like `Int` in the standard library: + +```mojo +@register_passable("trivial") +struct Int: + var value: __mlir_type.index + + fn __init__(value: __mlir_type.index) -> Int: + return Self {value: value} + ... +``` + +We expect to use this decorator pervasively on Mojo standard library types, but +it is safe to ignore for general application-level code. + +For more information, see the [`@register_passable` +documentation](/mojo/manual/decorators/register-passable). + +:::note TODO + +This decorator is due for reconsideration. Lack of custom +copy/move/destroy logic and "passability in a register" are orthogonal concerns +and should be split. This former logic should be subsumed into a more general +`@value("trivial")` decorator, which is orthogonal from `@register_passable`. + +::: diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb deleted file mode 100644 index 358f846ff3..0000000000 --- a/docs/manual/parameters/index.ipynb +++ /dev/null @@ -1,1908 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: \"Parameterization: compile-time metaprogramming\"\n", - "description: An introduction to parameters and compile-time metaprogramming.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Many languages have facilities for _metaprogramming_: that is, for writing code that generates or modifies code. Python has facilities for dynamic metaprogramming: features like decorators, metaclasses, and many more. These features make Python very flexible and productive, but since they're dynamic, they come with runtime overhead. Other languages have static or compile-time metaprogramming features, like C preprocessor macros and C++ templates. These can be limiting and hard to use.\n", - "\n", - "To support Modular's work in AI, Mojo aims to provide powerful, easy-to-use metaprogramming with zero runtime cost. This compile-time metaprogramming uses the same language as runtime programs, so you don't have to learn a new language—just a few new features.\n", - "\n", - "The main new feature is _parameters_. You can think of a parameter as a compile-time variable that becomes a runtime constant. This usage of \"parameter\" is probably different from what you're used to from other languages, where \"parameter\" and \"argument\" are often used interchangeably. In Mojo, \"parameter\" and \"parameter expression\" refer to compile-time values, and \"argument\" and \"expression\" refer to runtime values. \n", - "\n", - "In Mojo, you can add parameters to a struct or function. You can also define \n", - "named parameter expressions—aliases—that you can use as runtime constants." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterized functions\n", - "\n", - "To define a _parameterized function_, add parameters in square brackets ahead\n", - "of the argument list. Each parameter is formatted just like an argument: a \n", - "parameter name, followed by a colon and a type (which is required). In the\n", - "following example, the function has a single parameter, `count` of type `Int`. " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "fn repeat[count: Int](msg: String):\n", - " @parameter\n", - " for i in range(count):\n", - " print(msg)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The [`@parameter`](/mojo/manual/decorators/parameter) directive shown here \n", - "causes the `for` loop to be evaluated at compile time. The directive only works\n", - "if the loop limits are compile-time constants. Since `count` is a parameter,\n", - "`range(count)` can be calculated at compile time.\n", - "\n", - "Calling a parameterized function, you provide values for the parameters, just \n", - "like function arguments: " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello\n", - "Hello\n", - "Hello\n" - ] - } - ], - "source": [ - "repeat[3](\"Hello\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " \n", - " The compiler resolves the parameter values during compilation, and creates a\n", - " concrete version of the `repeat[]()` function for each unique parameter value.\n", - " After resolving the parameter values and unrolling the loop, the `repeat[3]()`\n", - " function would be roughly equivalent to this:\n", - "\n", - "```mojo\n", - "fn repeat_3(msg: String):\n", - " print(msg)\n", - " print(msg)\n", - " print(msg)\n", - "```\n", - "\n", - ":::note\n", - "\n", - "This doesn't represent actual code generated by the compiler. By the\n", - "time parameters are resolved, Mojo code has already been transformed to an\n", - "intermediate representation in [MLIR](https://mlir.llvm.org/).\n", - "\n", - ":::\n", - "\n", - "If the compiler can't resolve all parameter values to constant values, \n", - "compilation fails." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameters and generics\n", - "\n", - "\"Generics\" refers to functions that can act on multiple types of values, or \n", - "containers that can hold multiple types of values. For example, \n", - "[`List`](/mojo/stdlib/collections/list/List), can hold\n", - "different types of values, so you can have a list of `Int` values, or\n", - "a list of `String` values).\n", - "\n", - "In Mojo, generics use parameters to specify types. For example, `List`\n", - "takes a type parameter, so a vector of integers is written `List[Int]`.\n", - "So all generics use parameters, but **not** everything that uses parameters is a\n", - "generic. \n", - "\n", - "For example, the `repeat[]()` function in the previous section includes \n", - "parameter of type `Int`, and an argument of type `String`. It's parameterized,\n", - "but not generic. A generic function or struct is parameterized on _type_. For\n", - "example, we could rewrite `repeat[]()` to take any type of argument that\n", - "conforms to the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait: " - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "42\n" - ] - } - ], - "source": [ - "fn repeat[MsgType: Stringable, count: Int](msg: MsgType):\n", - " @parameter\n", - " for i in range(count):\n", - " print(str(msg))\n", - "\n", - "# Must use keyword parameter for `count`\n", - "repeat[count=2](42)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This updated function takes any `Stringable` type, so you can pass it an `Int`,\n", - "`String`, or `Bool` value.\n", - "\n", - "You can't pass the `count` as a positional keyword without also specifying `MsgType`.\n", - "You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now\n", - "you can pass the following parameter `count` positionally:" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "42\n", - "42\n" - ] - } - ], - "source": [ - "fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):\n", - " @parameter\n", - " for i in range(count):\n", - " print(str(msg))\n", - "\n", - "# MsgType is always inferred, so first positional keyword `2` is passed to `count`\n", - "repeat[2](42)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Mojo's support for generics is still early. You can write generic functions like\n", - "this using traits and parameters. You can also write generic collections like\n", - "`List` and `Dict`. If you're interested in learning how these types work, you\n", - "can find the source code for the standard library collection types \n", - "[on GitHub](https://github.com/modularml/mojo/blob/nightly/stdlib/src/collections/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Parameterized structs\n", - "\n", - "You can also add parameters to structs. You can use parameterized structs to\n", - "build generic collections. For example, a generic array type might include code\n", - "like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import UnsafePointer\n", - "\n", - "struct GenericArray[ElementType: CollectionElement]:\n", - " var data: UnsafePointer[ElementType]\n", - " var size: Int\n", - "\n", - " fn __init__(out self, *elements: ElementType):\n", - " self.size = len(elements)\n", - " self.data = UnsafePointer[ElementType].alloc(self.size)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_move(elements[i])\n", - "\n", - " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", - " self.data.free()\n", - "\n", - " fn __getitem__(self, i: Int) raises -> ref [self] ElementType:\n", - " if (i < self.size):\n", - " return self.data[i]\n", - " else:\n", - " raise Error(\"Out of bounds\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This struct has a single parameter, `ElementType`, which is a placeholder for\n", - "the data type you want to store in the array, sometimes called a _type\n", - "parameter_. `ElementType` is typed as\n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement), which is a\n", - "[trait](/mojo/manual/traits) representing any type that can be copied and moved.\n", - "\n", - "As with parameterized functions, you need to pass in parameter values when you\n", - "use a parameterized struct. In this case, when you create an instance of \n", - "`GenericArray`, you need to specify the type you want to store, like `Int`, or\n", - "`Float64`. (This is a little confusing, because the _parameter value_ you're\n", - "passing in this case is a _type_. That's OK: a Mojo type is a valid compile-time\n", - "value.)\n", - "\n", - "You'll see that `ElementType` is used throughout the struct where you'd usually see a \n", - "type name. For example, as the formal type for the `elements` in the \n", - "constructor, and the return type of the `__getitem__()` method.\n", - "\n", - "Here's an example of using `GenericArray`:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 2 3 4 " - ] - } - ], - "source": [ - "var array = GenericArray[Int](1, 2, 3, 4)\n", - "for i in range(array.size):\n", - " print(array[i], end=\" \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A parameterized struct can use the `Self` type to represent a concrete instance\n", - "of the struct (that is, with all its parameters specified). For example, you\n", - "could add a static factory method to `GenericArray` with the following\n", - "signature:\n", - "\n", - "```mojo\n", - "struct GenericArray[ElementType: CollectionElement]:\n", - " ...\n", - "\n", - " @staticmethod\n", - " fn splat(count: Int, value: ElementType) -> Self:\n", - " # Create a new array with count instances of the given value\n", - "```\n", - "\n", - "Here, `Self` is equivalent to writing `GenericArray[ElementType]`. That is, you\n", - "can call the `splat()` method like this:\n", - "\n", - "```mojo\n", - "GenericArray[Float64].splat(8, 0)\n", - "```\n", - "\n", - "The method returns an instance of `GenericArray[Float64]`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conditional conformance\n", - "\n", - "When creating a generic struct, you might want to define some methods that\n", - "require extra features. For example, consider a collection like `GenericArray`\n", - "that holds instances of `CollectionElement`. The `CollectionElement` trait\n", - "only requires that the stored data type be copyable and movable. This\n", - "imposes a lot of limitations: you can't implement a `sort()` method because\n", - "you can't guarantee that the stored type supports the comparison operators; you can't\n", - "write a useful `__str__()` or `__repr__()` dunder method because you can't\n", - "guarantee that the stored type supports conversion to a string.\n", - "\n", - "The answer to these issues is _conditional conformance_, which lets you define a \n", - "method that requires additional features. You do this by defining the `self`\n", - "value that has a more specific bound on one or more of its parameters. \n", - "\n", - "For example, the following code defines a `Container` type that holds an\n", - "instance of `CollectionElement`. It also defines a `__str__()` method that can \n", - "only be called if the stored `ElementType` conforms to \n", - "`StringableCollectionElement`:" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5\n", - "Hello\n" - ] - } - ], - "source": [ - "@value\n", - "struct Container[ElementType: CollectionElement]:\n", - " var element: ElementType\n", - "\n", - " def __str__[StrElementType: StringableCollectionElement, //](\n", - " self: Container[StrElementType]) -> String:\n", - " return str(self.element)\n", - "\n", - "def use_container():\n", - " float_container = Container(5)\n", - " string_container = Container(\"Hello\")\n", - " print(float_container.__str__())\n", - " print(string_container.__str__())\n", - "\n", - "use_container()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note the signature of the `__str__()` method, which declares the `self` argument\n", - "with a more specific type. Specifically, it declares that it takes a `Container`\n", - "with an `ElementType` that conforms to the `StringableCollectionElement` trait.\n", - "\n", - "```mojo\n", - "def __str__[StrElementType: StringableCollectionElement, //](\n", - " self: Container[StrElementType]) -> String:\n", - "```\n", - "\n", - "This trait must be a superset of `ElementType`'s original trait: for example,\n", - "`StringableCollectionElement` inherits from `CollectionElement`, so it includes\n", - "all of requirements of the original trait.\n", - "\n", - "Note that the `use_container()` function calls the `__str__()` method directly,\n", - "rather than calling `str(float_container)`. One current limitation of\n", - "conditional conformance is that Mojo can't recognize the struct\n", - "`Container[Int]` as conforming to `Stringable`, even though the `__str__()`\n", - "method is implemented for any `ElementType` that's also `Stringable`." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Case study: the SIMD type\n", - "\n", - "For a real-world example of a parameterized type, let's look at the \n", - "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type from Mojo's standard library.\n", - "\n", - "[Single instruction, multiple data (SIMD)](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) is a parallel processing technology built into many modern CPUs,\n", - "GPUs, and custom accelerators. SIMD allows you to perform a single operation on\n", - "multiple pieces of data at once. For example, if you want to take the square \n", - "root of each element in an array, you can use SIMD to parallelize the work. \n", - "\n", - "Processors implement SIMD using low-level vector registers in hardware that hold\n", - "multiple instances of a scalar data type. In order to use the SIMD instructions\n", - "on these processors, the data must be shaped into the proper SIMD width\n", - "(data type) and length (vector size). Processors may support 512-bit or\n", - "longer SIMD vectors, and support many data types from 8-bit integers to 64-bit \n", - "floating point numbers, so it's not practical to define all of the possible SIMD\n", - "variations. \n", - "\n", - "Mojo's [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type (defined as a struct)\n", - "exposes the common SIMD operations through its methods, and makes the SIMD data type\n", - "and size values parametric. This allows you to directly map your data to the \n", - "SIMD vectors on any hardware.\n", - "\n", - "Here's a cut-down (non-functional) version of Mojo's `SIMD` type definition:\n", - "\n", - "```mojo\n", - "struct SIMD[type: DType, size: Int]:\n", - " var value: … # Some low-level MLIR stuff here\n", - "\n", - " # Create a new SIMD from a number of scalars\n", - " fn __init__(out self, *elems: SIMD[type, 1]): ...\n", - "\n", - " # Fill a SIMD with a duplicated scalar value.\n", - " @staticmethod\n", - " fn splat(x: SIMD[type, 1]) -> SIMD[type, size]: ...\n", - "\n", - " # Cast the elements of the SIMD to a different elt type.\n", - " fn cast[target: DType](self) -> SIMD[target, size]: ...\n", - "\n", - " # Many standard operators are supported.\n", - " fn __add__(self, rhs: Self) -> Self: ...\n", - "```\n", - "\n", - "So you can create and use a SIMD vector like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 4 9 16 " - ] - } - ], - "source": [ - "var vector = SIMD[DType.int16, 4](1, 2, 3, 4)\n", - "vector = vector * vector\n", - "for i in range(4):\n", - " print(vector[i], end=\" \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, a simple arithmetic operator like `*` applied to a pair of \n", - "`SIMD` vector operates on the corresponding elements in each vector.\n", - "\n", - "Defining each SIMD variant with parameters is great for code reuse because the\n", - "`SIMD` type can express all the different vector variants statically, instead of\n", - "requiring the language to pre-define every variant.\n", - "\n", - "Because `SIMD` is a parameterized type, the `self` argument in its functions\n", - "carries those parameters—the full type name is `SIMD[type, size]`. Although\n", - "it's valid to write this out (as shown in the return type of `splat()`), this\n", - "can be verbose, so we recommend using the `Self` type (from\n", - "[PEP673](https://peps.python.org/pep-0673/)) like the `__add__` example does." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Overloading on parameters\n", - "\n", - "Functions and methods can be overloaded on their parameter signatures. The\n", - "overload resolution logic filters for candidates according to the following\n", - "rules, in order of precedence:\n", - "\n", - "1) Candidates with the minimal number of implicit conversions (in both arguments\n", - "and parameters).\n", - "2) Candidates without variadic arguments.\n", - "3) Candidates without variadic parameters.\n", - "4) Candidates with the shortest parameter signature.\n", - "5) Non-`@staticmethod` candidates (over `@staticmethod` ones, if available). \n", - "\n", - "If there is more than one candidate after applying these rules, the overload\n", - "resolution fails. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "foo[x: MyInt, a: Int]()\n", - "bar[a: Int](b: Int)\n", - "bar[*a: Int](b: Int)\n" - ] - } - ], - "source": [ - "@register_passable(\"trivial\")\n", - "struct MyInt:\n", - " \"\"\"A type that is implicitly convertible to `Int`.\"\"\"\n", - " var value: Int\n", - "\n", - " @always_inline(\"nodebug\")\n", - " fn __init__(out self, _a: Int):\n", - " self.value = _a\n", - "\n", - "fn foo[x: MyInt, a: Int]():\n", - " print(\"foo[x: MyInt, a: Int]()\")\n", - "\n", - "fn foo[x: MyInt, y: MyInt]():\n", - " print(\"foo[x: MyInt, y: MyInt]()\")\n", - "\n", - "fn bar[a: Int](b: Int):\n", - " print(\"bar[a: Int](b: Int)\")\n", - "\n", - "fn bar[a: Int](*b: Int):\n", - " print(\"bar[a: Int](*b: Int)\")\n", - "\n", - "fn bar[*a: Int](b: Int):\n", - " print(\"bar[*a: Int](b: Int)\")\n", - "\n", - "fn parameter_overloads[a: Int, b: Int, x: MyInt]():\n", - " # `foo[x: MyInt, a: Int]()` is called because it requires no implicit\n", - " # conversions, whereas `foo[x: MyInt, y: MyInt]()` requires one.\n", - " foo[x, a]()\n", - "\n", - " # `bar[a: Int](b: Int)` is called because it does not have variadic\n", - " # arguments or parameters.\n", - " bar[a](b)\n", - "\n", - " # `bar[*a: Int](b: Int)` is called because it has variadic parameters.\n", - " bar[a, a, a](b)\n", - "\n", - "parameter_overloads[1, 2, MyInt(3)]()\n", - "\n", - "struct MyStruct:\n", - " fn __init__(out self):\n", - " pass\n", - "\n", - " fn foo(inout self):\n", - " print(\"calling instance method\")\n", - "\n", - " @staticmethod\n", - " fn foo():\n", - " print(\"calling static method\")\n", - "\n", - "fn test_static_overload():\n", - " var a = MyStruct()\n", - " # `foo(inout self)` takes precedence over a static method.\n", - " a.foo()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using parameterized types and functions\n", - "\n", - "You can use parametric types and functions by passing values to the\n", - "parameters in square brackets. For example, for the `SIMD` type above, `type`\n", - "specifies the data type and `size` specifies the length of the SIMD vector (it\n", - "must be a power of 2):" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "small_vec type: float32 length: 4\n", - "bigger_vec2 type: float32 length: 32\n" - ] - } - ], - "source": [ - "# Make a vector of 4 floats.\n", - "var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)\n", - "\n", - "# Make a big vector containing 1.0 in float16 format.\n", - "var big_vec = SIMD[DType.float16, 32](1.0)\n", - "\n", - "# Do some math and convert the elements to float32.\n", - "var bigger_vec = (big_vec+big_vec).cast[DType.float32]()\n", - "\n", - "# You can write types out explicitly if you want of course.\n", - "var bigger_vec2 : SIMD[DType.float32, 32] = bigger_vec\n", - "\n", - "print('small_vec type:', small_vec.element_type, 'length:', len(small_vec))\n", - "print('bigger_vec2 type:', bigger_vec2.element_type, 'length:', len(bigger_vec2))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `cast()` method also needs a parameter to specify the type you\n", - "want from the cast (the method definition above expects a `target` parametric\n", - "value). Thus, just as the `SIMD` struct is a generic type definition, the\n", - "`cast()` method is a generic method definition. At compile time, the compiler\n", - "creates a concrete version of the `cast()` method with the target parameter\n", - "bound to `DType.float32`.\n", - "\n", - "The code above shows the use of concrete types (that is, the parameters are all\n", - "bound to known values). But the major power of parameters comes from the\n", - "ability to define parametric algorithms and types (code that uses the parameter\n", - "values). For example, here's how to define a parametric algorithm with `SIMD`\n", - "that is type- and width-agnostic:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0.154296875, 0.154296875, 0.154296875, 0.154296875]\n" - ] - } - ], - "source": [ - "from math import sqrt\n", - "\n", - "fn rsqrt[dt: DType, width: Int](x: SIMD[dt, width]) -> SIMD[dt, width]:\n", - " return 1 / sqrt(x)\n", - "\n", - "var v = SIMD[DType.float16, 4](42)\n", - "print(rsqrt(v))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the `x` argument is actually a `SIMD` type based on the function\n", - "parameters. The runtime program can use the value of the parameters, because the\n", - "parameters are resolved at compile-time before they are needed by the runtime\n", - "program (but compile-time parameter expressions cannot use runtime values).\n", - "\n", - "### Parameter inference\n", - "\n", - "The Mojo compiler can often _infer_ parameter values, so you don't always have\n", - "to specify them. For example, you can call the `rsqrt()` function defined above\n", - "without any parameters:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0.174072265625, 0.174072265625, 0.174072265625, 0.174072265625]\n" - ] - } - ], - "source": [ - "var v = SIMD[DType.float16, 4](33)\n", - "print(rsqrt(v))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "The compiler infers its parameters based on the parametric `v`\n", - "value passed into it, as if you wrote `rsqrt[DType.float16, 4](v)` explicitly.\n", - "\n", - "Mojo can also infer the values of struct parameters from the arguments passed to \n", - "a constructor or static method.\n", - "\n", - "For example, consider the following struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct One[Type: StringableCollectionElement]:\n", - " var value: Type\n", - "\n", - " fn __init__(out self, value: Type):\n", - " self.value = value\n", - "\n", - "def use_one():\n", - " s1 = One(123)\n", - " s2 = One(\"Hello\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that you can create an instance of `One` without specifying the `Type`\n", - "parameter—Mojo can infer it from the `value` argument.\n", - "\n", - "You can also infer parameters from a parameterized type passed to a constructor\n", - "or static method:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "infer me\n", - "🔥 1 2\n" - ] - } - ], - "source": [ - "struct Two[Type: StringableCollectionElement]:\n", - " var val1: Type\n", - " var val2: Type\n", - "\n", - " fn __init__(out self, one: One[Type], another: One[Type]):\n", - " self.val1 = one.value\n", - " self.val2 = another.value\n", - " print(str(self.val1), str(self.val2))\n", - "\n", - " @staticmethod\n", - " fn fire(thing1: One[Type], thing2: One[Type]):\n", - " print(\"🔥\", str(thing1.value), str(thing2.value))\n", - "\n", - "def use_two():\n", - " s3 = Two(One(\"infer\"), One(\"me\"))\n", - " Two.fire(One(1), One(2))\n", - "\n", - "use_two()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`Two` takes a `Type` parameter, and its constructor takes values of type\n", - "`One[Type]`. When constructing an instance of `Two`, you don't need to specify\n", - "the `Type` parameter, since it can be inferred from the arguments.\n", - "\n", - "Similarly, the static `fire()` method takes values of type `One[Type]`, so Mojo\n", - "can infer the `Type` value at compile time.\n", - "\n", - ":::note\n", - "\n", - "If you're familiar with C++, you may recognize this as similar to Class Template\n", - "Argument Deduction (CTAD).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional parameters and keyword parameters\n", - "\n", - "Just as you can specify [optional\n", - "arguments](/mojo/manual/functions#optional-arguments) in function signatures,\n", - "you can also define an optional _parameter_ by giving it a default value.\n", - "\n", - "You can also pass parameters by keyword, just like you can use \n", - "[keyword arguments](/mojo/manual/functions#keyword-arguments).\n", - "For a function or struct with multiple optional parameters, using keywords\n", - "allows you to pass only the parameters you want to specify, regardless of\n", - "their position in the function signature. \n", - "\n", - "For example, here's a function with two parameters, each with a default value:" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "fn speak[a: Int = 3, msg: StringLiteral = \"woof\"]():\n", - " print(msg, a)\n", - "\n", - "fn use_defaults() raises:\n", - " speak() # prints 'woof 3'\n", - " speak[5]() # prints 'woof 5'\n", - " speak[7, \"meow\"]() # prints 'meow 7'\n", - " speak[msg=\"baaa\"]() # prints 'baaa 3'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Recall that when a parametric function is called, Mojo can infer the parameter values.\n", - "That is, it can use the parameter values attached to an argument value (see the `sqrt[]()`\n", - "example above). If the parametric function also has a default value defined,\n", - "then the inferred parameter type takes precedence.\n", - "\n", - "For example, in the following code, we update the parametric `speak[]()` function\n", - "to take an argument with a parametric type. Although the function has a default\n", - "parameter value for `a`, Mojo instead uses the inferred `a` parameter value\n", - "from the `bar` argument (as written, the default `a` value can never be used,\n", - "but this is just for demonstration purposes):" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct Bar[v: Int]:\n", - " pass\n", - "\n", - "fn speak[a: Int = 3, msg: StringLiteral = \"woof\"](bar: Bar[a]):\n", - " print(msg, a)\n", - "\n", - "fn use_inferred():\n", - " speak(Bar[9]()) # prints 'woof 9'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As mentioned above, you can also use optional parameters and keyword \n", - "parameters in a struct:" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [], - "source": [ - "struct KwParamStruct[greeting: String = \"Hello\", name: String = \"🔥mojo🔥\"]:\n", - " fn __init__(out self):\n", - " print(greeting, name)\n", - "\n", - "fn use_kw_params():\n", - " var a = KwParamStruct[]() # prints 'Hello 🔥mojo🔥'\n", - " var b = KwParamStruct[name=\"World\"]() # prints 'Hello World'\n", - " var c = KwParamStruct[greeting=\"Hola\"]() # prints 'Hola 🔥mojo🔥'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "Mojo supports positional-only and keyword-only parameters, following the same\n", - "rules as [positional-only and keyword-only\n", - "arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Infer-only parameters\n", - "\n", - "Sometimes you need to declare functions where parameters depend on other\n", - "parameters. Because the signature is processed left to right, a parameter can\n", - "only _depend_ on a parameter earlier in the parameter list. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Value: 2.2000000000000002\n", - "Value is floating-point: True\n" - ] - } - ], - "source": [ - "fn dependent_type[dtype: DType, value: Scalar[dtype]]():\n", - " print(\"Value: \", value)\n", - " print(\"Value is floating-point: \", dtype.is_floating_point())\n", - "\n", - "dependent_type[DType.float64, Float64(2.2)]()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can't reverse the position of the `dtype` and `value` parameters, because\n", - "`value` depends on `dtype`. However, because `dtype` is a required parameter,\n", - "you can't leave it out of the parameter list and let Mojo infer it from `value`:\n", - "\n", - "```mojo\n", - "dependent_type[Float64(2.2)]() # Error!\n", - "```\n", - "\n", - "Infer-only parameters are a special class of parameters that are **always** \n", - "inferred from context. Infer-only parameters are placed at the **beginning** of\n", - "the parameter list, set off from other parameters by the `//` sigil:\n", - "\n", - "```mojo\n", - "fn example[type: CollectionElement, //, list: List[type]]()\n", - "```\n", - "\n", - "Transforming `dtype` into an infer-only parameter solves this problem:" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Value: 2.2000000000000002\n", - "Value is floating-point: True\n" - ] - } - ], - "source": [ - "fn dependent_type[dtype: DType, //, value: Scalar[dtype]]():\n", - " print(\"Value: \", value)\n", - " print(\"Value is floating-point: \", dtype.is_floating_point())\n", - "\n", - "dependent_type[Float64(2.2)]()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because infer-only parameters are declared at the beginning of the parameter\n", - "list, other parameters can depend on them, and the compiler will always attempt\n", - "to infer the infer-only values from bound parameters or arguments.\n", - "\n", - "If the compiler can't infer the value of an infer-only parameter, compilation\n", - "fails." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Variadic parameters\n", - "\n", - "Mojo also supports variadic parameters, similar to \n", - "[Variadic arguments](/mojo/manual/functions#variadic-arguments):" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyTensor[*dimensions: Int]:\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Variadic parameters currently have some limitations that variadic arguments don't have:\n", - "\n", - "- Variadic parameters must be homogeneous—that is, all the values must be the\n", - " same type. \n", - " \n", - "- The parameter type must be register-passable.\n", - "\n", - "- The parameter values aren't automatically projected into a `VariadicList`, so you\n", - " need to construct the list explicitly:" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [], - "source": [ - "fn sum_params[*values: Int]() -> Int:\n", - " alias list = VariadicList(values)\n", - " var sum = 0\n", - " for v in list:\n", - " sum += v\n", - " return sum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Variadic keyword parameters (for example, `**kwparams`) are\n", - "not supported yet." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameter expressions are just Mojo code\n", - "\n", - "A parameter expression is any code expression (such as `a+b`) that occurs where\n", - "a parameter is expected. Parameter expressions support operators and function\n", - "calls, just like runtime code, and all parameter types use the same type\n", - "system as the runtime program (such as `Int` and `DType`).\n", - "\n", - "Because parameter expressions use the same grammar and types as runtime\n", - "Mojo code, you can use many \n", - "[\"dependent type\"](https://en.wikipedia.org/wiki/Dependent_type) features. For\n", - "example, you might want to define a helper function to concatenate two SIMD\n", - "vectors:" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "result type: float32 length: 4\n" - ] - } - ], - "source": [ - "fn concat[ty: DType, len1: Int, len2: Int](\n", - " lhs: SIMD[ty, len1], rhs: SIMD[ty, len2]) -> SIMD[ty, len1+len2]:\n", - "\n", - " var result = SIMD[ty, len1 + len2]()\n", - " for i in range(len1):\n", - " result[i] = SIMD[ty, 1](lhs[i])\n", - " for j in range(len2):\n", - " result[len1 + j] = SIMD[ty, 1](rhs[j])\n", - " return result\n", - "\n", - "var a = SIMD[DType.float32, 2](1, 2)\n", - "var x = concat(a, a)\n", - "\n", - "print('result type:', x.element_type, 'length:', len(x))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the resulting length is the sum of the input vector lengths, and this is expressed with a simple `+` operation. \n", - "\n", - "### Powerful compile-time programming\n", - "\n", - "While simple expressions are useful, sometimes you want to write imperative\n", - "compile-time logic with control flow. You can even do compile-time recursion.\n", - "For instance, here is an example \"tree reduction\" algorithm that sums all\n", - "elements of a vector recursively into a scalar:" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 2, 3, 4]\n", - "Elements sum: 10\n" - ] - } - ], - "source": [ - "fn slice[ty: DType, new_size: Int, size: Int](\n", - " x: SIMD[ty, size], offset: Int) -> SIMD[ty, new_size]:\n", - " var result = SIMD[ty, new_size]()\n", - " for i in range(new_size):\n", - " result[i] = SIMD[ty, 1](x[i + offset])\n", - " return result\n", - "\n", - "fn reduce_add[ty: DType, size: Int](x: SIMD[ty, size]) -> Int:\n", - " @parameter\n", - " if size == 1:\n", - " return int(x[0])\n", - " elif size == 2:\n", - " return int(x[0]) + int(x[1])\n", - "\n", - " # Extract the top/bottom halves, add them, sum the elements.\n", - " alias half_size = size // 2\n", - " var lhs = slice[ty, half_size, size](x, 0)\n", - " var rhs = slice[ty, half_size, size](x, half_size)\n", - " return reduce_add[ty, half_size](lhs + rhs)\n", - "\n", - "var x = SIMD[DType.index, 4](1, 2, 3, 4)\n", - "print(x)\n", - "print(\"Elements sum:\", reduce_add(x))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that\n", - "runs at compile-time. It requires that its condition be a valid parameter\n", - "expression, and ensures that only the live branch of the `if` statement is\n", - "compiled into the program. (This is similar to use of the `@parameter` decorator\n", - "with a `for` loop shown earlier.)\n", - "\n", - "## Mojo types are just parameter expressions\n", - "\n", - "While we've shown how you can use parameter expressions within types, type\n", - "annotations can themselves be arbitrary expressions (just like in Python).\n", - "Types in Mojo have a special metatype type, allowing type-parametric algorithms\n", - "and functions to be defined. \n", - "\n", - "For example, we can create a simplified `Array` that supports arbitrary types of\n", - "elements (via the `AnyTrivialRegType` parameter):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import UnsafePointer\n", - "\n", - "struct Array[T: AnyTrivialRegType]:\n", - " var data: UnsafePointer[T]\n", - " var size: Int\n", - "\n", - " fn __init__(out self, size: Int, value: T):\n", - " self.size = size\n", - " self.data = UnsafePointer[T].alloc(self.size)\n", - " for i in range(self.size):\n", - " (self.data + i).init_pointee_copy(value)\n", - "\n", - " fn __getitem__(self, i: Int) -> T:\n", - " return self.data[i]\n", - "\n", - " fn __del__(owned self):\n", - " for i in range(self.size):\n", - " (self.data + i).destroy_pointee()\n", - " self.data.free()\n", - "\n", - "var v = Array[Float32](4, 3.14)\n", - "print(v[0], v[1], v[2], v[3])" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the `T` parameter is being used as the formal type for the\n", - "`value` arguments and the return type of the `__getitem__()` function. \n", - "Parameters allow the `Array` type to provide different APIs based on the\n", - "different use-cases. \n", - "\n", - "There are many other cases that benefit from more advanced use of parameters.\n", - "For example, you can execute a closure N times in parallel, feeding in a value\n", - "from the context, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [], - "source": [ - "fn parallelize[func: fn (Int) -> None](num_work_items: Int):\n", - " # Not actually parallel: see the 'algorithm' module for real implementation.\n", - " for i in range(num_work_items):\n", - " func(i)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another example where this is important is with variadic generics, where an\n", - "algorithm or data structure may need to be defined over a list of heterogeneous\n", - "types such as for a tuple. Right now, this is not fully supported in Mojo and \n", - "requires writing some MLIR by hand. In the future, this will be possible in pure\n", - "Mojo." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `alias`: named parameter expressions\n", - "\n", - "It is very common to want to *name* compile-time values. Whereas `var` defines a\n", - "runtime value, we need a way to define a\n", - "compile-time temporary value. For this, Mojo uses an `alias` declaration. \n", - "\n", - "For example, the [`DType`](/mojo/stdlib/builtin/dtype/DType) struct \n", - "implements a simple enum using aliases for the enumerators like this (the actual\n", - "`DType` implementation details vary a bit):\n", - "\n", - "```mojo\n", - "struct DType:\n", - " var value : UI8\n", - " alias invalid = DType(0)\n", - " alias bool = DType(1)\n", - " alias int8 = DType(2)\n", - " alias uint8 = DType(3)\n", - " alias int16 = DType(4)\n", - " alias int16 = DType(5)\n", - " ...\n", - " alias float32 = DType(15)\n", - "```\n", - "\n", - "This allows clients to use `DType.float32` as a parameter expression (which also\n", - "works as a runtime value) naturally. Note that this is invoking the\n", - "runtime constructor for `DType` at compile-time.\n", - "\n", - "Types are another common use for aliases. Because types are compile-time\n", - "expressions, it is handy to be able to do things like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": {}, - "outputs": [], - "source": [ - "alias Float16 = SIMD[DType.float16, 1]\n", - "alias UInt8 = SIMD[DType.uint8, 1]\n", - "\n", - "var x: Float16 = 0 # Float16 works like a \"typedef\"" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Like `var` variables, aliases obey scope, and you can use local aliases within\n", - "functions as you'd expect." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Fully-bound, partially-bound, and unbound types\n", - "\n", - "A parametric type with its parameters specified is said to be _fully-bound_. \n", - "That is, all of its parameters are bound to values. As mentioned before, you can\n", - "only instantiate a fully-bound type (sometimes called a _concrete type_).\n", - "\n", - "However, parametric types can be _unbound_ or _partially bound_ in some\n", - "contexts. For example, you can alias a partially-bound type to create a new type\n", - "that requires fewer parameters:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [], - "source": [ - "from collections import Dict\n", - "\n", - "alias StringKeyDict = Dict[String, _]\n", - "var b = StringKeyDict[UInt8]()\n", - "b[\"answer\"] = 42" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The\n", - "underscore `_` in the parameter list indicates that the second parameter,\n", - "`V` (the value type), is unbound.\n", - "You specify the `V` parameter later, when you use `StringKeyDict`.\n", - "\n", - "For example, given the following type:" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyType[s: String, i: Int, i2: Int, b: Bool = True]:\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It can appear in code in the following forms:\n", - "\n", - "- _Fully bound_, with all of its parameters specified:\n", - "\n", - " ```mojo\n", - " MyType[\"Hello\", 3, 4, True]\n", - " ```\n", - "\n", - "- _Partially bound_, with *some but not all* of its parameters specified:\n", - "\n", - " ```mojo\n", - " MyType[\"Hola\", _, _, True]\n", - " ```\n", - "\n", - "- _Unbound_, with no parameters specified:\n", - "\n", - " ```mojo\n", - " MyType[_, _, _, _]\n", - " ```\n", - "\n", - "You can also use the star-underscore expression `*_` to unbind an arbitrary\n", - "number of positional parameters at the end of a parameter\n", - "list.\n", - "\n", - "```mojo\n", - "# These two types are equivalent\n", - "MyType[\"Hello\", *_]\n", - "MyType[\"Hello\", _, _, _]\n", - "```\n", - "\n", - "When a parameter is explicitly unbound with the `_` or `*_` expression, you\n", - "**must** specify a value for that parameter to use the type. Any default value\n", - "from the original type declaration is ignored.\n", - "\n", - "Partially-bound and unbound parametric types can be used in some contexts where\n", - "the missing (unbound) parameters will be supplied later—such as in \n", - "[aliases](#alias-named-parameter-expressions) and\n", - "[automatically parameterized functions](#automatic-parameterization-of-functions).\n", - "\n", - "### Omitted parameters\n", - "\n", - "Mojo also supports an alternate format for unbound parameter where the parameter\n", - "is simply omitted from the expression:\n", - "\n", - "```mojo\n", - "# Partially bound\n", - "MyType[\"Hi there\"]\n", - "# Unbound\n", - "MyType\n", - "```\n", - "\n", - "This format differs from the explicit unbinding syntax described above in that\n", - "the default values for omitted parameters are bound immediately. For example, \n", - "the following expressions are equivalent: \n", - "\n", - "```mojo\n", - "MyType[\"Hi there\"]\n", - "# equivalent to\n", - "MyType[\"Hi there\", _, _, True] # Uses the default value for `b`\n", - "```\n", - "\n", - ":::note \n", - "\n", - "This format is currently supported for backwards compatibility. We intend to\n", - "deprecate this format in the future in favor of the explicit unbinding syntax.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Automatic parameterization of functions\n", - "\n", - "Mojo supports \"automatic\" parameterization of functions. If a function \n", - "argument type is a \n", - "[partially-bound or unbound type](#fully-bound-partially-bound-and-unbound-types),\n", - "the unbound parameters are automatically added as input parameters on the \n", - "function. This is easier to understand with an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "float64\n", - "4\n" - ] - } - ], - "source": [ - "fn print_params(vec: SIMD[*_]):\n", - " print(vec.type)\n", - " print(vec.size)\n", - "\n", - "var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)\n", - "print_params(v)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the above example, the `print_params` function is automatically \n", - "parameterized. The `vec` argument takes an argument of type `SIMD[*_]`. This is\n", - "an [unbound parameterized\n", - "type](#fully-bound-partially-bound-and-unbound-types)—that is, it doesn't\n", - "specify any parameter values for the type. Mojo treats the unbound parameters\n", - "on `vec` as implicit parameters on the function. This is roughly equivalent to\n", - "the following code, which includes _explicit_ input parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": {}, - "outputs": [], - "source": [ - "fn print_params[t: DType, s: Int](vec: SIMD[t, s]):\n", - " print(vec.type)\n", - " print(vec.size)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "When you call `print_params()` you must pass it a concrete instance of the \n", - "`SIMD` type—that is, one with all of its parameters specified, like \n", - "`SIMD[DType.float64, 4]`. The Mojo compiler _infers_ the parameter \n", - "values from the input argument.\n", - "\n", - "With a manually parameterized function, you can access the input parameters by\n", - "name (for example, `t` and `s` in the previous example). For an\n", - "automatically parameterized function, you can access the parameters as\n", - "attributes on the argument (for example, `vec.type`). \n", - "\n", - "This ability to access a type's input parameters is not specific to \n", - "automatically parameterized functions, you can use it anywhere. You can access \n", - "the input parameters of a parameterized type as attributes on the type itself:" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": {}, - "outputs": [], - "source": [ - "fn on_type():\n", - " print(SIMD[DType.float32, 2].size) # prints 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or as attributes on an _instance_ of the type:" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [], - "source": [ - "fn on_instance():\n", - " var x = SIMD[DType.int32, 2](4, 8)\n", - " print(x.type) # prints int32" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can even use this syntax in the function's signature to define a \n", - "function's arguments and return type based on an argument's parameters.\n", - "For example, if you want your function to take two SIMD vectors with the same\n", - "type and size, you can write code like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 0, 2, 0, 3, 0, 4, 0]\n" - ] - } - ], - "source": [ - "fn interleave(v1: SIMD, v2: __type_of(v1)) -> SIMD[v1.type, v1.size*2]:\n", - " var result = SIMD[v1.type, v1.size*2]()\n", - " for i in range(v1.size):\n", - " result[i*2] = SIMD[v1.type, 1](v1[i])\n", - " result[i*2+1] = SIMD[v1.type, 1](v2[i])\n", - " return result\n", - "\n", - "var a = SIMD[DType.int16, 4](1, 2, 3, 4)\n", - "var b = SIMD[DType.int16, 4](0, 0, 0, 0)\n", - "var c = interleave(a, b)\n", - "print(c)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As shown in the example, you can use the magic `__type_of(x)` call if you just want to match the type of an argument. In this case, it's more convenient and compact that writing the equivalent `SIMD[v1.type, v1.size]`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Automatic parameterization with partially-bound types\n", - "\n", - "Mojo also supports automatic parameterization: with [partially-bound\n", - "parameterized types](#fully-bound-partially-bound-and-unbound-types) (that is,\n", - "types with some but not all of the parameters specified).\n", - "\n", - "For example, suppose we have a `Fudge` struct with three parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):\n", - " fn __str__(self) -> String:\n", - " return str(\"Fudge (\") + str(sugar) + \",\" +\n", - " str(cream) + \",\" + str(chocolate) + \")\"\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can write a function that takes a `Fudge` argument with just one bound \n", - "parameter (it's _partially bound_):" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [], - "source": [ - "fn eat(f: Fudge[5, *_]):\n", - " print(\"Ate \" + str(f))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `eat()` function takes a `Fudge` struct with the first parameter (`sugar`)\n", - "bound to the value 5. The second and third parameters, `cream` and `chocolate`\n", - "are unbound.\n", - "\n", - "The unbound `cream` and `chocolate` parameters become implicit input parameters\n", - "on the `eat` function. In practice, this is roughly equivalent to writing:" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [], - "source": [ - "fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]):\n", - " print(\"Ate\", str(f))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In both cases, we can call the function by passing in an instance with the\n", - "`cream` and `chocolate` parameters bound:" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ate Fudge (5,5,7)\n", - "Ate Fudge (5,8,9)\n" - ] - } - ], - "source": [ - "eat(Fudge[5, 5, 7]())\n", - "eat(Fudge[5, 8, 9]())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you try to pass in an argument with a `sugar` value other than 5,\n", - "compilation fails, because it doesn't match the argument type:\n", - "\n", - "```mojo\n", - "eat(Fudge[12, 5, 7]()) \n", - "# ERROR: invalid call to 'eat': argument #0 cannot be converted from 'Fudge[12, 5, 7]' to 'Fudge[5, 5, 7]'\n", - "```\n", - "\n", - "\n", - "You can also explicitly unbind individual parameters. This gives you \n", - "more freedom in specifying unbound parameters.\n", - "\n", - "For example, you might want to let the user specify values for `sugar` and \n", - "`chocolate`, and leave `cream` constant. To do this, replace each unbound\n", - "parameter value with a single underscore (`_`):" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [], - "source": [ - "fn devour(f: Fudge[_, 6, _]):\n", - " print(\"Devoured\", str(f))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, the unbound parameters (`sugar` and `chocolate`) are added as implicit\n", - "input parameters on the function. This version is roughly equivalent to the\n", - "following code, where these two values are explicitly bound to the input \n", - "parameters, `su` and `ch`:" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [], - "source": [ - "fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):\n", - " print(\"Devoured\", str(f))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also specify parameters by keyword, or mix positional and keyword\n", - "parameters, so the following function is roughly equivalent to the previous one:\n", - "the first parameter, `sugar` is explicitly unbound with the underscore character.\n", - "The `chocolate` parameter is unbound using the keyword syntax, `chocolate=_`. \n", - "And `cream` is explicitly bound to the value 6:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [], - "source": [ - "fn devour(f: Fudge[_, chocolate=_, cream=6]):\n", - " print(\"Devoured\", str(f))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All three versions of the `devour()` function work with the following calls:" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Devoured Fudge (3,6,9)\n", - "Devoured Fudge (4,6,8)\n" - ] - } - ], - "source": [ - "devour(Fudge[3, 6, 9]())\n", - "devour(Fudge[4, 6, 8]())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Legacy syntax (omitted parameters)\n", - "\n", - "You can also specify an unbound or partially-bound type by omitting parameters: \n", - "for example:" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ate Fudge (5,4,7)\n" - ] - } - ], - "source": [ - "fn nibble(f: Fudge[5]):\n", - " print(\"Ate\", str(f))\n", - "\n", - "nibble(Fudge[5, 4, 7]())\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, `Fudge[5]` works like `Fudge[5, *_]` **except** in the handling of\n", - "parameters with default values. Instead of discarding the default value of\n", - "`chocolate`, `Fudge[5]` binds the default value immediately, making it\n", - "equivalent to: `Fudge[5, _, 7]`.\n", - "\n", - "This means that the following code won't compile with the previous definition\n", - "for the `nibble()` function, since it doesn't use the default value for\n", - "`chocolate`:\n", - "\n", - "```mojo\n", - "nibble(Fudge[5, 5, 9]())\n", - "# ERROR: invalid call to 'nibble': argument #0 cannot be converted from 'Fudge[5, 5, 9]' to 'Fudge[5, 5, 7]'\n", - "```\n", - "\n", - ":::note TODO\n", - "\n", - "Support for omitting unbound parameters will eventually be deprecated in\n", - "favor of explicitly unbound parameters using `_` and `*_`. \n", - "\n", - ":::\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `rebind()` builtin\n", - "\n", - "One of the consequences of Mojo not performing function instantiation in the\n", - "parser like C++ is that Mojo cannot always figure out whether some parametric\n", - "types are equal and complain about an invalid conversion. This typically occurs\n", - "in static dispatch patterns. For example, the following code won't compile:\n", - "\n", - "```mojo\n", - "fn take_simd8(x: SIMD[DType.float32, 8]):\n", - " pass\n", - "\n", - "fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]):\n", - " @parameter\n", - " if nelts == 8:\n", - " take_simd8(x)\n", - "```\n", - "\n", - "The parser will complain:\n", - "\n", - "```plaintext\n", - "error: invalid call to 'take_simd8': argument #0 cannot be converted from\n", - "'SIMD[f32, nelts]' to 'SIMD[f32, 8]'\n", - " take_simd8(x)\n", - " ~~~~~~~~~~^~~\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is because the parser fully type-checks the function without instantiation,\n", - "and the type of `x` is still `SIMD[f32, nelts]`, and not `SIMD[f32, 8]`, despite\n", - "the static conditional. The remedy is to manually \"rebind\" the type of `x`,\n", - "using the `rebind` builtin, which inserts a compile-time assert that the input\n", - "and result types resolve to the same type after function instantiation:" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "metadata": {}, - "outputs": [], - "source": [ - "fn take_simd8(x: SIMD[DType.float32, 8]):\n", - " pass\n", - "\n", - "fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]):\n", - " @parameter\n", - " if nelts == 8:\n", - " take_simd8(rebind[SIMD[DType.float32, 8]](x))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/parameters/index.mdx b/docs/manual/parameters/index.mdx new file mode 100644 index 0000000000..788c87977a --- /dev/null +++ b/docs/manual/parameters/index.mdx @@ -0,0 +1,1270 @@ +--- +title: "Parameterization: compile-time metaprogramming" +description: An introduction to parameters and compile-time metaprogramming. +--- + +Many languages have facilities for *metaprogramming*: that is, for writing code that generates or modifies code. Python has facilities for dynamic metaprogramming: features like decorators, metaclasses, and many more. These features make Python very flexible and productive, but since they're dynamic, they come with runtime overhead. Other languages have static or compile-time metaprogramming features, like C preprocessor macros and C++ templates. These can be limiting and hard to use. + +To support Modular's work in AI, Mojo aims to provide powerful, easy-to-use metaprogramming with zero runtime cost. This compile-time metaprogramming uses the same language as runtime programs, so you don't have to learn a new language—just a few new features. + +The main new feature is *parameters*. You can think of a parameter as a compile-time variable that becomes a runtime constant. This usage of "parameter" is probably different from what you're used to from other languages, where "parameter" and "argument" are often used interchangeably. In Mojo, "parameter" and "parameter expression" refer to compile-time values, and "argument" and "expression" refer to runtime values. + +In Mojo, you can add parameters to a struct or function. You can also define +named parameter expressions—aliases—that you can use as runtime constants. + +## Parameterized functions + +To define a *parameterized function*, add parameters in square brackets ahead +of the argument list. Each parameter is formatted just like an argument: a +parameter name, followed by a colon and a type (which is required). In the +following example, the function has a single parameter, `count` of type `Int`. + +```mojo +fn repeat[count: Int](msg: String): + @parameter + for i in range(count): + print(msg) +``` + +The [`@parameter`](/mojo/manual/decorators/parameter) directive shown here +causes the `for` loop to be evaluated at compile time. The directive only works +if the loop limits are compile-time constants. Since `count` is a parameter, +`range(count)` can be calculated at compile time. + +Calling a parameterized function, you provide values for the parameters, just +like function arguments: + +```mojo +repeat[3]("Hello") +``` + +```output +Hello +Hello +Hello +``` + +The compiler resolves the parameter values during compilation, and creates a +concrete version of the `repeat[]()` function for each unique parameter value. +After resolving the parameter values and unrolling the loop, the `repeat[3]()` +function would be roughly equivalent to this: + +```mojo +fn repeat_3(msg: String): + print(msg) + print(msg) + print(msg) +``` + +:::note + +This doesn't represent actual code generated by the compiler. By the +time parameters are resolved, Mojo code has already been transformed to an +intermediate representation in [MLIR](https://mlir.llvm.org/). + +::: + +If the compiler can't resolve all parameter values to constant values, +compilation fails. + +## Parameters and generics + +"Generics" refers to functions that can act on multiple types of values, or +containers that can hold multiple types of values. For example, +[`List`](/mojo/stdlib/collections/list/List), can hold +different types of values, so you can have a list of `Int` values, or +a list of `String` values). + +In Mojo, generics use parameters to specify types. For example, `List` +takes a type parameter, so a vector of integers is written `List[Int]`. +So all generics use parameters, but **not** everything that uses parameters is a +generic. + +For example, the `repeat[]()` function in the previous section includes +parameter of type `Int`, and an argument of type `String`. It's parameterized, +but not generic. A generic function or struct is parameterized on *type*. For +example, we could rewrite `repeat[]()` to take any type of argument that +conforms to the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait: + +```mojo +fn repeat[MsgType: Stringable, count: Int](msg: MsgType): + @parameter + for i in range(count): + print(str(msg)) + +# Must use keyword parameter for `count` +repeat[count=2](42) +``` + +```output +42 +42 +``` + +This updated function takes any `Stringable` type, so you can pass it an `Int`, +`String`, or `Bool` value. + +You can't pass the `count` as a positional keyword without also specifying `MsgType`. +You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now +you can pass the following parameter `count` positionally: + +```mojo +fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType): + @parameter + for i in range(count): + print(str(msg)) + +# MsgType is always inferred, so first positional keyword `2` is passed to `count` +repeat[2](42) +``` + +```output +42 +42 +``` + +Mojo's support for generics is still early. You can write generic functions like +this using traits and parameters. You can also write generic collections like +`List` and `Dict`. If you're interested in learning how these types work, you +can find the source code for the standard library collection types +[on GitHub](https://github.com/modularml/mojo/blob/nightly/stdlib/src/collections/). + +## Parameterized structs + +You can also add parameters to structs. You can use parameterized structs to +build generic collections. For example, a generic array type might include code +like this: + +```mojo +from memory import UnsafePointer + +struct GenericArray[ElementType: CollectionElement]: + var data: UnsafePointer[ElementType] + var size: Int + + fn __init__(out self, *elements: ElementType): + self.size = len(elements) + self.data = UnsafePointer[ElementType].alloc(self.size) + for i in range(self.size): + (self.data + i).init_pointee_move(elements[i]) + + fn __del__(owned self): + for i in range(self.size): + (self.data + i).destroy_pointee() + self.data.free() + + fn __getitem__(self, i: Int) raises -> ref [self] ElementType: + if (i < self.size): + return self.data[i] + else: + raise Error("Out of bounds") +``` + +This struct has a single parameter, `ElementType`, which is a placeholder for +the data type you want to store in the array, sometimes called a *type +parameter*. `ElementType` is typed as +[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement), which is a +[trait](/mojo/manual/traits) representing any type that can be copied and moved. + +As with parameterized functions, you need to pass in parameter values when you +use a parameterized struct. In this case, when you create an instance of +`GenericArray`, you need to specify the type you want to store, like `Int`, or +`Float64`. (This is a little confusing, because the *parameter value* you're +passing in this case is a *type*. That's OK: a Mojo type is a valid compile-time +value.) + +You'll see that `ElementType` is used throughout the struct where you'd usually see a +type name. For example, as the formal type for the `elements` in the +constructor, and the return type of the `__getitem__()` method. + +Here's an example of using `GenericArray`: + +```mojo +var array = GenericArray[Int](1, 2, 3, 4) +for i in range(array.size): + print(array[i], end=" ") +``` + +```output +1 2 3 4 +``` + +A parameterized struct can use the `Self` type to represent a concrete instance +of the struct (that is, with all its parameters specified). For example, you +could add a static factory method to `GenericArray` with the following +signature: + +```mojo +struct GenericArray[ElementType: CollectionElement]: + ... + + @staticmethod + fn splat(count: Int, value: ElementType) -> Self: + # Create a new array with count instances of the given value +``` + +Here, `Self` is equivalent to writing `GenericArray[ElementType]`. That is, you +can call the `splat()` method like this: + +```mojo +GenericArray[Float64].splat(8, 0) +``` + +The method returns an instance of `GenericArray[Float64]`. + +### Conditional conformance + +When creating a generic struct, you might want to define some methods that +require extra features. For example, consider a collection like `GenericArray` +that holds instances of `CollectionElement`. The `CollectionElement` trait +only requires that the stored data type be copyable and movable. This +imposes a lot of limitations: you can't implement a `sort()` method because +you can't guarantee that the stored type supports the comparison operators; you can't +write a useful `__str__()` or `__repr__()` dunder method because you can't +guarantee that the stored type supports conversion to a string. + +The answer to these issues is *conditional conformance*, which lets you define a +method that requires additional features. You do this by defining the `self` +value that has a more specific bound on one or more of its parameters. + +For example, the following code defines a `Container` type that holds an +instance of `CollectionElement`. It also defines a `__str__()` method that can +only be called if the stored `ElementType` conforms to +`StringableCollectionElement`: + +```mojo +@value +struct Container[ElementType: CollectionElement]: + var element: ElementType + + def __str__[StrElementType: StringableCollectionElement, //]( + self: Container[StrElementType]) -> String: + return str(self.element) + +def use_container(): + float_container = Container(5) + string_container = Container("Hello") + print(float_container.__str__()) + print(string_container.__str__()) + +use_container() +``` + +```output +5 +Hello +``` + +Note the signature of the `__str__()` method, which declares the `self` argument +with a more specific type. Specifically, it declares that it takes a `Container` +with an `ElementType` that conforms to the `StringableCollectionElement` trait. + +```mojo +def __str__[StrElementType: StringableCollectionElement, //]( + self: Container[StrElementType]) -> String: +``` + +This trait must be a superset of `ElementType`'s original trait: for example, +`StringableCollectionElement` inherits from `CollectionElement`, so it includes +all of requirements of the original trait. + +Note that the `use_container()` function calls the `__str__()` method directly, +rather than calling `str(float_container)`. One current limitation of +conditional conformance is that Mojo can't recognize the struct +`Container[Int]` as conforming to `Stringable`, even though the `__str__()` +method is implemented for any `ElementType` that's also `Stringable`. + +### Case study: the SIMD type + +For a real-world example of a parameterized type, let's look at the +[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type from Mojo's standard library. + +[Single instruction, multiple data (SIMD)](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) is a parallel processing technology built into many modern CPUs, +GPUs, and custom accelerators. SIMD allows you to perform a single operation on +multiple pieces of data at once. For example, if you want to take the square +root of each element in an array, you can use SIMD to parallelize the work. + +Processors implement SIMD using low-level vector registers in hardware that hold +multiple instances of a scalar data type. In order to use the SIMD instructions +on these processors, the data must be shaped into the proper SIMD width +(data type) and length (vector size). Processors may support 512-bit or +longer SIMD vectors, and support many data types from 8-bit integers to 64-bit +floating point numbers, so it's not practical to define all of the possible SIMD +variations. + +Mojo's [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type (defined as a struct) +exposes the common SIMD operations through its methods, and makes the SIMD data type +and size values parametric. This allows you to directly map your data to the +SIMD vectors on any hardware. + +Here's a cut-down (non-functional) version of Mojo's `SIMD` type definition: + +```mojo +struct SIMD[type: DType, size: Int]: + var value: … # Some low-level MLIR stuff here + + # Create a new SIMD from a number of scalars + fn __init__(out self, *elems: SIMD[type, 1]): ... + + # Fill a SIMD with a duplicated scalar value. + @staticmethod + fn splat(x: SIMD[type, 1]) -> SIMD[type, size]: ... + + # Cast the elements of the SIMD to a different elt type. + fn cast[target: DType](self) -> SIMD[target, size]: ... + + # Many standard operators are supported. + fn __add__(self, rhs: Self) -> Self: ... +``` + +So you can create and use a SIMD vector like this: + +```mojo +var vector = SIMD[DType.int16, 4](1, 2, 3, 4) +vector = vector * vector +for i in range(4): + print(vector[i], end=" ") +``` + +```output +1 4 9 16 +``` + +As you can see, a simple arithmetic operator like `*` applied to a pair of +`SIMD` vector operates on the corresponding elements in each vector. + +Defining each SIMD variant with parameters is great for code reuse because the +`SIMD` type can express all the different vector variants statically, instead of +requiring the language to pre-define every variant. + +Because `SIMD` is a parameterized type, the `self` argument in its functions +carries those parameters—the full type name is `SIMD[type, size]`. Although +it's valid to write this out (as shown in the return type of `splat()`), this +can be verbose, so we recommend using the `Self` type (from +[PEP673](https://peps.python.org/pep-0673/)) like the `__add__` example does. + +## Overloading on parameters + +Functions and methods can be overloaded on their parameter signatures. The +overload resolution logic filters for candidates according to the following +rules, in order of precedence: + +1. Candidates with the minimal number of implicit conversions (in both arguments + and parameters). +2. Candidates without variadic arguments. +3. Candidates without variadic parameters. +4. Candidates with the shortest parameter signature. +5. Non-`@staticmethod` candidates (over `@staticmethod` ones, if available). + +If there is more than one candidate after applying these rules, the overload +resolution fails. For example: + +```mojo +@register_passable("trivial") +struct MyInt: + """A type that is implicitly convertible to `Int`.""" + var value: Int + + @always_inline("nodebug") + fn __init__(out self, _a: Int): + self.value = _a + +fn foo[x: MyInt, a: Int](): + print("foo[x: MyInt, a: Int]()") + +fn foo[x: MyInt, y: MyInt](): + print("foo[x: MyInt, y: MyInt]()") + +fn bar[a: Int](b: Int): + print("bar[a: Int](b: Int)") + +fn bar[a: Int](*b: Int): + print("bar[a: Int](*b: Int)") + +fn bar[*a: Int](b: Int): + print("bar[*a: Int](b: Int)") + +fn parameter_overloads[a: Int, b: Int, x: MyInt](): + # `foo[x: MyInt, a: Int]()` is called because it requires no implicit + # conversions, whereas `foo[x: MyInt, y: MyInt]()` requires one. + foo[x, a]() + + # `bar[a: Int](b: Int)` is called because it does not have variadic + # arguments or parameters. + bar[a](b) + + # `bar[*a: Int](b: Int)` is called because it has variadic parameters. + bar[a, a, a](b) + +parameter_overloads[1, 2, MyInt(3)]() + +struct MyStruct: + fn __init__(out self): + pass + + fn foo(inout self): + print("calling instance method") + + @staticmethod + fn foo(): + print("calling static method") + +fn test_static_overload(): + var a = MyStruct() + # `foo(inout self)` takes precedence over a static method. + a.foo() +``` + +```output +foo[x: MyInt, a: Int]() +bar[a: Int](b: Int) +bar[*a: Int](b: Int) +``` + +## Using parameterized types and functions + +You can use parametric types and functions by passing values to the +parameters in square brackets. For example, for the `SIMD` type above, `type` +specifies the data type and `size` specifies the length of the SIMD vector (it +must be a power of 2): + +```mojo +# Make a vector of 4 floats. +var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0) + +# Make a big vector containing 1.0 in float16 format. +var big_vec = SIMD[DType.float16, 32](1.0) + +# Do some math and convert the elements to float32. +var bigger_vec = (big_vec+big_vec).cast[DType.float32]() + +# You can write types out explicitly if you want of course. +var bigger_vec2 : SIMD[DType.float32, 32] = bigger_vec + +print('small_vec type:', small_vec.element_type, 'length:', len(small_vec)) +print('bigger_vec2 type:', bigger_vec2.element_type, 'length:', len(bigger_vec2)) +``` + +```output +small_vec type: float32 length: 4 +bigger_vec2 type: float32 length: 32 +``` + +Note that the `cast()` method also needs a parameter to specify the type you +want from the cast (the method definition above expects a `target` parametric +value). Thus, just as the `SIMD` struct is a generic type definition, the +`cast()` method is a generic method definition. At compile time, the compiler +creates a concrete version of the `cast()` method with the target parameter +bound to `DType.float32`. + +The code above shows the use of concrete types (that is, the parameters are all +bound to known values). But the major power of parameters comes from the +ability to define parametric algorithms and types (code that uses the parameter +values). For example, here's how to define a parametric algorithm with `SIMD` +that is type- and width-agnostic: + +```mojo +from math import sqrt + +fn rsqrt[dt: DType, width: Int](x: SIMD[dt, width]) -> SIMD[dt, width]: + return 1 / sqrt(x) + +var v = SIMD[DType.float16, 4](42) +print(rsqrt(v)) +``` + +```output +[0.154296875, 0.154296875, 0.154296875, 0.154296875] +``` + +Notice that the `x` argument is actually a `SIMD` type based on the function +parameters. The runtime program can use the value of the parameters, because the +parameters are resolved at compile-time before they are needed by the runtime +program (but compile-time parameter expressions cannot use runtime values). + +### Parameter inference + +The Mojo compiler can often *infer* parameter values, so you don't always have +to specify them. For example, you can call the `rsqrt()` function defined above +without any parameters: + +```mojo +var v = SIMD[DType.float16, 4](33) +print(rsqrt(v)) +``` + +```output +[0.174072265625, 0.174072265625, 0.174072265625, 0.174072265625] +``` + +The compiler infers its parameters based on the parametric `v` +value passed into it, as if you wrote `rsqrt[DType.float16, 4](v)` explicitly. + +Mojo can also infer the values of struct parameters from the arguments passed to +a constructor or static method. + +For example, consider the following struct: + +```mojo +@value +struct One[Type: StringableCollectionElement]: + var value: Type + + fn __init__(out self, value: Type): + self.value = value + +def use_one(): + s1 = One(123) + s2 = One("Hello") +``` + +Note that you can create an instance of `One` without specifying the `Type` +parameter—Mojo can infer it from the `value` argument. + +You can also infer parameters from a parameterized type passed to a constructor +or static method: + +```mojo +struct Two[Type: StringableCollectionElement]: + var val1: Type + var val2: Type + + fn __init__(out self, one: One[Type], another: One[Type]): + self.val1 = one.value + self.val2 = another.value + print(str(self.val1), str(self.val2)) + + @staticmethod + fn fire(thing1: One[Type], thing2: One[Type]): + print("🔥", str(thing1.value), str(thing2.value)) + +def use_two(): + s3 = Two(One("infer"), One("me")) + Two.fire(One(1), One(2)) + +use_two() +``` + +```output +infer me +🔥 1 2 +``` + +`Two` takes a `Type` parameter, and its constructor takes values of type +`One[Type]`. When constructing an instance of `Two`, you don't need to specify +the `Type` parameter, since it can be inferred from the arguments. + +Similarly, the static `fire()` method takes values of type `One[Type]`, so Mojo +can infer the `Type` value at compile time. + +:::note + +If you're familiar with C++, you may recognize this as similar to Class Template +Argument Deduction (CTAD). + +::: + +## Optional parameters and keyword parameters + +Just as you can specify [optional +arguments](/mojo/manual/functions#optional-arguments) in function signatures, +you can also define an optional *parameter* by giving it a default value. + +You can also pass parameters by keyword, just like you can use +[keyword arguments](/mojo/manual/functions#keyword-arguments). +For a function or struct with multiple optional parameters, using keywords +allows you to pass only the parameters you want to specify, regardless of +their position in the function signature. + +For example, here's a function with two parameters, each with a default value: + +```mojo +fn speak[a: Int = 3, msg: StringLiteral = "woof"](): + print(msg, a) + +fn use_defaults() raises: + speak() # prints 'woof 3' + speak[5]() # prints 'woof 5' + speak[7, "meow"]() # prints 'meow 7' + speak[msg="baaa"]() # prints 'baaa 3' +``` + +Recall that when a parametric function is called, Mojo can infer the parameter values. +That is, it can use the parameter values attached to an argument value (see the `sqrt[]()` +example above). If the parametric function also has a default value defined, +then the inferred parameter type takes precedence. + +For example, in the following code, we update the parametric `speak[]()` function +to take an argument with a parametric type. Although the function has a default +parameter value for `a`, Mojo instead uses the inferred `a` parameter value +from the `bar` argument (as written, the default `a` value can never be used, +but this is just for demonstration purposes): + +```mojo +@value +struct Bar[v: Int]: + pass + +fn speak[a: Int = 3, msg: StringLiteral = "woof"](bar: Bar[a]): + print(msg, a) + +fn use_inferred(): + speak(Bar[9]()) # prints 'woof 9' +``` + +As mentioned above, you can also use optional parameters and keyword +parameters in a struct: + +```mojo +struct KwParamStruct[greeting: String = "Hello", name: String = "🔥mojo🔥"]: + fn __init__(out self): + print(greeting, name) + +fn use_kw_params(): + var a = KwParamStruct[]() # prints 'Hello 🔥mojo🔥' + var b = KwParamStruct[name="World"]() # prints 'Hello World' + var c = KwParamStruct[greeting="Hola"]() # prints 'Hola 🔥mojo🔥' +``` + +:::note + +Mojo supports positional-only and keyword-only parameters, following the same +rules as [positional-only and keyword-only +arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments). + +::: + +## Infer-only parameters + +Sometimes you need to declare functions where parameters depend on other +parameters. Because the signature is processed left to right, a parameter can +only *depend* on a parameter earlier in the parameter list. For example: + +```mojo +fn dependent_type[dtype: DType, value: Scalar[dtype]](): + print("Value: ", value) + print("Value is floating-point: ", dtype.is_floating_point()) + +dependent_type[DType.float64, Float64(2.2)]() +``` + +```output +Value: 2.2000000000000002 +Value is floating-point: True +``` + +You can't reverse the position of the `dtype` and `value` parameters, because +`value` depends on `dtype`. However, because `dtype` is a required parameter, +you can't leave it out of the parameter list and let Mojo infer it from `value`: + +```mojo +dependent_type[Float64(2.2)]() # Error! +``` + +Infer-only parameters are a special class of parameters that are **always** +inferred from context. Infer-only parameters are placed at the **beginning** of +the parameter list, set off from other parameters by the `//` sigil: + +```mojo +fn example[type: CollectionElement, //, list: List[type]]() +``` + +Transforming `dtype` into an infer-only parameter solves this problem: + +```mojo +fn dependent_type[dtype: DType, //, value: Scalar[dtype]](): + print("Value: ", value) + print("Value is floating-point: ", dtype.is_floating_point()) + +dependent_type[Float64(2.2)]() +``` + +```output +Value: 2.2000000000000002 +Value is floating-point: True +``` + +Because infer-only parameters are declared at the beginning of the parameter +list, other parameters can depend on them, and the compiler will always attempt +to infer the infer-only values from bound parameters or arguments. + +If the compiler can't infer the value of an infer-only parameter, compilation +fails. + +## Variadic parameters + +Mojo also supports variadic parameters, similar to +[Variadic arguments](/mojo/manual/functions#variadic-arguments): + +```mojo +struct MyTensor[*dimensions: Int]: + pass +``` + +Variadic parameters currently have some limitations that variadic arguments don't have: + +* Variadic parameters must be homogeneous—that is, all the values must be the + same type. + +* The parameter type must be register-passable. + +* The parameter values aren't automatically projected into a `VariadicList`, so you + need to construct the list explicitly: + +```mojo +fn sum_params[*values: Int]() -> Int: + alias list = VariadicList(values) + var sum = 0 + for v in list: + sum += v + return sum +``` + +Variadic keyword parameters (for example, `**kwparams`) are +not supported yet. + +## Parameter expressions are just Mojo code + +A parameter expression is any code expression (such as `a+b`) that occurs where +a parameter is expected. Parameter expressions support operators and function +calls, just like runtime code, and all parameter types use the same type +system as the runtime program (such as `Int` and `DType`). + +Because parameter expressions use the same grammar and types as runtime +Mojo code, you can use many +["dependent type"](https://en.wikipedia.org/wiki/Dependent_type) features. For +example, you might want to define a helper function to concatenate two SIMD +vectors: + +```mojo +fn concat[ty: DType, len1: Int, len2: Int]( + lhs: SIMD[ty, len1], rhs: SIMD[ty, len2]) -> SIMD[ty, len1+len2]: + + var result = SIMD[ty, len1 + len2]() + for i in range(len1): + result[i] = SIMD[ty, 1](lhs[i]) + for j in range(len2): + result[len1 + j] = SIMD[ty, 1](rhs[j]) + return result + +var a = SIMD[DType.float32, 2](1, 2) +var x = concat(a, a) + +print('result type:', x.element_type, 'length:', len(x)) +``` + +```output +result type: float32 length: 4 +``` + +Note that the resulting length is the sum of the input vector lengths, and this is expressed with a simple `+` operation. + +### Powerful compile-time programming + +While simple expressions are useful, sometimes you want to write imperative +compile-time logic with control flow. You can even do compile-time recursion. +For instance, here is an example "tree reduction" algorithm that sums all +elements of a vector recursively into a scalar: + +```mojo +fn slice[ty: DType, new_size: Int, size: Int]( + x: SIMD[ty, size], offset: Int) -> SIMD[ty, new_size]: + var result = SIMD[ty, new_size]() + for i in range(new_size): + result[i] = SIMD[ty, 1](x[i + offset]) + return result + +fn reduce_add[ty: DType, size: Int](x: SIMD[ty, size]) -> Int: + @parameter + if size == 1: + return int(x[0]) + elif size == 2: + return int(x[0]) + int(x[1]) + + # Extract the top/bottom halves, add them, sum the elements. + alias half_size = size // 2 + var lhs = slice[ty, half_size, size](x, 0) + var rhs = slice[ty, half_size, size](x, half_size) + return reduce_add[ty, half_size](lhs + rhs) + +var x = SIMD[DType.index, 4](1, 2, 3, 4) +print(x) +print("Elements sum:", reduce_add(x)) +``` + +```output +[1, 2, 3, 4] +Elements sum: 10 +``` + +This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that +runs at compile-time. It requires that its condition be a valid parameter +expression, and ensures that only the live branch of the `if` statement is +compiled into the program. (This is similar to use of the `@parameter` decorator +with a `for` loop shown earlier.) + +## Mojo types are just parameter expressions + +While we've shown how you can use parameter expressions within types, type +annotations can themselves be arbitrary expressions (just like in Python). +Types in Mojo have a special metatype type, allowing type-parametric algorithms +and functions to be defined. + +For example, we can create a simplified `Array` that supports arbitrary types of +elements (via the `AnyTrivialRegType` parameter): + +```mojo +from memory import UnsafePointer + +struct Array[T: AnyTrivialRegType]: + var data: UnsafePointer[T] + var size: Int + + fn __init__(out self, size: Int, value: T): + self.size = size + self.data = UnsafePointer[T].alloc(self.size) + for i in range(self.size): + (self.data + i).init_pointee_copy(value) + + fn __getitem__(self, i: Int) -> T: + return self.data[i] + + fn __del__(owned self): + for i in range(self.size): + (self.data + i).destroy_pointee() + self.data.free() + +var v = Array[Float32](4, 3.14) +print(v[0], v[1], v[2], v[3]) +``` + +Notice that the `T` parameter is being used as the formal type for the +`value` arguments and the return type of the `__getitem__()` function. +Parameters allow the `Array` type to provide different APIs based on the +different use-cases. + +There are many other cases that benefit from more advanced use of parameters. +For example, you can execute a closure N times in parallel, feeding in a value +from the context, like this: + +```mojo +fn parallelize[func: fn (Int) -> None](num_work_items: Int): + # Not actually parallel: see the 'algorithm' module for real implementation. + for i in range(num_work_items): + func(i) +``` + +Another example where this is important is with variadic generics, where an +algorithm or data structure may need to be defined over a list of heterogeneous +types such as for a tuple. Right now, this is not fully supported in Mojo and +requires writing some MLIR by hand. In the future, this will be possible in pure +Mojo. + +## `alias`: named parameter expressions + +It is very common to want to *name* compile-time values. Whereas `var` defines a +runtime value, we need a way to define a +compile-time temporary value. For this, Mojo uses an `alias` declaration. + +For example, the [`DType`](/mojo/stdlib/builtin/dtype/DType) struct +implements a simple enum using aliases for the enumerators like this (the actual +`DType` implementation details vary a bit): + +```mojo +struct DType: + var value : UI8 + alias invalid = DType(0) + alias bool = DType(1) + alias int8 = DType(2) + alias uint8 = DType(3) + alias int16 = DType(4) + alias int16 = DType(5) + ... + alias float32 = DType(15) +``` + +This allows clients to use `DType.float32` as a parameter expression (which also +works as a runtime value) naturally. Note that this is invoking the +runtime constructor for `DType` at compile-time. + +Types are another common use for aliases. Because types are compile-time +expressions, it is handy to be able to do things like this: + +```mojo +alias Float16 = SIMD[DType.float16, 1] +alias UInt8 = SIMD[DType.uint8, 1] + +var x: Float16 = 0 # Float16 works like a "typedef" +``` + +Like `var` variables, aliases obey scope, and you can use local aliases within +functions as you'd expect. + +## Fully-bound, partially-bound, and unbound types + +A parametric type with its parameters specified is said to be *fully-bound*. +That is, all of its parameters are bound to values. As mentioned before, you can +only instantiate a fully-bound type (sometimes called a *concrete type*). + +However, parametric types can be *unbound* or *partially bound* in some +contexts. For example, you can alias a partially-bound type to create a new type +that requires fewer parameters: + +```mojo +from collections import Dict + +alias StringKeyDict = Dict[String, _] +var b = StringKeyDict[UInt8]() +b["answer"] = 42 +``` + +Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The +underscore `_` in the parameter list indicates that the second parameter, +`V` (the value type), is unbound. +You specify the `V` parameter later, when you use `StringKeyDict`. + +For example, given the following type: + +```mojo +struct MyType[s: String, i: Int, i2: Int, b: Bool = True]: + pass +``` + +It can appear in code in the following forms: + +* *Fully bound*, with all of its parameters specified: + + ```mojo + MyType["Hello", 3, 4, True] + ``` + +* *Partially bound*, with *some but not all* of its parameters specified: + + ```mojo + MyType["Hola", _, _, True] + ``` + +* *Unbound*, with no parameters specified: + + ```mojo + MyType[_, _, _, _] + ``` + +You can also use the star-underscore expression `*_` to unbind an arbitrary +number of positional parameters at the end of a parameter +list. + +```mojo +# These two types are equivalent +MyType["Hello", *_] +MyType["Hello", _, _, _] +``` + +When a parameter is explicitly unbound with the `_` or `*_` expression, you +**must** specify a value for that parameter to use the type. Any default value +from the original type declaration is ignored. + +Partially-bound and unbound parametric types can be used in some contexts where +the missing (unbound) parameters will be supplied later—such as in +[aliases](#alias-named-parameter-expressions) and +[automatically parameterized functions](#automatic-parameterization-of-functions). + +### Omitted parameters + +Mojo also supports an alternate format for unbound parameter where the parameter +is simply omitted from the expression: + +```mojo +# Partially bound +MyType["Hi there"] +# Unbound +MyType +``` + +This format differs from the explicit unbinding syntax described above in that +the default values for omitted parameters are bound immediately. For example, +the following expressions are equivalent: + +```mojo +MyType["Hi there"] +# equivalent to +MyType["Hi there", _, _, True] # Uses the default value for `b` +``` + +:::note + +This format is currently supported for backwards compatibility. We intend to +deprecate this format in the future in favor of the explicit unbinding syntax. + +::: + +## Automatic parameterization of functions + +Mojo supports "automatic" parameterization of functions. If a function +argument type is a +[partially-bound or unbound type](#fully-bound-partially-bound-and-unbound-types), +the unbound parameters are automatically added as input parameters on the +function. This is easier to understand with an example: + +```mojo +fn print_params(vec: SIMD[*_]): + print(vec.type) + print(vec.size) + +var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0) +print_params(v) +``` + +```output +float64 +4 +``` + +In the above example, the `print_params` function is automatically +parameterized. The `vec` argument takes an argument of type `SIMD[*_]`. This is +an [unbound parameterized +type](#fully-bound-partially-bound-and-unbound-types)—that is, it doesn't +specify any parameter values for the type. Mojo treats the unbound parameters +on `vec` as implicit parameters on the function. This is roughly equivalent to +the following code, which includes *explicit* input parameters: + +```mojo +fn print_params[t: DType, s: Int](vec: SIMD[t, s]): + print(vec.type) + print(vec.size) +``` + +When you call `print_params()` you must pass it a concrete instance of the +`SIMD` type—that is, one with all of its parameters specified, like +`SIMD[DType.float64, 4]`. The Mojo compiler *infers* the parameter +values from the input argument. + +With a manually parameterized function, you can access the input parameters by +name (for example, `t` and `s` in the previous example). For an +automatically parameterized function, you can access the parameters as +attributes on the argument (for example, `vec.type`). + +This ability to access a type's input parameters is not specific to +automatically parameterized functions, you can use it anywhere. You can access +the input parameters of a parameterized type as attributes on the type itself: + +```mojo +fn on_type(): + print(SIMD[DType.float32, 2].size) # prints 2 +``` + +Or as attributes on an *instance* of the type: + +```mojo +fn on_instance(): + var x = SIMD[DType.int32, 2](4, 8) + print(x.type) # prints int32 +``` + +You can even use this syntax in the function's signature to define a +function's arguments and return type based on an argument's parameters. +For example, if you want your function to take two SIMD vectors with the same +type and size, you can write code like this: + +```mojo +fn interleave(v1: SIMD, v2: __type_of(v1)) -> SIMD[v1.type, v1.size*2]: + var result = SIMD[v1.type, v1.size*2]() + for i in range(v1.size): + result[i*2] = SIMD[v1.type, 1](v1[i]) + result[i*2+1] = SIMD[v1.type, 1](v2[i]) + return result + +var a = SIMD[DType.int16, 4](1, 2, 3, 4) +var b = SIMD[DType.int16, 4](0, 0, 0, 0) +var c = interleave(a, b) +print(c) +``` + +```output +[1, 0, 2, 0, 3, 0, 4, 0] +``` + +As shown in the example, you can use the magic `__type_of(x)` call if you just want to match the type of an argument. In this case, it's more convenient and compact that writing the equivalent `SIMD[v1.type, v1.size]`. + +### Automatic parameterization with partially-bound types + +Mojo also supports automatic parameterization: with [partially-bound +parameterized types](#fully-bound-partially-bound-and-unbound-types) (that is, +types with some but not all of the parameters specified). + +For example, suppose we have a `Fudge` struct with three parameters: + +```mojo +@value +struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable): + fn __str__(self) -> String: + return str("Fudge (") + str(sugar) + "," + + str(cream) + "," + str(chocolate) + ")" + +``` + +We can write a function that takes a `Fudge` argument with just one bound +parameter (it's *partially bound*): + +```mojo +fn eat(f: Fudge[5, *_]): + print("Ate " + str(f)) +``` + +The `eat()` function takes a `Fudge` struct with the first parameter (`sugar`) +bound to the value 5. The second and third parameters, `cream` and `chocolate` +are unbound. + +The unbound `cream` and `chocolate` parameters become implicit input parameters +on the `eat` function. In practice, this is roughly equivalent to writing: + +```mojo +fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]): + print("Ate", str(f)) +``` + +In both cases, we can call the function by passing in an instance with the +`cream` and `chocolate` parameters bound: + +```mojo +eat(Fudge[5, 5, 7]()) +eat(Fudge[5, 8, 9]()) +``` + +```output +Ate Fudge (5,5,7) +Ate Fudge (5,8,9) +``` + +If you try to pass in an argument with a `sugar` value other than 5, +compilation fails, because it doesn't match the argument type: + +```mojo +eat(Fudge[12, 5, 7]()) +# ERROR: invalid call to 'eat': argument #0 cannot be converted from 'Fudge[12, 5, 7]' to 'Fudge[5, 5, 7]' +``` + +You can also explicitly unbind individual parameters. This gives you +more freedom in specifying unbound parameters. + +For example, you might want to let the user specify values for `sugar` and +`chocolate`, and leave `cream` constant. To do this, replace each unbound +parameter value with a single underscore (`_`): + +```mojo +fn devour(f: Fudge[_, 6, _]): + print("Devoured", str(f)) +``` + +Again, the unbound parameters (`sugar` and `chocolate`) are added as implicit +input parameters on the function. This version is roughly equivalent to the +following code, where these two values are explicitly bound to the input +parameters, `su` and `ch`: + +```mojo +fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]): + print("Devoured", str(f)) +``` + +You can also specify parameters by keyword, or mix positional and keyword +parameters, so the following function is roughly equivalent to the previous one: +the first parameter, `sugar` is explicitly unbound with the underscore character. +The `chocolate` parameter is unbound using the keyword syntax, `chocolate=_`. +And `cream` is explicitly bound to the value 6: + +```mojo +fn devour(f: Fudge[_, chocolate=_, cream=6]): + print("Devoured", str(f)) +``` + +All three versions of the `devour()` function work with the following calls: + +```mojo +devour(Fudge[3, 6, 9]()) +devour(Fudge[4, 6, 8]()) +``` + +```output +Devoured Fudge (3,6,9) +Devoured Fudge (4,6,8) +``` + +### Legacy syntax (omitted parameters) + +You can also specify an unbound or partially-bound type by omitting parameters: +for example: + +```mojo +fn nibble(f: Fudge[5]): + print("Ate", str(f)) + +nibble(Fudge[5, 4, 7]()) + +``` + +```output +Ate Fudge (5,4,7) +``` + +Here, `Fudge[5]` works like `Fudge[5, *_]` **except** in the handling of +parameters with default values. Instead of discarding the default value of +`chocolate`, `Fudge[5]` binds the default value immediately, making it +equivalent to: `Fudge[5, _, 7]`. + +This means that the following code won't compile with the previous definition +for the `nibble()` function, since it doesn't use the default value for +`chocolate`: + +```mojo +nibble(Fudge[5, 5, 9]()) +# ERROR: invalid call to 'nibble': argument #0 cannot be converted from 'Fudge[5, 5, 9]' to 'Fudge[5, 5, 7]' +``` + +:::note TODO + +Support for omitting unbound parameters will eventually be deprecated in +favor of explicitly unbound parameters using `_` and `*_`. + +::: + +## The `rebind()` builtin + +One of the consequences of Mojo not performing function instantiation in the +parser like C++ is that Mojo cannot always figure out whether some parametric +types are equal and complain about an invalid conversion. This typically occurs +in static dispatch patterns. For example, the following code won't compile: + +```mojo +fn take_simd8(x: SIMD[DType.float32, 8]): + pass + +fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]): + @parameter + if nelts == 8: + take_simd8(x) +``` + +The parser will complain: + +```plaintext +error: invalid call to 'take_simd8': argument #0 cannot be converted from +'SIMD[f32, nelts]' to 'SIMD[f32, 8]' + take_simd8(x) + ~~~~~~~~~~^~~ +``` + +This is because the parser fully type-checks the function without instantiation, +and the type of `x` is still `SIMD[f32, nelts]`, and not `SIMD[f32, 8]`, despite +the static conditional. The remedy is to manually "rebind" the type of `x`, +using the `rebind` builtin, which inserts a compile-time assert that the input +and result types resolve to the same type after function instantiation: + +```mojo +fn take_simd8(x: SIMD[DType.float32, 8]): + pass + +fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]): + @parameter + if nelts == 8: + take_simd8(rebind[SIMD[DType.float32, 8]](x)) +``` diff --git a/docs/manual/pointers.ipynb b/docs/manual/pointers.ipynb deleted file mode 100644 index 7b2b495c0f..0000000000 --- a/docs/manual/pointers.ipynb +++ /dev/null @@ -1,751 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Unsafe pointers\n", - "description: Using unsafe pointers to access dynamically-allocated memory.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type \n", - "creates an indirect reference to a location in memory.\n", - "You can use an `UnsafePointer` to dynamically allocate and free memory, or to\n", - "point to memory allocated by some other piece of code. You can use these\n", - "pointers to write code that interacts with low-level interfaces, to interface\n", - "with other programming languages, or to build certain kinds of data structures.\n", - "But as the name suggests, they're inherently _unsafe_. For example, when using\n", - "unsafe pointers, you're responsible for ensuring that memory gets allocated and\n", - "freed correctly.\n", - "\n", - ":::note \n", - "\n", - "In addition to unsafe pointers, Mojo supports a safe \n", - "[`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type. See\n", - "[`UnsafePointer` and `Pointer`](#unsafepointer-and-pointer) for a brief\n", - "comparison of the types.\n", - "\n", - ":::\n", - "\n", - "## What is a pointer?\n", - "\n", - "An `UnsafePointer` is a type that holds an address to memory. You can store\n", - "and retrieve values in that memory. The `UnsafePointer` type is _generic_—it can \n", - "point to any type of value, and the value type is specified as a parameter. The\n", - "value pointed to by a pointer is sometimes called a _pointee_." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import UnsafePointer\n", - "\n", - "# Allocate memory to hold a value\n", - "var ptr = UnsafePointer[Int].alloc(1)\n", - "# Initialize the allocated memory\n", - "ptr.init_pointee_copy(100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
    \n", - "\n", - " ![](./images/pointer-diagram.png#light)\n", - " ![](./images/pointer-diagram-dark.png#dark)\n", - "\n", - "
    Figure 1. Pointer and pointee
    \n", - "
    \n", - "\n", - "\n", - "Accessing the memory—to retrieve or update a value—is called \n", - "_dereferencing_ the pointer. You can dereference a pointer by following the\n", - "variable name with an empty pair of square brackets:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "110\n" - ] - } - ], - "source": [ - "# Update an initialized value\n", - "ptr[] += 10\n", - "# Access an initialized value\n", - "print(ptr[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also allocate memory to hold multiple values to build array-like\n", - "structures. For details, see \n", - "[Storing multiple values](#storing-multiple-values)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Lifecycle of a pointer\n", - "\n", - "At any given time, a pointer can be in one of several states:\n", - "\n", - "- Uninitialized. Just like any variable, a variable of type `UnsafePointer` can\n", - " be declared but uninitialized.\n", - "\n", - " \n", - " ```mojo\n", - " var ptr: UnsafePointer[Int]\n", - " ```\n", - "\n", - "- Null. A null pointer has an address of 0, indicating an invalid pointer.\n", - "\n", - " ```mojo\n", - " ptr = UnsafePointer[Int]()\n", - " ```\n", - "\n", - "- Pointing to allocated, uninitialized memory. The `alloc()` static method\n", - " returns a pointer to a newly-allocated block of memory with space for the \n", - " specified number of elements of the pointee's type.\n", - "\n", - " ```mojo\n", - " ptr = UnsafePointer[Int].alloc(1)\n", - " ```\n", - " Trying to dereference a pointer to uninitialized memory results in undefined \n", - " behavior. \n", - "\n", - "- Pointing to initialized memory. You can initialize an allocated, uninitialized\n", - " pointer by moving or copying an existing value into the memory. Or you can use\n", - " the `address_of()` static method to get a pointer to an existing value. \n", - "\n", - " ```mojo\n", - " ptr.init_pointee_copy(value)\n", - " # or\n", - " ptr.init_pointee_move(value^)\n", - " # or \n", - " ptr = UnsafePointer[Int].address_of(value)\n", - " ```\n", - " \n", - " Once the value is initialized, you can read or mutate it using the dereference\n", - " syntax: \n", - "\n", - " ```mojo\n", - " oldValue = ptr[]\n", - " ptr[] = newValue\n", - " ```\n", - "\n", - "- Dangling. When you free the pointer's allocated memory, you're left with a \n", - " _dangling pointer_. The address still points to its previous location, but the\n", - " memory is no longer allocated to this pointer. Trying to dereference the\n", - " pointer, or calling any method that would access the memory location results\n", - " in undefined behavior.\n", - "\n", - " ```mojo\n", - " ptr.free()\n", - " ```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "The following diagram shows the lifecycle of an `UnsafePointer`:\n", - "\n", - "
    \n", - "\n", - " ![](./images/pointer-lifecycle.png#light)\n", - " ![](./images/pointer-lifecycle-dark.png#dark)\n", - "\n", - "
    Figure 2. Lifecycle of an UnsafePointer
    \n", - "
    \n", - "\n", - "### Allocating memory\n", - "\n", - "Use the static `alloc()` method to allocate memory. The method returns a new\n", - "pointer pointing to the requested memory. You can allocate space for one or \n", - "more values of the pointee's type.\n", - "\n", - "```mojo\n", - "ptr = UnsafePointer[Int].alloc(10) # Allocate space for 10 Int values\n", - "```\n", - "\n", - "The allocated space is _uninitialized_—like a variable that's been declared but\n", - "not initialized.\n", - "\n", - "### Initializing the pointee\n", - "\n", - "To initialize allocated memory, `UnsafePointer` provides the\n", - "[`init_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy)\n", - "and [`init_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move)\n", - "methods. For example:\n", - "\n", - "```mojo\n", - "ptr.init_pointee_copy(my_value)\n", - "```\n", - "\n", - "To move a value into the pointer's memory location, use\n", - "`init_pointee_move()`:\n", - "\n", - "```mojo\n", - "str_ptr.init_pointee_move(my_string^)\n", - "```\n", - "\n", - "Note that to move the value, you usually need to add the transfer sigil\n", - "(`^`), unless the value is a [trivial\n", - "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n", - "`Int`) or a newly-constructed, \"owned\" value:\n", - "\n", - "```mojo\n", - "str_ptr.init_pointee_move(str(\"Owned string\"))\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternately, you can get a pointer to an existing value using the static \n", - "`address_of()` method. This is useful for getting a pointer to a value on the \n", - "stack, for example." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "var counter: Int = 5\n", - "ptr = UnsafePointer[Int].address_of(counter)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that when calling `address_of()`, you don't need to allocate memory ahead\n", - "of time, since you're pointing to an existing value." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Dereferencing pointers\n", - "\n", - "Use the `[]` dereference operator to access the value stored at a pointer (the\n", - " \"pointee\").\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5\n" - ] - } - ], - "source": [ - "# Read from pointee\n", - "print(ptr[])\n", - "# mutate pointee\n", - "ptr[] = 0\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "If you've allocated space for multiple values, you can use subscript syntax to\n", - "access the values, as if they were an array, like `ptr[3]`. The empty subscript\n", - "`[]` has the same meaning as `[0]`.\n", - "\n", - ":::caution\n", - "\n", - "The dereference operator assumes that the memory being dereferenced is \n", - "initialized. Dereferencing uninitialized memory results in undefined behavior.\n", - "\n", - ":::\n", - "\n", - "You cannot safely use the dereference operator on uninitialized memory, even to\n", - "_initialize_ a pointee. This is because assigning to a dereferenced pointer\n", - "calls lifecycle methods on the existing pointee (such as the destructor, move\n", - "constructor or copy constructor).\n", - "\n", - "```mojo\n", - "str_ptr = UnsafePointer[String].alloc(1)\n", - "# str_ptr[] = \"Testing\" # Undefined behavior!\n", - "str_ptr.init_pointee_move(\"Testing\")\n", - "str_ptr[] += \" pointers\" # Works now\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Destroying or removing values\n", - "\n", - "The \n", - "[`take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee)\n", - "method moves the pointee from the memory location pointed to by `ptr`. This is\n", - "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n", - "the memory location uninitialized.\n", - "\n", - "The [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee)\n", - "method calls the destructor on the pointee, and leaves the memory location\n", - "pointed to by `ptr` uninitialized. \n", - "\n", - "Both `take_pointee()` and `destroy_pointee()` require that the pointer is \n", - "non-null, and the memory location contains a valid, initialized value of the \n", - "pointee's type; otherwise the function results in undefined behavior.\n", - "\n", - "The [`move_pointee_into(self, dst)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into)\n", - "method moves the pointee from one pointer location to another. Both pointers\n", - "must be non-null. The source location must contain a valid, initialized value of \n", - "the pointee's type, and is left uninitialized after the call. The destination \n", - "location is assumed to be uninitialized—if it contains a valid value, that\n", - "value's destructor is not run. The value from the source location is moved to\n", - "the destination location as a consuming move. This function also has undefined\n", - "behavior if any of its prerequisites is not met." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Freeing memory\n", - "\n", - "Calling [`free()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#free) on a \n", - "pointer frees the memory allocated by the pointer. It doesn't call the \n", - "destructors on any values stored in the memory—you need to do that explicitly\n", - "(for example, using\n", - "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) or\n", - "one of the other functions described in \n", - "[Destroying or removing values](#destroying-or-removing-values)).\n", - "\n", - "Disposing of a pointer without freeing the associated memory can result in a\n", - "memory leak—where your program keeps taking more and more memory, because not\n", - "all allocated memory is being freed.\n", - "\n", - "On the other hand, if you have multiple copies of a pointer accessing the same\n", - "memory, you need to make sure you only call `free()` on one of them. Freeing the\n", - "same memory twice is also an error.\n", - "\n", - "After freeing a pointer's memory, you're left with a dangling pointer—its\n", - "address still points to the freed memory. Any attempt to access the memory,\n", - "like dereferencing the pointer results in undefined behavior.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Storing multiple values\n", - "\n", - "As mentioned in [Allocating memory](#allocating-memory), you can use an \n", - "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n", - "as a single, contiguous block. Pointers support arithmetic: adding an integer\n", - "to a pointer returns a new pointer offset by the specified number of values from\n", - "the original pointer:\n", - "\n", - "```mojo\n", - "third_ptr = first_ptr + 2\n", - "```\n", - "\n", - "Pointers also support subtraction, as well as in-place addition and subtraction:\n", - "\n", - "```mojo\n", - "# Advance the pointer one element:\n", - "ptr += 1\n", - "```\n", - "\n", - "
    \n", - "\n", - " ![](./images/pointer-offset.png#light)\n", - " ![](./images/pointer-offset-dark.png#dark)\n", - "\n", - "
    Figure 3. Pointer arithmetic
    \n", - "
    \n", - "\n", - "For example, the following example allocates memory to store 6 `Float64`\n", - "values, and initializes them all to zero." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "float_ptr = UnsafePointer[Float64].alloc(6)\n", - "for offset in range(6):\n", - " (float_ptr+offset).init_pointee_copy(0.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once the values are initialized, you can access them using subscript syntax:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0, 0.0, 3.0, 0.0, 0.0, 0.0, " - ] - } - ], - "source": [ - "float_ptr[2] = 3.0\n", - "for offset in range(6):\n", - " print(float_ptr[offset], end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Working with foreign pointers\n", - "\n", - "When exchanging data with other programming languages, you may need to construct\n", - "an `UnsafePointer` from a foreign pointer. Mojo restricts creating \n", - "`UnsafePointer` instances from arbitrary addresses, to avoid users accidentally \n", - "creating pointers that _alias_ each other (that is, two pointers that refer to\n", - "the same location). However, there are specific methods you can use to get an\n", - "`UnsafePointer` from a Python or C/C++ pointer.\n", - "\n", - "When dealing with memory allocated elsewhere, you need to be aware of who's\n", - "responsible for freeing the memory. Freeing memory allocated elsewhere \n", - "can result in undefined behavior.\n", - "\n", - "You also need to be aware of the format of the data stored in memory, including\n", - "data types and byte order. For more information, see \n", - "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Creating a Mojo pointer from a Python pointer\n", - "\n", - "The `PythonObject` type defines\n", - "an [`unsafe_get_as_pointer()`](/mojo/stdlib/python/object/PythonObject#unsafe_get_as_pointer) \n", - "method to construct an `UnsafePointer` from a Python address.\n", - "\n", - "For example, the following code creates a NumPy array and then accesses the\n", - "data using a Mojo pointer:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1, 2, 3, 4, 5, 6, 7, 8, 9, " - ] - } - ], - "source": [ - "from python import Python\n", - "from memory import UnsafePointer\n", - "\n", - "def share_array():\n", - " np = Python.import_module(\"numpy\")\n", - " arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n", - " ptr = arr.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.int64]()\n", - " for i in range(9):\n", - " print(ptr[i], end=\", \")\n", - "\n", - "share_array()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "NumPy arrays implement the\n", - "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n", - "which defines the `__array_interface__` object used in the example, where \n", - "`__array_interface__[\"data\"][0]` is a Python integer holding the address of the\n", - "underlying data. The `unsafe_get_as_pointer()` method constructs an \n", - "`UnsafePointer` to this address." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Working with C/C++ pointers\n", - "\n", - "If you call a C/C++ function that returns a pointer using the\n", - "[`external_call`](/mojo/stdlib/sys/ffi/external_call) function, you can specify\n", - "the return type as an `UnsafePointer`, and Mojo will handle the type conversion\n", - "for you." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "from sys.ffi import external_call\n", - "\n", - "def get_foreign_pointer() -> UnsafePointer[Int]:\n", - " ptr = external_call[\n", - " \"my_c_function\", # external function name\n", - " UnsafePointer[Int] # return type\n", - " ]()\n", - " return ptr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Converting data: bitcasting and byte order\n", - "\n", - "Bitcasting a pointer returns a new pointer that has the same memory location,\n", - "but a new data type. This can be useful if you need to access different types of\n", - "data from a single area of memory. This can happen when you're reading binary\n", - "files, like image files, or receiving data over the network.\n", - "\n", - "The following sample processes a format that consists of chunks of data,\n", - "where each chunk contains a variable number of 32-bit integers.\n", - "Each chunk begins with an 8-bit integer that identifies the number of values\n", - "in the chunk." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "def read_chunks(owned ptr: UnsafePointer[UInt8]) -> List[List[UInt32]]:\n", - " chunks = List[List[UInt32]]()\n", - " # A chunk size of 0 indicates the end of the data\n", - " chunk_size = int(ptr[])\n", - " while (chunk_size > 0):\n", - " # Skip the 1 byte chunk_size and get a pointer to the first\n", - " # UInt32 in the chunk\n", - " ui32_ptr = (ptr + 1).bitcast[UInt32]()\n", - " chunk = List[UInt32](capacity=chunk_size)\n", - " for i in range(chunk_size):\n", - " chunk.append(ui32_ptr[i])\n", - " chunks.append(chunk)\n", - " # Move our pointer to the next byte after the current chunk\n", - " ptr += (1 + 4 * chunk_size)\n", - " # Read the size of the next chunk\n", - " chunk_size = int(ptr[])\n", - " return chunks" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When dealing with data read in from a file or from the network, you may also\n", - "need to deal with byte order. Most systems use little-endian byte order (also\n", - "called least-signficicant byte, or LSB) where the least-significant byte in a\n", - "multibyte value comes first. For example, the number 1001 can be represented in\n", - "hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a\n", - "16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit \n", - "integer, it would be represented as E9 03 00 00. \n", - "\n", - "Big-endian or most-significant byte (MSB) ordering is the opposite: in the \n", - "32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and\n", - "when transmitting data over the network. You can use the \n", - "[`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap) function to swap the byte\n", - "order of a SIMD value from big-endian to little-endian or the reverse. For\n", - "example, if the method above was reading big-endian data, you'd just need to\n", - "change a single line:\n", - "\n", - "```mojo\n", - "chunk.append(byte_swap(ui32_ptr[i]))\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Working with SIMD vectors\n", - "\n", - "The `UnsafePointer` type includes\n", - "[`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and\n", - "[`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods for\n", - "performing aligned loads and stores of scalar values. It also has methods\n", - "supporting strided load/store and gather/scatter.\n", - "\n", - "Strided load loads values from memory into a SIMD vector using an offset (the\n", - "\"stride\") between successive memory addresses. This can be useful for\n", - "extracting rows or columns from tabular data, or for extracting individual\n", - "values from structured data. For example, consider the data for an RGB image,\n", - "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n", - "you want to access just the red values, you can use a strided load or store.\n", - "\n", - "
    \n", - "\n", - " ![](./images/strided-load-storage.png#light)\n", - " ![](./images/strided-load-storage-dark.png#dark)\n", - "\n", - "
    Figure 4. Strided load
    \n", - "
    \n", - "\n", - "The following function uses the \n", - "[`strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_load)\n", - "and \n", - "[`strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_store)\n", - "methods to invert the red pixel values in an image, 8 values at a time. (Note\n", - "that this function only handles images where the number of pixels is evenly\n", - "divisible by eight.)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def invert_red_channel(ptr: UnsafePointer[UInt8], pixel_count: Int):\n", - " # number of values loaded or stored at a time\n", - " alias simd_width = 8\n", - " # bytes per pixel, which is also the stride size\n", - " bpp = 3\n", - " for i in range(0, pixel_count * bpp, simd_width * bpp):\n", - " red_values = ptr.offset(i).strided_load[width=simd_width](bpp)\n", - " # Invert values and store them in their original locations\n", - " ptr.offset(i).strided_store[width=simd_width](~red_values, bpp)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The [`gather()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#gather) and\n", - "[`scatter()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#scatter) methods\n", - "let you load or store a set of values that are stored in arbitrary locations. \n", - "You do this by passing in a SIMD vector of _offsets_ to the current pointer. For\n", - "example, when using `gather()`, the nth value in the vector is loaded\n", - "from (pointer address) + offset[n]." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Safety\n", - "\n", - "Unsafe pointers are unsafe for several reasons:\n", - "\n", - "- Memory management is up to the user. You need to manually allocate\n", - " and free memory, and be aware of when other APIs are allocating or freeing\n", - " memory for you.\n", - "\n", - "- `UnsafePointer` values are _nullable_—that is, the pointer\n", - " is not guaranteed to point to anything. And even when a pointer points to\n", - " allocated memory, that memory may not be _initialized_.\n", - "\n", - "- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`.\n", - " When you use an `UnsafePointer`, managing memory and knowing when to destroy\n", - " objects is your responsibility. \n", - "\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `UnsafePointer` and `Pointer`\n", - "\n", - "The [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type is essentially a \n", - "safe pointer type. Like a pointer, you can derference a `Pointer` using the \n", - "dereference operator, `[]`. However, the `Pointer` type has several\n", - "differences from `UnsafePointer` which make it safer:\n", - "\n", - "- A `Pointer` is _non-nullable_: it always points to something.\n", - "- You can't allocate or free memory using a `Pointer`—only point to an\n", - " existing value.\n", - "- A `Pointer` only refers to a single value. You can't do pointer arithmetic\n", - " with a `Pointer`.\n", - "- A `Pointer` has an associated _origin_, which connects it back to an\n", - " original, owned value. The origin ensures that the value won't be destroyed\n", - " while the pointer exists.\n", - "\n", - "The `Pointer` type shouldn't be confused with the immutable and mutable\n", - "references used with the `borrowed` and `inout` argument conventions. Those\n", - "references do not require explicit dereferencing, unlike a `Pointer` or \n", - "`UnsafePointer`." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/pointers.mdx b/docs/manual/pointers.mdx new file mode 100644 index 0000000000..37f0a84977 --- /dev/null +++ b/docs/manual/pointers.mdx @@ -0,0 +1,530 @@ +--- +title: Unsafe pointers +description: Using unsafe pointers to access dynamically-allocated memory. +--- + +The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type +creates an indirect reference to a location in memory. +You can use an `UnsafePointer` to dynamically allocate and free memory, or to +point to memory allocated by some other piece of code. You can use these +pointers to write code that interacts with low-level interfaces, to interface +with other programming languages, or to build certain kinds of data structures. +But as the name suggests, they're inherently *unsafe*. For example, when using +unsafe pointers, you're responsible for ensuring that memory gets allocated and +freed correctly. + +:::note + +In addition to unsafe pointers, Mojo supports a safe +[`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type. See +[`UnsafePointer` and `Pointer`](#unsafepointer-and-pointer) for a brief +comparison of the types. + +::: + +## What is a pointer? + +An `UnsafePointer` is a type that holds an address to memory. You can store +and retrieve values in that memory. The `UnsafePointer` type is *generic*—it can +point to any type of value, and the value type is specified as a parameter. The +value pointed to by a pointer is sometimes called a *pointee*. + +```mojo +from memory import UnsafePointer + +# Allocate memory to hold a value +var ptr = UnsafePointer[Int].alloc(1) +# Initialize the allocated memory +ptr.init_pointee_copy(100) +``` + +
    + +![](./images/pointer-diagram.png#light) +![](./images/pointer-diagram-dark.png#dark) + +
    Figure 1. Pointer and pointee
    +
    + +Accessing the memory—to retrieve or update a value—is called +*dereferencing* the pointer. You can dereference a pointer by following the +variable name with an empty pair of square brackets: + +```mojo +# Update an initialized value +ptr[] += 10 +# Access an initialized value +print(ptr[]) +``` + +```output +110 +``` + +You can also allocate memory to hold multiple values to build array-like +structures. For details, see +[Storing multiple values](#storing-multiple-values). + +## Lifecycle of a pointer + +At any given time, a pointer can be in one of several states: + +* Uninitialized. Just like any variable, a variable of type `UnsafePointer` can + be declared but uninitialized. + + ```mojo + var ptr: UnsafePointer[Int] + ``` + +* Null. A null pointer has an address of 0, indicating an invalid pointer. + + ```mojo + ptr = UnsafePointer[Int]() + ``` + +* Pointing to allocated, uninitialized memory. The `alloc()` static method + returns a pointer to a newly-allocated block of memory with space for the + specified number of elements of the pointee's type. + + ```mojo + ptr = UnsafePointer[Int].alloc(1) + ``` + + Trying to dereference a pointer to uninitialized memory results in undefined + behavior. + +* Pointing to initialized memory. You can initialize an allocated, uninitialized + pointer by moving or copying an existing value into the memory. Or you can use + the `address_of()` static method to get a pointer to an existing value. + + ```mojo + ptr.init_pointee_copy(value) + # or + ptr.init_pointee_move(value^) + # or + ptr = UnsafePointer[Int].address_of(value) + ``` + + Once the value is initialized, you can read or mutate it using the dereference + syntax: + + ```mojo + oldValue = ptr[] + ptr[] = newValue + ``` + +* Dangling. When you free the pointer's allocated memory, you're left with a + *dangling pointer*. The address still points to its previous location, but the + memory is no longer allocated to this pointer. Trying to dereference the + pointer, or calling any method that would access the memory location results + in undefined behavior. + + ```mojo + ptr.free() + ``` + +The following diagram shows the lifecycle of an `UnsafePointer`: + +
    + +![](./images/pointer-lifecycle.png#light) +![](./images/pointer-lifecycle-dark.png#dark) + +
    Figure 2. Lifecycle of an UnsafePointer
    +
    + +### Allocating memory + +Use the static `alloc()` method to allocate memory. The method returns a new +pointer pointing to the requested memory. You can allocate space for one or +more values of the pointee's type. + +```mojo +ptr = UnsafePointer[Int].alloc(10) # Allocate space for 10 Int values +``` + +The allocated space is *uninitialized*—like a variable that's been declared but +not initialized. + +### Initializing the pointee + +To initialize allocated memory, `UnsafePointer` provides the +[`init_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_copy) +and [`init_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#init_pointee_move) +methods. For example: + +```mojo +ptr.init_pointee_copy(my_value) +``` + +To move a value into the pointer's memory location, use +`init_pointee_move()`: + +```mojo +str_ptr.init_pointee_move(my_string^) +``` + +Note that to move the value, you usually need to add the transfer sigil +(`^`), unless the value is a [trivial +type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like +`Int`) or a newly-constructed, "owned" value: + +```mojo +str_ptr.init_pointee_move(str("Owned string")) +``` + +Alternately, you can get a pointer to an existing value using the static +`address_of()` method. This is useful for getting a pointer to a value on the +stack, for example. + +```mojo +var counter: Int = 5 +ptr = UnsafePointer[Int].address_of(counter) +``` + +Note that when calling `address_of()`, you don't need to allocate memory ahead +of time, since you're pointing to an existing value. + +### Dereferencing pointers + +Use the `[]` dereference operator to access the value stored at a pointer (the +"pointee"). + +```mojo +# Read from pointee +print(ptr[]) +# mutate pointee +ptr[] = 0 + +``` + +```output +5 +``` + +If you've allocated space for multiple values, you can use subscript syntax to +access the values, as if they were an array, like `ptr[3]`. The empty subscript +`[]` has the same meaning as `[0]`. + +:::caution + +The dereference operator assumes that the memory being dereferenced is +initialized. Dereferencing uninitialized memory results in undefined behavior. + +::: + +You cannot safely use the dereference operator on uninitialized memory, even to +*initialize* a pointee. This is because assigning to a dereferenced pointer +calls lifecycle methods on the existing pointee (such as the destructor, move +constructor or copy constructor). + +```mojo +str_ptr = UnsafePointer[String].alloc(1) +# str_ptr[] = "Testing" # Undefined behavior! +str_ptr.init_pointee_move("Testing") +str_ptr[] += " pointers" # Works now +``` + +### Destroying or removing values + +The +[`take_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#take_pointee) +method moves the pointee from the memory location pointed to by `ptr`. This is +a consuming move—it invokes `__moveinit__()` on the destination value. It leaves +the memory location uninitialized. + +The [`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) +method calls the destructor on the pointee, and leaves the memory location +pointed to by `ptr` uninitialized. + +Both `take_pointee()` and `destroy_pointee()` require that the pointer is +non-null, and the memory location contains a valid, initialized value of the +pointee's type; otherwise the function results in undefined behavior. + +The [`move_pointee_into(self, dst)`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#move_pointee_into) +method moves the pointee from one pointer location to another. Both pointers +must be non-null. The source location must contain a valid, initialized value of +the pointee's type, and is left uninitialized after the call. The destination +location is assumed to be uninitialized—if it contains a valid value, that +value's destructor is not run. The value from the source location is moved to +the destination location as a consuming move. This function also has undefined +behavior if any of its prerequisites is not met. + +### Freeing memory + +Calling [`free()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#free) on a +pointer frees the memory allocated by the pointer. It doesn't call the +destructors on any values stored in the memory—you need to do that explicitly +(for example, using +[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#destroy_pointee) or +one of the other functions described in +[Destroying or removing values](#destroying-or-removing-values)). + +Disposing of a pointer without freeing the associated memory can result in a +memory leak—where your program keeps taking more and more memory, because not +all allocated memory is being freed. + +On the other hand, if you have multiple copies of a pointer accessing the same +memory, you need to make sure you only call `free()` on one of them. Freeing the +same memory twice is also an error. + +After freeing a pointer's memory, you're left with a dangling pointer—its +address still points to the freed memory. Any attempt to access the memory, +like dereferencing the pointer results in undefined behavior. + +## Storing multiple values + +As mentioned in [Allocating memory](#allocating-memory), you can use an +`UnsafePointer` to allocate memory for multiple values. The memory is allocated +as a single, contiguous block. Pointers support arithmetic: adding an integer +to a pointer returns a new pointer offset by the specified number of values from +the original pointer: + +```mojo +third_ptr = first_ptr + 2 +``` + +Pointers also support subtraction, as well as in-place addition and subtraction: + +```mojo +# Advance the pointer one element: +ptr += 1 +``` + +
    + +![](./images/pointer-offset.png#light) +![](./images/pointer-offset-dark.png#dark) + +
    Figure 3. Pointer arithmetic
    +
    + +For example, the following example allocates memory to store 6 `Float64` +values, and initializes them all to zero. + +```mojo +float_ptr = UnsafePointer[Float64].alloc(6) +for offset in range(6): + (float_ptr+offset).init_pointee_copy(0.0) +``` + +Once the values are initialized, you can access them using subscript syntax: + +```mojo +float_ptr[2] = 3.0 +for offset in range(6): + print(float_ptr[offset], end=", ") +``` + +```output +0.0, 0.0, 3.0, 0.0, 0.0, 0.0, +``` + +## Working with foreign pointers + +When exchanging data with other programming languages, you may need to construct +an `UnsafePointer` from a foreign pointer. Mojo restricts creating +`UnsafePointer` instances from arbitrary addresses, to avoid users accidentally +creating pointers that *alias* each other (that is, two pointers that refer to +the same location). However, there are specific methods you can use to get an +`UnsafePointer` from a Python or C/C++ pointer. + +When dealing with memory allocated elsewhere, you need to be aware of who's +responsible for freeing the memory. Freeing memory allocated elsewhere +can result in undefined behavior. + +You also need to be aware of the format of the data stored in memory, including +data types and byte order. For more information, see +[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order). + +### Creating a Mojo pointer from a Python pointer + +The `PythonObject` type defines +an [`unsafe_get_as_pointer()`](/mojo/stdlib/python/object/PythonObject#unsafe_get_as_pointer) +method to construct an `UnsafePointer` from a Python address. + +For example, the following code creates a NumPy array and then accesses the +data using a Mojo pointer: + +```mojo +from python import Python +from memory import UnsafePointer + +def share_array(): + np = Python.import_module("numpy") + arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + ptr = arr.__array_interface__["data"][0].unsafe_get_as_pointer[DType.int64]() + for i in range(9): + print(ptr[i], end=", ") + +share_array() +``` + +```output +1, 2, 3, 4, 5, 6, 7, 8, 9, +``` + +NumPy arrays implement the +[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html), +which defines the `__array_interface__` object used in the example, where +`__array_interface__["data"][0]` is a Python integer holding the address of the +underlying data. The `unsafe_get_as_pointer()` method constructs an +`UnsafePointer` to this address. + +### Working with C/C++ pointers + +If you call a C/C++ function that returns a pointer using the +[`external_call`](/mojo/stdlib/sys/ffi/external_call) function, you can specify +the return type as an `UnsafePointer`, and Mojo will handle the type conversion +for you. + +```mojo +from sys.ffi import external_call + +def get_foreign_pointer() -> UnsafePointer[Int]: + ptr = external_call[ + "my_c_function", # external function name + UnsafePointer[Int] # return type + ]() + return ptr +``` + +## Converting data: bitcasting and byte order + +Bitcasting a pointer returns a new pointer that has the same memory location, +but a new data type. This can be useful if you need to access different types of +data from a single area of memory. This can happen when you're reading binary +files, like image files, or receiving data over the network. + +The following sample processes a format that consists of chunks of data, +where each chunk contains a variable number of 32-bit integers. +Each chunk begins with an 8-bit integer that identifies the number of values +in the chunk. + +```mojo + +def read_chunks(owned ptr: UnsafePointer[UInt8]) -> List[List[UInt32]]: + chunks = List[List[UInt32]]() + # A chunk size of 0 indicates the end of the data + chunk_size = int(ptr[]) + while (chunk_size > 0): + # Skip the 1 byte chunk_size and get a pointer to the first + # UInt32 in the chunk + ui32_ptr = (ptr + 1).bitcast[UInt32]() + chunk = List[UInt32](capacity=chunk_size) + for i in range(chunk_size): + chunk.append(ui32_ptr[i]) + chunks.append(chunk) + # Move our pointer to the next byte after the current chunk + ptr += (1 + 4 * chunk_size) + # Read the size of the next chunk + chunk_size = int(ptr[]) + return chunks +``` + +When dealing with data read in from a file or from the network, you may also +need to deal with byte order. Most systems use little-endian byte order (also +called least-signficicant byte, or LSB) where the least-significant byte in a +multibyte value comes first. For example, the number 1001 can be represented in +hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a +16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit +integer, it would be represented as E9 03 00 00. + +Big-endian or most-significant byte (MSB) ordering is the opposite: in the +32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and +when transmitting data over the network. You can use the +[`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap) function to swap the byte +order of a SIMD value from big-endian to little-endian or the reverse. For +example, if the method above was reading big-endian data, you'd just need to +change a single line: + +```mojo +chunk.append(byte_swap(ui32_ptr[i])) +``` + +## Working with SIMD vectors + +The `UnsafePointer` type includes +[`load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#load) and +[`store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#store) methods for +performing aligned loads and stores of scalar values. It also has methods +supporting strided load/store and gather/scatter. + +Strided load loads values from memory into a SIMD vector using an offset (the +"stride") between successive memory addresses. This can be useful for +extracting rows or columns from tabular data, or for extracting individual +values from structured data. For example, consider the data for an RGB image, +where each pixel is made up of three 8-bit values, for red, green, and blue. If +you want to access just the red values, you can use a strided load or store. + +
    + +![](./images/strided-load-storage.png#light) +![](./images/strided-load-storage-dark.png#dark) + +
    Figure 4. Strided load
    +
    + +The following function uses the +[`strided_load()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_load) +and +[`strided_store()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#strided_store) +methods to invert the red pixel values in an image, 8 values at a time. (Note +that this function only handles images where the number of pixels is evenly +divisible by eight.) + +```mojo +def invert_red_channel(ptr: UnsafePointer[UInt8], pixel_count: Int): + # number of values loaded or stored at a time + alias simd_width = 8 + # bytes per pixel, which is also the stride size + bpp = 3 + for i in range(0, pixel_count * bpp, simd_width * bpp): + red_values = ptr.offset(i).strided_load[width=simd_width](bpp) + # Invert values and store them in their original locations + ptr.offset(i).strided_store[width=simd_width](~red_values, bpp) +``` + +The [`gather()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#gather) and +[`scatter()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#scatter) methods +let you load or store a set of values that are stored in arbitrary locations. +You do this by passing in a SIMD vector of *offsets* to the current pointer. For +example, when using `gather()`, the nth value in the vector is loaded +from (pointer address) + offset[n]. + +## Safety + +Unsafe pointers are unsafe for several reasons: + +* Memory management is up to the user. You need to manually allocate + and free memory, and be aware of when other APIs are allocating or freeing + memory for you. + +* `UnsafePointer` values are *nullable*—that is, the pointer + is not guaranteed to point to anything. And even when a pointer points to + allocated memory, that memory may not be *initialized*. + +* Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`. + When you use an `UnsafePointer`, managing memory and knowing when to destroy + objects is your responsibility. + +## `UnsafePointer` and `Pointer` + +The [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type is essentially a +safe pointer type. Like a pointer, you can derference a `Pointer` using the +dereference operator, `[]`. However, the `Pointer` type has several +differences from `UnsafePointer` which make it safer: + +* A `Pointer` is *non-nullable*: it always points to something. +* You can't allocate or free memory using a `Pointer`—only point to an + existing value. +* A `Pointer` only refers to a single value. You can't do pointer arithmetic + with a `Pointer`. +* A `Pointer` has an associated *origin*, which connects it back to an + original, owned value. The origin ensures that the value won't be destroyed + while the pointer exists. + +The `Pointer` type shouldn't be confused with the immutable and mutable +references used with the `borrowed` and `inout` argument conventions. Those +references do not require explicit dereferencing, unlike a `Pointer` or +`UnsafePointer`. diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb deleted file mode 100644 index 3058ef0d98..0000000000 --- a/docs/manual/python/index.ipynb +++ /dev/null @@ -1,297 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Python integration\n", - "sidebar_position: 1\n", - "description: Using Python and Mojo together.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo is still in early development and many Python features are not yet\n", - "implemented. You can't currently write everything in Mojo that you can write in\n", - "Python. And Mojo doesn't have its own ecosystem of packages yet.\n", - "\n", - "To help bridge this gap, Mojo lets you import Python modules, call Python \n", - "functions, and interact with Python objects from Mojo code. The Python code\n", - "runs in a standard Python interpreter (CPython), so your existing Python code\n", - "doesn't need to change.\n", - "\n", - "## Create a Python environment\n", - "\n", - "To successfully integrate Python code with your Mojo project, your environment\n", - "must have a compatible Python runtime installed along with any additional\n", - "Python packages that you want to use. Currently, you can create a compatible\n", - "environment in a couple of ways:\n", - "\n", - "- We recommend that you use [Magic](/magic), our package manager and\n", - " virtual environment manager for MAX and Mojo projects. To use Magic to create\n", - " and manage the virtual environment for your Mojo/Python project, first\n", - " follow the instructions in [Install Magic](/magic/#install-magic).\n", - " Then you can create a new Mojo project like this:\n", - "\n", - " ```sh\n", - " magic init my-mojo-project --format mojoproject\n", - " ```\n", - "\n", - " After creating the project, you can enter the project and install any\n", - " dependencies, for example [NumPy](https://numpy.org/):\n", - "\n", - " ```sh\n", - " cd my-mojo-project\n", - " ```\n", - "\n", - " ```sh\n", - " magic add \"numpy>=2.0\"\n", - " ```\n", - "\n", - "- Alternatively, you can also add MAX and Mojo to a\n", - " [conda](https://docs.conda.io/projects/conda/en/latest/index.html) project.\n", - " To do so, follow the steps in [Add MAX/Mojo to a conda project](/magic/conda).\n", - "\n", - "- It's also possible to convert an existing conda project to Magic as documented\n", - " in [Migrate a conda project to Magic](/magic/#migrate-a-conda-project-to-magic).\n", - "\n", - "## Import a Python module\n", - "\n", - "To import a Python module in Mojo, just call \n", - "[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) \n", - "with the module name. The following shows an example of importing the standard\n", - "Python [NumPy](https://numpy.org/) package:\n", - "\n", - "```mojo\n", - "from python import Python\n", - "\n", - "def main():\n", - " # This is equivalent to Python's `import numpy as np`\n", - " np = Python.import_module(\"numpy\")\n", - "\n", - " # Now use numpy as if writing in Python\n", - " array = np.array([1, 2, 3])\n", - " print(array)\n", - "```\n", - "\n", - "Running this program produces the following output:\n", - "\n", - "```\n", - "[1 2 3]\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Assuming that you have the NumPy package installed in your\n", - "[environment](#create-a-python-environment), this imports NumPy and you can use any\n", - "of its features.\n", - "\n", - "A few things to note:\n", - "\n", - "- The `import_module()` method returns a reference to the module in the form of\n", - " a [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject)\n", - " wrapper. You must store the reference in a variable and then use it as shown\n", - " in the example above to access functions, classes, and other objects defined\n", - " by the module. See [Mojo wrapper objects](/mojo/manual/python/types#mojo-wrapper-objects)\n", - " for more information about the `PythonObject` type.\n", - "\n", - "- Currently, you cannot import individual members (such as a single Python class\n", - " or function). You must import the whole Python module and then access members\n", - " through the module name.\n", - "\n", - "- Mojo doesn't yet support top-level code, so the `import_module()` call must\n", - " be inside another method. This means you may need to import a module multiple\n", - " times or pass around a reference to the module. This works the same way as \n", - " Python: importing the module multiple times won't run the initialization\n", - " logic more than once, so you don't pay any performance penalty.\n", - "\n", - "- `import_module()` may raise an exception (for example, if the module isn't\n", - " installed). If you're using it inside an `fn` function, you need to either\n", - " handle errors (using a `try/except` clause), or add the `raises` keyword to\n", - " the function signature. You'll also see this when calling Python functions\n", - " that may raise exceptions. (Raising exceptions is much more common in Python\n", - " code than in the Mojo standard library, which \n", - " [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n", - "\n", - "\n", - ":::caution\n", - "\n", - "[`mojo build`](/mojo/cli/build) doesn't include the Python packages used by\n", - "your Mojo project. Instead, Mojo loads the Python interpreter and Python\n", - "packages at runtime, so they must be provided in the environment where you run\n", - "the Mojo program (such as inside the Magic environment where you built the\n", - "executable). For more information, see the section above to [create a Python\n", - "environment](#create-a-python-environment).\n", - "\n", - ":::" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Import a local Python module\n", - "\n", - "If you have some local Python code you want to use in Mojo, just add\n", - "the directory to the Python path and then import the module.\n", - "\n", - "For example, suppose you have a Python file named `mypython.py`:\n", - "\n", - "```python title=\"mypython.py\"\n", - "import numpy as np\n", - "\n", - "def gen_random_values(size, base):\n", - " # generate a size x size array of random numbers between base and base+1\n", - " random_array = np.random.rand(size, size)\n", - " return random_array + base\n", - "```\n", - "\n", - "Here's how you can import it and use it in a Mojo file:\n", - "\n", - "```mojo title=\"main.mojo\"\n", - "from python import Python\n", - "\n", - "def main():\n", - " Python.add_to_path(\"path/to/module\")\n", - " mypython = Python.import_module(\"mypython\")\n", - "\n", - " values = mypython.gen_random_values(2, 3)\n", - " print(values)\n", - "```\n", - "\n", - "Both absolute and relative paths work with \n", - "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example,\n", - "you can import from the local directory like this:\n", - "\n", - "```mojo\n", - "Python.add_to_path(\".\")\n", - "```\n", - "\n", - "## Call Mojo from Python\n", - "\n", - "As shown above, you can call out to Python modules from Mojo. However, there's \n", - "currently no way to do the reverse—import Mojo modules from Python or call Mojo\n", - "functions from Python.\n", - "\n", - "This may present a challenge for using certain modules. For example, many UI \n", - "frameworks have a main event loop that makes callbacks to user-defined code\n", - "in response to UI events. This is sometimes called an \"inversion of control\" \n", - "pattern. Instead of your application code calling *in* to a library, the \n", - "framework code calls *out* to your application code.\n", - "\n", - "This pattern doesn't work because you can't pass Mojo callbacks to a Python \n", - "module.\n", - "\n", - "For example, consider the popular [Tkinter package](https://docs.python.org/3/library/tkinter.html). \n", - "The typical usage for Tkinter is something like this:\n", - "\n", - "- You create a main, or \"root\" window for the application.\n", - "- You add one or more UI widgets to the window. The widgets can have associated\n", - " callback functions (for example, when a button is pushed).\n", - "- You call the root window's `mainloop()` method, which listens for events, \n", - " updates the UI, and invokes callback functions. The main loop keeps running\n", - " until the application exits.\n", - "\n", - "Since Python can't call back into Mojo, one alternative is to have the Mojo\n", - "application drive the event loop and poll for updates. The following example\n", - "uses Tkinter, but the basic approach can be applied to other packages.\n", - "\n", - "First you create a Python module that defines a Tkinter interface, with a window\n", - "and single button:\n", - "\n", - "```python title=\"myapp.py\"\n", - "import tkinter as tk\n", - "\n", - "class App:\n", - " def __init__(self):\n", - " self._root = tk.Tk()\n", - " self.clicked = False\n", - "\n", - " def click(self):\n", - " self.clicked = True\n", - "\n", - " def create_button(self, button_text: str):\n", - " button = tk.Button(\n", - " master=self._root,\n", - " text=button_text,\n", - " command=self.click\n", - " )\n", - " button.place(relx=0.5, rely=0.5, anchor=tk.CENTER)\n", - "\n", - " def create(self, res: str):\n", - " self._root.geometry(res)\n", - " self.create_button(\"Hello Mojo!\")\n", - "\n", - " def update(self):\n", - " self._root.update()\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can call this module from Mojo like this:\n", - "\n", - "```mojo title=\"main.mojo\"\n", - "from python import Python\n", - "\n", - "def button_clicked():\n", - " print(\"Hi from a Mojo🔥 fn!\")\n", - "\n", - "def main():\n", - " Python.add_to_path(\".\")\n", - " app = Python.import_module(\"myapp\").App()\n", - " app.create(\"800x600\")\n", - "\n", - " while True:\n", - " app.update()\n", - " if app.clicked:\n", - " button_clicked()\n", - " app.clicked = False\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of the Python module calling the Tkinter `mainloop()` method, the Mojo \n", - "code calls the `update()` method in a loop and checks the `clicked` attribute \n", - "after each update.\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/python/index.mdx b/docs/manual/python/index.mdx new file mode 100644 index 0000000000..43987ffee3 --- /dev/null +++ b/docs/manual/python/index.mdx @@ -0,0 +1,236 @@ +--- +title: Python integration +sidebar_position: 1 +description: Using Python and Mojo together. +--- + +Mojo is still in early development and many Python features are not yet +implemented. You can't currently write everything in Mojo that you can write in +Python. And Mojo doesn't have its own ecosystem of packages yet. + +To help bridge this gap, Mojo lets you import Python modules, call Python +functions, and interact with Python objects from Mojo code. The Python code +runs in a standard Python interpreter (CPython), so your existing Python code +doesn't need to change. + +## Create a Python environment + +To successfully integrate Python code with your Mojo project, your environment +must have a compatible Python runtime installed along with any additional +Python packages that you want to use. Currently, you can create a compatible +environment in a couple of ways: + +* We recommend that you use [Magic](/magic), our package manager and + virtual environment manager for MAX and Mojo projects. To use Magic to create + and manage the virtual environment for your Mojo/Python project, first + follow the instructions in [Install Magic](/magic/#install-magic). + Then you can create a new Mojo project like this: + + ```sh + magic init my-mojo-project --format mojoproject + ``` + + After creating the project, you can enter the project and install any + dependencies, for example [NumPy](https://numpy.org/): + + ```sh + cd my-mojo-project + ``` + + ```sh + magic add "numpy>=2.0" + ``` + +* Alternatively, you can also add MAX and Mojo to a + [conda](https://docs.conda.io/projects/conda/en/latest/index.html) project. + To do so, follow the steps in [Add MAX/Mojo to a conda project](/magic/conda). + +* It's also possible to convert an existing conda project to Magic as documented + in [Migrate a conda project to Magic](/magic/#migrate-a-conda-project-to-magic). + +## Import a Python module + +To import a Python module in Mojo, just call +[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) +with the module name. The following shows an example of importing the standard +Python [NumPy](https://numpy.org/) package: + +```mojo +from python import Python + +def main(): + # This is equivalent to Python's `import numpy as np` + np = Python.import_module("numpy") + + # Now use numpy as if writing in Python + array = np.array([1, 2, 3]) + print(array) +``` + +Running this program produces the following output: + +``` +[1 2 3] +``` + +Assuming that you have the NumPy package installed in your +[environment](#create-a-python-environment), this imports NumPy and you can use any +of its features. + +A few things to note: + +* The `import_module()` method returns a reference to the module in the form of + a [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) + wrapper. You must store the reference in a variable and then use it as shown + in the example above to access functions, classes, and other objects defined + by the module. See [Mojo wrapper objects](/mojo/manual/python/types#mojo-wrapper-objects) + for more information about the `PythonObject` type. + +* Currently, you cannot import individual members (such as a single Python class + or function). You must import the whole Python module and then access members + through the module name. + +* Mojo doesn't yet support top-level code, so the `import_module()` call must + be inside another method. This means you may need to import a module multiple + times or pass around a reference to the module. This works the same way as + Python: importing the module multiple times won't run the initialization + logic more than once, so you don't pay any performance penalty. + +* `import_module()` may raise an exception (for example, if the module isn't + installed). If you're using it inside an `fn` function, you need to either + handle errors (using a `try/except` clause), or add the `raises` keyword to + the function signature. You'll also see this when calling Python functions + that may raise exceptions. (Raising exceptions is much more common in Python + code than in the Mojo standard library, which + [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).) + +:::caution + +[`mojo build`](/mojo/cli/build) doesn't include the Python packages used by +your Mojo project. Instead, Mojo loads the Python interpreter and Python +packages at runtime, so they must be provided in the environment where you run +the Mojo program (such as inside the Magic environment where you built the +executable). For more information, see the section above to [create a Python +environment](#create-a-python-environment). + +::: + +### Import a local Python module + +If you have some local Python code you want to use in Mojo, just add +the directory to the Python path and then import the module. + +For example, suppose you have a Python file named `mypython.py`: + +```python title="mypython.py" +import numpy as np + +def gen_random_values(size, base): + # generate a size x size array of random numbers between base and base+1 + random_array = np.random.rand(size, size) + return random_array + base +``` + +Here's how you can import it and use it in a Mojo file: + +```mojo title="main.mojo" +from python import Python + +def main(): + Python.add_to_path("path/to/module") + mypython = Python.import_module("mypython") + + values = mypython.gen_random_values(2, 3) + print(values) +``` + +Both absolute and relative paths work with +[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example, +you can import from the local directory like this: + +```mojo +Python.add_to_path(".") +``` + +## Call Mojo from Python + +As shown above, you can call out to Python modules from Mojo. However, there's +currently no way to do the reverse—import Mojo modules from Python or call Mojo +functions from Python. + +This may present a challenge for using certain modules. For example, many UI +frameworks have a main event loop that makes callbacks to user-defined code +in response to UI events. This is sometimes called an "inversion of control" +pattern. Instead of your application code calling *in* to a library, the +framework code calls *out* to your application code. + +This pattern doesn't work because you can't pass Mojo callbacks to a Python +module. + +For example, consider the popular [Tkinter package](https://docs.python.org/3/library/tkinter.html). +The typical usage for Tkinter is something like this: + +* You create a main, or "root" window for the application. +* You add one or more UI widgets to the window. The widgets can have associated + callback functions (for example, when a button is pushed). +* You call the root window's `mainloop()` method, which listens for events, + updates the UI, and invokes callback functions. The main loop keeps running + until the application exits. + +Since Python can't call back into Mojo, one alternative is to have the Mojo +application drive the event loop and poll for updates. The following example +uses Tkinter, but the basic approach can be applied to other packages. + +First you create a Python module that defines a Tkinter interface, with a window +and single button: + +```python title="myapp.py" +import tkinter as tk + +class App: + def __init__(self): + self._root = tk.Tk() + self.clicked = False + + def click(self): + self.clicked = True + + def create_button(self, button_text: str): + button = tk.Button( + master=self._root, + text=button_text, + command=self.click + ) + button.place(relx=0.5, rely=0.5, anchor=tk.CENTER) + + def create(self, res: str): + self._root.geometry(res) + self.create_button("Hello Mojo!") + + def update(self): + self._root.update() +``` + +You can call this module from Mojo like this: + +```mojo title="main.mojo" +from python import Python + +def button_clicked(): + print("Hi from a Mojo🔥 fn!") + +def main(): + Python.add_to_path(".") + app = Python.import_module("myapp").App() + app.create("800x600") + + while True: + app.update() + if app.clicked: + button_clicked() + app.clicked = False +``` + +Instead of the Python module calling the Tkinter `mainloop()` method, the Mojo +code calls the `update()` method in a loop and checks the `clicked` attribute +after each update. diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb deleted file mode 100644 index c7c4c4e558..0000000000 --- a/docs/manual/python/types.ipynb +++ /dev/null @@ -1,292 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Python types\n", - "sidebar_position: 2\n", - "description: Using Mojo types in Python, and Python types in Mojo.\n", - "---\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When calling Python methods, Mojo needs to convert back and forth between native\n", - "Python objects and native Mojo objects. Most of these conversions happen\n", - "automatically, but there are a number of cases that Mojo doesn't handle yet.\n", - "In these cases you may need to do an explicit conversion, or call an extra\n", - "method.\n", - "\n", - "## Mojo types in Python\n", - "\n", - "Mojo primitive types implicitly convert into Python objects.\n", - "Today we support lists, tuples, integers, floats, booleans, and strings.\n", - "\n", - "For example, given this Python function that prints Python types:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "%%python\n", - "def type_printer(value):\n", - " print(type(value))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(You can ignore the `%%python` at the start of the code sample; it's explained\n", - "in the note below.)\n", - "\n", - "You can pass this Python function Mojo types with no problem:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\n" - ] - } - ], - "source": [ - "type_printer(4)\n", - "type_printer(3.14)\n", - "type_printer((\"Mojo\", True))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "This is a simplified code example written as a set of Jupyter\n", - "notebook cells. The first cell includes the `%%python` directive so it's\n", - "interpreted as Python. The second cell includes top-level Mojo code. You'd need\n", - "to adjust this code to run it elsewhere.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Python types in Mojo\n", - "\n", - "You can also use Python objects from Mojo. For example, Mojo's \n", - "[`Dict`](/mojo/stdlib/collections/dict/Dict) and \n", - "[`List`](/mojo/stdlib/collections/list/List) types don't natively support\n", - "heterogeneous collections. One alternative is to use a Python dictionary or\n", - "list.\n", - "\n", - "For example, to create a Python dictionary, use the \n", - "[`dict()`](/mojo/stdlib/python/python/Python#dict) method:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "from python import Python\n", - "\n", - "def use_dict():\n", - " var dictionary = Python.dict()\n", - " dictionary[\"item_name\"] = \"whizbang\"\n", - " dictionary[\"price\"] = 11.75\n", - " dictionary[\"inventory\"] = 100\n", - " print(dictionary)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mojo wrapper objects\n", - "\n", - "When you use Python objects in your Mojo code, Mojo adds the \n", - "[`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) wrapper around\n", - "the Python object. This object exposes a number of common double underscore\n", - "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n", - "through to the underlying Python object. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can explicitly create a wrapped Python object by initializing a \n", - "`PythonObject` with a Mojo literal:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "from python import PythonObject\n", - "\n", - "var py_list: PythonObject = [1, 2, 3, 4]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Most of the time, you can treat the wrapped object just like you'd treat it in \n", - "Python. You can use Python's `[]` operators to access an item in a list, and use\n", - "dot-notation to access attributes and call methods. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "var n = py_list[2]\n", - "py_list.append(5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "If you want to construct a Python type that doesn't have a literal Mojo \n", - "equivalent, you can also use the \n", - "[`Python.evaluate()`](/mojo/stdlib/python/python/Python#evaluate) method. For\n", - "example, to create a Python `set`:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "def use_py_set():\n", - " var py_set = Python.evaluate('set([2, 3, 5, 7, 11])')\n", - " var num_items = len(py_set)\n", - " print(num_items, \" items in set.\") # prints \"5 items in set\"\n", - " print(py_set.__contains__(6)) # prints \"False\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "TODO: You should be able to use the expression `6 in py_set`. However, because\n", - "of the way `PythonObject` currently works, you need to call the \n", - "`__contains__()` method directly.\n", - "\n", - "Some Mojo APIs handle `PythonObject` just fine, but sometimes you'll need to \n", - "explicitly convert a Python value into a native Mojo value. \n", - "\n", - "Currently `PythonObject` conforms to the \n", - "[`Intable`](/mojo/stdlib/builtin/int/Intable), \n", - "[`Stringable`](/mojo/stdlib/builtin/str/Stringable), and \n", - "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) traits, which \n", - "means you can convert Python values to Mojo `Int`, `String`, and `Bool` types\n", - "using the built-in \n", - "[`int()`](/mojo/stdlib/builtin/int/int-function),\n", - "[`str()`](/mojo/stdlib/builtin/str/str),\n", - "and [`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python \n", - "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", - " \n", - "`PythonObject` also provides the\n", - "[`to_float64()`](/mojo/stdlib/python/python_object/PythonObject#to_float64) for \n", - "converting to a Mojo floating point value.\n", - "\n", - "```mojo\n", - "var i: Int = int(py_int)\n", - "var s: String = str(py_string)\n", - "var b: Bool = bool(py_bool)\n", - "var f: Float64 = py_float.to_float64()\n", - "```\n", - "\n", - "### Comparing Python types in Mojo\n", - "\n", - "In conditionals, Python objects act like you'd expect them to: Python values \n", - "like `False` and `None` evaluate as false in Mojo, too.\n", - "\n", - "If you need to know the type of the underlying Python object, you can use the \n", - "[`Python.type()`](/mojo/stdlib/python/python/Python#type) method, which is \n", - "equivalent to the Python `type()` builtin. You can compare the identity of two\n", - "Python objects using the\n", - "[`Python.is_type()`](/mojo/stdlib/python/python/Python#is_type) method (which is\n", - "equivalent to the Python `is` operator):" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "def python_types():\n", - " from python import Python\n", - " from python import PythonObject\n", - "\n", - " var value1: PythonObject = 3.7\n", - " var value2 = Python.evaluate(\"10/3\")\n", - " var float_type = Python.evaluate(\"float\")\n", - "\n", - " print(Python.type(value1)) # \n", - " print(Python.is_type(Python.type(value1), Python.type(value2))) # True\n", - " print(Python.is_type(Python.type(value1), float_type)) # True\n", - " print(Python.is_type(Python.type(value1), Python.none())) # False\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One TODO item here: The `Python.is_type()` method is misleadingly named, since \n", - "it doesn't compare _types_, but object identity.\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/python/types.mdx b/docs/manual/python/types.mdx new file mode 100644 index 0000000000..a7d25d0f9a --- /dev/null +++ b/docs/manual/python/types.mdx @@ -0,0 +1,172 @@ +--- +title: Python types +sidebar_position: 2 +description: Using Mojo types in Python, and Python types in Mojo. +--- + +When calling Python methods, Mojo needs to convert back and forth between native +Python objects and native Mojo objects. Most of these conversions happen +automatically, but there are a number of cases that Mojo doesn't handle yet. +In these cases you may need to do an explicit conversion, or call an extra +method. + +## Mojo types in Python + +Mojo primitive types implicitly convert into Python objects. +Today we support lists, tuples, integers, floats, booleans, and strings. + +For example, given this Python function that prints Python types: + +```mojo +%%python +def type_printer(value): + print(type(value)) +``` + +(You can ignore the `%%python` at the start of the code sample; it's explained +in the note below.) + +You can pass this Python function Mojo types with no problem: + +```mojo +type_printer(4) +type_printer(3.14) +type_printer(("Mojo", True)) +``` + +```output + + + +``` + +:::note + +This is a simplified code example written as a set of Jupyter +notebook cells. The first cell includes the `%%python` directive so it's +interpreted as Python. The second cell includes top-level Mojo code. You'd need +to adjust this code to run it elsewhere. + +::: + +## Python types in Mojo + +You can also use Python objects from Mojo. For example, Mojo's +[`Dict`](/mojo/stdlib/collections/dict/Dict) and +[`List`](/mojo/stdlib/collections/list/List) types don't natively support +heterogeneous collections. One alternative is to use a Python dictionary or +list. + +For example, to create a Python dictionary, use the +[`dict()`](/mojo/stdlib/python/python/Python#dict) method: + +```mojo +from python import Python + +def use_dict(): + var dictionary = Python.dict() + dictionary["item_name"] = "whizbang" + dictionary["price"] = 11.75 + dictionary["inventory"] = 100 + print(dictionary) + +``` + +### Mojo wrapper objects + +When you use Python objects in your Mojo code, Mojo adds the +[`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) wrapper around +the Python object. This object exposes a number of common double underscore +methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them +through to the underlying Python object. + +You can explicitly create a wrapped Python object by initializing a +`PythonObject` with a Mojo literal: + +```mojo +from python import PythonObject + +var py_list: PythonObject = [1, 2, 3, 4] +``` + +Most of the time, you can treat the wrapped object just like you'd treat it in +Python. You can use Python's `[]` operators to access an item in a list, and use +dot-notation to access attributes and call methods. For example: + +```mojo +var n = py_list[2] +py_list.append(5) +``` + +If you want to construct a Python type that doesn't have a literal Mojo +equivalent, you can also use the +[`Python.evaluate()`](/mojo/stdlib/python/python/Python#evaluate) method. For +example, to create a Python `set`: + +```mojo +def use_py_set(): + var py_set = Python.evaluate('set([2, 3, 5, 7, 11])') + var num_items = len(py_set) + print(num_items, " items in set.") # prints "5 items in set" + print(py_set.__contains__(6)) # prints "False" +``` + +TODO: You should be able to use the expression `6 in py_set`. However, because +of the way `PythonObject` currently works, you need to call the +`__contains__()` method directly. + +Some Mojo APIs handle `PythonObject` just fine, but sometimes you'll need to +explicitly convert a Python value into a native Mojo value. + +Currently `PythonObject` conforms to the +[`Intable`](/mojo/stdlib/builtin/int/Intable), +[`Stringable`](/mojo/stdlib/builtin/str/Stringable), and +[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) traits, which +means you can convert Python values to Mojo `Int`, `String`, and `Bool` types +using the built-in +[`int()`](/mojo/stdlib/builtin/int/int-function), +[`str()`](/mojo/stdlib/builtin/str/str), +and [`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python +values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function. + +`PythonObject` also provides the +[`to_float64()`](/mojo/stdlib/python/python_object/PythonObject#to_float64) for +converting to a Mojo floating point value. + +```mojo +var i: Int = int(py_int) +var s: String = str(py_string) +var b: Bool = bool(py_bool) +var f: Float64 = py_float.to_float64() +``` + +### Comparing Python types in Mojo + +In conditionals, Python objects act like you'd expect them to: Python values +like `False` and `None` evaluate as false in Mojo, too. + +If you need to know the type of the underlying Python object, you can use the +[`Python.type()`](/mojo/stdlib/python/python/Python#type) method, which is +equivalent to the Python `type()` builtin. You can compare the identity of two +Python objects using the +[`Python.is_type()`](/mojo/stdlib/python/python/Python#is_type) method (which is +equivalent to the Python `is` operator): + +```mojo +def python_types(): + from python import Python + from python import PythonObject + + var value1: PythonObject = 3.7 + var value2 = Python.evaluate("10/3") + var float_type = Python.evaluate("float") + + print(Python.type(value1)) # + print(Python.is_type(Python.type(value1), Python.type(value2))) # True + print(Python.is_type(Python.type(value1), float_type)) # True + print(Python.is_type(Python.type(value1), Python.none())) # False + +``` + +One TODO item here: The `Python.is_type()` method is misleadingly named, since +it doesn't compare *types*, but object identity. diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb deleted file mode 100644 index aff69827b1..0000000000 --- a/docs/manual/structs.ipynb +++ /dev/null @@ -1,485 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Structs\n", - "sidebar_position: 4\n", - "description: Introduction to Mojo structures (structs).\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A Mojo struct is a data structure that allows you to encapsulate fields and\n", - "methods that operate on an abstraction, such as a data type or an object.\n", - "**Fields** are variables that hold data relevant to the struct, and **methods**\n", - "are functions inside a struct that generally act upon the field data.\n", - "\n", - "For example, if you're building a graphics program, you can use a struct to\n", - "define an `Image` that has fields to store information about each image\n", - "(such as the pixels) and methods that perform actions on it (such as rotate\n", - "it).\n", - "\n", - "For the most part, Mojo's struct format is designed to provide a static,\n", - "memory-safe data structure for high-level data types used in programs. For\n", - "example, all the data types in Mojo's standard library (such as `Int`,\n", - "`Bool`, `String`, and `Tuple`) are defined as structs.\n", - "\n", - "If you understand how [functions](/mojo/manual/functions) and\n", - "[variables](/mojo/manual/variables) work in Mojo, you probably\n", - "noticed that Mojo is designed to provide dynamic programming features in a\n", - "`def` function while enforcing stronger code safety in `fn` functions. When it\n", - "comes to structs, Mojo leans toward the safe side: You can still choose whether\n", - "to use either `def` or `fn` declarations for methods, but all fields must be\n", - "declared with `var`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Struct definition\n", - "\n", - "You can define a simple struct called `MyPair` with two fields like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPair:\n", - " var first: Int\n", - " var second: Int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, you can't instantiate this struct because it has no constructor\n", - "method. So here it is with a constructor to initialize the two fields:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPair:\n", - " var first: Int\n", - " var second: Int\n", - "\n", - " fn __init__(out self, first: Int, second: Int):\n", - " self.first = first\n", - " self.second = second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the first argument in the `__init__()` method is `inout self`. For\n", - "now, ignore `inout` (it's an [argument\n", - "convention](/mojo/manual/values/ownership#argument-conventions) that\n", - "declares `self` as a mutable reference); all you need to know right now is that\n", - "`self` must be the first argument. It references the current struct instance\n", - "(it allows code in the method to refer to \"itself\"). *When you call the\n", - "constructor, you never pass a value for `self`—Mojo passes it in \n", - "automatically.*\n", - "\n", - "The `__init__()` method is one of many [special methods](#special-methods)\n", - "(also known as \"dunder methods\" because they have *d*ouble *under*scores) with\n", - "pre-determined names.\n", - "\n", - ":::note\n", - "\n", - "You can't assign values when you declare fields. You must initialize\n", - "all of the struct's fields in the constructor. (If you try to leave a field\n", - "uninitialized, the code won't compile.)\n", - "\n", - ":::\n", - "\n", - "Once you have a constructor, you can create an instance of `MyPair` and set the\n", - "fields:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2\n" - ] - } - ], - "source": [ - "var mine = MyPair(2,4)\n", - "print(mine.first)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Methods\n", - "\n", - "In addition to special methods like `__init__()`, you can add any other method\n", - "you want to your struct. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPair:\n", - " var first: Int\n", - " var second: Int\n", - "\n", - " fn __init__(out self, first: Int, second: Int):\n", - " self.first = first\n", - " self.second = second\n", - "\n", - " fn get_sum(self) -> Int:\n", - " return self.first + self.second" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "14\n" - ] - } - ], - "source": [ - "var mine = MyPair(6, 8)\n", - "print(mine.get_sum())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that `get_sum()` also uses the `self` argument, because this is\n", - "the only way you can access the struct's fields in a method. The name `self` is\n", - "just a convention, and you can use any name you want to refer to the struct \n", - "instance that is always passed as the first argument.\n", - "\n", - "Methods that take the implicit `self` argument are called _instance methods_ \n", - "because they act on an instance of the struct. \n", - "\n", - ":::note\n", - "\n", - "The `self` argument in a struct method is the only argument in an\n", - "`fn` function that does not require a type. You can include it if you want, but\n", - "you can elide it because Mojo already knows its type (`MyPair` in this case).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Static methods\n", - "\n", - "A struct can also have _static methods_. A static method can be called without \n", - "creating an instance of the struct. Unlike instance methods, a static method\n", - "doesn't receive the implicit `self` argument, so it can't access any fields on\n", - "the struct.\n", - "\n", - "To declare a static method, use the `@staticmethod` decorator and don't include\n", - "a `self` argument:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "struct Logger:\n", - "\n", - " fn __init__(out self):\n", - " pass\n", - "\n", - " @staticmethod\n", - " fn log_info(message: String):\n", - " print(\"Info: \", message)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can invoke a static method by calling it on the type (in this case,\n", - "`Logger`). You can also call it on an instance of the type. Both forms are\n", - "shown below:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Info: Static method called.\n", - "Info: Static method called from instance.\n" - ] - } - ], - "source": [ - "Logger.log_info(\"Static method called.\")\n", - "var l = Logger()\n", - "l.log_info(\"Static method called from instance.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Structs compared to classes\n", - "\n", - "If you're familiar with other object-oriented languages, then structs might\n", - "sound a lot like classes, and there are some similarities, but also some\n", - "important differences. Eventually, Mojo will also support classes to match the\n", - "behavior of Python classes.\n", - "\n", - "So, let's compare Mojo structs to Python classes. They both support methods,\n", - "fields, operator overloading, decorators for metaprogramming, and more, but\n", - "their key differences are as follows:\n", - "\n", - "- Python classes are dynamic: they allow for dynamic dispatch, monkey-patching\n", - "(or “swizzling”), and dynamically binding instance fields at runtime.\n", - "\n", - "- Mojo structs are static: they are bound at compile-time (you cannot add\n", - "methods at runtime). Structs allow you to trade flexibility for performance\n", - "while being safe and easy to use.\n", - "\n", - "- Mojo structs do not support inheritance (\"sub-classing\"), but a struct can\n", - " implement [traits](/mojo/manual/traits).\n", - "\n", - "- Python classes support class attributes—values that are shared by all\n", - " instances of the class, equivalent to class variables or static data members\n", - " in other languages.\n", - " \n", - "- Mojo structs don't support static data members.\n", - "\n", - "Syntactically, the biggest difference compared to a Python class is that all\n", - "fields in a struct must be explicitly declared with `var`.\n", - "\n", - "In Mojo, the structure and contents of a struct are set at compile time and\n", - "can’t be changed while the program is running. Unlike in Python, where you can\n", - "add, remove, or change attributes of an object on the fly, Mojo doesn’t allow\n", - "that for structs.\n", - "\n", - "However, the static nature of structs helps Mojo run your code faster. The\n", - "program knows exactly where to find the struct’s information and how to use it\n", - "without any extra steps or delays at runtime.\n", - "\n", - "Mojo’s structs also work really well with features you might already know from\n", - "Python, like operator overloading (which lets you change how math symbols like\n", - "`+` and `-` work with your own data, using [special\n", - "methods](#special-methods)).\n", - "\n", - "As mentioned above, all Mojo's standard types\n", - "(`Int`, `String`, etc.) are made using structs, rather than being hardwired\n", - "into the language itself. This gives you more flexibility and control when\n", - "writing your code, and it means you can define your own types with all the same\n", - "capabilities (there's no special treatment for the standard library types)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Special methods\n", - "\n", - "Special methods (or \"dunder methods\") such as `__init__()` are pre-determined\n", - "method names that you can define in a struct to perform a special task.\n", - "\n", - "Although it's possible to call special methods with their method names, the\n", - "point is that you never should, because Mojo automatically invokes them in\n", - "circumstances where they're needed (which is why they're also called \"magic\n", - "methods\"). For example, Mojo calls the `__init__()` method when you create\n", - "an instance of the struct; and when Mojo destroys the instance, it calls the\n", - "`__del__()` method (if it exists).\n", - "\n", - "Even operator behaviors that appear built-in (`+`, `<`, `==`, `|`, and so on)\n", - "are implemented as special methods that Mojo implicitly calls upon to perform\n", - "operations or comparisons on the type that the operator is applied to.\n", - "\n", - "Mojo supports a long list of special methods; far too many to discuss here, but\n", - "they generally match all of [Python's special\n", - "methods](https://docs.python.org/3/reference/datamodel#special-method-names)\n", - "and they usually accomplish one of two types of tasks:\n", - "\n", - "- Operator overloading: A lot of special methods are designed to overload\n", - " operators such as `<` (less-than), `+` (add), and `|` (or) so they work\n", - " appropriately with each type. For example, look at the methods listed for Mojo's\n", - " [`Int` type](/mojo/stdlib/builtin/int/Int). One such method is `__lt__()`, which\n", - " Mojo calls to perform a less-than comparison between two integers (for example,\n", - " `num1 < num2`).\n", - "\n", - "- Lifecycle event handling: These special methods deal with the lifecycle and\n", - " value ownership of an instance. For example, `__init__()` and `__del__()`\n", - " demarcate the beginning and end of an instance lifetime, and other special\n", - " methods define the behavior for other lifecycle events such as how to copy or\n", - " move a value.\n", - "\n", - "You can learn all about the lifecycle special methods in the [Value\n", - "lifecycle](/mojo/manual/lifecycle/) section. However, most structs are simple\n", - "aggregations of other types, so unless your type requires custom behaviors when\n", - "an instance is created, copied, moved, or destroyed, you can synthesize the\n", - "essential lifecycle methods you need (and save yourself some time) by adding\n", - "the `@value` decorator." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `@value` decorator\n", - "\n", - "When you add the [`@value` decorator](/mojo/manual/decorators/value) to a\n", - "struct, Mojo will synthesize the essential lifecycle methods so your object\n", - "provides full value semantics. Specifically, it generates the `__init__()`,\n", - "`__copyinit__()`, and `__moveinit__()` methods, which allow you to construct,\n", - "copy, and move your struct type in a manner that's value semantic and\n", - "compatible with Mojo's ownership model.\n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct MyPet:\n", - " var name: String\n", - " var age: Int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo will notice that you don't have a member-wise initializer, a move\n", - "constructor, or a copy constructor, and it will synthesize these for you as if\n", - "you had written:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "struct MyPet:\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __init__(out self, owned name: String, age: Int):\n", - " self.name = name^\n", - " self.age = age\n", - "\n", - " fn __copyinit__(out self, existing: Self):\n", - " self.name = existing.name\n", - " self.age = existing.age\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " self.name = existing.name^\n", - " self.age = existing.age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Without the copy and move constructors, the following code would not work\n", - "because Mojo would not know how to copy an instance of `MyPet`:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Charlie\n" - ] - } - ], - "source": [ - "var dog = MyPet(\"Charlie\", 5)\n", - "var poodle = dog\n", - "print(poodle.name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you add the `@value` decorator, Mojo synthesizes each special method above\n", - "only if it doesn't exist already. That is, you can still implement a custom\n", - "version of each method.\n", - "\n", - "In addition to the `inout` argument convention you already saw with\n", - "`__init__()`, this code also introduces `owned`, which is another argument\n", - "convention that ensures the argument has unique ownership of the value.\n", - "For more detail, see the section about [value\n", - "ownership](/mojo/manual/values/ownership).\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/structs.mdx b/docs/manual/structs.mdx new file mode 100644 index 0000000000..59acf95f66 --- /dev/null +++ b/docs/manual/structs.mdx @@ -0,0 +1,317 @@ +--- +title: Structs +sidebar_position: 4 +description: Introduction to Mojo structures (structs). +--- + +A Mojo struct is a data structure that allows you to encapsulate fields and +methods that operate on an abstraction, such as a data type or an object. +**Fields** are variables that hold data relevant to the struct, and **methods** +are functions inside a struct that generally act upon the field data. + +For example, if you're building a graphics program, you can use a struct to +define an `Image` that has fields to store information about each image +(such as the pixels) and methods that perform actions on it (such as rotate +it). + +For the most part, Mojo's struct format is designed to provide a static, +memory-safe data structure for high-level data types used in programs. For +example, all the data types in Mojo's standard library (such as `Int`, +`Bool`, `String`, and `Tuple`) are defined as structs. + +If you understand how [functions](/mojo/manual/functions) and +[variables](/mojo/manual/variables) work in Mojo, you probably +noticed that Mojo is designed to provide dynamic programming features in a +`def` function while enforcing stronger code safety in `fn` functions. When it +comes to structs, Mojo leans toward the safe side: You can still choose whether +to use either `def` or `fn` declarations for methods, but all fields must be +declared with `var`. + +## Struct definition + +You can define a simple struct called `MyPair` with two fields like this: + +```mojo +struct MyPair: + var first: Int + var second: Int +``` + +However, you can't instantiate this struct because it has no constructor +method. So here it is with a constructor to initialize the two fields: + +```mojo +struct MyPair: + var first: Int + var second: Int + + fn __init__(out self, first: Int, second: Int): + self.first = first + self.second = second +``` + +Notice that the first argument in the `__init__()` method is `inout self`. For +now, ignore `inout` (it's an [argument +convention](/mojo/manual/values/ownership#argument-conventions) that +declares `self` as a mutable reference); all you need to know right now is that +`self` must be the first argument. It references the current struct instance +(it allows code in the method to refer to "itself"). *When you call the +constructor, you never pass a value for `self`—Mojo passes it in +automatically.* + +The `__init__()` method is one of many [special methods](#special-methods) +(also known as "dunder methods" because they have *d*ouble *under*scores) with +pre-determined names. + +:::note + +You can't assign values when you declare fields. You must initialize +all of the struct's fields in the constructor. (If you try to leave a field +uninitialized, the code won't compile.) + +::: + +Once you have a constructor, you can create an instance of `MyPair` and set the +fields: + +```mojo +var mine = MyPair(2,4) +print(mine.first) +``` + +```output +2 +``` + +## Methods + +In addition to special methods like `__init__()`, you can add any other method +you want to your struct. For example: + +```mojo +struct MyPair: + var first: Int + var second: Int + + fn __init__(out self, first: Int, second: Int): + self.first = first + self.second = second + + fn get_sum(self) -> Int: + return self.first + self.second +``` + +```mojo +var mine = MyPair(6, 8) +print(mine.get_sum()) +``` + +```output +14 +``` + +Notice that `get_sum()` also uses the `self` argument, because this is +the only way you can access the struct's fields in a method. The name `self` is +just a convention, and you can use any name you want to refer to the struct +instance that is always passed as the first argument. + +Methods that take the implicit `self` argument are called *instance methods* +because they act on an instance of the struct. + +:::note + +The `self` argument in a struct method is the only argument in an +`fn` function that does not require a type. You can include it if you want, but +you can elide it because Mojo already knows its type (`MyPair` in this case). + +::: + +### Static methods + +A struct can also have *static methods*. A static method can be called without +creating an instance of the struct. Unlike instance methods, a static method +doesn't receive the implicit `self` argument, so it can't access any fields on +the struct. + +To declare a static method, use the `@staticmethod` decorator and don't include +a `self` argument: + +```mojo +struct Logger: + + fn __init__(out self): + pass + + @staticmethod + fn log_info(message: String): + print("Info: ", message) +``` + +You can invoke a static method by calling it on the type (in this case, +`Logger`). You can also call it on an instance of the type. Both forms are +shown below: + +```mojo +Logger.log_info("Static method called.") +var l = Logger() +l.log_info("Static method called from instance.") +``` + +```output +Info: Static method called. +Info: Static method called from instance. +``` + +## Structs compared to classes + +If you're familiar with other object-oriented languages, then structs might +sound a lot like classes, and there are some similarities, but also some +important differences. Eventually, Mojo will also support classes to match the +behavior of Python classes. + +So, let's compare Mojo structs to Python classes. They both support methods, +fields, operator overloading, decorators for metaprogramming, and more, but +their key differences are as follows: + +* Python classes are dynamic: they allow for dynamic dispatch, monkey-patching + (or “swizzling”), and dynamically binding instance fields at runtime. + +* Mojo structs are static: they are bound at compile-time (you cannot add + methods at runtime). Structs allow you to trade flexibility for performance + while being safe and easy to use. + +* Mojo structs do not support inheritance ("sub-classing"), but a struct can + implement [traits](/mojo/manual/traits). + +* Python classes support class attributes—values that are shared by all + instances of the class, equivalent to class variables or static data members + in other languages. + +* Mojo structs don't support static data members. + +Syntactically, the biggest difference compared to a Python class is that all +fields in a struct must be explicitly declared with `var`. + +In Mojo, the structure and contents of a struct are set at compile time and +can’t be changed while the program is running. Unlike in Python, where you can +add, remove, or change attributes of an object on the fly, Mojo doesn’t allow +that for structs. + +However, the static nature of structs helps Mojo run your code faster. The +program knows exactly where to find the struct’s information and how to use it +without any extra steps or delays at runtime. + +Mojo’s structs also work really well with features you might already know from +Python, like operator overloading (which lets you change how math symbols like +`+` and `-` work with your own data, using [special +methods](#special-methods)). + +As mentioned above, all Mojo's standard types +(`Int`, `String`, etc.) are made using structs, rather than being hardwired +into the language itself. This gives you more flexibility and control when +writing your code, and it means you can define your own types with all the same +capabilities (there's no special treatment for the standard library types). + +## Special methods + +Special methods (or "dunder methods") such as `__init__()` are pre-determined +method names that you can define in a struct to perform a special task. + +Although it's possible to call special methods with their method names, the +point is that you never should, because Mojo automatically invokes them in +circumstances where they're needed (which is why they're also called "magic +methods"). For example, Mojo calls the `__init__()` method when you create +an instance of the struct; and when Mojo destroys the instance, it calls the +`__del__()` method (if it exists). + +Even operator behaviors that appear built-in (`+`, `<`, `==`, `|`, and so on) +are implemented as special methods that Mojo implicitly calls upon to perform +operations or comparisons on the type that the operator is applied to. + +Mojo supports a long list of special methods; far too many to discuss here, but +they generally match all of [Python's special +methods](https://docs.python.org/3/reference/datamodel#special-method-names) +and they usually accomplish one of two types of tasks: + +* Operator overloading: A lot of special methods are designed to overload + operators such as `<` (less-than), `+` (add), and `|` (or) so they work + appropriately with each type. For example, look at the methods listed for Mojo's + [`Int` type](/mojo/stdlib/builtin/int/Int). One such method is `__lt__()`, which + Mojo calls to perform a less-than comparison between two integers (for example, + `num1 < num2`). + +* Lifecycle event handling: These special methods deal with the lifecycle and + value ownership of an instance. For example, `__init__()` and `__del__()` + demarcate the beginning and end of an instance lifetime, and other special + methods define the behavior for other lifecycle events such as how to copy or + move a value. + +You can learn all about the lifecycle special methods in the [Value +lifecycle](/mojo/manual/lifecycle/) section. However, most structs are simple +aggregations of other types, so unless your type requires custom behaviors when +an instance is created, copied, moved, or destroyed, you can synthesize the +essential lifecycle methods you need (and save yourself some time) by adding +the `@value` decorator. + +### `@value` decorator + +When you add the [`@value` decorator](/mojo/manual/decorators/value) to a +struct, Mojo will synthesize the essential lifecycle methods so your object +provides full value semantics. Specifically, it generates the `__init__()`, +`__copyinit__()`, and `__moveinit__()` methods, which allow you to construct, +copy, and move your struct type in a manner that's value semantic and +compatible with Mojo's ownership model. + +For example: + +```mojo +@value +struct MyPet: + var name: String + var age: Int +``` + +Mojo will notice that you don't have a member-wise initializer, a move +constructor, or a copy constructor, and it will synthesize these for you as if +you had written: + +```mojo +struct MyPet: + var name: String + var age: Int + + fn __init__(out self, owned name: String, age: Int): + self.name = name^ + self.age = age + + fn __copyinit__(out self, existing: Self): + self.name = existing.name + self.age = existing.age + + fn __moveinit__(out self, owned existing: Self): + self.name = existing.name^ + self.age = existing.age +``` + +Without the copy and move constructors, the following code would not work +because Mojo would not know how to copy an instance of `MyPet`: + +```mojo +var dog = MyPet("Charlie", 5) +var poodle = dog +print(poodle.name) +``` + +```output +Charlie +``` + +When you add the `@value` decorator, Mojo synthesizes each special method above +only if it doesn't exist already. That is, you can still implement a custom +version of each method. + +In addition to the `inout` argument convention you already saw with +`__init__()`, this code also introduces `owned`, which is another argument +convention that ensures the argument has unique ownership of the value. +For more detail, see the section about [value +ownership](/mojo/manual/values/ownership). diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb deleted file mode 100755 index ef9fbd824e..0000000000 --- a/docs/manual/traits.ipynb +++ /dev/null @@ -1,750 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Traits\n", - "description: Define shared behavior for types.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A _trait_ is a set of requirements that a type must implement. You can think of\n", - "it as a contract: a type that _conforms_ to a trait guarantees that it \n", - "implements all of the features of the trait.\n", - "\n", - "Traits are similar to Java _interfaces_, C++ _concepts_, Swift _protocols_, and\n", - "Rust _traits_. If you're familiar with any of those features, Mojo traits solve\n", - "the same basic problem.\n", - "\n", - "## Background\n", - "\n", - "In dynamically-typed languages like Python, you don't need to explicitly declare\n", - "that two classes are similar. This is easiest to show by example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%python\n", - "class Duck:\n", - " def quack(self):\n", - " print(\"Quack.\")\n", - "\n", - "class StealthCow:\n", - " def quack(self):\n", - " print(\"Moo!\")\n", - "\n", - "def make_it_quack_python(maybe_a_duck):\n", - " try:\n", - " maybe_a_duck.quack()\n", - " except:\n", - " print(\"Not a duck.\")\n", - "\n", - "make_it_quack_python(Duck())\n", - "make_it_quack_python(StealthCow())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `Duck` and `StealthCow` classes aren't related in any way, but they both \n", - "define a `quack()` method, so they work the same in the `make_it_quack()`\n", - "function. This works because Python uses dynamic dispatch—it identifies the\n", - "methods to call at runtime. So `make_it_quack_python()` doesn't care what types\n", - "you're passing it, only the fact that they implement the `quack()` method.\n", - "\n", - "In a statically-typed environment, this approach doesn't work:\n", - "[`fn` functions](/mojo/manual/functions#fn-functions) require you to\n", - "specify the type of each argument. If you wanted to write this example in Mojo \n", - "_without_ traits, you'd need to write a function overload for each input type.\n", - "All of the examples from here on are in Mojo, so we'll just call the function\n", - "`make_it_quack()` going forward." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Quack\n", - "Moo!\n" - ] - } - ], - "source": [ - "@value\n", - "struct Duck:\n", - " fn quack(self):\n", - " print(\"Quack\")\n", - "\n", - "@value\n", - "struct StealthCow:\n", - " fn quack(self):\n", - " print(\"Moo!\")\n", - "\n", - "fn make_it_quack(definitely_a_duck: Duck):\n", - " definitely_a_duck.quack()\n", - "\n", - "fn make_it_quack(not_a_duck: StealthCow):\n", - " not_a_duck.quack()\n", - "\n", - "make_it_quack(Duck())\n", - "make_it_quack(StealthCow())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This isn't too bad with only two classes. But the more classes you want to\n", - "support, the less practical this approach is.\n", - "\n", - "You might notice that the Mojo versions of `make_it_quack()` don't include the\n", - "`try/except` statement. We don't need it because Mojo's static type checking\n", - "ensures that you can only pass instances of `Duck` or `StealthCow` into the \n", - "`make_it_quack()`function.\n", - "\n", - "## Using traits\n", - "\n", - "Traits solve this problem by letting you define a shared set of _behaviors_ that\n", - "types can implement. Then you can write a function that depends on the trait,\n", - "rather than individual types. As an example, let's update the `make_it_quack()`\n", - "example using traits. The first step is defining a trait:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "trait Quackable:\n", - " fn quack(self):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A trait looks a lot like a struct, except it's introduced by the `trait` \n", - "keyword. Right now, a trait can only contain method signatures, and cannot\n", - "include method implementations. Each method signature must be followed by\n", - "three dots (`...`) to indicate that the method is unimplemented.\n", - "\n", - ":::note TODO\n", - "\n", - "In the future, we plan to support defining fields and default method\n", - "implementations inside a trait. Right now, though, a trait can only declare\n", - "method signatures.\n", - "\n", - ":::\n", - "\n", - "Next we create some structs that conform to the `Quackable` trait. To indicate\n", - "that a struct conforms to a trait, include the trait name in parenthesis after\n", - "the struct name. You can also include multiple traits, separated by commas. \n", - "(If you're familiar with Python, this looks just like Python's inheritance\n", - "syntax.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "struct Duck(Quackable):\n", - " fn quack(self):\n", - " print(\"Quack\")\n", - "\n", - "@value\n", - "struct StealthCow(Quackable):\n", - " fn quack(self):\n", - " print(\"Moo!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The struct needs to implement any methods that are declared in the trait. The \n", - "compiler enforces conformance: if a struct says it conforms to a trait, it must\n", - "implement everything required by the trait or the code won't compile.\n", - "\n", - "Finally, you can define a function that takes a `Quackable` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "fn make_it_quack[T: Quackable](maybe_a_duck: T):\n", - " maybe_a_duck.quack()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This syntax may look a little unfamiliar if you haven't dealt with Mojo\n", - "[parameters](/mojo/manual/parameters/) before. What this signature\n", - "means is that `maybe_a_duck` is an argument of type `T`, where `T` is a type\n", - "that must conform to the `Quackable` trait. TODO: This syntax is a little \n", - "verbose, and we hope to make it more ergonomic in a future release.\n", - "\n", - "Using the method is simple enough:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Quack\n", - "Moo!\n" - ] - } - ], - "source": [ - "make_it_quack(Duck())\n", - "make_it_quack(StealthCow())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that you don't need the square brackets when you call `make_it_quack()`: \n", - "the compiler infers the type of the argument, and ensures the type has the\n", - "required trait.\n", - "\n", - "One limitation of traits is that you can't add traits to existing types. For\n", - "example, if you define a new `Numeric` trait, you can't add it to the standard\n", - "library `Float64` and `Int` types. However, the standard library already\n", - "includes a few traits, and we'll be adding more over time.\n", - "\n", - "### Traits can require static methods\n", - "\n", - "In addition to regular instance methods, traits can specify required static \n", - "methods. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "trait HasStaticMethod:\n", - " @staticmethod\n", - " fn do_stuff(): ...\n", - "\n", - "fn fun_with_traits[T: HasStaticMethod]():\n", - " T.do_stuff()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Implicit trait conformance\n", - "\n", - "Mojo also supports _implicit_ trait conformance. That is, if a type implements\n", - "all of the methods required for a trait, it's treated as conforming to the\n", - "trait, even if it doesn't explicitly include the trait in its declaration:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "struct RubberDucky:\n", - " fn quack(self):\n", - " print(\"Squeak!\")\n", - "\n", - "make_it_quack(RubberDucky())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Implicit conformance can be handy if you're defining a trait and you want it to\n", - "work with types that you don't control—such as types from the standard library,\n", - "or a third-party library.\n", - "\n", - "However, we still strongly recommend explicit trait conformance wherever\n", - "possible. This has two advantages:\n", - "\n", - "- Documentation. It makes it clear that the type conforms to the trait, without\n", - " having to scan all of its methods.\n", - "\n", - "- Future feature support. When default method implementations are added to\n", - " traits, they'll only work for types that explicitly conform to traits." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Trait inheritance\n", - "\n", - "Traits can inherit from other traits. A trait that inherits from another trait\n", - "includes all of the requirements declared by the parent trait. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "trait Animal:\n", - " fn make_sound(self):\n", - " ...\n", - "\n", - "# Bird inherits from Animal\n", - "trait Bird(Animal):\n", - " fn fly(self):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since `Bird` inherits from `Animal`, a struct that conforms to the `Bird` trait\n", - "needs to implement **both** `make_sound()` and `fly()`. And since every `Bird`\n", - "conforms to `Animal`, a struct that conforms to `Bird` can be passed to any\n", - "function that requires an `Animal`.\n", - "\n", - "To inherit from multiple traits, add a comma-separated list of traits inside the \n", - "parenthesis. For example, you could define a `NamedAnimal` trait that combines the\n", - "requirements of the `Animal` trait and a new `Named` trait:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "trait Named:\n", - " fn get_name(self) -> String:\n", - " ...\n", - "\n", - "trait NamedAnimal(Animal, Named):\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Traits and lifecycle methods\n", - "\n", - "Traits can specify required \n", - "[lifecycle methods](/mojo/manual/lifecycle/#lifecycles-and-lifetimes), including\n", - "constructors, copy constructors and move constructors.\n", - "\n", - "For example, the following code creates a `MassProducible` trait. A \n", - "`MassProducible` type has a default (no-argument) constructor and can be moved.\n", - "It uses the built-in [`Movable`](/mojo/stdlib/builtin/value/Movable) trait,\n", - "which requires the type to have a [move \n", - "constructor](/mojo/manual/lifecycle/life#move-constructor).\n", - "\n", - "The `factory[]()` function returns a newly-constructed instance of a \n", - "`MassProducible` type." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "trait DefaultConstructible:\n", - " fn __init__(out self): ...\n", - "\n", - "trait MassProducible(DefaultConstructible, Movable):\n", - " pass\n", - "\n", - "fn factory[T: MassProducible]() -> T:\n", - " return T()\n", - "\n", - "struct Thing(MassProducible):\n", - " var id: Int\n", - "\n", - " fn __init__(out self):\n", - " self.id = 0\n", - "\n", - " fn __moveinit__(out self, owned existing: Self):\n", - " self.id = existing.id\n", - "\n", - "var thing = factory[Thing]()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that [`@register_passable(\"trivial\")`](/mojo/manual/decorators/register-passable#register_passabletrivial) \n", - "types have restrictions on their lifecycle methods: they can't define copy or\n", - "move constructors, because they don't require any custom logic.\n", - "\n", - "For the purpose of trait conformance, the compiler treats trivial types as\n", - "copyable and movable." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "## Built-in traits\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Mojo standard library currently includes a few traits. They're implemented\n", - "by a number of standard library types, and you can also implement these on your\n", - "own types:\n", - "\n", - " - [`Absable`](/mojo/stdlib/builtin/math/Absable)\n", - " - [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType)\n", - " - [`Boolable`](/mojo/stdlib/builtin/bool/Boolable)\n", - " - [`BoolableCollectionElement`](/mojo/stdlib/builtin/value/BoolableCollectionElement)\n", - " - [`BoolableKeyElement`](/mojo/stdlib/builtin/value/BoolableKeyElement)\n", - " - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement)\n", - " - [`Comparable`](/mojo/stdlib/builtin/comparable/Comparable)\n", - " - [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement)\n", - " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", - " - [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable)\n", - " - [`Writable`](/mojo/stdlib/utils/write/Writable)\n", - " - [`Hashable`](/mojo/stdlib/builtin/hash/Hashable)\n", - " - [`Indexer`](/mojo/stdlib/builtin/int/Indexer)\n", - " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", - " - [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising)\n", - " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", - " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", - " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", - " - [`Powable`](/mojo/stdlib/builtin/math/Powable)\n", - " - [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", - " - [`RepresentableCollectionElement`](/mojo/stdlib/builtin/value/RepresentableCollectionElement)\n", - " - [`RepresentableKeyElement`](/mojo/stdlib/collections/dict/RepresentableKeyElement)\n", - " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", - " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", - " - [`StringableCollectionElement`](/mojo/stdlib/builtin/value/StringableCollectionElement)\n", - " - [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising)\n", - " - [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", - " - [`Roundable`](/mojo/stdlib/builtin/math/Roundable)\n", - " - [`Writer`](/mojo/stdlib/utils/write/Writer)\n", - " - [`Truncable`](/mojo/stdlib/builtin/_math/Truncable)\n", - "\n", - "The API reference docs linked above include usage examples for each trait. The\n", - "following sections discuss a few of these traits.\n", - "\n", - "### The `Sized` trait\n", - "\n", - "The [`Sized`](/mojo/stdlib/builtin/len/Sized) trait identifies types that\n", - "have a measurable length, like strings and arrays. \n", - "\n", - "Specifically, `Sized` requires a type to implement the `__len__()` method. \n", - "This trait is used by the built-in [`len()`](/mojo/stdlib/builtin/len/len) \n", - "function. For example, if you're writing a custom list type, you could \n", - "implement this trait so your type works with `len()`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0\n" - ] - } - ], - "source": [ - "struct MyList(Sized):\n", - " var size: Int\n", - " # ...\n", - "\n", - " fn __init__(out self):\n", - " self.size = 0\n", - "\n", - " fn __len__(self) -> Int:\n", - " return self.size\n", - "\n", - "print(len(MyList()))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `Intable` and `IntableRaising` traits\n", - "\n", - "The [`Intable`](/mojo/stdlib/builtin/int/Intable) trait identifies a type that\n", - "can be implicitly converted to `Int`. The\n", - "[`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) trait describes a\n", - "type can be converted to an `Int`, but the conversion might raise an error.\n", - "\n", - "Both of these traits require the type to implement the `__int__()` method. For\n", - "example:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - } - ], - "source": [ - "@value\n", - "struct Foo(Intable):\n", - " var i: Int\n", - "\n", - " fn __int__(self) -> Int:\n", - " return self.i\n", - "\n", - "var foo = Foo(42)\n", - "print(int(foo) == 42)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `Stringable`, `Representable`, and `Writable` traits\n", - "\n", - "The [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait identifies a type\n", - "that can be implicitly converted to\n", - "[`String`](/mojo/stdlib/collections/string/String). The\n", - "[`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) trait\n", - "describes a type that can be converted to a `String`, but the conversion might\n", - "raise an error. Any type that conforms to `Stringable` or `StringableRaising`\n", - "also works with the built-in [`str()`](/mojo/stdlib/builtin/str/str) function to\n", - "explicitly return a `String`. These traits also mean that the type can support\n", - "both the `{!s}` and `{}` format specifiers of the `String` class's\n", - "[`format()`](/mojo/stdlib/collections/string/String#format) method. These traits\n", - "require the type to define the\n", - "[`__str__()`](/mojo/stdlib/builtin/str/Stringable#__str__) method.\n", - "\n", - "In contrast, the [`Representable`](/mojo/stdlib/builtin/repr/Representable)\n", - "trait defines a type that can be used with the built-in\n", - "[`repr()`](/mojo/stdlib/builtin/repr/repr) function, as well as the `{!r}`\n", - "format specifier of the `format()` method. This trait requires the type to\n", - "define the [`__repr__()`](/mojo/stdlib/builtin/repr/Representable#__repr__)\n", - "method, which should compute the \"official\" string representation of a type. If\n", - "at all possible, this should look like a valid Mojo expression that could be\n", - "used to recreate a struct instance with the same value.\n", - "\n", - "The [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable)\n", - "trait denotes a trait composition of the `Stringable` and `Representable`\n", - "traits. It requires a type to implement both a `__str__()` and a `__repr__()`\n", - "method.\n", - "\n", - "The [`Writable`](/mojo/stdlib/utils/write/Writable) trait describes a\n", - "type that can be converted to a stream of UTF-8 encoded data by writing to a\n", - "`Writer` object. The [`print()`](/mojo/stdlib/builtin/io/print) function\n", - "requires that its arguments conform to the `Writable` trait. This enables\n", - "efficient stream-based writing by default, avoiding unnecessary intermediate\n", - "String heap allocations.\n", - "\n", - "The `Writable` trait requires a type to implement a\n", - "[`write_to()`](/mojo/stdlib/utils/write/Writable#write_to) method, which\n", - "is provided with an object that conforms to the\n", - "[`Writer`](/mojo/stdlib/utils/write/Writer) as an argument. You then\n", - "invoke the `Writer` instance's\n", - "[`write()`](/mojo/stdlib/utils/write/Writer#write) method to write a\n", - "sequence of `Writable` arguments constituting the `String` representation of\n", - "your type.\n", - "\n", - "While this might sound complex at first, in practice you can minimize\n", - "boilerplate and duplicated code by using the [`String.write()`](/mojo/stdlib/collections/string/String#write) static function to\n", - "implement the type's `Stringable` implementation in terms of its `Writable`\n", - "implementation. Here is a simple example of a type that implements all of the\n", - "`Stringable`, `Representable`, and `Writable` traits:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dog(name='Rex', age=5)\n", - "Dog(Rex, 5)\n", - "String: Dog(Rex, 5)\n", - "Representation: Dog(name='Rex', age=5)\n" - ] - } - ], - "source": [ - "@value\n", - "struct Dog(Stringable, Representable, Writable):\n", - " var name: String\n", - " var age: Int\n", - "\n", - " fn __repr__(self) -> String:\n", - " return \"Dog(name=\" + repr(self.name) + \", age=\" + repr(self.age) + \")\"\n", - "\n", - " fn __str__(self) -> String:\n", - " return String.write(self)\n", - "\n", - " fn write_to[W: Writer](self, inout writer: W) -> None:\n", - " writer.write(\"Dog(\", self.name, \", \", self.age, \")\")\n", - "\n", - "var dog = Dog(\"Rex\", 5)\n", - "print(repr(dog))\n", - "print(dog)\n", - "\n", - "var dog_info = String(\"String: {!s}\\nRepresentation: {!r}\").format(dog, dog)\n", - "print(dog_info)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "### The `AnyType` trait\n", - "\n", - "When building a generic container type, one challenge is knowing how to dispose\n", - "of the contained items when the container is destroyed. Any type that \n", - "dynamically allocates memory needs to supply a \n", - "[destructor](/mojo/manual/lifecycle/death#destructor) (`__del__()` method)\n", - "that must be called to free the allocated memory. But not all types have a \n", - "destructor, and your Mojo code has no way to determine which is which.\n", - "\n", - "The [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType) trait solves this\n", - "issue: every trait implicitly inherits from `AnyType`, and all structs conform\n", - "to `AnyType`, which guarantees that the type has a destructor. For types that \n", - "don't have one, Mojo adds a no-op destructor. This means you can call the \n", - "destructor on any type.\n", - "\n", - "This makes it possible to build generic collections without leaking memory. When\n", - "the collection's destructor is called, it can safely call the destructors on\n", - "every item it contains." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Generic structs with traits\n", - "\n", - "You can also use traits when defining a generic container. A generic container\n", - "is a container (for example, an array or hashmap) that can hold different data\n", - "types. In a dynamic language like Python it's easy to add different types of\n", - "items to a container. But in a statically-typed environment the compiler needs\n", - "to be able to identify the types at compile time. For example, if the container\n", - "needs to copy a value, the compiler needs to verify that the type can be copied.\n", - "\n", - "The [`List`](/mojo/stdlib/collections/list) type is an example of a\n", - "generic container. A single `List` can only hold a single type of data.\n", - "For example, you can create a list of integer values like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 2 3 " - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "var list = List[Int](1, 2, 3)\n", - "for i in range(len(list)):\n", - " print(list[i], sep=\" \", end=\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can use traits to define requirements for elements that are stored in a\n", - "container. For example, `List` requires elements that can be moved and\n", - "copied. To store a struct in a `List`, the struct needs to conform to\n", - "the `CollectionElement` trait, which requires a \n", - "[copy constructor](/mojo/manual/lifecycle/life#copy-constructor) and a \n", - "[move constructor](/mojo/manual/lifecycle/life#move-constructor).\n", - "\n", - "Building generic containers is an advanced topic. For an introduction, see the\n", - "section on \n", - "[parameterized structs](/mojo/manual/parameters/#parameterized-structs)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/traits.mdx b/docs/manual/traits.mdx new file mode 100644 index 0000000000..4e1ae7b321 --- /dev/null +++ b/docs/manual/traits.mdx @@ -0,0 +1,515 @@ +--- +title: Traits +description: Define shared behavior for types. +--- + +A *trait* is a set of requirements that a type must implement. You can think of +it as a contract: a type that *conforms* to a trait guarantees that it +implements all of the features of the trait. + +Traits are similar to Java *interfaces*, C++ *concepts*, Swift *protocols*, and +Rust *traits*. If you're familiar with any of those features, Mojo traits solve +the same basic problem. + +## Background + +In dynamically-typed languages like Python, you don't need to explicitly declare +that two classes are similar. This is easiest to show by example: + +```mojo +%%python +class Duck: + def quack(self): + print("Quack.") + +class StealthCow: + def quack(self): + print("Moo!") + +def make_it_quack_python(maybe_a_duck): + try: + maybe_a_duck.quack() + except: + print("Not a duck.") + +make_it_quack_python(Duck()) +make_it_quack_python(StealthCow()) +``` + +The `Duck` and `StealthCow` classes aren't related in any way, but they both +define a `quack()` method, so they work the same in the `make_it_quack()` +function. This works because Python uses dynamic dispatch—it identifies the +methods to call at runtime. So `make_it_quack_python()` doesn't care what types +you're passing it, only the fact that they implement the `quack()` method. + +In a statically-typed environment, this approach doesn't work: +[`fn` functions](/mojo/manual/functions#fn-functions) require you to +specify the type of each argument. If you wanted to write this example in Mojo +*without* traits, you'd need to write a function overload for each input type. +All of the examples from here on are in Mojo, so we'll just call the function +`make_it_quack()` going forward. + +```mojo +@value +struct Duck: + fn quack(self): + print("Quack") + +@value +struct StealthCow: + fn quack(self): + print("Moo!") + +fn make_it_quack(definitely_a_duck: Duck): + definitely_a_duck.quack() + +fn make_it_quack(not_a_duck: StealthCow): + not_a_duck.quack() + +make_it_quack(Duck()) +make_it_quack(StealthCow()) +``` + +```output +Quack +Moo! +``` + +This isn't too bad with only two classes. But the more classes you want to +support, the less practical this approach is. + +You might notice that the Mojo versions of `make_it_quack()` don't include the +`try/except` statement. We don't need it because Mojo's static type checking +ensures that you can only pass instances of `Duck` or `StealthCow` into the +`make_it_quack()`function. + +## Using traits + +Traits solve this problem by letting you define a shared set of *behaviors* that +types can implement. Then you can write a function that depends on the trait, +rather than individual types. As an example, let's update the `make_it_quack()` +example using traits. The first step is defining a trait: + +```mojo +trait Quackable: + fn quack(self): + ... +``` + +A trait looks a lot like a struct, except it's introduced by the `trait` +keyword. Right now, a trait can only contain method signatures, and cannot +include method implementations. Each method signature must be followed by +three dots (`...`) to indicate that the method is unimplemented. + +:::note TODO + +In the future, we plan to support defining fields and default method +implementations inside a trait. Right now, though, a trait can only declare +method signatures. + +::: + +Next we create some structs that conform to the `Quackable` trait. To indicate +that a struct conforms to a trait, include the trait name in parenthesis after +the struct name. You can also include multiple traits, separated by commas. +(If you're familiar with Python, this looks just like Python's inheritance +syntax.) + +```mojo +@value +struct Duck(Quackable): + fn quack(self): + print("Quack") + +@value +struct StealthCow(Quackable): + fn quack(self): + print("Moo!") +``` + +The struct needs to implement any methods that are declared in the trait. The +compiler enforces conformance: if a struct says it conforms to a trait, it must +implement everything required by the trait or the code won't compile. + +Finally, you can define a function that takes a `Quackable` like this: + +```mojo +fn make_it_quack[T: Quackable](maybe_a_duck: T): + maybe_a_duck.quack() +``` + +This syntax may look a little unfamiliar if you haven't dealt with Mojo +[parameters](/mojo/manual/parameters/) before. What this signature +means is that `maybe_a_duck` is an argument of type `T`, where `T` is a type +that must conform to the `Quackable` trait. TODO: This syntax is a little +verbose, and we hope to make it more ergonomic in a future release. + +Using the method is simple enough: + +```mojo +make_it_quack(Duck()) +make_it_quack(StealthCow()) +``` + +```output +Quack +Moo! +``` + +Note that you don't need the square brackets when you call `make_it_quack()`: +the compiler infers the type of the argument, and ensures the type has the +required trait. + +One limitation of traits is that you can't add traits to existing types. For +example, if you define a new `Numeric` trait, you can't add it to the standard +library `Float64` and `Int` types. However, the standard library already +includes a few traits, and we'll be adding more over time. + +### Traits can require static methods + +In addition to regular instance methods, traits can specify required static +methods. + +```mojo +trait HasStaticMethod: + @staticmethod + fn do_stuff(): ... + +fn fun_with_traits[T: HasStaticMethod](): + T.do_stuff() +``` + +## Implicit trait conformance + +Mojo also supports *implicit* trait conformance. That is, if a type implements +all of the methods required for a trait, it's treated as conforming to the +trait, even if it doesn't explicitly include the trait in its declaration: + +```mojo +struct RubberDucky: + fn quack(self): + print("Squeak!") + +make_it_quack(RubberDucky()) +``` + +Implicit conformance can be handy if you're defining a trait and you want it to +work with types that you don't control—such as types from the standard library, +or a third-party library. + +However, we still strongly recommend explicit trait conformance wherever +possible. This has two advantages: + +* Documentation. It makes it clear that the type conforms to the trait, without + having to scan all of its methods. + +* Future feature support. When default method implementations are added to + traits, they'll only work for types that explicitly conform to traits. + +## Trait inheritance + +Traits can inherit from other traits. A trait that inherits from another trait +includes all of the requirements declared by the parent trait. For example: + +```mojo +trait Animal: + fn make_sound(self): + ... + +# Bird inherits from Animal +trait Bird(Animal): + fn fly(self): + ... +``` + +Since `Bird` inherits from `Animal`, a struct that conforms to the `Bird` trait +needs to implement **both** `make_sound()` and `fly()`. And since every `Bird` +conforms to `Animal`, a struct that conforms to `Bird` can be passed to any +function that requires an `Animal`. + +To inherit from multiple traits, add a comma-separated list of traits inside the +parenthesis. For example, you could define a `NamedAnimal` trait that combines the +requirements of the `Animal` trait and a new `Named` trait: + +```mojo +trait Named: + fn get_name(self) -> String: + ... + +trait NamedAnimal(Animal, Named): + pass +``` + +## Traits and lifecycle methods + +Traits can specify required +[lifecycle methods](/mojo/manual/lifecycle/#lifecycles-and-lifetimes), including +constructors, copy constructors and move constructors. + +For example, the following code creates a `MassProducible` trait. A +`MassProducible` type has a default (no-argument) constructor and can be moved. +It uses the built-in [`Movable`](/mojo/stdlib/builtin/value/Movable) trait, +which requires the type to have a [move +constructor](/mojo/manual/lifecycle/life#move-constructor). + +The `factory[]()` function returns a newly-constructed instance of a +`MassProducible` type. + +```mojo +trait DefaultConstructible: + fn __init__(out self): ... + +trait MassProducible(DefaultConstructible, Movable): + pass + +fn factory[T: MassProducible]() -> T: + return T() + +struct Thing(MassProducible): + var id: Int + + fn __init__(out self): + self.id = 0 + + fn __moveinit__(out self, owned existing: Self): + self.id = existing.id + +var thing = factory[Thing]() +``` + +Note that [`@register_passable("trivial")`](/mojo/manual/decorators/register-passable#register_passabletrivial) +types have restrictions on their lifecycle methods: they can't define copy or +move constructors, because they don't require any custom logic. + +For the purpose of trait conformance, the compiler treats trivial types as +copyable and movable. + +## Built-in traits + +The Mojo standard library currently includes a few traits. They're implemented +by a number of standard library types, and you can also implement these on your +own types: + +* [`Absable`](/mojo/stdlib/builtin/math/Absable) +* [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType) +* [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) +* [`BoolableCollectionElement`](/mojo/stdlib/builtin/value/BoolableCollectionElement) +* [`BoolableKeyElement`](/mojo/stdlib/builtin/value/BoolableKeyElement) +* [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) +* [`Comparable`](/mojo/stdlib/builtin/comparable/Comparable) +* [`ComparableCollectionElement`](/mojo/stdlib/builtin/value/ComparableCollectionElement) +* [`Copyable`](/mojo/stdlib/builtin/value/Copyable) +* [`Defaultable`](/mojo/stdlib/builtin/value/Defaultable) +* [`Writable`](/mojo/stdlib/utils/write/Writable) +* [`Hashable`](/mojo/stdlib/builtin/hash/Hashable) +* [`Indexer`](/mojo/stdlib/builtin/int/Indexer) +* [`Intable`](/mojo/stdlib/builtin/int/Intable) +* [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) +* [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) +* [`Movable`](/mojo/stdlib/builtin/value/Movable) +* [`PathLike`](/mojo/stdlib/os/pathlike/PathLike) +* [`Powable`](/mojo/stdlib/builtin/math/Powable) +* [`Representable`](/mojo/stdlib/builtin/repr/Representable) +* [`RepresentableCollectionElement`](/mojo/stdlib/builtin/value/RepresentableCollectionElement) +* [`RepresentableKeyElement`](/mojo/stdlib/collections/dict/RepresentableKeyElement) +* [`Sized`](/mojo/stdlib/builtin/len/Sized) +* [`Stringable`](/mojo/stdlib/builtin/str/Stringable) +* [`StringableCollectionElement`](/mojo/stdlib/builtin/value/StringableCollectionElement) +* [`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) +* [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable) +* [`Roundable`](/mojo/stdlib/builtin/math/Roundable) +* [`Writer`](/mojo/stdlib/utils/write/Writer) +* [`Truncable`](/mojo/stdlib/builtin/_math/Truncable) + +The API reference docs linked above include usage examples for each trait. The +following sections discuss a few of these traits. + +### The `Sized` trait + +The [`Sized`](/mojo/stdlib/builtin/len/Sized) trait identifies types that +have a measurable length, like strings and arrays. + +Specifically, `Sized` requires a type to implement the `__len__()` method. +This trait is used by the built-in [`len()`](/mojo/stdlib/builtin/len/len) +function. For example, if you're writing a custom list type, you could +implement this trait so your type works with `len()`: + +```mojo +struct MyList(Sized): + var size: Int + # ... + + fn __init__(out self): + self.size = 0 + + fn __len__(self) -> Int: + return self.size + +print(len(MyList())) +``` + +```output +0 +``` + +### The `Intable` and `IntableRaising` traits + +The [`Intable`](/mojo/stdlib/builtin/int/Intable) trait identifies a type that +can be implicitly converted to `Int`. The +[`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) trait describes a +type can be converted to an `Int`, but the conversion might raise an error. + +Both of these traits require the type to implement the `__int__()` method. For +example: + +```mojo +@value +struct Foo(Intable): + var i: Int + + fn __int__(self) -> Int: + return self.i + +var foo = Foo(42) +print(int(foo) == 42) +``` + +```output +True +``` + +### The `Stringable`, `Representable`, and `Writable` traits + +The [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait identifies a type +that can be implicitly converted to +[`String`](/mojo/stdlib/collections/string/String). The +[`StringableRaising`](/mojo/stdlib/builtin/str/StringableRaising) trait +describes a type that can be converted to a `String`, but the conversion might +raise an error. Any type that conforms to `Stringable` or `StringableRaising` +also works with the built-in [`str()`](/mojo/stdlib/builtin/str/str) function to +explicitly return a `String`. These traits also mean that the type can support +both the `{!s}` and `{}` format specifiers of the `String` class's +[`format()`](/mojo/stdlib/collections/string/String#format) method. These traits +require the type to define the +[`__str__()`](/mojo/stdlib/builtin/str/Stringable#__str__) method. + +In contrast, the [`Representable`](/mojo/stdlib/builtin/repr/Representable) +trait defines a type that can be used with the built-in +[`repr()`](/mojo/stdlib/builtin/repr/repr) function, as well as the `{!r}` +format specifier of the `format()` method. This trait requires the type to +define the [`__repr__()`](/mojo/stdlib/builtin/repr/Representable#__repr__) +method, which should compute the "official" string representation of a type. If +at all possible, this should look like a valid Mojo expression that could be +used to recreate a struct instance with the same value. + +The [`StringRepresentable`](/mojo/stdlib/collections/string/StringRepresentable) +trait denotes a trait composition of the `Stringable` and `Representable` +traits. It requires a type to implement both a `__str__()` and a `__repr__()` +method. + +The [`Writable`](/mojo/stdlib/utils/write/Writable) trait describes a +type that can be converted to a stream of UTF-8 encoded data by writing to a +`Writer` object. The [`print()`](/mojo/stdlib/builtin/io/print) function +requires that its arguments conform to the `Writable` trait. This enables +efficient stream-based writing by default, avoiding unnecessary intermediate +String heap allocations. + +The `Writable` trait requires a type to implement a +[`write_to()`](/mojo/stdlib/utils/write/Writable#write_to) method, which +is provided with an object that conforms to the +[`Writer`](/mojo/stdlib/utils/write/Writer) as an argument. You then +invoke the `Writer` instance's +[`write()`](/mojo/stdlib/utils/write/Writer#write) method to write a +sequence of `Writable` arguments constituting the `String` representation of +your type. + +While this might sound complex at first, in practice you can minimize +boilerplate and duplicated code by using the [`String.write()`](/mojo/stdlib/collections/string/String#write) static function to +implement the type's `Stringable` implementation in terms of its `Writable` +implementation. Here is a simple example of a type that implements all of the +`Stringable`, `Representable`, and `Writable` traits: + +```mojo +@value +struct Dog(Stringable, Representable, Writable): + var name: String + var age: Int + + fn __repr__(self) -> String: + return "Dog(name=" + repr(self.name) + ", age=" + repr(self.age) + ")" + + fn __str__(self) -> String: + return String.write(self) + + fn write_to[W: Writer](self, inout writer: W) -> None: + writer.write("Dog(", self.name, ", ", self.age, ")") + +var dog = Dog("Rex", 5) +print(repr(dog)) +print(dog) + +var dog_info = String("String: {!s}\nRepresentation: {!r}").format(dog, dog) +print(dog_info) +``` + +```output +Dog(name='Rex', age=5) +Dog(Rex, 5) +String: Dog(Rex, 5) +Representation: Dog(name='Rex', age=5) +``` + +### The `AnyType` trait + +When building a generic container type, one challenge is knowing how to dispose +of the contained items when the container is destroyed. Any type that +dynamically allocates memory needs to supply a +[destructor](/mojo/manual/lifecycle/death#destructor) (`__del__()` method) +that must be called to free the allocated memory. But not all types have a +destructor, and your Mojo code has no way to determine which is which. + +The [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType) trait solves this +issue: every trait implicitly inherits from `AnyType`, and all structs conform +to `AnyType`, which guarantees that the type has a destructor. For types that +don't have one, Mojo adds a no-op destructor. This means you can call the +destructor on any type. + +This makes it possible to build generic collections without leaking memory. When +the collection's destructor is called, it can safely call the destructors on +every item it contains. + +## Generic structs with traits + +You can also use traits when defining a generic container. A generic container +is a container (for example, an array or hashmap) that can hold different data +types. In a dynamic language like Python it's easy to add different types of +items to a container. But in a statically-typed environment the compiler needs +to be able to identify the types at compile time. For example, if the container +needs to copy a value, the compiler needs to verify that the type can be copied. + +The [`List`](/mojo/stdlib/collections/list) type is an example of a +generic container. A single `List` can only hold a single type of data. +For example, you can create a list of integer values like this: + +```mojo +from collections import List + +var list = List[Int](1, 2, 3) +for i in range(len(list)): + print(list[i], sep=" ", end="") +``` + +```output +1 2 3 +``` + +You can use traits to define requirements for elements that are stored in a +container. For example, `List` requires elements that can be moved and +copied. To store a struct in a `List`, the struct needs to conform to +the `CollectionElement` trait, which requires a +[copy constructor](/mojo/manual/lifecycle/life#copy-constructor) and a +[move constructor](/mojo/manual/lifecycle/life#move-constructor). + +Building generic containers is an advanced topic. For an introduction, see the +section on +[parameterized structs](/mojo/manual/parameters/#parameterized-structs). diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb deleted file mode 100644 index 1cd9762b51..0000000000 --- a/docs/manual/types.ipynb +++ /dev/null @@ -1,1124 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Types\n", - "sidebar_position: 1\n", - "description: Standard Mojo data types.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All values in Mojo have an associated data type. Most of the types are\n", - "_nominal_ types, defined by a [`struct`](/mojo/manual/structs). These types are\n", - "nominal (or \"named\") because type equality is determined by the type's _name_,\n", - "not its _structure_.\n", - "\n", - "There are a some types that aren't defined as structs:\n", - "\n", - "* Functions are typed based on their signatures.\n", - "* `NoneType` is a type with one instance, the `None` object, which is used to\n", - " signal \"no value.\"\n", - "\n", - "Mojo comes with a standard library that provides a number of useful types and\n", - "utility functions. These standard types aren’t privileged. Each of the standard\n", - "library types is defined just like user-defined types—even basic types like\n", - "[`Int`](/mojo/stdlib/builtin/int/Int) and\n", - "[`String`](/mojo/stdlib/collections/string/String). But these standard library\n", - "types are the building blocks you'll use for most Mojo programs.\n", - "\n", - "The most common types are _built-in types_, which are always available and\n", - "don't need to be imported. These include types for numeric values, strings,\n", - "boolean values, and others.\n", - "\n", - "The standard library also includes many more types that you can import as\n", - "needed, including collection types, utilities for interacting with the\n", - "filesystem and getting system information, and so on.\n", - "\n", - "## Numeric types\n", - "\n", - "Mojo's most basic numeric type is `Int`, which represents a signed integer of\n", - "the largest size supported by the system—typically 64 bits or 32 bits.\n", - "\n", - "Mojo also has built-in types for integer, unsigned integer, and floating-point \n", - "values of various precisions:\n", - "\n", - "
    \n", - "\n", - "| Type name | Description |\n", - "|---------------|--------------|\n", - "| `Int8` | 8-bit signed integer |\n", - "| `UInt8` | 8-bit unsigned integer |\n", - "| `Int16` | 16-bit signed integer |\n", - "| `UInt16` | 16-bit unsigned integer |\n", - "| `Int32` | 32-bit signed integer |\n", - "| `UInt32` | 32-bit unsigned integer |\n", - "| `Int64` | 64-bit signed integer |\n", - "| `UInt64` | 64-bit unsigned integer |\n", - "| `Float16` | 16-bit floating point number (IEEE 754-2008 binary16) |\n", - "| `Float32` | 32-bit floating point number (IEEE 754-2008 binary32) |\n", - "| `Float64` | 64-bit floating point number (IEEE 754-2008 binary64) |\n", - "
    Table 1. Numeric types with specific precision
    \n", - "
    \n", - "\n", - "The types in Table 1 are actually all aliases to a single type, \n", - "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD), which is discussed later.\n", - "\n", - "All of the numeric types support the usual numeric and bitwise operators. The \n", - "[`math`](/mojo/stdlib/math/) module provides a number of additional math\n", - "functions.\n", - "\n", - "You may wonder when to use `Int` and when to use the other integer \n", - "types. In general, `Int` is a good safe default when you need an integer type \n", - "and you don't require a specific bit width. Using `Int` as the default integer \n", - "type for APIs makes APIs more consistent and predictable.\n", - "\n", - "### Signed and unsigned integers\n", - "\n", - "Mojo supports both signed (`Int`) and unsigned (`UInt`) integers. You can use \n", - "the general `Int` or `UInt` types when you do not require a specific bit width.\n", - "Note that any alias to a fixed-precision type will be of type \n", - "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD).\n", - "\n", - "You might prefer to use unsigned integers over signed integers in conditions \n", - "where you don't need negative numbers, are not writing for a public API, or need \n", - "additional range.\n", - "\n", - "Mojo's `UInt` type represents an unsigned integer of the \n", - "[word size](https://en.wikipedia.org/wiki/Word_(computer_architecture)) of the \n", - "CPU, which is 64 bits on 64-bit CPUs and 32 bits on 32-bit CPUs. If you wish to \n", - "use a fixed size unsigned integer, you can use `UInt8`, `UInt16`, `UInt32`, or \n", - "`UInt64`, which are aliases to the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) \n", - "type. \n", - "\n", - "Signed and unsigned integers of the same bit width can represent the same number \n", - "of values, but have different ranges. For example, an `Int8` can represent 256 \n", - "values ranging from -128 to 127. A `UInt8` can also represent 256 values, but \n", - "represents a range of 0 to 255. \n", - "\n", - "Signed and unsigned integers also have different overflow behavior. When a \n", - "signed integer overflows outside the range of values that its type can \n", - "represent, the value overflows to negative numbers. For example, adding `1` to \n", - "`var si: Int8 = 127` results in `-128`. \n", - "\n", - "When an unsigned integer overflows outside the range of values that its type can \n", - "represent, the value overflows to zero. So, adding `1` to `var ui: UInt8 = 255` \n", - "is equal to `0`.\n", - "\n", - "### Floating-point numbers\n", - "\n", - "Floating-point types represent real numbers. Because not all real numbers can be\n", - "expressed in a finite number of bits, floating-point numbers can't represent\n", - "every value exactly.\n", - "\n", - "The floating-point types listed in Table 1—`Float64`, `Float32`, and \n", - "`Float16`—follow the IEEE 754-2008 standard for representing floating-point \n", - "values. Each type includes a sign bit, one set of bits representing an exponent,\n", - "and another set representing the fraction or mantissa. Table 2 shows how each of \n", - "these types are represented in memory.\n", - "\n", - "
    \n", - "\n", - "| Type name | Sign | Exponent | Mantissa |\n", - "|------------|-------|-----------|----------|\n", - "| `Float64` | 1 bit | 11 bits | 52 bits |\n", - "| `Float32` | 1 bit | 8 bits | 23 bits |\n", - "| `Float16` | 1 bit | 5 bits | 10 bits |\n", - "
    Table 2. Details of floating-point types
    \n", - "
    \n", - "\n", - "Numbers with exponent values of all ones or all zeros represent special values,\n", - "allowing floating-point numbers to represent infinity, negative infinity,\n", - "signed zeros, and not-a-number (NaN). For more details on how numbers are\n", - "represented, see [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) on\n", - "Wikipedia.\n", - "\n", - "A few things to note with floating-point values:\n", - "\n", - "- Rounding errors. Rounding may produce unexpected results. For example, 1/3\n", - " can't be represented exactly in these floating-point formats. The more\n", - " operations you perform with floating-point numbers, the more the rounding\n", - " errors accumulate. \n", - "\n", - "- Space between consecutive numbers. The space between consecutive numbers is\n", - " variable across the range of a floating-point number format. For numbers close\n", - " to zero, the distance between consecutive numbers is very small. For large\n", - " positive and negative numbers, the space between consecutive numbers is\n", - " greater than 1, so it may not be possible to represent consecutive integers.\n", - "\n", - "Because the values are approximate, it is rarely useful to compare them with\n", - "the equality operator (`==`). Consider the following example:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - } - ], - "source": [ - "var big_num = 1.0e16\n", - "var bigger_num = big_num+1.0\n", - "print(big_num == bigger_num)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Comparison operators (`<` `>=` and so on) work with floating point numbers. You\n", - "can also use the [`math.isclose()`](/mojo/stdlib/math/math/isclose) function to\n", - "compare whether two floating-point numbers are equal within a specified\n", - "tolerance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Numeric literals\n", - "\n", - "In addition to these numeric types, the standard libraries provides integer and\n", - "floating-point literal types, \n", - "[`IntLiteral`](/mojo/stdlib/builtin/int_literal/IntLiteral) and \n", - "[`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral).\n", - "\n", - "These literal types are used at compile time to represent literal numbers that\n", - "appear in the code. In general, you should never instantiate these types\n", - "yourself.\n", - "\n", - "Table 3 summarizes the literal formats you can use to represent numbers.\n", - "\n", - "
    \n", - "\n", - "| Format | Examples | Notes |\n", - "|--------|---------|-------|\n", - "| Integer literal | `1760` | Integer literal, in decimal format. |\n", - "| Hexadecimal literal | `0xaa`, `0xFF` | Integer literal, in hexadecimal format.
    Hex digits are case-insensitive. |\n", - "| Octal literal | `0o77` | Integer literal, in octal format. |\n", - "| Binary literal | `0b0111` | Integer literal, in binary format. |\n", - "| Floating-point literal | `3.14`, `1.2e9` | Floating-point literal.
    Must include the decimal point to be interpreted as floating-point. |\n", - "
    Table 3. Numeric literal formats
    \n", - "
    \n", - "\n", - "At compile time, the literal types are arbitrary-precision (also called \n", - "infinite-precision) values, so the compiler can perform compile-time \n", - "calculations without overflow or rounding errors.\n", - "\n", - "At runtime the values are converted to finite-precision types—`Int` for\n", - "integer values, and `Float64` for floating-point values. (This process of \n", - "converting a value that can only exist at compile time into a runtime value is\n", - "called _materialization_.) \n", - "\n", - "The following code sample shows the difference between an arbitrary-precision \n", - "calculation and the same calculation done using `Float64` values at runtime,\n", - "which suffers from rounding errors." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0 0.99999999999999978\n" - ] - } - ], - "source": [ - "var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0)\n", - "# use a variable to force the following calculation to occur at runtime\n", - "var three = 3.0\n", - "var finite_precision = three * (4.0 / three - 1.0)\n", - "print(arbitrary_precision, finite_precision)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### `SIMD` and `DType`\n", - "\n", - "To support high-performance numeric processing, Mojo uses the\n", - "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type as the basis for its numeric\n", - "types. SIMD (single instruction, multiple data) is a processor technology that\n", - "allows you to perform an operation on an entire set of operands at once. Mojo's\n", - "`SIMD` type abstracts SIMD operations. A `SIMD` value represents a SIMD\n", - "_vector_—that is, a fixed-size array of values that can fit into a processor's\n", - "register. SIMD vectors are defined by two\n", - "[_parameters_](/mojo/manual/parameters/):\n", - "\n", - "- A `DType` value, defining the data type in the vector (for example, \n", - " 32-bit floating-point numbers).\n", - "- The number of elements in the vector, which must be a power of two.\n", - "\n", - "For example, you can define a vector of four `Float32` values like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Math operations on SIMD values are \n", - "applied _elementwise_, on each individual element in the vector. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2, 6, 15, 28]\n" - ] - } - ], - "source": [ - "var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7)\n", - "var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4)\n", - "var product = vec1 * vec2\n", - "print(product)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "### Scalar values\n", - "\n", - "The `SIMD` module defines several _type aliases_ that are shorthand for\n", - "different types of `SIMD` vectors. In particular, the `Scalar` type is just a\n", - "`SIMD` vector with a single element. The numeric types listed in \n", - "[Table 1](#table-1), like `Int8` and `Float32` are actually type aliases for\n", - "different types of scalar values:\n", - "\n", - "```mojo\n", - "alias Scalar = SIMD[size=1]\n", - "alias Int8 = Scalar[DType.int8]\n", - "alias Float32 = Scalar[DType.float32]\n", - "```\n", - "\n", - "This may seem a little confusing at first, but it means that whether you're \n", - "working with a single `Float32` value or a vector of float32 values,\n", - "the math operations go through exactly the same code path." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### The `DType` type\n", - "\n", - "The `DType` struct describes the different data types that a `SIMD` vector can\n", - "hold, and defines a number of utility functions for operating on those data\n", - "types. The `DType` struct defines a set of aliases that act as identifiers for\n", - "the different data types, like `DType.int8` and `DType.float32`. You use\n", - "these aliases when declaring a `SIMD` vector:\n", - "\n", - "```mojo\n", - "var v: SIMD[DType.float64, 16]\n", - "```\n", - "\n", - "Note that `DType.float64` isn't a _type_, it's a value that describes a data\n", - "type. You can't create a variable with the type `DType.float64`. You can create\n", - "a variable with the type `SIMD[DType.float64, 1]` (or `Float64`, which is the\n", - "same thing).\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "float32 is floating point: True\n", - "float32 is integral: False\n", - "Min/max finite values for float32\n", - "-3.4028234663852886e+38 3.4028234663852886e+38\n" - ] - } - ], - "source": [ - "from utils.numerics import max_finite, min_finite\n", - "\n", - "def describeDType[dtype: DType]():\n", - " print(dtype, \"is floating point:\", dtype.is_floating_point())\n", - " print(dtype, \"is integral:\", dtype.is_integral())\n", - " print(\"Min/max finite values for\", dtype)\n", - " print(min_finite[dtype](), max_finite[dtype]())\n", - "\n", - "describeDType[DType.float32]()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "There are several other data types in the standard library that also use\n", - "the `DType` abstraction. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Strings\n", - "\n", - "Mojo's `String` type represents a mutable string. (For Python programmers, note\n", - "that this is different from Python's standard string, which is immutable.)\n", - "Strings support a variety of operators and common methods.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Testing Mojo strings\n" - ] - } - ], - "source": [ - "var s: String = \"Testing\"\n", - "s += \" Mojo strings\"\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Most standard library types conform to the \n", - "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", - "a type that can be converted to a string. Use `str(value)` to\n", - "explicitly convert a value to a string:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Items in list: 5\n" - ] - } - ], - "source": [ - "var s = str(\"Items in list: \") + str(5)\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### String literals\n", - "\n", - "As with numeric types, the standard library includes a string literal type used\n", - "to represent literal strings in the program source. String literals are\n", - "enclosed in either single or double quotes.\n", - "\n", - "Adjacent literals are concatenated together, so you can define a long string\n", - "using a series of literals broken up over several lines:\n", - "\n", - "```\n", - "var s = \"A very long string which is \"\n", - " \"broken into two literals for legibility.\"\n", - "```\n", - "\n", - "To define a multi-line string, enclose the literal in three single or double\n", - "quotes:\n", - "\n", - "```\n", - "var s = \"\"\"\n", - "Multi-line string literals let you \n", - "enter long blocks of text, including \n", - "newlines.\"\"\"\n", - "```\n", - "\n", - "Note that the triple double quote form is also used for API documentation\n", - "strings.\n", - "\n", - "Unlike `IntLiteral` and `FloatLiteral`, `StringLiteral` doesn't automatically\n", - "materialize to a runtime type. In some cases, you may need to manually convert\n", - "`StringLiteral` values to `String` using the built-in \n", - "[`str()`](/mojo/stdlib/builtin/str/str) method. " - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Variable is type `StringLiteral`\n", - "var s1 = \"Example\"\n", - "\n", - "# Variable is type `String`\n", - "var s2: String = \"Example\"\n", - "\n", - "# Variable is type `String`\n", - "var s3 = str(\"Example\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Booleans\n", - "\n", - "Mojo's `Bool` type represents a boolean value. It can take one of two values, \n", - "`True` or `False`. You can negate a boolean value using the `not` operator." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "False True\n" - ] - } - ], - "source": [ - "var conditionA = False\n", - "var conditionB: Bool\n", - "conditionB = not conditionA\n", - "print(conditionA, conditionB)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Many types have a boolean representation. Any type that implements the \n", - "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait has a boolean \n", - "representation. As a general principle, collections evaluate as True if they \n", - "contain any elements, False if they are empty; strings evaluate as True if they\n", - "have a non-zero length." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Tuples\n", - "\n", - "Mojo's `Tuple` type represents an immutable tuple consisting of zero or more \n", - "values, separated by commas. Tuples can consist of multiple types and you can \n", - "index into tuples in multiple ways. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 Example\n", - "Example\n" - ] - } - ], - "source": [ - "# Tuples are immutable and can hold multiple types\n", - "example_tuple = Tuple[Int, String](1, \"Example\")\n", - "\n", - "# Assign multiple variables at once\n", - "x, y = example_tuple\n", - "print(x, y)\n", - "\n", - "# Get individual values with an index\n", - "s = example_tuple.get[1, String]()\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also create a tuple without explicit typing. Note that if we declare the \n", - "same tuple from the previous example with implicit typing instead of explicit, \n", - "we must also convert `\"Example\"` from type `StringLiteral` to type `String`. " - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Example\n" - ] - } - ], - "source": [ - "example_tuple = (1, str(\"Example\"))\n", - "s = example_tuple.get[1, String]()\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When defining a function, you can explicitly declare the type of tuple elements \n", - "in one of two ways: " - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def return_tuple_1() -> Tuple[Int, Int]:\n", - " return Tuple[Int, Int](1, 1)\n", - "\n", - "def return_tuple_2() -> (Int, Int):\n", - " return (2, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Collection types\n", - "\n", - "The Mojo standard library also includes a set of basic collection types that\n", - "can be used to build more complex data structures:\n", - "\n", - "- [`List`](/mojo/stdlib/collections/list/List), a dynamically-sized array of \n", - " items.\n", - "- [`Dict`](/mojo/stdlib/collections/dict/Dict), an associative array of \n", - " key-value pairs.\n", - "- [`Set`](/mojo/stdlib/collections/set/Set), an unordered collection of unique\n", - " items.\n", - "- [`Optional`](/mojo/stdlib/collections/optional/Optional)\n", - " represents a value that may or may not be present. \n", - "\n", - "The collection types are _generic types_: while a given collection can only\n", - "hold a specific type of value (such as `Int` or `Float64`), you specify the\n", - "type at compile time using a [parameter](/mojo/manual/parameters/). For\n", - "example, you can create a `List` of `Int` values like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "var l = List[Int](1, 2, 3, 4)\n", - "# l.append(3.14) # error: FloatLiteral cannot be converted to Int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You don't always need to specify the type explicitly. If Mojo can _infer_ the\n", - "type, you can omit it. For example, when you construct a list from a set of \n", - "integer literals, Mojo creates a `List[Int]`." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Inferred type == Int\n", - "var l1 = List(1, 2, 3, 4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Where you need a more flexible collection, the \n", - "[`Variant`](/mojo/stdlib/utils/variant/Variant) type can hold different types \n", - "of values. For example, a `Variant[Int32, Float64]` can hold either an `Int32`\n", - "_or_ a `Float64` value at any given time. (Using `Variant` is not covered in\n", - "this section, see the [API docs](/mojo/stdlib/utils/variant/Variant) for more\n", - "information.)\n", - "\n", - "The following sections give brief introduction to the main collection types. \n", - "\n", - "### List\n", - "\n", - "[`List`](/mojo/stdlib/collections/list/List) is a dynamically-sized array of \n", - "elements. List elements need to conform to the \n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait, which\n", - "just means that the items must be copyable and movable. Most of the common\n", - "standard library primitives, like `Int`, `String`, and `SIMD` conform to this\n", - "trait. You can create a `List` by passing the element type as a parameter, like\n", - "this:\n", - "\n", - "\n", - "```mojo\n", - "var l = List[String]()\n", - "```\n", - "\n", - "The `List` type supports a subset of the Python `list` API, including the\n", - "ability to append to the list, pop items out of the list, and access list items\n", - "using subscript notation." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Popping last item from list: 11\n", - "2, 3, 5, 7, " - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "var list = List(2, 3, 5)\n", - "list.append(7)\n", - "list.append(11)\n", - "print(\"Popping last item from list: \", list.pop())\n", - "for idx in range(len(list)):\n", - " print(list[idx], end=\", \")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the previous code sample leaves out the type parameter when creating \n", - "the list. Because the list is being created with a set of `Int` values, Mojo can\n", - "_infer_ the type from the arguments. \n", - "\n", - "There are some notable limitations when using `List`:\n", - "\n", - "- You can't currently initialize a list from a list literal, like this:\n", - "\n", - " ```mojo\n", - " # Doesn't work!\n", - " var list: List[Int] = [2, 3, 5]\n", - " ```\n", - "\n", - " But you can use variadic arguments to achieve the same thing:\n", - "\n", - " ```mojo\n", - " var list = List(2, 3, 5)\n", - " ```\n", - "\n", - "- You can't `print()` a list, or convert it directly into a string.\n", - "\n", - " ```mojo\n", - " # Does not work\n", - " print(list)\n", - " ```\n", - "\n", - " As shown above, you can print the individual elements in a list as long as\n", - " they're a [`Stringable`](/mojo/stdlib/builtin/str/Stringable) type.\n", - "\n", - "- Iterating a `List` currently returns a \n", - " [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the\n", - " item itself. You can access the item using the dereference operator, `[]`:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2, 3, 4, " - ] - } - ], - "source": [ - "#: from collections import List\n", - "var list = List(2, 3, 4)\n", - "for item in list:\n", - " print(item[], end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - " Subscripting in to a list, however, returns the item directly—no need to \n", - " dereference:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2, 3, 4, " - ] - } - ], - "source": [ - "#: from collections import List\n", - "#: var list = List[Int](2, 3, 4)\n", - "for i in range(len(list)):\n", - " print(list[i], end=\", \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dict\n", - "\n", - "The [`Dict`](/mojo/stdlib/collections/dict/Dict) type is an associative array\n", - "that holds key-value pairs. You can create a `Dict` by specifying the key type\n", - "and value type as parameters, like this:\n", - "\n", - "```mojo\n", - "var values = Dict[String, Float64]()\n", - "```\n", - "\n", - "The dictionary's key type must conform to the \n", - "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, and value \n", - "elements must conform to the \n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", - "\n", - "You can insert and remove key-value pairs, update the value assigned to a key,\n", - "and iterate through keys, values, or items in the dictionary. \n", - "\n", - "The `Dict` iterators all yield references, so you need to use the dereference\n", - "operator `[]` as shown in the following example:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "plasticity 3.1000000000000001\n", - "elasticity 1.3\n", - "electricity 9.6999999999999993\n" - ] - } - ], - "source": [ - "from collections import Dict\n", - "\n", - "var d = Dict[String, Float64]()\n", - "d[\"plasticity\"] = 3.1\n", - "d[\"elasticity\"] = 1.3\n", - "d[\"electricity\"] = 9.7\n", - "for item in d.items():\n", - " print(item[].key, item[].value)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Set\n", - "\n", - "The [`Set`](/mojo/stdlib/collections/set/Set) type represents a set of unique\n", - "values. You can add and remove elements from the set, test whether a value \n", - "exists in the set, and perform set algebra operations, like unions and \n", - "intersections between two sets. \n", - "\n", - "Sets are generic and the element type must conform to the\n", - "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "We both like:\n", - "- ice cream\n", - "- tacos\n" - ] - } - ], - "source": [ - "from collections import Set\n", - "\n", - "i_like = Set(\"sushi\", \"ice cream\", \"tacos\", \"pho\")\n", - "you_like = Set(\"burgers\", \"tacos\", \"salad\", \"ice cream\")\n", - "we_like = i_like.intersection(you_like)\n", - "\n", - "print(\"We both like:\")\n", - "for item in we_like:\n", - " print(\"-\", item[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optional\n", - "\n", - "An [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", - "value that may or may not be present. Like the other collection types, it is\n", - "generic, and can hold any type that conforms to the\n", - "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Two ways to initialize an Optional with a value\n", - "var opt1 = Optional(5)\n", - "var opt2: Optional[Int] = 5\n", - "# Two ways to initialize an Optional with no value\n", - "var opt3 = Optional[Int]()\n", - "var opt4: Optional[Int] = None" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "An `Optional` evaluates as `True` when it holds a value, `False` otherwise. If\n", - "the `Optional` holds a value, you can retrieve a reference to the value using \n", - "the `value()` method. But calling `value()` on an `Optional` with no value\n", - "results in undefined behavior, so you should always guard a call to `value()`\n", - "inside a conditional that checks whether a value exists." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Testing\n" - ] - } - ], - "source": [ - "var opt: Optional[String] = str(\"Testing\")\n", - "if opt:\n", - " var value_ref = opt.value()\n", - " print(value_ref)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Alternately, you can use the `or_else()` method, which returns the stored\n", - "value if there is one, or a user-specified default value otherwise:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello\n", - "Hi\n" - ] - } - ], - "source": [ - "var custom_greeting: Optional[String] = None\n", - "print(custom_greeting.or_else(\"Hello\"))\n", - "\n", - "custom_greeting = str(\"Hi\")\n", - "print(custom_greeting.or_else(\"Hello\"))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Register-passable, memory-only, and trivial types\n", - "\n", - "In various places in the documentation you'll see references to \n", - "register-passable, memory-only, and trivial types. Register-passable and \n", - "memory-only types are distinguished based on how they hold data:\n", - "\n", - "- Register-passable types are composed exclusively of fixed-size data types,\n", - " which can (theoretically) be stored in a machine register. A register-passable\n", - " type can include other types, as long as they are also register-passable.\n", - " `Int`, `Bool`, and `SIMD`, for example, are all register-passable types. So \n", - " a register-passable `struct` could include `Int` and `Bool` fields, but not a\n", - " `String` field. Register-passable types are declared with the \n", - " [`@register_passable`](/mojo/manual/decorators/register-passable) decorator.\n", - "\n", - " Register-passable types are always passed by value (that is, the values are\n", - " copied).\n", - "\n", - "- Memory-only types consist of any types that _don't_ fit the description of\n", - " register-passable types. In particular, these types usually have pointers or \n", - " references to dynamically-allocated memory. `String`, `List`, and `Dict` are\n", - " all examples of memory-only types.\n", - "\n", - "Our long-term goal is to make this distinction transparent to the user, and\n", - "ensure all APIs work with both register-passable and memory-only types.\n", - "But right now you will see some standard library types that only work with \n", - "register-passable types or only work with memory-only types.\n", - "\n", - "In addition to these two categories, Mojo also has \"trivial\" types. Conceptually\n", - "a trivial type is simply a type that doesn't require any custom logic in its\n", - "lifecycle methods. The bits that make up an instance of a trivial type can be\n", - "copied or moved without any knowledge of what they do. Currently, trivial types\n", - "are declared using the\n", - "[`@register_passable(trivial)`](/mojo/manual/decorators/register-passable#register_passabletrivial)\n", - "decorator. Trivial types shouldn't be limited to only register-passable types,\n", - "so in the future we intend to separate trivial types from the \n", - "`@register_passable` decorator." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `AnyType` and `AnyTrivialRegType`\n", - "\n", - "Two other things you'll see in Mojo APIs are references to `AnyType` and\n", - "`AnyTrivialRegType`. These are effectively _metatypes_, that is, types of types.\n", - "\n", - "- `AnyType` represents any Mojo type. Mojo treats `AnyType` as a special kind of\n", - " trait, and you'll find more discussion of it on the\n", - " [Traits page](/mojo/manual/traits#the-anytype-trait).\n", - "- `AnyTrivialRegType` is a metatype representing any Mojo type that's marked \n", - " register passable.\n", - "\n", - "You'll see them in signatures like this:\n", - "\n", - "```mojo\n", - "fn any_type_function[ValueType: AnyTrivialRegType](value: ValueType):\n", - " ...\n", - "```\n", - "\n", - "You can read this as `any_type_function` has an argument, `value` of type\n", - "`ValueType`, where `ValueType` is a register-passable type, determined at\n", - "compile time. \n", - "\n", - "There is still some code like this in the standard library, but it's gradually\n", - "being migrated to more generic code that doesn't distinguish between \n", - "register-passable and memory-only types.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/types.mdx b/docs/manual/types.mdx new file mode 100644 index 0000000000..4f276c82f9 --- /dev/null +++ b/docs/manual/types.mdx @@ -0,0 +1,763 @@ +--- +title: Types +sidebar_position: 1 +description: Standard Mojo data types. +--- + +All values in Mojo have an associated data type. Most of the types are +*nominal* types, defined by a [`struct`](/mojo/manual/structs). These types are +nominal (or "named") because type equality is determined by the type's *name*, +not its *structure*. + +There are a some types that aren't defined as structs: + +* Functions are typed based on their signatures. +* `NoneType` is a type with one instance, the `None` object, which is used to + signal "no value." + +Mojo comes with a standard library that provides a number of useful types and +utility functions. These standard types aren’t privileged. Each of the standard +library types is defined just like user-defined types—even basic types like +[`Int`](/mojo/stdlib/builtin/int/Int) and +[`String`](/mojo/stdlib/collections/string/String). But these standard library +types are the building blocks you'll use for most Mojo programs. + +The most common types are *built-in types*, which are always available and +don't need to be imported. These include types for numeric values, strings, +boolean values, and others. + +The standard library also includes many more types that you can import as +needed, including collection types, utilities for interacting with the +filesystem and getting system information, and so on. + +## Numeric types + +Mojo's most basic numeric type is `Int`, which represents a signed integer of +the largest size supported by the system—typically 64 bits or 32 bits. + +Mojo also has built-in types for integer, unsigned integer, and floating-point +values of various precisions: + +
    + +| Type name | Description | +| --------- | ----------------------------------------------------- | +| `Int8` | 8-bit signed integer | +| `UInt8` | 8-bit unsigned integer | +| `Int16` | 16-bit signed integer | +| `UInt16` | 16-bit unsigned integer | +| `Int32` | 32-bit signed integer | +| `UInt32` | 32-bit unsigned integer | +| `Int64` | 64-bit signed integer | +| `UInt64` | 64-bit unsigned integer | +| `Float16` | 16-bit floating point number (IEEE 754-2008 binary16) | +| `Float32` | 32-bit floating point number (IEEE 754-2008 binary32) | +| `Float64` | 64-bit floating point number (IEEE 754-2008 binary64) | + +
    Table 1. Numeric types with specific precision
    +
    + +The types in Table 1 are actually all aliases to a single type, +[`SIMD`](/mojo/stdlib/builtin/simd/SIMD), which is discussed later. + +All of the numeric types support the usual numeric and bitwise operators. The +[`math`](/mojo/stdlib/math/) module provides a number of additional math +functions. + +You may wonder when to use `Int` and when to use the other integer +types. In general, `Int` is a good safe default when you need an integer type +and you don't require a specific bit width. Using `Int` as the default integer +type for APIs makes APIs more consistent and predictable. + +### Signed and unsigned integers + +Mojo supports both signed (`Int`) and unsigned (`UInt`) integers. You can use +the general `Int` or `UInt` types when you do not require a specific bit width. +Note that any alias to a fixed-precision type will be of type +[`SIMD`](/mojo/stdlib/builtin/simd/SIMD). + +You might prefer to use unsigned integers over signed integers in conditions +where you don't need negative numbers, are not writing for a public API, or need +additional range. + +Mojo's `UInt` type represents an unsigned integer of the +[word size](https://en.wikipedia.org/wiki/Word_\(computer_architecture\)) of the +CPU, which is 64 bits on 64-bit CPUs and 32 bits on 32-bit CPUs. If you wish to +use a fixed size unsigned integer, you can use `UInt8`, `UInt16`, `UInt32`, or +`UInt64`, which are aliases to the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) +type. + +Signed and unsigned integers of the same bit width can represent the same number +of values, but have different ranges. For example, an `Int8` can represent 256 +values ranging from -128 to 127. A `UInt8` can also represent 256 values, but +represents a range of 0 to 255. + +Signed and unsigned integers also have different overflow behavior. When a +signed integer overflows outside the range of values that its type can +represent, the value overflows to negative numbers. For example, adding `1` to +`var si: Int8 = 127` results in `-128`. + +When an unsigned integer overflows outside the range of values that its type can +represent, the value overflows to zero. So, adding `1` to `var ui: UInt8 = 255` +is equal to `0`. + +### Floating-point numbers + +Floating-point types represent real numbers. Because not all real numbers can be +expressed in a finite number of bits, floating-point numbers can't represent +every value exactly. + +The floating-point types listed in Table 1—`Float64`, `Float32`, and +`Float16`—follow the IEEE 754-2008 standard for representing floating-point +values. Each type includes a sign bit, one set of bits representing an exponent, +and another set representing the fraction or mantissa. Table 2 shows how each of +these types are represented in memory. + +
    + +| Type name | Sign | Exponent | Mantissa | +| --------- | ----- | -------- | -------- | +| `Float64` | 1 bit | 11 bits | 52 bits | +| `Float32` | 1 bit | 8 bits | 23 bits | +| `Float16` | 1 bit | 5 bits | 10 bits | + +
    Table 2. Details of floating-point types
    +
    + +Numbers with exponent values of all ones or all zeros represent special values, +allowing floating-point numbers to represent infinity, negative infinity, +signed zeros, and not-a-number (NaN). For more details on how numbers are +represented, see [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) on +Wikipedia. + +A few things to note with floating-point values: + +* Rounding errors. Rounding may produce unexpected results. For example, 1/3 + can't be represented exactly in these floating-point formats. The more + operations you perform with floating-point numbers, the more the rounding + errors accumulate. + +* Space between consecutive numbers. The space between consecutive numbers is + variable across the range of a floating-point number format. For numbers close + to zero, the distance between consecutive numbers is very small. For large + positive and negative numbers, the space between consecutive numbers is + greater than 1, so it may not be possible to represent consecutive integers. + +Because the values are approximate, it is rarely useful to compare them with +the equality operator (`==`). Consider the following example: + +```mojo +var big_num = 1.0e16 +var bigger_num = big_num+1.0 +print(big_num == bigger_num) +``` + +```output +True +``` + +Comparison operators (`<` `>=` and so on) work with floating point numbers. You +can also use the [`math.isclose()`](/mojo/stdlib/math/math/isclose) function to +compare whether two floating-point numbers are equal within a specified +tolerance. + +### Numeric literals + +In addition to these numeric types, the standard libraries provides integer and +floating-point literal types, +[`IntLiteral`](/mojo/stdlib/builtin/int_literal/IntLiteral) and +[`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral). + +These literal types are used at compile time to represent literal numbers that +appear in the code. In general, you should never instantiate these types +yourself. + +Table 3 summarizes the literal formats you can use to represent numbers. + +
    + +| Format | Examples | Notes | +| ---------------------- | --------------- | ------------------------------------------------------------------------------------------------ | +| Integer literal | `1760` | Integer literal, in decimal format. | +| Hexadecimal literal | `0xaa`, `0xFF` | Integer literal, in hexadecimal format.
    Hex digits are case-insensitive. | +| Octal literal | `0o77` | Integer literal, in octal format. | +| Binary literal | `0b0111` | Integer literal, in binary format. | +| Floating-point literal | `3.14`, `1.2e9` | Floating-point literal.
    Must include the decimal point to be interpreted as floating-point. | + +
    Table 3. Numeric literal formats
    +
    + +At compile time, the literal types are arbitrary-precision (also called +infinite-precision) values, so the compiler can perform compile-time +calculations without overflow or rounding errors. + +At runtime the values are converted to finite-precision types—`Int` for +integer values, and `Float64` for floating-point values. (This process of +converting a value that can only exist at compile time into a runtime value is +called *materialization*.) + +The following code sample shows the difference between an arbitrary-precision +calculation and the same calculation done using `Float64` values at runtime, +which suffers from rounding errors. + +```mojo +var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0) +# use a variable to force the following calculation to occur at runtime +var three = 3.0 +var finite_precision = three * (4.0 / three - 1.0) +print(arbitrary_precision, finite_precision) +``` + +```output +1.0 0.99999999999999978 +``` + +### `SIMD` and `DType` + +To support high-performance numeric processing, Mojo uses the +[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type as the basis for its numeric +types. SIMD (single instruction, multiple data) is a processor technology that +allows you to perform an operation on an entire set of operands at once. Mojo's +`SIMD` type abstracts SIMD operations. A `SIMD` value represents a SIMD +*vector*—that is, a fixed-size array of values that can fit into a processor's +register. SIMD vectors are defined by two +[*parameters*](/mojo/manual/parameters/): + +* A `DType` value, defining the data type in the vector (for example, + 32-bit floating-point numbers). +* The number of elements in the vector, which must be a power of two. + +For example, you can define a vector of four `Float32` values like this: + +```mojo +var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0) +``` + +Math operations on SIMD values are +applied *elementwise*, on each individual element in the vector. For example: + +```mojo +var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7) +var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4) +var product = vec1 * vec2 +print(product) +``` + +```output +[2, 6, 15, 28] +``` + +### Scalar values + +The `SIMD` module defines several *type aliases* that are shorthand for +different types of `SIMD` vectors. In particular, the `Scalar` type is just a +`SIMD` vector with a single element. The numeric types listed in +[Table 1](#table-1), like `Int8` and `Float32` are actually type aliases for +different types of scalar values: + +```mojo +alias Scalar = SIMD[size=1] +alias Int8 = Scalar[DType.int8] +alias Float32 = Scalar[DType.float32] +``` + +This may seem a little confusing at first, but it means that whether you're +working with a single `Float32` value or a vector of float32 values, +the math operations go through exactly the same code path. + +#### The `DType` type + +The `DType` struct describes the different data types that a `SIMD` vector can +hold, and defines a number of utility functions for operating on those data +types. The `DType` struct defines a set of aliases that act as identifiers for +the different data types, like `DType.int8` and `DType.float32`. You use +these aliases when declaring a `SIMD` vector: + +```mojo +var v: SIMD[DType.float64, 16] +``` + +Note that `DType.float64` isn't a *type*, it's a value that describes a data +type. You can't create a variable with the type `DType.float64`. You can create +a variable with the type `SIMD[DType.float64, 1]` (or `Float64`, which is the +same thing). + +```mojo +from utils.numerics import max_finite, min_finite + +def describeDType[dtype: DType](): + print(dtype, "is floating point:", dtype.is_floating_point()) + print(dtype, "is integral:", dtype.is_integral()) + print("Min/max finite values for", dtype) + print(min_finite[dtype](), max_finite[dtype]()) + +describeDType[DType.float32]() +``` + +```output +float32 is floating point: True +float32 is integral: False +Min/max finite values for float32 +-3.4028234663852886e+38 3.4028234663852886e+38 +``` + +There are several other data types in the standard library that also use +the `DType` abstraction. + +## Strings + +Mojo's `String` type represents a mutable string. (For Python programmers, note +that this is different from Python's standard string, which is immutable.) +Strings support a variety of operators and common methods. + +```mojo +var s: String = "Testing" +s += " Mojo strings" +print(s) +``` + +```output +Testing Mojo strings +``` + +Most standard library types conform to the +[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents +a type that can be converted to a string. Use `str(value)` to +explicitly convert a value to a string: + +```mojo +var s = str("Items in list: ") + str(5) +print(s) +``` + +```output +Items in list: 5 +``` + +### String literals + +As with numeric types, the standard library includes a string literal type used +to represent literal strings in the program source. String literals are +enclosed in either single or double quotes. + +Adjacent literals are concatenated together, so you can define a long string +using a series of literals broken up over several lines: + +``` +var s = "A very long string which is " + "broken into two literals for legibility." +``` + +To define a multi-line string, enclose the literal in three single or double +quotes: + +``` +var s = """ +Multi-line string literals let you +enter long blocks of text, including +newlines.""" +``` + +Note that the triple double quote form is also used for API documentation +strings. + +Unlike `IntLiteral` and `FloatLiteral`, `StringLiteral` doesn't automatically +materialize to a runtime type. In some cases, you may need to manually convert +`StringLiteral` values to `String` using the built-in +[`str()`](/mojo/stdlib/builtin/str/str) method. + +```mojo +# Variable is type `StringLiteral` +var s1 = "Example" + +# Variable is type `String` +var s2: String = "Example" + +# Variable is type `String` +var s3 = str("Example") +``` + +## Booleans + +Mojo's `Bool` type represents a boolean value. It can take one of two values, +`True` or `False`. You can negate a boolean value using the `not` operator. + +```mojo +var conditionA = False +var conditionB: Bool +conditionB = not conditionA +print(conditionA, conditionB) +``` + +```output +False True +``` + +Many types have a boolean representation. Any type that implements the +[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait has a boolean +representation. As a general principle, collections evaluate as True if they +contain any elements, False if they are empty; strings evaluate as True if they +have a non-zero length. + +## Tuples + +Mojo's `Tuple` type represents an immutable tuple consisting of zero or more +values, separated by commas. Tuples can consist of multiple types and you can +index into tuples in multiple ways. + +```mojo +# Tuples are immutable and can hold multiple types +example_tuple = Tuple[Int, String](1, "Example") + +# Assign multiple variables at once +x, y = example_tuple +print(x, y) + +# Get individual values with an index +s = example_tuple.get[1, String]() +print(s) +``` + +```output +1 Example +Example +``` + +You can also create a tuple without explicit typing. Note that if we declare the +same tuple from the previous example with implicit typing instead of explicit, +we must also convert `"Example"` from type `StringLiteral` to type `String`. + +```mojo +example_tuple = (1, str("Example")) +s = example_tuple.get[1, String]() +print(s) +``` + +```output +Example +``` + +When defining a function, you can explicitly declare the type of tuple elements +in one of two ways: + +```mojo +def return_tuple_1() -> Tuple[Int, Int]: + return Tuple[Int, Int](1, 1) + +def return_tuple_2() -> (Int, Int): + return (2, 2) +``` + +## Collection types + +The Mojo standard library also includes a set of basic collection types that +can be used to build more complex data structures: + +* [`List`](/mojo/stdlib/collections/list/List), a dynamically-sized array of + items. +* [`Dict`](/mojo/stdlib/collections/dict/Dict), an associative array of + key-value pairs. +* [`Set`](/mojo/stdlib/collections/set/Set), an unordered collection of unique + items. +* [`Optional`](/mojo/stdlib/collections/optional/Optional) + represents a value that may or may not be present. + +The collection types are *generic types*: while a given collection can only +hold a specific type of value (such as `Int` or `Float64`), you specify the +type at compile time using a [parameter](/mojo/manual/parameters/). For +example, you can create a `List` of `Int` values like this: + +```mojo +var l = List[Int](1, 2, 3, 4) +# l.append(3.14) # error: FloatLiteral cannot be converted to Int +``` + +You don't always need to specify the type explicitly. If Mojo can *infer* the +type, you can omit it. For example, when you construct a list from a set of +integer literals, Mojo creates a `List[Int]`. + +```mojo +# Inferred type == Int +var l1 = List(1, 2, 3, 4) +``` + +Where you need a more flexible collection, the +[`Variant`](/mojo/stdlib/utils/variant/Variant) type can hold different types +of values. For example, a `Variant[Int32, Float64]` can hold either an `Int32` +*or* a `Float64` value at any given time. (Using `Variant` is not covered in +this section, see the [API docs](/mojo/stdlib/utils/variant/Variant) for more +information.) + +The following sections give brief introduction to the main collection types. + +### List + +[`List`](/mojo/stdlib/collections/list/List) is a dynamically-sized array of +elements. List elements need to conform to the +[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait, which +just means that the items must be copyable and movable. Most of the common +standard library primitives, like `Int`, `String`, and `SIMD` conform to this +trait. You can create a `List` by passing the element type as a parameter, like +this: + +```mojo +var l = List[String]() +``` + +The `List` type supports a subset of the Python `list` API, including the +ability to append to the list, pop items out of the list, and access list items +using subscript notation. + +```mojo +from collections import List + +var list = List(2, 3, 5) +list.append(7) +list.append(11) +print("Popping last item from list: ", list.pop()) +for idx in range(len(list)): + print(list[idx], end=", ") + +``` + +```output +Popping last item from list: 11 +2, 3, 5, 7, +``` + +Note that the previous code sample leaves out the type parameter when creating +the list. Because the list is being created with a set of `Int` values, Mojo can +*infer* the type from the arguments. + +There are some notable limitations when using `List`: + +* You can't currently initialize a list from a list literal, like this: + + ```mojo + # Doesn't work! + var list: List[Int] = [2, 3, 5] + ``` + + But you can use variadic arguments to achieve the same thing: + + ```mojo + var list = List(2, 3, 5) + ``` + +* You can't `print()` a list, or convert it directly into a string. + + ```mojo + # Does not work + print(list) + ``` + + As shown above, you can print the individual elements in a list as long as + they're a [`Stringable`](/mojo/stdlib/builtin/str/Stringable) type. + +* Iterating a `List` currently returns a + [`Reference`](/mojo/stdlib/memory/reference/Reference) to each item, not the + item itself. You can access the item using the dereference operator, `[]`: + +```mojo +#: from collections import List +var list = List(2, 3, 4) +for item in list: + print(item[], end=", ") +``` + +```output +2, 3, 4, +``` + +Subscripting in to a list, however, returns the item directly—no need to +dereference: + +```mojo +#: from collections import List +#: var list = List[Int](2, 3, 4) +for i in range(len(list)): + print(list[i], end=", ") +``` + +```output +2, 3, 4, +``` + +### Dict + +The [`Dict`](/mojo/stdlib/collections/dict/Dict) type is an associative array +that holds key-value pairs. You can create a `Dict` by specifying the key type +and value type as parameters, like this: + +```mojo +var values = Dict[String, Float64]() +``` + +The dictionary's key type must conform to the +[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, and value +elements must conform to the +[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. + +You can insert and remove key-value pairs, update the value assigned to a key, +and iterate through keys, values, or items in the dictionary. + +The `Dict` iterators all yield references, so you need to use the dereference +operator `[]` as shown in the following example: + +```mojo +from collections import Dict + +var d = Dict[String, Float64]() +d["plasticity"] = 3.1 +d["elasticity"] = 1.3 +d["electricity"] = 9.7 +for item in d.items(): + print(item[].key, item[].value) +``` + +```output +plasticity 3.1000000000000001 +elasticity 1.3 +electricity 9.6999999999999993 +``` + +### Set + +The [`Set`](/mojo/stdlib/collections/set/Set) type represents a set of unique +values. You can add and remove elements from the set, test whether a value +exists in the set, and perform set algebra operations, like unions and +intersections between two sets. + +Sets are generic and the element type must conform to the +[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait. + +```mojo +from collections import Set + +i_like = Set("sushi", "ice cream", "tacos", "pho") +you_like = Set("burgers", "tacos", "salad", "ice cream") +we_like = i_like.intersection(you_like) + +print("We both like:") +for item in we_like: + print("-", item[]) +``` + +```output +We both like: +- ice cream +- tacos +``` + +### Optional + +An [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a +value that may or may not be present. Like the other collection types, it is +generic, and can hold any type that conforms to the +[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. + +```mojo +# Two ways to initialize an Optional with a value +var opt1 = Optional(5) +var opt2: Optional[Int] = 5 +# Two ways to initialize an Optional with no value +var opt3 = Optional[Int]() +var opt4: Optional[Int] = None +``` + +An `Optional` evaluates as `True` when it holds a value, `False` otherwise. If +the `Optional` holds a value, you can retrieve a reference to the value using +the `value()` method. But calling `value()` on an `Optional` with no value +results in undefined behavior, so you should always guard a call to `value()` +inside a conditional that checks whether a value exists. + +```mojo +var opt: Optional[String] = str("Testing") +if opt: + var value_ref = opt.value() + print(value_ref) +``` + +```output +Testing +``` + +Alternately, you can use the `or_else()` method, which returns the stored +value if there is one, or a user-specified default value otherwise: + +```mojo +var custom_greeting: Optional[String] = None +print(custom_greeting.or_else("Hello")) + +custom_greeting = str("Hi") +print(custom_greeting.or_else("Hello")) + +``` + +```output +Hello +Hi +``` + +## Register-passable, memory-only, and trivial types + +In various places in the documentation you'll see references to +register-passable, memory-only, and trivial types. Register-passable and +memory-only types are distinguished based on how they hold data: + +* Register-passable types are composed exclusively of fixed-size data types, + which can (theoretically) be stored in a machine register. A register-passable + type can include other types, as long as they are also register-passable. + `Int`, `Bool`, and `SIMD`, for example, are all register-passable types. So + a register-passable `struct` could include `Int` and `Bool` fields, but not a + `String` field. Register-passable types are declared with the + [`@register_passable`](/mojo/manual/decorators/register-passable) decorator. + + Register-passable types are always passed by value (that is, the values are + copied). + +* Memory-only types consist of any types that *don't* fit the description of + register-passable types. In particular, these types usually have pointers or + references to dynamically-allocated memory. `String`, `List`, and `Dict` are + all examples of memory-only types. + +Our long-term goal is to make this distinction transparent to the user, and +ensure all APIs work with both register-passable and memory-only types. +But right now you will see some standard library types that only work with +register-passable types or only work with memory-only types. + +In addition to these two categories, Mojo also has "trivial" types. Conceptually +a trivial type is simply a type that doesn't require any custom logic in its +lifecycle methods. The bits that make up an instance of a trivial type can be +copied or moved without any knowledge of what they do. Currently, trivial types +are declared using the +[`@register_passable(trivial)`](/mojo/manual/decorators/register-passable#register_passabletrivial) +decorator. Trivial types shouldn't be limited to only register-passable types, +so in the future we intend to separate trivial types from the +`@register_passable` decorator. + +## `AnyType` and `AnyTrivialRegType` + +Two other things you'll see in Mojo APIs are references to `AnyType` and +`AnyTrivialRegType`. These are effectively *metatypes*, that is, types of types. + +* `AnyType` represents any Mojo type. Mojo treats `AnyType` as a special kind of + trait, and you'll find more discussion of it on the + [Traits page](/mojo/manual/traits#the-anytype-trait). +* `AnyTrivialRegType` is a metatype representing any Mojo type that's marked + register passable. + +You'll see them in signatures like this: + +```mojo +fn any_type_function[ValueType: AnyTrivialRegType](value: ValueType): + ... +``` + +You can read this as `any_type_function` has an argument, `value` of type +`ValueType`, where `ValueType` is a register-passable type, determined at +compile time. + +There is still some code like this in the standard library, but it's gradually +being migrated to more generic code that doesn't distinguish between +register-passable and memory-only types. diff --git a/docs/manual/values/index.ipynb b/docs/manual/values/index.ipynb deleted file mode 100644 index 0c6e34c116..0000000000 --- a/docs/manual/values/index.ipynb +++ /dev/null @@ -1,158 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Intro to value ownership\n", - "sidebar_position: 1\n", - "description: Introduction to Mojo value ownership.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A program is nothing without data, and all modern programming languages store\n", - "data in one of two places: the call stack and the heap (also sometimes in CPU\n", - "registers, but we won't get into that here). However, each language reads and\n", - "writes data a bit differently—sometimes very differently. So in the following\n", - "sections, we'll explain how Mojo manages memory in your programs and how this\n", - "affects the way you write Mojo code.\n", - "\n", - ":::note\n", - "\n", - "For an alternate introduction to ownership in Mojo, check out our two-part blog\n", - "post: \n", - "[What ownership is really about: a mental model approach](https://www.modular.com/blog/what-ownership-is-really-about-a-mental-model-approach), and [Deep dive into\n", - "ownership in Mojo](https://www.modular.com/blog/deep-dive-into-ownership-in-mojo).\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stack and heap overview\n", - "\n", - "In general, all modern programming languages divide a running program's memory\n", - "into four segments:\n", - "\n", - "- Text. The compiled program.\n", - "- Data. Global data, either initialized or uninitialized.\n", - "- Stack. Local data, automatically managed during the program's runtime.\n", - "- Heap. Dynamically-allocated data, managed by the programmer.\n", - "\n", - "The text and data segments are statically sized, but the stack and heap change\n", - "size as the program runs.\n", - "\n", - "The _stack_ stores data local to the current function. When a function is\n", - "called, the program allocates a block of memory—a _stack frame_—that is exactly\n", - "the size required to store the function's data, including any _fixed-size_\n", - "local variables. When another function is called, a new stack frame is pushed\n", - "onto the top of the stack. When a function is done, its stack frame is popped\n", - "off the stack. \n", - "\n", - "Notice that we said only \"_fixed-size_ local values\" are stored in the stack.\n", - "Dynamically-sized values that can change in size at runtime are instead\n", - "stored in the heap, which is a much larger region of memory that allows for\n", - "dynamic memory allocation. Technically, a local variable for such a value\n", - "is still stored in the call stack, but its value is a fixed-size pointer to the\n", - "real value on the heap. Consider a Mojo string: it can be any length, and \n", - "its length can change at runtime. So the Mojo `String` struct includes some statically-sized fields, plus a pointer to a dynamically-allocated buffer\n", - "holding the actual string data.\n", - "\n", - "Another important difference between the heap and the stack is that the stack is \n", - "managed automatically—the code to push and pop stack frames is added by the\n", - "compiler. Heap memory, on the other hand, is managed by the programmer\n", - "explicitly allocating and deallocating memory. You may do this indirectly—by\n", - "using standard library types like `List` and `String`—or directly, using the \n", - "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) API.\n", - "\n", - "Values that need to outlive the lifetime of a function (such as\n", - "an array that's passed between functions and should not be copied) are stored\n", - "in the heap, because heap memory is accessible from anywhere in the call stack,\n", - "even after the function that created it is removed from the stack. This sort of\n", - "situation—in which a heap-allocated value is used by multiple functions—is where\n", - "most memory errors occur, and it's where memory management strategies vary the\n", - "most between programming languages." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Memory management strategies\n", - "\n", - "Because memory is limited, it's important that programs remove unused data from\n", - "the heap (\"free\" the memory) as quickly as possible. Figuring out when to free\n", - "that memory is pretty complicated.\n", - "\n", - "Some programming languages try to hide the complexities of memory management\n", - "from you by utilizing a \"garbage collector\" process that tracks all memory\n", - "usage and deallocates unused heap memory periodically (also known as automatic\n", - "memory management). A significant benefit of this method is that it relieves\n", - "developers from the burden of manual memory management, generally avoiding more\n", - "errors and making developers more productive. However, it incurs a performance\n", - "cost because the garbage collector interrupts the program's execution, and it\n", - "might not reclaim memory very quickly.\n", - "\n", - "Other languages require that you manually free data that's allocated on the\n", - "heap. When done properly, this makes programs execute quickly, because there's\n", - "no processing time consumed by a garbage collector. However, the challenge with\n", - "this approach is that programmers make mistakes, especially when multiple parts\n", - "of the program need access to the same memory—it becomes difficult to know\n", - "which part of the program \"owns\" the data and must deallocate it. Programmers\n", - "might accidentally deallocate data before the program is done with it (causing\n", - "\"use-after-free\" errors), or they might deallocate it twice (\"double free\"\n", - "errors), or they might never deallocate it (\"leaked memory\" errors). Mistakes\n", - "like these and others can have catastrophic results for the program, and these\n", - "bugs are often hard to track down, making it especially important that they\n", - "don't occur in the first place.\n", - "\n", - "Mojo uses a third approach called \"ownership\" that relies on a collection of\n", - "rules that programmers must follow when passing values. The rules ensure there\n", - "is only one \"owner\" for a given value at a time. When a value's lifetime ends,\n", - "Mojo calls its destructor, which is responsible for deallocating any heap memory\n", - "that needs to be deallocated.\n", - "\n", - "In this way, Mojo helps ensure memory is freed, but it does so in a way that's\n", - "deterministic and safe from errors such as use-after-free, double-free and\n", - "memory leaks. Plus, it does so with a very low performance overhead.\n", - "\n", - "Mojo's value ownership model provides an excellent balance of programming\n", - "productivity and strong memory safety. It only requires that you learn some new\n", - "syntax and a few rules about how to share access to memory within your program.\n", - "\n", - "But before we explain the rules and syntax for Mojo's value ownership model,\n", - "you first need to understand [value\n", - "semantics](/mojo/manual/values/value-semantics)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/values/index.mdx b/docs/manual/values/index.mdx new file mode 100644 index 0000000000..21f04e22db --- /dev/null +++ b/docs/manual/values/index.mdx @@ -0,0 +1,111 @@ +--- +title: Intro to value ownership +sidebar_position: 1 +description: Introduction to Mojo value ownership. +--- + +A program is nothing without data, and all modern programming languages store +data in one of two places: the call stack and the heap (also sometimes in CPU +registers, but we won't get into that here). However, each language reads and +writes data a bit differently—sometimes very differently. So in the following +sections, we'll explain how Mojo manages memory in your programs and how this +affects the way you write Mojo code. + +:::note + +For an alternate introduction to ownership in Mojo, check out our two-part blog +post: +[What ownership is really about: a mental model approach](https://www.modular.com/blog/what-ownership-is-really-about-a-mental-model-approach), and [Deep dive into +ownership in Mojo](https://www.modular.com/blog/deep-dive-into-ownership-in-mojo). + +::: + +## Stack and heap overview + +In general, all modern programming languages divide a running program's memory +into four segments: + +* Text. The compiled program. +* Data. Global data, either initialized or uninitialized. +* Stack. Local data, automatically managed during the program's runtime. +* Heap. Dynamically-allocated data, managed by the programmer. + +The text and data segments are statically sized, but the stack and heap change +size as the program runs. + +The *stack* stores data local to the current function. When a function is +called, the program allocates a block of memory—a *stack frame*—that is exactly +the size required to store the function's data, including any *fixed-size* +local variables. When another function is called, a new stack frame is pushed +onto the top of the stack. When a function is done, its stack frame is popped +off the stack. + +Notice that we said only "*fixed-size* local values" are stored in the stack. +Dynamically-sized values that can change in size at runtime are instead +stored in the heap, which is a much larger region of memory that allows for +dynamic memory allocation. Technically, a local variable for such a value +is still stored in the call stack, but its value is a fixed-size pointer to the +real value on the heap. Consider a Mojo string: it can be any length, and +its length can change at runtime. So the Mojo `String` struct includes some statically-sized fields, plus a pointer to a dynamically-allocated buffer +holding the actual string data. + +Another important difference between the heap and the stack is that the stack is +managed automatically—the code to push and pop stack frames is added by the +compiler. Heap memory, on the other hand, is managed by the programmer +explicitly allocating and deallocating memory. You may do this indirectly—by +using standard library types like `List` and `String`—or directly, using the +[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) API. + +Values that need to outlive the lifetime of a function (such as +an array that's passed between functions and should not be copied) are stored +in the heap, because heap memory is accessible from anywhere in the call stack, +even after the function that created it is removed from the stack. This sort of +situation—in which a heap-allocated value is used by multiple functions—is where +most memory errors occur, and it's where memory management strategies vary the +most between programming languages. + +## Memory management strategies + +Because memory is limited, it's important that programs remove unused data from +the heap ("free" the memory) as quickly as possible. Figuring out when to free +that memory is pretty complicated. + +Some programming languages try to hide the complexities of memory management +from you by utilizing a "garbage collector" process that tracks all memory +usage and deallocates unused heap memory periodically (also known as automatic +memory management). A significant benefit of this method is that it relieves +developers from the burden of manual memory management, generally avoiding more +errors and making developers more productive. However, it incurs a performance +cost because the garbage collector interrupts the program's execution, and it +might not reclaim memory very quickly. + +Other languages require that you manually free data that's allocated on the +heap. When done properly, this makes programs execute quickly, because there's +no processing time consumed by a garbage collector. However, the challenge with +this approach is that programmers make mistakes, especially when multiple parts +of the program need access to the same memory—it becomes difficult to know +which part of the program "owns" the data and must deallocate it. Programmers +might accidentally deallocate data before the program is done with it (causing +"use-after-free" errors), or they might deallocate it twice ("double free" +errors), or they might never deallocate it ("leaked memory" errors). Mistakes +like these and others can have catastrophic results for the program, and these +bugs are often hard to track down, making it especially important that they +don't occur in the first place. + +Mojo uses a third approach called "ownership" that relies on a collection of +rules that programmers must follow when passing values. The rules ensure there +is only one "owner" for a given value at a time. When a value's lifetime ends, +Mojo calls its destructor, which is responsible for deallocating any heap memory +that needs to be deallocated. + +In this way, Mojo helps ensure memory is freed, but it does so in a way that's +deterministic and safe from errors such as use-after-free, double-free and +memory leaks. Plus, it does so with a very low performance overhead. + +Mojo's value ownership model provides an excellent balance of programming +productivity and strong memory safety. It only requires that you learn some new +syntax and a few rules about how to share access to memory within your program. + +But before we explain the rules and syntax for Mojo's value ownership model, +you first need to understand [value +semantics](/mojo/manual/values/value-semantics). diff --git a/docs/manual/values/lifetimes.ipynb b/docs/manual/values/lifetimes.ipynb deleted file mode 100644 index f218538d67..0000000000 --- a/docs/manual/values/lifetimes.ipynb +++ /dev/null @@ -1,595 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Lifetimes, origins, and references\n", - "sidebar_position: 4\n", - "description: Working with origins and references.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Mojo compiler includes a lifetime checker, a compiler pass that analyzes\n", - "dataflow through your program. It identifies when variables are valid and \n", - "inserts destructor calls when a variable's lifetime ends.\n", - "\n", - "The Mojo compiler uses a special value called an _origin_ to track the lifetime\n", - "of variables and the validity of references.\n", - "\n", - "Specifically, an origin answers two questions:\n", - "\n", - "- What variable \"owns\" this value?\n", - "- Can the value be mutated using this reference?\n", - "\n", - "For example, consider the following code:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Joan\n" - ] - } - ], - "source": [ - "fn print_str(s: String):\n", - " print(s)\n", - "\n", - "name = String(\"Joan\")\n", - "print_str(name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The line `name = String(\"Joan\")` declares a variable with an identifier (`name`)\n", - "and logical storage space for a `String` value. When you pass `name` into the\n", - "`print_str()` function, the function gets an immutable reference to the value. \n", - "So both `name` and `s` refer to the same logical storage space, and have\n", - "associated origin values that lets the Mojo compiler reason about them. \n", - "\n", - "Most of the time, origins are handled automatically by the compiler. \n", - "However, in some cases you'll need to interact with origins directly:\n", - "\n", - "- When working with references—specifically `ref` arguments and `ref` return\n", - " values. \n", - "\n", - "- When working with types like \n", - " [`Pointer`](/mojo/stdlib/memory/reference/Pointer) or \n", - " [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the \n", - " origin of the data they refer to.\n", - "\n", - "This section also covers [`ref` arguments](#ref-arguments) and \n", - "[`ref` return values](#ref-return-values), which let functions\n", - "take arguments and provide return values as references with parametric\n", - "origins." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Working with origins\n", - "\n", - "Mojo's origin values are unlike most other values in the language, because\n", - "they're primitive values, not Mojo structs.\n", - "\n", - "Likewise, because these values are mostly created by the \n", - "compiler, you can't just create your own origin value—you usually need to \n", - "derive an origin from an existing value.\n", - "\n", - "### Origin types\n", - "\n", - "Mojo supplies a struct and a set of aliases that you can use to specify \n", - "origin types. As the names suggest, the `ImmutableOrigin` and \n", - "`MutableOrigin` aliases represent immutable and mutable origins, \n", - "respectively:\n", - "\n", - "```mojo\n", - "struct ImmutableRef[origin: ImmutableOrigin]:\n", - " pass\n", - "```\n", - "\n", - "Or you can use the [`Origin`](mojo/stdlib/builtin/type_aliases/Origin)\n", - "struct to specify an origin with parametric mutability:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "struct ParametricRef[\n", - " is_mutable: Bool,\n", - " //,\n", - " origin: Origin[is_mutable].type\n", - "]:\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that `Origin` _isn't an origin value_, it's a helper for specifying a \n", - "origin **type**. Origin types carry the mutability of a reference as a \n", - "boolean parameter value, indicating whether the origin is mutable, immutable,\n", - "or even with mutability depending on a parameter specified by the enclosing API.\n", - "\n", - "The `is_mutable` parameter here is an [infer-only\n", - "parameter](/mojo/manual/parameters/#infer-only-parameters). It's never\n", - "specified directly by the user, but always inferred from context. The\n", - "`origin` value is often inferred, as well. For example, the following code\n", - "creates a [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an existing\n", - "value, but doesn't need to specify an origin—the `origin` is inferred from\n", - "the variable passed in to the `address_of()` method." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import Pointer\n", - "\n", - "def use_pointer():\n", - " a = 10\n", - " ptr = Pointer.address_of(a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A final type of origin value is an `OriginSet`. As the name suggests, an \n", - "`OriginSet` represents a group of origins. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Origin values\n", - "\n", - "Most origin values are created by the compiler. As a developer, there are a\n", - "few ways to specify origin values:\n", - "\n", - "- Static origin. The `StaticConstantOrigin`\n", - " alias is an origin value representing immutable values that that last for the\n", - " duration of the program. String literal values have a `StaticConstantOrigin`.\n", - "- The `__origin_of()` magic function, which returns the origin associated\n", - " with the value (or values) passed in.\n", - "- Inferred origin. You can use inferred parameters to capture the origin\n", - " of a value passed in to a function.\n", - "- Wildcard origins. The `ImmutableAnyOrigin` and `MutableAnyOrigin` aliases\n", - " are special cases indicating a reference that might access any live value.\n", - "\n", - "#### Static origins\n", - "\n", - "You can use the static origin `StaticConstantOrigin` when you have a \n", - "value that exists for the entire duration of the program.\n", - "\n", - "For example, the `StringLiteral` method\n", - "[`as_string_slice()`](/mojo/stdlib/builtin/string_literal/StringLiteral#as_string_slice)\n", - "returns a [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) pointing\n", - "to the original string literal. String literals are static—they're allocated at\n", - "compile time and never destroyed—so the slice is created with an immutable,\n", - "static origin.\n", - "\n", - "#### Derived origins\n", - "\n", - "Use the `__origin_of(value)` operator to obtain a value's origin. The\n", - "argument to `__origin_of()` can take an arbitrary expression:\n", - "\n", - "```mojo\n", - "__origin_of(self)\n", - "__origin_of(x.y)\n", - "__origin_of(foo())\n", - "```\n", - "\n", - "The `__origin_of()` operator is analyzed statically at compile time;\n", - "The expression passed to `__origin_of()` is never evaluated. (For example, \n", - "when the compiler analyzes `__origin_of(foo())`, it doesn't run the `foo()`\n", - "function.)\n", - "\n", - "The following struct stores a string value using a \n", - "[`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer): a smart\n", - "pointer that holds an owned value. The `as_ptr()` method returns a `Pointer` to\n", - "the stored string, using the same origin as the original `OwnedPointer`." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "from memory import OwnedPointer, Pointer\n", - "\n", - "struct BoxedString:\n", - " var box: OwnedPointer[String]\n", - "\n", - " fn __init__(out self, value: String):\n", - " self.box = OwnedPointer(value)\n", - "\n", - " fn as_ptr(self) -> Pointer[String, __origin_of(self.box)]:\n", - " return Pointer.address_of(self.box[])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Inferred origins\n", - "\n", - "The other common way to access an origin value is to _infer_ it from the\n", - "the arguments passed to a function or method. For example, the `Span` type\n", - "has an associated `origin`:\n", - "\n", - "```mojo\n", - "struct Span[\n", - " is_mutable: Bool, //,\n", - " T: CollectionElement,\n", - " origin: Origin[is_mutable].type,\n", - "](CollectionElementNew):\n", - " \"\"\"A non owning view of contiguous data.\n", - "```\n", - "\n", - "One of its constructors creates a `Span` from an existing `List`, and infers\n", - "its `origin` value from the list:\n", - "\n", - "```mojo\n", - " fn __init__(out self, ref [origin]list: List[T, *_]):\n", - " \"\"\"Construct a Span from a List.\n", - "\n", - " Args:\n", - " list: The list to which the span refers.\n", - " \"\"\"\n", - " self._data = list.data\n", - " self._len = len(list)\n", - "```\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Working with references\n", - "\n", - "You can use the `ref` keyword with arguments and return values to specify a \n", - "reference with parametric mutability. That is, they can be either mutable or \n", - "immutable.\n", - "\n", - "From inside the called function, a `ref` argument looks like a `borrowed` or\n", - "`inout` argument. \n", - "\n", - "A `ref` return value looks like any other return value to the calling function,\n", - "but it is a _reference_ to an existing value, not a copy.\n", - "\n", - "### `ref` arguments\n", - "\n", - "The `ref` argument convention lets you specify an argument of parametric\n", - "mutability: that is, you don't need to know in advance whether the passed\n", - "argument will be mutable or immutable. There are several reasons you might want\n", - "to use a `ref` argument:\n", - "\n", - "- You want to accept an argument with parametric mutability.\n", - "\n", - "- You want to tie the lifetime of one argument to the lifetime of another\n", - " argument.\n", - "\n", - "- When you want an argument that is guaranteed to be passed in memory: this can\n", - " be important and useful for generic arguments that need an identity,\n", - " irrespective of whether the concrete type is register passable.\n", - "\n", - "The syntax for a `ref` argument is:\n", - "\n", - "ref [origin_specifier] arg_name: arg_type\n", - "\n", - "The origin specifier passed inside the square brackets can be either:\n", - "\n", - "- An origin value.\n", - "- An arbitrary expression, which is treated as shorthand for \n", - " `__origin_of(expression)`. In other words, the following declarations are\n", - " equivalent:\n", - "\n", - " ```mojo\n", - " ref [__origin_of(self)]\n", - " ref [self]\n", - " ```\n", - " \n", - "- An underscore character (`_`) to indicate that the origin is _unbound_. You\n", - " can think of the underscore as a wildcard that will accept any origin:\n", - "\n", - " ```mojo\n", - " def add_ref(ref a: Int, b: Int) -> Int:\n", - " return a+b\n", - " ```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also name the origin explicitly. This is useful if you want to specify\n", - "an `ImmutableOrigin` or `MutableLOrigin`, or if you want to bind to\n", - "the `is_mutable` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Immutable: Hello\n", - "Mutable: Goodbye\n" - ] - } - ], - "source": [ - "def take_str_ref[\n", - " is_mutable: Bool, //,\n", - " origin: Origin[is_mutable].type\n", - " ](ref [origin] s: String):\n", - " @parameter\n", - " if is_mutable:\n", - " print(\"Mutable: \" + s)\n", - " else:\n", - " print(\"Immutable: \" + s)\n", - "\n", - "def pass_refs(s1: String, owned s2: String):\n", - " take_str_ref(s1)\n", - " take_str_ref(s2)\n", - "\n", - "pass_refs(\"Hello\", \"Goodbye\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `ref` return values\n", - "\n", - "Like `ref` arguments, `ref` return values allow a function to return a mutable\n", - "or immutable reference to a value. Like a `borrowed` or `inout` argument, these\n", - "references don't need to be dereferenced.\n", - "\n", - "`ref` return values can be an efficient way to handle updating items in a \n", - "collection. The standard way to do this is by implementing the `__getitem__()`\n", - "and `__setitem__()` dunder methods. These are invoked to read from and write to \n", - "a subscripted item in a collection:\n", - "\n", - "```mojo\n", - "value = list[a]\n", - "list[b] += 10\n", - "```\n", - "\n", - "With a `ref` argument, `__getitem__()` can return a mutable reference that can\n", - "be modified directly. This has pros and cons compared to using a `__setitem__()`\n", - "method:\n", - "\n", - "- The mutable reference is more efficient—a single update isn't broken up across\n", - " two methods. However, the referenced value must be in memory.\n", - " \n", - "- A `__getitem__()`/`__setitem__()` pair allows for arbitrary code to be run \n", - " when values are retrieved and set. For example, `__setitem__()` can validate\n", - " or constrain input values.\n", - "\n", - "For example, in the following example, `NameList` has a `__getitem__()` method\n", - "that returns a reference: " - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dana\n", - "Dana?\n" - ] - } - ], - "source": [ - "struct NameList:\n", - " var names: List[String]\n", - "\n", - " def __init__(out self, *names: String):\n", - " self.names = List[String]()\n", - " for name in names:\n", - " self.names.append(name[])\n", - "\n", - " def __getitem__(ref self, index: Int) ->\n", - " ref [self.names] String:\n", - " if (index >=0 and index < len(self.names)):\n", - " return self.names[index]\n", - " else:\n", - " raise Error(\"index out of bounds\")\n", - "\n", - "def use_name_list():\n", - " list = NameList(\"Thor\", \"Athena\", \"Dana\", \"Vrinda\")\n", - " print(list[2])\n", - " list[2] += \"?\"\n", - " print(list[2])\n", - "\n", - "use_name_list()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that this update succeeds, even though `NameList` doesn't define a\n", - "`__setitem__()` method:\n", - "\n", - "```mojo\n", - "list[2] += \"?\"\n", - "```\n", - "\n", - "Also note that the code uses the return value directly each time, rather than\n", - "assigning the return value to a variable, like this:\n", - "\n", - "```mojo\n", - "name = list[2]\n", - "name += \"?\"\n", - "```\n", - "\n", - "Since a variable needs to own its value, `name` would end up with an owned \n", - "_copy_ of the referenced value. Mojo doesn't currently have \n", - "syntax to express that you want to keep the original reference in `name`. This\n", - "will be added in a future release.\n", - "\n", - "If you're working with an API that returns a reference, and you want to avoid\n", - "copying the referenced value, you can use a\n", - "[`Pointer`](/mojo/stdlib/memory/reference/Pointer) to hold an indirect reference.\n", - "You can assign a `Pointer` to a variable, but you need to use the dereference\n", - "operator (`[]`) to access the underlying value.\n", - "\n", - "```mojo\n", - "name_ptr = Pointer.address_of(list[2])\n", - "name_ptr[] += \"?\"\n", - "```\n", - "\n", - "Similarly, when designing an API you might want to return a `Pointer` instead of\n", - "a `ref` to allow users to assign the return value to a variable. For example, \n", - "iterators for the standard library collections return pointers, so they can be\n", - "used in `for..in` loops:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n", - "3\n", - "1\n", - "2\n", - "3\n" - ] - } - ], - "source": [ - "nums = List(1, 2, 3)\n", - "for item in nums: # List iterator returns a Pointer, which must be dereferenced\n", - " print(item[])\n", - "for i in range(len(nums)):\n", - " print(nums[i]) # List __getitem__() returns a ref" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(You can find the code for the \n", - "`List` iterator in the [Mojo\n", - "repo](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L63).)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Parametric mutability of return values\n", - "\n", - "Another advantage of `ref` return arguments is the ability to support parametric\n", - "mutability. For example, recall the signature of the `__getitem__()` method\n", - "above:\n", - "\n", - "```mojo\n", - "def __getitem__(ref self, index: Int) ->\n", - " ref [self] String:\n", - "```\n", - "\n", - "Since the `origin` of the return value is tied to the origin of `self`, the\n", - "returned reference will be mutable if the method was called using a\n", - "mutable reference. The method still works if you have an immutable reference\n", - "to the `NameList`, but it returns an immutable reference:" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Diana\n" - ] - } - ], - "source": [ - "fn pass_immutable_list(list: NameList) raises:\n", - " print(list[2])\n", - " # list[2] += \"?\" # Error, this list is immutable\n", - "\n", - "def use_name_list_again():\n", - " list = NameList(\"Sophie\", \"Jack\", \"Diana\")\n", - " pass_immutable_list(list)\n", - "\n", - "use_name_list_again()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Without parametric mutability, you'd need to write two versions of \n", - "`__getitem__()`, one that accepts an immutable `self` and another that accepts\n", - "a mutable `self`. " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/values/lifetimes.mdx b/docs/manual/values/lifetimes.mdx new file mode 100644 index 0000000000..d191af8e2e --- /dev/null +++ b/docs/manual/values/lifetimes.mdx @@ -0,0 +1,434 @@ +--- +title: Lifetimes, origins, and references +sidebar_position: 4 +description: Working with origins and references. +--- + +The Mojo compiler includes a lifetime checker, a compiler pass that analyzes +dataflow through your program. It identifies when variables are valid and +inserts destructor calls when a variable's lifetime ends. + +The Mojo compiler uses a special value called an *origin* to track the lifetime +of variables and the validity of references. + +Specifically, an origin answers two questions: + +* What variable "owns" this value? +* Can the value be mutated using this reference? + +For example, consider the following code: + +```mojo +fn print_str(s: String): + print(s) + +name = String("Joan") +print_str(name) +``` + +```output +Joan +``` + +The line `name = String("Joan")` declares a variable with an identifier (`name`) +and logical storage space for a `String` value. When you pass `name` into the +`print_str()` function, the function gets an immutable reference to the value. +So both `name` and `s` refer to the same logical storage space, and have +associated origin values that lets the Mojo compiler reason about them. + +Most of the time, origins are handled automatically by the compiler. +However, in some cases you'll need to interact with origins directly: + +* When working with references—specifically `ref` arguments and `ref` return + values. + +* When working with types like + [`Pointer`](/mojo/stdlib/memory/reference/Pointer) or + [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the + origin of the data they refer to. + +This section also covers [`ref` arguments](#ref-arguments) and +[`ref` return values](#ref-return-values), which let functions +take arguments and provide return values as references with parametric +origins. + +## Working with origins + +Mojo's origin values are unlike most other values in the language, because +they're primitive values, not Mojo structs. + +Likewise, because these values are mostly created by the +compiler, you can't just create your own origin value—you usually need to +derive an origin from an existing value. + +### Origin types + +Mojo supplies a struct and a set of aliases that you can use to specify +origin types. As the names suggest, the `ImmutableOrigin` and +`MutableOrigin` aliases represent immutable and mutable origins, +respectively: + +```mojo +struct ImmutableRef[origin: ImmutableOrigin]: + pass +``` + +Or you can use the [`Origin`](mojo/stdlib/builtin/type_aliases/Origin) +struct to specify an origin with parametric mutability: + +```mojo +struct ParametricRef[ + is_mutable: Bool, + //, + origin: Origin[is_mutable].type +]: + pass +``` + +Note that `Origin` *isn't an origin value*, it's a helper for specifying a +origin **type**. Origin types carry the mutability of a reference as a +boolean parameter value, indicating whether the origin is mutable, immutable, +or even with mutability depending on a parameter specified by the enclosing API. + +The `is_mutable` parameter here is an [infer-only +parameter](/mojo/manual/parameters/#infer-only-parameters). It's never +specified directly by the user, but always inferred from context. The +`origin` value is often inferred, as well. For example, the following code +creates a [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an existing +value, but doesn't need to specify an origin—the `origin` is inferred from +the variable passed in to the `address_of()` method. + +```mojo +from memory import Pointer + +def use_pointer(): + a = 10 + ptr = Pointer.address_of(a) +``` + +A final type of origin value is an `OriginSet`. As the name suggests, an +`OriginSet` represents a group of origins. + +### Origin values + +Most origin values are created by the compiler. As a developer, there are a +few ways to specify origin values: + +* Static origin. The `StaticConstantOrigin` + alias is an origin value representing immutable values that that last for the + duration of the program. String literal values have a `StaticConstantOrigin`. +* The `__origin_of()` magic function, which returns the origin associated + with the value (or values) passed in. +* Inferred origin. You can use inferred parameters to capture the origin + of a value passed in to a function. +* Wildcard origins. The `ImmutableAnyOrigin` and `MutableAnyOrigin` aliases + are special cases indicating a reference that might access any live value. + +#### Static origins + +You can use the static origin `StaticConstantOrigin` when you have a +value that exists for the entire duration of the program. + +For example, the `StringLiteral` method +[`as_string_slice()`](/mojo/stdlib/builtin/string_literal/StringLiteral#as_string_slice) +returns a [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) pointing +to the original string literal. String literals are static—they're allocated at +compile time and never destroyed—so the slice is created with an immutable, +static origin. + +#### Derived origins + +Use the `__origin_of(value)` operator to obtain a value's origin. The +argument to `__origin_of()` can take an arbitrary expression: + +```mojo +__origin_of(self) +__origin_of(x.y) +__origin_of(foo()) +``` + +The `__origin_of()` operator is analyzed statically at compile time; +The expression passed to `__origin_of()` is never evaluated. (For example, +when the compiler analyzes `__origin_of(foo())`, it doesn't run the `foo()` +function.) + +The following struct stores a string value using a +[`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer): a smart +pointer that holds an owned value. The `as_ptr()` method returns a `Pointer` to +the stored string, using the same origin as the original `OwnedPointer`. + +```mojo +from memory import OwnedPointer, Pointer + +struct BoxedString: + var box: OwnedPointer[String] + + fn __init__(out self, value: String): + self.box = OwnedPointer(value) + + fn as_ptr(self) -> Pointer[String, __origin_of(self.box)]: + return Pointer.address_of(self.box[]) +``` + +#### Inferred origins + +The other common way to access an origin value is to *infer* it from the +the arguments passed to a function or method. For example, the `Span` type +has an associated `origin`: + +```mojo +struct Span[ + is_mutable: Bool, //, + T: CollectionElement, + origin: Origin[is_mutable].type, +](CollectionElementNew): + """A non owning view of contiguous data. +``` + +One of its constructors creates a `Span` from an existing `List`, and infers +its `origin` value from the list: + +```mojo + fn __init__(out self, ref [origin]list: List[T, *_]): + """Construct a Span from a List. + + Args: + list: The list to which the span refers. + """ + self._data = list.data + self._len = len(list) +``` + +## Working with references + +You can use the `ref` keyword with arguments and return values to specify a +reference with parametric mutability. That is, they can be either mutable or +immutable. + +From inside the called function, a `ref` argument looks like a `borrowed` or +`inout` argument. + +A `ref` return value looks like any other return value to the calling function, +but it is a *reference* to an existing value, not a copy. + +### `ref` arguments + +The `ref` argument convention lets you specify an argument of parametric +mutability: that is, you don't need to know in advance whether the passed +argument will be mutable or immutable. There are several reasons you might want +to use a `ref` argument: + +* You want to accept an argument with parametric mutability. + +* You want to tie the lifetime of one argument to the lifetime of another + argument. + +* When you want an argument that is guaranteed to be passed in memory: this can + be important and useful for generic arguments that need an identity, + irrespective of whether the concrete type is register passable. + +The syntax for a `ref` argument is: + +ref [origin_specifier] arg_name: arg_type + +The origin specifier passed inside the square brackets can be either: + +* An origin value. + +* An arbitrary expression, which is treated as shorthand for + `__origin_of(expression)`. In other words, the following declarations are + equivalent: + + ```mojo + ref [__origin_of(self)] + ref [self] + ``` + +* An underscore character (`_`) to indicate that the origin is *unbound*. You + can think of the underscore as a wildcard that will accept any origin: + + ```mojo + def add_ref(ref a: Int, b: Int) -> Int: + return a+b + ``` + +You can also name the origin explicitly. This is useful if you want to specify +an `ImmutableOrigin` or `MutableLOrigin`, or if you want to bind to +the `is_mutable` parameter. + +```mojo +def take_str_ref[ + is_mutable: Bool, //, + origin: Origin[is_mutable].type + ](ref [origin] s: String): + @parameter + if is_mutable: + print("Mutable: " + s) + else: + print("Immutable: " + s) + +def pass_refs(s1: String, owned s2: String): + take_str_ref(s1) + take_str_ref(s2) + +pass_refs("Hello", "Goodbye") +``` + +```output +Immutable: Hello +Mutable: Goodbye +``` + +### `ref` return values + +Like `ref` arguments, `ref` return values allow a function to return a mutable +or immutable reference to a value. Like a `borrowed` or `inout` argument, these +references don't need to be dereferenced. + +`ref` return values can be an efficient way to handle updating items in a +collection. The standard way to do this is by implementing the `__getitem__()` +and `__setitem__()` dunder methods. These are invoked to read from and write to +a subscripted item in a collection: + +```mojo +value = list[a] +list[b] += 10 +``` + +With a `ref` argument, `__getitem__()` can return a mutable reference that can +be modified directly. This has pros and cons compared to using a `__setitem__()` +method: + +* The mutable reference is more efficient—a single update isn't broken up across + two methods. However, the referenced value must be in memory. + +* A `__getitem__()`/`__setitem__()` pair allows for arbitrary code to be run + when values are retrieved and set. For example, `__setitem__()` can validate + or constrain input values. + +For example, in the following example, `NameList` has a `__getitem__()` method +that returns a reference: + +```mojo +struct NameList: + var names: List[String] + + def __init__(out self, *names: String): + self.names = List[String]() + for name in names: + self.names.append(name[]) + + def __getitem__(ref self, index: Int) -> + ref [self.names] String: + if (index >=0 and index < len(self.names)): + return self.names[index] + else: + raise Error("index out of bounds") + +def use_name_list(): + list = NameList("Thor", "Athena", "Dana", "Vrinda") + print(list[2]) + list[2] += "?" + print(list[2]) + +use_name_list() + +``` + +```output +Dana +Dana? +``` + +Note that this update succeeds, even though `NameList` doesn't define a +`__setitem__()` method: + +```mojo +list[2] += "?" +``` + +Also note that the code uses the return value directly each time, rather than +assigning the return value to a variable, like this: + +```mojo +name = list[2] +name += "?" +``` + +Since a variable needs to own its value, `name` would end up with an owned +*copy* of the referenced value. Mojo doesn't currently have +syntax to express that you want to keep the original reference in `name`. This +will be added in a future release. + +If you're working with an API that returns a reference, and you want to avoid +copying the referenced value, you can use a +[`Pointer`](/mojo/stdlib/memory/reference/Pointer) to hold an indirect reference. +You can assign a `Pointer` to a variable, but you need to use the dereference +operator (`[]`) to access the underlying value. + +```mojo +name_ptr = Pointer.address_of(list[2]) +name_ptr[] += "?" +``` + +Similarly, when designing an API you might want to return a `Pointer` instead of +a `ref` to allow users to assign the return value to a variable. For example, +iterators for the standard library collections return pointers, so they can be +used in `for..in` loops: + +```mojo +nums = List(1, 2, 3) +for item in nums: # List iterator returns a Pointer, which must be dereferenced + print(item[]) +for i in range(len(nums)): + print(nums[i]) # List __getitem__() returns a ref +``` + +```output +1 +2 +3 +1 +2 +3 +``` + +(You can find the code for the +`List` iterator in the [Mojo +repo](https://github.com/modularml/mojo/blob/main/stdlib/src/collections/list.mojo#L63).) + +#### Parametric mutability of return values + +Another advantage of `ref` return arguments is the ability to support parametric +mutability. For example, recall the signature of the `__getitem__()` method +above: + +```mojo +def __getitem__(ref self, index: Int) -> + ref [self] String: +``` + +Since the `origin` of the return value is tied to the origin of `self`, the +returned reference will be mutable if the method was called using a +mutable reference. The method still works if you have an immutable reference +to the `NameList`, but it returns an immutable reference: + +```mojo +fn pass_immutable_list(list: NameList) raises: + print(list[2]) + # list[2] += "?" # Error, this list is immutable + +def use_name_list_again(): + list = NameList("Sophie", "Jack", "Diana") + pass_immutable_list(list) + +use_name_list_again() +``` + +```output +Diana +``` + +Without parametric mutability, you'd need to write two versions of +`__getitem__()`, one that accepts an immutable `self` and another that accepts +a mutable `self`. diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb deleted file mode 100644 index 9601db72d2..0000000000 --- a/docs/manual/values/ownership.ipynb +++ /dev/null @@ -1,653 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Ownership and borrowing\n", - "sidebar_position: 3\n", - "description: How Mojo shares references through function arguments.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A challenge you might face when using some programming languages is that you\n", - "must manually allocate and deallocate memory. When multiple parts of the\n", - "program need access to the same memory, it becomes difficult to keep track of\n", - "who \"owns\" a value and determine when is the right time to deallocate it. If\n", - "you make a mistake, it can result in a \"use-after-free\" error, a \"double free\"\n", - "error, or a \"leaked memory\" error, any one of which can be catastrophic.\n", - "\n", - "Mojo helps avoid these errors by ensuring there is only one variable that owns\n", - "each value at a time, while still allowing you to share references with other\n", - "functions. When the life span of the owner ends, Mojo [destroys the\n", - "value](/mojo/manual/lifecycle/death). Programmers are still responsible for\n", - "making sure any type that allocates resources (including memory) also\n", - "deallocates those resources in its destructor. Mojo's ownership system ensures\n", - "that destructors are called promptly.\n", - "\n", - "On this page, we'll explain the rules that govern this ownership model, and how\n", - "to specify different argument conventions that define how values are passed into\n", - "functions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Ownership summary\n", - "\n", - "The fundamental rules that make Mojo's ownership model work are the following:\n", - "\n", - "- Every value has only one owner at a time.\n", - "- When the lifetime of the owner ends, Mojo destroys the value.\n", - "- If there are outstanding references to a value, Mojo extends the lifetime of\n", - " the owner.\n", - "\n", - "### Variables and references \n", - "\n", - "A variable _owns_ its value. A struct owns its fields. \n", - "\n", - "A _reference_ allows you to access a value owned by another variable. A\n", - "reference can have either mutable access or immutable access to that value.\n", - "\n", - "Mojo references are created when you call a function: function arguments can be\n", - "passed as mutable or immutable references. A function can also return a\n", - "reference instead of returning a value." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Argument conventions\n", - "\n", - "In all programming languages, code quality and performance is heavily dependent\n", - "upon how functions treat argument values. That is, whether a value received by\n", - "a function is a unique value or a reference, and whether it's mutable or\n", - "immutable, has a series of consequences that define the readability,\n", - "performance, and safety of the language.\n", - "\n", - "In Mojo, we want to provide full [value\n", - "semantics](/mojo/manual/values/value-semantics) by default, which provides\n", - "consistent and predictable behavior. But as a systems programming language, we\n", - "also need to offer full control over memory optimizations, which generally\n", - "requires reference semantics. The trick is to introduce reference semantics in\n", - "a way that ensures all code is memory safe by tracking the lifetime of every\n", - "value and destroying each one at the right time (and only once). All of this is\n", - "made possible in Mojo through the use of argument conventions that ensure every\n", - "value has only one owner at a time.\n", - "\n", - "An argument convention specifies whether an argument is mutable or immutable,\n", - "and whether the function owns the value. Each convention is defined by a\n", - "keyword at the beginning of an argument declaration:\n", - "\n", - "- `borrowed`: The function receives an **immutable reference**. This means the\n", - " function can read the original value (it is *not* a copy), but it cannot\n", - " mutate (modify) it. `def` functions treat this differently, as described below.\n", - " \n", - "- `inout`: The function receives a **mutable reference**. This means the\n", - " function can read and mutate the original value (it is *not* a copy).\n", - " \n", - "- `owned`: The function takes **ownership** of a value. This means the function\n", - " has exclusive ownership of the argument. The caller might choose to transfer\n", - " ownership of an existing value to this function, but that's not always what\n", - " happens. The callee might receive a newly-created value, or a copy of an\n", - " existing value. \n", - "\n", - "- `ref`: The function gets a reference with an parametric mutability: that is,\n", - " the reference can be either mutable or immutable. You can think of `ref` \n", - " arguments as a generalization of the `borrowed` and `inout` conventions. \n", - " `ref` arguments are an advanced topic, and they're described in more detail in\n", - " [Lifetimes and references](/mojo/manual/values/lifetimes).\n", - "\n", - "For example, this function has one argument that's a mutable\n", - "reference and one that's immutable:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "fn add(inout x: Int, borrowed y: Int):\n", - " x += y\n", - "\n", - "fn main():\n", - " var a = 1\n", - " var b = 2\n", - " add(a, b)\n", - " print(a) # Prints 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You've probably already seen some function arguments that don't declare a\n", - "convention. by default, all arguments are `borrowed`. \n", - "In the following sections, we'll explain each of these argument conventions in\n", - "more detail." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Borrowed arguments (`borrowed`)\n", - "\n", - "The `borrowed` convention is the default for all arguments. But `def` and `fn` \n", - "functions treat `borrowed` arguments somewhat differently:\n", - "\n", - "- In a [`def` function](/mojo/manual/functions#def-functions), if you mutate\n", - " the value in the body of the function, the function receives a mutable copy of\n", - " the argument. Otherwise, it receives an immutable reference. This allows you\n", - " to treat arguments as mutable, but avoid the overhead of making extra copies when\n", - " they're not needed.\n", - "\n", - "- In an [`fn` function](/mojo/manual/functions#fn-functions), the function\n", - " always receives an immutable reference. If you want a mutable copy, you can\n", - " assign it to a local variable:\n", - "\n", - " ```mojo\n", - " var my_copy = borrowed_arg\n", - " ```\n", - "\n", - "In both cases, the original value on the caller side can't be changed by the\n", - "callee.\n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 2, 3, 4]\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "def print_list(list: List[Int]):\n", - " print(list.__str__())\n", - "\n", - "var list = List(1, 2, 3, 4)\n", - "print_list(list)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here the `list` argument to `print_list()` is borrowed and not mutated, so the \n", - "`print_list()` function gets an immutable reference to the original `List`, and\n", - "doesn't do any copying. \n", - "\n", - "In general, passing an immutable reference is much more efficient\n", - "when handling large or expensive-to-copy values, because the copy constructor\n", - "and destructor are not invoked for a borrow.\n", - "\n", - "### Compared to C++ and Rust\n", - "\n", - "Mojo's borrowed argument convention is similar in some ways to passing an\n", - "argument by `const&` in C++, which also avoids a copy of the value and disables\n", - "mutability in the callee. However, the borrowed convention differs from\n", - "`const&` in C++ in two important ways:\n", - "\n", - "- The Mojo compiler implements a lifetime checker that ensures that values are\n", - "not destroyed when there are outstanding references to those values.\n", - "\n", - "- Small values like `Int`, `Float`, and `SIMD` are passed directly in machine\n", - "registers instead of through an extra indirection (this is because they are\n", - "declared with the `@register_passable` decorator). This is a [significant\n", - "performance\n", - "enhancement](https://www.forrestthewoods.com/blog/should-small-rust-structs-be-passed-by-copy-or-by-borrow/)\n", - "when compared to languages like C++ and Rust, and moves this optimization from\n", - "every call site to a declaration on the type definition.\n", - "\n", - "The major difference between Rust and Mojo is that Mojo does not require a\n", - "sigil on the caller side to pass by borrow. Also, Mojo is more efficient when\n", - "passing small values, and Rust defaults to moving values instead of passing\n", - "them around by borrow. These policy and syntax decisions allow Mojo to provide\n", - "an easier-to-use programming model." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Mutable arguments (`inout`)\n", - "\n", - "If you'd like your function to receive a **mutable reference**, add the `inout`\n", - "keyword in front of the argument name. You can think of `inout` like this: it\n", - "means any changes to the value *in*side the function are visible *out*side the\n", - "function.\n", - "\n", - "For example, this `mutate()` function updates the original `list` value:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 2, 3, 4, 5]\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "def mutate(inout l: List[Int]):\n", - " l.append(5)\n", - "\n", - "var list = List(1, 2, 3, 4)\n", - "\n", - "mutate(list)\n", - "print_list(list)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That behaves like an optimized replacement for this:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1, 2, 3, 4, 5]\n" - ] - } - ], - "source": [ - "from collections import List\n", - "\n", - "def mutate_copy(l: List[Int]) -> List[Int]:\n", - " l.append(5)\n", - " return l\n", - "\n", - "var list = List(1, 2, 3, 4)\n", - "list = mutate_copy(list)\n", - "print_list(list)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although the code using `inout` isn't that much shorter, it's more memory\n", - "efficient because it does not make a copy of the value.\n", - "\n", - "However, remember that the values passed as `inout` must already be mutable.\n", - "For example, if you try to take a `borrowed` value and pass it to another\n", - "function as `inout`, you'll get a compiler error because Mojo can't form a\n", - "mutable reference from an immutable reference.\n", - "\n", - ":::note\n", - "\n", - "You cannot define [default\n", - "values](/mojo/manual/functions#optional-arguments) for `inout`\n", - "arguments.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Argument exclusivity\n", - "\n", - "Mojo enforces _argument exclusivity_ for mutable references. This means that if\n", - "a function receives a mutable reference to a value (such as an `inout` argument),\n", - "it can't receive any other references to the same value—mutable or immutable.\n", - "That is, a mutable reference can't have any other references that _alias_ it.\n", - "\n", - "For example, consider the following code example:\n", - "\n", - "```mojo\n", - "fn append_twice(inout s: String, other: String):\n", - " # Mojo knows 's' and 'other' cannot be the same string.\n", - " s += other\n", - " s += other\n", - "\n", - "fn invalid_access():\n", - " var my_string = str(\"o\")\n", - "\n", - " # error: passing `my_string` inout is invalid since it is also passed\n", - " # borrowed.\n", - " append_twice(my_string, my_string)\n", - " print(my_string)\n", - "```\n", - "\n", - "This code is confusing because the user might expect the output to be `ooo`, \n", - "but since the first addition mutates both `s` and `other`, the actual output\n", - "would be `oooo`. Enforcing exclusivity of mutable references not only prevents\n", - "coding errors, it also allows the Mojo compiler to optimize code in some cases.\n", - "\n", - "One way to avoid this issue when you do need both a mutable and an immutable \n", - "reference (or need to pass the same value to two arguments) is to make a copy:\n", - "\n", - "```mojo\n", - "fn valid_access():\n", - " var my_string = str(\"o\")\n", - " var other_string = str(my_string)\n", - " append_twice(my_string, other_string)\n", - " print(my_string)\n", - "```\n", - "\n", - "Note that argument exclusivity isn't enforced for register-passable trivial\n", - "types (like `Int` and `Bool`), because they are always passed by copy. When\n", - "passing the same value into two `Int` arguments, the callee will receive two\n", - "copies of the value." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Transfer arguments (`owned` and `^`)\n", - "\n", - "And finally, if you'd like your function to receive value **ownership**, add the\n", - "`owned` keyword in front of the argument name.\n", - "\n", - "This convention is often combined with use of the postfixed `^` \"transfer\"\n", - "sigil on the variable that is passed into the function, which ends the\n", - "lifetime of that variable.\n", - "\n", - "Technically, the `owned` keyword does not guarantee that the received value is\n", - "_the original value_—it guarantees only that the function\n", - "gets unique ownership of a value. This happens in one of\n", - "three ways:\n", - "\n", - "- The caller passes the argument with the `^` transfer sigil, which ends the\n", - "lifetime of that variable (the variable becomes uninitialized) and ownership is\n", - "transferred into the function without making a copy of any heap-allocated data.\n", - "\n", - "- The caller **does not** use the `^` transfer sigil, in which case, the\n", - "value is copied into the function argument and the original variable remains\n", - "valid. (If the original value is not used again, the compiler may optimize away\n", - "the copy and transfer the value).\n", - "\n", - "- The caller passes in a newly-created \"owned\" value, such as a value returned\n", - "from a function. In this case, no variable owns the value and it can be\n", - "transferred directly to the callee. For example:\n", - "\n", - " ```mojo\n", - " def take(owned s: String):\n", - " pass\n", - "\n", - " take(String(\"A brand-new String!\"))\n", - " ```\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For example, the following code works by making a copy of the string,\n", - "because—although `take_text()` uses the `owned` convention—the caller does not\n", - "include the transfer sigil:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello!\n", - "Hello\n" - ] - } - ], - "source": [ - "fn take_text(owned text: String):\n", - " text += \"!\"\n", - " print(text)\n", - "\n", - "fn my_function():\n", - " var message: String = \"Hello\"\n", - " take_text(message)\n", - " print(message)\n", - "\n", - "my_function()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, if you add the `^` transfer sigil when calling `take_text()`, the\n", - "compiler complains about `print(message)`, because at that point, the `message`\n", - "variable is no longer initialized. That is, this version does not compile:\n", - "\n", - "```mojo\n", - "fn my_function():\n", - " var message: String = \"Hello\"\n", - " take_text(message^) \n", - " print(message) # ERROR: The `message` variable is uninitialized\n", - "```\n", - "\n", - "This is a critical feature of Mojo's lifetime checker, because it ensures that no\n", - "two variables can have ownership of the same value. To fix the error, you must\n", - "not use the `message` variable after you end its lifetime with the `^` transfer\n", - "operator. So here is the corrected code:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello!\n" - ] - } - ], - "source": [ - "\n", - "fn my_function():\n", - " var message: String = \"Hello\"\n", - " take_text(message^)\n", - "\n", - "my_function()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Regardless of how it receives the value, when the function declares an argument\n", - "as `owned`, it can be certain that it has unique mutable access to that value. \n", - "Because the value is owned, the value is destroyed when the function \n", - "exits—unless the function transfers the value elsewhere.\n", - "\n", - "For example, in the following example, `add_to_list()` takes a string and\n", - "appends it to the list. Ownership of the string is transferred to the list, so\n", - "it's not destroyed when the function exits. On the other hand, \n", - "`consume_string()` doesn't transfer its `owned` value out, so the value is \n", - "destroyed at the end of the function." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "from collections import List\n", - "\n", - "def add_to_list(owned name: String, inout list: List[String]):\n", - " list.append(name^)\n", - " # name is uninitialized, nothing to destroy\n", - "\n", - "def consume_string(owned s: String):\n", - " print(s)\n", - " # s is destroyed here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "Value lifetimes are not fully implemented for top-level code in\n", - "Mojo's REPL, so the transfer sigil currently works as intended only when\n", - "used inside a function.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Borrowed versus owned in `def` functions\n", - "\n", - "The difference between `borrowed` and `owned` in a `def` function may be a\n", - "little subtle. In both cases, you can end up with a uniquely-owned value that's\n", - "a copy of the original value.\n", - "\n", - "- The `borrowed` argument always gets an immutable reference or a local copy.\n", - " You can't transfer a value into a `borrowed` argument.\n", - "\n", - "- The `owned` argument always gets a uniquely owned value, which may have been\n", - " copied or transferred from the callee. Using `owned` arguments without the \n", - " transfer sigil (`^`) usually results in values being copied." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Transfer implementation details\n", - "\n", - "In Mojo, you shouldn't conflate \"ownership transfer\" with a \"move\n", - "operation\"—these are not strictly the same thing. \n", - "\n", - "There are multiple ways that Mojo can transfer ownership of a value:\n", - "\n", - "- If a type implements the [move\n", - " constructor](/mojo/manual/lifecycle/life#move-constructor),\n", - " `__moveinit__()`, Mojo may invoke this method _if_ a value of that type is\n", - " transferred into a function as an `owned` argument, _and_ the original\n", - " variable's lifetime ends at the same point (with or without use of the `^`\n", - " transfer sigil).\n", - "\n", - "- If a type implements the [copy \n", - " constructor](/mojo/manual/lifecycle/life#move-constructor), `__copyinit__()`\n", - " and not `__moveinit__()`, Mojo may copy the value and destroy the old value.\n", - "\n", - "- In some cases, Mojo can optimize away the move operation entirely, leaving the \n", - " value in the same memory location but updating its ownership. In these cases,\n", - " a value can be transferred without invoking either the `__copyinit__()` or \n", - " `__moveinit__()` constructors.\n", - "\n", - "In order for the `owned` convention to work _without_ the transfer\n", - "sigil, the value type must be copyable (via `__copyinit__()`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Comparing `def` and `fn` argument conventions\n", - "\n", - "As mentioned in the section about\n", - "[functions](/mojo/manual/functions), `def` and `fn` functions\n", - "are interchangeable, as far as a caller is concerned, and they can both\n", - "accomplish the same things. It's only the inside that differs, and Mojo's `def`\n", - "function is essentially just sugaring for the `fn` function:\n", - "\n", - "- A `def` argument without a type annotation defaults to\n", - " [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn`\n", - " requires all types be explicitly declared).\n", - "\n", - "- A `def` function can treat a `borrowed` argument as mutable (in which case it\n", - " receives a mutable copy). An `fn` function must make this copy explicitly.\n", - "\n", - "For example, these two functions have the exact same behavior." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "def def_example(a: Int, inout b: Int, owned c):\n", - " pass\n", - "\n", - "fn fn_example(a_in: Int, inout b: Int, owned c: object):\n", - " var a = a_in\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This shadow copy typically adds no overhead, because small types\n", - "like `object` are cheap to copy. However, copying large types that allocate heap\n", - "storage can be expensive. (For example, copying `List` or `Dict` types, or\n", - "copying large numbers of strings.)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/values/ownership.mdx b/docs/manual/values/ownership.mdx new file mode 100644 index 0000000000..95817dd2b1 --- /dev/null +++ b/docs/manual/values/ownership.mdx @@ -0,0 +1,465 @@ +--- +title: Ownership and borrowing +sidebar_position: 3 +description: How Mojo shares references through function arguments. +--- + +A challenge you might face when using some programming languages is that you +must manually allocate and deallocate memory. When multiple parts of the +program need access to the same memory, it becomes difficult to keep track of +who "owns" a value and determine when is the right time to deallocate it. If +you make a mistake, it can result in a "use-after-free" error, a "double free" +error, or a "leaked memory" error, any one of which can be catastrophic. + +Mojo helps avoid these errors by ensuring there is only one variable that owns +each value at a time, while still allowing you to share references with other +functions. When the life span of the owner ends, Mojo [destroys the +value](/mojo/manual/lifecycle/death). Programmers are still responsible for +making sure any type that allocates resources (including memory) also +deallocates those resources in its destructor. Mojo's ownership system ensures +that destructors are called promptly. + +On this page, we'll explain the rules that govern this ownership model, and how +to specify different argument conventions that define how values are passed into +functions. + +## Ownership summary + +The fundamental rules that make Mojo's ownership model work are the following: + +* Every value has only one owner at a time. +* When the lifetime of the owner ends, Mojo destroys the value. +* If there are outstanding references to a value, Mojo extends the lifetime of + the owner. + +### Variables and references + +A variable *owns* its value. A struct owns its fields. + +A *reference* allows you to access a value owned by another variable. A +reference can have either mutable access or immutable access to that value. + +Mojo references are created when you call a function: function arguments can be +passed as mutable or immutable references. A function can also return a +reference instead of returning a value. + +## Argument conventions + +In all programming languages, code quality and performance is heavily dependent +upon how functions treat argument values. That is, whether a value received by +a function is a unique value or a reference, and whether it's mutable or +immutable, has a series of consequences that define the readability, +performance, and safety of the language. + +In Mojo, we want to provide full [value +semantics](/mojo/manual/values/value-semantics) by default, which provides +consistent and predictable behavior. But as a systems programming language, we +also need to offer full control over memory optimizations, which generally +requires reference semantics. The trick is to introduce reference semantics in +a way that ensures all code is memory safe by tracking the lifetime of every +value and destroying each one at the right time (and only once). All of this is +made possible in Mojo through the use of argument conventions that ensure every +value has only one owner at a time. + +An argument convention specifies whether an argument is mutable or immutable, +and whether the function owns the value. Each convention is defined by a +keyword at the beginning of an argument declaration: + +* `borrowed`: The function receives an **immutable reference**. This means the + function can read the original value (it is *not* a copy), but it cannot + mutate (modify) it. `def` functions treat this differently, as described below. + +* `inout`: The function receives a **mutable reference**. This means the + function can read and mutate the original value (it is *not* a copy). + +* `owned`: The function takes **ownership** of a value. This means the function + has exclusive ownership of the argument. The caller might choose to transfer + ownership of an existing value to this function, but that's not always what + happens. The callee might receive a newly-created value, or a copy of an + existing value. + +* `ref`: The function gets a reference with an parametric mutability: that is, + the reference can be either mutable or immutable. You can think of `ref` + arguments as a generalization of the `borrowed` and `inout` conventions. + `ref` arguments are an advanced topic, and they're described in more detail in + [Lifetimes and references](/mojo/manual/values/lifetimes). + +For example, this function has one argument that's a mutable +reference and one that's immutable: + +```mojo +fn add(inout x: Int, borrowed y: Int): + x += y + +fn main(): + var a = 1 + var b = 2 + add(a, b) + print(a) # Prints 3 +``` + +You've probably already seen some function arguments that don't declare a +convention. by default, all arguments are `borrowed`. +In the following sections, we'll explain each of these argument conventions in +more detail. + +## Borrowed arguments (`borrowed`) + +The `borrowed` convention is the default for all arguments. But `def` and `fn` +functions treat `borrowed` arguments somewhat differently: + +* In a [`def` function](/mojo/manual/functions#def-functions), if you mutate + the value in the body of the function, the function receives a mutable copy of + the argument. Otherwise, it receives an immutable reference. This allows you + to treat arguments as mutable, but avoid the overhead of making extra copies when + they're not needed. + +* In an [`fn` function](/mojo/manual/functions#fn-functions), the function + always receives an immutable reference. If you want a mutable copy, you can + assign it to a local variable: + + ```mojo + var my_copy = borrowed_arg + ``` + +In both cases, the original value on the caller side can't be changed by the +callee. + +For example: + +```mojo +from collections import List + +def print_list(list: List[Int]): + print(list.__str__()) + +var list = List(1, 2, 3, 4) +print_list(list) + +``` + +```output +[1, 2, 3, 4] +``` + +Here the `list` argument to `print_list()` is borrowed and not mutated, so the +`print_list()` function gets an immutable reference to the original `List`, and +doesn't do any copying. + +In general, passing an immutable reference is much more efficient +when handling large or expensive-to-copy values, because the copy constructor +and destructor are not invoked for a borrow. + +### Compared to C++ and Rust + +Mojo's borrowed argument convention is similar in some ways to passing an +argument by `const&` in C++, which also avoids a copy of the value and disables +mutability in the callee. However, the borrowed convention differs from +`const&` in C++ in two important ways: + +* The Mojo compiler implements a lifetime checker that ensures that values are + not destroyed when there are outstanding references to those values. + +* Small values like `Int`, `Float`, and `SIMD` are passed directly in machine + registers instead of through an extra indirection (this is because they are + declared with the `@register_passable` decorator). This is a [significant + performance + enhancement](https://www.forrestthewoods.com/blog/should-small-rust-structs-be-passed-by-copy-or-by-borrow/) + when compared to languages like C++ and Rust, and moves this optimization from + every call site to a declaration on the type definition. + +The major difference between Rust and Mojo is that Mojo does not require a +sigil on the caller side to pass by borrow. Also, Mojo is more efficient when +passing small values, and Rust defaults to moving values instead of passing +them around by borrow. These policy and syntax decisions allow Mojo to provide +an easier-to-use programming model. + +## Mutable arguments (`inout`) + +If you'd like your function to receive a **mutable reference**, add the `inout` +keyword in front of the argument name. You can think of `inout` like this: it +means any changes to the value *in*side the function are visible *out*side the +function. + +For example, this `mutate()` function updates the original `list` value: + +```mojo +from collections import List + +def mutate(inout l: List[Int]): + l.append(5) + +var list = List(1, 2, 3, 4) + +mutate(list) +print_list(list) +``` + +```output +[1, 2, 3, 4, 5] +``` + +That behaves like an optimized replacement for this: + +```mojo +from collections import List + +def mutate_copy(l: List[Int]) -> List[Int]: + l.append(5) + return l + +var list = List(1, 2, 3, 4) +list = mutate_copy(list) +print_list(list) +``` + +```output +[1, 2, 3, 4, 5] +``` + +Although the code using `inout` isn't that much shorter, it's more memory +efficient because it does not make a copy of the value. + +However, remember that the values passed as `inout` must already be mutable. +For example, if you try to take a `borrowed` value and pass it to another +function as `inout`, you'll get a compiler error because Mojo can't form a +mutable reference from an immutable reference. + +:::note + +You cannot define [default +values](/mojo/manual/functions#optional-arguments) for `inout` +arguments. + +::: + +### Argument exclusivity + +Mojo enforces *argument exclusivity* for mutable references. This means that if +a function receives a mutable reference to a value (such as an `inout` argument), +it can't receive any other references to the same value—mutable or immutable. +That is, a mutable reference can't have any other references that *alias* it. + +For example, consider the following code example: + +```mojo +fn append_twice(inout s: String, other: String): + # Mojo knows 's' and 'other' cannot be the same string. + s += other + s += other + +fn invalid_access(): + var my_string = str("o") + + # error: passing `my_string` inout is invalid since it is also passed + # borrowed. + append_twice(my_string, my_string) + print(my_string) +``` + +This code is confusing because the user might expect the output to be `ooo`, +but since the first addition mutates both `s` and `other`, the actual output +would be `oooo`. Enforcing exclusivity of mutable references not only prevents +coding errors, it also allows the Mojo compiler to optimize code in some cases. + +One way to avoid this issue when you do need both a mutable and an immutable +reference (or need to pass the same value to two arguments) is to make a copy: + +```mojo +fn valid_access(): + var my_string = str("o") + var other_string = str(my_string) + append_twice(my_string, other_string) + print(my_string) +``` + +Note that argument exclusivity isn't enforced for register-passable trivial +types (like `Int` and `Bool`), because they are always passed by copy. When +passing the same value into two `Int` arguments, the callee will receive two +copies of the value. + +## Transfer arguments (`owned` and `^`) + +And finally, if you'd like your function to receive value **ownership**, add the +`owned` keyword in front of the argument name. + +This convention is often combined with use of the postfixed `^` "transfer" +sigil on the variable that is passed into the function, which ends the +lifetime of that variable. + +Technically, the `owned` keyword does not guarantee that the received value is +*the original value*—it guarantees only that the function +gets unique ownership of a value. This happens in one of +three ways: + +* The caller passes the argument with the `^` transfer sigil, which ends the + lifetime of that variable (the variable becomes uninitialized) and ownership is + transferred into the function without making a copy of any heap-allocated data. + +* The caller **does not** use the `^` transfer sigil, in which case, the + value is copied into the function argument and the original variable remains + valid. (If the original value is not used again, the compiler may optimize away + the copy and transfer the value). + +* The caller passes in a newly-created "owned" value, such as a value returned + from a function. In this case, no variable owns the value and it can be + transferred directly to the callee. For example: + + ```mojo + def take(owned s: String): + pass + + take(String("A brand-new String!")) + ``` + +For example, the following code works by making a copy of the string, +because—although `take_text()` uses the `owned` convention—the caller does not +include the transfer sigil: + +```mojo +fn take_text(owned text: String): + text += "!" + print(text) + +fn my_function(): + var message: String = "Hello" + take_text(message) + print(message) + +my_function() +``` + +```output +Hello! +Hello +``` + +However, if you add the `^` transfer sigil when calling `take_text()`, the +compiler complains about `print(message)`, because at that point, the `message` +variable is no longer initialized. That is, this version does not compile: + +```mojo +fn my_function(): + var message: String = "Hello" + take_text(message^) + print(message) # ERROR: The `message` variable is uninitialized +``` + +This is a critical feature of Mojo's lifetime checker, because it ensures that no +two variables can have ownership of the same value. To fix the error, you must +not use the `message` variable after you end its lifetime with the `^` transfer +operator. So here is the corrected code: + +```mojo + +fn my_function(): + var message: String = "Hello" + take_text(message^) + +my_function() +``` + +```output +Hello! +``` + +Regardless of how it receives the value, when the function declares an argument +as `owned`, it can be certain that it has unique mutable access to that value. +Because the value is owned, the value is destroyed when the function +exits—unless the function transfers the value elsewhere. + +For example, in the following example, `add_to_list()` takes a string and +appends it to the list. Ownership of the string is transferred to the list, so +it's not destroyed when the function exits. On the other hand, +`consume_string()` doesn't transfer its `owned` value out, so the value is +destroyed at the end of the function. + +```mojo +from collections import List + +def add_to_list(owned name: String, inout list: List[String]): + list.append(name^) + # name is uninitialized, nothing to destroy + +def consume_string(owned s: String): + print(s) + # s is destroyed here +``` + +:::note + +Value lifetimes are not fully implemented for top-level code in +Mojo's REPL, so the transfer sigil currently works as intended only when +used inside a function. + +::: + +### Borrowed versus owned in `def` functions + +The difference between `borrowed` and `owned` in a `def` function may be a +little subtle. In both cases, you can end up with a uniquely-owned value that's +a copy of the original value. + +* The `borrowed` argument always gets an immutable reference or a local copy. + You can't transfer a value into a `borrowed` argument. + +* The `owned` argument always gets a uniquely owned value, which may have been + copied or transferred from the callee. Using `owned` arguments without the + transfer sigil (`^`) usually results in values being copied. + +### Transfer implementation details + +In Mojo, you shouldn't conflate "ownership transfer" with a "move +operation"—these are not strictly the same thing. + +There are multiple ways that Mojo can transfer ownership of a value: + +* If a type implements the [move + constructor](/mojo/manual/lifecycle/life#move-constructor), + `__moveinit__()`, Mojo may invoke this method *if* a value of that type is + transferred into a function as an `owned` argument, *and* the original + variable's lifetime ends at the same point (with or without use of the `^` + transfer sigil). + +* If a type implements the [copy + constructor](/mojo/manual/lifecycle/life#move-constructor), `__copyinit__()` + and not `__moveinit__()`, Mojo may copy the value and destroy the old value. + +* In some cases, Mojo can optimize away the move operation entirely, leaving the + value in the same memory location but updating its ownership. In these cases, + a value can be transferred without invoking either the `__copyinit__()` or + `__moveinit__()` constructors. + +In order for the `owned` convention to work *without* the transfer +sigil, the value type must be copyable (via `__copyinit__()`). + +## Comparing `def` and `fn` argument conventions + +As mentioned in the section about +[functions](/mojo/manual/functions), `def` and `fn` functions +are interchangeable, as far as a caller is concerned, and they can both +accomplish the same things. It's only the inside that differs, and Mojo's `def` +function is essentially just sugaring for the `fn` function: + +* A `def` argument without a type annotation defaults to + [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn` + requires all types be explicitly declared). + +* A `def` function can treat a `borrowed` argument as mutable (in which case it + receives a mutable copy). An `fn` function must make this copy explicitly. + +For example, these two functions have the exact same behavior. + +```mojo +def def_example(a: Int, inout b: Int, owned c): + pass + +fn fn_example(a_in: Int, inout b: Int, owned c: object): + var a = a_in + pass +``` + +This shadow copy typically adds no overhead, because small types +like `object` are cheap to copy. However, copying large types that allocate heap +storage can be expensive. (For example, copying `List` or `Dict` types, or +copying large numbers of strings.) diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb deleted file mode 100644 index de3c812464..0000000000 --- a/docs/manual/values/value-semantics.ipynb +++ /dev/null @@ -1,431 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Value semantics\n", - "sidebar_position: 2\n", - "description: An explanation of Mojo's value-semantic defaults.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo doesn't enforce value semantics or reference semantics. It supports them\n", - "both and allows each type to define how it is created, copied, and moved (if at\n", - "all). So, if you're building your own type, you can implement it to support\n", - "value semantics, reference semantics, or a bit of both. That said, Mojo is\n", - "designed with argument behaviors that default to value semantics, and it\n", - "provides tight controls for reference semantics that avoid memory errors.\n", - "\n", - "The controls over reference semantics are provided by the [value ownership\n", - "model](/mojo/manual/values/ownership), but before we get into the syntax\n", - "and rules for that, it's important that you understand the principles of value\n", - "semantics. Generally, it means that each variable has unique access to a value,\n", - "and any code outside the scope of that variable cannot modify its value." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Intro to value semantics" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the most basic situation, sharing a value-semantic type means that you create\n", - "a copy of the value. This is also known as \"pass by value.\" For example,\n", - "consider this code:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n" - ] - } - ], - "source": [ - "x = 1\n", - "y = x\n", - "y += 1\n", - "\n", - "print(x)\n", - "print(y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We assigned the value of `x` to `y`, which creates the value for `y` by making a\n", - "copy of `x`. When we increment `y`, the value of `x` doesn't change. Each\n", - "variable has exclusive ownership of a value.\n", - "\n", - "Whereas, if a type instead uses reference semantics, then `y` would point to\n", - "the same value as `x`, and incrementing either one would affect the value for\n", - "both. Neither `x` nor `y` would \"own\" the value, and any variable would be\n", - "allowed to reference it and mutate it.\n", - "\n", - "Numeric values in Mojo are value semantic because they're trivial types, which\n", - "are cheap to copy. \n", - "\n", - "Here's another example with a function:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2\n", - "1\n" - ] - } - ], - "source": [ - "def add_one(y: Int):\n", - " y += 1\n", - " print(y)\n", - "\n", - "x = 1\n", - "add_one(x)\n", - "print(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, the `y` value is a copy and the function cannot modify the original `x`\n", - "value.\n", - "\n", - "If you're familiar with Python, this is probably familiar so far, because the\n", - "code above behaves the same in Python. However, Python is not value semantic.\n", - "\n", - "It gets complicated, but let's consider a situation in which you call a Python\n", - "function and pass an object with a pointer to a heap-allocated value. Python\n", - "actually gives that function a reference to your object, which allows the\n", - "function to mutate the heap-allocated value. This can cause nasty bugs if\n", - "you're not careful, because the function might incorrectly assume it has unique\n", - "ownership of that object.\n", - "\n", - "In Mojo, the default behavior for all function arguments is to use value\n", - "semantics. If the function wants to modify the value of an incoming argument,\n", - "then it must explicitly declare so, which avoids accidental mutations of the\n", - "original value.\n", - "\n", - "All Mojo types passed to a `def` function can be treated as mutable,\n", - "which maintains the expected mutability behavior from Python. But by default, it\n", - "is mutating a uniquely-owned value, not the original value.\n", - "\n", - "For example, when you pass an instance of a `SIMD` vector to a `def`\n", - "function it creates a unique copy of all values. Thus, if we modify the\n", - "argument in the function, the original value is unchanged:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[9, 2, 3, 4]\n", - "[1, 2, 3, 4]\n" - ] - } - ], - "source": [ - "def update_simd(t: SIMD[DType.int32, 4]):\n", - " t[0] = 9\n", - " print(t)\n", - "\n", - "v = SIMD[DType.int32, 4](1, 2, 3, 4)\n", - "update_simd(v)\n", - "print(v)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If this were Python code, the function would modify the original object, because\n", - "Python shares a reference to the original object.\n", - "\n", - "However, not all types are inexpensive to copy. Copying a `String` or `List`\n", - "requires allocating heap memory, so we want to avoid copying one by accident.\n", - "When designing a type like this, ideally you want to prevent _implicit_ copies,\n", - "and only make a copy when it's explicitly requested. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Value semantics in `def` vs `fn`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The arguments above are mutable because a [`def`\n", - "function](/mojo/manual/functions#def-functions) has special treatment for\n", - "the default\n", - "[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions).\n", - "\n", - "Whereas, `fn` functions always receive `borrowed` arguments as immutable\n", - "references. This is a memory optimization to avoid making\n", - "unnecessary copies.\n", - "\n", - "For example, let's create another function with the `fn` declaration. In this\n", - "case, the `y` argument is immutable by default, so if the function wants to\n", - "modify the value in the local scope, it needs to make a local copy:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n", - "1\n" - ] - } - ], - "source": [ - "fn add_two(y: Int):\n", - " # y += 2 # This will cause a compiler error because `y` is immutable\n", - " # We can instead make an explicit copy:\n", - " var z = y\n", - " z += 2\n", - " print(z)\n", - "\n", - "x = 1\n", - "add_two(x)\n", - "print(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is all consistent with value semantics because each variable maintains\n", - "unique ownership of its value.\n", - "\n", - "The way the `fn` function receives the `y` value is a \"look but don't touch\"\n", - "approach to value semantics. This is also a more memory-efficient approach when\n", - "dealing with memory-intensive arguments, because Mojo doesn't make any copies\n", - "unless we explicitly make the copies ourselves.\n", - "\n", - "Thus, the default behavior for `def` and `fn` arguments is fully value\n", - "semantic: arguments are either copies or immutable references, and any living\n", - "variable from the callee is not affected by the function.\n", - "\n", - "But we must also allow reference semantics (mutable references) because it's\n", - "how we build performant and memory-efficient programs (making copies of\n", - "everything gets really expensive). The challenge is to introduce reference\n", - "semantics in a way that does not disturb the predictability and safety of value\n", - "semantics.\n", - "\n", - "The way we do that in Mojo is, instead of enforcing that every variable have\n", - "\"exclusive access\" to a value, we ensure that every value has an \"exclusive\n", - "owner,\" and destroy each value when the lifetime of its owner ends. \n", - "\n", - "On the next page about [value\n", - "ownership](/mojo/manual/values/ownership), you'll learn how to modify\n", - "the default argument conventions, and safely use reference semantics so every\n", - "value has only one owner at a time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Python-style reference semantics" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "If you will always use strict type declarations, you\n", - "can skip this section because it only applies to Mojo code using `def`\n", - "functions without type declarations (or values declared as\n", - "[`object`](/mojo/stdlib/builtin/object/object)).\n", - "\n", - ":::\n", - "\n", - "As we said at the top of this page, Mojo doesn't enforce value semantics or\n", - "reference semantics. It's up to each type author to decide how an instance of\n", - "their type should be created, copied, and moved (see [Value\n", - "lifecycle](/mojo/manual/lifecycle/)). Thus, in order to provide compatibility\n", - "with Python, Mojo's `object` type is designed to support Python's style of\n", - "argument passing for functions, which is different from the other types in\n", - "Mojo.\n", - "\n", - "Python's argument-passing convention is called \"pass by object reference.\" This\n", - "means when you pass a variable to a Python function, you actually pass a\n", - "reference to the object, as a value (so it's not strictly reference semantics).\n", - "\n", - "Passing the object reference \"as a value\" means that the argument name is just\n", - "a container that acts like an alias to the original object. If you reassign the\n", - "argument inside the function, it does not affect the caller's original value.\n", - "However, if you modify the object itself (such as call `append()` on a list),\n", - "the change is visible to the original object outside the function.\n", - "\n", - "For example, here's a Python function that receives a list and modifies it:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "func: [1, 2, 3]\n", - "orig: [1, 2, 3]\n" - ] - } - ], - "source": [ - "%%python\n", - "def modify_list(l):\n", - " l.append(3)\n", - " print(\"func:\", l)\n", - "\n", - "ar = [1, 2]\n", - "modify_list(ar)\n", - "print(\"orig:\", ar)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, it looks like the list is \"passed by reference\" because `l`\n", - "modifies the original value.\n", - "\n", - "However, if the Python function instead _assigns_ a value to `l`, it does not\n", - "affect the original value:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "func: [3, 4]\n", - "orig: [1, 2]\n" - ] - } - ], - "source": [ - "%%python\n", - "def change_list(l):\n", - " l = [3, 4]\n", - " print(\"func:\", l)\n", - "\n", - "ar = [1, 2]\n", - "change_list(ar)\n", - "print(\"orig:\", ar)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This demonstrates how a Python argument holds the object reference _as a\n", - "value_: the function can mutate the original value, but it can also assign a\n", - "new object to the argument name." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pass by object reference in Mojo" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although we haven't finished implementing the\n", - "[`object`](/mojo/stdlib/builtin/object/object) type to represent any Mojo\n", - "type, our intention is to do so, and enable \"pass by object reference\" as\n", - "described above for all dynamic types in a `def` function.\n", - "\n", - "That means you can have dynamic typing and \"pass by object reference\" behavior\n", - "by simply writing your Mojo code like Python:\n", - "\n", - "1. Use `def` function declarations.\n", - "2. Don't declare argument types.\n", - "\n", - ":::note TODO\n", - "\n", - "Mojo does not adopt the full syntax of Python yet, and there is a lot to\n", - "do in this department before Mojo supports all of Python's types and behaviors.\n", - "As such, this is a topic that also still needs a lot of documentation.\n", - "\n", - ":::" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/values/value-semantics.mdx b/docs/manual/values/value-semantics.mdx new file mode 100644 index 0000000000..403db993b6 --- /dev/null +++ b/docs/manual/values/value-semantics.mdx @@ -0,0 +1,270 @@ +--- +title: Value semantics +sidebar_position: 2 +description: An explanation of Mojo's value-semantic defaults. +--- + +Mojo doesn't enforce value semantics or reference semantics. It supports them +both and allows each type to define how it is created, copied, and moved (if at +all). So, if you're building your own type, you can implement it to support +value semantics, reference semantics, or a bit of both. That said, Mojo is +designed with argument behaviors that default to value semantics, and it +provides tight controls for reference semantics that avoid memory errors. + +The controls over reference semantics are provided by the [value ownership +model](/mojo/manual/values/ownership), but before we get into the syntax +and rules for that, it's important that you understand the principles of value +semantics. Generally, it means that each variable has unique access to a value, +and any code outside the scope of that variable cannot modify its value. + +## Intro to value semantics + +In the most basic situation, sharing a value-semantic type means that you create +a copy of the value. This is also known as "pass by value." For example, +consider this code: + +```mojo +x = 1 +y = x +y += 1 + +print(x) +print(y) +``` + +```output +1 +2 +``` + +We assigned the value of `x` to `y`, which creates the value for `y` by making a +copy of `x`. When we increment `y`, the value of `x` doesn't change. Each +variable has exclusive ownership of a value. + +Whereas, if a type instead uses reference semantics, then `y` would point to +the same value as `x`, and incrementing either one would affect the value for +both. Neither `x` nor `y` would "own" the value, and any variable would be +allowed to reference it and mutate it. + +Numeric values in Mojo are value semantic because they're trivial types, which +are cheap to copy. + +Here's another example with a function: + +```mojo +def add_one(y: Int): + y += 1 + print(y) + +x = 1 +add_one(x) +print(x) +``` + +```output +2 +1 +``` + +Again, the `y` value is a copy and the function cannot modify the original `x` +value. + +If you're familiar with Python, this is probably familiar so far, because the +code above behaves the same in Python. However, Python is not value semantic. + +It gets complicated, but let's consider a situation in which you call a Python +function and pass an object with a pointer to a heap-allocated value. Python +actually gives that function a reference to your object, which allows the +function to mutate the heap-allocated value. This can cause nasty bugs if +you're not careful, because the function might incorrectly assume it has unique +ownership of that object. + +In Mojo, the default behavior for all function arguments is to use value +semantics. If the function wants to modify the value of an incoming argument, +then it must explicitly declare so, which avoids accidental mutations of the +original value. + +All Mojo types passed to a `def` function can be treated as mutable, +which maintains the expected mutability behavior from Python. But by default, it +is mutating a uniquely-owned value, not the original value. + +For example, when you pass an instance of a `SIMD` vector to a `def` +function it creates a unique copy of all values. Thus, if we modify the +argument in the function, the original value is unchanged: + +```mojo +def update_simd(t: SIMD[DType.int32, 4]): + t[0] = 9 + print(t) + +v = SIMD[DType.int32, 4](1, 2, 3, 4) +update_simd(v) +print(v) +``` + +```output +[9, 2, 3, 4] +[1, 2, 3, 4] +``` + +If this were Python code, the function would modify the original object, because +Python shares a reference to the original object. + +However, not all types are inexpensive to copy. Copying a `String` or `List` +requires allocating heap memory, so we want to avoid copying one by accident. +When designing a type like this, ideally you want to prevent *implicit* copies, +and only make a copy when it's explicitly requested. + +### Value semantics in `def` vs `fn` + +The arguments above are mutable because a [`def` +function](/mojo/manual/functions#def-functions) has special treatment for +the default +[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions). + +Whereas, `fn` functions always receive `borrowed` arguments as immutable +references. This is a memory optimization to avoid making +unnecessary copies. + +For example, let's create another function with the `fn` declaration. In this +case, the `y` argument is immutable by default, so if the function wants to +modify the value in the local scope, it needs to make a local copy: + +```mojo +fn add_two(y: Int): + # y += 2 # This will cause a compiler error because `y` is immutable + # We can instead make an explicit copy: + var z = y + z += 2 + print(z) + +x = 1 +add_two(x) +print(x) +``` + +```output +3 +1 +``` + +This is all consistent with value semantics because each variable maintains +unique ownership of its value. + +The way the `fn` function receives the `y` value is a "look but don't touch" +approach to value semantics. This is also a more memory-efficient approach when +dealing with memory-intensive arguments, because Mojo doesn't make any copies +unless we explicitly make the copies ourselves. + +Thus, the default behavior for `def` and `fn` arguments is fully value +semantic: arguments are either copies or immutable references, and any living +variable from the callee is not affected by the function. + +But we must also allow reference semantics (mutable references) because it's +how we build performant and memory-efficient programs (making copies of +everything gets really expensive). The challenge is to introduce reference +semantics in a way that does not disturb the predictability and safety of value +semantics. + +The way we do that in Mojo is, instead of enforcing that every variable have +"exclusive access" to a value, we ensure that every value has an "exclusive +owner," and destroy each value when the lifetime of its owner ends. + +On the next page about [value +ownership](/mojo/manual/values/ownership), you'll learn how to modify +the default argument conventions, and safely use reference semantics so every +value has only one owner at a time. + +## Python-style reference semantics + +:::note + +If you will always use strict type declarations, you +can skip this section because it only applies to Mojo code using `def` +functions without type declarations (or values declared as +[`object`](/mojo/stdlib/builtin/object/object)). + +::: + +As we said at the top of this page, Mojo doesn't enforce value semantics or +reference semantics. It's up to each type author to decide how an instance of +their type should be created, copied, and moved (see [Value +lifecycle](/mojo/manual/lifecycle/)). Thus, in order to provide compatibility +with Python, Mojo's `object` type is designed to support Python's style of +argument passing for functions, which is different from the other types in +Mojo. + +Python's argument-passing convention is called "pass by object reference." This +means when you pass a variable to a Python function, you actually pass a +reference to the object, as a value (so it's not strictly reference semantics). + +Passing the object reference "as a value" means that the argument name is just +a container that acts like an alias to the original object. If you reassign the +argument inside the function, it does not affect the caller's original value. +However, if you modify the object itself (such as call `append()` on a list), +the change is visible to the original object outside the function. + +For example, here's a Python function that receives a list and modifies it: + +```mojo +%%python +def modify_list(l): + l.append(3) + print("func:", l) + +ar = [1, 2] +modify_list(ar) +print("orig:", ar) +``` + +```output +func: [1, 2, 3] +orig: [1, 2, 3] +``` + +In this example, it looks like the list is "passed by reference" because `l` +modifies the original value. + +However, if the Python function instead *assigns* a value to `l`, it does not +affect the original value: + +```mojo +%%python +def change_list(l): + l = [3, 4] + print("func:", l) + +ar = [1, 2] +change_list(ar) +print("orig:", ar) +``` + +```output +func: [3, 4] +orig: [1, 2] +``` + +This demonstrates how a Python argument holds the object reference *as a +value*: the function can mutate the original value, but it can also assign a +new object to the argument name. + +### Pass by object reference in Mojo + +Although we haven't finished implementing the +[`object`](/mojo/stdlib/builtin/object/object) type to represent any Mojo +type, our intention is to do so, and enable "pass by object reference" as +described above for all dynamic types in a `def` function. + +That means you can have dynamic typing and "pass by object reference" behavior +by simply writing your Mojo code like Python: + +1. Use `def` function declarations. +2. Don't declare argument types. + +:::note TODO + +Mojo does not adopt the full syntax of Python yet, and there is a lot to +do in this department before Mojo supports all of Python's types and behaviors. +As such, this is a topic that also still needs a lot of documentation. + +::: diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb deleted file mode 100644 index 68b87ce68e..0000000000 --- a/docs/manual/variables.ipynb +++ /dev/null @@ -1,493 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Variables\n", - "sidebar_position: 3\n", - "description: Introduction to Mojo variables.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A variable is a name that holds a value or object. All variables in Mojo are \n", - "mutable—their value can be changed. (If you want to define a constant value that\n", - "can't change at runtime, see the \n", - "[`alias` keyword](/mojo/manual/parameters/#alias-named-parameter-expressions).)\n", - "\n", - "Mojo has two kinds of variables:\n", - "\n", - "- Explicitly-declared variables are created with the `var` keyword, and may include\n", - " [type annotations](#type-annotations).\n", - "\n", - " ```mojo\n", - " var a = 5\n", - " var b: Float64 = 3.14\n", - " ```\n", - " \n", - "- Implicitly-declared variables are created with an assignment statement:\n", - "\n", - " ```mojo\n", - " a = 5\n", - " b = 3.14\n", - " ```\n", - "\n", - "Both types of variables are strongly-typed: the variable receives a type when\n", - "it's created, and the type never changes. You can't assign a variable a value of\n", - "a different type:\n", - "\n", - "```mojo\n", - "count = 8 # count is type Int\n", - "count = \"Nine?\" # Error: can't implicitly convert 'StringLiteral' to 'Int'\n", - "```\n", - "\n", - "Some types support [_implicit conversions_](#implicit-type-conversion) from\n", - "other types. For example, an integer value can implicitly convert to a\n", - "floating-point value:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "99.0\n" - ] - } - ], - "source": [ - "var temperature: Float64 = 99\n", - "print(temperature)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, the `temperature` variable is explicitly typed as `Float64`,\n", - "but assigned an integer value, so the value is implicitly converted to a \n", - "`Float64`. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Implicitly-declared variables\n", - "\n", - "You can create a variable with just a name and a value. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "name = String(\"Sam\")\n", - "user_id = 0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Implicitly-declared variables are strongly typed: they take the type from the\n", - "first value assigned to them. For example, the `user_id` variable above is type\n", - "`Int`, while the `name` variable is type `String`. You can't assign a string to\n", - "`user_id` or an integer to `name`.\n", - "\n", - "Implicitly-declared variables are scoped at the function level. You create an\n", - "implicitly-declared variable the first time you assign a value to a given name\n", - "inside a function. Any subsequent references to that name inside the function\n", - "refer to the same variable. For more information, see [Variable\n", - "scopes](#variable-scopes), which describes how variable scoping differs between\n", - "explicitly- and implicitly-declared variables." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Explicitly-declared variables\n", - "\n", - "You can declare a variable with the `var` keyword. For example:\n", - "\n", - "```mojo\n", - "var name = String(\"Sam\")\n", - "var user_id: Int\n", - "```\n", - "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", - "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", - "explicitly-declared variables are typed—either explicitly with a \n", - "[type annotation](#type-annotations) or implicitly when they're initialized with\n", - "a value.\n", - "\n", - "Since variables are strongly typed, you can't assign a variable a\n", - "value of a different type, unless those types can be \n", - "[implicitly converted](#implicit-type-conversion). For example, this code will\n", - "not compile:\n", - "\n", - "```mojo\n", - "var user_id: Int = \"Sam\"\n", - "```\n", - "\n", - "There are several main differences between explicitly-declared variables and\n", - "implicitly-declared variables:\n", - "\n", - "- An explicitly-declared variable can be declared without initializing it:\n", - "\n", - " ```mojo\n", - " var value: Float64\n", - " ```\n", - "\n", - "- Explicitly-declared variables follow [lexical scoping](#variable-scopes),\n", - " unlike implicitly-declared variables." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Type annotations" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although Mojo can infer a variable type from the first value assigned to a \n", - "variable, it also supports static type annotations on variables. Type \n", - "annotations provide a more explicit way of specifying the variable's type.\n", - "\n", - "To specify the type for a variable, add a colon followed by the type name:\n", - "\n", - "```mojo\n", - "var name: String = get_name()\n", - "```\n", - "\n", - "This makes it clear that `name` is type `String`, without knowing what the \n", - "`get_name()` function returns. The `get_name()` function may return a `String`,\n", - "or a value that's implicitly convertible to a `String`.\n", - "\n", - ":::note\n", - "\n", - "You must declare a variable with `var` to use type annotations.\n", - "\n", - ":::\n", - "\n", - "If a type has a constructor with just one argument, you can initialize it in\n", - "two ways:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "var name1: String = \"Sam\"\n", - "var name2 = String(\"Sam\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Both of these lines invoke the same constructor to create a `String` from a\n", - "`StringLiteral`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Late initialization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using type annotations allows for late initialization. For example, notice here\n", - "that the `z` variable is first declared with just a type, and the value is\n", - "assigned later:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "fn my_function(x: Int):\n", - " var z: Float32\n", - " if x != 0:\n", - " z = 1.0\n", - " else:\n", - " z = foo()\n", - " print(z)\n", - "\n", - "fn foo() -> Float32:\n", - " return 3.14" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you try to pass an uninitialized variable to a function or use\n", - "it on the right-hand side of an assignment statement, compilation fails.\n", - "\n", - "```mojo\n", - "var z: Float32\n", - "var y = z # Error: use of uninitialized value 'z'\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "Late initialization works only if the variable is declared with a\n", - "type.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Implicit type conversion" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Some types include built-in type conversion (type casting) from one type into\n", - "its own type. For example, if you assign an integer to a variable that has a \n", - "floating-point type, it converts the value instead of giving a compiler error:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n" - ] - } - ], - "source": [ - "var number: Float64 = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As shown above, value assignment can be converted into a constructor call if the \n", - "target type has a constructor that takes a single argument that matches the\n", - "value being assigned. So, this code uses the `Float64` constructor that takes an\n", - "integer: `__init__(out self, value: Int)`.\n", - "\n", - "In general, implicit conversions should only be supported where the conversion\n", - "is lossless.\n", - "\n", - "Implicit conversion follows the logic of [overloaded\n", - "functions](/mojo/manual/functions#overloaded-functions). If the destination\n", - "type has a single-argument constructor that takes an argument of the source\n", - "type, it can be invoked for implicit conversion. \n", - "\n", - "So assigning an integer to a `Float64` variable is exactly the same as this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "var number = Float64(1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Similarly, if you call a function that requires an argument of a certain type \n", - "(such as `Float64`), you can pass in any value as long as that value type can\n", - "implicitly convert to the required type (using one of the type's overloaded\n", - "constructors).\n", - "\n", - "For example, you can pass an `Int` to a function that expects a `Float64`,\n", - "because `Float64` includes a constructor that takes an `Int`:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "fn take_float(value: Float64):\n", - " print(value)\n", - "\n", - "fn pass_integer():\n", - " var value: Int = 1\n", - " take_float(value)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more details on implicit conversion, see \n", - "[Constructors and implicit \n", - "conversion](/mojo/manual/lifecycle/life/#constructors-and-implicit-conversion)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Variable scopes\n", - "\n", - "Variables declared with `var` are bound by *lexical scoping*. This\n", - "means that nested code blocks can read and modify variables defined in an\n", - "outer scope. But an outer scope **cannot** read variables defined in an\n", - "inner scope at all.\n", - "\n", - "For example, the `if` code block shown here creates an inner scope where outer\n", - "variables are accessible to read/write, but any new variables do not live\n", - "beyond the scope of the `if` block:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "num: 1\n", - "num: 2\n", - "num: 1\n", - "dig: 2\n" - ] - } - ], - "source": [ - "def lexical_scopes():\n", - " var num = 1\n", - " var dig = 1\n", - " if num == 1:\n", - " print(\"num:\", num) # Reads the outer-scope \"num\"\n", - " var num = 2 # Creates new inner-scope \"num\"\n", - " print(\"num:\", num) # Reads the inner-scope \"num\"\n", - " dig = 2 # Updates the outer-scope \"dig\"\n", - " print(\"num:\", num) # Reads the outer-scope \"num\"\n", - " print(\"dig:\", dig) # Reads the outer-scope \"dig\"\n", - "\n", - "lexical_scopes()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `var` statement inside the `if` creates a **new** variable with the same name as the outer variable. This prevents the inner loop from accessing the outer `num` variable. (This is called \"variable shadowing,\" where the inner scope variable hides or \"shadows\" a variable from an outer scope.)\n", - "\n", - "The lifetime of the inner `num` ends exactly where the `if` code block ends,\n", - "because that's the scope in which the variable was defined.\n", - "\n", - "This is in contrast to implicitly-declared variables (those without the `var`\n", - "keyword), which use **function-level scoping** (consistent with Python variable\n", - "behavior). That means, when you change the value of an implicitly-declared\n", - "variable inside the `if` block, it actually changes the value for the entire\n", - "function.\n", - "\n", - "For example, here's the same code but *without* the `var` declarations:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n", - "2\n" - ] - } - ], - "source": [ - "def function_scopes():\n", - " num = 1\n", - " if num == 1:\n", - " print(num) # Reads the function-scope \"num\"\n", - " num = 2 # Updates the function-scope variable\n", - " print(num) # Reads the function-scope \"num\"\n", - " print(num) # Reads the function-scope \"num\"\n", - "\n", - "function_scopes()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, the last `print()` function sees the updated `num` value from the inner\n", - "scope, because implicitly-declared variables (Python-style variables) use function-level\n", - "scope (instead of lexical scope)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/manual/variables.mdx b/docs/manual/variables.mdx new file mode 100644 index 0000000000..6b77b88f0f --- /dev/null +++ b/docs/manual/variables.mdx @@ -0,0 +1,299 @@ +--- +title: Variables +sidebar_position: 3 +description: Introduction to Mojo variables. +--- + +A variable is a name that holds a value or object. All variables in Mojo are +mutable—their value can be changed. (If you want to define a constant value that +can't change at runtime, see the +[`alias` keyword](/mojo/manual/parameters/#alias-named-parameter-expressions).) + +Mojo has two kinds of variables: + +* Explicitly-declared variables are created with the `var` keyword, and may include + [type annotations](#type-annotations). + + ```mojo + var a = 5 + var b: Float64 = 3.14 + ``` + +* Implicitly-declared variables are created with an assignment statement: + + ```mojo + a = 5 + b = 3.14 + ``` + +Both types of variables are strongly-typed: the variable receives a type when +it's created, and the type never changes. You can't assign a variable a value of +a different type: + +```mojo +count = 8 # count is type Int +count = "Nine?" # Error: can't implicitly convert 'StringLiteral' to 'Int' +``` + +Some types support [*implicit conversions*](#implicit-type-conversion) from +other types. For example, an integer value can implicitly convert to a +floating-point value: + +```mojo +var temperature: Float64 = 99 +print(temperature) +``` + +```output +99.0 +``` + +In this example, the `temperature` variable is explicitly typed as `Float64`, +but assigned an integer value, so the value is implicitly converted to a +`Float64`. + +## Implicitly-declared variables + +You can create a variable with just a name and a value. For example: + +```mojo +name = String("Sam") +user_id = 0 +``` + +Implicitly-declared variables are strongly typed: they take the type from the +first value assigned to them. For example, the `user_id` variable above is type +`Int`, while the `name` variable is type `String`. You can't assign a string to +`user_id` or an integer to `name`. + +Implicitly-declared variables are scoped at the function level. You create an +implicitly-declared variable the first time you assign a value to a given name +inside a function. Any subsequent references to that name inside the function +refer to the same variable. For more information, see [Variable +scopes](#variable-scopes), which describes how variable scoping differs between +explicitly- and implicitly-declared variables. + +## Explicitly-declared variables + +You can declare a variable with the `var` keyword. For example: + +```mojo +var name = String("Sam") +var user_id: Int +``` + +The `name` variable is initialized to the string "Sam". The `user_id` variable +is uninitialized, but it has a declared type, `Int` for an integer value. All +explicitly-declared variables are typed—either explicitly with a +[type annotation](#type-annotations) or implicitly when they're initialized with +a value. + +Since variables are strongly typed, you can't assign a variable a +value of a different type, unless those types can be +[implicitly converted](#implicit-type-conversion). For example, this code will +not compile: + +```mojo +var user_id: Int = "Sam" +``` + +There are several main differences between explicitly-declared variables and +implicitly-declared variables: + +* An explicitly-declared variable can be declared without initializing it: + + ```mojo + var value: Float64 + ``` + +* Explicitly-declared variables follow [lexical scoping](#variable-scopes), + unlike implicitly-declared variables. + +## Type annotations + +Although Mojo can infer a variable type from the first value assigned to a +variable, it also supports static type annotations on variables. Type +annotations provide a more explicit way of specifying the variable's type. + +To specify the type for a variable, add a colon followed by the type name: + +```mojo +var name: String = get_name() +``` + +This makes it clear that `name` is type `String`, without knowing what the +`get_name()` function returns. The `get_name()` function may return a `String`, +or a value that's implicitly convertible to a `String`. + +:::note + +You must declare a variable with `var` to use type annotations. + +::: + +If a type has a constructor with just one argument, you can initialize it in +two ways: + +```mojo +var name1: String = "Sam" +var name2 = String("Sam") +``` + +Both of these lines invoke the same constructor to create a `String` from a +`StringLiteral`. + +### Late initialization + +Using type annotations allows for late initialization. For example, notice here +that the `z` variable is first declared with just a type, and the value is +assigned later: + +```mojo +fn my_function(x: Int): + var z: Float32 + if x != 0: + z = 1.0 + else: + z = foo() + print(z) + +fn foo() -> Float32: + return 3.14 +``` + +If you try to pass an uninitialized variable to a function or use +it on the right-hand side of an assignment statement, compilation fails. + +```mojo +var z: Float32 +var y = z # Error: use of uninitialized value 'z' +``` + +:::note + +Late initialization works only if the variable is declared with a +type. + +::: + +### Implicit type conversion + +Some types include built-in type conversion (type casting) from one type into +its own type. For example, if you assign an integer to a variable that has a +floating-point type, it converts the value instead of giving a compiler error: + +```mojo +var number: Float64 = 1 +``` + +```output +1 +``` + +As shown above, value assignment can be converted into a constructor call if the +target type has a constructor that takes a single argument that matches the +value being assigned. So, this code uses the `Float64` constructor that takes an +integer: `__init__(out self, value: Int)`. + +In general, implicit conversions should only be supported where the conversion +is lossless. + +Implicit conversion follows the logic of [overloaded +functions](/mojo/manual/functions#overloaded-functions). If the destination +type has a single-argument constructor that takes an argument of the source +type, it can be invoked for implicit conversion. + +So assigning an integer to a `Float64` variable is exactly the same as this: + +```mojo +var number = Float64(1) +``` + +Similarly, if you call a function that requires an argument of a certain type +(such as `Float64`), you can pass in any value as long as that value type can +implicitly convert to the required type (using one of the type's overloaded +constructors). + +For example, you can pass an `Int` to a function that expects a `Float64`, +because `Float64` includes a constructor that takes an `Int`: + +```mojo +fn take_float(value: Float64): + print(value) + +fn pass_integer(): + var value: Int = 1 + take_float(value) +``` + +For more details on implicit conversion, see +[Constructors and implicit +conversion](/mojo/manual/lifecycle/life/#constructors-and-implicit-conversion). + +## Variable scopes + +Variables declared with `var` are bound by *lexical scoping*. This +means that nested code blocks can read and modify variables defined in an +outer scope. But an outer scope **cannot** read variables defined in an +inner scope at all. + +For example, the `if` code block shown here creates an inner scope where outer +variables are accessible to read/write, but any new variables do not live +beyond the scope of the `if` block: + +```mojo +def lexical_scopes(): + var num = 1 + var dig = 1 + if num == 1: + print("num:", num) # Reads the outer-scope "num" + var num = 2 # Creates new inner-scope "num" + print("num:", num) # Reads the inner-scope "num" + dig = 2 # Updates the outer-scope "dig" + print("num:", num) # Reads the outer-scope "num" + print("dig:", dig) # Reads the outer-scope "dig" + +lexical_scopes() +``` + +```output +num: 1 +num: 2 +num: 1 +dig: 2 +``` + +Note that the `var` statement inside the `if` creates a **new** variable with the same name as the outer variable. This prevents the inner loop from accessing the outer `num` variable. (This is called "variable shadowing," where the inner scope variable hides or "shadows" a variable from an outer scope.) + +The lifetime of the inner `num` ends exactly where the `if` code block ends, +because that's the scope in which the variable was defined. + +This is in contrast to implicitly-declared variables (those without the `var` +keyword), which use **function-level scoping** (consistent with Python variable +behavior). That means, when you change the value of an implicitly-declared +variable inside the `if` block, it actually changes the value for the entire +function. + +For example, here's the same code but *without* the `var` declarations: + +```mojo +def function_scopes(): + num = 1 + if num == 1: + print(num) # Reads the function-scope "num" + num = 2 # Updates the function-scope variable + print(num) # Reads the function-scope "num" + print(num) # Reads the function-scope "num" + +function_scopes() +``` + +```output +1 +2 +2 +``` + +Now, the last `print()` function sees the updated `num` value from the inner +scope, because implicitly-declared variables (Python-style variables) use function-level +scope (instead of lexical scope). diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb deleted file mode 100644 index c84408c1dd..0000000000 --- a/docs/tools/debugging.ipynb +++ /dev/null @@ -1,637 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: Debugging\n", - "sidebar_position: 1\n", - "description: Debugging Mojo programs.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Mojo extension for Visual Studio Code enables you to use VS Code's built-in\n", - "debugger with Mojo programs. (The Mojo extension also supports debugging C, C++,\n", - "and Objective-C.)\n", - "\n", - "For complete coverage of VS Code's debugging features, see\n", - "[Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging).\n", - "\n", - "This page describes the features available through the Mojo extension, as well\n", - "as current limitations of the Mojo debugger.\n", - "\n", - "The Mojo SDK includes the [LLDB debugger](https://lldb.llvm.org/) and a Mojo\n", - "LLDB plugin. Together these provide the low-level debugging interface for the\n", - "Mojo extension. You can also use the `mojo debug` command to start a \n", - "command-line debugging session using LLDB or to launch a Mojo debugging session\n", - "in VS Code.\n", - "\n", - "## Start debugging\n", - "\n", - "There are several ways to start a debug session in VS Code.\n", - "\n", - "To start debugging, you'll need to have a Mojo project to debug. There are\n", - "a number of examples ranging from simple to complex in the [Mojo repo on\n", - "GitHub](https://github.com/modularml/mojo).\n", - "\n", - ":::note **VS Code veteran?**\n", - "\n", - "If you're already familiar with debugging in VS Code, the\n", - "material in this section will mostly be review. You might want to skip ahead to\n", - "[Launch configurations](#launch-configurations)\n", - "or see [Using the debugger](#using-the-debugger) for notes on the features\n", - "supported in the Mojo debugger. \n", - "\n", - ":::\n", - "\n", - "### Quick run or debug\n", - "\n", - "If your active editor tab contains a Mojo file with an `fn main()` entry point,\n", - "one of the quickest ways to run or debug it is using the **Run or Debug** button\n", - "in the Editor toolbar.\n", - "\n", - "![](images/quick-run-or-debug-button.png)\n", - "\n", - "To start debugging the current file:\n", - "\n", - "- Open the **Run or Debug** dropdown menu and choose **Debug Mojo File** or\n", - "**Debug Mojo File in Dedicated Terminal**.\n", - "\n", - " ![](images/quick-run-or-debug-menu.png)\n", - "\n", - "The two debug configurations differ in how they handle input and output:\n", - "\n", - "* **Debug Mojo File** launches the Mojo program detached from any terminal.\n", - "Standard output and standard error output for the program are displayed in the\n", - "**Debug Console**. You can't write to the program's standard input, but you can\n", - "see the program's output and interact with the debugger in a single location.\n", - "\n", - "* **Debug Mojo File in Dedicated Terminal** creates a new instance of VS Code's\n", - "integrated terminal and attaches the program's input and output to the terminal.\n", - "This lets you interact with the program's standard input, standard output and\n", - "standard error output in the terminal, while the **Debug Console** is used only\n", - "for interactions with the debugger.\n", - "\n", - "The **Run or Debug** button uses predefined launch configurations. There's\n", - "currently no way to modify the `args`, `env`, `cwd` or other settings for\n", - "programs launched with the **Run or Debug** configurations. If you need to\n", - "customize any of these things, see [Edit launch\n", - "configurations](#edit-launch-configurations).\n", - "\n", - "After you choose one of the debug configurations, the button updates to show\n", - "the debug symbol. Click the button to re-run the previous configuration.\n", - "\n", - "![](images/quick-run-or-debug-button-debug.png)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Run and Debug view\n", - "\n", - "The **Run and Debug** view includes a button to launch debug sessions and a\n", - "menu to select debug configurations. It also has areas to display current\n", - "variables, watch expressions, the current call stack, and breakpoints.\n", - "\n", - "
    \n", - "\n", - "![](images/run-and-debug-view.png)\n", - "\n", - "
    Figure 1. Run and Debug view
    \n", - "
    \n", - "\n", - "To open **Run and Debug** view, click the **Run and Debug** icon in the\n", - "**Activity Bar** (on the left side of the VS Code window) or press\n", - "Control+Shift+D \n", - "(Command+Shift+D on macOS).\n", - "\n", - "![](images/run-and-debug-icon.png)\n", - "\n", - "If you haven't created any launch configurations in the current project,\n", - "VS Code shows the **Run start view**.\n", - "\n", - "
    \n", - "\n", - "![](images/run-start-view.png)\n", - "\n", - "
    Figure 2. Run start view
    \n", - "
    \n", - "\n", - "If you've already launched a debug session or created a `launch.json` file to\n", - "define launch configurations, you'll see the **Launch configurations** menu,\n", - "which lets you choose configurations and start debug sessions:\n", - "\n", - "
    \n", - "\n", - "![](images/launch-configuration-menu.png)\n", - "\n", - "
    Figure 3. Launch configurations menu
    \n", - "
    \n", - "\n", - "### Other ways to start a debug session\n", - "\n", - "There are a number of other ways to start a debug session.\n", - "\n", - "#### Launching from the Command Palette\n", - "\n", - "If you have a Mojo file open in your active editor, you can also start a debug\n", - "session from the **Command Palette**.\n", - "\n", - "1. Click **View** > **Command Palette** or press Control+Shift+P\n", - "(Command+Shift+P on macOS). \n", - "\n", - "2. Enter \"Mojo\" at the prompt to bring up the Mojo commands. You should see the\n", - "same debug configurations described in [Quick run or\n", - "debug](#quick-run-or-debug).\n", - "\n", - "#### Launch from the File Explorer\n", - "\n", - "To launch a debug session from the the **File Explorer** view:\n", - "\n", - "1. Right-click on a Mojo file.\n", - "2. Select a Mojo debug configuration.\n", - "\n", - "You should see the same debug configurations described in [Quick run or\n", - "debug](#quick-run-or-debug). \n", - "\n", - "#### Debug with F5\n", - "\n", - "Press F5 to start a debug session using the current debug configuration.\n", - "\n", - "If you don't have any existing debug configurations available to select, and\n", - "your active editor contains a Mojo file with an `fn main()` entry point,\n", - "pressing F5 will launch and debug the current file using the **Debug Mojo\n", - "File** action described in [Quick run or debug](#quick-run-or-debug)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Starting the debugger from the command line\n", - "\n", - "Use the `mojo debug` command to start a debug session from the command line. You\n", - "can choose from two debugging interfaces:\n", - "\n", - "- With the `--vscode` flag, `mojo debug` starts a debug session on VS Code if\n", - " it's running and the Mojo extension is enabled.\n", - "\n", - "- Without the `--vscode` flag, `mojo debug` starts a command-line [LLDB \n", - " debugger](https://lldb.llvm.org/) session.\n", - "\n", - "You can choose to build and debug a Mojo file, run and debug a compiled binary,\n", - "or to attach the debugger to a running process.\n", - "\n", - ":::note Environment variables\n", - "\n", - "When you debug a program from the command line using `--vscode`, the program runs\n", - "with the environment variables set in the terminal. When launching from inside\n", - "VS Code via the GUI, the environment is defined by the VS Code [launch \n", - "configuration](#launch-configurations).\n", - "\n", - ":::\n", - "\n", - "For a full list of command-line options, see the [`mojo debug` reference\n", - "page](/mojo/cli/debug).\n", - "\n", - "### Start a debug session from the command line\n", - "\n", - "With VS Code open, run the following command (either from VS Code's integrated\n", - "terminal or an external shell):\n", - "\n", - "```bash\n", - "mojo debug --vscode myproject.mojo\n", - "```\n", - "\n", - "Or to debug a compiled binary:\n", - "\n", - "```bash\n", - "mojo debug --vscode myproject\n", - "```\n", - "\n", - "\n", - "For best results, build with the `-O0 -g` command-line options when you build a\n", - "binary that you intend to debug—this produces a binary with full debug info.\n", - "(When you call `mojo debug` on a Mojo source file, it includes debug\n", - "information by default.) See the [`mojo build` reference page](/mojo/cli/build)\n", - "for details on compilation options.\n", - "\n", - "### Attach the debugger to a running process from the command line\n", - "\n", - "You can also attach the debugger to a running process by specifying either the\n", - "process ID or process name on the command line:\n", - "\n", - "```bash\n", - "mojo debug --vscode --pid \n", - "```\n", - "\n", - "Or:\n", - "\n", - "```bash\n", - "mojo debug --vscode --process-name \n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Launch configurations\n", - "\n", - "VS Code _launch configurations_ let you define setup information for debugging\n", - "your applications.\n", - "\n", - "The Mojo debugger provides the following launch configuration templates:\n", - "\n", - "* Debug current Mojo file. Launches and debugs the Mojo file in the active\n", - "editor tab. Effectively the same as the **Debug Mojo File** action described in\n", - "[Quick run or debug](#quick-run-or-debug), but with more configuration options.\n", - "\n", - "* Debug Mojo file. Like the previous entry, except that it identifies a\n", - "specific file to launch and debug, no matter what file is displayed in the\n", - "active editor.\n", - "\n", - "* Debug binary. This configuration operates on a prebuilt binary, which could\n", - "be written in any mixture of languages supported by LLDB (Mojo, C, C++, etc.).\n", - "You need to set the `program` field to the path of your binary. \n", - "\n", - "* Attach to process. Launches a debug session attached to a running process. On\n", - "launch, you choose the process you want to debug from a list of running\n", - "processes.\n", - "\n", - "You can edit any of these templates to customize them. All VS Code launch\n", - "configurations must contain the following attributes:\n", - "\n", - "- `name`. The name of the launch configuration, which shows up in the UI (for\n", - " example, \"Run current Mojo file\").\n", - "- `request`. Can be either `launch` (to run a program from VS Code) or `attach` \n", - " (to attach to and debug a running file). \n", - "- `type`. Use `mojo-lldb` for the Mojo debugger.\n", - "\n", - "In addition, Mojo launch configurations can contain the following attributes:\n", - "\n", - "- `args`. Any command-line arguments to be passed to the program.\n", - "- `cwd`. The current working directory to run the program in.\n", - "- `description`. A longer description of the configuration, not shown in the UI.\n", - "- `env`. Environment variables to be set before running the program.\n", - "- `mojoFile`. Path to a Mojo file to launch and debug.\n", - "- `pid`. Process ID of the running process to attach to.\n", - "- `program`. Path to a compiled binary to launch and debug, or the \n", - " program to attach to.\n", - "- `runInTerminal`. True to run the program with a dedicated terminal, which\n", - " allows the program to receive standard input from the terminal. False to run\n", - " the program with its output directed to the **Debug Console**.\n", - "\n", - "If configuration is a `launch` request, the configuration must include either\n", - "the `mojoFile` or `program` attribute.\n", - "\n", - "For `attach` requests, the configuration must include either the `pid` or \n", - "`program` attribute.\n", - "\n", - "VS Code performs variable substitution on the launch configurations. You can\n", - "use `${workspaceFolder}` to substitute the path to the current workspace, and\n", - "`${file}` to represent the file in the active editor tab. For a complete list\n", - "of variables, see the VS Code [Variables\n", - "reference](https://code.visualstudio.com/docs/editor/variables-reference).\n", - "\n", - "For more information, see the VS Code documentation for [Launch \n", - "configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations).\n", - "\n", - ":::note Compilation options\n", - "\n", - "Mojo launch configurations don't allow you to specify compilation options. If\n", - "you need to specify compilation options, you can build the binary using [`mojo\n", - "build`](/mojo/cli/build), then use a launch configuration with the `program`\n", - "option to launch the compiled binary. Or if you [start the debugger from the\n", - "command line](#starting-the-debugger-from-the-command-line), you can pass\n", - "compilation options to the `mojo debug` command.\n", - "\n", - ":::\n", - "\n", - "### Edit launch configurations\n", - "\n", - "To edit launch configurations:\n", - "\n", - "1. If the **Run and Debug** view isn't already open, click the **Run and\n", - "Debug** icon in the **Activity Bar** (on the left side of the VS Code window)\n", - "or press Control+Shift+D (Command+Shift+D on macOS).\n", - "\n", - " ![](images/run-and-debug-icon.png)\n", - " \n", - "1. Create or open the `launch.json` file:\n", - " 1. If you see the **Run start view**, click **create a launch.json file**.\n", - " 1. If you already have launch configurations set up, click the gear icon\n", - " next to the **Launch configurations** menu.\n", - " ![](images/launch-configuration-menu.png)\n", - "1. Select **Mojo** from the list of debuggers.\n", - "\n", - "VS Code opens the new `launch.json` file in an editor tab, with templates for\n", - "some common debug actions. Click **Add configuration** to add a new\n", - "configuration template. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using the debugger\n", - "\n", - "When a debug session is running, use the debug toolbar to pause, continue, and\n", - "step through the program.\n", - "\n", - "![](images/debug-toolbar.png)\n", - "\n", - "The buttons on the toolbar are:\n", - "\n", - "- **Continue/Pause**: If the program is stopped, resume the normal execution of the\n", - "program up to the next breakpoint, signal or crash. Otherwise, pause all the\n", - "threads of the program at once.\n", - "\n", - "- **Step Over**: Execute the next line of code without stopping at function calls.\n", - "\n", - "- **Step Into**: Execute the next line of code and stop at the first function call. If the program is stopped just before a function call, steps into the function so you can step through it line-by-line.\n", - "\n", - "- **Step Out**: Finish the execution of the current function and stop right after\n", - "returning to the parent function. \n", - "\n", - "- **Restart**: If this is a `launch` session, terminate the current program and\n", - "restart the debug session. Otherwise, detach from the target process and\n", - "reattach to it. \n", - "\n", - "- **Stop**: If this is a `launch` session, terminate the current program. Otherwise,\n", - "detach from the target process without killing it.\n", - "\n", - "The debugger currently has the following limitations:\n", - "\n", - "- No support for breaking automatically on Mojo errors.\n", - "\n", - "- When stepping out of a function, the returned value is not displayed.\n", - "\n", - "- LLDB doesn’t support stopping or resuming individual threads." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Breakpoints\n", - "\n", - "The Mojo debugger supports setting [standard \n", - "breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints),\n", - "[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints),\n", - "[function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints),\n", - "[data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints),\n", - "and [triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), \n", - "as described in the VS Code documentation.\n", - "The Mojo debugger also supports _error breakpoints_ (also known as \"break on\n", - "raise\"), which break whenever a `raise` statement is executed.\n", - "\n", - "When debugging Mojo code, the debugger doesn't support conditional breakpoints\n", - " based on an expression (it does \n", - "support hit counts, which VS Code classifies as a kind of conditional \n", - "breakpoint).\n", - "\n", - "When editing a breakpoint, you're offered four options:\n", - "\n", - "- **Expression**. Set a conditional breakpoint (not currently supported).\n", - "- **Hit Count**. Add a hit count to a breakpoint (supported).\n", - "- **Log Message**. Add a logpoint (supported)\n", - "- **Wait for Breakpoint**. Add a triggered breakpoint (supported).\n", - "\n", - "#### Set a hit count breakpoint\n", - "\n", - "A hit count breakpoint is a breakpoint that only breaks execution after the\n", - "debugger hits it a specified number of times.\n", - "\n", - "To add a hit count breakpoint:\n", - "\n", - "1. Right click in the left gutter of the editor where you want to place the \n", - " breakpoint, and select **Add Conditional Breakpoint.**\n", - "2. Select **Hit Count** from the menu and enter the desired hit count.\n", - "\n", - "To change an existing breakpoint to a hit count breakpoint:\n", - "\n", - "1. Right click on the breakpoint in the left gutter of the editor and select\n", - " **Edit breakpoint**.\n", - "2. Select **Hit Count** from the menu and enter the desired hit count.\n", - "\n", - "You can also edit a breakpoint from the **Breakpoints** section of the **Run and\n", - "Debug** view:\n", - "\n", - "- Right-click on the breakpoint and select **Edit Condition**, or,\n", - "- Click the **Edit Condition** icon next to the breakpoint.\n", - "\n", - "This brings up the same menu, **next to the breakpoint in the editor tab**.\n", - "\n", - "#### Enable error breakpoints\n", - "\n", - "You can enable and disable error breakpoints in VS Code by selecting \"Mojo\n", - "Raise\" in the **Breakpoints** section of the **Run and Debug** view. If enabled\n", - "during debugging, executing a `raise` statement causes the debugger to stop\n", - "execution and highlight the line of code where the error was raised.\n", - "\n", - "![VS Code window showing a program paused in the debugger with the Run and Debug view visible. The program is paused at a raise statement.](images/break-on-raise.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View local variables\n", - "\n", - "When a program is paused in the debugger, the editor shows local variable values\n", - "inline. You can also find them in the **Variables** section of the **Run and\n", - "Debug** view.\n", - "\n", - "
    \n", - "\n", - "![VS Code window showing a program paused in the debugger, with the variables sections of the Run and Debug view visible. The edit shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2.](images/debugger-variables.png)\n", - "\n", - "
    Figure 4. Local variable values displayed in the debugger
    \n", - "
    \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View the call stack\n", - "\n", - "When a program is paused in the debugger, the **Run and Debug** view shows the\n", - "current call stack. (You may see multiple call stacks, one for each active\n", - "thread in the program.)\n", - "\n", - "
    \n", - "\n", - "![VS Code window showing a program paused in the debugger, with the call stack and variables sections of the Run and Debug view visible. The call stack shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2; the parent function nested1 is selected in the call stack, and editor highlights the current line in nested1 (the call to nested2()).](images/debugger-call-stack-nested1.png)\n", - "\n", - "
    Figure 5. Call stack in Run and Debug view
    \n", - "
    \n", - "\n", - "The **Call Stack** section of the Run and Debug view shows a stack frame for\n", - "each function call in the current call stack. Clicking on the name of the\n", - "function highlights the current line in that function. For example, in Figure\n", - "5, the program is paused at a breakpoint in `nested2()`, but the parent\n", - "function, `nested1()` is selected in the call stack. The editor highlights the\n", - "current line in `nested1()` (that is, the call to `nested2()`) and shows the\n", - "current local variable values for `nested1()`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Use the Debug Console\n", - "\n", - "The **Debug Console** gives you a command-line interface to the debugger. The \n", - "**Debug Console** processes LLDB commands and Mojo expressions.\n", - "\n", - "Anything prefixed with a colon (`:`) is treated as an LLDB command. Any other\n", - "input is treated as an expression.\n", - "\n", - "Currently Mojo expressions are limited to inspecting variables and their fields.\n", - "The console also supports subscript notation (`vector[index]`) for certain data\n", - "structures in the standard library, including `List`, `SIMD`,\n", - "and `ListLiteral`. \n", - "\n", - "In the future, we intend to provide a way for arbitrary data structures to\n", - "support subscript notation in the **Debug Console**.\n", - "\n", - ":::note \n", - "\n", - "The **Debug Console** only accepts input when the program is paused.\n", - "\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Tips and tricks\n", - "\n", - " There are several features in the standard library that aren't directly related\n", - " to the debugger, but which can help you debug your programs. These include:\n", - "\n", - " * Programmatic breakpoints.\n", - " * Setting parameters from the Mojo command line.\n", - "\n", - " ### Set a programmatic breakpoint\n", - "\n", - " To break at a specific point in your code, you can use the built-in\n", - " [`breakpoint()`](/mojo/stdlib/builtin/breakpoint/breakpoint) function:\n", - "\n", - " ```mojo\n", - "if some_value.is_valid():\n", - " do_the_right_thing()\n", - "else:\n", - " # We should never get here!\n", - " breakpoint()\n", - "```\n", - "\n", - "If you have VS Code open and run this code in debug mode (either using VS Code\n", - "or `mojo debug`), hitting the `breakpoint()` call causes an error, which\n", - "triggers the debugger.\n", - "\n", - ":::note Assertions\n", - "\n", - "The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of \n", - "ways to specify assertions. Assertions also trigger an error, so can open the \n", - "debugger in the same way that a `breakpoint()` call will.\n", - "\n", - ":::\n", - "\n", - "### Set parameters from the Mojo command line\n", - "\n", - "You can use the [`param_env`](/mojo/stdlib/sys/param_env/) module to retrieve\n", - "parameter values specified on the Mojo command line. Among other things, this\n", - "is an easy way to switch debugging logic on and off. For example:\n", - "\n", - "```mojo\n", - "from param_env import is_defined\n", - "\n", - "def some_function_with_issues():\n", - " # ...\n", - " @parameter\n", - " if is_defined[\"DEBUG_ME\"]():\n", - " breakpoint()\n", - "```\n", - "\n", - "To activate this code, use the [`-D` command-line\n", - "option](/mojo/cli/debug#compilation-options) to define `DEBUG_ME`:\n", - "\n", - "```bash\n", - "mojo debug -D DEBUG_ME main.mojo\n", - "```\n", - "\n", - "The `is_defined()` function returns a compile-time true or false value based on\n", - "whether the specified name is defined. Since the `breakpoint()` call is inside a\n", - "[parametric `if` statement](/mojo/manual/decorators/parameter#parametric-if-statement),\n", - "it is only included in the compiled code when the `DEBUG_ME` name is defined on\n", - "the command line." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Troubleshooting\n", - "\n", - "### `error: can't connect to the RPC debug server socket`\n", - "\n", - "If using `mojo debug --rpc` gives you the message `error: can't connect to the RPC debug server socket: Connection refused`, try the following possible fixes:\n", - "\n", - " * Make sure VS Code is open.\n", - " * If VS Code is already open, try restarting VS Code.\n", - " * If there are other VS Code windows open, try closing them and then restarting. This error can sometimes occur when multiple windows have opened and closed in certain orders.\n", - "\n", - "### `error: couldn't get a valid response from the RPC server`\n", - "\n", - "If using `mojo debug --rpc` gives you the message `error: couldn't get a valid response from the RPC server`, try the following possible fixes:\n", - "\n", - " * Make sure VS Code is open to a valid Mojo codebase. This error can sometimes happen if the VS Code window is open to some other codebase.\n", - " * If there are multiple VS Code windows open, try closing all but the one you wish to debug in.\n", - " * Restart VS Code.\n", - " * Reinstall the SDK and restart VSCode.\n", - " * If you are working on a development version of the SDK, make sure that all SDK tools are properly built with your build system, and then reload VSCode.\n", - " * As a last resort, restarting your entire computer can fix this problem.\n", - "\n", - "If these steps don't help, please file an issue. We'd love your help identifying possible causes and fixes!" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tools/debugging.mdx b/docs/tools/debugging.mdx new file mode 100644 index 0000000000..8631b5cc71 --- /dev/null +++ b/docs/tools/debugging.mdx @@ -0,0 +1,543 @@ +--- +title: Debugging +sidebar_position: 1 +description: Debugging Mojo programs. +--- + +The Mojo extension for Visual Studio Code enables you to use VS Code's built-in +debugger with Mojo programs. (The Mojo extension also supports debugging C, C++, +and Objective-C.) + +For complete coverage of VS Code's debugging features, see +[Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging). + +This page describes the features available through the Mojo extension, as well +as current limitations of the Mojo debugger. + +The Mojo SDK includes the [LLDB debugger](https://lldb.llvm.org/) and a Mojo +LLDB plugin. Together these provide the low-level debugging interface for the +Mojo extension. You can also use the `mojo debug` command to start a +command-line debugging session using LLDB or to launch a Mojo debugging session +in VS Code. + +## Start debugging + +There are several ways to start a debug session in VS Code. + +To start debugging, you'll need to have a Mojo project to debug. There are +a number of examples ranging from simple to complex in the [Mojo repo on +GitHub](https://github.com/modularml/mojo). + +:::note **VS Code veteran?** + +If you're already familiar with debugging in VS Code, the +material in this section will mostly be review. You might want to skip ahead to +[Launch configurations](#launch-configurations) +or see [Using the debugger](#using-the-debugger) for notes on the features +supported in the Mojo debugger. + +::: + +### Quick run or debug + +If your active editor tab contains a Mojo file with an `fn main()` entry point, +one of the quickest ways to run or debug it is using the **Run or Debug** button +in the Editor toolbar. + +![](images/quick-run-or-debug-button.png) + +To start debugging the current file: + +* Open the **Run or Debug** dropdown menu and choose **Debug Mojo File** or + **Debug Mojo File in Dedicated Terminal**. + + ![](images/quick-run-or-debug-menu.png) + +The two debug configurations differ in how they handle input and output: + +* **Debug Mojo File** launches the Mojo program detached from any terminal. + Standard output and standard error output for the program are displayed in the + **Debug Console**. You can't write to the program's standard input, but you can + see the program's output and interact with the debugger in a single location. + +* **Debug Mojo File in Dedicated Terminal** creates a new instance of VS Code's + integrated terminal and attaches the program's input and output to the terminal. + This lets you interact with the program's standard input, standard output and + standard error output in the terminal, while the **Debug Console** is used only + for interactions with the debugger. + +The **Run or Debug** button uses predefined launch configurations. There's +currently no way to modify the `args`, `env`, `cwd` or other settings for +programs launched with the **Run or Debug** configurations. If you need to +customize any of these things, see [Edit launch +configurations](#edit-launch-configurations). + +After you choose one of the debug configurations, the button updates to show +the debug symbol. Click the button to re-run the previous configuration. + +![](images/quick-run-or-debug-button-debug.png). + +### Run and Debug view + +The **Run and Debug** view includes a button to launch debug sessions and a +menu to select debug configurations. It also has areas to display current +variables, watch expressions, the current call stack, and breakpoints. + +
    + +![](images/run-and-debug-view.png) + +
    Figure 1. Run and Debug view
    +
    + +To open **Run and Debug** view, click the **Run and Debug** icon in the +**Activity Bar** (on the left side of the VS Code window) or press Control+Shift+D +(Command+Shift+D on macOS). + +![](images/run-and-debug-icon.png) + +If you haven't created any launch configurations in the current project, +VS Code shows the **Run start view**. + +
    + +![](images/run-start-view.png) + +
    Figure 2. Run start view
    +
    + +If you've already launched a debug session or created a `launch.json` file to +define launch configurations, you'll see the **Launch configurations** menu, +which lets you choose configurations and start debug sessions: + +
    + +![](images/launch-configuration-menu.png) + +
    Figure 3. Launch configurations menu
    +
    + +### Other ways to start a debug session + +There are a number of other ways to start a debug session. + +#### Launching from the Command Palette + +If you have a Mojo file open in your active editor, you can also start a debug +session from the **Command Palette**. + +1. Click **View** > **Command Palette** or press Control+Shift+P + (Command+Shift+P on macOS). + +2. Enter "Mojo" at the prompt to bring up the Mojo commands. You should see the + same debug configurations described in [Quick run or + debug](#quick-run-or-debug). + +#### Launch from the File Explorer + +To launch a debug session from the the **File Explorer** view: + +1. Right-click on a Mojo file. +2. Select a Mojo debug configuration. + +You should see the same debug configurations described in [Quick run or +debug](#quick-run-or-debug). + +#### Debug with F5 + +Press F5 to start a debug session using the current debug configuration. + +If you don't have any existing debug configurations available to select, and +your active editor contains a Mojo file with an `fn main()` entry point, +pressing F5 will launch and debug the current file using the **Debug Mojo +File** action described in [Quick run or debug](#quick-run-or-debug). + +## Starting the debugger from the command line + +Use the `mojo debug` command to start a debug session from the command line. You +can choose from two debugging interfaces: + +* With the `--vscode` flag, `mojo debug` starts a debug session on VS Code if + it's running and the Mojo extension is enabled. + +* Without the `--vscode` flag, `mojo debug` starts a command-line [LLDB + debugger](https://lldb.llvm.org/) session. + +You can choose to build and debug a Mojo file, run and debug a compiled binary, +or to attach the debugger to a running process. + +:::note Environment variables + +When you debug a program from the command line using `--vscode`, the program runs +with the environment variables set in the terminal. When launching from inside +VS Code via the GUI, the environment is defined by the VS Code [launch +configuration](#launch-configurations). + +::: + +For a full list of command-line options, see the [`mojo debug` reference +page](/mojo/cli/debug). + +### Start a debug session from the command line + +With VS Code open, run the following command (either from VS Code's integrated +terminal or an external shell): + +```bash +mojo debug --vscode myproject.mojo +``` + +Or to debug a compiled binary: + +```bash +mojo debug --vscode myproject +``` + +For best results, build with the `-O0 -g` command-line options when you build a +binary that you intend to debug—this produces a binary with full debug info. +(When you call `mojo debug` on a Mojo source file, it includes debug +information by default.) See the [`mojo build` reference page](/mojo/cli/build) +for details on compilation options. + +### Attach the debugger to a running process from the command line + +You can also attach the debugger to a running process by specifying either the +process ID or process name on the command line: + +```bash +mojo debug --vscode --pid +``` + +Or: + +```bash +mojo debug --vscode --process-name +``` + +## Launch configurations + +VS Code *launch configurations* let you define setup information for debugging +your applications. + +The Mojo debugger provides the following launch configuration templates: + +* Debug current Mojo file. Launches and debugs the Mojo file in the active + editor tab. Effectively the same as the **Debug Mojo File** action described in + [Quick run or debug](#quick-run-or-debug), but with more configuration options. + +* Debug Mojo file. Like the previous entry, except that it identifies a + specific file to launch and debug, no matter what file is displayed in the + active editor. + +* Debug binary. This configuration operates on a prebuilt binary, which could + be written in any mixture of languages supported by LLDB (Mojo, C, C++, etc.). + You need to set the `program` field to the path of your binary. + +* Attach to process. Launches a debug session attached to a running process. On + launch, you choose the process you want to debug from a list of running + processes. + +You can edit any of these templates to customize them. All VS Code launch +configurations must contain the following attributes: + +* `name`. The name of the launch configuration, which shows up in the UI (for + example, "Run current Mojo file"). +* `request`. Can be either `launch` (to run a program from VS Code) or `attach` + (to attach to and debug a running file). +* `type`. Use `mojo-lldb` for the Mojo debugger. + +In addition, Mojo launch configurations can contain the following attributes: + +* `args`. Any command-line arguments to be passed to the program. +* `cwd`. The current working directory to run the program in. +* `description`. A longer description of the configuration, not shown in the UI. +* `env`. Environment variables to be set before running the program. +* `mojoFile`. Path to a Mojo file to launch and debug. +* `pid`. Process ID of the running process to attach to. +* `program`. Path to a compiled binary to launch and debug, or the + program to attach to. +* `runInTerminal`. True to run the program with a dedicated terminal, which + allows the program to receive standard input from the terminal. False to run + the program with its output directed to the **Debug Console**. + +If configuration is a `launch` request, the configuration must include either +the `mojoFile` or `program` attribute. + +For `attach` requests, the configuration must include either the `pid` or +`program` attribute. + +VS Code performs variable substitution on the launch configurations. You can +use `${workspaceFolder}` to substitute the path to the current workspace, and +`${file}` to represent the file in the active editor tab. For a complete list +of variables, see the VS Code [Variables +reference](https://code.visualstudio.com/docs/editor/variables-reference). + +For more information, see the VS Code documentation for [Launch +configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations). + +:::note Compilation options + +Mojo launch configurations don't allow you to specify compilation options. If +you need to specify compilation options, you can build the binary using [`mojo +build`](/mojo/cli/build), then use a launch configuration with the `program` +option to launch the compiled binary. Or if you [start the debugger from the +command line](#starting-the-debugger-from-the-command-line), you can pass +compilation options to the `mojo debug` command. + +::: + +### Edit launch configurations + +To edit launch configurations: + +1. If the **Run and Debug** view isn't already open, click the **Run and + Debug** icon in the **Activity Bar** (on the left side of the VS Code window) + or press Control+Shift+D (Command+Shift+D on macOS). + + ![](images/run-and-debug-icon.png) + +2. Create or open the `launch.json` file: + 1. If you see the **Run start view**, click **create a launch.json file**. + 2. If you already have launch configurations set up, click the gear icon + next to the **Launch configurations** menu. + ![](images/launch-configuration-menu.png) + +3. Select **Mojo** from the list of debuggers. + +VS Code opens the new `launch.json` file in an editor tab, with templates for +some common debug actions. Click **Add configuration** to add a new +configuration template. + +## Using the debugger + +When a debug session is running, use the debug toolbar to pause, continue, and +step through the program. + +![](images/debug-toolbar.png) + +The buttons on the toolbar are: + +* **Continue/Pause**: If the program is stopped, resume the normal execution of the + program up to the next breakpoint, signal or crash. Otherwise, pause all the + threads of the program at once. + +* **Step Over**: Execute the next line of code without stopping at function calls. + +* **Step Into**: Execute the next line of code and stop at the first function call. If the program is stopped just before a function call, steps into the function so you can step through it line-by-line. + +* **Step Out**: Finish the execution of the current function and stop right after + returning to the parent function. + +* **Restart**: If this is a `launch` session, terminate the current program and + restart the debug session. Otherwise, detach from the target process and + reattach to it. + +* **Stop**: If this is a `launch` session, terminate the current program. Otherwise, + detach from the target process without killing it. + +The debugger currently has the following limitations: + +* No support for breaking automatically on Mojo errors. + +* When stepping out of a function, the returned value is not displayed. + +* LLDB doesn’t support stopping or resuming individual threads. + +### Breakpoints + +The Mojo debugger supports setting [standard +breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints), +[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints), +[function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints), +[data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints), +and [triggered breakpoints](https://code.visualstudio.com/docs/editor/debugging#_triggered-breakpoints), +as described in the VS Code documentation. +The Mojo debugger also supports *error breakpoints* (also known as "break on +raise"), which break whenever a `raise` statement is executed. + +When debugging Mojo code, the debugger doesn't support conditional breakpoints +based on an expression (it does +support hit counts, which VS Code classifies as a kind of conditional +breakpoint). + +When editing a breakpoint, you're offered four options: + +* **Expression**. Set a conditional breakpoint (not currently supported). +* **Hit Count**. Add a hit count to a breakpoint (supported). +* **Log Message**. Add a logpoint (supported) +* **Wait for Breakpoint**. Add a triggered breakpoint (supported). + +#### Set a hit count breakpoint + +A hit count breakpoint is a breakpoint that only breaks execution after the +debugger hits it a specified number of times. + +To add a hit count breakpoint: + +1. Right click in the left gutter of the editor where you want to place the + breakpoint, and select **Add Conditional Breakpoint.** +2. Select **Hit Count** from the menu and enter the desired hit count. + +To change an existing breakpoint to a hit count breakpoint: + +1. Right click on the breakpoint in the left gutter of the editor and select + **Edit breakpoint**. +2. Select **Hit Count** from the menu and enter the desired hit count. + +You can also edit a breakpoint from the **Breakpoints** section of the **Run and +Debug** view: + +* Right-click on the breakpoint and select **Edit Condition**, or, +* Click the **Edit Condition** icon next to the breakpoint. + +This brings up the same menu, **next to the breakpoint in the editor tab**. + +#### Enable error breakpoints + +You can enable and disable error breakpoints in VS Code by selecting "Mojo +Raise" in the **Breakpoints** section of the **Run and Debug** view. If enabled +during debugging, executing a `raise` statement causes the debugger to stop +execution and highlight the line of code where the error was raised. + +![VS Code window showing a program paused in the debugger with the Run and Debug view visible. The program is paused at a raise statement.](images/break-on-raise.png) + +### View local variables + +When a program is paused in the debugger, the editor shows local variable values +inline. You can also find them in the **Variables** section of the **Run and +Debug** view. + +
    + +![VS Code window showing a program paused in the debugger, with the variables sections of the Run and Debug view visible. The edit shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2.](images/debugger-variables.png) + +
    Figure 4. Local variable values displayed in the debugger
    +
    + +### View the call stack + +When a program is paused in the debugger, the **Run and Debug** view shows the +current call stack. (You may see multiple call stacks, one for each active +thread in the program.) + +
    + +![VS Code window showing a program paused in the debugger, with the call stack and variables sections of the Run and Debug view visible. The call stack shows three functions (nested2, nested1, and main). The program is paused at a breakpoint in nested2; the parent function nested1 is selected in the call stack, and editor highlights the current line in nested1 (the call to nested2()).](images/debugger-call-stack-nested1.png) + +
    Figure 5. Call stack in Run and Debug view
    +
    + +The **Call Stack** section of the Run and Debug view shows a stack frame for +each function call in the current call stack. Clicking on the name of the +function highlights the current line in that function. For example, in Figure +5, the program is paused at a breakpoint in `nested2()`, but the parent +function, `nested1()` is selected in the call stack. The editor highlights the +current line in `nested1()` (that is, the call to `nested2()`) and shows the +current local variable values for `nested1()`. + +### Use the Debug Console + +The **Debug Console** gives you a command-line interface to the debugger. The +**Debug Console** processes LLDB commands and Mojo expressions. + +Anything prefixed with a colon (`:`) is treated as an LLDB command. Any other +input is treated as an expression. + +Currently Mojo expressions are limited to inspecting variables and their fields. +The console also supports subscript notation (`vector[index]`) for certain data +structures in the standard library, including `List`, `SIMD`, +and `ListLiteral`. + +In the future, we intend to provide a way for arbitrary data structures to +support subscript notation in the **Debug Console**. + +:::note + +The **Debug Console** only accepts input when the program is paused. + +::: + +## Tips and tricks + +There are several features in the standard library that aren't directly related +to the debugger, but which can help you debug your programs. These include: + +* Programmatic breakpoints. +* Setting parameters from the Mojo command line. + +### Set a programmatic breakpoint + +To break at a specific point in your code, you can use the built-in +[`breakpoint()`](/mojo/stdlib/builtin/breakpoint/breakpoint) function: + +```mojo +if some_value.is_valid(): + do_the_right_thing() +else: + # We should never get here! + breakpoint() +``` + +If you have VS Code open and run this code in debug mode (either using VS Code +or `mojo debug`), hitting the `breakpoint()` call causes an error, which +triggers the debugger. + +:::note Assertions + +The [`testing`](/mojo/stdlib/testing/testing/) module includes a number of +ways to specify assertions. Assertions also trigger an error, so can open the +debugger in the same way that a `breakpoint()` call will. + +::: + +### Set parameters from the Mojo command line + +You can use the [`param_env`](/mojo/stdlib/sys/param_env/) module to retrieve +parameter values specified on the Mojo command line. Among other things, this +is an easy way to switch debugging logic on and off. For example: + +```mojo +from param_env import is_defined + +def some_function_with_issues(): + # ... + @parameter + if is_defined["DEBUG_ME"](): + breakpoint() +``` + +To activate this code, use the [`-D` command-line +option](/mojo/cli/debug#compilation-options) to define `DEBUG_ME`: + +```bash +mojo debug -D DEBUG_ME main.mojo +``` + +The `is_defined()` function returns a compile-time true or false value based on +whether the specified name is defined. Since the `breakpoint()` call is inside a +[parametric `if` statement](/mojo/manual/decorators/parameter#parametric-if-statement), +it is only included in the compiled code when the `DEBUG_ME` name is defined on +the command line. + +## Troubleshooting + +### `error: can't connect to the RPC debug server socket` + +If using `mojo debug --rpc` gives you the message `error: can't connect to the RPC debug server socket: Connection refused`, try the following possible fixes: + +* Make sure VS Code is open. +* If VS Code is already open, try restarting VS Code. +* If there are other VS Code windows open, try closing them and then restarting. This error can sometimes occur when multiple windows have opened and closed in certain orders. + +### `error: couldn't get a valid response from the RPC server` + +If using `mojo debug --rpc` gives you the message `error: couldn't get a valid response from the RPC server`, try the following possible fixes: + +* Make sure VS Code is open to a valid Mojo codebase. This error can sometimes happen if the VS Code window is open to some other codebase. +* If there are multiple VS Code windows open, try closing all but the one you wish to debug in. +* Restart VS Code. +* Reinstall the SDK and restart VSCode. +* If you are working on a development version of the SDK, make sure that all SDK tools are properly built with your build system, and then reload VSCode. +* As a last resort, restarting your entire computer can fix this problem. + +If these steps don't help, please file an issue. We'd love your help identifying possible causes and fixes! diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb deleted file mode 100644 index be8e4a6c16..0000000000 --- a/docs/tools/testing.ipynb +++ /dev/null @@ -1,792 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "---\n", - "title: Testing\n", - "sidebar_position: 2\n", - "description: Testing Mojo programs.\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mojo includes a framework for developing and executing unit tests. The framework\n", - "also supports testing code examples in the\n", - "[documentation strings](/mojo/manual/basics#code-comments)\n", - "(also known as *docstrings*) of your API references. The Mojo testing framework\n", - "consists of a set of assertions defined as part of the\n", - "[Mojo standard library](/mojo/lib) and the\n", - "[`mojo test`](/mojo/cli/test) command line tool.\n", - "\n", - "## Get started\n", - "\n", - "Let’s start with a simple example of writing and running Mojo tests.\n", - "\n", - "### 1. Write tests\n", - "\n", - "For your first example of using the Mojo testing framework, create a file named\n", - "`test_quickstart.mojo` containing the following code:\n", - "\n", - "```mojo\n", - "# Content of test_quickstart.mojo\n", - "from testing import assert_equal\n", - "\n", - "def inc(n: Int) -> Int:\n", - " return n + 1\n", - "\n", - "def test_inc_zero():\n", - " # This test contains an intentional logical error to show an example of\n", - " # what a test failure looks like at runtime.\n", - " assert_equal(inc(0), 0)\n", - "\n", - "def test_inc_one():\n", - " assert_equal(inc(1), 2)\n", - "```\n", - "\n", - "In this file, the `inc()` function is the test *target*. The functions whose\n", - "names begin with `test_` are the tests. Usually the target should be in a\n", - "separate source file from its tests, but you can define them in the same file\n", - "for this simple example.\n", - "\n", - "A test function *fails* if it raises an error when executed, otherwise it\n", - "*passes*. The two tests in this example use the `assert_equal()` function,\n", - "which raises an error if the two values provided are not equal.\n", - "\n", - ":::note\n", - "\n", - "The implementation of `test_inc_zero()` contains an intentional logical error\n", - "so that you can see an example of a failed test when you execute it in the\n", - "next step of this tutorial. \n", - "\n", - ":::\n", - "\n", - "### 2. Execute tests\n", - "\n", - "Then in the directory containing the file, execute the following command in your\n", - "shell:\n", - "\n", - "```bash\n", - "mojo test test_quickstart.mojo\n", - "```\n", - "\n", - "You should see output similar to this (note that this example elides the full\n", - "filesystem paths from the output shown):\n", - "\n", - "```\n", - "Testing Time: 1.193s\n", - "\n", - "Total Discovered Tests: 2\n", - "\n", - "Passed : 1 (50.00%)\n", - "Failed : 1 (50.00%)\n", - "Skipped: 0 (0.00%)\n", - "\n", - "******************** Failure: 'ROOT_DIR/test_quickstart.mojo::test_inc_zero()' ********************\n", - "\n", - "Unhandled exception caught during execution\n", - "\n", - "Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\n", - " left: 1\n", - " right: 0\n", - "\n", - "********************\n", - "```\n", - "\n", - "The output starts with a summary of the number of tests discovered, passed,\n", - "failed, and skipped. Following that, each failed test is reported along with\n", - "its error message.\n", - "\n", - "### Next steps\n", - "\n", - "- [The `testing` module](#the-testing-module) describes the assertion\n", - "functions available to help implement tests.\n", - "- [Writing unit tests](#writing-unit-tests) shows how to write unit tests and\n", - "organize them into test files.\n", - "- [The `mojo test` command](#the-mojo-test-command) describes how to execute\n", - "and collect lists of tests.\n", - "- [Writing API documentation tests](#writing-api-documentation-tests)\n", - "discusses how to use the Mojo testing framework to test code examples in your\n", - "API documentation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## The `testing` module\n", - "\n", - "The Mojo standard library includes a [`testing`](/mojo/stdlib/testing/testing/)\n", - "module that defines several assertion functions for implementing tests. Each\n", - "assertion returns `None` if its condition is met or raises an error if it isn’t.\n", - "\n", - "- [`assert_true()`](/mojo/stdlib/testing/testing/assert_true):\n", - "Asserts that the input value is `True`.\n", - "- [`assert_false()`](/mojo/stdlib/testing/testing/assert_false):\n", - "Asserts that the input value is `False`.\n", - "- [`assert_equal()`](/mojo/stdlib/testing/testing/assert_equal):\n", - "Asserts that the input values are equal.\n", - "- [`assert_not_equal()`](/mojo/stdlib/testing/testing/assert_not_equal):\n", - "Asserts that the input values are not equal.\n", - "- [`assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal):\n", - "Asserts that the input values are equal up to a tolerance.\n", - "\n", - "The boolean assertions report a basic error message when they fail." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: At Expression [1] wrapper:14:16: AssertionError: condition was unexpectedly False\n" - ] - } - ], - "source": [ - "from testing import *\n", - "assert_true(False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each function also accepts an optional `msg` keyword argument for providing a\n", - "custom message to include if the assertion fails." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: At Expression [2] wrapper:14:16: AssertionError: paradoxes are not allowed\n" - ] - } - ], - "source": [ - "assert_true(False, msg=\"paradoxes are not allowed\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For comparing floating point values you should use `assert_almost_equal()`,\n", - "which allows you to specify either an absolute or relative tolerance." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: At Expression [3] wrapper:15:24: AssertionError: 3.3333333333333335 is not close to 3.3300000000000001 with a diff of 0.0033333333333334103 (close but no cigar)\n" - ] - } - ], - "source": [ - "result = 10 / 3\n", - "assert_almost_equal(result, 3.33, atol=0.001, msg=\"close but no cigar\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The testing module also defines a [context\n", - "manager](/mojo/manual/errors#use-a-context-manager),\n", - "[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises), to assert that\n", - "a given code block correctly raises an expected error." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test passes because the error is raised\n", - "Test fails because the error isn't raised\n", - "Error: AssertionError: Didn't raise at Expression [4] wrapper:18:23\n" - ] - } - ], - "source": [ - "def inc(n: Int) -> Int:\n", - " if n == Int.MAX:\n", - " raise Error(\"inc overflow\")\n", - " return n + 1\n", - "\n", - "print(\"Test passes because the error is raised\")\n", - "with assert_raises():\n", - " _ = inc(Int.MAX)\n", - "\n", - "print(\"Test fails because the error isn't raised\")\n", - "with assert_raises():\n", - " _ = inc(Int.MIN)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::note\n", - "\n", - "The example above assigns the return value from `inc()` to a\n", - "[*discard pattern*](/mojo/manual/lifecycle/death#explicit-lifetimes).\n", - "Without it, the Mojo compiler detects that the return value is unused and\n", - "optimizes the code to eliminate the function call.\n", - "\n", - ":::\n", - "\n", - "You can also provide an optional `contains` argument to `assert_raises()` to\n", - "indicate that the test passes only if the error message contains the substring\n", - "specified. Other errors are propagated, failing the test." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unhandled exception caught during execution" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test passes because the error contains the substring\n", - "Test fails because the error doesn't contain the substring\n", - "Error: invalid value\n" - ] - } - ], - "source": [ - "print(\"Test passes because the error contains the substring\")\n", - "with assert_raises(contains=\"required\"):\n", - " raise Error(\"missing required argument\")\n", - "\n", - "print(\"Test fails because the error doesn't contain the substring\")\n", - "with assert_raises(contains=\"required\"):\n", - " raise Error(\"invalid value\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Writing unit tests\n", - "\n", - "A Mojo unit test is simply a function that fulfills all of these requirements:\n", - "\n", - "- Has a name that starts with `test_`.\n", - "- Accepts no arguments.\n", - "- Returns either `None` or a value of type `object`.\n", - "- Raises an error to indicate test failure.\n", - "- Is defined at the module scope, not as a Mojo struct method.\n", - "\n", - "You can use either `def` or `fn` to define a test function. Because a test\n", - "function always raises an error to indicate failure, any test function defined\n", - "using `fn` must include the `raises` declaration.\n", - "\n", - "Generally, you should use the assertion utilities from the Mojo standard library\n", - "[`testing`](/mojo/stdlib/testing/testing/) module to implement your tests.\n", - "You can include multiple related assertions in the same test function. However,\n", - "if an assertion raises an error during execution then the test function returns\n", - "immediately, skipping any subsequent assertions.\n", - "\n", - "You must define your Mojo unit tests in Mojo source files named with a `test`\n", - "prefix or suffix. You can organize your test files within a directory hierarchy,\n", - "but the test files must not be part of a Mojo package (that is, the test\n", - "directories should not contain `__init__.mojo` files).\n", - "\n", - "Here is an example of a test file containing three tests for functions defined\n", - "in a source module named `my_target_module` (which is not shown here).\n", - "\n", - "```mojo\n", - "# File: test_my_target_module.mojo\n", - "\n", - "from my_target_module import convert_input, validate_input\n", - "from testing import assert_equal, assert_false, assert_raises, assert_true\n", - "\n", - "def test_validate_input():\n", - "\tassert_true(validate_input(\"good\"), msg=\"'good' should be valid input\")\n", - "\tassert_false(validate_input(\"bad\"), msg=\"'bad' should be invalid input\")\n", - "\n", - "def test_convert_input():\n", - "\tassert_equal(convert_input(\"input1\"), \"output1\")\n", - "\tassert_equal(convert_input(\"input2\"), \"output2\")\n", - "\n", - "def test_convert_input_error():\n", - "\twith assert_raises():\n", - "\t\t_ = convert_input(\"garbage\")\n", - "```\n", - "\n", - "The unique identity of a unit test consists of the path of the test file and the\n", - "name of the test function, separated by `::`. So the test IDs from the example\n", - "above are:\n", - "\n", - "- `test_my_target_module.mojo::test_validate_input()`\n", - "- `test_my_target_module.mojo::test_convert_input()`\n", - "- `test_my_target_module.mojo::test_convert_error()`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `mojo test` command\n", - "\n", - "The `mojo` command line interface includes the [`mojo test`](/mojo/cli/test)\n", - "command for running tests or collecting a list of tests.\n", - "\n", - "### Running tests\n", - "\n", - "By default, the `mojo test` command runs the tests that you specify using one of\n", - "the following:\n", - "\n", - "- A single test ID with either an absolute or relative file path, to run only\n", - "that test.\n", - "- A single absolute or relative file path, to run all tests in that file.\n", - "- A single absolute or relative directory path, to recurse through that\n", - "directory hierarchy and run all tests found.\n", - "\n", - "If needed, you can optionally use the `-I` option one or more times to append\n", - "additional paths to the list of directories searched to import Mojo modules and\n", - "packages. For example, consider a project with the following directory\n", - "structure:\n", - "\n", - "```\n", - ".\n", - "├── src\n", - "│   ├── example.mojo\n", - "│   └── my_math\n", - "│   ├── __init__.mojo\n", - "│   └── utils.mojo\n", - "└── test\n", - " └── my_math\n", - " ├── test_dec.mojo\n", - " └── test_inc.mojo\n", - "```\n", - "\n", - "From the project root directory, you could execute all of the tests in the\n", - "`test` directory like this:\n", - "\n", - "```\n", - "$ mojo test -I src test\n", - "Testing Time: 3.433s\n", - "\n", - "Total Discovered Tests: 4\n", - "\n", - "Passed : 4 (100.00%)\n", - "Failed : 0 (0.00%)\n", - "Skipped: 0 (0.00%)\n", - "```\n", - "\n", - "You could run the tests contained in only the `test_dec.mojo` file like this:\n", - "\n", - "```\n", - "$ mojo test -I src test/my_math/test_dec.mojo\n", - "Testing Time: 1.175s\n", - "\n", - "Total Discovered Tests: 2\n", - "\n", - "Passed : 2 (100.00%)\n", - "Failed : 0 (0.00%)\n", - "Skipped: 0 (0.00%)\n", - "```\n", - "\n", - "And you could run a single test from a file by providing its fully qualified\n", - "ID like this:\n", - "\n", - "```\n", - "$ mojo test -I src 'test/my_math/test_dec.mojo::test_dec_valid()'\n", - "Testing Time: 0.66s\n", - "\n", - "Total Discovered Tests: 1\n", - "\n", - "Passed : 1 (100.00%)\n", - "Failed : 0 (0.00%)\n", - "Skipped: 0 (0.00%)\n", - "```\n", - "\n", - "### Collecting a list of tests\n", - "\n", - "By including the `--collect-only` or `--co` option, you can use `mojo test` to\n", - "discover and print a list of tests. \n", - "\n", - "As an example, consider the project structure shown in the\n", - "[Running tests](#running-tests) section. The following command produces a list\n", - "of all of the tests defined in the `test` directory hierarchy.\n", - "\n", - "```bash\n", - "mojo test --co test\n", - "```\n", - "\n", - "The output shows the hierarchy of directories, test files, and individual tests\n", - "(note that this example elides the full filesystem paths from the output shown):\n", - "\n", - "```\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "```\n", - "\n", - "### Producing JSON formatted output\n", - "\n", - "By default `mojo test` produces concise, human-readable output. Alternatively\n", - "you can produce JSON formatted output more suitable for input to other tools by\n", - "including the `--diagnostic-format json` option.\n", - "\n", - "For example, you could run the tests in the `test_quickstart.mojo` file shown\n", - "in the [Get started](#get-started) section with JSON formatted output using this\n", - "command:\n", - "\n", - "```bash\n", - "mojo test --diagnostic-format json test_quickstart.mojo\n", - "```\n", - "\n", - "The output shows the detailed results for each individual test and summary\n", - "results (note that this example elides the full filesystem paths from the\n", - "output shown):\n", - "\n", - "```\n", - "{\n", - " \"children\": [\n", - " {\n", - " \"duration_ms\": 60,\n", - " \"error\": \"Unhandled exception caught during execution\",\n", - " \"kind\": \"executionError\",\n", - " \"stdErr\": \"\",\n", - " \"stdOut\": \"Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\\r\\n left: 1\\r\\n right: 0\\r\\n\",\n", - " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_zero()\"\n", - " },\n", - " {\n", - " \"duration_ms\": 51,\n", - " \"error\": \"\",\n", - " \"kind\": \"success\",\n", - " \"stdErr\": \"\",\n", - " \"stdOut\": \"\",\n", - " \"testID\": \"ROOT_DIR/test_quickstart.mojo::test_inc_one()\"\n", - " }\n", - " ],\n", - " \"duration_ms\": 1171,\n", - " \"error\": \"\",\n", - " \"kind\": \"executionError\",\n", - " \"stdErr\": \"\",\n", - " \"stdOut\": \"\",\n", - " \"testID\": \"ROOT_DIR/test_quickstart.mojo\"\n", - "}\n", - "```\n", - "\n", - "You can also produce JSON output for test collection as well. As an example,\n", - "consider the project structure shown in the [Running tests](#running-tests)\n", - "section. The following command collects a list in JSON format of all of the\n", - "tests defined in the `test` directory hierarchy:\n", - "\n", - "```bash\n", - "mojo test --diagnostic-format json --co test\n", - "```\n", - "\n", - "The output would appear as follows (note that this example elides the full\n", - "filesystem paths from the output shown):\n", - "\n", - "```\n", - "{\n", - " \"children\": [\n", - " {\n", - " \"children\": [\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_valid()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 5,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 5\n", - " }\n", - " },\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo::test_dec_min()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 9,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 9\n", - " }\n", - " }\n", - " ],\n", - " \"id\": \"ROOT_DIR/test/my_math/test_dec.mojo\"\n", - " },\n", - " {\n", - " \"children\": [\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_valid()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 5,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 5\n", - " }\n", - " },\n", - " {\n", - " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo::test_inc_max()\",\n", - " \"location\": {\n", - " \"endColumn\": 5,\n", - " \"endLine\": 9,\n", - " \"startColumn\": 5,\n", - " \"startLine\": 9\n", - " }\n", - " }\n", - " ],\n", - " \"id\": \"ROOT_DIR/test/my_math/test_inc.mojo\"\n", - " }\n", - " ],\n", - " \"id\": \"ROOT_DIR/test/my_math\"\n", - "}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Writing API documentation tests\n", - "\n", - "The Mojo testing framework also supports testing code examples that you include\n", - "in [docstrings](/mojo/manual/basics#code-comments). This helps to ensure that\n", - "the code examples in your API documentation are correct and up to date.\n", - "\n", - "### Identifying executable code\n", - "\n", - "The Mojo testing framework requires you to explicitly identify the code blocks\n", - "that you want it to execute.\n", - "\n", - "In a Mojo docstring, a fenced code block delimited by standard triple-backquotes\n", - "is a *display-only* code block. It appears in the API documentation, but\n", - "`mojo test` does not identify it as a test or attempt to execute any of the code\n", - "in the block.\n", - "\n", - "~~~\n", - "\"\"\" Non-executable code block example.\n", - "\n", - "The generated API documentation includes all lines of the following code block,\n", - "but `mojo test` does not execute any of the code in it.\n", - "\n", - "```\n", - "# mojo test does NOT execute any of this code block\n", - "a = 1\n", - "print(a)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - "In contrast, a fenced code block that starts with the line ```mojo\n", - "not only appears in the API documentation, but `mojo test` treats it as an\n", - "executable test. The test fails if the code raises any error, otherwise it\n", - "passes.\n", - "\n", - "~~~\n", - "\"\"\" Executable code block example.\n", - "\n", - "The generated API documentation includes all lines of the following code block\n", - "*and* `mojo test` executes it as a test.\n", - "\n", - "```mojo\n", - "from testing import assert_equals\n", - "\n", - "b = 2\n", - "assert_equals(b, 2)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - "Sometimes you might want to execute a line of code as part of the test but *not*\n", - "display that line in the API documentation. To achieve this, prefix the line of\n", - "code with `%#`. For example, you could use this technique to omit `import`\n", - "statements and assertion functions from the documentation.\n", - "\n", - "~~~\n", - "\"\"\" Executable code block example with some code lines omitted from output.\n", - "\n", - "The generated API documentation includes only the lines of code that do *not*\n", - "start with `%#`. However, `mojo test` executes *all* lines of code.\n", - "\n", - "```mojo\n", - "%# from testing import assert_equal\n", - "c = 3\n", - "print(c)\n", - "%# assert_equal(c, 3)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - "### Documentation test suites and scoping\n", - "\n", - "The Mojo testing framework treats each docstring as a separate *test suite*.\n", - "In other words, a single test suite could correspond to the docstring for an\n", - "individual package, module, function, struct, struct method, etc.\n", - "\n", - "Each executable code block within a given docstring is a single test of the same\n", - "test suite. The `mojo test` command executes the tests of a test suite\n", - "sequentially in the order that they appear within the docstring. If a test\n", - "within a particular test suite fails, then all subsequent tests within the same\n", - "test suite are skipped.\n", - "\n", - "All tests within the test suite execute in the same scope, and test execution\n", - "within that scope is stateful. This means, for example, that a variable created\n", - "within one test is then accessible to subsequent tests in the same test suite.\n", - "\n", - "~~~\n", - "\"\"\" Stateful example.\n", - "\n", - "Assign 1 to the variable `a`:\n", - "\n", - "```mojo\n", - "%# from testing import assert_equal\n", - "a = 1\n", - "%# assert_equal(a, 1)\n", - "```\n", - "\n", - "Then increment the value of `a` by 1:\n", - "\n", - "```mojo\n", - "a += 1\n", - "%# assert_equal(a, 2)\n", - "```\n", - "\"\"\"\n", - "~~~\n", - "\n", - ":::note\n", - "\n", - "Test suite scopes do *not* nest. In other words, the test suite scope of a\n", - "module is completely independent of the test suite scope of a function or struct\n", - "defined within that module. For example, this means that if a module’s test\n", - "suite creates a variable, that variable is *not* accessible to a function’s test\n", - "suite within the same module.\n", - "\n", - ":::\n", - "\n", - "### Documentation test identifiers\n", - "\n", - "The format of a documentation test identifier is `@::`.\n", - "This is best explained by an example. Consider the project structure shown in\n", - "the [Running tests](#running-tests) section. The source files in the `src`\n", - "directory might contain docstrings for the `my_math` package, the `utils.mojo`\n", - "module, and the individual functions within that module. You could collect the\n", - "full list of tests by executing:\n", - "\n", - "```\n", - "mojo test --co src\n", - "```\n", - "\n", - "The output shows the hierarchy of directories, test files, and individual tests\n", - "(note that this example elides the full filesystem paths from the output shown):\n", - "\n", - "```\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "```\n", - "\n", - "Several different test suites appear in this result:\n", - "\n", - "| Test suite scope | File | Test suite name |\n", - "|------------------|------|-----------------|\n", - "| Package | `src/my_math/__init__.mojo` | `__doc__` |\n", - "| Module | `src/my_math/utils.mojo` | `__doc__` |\n", - "| Function | `src/my_math/utils.mojo` | `inc(stdlib\\3A\\3Abuiltin\\3A\\3Aint\\3A\\3AInt).__doc__` |\n", - "\n", - "Then within a specific test suite, tests are numbered sequentially in the order\n", - "they appear in the docstring, starting with 0." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tools/testing.mdx b/docs/tools/testing.mdx new file mode 100644 index 0000000000..ef10b459cf --- /dev/null +++ b/docs/tools/testing.mdx @@ -0,0 +1,648 @@ +--- +title: Testing +sidebar_position: 2 +description: Testing Mojo programs. +--- + +Mojo includes a framework for developing and executing unit tests. The framework +also supports testing code examples in the +[documentation strings](/mojo/manual/basics#code-comments) +(also known as *docstrings*) of your API references. The Mojo testing framework +consists of a set of assertions defined as part of the +[Mojo standard library](/mojo/lib) and the +[`mojo test`](/mojo/cli/test) command line tool. + +## Get started + +Let’s start with a simple example of writing and running Mojo tests. + +### 1. Write tests + +For your first example of using the Mojo testing framework, create a file named +`test_quickstart.mojo` containing the following code: + +```mojo +# Content of test_quickstart.mojo +from testing import assert_equal + +def inc(n: Int) -> Int: + return n + 1 + +def test_inc_zero(): + # This test contains an intentional logical error to show an example of + # what a test failure looks like at runtime. + assert_equal(inc(0), 0) + +def test_inc_one(): + assert_equal(inc(1), 2) +``` + +In this file, the `inc()` function is the test *target*. The functions whose +names begin with `test_` are the tests. Usually the target should be in a +separate source file from its tests, but you can define them in the same file +for this simple example. + +A test function *fails* if it raises an error when executed, otherwise it +*passes*. The two tests in this example use the `assert_equal()` function, +which raises an error if the two values provided are not equal. + +:::note + +The implementation of `test_inc_zero()` contains an intentional logical error +so that you can see an example of a failed test when you execute it in the +next step of this tutorial. + +::: + +### 2. Execute tests + +Then in the directory containing the file, execute the following command in your +shell: + +```bash +mojo test test_quickstart.mojo +``` + +You should see output similar to this (note that this example elides the full +filesystem paths from the output shown): + +``` +Testing Time: 1.193s + +Total Discovered Tests: 2 + +Passed : 1 (50.00%) +Failed : 1 (50.00%) +Skipped: 0 (0.00%) + +******************** Failure: 'ROOT_DIR/test_quickstart.mojo::test_inc_zero()' ******************** + +Unhandled exception caught during execution + +Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed: + left: 1 + right: 0 + +******************** +``` + +The output starts with a summary of the number of tests discovered, passed, +failed, and skipped. Following that, each failed test is reported along with +its error message. + +### Next steps + +* [The `testing` module](#the-testing-module) describes the assertion + functions available to help implement tests. +* [Writing unit tests](#writing-unit-tests) shows how to write unit tests and + organize them into test files. +* [The `mojo test` command](#the-mojo-test-command) describes how to execute + and collect lists of tests. +* [Writing API documentation tests](#writing-api-documentation-tests) + discusses how to use the Mojo testing framework to test code examples in your + API documentation. + +## The `testing` module + +The Mojo standard library includes a [`testing`](/mojo/stdlib/testing/testing/) +module that defines several assertion functions for implementing tests. Each +assertion returns `None` if its condition is met or raises an error if it isn’t. + +* [`assert_true()`](/mojo/stdlib/testing/testing/assert_true): + Asserts that the input value is `True`. +* [`assert_false()`](/mojo/stdlib/testing/testing/assert_false): + Asserts that the input value is `False`. +* [`assert_equal()`](/mojo/stdlib/testing/testing/assert_equal): + Asserts that the input values are equal. +* [`assert_not_equal()`](/mojo/stdlib/testing/testing/assert_not_equal): + Asserts that the input values are not equal. +* [`assert_almost_equal()`](/mojo/stdlib/testing/testing/assert_almost_equal): + Asserts that the input values are equal up to a tolerance. + +The boolean assertions report a basic error message when they fail. + +```mojo +from testing import * +assert_true(False) +``` + +```output +Unhandled exception caught during execution + +Error: At Expression [1] wrapper:14:16: AssertionError: condition was unexpectedly False +``` + +Each function also accepts an optional `msg` keyword argument for providing a +custom message to include if the assertion fails. + +```mojo +assert_true(False, msg="paradoxes are not allowed") +``` + +```output +Unhandled exception caught during execution + +Error: At Expression [2] wrapper:14:16: AssertionError: paradoxes are not allowed +``` + +For comparing floating point values you should use `assert_almost_equal()`, +which allows you to specify either an absolute or relative tolerance. + +```mojo +result = 10 / 3 +assert_almost_equal(result, 3.33, atol=0.001, msg="close but no cigar") +``` + +```output +Unhandled exception caught during execution + +Error: At Expression [3] wrapper:15:24: AssertionError: 3.3333333333333335 is not close to 3.3300000000000001 with a diff of 0.0033333333333334103 (close but no cigar) +``` + +The testing module also defines a [context +manager](/mojo/manual/errors#use-a-context-manager), +[`assert_raises()`](/mojo/stdlib/testing/testing/assert_raises), to assert that +a given code block correctly raises an expected error. + +```mojo +def inc(n: Int) -> Int: + if n == Int.MAX: + raise Error("inc overflow") + return n + 1 + +print("Test passes because the error is raised") +with assert_raises(): + _ = inc(Int.MAX) + +print("Test fails because the error isn't raised") +with assert_raises(): + _ = inc(Int.MIN) +``` + +```output +Unhandled exception caught during execution + +Test passes because the error is raised +Test fails because the error isn't raised +Error: AssertionError: Didn't raise at Expression [4] wrapper:18:23 +``` + +:::note + +The example above assigns the return value from `inc()` to a +[*discard pattern*](/mojo/manual/lifecycle/death#explicit-lifetimes). +Without it, the Mojo compiler detects that the return value is unused and +optimizes the code to eliminate the function call. + +::: + +You can also provide an optional `contains` argument to `assert_raises()` to +indicate that the test passes only if the error message contains the substring +specified. Other errors are propagated, failing the test. + +```mojo +print("Test passes because the error contains the substring") +with assert_raises(contains="required"): + raise Error("missing required argument") + +print("Test fails because the error doesn't contain the substring") +with assert_raises(contains="required"): + raise Error("invalid value") +``` + +```output +Unhandled exception caught during execution + +Test passes because the error contains the substring +Test fails because the error doesn't contain the substring +Error: invalid value +``` + +## Writing unit tests + +A Mojo unit test is simply a function that fulfills all of these requirements: + +* Has a name that starts with `test_`. +* Accepts no arguments. +* Returns either `None` or a value of type `object`. +* Raises an error to indicate test failure. +* Is defined at the module scope, not as a Mojo struct method. + +You can use either `def` or `fn` to define a test function. Because a test +function always raises an error to indicate failure, any test function defined +using `fn` must include the `raises` declaration. + +Generally, you should use the assertion utilities from the Mojo standard library +[`testing`](/mojo/stdlib/testing/testing/) module to implement your tests. +You can include multiple related assertions in the same test function. However, +if an assertion raises an error during execution then the test function returns +immediately, skipping any subsequent assertions. + +You must define your Mojo unit tests in Mojo source files named with a `test` +prefix or suffix. You can organize your test files within a directory hierarchy, +but the test files must not be part of a Mojo package (that is, the test +directories should not contain `__init__.mojo` files). + +Here is an example of a test file containing three tests for functions defined +in a source module named `my_target_module` (which is not shown here). + +```mojo +# File: test_my_target_module.mojo + +from my_target_module import convert_input, validate_input +from testing import assert_equal, assert_false, assert_raises, assert_true + +def test_validate_input(): + assert_true(validate_input("good"), msg="'good' should be valid input") + assert_false(validate_input("bad"), msg="'bad' should be invalid input") + +def test_convert_input(): + assert_equal(convert_input("input1"), "output1") + assert_equal(convert_input("input2"), "output2") + +def test_convert_input_error(): + with assert_raises(): + _ = convert_input("garbage") +``` + +The unique identity of a unit test consists of the path of the test file and the +name of the test function, separated by `::`. So the test IDs from the example +above are: + +* `test_my_target_module.mojo::test_validate_input()` +* `test_my_target_module.mojo::test_convert_input()` +* `test_my_target_module.mojo::test_convert_error()` + +## The `mojo test` command + +The `mojo` command line interface includes the [`mojo test`](/mojo/cli/test) +command for running tests or collecting a list of tests. + +### Running tests + +By default, the `mojo test` command runs the tests that you specify using one of +the following: + +* A single test ID with either an absolute or relative file path, to run only + that test. +* A single absolute or relative file path, to run all tests in that file. +* A single absolute or relative directory path, to recurse through that + directory hierarchy and run all tests found. + +If needed, you can optionally use the `-I` option one or more times to append +additional paths to the list of directories searched to import Mojo modules and +packages. For example, consider a project with the following directory +structure: + +``` +. +├── src +│   ├── example.mojo +│   └── my_math +│   ├── __init__.mojo +│   └── utils.mojo +└── test + └── my_math + ├── test_dec.mojo + └── test_inc.mojo +``` + +From the project root directory, you could execute all of the tests in the +`test` directory like this: + +``` +$ mojo test -I src test +Testing Time: 3.433s + +Total Discovered Tests: 4 + +Passed : 4 (100.00%) +Failed : 0 (0.00%) +Skipped: 0 (0.00%) +``` + +You could run the tests contained in only the `test_dec.mojo` file like this: + +``` +$ mojo test -I src test/my_math/test_dec.mojo +Testing Time: 1.175s + +Total Discovered Tests: 2 + +Passed : 2 (100.00%) +Failed : 0 (0.00%) +Skipped: 0 (0.00%) +``` + +And you could run a single test from a file by providing its fully qualified +ID like this: + +``` +$ mojo test -I src 'test/my_math/test_dec.mojo::test_dec_valid()' +Testing Time: 0.66s + +Total Discovered Tests: 1 + +Passed : 1 (100.00%) +Failed : 0 (0.00%) +Skipped: 0 (0.00%) +``` + +### Collecting a list of tests + +By including the `--collect-only` or `--co` option, you can use `mojo test` to +discover and print a list of tests. + +As an example, consider the project structure shown in the +[Running tests](#running-tests) section. The following command produces a list +of all of the tests defined in the `test` directory hierarchy. + +```bash +mojo test --co test +``` + +The output shows the hierarchy of directories, test files, and individual tests +(note that this example elides the full filesystem paths from the output shown): + +``` + + + + + + + +``` + +### Producing JSON formatted output + +By default `mojo test` produces concise, human-readable output. Alternatively +you can produce JSON formatted output more suitable for input to other tools by +including the `--diagnostic-format json` option. + +For example, you could run the tests in the `test_quickstart.mojo` file shown +in the [Get started](#get-started) section with JSON formatted output using this +command: + +```bash +mojo test --diagnostic-format json test_quickstart.mojo +``` + +The output shows the detailed results for each individual test and summary +results (note that this example elides the full filesystem paths from the +output shown): + +``` +{ + "children": [ + { + "duration_ms": 60, + "error": "Unhandled exception caught during execution", + "kind": "executionError", + "stdErr": "", + "stdOut": "Error: At ROOT_DIR/test_quickstart.mojo:8:17: AssertionError: `left == right` comparison failed:\r\n left: 1\r\n right: 0\r\n", + "testID": "ROOT_DIR/test_quickstart.mojo::test_inc_zero()" + }, + { + "duration_ms": 51, + "error": "", + "kind": "success", + "stdErr": "", + "stdOut": "", + "testID": "ROOT_DIR/test_quickstart.mojo::test_inc_one()" + } + ], + "duration_ms": 1171, + "error": "", + "kind": "executionError", + "stdErr": "", + "stdOut": "", + "testID": "ROOT_DIR/test_quickstart.mojo" +} +``` + +You can also produce JSON output for test collection as well. As an example, +consider the project structure shown in the [Running tests](#running-tests) +section. The following command collects a list in JSON format of all of the +tests defined in the `test` directory hierarchy: + +```bash +mojo test --diagnostic-format json --co test +``` + +The output would appear as follows (note that this example elides the full +filesystem paths from the output shown): + +``` +{ + "children": [ + { + "children": [ + { + "id": "ROOT_DIR/test/my_math/test_dec.mojo::test_dec_valid()", + "location": { + "endColumn": 5, + "endLine": 5, + "startColumn": 5, + "startLine": 5 + } + }, + { + "id": "ROOT_DIR/test/my_math/test_dec.mojo::test_dec_min()", + "location": { + "endColumn": 5, + "endLine": 9, + "startColumn": 5, + "startLine": 9 + } + } + ], + "id": "ROOT_DIR/test/my_math/test_dec.mojo" + }, + { + "children": [ + { + "id": "ROOT_DIR/test/my_math/test_inc.mojo::test_inc_valid()", + "location": { + "endColumn": 5, + "endLine": 5, + "startColumn": 5, + "startLine": 5 + } + }, + { + "id": "ROOT_DIR/test/my_math/test_inc.mojo::test_inc_max()", + "location": { + "endColumn": 5, + "endLine": 9, + "startColumn": 5, + "startLine": 9 + } + } + ], + "id": "ROOT_DIR/test/my_math/test_inc.mojo" + } + ], + "id": "ROOT_DIR/test/my_math" +} +``` + +## Writing API documentation tests + +The Mojo testing framework also supports testing code examples that you include +in [docstrings](/mojo/manual/basics#code-comments). This helps to ensure that +the code examples in your API documentation are correct and up to date. + +### Identifying executable code + +The Mojo testing framework requires you to explicitly identify the code blocks +that you want it to execute. + +In a Mojo docstring, a fenced code block delimited by standard triple-backquotes +is a *display-only* code block. It appears in the API documentation, but +`mojo test` does not identify it as a test or attempt to execute any of the code +in the block. + +```` +""" Non-executable code block example. + +The generated API documentation includes all lines of the following code block, +but `mojo test` does not execute any of the code in it. + +``` +# mojo test does NOT execute any of this code block +a = 1 +print(a) +``` +""" +```` + +In contrast, a fenced code block that starts with the line \`\`\`mojo +not only appears in the API documentation, but `mojo test` treats it as an +executable test. The test fails if the code raises any error, otherwise it +passes. + +```` +""" Executable code block example. + +The generated API documentation includes all lines of the following code block +*and* `mojo test` executes it as a test. + +```mojo +from testing import assert_equals + +b = 2 +assert_equals(b, 2) +``` +""" +```` + +Sometimes you might want to execute a line of code as part of the test but *not* +display that line in the API documentation. To achieve this, prefix the line of +code with `%#`. For example, you could use this technique to omit `import` +statements and assertion functions from the documentation. + +```` +""" Executable code block example with some code lines omitted from output. + +The generated API documentation includes only the lines of code that do *not* +start with `%#`. However, `mojo test` executes *all* lines of code. + +```mojo +%# from testing import assert_equal +c = 3 +print(c) +%# assert_equal(c, 3) +``` +""" +```` + +### Documentation test suites and scoping + +The Mojo testing framework treats each docstring as a separate *test suite*. +In other words, a single test suite could correspond to the docstring for an +individual package, module, function, struct, struct method, etc. + +Each executable code block within a given docstring is a single test of the same +test suite. The `mojo test` command executes the tests of a test suite +sequentially in the order that they appear within the docstring. If a test +within a particular test suite fails, then all subsequent tests within the same +test suite are skipped. + +All tests within the test suite execute in the same scope, and test execution +within that scope is stateful. This means, for example, that a variable created +within one test is then accessible to subsequent tests in the same test suite. + +```` +""" Stateful example. + +Assign 1 to the variable `a`: + +```mojo +%# from testing import assert_equal +a = 1 +%# assert_equal(a, 1) +``` + +Then increment the value of `a` by 1: + +```mojo +a += 1 +%# assert_equal(a, 2) +``` +""" +```` + +:::note + +Test suite scopes do *not* nest. In other words, the test suite scope of a +module is completely independent of the test suite scope of a function or struct +defined within that module. For example, this means that if a module’s test +suite creates a variable, that variable is *not* accessible to a function’s test +suite within the same module. + +::: + +### Documentation test identifiers + +The format of a documentation test identifier is `@::`. +This is best explained by an example. Consider the project structure shown in +the [Running tests](#running-tests) section. The source files in the `src` +directory might contain docstrings for the `my_math` package, the `utils.mojo` +module, and the individual functions within that module. You could collect the +full list of tests by executing: + +``` +mojo test --co src +``` + +The output shows the hierarchy of directories, test files, and individual tests +(note that this example elides the full filesystem paths from the output shown): + +``` + + + + + + + + + + + + + + +``` + +Several different test suites appear in this result: + +| Test suite scope | File | Test suite name | +| ---------------- | --------------------------- | ---------------------------------------------------- | +| Package | `src/my_math/__init__.mojo` | `__doc__` | +| Module | `src/my_math/utils.mojo` | `__doc__` | +| Function | `src/my_math/utils.mojo` | `inc(stdlib\3A\3Abuiltin\3A\3Aint\3A\3AInt).__doc__` | + +Then within a specific test suite, tests are numbered sequentially in the order +they appear in the docstring, starting with 0. From 10a22abb60943f7474a2f2413f56d478c3eda8b0 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 25 Nov 2024 18:13:30 -0700 Subject: [PATCH 1923/2019] [stdlib] Make non-type params for `UnsafePointer` kw only Make the non-type parameters for `UnsafePointer` keyword-only. This is desirable as there are a large number of parameters, and having those be positional only can be confusing and cause unintended conversions for some parameters. Many APIs that previously were expecting `*_` as the default positional parameters get updated to use `**_` as the keyword-only equivalent. The only positional parameter for `UnsafePointer` now is the type parameter. MODULAR_ORIG_COMMIT_REV_ID: d8f65de7f545939089dc90daad875f49c183348b --- docs/changelog.md | 4 +- stdlib/src/builtin/file.mojo | 4 +- stdlib/src/memory/memory.mojo | 63 ++++++++++----- stdlib/src/memory/owned_pointer.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 94 +++++++++++++--------- stdlib/src/os/atomic.mojo | 16 ++-- stdlib/src/random/random.mojo | 2 +- stdlib/src/sys/intrinsics.mojo | 12 +-- stdlib/src/utils/_serialize.mojo | 8 +- stdlib/test/memory/test_unsafepointer.mojo | 4 +- 10 files changed, 125 insertions(+), 84 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c8ec0e274b..858790cd11 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -121,7 +121,7 @@ what we publish. var l = List[Int](1, 2, 3, 4, 5) shuffle(l) ``` - + - The `Dict.__getitem__` method now returns a reference instead of a copy of the value (or raises). This improves the performance of common code that uses `Dict` by allowing borrows from the `Dict` elements. @@ -533,6 +533,8 @@ what we publish. - `Arc` has been renamed to `ArcPointer`, for consistency with `OwnedPointer`. +- `UnsafePointer` parameters (other than the type) are now keyword-only. + ### ❌ Removed - The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 0803d4f3e0..99e4d6a917 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -436,7 +436,9 @@ struct FileHandle: fn _write[ address_space: AddressSpace - ](self, ptr: UnsafePointer[UInt8, address_space], len: Int) raises: + ]( + self, ptr: UnsafePointer[UInt8, address_space=address_space], len: Int + ) raises: """Write the data to the file. Params: diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index ab215ab5b0..8f03fd2fb9 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -51,7 +51,11 @@ fn _align_down(value: Int, alignment: Int) -> Int: @always_inline fn _memcmp_impl_unconstrained[ type: DType -](s1: UnsafePointer[Scalar[type], _], s2: __type_of(s1), count: Int) -> Int: +]( + s1: UnsafePointer[Scalar[type], **_], + s2: UnsafePointer[Scalar[type], **_], + count: Int, +) -> Int: alias simd_width = simdwidthof[type]() if count < simd_width: for i in range(count): @@ -95,7 +99,11 @@ fn _memcmp_impl_unconstrained[ @always_inline fn _memcmp_impl[ type: DType -](s1: UnsafePointer[Scalar[type], _], s2: __type_of(s1), count: Int) -> Int: +]( + s1: UnsafePointer[Scalar[type], **_], + s2: UnsafePointer[Scalar[type], **_], + count: Int, +) -> Int: constrained[type.is_integral(), "the input dtype must be integral"]() return _memcmp_impl_unconstrained(s1, s2, count) @@ -104,8 +112,8 @@ fn _memcmp_impl[ fn memcmp[ type: AnyType, address_space: AddressSpace ]( - s1: UnsafePointer[type, address_space], - s2: UnsafePointer[type, address_space], + s1: UnsafePointer[type, address_space=address_space], + s2: UnsafePointer[type, address_space=address_space], count: Int, ) -> Int: """Compares two buffers. Both strings are assumed to be of the same length. @@ -144,7 +152,7 @@ fn memcmp[ @always_inline fn _memcpy_impl( - dest_data: UnsafePointer[Byte, *_], src_data: __type_of(dest_data), n: Int + dest_data: UnsafePointer[Byte, **_], src_data: __type_of(dest_data), n: Int ): """Copies a memory area. @@ -228,8 +236,8 @@ fn _memcpy_impl( fn memcpy[ T: AnyType ]( - dest: UnsafePointer[T, AddressSpace.GENERIC, *_], - src: UnsafePointer[T, AddressSpace.GENERIC, *_], + dest: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], + src: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], count: Int, ): """Copies a memory area. @@ -258,7 +266,11 @@ fn memcpy[ @always_inline("nodebug") fn _memset_impl[ address_space: AddressSpace -](ptr: UnsafePointer[Byte, address_space], value: Byte, count: Int): +]( + ptr: UnsafePointer[Byte, address_space=address_space], + value: Byte, + count: Int, +): alias simd_width = simdwidthof[Byte]() var vector_end = _align_down(count, simd_width) @@ -272,7 +284,11 @@ fn _memset_impl[ @always_inline fn memset[ type: AnyType, address_space: AddressSpace -](ptr: UnsafePointer[type, address_space], value: Byte, count: Int): +]( + ptr: UnsafePointer[type, address_space=address_space], + value: Byte, + count: Int, +): """Fills memory with the given value. Parameters: @@ -295,7 +311,7 @@ fn memset[ @always_inline fn memset_zero[ type: AnyType, address_space: AddressSpace, // -](ptr: UnsafePointer[type, address_space], count: Int): +](ptr: UnsafePointer[type, address_space=address_space], count: Int): """Fills memory with zeros. Parameters: @@ -312,7 +328,7 @@ fn memset_zero[ @always_inline fn memset_zero[ type: DType, address_space: AddressSpace, //, *, count: Int -](ptr: UnsafePointer[Scalar[type], address_space]): +](ptr: UnsafePointer[Scalar[type], address_space=address_space]): """Fills memory with zeros. Parameters: @@ -351,7 +367,7 @@ fn stack_allocation[ /, alignment: Int = alignof[type]() if is_gpu() else 1, address_space: AddressSpace = AddressSpace.GENERIC, -]() -> UnsafePointer[Scalar[type], address_space]: +]() -> UnsafePointer[Scalar[type], address_space=address_space]: """Allocates data buffer space on the stack given a data type and number of elements. @@ -378,7 +394,7 @@ fn stack_allocation[ name: Optional[StringLiteral] = None, alignment: Int = alignof[type]() if is_gpu() else 1, address_space: AddressSpace = AddressSpace.GENERIC, -]() -> UnsafePointer[type, address_space]: +]() -> UnsafePointer[type, address_space=address_space]: """Allocates data buffer space on the stack given a data type and number of elements. @@ -402,7 +418,9 @@ fn stack_allocation[ return __mlir_op.`pop.global_alloc`[ name = global_name.value, count = count.value, - _type = UnsafePointer[type, address_space]._mlir_type, + _type = UnsafePointer[ + type, address_space=address_space + ]._mlir_type, alignment = alignment.value, ]() # MSTDL-797: The NVPTX backend requires that `alloca` instructions may @@ -415,13 +433,15 @@ fn stack_allocation[ alignment = alignment.value, ]() return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[type, address_space]._mlir_type + _type = UnsafePointer[ + type, address_space=address_space + ]._mlir_type ](generic_ptr) # Perofrm a stack allocation of the requested size, alignment, and type. return __mlir_op.`pop.stack_allocation`[ count = count.value, - _type = UnsafePointer[type, address_space]._mlir_type, + _type = UnsafePointer[type, address_space=address_space]._mlir_type, alignment = alignment.value, ]() @@ -438,16 +458,19 @@ fn _malloc[ *, alignment: Int = alignof[type]() if is_gpu() else 1, ](size: Int, /) -> UnsafePointer[ - type, AddressSpace.GENERIC, alignment=alignment + type, address_space = AddressSpace.GENERIC, alignment=alignment ]: @parameter if is_gpu(): return external_call[ - "malloc", UnsafePointer[NoneType, AddressSpace.GENERIC] + "malloc", + UnsafePointer[NoneType, address_space = AddressSpace.GENERIC], ](size).bitcast[type]() else: return __mlir_op.`pop.aligned_alloc`[ - _type = UnsafePointer[type, AddressSpace.GENERIC]._mlir_type + _type = UnsafePointer[ + type, address_space = AddressSpace.GENERIC + ]._mlir_type ](alignment.value, size.value) @@ -457,7 +480,7 @@ fn _malloc[ @always_inline -fn _free(ptr: UnsafePointer[_, AddressSpace.GENERIC, *_]): +fn _free(ptr: UnsafePointer[_, address_space = AddressSpace.GENERIC, *_, **_]): @parameter if is_gpu(): libc.free(ptr.bitcast[NoneType]()) diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 4f00050e89..597375cbf4 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -25,7 +25,7 @@ struct OwnedPointer[T: AnyType]: T: The type to be stored in the OwnedPointer[]. """ - var _inner: UnsafePointer[T, AddressSpace.GENERIC] + var _inner: UnsafePointer[T, address_space = AddressSpace.GENERIC] # ===-------------------------------------------------------------------===# # Life cycle methods diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 9c75cc59a6..5f37054609 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -51,6 +51,7 @@ fn _default_alignment[type: DType, width: Int = 1]() -> Int: @register_passable("trivial") struct UnsafePointer[ type: AnyType, + *, address_space: AddressSpace = AddressSpace.GENERIC, alignment: Int = _default_alignment[type](), origin: Origin[True].type = MutableAnyOrigin, @@ -115,7 +116,9 @@ struct UnsafePointer[ @always_inline @implicit - fn __init__(out self, other: UnsafePointer[type, address_space, *_, **_]): + fn __init__( + out self, other: UnsafePointer[type, address_space=address_space, **_] + ): """Exclusivity parameter cast a pointer. Args: @@ -144,8 +147,9 @@ struct UnsafePointer[ ref [_, address_space._value.value]arg: type ) -> UnsafePointer[ type, - address_space, - 1, + address_space=address_space, + alignment=1, + origin=MutableAnyOrigin # TODO: Propagate origin of the argument. ] as result: """Gets the address of the argument. @@ -164,7 +168,9 @@ struct UnsafePointer[ @always_inline fn alloc( count: Int, - ) -> UnsafePointer[type, AddressSpace.GENERIC, alignment]: + ) -> UnsafePointer[ + type, address_space = AddressSpace.GENERIC, alignment=alignment + ]: """Allocate an array with specified or default alignment. Args: @@ -195,9 +201,12 @@ struct UnsafePointer[ alias _ref_type = Pointer[type, origin, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( - UnsafePointer[type, address_space, alignment, origin]( - self - ).address + UnsafePointer[ + type, + address_space=address_space, + alignment=alignment, + origin=origin, + ](self).address ) ) @@ -433,7 +442,9 @@ struct UnsafePointer[ @always_inline("nodebug") fn as_noalias_ptr( self, - ) -> UnsafePointer[type, address_space, alignment, origin]: + ) -> UnsafePointer[ + type, address_space=address_space, alignment=alignment, origin=origin + ]: """Cast the pointer to a new pointer that is known not to locally alias any other pointer. In other words, the pointer transitively does not alias any other memory value declared in the local function context. @@ -454,7 +465,7 @@ struct UnsafePointer[ alignment: Int = _default_alignment[type, width](), volatile: Bool = False, invariant: Bool = False, - ](self: UnsafePointer[Scalar[type], *_, **_]) -> SIMD[type, width]: + ](self: UnsafePointer[Scalar[type], **_]) -> SIMD[type, width]: """Loads the value the pointer points to. Constraints: @@ -530,7 +541,7 @@ struct UnsafePointer[ alignment: Int = _default_alignment[type, width](), volatile: Bool = False, invariant: Bool = False, - ](self: UnsafePointer[Scalar[type], *_, **_], offset: Scalar) -> SIMD[ + ](self: UnsafePointer[Scalar[type], **_], offset: Scalar) -> SIMD[ type, width ]: """Loads the value the pointer points to with the given offset. @@ -569,9 +580,7 @@ struct UnsafePointer[ alignment: Int = _default_alignment[type, width](), volatile: Bool = False, invariant: Bool = False, - ](self: UnsafePointer[Scalar[type], *_, **_], offset: T) -> SIMD[ - type, width - ]: + ](self: UnsafePointer[Scalar[type], **_], offset: T) -> SIMD[type, width]: """Loads the value the pointer points to with the given offset. Constraints: @@ -605,11 +614,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type](), volatile: Bool = False, - ]( - self: UnsafePointer[Scalar[type], *_, **_], - offset: T, - val: Scalar[type], - ): + ](self: UnsafePointer[Scalar[type], **_], offset: T, val: Scalar[type],): """Stores a single element value at the given offset. Constraints: @@ -637,7 +642,7 @@ struct UnsafePointer[ alignment: Int = _default_alignment[type, width](), volatile: Bool = False, ]( - self: UnsafePointer[Scalar[type], *_, **_], + self: UnsafePointer[Scalar[type], **_], offset: T, val: SIMD[type, width], ): @@ -668,7 +673,7 @@ struct UnsafePointer[ alignment: Int = _default_alignment[type](), volatile: Bool = False, ]( - self: UnsafePointer[Scalar[type], *_, **_], + self: UnsafePointer[Scalar[type], **_], offset: Scalar[offset_type], val: Scalar[type], ): @@ -701,7 +706,7 @@ struct UnsafePointer[ alignment: Int = _default_alignment[type, width](), volatile: Bool = False, ]( - self: UnsafePointer[Scalar[type], *_, **_], + self: UnsafePointer[Scalar[type], **_], offset: Scalar[offset_type], val: SIMD[type, width], ): @@ -732,7 +737,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type](), volatile: Bool = False, - ](self: UnsafePointer[Scalar[type], *_, **_], val: Scalar[type]): + ](self: UnsafePointer[Scalar[type], **_], val: Scalar[type]): """Stores a single element value. Constraints: @@ -755,7 +760,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type, width](), volatile: Bool = False, - ](self: UnsafePointer[Scalar[type], *_, **_], val: SIMD[type, width]): + ](self: UnsafePointer[Scalar[type], **_], val: SIMD[type, width]): """Stores a single element value. Constraints: @@ -779,7 +784,7 @@ struct UnsafePointer[ *, alignment: Int = _default_alignment[type, width](), volatile: Bool = False, - ](self: UnsafePointer[Scalar[type], *_, **_], val: SIMD[type, width]): + ](self: UnsafePointer[Scalar[type], **_], val: SIMD[type, width]): constrained[width > 0, "width must be a positive integer value"]() constrained[ alignment > 0, "alignment must be a positive integer value" @@ -798,9 +803,7 @@ struct UnsafePointer[ @always_inline("nodebug") fn strided_load[ type: DType, T: Intable, //, width: Int - ](self: UnsafePointer[Scalar[type], *_, **_], stride: T) -> SIMD[ - type, width - ]: + ](self: UnsafePointer[Scalar[type], **_], stride: T) -> SIMD[type, width]: """Performs a strided load of the SIMD vector. Parameters: @@ -822,7 +825,7 @@ struct UnsafePointer[ T: Intable, //, width: Int, ]( - self: UnsafePointer[Scalar[type], *_, **_], + self: UnsafePointer[Scalar[type], **_], val: SIMD[type, width], stride: T, ): @@ -846,7 +849,7 @@ struct UnsafePointer[ width: Int = 1, alignment: Int = _default_alignment[type, width](), ]( - self: UnsafePointer[Scalar[type], *_, **_], + self: UnsafePointer[Scalar[type], **_], offset: SIMD[_, width], mask: SIMD[DType.bool, width] = True, default: SIMD[type, width] = 0, @@ -901,7 +904,7 @@ struct UnsafePointer[ width: Int = 1, alignment: Int = _default_alignment[type, width](), ]( - self: UnsafePointer[Scalar[type], *_, **_], + self: UnsafePointer[Scalar[type], **_], offset: SIMD[_, width], val: SIMD[type, width], mask: SIMD[DType.bool, width] = True, @@ -949,7 +952,7 @@ struct UnsafePointer[ scatter(val, base, mask, alignment) @always_inline - fn free(self: UnsafePointer[_, AddressSpace.GENERIC, *_, **_]): + fn free(self: UnsafePointer[_, address_space = AddressSpace.GENERIC, **_]): """Free the memory referenced by the pointer.""" _free(self) @@ -960,7 +963,9 @@ struct UnsafePointer[ address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, origin: Origin[True].type = Self.origin, - ](self) -> UnsafePointer[T, address_space, alignment, origin]: + ](self) -> UnsafePointer[ + T, address_space=address_space, alignment=alignment, origin=origin + ]: """Bitcasts a UnsafePointer to a different type. Parameters: @@ -975,13 +980,13 @@ struct UnsafePointer[ """ return __mlir_op.`pop.pointer.bitcast`[ _type = UnsafePointer[ - T, address_space, alignment=alignment + T, address_space=address_space, alignment=alignment ]._mlir_type, ](self.address) @always_inline fn destroy_pointee( - self: UnsafePointer[type, AddressSpace.GENERIC, *_, **_] + self: UnsafePointer[type, address_space = AddressSpace.GENERIC, **_] ): """Destroy the pointed-to value. @@ -996,7 +1001,7 @@ struct UnsafePointer[ @always_inline fn take_pointee[ T: Movable, //, - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_]) -> T: + ](self: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_]) -> T: """Move the value at the pointer out, leaving it uninitialized. The pointer must not be null, and the pointer memory location is assumed @@ -1019,7 +1024,10 @@ struct UnsafePointer[ @always_inline fn init_pointee_move[ T: Movable, //, - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], owned value: T): + ]( + self: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], + owned value: T, + ): """Emplace a new value into the pointer location, moving from `value`. The pointer memory location is assumed to contain uninitialized data, @@ -1041,7 +1049,10 @@ struct UnsafePointer[ @always_inline fn init_pointee_copy[ T: Copyable, //, - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], value: T): + ]( + self: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], + value: T, + ): """Emplace a copy of `value` into the pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -1063,7 +1074,10 @@ struct UnsafePointer[ @always_inline fn init_pointee_explicit_copy[ T: ExplicitlyCopyable, // - ](self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], value: T): + ]( + self: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], + value: T, + ): """Emplace a copy of `value` into this pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -1087,8 +1101,8 @@ struct UnsafePointer[ fn move_pointee_into[ T: Movable, //, ]( - self: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], - dst: UnsafePointer[T, AddressSpace.GENERIC, *_, **_], + self: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], + dst: UnsafePointer[T, address_space = AddressSpace.GENERIC, **_], ): """Moves the value `self` points to into the memory location pointed to by `dst`. diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 73a97c09ea..90a1952246 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -62,7 +62,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline fn _fetch_add( - ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type] + ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type] ) -> Scalar[type]: """Performs atomic in-place add. @@ -200,7 +200,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline - fn max(ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): + fn max(ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): """Performs atomic in-place max on the pointer. Atomically replaces the current value pointer to by `ptr` by the result @@ -240,7 +240,7 @@ struct Atomic[type: DType]: @staticmethod @always_inline - fn min(ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): + fn min(ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): """Performs atomic in-place min on the pointer. Atomically replaces the current value pointer to by `ptr` by the result @@ -289,7 +289,7 @@ struct Atomic[type: DType]: fn _compare_exchange_weak_integral_impl[ type: DType, // ]( - value_addr: UnsafePointer[Scalar[type], *_], + value_addr: UnsafePointer[Scalar[type], **_], inout expected: Scalar[type], desired: Scalar[type], ) -> Bool: @@ -318,7 +318,7 @@ fn _compare_exchange_weak_integral_impl[ @always_inline fn _max_impl_base[ type: DType, // -](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): +](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): var value_addr = ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]() _ = __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, @@ -330,7 +330,7 @@ fn _max_impl_base[ @always_inline fn _min_impl_base[ type: DType, // -](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): +](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): var value_addr = ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]() _ = __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, @@ -342,7 +342,7 @@ fn _min_impl_base[ @always_inline fn _max_impl[ type: DType, // -](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): +](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): @parameter if is_nvidia_gpu() and type.is_floating_point(): alias integral_type = _integral_type_of[type]() @@ -365,7 +365,7 @@ fn _max_impl[ @always_inline fn _min_impl[ type: DType, // -](ptr: UnsafePointer[Scalar[type], *_], rhs: Scalar[type]): +](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): @parameter if is_nvidia_gpu() and type.is_floating_point(): alias integral_type = _integral_type_of[type]() diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index f0b8dee2ee..37461dd625 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -121,7 +121,7 @@ fn randint[ fn rand[ type: DType ]( - ptr: UnsafePointer[Scalar[type], *_], + ptr: UnsafePointer[Scalar[type], **_], size: Int, /, *, diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 38f6ae8e5b..054c66c497 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -468,7 +468,7 @@ struct PrefetchOptions: @always_inline("nodebug") fn prefetch[ type: DType, //, params: PrefetchOptions = PrefetchOptions() -](addr: UnsafePointer[Scalar[type], *_]): +](addr: UnsafePointer[Scalar[type], **_]): """Prefetches an instruction or data into cache before it is used. The prefetch function provides prefetching hints for the target @@ -508,7 +508,7 @@ fn prefetch[ fn masked_load[ type: DType, //, size: Int ]( - addr: UnsafePointer[Scalar[type], *_], + addr: UnsafePointer[Scalar[type], **_], mask: SIMD[DType.bool, size], passthrough: SIMD[type, size], alignment: Int = 1, @@ -555,7 +555,7 @@ fn masked_store[ size: Int ]( value: SIMD, - addr: UnsafePointer[Scalar[value.type], *_], + addr: UnsafePointer[Scalar[value.type], **_], mask: SIMD[DType.bool, size], alignment: Int = 1, ): @@ -597,7 +597,7 @@ fn compressed_store[ type: DType, size: Int ]( value: SIMD[type, size], - addr: UnsafePointer[Scalar[type], *_], + addr: UnsafePointer[Scalar[type], **_], mask: SIMD[DType.bool, size], ): """Compresses the lanes of `value`, skipping `mask` lanes, and stores @@ -636,7 +636,7 @@ fn compressed_store[ fn strided_load[ type: DType, //, simd_width: Int ]( - addr: UnsafePointer[Scalar[type], *_], + addr: UnsafePointer[Scalar[type], **_], stride: Int, mask: SIMD[DType.bool, simd_width] = True, ) -> SIMD[type, simd_width]: @@ -677,7 +677,7 @@ fn strided_store[ type: DType, //, simd_width: Int ]( value: SIMD[type, simd_width], - addr: UnsafePointer[Scalar[type], *_], + addr: UnsafePointer[Scalar[type], **_], stride: Int, mask: SIMD[DType.bool, simd_width] = True, ): diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 5eadeb4788..ddf05a31ac 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -25,7 +25,7 @@ alias _kCompactElemPerSide = _kCompactMaxElemsToPrint // 2 fn _serialize_elements_compact[ type: DType, //, serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, -](ptr: UnsafePointer[Scalar[type], _], len: Int): +](ptr: UnsafePointer[Scalar[type], **_], len: Int): serialize_fn(_kStartTensorMarker) if len < _kCompactMaxElemsToPrint: _serialize_elements_complete[serialize_fn=serialize_fn](ptr, len) @@ -46,7 +46,7 @@ fn _serialize_elements_compact[ fn _serialize_elements_complete[ type: DType, //, serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, -](ptr: UnsafePointer[Scalar[type], _], len: Int): +](ptr: UnsafePointer[Scalar[type], **_], len: Int): if len == 0: return serialize_fn(ptr.load()) @@ -59,7 +59,7 @@ fn _serialize_elements[ type: DType, //, serialize_fn: fn[T: Writable] (elem: T) capturing [_] -> None, compact: Bool = False, -](ptr: UnsafePointer[Scalar[type], _], len: Int): +](ptr: UnsafePointer[Scalar[type], **_], len: Int): @parameter if compact: _serialize_elements_compact[serialize_fn=serialize_fn](ptr, len) @@ -73,7 +73,7 @@ fn _serialize[ serialize_dtype: Bool = True, serialize_shape: Bool = True, serialize_end_line: Bool = True, -](ptr: UnsafePointer[Scalar[type], _], shape: List[Int, *_]): +](ptr: UnsafePointer[Scalar[type], **_], shape: List[Int, *_]): var rank = len(shape) if rank == 0: if serialize_end_line: diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 657fc02b81..a44fb58cf6 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -178,10 +178,10 @@ def test_comparisons(): def test_unsafepointer_address_space(): - var p1 = UnsafePointer[Int, AddressSpace(0)].alloc(1) + var p1 = UnsafePointer[Int, address_space = AddressSpace(0)].alloc(1) p1.free() - var p2 = UnsafePointer[Int, AddressSpace.GENERIC].alloc(1) + var p2 = UnsafePointer[Int, address_space = AddressSpace.GENERIC].alloc(1) p2.free() From e5f32ff89bb08304a57109f66ba3c4e3f188d505 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 25 Nov 2024 23:08:35 -0800 Subject: [PATCH 1924/2019] [mojo-lang] Enable binding to inferred-only params with keywords. This enables inferred only params to be bound with keywords, enabling some important standard library idioms to be ergonomic, e.g.: ``` # In the standard library already: struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]: ... # Example alias alias ImmStringSlice = StringSlice[is_mutable=False] # This auto-parameterizes on the origin, but constrains it to being an # immutable slice instead of a potentially mutable one. fn take_imm_slice(a: ImmStringSlice): ... ``` We need to decide on the exact naming convention, this is just an example. MODULAR_ORIG_COMMIT_REV_ID: 0376f5ed8d24212afe004a8f96169665b694c715 --- docs/changelog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 858790cd11..bef76b5e3d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -535,6 +535,17 @@ what we publish. - `UnsafePointer` parameters (other than the type) are now keyword-only. +- Inferred-only parameters may now be explicitly bound with keywords, enabling + some important patterns in the standard library: + + ```mojo + struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]: ... + alias ImmStringSlice = StringSlice[is_mutable=False] + # This auto-parameterizes on the origin, but constrains it to being an + # immutable slice instead of a potentially mutable one. + fn take_imm_slice(a: ImmStringSlice): ... + ``` + ### ❌ Removed - The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your From bd72610fd4c804fffda4ca0a3816a2d0d06a2f17 Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Tue, 26 Nov 2024 10:00:28 -0800 Subject: [PATCH 1925/2019] [stdlib] Adopt parametric inline_count for source_loc The `inlineCount` field of source_loc now allows non-immediate index-typed parameters. This PR updates its usage, and exposes the parameter to users of `__call_location`. MODULAR_ORIG_COMMIT_REV_ID: 2c213a20a9e4447c8a8f092215cf4e60cc8d64b1 --- stdlib/src/builtin/_location.mojo | 33 +++++++++++++++--------- stdlib/test/builtin/test_location.mojo | 35 ++++++++++++++++++-------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 0940e49f8e..2816204b62 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -57,7 +57,7 @@ struct _SourceLocation(Writable, Stringable): @always_inline("nodebug") fn __source_location() -> _SourceLocation: - """Returns the location where it's called. + """Returns the location for where this function is called. This currently doesn't work when called in a parameter expression. @@ -68,7 +68,7 @@ fn __source_location() -> _SourceLocation: var col: __mlir_type.index var file_name: __mlir_type.`!kgen.string` line, col, file_name = __mlir_op.`kgen.source_loc`[ - _properties = __mlir_attr.`{inlineCount = 0 : i64}`, + inlineCount = Int(0).value, _type = ( __mlir_type.index, __mlir_type.index, @@ -80,26 +80,35 @@ fn __source_location() -> _SourceLocation: @always_inline("nodebug") -fn __call_location() -> _SourceLocation: - """Returns the location where the enclosing function is called. +fn __call_location[inline_count: Int = 1]() -> _SourceLocation: + """Returns the location for where the caller of this function is called. An + optional `inline_count` parameter can be specified to skip over that many + levels of calling functions. - This should only be used in `@always_inline` or `@always_inline("nodebug")` - functions so that it returns the source location of where the enclosing - function is called at (even if inside another `@always_inline("nodebug")` - function). + This should only be used when enclosed in a series of `@always_inline` or + `@always_inline("nodebug")` function calls, where the layers of calling + functions is no fewer than `inline_count`. - This currently doesn't work when this or the enclosing function is called in - a parameter expression. + For example, when `inline_count = 1`, only the caller of this function needs + to be `@always_inline` or `@always_inline("nodebug")`. This function will + return the source location of the caller's invocation. + + When `inline_count = 2`, the caller of the caller of this function also + needs to be inlined. This function will return the source location of the + caller's caller's invocation. + + This currently doesn't work when the `inline_count`-th wrapping caller is + called in a parameter expression. Returns: - The location information of where the enclosing function (i.e. the + The location information of where the caller of this function (i.e. the function whose body __call_location() is used in) is called. """ var line: __mlir_type.index var col: __mlir_type.index var file_name: __mlir_type.`!kgen.string` line, col, file_name = __mlir_op.`kgen.source_loc`[ - _properties = __mlir_attr.`{inlineCount = 1 : i64}`, + inlineCount = inline_count.value, _type = ( __mlir_type.index, __mlir_type.index, diff --git a/stdlib/test/builtin/test_location.mojo b/stdlib/test/builtin/test_location.mojo index c70610663d..cc9a706249 100644 --- a/stdlib/test/builtin/test_location.mojo +++ b/stdlib/test/builtin/test_location.mojo @@ -126,20 +126,22 @@ fn test_parameter_context() raises: @always_inline -fn capture_call_loc(cond: Bool = False) -> _SourceLocation: +fn capture_call_loc[depth: Int = 1](cond: Bool = False) -> _SourceLocation: if ( not cond ): # NOTE: we test that __call_location works even in a nested scope. - return __call_location() + return __call_location[depth]() return _SourceLocation(-1, -1, "") @always_inline("nodebug") -fn capture_call_loc_nodebug(cond: Bool = False) -> _SourceLocation: +fn capture_call_loc_nodebug[ + depth: Int = 1 +](cond: Bool = False) -> _SourceLocation: if ( not cond ): # NOTE: we test that __call_location works even in a nested scope. - return __call_location() + return __call_location[depth]() return _SourceLocation(-1, -1, "") @@ -151,13 +153,22 @@ fn get_call_locs() -> (_SourceLocation, _SourceLocation): @always_inline("nodebug") -fn get_call_locs_inlined() -> (_SourceLocation, _SourceLocation): +fn get_call_locs_inlined[ + depth: Int = 1 +]() -> (_SourceLocation, _SourceLocation): return ( - capture_call_loc(), - capture_call_loc_nodebug(), + capture_call_loc[depth](), + capture_call_loc_nodebug[depth](), ) +@always_inline +fn get_call_locs_inlined_twice[ + depth: Int = 1 +]() -> (_SourceLocation, _SourceLocation): + return get_call_locs_inlined[depth]() + + fn get_four_call_locs() -> ( _SourceLocation, _SourceLocation, @@ -182,8 +193,8 @@ fn get_four_call_locs_inlined() -> ( fn test_builtin_call_loc() raises: - var l = (148, 149, 156, 157) - var c = (25, 33, 25, 33) + var l = (150, 151, 160, 161) + var c = (25, 33, 32, 40) var loc_pair = get_call_locs() check_source_loc(l[0], c[0], loc_pair[0]) check_source_loc(l[1], c[1], loc_pair[1]) @@ -192,6 +203,10 @@ fn test_builtin_call_loc() raises: check_source_loc(l[2], c[2], loc_pair[0]) check_source_loc(l[3], c[3], loc_pair[1]) + loc_pair = get_call_locs_inlined_twice[2]() + check_source_loc(169, 40, loc_pair[0]) + check_source_loc(169, 40, loc_pair[1]) + var loc_quad = get_four_call_locs() check_source_loc(l[0], c[0], loc_quad[0]) check_source_loc(l[1], c[1], loc_quad[1]) @@ -211,7 +226,7 @@ fn source_loc_with_debug() -> _SourceLocation: var col: __mlir_type.index var file_name: __mlir_type.`!kgen.string` line, col, file_name = __mlir_op.`kgen.source_loc`[ - _properties = __mlir_attr.`{inlineCount = 0 : i64}`, + inlineCount = Int(0).value, _type = ( __mlir_type.index, __mlir_type.index, From e7f116a659e7ba399dc1d3029253a2c441830b49 Mon Sep 17 00:00:00 2001 From: Evan Ovadia Date: Tue, 26 Nov 2024 11:47:18 -0800 Subject: [PATCH 1926/2019] Adds basic support for linear types, via the @explicit_destroy keyword and the temporary UnknownDestructibility trait. MODULAR_ORIG_COMMIT_REV_ID: 34f0f717b658780cecba602647f139aa581ff752 --- stdlib/src/builtin/anytype.mojo | 26 ++++++++++++++++++++++---- stdlib/src/prelude/__init__.mojo | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/anytype.mojo b/stdlib/src/builtin/anytype.mojo index 8c7a061ba5..c76235fc9c 100644 --- a/stdlib/src/builtin/anytype.mojo +++ b/stdlib/src/builtin/anytype.mojo @@ -20,6 +20,20 @@ These are Mojo built-ins, so you don't need to import them. # ===----------------------------------------------------------------------=== # +# TODO(MOCO-1468): Add @explicit_destroy here so we get an error message, +# preferably one that mentions a link the user can go to to learn about +# linear types. +trait UnknownDestructibility: + """The UnknownDestructibility trait is the most basic trait, that all other + types extend. + + This has no __del__ method. For types that should have the __del__ method, + use ImplicitlyDestructible instead. + """ + + pass + + trait AnyType: """The AnyType trait describes a type that has a destructor. @@ -32,10 +46,9 @@ trait AnyType: lifetime, and the resultant type receives a destructor regardless of whether the user explicitly defines one. - All types pessimistically require a destructor when used in generic - functions. Hence, all Mojo traits are considered to inherit from - AnyType, providing a default no-op destructor implementation for types - that may need them. + Unless they specify @explicit_destroy, all Mojo structs and traits are + considered to inherit from AnyType, providing a default no-op destructor + implementation for types that may need them. Example implementing the `AnyType` trait on `Foo` that frees the allocated memory: @@ -66,3 +79,8 @@ trait AnyType: end of this function. """ ... + + +# A temporary alias to help with the linear types transition, see +# https://www.notion.so/modularai/Linear-Types-14a1044d37bb809ab074c990fe1a84e3. +alias ImplicitlyDestructible = AnyType diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 874d79cbab..5592ec2663 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -14,7 +14,7 @@ that are automatically imported into every Mojo program. """ -from builtin.anytype import AnyType +from builtin.anytype import UnknownDestructibility, AnyType from builtin.bool import Boolable, ImplicitlyBoolable, Bool, bool, any, all from builtin.breakpoint import breakpoint from builtin.builtin_list import ( From af8657f88b4b524288e10b1063c414cf936f2544 Mon Sep 17 00:00:00 2001 From: Jiexiang Liu <80805665+jiex-liu@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:08:23 -0800 Subject: [PATCH 1927/2019] [******][gpu] add fp8 fnuz variant to dtype This PR adds fp8 fnuz dtype to mojo for AMD GPU. MODULAR_ORIG_COMMIT_REV_ID: f9b0efcf079e85fd1907888617c51bcb68ca5b46 --- stdlib/src/builtin/dtype.mojo | 33 +++++++++++++++++++++++++++++++-- stdlib/src/builtin/simd.mojo | 15 +++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index fa7a222492..09afc140d9 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -67,10 +67,22 @@ struct DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) """Represents a FP8E5M2 floating point format whose bitwidth is 8.""" + alias float8e5m2fnuz = DType( + __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` + ) + """Represents a FP8E5M2FNUZ floating point format for AMD GPU whose bitwdith is 8. + This dtype only supports finite and NaN values. NaN is when sign bit is + set and all other exponent and mantissa bits are 0.""" alias float8e4m3 = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) """Represents a FP8E4M3 floating point format whose bitwidth is 8.""" + alias float8e4m3fnuz = DType( + __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` + ) + """Represents a FP8E4M3FNUZ floating point format for AMD GPU whose bitwdith is 8. + This dtype only supports finite and NaN values. NaN is when sign bit is + set and all other exponent and mantissa bits are 0.""" alias bfloat16 = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) @@ -132,8 +144,12 @@ struct DType( return DType.index elif str == String("float8e5m2"): return DType.float8e5m2 + elif str == String("float8e5m2fnuz"): + return DType.float8e5m2fnuz elif str == String("float8e4m3"): return DType.float8e4m3 + elif str == String("float8e4m3fnuz"): + return DType.float8e4m3fnuz elif str == String("bfloat16"): return DType.bfloat16 elif str == String("float16"): @@ -193,8 +209,12 @@ struct DType( return writer.write("index") if self == DType.float8e5m2: return writer.write("float8e5m2") + if self == DType.float8e5m2fnuz: + return writer.write("float8e5m2fnuz") if self == DType.float8e4m3: return writer.write("float8e4m3") + if self == DType.float8e4m3fnuz: + return writer.write("float8e4m3fnuz") if self == DType.bfloat16: return writer.write("bfloat16") if self == DType.float16: @@ -397,13 +417,18 @@ struct DType( @always_inline("nodebug") fn is_float8(self) -> Bool: """Returns True if the type is a 8bit-precision floating point type, - e.g. either float8e5m2 or float8e4m3. + e.g. float8e5m2, float8e5m2fnuz, float8e4m3 and float8e4m3fnuz. Returns: True if the type is a 8bit-precision float, false otherwise. """ - return self in (DType.float8e5m2, DType.float8e4m3) + return self in ( + DType.float8e5m2, + DType.float8e4m3, + DType.float8e5m2fnuz, + DType.float8e4m3fnuz, + ) @always_inline("nodebug") fn is_half_float(self) -> Bool: @@ -459,8 +484,12 @@ struct DType( return sizeof[DType.index]() if self == DType.float8e5m2: return sizeof[DType.float8e5m2]() + if self == DType.float8e5m2fnuz: + return sizeof[DType.float8e5m2fnuz]() if self == DType.float8e4m3: return sizeof[DType.float8e4m3]() + if self == DType.float8e4m3fnuz: + return sizeof[DType.float8e4m3fnuz]() if self == DType.bfloat16: return sizeof[DType.bfloat16]() if self == DType.float16: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8146759a4b..60ed1aaebf 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -95,8 +95,16 @@ alias UInt64 = Scalar[DType.uint64] alias Float8e5m2 = Scalar[DType.float8e5m2] """Represents a FP8E5M2 floating point format whose bitwidth is 8.""" +alias Float8e5m2fnuz = Scalar[DType.float8e5m2fnuz] +"""Represents a FP8E5M2FNUZ floating point format for AMD GPU whose bitwdith is 8. + This dtype only supports finite and NaN values. NaN is when sign bit is set and + all other exponent and mantissa bits are 0.""" alias Float8e4m3 = Scalar[DType.float8e4m3] """Represents a FP8E4M3 floating point format whose bitwidth is 8.""" +alias Float8e4m3fnuz = Scalar[DType.float8e4m3fnuz] +"""Represents a FP8E4M3FNUZ floating point format for AMD GPU whose bitwdith is 8. + This dtype only supports finite and NaN values. NaN is when sign bit is set and + all other exponent and mantissa bits are 0.""" alias BFloat16 = Scalar[DType.bfloat16] """Represents a 16-bit brain floating point value.""" alias Float16 = Scalar[DType.float16] @@ -137,6 +145,13 @@ fn _simd_construction_checks[type: DType, size: Int](): not (type.is_float8() and not _has_native_f8_support()), "f8 is not supported on non sm_89 and sm_90 architectures", ]() + constrained[ + not ( + type in (DType.float8e4m3fnuz, DType.float8e5m2fnuz) + and not is_amd_gpu() + ), + "f8 fnuz variants is only supported for AMD GPU.", + ]() @always_inline("nodebug") From 39a5aa730f3344a2107a32fbf0b6bafb88f70f6f Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 26 Nov 2024 14:14:25 -0800 Subject: [PATCH 1928/2019] Remove unnecessary fstrings MODULAR_ORIG_COMMIT_REV_ID: 0c1c7f388099adaf2c8a02c6f822732fff35e2b6 --- stdlib/scripts/check-docstrings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/scripts/check-docstrings.py b/stdlib/scripts/check-docstrings.py index eb4b180125..cdd5b02f98 100644 --- a/stdlib/scripts/check-docstrings.py +++ b/stdlib/scripts/check-docstrings.py @@ -31,7 +31,7 @@ def main(): ] result = subprocess.run(command, capture_output=True) if result.stderr or result.returncode != 0: - print(f"Docstring issue found in the stdlib: ") + print("Docstring issue found in the stdlib: ") print(result.stderr.decode()) sys.exit(1) From 1ab5073f157e41d6221c107589787466ccaa9f26 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 26 Nov 2024 16:27:43 -0800 Subject: [PATCH 1929/2019] Remove unused python imports MODULAR_ORIG_COMMIT_REV_ID: 7cd9e7bb0e008361728cbf78ba4c66722e32339d --- stdlib/test/lit.cfg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index ad8ab36e9a..33e85712be 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -12,7 +12,6 @@ # ===----------------------------------------------------------------------=== # import os -import platform import shutil from pathlib import Path From 4eaddbb7a16cf5c3744cabeaf96353305be8f7e3 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 27 Nov 2024 00:40:45 -0800 Subject: [PATCH 1930/2019] [******][GPU] Move the default target compile options to the info struct MODULAR_ORIG_COMMIT_REV_ID: c3668f8bfebbe9ae0232a28a2b102373be9e2c61 --- stdlib/src/sys/info.mojo | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 840e5de7f6..a2f1891fc9 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -33,15 +33,13 @@ fn _accelerator_arch() -> StringLiteral: return __mlir_attr.`#kgen.param.expr : !kgen.string` -fn _get_arch[target: __mlir_type.`!kgen.target`]() -> String: - return String( - __mlir_attr[ - `#kgen.param.expr : !kgen.string`, - ] - ) +fn _get_arch[target: __mlir_type.`!kgen.target`]() -> StringLiteral: + return __mlir_attr[ + `#kgen.param.expr : !kgen.string`, + ] @always_inline("nodebug") From 21ec26b8ebcd4027bebf8d05d68fa7327e987ba0 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 27 Nov 2024 08:58:23 -0800 Subject: [PATCH 1931/2019] [stdlib] Format float8 types using lookup table This is faster and more accurate then upcasting to float32, as the trailing bits will always be 0, and can change the rounding for the last digits. This matches nvcc outputs for these types, but aligns with pytorch shortest representation. MODULAR_ORIG_COMMIT_REV_ID: dfeb2fa529bc4aeb149fa5e73b4bacfcaa6c48ce --- stdlib/src/builtin/_format_float.mojo | 727 ++++++++++++++++++++++---- 1 file changed, 626 insertions(+), 101 deletions(-) diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index 2686917119..b7e27ebc76 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -105,112 +105,119 @@ fn _write_float[ """ constrained[type.is_floating_point()]() - # Currently only specialized for float32 and float64, upcast anything else - # to float32 - casted = value.cast[ - DType.float64 if type == DType.float64 else DType.float32 - ]() + @parameter + if type is DType.float8e5m2: + return writer.write(float8e5m2_to_str[int(bitcast[DType.uint8](value))]) + elif type is DType.float8e4m3: + return writer.write(float8e4m3_to_str[int(bitcast[DType.uint8](value))]) + else: + # Currently only specialized for float32 and float64, upcast anything + # else to float32 + casted = value.cast[ + DType.float64 if type == DType.float64 else DType.float32 + ]() + + # Bitcast the float and separate the sig and exp, to enable manipulating + # bits as a UInt64 and Int: + # - The significand (sig) is the raw binary fraction + # - The exponent (exp) is still in biased form + var sig = FPUtils.get_mantissa_uint(casted) + var exp = FPUtils.get_exponent_without_bias(casted) + var sign = FPUtils.get_sign(casted) + + if isinf(value): + if sign: + writer.write("-") + writer.write("inf") + return - # Bitcast the float and separate the sig and exp, to enable manipulating - # bits as a UInt64 and Int: - # - The significand (sig) is the raw binary fraction - # - The exponent (exp) is still in biased form - var sig = FPUtils.get_mantissa_uint(casted) - var exp = FPUtils.get_exponent_without_bias(casted) - var sign = FPUtils.get_sign(casted) + if isnan(value): + writer.write("nan") + return - if isinf(value): if sign: writer.write("-") - writer.write("inf") - return - - if isnan(value): - writer.write("nan") - return - - if sign: - writer.write("-") - - if not sig and not exp: - writer.write("0.0") - return - - # Convert the binary components to a decimal representation: - # - The raw binary sig into a decimal sig - # - The biased binary exp into a decimal power of 10 exp - # This does all the heavy lifting for perfect roundtrip, shortest - # representable format, bankers rounding etc. - _to_decimal[casted.type](sig, exp) - - # This is a custom routine for writing the decimal following python behavior. - # it can be further optimized with a lookup table, there is overhead here - # compared to snprintf. - var orig_sig = sig - var abs_exp = abs(exp) - var digits = StaticTuple[Byte, 21]() - var idx = 0 - while sig > 0: - digits[idx] = (sig % 10).cast[DType.uint8]() - sig //= 10 - idx += 1 - if sig > 0: - exp += 1 - var leading_zeroes = abs_exp - idx - - # Write in scientific notation if < 0.0001 or exp > 15 - if (exp < 0 and leading_zeroes > 3) or exp > 15: - # Handle single digit case - if orig_sig < 10: - writer.write(orig_sig) - else: - # Write digit before decimal point - writer.write(digits[idx - 1]) - writer.write(".") - # Write digits after decimal point - for i in reversed(range(idx - 1)): - writer.write(digits[i]) - # Write exponent - if exp < 0: - writer.write("e-") - exp = -exp - else: - writer.write("e+") - # Pad exponent with a 0 if less than two digits - if exp < 10: - writer.write("0") - var exp_digits = StaticTuple[Byte, 10]() - var exp_idx = 0 - while exp > 0: - exp_digits[exp_idx] = exp % 10 - exp //= 10 - exp_idx += 1 - for i in reversed(range(exp_idx)): - writer.write(exp_digits[i]) - # If between 0 and 0.0001 - elif exp < 0 and leading_zeroes > 0: - writer.write("0.") - for _ in range(leading_zeroes): - writer.write("0") - for i in reversed(range(idx)): - writer.write(digits[i]) - # All other floats > 0.0001 with an exponent <= 15 - else: - var point_written = False - for i in reversed(range(idx)): - if leading_zeroes < 1 and exp == idx - i - 2: - # No integer part so write leading 0 - if i == idx - 1: - writer.write("0") - writer.write(".") - point_written = True - writer.write(digits[i]) - # If exp - idx + 1 > 0 it's a positive number with more 0's than the sig - for _ in range(exp - idx + 1): - writer.write("0") - if not point_written: - writer.write(".0") + if not sig and not exp: + writer.write("0.0") + return + + # Convert the binary components to a decimal representation: + # - The raw binary sig into a decimal sig + # - The biased binary exp into a decimal power of 10 exp + # This does all the heavy lifting for perfect roundtrip, shortest + # representable format, bankers rounding etc. + _to_decimal[casted.type](sig, exp) + + # This is a custom routine for writing the decimal following python + # behavior. it can be further optimized with a lookup table, there is + # overhead here compared to snprintf. + var orig_sig = sig + var abs_exp = abs(exp) + var digits = StaticTuple[Byte, 21]() + var idx = 0 + while sig > 0: + digits[idx] = (sig % 10).cast[DType.uint8]() + sig //= 10 + idx += 1 + if sig > 0: + exp += 1 + var leading_zeroes = abs_exp - idx + + # Write in scientific notation if < 0.0001 or exp > 15 + if (exp < 0 and leading_zeroes > 3) or exp > 15: + # Handle single digit case + if orig_sig < 10: + writer.write(orig_sig) + else: + # Write digit before decimal point + writer.write(digits[idx - 1]) + writer.write(".") + # Write digits after decimal point + for i in reversed(range(idx - 1)): + writer.write(digits[i]) + # Write exponent + if exp < 0: + writer.write("e-") + exp = -exp + else: + writer.write("e+") + # Pad exponent with a 0 if less than two digits + if exp < 10: + writer.write("0") + var exp_digits = StaticTuple[Byte, 10]() + var exp_idx = 0 + while exp > 0: + exp_digits[exp_idx] = exp % 10 + exp //= 10 + exp_idx += 1 + for i in reversed(range(exp_idx)): + writer.write(exp_digits[i]) + # If between 0 and 0.0001 + elif exp < 0 and leading_zeroes > 0: + writer.write("0.") + for _ in range(leading_zeroes): + writer.write("0") + for i in reversed(range(idx)): + writer.write(digits[i]) + # All other floats > 0.0001 with an exponent <= 15 + else: + var point_written = False + for i in reversed(range(idx)): + if leading_zeroes < 1 and exp == idx - i - 2: + # No integer part so write leading 0 + if i == idx - 1: + writer.write("0") + writer.write(".") + point_written = True + writer.write(digits[i]) + + # If exp - idx + 1 > 0 it's a positive number with more 0's than the + # sig + for _ in range(exp - idx + 1): + writer.write("0") + if not point_written: + writer.write(".0") fn _to_decimal[ @@ -1426,3 +1433,521 @@ alias cache_f64 = StaticTuple[_UInt128, 619]( _UInt128(0xC5A05277621BE293, 0xC7098B7305241886), _UInt128(0xF70867153AA2DB38, 0xB8CBEE4FC66D1EA8), ) + +alias float8e5m2_to_str = StaticTuple[StringLiteral, 256]( + "0.0", + "1.52587890625e-05", + "3.0517578125e-05", + "4.57763671875e-05", + "6.103515625e-05", + "7.62939453125e-05", + "9.1552734375e-05", + "0.0001068115234375", + "0.0001220703125", + "0.000152587890625", + "0.00018310546875", + "0.000213623046875", + "0.000244140625", + "0.00030517578125", + "0.0003662109375", + "0.00042724609375", + "0.00048828125", + "0.0006103515625", + "0.000732421875", + "0.0008544921875", + "0.0009765625", + "0.001220703125", + "0.00146484375", + "0.001708984375", + "0.001953125", + "0.00244140625", + "0.0029296875", + "0.00341796875", + "0.00390625", + "0.0048828125", + "0.005859375", + "0.0068359375", + "0.0078125", + "0.009765625", + "0.01171875", + "0.013671875", + "0.015625", + "0.01953125", + "0.0234375", + "0.02734375", + "0.03125", + "0.0390625", + "0.046875", + "0.0546875", + "0.0625", + "0.078125", + "0.09375", + "0.109375", + "0.125", + "0.15625", + "0.1875", + "0.21875", + "0.25", + "0.3125", + "0.375", + "0.4375", + "0.5", + "0.625", + "0.75", + "0.875", + "1.0", + "1.25", + "1.5", + "1.75", + "2.0", + "2.5", + "3.0", + "3.5", + "4.0", + "5.0", + "6.0", + "7.0", + "8.0", + "10.0", + "12.0", + "14.0", + "16.0", + "20.0", + "24.0", + "28.0", + "32.0", + "40.0", + "48.0", + "56.0", + "64.0", + "80.0", + "96.0", + "112.0", + "128.0", + "160.0", + "192.0", + "224.0", + "256.0", + "320.0", + "384.0", + "448.0", + "512.0", + "640.0", + "768.0", + "896.0", + "1024.0", + "1280.0", + "1536.0", + "1792.0", + "2048.0", + "2560.0", + "3072.0", + "3584.0", + "4096.0", + "5120.0", + "6144.0", + "7168.0", + "8192.0", + "10240.0", + "12288.0", + "14336.0", + "16384.0", + "20480.0", + "24576.0", + "28672.0", + "32768.0", + "40960.0", + "49152.0", + "57344.0", + "inf", + "nan", + "nan", + "nan", + "-0.0", + "-1.52587890625e-05", + "-3.0517578125e-05", + "-4.57763671875e-05", + "-6.103515625e-05", + "-7.62939453125e-05", + "-9.1552734375e-05", + "-0.0001068115234375", + "-0.0001220703125", + "-0.000152587890625", + "-0.00018310546875", + "-0.000213623046875", + "-0.000244140625", + "-0.00030517578125", + "-0.0003662109375", + "-0.00042724609375", + "-0.00048828125", + "-0.0006103515625", + "-0.000732421875", + "-0.0008544921875", + "-0.0009765625", + "-0.001220703125", + "-0.00146484375", + "-0.001708984375", + "-0.001953125", + "-0.00244140625", + "-0.0029296875", + "-0.00341796875", + "-0.00390625", + "-0.0048828125", + "-0.005859375", + "-0.0068359375", + "-0.0078125", + "-0.009765625", + "-0.01171875", + "-0.013671875", + "-0.015625", + "-0.01953125", + "-0.0234375", + "-0.02734375", + "-0.03125", + "-0.0390625", + "-0.046875", + "-0.0546875", + "-0.0625", + "-0.078125", + "-0.09375", + "-0.109375", + "-0.125", + "-0.15625", + "-0.1875", + "-0.21875", + "-0.25", + "-0.3125", + "-0.375", + "-0.4375", + "-0.5", + "-0.625", + "-0.75", + "-0.875", + "-1.0", + "-1.25", + "-1.5", + "-1.75", + "-2.0", + "-2.5", + "-3.0", + "-3.5", + "-4.0", + "-5.0", + "-6.0", + "-7.0", + "-8.0", + "-10.0", + "-12.0", + "-14.0", + "-16.0", + "-20.0", + "-24.0", + "-28.0", + "-32.0", + "-40.0", + "-48.0", + "-56.0", + "-64.0", + "-80.0", + "-96.0", + "-112.0", + "-128.0", + "-160.0", + "-192.0", + "-224.0", + "-256.0", + "-320.0", + "-384.0", + "-448.0", + "-512.0", + "-640.0", + "-768.0", + "-896.0", + "-1024.0", + "-1280.0", + "-1536.0", + "-1792.0", + "-2048.0", + "-2560.0", + "-3072.0", + "-3584.0", + "-4096.0", + "-5120.0", + "-6144.0", + "-7168.0", + "-8192.0", + "-10240.0", + "-12288.0", + "-14336.0", + "-16384.0", + "-20480.0", + "-24576.0", + "-28672.0", + "-32768.0", + "-40960.0", + "-49152.0", + "-57344.0", + "-inf", + "nan", + "nan", + "nan", +) + +alias float8e4m3_to_str = StaticTuple[StringLiteral, 256]( + "0.0", + "0.001953125", + "0.00390625", + "0.005859375", + "0.0078125", + "0.009765625", + "0.01171875", + "0.013671875", + "0.015625", + "0.017578125", + "0.01953125", + "0.021484375", + "0.0234375", + "0.025390625", + "0.02734375", + "0.029296875", + "0.03125", + "0.03515625", + "0.0390625", + "0.04296875", + "0.046875", + "0.05078125", + "0.0546875", + "0.05859375", + "0.0625", + "0.0703125", + "0.078125", + "0.0859375", + "0.09375", + "0.1015625", + "0.109375", + "0.1171875", + "0.125", + "0.140625", + "0.15625", + "0.171875", + "0.1875", + "0.203125", + "0.21875", + "0.234375", + "0.25", + "0.28125", + "0.3125", + "0.34375", + "0.375", + "0.40625", + "0.4375", + "0.46875", + "0.5", + "0.5625", + "0.625", + "0.6875", + "0.75", + "0.8125", + "0.875", + "0.9375", + "1.0", + "1.125", + "1.25", + "1.375", + "1.5", + "1.625", + "1.75", + "1.875", + "2.0", + "2.25", + "2.5", + "2.75", + "3.0", + "3.25", + "3.5", + "3.75", + "4.0", + "4.5", + "5.0", + "5.5", + "6.0", + "6.5", + "7.0", + "7.5", + "8.0", + "9.0", + "10.0", + "11.0", + "12.0", + "13.0", + "14.0", + "15.0", + "16.0", + "18.0", + "20.0", + "22.0", + "24.0", + "26.0", + "28.0", + "30.0", + "32.0", + "36.0", + "40.0", + "44.0", + "48.0", + "52.0", + "56.0", + "60.0", + "64.0", + "72.0", + "80.0", + "88.0", + "96.0", + "104.0", + "112.0", + "120.0", + "128.0", + "144.0", + "160.0", + "176.0", + "192.0", + "208.0", + "224.0", + "240.0", + "256.0", + "288.0", + "320.0", + "352.0", + "384.0", + "416.0", + "448.0", + "nan", + "-0.0", + "-0.001953125", + "-0.00390625", + "-0.005859375", + "-0.0078125", + "-0.009765625", + "-0.01171875", + "-0.013671875", + "-0.015625", + "-0.017578125", + "-0.01953125", + "-0.021484375", + "-0.0234375", + "-0.025390625", + "-0.02734375", + "-0.029296875", + "-0.03125", + "-0.03515625", + "-0.0390625", + "-0.04296875", + "-0.046875", + "-0.05078125", + "-0.0546875", + "-0.05859375", + "-0.0625", + "-0.0703125", + "-0.078125", + "-0.0859375", + "-0.09375", + "-0.1015625", + "-0.109375", + "-0.1171875", + "-0.125", + "-0.140625", + "-0.15625", + "-0.171875", + "-0.1875", + "-0.203125", + "-0.21875", + "-0.234375", + "-0.25", + "-0.28125", + "-0.3125", + "-0.34375", + "-0.375", + "-0.40625", + "-0.4375", + "-0.46875", + "-0.5", + "-0.5625", + "-0.625", + "-0.6875", + "-0.75", + "-0.8125", + "-0.875", + "-0.9375", + "-1.0", + "-1.125", + "-1.25", + "-1.375", + "-1.5", + "-1.625", + "-1.75", + "-1.875", + "-2.0", + "-2.25", + "-2.5", + "-2.75", + "-3.0", + "-3.25", + "-3.5", + "-3.75", + "-4.0", + "-4.5", + "-5.0", + "-5.5", + "-6.0", + "-6.5", + "-7.0", + "-7.5", + "-8.0", + "-9.0", + "-10.0", + "-11.0", + "-12.0", + "-13.0", + "-14.0", + "-15.0", + "-16.0", + "-18.0", + "-20.0", + "-22.0", + "-24.0", + "-26.0", + "-28.0", + "-30.0", + "-32.0", + "-36.0", + "-40.0", + "-44.0", + "-48.0", + "-52.0", + "-56.0", + "-60.0", + "-64.0", + "-72.0", + "-80.0", + "-88.0", + "-96.0", + "-104.0", + "-112.0", + "-120.0", + "-128.0", + "-144.0", + "-160.0", + "-176.0", + "-192.0", + "-208.0", + "-224.0", + "-240.0", + "-256.0", + "-288.0", + "-320.0", + "-352.0", + "-384.0", + "-416.0", + "-448.0", + "nan", +) From d8185e2edc334b2fc00a7d4dd71a45d407585021 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 27 Nov 2024 13:12:38 -0800 Subject: [PATCH 1932/2019] [Docs] Several small corrections & bug fixes to ownership and lifecycle docs. MODULAR_ORIG_COMMIT_REV_ID: 78075bc3ba6d211b7e4eef28000fc272cc75dfdb --- docs/manual/lifecycle/life.mdx | 15 +++++------ docs/manual/values/lifetimes.mdx | 16 ++++++------ docs/manual/values/ownership.mdx | 43 +++++++++++++++----------------- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/docs/manual/lifecycle/life.mdx b/docs/manual/lifecycle/life.mdx index 95c4367593..800a81163d 100644 --- a/docs/manual/lifecycle/life.mdx +++ b/docs/manual/lifecycle/life.mdx @@ -247,10 +247,11 @@ a constructor should support implicit conversion. ## Copy constructor -When Mojo encounters an assignment operator (`=`), it tries to make a copy of -the right-side value by calling upon that type's copy constructor: the -`__copyinit__()` method. Thus, it's the responsibility of the type author to -implement `__copyinit__()` so it returns a copy of the value. +When Mojo encounters an assignment statement that doesn't use the [transfer +sigil (`^`)](/mojo/manual/values/ownership#transfer-arguments-owned-and-), it +tries to make a copy of the right-side value by calling upon that type's copy +constructor: the `__copyinit__()` method. Thus, it's the responsibility of the +type author to implement `__copyinit__()` so it returns a copy of the value. For example, the `MyPet` type above does not have a copy constructor, so this code fails to compile: @@ -285,9 +286,9 @@ mistakes when referring to the current struct name. Also, notice that the `existing` argument in `__copyinit__()` is immutable because the default [argument -convention](/mojo/manual/values/ownership#argument-conventions) in an `fn` -function is `borrowed`—this is a good thing because this function should not -modify the contents of the value being copied. +convention](/mojo/manual/values/ownership#argument-conventions) is +`borrowed`—this is a good thing because this function should not modify the +contents of the value being copied. ::: diff --git a/docs/manual/values/lifetimes.mdx b/docs/manual/values/lifetimes.mdx index d191af8e2e..24ff2b7982 100644 --- a/docs/manual/values/lifetimes.mdx +++ b/docs/manual/values/lifetimes.mdx @@ -43,7 +43,7 @@ However, in some cases you'll need to interact with origins directly: values. * When working with types like - [`Pointer`](/mojo/stdlib/memory/reference/Pointer) or + [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) or [`Span`](/mojo/stdlib/utils/span/Span) which are parameterized on the origin of the data they refer to. @@ -73,7 +73,7 @@ struct ImmutableRef[origin: ImmutableOrigin]: pass ``` -Or you can use the [`Origin`](mojo/stdlib/builtin/type_aliases/Origin) +Or you can use the [`Origin`](/mojo/stdlib/builtin/type_aliases/Origin) struct to specify an origin with parametric mutability: ```mojo @@ -117,7 +117,7 @@ few ways to specify origin values: * Static origin. The `StaticConstantOrigin` alias is an origin value representing immutable values that that last for the duration of the program. String literal values have a `StaticConstantOrigin`. -* The `__origin_of()` magic function, which returns the origin associated +* Derived origin. The `__origin_of()` magic function returns the origin associated with the value (or values) passed in. * Inferred origin. You can use inferred parameters to capture the origin of a value passed in to a function. @@ -161,13 +161,13 @@ the stored string, using the same origin as the original `OwnedPointer`. from memory import OwnedPointer, Pointer struct BoxedString: - var box: OwnedPointer[String] + var o_ptr: OwnedPointer[String] fn __init__(out self, value: String): - self.box = OwnedPointer(value) + self.o_ptr = OwnedPointer(value) - fn as_ptr(self) -> Pointer[String, __origin_of(self.box)]: - return Pointer.address_of(self.box[]) + fn as_ptr(self) -> Pointer[String, __origin_of(self.o_ptr)]: + return Pointer.address_of(self.o_ptr[]) ``` #### Inferred origins @@ -362,7 +362,7 @@ will be added in a future release. If you're working with an API that returns a reference, and you want to avoid copying the referenced value, you can use a -[`Pointer`](/mojo/stdlib/memory/reference/Pointer) to hold an indirect reference. +[`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to hold an indirect reference. You can assign a `Pointer` to a variable, but you need to use the dereference operator (`[]`) to access the underlying value. diff --git a/docs/manual/values/ownership.mdx b/docs/manual/values/ownership.mdx index 95817dd2b1..5748afe073 100644 --- a/docs/manual/values/ownership.mdx +++ b/docs/manual/values/ownership.mdx @@ -29,7 +29,7 @@ The fundamental rules that make Mojo's ownership model work are the following: * Every value has only one owner at a time. * When the lifetime of the owner ends, Mojo destroys the value. -* If there are outstanding references to a value, Mojo extends the lifetime of +* If there are existing references to a value, Mojo extends the lifetime of the owner. ### Variables and references @@ -293,13 +293,11 @@ gets unique ownership of a value. This happens in one of three ways: * The caller passes the argument with the `^` transfer sigil, which ends the - lifetime of that variable (the variable becomes uninitialized) and ownership is - transferred into the function without making a copy of any heap-allocated data. + lifetime of that variable (the variable becomes uninitialized) and ownership + is transferred into the function. -* The caller **does not** use the `^` transfer sigil, in which case, the - value is copied into the function argument and the original variable remains - valid. (If the original value is not used again, the compiler may optimize away - the copy and transfer the value). +* The caller **does not** use the `^` transfer sigil, in which case, Mojo copies + the value. If the type isn't copyable, this is a compile-time error. * The caller passes in a newly-created "owned" value, such as a value returned from a function. In this case, no variable owns the value and it can be @@ -312,9 +310,8 @@ three ways: take(String("A brand-new String!")) ``` -For example, the following code works by making a copy of the string, -because—although `take_text()` uses the `owned` convention—the caller does not -include the transfer sigil: +The following code works by making a copy of the string, because `take_text()` +uses the `owned` convention, and the caller does not include the transfer sigil: ```mojo fn take_text(owned text: String): @@ -394,19 +391,6 @@ used inside a function. ::: -### Borrowed versus owned in `def` functions - -The difference between `borrowed` and `owned` in a `def` function may be a -little subtle. In both cases, you can end up with a uniquely-owned value that's -a copy of the original value. - -* The `borrowed` argument always gets an immutable reference or a local copy. - You can't transfer a value into a `borrowed` argument. - -* The `owned` argument always gets a uniquely owned value, which may have been - copied or transferred from the callee. Using `owned` arguments without the - transfer sigil (`^`) usually results in values being copied. - ### Transfer implementation details In Mojo, you shouldn't conflate "ownership transfer" with a "move @@ -463,3 +447,16 @@ This shadow copy typically adds no overhead, because small types like `object` are cheap to copy. However, copying large types that allocate heap storage can be expensive. (For example, copying `List` or `Dict` types, or copying large numbers of strings.) + +### Borrowed versus owned in `def` functions + +The difference between `borrowed` and `owned` in a `def` function may be a +little subtle. In both cases, you can end up with a uniquely-owned value that's +a copy of the original value. + +* The `borrowed` argument always gets an immutable reference or a local copy. + You can't transfer a value into a `borrowed` argument. + +* The `owned` argument always gets a uniquely owned value, which may have been + copied or transferred from the callee. Using `owned` arguments without the + transfer sigil (`^`) usually results in values being copied. From 04b0382ec42ffc6e846d1bfd418b466afbf4eaa8 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 27 Nov 2024 14:59:51 -0800 Subject: [PATCH 1933/2019] Format python with ruff MODULAR_ORIG_COMMIT_REV_ID: 868691303b9bc9874a7b80352a2acabf40aeb7ae --- stdlib/test/python/my_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/test/python/my_module.py b/stdlib/test/python/my_module.py index c78c39556e..8147b0a382 100644 --- a/stdlib/test/python/my_module.py +++ b/stdlib/test/python/my_module.py @@ -25,8 +25,7 @@ def __init__(self, bar): class AbstractPerson(ABC): @abstractmethod - def method(self): - ... + def method(self): ... def my_function(name): From 648846d97599d48dc09c8b34a4a8d88c3f4ea989 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 27 Nov 2024 15:48:14 -0800 Subject: [PATCH 1934/2019] [stdlib] Remove unused branch from `SIMD.write_to` Due to various changes this ended up with an unused branch, snprintf is no longer used for any dtypes. MODULAR_ORIG_COMMIT_REV_ID: e375d7f0f5c248cc1b164fafdd9a1092066bef0a --- stdlib/src/builtin/simd.mojo | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 60ed1aaebf..14e7075d53 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -3369,24 +3369,10 @@ fn _write_scalar[ elif dtype.is_floating_point(): _write_float(writer, value) - # TODO: bring in modern int formatter and remove GPU specific code + # TODO(MSTDL-1039): bring in performant integer to string formatter elif dtype.is_integral(): - - @parameter - if is_gpu() or dtype.is_integral(): - var err = _try_write_int(writer, value) - if err: - abort( - "unreachable: unexpected write int failure condition: " - + str(err.value()) - ) - else: - # Stack allocate enough bytes to store any formatted Scalar value. - alias size: Int = _calc_format_buffer_size[dtype]() - var buf = InlineArray[UInt8, size](fill=0) - var wrote = _snprintf[_get_dtype_printf_format[dtype]()]( - buf.unsafe_ptr(), size, value - ) - # SAFETY: - # Create a slice to only those bytes in `buf` that have been initialized. - writer.write_bytes(Span[Byte](buf)[:wrote]) + _ = _try_write_int(writer, value) + else: + constrained[ + False, "unable to write dtype, only integral/float/bool supported" + ]() From 036f29d791d03dd41b20c371189293e6a8094eac Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 28 Nov 2024 09:23:38 -0800 Subject: [PATCH 1935/2019] [stdlib] Add AMD float8 formatting support Add lookup tables for the two AMD float8 types MODULAR_ORIG_COMMIT_REV_ID: 05f4a3d5e2e063a0f265e396c509c654480c6c5f --- stdlib/src/builtin/_format_float.mojo | 526 ++++++++++++++++++++++++++ 1 file changed, 526 insertions(+) diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index b7e27ebc76..ff57737766 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -110,6 +110,14 @@ fn _write_float[ return writer.write(float8e5m2_to_str[int(bitcast[DType.uint8](value))]) elif type is DType.float8e4m3: return writer.write(float8e4m3_to_str[int(bitcast[DType.uint8](value))]) + elif type is DType.float8e5m2fnuz: + return writer.write( + float8e5m2fnuz_to_str[int(bitcast[DType.uint8](value))] + ) + elif type is DType.float8e4m3fnuz: + return writer.write( + float8e4m3fnuz_to_str[int(bitcast[DType.uint8](value))] + ) else: # Currently only specialized for float32 and float64, upcast anything # else to float32 @@ -1951,3 +1959,521 @@ alias float8e4m3_to_str = StaticTuple[StringLiteral, 256]( "-448.0", "nan", ) + +alias float8e5m2fnuz_to_str = StaticTuple[StringLiteral, 256]( + "0.0", + "7.62939453125e-06", + "1.52587890625e-05", + "2.288818359375e-05", + "3.0517578125e-05", + "3.814697265625e-05", + "4.57763671875e-05", + "5.340576171875e-05", + "6.103515625e-05", + "7.62939453125e-05", + "9.1552734375e-05", + "0.0001068115234375", + "0.0001220703125", + "0.000152587890625", + "0.00018310546875", + "0.000213623046875", + "0.000244140625", + "0.00030517578125", + "0.0003662109375", + "0.00042724609375", + "0.00048828125", + "0.0006103515625", + "0.000732421875", + "0.0008544921875", + "0.0009765625", + "0.001220703125", + "0.00146484375", + "0.001708984375", + "0.001953125", + "0.00244140625", + "0.0029296875", + "0.00341796875", + "0.00390625", + "0.0048828125", + "0.005859375", + "0.0068359375", + "0.0078125", + "0.009765625", + "0.01171875", + "0.013671875", + "0.015625", + "0.01953125", + "0.0234375", + "0.02734375", + "0.03125", + "0.0390625", + "0.046875", + "0.0546875", + "0.0625", + "0.078125", + "0.09375", + "0.109375", + "0.125", + "0.15625", + "0.1875", + "0.21875", + "0.25", + "0.3125", + "0.375", + "0.4375", + "0.5", + "0.625", + "0.75", + "0.875", + "1.0", + "1.25", + "1.5", + "1.75", + "2.0", + "2.5", + "3.0", + "3.5", + "4.0", + "5.0", + "6.0", + "7.0", + "8.0", + "10.0", + "12.0", + "14.0", + "16.0", + "20.0", + "24.0", + "28.0", + "32.0", + "40.0", + "48.0", + "56.0", + "64.0", + "80.0", + "96.0", + "112.0", + "128.0", + "160.0", + "192.0", + "224.0", + "256.0", + "320.0", + "384.0", + "448.0", + "512.0", + "640.0", + "768.0", + "896.0", + "1024.0", + "1280.0", + "1536.0", + "1792.0", + "2048.0", + "2560.0", + "3072.0", + "3584.0", + "4096.0", + "5120.0", + "6144.0", + "7168.0", + "8192.0", + "10240.0", + "12288.0", + "14336.0", + "16384.0", + "20480.0", + "24576.0", + "28672.0", + "32768.0", + "40960.0", + "49152.0", + "57344.0", + "nan", + "-7.62939453125e-06", + "-1.52587890625e-05", + "-2.288818359375e-05", + "-3.0517578125e-05", + "-3.814697265625e-05", + "-4.57763671875e-05", + "-5.340576171875e-05", + "-6.103515625e-05", + "-7.62939453125e-05", + "-9.1552734375e-05", + "-0.0001068115234375", + "-0.0001220703125", + "-0.000152587890625", + "-0.00018310546875", + "-0.000213623046875", + "-0.000244140625", + "-0.00030517578125", + "-0.0003662109375", + "-0.00042724609375", + "-0.00048828125", + "-0.0006103515625", + "-0.000732421875", + "-0.0008544921875", + "-0.0009765625", + "-0.001220703125", + "-0.00146484375", + "-0.001708984375", + "-0.001953125", + "-0.00244140625", + "-0.0029296875", + "-0.00341796875", + "-0.00390625", + "-0.0048828125", + "-0.005859375", + "-0.0068359375", + "-0.0078125", + "-0.009765625", + "-0.01171875", + "-0.013671875", + "-0.015625", + "-0.01953125", + "-0.0234375", + "-0.02734375", + "-0.03125", + "-0.0390625", + "-0.046875", + "-0.0546875", + "-0.0625", + "-0.078125", + "-0.09375", + "-0.109375", + "-0.125", + "-0.15625", + "-0.1875", + "-0.21875", + "-0.25", + "-0.3125", + "-0.375", + "-0.4375", + "-0.5", + "-0.625", + "-0.75", + "-0.875", + "-1.0", + "-1.25", + "-1.5", + "-1.75", + "-2.0", + "-2.5", + "-3.0", + "-3.5", + "-4.0", + "-5.0", + "-6.0", + "-7.0", + "-8.0", + "-10.0", + "-12.0", + "-14.0", + "-16.0", + "-20.0", + "-24.0", + "-28.0", + "-32.0", + "-40.0", + "-48.0", + "-56.0", + "-64.0", + "-80.0", + "-96.0", + "-112.0", + "-128.0", + "-160.0", + "-192.0", + "-224.0", + "-256.0", + "-320.0", + "-384.0", + "-448.0", + "-512.0", + "-640.0", + "-768.0", + "-896.0", + "-1024.0", + "-1280.0", + "-1536.0", + "-1792.0", + "-2048.0", + "-2560.0", + "-3072.0", + "-3584.0", + "-4096.0", + "-5120.0", + "-6144.0", + "-7168.0", + "-8192.0", + "-10240.0", + "-12288.0", + "-14336.0", + "-16384.0", + "-20480.0", + "-24576.0", + "-28672.0", + "-32768.0", + "-40960.0", + "-49152.0", + "-57344.0", +) + +alias float8e4m3fnuz_to_str = StaticTuple[StringLiteral, 256]( + "0.0", + "0.0009765625", + "0.001953125", + "0.0029296875", + "0.00390625", + "0.0048828125", + "0.005859375", + "0.0068359375", + "0.0078125", + "0.0087890625", + "0.009765625", + "0.0107421875", + "0.01171875", + "0.0126953125", + "0.013671875", + "0.0146484375", + "0.015625", + "0.017578125", + "0.01953125", + "0.021484375", + "0.0234375", + "0.025390625", + "0.02734375", + "0.029296875", + "0.03125", + "0.03515625", + "0.0390625", + "0.04296875", + "0.046875", + "0.05078125", + "0.0546875", + "0.05859375", + "0.0625", + "0.0703125", + "0.078125", + "0.0859375", + "0.09375", + "0.1015625", + "0.109375", + "0.1171875", + "0.125", + "0.140625", + "0.15625", + "0.171875", + "0.1875", + "0.203125", + "0.21875", + "0.234375", + "0.25", + "0.28125", + "0.3125", + "0.34375", + "0.375", + "0.40625", + "0.4375", + "0.46875", + "0.5", + "0.5625", + "0.625", + "0.6875", + "0.75", + "0.8125", + "0.875", + "0.9375", + "1.0", + "1.125", + "1.25", + "1.375", + "1.5", + "1.625", + "1.75", + "1.875", + "2.0", + "2.25", + "2.5", + "2.75", + "3.0", + "3.25", + "3.5", + "3.75", + "4.0", + "4.5", + "5.0", + "5.5", + "6.0", + "6.5", + "7.0", + "7.5", + "8.0", + "9.0", + "10.0", + "11.0", + "12.0", + "13.0", + "14.0", + "15.0", + "16.0", + "18.0", + "20.0", + "22.0", + "24.0", + "26.0", + "28.0", + "30.0", + "32.0", + "36.0", + "40.0", + "44.0", + "48.0", + "52.0", + "56.0", + "60.0", + "64.0", + "72.0", + "80.0", + "88.0", + "96.0", + "104.0", + "112.0", + "120.0", + "128.0", + "144.0", + "160.0", + "176.0", + "192.0", + "208.0", + "224.0", + "240.0", + "nan", + "-0.0009765625", + "-0.001953125", + "-0.0029296875", + "-0.00390625", + "-0.0048828125", + "-0.005859375", + "-0.0068359375", + "-0.0078125", + "-0.0087890625", + "-0.009765625", + "-0.0107421875", + "-0.01171875", + "-0.0126953125", + "-0.013671875", + "-0.0146484375", + "-0.015625", + "-0.017578125", + "-0.01953125", + "-0.021484375", + "-0.0234375", + "-0.025390625", + "-0.02734375", + "-0.029296875", + "-0.03125", + "-0.03515625", + "-0.0390625", + "-0.04296875", + "-0.046875", + "-0.05078125", + "-0.0546875", + "-0.05859375", + "-0.0625", + "-0.0703125", + "-0.078125", + "-0.0859375", + "-0.09375", + "-0.1015625", + "-0.109375", + "-0.1171875", + "-0.125", + "-0.140625", + "-0.15625", + "-0.171875", + "-0.1875", + "-0.203125", + "-0.21875", + "-0.234375", + "-0.25", + "-0.28125", + "-0.3125", + "-0.34375", + "-0.375", + "-0.40625", + "-0.4375", + "-0.46875", + "-0.5", + "-0.5625", + "-0.625", + "-0.6875", + "-0.75", + "-0.8125", + "-0.875", + "-0.9375", + "-1.0", + "-1.125", + "-1.25", + "-1.375", + "-1.5", + "-1.625", + "-1.75", + "-1.875", + "-2.0", + "-2.25", + "-2.5", + "-2.75", + "-3.0", + "-3.25", + "-3.5", + "-3.75", + "-4.0", + "-4.5", + "-5.0", + "-5.5", + "-6.0", + "-6.5", + "-7.0", + "-7.5", + "-8.0", + "-9.0", + "-10.0", + "-11.0", + "-12.0", + "-13.0", + "-14.0", + "-15.0", + "-16.0", + "-18.0", + "-20.0", + "-22.0", + "-24.0", + "-26.0", + "-28.0", + "-30.0", + "-32.0", + "-36.0", + "-40.0", + "-44.0", + "-48.0", + "-52.0", + "-56.0", + "-60.0", + "-64.0", + "-72.0", + "-80.0", + "-88.0", + "-96.0", + "-104.0", + "-112.0", + "-120.0", + "-128.0", + "-144.0", + "-160.0", + "-176.0", + "-192.0", + "-208.0", + "-224.0", + "-240.0", +) From b503f762d62b4a831c889dff9fa1e13a25dd481c Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Thu, 28 Nov 2024 10:12:04 -0800 Subject: [PATCH 1936/2019] [stdlib] format float consistency changes Make consistent with `dtype is` instead of `dtype ==`, and return on the same line as final expression. MODULAR_ORIG_COMMIT_REV_ID: 9ce3daf3af56f8542bc0a7614cbd9c0ff8b0b74f --- stdlib/src/builtin/_format_float.mojo | 36 ++++++++++++--------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index ff57737766..76c255c1b5 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -119,8 +119,7 @@ fn _write_float[ float8e4m3fnuz_to_str[int(bitcast[DType.uint8](value))] ) else: - # Currently only specialized for float32 and float64, upcast anything - # else to float32 + # Upcast the float16 types to float32 casted = value.cast[ DType.float64 if type == DType.float64 else DType.float32 ]() @@ -135,20 +134,17 @@ fn _write_float[ if isinf(value): if sign: - writer.write("-") - writer.write("inf") - return + return writer.write("-inf") + return writer.write("inf") if isnan(value): - writer.write("nan") - return + return writer.write("nan") if sign: writer.write("-") if not sig and not exp: - writer.write("0.0") - return + return writer.write("0.0") # Convert the binary components to a decimal representation: # - The raw binary sig into a decimal sig @@ -383,7 +379,7 @@ fn _compute_endpoint[ CarrierDType: DType, sig_bits: Int, total_bits: Int, cache_bits: Int ](cache_index: Int, beta: Int, left_endpoint: Bool) -> Scalar[CarrierDType]: @parameter - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: var cache = cache_f64[cache_index] if left_endpoint: return ( @@ -420,9 +416,9 @@ fn _print_bits[type: DType](x: Scalar[type]) -> String: if i % 8 == 0: output.write(" ") else: - alias sig_bits = 23 if type == DType.float32 else 52 - alias exp_bits = 8 if type == DType.float32 else 11 - alias cast_type = DType.uint32 if type == DType.float32 else DType.uint64 + alias sig_bits = 23 if type is DType.float32 else 52 + alias exp_bits = 8 if type is DType.float32 else 11 + alias cast_type = DType.uint32 if type is DType.float32 else DType.uint64 var casted = bitcast[cast_type](x) for i in reversed(range(total_bits)): output.write((casted >> i) & 1) @@ -448,7 +444,7 @@ fn _rotr[ CarrierDType: DType ](n: Scalar[CarrierDType], r: Scalar[CarrierDType]) -> Scalar[CarrierDType]: @parameter - if CarrierDType == DType.uint32: + if CarrierDType is DType.uint32: var r_masked = r & 31 return (n >> r_masked) | (n << ((32 - r_masked) & 31)) else: @@ -508,7 +504,7 @@ fn _remove_trailing_zeros[ """ @parameter - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: var r = _rotr(sig * 28999941890838049, 8) var b = r < 184467440738 var s = int(b) @@ -560,7 +556,7 @@ fn _divide_by_pow10[ CarrierDType: DType, //, N: Int, n_max: Scalar[CarrierDType] ](n: Scalar[CarrierDType]) -> Scalar[CarrierDType]: @parameter - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: @parameter if N == 1 and bool(n_max <= 4611686018427387908): @@ -596,7 +592,7 @@ fn _umul192_lower128(x: UInt64, y: _UInt128) -> _UInt128: fn _compute_mul_parity[ CarrierDType: DType ](two_f: Scalar[CarrierDType], cache_index: Int, beta: Int) -> _MulParity: - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: debug_assert(1 <= beta < 64, "beta must be between 1 and 64") var r = _umul192_lower128( two_f.cast[DType.uint64](), cache_f64[cache_index] @@ -668,7 +664,7 @@ fn _umul96_upper64[ fn _compute_mul[ CarrierDType: DType ](u: Scalar[CarrierDType], cache_index: Int) -> _MulResult[CarrierDType]: - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: var r = _umul192_upper128(u, cache_f64[cache_index]) return _MulResult[CarrierDType](r.high.cast[CarrierDType](), r.low == 0) else: @@ -682,7 +678,7 @@ fn _compute_mul[ fn _compute_delta[ CarrierDType: DType, total_bits: Int, cache_bits: Int ](cache_index: Int, beta: Int) -> Scalar[CarrierDType]: - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: var cache = cache_f64[cache_index] return (cache.high >> (total_bits - 1 - beta)).cast[CarrierDType]() else: @@ -737,7 +733,7 @@ fn _count_factors[ fn _compute_round_up_for_shorter_interval_case[ CarrierDType: DType, total_bits: Int, sig_bits: Int, cache_bits: Int ](cache_index: Int, beta: Int) -> Scalar[CarrierDType]: - if CarrierDType == DType.uint64: + if CarrierDType is DType.uint64: var cache = cache_f64[cache_index] return ( ( From e2f084e0e24fa5fd5144e77db66c5061043dfe39 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 28 Nov 2024 12:30:43 -0800 Subject: [PATCH 1937/2019] [******] Reformat imports, NFC (#51760) MODULAR_ORIG_COMMIT_REV_ID: 16f4eb58466393e8902ce4629cbcd4c6c8960772 --- examples/life/benchmark.mojo | 3 +- examples/life/gridv1.mojo | 2 +- examples/life/gridv2.mojo | 5 +- examples/life/lifev1.mojo | 3 +- examples/life/lifev2.mojo | 3 +- examples/mandelbrot.mojo | 2 +- examples/matmul.mojo | 5 +- .../algorithm/bench_elementwise.mojo | 1 + stdlib/benchmarks/builtin/bench_sort.mojo | 11 +- stdlib/benchmarks/collections/bench_dict.mojo | 8 +- .../benchmarks/collections/bench_string.mojo | 12 +- stdlib/benchmarks/hashlib/bench_hash.mojo | 17 +-- stdlib/benchmarks/utils/bench_formatter.mojo | 1 + stdlib/benchmarks/utils/bench_memmem.mojo | 3 +- stdlib/src/base64/_b64encode.mojo | 6 +- stdlib/src/base64/base64.mojo | 2 + stdlib/src/builtin/_format_float.mojo | 6 +- stdlib/src/builtin/_pybind.mojo | 35 +++-- stdlib/src/builtin/_startup.mojo | 5 +- stdlib/src/builtin/bool.mojo | 4 +- stdlib/src/builtin/builtin_list.mojo | 1 - stdlib/src/builtin/debug_assert.mojo | 14 +- stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/file.mojo | 3 +- stdlib/src/builtin/file_descriptor.mojo | 6 +- stdlib/src/builtin/format_int.mojo | 5 +- stdlib/src/builtin/int.mojo | 16 +-- stdlib/src/builtin/io.mojo | 18 ++- stdlib/src/builtin/object.mojo | 4 +- stdlib/src/builtin/range.mojo | 4 +- stdlib/src/builtin/simd.mojo | 42 +++--- stdlib/src/builtin/sort.mojo | 3 +- stdlib/src/builtin/string_literal.mojo | 15 +-- stdlib/src/builtin/tuple.mojo | 1 + stdlib/src/builtin/uint.mojo | 8 +- stdlib/src/collections/counter.mojo | 3 +- stdlib/src/collections/deque.mojo | 4 +- stdlib/src/collections/dict.mojo | 7 +- stdlib/src/collections/inline_array.mojo | 1 + stdlib/src/collections/inline_list.mojo | 1 + stdlib/src/collections/list.mojo | 6 +- stdlib/src/collections/optional.mojo | 1 + stdlib/src/collections/set.mojo | 2 +- stdlib/src/collections/string.mojo | 27 ++-- stdlib/src/collections/vector.mojo | 3 +- stdlib/src/hashlib/__init__.mojo | 2 +- stdlib/src/hashlib/_ahash.mojo | 6 +- stdlib/src/hashlib/_hasher.mojo | 3 +- stdlib/src/hashlib/hash.mojo | 7 +- stdlib/src/math/__init__.mojo | 10 +- stdlib/src/math/math.mojo | 17 ++- stdlib/src/memory/memory.mojo | 13 +- stdlib/src/memory/owned_pointer.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 3 +- stdlib/src/os/__init__.mojo | 4 +- stdlib/src/os/_linux_x86.mojo | 2 +- stdlib/src/os/atomic.mojo | 3 +- stdlib/src/os/env.mojo | 1 + stdlib/src/os/os.mojo | 7 +- stdlib/src/os/path/__init__.mojo | 4 +- stdlib/src/os/path/path.mojo | 7 +- stdlib/src/pathlib/path.mojo | 7 +- stdlib/src/prelude/__init__.mojo | 120 +++++++++--------- stdlib/src/pwd/__init__.mojo | 2 +- stdlib/src/pwd/_linux.mojo | 6 +- stdlib/src/pwd/_macos.mojo | 6 +- stdlib/src/pwd/pwd.mojo | 3 +- stdlib/src/python/__init__.mojo | 2 +- stdlib/src/python/_bindings.mojo | 21 ++- stdlib/src/python/_cpython.mojo | 8 +- stdlib/src/python/python.mojo | 4 +- stdlib/src/python/python_object.mojo | 8 +- stdlib/src/random/random.mojo | 7 +- stdlib/src/sys/__init__.mojo | 12 +- stdlib/src/sys/_libc.mojo | 4 +- stdlib/src/sys/ffi.mojo | 6 +- stdlib/src/sys/info.mojo | 3 +- stdlib/src/sys/intrinsics.mojo | 5 +- stdlib/src/sys/terminate.mojo | 2 +- stdlib/src/tempfile/tempfile.mojo | 3 +- stdlib/src/testing/__init__.mojo | 4 +- stdlib/src/testing/testing.mojo | 1 - stdlib/src/time/__init__.mojo | 2 +- stdlib/src/time/time.mojo | 6 +- stdlib/src/utils/__init__.mojo | 6 +- stdlib/src/utils/_serialize.mojo | 2 +- stdlib/src/utils/_unicode.mojo | 3 +- stdlib/src/utils/_utf8_validation.mojo | 5 +- stdlib/src/utils/format.mojo | 2 + stdlib/src/utils/index.mojo | 3 +- stdlib/src/utils/inline_string.mojo | 3 +- stdlib/src/utils/lock.mojo | 4 +- stdlib/src/utils/loop.mojo | 2 +- stdlib/src/utils/span.mojo | 3 +- stdlib/src/utils/string_slice.mojo | 13 +- stdlib/src/utils/stringref.mojo | 10 +- stdlib/src/utils/write.mojo | 6 +- stdlib/test/builtin/test_dtype.mojo | 2 +- stdlib/test/builtin/test_file.mojo | 2 +- stdlib/test/builtin/test_format_float.mojo | 5 +- stdlib/test/builtin/test_int.mojo | 5 +- stdlib/test/builtin/test_int_literal.mojo | 2 +- stdlib/test/builtin/test_issue_1505.mojo | 3 +- stdlib/test/builtin/test_list_literal.mojo | 2 +- stdlib/test/builtin/test_print.mojo | 4 +- stdlib/test/builtin/test_reversed.mojo | 1 + stdlib/test/builtin/test_simd.mojo | 7 +- stdlib/test/builtin/test_sort_issue_1018.mojo | 2 + stdlib/test/builtin/test_string_literal.mojo | 2 +- stdlib/test/builtin/test_uint.mojo | 3 +- stdlib/test/collections/test_counter.mojo | 2 +- stdlib/test/collections/test_deque.mojo | 4 +- .../test/collections/test_inline_array.mojo | 5 +- stdlib/test/collections/test_inline_list.mojo | 2 +- stdlib/test/collections/test_list.mojo | 3 +- stdlib/test/collections/test_set.mojo | 2 +- stdlib/test/collections/test_string.mojo | 1 + stdlib/test/hashlib/test_ahash.mojo | 12 +- stdlib/test/hashlib/test_hash.mojo | 1 + stdlib/test/hashlib/test_hasher.mojo | 6 +- stdlib/test/lit.cfg.py | 1 - stdlib/test/memory/test_arc.mojo | 2 +- .../test/memory/test_maybe_uninitialized.mojo | 2 +- stdlib/test/memory/test_memory.mojo | 2 +- stdlib/test/memory/test_owned_pointer.mojo | 6 +- stdlib/test/memory/test_unsafepointer.mojo | 4 +- stdlib/test/os/path/test_basename.mojo | 1 + stdlib/test/os/path/test_expanduser.mojo | 5 +- stdlib/test/os/path/test_expandvars.mojo | 1 + stdlib/test/os/path/test_getsize.mojo | 1 + stdlib/test/os/path/test_split.mojo | 2 +- stdlib/test/os/path/test_splitroot.mojo | 1 + stdlib/test/os/test_no_trap.mojo | 2 +- stdlib/test/os/test_remove.mojo | 2 +- stdlib/test/pwd/test_pwd.mojo | 5 +- stdlib/test/python/test_python_object.mojo | 1 + stdlib/test/sys/test_c_types.mojo | 2 +- stdlib/test/sys/test_intrinsics.mojo | 2 +- stdlib/test/sys/test_paramenv.mojo | 2 +- stdlib/test/testing/test_assertion.mojo | 8 +- stdlib/test/time/test_time.mojo | 2 +- stdlib/test/utils/test_format_to_stdout.mojo | 3 +- stdlib/test/utils/test_inlined_string.mojo | 3 +- stdlib/test/utils/test_select.mojo | 3 +- stdlib/test/utils/test_span.mojo | 1 + stdlib/test/utils/test_string_slice.mojo | 2 +- stdlib/test/utils/test_tuple.mojo | 4 +- stdlib/test/utils/test_variant.mojo | 2 +- 148 files changed, 474 insertions(+), 402 deletions(-) diff --git a/examples/life/benchmark.mojo b/examples/life/benchmark.mojo index 268e9496ff..c54c93f15b 100644 --- a/examples/life/benchmark.mojo +++ b/examples/life/benchmark.mojo @@ -11,9 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from time import perf_counter_ns + import gridv1 import gridv2 -from time import perf_counter_ns def main(): diff --git a/examples/life/gridv1.mojo b/examples/life/gridv1.mojo index e416860928..9b7ec2e386 100644 --- a/examples/life/gridv1.mojo +++ b/examples/life/gridv1.mojo @@ -11,8 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from collections import Optional import random +from collections import Optional @value diff --git a/examples/life/gridv2.mojo b/examples/life/gridv2.mojo index 8f937a2046..574d41e314 100644 --- a/examples/life/gridv2.mojo +++ b/examples/life/gridv2.mojo @@ -11,9 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from collections import Optional -from memory import memcpy, memset_zero, UnsafePointer import random +from collections import Optional + +from memory import UnsafePointer, memcpy, memset_zero struct Grid[rows: Int, cols: Int](StringableRaising): diff --git a/examples/life/lifev1.mojo b/examples/life/lifev1.mojo index c87ca0eeb0..9056388874 100644 --- a/examples/life/lifev1.mojo +++ b/examples/life/lifev1.mojo @@ -11,9 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +import time + from gridv1 import Grid from python import Python, PythonObject -import time def run_display( diff --git a/examples/life/lifev2.mojo b/examples/life/lifev2.mojo index c399d2eb9b..ca2071ae06 100644 --- a/examples/life/lifev2.mojo +++ b/examples/life/lifev2.mojo @@ -11,9 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +import time + from gridv2 import Grid from python import Python, PythonObject -import time def run_display( diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 9603e31c70..505303f30b 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -14,12 +14,12 @@ # RUN: %mojo %s | FileCheck %s from math import iota -from memory import UnsafePointer from sys import num_physical_cores, simdwidthof import benchmark from algorithm import parallelize, vectorize from complex import ComplexFloat64, ComplexSIMD +from memory import UnsafePointer alias float_type = DType.float32 alias int_type = DType.int32 diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 77eb9c447d..1d58d68bb3 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -17,13 +17,12 @@ from os.env import getenv from random import rand -from sys import info -from sys import simdwidthof +from sys import info, simdwidthof import benchmark from algorithm import Static2DTileUnitFunc as Tile2DFunc from algorithm import parallelize, vectorize -from memory import memset_zero, stack_allocation, UnsafePointer +from memory import UnsafePointer, memset_zero, stack_allocation from python import Python, PythonObject alias M = 512 # rows of A and C diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 42ab4732f0..34130a2c37 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -15,6 +15,7 @@ # the -t flag. Remember to replace it again before pushing any code. from sys import simdwidthof + from algorithm import elementwise from benchmark import Bench, BenchConfig, Bencher, BenchId from buffer import Buffer diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 57bfcc9e5b..0d482a72cf 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -14,15 +14,16 @@ # NOTE: to test changes on the current branch using run-benchmarks.sh, remove # the -t flag. Remember to replace it again before pushing any code. -from benchmark import Bench, Bencher, BenchId, keep, BenchConfig, Unit, run -from memory import UnsafePointer from random import * + +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run +from memory import UnsafePointer from stdlib.builtin.sort import ( - sort, - _small_sort, - _insertion_sort, _heap_sort, + _insertion_sort, + _small_sort, _SortWrapper, + sort, ) # ===----------------------------------------------------------------------===# diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index d8c782846e..c88c633579 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -14,14 +14,14 @@ # NOTE: to test changes on the current branch using run-benchmarks.sh, remove # the -t flag. Remember to replace it again before pushing any code. +from collections import Dict, Optional +from collections.dict import DictEntry +from math import ceil from random import * +from sys import sizeof from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run -from sys import sizeof from bit import bit_ceil -from math import ceil -from collections import Dict, Optional -from collections.dict import DictEntry # ===----------------------------------------------------------------------===# diff --git a/stdlib/benchmarks/collections/bench_string.mojo b/stdlib/benchmarks/collections/bench_string.mojo index be397e082e..7a46242714 100644 --- a/stdlib/benchmarks/collections/bench_string.mojo +++ b/stdlib/benchmarks/collections/bench_string.mojo @@ -14,12 +14,14 @@ # NOTE: to test changes on the current branch using run-benchmarks.sh, remove # the -t flag. Remember to replace it again before pushing any code. -from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run -from random import random_si64, seed -from pathlib import _dir_of_current_file -from collections import Optional, Dict -from os import abort +from collections import Dict, Optional from collections.string import String +from os import abort +from pathlib import _dir_of_current_file +from random import random_si64, seed + +from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run + from utils._utf8_validation import _is_valid_utf8 diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index 1f4f93c0cc..6ac5e62013 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -14,20 +14,21 @@ # NOTE: to test changes on the current branch using run-benchmarks.sh, remove # the -t flag. Remember to replace it again before pushing any code. -from benchmark import Bench, BenchConfig, Bencher, BenchId -from bit import byte_swap, rotate_bits_left -from memory import UnsafePointer -from hashlib.hash import hash as old_hash from hashlib._ahash import ( + MULTIPLE, + ROT, + U128, + U256, AHasher, _folded_multiply, _read_small, - U256, - U128, - MULTIPLE, - ROT, ) from hashlib._hasher import _hash_with_hasher +from hashlib.hash import hash as old_hash + +from benchmark import Bench, BenchConfig, Bencher, BenchId +from bit import byte_swap, rotate_bits_left +from memory import UnsafePointer # Source: https://www.101languages.net/arabic/most-common-arabic-words/ alias words_ar = """ diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 83c4c44e82..3d22aa92a8 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -15,6 +15,7 @@ # the -t flag. Remember to replace it again before pushing any code. from sys import simdwidthof + from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 6fd0b16a89..9cca51fc83 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -15,10 +15,11 @@ # the -t flag. Remember to replace it again before pushing any code. from sys import simdwidthof + from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width -from memory import memcmp, bitcast, UnsafePointer, pack_bits +from memory import UnsafePointer, bitcast, memcmp, pack_bits from utils.stringref import _align_down, _memchr, _memmem diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo index 03aa0a56ab..f2179b88d0 100644 --- a/stdlib/src/base64/_b64encode.mojo +++ b/stdlib/src/base64/_b64encode.mojo @@ -26,10 +26,12 @@ https://arxiv.org/abs/1704.00605 from collections import InlineArray from math.math import _compile_time_iota -from memory import memcpy, bitcast, UnsafePointer -from utils import IndexList from sys import llvm_intrinsic +from memory import UnsafePointer, bitcast, memcpy + +from utils import IndexList + alias Bytes = SIMD[DType.uint8, _] diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 0bf4f2a3b9..0787b01ff2 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -21,7 +21,9 @@ from base64 import b64encode from collections import List from sys import simdwidthof + import bit + from ._b64encode import b64encode_with_buffers as _b64encode_with_buffers # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index 76c255c1b5..7101144eca 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -25,10 +25,12 @@ # ===----------------------------------------------------------------------=== # from collections import InlineArray from math import log2 -from memory import bitcast from sys.info import sizeof -from utils import StaticTuple, Span + from builtin.io import _printf +from memory import bitcast + +from utils import Span, StaticTuple from utils.numerics import FPUtils, isinf, isnan diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 2644bceba2..fd2613961f 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -11,32 +11,29 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer, stack_allocation - -from sys import sizeof, alignof +from collections import Optional +from sys import alignof, sizeof import python._cpython as cp -from python import TypedPythonObject, Python, PythonObject -from python.python import _get_global_python_itf -from python._cpython import ( - PyObjectPtr, - PyMethodDef, - PyType_Slot, - PyType_Spec, - CPython, -) -from python._bindings import ( - Pythonable, +from memory import UnsafePointer, stack_allocation +from python import Python, PythonObject, TypedPythonObject +from python._bindings import ( # Imported for use by the compiler ConvertibleFromPython, PyMojoObject, - python_type_object, - py_c_function_wrapper, + Pythonable, check_argument_type, - # Imported for use by the compiler check_arguments_arity, + py_c_function_wrapper, + python_type_object, ) - -from collections import Optional +from python._cpython import ( + CPython, + PyMethodDef, + PyObjectPtr, + PyType_Slot, + PyType_Spec, +) +from python.python import _get_global_python_itf alias PyModule = TypedPythonObject["Module"] diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 97a815742f..e6efe691e0 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # """Implements functionality to start a mojo execution.""" -from memory import UnsafePointer from sys import external_call -from sys.ffi import _get_global, OpaquePointer +from sys.ffi import OpaquePointer, _get_global + +from memory import UnsafePointer fn _init_global_runtime(ignored: OpaquePointer) -> OpaquePointer: diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index c9073c3558..a877d384ac 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -15,10 +15,10 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import Set, List +from collections import List, Set -from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value +from utils._visualizers import lldb_formatter_wrapping_type # ===----------------------------------------------------------------------=== # # Boolable diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 80e4b61185..c50a2006a5 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -17,7 +17,6 @@ These are Mojo built-ins, so you don't need to import them. from memory import Pointer, UnsafePointer - # ===----------------------------------------------------------------------===# # ListLiteral # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 86f58ecb44..25d3d8b4e5 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -17,21 +17,21 @@ These are Mojo built-ins, so you don't need to import them. from os import abort -from sys import is_nvidia_gpu, is_gpu, llvm_intrinsic +from sys import is_gpu, is_nvidia_gpu, llvm_intrinsic from sys._build import is_debug_build +from sys.ffi import c_char, c_size_t, c_uint, external_call from sys.param_env import env_get_string -from sys.ffi import external_call, c_uint, c_size_t, c_char + +from builtin._location import __call_location, _SourceLocation from memory import UnsafePointer + +from utils import Span from utils.write import ( + _ArgBytes, _WriteBufferHeap, _WriteBufferStack, - _ArgBytes, write_args, ) -from utils import Span - - -from builtin._location import __call_location, _SourceLocation alias defined_mode = env_get_string["ASSERT", "safe"]() diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 09afc140d9..acdd3eba30 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement from hashlib._hasher import _HashableWithHasher, _Hasher -from sys import sizeof, bitwidthof, os_is_windows +from sys import bitwidthof, os_is_windows, sizeof alias _mIsSigned = UInt8(1) alias _mIsInteger = UInt8(1 << 7) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 99e4d6a917..3185adc6b9 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -34,10 +34,11 @@ with open("my_file.txt", "r") as f: from os import PathLike, abort from sys import external_call, sizeof from sys.ffi import OpaquePointer -from utils import Span, StringRef, StringSlice, write_buffered from memory import AddressSpace, UnsafePointer +from utils import Span, StringRef, StringSlice, write_buffered + @register_passable struct _OwnedStringRef(Boolable): diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 76c2512dad..2843c9dcbf 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -23,12 +23,14 @@ f.close() ``` """ -from utils import Span -from builtin.io import _printf from sys.ffi import external_call from sys.info import is_gpu + +from builtin.io import _printf from memory import UnsafePointer +from utils import Span + @value @register_passable("trivial") diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 940ac43683..efdc0015c5 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -16,9 +16,10 @@ These are Mojo built-ins, so you don't need to import them. """ +from collections import InlineArray, List, Optional from os import abort -from collections import List, Optional, InlineArray -from utils import StringSlice, StaticString + +from utils import StaticString, StringSlice alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 2e50208ea7..4d7e18dbac 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -16,23 +16,23 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import KeyElement - -from math import Ceilable, CeilDivable, Floorable, Truncable -from hashlib.hash import _hash_simd -from hashlib._hasher import _HashableWithHasher, _Hasher -from builtin.io import _snprintf from collections.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, ) +from hashlib._hasher import _HashableWithHasher, _Hasher +from hashlib.hash import _hash_simd +from math import Ceilable, CeilDivable, Floorable, Truncable +from sys import bitwidthof + +from builtin.io import _snprintf +from memory import UnsafePointer from python import Python, PythonObject from python._cpython import Py_ssize_t -from memory import UnsafePointer from utils import Writable, Writer -from utils._visualizers import lldb_formatter_wrapping_type from utils._select import _select_register_value as select -from sys import bitwidthof +from utils._visualizers import lldb_formatter_wrapping_type # ===----------------------------------------------------------------------=== # # Indexer diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 9efa7ae54a..259f065987 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -15,25 +15,31 @@ These are Mojo built-ins, so you don't need to import them. """ +from collections import InlineArray +from sys import _libc as libc from sys import ( bitwidthof, external_call, - stdout, - is_nvidia_gpu, is_amd_gpu, is_gpu, - _libc as libc, + is_nvidia_gpu, + stdout, ) from sys._libc import dup, fclose, fdopen, fflush from sys.ffi import OpaquePointer -from utils import Span, write_buffered, write_args -from collections import InlineArray from builtin.dtype import _get_dtype_printf_format from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer, memcpy -from utils import StringRef, StaticString, StringSlice +from utils import ( + Span, + StaticString, + StringRef, + StringSlice, + write_args, + write_buffered, +) # ===----------------------------------------------------------------------=== # # _file_handle diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 0936da0cb1..1eb57a4acb 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -16,10 +16,10 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import Dict, List -from sys.intrinsics import _type_is_eq from sys.ffi import OpaquePointer +from sys.intrinsics import _type_is_eq -from memory import ArcPointer, memcmp, memcpy, UnsafePointer +from memory import ArcPointer, UnsafePointer, memcmp, memcpy from utils import StringRef, Variant diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 345c2124a4..587b859e16 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -16,12 +16,14 @@ These are Mojo built-ins, so you don't need to import them. """ +from math import ceildiv + # FIXME(MOCO-658): Explicit conformance to these traits shouldn't be needed. from builtin._stubs import _IntIterable, _StridedIterable, _UIntStridedIterable from python import ( PythonObject, ) # TODO: remove this and fixup downstream imports -from math import ceildiv + from utils._select import _select_register_value as select # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 14e7075d53..93d37cd554 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -16,38 +16,43 @@ These are Mojo built-ins, so you don't need to import them. """ import math +from collections import InlineArray +from collections.string import ( + _calc_format_buffer_size, + _calc_initial_buffer_size, +) +from hashlib._hasher import _HashableWithHasher, _Hasher +from hashlib.hash import _hash_simd +from math import Ceilable, CeilDivable, Floorable, Truncable from math.math import _call_ptx_intrinsic +from os import abort from sys import ( PrefetchOptions, _RegisterPackType, + alignof, + bitwidthof, has_neon, + is_amd_gpu, + is_gpu, + is_nvidia_gpu, is_x86, llvm_intrinsic, prefetch, simdwidthof, - is_nvidia_gpu, - is_gpu, - is_amd_gpu, - bitwidthof, + sizeof, ) -from sys.info import _current_arch, _is_sm_8x, _is_sm_9x - from sys._assembly import inlined_assembly -from os import abort +from sys.info import _current_arch, _is_sm_8x, _is_sm_9x from bit import pop_count -from documentation import doc_private -from math import Ceilable, CeilDivable, Floorable, Truncable +from builtin._format_float import _write_float from builtin.dtype import _uint_type_of_width -from hashlib.hash import _hash_simd -from hashlib._hasher import _HashableWithHasher, _Hasher from builtin.format_int import _try_write_int -from builtin._format_float import _write_float from builtin.io import _snprintf -from collections import InlineArray -from memory import bitcast, UnsafePointer +from documentation import doc_private +from memory import UnsafePointer, bitcast -from utils import StringSlice, StaticTuple, IndexList, Span +from utils import IndexList, Span, StaticTuple, StringSlice from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan @@ -56,17 +61,12 @@ from utils.numerics import max_or_inf as _max_or_inf from utils.numerics import min_finite as _min_finite from utils.numerics import min_or_neg_inf as _min_or_neg_inf from utils.numerics import nan as _nan -from sys import sizeof, alignof from .dtype import ( _get_dtype_printf_format, _integral_type_of, - _unsigned_integral_type_of, _scientific_notation_digits, -) -from collections.string import ( - _calc_format_buffer_size, - _calc_initial_buffer_size, + _unsigned_integral_type_of, ) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 8e2f0aadbe..093cc41914 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -16,11 +16,12 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import List -from sys import bitwidthof from math import ceil +from sys import bitwidthof from bit import count_leading_zeros from memory import UnsafePointer + from utils import Span # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index c6859b4f04..367047a85c 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -15,19 +15,16 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys.ffi import c_char - -from memory import memcpy, UnsafePointer from collections import List from hashlib._hasher import _HashableWithHasher, _Hasher -from utils import StringRef, Span, StringSlice, StaticString -from utils import Writable, Writer +from sys.ffi import c_char + +from memory import UnsafePointer, memcpy + +from utils import Span, StaticString, StringRef, StringSlice, Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry -from utils.string_slice import ( - _StringSliceIter, - _to_string_list, -) +from utils.string_slice import _StringSliceIter, _to_string_list # ===----------------------------------------------------------------------===# # StringLiteral diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 698a73286e..6d7f45a029 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from sys.intrinsics import _type_is_eq + from memory import UnsafePointer from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index 2c0f3f7de9..cb3de1bcb5 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -15,11 +15,13 @@ These are Mojo built-ins, so you don't need to import them. """ +from hashlib._hasher import _HashableWithHasher, _Hasher +from hashlib.hash import _hash_simd from sys import bitwidthof -from utils._visualizers import lldb_formatter_wrapping_type + from documentation import doc_private -from hashlib.hash import _hash_simd -from hashlib._hasher import _HashableWithHasher, _Hasher + +from utils._visualizers import lldb_formatter_wrapping_type @lldb_formatter_wrapping_type diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index e6000ce5fb..a4a2ac1df3 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -18,7 +18,8 @@ You can import these APIs from the `collections` package. For example: from collections import Counter ``` """ -from collections.dict import Dict, _DictKeyIter, _DictValueIter, _DictEntryIter +from collections.dict import Dict, _DictEntryIter, _DictKeyIter, _DictValueIter + from utils import Variant diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index 5af332c273..37ec9d97bf 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -21,10 +21,10 @@ from collections import Deque ``` """ -from bit import bit_ceil from collections import Optional -from memory import UnsafePointer +from bit import bit_ceil +from memory import UnsafePointer # ===----------------------------------------------------------------------===# # Deque diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e6c8c2a821..0e545e8a87 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -31,12 +31,13 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ +from sys.ffi import OpaquePointer + +from bit import is_power_of_two from builtin.value import StringableCollectionElement +from memory import UnsafePointer, bitcast, memcpy from .optional import Optional -from bit import is_power_of_two -from sys.ffi import OpaquePointer -from memory import memcpy, bitcast, UnsafePointer trait KeyElement(CollectionElement, Hashable, EqualityComparable): diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index b1c833b75c..9112e51818 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -21,6 +21,7 @@ from collections import InlineArray from collections._index_normalization import normalize_index from sys.intrinsics import _type_is_eq + from memory import UnsafePointer from memory.maybe_uninitialized import UnsafeMaybeUninitialized diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 9d5610ff36..01486e07ed 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -20,6 +20,7 @@ from collections import InlineList """ from sys.intrinsics import _type_is_eq + from memory.maybe_uninitialized import UnsafeMaybeUninitialized diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1ed439c040..b3442b1228 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,10 +20,12 @@ from collections import List """ -from sys.intrinsics import _type_is_eq -from sys import sizeof from os import abort +from sys import sizeof +from sys.intrinsics import _type_is_eq + from memory import Pointer, UnsafePointer, memcpy + from utils import Span from .optional import Optional diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 2bcaf9ea8e..eb019dd463 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -32,6 +32,7 @@ print(d) # prints 2 """ from os import abort + from utils import Variant diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 10451bc126..500e4c73b2 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -15,9 +15,9 @@ from .dict import ( Dict, KeyElement, + RepresentableKeyElement, _DictEntryIter, _DictKeyIter, - RepresentableKeyElement, ) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 55c4da1b0e..1b38236b10 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -17,41 +17,40 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement, List, Optional from collections._index_normalization import normalize_index +from hashlib._hasher import _HashableWithHasher, _Hasher from sys import bitwidthof, llvm_intrinsic from sys.ffi import c_char -from utils import StaticString, write_args +from sys.intrinsics import _type_is_eq from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy from python import PythonObject -from sys.intrinsics import _type_is_eq -from hashlib._hasher import _HashableWithHasher, _Hasher - from utils import ( - Span, IndexList, + Span, + StaticString, StringRef, StringSlice, Variant, Writable, Writer, + write_args, ) -from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry -from utils.string_slice import ( - _utf8_byte_type, - _StringSliceIter, - _unicode_codepoint_utf8_byte_length, - _shift_unicode_to_utf8, - _to_string_list, -) - from utils._unicode import ( is_lowercase, is_uppercase, to_lowercase, to_uppercase, ) +from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry +from utils.string_slice import ( + _shift_unicode_to_utf8, + _StringSliceIter, + _to_string_list, + _unicode_codepoint_utf8_byte_length, + _utf8_byte_type, +) # ===----------------------------------------------------------------------=== # # ord diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 8812b4b61c..1dc60fcc76 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -19,9 +19,10 @@ from collections import InlinedFixedVector ``` """ -from memory import Pointer, UnsafePointer, memcpy from sys import sizeof +from memory import Pointer, UnsafePointer, memcpy + from utils import StaticTuple # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/hashlib/__init__.mojo b/stdlib/src/hashlib/__init__.mojo index 2bdc949799..9de67db15d 100644 --- a/stdlib/src/hashlib/__init__.mojo +++ b/stdlib/src/hashlib/__init__.mojo @@ -11,4 +11,4 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # """Implements the hashlib package that provides various hash algorithms.""" -from .hash import hash, Hashable +from .hash import Hashable, hash diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index 0845dc8d58..9adb7d08e0 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -11,10 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from bit import byte_swap -from bit import rotate_bits_left +from bit import byte_swap, rotate_bits_left from memory import UnsafePointer -from ._hasher import _Hasher, _HashableWithHasher + +from ._hasher import _HashableWithHasher, _Hasher alias U256 = SIMD[DType.uint64, 4] alias U128 = SIMD[DType.uint64, 2] diff --git a/stdlib/src/hashlib/_hasher.mojo b/stdlib/src/hashlib/_hasher.mojo index 7bd278d5e9..e29257a9e0 100644 --- a/stdlib/src/hashlib/_hasher.mojo +++ b/stdlib/src/hashlib/_hasher.mojo @@ -11,9 +11,10 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from ._ahash import AHasher from memory import UnsafePointer +from ._ahash import AHasher + trait _HashableWithHasher: fn __hash__[H: _Hasher](self, inout hasher: H): diff --git a/stdlib/src/hashlib/hash.mojo b/stdlib/src/hashlib/hash.mojo index 84f3a82a58..5eddfc21b8 100644 --- a/stdlib/src/hashlib/hash.mojo +++ b/stdlib/src/hashlib/hash.mojo @@ -26,13 +26,12 @@ There are a few main tools in this module: """ import random - -from sys.ffi import _Global -from sys import simdwidthof, bitwidthof from collections import InlineArray +from sys import bitwidthof, simdwidthof +from sys.ffi import _Global from builtin.dtype import _uint_type_of_width -from memory import memcpy, memset_zero, stack_allocation, bitcast, UnsafePointer +from memory import UnsafePointer, bitcast, memcpy, memset_zero, stack_allocation # ===----------------------------------------------------------------------=== # # Implementation diff --git a/stdlib/src/math/__init__.mojo b/stdlib/src/math/__init__.mojo index 3a9e67e7c7..54b918a837 100644 --- a/stdlib/src/math/__init__.mojo +++ b/stdlib/src/math/__init__.mojo @@ -15,7 +15,7 @@ # In Python, these are in the math module, so we also expose them here. from utils.numerics import inf, isfinite, isinf, isnan, nan, nextafter, ulp -from .constants import pi, e, tau +from .constants import e, pi, tau # These are not part of Python's `math` module, but we define them here. from .math import ( @@ -36,6 +36,7 @@ from .math import ( cbrt, ceil, ceildiv, + clamp, copysign, cos, cosh, @@ -53,19 +54,20 @@ from .math import ( hypot, iota, isclose, + isqrt, j0, j1, lcm, ldexp, lgamma, log, - log10, log1p, log2, + log10, logb, modf, + recip, remainder, - isqrt, scalb, sin, sinh, @@ -75,6 +77,4 @@ from .math import ( trunc, y0, y1, - clamp, - recip, ) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 0ada40bdfe..98ff2cca07 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -20,24 +20,23 @@ from math import floor """ from collections import List -from sys._assembly import inlined_assembly -from sys.ffi import _external_call_const from sys import ( - llvm_intrinsic, bitwidthof, has_avx512f, - simdwidthof, - is_nvidia_gpu, is_amd_gpu, + is_nvidia_gpu, + llvm_intrinsic, + simdwidthof, sizeof, ) - -from memory import UnsafePointer +from sys._assembly import inlined_assembly +from sys.ffi import _external_call_const +from sys.info import _current_arch from bit import count_trailing_zeros from builtin.dtype import _integral_type_of -from builtin.simd import _simd_apply, _modf -from sys.info import _current_arch +from builtin.simd import _modf, _simd_apply +from memory import UnsafePointer from utils import Span from utils.index import IndexList diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 8f03fd2fb9..1cbf5c43d3 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -20,17 +20,18 @@ from memory import memcmp """ +from collections import Optional +from sys import _libc as libc from sys import ( alignof, - llvm_intrinsic, - sizeof, - is_gpu, external_call, - simdwidthof, + is_gpu, + llvm_intrinsic, simdbitwidth, - _libc as libc, + simdwidthof, + sizeof, ) -from collections import Optional + from memory.pointer import AddressSpace, _GPUAddressSpace # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 597375cbf4..71c4c963eb 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer, stack_allocation, memcpy +from memory import UnsafePointer, memcpy, stack_allocation struct OwnedPointer[T: AnyType]: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 5f37054609..22fede2402 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -19,7 +19,7 @@ from memory import UnsafePointer ``` """ -from sys import alignof, sizeof, is_nvidia_gpu, is_gpu +from sys import alignof, is_gpu, is_nvidia_gpu, sizeof from sys.intrinsics import ( _mlirtype_is_eq, _type_is_eq, @@ -32,7 +32,6 @@ from sys.intrinsics import ( from bit import is_power_of_two from memory.memory import _free, _malloc - # ===----------------------------------------------------------------------=== # # UnsafePointer # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index b55400cad3..c07761b658 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -22,11 +22,11 @@ from .os import ( abort, getuid, listdir, - mkdir, makedirs, + mkdir, remove, - rmdir, removedirs, + rmdir, sep, unlink, ) diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index d2872d8a1a..7fec00bfdf 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -11,9 +11,9 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from time.time import _CTimeSpec from collections import InlineArray from sys.ffi import external_call +from time.time import _CTimeSpec from .fstat import stat_result diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 90a1952246..3d7cb39bd8 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -19,9 +19,10 @@ from os import Atomic ``` """ +from sys.info import is_nvidia_gpu + from builtin.dtype import _integral_type_of, _unsigned_integral_type_of from memory import UnsafePointer, bitcast -from sys.info import is_nvidia_gpu struct Atomic[type: DType]: diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index 0556389882..9ffe0eb2d8 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -23,6 +23,7 @@ from sys import external_call, os_is_linux, os_is_macos, os_is_windows from sys.ffi import c_int from memory import UnsafePointer + from utils import StringRef diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 1678cc89e6..86c793fa6b 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -19,11 +19,12 @@ from os import listdir ``` """ -from collections import List, InlineArray -from sys import os_is_linux, os_is_windows, is_gpu, external_call -from sys.ffi import c_char, OpaquePointer +from collections import InlineArray, List +from sys import external_call, is_gpu, os_is_linux, os_is_windows +from sys.ffi import OpaquePointer, c_char from memory import UnsafePointer + from utils import StringRef from .path import isdir, split diff --git a/stdlib/src/os/path/__init__.mojo b/stdlib/src/os/path/__init__.mojo index edf3b394af..ef6694482a 100644 --- a/stdlib/src/os/path/__init__.mojo +++ b/stdlib/src/os/path/__init__.mojo @@ -12,8 +12,8 @@ # ===----------------------------------------------------------------------=== # from .path import ( - dirname, basename, + dirname, exists, expanduser, expandvars, @@ -22,7 +22,7 @@ from .path import ( isfile, islink, join, + lexists, split, splitroot, - lexists, ) diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 839e66f890..3840a97fac 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -19,9 +19,11 @@ from os.path import isdir ``` """ -from collections import List, InlineArray +from collections import InlineArray, List +from pwd import getpwuid from stat import S_ISDIR, S_ISLNK, S_ISREG from sys import has_neon, os_is_linux, os_is_macos, os_is_windows + from utils import Span, StringSlice from .. import PathLike @@ -31,10 +33,9 @@ from .._linux_x86 import _lstat as _lstat_linux_x86 from .._linux_x86 import _stat as _stat_linux_x86 from .._macos import _lstat as _lstat_macos from .._macos import _stat as _stat_macos +from ..env import getenv from ..fstat import stat from ..os import sep -from ..env import getenv -from pwd import getpwuid # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 0d4449d405..5e47d0d846 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -15,14 +15,13 @@ import os from collections import List +from hashlib._hasher import _HashableWithHasher, _Hasher from os import PathLike, listdir, stat_result -from sys import os_is_windows, external_call +from sys import external_call, os_is_windows from sys.ffi import c_char from builtin._location import __call_location, _SourceLocation -from memory import stack_allocation, UnsafePointer - -from hashlib._hasher import _HashableWithHasher, _Hasher +from memory import UnsafePointer, stack_allocation from utils import StringRef diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 5592ec2663..d66496d91a 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -14,8 +14,23 @@ that are automatically imported into every Mojo program. """ -from builtin.anytype import UnknownDestructibility, AnyType -from builtin.bool import Boolable, ImplicitlyBoolable, Bool, bool, any, all +from collections import KeyElement, List +from collections.string import ( + String, + ascii, + atof, + atol, + chr, + isdigit, + islower, + isprintable, + isupper, + ord, +) +from hashlib.hash import Hashable, hash + +from builtin.anytype import AnyType, UnknownDestructibility +from builtin.bool import Bool, Boolable, ImplicitlyBoolable, all, any, bool from builtin.breakpoint import breakpoint from builtin.builtin_list import ( ListLiteral, @@ -26,38 +41,38 @@ from builtin.builtin_list import ( from builtin.builtin_slice import Slice, slice from builtin.comparable import Comparable from builtin.constrained import constrained -from builtin.coroutine import Coroutine, RaisingCoroutine, AnyCoroutine +from builtin.coroutine import AnyCoroutine, Coroutine, RaisingCoroutine from builtin.debug_assert import debug_assert from builtin.dtype import DType from builtin.equality_comparable import EqualityComparable from builtin.error import Error -from builtin.file import open, FileHandle +from builtin.file import FileHandle, open from builtin.file_descriptor import FileDescriptor from builtin.float_literal import FloatLiteral from builtin.floatable import Floatable, FloatableRaising, float from builtin.format_int import bin, hex, oct from builtin.identifiable import Identifiable, StringableIdentifiable from builtin.int import ( + Indexer, Int, - IntLike, Intable, IntableRaising, - Indexer, + IntLike, index, int, ) from builtin.int_literal import IntLiteral -from builtin.io import print, input -from builtin.len import Sized, UIntSized, SizedRaising, len +from builtin.io import input, print +from builtin.len import Sized, SizedRaising, UIntSized, len from builtin.math import ( Absable, + Powable, + Roundable, abs, divmod, max, min, - Powable, pow, - Roundable, round, ) from builtin.none import NoneType @@ -66,72 +81,55 @@ from builtin.range import range from builtin.rebind import rebind from builtin.repr import Representable, repr from builtin.reversed import ReversibleRange, reversed -from builtin.sort import sort, partition +from builtin.simd import ( + SIMD, + BFloat16, + Byte, + Float16, + Float32, + Float64, + Int8, + Int16, + Int32, + Int64, + Scalar, + UInt8, + UInt16, + UInt32, + UInt64, +) +from builtin.sort import partition, sort from builtin.str import Stringable, StringableRaising, str from builtin.string_literal import StringLiteral from builtin.swap import swap -from builtin.tuple import ( - Tuple, -) +from builtin.tuple import Tuple from builtin.type_aliases import ( AnyTrivialRegType, - ImmutableOrigin, - MutableOrigin, ImmutableAnyOrigin, + ImmutableOrigin, MutableAnyOrigin, - StaticConstantOrigin, - OriginSet, + MutableOrigin, Origin, + OriginSet, + StaticConstantOrigin, ) from builtin.uint import UInt from builtin.value import ( - Movable, - Copyable, - ExplicitlyCopyable, - Defaultable, + BoolableCollectionElement, + BoolableKeyElement, + BytesCollectionElement, CollectionElement, CollectionElementNew, - BytesCollectionElement, - StringableCollectionElement, - EqualityComparableCollectionElement, ComparableCollectionElement, + Copyable, + Defaultable, + EqualityComparableCollectionElement, + ExplicitlyCopyable, + Movable, RepresentableCollectionElement, - BoolableKeyElement, - BoolableCollectionElement, -) -from builtin.simd import ( - Scalar, - Int8, - UInt8, - Int16, - UInt16, - Int32, - UInt32, - Int64, - UInt64, - BFloat16, - Float16, - Float32, - Float64, - Byte, - SIMD, + StringableCollectionElement, ) -from builtin.type_aliases import AnyTrivialRegType +from documentation import doc_private +from memory import AddressSpace, Pointer -from collections import KeyElement, List -from collections.string import ( - String, - ord, - chr, - ascii, - atol, - atof, - isdigit, - isupper, - islower, - isprintable, -) -from hashlib.hash import hash, Hashable -from memory import Pointer, AddressSpace from utils import AsBytes, Writable, Writer -from documentation import doc_private diff --git a/stdlib/src/pwd/__init__.mojo b/stdlib/src/pwd/__init__.mojo index a62388303c..088bffae14 100644 --- a/stdlib/src/pwd/__init__.mojo +++ b/stdlib/src/pwd/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the pwd package.""" -from .pwd import getpwuid, getpwnam +from .pwd import getpwnam, getpwuid diff --git a/stdlib/src/pwd/_linux.mojo b/stdlib/src/pwd/_linux.mojo index d2ed4175a7..2c485aa5e7 100644 --- a/stdlib/src/pwd/_linux.mojo +++ b/stdlib/src/pwd/_linux.mojo @@ -10,10 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -from .pwd import Passwd -from memory import UnsafePointer from sys.ffi import c_char, external_call +from memory import UnsafePointer + +from .pwd import Passwd + alias uid_t = Int32 alias gid_t = Int32 alias char = UnsafePointer[c_char] diff --git a/stdlib/src/pwd/_macos.mojo b/stdlib/src/pwd/_macos.mojo index 9908504d97..5958ab1a58 100644 --- a/stdlib/src/pwd/_macos.mojo +++ b/stdlib/src/pwd/_macos.mojo @@ -10,10 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -from .pwd import Passwd -from memory import UnsafePointer from sys.ffi import c_char, external_call +from memory import UnsafePointer + +from .pwd import Passwd + alias uid_t = Int32 alias gid_t = Int32 alias time_t = Int diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo index 0bc26698d1..a6342d42b4 100644 --- a/stdlib/src/pwd/pwd.mojo +++ b/stdlib/src/pwd/pwd.mojo @@ -11,12 +11,13 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from sys import os_is_linux, os_is_macos, os_is_windows + # ===----------------------------------------------------------------------=== # # Passwd # ===----------------------------------------------------------------------=== # from ._linux import _getpw_linux from ._macos import _getpw_macos -from sys import os_is_windows, os_is_macos, os_is_linux @value diff --git a/stdlib/src/python/__init__.mojo b/stdlib/src/python/__init__.mojo index c91813320c..f8d0d9d73c 100644 --- a/stdlib/src/python/__init__.mojo +++ b/stdlib/src/python/__init__.mojo @@ -12,5 +12,5 @@ # ===----------------------------------------------------------------------=== # """Implements the python package.""" -from .python_object import PythonObject, TypedPythonObject from .python import Python +from .python_object import PythonObject, TypedPythonObject diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 4046ca5f50..2921ebb3bd 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -11,28 +11,25 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer - +from collections import Optional +from os import abort from sys.ffi import c_int from sys.info import sizeof -from os import abort - -from collections import Optional - +from memory import UnsafePointer from python import PythonObject, TypedPythonObject -from python.python import _get_global_python_itf from python._cpython import ( + Py_TPFLAGS_DEFAULT, + PyCFunction, + PyMethodDef, PyObject, PyObjectPtr, - PyCFunction, - PyType_Spec, PyType_Slot, - PyMethodDef, - Py_TPFLAGS_DEFAULT, - newfunc, + PyType_Spec, destructor, + newfunc, ) +from python.python import _get_global_python_itf trait ConvertibleFromPython(CollectionElement): diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index ef437a3d06..defbd9d4b9 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -18,7 +18,7 @@ Documentation for these functions can be found online at: """ from collections import InlineArray, Optional -from os import getenv, setenv, abort +from os import abort, getenv, setenv from os.path import dirname from pathlib import Path from sys import external_call @@ -34,14 +34,12 @@ from sys.ffi import ( c_uint, ) -from python.python import _get_global_python_itf -from python._bindings import Typed_initproc, PyMojoObject, Pythonable - from memory import UnsafePointer +from python._bindings import PyMojoObject, Pythonable, Typed_initproc +from python.python import _get_global_python_itf from utils import StringRef, StringSlice - # ===-----------------------------------------------------------------------===# # Raw Bindings # ===-----------------------------------------------------------------------===# diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 990daaf583..80437fc9e2 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -28,14 +28,14 @@ from memory import UnsafePointer from utils import StringRef -from .python_object import PythonObject, TypedPythonObject from ._cpython import ( CPython, Py_eval_input, Py_file_input, - PyMethodDef, Py_ssize_t, + PyMethodDef, ) +from .python_object import PythonObject, TypedPythonObject alias _PYTHON_GLOBAL = _Global["Python", _PythonGlobal, _init_python_global] diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 151336f510..a579696179 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -19,17 +19,17 @@ from python import PythonObject ``` """ +from collections import Dict +from hashlib._hasher import _HashableWithHasher, _Hasher +from sys.ffi import c_ssize_t from sys.intrinsics import _type_is_eq from memory import UnsafePointer -from collections import Dict -from utils import StringRef -from hashlib._hasher import _HashableWithHasher, _Hasher +from utils import StringRef from ._cpython import CPython, PyObjectPtr from .python import Python, _get_global_python_itf -from sys.ffi import c_ssize_t struct _PyIter(Sized): diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 37461dd625..e536c6f533 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -19,13 +19,14 @@ from random import seed ``` """ +import math +from collections import List, Optional +from math import floor from sys import bitwidthof, external_call from sys.ffi import OpaquePointer from time import perf_counter_ns -from collections import Optional, List + from memory import UnsafePointer -from math import floor -import math fn _get_random_state() -> OpaquePointer: diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 1323b94f86..baf2af6fa7 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -19,6 +19,7 @@ from .ffi import DEFAULT_RTLD, RTLD, DLHandle, external_call from .info import ( alignof, bitwidthof, + has_amd_gpu, has_avx, has_avx2, has_avx512f, @@ -27,15 +28,19 @@ from .info import ( has_neon, has_neon_int8_dotprod, has_neon_int8_matmul, + has_nvidia_gpu, has_sse4, has_vnni, + is_amd_gpu, is_apple_m1, is_apple_m2, is_apple_m3, is_apple_silicon, is_big_endian, + is_gpu, is_little_endian, is_neoverse_n1, + is_nvidia_gpu, is_x86, num_logical_cores, num_performance_cores, @@ -47,11 +52,6 @@ from .info import ( simdbytewidth, simdwidthof, sizeof, - is_nvidia_gpu, - is_amd_gpu, - is_gpu, - has_amd_gpu, - has_nvidia_gpu, ) from .intrinsics import ( PrefetchCache, @@ -69,5 +69,5 @@ from .intrinsics import ( strided_load, strided_store, ) -from .param_env import env_get_int, env_get_string, env_get_bool, is_defined +from .param_env import env_get_bool, env_get_int, env_get_string, is_defined from .terminate import exit diff --git a/stdlib/src/sys/_libc.mojo b/stdlib/src/sys/_libc.mojo index a76a1e47d1..6751a60e62 100644 --- a/stdlib/src/sys/_libc.mojo +++ b/stdlib/src/sys/_libc.mojo @@ -17,10 +17,10 @@ C standard library counterparts. These are used to implement higher level functionality in the rest of the Mojo standard library. """ -from memory import UnsafePointer from sys import os_is_windows -from sys.ffi import c_char, c_int, OpaquePointer +from sys.ffi import OpaquePointer, c_char, c_int +from memory import UnsafePointer # ===----------------------------------------------------------------------===# # stdlib.h — core C standard library operations diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index cdfbcfb18f..fea193e75b 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -13,15 +13,15 @@ """Implements a foreign functions interface (FFI).""" from os import abort +from sys._libc import dlclose, dlerror, dlopen, dlsym + from memory import UnsafePointer from utils import StringRef -from .info import os_is_linux, os_is_windows, is_64bit, os_is_macos +from .info import is_64bit, os_is_linux, os_is_macos, os_is_windows from .intrinsics import _mlirtype_is_eq -from sys._libc import dlerror, dlopen, dlclose, dlsym - # ===-----------------------------------------------------------------------===# # Primitive C type aliases # ===-----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index a2f1891fc9..2fe91f3b99 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -19,9 +19,10 @@ from sys import is_x86 ``` """ -from .ffi import _external_call_const, external_call, OpaquePointer from memory import UnsafePointer +from .ffi import OpaquePointer, _external_call_const, external_call + @always_inline("nodebug") fn _current_target() -> __mlir_type.`!kgen.target`: diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 054c66c497..dbc833dcc8 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -19,12 +19,13 @@ from sys import PrefetchLocality ``` """ -from .info import sizeof, is_nvidia_gpu -from ._assembly import inlined_assembly import math from memory import AddressSpace, UnsafePointer +from ._assembly import inlined_assembly +from .info import is_nvidia_gpu, sizeof + # ===----------------------------------------------------------------------===# # llvm_intrinsic # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/terminate.mojo b/stdlib/src/sys/terminate.mojo index 48e5fd8455..69fae08ca4 100644 --- a/stdlib/src/sys/terminate.mojo +++ b/stdlib/src/sys/terminate.mojo @@ -13,8 +13,8 @@ """This module includes the exit functions.""" -from sys.ffi import c_int from sys import _libc as libc +from sys.ffi import c_int fn exit(): diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 7f3c2dd09d..55c3ca42d2 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -21,8 +21,9 @@ from tempfile import gettempdir import os import sys -from collections import Optional, List +from collections import List, Optional from pathlib import Path + from utils import Span, write_buffered alias TMP_MAX = 10_000 diff --git a/stdlib/src/testing/__init__.mojo b/stdlib/src/testing/__init__.mojo index 23636b5081..79c2180d4d 100644 --- a/stdlib/src/testing/__init__.mojo +++ b/stdlib/src/testing/__init__.mojo @@ -17,9 +17,9 @@ from .testing import ( assert_almost_equal, assert_equal, assert_false, + assert_is, + assert_is_not, assert_not_equal, assert_raises, assert_true, - assert_is, - assert_is_not, ) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 5dff04c458..38423c9aa4 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -35,7 +35,6 @@ from math import isclose from builtin._location import __call_location, _SourceLocation - # ===----------------------------------------------------------------------=== # # Assertions # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/time/__init__.mojo b/stdlib/src/time/__init__.mojo index de8f50f95e..5d01e75fdf 100644 --- a/stdlib/src/time/__init__.mojo +++ b/stdlib/src/time/__init__.mojo @@ -13,10 +13,10 @@ """Implements the time package.""" from .time import ( + monotonic, now, perf_counter, perf_counter_ns, sleep, time_function, - monotonic, ) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index a8a38b5841..7b68624fdf 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -19,17 +19,17 @@ from time import now ``` """ +from math import floor from os import abort from sys import ( external_call, - os_is_linux, - os_is_windows, is_amd_gpu, is_nvidia_gpu, llvm_intrinsic, + os_is_linux, + os_is_windows, ) from sys._assembly import inlined_assembly -from math import floor from memory import UnsafePointer diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index d319cf884a..0f3b95ced0 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -14,11 +14,11 @@ from .index import Index, IndexList, product from .inline_string import InlineString +from .lock import BlockingScopedLock, BlockingSpinLock, SpinWaiter from .loop import unroll from .span import AsBytes, Span from .static_tuple import StaticTuple -from .stringref import StringRef from .string_slice import StaticString, StringSlice +from .stringref import StringRef from .variant import Variant -from .lock import SpinWaiter, BlockingSpinLock, BlockingScopedLock -from .write import Writer, Writable, write_args, write_buffered +from .write import Writable, Writer, write_args, write_buffered diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index ddf05a31ac..1cc0eea138 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -13,7 +13,7 @@ from pathlib import Path -from memory import AddressSpace, bitcast, UnsafePointer +from memory import AddressSpace, UnsafePointer, bitcast alias _kStartTensorMarker = "[" alias _kEndTensorMarker = "]" diff --git a/stdlib/src/utils/_unicode.mojo b/stdlib/src/utils/_unicode.mojo index 079bd7e180..fb61a24cd9 100644 --- a/stdlib/src/utils/_unicode.mojo +++ b/stdlib/src/utils/_unicode.mojo @@ -11,7 +11,8 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # from bit import count_leading_zeros -from memory import memcpy, UnsafePointer +from memory import UnsafePointer, memcpy + from ._unicode_lookups import * diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index 3cc522d33d..e7ca5a5e3b 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -25,9 +25,10 @@ Code adapted from: https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs """ -from memory import UnsafePointer -from sys.intrinsics import llvm_intrinsic from base64._b64encode import _sub_with_saturation +from sys.intrinsics import llvm_intrinsic + +from memory import UnsafePointer alias TOO_SHORT: UInt8 = 1 << 0 alias TOO_LONG: UInt8 = 1 << 1 diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo index cac20f191d..eab8549b19 100644 --- a/stdlib/src/utils/format.mojo +++ b/stdlib/src/utils/format.mojo @@ -13,7 +13,9 @@ """Implements Formatting utilities.""" from collections import Optional + from memory import UnsafePointer + from utils.string_slice import Stringlike # TODO: _FormatCurlyEntry and _FormatSpec should be public in the future for diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index ff6ada51d1..174cde61a9 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -20,10 +20,11 @@ from utils import IndexList ``` """ +from collections.string import _calc_initial_buffer_size from sys import bitwidthof + from builtin.dtype import _int_type_of_width, _uint_type_of_width from builtin.io import _get_dtype_printf_format, _snprintf -from collections.string import _calc_initial_buffer_size from . import unroll from .static_tuple import StaticTuple diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 3f8d5d52f0..055ad748a8 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -15,9 +15,8 @@ avoids heap allocations for short strings. """ -from collections import InlineArray +from collections import InlineArray, Optional from os import abort -from collections import Optional from sys import sizeof from memory import UnsafePointer, memcpy diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 9737dc2178..dbae8c0d63 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -11,12 +11,12 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer from os import Atomic -from time import sleep from sys import external_call from sys.ffi import OpaquePointer +from time import sleep +from memory import UnsafePointer # ===----------------------------------------------------------------------===# # SpinWaiter diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index efef81a73f..c0a6aa3f5e 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -from builtin.range import _ZeroStartingRange, _SequentialRange, _StridedRange +from builtin.range import _SequentialRange, _StridedRange, _ZeroStartingRange """Implements higher-order functions. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 3ebb316f2d..5a9588d107 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -21,8 +21,9 @@ from utils import Span """ from collections import InlineArray -from memory import Pointer, UnsafePointer + from builtin.builtin_list import _lit_mut_cast +from memory import Pointer, UnsafePointer trait AsBytes: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 3b6c8bb4fd..96f6aaeada 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -21,15 +21,18 @@ from utils import StringSlice ``` """ -from bit import count_leading_zeros -from utils import Span -from collections.string import _isspace, _atol, _atof from collections import List, Optional -from memory import memcmp, UnsafePointer, memcpy -from sys import simdwidthof, bitwidthof +from collections.string import _atof, _atol, _isspace +from sys import bitwidthof, simdwidthof from sys.intrinsics import unlikely + +from bit import count_leading_zeros +from memory import UnsafePointer, memcmp, memcpy from memory.memory import _memcmp_impl_unconstrained + +from utils import Span from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry + from ._utf8_validation import _is_valid_utf8 alias StaticString = StringSlice[StaticConstantOrigin] diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 729999be24..1a19e5788b 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -13,15 +13,17 @@ """Implements the StringRef class. """ -from bit import count_trailing_zeros -from builtin.dtype import _uint_type_of_width from collections.string import _atol, _isspace from hashlib._hasher import _HashableWithHasher, _Hasher +from sys import simdwidthof +from sys.ffi import c_char + +from bit import count_trailing_zeros +from builtin.dtype import _uint_type_of_width from memory import UnsafePointer, memcmp, pack_bits from memory.memory import _memcmp_impl_unconstrained + from utils import StringSlice -from sys.ffi import c_char -from sys import simdwidthof # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 1b4d8da6d2..bbb574670b 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -13,10 +13,12 @@ """Establishes the contract between `Writer` and `Writable` types.""" from collections import InlineArray -from memory import memcpy, UnsafePointer -from utils import Span, StaticString from sys.info import is_gpu + from builtin.io import _printf +from memory import UnsafePointer, memcpy + +from utils import Span, StaticString # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index 49d509d21c..d941ab794a 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -13,9 +13,9 @@ # RUN: %mojo %s from collections import Set +from sys import sizeof from testing import assert_equal, assert_false, assert_true -from sys import sizeof fn test_equality() raises: diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 7c9d3055f4..d30091958e 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -16,8 +16,8 @@ from pathlib import Path, _dir_of_current_file from sys import os_is_windows from tempfile import gettempdir -from memory import UnsafePointer +from memory import UnsafePointer from testing import assert_equal, assert_true diff --git a/stdlib/test/builtin/test_format_float.mojo b/stdlib/test/builtin/test_format_float.mojo index 1544f185ff..3bf820543d 100644 --- a/stdlib/test/builtin/test_format_float.mojo +++ b/stdlib/test/builtin/test_format_float.mojo @@ -12,10 +12,11 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from random import random_float64 + from builtin._format_float import _write_float -from testing import assert_equal from python import Python, PythonObject -from random import random_float64 +from testing import assert_equal def test_float64(): diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 8d898798da..9919b2f636 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -14,10 +14,9 @@ from sys.info import bitwidthof -from testing import assert_equal, assert_true, assert_false, assert_raises - -from python import PythonObject from memory import UnsafePointer +from python import PythonObject +from testing import assert_equal, assert_false, assert_raises, assert_true def test_properties(): diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 84d23a7955..9651e564b5 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal, assert_false, assert_true def test_add(): diff --git a/stdlib/test/builtin/test_issue_1505.mojo b/stdlib/test/builtin/test_issue_1505.mojo index a4f9c4e14f..25f8f32e6f 100644 --- a/stdlib/test/builtin/test_issue_1505.mojo +++ b/stdlib/test/builtin/test_issue_1505.mojo @@ -15,9 +15,10 @@ from random import random_ui64 -from utils import IndexList from testing import assert_equal +from utils import IndexList + fn gen_perm() -> IndexList[64]: var result = IndexList[64]() diff --git a/stdlib/test/builtin/test_list_literal.mojo b/stdlib/test/builtin/test_list_literal.mojo index 38ae7764fe..309a831b24 100644 --- a/stdlib/test/builtin/test_list_literal.mojo +++ b/stdlib/test/builtin/test_list_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal, assert_false, assert_true def test_list(): diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index ca3b03afcc..a18b7366ff 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -14,11 +14,11 @@ import sys - from tempfile import NamedTemporaryFile -from testing import assert_equal from builtin._location import __call_location, _SourceLocation +from testing import assert_equal + from utils import IndexList, StringRef diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index d6027d5429..790619c6c4 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections import Deque, Dict + from testing import assert_equal diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8b8698c9e7..d7bd8928b4 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -12,11 +12,11 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from collections import InlineArray from sys import has_neon -from memory import UnsafePointer -from collections import InlineArray from builtin.simd import _modf +from memory import UnsafePointer from testing import ( assert_almost_equal, assert_equal, @@ -24,7 +24,8 @@ from testing import ( assert_not_equal, assert_true, ) -from utils import unroll, StaticTuple, IndexList + +from utils import IndexList, StaticTuple, unroll from utils.numerics import isfinite, isinf, isnan, nan diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index 855b0a8036..e8cfb4e1f0 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -14,7 +14,9 @@ # RUN: %mojo %s | FileCheck %s from random import rand + from memory import UnsafePointer + from utils import Span diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 429378fbd4..2aaa26e063 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -13,8 +13,8 @@ # RUN: %mojo %s from sys.ffi import c_char -from memory import UnsafePointer +from memory import UnsafePointer from testing import ( assert_equal, assert_false, diff --git a/stdlib/test/builtin/test_uint.mojo b/stdlib/test/builtin/test_uint.mojo index 9e63c45937..1af1d2f125 100644 --- a/stdlib/test/builtin/test_uint.mojo +++ b/stdlib/test/builtin/test_uint.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_false, assert_not_equal, assert_true from sys import bitwidthof + from bit import count_trailing_zeros +from testing import assert_equal, assert_false, assert_not_equal, assert_true def test_simple_uint(): diff --git a/stdlib/test/collections/test_counter.mojo b/stdlib/test/collections/test_counter.mojo index 0abc8e1287..27f5f3b9e6 100644 --- a/stdlib/test/collections/test_counter.mojo +++ b/stdlib/test/collections/test_counter.mojo @@ -12,8 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from collections.counter import Counter from collections import Optional +from collections.counter import Counter from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_deque.mojo b/stdlib/test/collections/test_deque.mojo index a9350c2180..84b759eb91 100644 --- a/stdlib/test/collections/test_deque.mojo +++ b/stdlib/test/collections/test_deque.mojo @@ -12,10 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_false, assert_true, assert_raises - from collections import Deque +from testing import assert_equal, assert_false, assert_raises, assert_true + # ===----------------------------------------------------------------------===# # Implementation tests # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_inline_array.mojo b/stdlib/test/collections/test_inline_array.mojo index b16cb4b5bb..18a933c6be 100644 --- a/stdlib/test/collections/test_inline_array.mojo +++ b/stdlib/test/collections/test_inline_array.mojo @@ -13,10 +13,11 @@ # RUN: %mojo %s from collections import InlineArray -from testing import assert_equal, assert_false, assert_true -from memory.maybe_uninitialized import UnsafeMaybeUninitialized + from memory import UnsafePointer +from memory.maybe_uninitialized import UnsafeMaybeUninitialized from test_utils import ValueDestructorRecorder +from testing import assert_equal, assert_false, assert_true def test_array_unsafe_get(): diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo index b3d21ed3a0..52adea853c 100644 --- a/stdlib/test/collections/test_inline_list.mojo +++ b/stdlib/test/collections/test_inline_list.mojo @@ -13,8 +13,8 @@ # RUN: %mojo %s from collections import InlineList, Set -from memory import UnsafePointer +from memory import UnsafePointer from test_utils import MoveCounter, ValueDestructorRecorder from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 873baf32e6..cd173fc66c 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -13,8 +13,9 @@ # RUN: %mojo %s from collections import List -from memory import UnsafePointer from sys.info import sizeof + +from memory import UnsafePointer from test_utils import CopyCounter, MoveCounter from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 4dceebd59d..147e488304 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -14,8 +14,8 @@ from collections import Set -from testing import assert_false, assert_raises, assert_true from testing import assert_equal as AE +from testing import assert_false, assert_raises, assert_true fn assert_equal[T: EqualityComparable](lhs: T, rhs: T) raises: diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index d02ad51563..c70ff3a50c 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -17,6 +17,7 @@ from collections.string import ( _calc_initial_buffer_size_int64, _isspace, ) + from memory import UnsafePointer from python import Python from testing import ( diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index 87604606ca..f1fdc69a62 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -12,14 +12,16 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from bit import pop_count -from builtin._location import __call_location from hashlib._ahash import AHasher -from hashlib.hash import hash as old_hash from hashlib._hasher import _hash_with_hasher as hash -from testing import assert_equal, assert_not_equal, assert_true -from memory import memset_zero, stack_allocation +from hashlib.hash import hash as old_hash from time import now + +from bit import pop_count +from builtin._location import __call_location +from memory import memset_zero, stack_allocation +from testing import assert_equal, assert_not_equal, assert_true + from utils import Span # Source: https://www.101languages.net/arabic/most-common-arabic-words/ diff --git a/stdlib/test/hashlib/test_hash.mojo b/stdlib/test/hashlib/test_hash.mojo index 5e7662fbd7..56f475f55d 100644 --- a/stdlib/test/hashlib/test_hash.mojo +++ b/stdlib/test/hashlib/test_hash.mojo @@ -19,6 +19,7 @@ # specific. But for now they test behavior and reproducibility. from hashlib.hash import _hash_simd + from testing import assert_equal, assert_not_equal, assert_true diff --git a/stdlib/test/hashlib/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo index f41685b553..809d494c7e 100644 --- a/stdlib/test/hashlib/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -13,12 +13,14 @@ # RUN: %mojo %s -from hashlib._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher from hashlib._ahash import AHasher -from memory import UnsafePointer +from hashlib._hasher import _hash_with_hasher, _HashableWithHasher, _Hasher from pathlib import Path + +from memory import UnsafePointer from python import Python, PythonObject from testing import assert_equal, assert_true + from utils import StringRef diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 33e85712be..55976ecfe6 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -13,7 +13,6 @@ import os import shutil - from pathlib import Path import lit.formats diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index e5c2ba8fd2..82b5342aea 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -15,8 +15,8 @@ from collections import List from memory import ArcPointer, UnsafePointer -from testing import assert_equal, assert_false, assert_true from test_utils import ObservableDel +from testing import assert_equal, assert_false, assert_true def test_basic(): diff --git a/stdlib/test/memory/test_maybe_uninitialized.mojo b/stdlib/test/memory/test_maybe_uninitialized.mojo index 6c02fec9b6..5dbebfd8d7 100644 --- a/stdlib/test/memory/test_maybe_uninitialized.mojo +++ b/stdlib/test/memory/test_maybe_uninitialized.mojo @@ -13,9 +13,9 @@ # RUN: %mojo %s from os import abort + from memory import UnsafePointer from memory.maybe_uninitialized import UnsafeMaybeUninitialized - from test_utils import CopyCounter, MoveCounter, ValueDestructorRecorder from testing import assert_equal diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 52cbe98ae4..b9f754c97d 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo --debug-level full %s -from sys import sizeof, simdwidthof +from sys import simdwidthof, sizeof from memory import ( AddressSpace, diff --git a/stdlib/test/memory/test_owned_pointer.mojo b/stdlib/test/memory/test_owned_pointer.mojo index a09b8fa59b..133dff1348 100644 --- a/stdlib/test/memory/test_owned_pointer.mojo +++ b/stdlib/test/memory/test_owned_pointer.mojo @@ -12,14 +12,14 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_false, assert_true, assert_not_equal +from memory import OwnedPointer, UnsafePointer from test_utils import ( - MoveOnly, ExplicitCopyOnly, ImplicitCopyOnly, + MoveOnly, ObservableDel, ) -from memory import OwnedPointer, UnsafePointer +from testing import assert_equal, assert_false, assert_not_equal, assert_true def test_basic_ref(): diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index a44fb58cf6..d6339f7e18 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -12,9 +12,9 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from memory import UnsafePointer, AddressSpace +from memory import AddressSpace, UnsafePointer from test_utils import ExplicitCopyOnly, MoveCounter -from testing import assert_equal, assert_not_equal, assert_true, assert_false +from testing import assert_equal, assert_false, assert_not_equal, assert_true struct MoveOnlyType(Movable): diff --git a/stdlib/test/os/path/test_basename.mojo b/stdlib/test/os/path/test_basename.mojo index 24713367f8..acda789eca 100644 --- a/stdlib/test/os/path/test_basename.mojo +++ b/stdlib/test/os/path/test_basename.mojo @@ -14,6 +14,7 @@ from os.path import basename from pathlib import Path + from builtin._location import __source_location from testing import assert_equal diff --git a/stdlib/test/os/path/test_expanduser.mojo b/stdlib/test/os/path/test_expanduser.mojo index 78764e6bf1..7041eefdd6 100644 --- a/stdlib/test/os/path/test_expanduser.mojo +++ b/stdlib/test/os/path/test_expanduser.mojo @@ -14,11 +14,12 @@ import os +from os.env import getenv, setenv from os.path import expanduser, join -from os.env import setenv, getenv -from testing import assert_equal, assert_raises, assert_true from sys.info import os_is_windows +from testing import assert_equal, assert_raises, assert_true + fn get_user_path() -> String: @parameter diff --git a/stdlib/test/os/path/test_expandvars.mojo b/stdlib/test/os/path/test_expandvars.mojo index d81746d88c..4dccce1208 100644 --- a/stdlib/test/os/path/test_expandvars.mojo +++ b/stdlib/test/os/path/test_expandvars.mojo @@ -14,6 +14,7 @@ import os from os.path import expandvars + from testing import assert_equal diff --git a/stdlib/test/os/path/test_getsize.mojo b/stdlib/test/os/path/test_getsize.mojo index 6975d91335..1aa50e9bfb 100644 --- a/stdlib/test/os/path/test_getsize.mojo +++ b/stdlib/test/os/path/test_getsize.mojo @@ -14,6 +14,7 @@ from os.path import getsize from tempfile import NamedTemporaryFile + from testing import assert_equal diff --git a/stdlib/test/os/path/test_split.mojo b/stdlib/test/os/path/test_split.mojo index eaa6c008ea..0ef994827b 100644 --- a/stdlib/test/os/path/test_split.mojo +++ b/stdlib/test/os/path/test_split.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s import os -from os.path import split, expanduser +from os.path import expanduser, split from pathlib import Path from builtin._location import __source_location diff --git a/stdlib/test/os/path/test_splitroot.mojo b/stdlib/test/os/path/test_splitroot.mojo index fc52a959dd..fddf23677c 100644 --- a/stdlib/test/os/path/test_splitroot.mojo +++ b/stdlib/test/os/path/test_splitroot.mojo @@ -14,6 +14,7 @@ import os from os.path import splitroot + from testing import assert_equal diff --git a/stdlib/test/os/test_no_trap.mojo b/stdlib/test/os/test_no_trap.mojo index 3ccfd1a230..8c36b634f1 100644 --- a/stdlib/test/os/test_no_trap.mojo +++ b/stdlib/test/os/test_no_trap.mojo @@ -14,8 +14,8 @@ # We pass an_argument here to avoid the compiler from optimizing the code # away. -from sys import argv from os import abort +from sys import argv # CHECK-LABEL: OK diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index 820e1d21bb..ccd9382ad3 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from os import remove, unlink, PathLike +from os import PathLike, remove, unlink from os.path import exists from pathlib import Path diff --git a/stdlib/test/pwd/test_pwd.mojo b/stdlib/test/pwd/test_pwd.mojo index 0cab2ff11f..14f3946a87 100644 --- a/stdlib/test/pwd/test_pwd.mojo +++ b/stdlib/test/pwd/test_pwd.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -import pwd import os -from testing import assert_equal, assert_true, assert_raises +import pwd + +from testing import assert_equal, assert_raises, assert_true def test_pwuid(): diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index b6e5fc38c1..eed042c564 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -14,6 +14,7 @@ # RUN: %mojo %s from collections import Dict + from python import Python, PythonObject from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/sys/test_c_types.mojo b/stdlib/test/sys/test_c_types.mojo index 61029f21c9..f2610aa453 100644 --- a/stdlib/test/sys/test_c_types.mojo +++ b/stdlib/test/sys/test_c_types.mojo @@ -12,8 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.info import os_is_linux, os_is_macos, os_is_windows, is_64bit, is_32bit from sys.ffi import c_int, c_long, c_long_long +from sys.info import is_32bit, is_64bit, os_is_linux, os_is_macos, os_is_windows from testing import assert_equal, assert_true diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 417b5bd23b..2bc51de5c6 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -20,7 +20,7 @@ from sys import ( strided_load, strided_store, ) -from sys.intrinsics import likely, unlikely, assume +from sys.intrinsics import assume, likely, unlikely from memory import UnsafePointer, memset_zero from testing import assert_equal diff --git a/stdlib/test/sys/test_paramenv.mojo b/stdlib/test/sys/test_paramenv.mojo index abdbd3c266..b1e3cadeb6 100644 --- a/stdlib/test/sys/test_paramenv.mojo +++ b/stdlib/test/sys/test_paramenv.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -D bar=99 -D baz=hello -D foo=11 -D my_true=True -D my_false=false %s -from sys import env_get_int, env_get_bool, env_get_string, is_defined +from sys import env_get_bool, env_get_int, env_get_string, is_defined from testing import assert_equal, assert_false, assert_true diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 6f07f4d3b9..35283b7088 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -12,20 +12,20 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s +from builtin._location import _SourceLocation +from python import PythonObject from testing import ( assert_almost_equal, assert_equal, assert_false, + assert_is, + assert_is_not, assert_not_equal, assert_raises, assert_true, - assert_is, - assert_is_not, ) from utils.numerics import inf, nan -from builtin._location import _SourceLocation -from python import PythonObject def test_assert_messages(): diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 6be7e4e60a..985a0ec9f6 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -14,12 +14,12 @@ from sys import os_is_windows from time import ( + monotonic, now, perf_counter, perf_counter_ns, sleep, time_function, - monotonic, ) from testing import assert_true diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index 256146aeff..c487ea14db 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from utils import Writable, Writer import sys +from utils import Writable, Writer + fn main() raises: test_write_to_stdout() diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index 11712e5925..b2bc0d1956 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -13,9 +13,10 @@ # REQUIRES: disabled # RUN: %mojo --debug-level full %s +from os import abort + from testing import assert_equal, assert_true -from os import abort from utils import InlineString from utils.inline_string import _FixedString diff --git a/stdlib/test/utils/test_select.mojo b/stdlib/test/utils/test_select.mojo index cd82f84a2e..dc1911c98a 100644 --- a/stdlib/test/utils/test_select.mojo +++ b/stdlib/test/utils/test_select.mojo @@ -12,9 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils._select import _select_register_value from testing import assert_equal +from utils._select import _select_register_value + def test_select_register_value(): assert_equal(_select_register_value(True, 42, 100), 42) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index 79cd780401..ebf04f8157 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from collections import InlineArray, List + from memory import UnsafePointer from testing import assert_equal, assert_true diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 19efdc1e0e..191d98a7d6 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_true, assert_false +from testing import assert_equal, assert_false, assert_true from utils import Span, StringSlice from utils._utf8_validation import _is_valid_utf8 diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index b38a4e0e75..f6ccb2ce3a 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -12,11 +12,11 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s +from memory import UnsafePointer +from test_utils import ValueDestructorRecorder from testing import assert_equal, assert_false, assert_true -from memory import UnsafePointer from utils import IndexList, StaticTuple -from test_utils import ValueDestructorRecorder def test_static_tuple(): diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 2428ac18a7..f07b3aa099 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -15,8 +15,8 @@ from sys.ffi import _Global from memory import UnsafePointer -from testing import assert_equal, assert_false, assert_true from test_utils import ObservableDel +from testing import assert_equal, assert_false, assert_true from utils import Variant From c11379a6f06b0f99622f79aa5bc2b43fbf7b9215 Mon Sep 17 00:00:00 2001 From: Evan Ovadia Date: Thu, 28 Nov 2024 13:10:06 -0800 Subject: [PATCH 1938/2019] [mojo-stdlib] Makes Coroutine an explicitly destroyed type. This makes Coroutine into an explicitly destroyed (or "linear") type, which prevents it from accidentally going out of scope. The user must explicitly `await` it, or in rare circumstances, they can call `force_destroy`. MODULAR_ORIG_COMMIT_REV_ID: 8590e9fad437a5c27e91b745c4b3f0c98a94bc5f --- stdlib/src/builtin/coroutine.mojo | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index d33445e100..b1e7c4bd55 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -78,6 +78,7 @@ fn _coro_resume_noop_callback(null: AnyCoroutine): # ===----------------------------------------------------------------------=== # +@explicit_destroy @register_passable struct Coroutine[type: AnyType, origins: OriginSet]: """Represents a coroutine. @@ -130,9 +131,10 @@ struct Coroutine[type: AnyType, origins: OriginSet]: self._handle = handle @always_inline - fn __del__(owned self): + fn force_destroy(owned self): """Destroy the coroutine object.""" __mlir_op.`co.destroy`(self._handle) + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) @always_inline fn __await__(owned self) -> type as out: @@ -158,6 +160,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: # ===----------------------------------------------------------------------=== # +@explicit_destroy @register_passable struct RaisingCoroutine[type: AnyType, origins: OriginSet]: """Represents a coroutine that can raise. @@ -211,9 +214,10 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: self._handle = handle @always_inline - fn __del__(owned self): + fn force_destroy(owned self): """Destroy the coroutine object.""" __mlir_op.`co.destroy`(self._handle) + __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) @always_inline fn __await__(owned self) raises -> type as out: From 86cdf9ce08159a1725f356e487028eda64783b37 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 28 Nov 2024 16:25:44 -0800 Subject: [PATCH 1939/2019] [******][GPU] Extend the SIMD FloatLiteral constructor support MODULAR_ORIG_COMMIT_REV_ID: 8006835197cc190c357956d0993f4e8198244e63 --- stdlib/src/builtin/simd.mojo | 82 +++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 93d37cd554..b2fb025b4f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -441,7 +441,87 @@ struct SIMD[type: DType, size: Int]( # TODO (#36686): This introduces unneeded casts here to work around # parameter if issues. @parameter - if type is DType.float16: + if type is DType.float8e4m3: + self = SIMD[type, size]( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f8E4M3 + ](value.value) + ) + ) + ) + ) + elif type is DType.float8e4m3fnuz: + self = SIMD[type, size]( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f8E4M3FNUZ + ](value.value) + ) + ) + ) + ) + elif type is DType.float8e5m2: + self = SIMD[type, size]( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f8E5M2 + ](value.value) + ) + ) + ) + ) + elif type is DType.float8e5m2fnuz: + self = SIMD[type, size]( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f8E5M2FNUZ + ](value.value) + ) + ) + ) + ) + elif type is DType.float16: self = SIMD[type, size]( __mlir_op.`pop.simd.splat`[ _type = __mlir_type[ From 9375f0ef4530ae1791fd340106dff5f3a00a96c3 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 28 Nov 2024 20:12:49 -0800 Subject: [PATCH 1940/2019] [******][GPU] Export the Float8 scalar types, NFC (#51780) Export the fp8 scalar types as builtins. MODULAR_ORIG_COMMIT_REV_ID: 72806233f08c8d1fb58f3e386025929a61b5f241 --- stdlib/src/prelude/__init__.mojo | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index d66496d91a..5551b826ee 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -85,6 +85,10 @@ from builtin.simd import ( SIMD, BFloat16, Byte, + Float8e5m2, + Float8e5m2fnuz, + Float8e4m3, + Float8e4m3fnuz, Float16, Float32, Float64, From 26224d5976ab0acbd9132a5243711c2c48c7cdae Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 29 Nov 2024 00:17:48 -0800 Subject: [PATCH 1941/2019] [Stdlib] Use rebind instead of cast in FloatLiteral SIMD init MODULAR_ORIG_COMMIT_REV_ID: 2c23cbf9e6e4bcebfa4fc2c33706545e8193b0ca --- stdlib/src/builtin/simd.mojo | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b2fb025b4f..332220f11f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -448,9 +448,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -468,9 +466,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -488,9 +484,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -508,9 +502,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -528,9 +520,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -548,9 +538,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -568,9 +556,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( @@ -588,9 +574,7 @@ struct SIMD[type: DType, size: Int]( `!pop.simd<`, size.value, `,`, type.value, `>` ] ]( - __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.scalar<`, type.value, `>`] - ]( + rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`pop.cast_from_builtin`[ _type = __mlir_type[`!pop.scalar`] ]( From e0221d2c1f4719b0fc2484f31d12405b1d4baf9a Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 29 Nov 2024 10:12:55 -0800 Subject: [PATCH 1942/2019] [KGEN][******][GPU] Extend atomic operations to operate with a syncscope MODULAR_ORIG_COMMIT_REV_ID: 6ef07feb9a1786b1b325ee279d6687e18358c557 --- stdlib/src/os/atomic.mojo | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 3d7cb39bd8..28980d33e9 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -25,13 +25,14 @@ from builtin.dtype import _integral_type_of, _unsigned_integral_type_of from memory import UnsafePointer, bitcast -struct Atomic[type: DType]: +struct Atomic[type: DType, *, scope: StringLiteral = ""]: """Represents a value with atomic operations. The class provides atomic `add` and `sub` methods for mutating the value. Parameters: type: DType of the value. + scope: The memory synchronization scope. """ var value: Scalar[type] @@ -83,6 +84,7 @@ struct Atomic[type: DType]: return __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, + syncscope = scope.value, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ]( ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]().address, @@ -143,6 +145,7 @@ struct Atomic[type: DType]: return __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, + syncscope = scope.value, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ](value_addr.address, rhs.value) @@ -181,7 +184,7 @@ struct Atomic[type: DType]: @parameter if type.is_integral(): - return _compare_exchange_weak_integral_impl( + return _compare_exchange_weak_integral_impl[scope=scope]( UnsafePointer.address_of(self.value), expected, desired ) @@ -195,7 +198,7 @@ struct Atomic[type: DType]: ]() var expected_integral = bitcast[integral_type](expected) var desired_integral = bitcast[integral_type](desired) - return _compare_exchange_weak_integral_impl( + return _compare_exchange_weak_integral_impl[scope=scope]( value_integral_addr, expected_integral, desired_integral ) @@ -218,7 +221,7 @@ struct Atomic[type: DType]: """ constrained[type.is_numeric(), "the input type must be arithmetic"]() - _max_impl(ptr, rhs) + _max_impl[scope=scope](ptr, rhs) @always_inline fn max(inout self, rhs: Scalar[type]): @@ -258,7 +261,7 @@ struct Atomic[type: DType]: """ constrained[type.is_numeric(), "the input type must be arithmetic"]() - _min_impl(ptr, rhs) + _min_impl[scope=scope](ptr, rhs) @always_inline fn min(inout self, rhs: Scalar[type]): @@ -288,7 +291,7 @@ struct Atomic[type: DType]: @always_inline fn _compare_exchange_weak_integral_impl[ - type: DType, // + type: DType, //, *, scope: StringLiteral ]( value_addr: UnsafePointer[Scalar[type], **_], inout expected: Scalar[type], @@ -299,6 +302,7 @@ fn _compare_exchange_weak_integral_impl[ bin_op = __mlir_attr.`#pop`, failure_ordering = __mlir_attr.`#pop`, success_ordering = __mlir_attr.`#pop`, + syncscope = scope.value, ]( value_addr.bitcast[ __mlir_type[`!pop.scalar<`, type.value, `>`] @@ -318,69 +322,71 @@ fn _compare_exchange_weak_integral_impl[ @always_inline fn _max_impl_base[ - type: DType, // + type: DType, //, *, scope: StringLiteral ](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): var value_addr = ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]() _ = __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, + syncscope = scope.value, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ](value_addr.address, rhs.value) @always_inline fn _min_impl_base[ - type: DType, // + type: DType, //, *, scope: StringLiteral ](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): var value_addr = ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]() _ = __mlir_op.`pop.atomic.rmw`[ bin_op = __mlir_attr.`#pop`, ordering = __mlir_attr.`#pop`, + syncscope = scope.value, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ](value_addr.address, rhs.value) @always_inline fn _max_impl[ - type: DType, // + type: DType, //, *, scope: StringLiteral ](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): @parameter if is_nvidia_gpu() and type.is_floating_point(): alias integral_type = _integral_type_of[type]() alias unsigned_integral_type = _unsigned_integral_type_of[type]() if rhs >= 0: - _max_impl_base( + _max_impl_base[scope=scope]( ptr.bitcast[Scalar[integral_type]](), bitcast[integral_type](rhs), ) return - _min_impl_base( + _min_impl_base[scope=scope]( ptr.bitcast[Scalar[unsigned_integral_type]](), bitcast[unsigned_integral_type](rhs), ) return - _max_impl_base(ptr, rhs) + _max_impl_base[scope=scope](ptr, rhs) @always_inline fn _min_impl[ - type: DType, // + type: DType, //, *, scope: StringLiteral ](ptr: UnsafePointer[Scalar[type], **_], rhs: Scalar[type]): @parameter if is_nvidia_gpu() and type.is_floating_point(): alias integral_type = _integral_type_of[type]() alias unsigned_integral_type = _unsigned_integral_type_of[type]() if rhs >= 0: - _min_impl_base( + _min_impl_base[scope=scope]( ptr.bitcast[Scalar[integral_type]](), bitcast[integral_type](rhs), ) return - _max_impl_base( + _max_impl_base[scope=scope]( ptr.bitcast[Scalar[unsigned_integral_type]](), bitcast[unsigned_integral_type](rhs), ) return - _min_impl_base(ptr, rhs) + _min_impl_base[scope=scope](ptr, rhs) From 8e6072869595c31d85ed43144433b399606792f1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 29 Nov 2024 10:52:04 -0800 Subject: [PATCH 1943/2019] [mojo-lang] Fix ternary operator to propagate references. (#51773) An `if else` expression involving memory types with compatible rvalue types can and should propagate the memory value. This allows factoring of the copy and propagation of the reference through ref results. This fixes https://github.com/modularml/mojo/issues/3816 MODULAR_ORIG_COMMIT_REV_ID: bef08814a494ef976e2409e7b992b0546c794b68 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index bef76b5e3d..5c36145f92 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -577,6 +577,9 @@ what we publish. - [Issue #3710](https://github.com/modularml/mojo/issues/3710) - Mojo frees memory while reference to it is still in use. +- [Issue #3816](https://github.com/modularml/mojo/issues/3816) - Ternary + if-operator doesn't propagate origin information. + - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. From 160c6c264513c619759d20671ec4e1c1e3c65c57 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 29 Nov 2024 12:16:17 -0800 Subject: [PATCH 1944/2019] [mojo-lang] Fix origin union mutability handling. Previously it would treat differing mutabilities conservatively as being immutabable. Now we just "and" them together, which allows them to propagate conditional mutability from callee to callers. With the changes, this "just works" which feels pretty nice: ``` fn my_min[T: Comparable](ref a: T, ref b: T) -> ref [__origin_of(a, b)] T: return a if a < b else b fn main(): s1 = String("hello") s2 = String("world") my_min(s1, s2) += " fine" # hello fine world print(s1, s2) ``` This fixes https://github.com/modularml/mojo/issues/3813 This fixes https://github.com/modularml/mojo/issues/3815 MODULAR_ORIG_COMMIT_REV_ID: 806399a9c99eef117bab5307530a2dcda23b333e --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5c36145f92..9cd221161d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -580,6 +580,9 @@ what we publish. - [Issue #3816](https://github.com/modularml/mojo/issues/3816) - Ternary if-operator doesn't propagate origin information. +- [Issue #3815](https://github.com/modularml/mojo/issues/3815) - + [BUG] Mutability not preserved when taking the union of two origins. + - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. From d58b5e4af62e03775836483675fcbc52f2ad0d69 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 29 Nov 2024 13:32:04 -0800 Subject: [PATCH 1945/2019] [mojo-lang] Allow multiple memory values in a `ref` specification. This reworks handling of the `ref [x,y,z]` specification for a function argument or ref result to allow multiple memory values, implicitly forming a union like `__origin_of(x,y,z)` does. This enables some more ergonomic use cases, and also allows an address space to be specified as an arbitrary member. With this, you can write a mutability-parametric `min` with just: ``` fn my_min[T: Comparable](ref a: T, ref b: T) -> ref [a, b] T: return a if a < b else b ``` ... which is quite nice IMO! MODULAR_ORIG_COMMIT_REV_ID: 739170c1f0f981ad1a0c4c09831250e0717237ac --- docs/changelog.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9cd221161d..e3e60f7599 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -50,6 +50,20 @@ what we publish. The consequence of this is that the old hack is no longer needed for these cases! +- Various improvements to origin handling and syntax have landed, including + support for the ternary operator and allowing multiple arguments in a `ref` + specifier (which are implicitly unions). This enables expression of simple + algorithms cleanly: + + ```mojo + fn my_min[T: Comparable](ref a: T, ref b: T) -> ref [a, b] T: + return a if a < b else b + ``` + + It is also nice that `my_min` automatically and implicitly propagates the + mutability of its arguments, so things like `my_min(str1, str2) += "foo"` is + valid. + - The `UnsafePointer` type now has an `origin` parameter that can be used when the `UnsafePointer` is known to point to a value with a known origin. This origin is propagated through the `ptr[]` indirection operation. From 3efc7755ed3b18bd4aaa31ea06b24841eb730406 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 29 Nov 2024 14:11:37 -0800 Subject: [PATCH 1946/2019] [mojo-lang] Improve handling of address spaces in `ref`'s This allows direct use of `AddressSpace` in a ref instead of requiring one to dig into it, cleaning up code like `UnsafePointer`. Address spaces no longer have to be the second member of the ref spec. This also improve ASTPrinting of refs to handle mutcast and unions more nicely. MODULAR_ORIG_COMMIT_REV_ID: 2b09168755b43e2c4921b98050bd0524a5bfcb2f --- stdlib/src/memory/owned_pointer.mojo | 5 ++--- stdlib/src/memory/pointer.mojo | 4 ++-- stdlib/src/memory/unsafe_pointer.mojo | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 71c4c963eb..4b22f14066 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -104,8 +104,8 @@ struct OwnedPointer[T: AnyType]: # ===-------------------------------------------------------------------===# fn __getitem__( - ref [_, AddressSpace.GENERIC._value.value]self - ) -> ref [self, AddressSpace.GENERIC._value.value] T: + ref [AddressSpace.GENERIC]self, + ) -> ref [self, AddressSpace.GENERIC] T: """Returns a reference to the pointers's underlying data with parametric mutability. Returns: @@ -116,7 +116,6 @@ struct OwnedPointer[T: AnyType]: # returned from UnsafePointer to be guarded behind the # aliasing guarantees of the origin system here. # All of the magic happens above in the function signature - return self._inner[] # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 06ddb0f6f6..d95a8c1141 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -341,7 +341,7 @@ struct Pointer[ @staticmethod @always_inline("nodebug") - fn address_of(ref [origin, address_space._value.value]value: type) -> Self: + fn address_of(ref [origin, address_space]value: type) -> Self: """Constructs a Pointer from a reference to a value. Args: @@ -367,7 +367,7 @@ struct Pointer[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __getitem__(self) -> ref [origin, address_space._value.value] type: + fn __getitem__(self) -> ref [origin, address_space] type: """Enable subscript syntax `ptr[]` to access the element. Returns: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 22fede2402..9c1a6eac92 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -143,7 +143,7 @@ struct UnsafePointer[ @staticmethod @always_inline("nodebug") fn address_of( - ref [_, address_space._value.value]arg: type + ref [address_space]arg: type, ) -> UnsafePointer[ type, address_space=address_space, @@ -189,7 +189,7 @@ struct UnsafePointer[ @always_inline fn __getitem__( self, - ) -> ref [origin, address_space._value.value] type: + ) -> ref [origin, address_space] type: """Return a reference to the underlying data. Returns: @@ -227,7 +227,7 @@ struct UnsafePointer[ @always_inline fn __getitem__[ IntLike: IntLike, // - ](self, offset: IntLike) -> ref [origin, address_space._value.value] type: + ](self, offset: IntLike) -> ref [origin, address_space] type: """Return a reference to the underlying data, offset by the given index. Parameters: From 0c315293fe95b3f85b00b3b30d1455f8545e798e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 29 Nov 2024 15:29:58 -0800 Subject: [PATCH 1947/2019] [mojo-lang] Fix a crash with weird llvm dialect ops. This fixes the parser to not assume that the attribute returned by the MLIR fold hook for an op returns a TypedAttr. If it doesn't, we can just retain the op as it is. The LLVM dialect has an `llvm.mlir.zero` op that folds to `#llvm.zero`. This fixes https://github.com/modularml/mojo/issues/3805 MODULAR_ORIG_COMMIT_REV_ID: e2b18182eea2529afd721b3e3969ed3f36d2c3ff --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e3e60f7599..601e9f60d5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -591,6 +591,9 @@ what we publish. - [Issue #3710](https://github.com/modularml/mojo/issues/3710) - Mojo frees memory while reference to it is still in use. +- [Issue #3805](https://github.com/modularml/mojo/issues/3805) - Crash When + Initializing !llvm.ptr. + - [Issue #3816](https://github.com/modularml/mojo/issues/3816) - Ternary if-operator doesn't propagate origin information. From dda1e1d7d0e176cdb186bf1751ec9c1a087920ab Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 30 Nov 2024 21:50:23 -0800 Subject: [PATCH 1948/2019] [mojo-lang] Rename `inout` -> `mut` and `borrowed` -> `read` Per extensive discussion over on this public thread: https://github.com/modularml/mojo/issues/3623 We're moving to rename the `inout` argument convention to be called simply `mut`, and renames `borrowed` to `read` which can still be generally elided. This reduces the need to understand references for the basic conventions that many people work with, while providing a more strictly-correct and consistent model. These words are now "soft" keywords instead of "hard" keywords as well. This still maintains support for the `inout` and `borrowed` keywords, though they will eventually be removed. MODULAR_ORIG_COMMIT_REV_ID: e2b41cfb4cb8bb0b2e67ade93d32d7ef8989428e --- docs/changelog-released.md | 4 +- docs/changelog.md | 6 + examples/life/gridv1.mojo | 2 +- examples/life/gridv2.mojo | 2 +- examples/matmul.mojo | 18 +-- examples/nbody.mojo | 6 +- examples/notebooks/RayTracing.ipynb | 8 +- examples/operators/my_complex.mojo | 20 +-- .../algorithm/bench_elementwise.mojo | 2 +- stdlib/benchmarks/builtin/bench_int.mojo | 2 +- stdlib/benchmarks/builtin/bench_sort.mojo | 36 ++--- stdlib/benchmarks/collections/bench_dict.mojo | 6 +- .../benchmarks/collections/bench_string.mojo | 16 +- stdlib/benchmarks/hashlib/bench_hash.mojo | 8 +- stdlib/benchmarks/math/bench_math.mojo | 6 +- stdlib/benchmarks/utils/bench_formatter.mojo | 4 +- stdlib/benchmarks/utils/bench_memmem.mojo | 4 +- stdlib/src/base64/_b64encode.mojo | 2 +- stdlib/src/base64/base64.mojo | 2 +- stdlib/src/builtin/_format_float.mojo | 12 +- stdlib/src/builtin/_location.mojo | 2 +- stdlib/src/builtin/_pybind.mojo | 4 +- stdlib/src/builtin/_stubs.mojo | 4 +- stdlib/src/builtin/bool.mojo | 10 +- stdlib/src/builtin/builtin_list.mojo | 22 +-- stdlib/src/builtin/builtin_slice.mojo | 4 +- stdlib/src/builtin/debug_assert.mojo | 2 +- stdlib/src/builtin/dtype.mojo | 4 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/file.mojo | 6 +- stdlib/src/builtin/file_descriptor.mojo | 4 +- stdlib/src/builtin/float_literal.mojo | 8 +- stdlib/src/builtin/format_int.mojo | 4 +- stdlib/src/builtin/int.mojo | 32 ++-- stdlib/src/builtin/int_literal.mojo | 18 +-- stdlib/src/builtin/none.mojo | 2 +- stdlib/src/builtin/object.mojo | 48 +++--- stdlib/src/builtin/range.mojo | 20 +-- stdlib/src/builtin/simd.mojo | 36 ++--- stdlib/src/builtin/string_literal.mojo | 6 +- stdlib/src/builtin/swap.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/builtin/uint.mojo | 28 ++-- stdlib/src/builtin/value.mojo | 2 +- stdlib/src/collections/counter.mojo | 24 +-- stdlib/src/collections/deque.mojo | 39 +++-- stdlib/src/collections/dict.mojo | 48 +++--- stdlib/src/collections/inline_array.mojo | 4 +- stdlib/src/collections/inline_list.mojo | 4 +- stdlib/src/collections/list.mojo | 40 +++-- stdlib/src/collections/optional.mojo | 6 +- stdlib/src/collections/set.mojo | 28 ++-- stdlib/src/collections/string.mojo | 20 +-- stdlib/src/collections/vector.mojo | 10 +- stdlib/src/hashlib/_ahash.mojo | 10 +- stdlib/src/hashlib/_hasher.mojo | 8 +- stdlib/src/math/math.mojo | 4 +- stdlib/src/memory/arc.mojo | 4 +- stdlib/src/memory/maybe_uninitialized.mojo | 16 +- stdlib/src/memory/owned_pointer.mojo | 8 +- stdlib/src/memory/pointer.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 6 +- stdlib/src/os/atomic.mojo | 18 +-- stdlib/src/os/fstat.mojo | 4 +- stdlib/src/pathlib/path.mojo | 6 +- stdlib/src/pwd/pwd.mojo | 2 +- stdlib/src/python/_bindings.mojo | 18 +-- stdlib/src/python/_cpython.mojo | 150 +++++++++--------- stdlib/src/python/python.mojo | 20 +-- stdlib/src/python/python_object.mojo | 48 +++--- stdlib/src/random/random.mojo | 2 +- stdlib/src/sys/ffi.mojo | 2 +- stdlib/src/tempfile/tempfile.mojo | 10 +- stdlib/src/testing/testing.mojo | 2 +- stdlib/src/utils/format.mojo | 26 ++- stdlib/src/utils/index.mojo | 8 +- stdlib/src/utils/inline_string.mojo | 20 +-- stdlib/src/utils/lock.mojo | 14 +- stdlib/src/utils/span.mojo | 4 +- stdlib/src/utils/static_tuple.mojo | 4 +- stdlib/src/utils/string_slice.mojo | 10 +- stdlib/src/utils/stringref.mojo | 4 +- stdlib/src/utils/variant.mojo | 16 +- stdlib/src/utils/write.mojo | 32 ++-- stdlib/test/builtin/test_debug_assert.mojo | 2 +- stdlib/test/builtin/test_object.mojo | 2 +- stdlib/test/builtin/test_print.mojo | 4 +- stdlib/test/builtin/test_range.mojo | 2 +- stdlib/test/builtin/test_sort.mojo | 6 +- stdlib/test/hashlib/test_hasher.mojo | 18 +-- stdlib/test/python/test_ownership.mojo | 12 +- stdlib/test/python/test_python_info.mojo | 2 +- stdlib/test/python/test_python_interop.mojo | 8 +- stdlib/test/python/test_python_object.mojo | 2 +- stdlib/test/python/test_python_to_mojo.mojo | 2 +- stdlib/test/tempfile/test_tempfile.mojo | 8 +- stdlib/test/utils/test_format.mojo | 2 +- stdlib/test/utils/test_format_to_stdout.mojo | 2 +- 98 files changed, 602 insertions(+), 609 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 1bb87df302..5b3c84bdcf 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -6088,7 +6088,7 @@ busy this week. - 📢 The `__clone__` method for copying a value is now named `__copy__` to better follow Python term of art. -- 📢 The `__copy__` method now takes its self argument as a "borrowed" value, +- 📢 The `__copy__` method now takes its self argument as a "read" value, instead of taking it by reference. This makes it easier to write, works for `@register_passable` types, and exposes more optimization opportunities to the early optimizer and dataflow analysis passes. @@ -6153,7 +6153,7 @@ busy this week. Note that `@register_passable` types must use the later style. - 📢 The default argument convention is now the `borrowed` convention. A - "borrowed" argument is passed like a C++ `const&` so it doesn't need to + "read" argument is passed like a C++ `const&` so it doesn't need to invoke the copy constructor (aka the `__clone__` method) when passing a value to the function. There are two differences from C++ `const&`: diff --git a/docs/changelog.md b/docs/changelog.md index 601e9f60d5..640824629c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -294,6 +294,12 @@ what we publish. ### 🦋 Changed +- The `inout` and `borrowed` argument conventions have been renamed to the `mut` + and `read` argument conventions (respectively). These verbs reflect + declaratively what the callee can do to the argument value passed into the + caller, without tying in the requirement for the programmer to know about + advanced features like references. + - The argument convention for `__init__` methods has been changed from `inout` to `out`, reflecting that an `__init__` method initializes its `self` without reading from it. This also enables spelling the type of an initializer diff --git a/examples/life/gridv1.mojo b/examples/life/gridv1.mojo index 9b7ec2e386..b06cb51315 100644 --- a/examples/life/gridv1.mojo +++ b/examples/life/gridv1.mojo @@ -32,7 +32,7 @@ struct Grid(StringableRaising): def __getitem__(self, row: Int, col: Int) -> Int: return self.data[row][col] - def __setitem__(inout self, row: Int, col: Int, value: Int) -> None: + def __setitem__(mut self, row: Int, col: Int, value: Int) -> None: self.data[row][col] = value # ===-------------------------------------------------------------------===# diff --git a/examples/life/gridv2.mojo b/examples/life/gridv2.mojo index 574d41e314..3e43b5988a 100644 --- a/examples/life/gridv2.mojo +++ b/examples/life/gridv2.mojo @@ -71,7 +71,7 @@ struct Grid[rows: Int, cols: Int](StringableRaising): def __getitem__(self, row: Int, col: Int) -> Int8: return (self.data + row * cols + col)[] - def __setitem__(inout self, row: Int, col: Int, value: Int8) -> None: + def __setitem__(mut self, row: Int, col: Int, value: Int8) -> None: (self.data + row * cols + col)[] = value # ===-------------------------------------------------------------------===# diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 1d58d68bb3..ad81185519 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -70,7 +70,7 @@ struct Matrix[rows: Int, cols: Int]: fn __getitem__(self, y: Int, x: Int) -> Scalar[type]: return self.load(y, x) - fn __setitem__(inout self, y: Int, x: Int, val: Scalar[type]): + fn __setitem__(mut self, y: Int, x: Int, val: Scalar[type]): self.store(y, x, val) fn load[nelts: Int = 1](self, y: Int, x: Int) -> SIMD[type, nelts]: @@ -100,7 +100,7 @@ def run_matmul_numpy() -> Float64: return gflops -fn matmul_naive(inout C: Matrix, A: Matrix, B: Matrix): +fn matmul_naive(mut C: Matrix, A: Matrix, B: Matrix): for m in range(C.rows): for k in range(A.cols): for n in range(C.cols): @@ -108,7 +108,7 @@ fn matmul_naive(inout C: Matrix, A: Matrix, B: Matrix): # Using stdlib vectorize function -fn matmul_vectorized(inout C: Matrix, A: Matrix, B: Matrix): +fn matmul_vectorized(mut C: Matrix, A: Matrix, B: Matrix): for m in range(C.rows): for k in range(A.cols): @@ -123,7 +123,7 @@ fn matmul_vectorized(inout C: Matrix, A: Matrix, B: Matrix): # Parallelize the code by using the builtin parallelize function # num_workers is the number of worker threads to use in parallalize -fn matmul_parallelized(inout C: Matrix, A: Matrix, B: Matrix): +fn matmul_parallelized(mut C: Matrix, A: Matrix, B: Matrix): var num_workers = C.rows @parameter @@ -150,7 +150,7 @@ fn tile[tiled_fn: Tile2DFunc, tile_x: Int, tile_y: Int](end_x: Int, end_y: Int): # Use the above tile function to perform tiled matmul # Also parallelize with num_workers threads -fn matmul_tiled(inout C: Matrix, A: Matrix, B: Matrix): +fn matmul_tiled(mut C: Matrix, A: Matrix, B: Matrix): var num_workers = C.rows @parameter @@ -177,7 +177,7 @@ fn matmul_tiled(inout C: Matrix, A: Matrix, B: Matrix): # Unroll the vectorized loop by a constant factor # Also parallelize with num_workers threads -fn matmul_unrolled[mode: Int](inout C: Matrix, A: Matrix, B: Matrix): +fn matmul_unrolled[mode: Int](mut C: Matrix, A: Matrix, B: Matrix): var num_workers: Int if mode == 1: num_workers = info.num_physical_cores() @@ -232,7 +232,7 @@ fn tile_parallel[ # a global memory location, which can thrash the cache. # Also partially unroll the loop over the reduction dimension (K) # and reorder the reduction inner loop with the row iteration inner loop -fn matmul_reordered(inout C: Matrix, A: Matrix, B: Matrix): +fn matmul_reordered(mut C: Matrix, A: Matrix, B: Matrix): alias tile_m = 32 alias tile_n = 32 alias tile_k = max(4, K // 256) @@ -282,7 +282,7 @@ fn matmul_reordered(inout C: Matrix, A: Matrix, B: Matrix): @always_inline fn bench[ - func: fn (inout Matrix, Matrix, Matrix) -> None, name: StringLiteral + func: fn (mut Matrix, Matrix, Matrix) -> None, name: StringLiteral ](base_gflops: Float64, np_gflops: Float64) raises: var A = Matrix[M, K].rand() var B = Matrix[K, N].rand() @@ -313,7 +313,7 @@ fn bench[ @always_inline fn test_matrix_equal[ - func: fn (inout Matrix, Matrix, Matrix) -> None + func: fn (mut Matrix, Matrix, Matrix) -> None ](C: Matrix, A: Matrix, B: Matrix) raises -> Bool: """Runs a matmul function on A and B and tests the result for equality with C on every element. diff --git a/examples/nbody.mojo b/examples/nbody.mojo index 694f81be68..eedb4150fb 100644 --- a/examples/nbody.mojo +++ b/examples/nbody.mojo @@ -35,7 +35,7 @@ struct Planet: var mass: Float64 fn __init__( - inout self, + mut self, pos: SIMD[DType.float64, 4], velocity: SIMD[DType.float64, 4], mass: Float64, @@ -119,7 +119,7 @@ alias INITIAL_SYSTEM = List[Planet](Sun, Jupiter, Saturn, Uranus, Neptune) @always_inline -fn offset_momentum(inout bodies: List[Planet]): +fn offset_momentum(mut bodies: List[Planet]): var p = SIMD[DType.float64, 4]() for body in bodies: @@ -132,7 +132,7 @@ fn offset_momentum(inout bodies: List[Planet]): @always_inline -fn advance(inout bodies: List[Planet], dt: Float64): +fn advance(mut bodies: List[Planet], dt: Float64): for i in range(len(INITIAL_SYSTEM)): for j in range(len(INITIAL_SYSTEM) - i - 1): var body_i = bodies[i] diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 1ce972b6b4..66a768da6e 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -422,7 +422,7 @@ " var material: Material\n", "\n", " @always_inline\n", - " fn intersects(self, orig: Vec3f, dir: Vec3f, inout dist: Float32) -> Bool:\n", + " fn intersects(self, orig: Vec3f, dir: Vec3f, mut dist: Float32) -> Bool:\n", " \"\"\"This method returns True if a given ray intersects this sphere.\n", " And if it does, it writes in the `dist` parameter the distance to the\n", " origin of the ray.\n", @@ -698,9 +698,9 @@ " orig: Vec3f,\n", " dir: Vec3f,\n", " spheres: List[Sphere],\n", - " inout material: Material,\n", - " inout hit: Vec3f,\n", - " inout N: Vec3f,\n", + " mut material: Material,\n", + " mut hit: Vec3f,\n", + " mut N: Vec3f,\n", ") -> Bool:\n", " var spheres_dist = inf[DType.float32]()\n", "\n", diff --git a/examples/operators/my_complex.mojo b/examples/operators/my_complex.mojo index f13462dc26..bd4d03f275 100644 --- a/examples/operators/my_complex.mojo +++ b/examples/operators/my_complex.mojo @@ -55,7 +55,7 @@ struct Complex( fn __str__(self) -> String: return String.write(self) - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write("(", self.re) if self.im < 0: writer.write(" - ", -self.im) @@ -78,7 +78,7 @@ struct Complex( else: raise "index out of bounds" - fn __setitem__(inout self, idx: Int, value: Float64) raises: + fn __setitem__(mut self, idx: Int, value: Float64) raises: if idx == 0: self.re = value elif idx == 1: @@ -109,11 +109,11 @@ struct Complex( def __radd__(self, lhs: Float64) -> Self: return Self(self.re + lhs, self.im) - def __iadd__(inout self, rhs: Self): + def __iadd__(mut self, rhs: Self): self.re += rhs.re self.im += rhs.im - def __iadd__(inout self, rhs: Float64): + def __iadd__(mut self, rhs: Float64): self.re += rhs def __sub__(self, rhs: Self) -> Self: @@ -125,11 +125,11 @@ struct Complex( def __rsub__(self, lhs: Float64) -> Self: return Self(lhs - self.re, -self.im) - def __isub__(inout self, rhs: Self): + def __isub__(mut self, rhs: Self): self.re -= rhs.re self.im -= rhs.im - def __isub__(inout self, rhs: Float64): + def __isub__(mut self, rhs: Float64): self.re -= rhs def __mul__(self, rhs: Self) -> Self: @@ -144,13 +144,13 @@ struct Complex( def __rmul__(self, lhs: Float64) -> Self: return Self(lhs * self.re, lhs * self.im) - def __imul__(inout self, rhs: Self): + def __imul__(mut self, rhs: Self): new_re = self.re * rhs.re - self.im * rhs.im new_im = self.re * rhs.im + self.im * rhs.re self.re = new_re self.im = new_im - def __imul__(inout self, rhs: Float64): + def __imul__(mut self, rhs: Float64): self.re *= rhs self.im *= rhs @@ -168,14 +168,14 @@ struct Complex( denom = self.squared_norm() return Self((lhs * self.re) / denom, (-lhs * self.im) / denom) - def __itruediv__(inout self, rhs: Self): + def __itruediv__(mut self, rhs: Self): denom = rhs.squared_norm() new_re = (self.re * rhs.re + self.im * rhs.im) / denom new_im = (self.im * rhs.re - self.re * rhs.im) / denom self.re = new_re self.im = new_im - def __itruediv__(inout self, rhs: Float64): + def __itruediv__(mut self, rhs: Float64): self.re /= rhs self.im /= rhs diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 34130a2c37..322b66ad9c 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -27,7 +27,7 @@ from utils.index import Index, IndexList # Benchmark elementwise # ===----------------------------------------------------------------------===# @parameter -fn bench_elementwise[n: Int](inout b: Bencher) raises: +fn bench_elementwise[n: Int](mut b: Bencher) raises: var vector = Buffer[DType.index, n].stack_allocation() for i in range(len(vector)): diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index 5a469936f8..b65cd5abaf 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -21,7 +21,7 @@ from benchmark import Bench, BenchConfig, Bencher, BenchId # Benchmarks # ===----------------------------------------------------------------------===# @parameter -fn bench_stringify_small_integers(inout b: Bencher) raises: +fn bench_stringify_small_integers(mut b: Bencher) raises: @always_inline @parameter fn call_fn(): diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 0d482a72cf..0872012f78 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -34,7 +34,7 @@ from stdlib.builtin.sort import ( @always_inline fn randomize_list[ dt: DType -](inout list: List[Scalar[dt]], size: Int, max: Scalar[dt] = Scalar[dt].MAX): +](mut list: List[Scalar[dt]], size: Int, max: Scalar[dt] = Scalar[dt].MAX): @parameter if dt.is_integral(): randint(list.data, size, 0, int(max)) @@ -46,7 +46,7 @@ fn randomize_list[ @always_inline -fn insertion_sort[type: DType](inout list: List[Scalar[type]]): +fn insertion_sort[type: DType](mut list: List[Scalar[type]]): @parameter fn _less_than( lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] @@ -57,7 +57,7 @@ fn insertion_sort[type: DType](inout list: List[Scalar[type]]): @always_inline -fn small_sort[size: Int, type: DType](inout list: List[Scalar[type]]): +fn small_sort[size: Int, type: DType](mut list: List[Scalar[type]]): @parameter fn _less_than( lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] @@ -68,7 +68,7 @@ fn small_sort[size: Int, type: DType](inout list: List[Scalar[type]]): @always_inline -fn heap_sort[type: DType](inout list: List[Scalar[type]]): +fn heap_sort[type: DType](mut list: List[Scalar[type]]): @parameter fn _less_than( lhs: _SortWrapper[Scalar[type]], rhs: _SortWrapper[Scalar[type]] @@ -83,14 +83,14 @@ fn heap_sort[type: DType](inout list: List[Scalar[type]]): # ===----------------------------------------------------------------------===# -fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: +fn bench_tiny_list_sort[type: DType](mut m: Bench) raises: alias small_list_size = 5 @parameter for count in range(2, small_list_size + 1): @parameter - fn bench_sort_list(inout b: Bencher) raises: + fn bench_sort_list(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -109,7 +109,7 @@ fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: _ = list^ @parameter - fn bench_small_sort(inout b: Bencher) raises: + fn bench_small_sort(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -128,7 +128,7 @@ fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: _ = list^ @parameter - fn bench_insertion_sort(inout b: Bencher) raises: + fn bench_insertion_sort(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -162,9 +162,9 @@ fn bench_tiny_list_sort[type: DType](inout m: Bench) raises: # ===----------------------------------------------------------------------===# -fn bench_small_list_sort[type: DType](inout m: Bench, count: Int) raises: +fn bench_small_list_sort[type: DType](mut m: Bench, count: Int) raises: @parameter - fn bench_sort_list(inout b: Bencher) raises: + fn bench_sort_list(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -183,7 +183,7 @@ fn bench_small_list_sort[type: DType](inout m: Bench, count: Int) raises: _ = list^ @parameter - fn bench_insertion_sort(inout b: Bencher) raises: + fn bench_insertion_sort(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -214,9 +214,9 @@ fn bench_small_list_sort[type: DType](inout m: Bench, count: Int) raises: # ===----------------------------------------------------------------------===# -fn bench_large_list_sort[type: DType](inout m: Bench, count: Int) raises: +fn bench_large_list_sort[type: DType](mut m: Bench, count: Int) raises: @parameter - fn bench_sort_list(inout b: Bencher) raises: + fn bench_sort_list(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -235,7 +235,7 @@ fn bench_large_list_sort[type: DType](inout m: Bench, count: Int) raises: _ = list^ @parameter - fn bench_heap_sort(inout b: Bencher) raises: + fn bench_heap_sort(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[Scalar[type]].alloc(count) var list = List[Scalar[type]](ptr=ptr, length=count, capacity=count) @@ -267,11 +267,9 @@ fn bench_large_list_sort[type: DType](inout m: Bench, count: Int) raises: # ===----------------------------------------------------------------------===# -fn bench_low_cardinality_list_sort( - inout m: Bench, count: Int, delta: Int -) raises: +fn bench_low_cardinality_list_sort(mut m: Bench, count: Int, delta: Int) raises: @parameter - fn bench_sort_list(inout b: Bencher) raises: + fn bench_sort_list(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[UInt8].alloc(count) var list = List[UInt8](ptr=ptr, length=count, capacity=count) @@ -290,7 +288,7 @@ fn bench_low_cardinality_list_sort( _ = list^ @parameter - fn bench_heap_sort(inout b: Bencher) raises: + fn bench_heap_sort(mut b: Bencher) raises: seed(1) var ptr = UnsafePointer[UInt8].alloc(count) var list = List[UInt8](ptr=ptr, length=count, capacity=count) diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index c88c633579..b50d97a98d 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -38,7 +38,7 @@ fn make_dict[size: Int]() -> Dict[Int, Int]: # Benchmark Dict init # ===----------------------------------------------------------------------===# @parameter -fn bench_dict_init(inout b: Bencher) raises: +fn bench_dict_init(mut b: Bencher) raises: @always_inline @parameter fn call_fn(): @@ -54,7 +54,7 @@ fn bench_dict_init(inout b: Bencher) raises: # Benchmark Dict Insert # ===----------------------------------------------------------------------===# @parameter -fn bench_dict_insert[size: Int](inout b: Bencher) raises: +fn bench_dict_insert[size: Int](mut b: Bencher) raises: """Insert 100 new items.""" var items = make_dict[size]() @@ -72,7 +72,7 @@ fn bench_dict_insert[size: Int](inout b: Bencher) raises: # Benchmark Dict Lookup # ===----------------------------------------------------------------------===# @parameter -fn bench_dict_lookup[size: Int](inout b: Bencher) raises: +fn bench_dict_lookup[size: Int](mut b: Bencher) raises: """Lookup 100 items.""" var items = make_dict[size]() var closest_divisor = ceil(100 / size) diff --git a/stdlib/benchmarks/collections/bench_string.mojo b/stdlib/benchmarks/collections/bench_string.mojo index 7a46242714..85ee5050ee 100644 --- a/stdlib/benchmarks/collections/bench_string.mojo +++ b/stdlib/benchmarks/collections/bench_string.mojo @@ -65,7 +65,7 @@ fn make_string[ # Benchmark string init # ===----------------------------------------------------------------------===# @parameter -fn bench_string_init(inout b: Bencher) raises: +fn bench_string_init(mut b: Bencher) raises: @always_inline @parameter fn call_fn(): @@ -84,7 +84,7 @@ fn bench_string_count[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN", sequence: StringLiteral = "a", -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".txt") @always_inline @@ -105,7 +105,7 @@ fn bench_string_split[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN", sequence: Optional[StringLiteral] = None, -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".txt") @always_inline @@ -130,7 +130,7 @@ fn bench_string_split[ @parameter fn bench_string_splitlines[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".txt") @always_inline @@ -149,7 +149,7 @@ fn bench_string_splitlines[ @parameter fn bench_string_lower[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".txt") @always_inline @@ -168,7 +168,7 @@ fn bench_string_lower[ @parameter fn bench_string_upper[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".txt") @always_inline @@ -190,7 +190,7 @@ fn bench_string_replace[ filename: StringLiteral = "UN_charter_EN", old: StringLiteral = "a", new: StringLiteral = "A", -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".txt") @always_inline @@ -209,7 +209,7 @@ fn bench_string_replace[ @parameter fn bench_string_is_valid_utf8[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" -](inout b: Bencher) raises: +](mut b: Bencher) raises: var items = make_string[length](filename + ".html") @always_inline diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index 6ac5e62013..0c6873f109 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -599,7 +599,7 @@ fn gen_word_pairs[words: String = words_en]() -> List[String]: # Benchmarks # ===----------------------------------------------------------------------===# @parameter -fn bench_small_keys[s: String](inout b: Bencher) raises: +fn bench_small_keys[s: String](mut b: Bencher) raises: var words = gen_word_pairs[s]() @always_inline @@ -613,7 +613,7 @@ fn bench_small_keys[s: String](inout b: Bencher) raises: @parameter -fn bench_small_keys_new_hash_function[s: String](inout b: Bencher) raises: +fn bench_small_keys_new_hash_function[s: String](mut b: Bencher) raises: var words = gen_word_pairs[s]() @always_inline @@ -627,7 +627,7 @@ fn bench_small_keys_new_hash_function[s: String](inout b: Bencher) raises: @parameter -fn bench_long_key[s: String](inout b: Bencher) raises: +fn bench_long_key[s: String](mut b: Bencher) raises: @always_inline @parameter fn call_fn(): @@ -638,7 +638,7 @@ fn bench_long_key[s: String](inout b: Bencher) raises: @parameter -fn bench_long_key_new_hash_function[s: String](inout b: Bencher) raises: +fn bench_long_key_new_hash_function[s: String](mut b: Bencher) raises: @always_inline @parameter fn call_fn(): diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index 1ba4175a74..b7d4c1ae43 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -62,7 +62,7 @@ var int_inputs = make_int_inputs(0, 10_000_000, 1_000_000) @parameter fn bench_math[ math_f1p: fn[type: DType, size: Int] (SIMD[type, size]) -> SIMD[type, size] -](inout b: Bencher) raises: +](mut b: Bencher) raises: @always_inline @parameter fn call_fn() raises: @@ -81,7 +81,7 @@ fn bench_math3[ math_f3p: fn[type: DType, size: Int] ( SIMD[type, size], SIMD[type, size], SIMD[type, size] ) -> SIMD[type, size] -](inout b: Bencher) raises: +](mut b: Bencher) raises: @always_inline @parameter fn call_fn() raises: @@ -96,7 +96,7 @@ fn bench_math3[ # Benchmark lcm/gcd # ===----------------------------------------------------------------------===# @parameter -fn bench_math2[math_f2p: fn (Int, Int, /) -> Int](inout b: Bencher) raises: +fn bench_math2[math_f2p: fn (Int, Int, /) -> Int](mut b: Bencher) raises: @always_inline @parameter fn call_fn() raises: diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 3d22aa92a8..818a0a2cff 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -31,7 +31,7 @@ from utils.stringref import _align_down, _memchr, _memmem # Benchmarks # ===----------------------------------------------------------------------===# @parameter -fn bench_writer_int[n: Int](inout b: Bencher) raises: +fn bench_writer_int[n: Int](mut b: Bencher) raises: @always_inline @parameter fn call_fn(): @@ -43,7 +43,7 @@ fn bench_writer_int[n: Int](inout b: Bencher) raises: @parameter -fn bench_writer_simd[n: Int](inout b: Bencher) raises: +fn bench_writer_simd[n: Int](mut b: Bencher) raises: @always_inline @parameter fn call_fn(): diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 9cca51fc83..97925b2884 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -189,7 +189,7 @@ fn _memmem_baseline[ # Benchmarks # ===----------------------------------------------------------------------===# @parameter -fn bench_find_baseline(inout b: Bencher) raises: +fn bench_find_baseline(mut b: Bencher) raises: @always_inline @parameter fn call_fn(): @@ -204,7 +204,7 @@ fn bench_find_baseline(inout b: Bencher) raises: @parameter -fn bench_find_optimized(inout b: Bencher) raises: +fn bench_find_optimized(mut b: Bencher) raises: @always_inline @parameter fn call_fn(): diff --git a/stdlib/src/base64/_b64encode.mojo b/stdlib/src/base64/_b64encode.mojo index f2179b88d0..74b8c31501 100644 --- a/stdlib/src/base64/_b64encode.mojo +++ b/stdlib/src/base64/_b64encode.mojo @@ -213,7 +213,7 @@ fn store_incomplete_simd[ # TODO: Use Span instead of List as input when Span is easier to use @no_inline fn b64encode_with_buffers( - input_bytes: List[UInt8, _], inout result: List[UInt8, _] + input_bytes: List[UInt8, _], mut result: List[UInt8, _] ): alias simd_width = sys.simdbytewidth() alias input_simd_width = simd_width * 3 // 4 diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 0787b01ff2..d9a3f7f71b 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -65,7 +65,7 @@ fn _ascii_to_value(char: String) -> Int: # TODO: Use Span instead of List as input when Span is easier to use -fn b64encode(input_bytes: List[UInt8, _], inout result: List[UInt8, _]): +fn b64encode(input_bytes: List[UInt8, _], mut result: List[UInt8, _]): """Performs base64 encoding on the input string. Args: diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index 7101144eca..b46e9ce306 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -40,7 +40,7 @@ struct _UInt128: var high: UInt64 var low: UInt64 - fn __iadd__(inout self, n: UInt64): + fn __iadd__(mut self, n: UInt64): var sum = (self.low + n) & UInt64.MAX self.high += 1 if sum < self.low else 0 self.low = sum @@ -89,9 +89,7 @@ struct FP[type: DType, CarrierDType: DType = FPUtils[type].uint_type]: alias small_divisor = pow(10, Self.kappa) -fn _write_float[ - W: Writer, type: DType, // -](inout writer: W, value: Scalar[type]): +fn _write_float[W: Writer, type: DType, //](mut writer: W, value: Scalar[type]): """Write a SIMD float type into a Writer, using the dragonbox algorithm for perfect roundtrip, shortest representable format, and high performance. Paper: https://github.com/jk-jeon/dragonbox/blob/master/other_files/Dragonbox.pdf @@ -228,7 +226,7 @@ fn _write_float[ fn _to_decimal[ CarrierDType: DType, //, type: DType -](inout sig: Scalar[CarrierDType], inout exp: Int): +](mut sig: Scalar[CarrierDType], mut exp: Int): """Transform the raw binary significand to decimal significand, and biased binary exponent into a decimal power of 10 exponent. """ @@ -500,7 +498,7 @@ fn _umul128[ fn _remove_trailing_zeros[ CarrierDType: DType -](inout sig: Scalar[CarrierDType], inout exp: Int): +](mut sig: Scalar[CarrierDType], mut exp: Int): """Fastest alg for removing trailing zeroes: https://github.com/jk-jeon/rtz_benchmark. """ @@ -629,7 +627,7 @@ fn _check_divisibility_and_divide_by_pow10[ CarrierDType: DType, //, carrier_bits: Int, divide_magic_number: StaticTuple[UInt32, 2], -](inout n: Scalar[CarrierDType], N: Int) -> Bool: +](mut n: Scalar[CarrierDType], N: Int) -> Bool: # Make sure the computation for max_n does not overflow. debug_assert(N + 1 <= _floor_log10_pow2(carrier_bits)) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index 2816204b62..463cba7b08 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -42,7 +42,7 @@ struct _SourceLocation(Writable, Stringable): """ return "At " + str(self) + ": " + str(msg) - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats the source location to the provided Writer. diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index fd2613961f..24b0fdc619 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -76,7 +76,7 @@ fn pointer_bitcast[ fn gen_pytype_wrapper[ T: Pythonable, name: StringLiteral, -](inout module: PythonObject) raises: +](mut module: PythonObject) raises: # TODO(MOCO-1301): Add support for member method generation. # TODO(MOCO-1302): Add support for generating member field as computed properties. # TODO(MOCO-1307): Add support for constructor generation. @@ -99,7 +99,7 @@ fn add_wrapper_to_module[ PythonObject, TypedPythonObject["Tuple"] ) raises -> PythonObject, func_name: StringLiteral, -](inout module_obj: PythonObject) raises: +](mut module_obj: PythonObject) raises: var module = TypedPythonObject["Module"](unsafe_unchecked_from=module_obj) Python.add_functions( module, diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index 92e661dc00..c116d52408 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -29,12 +29,12 @@ struct __MLIRType[T: AnyTrivialRegType](Movable, Copyable): trait _IntNext(Copyable): - fn __next__(inout self) -> Int: + fn __next__(mut self) -> Int: ... trait _UIntNext(Copyable): - fn __next__(inout self) -> UInt: + fn __next__(mut self) -> UInt: ... diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index a877d384ac..e48f1c9f5a 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -155,7 +155,7 @@ struct Bool( @always_inline("nodebug") @implicit - fn __init__[T: ImplicitlyBoolable, //](inout self, value: T): + fn __init__[T: ImplicitlyBoolable, //](mut self, value: T): """Convert an ImplicitlyBoolable value to a Bool. Parameters: @@ -226,7 +226,7 @@ struct Bool( return String.write(self) @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this boolean to the provided Writer. @@ -404,7 +404,7 @@ struct Bool( return __mlir_op.`pop.and`(self.value, rhs.value) @always_inline("nodebug") - fn __iand__(inout self, rhs: Bool): + fn __iand__(mut self, rhs: Bool): """Computes `self & rhs` and store the result in `self`. Args: @@ -440,7 +440,7 @@ struct Bool( return __mlir_op.`pop.or`(self.value, rhs.value) @always_inline("nodebug") - fn __ior__(inout self, rhs: Bool): + fn __ior__(mut self, rhs: Bool): """Computes `self | rhs` and store the result in `self`. Args: @@ -476,7 +476,7 @@ struct Bool( return __mlir_op.`pop.xor`(self.value, rhs.value) @always_inline("nodebug") - fn __ixor__(inout self, rhs: Bool): + fn __ixor__(mut self, rhs: Bool): """Computes `self ^ rhs` and stores the result in `self`. Args: diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index c50a2006a5..0272fbc2d6 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -135,7 +135,7 @@ struct _VariadicListIter[type: AnyTrivialRegType]: var index: Int var src: VariadicList[type] - fn __next__(inout self) -> type: + fn __next__(mut self) -> type: self.index += 1 return self.src[self.index - 1] @@ -239,12 +239,12 @@ struct _VariadicListMemIter[ var src: Pointer[Self.variadic_list_type, list_origin] fn __init__( - inout self, index: Int, ref [list_origin]list: Self.variadic_list_type + mut self, index: Int, ref [list_origin]list: Self.variadic_list_type ): self.index = index self.src = Pointer.address_of(list) - fn __next__(inout self) -> Self.variadic_list_type.reference_type: + fn __next__(mut self) -> Self.variadic_list_type.reference_type: self.index += 1 # TODO: Need to make this return a dereferenced reference, not a # reference that must be deref'd by the user. @@ -303,7 +303,7 @@ struct VariadicListMem[ Parameters: elt_is_mutable: True if the elements of the list are mutable for an - inout or owned argument. + mut or owned argument. element_type: The type of the elements in the list. origin: The reference origin of the underlying elements. """ @@ -326,7 +326,7 @@ struct VariadicListMem[ # Life cycle methods # ===-------------------------------------------------------------------===# - # Provide support for borrowed variadic arguments. + # Provide support for read-only variadic arguments. @doc_private @always_inline @implicit @@ -339,9 +339,9 @@ struct VariadicListMem[ self.value = value self._is_owned = False - # Provide support for variadics of *inout* arguments. The reference will + # Provide support for variadics of *mut* arguments. The reference will # automatically be inferred to be mutable, and the !kgen.variadic will have - # convention=inout. + # convention=mut. alias _inout_variadic_type = __mlir_type[ `!kgen.variadic<`, Self._mlir_ref_type, `, inout>` ] @@ -489,7 +489,7 @@ struct VariadicPack[ Parameters: elt_is_mutable: True if the elements of the list are mutable for an - inout or owned argument pack. + mut or owned argument pack. origin: The reference origin of the underlying elements. element_trait: The trait that each element of the pack conforms to. element_types: The list of types held by the argument pack. @@ -519,7 +519,7 @@ struct VariadicPack[ Args: value: The argument to construct the pack with. - is_owned: Whether this is an 'owned' pack or 'inout'/'borrowed'. + is_owned: Whether this is an 'owned' pack or 'mut'/'read-only'. """ self._value = value self._is_owned = is_owned @@ -602,7 +602,7 @@ struct VariadicPack[ """Apply a function to each element of the pack in order. This applies the specified function (which must be parametric on the element type) to each element of the pack, from the first element to the last, passing - in each element as a borrowed argument. + in each element as a read-only argument. Parameters: func: The function to apply to each element. @@ -619,7 +619,7 @@ struct VariadicPack[ """Apply a function to each element of the pack in order. This applies the specified function (which must be parametric on the element type) to each element of the pack, from the first element to the last, passing - in each element as a borrowed argument. + in each element as a read-only argument. Parameters: func: The function to apply to each element. diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index cc5a6d2f1a..0d0fe96750 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -66,7 +66,7 @@ struct Slice( @always_inline fn __init__( - inout self, + mut self, start: Optional[Int], end: Optional[Int], step: Optional[Int], @@ -115,7 +115,7 @@ struct Slice( return self.__str__() @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Write Slice string representation to a `Writer`. Parameters: diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 25d3d8b4e5..4d0f1bb23a 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -308,7 +308,7 @@ struct _ThreadContext(Writable): self.thread_y = _get_id["thread", "y"]() self.thread_z = _get_id["thread", "z"]() - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write( "block: [", self.block_x, diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index acdd3eba30..1d1c37d16f 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -176,7 +176,7 @@ struct DType( return String.write(self) @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this dtype to the provided Writer. @@ -327,7 +327,7 @@ struct DType( """ return hash(UInt8(self._as_i8())) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with this `DType` value. Parameters: diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index c319e708ea..1d19b5d201 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -162,7 +162,7 @@ struct Error( return String.write(self) @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this error to the provided Writer. diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 3185adc6b9..823cbf9348 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -112,7 +112,7 @@ struct FileHandle: except: pass - fn close(inout self) raises: + fn close(mut self) raises: """Closes the file handle.""" if not self.handle: return @@ -405,7 +405,7 @@ struct FileHandle: return pos @always_inline - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): """ Write a span of bytes to the file. @@ -423,7 +423,7 @@ struct FileHandle: if err_msg: abort(err_msg^.consume_as_error()) - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. Parameters: diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 2843c9dcbf..8f7709d027 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -59,7 +59,7 @@ struct FileDescriptor(Writer): self.value = f._get_raw_fd() @always_inline - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): """ Write a span of bytes to the file. @@ -83,7 +83,7 @@ struct FileDescriptor(Writer): written, ) - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. Parameters: diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 81308401b5..906e8d40dc 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -466,7 +466,7 @@ struct FloatLiteral( # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __iadd__(inout self, rhs: FloatLiteral): + fn __iadd__(mut self, rhs: FloatLiteral): """In-place addition operator. Args: @@ -475,7 +475,7 @@ struct FloatLiteral( self = self + rhs @always_inline("nodebug") - fn __isub__(inout self, rhs: FloatLiteral): + fn __isub__(mut self, rhs: FloatLiteral): """In-place subtraction operator. Args: @@ -484,7 +484,7 @@ struct FloatLiteral( self = self - rhs @always_inline("nodebug") - fn __imul__(inout self, rhs: FloatLiteral): + fn __imul__(mut self, rhs: FloatLiteral): """In-place multiplication operator. Args: @@ -493,7 +493,7 @@ struct FloatLiteral( self = self * rhs @always_inline("nodebug") - fn __itruediv__(inout self, rhs: FloatLiteral): + fn __itruediv__(mut self, rhs: FloatLiteral): """In-place division. Args: diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index efdc0015c5..0239369fbe 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -249,7 +249,7 @@ fn _write_int[ type: DType, W: Writer, ]( - inout writer: W, + mut writer: W, value: Scalar[type], /, radix: Int = 10, @@ -268,7 +268,7 @@ fn _try_write_int[ type: DType, W: Writer, ]( - inout writer: W, + mut writer: W, value: Scalar[type], /, radix: Int = 10, diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 4d7e18dbac..61a74df2dc 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -418,7 +418,7 @@ struct Int( @always_inline("nodebug") @implicit - fn __init__[IndexerTy: Indexer](inout self, value: IndexerTy): + fn __init__[IndexerTy: Indexer](mut self, value: IndexerTy): """Construct Int from the given Indexer value. Parameters: @@ -765,7 +765,7 @@ struct Int( # ===----------------------------------------------------------------------===# @always_inline("nodebug") - fn __iadd__(inout self, rhs: Int): + fn __iadd__(mut self, rhs: Int): """Compute `self + rhs` and save the result in self. Args: @@ -774,7 +774,7 @@ struct Int( self = self + rhs @always_inline("nodebug") - fn __isub__(inout self, rhs: Int): + fn __isub__(mut self, rhs: Int): """Compute `self - rhs` and save the result in self. Args: @@ -783,7 +783,7 @@ struct Int( self = self - rhs @always_inline("nodebug") - fn __imul__(inout self, rhs: Int): + fn __imul__(mut self, rhs: Int): """Compute self*rhs and save the result in self. Args: @@ -791,7 +791,7 @@ struct Int( """ self = self * rhs - fn __itruediv__(inout self, rhs: Int): + fn __itruediv__(mut self, rhs: Int): """Compute `self / rhs`, convert to int, and save the result in self. Since `floor(self / rhs)` is equivalent to `self // rhs`, this yields @@ -803,7 +803,7 @@ struct Int( self = self // rhs @always_inline("nodebug") - fn __ifloordiv__(inout self, rhs: Int): + fn __ifloordiv__(mut self, rhs: Int): """Compute `self // rhs` and save the result in self. Args: @@ -811,7 +811,7 @@ struct Int( """ self = self // rhs - fn __imod__(inout self, rhs: Int): + fn __imod__(mut self, rhs: Int): """Compute `self % rhs` and save the result in self. Args: @@ -820,7 +820,7 @@ struct Int( self = self % rhs @always_inline("nodebug") - fn __ipow__(inout self, rhs: Int): + fn __ipow__(mut self, rhs: Int): """Compute `pow(self, rhs)` and save the result in self. Args: @@ -829,7 +829,7 @@ struct Int( self = self**rhs @always_inline("nodebug") - fn __ilshift__(inout self, rhs: Int): + fn __ilshift__(mut self, rhs: Int): """Compute `self << rhs` and save the result in self. Args: @@ -838,7 +838,7 @@ struct Int( self = self << rhs @always_inline("nodebug") - fn __irshift__(inout self, rhs: Int): + fn __irshift__(mut self, rhs: Int): """Compute `self >> rhs` and save the result in self. Args: @@ -847,7 +847,7 @@ struct Int( self = self >> rhs @always_inline("nodebug") - fn __iand__(inout self, rhs: Int): + fn __iand__(mut self, rhs: Int): """Compute `self & rhs` and save the result in self. Args: @@ -856,7 +856,7 @@ struct Int( self = self & rhs @always_inline("nodebug") - fn __ixor__(inout self, rhs: Int): + fn __ixor__(mut self, rhs: Int): """Compute `self ^ rhs` and save the result in self. Args: @@ -865,7 +865,7 @@ struct Int( self = self ^ rhs @always_inline("nodebug") - fn __ior__(inout self, rhs: Int): + fn __ior__(mut self, rhs: Int): """Compute self|rhs and save the result in self. Args: @@ -1141,7 +1141,7 @@ struct Int( # TODO(MOCO-636): switch to DType.index return _hash_simd(Scalar[DType.int64](self)) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with this int value. Parameters: @@ -1180,7 +1180,7 @@ struct Int( # Methods # ===-------------------------------------------------------------------===# - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this integer to the provided Writer. @@ -1193,7 +1193,7 @@ struct Int( writer.write(Int64(self)) - fn write_padded[W: Writer](self, inout writer: W, width: Int): + fn write_padded[W: Writer](self, mut writer: W, width: Int): """Write the int right-aligned to a set padding. Parameters: diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 0432c933d3..360ed0e9a6 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -378,7 +378,7 @@ struct IntLiteral( # ===----------------------------------------------------------------------===# @always_inline("nodebug") - fn __iadd__(inout self, rhs: Self): + fn __iadd__(mut self, rhs: Self): """Compute `self + rhs` and save the result in self. Args: @@ -387,7 +387,7 @@ struct IntLiteral( self = self + rhs @always_inline("nodebug") - fn __isub__(inout self, rhs: Self): + fn __isub__(mut self, rhs: Self): """Compute `self - rhs` and save the result in self. Args: @@ -396,7 +396,7 @@ struct IntLiteral( self = self - rhs @always_inline("nodebug") - fn __imul__(inout self, rhs: Self): + fn __imul__(mut self, rhs: Self): """Compute self*rhs and save the result in self. Args: @@ -405,7 +405,7 @@ struct IntLiteral( self = self * rhs @always_inline("nodebug") - fn __ifloordiv__(inout self, rhs: Self): + fn __ifloordiv__(mut self, rhs: Self): """Compute self//rhs and save the result in self. Args: @@ -414,7 +414,7 @@ struct IntLiteral( self = self // rhs @always_inline("nodebug") - fn __ilshift__(inout self, rhs: Self): + fn __ilshift__(mut self, rhs: Self): """Compute `self << rhs` and save the result in self. Args: @@ -423,7 +423,7 @@ struct IntLiteral( self = self << rhs @always_inline("nodebug") - fn __irshift__(inout self, rhs: Self): + fn __irshift__(mut self, rhs: Self): """Compute `self >> rhs` and save the result in self. Args: @@ -432,7 +432,7 @@ struct IntLiteral( self = self >> rhs @always_inline("nodebug") - fn __iand__(inout self, rhs: Self): + fn __iand__(mut self, rhs: Self): """Compute `self & rhs` and save the result in self. Args: @@ -441,7 +441,7 @@ struct IntLiteral( self = self & rhs @always_inline("nodebug") - fn __ixor__(inout self, rhs: Self): + fn __ixor__(mut self, rhs: Self): """Compute `self ^ rhs` and save the result in self. Args: @@ -450,7 +450,7 @@ struct IntLiteral( self = self ^ rhs @always_inline("nodebug") - fn __ior__(inout self, rhs: Self): + fn __ior__(mut self, rhs: Self): """Compute self|rhs and save the result in self. Args: diff --git a/stdlib/src/builtin/none.mojo b/stdlib/src/builtin/none.mojo index 3cfc856b32..2e5dc6ef35 100644 --- a/stdlib/src/builtin/none.mojo +++ b/stdlib/src/builtin/none.mojo @@ -75,7 +75,7 @@ struct NoneType( return "None" @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Write `None` to a writer stream. Parameters: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 1eb57a4acb..21b345b89d 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -124,7 +124,7 @@ struct _RefCountedAttrsDict: ) @always_inline - fn set(inout self, key: StringLiteral, value: _ObjectImpl) raises: + fn set(mut self, key: StringLiteral, value: _ObjectImpl) raises: if key in self.impl[]: self.impl[][key].destroy() self.impl[][key] = value @@ -210,7 +210,7 @@ struct _Function(CollectionElement, CollectionElementNew): """The function pointer.""" @always_inline - fn __init__[FnT: AnyTrivialRegType](inout self, value: FnT): + fn __init__[FnT: AnyTrivialRegType](mut self, value: FnT): # FIXME: No "pointer bitcast" for signature function pointers. var f = UnsafePointer[Int16]() UnsafePointer.address_of(f).bitcast[FnT]()[] = value @@ -320,7 +320,7 @@ struct _ObjectImpl( self.value = Self.type(value) @always_inline - fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): + fn __init__[dt: DType](mut self, value: SIMD[dt, 1]): @parameter if dt.is_integral(): self.value = Self.type(value) @@ -517,7 +517,7 @@ struct _ObjectImpl( return self.get_as_int().cast[DType.float64]() @staticmethod - fn coerce_comparison_type(inout lhs: _ObjectImpl, inout rhs: _ObjectImpl): + fn coerce_comparison_type(mut lhs: _ObjectImpl, mut rhs: _ObjectImpl): """Coerces two values of arithmetic type to the appropriate lowest-common denominator type for performing comparisons, in order of increasing priority: bool, int, and then float. @@ -528,7 +528,7 @@ struct _ObjectImpl( return @parameter - fn convert(inout value: _ObjectImpl, id: Int, to: Int): + fn convert(mut value: _ObjectImpl, id: Int, to: Int): if to == Self.int: value = value.convert_bool_to_int() else: @@ -543,7 +543,7 @@ struct _ObjectImpl( convert(lhs, lhsId, rhsId) @staticmethod - fn coerce_arithmetic_type(inout lhs: _ObjectImpl, inout rhs: _ObjectImpl): + fn coerce_arithmetic_type(mut lhs: _ObjectImpl, mut rhs: _ObjectImpl): """Coerces two values of arithmetic type to the appropriate lowest-common denominator type for performing arithmetic operations. Bools are always converted to integers, to match Python's behavior. @@ -560,7 +560,7 @@ struct _ObjectImpl( lhs = lhs.convert_int_to_float() @staticmethod - fn coerce_integral_type(inout lhs: _ObjectImpl, inout rhs: _ObjectImpl): + fn coerce_integral_type(mut lhs: _ObjectImpl, mut rhs: _ObjectImpl): """Coerces two values of integral type to the appropriate lowest-common denominator type for performing bitwise operations. """ @@ -571,7 +571,7 @@ struct _ObjectImpl( else: lhs = lhs.convert_bool_to_int() - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Performs conversion to string according to Python semantics. """ @@ -785,7 +785,7 @@ struct object( self._value = value @always_inline - fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): + fn __init__[dt: DType](mut self, value: SIMD[dt, 1]): """Initializes the object with a generic scalar value. If the scalar value type is bool, it is converted to a boolean. Otherwise, it is converted to the appropriate integer or floating point type. @@ -842,7 +842,7 @@ struct object( self._value = impl @always_inline - fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): + fn __init__[*Ts: CollectionElement](mut self, value: ListLiteral[*Ts]): """Initializes the object from a list literal. Parameters: @@ -1003,7 +1003,7 @@ struct object( """ return self.__bool__() - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Performs conversion to string according to Python semantics. @@ -1537,7 +1537,7 @@ struct object( # ===------------------------------------------------------------------=== # @always_inline - fn __iadd__(inout self, rhs: object) raises: + fn __iadd__(mut self, rhs: object) raises: """In-place addition or concatenation operator. Args: @@ -1546,7 +1546,7 @@ struct object( self = self + rhs @always_inline - fn __isub__(inout self, rhs: object) raises: + fn __isub__(mut self, rhs: object) raises: """In-place subtraction operator. Args: @@ -1555,7 +1555,7 @@ struct object( self = self - rhs @always_inline - fn __imul__(inout self, rhs: object) raises: + fn __imul__(mut self, rhs: object) raises: """In-place multiplication operator. Args: @@ -1564,7 +1564,7 @@ struct object( self = self * rhs @always_inline - fn __ipow__(inout self, rhs: object) raises: + fn __ipow__(mut self, rhs: object) raises: """In-place exponentiation operator. Args: @@ -1573,7 +1573,7 @@ struct object( self = self**rhs @always_inline - fn __imod__(inout self, rhs: object) raises: + fn __imod__(mut self, rhs: object) raises: """In-place modulo operator. Args: @@ -1582,7 +1582,7 @@ struct object( self = self % rhs @always_inline - fn __itruediv__(inout self, rhs: object) raises: + fn __itruediv__(mut self, rhs: object) raises: """In-place true division operator. Args: @@ -1591,7 +1591,7 @@ struct object( self = self / rhs @always_inline - fn __ifloordiv__(inout self, rhs: object) raises: + fn __ifloordiv__(mut self, rhs: object) raises: """In-place floor division operator. Args: @@ -1600,7 +1600,7 @@ struct object( self = self // rhs @always_inline - fn __ilshift__(inout self, rhs: object) raises: + fn __ilshift__(mut self, rhs: object) raises: """In-place left shift operator. Args: @@ -1609,7 +1609,7 @@ struct object( self = self << rhs @always_inline - fn __irshift__(inout self, rhs: object) raises: + fn __irshift__(mut self, rhs: object) raises: """In-place right shift operator. Args: @@ -1618,7 +1618,7 @@ struct object( self = self >> rhs @always_inline - fn __iand__(inout self, rhs: object) raises: + fn __iand__(mut self, rhs: object) raises: """In-place AND operator. Args: @@ -1627,7 +1627,7 @@ struct object( self = self & rhs @always_inline - fn __ior__(inout self, rhs: object) raises: + fn __ior__(mut self, rhs: object) raises: """In-place OR operator. Args: @@ -1636,7 +1636,7 @@ struct object( self = self | rhs @always_inline - fn __ixor__(inout self, rhs: object) raises: + fn __ixor__(mut self, rhs: object) raises: """In-place XOR operator. Args: @@ -1936,7 +1936,7 @@ struct object( return self._value.get_obj_attr(key) @always_inline - fn __setattr__(inout self, key: StringLiteral, value: object) raises: + fn __setattr__(mut self, key: StringLiteral, value: object) raises: """Sets the named attribute. Args: diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 587b859e16..2af6fb942d 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -60,7 +60,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): return self @always_inline - fn __next__(inout self) -> Int: + fn __next__(mut self) -> Int: var curr = self.curr self.curr -= 1 return self.end - curr @@ -94,7 +94,7 @@ struct _SequentialRange(Sized, ReversibleRange, _IntIterable): return self @always_inline - fn __next__(inout self) -> Int: + fn __next__(mut self) -> Int: var start = self.start self.start += 1 return start @@ -134,7 +134,7 @@ struct _StridedRangeIterator(Sized): return 0 @always_inline - fn __next__(inout self) -> Int: + fn __next__(mut self) -> Int: var result = self.start self.start += self.step return result @@ -162,7 +162,7 @@ struct _StridedRange(Sized, ReversibleRange, _StridedIterable): return _StridedRangeIterator(self.start, self.end, self.step) @always_inline - fn __next__(inout self) -> Int: + fn __next__(mut self) -> Int: var result = self.start self.start += self.step return result @@ -337,7 +337,7 @@ struct _UIntZeroStartingRange(UIntSized): return self @always_inline - fn __next__(inout self) -> UInt: + fn __next__(mut self) -> UInt: var curr = self.curr self.curr -= 1 return self.end - curr @@ -368,7 +368,7 @@ struct _UIntStridedRangeIterator(UIntSized): return select(self.start < self.end, self.end - self.start, 0) @always_inline - fn __next__(inout self) -> UInt: + fn __next__(mut self) -> UInt: var result = self.start self.start += self.step return result @@ -406,7 +406,7 @@ struct _UIntStridedRange(UIntSized, _UIntStridedIterable): return _UIntStridedRangeIterator(self.start, self.end, self.step) @always_inline - fn __next__(inout self) -> UInt: + fn __next__(mut self) -> UInt: if self.start >= self.end: return self.end var result = self.start @@ -478,7 +478,7 @@ struct _ZeroStartingScalarRange[type: DType]: return self @always_inline - fn __next__(inout self) -> Scalar[type]: + fn __next__(mut self) -> Scalar[type]: var curr = self.curr self.curr -= 1 return self.end - curr @@ -515,7 +515,7 @@ struct _SequentialScalarRange[type: DType]: return self @always_inline - fn __next__(inout self) -> Scalar[type]: + fn __next__(mut self) -> Scalar[type]: var start = self.start self.start += 1 return start @@ -557,7 +557,7 @@ struct _StridedScalarRangeIterator[type: DType]: return self.end < self.start @always_inline - fn __next__(inout self) -> Scalar[type]: + fn __next__(mut self) -> Scalar[type]: var result = self.start self.start += self.step return result diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 332220f11f..a70d660217 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -354,7 +354,7 @@ struct SIMD[type: DType, size: Int]( @always_inline("nodebug") @implicit fn __init__( - inout self, + mut self, value: __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], ): """Initializes the SIMD vector with the underlying mlir value. @@ -588,7 +588,7 @@ struct SIMD[type: DType, size: Int]( fn __init__[ int_type: DType, // - ](inout self, *, from_bits: SIMD[int_type, size]): + ](mut self, *, from_bits: SIMD[int_type, size]): """Initializes the SIMD vector from the bits of an integral SIMD vector. Parameters: @@ -619,7 +619,7 @@ struct SIMD[type: DType, size: Int]( ](self.value, index(idx).value) @always_inline("nodebug") - fn __setitem__(inout self, idx: Int, val: Scalar[type]): + fn __setitem__(mut self, idx: Int, val: Scalar[type]): """Sets an element in the vector. Args: @@ -1076,7 +1076,7 @@ struct SIMD[type: DType, size: Int]( # ===------------------------------------------------------------------=== # @always_inline("nodebug") - fn __iadd__(inout self, rhs: Self): + fn __iadd__(mut self, rhs: Self): """Performs in-place addition. The vector is mutated where each element at position `i` is computed as @@ -1089,7 +1089,7 @@ struct SIMD[type: DType, size: Int]( self = self + rhs @always_inline("nodebug") - fn __isub__(inout self, rhs: Self): + fn __isub__(mut self, rhs: Self): """Performs in-place subtraction. The vector is mutated where each element at position `i` is computed as @@ -1102,7 +1102,7 @@ struct SIMD[type: DType, size: Int]( self = self - rhs @always_inline("nodebug") - fn __imul__(inout self, rhs: Self): + fn __imul__(mut self, rhs: Self): """Performs in-place multiplication. The vector is mutated where each element at position `i` is computed as @@ -1115,7 +1115,7 @@ struct SIMD[type: DType, size: Int]( self = self * rhs @always_inline("nodebug") - fn __itruediv__(inout self, rhs: Self): + fn __itruediv__(mut self, rhs: Self): """In-place true divide operator. The vector is mutated where each element at position `i` is computed as @@ -1128,7 +1128,7 @@ struct SIMD[type: DType, size: Int]( self = self / rhs @always_inline("nodebug") - fn __ifloordiv__(inout self, rhs: Self): + fn __ifloordiv__(mut self, rhs: Self): """In-place flood div operator. The vector is mutated where each element at position `i` is computed as @@ -1141,7 +1141,7 @@ struct SIMD[type: DType, size: Int]( self = self // rhs @always_inline("nodebug") - fn __imod__(inout self, rhs: Self): + fn __imod__(mut self, rhs: Self): """In-place mod operator. The vector is mutated where each element at position `i` is computed as @@ -1154,7 +1154,7 @@ struct SIMD[type: DType, size: Int]( self = self.__mod__(rhs) @always_inline("nodebug") - fn __ipow__(inout self, rhs: Int): + fn __ipow__(mut self, rhs: Int): """In-place pow operator. The vector is mutated where each element at position `i` is computed as @@ -1167,7 +1167,7 @@ struct SIMD[type: DType, size: Int]( self = self.__pow__(rhs) @always_inline("nodebug") - fn __iand__(inout self, rhs: Self): + fn __iand__(mut self, rhs: Self): """Computes `self & rhs` and save the result in `self`. Constraints: @@ -1183,7 +1183,7 @@ struct SIMD[type: DType, size: Int]( self = self & rhs @always_inline("nodebug") - fn __ixor__(inout self, rhs: Self): + fn __ixor__(mut self, rhs: Self): """Computes `self ^ rhs` and save the result in `self`. Constraints: @@ -1199,7 +1199,7 @@ struct SIMD[type: DType, size: Int]( self = self ^ rhs @always_inline("nodebug") - fn __ior__(inout self, rhs: Self): + fn __ior__(mut self, rhs: Self): """Computes `self | rhs` and save the result in `self`. Constraints: @@ -1215,7 +1215,7 @@ struct SIMD[type: DType, size: Int]( self = self | rhs @always_inline("nodebug") - fn __ilshift__(inout self, rhs: Self): + fn __ilshift__(mut self, rhs: Self): """Computes `self << rhs` and save the result in `self`. Constraints: @@ -1228,7 +1228,7 @@ struct SIMD[type: DType, size: Int]( self = self << rhs @always_inline("nodebug") - fn __irshift__(inout self, rhs: Self): + fn __irshift__(mut self, rhs: Self): """Computes `self >> rhs` and save the result in `self`. Constraints: @@ -1643,7 +1643,7 @@ struct SIMD[type: DType, size: Int]( """ return _hash_simd(self) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with this SIMD value. Parameters: @@ -1782,7 +1782,7 @@ struct SIMD[type: DType, size: Int]( ](self.value) @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this SIMD value to the provided Writer. @@ -3422,7 +3422,7 @@ fn _floor(x: SIMD) -> __type_of(x): fn _write_scalar[ dtype: DType, W: Writer, //, -](inout writer: W, value: Scalar[dtype]): +](mut writer: W, value: Scalar[dtype]): @parameter if dtype == DType.bool: if value: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 367047a85c..554ddeddfd 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -123,7 +123,7 @@ struct StringLiteral( return __mlir_op.`pop.string.concat`(self.value, rhs.value) @always_inline("nodebug") - fn __iadd__(inout self, rhs: StringLiteral): + fn __iadd__(mut self, rhs: StringLiteral): """Concatenate a string literal to an existing one. Can only be evaluated at compile time using the `alias` keyword, which will write the result into the binary. @@ -388,7 +388,7 @@ struct StringLiteral( """ return hash(self.unsafe_ptr(), len(self)) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with the underlying bytes. Parameters: @@ -551,7 +551,7 @@ struct StringLiteral( """ return _FormatCurlyEntry.format(self, args) - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this string literal to the provided Writer. diff --git a/stdlib/src/builtin/swap.mojo b/stdlib/src/builtin/swap.mojo index fed7502fd1..549e71e28b 100644 --- a/stdlib/src/builtin/swap.mojo +++ b/stdlib/src/builtin/swap.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. @always_inline -fn swap[T: Movable](inout lhs: T, inout rhs: T): +fn swap[T: Movable](mut lhs: T, mut rhs: T): """Swaps the two given arguments. Parameters: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 6d7f45a029..50fdf9f708 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -59,7 +59,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn __init__( - inout self, + mut self, *, owned storage: VariadicPack[_, CollectionElement, *element_types], ): diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index cb3de1bcb5..dad8ed6167 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -130,7 +130,7 @@ struct UInt(IntLike, _HashableWithHasher): return String.write(self) @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Formats this integer to the provided Writer. Parameters: @@ -168,7 +168,7 @@ struct UInt(IntLike, _HashableWithHasher): # TODO(MOCO-636): switch to DType.index return _hash_simd(Scalar[DType.uint64](self)) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with this uint value. Parameters: @@ -403,7 +403,7 @@ struct UInt(IntLike, _HashableWithHasher): # ===----------------------------------------------------------------------===# @always_inline("nodebug") - fn __iadd__(inout self, rhs: UInt): + fn __iadd__(mut self, rhs: UInt): """Compute `self + rhs` and save the result in self. Args: @@ -412,7 +412,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self + rhs @always_inline("nodebug") - fn __isub__(inout self, rhs: UInt): + fn __isub__(mut self, rhs: UInt): """Compute `self - rhs` and save the result in self. Args: @@ -421,7 +421,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self - rhs @always_inline("nodebug") - fn __imul__(inout self, rhs: UInt): + fn __imul__(mut self, rhs: UInt): """Compute self*rhs and save the result in self. Args: @@ -429,7 +429,7 @@ struct UInt(IntLike, _HashableWithHasher): """ self = self * rhs - fn __itruediv__(inout self, rhs: UInt): + fn __itruediv__(mut self, rhs: UInt): """Compute `self / rhs`, convert to int, and save the result in self. Since `floor(self / rhs)` is equivalent to `self // rhs`, this yields @@ -441,7 +441,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self // rhs @always_inline("nodebug") - fn __ifloordiv__(inout self, rhs: UInt): + fn __ifloordiv__(mut self, rhs: UInt): """Compute `self // rhs` and save the result in self. Args: @@ -449,7 +449,7 @@ struct UInt(IntLike, _HashableWithHasher): """ self = self // rhs - fn __imod__(inout self, rhs: UInt): + fn __imod__(mut self, rhs: UInt): """Compute `self % rhs` and save the result in self. Args: @@ -458,7 +458,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self % rhs @always_inline("nodebug") - fn __ipow__(inout self, rhs: UInt): + fn __ipow__(mut self, rhs: UInt): """Compute `pow(self, rhs)` and save the result in self. Args: @@ -467,7 +467,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self**rhs @always_inline("nodebug") - fn __ilshift__(inout self, rhs: UInt): + fn __ilshift__(mut self, rhs: UInt): """Compute `self << rhs` and save the result in self. Args: @@ -476,7 +476,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self << rhs @always_inline("nodebug") - fn __irshift__(inout self, rhs: UInt): + fn __irshift__(mut self, rhs: UInt): """Compute `self >> rhs` and save the result in self. Args: @@ -485,7 +485,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self >> rhs @always_inline("nodebug") - fn __iand__(inout self, rhs: UInt): + fn __iand__(mut self, rhs: UInt): """Compute `self & rhs` and save the result in self. Args: @@ -494,7 +494,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self & rhs @always_inline("nodebug") - fn __ixor__(inout self, rhs: UInt): + fn __ixor__(mut self, rhs: UInt): """Compute `self ^ rhs` and save the result in self. Args: @@ -503,7 +503,7 @@ struct UInt(IntLike, _HashableWithHasher): self = self ^ rhs @always_inline("nodebug") - fn __ior__(inout self, rhs: UInt): + fn __ior__(mut self, rhs: UInt): """Compute self|rhs and save the result in self. Args: diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index fab16a7b44..3dc291c522 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -109,7 +109,7 @@ trait ExplicitlyCopyable: initializer is called intentionally by the programmer. An explicit copy initializer is just a normal `__init__` method that takes - a `borrowed` argument of `Self`. + a `read-only` argument of `Self`. Example implementing the `ExplicitlyCopyable` trait on `Foo` which requires the `__init__(.., Self)` method: diff --git a/stdlib/src/collections/counter.mojo b/stdlib/src/collections/counter.mojo index a4a2ac1df3..55baedf606 100644 --- a/stdlib/src/collections/counter.mojo +++ b/stdlib/src/collections/counter.mojo @@ -114,7 +114,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self.get(key, 0) - fn __setitem__(inout self, value: V, count: Int): + fn __setitem__(mut self, value: V, count: Int): """Set a value in the keyword Counter by key. Args: @@ -276,7 +276,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): return +result^ # Remove zero and negative counts - fn __iadd__(inout self, other: Self): + fn __iadd__(mut self, other: Self): """Add counts from another Counter to this Counter. Args: @@ -301,7 +301,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): return +result^ # Remove zero and negative counts - fn __isub__(inout self, other: Self): + fn __isub__(mut self, other: Self): """Subtract counts from another Counter from this Counter. Args: @@ -329,7 +329,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): return result^ - fn __iand__(inout self, other: Self): + fn __iand__(mut self, other: Self): """Intersection: keep common elements with the minimum count. Args: @@ -370,7 +370,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): return result^ - fn __ior__(inout self, other: Self): + fn __ior__(mut self, other: Self): """Union: keep all elements with the maximum count. Args: @@ -382,7 +382,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): if newcount > 0: self[key] = newcount - fn _keep_positive(inout self): + fn _keep_positive(mut self): """Remove zero and negative counts from the Counter.""" for key_ref in self.keys(): var key = key_ref[] @@ -451,7 +451,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.get(value, default) - fn pop(inout self, value: V) raises -> Int: + fn pop(mut self, value: V) raises -> Int: """Remove a value from the Counter by value. Args: @@ -465,7 +465,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.pop(value) - fn pop(inout self, value: V, owned default: Int) raises -> Int: + fn pop(mut self, value: V, owned default: Int) raises -> Int: """Remove a value from the Counter by value. Args: @@ -507,11 +507,11 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): """ return self._data.items() - fn clear(inout self): + fn clear(mut self): """Remove all elements from the Counter.""" self._data.clear() - fn popitem(inout self) raises -> CountTuple[V]: + fn popitem(mut self) raises -> CountTuple[V]: """Remove and return an arbitrary (key, value) pair from the Counter. Returns: @@ -573,7 +573,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): elements.append(item.key) return elements - fn update(inout self, other: Self): + fn update(mut self, other: Self): """Update the Counter, like `dict.update()` but add counts instead of replacing them. @@ -584,7 +584,7 @@ struct Counter[V: KeyElement](Sized, CollectionElement, Boolable): var item = item_ref[] self._data[item.key] = self._data.get(item.key, 0) + item.value - fn subtract(inout self, other: Self): + fn subtract(mut self, other: Self): """Subtract count. Both inputs and outputs may be zero or negative. Args: diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index 37ec9d97bf..cdaf52332d 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -142,7 +142,7 @@ struct Deque[ElementType: CollectionElement]( self = Self(variadic_list=values^) fn __init__( - inout self, *, owned variadic_list: VariadicListMem[ElementType, _] + mut self, *, owned variadic_list: VariadicListMem[ElementType, _] ): """Constructs a deque from the given values. @@ -226,7 +226,7 @@ struct Deque[ElementType: CollectionElement]( new.append(element[]) return new^ - fn __iadd__(inout self, other: Self): + fn __iadd__(mut self, other: Self): """Appends the elements of other deque into self. Args: @@ -257,7 +257,7 @@ struct Deque[ElementType: CollectionElement]( new.append(element[]) return new^ - fn __imul__(inout self, n: Int): + fn __imul__(mut self, n: Int): """Concatenates self `n` times in place. Args: @@ -410,7 +410,7 @@ struct Deque[ElementType: CollectionElement]( fn write_to[ RepresentableElementType: RepresentableCollectionElement, WriterType: Writer, //, - ](self: Deque[RepresentableElementType], inout writer: WriterType): + ](self: Deque[RepresentableElementType], mut writer: WriterType): """Writes `my_deque.__str__()` to a `Writer`. Parameters: @@ -491,7 +491,7 @@ struct Deque[ElementType: CollectionElement]( # Methods # ===-------------------------------------------------------------------===# - fn append(inout self, owned value: ElementType): + fn append(mut self, owned value: ElementType): """Appends a value to the right side of the deque. Args: @@ -508,7 +508,7 @@ struct Deque[ElementType: CollectionElement]( if self._head == self._tail: self._realloc(self._capacity << 1) - fn appendleft(inout self, owned value: ElementType): + fn appendleft(mut self, owned value: ElementType): """Appends a value to the left side of the deque. Args: @@ -525,7 +525,7 @@ struct Deque[ElementType: CollectionElement]( if self._head == self._tail: self._realloc(self._capacity << 1) - fn clear(inout self): + fn clear(mut self): """Removes all elements from the deque leaving it with length 0. Resets the underlying storage capacity to `_min_capacity`. @@ -561,7 +561,7 @@ struct Deque[ElementType: CollectionElement]( count += 1 return count - fn extend(inout self, owned values: List[ElementType]): + fn extend(mut self, owned values: List[ElementType]): """Extends the right side of the deque by consuming elements of the list argument. Args: @@ -593,7 +593,7 @@ struct Deque[ElementType: CollectionElement]( (src + i).move_pointee_into(self._data + self._tail) self._tail = self._physical_index(self._tail + 1) - fn extendleft(inout self, owned values: List[ElementType]): + fn extendleft(mut self, owned values: List[ElementType]): """Extends the left side of the deque by consuming elements from the list argument. Acts as series of left appends resulting in reversed order of elements in the list argument. @@ -676,7 +676,7 @@ struct Deque[ElementType: CollectionElement]( return idx raise "ValueError: Given element is not in deque" - fn insert(inout self, idx: Int, owned value: ElementType) raises: + fn insert(mut self, idx: Int, owned value: ElementType) raises: """Inserts the `value` into the deque at position `idx`. Args: @@ -723,10 +723,7 @@ struct Deque[ElementType: CollectionElement]( fn remove[ EqualityElementType: EqualityComparableCollectionElement, // - ]( - inout self: Deque[EqualityElementType], - value: EqualityElementType, - ) raises: + ](mut self: Deque[EqualityElementType], value: EqualityElementType,) raises: """Removes the first occurrence of the `value`. Parameters: @@ -797,7 +794,7 @@ struct Deque[ElementType: CollectionElement]( return (self._data + self._head)[] - fn pop(inout self) raises -> ElementType as element: + fn pop(mut self) raises -> ElementType as element: """Removes and returns the element from the right side of the deque. Returns: @@ -821,7 +818,7 @@ struct Deque[ElementType: CollectionElement]( return - fn popleft(inout self) raises -> ElementType as element: + fn popleft(mut self) raises -> ElementType as element: """Removes and returns the element from the left side of the deque. Returns: @@ -845,7 +842,7 @@ struct Deque[ElementType: CollectionElement]( return - fn reverse(inout self): + fn reverse(mut self): """Reverses the elements of the deque in-place.""" last = self._head + len(self) - 1 for i in range(len(self) // 2): @@ -855,7 +852,7 @@ struct Deque[ElementType: CollectionElement]( (self._data + src).move_pointee_into(self._data + dst) (self._data + src).init_pointee_move(tmp^) - fn rotate(inout self, n: Int = 1): + fn rotate(mut self, n: Int = 1): """Rotates the deque by `n` steps. If `n` is positive, rotates to the right. @@ -932,7 +929,7 @@ struct Deque[ElementType: CollectionElement]( """ return logical_index & (self._capacity - 1) - fn _prepare_for_new_elements(inout self, n_total: Int, n_retain: Int): + fn _prepare_for_new_elements(mut self, n_total: Int, n_retain: Int): """Prepares the deque’s internal buffer for adding new elements by reallocating memory and retaining the specified number of existing elements. @@ -958,7 +955,7 @@ struct Deque[ElementType: CollectionElement]( self._head = 0 self._tail = n_retain - fn _realloc(inout self, new_capacity: Int): + fn _realloc(mut self, new_capacity: Int): """Relocates data to a new storage buffer. Args: @@ -1018,7 +1015,7 @@ struct _DequeIter[ fn __iter__(self) -> Self: return self - fn __next__(inout self) -> Pointer[ElementType, deque_lifetime]: + fn __next__(mut self) -> Pointer[ElementType, deque_lifetime]: @parameter if forward: self.index += 1 diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 0e545e8a87..d35229a17e 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -79,7 +79,7 @@ struct _DictEntryIter[ var src: Pointer[Dict[K, V], dict_origin] fn __init__( - inout self, index: Int, seen: Int, ref [dict_origin]dict: Dict[K, V] + mut self, index: Int, seen: Int, ref [dict_origin]dict: Dict[K, V] ): self.index = index self.seen = seen @@ -90,7 +90,7 @@ struct _DictEntryIter[ @always_inline fn __next__( - inout self, + mut self, ) -> Pointer[DictEntry[K, V], __origin_of(self.src[]._entries[0].value())]: while True: var opt_entry_ref = Pointer.address_of( @@ -141,7 +141,7 @@ struct _DictKeyIter[ return self fn __next__( - inout self, + mut self, ) -> Pointer[K, __origin_of(self.iter.__next__()[].key)]: return Pointer.address_of(self.iter.__next__()[].key) @@ -187,7 +187,7 @@ struct _DictValueIter[ ) ) - fn __next__(inout self) -> Self.ref_type: + fn __next__(mut self) -> Self.ref_type: var entry_ref = self.iter.__next__() # Cast through a pointer to grant additional mutability because # _DictEntryIter.next erases it. @@ -331,7 +331,7 @@ struct _DictIndex: var data = self.data.bitcast[Int64]() return int(data.load(slot & (reserved - 1))) - fn set_index(inout self, reserved: Int, slot: Int, value: Int): + fn set_index(mut self, reserved: Int, slot: Int, value: Int): if reserved <= 128: var data = self.data.bitcast[Int8]() return data.store(slot & (reserved - 1), value) @@ -599,7 +599,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return self._find_ref(key) - fn __setitem__(inout self, owned key: K, owned value: V): + fn __setitem__(mut self, owned key: K, owned value: V): """Set a value in the dictionary by key. Args: @@ -650,7 +650,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( result.update(other) return result^ - fn __ior__(inout self, other: Self): + fn __ior__(mut self, other: Self): """Merge self with other in place. Args: @@ -805,7 +805,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return self.find(key).or_else(default) - fn pop(inout self, key: K, owned default: V) -> V: + fn pop(mut self, key: K, owned default: V) -> V: """Remove a value from the dictionary by key. Args: @@ -822,7 +822,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( except: return default - fn pop(inout self, key: K) raises -> V: + fn pop(mut self, key: K) raises -> V: """Remove a value from the dictionary by key. Args: @@ -850,7 +850,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( return entry_value^.reap_value() raise "KeyError" - fn popitem(inout self) raises -> DictEntry[K, V]: + fn popitem(mut self) raises -> DictEntry[K, V]: """Remove and return a (key, value) pair from the dictionary. Pairs are returned in LIFO order. popitem() is useful to destructively iterate over a dictionary, as often used in set algorithms. If the dictionary is empty, calling popitem() raises a KeyError. @@ -916,7 +916,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return _DictEntryIter(0, 0, self) - fn update(inout self, other: Self, /): + fn update(mut self, other: Self, /): """Update the dictionary with the key/value pairs from other, overwriting existing keys. The argument must be positional only. @@ -926,7 +926,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( for entry in other.items(): self[entry[].key] = entry[].value - fn clear(inout self): + fn clear(mut self): """Remove all elements from the dictionary.""" self.size = 0 self._n_entries = 0 @@ -934,7 +934,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._index = _DictIndex(self._reserved()) fn setdefault( - inout self, key: K, owned default: V + mut self, key: K, owned default: V ) raises -> ref [self._find_ref(key)] V: """Get a value from the dictionary by key, or set it to a default if it doesn't exist. @@ -960,12 +960,12 @@ struct Dict[K: KeyElement, V: CollectionElement]( entries.append(None) return entries - fn _insert(inout self, owned key: K, owned value: V): + fn _insert(mut self, owned key: K, owned value: V): self._insert(DictEntry[K, V](key^, value^)) fn _insert[ safe_context: Bool = False - ](inout self, owned entry: DictEntry[K, V]): + ](mut self, owned entry: DictEntry[K, V]): @parameter if not safe_context: self._maybe_resize() @@ -983,10 +983,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _get_index(self, slot: Int) -> Int: return self._index.get_index(self._reserved(), slot) - fn _set_index(inout self, slot: Int, index: Int): + fn _set_index(mut self, slot: Int, index: Int): return self._index.set_index(self._reserved(), slot, index) - fn _next_index_slot(self, inout slot: Int, inout perturb: UInt64): + fn _next_index_slot(self, mut slot: Int, mut perturb: UInt64): alias PERTURB_SHIFT = 5 perturb >>= PERTURB_SHIFT slot = ((5 * slot) + int(perturb + 1)) & (self._reserved() - 1) @@ -1023,7 +1023,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( fn _over_compact_factor(self) -> Bool: return 4 * self._n_entries > 3 * self._reserved() - fn _maybe_resize(inout self): + fn _maybe_resize(mut self): if not self._over_load_factor(): if self._over_compact_factor(): self._compact() @@ -1040,7 +1040,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( if entry: self._insert[safe_context=True](entry.unsafe_take()) - fn _compact(inout self): + fn _compact(mut self): self._index = _DictIndex(self._reserved()) var right = 0 for left in range(self.size): @@ -1129,7 +1129,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict[key] @always_inline - fn __setitem__(inout self, key: Self.key_type, value: V): + fn __setitem__(mut self, key: Self.key_type, value: V): """Set a value in the keyword dictionary by key. Args: @@ -1182,7 +1182,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict.find(key) @always_inline - fn pop(inout self, key: self.key_type, owned default: V) -> V: + fn pop(mut self, key: self.key_type, owned default: V) -> V: """Remove a value from the dictionary by key. Args: @@ -1197,7 +1197,7 @@ struct OwnedKwargsDict[V: CollectionElement]( return self._dict.pop(key, default^) @always_inline - fn pop(inout self, key: self.key_type) raises -> V: + fn pop(mut self, key: self.key_type) raises -> V: """Remove a value from the dictionary by key. Args: @@ -1270,9 +1270,9 @@ struct OwnedKwargsDict[V: CollectionElement]( return _DictEntryIter(0, 0, self._dict) @always_inline - fn _insert(inout self, owned key: Self.key_type, owned value: V): + fn _insert(mut self, owned key: Self.key_type, owned value: V): self._dict._insert(key^, value^) @always_inline - fn _insert(inout self, key: StringLiteral, owned value: V): + fn _insert(mut self, key: StringLiteral, owned value: V): self._insert(String(key), value^) diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 9112e51818..bb8809612d 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -112,7 +112,7 @@ struct InlineArray[ ]() fn __init__( - inout self, + mut self, *, owned unsafe_assume_initialized: InlineArray[ UnsafeMaybeUninitialized[Self.ElementType], Self.size @@ -171,7 +171,7 @@ struct InlineArray[ @always_inline fn __init__( - inout self, + mut self, *, owned storage: VariadicListMem[Self.ElementType, _], ): diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 01486e07ed..516be41287 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -54,7 +54,7 @@ struct _InlineListIter[ return self fn __next__( - inout self, + mut self, ) -> Pointer[T, __origin_of(self.src[][0])]: @parameter if forward: @@ -243,7 +243,7 @@ struct InlineList[ElementType: CollectionElementNew, capacity: Int = 16](Sized): count += int(rebind[C](e[]) == value) return count - fn append(inout self, owned value: ElementType): + fn append(mut self, owned value: ElementType): """Appends a value to the list. Args: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b3442b1228..19b477a47e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -63,7 +63,7 @@ struct _ListIter[ return self fn __next__( - inout self, + mut self, ) -> Pointer[T, list_origin]: @parameter if forward: @@ -178,9 +178,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( for value in span: self.append(value[]) - fn __init__( - inout self, *, ptr: UnsafePointer[T], length: Int, capacity: Int - ): + fn __init__(mut self, *, ptr: UnsafePointer[T], length: Int, capacity: Int): """Constructs a list from a pointer, its length, and its capacity. Args: @@ -320,7 +318,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( result.__mul(x) return result^ - fn __imul__(inout self, x: Int): + fn __imul__(mut self, x: Int): """Multiplies the list by x in place. Args: @@ -341,7 +339,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( result.extend(other^) return result^ - fn __iadd__(inout self, owned other: Self): + fn __iadd__(mut self, owned other: Self): """Appends the elements of other into self. Args: @@ -420,7 +418,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( @no_inline fn write_to[ W: Writer, U: RepresentableCollectionElement, // - ](self: List[U, *_], inout writer: W): + ](self: List[U, *_], mut writer: W): """Write `my_list.__str__()` to a `Writer`. Parameters: @@ -477,7 +475,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( """ return len(self) * sizeof[T]() - fn _realloc(inout self, new_capacity: Int): + fn _realloc(mut self, new_capacity: Int): var new_data = UnsafePointer[T].alloc(new_capacity) _move_pointee_into_many_elements[hint_trivial_type]( @@ -491,7 +489,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.data = new_data self.capacity = new_capacity - fn append(inout self, owned value: T): + fn append(mut self, owned value: T): """Appends a value to this list. Args: @@ -502,7 +500,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( (self.data + self.size).init_pointee_move(value^) self.size += 1 - fn insert(inout self, i: Int, owned value: T): + fn insert(mut self, i: Int, owned value: T): """Inserts a value to the list at the given index. `a.insert(len(a), value)` is equivalent to `a.append(value)`. @@ -531,7 +529,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( earlier_idx -= 1 later_idx -= 1 - fn __mul(inout self, x: Int): + fn __mul(mut self, x: Int): """Appends the original elements of this list x-1 times. ```mojo @@ -550,7 +548,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( for i in range(x - 1): self.extend(orig) - fn extend(inout self, owned other: List[T, *_]): + fn extend(mut self, owned other: List[T, *_]): """Extends this list by consuming the elements of `other`. Args: @@ -590,7 +588,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( # list. self.size = final_size - fn pop(inout self, i: Int = -1) -> T: + fn pop(mut self, i: Int = -1) -> T: """Pops a value from the list at the given index. Args: @@ -614,7 +612,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self._realloc(self.capacity // 2) return ret_val^ - fn reserve(inout self, new_capacity: Int): + fn reserve(mut self, new_capacity: Int): """Reserves the requested capacity. If the current capacity is greater or equal, this is a no-op. @@ -627,7 +625,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return self._realloc(new_capacity) - fn resize(inout self, new_size: Int, value: T): + fn resize(mut self, new_size: Int, value: T): """Resizes the list to the given new size. If the new size is smaller than the current one, elements at the end @@ -646,7 +644,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( (self.data + i).init_pointee_copy(value) self.size = new_size - fn resize(inout self, new_size: Int): + fn resize(mut self, new_size: Int): """Resizes the list to the given new size. With no new value provided, the new size must be smaller than or equal @@ -667,7 +665,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( self.size = new_size self.reserve(new_size) - fn reverse(inout self): + fn reverse(mut self): """Reverses the elements of the list.""" var earlier_idx = 0 @@ -744,13 +742,13 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return i raise "ValueError: Given element is not in list" - fn clear(inout self): + fn clear(mut self): """Clears the elements in the list.""" for i in range(self.size): (self.data + i).destroy_pointee() self.size = 0 - fn steal_data(inout self) -> UnsafePointer[T]: + fn steal_data(mut self) -> UnsafePointer[T]: """Take ownership of the underlying pointer from the list. Returns: @@ -840,7 +838,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( return (self.data + idx)[] @always_inline - fn unsafe_set(inout self, idx: Int, owned value: T): + fn unsafe_set(mut self, idx: Int, owned value: T): """Write a value to a given location without checking index bounds. Users should consider using `my_list[idx] = value` instead of this method as it @@ -897,7 +895,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( count += 1 return count - fn swap_elements(inout self, elt_idx_1: Int, elt_idx_2: Int): + fn swap_elements(mut self, elt_idx_1: Int, elt_idx_2: Int): """Swaps elements at the specified indexes if they are different. ```mojo diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index eb019dd463..95c78a9706 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -281,7 +281,7 @@ struct Optional[T: CollectionElement]( fn write_to[ W: Writer, U: RepresentableCollectionElement, // - ](self: Optional[U], inout writer: W): + ](self: Optional[U], mut writer: W): """Write Optional string representation to a `Writer`. Parameters: @@ -333,7 +333,7 @@ struct Optional[T: CollectionElement]( debug_assert(self.__bool__(), ".value() on empty Optional") return self._value.unsafe_get[T]() - fn take(inout self) -> T: + fn take(mut self) -> T: """Move the value out of the Optional. The caller takes ownership over the new value, which is moved @@ -351,7 +351,7 @@ struct Optional[T: CollectionElement]( abort(".take() on empty Optional") return self.unsafe_take() - fn unsafe_take(inout self) -> T: + fn unsafe_take(mut self) -> T: """Unsafely move the value out of the Optional. The caller takes ownership over the new value, which is moved diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 500e4c73b2..9c017dbfe8 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -149,7 +149,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return self.intersection(other) - fn __iand__(inout self, other: Self): + fn __iand__(mut self, other: Self): """In-place set intersection. Updates the set to contain only the elements which are already in @@ -172,7 +172,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return self.union(other) - fn __ior__(inout self, other: Self): + fn __ior__(mut self, other: Self): """In-place set union. Updates the set to contain all elements in the `other` set @@ -195,7 +195,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return self.difference(other) - fn __isub__(inout self, other: Self): + fn __isub__(mut self, other: Self): """In-place set subtraction. Updates the set to remove any elements from the `other` set. @@ -260,7 +260,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ return self.symmetric_difference(other) - fn __ixor__(inout self, other: Self): + fn __ixor__(mut self, other: Self): """Overloads the ^= operator. Works like as `symmetric_difference_update` method. Updates the set with the symmetric difference of itself and another set. @@ -334,7 +334,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): fn write_to[ W: Writer, U: RepresentableKeyElement, - ](self: Set[U], inout writer: W): + ](self: Set[U], mut writer: W): """Write Set string representation to a `Writer`. Parameters: @@ -366,7 +366,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # here we rely on Set being a trivial wrapper of a Dict return _DictKeyIter(_DictEntryIter(0, 0, self._data)) - fn add(inout self, t: T): + fn add(mut self, t: T): """Add an element to the set. Args: @@ -374,7 +374,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ self._data[t] = None - fn remove(inout self, t: T) raises: + fn remove(mut self, t: T) raises: """Remove an element from the set. Args: @@ -385,7 +385,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ self._data.pop(t) - fn pop(inout self) raises -> T: + fn pop(mut self) raises -> T: """Remove any one item from the set, and return it. As an implementation detail this will remove the first item @@ -454,7 +454,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): result.add(e[]) return result^ - fn update(inout self, other: Self): + fn update(mut self, other: Self): """In-place set update. Updates the set to contain all elements in the `other` set @@ -466,7 +466,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): for e in other: self.add(e[]) - fn intersection_update(inout self, other: Self): + fn intersection_update(mut self, other: Self): """In-place set intersection update. Updates the set by retaining only elements found in both this set and the `other` set, @@ -479,7 +479,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): # careful about concurrent iteration + mutation self.difference_update(self - other) - fn difference_update(inout self, other: Self): + fn difference_update(mut self, other: Self): """In-place set subtraction. Updates the set by removing all elements found in the `other` set, @@ -566,7 +566,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): return result^ - fn symmetric_difference_update(inout self, other: Self): + fn symmetric_difference_update(mut self, other: Self): """Updates the set with the symmetric difference of itself and another set. Args: @@ -574,7 +574,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """ self = self.symmetric_difference(other) - fn discard(inout self, value: T): + fn discard(mut self, value: T): """Remove a value from the set if it exists. Pass otherwise. Args: @@ -585,7 +585,7 @@ struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): except: pass - fn clear(inout self): + fn clear(mut self): """Removes all elements from the set. This method modifies the set in-place, removing all of its elements. diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 1b38236b10..d75d2098cd 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -904,7 +904,7 @@ struct String( # Factory dunders # ===------------------------------------------------------------------=== # - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): """Write a byte span to this String. Args: @@ -913,7 +913,7 @@ struct String( """ self._iadd[False](bytes) - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. Parameters: @@ -1267,7 +1267,7 @@ struct String( """ return Self._add[True](other.as_bytes(), self.as_bytes()) - fn _iadd[has_null: Bool](inout self, other: Span[Byte]): + fn _iadd[has_null: Bool](mut self, other: Span[Byte]): var s_len = self.byte_length() var o_len = len(other) var o_ptr = other.unsafe_ptr() @@ -1288,7 +1288,7 @@ struct String( s_ptr[sum_len] = 0 @always_inline - fn __iadd__(inout self, other: String): + fn __iadd__(mut self, other: String): """Appends another string to this string. Args: @@ -1297,7 +1297,7 @@ struct String( self._iadd[True](other.as_bytes()) @always_inline - fn __iadd__(inout self, other: StringLiteral): + fn __iadd__(mut self, other: StringLiteral): """Appends another string literal to this string. Args: @@ -1306,7 +1306,7 @@ struct String( self._iadd[False](other.as_bytes()) @always_inline - fn __iadd__(inout self, other: StringSlice): + fn __iadd__(mut self, other: StringSlice): """Appends another string slice to this string. Args: @@ -1423,7 +1423,7 @@ struct String( # Methods # ===------------------------------------------------------------------=== # - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this string to the provided Writer. @@ -1622,7 +1622,7 @@ struct String( var length = len(self._buffer) return length - int(length > 0) - fn _steal_ptr(inout self) -> UnsafePointer[UInt8]: + fn _steal_ptr(mut self) -> UnsafePointer[UInt8]: """Transfer ownership of pointer to the underlying memory. The caller is responsible for freeing up the memory. @@ -2014,7 +2014,7 @@ struct String( """ return hash(self.as_string_slice()) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with the underlying bytes. Parameters: @@ -2309,7 +2309,7 @@ struct String( var result = String(buffer) return result^ - fn reserve(inout self, new_capacity: Int): + fn reserve(mut self, new_capacity: Int): """Reserves the requested capacity. Args: diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 1dc60fcc76..1c9385c528 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -42,7 +42,7 @@ struct _VecIter[ var size: Int var vec: UnsafePointer[vec_type] - fn __next__(inout self) -> type: + fn __next__(mut self) -> type: self.i += 1 return deref(self.vec, self.i - 1) @@ -175,7 +175,7 @@ struct InlinedFixedVector[ self.dynamic_data = UnsafePointer[type]() @always_inline - fn append(inout self, value: type): + fn append(mut self, value: type): """Appends a value to this vector. Args: @@ -225,7 +225,7 @@ struct InlinedFixedVector[ return self.dynamic_data[normalized_idx - Self.static_size] @always_inline - fn __setitem__(inout self, idx: Int, value: type): + fn __setitem__(mut self, idx: Int, value: type): """Sets a vector element at the given index. Args: @@ -245,7 +245,7 @@ struct InlinedFixedVector[ else: self.dynamic_data[normalized_idx - Self.static_size] = value - fn clear(inout self): + fn clear(mut self): """Clears the elements in the vector.""" self.current_size = 0 @@ -255,7 +255,7 @@ struct InlinedFixedVector[ alias _iterator = _VecIter[type, Self, Self._deref_iter_impl] - fn __iter__(inout self) -> Self._iterator: + fn __iter__(mut self) -> Self._iterator: """Iterate over the vector. Returns: diff --git a/stdlib/src/hashlib/_ahash.mojo b/stdlib/src/hashlib/_ahash.mojo index 9adb7d08e0..56488c9a8d 100644 --- a/stdlib/src/hashlib/_ahash.mojo +++ b/stdlib/src/hashlib/_ahash.mojo @@ -110,7 +110,7 @@ struct AHasher[key: U256](_Hasher): self.extra_keys = U128(pi_key[2], pi_key[3]) @always_inline - fn _update(inout self, new_data: UInt64): + fn _update(mut self, new_data: UInt64): """Update the buffer value with new data. Args: @@ -119,7 +119,7 @@ struct AHasher[key: U256](_Hasher): self.buffer = _folded_multiply(new_data ^ self.buffer, MULTIPLE) @always_inline - fn _large_update(inout self, new_data: U128): + fn _large_update(mut self, new_data: U128): """Update the buffer value with new data. Args: @@ -129,7 +129,7 @@ struct AHasher[key: U256](_Hasher): var combined = _folded_multiply(xored[0], xored[1]) self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined) - fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): + fn _update_with_bytes(mut self, data: UnsafePointer[UInt8], length: Int): """Consume provided data to update the internal buffer. Args: @@ -160,7 +160,7 @@ struct AHasher[key: U256](_Hasher): var value = _read_small(data, length) self._large_update(value) - fn _update_with_simd(inout self, new_data: SIMD[_, _]): + fn _update_with_simd(mut self, new_data: SIMD[_, _]): """Update the buffer value with new data. Args: @@ -182,7 +182,7 @@ struct AHasher[key: U256](_Hasher): for i in range(0, v64.size, 2): self._large_update(U128(v64[i], v64[i + 1])) - fn update[T: _HashableWithHasher](inout self, value: T): + fn update[T: _HashableWithHasher](mut self, value: T): """Update the buffer value with new hashable value. Args: diff --git a/stdlib/src/hashlib/_hasher.mojo b/stdlib/src/hashlib/_hasher.mojo index e29257a9e0..f30e4de21a 100644 --- a/stdlib/src/hashlib/_hasher.mojo +++ b/stdlib/src/hashlib/_hasher.mojo @@ -17,7 +17,7 @@ from ._ahash import AHasher trait _HashableWithHasher: - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): ... @@ -25,13 +25,13 @@ trait _Hasher: fn __init__(out self): ... - fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): + fn _update_with_bytes(mut self, data: UnsafePointer[UInt8], length: Int): ... - fn _update_with_simd(inout self, value: SIMD[_, _]): + fn _update_with_simd(mut self, value: SIMD[_, _]): ... - fn update[T: _HashableWithHasher](inout self, value: T): + fn update[T: _HashableWithHasher](mut self, value: T): ... fn finish(owned self) -> UInt64: diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 98ff2cca07..0c06a6b00a 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1108,7 +1108,7 @@ fn iota[ buff.store(i, i + offset) -fn iota[type: DType, //](inout v: List[Scalar[type], *_], offset: Int = 0): +fn iota[type: DType, //](mut v: List[Scalar[type], *_], offset: Int = 0): """Fill a list with consecutive numbers starting from the specified offset. Parameters: @@ -1121,7 +1121,7 @@ fn iota[type: DType, //](inout v: List[Scalar[type], *_], offset: Int = 0): iota(v.data, len(v), offset) -fn iota(inout v: List[Int, *_], offset: Int = 0): +fn iota(mut v: List[Int, *_], offset: Int = 0): """Fill a list with consecutive numbers starting from the specified offset. Args: diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index 37c03786f7..d7b236af72 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -60,11 +60,11 @@ struct _ArcPointerInner[T: Movable]: self.refcount = Scalar[DType.uint64](1) self.payload = value^ - fn add_ref(inout self): + fn add_ref(mut self): """Atomically increment the refcount.""" _ = self.refcount.fetch_add(1) - fn drop_ref(inout self) -> Bool: + fn drop_ref(mut self) -> Bool: """Atomically decrement the refcount and return true if the result hits zero.""" return self.refcount.fetch_sub(1) == 1 diff --git a/stdlib/src/memory/maybe_uninitialized.mojo b/stdlib/src/memory/maybe_uninitialized.mojo index 20901a6fff..8d224bdd88 100644 --- a/stdlib/src/memory/maybe_uninitialized.mojo +++ b/stdlib/src/memory/maybe_uninitialized.mojo @@ -62,7 +62,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): fn __init__[ MovableType: Movable ]( - inout self: UnsafeMaybeUninitialized[MovableType], + mut self: UnsafeMaybeUninitialized[MovableType], owned value: MovableType, ): """The memory is now considered initialized. @@ -98,7 +98,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): fn copy_from[ CopyableType: ExplicitlyCopyable ]( - inout self: UnsafeMaybeUninitialized[CopyableType], + mut self: UnsafeMaybeUninitialized[CopyableType], other: UnsafeMaybeUninitialized[CopyableType], ): """Copy another object. @@ -117,7 +117,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): @always_inline fn copy_from[ CopyableType: ExplicitlyCopyable - ](inout self: UnsafeMaybeUninitialized[CopyableType], other: CopyableType): + ](mut self: UnsafeMaybeUninitialized[CopyableType], other: CopyableType): """Copy another object. This function assumes that the current memory is uninitialized. @@ -152,8 +152,8 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): fn move_from[ MovableType: Movable ]( - inout self: UnsafeMaybeUninitialized[MovableType], - inout other: UnsafeMaybeUninitialized[MovableType], + mut self: UnsafeMaybeUninitialized[MovableType], + mut other: UnsafeMaybeUninitialized[MovableType], ): """Move another object. @@ -174,7 +174,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): fn move_from[ MovableType: Movable ]( - inout self: UnsafeMaybeUninitialized[MovableType], + mut self: UnsafeMaybeUninitialized[MovableType], other: UnsafePointer[MovableType], ): """Move another object. @@ -196,7 +196,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): fn write[ MovableType: Movable ]( - inout self: UnsafeMaybeUninitialized[MovableType], + mut self: UnsafeMaybeUninitialized[MovableType], owned value: MovableType, ): """Write a value into an uninitialized memory location. @@ -235,7 +235,7 @@ struct UnsafeMaybeUninitialized[ElementType: AnyType](CollectionElementNew): return UnsafePointer.address_of(self._array).bitcast[Self.ElementType]() @always_inline - fn assume_initialized_destroy(inout self): + fn assume_initialized_destroy(mut self): """Runs the destructor of the internal value. Calling this method assumes that the memory is initialized. diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 4b22f14066..3974daa99f 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -31,7 +31,7 @@ struct OwnedPointer[T: AnyType]: # Life cycle methods # ===-------------------------------------------------------------------===# - fn __init__[T: Movable](inout self: OwnedPointer[T], owned value: T): + fn __init__[T: Movable](mut self: OwnedPointer[T], owned value: T): """Construct a new OwnedPointer[] by moving the passed value into a new backing allocation. Parameters: @@ -45,7 +45,7 @@ struct OwnedPointer[T: AnyType]: fn __init__[ T: ExplicitlyCopyable - ](inout self: OwnedPointer[T], *, copy_value: T): + ](mut self: OwnedPointer[T], *, copy_value: T): """Construct a new OwnedPointer[] by explicitly copying the passed value into a new backing allocation. Parameters: @@ -59,7 +59,7 @@ struct OwnedPointer[T: AnyType]: fn __init__[ T: Copyable, U: NoneType = None - ](inout self: OwnedPointer[T], value: T): + ](mut self: OwnedPointer[T], value: T): """Construct a new OwnedPointer[] by copying the passed value into a new backing allocation. Parameters: @@ -74,7 +74,7 @@ struct OwnedPointer[T: AnyType]: fn __init__[ T: ExplicitlyCopyable - ](inout self: OwnedPointer[T], *, other: OwnedPointer[T],): + ](mut self: OwnedPointer[T], *, other: OwnedPointer[T],): """Construct a new OwnedPointer[] by explicitly copying the value from another OwnedPointer[]. Parameters: diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index d95a8c1141..4fa37a6e61 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -274,7 +274,7 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): return String.write(self) @always_inline("nodebug") - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats the address space to the provided Writer. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 9c1a6eac92..3f8db596af 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -272,7 +272,7 @@ struct UnsafePointer[ return self + (-1 * Int(offset.__mlir_index__())) @always_inline - fn __iadd__[T: IntLike, //](inout self, offset: T): + fn __iadd__[T: IntLike, //](mut self, offset: T): """Add an offset to this pointer. Parameters: @@ -284,7 +284,7 @@ struct UnsafePointer[ self = self + offset @always_inline - fn __isub__[T: IntLike, //](inout self, offset: T): + fn __isub__[T: IntLike, //](mut self, offset: T): """Subtract an offset from this pointer. Parameters: @@ -420,7 +420,7 @@ struct UnsafePointer[ return hex(int(self)) @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this pointer address to the provided Writer. diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 28980d33e9..e2b8f1c65e 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -53,7 +53,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: self.value = value @always_inline - fn load(inout self) -> Scalar[type]: + fn load(mut self) -> Scalar[type]: """Loads the current value from the atomic. Returns: @@ -92,7 +92,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: ) @always_inline - fn fetch_add(inout self, rhs: Scalar[type]) -> Scalar[type]: + fn fetch_add(mut self, rhs: Scalar[type]) -> Scalar[type]: """Performs atomic in-place add. Atomically replaces the current value with the result of arithmetic @@ -111,7 +111,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: return Self._fetch_add(value_addr, rhs) @always_inline - fn __iadd__(inout self, rhs: Scalar[type]): + fn __iadd__(mut self, rhs: Scalar[type]): """Performs atomic in-place add. Atomically replaces the current value with the result of arithmetic @@ -126,7 +126,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: _ = self.fetch_add(rhs) @always_inline - fn fetch_sub(inout self, rhs: Scalar[type]) -> Scalar[type]: + fn fetch_sub(mut self, rhs: Scalar[type]) -> Scalar[type]: """Performs atomic in-place sub. Atomically replaces the current value with the result of arithmetic @@ -150,7 +150,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: ](value_addr.address, rhs.value) @always_inline - fn __isub__(inout self, rhs: Scalar[type]): + fn __isub__(mut self, rhs: Scalar[type]): """Performs atomic in-place sub. Atomically replaces the current value with the result of arithmetic @@ -166,7 +166,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: @always_inline fn compare_exchange_weak( - inout self, inout expected: Scalar[type], desired: Scalar[type] + mut self, mut expected: Scalar[type], desired: Scalar[type] ) -> Bool: """Atomically compares the self value with that of the expected value. If the values are equal, then the self value is replaced with the @@ -224,7 +224,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: _max_impl[scope=scope](ptr, rhs) @always_inline - fn max(inout self, rhs: Scalar[type]): + fn max(mut self, rhs: Scalar[type]): """Performs atomic in-place max. Atomically replaces the current value with the result of max of the @@ -264,7 +264,7 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: _min_impl[scope=scope](ptr, rhs) @always_inline - fn min(inout self, rhs: Scalar[type]): + fn min(mut self, rhs: Scalar[type]): """Performs atomic in-place min. Atomically replaces the current value with the result of min of the @@ -294,7 +294,7 @@ fn _compare_exchange_weak_integral_impl[ type: DType, //, *, scope: StringLiteral ]( value_addr: UnsafePointer[Scalar[type], **_], - inout expected: Scalar[type], + mut expected: Scalar[type], desired: Scalar[type], ) -> Bool: constrained[type.is_integral(), "the input type must be integral"]() diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index ba298916d4..83e493bc7a 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -96,7 +96,7 @@ struct stat_result(Stringable, Writable): """User defined flags for file.""" fn __init__( - inout self, + mut self, /, *, st_mode: Int, @@ -151,7 +151,7 @@ struct stat_result(Stringable, Writable): self.st_flags = st_flags @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this path to the provided Writer. diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 5e47d0d846..c6e9016b5e 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -124,7 +124,7 @@ struct Path( res /= suffix return res - fn __itruediv__(inout self, suffix: String): + fn __itruediv__(mut self, suffix: String): """Joins two paths using the system-defined path separator. Args: @@ -153,7 +153,7 @@ struct Path( """ return self.path.byte_length() > 0 - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this path to the provided Writer. @@ -225,7 +225,7 @@ struct Path( return hash(self.path) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with the path string value. Parameters: diff --git a/stdlib/src/pwd/pwd.mojo b/stdlib/src/pwd/pwd.mojo index a6342d42b4..105b26e7f2 100644 --- a/stdlib/src/pwd/pwd.mojo +++ b/stdlib/src/pwd/pwd.mojo @@ -40,7 +40,7 @@ struct Passwd(Stringable): var pw_shell: String """Shell program.""" - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Formats this string to the provided Writer. Parameters: diff --git a/stdlib/src/python/_bindings.mojo b/stdlib/src/python/_bindings.mojo index 2921ebb3bd..f5e14cd329 100644 --- a/stdlib/src/python/_bindings.mojo +++ b/stdlib/src/python/_bindings.mojo @@ -33,13 +33,13 @@ from python.python import _get_global_python_itf trait ConvertibleFromPython(CollectionElement): - """Denotes a type that can attempt construction from a borrowed Python + """Denotes a type that can attempt construction from a read-only Python object. """ @staticmethod fn try_from_python(obj: PythonObject) raises -> Self: - """Attempt to construct an instance of this object from a borrowed + """Attempt to construct an instance of this object from a read-only Python value. Args: @@ -113,7 +113,7 @@ fn python_type_object[ basicsize: sizeof[PyMojoObject[T]](), itemsize: 0, flags: Py_TPFLAGS_DEFAULT, - # Note: This pointer is only "borrowed" by PyType_FromSpec. + # Note: This pointer is only "read-only" by PyType_FromSpec. slots: slots.unsafe_ptr(), } @@ -138,7 +138,7 @@ fn python_type_object[ # # The latter is the C function signature that the CPython API expects a # PyObject initializer function to have. The former is an unsafe form of the -# `fn(inout self)` signature that Mojo types with default constructors provide. +# `fn(mut self)` signature that Mojo types with default constructors provide. # # To support CPython calling a Mojo types default constructor, we need to # provide a wrapper function (around the Mojo constructor) that has the @@ -221,22 +221,22 @@ fn py_c_function_wrapper[ # > When a C function is called from Python, it borrows references to its # > arguments from the caller. The caller owns a reference to the object, - # > so the borrowed reference’s lifetime is guaranteed until the function - # > returns. Only when such a borrowed reference must be stored or passed + # > so the read-only reference’s lifetime is guaranteed until the function + # > returns. Only when such a read-only reference must be stored or passed # > on, it must be turned into an owned reference by calling Py_INCREF(). # > # > -- https://docs.python.org/3/extending/extending.html#ownership-rules # SAFETY: # Here we illegally (but carefully) construct _owned_ `PythonObject` - # values from the borrowed object reference arguments. We are careful + # values from the read-only object reference arguments. We are careful # down below to prevent the destructor for these objects from running # so that we do not illegally decrement the reference count of these # objects we do not own. # - # This is valid to do, because these are passed using the `borrowed` + # This is valid to do, because these are passed using the `read-only` # argument convention to `user_func`, so logically they are treated - # as Python borrowed references. + # as Python read-only references. var py_self = PythonObject(py_self_ptr) var args = TypedPythonObject["Tuple"]( unsafe_unchecked_from=PythonObject(args_ptr) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index defbd9d4b9..4a28b2d117 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -492,7 +492,7 @@ struct PyObject(Stringable, Representable, Writable): # Methods # ===-------------------------------------------------------------------===# - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Formats to the provided Writer. Parameters: @@ -580,7 +580,7 @@ struct PyModuleDef_Base(Stringable, Representable, Writable): # Methods # ===-------------------------------------------------------------------===# - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Formats to the provided Writer. Parameters: @@ -700,7 +700,7 @@ struct PyModuleDef(Stringable, Representable, Writable): # Methods # ===-------------------------------------------------------------------===# - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Formats to the provided Writer. Parameters: @@ -797,7 +797,7 @@ struct CPython: pass @staticmethod - fn destroy(inout existing: CPython): + fn destroy(mut existing: CPython): if existing.logging_enabled: print("CPython destroy") var remaining_refs = existing.total_ref_count.take_pointee() @@ -861,15 +861,15 @@ struct CPython: # Reference count management # ===-------------------------------------------------------------------===# - fn _inc_total_rc(inout self): + fn _inc_total_rc(mut self): var v = self.total_ref_count.take_pointee() self.total_ref_count.init_pointee_move(v + 1) - fn _dec_total_rc(inout self): + fn _dec_total_rc(mut self): var v = self.total_ref_count.take_pointee() self.total_ref_count.init_pointee_move(v - 1) - fn Py_IncRef(inout self, ptr: PyObjectPtr): + fn Py_IncRef(mut self, ptr: PyObjectPtr): """[Reference]( https://docs.python.org/3/c-api/refcounting.html#c.Py_IncRef). """ @@ -879,7 +879,7 @@ struct CPython: self.lib.call["Py_IncRef"](ptr) self._inc_total_rc() - fn Py_DecRef(inout self, ptr: PyObjectPtr): + fn Py_DecRef(mut self, ptr: PyObjectPtr): """[Reference]( https://docs.python.org/3/c-api/refcounting.html#c.Py_DecRef). """ @@ -893,7 +893,7 @@ struct CPython: # have to always be the case - but often it is and it's convenient for # debugging. We shouldn't rely on this function anywhere - its only purpose # is debugging. - fn _Py_REFCNT(inout self, ptr: PyObjectPtr) -> Int: + fn _Py_REFCNT(mut self, ptr: PyObjectPtr) -> Int: if ptr._get_ptr_as_int() == 0: return -1 # NOTE: @@ -915,19 +915,19 @@ struct CPython: # Python GIL and threading # ===-------------------------------------------------------------------===# - fn PyGILState_Ensure(inout self) -> PyGILState_STATE: + fn PyGILState_Ensure(mut self) -> PyGILState_STATE: """[Reference]( https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure). """ return self.lib.call["PyGILState_Ensure", PyGILState_STATE]() - fn PyGILState_Release(inout self, state: PyGILState_STATE): + fn PyGILState_Release(mut self, state: PyGILState_STATE): """[Reference]( https://docs.python.org/3/c-api/init.html#c.PyGILState_Release). """ self.lib.call["PyGILState_Release"](state) - fn PyEval_SaveThread(inout self) -> UnsafePointer[PyThreadState]: + fn PyEval_SaveThread(mut self) -> UnsafePointer[PyThreadState]: """[Reference]( https://docs.python.org/3/c-api/init.html#c.PyEval_SaveThread). """ @@ -936,7 +936,7 @@ struct CPython: "PyEval_SaveThread", UnsafePointer[PyThreadState] ]() - fn PyEval_RestoreThread(inout self, state: UnsafePointer[PyThreadState]): + fn PyEval_RestoreThread(mut self, state: UnsafePointer[PyThreadState]): """[Reference]( https://docs.python.org/3/c-api/init.html#c.PyEval_RestoreThread). """ @@ -946,7 +946,7 @@ struct CPython: # Python Dict operations # ===-------------------------------------------------------------------===# - fn PyDict_New(inout self) -> PyObjectPtr: + fn PyDict_New(mut self) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/dict.html#c.PyDict_New). """ @@ -964,7 +964,7 @@ struct CPython: # int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val) fn PyDict_SetItem( - inout self, dict_obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr + mut self, dict_obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr ) -> c_int: """[Reference]( https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem). @@ -982,7 +982,7 @@ struct CPython: return r fn PyDict_GetItemWithError( - inout self, dict_obj: PyObjectPtr, key: PyObjectPtr + mut self, dict_obj: PyObjectPtr, key: PyObjectPtr ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItemWithError). @@ -994,7 +994,7 @@ struct CPython: self.log("PyDict_GetItemWithError, key: ", key._get_ptr_as_int()) return r - fn PyDict_Check(inout self, maybe_dict: PyObjectPtr) -> Bool: + fn PyDict_Check(mut self, maybe_dict: PyObjectPtr) -> Bool: """[Reference]( https://docs.python.org/3/c-api/dict.html#c.PyDict_Check). """ @@ -1006,7 +1006,7 @@ struct CPython: self.Py_DecRef(my_type) return result - fn PyDict_Type(inout self) -> PyObjectPtr: + fn PyDict_Type(mut self) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/dict.html#c.PyDict_Type). """ @@ -1016,7 +1016,7 @@ struct CPython: # int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) fn PyDict_Next( - inout self, dictionary: PyObjectPtr, p: Int + mut self, dictionary: PyObjectPtr, p: Int ) -> PyKeysValuePair: """[Reference]( https://docs.python.org/3/c-api/dict.html#c.PyDict_Next). @@ -1061,7 +1061,7 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PyImport_ImportModule( - inout self, + mut self, name: StringRef, ) -> PyObjectPtr: """[Reference]( @@ -1081,7 +1081,7 @@ struct CPython: self._inc_total_rc() return r - fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: + fn PyImport_AddModule(mut self, name: StringRef) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/import.html#c.PyImport_AddModule). """ @@ -1090,7 +1090,7 @@ struct CPython: ) fn PyModule_Create( - inout self, + mut self, name: String, ) -> PyObjectPtr: """[Reference]( @@ -1118,7 +1118,7 @@ struct CPython: ) fn PyModule_AddFunctions( - inout self, + mut self, mod: PyObjectPtr, functions: UnsafePointer[PyMethodDef], ) -> c_int: @@ -1128,7 +1128,7 @@ struct CPython: return self.lib.call["PyModule_AddFunctions", c_int](mod, functions) fn PyModule_AddObjectRef( - inout self, + mut self, module: PyObjectPtr, name: UnsafePointer[c_char], value: PyObjectPtr, @@ -1141,7 +1141,7 @@ struct CPython: module, name, value ) - fn PyModule_GetDict(inout self, name: PyObjectPtr) -> PyObjectPtr: + fn PyModule_GetDict(mut self, name: PyObjectPtr) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/module.html#c.PyModule_GetDict). """ @@ -1151,7 +1151,7 @@ struct CPython: # Python Type operations # ===-------------------------------------------------------------------===# - fn Py_TYPE(inout self, ob_raw: PyObjectPtr) -> UnsafePointer[PyTypeObject]: + fn Py_TYPE(mut self, ob_raw: PyObjectPtr) -> UnsafePointer[PyTypeObject]: """Get the PyTypeObject field of a Python object.""" # Note: @@ -1165,12 +1165,12 @@ struct CPython: return ob_raw.unsized_obj_ptr[].object_type fn PyType_GetName( - inout self, type: UnsafePointer[PyTypeObject] + mut self, type: UnsafePointer[PyTypeObject] ) -> PyObjectPtr: return self.lib.call["PyType_GetName", PyObjectPtr](type) fn PyType_FromSpec( - inout self, spec: UnsafePointer[PyType_Spec] + mut self, spec: UnsafePointer[PyType_Spec] ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/type.html#c.PyType_FromSpec). @@ -1178,7 +1178,7 @@ struct CPython: return self.lib.call["PyType_FromSpec", PyObjectPtr](spec) fn PyType_GenericAlloc( - inout self, + mut self, type: UnsafePointer[PyTypeObject], nitems: Py_ssize_t, ) -> PyObjectPtr: @@ -1188,7 +1188,7 @@ struct CPython: # Python Evaluation # ===-------------------------------------------------------------------===# - fn PyRun_SimpleString(inout self, strref: StringRef) -> Bool: + fn PyRun_SimpleString(mut self, strref: StringRef) -> Bool: """Executes the given Python code. Args: @@ -1207,7 +1207,7 @@ struct CPython: ) fn PyRun_String( - inout self, + mut self, strref: StringRef, globals: PyObjectPtr, locals: PyObjectPtr, @@ -1234,7 +1234,7 @@ struct CPython: return result fn PyEval_EvalCode( - inout self, + mut self, co: PyObjectPtr, globals: PyObjectPtr, locals: PyObjectPtr, @@ -1248,14 +1248,14 @@ struct CPython: self._inc_total_rc() return result - fn PyEval_GetBuiltins(inout self) -> PyObjectPtr: + fn PyEval_GetBuiltins(mut self) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetBuiltins). """ return self.lib.call["PyEval_GetBuiltins", PyObjectPtr]() fn Py_CompileString( - inout self, + mut self, strref: StringRef, filename: StringRef, compile_mode: Int, @@ -1275,7 +1275,7 @@ struct CPython: # ===-------------------------------------------------------------------===# fn Py_Is( - inout self, + mut self, rhs: PyObjectPtr, lhs: PyObjectPtr, ) -> Bool: @@ -1289,7 +1289,7 @@ struct CPython: else: return rhs == lhs - fn PyObject_Type(inout self, obj: PyObjectPtr) -> PyObjectPtr: + fn PyObject_Type(mut self, obj: PyObjectPtr) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_Type). """ @@ -1298,7 +1298,7 @@ struct CPython: self._inc_total_rc() return p - fn PyObject_Str(inout self, obj: PyObjectPtr) -> PyObjectPtr: + fn PyObject_Str(mut self, obj: PyObjectPtr) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_Str). """ @@ -1308,7 +1308,7 @@ struct CPython: return p fn PyObject_GetItem( - inout self, obj: PyObjectPtr, key: PyObjectPtr + mut self, obj: PyObjectPtr, key: PyObjectPtr ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem). @@ -1330,7 +1330,7 @@ struct CPython: return r fn PyObject_SetItem( - inout self, obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr + mut self, obj: PyObjectPtr, key: PyObjectPtr, value: PyObjectPtr ) -> c_int: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_SetItem). @@ -1352,7 +1352,7 @@ struct CPython: return r fn PyObject_GetAttrString( - inout self, + mut self, obj: PyObjectPtr, name: StringRef, ) -> PyObjectPtr: @@ -1378,7 +1378,7 @@ struct CPython: return r fn PyObject_SetAttrString( - inout self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr + mut self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr ) -> c_int: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString). @@ -1402,7 +1402,7 @@ struct CPython: return r fn PyObject_CallObject( - inout self, + mut self, callable_obj: PyObjectPtr, args: PyObjectPtr, ) -> PyObjectPtr: @@ -1426,7 +1426,7 @@ struct CPython: return r fn PyObject_Call( - inout self, + mut self, callable_obj: PyObjectPtr, args: PyObjectPtr, kwargs: PyObjectPtr, @@ -1450,26 +1450,26 @@ struct CPython: self._inc_total_rc() return r - fn PyObject_IsTrue(inout self, obj: PyObjectPtr) -> c_int: + fn PyObject_IsTrue(mut self, obj: PyObjectPtr) -> c_int: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue). """ return self.lib.call["PyObject_IsTrue", c_int](obj) - fn PyObject_Length(inout self, obj: PyObjectPtr) -> Int: + fn PyObject_Length(mut self, obj: PyObjectPtr) -> Int: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_Length). """ return int(self.lib.call["PyObject_Length", Int](obj)) - fn PyObject_Hash(inout self, obj: PyObjectPtr) -> Int: + fn PyObject_Hash(mut self, obj: PyObjectPtr) -> Int: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_Hash). """ return int(self.lib.call["PyObject_Hash", Int](obj)) fn PyObject_GetIter( - inout self, traversablePyObject: PyObjectPtr + mut self, traversablePyObject: PyObjectPtr ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_GetIter). @@ -1496,7 +1496,7 @@ struct CPython: # Python Tuple operations # ===-------------------------------------------------------------------===# - fn PyTuple_New(inout self, count: Int) -> PyObjectPtr: + fn PyTuple_New(mut self, count: Int) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/tuple.html#c.PyTuple_New). """ @@ -1515,7 +1515,7 @@ struct CPython: return r fn PyTuple_GetItem( - inout self, tuple: PyObjectPtr, pos: Py_ssize_t + mut self, tuple: PyObjectPtr, pos: Py_ssize_t ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GetItem). @@ -1523,7 +1523,7 @@ struct CPython: return self.lib.call["PyTuple_GetItem", PyObjectPtr](tuple, pos) fn PyTuple_SetItem( - inout self, tuple_obj: PyObjectPtr, index: Int, element: PyObjectPtr + mut self, tuple_obj: PyObjectPtr, index: Int, element: PyObjectPtr ) -> c_int: """[Reference]( https://docs.python.org/3/c-api/tuple.html#c.PyTuple_SetItem). @@ -1540,7 +1540,7 @@ struct CPython: # Python List operations # ===-------------------------------------------------------------------===# - fn PyList_New(inout self, length: Int) -> PyObjectPtr: + fn PyList_New(mut self, length: Int) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/list.html#c.PyList_New). """ @@ -1559,7 +1559,7 @@ struct CPython: return r fn PyList_SetItem( - inout self, list_obj: PyObjectPtr, index: Int, value: PyObjectPtr + mut self, list_obj: PyObjectPtr, index: Int, value: PyObjectPtr ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/list.html#c.PyList_SetItem). @@ -1573,7 +1573,7 @@ struct CPython: ) fn PyList_GetItem( - inout self, list_obj: PyObjectPtr, index: Int + mut self, list_obj: PyObjectPtr, index: Int ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/list.html#c.PyList_GetItem). @@ -1585,7 +1585,7 @@ struct CPython: # ref: https://docs.python.org/3/c-api/concrete.html # ===-------------------------------------------------------------------===# - fn Py_None(inout self) -> PyObjectPtr: + fn Py_None(mut self) -> PyObjectPtr: """Get a None value, of type NoneType. [Reference]( https://docs.python.org/3/c-api/none.html#c.Py_None).""" @@ -1607,7 +1607,7 @@ struct CPython: # Boolean Objects # ===-------------------------------------------------------------------===# - fn PyBool_FromLong(inout self, value: c_long) -> PyObjectPtr: + fn PyBool_FromLong(mut self, value: c_long) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/bool.html#c.PyBool_FromLong). """ @@ -1629,7 +1629,7 @@ struct CPython: # Integer Objects # ===-------------------------------------------------------------------===# - fn PyLong_FromSsize_t(inout self, value: c_ssize_t) -> PyObjectPtr: + fn PyLong_FromSsize_t(mut self, value: c_ssize_t) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/long.html#c.PyLong_FromSsize_t). """ @@ -1647,7 +1647,7 @@ struct CPython: self._inc_total_rc() return r - fn PyLong_FromSize_t(inout self, value: c_size_t) -> PyObjectPtr: + fn PyLong_FromSize_t(mut self, value: c_size_t) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/long.html#c.PyLong_FromSize_t). """ @@ -1665,7 +1665,7 @@ struct CPython: self._inc_total_rc() return r - fn PyLong_AsSsize_t(inout self, py_object: PyObjectPtr) -> c_ssize_t: + fn PyLong_AsSsize_t(mut self, py_object: PyObjectPtr) -> c_ssize_t: """[Reference]( https://docs.python.org/3/c-api/long.html#c.PyLong_AsSsize_t). """ @@ -1675,7 +1675,7 @@ struct CPython: # Floating-Point Objects # ===-------------------------------------------------------------------===# - fn PyFloat_FromDouble(inout self, value: Float64) -> PyObjectPtr: + fn PyFloat_FromDouble(mut self, value: Float64) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble). """ @@ -1693,7 +1693,7 @@ struct CPython: self._inc_total_rc() return r - fn PyFloat_AsDouble(inout self, py_object: PyObjectPtr) -> Float64: + fn PyFloat_AsDouble(mut self, py_object: PyObjectPtr) -> Float64: """[Reference]( https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble). """ @@ -1703,7 +1703,7 @@ struct CPython: # Unicode Objects # ===-------------------------------------------------------------------===# - fn PyUnicode_DecodeUTF8(inout self, strref: StringRef) -> PyObjectPtr: + fn PyUnicode_DecodeUTF8(mut self, strref: StringRef) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8). """ @@ -1725,7 +1725,7 @@ struct CPython: self._inc_total_rc() return r - fn PyUnicode_DecodeUTF8(inout self, strslice: StringSlice) -> PyObjectPtr: + fn PyUnicode_DecodeUTF8(mut self, strslice: StringSlice) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8). """ @@ -1746,7 +1746,7 @@ struct CPython: self._inc_total_rc() return r - fn PySlice_FromSlice(inout self, slice: Slice) -> PyObjectPtr: + fn PySlice_FromSlice(mut self, slice: Slice) -> PyObjectPtr: # Convert Mojo Slice to Python slice parameters # Note: Deliberately avoid using `span.indices()` here and instead pass # the Slice parameters directly to Python. Python's C implementation @@ -1773,7 +1773,7 @@ struct CPython: return py_slice - fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: + fn PyUnicode_AsUTF8AndSize(mut self, py_object: PyObjectPtr) -> StringRef: """[Reference]( https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8AndSize). """ @@ -1788,19 +1788,19 @@ struct CPython: # Python Error operations # ===-------------------------------------------------------------------===# - fn PyErr_Clear(inout self): + fn PyErr_Clear(mut self): """[Reference]( https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear). """ self.lib.call["PyErr_Clear"]() - fn PyErr_Occurred(inout self) -> Bool: + fn PyErr_Occurred(mut self) -> Bool: """[Reference]( https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred). """ return not self.lib.call["PyErr_Occurred", PyObjectPtr]().is_null() - fn PyErr_Fetch(inout self) -> PyObjectPtr: + fn PyErr_Fetch(mut self) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Fetch). """ @@ -1827,14 +1827,14 @@ struct CPython: _ = traceback return r - fn PyErr_SetNone(inout self, type: PyObjectPtr): + fn PyErr_SetNone(mut self, type: PyObjectPtr): """[Reference]( https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetNone). """ self.lib.call["PyErr_SetNone"](type) fn PyErr_SetString( - inout self, + mut self, type: PyObjectPtr, message: UnsafePointer[c_char], ): @@ -1848,10 +1848,10 @@ struct CPython: # ===-------------------------------------------------------------------===# fn get_error_global( - inout self, + mut self, global_name: StringLiteral, ) -> PyObjectPtr: - """Get a Python borrowed reference to the specified global exception + """Get a Python read-only reference to the specified global exception object. """ @@ -1872,7 +1872,7 @@ struct CPython: # Python Iterator operations # ===-------------------------------------------------------------------===# - fn PyIter_Next(inout self, iterator: PyObjectPtr) -> PyObjectPtr: + fn PyIter_Next(mut self, iterator: PyObjectPtr) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/iter.html#c.PyIter_Next). """ @@ -1893,13 +1893,13 @@ struct CPython: self._inc_total_rc() return next_obj - fn PyIter_Check(inout self, obj: PyObjectPtr) -> Bool: + fn PyIter_Check(mut self, obj: PyObjectPtr) -> Bool: """[Reference]( https://docs.python.org/3/c-api/iter.html#c.PyIter_Check). """ return self.lib.call["PyIter_Check", c_int](obj) != 0 - fn PySequence_Check(inout self, obj: PyObjectPtr) -> Bool: + fn PySequence_Check(mut self, obj: PyObjectPtr) -> Bool: """[Reference]( https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check). """ @@ -1910,7 +1910,7 @@ struct CPython: # ===-------------------------------------------------------------------===# fn PySlice_New( - inout self, start: PyObjectPtr, stop: PyObjectPtr, step: PyObjectPtr + mut self, start: PyObjectPtr, stop: PyObjectPtr, step: PyObjectPtr ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/slice.html#c.PySlice_New). diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 80437fc9e2..8771e54c6f 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -47,10 +47,10 @@ fn _init_python_global() -> _PythonGlobal: struct _PythonGlobal: var cpython: CPython - fn __moveinit__(inout self, owned other: Self): + fn __moveinit__(mut self, owned other: Self): self.cpython = other.cpython^ - fn __init__(inout self): + fn __init__(mut self): self.cpython = CPython() fn __del__(owned self): @@ -99,7 +99,7 @@ struct Python: """ self.impl = existing.impl - fn eval(inout self, code: StringRef) -> Bool: + fn eval(mut self, code: StringRef) -> Bool: """Executes the given Python code. Args: @@ -127,7 +127,7 @@ struct Python: `PythonObject` containing the result of the evaluation. """ var cpython = _get_global_python_itf().cpython() - # PyImport_AddModule returns a borrowed reference. + # PyImport_AddModule returns a read-only reference. var module = PythonObject.from_borrowed_ptr( cpython.PyImport_AddModule(name) ) @@ -265,7 +265,7 @@ struct Python: @staticmethod fn add_functions( - inout module: TypedPythonObject["Module"], + mut module: TypedPythonObject["Module"], owned functions: List[PyMethodDef], ) raises: """Adds functions to a PyModule object. @@ -287,7 +287,7 @@ struct Python: @staticmethod fn unsafe_add_methods( - inout module: TypedPythonObject["Module"], + mut module: TypedPythonObject["Module"], functions: UnsafePointer[PyMethodDef], ) raises: """Adds methods to a PyModule object. @@ -314,7 +314,7 @@ struct Python: @staticmethod fn add_object( - inout module: TypedPythonObject["Module"], + mut module: TypedPythonObject["Module"], name: StringLiteral, value: PythonObject, ) raises: @@ -366,7 +366,7 @@ struct Python: return PythonObject([]) @no_inline - fn __str__(inout self, str_obj: PythonObject) -> StringRef: + fn __str__(mut self, str_obj: PythonObject) -> StringRef: """Return a string representing the given Python object. Args: @@ -379,7 +379,7 @@ struct Python: return cpython.PyUnicode_AsUTF8AndSize(str_obj.py_object) @staticmethod - fn throw_python_exception_if_error_state(inout cpython: CPython) raises: + fn throw_python_exception_if_error_state(mut cpython: CPython) raises: """Raise an exception if CPython interpreter is in an error state. Args: @@ -389,7 +389,7 @@ struct Python: raise Python.unsafe_get_python_exception(cpython) @staticmethod - fn unsafe_get_python_exception(inout cpython: CPython) -> Error: + fn unsafe_get_python_exception(mut cpython: CPython) -> Error: """Get the `Error` object corresponding to the current CPython interpreter error state. diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index a579696179..4566b11501 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -87,7 +87,7 @@ struct _PyIter(Sized): # Trait implementations # ===-------------------------------------------------------------------===# - fn __next__(inout self: _PyIter) -> PythonObject: + fn __next__(mut self: _PyIter) -> PythonObject: """Return the next item and update to point to subsequent item. Returns: @@ -222,7 +222,7 @@ struct TypedPythonObject[type_hint: StringLiteral]( raise Python.unsafe_get_python_exception(cpython) # TODO(MSTDL-911): Avoid unnecessary owned reference counts when - # returning borrowed PythonObject values. + # returning read-only PythonObject values. return PythonObject.from_borrowed_ptr(item) @@ -276,7 +276,7 @@ struct PythonObject( @staticmethod fn from_borrowed_ptr(borrowed_ptr: PyObjectPtr) -> Self: - """Initialize this object from a borrowed reference-counted Python + """Initialize this object from a read-only reference-counted Python object pointer. The reference count of the pointee object will be incremented, and @@ -294,7 +294,7 @@ struct PythonObject( pointer returned by 'Borrowed reference'-type objects. Args: - borrowed_ptr: A borrowed reference counted pointer to a Python + borrowed_ptr: A read-only reference counted pointer to a Python object. Returns: @@ -303,7 +303,7 @@ struct PythonObject( var cpython = _get_global_python_itf().cpython() # SAFETY: - # We were passed a Python 'borrowed reference', so for it to be + # We were passed a Python 'read-only reference', so for it to be # safe to store this reference, we must increment the reference # count to convert this to a 'strong reference'. cpython.Py_IncRef(borrowed_ptr) @@ -374,7 +374,7 @@ struct PythonObject( self.py_object = cpython.PyLong_FromSsize_t(integer) @implicit - fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): + fn __init__[dt: DType](mut self, value: SIMD[dt, 1]): """Initialize the object with a generic scalar value. If the scalar value type is bool, it is converted to a boolean. Otherwise, it is converted to the appropriate integer or floating point type. @@ -427,7 +427,7 @@ struct PythonObject( self.py_object = cpython.PyUnicode_DecodeUTF8(string.as_string_slice()) @implicit - fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[*Ts]): + fn __init__[*Ts: CollectionElement](mut self, value: ListLiteral[*Ts]): """Initialize the object from a list literal. Parameters: @@ -470,7 +470,7 @@ struct PythonObject( _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) @implicit - fn __init__[*Ts: CollectionElement](inout self, value: Tuple[*Ts]): + fn __init__[*Ts: CollectionElement](mut self, value: Tuple[*Ts]): """Initialize the object from a tuple literal. Parameters: @@ -715,7 +715,7 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return PythonObject(result) - fn __setitem__(inout self, *args: PythonObject, value: PythonObject) raises: + fn __setitem__(mut self, *args: PythonObject, value: PythonObject) raises: """Set the value with the given key or keys. Args: @@ -782,7 +782,7 @@ struct PythonObject( return PythonObject(result_obj) fn _call_single_arg_inplace_method( - inout self, method_name: StringRef, rhs: PythonObject + mut self, method_name: StringRef, rhs: PythonObject ) raises: var cpython = _get_global_python_itf().cpython() var tuple_obj = cpython.PyTuple_New(1) @@ -831,7 +831,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rmul__", lhs) - fn __imul__(inout self, rhs: PythonObject) raises: + fn __imul__(mut self, rhs: PythonObject) raises: """In-place multiplication. Calls the underlying object's `__imul__` method. @@ -868,7 +868,7 @@ struct PythonObject( """ return self._call_single_arg_method("__radd__", lhs) - fn __iadd__(inout self, rhs: PythonObject) raises: + fn __iadd__(mut self, rhs: PythonObject) raises: """Immediate addition and concatenation. Args: @@ -902,7 +902,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rsub__", lhs) - fn __isub__(inout self, rhs: PythonObject) raises: + fn __isub__(mut self, rhs: PythonObject) raises: """Immediate subtraction. Args: @@ -939,7 +939,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rfloordiv__", lhs) - fn __ifloordiv__(inout self, rhs: PythonObject) raises: + fn __ifloordiv__(mut self, rhs: PythonObject) raises: """Immediate floor division. Args: @@ -973,7 +973,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rtruediv__", lhs) - fn __itruediv__(inout self, rhs: PythonObject) raises: + fn __itruediv__(mut self, rhs: PythonObject) raises: """Immediate division. Args: @@ -1007,7 +1007,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rmod__", lhs) - fn __imod__(inout self, rhs: PythonObject) raises: + fn __imod__(mut self, rhs: PythonObject) raises: """Immediate modulo. Args: @@ -1039,7 +1039,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rxor__", lhs) - fn __ixor__(inout self, rhs: PythonObject) raises: + fn __ixor__(mut self, rhs: PythonObject) raises: """Immediate exclusive OR. Args: @@ -1072,7 +1072,7 @@ struct PythonObject( """ return self._call_single_arg_method("__ror__", lhs) - fn __ior__(inout self, rhs: PythonObject) raises: + fn __ior__(mut self, rhs: PythonObject) raises: """Immediate bitwise OR. Args: @@ -1105,7 +1105,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rand__", lhs) - fn __iand__(inout self, rhs: PythonObject) raises: + fn __iand__(mut self, rhs: PythonObject) raises: """Immediate bitwise AND. Args: @@ -1138,7 +1138,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rrshift__", lhs) - fn __irshift__(inout self, rhs: PythonObject) raises: + fn __irshift__(mut self, rhs: PythonObject) raises: """Immediate bitwise right shift. Args: @@ -1171,7 +1171,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rlshift__", lhs) - fn __ilshift__(inout self, rhs: PythonObject) raises: + fn __ilshift__(mut self, rhs: PythonObject) raises: """Immediate bitwise left shift. Args: @@ -1202,7 +1202,7 @@ struct PythonObject( """ return self._call_single_arg_method("__rpow__", lhs) - fn __ipow__(inout self, rhs: PythonObject) raises: + fn __ipow__(mut self, rhs: PythonObject) raises: """Immediate power of. Args: @@ -1412,7 +1412,7 @@ struct PythonObject( debug_assert(result != -1, "object is not hashable") return result - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with this python object hash value. Parameters: @@ -1480,7 +1480,7 @@ struct PythonObject( _ = python_str return mojo_str - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this Python object to the provided Writer. diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index e536c6f533..8ac75d5a8f 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -224,7 +224,7 @@ fn randn[ return -fn shuffle[T: CollectionElement, //](inout list: List[T]): +fn shuffle[T: CollectionElement, //](mut list: List[T]): """Shuffles the elements of the list randomly. Performs an in-place Fisher-Yates shuffle on the provided list. diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index fea193e75b..6d65ab7447 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -176,7 +176,7 @@ struct DLHandle(CollectionElement, CollectionElementNew, Boolable): # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline - fn close(inout self): + fn close(mut self): """Delete the DLHandle object unloading the associated dynamic library. """ diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 55c3ca42d2..509c234ac9 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -212,7 +212,7 @@ struct TemporaryDirectory: """Whether to ignore cleanup errors.""" fn __init__( - inout self, + mut self, suffix: String = "", prefix: String = "tmp", dir: Optional[String] = None, @@ -290,7 +290,7 @@ struct NamedTemporaryFile: """Name of the file.""" fn __init__( - inout self, + mut self, mode: String = "w", name: Optional[String] = None, suffix: String = "", @@ -345,7 +345,7 @@ struct NamedTemporaryFile: except: pass - fn close(inout self) raises: + fn close(mut self) raises: """Closes the file handle.""" self._file_handle.close() if self._delete: @@ -403,7 +403,7 @@ struct NamedTemporaryFile: """ return self._file_handle.seek(offset, whence) - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. Parameters: @@ -416,7 +416,7 @@ struct NamedTemporaryFile: write_buffered[buffer_size=4096](file, args) @always_inline - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): """ Write a span of bytes to the file. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 38423c9aa4..20173be736 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -536,7 +536,7 @@ struct assert_raises: @always_inline fn __init__( - inout self, + mut self, *, contains: String, location: Optional[_SourceLocation] = None, diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo index eab8549b19..af4bbc8d7c 100644 --- a/stdlib/src/utils/format.mojo +++ b/stdlib/src/utils/format.mojo @@ -80,7 +80,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): self.format_spec = other.format_spec fn __init__( - inout self, + mut self, first_curly: Int, last_curly: Int, field: Self._FieldVariantType, @@ -267,17 +267,17 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): fn _handle_field_and_break[ T: Stringlike ]( - inout self, + mut self, fmt_src: T, len_pos_args: Int, i: Int, start_value: Int, - inout automatic_indexing_count: Int, - inout raised_automatic_index: Optional[Int], - inout manual_indexing_count: Int, - inout raised_manual_index: Optional[Int], - inout raised_kwarg_field: Optional[String], - inout total_estimated_entry_byte_width: Int, + mut automatic_indexing_count: Int, + mut raised_automatic_index: Optional[Int], + mut manual_indexing_count: Int, + mut raised_manual_index: Optional[Int], + mut raised_kwarg_field: Optional[String], + mut total_estimated_entry_byte_width: Int, ) raises -> Bool: alias S = StringSlice[StaticConstantOrigin] @@ -350,7 +350,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): fn _format_entry[ len_pos_args: Int - ](self, inout res: String, args: Self._args_t, inout auto_idx: Int) raises: + ](self, mut res: String, args: Self._args_t, mut auto_idx: Int) raises: # TODO(#3403 and/or #3252): this function should be able to use # Writer syntax when the type implements it, since it will give great # performance benefits. This also needs to be able to check if the given @@ -603,7 +603,7 @@ struct _FormatSpec: """ fn __init__( - inout self, + mut self, fill: UInt8 = ord(" "), align: UInt8 = 0, sign: UInt8 = ord("-"), @@ -680,7 +680,7 @@ struct _FormatSpec: return None # TODO: this should be in StringSlice.__format__(self, spec: FormatSpec, *, writer: Writer): - fn format(self, inout res: String, item: StringSlice) raises: + fn format(self, mut res: String, item: StringSlice) raises: """Transform a String according to its format specification. Args: @@ -691,9 +691,7 @@ struct _FormatSpec: # TODO: align, fill, etc. res += item - fn format[ - T: _CurlyEntryFormattable - ](self, inout res: String, item: T) raises: + fn format[T: _CurlyEntryFormattable](self, mut res: String, item: T) raises: """Stringify a type according to its format specification. Args: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 174cde61a9..0ea645ee88 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -404,7 +404,7 @@ struct IndexList[ return int(self.data[idx]) @always_inline("nodebug") - fn __setitem__[index: Int](inout self, val: Int): + fn __setitem__[index: Int](mut self, val: Int): """Sets an element in the tuple at the given static index. Parameters: @@ -416,7 +416,7 @@ struct IndexList[ self.data.__setitem__[index](val) @always_inline("nodebug") - fn __setitem__[index: Int](inout self, val: Self._int_type): + fn __setitem__[index: Int](mut self, val: Self._int_type): """Sets an element in the tuple at the given static index. Parameters: @@ -428,7 +428,7 @@ struct IndexList[ self.data.__setitem__[index](val) @always_inline("nodebug") - fn __setitem__(inout self, idx: Int, val: Int): + fn __setitem__(mut self, idx: Int, val: Int): """Sets an element in the tuple at the given index. Args: @@ -755,7 +755,7 @@ struct IndexList[ return buf^ @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this int tuple to the provided Writer. diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index 055ad748a8..cf8caafc31 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -103,7 +103,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): # Operator dunders # ===------------------------------------------------------------------=== # - fn __iadd__(inout self, literal: StringLiteral): + fn __iadd__(mut self, literal: StringLiteral): """Appends another string to this string. Args: @@ -111,7 +111,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): """ self.__iadd__(StringRef(literal)) - fn __iadd__(inout self, string: String): + fn __iadd__(mut self, string: String): """Appends another string to this string. Args: @@ -119,7 +119,7 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): """ self.__iadd__(string.as_string_slice()) - fn __iadd__(inout self, str_slice: StringSlice[_]): + fn __iadd__(mut self, str_slice: StringSlice[_]): """Appends another string to this string. Args: @@ -395,7 +395,7 @@ struct _FixedString[CAP: Int]( # Operator dunders # ===------------------------------------------------------------------=== # - fn __iadd__(inout self, literal: StringLiteral) raises: + fn __iadd__(mut self, literal: StringLiteral) raises: """Appends another string to this string. Args: @@ -403,7 +403,7 @@ struct _FixedString[CAP: Int]( """ self.__iadd__(literal.as_string_slice()) - fn __iadd__(inout self, string: String) raises: + fn __iadd__(mut self, string: String) raises: """Appends another string to this string. Args: @@ -412,7 +412,7 @@ struct _FixedString[CAP: Int]( self.__iadd__(string.as_string_slice()) @always_inline - fn __iadd__(inout self, str_slice: StringSlice[_]) raises: + fn __iadd__(mut self, str_slice: StringSlice[_]) raises: """Appends another string to this string. Args: @@ -438,7 +438,7 @@ struct _FixedString[CAP: Int]( # ===------------------------------------------------------------------=== # fn _iadd_non_raising( - inout self, + mut self, bytes: Span[Byte, _], ) -> Optional[Error]: var total_len = len(self) + len(bytes) @@ -467,11 +467,11 @@ struct _FixedString[CAP: Int]( return None - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write_bytes(self.as_bytes()) @always_inline - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): """ Write a byte span to this String. @@ -481,7 +481,7 @@ struct _FixedString[CAP: Int]( """ _ = self._iadd_non_raising(bytes) - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. Parameters: diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index dbae8c0d63..3916cd92cc 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -65,7 +65,7 @@ struct BlockingSpinLock: self.counter = Atomic[DType.int64](Self.UNLOCKED) - fn lock(inout self, owner: Int): + fn lock(mut self, owner: Int): """Acquires the lock. Args: @@ -79,7 +79,7 @@ struct BlockingSpinLock: waiter.wait() expected = Self.UNLOCKED - fn unlock(inout self, owner: Int) -> Bool: + fn unlock(mut self, owner: Int) -> Bool: """Releases the lock. Args: @@ -108,7 +108,7 @@ struct BlockingScopedLock: """The underlying lock instance.""" fn __init__( - inout self, + mut self, lock: UnsafePointer[Self.LockType], ): """Primary constructor. @@ -120,8 +120,8 @@ struct BlockingScopedLock: self.lock = lock fn __init__( - inout self, - inout lock: Self.LockType, + mut self, + mut lock: Self.LockType, ): """Secondary constructor. @@ -132,14 +132,14 @@ struct BlockingScopedLock: self.lock = UnsafePointer.address_of(lock) @no_inline - fn __enter__(inout self): + fn __enter__(mut self): """Acquire the lock on entry. This is done by setting the owner of the lock to own address.""" var address = UnsafePointer[Self].address_of(self) self.lock[].lock(int(address)) @no_inline - fn __exit__(inout self): + fn __exit__(mut self): """Release the lock on exit. Reset the address on the underlying lock.""" var address = UnsafePointer[Self].address_of(self) diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 5a9588d107..3ec2b3030c 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -69,7 +69,7 @@ struct _SpanIter[ @always_inline fn __next__( - inout self, + mut self, ) -> Pointer[T, origin]: @parameter if forward: @@ -150,7 +150,7 @@ struct Span[ @always_inline fn __init__[ size: Int, // - ](inout self, ref [origin]array: InlineArray[T, size]): + ](mut self, ref [origin]array: InlineArray[T, size]): """Construct a Span from an InlineArray. Parameters: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 9a14c36e24..7a907b7ed0 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -231,7 +231,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): @always_inline("nodebug") fn __setitem__[ IntLike: IntLike, // - ](inout self, idx: IntLike, val: Self.element_type): + ](mut self, idx: IntLike, val: Self.element_type): """Stores a single value into the tuple at the specified dynamic index. Parameters: @@ -252,7 +252,7 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): self = tmp @always_inline("nodebug") - fn __setitem__[index: Int](inout self, val: Self.element_type): + fn __setitem__[index: Int](mut self, val: Self.element_type): """Stores a single value into the tuple at the specified index. Parameters: diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 96f6aaeada..48be446549 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -190,9 +190,7 @@ struct _StringSliceIter[ var ptr: UnsafePointer[UInt8] var length: Int - fn __init__( - inout self, *, unsafe_pointer: UnsafePointer[UInt8], length: Int - ): + fn __init__(mut self, *, unsafe_pointer: UnsafePointer[UInt8], length: Int): self.index = 0 if forward else length self.ptr = unsafe_pointer self.length = length @@ -203,7 +201,7 @@ struct _StringSliceIter[ fn __iter__(self) -> Self: return self - fn __next__(inout self) -> StringSlice[origin]: + fn __next__(mut self) -> StringSlice[origin]: @parameter if forward: var byte_len = 1 @@ -356,7 +354,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( @implicit fn __init__[ O: ImmutableOrigin, // - ](inout self: StringSlice[O], ref [O]value: String): + ](mut self: StringSlice[O], ref [O]value: String): """Construct an immutable StringSlice. Parameters: @@ -395,7 +393,7 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( var s = S(ptr=self.unsafe_ptr(), length=b_len) return b_len - _count_utf8_continuation_bytes(s) - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """Formats this string slice to the provided `Writer`. Parameters: diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 1a19e5788b..e2a0303100 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -357,7 +357,7 @@ struct StringRef( """ return hash(self.data, self.length) - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): """Updates hasher with the underlying bytes. Parameters: @@ -411,7 +411,7 @@ struct StringRef( return String.write("StringRef(", repr(str(self)), ")") @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats this StringRef to the provided Writer. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 07dc22da63..6936a3a003 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -18,7 +18,7 @@ You can use this type to implement variant/sum types. For example: from utils import Variant alias IntOrString = Variant[Int, String] -fn to_string(inout x: IntOrString) -> String: +fn to_string(mut x: IntOrString) -> String: if x.isa[String](): return x[String] # x.isa[Int]() @@ -81,7 +81,7 @@ struct Variant[*Ts: CollectionElement]( ```mojo from utils import Variant alias IntOrString = Variant[Int, String] - fn to_string(inout x: IntOrString) -> String: + fn to_string(mut x: IntOrString) -> String: if x.isa[String](): return x[String] # x.isa[Int]() @@ -124,7 +124,7 @@ struct Variant[*Ts: CollectionElement]( self._impl = __mlir_attr[`#kgen.unknown : `, Self._mlir_type] @implicit - fn __init__[T: CollectionElement](inout self, owned value: T): + fn __init__[T: CollectionElement](mut self, owned value: T): """Create a variant with one of the types. Parameters: @@ -239,7 +239,7 @@ struct Variant[*Ts: CollectionElement]( return UnsafePointer(discr_ptr).bitcast[UInt8]()[] @always_inline - fn take[T: CollectionElement](inout self) -> T: + fn take[T: CollectionElement](mut self) -> T: """Take the current value of the variant with the provided type. The caller takes ownership of the underlying value. @@ -260,7 +260,7 @@ struct Variant[*Ts: CollectionElement]( return self.unsafe_take[T]() @always_inline - fn unsafe_take[T: CollectionElement](inout self) -> T: + fn unsafe_take[T: CollectionElement](mut self) -> T: """Unsafely take the current value of the variant with the provided type. The caller takes ownership of the underlying value. @@ -284,7 +284,7 @@ struct Variant[*Ts: CollectionElement]( @always_inline fn replace[ Tin: CollectionElement, Tout: CollectionElement - ](inout self, owned value: Tin) -> Tout: + ](mut self, owned value: Tin) -> Tout: """Replace the current value of the variant with the provided type. The caller takes ownership of the underlying value. @@ -311,7 +311,7 @@ struct Variant[*Ts: CollectionElement]( @always_inline fn unsafe_replace[ Tin: CollectionElement, Tout: CollectionElement - ](inout self, owned value: Tin) -> Tout: + ](mut self, owned value: Tin) -> Tout: """Unsafely replace the current value of the variant with the provided type. The caller takes ownership of the underlying value. @@ -337,7 +337,7 @@ struct Variant[*Ts: CollectionElement]( self.set[Tin](value^) return x^ - fn set[T: CollectionElement](inout self, owned value: T): + fn set[T: CollectionElement](mut self, owned value: T): """Set the variant value. This will call the destructor on the old value, and update the variant's diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index bbb574670b..2d9351036f 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -43,11 +43,11 @@ trait Writer: var s: String # Writer requirement to write a Span of Bytes - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): self.s._iadd[False](bytes) # Writer requirement to take multiple args - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): @parameter fn write_arg[T: Writable](arg: T): arg.write_to(self) @@ -55,7 +55,7 @@ trait Writer: args.each[write_arg]() # Also make it Writable to allow `print` to write the inner String - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write(self.s) @@ -66,7 +66,7 @@ trait Writer: # Pass multiple args to the Writer. The Int and StringLiteral types # call `writer.write_bytes` in their own `write_to` implementations. - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write("Point(", self.x, ", ", self.y, ")") # Enable conversion to a String using `str(point)` @@ -90,7 +90,7 @@ trait Writer: """ @always_inline - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): """ Write a `Span[Byte]` to this `Writer`. @@ -100,7 +100,7 @@ trait Writer: """ ... - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): """Write a sequence of Writable arguments to the provided Writer. Parameters: @@ -135,7 +135,7 @@ trait Writable: var x: Float64 var y: Float64 - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): var string = "Point" # Write a single `Span[Byte]`: writer.write_bytes(string.as_bytes()) @@ -144,7 +144,7 @@ trait Writable: ``` """ - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): """ Formats the string representation of this type to the provided Writer. @@ -165,7 +165,7 @@ trait Writable: fn write_args[ W: Writer, *Ts: Writable ]( - inout writer: W, + mut writer: W, args: VariadicPack[_, Writable, *Ts], *, sep: StaticString = "", @@ -238,7 +238,7 @@ struct _WriteBufferHeap(Writer): self.data.free() @always_inline - fn write_bytes(inout self, bytes: Span[UInt8, _]): + fn write_bytes(mut self, bytes: Span[UInt8, _]): len_bytes = len(bytes) # If empty then return if len_bytes == 0: @@ -248,7 +248,7 @@ struct _WriteBufferHeap(Writer): self.data[i + self.pos] = ptr[i] self.pos += len_bytes - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): @parameter fn write_arg[T: Writable](arg: T): arg.write_to(self) @@ -262,10 +262,10 @@ struct _ArgBytes(Writer): fn __init__(out self): self.size = 0 - fn write_bytes(inout self, bytes: Span[UInt8, _]): + fn write_bytes(mut self, bytes: Span[UInt8, _]): self.size += len(bytes) - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): @parameter fn write_arg[T: Writable](arg: T): arg.write_to(self) @@ -284,7 +284,7 @@ struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): self.pos = 0 self.writer = writer^ - fn flush(inout self): + fn flush(mut self): self.writer.write_bytes( Span[Byte, ImmutableAnyOrigin]( ptr=self.data.unsafe_ptr(), length=self.pos @@ -292,7 +292,7 @@ struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): ) self.pos = 0 - fn write_bytes(inout self, bytes: Span[Byte, _]): + fn write_bytes(mut self, bytes: Span[Byte, _]): len_bytes = len(bytes) # If empty then return if len_bytes == 0: @@ -309,7 +309,7 @@ struct _WriteBufferStack[W: MovableWriter, //, capacity: Int](Writer): memcpy(self.data.unsafe_ptr() + self.pos, bytes.unsafe_ptr(), len_bytes) self.pos += len_bytes - fn write[*Ts: Writable](inout self, *args: *Ts): + fn write[*Ts: Writable](mut self, *args: *Ts): @parameter fn write_arg[T: Writable](arg: T): arg.write_to(self) diff --git a/stdlib/test/builtin/test_debug_assert.mojo b/stdlib/test/builtin/test_debug_assert.mojo index 9df1b1e1f3..6d8ceb57f3 100644 --- a/stdlib/test/builtin/test_debug_assert.mojo +++ b/stdlib/test/builtin/test_debug_assert.mojo @@ -52,5 +52,5 @@ def test_debug_assert_writable(): struct WritableOnly: var message: String - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write(self.message) diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 65c118665a..899c75da17 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -183,7 +183,7 @@ def test_function(lhs, rhs) -> object: return lhs + rhs -# These are all marked borrowed because 'object' doesn't support function +# These are all marked read-only because 'object' doesn't support function # types with owned arguments. def test_function_raises(a) -> object: raise Error("Error from function type") diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index a18b7366ff..4ace3a30c1 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -63,7 +63,7 @@ struct PrintChecker: fn stream(self) -> FileDescriptor: return self.tmp._file_handle._get_raw_fd() - fn check_line(inout self, expected: String, msg: String = "") raises: + fn check_line(mut self, expected: String, msg: String = "") raises: print(end="", file=self.stream(), flush=True) _ = self.tmp.seek(self.cursor) var result = self.tmp.read()[:-1] @@ -72,7 +72,7 @@ struct PrintChecker: self.cursor += len(result) + 1 fn check_line_starts_with( - inout self, prefix: String, msg: String = "" + mut self, prefix: String, msg: String = "" ) raises: print(end="", file=self.stream(), flush=True) _ = self.tmp.seek(self.cursor) diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index b3a1f62be6..051656d27d 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -193,7 +193,7 @@ def test_scalar_range(): assert_equal(r.end, 16) assert_equal(r.step, 4) - fn append_many(inout list: List, *values: list.T): + fn append_many(mut list: List, *values: list.T): for value in values: list.append(value[]) diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo index 7ff2834d46..3c98be083f 100644 --- a/stdlib/test/builtin/test_sort.mojo +++ b/stdlib/test/builtin/test_sort.mojo @@ -45,7 +45,7 @@ fn random_numbers[ return result -# fn assert_sorted[dtype: DType](inout list: List[Scalar[dtype]]) raises: +# fn assert_sorted[dtype: DType](mut list: List[Scalar[dtype]]) raises: # sort[dtype](list) # for i in range(1, len(list)): # assert_true( @@ -53,7 +53,7 @@ fn random_numbers[ # ) -fn assert_sorted_string(inout list: List[String]) raises: +fn assert_sorted_string(mut list: List[String]) raises: for i in range(1, len(list)): assert_true( list[i] >= list[i - 1], str(list[i - 1]) + " > " + str(list[i]) @@ -62,7 +62,7 @@ fn assert_sorted_string(inout list: List[String]) raises: fn assert_sorted[ type: ComparableCollectionElement -](inout list: List[type]) raises: +](mut list: List[type]) raises: for i in range(1, len(list)): assert_true(list[i] >= list[i - 1], "error at index: " + str(i)) diff --git a/stdlib/test/hashlib/test_hasher.mojo b/stdlib/test/hashlib/test_hasher.mojo index 809d494c7e..4c10ac50a5 100644 --- a/stdlib/test/hashlib/test_hasher.mojo +++ b/stdlib/test/hashlib/test_hasher.mojo @@ -30,14 +30,14 @@ struct DummyHasher(_Hasher): fn __init__(out self): self._dummy_value = 0 - fn _update_with_bytes(inout self, data: UnsafePointer[UInt8], length: Int): + fn _update_with_bytes(mut self, data: UnsafePointer[UInt8], length: Int): for i in range(length): self._dummy_value += data[i].cast[DType.uint64]() - fn _update_with_simd(inout self, value: SIMD[_, _]): + fn _update_with_simd(mut self, value: SIMD[_, _]): self._dummy_value += value.cast[DType.uint64]().reduce_add() - fn update[T: _HashableWithHasher](inout self, value: T): + fn update[T: _HashableWithHasher](mut self, value: T): value.__hash__(self) fn finish(owned self) -> UInt64: @@ -48,7 +48,7 @@ struct DummyHasher(_Hasher): struct SomeHashableStruct(_HashableWithHasher): var _value: Int64 - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): hasher._update_with_simd(self._value) @@ -69,7 +69,7 @@ struct ComplexeHashableStruct(_HashableWithHasher): var _value1: SomeHashableStruct var _value2: SomeHashableStruct - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): hasher.update(self._value1) hasher.update(self._value2) @@ -96,10 +96,10 @@ struct ComplexHashableStructWithList(_HashableWithHasher): var _value2: SomeHashableStruct var _value3: List[UInt8] - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): hasher.update(self._value1) hasher.update(self._value2) - # This is okay because self is passed as borrowed so the pointer will + # This is okay because self is passed as read-only so the pointer will # be valid until at least the end of the function hasher._update_with_bytes( data=self._value3.unsafe_ptr(), @@ -115,10 +115,10 @@ struct ComplexHashableStructWithListAndWideSIMD(_HashableWithHasher): var _value3: List[UInt8] var _value4: SIMD[DType.uint32, 4] - fn __hash__[H: _Hasher](self, inout hasher: H): + fn __hash__[H: _Hasher](self, mut hasher: H): hasher.update(self._value1) hasher.update(self._value2) - # This is okay because self is passed as borrowed so the pointer will + # This is okay because self is passed as read-only so the pointer will # be valid until at least the end of the function hasher._update_with_bytes( data=self._value3.unsafe_ptr(), diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index b2f1203332..ec7a71d89e 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -17,42 +17,42 @@ from python import Python, PythonObject from testing import assert_equal -fn test_import(inout python: Python) raises: +fn test_import(mut python: Python) raises: var my_module: PythonObject = Python.import_module("my_module") var py_string = my_module.my_function("Hello") var str = String(python.__str__(py_string)) assert_equal(str, "Formatting the string from Lit with Python: Hello") -fn test_list(inout python: Python) raises: +fn test_list(mut python: Python) raises: var b: PythonObject = Python.import_module("builtins") var my_list = PythonObject([1, 2.34, "False"]) var py_string = str(my_list) assert_equal(py_string, "[1, 2.34, 'False']") -fn test_tuple(inout python: Python) raises: +fn test_tuple(mut python: Python) raises: var b: PythonObject = Python.import_module("builtins") var my_tuple = PythonObject((1, 2.34, "False")) var py_string = str(my_tuple) assert_equal(py_string, "(1, 2.34, 'False')") -fn test_call_ownership(inout python: Python) raises: +fn test_call_ownership(mut python: Python) raises: var obj: PythonObject = [1, "5"] var py_string = str(obj) var string = python.__str__(py_string) assert_equal(string, "[1, '5']") -fn test_getitem_ownership(inout python: Python) raises: +fn test_getitem_ownership(mut python: Python) raises: var obj: PythonObject = [1, "5"] var py_string = str(obj[1]) var string = python.__str__(py_string) assert_equal(string, "5") -fn test_getattr_ownership(inout python: Python) raises: +fn test_getattr_ownership(mut python: Python) raises: var my_module: PythonObject = Python.import_module("my_module") var obj = my_module.Foo(4) var py_string = str(obj.bar) diff --git a/stdlib/test/python/test_python_info.mojo b/stdlib/test/python/test_python_info.mojo index 1728a10c24..b1b7eb29e7 100644 --- a/stdlib/test/python/test_python_info.mojo +++ b/stdlib/test/python/test_python_info.mojo @@ -19,7 +19,7 @@ from python._cpython import PythonVersion from testing import assert_equal -fn test_python_version(inout python: Python) raises: +fn test_python_version(mut python: Python) raises: var version = "3.10.8 (main, Nov 24 2022, 08:08:27) [Clang 14.0.6 ]" var pythonVersion = PythonVersion(version) assert_equal(pythonVersion.major, 3) diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index be71cfd2dc..3e1a465628 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -17,7 +17,7 @@ from python.python import Python, PythonObject, _get_global_python_itf from testing import assert_equal -fn test_execute_python_string(inout python: Python) -> String: +fn test_execute_python_string(mut python: Python) -> String: try: _ = Python.evaluate("print('evaluated by PyRunString')") return str(Python.evaluate("'a' + 'b'")) @@ -25,7 +25,7 @@ fn test_execute_python_string(inout python: Python) -> String: return str(e) -fn test_local_import(inout python: Python) -> String: +fn test_local_import(mut python: Python) -> String: try: var my_module: PythonObject = Python.import_module("my_module") if my_module: @@ -37,7 +37,7 @@ fn test_local_import(inout python: Python) -> String: return str(e) -fn test_dynamic_import(inout python: Python, times: Int = 1) -> String: +fn test_dynamic_import(mut python: Python, times: Int = 1) -> String: alias INLINE_MODULE = """ called_already = False def hello(name): @@ -56,7 +56,7 @@ def hello(name): return str(e) -fn test_call(inout python: Python) -> String: +fn test_call(mut python: Python) -> String: try: var my_module: PythonObject = Python.import_module("my_module") return str( diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index eed042c564..92180fb8d0 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -21,7 +21,7 @@ from testing import assert_equal, assert_false, assert_raises, assert_true from utils import StringRef -def test_dunder_methods(inout python: Python): +def test_dunder_methods(mut python: Python): var a = PythonObject(34) var b = PythonObject(10) diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 0fbf502b7a..d621943f9c 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -17,7 +17,7 @@ from python import Python, PythonObject from testing import assert_equal, assert_false, assert_true -fn test_string_to_python_to_mojo(inout python: Python) raises: +fn test_string_to_python_to_mojo(mut python: Python) raises: var py_string = PythonObject("mojo") var py_string_capitalized = py_string.capitalize() diff --git a/stdlib/test/tempfile/test_tempfile.mojo b/stdlib/test/tempfile/test_tempfile.mojo index c186776e6e..d84ac25554 100644 --- a/stdlib/test/tempfile/test_tempfile.mojo +++ b/stdlib/test/tempfile/test_tempfile.mojo @@ -55,7 +55,7 @@ struct TempEnvWithCleanup: """Function called after the context manager exits if an error occurs.""" fn __init__( - inout self, + mut self, vars_to_set: Dict[String, String], clean_up_function: fn () raises -> None, ): @@ -63,20 +63,20 @@ struct TempEnvWithCleanup: self._vars_back = Dict[String, String]() self.clean_up_function = clean_up_function - def __enter__(inout self): + def __enter__(mut self): for key_value in self.vars_to_set.items(): var key = key_value[].key var value = key_value[].value self._vars_back[key] = os.getenv(key) _ = os.setenv(key, value, overwrite=True) - fn __exit__(inout self): + fn __exit__(mut self): for key_value in self.vars_to_set.items(): var key = key_value[].key var value = key_value[].value _ = os.setenv(key, value, overwrite=True) - def __exit__(inout self, error: Error) -> Bool: + def __exit__(mut self, error: Error) -> Bool: self.__exit__() self.clean_up_function() return False diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index e15d744a88..975d26464b 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -34,7 +34,7 @@ struct Point(Writable, Stringable): var y: Int @no_inline - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write("Point(", self.x, ", ", self.y, ")") @no_inline diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo index c487ea14db..fed40ec33c 100644 --- a/stdlib/test/utils/test_format_to_stdout.mojo +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -26,7 +26,7 @@ struct Point(Writable): var x: Int var y: Int - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write("Point(", self.x, ", ", self.y, ")") From 2f5a1f1d4e9611b930f3cfe09cee9be83d0a1d90 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 1 Dec 2024 07:46:06 -0800 Subject: [PATCH 1949/2019] [******][GPU Enhance FPUtils to support fnuz float types MODULAR_ORIG_COMMIT_REV_ID: 8a2976ab0b99ec2d63ac2c0136bd89b40782b5ac --- stdlib/src/utils/numerics.mojo | 97 ++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index f9a52a9972..f7d99244fe 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -67,9 +67,9 @@ struct FPUtils[ """ @parameter - if type is DType.float8e4m3: + if type in (DType.float8e4m3, DType.float8e4m3fnuz): return 3 - elif type is DType.float8e5m2: + elif type in (DType.float8e5m2, DType.float8e5m2fnuz): return 2 elif type is DType.float16: return 10 @@ -94,8 +94,12 @@ struct FPUtils[ @parameter if type is DType.float8e4m3: return 7 + elif type is DType.float8e4m3fnuz: + return 8 elif type is DType.float8e5m2: return 15 + elif type is DType.float8e5m2fnuz: + return 16 elif type is DType.float16: return 15 elif type is DType.float32 or type is DType.bfloat16: @@ -114,18 +118,7 @@ struct FPUtils[ The min exponent. """ - @parameter - if type is DType.float8e4m3: - return -6 - elif type is DType.float8e5m2: - return -14 - elif type is DType.float16: - return -14 - elif type is DType.float32 or type is DType.bfloat16: - return -126 - else: - constrained[type is DType.float64, "unsupported float type"]() - return -1022 + return -Self.max_exponent() + 1 @staticmethod @always_inline("nodebug") @@ -137,9 +130,9 @@ struct FPUtils[ """ @parameter - if type is DType.float8e4m3: + if type in (DType.float8e4m3, DType.float8e4m3fnuz): return 4 - elif type is DType.float8e5m2: + elif type in (DType.float8e5m2, DType.float8e5m2fnuz): return 5 elif type is DType.float16: return 5 @@ -537,6 +530,15 @@ fn nan[type: DType]() -> Scalar[type]: value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], ]() ) + elif type is DType.float8e5m2fnuz: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"nan"> : !pop.scalar` + ], + ]() + ) elif type is DType.float8e4m3: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ @@ -544,6 +546,15 @@ fn nan[type: DType]() -> Scalar[type]: value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], ]() ) + elif type is DType.float8e4m3fnuz: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"nan"> : !pop.scalar` + ], + ]() + ) elif type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ @@ -600,7 +611,10 @@ fn isnan[ """ @parameter - if not type.is_floating_point(): + if not type.is_floating_point() or type in ( + DType.float8e4m3fnuz, + DType.float8e5m2fnuz, + ): return False alias int_dtype = _integral_type_of[type]() @@ -654,6 +668,15 @@ fn inf[type: DType]() -> Scalar[type]: value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], ]() ) + elif type is DType.float8e5m2fnuz: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"inf"> : !pop.scalar` + ], + ]() + ) elif type is DType.float8e4m3: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ @@ -661,6 +684,15 @@ fn inf[type: DType]() -> Scalar[type]: value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], ]() ) + elif type is DType.float8e4m3fnuz: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"inf"> : !pop.scalar` + ], + ]() + ) elif type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ @@ -721,6 +753,15 @@ fn neg_inf[type: DType]() -> Scalar[type]: value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], ]() ) + elif type is DType.float8e5m2fnuz: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"-inf"> : !pop.scalar` + ], + ]() + ) elif type is DType.float8e4m3: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ @@ -728,6 +769,15 @@ fn neg_inf[type: DType]() -> Scalar[type]: value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], ]() ) + elif type is DType.float8e4m3fnuz: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"-inf"> : !pop.scalar` + ], + ]() + ) elif type is DType.float16: return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( __mlir_op.`kgen.param.constant`[ @@ -801,7 +851,9 @@ fn max_finite[type: DType]() -> Scalar[type]: return 18446744073709551615 elif type is DType.float8e4m3: return 448 - elif type is DType.float8e5m2: + elif type is DType.float8e4m3fnuz: + return 240 + elif type in (DType.float8e5m2, DType.float8e5m2fnuz): return 57344 elif type is DType.float16: return 65504 @@ -932,9 +984,11 @@ fn isinf[ """ @parameter - if not type.is_floating_point(): + if not type.is_floating_point() or type in ( + DType.float8e4m3fnuz, + DType.float8e5m2fnuz, + ): return False - elif type is DType.float8e5m2: # For the float8e5m2 both 7C and FC are infinity. alias int_dtype = _integral_type_of[type]() @@ -1087,10 +1141,11 @@ fn ulp[ constrained[type.is_floating_point(), "the type must be floating point"]() + alias inf_val = SIMD[type, simd_width](inf[type]()) + var nan_mask = isnan(x) var xabs = abs(x) var inf_mask = isinf(xabs) - alias inf_val = SIMD[type, simd_width](inf[type]()) var x2 = nextafter(xabs, inf_val) var x2_inf_mask = isinf(x2) From ce7cb147f2d18a44055d0fab3895845eb4404bac Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 1 Dec 2024 08:16:34 -0800 Subject: [PATCH 1950/2019] [Stdlib] Simplify the code for powf function, NFC MODULAR_ORIG_COMMIT_REV_ID: 60447a21c51b70796fbc62aa27c2760259703809 --- stdlib/src/builtin/simd.mojo | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index a70d660217..bacee6e4bf 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -802,7 +802,7 @@ struct SIMD[type: DType, size: Int]( specified exponent value. """ constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return _pow[type, size, DType.index](self, exp) + return _pow(self, SIMD[DType.index, size](exp)) # TODO(#22771): remove this overload. @always_inline("nodebug") @@ -2952,17 +2952,13 @@ fn _tbl1( @always_inline fn _pow[ - BaseTy: DType, simd_width: Int, ExpTy: DType -](base: SIMD[BaseTy, simd_width], exp: SIMD[ExpTy, simd_width]) -> __type_of( - base -): + simd_width: Int +](base: SIMD[_, simd_width], exp: SIMD[_, simd_width]) -> __type_of(base): """Computes the power of the elements of a SIMD vector raised to the corresponding elements of another SIMD vector. Parameters: - BaseTy: The `dtype` of the `base` SIMD vector. simd_width: The width of the input and output SIMD vectors. - ExpTy: The `dtype` of the `exp` SIMD vector. Args: base: Base of the power operation. @@ -2973,9 +2969,9 @@ fn _pow[ """ @parameter - if ExpTy.is_floating_point() and BaseTy == ExpTy: + if exp.type.is_floating_point() and base.type is exp.type: return _powf(base, exp) - elif ExpTy.is_integral(): + elif exp.type.is_integral(): # Common cases if all(exp == 2): return base * base @@ -3030,12 +3026,15 @@ fn _powf[ @always_inline -fn _powi[type: DType](base: Scalar[type], exp: Int32) -> __type_of(base): +fn _powi(base: Scalar, exp: Int32) -> __type_of(base): + alias type = base.type + if type.is_integral() and exp < 0: # Not defined for Integers, this should raise an # exception. debug_assert(False, "exponent < 0 is undefined for integers") return 0 + var a = base var b = abs(exp) if type.is_floating_point() else exp var res: Scalar[type] = 1 From 03d1d4fbf322bed4e32932562b6861da91f83fe3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 1 Dec 2024 09:22:56 -0800 Subject: [PATCH 1951/2019] [mojo-docs] Update the documentation to new keywords This does a big migration of the documentation over to the new argument convention spellings, and also catches some uses of `inout` in inits that moved to `out`. MODULAR_ORIG_COMMIT_REV_ID: b5562aa2bbcd3260a83e354ee97964c3d58920b0 --- docs/manual/basics.mdx | 2 +- docs/manual/errors.mdx | 14 +++--- docs/manual/functions.mdx | 14 +++--- docs/manual/lifecycle/death.mdx | 2 +- docs/manual/lifecycle/life.mdx | 14 +++--- docs/manual/operators.mdx | 28 +++++------ docs/manual/packages.md | 2 +- docs/manual/parameters/index.mdx | 4 +- docs/manual/pointers.mdx | 2 +- docs/manual/structs.mdx | 6 +-- docs/manual/traits.mdx | 2 +- docs/manual/values/lifetimes.mdx | 6 +-- docs/manual/values/ownership.mdx | 64 +++++++++++++------------- docs/manual/values/value-semantics.mdx | 4 +- stdlib/docs/bencher/Bench.md | 18 ++++---- stdlib/docs/bencher/BenchConfig.md | 2 +- stdlib/docs/bencher/BenchId.md | 4 +- stdlib/docs/bencher/Bencher.md | 10 ++-- stdlib/docs/bencher/BenchmarkInfo.md | 2 +- stdlib/docs/style-guide.md | 2 +- 20 files changed, 101 insertions(+), 101 deletions(-) diff --git a/docs/manual/basics.mdx b/docs/manual/basics.mdx index 2253f8c7c9..938701d322 100644 --- a/docs/manual/basics.mdx +++ b/docs/manual/basics.mdx @@ -95,7 +95,7 @@ passed by reference, the short answer is: `def` functions receive arguments The longer short answer is that Mojo allows you to specify for each argument whether it should be passed by value (as `owned`), or whether it should be -passed by reference (as `borrowed` for an immutable reference, or as `inout` +passed by reference (as `read` for an immutable reference, or as `mut` for a mutable reference). This feature is entwined with Mojo's value ownership model, which protects you diff --git a/docs/manual/errors.mdx b/docs/manual/errors.mdx index c87fab7777..47e1a23711 100644 --- a/docs/manual/errors.mdx +++ b/docs/manual/errors.mdx @@ -298,14 +298,14 @@ import time struct Timer: var start_time: Int - fn __init__(inout self): + fn __init__(out self): self.start_time = 0 - fn __enter__(inout self) -> Self: + fn __enter__(mut self) -> Self: self.start_time = time.perf_counter_ns() return self - fn __exit__(inout self): + fn __exit__(mut self): end_time = time.perf_counter_ns() elapsed_time_ms = round(((end_time - self.start_time) / 1e6), 3) print("Elapsed time:", elapsed_time_ms, "milliseconds") @@ -371,19 +371,19 @@ import time struct ConditionalTimer: var start_time: Int - fn __init__(inout self): + fn __init__(out self): self.start_time = 0 - fn __enter__(inout self) -> Self: + fn __enter__(mut self) -> Self: self.start_time = time.perf_counter_ns() return self - fn __exit__(inout self): + fn __exit__(mut self): end_time = time.perf_counter_ns() elapsed_time_ms = round(((end_time - self.start_time) / 1e6), 3) print("Elapsed time:", elapsed_time_ms, "milliseconds") - fn __exit__(inout self, e: Error) raises -> Bool: + fn __exit__(mut self, e: Error) raises -> Bool: if str(e) == "just a warning": print("Suppressing error:", e) self.__exit__() diff --git a/docs/manual/functions.mdx b/docs/manual/functions.mdx index d35c3cad30..19d4c2e067 100644 --- a/docs/manual/functions.mdx +++ b/docs/manual/functions.mdx @@ -51,7 +51,7 @@ Here's everything to know about `fn`: value). * By default, arguments are received as an immutable reference (values are - read-only, using the `borrowed` [argument + read-only, using the `read` [argument convention](/mojo/manual/values/ownership#argument-conventions)). This prevents accidental mutations, and permits the use of non-copyable types @@ -59,7 +59,7 @@ Here's everything to know about `fn`: If you want a local copy, you can simply assign the value to a local variable. Or, you can get a mutable reference to the value by declaring the - `inout` [argument + `mut` [argument convention](/mojo/manual/values/ownership#argument-conventions)). * If the function raises an exception, it must be explicitly declared with the @@ -106,7 +106,7 @@ Here's everything to know about `def`: `def` function doesn't declare a return type of `None`, it's considered to return an `object` by default.) -* Arguments are mutable. Arguments default to using the `borrowed` +* Arguments are mutable. Arguments default to using the `read` [argument convention](/mojo/manual/values/ownership#argument-conventions) like an `fn` function, with a special addition: if the function mutates the argument, it makes a mutable copy. @@ -179,7 +179,7 @@ fn use_defaults(): ``` However, you cannot define a default value for an argument that's declared as -[`inout`](/mojo/manual/values/ownership#mutable-arguments-inout). +[`mut`](/mojo/manual/values/ownership#mutable-arguments-mut). Any optional arguments must appear after any required arguments. [Keyword-only arguments](#positional-only-and-keyword-only-arguments), discussed later, can @@ -280,7 +280,7 @@ of the value itself. You must add an empty subscript operator `[]` to dereference the reference and retrieve the value: ```mojo -def make_worldly(inout *strs: String): +def make_worldly(mut *strs: String): # Requires extra [] to dereference the reference for now. for i in strs: i[] += " world" @@ -291,7 +291,7 @@ Alternately, subscripting into a `VariadicListMem` returns the argument value, and doesn't require any dereferencing: ```mojo -fn make_worldly(inout *strs: String): +fn make_worldly(mut *strs: String): # This "just works" as you'd expect! for i in range(len(strs)): strs[i] += " world" @@ -415,7 +415,7 @@ There are currently a few limitations: ```mojo # Not supported yet. - fn borrowed_var_kwargs(borrowed **kwargs: Int): ... + fn read_var_kwargs(read **kwargs: Int): ... ``` * All the variadic keyword arguments must have the same type, and this diff --git a/docs/manual/lifecycle/death.mdx b/docs/manual/lifecycle/death.mdx index 4494e8b446..2f7ee808f7 100644 --- a/docs/manual/lifecycle/death.mdx +++ b/docs/manual/lifecycle/death.mdx @@ -353,7 +353,7 @@ struct TwoStrings: # self.str1 has been transferred so it is also destroyed now; # `self.__del__()` is not called (avoiding an infinite loop). - fn dump(inout self): + fn dump(mut self): print('str1:', self.str1) print('str2:', self.str2) diff --git a/docs/manual/lifecycle/life.mdx b/docs/manual/lifecycle/life.mdx index 800a81163d..91646caa5f 100644 --- a/docs/manual/lifecycle/life.mdx +++ b/docs/manual/lifecycle/life.mdx @@ -71,7 +71,7 @@ var mine = MyPet("Loki", 4) ``` An instance of `MyPet` can also be -[borrowed](/mojo/manual/values/ownership#borrowed-arguments-borrowed) +[read](/mojo/manual/values/ownership#read-arguments-read) and destroyed, but it currently can't be copied or moved. We believe this is a good default starting point, because there are no built-in @@ -99,8 +99,8 @@ example, you might want a default constructor that sets some default values and takes no arguments, and then additional constructors that accept more arguments. Just be aware that, in order to modify any fields, each constructor must -declare the `self` argument with the [`inout` -convention](/mojo/manual/values/ownership#mutable-arguments-inout). If you +declare the `self` argument with the [`out` +convention](/mojo/manual/values/ownership#mutable-arguments-mut). If you want to call one constructor from another, you simply call upon that constructor as you would externally (you don't need to pass `self`). @@ -287,7 +287,7 @@ mistakes when referring to the current struct name. Also, notice that the `existing` argument in `__copyinit__()` is immutable because the default [argument convention](/mojo/manual/values/ownership#argument-conventions) is -`borrowed`—this is a good thing because this function should not modify the +`read`—this is a good thing because this function should not modify the contents of the value being copied. ::: @@ -337,7 +337,7 @@ struct HeapArray: (self.data + i).destroy_pointee() self.data.free() - fn append(inout self, val: Int): + fn append(mut self, val: Int): # Update the array for demo purposes if self.size < self.cap: (self.data + self.size).init_pointee_copy(val) @@ -478,7 +478,7 @@ struct HeapArray: (self.data + i).destroy_pointee() self.data.free() - fn append(inout self, val: Int): + fn append(mut self, val: Int): # Update the array for demo purposes if self.size < self.cap: (self.data + self.size).init_pointee_copy(val) @@ -529,7 +529,7 @@ values that are expensive to copy. To go further and ensure your type can never be copied, you can make it "move-only" by implementing `__moveinit__()` and *excluding* `__copyinit__()`. A move-only type can be passed to other variables and passed into functions -with any argument convention (`borrowed`, `inout`, and `owned`)—the only catch +with any argument convention (`read`, `mut`, and `owned`)—the only catch is that you must use the `^` transfer sigil to end the lifetime of a move-only type when assigning it to a new variable or when passing it as an `owned` argument. diff --git a/docs/manual/operators.mdx b/docs/manual/operators.mdx index e8a72e4a8c..a11cbd2cd8 100644 --- a/docs/manual/operators.mdx +++ b/docs/manual/operators.mdx @@ -849,10 +849,10 @@ struct MyInt: def __radd__(self, lhs: Int) -> Self: return MyInt(self.value + lhs) - def __iadd__(inout self, rhs: MyInt) -> None: + def __iadd__(mut self, rhs: MyInt) -> None: self.value += rhs.value - def __iadd__(inout self, rhs: Int) -> None: + def __iadd__(mut self, rhs: Int) -> None: self.value += rhs ``` @@ -953,7 +953,7 @@ struct MySeq[type: CollectionElement]: fn __getitem__(self, idx: Int) -> type: # Return element at the given index ... - fn __setitem__(inout self, idx: Int, value: type): + fn __setitem__(mut self, idx: Int, value: type): # Assign the element at the given index the provided value ``` @@ -1116,7 +1116,7 @@ struct Complex( fn __str__(self) -> String: return String.write(self) - fn write_to[W: Writer](self, inout writer: W): + fn write_to[W: Writer](self, mut writer: W): writer.write("(", self.re) if self.im < 0: writer.write(" - ", -self.im) @@ -1169,7 +1169,7 @@ this example. else: raise "index out of bounds" - def __setitem__(inout self, idx: Int, value: Float64) -> None: + def __setitem__(mut self, idx: Int, value: Float64) -> None: if idx == 0: self.re = value elif idx == 1: @@ -1372,44 +1372,44 @@ f1 / c1 = (-0.068665598535133904 - 0.37193865873197529i) Now let's implement support for the in-place assignment operators: `+=`, `-=`, `*=`, and `/=`. These modify the original value, so we need to mark `self` as -being an `inout` argument and update the `re` and `im` fields instead of +being an `mut` argument and update the `re` and `im` fields instead of returning a new `Complex` instance. And once again, we'll overload the definitions to support both a `Complex` and a `Float64` operand. ```mojo # ... - def __iadd__(inout self, rhs: Self) -> None: + def __iadd__(mut self, rhs: Self) -> None: self.re += rhs.re self.im += rhs.im - def __iadd__(inout self, rhs: Float64) -> None: + def __iadd__(mut self, rhs: Float64) -> None: self.re += rhs - def __isub__(inout self, rhs: Self) -> None: + def __isub__(mut self, rhs: Self) -> None: self.re -= rhs.re self.im -= rhs.im - def __isub__(inout self, rhs: Float64) -> None: + def __isub__(mut self, rhs: Float64) -> None: self.re -= rhs - def __imul__(inout self, rhs: Self) -> None: + def __imul__(mut self, rhs: Self) -> None: new_re = self.re * rhs.re - self.im * rhs.im new_im = self.re * rhs.im + self.im * rhs.re self.re = new_re self.im = new_im - def __imul__(inout self, rhs: Float64) -> None: + def __imul__(mut self, rhs: Float64) -> None: self.re *= rhs self.im *= rhs - def __itruediv__(inout self, rhs: Self) -> None: + def __itruediv__(mut self, rhs: Self) -> None: denom = rhs.squared_norm() new_re = (self.re * rhs.re + self.im * rhs.im) / denom new_im = (self.im * rhs.re - self.re * rhs.im) / denom self.re = new_re self.im = new_im - def __itruediv__(inout self, rhs: Float64) -> None: + def __itruediv__(mut self, rhs: Float64) -> None: self.re /= rhs self.im /= rhs ``` diff --git a/docs/manual/packages.md b/docs/manual/packages.md index ec7f1b55ea..1b1b6927e0 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -23,7 +23,7 @@ struct MyPair: var first: Int var second: Int - fn __init__(inout self, first: Int, second: Int): + fn __init__(out self, first: Int, second: Int): self.first = first self.second = second diff --git a/docs/manual/parameters/index.mdx b/docs/manual/parameters/index.mdx index 788c87977a..be9066c974 100644 --- a/docs/manual/parameters/index.mdx +++ b/docs/manual/parameters/index.mdx @@ -402,7 +402,7 @@ struct MyStruct: fn __init__(out self): pass - fn foo(inout self): + fn foo(mut self): print("calling instance method") @staticmethod @@ -411,7 +411,7 @@ struct MyStruct: fn test_static_overload(): var a = MyStruct() - # `foo(inout self)` takes precedence over a static method. + # `foo(mut self)` takes precedence over a static method. a.foo() ``` diff --git a/docs/manual/pointers.mdx b/docs/manual/pointers.mdx index 37f0a84977..57482a3781 100644 --- a/docs/manual/pointers.mdx +++ b/docs/manual/pointers.mdx @@ -525,6 +525,6 @@ differences from `UnsafePointer` which make it safer: while the pointer exists. The `Pointer` type shouldn't be confused with the immutable and mutable -references used with the `borrowed` and `inout` argument conventions. Those +references used with the `read` and `mut` argument conventions. Those references do not require explicit dereferencing, unlike a `Pointer` or `UnsafePointer`. diff --git a/docs/manual/structs.mdx b/docs/manual/structs.mdx index 59acf95f66..92f0fdc1c0 100644 --- a/docs/manual/structs.mdx +++ b/docs/manual/structs.mdx @@ -50,8 +50,8 @@ struct MyPair: self.second = second ``` -Notice that the first argument in the `__init__()` method is `inout self`. For -now, ignore `inout` (it's an [argument +Notice that the first argument in the `__init__()` method is `out self`. For +now, ignore `out` (it's an [argument convention](/mojo/manual/values/ownership#argument-conventions) that declares `self` as a mutable reference); all you need to know right now is that `self` must be the first argument. It references the current struct instance @@ -310,7 +310,7 @@ When you add the `@value` decorator, Mojo synthesizes each special method above only if it doesn't exist already. That is, you can still implement a custom version of each method. -In addition to the `inout` argument convention you already saw with +In addition to the `out` argument convention you already saw with `__init__()`, this code also introduces `owned`, which is another argument convention that ensures the argument has unique ownership of the value. For more detail, see the section about [value diff --git a/docs/manual/traits.mdx b/docs/manual/traits.mdx index 4e1ae7b321..99da582c4b 100644 --- a/docs/manual/traits.mdx +++ b/docs/manual/traits.mdx @@ -441,7 +441,7 @@ struct Dog(Stringable, Representable, Writable): fn __str__(self) -> String: return String.write(self) - fn write_to[W: Writer](self, inout writer: W) -> None: + fn write_to[W: Writer](self, mut writer: W) -> None: writer.write("Dog(", self.name, ", ", self.age, ")") var dog = Dog("Rex", 5) diff --git a/docs/manual/values/lifetimes.mdx b/docs/manual/values/lifetimes.mdx index 24ff2b7982..39c2402f43 100644 --- a/docs/manual/values/lifetimes.mdx +++ b/docs/manual/values/lifetimes.mdx @@ -205,8 +205,8 @@ You can use the `ref` keyword with arguments and return values to specify a reference with parametric mutability. That is, they can be either mutable or immutable. -From inside the called function, a `ref` argument looks like a `borrowed` or -`inout` argument. +From inside the called function, a `ref` argument looks like a `read` or +`mut` argument. A `ref` return value looks like any other return value to the calling function, but it is a *reference* to an existing value, not a copy. @@ -282,7 +282,7 @@ Mutable: Goodbye ### `ref` return values Like `ref` arguments, `ref` return values allow a function to return a mutable -or immutable reference to a value. Like a `borrowed` or `inout` argument, these +or immutable reference to a value. Like a `read` or `mut` argument, these references don't need to be dereferenced. `ref` return values can be an efficient way to handle updating items in a diff --git a/docs/manual/values/ownership.mdx b/docs/manual/values/ownership.mdx index 5748afe073..d45ecc7c70 100644 --- a/docs/manual/values/ownership.mdx +++ b/docs/manual/values/ownership.mdx @@ -65,11 +65,11 @@ An argument convention specifies whether an argument is mutable or immutable, and whether the function owns the value. Each convention is defined by a keyword at the beginning of an argument declaration: -* `borrowed`: The function receives an **immutable reference**. This means the +* `read`: The function receives an **immutable reference**. This means the function can read the original value (it is *not* a copy), but it cannot mutate (modify) it. `def` functions treat this differently, as described below. -* `inout`: The function receives a **mutable reference**. This means the +* `mut`: The function receives a **mutable reference**. This means the function can read and mutate the original value (it is *not* a copy). * `owned`: The function takes **ownership** of a value. This means the function @@ -80,7 +80,7 @@ keyword at the beginning of an argument declaration: * `ref`: The function gets a reference with an parametric mutability: that is, the reference can be either mutable or immutable. You can think of `ref` - arguments as a generalization of the `borrowed` and `inout` conventions. + arguments as a generalization of the `read` and `mut` conventions. `ref` arguments are an advanced topic, and they're described in more detail in [Lifetimes and references](/mojo/manual/values/lifetimes). @@ -88,7 +88,7 @@ For example, this function has one argument that's a mutable reference and one that's immutable: ```mojo -fn add(inout x: Int, borrowed y: Int): +fn add(mut x: Int, read y: Int): x += y fn main(): @@ -99,14 +99,14 @@ fn main(): ``` You've probably already seen some function arguments that don't declare a -convention. by default, all arguments are `borrowed`. +convention. by default, all arguments are `read`. In the following sections, we'll explain each of these argument conventions in more detail. -## Borrowed arguments (`borrowed`) +## Borrowed arguments (`read`) -The `borrowed` convention is the default for all arguments. But `def` and `fn` -functions treat `borrowed` arguments somewhat differently: +The `read` convention is the default for all arguments. But `def` and `fn` +functions treat `read` arguments somewhat differently: * In a [`def` function](/mojo/manual/functions#def-functions), if you mutate the value in the body of the function, the function receives a mutable copy of @@ -119,7 +119,7 @@ functions treat `borrowed` arguments somewhat differently: assign it to a local variable: ```mojo - var my_copy = borrowed_arg + var my_copy = read_arg ``` In both cases, the original value on the caller side can't be changed by the @@ -142,7 +142,7 @@ print_list(list) [1, 2, 3, 4] ``` -Here the `list` argument to `print_list()` is borrowed and not mutated, so the +Here the `list` argument to `print_list()` is read and not mutated, so the `print_list()` function gets an immutable reference to the original `List`, and doesn't do any copying. @@ -152,9 +152,9 @@ and destructor are not invoked for a borrow. ### Compared to C++ and Rust -Mojo's borrowed argument convention is similar in some ways to passing an +Mojo's read argument convention is similar in some ways to passing an argument by `const&` in C++, which also avoids a copy of the value and disables -mutability in the callee. However, the borrowed convention differs from +mutability in the callee. However, the read convention differs from `const&` in C++ in two important ways: * The Mojo compiler implements a lifetime checker that ensures that values are @@ -174,10 +174,10 @@ passing small values, and Rust defaults to moving values instead of passing them around by borrow. These policy and syntax decisions allow Mojo to provide an easier-to-use programming model. -## Mutable arguments (`inout`) +## Mutable arguments (`mut`) -If you'd like your function to receive a **mutable reference**, add the `inout` -keyword in front of the argument name. You can think of `inout` like this: it +If you'd like your function to receive a **mutable reference**, add the `mut` +keyword in front of the argument name. You can think of `mut` like this: it means any changes to the value *in*side the function are visible *out*side the function. @@ -186,7 +186,7 @@ For example, this `mutate()` function updates the original `list` value: ```mojo from collections import List -def mutate(inout l: List[Int]): +def mutate(mut l: List[Int]): l.append(5) var list = List(1, 2, 3, 4) @@ -217,18 +217,18 @@ print_list(list) [1, 2, 3, 4, 5] ``` -Although the code using `inout` isn't that much shorter, it's more memory +Although the code using `mut` isn't that much shorter, it's more memory efficient because it does not make a copy of the value. -However, remember that the values passed as `inout` must already be mutable. -For example, if you try to take a `borrowed` value and pass it to another -function as `inout`, you'll get a compiler error because Mojo can't form a +However, remember that the values passed as `mut` must already be mutable. +For example, if you try to take a `read` value and pass it to another +function as `mut`, you'll get a compiler error because Mojo can't form a mutable reference from an immutable reference. :::note You cannot define [default -values](/mojo/manual/functions#optional-arguments) for `inout` +values](/mojo/manual/functions#optional-arguments) for `mut` arguments. ::: @@ -236,14 +236,14 @@ arguments. ### Argument exclusivity Mojo enforces *argument exclusivity* for mutable references. This means that if -a function receives a mutable reference to a value (such as an `inout` argument), +a function receives a mutable reference to a value (such as an `mut` argument), it can't receive any other references to the same value—mutable or immutable. That is, a mutable reference can't have any other references that *alias* it. For example, consider the following code example: ```mojo -fn append_twice(inout s: String, other: String): +fn append_twice(mut s: String, other: String): # Mojo knows 's' and 'other' cannot be the same string. s += other s += other @@ -251,8 +251,8 @@ fn append_twice(inout s: String, other: String): fn invalid_access(): var my_string = str("o") - # error: passing `my_string` inout is invalid since it is also passed - # borrowed. + # error: passing `my_string` mut is invalid since it is also passed + # read. append_twice(my_string, my_string) print(my_string) ``` @@ -374,7 +374,7 @@ destroyed at the end of the function. ```mojo from collections import List -def add_to_list(owned name: String, inout list: List[String]): +def add_to_list(owned name: String, mut list: List[String]): list.append(name^) # name is uninitialized, nothing to destroy @@ -429,16 +429,16 @@ function is essentially just sugaring for the `fn` function: [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn` requires all types be explicitly declared). -* A `def` function can treat a `borrowed` argument as mutable (in which case it +* A `def` function can treat a `read` argument as mutable (in which case it receives a mutable copy). An `fn` function must make this copy explicitly. For example, these two functions have the exact same behavior. ```mojo -def def_example(a: Int, inout b: Int, owned c): +def def_example(a: Int, mut b: Int, owned c): pass -fn fn_example(a_in: Int, inout b: Int, owned c: object): +fn fn_example(a_in: Int, mut b: Int, owned c: object): var a = a_in pass ``` @@ -450,12 +450,12 @@ copying large numbers of strings.) ### Borrowed versus owned in `def` functions -The difference between `borrowed` and `owned` in a `def` function may be a +The difference between `read` and `owned` in a `def` function may be a little subtle. In both cases, you can end up with a uniquely-owned value that's a copy of the original value. -* The `borrowed` argument always gets an immutable reference or a local copy. - You can't transfer a value into a `borrowed` argument. +* The `read` argument always gets an immutable reference or a local copy. + You can't transfer a value into a `read` argument. * The `owned` argument always gets a uniquely owned value, which may have been copied or transferred from the callee. Using `owned` arguments without the diff --git a/docs/manual/values/value-semantics.mdx b/docs/manual/values/value-semantics.mdx index 403db993b6..58b1896f2c 100644 --- a/docs/manual/values/value-semantics.mdx +++ b/docs/manual/values/value-semantics.mdx @@ -120,9 +120,9 @@ and only make a copy when it's explicitly requested. The arguments above are mutable because a [`def` function](/mojo/manual/functions#def-functions) has special treatment for the default -[`borrowed` argument convention](/mojo/manual/values/ownership#argument-conventions). +[`read` argument convention](/mojo/manual/values/ownership#argument-conventions). -Whereas, `fn` functions always receive `borrowed` arguments as immutable +Whereas, `fn` functions always receive `read` arguments as immutable references. This is a memory optimization to avoid making unnecessary copies. diff --git a/stdlib/docs/bencher/Bench.md b/stdlib/docs/bencher/Bench.md index 1e71a29c53..19133b572f 100644 --- a/stdlib/docs/bencher/Bench.md +++ b/stdlib/docs/bencher/Bench.md @@ -33,7 +33,7 @@ Defines the main Benchmark struct which executes a Benchmark and print result.
    ```mojo -__init__(inout self: Self, config: Optional[BenchConfig] = #kgen.none, mode: Mode = 0) +__init__(out self: Self, config: Optional[BenchConfig] = #kgen.none, mode: Mode = 0) ```
    @@ -56,7 +56,7 @@ Constructs a Benchmark object based on specific configuration and mode.
    ```mojo -bench_with_input[T: AnyType, bench_fn: fn(inout Bencher, $0) capturing -> None](inout self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) +bench_with_input[T: AnyType, bench_fn: fn(mut Bencher, $0) capturing -> None](mut self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) ```
    @@ -66,7 +66,7 @@ Benchmarks an input function with input args of type AnyType. **Parameters:** - ​T (`AnyType`): Benchmark function input type. -- ​bench_fn (`fn(inout Bencher, $0) capturing -> None`): The function to +- ​bench_fn (`fn(mut Bencher, $0) capturing -> None`): The function to be benchmarked. **Args:** @@ -83,7 +83,7 @@ Benchmarks an input function with input args of type AnyType.
    ```mojo -bench_with_input[T: AnyTrivialRegType, bench_fn: fn(inout Bencher, $0) capturing -> None](inout self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) +bench_with_input[T: AnyTrivialRegType, bench_fn: fn(mut Bencher, $0) capturing -> None](mut self: Self, bench_id: BenchId, input: T, throughput_elems: Optional[Int] = #kgen.none) ```
    @@ -93,7 +93,7 @@ Benchmarks an input function with input args of type AnyTrivialRegType. **Parameters:** - ​T (`AnyTrivialRegType`): Benchmark function input type. -- ​bench_fn (`fn(inout Bencher, $0) capturing -> None`): The function to +- ​bench_fn (`fn(mut Bencher, $0) capturing -> None`): The function to be benchmarked. **Args:** @@ -112,7 +112,7 @@ Benchmarks an input function with input args of type AnyTrivialRegType.
    ```mojo -bench_function[bench_fn: fn(inout Bencher) capturing -> None](inout self: Self, bench_id: BenchId, throughput_elems: Optional[Int] = #kgen.none) +bench_function[bench_fn: fn(mut Bencher) capturing -> None](mut self: Self, bench_id: BenchId, throughput_elems: Optional[Int] = #kgen.none) ```
    @@ -121,7 +121,7 @@ Benchmarks or Tests an input function. **Parameters:** -- ​bench_fn (`fn(inout Bencher) capturing -> None`): The function to be +- ​bench_fn (`fn(mut Bencher) capturing -> None`): The function to be benchmarked. **Args:** @@ -137,7 +137,7 @@ Benchmarks or Tests an input function.
    ```mojo -bench_function[bench_fn: fn(inout Bencher) raises capturing -> None](inout self: Self, bench_id: BenchId, throughput_elems: Optional[Int] = #kgen.none) +bench_function[bench_fn: fn(mut Bencher) raises capturing -> None](mut self: Self, bench_id: BenchId, throughput_elems: Optional[Int] = #kgen.none) ```
    @@ -146,7 +146,7 @@ Benchmarks or Tests an input function. **Parameters:** -- ​bench_fn (`fn(inout Bencher) raises capturing -> None`): The function +- ​bench_fn (`fn(mut Bencher) raises capturing -> None`): The function to be benchmarked. **Args:** diff --git a/stdlib/docs/bencher/BenchConfig.md b/stdlib/docs/bencher/BenchConfig.md index cebb46f13f..8a83984d5e 100644 --- a/stdlib/docs/bencher/BenchConfig.md +++ b/stdlib/docs/bencher/BenchConfig.md @@ -48,7 +48,7 @@ frequency.
    ```mojo -__init__(inout self: out_file: Optional[Path] = None, min_runtime_secs: SIMD[float64, 1] = 1.0, max_runtime_secs: SIMD[float64, 1] = 2.0, min_warmuptime_secs: SIMD[float64, 1] = 1.0, max_batch_size: Int = 0, max_iters: Int = 1000000000, num_repetitions: Int = 1, flush_denormals: Bool = True) +__init__(out self: out_file: Optional[Path] = None, min_runtime_secs: SIMD[float64, 1] = 1.0, max_runtime_secs: SIMD[float64, 1] = 2.0, min_warmuptime_secs: SIMD[float64, 1] = 1.0, max_batch_size: Int = 0, max_iters: Int = 1000000000, num_repetitions: Int = 1, flush_denormals: Bool = True) ```
    diff --git a/stdlib/docs/bencher/BenchId.md b/stdlib/docs/bencher/BenchId.md index e305f890fe..0d215bd972 100644 --- a/stdlib/docs/bencher/BenchId.md +++ b/stdlib/docs/bencher/BenchId.md @@ -30,7 +30,7 @@ execution.
    -`__init__(inout self: Self, func_name: String, input_id: String)` +`__init__(out self: Self, func_name: String, input_id: String)`
    @@ -47,7 +47,7 @@ Constructs a Benchmark Id object from input function name and Id phrase.
    -`__init__(inout self: Self, func_name: String)` +`__init__(out self: Self, func_name: String)`
    diff --git a/stdlib/docs/bencher/Bencher.md b/stdlib/docs/bencher/Bencher.md index 4270a7a714..91d68e80d8 100644 --- a/stdlib/docs/bencher/Bencher.md +++ b/stdlib/docs/bencher/Bencher.md @@ -30,7 +30,7 @@ Defines a Bencher struct which facilitates the timing of a target function.
    -`__init__(inout self: Self, num_iters: Int)` +`__init__(out self: Self, num_iters: Int)`
    @@ -48,7 +48,7 @@ Constructs a Bencher object to run and time a function.
    -`iter[iter_fn: fn() capturing -> None](inout self: Self)` +`iter[iter_fn: fn() capturing -> None](mut self: Self)`
    @@ -65,7 +65,7 @@ of times.
    -`iter[iter_fn: fn() raises capturing -> None](inout self: Self)` +`iter[iter_fn: fn() raises capturing -> None](mut self: Self)`
    @@ -85,7 +85,7 @@ of times.
    -`iter_custom[iter_fn: fn(Int) capturing -> Int](inout self: Self)` +`iter_custom[iter_fn: fn(Int) capturing -> Int](mut self: Self)`
    @@ -101,7 +101,7 @@ Times a target function with custom number of iterations.
    -`iter_custom[iter_fn: fn(Int) raises capturing -> Int](inout self: Self)` +`iter_custom[iter_fn: fn(Int) raises capturing -> Int](mut self: Self)`
    diff --git a/stdlib/docs/bencher/BenchmarkInfo.md b/stdlib/docs/bencher/BenchmarkInfo.md index 88eafec487..52b2922fa0 100644 --- a/stdlib/docs/bencher/BenchmarkInfo.md +++ b/stdlib/docs/bencher/BenchmarkInfo.md @@ -33,7 +33,7 @@ Defines a Benchmark Info struct to record execution Statistics.
    -`__init__(inout self: Self, name: String, result: Report, elems: Optional[Int])` +`__init__(out self: Self, name: String, result: Report, elems: Optional[Int])`
    diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index f1c135cf4f..dc07d62088 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -283,7 +283,7 @@ the same type: ```mojo struct MyStruct: # Invoked as `MyStruct(other)` - fn __init__(inout self, other: Self): + fn __init__(out self, other: Self): # do a deep copy of MyStruct ``` From 12719581338dbbd7bab8ef9eded6466199388112 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 1 Dec 2024 09:29:25 -0800 Subject: [PATCH 1952/2019] [mojo-lang][KGEN] Rename argument convention enums This renames the ArgConvention enums over to follow the new naming in the Mojo source. This also makes a variety of changes to improve consistency in the codebase, cleaning up old remnants of 'inout' etc. MODULAR_ORIG_COMMIT_REV_ID: 117874f7161ed1df3aa1db967cd42678c6e358a2 --- proposals/improved-hash-module.md | 44 ++++++++++++------------- proposals/opt-in-implicit-conversion.md | 2 +- proposals/remove-let-decls.md | 2 +- stdlib/src/builtin/builtin_list.mojo | 4 +-- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/proposals/improved-hash-module.md b/proposals/improved-hash-module.md index c2f9c460fb..ee2bd138f5 100644 --- a/proposals/improved-hash-module.md +++ b/proposals/improved-hash-module.md @@ -62,16 +62,16 @@ of the `Hashable` trait. ```mojo trait Hasher: """Trait which every hash function implementer needs to implement.""" - fn __init__(inout self): + fn __init__(out self): """Expects a no argument instantiation.""" ... - fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + fn _update_with_bytes(mut self, bytes: DTypePointer[DType.uint8], n: Int): """Conribute to the hash value based on a sequence of bytes. Use only for complex types which are not just a composition of Hashable types.""" ... - fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + fn _update_with_simd[dt: DType, size: Int](mut self, value: SIMD[dt, size]): """Contribute to the hash value with a compile time know fix size value. Used inside of std lib to avoid runtime branching.""" ... - fn update[T: Hashable](inout self, value: T): + fn update[T: Hashable](mut self, value: T): """Contribute to the hash value with a Hashable value. Should be used by implementors of Hashable types which are a composition of Hashable types.""" ... fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: @@ -93,13 +93,13 @@ Below you can see a dummy implementation of a `DefaultHasher` struct DefaultHasher(Hasher): var hash: UInt64 - fn __init__(inout self): + fn __init__(out self): self.hash = 42 - fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + fn _update_with_bytes(mut self, bytes: DTypePointer[DType.uint8], n: Int): ... - fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + fn _update_with_simd[dt: DType, size: Int](mut self, value: SIMD[dt, size]): ... - fn update[T: Hashable](inout self, value: T): + fn update[T: Hashable](mut self, value: T): ... fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: return self.hash.cast[dt]() @@ -112,7 +112,7 @@ data flow paradigm instead of call return. ```mojo trait Hashable: - fn hash_with[H: Hasher](self, inout hasher: H): + fn hash_with[H: Hasher](self, mut hasher: H): ... ``` @@ -128,7 +128,7 @@ struct Person(Hashable): var name: String var age: Int - fn __hash__[H: Hasher](self, inout hasher: H): + fn __hash__[H: Hasher](self, mut hasher: H): hasher.update(self.name) hasher.update(self.age) ``` @@ -155,21 +155,21 @@ from random import random_si64 trait Hashable: """Trait which every hashable type needs to implement.""" - fn __hash__[H: Hasher](self, inout hasher: H): + fn __hash__[H: Hasher](self, mut hasher: H): ... trait Hasher: """Trait which every hash function implementer needs to implement.""" - fn __init__(inout self): + fn __init__(out self): """Expects a no argument instantiation.""" ... - fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + fn _update_with_bytes(mut self, bytes: DTypePointer[DType.uint8], n: Int): """Conribute to the hash value based on a sequence of bytes. Use only for complex types which are not just a composition of Hashable types.""" ... - fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + fn _update_with_simd[dt: DType, size: Int](mut self, value: SIMD[dt, size]): """Contribute to the hash value with a compile time know fix size value. Used inside of std lib to avoid runtime branching.""" ... - fn update[T: Hashable](inout self, value: T): + fn update[T: Hashable](mut self, value: T): """Contribute to the hash value with a Hashable value. Should be used by implementors of Hashable types which are a composition of Hashable types.""" ... fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: @@ -184,7 +184,7 @@ struct MyInt(Hashable): var value: Int @always_inline - fn __hash__[H: Hasher](self, inout hasher: H): + fn __hash__[H: Hasher](self, mut hasher: H): hasher._update_with_simd(Int64(self.value)) @value @@ -193,7 +193,7 @@ struct MyString(Hashable): var value: StringLiteral @always_inline - fn __hash__[H: Hasher](self, inout hasher: H): + fn __hash__[H: Hasher](self, mut hasher: H): hasher.update(MyInt(len(self.value))) hasher._update_with_bytes(self.value.data().bitcast[DType.uint8](), len(self.value)) @@ -203,7 +203,7 @@ struct Person(Hashable): var name: MyString var age: MyInt - fn __hash__[H: Hasher](self, inout hasher: H): + fn __hash__[H: Hasher](self, mut hasher: H): hasher.update(self.name) hasher.update(self.age) @@ -235,7 +235,7 @@ struct DJBX33A_Hasher[custom_secret: UInt64 = 0](Hasher): var secret: UInt64 @always_inline - fn __init__(inout self): + fn __init__(out self): self.hash_data = 5361 @parameter if custom_secret != 0: @@ -244,13 +244,13 @@ struct DJBX33A_Hasher[custom_secret: UInt64 = 0](Hasher): self.secret = _DJBX33A_SECRET() @always_inline - fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + fn _update_with_bytes(mut self, bytes: DTypePointer[DType.uint8], n: Int): """The algorithm is not optimal.""" for i in range(n): self.hash_data = self.hash_data * 33 + bytes.load(i).cast[DType.uint64]() @always_inline - fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + fn _update_with_simd[dt: DType, size: Int](mut self, value: SIMD[dt, size]): """The algorithm is not optimal.""" alias size_in_bytes = size * dt.sizeof() var bytes = bitcast[DType.uint8, size_in_bytes](value) @@ -259,7 +259,7 @@ struct DJBX33A_Hasher[custom_secret: UInt64 = 0](Hasher): self.hash_data = self.hash_data * 33 + bytes[i].cast[DType.uint64]() @always_inline - fn update[T: Hashable](inout self, value: T): + fn update[T: Hashable](mut self, value: T): value.__hash__(self) @always_inline diff --git a/proposals/opt-in-implicit-conversion.md b/proposals/opt-in-implicit-conversion.md index ea3858da06..e2d7dc7e81 100644 --- a/proposals/opt-in-implicit-conversion.md +++ b/proposals/opt-in-implicit-conversion.md @@ -69,7 +69,7 @@ let foo: Foo = 10 ```python struct Foo: @implicit_conversion - fn __init__(inout self, i: Int): + fn __init__(out self, i: Int): pass var foo: Foo = 10 diff --git a/proposals/remove-let-decls.md b/proposals/remove-let-decls.md index ec09b19359..34f8eb3b7c 100644 --- a/proposals/remove-let-decls.md +++ b/proposals/remove-let-decls.md @@ -68,7 +68,7 @@ a great way to define defaulted field values, e.g.: struct Thing: # This is not actually supported right now, but imagine it were. let field = 42 - fn __init__(inout self): + fn __init__(out self): self.field = 17 # shouldn't be able to overwrite field? ``` diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 0272fbc2d6..be5ddaaa05 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -311,7 +311,7 @@ struct VariadicListMem[ alias reference_type = Pointer[element_type, origin] alias _mlir_ref_type = Self.reference_type._mlir_type alias _mlir_type = __mlir_type[ - `!kgen.variadic<`, Self._mlir_ref_type, `, borrow_in_mem>` + `!kgen.variadic<`, Self._mlir_ref_type, `, read_mem>` ] var value: Self._mlir_type @@ -343,7 +343,7 @@ struct VariadicListMem[ # automatically be inferred to be mutable, and the !kgen.variadic will have # convention=mut. alias _inout_variadic_type = __mlir_type[ - `!kgen.variadic<`, Self._mlir_ref_type, `, inout>` + `!kgen.variadic<`, Self._mlir_ref_type, `, mut>` ] @always_inline From 7347db552cee27c0ea644026ca9f7ba9427434f8 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 1 Dec 2024 15:05:58 -0800 Subject: [PATCH 1953/2019] [******][GPU] Introduce a new GridIdx operation MODULAR_ORIG_COMMIT_REV_ID: 5ffe1df491c0c3f092ed645ffbe0756ebceeec04 --- stdlib/src/math/math.mojo | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 0c06a6b00a..2c7ee78a5d 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -1154,6 +1154,23 @@ fn fma(a: Int, b: Int, c: Int) -> Int: return a * b + c +@always_inline +fn fma(a: UInt, b: UInt, c: UInt) -> UInt: + """Performs `fma` (fused multiply-add) on the inputs. + + The result is `(a * b) + c`. + + Args: + a: The first input. + b: The second input. + c: The third input. + + Returns: + `(a * b) + c`. + """ + return a * b + c + + @always_inline("nodebug") fn fma[ type: DType, simd_width: Int From 24d4af8620b9bd3cc0cefbbc7a94b88112397d2c Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 2 Dec 2024 13:00:47 -0700 Subject: [PATCH 1954/2019] [stdlib] Simplify `String.split` with isspace Now that `StringSlice.isspace()` exists, use it in `String.split` to avoid the `str(...)` calls. MODULAR_ORIG_COMMIT_REV_ID: 9cdd0d76aa059456be6bcdf79301e09bcf2730ec --- stdlib/src/collections/string.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index d75d2098cd..a6051bbb81 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1816,7 +1816,7 @@ struct String( # Python adds all "whitespace chars" as one separator # if no separator was specified for s in self[lhs:]: - if not str(s).isspace(): # TODO: with StringSlice.isspace() + if not s.isspace(): break lhs += s.byte_length() # if it went until the end of the String, then @@ -1830,7 +1830,7 @@ struct String( break rhs = lhs + num_bytes(self.unsafe_ptr()[lhs]) for s in self[lhs + num_bytes(self.unsafe_ptr()[lhs]) :]: - if str(s).isspace(): # TODO: with StringSlice.isspace() + if s.isspace(): break rhs += s.byte_length() From f16419eeef01d9b81718d61e1a16d3c41a8aa18f Mon Sep 17 00:00:00 2001 From: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:39:52 -0800 Subject: [PATCH 1955/2019] [External] [stdlib] Optimize `_StringSliceIter` to not have branching in forward iteration (#51441) [External] [stdlib] Optimize `_StringSliceIter` to not have branching in forward iteration Optimize `_StringSliceIter` to not have branching in forward iteration. This also fixes an error in the iterator's `__len__` logic which assumed it would be called only at the beginning of the iteration Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3546 MODULAR_ORIG_COMMIT_REV_ID: 0919af33312b082a4bac7d1547a193fba378e56f --- stdlib/src/utils/string_slice.mojo | 58 ++++++++++-------------- stdlib/test/collections/test_string.mojo | 30 ++++++------ 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 48be446549..666a25c45c 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -80,13 +80,9 @@ fn _utf8_first_byte_sequence_length(b: Byte) -> Int: debug_assert( (b & 0b1100_0000) != 0b1000_0000, - ( - "Function `_utf8_first_byte_sequence_length()` does not work" - " correctly if given a continuation byte." - ), + "Function does not work correctly if given a continuation byte.", ) - var flipped = ~b - return int(count_leading_zeros(flipped) + (flipped >> 7)) + return int(count_leading_zeros(~b)) + int(b < 0b1000_0000) fn _shift_unicode_to_utf8(ptr: UnsafePointer[UInt8], c: Int, num_bytes: Int): @@ -186,17 +182,13 @@ struct _StringSliceIter[ """ var index: Int - var continuation_bytes: Int - var ptr: UnsafePointer[UInt8] + var ptr: UnsafePointer[Byte] var length: Int - fn __init__(mut self, *, unsafe_pointer: UnsafePointer[UInt8], length: Int): + fn __init__(mut self, *, unsafe_pointer: UnsafePointer[Byte], length: Int): self.index = 0 if forward else length self.ptr = unsafe_pointer self.length = length - alias S = Span[Byte, StaticConstantOrigin] - var s = S(ptr=self.ptr, length=self.length) - self.continuation_bytes = _count_utf8_continuation_bytes(s) fn __iter__(self) -> Self: return self @@ -204,26 +196,14 @@ struct _StringSliceIter[ fn __next__(mut self) -> StringSlice[origin]: @parameter if forward: - var byte_len = 1 - if self.continuation_bytes > 0: - var byte_type = _utf8_byte_type(self.ptr[self.index]) - if byte_type != 0: - byte_len = int(byte_type) - self.continuation_bytes -= byte_len - 1 + byte_len = _utf8_first_byte_sequence_length(self.ptr[self.index]) + i = self.index self.index += byte_len - return StringSlice[origin]( - ptr=self.ptr + (self.index - byte_len), length=byte_len - ) + return StringSlice[origin](ptr=self.ptr + i, length=byte_len) else: - var byte_len = 1 - if self.continuation_bytes > 0: - var byte_type = _utf8_byte_type(self.ptr[self.index - 1]) - if byte_type != 0: - while byte_type == 1: - byte_len += 1 - var b = self.ptr[self.index - byte_len] - byte_type = _utf8_byte_type(b) - self.continuation_bytes -= byte_len - 1 + byte_len = 1 + while _utf8_byte_type(self.ptr[self.index - byte_len]) == 1: + byte_len += 1 self.index -= byte_len return StringSlice[origin]( ptr=self.ptr + self.index, length=byte_len @@ -231,14 +211,26 @@ struct _StringSliceIter[ @always_inline fn __has_next__(self) -> Bool: - return self.__len__() > 0 + @parameter + if forward: + return self.index < self.length + else: + return self.index > 0 fn __len__(self) -> Int: @parameter if forward: - return self.length - self.index - self.continuation_bytes + remaining = self.length - self.index + cont = _count_utf8_continuation_bytes( + Span[Byte, ImmutableAnyOrigin]( + ptr=self.ptr + self.index, length=remaining + ) + ) + return remaining - cont else: - return self.index - self.continuation_bytes + return self.index - _count_utf8_continuation_bytes( + Span[Byte, ImmutableAnyOrigin](ptr=self.ptr, length=self.index) + ) @value diff --git a/stdlib/test/collections/test_string.mojo b/stdlib/test/collections/test_string.mojo index c70ff3a50c..da23f5d9c7 100644 --- a/stdlib/test/collections/test_string.mojo +++ b/stdlib/test/collections/test_string.mojo @@ -1273,19 +1273,23 @@ def test_string_iter(): var idx = -1 vs = String("mojo🔥") - for item in vs: - idx += 1 - if idx == 0: - assert_equal("m", item) - elif idx == 1: - assert_equal("o", item) - elif idx == 2: - assert_equal("j", item) - elif idx == 3: - assert_equal("o", item) - elif idx == 4: - assert_equal("🔥", item) - assert_equal(4, idx) + var iterator = vs.__iter__() + assert_equal(5, len(iterator)) + var item = iterator.__next__() + assert_equal("m", item) + assert_equal(4, len(iterator)) + item = iterator.__next__() + assert_equal("o", item) + assert_equal(3, len(iterator)) + item = iterator.__next__() + assert_equal("j", item) + assert_equal(2, len(iterator)) + item = iterator.__next__() + assert_equal("o", item) + assert_equal(1, len(iterator)) + item = iterator.__next__() + assert_equal("🔥", item) + assert_equal(0, len(iterator)) var items = List[String]( "mojo🔥", From 95046bc2f7c90a684cf84319a7ece6b82610339b Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 2 Dec 2024 14:15:19 -0800 Subject: [PATCH 1956/2019] [******][GPU] Disable print functionality on AMD GPUs MODULAR_ORIG_COMMIT_REV_ID: c167530a4bb665c5176661e38257dcd419203882 --- stdlib/src/builtin/io.mojo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 259f065987..c0b55f993e 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -266,6 +266,12 @@ fn print[ flush: If set to true, then the stream is forcibly flushed. file: The output stream. """ + + # TODO(MSTDL-1027): Print on AMD GPUs is not implemented yet. + @parameter + if is_amd_gpu(): + return + write_buffered[buffer_size=4096](file, values, sep=sep, end=end) @parameter From ec43232d2e69e009fc28f7d6f76c8c52590dda99 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 2 Dec 2024 18:33:08 -0800 Subject: [PATCH 1957/2019] [mojo-stdlib] Minor cleanups for `Tuple`, NFC. These are just minor improvements to `Tuple` from a walkthrough with @VerdagonModular. MODULAR_ORIG_COMMIT_REV_ID: 352c9d3bc6494f4696a405bf3d992cfced5dce89 --- stdlib/src/builtin/tuple.mojo | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 50fdf9f708..00bede563f 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -81,8 +81,10 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): UnsafePointer.address_of(self[i]) ) - # Mark the elements as destroyed. - storage._is_owned = False + # Do not destroy the elements when 'storage' goes away. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(storage) + ) fn __del__(owned self): """Destructor that destroys all of the elements.""" @@ -126,6 +128,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): UnsafePointer.address_of(existing[i]).move_pointee_into( UnsafePointer.address_of(self[i]) ) + # Note: The destructor on `existing` is auto-disabled in a moveinit. @always_inline @staticmethod From f1fe997c79cd5917431e88b7473b73269cd06718 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 2 Dec 2024 23:15:51 -0800 Subject: [PATCH 1958/2019] [******][GPU] Simplify definition of sleep function, NFC MODULAR_ORIG_COMMIT_REV_ID: 9f1a40a5d1c09bcaf5a9dade8491f28052905d42 --- stdlib/src/time/time.mojo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 7b68624fdf..71eb1e80c2 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -25,6 +25,7 @@ from sys import ( external_call, is_amd_gpu, is_nvidia_gpu, + is_gpu, llvm_intrinsic, os_is_linux, os_is_windows, @@ -383,7 +384,7 @@ fn sleep(sec: Int): """ @parameter - if is_nvidia_gpu() or is_amd_gpu(): + if is_gpu(): return sleep(Float64(sec)) @parameter From dca52e6b2d697c3ebd6b55112f021a422384acc0 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 3 Dec 2024 01:16:17 -0800 Subject: [PATCH 1959/2019] [******] Adjust the section comments MODULAR_ORIG_COMMIT_REV_ID: d1ddd74230122ada93e1237fa6e6e5a495964b16 --- .../algorithm/bench_elementwise.mojo | 4 +- stdlib/benchmarks/builtin/bench_int.mojo | 8 +- stdlib/benchmarks/builtin/bench_sort.mojo | 24 +- stdlib/benchmarks/collections/bench_dict.mojo | 24 +- .../benchmarks/collections/bench_string.mojo | 40 +- stdlib/benchmarks/hashlib/bench_hash.mojo | 600 +++++++++--------- stdlib/benchmarks/math/bench_math.mojo | 20 +- stdlib/benchmarks/utils/bench_formatter.mojo | 12 +- stdlib/benchmarks/utils/bench_memmem.mojo | 16 +- stdlib/docs/style-guide.md | 4 +- stdlib/src/base64/base64.mojo | 20 +- stdlib/src/bit/bit.mojo | 48 +- stdlib/src/builtin/_stubs.mojo | 8 +- stdlib/src/builtin/builtin_list.mojo | 12 +- stdlib/src/builtin/dtype.mojo | 12 +- stdlib/src/builtin/error.mojo | 4 +- stdlib/src/builtin/float_literal.mojo | 4 +- stdlib/src/builtin/format_int.mojo | 16 +- stdlib/src/builtin/int.mojo | 8 +- stdlib/src/builtin/int_literal.mojo | 12 +- stdlib/src/builtin/sort.mojo | 20 +- stdlib/src/builtin/string_literal.mojo | 4 +- stdlib/src/builtin/tuple.mojo | 4 +- stdlib/src/builtin/uint.mojo | 8 +- stdlib/src/collections/deque.mojo | 4 +- stdlib/src/collections/inline_array.mojo | 4 +- stdlib/src/collections/inline_list.mojo | 4 +- stdlib/src/collections/list.mojo | 4 +- stdlib/src/collections/optional.mojo | 8 +- stdlib/src/collections/vector.mojo | 8 +- stdlib/src/math/polynomial.mojo | 8 +- stdlib/src/memory/memory.mojo | 28 +- stdlib/src/memory/pointer.mojo | 8 +- stdlib/src/memory/unsafe.mojo | 4 +- stdlib/src/os/atomic.mojo | 4 +- stdlib/src/sys/_assembly.mojo | 4 +- stdlib/src/sys/_libc.mojo | 16 +- stdlib/src/sys/ffi.mojo | 12 +- stdlib/src/sys/info.mojo | 4 +- stdlib/src/sys/intrinsics.mojo | 36 +- stdlib/src/time/time.mojo | 28 +- stdlib/src/utils/format.mojo | 12 +- stdlib/src/utils/index.mojo | 20 +- stdlib/src/utils/inline_string.mojo | 8 +- stdlib/src/utils/lock.mojo | 4 +- stdlib/src/utils/loop.mojo | 20 +- stdlib/src/utils/static_tuple.mojo | 8 +- stdlib/src/utils/string_slice.mojo | 4 +- stdlib/src/utils/stringref.mojo | 8 +- stdlib/src/utils/write.mojo | 10 +- stdlib/test/collections/test_deque.mojo | 8 +- 51 files changed, 609 insertions(+), 609 deletions(-) diff --git a/stdlib/benchmarks/algorithm/bench_elementwise.mojo b/stdlib/benchmarks/algorithm/bench_elementwise.mojo index 322b66ad9c..2a302ec80f 100644 --- a/stdlib/benchmarks/algorithm/bench_elementwise.mojo +++ b/stdlib/benchmarks/algorithm/bench_elementwise.mojo @@ -23,9 +23,9 @@ from buffer import Buffer from utils.index import Index, IndexList -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark elementwise -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_elementwise[n: Int](mut b: Bencher) raises: var vector = Buffer[DType.index, n].stack_allocation() diff --git a/stdlib/benchmarks/builtin/bench_int.mojo b/stdlib/benchmarks/builtin/bench_int.mojo index b65cd5abaf..2d65c690c3 100644 --- a/stdlib/benchmarks/builtin/bench_int.mojo +++ b/stdlib/benchmarks/builtin/bench_int.mojo @@ -17,9 +17,9 @@ from benchmark import Bench, BenchConfig, Bencher, BenchId -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmarks -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_stringify_small_integers(mut b: Bencher) raises: @always_inline @@ -32,9 +32,9 @@ fn bench_stringify_small_integers(mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_stringify_small_integers]( diff --git a/stdlib/benchmarks/builtin/bench_sort.mojo b/stdlib/benchmarks/builtin/bench_sort.mojo index 0872012f78..a44fc3a150 100644 --- a/stdlib/benchmarks/builtin/bench_sort.mojo +++ b/stdlib/benchmarks/builtin/bench_sort.mojo @@ -26,9 +26,9 @@ from stdlib.builtin.sort import ( sort, ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Utils -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -78,9 +78,9 @@ fn heap_sort[type: DType](mut list: List[Scalar[type]]): _heap_sort[_less_than](list) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark sort functions with a tiny list size -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn bench_tiny_list_sort[type: DType](mut m: Bench) raises: @@ -157,9 +157,9 @@ fn bench_tiny_list_sort[type: DType](mut m: Bench) raises: ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark sort functions with a small list size -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn bench_small_list_sort[type: DType](mut m: Bench, count: Int) raises: @@ -209,9 +209,9 @@ fn bench_small_list_sort[type: DType](mut m: Bench, count: Int) raises: ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark sort functions with a large list size -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn bench_large_list_sort[type: DType](mut m: Bench, count: Int) raises: @@ -262,9 +262,9 @@ fn bench_large_list_sort[type: DType](mut m: Bench, count: Int) raises: ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark sort functions with low delta lists -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn bench_low_cardinality_list_sort(mut m: Bench, count: Int, delta: Int) raises: @@ -314,9 +314,9 @@ fn bench_low_cardinality_list_sort(mut m: Bench, count: Int, delta: Int) raises: ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): diff --git a/stdlib/benchmarks/collections/bench_dict.mojo b/stdlib/benchmarks/collections/bench_dict.mojo index b50d97a98d..e93406f837 100644 --- a/stdlib/benchmarks/collections/bench_dict.mojo +++ b/stdlib/benchmarks/collections/bench_dict.mojo @@ -24,9 +24,9 @@ from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from bit import bit_ceil -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Data -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn make_dict[size: Int]() -> Dict[Int, Int]: var d = Dict[Int, Int]() for i in range(0, size): @@ -34,9 +34,9 @@ fn make_dict[size: Int]() -> Dict[Int, Int]: return d -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Dict init -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_dict_init(mut b: Bencher) raises: @always_inline @@ -50,9 +50,9 @@ fn bench_dict_init(mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Dict Insert -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_dict_insert[size: Int](mut b: Bencher) raises: """Insert 100 new items.""" @@ -68,9 +68,9 @@ fn bench_dict_insert[size: Int](mut b: Bencher) raises: keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Dict Lookup -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_dict_lookup[size: Int](mut b: Bencher) raises: """Lookup 100 items.""" @@ -96,9 +96,9 @@ fn bench_dict_lookup[size: Int](mut b: Bencher) raises: keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Dict Memory Footprint -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn total_bytes_used(items: Dict[Int, Int]) -> Int: @@ -121,9 +121,9 @@ fn total_bytes_used(items: Dict[Int, Int]) -> Int: return amnt_bytes -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): seed() var m = Bench(BenchConfig(num_repetitions=1)) diff --git a/stdlib/benchmarks/collections/bench_string.mojo b/stdlib/benchmarks/collections/bench_string.mojo index 85ee5050ee..3cee895b73 100644 --- a/stdlib/benchmarks/collections/bench_string.mojo +++ b/stdlib/benchmarks/collections/bench_string.mojo @@ -25,9 +25,9 @@ from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run from utils._utf8_validation import _is_valid_utf8 -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Data -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn make_string[ length: UInt = 0 ](filename: StringLiteral = "UN_charter_EN.txt") -> String: @@ -61,9 +61,9 @@ fn make_string[ return abort[String]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string init -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_init(mut b: Bencher) raises: @always_inline @@ -76,9 +76,9 @@ fn bench_string_init(mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string count -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_count[ length: UInt = 0, @@ -97,9 +97,9 @@ fn bench_string_count[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string split -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_split[ length: UInt = 0, @@ -124,9 +124,9 @@ fn bench_string_split[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string splitlines -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_splitlines[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" @@ -143,9 +143,9 @@ fn bench_string_splitlines[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string lower -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_lower[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" @@ -162,9 +162,9 @@ fn bench_string_lower[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string upper -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_upper[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" @@ -181,9 +181,9 @@ fn bench_string_upper[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string replace -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_replace[ length: UInt = 0, @@ -203,9 +203,9 @@ fn bench_string_replace[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark string _is_valid_utf8 -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_string_is_valid_utf8[ length: UInt = 0, filename: StringLiteral = "UN_charter_EN" @@ -222,9 +222,9 @@ fn bench_string_is_valid_utf8[ keep(bool(items)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): seed() var m = Bench(BenchConfig(num_repetitions=5)) diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index 0c6873f109..291701710d 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -49,7 +49,7 @@ alias words_ar = """ تحت, الأشياء, معه, يريد, أننا, أنظر, لما, اعرف, إلي, ثلاثة, انتظر, الرجال, الذين, حصلت, أني, سعيد, لابد, عزيزتي, الشيء, فكرة, انهم, الله, الباب, سيدى, دائما, رأيت, مشكلة, استطيع, تكن, تذهب, ليلة, شيئ, أظن, طوال, - جميل, وهو, الشرطة, او, دولار, السيارة, وهذا, كبير, مني, بسرعة, النار, الأمور, سمعت, أشعر, يعرف, + جميل, وهو, الشرطة, او, دولار, السيارة, وهذا, كبير, مني, بسرعة, النار, الأمور, سمعت, أشعر, يعرف, أعني, لدى, بهذه, أحب, سنوات, بأس, الأفضل, بالنسبة, أنتم, عظيم, يقول, جميلة, جون, جاك, بسبب, الوحيد, أمر, بل, بالفعل, الشخص, الي, دعني, خارج, اجل, الخير, ــ, حالك, للغاية, فحسب, كانوا, أردت, فتاة, بشأن, يعني, كبيرة, ترى, آسفة, دقيقة, أنهم, يستطيع, احد, بأنك, تعمل, @@ -80,7 +80,7 @@ alias words_ar = """ غدا, ظننت, ولن, المرأة, لهذه, تحرك, يهم, تبقى, الطبيب, اسم, انظري, تبا, أتذكر, فترة, ساعات, تفكر, تحصل, بأي, النقود, لعبة, زوجتي, الكلام, ستفعل, أسف, فهو, الملك, مدينة, بكم, الوحيدة, أمام, عدد, اخرج, بول, سأعود, جئت, لأني, تحدث, السلامة, الماضية, أمك, اعتقدت, مره, مساء, بطريقة, الرب, ابدا, أهذا, وفي, وكل, أتيت, منكم, - انتهى, بوب, بعيدا, ضع, وجود, تعود, زلت, اللعينة, نقوم, كلنا, أحصل, يريدون, تأخذ, المحتمل, الشمس, بدأ, + انتهى, بوب, بعيدا, ضع, وجود, تعود, زلت, اللعينة, نقوم, كلنا, أحصل, يريدون, تأخذ, المحتمل, الشمس, بدأ, ارجوك, المسيح, جاء, كهذا, سنذهب, تعالى, إثنان, فعلا, حتي, سيحدث, الجيد, وشك, القادم, معرفة, صورة, أعود, اسمي, طلب, آنسة, الثانية, فقدت, حفلة, تنظر, مثير, اننى, وصلت, أنتظر, السماء, يقولون, الهراء, معهم, ابي, وعندما, مجموعة, العاهرة, ماري, حسن, الزواج, نحو, دعيني, الجديدة, مهم, أمس, اتصل, ابتعد, هراء, ستة, @@ -91,7 +91,7 @@ alias words_ar = """ الدخول, جين, امرأة, متأكدة, هيه, تخبرني, مدى, إلهى, احب, عما, نرى, بيننا, تعيش, قتلت, الأحمق, تشارلي, بيل, عليكم, سؤال, طلبت, الهواء, وهذه, صوت, انتم, ميلاد, ماكس, - تعتقدين, الحديث, الجانب, صديقك, ذا, خطر, أطلق, الشارع, عملية, ببعض, تتكلم, مختلف, تحمل, مساعدة, + تعتقدين, الحديث, الجانب, صديقك, ذا, خطر, أطلق, الشارع, عملية, ببعض, تتكلم, مختلف, تحمل, مساعدة, بضعة, المناسب, المنطقة, قم, بالداخل, البداية, لأجل, زوجتك, مقابل, يحب, هاري, ممتاز, قريبا, سنكون, فعلته, بتلك, التفكير, أسفل, للعمل, العجوز, امي, الكلب, انتظري, مازال, إننا, اشعر, الجيش, شرطة """ @@ -256,13 +256,13 @@ alias words_he = """ לגרום, המשחק, שרה, לעצמך, במיוחד, המשטרה, צוות, אחזור, שאמרתי, גברים, קורא, בראש, רחוק, למקום, לשלם, להפסיק, מיוחד, הז, שמו, שמחה, כיף, אגיד, למי, ניתן, מאחורי, תמשיך, כיצד, להוציא, מתים, כולכם, אצל, חבל, האישה, לעצמי, גברתי, תוכלי, רואים, דוד, להציל, שצריך, - בעלי, דוקטור, חג, לעבודה, בוודאי, תעשי, הוד, מילה, ברצינות, הארץ, עשינו, לאנשים, רצה, + בעלי, דוקטור, חג, לעבודה, בוודאי, תעשי, הוד, מילה, ברצינות, הארץ, עשינו, לאנשים, רצה, עזוב, יצא, נתן, שניות, בעיר, סי, חשבת, שאלות, אלינו, ידע, תנו, לשים, שאולי, בכך, יכולת, אן, היד, שאוכל, מין, דקה, לדאוג, שמה, תרצה, ראה, הצילו, נוסף, החרא, אופן, כשהוא, צעיר, הפה, עולה, עובדת, שמך, לתפוס, נמצאת, כלבה, האקדח, עדיף, הטלפון, טום, פול, חכו, קר, תלך, במקרה, יעשה, שניכם, הארי, זוז, יקירתי, בהצלחה, לשבת, אנא, דין, מכיוון, יד, הקטנה, לבן, בנו, בעצמי, יין, תוריד, - למישהו, מייק, מול, נזוז, ככל, הלוואי, בעצמך, לרגע, קשור, בשקט, האל, ישנה, מעמד, כזאת, + למישהו, מייק, מול, נזוז, ככל, הלוואי, בעצמך, לרגע, קשור, בשקט, האל, ישנה, מעמד, כזאת, רד, אחורה, איכפת, איתם, ממנה, חם, מבקש, שש, מידע, השנה, אכן, אהבתי, בשעה, בסוף, שקרה, לכו, אליה, לבחור, תחשוב, ספק, המים, הפנים, לכולם, תדאגי, קחי, שתוק, לברוח, מתוק, ארלי, התיק, שים, מישהי, לקרות, לטפל, לחפש, הידיים, ח, במצב, ואל @@ -270,313 +270,313 @@ alias words_he = """ # Source: https://www.101languages.net/latvian/most-common-latvian-words/ alias words_lv = """ - ir, es, un, tu, tas, ka, man, to, vai, ko, ar, kas, par, tā, kā, viņš, uz, no, tev, - mēs, nav, jūs, bet, labi, jā, lai, nē, mani, ja, bija, viņa, esmu, viņu, tevi, esi, - mums, tad, tikai, ne, viņi, kad, jums, arī, viss, nu, kur, pie, jau, tik, tur, te, vēl, - būs, visu, šeit, tagad, kaut, ļoti, pēc, viņam, taču, savu, gan, paldies, būtu, mūsu, - šo, lūdzu, mans, kāpēc, kungs, kāds, varbūt, tās, jūsu, cik, ak, daudz, jo, esam, - zinu, mana, zini, visi, būt, tam, šī, var, līdz, viens, pa, pat, esat, nekad, domāju, - nezinu, vairs, tiešām, tie, vien, kurš, varētu, dievs, neesmu, prom, tieši, kādu, aiziet, - šis, manu, protams, vajag, neko, vienkārši, tāpēc, gribu, varu, nāc, atpakaļ, mūs, - kārtībā, iet, kopā, viņiem, pats, pirms, domā, vienmēr, gribi, nekas, bez, tava, - vienu, ej, viņai, vairāk, notiek, nevaru, pret, tavs, teica, tavu, biju, dēļ, viņas, - laiku, neviens, kādēļ, vari, labāk, patīk, dari, mājās, nebija, cilvēki, ārā, viņus, - ejam, kāda, piedod, laikam, atkal, šķiet, trīs, sevi, ser, laiks, laika, nekā, manis, - iekšā, labs, tāds, darīt, harij, nevar, viena, lieliski, kuru, šīs, sauc, šurp, teicu, - laikā, tos, pagaidi, neesi, tevis, draugs, pārāk, tēvs, šodien, teikt, dienu, visiem, - tātad, notika, hei, zināt, bijis, sveiks, atvainojiet, tika, naudu, varam, savas, citu, - tādu, manas, redzi, šajā, kam, tajā, jābūt, vecīt, tiem, runā, cilvēku, taisnība, saka, - visus, mīlu, lietas, grib, tēt, izskatās, tiek, noteikti, nozīmē, kamēr, divi, it, tāpat, - tāda, ilgi, katru, dēls, noticis, jauki, redzēt, pareizi, lūk, kundze, aiz, iespējams, - pateikt, nebūtu, gandrīz, vīrs, cilvēks, ātri, žēl, pasaules, rokas, liekas, palīdzēt, - līdzi, visas, saki, negribu, vietā, gadus, starp, skaties, tomēr, tūlīt, džek, nevajag, - sev, vajadzētu, būšu, dzīvi, droši, gadu, priekšu, skaidrs, gribēju, nāk, paskaties, mazliet, - tikko, nebūs, augšā, ceru, joprojām, nevis, ātrāk, ļauj, gribētu, liels, zina, vārdu, reizi, - pasaulē, savā, sveiki, dienas, miris, dod, priekšā, galā, klau, cilvēkiem, tavas, patiesībā, - visa, vārds, gatavs, durvis, velns, nedaudz, naudas, redzēju, velna, manā, drīz, pāri, dzīve, - vēlies, nemaz, priekš, bērni, vieta, pāris, darbu, vajadzīgs, tālāk, rīt, roku, klāt, grūti, - beidz, laba, klausies, dara, varat, sveika, biji, vismaz, kopš, redzu, saproti, kura, draugi, - zemes, šovakar, patiešām, kaa, vietu, dieva, vajadzēja, mašīnu, lejā, saku, ceļu, gada, tādēļ, - cauri, runāt, ņem, oh, divas, lieta, tikt, šie, teici, vēlāk, vaļā, nogalināt, redzējis, jāiet, - nespēju, savus, atceries, ūdens, šejienes, labu, diena, mīļā, atvaino, doties, atrast, saprotu, - abi, reiz, jādara, nesaprotu, meitene, darbs, nevari, tai, nedomāju, pilnīgi, nakti, nekādu, - pati, gadiem, vēlos, taa, kādas, cits, ejiet, pirmais, a, būsi, mamma, lietu, slikti, pašu, - acis, diezgan, pasaki, gadā, puiši, asv, sava, nost, cilvēkus, džeks, manuprāt, mājas, o, - bērns, leo, otru, nopietni, vecais, laukā, caur, dzīves, izdarīt, sieviete, vienalga, - nogalināja, dzīvo, kādreiz, čau, sirds, paliec, gribat, vēlreiz, kuras, mazais, vietas, - piedodiet, laipni, palikt, brauc, ei, the, paliek, apkārt, sievietes, tālu, garām, pirmo, - dzīvot, nāciet, runāju, kuri, tiks, jüs, ceļā, nauda, nevienam, māja, vienīgais, īsti, - sapratu, gluži, svarīgi, atvainojos, i, sen, iespēja, tavā, pavisam, nāves, māte, citi, - viegli, zem, notiks, darba, nepatīk, daži, galvu, dienā, hallo, bērnu, neesam, kungi, beidzot, - nedrīkst, vajadzēs, māju, sieva, kādam, puika, kļūst, prieks, esot, iesim, daļa, pasaule, - pietiek, visā, saviem, rīta, pagaidiet, tētis, mājā, mieru, vīru, palīdzību, dzirdēju, - tādas, dzīvs, strādā, tām, vēlas, nakts, īpaši, jūtos, nolādēts, meitenes, pusi, mammu, mees, - aizveries, vispār, dzīvību, kurā, kādā, vārdā, mašīna, būsim, vispirms, vinji, nevienu, šos, - tiksimies, džeik, vinjsh, vaina, turpini, kādi, jaunu, tuvu, atradu, vēlu, varēja, citādi, šim, - satikt, neuztraucies, pārliecināts, liec, diez, liela, doktor, nevaram, palīdzi, uzmanīgi, dažas, - šiem, atgriezies, gribēja, priecājos, parasti, valsts, asinis, tēti, you, mierā, piemēram, - jautājums, atā, bijām, zemē, pasauli, spēlē, blakus, izskaties, pirmā, nomira, paši, šobrīd, - daru, gaida, tādi, iešu, labākais, jauks, maz, pieder, jauns, nezināju, uzmanību, skaista, - prātā, brālis, patiesību, mierīgi, šai, dr, patiesi, jēzus, mārtij, zināju, suns, juus, sievu, - dzirdi, tepat, mamm, tēvu, tēva, frodo, sasodīts, desmit, stundas, tavi, mazā, džon, cita, - vajadzīga, forši, minūtes, mīlestība, nebiju, saprast, izbeidz, šoreiz, labā, dāmas, kurienes, - problēma, šādi, spēj, gadījumā, tiesa, kuģi, pēdējā, tici, esiet, atceros, katrs, nee, palīgā, - mister, liek, likās, domāt, vīri, pēdējo, traks, reizes, vienīgā, tiesības, skolā, turies, beigas, - karš, pīter, uguni, pietiks, vienam, vienā, pakaļ, jauna, zemi, puisis, ziniet, negribi, labrīt, - ap, cilvēka, draugu, atver, nezini, sāra, vēlaties, gadi, dažreiz, rokās, dabūt, nomierinies, - istabā, agrāk, ieroci, savām, meiteni, paņem, meklē, pār, seju, ziņu, dzirdējis, zinām, gatavi, - braukt, sāka, sāk, dievam, neesat, dzirdēt, spēle, bērniem, izdarīja, muļķības, doma, pēdējais, - dīvaini, atdod, ziņas, bankas, darāt, vakar, ceļš, neviena, brāli, otrā, atgriezties, galvas, - pietiekami, gulēt, uzreiz, iespēju, bijusi, karalis, bobij, šrek, tikpat, palīdziet, durvīm, - vecāki, atrodas, smieklīgi, kuģa, bail, godīgi, pēkšņi, nedēļas, māsa, skrien, ceļa, džeims, gars, - lielu, mašīnā, bojā, kurieni, ļaudis, dārgais, vecs, ūdeni, kūper, eju, mašīnas, ideja, kājas, - spēles, galvenais, citiem, jātiek, skaisti, nāvi, vinju, problēmas, vērts, drīkstu, domājat, visur, - bieži, manai, citas, apsolu, zelta, strādāju, dzimšanas, jūtu, naktī, dārgā, atbildi, noticēt, - klājas, izdevās, dok, redzat, gana, divus, ģimene, runa, stāsts, braucam, brīnišķīgi, ģimenes, - kuģis, čārlij, hey, kä, sheit, ved, atrada, mirusi, meita, paklau, nevēlos, bērnus, boss, kaptein, - nekāda, roze, nespēj, vīrietis, brīdi, īsts, dzīvē, tādā, manī, jūras, jaunkundz, iemesls, sakot, - manam, daudzi, varēsi, pateicos, jaunais, policija, pilnībā, nekur, jauka, nedari, kurus, zināms, - jautājumu, seko, re, padomā, pusē, visām, mīļais, dolāru, gadžet, katram, izdarīji, šīm, vienīgi, - mirt, apmēram, spēku, jauno, mr, celies, iepriekš, prātu, vēlētos, četri, lietām, redzēji, nevajadzētu, - donna, jaa, ticu, minūtēm, sievieti, nāve, jūties, nezina, parādi, malā, redz, uh, gredzenu, uzmanies, - kara, drošībā, sapnis, bijāt, grāmatu, slepkava, vinja, paga, pieci, pilsētā, drošs, pateikšu, gāja, - spēli, beigās, hanna, princese, jebkad, dakter, veids, palīdzība, stāstu, izmantot, spēlēt, gaisā, - darīšu, došos, dodas, kreisi, negribēju, mazāk, pastāsti, tak, devās, sirdi, misis, vis, patiesība, - veidā, harijs, cenšos, tuvāk, kurp, klausieties, sāp, ļaujiet, neticami, kungu, sīkais, iedomāties, - daļu, mazs, iedod, mazo, meklēju, parunāt, jādodas, sevis, pārējie, veicas, otra, mīlestību, zēns, - dodies, galam, sem, bīstami, zvēru, iespējas, maza, ellē, virs, nekādas, maniem, skatieties, šonakt, - svēto, kapteinis, iepazīties, pazīstu, turp, gredzens, nepareizi, lieliska, īstais, pagaidām, kājām, - mirklīti, pašlaik, d, poter, saprati, aprunāties, paša, šejieni, interesanti, nevarētu, pašā, paskat, - bailes, skolas, vārdus, aizmirsti, gaismas, kāp, zēni, darīsim, pašam, beidzies, sauca, māti, akmens, + ir, es, un, tu, tas, ka, man, to, vai, ko, ar, kas, par, tā, kā, viņš, uz, no, tev, + mēs, nav, jūs, bet, labi, jā, lai, nē, mani, ja, bija, viņa, esmu, viņu, tevi, esi, + mums, tad, tikai, ne, viņi, kad, jums, arī, viss, nu, kur, pie, jau, tik, tur, te, vēl, + būs, visu, šeit, tagad, kaut, ļoti, pēc, viņam, taču, savu, gan, paldies, būtu, mūsu, + šo, lūdzu, mans, kāpēc, kungs, kāds, varbūt, tās, jūsu, cik, ak, daudz, jo, esam, + zinu, mana, zini, visi, būt, tam, šī, var, līdz, viens, pa, pat, esat, nekad, domāju, + nezinu, vairs, tiešām, tie, vien, kurš, varētu, dievs, neesmu, prom, tieši, kādu, aiziet, + šis, manu, protams, vajag, neko, vienkārši, tāpēc, gribu, varu, nāc, atpakaļ, mūs, + kārtībā, iet, kopā, viņiem, pats, pirms, domā, vienmēr, gribi, nekas, bez, tava, + vienu, ej, viņai, vairāk, notiek, nevaru, pret, tavs, teica, tavu, biju, dēļ, viņas, + laiku, neviens, kādēļ, vari, labāk, patīk, dari, mājās, nebija, cilvēki, ārā, viņus, + ejam, kāda, piedod, laikam, atkal, šķiet, trīs, sevi, ser, laiks, laika, nekā, manis, + iekšā, labs, tāds, darīt, harij, nevar, viena, lieliski, kuru, šīs, sauc, šurp, teicu, + laikā, tos, pagaidi, neesi, tevis, draugs, pārāk, tēvs, šodien, teikt, dienu, visiem, + tātad, notika, hei, zināt, bijis, sveiks, atvainojiet, tika, naudu, varam, savas, citu, + tādu, manas, redzi, šajā, kam, tajā, jābūt, vecīt, tiem, runā, cilvēku, taisnība, saka, + visus, mīlu, lietas, grib, tēt, izskatās, tiek, noteikti, nozīmē, kamēr, divi, it, tāpat, + tāda, ilgi, katru, dēls, noticis, jauki, redzēt, pareizi, lūk, kundze, aiz, iespējams, + pateikt, nebūtu, gandrīz, vīrs, cilvēks, ātri, žēl, pasaules, rokas, liekas, palīdzēt, + līdzi, visas, saki, negribu, vietā, gadus, starp, skaties, tomēr, tūlīt, džek, nevajag, + sev, vajadzētu, būšu, dzīvi, droši, gadu, priekšu, skaidrs, gribēju, nāk, paskaties, mazliet, + tikko, nebūs, augšā, ceru, joprojām, nevis, ātrāk, ļauj, gribētu, liels, zina, vārdu, reizi, + pasaulē, savā, sveiki, dienas, miris, dod, priekšā, galā, klau, cilvēkiem, tavas, patiesībā, + visa, vārds, gatavs, durvis, velns, nedaudz, naudas, redzēju, velna, manā, drīz, pāri, dzīve, + vēlies, nemaz, priekš, bērni, vieta, pāris, darbu, vajadzīgs, tālāk, rīt, roku, klāt, grūti, + beidz, laba, klausies, dara, varat, sveika, biji, vismaz, kopš, redzu, saproti, kura, draugi, + zemes, šovakar, patiešām, kaa, vietu, dieva, vajadzēja, mašīnu, lejā, saku, ceļu, gada, tādēļ, + cauri, runāt, ņem, oh, divas, lieta, tikt, šie, teici, vēlāk, vaļā, nogalināt, redzējis, jāiet, + nespēju, savus, atceries, ūdens, šejienes, labu, diena, mīļā, atvaino, doties, atrast, saprotu, + abi, reiz, jādara, nesaprotu, meitene, darbs, nevari, tai, nedomāju, pilnīgi, nakti, nekādu, + pati, gadiem, vēlos, taa, kādas, cits, ejiet, pirmais, a, būsi, mamma, lietu, slikti, pašu, + acis, diezgan, pasaki, gadā, puiši, asv, sava, nost, cilvēkus, džeks, manuprāt, mājas, o, + bērns, leo, otru, nopietni, vecais, laukā, caur, dzīves, izdarīt, sieviete, vienalga, + nogalināja, dzīvo, kādreiz, čau, sirds, paliec, gribat, vēlreiz, kuras, mazais, vietas, + piedodiet, laipni, palikt, brauc, ei, the, paliek, apkārt, sievietes, tālu, garām, pirmo, + dzīvot, nāciet, runāju, kuri, tiks, jüs, ceļā, nauda, nevienam, māja, vienīgais, īsti, + sapratu, gluži, svarīgi, atvainojos, i, sen, iespēja, tavā, pavisam, nāves, māte, citi, + viegli, zem, notiks, darba, nepatīk, daži, galvu, dienā, hallo, bērnu, neesam, kungi, beidzot, + nedrīkst, vajadzēs, māju, sieva, kādam, puika, kļūst, prieks, esot, iesim, daļa, pasaule, + pietiek, visā, saviem, rīta, pagaidiet, tētis, mājā, mieru, vīru, palīdzību, dzirdēju, + tādas, dzīvs, strādā, tām, vēlas, nakts, īpaši, jūtos, nolādēts, meitenes, pusi, mammu, mees, + aizveries, vispār, dzīvību, kurā, kādā, vārdā, mašīna, būsim, vispirms, vinji, nevienu, šos, + tiksimies, džeik, vinjsh, vaina, turpini, kādi, jaunu, tuvu, atradu, vēlu, varēja, citādi, šim, + satikt, neuztraucies, pārliecināts, liec, diez, liela, doktor, nevaram, palīdzi, uzmanīgi, dažas, + šiem, atgriezies, gribēja, priecājos, parasti, valsts, asinis, tēti, you, mierā, piemēram, + jautājums, atā, bijām, zemē, pasauli, spēlē, blakus, izskaties, pirmā, nomira, paši, šobrīd, + daru, gaida, tādi, iešu, labākais, jauks, maz, pieder, jauns, nezināju, uzmanību, skaista, + prātā, brālis, patiesību, mierīgi, šai, dr, patiesi, jēzus, mārtij, zināju, suns, juus, sievu, + dzirdi, tepat, mamm, tēvu, tēva, frodo, sasodīts, desmit, stundas, tavi, mazā, džon, cita, + vajadzīga, forši, minūtes, mīlestība, nebiju, saprast, izbeidz, šoreiz, labā, dāmas, kurienes, + problēma, šādi, spēj, gadījumā, tiesa, kuģi, pēdējā, tici, esiet, atceros, katrs, nee, palīgā, + mister, liek, likās, domāt, vīri, pēdējo, traks, reizes, vienīgā, tiesības, skolā, turies, beigas, + karš, pīter, uguni, pietiks, vienam, vienā, pakaļ, jauna, zemi, puisis, ziniet, negribi, labrīt, + ap, cilvēka, draugu, atver, nezini, sāra, vēlaties, gadi, dažreiz, rokās, dabūt, nomierinies, + istabā, agrāk, ieroci, savām, meiteni, paņem, meklē, pār, seju, ziņu, dzirdējis, zinām, gatavi, + braukt, sāka, sāk, dievam, neesat, dzirdēt, spēle, bērniem, izdarīja, muļķības, doma, pēdējais, + dīvaini, atdod, ziņas, bankas, darāt, vakar, ceļš, neviena, brāli, otrā, atgriezties, galvas, + pietiekami, gulēt, uzreiz, iespēju, bijusi, karalis, bobij, šrek, tikpat, palīdziet, durvīm, + vecāki, atrodas, smieklīgi, kuģa, bail, godīgi, pēkšņi, nedēļas, māsa, skrien, ceļa, džeims, gars, + lielu, mašīnā, bojā, kurieni, ļaudis, dārgais, vecs, ūdeni, kūper, eju, mašīnas, ideja, kājas, + spēles, galvenais, citiem, jātiek, skaisti, nāvi, vinju, problēmas, vērts, drīkstu, domājat, visur, + bieži, manai, citas, apsolu, zelta, strādāju, dzimšanas, jūtu, naktī, dārgā, atbildi, noticēt, + klājas, izdevās, dok, redzat, gana, divus, ģimene, runa, stāsts, braucam, brīnišķīgi, ģimenes, + kuģis, čārlij, hey, kä, sheit, ved, atrada, mirusi, meita, paklau, nevēlos, bērnus, boss, kaptein, + nekāda, roze, nespēj, vīrietis, brīdi, īsts, dzīvē, tādā, manī, jūras, jaunkundz, iemesls, sakot, + manam, daudzi, varēsi, pateicos, jaunais, policija, pilnībā, nekur, jauka, nedari, kurus, zināms, + jautājumu, seko, re, padomā, pusē, visām, mīļais, dolāru, gadžet, katram, izdarīji, šīm, vienīgi, + mirt, apmēram, spēku, jauno, mr, celies, iepriekš, prātu, vēlētos, četri, lietām, redzēji, nevajadzētu, + donna, jaa, ticu, minūtēm, sievieti, nāve, jūties, nezina, parādi, malā, redz, uh, gredzenu, uzmanies, + kara, drošībā, sapnis, bijāt, grāmatu, slepkava, vinja, paga, pieci, pilsētā, drošs, pateikšu, gāja, + spēli, beigās, hanna, princese, jebkad, dakter, veids, palīdzība, stāstu, izmantot, spēlēt, gaisā, + darīšu, došos, dodas, kreisi, negribēju, mazāk, pastāsti, tak, devās, sirdi, misis, vis, patiesība, + veidā, harijs, cenšos, tuvāk, kurp, klausieties, sāp, ļaujiet, neticami, kungu, sīkais, iedomāties, + daļu, mazs, iedod, mazo, meklēju, parunāt, jādodas, sevis, pārējie, veicas, otra, mīlestību, zēns, + dodies, galam, sem, bīstami, zvēru, iespējas, maza, ellē, virs, nekādas, maniem, skatieties, šonakt, + svēto, kapteinis, iepazīties, pazīstu, turp, gredzens, nepareizi, lieliska, īstais, pagaidām, kājām, + mirklīti, pašlaik, d, poter, saprati, aprunāties, paša, šejieni, interesanti, nevarētu, pašā, paskat, + bailes, skolas, vārdus, aizmirsti, gaismas, kāp, zēni, darīsim, pašam, beidzies, sauca, māti, akmens, grāmatas, diemžēl, tevī, kļūt, endij, patika, nabaga, tuvojas, tēvoci, dienām, plāns """ # Source: https://www.101languages.net/polish/most-common-polish-words/ alias words_pl = """ -nie, to, się, w, na, i, z, co, jest, że, do, tak, jak, o, mnie, a, ale, mi, za, ja, ci, tu, ty, czy, -tym, go, tego, tylko, jestem, po, cię, ma, już, mam, jesteś, może, pan, dla, coś, dobrze, wiem, jeśli, -teraz, proszę, od, wszystko, tam, więc, masz, nic, on, być, gdzie, będzie, są, ten, mogę, ciebie, -bardzo, sobie, kiedy, ze, wiesz, no, jej, jeszcze, pani, był, mój, chcę, było, dlaczego, by, przez, -nas, tutaj, chcesz, jego, ją, ich, nigdy, żeby, też, kto, naprawdę, przepraszam, bo, mamy, porządku, -możesz, dobra, mu, dziękuję, ona, domu, panie, muszę, nawet, chyba, hej, właśnie, prawda, zrobić, te, -zawsze, będę, moja, gdy, je, trochę, nam, moje, cześć, bez, nim, była, tej, jesteśmy, dalej, pana, -dzięki, wszyscy, musisz, twój, lat, tobą, więcej, ktoś, czas, ta, który, chce, powiedzieć, chodź, dobry, -mną, niech, sam, razem, chodzi, czego, boże, stało, musimy, raz, albo, prostu, będziesz, dzień, możemy, -was, myślę, czym, daj, lepiej, czemu, ludzie, ok, przed, życie, ludzi, robisz, my, niż, tych, kim, rzeczy, -myślisz, powiedz, przy, twoja, oni, oczywiście, nikt, siebie, stąd, niego, twoje, miał, jeden, mówi, -powiedział, moim, czasu, u, dziś, im, które, musi, wtedy, taki, aby, pod, dwa, temu, pewnie, takie, cóż, -wszystkie, mojego, dużo, cholera, kurwa, wie, znaczy, wygląda, dzieje, mieć, ile, iść, potem, będziemy, -dzieci, dlatego, cały, byłem, moją, skąd, szybko, jako, kochanie, stary, trzeba, miejsce, myśli, można, -sie, jasne, mojej, wam, swoje, zaraz, wiele, nią, rozumiem, nich, wszystkich, jakieś, jakiś, kocham, idź, -tę, mają, mówię, mówisz, dzisiaj, nad, pomóc, takiego, przestań, tobie, jutro, robić, jaki, mamo, kilka, -przykro, wiedzieć, ojciec, widzisz, zbyt, zobaczyć, która, ani, tyle, trzy, tą, sposób, miałem, tato, niej, -później, pieniądze, robi, kogoś, kiedyś, zanim, widzę, pracy, świetnie, pewno, myślałem, będą, bardziej, -życia, długo, och, sir, ponieważ, aż, dni, nocy, każdy, dnia, znowu, oh, chciałem, taka, swoją, twoim, -widziałem, stanie, powiem, imię, wy, żebyś, nadzieję, twojej, panu, spokój, słuchaj, rację, spójrz, razie, -znam, pierwszy, koniec, chciałbym, we, nami, jakie, posłuchaj, problem, przecież, dobre, nasz, dziecko, drzwi, -nasze, miło, czuję, mógł, żyje, jeżeli, człowiek, powiedziałem, gdyby, roku, dom, sama, potrzebuję, -wszystkim, zostać, wciąż, dokładnie, mama, którzy, mówić, zamknij, mów, twoją, chwilę, zrobił, samo, idziemy, -nadal, jesteście, zabić, były, sobą, kogo, lub, lubię, the, podoba, minut, bym, chciał, bądź, czegoś, gdzieś, -mówiłem, chodźmy, znaleźć, poza, spokojnie, wcześniej, został, rozumiesz, mogą, prawie, wydaje, miała, mały, -byłeś, facet, zrobię, macie, żadnych, razy, noc, ciągle, broń, moich, twojego, końcu, pomocy, czekaj, znasz, -oczy, weź, idę, halo, dość, innego, pomysł, jakby, trzymaj, jedno, ojca, porozmawiać, pamiętasz, lata, -powinieneś, którą, powodu, takim, niczego, powinniśmy, oto, napisy, jednak, świat, pokoju, żebym, sprawy, -dwie, samochód, swój, wystarczy, pewien, źle, pozwól, numer, jedną, miejscu, you, drogi, byłam, dokąd, miłość, -panowie, pieniędzy, którego, matka, rano, dwóch, całe, patrz, rzecz, nowy, idzie, wyglądasz, bóg, byś, życiu, -nimi, nikogo, całą, swojego, świecie, sprawa, dziewczyna, prawo, byli, zostaw, wiedziałem, jedna, widzieć, -swoim, kobiety, uważaj, najpierw, właściwie, dam, również, diabła, chcą, którym, zrób, da, jednego, dać, -musiał, ręce, powinienem, których, znów, powiedziała, wczoraj, czujesz, zaczekaj, sądzę, śmierć, mówił, -podczas, której, całkiem, pracę, żona, pójdę, pamiętam, powiedziałeś, mówią, wiemy, jezu, witam, cholery, -swoich, telefon, wielu, także, poważnie, skoro, miejsca, robię, śmierci, słyszałem, wina, zrobiłem, dobranoc, -parę, prawdę, swojej, serce, inaczej, dziewczyny, kobieta, powiesz, martw, rób, pytanie, pięć, innych, one, -gra, natychmiast, wrócić, szybciej, jednym, cokolwiek, wierzę, wcale, wieczór, ważne, człowieka, wielki, nowa, -dopiero, ziemi, gdybym, tata, poznać, stać, jack, myślałam, witaj, słowa, zrobiłeś, gówno, john, dolarów, -sprawę, inne, idziesz, miałam, wiecie, chciałam, zobaczenia, widziałeś, żyć, każdym, nasza, panią, wspaniale, -chwili, każdego, nowego, nieźle, takich, między, dostać, powinien, dawaj, dopóki, naszych, naszej, świata, -chłopaki, chcemy, poczekaj, jaką, człowieku, czasem, żadnego, inny, przynajmniej, nazywa, super, naszego, -szczęście, potrzebuje, godziny, zabrać, powrotem, syn, lecz, słucham, twoich, udało, boga, pokój, działa, -ogóle, naszym, szkoły, możliwe, wiedział, wyjść, wszystkiego, byłoby, daleko, wieczorem, skarbie, jaka, -mógłbym, ostatni, możecie, cztery, doktorze, zrobimy, mąż, przeciwko, zgadza, zrobisz, czasie, czasami, -brzmi, raczej, ciało, należy, miasta, miałeś, taką, brat, cieszę, rozmawiać, cała, czymś, wybacz, twarz, -mała, chcecie, dr, pojęcia, lubisz, głowę, najbardziej, dziwne, głowy, wody, pół, wiadomość, policja, -strony, l, pl, mogłem, mieli, widzenia, pewna, ruszaj, wracaj, ode, popatrz, końca, plan, kiedykolwiek, -wejść, została, rok, syna, uda, wrócę, zewnątrz, droga, uwierzyć, późno, zostało, zostanie, zły, kapitanie, -potrzebujemy, byliśmy, zobaczymy, gotowy, obchodzi, jechać, rodziny, widziałam, drodze, czeka, środku, film, -spać, człowiekiem, zupełnie, taa, pomóż, mieliśmy, pomoc, słowo, innym, ostatnio, and, zna, mogła, pójść, -chłopcy, wziąć, mógłbyś, tłumaczenie, potrzebujesz, słyszysz, blisko, godzin, miłości, góry, zabił, piękna, -napisów, pokaż, moi, lubi, robota, prawa, ciężko, kimś, dół, rękę, nazywam, wielkie, część, wkrótce, naszą, -jedziemy, zapomnij, prosto, radę, robimy, powinnaś, gdybyś, chociaż, zależy, stronie, wypadek, tydzień, byłaś, -nowe, małe, praca, drogę, chłopak, zrobi, widział, mieście, synu, oznacza, krew, mógłby, krwi, górę, joe, wasza, -robią, tędy, wszędzie, temat, pierwsze, zobacz, ponad, kraju, mało, racja, tymi, cicho, chciała, powiedziałam, -leci, powinno, mówiąc, serca, chciałabym, miasto, george, spotkać, mniej, e, przyjaciel, mówiłeś, kłopoty, -miesięcy, jakąś, żaden, zostań, roboty, zatrzymać, frank, nieważne, głupi, pa, koleś, sprawie, spotkanie, ojcze, -pewnego, spróbuj, drugi, znalazłem, pracować, całym, zostały, złe, niemożliwe, jakoś, zdjęcia, stronę, wiedzą, it, -dziewczynę, zaczyna, mogli, samego, sądzisz, rodzina, razu, trudno, samochodu, okay, boję, szkoda, wami, charlie, -dał, środka, ojcem, piękne, dawno, choć, panem, przykład, nagle, bracie, żadnej, drugiej, przyjaciół, otwórz, -myśleć, doktor, chwileczkę, pracuje, najlepszy, brata, czyż, często, http, powinnam, odejść, trzech, chodźcie, -nazwisko, szansę, ciała, policji, szkole, prawdopodobnie, serio, matki, org, wolno, sami, muszą, zabierz, -słyszałeś, siostra, uspokój, wystarczająco, początku, faceta, problemy, szefie, broni, me, zostawić, czuje, -będziecie, przyszedł, wiedziałam, kilku, inni, b, głowie, historia, według, www, wezmę, nowym, czekać, stój, -mężczyzna, mówiłam, pokazać, około, wracam, wieku, jakaś, pierwsza, niczym, zabiję, zdjęcie, zabawne, rodzice, -musiałem, całkowicie, sprawdzić, mike, przyjdzie, sześć, kupić, dobrym, żonę, dasz, pomoże, nogi, obok, ruszać, -trzymać, zadzwonić, panno, godzinę, boli, oraz, spokoju, walczyć, wróci, tom, wspólnego, zmienić, ostatnie, uwagę, -znać, jednej, dłużej, powie, pogadać, łatwo, większość, nikomu, michael, córka, niedługo, powodzenia, tygodniu, -włosy, niestety, górze, kochasz, prawdziwy, historii, ulicy, musicie, gotowi, chwila, samym, grać, zadzwonię, -strasznie, mieszka, kocha, rady, tyłu, jakim, obiecuję, tysięcy, pomyślałem, pracuję, jedynie, pozwolić, uwaga, +nie, to, się, w, na, i, z, co, jest, że, do, tak, jak, o, mnie, a, ale, mi, za, ja, ci, tu, ty, czy, +tym, go, tego, tylko, jestem, po, cię, ma, już, mam, jesteś, może, pan, dla, coś, dobrze, wiem, jeśli, +teraz, proszę, od, wszystko, tam, więc, masz, nic, on, być, gdzie, będzie, są, ten, mogę, ciebie, +bardzo, sobie, kiedy, ze, wiesz, no, jej, jeszcze, pani, był, mój, chcę, było, dlaczego, by, przez, +nas, tutaj, chcesz, jego, ją, ich, nigdy, żeby, też, kto, naprawdę, przepraszam, bo, mamy, porządku, +możesz, dobra, mu, dziękuję, ona, domu, panie, muszę, nawet, chyba, hej, właśnie, prawda, zrobić, te, +zawsze, będę, moja, gdy, je, trochę, nam, moje, cześć, bez, nim, była, tej, jesteśmy, dalej, pana, +dzięki, wszyscy, musisz, twój, lat, tobą, więcej, ktoś, czas, ta, który, chce, powiedzieć, chodź, dobry, +mną, niech, sam, razem, chodzi, czego, boże, stało, musimy, raz, albo, prostu, będziesz, dzień, możemy, +was, myślę, czym, daj, lepiej, czemu, ludzie, ok, przed, życie, ludzi, robisz, my, niż, tych, kim, rzeczy, +myślisz, powiedz, przy, twoja, oni, oczywiście, nikt, siebie, stąd, niego, twoje, miał, jeden, mówi, +powiedział, moim, czasu, u, dziś, im, które, musi, wtedy, taki, aby, pod, dwa, temu, pewnie, takie, cóż, +wszystkie, mojego, dużo, cholera, kurwa, wie, znaczy, wygląda, dzieje, mieć, ile, iść, potem, będziemy, +dzieci, dlatego, cały, byłem, moją, skąd, szybko, jako, kochanie, stary, trzeba, miejsce, myśli, można, +sie, jasne, mojej, wam, swoje, zaraz, wiele, nią, rozumiem, nich, wszystkich, jakieś, jakiś, kocham, idź, +tę, mają, mówię, mówisz, dzisiaj, nad, pomóc, takiego, przestań, tobie, jutro, robić, jaki, mamo, kilka, +przykro, wiedzieć, ojciec, widzisz, zbyt, zobaczyć, która, ani, tyle, trzy, tą, sposób, miałem, tato, niej, +później, pieniądze, robi, kogoś, kiedyś, zanim, widzę, pracy, świetnie, pewno, myślałem, będą, bardziej, +życia, długo, och, sir, ponieważ, aż, dni, nocy, każdy, dnia, znowu, oh, chciałem, taka, swoją, twoim, +widziałem, stanie, powiem, imię, wy, żebyś, nadzieję, twojej, panu, spokój, słuchaj, rację, spójrz, razie, +znam, pierwszy, koniec, chciałbym, we, nami, jakie, posłuchaj, problem, przecież, dobre, nasz, dziecko, drzwi, +nasze, miło, czuję, mógł, żyje, jeżeli, człowiek, powiedziałem, gdyby, roku, dom, sama, potrzebuję, +wszystkim, zostać, wciąż, dokładnie, mama, którzy, mówić, zamknij, mów, twoją, chwilę, zrobił, samo, idziemy, +nadal, jesteście, zabić, były, sobą, kogo, lub, lubię, the, podoba, minut, bym, chciał, bądź, czegoś, gdzieś, +mówiłem, chodźmy, znaleźć, poza, spokojnie, wcześniej, został, rozumiesz, mogą, prawie, wydaje, miała, mały, +byłeś, facet, zrobię, macie, żadnych, razy, noc, ciągle, broń, moich, twojego, końcu, pomocy, czekaj, znasz, +oczy, weź, idę, halo, dość, innego, pomysł, jakby, trzymaj, jedno, ojca, porozmawiać, pamiętasz, lata, +powinieneś, którą, powodu, takim, niczego, powinniśmy, oto, napisy, jednak, świat, pokoju, żebym, sprawy, +dwie, samochód, swój, wystarczy, pewien, źle, pozwól, numer, jedną, miejscu, you, drogi, byłam, dokąd, miłość, +panowie, pieniędzy, którego, matka, rano, dwóch, całe, patrz, rzecz, nowy, idzie, wyglądasz, bóg, byś, życiu, +nimi, nikogo, całą, swojego, świecie, sprawa, dziewczyna, prawo, byli, zostaw, wiedziałem, jedna, widzieć, +swoim, kobiety, uważaj, najpierw, właściwie, dam, również, diabła, chcą, którym, zrób, da, jednego, dać, +musiał, ręce, powinienem, których, znów, powiedziała, wczoraj, czujesz, zaczekaj, sądzę, śmierć, mówił, +podczas, której, całkiem, pracę, żona, pójdę, pamiętam, powiedziałeś, mówią, wiemy, jezu, witam, cholery, +swoich, telefon, wielu, także, poważnie, skoro, miejsca, robię, śmierci, słyszałem, wina, zrobiłem, dobranoc, +parę, prawdę, swojej, serce, inaczej, dziewczyny, kobieta, powiesz, martw, rób, pytanie, pięć, innych, one, +gra, natychmiast, wrócić, szybciej, jednym, cokolwiek, wierzę, wcale, wieczór, ważne, człowieka, wielki, nowa, +dopiero, ziemi, gdybym, tata, poznać, stać, jack, myślałam, witaj, słowa, zrobiłeś, gówno, john, dolarów, +sprawę, inne, idziesz, miałam, wiecie, chciałam, zobaczenia, widziałeś, żyć, każdym, nasza, panią, wspaniale, +chwili, każdego, nowego, nieźle, takich, między, dostać, powinien, dawaj, dopóki, naszych, naszej, świata, +chłopaki, chcemy, poczekaj, jaką, człowieku, czasem, żadnego, inny, przynajmniej, nazywa, super, naszego, +szczęście, potrzebuje, godziny, zabrać, powrotem, syn, lecz, słucham, twoich, udało, boga, pokój, działa, +ogóle, naszym, szkoły, możliwe, wiedział, wyjść, wszystkiego, byłoby, daleko, wieczorem, skarbie, jaka, +mógłbym, ostatni, możecie, cztery, doktorze, zrobimy, mąż, przeciwko, zgadza, zrobisz, czasie, czasami, +brzmi, raczej, ciało, należy, miasta, miałeś, taką, brat, cieszę, rozmawiać, cała, czymś, wybacz, twarz, +mała, chcecie, dr, pojęcia, lubisz, głowę, najbardziej, dziwne, głowy, wody, pół, wiadomość, policja, +strony, l, pl, mogłem, mieli, widzenia, pewna, ruszaj, wracaj, ode, popatrz, końca, plan, kiedykolwiek, +wejść, została, rok, syna, uda, wrócę, zewnątrz, droga, uwierzyć, późno, zostało, zostanie, zły, kapitanie, +potrzebujemy, byliśmy, zobaczymy, gotowy, obchodzi, jechać, rodziny, widziałam, drodze, czeka, środku, film, +spać, człowiekiem, zupełnie, taa, pomóż, mieliśmy, pomoc, słowo, innym, ostatnio, and, zna, mogła, pójść, +chłopcy, wziąć, mógłbyś, tłumaczenie, potrzebujesz, słyszysz, blisko, godzin, miłości, góry, zabił, piękna, +napisów, pokaż, moi, lubi, robota, prawa, ciężko, kimś, dół, rękę, nazywam, wielkie, część, wkrótce, naszą, +jedziemy, zapomnij, prosto, radę, robimy, powinnaś, gdybyś, chociaż, zależy, stronie, wypadek, tydzień, byłaś, +nowe, małe, praca, drogę, chłopak, zrobi, widział, mieście, synu, oznacza, krew, mógłby, krwi, górę, joe, wasza, +robią, tędy, wszędzie, temat, pierwsze, zobacz, ponad, kraju, mało, racja, tymi, cicho, chciała, powiedziałam, +leci, powinno, mówiąc, serca, chciałabym, miasto, george, spotkać, mniej, e, przyjaciel, mówiłeś, kłopoty, +miesięcy, jakąś, żaden, zostań, roboty, zatrzymać, frank, nieważne, głupi, pa, koleś, sprawie, spotkanie, ojcze, +pewnego, spróbuj, drugi, znalazłem, pracować, całym, zostały, złe, niemożliwe, jakoś, zdjęcia, stronę, wiedzą, it, +dziewczynę, zaczyna, mogli, samego, sądzisz, rodzina, razu, trudno, samochodu, okay, boję, szkoda, wami, charlie, +dał, środka, ojcem, piękne, dawno, choć, panem, przykład, nagle, bracie, żadnej, drugiej, przyjaciół, otwórz, +myśleć, doktor, chwileczkę, pracuje, najlepszy, brata, czyż, często, http, powinnam, odejść, trzech, chodźcie, +nazwisko, szansę, ciała, policji, szkole, prawdopodobnie, serio, matki, org, wolno, sami, muszą, zabierz, +słyszałeś, siostra, uspokój, wystarczająco, początku, faceta, problemy, szefie, broni, me, zostawić, czuje, +będziecie, przyszedł, wiedziałam, kilku, inni, b, głowie, historia, według, www, wezmę, nowym, czekać, stój, +mężczyzna, mówiłam, pokazać, około, wracam, wieku, jakaś, pierwsza, niczym, zabiję, zdjęcie, zabawne, rodzice, +musiałem, całkowicie, sprawdzić, mike, przyjdzie, sześć, kupić, dobrym, żonę, dasz, pomoże, nogi, obok, ruszać, +trzymać, zadzwonić, panno, godzinę, boli, oraz, spokoju, walczyć, wróci, tom, wspólnego, zmienić, ostatnie, uwagę, +znać, jednej, dłużej, powie, pogadać, łatwo, większość, nikomu, michael, córka, niedługo, powodzenia, tygodniu, +włosy, niestety, górze, kochasz, prawdziwy, historii, ulicy, musicie, gotowi, chwila, samym, grać, zadzwonię, +strasznie, mieszka, kocha, rady, tyłu, jakim, obiecuję, tysięcy, pomyślałem, pracuję, jedynie, pozwolić, uwaga, proste, zacząć, myśl, wstawaj, rany, prawdziwe, takiej, jakiegoś, umrzeć, złego, okazji """ # Source: https://www.101languages.net/greek/most-common-greek-words/ alias words_el = """ - να, το, δεν, θα, είναι, και, μου, με, ο, για, την, σου, τα, τον, η, τι, σε, που, του, αυτό, στο, ότι, - από, τη, της, ναι, σας, ένα, εδώ, τους, αν, όχι, μια, μας, είσαι, αλλά, κι, οι, πρέπει, είμαι, ήταν, - πολύ, στην, δε, γιατί, εγώ, τώρα, πως, εντάξει, τις, κάτι, ξέρω, μην, έχει, έχω, εσύ, θέλω, καλά, - έτσι, στη, στον, αυτή, ξέρεις, κάνεις, εκεί, σαν, μόνο, μπορώ, όταν, έχεις, μαζί, πώς, τίποτα, - ευχαριστώ, όλα, κάνω, πάμε, ή, ποτέ, τόσο, πού, αυτά, έλα, στα, μέσα, κάνει, των, μπορεί, κύριε, πιο, - σπίτι, παρακαλώ, λοιπόν, μπορείς, αυτός, υπάρχει, ακόμα, πίσω, λίγο, πάντα, είμαστε, γεια, τότε, - ειναι, μετά, πω, έχουμε, μη, ένας, ποιος, νομίζω, πριν, απλά, δω, δουλειά, παιδιά, οχι, αλήθεια, - όλοι, ίσως, λες, όπως, ας, θέλεις, μα, άλλο, είπε, ζωή, πάω, δύο, ωραία, έναν, καλό, απο, κάνουμε, - έξω, κοίτα, είχε, στις, πάνω, είπα, πες, χρόνια, ούτε, κάτω, είστε, ώρα, θες, σένα, έχουν, γυναίκα, - μένα, μέρα, καλή, φορά, όμως, κανείς, κάθε, ε, οτι, αρέσει, ήμουν, μέχρι, δυο, είχα, μαμά, χωρίς, - καλύτερα, πας, πράγματα, πάει, σήμερα, κάποιος, ήθελα, θέλει, θεέ, έπρεπε, λέει, μία, σωστά, αυτόν, - μπορούμε, συμβαίνει, ακριβώς, έγινε, πόσο, επειδή, λεφτά, πολλά, μόλις, εμένα, λένε, πεις, συγγνώμη, - γρήγορα, ω, έκανε, λυπάμαι, γίνει, παιδί, περίμενε, έκανα, φίλε, βλέπω, μέρος, στιγμή, φαίνεται, + να, το, δεν, θα, είναι, και, μου, με, ο, για, την, σου, τα, τον, η, τι, σε, που, του, αυτό, στο, ότι, + από, τη, της, ναι, σας, ένα, εδώ, τους, αν, όχι, μια, μας, είσαι, αλλά, κι, οι, πρέπει, είμαι, ήταν, + πολύ, στην, δε, γιατί, εγώ, τώρα, πως, εντάξει, τις, κάτι, ξέρω, μην, έχει, έχω, εσύ, θέλω, καλά, + έτσι, στη, στον, αυτή, ξέρεις, κάνεις, εκεί, σαν, μόνο, μπορώ, όταν, έχεις, μαζί, πώς, τίποτα, + ευχαριστώ, όλα, κάνω, πάμε, ή, ποτέ, τόσο, πού, αυτά, έλα, στα, μέσα, κάνει, των, μπορεί, κύριε, πιο, + σπίτι, παρακαλώ, λοιπόν, μπορείς, αυτός, υπάρχει, ακόμα, πίσω, λίγο, πάντα, είμαστε, γεια, τότε, + ειναι, μετά, πω, έχουμε, μη, ένας, ποιος, νομίζω, πριν, απλά, δω, δουλειά, παιδιά, οχι, αλήθεια, + όλοι, ίσως, λες, όπως, ας, θέλεις, μα, άλλο, είπε, ζωή, πάω, δύο, ωραία, έναν, καλό, απο, κάνουμε, + έξω, κοίτα, είχε, στις, πάνω, είπα, πες, χρόνια, ούτε, κάτω, είστε, ώρα, θες, σένα, έχουν, γυναίκα, + μένα, μέρα, καλή, φορά, όμως, κανείς, κάθε, ε, οτι, αρέσει, ήμουν, μέχρι, δυο, είχα, μαμά, χωρίς, + καλύτερα, πας, πράγματα, πάει, σήμερα, κάποιος, ήθελα, θέλει, θεέ, έπρεπε, λέει, μία, σωστά, αυτόν, + μπορούμε, συμβαίνει, ακριβώς, έγινε, πόσο, επειδή, λεφτά, πολλά, μόλις, εμένα, λένε, πεις, συγγνώμη, + γρήγορα, ω, έκανε, λυπάμαι, γίνει, παιδί, περίμενε, έκανα, φίλε, βλέπω, μέρος, στιγμή, φαίνεται, πρόβλημα, άλλη, είπες, φυσικά, κάποιον, όσο, πήγαινε, πάλι, λάθος, ως, έχετε, εσένα, πράγμα, κυρία, - χρόνο, στους, πάρω, μπαμπά, δικό, απ, γίνεται, εσείς, λέω, συγνώμη, όλο, μητέρα, έκανες, πιστεύω, - ήσουν, κάποια, σίγουρα, υπάρχουν, όλη, ενα, αυτο, ξέρει, μωρό, ιδέα, δει, μάλλον, ίδιο, πάρε, είδα, - αύριο, βλέπεις, νέα, κόσμο, νομίζεις, τί, εμείς, σταμάτα, πάρει, αγάπη, πατέρας, όλους, αρκετά, - χρειάζεται, καιρό, φορές, κάνουν, ακόμη, α, πατέρα, προς, αμέσως, πια, ηταν, χαρά, απόψε, όνομα, - μάλιστα, μόνος, μεγάλη, κανένα, ελα, πραγματικά, αυτοί, πει, πότε, εχω, βράδυ, αυτές, θέλετε, κάνετε, - σημαίνει, πρώτη, ποιο, πόλη, μπορούσα, ποια, γαμώτο, ήδη, τελευταία, άνθρωποι, τέλος, απλώς, νόμιζα, - ξέρετε, μέρες, δεις, θέση, αυτούς, καταλαβαίνω, φύγε, χέρια, εκτός, ήξερα, οπότε, λεπτά, μακριά, - κάνε, αμάξι, δική, λεπτό, μεγάλο, μήπως, κορίτσι, μάτια, ελάτε, πρόκειται, πόρτα, δίκιο, βοήθεια, - ήρθε, μιλήσω, δρόμο, εαυτό, καθόλου, ορίστε, βρω, πειράζει, μπορείτε, καλός, πέρα, κοντά, εννοώ, - τέτοιο, μπροστά, έρθει, χρειάζομαι, χέρι, ελπίζω, δώσε, διάολο, φύγω, ιστορία, όπλο, αφού, πρωί, - νύχτα, ωραίο, τύπος, ξανά, θυμάσαι, δούμε, κατά, εννοείς, αγαπώ, κακό, θέμα, εδω, αυτήν, τρόπο, - κεφάλι, είχες, μερικές, μιλάς, φίλος, άνθρωπος, φύγουμε, όλες, σκατά, ανθρώπους, βέβαια, άντρας, - κάποιο, πάνε, αστυνομία, αλλιώς, συνέβη, χαίρομαι, άλλα, περισσότερο, καλύτερο, εκείνη, πάρεις, τo, - νερό, ώρες, σίγουρος, vα, τρεις, εχεις, πρώτα, μπορούσε, σ, οταν, δρ, πιστεύεις, μόνη, ποιός, καμιά, - κανέναν, πέθανε, εχει, ετσι, αγόρι, ανησυχείς, άντρες, δωμάτιο, ομάδα, ίδια, εμπρός, βρούμε, βοηθήσω, - τέτοια, πήρε, τρία, λόγο, μικρό, αντίο, o, πέντε, πήγε, καν, ευκαιρία, είδες, έρχεται, δηλαδή, - αργότερα, ήθελε, πούμε, λέμε, όπου, αλλα, κόρη, κόσμος, γυναίκες, τηλέφωνο, εάν, δώσω, καρδιά, βρήκα, - γραφείο, επίσης, νιώθω, σχέση, θέλουν, ισως, τέλεια, είχαμε, κάπου, μυαλό, ώστε, καλημέρα, σχολείο, - θεός, μικρή, τρέχει, ψέματα, ξέρουμε, οικογένεια, εισαι, θυμάμαι, κ, ενός, φίλοι, πρόσεχε, - καταλαβαίνεις, αργά, ντε, θέλουμε, σύντομα, πήρα, σχεδόν, παιχνίδι, κύριοι, γειά, μήνες, μπαμπάς, - σοβαρά, δολάρια, τουλάχιστον, χρήματα, πείτε, πόδια, αίμα, κοπέλα, φαγητό, ειμαι, ποιον, μερικά, - δύσκολο, μπορούν, βρεις, όμορφη, φύγεις, τύχη, πλάκα, έρθεις, άντρα, κορίτσια, μείνε, αστείο, καμία, - είχαν, χάρη, άλλος, πρεπει, σημασία, φυλακή, νεκρός, συγχωρείτε, φοβάμαι, μπράβο, γύρω, κανένας, μεταξύ, - τ, χθες, πολλές, όνομά, τζακ, ρε, καληνύχτα, πολυ, φύγει, αφήσω, ήθελες, tι, ήρθες, ακούς, πρώτο, γιατι, - ηρέμησε, γι, πάρουμε, πάρα, άλλους, κατάλαβα, έρθω, συνέχεια, έλεγα, γλυκιά, νοιάζει, χριστέ, βιβλίο, - κύριος, μ, χώρα, αρχή, ήρθα, πεθάνει, γη, έτοιμος, εγω, άσχημα, συμβεί, αυτοκίνητο, ζωής, τελικά, φέρω, - τρόπος, κατάσταση, www, περιμένω, σημαντικό, όσα, σκέφτηκα, μιλήσουμε, αφήστε, τωρα, ακούω, γιος, σκοτώσω, - δύναμη, κα, κε, εκείνο, γονείς, μιλάω, σκοτώσει, ολα, μείνει, μείνω, αρέσουν, δεv, υπόθεση, φίλους, όπλα, - υποθέτω, εμάς, ενώ, έξι, σχέδιο, άρεσε, καφέ, σκότωσε, χρειαζόμαστε, φίλο, σωστό, προσπαθώ, κάναμε, - κοιτάξτε, μoυ, κου, ποτό, εσάς, έι, έφυγε, ταινία, μοιάζει, κρεβάτι, εχουμε, περιμένει, νέο, μπορούσες, - μάθω, αφήσεις, περιμένετε, χρειάζεσαι, υπήρχε, μισό, δέκα, αφεντικό, περίπου, άλλοι, λόγος, ξέρουν, κάποτε, - βρήκες, καλύτερη, υπέροχο, τζον, δίπλα, σκάσε, θεού, άκουσα, φύγετε, λέξη, παρά, επόμενη, λέτε, περάσει, - πόσα, γίνεις, σώμα, ν, πήρες, τελείωσε, γιο, ρούχα, σκέφτομαι, εσυ, άλλες, γυρίσω, βάλω, μουσική, ραντεβού, - φωτιά, έδωσε, πάτε, φοβάσαι, βρει, δείξω, γίνω, βοηθήσει, τύπο, σειρά, αξίζει, μείνεις, είπαν, άλλον, - κυρίες, λίγη, πέρασε, κάτσε, πήγα, δείτε, μιας, βδομάδα, έρχομαι, προσοχή, εύκολο, ερώτηση, υπέροχα, - σίγουρη, νοσοκομείο, τρελός, ενας, βάλε, πόλεμο, φέρε, δικά, τιμή, κατάλαβες, ταξίδι, οποίο, δουλεύει, θεό, - μικρέ, μάθεις, βρίσκεται, πολλοί, δες, πάρτε, παντού, πρόσωπο, μήνυμα, αδερφή, μιλάει, παλιά, πουθενά, - κράτα, περίπτωση, φως, επάνω, έλεγε, συμφωνία, οπως, ολοι, πρώτος, δεσποινίς, γιατρός, γνωρίζω, σαμ, - σκέφτεσαι, ει, φίλη, σεξ, έκαναν, προβλήματα, κάπως, ό, τελευταίο, ακούσει, τζο, καλώς, επιλογή, - σταματήστε, τόσα, οτιδήποτε, περισσότερα, άδεια, πάρτι, περίμενα, ακούγεται, gmteam, ήξερες, καιρός, - μαλλιά, καλύτερος, κανεις, φρανκ, μέση, συνέχισε, τίποτε, φωτογραφία, κατι, μεγάλος, περιοχή, άσε, καθώς, - είδε, λόγια, μήνα, μαλακίες, όμορφο, δώρο, στόμα, χάλια, εντελώς, μακάρι, τελειώσει, γνώμη, γιατρέ, ξερω, - πλευρά, μέλλον, θάνατο, νιώθεις, έτοιμοι, κομμάτι, μάθει, μιλάμε, ψηλά, αέρα, ερωτήσεις, αυτού, δώσει, - φεύγω, σημείο, τηλεόραση, κυριε, πραγματικότητα, ανάγκη, βοηθήσεις, προσπάθησε, γύρνα, άφησε, λίγα, κάντε, - είvαι, βλέπετε, αυτη, δείπνο, επιτέλους, κέντρο, περίεργο, ακούστε, πλοίο, κάποιες, δικός, σoυ, οικογένειά, - μιλήσει, πλέον, υπόσχομαι, περιμένεις, ήξερε, σκοτώσεις, ενταξει, δώσεις, εκει, ήμασταν, έρχονται, κώλο, - ρωτήσω, παίρνει, σιγά, σήκω, στοιχεία, αδελφή, βασικά, μένει, άκρη, πηγαίνετε, παίρνεις, tο, περιμένουμε, - συγχωρείς, μικρός, πόδι, δίνει, εκατομμύρια, ξενοδοχείο, αποστολή, ενδιαφέρον, χάρηκα, αεροπλάνο, γάμο, - χιλιάδες, υόρκη, οκ, ευχαριστούμε, καλα, κοιτάς, σα, π, χρόνος, ησυχία, ασφάλεια, εκείνος, a, βρήκε, - τέσσερα, βγάλω, μπες, συχνά, ημέρα, μάνα, εν, αγαπάς, άνθρωπο, γραμμή, φωτογραφίες, προσέχεις, ύπνο, - μυστικό, σχετικά, είδους, σκέψου, χριστούγεννα, κόσμου, τομ, μισώ, σύστημα, δουλειές, τελείως, πεθάνω, - αλλάξει, δεξιά, συνήθως, δουλεύω, μάικλ, εβδομάδα, νούμερο, λείπει, έτοιμη, τμήμα, βγει, ψυχή, έπεσε, - κάθαρμα, ματιά, οποία, πληροφορίες, μονο, κρίμα, τραγούδι, μαγαζί, δουλεύεις, μαζι, τέλειο, κύριο, - λέγεται, τσάρλι, πεθάνεις, σκεφτόμουν, καλησπέρα, συγχαρητήρια, φωνή, εκ, άτομο, παίζεις, σκάφος, - φαίνεσαι, ξαφνικά, παραπάνω, ατύχημα, θελω, ξέχνα, ήρθατε, εναντίον, τραπέζι, γράμμα, μείνετε, αμερική, - βασιλιάς, υπό, μπάνιο, ποτε, ίδιος, προφανώς, μαλάκα, αδερφός, άνδρες, nαι, χρονών, ναί, κλειδί, δις, - γιαγιά, παράξενο, πτώμα, βρήκαμε, μιλήσεις, υποτίθεται, ορκίζομαι, δυνατά, ποιό, θάλασσα, παίρνω, άκουσες, - παρέα, αριστερά, έμαθα, μάχη, μηχανή, σάρα, ζωντανός, όνειρο, παλιό, μπορούσαμε, πάντως, ανάμεσα, έχασα, - νωρίς, κάποιοι, άκου, παίζει, φτάνει, δίνω, βγες, υπέροχη, νόημα, έλεγχο, μέτρα, ξερεις, ζει, δείχνει, + χρόνο, στους, πάρω, μπαμπά, δικό, απ, γίνεται, εσείς, λέω, συγνώμη, όλο, μητέρα, έκανες, πιστεύω, + ήσουν, κάποια, σίγουρα, υπάρχουν, όλη, ενα, αυτο, ξέρει, μωρό, ιδέα, δει, μάλλον, ίδιο, πάρε, είδα, + αύριο, βλέπεις, νέα, κόσμο, νομίζεις, τί, εμείς, σταμάτα, πάρει, αγάπη, πατέρας, όλους, αρκετά, + χρειάζεται, καιρό, φορές, κάνουν, ακόμη, α, πατέρα, προς, αμέσως, πια, ηταν, χαρά, απόψε, όνομα, + μάλιστα, μόνος, μεγάλη, κανένα, ελα, πραγματικά, αυτοί, πει, πότε, εχω, βράδυ, αυτές, θέλετε, κάνετε, + σημαίνει, πρώτη, ποιο, πόλη, μπορούσα, ποια, γαμώτο, ήδη, τελευταία, άνθρωποι, τέλος, απλώς, νόμιζα, + ξέρετε, μέρες, δεις, θέση, αυτούς, καταλαβαίνω, φύγε, χέρια, εκτός, ήξερα, οπότε, λεπτά, μακριά, + κάνε, αμάξι, δική, λεπτό, μεγάλο, μήπως, κορίτσι, μάτια, ελάτε, πρόκειται, πόρτα, δίκιο, βοήθεια, + ήρθε, μιλήσω, δρόμο, εαυτό, καθόλου, ορίστε, βρω, πειράζει, μπορείτε, καλός, πέρα, κοντά, εννοώ, + τέτοιο, μπροστά, έρθει, χρειάζομαι, χέρι, ελπίζω, δώσε, διάολο, φύγω, ιστορία, όπλο, αφού, πρωί, + νύχτα, ωραίο, τύπος, ξανά, θυμάσαι, δούμε, κατά, εννοείς, αγαπώ, κακό, θέμα, εδω, αυτήν, τρόπο, + κεφάλι, είχες, μερικές, μιλάς, φίλος, άνθρωπος, φύγουμε, όλες, σκατά, ανθρώπους, βέβαια, άντρας, + κάποιο, πάνε, αστυνομία, αλλιώς, συνέβη, χαίρομαι, άλλα, περισσότερο, καλύτερο, εκείνη, πάρεις, τo, + νερό, ώρες, σίγουρος, vα, τρεις, εχεις, πρώτα, μπορούσε, σ, οταν, δρ, πιστεύεις, μόνη, ποιός, καμιά, + κανέναν, πέθανε, εχει, ετσι, αγόρι, ανησυχείς, άντρες, δωμάτιο, ομάδα, ίδια, εμπρός, βρούμε, βοηθήσω, + τέτοια, πήρε, τρία, λόγο, μικρό, αντίο, o, πέντε, πήγε, καν, ευκαιρία, είδες, έρχεται, δηλαδή, + αργότερα, ήθελε, πούμε, λέμε, όπου, αλλα, κόρη, κόσμος, γυναίκες, τηλέφωνο, εάν, δώσω, καρδιά, βρήκα, + γραφείο, επίσης, νιώθω, σχέση, θέλουν, ισως, τέλεια, είχαμε, κάπου, μυαλό, ώστε, καλημέρα, σχολείο, + θεός, μικρή, τρέχει, ψέματα, ξέρουμε, οικογένεια, εισαι, θυμάμαι, κ, ενός, φίλοι, πρόσεχε, + καταλαβαίνεις, αργά, ντε, θέλουμε, σύντομα, πήρα, σχεδόν, παιχνίδι, κύριοι, γειά, μήνες, μπαμπάς, + σοβαρά, δολάρια, τουλάχιστον, χρήματα, πείτε, πόδια, αίμα, κοπέλα, φαγητό, ειμαι, ποιον, μερικά, + δύσκολο, μπορούν, βρεις, όμορφη, φύγεις, τύχη, πλάκα, έρθεις, άντρα, κορίτσια, μείνε, αστείο, καμία, + είχαν, χάρη, άλλος, πρεπει, σημασία, φυλακή, νεκρός, συγχωρείτε, φοβάμαι, μπράβο, γύρω, κανένας, μεταξύ, + τ, χθες, πολλές, όνομά, τζακ, ρε, καληνύχτα, πολυ, φύγει, αφήσω, ήθελες, tι, ήρθες, ακούς, πρώτο, γιατι, + ηρέμησε, γι, πάρουμε, πάρα, άλλους, κατάλαβα, έρθω, συνέχεια, έλεγα, γλυκιά, νοιάζει, χριστέ, βιβλίο, + κύριος, μ, χώρα, αρχή, ήρθα, πεθάνει, γη, έτοιμος, εγω, άσχημα, συμβεί, αυτοκίνητο, ζωής, τελικά, φέρω, + τρόπος, κατάσταση, www, περιμένω, σημαντικό, όσα, σκέφτηκα, μιλήσουμε, αφήστε, τωρα, ακούω, γιος, σκοτώσω, + δύναμη, κα, κε, εκείνο, γονείς, μιλάω, σκοτώσει, ολα, μείνει, μείνω, αρέσουν, δεv, υπόθεση, φίλους, όπλα, + υποθέτω, εμάς, ενώ, έξι, σχέδιο, άρεσε, καφέ, σκότωσε, χρειαζόμαστε, φίλο, σωστό, προσπαθώ, κάναμε, + κοιτάξτε, μoυ, κου, ποτό, εσάς, έι, έφυγε, ταινία, μοιάζει, κρεβάτι, εχουμε, περιμένει, νέο, μπορούσες, + μάθω, αφήσεις, περιμένετε, χρειάζεσαι, υπήρχε, μισό, δέκα, αφεντικό, περίπου, άλλοι, λόγος, ξέρουν, κάποτε, + βρήκες, καλύτερη, υπέροχο, τζον, δίπλα, σκάσε, θεού, άκουσα, φύγετε, λέξη, παρά, επόμενη, λέτε, περάσει, + πόσα, γίνεις, σώμα, ν, πήρες, τελείωσε, γιο, ρούχα, σκέφτομαι, εσυ, άλλες, γυρίσω, βάλω, μουσική, ραντεβού, + φωτιά, έδωσε, πάτε, φοβάσαι, βρει, δείξω, γίνω, βοηθήσει, τύπο, σειρά, αξίζει, μείνεις, είπαν, άλλον, + κυρίες, λίγη, πέρασε, κάτσε, πήγα, δείτε, μιας, βδομάδα, έρχομαι, προσοχή, εύκολο, ερώτηση, υπέροχα, + σίγουρη, νοσοκομείο, τρελός, ενας, βάλε, πόλεμο, φέρε, δικά, τιμή, κατάλαβες, ταξίδι, οποίο, δουλεύει, θεό, + μικρέ, μάθεις, βρίσκεται, πολλοί, δες, πάρτε, παντού, πρόσωπο, μήνυμα, αδερφή, μιλάει, παλιά, πουθενά, + κράτα, περίπτωση, φως, επάνω, έλεγε, συμφωνία, οπως, ολοι, πρώτος, δεσποινίς, γιατρός, γνωρίζω, σαμ, + σκέφτεσαι, ει, φίλη, σεξ, έκαναν, προβλήματα, κάπως, ό, τελευταίο, ακούσει, τζο, καλώς, επιλογή, + σταματήστε, τόσα, οτιδήποτε, περισσότερα, άδεια, πάρτι, περίμενα, ακούγεται, gmteam, ήξερες, καιρός, + μαλλιά, καλύτερος, κανεις, φρανκ, μέση, συνέχισε, τίποτε, φωτογραφία, κατι, μεγάλος, περιοχή, άσε, καθώς, + είδε, λόγια, μήνα, μαλακίες, όμορφο, δώρο, στόμα, χάλια, εντελώς, μακάρι, τελειώσει, γνώμη, γιατρέ, ξερω, + πλευρά, μέλλον, θάνατο, νιώθεις, έτοιμοι, κομμάτι, μάθει, μιλάμε, ψηλά, αέρα, ερωτήσεις, αυτού, δώσει, + φεύγω, σημείο, τηλεόραση, κυριε, πραγματικότητα, ανάγκη, βοηθήσεις, προσπάθησε, γύρνα, άφησε, λίγα, κάντε, + είvαι, βλέπετε, αυτη, δείπνο, επιτέλους, κέντρο, περίεργο, ακούστε, πλοίο, κάποιες, δικός, σoυ, οικογένειά, + μιλήσει, πλέον, υπόσχομαι, περιμένεις, ήξερε, σκοτώσεις, ενταξει, δώσεις, εκει, ήμασταν, έρχονται, κώλο, + ρωτήσω, παίρνει, σιγά, σήκω, στοιχεία, αδελφή, βασικά, μένει, άκρη, πηγαίνετε, παίρνεις, tο, περιμένουμε, + συγχωρείς, μικρός, πόδι, δίνει, εκατομμύρια, ξενοδοχείο, αποστολή, ενδιαφέρον, χάρηκα, αεροπλάνο, γάμο, + χιλιάδες, υόρκη, οκ, ευχαριστούμε, καλα, κοιτάς, σα, π, χρόνος, ησυχία, ασφάλεια, εκείνος, a, βρήκε, + τέσσερα, βγάλω, μπες, συχνά, ημέρα, μάνα, εν, αγαπάς, άνθρωπο, γραμμή, φωτογραφίες, προσέχεις, ύπνο, + μυστικό, σχετικά, είδους, σκέψου, χριστούγεννα, κόσμου, τομ, μισώ, σύστημα, δουλειές, τελείως, πεθάνω, + αλλάξει, δεξιά, συνήθως, δουλεύω, μάικλ, εβδομάδα, νούμερο, λείπει, έτοιμη, τμήμα, βγει, ψυχή, έπεσε, + κάθαρμα, ματιά, οποία, πληροφορίες, μονο, κρίμα, τραγούδι, μαγαζί, δουλεύεις, μαζι, τέλειο, κύριο, + λέγεται, τσάρλι, πεθάνεις, σκεφτόμουν, καλησπέρα, συγχαρητήρια, φωνή, εκ, άτομο, παίζεις, σκάφος, + φαίνεσαι, ξαφνικά, παραπάνω, ατύχημα, θελω, ξέχνα, ήρθατε, εναντίον, τραπέζι, γράμμα, μείνετε, αμερική, + βασιλιάς, υπό, μπάνιο, ποτε, ίδιος, προφανώς, μαλάκα, αδερφός, άνδρες, nαι, χρονών, ναί, κλειδί, δις, + γιαγιά, παράξενο, πτώμα, βρήκαμε, μιλήσεις, υποτίθεται, ορκίζομαι, δυνατά, ποιό, θάλασσα, παίρνω, άκουσες, + παρέα, αριστερά, έμαθα, μάχη, μηχανή, σάρα, ζωντανός, όνειρο, παλιό, μπορούσαμε, πάντως, ανάμεσα, έχασα, + νωρίς, κάποιοι, άκου, παίζει, φτάνει, δίνω, βγες, υπέροχη, νόημα, έλεγχο, μέτρα, ξερεις, ζει, δείχνει, βρες, τού """ # Source: https://www.101languages.net/russian/most-common-russian-words/ alias words_ru = """ -я, не, что, в, и, ты, это, на, с, он, вы, как, мы, да, а, мне, меня, у, нет, так, но, то, все, тебя, его, -за, о, она, тебе, если, они, бы, же, ну, здесь, к, из, есть, чтобы, для, хорошо, когда, вас, только, по, -вот, просто, был, знаю, нас, всё, было, от, может, кто, вам, очень, их, там, будет, уже, почему, еще, -быть, где, спасибо, ничего, сейчас, или, могу, хочу, нам, чем, мой, до, надо, этого, ее, теперь, давай, -знаешь, нужно, больше, этом, нибудь, раз, со, была, этот, ему, ладно, эй, время, тоже, даже, хочешь, -сказал, ли, себя, думаю, пока, должен, потому, никогда, ни, тут, ещё, её, пожалуйста, сюда, привет, -тогда, конечно, моя, него, сегодня, один, тобой, правда, лучше, об, были, того, можно, мной, всегда, -сказать, день, сэр, без, можешь, чего, эти, дело, значит, лет, много, во, делать, буду, порядке, должны, -такой, ведь, ним, всего, сделать, хотел, твой, жизнь, ей, мистер, потом, через, себе, них, всех, такое, -им, куда, том, мама, после, человек, люди, слишком, иди, зачем, этим, немного, сколько, этой, знаете, -боже, ней, эту, который, отец, свою, деньги, два, под, твоя, мои, никто, моей, думаешь, друг, жизни, -эта, назад, видел, кажется, точно, вместе, люблю, мог, случилось, сам, нравится, черт, какой, людей, -папа, домой, тот, скажи, которые, должна, три, всем, сделал, возможно, прошу, будем, дома, парень, -снова, говорит, место, отсюда, можем, будешь, пошли, делаешь, совсем, говорил, понимаю, завтра, хочет, -простите, разве, давайте, хотите, отлично, сказала, туда, прямо, времени, вами, лишь, своей, хватит, -думал, можете, дом, дела, знать, дай, понял, помочь, говорить, слушай, свои, поэтому, прости, знает, -именно, знал, тем, кого, смотри, каждый, ваш, похоже, найти, моего, наш, мать, одна, имя, про, говорю, -будут, оно, свой, нельзя, извините, стоит, действительно, зовут, поговорить, доктор, перед, несколько, -нужен, происходит, ко, господи, возьми, мою, тех, нами, вижу, должно, наверное, откуда, понимаешь, верно, -скоро, уж, деле, твои, пусть, всю, хотела, при, более, ребята, нее, быстро, подожди, идти, надеюсь, чём, -работу, видеть, такая, этих, уверен, нужна, года, раньше, такие, руки, видишь, какая, посмотри, сын, -самом, ваша, послушай, равно, наши, другой, ага, мир, извини, минут, против, твоей, пор, жить, ж, жаль, -вообще, могли, хотя, человека, пора, ради, говорят, почти, твою, могут, над, весь, первый, чёрт, слышал, -собой, брат, вещи, дня, скажу, говоришь, нормально, своего, мое, ваше, итак, будь, ночь, хоть, ясно, -плохо, дверь, вопрос, господин, давно, денег, ваши, ка, мисс, одну, глаза, пять, будто, между, пойду, -опять, работа, самое, иногда, детей, этому, рад, здорово, бог, одного, ночи, готов, номер, которая, -машину, любовь, дорогая, виду, одно, прекрасно, вон, своих, быстрее, отца, женщина, достаточно, рядом, -убить, таким, пойдем, смерти, дети, такого, правильно, месте, никаких, сказали, здравствуйте, пару, две, -видела, долго, хороший, ах, кроме, алло, нашей, прав, вчера, вечером, жена, миссис, чтоб, друга, нужны, -кем, какие, те, увидеть, утро, смогу, неё, сама, моему, большой, сразу, работать, сердце, стал, своим, -сначала, могла, вроде, ними, говори, голову, дальше, помнишь, либо, ума, одной, вечер, случае, взять, -проблемы, помощь, добрый, год, думала, делает, скорее, слова, капитан, последний, важно, дней, помню, -ночью, утром, моих, произошло, которую, боюсь, также, вашей, ой, стой, твоего, никого, дорогой, убил, -насчет, друзья, самый, проблема, видели, вперед, дерьмо, понятно, чувствую, наша, будете, тому, имею, -вернуться, придется, пришел, спать, стать, столько, говорила, пойти, иначе, работает, девушка, час, -момент, моим, умер, думаете, доброе, слово, новый, часов, мире, знаем, твое, мальчик, однажды, интересно, -конец, играть, a, заткнись, сделали, посмотреть, идет, узнать, свое, права, хорошая, город, джон, -долларов, парни, идем, говорите, уйти, понять, знала, поздно, нашли, работы, скажите, сделаю, увидимся, -какого, другие, идея, пошел, доме, дочь, имеет, приятно, лицо, наших, обо, понимаете, руку, часть, -смотрите, вся, собираюсь, четыре, прежде, хотят, скажешь, чувак, дайте, сделала, кофе, джек, верю, -ждать, затем, большое, сами, неужели, моё, любит, мужчина, дать, господа, таких, осталось, которой, -далеко, вернусь, сильно, ох, сможешь, кому, вашего, посмотрим, машина, подождите, свет, чуть, серьезно, -пришли, оружие, решил, смысле, видите, тихо, нашел, свидания, путь, той, совершенно, следующий, которого, -места, парня, вдруг, пути, мадам, какое, шанс, сестра, нашего, ужасно, минуту, вокруг, другом, иду, -других, хотели, нем, смерть, подумал, фильм, оставь, делаете, уверена, кровь, говорили, внимание, -помогите, идите, держи, получить, оба, взял, спокойно, обычно, мало, забыл, странно, смотреть, поехали, -дал, часа, прекрати, посмотрите, готовы, вернулся, поверить, позже, милая, женщины, любишь, довольно, -обратно, остаться, думать, та, стороны, полиция, тело, тысяч, делал, машины, угодно, муж, году, неплохо, -бога, некоторые, конце, милый, the, рождения, трудно, добро, любви, больно, невозможно, спокойной, -слышишь, типа, получил, которое, приятель, хуже, никому, честь, успокойся, вашу, маленький, выглядит, -чарли, сына, неделю, i, девочка, делаю, шесть, ноги, история, рассказать, послушайте, часто, кстати, -двух, забудь, которых, следует, знают, пришла, семья, станет, матери, ребенок, план, проблем, например, -сделай, воды, немедленно, мира, сэм, телефон, перестань, правду, второй, прощения, ту, наше, уходи, твоих, -помоги, пол, внутри, нему, смог, десять, нашу, около, бывает, самого, большая, леди, сможем, вниз, легко, -делай, единственный, рада, меньше, волнуйся, хотим, полагаю, мам, иметь, своими, мере, наконец, начала, -минутку, работе, пожаловать, другого, двое, никакого, честно, школе, лучший, умереть, дам, насколько, -всей, малыш, оставить, безопасности, ненавижу, школу, осторожно, сынок, джо, таки, пытался, другое, б, -клянусь, машине, недели, стало, истории, пришлось, выглядишь, чему, сможет, купить, слышала, знали, -настоящий, сих, выйти, людям, замечательно, полиции, огонь, пойдём, спросить, дядя, детка, среди, особенно, -твоим, комнате, шоу, выпить, постоянно, делают, позвольте, родители, письмо, городе, случай, месяцев, мужик, -благодарю, o, ребенка, смешно, ответ, города, образом, любой, полностью, увидел, еду, имени, вместо, -абсолютно, обязательно, улице, твоё, убили, ваших, ехать, крови, решение, вина, поможет, своё, секунду, -обещаю, начать, голос, вещь, друзей, показать, нечего, э, месяц, подарок, приехал, самая, молодец, сделаем, -крайней, женщин, собираешься, конца, страшно, новости, идиот, потерял, спасти, вернуть, узнал, слушайте, -хотелось, сон, поняла, прошло, комнату, семь, погоди, главное, рано, корабль, пытаюсь, игра, умерла, -повезло, всему, возьму, таком, моем, глаз, настолько, идём, удачи, готова, семьи, садись, гарри, держись, -звучит, мило, война, человеком, право, такую, вопросы, представить, работаю, имеешь, красивая, идёт, никакой, -профессор, думает, войны, стала, стали, оттуда, известно, слышу, начал, подумать, позвонить, старый, придётся, -историю, вести, твоему, последнее, хочется, миллионов, нашла, способ, отношения, земле, фрэнк, получится, -говоря, связи, многие, пошёл, пистолет, убью, руках, получилось, президент, остановить, тьi, оставил, одним, -you, утра, боль, хорошие, пришёл, открой, брось, вставай, находится, поговорим, кино, людьми, полицию, покажу, +я, не, что, в, и, ты, это, на, с, он, вы, как, мы, да, а, мне, меня, у, нет, так, но, то, все, тебя, его, +за, о, она, тебе, если, они, бы, же, ну, здесь, к, из, есть, чтобы, для, хорошо, когда, вас, только, по, +вот, просто, был, знаю, нас, всё, было, от, может, кто, вам, очень, их, там, будет, уже, почему, еще, +быть, где, спасибо, ничего, сейчас, или, могу, хочу, нам, чем, мой, до, надо, этого, ее, теперь, давай, +знаешь, нужно, больше, этом, нибудь, раз, со, была, этот, ему, ладно, эй, время, тоже, даже, хочешь, +сказал, ли, себя, думаю, пока, должен, потому, никогда, ни, тут, ещё, её, пожалуйста, сюда, привет, +тогда, конечно, моя, него, сегодня, один, тобой, правда, лучше, об, были, того, можно, мной, всегда, +сказать, день, сэр, без, можешь, чего, эти, дело, значит, лет, много, во, делать, буду, порядке, должны, +такой, ведь, ним, всего, сделать, хотел, твой, жизнь, ей, мистер, потом, через, себе, них, всех, такое, +им, куда, том, мама, после, человек, люди, слишком, иди, зачем, этим, немного, сколько, этой, знаете, +боже, ней, эту, который, отец, свою, деньги, два, под, твоя, мои, никто, моей, думаешь, друг, жизни, +эта, назад, видел, кажется, точно, вместе, люблю, мог, случилось, сам, нравится, черт, какой, людей, +папа, домой, тот, скажи, которые, должна, три, всем, сделал, возможно, прошу, будем, дома, парень, +снова, говорит, место, отсюда, можем, будешь, пошли, делаешь, совсем, говорил, понимаю, завтра, хочет, +простите, разве, давайте, хотите, отлично, сказала, туда, прямо, времени, вами, лишь, своей, хватит, +думал, можете, дом, дела, знать, дай, понял, помочь, говорить, слушай, свои, поэтому, прости, знает, +именно, знал, тем, кого, смотри, каждый, ваш, похоже, найти, моего, наш, мать, одна, имя, про, говорю, +будут, оно, свой, нельзя, извините, стоит, действительно, зовут, поговорить, доктор, перед, несколько, +нужен, происходит, ко, господи, возьми, мою, тех, нами, вижу, должно, наверное, откуда, понимаешь, верно, +скоро, уж, деле, твои, пусть, всю, хотела, при, более, ребята, нее, быстро, подожди, идти, надеюсь, чём, +работу, видеть, такая, этих, уверен, нужна, года, раньше, такие, руки, видишь, какая, посмотри, сын, +самом, ваша, послушай, равно, наши, другой, ага, мир, извини, минут, против, твоей, пор, жить, ж, жаль, +вообще, могли, хотя, человека, пора, ради, говорят, почти, твою, могут, над, весь, первый, чёрт, слышал, +собой, брат, вещи, дня, скажу, говоришь, нормально, своего, мое, ваше, итак, будь, ночь, хоть, ясно, +плохо, дверь, вопрос, господин, давно, денег, ваши, ка, мисс, одну, глаза, пять, будто, между, пойду, +опять, работа, самое, иногда, детей, этому, рад, здорово, бог, одного, ночи, готов, номер, которая, +машину, любовь, дорогая, виду, одно, прекрасно, вон, своих, быстрее, отца, женщина, достаточно, рядом, +убить, таким, пойдем, смерти, дети, такого, правильно, месте, никаких, сказали, здравствуйте, пару, две, +видела, долго, хороший, ах, кроме, алло, нашей, прав, вчера, вечером, жена, миссис, чтоб, друга, нужны, +кем, какие, те, увидеть, утро, смогу, неё, сама, моему, большой, сразу, работать, сердце, стал, своим, +сначала, могла, вроде, ними, говори, голову, дальше, помнишь, либо, ума, одной, вечер, случае, взять, +проблемы, помощь, добрый, год, думала, делает, скорее, слова, капитан, последний, важно, дней, помню, +ночью, утром, моих, произошло, которую, боюсь, также, вашей, ой, стой, твоего, никого, дорогой, убил, +насчет, друзья, самый, проблема, видели, вперед, дерьмо, понятно, чувствую, наша, будете, тому, имею, +вернуться, придется, пришел, спать, стать, столько, говорила, пойти, иначе, работает, девушка, час, +момент, моим, умер, думаете, доброе, слово, новый, часов, мире, знаем, твое, мальчик, однажды, интересно, +конец, играть, a, заткнись, сделали, посмотреть, идет, узнать, свое, права, хорошая, город, джон, +долларов, парни, идем, говорите, уйти, понять, знала, поздно, нашли, работы, скажите, сделаю, увидимся, +какого, другие, идея, пошел, доме, дочь, имеет, приятно, лицо, наших, обо, понимаете, руку, часть, +смотрите, вся, собираюсь, четыре, прежде, хотят, скажешь, чувак, дайте, сделала, кофе, джек, верю, +ждать, затем, большое, сами, неужели, моё, любит, мужчина, дать, господа, таких, осталось, которой, +далеко, вернусь, сильно, ох, сможешь, кому, вашего, посмотрим, машина, подождите, свет, чуть, серьезно, +пришли, оружие, решил, смысле, видите, тихо, нашел, свидания, путь, той, совершенно, следующий, которого, +места, парня, вдруг, пути, мадам, какое, шанс, сестра, нашего, ужасно, минуту, вокруг, другом, иду, +других, хотели, нем, смерть, подумал, фильм, оставь, делаете, уверена, кровь, говорили, внимание, +помогите, идите, держи, получить, оба, взял, спокойно, обычно, мало, забыл, странно, смотреть, поехали, +дал, часа, прекрати, посмотрите, готовы, вернулся, поверить, позже, милая, женщины, любишь, довольно, +обратно, остаться, думать, та, стороны, полиция, тело, тысяч, делал, машины, угодно, муж, году, неплохо, +бога, некоторые, конце, милый, the, рождения, трудно, добро, любви, больно, невозможно, спокойной, +слышишь, типа, получил, которое, приятель, хуже, никому, честь, успокойся, вашу, маленький, выглядит, +чарли, сына, неделю, i, девочка, делаю, шесть, ноги, история, рассказать, послушайте, часто, кстати, +двух, забудь, которых, следует, знают, пришла, семья, станет, матери, ребенок, план, проблем, например, +сделай, воды, немедленно, мира, сэм, телефон, перестань, правду, второй, прощения, ту, наше, уходи, твоих, +помоги, пол, внутри, нему, смог, десять, нашу, около, бывает, самого, большая, леди, сможем, вниз, легко, +делай, единственный, рада, меньше, волнуйся, хотим, полагаю, мам, иметь, своими, мере, наконец, начала, +минутку, работе, пожаловать, другого, двое, никакого, честно, школе, лучший, умереть, дам, насколько, +всей, малыш, оставить, безопасности, ненавижу, школу, осторожно, сынок, джо, таки, пытался, другое, б, +клянусь, машине, недели, стало, истории, пришлось, выглядишь, чему, сможет, купить, слышала, знали, +настоящий, сих, выйти, людям, замечательно, полиции, огонь, пойдём, спросить, дядя, детка, среди, особенно, +твоим, комнате, шоу, выпить, постоянно, делают, позвольте, родители, письмо, городе, случай, месяцев, мужик, +благодарю, o, ребенка, смешно, ответ, города, образом, любой, полностью, увидел, еду, имени, вместо, +абсолютно, обязательно, улице, твоё, убили, ваших, ехать, крови, решение, вина, поможет, своё, секунду, +обещаю, начать, голос, вещь, друзей, показать, нечего, э, месяц, подарок, приехал, самая, молодец, сделаем, +крайней, женщин, собираешься, конца, страшно, новости, идиот, потерял, спасти, вернуть, узнал, слушайте, +хотелось, сон, поняла, прошло, комнату, семь, погоди, главное, рано, корабль, пытаюсь, игра, умерла, +повезло, всему, возьму, таком, моем, глаз, настолько, идём, удачи, готова, семьи, садись, гарри, держись, +звучит, мило, война, человеком, право, такую, вопросы, представить, работаю, имеешь, красивая, идёт, никакой, +профессор, думает, войны, стала, стали, оттуда, известно, слышу, начал, подумать, позвонить, старый, придётся, +историю, вести, твоему, последнее, хочется, миллионов, нашла, способ, отношения, земле, фрэнк, получится, +говоря, связи, многие, пошёл, пистолет, убью, руках, получилось, президент, остановить, тьi, оставил, одним, +you, утра, боль, хорошие, пришёл, открой, брось, вставай, находится, поговорим, кино, людьми, полицию, покажу, волосы, последние, брата, месяца """ @@ -595,9 +595,9 @@ fn gen_word_pairs[words: String = words_en]() -> List[String]: return result -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmarks -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_small_keys[s: String](mut b: Bencher) raises: var words = gen_word_pairs[s]() @@ -648,9 +648,9 @@ fn bench_long_key_new_hash_function[s: String](mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_small_keys[words_ar]](BenchId("bench_small_keys_ar")) diff --git a/stdlib/benchmarks/math/bench_math.mojo b/stdlib/benchmarks/math/bench_math.mojo index b7d4c1ae43..3e17eaf282 100644 --- a/stdlib/benchmarks/math/bench_math.mojo +++ b/stdlib/benchmarks/math/bench_math.mojo @@ -19,9 +19,9 @@ from random import * from benchmark import Bench, BenchConfig, Bencher, BenchId, Unit, keep, run -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Data -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# alias input_type = Float32 @@ -54,9 +54,9 @@ fn make_int_inputs(begin: Int, end: Int, num: Int) -> List[Int]: var inputs = make_inputs(0, 10_000, 1_000_000) var int_inputs = make_int_inputs(0, 10_000_000, 1_000_000) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark math_func -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter @@ -73,9 +73,9 @@ fn bench_math[ b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark fma -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_math3[ math_f3p: fn[type: DType, size: Int] ( @@ -92,9 +92,9 @@ fn bench_math3[ b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark lcm/gcd -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_math2[math_f2p: fn (Int, Int, /) -> Int](mut b: Bencher) raises: @always_inline @@ -107,9 +107,9 @@ fn bench_math2[math_f2p: fn (Int, Int, /) -> Int](mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): seed() var m = Bench(BenchConfig(num_repetitions=1)) diff --git a/stdlib/benchmarks/utils/bench_formatter.mojo b/stdlib/benchmarks/utils/bench_formatter.mojo index 818a0a2cff..12896f3c91 100644 --- a/stdlib/benchmarks/utils/bench_formatter.mojo +++ b/stdlib/benchmarks/utils/bench_formatter.mojo @@ -22,14 +22,14 @@ from builtin.dtype import _uint_type_of_width from utils.stringref import _align_down, _memchr, _memmem -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Data -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmarks -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_writer_int[n: Int](mut b: Bencher) raises: @always_inline @@ -54,9 +54,9 @@ fn bench_writer_simd[n: Int](mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_writer_int[42]](BenchId("bench_writer_int_42")) diff --git a/stdlib/benchmarks/utils/bench_memmem.mojo b/stdlib/benchmarks/utils/bench_memmem.mojo index 97925b2884..777557784e 100644 --- a/stdlib/benchmarks/utils/bench_memmem.mojo +++ b/stdlib/benchmarks/utils/bench_memmem.mojo @@ -23,9 +23,9 @@ from memory import UnsafePointer, bitcast, memcmp, pack_bits from utils.stringref import _align_down, _memchr, _memmem -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Data -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# var haystack = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sed dictum est, et finibus ipsum. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam tincidunt vel lacus vitae pulvinar. Donec ac ligula elementum, mollis purus a, lacinia quam. Maecenas vulputate mauris quis sem euismod sollicitudin. Proin accumsan nulla vel nisl congue varius. Morbi a erat dui. Aliquam maximus interdum orci, vitae pretium lorem bibendum non. Vestibulum eu lacus ullamcorper, egestas dui vel, pharetra ipsum. Pellentesque sagittis, urna a tincidunt sodales, leo sem placerat eros, vitae molestie felis diam at dolor. Donec viverra sem sit amet facilisis laoreet. Morbi semper convallis nisi, vitae congue velit tincidunt vel. Fusce ultrices, libero vel venenatis placerat, justo tellus porttitor massa, at volutpat tortor nunc id dui. Morbi eu ex quis odio porttitor ultricies vel eget massa. Aenean quis luctus nulla. Fusce sit amet leo at quam hendrerit mattis. Morbi sed quam nisl. Quisque purus enim, iaculis sed laoreet vel, pellentesque ut orci. Vivamus risus orci, varius eu pharetra quis, tincidunt non enim. Suspendisse bibendum lacus ex, quis blandit lectus malesuada a. Maecenas iaculis porta lacus, sit amet tristique ante scelerisque non. Proin auctor elit in lacus dictum egestas. Pellentesque tincidunt justo sed vehicula blandit. Pellentesque vehicula facilisis tellus in viverra. @@ -143,9 +143,9 @@ Curabitur auctor volutpat diam vitae vehicula. Vivamus est arcu, efficitur nec i var needle = "school" # a word intentionally not in the test data -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Baseline `_memmem` implementation -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline fn _memmem_baseline[ type: DType @@ -185,9 +185,9 @@ fn _memmem_baseline[ return UnsafePointer[Scalar[type]]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmarks -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @parameter fn bench_find_baseline(mut b: Bencher) raises: @always_inline @@ -218,9 +218,9 @@ fn bench_find_optimized(mut b: Bencher) raises: b.iter[call_fn]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Benchmark Main -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# def main(): var m = Bench(BenchConfig(num_repetitions=1)) m.bench_function[bench_find_baseline](BenchId("find_baseline")) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index dc07d62088..82002c152c 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -111,9 +111,9 @@ structure of header comments separating the various kinds of methods that can be defined on structs. ```mojo -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # MyStruct -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# struct MyStruct(Sized, Stringable): diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index d9a3f7f71b..6a9d585fb5 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -26,9 +26,9 @@ import bit from ._b64encode import b64encode_with_buffers as _b64encode_with_buffers -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -59,9 +59,9 @@ fn _ascii_to_value(char: String) -> Int: return -1 -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # b64encode -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # TODO: Use Span instead of List as input when Span is easier to use @@ -106,9 +106,9 @@ fn b64encode(input_bytes: List[UInt8, _]) -> String: return String(result^) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # b64decode -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -153,9 +153,9 @@ fn b64decode(str: String) -> String: return p -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # b16encode -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn b16encode(str: String) -> String: @@ -190,9 +190,9 @@ fn b16encode(str: String) -> String: return String(out^) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # b16decode -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 5405411ab1..74a428d3df 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -22,9 +22,9 @@ from bit import count_leading_zeros from sys import llvm_intrinsic, sizeof from sys.info import bitwidthof -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # count_leading_zeros -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -66,9 +66,9 @@ fn count_leading_zeros[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # count_trailing_zeros -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -110,9 +110,9 @@ fn count_trailing_zeros[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bit_reverse -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -154,9 +154,9 @@ fn bit_reverse[ ](val) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # byte_swap -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -212,9 +212,9 @@ fn byte_swap[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # pop_count -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -256,9 +256,9 @@ fn pop_count[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bit_not -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -286,9 +286,9 @@ fn bit_not[ return __mlir_op.`pop.simd.xor`(val.value, neg_one.value) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bit_width -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -343,9 +343,9 @@ fn bit_width[ return bitwidth - leading_zero -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # is_power_of_two -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # reference: https://en.cppreference.com/w/cpp/numeric/has_single_bit @@ -391,9 +391,9 @@ fn is_power_of_two[ return (val > 0) & (val & (val - 1) == 0) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bit_ceil -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # reference: https://en.cppreference.com/w/cpp/numeric/bit_ceil @@ -447,9 +447,9 @@ fn bit_ceil[ return (val > 1).select(1 << bit_width(val - ones), ones) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bit_floor -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # reference: https://en.cppreference.com/w/cpp/numeric/bit_floor @@ -500,9 +500,9 @@ fn bit_floor[ return (val > 0).select(1 << (bit_width(val) - 1), zeros) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # rotate_bits_left -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -578,9 +578,9 @@ fn rotate_bits_left[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # rotate_bits_right -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index c116d52408..3b5a12fdac 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -13,9 +13,9 @@ from builtin.range import _StridedRangeIterator, _UIntStridedRangeIterator -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # __MLIRType -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @register_passable("trivial") @@ -23,9 +23,9 @@ struct __MLIRType[T: AnyTrivialRegType](Movable, Copyable): var value: T -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # parameter_for -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# trait _IntNext(Copyable): diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index be5ddaaa05..1e44efe5b4 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -17,9 +17,9 @@ These are Mojo built-ins, so you don't need to import them. from memory import Pointer, UnsafePointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # ListLiteral -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): @@ -119,9 +119,9 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): return value in self.storage -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # VariadicList / VariadicListMem -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value @@ -469,9 +469,9 @@ struct VariadicListMem[ ](0, self) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # VariadicPack -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 1d1c37d16f..4ac807acd6 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -511,9 +511,9 @@ struct DType( """ return 8 * self.sizeof() - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # dispatch_integral - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline fn dispatch_integral[ @@ -548,9 +548,9 @@ struct DType( else: raise Error("only integral types are supported") - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # dispatch_floating - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline fn dispatch_floating[ @@ -626,9 +626,9 @@ struct DType( "dispatch_custom: dynamic_type does not match any dtype parameters" ) - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # dispatch_arithmetic - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline fn dispatch_arithmetic[ diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 1d19b5d201..e282d56ba0 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -23,9 +23,9 @@ from memory.memory import _free from utils import StringRef -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Error -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @register_passable diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 906e8d40dc..6cee98bafa 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -17,9 +17,9 @@ These are Mojo built-ins, so you don't need to import them. from math import Ceilable, CeilDivable, Floorable, Truncable -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # FloatLiteral -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value diff --git a/stdlib/src/builtin/format_int.mojo b/stdlib/src/builtin/format_int.mojo index 0239369fbe..0598bb816a 100644 --- a/stdlib/src/builtin/format_int.mojo +++ b/stdlib/src/builtin/format_int.mojo @@ -24,9 +24,9 @@ from utils import StaticString, StringSlice alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bin -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn bin(num: Scalar, /, *, prefix: StaticString = "0b") -> String: @@ -82,9 +82,9 @@ fn bin[T: Indexer, //](num: T, /, *, prefix: StaticString = "0b") -> String: return bin(Scalar[DType.index](index(num)), prefix=prefix) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # hex -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn hex(value: Scalar, /, *, prefix: StaticString = "0x") -> String: @@ -144,9 +144,9 @@ fn hex(value: Scalar[DType.bool], /, *, prefix: StaticString = "0x") -> String: return hex(value.cast[DType.int8](), prefix=prefix) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # oct -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn oct(value: Scalar, /, *, prefix: StaticString = "0o") -> String: @@ -206,9 +206,9 @@ fn oct(value: Scalar[DType.bool], /, *, prefix: StaticString = "0o") -> String: return oct(value.cast[DType.int8](), prefix=prefix) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Integer formatting utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn _try_format_int( diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 61a74df2dc..2c9a13234b 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -760,9 +760,9 @@ struct Int( """ return __mlir_op.`index.or`(self.value, rhs.value) - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # In place operations. - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __iadd__(mut self, rhs: Int): @@ -873,9 +873,9 @@ struct Int( """ self = self | rhs - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # Reversed operations - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __radd__(self, value: Int) -> Int: diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 360ed0e9a6..3d50918458 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -373,9 +373,9 @@ struct IntLiteral( ](self.value, rhs.value) ) - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # In place operations. - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __iadd__(mut self, rhs: Self): @@ -458,9 +458,9 @@ struct IntLiteral( """ self = self | rhs - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # Reversed operations - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __radd__(self, value: Self) -> Self: @@ -717,9 +717,9 @@ struct IntLiteral( """ return -(self // -denominator) - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # Methods - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn _bit_width(self) -> IntLiteral: diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 093cc41914..78447a82fb 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -24,9 +24,9 @@ from memory import UnsafePointer from utils import Span -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # sort -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# alias insertion_sort_threshold = 32 @@ -260,9 +260,9 @@ fn _quicksort[ stack.append(ImmSpan(ptr=ptr, length=pivot)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # stable sort -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn _merge[ @@ -366,9 +366,9 @@ fn _stable_sort[ temp_buff.free() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # partition -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -501,9 +501,9 @@ fn partition[ _partition[_cmp_fn](span, k) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # sort -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Junction from public to private API @@ -686,9 +686,9 @@ fn sort[ sort[_cmp_fn, stable=stable](span) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # sort networks -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 554ddeddfd..3e889e760a 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -26,9 +26,9 @@ from utils._visualizers import lldb_formatter_wrapping_type from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry from utils.string_slice import _StringSliceIter, _to_string_list -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # StringLiteral -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 00bede563f..b18221e3b3 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -21,9 +21,9 @@ from memory import UnsafePointer from utils._visualizers import lldb_formatter_wrapping_type -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Tuple -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/uint.mojo b/stdlib/src/builtin/uint.mojo index dad8ed6167..9c3feb155f 100644 --- a/stdlib/src/builtin/uint.mojo +++ b/stdlib/src/builtin/uint.mojo @@ -398,9 +398,9 @@ struct UInt(IntLike, _HashableWithHasher): """ return __mlir_op.`index.ceildivu`(self.value, denominator.value) - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # In place operations. - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __iadd__(mut self, rhs: UInt): @@ -511,9 +511,9 @@ struct UInt(IntLike, _HashableWithHasher): """ self = self | rhs - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# # Reversed operations - # ===----------------------------------------------------------------------===# + # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __radd__(self, value: Self) -> Self: diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index cdaf52332d..c79efe4a30 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -26,9 +26,9 @@ from collections import Optional from bit import bit_ceil from memory import UnsafePointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Deque -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# struct Deque[ElementType: CollectionElement]( diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index bb8809612d..93064e0220 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -25,9 +25,9 @@ from sys.intrinsics import _type_is_eq from memory import UnsafePointer from memory.maybe_uninitialized import UnsafeMaybeUninitialized -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Array -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn _inline_array_construction_checks[size: Int](): diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 516be41287..4e2e90349f 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -24,9 +24,9 @@ from sys.intrinsics import _type_is_eq from memory.maybe_uninitialized import UnsafeMaybeUninitialized -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # InlineList -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value struct _InlineListIter[ list_mutability: Bool, //, diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 19b477a47e..b470023659 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -30,9 +30,9 @@ from utils import Span from .optional import Optional -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # List -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 95c78a9706..73518b55a5 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -43,9 +43,9 @@ struct _NoneType(CollectionElement, CollectionElementNew): pass -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Optional -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value @@ -383,9 +383,9 @@ struct Optional[T: CollectionElement]( return default -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # OptionalReg -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @register_passable("trivial") diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 1c9385c528..62196e6a15 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -25,9 +25,9 @@ from memory import Pointer, UnsafePointer, memcpy from utils import StaticTuple -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # _VecIter -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value @@ -54,9 +54,9 @@ struct _VecIter[ return self.size - self.i -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # InlinedFixedVector -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/math/polynomial.mojo b/stdlib/src/math/polynomial.mojo index 8397218380..35384d33c1 100644 --- a/stdlib/src/math/polynomial.mojo +++ b/stdlib/src/math/polynomial.mojo @@ -21,9 +21,9 @@ from math.polynomial import polynomial_evaluate from collections import List -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # polynomial_evaluate -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -49,9 +49,9 @@ fn polynomial_evaluate[ return _horner_evaluate[coefficients](x) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Horner Method -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 1cbf5c43d3..8d3a525cb4 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -44,9 +44,9 @@ fn _align_down(value: Int, alignment: Int) -> Int: return value._positive_div(alignment) * alignment -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # memcmp -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -146,9 +146,9 @@ fn memcmp[ return _memcmp_impl(s1.bitcast[Byte](), s2.bitcast[Byte](), byte_count) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # memcpy -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -259,9 +259,9 @@ fn memcpy[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # memset -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -304,9 +304,9 @@ fn memset[ _memset_impl(ptr.bitcast[Byte](), value, count * sizeof[type]()) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # memset_zero -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -356,9 +356,9 @@ fn memset_zero[ ptr.store(i, 0) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # stack_allocation -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -447,9 +447,9 @@ fn stack_allocation[ ]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # malloc -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -475,9 +475,9 @@ fn _malloc[ ](alignment.value, size.value) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # aligned_free -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 4fa37a6e61..db7b78bc18 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -20,9 +20,9 @@ from memory import Pointer """ -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # AddressSpace -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value @@ -290,9 +290,9 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): writer.write("AddressSpace(", self.value(), ")") -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Pointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 82e995de53..450c18d199 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -21,9 +21,9 @@ from memory import bitcast from sys import bitwidthof -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # bitcast -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index e2b8f1c65e..5eaac49644 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -284,9 +284,9 @@ struct Atomic[type: DType, *, scope: StringLiteral = ""]: Self.min(UnsafePointer.address_of(self.value), rhs) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/sys/_assembly.mojo b/stdlib/src/sys/_assembly.mojo index 6cb2123393..2074afde29 100644 --- a/stdlib/src/sys/_assembly.mojo +++ b/stdlib/src/sys/_assembly.mojo @@ -14,9 +14,9 @@ from sys.intrinsics import _mlirtype_is_eq -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # 0-arg -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") diff --git a/stdlib/src/sys/_libc.mojo b/stdlib/src/sys/_libc.mojo index 6751a60e62..ca0104d19c 100644 --- a/stdlib/src/sys/_libc.mojo +++ b/stdlib/src/sys/_libc.mojo @@ -22,9 +22,9 @@ from sys.ffi import OpaquePointer, c_char, c_int from memory import UnsafePointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # stdlib.h — core C standard library operations -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -37,9 +37,9 @@ fn exit(status: c_int): external_call["exit", NoneType](status) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # stdio.h — input/output operations -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# alias FILE_ptr = OpaquePointer @@ -74,9 +74,9 @@ fn pclose(stream: FILE_ptr) -> c_int: return external_call["pclose", c_int](stream) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # unistd.h -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -86,9 +86,9 @@ fn dup(oldfd: c_int) -> c_int: return external_call[name, c_int](oldfd) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # dlfcn.h — dynamic library operations -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 6d65ab7447..2d6926568d 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -404,9 +404,9 @@ fn _get_dylib_function[ return new_func -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Globals -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# struct _Global[ @@ -462,9 +462,9 @@ fn _get_global_or_null[name: StringLiteral]() -> OpaquePointer: ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # external_call -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -502,9 +502,9 @@ fn external_call[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # _external_call_const -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 2fe91f3b99..ccd39c7ee2 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -864,9 +864,9 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: return (major, minor, patch) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Detect GPU on host side -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index dbc833dcc8..3eb81a505a 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -26,9 +26,9 @@ from memory import AddressSpace, UnsafePointer from ._assembly import inlined_assembly from .info import is_nvidia_gpu, sizeof -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # llvm_intrinsic -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -90,9 +90,9 @@ fn llvm_intrinsic[ ](loaded_pack) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # _gather -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # NOTE: Converting from a scalar to a pointer is unsafe! The resulting pointer @@ -176,9 +176,9 @@ fn gather[ return result -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # _scatter -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -254,9 +254,9 @@ fn scatter[ _ = base -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # prefetch -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @register_passable("trivial") @@ -500,9 +500,9 @@ fn prefetch[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # masked load -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -546,9 +546,9 @@ fn masked_load[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # masked store -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -588,9 +588,9 @@ fn masked_store[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # compressed store -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -628,9 +628,9 @@ fn compressed_store[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # strided load -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") @@ -668,9 +668,9 @@ fn strided_load[ return gather(offset, mask, passthrough) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # strided store -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline("nodebug") diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 71eb1e80c2..853c6d2d56 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -34,9 +34,9 @@ from sys._assembly import inlined_assembly from memory import UnsafePointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Enums used in time.h 's glibc alias _CLOCK_REALTIME = 0 @@ -169,9 +169,9 @@ fn _thread_cputime_nanoseconds() -> Int: return _gettime_as_nsec_unix(_CLOCK_THREAD_CPUTIME_ID) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # perf_counter -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -188,9 +188,9 @@ fn perf_counter() -> Float64: return Float64(_monotonic_nanoseconds()) / _NSEC_PER_SEC -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # perf_counter_ns -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -215,9 +215,9 @@ fn perf_counter_ns() -> Int: return _monotonic_nanoseconds() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # now -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -235,9 +235,9 @@ fn now() -> Int: return perf_counter_ns() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # monotonic -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -254,9 +254,9 @@ fn monotonic() -> Int: return perf_counter_ns() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # time_function -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -336,9 +336,9 @@ fn time_function[func: fn () capturing [_] -> None]() -> Int: return abort[Int](err) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # sleep -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn sleep(sec: Float64): diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo index af4bbc8d7c..5c1dc2cf8b 100644 --- a/stdlib/src/utils/format.mojo +++ b/stdlib/src/utils/format.mojo @@ -22,9 +22,9 @@ from utils.string_slice import Stringlike # people who want to write their own templating engines. This is not yet done # because the implementation is incomplete and we are missing crucial features. -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Formatter -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # NOTE(#3765): an interesting idea would be to allow custom start and end @@ -417,9 +417,9 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): auto_idx += 1 -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Format Specification -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# trait _CurlyEntryFormattable(Stringable, Representable): @@ -714,6 +714,6 @@ struct _FormatSpec: res += str(item) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utils -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 0ea645ee88..b526aa84ff 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -29,9 +29,9 @@ from builtin.io import _get_dtype_printf_format, _snprintf from . import unroll from .static_tuple import StaticTuple -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -48,10 +48,10 @@ fn _reduce_and_fn(a: Bool, b: Bool) -> Bool: return a and b -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Integer and Bool Tuple Utilities: # Utilities to operate on tuples of integers or tuples of bools. -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -147,9 +147,9 @@ fn _bool_tuple_reduce[ return c -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # IndexList: -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn _type_of_width[bitwidth: Int, unsigned: Bool]() -> DType: @@ -828,9 +828,9 @@ struct IndexList[ ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Factory functions for creating index. -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline fn Index[ T0: Intable, //, @@ -1029,9 +1029,9 @@ fn Index[ return __type_of(result)(int(x), int(y), int(z), int(w), int(v)) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utils -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index cf8caafc31..add0e6c7cc 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -23,9 +23,9 @@ from memory import UnsafePointer, memcpy from utils import StringSlice, Variant -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # InlineString -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value @@ -296,9 +296,9 @@ struct InlineString(Sized, Stringable, CollectionElement, CollectionElementNew): ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # __FixedString -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value diff --git a/stdlib/src/utils/lock.mojo b/stdlib/src/utils/lock.mojo index 3916cd92cc..6459952b4e 100644 --- a/stdlib/src/utils/lock.mojo +++ b/stdlib/src/utils/lock.mojo @@ -18,9 +18,9 @@ from time import sleep from memory import UnsafePointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # SpinWaiter -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# struct SpinWaiter: diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index c0a6aa3f5e..1d61112379 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -23,9 +23,9 @@ from utils import unroll """ -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # unroll -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -51,9 +51,9 @@ fn unroll[ func[i, j]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # unroll -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -84,9 +84,9 @@ fn unroll[ func[i, j, k]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # unroll _ZeroStartingRange -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -125,9 +125,9 @@ fn unroll[ func[i]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # unroll _SequentialRange -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline fn unroll[ func: fn[idx: Int] () capturing [_] -> None, @@ -164,9 +164,9 @@ fn unroll[ func[i]() -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # unroll _StridedRange -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline fn unroll[ func: fn[idx: Int] () capturing [_] -> None, diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 7a907b7ed0..6904888e65 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -21,9 +21,9 @@ from utils import StaticTuple from memory import UnsafePointer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline @@ -93,9 +93,9 @@ fn _create_array[ return array -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # StaticTuple -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn _static_tuple_construction_checks[size: Int](): diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 666a25c45c..34a33eb3e6 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -1010,9 +1010,9 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( return output^ -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utils -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# trait Stringlike: diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index e2a0303100..eaf6a285e3 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -35,9 +35,9 @@ fn _align_down(value: Int, alignment: Int) -> Int: return value._positive_div(alignment) * alignment -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # StringRef -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @value @@ -691,9 +691,9 @@ struct StringRef( ) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utilities -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# @always_inline diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 2d9351036f..4d53f23afd 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -20,7 +20,7 @@ from memory import UnsafePointer, memcpy from utils import Span, StaticString -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# trait Writer: @@ -119,9 +119,9 @@ trait Writer: # To only have to implement `write_bytes` to make a type a valid Writer -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Writable -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# trait Writable: @@ -157,9 +157,9 @@ trait Writable: ... -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Utils -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn write_args[ diff --git a/stdlib/test/collections/test_deque.mojo b/stdlib/test/collections/test_deque.mojo index 84b759eb91..fd2db54440 100644 --- a/stdlib/test/collections/test_deque.mojo +++ b/stdlib/test/collections/test_deque.mojo @@ -16,9 +16,9 @@ from collections import Deque from testing import assert_equal, assert_false, assert_raises, assert_true -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # Implementation tests -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn test_impl_init_default() raises: @@ -666,9 +666,9 @@ fn test_impl_imul() raises: assert_equal((q._data + 0)[], 3) -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# # API Interface tests -# ===----------------------------------------------------------------------===# +# ===-----------------------------------------------------------------------===# fn test_init_variadic_list() raises: From dbf271128b231b5607c0a672db843f0614f8d0af Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 3 Dec 2024 02:00:57 -0800 Subject: [PATCH 1960/2019] [Stdlib] Extend StringLiteral import support to work on Stringable MODULAR_ORIG_COMMIT_REV_ID: b4843f75d8d57376b45716189e89a2f0c41b1315 --- docs/changelog.md | 4 +-- stdlib/src/builtin/constrained.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 27 +++++++++++--------- stdlib/test/builtin/test_string_literal.mojo | 10 ++++++++ stdlib/test/utils/test_string_slice.mojo | 2 +- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 640824629c..664fe17057 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -288,9 +288,9 @@ what we publish. memory allocation and performance. These options allow for optimized memory usage and reduced buffer reallocations, providing flexibility based on application requirements. -- A new `StringLiteral.from_string[someString]()` method is available. It +- A new `StringLiteral.get[some_stringable]()` method is available. It allows forming a runtime-constant StringLiteral from a compile-time-dynamic - `String` value. + `Stringable` value. ### 🦋 Changed diff --git a/stdlib/src/builtin/constrained.mojo b/stdlib/src/builtin/constrained.mojo index 09d934ff1c..b50ca11e71 100644 --- a/stdlib/src/builtin/constrained.mojo +++ b/stdlib/src/builtin/constrained.mojo @@ -88,4 +88,4 @@ fn constrained[cond: Bool, msg: String](): "at least two cores are required" ]() """ - constrained[cond, StringLiteral.from_string[msg]()]() + constrained[cond, StringLiteral.get[msg]()]() diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 3e889e760a..dc7d05d49a 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -89,7 +89,7 @@ struct StringLiteral( # for now. @always_inline("nodebug") @staticmethod - fn from_string[value: String]() -> StringLiteral: + fn _from_string[value: String]() -> StringLiteral: """Form a string literal from an arbitrary compile-time String value. Parameters: @@ -106,6 +106,20 @@ struct StringLiteral( `> : !kgen.string`, ] + @always_inline("nodebug") + @staticmethod + fn get[type: Stringable, //, value: type]() -> StringLiteral: + """Form a string literal from an arbitrary compile-time stringable value. + + Parameters: + type: The type of the value. + value: The value to serialize. + + Returns: + The string value as a StringLiteral. + """ + return Self._from_string[str(value)]() + # ===-------------------------------------------------------------------===# # Operator dunders # ===-------------------------------------------------------------------===# @@ -920,14 +934,3 @@ struct StringLiteral( A copy of the string with no leading whitespaces. """ return str(self).lstrip() - - -fn _to_string_literal[val: Int]() -> StringLiteral: - alias s = StringLiteral.from_string[str(val)]() - return s - - -fn _to_string_literal[val: SIMD]() -> StringLiteral: - constrained[val.type.is_integral(), "input type must be integral"]() - alias s = StringLiteral.from_string[str(val)]() - return s diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 2aaa26e063..28ca91a1af 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -474,6 +474,15 @@ def test_float_conversion(): _ = ("not a float").__float__() +def test_string_literal_from_stringable(): + assert_equal(StringLiteral.get["hello"](), "hello") + assert_equal(StringLiteral.get[String("hello")](), "hello") + assert_equal(StringLiteral.get[42](), "42") + assert_equal( + StringLiteral.get[SIMD[DType.int64, 4](1, 2, 3, 4)](), "[1, 2, 3, 4]" + ) + + def main(): test_add() test_iadd() @@ -506,3 +515,4 @@ def main(): test_split() test_splitlines() test_float_conversion() + test_string_literal_from_stringable() diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 191d98a7d6..057601c270 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -487,7 +487,7 @@ def test_splitlines(): _assert_equal(s.splitlines(keepends=True), items) -fn main() raises: +def main(): test_string_literal_byte_span() test_string_byte_span() test_heap_string_from_string_slice() From b03321b4f8ceee284ec4b5394fa8f198f0b10c44 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 3 Dec 2024 17:07:28 -0600 Subject: [PATCH 1961/2019] [DRAFT] feat: Use `Origin[..]` instead of `Origin[..].type` in `Span` and `StringSlice` * Change `ImmutableOrigin` and `MutableOrigin` to be aliases for `Origin[False]` and `Origin[True]`, respectively, instead of raw `!lit.origin` MLIR types. MODULAR_ORIG_COMMIT_REV_ID: 62c5102b8ded23f6a5507021f2863d1c1080a383 --- stdlib/src/builtin/builtin_list.mojo | 5 ++++- stdlib/src/builtin/type_aliases.mojo | 4 ++-- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/utils/span.mojo | 16 +++++++++------- stdlib/src/utils/string_slice.mojo | 4 ++-- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 1e44efe5b4..80047f0343 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -236,7 +236,10 @@ struct _VariadicListMemIter[ alias variadic_list_type = VariadicListMem[elt_type, elt_origin] var index: Int - var src: Pointer[Self.variadic_list_type, list_origin] + var src: Pointer[ + Self.variadic_list_type, + list_origin._mlir_origin, + ] fn __init__( mut self, index: Int, ref [list_origin]list: Self.variadic_list_type diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 5bbee8d763..7c7c6ff25b 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -18,10 +18,10 @@ These are Mojo built-ins, so you don't need to import them. alias AnyTrivialRegType = __mlir_type.`!kgen.type` """Represents any register passable Mojo data type.""" -alias ImmutableOrigin = __mlir_type.`!lit.origin<0>` +alias ImmutableOrigin = Origin[False] """Immutable origin reference type.""" -alias MutableOrigin = __mlir_type.`!lit.origin<1>` +alias MutableOrigin = Origin[True] """Mutable origin reference type.""" alias ImmutableAnyOrigin = __mlir_attr.`#lit.any.origin : !lit.origin<0>` diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index d7b236af72..ecabe6c9d4 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -146,7 +146,7 @@ struct ArcPointer[T: Movable]( ]( ref [self_life]self, ) -> ref [ - _lit_mut_cast[self_life, result_mutable=True].result + _lit_mut_cast[self_life._mlir_origin, result_mutable=True].result ] T: """Returns a mutable reference to the managed value. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 3ec2b3030c..396744aa4e 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -48,7 +48,7 @@ trait AsBytes: struct _SpanIter[ is_mutable: Bool, //, T: CollectionElement, - origin: Origin[is_mutable].type, + origin: Origin[is_mutable], forward: Bool = True, ]: """Iterator for Span. @@ -70,7 +70,7 @@ struct _SpanIter[ @always_inline fn __next__( mut self, - ) -> Pointer[T, origin]: + ) -> Pointer[T, origin._mlir_origin]: @parameter if forward: self.index += 1 @@ -97,7 +97,7 @@ struct _SpanIter[ struct Span[ is_mutable: Bool, //, T: CollectionElement, - origin: Origin[is_mutable].type, + origin: Origin[is_mutable], ](CollectionElementNew): """A non owning view of contiguous data. @@ -259,7 +259,7 @@ struct Span[ return self._data - fn as_ref(self) -> Pointer[T, origin]: + fn as_ref(self) -> Pointer[T, origin._mlir_origin]: """ Gets a Pointer to the first element of this slice. @@ -267,7 +267,7 @@ struct Span[ A Pointer pointing at the first element of this slice. """ - return Pointer[T, origin].address_of(self._data[0]) + return Pointer[T, origin._mlir_origin].address_of(self._data[0]) @always_inline fn copy_from[ @@ -358,13 +358,15 @@ struct Span[ for element in self: element[] = value - fn get_immutable(self) -> Span[T, _lit_mut_cast[origin, False].result]: + fn get_immutable( + self, + ) -> Span[T, _lit_mut_cast[origin._mlir_origin, False].result]: """ Return an immutable version of this span. Returns: A span covering the same elements, but without mutability. """ - return Span[T, _lit_mut_cast[origin, False].result]( + return Span[T, _lit_mut_cast[origin._mlir_origin, False].result]( ptr=self._data, length=self._len ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 34a33eb3e6..394dccf89c 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -170,7 +170,7 @@ fn _memrmem[ @value struct _StringSliceIter[ is_mutable: Bool, //, - origin: Origin[is_mutable].type, + origin: Origin[is_mutable], forward: Bool = True, ]: """Iterator for `StringSlice` over unicode characters. @@ -235,7 +235,7 @@ struct _StringSliceIter[ @value @register_passable("trivial") -struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable].type,]( +struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]( Stringable, Sized, Writable, From d5402146c7a238415e6f9d4ffb82db73de3e8270 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 3 Dec 2024 17:39:50 -0600 Subject: [PATCH 1962/2019] [stdlib] feat: Use `Origin[..]` in `UnsafePointer` parameter MODULAR_ORIG_COMMIT_REV_ID: 229c6c76108284ab19aaa2e3fef02c288a1706ec --- stdlib/src/memory/unsafe_pointer.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 3f8db596af..cc760f1c01 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -53,7 +53,7 @@ struct UnsafePointer[ *, address_space: AddressSpace = AddressSpace.GENERIC, alignment: Int = _default_alignment[type](), - origin: Origin[True].type = MutableAnyOrigin, + origin: Origin[True] = MutableAnyOrigin, ]( ImplicitlyBoolable, CollectionElement, @@ -197,7 +197,7 @@ struct UnsafePointer[ """ # We're unsafe, so we can have unsafe things. - alias _ref_type = Pointer[type, origin, address_space] + alias _ref_type = Pointer[type, origin._mlir_origin, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( UnsafePointer[ @@ -961,7 +961,7 @@ struct UnsafePointer[ /, address_space: AddressSpace = Self.address_space, alignment: Int = Self.alignment, - origin: Origin[True].type = Self.origin, + origin: Origin[True] = Self.origin, ](self) -> UnsafePointer[ T, address_space=address_space, alignment=alignment, origin=origin ]: From 028df13d4c3eaa8bf0339e0a6248ef2b9082b2a4 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 3 Dec 2024 18:13:10 -0600 Subject: [PATCH 1963/2019] [stdlib] feat: Use `Origin[..]` in `Pointer` and iterator type parameter (Part 3/X) Also changes `_lit_mut_cast[]` to take an `Origin` as well. MODULAR_ORIG_COMMIT_REV_ID: 1b9b88f28acc46ed2c02b368892c1cddbdf98b7e --- stdlib/src/builtin/builtin_list.mojo | 6 +++--- stdlib/src/collections/deque.mojo | 2 +- stdlib/src/collections/inline_list.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/arc.mojo | 2 +- stdlib/src/memory/pointer.mojo | 4 ++-- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/src/utils/span.mojo | 10 +++++----- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 80047f0343..fa24d3c55c 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -238,7 +238,7 @@ struct _VariadicListMemIter[ var index: Int var src: Pointer[ Self.variadic_list_type, - list_origin._mlir_origin, + list_origin, ] fn __init__( @@ -283,12 +283,12 @@ struct _lit_origin_union[ struct _lit_mut_cast[ is_mutable: Bool, //, - operand: Origin[is_mutable].type, + operand: Origin[is_mutable], result_mutable: Bool, ]: alias result = __mlir_attr[ `#lit.origin.mutcast<`, - operand, + operand._mlir_origin, `> : !lit.origin<`, +result_mutable.value, `>`, diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index c79efe4a30..6d45725350 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -995,7 +995,7 @@ struct Deque[ElementType: CollectionElement]( struct _DequeIter[ deque_mutability: Bool, //, ElementType: CollectionElement, - deque_lifetime: Origin[deque_mutability].type, + deque_lifetime: Origin[deque_mutability], forward: Bool = True, ]: """Iterator for Deque. diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index 4e2e90349f..f98e307940 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -32,7 +32,7 @@ struct _InlineListIter[ list_mutability: Bool, //, T: CollectionElementNew, capacity: Int, - list_origin: Origin[list_mutability].type, + list_origin: Origin[list_mutability], forward: Bool = True, ]: """Iterator for InlineList. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b470023659..d4791dd79e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -40,7 +40,7 @@ struct _ListIter[ list_mutability: Bool, //, T: CollectionElement, hint_trivial_type: Bool, - list_origin: Origin[list_mutability].type, + list_origin: Origin[list_mutability], forward: Bool = True, ]: """Iterator for List. diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index ecabe6c9d4..d7b236af72 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -146,7 +146,7 @@ struct ArcPointer[T: Movable]( ]( ref [self_life]self, ) -> ref [ - _lit_mut_cast[self_life._mlir_origin, result_mutable=True].result + _lit_mut_cast[self_life, result_mutable=True].result ] T: """Returns a mutable reference to the managed value. diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index db7b78bc18..925707a0ea 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -300,7 +300,7 @@ struct AddressSpace(EqualityComparable, Stringable, Writable): struct Pointer[ is_mutable: Bool, //, type: AnyType, - origin: Origin[is_mutable].type, + origin: Origin[is_mutable], address_space: AddressSpace = AddressSpace.GENERIC, ](CollectionElementNew, Stringable): """Defines a non-nullable safe pointer. @@ -316,7 +316,7 @@ struct Pointer[ `!lit.ref<`, type, `, `, - origin, + origin._mlir_origin, `, `, address_space._value.value, `>`, diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index cc760f1c01..0d12a44a78 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -197,7 +197,7 @@ struct UnsafePointer[ """ # We're unsafe, so we can have unsafe things. - alias _ref_type = Pointer[type, origin._mlir_origin, address_space] + alias _ref_type = Pointer[type, origin, address_space] return __get_litref_as_mvalue( __mlir_op.`lit.ref.from_pointer`[_type = _ref_type._mlir_type]( UnsafePointer[ diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 396744aa4e..38bb86cbe5 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -70,7 +70,7 @@ struct _SpanIter[ @always_inline fn __next__( mut self, - ) -> Pointer[T, origin._mlir_origin]: + ) -> Pointer[T, origin]: @parameter if forward: self.index += 1 @@ -259,7 +259,7 @@ struct Span[ return self._data - fn as_ref(self) -> Pointer[T, origin._mlir_origin]: + fn as_ref(self) -> Pointer[T, origin]: """ Gets a Pointer to the first element of this slice. @@ -267,7 +267,7 @@ struct Span[ A Pointer pointing at the first element of this slice. """ - return Pointer[T, origin._mlir_origin].address_of(self._data[0]) + return Pointer[T, origin].address_of(self._data[0]) @always_inline fn copy_from[ @@ -360,13 +360,13 @@ struct Span[ fn get_immutable( self, - ) -> Span[T, _lit_mut_cast[origin._mlir_origin, False].result]: + ) -> Span[T, _lit_mut_cast[origin, False].result]: """ Return an immutable version of this span. Returns: A span covering the same elements, but without mutability. """ - return Span[T, _lit_mut_cast[origin._mlir_origin, False].result]( + return Span[T, _lit_mut_cast[origin, False].result]( ptr=self._data, length=self._len ) From 12de160d0a793874931d1ee31a1de95d815f0bb9 Mon Sep 17 00:00:00 2001 From: Owen Hilyard Date: Tue, 3 Dec 2024 16:21:57 -0800 Subject: [PATCH 1964/2019] [External] [docs] Stdlib insider documentation (#51673) [External] [docs] Stdlib insider documentation People working on the standard library need to have some more information about the API contracts and behavior of the runtime and compiler builtins in order to be able to write correct and performant code in the stdlib. This is not intended to be high effort documentation or acknowledgement that a feature is stable, but simply communication of enough information for those of us without access to the compiler source and autogenerated mlir documentation to work with them correctly. Co-authored-by: Owen Hilyard Closes modularml/mojo#3793 MODULAR_ORIG_COMMIT_REV_ID: 9f09d235fd6ec0dae76897a38621e779e460fc23 --- proposals/stdlib-insider-docs.md | 136 +++++++++++++++++++++++++++++++ stdlib/docs/internal/README.md | 16 ++++ stdlib/docs/internal/mlir.md | 10 +++ stdlib/docs/internal/runtime.md | 10 +++ 4 files changed, 172 insertions(+) create mode 100644 proposals/stdlib-insider-docs.md create mode 100644 stdlib/docs/internal/README.md create mode 100644 stdlib/docs/internal/mlir.md create mode 100644 stdlib/docs/internal/runtime.md diff --git a/proposals/stdlib-insider-docs.md b/proposals/stdlib-insider-docs.md new file mode 100644 index 0000000000..1bf0abfef2 --- /dev/null +++ b/proposals/stdlib-insider-docs.md @@ -0,0 +1,136 @@ +# Stdlib Insider Docs + +Owen Hilyard, Created November 17, 2024 + +**Status**: Initial Proposal + +## Motivation + +For most languages, people who work on the standard library have the ability +to look inside of the compiler to clarify questions they have about the +semantics of particular operations or compiler builtins. For Mojo, that is +not the case for many people working on the standard library. As a result, +the exact semantics of some important compiler builtins, such as +`lit.ownership.mark_destroyed`, are not fully known to a large number of +people working on the standard library. For example, the fact that +`lit.ownership.mark_destroyed` still runs the destructors of fields was a +surprise to many at the stdlib meeting. This creates issues where Modular +employees have to catch misuses. These language-internal dialects are, like +Mojo itself, subject to enhancements, breaking changes, and even complete +removal. This presents a problem since the stdlib is correctness-critical code, +and when people who don't understand the API contract of a construct use it +in correctness-critical code, issues are bound to happen. + +## Proposal + +In order to help address this, I propose the creation of a "stdlib insider" +document, which contains information on MLIR operations/types, WIP features and +other parts of the language which are either intended to only be used in the +standard library/MAX drivers or language features which are still subject to +change and evolution. This can be substantially less polished than the Mojo +manual, including "X is like Y in C++ but ...", links to academic +papers, links to LLVM docs, pseudocode, and other ways one might explain a +concept to a colleague. This document, likely maintained as a markdown file in +/docs, is intended to be internally facing to core Mojo developers. This means +that a large "everything in here is subject to being totally rewritten in a +bugfix release or security update, do not use outside of the standard library +or MAX" warning, which will hopefully dissuade people from using what is +documented there in ways that will get them stuck on old Mojo versions. + +For MLIR operations, I'd like the following information documented +for each operation used in the standard library; Operation name, +arguments (potentially as Mojo function syntax), a description of the +operation, pre-conditions, post-conditions, and clear hazards. Clear hazards +would ways in which the operation can cause UB (ex: is what happens with a +null pointer well defined?), conditions under which the operation will force +the program to abort or other like "`lit.ownership.mark_destroyed` +still runs the destructors of fields" which may be surprising behavior. + +For MLIR types, information about what a type is intended to do, what +parameters the type has (and their types), the size of the instantiated type +(for alignment), and any non-trivialities in the type (is it ok to copy/move +it?). MLIR attributes should have similar information. + +For features, a short description of what the feature is intended for, and +then syntax examples that show the capabilities of the feature. Ideally, some +differentiation between the design and the current implementation should be +present, slowly moving parts of the documentation over as they are available +on nightly. Documenting known sharp edges or limitations is also helpful, for +instance if trait objects could only represent a single trait (ex: no Movable +\+ Copyable + Formattable trait) or if some part of the implementation has a +high time or space complexity (ex: O(N^2) compile time overhead in the number +of traits in a trait object). + +## Current State + +At present, the majority of MLIR operations are things which I think +are reasonable to explain with a link to LLVM or C++ docs. For example, `pop.max` +is mostly self-explanatory, so unless there are extra semantics I don't +think it really needs more of an explanation than "see C++ std::max". + +What I consider the important things to document: + +### `pop.external_call` + +Lots of people want to call into OpenSSL to get basic HTTPS working, and that +needs to be done correctly. There's also a lot of ABI issues around this, +for instance whether Mojo structs are C layout (for now) or whether we need +a mechanism to force that behavior. + +### `co.*` + +This area is WIP, but some community discussion around the direction would be +helpful. Information about the API of each of the types would also be nice +since it looks like we would need to use MLIR to implement future combinators +like Rust's `FuturesUnordered`. Documentation around synchronization +requirements is also important for correctness as people move towards async io. + +### `#kgen.param.expr LLVM type conversions, +Documenting limitations is also helpful, for instance, can is `@call` ok to +use? Can I directly write phi nodes? I had a lot of difficulty interacting +with anything that returns a `!llvm.struct` or `!llvm.vec`. + +### `pop.inline_asm` + +A lot of CPU functionality doesn't have LLVM intrinsics and we'll need to use +assembly (CPU time stamp counters, poking MSRs for runtime capability +discovery, etc). I personally ran into difficulties doing multiple return +(ex: x86 `cpuid` returns results in 4 registers). Information on the asm +dialect, how to create clobbers and scratch registers, and rules for control +flow (ex: can I implement computed gotos, jump into the middle of a function or +return from the current function?). + +### `!lit.origin.set` + +This is used in Coroutines (presumably to store capture origins), but it looks +like it may be useful for storing collections of references. + +### The backing allocator(s) for `pop.aligned_alloc` and `pop.global_alloc` + +Time spent in GDB has led me to believe this is tcmalloc, but knowing for +sure means we have information like the minimum alignment the allocator will +provide (storing information in the lower bits), what kind of caching it does, +information about how it handles memory overcommit being off (db servers), and +what kind of instrumentation we might have access to. diff --git a/stdlib/docs/internal/README.md b/stdlib/docs/internal/README.md new file mode 100644 index 0000000000..b80fd03dca --- /dev/null +++ b/stdlib/docs/internal/README.md @@ -0,0 +1,16 @@ +## WARNING + +Everything in this file/directory is subject to revision on any bugfix or security +update. We (the stdlib team and contributors), reserve the right to remove, +change the API contracts of, rename, or cause to instantly crash the program, +any operation described in here. These are **PRIVATE** APIs and implementation +details for the Mojo stdlib and for MAX to use. **WE WILL CHANGE IT WHENEVER +WE FIND IT CONVENIENT TO DO SO WITHOUT WARNING OR NOTICE**. + +## Purpose + +This directory contains internal documentation for the implementation details +of the Mojo compiler, runtime, and stdlib. You must always reference the +current version of this documentation on the branch nightly before using the +operations or behavior documented within. Any new files should contain the +above warning. diff --git a/stdlib/docs/internal/mlir.md b/stdlib/docs/internal/mlir.md new file mode 100644 index 0000000000..ca0c174c4b --- /dev/null +++ b/stdlib/docs/internal/mlir.md @@ -0,0 +1,10 @@ +## WARNING + +Everything in this file is subject to revision on any bugfix or security +update. We (the stdlib team and contributors), reserve the right to remove, +change the API contracts of, rename, or cause to instantly crash the program, +any operation described in here. These are **PRIVATE** APIs and implementation +details for the Mojo stdlib and for MAX to use. **WE WILL CHANGE IT WHENEVER +WE FIND IT CONVENIENT TO DO SO WITHOUT WARNING OR NOTICE**. + +## MLIR Documentation diff --git a/stdlib/docs/internal/runtime.md b/stdlib/docs/internal/runtime.md new file mode 100644 index 0000000000..6d4921138b --- /dev/null +++ b/stdlib/docs/internal/runtime.md @@ -0,0 +1,10 @@ +## WARNING + +Everything in this file is subject to revision on any bugfix or security +update. We (the stdlib team and contributors), reserve the right to remove, +change the API contracts of, rename, or cause to instantly crash the program, +any operation described in here. These are **PRIVATE** APIs and implementation +details for the Mojo stdlib and for MAX to use. **WE WILL CHANGE IT WHENEVER +WE FIND IT CONVENIENT TO DO SO WITHOUT WARNING OR NOTICE**. + +## Runtime Documentation From 829f69d2f3e053075bfa906053d902e5323265f0 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 3 Dec 2024 18:48:05 -0600 Subject: [PATCH 1965/2019] [stdlib] feat: Rename `Origin.type` to `Origin._mlir_type` + migrate ~remaining `.type uses (Part 4/X) * Use Origin in dict.mojo and builtin_list.mojo types This does *not* update `VariadicPack` and `VariadicListMem` yet, which will require more improvements to parameter inference. MODULAR_ORIG_COMMIT_REV_ID: c4d20f6842ce361d124ee2e246431af0b3efcd6a --- stdlib/src/builtin/builtin_list.mojo | 18 ++++++++++-------- stdlib/src/builtin/reversed.mojo | 4 ++-- stdlib/src/builtin/type_aliases.mojo | 6 +++--- stdlib/src/collections/dict.mojo | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index fa24d3c55c..9e6b5972ea 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -221,7 +221,7 @@ struct VariadicList[type: AnyTrivialRegType](Sized): struct _VariadicListMemIter[ elt_is_mutable: Bool, //, elt_type: AnyType, - elt_origin: Origin[elt_is_mutable].type, + elt_origin: Origin[elt_is_mutable], list_origin: ImmutableOrigin, ]: """Iterator for VariadicListMem. @@ -233,7 +233,9 @@ struct _VariadicListMemIter[ list_origin: The origin of the VariadicListMem. """ - alias variadic_list_type = VariadicListMem[elt_type, elt_origin] + alias variadic_list_type = VariadicListMem[ + elt_type, elt_origin._mlir_origin + ] var index: Int var src: Pointer[ @@ -267,14 +269,14 @@ struct _VariadicListMemIter[ # TODO: parametric aliases would be nice. struct _lit_origin_union[ is_mutable: Bool, //, - a: Origin[is_mutable].type, - b: Origin[is_mutable].type, + a: Origin[is_mutable], + b: Origin[is_mutable], ]: alias result = __mlir_attr[ `#lit.origin.union<`, - a, + a._mlir_origin, `,`, - b, + b._mlir_origin, `> : !lit.origin<`, is_mutable.value, `>`, @@ -298,7 +300,7 @@ struct _lit_mut_cast[ struct VariadicListMem[ elt_is_mutable: Bool, //, element_type: AnyType, - origin: Origin[elt_is_mutable].type, + origin: Origin[elt_is_mutable]._mlir_type, ](Sized): """A utility class to access variadic function arguments of memory-only types that may have ownership. It exposes references to the elements in a @@ -483,7 +485,7 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @register_passable struct VariadicPack[ elt_is_mutable: Bool, //, - origin: Origin[elt_is_mutable].type, + origin: Origin[elt_is_mutable]._mlir_type, element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 352f6d10b4..a1c984e81f 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -142,7 +142,7 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_origin: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability], ](ref value: _DictValueIter[K, V, dict_origin]) -> _DictValueIter[ K, V, dict_origin, False ]: @@ -169,7 +169,7 @@ fn reversed[ K: KeyElement, V: CollectionElement, dict_mutability: Bool, - dict_origin: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability], ](ref value: _DictEntryIter[K, V, dict_origin]) -> _DictEntryIter[ K, V, dict_origin, False ]: diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 7c7c6ff25b..4139618270 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -51,7 +51,7 @@ struct Origin[is_mutable: Bool]: is_mutable: Whether the origin is mutable. """ - alias type = __mlir_type[ + alias _mlir_type = __mlir_type[ `!lit.origin<`, is_mutable.value, `>`, @@ -61,7 +61,7 @@ struct Origin[is_mutable: Bool]: # Fields # ===-------------------------------------------------------------------===# - var _mlir_origin: Self.type + var _mlir_origin: Self._mlir_type # ===-------------------------------------------------------------------===# # Life cycle methods @@ -73,7 +73,7 @@ struct Origin[is_mutable: Bool]: # Span[Byte, __origin_of(self)] @implicit @always_inline("nodebug") - fn __init__(out self, mlir_origin: Self.type): + fn __init__(out self, mlir_origin: Self._mlir_type): """Initialize an Origin from a raw MLIR `!lit.origin` value. Args: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index d35229a17e..99aa18c45f 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -61,7 +61,7 @@ struct _DictEntryIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_origin: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability], forward: Bool = True, ]: """Iterator over immutable DictEntry references. @@ -120,7 +120,7 @@ struct _DictKeyIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_origin: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability], forward: Bool = True, ]: """Iterator over immutable Dict key references. @@ -158,7 +158,7 @@ struct _DictValueIter[ dict_mutability: Bool, //, K: KeyElement, V: CollectionElement, - dict_origin: Origin[dict_mutability].type, + dict_origin: Origin[dict_mutability], forward: Bool = True, ]: """Iterator over Dict value references. These are mutable if the dict From 271b723d3e3e0d05ce605ab1e3cbd3c06f8b604e Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 3 Dec 2024 15:48:15 -1000 Subject: [PATCH 1966/2019] [stdlib] Align max_exponent with C/Python and clarify terms - Move back to setting the FPUtils.max_exponent() to return the max value disregarding `-inf` and `inf` bitpatterns. The actual max exponent is -1, but this is for consistency with pythons `sys.float_info.max_exp` and C++ constants such as FLT_MAX_EXP. `frexp` returns the real exponent + 1 and normalizes from [1..2] to [0.5..1] so these values align with that function that is shared between C/Python/Mojo, put clarification in the docstring. - Remove FPUtils.min_exponent(), this could mean various things i.e. `min_subnormal_exponent`, `min_normal_exponent`, does it follow `max_exponent` by adding 1 to align with `frexp`? This was only used in _format_float. - Put the correct exponent_bias in for the fnuz types. - Clarify some variable names to say what they actually are. MODULAR_ORIG_COMMIT_REV_ID: 1c72346acc7e5290dc5d76f4d6efa5e2a42faf1a --- stdlib/src/builtin/_format_float.mojo | 11 +++--- stdlib/src/math/math.mojo | 7 +++- stdlib/src/utils/numerics.mojo | 54 +++++++++++---------------- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index b46e9ce306..4c5fc6797e 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -71,9 +71,8 @@ struct FP[type: DType, CarrierDType: DType = FPUtils[type].uint_type]: alias carrier_bits = sizeof[Self.CarrierDType]() * 8 alias sig_bits = FPUtils[type].mantissa_width() alias exp_bits = FPUtils[type].exponent_width() - alias min_exponent = FPUtils[type].min_exponent() - alias max_exponent = FPUtils[type].max_exponent() - alias exp_bias = -Self.max_exponent + alias neg_exp_bias = -FPUtils[type].exponent_bias() + alias min_normal_exp = Self.neg_exp_bias + 1 alias cache_bits = 64 if Self.CarrierDType == DType.uint32 else 128 alias min_k = -31 if Self.CarrierDType == DType.uint32 else -292 alias max_k = 46 if Self.CarrierDType == DType.uint32 else 326 @@ -129,7 +128,7 @@ fn _write_float[W: Writer, type: DType, //](mut writer: W, value: Scalar[type]): # - The significand (sig) is the raw binary fraction # - The exponent (exp) is still in biased form var sig = FPUtils.get_mantissa_uint(casted) - var exp = FPUtils.get_exponent_without_bias(casted) + var exp = FPUtils.get_exponent_biased(casted) var sign = FPUtils.get_sign(casted) if isinf(value): @@ -235,7 +234,7 @@ fn _to_decimal[ # For normal numbers if binary_exp != 0: - binary_exp += FP[type].exp_bias - FP[type].sig_bits + binary_exp += FP[type].neg_exp_bias - FP[type].sig_bits if two_fc == 0: var minus_k = (binary_exp * 631305 - 261663) >> 21 var beta = binary_exp + _floor_log2_pow10(-minus_k) @@ -293,7 +292,7 @@ fn _to_decimal[ two_fc |= Scalar[CarrierDType](1) << (FP[type].sig_bits + 1) else: # For subnormal numbers - binary_exp = FP[type].min_exponent - FP[type].sig_bits + binary_exp = FP[type].min_normal_exp - FP[type].sig_bits ########################################## # Step 1: Schubfach multiplier calculation diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 2c7ee78a5d..7b998c7220 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -645,6 +645,8 @@ fn frexp[ type: DType, simd_width: Int, // ](x: SIMD[type, simd_width]) -> StaticTuple[SIMD[type, simd_width], 2]: """Breaks floating point values into a fractional part and an exponent part. + This follows C and Python in increasing the exponent by 1 and normalizing the + fraction from 0.5 to 1.0 instead of 1.0 to 2.0. Constraints: The input must be a floating-point type. @@ -664,14 +666,15 @@ fn frexp[ constrained[type.is_floating_point(), "must be a floating point value"]() alias T = SIMD[type, simd_width] alias zero = T(0) - alias max_exponent = FPUtils[type].max_exponent() - 1 + # Add one to the resulting exponent up by subtracting 1 from the bias + alias exponent_bias = FPUtils[type].exponent_bias() - 1 alias mantissa_width = FPUtils[type].mantissa_width() var mask1 = _frexp_mask1[simd_width, type]() var mask2 = _frexp_mask2[simd_width, type]() var x_int = x.to_bits() var selector = x != zero var exp = selector.select( - (((mask1 & x_int) >> mantissa_width) - max_exponent).cast[type](), + (((mask1 & x_int) >> mantissa_width) - exponent_bias).cast[type](), zero, ) var frac = selector.select(T(from_bits=x_int & ~mask1 | mask2), zero) diff --git a/stdlib/src/utils/numerics.mojo b/stdlib/src/utils/numerics.mojo index f7d99244fe..e1ec498345 100644 --- a/stdlib/src/utils/numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -84,41 +84,25 @@ struct FPUtils[ @staticmethod @always_inline("nodebug") fn max_exponent() -> IntLiteral: - """Returns the max exponent of a floating point type, taking into - account special reserved cases such infinity and nan. + """Returns the max exponent of a floating point type without accounting + for inf representations. This is not + the maximum representable exponent, which is generally equal to + the exponent_bias. Returns: The max exponent. """ @parameter - if type is DType.float8e4m3: - return 7 - elif type is DType.float8e4m3fnuz: + if type in (DType.float8e4m3, DType.float8e4m3fnuz): return 8 - elif type is DType.float8e5m2: - return 15 - elif type is DType.float8e5m2fnuz: + elif type in (DType.float8e5m2, DType.float8e5m2fnuz, DType.float16): return 16 - elif type is DType.float16: - return 15 - elif type is DType.float32 or type is DType.bfloat16: - return 127 + elif type in (DType.bfloat16, DType.float32): + return 128 else: constrained[type is DType.float64, "unsupported float type"]() - return 1023 - - @staticmethod - @always_inline("nodebug") - fn min_exponent() -> IntLiteral: - """Returns the min exponent of a floating point type, taking into - account special reserved cases such as infinity and nan. - - Returns: - The min exponent. - """ - - return -Self.max_exponent() + 1 + return 1024 @staticmethod @always_inline("nodebug") @@ -132,11 +116,9 @@ struct FPUtils[ @parameter if type in (DType.float8e4m3, DType.float8e4m3fnuz): return 4 - elif type in (DType.float8e5m2, DType.float8e5m2fnuz): + elif type in (DType.float8e5m2, DType.float8e5m2fnuz, DType.float16): return 5 - elif type is DType.float16: - return 5 - elif type is DType.float32 or type is DType.bfloat16: + elif type in (DType.float32, DType.bfloat16): return 8 else: constrained[type is DType.float64, "unsupported float type"]() @@ -160,7 +142,12 @@ struct FPUtils[ Returns: The exponent bias. """ - return Self.max_exponent() + + @parameter + if type in (DType.float8e4m3fnuz, DType.float8e5m2fnuz): + return Self.max_exponent() + else: + return Self.max_exponent() - 1 @staticmethod @always_inline @@ -306,14 +293,15 @@ struct FPUtils[ @staticmethod @always_inline - fn get_exponent_without_bias(value: Scalar[type]) -> Int: - """Returns the exponent bits of the floating-point value. + fn get_exponent_biased(value: Scalar[type]) -> Int: + """Returns the biased exponent of the floating-point value as an Int, + this is how the value is stored before subtracting the exponent bias. Args: value: The floating-point value. Returns: - Returns the exponent bits. + The biased exponent as an Int. """ return int( Self.bitcast_to_uint(value) >> Self.mantissa_width() From 8a5b198bc42cead875ee68abdae71d9cdbe5195a Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 3 Dec 2024 18:39:38 -1000 Subject: [PATCH 1967/2019] [stdlib] Clarify the differences for the float8 types These types have confusing differences, clarify the naming and exactly how they're different in the doc strings. MODULAR_ORIG_COMMIT_REV_ID: 80525ac525b1538664a33ae6d053addc0f95fcac --- stdlib/src/builtin/dtype.mojo | 58 ++++++++++++++++++++++++++++++----- stdlib/src/builtin/simd.mojo | 58 ++++++++++++++++++++++++++++++----- 2 files changed, 100 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 4ac807acd6..1ed5dd09c6 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -66,23 +66,65 @@ struct DType( alias float8e5m2 = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - """Represents a FP8E5M2 floating point format whose bitwidth is 8.""" + """Represents a FP8E5M2 floating point format from the [OFP8 + standard](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1). + + The 8 bits are encoded as `seeeeemm`: + - (s)ign: 1 bit + - (e)xponent: 5 bits + - (m)antissa: 2 bits + - exponent bias: 15 + - nan: {0,1}11111{01,10,11} + - inf: 01111100 + - -inf: 11111100 + - -0: 10000000 + """ alias float8e5m2fnuz = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - """Represents a FP8E5M2FNUZ floating point format for AMD GPU whose bitwdith is 8. - This dtype only supports finite and NaN values. NaN is when sign bit is - set and all other exponent and mantissa bits are 0.""" + """Represents a FP8E5M2FNUZ floating point format. + + The 8 bits are encoded as `seeeeemm`: + - (s)ign: 1 bit + - (e)xponent: 5 bits + - (m)antissa: 2 bits + - exponent bias: 16 + - nan: 10000000 + - fn: finite (no inf or -inf encodings) + - uz: unsigned zero (no -0 encoding) + """ alias float8e4m3 = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - """Represents a FP8E4M3 floating point format whose bitwidth is 8.""" + """Represents a FP8E4M3 floating point format from the [OFP8 + standard](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1). + + This type is named `float8_e4m3fn` (the "fn" stands for "finite") in some + frameworks, as it does not encode -inf or inf. + + The 8 bits are encoded as `seeeemmm`: + - (s)ign: 1 bit + - (e)xponent: 4 bits + - (m)antissa: 3 bits + - exponent bias: 7 + - nan: 01111111, 11111111 + - -0: 10000000 + - fn: finite (no inf or -inf encodings) + """ alias float8e4m3fnuz = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - """Represents a FP8E4M3FNUZ floating point format for AMD GPU whose bitwdith is 8. - This dtype only supports finite and NaN values. NaN is when sign bit is - set and all other exponent and mantissa bits are 0.""" + """Represents a FP8E4M3FNUZ floating point format. + + The 8 bits are encoded as `seeeemmm`: + - (s)ign: 1 bit + - (e)xponent: 4 bits + - (m)antissa: 3 bits + - exponent bias: 8 + - nan: 10000000 + - fn: finite (no inf or -inf encodings) + - uz: unsigned zero (no -0 encoding) + """ alias bfloat16 = DType( __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index bacee6e4bf..350eda13d0 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -94,17 +94,59 @@ alias UInt64 = Scalar[DType.uint64] """Represents a 64-bit unsigned scalar integer.""" alias Float8e5m2 = Scalar[DType.float8e5m2] -"""Represents a FP8E5M2 floating point format whose bitwidth is 8.""" +"""Represents a FP8E5M2 floating point format from the [OFP8 +standard](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1). + +The 8 bits are encoded as `seeeeemm`: +- (s)ign: 1 bit +- (e)xponent: 5 bits +- (m)antissa: 2 bits +- exponent bias: 15 +- nan: {0,1}11111{01,10,11} +- inf: 01111100 +- -inf: 11111100 +- -0: 10000000 +""" alias Float8e5m2fnuz = Scalar[DType.float8e5m2fnuz] -"""Represents a FP8E5M2FNUZ floating point format for AMD GPU whose bitwdith is 8. - This dtype only supports finite and NaN values. NaN is when sign bit is set and - all other exponent and mantissa bits are 0.""" +"""Represents a FP8E5M2FNUZ floating point format. + +The 8 bits are encoded as `seeeeemm`: +- (s)ign: 1 bit +- (e)xponent: 5 bits +- (m)antissa: 2 bits +- exponent bias: 16 +- nan: 10000000 +- fn: finite (no inf or -inf encodings) +- uz: unsigned zero (no -0 encoding) +""" alias Float8e4m3 = Scalar[DType.float8e4m3] -"""Represents a FP8E4M3 floating point format whose bitwidth is 8.""" +"""Represents a FP8E4M3 floating point format from the [OFP8 +standard](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1). + +This type is named `float8_e4m3fn` (the "fn" stands for "finite") in some +frameworks, as it does not encode -inf or inf. + +The 8 bits are encoded as `seeeemmm`: +- (s)ign: 1 bit +- (e)xponent: 4 bits +- (m)antissa: 3 bits +- exponent bias: 7 +- nan: 01111111, 11111111 +- -0: 10000000 +- fn: finite (no inf or -inf encodings) +""" alias Float8e4m3fnuz = Scalar[DType.float8e4m3fnuz] -"""Represents a FP8E4M3FNUZ floating point format for AMD GPU whose bitwdith is 8. - This dtype only supports finite and NaN values. NaN is when sign bit is set and - all other exponent and mantissa bits are 0.""" +"""Represents a FP8E4M3FNUZ floating point format. + +The 8 bits are encoded as `seeeemmm`: +- (s)ign: 1 bit +- (e)xponent: 4 bits +- (m)antissa: 3 bits +- exponent bias: 8 +- nan: 10000000 +- fn: finite (no inf or -inf encodings) +- uz: unsigned zero (no -0 encoding) +""" alias BFloat16 = Scalar[DType.bfloat16] """Represents a 16-bit brain floating point value.""" alias Float16 = Scalar[DType.float16] From d58d34c195a26d737f79450ff249e7a4a0846d43 Mon Sep 17 00:00:00 2001 From: Gustas <37534529+PunkPun@users.noreply.github.com> Date: Tue, 3 Dec 2024 21:00:08 -0800 Subject: [PATCH 1968/2019] [External] [Docs] Update the notebook tutorial (#52029) [External] [Docs] Update the notebook tutorial And add more clarification to end users not familiar with conda .yml files #3803 --------- Co-authored-by: Gustas <37534529+PunkPun@users.noreply.github.com> Co-authored-by: Arthur Evans Closes modularml/mojo#3834 MODULAR_ORIG_COMMIT_REV_ID: db24078163bb662924f5bf0ae744c1c41cd008c0 --- examples/notebooks/README.md | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 4ee3854595..d94bb7bf4b 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -4,9 +4,7 @@ Mojo supports programming in [Jupyter notebooks](https://jupyter.org/), just like Python. This page explains how to get started with Mojo notebooks, and this repo -directory contains notebooks that demonstrate some of Mojo's features -(most of which we originally published on the [Mojo -Playground](https://playground.modular.com/)). +directory contains notebooks that demonstrate some of Mojo's features. If you're not familiar with Jupyter notebooks, they're files that allow you to create documents with live code, equations, visualizations, and explanatory @@ -67,6 +65,7 @@ If you have [`magic`](https://docs.modular.com/magic) you can run the following command to launch JupyterLab from this directory: ```sh +# Run from an active conda or magic shell environment magic run jupyter lab ``` @@ -74,7 +73,20 @@ After a moment, it will open a browser window with JupterLab running. #### Using conda -Create a Conda environment, activate that enviroment, and install JupyterLab. +Conda allows you to export environments in `.yml` format. (For more +information, see +[Managing environments](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html) +in the Conda documentation.) + +If you already have a working Jupyter environment on a different computer and +server you can export it using the following command. + +```sh +# Example of exporting environment.yml from a conda environment named `your-env` +conda env export --name your_env > environment.yml +``` + +To create a Conda environment, activate that environment, and install JupyterLab. ``` sh # Create a Conda environment if you don't have one @@ -87,6 +99,19 @@ conda run -n mojo-repo jupyter lab After a moment, it will open a browser window with JupterLab running. +#### Using more magic + +Magic allows you to create an environment from a Conda `environment.yml`. + +```sh +# Create a magic environment if you don't have one +magic init mojo-repo --import environment.yml +# Activate the environment +cd mojo-repo && magic shell +# run JupyterLab +magic run jupyter lab +``` + ### 2. Run the .ipynb notebooks The left nav bar should show all the notebooks in this directory. From d0f527bff16e2c0104974387f7b838ad3fb320d5 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 4 Dec 2024 10:34:29 -0800 Subject: [PATCH 1969/2019] [Docs] Several small tweaks to parameter docs. - Document **_ syntax for unbinding keyword parameters. - Document that infer-only parameters can be bound by keyword. (Maybe they should be infer-mostly parameters?). - Document automatically parameterized parameter lists. MODULAR_ORIG_COMMIT_REV_ID: 23ab820cebbc7895413a4962acb661849866a6c6 --- docs/manual/parameters/index.mdx | 139 +++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 37 deletions(-) diff --git a/docs/manual/parameters/index.mdx b/docs/manual/parameters/index.mdx index be9066c974..9b3ba3250f 100644 --- a/docs/manual/parameters/index.mdx +++ b/docs/manual/parameters/index.mdx @@ -3,11 +3,24 @@ title: "Parameterization: compile-time metaprogramming" description: An introduction to parameters and compile-time metaprogramming. --- -Many languages have facilities for *metaprogramming*: that is, for writing code that generates or modifies code. Python has facilities for dynamic metaprogramming: features like decorators, metaclasses, and many more. These features make Python very flexible and productive, but since they're dynamic, they come with runtime overhead. Other languages have static or compile-time metaprogramming features, like C preprocessor macros and C++ templates. These can be limiting and hard to use. - -To support Modular's work in AI, Mojo aims to provide powerful, easy-to-use metaprogramming with zero runtime cost. This compile-time metaprogramming uses the same language as runtime programs, so you don't have to learn a new language—just a few new features. - -The main new feature is *parameters*. You can think of a parameter as a compile-time variable that becomes a runtime constant. This usage of "parameter" is probably different from what you're used to from other languages, where "parameter" and "argument" are often used interchangeably. In Mojo, "parameter" and "parameter expression" refer to compile-time values, and "argument" and "expression" refer to runtime values. +Many languages have facilities for *metaprogramming*: that is, for writing code that +generates or modifies code. Python has facilities for dynamic metaprogramming: features +like decorators, metaclasses, and many more. These features make Python very flexible +and productive, but since they're dynamic, they come with runtime overhead. Other +languages have static or compile-time metaprogramming features, like C preprocessor +macros and C++ templates. These can be limiting and hard to use. + +To support Modular's work in AI, Mojo aims to provide powerful, easy-to-use +metaprogramming with zero runtime cost. This compile-time metaprogramming uses the same +language as runtime programs, so you don't have to learn a new language—just a few new +features. + +The main new feature is *parameters*. You can think of a parameter as a +compile-time variable that becomes a runtime constant. This usage of "parameter" +is probably different from what you're used to from other languages, where +"parameter" and "argument" are often used interchangeably. In Mojo, "parameter" +and "parameter expression" refer to compile-time values, and "argument" and +"expression" refer to runtime values. In Mojo, you can add parameters to a struct or function. You can also define named parameter expressions—aliases—that you can use as runtime constants. @@ -104,9 +117,9 @@ repeat[count=2](42) This updated function takes any `Stringable` type, so you can pass it an `Int`, `String`, or `Bool` value. -You can't pass the `count` as a positional keyword without also specifying `MsgType`. -You can put `//` after `MsgType` to specify that it's always inferred by the argument. Now -you can pass the following parameter `count` positionally: +You can't pass the `count` as a positional keyword without also specifying +`MsgType`. You can put `//` after `MsgType` to specify that it's always inferred +by the argument. Now you can pass the following parameter `count` positionally: ```mojo fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType): @@ -173,8 +186,8 @@ use a parameterized struct. In this case, when you create an instance of passing in this case is a *type*. That's OK: a Mojo type is a valid compile-time value.) -You'll see that `ElementType` is used throughout the struct where you'd usually see a -type name. For example, as the formal type for the `elements` in the +You'll see that `ElementType` is used throughout the struct where you'd usually +see a type name. For example, as the formal type for the `elements` in the constructor, and the return type of the `__getitem__()` method. Here's an example of using `GenericArray`: @@ -216,10 +229,10 @@ The method returns an instance of `GenericArray[Float64]`. When creating a generic struct, you might want to define some methods that require extra features. For example, consider a collection like `GenericArray` -that holds instances of `CollectionElement`. The `CollectionElement` trait -only requires that the stored data type be copyable and movable. This -imposes a lot of limitations: you can't implement a `sort()` method because -you can't guarantee that the stored type supports the comparison operators; you can't +that holds instances of `CollectionElement`. The `CollectionElement` trait only +requires that the stored data type be copyable and movable. This imposes a lot +of limitations: you can't implement a `sort()` method because you can't +guarantee that the stored type supports the comparison operators; you can't write a useful `__str__()` or `__repr__()` dunder method because you can't guarantee that the stored type supports conversion to a string. @@ -293,9 +306,9 @@ floating point numbers, so it's not practical to define all of the possible SIMD variations. Mojo's [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type (defined as a struct) -exposes the common SIMD operations through its methods, and makes the SIMD data type -and size values parametric. This allows you to directly map your data to the -SIMD vectors on any hardware. +exposes the common SIMD operations through its methods, and makes the SIMD data +type and size values parametric. This allows you to directly map your data to +the SIMD vectors on any hardware. Here's a cut-down (non-functional) version of Mojo's `SIMD` type definition: @@ -590,8 +603,8 @@ fn use_defaults() raises: ``` Recall that when a parametric function is called, Mojo can infer the parameter values. -That is, it can use the parameter values attached to an argument value (see the `sqrt[]()` -example above). If the parametric function also has a default value defined, +That is, it can use the parameter values attached to an argument value (see the +`sqrt[]()` example above). If the parametric function also has a default value defined, then the inferred parameter type takes precedence. For example, in the following code, we update the parametric `speak[]()` function @@ -661,9 +674,9 @@ you can't leave it out of the parameter list and let Mojo infer it from `value`: dependent_type[Float64(2.2)]() # Error! ``` -Infer-only parameters are a special class of parameters that are **always** -inferred from context. Infer-only parameters are placed at the **beginning** of -the parameter list, set off from other parameters by the `//` sigil: +Infer-only parameters are a special class of parameters that are **always** either +inferred from context or specified by keyword. Infer-only parameters are placed at the +**beginning** of the parameter list, set off from other parameters by the `//` sigil: ```mojo fn example[type: CollectionElement, //, list: List[type]]() @@ -688,8 +701,25 @@ Because infer-only parameters are declared at the beginning of the parameter list, other parameters can depend on them, and the compiler will always attempt to infer the infer-only values from bound parameters or arguments. -If the compiler can't infer the value of an infer-only parameter, compilation -fails. +There are sometimes cases where it's useful to specify an infer-only parameter by +keyword. For example, the [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) +type is parametric on [origin](/mojo/manual/values/lifetimes): + +```mojo +struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]: ... +``` + +Here, the `StringSlice` `is_mutable` parameter is infer-only. The value is usually +inferred when you create an instance of `StringSlice`. Binding the `is_mutable` +parameter by keyword lets you define a new type that's constrained to an +immutable origin: + +```mojo +alias ImmutableStringSlice = StringSlice[is_mutable=False] +``` + +If the compiler can't infer the value of an infer-only parameter, and it's not +specified by keyword, compilation fails. ## Variadic parameters @@ -757,7 +787,8 @@ print('result type:', x.element_type, 'length:', len(x)) result type: float32 length: 4 ``` -Note that the resulting length is the sum of the input vector lengths, and this is expressed with a simple `+` operation. +Note that the resulting length is the sum of the input vector lengths, and this is +expressed with a simple `+` operation. ### Powerful compile-time programming @@ -797,11 +828,11 @@ print("Elements sum:", reduce_add(x)) Elements sum: 10 ``` -This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to create a parametric if condition, which is an `if` statement that -runs at compile-time. It requires that its condition be a valid parameter -expression, and ensures that only the live branch of the `if` statement is -compiled into the program. (This is similar to use of the `@parameter` decorator -with a `for` loop shown earlier.) +This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to +create a parametric if condition, which is an `if` statement that runs at compile-time. +It requires that its condition be a valid parameter expression, and ensures that only +the live branch of the `if` statement is compiled into the program. (This is similar to +use of the `@parameter` decorator with a `for` loop shown earlier.) ## Mojo types are just parameter expressions @@ -960,9 +991,27 @@ MyType["Hello", *_] MyType["Hello", _, _, _] ``` -When a parameter is explicitly unbound with the `_` or `*_` expression, you -**must** specify a value for that parameter to use the type. Any default value -from the original type declaration is ignored. +The `*_` expression specifically matches any parameters that can be specified by +position (positional-only or positional-or-keyword). To unbind keyword-only parameters, +use the double-star-underscore expression, `**_`, which matches any parameters that can +be specified by keyword (positional-or-keyword or keyword-only). + +```mojo +@value +struct KeyWordStruct[pos_or_kw: Int, *, kw_only: Int = 10]: + pass + +# Unbind both pos_or_kw and kw_only parameters +fn use_kw_struct(k: KeyWordStruct[**_]): + pass + +def main(): + use_kw_struct(KeyWordStruct[10, kw_only=11]()) +``` + +When a parameter is explicitly unbound with the `_`, `*_`, or `**_` expressions, you +**must** specify a value for that parameter to use the type. Any default value from the +original type declaration is ignored. Partially-bound and unbound parametric types can be used in some contexts where the missing (unbound) parameters will be supplied later—such as in @@ -1025,11 +1074,11 @@ parameterized. The `vec` argument takes an argument of type `SIMD[*_]`. This is an [unbound parameterized type](#fully-bound-partially-bound-and-unbound-types)—that is, it doesn't specify any parameter values for the type. Mojo treats the unbound parameters -on `vec` as implicit parameters on the function. This is roughly equivalent to -the following code, which includes *explicit* input parameters: +on `vec` as infer-only parameters on the function. This is roughly equivalent to +the following codes: ```mojo -fn print_params[t: DType, s: Int](vec: SIMD[t, s]): +fn print_params[t: DType, s: Int, //](vec: SIMD[t, s]): print(vec.type) print(vec.size) ``` @@ -1084,7 +1133,23 @@ print(c) [1, 0, 2, 0, 3, 0, 4, 0] ``` -As shown in the example, you can use the magic `__type_of(x)` call if you just want to match the type of an argument. In this case, it's more convenient and compact that writing the equivalent `SIMD[v1.type, v1.size]`. +As shown in the example, you can use the magic `__type_of(x)` call if you just want to +match the type of an argument. In this case, it's more convenient and compact that +writing the equivalent `SIMD[v1.type, v1.size]`. + +### Automatic parameterization of parameters + +You can also take advantage of automatic parameterization in a function's +parameter list. For example: + +```mojo +fn foo[value: SIMD](): + pass + +# Equivalent to: +fn foo[type: DType, size: Int, //, value: SIMD[type, size]](): + pass +``` ### Automatic parameterization with partially-bound types From b93e70a4b137e2c1ee5361947a9fceee330af6ee Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 4 Dec 2024 13:05:03 -0600 Subject: [PATCH 1970/2019] [stdlib] polish: Expose `_lit_mut_cast` as `Origin.cast_from` alias This obviates the need to import the obscure `_lit_mut_cast` parametric alias hack, and instead more closely associates this operation as a "method" on Origin. MODULAR_ORIG_COMMIT_REV_ID: 6d07f58832f984f3ef68bc1f0e19f6877ca3e91a --- stdlib/src/builtin/builtin_list.mojo | 16 +--------------- stdlib/src/builtin/type_aliases.mojo | 26 ++++++++++++++++++++++++++ stdlib/src/memory/arc.mojo | 3 +-- stdlib/src/utils/span.mojo | 5 ++--- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 9e6b5972ea..d736c55aa6 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -283,20 +283,6 @@ struct _lit_origin_union[ ] -struct _lit_mut_cast[ - is_mutable: Bool, //, - operand: Origin[is_mutable], - result_mutable: Bool, -]: - alias result = __mlir_attr[ - `#lit.origin.mutcast<`, - operand._mlir_origin, - `> : !lit.origin<`, - +result_mutable.value, - `>`, - ] - - struct VariadicListMem[ elt_is_mutable: Bool, //, element_type: AnyType, @@ -443,7 +429,7 @@ struct VariadicListMem[ # cast mutability of self to match the mutability of the element, # since that is what we want to use in the ultimate reference and # the union overall doesn't matter. - _lit_mut_cast[__origin_of(self), elt_is_mutable].result, + Origin[elt_is_mutable].cast_from[__origin_of(self)].result, ].result ] element_type: """Gets a single element on the variadic list. diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 4139618270..b3358188b5 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -57,6 +57,18 @@ struct Origin[is_mutable: Bool]: `>`, ] + alias cast_from = _lit_mut_cast[result_mutable=is_mutable] + """Cast an existing Origin to be of the specified mutability. + + This is a low-level way to coerce Origin mutability. This should be used + rarely, typically when building low-level fundamental abstractions. Strongly + consider alternatives before reaching for this "escape hatch". + + Safety: + This is an UNSAFE operation if used to cast an immutable origin to + a mutable origin. + """ + # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# @@ -79,3 +91,17 @@ struct Origin[is_mutable: Bool]: Args: mlir_origin: The raw MLIR origin value.""" self._mlir_origin = mlir_origin + + +struct _lit_mut_cast[ + is_mutable: Bool, //, + result_mutable: Bool, + operand: Origin[is_mutable], +]: + alias result = __mlir_attr[ + `#lit.origin.mutcast<`, + operand._mlir_origin, + `> : !lit.origin<`, + result_mutable.value, + `>`, + ] diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index d7b236af72..feb4c42711 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -46,7 +46,6 @@ print(ArcPointer(String("ok"))[]) from os.atomic import Atomic -from builtin.builtin_list import _lit_mut_cast from memory import UnsafePointer, stack_allocation @@ -146,7 +145,7 @@ struct ArcPointer[T: Movable]( ]( ref [self_life]self, ) -> ref [ - _lit_mut_cast[self_life, result_mutable=True].result + MutableOrigin.cast_from[self_life].result ] T: """Returns a mutable reference to the managed value. diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 38bb86cbe5..282a3bc229 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -22,7 +22,6 @@ from utils import Span from collections import InlineArray -from builtin.builtin_list import _lit_mut_cast from memory import Pointer, UnsafePointer @@ -360,13 +359,13 @@ struct Span[ fn get_immutable( self, - ) -> Span[T, _lit_mut_cast[origin, False].result]: + ) -> Span[T, ImmutableOrigin.cast_from[origin].result]: """ Return an immutable version of this span. Returns: A span covering the same elements, but without mutability. """ - return Span[T, _lit_mut_cast[origin, False].result]( + return Span[T, ImmutableOrigin.cast_from[origin].result]( ptr=self._data, length=self._len ) From ed492e4d34d9b338e9a7cdf14a1c6ea35e138a24 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 4 Dec 2024 11:19:04 -0800 Subject: [PATCH 1971/2019] [Docs] Add Intro to pointers doc. Also includes some improvements to the existing API docs. MODULAR_ORIG_COMMIT_REV_ID: de875f1b71f56ec072111cde4936a45b0ed2968f --- docs/changelog-released.md | 6 +- .../images/owned-pointer-diagram-dark.png | Bin 0 -> 26898 bytes docs/manual/images/owned-pointer-diagram.png | Bin 0 -> 26431 bytes docs/manual/index.md | 3 +- docs/manual/pointers/index.mdx | 272 ++++++++++++++++++ .../unsafe-pointers.mdx} | 86 ++---- examples/notebooks/environment.yml | 14 + stdlib/src/memory/arc.mojo | 78 ++--- stdlib/src/memory/owned_pointer.mojo | 3 + stdlib/src/memory/pointer.mojo | 3 + stdlib/src/memory/unsafe_pointer.mojo | 18 +- 11 files changed, 383 insertions(+), 100 deletions(-) create mode 100644 docs/manual/images/owned-pointer-diagram-dark.png create mode 100644 docs/manual/images/owned-pointer-diagram.png create mode 100644 docs/manual/pointers/index.mdx rename docs/manual/{pointers.mdx => pointers/unsafe-pointers.mdx} (85%) create mode 100644 examples/notebooks/environment.yml diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 5b3c84bdcf..79b64934eb 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -60,7 +60,7 @@ detailed information in the following sections: [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. Functions that previously took a `DTypePointer` now take an equivalent `UnsafePointer`. For more information on using pointers, see [Unsafe - pointers](/mojo/manual/pointers) in the Mojo Manual. + pointers](/mojo/manual/pointers/unsafe-pointers) in the Mojo Manual. - There are many new standard library APIs, with new features for strings, collections, and interacting with the filesystem and environment. Changes are @@ -518,7 +518,7 @@ detailed information in the following sections: - `DTypePointer`, `LegacyPointer`, and `Pointer` have been removed. Use [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) instead. For more information on using pointers, see [Unsafe - pointers](/mojo/manual/pointers) in the Mojo Manual. + pointers](/mojo/manual/pointers/unsafe-pointers) in the Mojo Manual. Functions that previously took a `DTypePointer` now take an equivalent `UnsafePointer`. A quick rule for conversion from `DTypePointer` to @@ -1013,7 +1013,7 @@ Big themes for this release: - New Mojo Manual pages on [Control flow](/mojo/manual/control-flow), [Testing](/mojo/tools/testing) and using - [unsafe pointers](/mojo/manual/pointers). + [unsafe pointers](/mojo/manual/pointers/unsafe-pointers). ### Language changes diff --git a/docs/manual/images/owned-pointer-diagram-dark.png b/docs/manual/images/owned-pointer-diagram-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..db8049790a34506ee083088c53719e484b639666 GIT binary patch literal 26898 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 Wr`&WenkzuSpUW2wv`RH?BmNgQU0g^2 literal 0 HcmV?d00001 diff --git a/docs/manual/images/owned-pointer-diagram.png b/docs/manual/images/owned-pointer-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ef82f349bf314b1c630b2337a85d37ed84db301e GIT binary patch literal 26431 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

    o>o;{?T)kl46hzWf+B2Va#1#Xj1zQKNHH3^ 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 literal 0 HcmV?d00001 diff --git a/docs/manual/index.md b/docs/manual/index.md index ba52bcd6c4..7d152a1b2b 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -56,7 +56,8 @@ feedback](https://www.modular.com/community). - **Pointers** - - [Unsafe pointers](/mojo/manual/pointers) + - [Intro to pointers](/mojo/manual/pointers/) + - [Unsafe pointers](/mojo/manual/pointers/unsafe-pointers) - **Python** diff --git a/docs/manual/pointers/index.mdx b/docs/manual/pointers/index.mdx new file mode 100644 index 0000000000..075fa33ea5 --- /dev/null +++ b/docs/manual/pointers/index.mdx @@ -0,0 +1,272 @@ +--- +title: Intro to pointers +sidebar_position: 1 +description: An overview of accessing memory using Mojo's pointer types. +--- + +A pointer is an indirect reference to one or more values stored in memory. The +pointer is a value that holds an address to memory, and provides APIs to store +and retrieve values to that memory. The value pointed to by a pointer is also +known as a _pointee_. + +The Mojo standard library includes several types of pointers, which provide +different sets of features. All of these pointer types are _generic_—they can +point to any type of value, and the value type is specified as a parameter. For +example, the following code creates an `OwnedPointer` that points to an `Int` +value: + +```mojo +var ptr: OwnedPointer[Int] +ptr = OwnedPointer(100) +``` + +The `ptr` variable has a value of type `OwnedPointer[Int]`. The pointer *points +to* a value of type `Int`, as shown in Figure 1. + + +

    + +![](../images/owned-pointer-diagram.png#light) +![](../images/owned-pointer-diagram-dark.png#dark) + +
    Figure 1. Pointer and pointee
    +
    + +Accessing the memory—to retrieve or update a value—is called +_dereferencing_ the pointer. You can dereference a pointer by following the +variable name with an empty pair of square brackets: + +```mojo +# Update an initialized value +ptr[] += 10 +# Access an initialized value +print(ptr[]) +``` + +## Pointer terminology + +Before we jump into the pointer types, here are a few terms you’ll run across. Some +of them may already be familiar to you. + +- **Safe pointers**: are designed to prevent memory errors. Unless you use one + of the APIs that are specially designated as unsafe, you can use these + pointers without worrying about memory issues like double-free or + use-after-free. + +- **Nullable pointers**: can point to an invalid memory location (typically 0, +or a “null pointer”). Safe pointers aren’t nullable. + +- **Smart pointers**: own their pointees, which means that the value they point + to may be deallocated when the pointer itself is destroyed. Non-owning + pointers may point to values owned elsewhere, or may require some manual + management of the value lifecycle. + +- **Memory allocation**: some pointer types can allocate memory to store their + pointees, while other pointers can only point to pre-existing values. Memory + allocation can either be implicit (that is, performed automatically when + initializing a pointer with a value) or explicit. + +- **Uninitialized memory**: refers to memory locations that haven’t been + initialized with a value, which may therefore contain random data. + Newly-allocated memory is uninitialized. The safe pointer types don’t allow + users to access memory that’s uninitialized. Unsafe pointers can allocate a + block of uninitialized memory locations and then initialize them one at a time. + Being able to access uninitialized memory is unsafe by definition. + +- **Copyable types**: can be copied implicitly (for example, by assigning a + value to a variable). Also called *implicitly copyable types*. + + ```mojo + copied_ptr = ptr + ``` + + *Explicitly copyable* types require the user to request a copy, using a + constructor with a keyword argument: + + ```mojo + copied_owned_ptr = OwnedPointer(other=owned_ptr) + ``` + +## Pointer types + +The Mojo standard library includes several pointer types with different +characteristics: + +- [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) is a safe pointer that points + to a single value that it doesn’t own. + +- [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) is a smart + pointer that points to a single value, and maintains exclusive ownership of + that value. + +- [`ArcPointer`](/mojo/stdlib/memory/arc/ArcPointer) is a reference-counted + smart pointer that points to an owned value with ownership potentially shared + with other instances of `ArcPointer`. + +- [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) points to + one or more consecutive memory locations, and can refer to uninitialized + memory. + +Table 1 summarizes the different types of pointers: + +
    + +| | `Pointer` | `OwnedPointer` | `ArcPointer` | `UnsafePointer` | +| --- | --- | --- | --- | --- | +| Safe | Yes | Yes | Yes | No | +| Allocates memory | No | Implicitly 1 | Implicitly 1 | Explicitly | +| Owns pointee(s) | No | Yes | Yes | No 2 | +| Copyable | Yes | No 3 | Yes | Yes | +| Nullable | No | No | No | Yes | +| Can point to uninitialized memory | No | No | No | Yes | +| Can point to multiple values (array-like access) | No | No | No | Yes | + +
    Table 1. Pointer types
    +
    + +1 `OwnedPointer` and `ArcPointer` implicitly allocate memory when you +initialize the pointer with a value. + +2 `UnsafePointer` provides unsafe methods for initializing and +destroying instances of the stored type. The user is responsible for managing +the lifecycle of stored values. + +3 `OwnedPointer` is explicitly copyable, but explicitly copying an +`OwnedPointer` copies the *stored value* into a new `OwnedPointer`. + +The following sections provide more details on each pointer type. + +## `Pointer` + +The [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type is a safe pointer that +points to a initialized value that it doesn’t own. Some example use cases for a +`Pointer` include: + +- Storing a reference to a related type. For example, a list’s iterator object +might hold a `Pointer` back to the original list. + +- Passing the memory location for a single value to external code via +`external_call()`. + +- Where you need an API to return a long-lived reference to a value. (Currently +the iterators for standard library collection types like `List` return +pointers.) + +You can construct a `Pointer` using the `address_of()` static method: + +```python +ptr = Pointer.address_of(some_value) +``` + +You can also create a `Pointer` by copying an existing `Pointer`. + +A `Pointer` carries an [`origin`](/mojo/manual/values/lifetimes) for the stored +value, so Mojo can track the lifetime of the referenced value. + +## `OwnedPointer` + +The [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) type is a +smart pointer designed for cases where there is single ownership of the +underlying data. An `OwnedPointer` points to a single item, which is passed in +when you initialize the `OwnedPointer`. The `OwnedPointer` allocates memory and +moves or copies the value into the reserved memory. + +```python +o_ptr = OwnedPointer(some_big_struct) +``` + +An owned pointer can hold almost any type of item, but the stored item must be +either `Movable`, `Copyable`, or `ExplicitlyCopyable`. + +Since an `OwnedPointer` is designed to enforce single ownership, the pointer +itself can be moved, but not copied. + +Note: Currently, you can’t create an `Optional[OwnedPointer[T]]` because the +`Optional` type only works with types that are both movable and copyable. This +restricts some use cases that would otherwise be a natural fit +for`OwnedPointer`, including self-referential data structures like linked lists +and trees. (Until this use case is supported for `OwnedPointer`, it’s possible +to use`ArcPointer` where you need a smart pointer that can be `Optional`.) + +## `ArcPointer` + +An [`ArcPointer`](/mojo/stdlib/memory/arc/ArcPointer) is a reference-counted +smart pointer, ideal for shared resources where the last owner for a given value +may not be clear. Like an `OwnedPointer`, it points to a single value, and it +allocates memory when you initialize the `ArcPointer` with a value: + +```python +attributesDict = Dict[String, String]() +attributes = ArcPointer(attributesDict) +``` + +Unlike an `OwnedPointer`, an `ArcPointer` can be freely copied. All instances +of a given `ArcPointer` share a reference count, which is incremented whenever +the `ArcPointer` is copied and decremented whenever an instance is destroyed. +When the reference count reaches zero, the stored value is destroyed and the +allocated memory is freed. + +You can use `ArcPointer` to implement safe reference-semantic types. For +example, in the following code snippet `SharedDict` uses an `ArcPointer` to +store a dictionary. Copying an instance of `SharedDict` only copies the +`ArcPointer`, not the dictionary, which is shared between all of the copies. + +```python +from memory import ArcPointer +from collections import Dict + +struct SharedDict: + var attributes: ArcPointer[Dict[String, String]] + + fn __init__(out self): + attributesDict = Dict[String, String]() + self.attributes = ArcPointer(attributesDict) + + fn __copyinit__(out self, other: Self): + self.attributes = other.attributes + + def __setitem__(inout self, key: String, value: String): + self.attributes[][key] = value + + def __getitem__(self, key: String) -> String: + return self.attributes[].get(key, default="") + +def main(): + thing1 = SharedDict() + thing2 = thing1 + thing1["Flip"] = "Flop" + print(thing2["Flip"]) +``` + +Note: The reference count is stored using an +[`Atomic`]([/mojo/stdlib/os/atomic/Atomic](https://docs.modular-staging.com/mojo/stdlib/os/atomic/Atomic)) +value to ensure that updates to the reference count are thread-safe. However, +Mojo doesn’t currently enforce exclusive access across thread boundaries, so +it’s possible to form race conditions. + +## UnsafePointer + +[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) is a +low-level pointer that can access a block of contiguous memory locations, which +might be uninitialized. It’s analogous to a raw pointer in the C and C++ +programming languages. `UnsafePointer` provides unsafe methods for initializing +and destroying stored values, as well as for accessing the values once they’re +initialized. + +As the name suggests, `UnsafePointer` doesn’t provide any memory safety +guarantees, so you should reserve it for cases when none of the other pointer +types will do the job. Here are some use cases where you might want to use an +`UnsafePointer`: + +- Building a high-performance array-like structure, such as `List` or `Tensor`. + A single `UnsafePointer` can access many values, and gives you a lot of + control over how you allocate, use, and deallocate memory. Being able to + access uninitialized memory means that you can preallocate a block of memory, + and initialize values incrementally as they are added to the collection. + +- Interacting with external libraries including C++ and Python. You can + use`UnsafePointer` to pass a buffer full of data to or from an external + library. + +For more information, see [Unsafe +pointers](/mojo/manual/pointers/unsafe-pointers). diff --git a/docs/manual/pointers.mdx b/docs/manual/pointers/unsafe-pointers.mdx similarity index 85% rename from docs/manual/pointers.mdx rename to docs/manual/pointers/unsafe-pointers.mdx index 57482a3781..a2a5f04acd 100644 --- a/docs/manual/pointers.mdx +++ b/docs/manual/pointers/unsafe-pointers.mdx @@ -3,26 +3,24 @@ title: Unsafe pointers description: Using unsafe pointers to access dynamically-allocated memory. --- -The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type -creates an indirect reference to a location in memory. +The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type is +one of several pointer types available in the standard library to indirectly +reference locations in memory. + You can use an `UnsafePointer` to dynamically allocate and free memory, or to point to memory allocated by some other piece of code. You can use these pointers to write code that interacts with low-level interfaces, to interface -with other programming languages, or to build certain kinds of data structures. +with other programming languages, or to build array-like data structures. But as the name suggests, they're inherently *unsafe*. For example, when using unsafe pointers, you're responsible for ensuring that memory gets allocated and freed correctly. -:::note - -In addition to unsafe pointers, Mojo supports a safe -[`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type. See -[`UnsafePointer` and `Pointer`](#unsafepointer-and-pointer) for a brief -comparison of the types. +In general, you should prefer safe pointer types when possible, reserving +`UnsafePointer` for those use cases where no other pointer type works. +For a comparison of standard library pointer types, see [Intro to +pointers](/mojo/manual/pointers/). -::: - -## What is a pointer? +## Unsafe pointer basics An `UnsafePointer` is a type that holds an address to memory. You can store and retrieve values in that memory. The `UnsafePointer` type is *generic*—it can @@ -40,8 +38,8 @@ ptr.init_pointee_copy(100)
    -![](./images/pointer-diagram.png#light) -![](./images/pointer-diagram-dark.png#dark) +![](../images/pointer-diagram.png#light) +![](../images/pointer-diagram-dark.png#dark)
    Figure 1. Pointer and pointee
    @@ -69,20 +67,20 @@ structures. For details, see At any given time, a pointer can be in one of several states: -* Uninitialized. Just like any variable, a variable of type `UnsafePointer` can +- Uninitialized. Just like any variable, a variable of type `UnsafePointer` can be declared but uninitialized. ```mojo var ptr: UnsafePointer[Int] ``` -* Null. A null pointer has an address of 0, indicating an invalid pointer. +- Null. A null pointer has an address of 0, indicating an invalid pointer. ```mojo ptr = UnsafePointer[Int]() ``` -* Pointing to allocated, uninitialized memory. The `alloc()` static method +- Pointing to allocated, uninitialized memory. The `alloc()` static method returns a pointer to a newly-allocated block of memory with space for the specified number of elements of the pointee's type. @@ -93,7 +91,7 @@ At any given time, a pointer can be in one of several states: Trying to dereference a pointer to uninitialized memory results in undefined behavior. -* Pointing to initialized memory. You can initialize an allocated, uninitialized +- Pointing to initialized memory. You can initialize an allocated, uninitialized pointer by moving or copying an existing value into the memory. Or you can use the `address_of()` static method to get a pointer to an existing value. @@ -113,7 +111,7 @@ At any given time, a pointer can be in one of several states: ptr[] = newValue ``` -* Dangling. When you free the pointer's allocated memory, you're left with a +- Dangling. When you free the pointer's allocated memory, you're left with a *dangling pointer*. The address still points to its previous location, but the memory is no longer allocated to this pointer. Trying to dereference the pointer, or calling any method that would access the memory location results @@ -127,8 +125,8 @@ The following diagram shows the lifecycle of an `UnsafePointer`:
    -![](./images/pointer-lifecycle.png#light) -![](./images/pointer-lifecycle-dark.png#dark) +![](../images/pointer-lifecycle.png#light) +![](../images/pointer-lifecycle-dark.png#dark)
    Figure 2. Lifecycle of an UnsafePointer
    @@ -293,8 +291,8 @@ ptr += 1
    -![](./images/pointer-offset.png#light) -![](./images/pointer-offset-dark.png#dark) +![](../images/pointer-offset.png#light) +![](../images/pointer-offset-dark.png#dark)
    Figure 3. Pointer arithmetic
    @@ -353,7 +351,7 @@ from memory import UnsafePointer def share_array(): np = Python.import_module("numpy") arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) - ptr = arr.__array_interface__["data"][0].unsafe_get_as_pointer[DType.int64]() + ptr = arr.ctypes.data.unsafe_get_as_pointer[DType.int64]() for i in range(9): print(ptr[i], end=", ") @@ -364,11 +362,10 @@ share_array() 1, 2, 3, 4, 5, 6, 7, 8, 9, ``` -NumPy arrays implement the -[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html), -which defines the `__array_interface__` object used in the example, where -`__array_interface__["data"][0]` is a Python integer holding the address of the -underlying data. The `unsafe_get_as_pointer()` method constructs an +This example uses the NumPy +[`ndarray.ctype`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ctypes.html#numpy.ndarray.ctypes) +attribute to access the raw pointer to the underlying storage +(`ndarray.ctype.data`). The `unsafe_get_as_pointer()` method constructs an `UnsafePointer` to this address. ### Working with C/C++ pointers @@ -459,8 +456,8 @@ you want to access just the red values, you can use a strided load or store.
    -![](./images/strided-load-storage.png#light) -![](./images/strided-load-storage-dark.png#dark) +![](../images/strided-load-storage.png#light) +![](../images/strided-load-storage-dark.png#dark)
    Figure 4. Strided load
    @@ -496,35 +493,14 @@ from (pointer address) + offset[n]. Unsafe pointers are unsafe for several reasons: -* Memory management is up to the user. You need to manually allocate +- Memory management is up to the user. You need to manually allocate and free memory, and be aware of when other APIs are allocating or freeing memory for you. -* `UnsafePointer` values are *nullable*—that is, the pointer +- `UnsafePointer` values are *nullable*—that is, the pointer is not guaranteed to point to anything. And even when a pointer points to allocated memory, that memory may not be *initialized*. -* Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`. +- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`. When you use an `UnsafePointer`, managing memory and knowing when to destroy objects is your responsibility. - -## `UnsafePointer` and `Pointer` - -The [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type is essentially a -safe pointer type. Like a pointer, you can derference a `Pointer` using the -dereference operator, `[]`. However, the `Pointer` type has several -differences from `UnsafePointer` which make it safer: - -* A `Pointer` is *non-nullable*: it always points to something. -* You can't allocate or free memory using a `Pointer`—only point to an - existing value. -* A `Pointer` only refers to a single value. You can't do pointer arithmetic - with a `Pointer`. -* A `Pointer` has an associated *origin*, which connects it back to an - original, owned value. The origin ensures that the value won't be destroyed - while the pointer exists. - -The `Pointer` type shouldn't be confused with the immutable and mutable -references used with the `read` and `mut` argument conventions. Those -references do not require explicit dereferencing, unlike a `Pointer` or -`UnsafePointer`. diff --git a/examples/notebooks/environment.yml b/examples/notebooks/environment.yml new file mode 100644 index 0000000000..45a48799ef --- /dev/null +++ b/examples/notebooks/environment.yml @@ -0,0 +1,14 @@ +name: mojo-notebooks +channels: + - pytorch + - https://conda.modular.com/max/ + - conda-forge + - defaults +dependencies: + - python>=3.11,<3.12 + - max>=24.4.0dev6 + - max = "*" +pip = ">=24.0,<25" +jupyterlab = ">=4.2.5,<5" +matplotlib = ">=3.9.2,<4" +numpy = ">=1.26.4,<2" diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index feb4c42711..8680d06560 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -10,38 +10,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Pointer-counted smart pointers. +"""Reference-counted smart pointers. -Example usage: +You can import these APIs from the `memory` package. For example: ```mojo from memory import ArcPointer -var p = ArcPointer(4) -var p2 = p -p2[]=3 -print(3 == p[]) ``` - -Subscripting(`[]`) is done by `Pointer`, -in order to ensure that the underlying `ArcPointer` outlive the operation. - -It is highly DISCOURAGED to manipulate an `ArcPointer` through `UnsafePointer`. -Mojo's ASAP deletion policy ensure values are destroyed at last use. -Do not unsafely dereference the `ArcPointer` inner `UnsafePointer` field. -See [Lifecycle](https://docs.modular.com/mojo/manual/lifecycle/). - -```mojo -# Illustration of what NOT to do, in order to understand: -print(ArcPointer(String("ok"))._inner[].payload) -#........................^ASAP ^already freed -``` - -Always use `Pointer` subscripting (`[]`): - -```mojo -print(ArcPointer(String("ok"))[]) -``` - """ from os.atomic import Atomic @@ -79,9 +54,34 @@ struct ArcPointer[T: Movable]( This pointer is copyable, including across threads, maintaining a reference count to the underlying data. + When you initialize an `ArcPointer` with a value, it allocates memory and + moves the value into the allocated memory. Copying an instance of an + `ArcPointer` increments the reference count. Destroying an instance + decrements the reference count. When the reference count reaches zero, + `ArcPointer` destroys the value and frees its memory. + This pointer itself is thread-safe using atomic accesses to reference count the underlying data, but references returned to the underlying data are not - thread safe. + thread-safe. + + Subscripting an `ArcPointer` (`ptr[]`) returns a mutable reference to the + stored value. This is the only safe way to access the stored value. Other + methods, such as using the `unsafe_ptr()` method to retrieve an unsafe + pointer to the stored value, or accessing the private fields of an + `ArcPointer`, are unsafe and may result in memory errors. + + For a comparison with other pointer types, see [Intro to + pointers](/mojo/manual/pointers/) in the Mojo Manual. + + Examples: + + ```mojo + from memory import ArcPointer + var p = ArcPointer(4) + var p2 = p + p2[]=3 + print(3 == p[]) + ``` Parameters: T: The type of the stored value. @@ -126,9 +126,9 @@ struct ArcPointer[T: Movable]( @no_inline fn __del__(owned self): - """Delete the smart pointer reference. + """Delete the smart pointer. - Decrement the ref count for the reference. If there are no more + Decrement the reference count for the stored value. If there are no more references, delete the object and free its memory.""" if self._inner[].drop_ref(): # Call inner destructor, then free the memory. @@ -161,7 +161,7 @@ struct ArcPointer[T: Movable]( """Retrieves a pointer to the underlying memory. Returns: - The UnsafePointer to the underlying memory. + The `UnsafePointer` to the pointee. """ # TODO: consider removing this method. return UnsafePointer.address_of(self._inner[].payload) @@ -175,23 +175,27 @@ struct ArcPointer[T: Movable]( return self._inner[].refcount.load() fn __is__(self, rhs: Self) -> Bool: - """Returns True if the two ArcPointers point at the same object. + """Returns True if the two `ArcPointer` instances point at the same + object. Args: - rhs: The other ArcPointer. + rhs: The other `ArcPointer`. Returns: - True if the two ArcPointers point at the same object and False otherwise. + True if the two `ArcPointers` instances point at the same object and + False otherwise. """ return self._inner == rhs._inner fn __isnot__(self, rhs: Self) -> Bool: - """Returns True if the two ArcPointers point at different objects. + """Returns True if the two `ArcPointer` instances point at different + objects. Args: - rhs: The other ArcPointer. + rhs: The other `ArcPointer`. Returns: - True if the two ArcPointers point at different objects and False otherwise. + True if the two `ArcPointer` instances point at different objects + and False otherwise. """ return self._inner != rhs._inner diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 3974daa99f..5d1ddb367d 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -21,6 +21,9 @@ struct OwnedPointer[T: AnyType]: system such that no more than one mutable alias for the underlying data may exist. + For a comparison with other pointer types, see [Intro to + pointers](/mojo/manual/pointers/) in the Mojo Manual. + Parameters: T: The type to be stored in the OwnedPointer[]. """ diff --git a/stdlib/src/memory/pointer.mojo b/stdlib/src/memory/pointer.mojo index 925707a0ea..16a4eadd29 100644 --- a/stdlib/src/memory/pointer.mojo +++ b/stdlib/src/memory/pointer.mojo @@ -305,6 +305,9 @@ struct Pointer[ ](CollectionElementNew, Stringable): """Defines a non-nullable safe pointer. + For a comparison with other pointer types, see [Intro to + pointers](/mojo/manual/pointers/) in the Mojo Manual. + Parameters: is_mutable: Whether the pointee data may be mutated through this. type: Type of the underlying data. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 0d12a44a78..f4971c68af 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -63,7 +63,17 @@ struct UnsafePointer[ Intable, Comparable, ): - """This is a pointer type that can point to any generic value that is movable. + """UnsafePointer[T] represents an indirect reference to one or more values of + type T consecutively in memory, and can refer to uninitialized memory. + + Because it supports referring to uninitialized memory, it provides unsafe + methods for initializing and destroying instances of T, as well as methods + for accessing the values once they are initialized. + + For more information see [Unsafe + pointers](/mojo/manual/pointers/unsafe-pointers) in the Mojo Manual. For a + comparison with other pointer types, see [Intro to + pointers](/mojo/manual/pointers/). Parameters: type: The type the pointer points to. @@ -84,12 +94,12 @@ struct UnsafePointer[ address_space._value.value, `>`, ] + """The underlying pointer type.""" # ===-------------------------------------------------------------------===# # Fields # ===-------------------------------------------------------------------===# - """The underlying pointer type.""" var address: Self._mlir_type """The underlying pointer.""" @@ -106,7 +116,7 @@ struct UnsafePointer[ @always_inline @implicit fn __init__(out self, value: Self._mlir_type): - """Create a pointer with the input value. + """Create a pointer from a low-level pointer primitive. Args: value: The MLIR value of the pointer to construct with. @@ -129,7 +139,7 @@ struct UnsafePointer[ @always_inline fn __init__(out self, *, other: Self): - """Copy the object. + """Copy an existing pointer. Args: other: The value to copy. From 094040b1882178f307f76b527aa2587697dff57d Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 4 Dec 2024 12:24:01 -0700 Subject: [PATCH 1972/2019] [stdlib] Remove copies in `StaticTuple.getitem` Remove the copies of the internal array before getting an element in `StaticTuple`. There is no need to create a copy before calling `pop.array.gep` as seen by the use in `InlineArray`. We'll look into the `setitem` copies in a follow-up as they are causing some errors still that need investigation. MODULAR_ORIG_COMMIT_REV_ID: 214bff889d1299f766dae591ef5ce86fbb2f1999 --- stdlib/src/utils/static_tuple.mojo | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 6904888e65..ff745bedec 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -218,15 +218,10 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): debug_assert( int(idx.__mlir_index__()) < size, "index must be within bounds" ) - # Copy the array so we can get its address, because we can't take the - # address of 'self' in a non-mutating method. - var arrayCopy = self.array var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer.address_of(arrayCopy).address, idx.__mlir_index__() + UnsafePointer.address_of(self.array).address, idx.__mlir_index__() ) - var result = UnsafePointer(ptr)[] - _ = arrayCopy - return result + return UnsafePointer(ptr)[] @always_inline("nodebug") fn __setitem__[ From 0f56379ae6ebdacb40401edd14c0cdef8b8ff1be Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Wed, 4 Dec 2024 11:41:12 -0800 Subject: [PATCH 1973/2019] [External] [Stdlib] Add `PythonObject.__contains__` (#51430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [Stdlib] Add `PythonObject.__contains__` Simple and small implementation, Improving the `🐍 Python` experience ! Example usage: ```mojo x = PythonObject([1,2,3]) if 1 in x: print("1 in x") ``` That method should be replaced by a `c-python` function if possible. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#3101 MODULAR_ORIG_COMMIT_REV_ID: d08c1f856feda307b8da58ec0cf3442553b3acc7 --- docs/changelog.md | 10 +++ stdlib/src/python/_cpython.mojo | 9 +++ stdlib/src/python/python_object.mojo | 21 ++++++ ..._for_test_python_object_dunder_contains.py | 40 +++++++++++ stdlib/test/python/test_python_cpython.mojo | 40 +++++++++++ stdlib/test/python/test_python_object.mojo | 26 +++++++ .../test_python_object_dunder_contains.mojo | 70 +++++++++++++++++++ 7 files changed, 216 insertions(+) create mode 100644 stdlib/test/python/module_for_test_python_object_dunder_contains.py create mode 100644 stdlib/test/python/test_python_cpython.mojo create mode 100644 stdlib/test/python/test_python_object_dunder_contains.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 664fe17057..b101a22608 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -566,6 +566,16 @@ what we publish. fn take_imm_slice(a: ImmStringSlice): ... ``` +- Added `PythonObject.__contains__`. + ([PR #3101](https://github.com/modularml/mojo/pull/3101) by [@rd4com](https://github.com/rd4com)) + + Example usage: + + ```mojo + x = PythonObject([1,2,3]) + if 1 in x: + print("1 in x") + ### ❌ Removed - The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 4a28b2d117..05426aa8eb 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -1348,7 +1348,16 @@ struct CPython: ", parent obj:", obj._get_ptr_as_int(), ) + return r + fn PyObject_HasAttrString( + mut self, + obj: PyObjectPtr, + name: StringRef, + ) -> Int: + var r = self.lib.get_function[ + fn (PyObjectPtr, UnsafePointer[UInt8]) -> Int + ]("PyObject_HasAttrString")(obj, name.data) return r fn PyObject_GetAttrString( diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index 4566b11501..a6b3a8a45f 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -1325,6 +1325,27 @@ struct PythonObject( """ return self._call_zero_arg_method("__invert__") + fn __contains__(self, rhs: PythonObject) raises -> Bool: + """Contains dunder. + + Calls the underlying object's `__contains__` method. + + Args: + rhs: Right hand value. + + Returns: + True if rhs is in self. + """ + # TODO: replace/optimize with c-python function. + # TODO: implement __getitem__ step for cpython membership test operator. + var cpython = _get_global_python_itf().cpython() + if cpython.PyObject_HasAttrString(self.py_object, "__contains__"): + return self._call_single_arg_method("__contains__", rhs).__bool__() + for v in self: + if v == rhs: + return True + return False + # see https://github.com/python/cpython/blob/main/Objects/call.c # for decrement rules fn __call__( diff --git a/stdlib/test/python/module_for_test_python_object_dunder_contains.py b/stdlib/test/python/module_for_test_python_object_dunder_contains.py new file mode 100644 index 0000000000..40ec598dee --- /dev/null +++ b/stdlib/test/python/module_for_test_python_object_dunder_contains.py @@ -0,0 +1,40 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +class Class_no_iterable_no_contains: + x = 1 + + +class Class_no_iterable_but_contains: + x = 123 + + def __contains__(self, rhs): + return rhs == self.x + + +class Class_iterable_no_contains: + def __init__(self): + self.data = [123, 456] + + def __iter__(self): + self.i = 0 + return self + + def __next__(self): + if self.i >= len(self.data): + raise StopIteration + else: + tmp = self.data[self.i] + self.i += 1 + return tmp diff --git a/stdlib/test/python/test_python_cpython.mojo b/stdlib/test/python/test_python_cpython.mojo new file mode 100644 index 0000000000..77d4bfdb59 --- /dev/null +++ b/stdlib/test/python/test_python_cpython.mojo @@ -0,0 +1,40 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# XFAIL: asan && !system-darwin +# RUN: %mojo %s + +from python import Python, PythonObject +from testing import assert_equal, assert_false, assert_raises, assert_true + + +def test_PyObject_HasAttrString(mut python: Python): + var Cpython_env = python.impl._cpython + + var the_object = PythonObject(0) + var result = Cpython_env[].PyObject_HasAttrString( + the_object.py_object, "__contains__" + ) + assert_equal(0, result) + + the_object = PythonObject([1, 2, 3]) + result = Cpython_env[].PyObject_HasAttrString( + the_object.py_object, "__contains__" + ) + assert_equal(1, result) + _ = the_object + + +def main(): + # initializing Python instance calls init_python + var python = Python() + test_PyObject_HasAttrString(python) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 92180fb8d0..b1c6799157 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -576,6 +576,31 @@ fn test_py_slice() raises: _ = with_2d[0:1][4] +def test_contains_dunder(): + with assert_raises(contains="'int' object is not iterable"): + var z = PythonObject(0) + _ = 5 in z + + var x = PythonObject([1.1, 2.2]) + assert_true(1.1 in x) + assert_false(3.3 in x) + + x = PythonObject(["Hello", "World"]) + assert_true("World" in x) + + x = PythonObject((1.5, 2)) + assert_true(1.5 in x) + assert_false(3.5 in x) + + var y = Dict[PythonObject, PythonObject]() + y["A"] = "A" + y["B"] = 5 + x = PythonObject(y) + assert_true("A" in x) + assert_false("C" in x) + assert_true("B" in x) + + def main(): # initializing Python instance calls init_python var python = Python() @@ -593,3 +618,4 @@ def main(): test_getitem_raises() test_setitem_raises() test_py_slice() + test_contains_dunder() diff --git a/stdlib/test/python/test_python_object_dunder_contains.mojo b/stdlib/test/python/test_python_object_dunder_contains.mojo new file mode 100644 index 0000000000..5d4c722278 --- /dev/null +++ b/stdlib/test/python/test_python_object_dunder_contains.mojo @@ -0,0 +1,70 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# XFAIL: asan && !system-darwin +# RUN: %mojo %s + +from python import Python, PythonObject +from testing import assert_equal, assert_false, assert_raises, assert_true +from collections import Dict + + +def test_contains_dunder(mut python: Python): + with assert_raises(contains="'int' object is not iterable"): + var z = PythonObject(0) + _ = 5 in z + + var x = PythonObject([1.1, 2.2]) + assert_true(1.1 in x) + assert_false(3.3 in x) + + x = PythonObject(["Hello", "World"]) + assert_true("World" in x) + + x = PythonObject((1.5, 2)) + assert_true(1.5 in x) + assert_false(3.5 in x) + + var y = Dict[PythonObject, PythonObject]() + y["A"] = "A" + y["B"] = 5 + x = PythonObject(y) + assert_true("A" in x) + assert_false("C" in x) + assert_true("B" in x) + + # tests with python modules: + module = python.import_module( + "module_for_test_python_object_dunder_contains" + ) + + x = module.Class_no_iterable_but_contains() + assert_true(123 in x) + + x = module.Class_no_iterable_no_contains() + with assert_raises( + contains="'Class_no_iterable_no_contains' object is not iterable" + ): + _ = 123 in x + + x = module.Class_iterable_no_contains() + assert_true(123 in x) + assert_true(456 in x) + assert_false(234 in x) + x.data.append(234) + assert_true(234 in x) + + +def main(): + # initializing Python instance calls init_python + var python = Python() + test_contains_dunder(python) From 51ce70866b378bdfa3e021160c88b978c51ae0b8 Mon Sep 17 00:00:00 2001 From: Lukas Hermann <1734032+lsh@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:22:00 -0500 Subject: [PATCH 1974/2019] [stdlib] Add `__reversed__` to `Span` All the machinery is already in place, so this just wires up `reversed()` for `Span`. MODULAR_ORIG_COMMIT_REV_ID: 91b588f3e0d91f27d9bc643c1e081cc27b52600e --- docs/changelog.md | 4 ++++ stdlib/src/builtin/reversed.mojo | 21 +++++++++++++++++++++ stdlib/src/utils/span.mojo | 13 +++++++++++-- stdlib/test/utils/test_span.mojo | 11 +++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b101a22608..177c2bbd9f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -292,6 +292,10 @@ what we publish. allows forming a runtime-constant StringLiteral from a compile-time-dynamic `Stringable` value. +- `Span` now implements `__reversed__`. This means that one can get a + reverse iterator over a `Span` using `reversed(my_span)`. Users should + currently prefer this method over `my_span[::-1]`. + ### 🦋 Changed - The `inout` and `borrowed` argument conventions have been renamed to the `mut` diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index a1c984e81f..e01947a3fb 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -19,6 +19,7 @@ from collections import Deque, Dict from collections.deque import _DequeIter from collections.dict import _DictEntryIter, _DictKeyIter, _DictValueIter from collections.list import _ListIter +from utils.span import Span, _SpanIter from .range import _StridedRange @@ -193,3 +194,23 @@ fn reversed[ return _DictEntryIter[K, V, dict_origin, False]( src[]._reserved() - 1, 0, src ) + + +@always_inline +fn reversed[ + T: CollectionElement +](value: Span[T]) -> _SpanIter[T, value.origin, forward=False]: + """Get a reversed iterator of the input Span. + + **Note**: iterators are currently non-raising. + + Parameters: + T: The type of the elements in the Span. + + Args: + value: The Span to get the reversed iterator of. + + Returns: + The reversed iterator of the Span. + """ + return value.__reversed__() diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 282a3bc229..5aa2fb4cf7 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -224,13 +224,22 @@ struct Span[ @always_inline fn __iter__(self) -> _SpanIter[T, origin]: - """Get an iterator over the elements of the span. + """Get an iterator over the elements of the Span. Returns: - An iterator over the elements of the span. + An iterator over the elements of the Span. """ return _SpanIter(0, self) + @always_inline + fn __reversed__(self) -> _SpanIter[T, origin, forward=False]: + """Iterate backwards over the Span. + + Returns: + A reversed iterator of the Span elements. + """ + return _SpanIter[forward=False](len(self), self) + # ===------------------------------------------------------------------===# # Trait implementations # ===------------------------------------------------------------------===# diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index ebf04f8157..a666788354 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -204,6 +204,16 @@ def test_ref(): assert_true(s.as_ref() == Pointer.address_of(l.unsafe_ptr()[])) +def test_reversed(): + var forward = InlineArray[Int, 3](1, 2, 3) + var backward = InlineArray[Int, 3](3, 2, 1) + var s = Span[Int](forward) + var i = 0 + for num in reversed(s): + assert_equal(num[], backward[i]) + i += 1 + + def main(): test_span_list_int() test_span_list_str() @@ -215,3 +225,4 @@ def main(): test_bool() test_fill() test_ref() + test_reversed() From ddd1625d22074ea6ec64c342be2bb5a076043965 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 4 Dec 2024 15:54:54 -0700 Subject: [PATCH 1975/2019] [stdlib] Clean up setting `_is_owned` for destroying packs Use `lit.ownership.mark_destroyed` instead of setting the private member of `_is_owned` of `VariadicListMem` when used in downstream collections. MODULAR_ORIG_COMMIT_REV_ID: a5503dce64c34fe17044834d9808a888417a944d --- stdlib/src/collections/deque.mojo | 18 +++++++++--------- stdlib/src/collections/inline_array.mojo | 6 ++++-- stdlib/src/collections/list.mojo | 16 +++++++++------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index 6d45725350..a0bb3cd262 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -139,17 +139,15 @@ struct Deque[ElementType: CollectionElement]( Args: values: The values to populate the deque with. """ - self = Self(variadic_list=values^) + self = Self(elements=values^) - fn __init__( - mut self, *, owned variadic_list: VariadicListMem[ElementType, _] - ): + fn __init__(mut self, *, owned elements: VariadicListMem[ElementType, _]): """Constructs a deque from the given values. Args: - variadic_list: The values to populate the deque with. + elements: The values to populate the deque with. """ - args_length = len(variadic_list) + args_length = len(elements) if args_length < self.default_capacity: capacity = self.default_capacity @@ -159,12 +157,14 @@ struct Deque[ElementType: CollectionElement]( self = Self(capacity=capacity) for i in range(args_length): - src = UnsafePointer.address_of(variadic_list[i]) + src = UnsafePointer.address_of(elements[i]) dst = self._data + i src.move_pointee_into(dst) - # Mark the elements as unowned to avoid del'ing uninitialized objects. - variadic_list._is_owned = False + # Do not destroy the elements when their backing storage goes away. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(elements) + ) self._tail = args_length diff --git a/stdlib/src/collections/inline_array.mojo b/stdlib/src/collections/inline_array.mojo index 93064e0220..6901ed1327 100644 --- a/stdlib/src/collections/inline_array.mojo +++ b/stdlib/src/collections/inline_array.mojo @@ -194,8 +194,10 @@ struct InlineArray[ var eltptr = UnsafePointer.address_of(self.unsafe_get(i)) UnsafePointer.address_of(storage[i]).move_pointee_into(eltptr) - # Mark the elements as already destroyed. - storage._is_owned = False + # Do not destroy the elements when their backing storage goes away. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(storage) + ) fn __init__(out self, *, other: Self): """Explicitly copy the provided value. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index d4791dd79e..033ceeb464 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -144,26 +144,28 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Args: values: The values to populate the list with. """ - self = Self(variadic_list=values^) + self = Self(elements=values^) - fn __init__(out self, *, owned variadic_list: VariadicListMem[T, _]): + fn __init__(out self, *, owned elements: VariadicListMem[T, _]): """Constructs a list from the given values. Args: - variadic_list: The values to populate the list with. + elements: The values to populate the list with. """ - var length = len(variadic_list) + var length = len(elements) self = Self(capacity=length) for i in range(length): - var src = UnsafePointer.address_of(variadic_list[i]) + var src = UnsafePointer.address_of(elements[i]) var dest = self.data + i src.move_pointee_into(dest) - # Mark the elements as unowned to avoid del'ing uninitialized objects. - variadic_list._is_owned = False + # Do not destroy the elements when their backing storage goes away. + __mlir_op.`lit.ownership.mark_destroyed`( + __get_mvalue_as_litref(elements) + ) self.size = length From 83f10cf036f695a71c2adfecc6d091ddc129a4e1 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 4 Dec 2024 16:14:26 -0700 Subject: [PATCH 1976/2019] [stdlib] Add tests for `StaticTuple.setitem` Move the existing `getitem` tests from `test_tuple.mojo` and start a separate test file for `StaticTuple`. Add tests for `setitem` as we are can use it to figure out why we do the copying in `setitem` and `getitem` before the gep. MODULAR_ORIG_COMMIT_REV_ID: c0ab1928aa8da0045a96f27cbe808d287f02c2a7 --- stdlib/test/utils/test_static_tuple.mojo | 52 ++++++++++++++++++++++++ stdlib/test/utils/test_tuple.mojo | 18 -------- 2 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 stdlib/test/utils/test_static_tuple.mojo diff --git a/stdlib/test/utils/test_static_tuple.mojo b/stdlib/test/utils/test_static_tuple.mojo new file mode 100644 index 0000000000..67f3fbc0e8 --- /dev/null +++ b/stdlib/test/utils/test_static_tuple.mojo @@ -0,0 +1,52 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal +from utils import StaticTuple + + +def test_getitem(): + # Should be constructible from a single element + # as well as a variadic list of elements. + var tup1 = StaticTuple[Int, 1](1) + assert_equal(tup1[0], 1) + + var tup2 = StaticTuple[Int, 2](1, 1) + assert_equal(tup2[0], 1) + assert_equal(tup2[1], 1) + + var tup3 = StaticTuple[Int, 3](1, 2, 3) + assert_equal(tup3[0], 1) + assert_equal(tup3[1], 2) + assert_equal(tup3[2], 3) + + assert_equal(tup1[Int(0)], 1) + + +def test_setitem(): + var t = StaticTuple[Int, 3](1, 2, 3) + + t[0] = 100 + assert_equal(t[0], 100) + + t[1] = 200 + assert_equal(t[1], 200) + + t[2] = 300 + assert_equal(t[2], 300) + + +def main(): + test_getitem() + test_setitem() diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index f6ccb2ce3a..0748552be0 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -19,23 +19,6 @@ from testing import assert_equal, assert_false, assert_true from utils import IndexList, StaticTuple -def test_static_tuple(): - var tup1 = StaticTuple[Int, 1](1) - assert_equal(tup1[0], 1) - - var tup2 = StaticTuple[Int, 2](1, 1) - assert_equal(tup2[0], 1) - assert_equal(tup2[1], 1) - - var tup3 = StaticTuple[Int, 3](1, 2, 3) - assert_equal(tup3[0], 1) - assert_equal(tup3[1], 2) - assert_equal(tup3[2], 3) - - assert_equal(tup3[0], 1) - assert_equal(tup3[Int(0)], 1) - - def test_static_int_tuple(): assert_equal(str(IndexList[1](1)), "(1,)") @@ -72,6 +55,5 @@ def test_tuple_literal(): def main(): - test_static_tuple() test_static_int_tuple() test_tuple_literal() From 776e802ab1b40e061be2c5011a92001e261541ea Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 4 Dec 2024 17:48:23 -0800 Subject: [PATCH 1977/2019] [mojo-lang] Fix a bad error message. This fixes https://github.com/modularml/mojo/issues/3829 MODULAR_ORIG_COMMIT_REV_ID: ce65257270bfe6443b2f91dae96590fe4a1c2528 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 177c2bbd9f..e781cf6279 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -620,6 +620,9 @@ what we publish. - [Issue #3815](https://github.com/modularml/mojo/issues/3815) - [BUG] Mutability not preserved when taking the union of two origins. +- [Issue #3829](https://github.com/modularml/mojo/issues/3829) - Poor error + message when invoking a function pointer upon an argument of the wrong origin + - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. From e3e06bdfd0e05c7f4559593336fc910854da38cf Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 4 Dec 2024 20:16:52 -0800 Subject: [PATCH 1978/2019] [******][GPU] Extend the time operations to be GPU generic MODULAR_ORIG_COMMIT_REV_ID: 9acc0eef450d97eccca0550b5f6f1eabda7e0023 --- stdlib/src/time/time.mojo | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 853c6d2d56..936ca89459 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -126,6 +126,13 @@ fn _gettime_as_nsec_unix(clockid: Int) -> Int: ) +@always_inline +fn _gpu_clock() -> Int: + """Returns a 64-bit unsigned cycle counter.""" + alias asm = "llvm.nvvm.read.ptx.sreg.clock64" if is_nvidia_gpu() else "llvm.amdgcn.s.memtime" + return int(llvm_intrinsic[asm, Int64]()) + + @always_inline fn _realtime_nanoseconds() -> Int: """Returns the current realtime time in nanoseconds""" @@ -137,7 +144,9 @@ fn _monotonic_nanoseconds() -> Int: """Returns the current monotonic time in nanoseconds""" @parameter - if os_is_windows(): + if is_gpu(): + return _gpu_clock() + elif os_is_windows(): var ft = _FILETIME() external_call["GetSystemTimePreciseAsFileTime", NoneType]( Pointer.address_of(ft) @@ -151,7 +160,6 @@ fn _monotonic_nanoseconds() -> Int: @always_inline fn _monotonic_raw_nanoseconds() -> Int: """Returns the current monotonic time in nanoseconds""" - return _gettime_as_nsec_unix(_CLOCK_MONOTONIC_RAW) @@ -204,14 +212,6 @@ fn perf_counter_ns() -> Int: Returns: The current time in ns. """ - - @parameter - if is_nvidia_gpu(): - return int( - inlined_assembly[ - "mov.u64 $0, %globaltimer;", UInt64, constraints="=l" - ]() - ) return _monotonic_nanoseconds() @@ -349,17 +349,10 @@ fn sleep(sec: Float64): """ @parameter - if is_nvidia_gpu(): - var nsec = sec * 1.0e9 - llvm_intrinsic["llvm.nvvm.nanosleep", NoneType]( - nsec.cast[DType.int32]() - ) - return - elif is_amd_gpu(): + if is_gpu(): var nsec = sec * 1.0e9 - llvm_intrinsic["llvm.amdgcn.s.sleep", NoneType]( - nsec.cast[DType.int32]() - ) + alias intrinsic = "llvm.nvvm.nanosleep" if is_nvidia_gpu() else "llvm.amdgcn.s.sleep" + llvm_intrinsic[intrinsic, NoneType](nsec.cast[DType.int32]()) return alias NANOSECONDS_IN_SECOND = 1_000_000_000 From b54f222b2fa5a00f0eaf3e81301256bbd84b86d7 Mon Sep 17 00:00:00 2001 From: Lukas Hermann <1734032+lsh@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:45:56 -0500 Subject: [PATCH 1979/2019] [stdlib] Move `strip`,`lstrip`,`rstrip` onto StringSlice (#52118) MODULAR_ORIG_COMMIT_REV_ID: 72291fc94933188ec12d2dae10c011023b026a75 --- docs/changelog.md | 2 + stdlib/benchmarks/hashlib/bench_hash.mojo | 2 +- stdlib/src/collections/string.mojo | 48 ++------ stdlib/src/os/path/path.mojo | 16 ++- stdlib/src/utils/string_slice.mojo | 136 +++++++++++++++++++--- stdlib/test/hashlib/test_ahash.mojo | 2 +- stdlib/test/utils/test_string_slice.mojo | 99 ++++++++++++++++ 7 files changed, 243 insertions(+), 62 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e781cf6279..788d1e9c95 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -296,6 +296,8 @@ what we publish. reverse iterator over a `Span` using `reversed(my_span)`. Users should currently prefer this method over `my_span[::-1]`. +- `StringSlice` now implements `strip`, `rstrip`, and `lstrip`. + ### 🦋 Changed - The `inout` and `borrowed` argument conventions have been renamed to the `mut` diff --git a/stdlib/benchmarks/hashlib/bench_hash.mojo b/stdlib/benchmarks/hashlib/bench_hash.mojo index 291701710d..6d2ba54044 100644 --- a/stdlib/benchmarks/hashlib/bench_hash.mojo +++ b/stdlib/benchmarks/hashlib/bench_hash.mojo @@ -586,7 +586,7 @@ fn gen_word_pairs[words: String = words_en]() -> List[String]: try: var list = words.split(",") for w in list: - var w1 = w[].strip() + var w1 = str(w[].strip()) for w in list: var w2 = w[].strip() result.append(w1 + " " + w2) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index a6051bbb81..47f06707c2 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -1916,7 +1916,7 @@ struct String( res.append(0) return String(res^) - fn strip(self, chars: String) -> String: + fn strip(self, chars: StringSlice) -> StringSlice[__origin_of(self)]: """Return a copy of the string with leading and trailing characters removed. @@ -1929,7 +1929,7 @@ struct String( return self.lstrip(chars).rstrip(chars) - fn strip(self) -> String: + fn strip(self) -> StringSlice[__origin_of(self)]: """Return a copy of the string with leading and trailing whitespaces removed. @@ -1938,7 +1938,7 @@ struct String( """ return self.lstrip().rstrip() - fn rstrip(self, chars: String) -> String: + fn rstrip(self, chars: StringSlice) -> StringSlice[__origin_of(self)]: """Return a copy of the string with trailing characters removed. Args: @@ -1948,29 +1948,17 @@ struct String( A copy of the string with no trailing characters. """ - var r_idx = self.byte_length() - while r_idx > 0 and self[r_idx - 1] in chars: - r_idx -= 1 + return self.as_string_slice().rstrip(chars) - return self[:r_idx] - - fn rstrip(self) -> String: + fn rstrip(self) -> StringSlice[__origin_of(self)]: """Return a copy of the string with trailing whitespaces removed. Returns: A copy of the string with no trailing whitespaces. """ - var r_idx = self.byte_length() - # TODO (#933): should use this once llvm intrinsics can be used at comp time - # for s in self.__reversed__(): - # if not s.isspace(): - # break - # r_idx -= 1 - while r_idx > 0 and _isspace(self._buffer.unsafe_get(r_idx - 1)): - r_idx -= 1 - return self[:r_idx] - - fn lstrip(self, chars: String) -> String: + return self.as_string_slice().rstrip() + + fn lstrip(self, chars: StringSlice) -> StringSlice[__origin_of(self)]: """Return a copy of the string with leading characters removed. Args: @@ -1980,29 +1968,15 @@ struct String( A copy of the string with no leading characters. """ - var l_idx = 0 - while l_idx < self.byte_length() and self[l_idx] in chars: - l_idx += 1 + return self.as_string_slice().lstrip(chars) - return self[l_idx:] - - fn lstrip(self) -> String: + fn lstrip(self) -> StringSlice[__origin_of(self)]: """Return a copy of the string with leading whitespaces removed. Returns: A copy of the string with no leading whitespaces. """ - var l_idx = 0 - # TODO (#933): should use this once llvm intrinsics can be used at comp time - # for s in self: - # if not s.isspace(): - # break - # l_idx += 1 - while l_idx < self.byte_length() and _isspace( - self._buffer.unsafe_get(l_idx) - ): - l_idx += 1 - return self[l_idx:] + return self.as_string_slice().lstrip() fn __hash__(self) -> UInt: """Hash the underlying buffer using builtin hash. diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 3840a97fac..64edffce5e 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -229,11 +229,10 @@ fn dirname[PathLike: os.PathLike, //](path: PathLike) -> String: The directory component of a pathname. """ var fspath = path.__fspath__() - alias sep = str(os.sep) - var i = fspath.rfind(sep) + 1 + var i = fspath.rfind(os.sep) + 1 var head = fspath[:i] - if head and head != sep * len(head): - return head.rstrip(sep) + if head and head != os.sep * len(head): + return head.rstrip(os.sep) return head @@ -365,7 +364,7 @@ def split[PathLike: os.PathLike, //](path: PathLike) -> (String, String): i = fspath.rfind(os.sep) + 1 head, tail = fspath[:i], fspath[i:] if head and head != str(os.sep) * len(head): - head = head.rstrip(sep) + head = str(head.rstrip(sep)) return head, tail @@ -386,11 +385,10 @@ fn basename[PathLike: os.PathLike, //](path: PathLike) -> String: The basename from the path. """ var fspath = path.__fspath__() - alias sep = str(os.sep) - var i = fspath.rfind(sep) + 1 + var i = fspath.rfind(os.sep) + 1 var head = fspath[i:] - if head and head != sep * len(head): - return head.rstrip(sep) + if head and head != os.sep * len(head): + return head.rstrip(os.sep) return head diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 394dccf89c..b6f9daa45a 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -616,13 +616,33 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]( # ===------------------------------------------------------------------===# @always_inline - fn strip(self) -> StringSlice[origin]: - """Gets a StringRef with leading and trailing whitespaces removed. - This only takes ASCII whitespace into account: - `" \\t\\n\\v\\f\\r\\x1c\\x1d\\x1e"`. + fn strip(self, chars: StringSlice) -> Self: + """Return a copy of the string with leading and trailing characters + removed. + + Args: + chars: A set of characters to be removed. Defaults to whitespace. Returns: - A StringRef with leading and trailing whitespaces removed. + A copy of the string with no leading or trailing characters. + + Examples: + + ```mojo + print("himojohi".strip("hi")) # "mojo" + ``` + . + """ + + return self.lstrip(chars).rstrip(chars) + + @always_inline + fn strip(self) -> Self: + """Return a copy of the string with leading and trailing whitespaces + removed. + + Returns: + A copy of the string with no leading or trailing whitespaces. Examples: @@ -631,15 +651,103 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]( ``` . """ - # FIXME: this can already do full isspace support with iterator - var start: Int = 0 - var end: Int = len(self) - var ptr = self.unsafe_ptr() - while start < end and _isspace(ptr[start]): - start += 1 - while end > start and _isspace(ptr[end - 1]): - end -= 1 - return StringSlice[origin](ptr=ptr + start, length=end - start) + return self.lstrip().rstrip() + + @always_inline + fn rstrip(self, chars: StringSlice) -> Self: + """Return a copy of the string with trailing characters removed. + + Args: + chars: A set of characters to be removed. Defaults to whitespace. + + Returns: + A copy of the string with no trailing characters. + + Examples: + + ```mojo + print("mojohi".strip("hi")) # "mojo" + ``` + . + """ + + var r_idx = self.byte_length() + while r_idx > 0 and self[r_idx - 1] in chars: + r_idx -= 1 + + return Self(unsafe_from_utf8=self.as_bytes()[:r_idx]) + + @always_inline + fn rstrip(self) -> Self: + """Return a copy of the string with trailing whitespaces removed. + + Returns: + A copy of the string with no trailing whitespaces. + + Examples: + + ```mojo + print("mojo ".strip()) # "mojo" + ``` + . + """ + var r_idx = self.byte_length() + # TODO (#933): should use this once llvm intrinsics can be used at comp time + # for s in self.__reversed__(): + # if not s.isspace(): + # break + # r_idx -= 1 + while r_idx > 0 and _isspace(self.as_bytes()[r_idx - 1]): + r_idx -= 1 + return Self(unsafe_from_utf8=self.as_bytes()[:r_idx]) + + @always_inline + fn lstrip(self, chars: StringSlice) -> Self: + """Return a copy of the string with leading characters removed. + + Args: + chars: A set of characters to be removed. Defaults to whitespace. + + Returns: + A copy of the string with no leading characters. + + Examples: + + ```mojo + print("himojo".strip("hi")) # "mojo" + ``` + . + """ + + var l_idx = 0 + while l_idx < self.byte_length() and self[l_idx] in chars: + l_idx += 1 + + return Self(unsafe_from_utf8=self.as_bytes()[l_idx:]) + + @always_inline + fn lstrip(self) -> Self: + """Return a copy of the string with leading whitespaces removed. + + Returns: + A copy of the string with no leading whitespaces. + + Examples: + + ```mojo + print(" mojo".strip()) # "mojo" + ``` + . + """ + var l_idx = 0 + # TODO (#933): should use this once llvm intrinsics can be used at comp time + # for s in self: + # if not s.isspace(): + # break + # l_idx += 1 + while l_idx < self.byte_length() and _isspace(self.as_bytes()[l_idx]): + l_idx += 1 + return Self(unsafe_from_utf8=self.as_bytes()[l_idx:]) @always_inline fn as_bytes(self) -> Span[Byte, origin]: diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index f1fdc69a62..e696b32c76 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -580,7 +580,7 @@ fn gen_word_pairs[words: String = words_en]() -> List[String]: try: var list = words.split(", ") for w in list: - var w1 = w[].strip() + var w1 = str(w[].strip()) for w in list: var w2 = w[].strip() result.append(w1 + " " + w2) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 057601c270..7ef357692b 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -487,6 +487,102 @@ def test_splitlines(): _assert_equal(s.splitlines(keepends=True), items) +def test_rstrip(): + # with default rstrip chars + var empty_string = "".as_string_slice() + assert_true(empty_string.rstrip() == "") + + var space_string = " \t\n\r\v\f ".as_string_slice() + assert_true(space_string.rstrip() == "") + + var str0 = " n ".as_string_slice() + assert_true(str0.rstrip() == " n") + + var str1 = "string".as_string_slice() + assert_true(str1.rstrip() == "string") + + var str2 = "something \t\n\t\v\f".as_string_slice() + assert_true(str2.rstrip() == "something") + + # with custom chars for rstrip + var str3 = "mississippi".as_string_slice() + assert_true(str3.rstrip("sip") == "m") + + var str4 = "mississippimississippi \n ".as_string_slice() + assert_true(str4.rstrip("sip ") == "mississippimississippi \n") + assert_true(str4.rstrip("sip \n") == "mississippim") + + +def test_lstrip(): + # with default lstrip chars + var empty_string = "".as_string_slice() + assert_true(empty_string.lstrip() == "") + + var space_string = " \t\n\r\v\f ".as_string_slice() + assert_true(space_string.lstrip() == "") + + var str0 = " n ".as_string_slice() + assert_true(str0.lstrip() == "n ") + + var str1 = "string".as_string_slice() + assert_true(str1.lstrip() == "string") + + var str2 = " \t\n\t\v\fsomething".as_string_slice() + assert_true(str2.lstrip() == "something") + + # with custom chars for lstrip + var str3 = "mississippi".as_string_slice() + assert_true(str3.lstrip("mis") == "ppi") + + var str4 = " \n mississippimississippi".as_string_slice() + assert_true(str4.lstrip("mis ") == "\n mississippimississippi") + assert_true(str4.lstrip("mis \n") == "ppimississippi") + + +def test_strip(): + # with default strip chars + var empty_string = "".as_string_slice() + assert_true(empty_string.strip() == "") + alias comp_empty_string_stripped = "".as_string_slice().strip() + assert_true(comp_empty_string_stripped == "") + + var space_string = " \t\n\r\v\f ".as_string_slice() + assert_true(space_string.strip() == "") + alias comp_space_string_stripped = " \t\n\r\v\f ".as_string_slice().strip() + assert_true(comp_space_string_stripped == "") + + var str0 = " n ".as_string_slice() + assert_true(str0.strip() == "n") + alias comp_str0_stripped = " n ".as_string_slice().strip() + assert_true(comp_str0_stripped == "n") + + var str1 = "string".as_string_slice() + assert_true(str1.strip() == "string") + alias comp_str1_stripped = ("string").strip() + assert_true(comp_str1_stripped == "string") + + var str2 = " \t\n\t\v\fsomething \t\n\t\v\f".as_string_slice() + alias comp_str2_stripped = (" \t\n\t\v\fsomething \t\n\t\v\f").strip() + assert_true(str2.strip() == "something") + assert_true(comp_str2_stripped == "something") + + # with custom strip chars + var str3 = "mississippi".as_string_slice() + assert_true(str3.strip("mips") == "") + assert_true(str3.strip("mip") == "ssiss") + alias comp_str3_stripped = "mississippi".as_string_slice().strip("mips") + assert_true(comp_str3_stripped == "") + + var str4 = " \n mississippimississippi \n ".as_string_slice() + assert_true(str4.strip(" ") == "\n mississippimississippi \n") + assert_true(str4.strip("\nmip ") == "ssissippimississ") + + alias comp_str4_stripped = ( + " \n mississippimississippi \n ".as_string_slice().strip(" ") + ) + assert_true(comp_str4_stripped == "\n mississippimississippi \n") + + def main(): test_string_literal_byte_span() test_string_byte_span() @@ -505,3 +601,6 @@ def main(): test_combination_10_good_10_bad_utf8_sequences() test_count_utf8_continuation_bytes() test_splitlines() + test_rstrip() + test_lstrip() + test_strip() From dd3bb6e517d535050e0a0385931ba08d53a70b89 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 4 Dec 2024 20:52:56 -0800 Subject: [PATCH 1980/2019] [Stdlib] Make time functions return the value as UInt MODULAR_ORIG_COMMIT_REV_ID: 55cac07ab34491dca8faf99109c5acaa8e8fbef5 --- stdlib/src/time/time.mojo | 36 ++++++++++++++++----------------- stdlib/test/time/test_time.mojo | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 936ca89459..7364d85324 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -47,7 +47,7 @@ alias _CLOCK_MONOTONIC_RAW = 4 # Constants alias _NSEC_PER_USEC = 1000 -alias _NSEC_PER_MSEC = 1000000 +alias _NSEC_PER_MSEC = 1_000_000 alias _USEC_PER_MSEC = 1000 alias _MSEC_PER_SEC = 1000 alias _NSEC_PER_SEC = _NSEC_PER_USEC * _USEC_PER_MSEC * _MSEC_PER_SEC @@ -69,7 +69,7 @@ struct _CTimeSpec(Stringable): self.tv_sec = 0 self.tv_subsec = 0 - fn as_nanoseconds(self) -> Int: + fn as_nanoseconds(self) -> UInt: @parameter if os_is_linux(): return self.tv_sec * _NSEC_PER_SEC + self.tv_subsec @@ -91,7 +91,7 @@ struct _FILETIME: self.dwLowDateTime = 0 self.dwHighDateTime = 0 - fn as_nanoseconds(self) -> Int: + fn as_nanoseconds(self) -> UInt: # AFTER subtracting windows offset the return value fits in a signed int64 # BEFORE subtracting windows offset the return value does not fit in a signed int64 # Taken from https://github.com/microsoft/STL/blob/c8d1efb6d504f6392acf8f6d01fd703f7c8826c0/stl/src/xtime.cpp#L50 @@ -116,7 +116,7 @@ fn _clock_gettime(clockid: Int) -> _CTimeSpec: @always_inline -fn _gettime_as_nsec_unix(clockid: Int) -> Int: +fn _gettime_as_nsec_unix(clockid: Int) -> UInt: if os_is_linux(): var ts = _clock_gettime(clockid) return ts.as_nanoseconds() @@ -127,20 +127,20 @@ fn _gettime_as_nsec_unix(clockid: Int) -> Int: @always_inline -fn _gpu_clock() -> Int: +fn _gpu_clock() -> UInt: """Returns a 64-bit unsigned cycle counter.""" alias asm = "llvm.nvvm.read.ptx.sreg.clock64" if is_nvidia_gpu() else "llvm.amdgcn.s.memtime" return int(llvm_intrinsic[asm, Int64]()) @always_inline -fn _realtime_nanoseconds() -> Int: +fn _realtime_nanoseconds() -> UInt: """Returns the current realtime time in nanoseconds""" return _gettime_as_nsec_unix(_CLOCK_REALTIME) @always_inline -fn _monotonic_nanoseconds() -> Int: +fn _monotonic_nanoseconds() -> UInt: """Returns the current monotonic time in nanoseconds""" @parameter @@ -158,20 +158,20 @@ fn _monotonic_nanoseconds() -> Int: @always_inline -fn _monotonic_raw_nanoseconds() -> Int: +fn _monotonic_raw_nanoseconds() -> UInt: """Returns the current monotonic time in nanoseconds""" return _gettime_as_nsec_unix(_CLOCK_MONOTONIC_RAW) @always_inline -fn _process_cputime_nanoseconds() -> Int: +fn _process_cputime_nanoseconds() -> UInt: """Returns the high-resolution per-process timer from the CPU""" return _gettime_as_nsec_unix(_CLOCK_PROCESS_CPUTIME_ID) @always_inline -fn _thread_cputime_nanoseconds() -> Int: +fn _thread_cputime_nanoseconds() -> UInt: """Returns the thread-specific CPU-time clock""" return _gettime_as_nsec_unix(_CLOCK_THREAD_CPUTIME_ID) @@ -202,7 +202,7 @@ fn perf_counter() -> Float64: @always_inline -fn perf_counter_ns() -> Int: +fn perf_counter_ns() -> UInt: """Return the value (in nanoseconds) of a performance counter, i.e. a clock with the highest available resolution to measure a short duration. It does include time elapsed during sleep and is system-wide. The reference @@ -221,7 +221,7 @@ fn perf_counter_ns() -> Int: @always_inline -fn now() -> Int: +fn now() -> UInt: """Deprecated: Please use time.perf_counter_ns instead. Returns the current monotonic time time in nanoseconds. This function @@ -241,7 +241,7 @@ fn now() -> Int: @always_inline -fn monotonic() -> Int: +fn monotonic() -> UInt: """ Returns the current monotonic time time in nanoseconds. This function queries the current platform's monotonic clock, making it useful for @@ -263,7 +263,7 @@ fn monotonic() -> Int: @parameter fn _time_function_windows[ func: fn () raises capturing [_] -> None -]() raises -> Int: +]() raises -> UInt: """Calculates elapsed time in Windows""" var ticks_per_sec: _WINDOWS_LARGE_INTEGER = 0 @@ -294,7 +294,7 @@ fn _time_function_windows[ @always_inline @parameter -fn time_function[func: fn () raises capturing [_] -> None]() raises -> Int: +fn time_function[func: fn () raises capturing [_] -> None]() raises -> UInt: """Measures the time spent in the function. Parameters: @@ -316,7 +316,7 @@ fn time_function[func: fn () raises capturing [_] -> None]() raises -> Int: @always_inline @parameter -fn time_function[func: fn () capturing [_] -> None]() -> Int: +fn time_function[func: fn () capturing [_] -> None]() -> UInt: """Measures the time spent in the function. Parameters: @@ -333,7 +333,7 @@ fn time_function[func: fn () capturing [_] -> None]() -> Int: try: return time_function[raising_func]() except err: - return abort[Int](err) + return abort[UInt](err) # ===-----------------------------------------------------------------------===# @@ -369,7 +369,7 @@ fn sleep(sec: Float64): _ = rem -fn sleep(sec: Int): +fn sleep(sec: UInt): """Suspends the current thread for the seconds specified. Args: diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 985a0ec9f6..342e453774 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -28,7 +28,7 @@ from testing import assert_true @always_inline @parameter fn time_me(): - sleep(1) + sleep(1.0) @always_inline @@ -50,7 +50,7 @@ fn time_templated_function[ fn time_capturing_function(iters: Int) -> Int: @parameter fn time_fn(): - sleep(1) + sleep(1.0) return time_function[time_fn]() From fbe418a2e7018092f940684deba9ff7479a12e4d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 4 Dec 2024 23:43:34 -0800 Subject: [PATCH 1981/2019] [Stdlib] Add string overload for StringLiteral.get, NFC MODULAR_ORIG_COMMIT_REV_ID: 2aa197a5b091954efe63da52515e1aade752ed2a --- stdlib/src/builtin/string_literal.mojo | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index dc7d05d49a..7b1598b481 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -106,6 +106,19 @@ struct StringLiteral( `> : !kgen.string`, ] + @always_inline("nodebug") + @staticmethod + fn get[value: String]() -> StringLiteral: + """Form a string literal from an arbitrary compile-time String value. + + Parameters: + value: The value to convert to StringLiteral. + + Returns: + The string value as a StringLiteral. + """ + return Self._from_string[value]() + @always_inline("nodebug") @staticmethod fn get[type: Stringable, //, value: type]() -> StringLiteral: From 33cad351a6bde92f03c48ac19588f16f14707b61 Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Thu, 5 Dec 2024 08:22:01 -0800 Subject: [PATCH 1982/2019] [******] Extend math.min to support boolean Extends our min function to support boolean values MODULAR_ORIG_COMMIT_REV_ID: 141e7e80532f1ad91a113d001ee2a185855de2b1 --- stdlib/src/builtin/math.mojo | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index eb1b088242..7293e16db2 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -236,7 +236,7 @@ fn min(x: SIMD, y: __type_of(x), /) -> __type_of(x): corresponding elements in x and y. Constraints: - The type of the inputs must be numeric. + The type of the inputs must be numeric or boolean. Args: x: First SIMD vector. @@ -245,8 +245,16 @@ fn min(x: SIMD, y: __type_of(x), /) -> __type_of(x): Returns: A SIMD vector containing the elementwise minimum of x and y. """ - constrained[x.type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.min`(x.value, y.value) + + @parameter + if x.type is DType.bool: + return min(x.cast[DType.uint8](), y.cast[DType.uint8]()).cast[x.type]() + else: + constrained[ + x.type.is_numeric(), "the SIMD type must be numeric or boolean" + ]() + + return __mlir_op.`pop.min`(x.value, y.value) # ===----------------------------------------------------------------------=== # From 50d5fb28b886bb01bd86b8f1da892621c25e5876 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 5 Dec 2024 09:46:11 -0700 Subject: [PATCH 1983/2019] [stdlib] Move `Span` from `utils` to `memory` `Span` is no longer a util, but is a common vocabulary type used throughout stdlib APIs. As such, promote it to `memory` module. MODULAR_ORIG_COMMIT_REV_ID: 33bae4c7dcc8191118d669985405f31599de386c --- docs/changelog.md | 4 +++- stdlib/src/builtin/_format_float.mojo | 4 ++-- stdlib/src/builtin/debug_assert.mojo | 3 +-- stdlib/src/builtin/file.mojo | 4 ++-- stdlib/src/builtin/file_descriptor.mojo | 4 +--- stdlib/src/builtin/io.mojo | 1 - stdlib/src/builtin/reversed.mojo | 2 +- stdlib/src/builtin/simd.mojo | 4 ++-- stdlib/src/builtin/sort.mojo | 4 +--- stdlib/src/builtin/string_literal.mojo | 4 ++-- stdlib/src/collections/list.mojo | 4 +--- stdlib/src/collections/string.mojo | 3 +-- stdlib/src/math/math.mojo | 3 +-- stdlib/src/memory/__init__.mojo | 1 + stdlib/src/{utils => memory}/span.mojo | 4 ++-- stdlib/src/os/path/path.mojo | 3 ++- stdlib/src/prelude/__init__.mojo | 3 ++- stdlib/src/tempfile/tempfile.mojo | 3 ++- stdlib/src/utils/__init__.mojo | 1 - stdlib/src/utils/_utf8_validation.mojo | 2 +- stdlib/src/utils/inline_string.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 3 +-- stdlib/src/utils/stringref.mojo | 2 +- stdlib/src/utils/write.mojo | 7 +++---- stdlib/test/builtin/test_sort_issue_1018.mojo | 4 +--- stdlib/test/collections/test_list.mojo | 4 +--- stdlib/test/hashlib/test_ahash.mojo | 4 +--- stdlib/test/{utils => memory}/test_span.mojo | 4 +--- stdlib/test/utils/test_string_slice.mojo | 3 ++- 29 files changed, 40 insertions(+), 54 deletions(-) rename stdlib/src/{utils => memory}/span.mojo (99%) rename stdlib/test/{utils => memory}/test_span.mojo (98%) diff --git a/docs/changelog.md b/docs/changelog.md index 788d1e9c95..d5ab2b45a1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -363,7 +363,7 @@ what we publish. `String.write`. Here's an example of using all the changes: ```mojo - from utils import Span + from memory import Span @value struct NewString(Writer, Writable): @@ -582,6 +582,8 @@ what we publish. if 1 in x: print("1 in x") +- `Span` has moved from the `utils` module to the `memory` module. + ### ❌ Removed - The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your diff --git a/stdlib/src/builtin/_format_float.mojo b/stdlib/src/builtin/_format_float.mojo index 4c5fc6797e..95d7e8a30e 100644 --- a/stdlib/src/builtin/_format_float.mojo +++ b/stdlib/src/builtin/_format_float.mojo @@ -28,9 +28,9 @@ from math import log2 from sys.info import sizeof from builtin.io import _printf -from memory import bitcast +from memory import bitcast, Span -from utils import Span, StaticTuple +from utils import StaticTuple from utils.numerics import FPUtils, isinf, isnan diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 4d0f1bb23a..e66c968189 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -23,9 +23,8 @@ from sys.ffi import c_char, c_size_t, c_uint, external_call from sys.param_env import env_get_string from builtin._location import __call_location, _SourceLocation -from memory import UnsafePointer +from memory import UnsafePointer, Span -from utils import Span from utils.write import ( _ArgBytes, _WriteBufferHeap, diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 823cbf9348..f5f4e6e5f6 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -35,9 +35,9 @@ from os import PathLike, abort from sys import external_call, sizeof from sys.ffi import OpaquePointer -from memory import AddressSpace, UnsafePointer +from memory import AddressSpace, UnsafePointer, Span -from utils import Span, StringRef, StringSlice, write_buffered +from utils import StringRef, StringSlice, write_buffered @register_passable diff --git a/stdlib/src/builtin/file_descriptor.mojo b/stdlib/src/builtin/file_descriptor.mojo index 8f7709d027..fef115e471 100644 --- a/stdlib/src/builtin/file_descriptor.mojo +++ b/stdlib/src/builtin/file_descriptor.mojo @@ -27,9 +27,7 @@ from sys.ffi import external_call from sys.info import is_gpu from builtin.io import _printf -from memory import UnsafePointer - -from utils import Span +from memory import UnsafePointer, Span @value diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index c0b55f993e..bb15718f20 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -33,7 +33,6 @@ from builtin.file_descriptor import FileDescriptor from memory import UnsafePointer, memcpy from utils import ( - Span, StaticString, StringRef, StringSlice, diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index e01947a3fb..9e1d778299 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -19,7 +19,7 @@ from collections import Deque, Dict from collections.deque import _DequeIter from collections.dict import _DictEntryIter, _DictKeyIter, _DictValueIter from collections.list import _ListIter -from utils.span import Span, _SpanIter +from memory.span import Span, _SpanIter from .range import _StridedRange diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 350eda13d0..2471fbf58a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -50,9 +50,9 @@ from builtin.dtype import _uint_type_of_width from builtin.format_int import _try_write_int from builtin.io import _snprintf from documentation import doc_private -from memory import UnsafePointer, bitcast +from memory import UnsafePointer, bitcast, Span -from utils import IndexList, Span, StaticTuple, StringSlice +from utils import IndexList, StaticTuple, StringSlice from utils._visualizers import lldb_formatter_wrapping_type from utils.numerics import FPUtils from utils.numerics import isnan as _isnan diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index 78447a82fb..23429d9b4f 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -20,9 +20,7 @@ from math import ceil from sys import bitwidthof from bit import count_leading_zeros -from memory import UnsafePointer - -from utils import Span +from memory import UnsafePointer, Span # ===-----------------------------------------------------------------------===# # sort diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 7b1598b481..377173470f 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -19,9 +19,9 @@ from collections import List from hashlib._hasher import _HashableWithHasher, _Hasher from sys.ffi import c_char -from memory import UnsafePointer, memcpy +from memory import UnsafePointer, memcpy, Span -from utils import Span, StaticString, StringRef, StringSlice, Writable, Writer +from utils import StaticString, StringRef, StringSlice, Writable, Writer from utils._visualizers import lldb_formatter_wrapping_type from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry from utils.string_slice import _StringSliceIter, _to_string_list diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 033ceeb464..13ee261638 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -24,9 +24,7 @@ from os import abort from sys import sizeof from sys.intrinsics import _type_is_eq -from memory import Pointer, UnsafePointer, memcpy - -from utils import Span +from memory import Pointer, UnsafePointer, memcpy, Span from .optional import Optional diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 47f06707c2..90ed9b5cd1 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -23,12 +23,11 @@ from sys.ffi import c_char from sys.intrinsics import _type_is_eq from bit import count_leading_zeros -from memory import UnsafePointer, memcmp, memcpy +from memory import UnsafePointer, memcmp, memcpy, Span from python import PythonObject from utils import ( IndexList, - Span, StaticString, StringRef, StringSlice, diff --git a/stdlib/src/math/math.mojo b/stdlib/src/math/math.mojo index 7b998c7220..9d5f53e0e4 100644 --- a/stdlib/src/math/math.mojo +++ b/stdlib/src/math/math.mojo @@ -36,9 +36,8 @@ from sys.info import _current_arch from bit import count_trailing_zeros from builtin.dtype import _integral_type_of from builtin.simd import _modf, _simd_apply -from memory import UnsafePointer +from memory import UnsafePointer, Span -from utils import Span from utils.index import IndexList from utils.numerics import FPUtils, isnan, nan from utils.static_tuple import StaticTuple diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index b2faf4890a..2717a5127a 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -16,5 +16,6 @@ from .arc import ArcPointer from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation from .owned_pointer import OwnedPointer from .pointer import AddressSpace, Pointer +from .span import AsBytes, Span from .unsafe import bitcast, pack_bits from .unsafe_pointer import UnsafePointer diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/memory/span.mojo similarity index 99% rename from stdlib/src/utils/span.mojo rename to stdlib/src/memory/span.mojo index 5aa2fb4cf7..cc5ebbb298 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/memory/span.mojo @@ -13,10 +13,10 @@ """Implements the Span type. -You can import these APIs from the `utils.span` module. For example: +You can import these APIs from the `memory` module. For example: ```mojo -from utils import Span +from memory import Span ``` """ diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 64edffce5e..4e65dfbf34 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -24,7 +24,8 @@ from pwd import getpwuid from stat import S_ISDIR, S_ISLNK, S_ISREG from sys import has_neon, os_is_linux, os_is_macos, os_is_windows -from utils import Span, StringSlice +from memory import Span +from utils import StringSlice from .. import PathLike from .._linux_aarch64 import _lstat as _lstat_linux_arm diff --git a/stdlib/src/prelude/__init__.mojo b/stdlib/src/prelude/__init__.mojo index 5551b826ee..3cb066f0d4 100644 --- a/stdlib/src/prelude/__init__.mojo +++ b/stdlib/src/prelude/__init__.mojo @@ -136,4 +136,5 @@ from builtin.value import ( from documentation import doc_private from memory import AddressSpace, Pointer -from utils import AsBytes, Writable, Writer +from memory.span import AsBytes +from utils import Writable, Writer diff --git a/stdlib/src/tempfile/tempfile.mojo b/stdlib/src/tempfile/tempfile.mojo index 509c234ac9..48ccb1ddcd 100644 --- a/stdlib/src/tempfile/tempfile.mojo +++ b/stdlib/src/tempfile/tempfile.mojo @@ -24,7 +24,8 @@ import sys from collections import List, Optional from pathlib import Path -from utils import Span, write_buffered +from memory import Span +from utils import write_buffered alias TMP_MAX = 10_000 diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 0f3b95ced0..fa6129cc1c 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -16,7 +16,6 @@ from .index import Index, IndexList, product from .inline_string import InlineString from .lock import BlockingScopedLock, BlockingSpinLock, SpinWaiter from .loop import unroll -from .span import AsBytes, Span from .static_tuple import StaticTuple from .string_slice import StaticString, StringSlice from .stringref import StringRef diff --git a/stdlib/src/utils/_utf8_validation.mojo b/stdlib/src/utils/_utf8_validation.mojo index e7ca5a5e3b..eb514733ca 100644 --- a/stdlib/src/utils/_utf8_validation.mojo +++ b/stdlib/src/utils/_utf8_validation.mojo @@ -28,7 +28,7 @@ https://github.com/simdutf/SimdUnicode/blob/main/src/UTF8.cs from base64._b64encode import _sub_with_saturation from sys.intrinsics import llvm_intrinsic -from memory import UnsafePointer +from memory import UnsafePointer, Span alias TOO_SHORT: UInt8 = 1 << 0 alias TOO_LONG: UInt8 = 1 << 1 diff --git a/stdlib/src/utils/inline_string.mojo b/stdlib/src/utils/inline_string.mojo index add0e6c7cc..8c6cfb3166 100644 --- a/stdlib/src/utils/inline_string.mojo +++ b/stdlib/src/utils/inline_string.mojo @@ -19,7 +19,7 @@ from collections import InlineArray, Optional from os import abort from sys import sizeof -from memory import UnsafePointer, memcpy +from memory import UnsafePointer, memcpy, Span from utils import StringSlice, Variant diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index b6f9daa45a..504b058954 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -27,10 +27,9 @@ from sys import bitwidthof, simdwidthof from sys.intrinsics import unlikely from bit import count_leading_zeros -from memory import UnsafePointer, memcmp, memcpy +from memory import UnsafePointer, memcmp, memcpy, Span from memory.memory import _memcmp_impl_unconstrained -from utils import Span from utils.format import _CurlyEntryFormattable, _FormatCurlyEntry from ._utf8_validation import _is_valid_utf8 diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index eaf6a285e3..75e864b405 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -20,7 +20,7 @@ from sys.ffi import c_char from bit import count_trailing_zeros from builtin.dtype import _uint_type_of_width -from memory import UnsafePointer, memcmp, pack_bits +from memory import UnsafePointer, memcmp, pack_bits, Span from memory.memory import _memcmp_impl_unconstrained from utils import StringSlice diff --git a/stdlib/src/utils/write.mojo b/stdlib/src/utils/write.mojo index 4d53f23afd..6d68951b9b 100644 --- a/stdlib/src/utils/write.mojo +++ b/stdlib/src/utils/write.mojo @@ -15,10 +15,9 @@ from collections import InlineArray from sys.info import is_gpu -from builtin.io import _printf -from memory import UnsafePointer, memcpy +from memory import UnsafePointer, memcpy, Span -from utils import Span, StaticString +from utils import StaticString # ===-----------------------------------------------------------------------===# @@ -36,7 +35,7 @@ trait Writer: Example: ```mojo - from utils import Span + from memory import Span @value struct NewString(Writer, Writable): diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo index e8cfb4e1f0..af50f5b896 100644 --- a/stdlib/test/builtin/test_sort_issue_1018.mojo +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -15,9 +15,7 @@ from random import rand -from memory import UnsafePointer - -from utils import Span +from memory import UnsafePointer, Span fn sort_test[D: DType, name: StringLiteral](size: Int, max: Int) raises: diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index cd173fc66c..1def6849ea 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -15,12 +15,10 @@ from collections import List from sys.info import sizeof -from memory import UnsafePointer +from memory import UnsafePointer, Span from test_utils import CopyCounter, MoveCounter from testing import assert_equal, assert_false, assert_raises, assert_true -from utils import Span - def test_mojo_issue_698(): var list = List[Float64]() diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index e696b32c76..4545e947cb 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -19,11 +19,9 @@ from time import now from bit import pop_count from builtin._location import __call_location -from memory import memset_zero, stack_allocation +from memory import memset_zero, stack_allocation, Span from testing import assert_equal, assert_not_equal, assert_true -from utils import Span - # Source: https://www.101languages.net/arabic/most-common-arabic-words/ alias words_ar = """ لا, من, هذا, أن, في, أنا, على, ما, هل, diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/memory/test_span.mojo similarity index 98% rename from stdlib/test/utils/test_span.mojo rename to stdlib/test/memory/test_span.mojo index a666788354..b8bde201e9 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/memory/test_span.mojo @@ -14,11 +14,9 @@ from collections import InlineArray, List -from memory import UnsafePointer +from memory import UnsafePointer, Span from testing import assert_equal, assert_true -from utils import Span - def test_span_list_int(): var l = List[Int](1, 2, 3, 4, 5, 6, 7) diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index 7ef357692b..dfeb37b12f 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -14,7 +14,8 @@ from testing import assert_equal, assert_false, assert_true -from utils import Span, StringSlice +from memory import Span +from utils import StringSlice from utils._utf8_validation import _is_valid_utf8 from utils.string_slice import _count_utf8_continuation_bytes From 1b7911baff73ad53e1a1f676b160251a4b08b9b5 Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Thu, 5 Dec 2024 08:52:40 -0800 Subject: [PATCH 1984/2019] [******] Extend math.max to support boolean Extends our max function to support boolean values MODULAR_ORIG_COMMIT_REV_ID: 51703fa8024d127eadd538055fee920053aafe11 --- stdlib/src/builtin/math.mojo | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index 7293e16db2..428407f857 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -182,7 +182,7 @@ fn max(x: SIMD, y: __type_of(x), /) -> __type_of(x): corresponding elements in x and y. Constraints: - The type of the inputs must be numeric. + The type of the inputs must be numeric or boolean. Args: x: First SIMD vector. @@ -191,8 +191,16 @@ fn max(x: SIMD, y: __type_of(x), /) -> __type_of(x): Returns: A SIMD vector containing the elementwise maximum of x and y. """ - constrained[x.type.is_numeric(), "the SIMD type must be numeric"]() - return __mlir_op.`pop.max`(x.value, y.value) + + @parameter + if x.type is DType.bool: + return max(x.cast[DType.uint8](), y.cast[DType.uint8]()).cast[x.type]() + else: + constrained[ + x.type.is_numeric(), "the SIMD type must be numeric or boolean" + ]() + + return __mlir_op.`pop.max`(x.value, y.value) # ===----------------------------------------------------------------------=== # From 3e58f3f01f50a46397f7f6b4e498046ed06d3f93 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 5 Dec 2024 13:47:55 -0600 Subject: [PATCH 1985/2019] [stdlib] cleanup: Remove `_lit_origin_union`; use `__origin_of(a, b)` instead This is a low-level operation that was only used in one place in practice. With the recent change to support using `__origin_of(a, b)` as a way to form origin unions, `_lit_origin_union` is no longer strictly necessary, so let's remove it. MODULAR_ORIG_COMMIT_REV_ID: 3128b8776cea534b0b58525368f2865805b57237 --- stdlib/src/builtin/builtin_list.mojo | 31 ++++++---------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index d736c55aa6..e74b93cad3 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -265,24 +265,6 @@ struct _VariadicListMemIter[ return len(self.src[]) - self.index -# Helper to compute the union of two origins: -# TODO: parametric aliases would be nice. -struct _lit_origin_union[ - is_mutable: Bool, //, - a: Origin[is_mutable], - b: Origin[is_mutable], -]: - alias result = __mlir_attr[ - `#lit.origin.union<`, - a._mlir_origin, - `,`, - b._mlir_origin, - `> : !lit.origin<`, - is_mutable.value, - `>`, - ] - - struct VariadicListMem[ elt_is_mutable: Bool, //, element_type: AnyType, @@ -424,13 +406,12 @@ struct VariadicListMem[ fn __getitem__( self, idx: Int ) -> ref [ - _lit_origin_union[ - origin, - # cast mutability of self to match the mutability of the element, - # since that is what we want to use in the ultimate reference and - # the union overall doesn't matter. - Origin[elt_is_mutable].cast_from[__origin_of(self)].result, - ].result + # cast mutability of self to match the mutability of the element, + # since that is what we want to use in the ultimate reference and + # the union overall doesn't matter. + Origin[elt_is_mutable] + .cast_from[__origin_of(origin, self)] + .result ] element_type: """Gets a single element on the variadic list. From 70bc390bc2a8b849329ae93db95e537ea3a65ec0 Mon Sep 17 00:00:00 2001 From: Lukas Hermann <1734032+lsh@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:42:27 -0500 Subject: [PATCH 1986/2019] [stdlib] Revert negative step slicing on Span. (#52196) MODULAR_ORIG_COMMIT_REV_ID: 0a0c51d90464c4cceeda1896f20f16e757cfd2bf --- stdlib/src/memory/span.mojo | 13 +++---------- stdlib/test/memory/test_span.mojo | 13 ------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/stdlib/src/memory/span.mojo b/stdlib/src/memory/span.mojo index cc5ebbb298..497a084c88 100644 --- a/stdlib/src/memory/span.mojo +++ b/stdlib/src/memory/span.mojo @@ -205,16 +205,9 @@ struct Span[ var step: Int start, end, step = slc.indices(len(self)) - if step < 0: - step = -step - var new_len = (start - end + step - 1) // step - var buff = UnsafePointer[T].alloc(new_len) - i = 0 - while start > end: - buff[i] = self._data[start] - start -= step - i += 1 - return Span[T, origin](ptr=buff, length=new_len) + debug_assert( + step == 1, "Slice must be within bounds and step must be 1" + ) var res = Self( ptr=(self._data + start), length=len(range(start, end, step)) diff --git a/stdlib/test/memory/test_span.mojo b/stdlib/test/memory/test_span.mojo index b8bde201e9..92c49210c6 100644 --- a/stdlib/test/memory/test_span.mojo +++ b/stdlib/test/memory/test_span.mojo @@ -136,19 +136,6 @@ def test_span_slice(): assert_equal(res[0], 2) assert_equal(res[1], 3) assert_equal(res[2], 4) - # Test slicing with negative step - res = s[1::-1] - assert_equal(res[0], 2) - assert_equal(res[1], 1) - res.unsafe_ptr().free() - res = s[2:1:-1] - assert_equal(res[0], 3) - assert_equal(len(res), 1) - res.unsafe_ptr().free() - res = s[5:1:-2] - assert_equal(res[0], 5) - assert_equal(res[1], 3) - res.unsafe_ptr().free() def test_copy_from(): From 85aa8638bdaa2e7856c46ce35d1f380d0f8a29aa Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 5 Dec 2024 17:26:09 -0600 Subject: [PATCH 1987/2019] [stdlib] cleanup: Remove `Stringlike` in favor of using `StringSlice` The requirements of `Stringlike` are largely the same as what `StringSlice` provides. So it's simpler and avoids extra monomorphizations if we make this code concrete instead of generic on `Stringlike`. `StringSlice` is already the common denominator of all the string types. MODULAR_ORIG_COMMIT_REV_ID: cf12cfa3455f00b1e0f06431856086a0e3a4f6bc --- stdlib/src/utils/format.mojo | 18 +++++++----------- stdlib/src/utils/string_slice.mojo | 24 ------------------------ 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/stdlib/src/utils/format.mojo b/stdlib/src/utils/format.mojo index 5c1dc2cf8b..752a34898d 100644 --- a/stdlib/src/utils/format.mojo +++ b/stdlib/src/utils/format.mojo @@ -16,8 +16,6 @@ from collections import Optional from memory import UnsafePointer -from utils.string_slice import Stringlike - # TODO: _FormatCurlyEntry and _FormatSpec should be public in the future for # people who want to write their own templating engines. This is not yet done # because the implementation is incomplete and we are missing crucial features. @@ -36,7 +34,7 @@ from utils.string_slice import Stringlike # a trait that all format specifications conform to) @value struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): - """The struct that handles `Stringlike` formatting by curly braces entries. + """The struct that handles string formatting by curly braces entries. This is internal for the types: `String`, `StringLiteral` and `StringSlice`. """ @@ -141,7 +139,7 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): return self.field.isa[Int]() @staticmethod - fn format[T: Stringlike](fmt_src: T, args: Self._args_t) raises -> String: + fn format(fmt_src: StringSlice, args: Self._args_t) raises -> String: """Format the entries. Args: @@ -177,9 +175,9 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): return res^ @staticmethod - fn _create_entries[ - T: Stringlike - ](fmt_src: T, len_pos_args: Int) raises -> (List[Self], Int): + fn _create_entries( + fmt_src: StringSlice, len_pos_args: Int + ) raises -> (List[Self], Int): """Returns a list of entries and its total estimated entry byte width. """ var manual_indexing_count = 0 @@ -264,11 +262,9 @@ struct _FormatCurlyEntry(CollectionElement, CollectionElementNew): raise Error(l_err) return entries^, total_estimated_entry_byte_width - fn _handle_field_and_break[ - T: Stringlike - ]( + fn _handle_field_and_break( mut self, - fmt_src: T, + fmt_src: StringSlice, len_pos_args: Int, i: Int, start_value: Int, diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 504b058954..e572747d4e 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -1122,30 +1122,6 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]( # ===-----------------------------------------------------------------------===# -trait Stringlike: - """Trait intended to be used only with `String`, `StringLiteral` and - `StringSlice`.""" - - fn byte_length(self) -> Int: - """Get the string length in bytes. - - Returns: - The length of this string in bytes. - - Notes: - This does not include the trailing null terminator in the count. - """ - ... - - fn unsafe_ptr(self) -> UnsafePointer[UInt8]: - """Get raw pointer to the underlying data. - - Returns: - The raw pointer to the data. - """ - ... - - fn _to_string_list[ T: CollectionElement, # TODO(MOCO-1446): Make `T` parameter inferred len_fn: fn (T) -> Int, From 27011d75b0229858d019886ea36d53c10686bc20 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 6 Dec 2024 05:51:58 -0800 Subject: [PATCH 1988/2019] [******][GPU] Rename has_nvidia/amd to include accelerator MODULAR_ORIG_COMMIT_REV_ID: 32397c88a4cd6f805e69ec1c5383564c9c76deb3 --- stdlib/src/sys/__init__.mojo | 4 ++-- stdlib/src/sys/info.mojo | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index baf2af6fa7..e469d501b5 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -19,7 +19,7 @@ from .ffi import DEFAULT_RTLD, RTLD, DLHandle, external_call from .info import ( alignof, bitwidthof, - has_amd_gpu, + has_amd_gpu_accelerator, has_avx, has_avx2, has_avx512f, @@ -28,7 +28,7 @@ from .info import ( has_neon, has_neon_int8_dotprod, has_neon_int8_matmul, - has_nvidia_gpu, + has_nvidia_gpu_accelerator, has_sse4, has_vnni, is_amd_gpu, diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index ccd39c7ee2..3cb4b2787a 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -870,7 +870,7 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: @always_inline("nodebug") -fn has_amd_gpu() -> Bool: +fn has_amd_gpu_accelerator() -> Bool: """Returns True if the host system has an AMD GPU and False otherwise. Returns: @@ -880,7 +880,7 @@ fn has_amd_gpu() -> Bool: @always_inline("nodebug") -fn has_nvidia_gpu() -> Bool: +fn has_nvidia_gpu_accelerator() -> Bool: """Returns True if the host system has an NVIDIA GPU and False otherwise. Returns: From 1491437c9310617c57cefa3ba8fd5782f61eddb7 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 6 Dec 2024 06:02:03 -0800 Subject: [PATCH 1989/2019] [Stdlib] Remove the time.now function MODULAR_ORIG_COMMIT_REV_ID: 189e758f6f3a147fad07afd382c09146a470a168 --- examples/reduce.mojo | 1 - stdlib/src/time/__init__.mojo | 1 - stdlib/src/time/time.mojo | 22 +--------------------- stdlib/test/hashlib/test_ahash.mojo | 1 - stdlib/test/time/test_time.mojo | 2 -- 5 files changed, 1 insertion(+), 26 deletions(-) diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 8e213c6b86..9babfd91a0 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -17,7 +17,6 @@ # Reductions and scans are common algorithm patterns in parallel computing. from random import rand -from time import now from algorithm import sum from benchmark import Unit, benchmark, keep diff --git a/stdlib/src/time/__init__.mojo b/stdlib/src/time/__init__.mojo index 5d01e75fdf..6e5097653a 100644 --- a/stdlib/src/time/__init__.mojo +++ b/stdlib/src/time/__init__.mojo @@ -14,7 +14,6 @@ from .time import ( monotonic, - now, perf_counter, perf_counter_ns, sleep, diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 7364d85324..0167c26548 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `time` package. For example: ```mojo -from time import now +from time import perf_counter_ns ``` """ @@ -215,26 +215,6 @@ fn perf_counter_ns() -> UInt: return _monotonic_nanoseconds() -# ===-----------------------------------------------------------------------===# -# now -# ===-----------------------------------------------------------------------===# - - -@always_inline -fn now() -> UInt: - """Deprecated: Please use time.perf_counter_ns instead. - - Returns the current monotonic time time in nanoseconds. This function - queries the current platform's monotonic clock, making it useful for - measuring time differences, but the significance of the returned value - varies depending on the underlying implementation. - - Returns: - The current time in ns. - """ - return perf_counter_ns() - - # ===-----------------------------------------------------------------------===# # monotonic # ===-----------------------------------------------------------------------===# diff --git a/stdlib/test/hashlib/test_ahash.mojo b/stdlib/test/hashlib/test_ahash.mojo index 4545e947cb..c9f01c3a14 100644 --- a/stdlib/test/hashlib/test_ahash.mojo +++ b/stdlib/test/hashlib/test_ahash.mojo @@ -15,7 +15,6 @@ from hashlib._ahash import AHasher from hashlib._hasher import _hash_with_hasher as hash from hashlib.hash import hash as old_hash -from time import now from bit import pop_count from builtin._location import __call_location diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 342e453774..0148bfeeef 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -15,7 +15,6 @@ from sys import os_is_windows from time import ( monotonic, - now, perf_counter, perf_counter_ns, sleep, @@ -60,7 +59,6 @@ fn test_time() raises: assert_true(perf_counter() > 0) assert_true(perf_counter_ns() > 0) - assert_true(now() > 0) assert_true(monotonic() > 0) var t1 = time_function[time_me]() From 8231184073b914ed3326a241126accc49f2f70f6 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 6 Dec 2024 06:25:21 -0800 Subject: [PATCH 1990/2019] [Stdlib] Add new has_accelerator helper function, NFC MODULAR_ORIG_COMMIT_REV_ID: dc9c60cda00ff29c9b6dc11d8fbabdccd146953e --- stdlib/src/sys/__init__.mojo | 1 + stdlib/src/sys/info.mojo | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index e469d501b5..b0777f5c6e 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -19,6 +19,7 @@ from .ffi import DEFAULT_RTLD, RTLD, DLHandle, external_call from .info import ( alignof, bitwidthof, + has_accelerator, has_amd_gpu_accelerator, has_avx, has_avx2, diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 3cb4b2787a..ee149ca72a 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -869,6 +869,16 @@ fn _macos_version() raises -> Tuple[Int, Int, Int]: # ===-----------------------------------------------------------------------===# +@always_inline("nodebug") +fn has_accelerator() -> Bool: + """Returns True if the host system has an accelerator and False otherwise. + + Returns: + True if the host system has an accelerator. + """ + return _accelerator_arch() != "" + + @always_inline("nodebug") fn has_amd_gpu_accelerator() -> Bool: """Returns True if the host system has an AMD GPU and False otherwise. From 0dcb1ee949abdaa029408115e1776697761e8309 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 6 Dec 2024 10:16:16 -0700 Subject: [PATCH 1991/2019] [stdlib] Remove copy in `StaticTuple.setitem[idx]` Similar to the recent change made in `getitem`, remove the copy in the `setitem` overload for static index. We can't quite yet remove the copy in the runtime index case of `setitem` which is still being investigated. While tracking this down, add an explicit test case for `StaticTuple.setitem` for compile-time index to make sure we didn't break anything. MODULAR_ORIG_COMMIT_REV_ID: 95818ecfb9845f7fcd410481c64894c4ab00f631 --- stdlib/src/utils/static_tuple.mojo | 4 +--- stdlib/test/utils/test_static_tuple.mojo | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index ff745bedec..0788b54b43 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -257,6 +257,4 @@ struct StaticTuple[element_type: AnyTrivialRegType, size: Int](Sized): val: The value to store. """ constrained[index < size]() - var tmp = self - _set_array_elem[index, size, Self.element_type](val, tmp.array) - self = tmp + _set_array_elem[index, size, Self.element_type](val, self.array) diff --git a/stdlib/test/utils/test_static_tuple.mojo b/stdlib/test/utils/test_static_tuple.mojo index 67f3fbc0e8..b2f46d55af 100644 --- a/stdlib/test/utils/test_static_tuple.mojo +++ b/stdlib/test/utils/test_static_tuple.mojo @@ -46,6 +46,10 @@ def test_setitem(): t[2] = 300 assert_equal(t[2], 300) + alias idx: Int = 0 + t.__setitem__[idx](400) + assert_equal(t[0], 400) + def main(): test_getitem() From 6a0b31f289eaf7ba136c66540b90c2101189c239 Mon Sep 17 00:00:00 2001 From: Evan Ovadia Date: Fri, 6 Dec 2024 12:51:23 -0500 Subject: [PATCH 1992/2019] [mojo-lang] Adds __disable_del keyword, to destroy linear types. Adds __disable_del keyword, to allow the user to destroy `@explicit_destroy` types. All these names are temporary, we'll all figure those out soon. MODULAR_ORIG_COMMIT_REV_ID: 5005b11c4c3bdba25d096c5ffc300debebfd5523 --- docs/changelog.md | 4 ++++ stdlib/src/builtin/coroutine.mojo | 8 ++++---- stdlib/src/memory/owned_pointer.mojo | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d5ab2b45a1..02a1a13eef 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -298,6 +298,10 @@ what we publish. - `StringSlice` now implements `strip`, `rstrip`, and `lstrip`. +- Introduced the `@explicit_destroy` annotation, the `__disable_del` keyword, + the `UnknownDestructibility` trait, and the `ImplicitlyDestructible` keyword, + for the experimental explicitly destroyed types feature. + ### 🦋 Changed - The `inout` and `borrowed` argument conventions have been renamed to the `mut` diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index b1e7c4bd55..04688695c8 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -134,7 +134,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: fn force_destroy(owned self): """Destroy the coroutine object.""" __mlir_op.`co.destroy`(self._handle) - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + __disable_del self @always_inline fn __await__(owned self) -> type as out: @@ -147,7 +147,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: # Black magic! Internal implementation detail! # Don't you dare copy this code! 😤 var handle = self._handle - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + __disable_del self __mlir_op.`co.await`[_type=NoneType]( handle, __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), @@ -217,7 +217,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: fn force_destroy(owned self): """Destroy the coroutine object.""" __mlir_op.`co.destroy`(self._handle) - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + __disable_del self @always_inline fn __await__(owned self) raises -> type as out: @@ -230,7 +230,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: # Black magic! Internal implementation detail! # Don't you dare copy this code! 😤 var handle = self._handle - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + __disable_del self if __mlir_op.`co.await`[_type = __mlir_type.i1]( handle, __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 5d1ddb367d..4dd473023c 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -148,7 +148,7 @@ struct OwnedPointer[T: AnyType]: """ var r = self._inner.take_pointee() self._inner.free() - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + __disable_del self return r^ @@ -171,6 +171,6 @@ struct OwnedPointer[T: AnyType]: var ptr = self._inner # Prevent the destructor from running on `self` - __mlir_op.`lit.ownership.mark_destroyed`(__get_mvalue_as_litref(self)) + __disable_del self return ptr From 8c3c8624b43f60b0da8db6a10ce46c48dfa8da79 Mon Sep 17 00:00:00 2001 From: Evan Ovadia Date: Fri, 6 Dec 2024 15:11:58 -0500 Subject: [PATCH 1993/2019] Added associated types; we can now have aliases like `alias T: AnyType`, `alias N: Int`, etc. in a trait, and then specify them in structs that conform to that trait. MODULAR_ORIG_COMMIT_REV_ID: f1ca27c6c5a94c02424b841adf16ff1f3ea04dc9 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 02a1a13eef..5398fd44c6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -302,6 +302,10 @@ what we publish. the `UnknownDestructibility` trait, and the `ImplicitlyDestructible` keyword, for the experimental explicitly destroyed types feature. +- Added associated types; we can now have aliases like `alias T: AnyType`, + `alias N: Int`, etc. in a trait, and then specify them in structs that conform + to that trait. + ### 🦋 Changed - The `inout` and `borrowed` argument conventions have been renamed to the `mut` From 77a5feea34b72f919a33788035fbdf3c9902cf8a Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Fri, 6 Dec 2024 20:23:56 -0800 Subject: [PATCH 1994/2019] [Docs] Origin updates. Update Lifetimes, origins, and references page for origin improvements in in 24.6. MODULAR_ORIG_COMMIT_REV_ID: d40dc85d4c74ad922335489c0c569fcee95c15d1 --- docs/manual/values/lifetimes.mdx | 109 ++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 31 deletions(-) diff --git a/docs/manual/values/lifetimes.mdx b/docs/manual/values/lifetimes.mdx index 39c2402f43..5dc05d799a 100644 --- a/docs/manual/values/lifetimes.mdx +++ b/docs/manual/values/lifetimes.mdx @@ -80,23 +80,23 @@ struct to specify an origin with parametric mutability: struct ParametricRef[ is_mutable: Bool, //, - origin: Origin[is_mutable].type + origin: Origin[is_mutable] ]: pass ``` -Note that `Origin` *isn't an origin value*, it's a helper for specifying a +Note that `Origin` isn't an origin **value**, it's a helper for specifying a origin **type**. Origin types carry the mutability of a reference as a boolean parameter value, indicating whether the origin is mutable, immutable, or even with mutability depending on a parameter specified by the enclosing API. The `is_mutable` parameter here is an [infer-only -parameter](/mojo/manual/parameters/#infer-only-parameters). It's never -specified directly by the user, but always inferred from context. The -`origin` value is often inferred, as well. For example, the following code -creates a [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an existing -value, but doesn't need to specify an origin—the `origin` is inferred from -the variable passed in to the `address_of()` method. +parameter](/mojo/manual/parameters/#infer-only-parameters). It can't be passed +as a positional parameter—it's either inferred from context or specified by +keyword. The `origin` value is often inferred, as well. For example, the +following code creates a [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an +existing value, but doesn't need to specify an origin—the `origin` is inferred +from the variable passed in to the `address_of()` method. ```mojo from memory import Pointer @@ -114,15 +114,15 @@ A final type of origin value is an `OriginSet`. As the name suggests, an Most origin values are created by the compiler. As a developer, there are a few ways to specify origin values: -* Static origin. The `StaticConstantOrigin` - alias is an origin value representing immutable values that that last for the - duration of the program. String literal values have a `StaticConstantOrigin`. -* Derived origin. The `__origin_of()` magic function returns the origin associated - with the value (or values) passed in. -* Inferred origin. You can use inferred parameters to capture the origin - of a value passed in to a function. -* Wildcard origins. The `ImmutableAnyOrigin` and `MutableAnyOrigin` aliases - are special cases indicating a reference that might access any live value. +* Static origin. The `StaticConstantOrigin` alias is an origin value + representing immutable values that that last for the duration of the program. + String literal values have a `StaticConstantOrigin`. +* Derived origin. The `__origin_of()` magic function returns the origin + associated with the value (or values) passed in. +* Inferred origin. You can use inferred parameters to capture the origin of a + value passed in to a function. +* Wildcard origins. The `ImmutableAnyOrigin` and `MutableAnyOrigin` aliases are + special cases indicating a reference that might access any live value. #### Static origins @@ -138,8 +138,15 @@ static origin. #### Derived origins -Use the `__origin_of(value)` operator to obtain a value's origin. The -argument to `__origin_of()` can take an arbitrary expression: +Use the `__origin_of(value)` operator to obtain a value's origin. An argument +to `__origin_of()` can take an arbitrary expression that yields one of the +following: + +- An origin value. + +- A value with a memory location. + +For example: ```mojo __origin_of(self) @@ -148,7 +155,7 @@ __origin_of(foo()) ``` The `__origin_of()` operator is analyzed statically at compile time; -The expression passed to `__origin_of()` is never evaluated. (For example, +The expressions passed to `__origin_of()` are never evaluated. (For example, when the compiler analyzes `__origin_of(foo())`, it doesn't run the `foo()` function.) @@ -166,10 +173,19 @@ struct BoxedString: fn __init__(out self, value: String): self.o_ptr = OwnedPointer(value) - fn as_ptr(self) -> Pointer[String, __origin_of(self.o_ptr)]: + fn as_ptr(mut self) -> Pointer[String, __origin_of(self.o_ptr)]: return Pointer.address_of(self.o_ptr[]) ``` +Note that the `as_ptr()` method takes its `self` argument as `mut self`. If it +used the default `read` argument convention, it would be immutable, and the +derived origin (`__origin_of(self.o_ptr)`) would also be immutable. + +You can also pass multiple expressions to `__origin_of()` to express the union +of two or more origins: + +`__origin_of(a, b)` + #### Inferred origins The other common way to access an origin value is to *infer* it from the @@ -180,7 +196,7 @@ has an associated `origin`: struct Span[ is_mutable: Bool, //, T: CollectionElement, - origin: Origin[is_mutable].type, + origin: Origin[is_mutable], ](CollectionElementNew): """A non owning view of contiguous data. ``` @@ -229,9 +245,17 @@ to use a `ref` argument: The syntax for a `ref` argument is: -ref [origin_specifier] arg_name: arg_type +ref arg_name: arg_type + +Or: -The origin specifier passed inside the square brackets can be either: +ref [origin_specifier(s)] +arg_name: arg_type + +In the first form, the origin and mutability of the `ref` argument is inferred +from the value passed in. The second form includes an origin clause, consisting +of one or more origin specifiers inside square brackets. An origin +specifier can be either: * An origin value. @@ -244,22 +268,24 @@ The origin specifier passed inside the square brackets can be either: ref [self] ``` -* An underscore character (`_`) to indicate that the origin is *unbound*. You - can think of the underscore as a wildcard that will accept any origin: +* An [`AddressSpace`](/nightly/mojo/stdlib/memory/pointer/AddressSpace) value. + +* An underscore character (`_`) to indicate that the origin is *unbound*. This + is equivalent to omitting the origin specifier. ```mojo - def add_ref(ref a: Int, b: Int) -> Int: + def add_ref(ref a: Int, b: Int) -> Int: return a+b ``` You can also name the origin explicitly. This is useful if you want to specify -an `ImmutableOrigin` or `MutableLOrigin`, or if you want to bind to +an `ImmutableOrigin` or `MutableOrigin`, or if you want to bind to the `is_mutable` parameter. ```mojo def take_str_ref[ is_mutable: Bool, //, - origin: Origin[is_mutable].type + origin: Origin[is_mutable] ](ref [origin] s: String): @parameter if is_mutable: @@ -282,8 +308,14 @@ Mutable: Goodbye ### `ref` return values Like `ref` arguments, `ref` return values allow a function to return a mutable -or immutable reference to a value. Like a `read` or `mut` argument, these -references don't need to be dereferenced. +or immutable reference to a value. The syntax for a `ref` return value is: + +-> ref [origin_specifier(s)] + arg_type + +Note that you **must** specify an origin specifier for a `ref` return value. The +values allowed for origin specifiers are the same as the ones listed for +[`ref` arguments](#ref-arguments). `ref` return values can be an efficient way to handle updating items in a collection. The standard way to do this is by implementing the `__getitem__()` @@ -432,3 +464,18 @@ Diana Without parametric mutability, you'd need to write two versions of `__getitem__()`, one that accepts an immutable `self` and another that accepts a mutable `self`. + +#### Return values with union origins + +A `ref` return value can include multiple values in its origin specifier, which +yields the union of the origins. For example, the following `pick_one()` +function returns a reference to one of the two input strings, with an origin +that's a union of both origins. + +```mojo +def pick_one(cond: Bool, ref a: String, ref b: String) -> ref [a, b] String: + if cond: + return a + else: + return b +``` From df95c1e699dd8abaf7a9505ec7c0577f18f2bffc Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 6 Dec 2024 22:30:15 -0800 Subject: [PATCH 1995/2019] [mojo-lang] Fix bugs related to SRValue -> ref conversion. This fixes https://github.com/modularml/mojo/issues/3830, a collection of issues handling emission of SRValues to argument conventions that require a memory argument. In these cases, we need to emit the value to memory and re-infer. This was completely broken for indirect calls, and didn't handle the case of direct calls that failed to re-infer. MODULAR_ORIG_COMMIT_REV_ID: 172c9efe1ca9083e50f1f58629d0b990bb3090cd --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5398fd44c6..3673d58fd7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -635,6 +635,9 @@ what we publish. - [Issue #3829](https://github.com/modularml/mojo/issues/3829) - Poor error message when invoking a function pointer upon an argument of the wrong origin +- [Issue #3830](https://github.com/modularml/mojo/issues/3830) - Failures + emitting register RValues to ref arguments. + - The VS Code extension now auto-updates its private copy of the MAX SDK. - The variadic initializer for `SIMD` now works in parameter expressions. From 85876e7afef5d8e6b4542a755e582e32149b116c Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sat, 7 Dec 2024 09:11:39 -1000 Subject: [PATCH 1996/2019] [mojo-stdlib] add implicit decorators where required Extra implicit decorators required for making the explicit constructor filtering more strict. MODULAR_ORIG_COMMIT_REV_ID: 82617a0e6e0de514abeda843bdadc48d42fdbbe3 --- stdlib/src/builtin/dtype.mojo | 10 +++++++++ stdlib/src/builtin/object.mojo | 5 ++++- stdlib/src/collections/string.mojo | 34 +++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 1ed5dd09c6..b8a7dff5d6 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -155,6 +155,16 @@ struct DType( """ self = other + @always_inline + @implicit + fn __init__(out self, value: Self.type): + """Construct a DType from MLIR dtype. + + Args: + value: The MLIR dtype. + """ + self.value = value + @staticmethod fn _from_str(str: String) -> DType: """Construct a DType from a string. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 21b345b89d..fce05d2476 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -320,6 +320,7 @@ struct _ObjectImpl( self.value = Self.type(value) @always_inline + @implicit fn __init__[dt: DType](mut self, value: SIMD[dt, 1]): @parameter if dt.is_integral(): @@ -785,6 +786,7 @@ struct object( self._value = value @always_inline + @implicit fn __init__[dt: DType](mut self, value: SIMD[dt, 1]): """Initializes the object with a generic scalar value. If the scalar value type is bool, it is converted to a boolean. Otherwise, it is @@ -842,6 +844,7 @@ struct object( self._value = impl @always_inline + @implicit fn __init__[*Ts: CollectionElement](mut self, value: ListLiteral[*Ts]): """Initializes the object from a list literal. @@ -1861,7 +1864,7 @@ struct object( var impl = _ImmutableString(UnsafePointer[UInt8].alloc(1), 1) var char = self._value.get_as_string().data[index] impl.data.init_pointee_move(char) - return _ObjectImpl(impl) + return object(impl) return self._value.get_list_element(i._value.get_as_int().value) @always_inline diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 90ed9b5cd1..86449179a3 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -785,7 +785,8 @@ struct String( @always_inline @implicit fn __init__(out self, owned impl: List[UInt8, *_]): - """Construct a string from a buffer of bytes. + """Construct a string from a buffer of bytes without copying the + allocated data. The buffer must be terminated with a null byte: @@ -815,6 +816,37 @@ struct String( ptr=impl.steal_data(), length=size, capacity=capacity ) + @always_inline + @implicit + fn __init__(out self, impl: Self._buffer_type): + """Construct a string from a buffer of bytes, copying the allocated + data. Use the transfer operator ^ to avoid the copy. + + The buffer must be terminated with a null byte: + + ```mojo + var buf = List[UInt8]() + buf.append(ord('H')) + buf.append(ord('i')) + buf.append(0) + var hi = String(buf) + ``` + + Args: + impl: The buffer. + """ + debug_assert( + len(impl) > 0 and impl[-1] == 0, + "expected last element of String buffer to be null terminator", + ) + # We make a backup because steal_data() will clear size and capacity. + var size = impl.size + debug_assert( + impl[size - 1] == 0, + "expected last element of String buffer to be null terminator", + ) + self._buffer = impl + @always_inline fn __init__(out self): """Construct an uninitialized string.""" From c77f9b5c45b5cdd6c98a16a66098db397b97e857 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Sat, 7 Dec 2024 13:46:32 -1000 Subject: [PATCH 1997/2019] [mojo-lang] Diagnose more implicit conversion without `@implicit` decorator Now checks for @implicit decorator in all scenarios when callable is CallSyntax::kImplicitConvert. This previously wasn't happening in instances like var x: Foo = 42, self.foo = 42, and returning values. This fixes https://github.com/modularml/mojo/issues/3828 and Fixes MOCO-1485 MODULAR_ORIG_COMMIT_REV_ID: 3a6b1ba1b5d7d3ff2ac7ddc2e36dac70cc5455cf --- examples/notebooks/BoolMLIR.ipynb | 17 +- examples/notebooks/RayTracing.ipynb | 1994 ++++++++++++++------------- 2 files changed, 1009 insertions(+), 1002 deletions(-) diff --git a/examples/notebooks/BoolMLIR.ipynb b/examples/notebooks/BoolMLIR.ipynb index 355dc77909..514770e9e8 100644 --- a/examples/notebooks/BoolMLIR.ipynb +++ b/examples/notebooks/BoolMLIR.ipynb @@ -126,7 +126,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -227,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": { "tags": [] }, @@ -239,6 +239,7 @@ "\n", " # ...\n", "\n", + " @implicit\n", " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value" ] @@ -288,7 +289,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": { "tags": [] }, @@ -306,6 +307,7 @@ " fn __init__(out self):\n", " self = OurFalse\n", "\n", + " @implicit\n", " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value" ] @@ -353,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": { "tags": [] }, @@ -369,6 +371,7 @@ "\n", " # ...\n", "\n", + " @implicit\n", " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value\n", "\n", @@ -429,7 +432,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -441,6 +444,7 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", + " @implicit\n", " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value\n", "\n", @@ -495,7 +499,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -507,6 +511,7 @@ "struct OurBool:\n", " var value: __mlir_type.i1\n", "\n", + " @implicit\n", " fn __init__(out self, value: __mlir_type.i1):\n", " self.value = value\n", "\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 66a768da6e..ea37cea474 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -1,1020 +1,1022 @@ { - "cells": [ - { - "cell_type": "raw", - "id": "7a924a43", - "metadata": {}, - "source": [ - "---\n", - "title: Ray tracing in Mojo\n", - "description: Learn how to draw 3D graphics with ray-traced lighting using Mojo.\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", - "---" - ] - }, - { - "cell_type": "markdown", - "id": "e48ca293", - "metadata": {}, - "source": [ - "[//]: # REMOVE_FOR_WEBSITE\n", - "*Copyright 2023 Modular, Inc: Licensed under the Apache License v2.0 with LLVM Exceptions.*" - ] - }, - { - "cell_type": "markdown", - "id": "214584ac", - "metadata": {}, - "source": [ - "[//]: # REMOVE_FOR_WEBSITE\n", - "# Ray tracing in Mojo" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "c4edb2c9-5109-4e00-98c0-3aa92dbca7d1", - "metadata": {}, - "source": [ - "This tutorial about [ray\n", - "tracing](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) is based on the\n", - "popular tutorial [Understandable RayTracing in\n", - "C++](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing).\n", - "The mathematical explanations are well described in that tutorial, so we'll\n", - "just point you to the appropriate sections for reference as we implement a\n", - "basic ray tracer in Mojo." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "fbd4aa63-a178-41f2-ad66-b017c1c69d2a", - "metadata": {}, - "source": [ - "## Step 1: Basic definitions\n", - "\n", - "We'll start by defining a `Vec3f` struct, which will use to represent a vector\n", - "in 3D space as well as RGB pixels. We'll use a `SIMD` representation for our\n", - "vector to enable vectorized operations. The `SIMD` type is a fixed-size vector,\n", - "and its size must be a power of 2. So we'll use a size of 4 and always pad the\n", - "underlying storage with a 0." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "78a07829-05bc-4a67-8ccc-f2e1537c478c", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "from math import isqrt\n", - "\n", - "\n", - "@register_passable(\"trivial\")\n", - "struct Vec3f:\n", - " var data: SIMD[DType.float32, 4]\n", - "\n", - " @always_inline\n", - " fn __init__(out self, x: Float32, y: Float32, z: Float32):\n", - " self.data = SIMD[DType.float32, 4](x, y, z, 0)\n", - "\n", - " @always_inline\n", - " fn __init__(out self, data: SIMD[DType.float32, 4]):\n", - " self.data = data\n", - "\n", - " @always_inline\n", - " @staticmethod\n", - " fn zero() -> Vec3f:\n", - " return Vec3f(0, 0, 0)\n", - "\n", - " @always_inline\n", - " fn __sub__(self, other: Vec3f) -> Vec3f:\n", - " return self.data - other.data\n", - "\n", - " @always_inline\n", - " fn __add__(self, other: Vec3f) -> Vec3f:\n", - " return self.data + other.data\n", - "\n", - " @always_inline\n", - " fn __matmul__(self, other: Vec3f) -> Float32:\n", - " return (self.data * other.data).reduce_add()\n", - "\n", - " @always_inline\n", - " fn __mul__(self, k: Float32) -> Vec3f:\n", - " return self.data * k\n", - "\n", - " @always_inline\n", - " fn __neg__(self) -> Vec3f:\n", - " return self.data * -1.0\n", - "\n", - " @always_inline\n", - " fn __getitem__(self, idx: Int) -> SIMD[DType.float32, 1]:\n", - " return self.data[idx]\n", - "\n", - " @always_inline\n", - " fn cross(self, other: Vec3f) -> Vec3f:\n", - " var self_zxy = self.data.shuffle[2, 0, 1, 3]()\n", - " var other_zxy = other.data.shuffle[2, 0, 1, 3]()\n", - " return (self_zxy * other.data - self.data * other_zxy).shuffle[\n", - " 2, 0, 1, 3\n", - " ]()\n", - "\n", - " @always_inline\n", - " fn normalize(self) -> Vec3f:\n", - " return self.data * isqrt(self @ self)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "2619eb1b-103b-4453-9725-4480e388452e", - "metadata": {}, - "source": [ - "We now define our `Image` struct, which will store the RGB pixels of our\n", - "images. It also contains a method to convert this Mojo struct into a NumPy\n", - "image, which will be used for implementing a straightforward display\n", - "mechanism. We will also implement a function for loading PNG files from disk." - ] - }, - { - "cell_type": "markdown", - "id": "acd55d71", - "metadata": {}, - "source": [ - "First install the required libraries:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "53266408", - "metadata": {}, - "outputs": [], - "source": [ - "%%python\n", - "from importlib.util import find_spec\n", - "import shutil\n", - "import subprocess\n", - "\n", - "fix = \"\"\"\n", - "-------------------------------------------------------------------------\n", - "fix following the steps here:\n", - " https://github.com/modularml/mojo/issues/1085#issuecomment-1771403719\n", - "-------------------------------------------------------------------------\n", - "\"\"\"\n", - "\n", - "def install_if_missing(name: str):\n", - " if find_spec(name):\n", - " return\n", - "\n", - " print(f\"{name} not found, installing...\")\n", - " try:\n", - " if shutil.which('python3'): python = \"python3\"\n", - " elif shutil.which('python'): python = \"python\"\n", - " else: raise (\"python not on path\" + fix)\n", - " subprocess.check_call([python, \"-m\", \"pip\", \"install\", name])\n", - " except:\n", - " raise ImportError(f\"{name} not found\" + fix)\n", - "\n", - "install_if_missing(\"numpy\")\n", - "install_if_missing(\"matplotlib\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "ca218d22-0578-42af-a906-7f15a91c5bec", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "from python import Python\n", - "from python import PythonObject\n", - "from memory import UnsafePointer\n", - "\n", - "struct Image:\n", - " # reference count used to make the object efficiently copyable\n", - " var rc: UnsafePointer[Int]\n", - " # the two dimensional image is represented as a flat array\n", - " var pixels: UnsafePointer[Vec3f]\n", - " var height: Int\n", - " var width: Int\n", - "\n", - " fn __init__(out self, height: Int, width: Int):\n", - " self.height = height\n", - " self.width = width\n", - " self.pixels = UnsafePointer[Vec3f].alloc(self.height * self.width)\n", - " self.rc = UnsafePointer[Int].alloc(1)\n", - " self.rc[] = 1\n", - "\n", - " fn __copyinit__(out self, other: Self):\n", - " other._inc_rc()\n", - " self.pixels = other.pixels\n", - " self.rc = other.rc\n", - " self.height = other.height\n", - " self.width = other.width\n", - "\n", - " fn __del__(owned self):\n", - " self._dec_rc()\n", - "\n", - " fn _dec_rc(self):\n", - " if self.rc[] > 1:\n", - " self.rc[] -= 1\n", - " return\n", - " self._free()\n", - "\n", - " fn _inc_rc(self):\n", - " self.rc[] += 1\n", - "\n", - " fn _free(self):\n", - " self.rc.free()\n", - " self.pixels.free()\n", - "\n", - " @always_inline\n", - " fn set(self, row: Int, col: Int, value: Vec3f) -> None:\n", - " self.pixels[self._pos_to_index(row, col)] = value\n", - "\n", - " @always_inline\n", - " fn _pos_to_index(self, row: Int, col: Int) -> Int:\n", - " # Convert a (rol, col) position into an index in the underlying linear storage\n", - " return row * self.width + col\n", - "\n", - " def to_numpy_image(self) -> PythonObject:\n", - " var np = Python.import_module(\"numpy\")\n", - " var plt = Python.import_module(\"matplotlib.pyplot\")\n", - "\n", - " var np_image = np.zeros((self.height, self.width, 3), np.float32)\n", - "\n", - " # We use raw pointers to efficiently copy the pixels to the numpy array\n", - " var out_pointer = np_image.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.float32]()\n", - " var in_pointer = self.pixels.bitcast[Float32]()\n", - "\n", - " for row in range(self.height):\n", - " for col in range(self.width):\n", - " var index = self._pos_to_index(row, col)\n", - " for dim in range(3):\n", - " out_pointer[index * 3 + dim] = in_pointer[index * 4 + dim]\n", - "\n", - " return np_image\n", - "\n", - "\n", - "def load_image(fname: String) -> Image:\n", - " var np = Python.import_module(\"numpy\")\n", - " var plt = Python.import_module(\"matplotlib.pyplot\")\n", - "\n", - " var np_image = plt.imread(fname)\n", - " var rows = int(np_image.shape[0])\n", - " var cols = int(np_image.shape[1])\n", - " var image = Image(rows, cols)\n", - "\n", - " var in_pointer = np_image.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.float32]()\n", - " var out_pointer = image.pixels.bitcast[Float32]()\n", - "\n", - " for row in range(rows):\n", - " for col in range(cols):\n", - " var index = image._pos_to_index(row, col)\n", - " for dim in range(3):\n", - " out_pointer[index * 4 + dim] = in_pointer[index * 3 + dim]\n", - " return image\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7a7dbe6f-f35f-42c5-ad37-b7400f6262f8", - "metadata": {}, - "source": [ - "We then add a function for quickly displaying an `Image` into the notebook. Our\n", - "Python interop comes in quite handy." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "9a3a8e81-7500-439b-a8d2-8e71ceea3e8b", - "metadata": {}, - "outputs": [], - "source": [ - "def render(image: Image):\n", - " np = Python.import_module(\"numpy\")\n", - " plt = Python.import_module(\"matplotlib.pyplot\")\n", - " colors = Python.import_module(\"matplotlib.colors\")\n", - " dpi = 32\n", - " fig = plt.figure(1, [image.width // 10, image.height // 10], dpi)\n", - "\n", - " plt.imshow(image.to_numpy_image())\n", - " plt.axis(\"off\")\n", - " plt.show()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5f42f174-d7d9-4f6c-9b4e-d0d586f8f5ec", - "metadata": {}, - "source": [ - "Finally, we test all our code so far with a simple image, which is the one\n", - "rendered in the [Step 1 of the C++\n", - "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-1-write-an-image-to-the-disk)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "52005588-38e7-4ef0-affe-6d063b9bf9b7", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAFnCAYAAADjbJN9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAAGG0lEQVR4nO3ZwWoCQRRFQVv8/19+LtyMILjyTMtUbUICCWZ1uP3WzMwNAPi5+9kfAACu4nH8Zq11+ObD108/+/Z1x9/xmfb4+zt+Jv/zHr/z739/x890xf95o9+Z9XpUtnQBIPK+dM/6FABwAZYuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIWLoAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQ8bwMABFLFwAiogsAEc/LABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgYukCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJAxPMyAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiKULABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEc/LABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQGTNzJz9IQDgCixdAIiILgBEnv7cEL5KaxYBAAAAAElFTkSuQmCC" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "var image = Image(192, 256)\n", - "\n", - "for row in range(image.height):\n", - " for col in range(image.width):\n", - " image.set(\n", - " row,\n", - " col,\n", - " Vec3f(Float32(row) / image.height, Float32(col) / image.width, 0),\n", - " )\n", - "\n", - "render(image)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ef3206b4-0bbb-4108-a592-f748843e5d3d", - "metadata": {}, - "source": [ - "## Step 2: Ray tracing\n", - "\n", - "Now we'll perform ray tracing from a camera into a scene with a sphere. Before\n", - "reading the code below, we suggest you read more about how this works\n", - "conceptually from [Step 2 of the C++\n", - "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-2-the-crucial-one-ray-tracing)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "778d8339-5b19-47ee-bdc6-a08e4ec5094c", - "metadata": {}, - "source": [ - "We first define the `Material` and `Sphere` structs, which are the new data\n", - "structures we'll need." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "1c4a79de-0b65-48ac-97c9-187b74b96b9f", - "metadata": {}, - "outputs": [], - "source": [ - "from math import sqrt\n", - "\n", - "@register_passable(\"trivial\")\n", - "struct Material:\n", - " var color: Vec3f\n", - " var albedo: Vec3f\n", - " var specular_component: Float32\n", - "\n", - " fn __init__(out self, color: Vec3f, albedo: Vec3f = Vec3f(0, 0, 0),\n", - " specular_component: Float32 = 0):\n", - " self.color = color\n", - " self.albedo = albedo\n", - " self.specular_component = specular_component\n", - "\n", - "alias W = 1024\n", - "alias H = 768\n", - "alias bg_color = Vec3f(0.02, 0.02, 0.02)\n", - "var shiny_yellow = Material(Vec3f(0.95, 0.95, 0.4), Vec3f(0.7, 0.6, 0), 30.0)\n", - "var green_rubber = Material(Vec3f( 0.3, 0.7, 0.3), Vec3f(0.9, 0.1, 0), 1.0)\n", - "\n", - "\n", - "@value\n", - "@register_passable(\"trivial\")\n", - "struct Sphere(CollectionElement):\n", - " var center: Vec3f\n", - " var radius: Float32\n", - " var material: Material\n", - "\n", - " @always_inline\n", - " fn intersects(self, orig: Vec3f, dir: Vec3f, mut dist: Float32) -> Bool:\n", - " \"\"\"This method returns True if a given ray intersects this sphere.\n", - " And if it does, it writes in the `dist` parameter the distance to the\n", - " origin of the ray.\n", - " \"\"\"\n", - " var L = orig - self.center\n", - " var a = dir @ dir\n", - " var b = 2 * (dir @ L)\n", - " var c = L @ L - self.radius * self.radius\n", - " var discriminant = b * b - 4 * a * c\n", - " if discriminant < 0:\n", - " return False\n", - " if discriminant == 0:\n", - " dist = -b / 2 * a\n", - " return True\n", - " var q = -0.5 * (b + sqrt(discriminant)) if b > 0 else -0.5 * (\n", - " b - sqrt(discriminant)\n", - " )\n", - " var t0 = q / a\n", - " var t1 = c / q\n", - " if t0 > t1:\n", - " t0 = t1\n", - " if t0 < 0:\n", - " t0 = t1\n", - " if t0 < 0:\n", - " return False\n", - "\n", - " dist = t0\n", - " return True\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e5ed8e76-efc1-4905-90ea-4cad6df0c553", - "metadata": {}, - "source": [ - "We then define a `cast_ray` method, which will be used to figure out the color\n", - "of a particular pixel in the image we'll produce. It basically works by\n", - "identifying whether this ray intersects the sphere or not." - ] - }, + "cells": [ + { + "cell_type": "raw", + "id": "7a924a43", + "metadata": {}, + "source": [ + "---\n", + "title: Ray tracing in Mojo\n", + "description: Learn how to draw 3D graphics with ray-traced lighting using Mojo.\n", + "website:\n", + " open-graph:\n", + " image: /static/images/mojo-social-card.png\n", + " twitter-card:\n", + " image: /static/images/mojo-social-card.png\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "e48ca293", + "metadata": {}, + "source": [ + "[//]: # REMOVE_FOR_WEBSITE\n", + "*Copyright 2023 Modular, Inc: Licensed under the Apache License v2.0 with LLVM Exceptions.*" + ] + }, + { + "cell_type": "markdown", + "id": "214584ac", + "metadata": {}, + "source": [ + "[//]: # REMOVE_FOR_WEBSITE\n", + "# Ray tracing in Mojo" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c4edb2c9-5109-4e00-98c0-3aa92dbca7d1", + "metadata": {}, + "source": [ + "This tutorial about [ray\n", + "tracing](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)) is based on the\n", + "popular tutorial [Understandable RayTracing in\n", + "C++](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing).\n", + "The mathematical explanations are well described in that tutorial, so we'll\n", + "just point you to the appropriate sections for reference as we implement a\n", + "basic ray tracer in Mojo." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "fbd4aa63-a178-41f2-ad66-b017c1c69d2a", + "metadata": {}, + "source": [ + "## Step 1: Basic definitions\n", + "\n", + "We'll start by defining a `Vec3f` struct, which will use to represent a vector\n", + "in 3D space as well as RGB pixels. We'll use a `SIMD` representation for our\n", + "vector to enable vectorized operations. The `SIMD` type is a fixed-size vector,\n", + "and its size must be a power of 2. So we'll use a size of 4 and always pad the\n", + "underlying storage with a 0." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78a07829-05bc-4a67-8ccc-f2e1537c478c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from math import isqrt\n", + "\n", + "\n", + "@register_passable(\"trivial\")\n", + "struct Vec3f:\n", + " var data: SIMD[DType.float32, 4]\n", + "\n", + " @always_inline\n", + " fn __init__(out self, x: Float32, y: Float32, z: Float32):\n", + " self.data = SIMD[DType.float32, 4](x, y, z, 0)\n", + "\n", + " @implicit\n", + " @always_inline\n", + " fn __init__(out self, data: SIMD[DType.float32, 4]):\n", + " self.data = data\n", + "\n", + " @always_inline\n", + " @staticmethod\n", + " fn zero() -> Vec3f:\n", + " return Vec3f(0, 0, 0)\n", + "\n", + " @always_inline\n", + " fn __sub__(self, other: Vec3f) -> Vec3f:\n", + " return self.data - other.data\n", + "\n", + " @always_inline\n", + " fn __add__(self, other: Vec3f) -> Vec3f:\n", + " return self.data + other.data\n", + "\n", + " @always_inline\n", + " fn __matmul__(self, other: Vec3f) -> Float32:\n", + " return (self.data * other.data).reduce_add()\n", + "\n", + " @always_inline\n", + " fn __mul__(self, k: Float32) -> Vec3f:\n", + " return self.data * k\n", + "\n", + " @always_inline\n", + " fn __neg__(self) -> Vec3f:\n", + " return self.data * -1.0\n", + "\n", + " @always_inline\n", + " fn __getitem__(self, idx: Int) -> SIMD[DType.float32, 1]:\n", + " return self.data[idx]\n", + "\n", + " @always_inline\n", + " fn cross(self, other: Vec3f) -> Vec3f:\n", + " var self_zxy = self.data.shuffle[2, 0, 1, 3]()\n", + " var other_zxy = other.data.shuffle[2, 0, 1, 3]()\n", + " return (self_zxy * other.data - self.data * other_zxy).shuffle[\n", + " 2, 0, 1, 3\n", + " ]()\n", + "\n", + " @always_inline\n", + " fn normalize(self) -> Vec3f:\n", + " return self.data * isqrt(self @ self)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2619eb1b-103b-4453-9725-4480e388452e", + "metadata": {}, + "source": [ + "We now define our `Image` struct, which will store the RGB pixels of our\n", + "images. It also contains a method to convert this Mojo struct into a NumPy\n", + "image, which will be used for implementing a straightforward display\n", + "mechanism. We will also implement a function for loading PNG files from disk." + ] + }, + { + "cell_type": "markdown", + "id": "acd55d71", + "metadata": {}, + "source": [ + "First install the required libraries:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "53266408", + "metadata": {}, + "outputs": [], + "source": [ + "%%python\n", + "from importlib.util import find_spec\n", + "import shutil\n", + "import subprocess\n", + "\n", + "fix = \"\"\"\n", + "-------------------------------------------------------------------------\n", + "fix following the steps here:\n", + " https://github.com/modularml/mojo/issues/1085#issuecomment-1771403719\n", + "-------------------------------------------------------------------------\n", + "\"\"\"\n", + "\n", + "def install_if_missing(name: str):\n", + " if find_spec(name):\n", + " return\n", + "\n", + " print(f\"{name} not found, installing...\")\n", + " try:\n", + " if shutil.which('python3'): python = \"python3\"\n", + " elif shutil.which('python'): python = \"python\"\n", + " else: raise (\"python not on path\" + fix)\n", + " subprocess.check_call([python, \"-m\", \"pip\", \"install\", name])\n", + " except:\n", + " raise ImportError(f\"{name} not found\" + fix)\n", + "\n", + "install_if_missing(\"numpy\")\n", + "install_if_missing(\"matplotlib\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ca218d22-0578-42af-a906-7f15a91c5bec", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from python import Python\n", + "from python import PythonObject\n", + "from memory import UnsafePointer\n", + "\n", + "struct Image:\n", + " # reference count used to make the object efficiently copyable\n", + " var rc: UnsafePointer[Int]\n", + " # the two dimensional image is represented as a flat array\n", + " var pixels: UnsafePointer[Vec3f]\n", + " var height: Int\n", + " var width: Int\n", + "\n", + " fn __init__(out self, height: Int, width: Int):\n", + " self.height = height\n", + " self.width = width\n", + " self.pixels = UnsafePointer[Vec3f].alloc(self.height * self.width)\n", + " self.rc = UnsafePointer[Int].alloc(1)\n", + " self.rc[] = 1\n", + "\n", + " fn __copyinit__(out self, other: Self):\n", + " other._inc_rc()\n", + " self.pixels = other.pixels\n", + " self.rc = other.rc\n", + " self.height = other.height\n", + " self.width = other.width\n", + "\n", + " fn __del__(owned self):\n", + " self._dec_rc()\n", + "\n", + " fn _dec_rc(self):\n", + " if self.rc[] > 1:\n", + " self.rc[] -= 1\n", + " return\n", + " self._free()\n", + "\n", + " fn _inc_rc(self):\n", + " self.rc[] += 1\n", + "\n", + " fn _free(self):\n", + " self.rc.free()\n", + " self.pixels.free()\n", + "\n", + " @always_inline\n", + " fn set(self, row: Int, col: Int, value: Vec3f) -> None:\n", + " self.pixels[self._pos_to_index(row, col)] = value\n", + "\n", + " @always_inline\n", + " fn _pos_to_index(self, row: Int, col: Int) -> Int:\n", + " # Convert a (rol, col) position into an index in the underlying linear storage\n", + " return row * self.width + col\n", + "\n", + " def to_numpy_image(self) -> PythonObject:\n", + " var np = Python.import_module(\"numpy\")\n", + " var plt = Python.import_module(\"matplotlib.pyplot\")\n", + "\n", + " var np_image = np.zeros((self.height, self.width, 3), np.float32)\n", + "\n", + " # We use raw pointers to efficiently copy the pixels to the numpy array\n", + " var out_pointer = np_image.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.float32]()\n", + " var in_pointer = self.pixels.bitcast[Float32]()\n", + "\n", + " for row in range(self.height):\n", + " for col in range(self.width):\n", + " var index = self._pos_to_index(row, col)\n", + " for dim in range(3):\n", + " out_pointer[index * 3 + dim] = in_pointer[index * 4 + dim]\n", + "\n", + " return np_image\n", + "\n", + "\n", + "def load_image(fname: String) -> Image:\n", + " var np = Python.import_module(\"numpy\")\n", + " var plt = Python.import_module(\"matplotlib.pyplot\")\n", + "\n", + " var np_image = plt.imread(fname)\n", + " var rows = int(np_image.shape[0])\n", + " var cols = int(np_image.shape[1])\n", + " var image = Image(rows, cols)\n", + "\n", + " var in_pointer = np_image.__array_interface__[\"data\"][0].unsafe_get_as_pointer[DType.float32]()\n", + " var out_pointer = image.pixels.bitcast[Float32]()\n", + "\n", + " for row in range(rows):\n", + " for col in range(cols):\n", + " var index = image._pos_to_index(row, col)\n", + " for dim in range(3):\n", + " out_pointer[index * 4 + dim] = in_pointer[index * 3 + dim]\n", + " return image\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "7a7dbe6f-f35f-42c5-ad37-b7400f6262f8", + "metadata": {}, + "source": [ + "We then add a function for quickly displaying an `Image` into the notebook. Our\n", + "Python interop comes in quite handy." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9a3a8e81-7500-439b-a8d2-8e71ceea3e8b", + "metadata": {}, + "outputs": [], + "source": [ + "def render(image: Image):\n", + " np = Python.import_module(\"numpy\")\n", + " plt = Python.import_module(\"matplotlib.pyplot\")\n", + " colors = Python.import_module(\"matplotlib.colors\")\n", + " dpi = 32\n", + " fig = plt.figure(1, [image.width // 10, image.height // 10], dpi)\n", + "\n", + " plt.imshow(image.to_numpy_image())\n", + " plt.axis(\"off\")\n", + " plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5f42f174-d7d9-4f6c-9b4e-d0d586f8f5ec", + "metadata": {}, + "source": [ + "Finally, we test all our code so far with a simple image, which is the one\n", + "rendered in the [Step 1 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-1-write-an-image-to-the-disk)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "52005588-38e7-4ef0-affe-6d063b9bf9b7", + "metadata": { + "tags": [] + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 7, - "id": "beeecde1-f365-4be5-a3b1-d8a145810be1", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fn cast_ray(orig: Vec3f, dir: Vec3f, sphere: Sphere) -> Vec3f:\n", - " var dist: Float32 = 0\n", - " if not sphere.intersects(orig, dir, dist):\n", - " return bg_color\n", - "\n", - " return sphere.material.color\n" - ] + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAFnCAYAAADjbJN9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAAGG0lEQVR4nO3ZwWoCQRRFQVv8/19+LtyMILjyTMtUbUICCWZ1uP3WzMwNAPi5+9kfAACu4nH8Zq11+ObD108/+/Z1x9/xmfb4+zt+Jv/zHr/z739/x890xf95o9+Z9XpUtnQBIPK+dM/6FABwAZYuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwAR0QWAiJsuAEQsXQCIWLoAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQ8bwMABFLFwAiogsAEc/LABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEBFdAIi46QJAxNIFgIjoAkDE8zIARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBERBcAIm66ABCxdAEgYukCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHPywAQsXQBICK6ABARXQCIuOkCQMTSBYCI6AJAxPMyAEQsXQCIiC4ARDwvA0DE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQER0ASDipgsAEUsXACKiCwARz8sAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiOgCQMTzMgBELF0AiIguAEREFwAibroAELF0ASAiugAQEV0AiLjpAkDE0gWAiKULABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEdEFgIibLgBELF0AiIguAEQ8LwNAxNIFgIjoAkBEdAEg4qYLABFLFwAiogsAEc/LABCxdAEgIroAEPG8DAARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQMTSBYCI6AJARHQBIOKmCwARSxcAIqILABHRBYCImy4ARCxdAIiILgBEPC8DQGTNzJz9IQDgCixdAIiILgBEnv7cEL5KaxYBAAAAAElFTkSuQmCC" + }, + "metadata": {}, + "output_type": "display_data" }, { - "attachments": {}, - "cell_type": "markdown", - "id": "1baec786-664c-4fea-8be1-c2d5f25924e7", - "metadata": {}, - "source": [ - "Lastly, we parallelize the ray tracing for every pixel row-wise." - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "var image = Image(192, 256)\n", + "\n", + "for row in range(image.height):\n", + " for col in range(image.width):\n", + " image.set(\n", + " row,\n", + " col,\n", + " Vec3f(Float32(row) / image.height, Float32(col) / image.width, 0),\n", + " )\n", + "\n", + "render(image)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "ef3206b4-0bbb-4108-a592-f748843e5d3d", + "metadata": {}, + "source": [ + "## Step 2: Ray tracing\n", + "\n", + "Now we'll perform ray tracing from a camera into a scene with a sphere. Before\n", + "reading the code below, we suggest you read more about how this works\n", + "conceptually from [Step 2 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-2-the-crucial-one-ray-tracing)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "778d8339-5b19-47ee-bdc6-a08e4ec5094c", + "metadata": {}, + "source": [ + "We first define the `Material` and `Sphere` structs, which are the new data\n", + "structures we'll need." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c4a79de-0b65-48ac-97c9-187b74b96b9f", + "metadata": {}, + "outputs": [], + "source": [ + "from math import sqrt\n", + "\n", + "@register_passable(\"trivial\")\n", + "struct Material:\n", + " var color: Vec3f\n", + " var albedo: Vec3f\n", + " var specular_component: Float32\n", + "\n", + " @implicit\n", + " fn __init__(out self, color: Vec3f, albedo: Vec3f = Vec3f(0, 0, 0),\n", + " specular_component: Float32 = 0):\n", + " self.color = color\n", + " self.albedo = albedo\n", + " self.specular_component = specular_component\n", + "\n", + "alias W = 1024\n", + "alias H = 768\n", + "alias bg_color = Vec3f(0.02, 0.02, 0.02)\n", + "var shiny_yellow = Material(Vec3f(0.95, 0.95, 0.4), Vec3f(0.7, 0.6, 0), 30.0)\n", + "var green_rubber = Material(Vec3f( 0.3, 0.7, 0.3), Vec3f(0.9, 0.1, 0), 1.0)\n", + "\n", + "\n", + "@value\n", + "@register_passable(\"trivial\")\n", + "struct Sphere(CollectionElement):\n", + " var center: Vec3f\n", + " var radius: Float32\n", + " var material: Material\n", + "\n", + " @always_inline\n", + " fn intersects(self, orig: Vec3f, dir: Vec3f, mut dist: Float32) -> Bool:\n", + " \"\"\"This method returns True if a given ray intersects this sphere.\n", + " And if it does, it writes in the `dist` parameter the distance to the\n", + " origin of the ray.\n", + " \"\"\"\n", + " var L = orig - self.center\n", + " var a = dir @ dir\n", + " var b = 2 * (dir @ L)\n", + " var c = L @ L - self.radius * self.radius\n", + " var discriminant = b * b - 4 * a * c\n", + " if discriminant < 0:\n", + " return False\n", + " if discriminant == 0:\n", + " dist = -b / 2 * a\n", + " return True\n", + " var q = -0.5 * (b + sqrt(discriminant)) if b > 0 else -0.5 * (\n", + " b - sqrt(discriminant)\n", + " )\n", + " var t0 = q / a\n", + " var t1 = c / q\n", + " if t0 > t1:\n", + " t0 = t1\n", + " if t0 < 0:\n", + " t0 = t1\n", + " if t0 < 0:\n", + " return False\n", + "\n", + " dist = t0\n", + " return True\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e5ed8e76-efc1-4905-90ea-4cad6df0c553", + "metadata": {}, + "source": [ + "We then define a `cast_ray` method, which will be used to figure out the color\n", + "of a particular pixel in the image we'll produce. It basically works by\n", + "identifying whether this ray intersects the sphere or not." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "beeecde1-f365-4be5-a3b1-d8a145810be1", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "fn cast_ray(orig: Vec3f, dir: Vec3f, sphere: Sphere) -> Vec3f:\n", + " var dist: Float32 = 0\n", + " if not sphere.intersects(orig, dir, dist):\n", + " return bg_color\n", + "\n", + " return sphere.material.color\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "1baec786-664c-4fea-8be1-c2d5f25924e7", + "metadata": {}, + "source": [ + "Lastly, we parallelize the ray tracing for every pixel row-wise." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "80c81118-acf9-4786-9a3a-694fe7f2d08a", + "metadata": { + "tags": [] + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 8, - "id": "80c81118-acf9-4786-9a3a-694fe7f2d08a", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAABKUUlEQVR4nOzde5CddX3H8e85u3t2N9lkc11CCESEXEgD2CRY1KJStNrRabWidAZGOy3qH62O1dE6dRCYsVXqH7ZTW+8WrdVpB2+1DkWQKVQCbeQypSAkQAhJSHYJuWw2ezm7e57+EQ3BAiWw333O7nm9ZpjZk7MhnznJHzl5n9/zVIqiKAIAAAAAAACAKVUtewAAAAAAAADAbCTGAgAAAAAAACRoP/5BrVYrawcAAAAAAADAjFev14997WQsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEA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AABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAP63fTs4AQAEYCCG+w9dpzgESSbo/ygAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAIDA2bbXIwAAAAAAAAB+4xkLAAAAAAAAEBBjAQAAAAAAAAIXPgf3yqAAlNMAAAAASUVORK5CYII=" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "from math import tan, acos\n", - "from algorithm import parallelize\n", - "\n", - "\n", - "fn create_image_with_sphere(sphere: Sphere, height: Int, width: Int) -> Image:\n", - " var image = Image(height, width)\n", - "\n", - " @parameter\n", - " fn _process_row(row: Int):\n", - " var y = -((Float32(2.0) * row + 1) / height - 1)\n", - " for col in range(width):\n", - " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", - " var dir = Vec3f(x, y, -1).normalize()\n", - " image.set(row, col, cast_ray(Vec3f.zero(), dir, sphere))\n", - "\n", - " parallelize[_process_row](height)\n", - "\n", - " return image\n", - "\n", - "\n", - "render(\n", - " create_image_with_sphere(Sphere(Vec3f(-3, 0, -16), 2, shiny_yellow), H, W)\n", - ")\n" - ] + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAABKUUlEQVR4nOzde5CddX3H8e85u3t2N9lkc11CCESEXEgD2CRY1KJStNrRabWidAZGOy3qH62O1dE6dRCYsVXqH7ZTW+8WrdVpB2+1DkWQKVQCbeQypSAkQAhJSHYJuWw2ezm7e57+EQ3BAiWw333O7nm9ZpjZk7MhnznJHzl5n9/zVIqiKAIAAAAAAACAKVUtewAAAAAAAADAbCTGAgAAAAAAACRoP/5BrVYrawcAAAAAAADAjFev14997WQsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEA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AABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAAAAAAAAIIEYCwAAAAAAAJBAjAUAAAAAAABIIMYCAAAAAAAAJBBjAQAAAAAAABKIsQAAAAAAAAAJxFgAAAAAAACABGIsAAAAAAAAQAIxFgAAAAAAACCBGAsAAAAAAACQQIwFAAAAAAAASCDGAgAAAAAAACQQYwEAAAAAAAASiLEAAAAAAAAACcRYAAAAAAAAgARiLAAAAAAAAEACMRYAAAAAAAAggRgLAAAAAAAAkECMBQAAAAAAAEggxgIAAAAAAAAkEGMBAAAAAAAAEoixAAAAAAAAAAnEWAAAAAAAAIAEYiwAAAAAAABAAjEWAP63fTs4AQAEYCCG+w9dpzgESSbo/ygAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAIDA2bbXIwAAAAAAAAB+4xkLAAAAAAAAEBBjAQAAAAAAAAIXPgf3yqAAlNMAAAAASUVORK5CYII=" + }, + "metadata": {}, + "output_type": "display_data" }, { - "attachments": {}, - "cell_type": "markdown", - "id": "ee9bbf4c-91ad-4787-a332-b509fdc8d512", - "metadata": {}, - "source": [ - "## Step 3: More spheres\n", - "\n", - "This section corresponds to the [Step 3 of the C++\n", - "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-3-add-more-spheres).\n", - "\n", - "We include here all the necessary changes:\n", - "\n", - "- We add 3 more spheres to the scene, 2 of them being of green rubber material.\n", - "- When we intersect the ray with the sphere, we render the color of the closest\n", - " sphere." - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "from math import tan, acos\n", + "from algorithm import parallelize\n", + "\n", + "\n", + "fn create_image_with_sphere(sphere: Sphere, height: Int, width: Int) -> Image:\n", + " var image = Image(height, width)\n", + "\n", + " @parameter\n", + " fn _process_row(row: Int):\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", + " for col in range(width):\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", + " var dir = Vec3f(x, y, -1).normalize()\n", + " image.set(row, col, cast_ray(Vec3f.zero(), dir, sphere))\n", + "\n", + " parallelize[_process_row](height)\n", + "\n", + " return image\n", + "\n", + "\n", + "render(\n", + " create_image_with_sphere(Sphere(Vec3f(-3, 0, -16), 2, shiny_yellow), H, W)\n", + ")\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "ee9bbf4c-91ad-4787-a332-b509fdc8d512", + "metadata": {}, + "source": [ + "## Step 3: More spheres\n", + "\n", + "This section corresponds to the [Step 3 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-3-add-more-spheres).\n", + "\n", + "We include here all the necessary changes:\n", + "\n", + "- We add 3 more spheres to the scene, 2 of them being of green rubber material.\n", + "- When we intersect the ray with the sphere, we render the color of the closest\n", + " sphere." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b4774ada-3cef-4ce3-a573-3fd721fe41b0", + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 9, - "id": "b4774ada-3cef-4ce3-a573-3fd721fe41b0", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAACKqklEQVR4nOzdd5hdd30n/ve5d+70plGXbMm2uptcMN00h5YQAtmwIUsNkJBNNgUSkk3IhmUT8sumJ8+PFNLYpW1Ywoaw+2Npxiam2oANuFtuki1ZvY6m3vv7Q0a2cZWlM+fOzOv1PHqYe+femTdn7sx4zvt8vt+i1Wq1AgAAAAAAAMApVas6AAAAAAAAAMBcpIwFAAAAAAAAKEHHg290dnZWlQMAAAAAAABg1puYmDj+tslYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAA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nszHPnZ7PvWpuzM11aw6DsCMabVaueKKe/ORj9yWPXvGqo4DAJwiylgAAAAAAACAElimGAAAAACgQocPT+bw4cmMj09XHQUAOMWUsQAAAAAAFXr/+2/Ohz50S+69d7TqKADAKaaMBQAAANrO6OhUbrxxf5Yt68nKlX1JiqojAZxye/eOZceO0dx0077ccsuBquMAACUoWq1W63s3Ojs7q8wCAAAAkCTp6CgyMNCZV7zijPzBHzwjRaGMBeaeD3/41rzrXVdndHQqY2OWKAaAuWJiYuL42yZjAQAAgLYzNdXKvn3jOXx4Kq1WoosF5qLx8ens3TtedQwAoES1qgMAAAAAAAAAzEXKWAAAAKBt3Xbbgfzt396Yb3xjV9VRAE6ZrVsP5+/+7sZcddX2qqMAACWzZywAAADQ9t7xjs35j//xoqpjAJwSn//8trz2tZ/PxESz6igAQAkevGesyVgAAAAAAACAEnRUHQAAAAAAYD5otVqZnj7274H1CgGAuUwZCwAAAAAwA+6550h+93e/mS1bDmZqyhLFADAfKGMBAACAtjc2Np39+8fT29uRzs561XEATkiz2crhw5O5997RfPaz27J373jVkQCAGVK0Wg8siNHZ2VllFgAAAIBHtGRJT5Yt682v/doFeclLVlUdB+CE7N8/nre97cu56aZ92bLlYKanrVEMAHPZxMTE8bdNxgIAAABtb+fOo9m582j27TNNBsw+09Ot3Hrr/txyy4GqowAAM6xWdQAAAAAAAACAuUgZCwAAAMwat9xyIF/60vYcOGBCFmh/zWYr3/3u3nzlK/flyJGpquMAABWwZywAAAAwazQatfT1deSDH7wsz3jGsqrjADymycnp/ORPXpHLL78nExPTadkqFgDmBXvGAgAAALPS5GQz4+PTaTY1GkA7a+Xqq3flhhv25c47D2V8fLrqQABARZSxAAAAAACn2Mc+tiV/+7c3VR0DAKiYPWMBAACAWWVyspkPfODW/Pmffyd7945VHQfgEVmSGABIlLEAAADALDM11cr//J9b8r733ZD9+yce/wkAM6jVaqXZrDoFANAuLFMMAAAAUKpWkqLqELNcWcfwRD9u2Y+fD9rlGJb3tfnc57blox/dkmuv3VPKxwcAZhdlLAAAADArNZutjI5O5ejRqXR311MU7Vp6tWuu2aSsY3iiH7fsx88H7XIM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+plce+3uakPNMutftz5n/siZD9zhZcpjaSXT49P5xnu+kX037Ks6TZJje26fdlpf/uVfXppVqwaqjgNz0vj4dF772s/nC1+4Jw+czQQAaE8mYwEAYA677LKVueiiRUmSnp6OLFnSk6LQbpWlKIp87/B2d3fk9a9fnxe+cGWSZOvWI/nYx27P1PeWWOUhBtcMZvFFizO8cVgByxNXHNtPduXzV2bknJEkyfi+8dx75b1pTlbzvdZqJQcOTOR977shmzYtyKtetSadprvhpH3ta/fli188tiz51FQzd9xxUBELAMw6ylgAAJhDiiJ54QtPy0/91NlVR5mXurrqecMbNhy//eUv78i//MudythHMbR2KGtfvbbqGMxCRb3IyhesPH774JaD2fGVHZWVsUly8OBk/vIvb8hTnrI4L3/5GWk0alYkgCfpewv5ffWr9+X3fu9bFacBADg5likGAIBZ7vzzR/KWt2xKrXbshP/FFy/O+vXD1YYiSbJz59FceeW9+fKXd+S///dbqo7TNvpX9Wf1y1an//T+DHutcgpMHprM7mt3Z/8t+3P3p+5OKpycW7iwO8973oo8/elL88Y3brj/Z3MrStmT5RjOJ9/85q78wz/clOuv35frrttTdRwAgBNmmWIAAJgDiuLYJOaZZw7mx398bTo6alVH4vssWdKTV71qTYoi+djHbj8+6TMx0cz09DxcZ/H+5WV7l/ZmxXNXpOY1yynSGGhk+aXLU++u557L70lzsplWRd9je/aM5Z/+6fZMTjbz4z9+bLniRsNr/eQpYueyVquV8fHp40sQb9lyMP/jf2xJszkPf1cCAHOOyVgAAJilzjprML/zO5dk1aqBbNw4bF/YNrZz59HceuuBJK20Wsmf/Mm3c8UV91Yda8b1LOnJxp/cmJ6lPRlYNaBb4ZSbODiRI9uOZPtV27P101srzbJoUXfWrx/Kq161Jq9//YbHfwLMY7t3H82v//rXsmPH0STHLmq4+eb91YYCADgJJmMBAGAWq9WKLFzYldWrB/L0py/L0JCLKtvdkiU9WbKkJ8mx6Z///b/vyk037cveveOZmJjb+8k2Bhqp3T8V2LOkJws2LUhjoFFxKuaqzsHOdJ7dmcPbDqdrpCtTR6YyPT5dSZbdu8eye/dYzj57JNu3H7n/3iIjI13p6qpXkgnaTavVyt6949m69XC+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AAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABUzVdV03PQIAAAAAAABgt3EyFgAAAAAAAKAAMRYAAAAAAACggP8fjymaWYXEdVMAAAAASUVORK5CYII=" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "from algorithm import parallelize\n", - "from utils.numerics import inf\n", - "from collections import List\n", - "\n", - "\n", - "fn scene_intersect(\n", - " orig: Vec3f,\n", - " dir: Vec3f,\n", - " spheres: List[Sphere],\n", - " background: Material,\n", - ") -> Material:\n", - " var spheres_dist = inf[DType.float32]()\n", - " var material = background\n", - "\n", - " for i in range(spheres.size):\n", - " var dist = inf[DType.float32]()\n", - " if spheres[i].intersects(orig, dir, dist) and dist < spheres_dist:\n", - " spheres_dist = dist\n", - " material = spheres[i].material\n", - "\n", - " return material\n", - "\n", - "\n", - "fn cast_ray(\n", - " orig: Vec3f, dir: Vec3f, spheres: List[Sphere]\n", - ") -> Material:\n", - " var background = Material(Vec3f(0.02, 0.02, 0.02))\n", - " return scene_intersect(orig, dir, spheres, background)\n", - "\n", - "\n", - "fn create_image_with_spheres(\n", - " spheres: List[Sphere], height: Int, width: Int\n", - ") -> Image:\n", - " var image = Image(height, width)\n", - "\n", - " @parameter\n", - " fn _process_row(row: Int):\n", - " var y = -((Float32(2.0) * row + 1) / height - 1)\n", - " for col in range(width):\n", - " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", - " var dir = Vec3f(x, y, -1).normalize()\n", - " image.set(row, col, cast_ray(Vec3f.zero(), dir, spheres).color)\n", - "\n", - " parallelize[_process_row](height)\n", - "\n", - " return image\n", - "\n", - "var spheres = List[Sphere]()\n", - "spheres.append(Sphere(Vec3f(-3, 0, -16), 2, shiny_yellow))\n", - "spheres.append(Sphere(Vec3f(-1.0, -1.5, -12), 1.8, green_rubber))\n", - "spheres.append(Sphere(Vec3f( 1.5, -0.5, -18), 3, green_rubber))\n", - "spheres.append(Sphere(Vec3f( 7, 5, -18), 4, shiny_yellow))\n", - "\n", - "render(create_image_with_spheres(spheres, H, W))\n" - ] + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAACKqklEQVR4nOzdd5hdd30n/ve5d+70plGXbMm2uptcMN00h5YQAtmwIUsNkJBNNgUSkk3IhmUT8sumJ8+PFNLYpW1Ywoaw+2Npxiam2oANuFtuki1ZvY6m3vv7Q0a2cZWlM+fOzOv1PHqYe+femTdn7sx4zvt8vt+i1Wq1AgAAAAAAAMApVas6AAAAAAAAAMBcpIwFAAAAAAAAKEHHg290dnZWlQMAAAAAAABg1puYmDj+tslYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAAAAAASqCMBQAAAAAAACiBMhYAAAAAAACgBMpYAAAAAAAAgBIoYwEAAAAAAABKoIwFAAAAAAAAKIEyFgAAAAAAAKAEylgAAAAAAACAEihjAQAAAAAAAEqgjAUAAAAAAAAogTIWAAAAAAAAoATKWAAAAAAAAIASKGMBAAAA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nszHPnZ7PvWpuzM11aw6DsCMabVaueKKe/ORj9yWPXvGqo4DAJwiylgAAAAAAACAElimGAAAAACgQocPT+bw4cmMj09XHQUAOMWUsQAAAAAAFXr/+2/Ohz50S+69d7TqKADAKaaMBQAAANrO6OhUbrxxf5Yt68nKlX1JiqojAZxye/eOZceO0dx0077ccsuBquMAACUoWq1W63s3Ojs7q8wCAAAAkCTp6CgyMNCZV7zijPzBHzwjRaGMBeaeD3/41rzrXVdndHQqY2OWKAaAuWJiYuL42yZjAQAAgLYzNdXKvn3jOXx4Kq1WoosF5qLx8ens3TtedQwAoES1qgMAAAAAAAAAzEXKWAAAAKBt3Xbbgfzt396Yb3xjV9VRAE6ZrVsP5+/+7sZcddX2qqMAACWzZywAAADQ9t7xjs35j//xoqpjAJwSn//8trz2tZ/PxESz6igAQAkevGesyVgAAAAAAACAEnRUHQAAAAAAYD5otVqZnj7274H1CgGAuUwZCwAAAAAwA+6550h+93e/mS1bDmZqyhLFADAfKGMBAACAtjc2Np39+8fT29uRzs561XEATkiz2crhw5O5997RfPaz27J373jVkQCAGVK0Wg8siNHZ2VllFgAAAIBHtGRJT5Yt682v/doFeclLVlUdB+CE7N8/nre97cu56aZ92bLlYKanrVEMAHPZxMTE8bdNxgIAAABtb+fOo9m582j27TNNBsw+09Ot3Hrr/txyy4GqowAAM6xWdQAAAAAAAACAuUgZCwAAAMwat9xyIF/60vYcOGBCFmh/zWYr3/3u3nzlK/flyJGpquMAABWwZywAAAAwazQatfT1deSDH7wsz3jGsqrjADymycnp/ORPXpHLL78nExPTadkqFgDmBXvGAgAAALPS5GQz4+PTaTY1GkA7a+Xqq3flhhv25c47D2V8fLrqQABARZSxAAAAAACn2Mc+tiV/+7c3VR0DAKiYPWMBAACAWWVyspkPfODW/Pmffyd7945VHQfgEVmSGABIlLEAAADALDM11cr//J9b8r733ZD9+yce/wkAM6jVaqXZrDoFANAuLFMMAAAAUKpWkqLqELNcWcfwRD9u2Y+fD9rlGJb3tfnc57blox/dkmuv3VPKxwcAZhdlLAAAADArNZutjI5O5ejRqXR311MU7Vp6tWuu2aSsY3iiH7fsx88H7XIM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+plce+3uakPNMutftz5n/siZD9zhZcpjaSXT49P5xnu+kX037Ks6TZJje26fdlpf/uVfXppVqwaqjgNz0vj4dF772s/nC1+4Jw+czQQAaE8mYwEAYA677LKVueiiRUmSnp6OLFnSk6LQbpWlKIp87/B2d3fk9a9fnxe+cGWSZOvWI/nYx27P1PeWWOUhBtcMZvFFizO8cVgByxNXHNtPduXzV2bknJEkyfi+8dx75b1pTlbzvdZqJQcOTOR977shmzYtyKtetSadprvhpH3ta/fli188tiz51FQzd9xxUBELAMw6ylgAAJhDiiJ54QtPy0/91NlVR5mXurrqecMbNhy//eUv78i//MudythHMbR2KGtfvbbqGMxCRb3IyhesPH774JaD2fGVHZWVsUly8OBk/vIvb8hTnrI4L3/5GWk0alYkgCfpewv5ffWr9+X3fu9bFacBADg5likGAIBZ7vzzR/KWt2xKrXbshP/FFy/O+vXD1YYiSbJz59FceeW9+fKXd+S///dbqo7TNvpX9Wf1y1an//T+DHutcgpMHprM7mt3Z/8t+3P3p+5OKpycW7iwO8973oo8/elL88Y3brj/Z3MrStmT5RjOJ9/85q78wz/clOuv35frrttTdRwAgBNmmWIAAJgDiuLYJOaZZw7mx398bTo6alVH4vssWdKTV71qTYoi+djHbj8+6TMx0cz09DxcZ/H+5WV7l/ZmxXNXpOY1yynSGGhk+aXLU++u557L70lzsplWRd9je/aM5Z/+6fZMTjbz4z9+bLniRsNr/eQpYueyVquV8fHp40sQb9lyMP/jf2xJszkPf1cCAHOOyVgAAJilzjprML/zO5dk1aqBbNw4bF/YNrZz59HceuuBJK20Wsmf/Mm3c8UV91Yda8b1LOnJxp/cmJ6lPRlYNaBb4ZSbODiRI9uOZPtV27P101srzbJoUXfWrx/Kq161Jq9//YbHfwLMY7t3H82v//rXsmPH0STHLmq4+eb91YYCADgJJmMBAGAWq9WKLFzYldWrB/L0py/L0JCLKtvdkiU9WbKkJ8mx6Z///b/vyk037cveveOZmJjb+8k2Bhqp3T8V2LOkJws2LUhjoFFxKuaqzsHOdJ7dmcPbDqdrpCtTR6YyPT5dSZbdu8eye/dYzj57JNu3H7n/3iIjI13p6qpXkgnaTavVyt6949m69XC+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AAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABYixAAAAAAAAAAWIsQAAAAAAAAAFiLEAAAAAAAAABUzVdV03PQIAAAAAAABgt3EyFgAAAAAAAKAAMRYAAAAAAACggP8fjymaWYXEdVMAAAAASUVORK5CYII=" + }, + "metadata": {}, + "output_type": "display_data" }, { - "attachments": {}, - "cell_type": "markdown", - "id": "f53abdf2", - "metadata": {}, - "source": [ - "## Step 4: Add lighting\n", - "\n", - "This section corresponds to the [Step 4 of the C++\n", - "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-4-lighting).\n", - "Please read that section for an explanation of the trick used to estimate the\n", - "light intensity of pixel based on the angle of intersection between each ray\n", - "and the spheres. The changes are minimal and are primarily about handling this\n", - "intersection angle." - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "from algorithm import parallelize\n", + "from utils.numerics import inf\n", + "from collections import List\n", + "\n", + "\n", + "fn scene_intersect(\n", + " orig: Vec3f,\n", + " dir: Vec3f,\n", + " spheres: List[Sphere],\n", + " background: Material,\n", + ") -> Material:\n", + " var spheres_dist = inf[DType.float32]()\n", + " var material = background\n", + "\n", + " for i in range(spheres.size):\n", + " var dist = inf[DType.float32]()\n", + " if spheres[i].intersects(orig, dir, dist) and dist < spheres_dist:\n", + " spheres_dist = dist\n", + " material = spheres[i].material\n", + "\n", + " return material\n", + "\n", + "\n", + "fn cast_ray(\n", + " orig: Vec3f, dir: Vec3f, spheres: List[Sphere]\n", + ") -> Material:\n", + " var background = Material(Vec3f(0.02, 0.02, 0.02))\n", + " return scene_intersect(orig, dir, spheres, background)\n", + "\n", + "\n", + "fn create_image_with_spheres(\n", + " spheres: List[Sphere], height: Int, width: Int\n", + ") -> Image:\n", + " var image = Image(height, width)\n", + "\n", + " @parameter\n", + " fn _process_row(row: Int):\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", + " for col in range(width):\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", + " var dir = Vec3f(x, y, -1).normalize()\n", + " image.set(row, col, cast_ray(Vec3f.zero(), dir, spheres).color)\n", + "\n", + " parallelize[_process_row](height)\n", + "\n", + " return image\n", + "\n", + "var spheres = List[Sphere]()\n", + "spheres.append(Sphere(Vec3f(-3, 0, -16), 2, shiny_yellow))\n", + "spheres.append(Sphere(Vec3f(-1.0, -1.5, -12), 1.8, green_rubber))\n", + "spheres.append(Sphere(Vec3f( 1.5, -0.5, -18), 3, green_rubber))\n", + "spheres.append(Sphere(Vec3f( 7, 5, -18), 4, shiny_yellow))\n", + "\n", + "render(create_image_with_spheres(spheres, H, W))\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f53abdf2", + "metadata": {}, + "source": [ + "## Step 4: Add lighting\n", + "\n", + "This section corresponds to the [Step 4 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-4-lighting).\n", + "Please read that section for an explanation of the trick used to estimate the\n", + "light intensity of pixel based on the angle of intersection between each ray\n", + "and the spheres. The changes are minimal and are primarily about handling this\n", + "intersection angle." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "3ed5bc7c-f335-48c4-abf8-31c75d6e79ad", + "metadata": {}, + "outputs": [], + "source": [ + "@value\n", + "@register_passable(\"trivial\")\n", + "struct Light(CollectionElement):\n", + " var position: Vec3f\n", + " var intensity: Float32" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "8b99f641", + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 10, - "id": "3ed5bc7c-f335-48c4-abf8-31c75d6e79ad", - "metadata": {}, - "outputs": [], - "source": [ - "@value\n", - "@register_passable(\"trivial\")\n", - "struct Light(CollectionElement):\n", - " var position: Vec3f\n", - " var intensity: Float32" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).\n" + ] }, { - "cell_type": "code", - "execution_count": 11, - "id": "8b99f641", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd7a8syX0f9uqZOQ/3aR/uPnKXu3wQRVOk+KDYBkU7kWXHD3IcGVESGIkTJAaSGAaSF3l4nX8pSIDkRWJbQQLDEQwYgS1bgBNHpmSLkiiKS2p3ubv33jOdF33qdFV1dU/PnOl7zr37+QAHM1NdXV0zd2ZI7Hd+VU3btm0AAAAAAAAA4KhWNz0BAAAAAAAAgOeRMBYAAAAAAABgAZv0wenp6U3NAwAAAAAAAOCZ9+jRo6v7KmMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAA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AACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAADw/2/fDk4AAAEYiOH+Q9cpDkGSCfo/CgAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmfbXo8AAAAAAAAA+I1nLAAAAAAAAEBAjAUAAAAAAAAIXNpczcumkMt4AAAAAElFTkSuQmCC" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "fn scene_intersect(\n", - " orig: Vec3f,\n", - " dir: Vec3f,\n", - " spheres: List[Sphere],\n", - " mut material: Material,\n", - " mut hit: Vec3f,\n", - " mut N: Vec3f,\n", - ") -> Bool:\n", - " var spheres_dist = inf[DType.float32]()\n", - "\n", - " for i in range(0, spheres.size):\n", - " var dist: Float32 = 0\n", - " if spheres[i].intersects(orig, dir, dist) and dist < spheres_dist:\n", - " spheres_dist = dist\n", - " hit = orig + dir * dist\n", - " N = (hit - spheres[i].center).normalize()\n", - " material = spheres[i].material\n", - "\n", - " return (spheres_dist != inf[DType.float32]())\n", - "\n", - "\n", - "fn cast_ray(\n", - " orig: Vec3f,\n", - " dir: Vec3f,\n", - " spheres: List[Sphere],\n", - " lights: List[Light],\n", - ") -> Material:\n", - " var point = Vec3f.zero()\n", - " var material = Material(Vec3f.zero())\n", - " var N = Vec3f.zero()\n", - " if not scene_intersect(orig, dir, spheres, material, point, N):\n", - " return bg_color\n", - "\n", - " var diffuse_light_intensity: Float32 = 0\n", - " for i in range(lights.size):\n", - " var light_dir = (lights[i].position - point).normalize()\n", - " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", - "\n", - " return material.color * diffuse_light_intensity\n", - "\n", - "\n", - "fn create_image_with_spheres_and_lights(\n", - " spheres: List[Sphere],\n", - " lights: List[Light],\n", - " height: Int,\n", - " width: Int,\n", - ") -> Image:\n", - " var image = Image(height, width)\n", - "\n", - " @parameter\n", - " fn _process_row(row: Int):\n", - " var y = -((Float32(2.0) * row + 1) / height - 1)\n", - " for col in range(width):\n", - " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", - " var dir = Vec3f(x, y, -1).normalize()\n", - " image.set(\n", - " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights).color\n", - " )\n", - "\n", - " parallelize[_process_row](height)\n", - "\n", - " return image\n", - "\n", - "\n", - "var lights = List[Light]()\n", - "lights.append(Light(Vec3f(-20, 20, 20), 1.0))\n", - "lights.append(Light(Vec3f(20, -20, 20), 0.5))\n", - "\n", - "render(create_image_with_spheres_and_lights(spheres, lights, H, W))\n" - ] + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd7a8syX0f9uqZOQ/3aR/uPnKXu3wQRVOk+KDYBkU7kWXHD3IcGVESGIkTJAaSGAaSF3l4nX8pSIDkRWJbQQLDEQwYgS1bgBNHpmSLkiiKS2p3ubv33jOdF33qdFV1dU/PnOl7zr37+QAHM1NdXV0zd2ZI7Hd+VU3btm0AAAAAAAAA4KhWNz0BAAAAAAAAgOeRMBYAAAAAAABgAZv0wenp6U3NAwAAAAAAAOCZ9+jRo6v7KmMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAAABYgjAUAAAAAAABYgDAWAAAAAAAAYAHCWAAAAAAAAIAFCGMBAAAAAAAAFiCMBQAAAAAAAFiAMBYAAAAAAABgAcJYAAAAAAAAgAUIYwEAAAAAAAAWIIwFAAAAAAAAWIAwFgAAAAAAAGABwlgAAAAAAACABQhjAQAAAAAA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AACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAAAAAABXIIwFAAAAAAAAuAJhLAAAAAAAAMAVCGMBAAAAAAAArkAYCwAAAAAAAHAFwlgAAAAAAACAKxDGAgAAAAAAAFyBMBYAAAAAAADgCoSxAAAAAAAAAFcgjAUAAAAAAAC4AmEsAAAAAAAAwBUIYwEAAAAAAACuQBgLAAAAAAAAcAXCWAAAAAAAAIArEMYCAAAAAAAAXIEwFgAAAAAAAOAKhLEAAAAAAAAAVyCMBQAAAAAAALgCYSwAAAAAAADAFQhjAQAAAAAAAK5AGAsAAAAAAABwBcJYAAAAAAAAgCsQxgIAAAAAAABcgTAWAAAAAAAA4AqEsQAAAADw/2/fDk4AAAEYiOH+Q9cpDkGSCfo/CgAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmfbXo8AAAAAAAAA+I1nLAAAAAAAAEBAjAUAAAAAAAAIXNpczcumkMt4AAAAAElFTkSuQmCC" + }, + "metadata": {}, + "output_type": "display_data" }, { - "attachments": {}, - "cell_type": "markdown", - "id": "78043756-0ce9-455f-9e49-fb75268d4478", - "metadata": {}, - "source": [ - "## Step 5: Add specular lighting\n", - "\n", - "This section corresponds to the [Step 5 of the C++\n", - "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-5-specular-lighting).\n", - "The changes to the code are quite minimal, but the rendered picture looks much\n", - "more realistic!" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "fn scene_intersect(\n", + " orig: Vec3f,\n", + " dir: Vec3f,\n", + " spheres: List[Sphere],\n", + " mut material: Material,\n", + " mut hit: Vec3f,\n", + " mut N: Vec3f,\n", + ") -> Bool:\n", + " var spheres_dist = inf[DType.float32]()\n", + "\n", + " for i in range(0, spheres.size):\n", + " var dist: Float32 = 0\n", + " if spheres[i].intersects(orig, dir, dist) and dist < spheres_dist:\n", + " spheres_dist = dist\n", + " hit = orig + dir * dist\n", + " N = (hit - spheres[i].center).normalize()\n", + " material = spheres[i].material\n", + "\n", + " return (spheres_dist != inf[DType.float32]())\n", + "\n", + "\n", + "fn cast_ray(\n", + " orig: Vec3f,\n", + " dir: Vec3f,\n", + " spheres: List[Sphere],\n", + " lights: List[Light],\n", + ") -> Material:\n", + " var point = Vec3f.zero()\n", + " var material = Material(Vec3f.zero())\n", + " var N = Vec3f.zero()\n", + " if not scene_intersect(orig, dir, spheres, material, point, N):\n", + " return bg_color\n", + "\n", + " var diffuse_light_intensity: Float32 = 0\n", + " for i in range(lights.size):\n", + " var light_dir = (lights[i].position - point).normalize()\n", + " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", + "\n", + " return material.color * diffuse_light_intensity\n", + "\n", + "\n", + "fn create_image_with_spheres_and_lights(\n", + " spheres: List[Sphere],\n", + " lights: List[Light],\n", + " height: Int,\n", + " width: Int,\n", + ") -> Image:\n", + " var image = Image(height, width)\n", + "\n", + " @parameter\n", + " fn _process_row(row: Int):\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", + " for col in range(width):\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", + " var dir = Vec3f(x, y, -1).normalize()\n", + " image.set(\n", + " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights).color\n", + " )\n", + "\n", + " parallelize[_process_row](height)\n", + "\n", + " return image\n", + "\n", + "\n", + "var lights = List[Light]()\n", + "lights.append(Light(Vec3f(-20, 20, 20), 1.0))\n", + "lights.append(Light(Vec3f(20, -20, 20), 0.5))\n", + "\n", + "render(create_image_with_spheres_and_lights(spheres, lights, H, W))\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "78043756-0ce9-455f-9e49-fb75268d4478", + "metadata": {}, + "source": [ + "## Step 5: Add specular lighting\n", + "\n", + "This section corresponds to the [Step 5 of the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing#step-5-specular-lighting).\n", + "The changes to the code are quite minimal, but the rendered picture looks much\n", + "more realistic!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3ed5bc7c-f335-48c4-abf8-31c75d6e79ad", + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 12, - "id": "3ed5bc7c-f335-48c4-abf8-31c75d6e79ad", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd6XM0SX4f9qxqAM81z9wzO7vckzK5q4OySJk2fTEsH7LCDof8yn+z7VCELdEKSZYVJk1LFimSy13O7FwPuv2ikMi7uhoPeoCZ+XwiEN1VlZWVVd14ZmO/+GVOh8PhEAAAAAAAAAC4V/NDDwAAAAAAAADgm0gYCwAAAAAAAHAGF/nG1dXVQ40DAAAAAAAA4Gvviy++uH2vMhYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAA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XjcWAAAAAAC8PipjAQAAAODLQq8f+6bN68ECAAAAAIDLojIWAAAAAL6MCEwBAAAAAHirDF/0AAAAAAAAAAAAAADgbUQYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwD///bt4AQAEICBGO4/dJ3iECSZoP+jAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABA42/Z6BAAAAAAAAMBvPGMBAAAAAAAAAmIsAAAAAAAAQOACaazXSuW+u20AAAAASUVORK5CYII=" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "fn reflect(I: Vec3f, N: Vec3f) -> Vec3f:\n", - " return I - N * (I @ N) * 2.0\n", - "\n", - "\n", - "fn cast_ray(\n", - " orig: Vec3f,\n", - " dir: Vec3f,\n", - " spheres: List[Sphere],\n", - " lights: List[Light],\n", - ") -> Material:\n", - " var point = Vec3f.zero()\n", - " var material = Material(Vec3f.zero())\n", - " var N = Vec3f.zero()\n", - " if not scene_intersect(orig, dir, spheres, material, point, N):\n", - " return bg_color\n", - "\n", - " var diffuse_light_intensity: Float32 = 0\n", - " var specular_light_intensity: Float32 = 0\n", - " for i in range(lights.size):\n", - " var light_dir = (lights[i].position - point).normalize()\n", - " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", - " specular_light_intensity += (\n", - " pow(\n", - " max(-reflect(-light_dir, N) @ dir, 0.0),\n", - " material.specular_component,\n", - " )\n", - " * lights[i].intensity\n", - " )\n", - "\n", - " var result = material.color * diffuse_light_intensity * material.albedo.data[\n", - " 0\n", - " ] + Vec3f(\n", - " 1.0, 1.0, 1.0\n", - " ) * specular_light_intensity * material.albedo.data[\n", - " 1\n", - " ]\n", - " var result_max = max(result[0], max(result[1], result[2]))\n", - " # Cap the resulting vector\n", - " if result_max > 1:\n", - " return result * (1.0 / result_max)\n", - " return result\n", - "\n", - "\n", - "fn create_image_with_spheres_and_specular_lights(\n", - " spheres: List[Sphere],\n", - " lights: List[Light],\n", - " height: Int,\n", - " width: Int,\n", - ") -> Image:\n", - " var image = Image(height, width)\n", - "\n", - " @parameter\n", - " fn _process_row(row: Int):\n", - " var y = -((Float32(2.0) * row + 1) / height - 1)\n", - " for col in range(width):\n", - " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", - " var dir = Vec3f(x, y, -1).normalize()\n", - " image.set(\n", - " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights).color\n", - " )\n", - "\n", - " parallelize[_process_row](height)\n", - "\n", - " return image\n", - "\n", - "render(create_image_with_spheres_and_specular_lights(spheres, lights, H, W))\n" - ] + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOzd6XM0SX4f9qxqAM81z9wzO7vckzK5q4OySJk2fTEsH7LCDof8yn+z7VCELdEKSZYVJk1LFimSy13O7FwPuv2ikMi7uhoPeoCZ+XwiEN1VlZWVVd14ZmO/+GVOh8PhEAAAAAAAAAC4V/NDDwAAAAAAAADgm0gYCwAAAAAAAHAGF/nG1dXVQ40DAAAAAAAA4Gvviy++uH2vMhYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAAAAAAnIEwFgAAAAAAAOAMhLEAAAAAAAAAZyCMBQAAAAAAADgDYSwAAAAAAADAGQhjAQAAAAAAAM5AGAsAAAAAAABwBsJYAAAAAAAAgDMQxgIAAAAAAACcgTAWAAAAAAAA4AyEsQAAAAAAAABnIIwFAAAAAAAAOANhLAAAAAAAAMAZCGMBAAAAAAAAzkAYCwAAAAAAAHAGwlgAAAAAAACAMxDGAgAAAAAAAJyBMBYAAAAAAADgDISxAAAAAAAAAGcgjAUAAAAAAAA4A2EsAAAAAAAAwBkIYwEAAAAAAADOQBgLAAAAAAAAcAbCWAAAAAAAAIAzEMYCAAAA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XjcWAAAAAAC8PipjAQAAAODLQq8f+6bN68ECAAAAAIDLojIWAAAAAL6MCEwBAAAAAHirDF/0AAAAAAAAAAAAAADgbUQYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwAAAAAAAAAAAAAXQBgLAAAAAAAAAAAAABdAGAsAAAAAAAAAAAAAF0AYCwD///bt4AQAEICBGO4/dJ3iECSZoP+jAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABAQYwEAAAAAAAACYiwAAAAAAABAQIwFAAAAAAAACIixAAAAAAAAAAExFgAAAAAAACAgxgIAAAAAAAAExFgAAAAAAACAgBgLAAAAAAAAEBBjAQAAAAAAAAJiLAAAAAAAAEBAjAUAAAAAAAAIiLEAAAAAAAAAATEWAAAAAAAAICDGAgAAAAAAAATEWAAAAAAAAICAGAsAAAAAAAAQEGMBAAAAAAAAAmIsAAAAAAAAQECMBQAAAAAAAAiIsQAAAAAAAAABMRYAAAAAAAAgIMYCAAAAAAAABMRYAAAAAAAAgIAYCwAAAAAAABA42/Z6BAAAAAAAAMBvPGMBAAAAAAAAAmIsAAAAAAAAQOACaazXSuW+u20AAAAASUVORK5CYII=" + }, + "metadata": {}, + "output_type": "display_data" }, { - "attachments": {}, - "cell_type": "markdown", - "id": "a6e7fc32", - "metadata": {}, - "source": [ - "## Step 6: Add background\n", - "\n", - "As a last step, let's use an image for the background instead of a uniform\n", - "fill. The only code that we need to change is the code where we used to return\n", - "`bg_color`. Now we will determine a point in the background image to which the\n", - "ray is directed and draw that." - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "fn reflect(I: Vec3f, N: Vec3f) -> Vec3f:\n", + " return I - N * (I @ N) * 2.0\n", + "\n", + "\n", + "fn cast_ray(\n", + " orig: Vec3f,\n", + " dir: Vec3f,\n", + " spheres: List[Sphere],\n", + " lights: List[Light],\n", + ") -> Material:\n", + " var point = Vec3f.zero()\n", + " var material = Material(Vec3f.zero())\n", + " var N = Vec3f.zero()\n", + " if not scene_intersect(orig, dir, spheres, material, point, N):\n", + " return bg_color\n", + "\n", + " var diffuse_light_intensity: Float32 = 0\n", + " var specular_light_intensity: Float32 = 0\n", + " for i in range(lights.size):\n", + " var light_dir = (lights[i].position - point).normalize()\n", + " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", + " specular_light_intensity += (\n", + " pow(\n", + " max(-reflect(-light_dir, N) @ dir, 0.0),\n", + " material.specular_component,\n", + " )\n", + " * lights[i].intensity\n", + " )\n", + "\n", + " var result = material.color * diffuse_light_intensity * material.albedo.data[\n", + " 0\n", + " ] + Vec3f(\n", + " 1.0, 1.0, 1.0\n", + " ) * specular_light_intensity * material.albedo.data[\n", + " 1\n", + " ]\n", + " var result_max = max(result[0], max(result[1], result[2]))\n", + " # Cap the resulting vector\n", + " if result_max > 1:\n", + " return result * (1.0 / result_max)\n", + " return result\n", + "\n", + "\n", + "fn create_image_with_spheres_and_specular_lights(\n", + " spheres: List[Sphere],\n", + " lights: List[Light],\n", + " height: Int,\n", + " width: Int,\n", + ") -> Image:\n", + " var image = Image(height, width)\n", + "\n", + " @parameter\n", + " fn _process_row(row: Int):\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", + " for col in range(width):\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", + " var dir = Vec3f(x, y, -1).normalize()\n", + " image.set(\n", + " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights).color\n", + " )\n", + "\n", + " parallelize[_process_row](height)\n", + "\n", + " return image\n", + "\n", + "render(create_image_with_spheres_and_specular_lights(spheres, lights, H, W))\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "a6e7fc32", + "metadata": {}, + "source": [ + "## Step 6: Add background\n", + "\n", + "As a last step, let's use an image for the background instead of a uniform\n", + "fill. The only code that we need to change is the code where we used to return\n", + "`bg_color`. Now we will determine a point in the background image to which the\n", + "ray is directed and draw that." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3de30ee3", + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 13, - "id": "3de30ee3", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB2MAAAWMCAYAAAATKi3mAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAATsAAAE7AGKbv1yAAEAAElEQVR4nOy9WY8kSXIfbpF3Zt3d1d0zPVfPzs7uzJ7iuQLFFURJoAC+6UEQ9CABAkR9DgnQVxAEiIJe+SIKIp8ESCQIEiKXIrW73Ht2do6e6bO6us68r/g/1N+iLa3M3c09PDKjqvMHFKoqwsPd3N3c3A4/kjRNU1hjjTXWWGONNdZYY4011lhjjTXWWGONNdZYY4011lhjjTXWWCMqKqsmYI011lhjjTXWWGONNdZYY4011lhjjTXWWGONNdZYY4011ljjOmIdjF1jjTXWWGONNdZYY4011lhjjTXWWGONNdZYY4011lhjjTXWKAA1+k+j0YheQJqmwE9CTpJk4YemnUwmkKYp1Ot1SNMUptOpMe9GowGVSsWYhuZtgzadKw/MR6qzdBo0rz9Px/NJkgTq9fqlb6bTKczn80t5m1DkydQ+bW7iDYDLNEpp+Xeask1peN6VSuVSf2Ia3kcuWkx84cN3SZJAs9kU2ydNUxgOh5fGQbVahc3NTZhOpzAcDo30hYDmMZ/PIU1TSJIEqtUqfOMb34A7d+4AAMB4PIaHDx/C2dkZfP755xndtVoNarVaRv9sNluoU7VaFdvNxbsh9dL0lZQ+lB+l72KNSdN44n/75KHJB3nAVo5pHjCVaSrP1Dch5dL3tv7mdGroNo11Sr9tLuSoVCqXvufluepg4u88fENlOZ2HpO+QBu34StMUNjY2oNVqZc8GgwHMZjMYj8fWbyU6aBsiOC00DeVrfIdzg6m/qDyMoVtcFbjmttlsBvP5HF555RXY2dmBs7MzGI1GcHp6CrPZLJsPpHyxTem8XKlUoFarwXw+h9lsZi0b5yY610h9biqfj3upDJP8o8+p/jafz2E+n8N0OoXZbAbVahUqlYq1LBuv2mSibXxrnmshlWPT8UL1oTLBNXdT3sL+brfbsLu7C/V6HarVKjx79gx6vd6C3sPbwyQ3Jd3fxqcx6uQLzoem/HF887mEtmEobXTcSc9jwzVOuQ7Av6G02WQQH0Ma3cRGK8eqxmWtVoNKpQKDwQAajQZ885vfzPSAp0+fws9+9jOo1+tQr9cXbGBOb61Wg1arlfFVv9+HyWQC/+Af/AN488034Utf+hKcnJzAf/yP/xEGgwHU63UAyD8G0J/S6/Wg2WzC66+/Do1GA5rNJjx9+hQePXoEjUYDqtVq5veguC76A5VbJv42zafSGLHNhzxf6f8y3szl28+1Wg2SJFnQZVxzqcnfZJKH2nmbl8f1Z5McpzSY6AuRZWWEL/9JdgqF6Tm1PSQflqYc/AZ1aqlfaJ/yfPf29uDu3btwcHAAz58/z/RaTEt9PdweNPGojbdt72ztbstHy2+m/Gnbc9j8PxKNLt2nUqlkemOapjAajSBJEvhX/+pfwRe+8AUAAOj3+/DRRx9l/fbBBx/A3/7t30Kj0ViY70zzkA3L1qu0MPlKbWlNaThfSj4b/FaKR9jawpQXpcWlN7ve2cq1fUttbS7XTWm5PU7faWwlagNLsNk7tvlMSnvdEXsMvgxttkxQH6bs/VoicAKWnrs6njtL8zgefJ1WseDL3PP53Cq8eb6rnhB9YesLzbcAukC0beKLDa1C7dNXqNTaykDFYD6fZxNczDqaypxMJjAajTIlD+Bikm232zCZTDKFD3kZwK44+TqVfHm+qLEi0bLsyazsciBWe8SonxQ4MKEoZ7WpLJdRLX2jyVtTpk++Pm2oTTubzWAymUC1WnWmtY05G70mWqTnITJmjeJAHTq+33F+1uicNB034l3jmOpvtFyX8yYvv/G65ZkXNM5vW9lrvOhz7kij4H3E++9lkEGmNvGZ3/LYFFcRWhmmTb8KxAxEUj8B/bvb7cLR0RF89tlncHZ2dsmBGFoWwAX9ksNSC0mHuQ7jXZo7Q+21or+5Sgi1W2yBobz0xPA3XLV+8wn4mRDDN6UZU3n1MjqOMeBaq9WgWq3CYDBYCErxedtWdtE+Kv5+WXI1JNhL00p2Dv5dqVSyhQ+0TvP5HIbDIQwGg+zvbrcLvV5vIY/YvsGyQcMHPuD6SYivUltObH8KRQgfut7HaAObPeRbdl5f2MuO6ywXyoSgYKzvBMYNYSr4TU4o245YxGQyWSgDd9OGKusxB6rG+NcICf58Op1mSg9NU3bjmsNE46qcej58k4eOWE4hvttVwnw+h16vB7VaDZrNJkynU9W40sDkjJ7NZnBycgJpmsLt27ez961WC9544w04PT2FJ0+ewGw2g9lsBvV6fWHVFdLnMx7L6GizObvLHiC9bjAFT1xpQ9770ATwgs9t+VLDSpOvjxyjsAVZtE4ErRPSh87hcAij0Qg6nY4qIIv5A9jpRnoxT747H0EXspjkkm0xCX3/MqOoNkiSRNwh60MT/Z3HgeOatyT9DedA3CEbc8EDzQO/K1KHWkOHSqUCzWYz29lnk2t8AUBZ2zmmDcVtRddOoWXSdpVB27WsiySq1ap6ntdgNptlp2nQ+eEXv/gF3L9/H87OzmA6ncJkMrGekOCDNL1YCBsib2MGolcNvuiIjsM8dSwj364CIWM4dJGARmbQPuWnyryM8J13Yskflwyx2TAhZW1ubkKr1YK9vT04OzuD+/fvX+p7vnEGecnFj0XyEJdPRZcXE9R3gKcroE8Q+Wg2m8Hnn38Ow+EQhsMhPHnyBL773e9eanM8tYjyjasdrsocVUSAVKq7yX/ggk1GaOR7yIIPLXDRqEaPWUZQ32Yb51n8tkZxWNteOgQFY/MEYjm4YwpX99CB1Wq1YHd3d+G7NE3h6OgoC8jSST1mx7tWuklpY03qJiGLQS8UlD4Cs2isYuCFlGcKPOBzVEwk45EaJabJUgr+5OEL3uf0iEbK93fu3IFqtZoFPLEu6Bigx+DFnLxoO/R6PUjTFDqdTja2kT+1RoBrhZMNoTzok79vnhq5EQOaFachAUnNt5p8Q8DLDnFAaOim5UhBNRf9Ep2a8nDu8pUPvm0gKbIxeI+ON9N8aQs0a2igcxv+9pnz0ICSgho+vBGKq7oAI0b9+Rw4nU5hNBrBZDLJ5iVahmmO4EYwHTemMcedPwAv+KYo443PPzH6XGqbEGcJ12Figo/lPPmXNVAkwadPbN/aYAvExmz3PIhdLpf5ee3OsvCT1F8ue4LCZXf48lQe+NjHvqA7fTjQDsJ0mjZD0CtS8LoDn0U8Wh3HRhPdEY95mOaNVfCtRp5o6HPxo6QL29JpUHYdS9JJED6BEFe/mMrNA+14N41Z/O26zuRlh2s8mNIDuH0NmvbW2ka1Wg3q9Tq0221oNpsLi6aozh6iD9t4yPXMBlf7aPy4Jjq0tqzrvcsnhmmkhUtvvPEG3Lp1C7a3tzN7i25ywLmOnsiirWNonVYBH/vIlpbbl5KNGWPekoB5a/KP6f/X+GSl/yUafOc0Uzk2HtX6gLRprzry8F3s9lnP7zos9ZhiaTDU6/XsHhiAF4Ejqqzt7e3BV77ylYW8ZrMZfO9734PT09PsmbTrYFkDz6S85HHUmPJP04t7AWq1GjQajUyI8SMqrjqWKTx5WXwVH73flH4jBSKKcrzjym28Z6jdbsNoNMrOHa9Wq/D1r38dms0m/Mmf/An0+/2sLqPRaOF+CMwvdpAhTS8WSZycnCyMbZOiajIqQ9qQ0hA6DpY5URdVVqgj+yrJjqIXn/A21PBj6LhHA4nfGUnfhxqcfCzY8vINSsaSczbZwMsEeCGbaVDNZqxITgFJVl8V3r8KkHgDF46Nx2PodrswHA4XVhPjYiPuXKCOdx6gmc1mmWNbS0+1Wr204M+FPLoI56sYux/y8Osy+DyGE+KqAPlz2TuBqCyP4aRco3hIQTfpXahD6zoBF+tIQDmepvIdbTa0Wi1ot9sAUPwCTxOq1So0m81MfwGItysuNlZlF5gCTC7H+csAepqLBquWG7ZrGK4rOM8WHdySvsmTj4beTqcDzWYTdnd3L8kvrpe78tOUF3se5LaxNO/6lBXTjnQtdEiSBJrN5qX3v/Zrvwa//Mu/DI1GI5tD+RxZrVah0WgsnCYUSndZdyT69puUnvI0tUN5ObF0f9oHLr8Gp8G3vlrY8uX+IJPe6oJrfsjbtmv/zhplhzMYm3fFDP+G54fHrTYajSzQhEe2Ydrz83P45JNPYG9vD/b29gDgYvDevXsXdnd3YTgcwng8hpOTk4XyqGFrUuxddTWl0eTB62yb2F35mhx6/KhmjbJStGFvcyTwNnDVV3L+m8ozBRxs9dX2OTqKbXSaVvVI5bvSmoDH2yG4kpUkCTQaDfjSl74Eo9EIptMpnJ2dwdOnTy/RGxt8nNF+mc1m2REqm5ub2c4odMbTVeIcLoXY1G5aA8jGj6HKBU/r6vu8cMkaTVto6yblVUSdXDCNKwqXzAiFTV5r+U7bZnQOsznfaXpKowTMy+Qc5gt7bI5CrbIeQqckx2u1WuaEDQ2OSW0qlYf58yA5/VYa59q5uOzIQ7s2QIT9qT3eOoQuOocjL0u6iEnf1TqotOMSx9d8Ps9WtyN9fKGAplxON+VXk7y2fW+DrQ98bYNVzBuxwQMquKhmMpkYeRoXyOEinNDrI7TOmlXDt49Rfkpyl6bR5LMq/qJl+wZPytJ/eWzlUNCTflAuTqdTePjwYbYoFheaamg1pcNj4SmPJUmycEqJb94ScPHP0dERVKtVqNVqMBqNnPRJ80CZ4LKnXXO1aQ4KrW9ZxowJmvnSVXecW2LItTz6qUlv5rowf67V9a8LfPwPsXx3JvkRMgfz/9Hucdm4mjqhnDflFZMfTGMvtAzJHpDGstYeNPWRZvzM53MYj8fQbDZhZ2cHNjc3oVarLdAzHA7hwYMHcHp6mj3nG05CbADt87LDVy7R/sqjg7v43tUnlC/43GviGer/sJUtfa/xx/N8tAiRW7b2p/leVb5cBsqqX75sCL4zliKPA2YymcBkMskcUrjrbzAYZGm63S50u134whe+kB1XXKlU4LXXXoPZbAbHx8fQ7XazXbImxTAW/SagUmFTVLTKt8tQQQOPOnFqtVopV9dKigttKwpJAZQCSz5wKbCa/KiDgLYxr5s0OdoMe9+60GBskiSXjPo0TaHRaMB7772X3ZX04MGDLBhbFEyTIj6bz+dZMHZrawsAIAvGokMkj+PHNOak5zbHel5HgPb7opyElA5TGZrAgqk+JmfoqgJPvNwyKF0+7evDB6GGk5SPKehI+cYmw/i3LuOc18GWJzdgeToajHXlZQI6ebXGDq0np5HSje3g0j3KwKexEeLATpIE6vX6JV1GWw7VD1x9ifOMpCPF6A+TI1J6lqaLq6mRLh68CxlXdC7l47pMcMnHq2QkYvviyTy1Wi27hwvgsn6Ii9NwEZppsZ+rvOsCm1Mqr24RIpfylgGwKOvpHW5FIYYDLDRNTKRpunBKAs4NDx48WKAp9F5Z6nuYTqeX8jLN4678TMAdvEdHR9kzvCs6b95lwCp0f8nHs2waQso16X/a733vU7b5l3znWJdz3CTDpVNNfMu0lXEVYLOj6TOTn0tbbxqo9/GZafNHvdX2XltXno+Jf2KPccn+jZGvqSxahu25a6wCyLtQcUPTzs4OvPrqq7CxsXFJRgyHQ/j5z3+e2UCmvHi5El080Fe2MekbJAzpd8kXwMvRlOXL3yFjwRYPcflaJH+2q0yTDMJvKT0uP5ipbaU0Ei9qdbjrAh85vka5sNRjiilsimCSJLCxsQHT6RQGg0H2/NmzZzAYDOC11167dIdso9GA1157DQAuhAEGcJeJmM4lKchHjVTqVESnHnf+hBqry4DUHq72S5LEqEDYvuX3yiFcwQ3pOSqRWkdziNFjA+50bTQa2XFbtVoNut0uTKdT+PGPfww7Ozvw/vvvZ8cE3759G775zW9mPPP48WM4PT3NnASxHETc0fzs2TNoNpuwv7+vzgMDLVKgxKY0m4JLmH5ZDoOXZeJfphKuHWt5oeUNSUHlz23fuJ7b8vEN5tnS8byobDXdq8nplGjk90NLjgHbeDUBnafSUc4STMaQTd7TdLY8XUFZ+kxr/L4MSNM0u6MPg1b0dA8K2k+hQexVBvVMTh8XqPOS5qXNh+ooRcnotTG3fKAcpQF8/n6NNdaQgTax6Y7Y2OMnhuwtgz0Rk4bQvCTdqQxto0Vs3sJTYZYt803lUb1ac2SnKZj+MsAneEO/8VkYogns0Wc2SH3VarUy/R31kNFoBM+fP1/YIMB1FvrMVE4ZoAkU8fQAfosyTPXlfSSlazQaAHDR5s1mE27evJkdvY/5P3z4EE5OTuDDDz/MTo00LUbFctY6pB2cn6XxFmJ3cf+BzVcgpQ1ZuEGR15/HZYTkNzfZ8LYgsQ+tvnJtjTXKBGcwNnYQw7aihjoZWq0WTCYTGA6H2fPz83M4Pz+H/f39SxfF12q17AhjgAtl9fz8XEVTiCDSfOMK9iFMTmApf3SyYf3p3YlcycFgrCnPIgJUNvq5MNYEG2wrdUwwpeE7RjRBOq1DONTx6st3uMM0SRJotVrZCuvhcAiTyQQePXoEZ2dn8N5772XK8s7OTqa8pWkKJycncHp6mvFO3mCsqR+73S6Mx2PY29tTG46cplAnvJbGEPiMG58FB0UgT3mu+i0zIGvDKmhAuUDbiAcJQ4LIPjzF5w2Nkktpt+VBF5G4HDBS+dLpATaY5khKJ8oEzekPlH5OI6cV/7f1hWmuMMknU8DE9V3ZEUNnwOMh6/U6VKtVMRhL+yOvfmILvtucQhrD0AeaPud8a6PBJH9xzFLHi20Mr5EfVIbkDYKbZIkki4vQ4dcIg20xTx5HpyQPeHk+dBSBVc5jLsc2Bx57XK/Xvehe9hiz9eNVg087S2Nl1XYUR2ydwBd04X0MGmK0J85PmgXzLv3uqvJ7aDuG6ruucWIrR0sX/Y2o1+vZKYb0yrTT01ORJu0uTKmsonlBki0hbWTKjz+X7EdTfqagU61Wy6666HQ6cOPGjQV9fzabweHhIRwcHMDTp0+h1+st5GebL7UBsasEyTeirQP34/P20cxNvu3lypP7selzV1DSx6612R4mnw9Pa7NbTOVp6XJB6udV6w5FQBuLWqO8UO+MzWNMajAYDGA8HsPGxkY2qOv1Omxvb8N4PF7YIfv222/D17/+dQC4WBn0gx/84NJO2Pl8vuDkwzP0TUpB7MCCNj9bGknI4qXr9Eg/U7/Q+ttWRJUFNuc+ggc8TEE7X1618beNbzQoImiFAUvpeN9GowFf/epXs93jT58+hZ/+9KfZ+/F4nB0ZDBC2kkuLyWQCn3/+OQBcOOGbzSZsbGxAu92GRqORBWzH43F2xF8Mx/GydqNRRSNJEucY4wEvfFY2LCNQvQzEnrdsY1nDazZ+4QttKPC5NjiqKV8qB9/jimfTqktJ2c6zGw+/8xmvPneMcmiMixC4+uhlBzVocVGR6bhoKifp/VIcefrNFODQ9J/WwDcFUPg8UK/XoVarwXg8dpbPDeqyyd2XDWl6ce99q9WCRqOR3Qtp2vW9xvUDn8sRSXJxFHuavjhanwZX6ZzJnaFUH8DnZThhgS46WAY9SZJAs9mENE2zMaUJPISWFRPY5+iDQB1hMplEWbxRJvD5KAQuWzz02+sA20k1FL7t4KtDafoB5V69Xs90PM2u2TUuQ+snDPk+BOir2djYuHQ/KZVntL9NOr5G/hXBM5TnpTlMo4Nr6OaBM1MA3eY7wu+wregmJcS9e/fgnXfegZOTE/jBD34AP/3pT+H4+BhOT0/VV8Bwmih4P13Vcay1lXx8Kb7zOF98YQvQa/L11SFcAVxTWgSOF01MQWvL+uJlCLKu8fJADMaaJhmXsynPYMBJm+aFd8jyibJarUK9XodOpwPT6RS2t7czo3A6ncJ4PM6+RyWQT4p56dXkQw1uVx6295KxLn2HDvTJZCIK+GULK1eZUtuYnIumFTghRgctX3oemocLpjqFTEbz+Xwh+IFGPj2mul6vw+bmJgwGA2i1Wtm3zWZzwemLO6h5XWLwzHw+h36/n/2PNCZJsqDEY3DZdLS2z5jlhgGvh0nRtAUETMqS9PcqHOO87GXRwJXRmGX68B9Pa+q30ECh651PEMYGPlfZyvJtb99+oY5greOVO5k57aZy6Le2fF3PYyn2vmlcwUT8vQoDNubc70s/HwP0f3oiQ4hTMbROMQO60pj0aSPO+yGGsVTuqgxTSYfgz6+qE8cFtDnwh9dZmptRF7LpES8rYo/9VbUr8gUP0PrY2WXkBSqv8o5patfyuuL/eK2Ky7Eco61C85DGMnW2o05FFyCZ2jBvmxZt8xeRt0ZXWuZYiKVfUmjmd1caW9v4BARC6qMNDuA8aLryJBSr8GXlxTJ9Ai77MeRbpJvLLDzdZjabLejzPvXl/BSzjWx2meY7rX/WVTaO2Tz2aZqmCztf0YeGfrR2uw17e3vw9OlTODg4gOPjYzg7O4PJZKJeKF4Erup4Ndky/G/pm5C2NPmqXLRo8/UZ/1I9qI5GwdP68rk2rUZGSLS8jLiO9S5KjpRFPonBWBthGCyJdc8kRZqmcH5+DtVqNQuw0mMwEP/7f/9v+Ku/+iv45//8n8Nrr70Gf+fv/J0sOPXgwQP4v//3/8Lu7i5sbW3BkydPLq0Mok7momFywvjmoWn3L37xi7CzswM/+tGPsmMpKMroENMEM2z8qD0OE4N9PMBgMoh5XtJx0LF4KMQowiNh2u02tFqtbLfp6ekpnJ6ewn/6T/8J3nnnHfjX//pfw+3bt+Hv//2/D9VqFWq1GmxtbcFHH30Ef/u3fwunp6fZDttWq7Xg7IgloGjbjcfj7P4KADA6VmhQNs9qWh9FP2R8mnbg+gZuriKKdm5qgnScHlcQM8bESxVWU1/Ssmh5kmPCZ/y7AjY+9UMa0MkpldXpdBZ4vNfrico4VdTpndoob7mCnsfZGTMdwpcvNAEUXD2OTtjRaJTpKcvEKhRNKUjpSh9KZwxHX8w53Xc+x7uQ6/X6gm4SUifehnTuzNPGsWGi6brMjRT0xAMapO33+xnv2ngP+YnroC8D6Ek4VyFI/TL1TQyY+pLqD41GIyjoa5rT0aeA73D85ZWP/PQpXBSN5WFA3ne3fKjeVOZxclWQtw1d/E2RR25IV1KZUKupD8RbgI/uXavVoNlsXjrFLQauG19TuRPr5D6f9Cb5QvOw2S2TyQQePnyYyTVNuejb1dBUBEKD1QB2XyX/7drtakvD/R9oE+B3rVYL3nzzzexEnYODAxiPx/CLX/wCDg4Osv7w3b0Y23e16vHK/e95oIl70L4yweQf4vpjHv7RwhRc5eD858rLVJdY/LBqvlpjuSjaz7xqqLQy08TFB6WPAmBywNCV4vReVDzqZD6fw/n5OQyHQ3j06BHUajW4detWpmBub2/D7u5ulme/34fRaATj8XjhqDuTMlzWyDt1RKDRx4Vdo9GATqcDW1tbCyuocGeiRMcqHQc+xjWl3WacSg4baYLg3yEtnCabo9DHOaSh2QfcKYdOviRJYDqdwuHhYXZXa6PRyBS2Wq0Gd+7cgdFoBD/72c8W8qRKiw9NkuPd1A+mI7alPDg9tna29QUPxtH0AJfvEubfmfJ10RKaNo+sCDHCYvGjlFdMaPiSj2GpX235ayHxuhZ5DEGa1hYI1qTj6U3/m5yUGnmhmWt9A9H8mcZZaZL5UjottAFGTLeso9NXDVsf+H6rNRTzyLK88jbGN64gmzQH8jpr+NmkA3LHjwumMZ9XnyyLYVQkqO6ONo0rEGTTT69i8M+ly5nqFaKTufIqe/uFOhND67WsMcjrZZrHpatYfPL3fecLiW6fAOqy+c82R+aZz0LsY9O3God2bD7V6hqub7X+Mvwur23JZSK3ofnCT1Nepv6SdAMbz1O9lzrw8/D5y6AXaFB0MMyVP/U9TqdTGA6HMJ/PYTweZ4EqzbxatrnXNoY09QAwyzKf8l0yk46jJEmyU+5woU+/34der5dtxlmlL2rZsNHvoyvGyF/iBY2NyuWqdixp+jkPf0rfhYxhbftq7CENjdqyrzLy2oBlk8XXHaZ2XgjGapyFmBHfkegKdOZBs9nM7pfESWYymcD/+B//A/b29uDf/Jt/Azdu3AAAgP39ffiN3/iNjM7NzU349NNP4bPPPlu4xJyuDlz2bpUQ5sf7ZRqNRnZM82QygdFotJCuWq3C+++/n+WN9wegwoQT97IGYIwgjeSElPjQVA6vI+9rk5NfQ1eappdWpfoEBHh5yxCIX/3qV+ELX/gC/L//9//g4ODgEh248CHP7ndb+/HjlZPkxSpJDNTW6/WFvGgb52mjIpzzNE+++3rZWKXCsYyyTeOEPnfdZxE7cKwZu64yqUKV14AKTYdHiNPVj7hrr9lsXqI3b5mm4JJvGimt1N4mY0pqd1/ecMk7AMjuDTTdkVoGxNbfaOCJGq5a45F+S+VqrJNZ8sy7IXIk5Bup3ULpDdFN1igHaEAW4EWQCp8XcVrRGqsBdwhjX/PrSRCrmk8k3X1ZoPPCVQMdt/RqGfp7GYjFN3n8J5IuT69c8j0ZKa9zctnwoZX62Vy6MO7Ixh3a/P0yx+t0Ol3PT/8/yuT89qWh0+lAu92GwWAAw+EQTk5OjLwoPQ8JopjSh8pJE70a25w+k07lM9VDWz9XnajtiLKg0WjA1tZW9u1wOITz8/Psm9Cd8NcV2rmK84mpD/mJGCH3D2toktLk8VVy2HZo8xiJaQOLCS49jbf1VdTp1lgjDxaktG31Hk/H/88zeKSVIqiE40SCQaJms5m9G41G2coseg9lp9PJgkk3b96EyWQCBwcHC0f34tGsNCAUCu5gk9pL6xzWgDpjarVaNkGfnZ1BpVKB3d3dLJjVbDZhe3s7E6ToXJeO6o0BW35SG3MHkwmmNC6lCr/TBAZMdLr6Ny/y5It3JOO9EXik13g8hm63C9///vfh1q1b8MYbbwDAC8Wh1WrB+++/D81mEz744AMYj8cZX4SuRHfVkTsbaBn0mRRQKbIPNH3vk0/etsuTD/92GYafdozlhW9dymDsupDXqAx5T+8uA5ADW/R9klyswE3TFLrdrujYMZXNx3ieoJdEo68Cz4OCpvxNdfTpq/l8fknPKDtPxpD7trna5XDWLFrISxsN8JahP5Ae6mjloHqS6Uj8GG2j1cdilOeTfxn6SQvsS2kRAsKnnZeJVbaz1N9cvmt5XbvYIxa4HWmaU106TJkc9QD2xa306N2i6OULUKRdfrHGUKxAAf3O5cxdRT/70LFs+YR6p8YGtcnX2MjbTyHBJgCdPUrnG/wGv7Mde+tCHvsT4MVCPMlf+LLAFmSIxbdan1uMdseTzdC/aron1qVb5x1PIXXRzq2S34m/d+kqJhpdNqvpG5p/pVKBzc1N2NjYgCRJYDgcwmAwyNLj9Rfcr23SpXjd8P1VH6ea8aX1GWjL8vEl+ORn+iaUNq19nXcOwDxcvnuJtlh27VXnYxdCZOlVta2vM9RLZkzBEBoYxOcxFNdutwu1Wi27Oxbg4oz8VqsFJycnMBwOs/R41Fe1WoVqtQobGxswHA5hOBzCvXv34PXXX4ePPvoInj9/nuU/mUygUqlkwauijUqpjnmB9R2NRjCZTODTTz+Fer0Ov/zLv7wQjH3zzTez8vByd7yfDAXlKo5OjCEQtAoQ5VtpctQ6H7GtyiLAxuMxjMdj2NzchEajAZubmzCbzeDo6AgeP34Mv/d7vwe/8iu/Av/iX/wLaLVaAADZkcX/7J/9M3j+/Dn8h//wH+Dg4ABGo1F21wtA/EA9vavJtBsdF1/YJmKXgerr5ESeyLOb1aZYlIVXigRv8zI4FbmzpmhwY1RjBPjQaDMSXWOCftNoNLIjy/HYf87/tVoNGo0GAFzM7Ts7OzCfz+H4+PiS44eWQeeS2O1eVF9qx6ivcYAyrl6vW+9Huo4IMaRiGV+uMnDR0SqdhJTfcDyhTmozlF18GqJTSbJoWXLzuhjKtA1xcSTXb3jfSmnKhGUa7JwPpNNrpHQcrnmxKH6jQaRKpQKj0Whh5xkNTFx1fRDlRK1Wg1arBcPhMOouOx7YxnFS1M66ongC6aYnjlHn6lXnA4o8bUi/xZO7NMD2xbEXI8ATq0+WKTv52ED9fjqdLnV+4XYv9gnfUfUywLSAhO+io35TTZ7SfCZ9y2VoXmB/TiYTmM1m4m5rijLrNSZQ/VfiUyqz+d8cmvGvGQuST6VWq8G9e/cy+/zo6Ag++eSTS9/SExApHVpf51WGpC/6yOFVxgGo7IytJyxL/6S8ZhpL+JvS4zM/lMHHeBVx1cf2dcWlYCwXBADmQAOm505oKV0I5vM5DIdDqFar2cTDMRwO4a//+q/hzp078M1vflM8MrZSqcD7778Pe3t78JOf/CQL5FLHXJFH95omQV/BiI4erJM0qGazGTx+/BjOz8/hzp07UK1WodPpZDsoO50O7O/vQ7/fh/F4nLVXzLqb8rK1g6ZsLW22VTY22AK29D3+Rp6hd2X4OIuWIRRp0HM2m8FoNMoWNYzH40t8hLtm0YgYj8cwmUxUZdn4WepzKmds96ZJckWzekrqexuNeXif847mHjj6Xd7y82AZQRBX2QgbDdim2rwkFOmM1dKA6fm86ms40TxswRMTj6FzsNlswnw+XzCwccw3Go2FHbLaumlo9nnHadfyAXeA879t33PdRsrfdrSPiSZTWWu8gGsccR01JH/J0ZHXWMV5Lc/VF9wID9WXbPlzXWaVBi2OM+q4LANdPgih16XvuALiNrssNq5af0hYBs+XQY4vq4/y1jU0OG0LQPiUjb9j2GTUiW8au5IOIpVZhjEmjREe8NTq2yE2DuqmuIB3PB4v0OWTF0VoX4fqBUX0JfKWFHDQlF8kf1UqlWyxdwjKwPtFQfLZSXpo3qCRNgjr8tWYZGPIGFr2vBjC9y4/N9VNNf6mkKCnqT+pPTGfz+Hk5CQbY9JpVT5yUsMHZYLkOzGl83nuys+WDx8rPHYSOu/EaHuXj801f/DNRy7/vvS8aF4qG4+WFWUd02sY7owFWFxtxztQI/ykScaXAfBC8mazCfV6XVRUhsMh/Mmf/Am88cYb8N5772W7/2i6arUKv/IrvwLdbhfu379/KRiLO0zpSr5QxdvkwKVpEL7pAGDhKFkJ8/kcPvvsM2i323Dz5k1otVqwtbUFg8EAxuMxbG9vw9bWFnz++efQ7/cXdseaaAlFbGPH1HYmYS/lSR1/PL1LGUNg2+Pu4sFgkO3M1sDkZLP1gat/kG9t7YhHVO/t7UG73RZXmlerVWi321k+5+fn6mAspVOTjjpgsR95G7ockjZjX2oHabWuT3kmGniZdMW26xsTrXkQUg9EXseDr3EpOalsjiwXTGXyO15sbaQ1ZjRy0zTWqYGngXT/Ji3bVR8+NpBH2+02TCYTGI/HWRrccY9H4ftAWtXpGsOcTtOcaKujS5bbAhza+U9j9Gr1pZcBPuPW9A2OFT4Hp+mLXYguB5SPfiM5y30MdN/dDvx/if8p34bKZ5pPnvnBtzyEVgbgd2UIbC0LJr3UJF+onJTayaZDmvJfY/Uoeq7QzJ3LxLLHOZZlOxI+T954XQwALJzepUWZdAVJr+Q6q49t5gM87azZbEKSJDAajTL7MM/cZ6Izb7uH2HMhDnppDvDVrfKe7GXSTZD/2+229Vsfe/0qwjT32naISf/7BI24DcX1CRd9nFZNOm1am49mFZDGjI/97vLpuPotpF/pkdCz2QyePXu2YAPZ8g6xQ7TfrQJaumLQr2070z2xtna16WIxdpb7jE3T+DTdFWvzQ2uemejQpjO1rZR+jRco65h+mWDiSaOXFZUHF9PjMTx4LC7AC0W6KLTbbajX69Dv941HF+Fu2lqtlgXMms0mfPvb34aDgwP467/+6yzIhArkZDKByWQS5ChHaJ3Frjx8gHXEu3Tx2a1bt6DT6QDAxVEWZ2dn2TcYkDs+Pl4ItuUxoLSgDkEsE8Bvtwt3WtkULJfBaHNc0fyRp6bT6cKxXI1GI9dOmBjAY9HQeN3Y2IDpdAqDwQA+++wz+IM/+AP4yle+Al/5ylfg9PQUhsMhbG5uwq1bt+Bf/st/CZ9++in80R/9UeF0mvpE+l9yTGqV+jxBABtsym4e5aCIMXfVHNqaoBnCJ8jLv6O/eTo67kPaTuMot9Fng0ZOub7Hu9ZRlvHFD7b8arUa3L59G4bDIRwdHRnrumyFT+tMyxOItaXhQTJbPpVKBZrNZnbUF/1+DTeorKVtHTJeQ/l02XzuOhEAwH+ecxnUMXjyZTP86EkiSZIEHau6DN7iDnDuyDXpTi6+WeNiERMGO3B+xGOLqcyyBSHK3MZ0dxC9P3AZtg9d5CItmCmiPFe9tPYqQtI7y9DXvD3p377HafLvNcD0ePR1pVKB6XSa+TR8dH0fXliVzhpD90/Ti80E+K7T6cDe3h50u13o9/uZ7HHt6AsNALrSoVzIc2/tVYMky30W6RdJiwm2wC0Fl7+2Msq26EdbPh8jMe13Xo4rD7qTGvmIXy1Hfb58k84yfU3XCRofI+WTmLy+6r7hwf1lBOm1Qdg11rjOWNCyqaFIhb8NODBxdymdMGKBO9yazSa02+1LtKHyRwNnrVYLOp0OVKtVqNfr8PWvfx2++tWvLihImBbptu081dJLjfG8cBnxtVot26WJqFQqsLW1BTs7O7C7u5td9g5wIdi2trbgxo0bUKlUslVXRawW9oVtYnM5MaQ0NF+uZJkmU+QBaUUcD2AkSZK1v7avqSJmo1mqG13JRMubTqeZw6dSqUC73c7ufn327Bn8n//zf+Czzz6DNE2h1+vByclJdhfkP/7H/xi+/e1vL+yA0wQ6QyEZLJJBwJWi0DEVc4GBtr9MdPC86DvTMUah5dnKlhAS3DSl43Uz1UtTTl6jQioL29tVtisv07jUBOaQDl/+lNrX9E76Do+sp053Kguluw0BLuTf7u4ubG1tOZ06Lpqld6Z+iq1LaPmLp9HoNTbHBp2vQ49xe5mRpmnm3HMtfjLNFz78xMeFb16m9/x5qMyR8tXmeRUQU4eODal90Tk2mUxgNBoVdselCbHaSuJ7/HsZoPbeMvg4dN6SgFeB4AIKtIdxAWfIHEixyvHAdQ48PSvvvC19b7PhqENaU04IH/nkHYIQvS8vfGnV2lymvqDtru0DtLHxqFsMyPrSXSaE6psa3qMLP1qtFty8eROazWZ25QjXV2x5cXvAxz8h5Uf9SpwWKb2EMs79vkB/osu3ZYJG38sLm33DZYBWL1uVjNPyqkufD7ETtOX45ok8RH2NaMvTxRbUD2+yPa4ytL4OCpt+IvWdpj8RIT6kVcM2V3Mb2yb/bXO7Vj9zpZHGp8YWXhWKpqVMdb2qKGsbLngE6X2kEkwTNd7tik5GNEDRUMt7D9R0OoVutwuNRiMLMCXJRUBxNpvB+fk5PHv2DH7/938f3nnnHfgH/+AfZN/ihfPtdjvbJdrr9RbqOBqN4Pj4GJrNJmxsbGQ0drvdQlf8hjIFOn34XbcUvV4P/viP/xju3LkD3/rWt2Bvbw+++tWvwvn5OZyfn0O324XBYHCJntlsluW7TGdImqa5AuCaMvIaQZPJBLrdLtRqNeh0Ohm9yOe1Wi1TkHiepnbkd4vmcai66ocBWx64pxiNRnB4eAjtdhs2Njag3W5Do9GAbreb1SuGQmfb7ePTV/QbE6T88t456KLJxxFVFgVZY1wtE1LbaO/iBZDnG017a9PwcnjZ1GkYi8+oAmwq2wQ6L+P8iQubRqMRAFzs7MG/aZknJydQr9dhZ2fnUr5YR1TeuWyxtafWUeU7rjS6Bm1DdJi7+ilkDNCdHXkXer1MoHyj4fcYYwzL9JkfkCZqwKJjRLOrh/K2Scf2rUPId8sEr7NmF1qZoJEDyEN8DqCLPlEWA1zm97LoBbFgk8lYX9s7LaissAUkcM6KAbqLbw09uFynu68oT8QcD7a8UG77QFrMjXmVaQznpSXmfMIDf678eduG6L8Uvn1DZbhPO2hOtQhFs9mEt99+G5Ikyex6G3ibFWGXzGazLECEizXQPxKKMo0hF3yDbpLNlKe+kr1pk3UmfZP3F/XjamiwlbeKOdLFfzZ5wvso75jR+hdoO0ll4qYLHGca/42tb7RB9jKjaNp5/iZ91cYjsWjM4yumedBNWNK4R97ndC+7rTldZcQq22QNHcrahgvBWO605s9MgogG8bjSygd4iLGdpilMJpPMuYWo1+tZMHI4HMKHH34I9Xoder0e1Ov1hRVErVYrM7JarRZsbGxkQWRUGPFuWgDI7gDldShCkdW2AwVV8iU6ptMpPHjwIKO92WxmR9ji6u3xeLywAhXzWragC3U2upw7NE1IORK/T6fTbHUazZMGZnkw1gQ6fkL5SaKR8gZ1Ko/HYzg/P4etrS2oVqvZan1cSbe9vQ1pmsJgMMjGDvJHv9+/VE6RQk1yYuJzCp+ArCtNCI0mWnzbhrania/zOiA0NGD+Urv7OoqLCqBoyncFYpch42IE3Wxt6DMGaZ+i4Vav1xcWODWbzWxOpGXg0cauwJLkBKDjLm97SH3nMi5NefBnpqCf1P4mPchEC76nx0mtEtcx0GPCKnSZNE2zqyOke9xt8wZ/ZhpPMeHKt0h+cTl/ymh0a/qPvjM5SDG4gDqarRyb/qKVR6FzbQxw+SnNyy470bc+kk5F/46tC5p2Qhehe8aCTxtqYdJVpHda/4IPfPVkk5NRW26SvLgrNkROFiXXi0KeOko2jUtvwu/zjGFJZpvycMlzbbncvg+RO6a0qE/v7Oxk13BhUIbf6UvbXsNreXmRLkqz2Qyx5HAZ9VmbDOTPaN/kLVNbbgidmvKlb6Q5OBQx+9pUT6kepnKL9oGZnlP9UesD0/hMXGmWPdZcsthXltn8BaZvfGhyfZ+37Xza30cP4gvGpblaI6+XMR5CZNEyYJIdy6SljHOhD0L8zNcFC8FYrLg2mES/4X8DQBbMBLgcuNU4VDlGo1F2HxOuCuL4+OOP4fd+7/fgm9/8Jvzqr/4qtFotaDQaC2lu3boF//bf/lv44IMP4L//9/8urtRKkgQ2NzdhOp3C+fn5UhjDN39UvNHx12w2odFowHA4NK4+297eho2NDajX63B+fg47OzswGAzghz/84aUdUTg5F7GSTXKUFLVizmdSwkkIHea429V0hHOlUoHbt29nPDYYDOD+/fuX6uIqm+avPeZb4snBYJA5B9BYG4/H0Ov14Lvf/S58+OGH8Nu//dvw/vvvw2effQaNRgPu3bsHb7zxBvz7f//v4W/+5m/gv/yX/5KNMVM5MQSm5lvJeKdKA6eJ0kXTSoqETdnliGVISHnxsRDbMRPaR6Y+Dm2LPHXTOGu1Zfo662IgZnCFfkcNs5C85vM59Pv9TI49f/4czs7OsgUaw+Ewu1+alq8tz9cQ5+PA5sAw0WJy2mmdpVR2u3ayagKslF5+/M8qlM2rpOCaxm6R5dH53wfUiE2SFztj6ZHKmrZ3OYm1TlVX/kWlB9A7uq4SiqAfF7uNRiPR7uJzHpV73Okem95lzZNXmSe00OyQR0gyjy/q8HEe+pYXEyY55SMPfVD0/EBtVSrr0V6qVCrQ6/VgPB6rjimP2Y9lA9XlUF5p64iLmnEe9ZmPQ22d0PYv6pQlXga1x/Fucorz83N48uRJNo80Gg1oNBrQ6/XEHboxbJEQxJ5Hr8L8gX5Paq/M53Ojr8fWV1I6TaBEA0pfLH7Q2l3avGLAFdjhc60rKMjzdaULQZqmmf+70WgY83PVjdbLt4/LMNZcdMfw33H+N/k/NYjNs1rZoMmL+6lrtRrcvHkTJpMJPH/+fOU6ScwgeVGw+ZaWTVMZxmcIpDa8qnUJxaVgrI+Qk4QCN7IkJuXBN5+ADCo0XLnBZ8PhEJ48eQL37t1bOCoFj5DCXY2vvvoqHB4eLpSD6VD5p8FkSguWpRGKLgEitalLseKTKVcaqHEwmUzg6OgIOp0ObG5uZkdJo6KOd6Bubm5mKyxpPWk5sRHLWZQHtrbmgoEqOXwneKVSgXq9DvP5HFqtlnhHlLZfabvwNnIpiDimkHepIdvr9aDX60G/37+0263ZbMJbb70Fn3/+eWY487FAd1BLbRQCrbyh7WFTNovkVU6LBiFGEpUvMRzuLoMthtNVqwDzui1TqbUpS6axR+Hbh9p0mnw148SWl1QXNOxw4YZ0fDnKEx5Qajab6lMAbIZMkY5kkww1PXflrx3LkiODzx8Sv61RHKS+s+mzMZxaPE/MN9RZ7IKN53zy4fnZnD6x5fcyncK+0NbVRx5T+8UHMdtI08/07xj6gqttpPdl5AukCY+qpcF1uuhY69yW2jbPGA75zqTr8j7BOtl0P02dJbpssoXPnSE+CxvNtmP7bKDfYyC2bEGImNAGKHyAbW67Z1Sjk2n4TzOuXPXQvnfRqQUdc5J8pPa/JogdatdK4Da6z9iJMaesEhLdtdqFexXv76Wwydi8KMKm0OrGlN9MPEp/l62/bT4L1zch/G4qV/J38e9sfSLlT+tRtnY3wZde07weo7559V2EiU8kHojh6+RtyPPE00XRX4zvYswNmhiJ6xtX2lXbBVdlLJUZL2Mb8jrXbIkkJrftBMHjfmkAJ0mSLDCEBkq1WlWtGnWB7wIcj8eX0gyHQxiNRlnA7ObNm5mSxNHv92EwGMDu7m52dCMFNwK1ziX6bRHggrPZbEKaXhw3e3h4CH/4h38I7733HvzWb/3WJbow/Te/+U04PT2F73//+zCbzWA8Hmf3/2Jd8+xclRREpHtZxzbmUVIxwI1Byul0Cu12G+r1OhweHkKtVoNXXnkF2u02vPPOO9DtduHg4CBbwWpS8nAy4QoXHR+hd1WkaZrtJreB8jL+xqDtjRs3YGNjA3Z2dmA2m8HR0VEWZF61AOWySEPPMlYxA+icSS7HEI6NZdCLdMRyvNvAA7KxYMrPJV9MSip9VyR82sGVTpIn+B0d5/P5HJ4+fQqNRgNeeeWVS3MizuUYoEUZlqYptNttePPNN+H09BQODg6MNIYEX+idwK7+zKtDmOjD/E0nZgCYF15RR6KrHITPPchrXGDVBhhFqIPD5EAJ4QO6UCI02Mt1kLyOaBt43ZH+er2eyZ6rAtNchov1MChHF5KijNGegrJK0KBiTDnlmvdo2/juLLcF27Q08DqbZP5sNoP9/X1466234O7du3Dnzh34y7/8S/jss89gPB6LR5XbyisCRfAYnswU40Qj7qykOoxtruXfSvlqHYC1Wi27pgG/wauQfHZ1ItB2LhqxdPNY+rhPmwMs6lnT6TQ7clfzPdXttTuRtYElE73abyh9scb2ZDK5VE8sZ3NzE1577bUFn1dsmJzrSZJAq9XKfA78G9tYLZMuFwuVSgX29vZgPp/Ds2fPnH4u6d2y7H4XqG/INV+3Wq0sPd+9TU8TWDZcY97nuN9Y0Pg/Ne2Vpml2ioCUl+Y+4rLroBRlGRcctgVEFHntRJ5HyLxN0+/s7ECr1QKAi/nl8PBwYTEZxnMqlUop7bKy8G4ZfOJXHWVqw2XTcikqqTFO+d/0W3yHR3Dw9HRiNym3mgbgzk7+zcnJCXz88cewv7+f3YWJiuJsNoNGowGbm5vw/vvvw+HhITx9+jRLg0Fdem+mtAvI1E6xnFTaVSXY3pITG43L4+Nj+PTTT2Fvby8Tvnh3LH6L96BSuFa6aWFrE8r0JqXdxpcuBzlPE8JfEp34g042TEvvMqY7rF0GSczAPecH6lB48uQJ/OIXv4C7d+/C9vY2nJ+fw3Q6hY2NDdjb24Nf+ZVfgcePH8ODBw8W8ll2kIrC1ia+bUeDgZr8TbS42oCWYaKPykptm2rKl9L4BPyk8ecL21jjAVnTHKAdr6ZgglaG+tCeByHyzZWfDx9S8N2uSXKx43V7exuGw2E2DyJteKQxvQ/TVS+pfB95LcHkVLell3QXac6RFj/Y6DeNFZe8AljewpDrCFN/2+TIskD7N0bwysQjtjmFvufpbXq3z5iMoQ/QMUR/bOWWEXQuc83Lkr6OsPG1yzaI1U5cZ7DJztC8XTyVd9y49A5NWt/yarUaNJtN6HQ62R2iCI1DNQS28Yp0xSyP5+urN/vSY5qH8bk2Tzo+OUxyx0YXL0daGJqHh330wZjy0TanmGxpXx3bpDPR+7NteUi2Usz5XZJNIXMS/z4WjUmSwNbWFnQ6nUuBFrqAHhdLmPTUWMC8QxYsXBekaZrxL+5KpieJYRtxHrddNZBXttr0PKk8TGuSrZJM4KfDUVuRz+VF2jo++Upj2TU2bP4vU9lSf2rnFtM7bGM6riU7nJevkUWrsJV8EUqbdmxR3jd9r9HXbXqtFrY+ov9T+euiCwAyGYXAOQPgxSJ86hfyXTCq8d34+OE0ZZWBZ6+CnVp2lKkNi6LFlK+4JEdDBE4CkgKGx/RMp9NLgzsr2HAsogZ8ApLwwQcfwH/7b/8NPvnkk4XvTk5O4PT0FNI0hbfeegt+93d/F37jN35j4dtutwsnJyfZUbR4z6pEQxmEwHw+XzgSV1J6PvvsM/jDP/xD+OCDDwAA4ObNm/D666/D5uam8f4BqjzSn9hYdlv6Gti2NPRHQq1Wg1arlSngJmiNO62Bj8ckA1ys0MZdvAAAf/EXfwG///u/DwcHBzCdTuH+/fvw+eefw2w2g6985Svw7/7dv4Pf/u3fdpaxbHDHrek9NXz4O0QZhD7yGJWRNmfRsmiSFNEiytcqUi7ZoFGOJWicRq5yfedKbdk+iOW0S5IE9vb24N1334Xd3d1L76fTKTx58gSePHkCR0dH0Ov1VIEHHxptwPxwHrK1Pw/uUEeBydFI02naysa/1IDh+WF51Ahaoxi45mfXd6HlVSqVrH9j7H506UgmhxfXGcow71HQXbBlo60o0OtT6Lwv6XdSn3OZFhMvSx9cF2jkWxn6lOsBfP7kvoSYdqZLB8Gre0KAMnc6ncJkMsl2goXQX1ZZHcs2t+mDtVotW8RAwdsjSS4WFjebzSytbXe2LUDlqzf7tEORJx5Uq1V455134N1334VGo2HktdFoBL1eL7uiy+Ukz2PnzedzGI/HzmtLyuAviw2UX+jr2dvbgxs3boj9Uq/XodPpwMbGRnZtmAkm+0Eq39eHJuVHZbBrFys+r9frC8fzA1zodMPhMOMF7ospE3x4XmvrutJoTpMw9We9Xod6vQ7T6RRGo5H15DubjkjzL8s8ExPUd+3rE6J94+Pzju0n4zxlqo+pf01oNpuwu7ub/UibsGjeeeeyZfov11ijzLCNA28LRFpVQxVslyEhORa0u2z4xMGP98MdiDjJzGYzuH//Pszn82w36K1btzLhg8LtjTfegN/8zd+Ejz/+GB49eiTSgAEtNLpssAUzfJRR2+ocF5LkxbFLeMwYdTpTutDA6XQ6cO/ePTg/P4eDg4MsyEt3eWqClD402niJp3W1IXd4S89ttEh5S/ngM2xXbOfz83NoNBqwsbEBjUYDdnZ2YDQawWAwyJzuLocq9g9NYxoPLuAYQND7H6fTKRwdHWXHU+zu7sIXv/jFS0H30WgE5+fnWUAZxwDeLbwsSO1B24W3kUbJlL6zlevKy5ZecppxfqX3EEtprip4G0vjniKEz+l3vjKWz1mu8Sl9GwL+va1dNIaeDVS24HxLv8E7pKvVanYihKkMyfDUtL2pvhrZ6wKd20z9KZVna3NpzjH1N71nTkt/aMBvjXD46gUm5JU1IXm45JKvTNXKFE07+fKxaw64aggJoJSp/lS20+OVK5UK3LlzJ7MB+v0+HB8fA8D10E1iY3NzE27fvg3tdhuSJMkcr77OaEmnKWq+8JVDPD2l1cQTfLybbDWtTkXzi+VQnk6n2TzearVga2sLBoPBwjVHlF5eB409QdObEDK3xEZIe+ahF6//wbL5zjpt2Sb7ryhZpbVptPn4pE+SJAu0AgBsb2/D7u4uPH36FA4ODmA4HF6SPTZ9VmMHmXRqTd0k/V7jyykrTL4qfNbv9xf6h9r20g40nlfRgTLJv4q/TXWS8qA/0iLUMvSlxKcunVh6X0RdNHIO30l2Jn1PbWDJfnXZ52WDpCe45BJvRx9dQdPnpvnfltZEq40GTX9odcL5fA7VahVarVa2+YryB556hvdb41imOk/R/grt2DLpjmusUWa4fATWYKzNmWlznEoCyUQADnhqCGlABQMeCYXHCdN8fvzjH8NPfvITePfdd+HWrVuws7MDnU5nIa93330X3n33XfiDP/iDLBjLUa1WYXNzM1vxFSoElumEqtfrWTBWk3ZnZwe+8Y1vwMOHD+Hg4CBTGJvNZhbARgFeZB1Md/VhQFNrAHDnvEbxNxkM/F7SNE0z4xGPAH7+/Dm0Wi3Y2NiAVqsFzWYTjo6OYDgcZquv0UDiZUuGPf6N6X13UaVpurBSlS94ePToUXa/y+3bt8V27/V60Ov1YH9/P3NQ4Kq8WEH5ENDxb7pz0UZfURO6jdd4AIz2J1V2qPFs6vMYRoGPsRfaRj4OFYRWGY0VWPEpm8ueog1mDaR2sDlAUJbS49Vnsxn0+/2FHQfLqJdJlzAdj24ClQc2uk0nFEj9yB3oNoOL8wTCRgt+t2r+0eIq0SphlYZb7LJ95joXltEu3OlrsiOuEiSnz1UG9gXaZOPxGKrVKrz99ttZcPHJkyfw/Plz4x13V70N8mJvbw/u3bsHm5ub2VyDQW0bTGOB6v/4P/3tC9+Aq+05n3Nj9/0yZQMuRkNsbW3BW2+9BZ9//jl0u91sByINnsTsg1XLQlPf5aVJyxPoa5D4KE9guwjYnP3ab31pltoAeRbzunXrFvzSL/0S/OhHP4LhcAhnZ2eQpqnz7uKQcesThAUw1/cqz/8AZvrn8zmcnJxkwQ2+q20ymWTXwACsdgEG9T1oAi7Se356HtqYtnlPM65jzilFyFjfsW8LclPbXQq2Jkki7jyndjLlNVM5IbDVc5l6ny24Sf82yUvpG1t+WKbGjo8BXzmtHa+z2Qzq9Tpsbm5mzymfzOdzOD09zWQSnlyK5Wp2c1Mai/avrnF1sLYLdXy7EIylR5nwlSZSQBbfc/BASehRfJLzU3J0cmPVdO8IHsn65MkTaLfbsLe3B++8845RyPV6vez4HNcl65xO6T2m4e3J04SWwwPRUrt/+umnMB6P4a233oIbN25Ap9OBVqsF5+fnRmGLgXLcMYU7kGNf5u3TJj7CXtM/+I466yWFyAeUP32MXZMyRn+HTHBJ8uIO20qlAp999hkcHR3BW2+9Bd1uF77zne9kweSnT5+KeWgn5WUiVj9JeUqKmZSGTzhSnnRneaVSyXYl407l8Xh8KRAVWicNYhs6CF9jzqTo+wQ+bfMQbUvT+JHmOJ7OZixp6DGBz7VozLqO/DKV63JmUfpxRSRdbEIXK+ECApxL0jTNVntXKhVoNpsLR6LbxotUX06PBNN7ehKGBO2cqpGpeRRKlwF5VXAVaY4Bn3qjrOHf4OkZrh0/lM9cDgAf2nzTm2gqCi5H4CqdlRy+wQJpAQjWFXVrPs+gbFtWfX0dlahH8nvHTbDNpzwdnbOpDh3Kh9KY5PwWol/TuxmbzSbs7e1lOl0IXH2Qlxdc9pVkl/pC+i7ECS7pk7FlEOpZk8kEarUavPrqqwt38j148ADOz8/V+eFJSfjbZlcAlGcxh8v+cenWAJcXrtHx5KqXL39Q+Sid8mKqE9JNf2tkFk3H7QnNt6Y6aGAKKjx79gza7Ta0223Y2tqCN998U7xaxFWepi4Al3UAaW4y8bZL5tPnV1HHxPtha7XaQjAS643PKdDOk1D0nK/Nn17ZYvKJUX+Sqe98xqdvGp5WM+5M8synbFt5pjGrpVXSSUzzKv1Gyo/bxpqxaKNJQlFj1tROpjrY9MmQMunfEn+E2lKmOYSWw+soyVOXrQRgH3vNZjPTV20LBlGf0daP0msaazF9KVcRy7Cpy4Ki6nkV2lCST2hfcFzaGYsVtCnjJkWL/k8LdK0KdglMl+Is3UcrCaHZbAYPHz7M8nn99dfh7bffvnSMC36Hx8u22+0gZcIHLsFPYWoP7piWgrGPHj2CR48eQaPRgHa7Dbu7u1CpVKDX611SGLAcXLWKSic6YWIEYyWh7ZqE87Q9z1vK08TfNkWGp+N5xHJuhCoWAC/GIRoHT548gXq9Dq+++ir0ej34/ve/D51OB/b39+H58+eXxr7LYVoktAqsC66+sMk2bd4mxRnbE4Ot9K7m4XCYrUoz0Rji1KL8bXPC8ed5xpp2/Lqcg/iOO35M8OlbWp6rTemcGOo0kAxaSfEHeLFi0UcJlug1lUHznM/nMBwOFxR0Cn7EFvLvZDLJlPk0TS8Fjk2GItctOD28Hrb68WAA5rlsRV8zLvnccV2MijUug/MfOt6SJFHdj5rXyFimgaKlVUpn0i2uy9ig9eBHnKIcTdPFRY3YRnRX5TLgK5NQ/ubtK5tOQu0yLV9oeVFjZ/E5lM9d1L6q1+uwvb19af4sM1y2vdbBzWFznuL/vBzJtip6nkSZjNcz7O/vQ71eh0qlAs+ePYP79+975ed7/7WmfkXIco0eTX/T59xPwW1L+h0P0Njyc/Eif27T81xzq8mOd/mgTL4A/N/kw4ndh2mawvHxMQwGA3j11Veh3W5nVwz56qG+5Wp9f/SZzZY0+V2uEvAOYxqIpXXBk/sQ+L6Ie7G1bSjZYxya0xz4tVYStAu2ikCo/0IrIzVlmXQXja/ClQb97FL+Wr+Ji4Yy6OMmGkL7ygVXnjZZ5uOL1DzndfSxkWz2Q6PRyBbeT6dT427qkM03NvpCfGeuPK8ali0HfeXPVUCZ6TfpNTaaxWOKfQS4xmCeTqcLCrQ06Pl9rz6QBiuu2EI8efIEjo6O4LXXXoNGowEAAOfn5/CTn/wk+/7dd9+Ft99+G/74j/8YPvvss+xbKoxqtRpsb28vBE/KCjxO2bRTOE1T6Ha7C46F6XQKN27cgF//9V+Hhw8fwoMHD7L0NKC0DPAAhmnHICokvuDGlckZ48J4PF6g48mTJ9DpdGB3dxe2trag0WjA8fFxttO6Uqk47x2WYDqONxQ4Dn7yk5/A1tYWvPvuu9m7e/fuwdbWFvzkJz+Bzz//HM7OzjLer1QqsL29DZPJBPr9/komScko1BqaeZ3dJjp80s/nc+h2u1Cr1bLVzTdu3MjS4fHW2gUtsRGzjUz5mZQsE59r+YzKKdcYXzZMThz6HE844PdMm2Sc1qGEaXkwFhcdNZtNGI1GcHx8nMk0W36TyQS63a7aqKAyXLNAS6of0gygP7ad5+XrBHU5LCSHk+S4ozwZU46vocOynPwALxxQyOeoi4XMF5SnQnUwOkcW4Rxe8/Jl+DpIJVD5gv1uk02hfL1KR8d1crLEBJ/Xbfp/EePPFmCSsCwZwE8X4DIyJl3VahWq1Wp2TDEeC8v7wfeaJROtq4akO4fQyfVVHwdyKGy8qnXY+8xlywgwUT7D08lMZR4cHMBHH30EJycnCws589DnCoKUkYeXCeQX285QgBeLd9rtNmxsbMDp6al48pEPT4UE+Ux52MaL9L7ZbF46hhiPzaYnwS1j3PuC34O5avgG0F15Udl7VfVyW2Ab37vS+OSbF6GyUCOjffxeCCr/q9UqbG1tZdfM4aYqnEsePXoE5+fn2TUNeOJLSCzGdLXTGmu8rNAupDDeGRvTwUKdCdIKylgGAAUvq9vtQrVahdu3b2dKxGg0goODgyztF77wBbhz5w5897vfhUePHmXKEl1dUqlUoFarZceK8npi2ba2oGk09fTpC5ovDeRwA3Y2m8F4PM4chtTpg8ff+BzRFArNxKkxlEyToU97awOyUkCD5nF+fg5JksDOzg40Gg1oNBrQ7XYB4MVqYcl4l1ZQmII3PvxjAvb54eEhjEYjuHfvXnaXyebmJuzs7GTB+PF4DNPpFDY3NzPnRAgv5wV3TLuCWzaFzWVQSkGuWE6f+Xy+ID/q9TpsbW1leZydnRnLyYuQIGeMPqbjS5OfrX+1kMoJmddcfMWfawKkJppQDuOx8Br+tJXjKps6BabTKQwGg+wkBNvuXORhaWW0q855+Qh/S3LAx2jVpDOlz+vkcvWfb/6mPK+qQR4TpnFZVNtw/SsPigie8ry1emgMenwdF1eVf2l7hco7E79K/ZZHprrmGcnhnMeJaAso0+/yOip9bDHtO1M5MfgU20KrJ5n6I4QXTPqWNj8fWm3pMXCB8zvf3aGZJ0N0zUajkTkoUWajvS/du8kDDlfF8Wgbj1r7kratyynu2y42G1+DUFnC/Qwu2cHn0aLmNJo3v3uU0tnv9+HBgwcwm82CFgdowNvR5B+x/U/zug6gvkasq+TbwQAIBjI5KE9pxqBpHLt4yzbfSe+lcqrVaiYrEXhiEsDlk0CWBZ+6h+hlLtmppcHX1pPylvJw+cdseXNaVjU+feWF9rmvjqb1FWt8NK62dPm5fPUvKksqlQp0Oh1oNpuZjoMbcebzOZyfn8PJyclCPlgnH/6hMjCmPnRd5oll4rq2Wdn9WSbdh8oIU98Yg7GmQlwGjUuAoHFFBzD9ht5Hmtdhi+egoyKUJAk8fPgQ2u023L17NwvGbmxswNbWFjx8+BAODg7g9ddfh+3tbfje974H3W4Xzs7OssuvMV8MngwGg6C7/WzI47yx5Unvtvjggw/gk08+gV/91V+Fvb09ALhoM7y3EuDyEQej0QjG4zG0Wq0soI2KfywakQ76TOMA4oqIifdoHqYBkmew4+rAfr8Pp6en0Gq1xHuktP1L6xXLuMLjtHBMNJtNmEwm8Dd/8zeZ8+HLX/4yfP3rX79EM90Rvqwj9GxwjZWihXdeBzV+PxqNYDKZwMbGBrRaLSPPFyEbVgGpXySjhvK+VjHUOGBdyrPrPtJVAI/ljdX/VHnmASNU3rvdbrZoh38bqnBju0rlhhqCkt7hcrIXNZZCHW1FlFtmxfU6YzKZwHQ6NTreENx5IsE21rg848GcsqGsdBUBPn8s+3QLXyDv4KlCKJ+3trZgY2MDqtUqnJ6eAsCLk4eKsn34T16+4c5JOpYkRxhNz+cTST8BeNG/6JRGW8q331c5Rvh8xB1t+KxIvWh/fx9u3boF5+fnMBwO4fDwMLsWAUC3e8y3Dbe3t+Gf/JN/AtVqFQ4PD+FHP/oRfPLJJ9nC0/F4bL0LVoKkh5RFf5/NZqL+ZQPVFzc3N6HVasHe3h5UKhX4+OOPYTQaQa124VIK0d9ctFQqFWg2m9lpLvQOdg0vSGNZE3Tmc28I74fqtTjWeBAW80T/AvpkZrMZHB8fw+npqfXkNo2vzkQPrQ/P0xfXSR9oNptQr9cXbCYMWrZaLdje3l7QBelctEo7M488wv6bTCYLJzeUvV9jBoukvF1+Cy5/JPjakaHfcpTFfuCbXPLANwgb0p4+c7vLZ4k0xOgH3BmLc3MR4DrzGmsUgTLIJQ1cY1eaFxZGpzSQqGByDTJJuZMMWZ6nyzGuhWuFSpIk2VZ8DH4MBgNIkgTa7TYMBgMYjUbQbrez44kAXhguNLgr3Q3B664JCvjUwQc8H6QFlfp+vw+9Xi8LNOPZ8dT4qNVq0Ol0YDQaZUdnUiVi2YLX5bjU8in9PqQOrjGRpilMJhMYDodQqVSyu0HpDmXpe1e98O+QcUHL4GMT6UJHG8CFQ+Ts7OySQYd3CyRJkvHDKiZfzguSA4m3k82pZsufp9Hwl41m/owfRwtwcRx6o9HIjrfk3+ThWRNcPB17IpTk0zLqqs1vmRO/RDNfbY1/F2WYoFMAF1nY5rhYtEh0+Ro0vvMtb0+fMrXwybcoGl5GmNrSJhNijXM+B1FHqtb5Y3MCmL7X6hC28pch61xlXBf+l/QRfCbJHgqeRtPnRYDSgAs4qU20jLIl+R6ih9gCsZrvbHRKTlUc98t0sOcdvy6+pM9D9DHTc6kstEfRwc/TamjVgNrxeGcsnpiFC2ipnUvtX1ovzp+cXs24p/nEgE0n0swXCD7m0H/QbDah0+nAxsZG1obLkt9p+mKxvsmmk+og2Rba8jTPaDl5dA3THMDrkaYvFs7QPkDfg7a8UEh9Lo1R2/cAV3/eR9mAf+NCHASOmVqtBmmaLuxaxv7k9kmMftO0q9ZXYfMJ0PlumTZzHvj6cEL9CcvQuW02g/b7qz4GbQgZTyb57kpjy8c0J1M5aOI7l+5q6sN6vQ71en3hdA8qg9Cvb6qjq055oZm313DDt52uipy+KjDp96bxK/WX91IJHwVKEkScWFSsi4J0v9xwOISf//zn2f+3b9+G6XQKu7u7sLm5KeYznU7h9PQUms0mbGxsZM+5oC9KeGgczhy2vmo2m5CmKXz3u9+FdrsN3/72t6HT6cDW1haMx2Po9/tw9+5duHXrFnzwwQfw+PHjOBWJDKldfIV4nl1eJozHY3j27BlsbW3BdDqFarUKOzs72X0ueMwvLg6wwfeORB8akyTJjqyg+MUvfgGfffbZAn1pmsJgMMiOvcBAc8zd0SHAMah1fplkkW95mnJoWWgw1+v1zGir1WoLxxPj6rX9/X3Y29uDhw8fLuxWXxViTN7LDm7m+Y46FXzy0ji3NPQlycWO9Uqlku0CoPOka96h9eBppfEyn89FB45tTCVJol5tSZ2aNkOHO855XShQ5izDuUnzRCcYRQhv05XksR2xL7uyTeuPfFLkymBT2bhDNs9uGvp/6HxFf8fWdWIA6Vl2EGvVoM57Kgckecn73teJssb1hKvvJQfAsvjFtZgAA6L0lKUQ2RTyTbvdhlqtluk9PjDJqDI6EKW50Oe7Wq0Gs9kMJpMJbG1twc7ODuzt7WVX1uDRpJryTUF1DVBH9ZkjJLmq+QZpXUVf2mjEHdoA7tOCarWaeC0Yh7aOkh2hpfu6gdpi0+k0OzUuSRIYDoeXbJPZbAaDwQCGwyF0u10j/7runi0Kpn418QbdoILBHZ42pi63LJsmbxmm79HHGFKGad42+bxMbaVpQ58FKrH7Q5tf7HR58tD0qY+eHjLf0DxpcLVWq8Ebb7yR+ZHQXzscDuH8/ByOj4/h/Pw8m7+lqxg0dEp+IMofeebQMupTa6xBIflWOfA0UhsWRpAtYKqBtKqDO1X5ADVNGi6aTDDRzf+mKyv7/X4WJBsMBrC9vQ2VSiU7gufk5CRb/YWNSlfU0vx8aKXpXG1nqqNvvqggIf2TyQSSJIHDw8NsNTBdyVetVmF7ezsT4JSp6L06MQ2XUAFsa3/+TqsY2RQbUzm0LLqj2BRMXaahKK3UoLt18flkMlkwsqmQMRnTq5g4eRAnBKbVaMsAHmuNO2GRno2NDUiSBJ48eSLSyBGTZkmGSg592/e2/vDpK6mcUKMmFJo5i8MVcOTPpO/pIgw0vGxl+8wZtnaVnLc2aMa9iW84L1De0bZhyNiX8s8jv0KN31UatS8L0jSFarUKrVYrk7ODwQD6/b71G1s7SgE0Cvot3QXholP63vaMvvPhXY080JTLvzfl65oTJDquCh+bdEtf+NS3aD2LOpBQF+SL7tZOkheYzWYwHo+z04bwmMoiedglD/Lm4UoXGlQLhUbn5M9sMDlOpLJMvgQXDTbkGT8hATTbNy45Tfsad8hXq1Xo9/swHA7Fe33pNyY7IiQwoam7ba7xfR6aNrZNRtux0+lAp9O5pGeMRqOFRX4uYHva2sQ2v5n4RNtHVxnYxtgmWB+cO3HXMrWn0H8Wu+4x/LWafGidXXnFxKp1wTz+SNvc6bLXNTJbO+8V5SMqEpKdH2Lr54HNdxEDvv4WDo0eiIslcNEbLq7CHfymcW3yWcWou6/uedXni9hYt0d5YOJlre8HwBCMlTKmCoYPgehEpmWECtU84I7ter2e3e2Dd2wAXNTvW9/6FrzyyivwS7/0S3B+fg5//ud/nu0QnE6n0O/3s8uw8Xe/34fpdBqlTtwgCnV0S0BB3Gg0Mgdlmqbwne98J/v+lVdeWbgv9N69e/D666/D9773PTg6OoLhcAjVanVhd+R0Oo26OzLGBGpqD2582PjR5GS3vaOGqWllIAZpbfcf8SBFbOBYpkfqmNqtXq9nweRVK8YSXEaiBBrwAnixCCEmTdRQozIUaR0Oh/Dw4UPY3d2F/f39LDB+584d6HQ68Omnn8JgMIhGU9GQHDDab/i48nEqFIWQhSbcsYV/2yC973a72WIYzGc6nS4sJOIOAeRfDb2uPpLaPu8OP14m1iPvuJNkuMvQpXynda5p2xX1Hq1Taq1YF4t6vQ537tyB7e1tuHPnDnzyySfw0UcfGdPb+CFWEF9adKAJAksyhTtrpXKKgLbutgUWVx0mp+WqHdIhfZ4kSaabos6CwcUkSaDX6wEAiEecvcwYjUbw/PlzqFar0O124fnz5zAajbIrSvLK+DKNHapzJEmytN1ckl7F6aLpQsug8zbV26/TPE31LayvZhclRZqmcP/+/Wwhu6R7+fBFyDcc3N9kS5enDOR7emKNNugSWja9IuuLX/xidkQ0YjgcwvPnzzP/DpZr84P48vN14f+8oBsWJCTJi5ONAHSLOfA7LUyBEx9ogn00bzzy1AWt7bMK5KVLY2Picx97XMrfBC7jtAFZjjL2Ea+bSZ5L8ivUX8TtKlMeeXU4E/juWXpHuOt0MjoH0DLm8zn0+/3Mv4uLQzg0O2J9+MnVB8teyLHGIsrov7+KcPlpqA2twaU7YyWnjvR3DOXSBM1KFK3zlH9n+hsbbD6fw8HBQSbA8IhI+g2uEJcCeiZ6TeAGiMmRL9EtKWNaxxtnEvxmOp1Ct9uFhw8fZs92d3eh1Wpdqi8tCwW9rxLma0D7Bi3z0mLjOZNDgOYzHo+h2+1m7/HooNFotDCRYnvSyczEU76Kgo+RyCd3HjTGyZwKGzTkV+2scAVkNbzmUmptTiBTXqZvaJtJDvlarQZ3796Fzc1NODo6gul0CpPJJKiNNcpRTCMF//fN09bemvxCjRJfaOSsb5BFes9lfZIkmZzlO29swUXbnGErk+fl61zQOE1N7WKjzVSWqT1M+dMgkZZXqZy2facdV2tjpHjQuQ0XONy9exdOTk4u7ZB1jVPXO59gAeUj+r+rLFu+pu9DZaFJNsWaN3zKX1aZoeD9yd9JfcL7nvOHS07y/ELptr2TdBkT3Sa57SvPedkUkq7H0/IFPra2N72X6EdZwoN2qBOHOKKL7FsJWtlgGnshfWkDBrYALmykZrO5cM+ZiRZOl60uFDwN3oMagqJkkq+/w5VPmqZZIAV533eXXrVahUajAa1WC9rtNiRJsnBSmC9NCK3PgsscPkZD4KtT2pzNEn/GBuZdrVZha2sLms0mnJ+fw2AwiGKD+3zvksE2OyTEPiwLKA/gby77Ud/EO9cpYvSTa841weaXsJVFf9uOIA7Jv2hobLwYc7DLzrfJDpqHBia9SFsPm666CoTKHRNC7Tjpe18b3sZrfO7i8hLlhqsfeR5U9jSbTajX66LuNp1Os+PuXfOZpq4hsOlMRc6dVxl52n3dpsXAxMe2OdDUFwsaAl81YXIo+KBsAp/fkyfhww8/NH6PwgwNGfq8SGjbUHK2S4oIN8JarVYWZD49PYUf/vCH2buvfe1rcPfuXWu5uJIHjzMrAnTiMK1G1U6gLqVJC8nwoEdXDwYDGAwG2QR769YtqNfrcHh4CPP5HGq1Gszn80u7il2Oj6IcALhbGjEcDmE8Hmf0YVAelfHpdAq1Wg3q9brxbrxYbe0CdZJp0wMUO3ZteSOfmHaUV6tV+PrXvw79fh++973vQbfbzXbwXxVo5ZaPkbSMucTGsyEGMP0uT154EsFkMlk4XrxIUOMBHdx0IUYsOUoNB5+5zpXWZRxrggkSvXROlYLUpvqURRd6mXH79m24ffs2/PCHP4R+v5/LqcUdiyF52XjExwGkCarERoh+b2ojkxOjaDlXdPvw4B19zumgv20np5QJIe1n4nHJ4UsXAdL5KNRx6bIT6HiSFq1SHbjM0I4l0zNTHhJC2gKvzKGn0jQaDdjc3ITBYJBdoxMTlHfoaTVbW1vRbNeyjVdcuEcdtPzYcQ1qtRp0Oh3Y3t6Gra2tLMhUq9WCA0KuU1E4H/KALE3n8llxmWvjWZMuZ6KXO8djBNxMdGF/3r59G2q1GhwcHKzcNgzRf8rgG4w1908mE5hOpws70CqVSrZoAcuicicv+LwZsy1t+XE/X71eB4By2jbSXG/qb1udtTxi+951+gCXGaZgmWQr8HTUVrehDHOV5KM2QUoXUofYwd8Y3/gEYmm8hs/l29vbC4vLaNrhcAgnJyeXnmvAbRju97Dp1cvyU5U9zzLjZatvKKQ20thLtrF9aWcszZhPCqZCNNFfjbPJlE9eJ7Ypfyr4MMCnCZ7QQJStcbWBQS3dGkebadLmeVGFntYdj2+mitbBwcHCHWu4Wnk8Hmcr//IOYJdjnedvMog0bWH630WLywnkcrinaQq9Xg8qlUoW0HcFfDg9kvJtolMLemxuml7s5sU7gbBvsc+leuFx3zGU2Dx8RAMrtvxM77mjD59p6Na8s6XDI6ba7fbC3c2URjwafDqdZsem87x5G9D3edrWpFhp2scnrU/ZprJMhoq2DIlekyx3yRgbnS56uCxrNBrWi+ClOdtEuw9f28YQ/Z+W7/rGVb6GZyR5pOVzbsCG8qZrrnCli40yKNNloIFjMpnA8+fPYTqdQqPRgHa7veAkWyZ8y3TNIXn1Hf6tSXfROAhCkKc9NGl9bIRVQFu2JKu082+e+TsmtDzkO4/66vIampbNE9xhbwLytIu3l9m3Jr1C60OIUbZUnkSTb95ct6aw6Tm+vJ7X1jDlr3lO/SCucm314s5ak//BRXMeHYLWQ9LBeNq8domt3WzfmWjiQBpN/qlerweffvopPH/+3LkrX+NDkujUPHchz3dFymLfvqM8irYYPeqb5jebzaDb7WbPxuOxUYfL40eM5X9zPed34PH6FiXffevn8oPaytLS5Asqk0xzls2vyG1s/I2nFdATG10+EtPzZeuDCK7zmHwJNtpj0I/5hMxX0jteJ1Pe+FwzniTetqVDGYTg19L51kXy/fiA5qX1nWrz1cBHnpTNj6HFMmzxMvp5NIhBN7XB8H/+XtIPbbJZvDPWBB9h5xIiLoMztFwfUAGIgQ+ToztJFo+HxLRaRdwl3G0GnwlcMbQZhvg33dGUpmm2MoYeEcUd/gcHB3BwcJD9j6thR6NRFrCLNTB9haTJ+MdJTZrkeXrehiG8pnF8pmm6oJgDmFcmaYxYW9/7OCG5UBmPx9kdsbjrFe++6vf7lww+eneYVGYRDlEf41FTnkn5c9Hgk55+x78ZDocwHA5hb28va+tKpbIwDqvVKmxsbMBgMIDhcOjtRAylt4i8+Pc2Po81edJy+DtJ/nLapMk3z7zk4j/+DHc18DmK0iDd7c7bL5bBot1VIH0fmsZkyGjqZOr7GJD4y9eQizE+y6Acl4EGjul0mh313mg04ObNm9But73z0RrpPI2kJ+L4trWXbzmURhc90jcu43iVfZvHAabJe1V104x7qtem6YsjyXz05VBZtwwHnakMF90u2mz6vu1ZHvjYthQuHYj/79L/fGnwbUv+3FZvkyM6FJIsK4pPtTuLJJokG9SUxgcax6iNLv4N6ot5dCSb/8QXefhEWiDv4ktNWlM6187eEEh+DAzGcpq73S78+Mc/zk6myiMvNTQt+9ui52UfHUyy2dD/QXf3I/DeRhdCechGt88Y0MhQ6iPl/KkpJxR5fUex6cmrh2p8hfgNl/PSXISnFeBJEpQPtbaBjY5lwKbzaHaKasePqe3pXMyvjvPJUzPXSs9oLIL2tav/0BaQ0uC3s9kMTk5OFtpT8jvbfC6hvlHbO5c+6Ytl+ZmXjWWOS1+b8ioiFt0ae4mPaRsuBWO5UAo1LK8S6Ko2KtRRYA2HQ0jTNFt1RO9/WDVDFuFISJKLXVgI6fhZGhxABQB3U2LAoCi+4XyKz1zACc90NDDNx2QoY9kap7vG6cOdajwAg2mwTPoOeRYDI7HaG/Mbj8fZvU34vNlswmw2uxSUxTpIdSxijLiMEN+28KUxxEmOwP5EWcPlbJqm8ODBAzg6OoI33ngj233OF0nECN6ssQg+vlzGi69y6qvozOdzOD8/h3q9Dp1OJ3e+NK2PA5x+g7J/NpvBZDJR58vbK49T3DTGXcaQlFZLRyxHsgtlMACuE3C+pzoMylRccIQnO+RxrIbyhw/PFo2iAxum8laN2Dp0EWmXBdQx0M75+OOPMz1zNBpler/rdBcKrjO7IO0w0jgxbfMC1d1xnFMbgOr8dBzgiUj4w0+JqdVqcH5+Dj/72c/g008/hUajAc+fP8/ar4x9nAe2OdyVXoNerweHh4fZfd5oZ6DenDewg/epAUDGw/P5PHNmn52dwXA4zFUG/o6lM9B8QvNLkmThnlest+9RhQAAg8EgOxHJRpOp7012tQb4Lc7ddDGw7Rtalgu2NKayipjLsF2wjrSdBoMB/MVf/AX0+/2MXzU0aG2GImziZesXGsTuNxz3Jh/YVdPvyxrAiwEtP8buL60MooE6Sgs91Wc+n0O32720EIbOP2XlOVM72Oww7oMNqZdJl/TlZ5/vNHTSert0B5y/qZzZ2NiAZrMJo9EIhsPhpfd0rpf8Hrbgr7ZtTHq5bz6+WLafuazIQ/NVrG8Z4ROERdT4A1egK6ZxIZVpS29zvvoYhaZ8+IoRuuOTGi50taIvNA5r0ze2NPydb5CAl0frSB2ZNF96Nyr+nyQvdhDn4RNpUjAF+VxOeZoW6UNnkslJxMeBLTghQdtn+LxSqah2mlFatMqI7xjBMvAYbnqMMR5bbOvfVSp+IeNSCrzZ2iyG/KOKD/Y9/j+fz+H4+Bi63S68+uqrUK1Ws+AXjjUXD2iUKtN4CqljkQGFEAPeVn/t95oxHCJvfWhJ0xRGoxHM5/MsGMtllinPkEAtpnfJK5T99BuexvS9zTHvCthqYGsPE51SX0t5aJ1da6W2PEB9ht/9iPpeHn0O4DJPu8YO5+9Q2alxIIU4vGPp+C8LeJtKd4xqdFRb//jq83nkD+omGLA6PDxckPt4lYn2PtsQWlBPR9hOg6DfaPidOjip7kd1KUn3N7VttVqF0WgEvV4ve1ar1S45FGPOCS691KS7hMoX07ys0c0kGWdLPx6PodvtwnA4zHQfVxlaJMnFYtxGowHj8TgLxqbpxYlA4/EYhsPhwiIzF6R2suk4ITSHpJF0YDx1ZzKZOAOpJmBbURmgtYMlfrHpZaZvcf422c2+9roNmrk9RMf2AfWzoP09Go3gk08+yfoRg9M2OvO2O8/rOuu5IX4EyitScKwoXS+PL1KTxpb2uuuKoWNbGi/S3668+LyLchz1i/F4DIPBwOrzsNUhj2wsAiae475P21V10vf8vQl5fF95ofUbY1vwRcZ4Bc/x8XGm39BvOD/RXcCmHbahtEvPy8Jj1xXXeT6+SnD5OCUsBGPR+HUpEUWgDIodLx/bge5I4sKvTOCTkNSmyCRYB7rrkn5LgTtI8JvJZAJJ8mJnLPINPYt+2UKX1hl/+NFL+BzpxwvOsQ7VahWGw6G4Y9YGTV01xrmvco5H4xQ5biaTCUyn0+x+4I2NjcLKKgIxnJPaMqRytLIT+7Hf78N4PIbt7e3MGdpsNuH999+H09NT+OijjzIHVUyHD8fL5pD3MUhczlnN99QJjHn4tDfK5PF4LK7AzsMfEh15+c1Xtmnagy5mkIwJX+B8YcuHz59Ih2buxfcS37xMY21VKFJeSpCcL9qdtzRQlIevtQEq6TtMbwLle5+819CBywnJKbeWG1cLMccIzYcHxm3BGK57FAk8gQBlBaWTOhP5qS9FgAZJxuMx9Pt9mEwmavmqCcrFhCYQogW1i7X5YR/hqREAF203HA6hXq8vnBBm+n4VsAUlfKDVF6g9UKQ9hgthqtUqHBwcLARoXbQVBdOiGJ/0VxnUd8TB9aPYvLGK8UWvabuqCLW78tRZsvO5PLblj3NUu90WF3rR/ADg0hVXVwUmnZa2n6nu9HdIuaajnfPAFQTHBUWcJ31PlsR8BoMBjEYjbx+2D90ca7tv9Vi3/9WCNLbFnbE+GcQghk9GeRzIPo4mF10oZHA3IACojsTJC9fk7GMgmL7lSoGUL9ZfUiB4njanYSxBITm8pf9pemmCxXrRSRAVanzm4smilFFXW0l1dK3AkNpHChryb6gBj8EYm4JnUyyXNVnY+sU2rnwcm1pHtW8Z2L6z2Qym0+mC42Nrawtms1nGo2j8ads5RGEKdfaaxozUbhq5HXMMSnlpHJimv0P5WuMU5e1P7/tGGZWnfOlvKT8TrShDpTnBlp8vfRoa8bm2fzQOtBBHikkGuoyy2LrVWjm/DNovKGdj6XK8r03jS/rfhhj9aOMv21gCuH7O06uGVY5jSf9f4zJsOgVHEYFYTovGhogVkJX4gstCKksoL0nBZBNi2RXUVsXrd7a3t2FzcxPOzs5gNBplCyJpeSabl8InKCe9L3Ks23Q0fM/rhPSg7ac5FYl/K5VjS+trU0n1kto/j5Oefy/xr9R+Uvl5A8PU6U77waS3u/JcIz9s/ieeRuJ1k50s5eMDzXeusqU8XTK/CJhkqsbHYEpL8/ZNG+Kv4GVp5hROT61WW1gEw4Pj3CdA/QWmPEP9KLHlh0Znovoop8Fn7tD65zQyPQRIN/U30/nWZ4EY/kb/Id8NG+KnDEGeuciW5mWfp1zjlOvYa6wWofy6EIwdDAYLSndeYq4aY3ABKK12q1Qq0Gg0YDKZwGw2M67SWbUTBcB/dUuapgtH0pp2XeKOPbpbCvmmqLtieZ3yGjccGPxqNBrQarUA4KJ+/X7fWR/NjioTfHjFZAQuawVcmqbQ7XaXUpYPYo23GHIrlgHc6/VgNBrBX/3VX8HW1hZ861vfyt51Oh1oNptwenoK5+fn2XPXSvVlo0g5qGnnvMFaUzCN8kksh6aUB93xifc00/SmbwDCZJEGKPMBLubHVquVHXknOYeWMRfyO5i1/Y79x4/Zl/JHuE4ikPjHlJ7Pn7HmzatqvCyDVyaTCXS73eykj16vlx1VCXCx2I7ykgtUBkiOId6n0g7zIlZk54FEW6g8MfE1dTzQckxYtU7tQwPWGetEj5dcNvKUiWMB9X0E77Oy8O0ygWMY5+ZKpZLtWqtUKpl9SKEN0mkdhQDFzfMc1D40vXMF/CiQbs19pfxKhJjodDrwu7/7u1Cr1eA//+f/DOfn5zAej7OjX3EXbQzEGv8x8+G6LAXt63a7Dbdu3brUX/RoXPoNdRKbHOGh97PjWJtMJjAcDhd0KClAyssNBT9NTMO7eW1K1E/598PhMHtOafF14ofSVFTaZc2RoWWZbEIpyOajQ0p5mlB0G5kWM1AZYbKVioImEMsRyqdS4JynsbURgL3vMdhmS0NtQryiqF6vL3wznU7h7OwsS1Or1bLTCnlesRHD5+HKzxUQ99E3pDJpOTHtbxdQXsfyWXL9HAAu+TQ4f5tkl42nTeUvAyZ5XYY5owgaXHnGLO8q2dZlh3b+ltIteO+pkNNmHANaZWTZQV4shztNuRFjc9zTvLhD37XixqYM+NbBJ+BnMtAkAV7UILL1tU+dbG1MFSfqbJCOjZBos5XnUi6kdD4KQQwjz0SbDaHOnyJlig9vu/LwNYxC4cobnXyDwQAqlUp2pBoa3cinrvwlI9GnXi6ejCWLpSCeLY1rfOali8p1PkZN9Nlgc0rZ6KXl2e64dsE03vO0k+QAWwV856A8ddZ8a+Jd33Zath62aiyjnujcxBXEuIDApPP49IFJ75P+9xk3fPe5Sy+UxrppvLvqZ5Nb/G+tcyzE+VGGMeA7b5ogzXUmfdFXn4wFThv+L+l/IXOJzbGRpml2FB9dHGpygFI6OF+Z5m/+t0SHLY3ksE2SxKqXlYGHAdxyyuSMxnS+DjsJNv1uMplcOm7PpRf62AB08RYGEieTCRweHi7c+8vL1JYRwx7JmzcHLhTA8YE/PvMaBhHQDsEFB64xosnbBj6esQ+lOvC/NXONbTy45lb+TqMD+LSLVD5f3CONV5c9Qf/38TkU6XtbpnxcVlkuvYv+H+rvi9EnrjyW5XPVlG2yxzV2tKYsX3vfV+9xySLpfZIkC6f3YV6mXbG0nDy8FQN5fGqm9LH8nvyd1P55fCWSDmHym9B52QWbv4zOeTZdzkRrDMSWXXl0jFiIQYOWl1z2oG+5vmUtG2WgIS/yyjbxmGJUthuNxoLCmySJVzCmqAkghiNZA6wrHg/aaDQupeHC02fAFq3c2kB3PJhooIo/flOpVC7xAL8/r0hwx6lkXOA7/LHdHYt339AjC0OcXBolK4bzrExCq0y0uJDXqb5KTCYTuH//PlSrVeh0OjAej2EwGETJW+OQySOnfBxlZUNe2kPbTeLVNE1hPB5n90ah3I6xM4bLxavSXzRIpaFbktG0j0x95TqKGfMx5eHK30QrzXeNYsH125Bxy3fK+BjXSIMUSGo2m0E7tCQnTShiGIGaYJr0/LpA0k3pc5ONYGqXEJusDLA5ktDmQF0HxxQey0l34bnkNQV1TvGgKU9Ld67wOeaq8mVZ5hE+V0vteXR0BMfHxwvjwka/T93Q7ptMJtBqtaBer8Of/umfwmw2g9FotJCnZuejprxVAsvf3t5eONEKFyK5kCQXJ15NJhN4/PjxJbnlO7fkmYtQ38W5EMexCaarXDSg+WKdbe1FfQ3025iyGW0AAIBGowFJkizc57vGy4Ui+90ko01BpFVDMz9o6AxdTKHJ23X8LNVHJHmTJMmCHO/3+/D8+XNnOWWZ+wHM7VREkMmFVQTy+I5Y/Bv9OaGnHPINRDbfE+etGPbhGsvBup/Kh1h9YjzXEgUDH6i+DmbTRKhxXpqcE6bvXN/mARoBFHjUHadFM3mHQKNUuFYS8dVTrrTSc1f5pvaI1Q40P+0qIk3AiTtnkiTJAh+aC9FNjv0iAvE2ngtpZ4mWZS16KAK+7evi0TztE7KiiSpM1DmIzlc0yuv1+sIxVpwml1KmRd6ALKVNk5b/rf0+Lw1SQEQqH+Uod9Byx3nsMUT5AvM3OZ9pua5FQrYgpUTDdDp17s7W5hcbedvcRaupbTXzDAf9Zplt9DIC23c0GkG3282COzs7O9lxxSaY+pb2mXQEsQ1SntL/Jqd3CK+4dOoYOhoNetFnsaGZd68SfOdH07My19vVZ7ZAT14e0gb3VgluP1A9g6eTnHouLLve1G7WBrRpwFbTV3nlwHQ6Ncp+yf8BoL/jVkIMf4HmG8o3WA8pIKDxGdAAAX3Oj5BeFR9SnvFxTOO3Jto4z+aRQT51lmjC/gsd+7ZyJP/Qyw6X79Ikmym0/GKTdXnsGFc+Wv8ZpTEvXVqE+G8oYumyvrTQb112t01up2kKrVYr+6HXIGA6TIsyj8+vV9We9PHDub616Rx5bCofe10zriVZ7Jun9L1L37DlofGDmL71fafN/2WFpj2uss/+KkPT7q7+MwZj0eGP59BTUIcwQlpJ6HI00fT8by3jLYvp0jRd2JlQr9eh2WzCaDRaCNLmDYqVGRpDGmC5AoEb7qY0LvDVrKjcdDodmE6n1rtSJYPXRKekSHGeKZsgjWkgvKywyUJJZmDAC+DyTo/pdJodp9bpdGA4HGZH2NrKjuHMLEO/xwoaSDCNYdu8xMe2LzQBU6ocTyaTLBBK7yWU5Igpb00b8nKpI3IwGEC9Xs/u1+bpXfnlgY+OEJJ3aL6hhh1fpVqGMSahyHG3DCRJAtPpFM7Pz7O7tt966y24c+cOfPrpp8770E31R/mMCxSku7upDDbpxy7a+ZGtPG9K5yrA5zSq60g2wxr5IAW3rvL4vM7QjHfbrnweWDOlo+UtAzb+Q5mIJ21pd5r6Bq2K4nmXXRmap3QCU2hepnxMeup4PLYGLmieuLhIAl+IV6RO5gLnwVqttlQ6NDpbXnpsvoW8KKu+WUZQnqcBNVOgU9vvvrbjKvqsTPp/EXSYZH3e0yD5M7pYxOaznM/nsLu7C3fu3MmeS/dH4x3a+J0t+FY2m5miyHk8RhsUsRAh70ILaYGEVi90tcN6XlgjBso0b5QZC16jN998MwvCDodDODs7AwB55TxXQPg7/h1CGzDTCo1VrQSgq31dsBk/NvpNBk7INyZabG1ro9uE2M5kn7raBr2Ulu78rtVqC84CvLOqCISuWDIZ3aZ8ihSAvFztOFyl0Y7lh5S9LDmD9NFyZrMZHB0dZUeE03u0uAw2OZB4OoRmPNNvYrRDqIygNLj60UWnxIdcjricVrEdJLxcrnDzPqTOMluf2mRiyFjAsnDXC62/tEuXfuPq+1BjQQpwSbyrkblaGSaVEQNlVVzLSpcGpn46PT2F0WgEw+EwyOki6VO+7eQjD0PSxuBNbvDb6DDNGfw5fa9tM+148w1QL4u3bW1G+8o0h/u2UwxIspW+C8lPm4fktHTxnjSPSnOndH0J/pZ+aJ9wHgZ4cfyrpBMsw1m5bB015DvXO3RUS33mgi9Nk8kE5vM5PHv2DKrVKty+fRsmkwn0+/2F3bK0XbW8KvFHKKT29pkH0vQiCIuBcayHdJKDqw35rlPpO00/+PghkuTFQqTZbAbb29tw9+5dGI1GMBgM4OTkBAaDQXYEJB+noX2Au1BbrRbUajWo1Wown8/h9PTUmzdddXR9x7/V6iqUNsmfEVtWXGUdUQuXz8z1nvOkT5vlbV9f2b1sv6oLfHxrYRuvvrLI9q0pjbbPUS5Lxwyj/MMFpcPhcKE/pXlnGf0Xa8yH+gZdfeLSO0Lp1+gCrmeYj8/1OFo/ME+v4VVOF01XFv3zqsKmI/n2zVXCdamHDdx2R9jGPMdCMPaLX/wiTCYTOD8/h6OjoywYSzPGQe/aOWS6j8c2wKW0JqY1OXyWBdf5/y7EdJC5nN+2d0UK0Jh94uITrpSYwAMJ1Cilzpl6vb5wry51lrl4ktMtOSNNQQP+zmeAm+pa1NgwjfGywMTbsdsl7xjid1JyHpjNZvDs2bOMZxuNBrTb7Uv5hNZrWTJUo/hKY8Q0L/C5xGRUaZyAJjps6TXyJgY90nc2Bzn9zkWbj1NA6ge8e4zqBrZ53GeMSLTZ9Al6z58pP4lvbOXZ6NLysolen+/WiAds3/l8DkdHRwuBFpOzGaEZnyHy18SHPo4FiR76N5+vtY4In+AILc90bLNtnLmc5zF0jRBHqI/jhfelJLtM+hzlIR8noYnevLIkSZIF/Zfmn4c+SqOtbFvb2r6TdGzeznSHosTbmAb/x7lF0tfRUVrW3d8hc5WEonRF3sb4LFRfAHDTOJlMYDwew2AwgHa7DV/+8pdhMpnA06dPYTAYiEcX2/QujT8jVH7lbffhcAhJklzaWcV1JZctW6lUoFqtwmw2WzgRjM+bsW1CDISmaQq7u7vwS7/0S3B0dATPnj3L+pDvvqbj2HfexN/z+Rza7TZ0Oh1ot9swm83g/Px8YTG+acxr51fbNyYd3nV/rQlcdvnS5sr3uiGmvWKyj6R8TP2eRx7y/G10S9/H7GObHm2Cj2Nbk07zHW8bky0ufePy41HQfPl1eNzGHY/HcHBwIC4WMn23Srj6zcZnvn3k850pra9fRwubv11avGezAVw2lctWc7Wxj35vGxMhfp/rNpdobZZYKMu4v66gtor03NfvijAeU7yxsQGvv/56ZhydnZ3BcDjM3uNdIdLE4WPYUeGChv8ay0fegFLZwCctDBwAwCWDbTqdwmAwgPF4nBmceNxgs9mEer0Ou7u7MBqNoNfrXZpoeDCNwqSEUX63KW88L8nRxOu5xtWCD98AQMavyEPohDAdQeYq20dZyMtfvg6KUEMLISm0ochjnIa0Hw9mxKxLTKD8czmIJEeQRnFxQfs9l50hbak1qkPzX2M5iNU3WgeSzYjWKO44vmLIYRwHfBFaaF62d0Ua1ra28AlehBhORUOjE8Y67nSNqwHb/K8Zh8viY1rW7u4u7O3tQa/Xg8FgAIPBYOEaGICrMU+inFyGf8IW7PXJI+Q7+j3uNI11YoT0PCYwUFyv12E+n2c7xzTgOrbPYnvuwzJBM9ebnOzU17BKXIWxGguhgaCQtFIgzRZw8R3ftsCsqdwy9bUrCOVqD9ShbXYbrbc2cG6DKQjG52KXzOAbR+h3KKPL1FdXHTY+0S6mALAHoUN4y+Tz5eW68pXyofRIc3cRPruXFb4+UN+811gO+DUeefv0UjAWB16z2YRmswkAFx2MR7nxgvOuytE4uG2KSRkUh6KdIT6OpTz5UiUkDy1F94mN5yj9/G4GVGR4MBaVHLyLEQ26NE2h0WhApVKBTqcDACAGY/Fv+pxPbnzlsDR+NO1WpkAsVyhtdbgqDkMX3RqHmK0fuHM1pF1wdSQuGmg2m1Cr1bIjyCgNWkPNN6Clga0t8/KsL80hkGjUKNp5ypTyc821mvJC2tsnrVZ24W9NO9oCEabvuRzitPkGS7Xt5iMnJBQ1x69hxrKcGJKzVuN8o++0hjYv1yaDpXHI9RhNGT7PTXTZaAzJx3duzTMfFaHrmPpAI0OLNPYpDbHBnT55naEmuNpHM16XCa4D2Jxm/Dv6XGvbmfKzpeXf4O9OpwN37tyBZ8+ewXQ6zY6D1wSVltXm2M/VanUhUMztm5AxFTqvm3QYFzi/hupXAC8CfzabTvtMKt9Gn+l77Af+g/Ti/Ym+wVhejnahi6+sCJHNtK1M14CYvtE819qIa1xA4+v0sS0wvUl+2/6W0i/LhnDNAUXmq2kPU94SfVLbh/arKa3vHIKyFIOxfOMTwItNUT72aR79MJZu6aI3hh4eKut8ynCls+nvmsU+Wnq1dNl8SRIf8XlNmudCx6IGRdsyRUCrM/jm8zLMwWXsb63NRG0aSX939d9CMHZ3dxcmkwkAQHZvLH23sbEBjx8/zp5LCrE0oG3Hmb7sTsgiHDkvM6QjM5H/Go0GpOnFTljXABsMBjCZTKDRaFw6/oimsxlgkjEvjQ2cmDVKaFn542WYKGJDE8DB//nxbVSRm0wm2a5vXL0vKf4xgw9lcFIiipShtnpqjv0sYn7zCRD6Bh99gKt1q9Wq94p9pMvGqyZolSNuREg0+BgxPn1YhnGxhhnYlzivxwj8aHiEygMuN0IMEVOZoUFKk06j2f3uA9fY9MnHFKCS8l7mvGXT52zzfAxezEMfT8PnsJgOOal/8BjU0WgEo9EI0vTyCUx5wHXtJLk4vrVSqcDm5iZ0Oh24ceMGnJ+fw/Pnz2EymWTO0NlsBrVaLaNfmm9CcBXni6Js92WejlWpVOC1116DO3fuwO/8zu/A6ekpfOc734HHjx/D6ekp1Ov1bGGuj8Mb4AV/UFs0BmLkZes73G1lg03u8nSmsjXAsTYcDqFarcLNmzfh7t278KUvfQk++eQTODs7U+uevrJ/Y2MjW4Q9Ho+zo5JpXjHB5atWz9XkS/PztQNdffwyQKvX0b99FxpokHe+yfNNTP9BDEi6iQ9ij2ENj2jL3Nragv39/WxDFH7/7NkzGI1GAADZ4iFTWaF02lAG/6NGR87Dp3kC1RLodXeutD4LY0xpJZvQpj+b2tLXp+PiuZA+KQO/rbE8XOX+1sh3HpOiWAjGNptNqFQq0Gg0AACynVZpmmbHtSbJ4rE1tVrtEhE2RxBiFY1exqg7h41GySmiyY9+o0Esh1XRKztMdErGDDp5TMcP8WOzMLjFjzbmR1W5grC2Z5Q236CJj6NvFSjTOIvhJAsZP/R77XcuPpH4bzabiTyUxwCX6KdGmSmNb56h72z1iSXnNUao5JSSJtoiguBSf2gchj4BSFeeMY469RljpiCG9L3kIOHfu+roq7MUpWOsWncqG2K0syQvY+UngfO5LSin1fVomVJaWxm+cDnWtXorfR5LR6TBbCmPEGeq5rtQuOaUInS4EPlqehczIEtB51PUvWlQK7QMF5/hjsh6vQ6tVgv29vZgNpvByclJFoRFOqQ7dGMitjOwCPjoEGhbxRxLeediKoM3NjZgb28P3njjDeh0OrC1tQVHR0cL9NsWy5rocekqNtmu0WVcZYaOGamv8srlUNAFj9VqFfb29mBvbw92d3eh0+mIJ065eE3r38BriiaTSeY3CL2r1Qd0LgvNn9t+mnzy9FWeoNhVQUjgVBMw0eomNjkRU/Yj3Sa+cZUVQqdP20p0mNpa0y6cNikfV1to6DeNaym/er0Om5ub2fv5fJ6dLDEajawLZq6zrZhXhw0pI+8Y4/4hn/JNfYm6qG8eFBJNmjkglj3JcdV5NabPUfPeZLOukQ9FtKXkj+d9femY4lqtBnt7e5nRORwOYTgcQrfbXdgpi5hOpwv3dGBggFaKr8oImTSX4SwrC5at2BblQC4LNPW7ceMG7O7uwpMnT6Db7War809OTqDRaMDOzk42Nvr9PgwGA9G5iuUBLK7gms1mMJ/PoVariYYS3XmCgV90AEj8YAoq4N9r4Vw+2E4IwOeuvkuSJNuJiE5C7pBY9b1CJoTIGVN7+BpB9BtfYJsjuEOG3p9eZCBOypv3PXca0jbnxmAsGYFHvCOk/JctjzQy0/V9SF9q+TKUrjUuUFZ9JYYcoLtvMChl4hFMa5P5pnch8wTKOml3Yd4+sTkYbDZDaLmhzrtY0DjTTO9pW+EcsMwdhcsGLqTk85mEl02e+gRtXPpUbDQaDdje3oazs7MFBzIPRhShK5j4A8fLdDqNOmb4uNRCE/zUBJYlWvBdyM5yKQDtU0cf57HpewzC3rx5E1599VX4nd/5nYUghfRNq9XK7O00lRdHusrFXbCj0QiazWZmcxUhY30CDD5zHbafbxB3jWKQR669bHMawjR2bQFfns4UXDbB5ltz0cC/045ttBt4nujjefbsGfR6vQX/4HWApo24nrCMBTE+MPELPjfRG7K4A8But/H2lHgK8+Jzg0uv9p17ON1r+IPy0npuLic0i6lonMcE8c5YPBoG4GIwTyYTqNVqUKvVoNPpQJqm0O/3F1ZWSILCtJo0D1O5BMVVG/g2upcZlJWc9LbAUKhxQL8tEpw+WqZ0zAWugm02mzCdTjM+Ho/H2YSGY0MaUJq6SW3GJy1bPjEnw1g8VeZJIiZtoWMxpHxNMB2VLq58mfhHM+Y0fOIzhm1BuRCngtQuMfrYVy6h45sbedoFET7OU3yfV86aFozYaCtDAJVD21cx5k6p/fBvbdtoDXvpm7LK1esC07iytT/nCVdgw+Rs4DqIbYxr9DFeLi1fC0nWh8gAnwUJvuPIJRtM8wRPJ/2tBc3f1G/auvkYcvy5KQ9Jj+ffmPIP7fNlQNNvq6Sdtp22z0x6moYvTbaD9J7T5wOfsSc9x0CWjU9j8p2P053qRfV6PTvpy8dJyfPU0GeSGZwuV54mPvNpSxMPSvnk5aFQ1Go1aLVacPPmTQAAODs7g7OzM+j1etkOeoTtXlXt/Mn7olKpwHQ6hclksmB3hdofMewWjX6ax35bYxGhdpjruUu++up8PmnKZGOEyCwb/SG2F4fWbvaBadzSOs3ncxiPxzCdTmE2m8F4PIbJZCLOoyaaywaTXqqxe2yIoWfb0tH5kKeV3knjW+PT4OXFgIvXTLRIddWOAd+0GqzKz6zxk2n1QpvOl4em6zCXlzmOoIGPvWaSJwvBWOlsc0Sn04FOpwM7OzswHA7h+9//PgwGgyz9cDjM7ldB4MpTl3PKxkxl3eW1TCyTUWMP7FUKUSwX+RlXZjebzUzB4bh9+3bWBpPJBB48eLA0mm0ODdcEl9cR6wseiCojTM4N07Oi4XKOciPaRSPeIxujHnkCQMtw4God7Zo8tHA5wnH1favVgjSNe6+dqUwOKtt8UaRjRpsn9ok0NsooW7SKt0+bLttBVta2XQXyBEM07YgymjqMK5XKgg4S0u9clsXoU8nANDlQfEC/5TpDbJnJ581Y7SI5kFwBWRoUWMbY9q2niX7OW5L+eR0cADGRh8fKyBuufEzBSvocZV8RZcZGtVqFW7duZQ5wfjcovZ9PK1NMbRVTTnP4jE1p3AO8kMlchko6JqdlGbvznz17Bt/97nfh/v378PHHH2f0uvhNE9SyBQaePXuW3R1L75oPqXOs8Rg6Lxdp5y1bv1xGedz29Amu2PL0/caVj887bd5cf1s1fAPQsXVBXkao3mXSGRuNBoxGI3jw4AHMZrNCTnAw+TU0gc8YbaidP0PGEs9DS5MNPu0f8+omBOc7H/s0b7DaVVbMuczkpy2D3MkDH/rzzgVXsa0kGV32umhkpQmSPFkIxj569Ajq9TrcuHFjwQhpNBqZ4xkdDDdu3IBerwenp6eiUY9/06NdcGKn6aTJzVRpzSqU6whT8DrUSDUZbJLQy2sIS/0T27g25WdyGEh0jUYj6Ha70Gq1sgUFaZrCxsYGJEmSHcONx4Q0Go0FJclWFzohUiNfCrj5KPyu9itSmF2lcWfjwTz55eVf19hwKWBFGNRSuZwuE71Fg44XHoigtHB6JIeTbfGCJEeo3LA52ULGnGYOlOgIaXOtzLDNsRo5mwcxeYnXx6deUhpXWTaEyKHQfval42VH7HZ26QP0xxXUy0NnniCqC3nlnY+zwIZYeo6rz0JoMM1ZPM88gYJY8HXc2OZJm32hsfMwyCGltbUjTyPJeJtdUKlUoFarQaPRyK4UQTu20Whk10OkaSoezW+qjwnL0Ju05UiOryLpk/qxyPJoX1L522w2odVqLQTztLqAj+yhbSvppNo8TPRpv6djo6j2NtVJ41yl7/DIf7preTqdQr/fh+FwmF2TJS1KDaknpsUrhdrtNrTb7YWrg2j+Pu1n4h+feQHraTp+Ult+Hkdi3rxjYxX6rK+uHzrO8vRDiJ7oeu7rg43VNz7t7WuP+th9eeSvS+/h98DitX+xF7i46hbybZ5vJPkXg29i0aodG5r8imo/m56r8XlJz339TT6yR+sLWaZsX5YuDuAeg1J7h/DbVcMq9Yg8/a+x0V39uRCM/dM//VO4desW/NZv/VamfLZaLWg0GtDr9WA0GgHAxeXi77//PpydncF3v/vdS8fEIPjdl5iOrxzhBopLycX3612z4cDAIgUaPatE7ACiNr/j42M4OTmBN954I2uDWq0Gr776apZmPB7DYDCAVqsFrVYLTk9PYTQaXVL+pGOQqZE3n88XAr7Iy6iE8CNQecCXC2zNxFdEgOMqoSw0l4GGUCzLKcwhOZCKdtwhuEIK8GLHk+Yu9GXAZbxw3nf1Y6ihGTMgEpKXyyjRls0R6mj3hYmniyjrZUGeYGGeb0IdKBL/cwd2Hn7W1s2ka/AdY775uuihOr2vs7uskMa1qz+XNb+tAq66oc4rnaqkgSmA63L2YGCu3W7D9vY2dLtdqFQqmR17584daLVacHh4CKPRKNvVXnY7VCMDXbxWVB0lOaOhJwSVSmXBvsUxuLOzAzs7O8F11Oqjy9QPJZ2QtjGXs668MB1N6zsP+fZpmqYwHo9hPp9Dp9OBdrud+ZEwEAsA0Gw2odFoGMu18RTX71H2TKdT6HQ6cOfOHWi325AkCTx48ACGw2H2bawgCb0T3uWDqdfrUK1WYTgcFjo/rsrWk1AW231Z8BlX2nYJtac0z1YNH1kWEzaZ72Nba48evq5jgMsxU3to5C1fTBWiR2vlah75G3PsSrpTjHt1eb6+yDsWr7vcD10o4oPr3oYhKCLGBGD2E3LfCf2GYiEYm6YXd8F++OGHsLe3B6+88kqmuDcaDUiSJFOOuUIPcCEsJ5NJtlIR86RObE6kVCmahleWGwMmY85U4bLCZTDQNKH5SkFDLrRNfbAsh3ARggNpxroiH9ZqtYWgdJqmUK/XodlsZv/To4yn0yl0u93sf80RezyQ5JvWZOj79oc0djQ0mfK6aigbzTHGk6TEhuRPecFlRMRqRy6LNHOBpi6hyjd+q82D05wkF8e44aQb2q+u9qfywCdImMewNvWVT558HqJKSshYiMEvlBZOo5QWf2vGiUSjDZo2KJsMuwoI6YMQ+PIx6l50HEh0SOPdxNu+RgaXXyGQaOU0uegyGf1cXzG1U1GOY60ThCMWPZo24+VK/JSmKVSrVahWq9DpdCBJEuj1etkReJzuZcBUN5O9EgM+wTITT8Z0ikpBKlfetjqsyt7lY5Py/2QygV6vB8PhECaTiWp+K6oemD/uNrp9+zZsbGxAs9kU+5n/beubUAcnlY+heqtEs6lc+r+kI1L+5v0h8aumbNM3WpoleSr5gfD4YNxR5kML5w36nI/5ZS6+oL4zn2tpTGNJep5Hnml9eHmwKr3XxZu28YDQpHF968Iy2idvGXnGv5SHS+/0zU8D/J7LB1sZLkj+PvzN/TIvs/3n8meE9kMIT7hka0i5tHzuI9HkLfll8siPkHbxabtQuopAqN8phD4Xn/r0Xez2fllgsp19oZl3JHluQo0/6Ha78N3vfhfu3bsHr7zyCgBcEI+7AU9OToyrLvDScTzOCb+lv2MyEFfIuZIcYuBcV2CQAIGKPWWSNE2zY4EAZMcDbdOrNNCxbogkuViByu9jaDab0Ol0AODF4gKs73g8hpOTkyxtrVZbuBeMIxb/Fe2kWCMMRcoXm4MeIQn4vDQVwWtFtZOUr0S/b5DClA8vkytm6Aiiu8eKqLepPknyYoEU300f0gax6OJptAHQUGC7m5x3CMnpJqWVnM2x7mo20b7G1YON/3m/UueqS1GXArI+ZZtQhCNVcpLn1RdtMixG/r7QOCqKkreSk06a63jZePTunTt3IEkSePz4MYxGo+y4y2XMD2u8ALa5zbHL05cFJn7H3/Tv0WgEp6en0O/3M35bNSaTCUynU3jrrbfg9u3bxnanpxXFvP+Zj8+iZRjXU4twmsam28cJjeMId43OZrPMbtfShYuj6NHjJuCiy2UA2xaPaV41fPXT6zyvaNvClkbD5zGCPz75mtLZ7O3YMNluJv1Gk1+obeUTmKLlhASgaRmmU2jwWZl0Ai1i0C19H4MPXf3mm1do2Vy/j1VGnnw0vsjrjmXPYy9Dm151xAqaAwjBWA4+KVKFcHNzE9599104PT2FR48eZc/x2JV6vZ4ZNCZjw3QUlclYwXdJkoi7OrkgczlkywbbZMXbUGpTmwJAHX+YDneB4nG73DFDd2/QvGIgdt/4OvbRqV6r1TKn1JMnT+D8/Bzu3r0L9Xo9O6IIeXk2m8H5+Tn0er2Md22OeZMih0FvOj5439jysdXZpDCvg7r5wY1Ll3JpUmJ8HSJS/+N45ndya/OVgk6m3Vo8HafHxVu+SrjWOOVBMhP9Ps4uzZiimE6nkCSJ6CzRKPihQQ/aD1Se2L7R1j8PH9nKp3+j7KT5SUfnayDxozT2TI5kTosLJsespu0oL0p8KcmJq2p8X3cU5UQP7W9Jpmt0Ey3P5qXPlW8s563WgeYqTxMMMJUh5S21Y9n0Ml86+BzsC5ND2tR2XK5LzloKqhNwZyn/djQaQb/fh6OjIzg9PYVerwc3b96EnZ0d+NKXvgQbGxvwl3/5lzCdTqHVamVHmkqQ5rtQZ3JMmOYcibY8/crz0KbhfVTU+KjX67C1tQVf//rXoV6vw//6X/8LDg8PYTgcBgWNTXoz0u/SL3ydoiHA732P2LXZpDa6XfqNlGeSJNkO1fl8DpVKBcbjMZyensLPf/5zODg4gMPDQ+j3+2J53W43u0vWNNZt2N/fh7feegs2Nzdhc3MzW5wd8wonStPm5mZmO8xmM+j3+0a/wng8hul0mvnYTLLExIP0f5PuYvq2KF2nrHC1YYy8bXNBqF5ShB7B54TYc5jL56lpLxNi+IE1eqBmnjLlw/04rm/KoitqYOIXSS7b/M74jCPUrxbKv5Rmk87qm7ekH2pkA59Xpes9pO9swLnBpCP4+vGuEq+aYBuDIXzkkhVa3+MaYYjdpjHmQmswViK4VqtlBVerVXjjjTeg2WzCkydPssGLAoGmlfKVHNWm9JKxpqGXomxOj1igBqtNeKOwpoK/Xq9DkiTZ/UfcmVGkQmrLN08faSZnyoO1Wg3S9GLn7NHREZydncH+/j40Gg1otVpQrVah1Wplq2dns1kWjAVYvOPAZrRi2+JEh8ETyVC05WPK2xRoMH2ref+ywdVOeRGzvemYN8lRF1/y5+j8MDmWbHI4hsPOJgc0Mp8qsxqjyDZmeJkmGjEQKgVjtW0SSi81VlGe2KCdI230xJDV0rwiKaD8uc+40TiZQpVeV9to+JnPtRJN2ndr6JBXB1yGDmkbg6HlUt3D9ztT2bbxY5LVGhqkceED05gLkSMaPQzr5ENziB4QKmdj6BomWaWB5juNvmKjyxVAcNGFf4/HYxgMBtmCy+FwCBsbG3D37l149913YWtrC/72b/8Wzs/PodFowGw2cwbuJKdjGcD7VOoDrltqdCuanzT2bPTQ8ml5/HkoaB61Wg06nQ584xvfgOFwCH/0R38E5+fnAACXAuw+vgUNf+PfUp01MkfbniF+Elt+SIvNUWyDxk8BAJeOGh6Px9DtduHzzz+H58+fw8nJCYxGo0v0penFVVuz2Sy7R9b32pC9vT147733Mlo2NjYgTVOrfh8ix5GudrsNrVYLAC52bXe7XUhTeXHgeDxe2L1r8rFx3sozH+fBy+BX8B1jq5oHNH3h8l/RZxr7KgY0epvNZsrr03HVg8rDUJ8DQLw7qMsKyZbR8hT3c9BvfCHZV1p9htJt4q0Y+orNj8B1Bc5/pjrhd9K84lt/nzqsYg7gfWV65/q+CJpseWv8UWWzI64C8rSZi1+0c4yLp4zB2IODA/izP/szuHnzJty8eRN2d3eh3W5Do9HIjiNOkgSazSbcunULfu3Xfg0eP34Mn332mbEwPN4FdxKhwpmm6UIgN0mSbBUipqWCxrRCJk9DXFeggx7rjwYOtvdwOIRKpQLtdjv7Bo/5QYQYHGWGtOpKAn/farVgf38/M9g5fCZ2LS0aBfe69Mt1Bw8wxXDeSk4cXyXIpbj4Kl+hDv/YfKw5AtQXRTpVNW3tSsNlUNHOlpAybOmlo/NjgjtNk+Tyblyelv4fy4BHaOXAWgG/XsAFHM1mMzs20+Q0psY/faZF0XqwRu7zgI5mFzp1MiRJkumu0hyVt16SM8OEZdgVJgcvd76Y6KHyE+0sk+N+jeuPWEF5LarVaibbDg4OoNvtwng8BoDl3rnJ4WqDwWAAT548gdlsBp1OJ/NZ4E5N06IEH92A5oH+DGyTPLowLhbmsjYEMflFmr9c5eLvvb09aDQaUK1Wrd9QnxQALMhyW5AD56J6vQ6z2SzjUQCARqORHQ/M/SF57YD5fA43btyAzc3NjMaTkxOYTCYZP+Ai8fl8DsPhEABeLBJwXZPhamsTjxRp31wHrGr+DO2TvLZFkiSqe5iLoInK1BjtnpevfRd1mMB1tOsehJUCrDY5I8mj2IGpvPwUU4cJnU/Qd2ybGzXw1V+0eb4sCPHlaubnNcoNbZ9T/d4GYzC23+9nKwxrtRq02+3sSFuq6FYqFdjY2ICNjQ3odrsLeXCFz+T4lIQBvaNFcjq4Vn9IDG/Lr2zQOt40A5syAnV24xFblUoFms3mwj2/eAyOTzkxEep4tOWDebmCYmh841FH+E21Ws3GAT3a2JdGWwBN4nFOm1QvG8rM52WDJujJ04aUERKQ5fANDklpJV40GekU3FiS8rTJ5hC4+sbkJPdxkkn5h4C2TaxApQam/nAZM7a+Mc2jmjSmgKZrUYmWXv7OZsBLuoGNj10w8bfGSWabW5cRQDdh2fP8qrCKNqZ9P5vNoFqtejm68vCqD23S/ERlGedVH/g65dFZjraDb/matuLBXNs8yPO06QU2/VLTBi6ZzN+b0mN59MQLE+0auOwRmrdJ/mvyziOLTG2g+Q7Hp3SnuIb3fOgODeLFTKuhQ5IFtrzQSYinCI3HY+OVN6uc6/CeUFwQg3fc4hU1CNMOaF4fmjdNIz03pQ+BbUxz2W77XgOt/ZJXzwe4WACNp1IlSQKTyWTB7sb0OGYRvnwl3QOLAdHpdJqdHBaDX5HedrsNOzs70Ov1YDQawXA4zIK+1CemlUf0Hf3tS9uqEMvnEwOhdrXEH1pbUCMb8ujmGruew8Rj+K1rPoxpy+bxH9hkn61sV1v52no+74tGkfoEwqQ7h/RF3rJNefrm7+q3kLZy6asmfozlqzLRYaNRkx//dhm+BZe+Y6tHDB4PyVPDj6ueE68aYunUmjSuPtOM1YVgLD2uFXF4eAinp6cwGAxgf38/yzhNL4JTOzs7xkJGoxFMJhNotVoLCmK9Xr/kgOLMOBqNoFKpZEfNAFysCuRKqanyWudwGeFrRNuQpi92IlerVdGxlaYXR/tgsBGPb+J9FGtlnC+KMtjpDutarQbVajXjsQ8//BDa7Ta8/fbb0Gg0IE0vjilqNBrw+uuvw61bt+CDDz6Aw8PDS3dG+ihZmIYbWq58+OBGY9K2u0ZyjK2xWsRQqOjqfZ43gH8/0+Ozms0mDAYD471oa7xY7IJyAOc3nzajfBBD3vnIIHpkemzErpcNoYo0v9ee78B7meTky1TXVWE+n8NoNIJqtZoddYo6dWye8x1zIQasDw1cLqHek7e+1HmtoSMWtAGsVQadEEmSwPb2NlQqFTg6OoI0TWEwGGTvbbqIq32l+YYHjPF/nGd4cF1ySPg4QDUOVFMdcXdBv9+H4XAIZ2dn2S45DMzhHZJ4chA9BhVgcaGrFLhxBUliOHVC0pq+o39L9okGNJAVQouvE12LNE2h1+vBs2fP4Dvf+Q50u1346U9/CoPBILN98dSoJEmyYHKoEzlN/e6kLwrS+AwNbmh8D3Tc03J9UK1W4ZVXXoHRaAQ/+MEPYDwew2g0ynxQ2vx8gx+DwQD6/T784Ac/gCdPnsBwOIRmsxlVlqNMnk6n8OzZM5hOp1kZNuDJGniCnIse+l6zE2+tB16Gi+eLarPYugOdm6SAJ15dJp1AaAvOLgua9ohFJ/rUXGVdRSxDn6CwtSX2V5FtaYoJlAEu3RBg0Taj4xTgxbH+eMoDPckBYHFDFm9n3g55doDbArHLAMY58KrMq4Qy8eMadpj4XJpPtXr/QjBWKgBXjQ6HQ/GODtxZCXAR0Nrc3MyCsBgg4Hdr8snU5PzhxjXdLUvfSYNOMoKvKnxWVpgcG/QoAz7xYT9h8Bv7leZjUuCW1ba2ySM0H9u7wWCQTXB0ogO4WK3bbDYXDCFb+/P8JQcRf6/tc5uRK9FQBofAGovwCchy5yXvf0k+mqAJ9lcqFahUKgs7xCVHqVSOr/MjFmI78Gg9pMkWf0uOJ/59SJn0WV4l2dRnkgwrIoBgo2NZfMLLk4IGpqCCK08XTHWUZIArsLRGGEzj1Ad52x+PruEyldKYlxYqk3zHVuh39HtOm/aZRAfOQ6GOAtoWtnJ8Efqd5vsi5CHuxsb2dJWpqZtGfpucbXkcgkXwNO6ARPsVADL9B394kEmyjTTtKNGySrmuCa4h+LG6EtAhlscp5nJO5mkv7OfT01PodrvQ7/dhMplkd4Si89gUuNCC8odLH6fyUNLJaFqf8m3wtRtCytTqxHxunk6n2c9kMoHz83Nxh6iLDpudjOloG2Df4/HBh4eH0G63g64+kcpGn0yj0bgkGyW53Gw2oVqtZjuD0T/jqzvbbD0T3TF0kdA0ZXZQ22gztbMNtrkpr31hS8dtHekIbNP8nRdaP5m23Bi2N4etTzR+SZO8iYm8c2HR8NUN844DU9l5dXUXYszLPryOP3RRK9VLXXRK+qppTGp5VtInioDNj6NJH6v8EGhoKPPcVyYU5aPUPHeNG1eeFOIxxbjDz5XZdDqFo6Oj7P/d3V34zd/8Tfjxj38Mn3/+efZ8OBxCtVqFzc1NAJB3WEoDBVen1mo1qNfrmTFM06XpxS5aU0C2zBPUMsEZBe9DGQ6HWds1Gg148803od/vw+PHjy/lgY4IdFRch8CehkfSNIXxeGytb5IkC6t0896x4aNU+yoA6zFxteHqQ5sDJ2TCaLVaAPDiZAJclBPLcZMHUhAtTV8sLtEopjGBwYJY98tQUMXb5OzS9kmSJNBqtSBNL05FoI5HgBcOoRhtx2miBn8ZFU6sO+4+0CCG40oTkF3L7vxYRhvGMPxi7BqlY07j7AjJm4/v0Lwl3k/TNNNVUc8P3WnnKg+faQJNsWFyGHFHQ+w+XOPlAQ/0FQkMoFH5w+nIC1+5iKeV5Cm/qLlDktOh/US/k46G1oDLdHqilia4LgUsNTSgHoq29PHxMRwdHcHDhw8X6JLA50sf+4Tq1LPZLNsVLdn8dKEGp93lDKa03bhxA9544w04PDwUr/jC8VOpVOALX/gCvPHGG/D555/DyckJHBwciCfv+PQzrVvsYPwai5DGQqwA+DKAGzVwc0IIf4TyVNl4sWz0AJSTJgTKdA6qa9uQt244F63aXy3N56Zxbgpm8raoVqtZbITvhjWV47uIJBRFyzBcoOSyB8s2Nsom29fIjxh9einqamLc0WgE5+fnmcLZ6XQWdqoiQUmSQLvdhq2trex4S66YoxDG7eSaCtlWPNGVXCbBT7FMo3TZsAl3bpjxNp3NZtDv92E6ncLGxkYWUBwOh8YjN2OtOtIA+ctUjoYW14pF5E00zrrdLjSbTeh0OtlKatxZsLW1BaPRCM7OzmA6nS4c92kLApkU7tBAaZ6AbFmV/1VD40jQOJhsfc3fawKs+A0NPobCRAMvi5aHiw4kZwuljdMcSlMo3RJdpucuOl3KK/7EMBo0C0NsY9b1ngLT2fqTppPmeql8TZmaYPEyQOdFKWhsosnWVkXKUs7rZTM01tDDZ9GGSx7GGk95A78anUYrK22BSNd3ofoQlueaU7gNkafdtPLCdw6jzzAQhcdj48IqV17UvqrVauLx+y69/LrCFHDykf+r0r+1fYW2dK1Wy/oYA3Ku+dA0R7n+LxI2PQffY8ABTy/gyBNgttmoGt1Pk07j9KV1cM1BJv1PQ4spLxut+DxJkoWT1dD2CKm/6ZkUEMB5mS5IpM9NfWijC9/N53PodDrQarVgY2MjG1cU1H/VarVgc3MTms1m5mx3tYGJL13+EW2gwBehPo2yIXY9bD5N7TwSMwhBxxzKBV/dS1MGgK5/tTxOIfkGTGl8IfnNQr4tI28XAUmnpv/TtrT5jEJ1KlN+vgix6fPoOC7/GfqfcR6gOg31R3Geo21uGhcue8eGovkb88VFUpJs4nXw4YXYeihvjxAeWOMyytQ2MWkRd8ZKODg4gIODA5jNZlCr1eC9996DZrO5kGY4HMJwOISdnR3Y3NyEX/ziF3B2draQBhVqXNExHo8XVnRIAxq/MRkNlA50OJggOVzL0rl5nDqmSYy+n06n4u5ixGAwgJ/+9Kewv78P7777bvb8888/X9gBjbDdc0rLja3Ixc6D0ohtg8ciffzxx7C1tQXvvvtudmR3p9OBdrsNX/jCF+DNN9+Ev/mbv4GTk5PsnmO8IxmdV3nv2vUJ0tBdXVKal9VxdhUQGuBCmJQhHycOvXtYSp8kycJR5iYFLkSm+iotUntJzpGYSiJVfjE/lIOoHBc1tvhiGsnA861rpVKBTqeT9Sce06hBDNnuojfm/GEC3S2TJC/u4aLgin8RO58BFg2xVbbJGnFg42/c5Vl0Ob75oCMuT9lc13AZzXlpxjzpPaRanSkE3GFic9Ktwr7g9hPeefns2TOoVCrQ6/UuraRH2rkOWalUoFarQavVgslksnCFCZbjc3fjdQAN2mn0Ftv4dDm88/Jpnn6ZzWbZqVboBOv1etDr9TKn4HUByr3hcJg9C70rl4PLOxro0+RrCqbSd77HQfM8THSgzsv1Qu7wDUXo3IVtZ9rBaiqHgt675/oWg1Shznmc799++234xje+YZwrJpNJZovduXMH3n//fXjy5Al88sknqiBzTLgCuC8DNGO0bIG2kL7CI7Pxii4AyI7S5liWDRLTHxojn9B6l2nsrEKf4H6ZovVk3FBDN8iEIGa/aXwdLl0ySRLY2NhY0NFpvMO0yBK/pdcV8o1woddJLNPmSdMU6vW6KJPwfYwTpYpAWeaGNeKC96spvkYh8aY6GEsd0PP5HI6OjrLjjJvNJmxvby8UxO8dQEW0Wq0urAZEQ58KEF6J+XwOk8nkUp4uZzzPy/SuLApnUWXT+tH7ewFeOFGw/bkTLU3T7Hhp7Pvj4+NsJ6itrGUH/WIFa7lTcTqdQq/XW7gbudPpWCd5KShkM6gl2AazKS+k3zYOtGWs8QIhMqIoQxn71+UINgUmTTTxIB8ubKEOC5tC6RM0jpVW6heTo5zni2mkMW/6jj83tYPJ6Wr6Rlu2iQ78X+M0QCMFd/mj8kADjTyIIgV+8W9fuautW1HzsmmO4kF26RutzDbxbdHOszVWAx/+lHRYSf6E5G2CafxKtLn40zZ+bPmannM9SgrqasZMyLiS5FeIU4Lmo5k3TOmK1pdpe0oyzUaThjZ+WhKWSf82HYFP522T/mrjNczPNve6gPyIu4fptQO4QBMD0rVaLVdZ2rR5eMLlKLPlzdvShw+KQOjcyf0Q2L/Pnz+H8XicLSKniwxCxqZvoMylU/C2L0oGavIrIrghyUNeHvp8bHY2lQnSMcoafsUjgbe3t6FSqcDJyUmW12AwcNIegsFgAJPJBMbjMcxmM1H+Yd3RD0GP/6aOdQpXX9lsH20ea1xGaJtJ869JB9H2rU/Z+EN9gKbyfK5w8OWrPGPKVJZJjuedR2x2r0YfXiVi6xPSe0kPM+kPmnZy9RnP27RY2se/4gOTr42XLbWLD+9K5dmemeDjf9Omd+nmvpBkD114hX1sunqC+0jR95WHJp9vQ+3RNewoyofmyw8+fmLNe3UwFoGKIb0Tdm9vbyEYKyFNL+6mq9frWXAP4CKwVavVYDgcGld04G6dZrO5sCLCtgLE5TB+GZjeJOTpivd6vQ5pmsJwODQKtBs3bsCNGzeyY3oHgwEMh8OFXR0hTvllQTuxmdJMp1M4OTnJ3vMd4ab8QhBL0EiGbujKpzXc0CrqsWBypNr6mQbVNDw2Ho9hPB7D1tbWQkA2r2K2LNicuxxaemkb8nLw71XUmRrVnCaaBgAyJxMujmq321kabjBI98fTvDCtj7ObHgPvcv4X0ZbotEPFHk/dwHnRtCvcpnxpjm1EFFWvNcqJ2EZZKP/E5Dl6r18oqMyiBjaFTzBWUx7Plz/Hd9LzvGWbHK0mJ/9VlBFoH9K5l8/DPNhA+5g7T2geaZpmpxZw24/myx0upvnENPdUq1VoNpuQJEkWkMXrWqrVKgyHQxiNRtBqtaK0WVlA22oViOXE499TvXg2m2UBvs8++ywocOdbvu84Njmxfb6PUQ9TMH4ZcokHY/E0HtupZ3TxBMKlEwNAthu63W7D7du3IUkSePToEfR6PRgMBtDr9SLW7MXmgpOTk+yuWNM1UPSb2WyWnWhAndKrnieu+pwVA771DnXghuTpAp5wMBwOnT4i5Lu8d29roVkAsSyek/TEsvo8Vw1Tv+Xxw9vkOF/MGXIaoY/er/EnmewNWpYPXdJ3aDtpaPf11ZQBtL7VanVB58a4EL0zNkkS8eh/upFv7QdfIwa4jmqDZsx5BWNNwnAwGMCDBw8upd/c3IROpwPPnj3LlM3ZbAaDwSDbkWOqiKTgoQJQr9cXlFEURmho1ev1S3cbaZRy+u6qCiyftLSOtM16vR7cv38fdnZ2YG9vb8HRUalUYH9/HwaDATx+/HhhF5V0jCbFqto0ZHJFnkIj6Pz8HOr1OrRarezu5EajAZVKBe7evQt7e3vw+eefZ5MD7vhG/nbtIkZQpz415F19bDKc6be2Z2UInl0VuNrOJs94+/rIG5vz2Od7F0y04hHG9Lgu37w10BhgsRASRJcCstpybN/R/jW1p+TIttHI8wV4cRoCBiQxGIvH2APAwl1dtrr4vMPndJ7AORxX+5vq6gONw50GfxqNBtRqtWyRzenpqRgw4cEBW7m0LJcs5gEMF66SbvKyQGsM25673i+j33lZLme8SxaE0GyTj6ZxR50SseYik3w1yRRb+8QCDUxq5mCqm0+nUzg+PgYAgNFotHLHTEigKjQ/Wz192iCEXskJVjY7U0MHXsFCF+FSXYgeD0d1FNovoTJhmQ73mH2itYHzBGfytK0kL4vQQ2xpuT3hOx65boaQTlKTyqayNEmSzIanO6VjwDR3HR0dZbtk8dnW1hZ8+ctfhnv37sHXvvY1GI/H8OTJk0v5uK7LsOkWLr+bBjGckLbvVuWPCNHj6DvbPETrRu99BHDv6AuhV8orTdOFeyc5ms0mvP766zAajeDp06dZWrTffPslj3zT5O3SnU3PivBh+NBynaAJdmnaQOIJaZ7i6fLq36b5RzOHu/jbp+9NPNlsNqFWqy1cpYdtjX5qU9vTuZC3pa/N5GufxQaeTIObCrBMPt/TIDW3m9YoN2zjRut/CCnPRQMvm/7Q95JOzscfp9l7Z6yE4XAIjx49ulSRd999FzY2NuDk5CRTbHEVIjo/JQPCJIxxJQS/wBmPi8J3jUYDxuPxQj787+s+MWpA2wDvN51OpwvB9d3d3YVvqtUq3Lx5E3q9XmYc0Lx8ldiyCEY+yKjiiStS2+02tFot6Ha70Ov1YGNjA1qtFrz66qswm83gyZMn2VFmuOObOghdk3psJZE6Snzqv4YfYradhgekseajZJicL5IyKk0+fNEFLd80cXG4FN5VKE2miV1q71CnhYZXXHWX2tpUJu8PDFpUKpXMqYp3XKNTDB1URawgpG2JBj7AC6eYrS5SPq6yJEWJOt3S9GJxV7vdhr29PQAA6Ha7wceE8bJN9bDNl/zdWjZfHWj5EkA/x1Neksa9aWzQQIg2KEDfa5yCJqM+xLFO87E5ZFxyggaGbOm0jiHXeIwdwHHB1AaS7kDbgwZj6ZFfEh/anMk+dIa888k/Jn+5vnHB5RRGB7jpW205qwDaROgQxGf428R7vijL3BdjPLvkhMtmNr236eemsl30+bZ3qMyT+pfe6+crX1y2igvol0JZmKYXxx72+30xfShf2uTC8fExnJ+fLzzb2tqCr33ta/DOO+/A1772Nfjwww8XaOBzoU0/LsJRaXoeex5ctRxA5LGvbaDOXPRhFg0alKBHXdP6NRoNePvtt+H4+BgePnyYXS+HPlie3gbJBtPSScuR+iDWOMRybHqyqXzJ2R5C21WH1mag6TXPTX5jLS/Y/DYh/mqTP0GTh8Zng3lxG6bZbGYb0fhmMzy1kueF4MFYTpPLPqR9q5lzTPlo4OKhyWQinpDBZRnqFChX84xHH9kfq84vK2zjlY8J6UQtEy/76LZSeps+H+KL5QgKxiZJsmCQ0SP+aGGPHz+GWq22sJKWA1c20AupTWXSvHFXD/0Gj7ACeBFcHA6HMJlMLgUQfA2X6wZcRWM7as4kpNM0hWazCW+//TZ0u92FoCwqeMtQKpcBKuCn0ymcnZ1Bs9nMjktI0xROT09hMpksVZFGmBQBOmFqnWLLdCiuURyke9vyYDAYwHg8hna7vXDfG90py1eh5UUeXpSMIpsjNBZoeXxcSgFs/l0IQpzjJicKOlsrlQqcnZ1lx7+EGtMmGkVF5P9fUOU6ri0UPooYHluM3xURlLb1m+TcLotTao1iYDN+pffLRlHlU6OZ3/dHF1y64Aps8DTUAarVlUJA85fu+ON9LM2hq+57AMgWuboMUpcOYFrIhXnZ8qF9hvlw54eNX1zOVgq6y+D8/BwODg7ggw8+gEajAYPBANI0zeZHHpy86uDtVKlUoNPpGK8sCIXLLonVnuifQN5A3sH64NVHuFs8FLTdbP4FH3mGf2vloBa+jinXM5qvK29NGp6eO5o139A+15SHuwApv6DNkPdIfgmnp6fw9OlTqFarsLm5Cf1+H9rtNvzyL/8y7O7uLtgr/X4fjo6OoNFoQLPZVB0pa8J1kVNXGT66TdF47bXXYG9vL9O/ZrMZTKdT+OlPf5rNdWgbLitgTOGj27nSab73+aYM/bdq5OFjm69K4+fEd3xxCj7n6UIQGkw2BV9MOq0N/L5UqUxN/WL6EWz8n3dcYN50kxjerW7yEfHT1Yr290lYy4MwaHmX96lmIUEeXreN/ZBykF6pvkHHFNO/8bc0QdPVfibwIwsxOGtbZUGNcfyfGleYlhrMtjpgHhTLcIbEmsxDnEg0QGFbCUCNElpOrVaDmzdvQrVahYODg4XjAnwMII2iFRMaw5g7IhAY3McgFPIhHjEkOfh4m0g0SLRIaVyOLh8n1BovENo+Gt7No/xonBq8HFu5oTwwnU5hNptduiMN5TV1itLfLmhlrKsNTf3A6ZCcWj59r6GXO/f5O5OCyAO5Lt4KnZ+kOYfuTMDd/FLgIG/ZNkhGWcw5wSVb6XMMxvoGYkPGulZma/j0qjoHrirdRcIUsPKRrzZIATBf+kLKc31LecGmM5kCeL67q0w0+r439YmkC5q+d5WTJ4ASCuyDJEmya2V4ObyPXPTTPtbWlZfF85HSmuqj6Wdqz4zHY+h2u/Ds2TOo1WowmUwWnNKSg8AXy9LPXQ406R0G4kOcW3kdIT68bOIN7Cuq16CsqFarWTAWF23nAfdJxO7XkLHtI+Mlmvn32qCIDZKssOm6pgCQrTzq+NK022w2y+5wRXmH3+V17HLeRL/B+fk5NJtNaDabMBgMoNlswjvvvJMtfEUaJpMJDAaD7EoNXk9TWfh/CB+udbJF5BnLUp9QWbQM0PFLx9/29ja88sorWbrJZAJnZ2fwwQcfZH4t25HGpnJcz3y+x+cuf50tXUxIdv4y57qy5G2TrbZ2oXqlT1kIk/7Pn5v8Ylq4+tSVn8lu0eRD/Wk2e9AFKeZh8o1p62H7Lqb9Re9Fx135XHbShfOYB49X+GI99y0PmtgIgvsJJX1dY0tLaX2+kXy7+Fvy39L3Jj9F7qWuWDgaNACLZ5lTUAdvmqYwGAygXq8v7GjFQJdthwxdtcyFrm31CA5cn0mgyEFZtgFfqVSg2WzCfD6HyWQCh4eHcH5+Dq+99hrcuHEDABaVSACAnZ0deO+99+D58+fZMb2z2WzhbHsA847nVcDHqOQTWZIkMBwOYTwew+npKVSrVbhz5w40Gg14/fXXs7t0+d3I2G557tugEw1VPjRBHi7wNApz2fhzjcvgTqfQPtN8x2U6X4ABYF7puAyD0zSufZxJmjJMwQKbI8+nTB/FWKNM2JTSyWQCT548gXa7Dfv7+5fyw9WGtVotWLk1KeyrWLnIMZ/PoV6vQ7PZhF6vBwAAt2/fhvF4DM+ePcvodDkoOWyGI4XWWMPvbYrmVZXXV5XuVQJ1CtS1Q8dSXkcN10MoJIMlpKzRaJTJHpPugvnHgotOrhfShaRFgdedOxy4o4UagPREC58AxRrXC0X0ObVLXGUXzXPI33mPpdMgjz6Jfgrpnjct3ShvJF0kNGibJIm3HJOc4L405OELfrerT3CEyswiTkBBYH/XajXodrvw0UcfwdnZ2aV0jUYD3nzzTbh16xa8+uqr0Gq1rLvSlxXI83FwruEGjrVltyXaOxsbGzAej2E4HGa+OUlmYtoidoavEnkCRihz+YJlk26qKes6+Jk18hN1BOrDdMEW+PCxlzX5+wRLOU2aece3L5IkgU6nA41GI4uN9Hq9S3qDie/Qb0P1Ikxv2/gWgph8hnRLuHv3Lty5cwcALub+Dz/8EAaDAQBc8BUe5Yz6VR5dcBkLO9aQ4fKBavs09jzLecKmd0vHKpvsJFUwVivQqKHvyocLAyloxMGjz9wJZXIIoaOE5sEbxEeBXxVsE4XWOWwLlKCCgf+Px2MYj8cwGAxgNBplQo7mV6/XYWtrK3NkU2WFrmrhDiKaB6chBrRCVKLH1YZ4bAK2+3g8zlYOUqOJ34XDhYJJyZDK9QmwmOD7Tew+KStswtWVVnoWO2CSR5GICcoPGoXYNJ4kPjTxWlHKkC3PEGeOBMlJbsqft0nseptkyHw+h9FodMnZg44jyVkYAlN9aGCgqLnXVjb+RplOj2S09ReHjxMyb9/axo+NrjXKhdD5FccLn2dW2dcheqfre6rn07x8yte+p/AdvzwISiEZXqZ+95GBLnljs8FM6ak8lOilthrmQXV86VtOj8ZpRe0F+q3UXpq+4jT4IEmS7D6u0WgE0+n0En2muqx6TMYCt9FDoHWMYnn4TYj+LNmatrLy1s/XEWvyddh0YpPdwfOm/7vGJE/vkilafvbheZ+0pjZwjXGp3aX39LeLLp6nq20wT/RFdbvdbIc9/a5SqUC73YatrS3Y39+H4XAIh4eHmbNZ4+vRzIU+/qKYMsw3v6voj6OI0XZaOWYqj36Pvih61x6+p8FFevQ+bpYxBTWW5TvV+mjy5h2Sf9758apDOxfR9zZ91+e5q919x6DWzqdpbfxjmu9d5VEdG8cmHtPL4xk2ermOTvPO4580zaMhPmaeD6W5UqnA5uZmdoT65uYmbG9vw2QygfF4fIkOunAkht5aFHz9zNcNGnluGlc2eWObozTQzrcm+aXlN54u185YE8PgrkgcCLhyEQNYOFjwnjh6lHCj0VA3HB6hgbtycdUHVSo6nQ60Wi04Pz9f2G27LAWi7KA7n0x4+PAhPH36FL74xS/C9vb2pSAjDdKagLtCsQ+KFjQuwwhA56gxpaF5PHr0yCn88T4OjWNRUw8aLHHtOr7uQj02bEqii09W3da2ScL1HYDsmIixm3uZCG2DvMA5jx5xa3NySUYKtjdAcTxF70xL08X7uFCu7+7uwvb29oIjyISQvsYTFOr1+oLyHRvYlqhzSHdTHh8fw9nZGdy6dQs6nQ7cvHkThsMhPHr0CNLUfESexjFG0/GVrKuWFWtcPyCvSUa2j5EgySV8F4tvUUYCwKVFfr4n2NA8TXoaTYPvYo5B3va1Wg1qtRpsb28DAMDh4WF2zBb9piyygM5dAC/aDoOReCLMfD6HWq0G9Xo9W7GP9hWvm9TGtJ1MASjOF3wXsHTME31H8whpB6xLo9GAs7MzOD09hc8//xyS5CI4S+8Vx2+k+tA5h7ZHqF5VNKjuRB0faGsDQGH3uhcF9DlwoB5AFxLHAG27vDKG5iU5VWPANh4leqQ6Fc3Hofnb5kPbN6gb+5TjQr1eh83NzYVxNB6Pod1uG3ce/tmf/Rn81//6X7NdtLizAvvKx4+grQ/KLJt8xr+1OjDN+7rCJyjE2wEX+RSFNL2497XT6cB0OoXT09NLOt6dO3eyq4gODw8X5jjclBFybUsM+ASxTMirZ5Vtri4DXHMG9ydp7HxTfjbfKV9QECKLtHXRQpKdIfPmcDjMfDTUZvJF0eMxBuhVKGmawmg0gldffRX+3t/7e9Dr9eDo6ChbHPLJJ5/A8+fPYTweL9BVhhPXNAjxM78McNV9VXLYNn8DvNjNTfmRppd8jwvfxyIOwDzZSQY6/44a4Vy5c03EtFzqBKc7EmkekrPpKoPW31UXSQBIxiNVxtFxzo+Hpu3abDZha2srmzi4M0WizeYwjN0nLsNBuxpCemZyqnDHois/nzrz/jIZnSaaTDS8rHD1hcnRpkVIwMrnW8oPsfqU5oVBqTRNg44iNrWVz3g3tYPN4W5CjHJN+YQGLqgMN81vvEwNT3D68J1JaUXnZcj9iz4BH016l7Iayuu0TXkAgh4tz8sw8Y3UZwitcWDrz9DxXZZAz8uCvHxp0xPQoUaPYqIL3Ez5aHnG14GhGR/acvPoX5pnvtDqU9J3XDeTjrNzGXaadzZw+arNx6ST0x/6jjs/XPImpD4amzJ24ISmRfsHgfeq+5RlqsOy9G+fslxjyjUn+zgLY89PSBvKR1ywShecmb4pEpJTularLbQV17dtsNmb/H0MPrOVp9VBtd+6fDOmeUfrj6L5SPKuKH0JfSkI2/GJ8/kchsMhnJ6ewpMnT7K7jblOHjp+XG0HcNmO0fgWrro/QeOnyZv/svRxie5qtZr58XZ3d2F/fx9effVVuHHjBrRaLahWq/D8+XM4Pj6+tNhIK9fz6iyx8w2Bxk921Xm9COSVpya91eSv1djjNh8NzV87p8aUEaY5m+fNF4S7aOW6OdaP6/Muunx8er5+CVN+dAH9/v4+3LhxA7a2tgAAYDAYZAuSptMpjEaj7Ht+BKzWrpRoCIHPXPwyyY48Np9p3GvGTShMeqH0N/WF2+SCpg0WgrF5VhSbBF6SLK4yRAFhGijcaPLtyPl8DuPx2Gkwm3aEJkmxd4eUFdge9Xo9c/hNJpMF4wFXcgIsHneSJAncvHkT9vb24JNPPoHHjx8v7FrWHtVWpONYY9S5jED6jvIJ/x+fobE9mUwyfjQJGVe96cSKwYPpdJoZaQjN+fh5HcZrlAsagR+jr/v9fnavNPI8Opul+yfKyl+hu644TO0uTdB8zPmWbXOImOQmGtCmoCpdMEPppAp7kVglf2DZuJvZ12CMkd6HF2jaso6rNYoD5ddWqwXb29vQarXg8ePH0O/3VXmYZFER0PI2153oDln+resEkLyQ5CHHKmUiH/vUwULbS9qxuUwn8BrlwLL72+Yg4TubKWLYfcjfaHfevn0b2u027O3twenpKXz88celGgN4FyM/LQrbiNuSruB/pVKBRqORHWNIvwXwX2gjyZm8cAVtTQvxbToPbSuJfp8Aj+0+r7zA4Gq3283ostE2GAzgwYMHcHx8DAAAGxsbsLGxEVX/8z2la41FhPQFLsJAaAIjeWDL9+/+3b8L//Sf/tNssdiDBw/g4OAA/uf//J/w/PlzmE6n0Gg0lmILmuC7mMKWLsYcs7a9/BDS7q65Rxoz0skXoWWuMg8Kbof4xCVwQTm2C92th8f90nJsKNIvj8CYw3g8zmjb29uDf/gP/yG02+2F8vG0U9whC3AxNmk69LsvE+t5NB9WZXObaHD5MOgubv4N/V+SV1Kel3bG2lYA+TgNOTSVdK32MJUn7UzkwhmPQ8R7OhB0F62N/jLAZliFOvltZXEn/fn5OcxmM9ja2oIkSbLAH7a1tGqTgwfiOd3LEPy8vJBvuYMrTdOFutF20xp4Lv6XDHUpwKtpP1qHMvP8VYSPfCsbpPFH+cu0UinUYKOBP4kX+Rij30h5cbpM723peHpbkFmSI9z4cxmDLrpN71yTuw+m0yl0u91MgUc5ww2AouQFDTbZjvLg6SW4ZDvKZC5L8fQHPIZyf38fer0eHB8fG/lbmrdsjkPpe4lXTNC0fUy+WCMeYvQFDbzZ8uNzkG/Zksziuk0ofBzkWpgClS7dK0ZgWtJlsexarZYd/VepVLJjil1BBhONMRyKlFYXsExc5MoDRzSAQedwU7tLxqlp7jQZtrweNp1Eei7VzzZX4CkJea4YkfQaqT6xoe1vEx3Yj+jMS9P00lHbZdJrqV2Gd60t49g6m96M7xHS4jfTd1I5seGjb9h02Dz6P5bl42PSzkd8DJi+Kdpe43IPj7GfTqcL9wCiTOV+LfRf0RPKKJ0+MsTXftLywVVFSN9rbBD+LERuasaWqz/41XH4rNVqwWAwgNFolF1DgAGRRqOxEPAwlVW0/A/RXfnfmna36RQ2WnzSXheYbEzfeYDrQdr0/Bmny2UD2/S+MtnP0tylAc4hfH51laOhBRFj3FP/OM5r1Wo1263fbDazXbBJkkCz2YTz83N49uxZdn0WP9rYl7ZV9/F1Q4z29LHhXHlQunzsX1oPbkNwHwzaRVzH4+XasBCM9RGKJkjGspSGEkgN3ZDycQUENXK4M7fT6UCapnB6epo5FLAB6XHGZUYMZwyFS0GhF4cDADx9+hRqtRq8++67UK1WYTKZXGo7V/+hs73oXQ4+MDl+6HvTd/zvNE0vrUzW3GPgY4hKhhiCHyHrmoBdzqg1ZJj6SnKC+xpSZesLHN/SUSn0flR0woY4mfG3r0MhVB7mlaX0e5MjTpIpJsdbjHnXxpOu/MfjMRwcHECz2czmSpwfOb10vta+09APcLFoajabZcfPuMYZLVuTP6bF4wsprZPJBMbjMUwmE2g2m/ClL30Jnj17Bt1ud8FZJsHF81If2ByCefhB65yLrU8sA1eR5mUi5txhOvIptA+o48XXMNLmj/nhfCQZTUWAX0WRJBenzDSbTdjZ2YEkSeDRo0cru28T24I6PuhzSrf0LQYMsP3QNkAnr6l9qSzicwr/hjtOpX6ji2e54UttP0l/N8lFl8GPp9rw44ol2IJsyJchx/+bEFse8rqj7XJ+fg4AL9oWn5fx3lstuPO9KNsagX3vuwNTcvDEogvz9eVLkx7r+kaqA7VDfb6V5pKrhHa7De12G46OjrLFDqibmnZ69fv9haMZi4KP/6Ns438ZOqKNjzVY5ul7SZJAq9UyBr6Oj4/h+fPn0O12od/vZ7IAN12sAqHlxgoE2GyosvJ9LPiOH5cf2adcX7h2j2rqYRuLLr+Kz3ehbaopj39rW4RWpkAlnuyIC0EALnxAv/7rvw57e3sAAJk/KEkS2NnZgU8++QQ++OCDLA8M2Nr84muUF3l4PXb5pnmd6mOmMUGv56RptfXzujNW6+TjMBkA9B115PJgUohAmE6nMBwOM2GNDoNWq5WdNW5zHND/ywLurAilz6Zg0CM8sd2osQYAmXO60+lcEvi7u7tQqVTg+fPnMBwOYTqdZo4bbT9qDIFY0CgSEl/Y6OIDV9tPUr7SOKF9gs6hTqcDtVoNhsPhgqPNlj+vj2+A42WCLXDoGo8mHtAEbm1jYRnySeJ5XKUmTVCSg1RqH43zFsGPFc5rbMfKx4ZqtbqwqpgG82zzIb73kRt5YTqtgNLCT5AIAW9vlyOQKzIx2oMGJahjHfOezWZwdHQErVYLbty4cen4bZtMdfUZb2deNy6bbfLYFMCwyRmez1WU72WlOS9dNtku9R+e8LKxsQGNRiM7tUSCRp/y0blszm+pHj5tY9LBY8lCHFc8eGqjISYkGRdax1qtBkmSLOh7NpnBr9WQvrFdIUPpRQcJ3f1bq9Wg3+/n7iuNLspptNGbBxo6NN/jb7pYrYjgmo0G/o3W6Szlo9F5+bdaGyNvn1UqFdjf34ednR145ZVXoF6vwy9+8YvM8Ye7CyVbyAaTo0bz3mZbur6z6RVJkizIAZPvhOYR6j+gfgEbQvRqTXrfPnIFlzk/x5xjJOBR0lQW4N+NRgO+9a1vwd27d6HRaMDx8TH8/Oc/h08//RQALhx9/X4/1yJ2k/7oM59L34fSUpR9E0KTT6DDltaHR7n8tcnpUFCbLU1TGI1G0Gw24d69e7C/vw+NRiPzlUpXXOFxn9pFZDF0J5eMMelLWnlu42ntvOyTpqw2iwkaG8H1XrIxTXqH1OYme8TWRz7jJI8vzZZW0kk1cyalidbPZ5Ma+uTovIcLzqmvxcfWk9Lkldv8mPbRaJTpY2+//Tbs7+/Da6+9BpubmwBwsQBpOBw6xy3+7VoguMrxuCyf3iqh0fF5Opc/2Kc8DVDuuHiBn2IjvUN7Qmsrmsq8FIyVEoZU1lYwn0ypsDI5YUwNYnKO0uMGqYBqtVowm80WzlCn30k0lBU+QkVTpyR54XTH4Gm1Wr10tNrZ2RmMx2Not9vZt9h+N27cgN3d3UyA0qMHfCYnPlBchnBe2BxUvvnwVa0+/GQaN9ge9PJyPM5zPp9Do9GAdrudrc7A9FzZoc+lcqUyX0ZI/cCPNuXt49NWtL2lXTU8jUSTZMDFMJJdoAsseJk8eFXkXR6hBr1JrnAHmA94u9dqtYW7nHHFMS8T/5dko01pthkjmjlcw2/0Pb/T0USXL7hxxfNzzQH0nWZOQZ2AykZazmw2g8PDQ+h0OrC1tbVw9Ahf9UYdpbw9aZvx55qgGaZ38UXIeHd9U6Sj7LrCpafSND55SjyRJAmMRiMYj8ews7MDlUplwVFrcnRIY8tXjnA6bDKK33uorS89bUUad3lhaxN8r6U5pGyaNx/TpjldGv+4g2o0Gl062p3mT+dlfG7aWWqST3xuxJ2ymA/e1zQajQrZ/YvzDtcvaN0kepdly5lsG9rGaBtQnl6mfr1sXd4mD+h7LkPy9lW1WoU7d+7A/v4+vPnmm9kcjsGwmCczFcVfOK5s+aM9WK/XIU3TS1cwmWj1gaTX8NOXJHD7U1tWLF5AOrXIU44rTypzqSOatmOz2YR/9I/+Ebz66qsAcHF/55//+Z/D0dERAEB2RLyJDzQ2BKeL0mFKK5VjyscHZdQvbfYP/z8Wn+bxNblAxyvAi3uLt7e34Ytf/CLcvn07u2e63++LeeA3mF+RsOnOHK75PgSuvliWLlE2aOvL5Y/NZ2FbmO4qg8sgDX2hfjoXLUVCUzdeFzytBkHnDG5LUdng2w9SGk27or6CaUejUXbSQ6VSgS996Utw7949eP3116HZbAIAwMnJCRwcHBjtQO6T1eyODvHxvWzQ2ME+trLPeDHpGS7fuESLJItMdhpPY9pESOdWtIWlNC7aKLx2xsaCNPC5kyD0qEPuHKVOCH5EF/0u9Pio6zpB0/pgu6BRi86fo6OjLNC9sbFhPNYEDcUkebGKF48rDh2gy1IMfdK7lHnJQR9Kk0lAbGxsZGVMp1Po9XrOPG2OwDVewGQIa1G0rIjhRNYqKWm6eEY+n9yo/HXR5NqNs2yYxpbGUMC0uHOIHznmUmBDlURX37vyxflxNpvBYDCAer0O1WoVms0m1Ot1GA6HovzNKzNwsQ7OC9RBhfOypEz51tEWkMKf0WiUzVMuR542YGX6zoWyjIU1ygV0uON9XjhOUQ+TArK+8JVBPLAnOfDp/xSx5kTpqhHuoI4ByRGLZfH/Z7NZtusEIOxoQsmmwd+4Ihf7H3mDz6WSc56fgKOZg2kedH7Hk4Y4nUWjDM4UEw20fXCnZpIkMBwOs11uMY8rNpUfax6RVn7zcmKPNR/Qo6SXAU370qOcEfQe25BjgbksrVQq0Gw2YTabZdc12fpB41hzgcoYXodYATqtY1dTriTXlulLwLnAhul0Cj/+8Y/h888/h5OTEzg9PXXOFxq6JfsC616k/LGhbI5wLc+G0sznX+r3MpXNFzyE2Fl0DhoMBtBqteDLX/4y3Lt3D37zN38T9vf3s7RpmsJPf/pTODw8zOZy6djFMiM2T3GZK7U/ynEcV1epvfLCpPtKoAuiJMQYg77jw7XrNC8/rVrG+QZyV40kSWB/fz87zWQ8HsOjR49gMBhAmqZwcHAA9+/fh+fPnwPAC52Px3q0Za0RD6FzuslH5tLn6G9X/iF0SUFY6he05Wta3OaiQxWMzevc53nRitjuntPmx99TxQX/p8akVCY3Jl2ReansMkOzuoCmpW2BP7j6HpW0Xq+3oLBsbm4aBxLezUtp8Alw+Lyz1S/UgDGV7RrskjNSI0R8BzKmbzQaC846XPHoMzFr2/wq8X9M5Jl0NP2w6kCMdmI17RDmeQHIDmF8XhaYxpxtbJj6ip7MYEsrGTR5nZom2aQJyOKuEXonYJqmC/dUSfmGAnnIdAcdnqqQFy4HJTof8D3uoNHwqm2+COlD27xse++ia41ywqef8N5KuniBH00VMh5t+rgJnM+19zVJRk5eYFtITk9fQzHUsOTjE53w9BQem23hoofPEbiQBU+owUCUFBgzyWvabi4jmOdD6ViFXRSiRwMUo2OZ6o99t729DQCQ2VCcL4vQ+WLlLfGFNIaxPiG6gUY3sQF3HfKytQ4bG122b2x2Nbdx8X8eiNXaqdQZhOA7TShfaXc8hgDLof1mG1cunTaURtdYpnTy58vUj0y8iM9msxk8fPgQDg4O4PHjx8bFDwiTPcXzNcl/7ofT1sFVTkgeq4CvbHTVz2UD8PFJ09hkjM98zJ/T78fjMbRaLXj11Vfh3r178N5772XvcUHVgwcP4PHjx9n3qF8gjUWPlzx+ldiQxo0pHW4swQVuZeHxosFtBmkM0DQmHcJXR+cLFbhs1fpKXfLV9J0P8srXkPw08sXF35J8jMnXnDbUY7a3t2F3dxcALvzXz549y+ya09NT+Pjjj7M86Ol8AMu9hzsEZZcLPvSZdIHQcaPlM83YpTylPR1FsmekbzV+FpNt4PJ7LH1nLO8wU8Xy7JZyCeYkSS6tJpXSmxxUpjxNNFxF0JXzpgFA+7Lb7cJ4PM6+qVarsL29Dd1uN1hISgYFpyW2cRcCiRZKZ5q+WBEmHQ+nLYP2Ca7cwKDP6ekp9Ho9uHHjRnZXZbVazS5BT9MU+v1+tsqI00TLiXWE1xr5UaSzzpcOPGJPkuGUp1dNqwu23ZYuueKSQXyc1mo1aDab2e4oqbwilFykSZpHNQr9eDy2zo38eUjfmwxXVMxxZ1lMR0CapjCdTheO9JPuw51MJnBwcACDwcDpGKN5aw2qUKe1Bst0Mq5RHCRnvq/OUNT8IemCvo5ZCupg4XNL7JMTTAYaPZUnlg6UpimcnJxAtVqFV155BUajETx79szqrNXIBS5zyzzmab9K86d07Cl3FlEbwjSXudrBN/iAeaKOXXa9xgXelprxmsfBJTlYbE7AWHycpikMh0Po9XpwdHQE5+fn2XMeDMlTnokf6DUIoaBjxYfv+Mr9kOOwTc4khMkRJ80JpvQu5JlLEGVxziZJkl1ZQhc64qJ2vmgT4OL4xqOjo+woeICwOdaXx7X5U1p8joO+6vDl5Zh2iw9wgR7exyjh8PAQnjx5At/73vfgF7/4BZyengIAQL1eF3eda3lpWbpI7HJQ3mrriMfB+p7wd5VB28Z2bCz3f9rmDN+5kusVtrb3yZvLsavUp7hwHuuK19hRvZXKdl+ZHcP+ajQa2dw3nU5hMBhkPPTtb38bvva1r8Frr72Wnbp5fn4Oh4eHWTCWn/J4HXTysiNGcNWUj/SNK+8i+lvKE08cQ0iyDn2kNJZiSqvBQjBWq3zHMphc0DifbM4KmoZ3tOneE2153ElmwqqDKZoVLrZ2tj1HxxXAhfN6Op1mgh7v56lUKkZHiqts+o0m6Kpp6xBjRvve5mCndeBKn5Y3uFMLV+QBvGh/qhhiH2A54/HYSpMJJjpjGMxlh08dfRZk0DY18YKm3bV9E+LIl+pkG4cuGvLKwJC+cJUpOYd5eb6Qxikqodxwk8osw3gy3XcKEGfcc37kedJ5Q+ItTf+aDEc6b5nqgHcl0QVGJsRy0NjGj403XWPUZyz4frOGHto25fMB7xeqe9Gddqa8fPqQy0OJJ+g7H4c7dby4bApJDprmV42Oy6HReU2LdrRzKfYRHle/u7t7yckZQ9ajEwad+zabSGvLmdLGkAeavE18x1GEfHIFnkLzDHGCFQUNH+S10U3vlhGgwIVceNem6xtfH4etzhoHsFYu+9i0nIa88NX1uTyLMXa0MjLE9uJpi9K76bingTHckYhth/Y5LhYcDAYqGy8PtHlIc71Nfy8rYtHnyievbeSrW0l50EVsjUYj8wXh6Xanp6fw+PFjODg4gOfPn2eLb+nRuyHlhsI11qX2CClPIxtc+dI24sHuso+BUPjWC3lPY++EHO3J85bSmvrTtxwTTLJ5WXKGpuMBIXoCC02n1U/o39r5lNqFXIdHHxi+p3rZrVu34Atf+AIAXASSu90unJ+fZ9d60OtebHRpbEaTbV0WmNqwiHI04L5qWxpbWT5jeZWgvApwed7B32hjaK/qcLXRSu6Mpc4kvoqUV1h67sOg+B0eQYjf40pFCu6gReGGO8K4c9gm1Mo4yG2Q6MX6IrNhO+DxJXgEbpIkUK/XFwJ/CH5M53g8XjhSCpXDsgxEDpORoVEEpPSSM4bzUR7BxO8joeXRneBJklw6cx+f1+v1S+XzsVHW/roKkNqQj7U8MPFsLGeqqxwpHR5laHNG2njLVifTN0UhZE7a3t6G2WwGJycnxsvei+obH+cTdQRI9PAABVdsUZ5rgd/jN3TngPZ7W7vxO+h5vq6+azabsL+/D71eb+GUB84DtD1chotJx6Hw2dFy1XSNVaOsRhhCQ9t4PIbZbAbHx8fZDoi89yXaHK4+QSSqP5scMnnkHdIiLfJD5zbdIVYEpPGPcgblIDoRms1mITQAXMizN998EyqVCjx8+DBzXlAa+d8Aizo9hdQntO/xuzLr7KsE13HwfzymmI7TMssgDmme5+9M+kSRfIJ5810gZQCOEZRTuJMKF01g0C0UVFejx2T68JXke9F+R3mB+kRCEMonZepvDpST9OoPPPFlNBot0D6ZTOBHP/oRJEmSyQfkF2n+1LQ19ylIuqup76X8qdznznrffn9Z/Qgu3w7Or7yP8rRTs9mEb37zm7CxsQGNRgO63S788Ic/hJ/97Gfwgx/84FKQA8vmi/eXAY3TvqhyNUD/GA9QrJqPi7RpTP4DiQYTYt4tjt+78tC2h822CaFzlXod+uZj6UK+vG3SBdHWMLU1LgwBuLDhPvzww+xUsvPzc/joo49gPp+L10f5wGYTlQU2fXrZNPhi1XIQ4LINZoJEa7PZzOIiUno+rmy7Z12+bt7GxmCszVHoWo3gA5tCKEFblhQoo43l2k3AaTOl44qSNNh5PstkWE1Zof1J2xN3MtEgO4D5LkBTebY+oYZfXmgDSabvpOfcQDGlyzte6G/eJrg7mSqMmK5Wq0Gj0VjYQYt9xemS+sTHcLtuMI3xZZWZJ4+i6ZXGki/tRRnnGnlBHUt8fFHafGnFyXs6nUK1Wl24FD5U/kg0mYLXsR1jedrERQuXnbH5IE1TqFar0Gq1AACy1eGmANZ8Ps92xVL6QucfH/6W5CvNIxTaPrjusnzV9YtRPs7duDJT4quQcrT8LY1/ST7Y8tPwoy0fyUmkddLQAIJEM89XQ69EPw2UjEYjMfhik9UuOZ4kycLxX9wAtAVntE42KS2mp3XEdBoHsosvQuTtqsc1tVHwf4AXzgH6u2zw5XHTfMafu8ZjrHleygcXq3S73SxNkYsIJDvJ1j4YlHXlx3UjPqZNbewKuvnosbbvTWlXgbI4AwHMOi2Vl5g+SZKFoBguWC9C9nHeWWZ/rZo/KB0ceXxLsexXl16lzYP7cjqdDnQ6nUxXGAwGcH5+DkdHR5ldyv1AqxpLRcvnvN/QxYBF2KohKHJMhfp1OA+6dAb+twm0zW267TJ97iZ/SJ48fMF1cZ86m/w9MdoNd5Fj/XBjRqfTyTZxNRoNmEwmMBqNYDwew2AwgMFgkF03MRwOF3bWcsRo/6JQBvmAyKNPuHRMn7y039rKtpXn4gdJBqF9RnfE2n54HbjdKpVrogdgRTtjEehEog5Rk7GqZQ4N0vRihybNV8oLV5rayqJpXPTSd2UaoC5odjpNp9MsEAgAsLGxAbVaDTY2NmA2m0G3280UPuqwKTukXRgIzht4XAkd2JJj3RbooDs6TO1OdxlikGc8Hmdn7ddqNbh16xbUarUs2Fqr1WB7exs6nQ48f/4cer1e1l9coEi70lwOMu44pXVa4zJMTpGYbUaNf5tT1hdF9K/EP/x9KDTyVutE1gLz6PV60O/3YXd3N1vpTt/zcU+frUo+hhr/Me6Xw/and8W65Iq2nVqtFrz11ltZ+oODA3j27NmldDiX4crw+XyeGRQu+jWQnKSufH154SrMrcvAVdEzYiK0vshrpuPU6TisVqvZKuzQAJNrHON8aDrCLBQoU7ispzvYcHU56ll5MJlMsmMnXfqf9KzMPIw8wG04gMU5FUH/Np0SQ6Gpu6TbmHilSEfvddB3JR0ojzzh85d0XKsJeXn+7OwMfvKTn8BoNMrKxIBXTH2YQ3LaIDDIhjLBhLxHX6K+ggtvabtLMs3knMJvYvLFVUeoPMarL1CnxH7AoCveP0aR11eikUt5ApAuunAhEqbH8spwXHvsgKwNWtsI2yj43rlaLeMnrsM1Gg14/fXXM//OcDiE4+Nj2N7ehlarBRsbG5lvVJLPqx7vRcoebX6o+3Kd52WC1p9iu0cxljzjtod0otR10c0k0HaeTqeZPm5Kq233WG2WJAm0Wq1M5uBO1+3tbXjnnXfg7t278Oabb2Yy68GDB3B+fp6dHvHTn/4UZrMZ1Ov1LD+pjJcFRduCph2hV8FuMsUuNKjX6+IJVtLpEOgbkMr3GV8czmCsT9RaQ4hWMeQTr+SQtUXqtWXGVr7oYDEFXGzlFqUMhqxCMAXdTOloerrqV1qNLPGMrb2kuuSFxsnjosHlRJPS2BxHpuC+KQ9sc340mgR6PLQpX0635EiLGbS6ijAZBbb2MMlIzkc+Bgd3kGv6w7ffON227yUZx/NxtYOG9zXzjY0WV334M015pjywj/gqWlMZITDJE84fvuWEBABjyGs+Bmzt71MvXBTUbDah3W7Dzs4ONBoNOD4+Xrifix5tjP0mBW9c9Ev8z2VoKLRtTdMVrci75rlVoAw05IFtbKPjzMWXeeRkCK2YFz2meBmgY5eD6+U0OGRbaBcDvM/y5IGgfU6vm+AGoU2fiEGXBKmdNe0Zk/di5h0LeBoDBsQQq9aj+byg0UFNNFNeCpkPtG0h6YRoO6VpCmdnZ5lDz3QthC8dobozpzFkd66pf9AxRJ3Q3MZz0YXPXGPV1ucSnUWNOa2fQIsQPdcGVztxvR9/JEdiqM4W4xtJZ+XpfdsC09NgNI6LULqLgtY/xJ/ZfGY+yGNLSUeVjsdjuH//fvb/4eHhJXmNfMht1TL1C4C/jNJ+78rDpl9dV4S0qc3P5ePf1JbnQ6erHFc+qxgLtP1s/sdQ/cn1XKJFm4bbV9vb27C5uQm1Wi0Lgh0eHmZHFeP1h6PR6JIMswWaXwaE1tOnXzXjqkhbjSOGrsxPIrGVxX+kd7Yyabk+OlOhO2Ndgtf0TlpFY1JUeToTHa40PsEMF6gT3vadzaF6lSZ5yXmOuwA4pB1g/P+rJlhNwsXHyUcHb8hOE3qkkekoB1wtbVqVit/QVR/SHSaYlgecTPldJV4uAr71j2GAuwyKPAYifm/rX1NQKOReq7w0m2hzBSli8i03jrEN8iqXrjEWyksu4Li3yaoiAhrSc205w+EQ7t+/Dzdu3IA33ngD3nrrLbh58yZ85zvfgfv37y8cUcJXv8VySGjp1chOH3qKlsEvu4xfNujRt+hAizneuJPU5xsq4yQdL69Mss03ePqAdGcSnbv4POTS1VwyTmNjxARt42azmZ2QgvKYtzFtd9ov/F7LNYrDfD6HbrcL4/H40lFYiKtg+5h0P5fjw1enMZUtpcWAJPL2/fv3C+HrPP2DMltzRZImL0w/Go2y/6vVanavmmb3MQXKBp860iBaXt5dht9jWeNL4wtA4Ckw7XZ74SSuPPcJSwgNXkhziU+ZND3WD2Vgo9Gw7uYqEr68sEodV0sryrzxeHxpEcrx8TH89V//tXFHJ+7Mxp1s5+fnAADBu42WBRd/xggcmP7netXLCKy7dNoCDfAD6E7E0JRF/we4LF9d/in6rYSrYs+G0CnpnEUuMBiNRln+7XYb3n777Usy5Yc//CH88Ic/XCgf71indGmDamvoYNoJuyo9iSJW/IDr2zZZjfq5JKfSNC30NISowVhNADI0X0npl1beuwxb6pChMBkCNkHuwyTcSWWjUeNgN31jKx/T+SjhvrQBLO428FGMYilUoUadT3CG96VWgGno0uQlORQpLYPBIHPOVSoVmEwmUK/XoV6vw8bGBlQqFeh2u9nRFkny4s4QG70a3lumMC87NO3n+pY7UEMRa3JzOeNc5UnyT0NT7AC1ab5yBf648aWVVUlycWQLHoEbw5EljTVJVrnyd9XFFtgwzamucmPICEn2SWlQtnFlniqj9Eg/nMPokZsUJgcAHataXYTTz+si8Yem7VxlahBLZqyRD9j+MWU3hw9fcP42wZQnlxkm/cpFs5Qfjus0TbOgLD3qmPN0TN7WOHhM49L0Tiu7Tc98nef4jUm+4f+mfqHzo9TGXOZJDhbTfILvbH1osuOKcJrS8pHParXawmKANE2h1+tZTwhaBsoow0NsXU0eJueLbZzlpZWXbdIDTDSY9AEp7zzwscHztBeHzf9h87No0vlAO5fEgEk2U9mAO5npUdI+POZK49uHkqxGXpB41Jb33t4ebG1tZfr006dPYTQaQb1eN+rWnIaYCOVn05yiyctlB+DzmDzO72l8/Phx5uvh+W9sbMDOzg6cnp7C6P9j709+JEuS+3DcXuyRW2UtXd3V0z09G4dDUiSH5BCQIEg6CDp8oSMB3fQX6KD/QAed9BfoIugoQLoKAgHpIAgiQRASKYkghzPD6ebMdE93V3UtWbnGHvE71M9eWViauZu5+4uIrMoPUKiM9/y5m7ub2+bbZALj8XgjR/Ba/eZQe3Aet6QNlaWhqqr6BBLM601fwGbhR60dcVIj194K2Y2cjhRdmVNHbxovQjIHANZ4UfLHUm2cmC625MHRarXgzp07MBgMoNVqwWQygdPT0/qo9KdPn14rl14VA2Ab2030Qyp20d6m0MaTl5dy+SX3W5Qxmk8Ykvtod1XV9dMLuYy36pYYtLTFd8Z6DFlpAEnBEoDrx7fSDsBZbK9yzx0sFkcyFHDg32pMZYHH0PAYPdJADdEQqoNmvGMabTdGyIEN0RGjWwruSMD3oRXN3naSvqGgq7xpWin4I5V9dXUFVVVBt9ut3+/v70O324XDw0M4ODioj/BChYe7SnhQI8U5jQUWQkr/JoC3SUp9Qt9octCTN/821I8x+vm3lr6U2ojLh9g3VvCFOVI+PDiWG+ixBJL4u+FwCIvF4tqd5all4DtN5mpBSS0fXm6ofCobNJ0aoq0ENH7hz7vdbr1iXMNsNoOqqmBvbw8A4kebhHgsRh/lxZiclN6F8o0h1ieSHZXbfx79GMsnxN+7DouN5fk2Bq3fNFluoW21WtU2d6lAi/a3Jw8K5FkMKmoyKjWgFtNTIX7X3kl6ydpPKTSG4HG+Y7Z+6JvQO433AK7fS6vpH+p/bSJgg6v50Y9B+s7OzsQghwUhmeHt45w2iOmJphALpljsMGkxRinEeNnqC9J8+M7plLEv9TVvq5gPKtEW0sEcHv1j9a0pNBssZJPGUEqnxcrAtHiyAU5goM4K2e5Wna3pt5gdyfPixwhbfLiqquDdd9+Fb3zjG9BqtWCxWMCLFy/g6uoK9vb21CsFtglJnlhpzKkLPdGiBHBREOb76aefrt3fS3F4eAgPHz6sF6OenZ2t2U1WbDKmE9JFkmzLpYvH0qh8LX3/8SbsFCtiMlnr88ViUd/1KW3ySC1bsw25zMfvQ3qHlxFLG3teAlo7SbTReLm1fVP9Ha8c4Gi32/Dw4UPo9XoAAHB1dQWffvppNC9+pUcshrUr2CW9po0jyymlJRDy43gai8yW8pN25UvzSJyX6NWN0kIlbSe/Fg/VaLTEvxo5pthi4FkbWhKuFuUnOSA8P62cELzBP6kttOALD8yGLkCP0de0wIq1KZ0cofWlk+pWp86qJEvBGnhODcbRZxZBxfNNcX4BoL5DCd9rR/PhKlZKX+kgxi4pKy+sTpr3fZPOTC6tmMbLA576WAME1nK0QE0JXg7lge9ofUajEUyn07XjxK35pUA7NUIqwzLGJWNHw6aNZa43NX3D0el0YDAYwK/92q/Bo0eP4Ic//CFcXFwEy6HHyFltmFh7pMoBD89YdVpuOU3mgfk0XUZTCNk3XmeX21+p9bYG1T2ggREuC1Mg2Yx0bFN7JXZfogXUhg75EdviN95nL168gKqqYDQa1QtKYuME8abv7rjpsPhEKX5EE3TFxkKJnTK8PD4+MXhCd3aVbofUSSRtAQFAmgzH9BhE4v62BRIdsfI0eyXFT7f6UpY8+LNd8TGrSr+OhIIe1+uhPTVQ7pH9Fh3e6XTqBY/37t2D999/H46Pj8W86G4UALh2T+muYJP0lKg/391D0Wq11hYD3717F+7fvw937txZS4dy1SurPT5hbj15bIo+pzYn51tPXJhudMD/kb+buuM4Vyc0RUsojbQwU9tRFvNDrb4yto3WhzHZtmtyBsC3kAfAb7trNltObEDLi2O5XMLV1VW9GH48Hovp0H/jO/N32a/n2EXessBLd+4YjqWx6kPJNtHmNqg851c7oSyLLW7I1dPat43dGasZ5t6Z75ihTQM/GkoJG4kOS/7ccODpNKXLV/NaAyzSb0vA3eLQc2MmxJiSoKdtltofKULA2kdS/ULf5YKWm8rDUvBQcrDpjjyt33ggVTJ6tTbS6PLWKcWp3xakNvEg10nh5Vv5hD4LTSpyOmP11RyfEmk0PkkNYkllSeli/C3pK96uOO4Gg4GpzBhtIeAYlgwSS//xd5Y+kmgogRITK6vVSlydi6vivv71r8P7778PP/vZz9TJWGwX6wp26+Q11+0lZR0NSIRoKFUOh2Qflahf0/XJQdM0IQ8i+OrWmC5tGlQPWe3jGOgYxt/8GDR8pulDb3m8nVPpznnPIQUWV6tVfbcb3hMXCkDy8qUxGfo2xdZrwnH1IsffyAUfl5rdnVuG1a5rGpofaZEDFpp53Xi9URY0AUm+pcQiSvIjX71vDRZ5x/k2x1AMu+QjxvxxfMe/yb07NVXPhtpOiotoeaB+Xi6XsLe3Bx9++KGqh/lvuqAK31M+3nT/5vK5R8bHxp5n3EltRvuw1+vBbDaD6XQKh4eH8MEHH6zFemiZ0qlrKWiq70I6VdN/Gi9LbUx5muZb8i7nXZSnHj3t1bV0IZPU3h4ZZrVFvTEVa/yjFFLldS7vUB8nFA/OAR2H4/G4Hk/aCWXtdrs+JSLUj5vuozcdVl7KseEt/BXzk6TvreOBpuX+PX4r3RebMs5CsWENjU3GloJ0rwTftScBhUvpIyRo/pyuHGcF60MDTDHjLVUZlYYUeMYyMQguMXisftwgx+9omhIICQktAOalxZpGCizEFA9Nw/mH/sZjaPC+lqOjI5jP5/URahq2Gdi5KSg90VAKsWCAJSCLv0uPOY2vc8rm49VLd8ngbylYAuslENpNq+kjyYjfhqzQArTUyEKncTabwfn5OXzxxRdweXkJjx49guFwKObJ84uB7+KTvueOhsZzmq7xOJPW9LsYFLhFHFLATQt2pECzcaxB2hTkBBvwCoZHjx5BVVXwt3/7tzAej9WV2B5nyQJpchzbiQartUDzrttZnG5OL79eg34npcfnUnvQ51o5PC+AV8d+VlUF0+m0SMDK+h7pxas/6C63ly9f1oHcbU4y5KAJPaHpWOzzUPB2FyHJrpQ+DslAabKl1WrV97GF8kuhBSdlcMKM0mDtG66n6POY/aMF7bUAcqh8K1L8BovfIuUpHUsc+ybUbkiLRI+WX1PY29uDwWAAVVXBbDa7NrmFdElXgmx7vJcsP9bmTcg4jLtVVQX9fl+UDavVq5M1zs/PG7krNhTj84zdWHyK/p8DKQ8+KY066Sbp7pKg7RGbiEVbiN632wQ8uuCmQYoLS2m0d6F8+e+UNuT0YawFx8jx8TH0ej1otVown8/h5OQEAF5dm8fnBnAy7CbaxzcBPJZXctw0PScDcF0WW74LxSnotRChcmNtFHpvbZcik7EWx8PT4ZrxjcJCEhqaYx5SmqWMvlL50LpZ6kjLtBosNF3K4PE4DPSdRjtvO2uALzSBFKpjrK80B4o/t/Kf9E7LIwSpPpqzBXD9DpKqqmolie/wPgeLYyv1lVS/nHHuSbdNJa21Q8r4D/GJty1jY9vav5Y0WuBUKs/SV5ZxmZqHNHaltJYyYnWSAutSGaUnAaS8Lca1R9Z6HNBtOKsW/QfwaifJeDyu77G6d+9evWM5N2/puXVc0zbT2ntbgSqtPz28VpKWEt/eVEfPI4Ms8rTEWC2lx2JloH2IK6g5er0e7O3twf3796HdbsMXX3wB8/ncZQ+G7LWYnYv/SzJfO940N/CAAddcx9o7hrUgEdIRkhchrFbXF9HSvqdlSfm1Wq1rTntqkCklLZ9sX61W9WKAFJvXwh9eey8VOXLCKmck/0LziT35Yl6h5zk+iZZfTt9YZCk+63Ti4ZzUMS7ZJZS/PfnR/zWZZdFtXuySvqd8EbrXNTXmYNUzqTIy1pZVVdX3Zw8Gg3oRCi2L6ixv/KcJeMvNpTPW9rnyW4oH0efY7uPxuD5dQ1somlJXzT6g7z1IiW1wnyqWt0SfxJMpfo+3P7c1DjxxLUqjtBCR20FanhZ5ItEU0tsleHZXELNPUmLU1jQaLRpWq/XFNd1uF3q9Xj3xNR6PodPpwN7e3tpx36uVfDrELvaHhG3HPyT/KJQuxV+M+ckh39mbvzXvGELtwnnVSovlPS0f/w+131Z3xqYG8NDZtjATpuVn/IeEt4UhQgMilocFlF40bOlz+o5Du3SY0tW0gOP1xx2ynPEtTm3McUtBiPc21UaUFlo2NwIl3pXahdLN7xum7Y5H2e3v76/lMZ1OodVq1UFOKaiYakDzer5pKMEzkmHEeSPE/zEaLEZUipOh0WEpOyVAl9LGWgDJanyWkjuTyQRarVZtnJY68qhpmYXyw7K6ddPyM0SDhPF4DE+fPq1PBjg/P6/1Ez2JgzoQmKd0DGiMDi6rqS7kcjsEa3tK+Vn0bI4hTA18qbwU3RHDppyfXYBkI/BgG76zOBGpslDLj/6v3f+WMhFAsVwuod1uw/HxMezv78OjR4/g+fPn8LOf/SyJbqSZB6dDNjwfz6Xs/RSgbfj48eM12/qm2Flv0/hNhSWoYk3fNDxjItemojIHxwHV36Vs2VC5Od8jXfzvFD2JEy148hHfmRKz2STdYpnACH0fQyyd9N4TKLf0j1QGr38OMLgMcJ32UH974xyptpU0QRdKK9FFy6ZXgfR6PTg+PoZutwur1Qr+/M//HD777LP6FC5cIDWdTqGqXt/f5gnwlgbfhd+EzbpNrFYrmEwmsFwu6/t9r66uah/06uqq9nVydzFq/ajxthbrCNlgXnqkb3h8C+D6Yi6+c69p7CLPSf0wm83q9uj1evDgwYP63fn5OZyfn9c7YrFOseNnLc/p9yWOdb9JkMZJaj4lbUVpHFEsl0t49uwZLJfLtbgz0kL/zo2DlJDbN1X2U7q5rSmdMtsEJN7yxsz4tzG+sNp79H+L7MgZI/REGUToPtrik7FWQZuaN2c0rZNjRp0luB+iPRTMkoJlWp45Tp1m3EtppfKl56lCOneyhOcT6r+cAWJpJ4tjHOtHi8JMnVSKPaeGbMjRwx0VtL5056xWjsVI8mCbAc1tIFTf0PjLMaAsigzzD8mClMB9btDDSoMlwGOhrSloRoAkK2JjLPSNJItjcsMT5Eptr10Y3xi4xYnwxWJRH6WEoG0Sc+AxnfQ8FHTQjDI+/rR8NIT4Q3rObQiv/WbJQ8rHWrcU/qTpU2Sm1Z4sCUs9LXXxOjyaDZtLg1UOx+pLv8F/nU4H+v0+HB4ewuXl5bVvMLiLdoxkz0n6x9PH1vppzjHPi7dJyKnmmEwmAHD9CCfuxIb6jY8X6sOk2mcxXySUZ6qeQedXmpTjZXr7m9rO9DnNl/6TvqdpU33BVFm6KVC+KUmTxDtSv1j0kbUsq40aozWEkAySeEn7FtPSxbcWeW59Z2lLjzwtpcuskGwDDq+9w/UnzyvEs1qeIZotdMXsF628WP/S93x8cF3barXWjiY9Pz+Hk5OTOg3lUylIHIpheBGSxZwXPH0Twq75SCgPsL17vR602+16JxoeD7pYLNTjjCVo9SxpL1vsCE/5lj7lY4mW1+/3a79RGncl+pDmmWKvNAU6PuhJDP1+H3q9HqxWq7VjP1E2AMR1GS8Hv2kS22zTVHuPv+PtmhPr0exbLQ6ixRno38gPePwwPdGI0m71ga1pNHjG/03Epu0qRKqt7MlXs6ms8IwT63MJ3Pfm7yQ0vjNWCoCk5BELnmgNhYYGrrxD4Co4aReplcZNDljNIEkxqqX8mhpItDzvoElxljwIKXzu3MeMA1q/nFWFkqPAaQq1C6aZzWZrThKlCdNiIBOPEyq1S09DLKgovWvCId+2ovXULzVwaIFkxDXVNh75lZJ3rvFude5K8ONq9epoqFJ153/HgpHUOcpFSNd4dtNakWpPIA0YgAidHoG7l3FVvxVWR6Hb7cJisTDL29JBbQrKM6lGfFNyI7fOTdsz29YjEniQNGciAsEdCylIT7/F+zylIIzm7JdqT5yo3d/fh06nUwcdPXyeAm4/S/YubQOtHbTfTYPufAoFAEN1wnw0YBq+EIVfqUHBdRmm4/Yt/XswGEC73Yarq6uar3Jtco0+/gwDTnRnItaRvqfI1cclZZHVzmliHJUITjaNEI187HBZnFIWXzCb46d7ffBQPogUu8ESEIvlZ7XXc8HHlhaU1soO0Ul3c1sCyFIMzALLN9iP9P8QLRT9fl+8MkBCt9tVZTGWi4skkS6r7LbKQZ5Ou8fWk18JNGXjA8Cav7m/vw/vv/9+3a6TyQQuLy9rXsyFN1YRGi8l21aLP1nGNMAr3T0YDODrX/86nJ+fwy9+8Qv1uoxUxOytbQJPzUO/9Rvf+EZ9vc9isaj56MWLF2uLgah9E/K7N4ld9N04cGzgohZ63QpAc7Eunlbz0SRe5f27Wr26k7rVasFwOFzjBTzJA23iTen0TfR9k/EByebiz6z2RAnEdIa3LSSfLwatLqlx25y26XQ64tiI5bk2GevpTA9CRqTXqeNBAu136DtLB3nSpL63pgkF2KgBLeVp7VMPM3sHfSq0gFCobzRjhiuUUB5S/Wgb029j/BYCFVKcPs0o1caQVC51KEL1pA4YF6wWWlLGEVfyUt1igQ/pmxhKyrWcoAjtdwxe83caH9J8LPmH0ob611oXTlcsL2uwwkNLjvHjqW8O//C+CLWdJzBpNWJpuTFnNIRtOogxuml7SPINf+O4m81mMBqNoN/vw9HREQAAnJycXMvDSxsF199WAzPWbxZ4xk/ouWR78PeUbotups9SjeVNB8xSyrbQEtOFqfDKf8nJo+8kPcTfYyCGj6FStnRVvdr5OhgMoNvtit/SALfX7tXkNL6T+p23k9XZtcoCiV5L/fg3TcBrL1h8Dcne1vKT+qKEr5mKEL97gyZa/hZ5WwqpMrpEmZ70ITs2JKtD7Zdi34aeS2XT8tEXCB1nJtEjyRzq/1nywG+s44nnE/LxUpBjF5eCh35rWkkG5vpBVnveY3da/XFMN5/PodVqwYMHD2Bvbw9ms1l9rQffqY07NI+Pj+uAPMYfcLGkVzem8polniOhCZuyJKT+w77AO8wB4Np9viXLj9mWnrhGrCztmaYbJJtbKhtPWen3+9DtdmE8HtenkXh5JlQOp2+XQMcttkOv14NOpwOLxQLa7TYMh0OYTCZrE3KhsSXJLC2N1Zb21EVDrt4qBZSHJcaIlN7rn3h1B92NT+2T1er1qQj8Hc9H+r0LfUNhjXV4UMLmDrVZCf6h+YboDaUJyQTpuTce5/kuxZbX9I6lXP7tVu+M9YI6F15mR8FAj1bg+eyKAvQ619xhxHpJ98uGGC4WbLPSHqONPrcgNmBLCWfJYIit9JcUt3RElNUhpsE+qrhC30jfa3RKgUZ8TseGRMstygD7g99RE+IRyocxB4DmHwOXFbmBQppfLBhG6yEZfSkGw64Zal6Egu0hgyglIJ6yCtpqPG0bmgykR1jOZjM4OzuDx48fw9XVFXz3u98FAIA/+qM/gtFotPY9txU8d9XgWODyledPafUGf0OwTGrkIpWfUsqR/tbuNHvT4HUwrHlifha+09JwfYZBmVS+4DoP0el0YDgcwsOHD+Hi4gLa7fba0Zy5wHpothTAbsk9ekevZuftEr23sCNm6++q/wqwPqnehNzSyuUI2djWcREKIGlprcHLVqu1dlQowLrss9JId11zeSCVK9EM8HpCmP+zolQAcZdhHXdUJyK0Y6S5jqV9aaWlKcRoWC6XcHl5Cffu3YMf/OAH0G634fT0tLYDcPIK85rNZnB4eAjf+9734PLyEp48eVLvvJvP5zCbza6dZtcUaKyldL6ImL++CVTVq0Vso9EIfv7zn9fPcsrn8kU74ljyGzVZzOVnil/PYyY039h3/PdwOIRerwf379+HyWQCn3zyydpRvE0gRivafE3fYYuLJnBn5mKxgEePHsGdO3fqOM/V1RX0+3147733YLVawRdffHEtH76rc5exKzqo9K5rCutY8LQFyhcKGlfE02zwt5Ufdnkitmlw+VxqwrAUpHKt/VrSrqHtlFNfj4ySTjQJxbhjKDIZa3GychrKOkFgMX6kyTaPo8XzopOckhERozXEzCUGkTcPavhI7a4Fp7QypfJzlXJoUiIlfz6QJcdUKiPWtjxPnrc22SKlCznZGn2cDqneNE/NaS9hRMXyiQUMUibmLGVsGzxIpY0dj2yRvk/NK7W8TUCSk1oQfRuQ2jlFv9C8UspFSO1i5YEcHtGCSk3yi9TmVFfTsvE4y8PDw9rAwgkeTnuKgcjLx+CrhFif88A2fc7rHsqbf+fRZRptMYR0Js+3hI2QEmin9Fjsn6bg5TdJtnhkNm33VHsnRFvoXYqzrU0STKdTuLq6ghcvXkC73YbxeAzT6bQRnRDjCSprvHKD5rsJ3tMCpjxNKqgsPDw8rHdULBYLGI1G1/S2xv945cydO3fqYOR8Pq93YGGA0EKvZ4xR3RHLW+I1zZ6nz3YJks9B3/E0sTwskPqD+1+YzjumpLK43Ck1vjyBoZCupn/zxUYeuSnxWsi3Cvns/PjtGGJpU9rd40duQl+n9nEqJFkSa2P+raWdtHgEB93dhPYtymGaR7vdhk6nU8vvfr8Pl5eX9RG6kr/SpN6WQOMr9FnsuxB2wRflcYaYzUJ/S99Y+C2mK0PyK1RWql7xpFsul9DtduuFf/1+v57cpdfvhK5YsNJjtT942qZsBtp/9Do/pPvw8BDu378PAK/tscViAY8fP4aXL1/WeXiuX4jFDkLpNPs45mtuC7H2oHyF6blcksaW5i+kyh9vO2F6aiPwyVmJfot8SKHnJoDWvdRidqmdcuKIlnJCvBbqY61Mq20e00kpsj/0zMqTlm85dmJnbK7BFXMuKPh9Vlrgw1u2xUGwdlBKEA3/1gJp0nutPGpYe8HbkQroXQGlUXP0qfOJkI6MqKrKfA9CjlKkQSZqDMYEr6SgOY/Q+1lCNOQ6FSkKgTqbu+DUNAHLrq6Y3LoJiMmUknV5U/glNVClObCSU8fhCSbyNFajpklYbQHExcUFXF5eQqfTqe9nwRX6JYMyKMf5RK8UUKO/S/EAz9fyfJuQ2sdiS+U6oTS/myBDJN2RQzdtayovmmoL3rc5wIUVn3/+OVRVBefn5zCZTLLtfCt4X2xit3guVqvVtQlMyafijm+sLel73FmxXC7h3r17cHh4COPxGMbjMVxdXcFyuaxPDuB2K81/Pp9Dr9eDhw8f1juuLi4u1naqLBaLtQkCutOP0mWF12aRjpi1BAq1MbYtOcQnVOjzTUDjA34Xfam2ybWpm2gXiXdjASgJIZ+XQnuP44jbLSUg2T8Sdl0Xe8dpqC9CwUfJvtX0m8UWzgnQrlarWvZyW5n+xonYXq8Hg8EAPvroI5jNZvD5558Hg/Gbln2r1fq9h1afVYq77RI8O+xK0m+Ji2rfpQS0rflY0u/t7cFwOISjoyPo9Xpr6VqtlnrSUWk5RsfyJu1JHH+LxaIe41VVwf379+FrX/saALyytfr9Pjx79gx++MMf1roZxzvA7svtXQPnLemUOY4Ssp3COhmmlblYLKDb7dZyR6M/1Z7ZJYTax2JTWtrW2v6WycJS4zF3sUVM/ufGBLx2qqUeIZpCzy15F52M9QZBU77Fd4vFIrhNWHNepEAdddg1umL1kYJWloERyotDM7y1NEgLT8cHUYoTYekji1HCB51m1IaMKkuwQEujOTFa29KgldUw0tpDChRp9Qj1NdISCzhKDrmmJD3BIgtC5XFnXKqHxVnXII35UsHfVHDDmvJVTBHht9YdIAiLYyQZSiltFQoghWiTZKblW84fUjB5W2iibEuQgI93j1wMyctQf267za0GFQIDjFbZJtXdG1ADeOWs7u3twXw+r3fuWeE1MmmflQygcnpo/nSBiUSv17nL1QE5yHFMS5bflByhC3xCi+asba8dVefJJ3RU5uXlJbx8+RKePHkCp6enwXw8QQoe/LXya0xmhr7x0Izf8fGG/3IWPJYYW03y6dsKzYfk8pzzbig//Ea6242XkUs30om7PaRJf4t9aIFnjObwOpcR3kATvsf2WK1WsLe3B51OZ20Mz2YzuLi4MNEkyR4qHzw+ArcVcxdSS36n9C4Xm7Q9qd0Y84+4HcRppc88/jgvwwNqU3m+ieHi4gJ+8pOf1Olx3F9eXl5L22q1YDAY1H9fXl7CxcUFTKdTMW96ag0CF/iEIMkz2m9Un1riWh6kyIYchMqjtjmmjcWKrL5ejAZuq0ixHq0sT3txfajRw5+n9kloDGl8R59p7R5DKZ0ZQqxNrq6uoscPW2VfqGxr7OimIoUHmux/KXYm9aFGN8pp6aoFC93bjMumwmLvAVxvy5A8LEmPlac8+cfkak7M1wKev9X+t9IXo8vjw1tsrbXJ2JKDwKqoJSdQSqflIeVHjSzNMKCTr54AbkjxWugP5ZMS1NXy8xqVUtmSw0mdvFyB4R0MlsGd42BY+IEP5twxE3NGSwV/Y8GDEC20XbwBSprW4lBqQaHcdgjJndJIaSOvw1EyiMXLT3U6Q/XZhMEcMvhTZGtpWkromVK0WNNToIG9Wq3E3fSSA7xNRylUNtaD3tMm0RwzolNowCDVcDiEyWRSB59KyyRNf5eGFlisKvnUiBz5EuLNJnnNMy5T7AKLfkp1UiyQAnSxfEP9Tr/x0sPHIQ8ijkYj6HQ68PTpUzg/P1fzCNm00m9ub2h9HuIFTzCN1skCicdXq9VacDrHNrXSnuOn3MIPSedwfWTZ+YTf431z2ok+JeRo6UCFB1rQMLWMFB8tZnPh6Ru462gwGNQTU/jtaDSKTsaGaMux9Xgaj6wL2f4Wn98Ty9gWcPxgPwKEaecThqV3uFnlvSUOoH2j5YOoqgpGoxF88skn9TO6Q47T12q1oN/v1zLp4uJC1ef4fbvdhm63Wz/ju7V5+hBiO3usOiukDz2yLidGYY1dIaj/0wRivhdPx/UZQLj+Fv5NmeCI2Y0W2eShLQYPb5WE5t/Stry6uoLZbBbMRxub1rItNuUu6ocQUvRyTP/mxu9isTtpbGrlSJvkUnT6TfUZYnMy3B6S/Dfpe0u5ljbLtbelcqS6euwLjf8kO0PiR0sZGq0heNrUWr6GrR9TnBI0QEiGBAaMEfRO2JhgTwmIIVN4jevQhFdpSLtUJBo0SIPFYrTQdokFsKQ25CtPLce5WoH5aAYb5ZuQcOUGLtLN0W631+pHj1WTjGLNgdfqTo+xpZN1Gt20DAzObOIo6Rw+9yiRUDlWAds0FovFNXkVg6S0tLqGdl0jmjJkOU2SvNNkQYoTGipr09iGcxAbV5oMTy2Lfa/1LwABAABJREFUyvVddoY4L2gnYKxWK3j+/Hl9hHCr1YJut7s2WZtLAwbwJpMJtFqta0df5cITkKJBQq8MstCwyTGIAdFQX4X4NDUAHJOr25ZDOUDbpt1uQ1W9Pq40pU7UHokF6r35U/sQAGA8HsNsNquPJh6Px43IJ8k25DYlp5Gm2yRQnnkCgDQttQtjgTKpfly29Pt9GA6HO687ENT3CN33nZIvhdRum24fyxiMTSbkyj0p6Md5sIS+akpXaXLAisViAS9fvoTlclnbCPz+euRFb1Bf8qEtPIZ94AmSS3ncZJ0I8NqGk8YqbVtN3nMb3OoXpdLatPzAO7sBAHq9HiyXS3GC5vDwEPr9PnQ6HRgMBvDOO+/AbDYTT7bAWAltQ95OuIihhCxOjYullrXJMcBjQVg2nyTjfOflw1CA3hOUt/KslAZ1Qmo8BcvGo7Q7nU5dzmKxgC+//BLG47HpJKXY5IIX/Hj4Jo8rrqoKut3u2vibz+fwzW9+Ez788EO4f/8+rFYruLi4gPF4DACvTqtBnURjnSV5nfNyLK2GTY11L6qqqv0tgNf8KC04SR0jlm80ux5plH7j36F4o2Z38Djmm4wUfZXL75tCKb+o5PhMtadi5TbR3qbJ2CYnCjHfUOVCk2GhPPn3Wj6WQDZPK73zto+msC0Tc/Q5N/5jwPQew5B+E6JN6iMejKNCWOtTia5YWbHvJYTSa8ftxfhI+o3OGn2n8Y7UN5b6hOoS6hce8CghaCz9aA3qpDgGofybhlRuqE80YyTGX/SZNhasRmksz9zAlYeXrcpYylNr+9K8YOkbnnYTBpOlrhKfaU67Bml1s/Rb6pem2yG1rKurq3qSlDoV9L4cPiY8bUadK8t9Ojk8a9HxTY2J0Fj38qYFGJzQdBxNR2nk9lNOe2xK5zRpg1veS/aPFmzijnnMdg7xqfQt3g1K9ed8Pof5fF4HiTC4wW05yR5NQUyHpuQVCt556c11RKWAaopNQdHpdKDb7cJ8Pl8LPnmAk6KhIHBpWPwfrfyYPR/ioyZkpqU/PXZLEzod8+SByFgg2tJmHt839Rua3hLcoXJ1NBoBwKsJLrQZpLFIy7LC2z68XIut7W1bSU+XHseleFSSAdyXpmlDY80iB6w8FKK19PisqtfH4lsW9PX7fdjf36+PJj44OIDxeLzWXvg/j5XQdwh6j6UGa1yM9lHIXwnlkdO+JexO+luzI2g9pPrl2EJcftH/c+Fpf8kesALbpd1uQ7/fr/PDRZ6np6drR2hrcs4ah7PaUUgDzbeJyVjahnS3Hj6/e/cufPjhh7XtPZlM6slptLelby184LGH8P224ntNgsdV6ERsLK6Y2zbWcctltjW2F6vDJuJjqYi1fSxt6HkorfUbT0w1hy7tW6lvU/0GrqskGeKlOUUGafRbfT4vtr4zVkNIAeOqOe0IPNxx5lXMmjERYjLONBb6Ka08v5jxnuLo0Ly8wZUYvaEyYmmwf6hRD3BdIXkGdeqg4EIlRzHgTlNKy3w+h6qqoNPp1P8jr3K6PX1j5XFLGu24BK1MNBZSUCIA5CmH84d25/QmgUY1GrA4FqQ2pQEZyVn1jLsQ+D22kqPqAR/j/B1A+eAplpUa6I2hRDvvAnjQSzu2SwqEhvpzF2B1iDENlf/T6bTeGYsyut1uX9slSL+3gO6EX61WxVb2czpSDNVdkIdWcJmuGdExXW7R81Kb8HbedJuVLDcnwMvzKSHzpDwkW+r09BT++q//ukggU7MRuD3fpEzXAvhScJXvbkO5sgk6mwann+om/Lvb7cI777wD8/kcLi8vYblc1pNXALD295sYvKuqCnq93trpDXicreQP4G+83w3ToL1fql2oPKUBY2pHWoKsqWXTfLBufIFpTpCS04rvaF1Tg0SHh4dweHhY8/KTJ0/WAlH0nxbboO1vOXZeg3avbwg3wW4ogVarBXfv3oXFYgHn5+fQarVqn14D7S9vPIp+4w3mWgOwuX2HxwbjaRS4mLHdbsNsNoPpdFr7rd/85jdhf3//WvxDmlziCxBwIgwncUv5vN7Atweb1MdSn1oWroTysX4TS8/tk5LyQquj5UQjLlspDg8PodvtwuPHj2E0GsFyuVw7ktwLya8O5UPtOvwWfVCMJ1quJ/AA7QosH+sN8Gqx8osXL+Di4qK+Xmc2m8EXX3wBs9mspo3SzWHxXTT724ISfFWaP1PKx9MXNiE76HyKpTwtDY8Hx/Tdm2gzeHzzUgsqvPM3oe+4bSu9499JR5Hzvk/hY8m+TbWvbwLUO2O1gIv2rgQ4I8eYRzN0LN9p6UKBaE6jxCy8PK1OHkUTooFD68PcgcHLl9pQG6wxXgq1u2a8lXQmaH5SIEijOwQpHecdVF4lHApepsRfMd6O9Zs0jmK0WXiVt31MgYeM2lg59Hur0VVi7Fjqxo/Ak+jlecX4sSnDMsbfPE2IT2I8FONfqTxaroSmlXRMppWAll9oHGtt5oWlbzcZiPCC0x3TpfjbGgQKlYv/a7Jf0hMhuuizkE0Ts59SZEWpPvbKYfq7ql5PcONxxQCyHIjZETG7omkbWILWLzk0cJ2WK5O8NkBMX0n9RNseV+bTXQyImDyKBQl4efT7UHrtmbdtU51Ojx9Bx4O3LI/cSOGrWDvT0wu63a54tJvFnihFk5TW2qbWfuNjB48D7HQ613bNhPLBf1VV1aczSOly6I7JWk2uSvWsqupaADy2gEkb31ZfxeprecZNLC3uFqfHvHIfLhR74LTlIjSGvHX3+Fy7ZjNymYnjhvJ4SFdJ0NqjVExD67tU20WzPbgOwWe4WJguCEAZ1el0oNPpwHQ6vbbLUPqb1oeOe5R/mqyxLHKUyizRNvRdKi+n2Az8W49NbaUzJHs030MbI9rvHF9Ayx9APqKdpkferarXi+JXqxWMx2MYjUZBfisFKYYB8GrBA8aHUuxDS5kA64tPcUdwu92uJ2lnsxmMRiMYj8cwn89hOp1em6hGuiX5IL2XftNnFr8hFZJN0lTfWsBlnTROrOMPEZJDtH1jbWtpF6lfc/PcJmL0W+JHIV0ca/tc3rfYuxr4uNVkvyT3Y/0aai+N1tL1t5adwqMptK55Y5u4O1JjLo2pQ8wqbemnadFpDdGg0WhlYm+AJgZqDMQcotgzz7clEAqwhcrWDGm+2tgSQLIYfiFo/CYZBjwtvwOUv6eOinQUtTUIQGnCfzE+1wxQ7kR5od1HLCGkuEKGWgypwbVdMARWq1c75JAvYkYhb2ucfAi1fejuWD42aduXvLMMy+fKvUlsymGSsEsBJQR38KRVtRj8lHhqtVrfNYp9Op/P11Yhlw4u5UA6bUED8j4epybRjOMUj0iNgQdRsRwLf6BcR3vAgpgekZyAXeFV7sDT/0M00gk5DJ7MZrMk+WXRw6V52etwSTSW7sOmAiDIbygvMLAd0wmhAAQeV0hPJKETOPRbTNMU+F139H/LwrsSkOzOUDpv8IS2KT/ZRSt7l+SMFzxgRXdbagFFT5tK95hTe4li022IdKAslXiYn5ZRyrbGsu7duwe9Xg/29/dhMpnA559/DgBQ765LbRPNJ8qFJbCb4pNKJxdxGyvW7lpgsKSdH4PXv9s2kMc6nY5qF+LEBZX1ALb4kDUY7gHlBXqikrUcS4wM06D+xQkjaZHHfD6HyWQCn376KXQ6nfp4U7zPnIKebAQA9c5aiYZOp7M2WQQA9a7lUBCclrNLKD0upBirpfzUOLDF90BoE06h761+E0+HiwDQb5OOuO71enB4eAjT6RQuLi7g9PS0Hjuld6B68cEHH8DR0RF8/PHHcHl52Xh59NSmhw8fwne+8521cb1cLuGXv/xlvRs+1j7WfrOkual2pAUSX5aoszR2NF1WQh/ROYwmsU1bwipbS/GsFKMvNa5S5kykndSSTxQrS3vm1V0lsE27VNwZazHEAOzOjDaBZQ0cehS89kyiMZQ/n5jQaJCEQaxdYsEnK0OkTGrEmJ6mCfVP7oSKVqaWL7azFqAoMYi0vqaOLw1OaBMWEv30PV/hSYV1LPiijR9p4kNq01DdYn2iITdwq/FZSl5SQEyr1zagyR76HNsjNPYkJzY3qCLRkDOxFtIFnCdLBiRiBqa3DIvMzJWHtCyrTmkKUmA4JCd2DR67gbY37UPJ6OX9ktJHPNhBdUrsO0lWSPnib0kOWnVlSA9Lz3Og2Z2hMrjOwRXcJY7+sdiPKe3AnQuL7Wexk3Oh1SHHtrMEoXOdfZ4ff8fTSHat1I8x+lNlc0nbiNchZPNZyuD5aGlidGmgfDydTmE8HsN4PK53R3nHEd4NLB31rvlalAarDEzxrVL6IJU3cDdlqF6YN+4mxklM3IHpLZPm6bEFPeNmtVrV8hx3AHe73ToA5O0XSc+XgNYO3j5HW4BeUyKN71QaLfYFpYW/C9kclvJvMij97XYbDg4OYDabwcuXL6GqqrVFQJa6praHV0am8kzud7QtuM2D8no6ndY763ACF49elcrvdDpw7969+h3uVkRb3bJIUsrb0xdcdmh2ayzfmAzahu+X43+H6NX0hccG9NAmyWHkES67JF8JeRYnbNGvCPlBMfuMfyPRSZ/jaReo99rtNgyHw1p/Yzpp4Z8HGt+iP4UL7ebz+drVDxcXF/WCV2yfJhDybzXk6spdgCQ3OTz63MobmnyzlOGFJ47i8cV2CZZ+y5XtmvzJjQNbxpnUT1yWSvRKf+cgVQZqNrtFRzWho8Vziugq2BCsjk3IcdMM/Vh+q9X1lX6lLzXntHEBSWkI5UFppH/jO2nnWqxtLW0Wut9vW7AaVN6BGuMxT/khLJdL6Ha70O12YTabiSuZpPKx3NlsBlX1+s4n3IVGj6biQqAp/ubgq3ktAdXcIB1CmpTehKK1lMON9lLOkVQ23k2Jhq72HQal6E4fpE+7c9aD1KCbJ3/KZ6XatHSwbVPwyP6UvEMGG9dDIfpyddKmEBoLHOhMhu4YwgAS7o4IpeM0SKDB8NQdPtYga1VVazvYYnZLjlGL+Vt5wJNO46/ZbAaz2QyGw+G1I2tjtMZA24vebY+yNwVeHUfT7Zps0wImTejv3Px4oCEkE9H/2VZ7S7Ry/uenkuyC3A0BZRHeO4YBeXwn1UGSofP5HM7OzmA+n8PV1dXacZcAcjAn1a7ctfFGUVUVHB8fw3K5hGfPnq3Zg1SXYxu/8847MBwO4e7du3B5eQk/+clPoKqq+m700nW1BBUl4ITk0dERDAaDpP6itiUGrmlMo2RdS7TdYrGA6XQKi8VibZI8VQZxmnL8aer3WHylTcuhTZbX7XbhV3/1V+Hi4gI+//xzqKoK+v3+mj2noWQcQoI03jYdyMZ2oJM5GqbTKXz55Zf1ZOxqtaoXl3AcHx/DP/7H/7i+n/ZHP/oR/MVf/MW1ss/Pz6+VW8qfigWaKR0WP0nKf5dgaTfPiUNN6JgQcGJTO44f4DX9SJ9l16nFz+VprPXudDpw586d+vfx8TEcHh7WCw0wTzoxW7JdMa40mUzqPJ89ewbPnz+/lhbjlxIstGnyadfGwabR9DhBewjBYxbaN1bs0nzDLsLL37H5EOr/hWwM6Zh2nq/Ud3whi+WOWPp8l+KCGjTapPawzI+k4JqWsiiPnMCQpKSkWWZrkIo7DbnQJiE0htIGggWcfsr0nokfi9Ht6attBiCk9sXnlmAa/8bDq1Ia6Xs8kghgPZDOj2zT6NR4KZQ2Rh++9/S1xUjytBt12EN1DhlhUn2ltCkCvpTwDE2weJE7LnlbevKzjiFLvp7+CPW9xAPePqaB11TZnIoc2ckNl5DRZNHPVppS2wbbFR0ya+DQS18OYnWTxg492hjvJ+TBJc2o1WQYTbMN/ZpatjSurbaZhR4PrMEPT/AYv7UEmfH/mE72YJccFG9drG3Gv8Edb1R2SEcexSAdsxWzFTz6xEqPJKt58NFrm6VC0n1IE4B8fGZJuyXk19Fv6T8tj1xaSkM7VtOKEnRim3W7Xej3+65jt9HH1I6ns5avPeOyN2QPWGhut9v1wlcAgPPz8/qeaCyP56VdbSPxnNUOsYxbid+5Dy/5RLFnmj8U8rVyERvbmi2xDV0WoykVOPmxWq3g+PgYBoMBtFotGAwGawvyUv0dK3Jtc5pHalxACj5axzWO39VqVR9NDAD1nbF0gRt+T3VVVVVweHgIh4eH0O12YTKZwFdffQUvX76saZKOX9bsAq9/HHrHx3VOX4XKlMa4x+4JfWORSTS9Rh8tz9LWHj9Rk5+Wb2l5qJPopKZUFv4dG+Ml+luyG/nYbbVacH5+DqPR6NqCj1B/WWmgZSOoXYHtxpFjRyBCsUCergl9s2v+V8y3bDKWVcKWaDoGukuQ5FLomhpLXgBxfWAd8yXibDF/mqYLybVYnloZOdDGT4g2+l3MN02lUau/vmQIbINTUsQxQ81jwIby5YKr6Z2DXFECvA5AW4K+nF6JKdBZBnilEGNBKmvZmHaXEaNTMlykI8pKCSEtsICBeQyGYBnj8bhOS52EUuCBBE2Q0EAgX/nnUbihwAVNIyFXsYcCJiX4uASPlAStV0odaXt72j7kkHFju+k2KhFg0oJXbzuaNOBxNzfu7NglhOSWJj9ns9naEVEHBwcwHo/h6upqLZ1mlOJ7+jskq0tC0gtUD3B7wmsbWOnNrVuq3PE6q5LhvSl5EQtCbEsnSXauly4cHzSf1WpVywp69BsN9lgCeJgn3fHAy6Lp6f8l+5a3D6UPg1l0vDUx3imsga1UUHuY58vbvmlfbBtYLBYwn8/r3ZqWU3FiCMkdfMd3MSyXSxgMBnBwcLCRdsaxzGmlJy+tVqv6/mf8hp72EwvUSOO+0+lAv9+Hg4MDWCwW8Nlnn8F8Pl+7H5zmAfA6GIZ2CF0oRn04Lpck2ihSxhC2x3w+vzY2vTa+5PtRPy8VXlubywD6DOm86cAdXzje33//fXj33Xfrd7g4oDS8fRGS95puos9iecfS8zTSyTr7+/tweHgIFxcXcH5+XvsIUj2Rl9E2mE6n0G634b333quPKH7+/Dn8yZ/8yZpslBaDbsPnu0m8H4urSP3vCapb5IHH5tYC41b/DidhcQFAt9uNlol2KvKrh16t/qFxFMrz8ePH9e5UXq8S4BuBer1e/Q6PE982btL48sKix1NsvZCc1d5zeHz+tw18wWHKna4atFiOxCsW2RzbAS3laamPN+ayLfC5EAmxcdhkPdcmYzVlE3NQeAA9dyImhRbpnUfBSw4GTaPlJQWuNEhlUKHIV63TABMdSNqASEWOI2UxJKz5S85FyNiK5WNpJ0saFEp0VZ1EL+6SxXch/prP57UjgYFFzF8KeKUKiFRjjZfndc4sfS3lZQmQ8HGj0WXhkVC5TUAqB+tDnVkpgMV5SFMcsTGzWq2urfqM1dsqS0sFs0IBBI03advFxos36EHzT81Hy4/2dQofWnQUT6uVEaJDanctICSVHSp3E+B1ismHxWKxtjiKvpN+a3UN8U1IRwDANZkQo1uji+owy1hP6SMaHJZ0WCo0enL5yNOe1BaLfRPTQbSd8HdKMEorM/ad1Vblv722t2YDoY1DnT2r3ub0pfKppEt5vl7+ldJpMkdztPF/j09hTVNK7sZkfih9q9Wq7xvDSSoqXyWeoc9brRbM53P46quv6qPjp9NpY050VVXq3bSx/FPa3duuuDBU4lXcgY4+y8OHD+Hu3bvw6NEjePbsGfzkJz+pJ5dw4slKT2p7emyVXHC7KuQ3pNhbmmylOydDfhyOi1arBScnJ3B5eVn3g3X8a5DkjuTv4N90/NDxWLI/pPws/rglr1z/JfZtVb2aoMDd2nQRNtpUXrssl05N5kj6Tet7D30W/57ye7vdru+bxCPpuQxF+5rqPKk9B4MBfPvb3167xxP7w2p3WxCS414bxZp/7F3I9m1Ct3OaND6SvtHGd468tUCiC/kN7xFGnkCeifnF3vgKhzU2RZ93Oh04OjqCvb09uHPnDgwGA+j1evDZZ58BAKydKpMLSgPdQIJ54725ACBeZWCRIx7fiubZNL/sElJ0nvUbyW/zxEFy4lKh/G4KeL3b7TYMBgOYTqdrR3lbYgMW/SG1N/eBpP4MtWvM5rbGw6Q0pfzUVD8+9K2FLqs8K8m3Gr1rk7H87tIUw9BqJEgBB0khcuUQyzOFVktamp7SHhtgvD1QOGr5onGPabV7X6Ugl0a3t445Boi1HFoe/y05DqkCLsVolYRXyEjAd3SnBr37h/c9vscjz9BABAhPnkplh3YvpghuKySF7jW8pGeaoSAZ+blyQKIllq9HYHvowH/UueQKmf6Pz0P3/GrjAf95ecHqLOYox5z0/LuYHMsxLEvls22EgnAYGNHuQV2tVnVgRNKHnFe3DUlncvnF60bTYtAolLfHOKVjUTrehtLF7+bx6jFc+GPdzRWSg1p6WiZvy9yAmUaP1+bTypdsT+xLyhshHvCA859kF5ZAE86vpuckW4PrGm7b8oUO3PHk70oFBTh9vF5e/tfyt9qn/LckSzmN/J2GEF81wXOxcvAEGYBXtNOdZjwQz/mBTsbSfC12WWqfWu4/RFqkckPw0CTlR2mj8gp9ik6nU+9qeffdd+HRo0fw0UcfwXA4rMcfBlo9dUjhG65/8Rnn+Vx+lMpJkRspOgf5Gds9Vh4ukHr58mVtz2MeObJO86O0dJiG+yD0W0vf5NrFGo+kIteHoOj1enXf0slYzxH7pexgzbehMqBUvtJ7zS/Hd6jfF4tFfa83j23QuAelH++cpc9xMvb09BQWiwV0u10YDAZq3IHzUS4vWfg+Jhdy8tfgqY9ma8byCNEmxW7oO8mujuVtaYvQYnKsn6S3caEMfsfjq7xdpN1kEj/xcSfJMQRPQ+nBydh33nkHPvzwwzrdYDBYo5+X4QX/Vrq+Yrlcwmw2g06nA71e71osQAJ+773rHG07Tp8nluSxv1LGWylbOcfe4+8sefF8rHEZSd9Lcq6p2M42+0hDp9OBg4MDuLi4gNFoVD/XFkh4YmCafcVjVNq3Ut157II/52VoZUvfUHpjfjtPo5VhgYX3Nd879E2O/k4BL0/dGetBjiHhEZg8oEPzyYVFkJfqEE05A7xWYji4paAE/sa/6SS6tT0tg29b2FSAyAJKC11FjqvyF4sF3L17FzqdDlxcXKwFRrrdrsmAofA6UvxI5BCf0OcxeAxnjUdDeccM/G0ix5nNARr/dLUwdwaw7aSApbcsGnixBp20smJ9H3JO3nZIwS4u33PgMdwthhI6adQBxaDiTYFUL5ShWDdMxydCAa4737GyPDotd8LP0t9ID68HX5CnfSc9R9oxH699J+VnAaadzWb1ZI1HPlL96TlmW5NptP2tjgMi507KXHCHysqvWkAM/6e8hvbQwcEB9Pt9GA6HsFgs4NmzZ+4jzj3yrKTO8dqnJYKeKeA82Wq16tNb+Ek71rJTbMk3EWjfY9CXIoU/tKC21sa448d7ZOrFxQX84R/+IZydncFgMFjzY7RyKI3cV6U0428uP1L5ej6fw2w2g9FodO24b/y/aR60BNNQttH7bWezWT0BFQqyeejHPLCvUE/k6AtuB1BaY7KTtkup3Vq7AtoO/X4f9vf3od/vw2w2gz//8z+Hi4uL+rhy+g2Cy8ld8HctcoWnjfWplXZ+RGCsTVqtFvzu7/4u3Lt3Dw4ODuCdd94RFyTOZrO1ezSbbstdjptZYbVLS5ZXCrFxFIuBYuyu2+2KctMzeSLRw31CaYKR/o32Q7fbha997WvXZMrf/u3fws9//vN68dmmgHq/1+vVun0TY8uiQ7alZ6S4/aZo2UadS5Z5E9sJT8CgC+MWiwWcnJxcu7t5W+AxQ+m9dLIb99Wl+S8eD+BllBirpWSKhRaPrbINOyI6GZvipHu+tzhtXMlajUrNmbBMHknMGaIxVI5UJmdqnlYy5qX6SIZDKEicOtGktQen0QutzXh+Wp2twiFncGE5fBUdGna9Xg96vR5cXV2tTXIBQH2sDofGwzQwE2tz+r3U/qnOYYiuEB1WhIL5MVqkMcudCz7GLJMSGo0hmWhxaHkaSz3phCwNeEl8ERr32li3Btu5EvbKFd5OUrC+CaVnkefbRIj/Ke9aZEGorl6+Dj3n4AuApIlYa16l5EqsDEnnSjTSumiBTtpP1qCqxP+xfEN5SM+lYGoItDzLBKq1bPouZK+l9LnWLngahbS7Lmbbor7GoL9WDzouY3wbs7VC+nzTsMok+tyTJ/12uVzW90Hu7++Lu7U94ykWgOO0bAKSw8v/1uS61ZYPQbIF6U7k2Wzmtgm5naWVRdNLNGl8JNkJuwBsO85ndMGchXbPGIuB34Eq5YvvsN9brRZMp1P4y7/8S5jNZnB0dATz+Rwmk4lKVwiS/OM05Yw7PCaRy/QUez4XIbsX/2HgDulF/5BeXxPLL5SGy1BaNk8f031aXbw0pabxYNOym5fX7Xah3+9Dp9OByWQCv/jFL2A6ncL+/r4qy6ztrvnsqW2o2aVSORb/tRRo/MTiW7daLfjoo4/g/fffh1arBYeHhyI9lvssvTGBGHZNP6VCkxf8HaKUrZ7ybSwWyH/jSUD8vXSvsOYDesevNR7EbQWU5cfHx9cWVz19+hT+5m/+RqyjVRZb7DpeF6RJWryn1U2jyTPmLG0YioFtEqXHQ0iXSP5oKlLt7BydZOGBTev5GDqdDnQ6HRgMBrXuGo1GcHV1dS2t5hvlwDoWePlaGoB0ubZahRfcpfiDFnh4IuRfefKQZAzPy1tHi73V4Q88gVFu5DTFkNSQo88tStWSv4cOb76S4uD5aLvgOGLMpg0aqUytLkhLaluWEqiWfDYpvDVjENFqteDOnTvQbrdhb2+vfn5ycgIvXry4dl8K5oH3A9G7IKqqujYJR/9PoZ0ebaI5+QiJd/kzvPeN0pzKNyXgkVvSt7tiCIQcaAqpb2Lf8LQUsdVT0rcx+iRaY3Q0AW13QmqgG3HTgk7ccMP6SydNpNBCV5RZgqabRGrbLhYLGI1Ga/I4hKbq5Rl7EqQjYUuj6bp7+ZIunpLGvxf9fr/eTbZYLODi4kJdhGBxDKz0aAHWUqD6htofqdCO3A6Vj/ZRbFUq1j9X33Oe0PST5pjlwEoz2vL4dxO0NA1uN6KtizyGtiRPr+VDYRnT1kCQ9B1OBr7zzjtw9+5d+MY3vgGDwQD+5//8n3B+fl4fHbhtYNCUT9BOJhOYTqfw27/92/D+++/D9773PRiPx9DpdEy7yXLGmIZUWe7NH0HHSwkdIB2rvlq92qF3eHgId+/erY9llY5m1YC00atuEFa+jfF5apvzMbxNP28XgAu3xuNx/azJ9rAG4il/U/laIpDvPd0LQRdaxXgPj0PFRQwArybCv/Od78BwOKztLoDXOryp+J30Pf1/k/Do/BQbMzZBYokp8HjjJk9JQvqp3Xl8fAzf/va361OcEJ988gm8fPmy/o3ppWN6U2iw2C/0FB6pnfC0isvLS3WhQcqkSgw0rqfdxdwUOP+E8KbrnhJ2SgyhNpTiBJa4MX0W0lEeWjaJTqezthjCEi+R5qa8iH0vtU9qmdJ8lOT78msCpPiwlHcOvH5aynxcaj5SG1kmZD00Xrf84brxrSEU3C49KSuVSxtEMgiseXlolBpaqmvMAJYcXYvR7G1X7vxKNEq0WBRiEwrDMjG4KVjriHdT4H0yw+GwVqjj8bi+b4EeYYz/awpO46ncwImVf7SyYm1SatxpvOjhyxL8wvPyGMDSmNPylfK2GDTS+JZkSiiAYulPC3+VGqMxZRYKoIcUpHVsSHRoeZSWSSUCBhKNWr4xw99SP5q/pX2sAfdNOoMSkJ+53C6Rr/S3hpSguGanaJMTUv9JcqZ0n3hloyTfrDxK85byigHvYFytVvXKe8zHapxbIdnWTYJPWITaVdM/0nvtO6l8axCG92XID5FosDqSIVs0FyEbUEprkZmWQIc1Lw0entDA5ZAWuNV006Z8AeTJfr8PR0dH8O6778Le3l498dk0DRqfVlVV+xP4j4/ZqqrqXf537tyB9957r14syneo0fIwD49/GRvXqXIRJ5f5vbgWmigkPsrxEbj+QV7AnbF4lU1MLknyhC+alWReu91e68ddQYp/Yfl+28Dd2avVau0KCx6wDPEUjgOrbvToGK89qSFHL2jf8fiN1kZVVdU2FdWN7XYbjo6OoN1uw8nJCZyfn1+jy6MPUupSwn6zlJHad6l1stp4qbDaWSXLrKoKer0e7O3twf379+vTcmazGczn89pmT40vcZT6loKeqqfFIDxxME/53A+gz0JlxvRaLrYdC+DQ4m2h9Lnl5SKkn3J9M09+uembiDNivtId5lTfS/ee59iSNB8pj1LtLvmJnticpY4pOkzqy1w96JFbljJz/WVrzEGcjKUF7YIQXK1erZzH3R08YLSpVVjcuEwVIJa2xTqFLoYGeKW4rYEBS8AMkbJDdpu8kmPMWkB3UnQ6HWi32/VxVF999RV0u1147733oKoqODs7qw3C4+NjODw8hE8//RSePXsm3j+I/G2BtsMVf2v8QtuHjh3O0xwxwe0dezHadgkaXfx5jP4U44E771IZmuKO0UIDSKF0CHqPdSwtD+alOKU54HxuzXNT/LctPi8d6KJti8HSTqezk+OYg8tBLsP4bjQOLdgr6UtLe9BJCVq2VJYlnxAGgwGsVqt6ty+l3+rkxwKLqdBkXEyW0H7E3UgYHA+Vhd/iXfBVVZnugkEb4PDwEObzOZydnQXps2KXgtGcH0rraMpzOflqQWxq23B9kItYQEzb8VCiXDr2Qg6eFmjo9XqwWq1gMplkOcGlHOhU7JKeSQlWIWg9tPvlKNAHvrq6gvF4DGdnZ7UdjnTgsaq4QzOFlhRo7ZAqOy4vL2E8Hq/VTbvnr2mklEHHaavVgn6/X9tLeBIA5ot+G5W3+DdfyPvBBx/A/v4+XFxcwGQygZOTE1itVuKu2hCs8QAuQ60TAlYatLw27RdWVbV2Pxziq6++qu2K6XRqvi+uVBtRaPmFbB2rjkVbSLKJU8DjE1p7dLtd8f7dwWAAe3t7sFgs4PPPP4d/+2//Lbx8+XIn7KRdiFlY/PcSEwUhbDvmR//Go8O///3vX+OnX/7yl/D555/X+hDf8esapLwRPOYTAvfjLFgsFrC/vw8PHz68Np5R76P+iCFlUoT+nTsh8qajKb7ftkyhsNq1u0SzF1TP8bru7+/XMa3pdArPnz83x+lL0WSFpQ8sE4I8zSYW/DXJP5Y6e7/HPGIxOm+9gta7FgTzTN5YjAGLU6DlJwVapMkSiU4PjTG6ed4SjdI3Ocor1la5+d6UidhtlE8NFhTQPEjW6XTq1Z7D4RD29/fXgsU0uGAJOPPfMWO8CT6IyQKrwWoNBGh0eOCVQfhNaHxx4zWUF6dB+jalbryPYw6iVBbvh1hdtCCshR9TkKNMc8eA5fvc/qPfhiaeSjnVniBCij6Mtdk2nbXQBEau0RbKR8uL9gXVCVpfe2SUVFZsYipkQ4WA9bZ8E+NjXn8toKfJX35/o0U2Yx5Uf0v1wCA6OmjSnVRSPWgZofQxNBHclcqQyvI6MKl6QdKTJWSe9H8oLaXHQkdMhsTsOymvHJtW67+UPC18R+sX4h06pq39GhuXPC9N1vJyY7YSvkcbHo8xs/K2B1a5yfVFKHCDaXF30Hg8hvF4rI4HK2/SbyxI5Tl+jHUsMCTpjRCvefjPAiyX30so+UgxPpZsdNx1i34l73+LHg7pRIvv2YT+8dhMqfnH8pLsDqQL71XGcYS0Wa+uCEHzC2O0UrRaLej1emv3QuMkE+9bqz+zabTb7XpRJ9JIF5ZPp1P46quvYDQarV0/YdHlpewIiibaySOHLbCMZ85/2/TPOGL+iOY7DYdD6Pf7axOaeN0M2u2SrmiCT0J1QgwGg3ryuKqq+t7xs7Oz+ih0j69ngWarxsrhdpZkp6e0Z0kdELPrLLR4fN9YXtuC1Ff8OU9r0fWx2FQoziDxhGSP5MYRQpD4lG9iwmd0Jz1fQOf1Yzy0WdJpfg5AGb6M+Tkl9IU1PuNFjHetMi6WJoVerezoUspNCpOQwx16FruoHX9L36fO/PNAISK2QhG/pf9Tpz+Ut1VJUMOkiYBOihP/JoK3Ta/Xg16vBwAAw+EQDg8PYTwew2g0gkePHsHDhw/hpz/9KZydncFisYBWq1Ufe2gJWEpOmGRMNgGuqKghjLRp9ynlyJCmhDXPk+ZrkQmpwdKYoYh0SI6+VS7SXfUx4J3FuKpeMsboHSf0Ob6jMo86yttASNlagrlcZqaMyxBijgrdHWmBlwZvYM1iPNP8qDzYhHOr0WEJRHjztMBzokRq8C0lj/l8DvP5HPr9fqN6oum+pkEji8NRip5WqwWj0QhGoxHcv38f+v1+VMZJujl3TOxawAxAXjQg2QMhULnL7XJpYqIUrRaaLG1uyZfXqUTQyJq2aWh8TvsV+SJlZwe/j1wqW/sW9ZL17mgelOz3+3B4eAjHx8ewv79f2zt4KtG2QPUtBe6kxHcXFxfw/PlzWCwWcHV1VR9/OJ/P1b5oSs7EfAKUG+jTp9JAeU0KEnrzkuxKnhdO1uF9sVYboNVqmSbVHzx4APfv34erqys4OzuDZ8+e1YuDOL1UT1oQ00sldemmYelzbCscE9ruV5zMwbEXC4qWpFFDr9eDhw8f1jviv/zyS3jy5En9PhSTQmgnajUFaUxNJhO4urqC1Wq1dncfRafTgcFgUE+u3UKGty9z/CJrjKZkGTgO0S7neqzT6cDe3l79ezgcAsCrsYLysrSfxmmW8pEmfD766CPY29tbsw+//PJL+L//9//CYrEofn+rRhs9eRLT0XaVYgC8PiGZuGvwzDV48ipR/9K2vTdGlTOGc2nO9YssoPYWbpaivB+KH1li9hZ4556s8Y6UOS1uf1psBkxDbexdgWT7enhnG/XxnWuTAUsQODbJhwawFDCXlDUPsJdoYI/TQgd2LAgvCXLrpKcnwKcp4tg7DaWUTizfXRns0qQdBnrOz89hMBjA/v4+LJfLehU6TtDyiRbar3wSTgreSnxgcQS9yo2PFesEomUcNgFOb2isecaI1M4x2cW/s3wbo5W/kwKKsXpJfZoypkL1T5lokOpmzSPET7F+svKixFsl+dgTKNK+TZX7mIeUv5c3aAAwlcZSaNoRTNGTXj2nTTyk0EZ1Cg+ChXSFFDCT0jfZtxpvanolxG+WsYuBD76oAPmb6+FQ2SEbLwZJHob6rRSwHPznvRtT4mfJjp3P5zCdTmE8Hot3Y8X6kZelBYpKBlw4DaF+kN7H+q2EPOf8Sp+j/5TCOyk8l9rGtP9Ky/IU/U3Hg6bfYt/H2k7yBaT3Kc+x773HZ3vtfit/xNpQ+x3jB0u7WceYte4WGhA4eU91iwfD4RDu3LlTL6zC8Uxpsvg6Hv4KIdWGTs1/EzYkb0MAqCf9cJGDt+9LyDBJv1F7pKquT6bQ8WJtu5Dt4skjNla1d6vVCu7duwdHR0ewWq3g4uICvvzyS3j69Ok12bVJn2LbCNlVXp0U0zVSegsPN9UfnvHTarVgf38fhsOhyoPcjqfPYzSE/DIrnbz86XRaLzbDe21PTk7WduFbYYlvSHYp9/N4ftZyU77NRa6dWZqGEr5F7O+mYxwhWMaChqbtAM+4pGOFXn+JizxwTI7HY5hOp+7YlkX3etJo30h5cP0Q66sYP6XQYMk7J0ZiydOjG71levosVjbABidjKVKNc3Qo+d100m4UroilY32aAD82KPWOG68BgmVb62dV2jRtCm1vMtrtNrTbbZhOp/W9Jnt7e/Ctb30LptMpvHjxAg4PD+Ho6AgAXvWPtuKk1WrVxyBivhp433FhS/ur6TPfkQbO9xR0V4MXtK74TzKkcyG1IV+RKCk7z1goGUSn8izFAME8eFpt98hNdnw1wyHlW/w7RwamBoNjuye21UfWwGYJ+mjbx/ohZXx66KB/x3YWWMdoqmwL2T+4CIjKTi5LtXJ5G1r5TJKnsQCdB1Lf5uSN+eGO16urq2ienU5HDKw3NRZRNuNOqpRjEqV2w2dcN3h2eWv0Yv40n1arVR/7iEc/pTj0lqBiKdkj6Xsuj7c1ecDLkk4oQVotu1L5t7ws+vwm2wWbhEUmxMbatgJzkpzF+iBfNelXc37mcq8kD4bGsDfgStupql4dP5kzGXvnzh149OgRzGYzWCwW1/w/79hORWiSY1vItclDeP78ef032gjbBupmvPZIAgaVPZB08abjPd/97nfhG9/4Rh1T+fnPfw4vXrwQ+ftW/4R5f9Pto51AuCl0Oh14+PAhdDodODs7u/beYwfF6KYLYTx1xOO4ES9evKiP6QYA+MUvfnFtZ74lf4t9wXVGyG/1Trbl2ADbjF/sErQ22HbbaDySOga2jZCO6/f7axuoTk5OYD6fJ90D7Zn8TB0/mt9g7Rtr3/F4uHZ/7iZ9k9JIGWel5vPWJmM1gagFsUPB7RLGQWjASAFZLTgiKSF8jv9osFIqn35vrQvNPxY4tARrtfYOKbJQH9F20CZgYk5XU4NNojsnAJTybcpE9Wq1qu9roefN4/+PHj2C/f19+PLLL+sVzlX1ejUO3fqPyJlQDfFfqYlNyjux4EVqWbHvS/BhycAtz5P+loxcST5J3yN4YF6DVidprHM5yfPnxoi0eywVmwruemRnCLStmgxSh/LT+tQzFrx0xvIuwQPWNLH0HnuFp7PIm1jg3BqAp7LYg9B3KcHJUJ1j7cH1iKYT6HtrfWlammeojiH9zPNdLpe1syXpbJq2ql7v6uTHQ2rlcttKq3vMRgNYtwVSdJ7VVvfkTdsGIO4v4N9SX6TwiucbzjNSufz0EonWHFgCLiV1qiddat1K8KLGA3QxH7V1Qu0Ys7MswO/wzsjxeLxmD8UWEZS0BWi7WPQXBkparVZ91+JgMIDZbFYvzsGFEFa6c+VEiN5YfqnlSItErbJXg/b9ZDKBs7MzmEwm4m5/LS8eJwiNR4tPWsKvk/SXxg9N2exWfrem0fKX8sJdzDQNBiNjtkcqPRbatLxx8ZTluGHOH9Z+DPFADtD+wpgH8hvuQsYyO53OtXtlbyK8fluK/rLY/5Y2tJSn2SpeG9KahvN6Vb06OvRXf/VX4cGDB/Wdq6enp3B+fg6np6drCyoo3Vr5oT6RdGZMVnGZjleUSQsmPHKPfxMCzS/GU7ljzON/psQtvGgybwkl5FOu/Z8Cqz1r0RXat9ZvPLRZwH25qqquHVu+XC7rhcIA6za+ZldZ7ICYT5yrV+n31utYYrTS5/SflEarh5ZfDlL9uVDaFN7yjs8Yra6dsSGGSnH8SzgLnoAfF/o4+LRgX6hcKX8pjeX+xliZHiGVOohDAULeZyUVhKW9c5y+FKVAy+bfxL5fLpcwnU4B4PWZ6jSfr3/96zCZTOD58+cwm81gNpvVO2HRuOR3OWmC1VMHmheviwRrABRpw5V9WlDBIyO89fDAEmiKjcMcPtQcF95msbZfrVa1QxKCxrO8P6gcxIAjX6ASkgO5/WtBbv7SOArJ7xzj0NKXoTZLlbGSY5Oal1evSOmlcZbibJYEb3fNYKYGphSQjOl/qZzcMUPTSwvIpHp6ytAWd1h5KNS3ofbQbA/+nVcGh9LTsvEIT+0kEyq38T5AyyIp2v4p+oO3T8puWE85MYclFGzCd3RFrhU5ulVCTH+GxgWtR2l7V6Ij9D4Vqe2Zo8e1oJomV7WxQducBlzRX7PWg+ZHy7PodRqEwcnYy8vLNRosd0anIkdH0l3z7XYbPv/887Ux2ev1oNVqre2+4fbKJnSz1xdL8W1pvflEVYmgENLebrdhPB7DaDRae2blNUlmWvmVp60q+RQVzS6xQhrX1nxi/mQIJcaRN720KLrT6dSLGADy+MdjT8dsPhznVFbS7/nfEi2SHSbR2IRcWC6X9SIRSgM9Vr2qqvoe2dA917sIawzL6m/F+tKSLhXamG/STuLltVot6PV6a+V1u134wQ9+AA8ePAAAgKurK3j+/Dl88cUX8MknnySVo0Hrp5h9DABrenkwGEC321XHKV2EZuUbjRZuX2k0Uj+jqf5sCt54xbYhxQKl8btL/ZBKk1VmefOx2pCUrxHtdnvtXmm0xcbjsfgN9008tEt2Hc0Tn4f0sFZX6Tcf/xb9z/1i+r/llKwS/ir/rclGax94286jPzVbS6Mp5tNdm4zdpBOfaqDjpA89yhUVHHfYOWNqTGUNjKQ4hBaHLBfSgEZoSjlEg4XBN4VNBQcskIQc8h0eu/fs2TPY29uD4+NjOD09hdFoBHt7e9Dv92uHwlumFZqg19qwdBAUgeOQB7Jz8ktRfCmIBWrphIVUJ0mga23PFUwskClBO4I91u5chvIyaf/RyVmelge8KE27ZEhawNubBoK1+vDxlns0uHYvupV+OuZKweJoxtJtG1xHagZgSCbyd7GAqeYUa/mngDvtoUkC7mRr8saLUBCYOxlN2UN87HrywTFHdzTF7EG6gtbD89S52TXQExfo7iB+CoIFko2JOgQDrXz3iwWxIHGTQYxY3UuNp1xIbU8XCnLE7LPUIMjbhJBPaQ0WSc8l/cFtjqZ0riYLt2XbcbssdBqWN98Y/6fadkgTTjihzrDsYqRot9vQ7XazfClN9+fkafn2pskJqqsQTU1QSPYjxhM0LBYLaLVa8O6770K/3zfx0a71AcbsOO0/+9nP4Pnz5/XYxqugpJ38uXGFTSBFNmsyXZK7b2M8brV6dQIdnmjT6XTg9PQUlssljEYjGI/Ha+1Er92guz4tPi2Cjv92uw39fr+mIURnLAYzHo+vHSnK40L0f0qflC5UBw1S3HDbfJaDXeDRGCxxk12QbVI8mb/bVFuHYhehbyTa0R/S5kfoPBPKm9z+4Lpekg2a39wULHXC9tDiwAgeLy1JX0rfh/LbNVw7ppijKcJDTlPI8ceORmNUSkfz4c4qMgu9c4WWaQ0UepnBm38oLU/Dg8whaGm0wLPUFzy4uml4HG6t3XODSdzQQ55cLBZwfn4Oq9UK9vf3YT6fr90/R49B1IwuqSwL//CJgZhDFwocetrFYkjwNKlKVWo3jty+5W1I87QoG6k9Qoattd0lHub0hAKC2niWaKAGAB/r2rE6lvI4tPchZ2PTRjYN/KXwVErAICS3LG0ak3dS/paApDQ2JJot+mRb/RlykC20cJ2rjWVeHs/f6uxq+p6CjtXQzkRuE6XwpsVu8jrDJXnAK/9pemnXi9aneHffJmjcJLDvMFAaszliepCnQfsb2xCfSem0MrVy8HeMJi9/SuVqfbjtvg3ZH3zxgIdGqb0swQRLOZtoK6+/x/mJytYYuI7x6AspL0qPpINT+FkrJyWthZ9SdA3/PmQPWfoy9M7bL5a0NE+cTNKu/YiBHqMfglfvev35Tdtr20JVVdfsqlI2K9VZUnCTH6/N5QfGv46Pj9eOWeS6zypfttWnEj8/efIEnjx5AgCv6tvr9YL+103lxxjdml8t2UhNtUHIjioNr02CJ1YMBgPodDpweXlZ3/HITzVB2SktttTiKpwWOibp7tzY3Ykxn5reAU5lDqVPWmhvgWaz0b8lvRqLy4TSWXlxU/bKJuVDjt3A/Zht379My9NshJh/aAEfj1p8JsWvwHwknxDHLpbLTxpEGYLjfDKZRMsK0cbtiRSUkrtSm+Nv6RnA67uu8YQQrb5WveaROx45w/02r9+RYgOXGJfRY4pzHShL/gBxhUXTIuh9m1o+dIcWzUfauSUpqVBgxwrMw7q6wWMch5g05KjHBoF1YDXNHzcJuIIG4Hq7XVxcwHg8hrt370Kn04HvfOc7cHFxAT/72c9gsVjAZDKpVz7jkUM8AJQy4D1HKYZ2e2rgwc1N80Jq4HOTtHrHP4BPoeH/KRMDdJU+QPjoPazD3t5e3X54lxqCH6NpOab9TUbJwHxpnpV0Ij5PpU/6fteCJZrejxl3HmgOvZYvl8Gh9MgHdBejBdQ4DZ0iwmkL5RcKjofg4WVL0ERCaHe+tX4SDbgaPnTKBadNc3DeFPDAl1eGxL4pKfuoP3DT7Ndd4xs+Ni1+HH/G5YiUFx/H1pM3KH3evq6qqt5xg/eBdrtduLq6qu2tXeYdKuvp3XTWCZomQH2F0LNccP6QdC2Ft74hf9m74xXpDQXCYqBxEGrLWyYEOLSYyC02B7TN6LHEWh9Op1N4+fIlALzio8vLS9fdsdru8qaxWr2aiKIyCncbWhe97CKonC0hR3kcrwl5eZNAd0gPBgP4lV/5Fbh37x4Mh8M6zdXVFXz++ecwHo+h0+nUEy85GA6HqnznEzmWWAw9DjXme0q2i/QN5ZEQMA3XNyk2TQqf07baZRuqaWzK50nJS+IhC2/lQrLNcmNKPEZCxzCOaby7HEEXI0ttF4u7xNrK6r802eYxf43KsVhsKsXutdCUOgYk3xKRSmeTvB+cjPUq/RQDwSMkuJOAja2tFqW0cMc+5phq39JnXgc/5uTwvHiwyNK2WlBECjzFAijWtLG2ktJ58qf0x8rwwkI7b/8Qz0gTUQCvjJzpdAoHBwfQbrfh6OhoTXih40X5igfKPePQUz+aNjVQE5vQsOQnBdt5eSGlzAMPnoC71Mc8bymA5EWIh7SgZCgfDbQ+IeUWSyeVgyujANadjphiz5FfOciVHbljAv/OqVMO/bm8yiHpKAs04zMkXzcRcPDaLZ4JgFB6iw0QSuehZROwyF1NxoVklBQ4COVB34d43xv4proaYH3xlUZ3DFY7slQ/biqIR+0XTadJ9qjG+yVsjlJOHv0+pS09ejBUthdNfeOxs7RvYmPQGpApoYcpX1VVBZPJpF6AgbtrKH+nBC83CfQz0GaL3XnJbWL+XHpmkZ08rSbPQ5BsBerbhuxn3q+8TinyQZNZId7kNndMjlG9M5/P6+PiuL7DPqble/WKxQ/YtH0WQq5dv236Q2MqFncCeM0To9GolkfSqXFWGrztkduGPO7RbrdNx8juMkr6sJpMyom3lKCnVLkx30azE5DH2+027O/vw8HBQX1N2Gw2g/F4DKenp7BavYr1enlKkum4oIlPOiCdlsVOVL4iTVxfxMZUiv0Ys4swjbb7P+bT5cJi/1lo4e93UX6E/FGvT+qBx/aVym66La185KVDkpXaqao05t7pdFRZrtl4ofK5DxOyPVPKoWVp8MTf6Cku0jecVyh/Wfy1UFzWwqeajIvllcvHqfo9JuPVydhNCzHurGkVtSgqvuuDKkqcPJCYjO7iojTwVTwxZcsHH3UW0TjQ6kSZmpZVYldtSKGFBsUuKrRdBG3D8XgMT548WTtaqqoquHv3LgwGA9VZl9obv48Nfs6bMUHOdy1ien7EC69fiG56n+pNR8i4RhlR4h4hrsSshvhqtaoDhKG+8RgN9J5ALkOlerbbbRgMBuqRnaWNy9TvU+VYCt3Sna90bG0TVM9IwWSLwc6/KZl2m/DYHFowRAvOSro3xfkI8XEsQEPHc27/WQMaUjq+o1ejRRpHVnpTxlms33haPLbIK+OobRfirSbGjbUt8W7DHP1G64Y6ymujcFjGIc2T8ntOe4aCW9xeR97epNxDGnL0o/T3LXyQAmoA6ws4SqB0H6XYOZvmk9ApK8j7OSexSEdB8lOKUoD6CI9k9+aDdyI+ffoUlsslPHnypD6SE8f9crl08RbKKGrve22/XbBnc2DV002A5t3v9+u+Wy6XcHl5CQBQ3w/M7T/s9+VyCc+ePVvz1agPFrpOhgPL8I6bkrEhnEjrdrvQ6/VqWqbT6c7zWRMxsl3Ww6FYYilYfYy/+qu/WksbsltjeUrvkRf5ZE6v16t3cVtic9RO5MeQa2XT8W6J5Wn5hGhKec9j1jFoaZuSsbses+Z1Dp3umYpYHDHk0+QgxQ/xTJiVnJNAWUHHMD2GF2P5Xv2TE2sEgGvypmlQG1CihwIXo0ong/F5Kw2arIvBIwe1MlPatXRfSPlFjyn2IifoToWR1TGgZabkpzFfqB6x8qzBU/pdTgCcf5/bbilIycvTx9r31rItfBnLj+ahBWDppNpsNlsLylVVBePxuHbw5/M5DIdDmE6nMJlMasOLH6GA+XG+iikw+k2ojXg6rV9KBjM1OmhaywSGxSnwKnpMI5XpMUJjsiPEZ6HgKqXL2kY8WK3VC/9RPqZpVqvXE8D0SG06CU/zonlYxmAJmRSS6VqfhPoq1nYeeqxlh8qx8nNM90iTI1aZodHr/Y4iVNeSxpC1fnwceIw5TrfWxykBAw+sBraWvnT5Vjq0vELPJX6WUMrR0+Qs16E57ZlrI3nKAXhdJwzshpwffGc9Hj+lTagusX7rGd+0nNi3sXcpNnhJlHAyS4//XEh2CH0Xkgl0sQfX4TF7GNPRZ7Ej/PhYidmP/FsvPDwWk4uS7Eyx0yx2ulZ2Lqy6zdL3SBdFSA7Gxg36dqljq9VqwWw2g9FoBNPpdO04PW5vc7piCNUrJT+OWN/H0oS+3aScLW2DUjkjHS1tGUs4wc8XUEvxgxAN0jdSeU2A0yDtPtwGPLowZGNbZaol/23YFTnlWtvQ2y5U3o1Go2s2aFVV9V3KuWXxDRX0hAl+eh393wPv2KPpc/krpg9D8RStzJD9rMWvPLR59NomxkxOGTTulpKXFt/TaAz5dKFYoTXuRMuypLV8n1MOb1fJV6MLHugYx3c5salUvzDFj/VAoos+w0WAXM7RdNQ398o/zT8KpQ2Nkxxb1/JN0/Kk+GRsSViDiDhTT4/okZiklHHnOe+e0pRzxxDdoVaiHpoClxgO23bXVyfuOlarFXz22WfQarWg3+/DYDCAX//1X4fT01P48Y9/DMvlEqbTKXQ6HdWQpHlZjLDYfbOobGg+2vix1K+JoEqTTpnFoc9dGY/QnGzNmLUEg+gzvBsFHXQP6LE/WvAbA/QnJyewv78Pd+/ehaurq/pelk6nA9PpNOnu2lKIGWPWdtXy9o4Jy0pzj9PvGYuxPGm/on5CWW81AEPBHKtxViJYwfPbdiAH6Qg9T+GnVN61wBtgt3xL80gxnjlCToEFpewnWqbmqK1WvnvCed50fG4a0+kUqqqCwWBQy3VqK1RVtXYPpXSaBj2hhrYH2pKSE0Z1rRZYlqCNeUqf5tBhOSj/MA3Ssk19FkITTmFTclOyJzTQemltH5KZUtCJ864UkNFow+/Oz89htVq571/NRa4+w+93yX/jctgaSKHjUroDUNOPXt2DtrDGZ1IwXKtTaiCS+2be7/Fvr/+m0bvJ4PYtXvcH9olmS6xWq1pfA9hsHK7ntm0vz+dzaLVaMBwOaxthMpnsnC9pSY+2UqlTEDaJ0r5TE3zV6XSg1+vVv6fTqZiuVByp1+vV8TgcL+PxGJ4/f16XQXerS+Bj03vnPX/W7/frd3h0fWjcx+wvbnOXQsxmT8GboH+kmLuWruRknMQjPE6zSV3giQ1Z87P4F1Jd2+12NO4O4Ds9QjqpJYV/m+L50K7+vb09ePDgAZycnMDp6amYpqoq6PV6MJ/P65PCQpB4LzX2BWCLjZSM+TSJKOdZhIGEWGBPeqYJg5AjJQVVuENE04SUoERHqB4SXSGlZwkqpcBDo/a9RYCl8gLNA+miNErvtACn9xsLDV54A8/S3a/tdhtms1l9H5XUth7+i01o8D4OBbJ4sEoLinp4gZfv4SULL3iC/bSOsTIsbRmiO2ZoeREbO1qeUr9KaXg+IVlbVa9Wnw4Gg/q4DzoRHGt/a31zZI6FV7U6Sn97ykHaLXwfGp+p40aCli8G/qSJ2FQHPYXWEgE/azBRG9uhPHKh6UAPf8ZkisXRj+nOlHaIyZUSsMovr44OpQs9D40nK6TxpY253PEfA68XBnlC/GQNLEiQ2s8rA3JsU805DOnZXcOu04fQ+MTLM6UCfB7bDvXjanU9yK7peMmepzR6bKBS2AaPeOwn7XueJlSPVDkSsuEs30p2M627R8/zZ6vVq2PxLy4uzEdjhmjVytDSxPK4RTo03arJD/y7qtavyaE+nnes0L+3pUeof4r+5E2Btug9ZZxY+0tKmzsuY/mXLMsL1MEIenqLNNnBZS9HyP/jaej/+DffTWfxOSzPOC1cf9Bv6L2z9JvVKr4QlPtLJXhJagMLT3n4LvbttuVYDFZ6U8eXZl9afGfr85JpPXYHponZUR79J41tlDVcB1lkBqfR2u45YyCGWF79fn/tukL6jNOFG3Zw0VQIIZmn8UDo+9SYYgm9lhuTjH2/0ztjQ5AcPMsxQNbV7qlB2dB3njs8MA8LA4TKtDJhyGGm9x3ROtwkY5kjdYKBfg8QDtaigUN5jd4NMJvN4Je//KV4nLEGbvhRIypWJ01JhYwXqyLjtJSGZHBK9JQqH5VxqmAOGUT871BgxcIT2O7WlbiWdkJ+pLThTgG6I2EwGMBgMIDRaASTyaQ+Qk2ju+lxVyL/2PfW4FxTNJW4w4zTCHCdD73BwxQnQMOuO1QUtC+aClB4AtqdTgeWy6W4cwjzkv6muiWXZ5sCDULG0nlspxx6+N8peUp9ss32x7Jx10GqHUzrIgV9StUV+ZXyrtY3UpDLUw7NU6rftrDt8t92SE4/gB4Y3VUZG4PV/rHYap7n9I5UKW1uW0q76Hk5njJQR/E7/7xjFCcdnj9/Di9evFgLgqEutNIl+WseWxKDcCn1uMVrcB6mvAIQ5zP09WazmdkXvQlA//KmxJaq6tWJQjzGQ+U9nliVs4DibQRvK5SDyBuTyQQmkwn0ej3o9Xp1+pwTCLFcaSKFx1dwoiJlQsILfnUZyn26G43LcYtNit80xZvaREjO+A7VyaKbStoMudDobcqWl/L1xHiahiXOa4UlXkLHNE/PT2ZIsf3omPXs4mwyls7RarXg/v37a7vtQxgMBtFYs+YLSUiNl7xpunTjk7FSA6YKRC0vzclNDSjxb6UgjseZsaQPCePYRIck2L35auko/TlBdms6Szt5v0HE+CRlsPP2kcrE44hbrRZ0u12oqlfHjuBxI6vVqj42ludLf/N7OktB4nsa5LRMNFposqSXyqI0pPKhBVo70Gch5Wnh9ZBSsRiNUmCKHw+pIcbnmtGG+V9cXECv11tT4r1eD6qqgslkUu+0DJWxTWh8I41db+AQ258aJfz/EL9TGlNhDfZicA8n8ehuOEtenkClxTnctoOkwds3ofcov2kaq+GvjUvadjk2Cs/bo7NTx3lovPF71HkaavOFxpa1LUL8nOPIl7R9m4RFL1B7gH4DkL5IT9PlVh6M6TqavxRs63a70O124fj4GBaLBVxdXcFsNquvQfH0VRP2GNclkn7fRX4qDU/bhmz8EH9L772LaZvGtvgxhJj/I6WT3qXSK41r+i43NqCNN+l5ab9E0m/UjtDuL7ToM8mv2aWg9ZsEbFc8CpvLFe1IQylQTfPi32j+Of2/tFyw+JtaHZqIZTQNSQdTPzlVBsZwk9pIQsy25s/RnwZ45SfRo6Alfk6RV7w/ut3u2tVP2Nfz+Vw9GtlajmWc8Gf0H453jMUAvOZFnjbVnpa+Cflpkr6wxK08tEj5WmI1m4gRlvo+Ry5Yy4iV7cnXIuNy6bK+l3SelF5a3IDjHMso6a9rOkJKtwlbCxewYIyy0+lcG1vT6bTeZMNBeVQ6xSIkKySbU8o79MwrP2+CvjRNxjYlyDzlhALn+L4J8MHBmQz/jq3GKjXIYvXV2kii2dOnIQPBGnyLCe1tDJhNB/+pQYkTIO12G4bDIUwmExiNRmuKILTqDo2y3FVtmqLgq68lx57/zg3+h+jC/HmwTBsTFlkSMxZWK/24F8k5juWZi1iwqaqqtclYjV4NllX23W4XVqsVvHjxAg4PD6Hf79c0DQYD6Pf7tbOEyNnNmSIbLMFWT/5ePUN5p/SdTJ52pE5UKGjQ7XZhb2+vlkFaXgBhYydXjsfG3E2Bpb/p/czSruQQaHvz+zCx7Sx3/4byTtGNlkCDJ9+Y4e6ls6R+ssJr0/LveJtty1bC8nHBBg1aURmXw3Oa/ilRX6nvqSwdDAawv78P3/zmN+Hq6gp++ctf1kdV7cK9egDXF4DtKkrY1SV0iea3cNuNl8VlMdpV+M4il24aNsFTlH8RpYOn2D8xOyUlX06jNAHq1W00vxBQt3M/DO1xekWIVxbTMUH9vjeJv3cNONlDIfUxgsssyuce/yo0kcGxyfiIdCzkrgLHvnT6DNowuNDektfbPM4sPEgXquKmhU6nA91utzEahsMh9Pt9GI1GMJvNoN1uw2KxgMvLy6x8vd/RvzGegL4jThYDQL2jnB8fyvnLypMeSLrMowdTabIskKP28q6Ms237Elo77TokWWmxmzj/oy2PvNFqtWA2m9WLLErxSUo+npNOUrG3tweHh4fBNOPxGJ49exZMgxvLAOILMCR4+NArI3aFn610mHfGNmkwUMciNNgs5Uu7kUJC3CqgQ8pGcsJiyk/7VvuuZDDKWqY0ucV3bIbySqHP0xdaYNfbbiUc9JRvl8vl2sRH6Ohs7oDTf5SXLYatFEiKBUAlxxCf8bPmOS3W4H6Ih3ImCLS8uIzQZAGXKVJeND/tt4bUsaN9x3klpvBoGi8N6JDi6qicvgnR6dU/3gBfquzxQpsop3mHyo3RZHmHjpwkb/AOYNwRhnlYnKvY2Nb4TOLjJp2m2DjWYNV1Vv1D2wT7BB1qq5yjEwnSMY4WPi0x4eEdP5Q+jYZUWYJ8fXR0BK1WC87Pz8Xxxmn2Biw4PIFvmp4GVTUdKumWTQQWePnaosNQP/KJTEp7KIhcSs+XyIvnsyuO3q6idHtTlPKFUvORvuXyOuRX7jLvhNolRb6XQMg+1XzAFPDFS1w+W6H5aqm2I/e3eV2t10tovk2OD5uKEvGCTei/JoF2CtX/FltN4imJRyWZJMV06P8cVp6wyoZce3SXZOdgMIDhcLg2+bVavVqkTBdG8IXsHJpMo7+1tCHcBP0TooXSH7IT8bnX/pae06Pq8USUUDvGYj6SvRiyd0PfV1Ul7qRfrVbR+IulnS3+O/+G/x0qQ6p3Cr1aWhpX4ceoUj237RNONBms/S6ZN03n9eM0eHw3i10c4xFrHIrbYFo6GsOMxSBzkGJDhuJuFj1JxwQfszzfTqcDd+/eXRsfT58+rf/udrt1nErK00qnRQ5IfByT8ZYY5abhGU+uY4pLGNAhxIKdFkcR08QcNKp0pbKkvOlvLtAlRRWjgdZTY2paH4kuTyBUKpeXF4K1Xl46aN4pabjQsjo0VvpS+N3y7Wq1gvF4LH4nfU/bXzpmQSszVdl70vGgCOVLXgca9AgJcylobaHD+33sG8yX7ozg31vHIB9z+K1lRTCXcbFAZczgkr7jZWj54G80ZKz3IUh1scIjp0LKPZX/Q2PTCk9/0zJSHXKaHv+nk7F8zKKzZ7l3xyND6e+cIAGlNybHPXoq5hR67QP81lrfdrsN7XZb3B3A85DK47LWq9OtaVL6PFRWDi9o32CeBwcH0G634fLyUg1UeG0niibsYMkGzhn7Uv4hx1R7hu2EAeSY40NtMppW0rUoi+i3IbokOr3fSHnEvtfSlApqhGDR95yuW8hI6RPJttTksAZv35TUb5ayUlCS32J2G/XVNZlSomzNbwi1t6SDvbvpNdsG7TJtkZZmS8biKSn2AoXmo25DBqWUK33TJP28/bE/rb6fpjtpXvQZ/U7ygT38baXLk76Eb0O/b8IeC6HX68Hdu3eh0+nUO5yXyyWcn59fm4xNhbVuMd3kRej7JsZHyL+hvC61pfdkOD4meCwGY7vUB5b0EtVBmv0pyZcUn4fSxmPPdOI4ZpdzhOjhZWvf87RaGVpZUnvF+E+TY3TjBJWN9A5PqU9SEPvOE4eyxukssG40s/gTHqTSy20eT4whh14aw6TyWipLs3U4LaV4q2Qe1thsu92Gu3fv1nFufgIAnkRg5SstLialty6Q2LSOT0UKf278zthceA0NiXHo4KL3btJvrPnyIwE15U3fh5wpSyAIy8e2sAgvWjY1Pix1jTFWCeOPlxUqr2QZu4aYs0LTlYKlPfjqbK6UQrtHpbJoHa1BrBKKV3tuKR/v0txWsIHSgs9oAAh/SwtFYm1H5ROXFbx/pL7udDrQarXqiVl8b+ULWrdNKFwuC0OGg2ZgefWQlJc3bYm2QccNjziaTqdwdHQE3/3ud+Hk5ASePHkCvV4POp0OjMfjmu8tdDfd1yFHN5R2U+B9RxeeUD1P7w3H+zpwrC2Xy2srezHvXH0v0WlxqJtATiAYv4u1R7vdhvv378N8PofRaATz+bx2vKzl8na36gz6T3OIuB600hWSTyGU6NvBYADHx8dw9+5duHv3Lnz22WdwcnLiPmqbpkNZxPUPRS6/0DyovsNJEyxzOBzCarWCwWCwdgSTNFY27RyuVq93QdD22GWbdpcR0uW0z0vwnhe7qN9yIQWwpP9Lg/vfvF9j0MY9lQlW3RzKX/N/vflymWD187mdT8v2xAo2jZRypW82QT+1CzzI4S+eD8D14xBLtWEqPTFsc0cb7S+6k4ri+fPncH5+Xi+wxyN0paOMNWxjMuQmgNteKM9TruiS2glPrJIWIc/n82tlU2j2Qc7EDB3r2pU9rVYLer0eVNWrE5XQjs3xByQaudzRdLUWryg9TqmPEJpo2tvbg7t378Lz58/h5cuXNY3U3m9ShvD2oL85n+XY8LkyHCAc228KofbJgVQXvEqHtvtqtbo2tqVYibUMzZ6w8Jg0lqz2Gy+X7p7nR+kPh0M4OjqCBw8ewPHx8bVv8CSxjz/+GC4uLgAA6sVG/OQY6yYcCaWuIXuTfN+NTcbyYFaq4VciAM7L9zotNB3NSwsO8vxDDKQpOE0Z5t7vEvou1EcSnXRyKPSNxAcWo8AqIDkNXoGYwpvbCNaE+i3VyIgFmLWyvWOaGkMxvvHSmYLYGJAMNy1IEWonzvuhdovJCN5mNLgcCu7wd16nQSoT4JVCR+MGFbsWWG8aljKtdKXwJ/8eQDY+Q3lynZkDqc9wwm+5XEKn04F79+7BbDaDZ8+e1Su9Z7PZ2m64EpDGQEk0wW8WucjbmDrHUh3pJLeHXqr3Y+ks9HN4dHgqQraZBVx2Su2IjsJwOIT5fA7T6dR9BLQVmmPt5Rst7xQbtSl0Oh3Y29uD+/fvw6NHj+Dk5AROT0+DNpumj0L2bRPQ7HNqR6PzSZ1ajW6OmM1cQi7hnb1Y3k12Rq1+YRMy3RNU3CZK1X0bdpgFlgAwRU7Q0mPveewz+syavsn+SA3khVBSD21bh20CuTws8Z8Wd9Hy18aWhUYLUvoxp+9zY4i55SLQtkRbAReunp2dAcDruz0RIVs818eztsMuyP5Ue5/nQeNHXkhjCOmSJndDp0RJY0lqZ83WiNEpna5A8+ATsHRXZKi81Fg6/x2KJXn6x+ufapPwOOmGE9XD4XDtXl2pDtsAb5tYLDCGHL2867rYOlcSAp48Rtt9tbq+mAPHEpfJfJxbYElnsc0oDZruoH/zUzPx/3a7DcPhEA4PD+Hg4AAODg7qbzHeN5/P4erqCh4/flzruFarVS8sorRI15yFZF9OWvqNFmvZdT6OYWOTsRLT5OblGRSaMZWbv1fhSHXvdDprRylIzBYyIOgzS2CPB3OtinkXjDkNoQmPnDybrrO2Awr/533VarXqI6vw/l7KC7hSDuAVj08mkygNoQA7pYG2R+gODw5LWir4NaRMXPBvcwU2bw+tnND3PFifSxPmh20Yu3vYQiemjU3oTCYTePHiBXS7Xeh0OvVqqb29PVgul3BxcbE1JRkbv962SAEae9qRdZpsxzI1Ayw3oKHplfv378PBwQH0ej1ot9vwZ3/2Z/DkyRPXSjZPu0q0SXnl5LENxPraG0AJ3TXMoTkOu9AuTQAn03CMYVvhooKHDx+q30oOMX0ecgRpGi09PufXYsTqs2vIlTn4LdY/dGxYKn3cMePjoIQeopP5VIYul8s6+IM7VyeTiXhsGsWmg8q7BqtfuCtt5A3KxHS5BSXqXpLPttUX3P4vPXam0+mafU9366fSS+1ySV9YoPkZXKbGaAnlJQXRpQDvTQ94NYVYoFRKn1oO/o96B+2fEHZFflqQ4+c3jdlsVvfB3bt34Xd+53dqu/P09BSeP38Oe3t79U4i784hb50lu0LSUbvYll6gbQ9QTg7hJAPHarWqT9AJlaXpB0neWmKzWj9Jd8Uul0u4uroq0hYpefC6eSdUvYjFrA8ODuAf/IN/APP5HD777DM4OzuDjz/+uO5HXGDJaedoyia36F4PYvplV3X1JmRRp9OBfr9f/5b0I04oYjvh9V0hxCYWS7S5RgN/3m631+QXt9vQ/wQAuHPnDvzqr/7q2uIhgPVFQ6PRCP7yL/8Srq6ugnqL7yYOQYttpqBUrGBXkTQZW3Jiwzs7His7FhST3lnK1fKX8g0xoGawx5jMGsi1Onq87b3BYU+amOLzlG9BKSUkOf9SWZLTSvOICXApLQ1WShN4fDIFf0t3SnD6edDZOhll4cFQXamy8I5fXhcPtP7jbRei3wOPHMnNOxbgTaEl1F68vMVisXZ8M598pP0eojlWp1RY5EtIppekgSJ3coPmK41vDdq7xWIBl5eX0Ov14PDwsD6mGI220KRSk0bSTTK+eOBdcoi08ardaRoqJ4QcR7kpxHi+pCNTVVXtjKADTnfue9qD9iPNPxSgturLWD22DS3IjCuN6SIBi0zjbVZygign+E2D27PZrF5U5OFJGizE35ZvPDSvVqv67h66+Ap1bpPQxk1pvbkpeZWjg3O+28bYLlFmTtDQ017etHw3EM+H+2eS3xvS2R5oPnVIV2jtw/01i28Z82U8Mlr7fYvr8AZxtT4NfUOfx3j1pvYZj7mE4gChMdUUXShv9vf34ejoCI6Ojup3GBjndpGWV+y5JQ7KkSu/tolt0B2KwaXQkyIHQnnRmJl0PDJdeO/Rszwek/Idp7XkN1paTisufAB4NRl7eHgI0+kUBoMBnJ+fw2g0WsvDUtcm7TNL/2v2SQzclrGmDb23wBM/9voK3vFEv6PHUdO01Nez6s9NxAQBdD7nCB1JLtWv2+3C0dHRmq2MPjvqqPl8DmdnZ+IGLt52IT+myfEVs5W3jRxf+MbdGUvhMV62CbrrL2SI8R2yUhqExVDzOF27Evyj8EwKWd41gSbKsgTScdJ1NpvBdDqFbrcr7rKNwTIxRifZ8BnyMl31rTn/HHxXDM/HQtc24JkE8Uz+4d/c+NYUrfZ7E21H+VEqD3lwNputybFd6tfSwWML+Pigd9RsE9gWL168gD/5kz+Br3/96/C9733vWrrFYlEfc7JcLtecHAnU2JN4meseegcQpesmgwdjpIlsnPyh3+DdJji5kzKRR2Uq5ttUm+bknfMt/Q71E7Yfvmu32/XO2OVyCefn53B+fl6/s9LIdR+AfAKB5qikOqPbhtQ3vV6vDkTeuXMHVqsVXF1dwf7+PnS73XqHmRfWIFhKW2pOOcB6P15eXsLnn38O0+kULi4uYDqdrunlbQUM2+02LBYLmM1m8I1vfAPeeecdODs7g/F4DF988QXM5/Piu0YkOjaJJmUW5x3NJsvFTRnnTUGzYT2Lj0qDLySzQLNhStm1/Dh0iw8o5ZGjXzY5ufW2Afsl5YQZ+j1/hhMR/Du0g/g3NxHarqZN1qvX6wHAq93z9+7dg3/yT/4JDAYDMS3Kt/F43CiNKRNgu4hNx+wk5PrhPLbraX/+Hbdv+aQRldOeidQYDbH4naYf+HHcUl6l4z9oE//+7/8+fO1rXwOAV2Pz008/Xdu9zlFKbqTmgbIB/frUOzclWrY9hkJIoS11sk9KQ+83Xa1WMB6P12JNkh6N0VaqvWkcwjIJXFXV2gmYPB29jkkDvX4MMRqN1r7BUzgxTw9K6h9LO5eSg9vEGgemTM7lNELuDDmFh3bLpJE1IOT5JgRtYNFyQo6WlRZP23gFjkWhe5CTnzZh5BHkmw7G0HIlHsD7EPCYYnpEESr5+Xy+VldMy+mIrTAJ9T3nkdCEYMzQDtGhBXJCATPJYOTpQ/Rr+ZYC7+MQbZyWEC9ZlbhGi6csCXwRybYC1zFsajJEK6eU4xnTEaHveH/jxPB4PK4nXXFX7IMHD2CxWMCzZ8/WJld5PWPlWfSplN9NcDQQoTFoCZxYZG8JfWz9ltJkSVsadBJMKkPT8TwNBgtCi+EoLHIW+yikS6T2lyYFpbSxOmn0akHb0kA7pMQiihTnWnsXs1li8mi1WsF8Pq+PGqa8R7/F72nAu9PpwP7+fv09TkqX3rHabrfrkwtSFmuURmldatVl2wT316z2rjXNTUGOnPH4ZnTMWnwOD704nkO7DehzTTan6ttYu9HxIOmOlPKQB+k/brOHYhxN8u+mbPRdgJdnNJsCwO9r7KoMkuR/yVibB5JMaLfbcHh4CN1uN7ggj9pF/D2Avf2bqmuI9zSeaoJntsWHpcq16p+cfkRekuLEm2g/j96JxRq1uB59J+lWiV8HgwHs7+8DANSbU8bjMYzH43qHH9VxJZAS06Xw2Ie5ZYXK9cQTpe9TYRkfMdkfoh1PJqA2TWpsJoRQe3r7QkvD86HXMWkxCFo+6qr9/f3apwVYX+CxXC7h8vKynozN8b9SZFwuj++CnVhibGTtjN01Yy6HnlxFaaElhT7rnSASPOVtk6FjbfW2OGfUIKGTqCggj4+P65Uq4/EYrq6u6kmTyWQC0+l0bQULBgcXi8Xa0QNc2Fp2pWqGuzUAIgUztPxjfMt35aZCC6jxNPSO3hxYJ6TwbymQ651MaFpGU4eTto+00mzX9MW2EDMmpfQA9qCdp/yqqmA4HMJ8PofxeAytVqu++3AwGMDf/bt/F8bjMfyX//Jf4OTk5Fp5FjltuQ9amtzgkyE52FYAnJarBWZ4G5akMyS3m3S4QvQgvEFISxq+GwQn11LHhvSOTu4CgHhUWEo7lm57Wo/UiYISkORCacc+xsu0X0vIArTL6XGE+/v78NFHH9WnlYzHYxiNRtDpdIrYK7sIPl7eRB2vBQYl+5nzuHeC5CZP1DZNu2Y/5Mpb1A/D4RC63S4MBgMYjUZwcXGR5XtrQWerHJZsKypHqM3kqT/l39g9fBqsbb5NvbPLCAVQQ4j50VoZNwFcrm5bn1RVVS96Alj3Y4bDIVRVVV8R4PXpmsZN1SFNg/NXyvcUJSZeLWmR96TFd9S+b2KiP5TG6u/gos0U8Ikn9Le0MkejEfzsZz9ba7OUUwNLgct6vEs6V15gu/OdydY+3SV5heBxIC/a7Tbs7e3VC4Vns9nayWNS+tLxFgDbZKxFlvC4ON0Ri/4nx3g8rv8eDofw27/929BqtdaeU75ZLpfwySefwOXlZb0RIweeuNIu8uC2oLb6NgJ1OdAcn1L5peZFB9JyuYw6TlRY8zxCwSyNvpyJzE0NqpAy9zoqJeiJ5d+0AOGrU6Rg12q1una8JQcegblardaEtsRbCM0B4nXnR5CisRXryxifar+t+WkBMGveUgCXGgio5BH8ON5QPpwm3rf0f+vEd1O8GON13s/86Gk+QbEr4LK06QAi7WO6sjW3zBJBLrrQB+nBhRudTgdarVa92COkX7nMkPiTOm0S3aF+KGGLbJMHLQ6yVb95JzC9ejOWR2k7y1KuJCe1ckNyy9MWsYkVi81C08aCNlb+tOq8EEpMilJdT++pL8XrofTU5kgJalB7hee/WCzg7OysPmUE62i1R6RJjhLAo8sBoJ4w6vV6wUDDTUbuBBuAfvyXZnPxvz1l5o7JnPJD8MoXC1L1SsoElNfn4mOay3NLe2jj3SI3Pe1MA8z8+1g+lgA5H0MhH0vTUbn8cxtsu45SbbILflWJmAjl+Rj/83HcNFarVb0QGydeqZ4HAHj27Bk8e/YMTk9PAQDWJmi1PL1yIlTfmO5JjdmVbOOSvJrLayFfNdRvMbve65ulpl2tXp/eoslnSx2kGFSMbzRfLKUenu/4tw8fPoR79+7B4eEhAACcnJzUO/twzK5W2zuqXfItqurV6YadTufaMbGaL2sZo9Z2LW0LpvCw1C6xPKUxGipHOzKcjm8uw2PtGIt/SXW05BWLV+Bz9LGlWBzyCZcJmAePT1dVVe+EBYDa19XqytssFzljkfbTLtuWFnnM0+zcnbElg+MlAtap0AYXHgOpGZ303abufAuhpACPBdxjZWyzPzeNmNDBY/BCWC6XcHV1VQfvEHwCUTPCKA0oiNGAoDtD6DeW+mjw9i/SVGLHSUjR0PsbB4MB9Pv9+t3p6Wly+byPrYEYKZ/S0BwW/M0NGSrbAOTdYruKTcjWEhMgHLntijtC8D4kgFey4fz8vA5AoLOD5dG7zbTgpsUYlXgqhCaCypsAdQT45IDmoMScjRTk8EpT4zfmCNFn2jG4qUH6EE2oUyTHzTuOMX3OCScSjTRvz3eaLNcgvUdZD/B6gRK3aa0THxqdtCz+jv/zgvcnbZf5fA5PnjypHd8U+kuD13UwGMDR0RFcXV3BbDZzBwNuAiSes/YDBmO63W4dpOCQ+OpNx7Z8yBR9n4NQYKtUG6TsRuHfS4tJJNvaCrrY21NXGrwG2K4cedPHoXRXcigwva0xuwlwveWZVNgUb6LdeXV1pfr5P//5z+F//+//Xf+mOrkU3lQe2DYoL2l8pU3EaEdUl6SL3ttI39H7iFF+W2U9t9O9PtUmeVGz8X/lV34FfvM3fxMAXtnsX3zxBVxeXq6lwbbjE1KpfoNXFnNfsaoqGAwGMBgM4PLycm0hZc6iypsyQSWB0xvarGaV+1JMGnmAL6hBaH9LNMbiFiFovCfFgmi53W4X2u02dLvdtbSUr6fT6doJmJhmPB5Dt9tdK//58+fw5ZdfrpWP7cbrhbZhyu7yUFum+u43ASm84bozVmPKkoKgSSMmh74QXZb6c4crlCffGYvfSELEG8RObQMetEJ4+suSNkZnKDAstSvPL6fttgWs8+XlJXQ6HRgOh9Dr9eDOnTtrdyN0Oh34+3//78Pdu3cBAOD58+fwJ3/yJ2u73WjgFHd+YBlauWgUSpD4mueB77SyJOUmGY00fQ6fhJS9pIQAXit4DOzh/b3aePQEQihNnO5cmRrjYYtc0/LENFReWcbZNgIMlrGuBQVy+yCmSzVejfFwDl0aX5yensLf/u3fwqNHj+D+/ft1QJvqo1arBf1+HxaLRe1EcGeHlsGfW++XvKkOhoZUQzQ0XixtlGP0NqEDY/yBiMlvSa7w4PZyuYSXL19eOyYrdnS2R16GJlit41RyqKV8aX4WHcjzl74J8SVP3263YTKZwOPHj2ud+PDhQzg4OICf/exncHFxUcsMvDNVums6VIZUVyu4nao52Z58Q+1M7RpcYYz/qC3wpoHyAwa8MEAZsgFS5UjIpk+FxSeTfDFMH7MjLXy4SVhtPUu7Wsdmqn9okQWpNhDarPj/4eEh9Ho9ODg4gOl0Cs+fP4flcgmdTueaPpF0VMwf5b4PtYOkOtF6ldC/1I9DUN8lVAetXp54iNXu2bafvSlIdgtAvrxsqv1ybMxQTMCTjzXvJtHv9+E3f/M34c6dO/D48WOYz+dwcXEBL1++BAD7JE8sTY6cy/ULY3k3hU30ZcwP93zL4wXWtrHa7NiPXEdg/MkiQzR44qcWmr3lS5DKpM/a7Tb0er1aV9HjWrH8+XwOz58/r69bkmjO5eFUPu12u9Dv968t8NRsAF6WZA9wf85K2zb8EUqfhY8orDKVti+eCkkXVYfy5P4xp9vC31r9pG+0WIeWPz+hDmUD9be4fffw4UPY29urjyg+OTmp05yfn4u0UF+Wvo/JOI3/SsdOd82XtsaJLDwcPabYwnw3BR6DMSf/WH60bUMKMOaUeZRjzIGy0C/lYQloeBAyUqxBSJ4fV1q5BmsJg1dqSy1fTHd5eQm9Xq8+oq7X68FyuVybjP1H/+gfwbe+9S0AAPibv/kb+F//63/V98lWVVUfs9tqteqAoWS4UCedB1NpG1onVqT6a/WkPE+NU5ou1AeWMULz1XagS8c541EQ9GjGkPEUkzkhpSy1w6bB+53+j6BHVPMd07xNdiXg4pEhOU6cBGmchcpukg942WdnZ3B2dgbdbre+N5bTiIbvbDarZQg1xtBo5CuHeT7WoJ5E5y7Coz89jhPXfRbZmYNUWZ4TiAoFbLXAg5YPdXBXqxW8fPkSOp1OvToUn+OJB9J9Rp424LukLHRa4A3a8WcxR0Eai9qYxLQ4GfvkyRMAeKUjf+VXfgXu3r0LX375Jbx8+RKGwyGsVqv6igT8NrYrOMWu0hzoGDyyNMQbvA03NRmbq39K6JHValWPq+Vyee1oZ4Bmg8MW+qzvpHETklXe4Cv+va3gQqqdE3ruLbeEnpDeW/xtmp7a7q1WC+7fvw/D4RDu3LkDJycn8Itf/AI6nQ70+/16LGOZvFwLj/HAFuob6fi4UL1KyBPqD1uCbJtCSRp2yc+IwRPA1ALIpVDaz9HyTPmuKV2qtaE0Lvr9PvzgBz+AVqsFP/7xj+Hq6grOz8/r44lxbMdAYyuWtCHapXw9sMZEd308xfyfUN942ozqMKpD6DtNz6XYDNpkUujbkP+p0bKL/jWlsd1u1/c0A0C94BPgNe2z2Qy++uorWCwW9aQcr/M2+BhP2RkOh/UzahtoNPJ+53/TXaSWmAOHR15wOzYEyv+WukjfhsqS8qT3eAOsnxzp4W2et0a3Ja6ryQNru3D+51cN0qPz8RlN/95779U8Nx6P4csvv6z9NGoHcvqlPih57Y5nDO6iXEKUliU7d0zx2wDp7kAuwFDQ8jPALQyAA6pJhas5iiUc9yYVJlcq2xrsmzAKHjx4AH/wB39QT4r86Ec/gh/96Ef1kQcIfoQibyN61Afvo1AQF3k5pY1D7UOD6PRYLu3uVs3hxXdc0WB6XBmP/2azGXS7Xeh2u7C/vw+DwQBWqxVMJpP6vgrJOAoZTJSOWL015EzEaOVq/WmRK7QvtGPY3lZIhmoOcmUYTiBgYF3ih06nA9///vfh7OwMfvKTn8Byuax5n9MC8Hp80vFPZUtot1GoXm8631j6MbWvU+WLh7+8Y1sKbDfVx8jnVVXB3t5eHWQvNdFgtctCtl8OSuZlxWg0gmfPnkG/34eTkxOYTCZri7y4jWAB5zeJ/0oEITld9H8+ac/T8W+aBre9Li8v4cWLF3B+fg5XV1c1LSmBlTcJ2Dc4eYZ6SLvz/BbbQWk/j9oaIfCTbObzedKd0xwpegDTctuYBhdpWi1IRoHf0CPkOX0aPZvwvT3I6ZNdqUPT2EQ9d6EtLUH8VISC/QBQB7z7/T50u1349NNPodVqwdnZWdLRol77mNoa2jHJ3L7a5QB2CXhtXW3Sgech5Sf117bbt9VqwdHRESyXSzg7O1t719RY2YZ/QX0lXq9PPvkEPv/8c5hOpzCfz+Hly5drG0443dsA2p57e3s1TUivdMR1UzTcRCDdfKF0aBKU+vO8fb2LGjDPUNk5sVorer3eWrx+sVisTajiM8pTnU4HWq0WfPHFF/WihMlkIt4ny/10Xt+Sk7C3CON2MhZ8q2zpN1payyQoX3XBJ8DwX+g+Sk1ReVZyhOiMfaPV39ouIeROMMXK8vT5LjiqmpHFJzyn0ylMp1Po9XpweHgI3//+9+t3L1++hB/96EdrecSUDZ3oDPV3zKnx1jH0PfI4HreM40Qbk6F8pDsKaH/Te50Wi0WtGHu9HvT7/fou3vF4LAaf8W+kLTYhy+WAhm06BJYgOz+2mH+7beQ6FyXqUaoPtf6wyjh+ZCvyKa6+63Q68OjRI9jf34cf/vCHMJ/Pod/vi/qL6jU6Pjm92njgEx88/13hHytCskV6LkEaZ6mTW9aJsdTxEcufykMpf63/Y3TGaEL5XVWv70j23PWt2Vra8xAtmB/VXbmyIESfxX5JsQ+n0ylcXFzAyckJTKfTun21oz2pjuOOIKfFOsHBdXQMsfHIeZPaSbTPJNmKMpM7yyVA85tOp3B5eQmTyaTefUzp2ASwbfBeLt4mm5wo4GMKn+EdmtJ3KXKNllkq3ZsMTT7m5glw3R6QeE+yPSk/YGCrZF+E+l+y0ygvSvRK+pHnx99bfA5KR8hf35ZseRvh8V1LomQZHrtWSy/5wtviQ7QRe70etNvt+kjiq6urazTFxrwH3M7YVL1Ll9Mk/+bGnGI8JfmuoXxieWnpJXsypsOGwyEsFgs4Pz9XY0oxxOJlHl7QdFEsTSwf3kf4/7Nnz+pNEHTiDe/ULMl3KWMCy2+329Dv9+vneGWcNe8YP3l5z5K3FZbvU3kTQPYbQ9+j/4lpeNt46Y3p4xA92rehuIcEeqIXxjBQL+E3eCIRAmPaZ2dna+NGK8Myx6ThbbXVmsDOTsZag3pNl1MS/FgBi+PO76r0BNZiSj8HMeOCv7tpQfRtIRYoms1mcHJyAv1+H/b39+sdmqenpzCdTuE//If/AF/72tfgX/yLfwGHh4cA8GpVzOXlJfzu7/4ufPOb34T/8T/+B/z0pz+tBTpdUY3GHlc62kqu0CQJneTRlGQqSvCTlge2RyhQc3Z2BpeXl3B4eAj9fh8uLy/XVsqGxoQEzQnV0u4KUuTnLsmClMBsCVC52ET5njaW7nT86quv4Pz8vL5nejgcrh3Zp5UpyQoe9AvVF2UQ7san+e4S3+8amrRjNh0Qksq01I2upAZ4vaoW+fjg4MBVds6kjQda4JzqY09elA7ujJXikVarBdPpFGazWX1HfYwuKmNCVyRYgI4pto929CengR6pXwJYXrvdhtPTU/jRj35U5z+bzd7YXZnY1o8ePYJ3330XWq0WzGYz+H//7//BeDyuV2SX3gEg8TD2P05Mv//++3BwcADvvPMOXF1dwV/8xV8AANR3jiFdEm3YdzjBX1VVPbnOjyOXjpnF55qvdavHdITkU2jMetoU86HyEHcW4K4Vnj4GLr81Wj3+t3bKT64soTzMg3gW3PLuLUohNHEb8hc2wYMheTOZTODTTz+F1erVglPU87nlIUI2X+4EWSitxT8riRx7lNvoqRNDlm+ssVYpf+0KE/pNyL7HNP1+H9rtNuzt7dU+ObVftHhsCFJ/a5NNnoWrJdFut2F/f7+2x+bzeX0XqAQrT20q7i9NCgJcjy1yPgnZOtLkfQpdTSKVPqx7yG/i7TMYDNZOSMQTT+j4CNEgtad0Z2qMbiusV/uhbUrjyvwKJvQ1cIG5lC/fbMHzoaBprOPjbbQJm5AdUQtik5OVFBZGbaIczyoSa1rNKaeDUgrOUyEQcjj5Nxq93onRWJ01mjQ6Le1iKbuJunrRZIA61Nc0yAfw+ix5NFY+++wzmM1mtYDGnZvT6RTeeecdeOedd+DP/uzP1hQVnSzV2ivGf5Y6If1WxPhPe2/hD14Or3/sG3p3LK5ewgCx9H1TzoIVTcpxbaztspLWHJBt0qGhxGR3jK85f0wmkzpQ12q1YH9/fy2AaQ2Ohhx8iSaaf6g+u4wUWZcbpE9po5j+pPYH70epvBQZI9U7pINikOqCzgxOwPDj9i26LbdvLDaKx5bS8pWexWiX5LYlAIUOL57GQU9D8LZXbMzToBX+3ev16rsdR6MRjMdjtV4xW5rKHU2+hdoFJwNx1XtVVfWO0VRo39KJo00dd8aB7TkcDuHo6Ki+Sxhpth4vllKuRgv22XA4hMPDQ7h///7a3WLaWA/JvxAvh/iBy89dhxSEDcE6xr1ygLdpzG6wti+3SSQewEl3LQ1Pb3nuiRvw71LsOAsoX/O4g7VetyiDm+QzpdpfKUjxn3PyR4R0/3K5hMlkAuPxuL4eADdLxMYltT1oGZZ4S0rdPTGZbemo1HKtdHtiuSF44pYxejQdpMkBak/hiWw4EcttP4sPaZE3JWSS1/cNfUd3BqLPIfmhUuxPy7dJexTLootE8XnIBpFkiNc/3oT+sPCY1KchP1jy1bTYg1Qe9Q21k4m09qV/h/pFotsDWk9PG9L6WxbpSXwW8+e1NrHEF2K4KT6QFSXqI/WJaTlXSpBtl2GtT269pcFEg0poyFHmpquqLUG6UIBJQmml1ES5mkJNRSw/j4O7K2OBC1wJ3W4X7t27BwDrx8bijlmAVwbD4eFhzYuz2Qyurq7WFBwA1PdJ4o6W0Op/Cv6eBjxDtPO6SnnxnUPeXS9UUfExV1Wvjz6md8dKaLfb8P7778N4PIYvvvgiaDRgvlJ9KF0pPJYTqJHa2DIOeZqmjN5bbBYY6Mf7JwBe9WW324Xlcrl2RzI35FCWSDuHJNAARWg3eolA5DbhlZXbqqcmfyzB6RL2RMo3KFc12ufzOZycnDQS1LPUnY4TKv+le9o9yHXAQwF/K7788kt48uSJ6Pjl0MfbFo/7whMpfud3fgfeeecd+PDDD+GP//iP4U//9E/rAAh+Hytfekfl1q5MUGBd2u02fPXVV/D06dM1+jZ9tw9e10Dv53rb4bE9b2EHty9CY5raCNw/8ci11er1kXDcF+f+T6ngjJdGBH7j3b3Eg5j4/63N/nZjUzwQK4eO8W3JVRzfs9kM5vM5/PCHPxRPSvDkRRGLFQC8Pk49lG8qbupY3yR/Sn6sNvkRyounl67I4mnwzkiabrFYwPPnz+tYXNPY1CQfgrYnTr5Kp5Lgc4xJaHltCjR2iLuYke7ZbAbj8Vg9kYXnE5uQfRNA64X/48YiLa3Uz7PZrG5TviMWIZ3ApNlufCGpRgvf0BQDfqOVS5/h7neeznKtRmzXtdSG3tO3bn2dPEj9vzYZq82O83ehDG8CrHSnOnLaBJSURptotaxW8Bh3peAx1GPB8xhPNeEUcJpSJgU3gZCRBvBK4M5mszr42G63odvt1sd4fPzxx3B1dQUffvjhNUH7wQcfwHe/+1347LPPYDwe144FV4TShD+nJcSnEt25bch5IqUfU0ADHvP5/Npdad1ud+3OAu37punz5J8ycaqVI43Vm6ofQtjUZLNH/6bwlPQtDTzSoCM/KoV+R9Pi36ExkCpnb+JEbKrNoNXfUnepnVJ41Boo03QCp0VKH+rT0rJEch5CukqjzUKHt+1DfW/RdZbvm7Lfq6qqd9CjPpQmZGNla3YwBd8Zu7e3BwcHB3B8fFzfYY16ma6ep/lR+qT31jprz6lNHps4SgUG4TD/UFAvF5qvQe1OPB1Esn82FSykx5R1u13o9XowHA7r3brSanl6FHETdG7KVigBr+/P4ZVzVlros9T2i303nU5rXp5Op0H5GqNJ+ra0H+u1oTh/cx1t7d+bZHvdVGxDRsT8/E2UjWhivOSA+v34vzSRliIjpbqjLpPSx+ANkEv2iTWes21IcUKP/WZtq1iakF+RohOlvOjpd3h9jzRBH6OV2+ReXrH4ECX8DJoHjXMCwNqdmTRvOoFWOlYQ0v98rHAbGW1PeipTKH8eS4nRgO/eFN3MF9MCxOsOsL7ZSEsT+50TOy0FaTzR2HmoXCnGLsVitLJKxQNuErzyKtZmOXyxs3fGvmmwdDBXQgCvBTpVRtZ8LELGIvBi36Tcw5VS7i1eYzKZwGw2g/39/fqouOVyCc+fP4cXL17Av/k3/wZ+4zd+A/7Vv/pX9VHFiH/2z/4Z/NN/+k/hX//rfw1//dd/DWdnZ9DpdOD4+LhOwwPoljtMQkYLzzd1goCXowV6U4Qi0kbvjKXAowin0yns7++vGcj4XjuWRKIvNAa24ZBqZVppeVMMwrcJsX5Ffh+NRrVjqIE7EXQcUUfF6wS+reCG9C47XVSmURmqpY31K6bRdrtabBr+GwMZlh1EKTzqocmDmK6I0dXUGELbFO/o8YCeCiPZEN4AG2I4HEK/34fT09N6Zz/mB7B+mgYNrCKsATqpTVH/4z/LKuabgqp6vTMZ2wyPih4OhzAYDKDb7a6dUrKpe8ZwMhhtr+PjY3j33Xfho48+guFwCFX16u4lPFoSAOojrpF3JV6jsof+04B8JQVF8DcNtKeO6zcdTQVVsT95/i9evFhbUOA9Xpz6NFzvefximlbbHeKliU/sSPeHxXDLm7fYNLalN+m4oXyPC8729vZqfRI6LSuUPz95A+C1vYtHsqbS/qbAM9FdagI5Vqb0XqMt56QSyh94NPHBwQH0ej2oqqq+hswDi7+1C+DtuVgs4OXLl2LsgccBvYsjNoHFYgGj0UjdEetZ1BHrI+6XxPIq3UZSH3gnu2g8FRcdaOB2mpTWEmvwgMvvFL7j/SQtLJnP57XOkcqRaOFlN31a0i7Ho5pA0zIyazL21pG0QwsmxhxOLbAYKocOdim91m+lnd8Up1r7RhJelvw9NKSscijdZh5owb7VagXj8RjG43G9UxYA6iNPMGDEj8lG5TYcDutjefC91A9aIJXTor2zgrex1Ke8HUooCr7IgNYBd8iORiNYLpcwHA6h0+nA4eEhTKdTuLy8XAvShOhLDeanGAH0u1Abaf1Kn79NinjTyBk3oW81XrPIUB6Y1tJL3/KytO9o3rvoXDUFbYKHvyvtWNA8U2QIhUXm8nrE5I8U4C6FUJuHvqFI6Q/eTp4xZB0Hmm1ngVZHze7CgAJPR+uJjqPVOeb6kX9DJwL39/frICZOEkr1D8k8TdZg2R47vGl/iLbvNnwvWuaLFy8A4NVpK1qaTQNlBl1lXyLPUtimz1AKVn9VQ057akE+SZ7j2KX/OA1815uUrxU5fdtUcEuiKUX3WQP6tz7C2wNP/3psp23yjSRDcFEXfeYZ69SOsNrIpWCZqGyqvUvYy6XKTJHNWvxHQyhO6SkXj9/tdDprPvd0OoXpdGrqUwtNWjqOWD+m6jvpe01na3a6p51z7Y5YvpRGXOgXO2bcAw/9Hr6g8PalZx5BK1OKK/G+T6HLK6fpt5IdI/mGpeNU1niAh99D8sIj10rIc6/saQI5PB6iNUcu3u6MjSDFaSldviSQtEASvpMGX2mD66Y5WjkTHLsKzh8Uy+USxuNx/RuDl6PR6Fo+VVXVK++GwyFcXFzA2dnZWl6ScrPc1WAJjGqQhBv+k+5cLgm8J5cf0VpVVX1cyosXL6DX68GjR4+g2+3C+++/D2dnZ/WdmkifRZhr7dLkOJOMDsv4uGlj/21BCRkXCqbhzjIcGyEjWZpMkQxXycCN0XWLzcIqs0sZ5/ic777kO8tyQXk0Nz9LHlL7hI5Y2nVUVVXfaUWDDnxCFv/hRJmVT2g+dNdhu92Gw8NDOD4+hocPH8L+/j4ArK+sjuVLT5tB3qqq63e680Aq1iMUGGoK256IpVitVvDJJ5/AT3/6Uzg6OoKjo6Ot0nOL7cA7lmNpQnJQWxQh2dq0PE8wNOTf5AT2Oc08X49PVFLeeCccPPne4hY3BdQ+4Xq+3++v+f0pSA3ae/zxpiabNgUP/Z5YSapfEouNSHEdS1kxGxh3w3J6zs7OYDKZXLNRNZpCsPT3Jk93wXKwbXjcjacDyJv8KBXb57Y4+hXYT3SHY+g7/g5ppL9DNNxUoA9J/TYqZy0nEEiThKljQ7PtON+VWugZ4kPteewO8VCcwZL/LTbfNupkrEe5v4mTXIhSdSq1GoA6YppSkiZqrasttJUkFiMn5lA2NSmcAknZWemKGWU036YxnU4B4JXx1m63YTgcQrfbhdFoBM+ePYP//J//M3zrW9+C3/u934PpdArn5+fQ6XRgf38f/r//7/+D7373u/Df/tt/g8lkcq0enL9yeLhEe6SWZeE7iQ+07zBgu1qtYD6fQ1VVMB6P69WzaERqjp1GS6nx4Z1YkCZkKWK/tWeYN69XKVm4C8jRfRqfetGUXK2q6tqCh9CxMbG20IxOy5i7qSipF7iO3kQbhRyTUhOYWlmh4IZFJnFaLbRsmu88MpHXQ+ob62RGaIxantHn9JhXqwzDUzhwlb9EV25/cJ0m8ZWUP530jUHKgy4aiNEVyzv0vEmdSWlEHYC25mq1gk6nUy/IWS6XcHFxUdtFAJsN4nGakSZa/p07d+Cjjz6CxWIB8/kcnj17VtvCtG4A4XbV+MhL4y7D6iumQpNbofSeCVotP6o36Tf0+OLcupXUh9p7i5zFdN4dOU3akrfYTTQhv1L5Z1PyMWaXa7KdLpxL9WNDfp9kS4Tsu9QYojQZuIk4rlV2cZroO+m9JT6J6bR6WuIU1jK0OK2UXqINgcfb4iJDvJ5M44GSfSjR4/FnPTSE0lJe9fgnOWWG8rf2J04o8r7KaZfQ+PHobs2mym3LEN+HvqN/x77TZDJ9L7VzTBZItqE2zqxxzRjtFtByQnHaWBkxeV9a9+6ir5Mir0rEdi243Rm7ZdBO0+5elYR/TCFozmjIOVutXt8xk2N0e4ytWL5UUMSMJ4vC9iosa1rtW4mGHHCaZrMZzGYzaLfb0G63YW9vDxaLBYzHY3j69Cn8p//0n+Af/sN/CL/3e78H4/EYJpMJ3L17Fw4ODuAP/uAP4MmTJ/Cnf/qn8PTp0zpfjXap/S2GQU778X6T7mSi71NA80B6Y0YTpkPDeDQaXQtaVtWrySy6ysvCmyUM6dRAttaekhFgHR+7qJR3EbmGdinnh4I7QqHJWEwj6ZCQntDGBG0Pr+G5K/DyvtZmtC0wmMLvwMN3qTRKbR0LgsdkZAq4TVQiTwS2nSWYtg29bwkQaTZRbIxo73N0aCr/4X3r0+l0zSbFiV1vfjG6LKDji67G1iZnQ+2ZeizZJgKiViB/4V3L/EQV2i7n5+dr92Lxu56btgFo/th3lAfu3bsHP/jBD2AymcB4PIb/83/+Dzx//hz6/T50Op16UYAW+KPP6K4UHviRZGJInu5CP28C3iCk9H0pOjiwP3EHi7SQItavFl/IS6PFp9e+R9r4ZE5qYNuiZ0roy1tf4WahlI2Ez7fV/5IMl3iTjifL+NHicHwSQAK1QThtlgmA0FhOtas98T7NPrXIMU0He+imbev1I2PtEauDlS4KyWacz+cwmUzqCVm8FsuTb4gujc+1/GNpaFpqH+bELWI6i+eVEpPOgeb/pCxIDPmmkszwxlX49zRvb7txPw1/SztZPTH/ED2YF40xWXia58+f8TbVdpFqYyDV3/PyJa8vylht8a+mZ6gesfBojn4IYVf8H49/khsj0nA7GbtFcCGeYljhO+t9XB6G40ZjSoArF7GA767kuWlw+tFg6/f719JOp1N48eJF/c3+/n59rB9iPp/XwanDw0Po9/twfHwMV1dXazsiUgILOTyzyX7idKKCxR0g0kTUavVqd+z5+Tl0u13Y39+vxwk1RGMBa+o0NDnGSjjCngDrLgWXb+EH8i46RNLR4CV4VnIO3jaesTiumwTvg5CjCJC+CAsne6hDI00+huiMtRnKby2gk2qIp/Jp6iSVpjtSnEFq0zWld7guTC1H0qnz+RxmsxlMp9O1ScDQEW5WoKyjQSVePi8jd8LpFre46ZDGd1NjImRHU+TKNa6LqK2eE4SLBf80OiQdxPP05JWjx25xCw9uCo/x8RFbhBqC5Q57er95k36AFJRv0t9IsW9524Qme615WsHlqTX/0m04n8/rxW94byylKWccaZNSpScZpPiXNCGklWe5bmQbCPU3lRXSrnbp7xBK89Wu+Sgo91D2aRPYobajk/6x+vGJzFgaqbzY9/zb1D7UbFle19T+3JU407YRiolvEmuTsbmFe1dWaHnskrBoGjQQlqtgQ8o0xUlNVc6lJ2B4sNAqbC15UmPgpk4yVdWrO/UWi0V9fxvFdDqF09PT+qiT2Wy2Nnnf7Xah1WrB5eUlrFYrODw8hF6vB91utw52IkJ8uok28QZ6JGPI27/tdhuWy+U1h4wGMkajESwWCxgOh2r5vO2sE6Mp7WmRxU0ErkL0WMbtLoynEFInUVLKsRiOIeS2qRSk47vEqO6yjiuND0O6sOlFCruCUoHjUnxK86O7cWkZHFLZUh/jc/zHA9LWQJE1gOKxDbyQFhPE0nr0UC5i44eOOU9dvLDauRK9/DfuxER7BtOkTpDw8gGur8rX2lFqs9K6bNu6kS5i4LwSanct+JcKK39KgR0evCjZpqXruSuwyLKYTNsE727CnqX1lfQb9QmseaXSgNAWn0gTGwDptuS25c8ttoOmxtAuQrOfcydjaV4I7lfF4m2l+8Grs5voN81u18r3xCxTaLH4ESXkdkyeLpdLmE6n9XUQUj7eSeYQPaH2t8SBQz59KAYWog0X6fJ0PHYbg9Vmp2VIbePxHyUbQOq3mE8Wg0abtZ9K+3ZetFqterMLgL6rNhZrCPGflUaLnWt574lRe+MW/O/Y3dOhvLaNbdgBMT6ivz1zC6FnVuzcztjQRM8uG3EhhAacVTDnBFetyg/fhRhWU7JN4yb3/yZAJwUBXq2mw8vjP/74Y/h3/+7fwe///u/D7//+78MXX3wBz58/h4cPH0K324V/+S//JXz88cfw7//9vy9GS44xI+WRmy4H3ECQylwsFvWkd6fTgbt378J4PIbLy8skektPPmmBmdQyd0mpv6m4afLOokMs34fev4l8J7VbyKDHCSiE1yC3ImUy3yqz0ZHAb0ITKp68JXpwh2On06nbrsmxxfuwqckfDz1U/iMNuCCL32u+CdDV0E2CHnscmziyBnksE9va5HZoXIcmxDeNqqrqhX3T6RQODg7g937v96CqKlgsFvD555/DL3/5yzpY+POf/xyq6tWCChxnVh8hdKpPTAZhGbQ8vAu21+vB6ekpLJdL6Pf78ODBA5hMJjAajeoTZNrtdr0YkfO/dDw1pZOOK6n9KP0psnQXsA19W6qN6DjkOrKEfyLlFQPnGXqFSUxPeAPQ0t90B4oVt773Ld525CxooBO6mj4pIWdjE4gxHRpDqnxEcBnMbXtsh6ZoyZ1gjtkoXnq0MnAScj6f10fnow9TYqFhCNS2SeVJS0zbq8dKxZutehv7WlogLNEXQ2jsWcH5z6OXQ8d7l2jX2F3aIaxWr49/1xZQNgWJv2Llx+pXgl5pEUOszNvYbRlsy9ZtfDK2lBN6k52BUnWPDaKQopHyCKXhgzvFCCkdgJDoSsmD0mRRaCnOepP5cNA6zOfzOviAODs7g7OzM/jwww/h6uoK5vM5jMdjuHPnDuzv78Nv/MZvQKfTqYNPeD8F5oM7Qz3KX0prMdJieYTS5qThvKWNl1AeuJoRA5l0RzFHk7wglWOFdTIlBW+SEZAi0zyOiUV2ljRavU4BfqPRqRmSfELobUUoABsLGvDFId4JHD7pK00eWoJPqbo4NOkktUtuAILqMcqz2l2glA6JRnwm8XjMwbfSK9GRgpAOxvy9x+OVGrua3LPIPHTcMWBFd66kyFmtLI/tLbVtaKIu9rwpG8EKDADiiSsffPABAACMx2N48eJF/bzdbsPFxQUsFgvo9XquoCpCa7/Qe4DX45AGcabTKUwmE7i4uKgXw7VaLej3+3WADY/Ao5NjXCZ5+097Z5WlXmhyvEloMj+FR0PjwCKLQvqH845ld5omd0N1lvSYxPu8HFq/GJ9Z6qhBkimxNpXyTuEvjy/hsXND+afkg++2aZNuu/zS2EZdctowNr61NADymJJ0Wci+DuXvTaPRqOmmHF1t9V94Wp4Pfx76Tnqn2QolbSepDa35e/qb2zT4z7J4MUUnaO8tdU2Vt6G01hiBRXfQdBYb3osUfkjVpZT/LHWyxGZC5Xnfxeye0HNuw+fSpsWfYvnljGfMy0JDqA2lZ9rYs47lpmGxKVPy3LQdYWlTD2/FvqfYuZ2xtwjD4qDFhFVKsCRECy27aZQcoJjXtoJdpWBtjx//+Mdwfn4Of+/v/T341re+JaYZj8fw5MkTODg4gKOjIzg4OIC9vT14+fJlcHJRgqevcoxnq1FmKR/zo3lKRrDmROCRiYvFAubzeZ0PHvEZq0eTyqdU/tqYeRPG0i3C4PcnWu8qR3CnS0KMj3Ac7eqdMjF4xgjuIOx2u7VM4cC7UPlxVhq0AHSIXm4zWL6zBKl4OZZnWln8b5ygC/FJKGgW+obaUZTOTchwLAMnllarVV3HlInrEragtzxpl0gMlPen0yk8fvwY5vM5LJdLePHiBQAAzGaz4ASHl06Ub7gAgi5So+nouOA29k0MsldVBYPBAKqqgqurK9jb24Nf//Vfh/Pzc/jpT3+6bfJuDLTJ0tI26y7wmDdYmwMaMKbykNolOTTwvtKCoHy8x+TvcrlMPtWCt2/oygBOC5ajpd8F/tkmtl3/bZffNDZRvybKaEp/e4LxOflxhGzhmPxGOwjTeiZFaFppkrH0pGnJPLSJ3lLgbYQ2bOoiVIvP5fHZN42YbZRLtzZhhu+keB/dqBID7zfpBKgS/G6JpWA66ehf+ndq3DXXfl2tVrUP16QPmuLjhnYT09+lFiRYgLwbo02iq+ld9aWxq/KpKWxsMraE8/m2IzRhpU2MSkaRR4hYy7ypE5vckQfwTQym8HXpSewQ3bReFxcX8PjxY/jqq6/g6OgIut0uDIdDaLfb8OLFi1phzWaz2uhAJz5V4WgK2xuA8EwalIRnMgGBARc+5rgxFssrZ4IaIbWfJ19r2pss129qwJwjFJjLkU8peVgQyvMm6pJcaJOX9B91WnBX2nQ6Ve+NK0WLxfGT+jPWjyX4Kiaj2u02dDodGA6H9TGlkkNu1fexSRCtTtTWoM9CiNXNY6Nok4NeG8ajN6juo6D8almoxL9BO2U6ncJoNKpPBOETo5yeGL9RPS3ZzBaUSF9C96eAtg+dKGq1WjAcDmGxWMD+/j70er2aTj5B3dTkvjUAPJ1O4erqCs7OzuDy8rKmBxezWBcCxPogNl6swbKbBolfPXayh9d5WZsOLHNZHaLdwvc8DdcJXn8nRHen06nz4cFOb3tZ0jdhs3n045s0xjYFKYZjQYz3LM9jMmRXkKIrJFsvlT95bMoiY2KwyCprnEqy+Wn60v1e0r6wxFKt72iamM9jycvLLyEfwRqDk9rW20YWPikprz3jYbUK7zDmupj7AjxtDNQPitGtjaPQN1bw9k6Ja2r55iLEL9I7K+9o8QtL+bG235auovxp8RFL+ZGhmMZNxib5PvbN7c7YGwqLMkxhtNzB+zYG0XcJvO3x7tjFYgFnZ2dwfn4Of/zHfwx/9Vd/VR/deHx8DJeXl2srtvEIQMwzp089hte2gpAhSIHi0BjDtsP71DD4pxnEUnBmW/BMvlqMmF3qRw2baG+P47uLbVZ6VV0oaIGI6RLcEbqJuyebAspWSx2oTAYAeO+99+DOnTvws5/97Nrd1NuCd6d002i1WrC/vw8HBwfwwQcfwNOnT+Hk5CSJZzyTth76PPo15NxrWCwWa8F4rPumdpZLOg6P88e7O8fj8dqEhlQ/5H3Uq6k8Ru+RBbh+j6K0+t1i12r0pNg/20Ks/Dt37sDf+Tt/B7766isAgPpuVpzkxLbz1CO3zrhrGfn5l7/8ZZ0vnYQ9Pz+H6XQK4/G4tnXxWy6z6CJEOn64rN52f20TKXVv2raJ+cXUbrdMpuPJFNJkJrWV0RbxtgnyX7/fh1arlSx7aH6r1Qr29/dhOBzWR3M/fvwYZrNZffS4BM329+jKbfn/b/M4LIUm2vBN6ZcUnuYLNULprBO+JcYW351vmTwPTUiFgvT0Hd1VGypXysvalh7EFqfk9HloEo/nL8ldz4kJJYD9SE9z2xVY+MWy0IjHHDTwxQ/YLjxNDFYbw5qfZZJ707sfrbySO5lN8ykxLkJ5SPaORYaXKDv0TSjWRdvlVo/7sQv12/hkbM5E4S1eocSEh8ewK7GqpCRCPCQF/iTaQoaQhl1I64UkxGezGYzHYwB4pXgwmHZ8fAzj8RguLi5gPp/DaDSqd191u936Wy+dMeNIShPqRx4IiZUdW9lGy5by5WV7AiOWFelaXWMGI6ct9Jw7NNY8Q/lTuiVjwMMnb7I+SDHiePuWpkEab1K6GEITqZifVedb9dq2An7bALYv8gMursEjUzudDgwGg7Vj0WOTBaltp8lQz0Riib7T+ERzRjCgjvei02+skOSp9g6fWfndYyPwvzU6MQ2dRJJoa9L2oNB0nFZujJ75fA4vXryA2WwGs9kMzs7OokeJNVlHHrik/4d4PiY7dw14ZDqdyNfshlDdaDrtm9R2kHa+rlarWm56A1YWOlKC7TfV5impP1LzkMabtT0l2afZ+CXsMFpOr9erTyPqdDrw8uVLGI1G0e+09yE9g3qP2+xNYRflFWKXads2moo7SH83VV4uYjZlKG0MHt3vLSeFFq+96UWufgjF6Lz2oiX2hL9Dk2cWHrZOFoa+i9kYIRot9pdGq5WGUDuUGCta3piXpKtT8tL6z2N/xnhCa9Om5F8s31L6PzemrcVvY7xlKZf7XiVi1bH4Mf9tGT88bWqMMPZtajuUQEyWbssOsMp4jqbb8nZn7BsKqyHnUTQ3NXgQg9eZt+a5K+2FO2Dpb47BYADf/e534cWLF/A3f/M3MJlMYDwew/HxMezt7cHe3h4sl0s4PT0VV4lpStVjaPL0TSMUMKyqKhq0i9G6XC7rHUCx1eXeSY1UlOZLbUKWvqdlvy2gE08WGUvbLtSeu4SQYyghNsat9S01qbctcGckZOij3F6tXt0Lure3B5PJBCaTSZ3u+PgYBoMBPHnyBBaLRSM7hb2TDZoskHYZ5UKbhLXwZwodubqq5LgOOTZ4IsY2EQpQAIC64jy2w/ry8hJ+/OMf19/g5Aa9V5mPsZw2jwXqaBl8Bwil5U0At++wzjftTqJUcF2tBQ35M+n3ruswXpdN02stzxogpAFtKmtCQf4UvqY6CXXebDaDu3fvwve+9z3Y39+H/f19+PM//3P4+c9/bs5X4yur3ZS6GGnbeFv9iJuGN0nPAZTntU23Dy1PWxxO7dlc+jztJekViQbrBLIFXr1rbZOciQcqw0OLgqyIpaWnfpSenCsdQ43l65mgC4HHSCl4vCFWx1C7WiffuL7L8VOl/EvllQIan7BMIJd477EfuH0v2X9ajFsrI9TmnvEjxQn5d9u01990NNmeW5uMfVMDFduAZ6Brg1nLlw/smxwEB9BX22iTcvybWL6bdhq9Y6eqKhiPx2vBs8lkAv1+H+7fv38tP7qLVgtqSgZGiTEdan+PsouB1sljPIfoLDlOQnXTyrG0ndaPse+tuCkBxyaRUnfJCcuVTx76QnzhSUPThgxH61jmbfEm8FVMTtCjmNFhxPoPh0M4Ojqqd8laZWCs76wTVzSwTfPhdoLFkZYmOELfeYMfuKOv04mbvB5d4uVBjb4mbeDBYAD9fh8GgwG0Wi148eJFfccqLb/p8WQNYsSCD/gOd8Ii/zcNi963BFti73bVzrYGozx5xdLwQJglz3a7vSYj5vM5XF1dwaefflrbsxcXFwDwWr4CyHoX+SsnGBrSV7vg+8baV/JptLGgoSkfgZch0RULnFnylb7Vyouhqirodruwt7e3po8on1SVfzEo9iM9NaPX60VtA6n/eazAUn6qvErxYW9RBpvqs1LllkLqREXOpJC3zSS73AJpPJdo75D947FXQmmt+VC5rslJT3uH/OtYfl5fhNqvoW88k3Ca3anpQ23Cz2qjWOxcK82etJ5xq6W1+reYVtL/Hr3I6ZFsIP6/VH6IxtBvWlYqr3pgiStqsPSZxxcI0Wflde+4D5Ur+Rc52GQcIVSuhYaQPI21gyetBU23UwqNtztj3yJYlJAngLqr4IHgEkq5BC0l02J6K2hdr66u4PLyEqbTaV3ewcEB3L17dy3vqqpgOp3Wu2tixhp/nyJ8tQCHFri3QmtbmlcuL9AABg2MbBObCrJbHahdDUhuE6Fg2DbAxxenJdXJ5XlUVbUmW6S8pTbgdzzedITqQydjq+rVDkBMf3BwAPfv3weAVxMQ7Xb72k7DVFj5MWYnhHiFOyTe+1MlWjVnqt1uw2AwqI/bl+ANEnkCcLw9UurJYf1+f38f7ty5Aw8ePIBOpwOXl5db3zGrIaaLaTt2u936Pk9vGbxfQm2ZIvusgZtt2qa7hhyHm6bHBSvtdru+Z/vs7Ax+9KMfwWw2g+l0CqenpwDwarEh8o/GE3Qy9m3rE0SOPYLfWgIupWQi9hPqylI2DILLHO9ikF6vBwcHB7U+igXHLSfr0G/x+pl+v79mM3iwTV63TAbfYjdx22dl+ZfHPnLhDYJLk61cnnjpkiZEYhNcVLbzq1hicakYHSFYYlIp8VI6KarFpnj+JSZGpTykqx1i2ER8wuNjeb632nPSQrwYL3jo4H4C7WfrIkCLfWWZiC0By/xCSC5aJ9ut8QXaninjw0qnNxbvRakY9bYQozsWe7GmtZS/q224E5Ox1k64RT48QUBuDFgVzqb6zSoQLROyVEmkKPdS6ZtEqO8nkwn8/Oc/rydo0eBdrVYwn8/rIzx4W2EaqRz6jhqeCNovMRnADQ4puE1/a3l5ZI1mzEu/JWFP0/FjF5swgErKUckBLBEsywmK3URgn/PgnWRg0L+bDkSlOA+p9HCep/zKJyL5GA+1w5vOO3wMWnQ3ymvcGZOi76161Nr+VK9SnUFp04IM3j5erV4vgMEg9HQ6hel0CgAAd+/ehd/6rd+Cp0+fwldffVXvOLbIOC2IJTnQlMc97RRCyDbh3+LRmACvAv/7+/viMVzaBJT0jj/3QtL3qZPCtJ9xQiPGmyV0l5Qft3H4s1xYgopNgO44Xi6XMB6PYbFYwGKxgNlsVtPkmQynbSa157ZkOpeTIZ6XxiG2g/aOB5A9cnkT0GS71weU8k2hQ+KT1DGl2Vop+ovSoaXledPTLDqdDhwcHECv17NXgNBtCbxSoN7jd4fHAreIUFm54zXkc2nPd2nM3OI6Qnp3kz5HE5D8l00hNAYpTdx31GwiKst5bEbyO6RyJT0XkwmajvHE3eh3tEzJDqMI1V9LFyvbY5NrzzyTbpb8pPdNjKNYe5fIO1Qm/V9DLB4oxSXxXUweUV0co5eCx0wlv9Lrt1t8R608b3yA5xvif15PLX2s/ahc0fzwkM0uXR3D0+FVN5gOFyhIvKDZ+bwMni7FhuZlWOzPkr6Uh16PTZeaDtOmyExLLNFqr1jTWLETk7EaSgWBdhkW48UbtCgxELmi9RjSVoVZ2pi1OJhWwzKWX4yGXeTVkOKazWbw+PHj+jcGqfE4Y3rUFeWH0B2ANEiH3297TMcUOXVmtMCHVA+LA6Sl1/LVxtEmxw2nx9JfsT4O1fFNgeYkW76h0PrcwmOlESrTqo+0oK9URgzSQpA3FdTpkxw27nhYg8deePWkJueqqjJPxFmD4Nw5xiMaZ7MZzGYzWK1WcHBwAAcHBzCbzeDLL79Udwx5nabQOOf8niJTpbx4Hpq+6Ha7MBgM6p2CTYCX7dEpCEs70PyRz7FeIbszR+dovLBN2dO0/McxhO26XC5hOp3W9xCHTkuxBlU5tmUDxAIcmvzieWh5A8Ca/Sy9z9GDTcKj5yV4AxramC1dZiyIWMofBFjfRdbpdGBvby94QkOpQNpqtar1nhRg2oYNGUOsjXODmbdoFpI8C9lDuwLrpI40qWHxZ7yyhn8fw2q1WjsFQPpWy4vrJk+Zoec5E2WW9Fxva/FG2jZSftw/5Wmpn8LlqEVXxGKosTspNUgynb/XaJN4xGLDazEKr+1uhcUu0/KNlYd0p96jHvLDY2WWGPPWtB7bMjamLdB4XbKBaVoLv2n+utfOlIAL2DGt93oSqQxrHMYqCzX7jeYRk01WeOsem+9pGinlenRl03bLTk/Gvg1IMVhKGzlNoZRT+TbB42zmtG9VVWurs1erVb2KmoIqUF7earWq756jR2F1Oh2oqgpGo9HaiiRqdFoNOE6zNkGaA8yPOzMxWiyBg6rS736y9rXmTEj5YfqmYHUeb8d9uC3epnaytAP+jfwVm9x609vNMoZnsxlMJhPodrtwcHAA3/72t+Hi4gI+++yzImWXbGMq/yU5bu3TGC/R57PZrJab5+fn8Fd/9Vewt7cHx8fHMB6PodvtivzlCSwAwLUdbxIWiwV0u1348MMPoaoquLy8hKurKzg5OakXPlkQc+hC74+Pj6Hdbtf1bgKlxqYnj+VyWdsf8/ncPOFsoZU70zxwkDs5xfPnR+Z6eXEXA91WYGAMj1uXFmtY2h3TzOdzWK1W0G63YTKZwJdfflkHWBaLRW2n3mJ3YOFfelc08gwuGt10QIgufLLQPp/P4fLyEubzOUwmE5hMJgDwWrZYg7r0G17nqqpgPp/DaDSqFyQ1hablze34vNnYVX3k4asmedCix0oE8S3fozxBGyQkN6RYkKUsydbR8swFzV+bgKS0cfD2SKEtNmHc9Pig9ZYm3eliO0+e2xzXJXheykNqA83etLTXpmM8kg3SlO5PkZ/eCUdaF+RdLR4klReauMR0aD92Op3gwmyLfRfbHJAzbkpMNt8ijG23342ajA0p1KZW5rztsLaVZTWVtCrGW05TiBlrm4RnQjYnX2qcYVBBMhw1oHLBY7j4ZCxXTlYjWHpXcnWRBZZ24O3F+Rzfh/qTvivd55xuy+ofCyxtI42n0ATbmwxPgE37timaELljJ5afFDTE5ymTrVKb3oQJbs+EY+h5VVUwm81gPB5Du92GXq8Hd+7cUXdgeWhL5U/te/obJ85CeWlylae18AgGG2azGTx79gyOjo6g3+/DYrEosrM6FkyjbVJVFRwdHdW/cdcS5sPbsqS8Xq1e7RTudrsqj5SSy1rfWPP36npM5wk8SHrZSl8pmUyDATl5xvR8KvgYpDtj8W5qPolkzU8CDYzg5Jr0naRrtDbAfl4sFnB5eVmnT71Hk+efKmtpu+2ivgrR1aSejY1BzYfk/R2jL1S/2Pcxmz5Uxmr1egHrarW6tusqpPtDPM7pXywWMJ1Ood1uJ9Mp/S3J3Vg7Svmk4m3yG24yYvZbDE348qF4Qsg/t+Rdmj4vXamxNMkXk+RJKE9Oq2b7WemM2X85bW7VLVVV1TZILN4R8u+1b638HSrPCy0v72k5Vhs+1X+J9VGuXtHysI45y1jQvg0hx4YO8VYO3SF4yrHwcWjcxGxRDZZ64QJQTc5xmSWlQxq9Pq4HKX206ZhYSPZzmpooK5ZWk9VajGmTuFGTsbuOXXWsLbDQnhOsvSntctPozQEGAnBV0GAwAIB15Yfn5iN439N7xSiqqoK9vT1YLpdwdXUVFHbeti4pzJsWuBbj0UIHXYFvDViljNcQSo6Nt2FClju2Wruhw6cd5Z2C0n2vQXLgtSA3TsTxY0W1tCEj/G0CBm3Rce50OnB0dASff/45fPHFF/Brv/ZrcHh4WKfF/62TJJtGE3YS5kd3S6F+wyMhZ7NZ8h2lHjro5Bo6eTh5ZQHKAX7/LEDaeJ7NZjAajeDs7Ay63e61CYBNjDGqk0oHBQBe2SmaTJHGQSldptlG9OgzOulGZdpqtYJutwu9Xg+m0+naJM0uYDQa1bSfnp7CH/3RH9Xj6vPPPwcAqCdoYwE9qZ3omMUd24PBAPb39+Hs7AwuLy+LtQW/D8oaOKF8y+/dpHqO9y2HtItz1yCN0U36tKEJBj5upPSW/Gke3LeRyrMGSLVgaCr/Uv1Bdy9Z6zyZTOpduABQ3yNP2846oczr4Qk4YtqcQPMtbrFpbGISNpZnzsQOT091rfQc31E/lILbMyGE5GEurDFK+ttafrvdhn6/X/8ejUa1TRaCpCP5blptkoe+L81ToQms2MSjNf9SfWuxM7ST63LbLWSLeWwNKa5H/y5hF0jY5RgJ2rxa3TnfS+1N247bHpZJXgmhhZjULtTu993lNt80bmJb7KKt+cZMxlpXY9DnuR3SpNGxaZSaYAHwT9Z6HN9ceGiU0nI6NSNiW5O6MaNGMv54oJD2R2gM8cA/wLqSQyWMAYXQcS9S/9M21Port515PWJpPOCBj1AZ1vwshqjXSLbQFuN5j7EqBfssk5Y3HTFepe2aEwRN+a50+1vkUMwp1Iz0TQaIS8MirzRZSI8Pa7Va9XGHeBwhTujwsmJI0YmhMrzyxFKO9I01WEV5bT6fw3g8htlsdi2fpmwg7kxKDqqmc1MCb7T8qqrqydjpdCrKl1igvfRYi+nz1PKs/RiTNTnAYKYlb+SD0G721AmTEqD3wi4WC3j58mVNz3g8XnuHtMYQohHbg+9alfjUwyfUpqX5aXIqxbaJ5evto1I+qhepdY8htR6lbBGeF++PUDkhu5varzFaq+rVjtXRaASz2Qza7Xath6SgpBaAlPKVaOYLgEJ0WvtHk51evgn5gNQf9eZxi3VovLuLbbcJmjTbMJY+xV6VnpeS56l0SbEenk9s7OXIDy0fT/zWm4aXwctCG4zaYly+W/O2wMor1ncpZQFc12Hat5Ku1GS0Nw5koZ+XJcWLcscV17+UTv7b4z+E5I2F5hLyIscP99Cg2dG5vCv9r5XlHYearONxSSk+po0PKS/+7S4jV5bzZ5v2YSRIYzmmfzaNN2Yydhu4CQNrk+BK1hp4pn/vwqDwYNd4ICV4S1eGYTAMwVfx4zvaT4vFAhaLBQwGg3oXEs1/b28PFosFXF1dJdHoNYi9x/6lGN8WxJSw5W4WitSx0sS48rRTrB2aDv7fBGCdrYFtb4BYcjYociakuLznd7eFjDK+SKPb7dZHYWp04rucI3l3HXTM0joul8v6PkVcyf7FF1/A8+fPYblc1jvVSgWxd9XITpUZZ2dncHJyUo+HEjzEgxQaWq0WDIdDqKoKxuNxvVNPKx/1At/NIE2k4v94LCWm6Xa7cHp6CmdnZ/DgwQPo9/trO0hD/ZuDXXR2YpDsV7RtUI7xe4/5t7ngu9fw/xC/0+Ouuf12i1u8DZDGSGqAn0+KarLS62O0Wi04Pz+HH/3oRzCfz2E2m8F8Pq/vSMZ0VP7wE3EA1newcbosk5gx5ATjboKcf5vQlH5/k8B1u/YeIO5HlYR1N2pIRnG6qZ3AUUJ2SDaRdXFFDKF6SmmtqKoKBoOBuoguFuugbRuqJ5fjKN8t9OUgJJtTebmUjxDLT5ow89BM08buQOZ9gTpby2/bCNkjTcl4T76WWF/sm9A9rpb8OPgiz16vF60T978lcBndlH+4S7jJdsQu0n7jJmNzmNqiyEtOxmwi320gJGS0IDJNa5mc3Ybw0iaTOTYxaWyhoUT+vAxutKNBIt3fFQsSdrvd+jfdiaTVIdSudPyEAi+hNBJ4cFsrOxeWiTD+u6S8CI1Z7kQ0zduxlWQ83U2Vmx4eBPC3e25/5QQXtUCnJSDKv+Hf8YncN9GQlXib96fWP/x+78ViUctXPDaeOpLoaDYpV6RAieaMpdpCFl6Q5DmWie3W7/frYHhJu0ziYVruavXquNdutwv9fh+GwyEcHBys3cGpTbbyOobA2wlpOD8/r/PudDprxzFJfWYZdzG9touyW7NbrPooBqy3ZTzgwhOLw2+hqwlZiYsGLi8v63rNZjN3YCbG101C6pOQrYlprEHfpvpg1ye5cm0X6+8QQnot5BdYbHKvTSTpc1w8hgtlxuMxzOdz8eh6jc5QsJr+r40xiS7+twVWP0EbO5vWCbs6bm6xeb+uicmKmE29K/zHJwvo89h3AHG7UJJDofykyQsPbSEarHE8AKivzaHl4wI8y0SpV0+E5LjWHtYJLK/9qsXuYs83NW6bin8B6AuuPPmljG0Pv8T0K/0/h5ZQ/hb/05JWs489Mjlku/N8LDIKbRT8Ta8h0fpJs69CstUrxzi9UjnchtuWnpH6bNM+i1aOV6dsGzduMrZpNKVodjEoVRpvUh03bXjsClar9eOt8K4tfjcYBhY0VFUF+/v7dSAP76zLCcbT73ZlksZiNFvr7AkINzWZESqzBCz070K/lkIJY5kHtkoHe5Evcu7TDAU7Q32NzvB0OhXp2vVg9CaBbcDvLqTAibZWq7V2SsF0OjX3b0xOeIId2vexO4NjZdPytXRV9Xoh0WKxgMlkAnfu3IEPPvgAnj17Bk+fPnWXbwXd1bparerJz7/8y7+Eu3fvwve//33o9/swGAzgyZMn8NVXX0G32613RkqT5x7ZT79BWn7xi19Aq9WCXq8Hw+Gw6J2cXmgOtTSZ3kTZORMSGqie1xx+jtlstjaxuSt2DaKqXu0aWS6X8Nlnn9XPUb54Alm8zfE3PRoQbUwpEJrbT9I1GdrVGVR+IF00Hc+D6lELDVKZu4RQXZri0RzfIJQngGxDSbrEUy9pXNPvka8nkwm0Wi0YDAawWCzqI75pekwbCiBZA7kSj4boDD3Tvo+VH7IHPXnEAp63uIUHIfmy6z6pJB/oGNFkB9VfEnImHfG3dGJaTnk8f4pSPiGeVMN3300mExiNRmb6YhNWMRtQ+iYHWj6hSaZtg/OwtS1KxLM0W5P7Iil2LgBcsyFzYTlJL2ZHNdH/lj7z8l+OPSjZYiHgqWP4bSieK/0NYLOzUrFrY/YWZfFGT8ZqRgoPmNwyeTp2OaAQwqb6Pje4sOn2lQLAPPhPDSY0NHAyZzKZQLfbXZvA7fV6ajk0PytiK59CClL7jtdPyytEd+oEKaeDlxUK0MToTAnySsH/0uOEBt6ld5yWmw7aphp/eusaMxY9Mg7Hsvc7LS/Lt1R28OAbd15pWu4gpbbfTUev16sXu+DuWFzVjZNwoSAvh9SmHLEJwSacaKuMs3yHbXFwcACtVgvOzs5gPB7Xq+NTJpZpvjGgwzcej2E6ndZ9RvOyjldOA3/P661NPDXpQPJ8Y0GOkF5oEnwCIZRO6qdcenEBBT1yOAZMd3h4WPfvYrGo7wb2BEdCdoJVH5SSv5xvc9q2hJ2/rcDVpuDxUVIDlE2h9ORtrKyUb5A+3G3Fx5p3XIXGREgvlOizmO7gcidko+baELe4GdDsMC0dT9tE+Ry59qjHZ02xobUJPY9P77FXQnGPEK0p+npT/ptmJ3O+k2z63NhOLI1kA+P7WF9INEp84u3z2PPctKHv0X/VYNVzPF2sT0KxNo1OL30xhGKbUlpKS6h+qfG/GA3S9034SN78+Hjg9cKYNcI6WW4dj1Y/3qKXvLorRV6l2re0vKZ996Zk0S7EnN/oydhb3CIXm3T2dxHSLiwqgDudTh1gnk6nMJ1O4ejoCKqqgqurq3pnrQSpbUsFz7x9xieCmiqHliflVwLeelgQCwClOGC7FNRrEh7D3OrAWdrL26bU+cntD26Q02CC1B50J6HmVHIZRPMtQfNNw97eHgwGg3pS7/z8/No91J1OR5W/NwHWwJYlLcW9e/fga1/7GvzkJz+By8vLejIsdXe4RxfM53M4PT2F+XwOo9Fo7Qh/S9AEIY2NTdsrqc7XJiZcrW2BafhpH3x3M09fygkGeHWlQ6/XMx2Ph2XM53NotVpw79496Ha7MJ/P4erqCr766itotVrQ6XSiwSKpXtuYDN91vMntcVP9m9RgYWpZnvwlfwLlPL+3LMV2ti62iNGG8E5GaT6G5Y69FB21yb6+RTMI9dmm+tPjL0jB+xw6pQlRj32ivUvJQxq/KTGTTY/Dpsuj+Uu76ayT0hKwvy2TniXteI9PsQuwTmJZ01rzTp08zynTUl5sgrVk33oWiMSQW28pPxr3iX1viR2gTZZq2yF43CUX3sn8TfpsWt/skk12E33YmxulcyA2OUEFbBMTGW8brAaMJY9tDCjOA03xRCiYV2LSJgeaYajRzO+gA4B6ZxYGut955x0YjUZwfn6+ljfNP1cpauloPWJGLz12LmfSVFshxukKfSeV62mj3EC51u+eSXTpXYoxe1NlssTfVp2UA6ksHnxvok1DvLBavVqNaDGmeR5avlr9bio0/dlut6HVatX3zT148AAePHgAn3/+OVxdXdXHIE4mE6iqqj4CutPpwHw+b9Rgtk5W0eexiVRJxnj7GPmNOku9Xg8GgwHs7e3Be++9B4PBAJ49ewbT6XTtnt0chJy1+XwOT548gU6ns3aUNAe9+xd3PVI54m0LnFScTCZ1u2A5iBT+0L6J2dNehzOHLj7RqNkAVfV6B3EqTTFbRsoXd7ZrgV9uV1VVJd45WQrSJEu/34fVanVtwUypslB2LZdLmE6na4ttSvNHaDIK+aPb7cLBwQFcXl7WR8tKdiH+xtMbpPx3GdIYtcpoK1JtVo+dGSqXj3tpooU/twTNEbTvrXTFbGWL78HpbWJcxp5JQHpCR0Ba80mloQnkTMa8iUiJ0zQ16WHJM7efmp7YskwgxPwrTQ6UmlCJ2TWUBi5/tXz4316f38pTmG+v16vtKNxMgHRrdpVkN2q6Kka/1U5IjZl4ULKNPflo/CuNU2xrjCmG6PK2k8aHoXT0Wai8WNzMCotdUqoM6blHnnhtJ2s6r82Hf1vGmhQblvJEm8bSr7wd6OKO0vZ1ie9LlN20n5b73S77ZRuZjG0yCFgClsmCXaZ/11HCiS81iDgvpubv+c5jWIYMlFi6UsY25iVN3PAjFfEdBiPoziJ83m636wD4ycnJ2mRsiI6UQK41gBFTzKvVqj66MqTQLbJN+zbUpx7Dnr6zyDAtbYynQ8YazSNlrFrGkxQwu2lyuSmjJVamxEOh/pQM6hD/aPlwxBY6IDz0Uj5oInBfCpocj/Ewvsd7GtEZuHfvHnzwwQfw8uVLmM1mMBwOYTab1ffvouxFOWaFJlu1OqU4FCnBu5DOC5WLgRaA123Y7/dhOBzCw4cP4fDwEF6+fAlXV1fQ7/fX9FwKJHlPy5/P5/D06VPY39+H+/fvq/lgv9GgUayuFpqQP/j9lTmreiXZINXf49RbdLmHNg0SbVJAsURZGiS7KhRUpWNaopHbLSng47rX69XBylDQ1wvMA49Yx8UC9F3JoAUiFKRZrV5Nxu7v78N0Ol2755OXry1m4PYQrVMpPdWkDbRJPeopK0V30G+5rOJy1ev7W2yPUD+FZKI1mEjLsdCslafVPSWIKtkGXFZxei22R2odS6EJWXRTUCKWE0KTMpHyXozPQ76Gt1wNkq7wIBQz8fgV/G8tbykdTW/5JjamJV2a4mdKcT6aT7fbre+5xcVf9K56qT1Csi1UJwsvhWSKty9KfbtpcPtIg9V2ssZILW1hib3FyrbCyy8xhNJ66s7Ho6a7S9uNpeOJVv2t2V7S2JcmWqVyY7LQ0oalx663fSXZuovw1ssSX2wSG5mM3YXO2gUabrF93EQ+2AWaqbG8Wq3WJlXwPa5am8/nsFwua2OXAoO+VsPCEhTNdWJKTQyEvpHS0za0BDdLKwVOl6ZkQwY99rnFObEEVj1Byl0YF1ZojpnUp57AFEdojGwyuOqF54hklCvcWN51fiht7H388cfw+PFj+NrXvgYfffQRfOtb34KzszP4wz/8QxiPx7BYLEQZvOuQnBavMyyl6XQ6cHl5CR9//DE8fPgQ3n33Xej3+3BwcFC3U+rOWM1RDWEymcDz589hsVhAp9MxyQYLUr4LBa5KoeQEnrdcgHR9YWmLUECmyfq22+16d/d4PIaTkxN4+vRprZsB5LuQkCZ+NDMH1esYrMSJ412Xt02B7pym9ux8Pl+zU6ldJ/FCiXHW9ETsNie+NLuotM633k2XitJtmJoftlcOz3l1M/8WwO5D7bK9eovdhZUfPZOVVl7V/DlvED4U88CFWJb75bls47YA1e2xSTBOq0a3lL/0ToJVnsQmWWKYzWbiAjgpv9jEiNdnsMRK3jaE2gN5E/l+G+23jf6KxYE4TU3YaNax6JG5TUxUe9N44F2Ewb9Bmry2/62c2A1syvd5K44p5kgNcKV+e4ubCc15LBWsLL2SKDRpViovzE9aQUh38NBjGfluuHa7vXYXLW9PqV208ScFsCV6LZD6NWTweAJWGs+k9L1l1VSorFgaT3vRyeTYuLA6t7F83gRZHJtIlH7T4yGlNtImBCxBh5SJkpTJYk6blJeUn5b2pk3IlsTz58/h5cuX8O1vfxvee+89+K3f+i14/vw5/Pf//t9hPp+v7TBrAimLLzx5S85oqCxLYGo6ncJoNILBYABHR0fQ7/eh2+26ygrR7KETFy3Ro7e4A2aV0bStaRmePijVXzQ/riNzxmiObg+VHQpCWumV5FcpPRUKClZVBQcHB7C/vw/dbhdGo5FKU4hmiV8oL8UmFj31iNHAv9F0Q0r5dPGYVqb2rcTP9L106kMsoLLL+qqEbZrKIzF5rNkDqfou1xej44RCaw8u4zXbjf4t8VJsIoHTFvIRrf6JxRez+G6hPHc1CBlqp10ey01j1/uNIxRjiIH3tdUOpr9D/pbFjrXqGMsYtdab6zYrQjLKIhNyoPElvbpEeh/jZypTrYvRpfxi/eZtg5skgyyxKim9lsYylnPtE/rdJmSd5hdK9KT4evy3JybD4ZUnobJj6UvYprx8nrdUn1C5ofy9p05Z5Qnmn4KUNkyNMXrhoe2m2BwxvDWTsRaHAAWatJLcU04TjNmkkk3NnwdrbvH2gDsEGhaLBTx9+hTa7Tb0+31ot9v10Zqnp6fRSSjruxBS+RPv6LMcQWEpJzZWpCC6twwvNIfOGshdrV4dTY1BTn68p9VJfNtB+13jgdVqVR/5jZhOp9EjVflxpPR5Vb1eHIH9SHme3o9Rkvc8eUmG6dvIP3icVqfTgX6/D6PRKGqr4PjEfrTIIPzOA/5dU/3Dd/vxY81i8gb/n06ncHl5WR9b3PT9m7R8rjctNikeOU3vjvU4pVIZnU7n2uSap983EYDlwUOr3LCk6/V60Ol0YDgcQlVVcHp6unYv7yadPLwHGhELtNJneLwv/W7X4Al04wIFLUiSgzt37sD9+/fh9PQUrq6u6hMEQsH1mzJRVAIl6hbSAZqe8ASfculC2YA60Rso4zYale3eOliDxpZ8QnZjilyQFtzGJk+QBmsMRRpruyrDer0eVFVV21P0+oNdpblp7HJ/lQAf5yE7MRYP8d5FT+2dqnp1sgvXi1ZatKPYtf7z+GZSvjF6eFr+PmVCKfZcypP7t6HveRmdTqfOw0ID78832Y5IgcYDUnvl6M1tyWwrX3nSbxueMdu0b8ntuNTxhWVrd8OGTnGz8p8n1oppbgI/5OJNtydCeGsmY0PgCjJHUTbBSE0zZ8zBihk9mxg8lj6JrRyK5W/JJwfbMgJCgr9UgEkKsmBgbTKZQKfTqe8v7PV6puMzpT4PGfRW2nIQy4uPlyYmKSwKKzamMU0o0GsxDLX60nys5cbof9OclxAv8TGLBhzVVTQP/CaFv6ju8xqJnB6eb6jPNEfeM8EkpX1Tg+bYP51Opw7M0CBLt9uFbrdbv6M8g2loXpIusMiEEjrEY2OF+ljLNzYWVqtXk9QYXIsFpzwIBaTwf+vYonnR/g6VY4XUbzE7qGT7aGVZgmkWnpN0E+UPvMeeLm7xQOJhT7CNpvHIXalMzxjR8gzR7NENpR1qHoRL1XGr1et7YMfjMUwmk2t5Ut5cLpcwm83UYKsUxKX/pLRUBuyafrLITP73LuhaC0+kBM1C8JYnPdd8G208W5Ez/jz9SWXGJvznEA2bKAMDsbs2bjcFzcaPpbFiE+1qjSHFJoBKy2+LrUD1Sogumt6KWIyP06rxgrf/Y7agVmZO/IPn67G9aPuiPR6z91PwJsoYzc/P8QM935XWEx4ZYClbi+lQxHjV20bbst9S4kU87mWJLfG/Q2k1+Rqiu5TdYyl3F2xtDqu/EPvG01/edBY0bUNacDsZ+/8HVfZacDo3312Bh6YStO9iG9zCB0+AFIE7DujE63Q6rY/QDOWHYxDvNLOutPYoaY1uSxmxwGXJgHWTYyc0+WIJznH6cNVYbLfm2wpucIccEhrswjGAweFerwfdbvfa/UGYj7TbQzIk+TM8Yhx3OmMabfw1zZuUTk4DbcO3Xb+022342te+BgcHB/Dee+8BAMDp6SkAvOKFXq8HvV4PRqNRvQOaw2Lwp7R1E4FaafKElhEbCxr4Pd5W8DKlSZnY9wg6kR4K9sTAy7foUBpwpv/4DgJrm0p9z+UapQ3L9oxtPoHG645ltVot6HQ69WkCjx49gvfeew8eP34Ml5eXrrrwZ9IuMqQtFAxqalJOCuzQdxKdPD3+Te9Ws9Dq9S1o22Ef8WssOK2p8r7X68Hh4SFcXFxAt9uteRt1HZUZo9FIPH2A8/RsNoNutwsHBwf195PJBObz+bW7qPn3NxGWiUYP0M6n15tofVzCJ6fl0v/5315otKXoP2miVgrMeiYPvDQgqA+WQ0Nq+Z68m0CI56iPuktB0l1Aadldgh5EbDygjkR7ZLFYiGOS55VLmyUvukBImkyk+l3bEVuKXg7Lkb0hXzdko1r6zyKLeNtY6cTv6O7kFF/oVk68RmzyrYmyLGO/KWg8FUob45dNxvI5Sst4SU6hHJbKlkBPusmpc4mdttqmoxBdnvu7b/Hm4I2ejNUEn2b48CCQloYjNGh2cUBtmqZdbIMQrMG/0uWl5htyFFPTptCiGTt4tOJsNlsLwnY6nWt3d2jQxqU2hnMCQxKobOAOj+ZchPKLlUXTYH1C/cnfW+RUTD42iZTg0E2TIxJ4P3l4lTuRGAQCgOgqXS0vy7McfrD0mVWnarRwXnoT+MQCbAvc4YlH2eLpA3t7e3UgSXNoNFCZE3OYPfyhOVsSrH3pdVyq6tVxc3i/Jk4E8DyaCGBZd9ZwmZ86CWb5nuozazta9E2MNunbkA1OeS2kc63ldzodGAwGcOfOHeh0OvD8+fN6HMXqEpqQSpU/3MbAZ/R//Jv+G4/H0Gq1YDabwXQ6vfYd5luKv5uUr03nTYPssfrj5CBtO6lfKJ9KvKHZqm9CYDYUWI/50hwxW1dKH6LLkwellfooMdsiNxBK28Y7gWGFZ0x5gsWlyy6BXP8vlO9gMIBOpwOz2QxmsxlMJpO3wtbMDazHnnn43jverHyQYsNK9k9I1ln4MiRjaJ0ku8Brw/HvrJDK1N7zZ5aYSSlI/aPZQbxsqy2Z6vs0Ic9T6doUcmgK+YtNyHoObs9446gWGiXd5YnnNBFHs/rp0t/ecmMyW3ofs5889IR0TciusPKClq/0P09X2s5o0uez6LgclBzvu2S/7eRkbJMBVatjeIubgbcp+L5NpI6b1WpVO6/j8bh+tre3B3fu3IHz83N1l6yUFwa0Y4hN3HgEOg3I4fcYzEvdAWoxbCzp8ZvccUDbK9WwCzmS23YOdkVOSDxsbW+cfMWdjb1eb+2eXlzwgOXQ/OkzyQitqgp6vR6sVvIu9F04no0GJTw82rQ9sY2AIzXeLy4u4OzsDMbjcT0Zs7+/D9/61rfg2bNn8OWXX9aBRI8c19pYWrkpBTOszpf2PMSvPL31BAXc8X11dQWXl5f1AoZ2uw2dTidJv3nh5V/+bQ4o72Bb4N+8Pz3ji+ugEnTSv6n+LXk/aqvVgu9973uwXC7h008/hdFoBN1ut15AhihVLw28/ejdyLwf6K7RxWIBX375JfR6PVgul3B1dVWnsy50u8Xbgyb1VYqtl0oPt8elYD+VG7Q8DzxBeUvedKKX5k/bYVt2Fi/Xej+l9C1+bznKc9eDa9g377//Puzt7cHl5SWcn5/D+fn51m3iW1yHJBc0eCZJYryV6ofHaKiq14uHaN5Ut1snqEoH+L1Beo0ua9ulynLJf6Xt6T1umPoaWszDi12IT2wS1kUA3A6Xdht6bQ6JllCa0n0Ti/9ZJvJ2CaV1eEhOUX+TP5PSW8vEMR3jS6kcjV+4jLbGXfjvbduGt7Ahx7/aycnYHIETYtaUiY5YnvT9rgrKNwWSQJKEdmj1Sqk+yhGOu8gvuROX1ok8VJp4VLG0IymGkHKiz60Tg7kBIQ5uJEg0hPJMVbi5ilpqHz7WtIm8GP9Y21ga2yUMEU//Wh1b6Rup7UJBQXyuTXbGysagPTooeFwxl4uhftLSa3XcJlLHaVPYRpvgJAzto+l0Cj/5yU+gqir4zd/8TXjx4gX84he/qPnCOna8E6HWsUGD0JYAtzQZ5ymXf8PlCjpeGIThx5SWBg++e4KF9JhYAFjrUy5jed4p+lXrJypH6N9WeAJtSAf+T8sM0c1/82Pz6ARDu92GbrcLe3t7pnbyOtIpdg2lnT4PjTvsZz7J4w1Ex8b8JgIBKWPdA9wRj0dVA8h2iiUohry0XC5hOBzCu+++W+fx5MmT+iQYLvssvNwUvONV4xFJvuboQu0+7JJBKKvssOoo/q32jbZ41GPvhXwXiS6LbxJ6XlUVDAYDAIB6EW0Kz2rHllr6NccGLTG+sA1RTtCrO7BPtzGGN4Vt2vsp7VqCXuxzj4yWxr8UD5HGQSg+wGnRbBTN5pD8uhjdtFzpXWy8ctsxRK+3j0P964nhpugqr+0nlZuav5XWN0UWhezgkvaAVnZMR1r0ktc/TaHHWp5FBljy8ZQdkmtSOsmf5HxgkaMe+yBEr7ddUuwNCx/F5L2FtlxYaLjFK+S0y05OxpZCTLBisCb0Paa9xS3edngETczYmEwm9TFPJQO6NF3IAN/lsZ0aSODBvk1BckK1dCV56CaA9on0DPUPOtuhtpTaAgNENIgstRu+k/QdOvmeY2x3Cbs8lpsA7tqj9R2Px/Bf/+t/hffffx/++T//5/DJJ5/Af/yP/xF6vZ45Xy0Aldu+m3CiKWITWSHk7iTEemr15WPN4rTP53MAgLVAMO56jE0ic2c3pw+1/D150gms0M6OkjRSR59Oxvb7fdjf34fDw0NYLpfq/T6pZXM9nDLB0xQsuhXbLWV1t5aXRIf0zDNR5cF4PIaTkxO4urqqTxHQJgLxnQQ8mWI+n8NsNoPj42P49V//9Vo24+65drtdHyFNA9W7oms1OSUFxxAlAnicH0I7wXlZ0pgqZSd66hKbhEVYZYD2TehZ6HvatxY9Q79tt9twfHwMi8UCLi4uoKoq6HQ619LGyufy4ibZ89JuxFtcx5vgpyH9eNWHF7E4o+U73ob05BJpkjMUuLfqGUmGpfYltxVovpo8S5WH3sku6+SQ5b3H5pXalX9L++ht8WE1aDwd8plKtJlVhpXsH40npbGdaw+XlM9N8GjMjrOOOYs9aF004fVtY2Wm9sFN1623sOGNnoxNUZiIlOBhacfwFjJifWMJTvK8UsqPlZWSb2pgA0A3UqzKh6eNpQkZCFIboaHOA8QWaJNZUjop/9BEbCjwEWtTnAjTAssWwzHET7SuPNDf1BGEsbpzmmN8JuVjHUOS87lrDgu/f06rM8Crewrb7TYMh0NYLpf1vZUAebLEMpkaCoohD4ec8021u0dexb67KfAGtFarVR3of/LkCZydnUG324V2uw2np6drfIXpubzQeABgfUdLLFATGrv0GFxMZ73b2NoeqTqdTmTmOkpeO1FrW4tuxbTaMZJWO5TqFpzYDZWp5WF9ToPznuDfavX6aHaeXmqvXJtMc5w9ARtPOm/gD7/D/j87O6vHF955S9sqZqdtU65LaJIeb4AlhtRJs12zYayQZBQf1yGejo2N2FgIpaN0Ub4OtbUkP/h33O729p00oSLJ6BSdGPMnrEAdsFqt4Ojo6NoVGNYyqGwCeK3rNdvT26ZN2nicLl5Wt9uFfr8Pk8nk2oTdJuXoLsATa9C+QVh4IEXvWmIcIf0Y0xVUTuT2fYrPE/rGYrOHZIe1Ppb2Cfmd/HmofA9tnrRWvvP6Z6E8c3yONwWxPtL4pwRvWmmJ9VFuH0r8JcU6c/Omv1N8pVz95ukzy9i1yHbre26PWcZmzIbR/PoY/bwMqz5rYkxoZTWRrxUlyonpoKbqEsv3jZuMTTUmuCO3K5Do2QUad4GGFAHHUaIeb5IjZgmSeutqMbpSDd0cHogFQjENnUSgZdIglPROWyUqlaFBC9qkGLLWIH3IAKCBrpRxo30XGkNSgK/pgC2njT+ntNDADE4caHl2Oh3o9/tw7949mE6n9aSZFBzX2kSjxxPklPK1LijYBlJ4roR+2GUg3z1+/BgAXt0T2+l04OTkBC4vL9fSrlbX7ziSbJ/VaqXe9Zaq5yhvWndAecvSAtwWmvD7UrRYyufHylrtVpoe6dICcNJkguQwUpnOJw68COk4vtMo1F+0bphOmtiXvssFz1+jD0Hbb5PA9nj58uUaXXj3MW83Dt6+FnuoBEr5DpZ237aPomEX/CcveLCQH/FO34XglesWmrTnMZ0g2bfScy2IZwH3EbwBX5rG8q3Hr6G2Nf4+ODiA4XBYn8gQy0N63m63Yblc1nmE+CPWth69HqPXmo7LzKqqoNfrwWAwgNFoBO12e+ds5Kaw7bql+pk54Cd3hMrJKYsH9WP+vPYtlVtaWiq36Xf0nddn0uxJ/o7GSug7HkuJ1TNGBy8HQPZrNfmiyZqQTJXaLGaTWPOywOo77Co0+kMxLMv3nvJDfJmaJ0LiNVquxGsAso9coq9DY6AEcmxbiQ4rnVpbhmw/baxKsVtrnTwyU8Mm450hGjTcVFmD2LTvZWmvN24y1gvL5MI2lZ3Vedk0doGGXUKpYFzpoN62+kkzrHLya6Iu1vYOlc+dj5SJBIku7Z0lv1JtpRkEmgOiGbG5MnSXgpe8jovFQp1sCNW72+3CgwcP4PLyst7dJE2CSfyFd94Nh0Podrtr+WIQjNIk4aYZVCX7Pye4um1YdpbipIw2EYPH5VqDtqlYrdYngXOcNClYUqrPmsiPBuDp81R9w2WCxznk8lmiV/odmvTkgbyUwCTnT/o9353tyVuqJ8pF7JOXL1/CF198UY+n2WwGAFAfAa3lswmEgqtS8GiTdJWeFOE2RohHabvE9Jo01vB44sViUR/1rumCkI5YrVZrxxt3u104ODiAxWIB8/m8PtL1piHET1QGl5qEsPgJ/JnniOem7EZpooZPrOCEHbYVv1pAQulxnZLP3t4e7O/vw2QyWWs/yTbVykS9z30CS/2kyZxYWopS/Y164MMPP4Q7d+4AAMDp6Sl89dVXa4vdmjqh6G0AHzMp/Jpjo1I5ZpFt3NaRFmHTvPkzKS8rjdK44OVJk0maLWSFdYxp+tk6seUZ99r3q9Xq2uJo3l4xvR6jky6q2ZWYxJuCJmKfuf5rUwjJGLRxtJMprNDGXspY88RMLGPDM9looTMlvlCiXFo+l6uh+Fson1uUQUpblp6DseJmeoyFEQp84QDXjCFMo32bUnYu3jYjQaqvVxF7BmBu+zYdAC+Vr7WemrEuTSjR91bj3JPG0y/eoCYNRmn143JDo1Oqg7U9qIGVYwx56PHyAneyrXnEJiBouk0rTKlOnC76Ltb/7XYb9vf36wmCUNtwvsK76nr/P/be7Eey5DoPPzf3rLWr94UzHM6MyCEpLhC1eBPgnwTI0IthwDCgJ7/6zfA/4TcDfjXgFz8YBmQIhgHLhmVqgWwatASatDgiKc1ohjPT3dNLVXfXkvt2fw+Fc/vkqXMiTsSNm5XVXR/Q6MrMuBHnxnKWL7ZW68xkLIB9sk2TXQv4q0RVtlArQ2of+vs621FKCHCSfDqdFgsEfH2Kj6OQ9/XpLeoz4Z2cPn3rGuMxxOt5+WAaWYX/W+wBfUYi2kL8FYt8ElzlU1tokccih/buvCyaVvP9uKwAsDQZOxwO4ejoqLhXGSdL+CSCpX9IhK6rD1iQ0ncN9TF8aVKNF6nvSDYp1G/VyhqPxzAYDJaOcnbJxf/mafA3eoespHc5KbyOsNqBsu+i+YaSrab/az61xaaUjQ8luXyg92Pj+6XqA6FxDx1Dkl9P9SQex4u+BH5v8Sfxd9euHtd7+L63/G7N0/cuuHhnb28Pbt++DYeHhzAYDOD4+BhGo9GZfNfVV4xFKBEdCtczVp1viZn4Z5fOAbBPVHI77/KDfe9K5ZP8HE0m+ozkn8WmlZ7j8mrvQtNrNlbTv1J7xPi5rne1PM/l9JXD065SF6y7X6FBGheudwn1A13jkaeJqcOQMW0tA20sXehHEaoTqc22jDVfftpv3FfDv61+iY8rs8oZwlH6fouNm/E5bSLW0o+lNC497BtHZcdNmXGSEiH9xNofqkRIfV1OxgrQjDddqZ8KVXSWVXfAKsjMEJz3gEOsi8KyIIXxCwW/8w2JCgkupzykvS1tgkrbeiwRwPJuSIDTiTUpaHIZXu4UusaRNLmG5a8j0ZdaHhfBfd6gDjTto7xt8jwvvjs6Oip203Dwe4jpZ1oPtVoNZrMZjEaj4vvt7W2YzWbQ6/WK/Hj/cDkpoYRVSlRlC63jQ+pj69rnEFmWFSvBB4MBfPbZZ/Dv/t2/g/l8DsfHx/DixQsAOJ2Ems/nxW5ZbcWtFoBoRO46IM9fHg0eqhNDyJpQmco8m+f5mXvpXAixYS7wozT5LoOYnb5U3/BFJ1IQ7wKf1KCTBDyPWq0G29vbkOc59Hq9JR26CqxCd+DkCS0T7/+lZeOxi9ie6xCohiCUiEtZroQ8zwudCgAwHo/h+fPnxc7YyWRSpFsXPRmC0L7L/RyA8voU85Tuiqa2mvaNMnVN8+L+uDW2801u8H58HmOQ1inKQO9P156hupO+Z2idx7QRvd6Alh9bpm/yCcup1+vw7NkzGI/H0Ov1iv8x/rtEWoQQzBxWX46Ob1oWj7skOaRnpPwlWHQI+kfW6zy0Mrgs+L3v6HH+rMbDuNom5OQC6XnX75I+5u1Ov7NwL1r50jteRFu+jrD4F7h4t2xM45tQrxKafUR7i1eKcF99MpmY+25VsNi3EH4l1J905cPzivEHUtYjz4/GYanyv4QNlgUAAPELAavC0mTsqgPeMmSF79nYIMHnzGjOCU3zusG6WmQVxBSWxb+T5HF9dv22bitHKCnhg9S/fWPFWoeW3zh5KpVdxrCvcixyQoh+LzkLvvryyezK19L3aZqq6yeGzKsi3xTgxD+H63upb6Oz7RqzFmcTAxV63CIl2zUZ6DtZcB51vgrEvlcIsbcKYPnz+Rz6/T58/PHHUK/XoVarwXQ6PUMyxcjrIl/Om1xGGTiRp8mIv+P3FhsYK5Mkm/U5CVzPl/WBpXz4M5Z7ZDkZRvN0+dQ+4pJPtkjy8Wcoms1mQXBy0m4+n8NkMoHRaHTGjlrzt6LKcUEDfn6EnkROuvzXqlHWB+L5SP2jLEJkwHLn8zmMx+Pi+GPtGNt1s6Vcd5cl1aQ+ZiVHuDySXfGVbcnfJxtvJ6tO8MVg59X+oW2qTWaUia3KjM8QMj1V3dJ3HY/HUKvVoN/vw2QyKU5X4BParyOqem/NZ4jpg7Hxj6Qn+JiI4VyoTbbwAvyzL2Z0cSO+/ELGqYvzcr1XWVvNy9U42nWbwIitZxfWzZ+gKBMnZ1m2tAgrJGaw5p8ClvqXZKY8KF9shpwOT4vlxcjo+uyT3ZdnSDwR479Z8rG0pyWGDJWB6hrNzsS0nUVW33M+WMfAuuoXjtA+IenjVdgMTa5z3Rm7TuSGpFRXMbFThTFdFwNtDWhTlnOeOC/n7zzf3TVGXPepIFwkvwaLQcf/fSuTyvYdzWjSnaxotCmpzdOHlknJVgllSeUqJidCwZ3QWHlw9SHmg45urGMrGX3axxuNRrETifcPPCJxc3OzONoQ75fDZ6i8FNJOOT628D48LlMMISHVubRLJRWqsoXSd3z8uMbSutgXDVJ/xn7ebreh3W7DYDCA8XgMAOnuOOPBCH4n/V4VYoPUVTnf3A6FkGscPrLf8ryGmD6O74J6jt+Z7SvTJaeUB/8/tu3q9To0m004ODiAg4MD+PjjjwHgdNdIvV5f2mVdBqvSGfxO5rL95Lyg6RCAl+MHFx654jX8W/PBtLG4Cqxzu6QiOgHc+oS2H+5+mc1m4gRgDGgeKXYmaJOTAGd9vxCbJ9VPqj7JTzjw6X46robDIWTZ6STkcDiMLp/aWKyXKv27Ku0o1gsubtOOkbzE6pDCv9RsCQfGXpY2l/q6RvxaxyfAS9tHr/ug+VdhW3gda+X4+AYpjk8lr49fsnJMvjrUJn3PE+vsT3C4JsDob7iQOAautqmivVyxjmsyul6vq3xOija19mXX85bvYsqm6XwTxWXk8JXtKtdSHo83eB8OlRPrw3UywyXC4au/KvV4iI/ySh9TfN5GUypXcqJSY10Gb0o5XEGXpU5DlLxlksL1jOR4S89bJsxS9l1X0JG6PG6oXMaPl+2qv1ACwxdo0TJRcUrBh0bE+MqX0vJgTfvbJS/maakf1/eWcizPuxwrV7DE86/aMOL/rvb0wUoqSRNV6GjRydpGowEbGxswnU4LUtJnxKXgFv927coJgdQmVU5MroJMiJWhyvemZbhkoOBtQ+Xjxwny8WepDx8xLvU/TbZVwKI/+W9V9mtLsKv5h/S3MsSVRY/w9K7vLe9kCXx89s0XgPveif6GpA49uhrlxGNmMb1093YZQkNKG9OOFoLJR/wi+cP7vFRWFYRpCGgb+dJp4H1E8vPKvhuVsdFoQKfTgUajAfV6HTqdDgC8JGVi7O8qIPloPn9U0gchfqdks7i90XSXT0e5ZIgh/mja2PbTbGSonQxNJ9WzJW/0TaUjUlPphjJ1WabcELIb+x8eO4473nHx5LrwLOeJsvE5fzakz7ryD+0n1DdAWahc/DsffM+EjGWr3cfPFr0cIpM1FkXUajVoNBpFPOvTcyF6mPs21tjD59fE+HmaXbTYy9cBKXwel9119XMXhyilC+nfVmh9E22QpGPQ7vry8sHnd1nSazEFfq6izlLllWqsWfV4qH8XovM0mVz5WZ7xpV2l3oqxk74+Sz+72oL2ZaveCElrrc/kk7GpSa11Q5Zl6v2Ql6gG6+jEvOr9PBS+AF0il12Bj/a8pSztOQmhToaWPyez6O8uogr/oRMWEiTwfLgsZY4lrRohBFFZ+TEg5HfrhcInj3SvL4J/N5lMoF6vw3A4hE6nA2+//TY8efIEDg4OlsqSAkiaF94BxMlM/I7fbUzz1iC9g3bH6EWBlfhcR1tD4Ro3uKIYd8ACnPaP2Wx25g5JF9leFehxsRZw2VKQ1tr7xhJYPhli+hO9ezoUVPdT+8J1g9Wu+H4PlVPSZzwPvtMzJv88P90FubGxAYvFAvr9fjG5QO8DbDQaMJlMSvnyKQinsn3O1693dnag2WzCycnJ0i5EivP2E7h+knwal4/DfTDtPr8U74mnWqD93dzchC9+8YvwxhtvwJ07d+CTTz6B+/fvQ7fbhXq9vnS/bCxCJyhiJzM0Mll7FsvibWYFjkmunyQixUJsWcvk5aW2hXThHcUqyUV8jsYWkj1wQbpLM0SeGHJPiqPo86F16CNVXTg8PEwWi7xuoPoh5BmEiw+I7QOuyR20G/QEBZccZcHzpSdA4GfphCULZ6KVR/uyNLatsZKGjY0NuH79OhwcHMDz58/V/EJthc8O8DqRdh5y+Gzbq4yq/AkXLKeSaNc8ALgn29cFmux5nkOz2Vzql/yKkdFolCQWtcS5LoTaS9dO5jL6qUx+2rtqMXEMQu/01pBCllcRVhth4VxTlBOTtxVnJmPLGuJ1UoqIGCJWC0jL1o9LQawK6ziRaFGclrRVyeD63ZU2hkyuEpoTo/XvUMcglhiRiFf8PobcQbhkpu/sCtB4nj5o7+FKI72/jxTzjeNUkwpV6IqYPMvIkef50sRlGUgkHgUPCunkCk07n89hMBgUO2p4ftJY1HQRJ6to+TETGvw9yuRH8z0Pu+PS30i+SGQ+9hcpP6svkfp9LQEK7yc+PefybWL0r5ZnaF6WOvQRNC5w2cpAIvNdBBqXwfU3zUuzERZbJuWHf9OjF2Nsni+9tQ1pXnw8Ss/Qcevz3zRygi9UcLVjFSjT92jdUDnxnXDn7+bmJmxsbMBkMgGA08VA60Zo+coPJaBdbRir11xl1Wo1aDab0G63l+5uT1WvoeRC2XIs+ouXRfWQKxbiejskpuPxQsizmo3TfrM8x2WTUPVYC7XZWptk2enxxHmew3Q6hel0uvSMS09KZVvqlkOypbFIFQMBwJKdWIdYviq4fKqUekXSFRyavyr1cdcY4P1c8k95vqH6Q5JXGjNSPKfBIhNPL8G12CyE1+JoNptLYxxP4BgMBku6g/uWPlsi2Q6f/6zla40NUunki6IbqvQnqA7XbE1MzOayzdrvUjpXnpqeCi1PK98Vw2ncjaRvfLrZpR8tcvJnXfwS/Sc945LN9R6u8e3TuzEI6eeSHfL5pTH9JUa2GJyHbxMax1HwvhVjH2ifLjM+fGMP07jKeKWPKY6B5CBZHaGLglflPS6xGliIGJ8iQ7JKU3ouZWYl6WKNiGt8W/O0kh8aMJAJMQo+4mddyIx1QJ7nMB6Pi35Y9r3omMA246sDqZMgpZlOp/D8+fPi/tj5fL4UtCKhyycFrWODjjnfLml0IufzOczn8zN3muD74ncxgdR59yXuDFGSjZLnzWYTFosFnJycqM+7HCtK7MQGbqmBfcGyO8u1wzukvDLvS+vQVUaKAMIaEFh0uAbN5oXKrwV+IXlIyLKXd127VqZrzyLoc3w3vo/IlOCaaJHIcWt9Uv2FR8nSI4tDJgPK9vVQaKQH1Weo01Cf37x5E65fvw7j8RhOTk6WJmPPWy9f4mKCE3BWnUGfoWOuCtlC0iMskzHa7y6sarzFEkuoTw8PDyHLssIPdeUl6fIQfZjSP6oiTkSfF+0NQLrdMBcFIfa6qnJTPCuNXeqnh0zYufwSTQYp9rD4GbTvcbj4FM0v077nslnedWtrq1hI3Gg0oNvtwuHhITx48KBIZ72nneoPza+T0mtw8U3WPC5RHinubaeg7Zk6b1dZAOExHn+O9u2yfc/HQaScXMM4goOeNoeLERG+02B8/qJvIpbrzvOwyVQ3l+VMeL6XOIsQ/9tahzFcpg8xeuK1moy1zF7z713kdZnj5Fxl+sp+3WAN+F3tFmKYQgi9kGeqbs8Y4+tytK3ks6vvSitPpGCAGjVJOUryWAKZkPagslYFLchAIoTfGSE5IFJQJ/X3lM6YBVWMMZo+xXtQ4jrGMbaMF97XpWewjXGS+Pj4uNi1ROsmZFKAykL/DwENbubzOdy6dQu63S6cnJzAdDpdOkZn1f0rJWhbYcBAJ2RpOo2sKaPPpbxi8pOe4RPvKLuPRJT6bEqbJY0LXzm0/3O5JN3og+t9yjj3FkJM6z/4jvR/izyhiCFVqQ5y6bUQIl4jRel3rnwssmManHR09f1Y/VXG30o11q1psE41mUPa7zzA5QslxasgbSjJ3Gq1YGdnB7a3t5cIan7c5bpBG8dlCAqXLtfSuPwcV+wntWvouNR0Wqw+xvTaMcVWWVJC82l4GoxBuDyh+XPE+LGuMiRIdRdbnxY/6SJDs90uSGM2z8+eLBECqa1c7cgR0hbUr5C4BN5HuS6k/2JRxqeJSVsVycw5imazCY1GA1qt1hnbZ7F/KeMfKX62cEeS7auas3uVoNUnh5Uv9OVjyVeDy0dIMUZ9z2FMQmPzmLtiQ2JSV71Ln/GKK5ccuIGg2Wwu8bfS0eox7eOSkXNu1jxjnuHQ+qdkI31/x46bVPzbefF4mu629nnLONXS0v5Y5XtLPoQGdTL2IhOtVvgUsqZEfSRbKrxODoGrLWJIVws5qqXjbewK8rV+cp6K0vKOlnJCCBMpL54nOh8YvKFyWiwWxa5QWhYtTyNoadtQok4yfD4jF0M8aHlpaXnfov/zOsDvtX7nk28VOioWPkOqjV+pfixOJgW/T9Fq3F19j3627j7EPj4cDotdu1RuaTerpd3LtDUGz7PZDBaLBdy9exeuX78Ojx49gn6/D48fP156v/PoV2XsouTA47viGMTfYsuxklsu+PSI6zl63xTqW/q9NT+fXpR0GKbX+izt09a+w+2ARWaXnFVAG3vYBnR3O68nWhfaQhyOkHFXhkDU7vl1kSZWf4P3EYtfbW1DTIdHbvLjl3k52lj3vYv1XWPqP7Q+Y4H1ft6ThhIZyn/DvyVCRktfBbLs5U5ygNPJ2CtXrsDu7i7s7u4WOwTwXuJ1juG4HwSg6yH8WztpwxcjcL0hxVj0b94nrbEf70t8Z5ZEzMXEWDRO0Qgfn460jPNQHSLFuloZVPdmWXaGSPX1XYsPTcvx5Wkh2aR0+JmfyPAqIcYn9T1j7Sf8dz5pL9luX8zKx5HFFsdyIi4OQdJLdGelNlkilaP1S+0zlQ9/d9lCrRyaj/ROZcHtA+oJvD8dj+jHyVi0fb48JftCP/M68Nl7aqN8+oimT11frxtoW7ruTbV8Zy3PmgcfLy4bo/mfIbGB1I8peDxCj/R2PR8SC/jqWiqDtl/IKVp0R+xisRBPc7PadZd8EkJ4hNBnQsFtjEX343M+35mjzDuExrkpIb2jZgMkOX32k0N7N6k/uGTT+AMJrvhCs92v1c7YsqBB1zoH1xcZ0qCSJk/w6CAA2ZhJCGk3V9pYcu1VgFQvLuPvUpIUs9lMJDho/vh3FeTkKtvT1bekIxMluOqB5x8TvK4TLASP5Uiier0O7XYbrl+/DtPpFIbDIUwmE/VuUF6G6zcfeaU5aS5nTctHchx8Y9ACfBc83pKjVqvBlStXYDwew+HhYVDeqUDfnU+clunfi8UCGo0GNJtN6Ha7MJvNzhxTzOELBsuMX+m7Kurap09T/hZDJKZASD2vGpQIDw0q6YQIJz9C88TVzbgaejabndFFqepL0pn8dyyTfqaLuDjpSPPCo96lo8ZTIoYESFUurQNO4vrITZqeHmNPV8JfIg68fk9OTor+WAVwTODEsCUOCgG+i2WCi+ojbTIhtn9ZfX4+icwJF8k3Rj/CcizveUEih/lvZXR0bP8Mac8yZGoZvSS1u08ebk8A4usoFXhfttRJTH/gsYlWDj8Zjsvk+00jrbVYxvW+2hhAXSQdk8snjrh8aBPxb/ze4vf6ZHfZaAti/XILNFlRv9JJ2GazCa1WCzqdjrhwhk/Y+/qUT3YfnyTZnZD6WEfdfxHBeXLqq0vpuN32xdZWXtGVx6rbGt+1qqsZpPI4Ysu9ffs23LhxAwBO9eXHH38Mw+FQzF/itFLrpBg+JEQG62Sb9NnKR0l9vCqeRyuXln0RIPlmFr+gCjlCeFXpM//eOxn7Okw8uYhsiRg6TwL6dYCk2PB7/NdoNArnbz6fm0kIyVHn30tpqVwSmagRiKlgybdMPykjL9ajtBJRM1o8nRboxii8UGPjasdQOULkkn7ndalNyFnlDpU1hY5ZtZ7CYJsHzVKaTqcDN2/ehMFgUKTzTcbSPKS8KXx6wBVkWMgHrgtdz4fKjum4g4Nl1et12N7ehlqtBs+fPwcAWLpXtkpIclMSla80Rbk5qN6WJntwwt71LA8yNfmsiHXqYvKQnlkHH69s+auo/1XASlLRvq75SNy2+PoPXdwg3S9s7SfSmNDImNAAmY87qRxa3nA4hCzLoNVqmctxlU+h2fbQQD4EUp1y4ssnj/Qe/K7fFLtkY/0J7l+HTDr4iNqUek7Li8owGAyCicMQ4LjFhRRlJmN97+OzdZpd5d+XIVGt/nCIb0T1nkWWVG1Jx0eqfmnR85osLt/SBykWkcq1xECh5Vmh9U+uK139u2r+p2z+mp0NLdPXJtzOhvRjjYzkZcWOZ247qD/E68bFT8zn8zM7Yn060lX3kl/Gn7W+owu+PqD5S5JMHLQ+kIdrNpvQbDZFTsy3Q9IVD0uQ+gF9B832WPK6qEhtQ8pA41fxs/YM9z99flzZtrOMjRj45AawLerx6eQqQXUm4tq1a/ClL30J8vx049PDhw9Nk7GhtgjLd32mefPfNZ8zNs50PeeKkSR/yCpDFT7GefUlCRJ/xr+n3/HPXFfwtK62s9jYEBuiPev6TRsTS5OxMU7xqwxrgJ9lZ3cmXCIefCcsriyfTCbQaDTOTADUajVot9uVrAx3QXKsqw7WXhVgveGRbi9evFDbDpUXvSid5lGlfLS8VYGWh7tV6K4gK+g4Ci33IgDrYj6fw2w2g3q9vnTUc6vVWpqw6/f7ZyYY8FilXq8H0+m0OFKwTBAnGVuN0JBWbFvK0OSSHImQiQKOTqcD29vb0Gw2YTqdwvPnz9fO1lGHzHqUGCUsXHWCzl/IM6GyU3uGq2ZT5O87pisFyky2WL/35UVlWSdfTOo7sflIf0ufJfB6oQt9qG2x5Ed3w6ySHAB4efw7P1LchzzPi52+52njsHweQJbtF1gPdBcPL+c84CLxAdzBMtez/G/8XK/XxSOoaezWarWKenERxBcNSIxNp9PivvkQcEIk9Xim7eIjXSRY4m8KfmcZfldmjGky0bJS6RSXr+aT3+fj+drYFWOE1F3qPqT5CZLdl+ySVR48kYjGFFZ5UqJs/lI7a/0hVVuVIXpjJuWsZCmPhzQ9QHkF7g9xmTC+dNkQrFtr7O1qK6l+zmPyjduKa9euQbfbLRYBPXv2DKbTKfR6Pa8tCtXrvnwucb6w9HPp5B4OHHc+vp3vyE8JjYex9FVrf6a+aKo8rdDqDsvpdDrw3nvvQaPRKPTlYrGA7e1tAAD4m7/5Gzg4OIBerwdZlkG73T5zuk6ZtvFNxIbmXUU/sfB/vu8uGteaElbuRxoj1P+mdpr6+z5oY++820TdGRtqMC1BSUoSrgqEOIScHJCeiyEW16EeUiL2nehz6NzSY6SoQ2wh5lIoQzomXONDkr0MeH6STC6kdt75u7vkk+Sg+TQaDeh0OtBoNM7cRUmDGl6OJosmq0sO3p6pJ1xcZVvSxbSbpofK6KcYGaoqg74HrtbNsqxwIgGg2DECsHwvaLfbBQCAyWRSPDsajWA8HnvL1d7HQhD4yGn+fq66c+XP29uXl0tH4fNbW1swm82g1+st3QOUktQpixgn2ZUXHtcc04+teopPmljuaZFgIVAlmarwxzgBJkGyIaFlu0g412/S3zTPlDprlfpWKpOC1wtvA35cn1aPWZadSZsKPr8K/T4+bsrkmwrWtg7VmZr/phG+0rjDBUuWQJT7XVVB64/8bwkuspv7o3SBVqx+lcqg9UzJKH7Ed4qytLbA98FFe6H30/piWC4Hl0nz/aU+GBIb8HJcyPPcqQukeM0as2jl+eSWxrjVnyvja4RyGPR/Pp6qID5j9LDPnmv9LwRa/6kyFsT8LX6ZheCl75DKL7f4S1ZoPmoMV0LjdO13XpbVJ/TpKUlOaaxrttkF3/hI3Q8teldK0263izh6Pp/DeDyG0WgER0dH4jOWMRTS/ppuLdvnV+WjxyBV/aWO+2g/l2wK/ezLR1uczsux5onPuXgZy/MUZfoYlcOnj6R6tcpIvwvRKfV6Ha5fvw7tdhsAlnfCAwAcHR3B/v5+8RxdxMT94ViE9EGLXYwpL5UetvKAofnGPh9SX6uyNfibpDMsfh+mtfonPt1i1VWuzzH9MtmdsZaGW5WDkRKaQ8W/o0fmlinrVUPIO9F7YHEnbK1Wg0ajUeyInc1mMJvNllbf1et12NjYCJYtVRCq5Z2qPV0O87rA4izQSZzZbAbz+RyOjo6g1+vBzs5O8ftisYDhcAjT6RRGo5Fanos0CjWWLsW+inGJu7rr9XrR5+v1OkwmkzMEtPbuuFKIr0I8r35SNfFNCVncITMcDot6wF2HVCffunUL/vE//sfF9/v7+3BwcAB/+Id/CB999BFMp9MlUjNV3WE+fEeFFtRz3WRxGHkAjSQlJ5Tws7YCnJL2/+///T/44IMP4G/9rb9VBN88v1SBsBUoNx4jhjvq8Z1id4Rh4IH3x96+fRsGgwG8ePHC+VwoIeUaF7iYgBL6qccR6hOa/6qOmw7BefhDKW029tE8z5d8GwoX2Zta97jywx12rVarONIXfTBqb0J1oo/w1wIvqU7ws+sYM21SY9U6ioOSuNSn4Tvjy5aBeeb56a5J1IkPHz6Eg4MDODo6gtlstuRLtFot6Ha7hYy4u4XvFqsCUv4p2yrLMmg2m0sLtlIgz18eYwkAMBwO4fHjx8U9xXhXsY/ct0Aj2rSJgTLgfgr9XhujrlXpMe9vIWO4DPR/1zNcP9RqNWi1WsV74IR6GWiTGBI5uiobJ8UEvvJT9a0yts0nn+v3UP9psVjAeDwu4q9V3O3nI9Fj+oePywipe2lyBPkY/H48HgfXkyu9td+52p/rKemUKK1ucPyXeafYcYW2mXIlWh7c3lfRV7kPhf1hOBwu6Umejvo0XDZXn3fpfkl/0jIQr8rJF1UhJQ/ve8bSFpI/r/Eevn7uswc+nRpqky1pUV6XXrHkk9rXQ45xNBoVsrkWEmp1j7EGlRO/D4GvDiy+4HlD2gGeci7gVYbVhmGdYp/V+KuQ/KwISRvqe3knYzmJ+7ogRMlq9WN1zqyoipytCpoz5YLPKcP7/IbD4ZLiQ2LEWkc8XVWk06qJLFpm1f3EUodSGvobTvx0u13odDowmUyKyfbFYmEONEIcHFe/pKQO/iY5hJbyLeD9nB+ZxPPnQY9Lx/j09jrrE6vsmBYDRbzrkE6s0TRIuOzu7sLNmzcB4FR38DsFMRjG+2RT15UWSPI2jXHkYiZLNKKw3+8XxyAiqZ1lWXEsPJWvauIeQccGEi0SQmVBInY+n0OtVoNms+klRaz616WrKTFK05Vx4n0+Ce4W0+5LLtPfLfWu1QWvs1Rln5eOkyYxfGQAPucDt1M+OVz54jiSbCPXRxbEEiY8DzrW6ekHeKLGdDoV7y6qAqlioTLPaj4W14m0z6G/PJlMYDqdFrso6bO4CMxHRlU9jrSJCFdspQHToA8ZSrRZ8scycOHCaDSCwWCwpFct/mMsms1mMaGIvk2I3Uhtr8sSvZLstO0k36isneTXn0hpYurJp0urGEuW2ITbAjqpk2XZmUnIWAJKsuUWjsRS31p8FAsaL3A9eF68V4r+IfEpljJcZWvHWfvaLSSG5/K6uDZNv7reQasHi98SE1/h89y/t8LXTpYxo+lOV/7S8xhHI3cDAMViaCtfEtq3pf4g5RHTruuMdeRmAGw+TYgukN5T60ux488SV/v0vjSetd9pOtfvLlj1teVZ6XkaK2xsbMDGxoaoo8bjMYzH4zN+raafQ+HyzS3xpzVerhIW7kHS/WXjkthnz1MnWnS3a5e89Jx13FrlsaS12nIqo5Y22c7Yi4DYoK2solk1ygSn5wmcRGk0GlCr1aDT6RRHoACc1v9XvvIVuHHjBvz4xz8ujkTB+yABTt895W6DMrio7VAGrkkH3PmJEwF0Zcvdu3dha2sLPv3008LgNxoN2NragslksnSErJY/nZTBXR0xzjgNmlK2nyUv7QhGzQmV6psbsVe5H+J473a7cOXKFbhx4wZ8/vnnsL+/D/1+H4bDIezs7BT64OOPP4Z/+S//Jfz6r/86/NN/+k/FPGu1Gmxvb8NisSh0TCpZERLpzJ0yukpRcspdxDwHJ8YwoAaQd7TgOJ1Op0U94MRHo9GAt99+G168eAGff/750kp5mm8oLP3UMlnA7xH05YcYjUbFiQvNZhO2traMkoeROfRznucwmUyg3W5Ds9ksxnnMHYAW+XAi5urVqwAA8OzZs6TlUMQS2NLESUof7Dx0oetoXfw+hpxDYhTHH+6G9D0rpanVasWkEj2em58u4IOLEIupe+wLeKf3zZs3YXd3F9555x34/PPP4f33318iFV518D5Ed3zyOsC6GwwGZ2zNRQKdQEagz6fpBnzGEihftPrASaM7d+7Azs4OvPPOO3BwcAB/9md/BgAg2sCL+J4xcPk/q9APfKJDk4kidbtY3zPP8+I6D1yQ0e/3i/5VNTR9FOJHUd1PfQdLneJJJKPRaG1sR5V91TKZqdUbv6cN/QT6DI09U9nj0AmJGHLbpxt5TM19U42TsNhbyl3QY+ZDJ14s9R1qCzmvwtP2+/2lPkAXr3L7Y60LDtckzetgz9Ydrnaw2sAyk4y+iZ2yeiikT4ZiVf0XY0w6PvFo8TzPodlswi/90i/B7u5uwUPQcf/JJ5/Az3/+84KbRX4Vd8RqPldKG0bb0dKXQvmfFLJa441LvITE1dVqtWJhAMV4PF7ahS0hZLz7bJFlfIb42q6T+4ImY1cZ0FQBixNX9t1SK6BYGdYRIUYUHWp+j+hwOIRerwdbW1uFYcGV4aPRSCSXXe3hC5x9TrY0GcIhOaGp+khIf6uqb4Y4xbR9kTBaLBZwfHxc7JDFyR1Me3x8vDQ5UYWR521UhaNvzZOSq/Rd+QQx/eybxLiogYs2Nvnxu9ifms0mbGxswM7ODoxGo6WV7u12G2azGRwfHxe7qer1OrRaLfjCF74As9kM7t+/XxxVjL/hPXAhQaTWR3k7WNuE9wONvOLptDJCx8/x8XGxmILWN82P3x8SitBgbT6fQ6PRgN3d3WKlNh69gzt6NTm0tsnzl7tFKdlEn9F0hEv3a79pNrHqYA+dQjzeH68AGI/HUWQWLctnb2NgCcK4LOfph2HZm5ubS4sapElNrQ/53pnrEa4DpOe5DcFyNJvhez9JFg0xfUqSAUmFdru95CdobS6926ptIS0Xj3sHWL6TqcoJG94fqP1stVpw9erVYgHOaDSC0Wh0ZqecZFtSw9WWkv3T+mHsOAqRE6/aQHvz6aefwuHhIWxtbcHJyUmlfQzfqV6vF2OBn+7B4Xr/UF1Z9t1C/SCXfBZ7xX1lWibVd7E2g9ehlYSW0lQ9xjS/Q6rHsnY0VKdJ7eQC16u1Wq04VYmSyVI5AC8XNezs7MBsNismoqu6E53LzRETR2v2nffH0LiDp+f/eDrrGJTy1/q8yw6UgZYv1wn0XTV/i+cr5enTX+gTIDFt0QESR1AW1v6HPgE/HSlV/q60WuyWqtxLhKPMGNXs9yrbTONTrOn59yH1YRm/IfL4OAn6G9Uz9Ioa/H44HMLJyQn0er1iEsx3go6Fc5H0vMt39/lmLi7FCks7WDiOmD5QdTxd1j6Ejg/Xs9yeSpOTfOEXPbmE+nXalUWW9vGlLxOTaOW6xk2ynbFlyP51mCiwlG9xkLBjWXfm8PzPux7WAa6g4bPPPoOHDx/Ct771Ldjb2wMAgFarBbdv34ZPP/0U/viP/7h4zkUq0ny141V4PjHQ2jQk33XsFzxwCTUmzWYTms0mjEYjmM1m8Jd/+ZewsbEBf/fv/t3iuGLEJ598AsfHx2fy4GVQgrHMZJCLBAx9lueT57lzxTknELJM3k3lKl9bxXuekxNlQAkGfDd6Lx7HjRs34Nq1a/Dw4UPo9XrFTvsrV67AeDyG58+fF23Rbrdhd3cXfuu3fgtGoxH823/7b+Hhw4cAcEp07u7uwmg0KnbIlh2HdDeq5hRIJAgtmzu1IcQzhcuZ5qT4J598AhsbG/DVr34VAKC4E4+Wicf64h0kVd7dg21/5coV+JVf+RUYj8fQ6/WK3z/55BM4PDwMvv8tz/Ni9R1OwuP3riAnFYm7SuC9uPheR0dHMBgMivGSWi6XHaZjWyJRY/X5quuWBw/37t2DK1euwP7+fnH/sG9cSLYUiS9ahlS2dKw//k2P47TayFXdv0XbGXUevb9Z05c4wU2DNx/hWwZl8pjNZtDpdODOnTsAcCr74eEhHB4eLu2IqRpZdrqDBXdBX7lyBX7xF38R2u02tNtt6PV6cHh4WByBi7tqadukguSju4Jl6hPRMaG9J8CyzU212w9P48ETfB49egSfffbZUrnaPdGhqNL/jyHHOXEWE+fi/9a4W0tH29RKpPHYEH2EmDtCXSSoK461oOp2D6kvlCe0DI2IC4VPhizL4N69e9DtduHzzz+H0WhUnJbFFw3id1mWwWg0gna7De+88w4MBgP467/+6+K4b4xRq7o71hozYz3SPov8El2QztsLbagvfytQ79Ij7bW8acwmTWy6/GctnXaqCP3fFbtbdU1ZUh/B/TWXDkM//OnTp8UiHw1SfBgqoxZjWp/Be+/pMcU8Hba9xea6dF0ITxZbxuuEqupBijl8cOk87FMWLj3V+6zSB+djMIWNSZ3v06dP4f333y+ubUJIetZVdy677eNVrT6mte188QX9nBKp7MpFhvTOeCoLQjrVdDabibyiFjenrmtLGb7nfTKYJmNDJiNicF6dUirXOhhdxGIZYtaCEML9PGExBNKgwePFMNDGgAiNMj/yDwOE3d1d+OpXv1r89ujRI3jx4oXYLlId8vrkExKx78yDElddaDLEtLn2jlbD5krn6+su46a9JyXN8H7KPM9hZ2cH7t27B8+fP1+aXNOOweHy82CWy0NllAibVUILYNEhkSZrNXByaBWoytHXxika7slkUuxG2draKnb80bScjF0sFtBsNmFzc7OYgMO6xuN4O52OKI/WfyQZXb9xksX3ziF2x5XW9x1/HndN7u/vQ5ZlxRE3lJRB0gq/w52pGokf2yf5RCHqi42NDZhOp8VdRjhJw8tylUvHmDapJT1DodWxqw/QfGII7hBg/14sFsVRMKkn3WLkj9W5rrHo00W+8RSjy/jz0iSoRl6G5I9/0/JC+g6vI5SBH/1aZoKiCuBOwO3tbeh0OoV80mR+avCyYvs5/T+VXLz9KVEr9Tf8DvUnTsaiTY0heVMipOxQMjk18jxfItDpAoKy+lUa75jf1tYW7O3twd27d4txS69pCV2MFCKLBO5DS763lqeLsPPJ5IsveDr6O14HQXWzpKe5rpXGsYU0luJBiw+WAlxWGktTX06K3yztGCKv5f25DHThNNXFVH5XXqi30Te8fv06dDqdM8fDhkwEpILPN8d3w2tUAE6P7ZN28qbwITUeQwNeP+SLy3nekr2SytcQq/slXaD58prfJuXv4n+4bgQ4e6USwNmJXF6Wxe+L9TGtcSON67QYT4r5tDrkNjLGd3fhPH0ZDXyclJEx5XgJfU7jMFzPSf0dAJZO2KKgu8ZddaXpFp9cFn7CWndl4iZXXUqySTqF/kb9fAA4c9/zzZs3YXNzE/b39+H58+dF/nmeF5/L9JnQeJR+Du3TFj9TSyfZKZ+MljSr8h9CYK0n6TP9LiQf3p54EpPUbxuNBuzs7BTfHx8fF4uCAeTNLNifpVgrdCy7/HwXJ+jSdb46e63ujE0F7lDEknavA0Id6zw/Pf6LBljtdhsmk4n3Hr3r16/D3/t7f6/4/L3vfQ8ODg6KQCs2UAyFZlQtpEKMDKHvFuMoVAGXDKikZ7MZXLt2Da5duwbvv/8+HB0dFYThZDIRlaEr8LLIJBltbqhdxBF3lKTPPgcVSQdKqnFjZulPlFgqC4ueW7UexKOuh8MhDIdDODg4gLfeequYjKWgk7HokLbbbeh2u/DixYtiJT0em91qtaDT6SR3RH1Otkb0SflqjkJqmzSfz2E4HMJnn31WEEJ4TDACdynjBB9dQJMS9GhSBO5i6Pf7MJvNYDabwXQ6hU6nA7VardjhinCNYW28SHUa4ozy76kMqcaoBXmew8nJCUwmE7h58yZkWZb8jtpYYN+yoOr6sowfjbB3BXMaKcd/s7yfRuLhbz5dQ+XABU64GE7TQT6dFuvHWJBlGTSbTeh2u3Dt2jV49OjR0u5ZXm5VfYSS+ucBTXe4/BKalpPBrVYL2u12oS/xmcu4Jg44nuikTgqgD4P2bG9vD27fvg1f+cpXlnxLtJG+Y9FjIfm02qRK1aDlh/o/eZ4XcQQfF5L+4Hdixrxj6DOhMbQ1P95faLwQ0l/p+1RxogaCtjEnmZvNphr30QlcHJc4ifnGG2/AxsbGkn+I153M5/Mzd6JWgZCx0mg0oNlswu3bt6FWq8Hjx49hPB7DcDgs9A0/yg9RJU+FsSr6D74+IMWlUj1YYmUpX3xWg8WHofLwncguWPo/JZP5CQq0z6IsEvB7GmdpadEOWXyFkEkgjQd19TPJV+T2w4p14LFicZFkL8NNur7PskxcvIGxCC3P4mvH1qm1z0mxu/Z8DDcQA24PeTn8N1xYj7+//fbbcPXqVfje97535gRCfs9sTP2G6mGJf00Nn91Jka+rPBdS941YVOEr8EUXmj1tNBpw586d4vfPP/8cnjx5svQ7PwUs1G/1+RYu7px+57NbVt0QNBnLj3O9SMYkNXyOLW3MKmXAstYRZfoJH1i88z948AAODw/hrbfegizLoNfrQbPZLCZPaHpcBUsDtjzPlwIwKWijv/E8fXVvJVJdaaX+o5Ub2tcsRG2KfKR8kewFgOLIo+l0CtPpFD744APY3d2FL37xi0sBHu0Lk8mkCDxxNS5tT06O8/+twUjsuKIkh5aP1Kck0BWydMWgy+hI8qfQ2eugZ6hxREcRnXkMSm/evAnvvfce1Ov14rjF2WwGDx8+LJzQjz/+GH73d38XvvWtb8HXv/516Ha7BanvKpPutuTgfUvSIzxfF/izUrtyApSn5eklhBAedKGM9i44prXfQ4Nt+l58zPR6PfjpT38Kb7zxBnzlK1+B4+Nj6PV68Mknn6h5Wpx9PKpOqnfXWAoN6vBv1F/4fbPZhDzPl+69TTH+kKSntq/VasH29jbMZrNi53O9Xg8+ns9KwPFnLD6lZn9j4dK9IfWtkRRZdjppeHBwAP1+H65duwa7u7tQq9VgOBwWQS+Vg+sLbTzzskKgtQG/q0x6P/zdSjJYg2qXbqD2DsueTCbi8fQUfHevlr9Wr9YA3dLnURfOZjPodrvwzjvvFEcEz+dzODw8LAJT3KXnK8sXf1xEWPQpJcfpZ2536W/0DnipvDL1SEnCVquVlNixlo/6nOp0zUfhcQ7AWT2T58tHS1r8h1DiUerLEsEZG1/xfiH54rFxKc+X+iVVxPta/WD5VB5rfnhX6sbGRhEXo17N81ycvJHg8lFd/pLmC0v/+3Qd7urvdrvQ6XSg2WyKC3U5Go0GvPvuu7C7uwsvXrwo4qtarQatVqu4e7YqSO8k+YUW0B3OPj0aymUAvJw8pLtPtHJixpjFLnP4+gXXayG+uaZ7JF9F4op8+eP/d+/eha2tLdjc3ITpdKpeYyG1Fz0pTrJ/9F1cckhluMrXnpdkc+Xj42Kq0KXrBs0Onqdv5yo7hLNwPS/tgtZOZ7CUGxp7lvUHXDGLL04K8Wn4uNaeRZuFkOIIfJYeOZxlGdy/fx+ePn1aHD2O32s7lDVw3RPKj7h0gfVZn3xSnpLM3I+x+llcp4XGGDGcQwhC/XT8voyv7Co3yzLY2NiATqcDrVarGP+NRgPq9ToMh0N49OgRnJycODlF7XurPqJxOn+O/o28mHURNtUzvv7tnYyVggzNkb4I5EDZQWFxWHDwruq+rbKoitjxDQpfXbomYx8/fgydTge++MUvFvdaYUCG5dEAFo8kpTsa+G4pyZjEBhfaO6Fs0ueQ/EPaLHZ8+gyQRW6pTmm90h2ws9kMPvnkE7h69Sq88cYbRXvhkRqYD05a0LPm+cS69L4uB8z1jJVkofVlqWspcOL9HNOgkcIdia4jfXzvf5GDHC47HmdNSYLr16/DO++8A8PhsDhmuN/vQ7/fL3YA3r9/H+7fvw8bGxvw3nvvQbvdLiZ3NedDC3gp6LNSf5AMsjX4CAn0ad+RyqdptbylIAMn6LT7Q3BMa8eIlL2zj9ffYDCAjz76CK5cuQI3btyAVqtV7JItWw6f8LEGySF6lrYL3YWB8iM5mso+o77FsvL8dLX/3t5ecQ+w5T1DCb8U8I1Li4+xCh2Y5y+P6n7+/DnUarVit/5sNoPj42M4PLgvkUgAAQAASURBVDwEACgWf1CbSI/rBzi7+5K+bwzJqPkhFn/EFWD6PqMNDyF0uP3N83xp0kAD1X+aPL7nU405qvPa7Ta88cYbsFgsYDAYwLNnz4o0MQsgXLCO4RDSaxXg/Uvq53S8aDYV6306nRYL93gZsaCyzOfzYjcdPaJ/FfEfxja+ewYp6D1/0viSxo5ULvc1AMJ8bp6P9DxPXxZl7JLmp7n0ZSodYiGeeLkuXw/7LO6Ez7LTydjRaFQQYT5IcR0dl65Yyhr7Uh9W+w139nc6neLIYVyk60K9Xoc333wTrl27Bg8fPoThcAgAL4ltzg+kRJl+IXESrnel6UP6PqblkyTUd9TK43m4ZON/W7kES570Wd/zVFdp6awxmGW8ZlkGt2/fhmvXrgHAaSzTbDbPxB3cvnFd7bIzrsktl/30wVU/1nx8ujKlD7ZOcHEB9HcXtJg+Ro6UcNlv+r903RY9SSSmT3IZqI2xcBuuOJKmk3QWLdM1Hq0cQqhPjv4nrTctbsTJWPz8+PHjM2mzLCs2OoQeH+7yyV16Q8urLHy2kaaT4l2fz2jto658Uo/FGF0uycBjMC1/nz8njS/8f2NjA3Z3d2FjY+PMZhg8jQ/g7NHEqeMC32QvpikT17lkNu+MXQWJVTWqNOxSZ6yCkKwC6+rw0BXcSPDTs+45ut0u3Llzp3if3/iN34Bf+qVfgj/6oz+Czz//HIbD4VIA2ul0zqwopyhbL5JRKuPIc7iIjIvQ7wBsx7eh8/D222/DrVu34IMPPigmDlzg5ACFhUgKcQhjx73Pqeb3I0vya33Kenb+RQddFUh3wXzxi1+EGzduwM9//vPiCGKOR48ewV/8xV/Au+++C3t7e/A7v/M78PDhQ/jd3/1dGI/HUKvVoNvtFqu0BoPBmTxCnDFfeklfuJwkLS96hAcnY3zEqa9P5vny0X4+UFlSBNiTyQSyLIPNzc3i+GQJWZZBq9UqyLr5fF7sjPaBEhWh49+CPH+5+0hqE2mXf2rM53N48eIFzGYz2NjYWJujilPAR4hJQXRKYJvyY564jPTucxfpxmXFv/muCC3gsYLuaqfEghaMWfOnxKEv6LNgOp3CwcEB9Ho9AICl4xGpbGXudnXBlx/vY7yNkOwouziFg06EuPQXXdA1Ho/h2bNnMBgMoNVqFToS63MV8QEnhiWEykLHSJZlztihLHBClOpvetw3Egnr6n/x8Uf7EIfVR6kSoSQoQtJh2K+su0FjwMs9j5gb2wnrKc+XrwTy1Z/PJ/SB2zlLvErHMH/GV640IYsnELh8HYwn8LSmVH6rBKtOwF2/X/ziF2F3dxc6nQ5Mp9PC/vH4htsACzQCnadBgp4vlreMm5TjipdJ9awPfAcxz8P6PhyW+s7zvDjFDRdXf/bZZ9Dv92E0GokLyzFvaQyG+LB8POFzWptbfGgrYuryVQKOGdxEQBeLYaxgfeeLVjfY9vzEGnolhgbXHdQuWGJ4K8eXqr5dMRrVPVQHuOIl5DbwOfTrcXzz01JoPIQ7/NAmcn2YcmHoKqDxXK70ljxD4NKlVSNGJ2vpQ/Jx+ba8DfI8h263C7du3Sp0YLvdXvp9MBjAeDyG/f19GI1GZ3asSvlbxzGH5jNInDueZiUtTnCNUd93CO9krI+Y1dKWQap8qnKey6QPURAhTvRFM84W8GBfGoh4NyAOKLxPtNFoFEcv/vmf/zkAQEHG4C44KQC1tI/kxNPf6f+WPF1l+MZeLLGcIk3Ie4WMAwz46E6xLMtgb28Pdnd3l44gxfd39X9pfFiDVClNmbb1ycfz1oJC33iX0p+XY1WlfqJEGiVCJ5MJbG9vw8bGBjx48EAlvXu9Hjx58gTu3LkDu7u78N5778G1a9fgP//n/1xM+qE+8R2NKckmAdtCGq+S8+JymGL6p0SGSc9rcqGjLsnB/7a+oya/BDwqWrvPN8tOd5biRCzahizL1MlYqS6p7LQc3n4cvvfxjYdYh17KV2trtIHj8bhYjV/2/ssy+mXVAYxvXKUC+hlo03gwzglyLqMPlOSQYA04qUz06H9fQG4lHy1wBVk8v+l0CkdHR8XiGKzfkPKssoSmt+hkC3HsI5Bc/VfyI+gz9Dtc0IL+NBI4tE6rGpM+HarZD/xsnWyn489VbixwrGC+dLIbxxQixKe3lIsTwb57IiW9w0H9UGssY+nDsfVt9Q185JtmF3lf4n3e56vE/pbaJ/bFi1J70neV8pPa31VGaDzlGwdSv3P1WT4OpCN7ud6VxgHa7KoXUGj1C3BW3zUaDeh2u3Djxg24du1aobNxQY9WL5Y+7Brf0rjS7qQtC9+Y0OqJjmXL4iaq/1x6w+eHu+IcV79Be4DHg+OiyF6vV5yo4Orn0vdVxNgp/ajQGG8VqJKX4OXg/y6/xcdjlcGq6jvEBkp+iIWvsL6Ly764+Aj+vPYZn3XpWJ+sVA+hbZLicE0W6U5XbaEF7+/IsUonSVD+1fX+Enw6U+JSLND0L/3d51O4ZJXKs3I8+J3PPw4Zh9q40PqWNda3fEe/d40VCzfA+0O73YYrV66cyQf/TSYTGA6H8OzZs6WYSgN9NkbPST4DXSRC5dfGpkVGX5qgO2OxYMy8KqyDoU4F2iFfpfeKhdTxtfslAF4aF6nDTyYT+NGPfgTdbhfu3bsHz549g08//RTeeecd+NrXvibmR4Mxl0J1DbAqArTU/eMi9DX6zrgyazqdwmQygUePHhV9BY8d3draKo7vzPO82OmMOxe73W4xOS+VxYMneg9ObHtyg1OGdKK6ledDd8qh7D4CSho3VZILGrhTkapvIiGBqwPxvf7P//k/8NOf/hR++7d/G9588024evVqsQNAwwcffACffvopXLlyBfr9vtiHpLtSXfD1CWtd8HScKHM5gBJRg+9ASUctoI9xbtGB6na7zp2rVljqCYm4er0Om5ub8Lf/9t+Gfr8P/+t//S84Pj6G7e1t9VmJpKS/UcdMI7h88rtAdYglSHTlo6HVakGj0YDhcAjNZhN+53d+B+r1Ovz+7//+mbR0l+FFAtZfzBitEovFAvb396HZbMLz589hPB4XAbTV9rhIGxp88x3xPA8XITSfz2FzcxM2NzeL+0xdd5hq8qGMWFYoccvlpERFnudwcHAAL168gEePHhX3x0syhALrFYkJn1+aEhZSSpLB5cfy/Ol31nZdN9B78rTTQrSjGS8KXHqc74R/9uwZLBYLeP/99+Hx48fFuJGussA6QyJkPB6L17WEwmKfY8lKC3zj1GWzs+zlDvVms6neB+WSM0Ufi/GLfeX66hhX+tMFQ3QhTshOLUnfuJBlWXFUYugiR5S91WpBu92GdrsNjUYDPvroI5jNZjAajZx3VlLfBp9fh3iZ6yxqBzkh+OGHH8LR0RE8efKkOA0IAJYmon3ktRW0zqR2xqtdpDQuX1mb3FgleNwTE49JeUpp8zwvrtLCjQAnJyfF8eCTyUS96zhkAoraRbzHHHeC0wXuKJOmJzhf4vJPOMeQihepEquSyeJLcs7xIvotErguwP4I8HJRB/XttaN1y8DnI6fqB9Q39eW7tbVVnOa2WCyKxZCxwHJdsRavX9xkkOJ0nlS2JqbMmPareuxXceJRFXVqmUgEkCeaXfJgvjQO6XQ68MYbbyydYjiZTIrFAL1er3jGJwuCH3eOkOpfkrdery/tzEVQGfL85UmA3M5Z5bTUcdBk7EUzFOflCJQp1+IYXpT698GqYDTnN89zOD4+hul0CtevX4fZbAb9fh9u3bpVDKbFYgFbW1tw5coVOD4+PqNUaCBqIc25PJKC0iY0aBqen+/9fb/TuuRyhjj3rrQ+5etK4xoT1HnHtuj1eoWjgAqdB810Bal2vx4tXyvbhap0ni+YoX9rQXCZ8kLziCGLJKTWxfQeQnTqj4+P4eTkBHq9HsxmM2g0GtBut2F7exuyLIPhcFiknc1mMBgMCkcAA1esc5zETeEMaQGB1PYhjg9HrJPKx6g2OekDTsbiWE7V5lIb4Gc8ghh3RuAdFKg/6HNW8kUiaujfrjYp6wOkBp1kyrIM7t69C81mE3Z2dgoCE0lR2nar8DVcfcTSfyQS06XrNbsY208tMg6Hw4J0o+QpldsFH6HA+6krLc8zy7IlAgHv17TWJ83fStjFAOVFYhH1uDWgdOWJf2eZflcSpgHw+zna92hTsmz52Fw61kLGXEgda22IC2YWi0WxQ57rvqrg8qX57/QzTgBIcmqxAv6ful9KwLiC34FWBlI9oU7p9XpLi55c47/dbhf9kBLBKWDxly3t7HtW0vlcBvqZEsAcdBdkjM+k+eihKDPutTRSXWhklg/ae8aOJ+1oZFdf4PoaTz6p1WrF8Xbac2jnsiyDnZ0d2N7eXpqsoicC8PdaNYfEkecvd8BPp1M4OTmB4+NjGI/HS4tuQ+2SKy3Xu7TttZglxoaFQPPhyrSPNbbz+ZTaMzR/3MmGfXY+n8N0Oi2Otdd8DqsPzGMU+llqv5TtZGmDqviUdUaj0SgmIABetgv6f1ofjunbVeqpWG6QpqUL2qnvhro5xXiWntfqVvrd2je5fXD5ADwt7owHgDO7/yyxliQzPxlIkoVzpnTSKtaO++rW8kxouS6+wJpW6rursCNW+PKS5A4t32VbpJjJlb8r7gKA4jS7er1exNd4yh3l6bQNVa68JTl87UHto5SWvjPfuOJ651gE74ylQqQ2qKsI/quGZkx8jnLq916XuuRyaLv1+DMcdAUVJYqlybfpdLp0h8pv/MZvwC//8i/D7/3e78GzZ8/g+Pi42GnZarWg2WzCeDwu7m5YFVIGe64g6bxhdbCyLCtWjR4cHEC324W9vb0iUMnz05XbMSupfeXSYNO3qqns2ArpY9quH+0uWf4sNySxWJe+RMHbAcfycDgs7hTc2NiA2WwGm5ub8P/9f/8fPH78GL73ve9Bs9mE7e1tePbsGfzgBz+Ak5MTWCwW8I/+0T+CbrcLAKdE59OnT4v7faTVUalRlgSX+nAI+VlWJ+V5Dv1+H+r1ejH5zfthCuR5vnS07snJSXF8eZ7ncO3atTNHQ1J7YV25qI0/GkDGyk/vFEwJa//Z2tqCv//3/z58+umn8Cd/8ieFXm2327CxsQGDwWDlNlFCyokMa7qQMl154/Hpz58/h8ViUZDF9Fh1ngf+TXcp8R1xvnKt/dvnl2LQxMeSNqlhhVTP2vtZx1is7qLkhO+EjRjQHUP01I/5fF74qXQCdFXA3YBHR0fwwx/+cKlNms2m996qsvAF07Q/0PLb7TbcuHEDRqMRHB0dAcBZQkvKD3WZdtJOCiChfvXqVdjY2IArV67AYrGADz/8EGazWXGCR+xkWgwo2ZBlp3eto/3E8U0nI6lfaZU1RiYOOsGSipSlk+LaQs0yk7EotwSu060TZSnaXPNXsF2pPveVLb2Hq0/4eI6QHbcITV6ePyX36fe4Cwjj/H/yT/4JfO1rX4OtrS3o9/vw2WefFTvLtbvBUiL0/XHB3P7+PvT7/eKIfnxfqX5CJkalNrXqHxxXGpkZihAfDBflaGWG8CL86hWNpJX0BK0rbTzT9tjZ2YGtra0zE7L81KJY4L2kSHij/2iZpAod26F5xCBl3L1qbvTmzZvwa7/2a0WZjx8/hmfPnsH9+/eXxjAitd5ZFy6YAnVWvV5fuhpDS+uCq0+GTh6FgvZ1X7tZ9LF0z7kEXNSRZVmxqI6Wry3swGepvZAWq19EWMZN1eMg9Y7YqmDxdUP0OJ/Y59deYX8bDAbOk6wWiwU8evSoWGRW9h20cmq1GnS73TPP00liOl6lKy80OWKv+zJNxlqD5rKoaqCkCuxiy/UhVC5LemsnrbpOfARJiFOL6Tnpluenlz6jcT85OYH9/X3Y3NyEbrcLGxsbAAAFwQwAxa44NEyxg9qHMgTbKsm5mOBagisw8uVPA/X5fA6TyaRoL4DTY9XwSNqtra3CkUXHg+/mcvUxV9+npFDoO9PvrMaM1xmXm8vjCjBdZWp6MDUZtCrnn76zZLjpEXxZlhWTtfgMGs35fA6DwQDm8zk8evSoOF4XQfN3Tc7Fyh8CX9tr6X02kP4u6WBtPEh9FeuLH60t5emT3/Ub3b02HA7hyZMnsLGxAd1ut3AG6dFBoRNaXI4YXcxtFc9rVUQ3rf8sO91Z1ul04Pbt2zAej+HGjRtwfHwMR0dHZ8j4qmWrAhZSaZX+IPZFXAkqEdySvdJsGH+Wfxdif+gzuMMc+wjA2T6s6QcJPjIkxrfg9SP5HBIBKj1rJVolOSyy4vNUDuwL/Og6zS+RfBoNMeMVd8Ri3j5/OIXtC/WL+Pd4tBXvExrZnErf+mJebKtOpwM7Oztw/fp1WCwW8NFHHwHAywVyloDdVa/422g0gnq9DgcHB8XENJVDgkYsxIzhMmm15y1xX1lI446PUx9CCN9UepDq3xC9QJ+P0XH42XcMIo9Z+G/4v3Tlh8/HlPLCiSzXEY2YlvqCyBW8ePECDg8P4eDgAE5OTqDZbC6dxlQVLD47/y7PT0+MGo/HxRHjGnj/pmWGyGh5xkKaohyh/ADtE5pfhLFdlmXFCQ98UkOLsfGzZQxrf1ueRTkxLhqNRkuneSH56/IBtXfhoN9xP8Micywk3yWV/U3pq1fp99N2Bni5EAQXheOx8DiRRvWhS1ZNX1vimCrfV2pXGn9g2ahXkX+xTlxypIqdLb5VCKz+FNodOvbpblXNjrnGlhZLWN6Hy13Wl/G1iWZTQtqD6+sU3IxVnrL1c97g9ihVnq42zbKsuA6Cth0ueJ5Op8VkLfpz/Dh9yzshrPqB6lyJJ/RxhZJuLtP20TtjL3HxUaWR9pXLBwKAHBBaBtRsNoMHDx4UO9jG4zHs7+/DN7/5TXjrrbeKdHfu3CkmZgeDATx69AharRZ0Op2lMsuCD94YJV2F87lqI1GWQOn3+/Ds2bOl/LIsg1/4hV+ATqcDf/VXfwWj0ajYiddut88ENRpcO0slY79uwDEjrYjmztyq23/V9bVYLGA0GhWfpVWWSGTg5D7ez7O1tVXohNlsBn/0R38EWZYV94/S1VONRqNYXU8nGlMjhXGPDVhcQRUNMrmcUj79fr8IwHzONv4eMn7p/UpPnjyB/f19eO+99+Dtt9+Gfr+/dP8b3+EZszuqrD7jZIsWPFUBuugoyzLY3t6G69evw7Vr1+DWrVuwsbEB77//PvzP//k/i2fonT7rqANdSEH6VNkm0g6I2HrmMuOYowsifO+CO4fQnt64cQO63W4xwWMZkxyaHfW9p2ucZdnLuwbpjj4+mULrlfpjIe/A89SCPu2zlEcM8F1RhlRjMc9fHpmMZHaWZWdWyl8iHNevX4d79+7Bu+++C9PpFH70ox8VO2O1e0lDgPrj0aNHAADw0UcfQZa93CGHE1RloU1Q+sZC7KSFKy+aj0ZGx4wNvjiiir5f1nfAPLS8rPoZYx46Ia/tmrPCWmeoXygxj4uTaF7Wd0C/Hxfl4m8ScCEUjrtHjx5Bo9GATz/9FJ4/fw4//vGPIc9z2N3dhdlsBsPhcOV6kNcj3dmIv3/22WcwnU6LOuN+BLUPku3xtTE9QpTKZZE5xaSQdSxzO7i3twetVgs++eQTmM/n4kIy+hn9JF++1t8kSPnjtTjHx8dLkzDaZE7o2MA02Hd53GO5fkTTL5ayX3e/ASfaaGyK/x8dHcHDhw/P1FGr1VIXv3L/luO84jL6bhJnQP+u1Wpw7dq14qjm2WwGR0dHpSb1NFA+7Dz4Tvo/gvphGxsbsLm5WcTiaGeQW6E8lpQXBy6k5HDFTwBn78RMgZDJr5TlxUyqXiINaMwB8FIf0AXn9Xodrl+/fuYUi16vt8TvY37SySnSnFGKfouxtbb4jp+W4erDPKZzpZVkTzIZe9nhT5GSJKki33UBDgA01tQh5c6pyzGW8szz06Pgjo+Pi50+AKcOwJ07d2B7exseP35cHFNK78nSHHSpbC6fy/hZ05YJiGketGz6P/09RpH5CBiahr6zzzGgf/N+0Wg0zhyzc3x8XKwsRQePThrxgMNF2JRR6Fp7huaptTm+AyWg8UgS/J0GdNT5tQRGVfbD8wTK/vjx42JStV6vLy26QODuWdxxDXB2DOX58k4WbeUiLVv6zfq99C48vbVttfGXghzUyqR/UznosauS04bpXPlKwH6f5zkcHR3B/fv3i3sfx+PxkgyNRuPMuKHlWkhOmp8WKFsJKl9ZKYCkDMDLo7x/+MMfwpMnT+Bb3/pWcWR/u90uZI45SvC8YfUVQhDTjvQ52qa0Xn3jU+pTkgxl3o3aTF4ukuWbm5vQbDbPBPzSeAmZoNHeg9eHVI+abbcShi45Yghsa9m44+rg4AAAYOmYYp+/IgWnqVFmMqYsqK8jgX6Pi6ToMe9af0hVT6H5UF+MxhTWyYZQufjdtC47NhqNCp+GL7aJ9V0tPr4FKKemO1zlSL4SjSskv04qX5OLy+LLy5Vf1Yj171yxiPS36/nQ8ul1Fr5+o9kfF39Ax8aDBw+g3+/DwcEB9Pv9ot/hhG2VE/Ncbukz1xOUp9AWyNG+LuVJ01nb0PLZ9w7a99ye+/xxGot1Op1iES3A6alZo9HIuavFZ9NjfTzXu2rP0X4e4rdq41Dy4bRF2ZpM1rQhaazcyEWLNTgoZ4VotVpw584duHHjRnFPcJZlMBgMiquU8FmAsz6/xkXG+KdlEKL7XX2ZLyzABVAhd8VWpY9j/R/+vC8/9AfpYihaFzwPPq5pvqHHC8fExyH5xyBUZs2/tTwbI1cofyM9U7UPaPH1LHGHpW4tNonXQ56/PLmUpsM4xCcXHQecO5Fk0L7D9PyqS+k5l0+ilevzKXxttDQZu+pO9DqirNJ/FYCGhO7UwUEWUi9aRx+NRjAajeDJkyfFBEu9Xof33nsPRqMRHBwcQKvVglu3bsHh4SHs7++fyXfVTqNL+Yf2mZQkVAqEyEJ3fOK/yWRS3LWX5zl8/vnnkGWnu7uwfXEXECpavGcWgXXiU6SxDk1VwHHS6XSWdjoBnB1HCO0ovHXqE1UC3/Ov//qv4YMPPoButwtbW1vwzW9+80za2WwG4/EYGo3GUl+idYdHd0plrBKolyxkM+/vUtpQ4iw0De+TuJuBHstFv8fPEpGgIcuyov/XajV49OgRPHjw4Ewa/B93z1vug5SIYdf9y7E2nZNHKfsW5o028fr169BsNuF3f/d34c6dO/CVr3wF6vU6bG5uFpOxIUf3X0Ss8r24T4M7NiyEM+p67T5D+p3v+EgOKbihwFNGrl69CpPJBPb398/0fTo+XOSwdVzgGOa2iwd71t35HJYJFSxbap8yPjvqqclkAh9//HFhy7EcyV7z58vKsG7gJCT+8+1YarVasLOzA3meF3eBakQC75cW4oIjdHfSKoG7DNGuDYfDpd/p++J79Ho9yPO8uJeJ6nqNiKgSLv1khTZWUZ+47qSLhVXGmD4T6ptJstBdji6i3FUu9zetcXEomekinkOe43JK+iTPc/iLv/iL4nOtVoNOpwNZlhVj4jxPB+C6Hhfx8JhWegYAzkxuWNvAuhDdihTjWsoTJ2OvX79e6LfPPvusWNjk0/HS8dj0OdfzZfSij6zF/Pn30riTSG8Kfge9q20l39EVK17iLOjRs4jNzU341V/9Veh2u8Ui2Hq9DkdHR/DkyZMzefBxK7VZlUenl0VIn0H/N7VdtozPlH3Z4k/SiVd+XyvAqcy4k9qXH/3NV3cx9ZBl2Zk+pk1qlUHVk6mX+kqGzx+I0S88PpfypDtgtbbXrq6QTuuIAfIolqPS0f/DtBaUlU+djA1xvFOiyiAwNEDwISZgjHHwq1CG6wJpEslyfAsFNWKTyaT4/cmTJzCdTuH27duwubkJh4eHxepJi1zaqomyQbiFWEsZCKbKK0QfaO9Ix6AWeEhOHT12CJ1WeocszRvzwEkeqfwyR2b43i30eU0voeHE40twMomXNZlMxElZi8yvIiQiazqdwv3792E8HhdHBeIRjQAvDTM6BJ1OpyDxKGHOwduuKhuj5as58BK5iu9nkZ/rYQm+vkRXveIxqHRc0rFOCUM6Ge7rz9pnDkpOSJM5GpnvImU4UekCfU8feL1Y8pee9T2PxBZObkg7x2OwSiKflwvwsi+5fAerbS77LpZxRNPSv/kEVQixGkuOYxm4YAEJE+3Iapfe0WSRQOub6zRfH3bFKr7yfTGP1I9ibSjmhQtBqJ/i6rOaHCkR0k/LloPQbJs05ui1EjjpOBqNir6plUHzRVh9P2wTbB/fZDlfSITfSVdJnDeoH63pFUvcKRExKfoQ9ncrQUnfAfUlwMujXl2LqKh/V2aMWWxL6jEcSrhK+pL7MhxcN9N8pfbR4jlsg1arBa1WC7a3t2EymZxZQBADLKNWq8GVK1eg3W7D9vY29Ho9uH//fvEb9guqN6js0m4jX3waA8l3lMYP50fwZBeXrtTicVf/tryTRUdYYPENLGmk8eaLgS1jXNN7Zf1A/J/HYlrcL8lC0+DVAgAvJ7c0WSU9zf+2xFqhvp8mQwiqiCdS58nfrdlsFv+o/ac+BULiF6g/WNYuhSCkXqS+w/tKt9st6mCxWBQ7glP3EVe/dCFUF/AyrTEmXoeXZac7o+kpjmhzYvwny7ta4h8eb1ryj61zCzRbY7VfPK2mUy0+bihCYtHQekvpf8TKQGGJCbT8facGaXVllZfnQcvjdpPLqckllSGNW5+sUp5Lk7HWBnIpjVDy0CLkuiK1Y+vK83UAJTNC6oH2Obrj6enTp3B4eAg7OzvQ7XbhxYsX5slYzFcjAF0y8DShZ4e7YHGeNUgBXIr+5qoPazmSkaFBC52MRWcOJ2Nx9Qqte0qOWR0m+r0Ei/MRG5y6Auo8Pz3mASdNeFq8CJ0Tu1qer7qOkd5zOp3CgwcPIM9zaLVaZyZZKSFVq9WKYxBxtTWdQLSgCqcpJg1/N5/DT9No/ZKXJxGdqMcBoLjDCSdjuWNMv/MRllLbWo7URSI9ZGer1oah48flWGqI7T8+PcLLwDvbtre3i4UeZbEuPpxFDl7P1vb22Q8rgcvHGwUni12TOhYyx9ouWfZyMhYnZJEwwKPGNbjqCn+XxrhWB/Q9tB2x2rHxvnYNkT+lzaSTehggnodt9hEYqwK1Sdr4wX9ohweDAYxGo6UjRV15I2J8Zmwj1265LMuKXbq0HCQhqyB+y+SJup/bJqne8XveNmXvGuXySG3Pf8PfNVtN02or7Xn6FP3eEjecl/+ttamF73HVjfY+Up8BeKmn2+02bGxswNWrV2EwGIi7xKzvw8ut1Wpw/fp12NnZgS984Qvw6NEj+OSTTwodi6ctSRP03MZIfnBVvo2vT2Bd0uPIXbZd4+YsvnWMbJY+HVt3Prlo2ZYTXUI4FJfvzuMYHyzlusqj5dLd3ACnC7LpSSp8bGs61BUTluGapDz4O1jzrWLMVR2jNJtNaLVa0Gg0zhzPSccxfmfRv2V9GQss44HL4nqm2+0Wx4kjn5JiN2yKuijDrbh0nlRHyN31+30YDAZnFhFyX4v+T/OS8pbiPss78XeQbJ+GUN2H6Sx+kvaspc/RsULfR+LgY/wwV/1r7RXCIfni51g+Suo/mm6WEOMb8DbgwKuQMA/pKhGtLIsvw+uenwCEZUqn5NH7bTUuwec/h/YvdTIWCz2P4PwSrwforikXCaBBUn4UeMfKT3/602L3ZLvdhi984QswnU5hf38fNjY24NatW3B8fLx0dwxO/sWMAV96zSiFBBGufC4qaHBAiSJ0XrG/4OpggJfHGef56ZEC9ChfDFowb4SP2LHIGQqpHIszg++Gq143NjZgPp/DZDI5kxaJSVffPS9C6DyA70qNLR3bAC/rDPsJv7MYJ2Vxx/1Ftol0dxHAWWKSvpsWALqAedBdxlqgid9jvw45DsQFXI2McBH1GoEVQj5pgQ1+Rx3C8x572Dahi51eNUgOOf0Nx4hGhMTYXMsu81arBfV6HabTqRjE0InQMuOUlqkRdb1eD8bjsTpBz3UGrytXcBLqt/BypB0lFn/RgjzPYTgcqu3tIkVSgdbPOh9Ld4nzBZIdeZ4v3Y2uxU/4N+/fLoRMGmgyuj7HQLK5dDyiP4c6N2QMaTEl/u3bVW0htCSdbc0f05atR95H+IJWF0IJefQ58F76vb09qNfrxUk1mBffUW4F9kU87UYjNNF+TqfTYnEgpq3iGOtY8D7Nxxq3txxlY5RVxMOhQE7AN5Zp/Gbx43ndSnc3Yvk+olx6Rvrb9awlv06nI3Jms9lsadG6i0fjcYkma0p+iZfxKnFXCLqwbjQawV//9V/DYrGAfr8Pw+EQBoNBscAbQeNl1H0uu7uuoONJmzTCxZ18wUAIUti+2N/5mHGNFWrDZrMZ9Hq9M3bGspCUp0/RB9DeWvLi74qcfQhCuZYyfqN0TL9rYk+TNeSZUBlTIVSPuk75iynbCj65CWDjB2Og6R/Jx3Pd02zhFKjfWhYN149lCIfzJh5dSEXc+PKnsBKymhF7FUHJNa0tqLLRFKxGSOLdhI8fPy5+29nZgS9/+cvFGf2NRgO2trYKggLT8TsgXI6t5T3pcxJBwtNqeUjfu+qOlrHKfhRSVxK5wklXmg9tF2wnbEuucK1OX5V15Aq6eP/GOsB3ppPQSJpkWbZ0HDeOH8055O8U6mRcRNB35DuRKGjQirtfEPQIbJ7nRQW+q+ZwSjo11JHm+Uo6HIGODHXWfPlJwHwbjcZSG9OdUyFEqSsdHafSb1K+PvlX1be48ygRe7SPcIfZ6secB2LrkL4DEvnYh6Sj7q15hQKPNOU2DFfX4/HqqKN8fdpHfvEAG5Flp3fnIWntG5c8j9g7XSXZNd1T1XjhZGzIuLXqQ9d3Fv3zukAjDajOChlvrrhMI9WkMWP1aVHXUnIotb7E/OgCRKq/pfLosVzWse2LXSzvFfLuVsJI02m0vrkeDJFBesYVb2n9yAVX/pIfZinDRyDRPNDeWcYUxiWueJjLh+Og3W7D5uYmbG5uwnw+hxcvXiwR8/hc6B1hUnzIOQVMg/5gq9VaOokLF7eu0g+TxhKvV1//DimPl6OlicmXQypH6i+u7/j3/Jh4SafyxbQ0reRL8HTUv6flS6QtzTuGG/KNUSojfcdms7k06SrJQf18zi9I/YqX60LKfnLRIfUxehT6kydPYDKZwPPnz8/YavoczU9biLLqmCumHNq3qO4HgOL6ITxhROrb1jIkG5NCd1v8NIvNlfTHaDRSy5Py5TJoslm5kdDfLKB6ymULtDiO5sHbMFY2XqehfBXASx9EawNLuSEIaSMLf2uxLWXhK5fWPeVUpOe0RQla3q5y6Xc+318qQ+KItf6q+WIxusg5GRvj9F3i4iCVASsLNNBI/PEdjpJC5HJL9znRe4MATp2gfr8P3//+9yHPX16aDgBndhpyAxK6upoPWMnBCnVEQpwxV3CwjkS6D+jU4Y5ZADhzFwc9Jg4hBST4feq+LxEVtEwriedLhwsIkKTf3d2F2WwGn3/+ebHSnMrE5aga66JXJCDxYhnPeMcJJ8Iu4vjRiB8KFwHgy5s6JryucJzSIxxxEYW2k4HnzyfFNKIKd9rySSS+G1Qi/6Ry+d9aECj1D1/QVhXxoZXJ5UGyAO+Lxfbp9/swGo1gc3MTut0uHB8fO+9LfJXASdEyARaHlBcdI3gsMB5vNp1OYW9vD37913+9+O7jjz+GDz/8sJgslYCBf57nSxO3fGe8b9Ixz3Po9XpLBJ5EhMQQKpKNoOPX5bf4YLWzMTKmhNTXXGTQutrUWFjIG06GY99++PDh0kIb+gw/3tt1NL0LmCe991XrEzjWsMzj4+PilAZ67zKNOaqGr/+iTqALFrVYS7KFodDqzZfWQvjxeIveEe/KP8uyM5PlqfS8a9LD8iy3P6ExC/dLsL1TkIYWYpPnjfGbdvIDldkyRnBsoh1899134erVq/ClL30JOp0OtNttOD4+duZBd1tS/7UqXRvSnpZFQZqdlPpPalj8dimtNMY0gjnPl3dh4ZVFo9EInjx5UqTDXaFWu845GfQ9JJl4n5QIWx98fVqyXTS+oOn29vag0+ks9Y+joyPo9Xpi3q5+bZGpLC5SnBwKGpvOZjNotVrQbDah2+2Ku5epL4K6mF9TofVDCefFtfj8AXzXk5MTGA6Hhf+TKo6MiTmsiB0TGk+E/hXWQcgkjksWy/UmnJORynHJQfssLTMkFgvpn2XbNMteblgBcJ+MBvCSQ/a1ayhC7UMIXPKU0QfamOJt6KtTyhVI9Uv9c+15CZZ3cz2rwXI/bMhpGbFQjymOITS4wxKKdSbxy6JMo1VVl6uoa4scUj8K6X8uQ0R3DaLT9OzZMwBYXoHrI/o1ea1GTqsHlwKkefiMrhVV5RsKqc2lNLR+pP8pyUz/WRRliK4KJUHK1KcmOzp0eDQzPZJ1Pp97CUOXvCmxKh0eay80Z0L6jgcP1vGSgtBLVY+WAIKPBR9x6tJ7mo6h5BMfq678pQDF5XShTpCOddWe0crTdLb0nU8unm+VwaT2DjS4ms1msL+/D+PxGDY3N2E2m8GVK1dgOBzCeDyG7e3tpcDmVUEIYYfg4yJF23HSHidzaMDbarXgzp070G63oV6vw8HBQfEb38nPZbf4RwDuu2j5HZgULl0RA63O8TdJ57jsvI9wsMqUsv9LetZFzrxqKFOXWEd0d4WUr6aLY9vS6rfTth2NRtDr9eDg4GBptzUlY13jMraerH1IGltUX1jHTowd8+lQLf5yyae1B/c5JDmk8svoCy3PFPmEtK/U/1McqRZbNzhucTFkWZ3HY72trS3Y29uD3d1daDQaMBwOYTqdqn1pHfSutWyu69aFK/PFDTFjyacXkMcZDodLz/jK0ngE/NvFQ2lcAe1DFr7NwglxPcc/t1otaLfbhV1xEds+WVzckwSffottawtHmNLPTDl2aD+hcS0ALPkqvL7xs2tSzdenLe9Rla5wcXb492w2KxbguPqOlU9xcRDrCjrWJO5J80tctpr3Mw2az+TKlz4n/e7a4SjJSPPlskl5lOHWqHz4meoY3n8kHW+RwZKuTL5W8PFmlU97b1c6VxqeXuon9H+q+7gM1j5tkccHl/3V0vh4lVg4d8aGoqzSvyjK9SJgXeoyRI4QpcVXu0vnj1NFUqvVoNVqLTnx0uQAv+QZv280Gup9ImVRJvj35XuRgcSbZhBwVTNOUDabTajVarC5uQnj8Xhpt/OqA26pP1pILvyO72ICABiPx/Dw4UPY3NyEa9eunemLWN5kMikuR0+FdQn+KULloau0+Gp4Cb53rmrcYt4p87GQhBb96zoGzPes5nBlWbY06YOEAz2SFX+THCGqw/F7fr+U636KEPDgAvsIEkSulb8WIj4FXH3n5OSkkLfX68F/+A//ofj8jW98A/7Fv/gX8Ad/8Afw/e9/v7iD7VUH9wEAqr2rk5J3UiDjurZhNBrB4eEhbGxswObmJvT7/VKrzev1erErmu5Sp34PAKi7cCVQIgp9Ki1NDIkn1Q3WGydGaf6xRE4KvbGO9vMSNnAiFceGRqzWajU4ODiAg4MD+OSTT4p2bzabJuI8tJ9IpH0sOGGC8vJdZFTOWCJeA5YnHRGvTVQgyUltbAypI/kS1N+xTrxYUabONBmk762LAFzw+VA+gm8wGMBoNIIXL15AnueFTZH6mAXUVvLyDg8P4b//9/8Ow+Gw2EWI5aG/qdnNqnS1pa198SH9nZ4KwG2s5I9XaX8km+uDRHJSubkfJF2ZoNl1F/mPv3GfhvteNG/8n8sU4xto+lMDjSt4Xx8Oh/DgwYOlU+SkCR/p2G6rHvDFKxZ7liruSoGUeSEn2Ol0Cv4Jr0ajC4IluxRab7Fyr9LvRJ8Bd7BLMlx0PzgklqB2LhZavGOVT/qdcyMSJA7Sx4dZZUQ5NEi7xjno0bc8P+yHLhk1G2FFVZNyFlivlbBC8zUkf8NV7xosJ+BpvEgZYD786jKXDNLzvnQhsmh5qJOx1GEI7aA8kHHlf55YhZNKy7roRig1uEGjRD9+ZxnAviCQAo8c09reFRT6lLfV+EhOsW+ghuar/W7JN2bsVjnOeR1LJFisw1/VmPT1R0uwzANTdOzoSkt0fF35+36z1EFK8uk89S3VLUj2+erK4oCugz0LQSwhRxGqs1z9jo5hrd0k0tkXlIQSEKF9U3PuXf3ivPoKddzxLuqjo6Pi+xcvXsBgMCiOisX/L6LvItW/j+hcBbje1+SkeqrRaECr1YJWq1UcxY/f+9pH64e0v+PuZ7p7z0IaWXSjTwdL7y7JGYJQUjhEhhAyQisjxF991cB9mhD4+jj/Tqtb/E3qvxY/1kKi0qNYs+x0cRFt8xh9GuLTSd9L9panq4o41/K36C4NVjklTiLUB7a2V6zOqQJaG/M0EqGn2aMQ8Gf4hLkV1vGCC3MnkwkMh0M4OjqC2WwGnU4nyK6cl88TWx7XZRaCVkrr04WuOFyTqww0eajM2rtafH2ss42NDcjz/MwVP5Z3cNkY/ruvXTSdhN/zk7BwknY8Hqu+JM8r5B3o7zE6OsRmXnRQPgYXfuBELdeftP/x7/DvMrpgVfwKgK4jpL5exnd2IcX7Sm3iSuf6nfv3oXpTSivpQj7efTZNkkeSX8orNHbWYjCLzPydXZ+5T43+RYgO9I3H0O8sv4Wk4bD095B8fbESTye1KW8jl+8Q44eHxMuSLGXibasvFZqGf5d0ZyxCUyAhz79qxvtVe5/UQEeGkoy4GzVk54fr8mXMv9vtFt9h8OaSyzKQfYQQGsFV3x27inwwj6r7ONYdP6KOAh1g7a7Y1NCME7+Hy0V2SN9xBw53ddO+incMbmxsOFeQ0TrgRMiqHPcYgrwMLORa7LHOVUGSowrZYvP0BVP4u7Zjm6+mw2c00o473XRM0XwkcB9EOjIF87XqBh+5gDLyZyzPud7FCkv/4bYQ72efTqfw/vvvw1/91V8VK7qHwyEMh8NoMnYdEUoSxKQLlceVN94Jvru7C3t7e/CTn/wEAE6JuU6nAycnJ97xzMcd/sP239nZgel0CoPB4MyJEqHy07qVdmO4QMc4H/+8TOl3ifiyvoPUJ6RdDdb8XLbYR8xJn7XfLvK4RPvrmpyhfVULqrHf0J1i+L2PfMJ0rjZeF/8gBWhdYP+m//Au95jd9qGT69wWI2JIW4mYA1jWCTiWsb+Ekig+W17Gjof4x3Q3qK8sy8Qnjb3591aZeHrat+h1QBK436jVKwe2JZaDR4M/fvwYer2e99257j3PMe4j63ztrPnYWt6pZHTJQ/8PgWtsYl9FeVxjTiKJAZb5g3a7DV/72tdgOBzCX/3VX4nlaWM+1A5LNl6ST8vr6tWrsLm5WZzutlgsiglkeq+5lIcWG5Q5Aeai28QUdh1tyWQyKdqC1yuNs+hCekke6e9QeaqGRTbpVKt17y98PGv2UHou5N20UxwlSN/Tce6LG1zvwPkP/qy0I9YVl/nexQU+gWrZYCK1B+pErg81pOaFuT1K0edTjx1LfmiDeTu4nsNxYI0D+Biw+uQStLgw1HfVgPo8pD5C38U5GcsDhJQd1+dErRIXPdhehzpMBUuAKT3jGniSk0O32nODxI8v5pMCPE/6WQqqLe9jnRQoMxZ9JCHPNyYYD8lfet5Xnlbv9O4UOmljrVdJTsu40si72PJdMlHSESdJarVacZ/g1tYW1Ov14ihS2l9dO0B9DlxZrIsu0pzfVA5TFXmsS91JCB1XvP59zrWmTzh5J/0m5YF/h8htDcDwbzrpayHQUjvZru8kshr/bjabkOf50rHE60BSlkWK9va9fxkdL/UjboPxNAQarIROJPDvaDn0jlprHqHvy30s7g8gkUwX4MW0mxQMu/IJeRfreA0NAEPsb2ryYNXIspcTn0iW0ONDEVRXS/0G07vGq9RenICSnqXl4T9qa1z9gPY7jDGqiJ/LgMsvEYPcntHfLKRY6DtLeskaK4QQYK4YTso/FJJe03wfXywREg+6QOPjUB/IYvuksuhnVz5lfQtuK2ezGTx58gT6/X6Rv+/0m9Qy+fKtIs539TOaXyqkGPOSbDRudfnQLr5H8nlpfhgvt9vt4nhZugAN+QO+OMA6FqTypYVCtA60eILnT2UbjUYwHo+ddUHh48m09NbfY3TmecYWZcvmtgTrgx7Ri/Y/VAdhniG6NBXKxkuSP5uab6kaUtuGwOXba2Pd5w9IaSVOS4snQv0dzR/X4GtXXo8x8Q/+jrJwvgLjSP7Olom+WP3lkt8ah1rLwjy1PmmtOwptvLr6n1Q+f47+zjdShJQZA+kdfbrK0j6hNtQVZ3FUsjP2IsJKtFzU8i4S6CrdsgqMK3wkN/GuNCk9H3C0nVLdPSghhcIOxToRRAD+yU2t7nFFVKPRKI7yCQlQXDIhQsYrXYEf218k56zRaMB0OoVnz55Bo9GARqMB3W4X2u023L17FyaTCXzwwQcwn8+XVobxI3O0IPBVhnQHxuuCEN3i6ve8P1v6juTUW8rVgO1H7/3VZEmhq3077mif8t0duw76lr4H7oLtdrtFXU2n01fqrtjYvh+ivyXiM3a80fLm8zmcnJxAp9Mp3Xe43vPtiuC6ko5dvtPcCqmOaMCMd0Uj4ViWBHEFrCGkJH82Nc7D9zsv4HuinzYajQDgZR3QxZDS2OA+jJSO5ucirmie/DmpD/r6PT6HO0xbrVbxjiHBfhXg9aWdQEH1HrW1fDIA8ykzPjSSVhv7XF5LfXGSjsoPcPbkgFi4/JqyNsH6my+ti8zl0IikEL3JYzeef+q+Pp1O4Wc/+1kRC2ZZVupO9VXBNYZcNszil1gQOklnjY1DJuto38QJLFdafs2Mi0DG3f7z+RyuX78OW1tbcOPGjUI/I3AiDe+4CyWJpbazTLJYfqN5zudzePHihXpNkfQM9eescZHVHr0uvgtC4xEajYZ4Eg2Pp0LGeBmk9Ces/YWizG63VSMmHoj5PTT+0HwkX1ofrLYf06bgUXygixc0mXDX63g8PmPbkSPFv/HY8FartTQuNVlCfFmr/xnazpZ6ttZlTD5Ut2FbaL44fca1E5nrRKoPtV24vAwXJN6G/hbbf6uwa1qeQZOxZQIKl7P2uhhyOnDLGKgYo4hYJ8Poanved1L2EcwP73XgJIUUQFCH3BVcWwy1RP74nrFAI4NDAiILQvVASFrr5A4nZGg5NPCIPYaHtzfmGUM8ha6O5KSURBRS9Pt9mM1m0Gw2i0lY7N/ScR0x+mOd9EZK8OC0TNC97kjRhjG6SuqzvrrmOlY6+jikfBd8cmifqQ7iOxeltBKRr9kXl3ypbaFE0GrE60Xo5wDyBIsvDX6npadI4ZNoMtG7ejXEBhe8j+FkL11A4CI1pTx8ZdG0+F6U6JTy4s9K6VyTH9L7Wn0u/v4x/o5FT4bY2FfV/mbZy135KQgfKX8A3d6gztYWn5QhMyUSlsrkknfVkCYdtHiHx7GuOIOP9TJjyWfb0eeP7UNU5pD0qSYrQiY+fGWHEssh8agL2B9csY5m92LKRr8qyzJ4/vx5sVg1pa5OAa1tLT6XBk46WtswNP6z6KvYuImWJfkdXA58ZytPoJVZq9Xg5s2bUKvV4Gc/+xkcHx+fibWx/DzPTUdeWmUIHbP4vpPJpDh+GycZYu2T7+QRV9zjakP6+6vqs0iQ7Cd+77MrITErluFru/NqA+n9q8w/db7cb6GI4RZSycV9K9e413SYlo73T02/Wjgb7RmJ83DJy3+nafhVdACnp+zs7e3BfD6H4+PjpWfpAgnMW1rE4xufPrligOVauIoYOUL4G+5PSOktMZqLA8R6536Lj1uI4aytaS3PlfVzLPGtczKWDk7tNx/KBLOrRtWyauRUVdDIRkmOUFligmpXPlQea4AYMogkxcvvRvMFAiHGiMsuKVL8ngaUmMZSvz6nzCqflG9ViCVjNAMuGXv8Pcvij6jCNpGMYAjRQYmoGLLRR2bleQ4nJyfQ6/VgY2MDAE4dE7xfttFoqHdMSGODp3nVkWWnK7r4/SYAr1c9ANh3/UvjzadbfM+4QHem+saepAd8OlTKQ5NV0+P4Ha7KpE6/Dz69IhFSrjRWuN59sVis5RGbKRBqNxGuoMnq3FtIOcsRvdxPsvquvC/NZjM4PDyEer0OjUbjTKBE0/pIJd9vNC9Mi/3Zt3vJR4RINrqsH8PHpS9gjMnfIqOlz6wDYuKnLMuK3dB4XHFqmVzAo4RDj8W2lIt6FBEyPjUbStNoiPWxXSvpKZkCELYzHvtF7A5UfE7SEZgv14W0XOk9uHyWd8Dntbx5Oa68Y20QwNm7VbketBJzrr5pISe1ZwHktpJkDek/0vfUXu3v7wff770qpCBt+eeYupTyLdMXJdk0HcbLCYmjAV7qAR8HQ3+nu+EparUa3L59G/I8h+9+97vFsdbSjhqaj0VOCp+OcHFOiHq9DlmWwWg0gtFoVEzEanliPi5imz9P0/raKMbOx2BV5cSCypfC3/SB2lBrjBHCWblsla9Mmnad28wCi32jv8Xwi7wczVeRPvMTwSxyh/wmTchJ7yj1LSmdaxcq7S/I10qgZfD7iAFOd6TfuXMHRqMRHB4eFnI0m83iBB58jzzPz/B9XA5NH7qg6XLfWPXVH/1NszHI8bp4KqkMLrP1rl6t7V32DOsd5eVxbUhd+9Jppyv5xoxWv1ZbTdPztD6ZlyZjL7oSvUhYRV3HOI8XHSEEF+4gdO26kpTEq0hQrwtCnEcK2k64s8jqKIUQ2THjlspAiaOY/kPrhzolWZZBp9Mpji1GB6Rer0Oz2Sz+n0wmF+LYrqogkfaXOEUqQibEUbLaKFew4tPNLn0tHUFMywxxDuk/FyxOoqucMkS9liddmMSDpFcVviAbJ2roKtwYG8D7Ig/2XJhOp/D48WOYzWbQaDSKVb/T6dR89KkkB5cHf0e/yHU0kRRExYCOFWn8uPJuNpuws7MD4/G4IFIt8I0dl/5IDdoX8L1dfsu626xYuVz+EG+PEFLJ0i+lfs4/o42wkpFlUGXb8n7O624+n0Oz2RRPeIjxGTEPejIMJ7xc5F2M7XXpplarBd1uF8bj8dLuSavdlt6Ny3EekPqvj9i15BnjU1j8Nfo5xZjCPNBev6rXkPC+DWAjfH354bPSzlAOl11M0f8t45Df362BHjk8nU4LHba3twc3b95cuiu21WrB3t5eoevQ5xsMBuLpBpT8ljgkLl+sfy+1B90RaxlDIe1E8ztvP+O8y7eC2g8cQ3yyAaDceIm9FuSi1OFFRSqbr03euHw2LoNLN4f0A6sfjTK56sDCcfh8BloH8/kctre3YWNjY2mM5XkOjx8/hslkcqYeqD/AJ3Ix/xCfIfWYsk5Kau2fYp7HN4Ho4j20ukN9yPlvrQ+H2EgNUlyj2eCYeLFKqDtjfQ0calhcBv5ycqs8YkhCKQ8K66Sm9GysXK60WhlSYKLJxRWf9ex43CmkyakNbJex4mMiNqCyjj3fdyGyhAbr1j7g0hM+sg7rgh4ZmjII8j2jpeF5+gyuJgOtmyzLllbI0nfF+xHG43FxdxkuPKDOSMjYXJcAjSJW/pjx4sOrZMNCHfeYPuHrT9r3fDxp49YXBEhjS3PiLKDBuJWAsQQymh4MrTdNHo2Ivkh3/Wiw1pMr8KH/LEEvfufrPxa/AACK+8GzLIPNzU0YDocAAMVOQquvY/md7oaWFqjx4DtF/+DBkqXeAE5XQm9ubkKWZcVkbIw9kPLmaWJINF9dS8GvRgq8CjbFB003SeNEaheXvqf9gvdfvjtQK8tCKtF8LOliEJun5ldSWbl/6SrPKoekOzVZuB7QyCcJ9PgzidwCgGKhIrXT+HfIO2nw2QVrGgqpT2q2yJon78uhdkJKI30XE/uXsSnY9jhRtq733pflQ2LskSt/bnetfdfnQ4XIYM0j1C+v1+swm82KvlCr1WBrawt2d3eXFkY3Gg24cuUKTKdTGAwGxffj8Vi875MT/C55pe98frpGKtP2suwU5r9bxramPzVZLUjlM8bGm2WfdeWJoLFfqgUhVr6ySlh85fNGFW1L806Zly/O8Ok4HvP5fDTug/Hfted878FlceXhy1PyPaU0eZ5Dt9uFq1evLp36NxqN4IMPPvDqal5f+Hto37H6RDH54TiXTi6RZLX0AR8sp6BZfE7+Gz8O2nXcf4px5osJY+rJ96w1P1/9Bt0Z6wIPcmmlrOvRMa8SVl23VuNXhVzWoNdCTmbZywu/ad6S08PfuYzy4AYYV9RJR3lVSe7Eour+JtWzqx60oAHr8zycWAC34YoNqqnDT4/Y6/V6SysoJceGj4uLvIo8pg8i+aYFu2XyvkQ4sJ/y4w+1tPR/F3wkAs2TB9Auh61Mv5AICU3OqnW96z1o8Ka9M9oqSmavIyQC0VW3NA3dgdBoNEzHw9FyfUS3NhGB+rxer8N4PIb3338f3n77bXjzzTftLx4JHjzhSmM8bYIew11mPGA9oy7G4HM0GgWR8vV6HTY2Nordbpg3ysjBdcg66fl18u+qBLbvdDqFLHu5qExrE9ydrk32+CYSXL9Z7I6Wn5ZnVUjdP/iuf4CX9TGbzZa+d61+l/4uQ5jTCZJ6vQ7dbheyLBPvdfQRl1L+8/m80Gdcbin9OumILMuK3ctoJyyTjiHvETJJzG2qbxxS3U7Hb9mJWAAQd6NdJFh8sqoQY89p+1GE5BGSVjutwzVpSXHnzh24fv06tNtt6Pf78PDhQxgOh8XibYrRaFT4FFj3aKtonGA53l7ayYuxO8or6TFu38rEPzzmqIq7Sz1JYcl/Fc9q+Wjth5+xf2i6D/92gccEF1nHVYWUdeKKEa1XOYXClyfVQaFXr2nl0bzp/9bnYsH7vWZ3uH7d3t6Gvb09aLVa3jIajQa0Wq1i8g9PBOR8v+WdU3OkMbbc9UzZWFzLUyuHf4f1KqXBv6VJ2JTjCMcs9YVT+EwaJ6hxYgBn5xqsMqiTsa7G1YyIdVC7nquSkLB02FWTNDGDqOzkXEqFugpoAb/rO1deXHYtoPTJElqe5dmYtgkJsi2OXxXll+nnUl5a+hj5U495FynlIuO1tJoDTx20PM+LwBGf4YsMrO/nC3gvOiyE0bqRcKlR9v1iiD1tfFrHbOjYDvVDpOdjxoJvcoB+77NtobD4bT5Y9Ci3matGivEpkcAS0YIEiG+ix6VPQkAniObzObx48QIODw9hMBgUE44xBJ0Fef7yvmCUxRU0xZYr+fwY2OFJDvx3PmYajQYsFgtotVowm81gMpkErfDl8rjSp2pbCyRZzmucpQatMzoR7/LpfPm5nnPpbz52ysZVWjkxOC//g5LIdBG1a8e2zw5w0s36blguXrlBv5eIb/pZm3zAyWdpfJX1T6oCl6vRaECj0YDpdAqLxaIgoELGSigRxu2i9L1L5qrhirM4zrs9JYRMjtHx5CO0tWetZfJntN9c4yZFDMD7q49rlPLrdruwt7cHAKe7WQ8PD2E4HC6dCIKYz+dLV3dIeaOv5JJZg49zovpIam+aj1aerz9I9RlidyTfjOe1jmMtFbgPjqDvr23qkNJrZQC89MP5ZGxI7FzWnwjRURcVmn8Sozc1WNpM8l9iOJfQ9DE+cKz/LPHwHHSTUrvdhp2dnSItnvo3mUzEHbGU/1wsFjCfz6HRaIh34kp/a+/k43Z845u2pUv/a3mkjsWtz2v63OJXuuT31WcIQnSrBVqftsSVoXIk2RnrC25Dz+OuyoCvo6FYR5kuEjRnUCJ5XI4jh0Y+oKGWFGpZY8SdgFfJkU3dzzXnkgcwKaDdLbxqxDiIi8UCJpMJNBqN4u7Yi7wbNhVoIEXHM67Ee9X18rq8H69radeyBZLDG6KHrbqbk0Dcvki7VHxOmqSz+I6tqgOxi9bnUwfErvyQwKc7ZWke1M+12h5cTSr1U0k/379/H37v934PJpMJNJvNpaP3UoAe1YnyYAAMAMVRwHx8SvWYoi/RIHw2mxVEFMDprr12uw1f+tKXYDgcwvPnz+HJkyfQ7/cL++YiiTVIp5LQv1fhm9HrFVxB90Uaq5cIR8r2raKv+Mgi9Jc1+8ZtfOh44ifeUNIaiTr83Gq1YGtrC2azGZycnKzt8bVW7O3twcbGBhwfH8N4PC7uDrcuvqRt4YpjXaD1TT9T+Hwqfg+76wg7izwx9xpfdGhEsq8NQ9rYmj4kHebv6nMu0pb3W6mPLRaLpbtiEa1WC/I8h7/8y7+Ek5MTmE6nxW5zjJNRv9B4meo0fqKAJLuVLwjRfa7YSPIlfZwBj30vYYfWHzVeSvpbS0MX2Ujgvqqvr136i+WwSs6Mj/EYPkTL97w4ZZ/89K73PM9hMpkUfbrb7cKdO3dge3sbrl69WvCYWZbBbDaDH/7wh3B0dLQ0XlD30/z5oj4Oy2Rk1fCN5Zi41oWQOQuXLZHSWvot918s/VxKYzmhAp8NeVctLc0H/+f2M3TMlpqMLUOAUqSYGQ/pKJewwRfQcVSt7F15S7KGKJrYlQ6WiVhf/6a/V1F/MePKKotVgZbRCygP/45OpEnyhJYplSdNVsSQFq58Q/Ogebn6H51sROCuI+k+gtST2KlQFfmM+bpIb47LoMYPVx1J9S19tjpxoe0R+pwvsHaV4UprlWOVEy/SeNDeHyfK1lFfAIRNtvp0si8Aig0ifLaMfj+dTuHFixdQr9cL/c3vsdfKkWTU0mgTw51Ox0tASnlrEy6avybZNElOnCjGyZYyRH5ZWG25Zmv4O1smqS7qhKwUxEpjlf5m9bMsMWBoTLNOCLE7lvRa/nl+ei8XTk4sFgsYDAZFmhCf1aU7Xb66RnRLMruulMF80Fa5jpq3QvO5Xb5C2b6F75LnuXdhpcuvjZXDF3tb5NZsDv0b019E3ZYCkg6j9WJtP+5PWHxS/E0aU2X0ZhkeIba/8vfIstPJxvF4DPP5HObzOfR6PRgOh9BqtaDVahXlW2Jjbp98/TWVzSnLc9J6tuhWTZdwXy2FbBcdIX3Axzvgb61WC5rNZnF3Mf0tlW3h8r1O7ebi/GLg4ilj87LEA64xzGGRLWYs+/wALS23U1zXNhoN2NzchG63W/g+dGJ1MpksnWIg6SafnCFpXWlC7LMGqb1dvnJZhLS1xIdr6Vy8Slm7zuXV4uqQ2NzyHS0zdb7J7oyVCiobHF7iYiGU/PDlQQdzzODVBqO0+4rvoj2vvsqdZfxu3WANQmKBO4d8K9IkkrkM4YBEb8gzWG4ZxMitjS0MJvHIRyTUAE5J9Xa7Db1ez7wSNvbdUpEqrkC2TP6utqvadr3OhFNKWEgRqkctO2+5w+kiwlykJy8jZIzzXZbr1lfq9Tq02+2C3HoVQftWaDtwUjmWrMuy5eOWMBCVjmaKyV+SmaPVasG9e/dgNpvB48ePiyOBy9h/7mvxYwJpmir9DF6eNGZT2HafL0uJUR+xso7+YAjwPfjxqi47r9ljPjZDSaRV9a11BOrtLMuKRR54R/TXvvY1uHr1Kty6dQsODw/hu9/9LgCc+o/T6TRI90h6ULurl44P3C3RbDaD24n2i9lsBoPB4MxpAqETW9bvUwHzxnrAXX4uQlYi76i89H8N/Fk6oWWFdIKCNGa197DIVRZaHa1SH/gmObGeaEyX57l3l48LLh9ZsjEW8tP1Dq7feR70HmtXP3HVG/eZUIccHx/DZDKBk5OTYnKr2WzCm2++WegY1BXz+bxY4JXn8t2wMX4h9XN8i0Is41Qr2+crSM/gCWDSe9LnKC9Thm+JgWV8rkJfazEfr78Ykp/u6Lt37x688cYb8LOf/Qz29/eLu84lf3kVWFfeooxcIc/G8tAhv0t8ps9m+vJ0jWkNrvjVN7FmrU++oEw7yeDGjRumPl+r1Ux3ylKETrjG5gHgrxd6RQ/Aqe/qOn1kFQjtdz795ivL0nd4OroxQJNhlfUXY6Mqm4yNeXHuXITkkZq0XFcSlILKVlVHC3HmyqSrOi8pKMTPGmJ/szzjU1iaE7cqheIK5lGWVcrha4sQeWL0S0jdW/J3OTAxeo/nw50lPPbGeuypFICFouo+kqLNrXre1w9dZIYr/SVkxARXtC15/7fqMs1O+Mr0ycO/dwVgWr6rIH5d5V80WMa2q8/4gM9odeebZNLKkvrjqsDrARfzbG5uFsfdhxzlbrWBPjKX/43H7+PEUOxutxg/P6av8OdCy62iH5x3jOPyMWL1j2syQbLhoT7uqvViiM/te3fXc/SYOACAra0t2Nvbgxs3bizlg+N/sVgU92/hjh16zJxLPh8oAWnZjW/1t32EZozcVj0YGrtp/gGWl2WnpyX4FsXQupRkkYjfVYDazlREZowMVZcRIwNCsjG4OwgX2PZ6vWTxrysPy28hMmj9z5q31aeiz+O9gXx3PJL/eZ4Xi1FonUr9U7sCS/usyZc6TrTaCzqxgf/TRRcuXajZUJ+NSQGrrk0tg8sXcPktln5K86nX67C7u1vY462trWKhgJSvdP+5FTHPVVG3KVBmnLh+S5mvCy496PJbLHrZGsuG5G3NV0tL89b8xnq9DleuXCnGw3w+h+FwCI1Go5iw5JO3Ph8vVP4y/pA0Zl1twnWyzwalRqgNo5B0YIh/F6JXuO3U4tgyvq3VL7M+Kz3Dv3NOxnJDEaqYeEd6FQi+1wllg+qqyrc+6xvgPmfaRRDSoJjnl0KBXo4VHVjn/J67qldr0t1qZcdAVZMtNFiiASfuCsD7cC7710uEOHCWvM47MLnoKEsQao6PFBDEtFWKviL1E8mhjZWxaqB+eRX0SBlSgrYNnzSwtBudJAhpa26HUts8zL/ZbEKn04G9vT0YjUbFztiYu52pjKETgXwyZT6fFztchsPh0m4Cy8RLCKS6jR2TKdupKltT9Zjm7RN6L3ZK0LtHQ2KAVw24ewzv6rp+/TrcvXsX3nzzTWg0GsWEbafTKcZ/vV6HVqu19DkWfFwg+ZbnOQyHQ5PuoPFYCp+hCsT0J/peSNjX63U4Pj6G4XBY7JIM0XmhpDq2P5bDJ3As8J1+cB4IHePnpRNQP926dQt2d3chyzIYjUbw4YcfFgsjUL4Q8D7j43wkO6gdle0rl+arlYn9HcvBCVVJDqof6A5CDpzcwonX4+NjAIAzu/153WAZmD/9XnvG9/4ucPm53YwljOlpAxTT6XTpOem4Zs69XOIU1naX2oz6HhsbG/DOO+9Au92GbrertjHa2ot+//l5gvuf+B3v96vU95a7gH2L3ujznKMGWP0Evgt8ESBFt9uFb3zjG4Wu6vf7sL+/DwAvx9F8Pl86ohgACt2P6VZ53y/CVaY0H4Z2LgVc8xa+NKlh1YmaX+jLO3SC1ZU+Ff8ei6CdsSGERJkJqRSTWKlIj/MOFqyI6cwayjqTZeqqTL+R6kAjvEPeMZRwtOa/ij7lev/QCcGqVuloesUVGPpksMgqTX7g35wcT9VWZepOm/TX8pfeT/rd4tSFThZcVGh9y/rer3r9pEDIBIwvD4lAd5XH+7CFXONET0pn1zXpuqq+5NIDmm7GnfZZlkGz2UxyF19VkPSlxYZIOpPCVW88/xR+DQbn0+l0aSFSGYKbp7WMBU02CywTm76xlmWnxwm+ePECptNpcaxgiI4ObQfNV0gNuktHIoNTwJVn1X5raGwRo59TgpdflU52jQmUgY9V7ZkU9SGReIhGowHtdhtarVYxUWvtq1yuVLG+Ky+cPEl1VyzCEitpz2rfSc+ibd3Y2IBOpwMAAMPhsFgQE4rQZ/gkqvQ3jw+0cVNVDGlFSBtKWGXMTo/svXXrFly9erWYeD88PCwWKSChHUJOusaKJI/Ubpp+CPWXNBuvfYf93scbUBua53mx0ITqgTzPYTweF/kipIV1mn7DeijDm0myl+1rVj/QdyQ0R8j7vmqQ7JZVj/u+p7+3223Y29sr8sejV/E0CqvNfR3byAVrzGdFjD2z8Jv4u9X/12Lc84JPHmq3AKA4sYC+73w+h+Pj42Kh0XA4LH5bLBYwGo2KBSRS/FqlnrLUs9TOFllcdkjL15fGxYtZOLSY/K15SP6Fpa9LeWmfLbJIPrt1nsH6PeYrwTwZW9aJjcHravDXHSkcRR9C294ijzbgy5Szjv0T666qNlrluESFGLu6ySerRLKGkNkpQct16VsfCeQjRsrI9Trg0u5cTFjGemigIxHcHJLecOUnObDrOLZc74sEMd5huu4TsVQnxhLCvF1D8vFNlLjanxKt8/lc3MGRAhehD6KMs9kMnj59WiwKmEwmxQ4aC9ZJx9P+4NrpQPvwusi+CmgTWbSt+YTRq1I/lHQNGZ9VxE+IZrMJm5ubxXFxmowWcphOKJbRPxoZh3LxXV/noeuob66RWJJcaGtv374Nt27dghcvXsDJyYla36ljP3p3potApvaR20bXfW8ufyklLpJOoHp+sVjAW2+9Bb/4i78IH330ETx79gweP35c7Iqmdo+Pe+md+SQ+/Vvz46TJSI3AxP9jJyh8fpWPE+DjDCdgcaFTq9Va6tODweBMv5X6Y6ye0upIK4NOINP3tcI3ycPLpXXjwqrG6bqhKs7PVZ/dbheuX79elNdutwHgZd/NssvdybFw+SDrBM03COE06XeSHgl5b8uzVrnq9frSvd7T6XTpdCOA0xhvf39/accoHTO9Xg+Gw+EZWyDZJp/vVzamsk4G+srQTn4og6r9Hp+90b7z9T2X3uVl8h3lsfJxhMQMZXXI0mSs5ARpjpEmpORoWJVHzAx9arjq4BI2lOn8PH0I0WmViypq3z0ZZQkCl/wxk1xS/wwdJ1aZfBOCVY1PV73wcclJIE0XSfVFn0HnNoagd/0WU0fWfiHlrxGXmH42mxXkjjQhVUWfXSeEOpA0jYXsvaj1clERW9++nQSSbpGOKaf2w0VGavraRVCuGlxml62hv60zKZDCfnPyPNSJ98mm6VTum5yHP4zlIqGpHefqg6Z3Y20k3h9kJQj5pM95jS+p7iSd4SOkU8q/apvO/QwL+a597xuPViKV63PpWen5VP5EFfDVK09DdQzeRzoejwuSjE6C4HUXVhnweW53XYQN6p3xeOzUExpJKU1mWOokFtK45bJZCR18HicxsyyD8XgMx8fH0O/3YTweB9cH/43KHfuePC+ep0+XpRoD52EbV4Futwt7e3tw/fp1uHr1Kjx+/Bh6vd5Sv8DYFcHjWdc4jfFtpD6EzwP4j6522V+pv7om8n3PS+XmeV6cpKHpFYvdDakrKX2sDYgZu/iuuLNSgxSv4PMh7cDlvKiw8MF8/CFc7cv7XqvVgi996UuwsbEBu7u7xQkIn332GTx48AAODg4A4OXYkuKBV1UHrgLUl/TxhQDuyR9LWdpnvmufl4tpXL6vK26nMSVPXwYaj6j5H6iDtee3t7eh1WpBlp0eJ9/r9c48j8cTx/rgXCbX+7jylLieGN3umoT12VwOi/3SOCnpGc0u+MqQvtP6p2ZfJL/C5UNoMY1PlhAeS2sPSXZrDC/ujA1pcCmolp6xkC6xQbkmc0zg5SMmeENbZDkPVBV0lnm3mGDM4nhKbaa9v+RIWftlCHxjQUJI/3bBZaS5kQlpEyupEwrtWdqu1t9W4ZCuw/gGCJuQlwxaqDPvIpsuMnwBl4WskGwhd2Behbp6FWANuOhvksPNn9McRNczWjmh/a8MLPZT8/HQiV1HIkDzY2kd++oW01jqXQvmY2Sm/iU/gtfq2/hktaShbRx6nJ2rPCm4C5EfJ2nw3kqpHKkP83qMiQ20z6HkrCVN1TYjpU6xxGDau8X2K61sK+iY1eRMjar1OYfUz30EFO42H4/HS3dyIWlSr9ejCQzr99gn6I4JTae77lGLJcdWDR9xPx6PodfrFcfzuXYxWj+H+LaUYHWNdZ63VqYV6+hbrAp4XOmVK1dgd3cXut3u0n2f0qJCq6+iffb1H+k7yZ7TtFzH+vQPLUObPPSNaSk/aUGli6sM4UJdtiOmTWi+KcZAlmVLu9G0sqXyXOQ3TcPzuuhw9Wf6u8uXdNUbTkC0Wi344he/uLQDFgDg0aNH8P777xfp0Qc/jwWwko5Yd5uK8LWNFjdIOmYVviEHLZNzeD55LLytZexaeWFXDEx5Mtdk7NbWVnEk/2w2K+71pnDdN0vLCpVfg+V57W9Lni4eRdI5vBxJR7tk8MmncUC+PMr0R+szLntE5bXq5JC01J/xcQIaJPmXLLPFmZAKDxmk1g76qhj0ixAEviqwGiYaXHIHHXHRjz2zBDspUXU/98mODqolaACQ6wf7wTrv9JKgBU9lcJH7fix8dqcMkX+J9UMouWAlWXg/oauZNVj70nn7E+ddfln49CR+5jueXWQV2h3fTjFNp563LpHeDWUdjUawWCzg6dOnxaRw7JUBFGXemY5LnJTlAWnK+lz15FlqW35ewHa4qPKfJ0LjZBepGIrFYgFPnjyByWQCR0dHcHh4WKzaPzo6glarBe12e+n+RdcR4aH3WXJQ0s43YcjTrVqvxvZ3l97C3waDQaGPaQxLy7bIQAlR/Ezzs9SZj5iTCMLXMaaIAd4VO51O1eNjsV7579h+WL849ujvFFZC1kUUu8hPLR6X5ODfcZ3i0yEuMpUedYnf8R1mrjHjmnyK0W2a/2fJi6bxTYpp8lKOA+/T9ukM10Lu1xnYHlbdj30N6523xWAwgH6/D4eHh3B0dAQAp2MhdGdylbByresiL0LThb7JHYx5eDrKOZaJZ0Lif+l56W/pudTt4YqPuazUV6Tf0R2h2M9fvHhRyCrZOCvPG4t167ccWCeh/n7smLROVLqed00au9Lzd6TPu+xlWZklUD3gKy+0fOedsSEKghM0LkFcCjGkXKm8kInk2PxCnl33QS1hlUGTpcNyZ9nnkEvGTSItqCPLlRR1fKQBH9I/LIrHhzLtsKr2LJt3WQJFaxNLf7E6MWWcLt7nfLK58qF5hMAqf9Vk1ro46paxodkoyQnl9Wa1g5eQEdJPquxTvr7hI3J8/YGWw/OS+lQV71lGF60bXPpLI+753zw/zQcI8ZO5H0N9DwuBzmVKBUreUqIS7zDt9/tFmanKddlpyd5K/lqen66wthJVvrEjyRDazlUjNAA/T1jbJOR7zDe1n6iVkxoxdUL7fohus4Ln1e/3oVarwWw2K46Io0cG1+v1JSKZ39kVQk5b5Qrx6635hupcH3xxROhzFHjvLfYBbQK8zJjD533EklY3Wr+15rUuOvY8IBH9SPbn+fLpFDSNRlCWHXs0TyR/Jf4EP2vPU33A83CBczAuuGwIr1f823IkKP1O0j8aGazl4+KS+G8Wu2jxCyVZ6AJR2qesvpELqxjL5x0X8jYLjc3Qx6a+K7bJeDwu0uHfvl2Ase+Qyj7H2qFVwjVOrZwY1SVVb9oI8U81XSz1S02PlZGR+wzWY7tR/yBwLIxGIyfn4jt63/WZf++K+SzQ9HmV/d/n+/s477JlIUJ0sJZfiM/MbXnou2hjQPJ5ff2GPufzl2l6LV9xMrZKAt6Ki+CclyEELmFHqFKztknISj+q/DUjXOVkQChcu0R9Y2ud3sOCWAPqI96rMKop65YGmtSZlxyeMkTWeRGS6wCfnCHvwZ2+S/vhR0j9xjiGIWmtTul8Pl+6kyp1EL1OKHN07SrBxxvKHNs29J1x9wndOUp3g9HfcFUw1wVWQi8lJJtw7949uHv3Lnz44Yfw/Plz9dmqV+lrbaMRvPQ3mkdKW5siT8tYubQLLyERJRoBRKH5Q3yigrYrT3veCJVJ81ktfQl34kmxTa1Wg263C5PJpFic4ZKX20z6j08alYGkz7k8tD5Cy11FX9DsTyrCN0SXuOJBSdfSZ3wEqE9vXrSYMzXQN8jz/MxJDwCniyMODg5gf38fDg4OzoxDrtfo97QMH6T+aJkUlN7HRTy6wG1trJ7QdDu3Hy5d6ZM/ltPUeBnu1/BJcE1ui4yLxcKpvy8SqogLrZDsGy9La19cxIS7AWu1Gty+fRs6nQ48ffq0uHYDn6/VasXR0qkn/1z8Fx6FTtNMp9Ol/vOq6WzJh3DpsBTvHqPbXDGrdoJBGQ6C6x4LfHw5TsJmWbZ0zcxsNltqB+1Y9XVDVfEaveOb+oR0HIba2arjSyoL749lx5LvBAcXfBP4fMe2FWX9E0RlPZ0HSvR7DalJiqomU2JkceVXhbPAnYIQsi9k8FQ9cV9msiNF+RK0vk0/WwgjV74h0NpC6ltaEC09a93tnhqhbceDEy2gctVPaBCmIaRsqRzu6Kcmk0PSV+2I+spOndb3vG9SxEfclh0fnDi8JOSrg4Uc1OBrGx7UWfqYtWzMw/XsKgNkzQ6uCyT7V4WOxcmfMv6tlCd/rgq9QPNsNBrQbreh2+1Ct9stdmNhkIwkkqb7LHUZ68dL+biIAqssvrLK1HesL7Mq+PzDVcFHvLv8kFByWiqbyiDlbZUnplxrXjGTG1a/FskxxGQygdFoBFl2ukuBPotpJeIvZvy42s6nr7X2ifHnffFjTB6u+nHF6PzZMv3NNa4sfTA2NpL85nXyXdYN2E7oRzSbTWi327CxsQHj8Rj29/fh+PgYhsOhuquTjxX8XoKrXa1tEWMnfL4z/2z111zxfEg8q6W11EkqX9KaJpQfs/rrZWzqusYCq4LFN5Z0e6/Xg3q9DhsbG8V3ZSYfrKCy4CJLvtiSLmxbt/YNjeVdeoJD0wNl5NT6AS1T+pt+LsMTa7K50lltCE/r8+ulvDkPpvFvLoSMGV97uGTE31KNCam9kYdH/W3hKF2wyhrSZtr31rJi+nVZngzTWPoopk3pE/G8GtKPMR1L2okn5RdKtL/qqMLQvs4BDaJsYEeP35IUJK5wkwzEquu/iiD2IvehUJIrNL0LFqUeW541iNXyp84g/22d7sn11Quf3DpP0Lp03asUmuerhFeRZOPj3HJfNb+/qyq5Vg0r2boqWUICRw3YvvSfr315UCeRnfR+WZrGdbfbKv3g58+fw3w+hzt37sBbb70F77//PhwfH8OjR4+WZImZGMLn8P8ykzSpkNL2Y36u/mclElwEUiqcp66g5bve1Wfn+Q50C1me53nh13P/nRIfqUg4noeP7DqPuBfrD1eHP3jwYEkuGg/N53MYDAZLz1sIN+vdsXxMliW7eNna+EqNmDFL9SqXE+8SreqkjSr6u4tIvQj8zip9Glof3W4X7t69C/fu3YNf+IVfgB/+8Ifwp3/6p8Xu9VqtBvV6Xdw5KeXra1uNq7PaLJ5P2TqjvhLKoN2fi7L68sN/IT6M5pO5ZDlP+OwIn5C12N6qsA7xQlmEyC/14cViAY8fPz7Dm+OOWF8/S1WHmE+z2TxjX6bTKYxGI2g2m9BsNou7btcF0uQfB43fcEGpy/eXxkCZei4bN7nyBDjL3/F6iJ2c1KCdwqflR/Wu1Kep/8nv9+bp1gHSnECZSdIsy8RdwLxvTyaT4LFXxUQtwPLdv9yWlOFCQ95PSquNMWvsJcntunPeCt9YOTMZqxlw/N0VGOEkFUITtkoj7Mo7pFPS/Oiz/PvQZ2NkSIGyytjV5rzO10VhuoKQmP4XEsDH1EFsvVkDCiuRHJpmHdrbpZcs7cTHqNbWof0mZd1oAXOVOC995UKVAZzPvlmcqpBJlSoIsHXDOgfcVtksthztTavVKogyDPx85YX4RJb8zgupJ7dCUdauW5/npB7Pz5UnbWuX75SKCLAC32k2m0G324WdnZ1iV06/309OKFh0n9V2h+jZsoQjJXY0UluT1SWXy9aGkMZaXqkJJk2GdbNpPmJqlXY5xM8u44e4nrcAd8Jjea77G0Pslksmrc+6CFbpb5qX5tevm90EcJPCCL44uAzpmtKPsPRZKe06twfAauXC+sAyZ7MZ9Pt92N/fh6OjIxgMBkv92mWHrTrCJ4/lu7Lw6Waf3rDk79LnvA+GkrlamTG+nM+n0PxGq13xjU9LPqn423Ud8xQ+mxcbt9F8pMkpXHhjlS8GEsclHSvKFxrTBWurhKWuLX3citAYQSvb5/OU4ZG1PuXyJSw+mMRt8DT4vYVXjpm04mWnmLfRvnPlr+XhkstVJ1Q2yfZgGun9tXiuqrHo8i9iIPWXUP1p1bmh/m1IPYbWt4UzEY8p9hEbGur1OrTb7eLzeDxWV/a4AqyLYKQRMcqmaqy6Di9Se5UBOisUPpJAS6MhtWK1GKV16rtlkKofouLEtj6v4COWWNHyKeMUvi7gbW8B1pt154eLYOTpaP6XuBjY3d2F27dvw4sXL+Dk5AT6/T5Mp9Ni5WOV7Vm17X/Vbb1PR1Knmvu2OK5dq4Yxf3rXrs8XXsWdw3jPLZZ19epVuHXrFjx8+BDq9TqMRiPvfWPn0Tey7OyKYil+Sa1LXwXdvKr2uvQt7MDxLsUaFHySwjc5E4t6ve4cT/Q7V9xTZieNZaxZJ3AvMrSdCHRxCIWLJCy7s4nulKYTxGgTQ8nQV7G9QoE+Qb1eh9lsBvv7+/D48WP40Y9+dKa+EZy85f2ATzpyAvci1Dl9Nx/BKukKzYeSJqN8OtTiT8RM4vDPvnxD2y2lD3kR+syqYdG9/HtsE8mvXpW/xPXDcDg8k6bRaECn0wGA0/fE3Yur3iEb2+9CxgufcC4LCxeE493SZ3z5hNQRbzvtnSU9mRI+P/e8YOkDaLMtcbprglva2Sx9p+Xp+s5Sh1rekq8hPcvlDemzFljnXqTYydou540zO2P5jL80U8+fwd/xYmF0GDGQ44EDJbG4Q+gL3K0kSOoKdgUXZfKpQtlU0blCJxQlxLyrVO+aspH6jtZuISsgpEA/JJ/Qto4lr0JXi1jSrouisgQ/CO0dXel841sKBlOAl5ty0pwH5TwfKQiU5ON24XWDT4dYg2/f85dYLWL0m/QMHxu1Wk087omuMNZIMkv5WKb0uaq+FGJz1w3cQXf5EhQ8HQ0eeCARqqMtizY4LMFIGWiTO1hOvV6Hra0tmM/nMB6PzfVohaUvUTtstU3SWAlpJ1eeoXCRydqklks/pPATJfK+THum8l21duLtGKtLzxuhBCGfTMHvJPDvQ+ukXq+X0usWe0Tbt+x4wrxcfTn2fVLG6pxPkeR2PafB5ZeE+heaftD0k/Ydfk8Jy4syNs8byJNRnwXJURd5r7W1q11pGhcsdlPKU9JfFlThV2K9lc07hqcsE79b/fsyZVziLDSOSOpDoXVJ/Wru12LMUlX7oOz1eh1qtRpMp9Mz79PpdODatWswGo3g5OTkzMIs+g7ngRAfXptjsIwrix+h8c6+vF0+UIi+5bGpTybfO6TgOy36MfS5mPzKwOf7peA/pFNneFtY+5OUxseZa7+7xgaVU+tXnBNzyemqQ0nXNhqNM3lSjkd6D0yjvZtU95LPKrVFSF/X0i5NxuLL0XsouDPFgYYEj+Sbz+fQbreLM+855vN5sfrmIgXOrzpelbZIGTjzfDAYSnU35CXKo2pnlX93UcbIRZFz3eFrc2rcrUcLabhss/WAdUJIcuRwwlVyFrFvNBoNJ6HGkdqmvW5oNBqwWCxgPp+bSS2XE85JlFUgJiCz5Gkh2hHtdhvu3r0L/X4fnj59GkTKW2GZnKAnVljzo581hNQvj5Us8I133gZlyb51h6RHefuHEGA8Xw2uSSHU37Ts8wSV1dV/XDYpBs1mEwBO74sLkdX1vY8U0mB9D5/OwPpZBztK29LXV0PytBBHrudpPtrY1EheXhadiA19l9cdeZ4XGxuazeaZOuQ7kBGxdRwyxkLHT2xMZCknpH8vFgtRFj5uQt7PVR8uAt9XjjQRK6Wx2Ab8/XL8xUOaZAgl5TloHEFtE128UjVarRY0m82Cu6fY3t6Gb37zm/DZZ5/Bs2fPoNVqld4xel79MERPhvgkoRNjKRDqE1ifl+x6zAIDK6+hYR38s5D3dk1WWmIJPPHE9d5ljiy3xMDc3rjkpqd6pYQW40nvjnqLYj6fQ7/fLz7zKz3o9xyab1BW34X0I/HOWHrcom9Wlx6fQtPMZrNi1Q1FrVaDVqtVfOZGIBXJxPOyBvjWvCl8Qd55OkJWZZxSPq1+XUorRXmuPFMELRbnuez7VGWIqjRwqQggS/6WdkP9xeXRDKSL/AwNCKuA1nd9BjZE71jelaaJJdbKogpd6nOssUwr+V+v14MWbEi2ySIvLfMS8UD7Le2cpH0jNFjZ2toqjnQaDocwGo2STdaXHQcpdcO6ENwSssx9fJA29tEPbrVa0Gg0oNFowGAwgMlkouZTNgCV7BInyC2TeYjQ/oF++2g0guPjY2i1WjAajYpV87VaDbrdLty8eRN++Zd/GT7++GP46KOPxLuRLe/J5eRtEUK0xqQPhRTUWic4rPJIExqpwOu+DKlT1SSappcwL3pfmau9JYKdfufSfyH2Pga+WCHEF5CeTSG3dtQ65m05BszX30J0phbTU/jqFeWpOl7hZVoh6X2rrK6+wm0L9+HL9jNL3CDJYr3W43WDNoZ98amPh9IQmj5k/IT2f2uf4mm0d5dsdKgNx3T0f8328L9jx6zPNrl8qNg2SRFTvIrw2WlfOhe0dqsqpscy6vU6NBqNYiL4zp07S5MbuND0ww8/hKOjIwBYPq4YY4VQGxL7Xin7liaD1ZeP4b4knel7Jz7pRSeXeL8JyT9VXUq6MFbfhcoV67NYf7fISv+5dL1FN/M0ea5fU6LN37jiJpcMrnelMsTGsK7npXfEuszz0ysbNjc3i9/wtBC6S3+xWMB0Oi04CPrO1neT9K/L5kvvanlnVx9fmox1KVasIE4y44pTWjl5frpTlk6K0HxwMjbLsjN3UVkJR2vgJeWbGjHKzVXXmnLzkVihZWtIVVcuhzJFWb7+4XrP2EA05jkXyaghtm19cqRwIKV8EVjvKcuRlKuVyLL0dT7eLHrjogQeNHizOmc+xyLWyUuhV6rU4wir0ZWAthAgfNcUPi/lGSLbKuroVYLr6DeqRzhZRD9Ldb6xsQEbGxswm80gyzKYTCZRk7G0DG33dRli1VXuKonrqmCdjKXvig5/q9WCdrsNnU4HZrOZczJWyo/WodXu+wKnEMIyFDgZe3R0dOau2Hq9Dt1uF9566y34O3/n78Af//Efw/3794t+XTaY9vkMPrKQ1n1qv8l1zJLFfq567PjqSvKlQmSM9eFDiG+pLAtc/iIfj6uIHyXdjHqc7sSlv5WJKS3prMSYKz7lR6j6yvLFY9x3l57xEV+xSEkUpoSl/lx17yOLLOVrz2ixnk9GGpNkWVZqgdqrAp8N4cSvdQxUQVqnSpuSI/DlLZVjlV3Sb9KYtPATKeDivMroxsu48Syqjq01rrdqYGwDcPpOt2/fht3d3eJzo9GA/f19+MEPflDI1Wg0imfyPIfBYGDW/xach411+TcSl2jluHy8oeVdcb7Fl4+VX+a/+3g+i3zrgpTj0qrHad27xoHUVloa/FuKp116ny5SleQrC19ern4U25cWiwU0m03Y3t4u8phMJkun9aBc4/H4TJ35+DwXZxFqy6Vy+Lv78hEnY7UHUUlbkOc5jEYjVYBms1lsNa7VagWhw2fiL1dPykRCiCP5qtbdRXEeUzosKVFVv1ind3URozFkeUi6EBlXgVhdsK79t0rE1BU9Ci+VDBRI4IZM8l7CjTzPi51/OInugqVtj4+P4eOPP4bt7W3odrswHo+XVhGH5F+WTLWWE5vmoiDLMvOxwlqaxWIBs9msOGYaFxxubW0V+mIymcBoNFoql/5dNmjheYb85gLKhStMP/roI/jss8+g3+/DdDp1EubSEWcpIJH9fDKGBlWp7JQr6EVg8OvyHUJJXwn8eNWYiRSK0PjhEumA7YdE6HQ6FRd3YF/WYl86IROD1P6JRPLh/ynKCrWbVU3WpsiTTkBaiDxESFqrHJifS3fRtFIaWi+W9qblWfyt1x1W7ovW/UXnfFKNMytCjiIMkS3lRB59lscS2PbaeLq09euJVY7RLMuWdr7SIz0xlgF4ueDynXfegU8//RT+7//9v2fkHA6HRZyQClXURWjsrPlZLt7dF9P5bGsIUuaFeJ10g+Q7xfpTVrvsmlPDcqUdmmXaVcujbOzo+y2FvaO+4fb2tjrfiBzMyckJzGazM9dh8COK6Vj26S2f/2Th6GLyVidjNUhb5XESlT+PlaQJhWfQ12o1mM1mS3fV0nQu2VwV4xto1tUPVQSUvlUUvs/UgfQZmFDnvCwB7COhQlYLxCDkfVNPNFnIufOQK0YGyfHw5aG1vfQusRODVvgmW5Hw4nlbiIgqUEbJW/L26RbX9ylkSUFOrxKhegT7Wpl7BlzjJ+aewpSoclJ+FXlLdYvOGeoCi82S8qcYjUYwHA6LNkPfxteXrHVAJ2e0ALIKaHZb0y1WOaqUmcI1qadB8tvwu8ViAbVaDdrt9pIv7HofqUxOpFrld+VJ04S0Q56fTm4eHBws2Ucf4YB1kYIIDm0T386qFHWDiJnM5P1BIwVCZfA9x9uiqonYkCDcldbqn2j5Wn1Mq75x+YFldFaWnS5q1uw475v0s9ZvJXmq0KlUJt8kHV2wQP0jDaExsqZLQ0mylGl9QF1vmbjk5Vv8dStZzMddSH/OMnk3q8tecF7GIuPrAFdMTX/T6i+kzWJ+K4vUbRvCHUlpqe8spQPQj2j3leGTKcROutLxhRwAUFyHgum1WCclLvqYtei82LER6h+kjoGkvkZPr5zP5zCZTKDRaECz2SyOLcajiG/evAnHx8eizzidTmE8HieTzZUupk6q6Je+mE4Dj9OlmF3TIaH+cJnfLdxuiK6LiW98z6Sah6H5aH+7no8pU4PE48S8N3++zBgIsWEayvq3eCWSxM/j78it0Z2ytHwpf58/oj1H38kaX7j4Ggmmba70YXRU6CosVPTT6bSoGF+HmE6ncHJyspQvdeZ5+bQ8zHsVzsB5TxxgffA7eqQVAy7Ct0qn7KIjlSPkMmixZbwKgWqq+g0hUDGNNH54Gvr5POqaGonz1jeXeAlrX0i9I5baNkpqnjeqJm1WlTf6E7jLGHdMUvuJhHkZcAdM821o2tR9qSpIhFas7qzyXfP89IQWbE++s5wTnjihWKvVYD6fL6283N7eht3dXdjf34fhcFjcuRQqjw9ImuCuOZSr2WwW8nOSxBJA+YIJX155nhcLL+lE9GAwgMViUaxWDT2y2Cc3tYt8bPgmKmLKq4qgi0GsLtAmJtdNr1gCZAQnTvjY5f3EUi7PD20BxrExBFwKZNnpYuXFYrGkBxDShKSvfVfZ9vV6HTqdTqGvQif8AOIn49etj0tAO0N39dOxwNtX8ws0kkcjcrWxhmUAvLQ/PgKa2xUA98IYOnFEP1/0+LZqSPV0Efo4x3naH9pf+akWCBenGCK7Nr64PClBF5RaT4B53bFO8ex5jYvf/M3fhO985zuwubkJ9XodxuPxGX650+kUv6+KdwdYTZ1YJzBDZNHSoo3lk0qh+XCsCz903liVfcH4O3VZqSZQpXx8viP/rmy/D60bWt7Ozg60223Y3Nws8pnNZjAej6FerxcTsZL8vsXjLpktMlp8sNjx6N0ZKwW9XBBpspYCd8BSzGazpbtiLeABgCav9l1VRsSXr4sYCQ04pXxp4ORz+mLrwDLJ63tGa5vXxXGMUXCYvkwd+SaJpbT0t7Ljx0USaGPDBZfRlQiC1P2rinxj9EFI+os6xlzk0bo5Q7GkuYuU4v3ZN6Ei5XMRiZuqwesT4CVJGOOXUND2xOCLrrbX2kebfJLSxxLVkpzae2gI8b3WAUhk42QhwLLPhGk48vx0gh4nY/FqDf7+eFwx7k5oNpsiaeEi+lAm/r82/hFlSQLJxtMyuO3Glah4RwsPflJNtvj6ly/msJThgmTjXX5MCoS88yrKriJfnx4KjTMsMY/2HH82y14u0qkKfAxLuhm/o+SnNY7z2Q4XQnwYX94ovyQrf3cryRGCVceVFt2Aaejx06nicwmuWMhni6i8Wnre16R0Lj26SmK/alRNBFt9CcS6+mKWerKMJd8Y08aVy576dKpFT/nGVwh4/vTkEVoOxiuLxaIgqzHmiOkHtI1elfF5iZfIsqyIV27evAnvvvsuAJz2H7yW5OjoCHq9HuT5y4WgZfnq80QKfajFaZbnfLZO0+9WXanxSDFccxVpY2D140PSW/PQYhJX3VrimFi5fHD1n5D3k/KVxr0l5nLFbDQPOjayLIN2uw2tVquwZQBw5tRcfqUp/S22D7t8ilAO2sK3cSxNxvIdA9pODiS2+O/SsUuNRgO+/vWvw8bGxtL3jx8/ho8++uhMetoAGnhQwxE6EFYduCGsJAJX5LgKDo+WoM/zursIAU9V9b9qh1JTgCna4Lz6aBlUHaCGlC0ZTkqIrGvwel5Y9/6WyrlO2e7cGUZi10KcWNI0m81C91/eHVseuMKu2WxCs9mEr3/969DpdOCTTz6Bfr8PBwcHZ+7zCOkvw+EQ8jwv7tjRnEf+Ny+r7Fgs28dTE+XrCOl+Rs3PpZjP5/Ds2TPY3NyEO3fuwNbWFly/fh329/fh+fPnxULEkKN0qU3CZ0OCcjrRxPuvFXTSOc9fTkofHh7C48eP4cMPPzwzPlLBN8nD0/JxWbXdyvPlnZOpbGWVEzO0DGlyJiWsetJKOFnTWNLT98fxhYQj3pc2nU6L3fQxZfuANofueKVtgnYd7RM9ArwMfO0SMhGbZel2wGvjp8yYXlffFY+BDDnqMTY+0fwLbWJUAk/LiWX832ejpHdY1zaKwavqF503YmyrjwiV9LqPi3P57q6yUgAXA16/fh263W7xfa1Wg9FoBE+ePCl81U6nA+12G/r9fmFfQuvwsi+/WsAYJs/zoo90u13I83ypPwGc9vP9/X349//+38PR0ZF6otzr1kdCuHr8n8d0tC65Hx5SVojuOU8O9lWAxN2X1f10/ir0WReoL0Zh2alpKTu1XaO+JM0bbZhUz5PJBKbTKUwmk8IuWucLfQht15AJaAu85/ChEsHAkH7Pg0g83oAflzuZTKDVasHm5maRx+bmJuzu7i5NICJxaTmSB6GRmNLMv0uBhjosVQYSkrxcProKjqaVlAcPnCx1FiqrBK39XJ04Vb2ueiLWV35s/fqMfsizFhmsBBmfLLCUnxKuVSxaGkufCCERU8M36eHSf5b6wM9W+ddlQraqsazVccrJqxBZLBOySBpjOSkWeryOkBZh4HFMrVYLRqPRmYme0H6B9/KgP+QinenvUh906TWus7XJKav8luBjXXRDCHzjhb8Trc/5fA7T6XSprehvdIGcZUEhz5/KqKXT/Daelk4QhupOzV7gZBBOytJ+fR79wDdGyuTjSsv/ttppipBJCd72XF5pHLp+5zpP63tlbGCoD7UqwojWpTZR5SIaq9R5ms6m/4fUUYr69JVpKcPlY4Xqppgxn6LNrGScVBbVE/ivXq/DbDY7Y+9d9RFru7VnaV3iaQ7T6XRpgZ+PR+B5xcSklzhFjE14lZDCBoRwJVzXumyf1hZWe6mlC9F/WD/NZhO2t7eLU15OTk7g6dOnS7sX+XUrvnwv8eqBtjtdyNVoNGBnZ6f43Ol0IMsymM1mRb/BRZf9fh8AYGnhtxafItahP5X1XUP8kTLllPHnXPqLwxcfVImU/aEq+0Dhi8VTyKHlwe2EFrdpfpn0Gy9P+j20jWJtNZWB/wM4XQCO91ejTgI41T+on3BHLP5N87WUHSon/T8k35DxiFiajKX3KyLJhGi320u7MJGYarfbS2larRb0+/1i1edsNoOf/exnsLW1Bd/+9reh1WoBAMD169fh6tWr0G63odlsFmm///3vw/HxcZEuNThBFesclYFlUEnPZFl2ZnWNdK/deDx2kr6aPJcoj7LkRZn0q8Q6ywbgdxolrGr8nzcu4kTKqlC2X4cQ2tay6OqvWq0GrVZryVG5nJQNAzp06PghCcl3EeLuN/RP8HsrptMpzGazM4QslsMneq2kDKbnz/km3/L85erckNWSWj/lx6Gts/5EkiEmWJnNZjAYDGA6nRa73fmpLPP5HAaDAbTb7TOrzbV8Q9IgiU/Lo+1J24D+i20LGuign0/HgSZ/lUe8YhnYz8vsFrTqaZ/PXGVfxzbn9eqakPVN1EpYx/G6bkjl72L/1XaVon2gaUImalIiZAJQSuua8JbShkxKWH7H+LjqU0RCJjzognU8LcM1nqsElru5uQnb29twdHRUnOYRQ0BJPsClbrGD2jP0V14HlJ3UsOZL9YKWLnQitkoehPpxiHq9Dt/85jeLjSwPHjyAv/mbv4FGowGbm5swHA6LSbRLvN7ARaE0btjZ2YGvf/3rRZ+6ceMGNJtN6PV66kkNs9msOLLY4t9bFy9dRPh2M7relT6bkie+RDpYJjbLgut0vnkxFtxvw0lNKV0ZpOBIJX/8xo0bsLW1VSwoOjk5Ka5FwueqkkmDdjKAS4bY+hXvjKVkBzYwXlqMDiMle3jQg3dmTafTJQW+u7u7NHmLL0sdm1u3bkG324UXL16caTAsn36WlKEWsFmcsyoGYOxKAu7wcdkWi0VBENLdUkjg4Gc86oofJaTVR6o68E0Ir8roWILb2DaiZfD8+G8pV6BUoWS1sn3tFfJ+lrRSGh+JrqWzECWuiYSQybXzQGrHd1UkEIB9pdp51XsICcr1qo+c5LDop9lsVgRX1BZz5+aSAJPhm6hsNBrQbDah0WhAu92GK1euLB1ZqfUHSX9Y9AmVS5PJlQ+Xx2UTQscQ173rqPtCQevCN0b4eEb/V/KjaDpcxUkn3H3gbaz5aVpbWvVKWb8L75Ta39+HVqsF+/v7xcK/shMI+HxIHnxMWn0Ll7zSOKf58t+k4635cxpcz/A+4JJXmsxxIbWfHwIXuR1CkseWac1HI+5TIWYsWnVJ1bqa90VaV1RXoR7E8RHjq4fqFd63Q3aJlYE1b4zZ6U4j+r+ke6z21xIvakclck4nFtzeSP7Qq+BLpIK1rte5zlL4mIhQ2xTCU4T4fJjeZzs0P9zlW/jGmsSzAgDcuXMH7ty5AxsbG5DnOXz22Wfw5MmTwlfgi9NC4tdLvDrAdsfTLOmiDlxIurOzA7u7u7BYLODRo0fw+PFj6Pf7MBgM4Pnz58UzyDWE+ubrilD+UeLcXflZeUpM69KZFu7XwpG6sGoufp3g0/dl83OlSc3VS+0o8VIh/SVkrPi+86FWqxULFAGgOIGLT1RLd8WGyBL6Lq7fU4+ZpclY+pJ47AVWCv7rdDpLk394XG673S5WerbbbWi323B8fLy0u+TevXtn7o59/vw5HB0dAcCp4v/a174GvV4Pvve97xXPFsJ67u9CnCfZEIIYp5E6cLxNENJu2dlstrTy6bzq5ryCsbJB5nkAlax0FPUlZFgmLQDWvz+cN2lx3uVfdPic81AsFgsYjUbFbk7puOKYO9wuYjtXEUBkWVbsasSjm7a3t+HFixfw85///Mz97Fwey/0fIc6+a8Io1SSF9Vluh84TKfqr1Y5SAptOyLown89hPB4vESC8LbVgGtNwIgTTcSI9pD3o87E7SqfTKezv78PTp0/hr//6r5eOY6a6KBYWm5yi7/MJDvxbm4jlASydfMJFqKl2BPPTiVz58n60aj2e2odyjUvt/Xw6VSMqtHIw3UWziecpr9Rui8XiTLwZO0kSKgeWTyeBqe0O6V+pgSco+PqtNKkixTY+8L5MxwJ/X3pHcQxi5LvEJTSk7j80JgtdBBOqG6Qxiz4bv+6NA7lX+lyWZfDNb34TfvEXfxGeP38O+/v78Ed/9EfQ6/WW8qYTcWX02UWNCy+azFUA+xj2ocFgUOh0tMm3bt2Cr3/96/CTn/wEPvjgA/joo4/g+fPn8Nlnn8FoNCrywTxeV77MNXmVZcunFbkWl3BIp2P5nvP5r+veRuuKKmIYKW/U4/yUozIThDQP6tNpsdI6ArnM0WhULFJcLBbFnecAL8cJzkcC2CZPpXg+xL91LZjiKFu/3jtjucLB2epms3mGDMK7RxD09+FwCD/60Y/g+vXr8OUvf7n4rdvtQpZlS7tmcfX9s2fP4OHDh8VL4q4DPikrVW7IChUpjeX5lKsoXOSrr3wkChEWR8z1HlVP+knvWpUTtaoJTI3wkUhYTQFbVkmVVdw8nxT14yOZtfxTtHkIeetKo+12klCmzkJW6vB6TdmXXeNP+r6K8cMd0VXog1VCGwPau0n6n9c/nj7B64rfs2DFRaznlJNx6FNkWQZPnz6F0WgEk8lk6WhofEbSnVIw5fMfXLqwKp3CJ598z1I5NZmqmBT3IRXB5GoH7BfNZhM2NjZgPB6fOcKL1iUS2IPBoCDFcBc7ptFk5/2H1zvmBwBFP3UF6jQwo+XSXfQxwHwxHz4JGeJ7uMqwpkVwcpPnJbWB5mtrEyCuSSTqy1nl5+NQGnOx5K9WBpWZ+jraGLfKETMefYSFSwfx9qR5SEQEz89VL3meF0fL83xS+T9IBuDCZQn0fmZsK+vEm+U7gDgiz6WnpDTa2HERnK60ZXU/gN5/UpMrUv6tVqsYfz4SDp/h7WKRW5JFGs/oN7bbbdjY2IDBYHAmb0lHSTbI107cpl1Ev7NqcL1zEevI4mNKSOlLSn02BD7fIBRWHczTop3Y2tqCjY0N2Nvbg62tLej1esXiknq9Dt1u98y1bpq/X1bmdcZFlLkK0NOzsO1brRbcvHkT9vb2YDAYwNOnT6HT6cD9+/fh8ePHMBqNCtuEeWBcg/mE2qF1iQstsvliXQ2hfKw1f1/MqOkOH/dwEZGK7+Fwxf8atLrnVwy40vI0PjklPS49R/2qKto6Nk8pNqUxe6vVKub08jyH8XisXi3CF6S7ZOPxXujY03xmy7M+uSR4J2P5Ebij0QgWiwU0Go0zBeN9dvQzYjAYwJ/92Z/BW2+9Be+++24hULfbhY2NDXjrrbdgd3cXAE4nY589ewYPHjyAR48eLc2WY9lUphSr0qWKd5GaIY6ONEi0vLkMPB8uB8DLSWpelgsSeeHqSCFGyhJk8nd1Ka1YJZDSoU4FzehTktD1nARLHYbK6KqrVPVq7e8h8kvkBX8fyRGjRLMGfjeilSTjZfl+cxEd1vwtbSKlQQdca+NUpCTC12dj6hifo/n4Al9ets+IWmQq0281+TBA531VmoxdB323zsB+jnX2+PFjOD4+BgB5ksdCNEnOML9/UwpuXYFbiE3QiFf+m1Qu15X4OdSJtcgsjbGUhFwssG3m8zm0223Y2toCAFDvUwJ4eVRtr9dbkh0JM4sulXQL/o/31M5msyV/XMpTu+OETkJIbeBqO/ob3nnY6XRgMpk460VCiI9i0f1Z5t6dSt+P222Xjrf2+ZjTCCj4syiXb/eMJV/NfmRZduYqE6t8COsY1drQVc/a0c8W/aL9bomLcFd7mZjDIguu+tZ22qOseMd1iCypZXflHao7pPwoQglVmofvnfEELytS+rk4nlutFszn86UV/65nLPJocTQnB6W74lEPtNtt2NzchMPDQ9P78JOaAKDoq9pkN8CybUodR6wjfLaEtzG90x53pYX4X+sAiTAOITB97+rizVzP+HxNrtdcPltILOnSWz7eEk+zu3XrFty7dw+uXr0Km5ub0G63odlsFuN3e3vbGT9cpP5zifLg98QCnE54vPXWW9BoNKDf78Pjx49hOBzC/fv34enTp0tpsV8ByPGkbwyuI+9g4YMoLHGHK46X9Arma/WJpNgFQJ+Qcn13XrDEU6uQl8d/Lpkk/e+zUbyNLNyJZGMk2VyyIHwbB0IQ8qyrf7tsItVPrVarmDfM8xyGwyHM5/Og0x0s8V3ImA/VZyls7NJk7Obm5pIA9Xod5vP50gw1OoiUhBmPxzAcDpfy6XQ6sLOzA51OB46Pj4s8Dg4O4A//8A/hzp078Pbbb0O73YZWqwUHBwfQ6/Xg5s2b0G634Rvf+AZ0u134/ve/730JJBak89y1jlxGAYQMbFeZLjLV1xG5cp5Op9BoNCDLsqX7fREYDOI9Pvg832WM9Ri6wyoEVsLARdxan11XSM6B9o7SJOC6I3Tiap1QxpDFYh3qaB37VQpy0UVkrbszq4E7CxZn5BLLoMQXwKlv0mq14MaNG5DnOQwGg8Kv8TnX1MaXGct0IsJFIvmA9p/nox2n7PM1Qux12X5Ytg6t+VvlbLfbsLOzA6PR6MzVGYiQ/GIIRQS/i9YXJGqTUq4+JsmVQrekaFPaN/B/1+Iha1+KJZOqJMvr9Tpsbm7CfD4vAtSQsixpfcTOJdKBjj068YLgNoQTG1ZCK4XPJMmMeWP+vpi36sk3lGGxWMDW1ha02+3ivQeDAdTrdbhy5QqMRiM4ODgw51uFrIvFAobDoUjehrRViD5z5YsxJR6b/OLFi6Vj4lzxm2R7cOFRlmXFroZ19Elj/amyZbrKQeLxrbfegjzP4f79+8VxfMjDVWlnyqCML6PlV0WfsZDEFOg7p7p+QJOFIsuyYpPLaDSCzc1NuHv3LnzrW9+C9957D27cuAGLxQI+//xzePjwYcHl4a5GeufeJV5foA7HUyxpH242m7C5uQkHBwfws5/9DPr9fnEsMfrRrutP1lEHhUKywfx3Du4HaWl99Wctk3OQISf3XWIZofMIUptq/Z5zb9xPpun4c67PVtksz0p9q8pxHGrDx+PxmSOJrVcwndeYiKk/n6xLk7GdTudMAk50oGM4mUwKB4Cj1WoV98Y2m03o9XpFul6vBz/5yU9gOp3CnTt3iqONT05OYDAYwNWrV2FjYwPeeOMN6Pf70G63xXKwwamS5MSCr8IsqxBCn9dgdQQtJBn/Ht+bOu08EEJHj27/Rkefr3TFu2it7ybJ53tOC+i1Oo1RQrEoMxnH25m2o6Sced/T3kn6LeXqF8mY8LQaaFtqixRcslonBKTnLflTGX1puByueqkK2phImSfNd1VkfAgkEjKUJC9bNiJ2wjZmfIY4ZdxpWUfya91B9cLJyQk0Gg3Y29uDWq0Gw+FwadGZi5jR+qjLjnEnnvoykl8VopNpOleZkq/An6f5aKS/ZMcstlxLI026pejboZMWWZYV9zTPZjOYTCZOGx0iZwyRiX6uxZ5qwLYJIRpXTcJotkny1V3kppQHfqfVneS7aPlzXz1FPWF+tVoNNjc3ix3I/D19vg4ds+tGornk8dW79kzMuDsPe+kid/B3gLP2xjUWJD9VejdLHCX58bwtLHW4Kl+ZHilPuYBmswk3b96Eo6OjYjLWNZarlhknSihvwW3vKvsm1tN4PIYsy4rrGXj844uXAF7uxOX6hr6fpC/PA6toa6lMV3m1Wg2uX78OeZ7DgwcPAOB0UoUfN7pOSCWTz2e2wjV2rDwLlwsnZVOA+7SSv0j9zL29Pbh58ya89dZb8N5770Gen/KuL168gOfPnxfc7HQ6FSdi17HPXKJ6YPyI+hyBur3T6UCv14OPPvrozLN8cw7PV/obYD0W8lljIiketXDt1Kf2+XA0nZanzw7xPFz6zWUjLLxq1VhVOSExuGQjpLr29XvpNBCfnx3C1fn6Gc/b94wkTyg0n1WCz97ioiKEayOi1O+5HCGyafBx4r5+Jsnpq2/vMcWz2axYOYPodDrwD/7BP4DpdAp/8id/csYJOD4+Lu4fyfPcucsS72gDgGKyEDv3m2++Cf/8n/9z+PGPfwz/9b/+1+KZyWQCs9kMtre3ixWZ2JjacWU+nIfDKyljjZB1HbHEyVWaJ1UWAC930SLwbjMMZFPBEvCcd1B23gjtb7yfcBIwtYMUSnC5yM+qEUvYSwGSb1f6q9xvUVdIk0HngVe5rmPA9TdOFF2iPLAeHz58CFmWFTvR8EiwKkDJyVSTjZgn35nkIs3pojuL/yTZmrL+E8pHT4JI7ZPxOxhd9dJoNGB7e7tYwNbv92E+nxeEha+erEQ2f0YCDfhijsal9Yn9HHfe0OOUXSQDbZ/hcFjJrhFajg98l7APvK2l9rEQOugjxMAa5DWbTWi327C3twej0ahYPYztV6vVnPeOIrQjf9cV3MeVdvdjOmkyiwK/xwkNnj/3GZHYvgjA9w4dAxSS7lvlZGBqYPtlWQb37t0rdpZ9/vnn8OGHH0Kj0YB2u13wB1WUH/OM1gYx49Y12SwRjRKkaxV4vKm9C3I51F75Jp1XAan889KJKAvuWvvCF74A29vb0G63C64ty04XgQGUPwq/KkgcRFmscpLHl7ekC/F9LfaHl4XPSTa72Wye8Snq9Tp0Oh3o9/vw8OFD+Pzzz+Ho6AhOTk685cXyIZe4uEB/ibd5s9mE2WwGP/3pT4vNT/1+fykNP9bep+el79a1r/kmyVKB6wNrWdxmh0y0XkKGFOvF5BEK11U5qWG1XzETtSHlxuYXy2VocrjK8cU6PlTFdVAsTcZK94oAgOhA7O3twXw+h62trTN3oIzH4zMTuBw4yYvEAgbd0+kUptMpNJtN2Nragq997WtwcnICV65cKXaq0OMUkEjCo17oe/iUmBSMU/jItDID3FUmfs+NiGUWnuYvBXW882NAwIlbKR8LQgMurT6kevANJkn5xgbIUnlauSnhKlvrn1obWeS01JPkHOAz3IkoY3hSBXPavUW8bi3E6zqgTGDlew8+ZqwE5yrrR+vjq3L+NXLLCmtaq+7kdmGdA6GLBKxDPE4Qg1u+O8EVOFkJzzLyWSHpDW2SUwtYq9bnLn/I6ntYfIjQPOnz6J/ifa3ahJ4W/Fn9UJpeqnfLBLCUjy+4p/aQHoXr8n+qWqxjJTZRjlBoz6SyrSH5uEgt/NdoNIp+h4QZ9sV1nTh02WnXJAjVndp93Tyt5pu6vsuyTF2okkr3+aDZ7LJx5bqkt6CK+kUeYGNjA7a2toork7A8baFlSPwjwWf3pf7qq1P6TAq/2xUnSkdKh5DElF8AgKU7B13vULXfukr/TCtTS4O6cGtrC65cuVJcI4V1hXfFr4IIjEWK9ltlLOniMqTP/BnLmODPW0B3llNZarUazGYzGAwG8PjxYzg4OIAsy5Zs/zpxFZc4P3BfHz9j3IB3gkt91srdrXNf89le+jfnAClCOW/6nNVPlZ7lCLGTZdslZVy0CoTwAgD6HAn9bOGBQ9pB4wMkSHbD0iah3GJovbnKDoVU37FzSy45zlNHuTgTH5YmY/GYvp2dnSLTra0t2NjYgOPj42KCdTAYwH/5L/8Frl27Bv/wH/7D4vJdxP/+3/8bfvrTnzoLvn//PhwcHMC9e/fg5s2bcP36dajVavCXf/mXsLm5Cd/85jeh1WpBrVaDb3/72/DWW2/BH/zBH8B3v/tdMb9arQbdbhem0+nSqldtZbiLQLM2Jg2ctWel+1j50ZK4ApKCvwfewUJ3QfGGlwKh+Xy+dNSipnD4SqnFYrF0ZEXIxB7/bp0NeFmEkie8v0l3wFnzo2ktOxrPw7BSB8V3vGJs/qn71+syucVJT0pWcWKeY9Xj+nVpk1BgG2G7NZvNYiffq6x3qwRd4KUBfQo+fvA37ZlQ5zkELkIJJxQ7nQ7U63Xo9/vmO/14IGORjd9zLuVnCVRXpWeoTLVaDZrNJgwGA/jwww/h2rVrcOvWLbh58yZcuXIFHj16BIPBoNgxTRf/xcrvChxxt5d1Bzw+32g0oNVqLfmSWZYF7QqT2ntVeqUMae863QLHrqtvxrSbBb7JACwfF6s+efIkCRnPJ05iViRXbYM5EeEizDANPoP6jX7PSUbqf0rt65swiplQc4HXZ9Xjir+TtexVTGBdZFj1BPY/7KuhcaM2SWp5Fp+hYwJlGg6HxUlmlvfAPOn/uCCfX5XEFy+FIJW+wViGnzS2DvFElmVw5coVuH37Nuzu7sLR0RH86Ec/gjzPix1tZfS/Zr/X4d0pXP4ihdb3feNB4vli6yDGJ9WIZz4WR6OR2N55nsPf/M3fwIcffgjtdhsAXvbh6XR6qZsvcWbhBt4/TPtHo9GAbre7dF0dAJj4gos2YUch8f+a3JofCLC8Qw5tuRXSJreLinW0IS745A21KS5uI3QCl5dp3V3ri8tT+U4afNeoaNDe0TWWLL4Bl40+o8U6WXb2+k6ejyZPKHzj/czOWOqwA8DSzlWa6dHREXQ6Hdjb21u6azbPc7hz586Zyb3JZLJELOAO2L29PZhMJjCZTKDRaMB4PC7uLEWya2Njo1jpSsuhx6yhzLhblhom6wQQJyx88E3EaqCOmRYk0YkRK3wBNf1dOt5ysVicuUgZZfTl7yNUQ+W1pF8HY+B7Nx5E8zQpyOaQ/kfTasGB5LjQZ13QCJ9UxJOlDkPGIR1zsbKkcKpSk3IWco3qGi5DmXpZFSz90hJglDGsqXVQSN/F/6Vj5S4RD4m44XqNLqIJ6X+aHoxx4kPJdPSV6GItl/0qY7dTjo+U/VmqY64D8TPep4fHwWL9aflY5NXsq7W+fTab29t6vV6cfsLzQL/edZTtKieLfO2slR8TaK8aoT406nUk0jA28+2Uco07Te+EvgM+v2o7I5Xneg9Jt2r+Dv2cyh+ncvj8bklu+nfMJEUZn1aTY12BYwN1nVRvdBxZUUUf1/S95m9z2xIbB0s2iuoV3wIW/plyL0j8hyz0CfFvpN9C2rHZbEKr1SrqHicfzsNX5j56o9Eo/LJQzsdSluW7qmDlDPh4tfBJGlen+WLch9dk0TgLfI6nkWSSyvSVS2M55D4nkwkcHx9Ds9mE4+Pj4qoMepetxXe7xOsJ2gfwRBV+gmSIHtX6c0ouLBau+MAKibPUnudpXWXG+NmhfK72mwspeNgQOUNQdV+S8rfGZ/Sz1f64bFHou4bWp8/2ueTxpbfKZemjmr8ixVRa+SFjz8f1uSC1Z0y9IJYmY1MQuVmWwW//9m/Dzs7O0veff/45/Kt/9a+g1+uJzz179gyeP38OAAA7OzswHA6Llf0ajo+PoV6vw9WrVwuCEScZ+VHJ+G4x96GVAZ2hx398tSNORNPjlJrNZvGPGs9+v68GEZg/XfWJzv1sNluaaL1x48aZNhoOh3D//n11ZUZVyjGGaLgo0MgU+n1V7xdKrtI+gw4+/tNWrXCSyWWIqmxDnxL2GVmLbrjI/VACkhKoMxC0T75K7/sqgq7myrLMaS8vEQbfPe1aAGYh212EfQh8+hwnb5Dsa7fbS8cuh5BevDyfPpRItlBYA71YPYXP0V1zHNPptCDB6A4bftKDj8ST/qYySL/RNNLqdUqqIymOk3jNZrOYPG40GjCZTJauIul0OsXz8/l8bY+91XwkV5u7diunwCondPL8dHcDLlqdTCZFjICT7Rz8BB4pz0usBin6INcp+JmfzMBtUJmyU+jvKkFlm06nMBqN4Pj4uKgTesoLwOluc7yCwILUk2LSKVn4mxbPx9xNbeFxuH/Pd5X7nkVbtLe3VyyUn0wm8PHHH3t1D+YRC2rnrMflX7t2De7cuVPozwcPHsDJyQm0Wi0zL5QKuGFgNpsV8uf56QaH4+PjpQnyi6inU3CIVYBPHGmw9IUq37HdbhcnBT58+BB+/vOfw3Q6haOjo6V0Ln/wEpegE/t42g/vH3yhpgva5NU69bnUE4Oh44vadUyPtjA0huHlWiekLrEMS5vxz/Q7yZeR2iKmr/DyLXlo/JElnYaYMVH1uHf5kNz20WP+NdA5OAu0xRhVvfeZydg8z2EymRSraIqEjQa02+1ilwDA6d2wH3/8MVy7dg3efPPNQsh2uw2tVmvpmNurV6/Ct7/9bXj8+DF89NFHxQsOBgN4/vw5bG1tFUTyeDyGBw8ewN7eHty9e7fI99atW/Ctb30L9vf3odfrFROTtIJw4qiswrUouthOKZFLi8UCZrMZtFqtM4Q6zZ/u+tVIPLrKFY0Bl3E8HsNwOIRut1sQdp1OB27durUUoNXrdTg5OYHhcBhMekpkrvZe2nfWCTX6u0R8xhiukNUXoflJxApNYyFoY2Tz/eYqi9evVLc0jZZfSifCRYC70tO/qawamcXbq+qJ5SqwSgK5LFx9S0oXkg+HTz+to9OrOfbrKOtFh8/Oc51HcZ59yEXy+p4DcDv9NG/JftH0Fhsg2Tuqk31twPW6zym3+hK1Wg3m8zkMBoMlv0sKFFz1YJVBel7yDSxBRbvdht3d3eKkmcPDQ5jNZoXsePyxRd7zhK+vufwRV7+kv2vlVKFTrfnhzmycXK/X67C1tQXtdhsajQb0ej2YTqfiTgf03VF+K0m16n7gG6eYRvInuf/Gv6dl4D+Lf2vRN1pZVqTwrzQ9YHlXnt5a1roB41scIzi5NZlMYDQaQa/XKxZlhxCfKeSi0Gwp7buWvmaxK64yLc/RXXeYjwTMn96njt/hdRmuOCoWkg7gclKOBfUiPzGNpksBn27Rfp/NZoV+t14dcd44L7/BZeO1tBad7/pdsy+a/xbjM3A7TXUaLiAAeHn07OU1NJewgOo3qltQN7t0Oz4fwoVfFPhiectz1rrx2ReuW2K535SwlHtebR5SPy77bvFxeDnaMxaZrBykxiFpn2PGp2+OxPIO1r4q8Rexfdyqs3y8F5VJipGt8lnHgCW/Bv9iPp/DyckJNJtN6Ha7xYMbGxuwubkJz549KxyDw8ND+P3f/3345je/Cd/+9reXCKper7c0wXrt2jX4Z//sn8GPf/xj+Nf/+l8Xx9ns7+/DwcEBfPnLX4arV68CwOkOze9973tw+/Zt+K3f+q1isvCXfumX4Nvf/jb88R//MfzsZz+Djz/+eGn3K395any0WXYXKROzWlIbtDR/DFzoTlW8L6Lb7cLe3l7xfa/XW7rva2NjA2az2ZljoGnZuNoJ76Llx0wDnO5EfvHiBXzxi18s6ndrawt+4Rd+4Uzan/zkJ/DgwYNS9RKKdQ3+Y+F6H67UfMo4lTw+hJaLCo2vsqYr1asgNy2InZhwwUqiXARQh53vKHhdsWodFDoucDxRfYxH918iHrH6yeVwWXV/GUh5IVnqcq61CQ4kLn13efD8QqGNMzoBE5qvFjRrEzgcdKfpZDIpTm1BeX33t/p2PVlkwDTUbtKrRHzY2dmBt99+G8bjMUwmE+j3+9Dr9aDdbhf+JpbfbDaL79cVNJizgk8s4POS7cY2XZc6mM1mcHR0VLT71atX4caNG3Dr1i3Y3t6GP//zP4eDg4MiTsMjjbF/ttvtYveVdnRrKqS0lSgnn5ig+kAqC5+jYzPP86X4if4vwXI6Ci8zZNIzhb8pyaNNSmk2yUqcrDuwL+C1R6i3j46OignZFy9eLMUhKW1uWfjsGz3JwJdHWfC+FKsH6/U6dLvdov5TH71rBS6kopP0HKuIc2jbItdCJ0NGoxH0+/2la7guImLrsYr6T5mnxUcL1Zt0zKKN4s/iiSwI5FKHw6H3WPFLXIL2D9QzsdD6NfXJ1g04LjV/nyPEJ7C+bwyH6nt2XXyXVwVWv9r3TNkyLfnGnJRSNULGv7QIJMQH813Tg+D15IoZ+dUaVcyNWLA0GUtXXeH9ofV6vfge4HS1fa1Wg/F4XJAET58+hT/90z+FN998E959990iPzz27M6dO8XE4GQygd/6rd+CTz75BH76058CwOnL9/t9saP95Cc/gWvXrsG9e/fO7Pa8cuUKTKdT2Nragvl8XhxRhLtyO51O4Yj7KljrUC4jxNO4HDKN/JFWC9B88F22t7eL+kFSjcoiEU48AGm1WkvH7AEAbG5uwvb2NgDA0jEW3JDxvEJICasBtJAFFxm0fV0ENE/vcyQkxYakBM+Dlqv1G1dZvDzf55h2rGIy2joRe5H6nZXMB/CvyuIXngO8PE5L2okfkn/VqJpkllBVP4kZO9adDJcoB647q8jfkrfL39AmZLkdl/oLtwv8XX1jIXW/89kqlyw0D+l7qRyNGMe6s+QdY095PaOfhyel3LhxAwBOF9n1ej04OTlZSq9NUqI+Rx+e5k8XckjBiK8PpmhzV79N4Tf4wMso804hz2rvKY3DRqOxdJQctmksGYQxVOx1LZa+Hgu+WI/3Q21Clu4IxpgJ70bDyRh60lOInHmeF4tncbIbY1kaE1nHeaivnEq30slITZ+55F1nv1iro+l0Co8fP4bBYLB0dQ8+s0o/yVWe1t/pdzx2tILbeTpmLP48b3+qP7Isg93dXdjb24Pd3V0YDAZesjCGuOfvwP9pxwhSHdFqtWBnZ6eQEXVBiC4tC1r3dLHX/v4+9Pt9aLfbxQJ91F3r5su79JF0DDetX40XC/XrQuvE5btp31n6hGssWvwZzQfHDTF8BzftD9SnX2fdfInzg3VsabDGAOsIybennDbAWT42NG8t1kZoiyW4j2uR35V21YipqxT5p9DZljy1uNCFFFfC+GQJ6ROx8agljavPu2Ic6rNp8mqf0Z+gY9Y3bvlYt/imrvL5b6nH49JkLB4tQ+/GwiOxsBI7nQ60Wq2l1Yaff/45/Kf/9J/gN37jN5YmY3HC9vbt28WE35UrV2BjYwO++93vFpOxAKf3v47H4yXher0eTCYTeO+99+DevXtLv9VqtYKkAjhdLfb48WNoNpvFUb/NZhN6vV6UU1vWkEn5ScQI/Z2TYlmWFZOx9+7dK1bG7e/vw2effVakoXlQ0F1SeEfYeDwu2i3LMtjZ2VnaiSsF3/P5fEk2XN1peUcfJHJFeh+JlIkpZ50gvTd9N8lp0Ihy6W8eyNP3R+Kr6nrhAUNVZaV4D0se6+KAppYD747FsZ1lWUHan+ddglJQqwWxrxp87yaR1fR/17OXKAcfOSS1nVVHlXHefelxBb5GtlJw26QFsquCVpZmEy32jRPhEvFrkYHKIZXhmmjhz9FjZ5vNZnEFyHw+h4cPHxaTsb4ysP3o8ZGo45GQxiOMY5HKf3DpeP57bL70s6s9+Heh5IOkf3kftaBWqxVXw6SywfS+aC0oPg8/meoaVx2hbJTowskhDNYx9sMJVNxd5LtnUrKneZ4XV/TgxC4ubMDfU+2kLjPh4ALvj5zk52l9BNBFwXg8LuJk6teWGc+W9IiQ5zSSCfsYJ3ZpGpd/KMkhkWK+dqb2A++pzrKsuIt1b28Pjo+P1TusNVsaWkeckKNxLAce9QpwegXT1atXl2xfleDtQ98T9Qje6f7w4cOlxTZ4j631Ptx1AeWzrLJXdWQ0RwxnZIEkc6itp2MD2/3w8BAajQa0Wq1i7KOvhp+xjukmFReHdonXB2X5a8kX0+zFuvgIGifKfUZu/yz1JI1xnj//jZ/Ewe2Xq6yy/HaZNlmX9iwDSyxhzYN/p8VNNL3lhB1fzOs7+SB13E1l0dJK6Wk/d+Vr1UlS+XxHLMaxoXN72ji2yJDCh3A9f+bOWLzoG8FXlLowmUyKS+azLINut1tMIGIeGxsb8NZbb8G1a9eWnh2NRjCdTqHb7RaO8nQ6hefPn8PHH3+8JNOzZ8+WHG2A04nk27dvw3g8XrpLFlczWypEC0RDCDmaxkIo0bzq9TqMx2N49uwZtNvt4p350XRbW1vwta99DZ4/fw6PHz8+Y1Qo4ULLns1mUK/XYWNjoyj7008/hf39ffjSl75UtNX169fhq1/9apHHV77yFXjx4gX8t//23+Dhw4dFIGY9JsUXdHHyVHvO6uS6yM+qFFiZvCQjalV6NK00SeUq1woqk8vgWw1gzCSCJhP/jjtZnNzwyWZJ62qjVSCk/BgZNRIhVf6hWEfnMMYmWFCGkNUcCF+aS9hBbQu1R3zMcD2p6SsK3EkglcnzsgaSVFfM53MYjUZqIIlyuxzWLLPtqqNl+MhiyZ6F6F5OXPMyXQEUz8en81yyWcerLx2SbM1mc+m0Enx2sVgUO//oMZa8bkajERweHhY7BbkMo9EItra24Ctf+UpxP9mzZ8/g8PCw0gmL0Oe0cYF5ALgX7pWxgalg6Vs+7OzswBtvvAFvvfUWXL16FX7yk5/AwcEBtNttAHh5EtFisYButwu3bt0qdtU+efIEBoPB0gJNzddJ5SdXlV9qSH0Ld6bhgoiNjY3i3mg87g/HHj/eOsTGcl0eUlda3BQKSQ9eJFB5MfY9ODiAPF8+Tt5FoK4SXGdJOirEDnHfw2W/fXbQF4NzGXBiEf9xoluDjxTT4t9utwudTgeuXLlSnBA2mUwKvab5G5PJBHq9HvT7fRgMBoWNddmWMtDaE8tF+83TcF7rIkBaKIB+opUjqnpyHCBscZ2PcygTp7lAeU4AWFoox/sqv07oMra7RGrwq/5oH6Mnkqyy72njTYr7siw7cwKlBtc7cD+ZfpZ4R/6/zyb6+GqNT3DxsTFI3Y5W3tWShwUuP0LjsS35Se0LcFYHh/jRvN9IfVd6J/58DKzccYo5Ba2+Xf4a/47rHq3eJB6ElkN/41cD+Mag6x1jxiP/fWkyFjPik7FWTCYTODk5KZzKVqtVVBo6Eu12G27fvg1XrlwpjuDK8xwmk0nxO135dXJyAnmew3Q6PVPpdJVJo9GAvb09ODo6gmfPngWRZlojSMSq7zn+PQ/QXUoddyrgO/N8MG2324U333wTAAAePXpU5IO/S04+yoEEH+LJkyfQbDbhC1/4QvH99vY2fPWrX126a3YymcAPfvAD+Pzzz5fklt7HElhpkMjUmHzWGS4DFWKwaT78fi2rDK7fJeeG9mdpHJUlqjEf3zOhfYrm61L+PjJDyi+FjBaUdZRC6tVa91U54a5gN6ZMy7ufN/g7x5K6rndN4Rxf4iU0ApTD1YddZGhseRT0eEp6T72kwzVdGTN+YvqY5lfEPOsD9/F8wQ2t9xT6RMsPbSxOomo2iR7rStsYMZ1OodfrwdbWFnS73TNlT6dTaLVa8OUvfxnG43FxOs3x8bFpBTeXp8z7c9ktaWndac+WIbapDySNk1g7JLWlpa43Njbg1q1b8MYbb8DNmzeLu2LpEdSYT7vdhqtXrxYrip89e1bEYBpBXia4tyLFuLEE76mQ5y/v38XxRnUoXuVgzYvCpX8lPawRS6usj3UG9m2A05O2AKBYqBBCwFGU7acamUq/1/qAJo8rfcgY9sVqlrxwp/lkMlnavW+tb+4HSXIg6vV6MRF79+5dePbsGcxms+KuYG3iHbmkwWAAw+EQRqOReF1D1eAcjUY4xtqW84LEN4XAxRv4nkNYOQ9X3YbmlxpoyxF4hQTdjEHrSvMlKNY53r1ENfC1eawPRu0rQDnfehXgYyXUB7Dw/nyihn6miydCeF3fMxp3cNGR4j1iuXWrTFIevD+EluPyCfD3UPsY866xeoHKx8eD9Dcdk9Y7cLmtc12zoflVVI48P3v1JpVNkluTq8zvFEtRJN6tsrGxUXQqfGlccZplp5OGOzs7MJvNoNfrFQJ/8MEH8Pz5c/jOd74D7777LvR6PRiNRvC9730Prly5Ar/2a79W7L78+3//78O/+Tf/Bv7jf/yP8D/+x/8oXrzX60Gz2YSrV68WzjU+wzEYDM4c34WrHmMdPAuqcJbREZOO0cLj6drtNty8ebPowNo9rvR7qR7yPC+O6+l2u9BoNODg4KCYjD06OoL79+/Dt7/9bfjVX/3VIp8vfelLRd7D4RB+/vOfLx2VsorLpS/CpEpZaO+Gq3/pfTdc0fDVHogUilaS0TKB5CIeqgY3FDGB4usG1Pl8TJeZkHmVx+slXi9ozqcvPXdOQ8kfasvLOomSjBZwh5hPJob4AJbJTzqBhGVa8kvho6UK6GLLrtVqMBqN4Kc//Slcu3YN3nnnnWJyAXWz7zjAPM8LAhp9ZXpaze7uLvzKr/wKDIdDePbsGZycnMAnn3wSFChZYW2TkLYrY8+loG3VdgploMcuUXCfKc9z6Ha7sLOzs0TcUrKW5kt9RLzapVarwdHR0dJdtOcFjVi29hMpPb4rXq+Du8vX3ZeL7XuoJ2ke3OeVJnn5+L7oPhqdbEEddx7922WjsR/yvmuZILKUSaGNj1D4np3NZvCTn/yk4IKyLFu6xormo/UxfgwdjT3wuPZutwt3796F2WwGDx48gJOTEzg+Pi74DFqPuIN9e3sbvvzlL0O9Xofj42N48uQJvHjxAobDYZHuPPQC9W2wr5a5LuA8gG2NbYVtOJlMivhx3XXueUDiTDiPM5/PC35uPB4vnfDnqtN6vb60yQUX8120vnWJ9OATk640+LfE/4Qu1KwaPh2DVy5q8UysjpJ881CExJjcT1hVG1y0xUEpINUt96cwXgzdCEURG2dX3faa7+rzKcty/L5+xuvb8izOByJcE7m+dteAz5YZJ0uTsWiwqVOA/6hzjA4k70SHh4dweHgIb7/9dqEAAU4nSEej0ZLje+/ePbh79y784Ac/WJpsRcIInQr6otRhz/O8cFKokqIVzysxJMBPrXx4J5bKoWQQrXuA05W+uNqdkjA4gUs7qEs5cPKn2WxCrVaDwWBQOIT9fh/29/fh7t27S3JeuXIFbty4UUyaP3jwoHAcaSBcBr5AVvstRf4++FbFlM1PUna8P0hHAuHkLDrd/B1dk5GW+nC1q1SG5X0tY7PqiTxXvqtyPlZF+PsmPmjZnNjF5yXZXPrydXPgUmCdgpxL6IhZ1CJ95xojVv/FqsckR5r7ehZok55SWT4CwGWrtPdwycPLt+bhkos/Z/FzQnWfJu98Pofj42PodDrQaDTEo4tdec7n82KBFvrGdAdRu92GW7duwWAwgCzLoNPpwHw+L8qg/WIV+pxOHlEbFNIffO3jslWWd7TWfUgfwWMrsb4xYOQ+E5JK1j7A0+A1MCcnJyubiAgdixbSUMpb8m0ti0RD/CIAWLo+JtZHrSq+pOPGEnNKeVQh3yqBkxAAZzmNKsF9Zl9ariNifWiX/S0L3zilx9vj3cz4PX0/re/RfisdiYl5tNtt2NzchKtXr8Lx8THs7+/DcDiE8Xhc3A/N80VdubOzU1xf1ev1xDvXVwFeF/huKHvo/WfrAMpJcA7OMhmLz63C1wjRb9byXRMnUv+nv9FyuK+Dz2r2S5MP09PxJNmDS7yeCPUZJZvG+9Yqwf1hadcb/R37Pb3ORYLLj5N0EtXhFrj8L6sPuUpffVW8pJUv9sEak4XI5ONVuM2ywGcPVomY/hTDQVnLkuJ9SfdI/LQmi+RzWRdoWnm1EO7ABfV8JU7a4E5K7mRLODk5gadPnwLAqfK7evWqmH+WZfDlL38ZfvM3fxMATidi//zP/xwGg8FS2ul0CkdHR3B8fFwcPwQAhbO9t7dXTFC22224fv06DAYD6PV6Rdoyd1JQkiTFsy7DgKQMHqszHo+L9LhjGXHjxg3Y3t6Gzz//HPb394vvx+OxKiuuht/Y2Fg6wunk5ATq9Tpsb2+r77KzswNXr16Fw8ND6Ha78I1vfANevHgBH374YTEh3Gq1irbAgCg1rCTNeRMKqWTg7yqtVqsaNDgAOLvi3EcGrRKxBFksVtnXqiyLEhjUiaXHxsfW6arbJATroCtisY71+bpjHfqSr08jYdZqtYrj0Dh48OmbMOXPUntRJngNTc//dzntKKPPQS9rz3xBpxRg0J2L4/EYnj17Bru7u/Cd73wHfv7zn8P+/v4SGUH/bzQa0Ov1YDAYFGUvFovCj/eh2+1Cu92Gk5OTIB/O1e9ov6EkLP6W5y9P9nCRASHjSyNgpfaM9fFDwNsZ/ebr16/D9vY2HB0dwXg8Luqd9oHBYAAHBwdwcHAAW1tbZ04FuuigY9DXxlJbYYyHx5Zi3WE8hfdlS0G/pNvwu9lsVsRhjUYDvvOd70Cn04GnT5/CyckJPHz4cGkn7jrY5FB/y6oz1xl8QojivN/LN1GbkkzE/Kroh3RXQa1Wg/+fvffqtSRLzkMjd257XJ3yVT1dPa7HkboihxwOh6R4KQoSdHEhQBIucPWiV0I/QdCD3vUf9B8k6EmAAIkyFCFxdIdDzXBsj2kz1V2m69jtTd6HrMiMjB2xVqw0e+9z6nzAwdmZuUwsFyvMMp988gmcnJwUju6mNFhAT3NqtVrZQvwoimA6nUIURfDo0SN4+PAh/O7v/i48ffo044/0ZDaed6fTgeFwCH/5l3+Z8dkkSTKZp2lY9YqrvGuROl2p88Pa9r5xwbFJh4VLDrE4j0OMxXQTShzHMBgMYLFYwHA4zPLDjRMWPRyPDcdNM7hDtgl7nAtXWbe+LpCcBSHzA9XjAIpza9X2LRPfRTff9Ybpl7X/S85fjQbKB330WsPSuqFl43pTE2PM5/Qqk9auIlRWqpOXSv0VsY06a9p27+tT3HHK58uQuTYEIfywiXYRnbHUIO9TIKSjdS8vL+Hly5eZw28+n8NkMoGXL1/C4eEhHB0dZenev38fvvzlL8PZ2RkMh0O4desWtNvt7I4rakyaTqewWCzWhHcuzErGJs0og9+kdzwsHzSu9DRozBc7HlWi0RiDdTufz2E0GkG3281Wxg8Gg0xIw3j8Pl1OJ+aF96xEUZTVIVWqXr16BR988AHcuXMnOxptNBpluyh6vV521y8tBze2uQwUlnqX6LcyDB5O+t0Uw6tTyODheL+kxu8yx4hYjTdc6eLxKU0a7Zb0ff3F2vZlaKBhfP3QRZPkIPDRxr9pfTc0/ZC2wvr3KZtWhBoGaVjJcFqVHo2+KqijnjZhxN1lgfiqQmp76/wUOiYkmSxE3uDv6hAsXfMBP+0jBD7+baXbpQhyOcVHT9mylAHlv0mS3/Ha6/UgiqLM0cTjIK3L5bJwvB0a+vj8Ii2wpAZxTd6qohRR+Z3KLWXha7+6nR1W0PaQDCkYpt1uZ3cEU1me9gHchYZ/cRxDp9MR6cZ7HDE+HvW5v78P7XY7u0OWX2lSpmw8vo8/aQbuEPlXAqbrOsaK8juXrIl9Hvsl6pxxHMOdO3dgf38fJpPJmi4qyd4uWbiMfBgCi9FDe27aKLMJbLIMFpneEsYyD5Wlpy5ZF3fg4fH39HQsGs7S/3CnOX9P267b7cJgMIDDw8Nsd7+mJyFwDpxMJtmzNAc2hZA8mrZFNA1Njqzal108OlSvDOUFIbyzbDjK/1utFhwfH2cnoAyHQxiPxwXbjnQ1BaUT7Xp08QENs2meflX781WEq225HcXHm2l/abLv1Gl74fIy6jCSg1ZLQ/tOafXxo5B64nqB1U6H/EIKVxfqTpf2OV8bbBuabFGHrcT1bM3PJ/v44vtgTZ+HCaHFJy/4xqHlfZ19yiLDV0HBGYsGnsvLy0z4RaCzjh4JgwaE8XgMJycnWdgf/vCH8LOf/Qx+//d/Hx4/fgyvXr2C09NT+Oijj+ALX/gC/ON//I+zFfrf+ta34Dd/8zfh3/ybfwM/+tGP4Jvf/GbGaF6+fAl//ud/ngkfR0dH8Pjx48KROKvVCk5PT9cqiAssqKBrK2R8k1NdcSSa8De961Y6emcymcD3vvc9ODg4gCdPnmR581WpuPJzOp2qeV9cXMBwOITbt29Dp9OBbrcL8/k8O3oYAODZs2fw7//9v4c/+ZM/gb/9t/82fP3rX4fRaAT/5b/8l+yoH1R0EPP5HBaLBfR6vcKKvNDVnxZhwSWo87CSomjNa5NwGT3RcDqZTDKBHd+jEQ/rmy5i2ATNdafTlHFK25Vu7QNl+USTqDt9HCu+o122hV0ZqzfYPLhAv0t9YRP08DtUXWNfM/BxpwR9L4Xn36myS49T5fwb6UySZlbkS/UdaqBDSCeYaAp3XfzWpWxrbTAajeD8/DyT7ajDiNcHygOLxUJ1UK1WKxiPx7BcLjNnIKaL+gD2Oc3g6oPUTrQPodFksViIO6wkSMYlV1j6n4bfBf5Rpj9FUQR37tyBBw8ewGg0yq6nQIPt5eUl/OIXv8jS3tvbg7t378K3vvUtaLVa2SlDuFt0G3M8H6vcKCW1keYwRBkYHTB0Jyz2sW63mx3brfVHPn6wX+IOvG63C5/5zGfg6OgIxuNxllYURVlYy6ICi5He5wxw8Q8reD1jmpb5ZRcRSm+T8sRVq7sQ4CJw5D0crvkex+xisYB+vw+PHj3K+Nann34Kr169Eu00GGc+n2cL8yVIeguOZUxHSnvTc8EuzD1NIHQzhA8WmcuaZtU6t8qXVmcXnuIAAHDr1i34R//oH2X3G//0pz+Fb3/729DtdgtXudH8+dw9n89hPB6r9G1rh2zT2GW9cFuQHKoAtnkJF2Ja0t80JLlHOqnv6OgInjx5Ai9fviz4J+gCVR5H+g3gPlnTdxw7/ebToTSHEqVn1+xxVxkupzp9toQrI3v6vlv0gDpRJT1LXGtZLH6dMmlWRZMLU9acsQDF1fI+IwddyY1YLBawXC5hPB7DeDzOHHOLxaIgKCRJAp1OB9rtNvR6vUzoQKE7juO1naG4E5MKJ5g/Fc7rMrZoQqTUyD4mKxkspbzQCMYbPEmSzNlJd0V0Oh3Y29uD2WyW1RU1hGoTDzqA6dGzaFDAtsJ7WbCter0e9Pt9mM1msFgsYDAYwOPHj+Hy8jI7Qhrz47t1JVq0/sXL7ur8moAcMljKKgtl+pmPiUtCpVY2Tjf2HZ6ORmOIMd/iIJUMn1ZIZQqlKSQvzRiI8I13Sx7WMJIjxDJZh3wro7DSMVulrutAiOMllFdcR1xHhZQrNrsAl7Ae0s985aki00j9nhpytLnB9R7/QvhcKL0+Wnh/qML3LGn4+Aqltyoo70V5Go3R0t3wGk2SAYDydHo0sKUPan1Jg/aN9iGNt0s0c2edK07oWCnTbnXl65vHZrNZdgVMt9uF4XCYyep8h+tyuczk9iRJYG9vD9rtNvT7/cwxiYZZKa+mYeV1ru8+JVySjS19XAI19uHiAUn39dGswepwsBhoeJ5lnB/XAbxvSzp4nfKES1+TaLLOnb5wm2wvfuQp/pUZV2gXwPvQW60WnJ2dZd9pmq751TcmuMMK35XV+bU8fJDyu0p6CK8zfl+jZKOw1IvWxphGGUh1XbaNy8rdLjmS9u1WqwWPHj2C+/fvw8HBAURRejw3Lh7A/osL4yT0ej04ODjIZAH8j7vW+WkbXIa66jx/F/XCTaOq7sPj8bjaTutNQrNN4VgBSMdTp9PJToFBRyravC0ylEum13iLy05mmcd4OloYjZY6oNVJXfmE2OpccM31vjhlZCmLfmpB0+1lnYMtduaydh1f3fi+S0d407nP10eldq6zj7nSs9gkNKh3xuKRWGsRXq+YsWTy6tUriKII3nrrrex+UgRlpKvVCvr9fnZf6XK5LNwNi7i8vIThcAiHh4dwfHxc+LZYLApxkMY6V5FUZYCaAiClR53QPkZw69YtODo6ghcvXmT37aKRjaYjDUy6whTv351MJoWVRDyNg4MDWK1WcHZ2Bvfu3YM/+qM/gh//+Mfwne98J4uDK10ptLvpyoJOfq4ByifCXVWALEyMMyjEpstUd102RT/nVZu4K+i6gCvYruP/bnCDNxFl5payvJPOd9a43AHB5Q88xoket4kKq0V2sshYLiVVCluHQljW6aKl1QSoUUz6Rp2kk8kEnj17lrUVLkrUTi+gNLuMpriDEB1LNFyTc6XWPtb2x/pB50BZGmh6WN5tyIf0WEFaB3THM71P9vDwEH75y1/C5eVlVpdINx5lSHcAJEmSORHxGhi8j+4Gm4VmAKdtWIbncF4ipSU5sVw0XgdY9MS68wuZn68S8MS04XBYOH2J8q2QMsVxDEdHR9DpdKDT6cDp6SkAFHey4rNLzpDqHPk5PwZ5F3GVxhvSSk/SsGze4AhZUBIax5VfCI0Wx0gZ4Fw8n8+h0+nA3//7fx/u378PrVZr7XQQ7PvoWJVw584d+LVf+7Xs+fz8HEajEfz4xz+Gy8vLwtUWN7i+qHOBCQc6+XcNURTB3t5exov6/T48ePBgbb6gm5UAig5cPq59u10xX64zWeELe5Xmg+uCMg7eOsHHLj/ZA6Ae+3XTDvfQfDn6/f7a2MXTUCy4qmNHdcZSYwetGDQQUQE3jmPY29vL7jLC+CcnJ7BYLGB/fz9zxn744Yfwn//zf4a9vT04ODiAx48fw7179+Czn/0s9Ho9+MUvfgHD4RB6vR7cvn0bfu3Xfg1evXoFv/rVr7J0UVjhx6rR+8lcK6UsCmhV774WVlOYOR0u+ufzOZycnIjv6cRA7/yihhoqlOEu23a7XXDgUvzgBz+AVqsFX//61+HWrVvw4MEDGAwGBeGQH6OC/YQ670PqjLeFq200gyYPQ9N1TaBVDCIWuFZPULok4wwNiyuS6b1W+L3J42g0GimqMkQtXd6fy6SLkJwEVl4Rmqc1HWnxgIuOKgKgtlqPh6V9MGTlz6bHUVNxbpBi1xez7AKsc5Fv3nelIY1BV3hNlsD5HgAyp6x03BOPGzKXlgWXgXyGdPwuHdnrc0ogsB7p/xB6aR5Wns/nUClf/D6bzQr3gUnh6PyotZe0yJKGaxpchkFHIX7T2onv7sDf9Bjl0PwpeN939Z9QuMZsHMfZdR94DynPC/v1bDaD09NTGI/HMJlMxCOkeX+S8uflCZFRXChTR7Q9aTks9PjkeNx5h/flXlxcrC30dfEFqjdhXxuPx9DpdNaM4z651CU/Ve1jnGdJtPh4nkTPJhAiU4ak4wpXh1OnCg0h6W9T3pLGpfRc1QknlZkujplMJnB+fg4ffPABfPzxx3B+fg6z2cxkNN8EyvSp6yJH4xzOd680WR9l7HuuME3xA+07p39vbw/29vZgPB4DQLrTlTqLeFzKp7H+8fTB+XwOe3t72Y5zgNzxdN2OJ76BDS5b3VWENH7jOIbBYAAHBwfZ9R2vXr0CgNTBI9m2MS38w/nEx1M0mzbXl122NYu/wScvvkko6z8ISVOrX4u/wJW2y4aq5Uf9br7yVLEDh8bXoNEb0m6aTZrXh5SP6501X1f8puVypzN2Pp9nx8cg8Ig0ekRTu92G4+NjuLy8LBh6PvnkE3j58mUmaAAAvHjxAv76r/8a7t27B0+ePIE/+qM/gvv378Pf/Jt/E77yla/Ay5cvYTQaZXEePXoEP/rRjzJnLEBqsJhOp3B8fFw4aosfu4Xl2BXGhcyZTwhaZ9UG7nQ6hY8//njtPRW+oijK7kiix0tQ5QV3P+NEliQJTKfTNWPdn/7pn8Kf/dmfwb/8l/8S7ty5A5/73OeyY4kxbL/fL8TBnRv7+/vqQLKiqvJ8FeEzCKPyg0ZMalTHYzrwGOqqdOwKqLG5ShoAurORog4jLDfwl4nP6ZHSbxLU8F9H/e8KP77BDaqiLI/Y1jjgxvrZbJYdW6rdd0h3X0rOEo1vlpW9tHmPyjFa2aS7DkMcDRK/riJ/WPuHqz9EUZTdFU8NB1pYACgYRTk9s9nM3DZljKpWoI6B8oqvnul1JLRt8Bg+vkOWz53c6cTLH7LquGqdcHn88vISLi4uCkYeSivezzydTrOrXngYuvgyBDhufONLilcmL4qySjsPrx1DGkURHB4ewuHhITx48ACePn0K5+fnqtGMy8uYBl3ciPHpqUIoj2+Kp2sGfsnQ54svjXG+SOIGbza4o5/PtSF6pmWco+6K42o4HGb879NPP4UXL15kuwt98/kNmgXamMo6YuvmmdK8FDJXobxVN02ubwcHB7C/vw/D4RAA0jve+WmCCDrX4ZjAeWg6ncJwOIS3334bDg4OoNvtQhRF2eYZvuv2BtcDlv5dxRbepB5QF+I4hjt37sDt27fhc5/7HHz00Ufw/vvvQ5IksL+/ny10QPA5jcrkPJzmjN0kbu6K3RysbVvW6SvZn2lY3r+aPNGgjvnXsoChCrQTsFBGrIK6bNtVId4ZSyEdLRtF6Z0GcRxDp9PJ4vV6PTg6OioIB1EUwfPnz2EwGMC9e/cKFZokCfz85z/PmORsNoPvf//78OmnnwJAKpA8efLEWwhUDHq9XuEoBc5seTldFe8yEmjxNUWYh5dWEISsrtAgHWPiuj+UtgVv416vV1hJlCQJ/OxnP8viT6dT+OSTT6DdbsOtW7fgrbfegj/4gz+An//85wVHMc0HhcKqTsKyBl5KUxXHWN0DVlo1Y53suSJMnfESeH/ylclCjyTQcAO4FFbLS6LV975OB62v3ssYIDVeESLQuepjE5MI5hNKe1VnhsWRwePc4AabRJm5ocy4pY5CblDn4Xgc6btklNfmIZ/zVfqNDgqfMlGFH1Zx6HAnnY++KoZfTq+rjqX0JAdSVQfiaDSC9957L7tbCe/soyfcNGl0wLTn83lQWTqdDhwdHWXH8dJ7dF2yis8hWwdCnGKULv5b6hMhyj9deDmbzeDy8jKT2SeTSWHRZSi9NB/tnYsH0fw0XuTLR/pG+SP+brfb0Ol0st1G+B0XmfE+QYELVheLBdy5cwf29vbg7OwMLi8v4eOPP4aLi4ssrGYYCJkb6hxrrrR87YEnYqERH48IK8OnfcAjaanu3rShs2kZcdcN12WBd1HTvh46R9Axv1wuYTgcZndkas4iXPxwcnICk8kkG8cUTennodh2/k2D8ldpAcou933rIhWXbCDZkixlposZMXy/34fBYFCIf3JyAj/4wQ/gk08+AYD8eFieL/LmdrsN9+/fh3fffRdOTk7g1atX2e7xy8vLNZqrOOVusDvwOXS0bzyMpldIstAujG0cb1EUwdHREfR6Peh0OjAYDOCtt97KNgAhqFyhOVoleZSGcdEiQbKJ0vdSWJ6epMdseo5rOp+y6bv0uzphmQNC2kzqX1zvkU772JSNF+m0QtKbLPqc9O7Ro0ewt7dXkO3G4zG8ePEiOwUU0+d/ZaDZCeqw/5fhk+rOWIR0aTf+RiUXv+O9H5zgTz/9FPr9Pty5c2ftqLH3338f3n//fYiiCBaLBfz4xz/ODEJ37tyBz3zmM076MB90xqLiTOFTSstCMpZpTibakbQ7vFyKvIVe7Ux/KQ0c9PhM4+FOGUwTIO0H77//fnac2nK5hMvLS9jf34ejoyN48OABPHjwAIbDYcEZS8uKyj06eUOMqFWMHFI+XIguw4CsYZsy+KHQZBW0NaMvfqMow3C0vk/Duurb2hb0OHItP9d76nCQBC9pEikrBIUYY+sQdEOMZSFKtKtey+S1bTQl1FYxVm5S4CoDqgS5wryJ0HhhCL+z9pmqwqBvfuAGI+5cClGE+DGe9L/Gg335YFyepsWQJsW15MfD87T4s2SkDGk3nzGFtpFFIdHKNh6P4Ze//GUmu6NzCe8bDZm/XPn4aNKOTtbQ6XTgzp07MJ/PYTQawXA4VB05vjm+CuqYR0LqzjpHcD49m81gOBzC8+fPod1uZwtmsY1dx5M3BZ9hgoP2d5oG3w3PZT3UU+m1Nho/k+oXx8Lx8TEcHx9nR6Q+f/684KREXudrR99c4foewqekudrV17AfJEmS1VWv18uOx26qT1Cn7y7eSdcUtDbYBYO3BLQToO4eor/zdDAuOmNxl6tr5x6eCBZFUWZ3KrNjJER3tOo2uy671w3J9iDB9X2T9VUmL0nGlNL1yWqYFtrc6CYIvIMZbRpJksDZ2Rl8+9vfzvo2Lgji463T6WTH8N+9exc+97nPwWAwgDiO4a//+q/hvffeA4D1zR9vUj99E+HT0RFSP0B77bZkwhBEUQS3bt2C/f19AIDsJE3qg8A5azqdirIi/00h6av8O9eNtXCcbuk3D6PJB01gF2WOTdPkajeXbVt7L80frvkB/1ttSU3ZgULSs/gANL2Gh7l//z7cvXu38P7k5AQ++eSTNV4kXallgWXsheikvjxC4HXGAsgKAgrk4/FY/MYxm83g5z//eRb2/fffhx/+8IcZ8b/xG78B9+/fhy9/+ctweXkJ7733HlxeXsJ3v/tduLi4yBx9o9Eo69SXl5fQbrfh4OAgSxfPjaf317pgYXhVDOwWWJR3i3ERf0uTqG8Q0QuiUfmOoggGg0EW/he/+AW8ePECvvSlL0G3282Onn769GmWDq7Ew3xHo1HWJiiM7qLisos0aXAZj/DIIAD/sY6udAHCmTWdLMoq6hjeNflFUVQwqklMmTsUQsoSegddXXAZYiz9M9S5UAZSHrtsQLpBfbhpYx04LiwCooW/aDKHFI8aclyQeAg6Eaix3UdbnSukrfOuxajncn665oKyhosqcDmTQ+JLc7aPVpTtOp0OjMdj+O53v1s4LhdPL2nSAaOBlovnzft5HMfZEcUAab/kR/XS8Vi1z3LDi9UpVhcwPzyNCHUwbbxSemezGSwWC/j+978PURTBcDiE5XJZ+mjjNwG4O5Q6WutMG0A2+GFb0rypMZ/2vZB2K9vGyCuWy2Xt9YAOhRuk2FUZqym65vM5vHjxIkt/sVhkxvTlcpn9fv78eWab8B2dHbp4oQrKzt/XAXS3CkD9NpRNyWmbaDdOV6fTyebeVqsFT58+hdPTU3j58iU8f/4cAIpHM2oy97179+AP/uAP4M6dO9l9sdPpNJN7BoNBdnVVk0dd3mD3UMdYpH1mV2RESd7FRZkff/wxzOdzePbsGQyHQxiPxzCbzWA2mxXuILeWxWpzszh3Q9Pflfp+k1C2X4T4keqg56r0Dale2u125lei4bQ70mlZrSethdBUduxa0g6ByRmL4E4fXHVCCeEVhXEWi0V2mTZHFEXwuc99Dm7fvg1HR0dZo0wmk2wl5P7+Pszn8+xI4yRJsmN59/b2CkYcPPJIopvn6ypjXbCsdnDFtUBa8cPz43RwAwCdWNBQQ4+9PTk5geFwCE+ePIE4jiGKIpjP5zAcDjMHGd4njIMG26jf7zuNvGUc3nUOHL7KqSm4Vo5YjCy++uEKki9vK1zG7CrCR2i7837MFx/Q99qYcNWtplxLzuWm+gqvExcdFgcMj2dBiJNk20YI35ii/0PqY1Pzw1WBZaw2PTZ2FaHjsMxiFc0Bh+9cfM03J9AjXiV6pXihi100Gq3zVWifCumvWvq0vSxOgxAaLc54SzxfGfg3/N9ut2GxWGTH4eE7dHLWKZOF9nHpvdbvtW9VZQ0fXVXnvlBnLpYzjuPMKeda2EHbG3fBjkaj7DseDyvlZaG9rHGhbL/XeAeXmyUehnUVOj5Rp6HOUM2oZ+0PFr7p4tlNyFsaHdjPaH+zyoUA8jzCZXRX3922bPmmwzp3ApTjIbgzFuOjzQHHFva74XAYNCdiepvQ6d9ESHoVtyM1gapyhDWcVI4yMjvG4zIangaA/09OTuDy8hJevXoF5+fnWR70Lndev2gX/epXvwqtVis7FZAu0kKnL6e/TDlusBsImX+rpM/lm21Ak/kBijZvvE7x4uIChsMhfPDBBxnt/LRMSa6mcOm8IeF933YJ26IzhP9YZWsXqpazrM6jgdJrsbnUBZetQ0Id7YTpt1qt7C5zDI8LX2lY6TpLfB9Ki6tOy8itUjo8n9C51eyMlRgRHp/Bj96I4xjiOF5bWR/HsViZSZLAt7/9bfje974HAPkRuJ1OZ23bMtJAG3IymWTP7XY7O2J3G7A0gMbUrQZVX4fEeqYdTZtA+Hs6IFarVXaXCwp0s9kM/uIv/iKLh6tVHz16BJ/97GfhnXfegQcPHsBPfvITOD09zdJCgwaAvHN6V7AN5S3EqMfbje4KocY5fM/HnGas4mnjX5WjAELqkdPFlRdXWpT3YH+sqmQ0oaQ07aiSDJJ1pWd5v6u4KnTe4HqD8lTf+NQcr3Re54bssuMS05PSQtC8rDsiNL5NZQdMW6Kbz3UuZy7OcavVKqOVO6x8ZZTo5/8tjiTNOWrJT4ur5aXBZxzFfpgkSWHx3C4D+wuWaTQaBcsn1raU4tUpE1RJi55+Yu0vuwSNT4U44DS+SNNBufD8/DwzyJ2ennqP3+NjBvO6desW3Lt3D54/fw7z+RwuLy9huVxCt9ttbPy4dDgLOB+g/dhV/3hE8d27d2G5XMKzZ8+CaPYBeT4ursZ33W630L/rxI3joR7UZdDEo4ZRntB2RrRaLRgMBpAkSXbaGbUhcHmibDtzuxLKEYvFonLa1x3W+bFM/Vl3znM+Z4FVPqujz6N8miRJZoz+whe+AHt7e/CXf/mX2VULq9UK+v1+5mCi9pwkyRdWHR8fw+HhISyXSzg/P4ePP/44O+YbbbLT6TSzw+6Cc+0GzaIqf6J2wl3G4eFhtglrPp/DrVu3ssUNq9UKJpOJuSyuay4khOgCN/PFm4Ey+iG3RYT4jrYJn7M19N3du3fh1q1bBb/ddDqFn/3sZzCZTGp3Qu9CHUoI2hkLsN7pqEHNYiCi/ynOzs6yu2JpXvP53CtsU+OgZHyyGMEsDaQNuND8JGhGFSlPycimGRQko4XmmOV0oODInXmXl5dryvL+/n6mWON9Q7RN6FFblL5Nw+IIr9ORRX/7jKvWvsudpppRxUKfy1iOv13jo4zhv27wFTUhTgnN8EbT4++rGBFDwmn5udKUDPouo7yUDx2nFkes1akRoqxLvO1GsH0zcdWc/xRS362rH0vORZfwK4WlcSy0otGUygQ8nEt+4W0ZKndpafFy+GQcba6t0se0OTw0DQllFT2LQxeNfU0eY+eiQyub6z0a8bEfhtR5FcdqaPtqfbcqysyJ2M5Nzaeu9ELykurMKrvwuEmSZAbqOI4Lyr2PZ/C00cCNRnV0Gvrkq7pkZ04zPrvCciOPVTbE99oxwha5UKOJAo+h5f2yzr5pmXub4NkaDZb03gRZl5aRLvaS6pMfzy2lgXFd+otlHqLjRpMx3iRY6tKlH0thysJnr8Dnsu0UEk/ipa506R9fNH56eprtiI3jGPr9/lpeNG6SJNl1BcPhEE5PT7PjjVE24qcZuGwZbwK/uYHbVr2Lejbvn8iTcdMVbghD+zLeD0tP9nGhrF5eh7ygzWVV7bch2MU2p2iCL2n1v6t1UAa7ys/xVCdKH73yErFareDi4sJ01agFZWSCTfePYGesBLxfSnrPgatfOWaz2dqOyel0WjhCDQULfpQa7s7F44nxXtkmhGcudHFDUBP331BBSku/ivDpMnpFUbR2VxAOKHr0w7NnzwrHUMdxDAcHBwCQ1tF4PIZWqwW9Xi/rA3ikCs3vTUadSnjIXQ90nLiMFUliO6qRpqulIxlctPJTgxGPSw2MUpm5Ya4s3Rqu0wReBlfByPSmGlGuC/Ckjfl8vvP3HrkMV6GOE8mAUkV5lBY40Lx86c7nc+h2u3D79m2YTqdwcXGxZkAPcbJSUJ5etY1DVndTuSqKoiDHHjdqac7sEDqkd5vgme12GzqdztqRXpuAJKu7DOoUy+UyuyONLgCksgKF7zqPUFjvpLWWR0NVBzLGRd0L2/g6zYuSY3W1WsFwOITRaATn5+drRz76nKjUqHdxcQHtdhsuLy9hPB5n70N5xjZkIBdflnj3crlcO/ayDuD4m8/n0Ov14Ktf/SoMBgPY29uDp0+fwg9/+EPodDqZfrjr870VIeP3TdCDqTyDd2dqSJJ8N+A2QJ1oAM3YeK4SOL9rqr+GystVUaV/WfKnvAwdRn/xF3+R2ccoHbS/4xGOko307OwM/tt/+2/w6tUreP/997N+yu2x12mev8GbCWmMnZ+fw3Q6hV/96lcF+cXV3138W5PTd238vAkLtuosn6Zfb8seuSu0VIFrTEjf2u02HB8frx1JrNXHcrlcuwbVh7r1+6YgtXcpZyw3ErkMSRq4k4TeTcOJ5GdHS4ZJmq8vb8lQSn9zBV0yXPqUeA6fk4u/s6zu9CkwVkgdwxUfw9JjhdApjoI6ruxDxy22K083ZIABVDfUbgpllRVtbGmOy7KGPQ7X6lKrUZinKwk9Es2+BQFS+i7HrdbHqhpDXfTwZ1/7bQMh5beEKVOmXRKALChLb9l4u75SEaC5Pq2VnRrOLWlY5uUmDUhl207iGRa+Y0GIs0Dru9Q41G63od/vZwZ7dISFyl4aDVpYV9/jjrgQOqqMN19+vO0kh2FIv3E5LX1tx+nxoazzPxSW9uIyyWKxyP54uevkn2VlLYu84ssTsek50yLruXSakLTKIKQ+cVxouoPUbyQeNJlMYDgcwmQygel06sxPg2+O85VD+u1bBBCKKIqynVu40AH/QvLQeDQdS71eD/r9PgwGg+zYWrxHcdeus6nSj3dB/m8KdfFan3FPs81UWaSizZkhsgQNf90RYtvyfasjbymvELscf++TnVz5hgCPIJ7NZtnVGlIfl9Lu9XrQ6/UyGWg8HsNoNPLeq3yD64m6xthV6TvY72ezGcznc5jNZtlR3ChfaA5XiUeE2Fya0jF8NEj236p2hl2GS/cpI1+73rniWPqGVQ/ywaK7V4WmQ7gQai+Q0sXF3ugPSpL0ZAeU8y8vLzOacHGSdJ0M15O0uZu3mzZeLGVrgi9K+dayMzY00/l8nu12QeBq2MFgsMZIJ5NJdoQHBQ/HV6m7KloKaxHsJSMJKq88XR8d2EGknQi8fiS6Mf0QRqExF8lIqKWB9PX7/ewbOsyn0ynM5/PsHt/9/X1otVqwt7cHy+VS3EHtAg5ihLQ6sEnU7TyqYqznoEek0RUhIc62UAMhzdMHdMDz9pJ2sroMNzQeXZiB/QLTwftxOc1o4MFnKZ9dwLbo0AQAn5JNJ7oqhuZdRZn54AbNAfncLi7ICekHIXMApulTMkIM4hL4/EHzkRTbvb09OD4+hpcvX8KrV68Kqx2rzD+hhjRMI8QZyu9Vp3ORtoAIw0nOU5yPrEZJyjeroqrjFcPi6Ta7yM8ojQhU4Kg8QGVRGrdKeVxzHH3GRYiWI10taMqIvQs7YnfZ6CfxCmz7k5MTGA6HcHl5CYvFwsnvQgz8XJ+k/Z33u5C6K9uHsNyr1QpevXoFSZIUeINFd9DkedQJXHE7nQ7s7e2tnZx0g+sFi1PVos/ysWTRWywGWOk9l8feZCD/a8pBQdPk6fL658dcW2QyDNsEfHIHzsN4pRfyemkHOJ2HWq0WPHz4EPr9fsF2qJV5l+daH3ZRFr3BOjbRTrwfD4dDGI/HMJlMCjICv3c8xMHqktM0eaaKQ/Smb8tool6a4oN10Lpph2DVtELjt1otOD4+LozNKIrg8PAQHj58CB999BF89NFHWdrD4TC7AsYF6xjSTvbZtRNOanHGulabaGEkox+G45WEd/XQMD5G6FsBwJ0JljLx9KUyaXlIabsQx/HazlOA4j2uWj6SkiO1h9VoJ022KAhi22C74X1ymCY90iZJksxhZjWoo5CJwjb2jdB7wqR0kUaEpR9XycuVjytOKA08fUt+vvylPhXqUKC/JSNLyJiTwuGz5LCR6tMyNrU8LPS6aK9rArYoXjxMmf60CaXONxZ9kNrGl07IPMLTa8rwgJCMvXXnaXGG+fhXXcq/xK+wv1Jnn2Skpmlo9Lja2mJgrhNSv6RztbUeJdlDqhstTV5uS94om/R6vTXll8NiGA35rrWTlaf7xhKtO8nxQOvbosS78rcgRGl31ZcvHg27KX5fFWUci9rYoN8tkNr+KtTbrtPngkt2scgLVduHGsVdOmWZPKR5OFRelMJKczfKxpZ+z/V0zv9csoCUNi4ynkwm0O124cmTJ7C/vw/dbhfOzs6Cy7pNXOWxtG1osqPU5y3jNkRG1gzrWvw33XDukqlC586qeh1Pi9Lhs2Fpafh0Ahe08lj7C71Sw2WvSZJ8odd4PM7u0kNeijuLQrDrfXrX6dsW6qqXXUvHB00G4ZuWfHbcEN4j6YBl0vHlgek1WZc+2WzbcOllVpTRfy12U2me8NkYQ3wsV20savlqNhXpirHhcAgnJyfZggr849eT8Hr29ZOqdV5XHYbKBI3vjEVIxivukKGdl4bX7pmVlEqepwYU3vj9YlYnSShjtnYQBBo7eRq4WpjvpvVNNj4jFIdkfKTv8QirOI5hMBhkkyKfHHFFNQBkd8b67gKieeEKbdwhi8ZfPN7FVeYy2KXJyYImHUI4RrAdqMKAu4ssoIsrXAZfyZHiYmj8G90hS595vjStsoYzH2+pmkddsDplLOlobdFk+Zqsv20JLlahG/kpX9l11XhUVdA5ZbVawXw+N8/X2jxN2wD5TNOKUJU8XI5ULTw3ZvKxRMvto4uGRdlkb28vO52AG+0RPrkkZHWiNAYsCmaIIo5lwP5mkdvoSQ/UaIbfrHlzaO1i7UMuh5Fl3Gybz/icXigDl1nhSuuBz9eSbCrNo/iHbb7t+to1hPK7UGO61WHJ26qMzrcp0HKGnELjS4/yf5fRv27wtLvdLnQ6Hbi8vITBYADf+MY34PDwEFarFVxcXDRGRwiNN9ge6hiTZdPgcgKdB274+zp8ctUm+GoUyUeScluiL41QB01dWCwWzjmMyvAok758+VKUcaW5YpfmthvcoCpQfkHHTbvdLox/yadQ1Yla5xgKcQ5Z7fhvKix1UXbO1vTuUNnCp2/veluWoU+y649Go7W0JpNJtgATIL1HXToZomod7YItw4JKzljeMS1GL5/CjcfccqVUOlquDgFZMlByOi2NSe98oGlpg5E+02NKEFgHeIwJpw3fobEe752QIBliKR1cKXcp6dJAo0cPU8crlg0nzyRJ1tqW1wenFf9LR1ZpcULAja2bcP6EGkOsjr/QuK7v3JBvicvD8jHropf2Q80ginlK7aQ5HLT4VcH7nhZG4gdNgvcpjZ/Rb9o713tL3lXSQFgdMBzSuK4DZQRkaxxaviiKMmcDvY+zaaHC0lZNG3N96XU6nex4UkkOcPUTapDvdrvZIi+cxywnNviERWwnnyJYh6BL30k8W6oX6R2Fxh+wnqfTqXdHrI9urc6k76GGMq40Wfiuaxe6T5FytalVYdTmNAt4XVnqyzXvW8sdipB21PqoVE8h/Jf2Bxf/vQrKm1We0MJULWOVfhIyv4TQECpTW+Vn5Hv0juKyCB3j2twvyaBSei7eyuNzoL6m8ahQYBr4R++NsugJVWCV063xbrB5SDKMK6zL7uLSa31j6k1FmfnGFcfXHr525vM4vds6pN1cuoIUpk6d0gXa3zl/xIWQ2pHMTelnN2gWm7BBXjdoJ0ZyhOqSEkLmIFe6Fr3SpzdfB7j4lnXu0N6XGUvWPK38tqweXbWfVU0rpK9pdiRpnoyiKPN5jUaj7L12nLBGi69etVOMKE1N6cZl0NjO2LJMYzKZqN+40RUrVbtXxmUIpfFp47nCcnDjeZIka7RoSj9NH4/gpbtd5/N5dreudGwzXw3EV1FbjG3ScyjwrjGaHv3jzlgso4VBUbr4GeKdTkcd7BzW8oU6GVyG7BA6yiiIIXT6aLEa9Xh8iRbJMMzvc5FAw9C4PA5vc04HH5NSnhbjVV3QDMehfYfGDYXGg1wTnDZ5uya2OoWGuoXOphXTsmNIAo6F6XQKSZJkjq9NCgiWeaFsf6yCKIqyo/DpqnKJXq2vJkmSnbawt7eXfccFP5YySf3UpzyFGBCtBmLrPE/TdtHjMmBimnjEpEZ/WaeW695WDZLyLckEGr/leZZRfnm7S4sDrMY7i3JgUUzK8FFtDi8751jmGx7H9RwCre9JdPG5jo6/MvMbldW0fmdpn23w16YR2qYuOcklw9YhG/P/k8lkTf6U5DsJPnos77W8fH2ojKyPYfA4TP5N42ecJokH0HfojAVo7g4ni5zrap8qMqk1zq6P9Sq00X4bUpehY0ULy3VFH//lMgwtRxkarhss8ofvvRbGx0u19kBY7pqzgM7/Lhp4eFcYC1wyOrdb4L2xfDOEpLvjb43HNq0f38ANTVb26ZQ3SME3M0l15NrgocXh4aR2CdERokjeyS/Rw9s+VJ/2YRfGOte1+Dctjs82oukIPlpC8iiDqjqxK12JV5RJqyykOUay/cdxDJ1OByaTScEZy/1a1vxc36WFWdzXsCn4+PjGjim2wjUIqt4TaskboLoRCOEqh8VYiO8pc6aGPh7HdfyWJli6jAdl6gPppUZtq1GMIo5j6Pf7sFgsRAc93leLTllMU7pPsAlBs6l+6JsEuEBA45VJT0qXvuNpVQWm0W63s1WstL/w+x8Q/Ax53rfpmKALE8q0U0gb1JGeNV1fntzoULWPWgxVdWLXDVIh4AZby7Hs7XY7W7ySJAl0Op2Cso31U+Xo07qxiTabz+ewXC6h0+lAq9UqrKDT5kIXfHNt6NiWjI08Pc14HQKfccpnYKRxOJ2u3QRSvovFAobDYaFvUrkG/0vvOLA/W3iWJl9ZFSkMhwI5ttHdu3eh1+vBZDKBxWKxdgcXLQNPmyohvjamJ6hwHsHLZAXn96H9t8r3uuPVkQ9V7uvkjzQ9vvhRaz9sb0qn9YqHptpml+Az4ocYLzSDjmbc0tKgaVGZFOcdPKnCZwAp235lxnEZuOqeGx3xnes0qrJjbTqdwieffAKnp6cAkNazdMfUdcZVGct1yXu+dMrkY5lDffJiiFF927L3NrEJndCVh2arsNCDvMzHX6Q2dsmyVO6oEzy9brfrtG9I8r1V/3jT+/WmcVX4/i6Byl14XDEHt5FYeYjV/hYq82l8Q7ONc5rr7ie7MMYlG33V9Oqsp6vEC616TZP5SXUvzbG4gY+e6CC1naU9m7B5YJhNt30tzthNKY80Lw1W46T0PUSYl/KTnn1OUF+e3FDqYuaSkdDH/H2TSlmhmw9CixGVQrsnmKdDnRO+42ksk6kWnsax9I8qY8FnEKftaqlLl1JhTdciRLggjTVq5OL3vdKw0iIMLX908FLDuMuATvMpUy4JlgnEx5PKpEvDuSZLXx7Wug6Fhf4mjOgSDVL71+HQKwssdxzHmZDC76Jsok4s/Kppw4tPCUKFS3LGhtBlmeu4guAzdlTlHRof5mn70giRWXxhLPxrsVjAeDzOFtFgP+V1SMviamdqXJJkB0lesioEWl50N+z+/j4MBgNotVownU5hOByWUrh9+fvm7dB5lvNLPudpyr6W564on1qfttIn9beyZXOlIxk6aVtofaIJo+11gWS4Cq0rOs58cxsfk3xsoixJF3r6VnDzdH16I0KTx63QZHgXndI7ngZ9R8dmVX6RJOlis7OzMxiPxwCQyvBVjoIuW2+7hl2iqS7ZxppHGXnUyusl3faGF9thkXHKzt2a/cyiz1p0JI2PSXn73mlzfp2Q+qd0Wp4WV5JPpHBvKrYph9VhV/PhusmZfMxKtnGpTjlvcNWJz1YawsfKyP4SrVVsMLvU/pwWq03Qkp5v/rfaBkLjWKH1hSZRd38JtSVJ75OkeIKspk9peoiWV8g4ddndmgKnlee3cztjtwXayNL9r6HplDGo4q4oitlsBovFAnq9nioYdrtdaLfb2Q4iDXxSchkVqq5KtggB2rZ0dM4Nh0OI4xiOjo6ybwcHB9Dr9bJwL1++dJb5usJq3Gkq3RDhQGK80qTHFQ7sq7gTgTqlfOXFsJgXn6jRiVC13iz1gPlseqW/psRWSQ9gt4xEZVF33Uig/TQkP+z74/EYer0efOMb3wAAgFevXsHp6Sl8/PHHZoW8LL2bhNXwtlgssrtirbvLENIRuL1eDw4PD6Hb7UKr1YLj42OYzWbw4YcfwmKxyI5PlOZy6uzytXEThhqXQ80KLuz6HBjIw4bDIUwmE7i8vMyuUqCyBU2XLijQ+C3OD8jn8T4RyQlG7yGnjltJeXU5U30KB72vmX8rwwf5QrEqc4/UnyxGSJ4GNdbtIiw802pgwe9lnTQ+477EG27w5kEbU65+6lLS+Vi3jglpbqg65ml8rkdSSM8YFvkq8thnz55Br9crlAnvqS0rL+8qP7uBDkuf1sLwo/C4XKbNl3EcwzvvvAO9Xi+Taz766COIovQ+Tu0ktuvI2606qmts0RNOeN1pertPn5dokvQevmC1aR7QZPpaffB+jnxSit/pdKDdbmdXugyHQ/VEPlpnbxrv3LXyNmWzuW7g5ZLum+d6J+dHZerGtckoBFYH4w3KoYr8uAu4jjIGAGR2d7StAZSzoyC0saLZ7HzhNwFNlt2qM9Zl9HPFKZNPKEImRd7wIQIhN15K33EnkObFRwMmvXPWSncVY5zL2InfXe0rOYOpwo9HEdMdsnt7e9k9f4vFAs7PzzOHtcVAoLWVVC5fGrsEn1EyhHafcuQKJ+WjGZtoP/DlxRdIWMaYFobXQd0OlCq0WdJFWOLS+tba3tUedfV1XxuEoMz41CZnn5FUmwe0fu7ifZZ+mCRJYcdeFEVw9+7drP9TJ2HdKMvvNuHQoUaZMtcVuMLjHRa9Xm/NAYu/rTzVJTeEOI5oeK3fWJxPPF8pf5/zgMfBncqagyC0P1AZiMoE2iIrbjzS5n2LDEfzxbyxr1nqpM5+WDZNH6h8qZWh6vi11HOZuHXOG1I6ITxOo0mKbzVs+sbmm4AyBkhJvvTJLTScJp+G5G+RW13wyRacthB9UtMrXbJvSH8tO2ZxMRX+Pjk5gW63C8PhEKbT6VqYEITwsZA2b4oHaShDm29uCqkXX9g65T1XWa31wI3tUh/X6MUTMegiP34M+XUHlbmonG3VL/lvqe60tvHZinhYTW7l9jEr/a68LOltq38kSVLQgyR7YLvdzq66wTAu+8yb0t8lbEKHpbDwWPrfEofHv65yJJVvpG80jAtWvVdLxyfja99cPFCLW+e43NUx7tPFpPrRbG5V5LuysOqQV3FcWu0WIbqPJf0QPc03X2t6lSVvV7rSO02vcy0QuNkZC7kgR41yALkDSArPf1sbXgJ1SGqG5sViIebBaQ5N33U8bB1Mu4yCTGnpdDrQ7XbFuwHiOIbHjx/DaDSC999/v3Cnk3b36FWFZFDl/TBJkuDdcjRdixJWVUigYw0N3/RoVjTy83LQdzQNXmarAknvrJWUPEs5rPltE7SvhPQPTZgtOylXOW2gKsq2VR1tG8L/FosFLJdL2N/fh06nU7jcvtvtwuPHjzPj0SeffALPnz/P7pl9E04H4E5ACTiWpZ2VAEWjzenpKVxcXMDdu3fh4OAA9vb2RGFJ6j+8P4c6AasCjTFU+bTGQ1DZwSLIcjmCni4gLZYJHT/oFB8MBjAej2E4HKp08zJJSplL6cW6a7fb0Gq1YDAYZIu8mjBklFUU6PdWq+VdiKAp9pLRlMsRmrHRZfxoCpriTb9TWSFEZqGnZvhAFaiqdRDKF3bNoNYUPb4+G1pnPF2sd20+sBqBsJ/RfqfNAXXDYhjw8V4q59P+7zJKhhq1tHbD3+PxOAszGo3gP/yH/wDL5RJOTk6yUxbK1uMujZVtoor84ZoD6oSPX/v6uzbHAxTtNlznwBNlZrOZc+dr3brKtni5j+d1u13odrswn8/XFvNbaebj3mePsqRHaXAhZPdTGT1fQ1PyvUWP0MqcJOmOWPxDXoqyEvL8benhu4xNjU1JDtdQpY3elLlQsjPRb1o9WOpH08Ms2DXZ/bqgSr3uSpvsCh1NQht7Ze5yxrBWuPS4qv4LK8rI4LU6Y0M7mUtI1NIu04mrdP4yHaNKHIsThCu7NK7mQHYJeZpBt6xS5zKcckg7UNCRisoSCpJoGFsul/Dw4UPo9/sAkBrV33//fSetFuVOisOVPyt8Bk6JBhcsSqgFrvGlwWXc4fF8dEkGGtd3qY9zumgYF8PlaVgUs7oEsqrpWOJZ2oIqZDQ9yVhpMYZK/TykP28bZZRzK3+T+qeWDx/f1HB6enoKh4eHcOfOHVitVjAej+Hk5ERswyaxiTzKwMWb6THlSVJcTY47IdDJG1o2n9GwzHwUCst8YGk3HsZn0G8CKL90Oh2YzWbeOZfzfembJW4URTCdTjP5IoqibAEYD+eiXcvX5ySRvmk831VW2s95PI1my7xH6ZfSsMDa37W5iOdHy4xH8YXSF9JnLPRL6Un8n4evYjCyoC7l39J3XfIXXksiOUGkvkjfuerKpTNJY8qnkGsyEP3N+5u1fikfsfBbLQ0Orf/zMFIaknzi+26hSeNV+A53xi6XS7i4uFDTaRK7KM/sGiS7QF1pVUnHN2dS0LC0H9OF29xQyNO0yPouercBjdfhOMQFyfwKoDLjgvNNi/4o0YjvQviZKx/NviWl59J/tXnCJ3uXhU9e5M8YHo8njuNYPDXOlc4NmodmK7lpi3VI8n7TCGkHC12hfKwqQmjxycJlIPEt17MmD/M0y+hgVhqs6TTd/+q0C4WiTrkuZP4vS0uVvuvqa3VB06G0/Da2M9ansAPozhT8VhZlDUZaZ5JWl/nysE66UZTfHUtXwOFqbGnHJ9KCigXerSflw3cX+kDpLnMGu6tepMvXcZdKkiQwGo1gPp/D+fk5DAYDGAwGMJvNAADgH/yDfwBf/vKXAQDgJz/5Cfyv//W/go9pDqF9W8qUC5KgUlc/1OLxuxetKy2l8eIyBPN7ZtFQL/Vrl7FY2vnNy1VF0AutwzqESovhLlQxpQYyrGvMh7d9GVr5jmc+GWpl2gUFpaxxgvZ5qdzSfMfrYDQawX//7/8dnjx5Av/wH/5D2NvbgyiK4OXLl4VwvqNd60LZughFiCECIfG/fr+f7biczWZwenpaaBPrThytj25yd5SLNgvfdxljsO5Q7kiS9fu2LeNTk90shsxWqwXtdhu63S5MJhNnefg4KnMSBrbbYrGAX/ziFxDHMTx69AharRbcvXsXxuMxvHr1KqNLKpeWplRm/kzlH76bpwykPmhVOix9yEpDXWPAklan04H9/X0Yj8cwmUxM49lnyA8tAx//dKzRb9b5s846RJqahEVOTpIkOzZxPB4XdtVL/ZLKtpLsKLWdpI9J8TldGs38PfKY6XRaaO+Q02h8Dh+JBgs/oH3LJeMjX9e+0/e83mhdle1TNL3VagUXFxeZzrdcLmE+n5dKtwwdu6jTlUXdcplVx2iiDn3zlPSOyi0u+xEuCMF3e3t7sL+/D7PZrHAdkpa3lVf4wm8TtL5w3sR67XQ6wX3JdzIFgK1/8nQ4f+TfOC/x2eOwPZq4S7Bu+V/i265TgfB7q9WCXq8H3W4XWq0WvHr1KmvfXeyLoahSjl2rgyrOhCrYxTp0yYA8b8tJkvwOaVe6PjpCgfRJNITKi3VjE/abEH+LS4e20rkLNsIbpHDZerhdH8NrYTX/WxW49P5QlOWDWrzanbGaUGJ1bG17svQZtFzxLIK6yyBKDYqS4IUCNE2XKscuJURibpow4CpHaOe1GNV5/aBTlRpAl8tldmznYrGAdrsNg8EAAADu3bsHf/AHfwCnp6dwcnKSKVwvXryAs7Oz0pOfqy9b6iFkUuLhLcoNxuX1V9cYcvWDMgyN11tIPPxPHbK+8SYpNL6wlnL56HalofHFMnmVVQAtSgBPWwvjc7SEtNEuC1VVBQMtnqXtFotFdn82NSYB5M7zqseyu/qSa4xUgatOLXlo8SmfQQcAHk93+/ZtmE6nMBwOs767WCxgOp1CHMdrR7VL/dNn7N6EDGPNg9aFtU5d8xs3xrvkPCvNLhmFhtGcF6F1gf/xjz8D6IZBnl9ZxZ6WJUSu8oWrMmdxBchn3JTS3VTfx3za7TYcHBzAYrGA8XicfeeGG26ADaEzRC6zhLPoSNcBtG+g3IbXrVBei/CVn/ZPOmbpM09H4xkWYP/h6Wq8j8tDrrHhGv9V5lmfPBaalkSPb07hZeFtwOsBjTTSKUlNoal86kg3RNbk8kaZ9pf6aYj8tS3wucqns0jjFeeM2Wy2tgDMpReF0LeLwHLgInd6Z7PPlsTTkOrdJ9Nb+7Y1HM/DJ6/UoWta0rLySYRPFpTsPbzvR1FU2O1M39Pfu6xva6gypnZpPPJ+usm2uEp1KI0xn00K/1t0kybqvc40m5hvdmEcaLyzzrqz8riqeV4XmaAMNB0If+P/0Dp26dxV5/Ay8TVdPZR/u9r/5s5YB1ydSGIiSSLfQ0WFHym91WoFcRwXnI9JkqwZK5IkEXd/otGg3W6rnVhaOWgxfkjlCFlZ6KoXGoZ26tFolBnZEJeXl9lRVr1er7CC+q233oJ/8S/+BTx9+hS+/e1vZ3T/6Z/+KXz66afZas864WP0mzQqSMYPrb5dzgJuZJHKKPUby6TnMqJZ6aR5S7uqtX7JDc1amNBy+RTOTcFXNgqXk4G/u26CwzaBfd7Cd0MManjE7mAwyO6OvYpKthXcII/vEJwX9Ho96PV6AJDej3Xr1i14+fIl/PznP8/ijEYjmEwmmWEOIHce8Lx5Ppa63vTcE8IPND7Lnf4U3EFR1aCAaaBB3ko3jeuSr3he1ICF5cdj3TSFXWp77dlaDyFKgSQPSgh1aGi8hs/RrvKHQupzrj7Lxzy202KxgH6/D/fv34fZbAbn5+dZ30V68ZQZlJtpWazjpIyDQyvHmwg8gWA2m8F4PIbBYAC9Xq+wI8s3hjTDvwV8gQ3+drUHX/hE+4rkSLbwHVeeLh4aIl+XhUsn4HqBCxoPdtGPTnqa3zYQMm/uIqrUnTSf0/e7DMkZhe/xnYTVagUvXryAbreb8Sb6bRMInQtD0pHAecndu3fhnXfegffeew+eP3+e1R8u7uQ7WbitQfrmu9feUhYu71h1Jksd+OS5bcIiv/E2kO76nU6n4ilmmk1n1+Hr31V1nk3iKsxvddZ3FYTyEarXcVTVU0NokBAyP+/CvLuJNrY66q4Cj5KwaV5TV35V0pF0o7rbuKzvJXRcSWk11Z43ztiS0BRkyVhMQY81lAR+TBedq5LAmyTr270BckHOMpDKCKOa8dBikPXRxAV4NODgN4rlcgn/6T/9J/jggw/g7/29vweHh4cAkB6xIx2bi8e34NEtPF+sSxftvLw+o4oLlr6jKZKa4hYifPC22IRiQmmxCkVan3HVva9eaXxqjJfotObroh/TdLUFzVei21ImTMM3SdFjiH1xXX2KP1sMhlZo/bFqP5XihwgJrj7Beb5mtPD1Z2yj1WqV8Ss8mh0Asjs1Hz9+DK1WCz766CM4Ozurbdy62txSZyGOKV53dQhsnL9cXl7CbDbLFvZI/RfnHtwZwNvO16Y+uGSCkHS0enLNEWWM+Fo/t+ZbVgYASOfj+XwuyjchTgENvv54eXmZ5TObzdRj1UPbM2TedtHnmkuq8gArLdq4teZvUXBcMgoFyom3b9+GTqcDFxcXziOuQ2UHjUbOs6lM4ZrLXWneoIimDVIhde+Tw8vmQ8eQdV7VZD8tHl+4yI302vjl+h6Xk61GFg4aBxdLuMrgS6OuMbRr4xPldK6vSOHoN4sxqi6DnYS6jd4WPQQAMnkZ78iUwklHRy6XS7i8vIT5fJ7ZHGj8JvlQXX3O1+bIM9AOcnx8DG+//fbaiTo4n0qLOjX5l//m9euTkXxyIZV5pPhSeCk/Cx/WvlntAT5eaoG1v9NvdDMGd87yutP4+S7DR19d8zF+b2LMW+qbz7musFXyrxq2zv4S2t+1E1Uo76lSfyFjN0TnkdLFtKseXdzE+K27jcvyRE3XLosy8knZvK3x6uQ3oWlZZBCtzlw2iDJj0GLfqxK/DCzytCUNX9jKzliX8QGJuIrwGYp9nVMyHPL/kjMW6xMdsVID4m4oHo/fs2kpo8twxI1MlrSkOFRZlBgrfqP1gTtV+HuA9NjOf/fv/h3cv38f/tbf+ltw7949AEidsRJQAet2u2u7X2azWeFIaE6nVlZfPVBozM5atxYaQtvbJQBxw2KV8e0yElkmZ0m40trIN8nSONYj8uoyLFSJzxm5qz1cYTAcb1c6brFepPyqGCR9KFPPFgO3pvi7hAqpX5YRqnwTsPYNF4j0ej3odDqFUwDQmPLkyRN48uQJjMfjzBnb1FyrzWNNKIxl4DIin5+fQ7vdhr29vbV4uPNpuVzCYrFQ76vT5qwQhPShTcHCR+i71WpVMA5LCqTUJyQ5Rhvvq9UKptOpaFDS5Acpf+m7b9fBarWC8/PzQnzJGSvlJ8FnQNPkP1/cEPB68+UjzbcuWNvVFU8zFlrSiaJ0F8+9e/fg4cOH8N5773nvG/bJ9BKdLt7nQxOGvasMl85xVfTGMvN6mTxov+PjQpMLpTpMkvwOcK4najumMF6TbaLpeBqq6kpV5nAf6qofOr/yq4ukRUo0nqSzSfr4tlBFDnLprAAA/X4f4jiG4XBYqCc6h6OTkeuCFxcXsFqtoNPpZIsh+dgro3PvAtD+geN8MBjAkydP4I//+I/hww8/hF/+8pdZ2DiOIY5jWCwWTvlEek/ryCVfaO9d+lyZunbpeBq2YTy35qfVDS7Q7ff7AADZlTaaDIzxblAErZO67D40PZ/OIj2/6e0UqqNquosm3/jqV7PRWnmSz855HXQDa71K9mdXX/fp+qE0NYGQfK7KWK5Sdxov47YiTa/Q9G0pHscm6leTBX00UD6gxa3sjL0qHawJuBRY6Z2LgS+XS5hOp9kdd5i+S/nS8gx1UGjpuYypUliJ2brCarQCAMznczg5OYFutwt7e3uwv78P3W4XLi8vsx1MHJ1OB27fvp2l/Xu/93vw7rvvwp//+Z/Ds2fPsuOPB4OB11Dqg6U+dgmWyU8CddS50qVMKqRuLcZWDEcN5FT5o2miMsl3V4UaIsoYbbTxTfMOUfbKCI+hKOswlCAd7crT4g6RshO/xHPKwsoPEb46q0oD9l26uGSxWGSOIuow36Yw30Q9VAXSg3fEolNPGoOaooWQHDDS/LYNXr/Juqd81ldnPj4rjVc6xywWCxiNRup1DC7Zqkp9cMOrtFjHqkRLDlyXTOabY62Q8qPtpfV7n7wooSlFmddJFOU716nxXEtjuVxmO5zQmYGGaKn9LOOY8lntKOuQMvryuy7A3TrL5RJmsxl8/vOfhwcPHsBPfvITODs7y46Pn81ma3OZpieUMZJK/MY650sOBp+MI+XZBCxyG+333W4XACBbdBTCz/F33eXa1rUKuya3cCyXS4jjGPb39zM+eHFxAbPZLONtlv5ohdS2TdRRE+PCpXfRuZX2d5yjT05OAKBYhyE62i7zcGkXcLfbhbfeegvefvttePLkCSwWC7i8vISPPvpITEMzriKszhLXe/q9jJ5lsSe54liwSb5eBniijCQrUewq/W8KQpwLbwpC+qSkK1EZpizv4XRouh63QVpp9vHRUFqvKqIoynQybfE7RUg9bxq7Stc24LOh+cJJ6YXyhTrAZUnOE5rk1zfHFHvgEj5dRsBQwy0aIumKNlf6PkdESBxN0AwVVl33lErpu7BarWA4HEKSJHBwcAD9fh/6/T5Mp9OCM3Y+n8NisYA4jqHT6cDh4WGW31e/+lV499134Qc/+AE8e/Ys2wXb7/eDaCkz+ELK7ArD0ymjQJQNW6Zv4fcyzNFaD/hHHS1USGq329nqZs3o6hsDljFoKU9Zxl02nqufSEZJS34WXhbqZC7DazQHQ0jfteZFw5Wd6F2KgUvwXywWEEVRdoc4OiQuLy8L94JvSxiUnJPbyNvVPjgX0SPoqXEO4eOvZfo4De9rI0sYiTdVqXuro43n7zL+SnIRp11ScGmZkGfjMYN1zNEhDkj63mqclfih5OTQHLRSHIkeV35WRV8zMEh5+r5b3oWCz72SPIm0cQMM/uHuMXSaojLv649aOUKVL6k9ffV13QykWG6UyXFMP3r0CL72ta/BBx98AC9fvoS9vT2IoqhwDL+UDsB6fVnnfGq8C5WHabwkSZyLAFzykTanhM7fZfUzjIfyhEaLJT1NdgmdUxB4KpFLf3gTjV64aLHX62UneIxGI1itVpkMiHUWsoMRv9G5pwx/2zYoHThPuGjTZKbLy8u1dGharrqJYy2/0Dqqq39HQIuXJABJssq+AaQL1u/evQv37t2D+/fvw+npKRweHqqnihWo3MA4dMnfHBY9ROJPofKzT27a5JjQ5gCcY5fLZamFBW8ytlFHrj5VxW50VWGRbSTbHD5XvadaykuTiTgdlBafzCqFq0pnFWyyn1E51LLJjOtSWt2GYpfknU2Pc6k8VWnw2Qo0G4yPrlDaLDKbj8+gzUCDtvDR108s5bhxxlYEVyRDYDmiSVoZgsdYSpMCQH2DK4QR8bDSwPAJwNJ9r0mSFHY6UJydncG/+lf/Ct599134Z//sn0Gv14MvfelLMBwOs7vDpOMO2+12lhYe+4lOXquBs07j47YEL6vxhSrvUr243vsQwoR535CO4kFlJI5j6Pf7MJvNCuOLGsdDDHRWo14VJwmlr6n0EU0IGSG8UOIxLkNn6Bipq560tEJocjl+tH4OADCZTDKD3GQygW9/+9vw4MEDePfdd9X0ut1uwaFVJ3g9bFtpdM1TSCvOG3gnGEDRAAcQflQi5pkkydriqU2N37Iok7drnsIy9/v9zNiPTrHVapU5WiT+KRngqUyDSrZEC87f+J2Hs9QzVxTos6seaB5SXq4xzeuSyneaDNUksN9zxXfTdCAtFNKR+RTooPjggw+ycT4ajbI6xdNmaFoUnJdZaaQKm3a3sYtu+m1TbbwNntNqtaDf72d19NZbb8Gv//qvw5/92Z9BFEXZosjRaCTKj8hfAWyLEvgzDxNqCMNxgbRQWXgTqLOPtFot6PV6EEVRdiSrxWjh4gV1zP3X2VnA73AMrS/c7Ya8TZrrmjDq7rojQBrvuPAjjuOsf9Mjnuk8hzIb/qbfrEb9O3d68M1vPoB2Gx2gOH9K9BafXcnTb0U7CA+TZO+ShMoi+fflMoH5fAnf+c4LODubZXYjmu54PIZXr16tzZMW3sDlJ5/8a9EXyvIkbf6gz2Xm+10Cyth0/C8WCxiPx8Fp7foYf5PxJrWN1TZCZW4+rvG71R7h0/Nc9GmOGpeu6rIlc1qabvtNyK+0rXBupnUxnU6d8juAXs/btp/cwAbs13zODbGx+XwwmCb9j79D7fo8vTrh6+uI2pyx2iDZpNFhE3A5DCxhJUZMBUduHNPSduXTpBPCAp8xSjLGahMSdTyjEoWK6Ww2g+985zswHA5hNpvBwcEB3Lp1CwAAptMpzOdzaLVasLe3BwcHBzAajdbyRwPyfD43GdEkGnm5tXAuY2tZJUVqe4mOsg4tGl/L09VPQsY/N4xrtFCDqKYM4nHf2H9C7qRyGaQtjJXSX3YsutrLUt8uGkP6uZaulmYoygqfnG9yGnj9Vy0rd9b4eKOVP7iAfK/b7cJyuYRnz55Bp9MpCLN8rsAxIR3zKpXHF8ZHv4+vhNS/K6z0zdIGKNSjIxYX6PCFHGXGq0+Q47yEPjcp9NGyhDqEtH7uaz88ElarA4SPr3F6NF4cWkYJ1rHMaSojb0l5SGUoI0NX7UvWfuOak13vfOm46HKVGfncxcVFNp5RjkM6tBXYlrmbhnUZgDU6pXYN4V91YVu6FxpikAfv7+/DnTt3sl1Y9JQHXzr0f9m5NaQeKN+j7RwCi8xukfvLtJ/EMzh/pmn75Cj8bZkLyugam8AmxwHVT8rWh2QLoN/o/6q0SmlL35qEVZbkz7hzmOuC/JnGo4uxARLo9XLjcRT5dfNbt7rwzjv70Om0CH+S6bSgyBOA/S46XvE5IU7YVL1N369W+f/5fAU//ekZTKfpPNjrtaDdTgBgDrPZEObzMSwWY4jjFfT7MXQ6EbRaEczncr3R32V0K97GLlmqyb6n0V6n/FIXkCZccEDvPeYnf1Stv03JJGVRhb6QuHXPX2Xte01gU3VYFRq/0RZWctq0Og2xidVdVotd0YWq9ITafHzxNFmcOlNxfgaQ77236s8h75vGLsq3AM3PTRYbgC9siE+gTD4hQL7C9fkqeYXY5ylqc8bu8gReJ7iAiuCGWVd8GodjsVjAarXKVnlK6bfbbTg4OID5fA6DxrVyAAEAAElEQVSTyWSNDlS662AYLnq5UM0NudLEaJnM6a7F2WwGr169yu6N3dvbg16vB2dnZ94z5weDAfR6Pfin//SfwqtXr+Bf/+t/Dc+fP8/uokXHbdNwGWLrSr9quhajdqjR22JgpvmHgMfH3YCI4+NjOD4+hiRJMkcW7S+aI0IyTFNod8/5sGv8EQVaLgyG8A2pX7t2DNWFXavLUIQ6fmazGbRaLeh0OvD06VP45JNPsu/z+Rx6vV7Wz+mcUYWGEGza+Ip50R1w3PhJ707iZe33+/Dw4UMYjUalVpVzWixGREnQa9rYZFX0rGWg4fm38XgMk8kE9vf31/qgZCyy0MR5lO+bZlgLrWerLNfpdLI+p+1G5/Uqld83t/L0JPCdwnXCJ7OhvFk3z/e1QbvdhiRJsl09lC9wWE7C0OR63n8xHS09yncsxqFdQt3GNloHeIUEXVR5FeZybHPka5Z2tNSjJG+6wmkyqStdS3gNFt5aBb4yXxdIuy3LtAmOH26U5rvHr2Ldcf7Kv0njBI283W4X4jiG4XCYLbzFOZmmIc0Lq9UKDg878Md//BgGg/j10cMRpEHzY39pc0VRBO12BAcHXYii4rcQ8GaSHLL5q9wBS8PS5/x7/rvXS+Bv/+3PwGKxeu28i6HXOwOA78P/9//9AsbjMTx8OIJbt/ZhNnsHVqsEJpMl/MVfvIDh0D2fb7Kf+Yybmh7vi6+lwb9p9GyiDqTy4DVe0m68OI7Nu/Q4mnA+1S1PbCPuJrCp8XTV6pDy7TqPI+bjvmy6LnnLqntfByDv4ZBOtXSlAUAXGdWvz96gXkj9l7e3Nm7rXlwQMoatfTI0XUt46ftOHVN81RkTp58ylhBhEu9O04yayPSk3U8hnabMKgffxMWNwJJy5aoPSaheLpewWCwyZxqPO51O4f3334c7d+7A8fFxdjcs5nvv3j3o9/vZiny8Y/YqGst8aMrY73IoW41ToflZw1DDJ/YZdF4BpI5BerwP9ilXOWjfLVNun/FMUxa54aHpPqnREeKQDZnUtHzrAK3zEPo5fLw6FFbnq5Y+Op/QMTubzbKw7XY7O2aXhm0SZZxcrjLy/l5FOdIcaVgvuHNOc1byd5Y5UQpL+ZEWNpQ/V1EWKQ1l83LFp8I2Gj4t/TDUSCTVnzSHhziXaVzaB/n8IuVBjbxan5HyouUJUdi1clnbVpMPfOnzuFq+ZZ0OVcY81iHORS7ZUhurFtrKhHGVS/tWRm6raz5tUv/i40jiwZxvSuMZ29rHwzWUkZGlscLTqgO+eTJkfEoGfF8cVz/WeKAWXkp/U9D0zibB53TLka8aMB3UYyUHixSe/pbkql3UcTX9isJnb4iiKLMTSHEGgzYcHOT3otJFo0dHHbh/vw/9fgxx3IIownRzR2s+P0o0uMunVTl/T599v5MkgqKDFnVf/A2v+00Et293s3BpmBUkyQjG4xGsVgn0+wDdbhuSJIbFIoHxeAG3b3eh26ULj/P8JpMVzOf2u9Mor3bJ93XYLly80WfvseTf5PzI4eNf/MhpTr/Go7W5tglssr52GXXbFK4LrGM+RKbjccrYJ6voWK6wuzoHV4GmJ/Nn9Gto8TGO9N4VR0JV+bNOvhUiz+wC6ujnHJqdo4z+VQW+OZG/w/Hqo72Ocb1Tzthdnbh5pUtefpdy6puIuVHBZ7BG4MpQa0cvMyn5JkiLIlW2XamTZzqdFnYBU2fahx9+CP/8n/9z+Bt/42/AP/kn/yRro/l8DqvVCm7fvi2mTVcZc4eSpNBKaaASWGUgVh3IkqGqjIOsTH+xoIzjwQJcAY0ro9vtNiwWi+yeQkQURbC/vw/9fh8AAGazGZydnWXl4yu3XM4Vjirl0vqWRZCoo07rMAI3bbwFkAVlF0/d1XkkFHxOmE6n0G63YX9/PzvuZbFYwGKxgG63C61Wq+CodcFXTz6Db9k69vG6EMeHNH9K4zRJUic2PUmiqrGnjj6G/GtbhmvJ6UHlidAyRlEEnU4nO9UD7/C0AusDF6Hx+1RxrtXytrzTgPnw3TS8vV3joi4ZzJdmXShTPy7FsgqtuKCEtq+VR0lGRUv/pXRr87sUh/KaMnIWpZnSge/fNGjjhr6nO+HrnJt2QV6g/Un7hihDL/IRvCsd9Vepvpo00HNDW9N1v2nDJy/fZDIBdKSGlhN3/Z+fn2dx0ZgpLTjx8RDuCONxtHI0DZ99gd7pyoF9yNLGX/ziIfz+7z+EVit1srZalIcD9Hq4OJs6VyOQyAupHom09F0ihOFtmv0CdLBK6ebzWDH9/Hl9V22SFN/j79Uqgb29NvzxH78Fq1Xy+i99v1ymf9///gn84hcXWVp19pey6ZWRQUJ5g1VGqAsumZLSgldtSeHK5Lnt+fBNQ9Ny/lWBJo9UgSRj12nXrMOGVjbvpvOqag+kp3smSQLj8di7QFu7lqEKTZvCdeadITYqSRe3jjlrHbrsENZ86rYjW3RTFzbmjK3TiLlt+DqmRQm0xOVOX1ROqPLebrcL57BvipGFOGHLOIF5GrQuaF/C+hgOhzAajbL7AFGBWy6XMJvN1i4NXy6XMJlMsrtF2+029Hq9zIHrK5v03Wr8q7sdkHFITisprJYOjysxVUtaIXS76LXE1Zww0+kULi8vYTAYQBzHBaFAytvnFAkRFOuulyqQJhXfhBEyXrdhTGuax9VhpLI4EVxxXH2RGqmow4oLPSHj3QLu/AiBZS6QxrQlHes45AZon0AXomj56pnmW8f4DnHgYJ4SL6iLHlpOyUgq1YMEq+CuxS8jpPu+a2niPbn8+FJfP9HS4/3eNweX4cFRFBUUXlc9avNgEzyfO4iqQJITy8oaFCHldskZdfOCXQfVZ+jCRd6XNZ3C1S8kWVDK2yXfuWRBiT5rX9LmD6n8rrmuDCRase5dedFvWpldfVbj83T+8cl1VfTqbYHW29HREfR6PRgOh7BYLDKnrFa3Wlp8HqX156pHF427xHtonyvTp/Bd6qRewoMHfeh2pXEH8PjxHhwedl7XG5D/nJZivNe/FLry3xr5eZjcOZq+o2WQ40rftGf+H3+nz3TXbP4+SfL3+LvVwiPZU+dvev9s6oRFh+z9+32Yz1ckbu7Unc1WcHrqXhCq8T1NZvSlEyKTlOWpZeTKOhFF6ULE+Xye9XmJrqrj21I/IXqRNb9t86JN0bAL5bwKsNisrPMG/c1tAHWhLtuQD5ocFjLefbalkD7qk+Oo7C/J8a78qsqBTYw1a3+7SrDYj/jvkPrX+khdOk8Z3d7CXyx2ozrnr53aGduUoacqOPO2MIMqyg4e8Yd/mA4a/5COdrsN7XYbptNpdm8WpZfSYkUTwhEKkvS5ajujQ6Lf72e7E3q9nhj2/Pw8U4wRs9kMnj9/DoeHh3Dnzh3Y29uDwWAAJycna3eQ+SDtqvTF5xNpHfXexNjRjMG0TzYBn8DgMyafnJzA2dkZfP7zn4d+v5+tHpWO9kbQe/fq7P8+A5hLONwlZUVCUzxbckxY4jRpZNrU/CQJP/wZT0VAPqgZrHe9/1SBq54QZRWvKsKmRbgtY0iygqeN8gQ1iPvihgLHHt6dalkgpr3nd+GVoQURWh4LT8a67Ha70Ol0oNvtwmQygfPz88KYtOZPje58brP0D59hv4yi4TM+WpQZK5IkKcivtP7KpMXrsSkDjI8GvoON3zd/3UGNxgCQnUyA4wcXBfjkZC5/arIfvb/ZNRdiOtYyuOLsYntinXKjPZ4YY5GfpSN2se5DHCYSbQD6WNDklRDdW4pfBmV5xRe/+EV4/PgxPH36FC4uLuC9996D5XIJ3W53bUxYaUB+Qt+Vmce5zFSnY6UMrO0qyS5Yl7PZDFarFnzrW2/D22/vF3a/Rq8drq1WlD0D2/FKnaOu4kvfLN0sSfL07aAJR2v55M8JpEcWF9/nMmD+vvhufVcsQOp8xf/0Gz6vVgn8+q/fhq997TjbObtYpP9nsyU8fz6Bv/iLF8APMNH0ExcsDlCXwVdKj44nqx1P+r1Jvo/zGtr5tPG/i3PRDXTctJcNLt5BdaRNyfhleBnG2xWUdSpa7dsAkN3fjmGt9vC6ZJImbYHXFXWMHVrn0sa2Ovw/Vexmkm3Bx2Okd1yW18pl7YMFZ+y2hA0p/12HpjRqBk/fRCEpGjz9JEkyZxINi0cXSQytiiM2JK6PgVKHQchEINFChVHqgPn000/hz//8z7Pv1CC8Wq3gS1/6Ejx8+BB++MMfZvUYqhxzcMOE1C8sxovQicNnbLLG0eBzbkhhQxikz4CMYaR+xePS9tPGQK/Xy45C7HQ6cHBwkO2YDoG1LSn9kqDo4h9SOr48q8CqyHI6+HiWxmiT88g2hK0kSQrHWtM62AQtuLuI0sOxKSVdGvfWvtQEXVofrRuW+dFn7OFzfZVx7ZI3QtKgtIXKDdQZCxC2oEVzuLh4o/RuU8o4lTk0/heaHoXWh3k782/8HX8vpeGivYo86AJvx1arBcfHx5Ak6XHiePx6GflT63NSnwqdt2gatP0xHh0DvE9rSui2da4mge2JsvbLly/h5z//eXZ0+Ww2K9SL5oB3KcP0mV/bIo0NLb6Ul0U2c9FbV3ta0/PNx5LszIHftXrw0VCmzD7+sg27QBmjCva5o6MjePToEUwmk6xfLpfL7N5XTd/UdGj6nuv5VXm0pJvQRanbAOpodDzPZjPY22vDF794C9rt1mvHar7LNY5bcOdOD3q91usjiNPjh7FcWC0Rc8ZK1RUm+1QpqYy0PaT+pz+v/6ayAaz9x52w6TPvA0n2f7VK6cAdskkSQRynjtnlMn3XbqfO2HY7gjt3evClL93KHLWpfSWBZ8/GMByGzenF+vC/o5DmWYuMQ99tQ7+kkOQSgHVaXYtxfe9C28NSH770LfP5JuCbB0PjuKDJ3WVou67gtjGOqnXmk/c0OYu/d/XvMnaQEGi0hcS1jn+LPILvqfzukjFddekK76LLQuMmsOkx26QdjcJih6GQbKNSulV13xC9xBfPFd9lU5BQtlw7tTMWsSmjWigok+GGFxoGETpYfJ0+SdZ3ENDfvk4TamCtAs6QUWBER0IdNPHy/+pXv4Kf/vSnYtz9/X34kz/5E0iSBH72s59lBiLq3NAEWg2+iXHbwnxdaHIc+oyfGqhBlDqnNAPCYDDIjvmJovSe2cvLy7Xjq5uAzwCmGcqlMKGoo+0kIZnWmXSPVR2CgoVHSIJDU0IKpof3EyMWiwUkSeLcde0TcFzg9T6fz9e+XRdeUweoQVm7YxTDVckDUWaMUeNQHe3m45V15aGVFQ2ndDWsxZjrEtZ5OC0MF6it/LYs8HQSSZ4JAS0Tpkfrg5+4YTFsWfpUHXNCnTJ6p9OBR48ewWq1gvPzcxiPx9n9llY6+G+tn+B7vqLVCip38LyxHfnY5joDOmU4b9o1facqlssljMfjbL768MMPIYoiOD8/hyRJYDQaFRxALoOz1q9R9mi1WtButzMeJKXjg9WY5+MtVmXcGrYOXQyhneKj5RkyvkLD+eL5+HhTKJsnjul79+7BZz/7WZhMJtlpVovFIrtSyCUn+uiSeE8ZaHWL9GJ5NglqT2m329DtdrNTPcbjMdy61YX/8/98DP1+/NohC2tO2fQ/gORwLeonPO/sV0nqsT34fFwmrfWdsJg+f1/cFcvDYDq48zV9zsPlY5z/xnD5//V3dLds8to5Oxi04d69fnak8WKxgvl8Bf/zfz6H4bBcv9dg5ZfUUeAD58lVaKtL93bpjvS7TwcMtbFsG03z/121M+8aPT5UbaeyY85lf5VkibLQbEp18QqeR5PxLDv5MF1LnVGdJpQeX/pXbRxcV4SMH9TFEKmc4Jcjq4zR0Hhl/XFNQ3XGWpWtUJQxsO8SJPrLKpwhsDj6XM5KzNtiBLY4dqX0pbA+mixxXPGoYrtcLjOjTKfTgel0mn2fzWbwX//rf80mjsFgAADpUc/z+TxjIr1eD9rtNkwmE6chX6ojS31K9eeqU5/CIRlL6hJsJFpcxy9ajVBSvmWcVTQOXXGOxs4kSRcv4E4MPNIK06a7pheLReEIcEvZQscC/e8SgrTxGVJHrvR94H1bo5n361DDZ9XJrowzSytDSH+T+ouUhstR4uOxVMHGd5xmLayLdikvV/hQ5d6HOtpdShMhjU1fHB6XfrfyeAm0T7jqn97h2QTqqm/adlbDsIWfatD6eCiv5TRboPUPWtbZbJbdGUtPLMETGLQ0Oc/kjiheDo0eCmsfojRgHMmYZxkL+K7dbkMcx6+Pi6xuwEfa8AQCrCde/5yfuuSPkP6mPfM8pX4lxdHaN4S2qwRaFzguut0uPHv2DC4vL+Hi4iI4rU3Cl6drbubhNH3RohuVMTJofTY0LUlW4XT7ZAr+nR6ZjH/8+hqehs+4R+ehOtBkfws5LYLKmtbwvN5DdLDVagXtdhuiKCpc6VJXvfrkIN72X/jCHjx+fADtdgsAEpjN9mF/vw37++1sZ2yrlTtg0fkaRXR80vTTMPx98XsV4BHSVdORkTpLqU7GvwMAcL1N+5+mRZ/z8bZ+jyxA8V3qfI2g1So6Y9PnBOI4gdUKst2ynU4LvvzlY3j8eA9Wq5SOxWIFw+EC3n//MjsauVgefS726Sz0tyQb+OL68rGgDj7CZV6Lnhqqh1v0E0pDKK/l/LsOG0ZdqGIjKQOprjWZ+irJhFVolXSisum67CkufUDKJ1RPtIy7EB1As8GVoVHTnVz932JHkeL75JVQW5uWT1MowxNC6KuDt7j0Bmsf1NrBdzqRK32q5/JrsTTbB6bt098l+PT1sv2mrF2TpxGaTqmdsa5CShV0lSYWK3xCDIWvI2t15Bp0Upo+ButL3xVWg6VsljyttHEsl8tCx2+1WtDpdLKdkGgMms/n8J3vfAfiOIaDgwPo9/tZGrhqudVqZff60Ls5rGWQDKKS8Opq79D6Cp3YQhR0qSzW8oTWnSuOLy8E3SWBfWI6nRaO3cJdGklS3NW0XC4hivIjf6yQjFQh8SzhAGRjS5n+wtN00WYVQLjgZjUGufplWf5qqVfKJ0Pv74qiKHP8S0Z2Cy8PGY9S+4Ya21wImculbyEKgTV8aNo0XohyxKH1ect4wPgYnv4Pzc/Svr4wdfYRC0KVlVBhu0yf4AI/pym07ih/SZJ0hzoeP0nvxy3DWzT6Q8eX9M5V1rJjjdKHJwXM53NzepwmjY/S+44pvVIcKp9IO19pu1qMHVo+PLzEdzi/53m/CcB2wAULr169gufPnwPAZg0sZeGap3mfou+1tnb1Aan/a3lL6blolNK3gsq2lG5pTLlorcJ/tHrE303PdaHpSm2HfCGUz4TApf9b4+POWJeu54ovwVp/UZT/PXkygK9+9RA6HXS85vfAAuT/0/D5b0pH/lzIRXhXDjwNt27rT89VTWkdch7Bf6PjdD2M6//6b+p8Tb+lxxWj0xXn2PTdaoXPALmzNnXGxnH67XOfOyjcLzudLuHlywl89NEwo9UiLtn7kl/+qCOfOhBqCwAoxytCZWctvk/HkuaEqrzJV0dV5KoybV3GplYlvzoQqk80Vd8Ibscqo7NzmqT0pXFTda7S4vhkNQvKhnXJQbwOXJtqLO0gjWeMxxedlR37FpsLDYs0vMlw6RHSsw8h9e/aEetrnzrHfdNw6W5V5PvGjym+Ckp3CFwKrw9ahyxrCAMIO3aK/vG06urQPmawSQwGA+h2u9kzOkul1R98J6xrVyyCl5Wu/PaFvcqQmE9TRkaXwwLvlcMjMgCKK3IAAM7Pz2E4HGa7bHD1N/YD2heQftcdLBLQMdckqhjNyyJEGGoanHfSNsRveEc0V/54HNpfMJ4PNB/sG3jENf3TBE+6C40auix5c9TJr33wOU12AZQH0Xaqg1YUKqW2o2FcxmlOB/Idukhkl5w0ZWjBeuGOs7KKJXeYuQ2dabh+vw/dbjc7Knk8HtfGm7VdTUmSHxve7/cLY5rzHR+2qVBa2kqiL0nS3Y+uuxC1dJAXDwYDePjwISyXy2xX1vn5efad9wUE39lryZP/pnybftcMjpKMEGrkxDnqTUW32812k/sMWXSeB4DC/O4yBEuQ9K46dLE6gHMXgFuPCE1T08d88krZ/DQauO5JxxF9LoNtyqkS3dPpFC4vL2E0GsFkMilt7JdkXZ9uUodsuEn5EvN55519+PrX7wJA6rh8/HgPBoN2wQkbMcdr+jPsOOL8Xah843pXn4NXQpLkeYB6XDGGoTtec/rycY/feNhimOJ73PVKjzmOXu9ozXfKpv0m3ymbys7a/bItiOMW/MEfPITlMoHlcgUffjiEDz7IrydA3hG6qM3HS0J2qG8CZWVti1xblz0O5ZZutwvtdru2U1CssMqmN9BRp0OwLt267rmbzplWxyjvx1rZNNs5/qe/fWlp2HY/LuuIrZMXaLraDeqB1L58LgmRAVFH57bMpm3iALb5zOos3TZqd8ZaDUqakW2XjJJ1oa7O4KubUGOehS7eTtpkVLXNmkgXV+Qj6ITBmc18Pi/cxViWkXC6Q8pRF6PQaEBItGiGFBqHpldWiHIZ0FzCkyTgYBty5xqldzabAUB6HDUep0jBnbGWMaEZassaXXhcnk6dfUjKx+Lo4PRqaW/aMCY5012QBEkfzZrBkhvLfPUkOe00hBiJ6+LBGixlsbZ5U30jZKy6nHyusW2tX9eYpkZpfpyt5IC0KEahsPSXsgolzrFlHFUUWHZJMdPSxRMxcMfqeDz2yjUaeLtod4yiEoILfdDB5KtjH7/ZBMoa6ni/pMbNUNpxJ1av14Plcgn9fh86nU6WltVZFDrvSP1LCgMgjxcr/5bCcTlUy8+VT9M8v07w8qBcTq+MoN/pb254xhNMaFjalvhb4qFSvZYZnyHl5e/LGud88nkILZqhhc4/VfqVFFdLTzuJho9nrZ9YeYOURp2g/W06ncJwOITxeJwt2OP90kIv5X1WPliGZtd7aY7Qvlvrt9uNoaheR3D3bg++9KVbEMfU+Rpl4fKdsNwZqztf12U7E3lryONFa2nIaa6HC0FajbSO6bei4xerPIrye2GjKH2fh8H+B+Rb6kDlzzQMpp+2edERW0wr/Y/TGn3O+Qm8zg+vZkiPMB4M9rO7ZS8v5/DJJ7l+jjT71qtadVLKB8oseNk1Q65VfnPJRmX0UMneooW1pmtFHfZhrT6qtm9IHZdtjzpQpQ7rqP+683LJMogQeT1UtqZzomS7q1pfrn5p/VaXPMdlEmu5Q3TLbem/Esro6Zq8WYdeUFVfsfKdMu2l3Udct60jRIamz1I9a7pHHQhNJ8gZywWaJpQDV2fbxQmkDMPj4ahDwbdqT+pMWseTaLTSGmrc4nlxZbkOQdbVjvT9YrGA4TA//qbf74u74SaTSSFt3g4A5ZyykiGaGorqRFkm2lT+ZcH7jaUc2Kb0eMTBYACHh4ewWCxguVzCcDjMjqrG3VI0T9exxFp7Se8l5c5i6PWBj+NNCCq8r4b0W6r8NgmsB3q8NKWBGmc5fYvFIttlgN/5rhQeBx0uUZTfc/a1r30Nbt26BQApzzk7O4PLy0t49uyZSjffzdVut4N2lVXFJpUpHx2bSpcbEC11wPsw5Ut0ruD9zKIYYt/Du61dYTcxlig0ozFXwPgch7tR8YjDMvfjcfBdQJowjQai4+NjuH//PsxmM5jP5zAajQp3ykug6VM+4lKcOB24I58u5qLhXWXkKKPASvUTKnfxepDo4flxXeDg4ABarRZcXFwE7ZIdjUbwP//n/4S9vT149OhR9m21WmX9ioYvo4xpYWjdURmxiiEDjb2aIyPkjmjOd0J2A28KPr1wtVrBbDaDdrudOd1ns1lWDukYak2upd9xvGK98IUQPD7l1VL7SmMvlBeXmVuqQJujJFrwvbawhMaRxm+ZPsfnRirrYPu5dLq6DTl1g9Y1XnXzwx/+ED788EN4/vw5TCYTiKIoO8bdmqaLBzelS0p51Z12txvD3/t7b8GdO31ot3OH695eG3q9uHAPbPpHnY/5e/ou/w1rvwEi5b2LzmJ8Pe11Otxp6VhvShoxEb4X47julF3fBZumn/fd/DkPk8dx3SkLgMcVQ3b3K90Zi//TI4zT+2STJN8pm/9vwVe+cgxPnhzAYrGC1QpgNlvCeLyA7373U5hMlqa+KdlrND54FeCyP6EeKp3Egqhif9vFeqqDP21TB960PqfRsI24ErhcVzWdsnlzOjaxq48ipF5DwmJ5cOF3SD2hnOGzY1J7gGY3oLRIuzDfZGg6tiT3NQWLvq7lz096ctkM6oLWz7T5/yr0sSBnrNXRQL+HDn4JLmNclXTLwGd09eWnGQK076E0+MK46JaMbDQMd9CEwsWoLXAp7TwfPMIWIHeAUIYjOUCs6W8bWjv42trXLzlcSkzIuJbCWurUN064IACQCh2dTgdarVZ2FzDt3/QoBUnQCK0z13i18KsmJ4mqSofLwFo3XOm76ogaWC1CgM8Ia6UTjfedTgf6/X7mjPHt9sG4Et2WcaKNQeucs6l2bFr44U4KhLVO6Hzm4wPaOPDJK668aRo+p6NUp3XIV6FtROvLRwvtby6ZQ4tv7ae8/ZIkgU6nA3EcZ+OS3ufaBEKcaxwhsqIlHO07rr7q46mWdzRd5IfcmW2Z/5bLJVxeXmYONdfOUddcS3/zPkX7SUiaTRqfQtpkF4x4Wn34HEd8vqPPIXn5+mySJKJRoUq9WXh+CC/UeKgErfwW3dsyTiif4PRaddTQuuXznGQ8qUuG2JTeRtsljmO4uLiA0WgEZ2dnMJ/PodPprOmfVki8XOtb0ndrHWjyVB1IHa0tiKIW9HoxPHy4B/fu5c5Y3A2bjg/uhA1xwK6/Xy+nm9biWJfi6t9D8nGFX6/+fAdrMU7uPE2/F2krtmO0Fi8dx3lYfE7fJZDvwl3fNZtkzlbcvZwU8sjDIN15ekgWprdapTtmDw46sLfXfu2MTWAyWcJoFMPhYQfa7ZRPLBYJzGbyQj+XnhUC1xxNw+yCXaiJ8UqBPA3zwcUzLrvTNhAyl5Zptzrk+13oL7uIsvZBGrZsH6zDdldX/+dyj6tckn7jS1eT3X15aLCMuTKynM9+ZrHz+ehqAiFl9fWvEN3Iml8VejhdND1tR6wrfhPg/dxqO0FY+1BTZVhzxnJDxi5PINukz2ckdzEQXAkuGa5Q+OFhpTzxnW8ykxi3phj6mIAvDA+rPTcFqe7pDlgpnCR8+1ZGSQzRVS90p20VQdZiiCmTrg9Wg3rVPCx04+4rgPWd5NgOaBg+ODiAxWIB5+fnmVEej5TUaKAr97WJH9/jWG3qHhrfTi0XQibXspCM200C86I7YqMogk6nY+Y5dNLmO6Ndwik9inS5XMIPfvADGAwG8Fu/9VvZ0ZroXMA0cLcE79tIPz+q1lJ+n7B6FdCkMu+afyWjs2t3DsbV6pw6F6R4kqEbj9FNktSBtb+/D9PpNDtSPYQOiSZeTh8kJ4lmPPbJG1Uckz4aAYo72JFX47enT5/C06dP4Ytf/CLcuXMHPv/5z8N4PIb33nsvu+/KtQvdqlzyuQffdTqdgvOXxnH1jzpQxSBBwWVR6b0G3MUaotTgOGi1WrBcLuHs7KxweoUWj9LEf1P6tTu5JfnPl5/0nt5Rzw0jyFf4qRk0LIbZ5BxaFlUMktgW0i5YDZrCz8NgOFyA6Zq/Jdrof4kulzGL5u8zEnEjmzQ3UB5MZUuJrlDwcnB+ptFaNp8QY2UVOZfX2zaRJElB1wAoHq1dJV2Nz/DfZUDnNFxQWJcO/1u/dQ9+8zfvQquVOl739zsQx1HhHthozQmbOz+x6vA5/w1rv2lZfCimITtZ89/60cNNdzmp2tN3EYBwh6zvd8h/zCftD/gen+l8Fr2+VzY9Uhrf4X++a7b4P71HdrWC7D7ZJEmg02nB3l4b/vAPH8NyuYLpdAXPno3hf//vkyxfra3p+AvhQxxaHpvgM6hL+vKrwjs14LzQ7/fh7t27sFgssl3+oXLrtnnytrDNcu/CXBiKKvRabV3cDl82PR9PqWt8WMca190R2pHiUv5c9+P5huh1kv3lqvXHJtF0X68bLt2HjqNN7Si/jn1pzSNRFEo3I3CE5ONTEEJpDnWq+eAy3Gj5WeNKcfg7ibHis5WZlhVa60yvLCzGFRrWZVi25OObjEPqJYSWMgYPC7R+WmceNC3Ob7Q8uCELf6PRCo187XYb+v1+Fg+dZ6vVCubzeXaPoMt4Q/NwGaVcY43SVxZaHyrbDnUb6xFNlJGHsRiH6NGvGo+0tKOPXnSgzufz7L7DXq8He3t7sFwu4f79+zAajeDy8tKZFhdkXHmWQWh7S/VjcfKE5GPhyzxcWWhzII5Lbsz0GV0kY3pV2gB0h4zFIO7jnz4HQRUHi/aubmOxRTFGwxUeVdpqtaDb7a7dMelqT41OTB+Pl8aFPLjIi9cnn0Ot7VIWknPH13e0/iUp9T4ZBsP5nGFafPqbO+ukPsTHHw0vyX+hkPoFf5bqRuoHfO4KHQe8L236+LS6YJGVKKr0IVf6vrGofQuVEXyyvDaHaPSFGMK0vH1zFu9rWngrX5fqFU+tQRkK71StAss8aUHV+HQeotdaUBpdeflk1ip8xAUXLwvRSTudFty/33+90zV3tD58OIBbt7rQakXZH36LiBMWk83HnO4M1d6vf9fKHJ7u+rOtv2jB7E3oCsgTx/tZ02/5b5pOXr9p/0odrPw5DcPvoi3OjfgNXu+MjaLUoZp/j7L8kwSg1aLO3jQOrQesq/RdC1qtBA4O2rBcJtDtrmAyWcKDB/3XztsEhsMFTKduHSpUJslp2w34dCZJ/sOw2hxjBcq7aFtZLBaZPaWK/hYyn9YBSV7UaOJhXLYOS1qa/aJJ1KlfbBI+uZqH056l9nPpNFZ9O4Qul+7lS9sVh4fz9StXv3fJ4VZbjY9eX3pl+HMZ1DUmQtOpWramx7KFV2vyqqTzuuwhWr6avOnrRxJC9LuQdOuCRkPQMcXXEVUEFR7PynzRoOK6z8w1AHiamtAlxfPBwrCvG6oOQtdxxzxtKWzdTMDXTr6+UiVPF/Osq//galE07OA9ZNPpFPb39wv3zk0mE1gul/Dw4UMYj8fw05/+FBaLBcznc3WHrM9QuE3UTZPVIB+KbfCMdrsN7XYbptNp4R5hCslZ4XLgSuh2u9DpdGA8HkMcx/DgwQM4ODiA/f19WK1W8JWvfAV+/OMfw1/91V8500HDHXUi+5yzvE2aMMppz1ZsQ/iU+q8rLSk8X0UqrTjndxNT+BR4TUGUUJZHVzE81dmXXEprSD68PJbFC9Rpivwdx42vP2h3fa5WK+j1etDtdmF/fx9arRY8f/58TYajfWa1WhWeab+kO3zr4LO0npqY313AfHFHq3R/egioUuc6wUXrY9ywUkb25W3FT8twGdc0w1CoQQ7nJqwHPBGE36N7g2bg49H4TeL1/H/I2LQaIFz9j+Yj9UdfmbQ8ywJlm7t378LBwQEcHh7CfD6HH//4x1nfpnleJV2Tzh0WGc4HlAsB8nlil3QQDUdHHfi//q+3YX+/8/r+13QnbLsdQRy3Xh9nG7H/OIbwd/qt+CzJpiA8u/q09Hs9H3teeRquvEIgd/l1h6UWB52b/FsxjPvd+v2y+XPy2vGKTljtXtn1nbG5ExaHRnqHbPR6ZyzO9fBaDk9/p/8TiGPcKbuCfn8fHj4cwHy+gvl8Cd/73il8+OGQ1UGKKIpEGd5iH9kFcCcSPSHBZfCWyho65/A6QB11OBwWFhlLpwqFzje7jKq6KH23jX51Fepeo896jVxImjwMHR8W/dDShk3Vt6brhcgbFrnPF84V3xqv7HjY9b78JoEfT0ztZ65xpenBuzLvbguNO2PrUK5C0tim55tPfJLRnJZFMyhZyqvl5TJK+dKU0uJ5uAy3roks1HClPbvyoXFCJoVQcEOr9J0a8sqUkX/T2i/EMUPrRRPSqzJEnm6ZvhxKB407Go3g+fPncHR0BIPBoHAMLW8vVNZ8fdpl1KLhJAOw1hf5ZBUKq5JlhaudfIKT9N3Kb8r2N96X6bG/ljqhcaoYHzGd1WoFnU4nM8ZhX6NHG2vxEdzQ79oFVYfzJVRRdwlQvvGrpeHiZ65wrjjSvKoZDLijr2xf8M1vUrn5nzUfq6Kp0YWGnTp4R+j8Y/2u8UxX/8D44/EYLi4uYLVawWQycbaNTymQ+g3n2dIzn1Ossqtv3IeibmOMVDbKs8oaK7BPDodDmE6nQfF9RpuyhgVedy55mCK0zjEdbUyinNLpdKDX68FkMikcdb6rqLPflVHesd7onXf43hK3KWhl0Y4lRtqt8xwNq/Un+l8auy65NGSM438sE54gcnR0BLPZLFvoIF3l4EpXK9sugPJvFy+yyDSuNvIhJC9XXWrzYRQBvPPOARwcdKDViuDwsAOHh13o92PodFpru2A1J2z02pGKSef/i3lpZZO+y9/kXbDufNJ4YfmUA8YvNlWkfNP0NPoG+V3uSE1/F3fP0vDpe/suWeleWcjuj02ysKtVsQzFHbLF30hz3jfwfbpTNtXzUif/w4cDiOMIFosVLJcJvHgxhcVi/eoiK6xyGkXTfMhnd3DZ3ULsMFKeuNh9NpsVFji78rwOKCNDWuOUsWmWxVVrjyp6sMXO6hu7delLddvlJITuTvfZBDg4D/XVXd19LYR+axqub5Y5wyVXb0sXC7HBSHEoNFmP6haSPd1Sz1IfqrPOyvSPXcLO7Yyt23hUF6x0hdCPCihXviVjgaWza3e88d8+oye96ypkdZKr7BaGUYdjxkdjWdA06b1w3HAqMRtXG1gdIlb6dpkRSf2al79sOTDeyckJnJycwLvvvguDwcAbhyosXLiRDO++/BGScUvCLvA7H4/gYXYFdAzNZrPsflYXaPkWiwXEcZw5cK1GRtpmSZLAZDLJds3hDm1EHMeZk5bfJUnLgO/iOM4ct7tY5z7UbfgIAW1DbQ71GZklZ5ivHcqWA+dX61ij/d2lDFh5aJ28p+62lBxhErCdaT2enZ3BfD4HAMiOVAvJV8pDu1cYnSicL9DvPiN6GUO7hqb6Ks9DUlx9d6370lwsFvDq1SsnrdzASNvdl6emfNPxwudCXx/kd46HOKp8xg5Ox2AwgKOjIzg7O1N5x1WFq565rCx9k4DyQBzHsFwuxTu5d2GOpcYNbGsrT+C6n8vY4Zv3rAjRT5F34sK3/f19OD4+hvv378N4PM7KjE5ZK6+mY5Maini5rO1bp/EUT2OowtO5rKL1hzqcImXlgCiK4Ld/+x587nOHr3fCAsRxC6IIXjthU8drWg6q48HaM6WZkpL+jtgz/75GGUu3SLMrD3f6chg/PTZQR6T+Dds/dXzSsPkz8suIvS+2NaaZvHag0vD8uRh2/Z38nO+MjSIo/Me/nNfld86uVvlO2dUK3yfQauGO2RW02xEsly14991D+PznD2AyWcJotID/8T9ewHy+zMpKFzhdl7mSQuPvXE6i70Pqod1uw/7+PkRRlJ18AuC/6z40nxvcgMLlMLTowdJ7vpPPkrd1QXxTNmeXzOa6T7qOBb2heg3C6ie5atgFXQGgut0mJK40h6De2e12CzRZZPdQHe5Nwk44YylDa7LDWxXb0G9aPjw/zcgbqjDS9F0DwKKIS+nTZ0qzxXDscoxKE5ev7V2DN7T+qkCb3FFBjuMY7t69m63Cn0wmcHZ2lrURX0XiEjYA3EYVLY7kKPIhxOlWJV0p/TJMWBN+tLTQIId9+bOf/SxcXl7Cs2fPCjRpjhuJfp9jILRcrsnV4lDhNFghjXeepq+efe2sGcnK0Ozrf9QpY02TxvGNHbx3mBpM+XeA9Phs3OWCxuButwur1Sq7I00ytmll1mih8Sj9Pp5bFlYFxsKzXW0o9R0Lz+TpW5wzdEcOn7fLjMlQOWG1WmUOQ43fSL9Djrfl/cLCm/E5lP+40rPCWi7ez6MoXcwwHo9hsVjA3t5e4TvuJOQyh0UubLVa2X20R0dH0Ol0svj8KGL+W6OZPpdVfF1pa3KohR4AeXFfHYqTxguiKMruJ0uNr+tHDPrS5ceIcxnRKhPT8NIiLYku3p+k45qpjCb18263C61WCyaTCRwdHcE3v/lNSJIERqMRfPrpp+LR2FcZWhuE8g6JN3Y6HWi329DtdmE2m2WLpFx8XTIwa2OaO+Kr6I9c/rCA9kOf3kX7HO2XfO7D9xovkerHVU4uX+GpBe12G2azWfaN82aap5Y2D1NFxtH4TRlZ3hXXkp5LfvPVv5ROFX2N46239uCznz2AOG5BHEfw4MEAer0Y2u30GGLNCZv+BwC2AzYvF5D/1Ryj9B1PX8uDp+3OW06rKvI88mOC+Td0kBbfpXF4mChCh2eShUv7D5DvfDdsGn79mYbJ4+R5R6/DJABs52xCnK/8D/PKjyumZcI+gmVKHbLLZQuiKCG7ZNPArVYEX/7yEZydzeDnPz/PjkR28UZ3ezRvV6Kw6EQUmq4nfZPSd8mnrVYrM7afnp7CZDKB8Xi8dvWGBKueqMFq29kEytKy6b6zy/DNVy4ZMKTtpbkS33O9z0ovpcOiz7nSkN5b6NHg4mm+fDTdMDQd6Zm+8+mgmxzbVposaVhQtWw+3SgkfZcejHDND1zv4eEk3SjEjlOnXWFX4aOvsjPWqkBaOpCPWdbVaL78tbybSJ8ahK1AWvhKYmk1jNQ+lglEMhZZjHO0ja6qQCIJ75rRlJb17t270Ov1ACDdoYnOWGoIoWm68re8K1O/vraRvlcZcy7+IBnT6e86jAhxHEO73YYoiqDX68H+/j68evUKnj9/LqYhtVEdhnIr39MEpjrqwzK5l1E+NKM0T7ssfHFpPUl3+7nSoHEAcgOrtot1sVioR4+jQY86Y5G/d7tdSJIEptPp2jHGPA1KP/72KdKasZR+t/KLkLBS3KphaP6+/qPxM+t4cSkomvHXYiT25YVpaTuatPjUmG7JIwRSuX3G3jLGapehH//7lFceB8ceQHpPOO5Wx3HIxzSlRVO2eT3giRg4niWFw8dvrEaykHaswmOtcoXvXV158yNly0KSZaV5Xhvj9HfIHbi0LdDRFMdxwYCp5R1FUcEZe3BwAL//+78Py+USXr58Cd/5znfghz/8IfR6Peh0OqZ6kFCFvzeZhyaHueYzV/t1Oh3odDqFRRn0u5aHZrThfNdiYLLwO0t/t8iAVNeQyhEi22l1Y5kjJZowPO6GBQCYz+cFZ6yVtpAwIeHqQlP5aXxDmz9cc5AEC91vvbUHv/d7D6HTaUG7nTpkowhe/8c/dKIVf6d5F3/n/6V3GcWgdTPpvZY+DZt/4zxYSt8dxgKMY+sakRo+fV901kYRAHfSpt/5McPp+/y4YZSl8JvumM3j545Xnn46zss6ZfEeWUpnRGjKw6T/UwduFKX3yrZaAJ1OC9599whOTqbwwQeXMJvl/No3FjbNI+qAdiqbT6bh7/N6Lsq76Iw9OTmBxWKRLZ7x2Su1+dqFJvXNbUGaf3eZ3roROv/Qb9q4tOpKXGbxpeODT0bbFB+R7EMaLTy8pQ4s49TSfjQtSX705SHBx3OsdgsfQvXxptpcs0eUzU+rH1fflspO74YF0DfpWccwp+dNRWVn7E0l2mFR3qtA2i2DzgBXftTgROMhqDFJgtU4XdV4tyloRnOs23a7nR0nqhnGe70e3LlzJ3vmd6G5jj2k/13ghkKfEXsbaJI/LBYLiKIo65+9Xg8WiwUsl0sYj8dwdnaW0XB4eJgdH4bA1fi+421dgoyrr9O2rOII2xaPtY5rF+hR7E30STz+Do+5A5AXjWjQ2tFa1tlsBt/+9rfh8PAQnjx5kvGGy8tLAEgdQ4vFAgaDQWG3LC7aAEh3b7fbbRiNRjAajbJxjOXQjoSyCNOSU0IK50rnKs7xFuMLV/zKlFNy9Fjj+eDjMT7ji8XIr6W76Ta3KNQA+vFL0px9eXkJrVZLHD88Xdc32j+SJIHz83MYj8dweHgIy+UShsOhiedoiixPvyk0LRvUdTSWTwYKmZdCDQFaet1uN9tpGUVRdhcxyoUuBdPV5kgXlS2xv8ZxDMfHxzCdTuHly5cm2suWr27UmYd1fGAdomMPF6tS2SPEiUj/0zj8G+WXEu90GQzpe+RV+A13QNc1Xl18nRrkJVkoRC+xhsP+zvMIQZ28bNd0JwpNL7DwN4tR3KerPH68B7/zO/fh/v0B9Pu4Eza/E5beDRt5HLCR0/kasis1EsPIzlhX+JweKb/iczXHrCU8ba7otfO1+G3dWZs/J+S5eJRx7tgsxkfnrM8xC46jiykd0jfLH+a3WiWvy0jpSrJ+tlolr//Su2fTo4wB4jj9fnzcg29+80F2j+zz52P48MNRkM5jRcg8F8rjuE0H5wa+cAVAntdC85KwXC5hMpmsbfx40xyLIbiK+vJVgmuerqvu+UJbSZe25lXWrqCBj/UyTuIQXaiKXLYpHlFXu19HR2Hd9POj/31Ocun/Ddax8WOKd1nhqRuaga/s4LAYEiSF2mWsRbiMidrRb5SmUEgGC0u4bYHWPe7e4LvnKK3tdhsODg6yuNPpNDPgAeROKslgF1KnmsGYvtu2kUOCpV2tBjMMS9tlNpvBcDjM3h8cHIgTA+500pzjmsGCvnM58rixlre3VcCzOJd8KNMfrMZLX77cWFnGGWhxpqBzVqIhlGYLlsslfPjhh7C/vw8HBweZwX4ymWR3pS0Wi8IuPQAo9LlOpwO9Xi87wjiERknI3tQca3UW0jBN0VTF0RLyzTdeXcbuMqB1bHEYhtavy1FgeedL24c65net7LPZrGDQCjVW02+Ub4/HY5jP53B8fAxJksBwOBT7ukRjKN/3lXFXUKcybJkbJPnWZXjQ2sfXbhim0+lAv9/P7geezWbZXOPiub52o7RFUVRwyLVarezee8lJdh2h8SOffMxlaWyXJCleXcDrW0pHA29rGp7zaWtbSf2FLiyz8CYePyQ/jEP1Rq3+XXUXUl7qJMfy0jys6WkGnrLjhOZfRU+vCxbeRNuFh4+iaM2BIsXn+WHZ2210xEVw924P/o//4w7EcbR2L2z+O3cC4u98TBa/5fmvv8vfy3RK4Xg6eXDpXTG9dXrSeBotvvf1Q8pI4wuRI4z2TXofOcKth8F+IsurURZmtUInq7ZTNnXEJkkCrVa+A7d4ny1keaTpJgDQyv7v7UXw2c8ewHKZwHy+hMUigadPx1ncdAduvcZhKw8KdW5I/NhqOwuBRu9qtSrcte6zK1rpkuxe24bPrmpNw6dHWvj6mwrf/M/tSNI3LU1LvjStEJmzzBi00B8qj7pQl63eEp7LIxZe4Pu+KfqrYps2Lsu4kdKz1hGGs254sejzbwp8dbwTd8ZeZ1RhQlI6NI5rxwcFOpyoso1HRFmAuw0xHzRIAawflcxpdk2u10kQiaII9vf3sx1vSZLeLUedsIPBAE5PTwuGPNxpgXEwLfrMf9M8rbRdV4aYJEWneJKkxwKenZ3B5eUl3Lt3D/b29gpOOu24OW7ACBFypDrWDDv8t4VHhCpwnIY6II1lbvhE0B2rlnSr8gI8PqlsOrStfMZa/I6Gc3S6/uhHP8riHR8fw2/+5m/CBx98AC9fvszqA/nlcDiETqcDh4eHa8d+YB7IZy0COX3eJjR6Q4ysrrRpWiGQ8vf1O23ORYOyRJ+VRvy+XC4r7xovU7dSO4XOEyEGijLw8VQtbWrcrzoepLGFJ2JoBj2NT/KwUtr0/3Wdsy3QnGscfI4JORWB5oVpad8GgwEcHx9Dv9+HKIoyuc5ydQeXD/CZ372mKa7dbhfu3LmTHber8Z+rDmk8cZ1DGjO8/vD9ZDLJ7orlJ2houpPVGMjppO9ov8U29jlXpXR5+fiKdB5f6lOh0OaE0P7G6zaO40wuPz8/hxcvXgAAZLt/ty23AKz3v23y3xADMr/3ni4+CDHY0jYbDGL4+3//bTg66kKrFcH+fhu63fj1bljsb6kTNqWBOl2pAzR/n/+Xjwym4aVvnFZXuvz9ej7Zr7U81vPUj0n20WkBb2oprZwn5DQlSTFs/oz9l++ApWmt75rFOJSm9f953GTNUbp+7yv9Q/r5e/6HYZDl0D6c/s93yEYRlhOvicFji9PvcRzB5z53AHfudGE+X8F8voKf/OQcXr3KTykLHedleJU2b2h2AYv+oM1Vdej/y+USLi4udkq3vMH1QxW7pjUO161ceSO0Rf1VaLKOxap1Urf9700f9296+TW4dJkbJ2w4dtIZa3HgbbqhyxhzXWnVMcAlR4hWL6mg2irE5eGjKFpzDEg0UyNnqMOKvwtJY1fAnXfUsJ4k6S4KWr/z+Rx6vR5EUQTn5+draVSBS8jfZUHaNQZCxzg1UiHm83n2h/edUIcYHmmMxqCqsChPljYo63TVftM0q/Q5GpfzAk53WWeZRpdVOOVGb41+V/pl6h8NvDi2AQD29/eh3+9niy1Wq1Vh0cByucwMzJIR1aK8a/TwZ4sBmCK0LXh4lyOJh90Ej7K2vfSNlkUroyanuPohf1dHPfh4jGRo96HONvH1C5pfyDjUyuVzcFh4thQXHRM4njXnm4VWKU7TDtmr5ugNNUS4FlPR9ufymwtxHEO324V+vw8A+jUTCM3JZ+WLnOZut1u4/5g7Hq9Se1pRdt7C77jQhfYHrZ7rGBNS2r65jssempGdt7eUXkh/tpbFKhv55h78RhcBo77ZpG4SWheW8E3rrbzOXXKLNlfQvuDigzRMevxwBAAJHBx04O239+H4uAdxHAlHEq8fSwyFHbH5c5pP8Tl/p/Hp9XrJ3+np8jTX81t/v/4sO1+tzW0JR7uZKzyGk3Q6+ipJivWTPyeFb+lvDEj7uvQO37vCRez3+rcoSh2mef8sphlF6Y7Z9F3+v9VKnbvpjtf8GcuP+aW8EfOAQl9IknQhQa8Xw2y2hNlsCQcHHRiPlzCZLF4fiRwOy7xr0Y1DdCcNkuzIaZDi+NLkJ7/x9JqWOepM31ovZWioM63rBK2vWGxhrvAhczSnIwR1tJeLT5TReX16q9XGEVL/PLzEz3zto/E1l92yDEJsGpqOVjbdbSLEll5WJg6Zm940XlcH1pyxVqfBDWzG7BCjrCtNF9AwRHevSunStDudDnQ6HZhOp5ly3Ov14O23317bzTadTjOn1Wq1grOzs0xQo44EqRy+QXmV+hoKqPR40Xa7DdPpFGazGXz/+9+Ho6Mj+LVf+7U1p3av14NOpyPugrNOINiXLA4RS5g6sIv8gvbJk5MTODs7g08++QRarRa0223Y29uDX//1X4eXL1/C+++/X4hL67fMJGSBz9hWJT86zkOcBFIaUl+jRjqLQCcJdU1N1Dg+6zJKWutL4rvPnj2DV69evTZy9WGxWMB8PgeA9TrAPkmduZLx3lLfPgeBr17K1plkVNDS2xbPKJOvZnjFPqbdx25Viiz0lakrl6IhG/Z0eWXTBhjOe6T8tf7F5RwpjPYtZHytVis4PT3N3mv1zfmxz5C/ybquiqbkCQ5psZWFDit9uLPMAjruq5Qd+4Kvb+KcQfPjC3tCFORdk9V2BbtgWMYd99jH2u02rFarwoJBTc7X+J70niPEGOiCZe7DHbLa97phlQN32aDOZW5psQnVHzqdjjM9OtfTtv+933sAX/7yLWi3WxDHEdy61YV2O8qcr/Qo4twJmzs3+TOn0fVu/ZnLW/Q/dQC7HL3ld+BqdEhhy8CXBnazYriEvI8KYWm4tB9kOWVx8nD5Xaz0PeUteZxkLY28PwILm//RZ+kb7nrFd2n+xb98Z2zxffTayRpFCdkhm5YJd8bmO2XT7+12Au12BN1uDH/zb96G0WgB3/72Szg/z4/gdbdHzr/oGHRBkvt8YfE3hqenOPATdHyypC8/LT6+r3rKgivvbWKb+e+6TN8UuD0j1GZssW1ocV3hJZTtH5KNwEpLSJ4+pyYNFyr7WHmKy0bAv9clX2p0VGmvMjRtQo9qKo8q9rgmedeNbio4YzdVIaED9Co1FlV2EFXKKRm5tXypAKU5UjRnRZIk2f2Gg8EgM/pQY9hqtcp2fuJdEhalv2zb+SaebfQLyfGwWq1gOp1m9z7yyYj+8bQwDIf2TqvvEIG/Lmx7TPqM2th/EehAp0d5AejOC8054eqXrrhlYBW+QmHhwVrZywhYIf21qgCnxQt1lmlORikOv1t7NptBu93O7jLWHA34rd1uQ6/Xg/l87qW/LK6SMqi1hyt8iBIR0sdo2taxFzL+6wpn4VVVBHIa32rk99Gjpa89VwFv/ypz5mKxUPmIpW4kOYHSWFYx9qGu+mxKnvDVn7VuaVhLO7vqe7lcZnwZT0LQjirX3kn9muaJNGC6eJx9FOX3FGNYeq9o3Q6nTUAah9JcS+vIMi4k2cvizOcIqdMqc7Wmo9H0XfKmZJzX0vfJpZJso8m/VftS0wa6UDrqSFfi5WUg6RFWGmgafLzwtAaDNty+3QN4vUvx/v0B3LnTg3a7Ba1WBHHs3gmLvzHd/DcAOHar0nfF334nbS6DlXHC6s5ZGlfKV0Ld7JQ2rZQ2PXYYsqOIuSO26GQFSNbSytNJWPy830VRnlb6n4aNsvwxHsbJ00nU3zmNCUBhN23xXauVly+Kis5ZgNzRikdkp3MFZHfN4h/WRRyn4fb22hDHEdy+nS42WC4TWCxWMBrlC5w0UB2gSV2K8oBQfVvjPRIf98Wn+ddZ5m3KIi4+rc15VfO5QQ5ev7Rvh8ppId+tYUIg2XnrSMf1XYvvkxMstFn4TNO8j8NiJwz9VgVa+eusk7r6kU8/qJJXndgFGpqGr4yljykOMd7VgaveWGXpp3eKIqjBhoKvNqJhkiR1tGorkwHSHbAffPAB3L9/Hx49egTj8RiGw+FaOW7dugWz2QyeP3+e5RHHccHJRcM3iSbT1/o4rX/JyYKrGRHaCsMkSXc+4I5NANtEZ7mH802GT7gbjUbw3nvvZUfFAkBhp0lIHpiPFoY64nzKT12TucsQ7XMo+Qx2rnxccNGyC0pLVRparRZ0u93CuyiKsqOwNSfZbDbLxv/+/j4MBgP49NNPYTQaqTRaFBZX+zbFMzctE2g0IKxOrDLzVaiTp0r/qtNAQLHttioDTrPUDppTB79R41KZeqVtalFa0YFHHY3oTKPxfU4jCSFjbhf47KYgzXPW8kt1OhqNIEkS2Nvbg1arlS28045ZxTaWxr+Ljvl8nsWZTCbwi1/8AqIogtFoBGdnZwCQLijr9XrqnbXbRJk5gDtcJWMwvcOLhtVkYS6j4xwbIqv4eAMdz1L+PlmrCjBvvgBMOlqSfsf3VvmuLmOjlPauoerd7XUDeQinieqTdPxL7UPbkH975519+Lt/9y3odFrQbreg12tljtg0byj8j4wOWPo9pw3Yb93xSt9R56v8nOdbTEt65897nZZIee+GFt7VvXicYlh+LHGRLgwbrTlZo8K35LVjFdOg8XOHa37cb+6YjQrxpWcaB/NN+18eNv+jYdx/GJfWEx57nNOANCaM5vwu2ThOHa9xHEGn04Lf+Z37MJ+vYDxewKefTuG73/0UrNNpqHM0RPaQ3vE5Uvrusgu5bBRl5muehgVV5o52u702v1WFJrfdoDlgO2py6y7Nv1Xhui5FC+saw1ZZX9JLpRMZ64RLTtw1HcUKS7vtcn/dZdpusI7SzthNTFouw/K2DPkhDghEldU+9JiS0DJzo4ZEBxqTUMjBFf9RFGU7teI4zo7Qwry1SUMzAGgo036bWuHhazf8jgrzarWC+XwOL1++LNwv1ul0YLlcwnK5hIODA2i1WnB5ebmmSNO0rXT5BOPQ77uEMu1JFR8pPvZtPFrW4rDxOQC0764wUlpWY5mLF0j5uejDZ03B0yA5bX2OEckwVIdztkw/KcPHrWnydLUxmCT5MZQAAN1ut7Aow5VPlfmHt0lIHlq9acZn7gjTHBRaPlVQZW7xjRlf+lL98fHN31WpjzK8oirKODJ5fIB66AqRsSxt60tL469VjG74nstkZdO6qijrXA7hmWUMkKvVCiaTSSbLWeUHjUap3XC3davVgtlsBj/96U8hiiKYTqdwenoK7XZ7pxfjVZlPfcZRrG8+p1jkZU3GkX7XDescy43tvnnSJW+V4WlXCU3TrMlyTecl5cf7LrYxyor0yiCLo30wiOGddw7gyZN92N9vZ8cSpzth+d2w647YaM3xWv3O1uI7f3qyU5bPy+v5yjTl+Wq0ud6HwJIGNp1EW/4tKYSnYaUdtMVwUeGb9p7m63pHd9rid3SIrsePXv92ved9N32X7nhFnpmGbbXSHbPpLlo8xjkqPKOjmNYPQATdbvL6jmSAo6MVPH68B5eXCzg7Kx5d7NLf+Ls6QdO3ODSq6s2aI3gbsM7z27IDS7D0gTfV2etqp7rG0bbiInxynsXuUZUu1xjx2QyroKpNqm7QMtfhg9gl3ihhm3z6Bjmq1EfBGRs6oG6wjqp1yOOic5Su6pYULt+EhnG4IafT6UCSpKvw+YDudruF3V6z2SxT/EajkTMfBL3vyEWf6/suMT0EpRFXHc1mMxiPx/CjH/0IDg4O4OHDh3B4eAi3bt2C8XgMq9UKHj16BPP5HN577z2YTqdq2nWVuc60rgPosbCIEINqXfxxE33c5fi0xLOGtTpCfEZF/nzV+i11nrRarbXViBJvn0wmMJlM4OLiAm7fvg1HR0e10EIVAMkZXlUgdzkRJacsOjPa7TZ0Oh2YzWbi3KDRVNe4k/prmfHhog2/W4ziVRyD20RVOsvGD5WvsN9hnmUcpy4nkY+Pa+3LjWs0XrfbhVarBePx+MrxwE2DjjUu27p4k8YHfTg/P4coijLe5boWhK+MR3o0mSxJkkwm7Ha7MBqN4D/+x/+Yfe90OtDv9wvzynXR2bQycF3HNX75N2wb3uZWA43LeW91AGtHSdPfSCfqerggg7ezFdKcrxnpXPVq7Vd8d24ovdvCVR83vV4Poii9LojLHNox6gAAd+/24R/8g3eg349f74SFtd2w+Btg3RlL36W/83fFZwDNwelygup52I4+1vLNf1toou9t/cTanXxDA9ORxyx+K5Y7D5qwZ14/ydq3NHzR2Ypp2HbL5k7Z9GeyFp6GpeFWq/X363/4rbjLdrXKHcn4mzqCcWdseodsGgbzW60wXkprrxfD0VEX3n//Ev7qr17VooO69F2f/mXRm7U8yy7IqdNJo8k3Lpq4fZPGq7Ij9qrz+V2HRQYtewLdpiH1vdDxZ/1ukSFDUNZ+4XLg8t911kVT0HRzREg93fCO6ripRxsKzthtVNh1cxaF1qElPBqaaFjcjWldseEyKErhqSJHlW0Xg8NjkLVjeTX6QmnbBUh04ZG09Hi02WwGFxcXMJ1OMwMe/ud932Wko+ATpc/gaBGAtTI1iVCjT5158ndljW2WOrM6K/GZH1fpa78Q53KZcD64JltLPYb2uzr7i4t2K2/W0g05pgYgPSb+8vJSPWrQZ3y2QKtviReFxNfosrznqNIfLHFpm/vmGQvNdPyFlrFsX66Db1OHxSZ5v8VB7ZMduMOBf5PysfZLXzqu9DSDkhZf4t8Iei9oFbj62q7LWSGQ5lJ+zKd1vsG4GH+5XGYyXBzH8M4778BqtYKnT58W5mvJ4WflLVY5BI+9v0qOLyu0/qjVSxm+HerYrAu+vkfzWq1W2WlE9Js2hsvOJ77wUjv4ZBLsx1elX/I5pSzdVfuKZb7Ccd9qteDo6Aja7TaMRiOYzWZweXkJrVYLOp1OYd6I4wi+9rXbcHjYgThuwe3bPej14mxHbBQV74WVdsSm9MDaM6UzfwZwO0PT77x4ND2eVvF5PU/9nT8/Hp+jbvXUnp6uW+hp0A+SrivvmoVKu2WLz+iIBfXeWPwWQauV72alYVInKnecwlrc1SqNl+6ETX+jo1ofxujQxR20rdd0JHD3bg++/OUj+PTTGbx6Nc3Kk9dfeWeEJLuGAOPhqRyUNo1XlIE0l1jtEWXB66TsPOay72zLIXBV5sGyuC6OFq2PuGRy2u+q2BAwb8s4ttj3pbFk9RVoCLEdhozlsrZDVxpN67XXfUxT1C3X3kBG6WOKNwmLwXwbjqRQRZYi1NBPGTUaUPG4NEu+IQIlGp9ovr5JClfi0mMoQ8DjuBi/y1C7zYFPnbFII+58WywWhaOgAdJ2pO1nrTM0/nFBfNMTEkddBrCmUZWWEMNXGSXJlz41yJSBNE40Z5xLoZTeW/ILFdaahs/wWzZNykOl/LgzZjKZwGw2M90dyevPxy+580oKb+0DFv7OeZLmVKqjrn1zm/TdMleEzO+83nyLpaTxV5Zf+8pfF7/j+bmUT1c/8hnzfXloxn7JQBVFUaHvufo0T9s1xlzp8R15Wtl96bp2XbrKwvOkz9vmtRZYjZa+vqLxNWve+D+OY1gul5lzrNPpwDe+8Q1otVrw9OlTWCwW0Ol0CvlSmqSd2bxvWA0dAJDJklehLTVYeaurD2hzmC9syDeefhVHnSQH4X+qZ6GxnV5hQGUFia4QaLxN4lHSvE1p0ep9G/o4pa8KQtu6jr5B/0tp0rpG+fD4+Bh6vR60220YDofZMea8r8RxBL/92/fgrbf2odtN74SN4xZEEWRHE0dR6oQFoP8B0HGV/iGdOY20qtPf0rvsCXjTFMMWd97id+kdzW89HVteHFq34WWwIopcDkEJ1sASDVJcHo6HiYT3eX0W+7MeVnPKguqEpd/y92mfxfkwd9Ti8cT4n+6SRedrGu91Stn/PP28DwMAtCA95rkFq1UCSbJ67dQFuH+/D8fHXfjBD87g5GSmztOhPEbSFXw8U+LTcr916w4+JxGnh9qnKB+xzj1VeL8mI4XE12go23aW/KR8bmDXa7V+jmOuDt3Fl79Pj+a0anqZJW/+zDdb8bAWmyHXe8vIZFp/rsLvdgUhNF1l3eoGm0fIWNP61pVwxgJcHUNSXeDMdLlcZiv1tTAIi+HBxeAvLy/hvffegzt37sDdu3cLxiTMfzAYwOc//3m4vLyEjz/+WKVfurDdJxxq4Sztv+1+gmVeLBbw4sWLAl1JksDBwQG02204ODiAxWIBl5eXACAfW2gxTNdN+y5OonWjbP+QjCV1to2k+LjGhORk07Dtdi3rGNAUxqbo2ibvSJLEZGTXFHKf40rrs/yb5mS0Ao2FfK7CXRu9Xi9zblQFrwsfD3N9L2vY5nH5mNwU/+aog89ViVt2Lg6Zh3zGJ94P6XvqhNDodDnVLbRYgDQiHYPBILsbdD6fw3g8zsJhPm+CLOxyQAMUjYZNj61OpwOtVgsmkwm0Wi149OgR9Pt9+OpXvwpnZ2fw/PlzWK1Wa4vryvSbJEmyu2P7/X4hndksvc/uTWj/6wRpruXf+fyMsgDnQa75WTMoav2lDgMn5rOtu4yrOkG3LRsDuNuB1q/vtATUtbE/ff3rd+Ezn9mDe/cG0O22Xh9LzO+F5c5YAM0JqzlEi45Od7m0uPQ5f+d3wOZZ2OngZNE0XOFckMLSd/5u5svMlYDP8ZqGidacrMW4eCzxeprcqSq9y48HztOixxAXaeDh0u9JFoYfJZz/zylId7bmYQHWnbKYFv7m39ApC9DK+v7bb+/B/n4b5vMVzOcr+OCDIYxGi9dxE/G/j5fyRfQSJFkAHTU8Ht4ZLfFfn+wh2TJ4nG3b0W5wAwC/7BSShut73bYgyalsoaUsKK+4wQ1ucLWwE87YXVKKNgVfWbmwhEqWdOyaJT1JyNMcpLibs9PpwJ07d8T8ut0uPHz4ENrtNnzyySdq/toxiDxNyekUshpol4A7Xi8uLta+oaG13+/DfD6H0WhkNtpRuBQA66ou6b0vvivNqmHqQpUVYWVWT7kcAj5nqi8tXx/X2i2kHLvKd7c1tut01rnS0XiixXDqc1SEpMu/VykndW7xuQrv0UVHU5lxaoHFmS3xP58hosyc7YM0vqvIQy4afOnSflDnwpyy5QjlxS4ZBPulr4yuOpPGkYvGEPkE6YmiCDqdDnQ6nWyHnMWI96ZBcqLXUT9S20ZRlPGt6XQKrVYLbt26BYeHh/DWW29Bp9OBTz/9tEAP729Smq6yoazf6/UAADJH72KxKPDOXZ27rQhxVocuvNkVSMZ7aTEILScd93SHEi+v7z7sUDnQMoZcNFw1hNC87fLRvqB9T8OkzqYoiuCLXzyCr3zlGNrt6PWOWAyjHUlclAPxHT6n/4H8lx2gNucnTb/olLXnZXPAyvmn8bUwvvdCSPfXyLrATMs3v4f1dUgxLLY9eWP6ln7PnaXF/KO1sOvv5OdinokQVv5d7Eu4MxZe75iNXjtQ6bdkrb/gUcbop1ytcgcurcu0zABR1AKAFdy7l+6QHY8XMJks4dmzCUwmy9d5+u1TLhk7RLegciGfI+gJefTZx/tdeqDFbtGE7GF1aFtRJp0ytp+yeb1JKOPgD7FnW+2PIeMVw1dpW+6MDUXVMRdi9yz7vSrK2Fl88UPTCMEm9A3rooAqNvsb1Is6xqXXGburDq/rDhSMfKvdQoy+FHT1LABkO1jpyv7RaFTY3QmQGjaPj4+zVXkc8/kclstlZnCX8i5rWJSEzG30TcuYQKPqcrksOL0vLi5gOBxmz7QepZ0VN0LfboK2jWQIq1tRoXndTLzbQVNzodZ/+HGnfEdEiGJDDcA8vuTYoGGs/Y0r1C4FezAYQLfbheFwWOqodg2bdFLQcklzHVfGtLrcNRmrTmesNIdxhygPF6JE0nh8ZxavU5fTwCejaO3GaeCQjkjmcfm3k5MTaLVaMBgMnPn7cB2VtdByYP3T+i7jWOc0XF5emseI1q8tSvZ0OjXFuQ5wtQl1UMZxDHt7e9kYnc/nhSN9tXSvGixO906nA1EUZXMo7dv4X9qxynkWXQAAUHTIufKn8fGkiziObQW8QQbNIc/bkF+Bs1wus+stkiSBz352H373dx9CHKcO10ePBtnRxOs7YqnD1XYcMXeAUqcmZU0Sm7Kmq+Xlzl93vmIcN21rb9Sw5WFLLIpyh+y6A7SYXjGsFr5Y9uJYLjpmMb20/xXTxHfFNLhjN6+3NEzRuUt3wGJ6PByGQadpq5V+T3e/0ntipWOLgdC4ft8s9i8kP42D18Ikr3ffpvngTvFOpwW/9mu34PJyDj/84RnMZm49TLK1SbqXy2bA+S8AZDa1breb2eoof8AT2SiP4HA5L6jTtyws84QrfaqLclntBlcPoW3XpB1tk7YzVx+XxraGUHpDdQz63XXiBs+jDI+4zrpLHQjtn8jz6zpl7gbbhdcZqxmuds0JtkkjLKWH5u2iyUeXT6CT3vuMpiHvpTLgzs0oylfd4a5PGh7v1MJyLpfLxhTyTQtmUp+zrsQCgLVjpebzeeFbt9st1XfLrgiyTNZXxWlghaV+qTKiOQY0ZxO+s+SjhQmpW9o+VdrJpbBtWwGyGMnLjgGehjV803OhREtT483XvzUHWghdWt9MkgTiOIY4jmE8HjsFybrLX6VvW+dN/k0yspTJty4FKMSJpJXZIs9wZ6UrnnVO1fqh5tR19XMKGtZVZ5ITg5fRlQ/PUyrDbJbeVTYYDLyGKytCxqtWhrrHYhXeKdW3Fo7+pn98rtcMQZJsvlgsYD6fF9q/DF+R6oC+4wsIdln+qtqePB1tnAKkJ/Pgsa2oc0jGJNe4t/Sdpg3TLnDeJYXHxZzUAO+Tc2na/LfEk1zzD12EhEdr7wqsOjXHtmRfX1/l7TCbzWC1Wr3mQyvY3+/A3bs9+Oxn96HdTo8kRqds+sePIF53xFJnWt4HoJBv/pxRJ7yjz0VnIL6jcWx5uZ2wrrzlb4U3yntXnHpBm5zWLf3Gd9cmSZEu3NmKDlT6PU+juIs0D5O/x3dJgvWbiO9cZSmGoU7YPC+kBwo7aKmjN8ni5sceJ4Vv3AlM32N6UZQuQEBnK0A6Hlar3Omaj7m8QtvtdEHM7dtd6PVaMBjEkCQJzOfyvO2yuUl6Ff3Owb+j8T1J0oUz3OnKZQYtLYk2GqYs3wyBliefw8o4iLR5uy57gYuGqwxLHTYJX7/ztZ9LRnbZ7lw6nBbHlY/VduUrr09nrQpus3Tp53xcbqN/WOBq+5A4PljtJ644ZeuP9nP0L2h99CpjV/uYC1XsW6WOKb5qFXSVwTskP5JEgkUoQ0bMw3Q6nez3eDyG8XgMx8fHsL+/n6V5enpayOPx48dweXkJJycnKk0hd68hfXEcr8Vxla0JWPu6xjjoUXO+tOM4zpirtKNGK3MT49Fl+G4Ku8J8NQOUZNjF73TxAY+jKQbUwE/DWo14GJfvZrgq0ATkq1SOpsaexkt4fylDi09wk/gt5cVWHs55WBRF2S4O3BlbF7S6cDmTLI4mPtYsDiAE8nPqtHHRX0aRKAvJ2UP5EdKj3VFn4VM8Lw4fv+eGq9yImMs/2m5xdNAArO9apuXk5fYZW3g4SpOlDqyOkjiOs7uVtbCWfhjSTptGHbyT8iPf+NHm2ypAZwje6yvtzsYdjtiWLt6kGWWvwpxYhkY+jrT06NzX6XTgwYMH0G63IY5jePHiBUyn00b6etk+IsWj/a+Oo8ejKIKDgwMAAHj58uVanjxtyuulsJKsS2VLpBvL0Ov1siPVkySBk5OTjellVcENkfgOsWs8kzonkJ88e/YMAFKbwGc+swf/z//zJTg87EC/3xacsBGgAyotO/2f/wYoOjej146r/BtSpB1PnH6joOnScDQfOV8uA+jveHwpf4luOYz0rXn+W7yvdZ2G9JtGR+INR9NPxzNNu+iIxHTyODQ97rRcd2Kux3PvjKW/sY3wjtiU32DcfOfs+l/ulF2tcudz8tqJjLtk87Jhn1y9Hhfp3bHLZQLpztiU5vS5DZ1OC37rt+7C6ekM/vf/PoHl0m94dc0f2lyvhev1etDr9WA0GgEAFE5eqzqPoB2B2wQ1+1tTKMt3XfPeDWzYlTqUbAyWudk11lxj0NXneJ2EOPZC7TQhfV+SHaV8qHzty8tqdwxFGbtLk9glOzOFJDvjglNEu91W2/S64TqVz9LnduLOWITFYHUV4TMYYRiEtRPyerLUW0jdovK9WCxgNptltC2Xy2yVXhRF0O12IY7jwmRFDQ4I12TmclppYXfJeeMSAnz0UaOw1bhrQRlnDR2D1vFYF72WNMrkxZ0oljHA28HXb7mBXkrPRZ+UpyWuFi5EsHKlvy0DFa2T0HmB8xmprFI/d6W3CXBaLA6iKulbv3ODuWTEtM5F1BgtHafVRF+3QKK/rBEe/4f24TLlKMPbXAgpM3Uu0LjcoMNp1cYmTUNKU2pzrR9wWUT7JoH38VAFnI8XazyJDjSSaYq9L10LHVdd3pbmYA5tfFjGOeVZ+IwnxkwmE5jP56JBtO5FUlYjbxVsylChzclWuQVPVqDXoUiQxnEVvq6l70Io76d6gJY/thMukqLvJKNqKN+wjCm8Ax6dsbugh1lg0cl2GUj7fD6HOI7g9u0O3LvXgwcP+tDp8N2w2i7Y9WOJMW18jF47IYvPLmdoGp5/k9Lx52PfBcvDr9Nmd8DyMkioq+usD0lOZ9E5S7/he3yHR/RCtvM0dzjm4dNdpTRu/lveLYs7UWlaIN4Nm9NL49EwOf9dL3eed0TSwd90ty/Pm9KDtOa7Z/NdtEDe5eXH+2fxO95Li8Bds+nRyC04POzAapXA0VEHJpMljMfyYrm0DsOdMC7ZDfmtS5bhcaS8LPYJKX7IXMfTqHPOrQtlZOky6dSlm11HSGOkar+xoGy6mlxmGdsYrmqZfPIdH+OusW2Vt7ktypp+CKrYdq1hrfHq1Jct4V36BaWTX11isU/Ugask328D1jZ21WFpZ+xN42wGUh1rA5Aa7zT4Bi+dXHBVxnA4LNxzCgDZ6nTtOGI8ssp1d6yWN+bvirMJhbqOPk4nMSktdGzTNnMZPzBNmnaSJAUhfRvYFD8ok0cVwcuaDu5YL3M8N23XTQmjPkgKncVYWhaagMvHDb0nZ1fQpGEcEWKw1oAOUOyjrrmA13vojhfJUEAFSTyaeBttGVKH1rC0LtFxg7vmut0utNvt7F63umCdT11lqMuZEJqexYChyQO0XLQva+BxXOWhczV1wFEaXDIZD8P/W9t/tVrBxcUFAKRHsvL75OvkN03y9U1Bcx65nLMhZcZ7SbENfvKTn0Cn04FXr17BaDQS54Berwftdju721GjIwQuWbIO1CHv1mlQoTwVsVqtYDqdwmKxyO7JwzB1y051jzPKVzhPw+8oD6KsI+2ipdfClKVFog3poXQBwJp+gvN5u93OTlO6avYAzZmxLWjyFu03tI2iKILj4x78v//vF+DoqAv9fhtarQjiOL8X1uKMzfMo0pE/A/ico/lz8TdNaz1d+k3Og8eh9Mjv3TRpdGvhQr6HIookh2whhCMM181yh6sUjn6naWq7ZbmzNY+TZO/WaVm/H7ZIQ3H3q/Qtd6oi/4leO0wx/fTOWKSH3iOLztY0j7yMmE76Gxz/80VveZ9Md8sCRBDHabna7T5885v34OOPx/C9751mi0o1Hpq3kT5vWxducd0C08dNE9pJMTwN13eOJvk6raerNn/coBqk+Xcbc3AZm13denzdfV/iNVSfpd+sujNNOwQhtpamULW96mxvi22N+wQkrFarbJNc0/zzhjdXh68OSztjt9U41KgmIdTotWlYjKIWRkmVeYnp1gmJIS+XSxiPxxnDQKZADe4htLn6kzVO3W2/qcnBZzTX+gMNLwn/0rOPwb8JqOLQtSxkcIXX2lczJlvysNIXCqnf+fiulQZfGTUe4lMurXk2VU9VodHF+4dPofb1U9kYFcZPuWHQB25QXywWa3zLWpeh4Tmq8gCLUw/j0LBVx6k2n/L6cNEkpaOBG4BdaVYFraMQWi1KvOYc9UFysFaB1QmYJAnM5/Ps9zbk2W3J0D7eJPVJrY1CeL5lbsf0Tk9PIY5jGA6HMJ/PC9d7YHsNBoPs3l88ytjVnzTZwGXE3TWUpZPLw9qchrvELy8vs92x0+k0q3NNBnO1rUWmqQKfTM/DdTqd7Ds9lUjqO1Zjv0aXVY/mOkoURZlDYNsLQa1oisay8ievf1c/ofX/8GEf9vba0GoBHB114eioC4NBnDlhowheO2Fdu2GLzkv59zrdtAr19wDAHKt5v5W/rz8X49Bwcn4S75fr0hXG/b7+/pPmw3VEnRb8lpeluNO1GAYAnZOQHROcf0uIg5O/p+ljmum34k5ZDJs7YIvPaVyeR0TKHGVlyNuXl4mWlaZV3AG7vhu3mCbm22pRZ22+6za9QzYCaiJBOlotdMi2AGAFg0Ebjo468PBhHy4u5nBxMSNx9HlcCqPpMpJ8wONR/kxPGqJypkt+tzhvN6VjNS1vhqR/Feaz6wpf3Vt1KAqrfmgJWyZ9a96uMGWdopptWHtH05by1PItW2912C53abxK5dHsGVa9xCLb76rP6wa2/rlTxxRfV+BAKmNUc8XRVk/UMSh9acznc3jx4sXae7wn7wZFaIxZeqawOkikHTwWmnZpErsOwEUS1t3giDIC5nVFVUfbVQU3KNN5A79L762gfNnn7Gqi7mme8/k8M+jyMHXmzevP5wCx5q3VmWaIwUVKTTgvy4wXSz3zMPS4XImWUOVQSwf/W4zVdMxoc6N2v6yUL4Lfm2VxyLraA9OgtPj6wmQyyeinTrk3bZ7gDkludKzKL1yyGU8bV9H/6le/ysK22204ODiA5XIJs9kMFosFLJdLuHfvHty9exeeP38Ow+EQRqNR8F2hfHHjdYQ2rqWxH8cxLBYL+OSTT7LvVA9yGZy2XY+asxj7w2KxgFarBQcHB9DpdKDdbsN4PIZPP/1UjYu7guuawzXexXlgkiQwnU5hNpvBfD7P5oZt1/GuIorSU6KSJFF3yQP49bhWC+Bb33oAX/jCIfR6MbRaEXS7cXYvbDrH5DtjozUnLHWK2pywlAzNAZs/23bc0rCuPHm4Yr7ae7n+ZHrztFxhm4UvM9d8L8WVwkeO75ZvifguKjhgaTgeBtt1/S5ZIDthMS46V3ma9H3Kl3CXfnFHLN4PC0Adr8X7ZvEb/Z+PE94HcYdsK7t/+cGDAdy61YWf/vQcfvzjfOGcBmoD5PIfPx2LxsE/voAVoTlbfHMCP20Ff9OFZZuCTx+9wZsBS9tbF7dhWEnv42PMklfT/ZLrdj5bhRVl/A5XFU3JoFKaljrVFsD47Ai8f16VBY83KI+ddMbWsVJik/Ap+3yVmq9cmkJM08N3fHWcb6WLlJ4FmlOQ5ms1WIYwsavSB6oAFXRqtJeYr7TyMWSsSIIJplHnaiekz0fLtqHVXZVVd3wsSHlKq1F9/MH1XqK5LkHOihAhyKUkSm3hSrvucm1S4JHayOUYwDiSo8qaF587fDTR95R/SLRLCo7PcEB5D5/jpLAc2zK0S/MdvuNtSO+atKJqeSTZwSp7SE5Enm6IgmfNnxv7ffKEJPdYFsFospQrnA++/l5mzGJZXAvuqo6JXXGgWOcxV1yESwbmPEUqv6XN0LGC13GgswUdaCjP0T9fmaXxQfv1LshMdcEit1r1JEtYnu8m4JLPXfoUtvX5+Xm2aEmLP51OxfR4/pwOaa6y6mY0fToGkPZNI0TX4HM1visztpow1Lp4zpMn+/Dw4R7cu9eHXi+GTqf1+ihieK07ohM2d4Lm/9cdo/h+3Slqv6+VxsHfmBcNW8w7z4N+L4YvplnMV3fCSvHWac3DaOHkOM2Adz2eX37cLwDd+YnxaPgkkeYV+g0AXu+WxbTy92leUVSMi2mm/bJAaZZOTottlyxNA9gdsXnaxXtk9ff0e7FO1vtvHh6f052yxR3E2F+RnWEd4f/0DtkIkqQFd+/24POfP4QXLybZDlnLvCbJ1TwclR/QESsdR+xLR8LBwQHs7e1lxx6fnp5mVzGgDcrCF+u2NfhsmJpdtIn060SIre5Ng2sslLHHId+x2ji2ZUewwiU/8nLSfsZtD9qCfC1trndI9ppd6s9VxhjtAz59q0qZXX0M8+Y6I1/AF0XrduQbbA8WG5UrHMCOOmPrQFkFqymECBBWA1mS5E68MrSE1o9L0UaBzpKva6JEVGV2u9T2HJLBfrFYZLuKkSHziVNbUYOM2dJvrMLJrtdhk6hiZOE7X1wGVu1Zg2XcNtVuIenWQQPlD6vVqiCghBoM+e9dFbglcJqtBsQ6FWRqIEQeT3dsuQy6Wv/Hd3zRCX7T2rpq3yrrfJLmb60/8XqR7jm2lkNTfELKUNV4EQpJGbbSG2oQwXkvRGZCaH24iToKSRPrTrpHbJNzMu2jTfNMNPy5dgyXLTsvh0/2pPlpdd/r9QAACotTOp0O9Ho9WC6X2a5mF02Ur7rKfJXmqxsU4eMrUvvGcQzL5RJevXoFAOmd0dq8SvuZxst8PLWsQxbHETqMr3I/bZL20LrRwn7lK7fgt3/7HnQ6LYjj1utjiYt3xFJHUqpHAlDHJ3V6ag7S/LlIDycL49Bved7FZ54H/V6MX6TD/W69vmQa1+nkcDePO25ZUOefK0zxe8Te5w7VdRqLcxd+y+Pid2m8R2vx+LvcQSk5WGm6RecvFByw+TP9BgDZblfucE3f8/D0P2RHEdP6wHd4B2x+xHHxe1YKNhZWK4A4pmVO5bPHj9MFEn/5l58WjiuWwPUbDv5utVplOtJiscicpdoCzxBZ8fj4GN5+++0s3el0CvP5PLNhtNvtgs4nYds8/022U111hOjAFschBY4z1/igYV1p1QWrHCDZyTWbpPSN6hbUiWfhDZItw+LArQOSntZkXhJQ75bayuL89LWvZitCSKedYjta+vINri6urTN2F0GNbD6HD2UGVKGWJhjLZCINZJ9hir93GeIp09buoQhxpljCXTVDFa0rqXx4DA2tX83hIRktfU4B33vtnRW8n0o0URq2LUT7BBLX2PCNNeqUpWWl479M+V1tR9/7wpVBSJu5BL8yjhMpbRctZXgkTT+k3izHoFqgCb5aXYY6aULC8DbC391ud03Ip3GpEwnT4vMdTVtaYKLxR8npFiqg1jHmQhQq5AUarXWPT4RPvrCkJSl5tDxSuDLl8fGDOupNUvKkRR4h6fJ+wed3mrdv3IaOZQ4p/Tr6VhPyFaULeQW9H1dShC19TAujjVsuY2m7/jmsdYLlQAUb40mLGqU2w36DxyBPp9PM+XaV5F4Jrr7JjTIA8hzLx62vv5fh49sCHQOSrM/Hu8arQ3kzrUsqv9JvljQoLbuIJvqAa/4N4Rkc77xzAF/5yi343OcOodNpQbuNC3aj18cTQ/YMQJ2ydPxwp2f4na05jWn4YlieZkTer6fP09ZoAMUJK9HAw2jftXc8Lx9CwkrDhpZBDrvuqMVvtIxJweko3yGb9yvORyhfBOB3roLDKQvK0cXFd9ozD1/8lrx2whbLT+PwtPi7FOi4TXeypmGo0zUdp1Q/z/PA+2OThIZP6zVJWqSuVgDQgidP9uHwsAO//OUFDIf5ccLaJgZpnnMBd8ZSfYLb1hCSzMn1Np++ajmNo6oNri4Zddu2JAuuAo1NQrNB83ch8fn3svaAMjYlC038ncUuaoEWjuqiWllQH3EtfOW6Cz3SnOu7dUDT+0Liu6DZKSw2RnSE+tra14cke5oUV+LLbzrvuIqw9AOKRp2xZSZqLmDgOykcDbMLsNAklc3HWJuG1XjnU+w1YweW0WIYKGNILBOnqhBZFVqfxgkvRCjR2gLrXBJWLe1hySeU1l0crxQh9GnGePwtrXDCeFEUqXdH+ZQLC41avW/KUFbH+GqCx0t0SU4Uq4KHAiuAW7m11Ac3fErhqaGUx9X6c6hBVgqL+XU6nUwwxz5urSdMkxt36VhxOWF8tPoUHDpO+TxsSVOjzTJvhjrqQ8aPRV6i37X3vnJgW3Mlro7xqfVd15Hu1nTo+NbmQytoWUPuf6XKHOUZGt2u/ll2jGj5bFOWloyErntS+ZjTeCT9z/OjcBlKfe9C5geaT4i8irwRjxMcj8ciT7pKyro2H2k8T1qciHMl8ghXf2iiX1etd+z3PnnEZ5zRHPuSAVBCiM4KYF+wsAtwyVDS7zpRV/20WhE8frwHv/M796HdxmOJ878owvYG9XdOU+4AzH/TMQXkf/Fd8bfsuOXp53ny9H3PxTSksC7a+DfpmZdD/FpjF/elRbshL0v6PWHf8jjFd1imJAuD39Pw+Xf+LYrQGVnc+ZrTkpBwVJ4qxsdji+kzKV2hLPQ7p53GeU0lK3NEwuJv/h/LLMkC9Fuedzpn53SgUxbrKUny44vjOD2qGGAFDx8O4PbtLjx/PobxeAnItq26t0++kE6f0+YPLm9iGM6/XToBOmF8aFr28PFSrlNqYcqmXxVXSTZrAtY20U4qAljX+0J1Yx89oTpQFR2S01DW/q+Vw6XjIi+w2KwwrHRcuY82q64k8aGmbf9aPlRvpN98+r1VdrY8czoseNP5yy6gTD/kbd+oM7bMJHcVlLw6YRE0rHAp6BYDLTd+agw9dPBz5qaFsbQ9NzSUxbb7mWsyR+aPxxYDFFczzWazgiEK06Npu/KjE/a262EXQY32HFbBkhrRuHHZagBzGY12vd20/lhWcKD9VXLSSPmX4VMhaaADweU8oGnVAT6OqRAg9dmydY87VjFOv9+HbrcLt27dgul0Cs+fP8/S5oJ9qCNx08Ik9iGrMwQgb2PfXLZNwVgz+qDyxR0XVrTbbYjjGOI4zk5voPk02feldqL8mZeZKnfYVtoc6WpLi3Bt+YZHzKFCO5lMMpok+kPGzlUFLd98Pod+vw9f/vKXYTabwcuXL2EymcB4PF47wlyCy0ggzaFYvxLPDjHI4BjAsNPpNFuggndonpycwHw+h8vLy+xuT2nximtuX61WMBgM4K233oKPP/4YTk5OoN1umw2luwjaLj6jES78oactWGWhbRspJDqlO62pnNhqtWA4HJZuW1dduAzw0rhJkvVTXHg6lIdZ5sdNwjWOQ42vVYE8P6Ru3nprD37/9x/CvXv910cTUydsfjxx2gZ096vsnAWvkzQNQ99p7/P/+XvMS0+fhl2nh4ZZp0Gjy0Uzhex49TV/8XtdfYXOSX46kkQybgNwB2PCnKf5s2TDyesDHao5inHz37lTlIb1v7M88/iJGC519PLji+lv2SGLRx4nCWROUnmHbJoWdcDyPkqPKwZIHbFR1Mra6WtfO4bz8zn86EenMJ3mfNzCcyT5D52wFnlT47/UoXLv3j346le/Cnt7e7C3twfT6RRmsxm026k5uNvtQqvVEo/G3EU0wcO3oZe+ieDyudUGbAGVt2g83GEemp4lvzr7Ih/LPH2fHsF1nFarlY1xCVw/xmPKrwpCbaSS/idd9xfqu3DpNNYjn98EO8B1hUufdeFaH1NsMahdd1iYCq0nHl5iLNtmFCGG0l1kaBbmLhmXW61WZgREhJSPt6XFWGEBdwpZ6Gt6TLrSt5bbR7vPMas5zSx0aDTReNwxp4XR0ikzRqqOp1BhW+JHVeFKxydYcecnN1Rymn10aOGt9GntLxlfQ8c0d160220YDAaiAdZaNs25pqHuObwOx6CFFl7+bTtp6V8oLegk0HZBcznBNX59sI4h6/jg+Wq0SelradH+rwnedOzEcZwd7z2dTgsLHULrJ3T8WOFz0DQFPCr67t27MJ1O4eLiIjOW+IwN9L2lv9D43CkWWlbu4Ee5DMdIFEUwHo9htVrBdDotyG1W5R0RxzEcHBxkfWhX4eqXkjyL7yU5hhsttLRd/MKn72goIw9Z28U1d6PxbDabrfGskHa38j/pu0abtS53Ud9ywVevPl5vzUPq41pdRRFAtxvD7ds9+PKXbzl2xCJ9xXHFv71ONXtP886f0zDr79bf53QWjyXW0l/PS86Hv9ff6XQVaQx3wPK0reBpursK5f1ZDDUuhqHvsb2SzCm5Hi5/xnpKsu9RRMPx3arFtIph0ZEbtks2Lzc/7jjJ4kNh5yt9Lu6ezcub/y7WreyQRbp42bAu6Q7ZVit1yKbfkkKY/M5alPPSdojjNOG7d3swGLThl7+8hOVyDsvlOg/Wx77MnzW9g88nVO7Uwg0GA3j8+HEhHkA+/+AiuE04Y7cld0r5hdjDLPKHFO9NQpmyW3UgS1iq81rT9NHi0/tcspfWVyz9j5fFIpdxeix8B//z03w0fcynT/vaqIo91CcXW+2FUnv46JL4q6VvaHqPFM9ia7hBOOqyIwOU0xU1XGtn7JsKutK6DtSVTlnDRVPhdxlolFksFtDr9WpjHnW1JUedDK4qNtH+VuMivqM7HUIcDlI8HlcSzJJk/ZjkXUccx4VnqlxiPWAYi1NM4ge+OkdHCV9R6coLdz1hWFxR6LuXAxFCn8bjNCFdckBod3q7jLI8vHR0DY1jFYLxz2VssDplpO9N8yRJMaiidG0KnH/wtqA7N5fLJSyXy8xJgN9oH6jC/zEezbOMk4waolz9RlOuKL/lz774vCxYT51OB3q9Hty6dQuSJIHxeOw1crkU+F3oO1UROj9b+BNXXBG4Epzed1R3HdI5Ch3JcRzD5eUlDIfDLJyLZ2J/de0AvWrw1TUdY5JRmc4LlDfw+QLnWm3u26Uxo829AEVnLEBR1qH/MWySpMd7c3klBK7+SPOR5CIt/i7U9yZoCMkjZD67c6cP//f//QSOj7vQ7cbQakXZrtgogrX/6Zgo/k/py9/RZ/yd/5fe6e+LaVZ1wtp2x1KaaLUXm2Dd+So1ES+fL3wZhKeDjlUpbvFIYSluFjIBkI7kpXnI32niScD70HfSc/Fb7sDNnbnUCUvfQ8HpCuQ3/5/vhC3uiIVC/03HZwSrVeqQxTrDP/yW3xnbgtRZiw7NfM75+tfvwNnZHL73vRNYLGRZ1CeT0jAuSHOfxp+GwyF88MEHmVyP8wie2oJzylU9eaMphMgT18kWuSuwzreom2rh2+12oV3K9HNuY3DR5rKTWMKFpiuB281cNpJOp5O9Wy6XMJ/PRX1kW3Key1ZUB03dbhfa7bbYL+jJYNY8Nfn8hr9uB7ugn0i4Es5YOrFxIWMXFe6qNEkTuYv5l2XiVsO/RlPZfFzODR7H6iDQ4uxi//CBGpy0y8MR3CilAQ19Wr1a2tXXB6QJ3tKernR8tITEKRM2FFrfDumz1rgaf9AUMZ9TcRdAaaROGoTFmC71DZ9R2MUzaHyq7FIBlzqnqHHZ4qTjtPrKE4IqzgdpPGM5+/0+LBaLTDBt4ljFMrCMA5qvZCS18hZt/PniWtukLscRlZu444KD9ld6NLOkhPDwND8ahr/30VomjCsv3xiU6hnjWOZLyQBG6zyOY+h0OtkcLI0ViQdJNFbl3yFpNG1QwjLhrvvlcpkdwYv5V+FdUl/3ydZlge1N2xYdwHTc8fqX5jtOq6sednU+p/UvHf1FQceaxZgsxdfSCU2rLKR+JeVJaXG1qaTj8XJgOasa9apA462boKHJdnW1ER+vVWmIoggODtrQ6aT84d69Pjx+vAeDQfx6J2zuMEr7Rf4fHZD5M6ZZdDJhWCF38r34nqfHy1rM07fzVne6FvOR3sl0SvGkb1I6vrAS6uhqrmFB0y+GW78HlsfBd1jG9DnJ3tHvdKdsMR46J+WdskWHaP7OskuW10GRpvV7ZCG7t7b4275DNq87/o2XmZcxioo7YQFSB+5qhf04Ic/UyRu9DhtBu92Co6MuAAAcHLRhMlnCdJovzHXZCHw6ixbPBwy3Wq1gMplkjpblcpkdhezj3WVsM1b40m5yDg+t2xA9fRM2yE3IOBJC662K3dqSlyQrS3JTGfsyxg210fhkQhd84S19i8spkszIdQ0atqqz2kpn03C1OdUV+a5gjKPZinyyf5U+z2msW2+97tgWXywDkzN2lwq0K3RsClhe14pqywSBTMNy1yJAcaBL9xxVRRlDLYaTGPum+oU0FkLHB58McSKwghoYXcJzFSFg09glHgNgE24s8TXBx5KGFBff+3ZEWJwtuwJaV6ggUgcGBdZJ02XgO2O5kX21WsHdu3eh1+sBQLqa8M6dO3B+fg4/+9nPsvue0RmDhnkLrMZ6CT4DsCWuNpegE3Zvbw8ODw/h7OwMnj17BvP53Js2pcVSHk1ZsBgKfH1ccopY6KaQ7iF10bMt3obKFNY7LiCg9+ZoRuVut5vt8OPH42O6cRxnjrRN8RWLwcincPN0+O48n7IuOUowHepMpGnjGMJ7MH3lcu2M2zao06ts346iCHq9Huzt7cGDBw9gPB7D2dkZLBYLOD09zcLwfsodWrztMExIWWj6ZcqB6VDHo7Q632rM5OWmTmqN/iplqBvIawaDAUynU5hMJmt3WGnzDB1Lu9Tnm4A0N3DZI45jWCwWhb6FcTTdDOuR9qGyepw2f9Gj61F2o+Gbbjvf3LvuoKumt/G4aLirijiO4I/+6DG8/fY+9HoxdDot2NtrQxzjvbDokKVHFMtHFeM7MDhHi89Ije6Edf2W0qV0uPMuPue/tfc6nRTr7b8exv1Nc4rp6WjgDsDXb9WwGj3r39Z3zebPEXmW8qIJ8XToXCvFoQ7YorOYhyvSnqjf6XORfyCvS48E5mHSYPieV1yy9g2dp+hMTcsJ2W5YdFZj9pyNFfstynYJRNHq9ZHErdfpJHB83INvfOMePH8+hu997zSrUw4qI2aUJ+6TXiRocwnK+3wOBgA4Pz+Hy8tLWC6Xmc593eddCu6I4ihbF5usx12R+wDs9UX1hzrpT5LEuXtRk8s1mwNPI9ReuylQeqW7cik2oTNItp4m7LwhbaHpHPQKGK2+ptOpaI+05EnT9PlgNIewNb8b5NgGXyzrlyo4Yy2efQy36UJWXVFQNY2qCKkzn5GCCmnSdylPl5He58jV0ilbn5b+VCZtq6E+NE3f4KprLGjpUEM3MmpJUA+pM96fEK5Jusr4cQk6nJ4q6YbAYqyn+YQImRjH4jhw0cPja9+kuDzsrvBxX79Cozb9o/3cpZz6DHAStH6gCZDYFxaLBcRxDHt7e9DtdjPnFX6XFtBoAiqH1ncoH5ba2mWwt7a1VIe4M3I0GkGn08mO/9TaQqPfNRZ4fUg8u4qRV+sbrn4k1bEvHUt/qkq7bxxZymbJE+s7jmO4d+8e9Pt9ODo6gsvLS3j58mV21FlVGSu0b0pzV5m0ePvyfqv1N9d4KiNruMYQP+68DM/W5paycMmVZdJBGcdiEON1xWkJKWPV+vCNwTLjgvet5XIJ4/F4bUGEj5ZtgI8dq6FNmmt98pP1W9l5j6IpWcmlT7jmThfv8clAvr7okjuuIrZJu6+u79zpwa1bXbhzpwdHR13o9VrZscT5+Fk3BufvoPA7BXdUYjztWYtXDE/zxu9aPulP7oi17o7VaeH08G+W7/p7W7wykNJCpx95sxaWdx/8lr/PHaH4jofB+sz5YfFbkmA6ydr7tC75zld0wHL+tL5LltPOwyBdlGZOk1SWvLwRqbf89zryb5gHLQ+vJ6nfFnfMpnWejockG4OtVn5UMd4hOxjEcHDQgbt3ezAaLWA4tC9e1VBWvm+1WrBcLjPn63K5hOl06l0svOtOAI0+Lt9r8kiZOcJiN3oTYLFFa9BkRJ89y/XOApf+aIFVr/D1S5dMp+lBWtrcRkV/h+girjwkWORVq43V955/c73T8uPpxXEM3W63kB+330mLzUN0G67LuMrExwSPs+u8+E2CVU9y9WfElTimGCCMwe8iqkzQ3EABIN9tyGEJQxkvp5d3oFCl3gVL5wzBVe4bPuAdUgDpxNHr9bIdhFGU77yzTrIas79BM3AZ9PE7PksCWRRF4i4Rn4GQ3x1N231b9xVQenGVLq4463Q62bfValW4ixV3BmE9YFxaDk2Y9Rlt6VjQxhEXlgAATk9PodPpwN27d2EwGBTCoKKL6Ha7mSLsg0Vgo8+aY4Lzdp9iIAmEiMViAcvlEj788EPo9Xpw+/ZtmEwmpnJY+ExdvEiao1wCkja2fLSFKpx0Dq7imOE0UdB7lbV5PTR/xP7+Pvydv/N34O2334ZvfOMb8Fd/9Vfwb//tv4UXL17Ay5cvodfrQafTye6X2QakMWqJI/FVabcl/S6NL/5d2oUg5auFQRpQUaQ7z6pg14xFq9UKzs/PC0fmIbAtyjqicSzw3fu7KvNw3t9qtWAymcCvfvUrOD8/L9zhWZdjfVdB5zL6n/8GkNvTcr8p/25RpqsipC9jW3N5jfIYAHDKFS79D+dAqhuEAOPRunXxzl1AmbHfFL/4rd+6B3/jb9yGwaAN7XYL2u2o4IyV7odNmxP/5w5SzRmKTVF0JPneradXfNZ+F9OTn4txaDj6TgrP3/O4/Jv8zh3WHbcaaDdaTzdaCxe9dvrJ72m6tA213bJUz6Tvef7F+Jg2jYfh03B8l2zR2YppFsupOWSlHbTu36kMz9/T/1B412rRRb0JrFbpe3Tmpt9xpyzAapVkYxDrDP94W6R10CJ1kP6/d68P+/tteP/9S/jxj89AAjfYY9ksi2cwvpQmQH7KShzHMJlM4Oc//3kWJo7j7Pt1RKi+RX/vqpxYBWXm+TJxeb+UbL4hdLh0Lmt8SpPLBuayhVghLca32jlcjlhu85JoRT2K8xJcRC8B06eLPkPshNa24f4Mza6loWx7aOh0OnB8fJw9z2azNXl6Pp/DaDQq6GWWHbmudnSF4XgTTgl6kyC1ZSln7C4qWmWNNU1iEzSFDlBtQnQZFV1phTJfKb7GsHz5bII5baI/UYOGb2LCHWqSgcYK62RWpf9a4lZpvzod+CGOlRBwg7vLOdKkw4oqeS4j5rZ4Jx7duVqtoN1uw4MHD9YEzNFoVHDOlhFmy8JS7+12G+7duwftdhtOT0+zsO12G+I4hrOzs8KiCim9kHb1OZjLpivlgQ5L6rzEdOnRida5gNJo5TFSG1jjSnOJa87RHGXaWMkNO3KZqvZFHy2cDl86VrRarexIJ9wZ++jRI3jy5Al88skncHR0BOfn54Ww9PhjjpB6oGUOKZMmZ9B+66JL6l++fKljhCtNqNiOx+NCPFfflRTVMn3JJXO5wjQBrS5x0c3Tp0+zHRuz2UzluUmSHw2qjQvuGNL4LHVENQELf3fRhr8XiwWcn5/DdDoV64X2jW0q7FifaAzqdrvw4MEDODk5gdFolPEI3iYWmrU5rU4da9Pyj28ewv8uOd83D5SR82k/4gsj+Fy3i6hzDtR0Mm3esaZ//34fHj0awIMHfej3Y2i3WxDH0es7YumO2KIjNnrtsEKSaLj8XR6Ghsu/FR1wtHj0PT4XZfFiWjxdqOCI1eiQ3+fxpG/SMwjO3rUQDXdrKX2py9Bw+Q7aohOVh8/f5c5T6XtRviiml4Yr7kjFuEW5mH+j/4vOWxo3p8O3Q5bu1KX0RABsJ+w6bVTG55VbdMTmZc3/w9pOYLoTttg+OAbw6ON0vKbpoPN2uYyg3Y6g243h1q0ufOYz+3B6OoXhsHjShYWnuviMlSejrEqf65AbyswJXNa1yKY+PcgHi35WRYfh8/euocrc7ZPDaDifbOarf0nG0+RGS5/AML4F8ZLOVdb2RtN01Tu3d+F/a36UPq4DAUDmhHU5chG03q0O6yrwjXmpz/lsplJ9Sn2K/ua2IdRT5vO5eCpRaJ+gdjMOSQ/ksvcNdhtVx0OjO2M14/8uK3K7Bp+xkQ5Yy114mgHKZRSi7ziTcxk5LXBN1E3u3tt2P+SMnO9ipGEokiSB2WwmTrghAskNirCMiarplnFG0HR4mq6xIzm90JBdRx+oc/y0221IkvROhn6/D1/4wheylbpI7y9+8Qt4/vx5If+6oPE1CdoKtV6vB5/97Gfhzp07hd21+/v70Ov14K/+6q/g5cuXtdFsBeczocI97z90BTUKjNpcoUFTrqyQymTti5TfVjnSmTodKE2u35uANC/gex/f4MB7m6Mogk6nA5/5zGfgc5/7HLz77rvw/PlzePDgAbx69aoQli84CKHbanyhZbE4B7Rdyda+Z+3LeHcuVdxmsxnM5/NsJa3rBAtL+a8bVqsVjEYj+OEPf1ioQ0nRleY0Dq6oS0q0Nh/X6djjaVrDSqdZzGYz59whyQbbQrvdzhZWDQYD+MIXvgC//OUv4dmzZ9BqtTIe4dNRAIq8TOIBrv5QhwFNQ5O6g8S/eVktczofN1ofoSeNSDuJaX/U2uOqIlRmcKUT6iD4whcO4Q//8DF0u62CI7bKjtj0HZXdiuXMn9N06DstPOZRzK/4vB4nTb+Yn06Hi4b19zntnP715/Uy6mG1MPX383XdTgojfY+y9+tHBBfTSRJKO9/pimlQR1gxHXQAFx2mKQ00Ds+niGgtHM0LhB2ywHYB53Sl39bDyb95HsVvmFbxG33GHbG4ixZpwLpD52te7qhQd/R/ux1BFLUgSQAePBjArVtd+NGPTgvO2E3eQYnyfEa9x+mySZSRX3yyhEXWoL/rkM93pT6bgqVOXXXP7QsStNNAQuiSwuOCYXo0rRY/9D5SWq4yzlTtmX9z6T4crVYL+v1+diQ5Tac4BxRlGMspbtsAlf9D52fX4lwO1OUvLy/XTnspIxf4bDOWsDfYPVhshRY06oyViKgi3FY1NDSlnNcNn+FcK4dmoKC7mrR0XGlKeVapS618TRpQOLZpTNAmQBekOuPtaqk7brwOqec622aXxqDLmCUZsUKFgDoMpJKxx+WMCBVaq9BTJQ2+y6nVasHe3h4AFI/nxh3hGI4f3cnhEspdShW2FToE0PlIHW/oIEbHynQ6zQzMg8EA3n77bZhMJjAej7MViSjY4ZhFZVgSDi3zptZ+lCfgn/WIE6mP8b6FAv3JyUl2dHGIU4v2XW1estBYFw/yKY4+47OmrFmVKSnPTcxNoY7IJMlXiGK94dHhEkLLEGLoQHq0ccOdMvSda2zx/snDU/7gks3waFyqwKES7DpyV+t/aJTARSuu8CFw1bmvLsrk5XNYJEmS9S/f1Ro8Dc24g+XAOtToKKPYS3RVTYPzIW3e0sJtQ67icwW2XbfbhTt37mSLqOhOe65zWPoHjyfVgaTLlO2/Gg1V2ljSnyT+I8Ei5/O6oXXgW+jpyos7Y2n+2z5CTRsvGqS53iKDSemE4v79PnzlK8fwzjv70Om0II5b2W5Y3BGb3juJfa34n+advssdTzRM/o4/A4BnFyqNmz/T/PI0eXo8rE6DFE+nDdNdp3k9vvRde5d/25w9QMprvf/x7/x97hgEYcds8Tnf2ZkoTtmcL607QbW7ZFPeRZ2SUUZLnkb6jpSsEC+ti+Ixx0WnK5D4UeF3kZcmLH06xvF7fr8r/ubfQHDa0j6ZOmGTQr2mf8lrB246jtMjjtNIeHfsahVBpxNBkrTgwYMBtNst+OSTEYzHutPD6jjIaleZQyX5B/l5Wd2wThT7YLT2zRqXwyWT+eRMV96uOWeXbFt1w6UjA4S3XR35Yx5xHMPe3t7atVwIPHXHpbtZ9MOQfkrT9NmgtHcuXReBYVC+pvBdU0Hj0/Rc9Enhq9oyfLq1RlvZPhbHMQwGA+j1eoVFu4vFIrPVSTT6dABfP0C4yhkq095gu3C1rXU+uDJ3xlpRh+GoboQaaaJIN6C70qFll35r8V1GRi7YVjH6+IwOLqHKUu7QeNuGhTZtUPPfmmHKkq6VBs3gWSbNqwbJwKcJYy5jcUh+VccYhdRHtg1JQGm1WtDr9SBJkswZi/yQ7kKU7owN5VGu+lgulwXjMaWXOluQTrz3ttfrwePHj+H8/BxevXoFSZJk98eiwIfpAhSVZInmUOMvFcq1Mkv5+NJEYDvQo0RD06DvLEKtT4lxxQ2hyzrH0HBopOZhXPExjdCxWFa24X2oDA9IktQZO5/PMyej5IwN7a+hNNQVT6sP6mCgipdVhkO+RJ3WSZJkzlhNmfP1HeQ7lA/WCcw/9J5Na7pxHBfSpos4KE/iO82leg+REakBReInXBatMk6oDF1FD9HkLG5IpdiVeR2P6o6idMHR7du3YTAYZN+0eYm2j1R/0n+pjkPGVRkjapV2dfVDa/6WsFq6Lj7o6mv0WTPCYbl2Wd+iqEJnqD5PcfduH771rQfQ6bQKd8QWHbJY38X/Kd3r7+h7/E3LmD8DAPB2Xv+N4aJoPd3i72I4OU+ZBjnfaO0dp1uK9/qJPUth6HurHGkKZoY2bH28AD/T13kUdMzanLI5D8u/FefbYnzXscXg3Q2bO1hpWYth0u8YnqdRpDUi5czjATnKGIiDlTty1++0leqTpkfzy+mjd8mm+mhKa6uVZDTi7mJ0yq5W6e7YTgfg3r0eHBy04exsBuPxuixUB2ibop6Juiu+L2PXq5NGSfby1YVLXquqz/lo4r/x2Ur7VYB1fnP1GYvt2mXrsMpCXBc/OjoqLKCn+vl4PBZPspKeNTolu6uUDu9Dlv5hqXdfvbRarcK9sHyzgys916I6jV4XXSFjQKs3LR/e7i56tDK1Wi04ODiAVqtV2Ogxn8+dOrYkG/vKJtlgrjKPaBK7XjeSbaKMrYDj2jljrwtwEvEddyEZdUMFAl/n552ujDFYuqOC0013kuyKcWmb8NUBbXvNGCK1l2tHrdSf3mSUnRjK1KG17jVhwJrnrrTvcrmE1WpVUBCHwyF897vfhdu3b8OTJ0+yO1e/+tWvwjvvvAM/+tGP4Pz8XDXqArjbzGUItYy3OI4LdANAdifecDiER48ewW/+5m9m4Q8PD2EwGMAf/uEfwvn5OfyP//E/YDQaQbfbhdVqlTmcKc8uo9C5xrClXBpC0rHSGJpWU8JZ2fJwg3oURaUcZE3OdZpMEFKXuDs8SdIdi0+fPoXbt2+rihdXOuosl0+Z5HMhQK6koSyF5XEpftaxQIFHXuPOVZTbrIYhGkZqH+3Y3qZQpwyAdXHr1i24d+9e9u7p06cwGo2yY7CxvqTjw7HN+J07UZQuCEDjQ5IkmSKt9YUQuuuWoy0Ijc/z3Pbcju2IbTkej2E+n2e00e8A/jlBc/Dhe97Ouw7NMKPJH1zO0+piU3AZG7elu23acMMNgXgsN33Habp9uwu/+7sP4N69PnS76Y7YdUfs+o5Y/WjiooMzn7+KNNIwPidoni6VbYrh5N/2/Nff8T6+Hp6+L/6O1r5JzzyP9W/qp9rhyosOG81xweNjHKyv/DkRvq87XvFbHg4zKDpfMW3qyEyIE5S+S/lyMSwIO2Qxn5wm7Q7ZPD2MS7/B2q5YWkM8X/qu+B9pwOd0h2sCdNyk/9M80MkKkLzeEZuWH+sS/6c7YyOI49XrtFvQ68Xkm38elxxXZbCLNjZNbgqVpyQdh5eVyh30j89noXWzK3VZB9y8sty3snT4ZCLuTOThWq0WnJ+fw8XFBUwmEwCAbBGxdNoHh9Uex+kC0HUOqxNH6pNS2ZMkyRa7Yp4PHz6Efr8PURTBZDKBDz/8MNvtyTcZSPJnqHO1rK3KlR+FxA+lXcC++Fx2o5jNZjCZTJyn1Vnzk2zu14lHNIlNy/NVEDpOXFCdsaGGpE2hDkFi14QRCl7XLsGgTDmo066M0Z/nJwkzPlDjmnQvAA+rfZPSvQ6wjD3XhCYpcppyx9N80x2y2phyGQ9DxmFdSpWmbFji4G9LGZsECoBUKJrP5/Dxxx9DFEXwxS9+ETqdDnQ6HRgMBrBcLuGXv/wlnJ+frylSocorH2OWuyQwP766cjwew3g8htPTU1itVvAbv/EbGU3dbhf29/fh8PAQxuMxfOc734HRaCQ6k7V6l4R33pZaOCtc847FEWZNT+MtVsW7ilyiKfzW7zQMnUeroAlZROrH1rql4ej8vFqt4NWrV3BycgLT6RRWqxX0ej3o9XrQ7XYzxbaMPCAhZN53hXONKem3ZZ7UwMck5dGWeVpKA8NXrU9uhLKijrkA+eDx8XH2/OzZM/WYcqm/cl6Pz61WC7rdLqCjT7oj1lWOumWdqm1VRt7btqzGxyrSs1wuYTKZFJyxktGnCh/nRiqLcauuPk3TCjGq4X9aF774FmNQVVnSla+rnXZF79okHZKxTaqrKIqg3Y7g8LALX/nKMQwGMcRxfkdsfjcsdRBA9r/oCCoeG5y+o32qSFv+XEyHfiumIz0X817/ncf5/9l7k11ZkuQw1CIyI4cz3HPnW3N1s0tkN7tFNikBhABBAsEH6D19gP5B0E5/oH/QQlsttJcW2mglUeAgkBDFBocW2VN1163pDmfOOTPeIo5FmFuYuZtHRObJc+sakMhMD3cz88nchnB3jlujT2nX0+p81Hmu16f+vOJVAutQ6XpI+UQ10pLyaHImSXgQF3EkgEFD/hz/uz4gTrtevvhd31la3ZHKd8Ty7wKnVO9cCOwCuPxRPiivRdv4gsRIl9Ln9oi7E9bdgauN5WpnbHX8MThpRUA3gTTF+2eLoG2vl0KW5Te741NYrTZRa6GWV9OdeB4NduUbtdIJtYlms/Ny+vyx61u+MpY2fZNA05vb1tXi46U7Ibk9IOVfLBZwdXVVptGgpUX/t8wFvjuTjx+fnsfLcLtP0v24Hp2maXn1VZqmcHx8DEdHRwBQHM+Mp7MhaLisdjEH2v4xvjhLu4TohoDXg+vg6ONYLpfOnboUeP+G7GmaTn2Lt22rvYXmELM2Nym/052xTQzvbcFdmRRdtVdo0UKQ3hYK8WBZzCTHW8hxtS9jJQbajvEmQRRLGUlJoEfXNMX7TYAYg6Bp3/P56TO2NJ64M6gLvihdi2OuK8C7Sfv9fnk/LN81lSQJDIfDMq+k/ADI4xvbO7RjigZeufzC+UNl5mKxgJcvX8JkMoHLy8vy3tgPP/ww6k0+H2A7WHEh3/TFFzrGaBuEFGkfaEYFxxMbEPLRigXKW+jUibY8WAzLfQQcD3hHLN7D84d/+Ifws5/9DNbrNXz88cfwr//1v4a//Mu/hB/96Efw3//7f4ef/vSncHx8DL1eD66vr8WXrbriDYNuknHFx502DilozhsNqHylPGkyVwoE7Wp9bRv8agP4FvZgMChPQJB2kSNf9IgtTRZhn2Mg9vDwsDy+eT6fw3q9LmWtdlcs/U2PMttH8I1nTOP5bgvo8dzn5+fwF3/xF/Dq1avyGUCck0VySEl2g8+pum99a5EtWjqVOZY1rOnc59cn0GAjzWPRod5EwL5YLpcwHo/h6dOn5VUUdDf4wUEf/p//5z14+HAIBwd96PcTZUesG4jVd8TqO1ITJ1BUlcPn9Js/c/HKO2IpfvrMxV8P1NZpa0FYKp9B+B0OwMqO0VqS59l2ZahLj69vUh7+rC77aZKLg+8orQKj1W/Xge6id3eaVsHOKhhZpVH9i/4v6Fd0E1Zvvps1cfACC6CCs5O23oZV/9EAK8VT4XPnUXGnK+bB44ZRvGJ9C7r03tniaGKkW9wVi21T1L/XS8o2AUgBYHNTtgff/e4JfPDBAfz1X5/CZOIeVxwKUOzDet8WQvanBUJ+RE5PKt+1nfImA/VR4X8LWPxokl7uG/NY5smTJzAcDsuTdnz+kZj+5nqQ5NuImYfW+lt0NVqHo6MjePLkSUljOByaeZL4oLxiH9ATmmIgdPUN6pCSb1rKj8F0DDBjn3M7Sepj9AXi88vLy/JZF9f/dCGT3wS5/hb8IM3vVsHYWMfOLgdZiLddG+nWhUtyPMQ4Bnl6DISch7H0KYSUzFA5X5rmnNKeW+k2Kacpe6FFuA0fofr7nlFnV9t2exMWEauzHiBuLLeRNdxgDuG2yo0mPPDfXQJtz81m4xw5Sd9mpMDv7JTaSnLEY6DIEvTGb5/TkTrnzs7OYDabwWQyKZ+98847TlDYN+8kkPipO1L848RiQHFjoGlA0bpuxKRr8inG4UzXVovDg6dL5XjfaM+t9doW8Dkh8c2f47jP8xxOT08hTVP4m7/5Gzg8PIQHDx7A48eP4dmzZ5BlGaxWq5ohLLWFVX7IjtW6DJb+N5VRmrFvyU+P0Y0dQxreNmPDJ9us7dh2bNLxgDhDclSiqzlkqBzFAKxVnmrjXaLj49WHtwlo8oLWVZNDt62DUR5XqxWcn5/DbDYrecX1PJbPpuuRVO6224jyYFmrJcA5he0tjRlrnaX+8K2L0lzZB+BtEFMn/G0dG/R44sFgAI8ePYKrqyu4uroqddXxOIWTkwG8++4BnJwMyiBsNX8xEJQABnMwHUAPjhZptJ5lqsNjVb7OPy1bx6vjd/NyHHogNsRX+Le2s7fOjy+P/My3vus4YkCeKhS5G/TU+ODP6rLNzVP8RzrSTtcKDwY6NfyYX8JRfct3xrpHGRe0OI0Kr7T7VuaTluFBVs5rvU3d3bFSG9fHM5UxuVM/nDPFb7p7tgra5nmRH1/COD7OoN9P4eCgD+t1DosFHmVct9d2KW+70v8sdLSdjj6Q7CvEJ9nI9HkIn+W5Zt98E8Dapvx5jM4Q0/8AUJ7QJJ1wJp2+aFnnff5Qi24hPbPaklQ2cTy0HfF6nMFgUAZgsQwGF7Ujcrn9b+HL0nY+u93S/0mSwGAwcGQDBQzC5rktqK75vdBfR/9rfkNL/3K9s41c+CbKlbsCXfSLNEdaBWO7cDrs2jDeB4dFDHBhpAkfKjhoGoVtTm6r0OLPqeOWv5kScmJbwbeoamBRBto4lduC5nSJxZEkSe34im/im+4xoM0rSZmKBRoAuSugOfOs4x53XdB7VBeLBbx+/br8fe/ePedtP26MUXr0zbk8z8tdWfhsOBzCdDqF8/NzpwwAlGUkRRXfDOQymO4wf/36NfyP//E/oNfrOTQ//vhjGI1GJc7FYlHWmTqpY8aMRdZK48gq/6wO8CbA34aOlZGhQEqobFe74egxvpQXzqME23Joa4EmgGqNxV2Klp1V0+kUAADG4zFMJhP4b//tv8Hz589hOp3Cq1ev4NWrV3BxcQF5nsN8PoflcunVU2LqwX/zwJ5lbcc89E5XqayPD83A5H3NgyNafaRg2i7BqhP5eIydtyjv0IFQBSVkXYan87ZerVZwfX0N19fXcHR0BIeHhyYecNxLY4nS6MohGdNO1uCQr93eQjzswjay0Kb6t0824x1hOJZRrtPj9yhOTWeleoI03n3t8iY5jHwOfA54JCDuxH/27Bn803/6T+HP/uzP4NNPP4XRaARHR2P4/d9/As+ejeD+/eHNjtiijfGI4iJAw48pxjFQ0KrSePAzFAjV0iqcIfw8T4VH3x3L0yhtmo/iqvNX57/+THP61pIcfJb824AQnSpgSvPimJTxYHrdWe7mwf9Ig94nWz0DACUISuVCktBy8j2yle2gB2QhYodsNQ74scMVXlrGTUeEOXmupRXfaZo7u2RB2BGLeXAX7GZT3yVbyNbqHt88T29wbsrfKG9+8IOHcHm5gL/8y9ewXG5u1f7flY80SYqTUIbDYfliy3w+N9OX1h66flp0XMtdjtJdpBJdjac3Bbgescv1n9OmOg/1H5+entb6dLFY1E5UC4Gme6HdHNKRfLq55EOyzHdq9yDO4+Nj+OCDD2Cz2ZTXX6VpWvqXAKC03ylI9pTGm4Unax4+Zri/ebPZQK/Xg08++QSOjo7g+Pi41jY//vGP4Re/+IVYJ/pNwXcqXRs/DJVVktyy7ra9SzGpt7AdcIKxPkcIT+sC2uBruvhxwdAEx7bBJzR8+Zs6fDge3/OQgyy0QGtjzFLWh5OX3daYRdi28PS1k8QHX1Cs/QEgH+NqAR9f+zantglNxkJojscaRNI8pOn8dwxPIfAZJxbgfKFjcL1ew2KxgOl0CqPRyBmnVBH3tRl3+OOxmfxtXK3d+JziNDjveG8hBoSR3vX1dRlMpkelUPqSUthWjlmDLj5osk76+Jb6aJsQkp+hdRVAr3vT/morH0O8UT60AI/Eo7SW4ssSOD4XiwW8evUKfvzjH8P19TVcXV2VBp90nGVb0NqY10ELONDnPsNJA8v6izh9YylGv+lq/dTkmJYv5GxpytdisYCLiws4Pj6G4XBoxhPqI3S84LcUTOV4qNzlz6Q6tx3P29ZBpfVjl8a9Tzbiixm70pd9Y9vyvCndbUKb9ZeWl56F0vFlMbxLizvG9tWJZK0zzd+0L7Msg+Pj4zJAe3KSwcOHY3j4cAgnJ5mzI7YIuuInYd/VB3mqqoFrNtXx3WesRiB1jSvr6jgofp4nSSq8lEeXF5cfSz6aRvmR0+v1kJ5zXPqzMLQd4pZhhTTcvNg+9aAslqmn6TtlKxoJgLpLtrr/tV6e72Ctnte//QHZqn51+8rXLqH6YF6k5+J3aUp8I18YyC34qf4XOHJwg8v0ztiqz4oxijtm3XmPu2TTNIFeD2A87sFmk8G9exnMZmuYTusBka7XGqufrivg6xja4dRG9+nzGm/cnqG6UMg3ym3wEC3p+b76kXcBFp+xpV1867VFx8BT1FarVe2EIkn/9Nl/kg1Mx2ZMnX22uQaa7S6VTZLECRBjfReLRbl5YLFYOHxLoNnFml9Z4zvG7yG143g8huFwCKPRCIbDYXlFEgDAbDaD6+vr8roTia5l7PnsdB+/ljwhf6s2zkN+xbdwO2C1b3z9ZbVFdnpn7F2EGKfdLsC3UzakFOBz6c0XSx2bOi+tgkV7o82CoytHxT6Cdu8ZQkjh4coBr//h4aFz791sNuuS/TcGQs79LhZQ+lafdO8Fp/cmLNq8HlmWlXcHzmYzmM/nkKZpqXBjOt4ZSOUZvZuathW937Xf7zs7YfHeCQyeSnNF6gdMQ57yPIderwej0QgWiwXM5/NyJ9inn34KL1++hF6vV+7gwvvFkKdQG0kGqwaSQSDVQRpDbRyTPnrbhFiecWdcr9erGXBWoONKetbEMOkCLO0fU09s29lsBv1+H46Pj+Hrr7+G//yf/7NjAFLY1h1MdK5ZIM+ru26avHQUc5+Vr864E7TJDvi2EJIDFLCdaJCT44mlnec5vHjxAr788kv4zne+A0+ePKkFTy3g45u/KU+PcdV4QqOe7tLdJ30/xMc+G+9pmsJqtYLT01MAgHJ9sxitIScGd4rhrhrr2rivQMcg/tfyrddrR1f05aWgObL5M9yh8PDhQ1iv1/DFF18EnZd3AdrqNhL0ej0Yj8eQZRkAAPz2b5/A9773AA4O+pBlKfT7KaRpdXck3hWLvzFAI38DuAFQN4BJA7ZVF3S7I5b+r/JJdOp0pSCsnFbnneehZaVnHI8vn1zGApbMckBRzU2y87x5XtFMkgo3lqH5qzRXjmIet4x7n2yeu/kwkIjpFHc9IBuzQ5bXMXHqhHTxWUGDBjrdZ/w+XCABUxfqvFTBUvqNZYu0NC3yu3fHVsHZNAXYbHJIU1yLsEzBH35o+xe/i52xm01Rh8GgB2mawA9+8ABevVrA3/7tGWw29kDgbUKID74u53lxEgOeFEV3j6FNZqFJ8fNrjKSdk1KghKdJLxtJa2TXtts2+7Ir3E18sF3S5vjpKSDn5+e1067Q9sO83Gdm1a+p7SgFR7kPXrNrJB3WAhQfb8/NZlPb+ZrnOXz99dcwmUxKvVvybWkQ47/39S9Pp+2EPq/lclkeFZwkCXz44Ydw//59cafwF198AT/60Y/EY6eb2ObbmG9t/B4+3fwt7A9sw4YAeMODsV04hW9jQvic5zwfVxS0YB3HLdGUFhoNl5aXB0EkvkNlpPxNHZghAXcbgQMOoSC6BE37iisL3LGFii3F20TxDPX5vkOTPpGEtG++hIIl9ChFyZDwgdRnVmV6H5SA5XIJl5eX5fhbLpcwHA7LFwWs8gCd7jFGXmw7cPmKjuF+v18eV3x+fg6TyQRms1kZBMDAjI8XiYaUjyv6ViNIk7WcvpSX1tXKWyid8xDi0RJEoXk5XmmNtc6dfQuG+JRE+qwJr7Sd1+u1Y0QBVE4ULcjWVLZY5WkMLhwrofVRmlNNeUc53uZY6zZgDYLR9Z/uiOb5OL9aP3F94/r6Gvr9PqzXa/HIrxi+KR/4Njh1qGF9eF/isfU4VvHYL0keNIGuZYHm5NoWvS6gjf5n0aO43npXgMqgLvpNw2NJk+YGb0+uM9Ey+zLuJPmtyXfMI6WHntO2wTtjAQo9bjabwf37ffjudx/Ao0cjGI16N0FY3A1Lg6x87Er3xILzjD6v8khjv/sdsTwf/V+lbTcQW9fTeP00vmQIiw0/vVBZeWhp4+3mqfC4/gzbL6+VSRL+n8tMKX8C4Nklizy7z9vskJXkRp0Hf/2l+12TWptg/XjA1uUJd7i6+ar8SM+lS+lzHal45uYt9KqKDgZ50dXS6yWQ5wkMBj3IMlm2WnTf25DLsYElrZxlTbfUTTp6WNOXQj5VDpa1o4k+v009pkvcWr1jdXifztGEh9VqVeubEF363+pv4OOozVyjtLnfKWSvY571eg2Xl5e1ubNYLGovKXLeNf8Hr5vES5MxxQOs9CX4w8NDGI1G5QttyDv67jA/v1KvK39AyC/UBeyLvvwW2kFT2zuU940Oxt5VkBQCq3OjiTJhHVCa4d7GSclxdQW7oHGb0EYRoGWluwuyLCuf0wUTwN+GPiXmmwJtlAMKkrO+LbTFFZItXcJ8PoevvvoKVqtV7VgSAFmploIGeZ6XO2vH47GTH98UpLh8b7ZpbcePHV4ul+VuCdyF++WXX5bHxVjwS8ohD2z4eJKMTAvEyk06TiVcIcPaIjMs4846trnBI/Fu5ZkbA1S5DxmbbeZiF/MwhjYPrM1ms9pdKIPBAPr9Pszn89qYaMqrbyxa24/Oc/pJkvpOWYobHTr0dAILTc0JhLsv89y94/G2gY9Temwc3rvN88e0PQIG61+/fg2np6eQ5zn0+32TvA3pHEmSOIY6P2WAlz84OICTk5NybXn16hXM53PIsszJu2vdJSZQ6XM27gvQuWd5Y1yzezSHqVWu3IaD2gfW9rDi0vQemsbBJ9NoGZSRXE7sUheMgbZ8WcvSnViLxQJOT0/ho49G8J3vfAzjce9mR2xCdsHS3bBV0AZ31ElrJeYpkvzHA9M8NJ3n1XBiep0uLy/RCQdiMV89zS2v8S4943il56F0zlNXIOOjawtNrwKeTioLnLrpyHc9AMnLU7mAz2l+X0C2KCvfI8sDspQnzC/lRZwcKhpYCXpnLM3Pbb3cSUccPOAKbGetxIOL3z2u2J0jADRIW4iCBDabij7mxXtl8zwp75TFNsN7fHu9BABSGA4L2VHIcCzvvhy/j3LXAtLdq6hja+tVrB2b57noL9BgG215V/unCeyDXoV9zvViXKMlXSdGd6R0ugzGUuAnEIXukEX7dbFYwIsXLzrnCXVU+pI1PT3M0n6cDwy0AlQvsCE8efIEnjx5AgBQzl/qu6PpCLRvfTpsiLd9GMNvYb/gNmIZajBWElpt3orYFnQV/NgVWNsw5IjU8mhlKF7NoRMCFNDaAsUNex+fPI0fM6YJV1+7xDhK92kMa6AFJaxOYRocAKgWL3yGi+t8Pod+vw8HBwcAUDgz8Tk6TKmTnUPIWUp5uCvzVeOvKd9NHNgSPUtbW+nFzoddzRnOF93FhEDvldX4o47gPM/LI4nxbbuDg4Nyh1+Mw1ijR9scjxHs9Xrl3BoMBjCZTGrHtdDghzRvOW06h6X1JEbuxoA2NmP1Am29aJqP8mfBJ+XVeA+1odTWTQwV/synDG57HoaMrTzPIcsyGI/H5TgHqBtL2wQ69nwBB2tAj+LFT4zxbh2frmPU38YhXpsCp01/0936vjphXZIkqclmXxm8JwxlLuchRj7RMug4sPC8WCzg7OwMTk5O4OjoCM7Pz8s7MfdFP5H6ed+CiiHga1QIYsa29BJQjAzndLfdrpwGnYNSIM6HB6B+XHtM24VkIeoiSZLU9BVqg1D6bSG05nHetbVc8leEytNn9LnUR5i+Wq1gOBzCJ598Avfu3YOvvvoKnjyZwOFhCr1e6gRhi/YsAjiV3Kz3Ow30VHnAeablBxbQlPK6+Gg6nUM6LR9djtfN55ajZev46nj4M15eeq6lhcrsCihtDMjd/Kvl4UPVTcc2zGt5k6ReVnpW/K7jqfLwu16xXP3oX15W+4ZaMFSjUbURfe7SKnC5+Kt0t12KNJefancsKIFxXsdq7CP+6t5YpEvzoAyg7YN507R6KaPXS+DoKINf+7V7cH6+gFev5rU2p+CzaW4LLD6LPM/Lk3Wk/Hx9l3QJTTbz523bRMNP69IlvX0HzS8BEKfDdEGb4w35QaQxEwKKUzoiV8onAddxtPFE5zHHr12jRel3YSPSI6Al/qx0qB6F1wUBhI8NHgwGsF6v4dNPPy3zXlxcOEFq3t+cLv325WkKFruiKezK1/oWtg+abcPTtrozVmIi5ITaFh8ArgC2Oq9vEyRnNP8dckB1WScUrNpbO00VQ5/TnRv/nI4GmtJ6G+OvKWiKn7YIWYIv6BClx0nOZjMYDAbOWf0YuEIcfIesxFOMAzm2P5uAz3CJBYsSLuWVHDo+8CkYFpBkmW/M0zrE0N32PKJ89fv92hEl9ChNzMeBBwswEItH2hweHsL19XX5VqVEW8LtazP8j/zi7lh8qWE2mzk80x0WlF9fe+Bc0wwBaayG1gkL+By/vjbT6MasD9J6beWXArYdfakodj2xzOnQ2NFwSzRinnUFISd/lmVweHgIq9UKlsslzOfzMpDXhDepXKh/+JoeWockkIxjbuyG3lLWQDO2dwm+NUpyTOC6b6kz1T9D+gg+p3cFWeQJ5ZXLADqXAfRTDWg9e70ezOdzuLy8hEePHsH9+/fh+fPnKm1f2l3RI28TYnaAamsGH6u0zzGNHk0dS8/nXJR4iwXOuwV3KB/qNG31Ra5XcJmAwVifYxPTfXxYnZaWNTnUNnQtiIHQWk7pr9drGA6H8Bu/8RuwWCzgiy++gMPDHN59twf9flIGZJNEuheW/pbS3GBQ1Scq54xfNx1xV8/kdKQvP3NxIx6XJ9oHOu9SWam+Up2lunGop4XaL1S+PfjEEm/DKq8bGOQ4koQHGQF4QJPm4XPLfYY0kpKum6cKhlJ+qrlW5wWcI46Tm++c9SENorpHDbu+LmC0i7K8DhxHVTe3XnXe+e5Z5K0KpuJ/qB1XXNEs5lBRPk3xTlgaqIXyiGIANwhbfKdwfJzBJ5/cg1/96roWjK34131DdwHyPIfFYiHyi0fAcx2A6tN0Vxx93hU0tWHeZKDzMbauTcdlLB1ut0inNFl54fKyzRgL+d+kfNKphZR/vhEh1k7ltHl5vEZG4sGKE9O4bPL5uPA+6clkAj//+c/Llzakq8YkG9Jqv3GwjBFp/If00TddLryJYO2zJnJNK7PVYKzmIL9t2AcemsA2lI0mOCXHe4xw0spIR/eFnN78TRmLsnAX+l9yMHXV/4jbevwwAJRHK9IdzDHtGOsk/6aC5CCn36FyGmjBjq6Vy21Br9dzxl0M3zRwu1wu4erqqnwW2s0XG8TkCjsGgHFH2L1790oe+By3KPuaUxPnMleopfwaDcm49Tk2MbjBlW2fU7PpePPx0BQf7qYL4bTMvcFgUI4vK2+3uQ61cVbT+zmxHdvK9lhecP2STurAb2280HnAA7hdjjGKl+4g9uXrGqS6SQ4AmscnH/C5FMQJya88L3ZD4MsweHesxgvlWcIVyhML/DhA6fdtgSabNR38roG0lmj3kTfRPaXftwV8voTWZS6jrE7BUHAx5NDP87w8Sk7Tg7CfmgCvh9bXPvnFx4Ik06X5ovGjAS+3XC7h7OwMfvSjH8F7743hN3/zBB48SKDXw0AsDcIin1ReAvu4xwcXNKk9QOsLLI3y6fJLadTT60cTa8/CPPA8bj2kvHXe5XSOT3oup8m7hf1ltgMSHW24Yd5qx6wblKVlq7xladCOLq7+V/Oo/kwKitYDsi6/9R2ybjkfuEFeHmQteM3L3zQ4C8qxxNJvlxY99jgp25cC0nPz04At3PCZkG8ADLpiuxT3xBayAOuYJNVdsdV3UQiPK+73c3j4cAi//uv34NWrBZyeLu70+g7g+jYkG4uuB/xbC95Y9E5OnwI/jYrifgvtYBu6FtcHQmNJsgMt/mnMR23eGP3A4o+z5JPK8N8xINkJ1vax2GISSHfFpmkKg8GgTDs9PS39ckmSQJZl5YYG9JNJdr6Fh5i2svSHrx/2wb54C/sLvvHhBGOtA6mJINk2hAzafYOYNtQcZW3a39JesQ4PLqRCzgBO3+cE9NWbO1sk5crCiwRNnNZNQapXTCBW6gNfXt5uIUcFbVf6hpKmEOEzH06KwwddLHqxC20TfBqfVgcY5qUBREswqIu6tXHwbRvQWavdH6EBb1s8cps+l2jxsdlE2UNHJSqfSZLAaDSCfr8Pk8mk5viUjAdtvCB+Wi8rXyHgcqdyttTHMB/fMbhjgfLQxTjM87hAom8NwcASDcbSMttU2tu0adM1HndQ+spvU1ZQmedz1of4omtak7Fl1eO0wJLEUywPIYjto6ZrSchAxvkGAOVd2r67c2NkSlcQM5c0Wc3XY5/zxxoosvB12/aYbx20ALYBHSeSzG+ik3P9Khaa2LzWucWDhzQvXYe1F1CstK1AaeJ6hi/D0ee++lntFkn2xqzHFgchjqfYF+sQv2RfFqecTOGLL67h8ePH8P7798o7YpMEy1IcVZqLH3+XqTd5ab4qv8BlLZ9bhyoPpeXWyc0v96ucl/LlFqvTpLzWcen8SPg0PJjXN/ybTA1rmRg1jOKUyuFzHpTFZ7RMlRcADLtkJVr4rPidKPR48BSfSwHZel45n0Rf469+XHGVt+KZ40OgvElBWMQj7QKmuLS2qOY03XFb/Xc/7tHGSYJB2/zmuOI+9HoHMJtt4PR0ESUbtwUhXcYHmE96AVazxUI+RaorhGj77Ddue6PtboFd+gi7gKb8NvWNdW0D03Lay+caryF/j+bH9I0Fi++U845p9Oo4Ka818GdpS9/4t4A2b0Np3FZH/bXfr8JPeEKdRpcGY2Nf/vPJTd6GXczjNjjukhz5pkNXsTOEre6M7RK2teDdpYW0q0AsBU1QhZxJvnRf3pDw48oVD6zelb7aBkjOYt6GPucmB3qnpkSL7trztXuv13MuZ8eAE0ARNDs7O7sxMOrHXuwams73bRpCUjAB+fT1UZfA72zeFjRpf9ruMeMbwCbHKGhHvXCFncslTgthNpvBcrmEw8NDZ04g4C4x+oZuaAcdOmYlvrXxEtPuMcGIfQLJWczXDu1NaARLcISXxWN18LjeEI/bbLfK2dXNPA7pBruGJgF5Pi40WUfXqNg+ktqcBpdovibB/y7GDPKDcoi/OODjUXIKYDvyZ5YjY3GnMA+Gb3Ns5XnuHHc/n89hMpmU/6X6tAGrkzJUnqehY6KLHelvoVvgY1jS7QD0O7O0gKykG1K8Fr4oxMo4zemmOQRD8wjHcZZlQIO+0hGVWl00eaHZiyE9WpO31FFKnadPn47hn/2zd+HBgyH0++nNrtjig7wVO2SrYAumVQEzNwhZ/ZXvYq3+V3mketTL1Xfp1vNqZeW8Wp46ny6vlGde//rvpPas/l/OI+fV8nTlT/E9re8q1crRfLQtqvT6rlaatwoWunSTxA2gFnnlAKZ/hyzO/y4CsnQnq0u3qneu8MXz1nG4d8FWvyl+IEcTS2lVOfxP29zNQ+cm7owFqO6MrdozJztii7ptNgkUO2OLPHleyA98mYSuB/u+9qMM1AJcmp2GaZiH64hN+NCA2txt9M+75Edu43+h64klwNUWQuOcvrQnfVvA13eantTUlxJ7bYePTtPrcySbrUvgPPMTGIfDoXMdHr2yZjAYiPWKDYaH9NvQcctWuCtz/i3cPoTGyp0JxoYWVMuk0AKC9Pe+KzgUrG/NaGVCeUPtoS3SPp66XqglOhZDPpYGxW1J6xp8Y1YbwzFzJs+LY6LpG/AU8O0kHiivnA7VApplWRmclYIfoXEj5esKQv0UM294ua6UNotT38JfGwMmhg6ChU4bXtqOB5/RJ6X5DERfPegz3MmCQVfuENSMh1DfacEfCYcPjw+nDx+HJmuFL48Vr8ZLCEJ9aJHptI/o3b8aT9sOwiJPTQ1Fa7mmddmF4bKtNm47Buk8Dxn/Uvkuxg+uI3iqBQ/Ghgx0KU2SZRLPmn64TacAB05vtVrBbDZz+qOJM0cCq+ywlJHGAg1atdVpJZ60NfC2YRf8WOru0xFi2i7GsR5a7yk+6X9Ir7HwKaXHQp7n5Sknw+GwvHfcQs/Hr8+578sb4zsAKHavjUZ9uH9/CB9/fAzDYQ/SFHfFuscQg7NzlQc++e+k9sytt/ztptX1txC+Ko+kC8v8cvwcH8Oi0Ke8yuWkZ+5/OY+fnzr93QBtBz0wC1DwLj3H8vSZlJen0f/1Z/W7WavfCYBnR249rciPaYhDOt6YYSnpyHSL/zIONy/HUc0tXr5eTk6rt3vFDz/CuD4+i//uM5xveB8tv1M2TaF8sSPLUhgMUthsoLyDtqnuroFlHWuiA2trFtf7NNxaILaJ3rNtP902/C23Cb5gaxs7MxYsvoW2dkRIv/H5iTR8Vn6a6JkhPDF+zia6p7WcxBff5UptyDRNy5eFsVwogC3JqtCYaaKn07Ix0FRO7SKu8Bb2B+5MMNYH38QB21XwxeJwtwjuLgUHd2pL9H1878LpHUrbBk2pzvjBNsOdPfxtJN+OPyxzenoKWZbBwcFB+RwdfsfHx3B4eAinp6flEZX0QnVKDxdYfgfbcrms8ZJlWYkjz3NYLBaN2mgfoInDnI9bqqRI+SwKo/XYHgD3LrBvghwNKTmhYIm2s5Lnx+Ac5l0sFo7DEecc3enrU/ikuY9zkCuWPuM2BE2d+9sINlK5tw2ZbjGSLHn4PdrceRFr+OwCtjXntx101uSlljfkUKLHwQPUd97zNcwXLJUAZQC+jCEFKDXeQ3liQdIDeLtY+44fo6/xS+UgAMBwOIR+vw/z+bzW1nwudd0GVC/q9XpwdnYGV1dXsFwunWsXaH6kLwW0rG0VMvpDASGuy3H+tHWD4uV3isfwviuZhX1PX/zj7Y714PLWcnSvBjFOmSZAx4rkLNLuvab80XbQnNJYDnecoh5O24zj19qM6ocSTzwvraOvL/Bo+4ODAzg4OIBnz57BZDKBn/70p6Wc5DqYNPc0PrRxopXP81w8scSnHx4dZfD//X8fwP37QxiNemXQpArIQu13gZMGYarvG4rlcx5grPPC1x43n0vDpUNpc55oWY63+l2nJf+v867Vh/6V6i/lAzXAW0+TaGrQtQmkTxvOv3QXq46nGstFWcxL81Vp2J55mYfnd9fbekBW2iFb8MB3vbpBUJrGd7tigJLn4fxxfrBOUn1w7BTyQ2uTijaIu2I5j5XNWN0Z6wZgKW6km6Yo+4v0YodrdU8sBnOrb4BeL4UkyWGzAcjzDWRZCh9+eAgPHw7hpz+9gtev52V/YT05dOGLS5IExuNxaWeu12vxGFHt9DRLIEQ6AY+u6SH9r6v1OkkSGAwGpR7Rxn6+LbjNwM226Mb0AbcXmgYYKV0JR+zJSVadX7NL+TPNVuA8UV2wDZ9I05fuiwnQZ+iron6wJEnKjTuSfYr6oIUfTtfSbvsIXF99C83hNuViLDjBWGlSceO+a2gjNH1578pi2pXDidfXYnD7QBIIkuEv8aE50Zv0icVhuQ3c+wKa01fqb81ho/UHf447YxGXtJBq/FAnKg0SAYBzGTvlHfNThZ7f6+QbZ1L73DaEnMhSH9C0UH15OQ04Dz4FVerTfZGf2+DD7pip5hpf1K1yCfNyx7JvHPhwh+ZwF+3VFEfbcROqNwUfnRgFLITHimO1WsFms4F+vw+bzcaRdSFjJoYWz28xTDg9Ll/b6AexcqkLiNFteF58gYi/kSsB75+2dbUGQXzrQxvAOvA1nq890vorrf2S0asB0sIrDZbLZfSR810C6ilS/WLWSQ3oy2g+56JPrnUxj3x12dUa7xtD9Dm2Gd/xa1nbLDJW4ge/JZ3XR0vKa51LVqA8abq9VAaDsehEz/PqxS1eD/otyZ2mjk3qWJd47PV60O/3YTQalU7+bY3HEF6LjozPHzwYwqNHQ3j0aARHRxnZDQvk4wZZKQ2AentgOel/xRuI3zKu+lHD+Fum49LgdOq0rTxR2jKv9f91Xtz/8nMtDQL3x+rlugGK2z8M60cKczx6egLg7CrVafCAI83v2q48IKvx48fph6Is5q+X4/fDNtnBSwOmdH7LtCVcvvbQ8vOy9DedR5XMkHbIFrJlOCxOKzs46MF02oP5fAObjd/H0Bby3L23HfFa9QnuB9F4aurvCNk1UhkNQmuqhG9f/CMIu/aD+fySVh3Wqmd3wZ81n88PF8tXGz8Kflvs/JANFhqzFj5DPs0QXqpLSum0DJU71n7geramy2pl3sKbCV3KRWm8NPVFSRDcGdvUWfcWbg8kQWSFNn0tjRW+EOCb0dIODc1ZTPOGBLEPrMGUuwhWp6iUN9S+ANUdb7Tv6B0mq9UKer0e9Ho9mM1msFgs4PDwENI0hYcPH8JisYDT09OStkan3+87zjntUvddg6Q0+PJZ7qbShLumTNBvCWKc4/sGvjHRxRrEneEhfLGKm8Ww0OSjpuBiOh9LfLcKdXzifNGOB/cFFDTFYt/Gk08B4m1Cgc4hDMhZjB0fII7z83MYDAZw//59mE6ncHV15TXsKM19a9+7CBZHepZlMBqN4N69e3B5eQkXFxdqfrpTssndnJrMarNDMSa/xA8GZsbjMQBAuTuVB0V9c19zunBa2nwaDAZwcHAAs9ms9owGnuinyzWN46Z1onVDPacJbQz041GsCCFZw9coDajDIgT4kkiWZa3umEK62wKsz3K5rLWdr12azEuKB7/5Tm8KnCbupOQvFwJU7Y2gXfthBXrKDNWNJcA53u/34eTkBJbLJaxWKzg7Oyv1c4seSZ/jPPHJCAtwByPOr36/X74c4qsX/ab4Qs4QrhPXg5OuPYPtLcmhwSCFP/iD9+DZszEcH2fQ66XOjtjqAzd43R2w1bf7mwYYZV2QftcDqlVd/HhpOuWjyuPixzKcjv9/vS98abRePN39H3pO0/SxeZtmv0S7Puz1o4xp+Xo63idb3xHq/nd3lOLz6rcW8Czwu8FRDGAmQIORmFbMITfIabk/FpzdtklZp/rz6jfy6P5Hfir+/Ly6d9difwC7O7bSb/gxxVDmqfqhSi92xuJueYDNJr/5nZQ8VnfIonxLIM8L+fTee2O4fz+Dn/70CqbTuBfZYm3ozWYDV1dXZdl+v1++4BNz76WVN9//bYC2uQBAt8nf2mnxII27kH5lCcA18fla0vlzrjN3PfYRpJN5NOA+naYQCnb69C4J+DOu12l+LAqSvzdGboWCvXcd3sbjbh+2tQ6Yjinuyhm+jbJdD8w3wUEqCc+mzvU27RFTlhvJUhBBwtfGOemjua/gezuDtxN9TuvnW4B5AJQubhp+mhedNpvNxjn+Tzt2F/NTZWSfFhurIsJBcxZJzmzqKJICqnVjse5I5rR8PIXSbmMeWNq2C9nclXIWcgzSdF9ZiT8f8ICBxosFh8SXJBObrh27AF/wos3awHH61gecr9yRS+cy56/JePfV0WIkhdrDgkvDLeHpCnxyS1qTQvOLvkSk4bc4BKTnFt1E4nVX8i/PiyPTNX2Aj3npN8fH80hrE83Pj92l88NCsw3wtueBGqsM1/paWqP58xjn07bsJqsO3VSHt9DmLyRsNhsYDocwHo/Lk1QwOEtptFnjOA+ITxoHmszHMtLO7sFgAP1+vwzKSs47yZahY8a3zljqRmmE5m2Mc80KlnJ4X/Pp6SlMJpNWtDVdSMon4Zd0HD6PnzwZwsnJAO7dG8B43GeBVnd3LKgBUyjLkKekXFwbVHgT5z/Hi7xp9Ogzmkb5lujI31zP0fnmZWSeKp7DeH1jWX3UKq8GVrGNtOT87s5QXo6nF2kJgLJLtvpf5ZHwhXwhbl5fXQs6mEf7bkKDPwfg//lOTTcPx1N9Y7l64FinVeEAEuwuxmNO+gWA7nyt5iVdi3LnGcqWNC12yK7XVcCWr1sVTak9w7or5sGX9nggBde7mJe6+LoaWo+sa1ysDc3bCl/2wpdyF4tFuV6jjRBD08LzXYOu6mOxOzXd26rrhPQ1nu7LT8vtEmJ0bZ/+rtldu/LjSHJJykPz+sBXv1i7JBb2zd/1psmYfYaYvtds+hj/mBOM7cIBwIm+HTztQGrDtu1qdRpiXt8zKx8xTjbEKx2TwoE/CxnjPiP8LgGvJwYBtDestDe6eP3X6zVMp1ORDnWicXzYXzT/bDYrlVrJcYWOYQCAg4OD8s39JkGBLkEbF77xEiMzJSe4FVdXc2Hb0GRubZvvyrB1dytJd/JZnfL0ueY01pz2VvnJj+728SndRRsjp3m/8bLa0aJt+8wXEJJwWxwQITrWvD78NM9ms4H5fL43u/nfZMA+kU51oGOGO5R4ujYe+D2L9HdIX7LqU1YjtMt1MM9zuLy8FPmQ6sq/OVhkC60nBthooEPCsY21n/cNOh3plQohZybVgTSnA8prqsfS//yeYorX2p4hZ0eovA98baCV1YxRDnQO9vv9shw6Qk9OTuA73/kOvH79Gs7OzuD09BQWi4V3J0sTwPaT+gLro+kw2j3tAAAnJyfw4MEDuLi4gNlsBpPJpHbvFY4BiSblr60Ties2Wr6ugbeLNDam0ynMZjN4+fJlo7WcOlPpkeAW3vhcpnzSZ5VMAPid33kE3/72MRwdZdDvp9DrpZCmeDcslL+LvuXBFAAeUHSfUd7cdqj+13FQXDe/bvDST+Lkk/LWaRfPXfqh//I3zaul8zpxHuVneh45n/R8W/Yl3115k+qZajS/m08/vjhJ2gVkLTtkka88d3HXaUu7XqU6Y0XdOY+0cicgWtF074eV74ekvOIzpCPdYwtA6br8Y5kirdr9WsmJ/KYuOfD7cKu5Wg+yunMUd8sWsmOzqQKteN9sr5fAZpMAQAqjUe/m2XbGbZZlkCQJTCaT2rqBAcvBYFDe8QgApS7nA9xNS0+7SJKkZqtSPD79ikNo/aBpGGwdDoeQZRlkWQbz+dw5yYj70CS74i10A018PiE/rtVusuhYNE+T4IxVr7f4PDT+fL4T6beFB0u6lb+m/EhtEFuPbdiRb+EtUMC1SrK1Yuy4WjDWEiCImUjbXrw0J7WlLndhomrOu1hhrRnFsbxobecLPPA8+MynOElBEjS6Y4/4oyAFrPZZydIWJM6z5BiieSmu0Digjmjf/NKMIg00BxbSpI5Z7JeujhOVjnYNjcFQXSz1tc5ZTVnjRx02rb9PfnO+mga6KPjKNVWyQuBzFNNxS7+lulvGhNS3PvmogXavoFYfDT/eCTcYDByZb5FrobWSOq1pmV1AG7ksOV21PBY8FrkZugtTM7oshtq+rlEcdjE2LG0h5cE5kmWZs7aE+jY0R3zlNV2Jr4UWxwCd023WApzTfD334Y0Zh9o4xx0INEDlM541/TcG3MBKXJtJ7Y26qeYslP6H5CvXo9rOIcQ3HA6h1+uVcglfeAu9nKf1tdQfvH0t/CdJUgZjKS9ZlsHR0RFMp1OYTqfO+sh1do2vEEhrLMoBTk/qN7xqBcvQz8HBATx9+hTSNIXr62uYTqeObst5lX5jMH+9XkOWZXBwcADL5RJms5lYT5+eENseIfy8D6T8lH8pj3R0N/0f0rV8fFpkKC+P/UNx0w8AQL+fwnDYuzmWGISAK/YtAEBC8BT/EX1VhtYlXi758FZ0KX7KF6efOHlBCNRWz7TvsCOXl9Pr5dat/kx+7sct598OcEJusBAgHJitP09u0vk8qOMr8iUO3fpz5NNvk0r0Qvez5k5AVjquWP9260sD0AVNmk+uh14nzpOEtxrLuLaBSJO2e/Vbol3hp2WrOY9yFNOqYG8RoMVjjIvAbJal8PjxEEajFF6+nJd3xzYB3BmKgPrBd7/7Xej3+3B9fV220/n5Obx69Qp6vR4MBoPyigvppUW3zYvAJ9U15/O56bhXTdfVfHY+HRZxDQYDk08ltJbsyv69KyDZ2JIO2eS5lleDkC3ne6bpNLHgs9EoTTov+PNYu4LrLD7eNLslZKtKfPDyfBOVL28T3dXid30Lbxb4fLuWtKb2kIRbGreWsR4C8Zhin3D4pgz0XTtAt9m2IYVJ4gXzW52SMYEDqYyURzviy0KLOzp9cBfGNecRhQJ19KADhAJ/y0/rE64Q+ZRrzSGn8Y1Ad/jx/NjX+NYl8kCPfPPxHwL6piU/SpTXzweS49EC0li31IHf9YY7Kiy777gMk2RaSM7FtHfTvuHA7+qLPRaJAm9ri1ynTlkfTnyGn1hFnivP0s49ia7Un7jLCx3cmMZlv7aWS4asJn+bQKicJK+b0IoxxHw4KA+aIcJp4c5Y6VksXQ7WNcxC97bWuq7kA4Cr02iOF6mver0eDIfD8jhRukuT8ynxDiAHE6w6hNZHPj2lCx2Uji2fPItphxjaqKvM5/NyjgC46zIFzVnWtC0keWcpo6WF9EqLI0XC06UuutlsYDQaweHhIbx8+RJWq1W5PqCeaNHNfTaANP6txjDuxFkul07a0dERXF9fw2QyUa+vaNpOdK2megY9mhFf+JTWoiSp7mXnO5s3mw3cu3cP3n//fQAodv6+ePECNpuNswuYluW7qJEG6gKDwQCePn1aHucrOe+6AKn/NJ3V58TDe2D5kY/aSy8UBwVN19F4860t1D6SeOAvgtKdz1gmy1IYDNwdsUXeehCUfgr6bp1u/jl5XH6lb75zlQd1aCCJ1luil5DyiYMLakFLitvPS53/pJYm4XR/+55J/El59HwSdDmd5CWBEsBx6i+Dz/kzzX5JEjdv8R+J1HeyVv/doKdOF5w89d2qFT6oHYHsD8je5HL4pN9aO1Rz3s1TtFH47ljkqV6Her0pDtwtC+R+WNoemCbNmaqtcbdslbeSIflNABYgTfF3Ar1eCllW3B17fZ3B6ekcPO99BqHf78NwOLypV3FiSpqm8I//8T+Go6Mj+Pzzz0s94ec//zm8evUK+v0+DAYDmM1mNR8El6v4fzwew2AwAIAi4IvBWNxc4dMXNDuM5/PlwXx5npe6v8Yrh9uylW4bLH6xkI7If0t6A12PNX3aorf71v+29pPPLpP0Ep/vRjoNJWTfaDoXH/vcZqL6i68sf8Z5jpkDIbuI5431l8VC2/nr88lotLqw199k8PVJU/9C0zwWepqfFMvzE0FpGc0ekZ6Z7ox9C7uBLh0xTUG629MqlH0OqCZlqVFP00NOMKuidVcgxuFrdTKi06kNP9J/Sh+Vdb6gUcWJ45KOrNEc602AKi0xigOFNottqA/5kQfj8Rj6/X75DI/Vub6+Lh2ICCFlwLJg3KYigW2jBQ18EKM0cZqxjnrKp8+RrhkrPvy+eviCAwjU2Y6KAp/nFqOHg3QagebcDIHmGPU5eUP4JNBkjpXHWD58ci2Un/Mr5bPguytrHVVqLW/JA4TbgQdP8DeVqYvFAq6urpyAIOVJo9VUvrRdqyg0Xa+wLAJ3gFkMB44jJMNoe9EdhFJZ+iwmYNMVhNabWGcEAt1xR4NuWN9t6B5JUr3Atlqt4MmTJ/Dhhx/CfD6H2WxW7ozF6yGkvojV1Wla6NQaOtak46HxLlF+tG9XQOnR8Xjv3r1SRqDDmOZHsMogXj/f2kD1cZw72I5ZlsHJyQlMp9MSLz8thtYtTVNYrVZweXlZvoyV5/r1JZwP31oUcqJo8oPKA8zjOz0ihibm6ff7kKapc2wml0MUn6UfAQA++eQefPTRETx9Oq4FYhMngMJ/4ziD2ne9Sgl7Vg92Ci0j5JF3x3L+OF5evuJFqoN8nDHH5a4v9fwh+nHPqjxyupa/e5BouEONZpCDiRI+/qxoX/fo4iSB2n9aVvqfCwFZTqeaT1JZmedcCW6SXABs524lIyq+pDaS+ZCPXQbP3bF1PHRnL7YvD75WZX1tWY1XNyhbzUVaL7oTln4XPKVpQb/XK4KyWZZClnX/Us7BwQEcHBzABx98ACcnJzAcDuHzzz+HP/uzP4OzszO1nKbPDYdDGA6Hpf8Cga5deHyxJKt9NPj6HQtaEJjaJJJ/rKkdcJdA8reFbHUpraneHKNzNfVBSBCr6wHo48RKw0pHK9sFXg2ovXJX/Apdw5s+199CHUIyntt30lH9TeZMLRgrGSt3aSLGLNBtFvPbAM2Q9A2cpgsi/x/joNEGpm+xluoQOlLNx4vmYPQ5vO/qwtNmrvocpKH+8eUH8DtcNKcUHm22TaCKvzUgwvmkZUIy02dg8DKYhmN+MBiUb5X2ej0YjUaQpinMZjPI89wJeNPxK/Wrb8Gw1L9LpTc0D3malhfxhfBq+NsENzSDxBdssDhn2wAqCL1ez9ntYzFyfe2C40u653CbEDMvEWLWxxBdioPy4ps31nmy7/rHttZCHnBoeq+hNs8QPwKls1wuYTqdeu+60uoc0xbWvKH1p8n41/ihAbMYZ5alPaS1j66x/N5UCSwOnrZgWV+aBnAkHJY1tska4MuPwToAgPv378MHH3wAf/d3f1euCzHtb+HBqjNJDlU+HnD3tDVYFwuUHh3/BwcHZSBzPp/DdDp1ZBTml2wZjtMnz3g70HGCOGn/9Xo9ODg4KHVA5Ek7tQLX/clk4vAi3Tuv8RcCbTxL/auVlU5r8q032pzF736/75yio+k1vLzfwZnD++8fwA9/+BCyTArE8t9YrvqW0oEETd3fxX+BE+BsVjiT8r+Lp3oupdNntLzLZ50PMbVW70R5ljhp4XT5meV5Pd/tAeXBnV74wBaUracnZdlwPqjldfPXd8hWv+WALKdPcdVpWdN8deE8yutmvR1poFbiuUjD31I74Dh15X8VeC3SOV2sIwDtYz73AbjviR5djI5flP0J9Psp9PtpKYssoMlWqpMNBgM4PDyEx48fw8OHDyHPc3j16hX84he/8F5rIMlmPPJ4NBo56fgt2UuaDyMEVnue2wShttN8M980yPPcrD/wchws4zXUn7Qf2urpsbglviSdzodfw+OzE3y6FMfVRL/32Z3auNdkimU+Wtqoif+hK39FaAzdls8mtk225cNpAxbfdCi9K4idQ3ye93o90ymVPpoI0Ttjv8mL0jcBfH1rUXS0PNxAxjI+evjWHB6lgmk0v/QGvXUxbKos3CXgwsNavybtgEq2tBNP6yP85sfm+Y6JDQF3VuV5XtsJRRUudFA2UTatSpgEdM7wu5CTpAh8nZ2dQb/fh/v375e7G4bDITx69AjOz8/h4uKidjxJzMsLMYt1l3PDqpTHBNbQUekLPnKg+WlbhI7ojZGFvnlH5aK02yWG/yRJyrtjOQ0cY7F9iHixbJZljpOXOs25EeOj5ZO9IQMshBtxbEMRDfW7dYxYcd4mdN12OIa4wzxmXErHe0rA5zKO4dVqBWdnZ46M8L3ZbDEcLLyH8EjjO7TLMAZQNiRJsQswTVM4Pz93jhNtMnYl8N05r+Xl+Wh6F+Mw1H/auqnpqJYXCWibWoz7LuQF4sE8uNPTUlZ77huHlvWZ8oNt0vRUlm0C6llXV1dwfn5e6z+6y5Q7dnGcnp+fwy9/+Us4Pz+HyWQiHuWIRz3i7uWQDbRYLKIMfpzTnF8ug2Kdl6E1FdsId13TvJpOgG0XO9/pOMc6DgYDmEwmpd1oqZvmbPzWt47ht37rQbkjFnfFFmNXO54Ygyvu7lhabyxTpQdryupdp0dpIq2KN7fNaB6Xh4T8lvhNBL6lALSLR8Mv4bDmd/9LZd18vvzNQZJz4VKUhyo/JvqDslK67Lh380n/XRxxAVm3nHxcMeav/mM95XbTyxVlqjzFf5c3xEPxV/mktsA8Fa06bxUv9WC0jBdpurTdcY5HEtfz4N2wm02xGxbrhEtlr5cAQBGMHY978J3vHMPl5RJ+9avr4NiT5C5A5WPjOvh6vYbz83O4urpyyuV5XjuRSwN6DDLipGuq9GIY14FDvjyr7o15l8tlefIF9T1JwUbJPvim+L6b6twxPkYNQjoAXfcRYvrIOmZ8/oZtjAHJn+gLZtK8/IqrkE1N2yvmZKo2/s5YeNN88V1AUx/zbUGsjdEEd1dAeeTXyki01us1rFar2ovDUgzDAo2CsTH5aAU1IbsNp6nGw77DttqC4ufAjXMtnxW0RYQb5LSuNI0vssPhsDTY6bHF3Ni3BD+6qF8Ib9OFO0axtDjGfPh9TrSYtrPUzToW8JvXz1JXPo64UxIFJh6ZQxU6Xx1i6hcayxK/Ujp9RneJYHq/34csy+D6+lp0pGu8tZGHfK7FjFUf+NaJGH5pGct489G30LL0a8jQ5OkhZyd1DvA+5zh4GY47ZOxI9aU76vh85en0mVQHnsbLNJGZtFxX8l0bNxJtjT/LerALpXnX+pCv/6kBaFVaLbokz0f/J0lS3rdN+yW27aV53GbNpPPHN/ab9hufX4PBAHq9HvT7fcjz3GkPaW7HrmGx/GryZpvjNUZXpKCtH5ZyvnkfWisl+SrR5zrNer2G5XKpBr05Dz5eurAPQjgweEyP2OU8cn6byE5OG+86HY/HZTBRKuPTI5KkOP758vISptNpeTceB7xuIs/z4N290k5haQxw+eLTbXn/avPMp6NrMlvSAWLGilWmc76SJKkFYENrt0/O3buXwbe/fQxZ1iOB1/ouU+0/pmE5kqNMr/Nk+xZKkjw0uFnRktuAB4oqHC49PtY5TY0vGqSqtxPHG5Of0/Tj2wZQxFWgrkwxDHnMX+VFBG7gTyrH07W8dZ79uC246HMLXX8e/YhkCw46b3l72trEzcv7UKorT6P5Jbz1cvQ4ZIAioI3yit4lizjyEk+SFMHZfj+F+/cHJY2Y5bjAUQVjsyyDLMug3++XO1kxYEnvc8f8/FQD35rIXzCnz0I6uJQm2bZSHg0f6hR8PY3RNWPs030ErX2430Uri+Dr/9g2CtnI3K/nwxHSkX30rL4mLa0Lm6Vr34UEsf0Tsg18dGJoSP1iwdG1/8QyzjRbxuLv2RbE9s9tgeYXsspinx3ZtP60z+nmKA2kQCznwWdbcT6jg7F0ZwKH0KDzOSzfQnOIWYR2xYtlQqFSxN/iobtdj4+P4f3334fz83M4Pz937n/L87x0KFIHSojmNmEbbR5y0mF7SNDWYUWFE8UXKte2HWiQEQUjvz9YAzoG8U1IAIDRaASffPIJAABcXl7C5eUlvH79ulauiZJCnelNAd9S5W+MJklSGkubzQaGwyEcHBzA+fk5UGc6f5tHA6pw012T1sUvxgFtgZDzLLZffHcCazKSG418sbe0jaUNKW7NaeoLFFgNC5SNR0dHMJ/PVQczB80omc/n5YsxEq3YcW+ZL5rCFuJ9GxBj9CPwAEoXQYS7BFQO43EueLw6z2cxDkOOAopHmvc8CMv7T5vnmk6D+fnudEleWYx9lEHWnQdtoN/vw3vvvQfX19fw2WefOfQpn5K+T9sJ6x8ayz7DBtf4PK8f8dqF40uag5wGgH50Ku1Xvm5qODluCtopMZznEG4JcJxjXV68eAEAAFdXV6WeHMK5C7mU5zlMJhMAqMZ7mqZweXkJP/nJT8o1a7lc1vShrhwOeZ6XjuhHjx7V1rYuAHVXOkf6/T4cHBxAkiSlbkrHPT2ieDabwaeffgqLxQKyLFN1fdoe3EayrNFctkpzwVdHTc/S9HXtPr4YZ7CGl5+aENLpqC5SyaAcsqwH/X5xVyPuhsWjQesBWndXLNQCnLpeWyWHA5ycnvvhwQ5QedBpus/l7+Y7YqU0f3rC8tTTpedYNzk9BBYbt06Hluc0fcMW81Z5sLB29K1cJkm4U9jNg21cjXspcJkAiEcPu2sxTdfKyGu3m5fXiZaTeKJ1kPmn9S7KVWMrL/Hhbl6arrWBzBuWw7zVkcX0ztii/jT4WtGu5mgVbE1TgM2myFPcGYv1KHbOAgD0eikAbGA4LO6OjQFcS/GUmOVyCU+ePIFnz57BD37wA3j06BGs12s4PT1lY6l62ch3xQeF+XwOV1dX5ctgdB3iOxkpaDqlRlPiE+W4tP6ijmR1/EvQhV56W6Ctr7dtj0r2lfR8WyDZaz6I4UfKy8dtE1zoQ9dw+3xpOEfa6lxdgc82a6PzW8pKeajdIdmmEp/as9ix1RR2OYcttJrIVfp7122FPnStbpJ/ggP3lVh8tgCeYKxlkQoJmNuEmMnry9t0sb5LINW/qXOoiVCXDGUEDMClaVremwTgBmzX6zUsFouacU35sfRfl05yX12155Z+kPDF8G0dx5JzRVpUNIUpZv75lDBre/A2kNo5SYq7wQCg3CGFY6oLh69Gk6dZlEwMPmvjmAdeab4mi1hovDbB1WY8NukPia62Rvn6SoKYeWmVc768FqD10OZQ6A0vqc2keYjKqGUc++oU294hOeIzOqwyNsSnj5ZP2eLtKPU9bzM6Rm5L5+gq2KHBYDBwHCT8qoGmsouPGUkf8JX30fatKxr46EoOpBi8bSFJijvCrC9pSNB0fHbVhm0hth01HZUDH3eazI89htrKL5XT0+kUzs/Py2NupUCwRMNS1yZyiublR/SiLLi6uiqP8N2GLOJjKkmSMiiLtoaU16Iz4J2z9GhhnN+0bUMvMGC9N5tNef+rVQdB/ACu80DSESS5Jun+HD/nJ2TXaPxadUUuo3lb0rrG2B1YX/ydZSkcHmZwdJSVxxInN4ES+qnjcp/RPPy3XF7rS1ouEWjwMVHHj+U03JyOmyYFXkVWBdwJ+e1rkyQiXTvWuE6vOXAE9TFNadSHPD6kckLL6+J0nycljvozKw4xF2h1ykmwEvFYcfp2uCIO+ZvvFA3RcvO58lyuWxPQeOR5EORnbvC1kDUVXoqjGO/VMcaVTKG2QSULivtjExiP+7BYbGC5tL2oTmU1rnd453aapvD111/DYrGA09NTuLq6cq59ouuDRY/gRzlKdg6FkL7clS6I9dD0cF+5NwUsfiiaHmObSHlDvrtQGl3zrWVCPFj8cxINzZZvYrvG+iM03Tzk67Lg3NfxHTsvQz7AEH6us1vx0Hx3qX2bQht52bRNurQHtX7nfEn+PB8vTXgM7ozVHI2aI2GbzpNtwrYdkLcBMUERCtvoQx8+6b5MzH9xcQE//vGP4enTp/D++++XZRaLRakgTqfT8s1/LCvtuN0HQEe0tENonwEVV+18dK2MtKhRfFIZ3/8QJEl1XJnGX5ZlcP/+fRgMBjAcDuH169dwdnZW5s+yDNI0VY8g8IHF8USdsVr7DYdDGAwGMJ1OAcA9HkhrE/oWKPZVqP0sd99tA5oGXBBCzuKmyhvvo5AMRYNWw4njXFov28x/ipfyTu/CofxZjt2geC2g6QZNnPNt5XRo/d71OiD1szTHLIpdLN19W/PyvHgTPU1TGI/H8OzZMzg5OYGjoyPYbDbwf/7P/4HZbFZeR2CVu1o96c4oHviiPNFv/O0bj4hDG+M+B1MTJ0UocNYUpGNgKS8YBOJ8U2cIzSNBrMHi27W8bZuCj5EuaNEgj3asXwxv1sAXzdPv9+Hy8hKurq6ce9G6bEvet9KY1fQ8vus6SYqTTOjuXTrW6P+ugN6ris5ofEEvTVPo9Xrl0clUV6J1ojtDT09P4fT0tDVPAFDWe7lclrwghOYE1bu16wXotzb3JLwIljlO5wEPAtBy1j7lNNGeQt2Hr7F8zGvzCHl8990D+H//3w/h4KB/c1esuzO2qEcVEMHvG4wqDSwjpetA80j3vdZ5kdIrmjwAxIO7nCe3LAiBWSkN84bTOH4prd5OdQea8y/Qpj7Q5hMd8/V8Er08d8uB8ShjfF49wwLuTlBf/iKfGwim5Yr/iYpTK1ek+e9PpWnFt5S/vrO1TrvKU+DmwdA6/zQvkB2x7tzPa3mqOlTPsZzLa7XrFW52wxa4ERely+uGxw/jbyD1wzHr4qBzOE2hvEe2uDu2OKr43r0BfPe7J/Dy5Qw+/fRabkzSppINOJvN4Pz8HP7kT/4E1us1PH/+vJSlSZLAaDQqd9GG5DS1M+lJL/y4Y4rH53uIsZc5b9w2pmteGxv3LvnttgGWIJUlCEP/+/pE8pNhGf4shi7mt/oNQv1usb0tPk+JNgAE/X+xsG9+gn0BzS/rrmXN227b9uy2oW3dKXRtjzbhAdN8p/1SHtEe7BqcYGzIUcQFkyQgOa6Q4yCmI5oOAp+h2BTHLpxDMfX18WNZJLS+beIstpT1BUPQAYJOD3zDPMuyMg8a9HjnxeHhYenI0cYxN8pjFlorWBTGJCkChjwvdVLQ/BZ6FgGvKbZtHFwxY0tq7xieLc9o8IkDHrNKd1hTR9vV1VXNEWjhcZuAPKBxhHMAAGA8HsP9+/fh+vq6NJRCSrIG2ny8DZDGpk8mxio3bdYkq0yT1kbfYl8Z/bZ213jkznDqaJT4igFu2IaCTD48sQGJtn28zfHsmzuU7yZO7iayh9MJze1dyrckKQIu0+kUTk5OYDQalcflnp6eRr0UEtJXfXNEyq/pLdr6qM0rn75pMf75WKd8hZ5rdGkdUEfK8xxms1nj9V+rc+ioYV9gRPvfBny4YvRbqy6GVw3g8XzamoVlNBnFdVXOq8XxZD1OmeIM2Ut0zEuyWZI3XAbyNC0fr3+X4wJpbTYbmE6npd44n8/L5yFnAZ07PvlBaaLd4jtpgtZZs6V9817i2zd+eHtb9XJp3mrrdZt1RuMJ6yntspbqo/GQZSm8884BvPvuARwdZZBlKSRJFeCpcCHu6ndFj+ZvK8N4u9a/OW88nZd1cfKgZeLk5d8ql2I+CVci5OM4EpZW57FOT8dr4Z/lBgAMomkOcprPJxeA4MIyXC7GBUJDZXz5fP/l3y6/bp5YXdzPc/HctrPWgs/CQ0x9fM/r/QwATuDVzcPrUclLylfR9oVMce+Lpd9pWrwoEnNcMV3zaHBns9nA5eUlzOdzuL6+Ln1u+HJSSN/kLyqiX4XeV8/LIw5pLZHWRUlf0ECzdy3+spD+c1ehKf+SPqe1a2ybamDRRdvq6xSXVk6z5a0Qw4OkR2o6T8hutdK32MvfBNDsz1i/qNV3eJdgW7a5pT0kOaON8xi+fDY554/TjPVdWiG4M9bnKMjzXHQwt2HotqBLI78ptHE2tBGqFppd8GV5niQJDIdDWK/XMJvNynRpZ9dms4HFYgGDwQA+/PBDuLi4KO8/AyicYrQMKpj7ADQgiLBarcRjA63joomyEAtNlFj+XHPSdDEH0cjo9/vOTgKAYjzcv38fRqMRABRvhI5GI3j06BHkeQ5//dd/Xd6xBgDqTgiJdw3a9gneE/v69WsYjUbw+PHj8tnTp0/h8ePH8Pd///fw6tWrGl1+1LHEt1Xp6NoRGgtW5zP9Haobd3ZSA1VbmCW6mtOUlqOyiDrK2+5I5nzRoxFDvFsB65gk1R13uOt6W+sCxx3n/Kn6l9/JZ9FX2oIvGMFBcxognhjZ2KUc3QagTPr6669hvV7D/fv34d69e/D7v//7cH5+Dv/lv/wX8Y5IDWj7SHeaSu2Bx9PjeKbzUgsihHjgdZT+cxnBgRoTmvOqTb9SXZ2eAgEA5vbmwA0gzcDxOQt8ZUOBsLbQxMjyBXhQnh8fH8Px8TG8fPkSJpMJDIdDSNO03B2i6RU4/ix61LZOtND6gf/n7RCaN3T3qNSGNB/SwDEbcsw1ASoj1us1fPnll+U8wFNr8Bkvg4B9ReuGdooWBEcnNR5jTIOyXclv5NvqxNacG03nnm8HtuTU57zF9DXfdUXnRkg20Wf37g3gX/yLD+DoKIPBIC0DHdWOWGA7Y7X7Xbn8x+cJyZs45ar5ALVvSgvLyjjquF0eaVtwWv4dsi7dej7ellJZf30l3V3mm+OCWsAW2PM4KMZeRYc/c2mE9VPMkpcBTgAe5Kye28omST1/Pc0f3MQ8IByDXP0OH1fspiUOfxSPFtwWuaqVl+nRfMDuwgWQg61Fn9WPRK5w1XmtaNTTKA0gd8fSeuOzIr+7QxbILthqHuaQpglsNni/akEnTfObb9wVWBxTPBj0oN+3+WHxRBS+UzXPc3jx4gVcX1/DaDQq70/fbDbly0mabpAkSXltUp7n5S5apMH9cFjOt77wuyxjdUcA/0kniNtqF25bF91n0NZsDvwl11g9hs/TLm0Crl/RcRUTxGzq/7TyTMdhyD8kzY2Y4OEu/MVt4Tb8jlbfX2yeNwGsfSHZStZy++a/yvO8tl4C1OvUlm91Z6wVsTZwqaDwKa0xAiGWv5Bx26bxunR2Szgt+ax0rUaphD804LTAmo+WNCboMWASXF9fw9dffw337t2D0WhU5qWKXr/vvltAdz1Sp4m0AEvOtS6EAlcqpIAw3tmB39yRYu0zySkWIwQtZUIKh9VxLM17zXnTtB/QSEjTtAx0f/rpp3B8fAyPHz92FH5KB8cMjid8zhUjn5NSqqNP1lGHLublONfrNUyn0xqd9Xrt8MjvPJN41eogpTdRrENgNbJC7clBW3+k+Y5OU22nnMSvlWfucLYYfV2tR8ijr314mZhxqsnQEE8xECu3pPFE57avbAw9K08YSJDwS2sqdw5YaPD5sa1jbbsC6gzCuxCvrq5gPp/DarWCLMuc+WoZX5Iuoa3teZ5Dv9+Hg4OD8r+0e1EbSxxnbN0t+ppl3YzV/6wQY8jz5z55Q8c0D7r4HCSh+bst4LTpmLS0Pe4m+eCDDyDPc3j58mXp0OR0ANo5RTR+JHluWRMsuH10LHxqukWIt64Mdc5DnucwnU5LXtbrdfki33q9htFoBFmWwWQyEY9qlPD56OKLpnzXEG8H1OUODg7Ko8WbvEzE7Rqk4bPdfWNd09O39aJTyJfA50+TcZIkxe7Yfr/YEVvdF+sGKqVy0oflqpVpBm5AlP6mz5My0CPzGs+PPxDL01x8iSePvKbiM52WvmPWxReo1g3oMswNZlbPpLtD/bISi+aeoKwmZt1nCUDDgCx/XvxPHD6sINtUMfjcfFjWfxRxVcZCy21zmQcaOK0Hr3X8yJO2dhdyA+VuvRytGx8PdAi6870K1hb2awKbTfHSyHjcg8ePhzCZrGAyCR+hiKfP5XkO19fXpS5MQbvGQqtvUdccRqMR3L9/v3x2eXnpnDiG+XhZDm11Ih8OyXbQQDoetq0e0pUOH9LdrH6WXQH3i4R4sfAWslu6GEcUl69Nu2xLi99Nm59SOV+a5icLld82xOtx/r73tYH2DF80Aahe5vbRsNr6+w6+udiF/IqZP9v0dUjzxWrPUbD6iDUI7oz1QcxE4UJYWtjehAG8D9BEaHKnNv5u0iexxjB1SHAHNtLHu5i+853vwGg0giSp7gbFPHwnJDowhsPhjfJa4N7lDll+19NsNqudN97v9537qvCt+abQxFGM5azOYC1oYOl7afELOfxiFSzMj0fsoAPsRz/6ETx+/BiOjo5UHLhzajAYOItwG4c6VZJ8ODBQId1bsFqt4OLiAmazWblrPM9zWCwWZWARoB7wCznVYtt+F0DnK+eH/7fwJzn66Y4Wno/fUddELtIy9Eh1Ov4prhhHeSxYDGHOh5TPMg+6NkgsbeCbW03XsTagBT+0tqHjTNONpOcS3duYrxbAl6Nwbp2fnwMAwMnJCSwWi3Kdxny+ezmwjjFv3K/XaxiPx/D06dPy7f3T01NYLBaOwRWCmPZFfiwvfNDntA6SLLKsf1oevrZI98NaQXbKumn4shDfvUzrxnU3xIN3t/tkpBVinGlS4McqR/r9PoxGI/jkk0/g6OgI/vAP/1A88QT52Kb9E3IcWeQFXyelORZTB0nfDK1D2okVscB5R9zokAYA56XI1WoFw+EQTk5OYD6fw3w+L1/Sk9pBS6Pfq9UKrq/l+/3o/M7z4uWRk5MTmEwmcHl5WfLXpM6cL7qDlMsAbaxrdZPqwXfIWmSWr38pTxb/Qawc6/VS6Pe1u2HDd8UCCS5inuq/G2Sh6dq3S4s+qzvFOH6ev3rGA7pVeYk+TZPbrc5HnUcpTz3Y6qbzALC8IxiEQGyciMhLnDy9CA7S/5ymFGzzy0HEl7PgYv1ZvRylHxOQ9QV96X/5d+zuWDfN/63v3uX5LbSLts/FttXK+9JlXpDnCj/vN6mvkL9Kj89ruCtcuHvWnfvFjlmUrcULI3in9fFxBh99dAiffz6FyWSq1iXP89IntVqtYLVawdnZWe20FPSZuO0g25A0bbPZwNHREXz44Ydl+k9+8hOYTqflyRNcdvvmTIzOxvPFrDc+h/s++qW34Rdok6cJPe772JavOcZWalLXbdjbVBeV0vlv5APnt8aj1M6+ObaPY78N+PwvEqRpWsYbAKCUmSHw6cV3Aax+qxgc24AYGtLYpvZE2zuZ2/rexJ2xmrFHHYK8jGZUa5M7tLC/hebQxqmvOVlCtLjSEjKqJVroEEBnYZZlpWMW806n09KJy+n0ej344IMP4PLy0snjo+mD2LbAPNKOKHokGYfNZlM7kpneDUrfUGwTBLH0j5bHIpxDbcvnvu84M422NRjBd+NoNKgTWKqTpZ18vHBckpPXB5w+jqPValXWC4PHCNSI0uZniA6vx7YW1hgDizvE6TdvU5QjfIxJ6xSnEcoXUwfqXKW8YTq+ocwNX9/aaeWrjVLB6XAjwGo8xY4b3o+W3Z6UH0lOaX3atd4h4dXkJq0b1btwXGh3/0p8h+Yu/W46xrsASjdJEri8vITVagW9Xq8M0uZ58YZ+7FriA7yffblcwmAwgAcPHsBqtYL1eg2TyQQmk0m5bktHbVFFHaDqJ5yzdE7z+c2BH51G8fuOVYtJt4xza5oGiFva9SrRjpFXtN1xvQPo/ohxLptpupRPykPrib/n8zlcXl7Cq1evYD6fly/gSW2i8cCBHn2rjS3OX2hcSOArQ8c3zxsjQ6zyuCl+K/C+ox8K6LyW9FBpjZZkLC0XqstgMCh1vPF4DB999BF8+eWXcH5+Xl6/QW2iNsBlG62b9D80ny1rIID/CEkfcN1e44nPE2ls5XkOWZbCb/3WI3jyZATDYa88lrgKyEpBWD4vqoBJnV+enpTp9D8rpaZhOZcH97luO7nlJV5BuUvWpc158em/Lk5srzoeKQhbT+PPJVo6SGNNCgjSQCyOeykwi/xjHspLQvIJVBMgOJoHDS1pGj6So8aDpZy8xmhlqnbGPPzbxxttr9Dxy9hPmIf/d9PculdjiNo7VN5rQVedLuKk5Xl9iv+0jar0uvzJnf/VccUp9Hq+uejKQnr6F80TuuMVoHp5Dv0Q+LIZzXt+fg7n5+fli0fj8RiSJIHJZFLa5zH2P6VPgdv3FvuI33Er+Qi6tg85z9Z0ri/eFkh6AELI7qH5pP+8HPeXdMW3L4/Fz8DL8N8aHWu/aXoXPov1F2I5X/2kdo/le1/A0v7cB4OA/kKpzlo7+K6wk3ij9KTrgbYpc9qA5GP1QVvfVRNfRJOyXUEXPAR3xloFFJ3s3Gi3wrYm/q6MeqTV1WJuNXy7At9EiXGqNS2DYwYFIr6dTh1ZeJwY4qVBy+FwCO+88w4AQC0YS+tkdSQ0qXOSJDWBnud5+Za9BPReDgAoj0VDsDindw3SrkKrAx3ApiD5INRX2OYaH3ScYZkmOw6a8t0kPzqnAQAWi0WpCPBFne5EsvC4bbm7TZkRCz5Fi4/PLgxFCahy3O/3y8BQjNzZppKsOTclHkJK2rb70we+dX8b8rSJIcaNTk0GYVvzu5Q0/D5j38JPkznr44cC1nEymcBsNiuPMEN5hnLb91Igp6WtJ0lSvfAAAJBlGdy7d68Mdnz55ZdlPj6PaRo1uvAUC+7EsayHMY4K6ZlEy+dEkpwc0vMmxk/ICcbza3XC39yG4LujuwzG0jr45pImNyifHBaLBVxdXcHZ2RksFgtYLBZiMFYr3wR8Tkhtzmtp0joUsx5K9CzjC/NQvUVq7y5kNx/31DkrjQGq0/P2kPRaTUZJDjY+/rIsK/WB4XAI77//PkwmE4cXixPI6jzxyQ8pH6+rlsc3BiQZ4uNPajvpP6VD11Nax6rtcuj3E/jBDx7A48djGAzQHgD2kY//laYD5tfAOt05XUqf4qA81dMpLokGL8cz1evqK8P5q/+n+F35SdvNnS8yDnfO6Pzy+tTHWg5uvXkgNoE8B3ADrvwI2gRA3KkqH1VLeazKIz3+LFxGy8v5wDw8P/0v47IFQKvniakuMg63rI8Oza/xXeGq8knjQOdR4qfaHVv0L+KjNOr4kF8ecKXP6Zih84h+ijtjcS3Ib+63TiHLemIwVrMP0P7kIK0t/D+uh/iyHMpaPPkpTVO4vLyEL774ouRhOBxCmqbOWma1nX35+Hpq0a+QR1rGshZt21fiSw/ZbgD+NbcthNrex4eW3+o/4Lit6SF9UdL1tHpqOp0EIZtMA9848NmTMfr1NxWkFwAtvgut7zW7XwP03yBQv63Fjr5tsPIV649qCzHtL82XtjxYbeMQHTEYaxH6nJk2YDUa28IuHMK34XS2QJNJjkKI94+ljtqxfJay1AFHDWqAIhi7WCzg6OgIsiwrdwXisSt4Bx3nhdLFnTK++/lilSCad71eQ5ZlMB6PYblc1o6oy7IMvv/978N4PC75o3V+/vw5vHz5slReKdyGsI5RcJtCSNm2KjD427ebZrFYwNdff13ixN0GJycnkGUZfPnll+KF3btsd1878GfSUY90fO/zAm8FukMKoF5X3/j0HXHIjTKch7FjmS7I6Ezm9/cijSRxj8DG+uFzyneME3yX0FQ+NqVlMTp8fISMp9sA6WQA7G90xoeCQRRwXaN5JYfHtvurCeR5Xr6UYA26SXqjpmzTdQGPdZfeTJVocNxYFk+6sBh0ktFPgepakt7Fy9Jxsmuw1FcDrB8PrqBzD48lXi6X8PjxYzg5OSmPhnr9+rX6Ytqu9BNOkxvkqIdOJhN4/vw59Ho98b5YDehRfnQNp2tYr9dz7nzrGmi/+JxjPJ9FzwitZ3RsS/RxjEj3vVuA05fmNq1PmqYwn8/h4uLCkU20730OuSaON6qT4Msjo9GofG6Rj7wv6K5/2mcx4yfk7PTJYA4+h2eMzkPlicQTnSNUB+j1UvjhDx/CO++M4d69AfR60o7YehAW/yO56pvLd7fcTaqpPnV8SflN6dEsEh88j1uHhJWrf/M0iX9feeTXpZXUnsl59fR6Pd266YDHy9b/h47iBSjSCvwYgCvKFc8qPJj3BpuSTnkPBV7D6TytnicBf4C0eq7Rlcr6ZH6IhxB+Hx8uD3o7AbSvS8VPfXcsp1XQw3pWu1uRD7luVfBfnj/8k5fHFOd5chOMTcoj1q2gyX+f3OYbDQ4ODuDevXvw+7//++XO1/v378O7774L//W//ld4/vy5s7lgs9lAv983nXiEoPkPpbUohFMLaCVJUtPn98FnEus7lcCnZ3QBTdqpSRmLniW1l/Ydw0NMIIqfFtRUB6R5uhqLEj+aj+FNAs0nSEE6gjhJEscXrPmRHj58WOrom80GptNp2Y7z+bw8DcAHb2K7bwO68md31d6avycWvxOM5QJAI9LU+JTo8PJvEjQR+vsIUkAWwdLfPuedRRmkDpDlcgnL5RLG43EZkEHnBQpT6YJt6syy3EXpq4dF4UuS6phlnh+PU7537x4AFIsAvQP09PS0XIB9Tp/YwITfcLKPVY2fkKIs4eC028oWjl8qt16v4eLiokwfDocwGAxKo+HFixel01dzXMU6mhCkdtbaTTMctHy+eerjRYJdy6yQHOBzWKsrH+N8/viciTxAQJ9r41kzCH19Qh29AJWBq7VBV0aTxG/Tftac8k14aFs/q5yjfWOV5bFgldWh8dMEYuZ+LGjj0DdvQ0Yfz08D0BYZyvnic5nmoc755XIJWZaZ74mtO7iL/9rJAzFzVpqPlr5rM05C9CkNa7kY8DlJMBi72Wzg4OAAHj16VL7QdnFxUR6Lp/EjrQ2++jSZM74+Q2dMnuflHZ8abSzLxxS2gYYf8/h4a+LE43qY5JTka6VE24fbClo/Il2svxaMtYwBn+yl9Vyv1zCfz6McyCF6WltJ42EwGJQ7mJrMeUk+ctvCgiPUpiG551uXKU9N25jLf6mO2LZJksD77x/CRx8dwWjUu7mLUfog7upT4ZK/ZR71cj58Ba912rRMlZ445VlOFQd97qtHnb/E81/eweu2qRSY1Z+5NFxaPr7dOvp3eAJgsKwIpuHvahdkFWxLEv4bebHsVmWclbgAIGpXaeLkbwd1XJS+tU60XZrQlPHZ6IK4+1nCU9GV8cfuBvY9d+uI7UMDtfVnKE9lvJUcq/7jyyS9XgKbTcV7yK8SC/QUidFoBA8ePIAf/vCHcHR0BAAA9+7dg6dPn8Kf/dmfAUDxsvtgMAAAMF3H4QO+Plv0Vcu6K639IVxYLtaejbWX2+j5sb4oX3oIuGzmPIR0YMwj9S2l0URPp+u+hltL9+ks2wKqg1vox+jieR4+gatrH02XYOHN6qug9fW93Jrnefky5mAwcMZ0r9eDg4MDODw8BIDKj8H9GruAEJ2Q/i7hie37bdZVkzHWcl34Uy3jrUkbBI8ptoJUSd9bxLsanG2ha2f4rqCNosPL72Lh4f+504dPQhpMS5Kkdqwv3TmAx91hHt/xtT4em7QDOlLwWESA4m33H/7wh/D06VMAAHj16hX8zd/8Tbkz6Nvf/ja8++678OMf/xhOT09LBZZeJI7HHms87WLMNm2TNgGgpgtDv98vy69WK/j888/LPE+ePIFHjx7Vyk2n0/LondgdGFbQlAbeRtQAQqC7QyV8bZRs7tjqCix9SBVF3DXO2x+PfdTucqOOPboTHmUJPwJTgjbykAZYqXGBb9mtVivIsqxU4EKwT+vlNnjpcp3VlKVdrWMYLGii61iNQf6cj9UmSnmXwRQL4Np8dXUVpB/C4+OP7qqfTqfwxRdfwIMHD+DBgwcODknvQLlB881mM0iSYnd7r9dTT1HQ2lhT3H0yJhTIiAUqD607kXkgp+48T5znEj16NQCVj3RnLOIZjUblztjlcglffvlleTy/xh/SldbGEPjasuv1n9cb/+MR2EmSOCem8HEjXYXRlVxGfrhjTNLDYx2Lu4Jd6L/SqReUPm0vLkN4e9JyVN9Zr9cwnU5rMobOHwk0WaE5j7tw+CJu3/UYkrNPGsc+uybkDKHORm3eJglAv59ClqXQ66U3O81Cd8VyRxD/pkHEIp2yWme7CiImiZvfn1ffsSvxwZ8BC17W21NvX16m/t/l0+UlYfwnN2nSLlk3QCvJnYq+y4sMNLDmBqrcNByb2h2fRR5wdsZKu2T5cbXIY07SeBvb0920ih7NV//PXzSNCbQmEAoS+9LivulRvm57Vm1JA6kubyL3LJ/bN24bYR6dH/cb2wfHDT+6uDp2uMBN8VH+6BipZA+d88WO2M2m+p3nKLcAer0Enj0bwcFBDz777BrOzpaEbjvgVzoBFDrW97//ffjoo4/gk08+KW3Z0WgER0dHcHx8DMPhsKaLcV3bpz/QNVbSL0N1C9HYF9+uz2/jy9/EzouB2KAcHyNagMeHs4lO2UU9+f2hEl8+Oj69hufbJki2mG++SLbamwToZ6CnJR4fH5eyBa+VkWAwGMDv/u7vwmazgf/9v/83rFYr56Sjx48fw71798oA7WQycV4ql16c7fV6Ufdl3za08c11YZd22U6xdgWCbkd0x1swGCs5+jRGYp15tzHpYwdWk8bmNNrW3aJ4tGnLmH70lbMuPqGgJ1/AqXMI02gghT+nQRdq3Gt8WBd/CY9FOcFj5Xq9Xsk37ry8f/8+rFYrODo6KoOxR0dHkOc5/PznP68547iTocsghlYfn+Jnpc/HcBvHe9My9OUQuqDO5/PyPxW69IhAi1LJn1n7pYki3qbfm/BF/2vjgJcJOc0swJ3WroPHfzc5bSOJd8l5quVtAhp/eZ6rL1JQ2l3MOw1/2zxNwTd2fHmsEDvmrEZ9Ez586yp1xlOHQdO2aKv07kIX02Rj7NHgmoPAEpjYbDblPbX86HNfWT4fuWyxjOsuIHatCMEu+p2Pcasey9tZG+N0Lml0uwDf2i7Ndd5XFv2YBx+kPPR/rB1h0VebOvja2DS8r0PlujDykb7UtlxmcDnh6++2ehmlt1qt4PLystRPu3JuaPpayHHHeaUQ6r9YvZXbZ1L7+tZLtL3oszzPYTzuw3jchyxLb4KvOO+wnBscYbUo89BvH8QOh6qe7ifEi0RLqoeU3/8dqkAdv6unF3mqdk7KclX96gFYYEFZCR+QADbFWQANeNEAWD24Vvyu7outnrn+B/ebBwrrv7E9KB/g2TnLy3pbXcHhy+Mr4+fJRyPmDlYRA4TqS/FZ+Kn63rIb2rZjWisLniB19b8+HqpxrZfH+mCA1g30V3MjTYtxORwW8q7ft71gZ67ljSzla+Hx8TGcnJzA4eEhjEaj0q5Fnwlf32Psm5j1tK2fVuPPgiMGQnWK9R9ZfLeSPqnhCLVByMbapu/AR78tPsnWoBDj47SU4+3cxC8m0aF9LfkbfP+7gja+SR9OH4TmEz9+HAOi+IIw7twHqIKlAMWpiQ8ePIDlclm2LZ5Sk+c5ZFkGw+Gw/E83eeB/qa8sPsaQzzJU9xDElGs6H7qy0UL8dAG+MaT5FS3y1wrRO2Oxga1v0+8bdCkkfJ10G9BkwsTg5Ya7RKvJYAy9xczvTkSB6Fs4ccecZcFp4kRCOr58dHwcHR3BwcEBnJ2dwdXVFfyH//Af4OOPP4Z/82/+DTx8+BD+yT/5J9Dv98vdu6vVCv72b/8Wnj9/3hlPbyr4xp6mYOFu1/V6Da9evYKzszNnnNPFGdOlOx7bApchPsc+rwf/5uO967FgCXrQtmpDg8Px8TEcHByUeUajESyXS/j8889L5cd3lx51ovAXObo09hA/4uV80PtkMU+v1yvHW57nsFgstqos7xLoGI01aNrU2WfsUMfCNtoVxwAehYtvZUo7sQtHSvWSSOgoTL4OYjmAuN17kiOC4twFIC08taBr2cpp4a5DAIDr6+vy+FHaDlZZQPPhyRf8aFmfEdG2nbsIyGC7NynbtJzm/KKGLLbj2dmZs0t0MpmU9/1SPJvNprxqAPW+6XTqGMVtockcQT41g5wDzasd2U3Xryb3pd4GoIzj85vakpLTRAJ6ZzC+dd7EqSDpTHlenVyBaWmawmKxKOXELtpwsVgAQCFXzs7O4I/+6I9gPp+X85XfYdVmDsfUJ0YH8jkCfbg0B0dMPfO8eNmt3+/D/fv3y/F3fX0NFxcX8Hu/9xi+972HcHJSvyu22hlLg4TA0gBo0K9Ko/UwsUry8aBjkYbPKjry7lk3Xd6dW8dPaUANr/yt4UvEtOp/dZ8l9rHbtu4H89ITgbjuQ+vrh2rXKwCOT0x304r/uDZhkM43/nIAkIOzmI7tUhWv8uhDOin5rZfX81vz6Xh9eGKP7pVo+XeZamXrfLm8hOtd5QMAsVyoLdxveXcrxcXx8TEBLEBL87kyJyf/c0jTBDYbgDQt6OPdsXhv7HqdqnfH0rEcI/vRRp1Op+X6BODqILPZrDxxLEkS+PLLL2E+n5cvxCyXS3V937UfFW10qkvtg41N+6Zr+9/S57F0pLU+BpfvuFwL+Nqpqb62Cx0vpp2b6nicjuQT2YaPaRvtF9MGlD49kZLDarWCi4sLODo6gm9/+9tl+tOnT8srBJMkgeFwCOfn5wBQ3JH97rvvlvZ+r9dz5CG1R+bzObx69cprp8XGHrYR44ldC5pArO/Pgq/NeN2mLdeUL/XOWB9yXyW4wyIknHe5AErOFKti4AtAWMo3ydNV23SxQNHyvnRLe/ic5Fo+6b/Uj74825h8Wh/h3XQYIOIOxPPzczg7O4PFYgH9fh96vV55vAtAdYwpBepM5sqjtV+30ffbBOtc9c0VnxMVhTpdsH2BfglP7IKqATeSQuM/lEdqO18fWtqyDWi84X8tGIMOdxrgGgwG0TI01hDxGYihNgr1M8/nOpls8+w2Amix9KjS5FP6aP0tbedzqmvgo99FW8bOn9D6RfF1xZ+kj21zvof4pf2u7T6L4dMiX/GOdrxf3jcm+JGbMcZrjNPJEnCw6tRWiMVD6UtroFXP48+5jMA0NKTxDWZJ58G5gdcJ4NqNd3zuwtBsClqbIN+aLYUvbGAeOoe2yWto/FkMZKsOo9EbDAaQpml5ygwGZ2PqblmPpDLIE+crdtz70iktfEELjz5rOv+1eobs3BjdScIRMx5ou3IZy/Fo7SDxSAP2+Hw47MHxcQb9Pg+8In732w+6fRkzHet5EyVdym+x+6zPeEZt/NafJQnXY/E/DxS7wVi0ad0joqUPxWcNxla7XQHcwGq1A5UHDKuyWEc+pvVvAHdna/XbxS3R42kUT5Pyeh4LhMrR5765Hku/y/z1Z26bVnm0dPe3RaZJ7Q8gp1W0AcDZVQ01fmg5+r8Y/1XANk0BRqMeHBz0YD7fAFWrY3UFyX+W5zkcHh7CwcEB5HlenjIDUPitzs7O4Ouvv4YXL16U+gz9hPAjDdTreH5Nh4jVf6geEKtHS2tQF/pXrB4eg5e3tVa+jc/LcjkzDwABAABJREFU2i5cx5NwavYqzxNqD8m+oL95/zfpx65s6Bj7sgsIy7L9tJ18wPsS/eh5njsnU9IXqLMsK68ueuedd8pg7Gq1gk8//RQWiwWMx2PIsqzcyENfoscX7qmNimld2KBt/I0A8b7PJtCUB4vPwydXtum/avOcQ6gPGt0Zu00hse0Gluh1vaDuC+AiQ+tnfZN81/2ANClv0j1LvrLSoiope9YdBaGF0Sdkl8slnJ+fOxd7c1iv13B+fl6+WTMYDOD4+BgAikWAHrGVpikcHR2VaVdXVzCdTkVe30IYsO/wyAmA+tzg42RbcyG0G47StjjxtP+7ghgeQ3mw3peXl3B9fQ1Pnz6F4XAI4/E4uPuWOqvpR5KLWjChqWMVjVBKkwLd8YX/Q3Tos13K5bYgrbHWdo2pJ18D6s7R3cwF6qDz5eE7Yps4BShQfNzQlIxWqa34KRRtgbaD1J+SkdyUhgXHcrksAzhYDtdUqc2QbwnQAcXzU7584zzk9PDJ/LbzHw1IOg7b4OIOD81o4voY0qa7R9M0LV9Qs9LH+7cxWIc7Y2NliBWs6xhApYdaHJF0TNF0Wgd6RYWVl6aAeDEASh0alAe6g83n5JNkDv2vyW98C/3JkycwHo/h/v378OrVK/jFL35hklea3KHyjstk2gY4FjXnn2Z/NIVerwfHx8ewXC7h8vISAMB5McTi+NBsFok/i+OhTX0sc9DiWOXPcN2MkWG4g6zYFcuDgFWAg/8ueJS/qzqUv3jt1HJ+3DJPFJ+Ly79j18WfsDReT/l//VlCeOI7iaWAagpJ4t5DWcy9+jws8lRlCp4Th6Z/WNJdsPiNH4A8r2Sp9sE6SvoZXfuq7yq4hzxWeOqB1irN7SOeT4OYvBI9+p/yrtGwHukr1evmSSM+/c+q9nbr4nsm1xMhz3UeML0a93QnNe17rW/d/ufyoP4pcGP7u/Mpd3b29/spPH06gqOjPnz66TVMJvUTeWIB130c57/2a78Gjx8/huVyCWdnZ3B9fV2e3PDXf/3X8J/+03+qBT5iT/Io2sK+5ljWKL7GdKUbxvDp8xfQ/9vSXTVo0h40v1VfB3D7gaaFdBYtn5Vvye9D213i6y75WihI44zqSV2O/9sAyzwaj8cwHo/LZ2dnZ+WOWA4//OEP4V/8i38Bjx8/Lv3wp6en8O/+3b+DV69ewXvvvafygqc2UViv145/GXloAne1n5raDF36oNqA1u4og6Q1rSnfjYKxALKTjz/3pWlO6tuGLviRFo8mIDlPffg0J7C0+PgGu+QkCkEXQibkPLcY4dIY48CdEF31lwTcMU4B76/r9XqQZRnMZjN4/fo1HB4eQpZl8Du/8zswGo3gz//8z2E6ncJkMil3BaZpCsPhsLxnNjQ2dg3b4MPn8ArJEys/dM5Z7+W1OJAsss7iHKMyN6Swh+Z3aKHx8RkKJPjw82fa+kEdNQjL5bI8xrff78PDhw9hNpuVDkvEJ/FCHcvYtzGLfsi5zvFJzlr+nwbhNFnlG3/7Mt+tYHXKYl7LWqc5yPizmPnXFrhc8q1DmqMvhNsn80Nz3Kef+eax1YAOQdfrVVP9wyK7eT7JmRMztpr0lwRN2xDrgDvvUJY1GYMSfW288PHO83KYzWZOfl/QBd9ypkeCN3H+aeBbo6R8lOfQfJXawyf76LqxC9D41fpQGsu87klSvbGO96HyQCgv3+v14PHjx3BycgLvvfce9Pt9+PnPf95IhvA5zPuNj1urztOVPEPbQDravk2/41y32uwajph1xQoWB3TIvsP/OPfpyURPnhzAe++l8ODB0An8FeWqoAfBJuLGLFpX24aASydJ6HeYho2mPjYt+H28VGmYKAWN5R2ufAcsBmV5MJaXAeGoY14vF3yBWPykxjGLR+vWZTkdt9U3D/ZVv4tnbpoVqrJx+dz/8XSb82OlpedrWufQMz2/xEs4rY4v9LxOt8hTjROKh46x6uhicP6naZHW76cwGIQ3XsSsV3SeTCaTMqCxXq/h//7f/wubzQZ++ctfwt///d87gQnNab0NaLs2xuKQ9FvL+mXplyZrrKU/JT3cR1fL31QH8Om1VgjZofS5pFtruCT/TRtdyaLzS7xawWeX4Dqq2fr0A2DfqHVb4OMtZLviCyFPnz4tA6Sj0Qjef/99ODo6gocPH8JHH30Eg8Gg/AAUG6QODw/L4CwCnmyJtLWjkEN1aOo/kXC1zdeGblu/u5a3Ce+heRXTbj4Z17X93TgYS40dCzRxMr2FeJCO9QCoC1+L4PU52boEiwOKAzopQkfKSsAnqWWibqMtNpsNXF5elkfsXVxcwMXFBXz44Yfw5MkT+Ff/6l/B+fk5/Nt/+2/hZz/7Gbx+/RoGgwE8ePAA0jSF4+NjuL6+hul0WtYj5u20uwBUUQg5a5oo0z66/G7RtvegWvhDJSrGeGmzoDfBaeExZi6HcNBdN7PZDDabDTx8+BAGgwF8/PHHcH5+DldXVzUeKR7+DAOyeMcglwO+RVi6C5D2sXYXoyR3lsulaNBxnFo97grQ8USNBWsZno7Agx10pzFf+zhYgghtgBpE+J+OF63fNVwcLO1JHThWvLTcNnUxiyNgW8DfEJYg1CfaHcCIs6v5ymWMJKticSdJUp7YgS904V2UoT7AuSXxQeegpIv49Cje/1dXV3B+fl6m0WsaaFmUxXg0cZIk5Q5o/mZyU9B0Rpx3lG9ph7oEoX7zld2GczMka6X8Ft2dPqMyEe+Ap0F3H2RZBt/+9rfhnXfege9///uQZRn88R//caN5JdlFPlkoyWrJ0UXHQ6z8pDRWqxVMJpNyl3ibtd7nsOP5utKdNboh3iz2GOVd0slRHiRJUh5d3uv14Nd+7RC++90+ZFlK7omF8rv6hHaX8nazrN9yGqdF6+zyQHeA1nfI0nKcHuWP4uB14nzw59UzOpexbGhHLAZYgQRf+XcCSZKKQVkajKXzsT6GacBLC8IC5PmGpW0gz8F5KanuM9HnM7VNq++Kl+J3AlpA1uWb16UqV6+j1E/hOezD4ccfhiblsQz/DuG10LLmAZDz1XkLvazs4irGKN05y+d6fazgeCl+5yQ/zUOfu/deDwYpbDY9aHnoiQovX74sg7HD4RDyPIfLy0v4i7/4i5puIumDVturiW6rrR8xeGL9fVbcvnzbtn8sbenzm9B06Rl/ycvSJtx+jykbakv85nKcQxOfaciHR/NYbW9J92k69rl+xHV1yW5DXxjHdRcgpGvjSUm/+7u/C48ePQIAgGfPnsG//Jf/snYdIAU8jYfD1dUVfPXVV+V/3EC1K+hC3kigyYhYO7Wtb7IL36ZWl7a+aau/KMYOAmh4Z6zluc8xaMG5TYhtpH0Di4OHLgjWfpUMHI5HUpY4xCoyEv8aTom+b9JJ4FvkrAqBVEcp/3K5LJ0qSZKUu2CXyyVcXV3Bn/7pn8J7770HP/zhD53yo9EIAAAWiwUcHBzAaDQqnVYW3vZ9IbUqatLvUN81XajobpOmDu7QM4sSF0svZtGJWTh946hpoME3T2OMKNwFBVD0G3e6S0olbw+qqPv41WQOd8bGKOj4G+9E1JRmqyJvoXfbQNsSZTA33HmwJUaW0Xz09Afetny8cece0vQdK2rlxze+2iiLsTKe49RkpYXnWFoxEKMjxhr7PN3CB28Pigvnv0/PkJ6F2pbrb6H2tOpLKCPxnvoPPvgA0jQtj5r78ssvTQ4A2j94dw6VYRjU9ZXV+OVrmuWlwTRNy2AsvtxiOfo/FriDR3rOZZq2joXk+jYMXAtw+4Gm80AF58mnf9ExjEceHx0dQZ7ncHV15fSX78h+dBqhTmx1gPD5RNMsslRaN5rKJ5/+StsAg7Bt+12qrwZNaGEZbdxbgctS+tIHPpdwc72Cps3ncxiPAT7+eACPHqU3RxO7wcOqHMcLZVqVD5xvG9gdyy5+Sa67tKX/SDPEo78ulJ/68cRVH/AdsfVgLN/5yoOwvR79j4HyehkAeYesDL7dsEVgrNLxcths6sfs57l736ZIJXePIk4SN6Dnjs3mQcqu8pESEBu01X5HUU3CQVeNfp1n+T/tDwkfgA9nrYT63M97xQvPo+sP9TaWntFv/sGXTNK0OIbdInsskOe58/IxzpHZbAbz+Rx+8YtfwGq1gvF4DKvVClarlaq70lOpKH5N99F0oRj7h9t3PjzSOhljT1t1Il8533PN5uB2gxVCOjkFqptR3Zzev8l5sdCP9VNY9Gduw8Xga+M38dl8fGw1uTKP4tR8ZgBF/4zH49oYn81mzhV39GVxXx0k+ruwSUI8ABS8D4fDkh88QW+xWMBms4HValVr+36/DycnJ+UuWDxF5eDgoLQruA5+eXkJz58/h1evXjl80GPYUa/wHWFr9T9oeX06sJRHohvCGzN/JTxN55TVX2O1wyTg86aL+e9bwyxt2fq1cU0YhBxUvsryCsUugLHQZAGLgbaOlRglheOWHEQ+PkOD0tJ/TZyHXEhIeaS00JHLMcpQiL4PtPyomOLb2fj20Wq1guvra/hf/+t/wSeffAKffPKJEzQYDocAUARl0fGEx7nRemn8vwlgcWZZ5pZVsQgJ0DbjgpfltNre26cBda6GnFoSaAaTBlgPqyJiVZJ5Gapc5Xle7kblhh4vF5KhWh9pBhZN58YI5tXGI9YDj9GTHMUaL03abR/AJ/s1BdNqeEt9zd/2DB0tzfmQFCzLvEH6Pgd1k7WJ5tedOnFBQM6zNA6bOD6aQqyjZZu8SOOFylRNt7LWQWrfkLMohEsDnAvz+Rx6vR588MEHkGVZeaTc8+fPnaCqDx81ONEpg8HepveDcZoUJwCo1zFgH6B+hOOXvyzTxbikvzVHD6UvrQPaGqTJxl05PCgtyQbjO8FpHp8ehs9xvvR6Pej3++XO7NPT09J5C+BeI8D5w+fj8RhGo1Ft3bfUUVtLNYcZfW6xuax2mkST/s6yzCnbZE5JToeQ48aHQ/rfBDS9iOtWsWsvP4ViPp/DYJDCt76VwnBYBSoSFsQoytCPZH8CgBPk5PpKPV1il9Om9IHtUPVXV7OP5N++NPdZouCo17fKX82ZIp3vgq12vdaDsD2SzoOx9Bv7N7nZ8VftzHWBzlccO5U+RuV2NRfcF3GLuZZCmkqyJb+hWXwXxRAXAN7tScdsNWewXNF2RXICWuCOtHZZLgwWfKxELa+PnvzMp6vE8CLRoeWb4VIoeHDhuC/6FIDvbMVvhOI/D77LdeD1c+ki0uoo4qrueCQxxUtfgKh2yaKc6wJw3vB7TXHH2cXFBWRZBvfv34fFYgHT6VS9SsFnV2vjx+oz0PQQpKOdpCC9eBvyKfI1LGQrWX2iMWuzD5+v7ZvYWKi7IWRZVurY9OV4X9s1gRAuzVblY7UrPkK6blM8vrIW/JLudHBwAL1er6ZHXl9fl//RN404urI3twVSuyVJAqPRyJnf8/m8DMgCQO2EGQzGoq89y7Jylz/NQ+fK5eUl/PjHPxZ5wPGPfj0rhHTvkDyx6MYx49ICFl+SFdrwbZkzTehb1qEmz310OwvGdlmhpnnfQgHUoYagHcVAnSBWaLKIx5aNcaJJzhjrRNSO3NslTCYT+NnPfuYYtC9fvoTvfve7pTMyTVM4OjoCAHB2yKIz9C20hy4CWH6DtHAqSAs1vQO56SISa6TEQMxCjfUEaOZAlB1hRRo65F++fAm9Xg9Go1H59pt1obQG0/j/UDAN68yd1jTfeDyGPC/eKOaObloH7sjnEKoDleu0P/YFfIEMAJuCJhmtmpJsMT5xbNH1ANdSy/rAn9M3JTmPuGZRh2Ov16uN41hdSaKFz3zzynVM7jYIxMEnP/dFH4wxWK2GUMjJ0xboWEuSxDHCEaiT2oILA2f07WM+Bq3zh5aJ4QXnKdYnTVO4vr6G9XrdidyT+oUGtTh/0n+an8qZWKD0tzEXpLEXWsMtvNC+5PKbtg0vg89x3F5dXcHl5SVMJhNYr9dwcHBQOl1i60lxU554P9O1ytdvriO+nS5JbZrbtk+sQNssBFTPAZCPfKd5rb4E3v6z2QyWy+Jo4l6vOgKX31tasewGUvEjge+ZHxL27ctDgy1VOqft1oHzlbA0fiSxXJbTq+jyY4LrJ4vUd8FWwVYMwuLLQUW/1D/4jK9b9FOvKwau5B2xNKhE5VHxvYEkAdhs8vI7z4ugrOtjkPuvwFMF4CSZlqjBPz2A2ibN9zyUvwug7cGeCGlNcNd/y3koPZe2lCe+bapArVze7V95/kk84X96nyy9M7bCW83NHNIUoN9P4OnTIRwc9ODFizms1+07G/Wr+XxeXguBVw4kSQKTycS5KgbAld8xOmATJ31Ib5bWC8mO9NGWdFwfWPU8q50vy5Xm+kasLombSkajEeR5cTw11Wm1+mpt5qOt1Uurv/TcN1a6tnG70Mtj9CgE6jdIkgSOj49hOBzund+nKfDxwn1q/KV7Dr/3e78HH3/8MTx9+rQMvj579syRUfj92WefwYsXL+Av//Iv4auvvoKf/vSnpc/94uLC6V9qcwI0i6fEwm35ZTjE+MP2hWcf3LZ9ZQrG+oQrnRRSZWKM0zaNERLOvjJ3YaBIwJ1WmEYd+Vw54UINgwAcn2VBbcKnNk58i5ivHM+DuEKLPXcEhe6/1CBm7GiO8sViAS9evCgF+nA4hPl8Du+//z4cHx8D3imLfNIdsjQYe9vC5DbA0v4W2ROTx0JPG+N8XEpKhs/ZF0PPCqE6U76t7WNVKLV5KtHD8Y93LeMbf3THFMXlk2GxMt+SH526vt10ePwJDSBTOZzn9d2csXwiPsrXvgFfs2iaNE8seoRkJGPA05KfOujwuXXsS8ADq5rByNciDhY9JdbZwYE6JbuCJrhC/N+Wrqb1C/ZZyFlgpWFxyGC+2P7i44s6vflR6VZ5h7xwZ4DGe6zOHeO8Qz1J25HaBkKGpdRHPt27KficTl1BV+tjSF/X5J40l3BsTqdTmEwmMJ1OyxNnlsulqEtJfElrDaZb+Q21T9sxx2V5l33cFS7fPObrpSavuF3ns/8s6RRXMaYA8nwNSbKBXq84thPTqzLVN/+0AWv5xAnG8CBNU1p8zNv48uOp70It+pLyrh1TjLti67tdi+NUMSjbYwFZbYesfmcs1I4nBqD3w1I9pwi04jjKIc9TyHO42Q0LkCRFILb4prSSm/z1O0CrQKwvmFfkwTa3T8mq3C4hjkd7mXqecP0kvBqtuj1gq0eRr7u2rsYGALCgrUy7+o1zqZJrVZ5iHtQDsXTeHR9n0Osl8OrVopNgLOJeLpdO0HU4HJY77qzrOgef/R+jB4ZohGxLC35tPfPxE3qu2WAhna+JjUrpxLYxnmySZVn5Eqa0GcaHt0l/0r6S8sTaYJq+KNnlPnw+21ACC55Qn0ttS/kdDAZl0JE+19pwG3bEriDLMmdnrwTf+ta34Ld+67fK/2mawsnJSWm7AlRHbb9+/Ro+++wz+NM//VN4/vx5iTfPi5MA6BjBl4AxD/oitU01oTFqsS27hthxasUnzSkrbR90iUuDWHs2BL61DUEMxjYxJu/yZL7rwB0Fi8WiFMiWshxPyHFkWfgtCo6mdFh208U6ISlPsQ7BLoDuhkwS9/g9fnzCixcvymNfTk5O4Pvf/z48efIE/uf//J8wm82c9onZuRWCrp3xbxpYHKF8bNEy6HTA3UTz+dxrHPhoa881RT1mboXAdwQx8mA1bGi+GN7yvDgKhL+pJtHAoG2b+RFy1nJ6lE/6DVDM2cPDQ1iv186dFtKJBhRviC4AlI5pCjTIi7svbxskYzYGpP6gzjZUhmmQG+cexcGV5n6/D++99x4Mh0PIsgwuLi7g888/B7znMPaOFy6rueGMCrwEsYaTVW5o6zum0TXlLbQDPs75iQHSeoFlun6JAmUM7dfBYACHh4fQ7/dhNBrBV199BS9evBBfcOF1wvXs2bNncHh4CElSXL/wq1/9CpbLZW2uYVn68h9+tMAKl5uIg6b1er1SluJdSF3KOD5n+dHjtA7cyYX9iO3lo0HLUGOf50F60s77rkDC2VTPpGsgfQHp7OwMAKDcYaPJdPxeLpfw5Zdfljrw8+fPnV0ZMcBpScYypmv2gqQP0o/rRA+vc132o2TXIR9tdXwLn9o4oWOWz3GpT2IdRTT95GQAv/d7T+H+/UF5V2yaQvldtEUVEIXajsfqG9mo2IlvQ6RFPxRnNb9p/npAtOLX/e3yJ+WT8vKjTH088EBr1YbSHa8YTKU7YlFeFzZwD5IkLb97vZTsku05gVo8npjywuuLUI2B6ru4/7WSP9VOrvXNfxx7a8AdsklS7I4tcG4Abo4k3mySG15wHaMBtLzMR48rdmVBN8FNP54EpKCw+z8hbSThc59b+LDYSRpPtnap89QEcNzY2tRtS18bchzxfV0FWPPcpc3x4nc1F9HGyctjigeDFFarnjhPYoHbMr1er9wkgMcSI/hkellT45rIy2jjy7opga/TNF3y2/iA3nVPoYt11gIWf4HFx2uxxzEAOxqNypceu9Kz28QbqG4do0NJerxGi7eZL1hj0fl8ejS3MbiPgtednrB1fHxc9g/HP5/P4ezsTDy5renJktsGiQ/JJkLA6/+0cYnXgr3zzjswHo/hs88+K3Hgkcb0CGeE5XIJX331FSyXy3JDBUAVb6H8hNouZoxyebcv/WIFTd7sE2zLzxVbXzEYa0USQ6xpha1O8LsGMUY6giUICgCOY8OH3+Lg1xSpNhBLVysXWux8CyZtS77Adl1nycHEd7+hQM/z4gjTJEngl7/8Zbkzdjwew3q9LoO6lNeuHOf7Kiy7Ast4iCnrm4+S0saDQRrukPPP19daPag8aDJWLGXajp9YJQYdtJZ29bVdSNnhgRKpfKjdEVA2o/GABo1mDFn7ijo26fjBO/WQ1j6A5nCngQ2pTIxegh+UjfxlGJ6P8nB0dFTeSYg7sUNjwFJPrR5NlG2q4MbwExpflSO1GpdWsPBuoS+l30XwGdx0vu5Kx6X8oOzcbDYwHA7h5OQEkiSB6+trePXqlfkoXdyNii8v0KOKLfJcqnvsmMOj/+m9VVS/qjvDm69TsQ4XSt+KX5sTFBdf27al/0myqQ0tLLvZbMoXh+jb5BJdWmY2m8Hl5SW8ePECzs/PnV0ZMe0s1Ucbhz75THFIzt3bBIknKY8GTe17afxqayGdRyHdzMoDzo8s68GzZ2M4OOhDdX+iG1gs8rufiu96WntI2LehhMCDjy9ehxjetLIVvYQ8cwOy1W/8pOWHBmarwG3PScedsUVAFp9VwVjef2otknowdrNJIE3dnbDFMcT0s4E8T2CzSaEIbBW7Yot5wB3x1ZHEFb3iP7DjcJMEg3jy3bF1/uODtV1DmAed/+Y4pXxxdDQa22jTWJw0f6UjOTnAras7frBcsbNW3mHLZVrxQkP83bEW/xm1Y/HFci2/5iPxPfelx4Ckq2nr4i71ccqbBrH2HepRTX2ZWr+jDMYXukO+BMtaLvnILDib9I3VrxIawxrPGm/aHArVoYkeWeg81b2nPOi6Wq2cUxUp/rvk++X2D38JhAZLUbdAPxieJDkajSBJEvjss8/K9p3NZuVxxKvVCmazWemXn8/n5QsnNPBPTxZFehabMOZ5KO7B5VosdFEmpMe3pe3zrzYpH7JXtLwhG7kNtL4z9i3cPlCnbMjw7vf7zrZ63Jllgdt2LlDgykDI8a05SnapeFHAewx7vR5cXl7C3/7t38J6vYblclnm+au/+itnJ81kMil5RuDBgl3BXVrAmzrpmsI+tUvbRQxgews8NR7a4EKliAbfMF1SXHwKd9M3PbnyK+1yzfMcrq+vodfrwcHBQZkPecS53OaYYgpHR0fw/vvvl0GKly9fwuXlJfT7fYe329wpy+Ww5JDX+l8yOOmO2CRJ4MGDB5AkCbx48UJUIPM8L41LHD/Hx8dw7949ePz4ceN+4GsP51PDS50BVoNRajtLGYCq7yk/+Ab0fD733uX3FgqICWiE9BQpXRrn3Cngk/OSM2qxWMCnn34Kjx8/hvv378M777wD3/ve9+Dy8hJ+8pOf1I7slgDfkn/x4gV8+eWX8O1vf7s0hKmjDucifqhslMYf/qbHIMc6YnDHBhrTXa3J0py2rGG0PN9hGrtGIw76Es8uoCtau+S5CUiyGyDeeWi1Ldo6U0K4ufNql21P24yubb7AvmazaSD1EQb1siyFfh93xeKOWPopAm0ufTmISdPc34mYztN4oATpu98APCh6U4qkSc5yKViq88Hp6PWhQez6EcRu4LU6aaTQqYpgKt0RWzhGcUdsv/xPHf3FPbGoF6NcTcjHbZd6sK4ejMXgapoWgdj1Or2R2wm5IxZ33+LO2E3ZLnmOAbECV9E26Q0Nd27RMhqPiRNgw9/xAU4OFO828ncLzesrtVlcXVza9T6TaNG0ejA1z7V+lXHx3/Qbf4fzu/fHFvfFVscUF7vN4+27UECBBjCSJHyvPMet6bUIkk+Lr78xPoYYH1DITqM2EV9bfeU0kHYlhsrTNuS+B17Xtj7OLMtgMBiUeNBPIfGurdkhG74phPQIK11NP+LluX+iSdBUoh/y22vlcc3lcHl5WTsljQYS7ypI7XZ1dVXW88mTJ/DP//k/L+t57949OD4+hqOjo1p7/PKXv4R//+//fRmA5XbmZDJxNj9J81KyW7/JEAps7hNoJzzGxgp8vrwYcIKxbaPU24Jd0butwFxb8AUYJKWGHueBgVnt3sUuoAkeS5/HKGUSaM6wEN7Y8agtvCj8V6tV+YY/VapwkaB4qAIWcrBsc95sA3fbAK+vPO1rqX/5WLA6zS2BQooXwafAW4MwWvkmZS3lQn0To4TzPLGyN5SfK8p03ki4rLy2BZTH/KhODpa2lMYUddL3+304PDws5crZ2Vkj47Rr4P3gq7+0Jvmc4nx+o8NvMBiUxggeC+1TpJKkOOYf10dsW7pOtpVVMc+0McrXAov8sqxvlnw+/pu2jdS2+66T8XaK7VuOyyr/ffPANybQYX5xcQGj0ag8uokecSXdAyXRTZKkPCL43r17MB6PnTsA0VEkyWIJp7RO+wB13IODg9JplKYpnJ+f145KbgPavOMBJskJFjuWQ+t4rMHYBLhu1MY20nilpxVIZbgsSJIiuI4vME6n08a2RWw531yT8lmgqb4UC3y8WGk0cS5SvUua43wsxdhYVn7SNIEHDwbO8cSJE4DUd4D6AceANX+9vKWsj7eqHvLzNlDHlZTfSLcaS+4a4H7S8hvvi8U7Y+mHB2mrl+JoMJZ+QPjmvzE4Sr/duuU5Hrm+gTwvAqqbTQppWuyMBUig2HmYOPUtvum6imPYpVP8h5u8tgAhzyeVs+LaFTStW9f4dwk+nqzPLPWqjwcce3U5imMbn9F8eBy7FTQdk9qNvvwhnE18Z741oqmvhPMirUdWXrnOp+HxrfeazdTGpxKTR1qzJV8X6vMI/GXdrn2TvF8suGh7++x7K21eRtJ1ML3pWG9q73LdDjf0oE+ZgsUu78KuaKO7+XhAHwwCvhRAT/BKkuJkM/SDjcfj8lTJ6+truLy8LPE8f/4cvvjiC+eYYY1+kiTi0c9NfMI+aCMrQ/g4dMm3D7pYf2LpWe2MNiDZhG1kc3BnbBeNEwu36Sh+kwDvjqX3k3IH28HBAazXa7i6unIW1q52S+2qL5s6jkNl2uC1AF0w+/1+eel6nhfHFUs7k/CohbYO632bZ2156bouTYMu1nbFxYIfg9oUtj1W2wQZeB4+diVDz9cWmkOfLsAowyx39VnBN+fojidJUUeeMDiq0ebjznKfB92RsF6vYTabQZZlcP/+/bLMq1evAADKQCTuHrsNQKUa6aOco3OHtyM3XKU7grAN8C3IPC92vj569KjMM5lM4PT0tAwa8T6bTqcwGo0cvHjUTey9xtxRCSCvrdh/lqOB2wZjrA4ArDO2ZRPnxJsAobprctfn2LDgQboY1IwBiR7KnMPDQ8jzHJ4/fw7L5RI++ugjOD4+hoODAzg5OYHHjx/D5eUlLBaL8ijjEPR6Pfi93/s9ePz4MfzVX/0VLBYLGI/HsFwundM+EOgYQ361QI6v7hgI/o3f+A34wQ9+UN73/B//43+Ezz77DI6OjkpdqiloDiCfMxCBHxnncx6G5rXUPtta6ynNbdDJ8+LNc6xTaH7hOHn58iUAgHPXUxPg7WftTys00ZXaOIR9IOlJ2x43lC6lRXfLh8YW3xlv5Xk4TOGf/tOncP/+EIZD3KGJu8XcoKIbyKBtJQX9moPbBRVuSp/Tq2wCL2anLM1Lyxc/5d21+Izz6/KWOG1GdRo8arj4Xd31igHX4nev3AGbZf3yeHvUMfCeWCgDr+h05Ttj67xWkLPfOfudAwB9CbLYCVvslpVOacDxi5+8PMIY8+U5AN4Ni3nx+Fh8ToOy7txLGM9aWszz2wGs3z4C563OK23T6jcOg6LfYupXHSWMwXtddmljoBhHOA/52KnPTZo/KXd8U9nXJVBdha7P/B576zoYGxyTjgPla4SEn983iS/noo5K1yzNNvLp9WhTxthMbfxM9DsWrOvqer12jrfFq6Eojhj7xsob/b4rQOeEL9il2RRN6NExPZ1OYTabqe12G7Z5SAZYeer1ejAYDMr/19fXom0JUIzRyWQCs9kMzs7OAKDwe/3RH/0RvH79ugzW8kAs0uE+wzyvH/v8FsLQdrw1jVGE/AddyRWf/ywW1J2xIaeABk0XlDY4ugQr7aYNvu26hRQIVDzouf9dBIJ8YFE2YttzF04TyZkQ65AOjW+fA5fuMKHOIm0B4hBq9zfNaW6FJmOtiVzj/UyN/dtSMvelz3n9LYEMqqBz53WMbNGck5LTXMIbmr+WYBc9VpkG+rRjO7kiryn6nLf1el0eTX9ycgLL5RLOzs7KF3Vuazxw45gGIUNGC4XQc3r3K5Whp6enDv1YvBLEBkikfrTKBq3/Le0ljV+aRncO444VS5A41qlwVwztmDXft95r+gSXN/Sodct4iGl3XIfwe7FYwPn5OVxcXMDl5WW5SzbPixM7LH2EeWazGUwmEyd4y8eaNkekNrKsu71eD7Isg+PjY3j8+DG89957cHBwUBrtTQLZUt1CoPHpqzPPE1qnfM7GXYJlvIXGLZdXfG3X8Gvrfeh/DF9Wfbnt2rRt0NrEpz9I/5uuURI/lHZo/ND1WqqHxDc+Hwx6MBymzs4wvyiRxlqoTBwgLsQr4dbo8XQfb3K6fQ2j5Yu24zt65Tti3aB3FaR1g7L1b7wXFkDaDUs/vB70d07+428akEXAYCoGUlMA2ECaFjSq+2LpLt/8ps5VILoYdjToVtBNknrwrvid1HihefYR9oW/JnxoZXi6H3e9zwJUAby7oRFfhdeda3F80LFWlK3oF8/xXtcE7t3rQ7+fwNXVKsqfodm3mu3IZbxaI0UX1uhqfEn2my8QxX0G6Fuz6roaHkrbp6fF+D0s9fLpSk30E6seK9VTy+/TQ2L1iBjdw6d383Gn8eTTwS0+Kx9dH9/0RXOfPqrx55sDTflqCr4+a+p/4nXEF+4fPHgAq9WqfNmP+l2pXYr+oNFoFEWna4jBHWt/bIOHJkD1eKufipaNyU9Bk7uaXUHp+dK2ZYNH3xnrE1RvwQ4xCpEVLA4d3KkwHo9LhxV3AHI+dwUxygHNb52wmrJnUYi2Nda545UC3tNwcHBQLs7L5RIuLy/L8cODN7ftANp3aNo+1GHatK1xFzQ6iC3KWyyPFlx03OwDSIEeNJQ4SIs5lV9NlHv8blrW6iykfY7HLQ4Gg/JNPNyJxndTofLg6zN8a48GH9brNUynUzg4OIDRaAQfffQRvPvuu/AXf/EX5c5QWnbXgLsjAKodw3wM+/oltF6kaQoPHjyA4+Pj8n+/34ckSeDzzz8vHYU0CM7x0/EWO2csynMbxwinYzE6LXxQHQF3T282m/Lt6Ld6XwV0vKIM4rsDYnHhvOTGZFftTneNz2YzeP78OWRZBg8fPoTJZFIeUzybzZyX9iSgdf3888/h6uqqXOd88pgf085flqAvq/hgOBzC0dERPHv2DN5//3349V//dTg+PobxeAxJUh0t1TSAFALJEac5KvlcbMsPbd/bnJM+Z1FMHX3yFZ/hbjo8AYLfAxUDPocp18122b5d04pZU2n+bQHOdW391urPZYE2r5MEoN9PoN9Py3tiMUhYBdTqH1re998KWIaWl/BU/ULz14OOSVL9puky3kRJr/MHwvHJbrAB87hHEbs7YlMn+Ip3xfZ6fej3C/22uiM2u7nPN4Mi2Nkr8Rc7YRPlw+vPIRe+tU/xvGpr99jXakgVwdbNptpZXRxhTNsFdUTOGyIJBemaQ3ucsQHHbUMcP/X6Y4DcjltqQ71dJRw6zxQP/w3gHsWPY0/Dg9+VHMjLsVs9o3fIFp8sS+CDDw7g8nIJP/nJlTpeYvxnUlrTwKBkA4V8LFqZJElKe5b7awDcHbVof9IdctY2QFuSw3K5NNnTXQd5eHtZg348aKr5LiSQrrbbBcT6zWL9yj6w+ABCuH1t5gsM87Ix99fuWofdJuDJcrSOo9EI/uE//IcwGo1gMpmI5ZKkOMKY2p6SfXl9fe3sBN8nX+ldBfSN+K6m4dClTzyEvwktaQ1qIw/VYGwo+kuNIOuCvC/QBW9tnSma8kEh5HyleSTFA59LQn29XjtOsyzLyuMdEXy7Ydq0YexbEfR/7ATwLWaxZS08xjzncwV3JPHys9mszEMVTY5rn+fcmwCxTt1QEKlpANFHAxUMyUksBWYs/MbS1+an1nbUqAkt2BSHVTZZHONaPh4UDfHkoynxQOvK66WtpbHBt9VqBdPptHSIae0Ra4y1BV/ggoL23Mdfv9+HPM/h66+/hsvLS3j06FHpyA/J/Ovra0jTFF69egVXV1eAwaLY43p99eH6lSRbtPUPy3BlMmZtswAaPRis3lZAax8hpp+pPAeI142wXfEzHo/LcYovVCB+OlaaAvKLjqhXr17BZrOB6XQKX3/9NZyensJqtRKPguN46GexWMB8Pi/1Rp98pvMwpj6a7Mrz6rj/Xq8HBwcHMB6PyzbcJrQx5HzzXWs/yRnhczhp60XI4dklaLQ1nV4D/nY7xRVD37d2hoz0GLtsF9BE9/A5/DRc0njR9E/pufZboxFqw7ozGeBb3zqGBw+GMBr1SHCCt0ci/t6O/cSDiLFrQ/d5aT73d53HpAw4gtOe7m/cEcvvg+2V3/gCRa9XHUucJLgjtjoyWA/GgvDNgQdhMa/kTE3KuuU53PCOayyU98cWweJN+V0MSQx4VTtki7bC44nB+a63f+io4q5gm7hjYZ94uT3QxoSeL6mNK54H56Zbvhqj1S51253VCLvw/YVsMWkdsOikmE/SL+hvtG9iAfVMTjumvLWMtG76fBEWmiH/h0ST2x4WvaHrNdXno4kB3o4WfUMah5rerbURpyMFA33ty9s1pNuHYBt6qqUdY/vRd40T+np++ctflr5E7AN65HCe544vHUE6hluTCU1sDam8VeeOxS/hipUxsfxQOcvTLP69puDq/rpfQOPZh7cJL7HAeRCDsTGdZ3WUbqtDfLBtmtYF1WfQWxZPDfjCqE08eiclAr+zcDgcOncncD5j+LLk8zmhLGV8OKwKA8fFy1gcRU3qwfNTZQeDM/QZ75PQnLuNudYFdBHoiAGLc4mDlUdfXyAOehxkF0Dx8rpRo0NTFmMcj1oen5KlzUt0zgC4LyT41hCNL97ukoObp3FloomTGhVFiaYG0o5MfvdODD6AusEEAOWO26Ojo3K3PS9Df+9iDnKDWXtOgfe/NO4wHRVw3PV3eHgIAJXiLgGOAbw/vd/vw/n5eZlOj+71gTTerfm1+tLnWEceLKPrlW9OUjxSOvYJDcRuY22xGgq3AU11Ej6eY9cWgEIXo2/sTyYTVa62MRyyLIP1eg0vXryAFy9ewN/93d+V/KPzPISHyqnFYuHcVyTJIvzNdxHH8s55WK/XpU7b6/Xg8PAQjo6OSsN6GzJNc/CFHCUSDsvahn3D56QGu9anmkJIH+FziDtQY3Vwy5yU2o7LTSnoKPXjvvSBtZ0k/cgKVK+lu+ZDc0FqN8u8pc97vQR+4zdO4N13xzAe98vgg/RhWAg+97+UJwYoLfxd8JA4vEh0papz/uX6hNLku2M5PuQJ+aW/6afQQ7SdsT1nZywNxhZHBhff4NwVmygfANx9Kg8LHowtPnmeAkAOxQ5WlBtJ+V3UKYXNpgrG5nn14lNxdPGG9FnFE+5CRN74rswkoQG0hPDYBvx4XJr1/7uCrunuqh4hOlY+7Pzq/VnHQccYLUd3xNJALMBmUxxV3OuhvaAHiEJ6iM925MDXyxhfgrWMbz2hR71qZenxxJIt6qMfsgM5PxJei39C4iNGt+P6ldavvjXap6v7dF+Jtm9cxcQNutCrqL3M9aNQm3UFsf70GFvBl+e2oImf3HeKG56u9pOf/MTRzbkfNEkSOD4+dgK2AEXw9erqqlOe20JTepax0Qa/hkuyGaSTbLrkR7NTdjneY33EtAwv2+eZ923ivqmwrckd04d0QV+tVqqDoykfoechhStG4ehi3ErOitiFchfwps7RfXFa7Qq0gFJbnKhsaDuCaCDHpyBboOkCSJ3KuAsLd20OBoPS6crpUIc0lR+Yl7enFGi08qrVzeJQtD7nAXKJv1D/aHSvr6/hF7/4BQwGAxgMBuURpIvFota+SIcG9LYJdNyhso0BQAmshgg3GlEh/Prrr2EwGMBoNILz83ORBua9vLyE6XQKk8kE5vN5yWOM8U35oXON10kyXgHcQL1Gmxv0koHfRkGlzvRtBRbuus5J+edjV5vLWhviOOn3++Xxulpwqot+QH750a/8Rb1QeeTx+fPnMB6P4cmTJ7BYLODy8lKU4fgb5TiH0HjnaZvNBv7mb/4GvvjiCzg5OYHf/M3fhPF4DEdHR/Dy5cvay2zbAN/8sMydmDnQ1Kil7c13cbcJWGvOta7mNZf5bZ1jml6A447T0Opyl+XWtoDqFdyeDI1/yTGN6yf+B/CP8V6vOp640qno+goljiTRg5ndQF1nqdPid7T6nvsY9c17H34trQq+0npUH+l4YnyJp9oFiztT+v1+GYytArC9G9yhXbEJSG3pQg5VcAq/i08RhMVAbAr1oC3c1JVeNVDtiK12yfLjiTE4nNzQocFivEvWLyOSWwqUvoX20HSNs/a5bDMAYOAV/xfjMmd53Gf4Yop0rRW1w0K6RcgOx2ehQKgPfME6yW9oCfZR3JvNpuQPTxjx+SND61bIj4nPKB68Hkcqtwv7G8Bmy3GeKezq+NZtBVn21d/o6/+Qfv8mAvdVoZ2cZVn5Ypc0NqRjnKfTqZPu8zm9CdDWr9sU8KU8Ce8u5rG0McbqQwzBtoLctWBsiJgEXTsb2kDbQRQq71MU2kATZ6fknItdyKkjUVIqNFw+x6/EnyXdSseXz5LXStuCI1RHC+x6zL6pi3bXoAnt0JiMlYdt+oMbImgIWZUMNKqkvJY5IhllGh7NqUqDsXiPCwYFMC91BPF6A7jOZC1IRXHR36F6Wg01q6JBHeDaOOD9quHgvynM53P4+uuvYTwew+HhISwWC8iyrAxOaPekbjtAJgWZfMcH036naVK/8qACKoQXFxfQ6/VgNBqVx77SsogTAMrneDc356GLwCR3anDgThJfkCeEX8svOb81HqT8XYE03u5SoEOSM77+knQ0/F8d81jsLpN2p4bWHSsgH+gspy9qWHDy44hfvXoFo9EIHj9+DMvlEs7Pz53ALu1nKs81Q9oSdEPn2q9+9Sv4+7//e/iDP/gD+Na3vgVZlsFoNHLWkbagrStN1gAJL8Wh/bc4/qwgtb/0XyrHwYdDK6OVtwDmDx07L9Gna1ysbaLNDV85Pk7a2DIhWRzSOZvY+D79Q3rGncyaTuFb733Pdf3OPY4TPzyQVzURnbciKYE3//M8d4Mhbjl/4Xp+O11/vhinOwYXq7JJGdRJav/x4+6Kpbtje+W94xigxR2xeZ4ABjn9AVmZLzfQif95utYOifCNQeYc0nRT3hMLzn25btAXdyPqgVcfP29h36Doy7hnOHVw7rt5iv734WXYQBov0rzmOKvxWZclxTOUlfpLvz5/lmR/W07ooDgQt9VPKa2Z0lru40WjledVkIfbWJb1H//z9S5UL64PN7XtmoKmh4TW41j/RhNdw4ePQhd2qU/PtoCvXhZdR7PrQ7itebZtQ4fGS1N8Unl+UhzarRpIYzbP8/JuaCs0qcu2/CSxNC39Y5UzsWMJfXq0bNNrnEJ0JDkm+c00H571SOqQX8Rn91rqrN4ZawXrovoW9guWy2V57CICVSwQ6BtcEmzbUbsraLuAtMHxTYMmMmObcsaqlMaAb17QZ01phdojSZLy+A7qeJxOp7BcLqMcgjydL65II9aoQMAA2mg0cu6P3Ww2MJlM4OrqqnTo8J09PGDEaUsLMP2m+HiwwAcUB72Tgit/FCTlnx41azFiQs5WBNqOq9UKzs/P4fLyEpIkgeVy6SiyMfS6VvB5f2Jam7duQzxSRVGiQ5VGPn66qj/ios5K7JvNZhM8wpyOMd+OXWksako2Dc7QubPtdW3bRuMuQZrLXImXZBLNe3p6Cr1eD46OjmC9XqtGZ1fOjhiHGgc6/qbTKczn8/IlBjR8fWNIGotW4wqgug8b2+lP/uRP4Pnz5/Czn/0MTk9PYbFY1Hb6+pxg1jpLRh0C3eXvC85JaXy8+BxRdL5qoDmcsKxPDu4jSO3dpj9pWWwPX1to7b0t/VRzkGsBzib4uwTNYYFjVjslQgK6Fllpp2lxJKcUlC2aEcd/VLWiytB8chkpUQ426Hh9O2brdZTy6rLBpUH1gOpuWCn4Su+FdXfEFi9a9oDuiK12yMqB2CpQix9eRz4ucvKNMjKH6mjiFKRjiqtvDMQmsNlULwimacELANWH6M5YIL9zgi8nz2BnwGndRfVq123WFHQ+ef9L45XONfdbwlfg4TallC8ncxfKeYsvPK/X9ZeDQjoE19O4D8AafKLPLI5qy5rKT2hBfVl72Zja/Rb8mIffV99Eb7bqCPtgE2m8NrGDm5Sx+pQk3Yu+zNmFjmvhI2Qr0/z8iiGeL+Q7+iYDv6YE+xf9nQA+HaqZ33MXYO3jNmMhRt6FnvPjnvG3NA+Wy+XO2tbqs2oiD5rIPWt/tQ7GxhLcNmw7OGgVttsEi/MlxEvM4hhj9GuO3rZ4KfgcmhpdyxiNEYYhGl2Nw22PYy1wtE1eLAui5ihoQ886b2LGcBM+usAdkkNUCZUcl9JYjXEch8Z5yOEs8V85bargUuhNNlS6Y3c/8TrHBK20fpMc6TFGrhaYCMkSX7AHAMo7FamxGho/PmU2po4ho4LuZJUUOatiFeKbGuG+8UufoXM+1ikc4pEDpUPTpLrQ4AEfs5bxQfNRPNwxo62xMUaiZc1t2qb7pHMChB1KFl0IYT6fQ5IkMB6PO7/vVGu3pg4WgMoYXq/XsFqtyhcItKOKQvhi1sk8r15YSJIEPv/8c5hMJnB2dgaz2Uy977iJrufT+yhOTTeV5k4buWJxZvLn2vyNkbO7Ap9c0/RDSa5b1mHtv4TXov9jmZi+teKl6fR7m/2n6UWW/NJ/K1idR/0+wGCQQq/Hx7F+DDBHjf+7aEaJDv3ukpZGz14uYf9pu3Nk+r2xeFRxsbu0HqitZAx+9GOJq+N/6cflA9Rdp3n5rAro0nIAVVle9+Smji5t7LM8r77dMjn5Xw/Q0TJv4c2HZv3tjiM6xigu6T/9cDz4SdMEsqwHSbIpA7IVDllH4HqB1fZt4i/UcIX8Nxa9yucvlNZdy7qjBWOb+Hkob742bgqxNrqvzzmPTe28UJ19fSaBNm4ojxa+m7azlV/u57DYhtvQ7br0bWp4t6WTSrJGuqM0dn62nWNt/R1WfTcGp1TON1e0/zy/9kKBZPNLstJi0/jq4AOrL8wHMT6mLsZ5J8HYNxW6MHItOLp0LMbgsgghH/9thTkuMtt4K98i9No4gt8k2Lf63IZTMDRvJMUuBPgmKi+PsFqtOm17HNPX19eQpikMBoPaghly7OFvS4AHdxxJQV9Lvfr9vuNMB4DymGIEvitMuj8MecAFX1r4mwQa8jwXj0HkSjQPmEhKdpMxjWVijuzkfUfbh/Mh5evKiRoCpIUnNNC3V60KK+4eDSlwm82mPHp4MplA4bzte8t0KYO0t7C5QRar+OG3dA+UFQ8//qdL2KYcv401wgJUJqD80IzpkMFO53MXxgXilWC1WjXekc7H0GAwqKVLQOvF75fE75ixnCQJ/PKXv4Rf/epX5csnXeiWVC5Kjj4Kof66LdCcTk1fZtoltFmDsC98zmIeWKJpTaCps6Qt3Ma40+qq2VehuaPhBdD1oCQB+O3ffgQff3wEjx6Nartiq+BaHbe/uZrobP7fblpSpkl8SOk+fmUc0rz3/edzgOrYlS6J/+vHEvdvdsb2od/v3dwTi7titTti6ztji12zUP6nO1CrdAy2AoBzRHFxL2yBA/9LO2IT9r9qM7wrtjiquO5ABxIMTpJdB1qtxG7Xrm/TJnvmkgAgAU4AEPvcNg6qMQMQzs9xUjlSPHP5os+qcVt8j0YpfPjhCC4vV/DVV/MQozf82TYYhHRZgLidSFJgRfotBbVCukxM0Eyjmed57UoPi08xRqdtwvMuQRsHTXWQJvWSXmK+a+AbN7sIZr4pYJ3L24Ku7AUA+zzSdGwfjS75jClrHXPWFy9iIdYX0HbMxJYXg7FNHD/7JgSbOsK5g7otWHDEBjHa0rOA1n4hhSB2IqHQ2dXi4OPfKgApv6Fxto+OuabQxcsJuwSLghOqi+Rs4sEQbfGjTkAasLPec9YUcNHhAQEOTR35vrsamuCj7aSNMc3JTf9jQFaSJzzNOoa76CfOtxbc9dEL8cuNVV87+XCHZDHtJ9p/MXIf80t97mubED++dqaGOh6nF+ITy/FgzLaA8osvJHDHQsjRbzHYYxV9KZjRFcQ4Rd5EAxJBqhvdzW5dd9qAZKw0oWVZ37Qx6EuzjNnFYlGuAV06abgM8M07nzHcVfC8KzlkcbTG4NkmWNoutA5y0MZIzJoo8UBlZldGvcaHpttsS15q+oXkuLeCVjc+LrnthemHhxk8eDCELEsBWavKgPNdp23nM89D+d3gXmzX62OFjsc4nBY6SeLirX4XQcciPwY+pbtUq2AtP8a4ksWxHxD+O1zffNNArJSPP8dxRvO65d0xw3FWd3P6IEn2MajIYe8ZvGNAx1dLTN7xI9Pxybg0TWA4TGE2266/TdONQnaUVI7rpKFAgwTb0E2onqf5MH16a2wQV8IbE9yI8eFKPPKyFnox+K3+5BieYvLG4AiV6Qq4DXMXIcafY+2bWF2c49lHP4LVPxuaVz4fp8XPGOIpBniMxIcvNlArzY1t9XUbHCF/JAVvMNbKRBeddteFzl0HywQPTSbqhNgG+JwRPr5Ced7CW+AQu3BRoMGePC/eoMT7mZOkOH5yG8DHON1xikcAI2CQJ3QfIQW83/Lg4KB8c3U+n5e7DhFiF3q8M5PeK4D3TdG6rFYrGA6HpbO/cAC5d40ibv5mLabRwJ2Pr6YLsBZcpLxJSoRvvOF/6X6eUH00oIZiEyWtrZLD21mrh6TM+5QaTZfA4GaoLIc873bXGG/rzWYDq9WqnFvI/6NHj6Df75f3Xs5ms5pxH1rb+I7F0BjjRrTkCJfKvoUKfHNDChBqY3Cz2cD19bWTj77Qs6s+wHmDpzg0ceBI6dJOWICqjdARpxlf2lzFeUTnbNe6H11fJNmAdYt1sFnlapeBWOlFrS7ke9cQy0+Ms5OOLZ/zaB/aJNYG6sK2DtVbws/bVApM07mNupn29rpPrmCAFO+KLWi7AcYqiEj/S3XRn2uB2Lqzmwcz6bcOddxaQDccmJXS5bxu0JPy7bajHGgtbJ201OFR/hY7ZNHm0O6G1XfGAgvCVu3LZZUWaK0CWfWyCcj9UqXRADLWn7YRjgXcsQs3Adpdi4ht0usW9+3LzqaA/b1bPO5Y9s3zan5yPQqcT5oC9PtpqVu5eFxZjb/pN4C7YzU2GOjzDXLdkvKgrXc+h7xGC9PanJii2ZDbWmdv23cp9U2sz7WJHmW5ouW22yYWrO1w1+p1m7APerkV9ilW1vRlixjAE7ea8qM9j2mHNr5cq6+NgyVWxqEWjPU5eyyMNHEWbVvwNDEmu8B7m+BTerYJFuUktFj7gDojJAWyCxo8f0ybNQ3+7vqFhDbjoO58kBUMTYY0qauljBWvr49C9bIqh3QBos7ZPM/LY1m5wyp2nIXGNl9MtONSeRnfMwweDQYDODw8hDRNYbVawXq9jjJwNDlBg1/SDis+vqRdUBJe/J8kSXm8cqi+MSApCjy4pZWL4UULQlDQjFTezto4kMoiDewTKdgd4tMHPoNbU8IkZwEvT/MkSSIebbwtuRuakzSNfgMUL09kWQb9fr/1vGoiV0Jtu0vYBx5ioCsjgfdBU90iFihfKItjx5DPcPLNV6vxE8IX0jPazHurXPcFkPr9PgwGA1gul7BYLERZJ0HX65YPuMy/LbCsn122S9uxAdDdHOVruW/9kOhuQ1aE9EcfD1mWlcEA1IWlvL65Q5/duzeAe/cyODzsl2UxOFHh5Px7KtcSJNyYFkM3hmf3mY9IYuLB7b+EtWuVxj94Zyz9yMFV94P3ulb3u/KPr14JgLjztfiut83u5JhFFN0x1eYt7CW445qP+SShwdDqueRHc0p6/Atcv+tSP5B0QCv+Jj4i31rjw6Ol+fySseALfEr9xnWBNj7MEF/W/D79xWoDWfBL9pLP1rCCb1748jalvU37rosxuY25HqLXxo+/j8DnQNfzM0TXh8MyPlEmS7aH70Vrmta2zk3awVd/n5z12VghWhoeDp3dGbsrR9Fb6B40p7Evn29gxQr8GGWL8tFmYenCgaMJpaa4dgm36VxrUldLma7bsHJCVDuTfIBvhq/Xa5jP9TtZ5vM5pGkKo9GoTLPclSnxZn2+Xq/LYy+lC9al8pwfdJwtFgs4OTmBJ0+ewNXVFazXa5jNZt46WyHPi53EvV6vdNrxOtE0DKzSXbQ0LwW+i1aTHxaFGmnw51KQL8ZxLzlfKc+UtsSPxYDG736/b54zlA/cEdH0jkkJZ1scPoMJ2zdJivss1+t1+cbeLvQWn7NDo5tlGQwGAxgOh95x6lNSpZ3UVn7p3ONjuQnObxpYnEKhslL/dmFMWyHPc5jP52Xw0KozNdlJznHHOEA0We8z+tq2oXRPc4yTaTAYwMOHD+Hi4gKWy6UT1NhVoFUzpiXYFV9WsDpKNRmp1aeLO4a7AC5vaXoXDsa2fEkg6T60XJqmcHBwAFmWlXfFX11dmceV1Dff+tYR/PZvP4LxuAdpWuz+QtKJNyhbHblbfxYHPMgh05PzUh585bYBnGe3PWjwhgZdU3J3LL8zFu+N7d3o73RHbH0XbLGrFAOy7q5YfFb1n7VRaCCWl83Jfz2Ii4FhPkaClJVhnOfdBF23IX67wrmLpWGPlp/GkCRN6lG9WJDnFAd/uYDen0zndyVzijnrYud6Epfd0m7YkMMdf8cGR/Elde4P0exdTj9Ey5e/qZ7Y1te5K72qC1tX6lNfP/CTV6g+FhOItfKM4ybGfvDR7zJItg+xkX3gIRbuIs8+2HV9fLKzCaBvNnSFFkB9TsTKui5ko8V2RftEo2l5Idn6YoGGp7NgLCV210AyHu9iPSSIeaMg9q0JX/7YNy9C7e9T7LoIqkrlfcoLL+MLgtwWbMMJacGFws+neO7ScRRy3FkcvT7HJf2Nx/7ijrbNZgOPHj2C4+NjJ3+SJJBlGUwmE/j666/LsrFB2RBwvnn7WwwTGgTVgo0YDJ3P58HjZXibY3vgUc74nO4aonyE6hsCzemtjU1r4CRE0zrWNKD8SQGyEB4pyMbz8/rwPKi0ZFlWji3cGW2tU6zxbAGtz6Rvn0LUtXyyrln4ogTOscViUaZLQfgYmeYro80Dvt5yed61/G67hu8bcJlrba9Qv9E+2FZgFmVEkiTw0UcfQb/fh7OzM1gsFuWR9NZAfyx/WD96eoHl5SEOTRw5Vt5CeTR+NBklyW+LEWupY0hO4GdfdNZdwjbq3HZuhnRqmicki7n8pt++PD7a0n+LfoRrGO4I9zllqZ6i8VvJhgT6/ep4Ygy0JSyItpvhzYN/fvqxPDWtQ6icu+bIz7BPkoT+dnfFFt8YsE2hCMaC0y/6B5z/xRjjzySggVQgv2kaK5GH2iUnH718lZd+bxv2Rz/ajqq2P/VD2E499fFpxpBovPEji/Gb7lYP4ZZ9XlKQVNJXqGwACAdCETS7UyorrbGav0arE6Zb7EG+BmtrvGQzc71eag9Jz6X6mdU/IKVp67WGN6btNNpaHovOYQW0mS1g1ce2YYPuk37dpH67sMtj2qiNzdKVryHGh0rzS2W7tqO18j6/ocVG8PEa8q1J5Zv0QVu7SlqzpL7B31zG+Owunx0eqqs3GBsabL4K3TX4pjokNJAWUN4+9A5GCbRJzQWC1Ukcy/+b4ti9DbAKYK0cVVb3tR/ayi5J/uHdSUmSwGKxgM1mA++//z58/PHHIp2vvvoKPvvss9JJRQNbXbSdtkBaHXr4LEmS8t5AWg4B76TFOuPbU5wXCdAIQfwYgOL3rHbpVEeamiLfRmHT+KTGlmTMaYu7ZoRKfacZOrhzQaqXdR5gH/R6PRgOh2X6dDrt9F5VCpohqq0ZvueoVElzwqIsdQGa4Ys713E84ksN9Ahwy/ig81MaXzGKMp8jb3WkMPh0oRiZQmWeNDbbBn1CsNlsoN/vw/e+9z04PDyEv/qrv4Lz83O4urpy5KdvTMXwxeduG9l/G/qGlT/an/gCBgKV600cb03yIw8xhuVbCEOMkzCEZ1f9sI25jID6XL/fd06EwblAr5vQZIuk76RpAr0e7taE8lPkc7/99Yhr4zzXg3kuPQ03DyLE8UPr6ccjpWvBYho8cIM4mJ/K42qXbFr2G/0U5fSP7TlvC/wtBV+B/M5ZmSK9CPDi2im3F50Cxe+8/Jby8fxdLD9Nceyjqb2PPO0KEjVg2gxHMSf48dtVPv7tS6uXT7zfANW6xq9Zwm/ftUHayU48wBmr28TqTG3W0xh7Sgt8NNVlm/Ld1IdhCTzzMjF4Oe6mQcIu+lXCK8Fbnbg5dNl2d7Uf2szhLurcBQ7fZhtu99C1oAu7PPbFbEneSDLZV9763Je3VTA21ICSk2Rfgy5WCDl4d0VTg23xog1WLV8XE4zjsrQ9d8yHBnisI9rCqxXuwmLB20O7BzKm3bbtJN4GWBRePtZ8bYMBK2zPzz//HCaTCfzGb/wG3L9/H0ajUVkej/bFo43TNIV+v19z1Datk1VZ1YKsWC5JknK3L73T9eDgAJ48eVIeU4wvaPDjg7VgAp2XNI3uvLIEkCx31lIHf+zLBbwOPN3qyIxZvH35pOeSvKRtMhqNYDgcwnK5hPV6DcvlUuwXSSZTubvZbGA+n0Ov1yv7N7Qbug3geODjROOPyh7rPclNjeUQaPOPpvPnSZLAZDKBXq8Hi8UC1ut1edetz9nB66M5N3hZqd0kvmLq3KTcmwJa/X06jiYftXK7gpOTE7h//z48efIEkiQpT3Gg0IVzi469w8NDODw8hOvra5jNZntzdKwPfGsDTcf/eLT/crms5fPhjqXvK4/yRBuX+2LPJYl8XYEPuK1ggX12wPn0szbyVlp7moK2rlBYLpcwn89LHYTSlfLTvqcvk9FvPJq4wCEFIfQ6adUtAnVSkM8fiA3hjclL6+EpbaUS+dwNiLr+Hr4rNiEB2OoY4+pI4gpn0XaJQ6P6+PjibZ9AFSS9yRFsiuqo13r+3Pmugqq58LyiKwVkJXzOk1zK1zVU9bDSps/cukt5uuDbhsO3DO3JEqXCrvjzjf1KNualjKznqds/3IdgscUln5umy/KXk1HWb8Pf5lvnrD6GGH+3ZrNTm9ynl1nxWUCz/yQfPs2v2YUh3D5/hzSmpOdWiLWTmui0+6D/3RZso7120Z4+Pfk2aEt5fDp9U7zWedoEN+bz2UkxfksL+GxZi9/eZ1dIx+9z/nwn/lntbKmsBI2PKdYEbsxibcl/22BdGNvgiy27LWEmLbp0QZboxzo8JJrSf2ugwgJW501TkJSWfXVItxUYHBcK4BgZsM8gKaK+fBqgcwIA4OXLl/D69Wv45JNP4PDwEI6Pj8vjjE9PT0sjZLFYwHA43MpxxRSaLtj0rgBczEajEYxGI7i4uCgDRSgreICM0/K1MeKX2kHKb9mZaQnMcR4kmpKM9AW8YuYS/qbjkBpPFsORAnViZ1kGBwcHpTMU703V+JbqhsbkYrGAwWAA/X5/K/IYabU11mIgZp2wjA0t2Crh4+Vns5njnMD09XrtvJUeCrRS45/Px9AY9RnhFmjrVLlL60hXvPqcC7EypS0kSQJHR0dwcnICDx48cO4E57IoZk5L+Wldx+MxPHnyBNI0LV8asdxPsy3oSu7QuuMLLfSlK5984DLGYkeFgot5Xr00tW3Z2gRCOpgGTWzQru28LoHq2aF8AHG2k1XH6QpWq1Wpe9CxJ43pkHMnKQOv1RG6PHhHcndeFw7IjyeHCUeTcjIuzUHl4zNx8iGeqgw9qhhq/UePi676o/jQO2LlOmnP+Xx1A7JAArOWYevmsThOq+9qOOakrB54VTA6eN3fmr1pS7ttuP01JN9Su+yyXjiuDTlv5qWlzlReuulyAI0GDaU1UnNyI/h02ZBu0iWEdKdQmZhyND/FhZ/YE6Sa+tZ9foI8z8VTsjSdMuQD1vzCPpD426bs2JaP4i1U4GuzkKzwAfczd20PNIU24zfWj9cGfHEdfG7BEfpNgd8V3RXEtJnFLyvJNgrSi+CWtaDJ2PAGY6mQ5ci0HRrfBCEW46Rs69DcNVj45QM6dFyxlda2hVNsgC3k1NiGA/s2wbfrSmo7GiSS4LYNs7Z94dvtx+tOFW5tnE2nU7i8vISDg4MyGMuBHyfbFqijlSszlkCOhK/X68F6vS4DyQBF3fCIYgmkBZx+a2sJ363dtk+19coa+OB9bKGntXmovbV+wrL0Ob/DEduO3l08HA7h8PCw3I2FR+PS/FSOS/IZ54R2of0unPpW+S3xww3KWAUav5uMQSxDDXKLI4SmazgxL51LSZLA8fExHB0dwdnZGUwmk1pe6ehsXufbWs/2MUDUFUjtKunYAM3uTN0m4PyPfWHIlzf07Lb1qtixGMqPL7Twly26AG0e4zPf/32bb5L+YilD69PGecNlc4xD9DZA02+SJGm9wxz7wjJm+TPUaVFP9PUnfYbleLs/fTqC733vPjx+PCp3xhZ03Y/LU7N6FzxJQbx64FICypvwlOS7jbHj1iEhgVY3KFoFWKl+Uf/gXbE8sFqUzfNid2xBzxKcZSliE+WeZ7jWVnmLvpTmcU7y1D9Q7ootcBT1yGvf9bSKDwvo2UIvY8ThtYrFWLxt8zXN30XZbdWlW9CDta6soS/yVv8r2eiXN9wejA22xcgzLvPrc4/WUX/J22cnaWX4em8NTjTRK7QjPCVeaB5Oz9K2Wt9pdCiETrqy8oI8NNkN/BbeXIiRHyE/823CPo3bXbRPm/repm+QAj21UirbZD1oyxNAxJ2xFkcddwzuMtC2a/AFXaS8TXG3BS2IFloUed9rRj7+li44joGQ80hSSNpCU6c8Ly+l78PCERqfvvlpVUg5nhCOXbYLX8Tb9LG1T300lstlucvHEujdJoT6xjdm0FiiwR260wEd9Ygnpt0kOSMF1KS8TcA697lD2MeDJiulOWa5d1taZ7U1mQMNmvZ6PciyrNzhTJ2rGn+8PtzJzdeVGFkQA9oY8IGPfy2IqeWPoUv7W8pvGb++O2I1WjQN0weDARwdHcFkMoHJZCIa5XSOSfjarmexTgQKu1Dgu4aQ44On+wISHO8u2oLrRVwHpGsAL0N5ldIxzScnkqQ65h/p0HvKOI/bBrsTPawP0bmmBbMtdWuj02j0fM+ldbgLkGQDrot8zMWAzxaVaEvPfG28rXXutqAp39ZxiH1J9URanuLwrccIh4d9+OijQ8iynpCPr5Hx9eoCNLp7YBoaoAriFH1HAzhugLYK8tAjUOXganITkA0Fgjgf6tNECozRdan6rvLyArnzO8+pfkvTqg+w44o5Pf7bB1U++xz0484NebYLPtohvu6oCN0zKI4lBuABWrjRr1LYbOrjV/Pnxq6/Pns+xp7Q9EpNF+G6amiNt+L18cSB49B40eyEJu2jraMafxIdydaldfHVO6RfxR4/vQ/+0y5gH+th8fPF4ol5Fou7S998l9CFT2sXODWwyLGY/E3odsmvlD/kQ+OyTfO7NOUlhufGxxQDbHcyfpNhF20XUlZ8C3mIP9/zriZUlzi2Ibi74HsfwSKo2jgK963daB34G5sc8OjBNE3Lo4Y5rNdr+Prrr2E4HMKzZ89qb+jgnazojF6tVlHt6FuAYvsFFXF+tE6e5zCbzeDLL78s2+Lg4ABGoxEcHR3BeDyGi4sLWK1WZZk2u+el/9seI75daVIba/xQI88X+JTSfDviYo1aCgcHBzAYDOD4+Bjm8zl89dVXjjJCA4G+QBHec0wDJXSc8fHP+y7Wud/FOuDbzWcNgjTlmQaa+FzwKajIN0/TlGVp3PT7fRgOh6U8keohORHuanBhX4GOMZ/M4IHPXUKSJHBwcADj8RjW6zWsVis4Pj6Gq6urki/6wk0MWIyfg4MDePbsWflssVjAcrncWkAwBix6M5+b2npM5yGvG+KR+l5qh6brwb7pWwi0TZIkMR/v13WQWnMm3wZoazHqFfw5noiBL1+14ZuO667aFnkEcF8a05zk7q4iuLmrVAoYgpOPfns4AijvNOW8YlqR5wZjyVeSAEmXadn58IO/vAV5xT+2l1SHOq1EKKN/OC0JL3ZzRUfiPxfTiz6hwaZq92rVVzkkiburtdoRm5MP1H7n+eZmzG9uglbuDtmcBGVp0JbWq+K1fj8czR8CS974KcntKvm5jNfuM9J5t9y766Plf67ltbSTlsfWxvutLycJwNFRH7797UM4PV3Aq1cLNS+/3kSyCSW7RBoHIb8h1WnoCW0+3ckCqD9b8od8hJagpFaOByM1WtzfFMId4pWCDx/VIzTQdKsQXl5mX3XOuwJvSvu9KfUIQUwdmwZAm7Zj17aM5K/w+cy7AgtutINofo2vbfMrQVQwNrSAWIKzoWAOnaD77gi8Lf5C7W0NFmhKkwRthCbtz6bCJpQvNK74s1AwhObh43GXC0jMgtVmceNBAyut2HIxfNxFoM4l7jRFxzbuDlitVpAkxVGis9msdicfQuzY7hI0fPyYxSzLIMsyAIDarpYY/vdN5vsMHu4A12Qbl3+Sw16DmLkv8cfT0MHf7/dr5XgwQJOT1GimRxZa15iYeml5u1I+m+gaIdohXNY1sM1cwP7Ae4Kn0yksl0uYTCblSxIxjo4ma4vUtrexhu4SpDqHnEe+OcJli2QkbKMtcVfqfD6H6XRanubAwTKfpGCjr/xmsylfGmgbSJICO1JaLFj71KKHhhyVPno+3ToEvnaw8rUNaKvHSmvXbegVXck63seSPkHpcYdul3o5voRBg7+x+H22V8xaUb+fFHktf4EtQMl50YOevmcVTYkXMKU3he1NUx5kTmppLm2tzS0MyoFXOT1n39IzQt05lpiXrQKsNLiK31Wam88N7jU7krjG+X6ZPw649dsKhVZ5mvLUVV0seHbRv5IsKORkQbzXS2A87sHVVf0I+JDvsIntE7KPfWk+H4Jljln9gLyOXdiDPhyxa6dFZw3piCG6XepITXWvN9Uu7BJ22UZNaFn9NW39KF2V6QosNhz9HZqrMYHYkE+1adwlNm8TWbgNkNawJLG/2N6FTRN6xnlstTOWErJM2raLJ8e1z4L7Nvmz0vVNYl9fSbvG2jjM2kJM8NCKI9aRsq26c+G8y0Ac0ozBve0xcJvO/Ni69ft96PV6sFgsYL1ew8XFRcn3cDiE4XAIWZbBH/zBH8BPf/pT+Mu//MvWNEO8t3Hk+4I3k8mkFkzu9/vRi/6u+zW0iFIHaJIk5Y5nHhSJkbmULsXTZg219OtyuYTZbFbmxV3YvByXOVJgCZ2ydOe2rx4WhWQX64emAGMQaJvQVQBZA76jOU1TePbsGfzO7/wO/OIXv4CvvvoKfvGLX8B8PofBYGCus3Wc74NOti0e2uKlQSKKkz6nfceDLrsGvFf6Zz/7GYzHY5hOp3BxcVHyslwuo8cOgN8Ixfp+/fXXcHp6ClmWlXdeA9THdxewTX3C1384P/O8fo8YL+OTkdq4lPTGJo4/Ce82xqPWVvRoaisOPHmAnswRq+dsa85tU0ZSvOv1ujyt5PLyEhaLBfT7/UYyH1/e6vV6MBwOYTqdOldU8DKSrKN5UH+wgKQfUZ1Mui+W/+YgpYeDrG6eUF7/87j+jx0uzYZX4nwnN4HXoh0T9p/K66qcPK599mqIJxqE5esITSsCo0XA1U1z06XPxvlf7ITlnyoYy3fJVh8AN2hb153FGgYCnNY08lTM4/737xDdhuhDnG1xh8pb8NfbXApM2ctb68T7oGuQ5hNNo3MZf1e8VWNXk83UHg7zEtZJ6A5Yba74grFSIFniuanPrO06LQUEfHkor7TeIRniC86G8khlYoJAyCOncZtBsV3CPti8EmyDr1ic+9guXUJbO6GrQHebMrcZb2vrT0Fo49PuEmL8hxKPTjC2iXNZK79t2PeJvu/8IVgc56E3ONrS3zZ+gLBSsy0HTJdCbNtjSgq6WEBSmMNGaLN26UqAhyA0Vnx5pABWmqblnY2bzQbG4zGsVqty5xruKsUA1zYD6xbw9T9X8vE/XYz4jqZ9mVuWOlnohNZK31yQFAmNfij4asGFfbRcLmE6nTrP8PhPnt9HN7Zf28gRzYC14NT6RTJSm+IL5Y+tOwdt7FCHtJYHj6gcjUZwcHAABwcHZQAIgxxSIChEW+LFVwdfvfZJwe8arzTWeMBCG48avl0BrkNnZ2cwnU5hNpvBdDpVxxxAxTsfozHGEgbRVqtV+bIIbSOfzG/TPl21rYVPrUyIj5AzT3oWa9NJ/RXrgI3VMyzPkK8QH1QfsdDYNVjmt6Zf8Xz4O0mS8qQLAPd4SepYl2S+jw/KS5IUL6JRvc7SH13NSaSTZSncv5/ByckAEhYgvMlpwFvlp79DeTnw9Fg+3Pz7A3Kf+hi1VCJ2HGAgFssl7Bn9nbN0Kegqpddp6sFWujOWHlNclbUGCuUAoC8gmAtp3UHXOGMDorugtw3wybZ2PHVbIVnG6DarFhTk+aTyfE3w2SkVf24wVpI/fOMH1wf8fREf0OxKj+j1erWy/JQZ3tZWvjV9UMMXsjN87a/pIDG6674ETbqEfa1D17aHFWdoznThL9qnQH/T/o+d67TMtn0mMb7vkO8zhMdal67sYktsqykfbaH1ztiuYV/fNLlLEOs8seKkZSTFq+m9kDFOayuE2sDnTOyKl205nG8D9mkBvAtAd2X0ej3o9/vw8uVLePHiBcznc7h37x588skntXLL5RKWyyX0+/3OjphrCnw+aM5CNDjwv2QQtK0HlxFaMFAyoroA64sGVkd2F31LZXBIWen3+zCZTJxgLO5cxp1EEqARHCPX6w4se30sznbk13f/a4gO4mrKR1PcTXAhPk0R5mOp3+/Der2G2WwGm80GBoMBHB4ewsnJCWRZVrZhkiSOnNLgTVi/dgl0PsYEWy34dgE4bzebDfzqV78CAID5fO4Effjc1pxX1Bkn0eHOANypnyQJXF9fl3l8MsoHXbedL7gZs9ZxhydtT8nJuMs5KAU+29plXfSDRl9zpHbZ912OIYsDjI4NDKqivF6v1zXn9NHREazX69rLVhSyLIM0TcsXHGL47ff70O/3YTAYOKdr4HOfo5WPdW2eaP8prvv3B/DP/tk7MB73bo4pLima6xMHGBT0Q9fTU8e3XTnQpj3lIYWBTymYyr8dTkh6wvJYgq/8g9dpuGn0W94VW90dW6yJvp2xUP7m9kJ+E4ClbVT9bmsHuHh4P4T+15/5A8NyHVSM/qe5+62VlZ7HiOR6Xq2wNM7s+Lvq0+0A3+nuv9+VrjGoD0rAbT2uG1IbUqJF8Uh6T5qm5cvqANVd6Bynz56T8lt4k9YqawAiSRIYDAbllR8I0+m05h/i5Zr4UREkm71L/TGm/r4yb2G/YNd2pgS3Tf+24Lbavus4kQTcR9lVANuXT5M922hjy45YC+9qMJY2nMXJ0FUQNSTAv6mTFaDZ2y7bXoT5M6l/Yt6U8IHP4I8FLvxihKEUCPK9XdIl312DzzESmveSwrgPC/q+AbYHPW5xPp/Dz3/+89LIePXqlXjx+a4h5i2pLvDHlkV+6GXsCDRA1/Uc4w5FzpeUnyohUuAsxKPknI9ZEzkdbjRzh65UJ1pWa+8mjm8Jny+4QesiGZnWAAjH32SsWILf2wSNPu0HfD6dTuHVq1dweXkJk8mkDHY1XacprV1BTB/vC/j6iIJv7mrzdpdtj/JUc1qFeJF0Ua0eof6VZGBXumVX4BurnA967LIFrzZncd5Lxzj7+svnvAzR9ZUP4bSk+57HjDmeRtdlC1gChDG8+fjigC8grNdrGI/HcO/ePTg9PYWrq6tS/1mv15AkCYxGI1iv17BYLMoyh4eH8PDhQ5hOp84VEr76SfwhviRJyuPJpbU6NJ64L6GpkyTLEuj1cJzT4EKV1gTyHMsWwcPq/92C3fNNA5xAvqV8GmP4jJeV0igNiW4ufKT0Iq0YezToih/fzljXB+DawlVQVB7WvmBnOK+tXHtoi7tJeSzDv2/+qXjltHYVkPuvbYPvRoeV5r+UFvJhcX3Btx7HBhG5vyPGByj57xDoyRCcH3whCddJSzCX06c6lw8wT5LU7ypEnVryrzexLTW9z+c30HDwNpHscFoPDfbZ7/mmQRu7jJbv2taO9cvcNRvfB1bbQap/jK8xhgcJj8Uu78Jv2yYuFZNfarsuxvW2fWZ7tzM2BG+DPfsBTZ0ZseW3CaEJuu3A1F0Bn5MFHTP0bq9dtNM2Am7bAKrA0gDiZDKBP//zPy/zpWnqvCHZ1Pke6+BqojDvOhBFZT72O955ic/RUYg7uLYFVuNGCnxSh7mln+gbV9QADMml0LjBAAt925iCT5mR6un7r5WJdeTjbiCLwRoD21QKt8mjloZjDGlfXFzAp59+CldXVzCdTmG5XDpyW+Nbkwt8TGz7nt27DDEOEATJOcTvqN7luof9m2VZOQ+7BIsDCIHv1LgNsAYnQ+tErBNQKu8Lzob48+Ftw1dXwNdHq91ncZJSnBb6tEwXEKKNz3u9HqxWK1gul3B4eAjf+ta3YLlcwsXFRakv4rp479698hoClBcPHjyAb33rW/D69Wu4vLw08yeN3cViUR6vuFqtanf5Ir8AIJ62oMkuGnAOAQbD0jQhu2IpD9YaOlgdHBUtFx/+5zR2P0Vs/O4OfPqf9FsLnibs20JTC7JuhPQNJMkG3Dtiq9+bTX5zLH71ke6N5TtjeZ3rwUPOaz2fHlzUy2htEhZRIZx2GefWYftByjbBXem37393kIt9uWugsoHLMWqjUqCBx6KcrKvRb4Bq52rIxrXY0nyeSfaGpkNL83M0GkGWZbBer2G1WkXp1HzeW8vxfPib75hFHUezqZrqY031ZrqmcxukLU9v4c2HGD/PN8m3HhtX2NYc2+Xc7YJWUz84B/qyjrUvQj7vLuRiVDC2TYN2GTx5G5DVoe0bDBR8ChQ+9+XxOVFixoMPB6cXwz/PE1IONT6szvLbgiZBUi6o6LGWXbxd0laA7UP7xvKABgq93wvb2aeE0zYLLTI0r+XN0yZvb0n4fQZKk+AtL4O08B7B+XwOg8EAnj59Cuv1Gi4uLpyyNHjZFCxygM8tfBaiy3FIbcSPnw052TUcCFxeS3cTU1yhoIPVOU5hm+u2Nk+sCpZFr5D6zCf/LTQ0pTA0hrizgQfver0eXF1dwS9/+UtYLBawXC5LRzreFYzGtrWv6dwKHfEVo9i21S27wNMV+PQQSV5oQI9W881HjrsJr5KziNJrG3TnAVZJfkq8WF7A2LU9ECPTbsshEatfa/Rvg3fKj7TbV6NtXXu18hINCZ/Ej7X+PpvIt45g8FXKk6YpjMdjyLIMxuMx5HkOs9mshqepfs1lz2azEe/wQ72W2ghuoKr+EhDHbbm+oaBXfbvPzFWEPC/y47eeLxS493JrZ2gPIDSM8XmeY9/R3+4O0iqwSn+D59vEofO76DukwT/V8cP1PO4RxZvNWjiaOP4DUF+raZvV6yr5NYRaG9P8efz32tJncj77vbj+tLi1QqcZbsumNO08NMON82bXUMkq10en56+vh3y95Os1xSmtkbRMyKbF37guSHqottYAVFdEIfAX3il+Depz3F3HeRsAFC8v9no956WlkI4t/eflJKD4m9gAFv2G+qko+AIb32TYl7YI+Q63obdTuKsva2+r/yx9oMnGrn1psfYhpRlaF2hZCx2rbyPWJ2bFT+lIdZNeFg35nTRcGgSDsU2M2y7AqiTs0glD6d4GbQtYnHwIkqMV05PE3WFD8Utt4HPq0ecSrzRv12NNcvqF6PicPm0cf/uyQFuB8ouKJb1vyte/PiEUat83FXDB6vf7znhP07S8C4xCaIzyNud9QwMtMQ5ALCP9pvhoHbihokGoj7VFFI0apLNYLKDf78OjR49gNpt5d35Y683HNR/fmmzTwGpEaf3DDSC+uIdkmlZHzI9HD1rGQAi/NL+1OR9aU7qWATEyu4nDuivcsWuL5Bih/PR6Pbi+vi7v3kRAeZMkSbnjkRrVEi4EdFbSt7rpvORj2lIHq4JtTb9NiJVvPuDB2F1DyAjrgifNkRYyZPZRT/AZqpostuqhMXp9SE/1OWgsfdpl21vmvnWcWWVOF/Mztu2oXLTqI3xNlHajAlTB2NVqBePxGJbLZZAP33PN0YI7YzEflfl4rx+fz5o+hzSos47rk7qDg7eNt0qdwx6Knk4hz+t1LPotL58DCaQW48ANWmCZCg+W542XE1wm7mo8VXSkHbE0D/7flGnguSuWB2erXbH1utK2o7/xP0+Xfqs1DgY4czWfD491+Q7j1TLobdMFXS1P16pSU3yx/Wyn376COC81XavIY9f1pPlA8VrSpecA1YtI+CKP5JuQ52JxFPBoNHJw8Ty4fkn80Hr5/CCuzKvuisX6UNw+ejS/BbS8El++fBIuqbymx7zpvruuoWl7xdgBPH+M/RTr25B4k8padOS7AL55a5lroXbtyodggab+lTZjOGZt8fkoQ3S0NN+6xNeIJi8UIK6YebSzY4pjG/MuTcxvCvicTfR304njezsN6e6jA/abBFmWlUeVNekLq/PxTQa+OwCF/XA4LIOylsAaOgq0+1OXy2VpyLSZN3zhxOBN00Wyazg8PITvfOc75a7Zzz//HF68eFEGvanhYHVmS7IopMzGGFY+B70FB3fK+owmXl4zEiV8ODY12dxEacdyPqerBDFOec7fNuE21qVYBVoK1AK4R0pyZzgvz+cF7rKV7kHGOYfPtmms49vvq9VqL9/E1QwPq0GMv6U2jDXOLbDN8Rwy0KkRg8Gfhw8fwmAwgJcvX8JqtXJ2N+wD+OZLCLBPLYYv4g3xQvNbjf62TkELvliwGNM0na/1Ur4QSPzzeYfjE/WqbckclGdpmsL19TV88cUXMJlMIEmSUgcHKF6qevnyJazXa7i6uirvhz09PYVerwfT6RSSJClPQ2gDkiNFcuZSeSU5sUPAyw2HPfjN37wPDx4MoNfDgGwVVOhK/CHJInB3e/fGNqPrBjtDOPTn1Q7IPK/yFWl5+dwNTG4gz6v5V+xGTW/GQF7irQKmCfnGIGlCPjn5lupZ8VLUgeJ2d70WxxPjf3lHLK41+F0/npjeI6vvnK3ahvNZtWf1WwtWxshOPRAbI4JdfsM8hejR8cNy1Z7J36H10PvYox/7cYfxep/6C0dBPK6mS27Irye1Jd31ib4GH2hBSw0/TaN3ofNd6xotLDccDuHBgwfQ7/dLf8B6vYbr62tYLpfOi/FNQWon9L1QnlCm8GP8u9K9NFza0c60/aUXvaztrPke3oIObdoI5w69Zor74vBaiduAtz56G+xivmi2aRc0m/ryrYHYGB5jx5zWJjE+Gc0navHNSXScYOxdC3bFOvi6Dh7tGrqavKHy3BEhlfVNKKuDKQYsjnuLcqe9EWHlgeOLwaW15a7AF0SXQBpvdIesj06orvs0r3wQGnPWMcnL0LZFZRjvAKNlfc4vNE5QkU7TtLbDjeLx4fKBVgdKmz+TeJCgaVCJwmAwgMPDw/L/2dmZOj/byCbeZyG+ucHD81h5sPaRz+kpyW0pH08POaWt0MZBLvEYUujarBU0vWlwISa/pARa2qgpj9K41OjRfFRWaUEIjReNTowuoj3HwAg6OzQcsbTaOESkPtXmo3Wt1AJQXQDy4Lv7XZr/ljHny6M5ohD3ZrOB0WgEBwcH8OrVK68Tb9vGrbauWdYUn/yz8M1p0DYKOVNjoIncsZaLAb7uSDta6G+q30jrtMRzyBHQVIf1yWMrfRz7WKfFYgFnZ2ewWCycuQFQOFsvLy9hs9nAfD4vnb2TyQRev35dBmGlXaht12WpLJ8TUp2ltUtq38rxmMIHHxzC8XEGaRri01KP3Jjv9iHPbycgTDgAKI81rQJaxe/c+U0/AHQ3qoun+k+/dbr1MkXAHOlUz8Kfqkyu7IClRxVXAVmuD5cc5e6nosXr1sTmsKUZMJlxaM+a0bWU7cJPpB+hzNP1/zn57+83v+z28ek+j23TLnxqSfkSi1+/wzRpPeX3ncbY61agOHFN881DShOf93o9GI/HMBwOy+AV3rOOzyUbJ1QvpMPzYZpki+DLHhYI6Q7WMphO+1bCI9krtCzH71u3u7Kj3kIBWr/Sl1LR/+fLvy3YNb1tgcW/ZylvzcNlRltfCMXhk8ex/qLYtuD1aTs+rPIj1h/C5ZmVX1o/ac7RE2Ul2cnxIOxsZ2wshBq2S0fUW5ChiZPbV7ZraDIGmioKPmeQdJzzbYK1XTTnElV4sd74xvDR0VF5LNpyuawdgfmmQagdu5JBm82mfLuUGgj06GGNLuZ59uwZDIfD8jneGfnVV1+VOPr9fhm0DclXBO6gpwYF/89xhHa7h2hzwLtY6Ju4vV4PRqNRySPeEYMLbL/f9/IpAd35qzn9+f+QY5MbkZoBFAJJhrVxvOMOH58yiru1sW0l53dT5eZNXsu5wR5y6McohNTw57i1sdsUqGMcd6IOh0NYLBYwn88dXiiEHCcA1drKg7ya8c/xUgcRBmU1mUlfetnWm8OWNYMHY+ncswZsdg0WY7HpPPaV892XeRsgzTXJkecDTW7G0N0V3Ga741yO0SO63qnqc14mSRItR+hpAppzmdLs9Xowm81gNpvV9MEkSWC1WsHLly8BoBpLWZbBbDaD+Xxe6jLakcAc6FjmskkClNnL5VLVIeh6pAe13Pz8f5oWn6rt8Zn73QTymwBMtc74cd7WlMjz9rQpjqqJse5VALQYBxhklHaCVsf3FmMMYL0uxljx0i5AkqRQ7EJNb3DTnbGYBiQtIfT5mKsHyOT7YqUdsvUdsXm+KXef052x6/UG1uuNc48sDcoW/3lgqPrkedWutH3pkK/bEPU6SsuCm5ab8/mWGBz7Gi4fbp22FhiSvw1UWX65nWLVJT2ApadxelJfx/BT4QiPCQufYbqJ8416cSjwiGsIDzQ2cWQD1E8Ik+tRP/pco4M6Aj21Ddcl6sPQfCo+fn0BRwkXXgPDy+xKl/IFfXzAbad9sj++yUD7cDgcOn7Z2WxWPkPgdyXv44lR+wpNYwQh4H3AbUgObf03IR1bordvEHrRw5c3hkbIP8xxS/ZoTHkJasHYfemQbQRBuq5bV5H/WLBE2X0LsCZsLI7TJjw2dThLuKx4thEIkIRnrHDYxliRnIOxEGpL7oinTnMfviZwG07Hrmla8VnmsgTUGYjzFncQAUB5hxe954uX1eZ7KJDjc+T58sc6nylge6LhiLBer8ujndFpL9GWcFloSr+1PFYaXchGyenJ+ZWCOlq/+hzOmlLnCyj46s/rGDPvKF2t7bqENmsLz49ltqkvSG0S0zc+wKBIv9+H0WgER0dHcHl5WR4hptHy/ed48zwPHjcs6S/84zOoLG1kyRPDH/9tcdRYZc629NC2xmAItwRSAMeXd5s8+kDSuWLnFeU9VE8LP4gnJs++ONuksc+dsCG9hJbV8HYFfB3y6c2cJwD3xRPLPEe9Bx3i/Dm+nIb58IW79Xpt0ok0HS00rmg5qS6SvPPpFL7nSVJ9YgHR8bJ5fnuB1W4gBzAcTdy0nvLQdOel9KkCLTnkeQL1HbLSt48uT+D4tM/G+V/wjcHVzU0g2d0hqx1DzGkW6fibBtX87VjXiep1DItlPQMvG/pvAb1MV/4iP06Jfkw9trHMuX0qPav3s4+PEI/bqAPXO6y6xmAwKPX1PM+d9Yfi5uuHtM7w3UW+tUF6ztcNajvQdQnXQ/rSUAg3rQun5ctvwRUqb4GQ3eCzRWLb2afPvIUCtqVv0vlDx570UjHVAUO+/ZDN6RsvXY2BXft6Y8HqL7OU5/ae1r6xsYOm/NCyIXkQAqv/wlK2CQ4rnZC9FWM/avKyie/ftDO2qfNuX8Fanzet3k0g5PzAAB0X9vy3zwHAYdcLfdt+ttwdlWUZpGkKi8ViJ2/ud4kb8adpCqPRCNI0Le+lsgC2L90NEKJpxdkVdN2GXeALtVO/33d2idJgbL/fh3v37tXuJ0Ggb5FKQB2fOF5xd1KT3a5tgTv6kqTYkfLq1Ss4OzuDL7/8Ek5OTuDx48ew2WzEu2AsDutYoLik41s1Z61PwZPyaspZyPkuGcR8Dkr9yZV+XlfEwef0NoIjmoOY86SVpYbMrtd0Ol8sjvcmvFnKcAd+kyON1us1ZFkGT548gQcPHsB7770HP/vZz2AymZQ0kBf+5rx2d2yeF8HXLMvgwYMHkOc5zOdzmEwmcHV1Ve7kp/ml+mdZVo5ZDA7jMwxGUKcMPvMZEFpAtsvgp289lO5r5nMN09qO6TbB5xiw4KTzRNIt+Xzigapd6Y++ORs7TrRgrEUuhIzopn351uEmA11XY9sIr3bIsqwcq9QmkBw0y+USPv74Y/gH/+AfwKtXr+Di4gK+/PJLmEwm5e4HemIFlu/1erWTAprKCe5k53VCuYs8WBzR2jONV9wVWzwDAIg/WaTquyo4SX9DGTwsfsMeH2Xs8i3mAKjde4tpdM2o0jC/GxiUjyGmO+QqG2ENAFWApZDNAMB248ntmpTper/mwrf2cXfDFgHYdXlvo/tN74wtds6u11XQFutbfNN2gLJtoAz4AvkOBZKEGioixZK3nscvn3hfy7j8Omudbu6k+fHI7eF+a/gkXt00qbz7TP6vpVn5d9P4mNVx0DFF8fn6OTwG6lCsM8Vv33UjWtl/9I/+Ebz77ruwWq3g8vIS/viP/xiWyyWMRqMyH74k7XuRp40PUAvw+uCrr76Cy8tLWK/XZTBZKrsrG9Gqm0n2eAwN+i0B+oF8fYIgre/fdD95l8DbF33H+Gw6nao+ZHpy3Gq1gsPDQ0ffRB3tLewWJJ/eYDBwfBP8JREJJJ9A1/Cm2H1aG8XEXyTfinYdVlswBWPfNEFrrU9sviaDuEvHngSWwJfPcNYcPb4FXgpAxAZkfdCVE5TzEeoL7iTgios2QXlAxHLXxj4Bd8TgDkR+X6impP3/7L1ZkyQ5chjskXdWdVXPdM+9O6vhco9P+5ESKSMl0UwmmfT4PekP6N/pnS8yvfBFRqNoRooiKZHctZk9Z3d6Zna6p7vOrLzje4jygMPhDjgiIrOyesrN0jICh8MBOBwO9wDA83dR5/suk6R2srYLbVPqNMVdogDuaMfJZAJvvvkm3NzcwHw+9/hPcxRayk/Vxxqn4dacDjzdzc1NXZ/xeBwc2aotttpAymjIw2J9LTkL+XMb+i2LJkk2I19R/pLqzcd8jvPAsujT6mENp7R34bSS6NIcelw+WvimKWg8mMtDWjtRgz7KFlz40buqm9CLpy0Mh8Pa6Ml3c1lopnhpHbjzLteJ0nbOiulQg8EA1ut1faxZzOmh0d01H+XORUgb/Y/xvWUc0vLX6zUsFot7oSsBNJNvUvrY/CdBrn4tyfz7AjEeSukiUr3vgrdSay8At6YYj8fw5MmT2shGP26R7nnGelqMq5Z+t6Rp4oDlaXibhH0lOSBzHbIVDvyX4r7ZUIJzzpLQkjsgaRg6aKt7Yv1wd0xxUTu56f8WXB+Wt88lhP1asueSPeNvW4c7JyndDRv+qt2w8j2xiAfrDMzpSp192GTuPyaLtLrZ0lpEFk8Ty0Pj3DPngbzyuoAQZxmJy8JsTtNtvdqtSTmPdQ1cb67KqsbA8fExTCYTOD09hePj4/qDH5xn8OM53K3HZbkm27lua5kb+TMPQxzogNpsNrBYLMRdvBpd0nqiC8i1+6bqLMnqpmDthxg9sfAHyIeiKOqP3k5PT1V+QP7G8STZ+yh0YWNqAvedN2JtZa0b7l7GDyalo821I9U1edS1neJQIGdtEbM9NCnDmjZl803Bwd4Z+wD7gSaGYfzaLWYwReVM2iWGgMKIg7Zbb5eQUrboHZIIqIhifn4kLMWJx0nwr5zoLp77AkVR3VuAhiCk37orxbpD9psE1IkYM6BJcfh+cXFR71g+OjqCJ0+ewFtvvQVvvvkm/PznP4ef/vSntRMFwPGsVdmmzhhOuzbpWe6F0UCTTXTRd3Z2VodPp9OgbKkeTWiQgC76MK0kFzUDMMXPFYiUPLIYWTnEjJwAvtOKLq5pXrqDJ1UWp5GO+S6doinHr2bo7QpiixxaNvIsve+YO7+s9OUs4lPKfE5f4Becq9WqNnIMh8P6K/jY3Z4SX/d6PTg6OoLxeAyj0aieU6STFzQFl8sxzueUh9HZ24QXuaOD3ttD+1dyTPNF72azgaOjI3jjjTfqo57xpAPrGDtkaLtYpHx7dnYGvV6vPor+0PUGi9OZ8+gh93UT+dQWrItnSe42oVFb1O/KuJErfx49egTvv/9+LeeePXvmxY9GoxovHsVo0b1iBgQrbRb8vL4p4zKnqSy3gSPWz0ZfqCMv5txz6d1uWFq2K2NXw5OWEUkFnHY9HzpSeXxVR4AwXGoX3zHHd8ZuYbut2mu7re6ExTmrcsL4H3lW62QsF8tyfVLRVQjxjg5HM3fkckcsd8qWNX24BscfGkCru2OrO2TpztjKIbsl+kJ1V+x26xy06JSl/C3rW5TmPOeqNS0PCx00Ok0xMZcSgRZasJ1onPwfpkuXGTqiXDxHEq8M7ye/rPjduzEa5bbPx8NigjRtp2eUzbibDmXzcrmE+XwO/+Jf/Av44Q9/CADVWuC9996rT0tDu9ByuaxPy6lo0uc5bcwgvph9Qpuzke5er1ffp462RmlnoKQzaOv1XUDMHrZP3dDSDlrYA+weFosFjMdj+KM/+qP6BDxuz/ubv/kb+MUvfgEAu9Fp28AD34TQ6/Xg0aNH9UYayX5Kj6HOtVU8gIOUXcYKmqzW1vvWPntwxnYIlkbP+YIpx7ueo7DsCixOBAr0653UFwy5g4YrgDlfRlhpoQZZbkDVlEz69Qs1DHN8+xammqOGG5e5IKJ9l9odZTFMftMh5TCTjGcYNp/PYbPZ1I5yjBsMBrWhH411mD9WnlVWcWdkqg4xyJF5PP18PoeXL1+aHDltIEfGAcTbtQvDsXWxqyniEk9phlOap+s2pbTsu78s5eUYthFnU6cnp8kyR9I42j9056qF9yTnC52bFosFzOdzmM1mnmM5BkXhPsxCwwjKpOl0Cv1+H25ubuoy0bAjtQMPQ6MqL4/+MA9+iUpPEZDqTemIKd5Se0p5tDEk7T5IjYVc+WOBrpxtXG7E8Er1k5w0aDjXHEep8aU5hLoCqa+tej1vJwv+JpBbRs5apslcaNHzc3R2LYzzTM68xedOHsfHqxVQB5PyazSuViu4vr6G+XzuHWtMZTzFzcEy/2lrsZhhWmqjVHvExi7P2+sV8NZbY3j8eASDAcrK0HEaL8/mTKXprHnaQFdlNMWD+bDJKxzOMe36wjlyMR8A5X96DULlqK12yG69D/sqFqU7ZB1Ud8pSpzmWTZ3qwJ5L8gNwO2Kd8xRujyfmTlh0ttJdsXh/LL0zNjySuLzF6drC51lNfoWOs7BeIVjT7moJTfFqz7lhJLYpWTI2BR2nm6dDfk7hsZRlyS+3435tIOH48/Uu1E25fMZdsXjcPl4jQnU16Q5LO12+zp6aU1Jra3xHOWQ9wSelK+TqECnbyl3CIdHyTQe00QGAN2dRO+xgMIDpdAqPHz+GwWBQf4gHAPDjH/+4fi6KwtswBFDpkU1sNym7USzdfQFtfdkUhwbj8RgGgwGMx+Pg43VqS08dhyvJliZ0H0KfNVl/xnB1aUfcFQ5exwdn7D2CXRjhKG4NdvU1DRoOYrtpdlGmNomgsdYKVGmM5aMGC+lceIrjLu7jRNCEOyoDuDuF7jSiO1VifcidOocwARwqxBYekiMfwy8uLqDX68E777wT4On3+zAajeqjemLlSHExIynv267GsgUPbY+zs7PaQbRrXpN4Xvoowcr31Bgag5w24WWmxildvOIzjnW6OLa0p9X424W8k9ptV3OWFbD89XodGCy6wg+gOyOwTbBcfp8xV1glYwxfCOBC8erqCnq9HozHY7i5ufH4SnM6UtpQFh0fH8NwOITpdArr9Rp++9vfQlEUMBqNoCgKePz4Mdzc3NTyiuKnu1vR+MPnY6w3/fK83+/D8fFx/eGKBtQYo/Ub/0hJ24lmnRe5g4UuovfFy13dSUN5ou3CFvUPSU9MOWLvGnKMebs0AOSkOyTIrRudwzQ9v4t2oLI0F/j9UJKuxWm8vr6G3/72t/Dq1Su4urqqPyThTtSmc6qmf8RkOnck54CGl9PU6xXw//6/b8Jbb01gPO7d5gMA49HEZdRRqd0PS5/bgh2X1P6UfqkuOfEhTX79q64sbh2jUNONzip0ThZFWe8OLYrqCGK3M3ZLyvRPpKiuNCih18Py3b2yQO6LpWXLtOO/5JR1v+3W7XZFJ6z07++IdccWu52xbkdwWWJ9Qn3ZtVX4TvuD10UbQjlpw7xcpki4tbnJL1MpQQ2XaHTtIv+nyomlp2H6fKtgN4drdwDT8WHHz9PIeWkg739Jf/Dppbgl/Jr8pc5YnnYymcDjx4+9E94QNpsNzOdzUQ/WdGmuH3IbmLbuTNku+HyG64XUOoyvVdo6BWL4U3Fd6vxa+78udrhd2sb3Bb1ez3Oszudz7wQqCtPpFH74wx/CaDTy8vyv//W/vHSj0QgGg8rNs1qtYD6f13HW8ZQD970PELp0NHPbyuPHj4NT/Dis12vxQ3dtDL8u7Z4L1j7JtVnG7Gq5YJ0HHpyx9wxoxzYZgDnMlHKwpIR5yhBFFS36PBgMamNHDr2ak1MKsyglUn76pS8aCQeDQW1E5pekD4fDejIsigLeeecd6Pf78Itf/EI9MuVQQKr/1dWVd0fI0dERrNdrWC6Xwd18AHJfP0CcN2NhsaPnsE9ubm5gu93Cr3/969rpsVgsaqcQOok0h6tmKONORimvVg9uMKThlnEbC6f0LZfLelx2zXOSY0F750bSmJGTt4fmaLMqiLzuFmMvQHjMeMoZpTlGpLJic4Glf2LORxqu4dqHXNXKSNFuNWxb6yCNzVhenp4eDSbpANvtFmazGTx//rxeMAKAaETh5VAHKd4Vi4D3p97c3NRf/GpOAl4ffjIFH1/OyFrlG41G9W7c5XLpfXSQGuMxoGNeyo/huFt5sVhAURQwmUyC8WelYxe83XShnhrr0rvkSKP4aHxsrPBx1rWukRpXTeebrhZ4FKRxS+nNpTHWr1I5GuTwsKVMTCfxSBOwymBaBueD1FzH88agKKrdDRcXF/DTn/4UZrMZLBYL76NIAP9DjhwncU5/aHOCZU1q1WWkcnu96hfn2RJSTs+ylB2X/LmiL4qqJcjHIsfo6qpcuD3CGEDHjfGUR4sC3/l9sOiw9I8r3m5TfIA7ZQEAeuB2xgKAtzNWqgP/p3fTOrtBtft1S3bDcies0wt8R6zbESvtjHX//hiWxzSVC2E9tOEvpU2nwzA+z8bz0HcLPZb0fpyUSKJRP8qY5vPj9HaU8uZA1c/xeP6s063hK4N4vQ9CXqJtkEM7Qkxez+fzehce6u2z2QyePXtWy/3nz5/D+fl5YMuy6BfSeOG6ipQmVx/n9KT06rZ2g5gOq4GkOzXJQ+Niul5KR+LtxddgbXSsXcDrYFcsy7K+8gYA4OTkBJ4+fQoA1Xrxn/2zfwaPHz+G3/md34HT09N6DQsA8POf/xw++eQT+M1vfgMAbvcl4gSA6MfHbSBnrN83sKxpLHmk8YTPi8VCtMfGcHO4r+0rgdV+FeM73vYxmyq32eTYypqChOfBGbtnsBqVu4LYoppDzBmk4UkZXiTnC+Klg4AaFvDeNLrDTduynzLScsUqp+48jhtLkT50TuLOQ6qYDgYDePToEQBUE+oPf/hDODo6gs8//xyWy2Ur40UbsPSbdGTy1dVVTc90OoWnT5/Wu5fQyC7t/uW4LTS9DgqWFSQDvrRowOOBpPua8avP2WwGs9kMXr16BScnJ/DOO+/UzliKRzPa0b7H9BwkoyS+038tnMsEiq/JYqgoiuC4Un7MkiaHpHrxuNTET/PRnXgYzmmJKRwxxUErn0Oq7SQFRLtTQVqIxfDFjNMpZ5fFOG1JJ9EHAKJMs+LSlGxprGo83FTGxeQmx8fT4QccFj7Bxdtms6k/MuKw2Wzg+voarq6u6vJQJmFbSLuzMB11xlInKDpjr6+vYTqd1s5YlHlSHXj7YxmUbnrkD/I4fjGMjg3Mw41Bmhyj7UWfNTnH5RvKiPl8Dr1eDyaTCSwWi9qxLckrzsMcb5fQhR4qyZgmdGqGNKt83jdIczl/1uYlnp6mldLE8Gh5NeD9k7NI7Rro+iAWj8+aHmItCyBvbaDNlxJohgELoKx89eoVvHjxosbB75emhrYm4wzpivEaXc9JbU5x0bypdRvXlSQZ7PjcXqeyDB2b5a3zoqJ9107XNlCC7lwO47S6opMzXld6NDGmc/kwvuoLtzsWjyQG6EGvh8cTVw7RzQbz0rFV3jptSyiKHhQFd4JTAiViS/Zces943DA6Wp3jFed+3ymLaeh6iJ64QZ/5z7V7qfwzyr13OU2TtGFeq4xO4Y3djxrmRT6RaTKSlCinCZ6QRvfs3kv2HsfD6UuFtaFbqnuct/x68XCeVpPh+KHgZDKBk5MTGAwGMJlM4Pr6Gj799NP6w+d/+qd/qu+HtZzIEFt30fypDxNT4Rgn6YiWMaLp+Sk7QE76ttBGH4vlldYwufU5JH18H9BVfbkz9t133wWAyo78H//jf4S33noLAMJdtJ988gn86Z/+af0+mUzq9TPaxHKuKuLwTe9PTU42wUvH02azgdlsFjjKLePzdYOYXVRa2+XY0mL2OG2tbsHB9T4rSHV9cMZ+w2CfBhWEmCBraqC7C9AGId1JtN1uPUeZpKAOh0P4wz/8w9oZe3l5Cc+ePavLwK/fESTH2y4g17i5Wq3g1atXsF6vPSeYZFzkxx9Kx2Y+gAPJwYNA24yOH35/MQB4R9pxHJxXefkcJPwUNGeVhJdORjFjXRcLDs1gzQ19sfIsBsjtdguj0QhOTk7qvrm+vg7usNUWiRwfdepq9OUsLCXjteRA0mjKgZjMb7pA7oqGfcgazZGGdNG4Nm2d63SI8TfeAfXGG2/AZDKBV69eeccaUdDuNucLd2lsbTab+sik1WpVG0dvbm5qWbVer2E2m3nHcWr11RwIUniv14PhcAjj8RjG4zGcnZ3VOPjdum0MD1Re0LFG60EXytguMfoPWU+SHK6xOSyGg4I1PxoxcF7bx3UP3CHI4wB8+vmpFhZHo5WOJiDxmNR2FnrumjfxCo0YLU0clFJYrs7K+1yaD6x9Xh31Gt7lh2uJNkajWPvQD3p4HbiMp3MDd6hajB1h24DnuNOAOhtK4ogkKdi7C8N8+2djiSYhVeloo8+mEiLpab2xzcpbx2jVbwDVfbF4N2x563gFqHa0Vs+bTQG9Hp4W1QMAXBOXUJZ96Per56Lo3fJIUfNUr1cAgHxXJKP2lraS4HY/PF64OipZ/udp0OnjnLL0SGLZEcsNcOG/o9d/p3XQ+4KmlcO19Ok02H6ptDllWvKl4qR0WntY6InVU8NV5S2DNH79rLK1VNrF8YqOyqch3hel92yfk+R1cVFUH/rwNflisYCbmxuYz+fw1Vdf1eMNT2jA/LhBock81JU9qO16SrNZWGEfNs0c52junEvj6Ae2qTssHyAfJH7jcHx8XJ9ASD98oH0hHWmLUJbV7kvefw82Vxk0h2BuPguUZXV1IXfGxk5A/KaDJl+7krtN5L0EuTaIB2fsDqFrQ/O+B6dlEo+BZKDbJUgKoOSQyQVuXKaTGjXOYFp+XCNdrKGQ/eijj+o0z58/h7OzszodTpwIuYM6p460brnloBOWKtFa+TQN3Q0VGyM5Cud9BW6c5+EaxI6Cpn0xn8/h5uYGxuOxd08Bd8RRkMaRtMCKGZa1dBSXtBiUyo05kCTaJbA4XLuAfr8PR0dHNd2r1SpwxiIN1LgptaVlbFCIOfm4LJbaXFoYpJwMnPeaGKjxfRf9kjuuYqDVVau7xnMaTZqhXoKu2wtx9ft9ePToETx+/Biur69VZyydC3Palxpx8Gh7dMjSRQnebZ3C3cTB0u/3a4esNBfx8ixOQqsDiH+UhEbg2ILv0OdBbR7ghpscnZHnj+XF9rFeaxGbCzSHUA5IcyWVlzlzkYX3moA092s0SnRYeLJNG+bkQd2V9r+WN7e9uaGa92lOvSx8l6IXTxSg6w90JmF8E10nlofzRZu5v5kMy9tNHEIJmsOzLKmj0pquBSW3TdStKHd0V3Ta6hGm9fMhra6/q/tkq12y1Ejpjiru9fy70/22xfKqY4z7feQneq898oifv2TOJtQjKhrQoeqOJ3bvvuPVHUPsO2PdL+2ExfKRJr+t/Pr6QyLmgAt6SshPYoXwcNzG81hpaTLd8HaQ4vz/nLbxHZEuTNOfXNqKbzVa5XcpnPernr8M0uh42kCpPGtpKtDW+vQkmrIsa0cQOoMQhsMh9Pv9ev5xH1i4D3Iscwtf+8TkvSUuR0eS1r8antg6Toq3lq3RY8nP9c2UzcRi80GcVJ/AdVoMDnWNcqhA21ezx0wmEzg+PvbuZEZ9l/YNB7ouoqc1dq2n7dKedghgbS+r7EH9A5/X67Xp1M9vAlh5SZNjXa7JYmVb8HKbUmyuALhHztjcRm6Kd1flHDq0qXPbNkOj7Gg08pQhfnRxTHBpdJWlfGRiV1CWpUc/BzQwoxHlxz/+MRwfH8Mf//Efw/HxMQBUDrPT09O6XtfX1zujl9OOUBSF1/54VKWWL3X/gCYoJWcDFVq7utfg0CGlxGMYXeRIO1hoGv7lHD0ChSp0NG+uwdwK3GDPjyvFY5Galpe7+KKKKuXBJk6umGwaj8dQltVxpPQ4VE6PNGlLEzqC5hCM1Znm1RbhUj5NhobGqerdKm+bKEJNILdPYwvxNvMc5TWKP7fu1Plg2VVOaZbu9cbw7XYLNzc38PXXX8PV1RVst1uYTqc170q8SMuihk0shxpZkF4cJ9geeKw9jo3YSRBSW6WUcnqHNB6xjDRRmWg1BuUAx0fbw8JP2G4oK6V8h7oYThmFME0qv6UvkPe4IZB/GCeN7bZ9zvsC6dCM+RbHG5eld9HH2txDIWWc3Afwfm2q71vob6ojIG0I3Fgda2esE/4kyDm6XDKAc+M719di9aU6pbVtJF7n5TlaaJzkyNSdjxX+MA+tI42X0lpxNk2P1ac08PfwuGF3tHC6PJ5X0zkpMtwVS51mJWy3AL3eFsqycqhif1S/EjYbqhNsoSwd33Ij8naLVwOgM9bNkRUKnx6koaIpdJpKjla8C7Z652lCh271jvipTuA7gkvPgegcfUgrHwKxMRFGcXyxtDL+XPHkp485DvNoc7zj42hKX6pc2gc6bXL99HCpPHe0sfuVSn3ds0Y//8k8FMbJdec0SR/6FfVJOOhk4zoxrmvPzs6C07WGw6H3AVRszrXqubF3gMN0TnAnKEA7Xa0r/YnqKVxHw7tE+SYSrp/w9sbr2BBw3XaI/XJfgK5X8ANkhPF4DG+//TYMh8M6bLFYwD/+4z/CbDaD+Xxe9xmerEjT8VOlHsAGXfI0bXuUlWdnZ57+/7DbvAKJT+W5S4d9yqKux9W9ccbuqpE142LXZWiG730Lyq4MJjnKk+R84O/UYIqAxkeKL+XIsNKbMn7E6Oa4UJhyY0S14Nx6i9CXL1/Czc0NAEB9h+dgMIDhcFgLnrbHEucYOinwr7Qkx6iu3Du8Kdp4n1ryfZMg1racvyQ5IhnSqPOD54sZvWm8RE+MXi09/lMDI369l2vIs5YfN4akjw3mOGgf0EUpjhu8D3M4HNaO5pjhOtbOEqScMVodcseZRlfbBWeOzLYYwGP11Qy/Voe0RlcT8I3AsoNGq2sqrWRg1WSERs92u62dr6PRCEajESwWiwCHNDfG5gXeB3QBgotGXi/eDk10F942ZVntVl8ul/VHIDH9xMpXKRq47mIZ7zmywELzvkHjmRx5TecMaaxKYan26IKnNAdaahxIdOTo0rtcL7RxMnI5ZM2TomeXIOG3tkHKgQoQryNd21h1YKucSukZsWcJZypMo0mbl2L5qS46GBQwGvl3i+ZCWYZOzqocP47kgJhjN1UGxyPHmTGrtDTHGzpxfVzYSCHtVb8A4P2xGAZQ3h5fDOR/CwC9W3wlAPjHjzqeB+j1sK0KFuc7Y8vaEYU/AOqU1Ryt1BlL/6t8mKasy3C4y5r+suSOMP0Y2pC1Y3O8nC5HDNvkbfzdGhfJ1Sg/pg3/S+89loeml9JZ6eB9Qfs8D5rO0WEbhryWrqdWb14fnMPo6TCoH5dlWduk+I5IXPPyo1ItNrV9QY6+JMkbSSdLram1tVFTsNghpDjJliHZjCy4uS57lx8Hvm7A1+Y4V9HxRT/0R0ft8+fP4fLyEi4uLuo8V1dXHm56dR7CQ5/lQ06babo1T0N3KQOEHzkAHM4a/j5BbB11H3j/3jhjc8Bq7L0v5QDIu43uA4PlgPWYuRhIStg++okK2bIsawcrAt6PBwDe7iDLV/z8K7W2X9KgEoAGoc1mA9vtFmazGQwGg/r4xl6vVx9FjOXzSR6VhRyaaH5+dPF9hiZ8pi0aUo50i0JNF1k8LnU/IU2bazSk0GSXZNcgKUlNlB++MJHyzOdz+PLLL+Gdd96Bd955p77/+ebmpj5+nJ4CYNkJrhmNadvm7lSPydnYIjDX0E6dI5Z+0Ohp6iigZeTmz82XcqJaDQOpdsJjfkejEQwGg3ouwSPjLW2lpUGe3G638OTJk/rI7cViAbPZzMMfcxpZ60vT4jxDeUf6IIvjTskv+rxer+vjni4uLuo5me6QjeHkdeXON6SZfvyC5Ur0ccNJSr5gvq4NP01BokWiTZK3Vv7gJz1IbcblTQ7E5jUehnJ2MBgEcfzIS47bel8TrVPXTlhru0s6HudNi/Ehhrtt3ZAGKvsQX5cLcuu45IY1XibnM41XNV0Q8e5yzGtjgOrnmpFP+keex3HD9fyYjPjhD9+Ad9+dwunpSKUtBmXpO2Kr58rxx1KCcwRmFeGVkQrX0rYB7Ao8Ptjhr+oZK7Pqp+ro4apNXDtozjXcGVvtaq3S9XroaMXji924dB9cuiMv+V2xVTj2r6uD62/fGVWVUd4+O0cqdb5WtIa7X/07YXFOoTgcXr8cSY8L6ZLbWAoPQpRwLb2Onwf57/KOy3Q5eek12iRnohzmcJeBQ9LlkducP4dpq/7Uy+b4fFpsQPlFanPKOyRGLF96l/ohTZ9PS1EU8MYbb8DR0RG888470O/34W//9m9hNpvBarWCXq9X74ItS3eCwl3rnV1BbP6R7JGpuqccqNZ24+VY1pBU9lHbANohJd0BdYrRaAS9Xg9ms5l3Kh5Nj+tNupP6AZoBrh9Qt8TjhwEAjo6O4OnTp7BcLuGTTz6B8XgMg8EAPv/8c9hut/D8+fN6PTmfz+H8/LzGiYB2XYSux+s+fR93Dbm2Jy0tOtdT6e4b3MWaJCeNtHZNyWkrXdL80PRkJs8ZqxFy6INOMmrtA6SJLdcYqRmwuEFHYniJGSxGWGv7NDVeW9Nx/NLOvRQ9Gq4ULSmDkNUAQ/HFjOjSIpGmwaNDAACm0ymcnJzUipFk2OPl5IKmlAGA50TG3X1INzV6acfkpuiSF5DxetyHyb8JfVKbSTyZO2ZjBj/qULGEW2jW6JZoo+/ckG6Vmbl8nzJ+twE+7umXxePxGADCYwS1OmjGUEtabYzEjMc57ZLT7po8pseJauVZ5XyX/UbDpHrHwEK3VC7/t44Dml6aLy2ylNPGdQ1czKGDlMt6nj5WVz7XSXRqepCGl+KwKtbUeYP/0s5YKY8FvwUkJxZdlFvGRRP5d5dgqZd1wcXnDAkff7aCRo8kZ/FrZvoBBE+TS0NK7nTV57nGRK7bSpBa++T0sTUNxWkxdlrGTY7emrP+yuWFlEyNQayeGp9KcwiN42XnjllenvZOYTrtw8nJAPp9/Theh8d3OvL3WL6KTp63hNBpm4+LpSA4q+dUWbQeOXUqiljesLwY7WXttCpqvAB4fHToqKyWudUOWbfu9deuFV/48171g7ocgZK6bKdXALgjhWVnrO+E9R24Fd3u39UHSBzWn/67NuPPfptJ4WG99DgdpPRdqgU5uGJp8+vFM1jbUY6P9ZOUz0KvS5NDm6/vU16zgp82ndHxshTnTnEajUZwfHxcn+hEQdNLrGv1WNqubDpt8DSZY2N4uL4k6ZLSeouWnUOTZU2s6ZIod/GjmaOjoyBuuVzCYrGor1my6FrfFOiC7+iaFADqDyBWqxXM53MYjUb1mCzLEm5ubuo5Dj8ultboFLrQv6267q4hR+7k1CcW1wWvU126yfrwUGGX9WiyBubyqe2aLoe+FP4Y7tdiZ+yhMPWu6KCTtXTh9z7BYghoCqvVCoqiEO9epfj30d9cIZFo4EoU3fnA71zVdo5uNhsYDAbw5ptv1mFvvPEGbDYb+Id/+Af4+uuvg3bpuv+pw7ff78Mbb7xRl0EVgfV6DT/72c+8YyuRHqospECaiFI7aw9ljO8SchTpmKEMn6nzgeOj91RQPqa7j/iO7Fy+4w4GbshDRw86+/c1tmMQm/w1+iRjNX7E0Ov16i8bdwnYz9JdLxpQHtCgq/5IGecl3uL808QJ1URW7kJRxv6RxgL+l6W7e4neD5rCqcW1hS+++AKKooBHjx6Z2yOn3ZBGHBtYF2cwDXdC8nJynSt0lyXfiavVQZNjljJjfE3fY7j5Ao4fi07z3aX8TPGq5JiheoeGT5rrOPA4ugsrRSu935jHa7rf0dERAAC8evUKACoe1u7dlIxzWpyUJgVtx3rMyBeTMU3KSfF6G9xd4KDrLB4GoMsGfmexxjcSvVp7xMY0p5OXJZWDdEk7JjjvSjg1XuBHVXK6+VxWlmW9M4SuPej8V7Wj2zUZc4yWpXM+VrikY3j9naPlrbMsZRxD3DlhlB76n8Id0sRxlmo7aGXw+gNwmrBdKtzxcVQ5Md0cJPEnPTmjIFf0FNDr4cfWVZ/Svqbjg/a3q7/vwAJh9yrdIQsQnlSg/Vxe8MJcOfQ/XGfxtpacZHKzlpG4WD5Z3klp/bD4rthccO0RD9Piwna1linLyYoXeDxvY2mXdVCKl0Yqh5bHf1JdffqkepUCLp/nfFy8zpyu+G7cogBYr1cwm83g9PS0TkOdcwBQn5CGp+UAtD+Z7VAgR1fuyhmTAq7PW4Cu/emmDgSc77nteLvd1rtdASod9qOPPqo/IEcn/bNnz+Djjz+G1WoF6/UahsNhnacs5evMHiAOdDxpOtfFxQWcnZ0l1z2vw6mCXUMTG05szN21XfIB4tDEp6CBxQEcs0U2Lfe1cMbeJ0hNsk0Mzl3m7xJihkX+TNPGmFkadE3rq+FJ4ZcMDpqxUeqPzWYDn332GVxeXsLp6Wl9nyQuHqkjh5eL5cUM1bn1poBHqCIMBgPP0RozWGrxEmhtY6XzdQF5UW03ymmQ4zzhRjjNOJjCaamLFJe6t1ErO0Zvl05E6gyU6JOM7+v1Gm5ubrxFz9HRUX1fNKVTM+6kjNa8zFhaqQ5a/aSxKY3VlCGT18kqp6147wOkxpSUPscJ4xsV/TK19xQtGI+GcjyuTKJRG3M5PE35T5pDMEyTERq/cno5n3MjMKUhBTH5lCOvJaeNFaSxu+/xYpFR9Nky38d4WcOt4eLpJB6SxlyuAUSSlc5pEd6rFhvnuXNhrA9i+nIur3MZEKMphos7OqWxbQEpTxM8lsV2Si5bPoKicorLCEtfcDll1bWl/BrOFJ7YR0La2OLxEv+GOgK2T0hDWfpOOrndS0DnYoXfOSE1vBS3X4aeXis3mSoLpz2vHyfTklO3qv0AqMMWw6nz1unyVfrqKGMn/6o+crtfaRu7eRhq/Hr9aHnOGet4TTtyOO6MpbiwXXgYLZ8/0zxSews1icTF8snhlrCUiKDxOWVbcKb+rflTYU1prXghzG/HEdNT8sJTZYT11xzalFf18t9880349re/DR9++GFg/8ErUaxrtwqvbW5IQRP92QKaDZLa1iQdvomdIaXjWddtqTVFTIeS9A4A8O4jpWH4cfzx8TF8+9vfhsViAR9//HGNazgceqfloZ3jvq/V9wm8b+l6Aq+MWy6X3tqBHhEes4dx2NU4OgSI1Tk2HnYJFlvYA+wGrPNMzhwVk7ld9me2M3afQtdi5D0UWnLSWoSm1Xir4bmPgx7rjPe48XD+TMMk45DVWBeDHKMTTgCpLyMWiwX8+Z//OTx69Ah+7/d+r/7KbL1ew2q1guPjY3F3MDf4pL5IazI++v0+TKdTD4eGOwWpPmjKo6+j4qcZUrnizw2ikkFee5bKQCVQugOEl68dg5Iaf7wOlJ6yLOtjViy8EqtLm4VZquwYbnrvMdZnNpvB8+fP6zF7cnICR0dH8OzZs1q+UcVaclrEvngsS7fTkhtltfbWIHYfeVfzSBM5knIwWZSlQwLKR/xZg5QiyHe27wJubm6yHCa59NC2oLyIxwiXZRnchxOTVdpCldYh9qETH4+SzMoxyGA+La3VSUJ/9GOsNrririC3j2LGhVS9Uv2u0RAzfmnl4ByM/MjvB6Y8hB+y4VHG1AmZkm1WmlL5mqyfJF7XHGg5MpbSVBSFeI+yFc8u1oVam2Eb0N2jHAaDAUwmk1qHT/FW6koWiQ4MQ97CNuRjjRpfrcBln9QGWC4eXx+jk8oomo7vzqHl+WMTHXXmKgAw5yPiiKWrynPH76bKs6TpOj/N4z/zfqL1ksqh7UN3CuOuWcTnv1e4QmcVQHl7d6y0Rg6PIK5wFrd3zOI/n3uhppHSX5XNnRP+ztiKf9y77IyFIMzhA5bGlcnHl9cKjDYXJkGZiE/FSevEPBzpNFXf5+CypYutsfA/bB8XJ+XjgXwHqZy24hOdDh7m0xWrqNxuHC+vbwxieKQx4cYF52d5J/APfvAD+OM//mP47ne/C5vNBv7sz/4MXr586d33jcfYxum8X3aZlC1Pe5fm2iZlWMDCH9a+ofPxYDCAwWAA8/m8tkcMh0MvzdOnT+E//af/BNvtFv7n//yfdfh0Oq1Phlmv1zCfzw9q7XFfgLYZtfeuViv44osvgvTj8TjoZzzh6ZsC99W38QC7h6Iogo8adnGKg2Znt5QVmw9Mzlir0aLriTiFb58Tf05ZXdHFjWyagtDWMaHhyRF8mlFBMlpK5WnKjkZXjG4Jp/Su4WjC35KRHZ1MGM+Vps1mA1988QUcHR3BkydP6vDValV/kdgGUk4MTq/UH5PJpP4KbrlcevRz4RczdAKEOytyDdpN0t4XsIw1bZznGLY1PJwGiSfQgMiNqrKBIH6XY6zv2xh5rZAy5uaUy3H2ej1YrVZwfn4OR0dHMJlMgn7SJm7anjn8oI1hqW15eZqjyTI+U/hT9Gt9T++msYDFIaXRyvlnF/JFMpDTcrWxHZvbrXzSFPDjgi7ahRrbNWeHJMe0NuPpaDxXlqlBmJZnodlaZ6l/U3IYQZKr2rwc41XLGNgX8HbnbUllDU2L457qTjQPB4l3UnoI0qfhTrVjWZYwm83qtDHZmNsnms7fdE7iuK3xReE+KkRDEfYJptMMgDFZZtEdUnBI60KAqr6DwQCOj49hPp8DQKXDoxEb09D0lNbY+JbKAnAyQ/qITkqv4bSs7bHf8MhKbac3TY/XMyDQu820ciggqbHxVZa6s5UfSVzcOh2ps5KW0wT08kN6ZCeow0NpqdqmCGimuGjZMTowzpLGL9uVw+l34Jx2uCO2yo9OWiC/EgB6BFdRtwOfyxydYZmOHq7/hM5Y+d3lwXiMC3ECC5No4W0SOrtomhjbN4nLE5++k7UZHnTW6ynaqh+Yn7f3bahaBg2LPfs8JMlAvywdp+z45fh9HKXIR44unlfiN7+sWDqpHgjf/e7vwP/z/7wP3//+9+Hx48cwnU5htVrB48eP4fz8HM7Pz5PzR1BSpp6Tmy+Hlpx0Gg00P+qm0ql0lnUnX3eEssje1rn1p2XR46ZPT09hMpkAQKXn/e7v/m4d9vTpU+j3+zWO8XhcOwTxozN0BFptsg8gA28valfV1lAcmujTOevbfUHKhpWCJnkw3wMcNnAel64YAmi+fuZl5dCSWrOl0j0cU3wHkGsUoeESWDu7LV00jWQ43Qdoxoqmhp2ugQ5+dKZiGO6UwPftdgu/+tWv4MmTJ96dsXg8BXfGpozSVpAMJ5ReCsfHx3BycgIA1Y5eriTQd+k4G8kYLtH/MBHaeJge6avdF0bvRUyBNJYpLkzDy6bxObil+uHdjZwvu+SJmEzVDJA0jQQSD+OYWCwWMJvNoNfr1Ysdmo8atkNjlG+41gy6vE48X4pmyaGjGWdjxnNLH0t5JScKTU8dAtxxYwVKvyVfW56LOdIofks7Unyp8JjjqOnCRLs3UUufUz6ln+4sT9FB/zV8Mdq4MUWjT9stbhlbsTbXeJ7zaazPMD71NfxdA5Uv/X7f033QWUXHQ6/Xg36/H9z1h/E5QOWGRA/+N8FfliVcXFwAgLsrVtoFKOk8UrikI7W5hkKClNzhcw2Nm0wmUJYlXF9fe3Wj9cqlpanBpC3Expckk2ic9Iw8OhwO4fT0tNal8YNKaW7UDLISPRItAO5UHHofHC+H7563tDmdZyWateOYedsNBoN63VCWJdzc3MBisajTp3XTFK0lgLALFpuxuHVk4vG6eF+qo1fOnyrHSo9EV4jfilun0dc1AOjduPw9pCfcHUvTAbh2cvypOfSq3a5ux/eW8R3VWXH9EspjhOoxLKv0HF36f/XMnbH+M4hOWPDiaVto7z5tHHgeGWLxlBYLSLhcmO6Q7RqwTK1ufptLMlBal+nlhPGlEh4DTlMYz+MqfuNhPH3cgU3pdHyq4Qt5SqPX4fPxAgD83u/9Hvybf/P/AUA19qbTKfT7fXjrrbfg+voaLi8v93YfqNWwvUtI2WRjOn2u/qjpASmcki1Nw6GloTvGPvzwQ/jggw8AoHK2/v7v/z48ffoUnj59Gsz1R0dH9bVqy+USrq6u1COdHyAPuD6EJ+/gOi/Gf9wOdNfj6C7Bsi7OtSE9wGEBtyvwuF3tiNUgJf802zOHTp2xOQz+OgqJ1GQuvWudFOvAVMe2mRwtwlxaPMXooO8pg76FhyxG11zIKT8FsSMQaVkIs9kMfvGLX3gGFIDKKUsnZX60Wez+phRIfdzr9WA+n8Nnn30Go9EIxuMxzOdzGI1GcHJyAkVRwPvvvw+PHz+Gs7Mz2Gw2sFwua0MrKg3r9Rq2220tKCm9FPhX/V32wX0FbfxxA7xFwGOa1B3MbRYhGt1SGimPli4FsbGekjFNQGprSS7zsKurq/oYIHonNG1XujiTjMUxRxuPt9RbWrRRPpDaTyuL1gOfLfyU6m+UbXSHLN8tG3MsWPqapo3xrpVmilejL5UH86X4OwapcWlpm1Q7a+lTaaW2ke6j5fyolafpUb6RmhtpQ3ownn7IIjkkuGNCqrNEb+wocA2oc4PrXFI70rh9zaGpupdl9eHJeDyGR48ewaNHj2A8HsOzZ8/g+vo66Bt6bL5WhkYH9p+F5yUHKk8bk6US3/E4aecArxPV53g4T2sB6zys1YumQx1uOp3CZrOBq6srNa02p2hl3LWOF9NLLOFSOnqsegqk+Z/G0XBpJ2qMzpgc4LoGpuO78yW8Wh3xGU9SKMsycMbiWiHW975u5f/LUAKQXaL4nwPlraNEnmN8fPRde9bypsOquoS02epkqXuVJtyZS/P77SEdXwzAHXplCdDr0f8Sej3c+UrvieWyjuqNiC2kjVBJysR5w4X7OojmfLX8h8+uLIkWndaUKIjF8/It+WziyqIfSDI+lT6lz1jwxPKULIzTRhHzI3nDvqV08HdOZ0izVInS+4/lx/I0nvLT6n0glSHjDmE+n8OrV69gsVjAer2G58+fw3w+hy+//BJevXrl6VK5ayANurD77st2zOWVhY4cexa34Ui6ZAr4uov2E12n0LUzdbCjXv7+++/XO2KLoqjteQDVscRPnjyBwWAQ3GOau058gDxAXcui9yHsa3x0Ddb1MIWcusbWIw9wP4D3d2oNT99T6ygqSy12bys/SmHSOtFzxu7TiGOBlFHSanTdBy1NIWbcT4F10Z/KkzIAxWjMFWgWw6XFOC7liZUTKy+GV8JlMWzFFDiefz6fi3cEDIfD4MsPbkSJ0WLlWUrvarWCFy9ewHQ6hePjY7i8vISiKODdd9+F6XQKT58+hUePHsFyuYT5fA43NzcAAPUdVpJySP9jbZHDQ/dV4bAAdyjx+kpOBuskwEEbj7GxwPu5bV9YjN6YzmL0zZ3DUjIs1r6p9ri5uanHCIA/pjEdnZhThvTUAjAGUjvlyPNcmcxB42ctHcolSeHiOLhclOoSmyPa8LBFyW8yT+aOX6v+1pRfcvNo/ZPDz5KjSgNqhJDmIumOZWtdqBOD027RlbS2sJRN34sivDec478r0Ppys9lAv9+vr2N49OgRvHjxoj7ql+PAOrb9WEuTV1b9SQJJttA+oTxCjV5a27SVSZzHUjglvCn9fzwe1x/YSTswLTofj2vDt03WhbHxpOWndeNyQ+on6/3dKZ2G4o/pJCn6pbIAIDBKWMvCsczlIuKja4BerwfT6bROc319HbSppn9U8Sn56P6LiDMzbJ4qHea3sp+Mi/IKrUNYFt+JK+HUadKOXbbSycv26aK7Y30nHD2u2L1XdDrnm89DVQnbrd8O7khinzeLAsB3DsecsYiL1iMcK6l/EJyvNI7Wwy+Pl2uhUUlhjpdkQzyvLb3rv3xweZvSYv3XwngcrUtZ5tOFOFxeX8ZZaJFo4rikvJVMdHmkdLxO1XvYB+HY0NtjPl/A2dkZXF1d1cfr39zcwIsXL+Dq6kr92DAHUvO7dU24S9DsK3RnKNcVtXUz4pPKkECqf46OZLF98HvaKQyHQ5hOp/Dhhx/Wp/PxNLgZY7lc1h+Wt+GJ1wV2xa+Sfc/qdMqF1NpACm/C3/y9jc0ud+0sxR3CevkB8iDH9pljD+frGQ20dClbZmztSOGgjym2TuT7gF2UJRkGu5zkuqJ5FwIspnw1dY6k4K4FMDdmFEURHGGK6QDcPUto5ECw7hrJrS81tK1WK7i8vKzxPH36NEg/GAzg9PRULBedKPiMfUaNrSkjkAZ33Y/7Bu6couHcsEWdERQkR4J1wqJHPlLDMw+PTZbcWJ3i25w+bmJUb4pPc8pIizW+Mzwm77ihUktD6UjVwZKeG5wl5dziBJHmCEsfWvqNypOuwOrY6ULW7EJe5Tpt9g3aUUG03amssowBCQ9958qyhRelsRtzGEjGGY5buu+YjzOeD/mblnffDB5It/bxBDrzBoMBbDYbWK1WXt8XRXWqBm373PHJ+1NywHMZzvUVPI0E8/OPZSRZznk3NhdK/U9pKstSPLI71QZUB2gDkhODP8f6JqWfW8e4Bl2uC6U2iy3auc6F6ReLBXz99df1bmjkdTomiqKA4XDo8Tpe0aCVB+DLk5SeFTsamfMdl8UUpOOIcQyXpX9fMD2hB8tZr9f1+gGguuKEyrfUXK7FUzLL0jn88Bnj+TuHQnTg8ncQ4tzRx1paWrbGinKcjDOsZ5XOhYf18I8qTtFE8UnHFbu6S048JxN9J6wvK12akA+hpt8+dNNOWTlM3v2aNg6nHKz2Y2jT8TE9uznuEDjvCymKQqHJ5syl4zGRMiuP7BCQ81RySU5D4yS6pTAfl3PgUtxcTvllSXYPCY/mHPbf/XQ+bbSsajy6dD/+8T/B//pfP6/nK4Dqg6KLi4v6A7pvAki6HF+7A1Q2MU23wXT0Azw+70rzLJWFg8EAyrKsT1ORaEM8OWthfjWaBpvNBn7961/DcrkEgOpUr08//RS+/vpruLy8rPUVhH0eBfoAry+0Xa9I6yhL2ge4XxBb97SBHBxSWjx5oC1kOWNjhuIHiEPMyI3vWtoULo63KTRhbG6c0pQNqZyUEyBFW8zwlTJKSfTm8ncToymfLKjhj6ejhsl9w2az8e68pe9o8On1ejAajbwz2jVjsmS8thrgH+RL2ghH0/FwbSyknGuSTLIYYGPjoklfthkDFnnE42J1jOHUDPaas0mSXzGZpqVNpcP3VN9JC7bc/rLwYy5IBvAmtGi4LXFdLRja0iRB13pZ03y5baTxuuTwSjlLYn0lOb+k/No8xGUhd/jF6NB0PC1PV7ALnFp9U/MI/qOeQH/SKQE8nzb3URqkclN0aTo5d0Rxo1Pb+UhzlqVoTgH2Rcw4EdPVuS5M+4THUbwpxytP05Ve0BVI7ZBykPI8m80Gbm5uPF1YkgX0Oo8cY6amW0m75Gme2PpGm1djcxWvG1/D4ft2u4X5fF6nQ4O/pZ97PeRTHlOC74RksUo4zWvJQ8PiOPPKQnwAPk4eVt46W+LjSnZEW2nleaR/pKEKw7bnRxj7zldruVUZEv6wfRiGGk/4HvJy+tl3xuWukyzxPr2pNLE5qx3+OGCDa4jormi9fP6vg9TOsXje7+my5PBSfC7LMH0VFvKXFSRa47QlMYrt7I8Ffhepzp9ff/01/OpXl0E41W9z1pE5IOF1NLfHn4NLooXOqzFaJVzWtSpfG8SuzuCQWvPReZnbImj4druFzWYD6/UaVqsVnJ+fw/X1db1D+h//8R9rXLg5RKLjwUbXHVj045iO2nXZnAaLbX5fNHGI6bz7pvsBugPLGjOVbldzDJ8vNNpScNA7Y62wK8HUJUgLfrpA32WZqba5S6HUdDJHpSXni4QmeQDSypi1/SRDGFVuuBFwNBoBNWJKeLoCzTiJcS9fvqyPFeRf/W82m2C3rnSkHcZJXw9qQGk49DG+a0i1gcYX9P4P3lex9pUmudw+sKRvirsJ5LRhrlyMOYlwTHAjNjemWvmcLqZSdNC0KfzWxWuTRbnVGLvdbmG1WgVfnDVxyEqwLznS1nCRK/NiPLEvkIzyXC7R8c6ddQiaQ5Y6zDCM46Vpabg2v3FIOWTpvaap/sUvyR8/fgz9fr/+uvzm5sYzwMTmQYu+0cWRck0hxqM4hufzOTx//hxWqxUcHR3BYrHwHFPIC3SnoIWPaV7JQSi1i9ZG2MdIM3UkSWkp/hhe6SQJSmMsbVNZxceCRG9KPiGOq6srT5+jbd12Lrkr4HWhbaHp+7H6bLfbmneltsf2w2sKUIagPpa7VsMTdvj981oeil+6+oTr6lxex8YiHX9FUe3w2W638OrVqzoNOmPpLh2prLIs4VvfOoL33z+CJ0/GjE500lWOOwzz+dA5DZ0Dye0SpWmqst29qM65qB8LrD1zvLQ+6HCscPN0YR6tzv5xvs55WRR+G1RhfHes/d05nQpGP9JK//V24XSF/1UZtA0xTGkNMcwfltpdr7r8z3XAcnx6mjR0iSsH/PFA32nb84K5w9a9V/kLloe/+4DlS/Xz4/ydn9zZyHmgkkVaWvfM+caFc/7gNIROUTlMLlOiVf+VXl6pbTgeKX1Y3/Bd2uHzYHeJ60na3Mvny5RdRgKMs+DX0nGdGm1vR0dH8PjxY7i+voZPPvkE+v0+PH78GBaLhVfGzc2NpzvsyqHxAHlwyGNyn/YVCqn1omWt8wCHB5KMlK6EaepL20Xfp2za/KMWgI6csU2+PuryixoJ910Y9nOM+NQIyQ1Fu5jkuppErQZNGpfTHhqOWLq2vNSEf1N1S9GkKU4xo5CF5i4NZBTXcrn08saMkJyfY7wXcwKnwg4NupA7Uv9oTgYLDmqUzhm7TdKlaGrSr1Z+zZFvsTaUJlGp3SxlUDxNHYkxA7FvgAvT0bqkFlIpvpOAO96kPNI8Z+En7lyR4mO0dqlftIGYnNf4iNNukedSv8fSpGhsAxZ8WrvQeIm/Y3hifEhx8jSSo05yqNAxFNNLJJpGoxEMh8P6CLD7YsiQjOlN8axWq3r34GazUQ1ZucZxjM/Rnyz0SotA+lFTDn3aeE/N5xr/txnLsTHFxx1A/Hg+Ka8ULo29fY4BqV5SGoTcOT/Fs1yG8A8vJdro/MrjUJ9DPDljRtMZrG2TisdnfqShRbctyxLG4z6cng5hMOiBVqxctRKA7ZzF5+q/iu8apP6hNOj5qn9Kq/8u82sMd7xcXn/53W8zTgemoTtkfecefUaHnBt/Wt9ZQcocOmQBuLMqzE/jY2F+nB7vp4lDrJxmuDSQmEGTC7wPse/0MsK4uANWoxfDYnEp0NqU9ltZ+vj4u6VMTivFQZ2ncVyxY60lnqTPnIdl+vSyK6DrQmmN6HDI88R90WFTIOn/WrqUbhmbc3lcr9cTP4yi+HN02NhameteqDfM53NYLBbw1Vdf1fcG46kWqKtj3i6O4nyAEKy277uG2BrEah+PrTmk8K5opeGH1q73HfbdppJtEK9y1OavmFxvS7tlHCAd9J/Da7EzlsO+GCOnnNhinhp2NIbiHd5m90uXhhCLM016b0K/ZETgBtvcvre0g8WIoyl0MQOxZNCld8o2gTa8H1OycVcsKnF49xWC1EaWncivi0IPsD+5oxtp0gZbqS9yjeLcEB2rt8YDh6oQIV39fh/Ksqx39mnyirad1l60rvx+NZ4vpkxQQy4HXNRpSi3dicNBCtNw0TrxeKmvsQ2bAM3blF9SbbsP0PpUc9A0Aeo8kgzc+wI6fgDCXYXUgUDpsy6eNEeCRANNpwHGW47mRlpQJmjjne4Gxt/p6SlMJhNYLBZwfX0N5+fn9VimeTguzQDEece66zwXrDilcSbJxevra5jNZnV78/HN+QKfpbah+Wk4l5GpeZHra7grvyxL73jVXq8H4/EYer0eXF9fBzsasT/4MbRSH2J6zTHdVV+mjIcScL0aj5ttcqKMNEYsu4l3ARaHItLM+xUg3E1LceKuIkme0bbEq0cob1nmR4qjKAo4Pj6ud9mu1+t6FyreOxfjeW3MaeVJ4ZZ5C3fI4q5gipPzkj+WAPp96Zji0NGAOzoxHMO4w5A7paqysP4y/b7D0TkV8V3edQu3OMN7W3k9XFhIn1Tn2I5dKY9EQ/hPjwrm7zQ/pRGfsdK+A84vw79jt6LX30lJ6+D6Kwa6riCl84M1R5buANPLi9EopbVlyMObluuUX25DlPAaax3v43f95njC4YOE45bunKXjNUwn9Zufvixdm7rnUD7QZ/89vDuY9xFNQ9MSLEKYj9PVt/TCeT2qXyk+c/pomhAHzV8SPKHzt9+vrpmaTqcwGo3qe0E1eN0dGdKciTpaaq7jV3UhSHevI/T7/cCOtlqt6t2p2nqgDRRFAePxGBaLBXz55Zd12PPnz4N1k3Q9xevc/w9wv4Db/h/gbmCXMsGCu9/vw1tvvQUA1SlOHBaLhXqSQO66OGVvSgE9mZLnC5yxh8rcu5oION5dTjjc2EIXtpymWP6YENIW4dR4kNO3TdpCcwg0wcvbKGaYo3VLGQza8neOgS9m3LWWk9MPKf6JxWl9x42+1IDI8+IXf/zYSSudD+BDTCbFxgIN0/gR47gcsvZNzoTGabCMzVg6Pt4lHNy4L6WLyX8qp2NjOtVeUj1i7a2Nl5T81mRhr9dTna4arU3lp4XOXJDaLoVf4iOa7y7lD52frG2Vks1Nx3GTdtCUUpwfxuPqiMl+vw+bzQZWq1WQT5tzYnM9pTmlGFv4gzokctrKwkO0HmisiRlUUro3bxerU2QfQJ1YqY+vco1KWj9rY4cbkHLKQR6mZVAaUvOd5sCSyuFjocu1F8UnGdVyIdWvKTo0Pm271mqzXtN0B63faZzWdxpw3qLGTm1O4nMEpWE0GsF4PIbBYFDLVoveH5tDJJ2pTf9ocl3Snfx6Y925EwTp0p2QDqfvbPJxpfNbcWfmBHoUMq2Poym8i1WnocLh6hffSYt5NAetRrOjM3TU0jQ+Tsmpi7to3bHHrl8oLivk6KbOcaXhiMX5+E3EkbS2DHliOZ8B+RiQwis+KMAfd9QxSxFojnMabnGu35YUSUbpsbdTTlqdBhqGz3K6kMf4c5oead0XSZ3VZpzXnX6QswmgybzQdp63Qo5uE1vb4pH6WnrLel3TC9BBy/UpfoVULljsjJodVut/zcayr/58gDgcwtqvCVjWOikbeyp/Li0PcPcgyRhJZtHrTvr9fv3hp2aXlfik7XWV3CfC08ZsExLP3ZudsbsaMNrktAuQ7svp9aqv0xBWq1VwJKwEVsON5mzZN7Rd2COkHASWMna1i+R1gZhSR4UbCkEEVCSHwyEMh0NYrVaqQ/ah/dNgaS8aLn1xk2v4bgop5ySlTZpkcQEUw8lBWhxZnJUxvFbZYG1jaVLmbWCV9bQsjlfaIUbLQqdYjkOW4rHm4Y5BTn8upBaIUlpNaZcM6XcBXOFsOz55X1Ejwr4Wa3RR3+/34Y033oDBYACbzQaur6/h+fPnwbFc3EFBw5HP+d3jfMx3cXSWphtpPBbjM4qTws3NTT3+8NhXzTEZc1pxR95d8zIFC69RXkUdIpWWP9MwTfZanOVaeeg45ztfY7sc6O5ZrU8xDR6ppNWL02OhmUOv14PBYFDvxIwtDC0Qa38rzV3IOgtdXeLWjAOS4zKmA+DaD/sDwNc1Ym1D6UAePDk5gePjYxiNRjCfz+HLL79UaaD3ENM0MX5GkOqZqi/GS6cNSLJW0o1okqqs8PhhdFwWzPlX4XHpCs/hSB2Y1KFYlwZ896mEjz/T/JKjTE6rQegAdTh8/L7Tk7dJmF6m0bVJLN61M0DoXI3xAncCU10dbp+pIyvmjE7ZO1JpUg467rDKg1z5ll9Grrzj9i25bM7vzgnr95G8Kzb1LJXp4qU2wLBq7LtnPz68KxbjpLRaHK8zTcPz8rLpL6SLykr37vKWpCxdh3E4wp2zFS7pF9aPtzPiv7m5qU8+a6NzvC5A5y7pDl1pvpL0IK2dhsOhZ/PF8q6vr4P51gKptFJd8N56fm8trcd2u61t0ffV6fcAhw85699Uuge4/0Btllr8yclJ/cE/gvYhi2ZbbstT1EYlgWab0uT1vXHG3ifQFie4mxCBGyYxbDgciobzNpNizDiVY3hva8ChcU3qkspjxRkzIsWMe02gSd59K7xcwbSkxy8qaRivq3S0Gy1PclQ9gA9NJg3NAEvxpRxTPH+qzBw5wtPGPgDIgSYGZ83gyGnizkY62abqbuXrXLlocT5rdFgcS5Y+1ZwmUt5YO1BaJHmcopGnj6VJ0aLR1gX4BlZZDvJ0nA7uzJGMBLuEVHvghzrUAUv7VaqbVCcprSaTmsiQVJ5c44jkwCnLEmazWW3YWK/X3l2PsTpoziApTWys7wM0J5VUT+t4amKUsshgTUYgrZRmmg+Po5XuYKcQk0H0GOsUDklO8HpoMr0sy3q3hSZTpTJThkXOr23nvH0C50GLHJZAksuIT+pb2vcp+igOjhtx4vpwvV7XOnhuvzSZE1NjTFpnSvzJj/KWdKk4pHd4+u8lgLKzFknFfwlXLvg0VWXTZ16e9p56jpdbh9Zl6un5fMzzhE5eyREuOWh5HAB3ykIdjmW5MAvEZGg83q+ftTxeRhM7RpOSpPHK8ebMlyEtrn/p/E37BxMXXn9C1AkrO2YloLRY24j2sZZfwsXz8TRS/hCPZCty/3q5YRj/aWmlfLGyYzh2YYOLgbaeOSTHCjWwN7E3WvRzGoYf/knzcs7aJTVv8vU+v9YGacE0lqtbvolwX9vCYiex6PK5fGeF3HV9DO5rH31TwWrbG41G9ccs9Gq25XIJ6/XaS49rI77m4h+hWmzGTeyyGmh4HpyxOwKp8/h50ZLnHHfKLpdLceGeu3uMQspYdxfGOwo5C/wUdG2Y7MJ42MTIc6iAhj76gQGvX2zXC4AbI1ZB/E0DKy9p44jv8pTGO9010QS0/pMMiNJiw1JHft+YhSYJdsFPUpvG7pfNwZnrAE3JF2rgLYpCPDoW8ViN91J+LSwWHzOSa7yeW8ZdgeZUlcZnF0YJiwOva+ALfXTGFkUB8/k8mZ/TyZ0RKRkXk0ExmmNGEIshRiub0315eQkAzumAd1JzJ5vmyMk11NwVpOS5tPCxzv9WORCjAds2ZkSLOVopX/O8KTnMHXK8Ly1jNFUO6mV0ByYA1B9G0HQSSG3D8/H5PAd2Ie9yIVaHpnVDWUId9VL9LO0em3sRL94tR3evAMjyUBo3Eh/l1DnWd5LswvpRnY6n8WkBkJxPFQ5+byp1HOIuWtr+fKxWYVVerYZ+PqySXy51dPjpMY7++/UI6dYdpnx3LATPai1KmRYJp5bG1V12yDr6gdUH3ymfhI65CpcfbqmbUNt4rNdX+dAmfzsxZ2kIHNfp/JwWyk/4Xj3b7hPGMUfbpUof9rVGh/xcBmFl6WhL53dpY8+cJl+uhTtzkQb/l767lsdrNHAaw3L8siTcAFTeSu2Yf13ENwVo2/B1aFcGeQp4d7ykU3bZPxZceGrPA9wt7GO9foigrWkOfd37AM0hZkPhMJ1O4dGjR17YZrOB+XweyFAM50BP8wSw8VPMBsUhZRPSIMsZazX6xojQDL+vI3ABgh55PJ9/vV7DaDSCjz76KHDMnp+f10eHIKzXa3j58mWwOOdlScyQ4+1v2s8p5UEyrNOdk5KhrAlvcBokh5CURzNcW9uhS0OTNinFyuwSunYMpOqTMqi/rjLCCjH+TTkFLID9LR13mxoX9F9zoknH/Wj14XSl5FnbeQllM8dtGW8WuWJJb3U8IOAXrNLR9zz/druF9XoNg8GgPh4J82p3WfJxJ5Wh1VnbDU+dHNLYpzIn1gdS2dZ0TSHlZGqKi/NDLi/zscfxaI68JnSnoCjc0TIvX76E4XAIR0dHsFwuo4Z/Pu9Tp5j2dXbMyWGRKfxZ6wcpXSxOo0Xq31h9Us4YqU535dziEJN5+JM+WJEMYJiPv6dks9afmo6J4ZJjiNJGnZyxuZjn13BKOjCvH39Gh590z9dwOITT01M4PT2FJ0+ewMuXL+u1xGq1guFwGNAsldMkXktL25bG3RXPSvpEbH6jfGmVz7E+jOWRaKG6GcbhTnuUkVQPkMZN0zVVjNel9snBS+sU5kVHDh3T6DgKjxcuSxeOqGh6js+n1y/HLy+Mc3iTtSR5q2fsBpqfh0lpOF7qFIWo0wwi/+Fu2HC3cSoN8hVtVwB6N25Fo2sHRzemkeY/1w7tIUdutcsfx9MEUowmxWt5XHtzCHna8WvIU9J9wZiejjVedhgXthPXHeTneHypPFdp5PewXD8tjY91rly3kJ6yDpd/NF5rrzKg08lBrR7hO8Bu7EixdWlqzdoEdyq9tM6IlcnnzaY0cTzb7bb+EBrjYziazt0aLQi4OYifbsfhm2572zW0tdt1AVY+T+mBTXCmytvVmHjg68MAbT0mXaOi8QMAiLJM2vBI11FteJQf8Y74JJuVZS5RnbFdEGsFzRC1a+OhBXIXmRpQZQCN33hc3be+9a3aSIJpvvrqKzg7O/NwLBYLODs7g/V6HeCmIDFgTKBpRq+uQTIKSMpZrhHDUq6lD1N8GKMrZ5JqAvvG0aQPJOVWmzSl9juE8X7X0NQ4aVFONH7kBnKpD6khHcNiPGIZH7nQFodFBkpxkvNGSptKRydq3p7Wfsf06ESlCovmBMDF32g0gtFo5JWFO2vo8URaudygri3cKI2SU4QrK1xpSi2QeTlavn0o27n6QY4xP4cGKV9XRncr0D45OzuD4XAIRVHt4qK6Dz2iV6KD6kpSOt73mk6h1cc6r8fyamGSPOFjXSoL3zW+iBlJYu2xL4jJf8pb/Gi0VF7an035XFpPcN1TWvRJdHFnrFZPK2CbaH2vzcXojKVhAJUz9o033oBvfetb8L3vfQ9+9rOfQVFUu9M3mw0Mh8PAkZvinaZrID5+uUF0l/JZw895IedEkKbjKiZTpPlQopXPz7PZzEtLP/TFMqleR98lOjS6Y/pRTJ+UgIZzXTNGT1k7D0qAwDnrO2BvYwDvNAXiAPVpcWljDi/Mi+mrMv2y0g4zKZ+YiuBytOtlh/hdGk6XTGc6Hx3HLo3vDHb5HC7kG7p7EtO6Ovo4bmMLuc/8NGIrRPPYh2/eOO9qug3x6M5Tmeds+oori8oaDKdzG/KGZUds6j8NtP7hM8Uh3xUbPvs4eBzlXf/dhfO0Th5ymmgamhfTaGWE5amxBH8lmzmdpVcO/fFyLMbhXJDsiHyO2LWNcZ8Qm+ekumMcrseb2CRj5ebALvr/AQ4DUnYc/G/a/3y90NZemSrLYhfOsR3H1v8PsF/Q1iPUVsAhZiOhvjG0NeXgiNHIAU/J4niXy2WjMg/ymOJdL9RzoK1xUjqqlRpCrq+v4X//7/8Nb731Fnzve9+L4kNjZmrxmzqelOOUnjX8KSGe015IpzZguoRd8RQ1VqSO5b0v0IVhnuKRQMJtcWh9E8BqqLbkT6XjOyv5PdUWkORRakzTcYMKl+aQS41dmpfiTTkYJXpiDppUu1oWnJLSqDmcpPqMRiM4Pj6u70549eoVzGYzrx/5vIPHVl5eXkK/34dvf/vbcHR0BKvVCubzOTx79gzKsoTRaBTcVY708aM1tTbRDO0xoy7HG3PUYVoLT+wDuiqnq/kJ+YQ6Pu6CDnQMnJ+fJ79EpAs6vvjisjDHmaDRRcvhRhPN0SaVa9GdaDwtSxtPsbpJY4rLvUMErQ/5Ql7TCWLxOWVSBxB3TEkGRIkO3OWNc4t05LBGCy1XMmLgPFYURfDBpQac3sViAV999RV8+9vfho8++giurq5gNpvBy5cvPQdeG37hfcJ3sFO6aF17vR4MBoP6eN1d6v0pXun3+9Dv9z1aKI9oQMcc/7BKO6UiBdLX1ZQXUJbSEyxouxZFAaPRKLnLhfN5qp6WNNJ77n3KsbJ4MDoXnPPIdyZienymaW9zADo5Ma37d3GIg+dz+at/Wrajx79PNUzn7yx0feKX6+PVjwz26dPbJTxm2NHip+VlaeWDR3u4CxbpcmGuTWh/+U5BXUckWFtPd3YEXU6tdlyaHOHh3GErx/MwXUw5vvTD+JgFr1zKF3R88HEj/4d4tWc+bt26wk9bvYf3ANM4jq96152mvrygTs+SlVcy2uTdua6sEJf0TOsTx4E0lEH7AgBcXa3h2bMZzOd5a33r3HZI9lsAWWe36j/aGkpyElltV5rOKOVp4zR7gAdoun7i0HbdnVNGV/bnB7ifgOsoSe7hiUAUttttvY7bB/R6PW+td3R0VG+m3G63MJ/PA9pvbm680xAkOEhnLMJ9H5SSM5YrKqvVCr744gsoiqJ2xlLnhGQglBaz2jGgmI+GNWlPi7HcgjtnIX7XEGsvyXCjTXw5BjDNKRNLn4O/DS0aLi0sty4xfF0ZD3PoOSTIGbuaMVlqB7qz0mJYThnZLI4yLIM7M6z8IdHA72DEdNwJotFtgZzxxZ9jYTH6aJ3RaDyZTGA6ncJ0OoWrqysxD+1XdLDi/c7o0N1sNvVRxQCuDblDvq2M5vMW52XeZhYjMOezmFOr6/nlkOSJRIvWzppRwWI41+R6LLwsS1gsFo3qwGlMhcXGUGp8pdpQW4haHalxZ4O+e4CHWcfGfQJOb8yRk+uM5XMBgHynZk6foWy0yJtUOdj3XLeO1UMDWj9c9D1+/BhOTk5gOp3Wi0e87xbvGc0tzzcw2z9AxDUKP8L+rmQmX1DT/9wxhPWI7XBG4E7U1FoK8SLfctmO/3THubYmkPgvViepjql0vJ5WsPBCWfrOH3zn/1Ja/2hV37Fno0/OR8uG4Ljg/HL8skoA5W5bqa7hv+24YvCOb/bz+mXGwyg++V5ffHbpXT1p3bmD0W+XGMjtHePzNM420C3+HEcsd7hqjCj3Bf2YoPovvDHkO10pL/qOVjc2dODxYfpSCU/lc+G+LNJ1MZoGZQwITl0rHf6zf0Qxy2XGG6OFpwnrDrBcbuHVK3n3Doe2toAmedvQZCnTWrZl3WFdu9J0/KNAnibXbvYArz+k+Cq2hqXv1nUkxw8A6oe/kj0nZ3zn+CdSY+Nh7Nwf0Owc0slp+LxarQJnLNpGm/Acx2/NS3fE4omDAO7EQo5vsVgkyzhYZ6xlYN5HsNbnzTffhJOTE7i+vq6NK/xoKgQ0LGgMGTsGS/o63WJ8tzoaLNC1ATHmEOy6HElgvG48e9dw3wzMXULKkRJ756DhwHwoR3ByS+WXxplmJIzRxOWTNBnHHCG9Xg+Gw2Egt3Bi5LQ3cdpIaaT2SDl+uGOS4tUA2xCdqf1+H8bjMTx69Ajef/99eOONN+DLL7+ExWJRH0OJRvLtdguDwQD6/X59HDHuUMLdNLStB4MB9Ho9WK/XdXm0zbgDQQJsc75zCPPTNoodWZsDdyUjqMzflYEhh5YmTo6UQzIVFsPLHQj4rukktAzOM1p6mi8VJ83XKecJOpE4Hdb5nvI/r79kzEFaY2nuK3Da+X2ptJ3b1pO2YS6NGr9x+W0xHFicWDhX4Y5YfvwRTyvxAcr6o6MjKIoCzs7OYLPZwGQyqWX+H/3RH0G/34e/+Iu/gMVi4TlTY/pEal6Twvj4x7krNX/sEuiYl8YyT2ulM2UgluZRGh/TCWj7pfhts9nU8gp5iupUdIzFvtTm9KbSSfTiM/8AwirDQoeC29EqHYMLZEcedRZRh+QtRgidUzRMxkXDq7r44To+RzN3Zrn28uvh53Nt6fdHeH9reGRz0Kq3/3bnK/YDTcfDqudw5ywP92lACNsO6xQDqZ53MTV2X6bUgZqjNXTmxNJoTlnHYz5/8TGhOzIdHjc+sd/DvLzNtDbE8e7eSxJesjThXbF+Xglv6cXxPBRnJaOB/cIwvSz/Xcvvh5WRd+mfp5fb9S7hrvVXbf5popNIc7LFdsrlbczO8wAPkAu5fJTL+5q+2lavz7VpPsD9Bkl2Wnh3uVzWax0JLNchafTwD4Y5TCYTePz4cf2Oa+j5fK764DRfG4VGztg2E0cTh1XXE5RVaHQhXLjxmr7HDPKDwQAGg0H9BTtAdR/UZDKBxWLhLaQlwwfHR9PG3jV6JANpl4JS62OLIUtLn0uj5FxJQa4DLFc47Eo5swjBrvo3Z7x1jRMgNMq+ThO8Np65I8Nq6KMLBa0/aNvzuUDrF4szJZUuNe654ydGdwxiDh0LaGNcM0Za5kQeh8csTqdTz1mEabljh85BuEsWHa8I1FGLR+lrBuCYHGsqs2LzVYwXNbpiOLqa21P4tPBcJ47V+cOdH7xMKgskY0AT4HRKfJPDI5Khwsob0hjQypDKa9IO0hjnbWxxalA6uhhDdznPxeoR408enjMWLE4eviDK1cd4OSkaJP2fpqPvdKemNl4s/NHv92G1WsH5+Xl9VyzS8fjx43p3rES/FmYBPvYkyDVcdgVSe0oL51S7x/BrfZOSASkZRWWqlIfO71Lb8zDruMldS8XmF61+epqSPftOIZ9c6uSk+KlzCNusCOJDurA+YTrtWcJBy5bSS+W0LTcsP747Nk2zdTdsKhwAIIzD+Kreft8gxNpYgl1Ne/sRVXxs8jD52bWd5IRN75LlvAfCBwau39xdsY63aBiA2yGL5eU1Xlnq7S2Hl148TePPxzROvysWw+x9Luk5YX5adg4+iUZaRiV3abl5u3n3BV2tFZuCZQ6jYKEvpttZbAFWOES7VU57We22hwr7pLGJfSLFq5a1g2XdZimrCeTYczncd976poK0ZtB4j4bT69SarGdi9FjwSCfe4lUyEt2W8XywO2N3CW0GfQ5st1tYLpe1YxVhMplAWZYwm83qzsa0fPLu9Xr1Fuh+vw8/+tGP4OzsDH76058GjMKPAZPunZLuk6UCeJdtIzEx0rQLiB3dbIFUe7Q1nFqMhxbowrGwS9gFbal+iSn9OK54fJd0do0vZhBJ0SEdzYgTxHw+h6Io6p2lWBa2IZcXALbjjGk59F+qDx9HmgFeagNtsosZPiX5x+sutRWvE69Pzh142k67mCGbyneE8Xhczy+0z+j9chxevnwJy+USPvjgg7rc1WoF6/UaptMpPHr0CFarVU1P7r2UWLbWHjG+0croWobsQ142ke1WxQ2B8gt1uljLarKwSS24nQE4NEYgX+S2f6xcOoYlORM7PpTKHE1+tJnn+a7a9Xqtyg+LMyPluOkCupy7aL9pDiQaZ5kncMd+TFZKi7vBYADT6RSWy2V91BEtD+c1iT8luqSwGK9gPNV3UVbjccJvvfUWFEUBP/vZz2C5XKon11BHIp03yrI6GvzXv/41LJdLmM1mcHNzA7PZLMCDawu6I1dqT37CAR3fWl2ldsMFK22/u9Jb1+u1NxY1HtVA0i9pG6Z0lxjvUsC+4cZbrvdMp9PaGEDvjkX5I5XFP+CK1Y3+p8Ycr6fUtql2xuiqjvyOV7o7k+4GdQ4heqyqvGMUaSoI3irctXHaURpWuXJiYVxR+P1f1Rv7snoOcTscfv143QHCe2Djd8Bq6WRcWplcvnLaQUjv2ryonXVSvUEI90LFNF4PdDYVdjmnpukOnaThe5UP+Yc+F0qawsvP83HewzB3JzDlRze+oHbWOkc7PYoagg8oQHj3gY5xnqYsufwI87jnMsgjx1H8+k5axIPx7jm2qxUR+WkB4nlknJjPtjOXxpHaiG3+TQVp91MXugi3JaRo4HpkVzbBB7jf0LXtMLWe4WXG+DCVVwvrgq9jeuoD3D+IraMobDYbL7zLvs89HRZ9dRxWq1VwxdtqtRKPKJbGS9IZmzKCceh6gMQMhl0ILMlg2DVeNJ5woG27XC7h5cuXMJlM4OjoqM6H6QCg3gnFDSuIv4mRk5bDn5sYdWMGKg26VkCaGlK7pMOKp4symxjuLWVaDKM8ftcTZAo/N1phHok2Or4OydmTkrdN+i6VTlsQxOohGRi1MjR6pEWRJpMlHBhGj+OTcMTqYJnfYu1J+4Xyn8RvMQXV6lgoyxKWyyXM5/P6WGLpPj7NWI4GXjzqYzKZQFEU4l0MuW2h0S+Ny1hdpXLpO+eptuO3ybyfk96ijGG6pjqIxZlH08XwaDRrY1DK03WfSOMslUeC3EUi14+ksVAU7kOzoihqxwjKJD4m6XiItbdEh0ZPV3NvU97rgoactYbkoErxtsXAIOFtA9r6gvIFnkhwenoK/X4fRqNR/XEFxWMZg3hv7Gw2q0/RwTw3NzcwGAxU3QfxaHWnbczzafMbzSPdR87bKAY5slFLKxlAKS08P6eRz8cpGixpY7oNPsc+9sK4wWAAq9VKXA9S+aG1uybf+LPUZhrO3HWHwxeEAHci8bS06LKkzkfn1LDxjytLoitEoafX86TzV/mqOAkHr6OFljhdfhtjnD7uHG0APD2IdLt+ADGvn06iL0wj5b8L0Nnb7xML3X645mSVw+W1OkWI6UL6pTHknmk9qPPe3xEryTs3Bmn+sN40rCy1Ni2DdGlIz/1+mVL6WJjOm3JYjt7p59PahdKdLn/3EJtndlGOBjHbVVs7tjZHWvHlzLe50HQtmYMfIJ++LteJDyBDTru2HQMcxy7He0z3zMn/AIcJkk3Daj+z4m+CpyiK+jRBbf1blv6H0tJuWQ063xm7D4cMLaftwLI6FHKA06VtX6bOg/Pzc/jbv/1b+M53vgM/+MEPVNx8ly2A+8p7PB57BsEcYwHmaQIWA/tdCkDNmNIVNDHK84lrH2Nml3AoE5zGa/TezVg6Kz5r/F0Bn8S4Aa7pWMitb+xi9X6/7xkYY87AmLFys9nAarVS76Oz0GtNE1MKuENYopfXiRq/NQWWl7FcLmG1WsHZ2Rm8++678OjRIxiPx/Xl9qgQSMoCfT87O4OiKOCDDz6Aq6sr+OKLLwAN5ugI0HgmpWDw+Zk+S44oXmeKxwJtx2FTfWJf4yiFC2mhPBpbKMcM5vgvHVVtcRBK5Uj9n1M3/mxZPMZoo23Fx4bmwIiV0+v1YDwe10612WwG8/kc+v1+ra/RLypRT5PkAaVPKzMVvguwzIWxtNJ4x7SWemh8LDnXpPf1eu2dRiPR3WRu1MYHhqNTjH7Zix/PcKf9Bx98ANPpFD799FMoigKur6/FMjUnGNaTOz0BqvH87Nmzulxeh9gY1vpIGj8anev1utYHdrXW0NLG5EUOT1vp4HKDnk4ixaEMofmtBlzEizu/y7KEm5sbKIrqAxFuIIhd2ZGSOdYwWmf+gZxkyNDowaiydI4h9185hkriJKLpeVo3rn3coDjKaBlIFuJzz/I9rTSuwhWmo+W4dtB2xFJnlr/TFQQHqlb/GB7erjTM7z/aPogHSF/Q+lEnHa8vzUv7oxDShSC1+WGDz2d1aCnJk8J7xvYFwLQuLPwvIvk47pCW6r/0+reSRyi7sB74TOsBjDcpXnsH4XjneVx4iLeKK5XwEC/Hw8vDsKruYfv4aaT04Z2umJenlcJcWjnOx8/vi/Xz8Ta8D3BXthWrPsrz0P8UaLpw1zbKLu3ju1pr3FU/f5Mg1sbSmj7W1035YNd27qZryAfYLWi8Zx33mE46VbVJf/L1bMp2hTAajeD09FSV3QDVmkeyMVjA5IxNGUxT6duCtmg+1IFlMRRKaTebDcxmM3jx4oUXxvNeXV0lF7O4uMeFeBd1kMCyOJeMYzS8ifHLml4yysUW/9KEkapjEwMeLbtJGzQBjU+k91h+yziUeCCFv4vxbFFwmxr0msTvG5oe+S0da6s90zA+lvFZAz4J4g4OAKh37OBRexxXjM/osXyUHkvfa0qiJDt4Pq2OPJ828aPs4MZ8iR7ERZ3WV1dXsF6v690wkvEX8WHboMN2tVrV8YvFAoqiqHdV8baL9akm36V2STmZtL5oMs5yZem+x7JF7lt4k6bV8FvzoNKLukPuxysanbtoWwvPaflSMkpzQEntiel7vR4Mh0MYjUYwn8+Duvd6PZhMJt6xsJa6xMbIPnnWMhfG5JYEOfoGfbc60DQ6MR1NGzuuKKZX4DPOXVSmU2cU4qP8gHnwSGF66o0091nH4WKxgPPz8/r4eeS3+XwOg8GgPooej+rV+F2iRYtP6XZN1yJdgqbfY19Z5yMpjB77K5VrpU/DHxtDOJfzo+qxT5usKyzGs6brnvi4x/8SQHW2Vu88j0tX/cvH8XqlAToK5Xi/PIq/qgd4uBEnjROwAT1amTvHOB20/pQe1/YQ1NNyXDHSwtuR00XTh+WGNPJ6uzhQ42l9wdvZFzq6eT4/7+sAhfDsrw8KxQFb/YcOWpqX85t7Lwm/8DFaACjOVOxP5DE37kjuAsA55EO8KfFRlrS/Q4dkN3l8p6eEL/yPyzGKhtMTB92RivRh+S4sXo4Lz58LHI5QT7Wui3Oha902hY/q85Y1r5ZfwsfzNl1LNW0Ti56NoNn32tomc3XYB4hDmzZssn5K9X9T2dDEZt4Gdmljf4AQYryQshfQtYvW9xJ+ax9b1/cAlU11Pp+ruPCkwab2y2/knbGHDGdnZ7BYLKJpqBEdQWIA/Cq66WK8CUgKiWSEp0ANIbuiM+Ug4GklRSplKGnq5LMYPbqCGJ05/RATkE3rsg8lLWYwu0/AeTFmBLQsKqRz+mm8VIbFIcAdgviPjh5qsB6Px/WXRbSM1F2siIsb65s6ayx1k5yK+I4GeZS/MceO9T5Wiu/m5qZOv16vod/vw2Kx8Npgs9lAr9fzvibDHVpXV1dimUVR1AoHdZLnOn5yZAIPb/MB0V1CWxnOnUKUv3JxN5XJmG84HMJwOKx32FEezim7qVJqKSO1yEsZH1JzjXX+o+nweNmjoyO4vr4OTgXo9XpwcnJS72qX5lvfqBrSwuO7MpTsGlI6VQp4O/JwrudK7UfbDB1X9F0rTwpP6ZP0rk6UwavVysPR6/XqueL6+rr+EPP4+DiYN2L9LNFydXUFFxcXXliv14OrqysYj8fwzjvvwGw2g88++6zezW2pHy0zpx9pP7WRAV3piDnGIqtTUpq7pPEstV1TQyWmnc1mNQ30VAwugyQ+p/TwscDjpLK5HLPqidpYrX74DgDkKFzJuerajO5Edfnp3bFAHKkhVPGOFqwnS1Vqzj8/Pw3DPBJOvZwqr18XmRb+n0MP37Xr4lz5FK8vj8K2rp7DfqjeS/Iup/HbInScyfpNEMTyxOPbgGXqjZdPHdS0Tajs8B2sLoyn0Ry0tN34P61LyfgC+QB5qSQ/VzfKB5wfCXZWFo5ZfA9bpirf5XXv4bOfpvRwhPhcfTGMl494KA2cVpcn3OEqlxm/41Wih8dRnCkcQotKgd9IsK5JAeL6XhNcTXWYXP1gHza1XDg0eroGS5u37ZddteGu1uxtoGn5mh3kruvzAA6k9Zc/d7efr7qyjaxWq6gzVgNtTcmhkTM2x/B1KHCIg1CiZ7PZiB0+Go1qIxQ6LvilxtvttvWdTPsA7AvtHl1M0wa/JYzT0wbusn3btJXVQNKWjhwcFudhTlkxY9MhgsUQ2hXE2rGN802K14x0aAwejUZQliX0+/164pOMmRLgIhQdvJIDN2VA1+IsOHgdkec0+nOdjZKRtyiqe14RNxpikQbpaHxqfJX6JcdwyuMo0HrzXZY8j1VZScFdz/HWuatp3Sz5Y32q4eWOAO5MiuHWaKBpOe1NZY7kDIg5VbSxy9umTX/RONyhvlqtvB3FuNt8MBjA+++/D2VZwnQ6hfl8DpeXl+KRstxJwutrpX3fQPU6iVbqMOL5KMR0uFwel2ikDiSt7Fj7chqoI4rWlTqpKH7kC3zGI/Y///xzGA6HcHV1VZ+AQPHEgMbzKwCQntlsVvPncrmsxzilsUvjB43rglfbyncrDbFyYnwYyyPJUnxO8ZxljizL0rsvVqJNmvdz9TdOO76j7iW1D9eDtDB/nAAA4PGochg6EjG+QoVOWf9Y3VsMdRgNr8rF8Oo5dN46py6/G1PqC1p2UXAe8I8wxrIRJwdaVxlflRcdbFV67d+VR8Mkh6yE09UPPDyuf3idpHjEDcAdt2E63hZ8XoeaRg3uepqk5evDjbaDPzYwzPV3eKJO9Sw5ZLUdsi7M0Yl9gs/+R0FlCVBN3QUAbG9xb706hjzC278aOxLwcRmH0nsOxzSVF+7Zr5+PKwz38VdpSvYv0VwyXD59fh5f5sl00zAfp8uvO2HLEmC53MJvf3sD19fyh9cSWOY2y5y8az1VmlesNFjXIzG8XdSP85WlHm1tRTH7Rkzv3Mc627ImuK+Q237S2hegXZvk8JGFprbrhxR+TU/MwfEA+4MmvJm7ju8qXRvebTqWeb5OdsZ2IRjagMUIu49BmWsMlhwG3MEKUNE+HA7r9sXj7pbLJazXa2/SlAx7uwSrcZ6Hp4xcGk/lGKTaQC6/pOosGVIlg0wbo1VX4zCnT7X8+1ISYoaxmPEL03IF2AJ3McHHjHkx2mOTDK1/avcpB80IJRl2U/ThvYr0g5Obm5v66Fzr7lE83pga+yWHBsUVM/rHwOKgoe3Dd2RrzljJSInhVLajER13LuJ9lZy36fHNtE1iC62yLANcKVpjbTEcDut7aKX8TRZ7TcagVa7kzuNaO1kN6DzeUnaOrI+l5W1C5YHGI5w+yst8V+8udcKiqHYd0g8QuIFSohvlSew+a5onNrdQXlmtVtDr9WCxWHhtUpYlzOdzGI/H8OTJE+j3+3B6egpff/21t0s9Jpt4vbSxo80Lqf7gfa3NG5ZFi4SjLN2R9DHZ0wT44txCW2rekujn6Xn5/Kh/nLvwXliaHj+cxDyDwQB6vR58/vnnsN1u652qWpla3SiPU57BcmazmUcTHQtSGbH+tvShRvO+dShNztH4mPEnNn5SMgLDuWyl6YuiUOVRCjcCPf6cf5il6X+581xsTovpEzxvTJf19fIQT1lSh4/v3ML0WjyjDCBwSvq4KB6Kl6fh1Zbw8fpouKu6y3fHhjS7tFWY5bhirR65O2SrPKA4Xe1HE2NfFx4O3l48zk9TRtNAwlm7T/DbQqLL11+qf9wRC7fP3Bnbu40vvHg/v6wXuf6j4ws/cqAOv+rX6+F816vzVHixfq7fhdqTuNi6kNuL/H527/4zlu/eSxbH04Y8RsunZdC6+XmcvuPn88PpO6WNlxW2gbQjlu/E5Ucsh5VaLjfw7NkMNpvd6eS7hJx1EQeu27XFRyG2qUR7b2p3kEDS+dvo1pqu0DQvj+uCxn1Crq66a92W65uWNRmm7ap8K84U3zehSRrLFtj3muMBdgs5Monbty08ZLUNSesiXONreSR4LY4pPpRB1gUd0sK7LKujKSUjqLaovUvo0tB2HyBluMyFfRiyc8HiWNgFxAxGfIxI7Z9Lo9Wou29oy1tavax15Hm4Qq0ZO2kfSEqcxuvaJKfRZDWc54JV0dXycR6MOUO0NsTjifFOyslkAvP5HObzuXcUMkB4zC9vf62dc5QTxJ9yeuQ6bPYJMfr2RYu2IMe+1sYOhdhca5F/WB7iKYqiPvIaAOrjijWHFyq62pij/Kg5LXKBOr/oUedanVOOlCb08HanzjWM546usizrOzvpXZ28/TTHBe2nWH3aGmLa4sX6UCcMPQY35rCh5WG4RIsUx/lYy68ZMDQe5noG/af56YIPHZ14yoN0dG1ZljAajWA0GtX3zfB2om1olc/UEUcdf/hP776R5h38WU5vyOGLQ1kXaDoL/Y/lo9BELyjLsv6IhDvBOT9qC33+Tu+R57TF+FhKK42J2NHgnG5J/vK+5/paWEdM43ax4s+VjfldWu4cBO941aLGTd99CB2SFZ3he1V+mJ/irdKEu2DDsuQjlmlZsXIxjP9zmly85tjWd8hKd+9iWzoek+hHfvHrF7ZvScLCtDQ9gjT8ZBHD+V1Kkwd+OZzn03nlMUP/wyOHAZzT1Y3VAnq9ok7H43w54O+SletEHXz8uYTttoSqz7cML5dldKy5espzQAG8j5AeX274OPkzT6vjCR22rp5Sm/D/8LhhKX1Ig//s2peH8TqHRxhr+Xi5DyCDtj7S5iQpfwokHBbcTfBieFMdK2Vv6RpyaD0EvfEBfB7pQh9uSkOX6R5g/9Ckb6x21Jyy6BU9AFBfzSXhaCq3rXk6dcZqE09X0MUkdmigGaEwDgHvm6JADX7SIp7jkPJ3DW2YFiA+yFK8ROucM1h5uSnaY8ZA6d1i6MmhVSorlk8zSFppsiiluxyXbfDHjERSOV2Vu29o4lRo0/c4HqnBWAMaLx1RaR2rmuE4xt80XRtIjRetDjHHg3WhRo0rk8kETk9PoSzL+m5xaiiNGWAl+mMKTqpdm4JFHmnvMbDIPS2+y3HeZCxy4HxAHVypfNq7ta9RIR0Oh9Dv9+vd19oYiMkIzanTto2oQ4Afp5yDI2aMoeWk5pGyLIMdkFRGYprVahVNq+HntEi8QMc+LTNn8RpbYMd0MInPqCMHdVW6Q56no3JR4pvUfGCpL+bhTjCpLSU+kMrh/1K/Ss7Y7XYLw+EQjo+P612NeL+sdoe7Vh8OyGOYhn8ZvFqtgnDfiN8eJHmjyfcUtNXFeF7LbhbJeSilk9JY6cldn2n8qF3PIJ0qYikzNtb5uEi1TWre0eRZFU8dKZiOOwslRyHfUQoAyp2w/L/CIzuI/XJDGlx+OS0v28LS/no+jpfXE9NIOFzbhzjlcjRHLeICAGUXLOYH4ijnafywkoTzfgzzcXwp6ECN5RiRChF/nuii9XXPlWym6xn6j3agHknXI/n8I44xvKa+phd5B6+SKqEoSqDiktLj/unYKmpcFS/5R35LQIN1OVAq6bX+LFm9ZDw8DdLg4sIdsrQMCVeMPsQp0evTqeH05wBKq94Wryfk2ARzoEtdwwJN1kG55cR0LiofUnN4U4itLXZla/gmQ44N3DqO+PogtR606MpWO0xsjbkrnn2A5pBq/y7sZJZyU/aAwWBQr0HQ1sUhJru0eEwTs1NTeC12xrYBvujatwMmZcjsEueugLeZVaBa0lgMcDn4qPBu205W482hQa6BlufVcN4laPVBo5v00QLA3dN9n6Hp+OE7wdbrdX2vIn6pZDVGx+hp07eSgdAy+XKIpcPJX3NKYPmr1ao22r/zzjvw/e9/Hz7++GO4uLioceHOw9FoVOfFdo2NWXRm5dBtTYfGYfzyLGZ0pXnaOOjuQgHPKddaN25Ay22zNryPeZfLpceTlC4O/OhfTj/92pDWKbe/Eedms4GTkxP4wQ9+AC9evIBPP/0UBoMBDAaDgObUGNR0lxivajrjfD6v243ufkPn4hdffFHfDzqfz2s5Jzn+LDyV69Rqmseq39E0dDc0HkWPTmhpUc95xsIbmmFBk9tI12aziTrxUXZRR6rkgMUwOqfRMjANx4vz3fHxMXzrW9+Cn//853B+fg7D4dAkJ3m9tLTaCQaIX3Y6hbtxc+fBruXwLvBJaz/6TNtAGzMp2U/nwLJ0H1DxXbGSrM/RNzgvS2PMAlgOztmaccJCHx/HKYOGe3fHo1Z50JHjnIj8zlgMd2X5d7sChLLm9g1LFtqiivd5xeFGOqt47pgMd+hjOsSJ5cfujnV4+S5a8PBhHYrakSodTxy/29anH0B2LruyXRu5NJRuDKfpAOg9ooWXxk/nO2Z91pGds7Ehsj+1UOYn2r4hOCdmUWC6cHdrUaCzlTpd8cff5R2yiBsAvH5ydPoOvoqPqjFYhW3rZwC4ddZW9e71trDd8nGPvIHjEMt34wfjSuZ4pM+0HR2Nocx2eaSjfTkeqGmiaXw5Jd8V66enedyRxBjO36XyOE5XDi+ret9u9WOKJYjFPYAP+1hDdrFW1eR5DuxrvZxavz5AM2hjL+HQBE/b8rugHXHErlp7sPMeJjSRC5Z1BEC1hhkOhyr+1Ol+bSH36r9vlDNWmrxS73cBMQMRBbrA5QzKjUa7BC6QtbIlI5lmKKLPGn7NQGSBmEFSa8tYORaDr2VCSNHVVX9qNEn4pfbOKSeFvwnExoiFTomnDn3CthjRrQZ8az9IhllalrXNNKcHygTcMYVGRe0OOw00IzktX6JJwqG9W4G3S6rfOB1cpmMb4V3hb7zxBhwdHYm7JalMzVUGYjRR/LG6aHliclZqd94GbeWGxKsUt1XB00Brr9S8FaMvhp/zdoxWyTislesbtO330XCc+JOO4JVoy8GNdA0GA3j8+DEsl8t6d6GmD/F6SOMzBjQNH1fcCbJer6HX64njE+/sxONrKV25c1AsvVb/GC5rmU1AkwESUJ7gjpwu9R+JRt7PluPYtfxlqd8bg/GDwQCm02lwx2csjwax+SUm6yxGvdz8Eg0SrV33awoo/bE5KUc+aLoNheFwCNvtFpbLpSiDY7pHjCe4/ADwd/1S/U2jmfetll6iLbX2k+Ypy5rLj3OOE3TgYfKypI5A987jeJEOpxcKIOzclHDyZ5rfL1ve1UrrQPH577k7YnmdJYes1Cau3rEy7DT4dNM6+Xnl9tbah8SQvtN3zUq4OOxJ7NzS4O94laEI4ooCf/6xxamf+wA5vE/W4cZnycFfejRU0AM8jhhq53Hhvft14Q0vhemg9RsNj4lnVxcpre7AxHAnV8o6nOLldPjxIR1VGllHdTj4xwehY5nXx+H1+y1Dndw7dGnPSkFq3ufhKbtqF9C0/traz7rWxHhNj+PtZNFFLOWmdMAHsENOX+fiy4Vc3T9nLZqyy1jGNeLZ5Vi+K9inDO0Kcu09FFJX59D0/AhiCpJ9mNu8UrTQsBwbr4b/G+WMPUSmbTuYcg14+4ZY/YbDIYxGIy9su93CYrHIMoak2i+mrHQFqX6gOzQtA5aGSUqRlj/XgdDEyA6QFoqIf5eQw/sWA1gXsMvJsSu8uXiQZ2MTFUKs/lpe3EGERzTm0JnT3l05OjRA+tGAn2s4p3KqLMt6Bx3G3dzc1HfDHR8fw9tvvw2vXr2C2WwGAKGTSDLU8jhangRcZuYce4h46dGkiCelZHcN2AaS3NqXQqu1mWacLkt/13LOwlrCJZXDd//lAjVaIH9IhkGksw1wPl6tVvDy5Ut4//334U/+5E/gb/7mb+Af//Ef1fy5u8VScswiHzAN9iPujON91tVcycdyjmyMyQgeFlswc7mD/EWPzdV4Hk9HSPE95WlpbtfqSPvDojemeJeebMBpRVkttSum22w2sFwuvd23vHzZeJ4GmpbyflH4d/hKtFHZE5NbFjnOx0sTObBrGS3V0bruoKcBcJybzQb6/T4cHx97fa6tB3CsYPtzGvi40GQ6p5/3lTb+8L+pPOI0xT4wiGCBsnZKFN5zVQY6GgEg2EnqO/h8A3Nxi9t3GmH+cIeqvxuWlhHbeYs4uCOUloG4kEbnyPKPO3b19dMhzQAuraur7Hil7cbTubrRcuUdsq5+wOjx87r3vJ2yNJ2fNowD5hDz01nWD8kkrYCTQNv3NkXdRo5e/zji6ufrVdUYd/pWryelC3WxiiZXDqfNH3v0ftjSG8vbbQm9ni8rHP4eAGyhKOguacf3KXBj1w/DnwsLd8LSOmB5Lm+p4PLTUDwhPWVQhstTBu8hTk6LXLb/TOdlF4e7Y6v/lGNZHyd3Bfu2x3ZRnj+f7A9itOfaNWL5+XqlCVjXNftad79ukGPzbMOnlr6xrBmttkNrmTwdtxvydDnl3zd4HcdP0zql8qE9la6vEObzOczn88Z2MIQYn6Xo27kz1uJA+iZDrvPsPgBXWLRFPr4PBoP6PjEA+5dYViMcpUkKj+GNOU1SeGK0WPE1Nb41BW5IlfCnFKx9Qpv2oXW0GBUt5d315Nilgst5ge70svCAxSkgyQNu3LTio+ktDipaNwmoIhejO0anlkfKlwKqRAyHQ5hOp7VzZz6fe/3SRh5R+jQ5ZJXRaJCmeHj6fS7KLIuHXBwcTxtlUpozOf4UdNnW0lwYw8nHDPJkm/6VeK3f70NZlnB5eQlvvfUWnJ6ewvHxMUwmE1itVp7zX6M1p2wup2I4YuNa04U0npLa0yqXEK9mHInVI8VvKTponficocluK89IdbDqZJyG2EI9JU+leK2tJZ4oigJWqxVcX1/DarUS65oCiXZt/McMJU3L5ri6hK7mMCtOrY+t9Urp7NQpIuHV+pHmb1J+SleS3jl9kj6mQc64l+vh/8Dbuemeq3eq73FHInXeSmVIfes7a7Gq9F16pvmpk8vR6OPh8Vo7AHAaffxhObnpafm0nWk6GQfm0e6RlfJrfVGFIV/pu2UxvRbnp9utfEqUDrwtdSiE54L9bkPruY5/8BY6YXu9Kh29ogfz4ntN7W0/8eOEe71tXQfnZHXO4iqO/lMHPsUl1ZnOTXS8u38gzk//Hby8ElB8QmwyDZU/nC73HzpaNbpQ5lDcsqyL4Y7pG1pd/Tp3Dbnr2Kb42+Sx6DlW+ruUK13pXhaQ1lExW45UT4u9zGoPwTiLjnbXdrVDgl2Ns1yw9FtKp43hjaVN2fof4HAhhwekcKu84nio/JLwxxyxVjtJG967dztj92m4fYDmYDFOL5dLWK1W8PTpUzg6OoKrq6vWXybQMrsWyim+o/fmadAV/1qdIbuCXbRvV8Bpi7U33ut3qHXJhS4dsdgmeKwmhqMDUMojhcUcnhTwHjU6fjSjoVRGrC5N2wWdPtS5o9WTtku/36/zWoHXG9/pl3/9fh/G4zGcnJzAW2+9BU+ePIHNZgMff/wxXF1d1Tu1ytLf7Whpg5jRFp9zFWU8mpHDLsZbroJurU8upJwwElAjPe07y70Wqb6gfZ/jnJBw8TxaXZEPUb5ieFvDBeI4OjqC7XYLv/zlL2EymcD3vvc9OD4+hg8//BC+/PJLuLy8VOvAd5elHFYSHTHgY8aKF9NjHv4Fp8WRxGWttW60zzS89J+WRedbrWysC59rpfyxNpPCttttLW+xrSSZE6u75JRCB6k0Lml7aPRqvE5Pgnj58iWcnZ0F9NIytblPMqRJ/cT7gafZhy5J2ygmA3D+t+xc7hIsztwmshPrgbyEPLRerz0dgY6LJrp1rsOTA8pF/HBK2qltxcPpis0/WhnOEeF2xwLgs3N0UUcsxgPbwUrDNaduVSbWAeoykFwah+8FcbKC4vREXL6MKOo8lHbwdg/Sst07byrtbliflpC2vB2y1FHn17sosN0hoNFvO9wB699TytsXaaspLzndpFYKa2pDoDvZpo8xqWzaln5+7oSVdsQ6B2u1+xUdrfF/6pzFXbToRMVnTmP1j+OucsJutzjeqp2ulVgoAKCKr5y1vds+K6Fy2Lq+rspC/sP2L+pyUkDHJk3vOyrl3a/gOVz1XbQ8DcUD4OfjNBGKGI6yzsPpomW4/9gO2XCHLf1tt6W3O1anLd3eD3CYEM4h3YBk++JrRivs2in/ABVY9cMu+8Fqw4vR0pYejQbtRLgHOCyIrbHarvUk3LFjijWQaNkXP0Up7cpx1BReB6frPtowF/+uaNIM3lQx1WgpCnd0Gr1LjRrBuLNCqwNVJiQjWy7EDDK8TJ6u7aRpMbJYnSxthErMUSNNtFpZsfp0yZMxo4/koNCMqq+DDOoCJGNqzvn9OQbf1MIjd2w1MW5KZcYcWhL/p5zKOTThommxWMD19TVst1sYjUawWq0Cpysvpw1YFmZSXYpCPhIY4yz15zzTRgbSeQbfpTQA8hFNmlPK2odafansQcclbzepL2PjAuMlOZbjOMrlUZoWn/liPwcfdUjR/BiOR99eXl7Cs2fP4Pr62nNuSLTRdsilRcLH42J8GBuTnL81/cbKd1r9yjJ99HXOWJfaReLDlBy28rgGqTk7RafWv1x/jc0BsfIkQBlpuR89Nm/E9BqNPh6WK995fAyaGveaAKVTMtKk5oCYfp8LNA86Y/GIYirXJPnIy7PKgpjeG+Ohoijqe4ulOSgGTYy2mlyv4qhDhL47RyAtUnrnafU0SEMh4kMa3DvWgebX6u/imvK/Tws+U7wVDfzo5RgtEn6/fVy7+2Vr4XIZUpsD8PkBArppG4O3my++Y5bnr3N1LnZCnuFlh3XS+qeo8RS1A9OPczIrdU9sD7gT1oUDVI5SOl7p/Ed5vYTtFp2q1Lm6BX93bEFoxB2xknwpwPEGPrv2Qr7w2zB0goLoVNTmJdkBycOkNOA5aKks8mmrZJiGI6wbrUeMflqeC5OctbJD27Vb9b/ZlHB2toTr63WWTpUCbY7q2nidYzOK2RofwAHVyWNpYusQTe9osq7aFe8cAmjtvKtxEoOm+m2MVzRd1oJXwxcDad0m2Scpba8jX90XsPQnH/+W9WYK72azEU+cktY2dA0u2Wr3AVFn7L6IeJ3hENtwnzTlLEKn02ltYOUDZrFYwHq9rt9j58QD6AYgSXmzGqVSQMvkA/k+TgaxNkwZVQ8JeD00nrTsbP6mg+Y0ovzedgx1AdQhY1EkU3RTpY+n5XdcA/g7fGLOPY6bxlN+LcsSlsslXFxcwG9/+1sAAJhMJrBYLGCxWHg7KWl5kvzJHaMSfZohOLXoQCMR0qopPJLDqAuD/mAwgLL0j06OgWTIxj4tisL7cKgN9Pt9GAwG9RzHDfaWRStNw+/oBJCdExrtFFdO+/OFGb+nNacPNdq4jvDVV1/BX//1X8OjR4/g+PgY1us1rFYr787lGFD6UkfVYNtS+qRxkaoLH98ItI94f0mLTC4jYnRY+VRKJ+3QprIwhtvCZ1pYWdruTdZolPDyMd3mRJOu5ivLTtBU/0m6Z0x2U5yajkflNZcnOWNZ0yWldG3uK9MgxZ+7WBNRnNfX1/Wz5Ixtq3NqRml8pmVIfMH1FGmXvBW4HLLwrRQWOjPorliMr5w7VTq+/qJOoNJL63iYOi/QGeXCsAmoo8ovx6OalenCEGdx62C03h3raPHLCx2wSJdzrGF6VzYtN8TD60LzY1sBxO6RDeUJxem3pXM+SmnkvCWL43UBEXa1JJUc+bzMkCZ3pK+PC52all+P7YT132O7ZGlZEs1Or8D/7e29sdUu2aLYQq8HsN1i/Xvef1GgHJN1AzrewrK5I9Hf6enyhU5QpF0qQ5YlkozkdEg7TaWdruGuWIke/yftcpV2yPJ0YRl4d6zQqlCWAOt1Cb/85RXM5/LVHQ/wzQPUB/ipZzRe0sWkudqqn+foiq8THEJ9mtDA9WLetzRO0l+1MjXbUS5Y1gmSL8ECTdYEUp5drS1yaLhLPDGw2At5+hxYrVaezyhWLreb3gXc+THFhyCoYtDE0HrodTo0oJN+UVRHEKJBVTMWSRCbLKx5mkDMMI7x3KgtwSHyTcoB0yXk9DXPQyFlENeMsg8gQ8qQy9Na0lj7WnJi8HdNudPGXEpJlMpJKVnD4RDefffdWo6h/Lq+vob5fF47hqwyihtP0ClxdXUFv/zlL+t08/kclsslbDab+k5ffhRlU/7WFCXa3nxXV8pZon1EE+sf6iiTHA0p+pHOwWAAk8kEyrKE9XpdH5OfUsIkvkLc6NxFpc/a3rR/cP5DXBwPLRN3L0n00b7BtJIjTAqz0s3nMk6DlseCg9MTw4u8gEfTbrfb+qMIGo5pAdzirav5V3J05eSRxqg2R6XKsdDQRhYgLXwusNDpjIqlONZi/ZGS1Vxmc4es1N6psrkhivOrNk+01d80uRdbGKdktBWP5kCTZAXH24WRDcvo9/swHA6T9z5bcPL0tF+1Y3i19ozpK5Y21YDOaTm6bAwkfFKb8blMKreLNYllruaA5FZtX5B3rItz9hXEYcmdhEXtxAsdi/QdgOLEceIfeyy9Q+B8DfGFdHKnY5WW0lal0e9jlfDRsOpfdshKtEo7N2l+SjPFI9Ek1YnHu3rSI23l9qHhfhzXK7j8hJ2Co8m1qV9m/D7dsB9ouDuyGIA7YsP30DkbHmlMcUl1QZ5BBx/2Ta9XOfWqI4l9x3CVj+oVBbgxkQu8P/1/fPbb3fGR9OzL1HC3LE1D80rlY14/XNIf9DwgOJi505XjwTiA0BEb4g+oicR1C7u2o8Tm4G+CDSdVR6tehOmsV0FI+qOmN0k6R5P10SH0Z45uuQuI6YNd+yWa6J4pvVVag8RwaetTaT2J/ynbTVP9tUm+nLXBrqCr8vZFd045OTyfWi9T2yW+59iKc8u1wJ07Yx/g9QAUvpadJXxQ8a+y0RlL732TjGE5Ru9YfGoC4ApG15OaRtddQxNlah/QhQKkCWKAw6vvIYBFsUoZ3TSlygIxo6nFUNql0iyVN5lM4Lvf/S4Mh0MAALi5uYHr62v47LPPYDabMYOITCPHzR02vV4PLi8v4fz8PMAxGAw85xs6PalcjslP+hxTcLlTRZKHmtMi5gSkeaT5AetAx+1gMDDJ96IoYL1eQ1G4u0bX6zVsNhuYz+fJOnN89MOh4XBY4+N1iuHggF8oS/3EeQAAREcFzYdz6mazqXcBxxZPEt2xhbVUR4mHtLEpHc2aYxjA/IPBoOb1+XwONzc3teOd00T5XnNYWWWURnduemmMcEAHIz+COeYw4U7JnLk8xrsWXSg1/qUv6bUxgXWJjQmKH8cFl6O8DCp3sB6cp3P4sQtILSABbPqlhQd5OtoGPN6qH3fRFoPBAKbTKQDIMq4pSPNqqp3a6gwxXsLytRMamrZlTCZr8i6lt1F6m4A0VqV4jPOdDfKxxUXhnCHckYj5+E5Q34GIvO07ekHYEeriXJjeFNKOWefo8tvAd+DR8vGdxvF7WyFyXLFrI32HrGtDLYzTHNIDgkOW0le9y8c8+20rO2V5Wh7nx3Ped/g06E58u7bgeDkvMQpu29dyFDF1xMbujO3d6n/+rlg8rtjxHeVT//7S6r2K3G5L6PWqo4t7PX+uKApgNEqnDMmVd+OahvlOSffvHJI0L8Xl18NWjp9fOgrYhQNzpPq4/d2wtD05XX5d6BxM49M7YkNcchvzctrCodhHdukIu09gaQdpzk19nCrltTzje4695xD7ctc0pWzJVhwIObhiax2t3tZwyls03lJGDLidyErTocirBwghxQdN1mJU/5DsABhuuUJL4x1L2lx+f3DGPsCdw3K59JwH4/E4mh6NGFzodwUp4Z07iUoG7ENUPjToQmnoCrpot5QAfoAKkF9xlwqOUZzEqPNPmvzov4SbGsRjYFH0aXjK+KfRI73H8tKy+v0+nJ6e1s7Y8XgMk8kEXrx4UcejM4U6DTj9VD5g+9Ly+HFk2Beas0uqQ0yBjSkUWpiE3zfYOLnX6/VqB+Z2u61PP6DtzR0so9EIRqMRfPDBBzAajeDjjz+Gm5sbsQ4SrZQncfcpOresOBDojkyp7pLRWhsbtK6cbxFPrtxF+sbjMZyensJyuax3AEtHT6aUTat8jNHJZQSPk9ojB5bLJazXa5VmyzFaqSNuNeC8ocVLPEAd5ppzmtbJcrdoE/olmS21U+5cyfszRrc2RlK4efukeAzfqaylOwSKoqjx0HSSw2ofuoNkMOPzRMqZpvWlNFciSLqqlDY2tnMB8QyHQ1gsFmo6yZBE5WXsQzsLvl1AroGrC+A6ljTX0D7epbE0xaN+WdzZQI8k9uPRoVTRxx21oQOSxrsqVXlp+eAdJSw7GxEXx+GHO1xF4egsiDMVy/P7RD6umDpAabm8npQ+Jytlh6yjOTzC2dEMHg6ap3qWHMvNnLIuPO2Y5fFhutTaos4RTRcDGw94OYS8VTh3YlbjRt/tSo8irnbI9tfEIwoAAQAASURBVOt00jHF8jyG/26sAWzr52pHbA+KYktw0GOJnSPZb0d8L8m/Xya2Wzge6Tj0aaV8QmUBx+vkRpheSsPLp7SB54jlembJcPFjirnjNCzXlZE6phhYPK83rSdvs9cHHuw27WAwGNS2CgDHa8vlUmxbzvOpNd0DpKGtztymnduu5XZVjhWftO6RbHYPvHj3EFtf7YKHpbUM1ZV43i7XPW3k4WvjjLVUuEsjllRezIAfc0poeWJlxXAeOnC68chIgMr4NBqN6rjUgLMaoiyGwF0Dd0wcAmjtw41dPOyQQaKdhyNoxxo+QAVFUdS76/BdcwACpMebZKDX+isG1nG9S9nJ72adTCYwGo1qgzL+MC11ZlvusdDmEXQq9vv92pHDFRD+npp/UuWl6JMcYUgDvwdzMBjUbSDd6cAXfDgnfPvb34aTkxP41a9+Ve9ojc21Et3oEEdacoHOO9oxwFZI8aAUb5nr0Ol6fHxc75jebDb1kcwpozwNs8wD0pjWaJPGZGyhZ5kvN5tN7bwpiiK4K9aCg9Y11u4WPHxMafiQVhzDEj/SvDhXacfnWiHWX1r/tMFPy7DyfM7in/Yddzpxfsb/2O5i7rzn8sLaHk3mNQ6cdvquOedTMl0yaEhOO6lcHhczyjUBvmjOyUfp5c80jPeLVmeOOxcOYc3By5OdMaX367rcXHlCHRB+eOiMxDDnqK3infM1dET6/U3zIs1eqSA52Nyz5NQM6QrveA3L09/lMsK28cuNsVgsnVxP1xaUTkpjlT52NLHv7JXq7IeXJNzmmOV4KcSHXIrvY3qXXIbUTz6+on6m6Xynqfzs//guWHTESrtjC688zjfueOIqQa9XwHaLjld/ByylnR8RLbUXHbOxtqRjH/kAxy1PB8xx6uL8cC5PaBqKh5Yf0uHnkdLx+sXiXVy4G9hPX97WgTt5/Xr7UIVtt9inrzd0NWffB0itRaR1HQduZ0UbAr0ajgJfk9NwTee6L/bC+wRt2rTt+raLsqUyMV5ba2i819V1XA+we2gqj1NrS8sarykdTdagMT9KCl4bZ+xdgNWo+DorBhyaCMWbmxtYLpdwdHQERVHURw1SQ32/34ejo6P6fkRangZWh82uoUtj1T5BMmS6RVovmEDvw2SoKZKH1D8WfmnCU235EMejpZ+ldpUcdk3BYujGyZHudLLiROAKH8eP/1dXV/AXf/EX8Pbbb8OPfvQjGA6HMJ1O613+6Ixtcw8eTSMdbygpsRanrwSxNqXPOQ5Jmgbv9OTOF/qOu4lxZyc9ani1WsFsNqtxWepTliXMZrO6DPqBQewuVv6MeWl6y7jSjNLIo4iTOtd5e0sKH3UoIM5+vw/T6bRu4/l8Xjv8JFpzHEyx/JJzg0NqLObIKXpcMcUv0cbpo+noBya5jkD8j+3M1GiR+kNalCJ+idfvau6NOQRToC3++X2eEr9r+Gg74BHm/G5Q/OeOb+0+al7+Ptu5LKudCsgnSI+kc1Ge5TKD0s3nRIzX7lDFuKIovDG2C8D6SPfFxuZEjUckHkOejc23+x5LlHfblp3Dq9ywJenDbenJdcRWaZwToSwLwGm2ursSed7tKOUOT6gdpO4d01NnLnV+QL3zjeN3dLvxRp291Cnpzx+8PJrOOcR8+mm9XB9Ijt2K1lvqAB1i1LHmO8iwMohDS+fw++XTOmI7aY5jIPWjca4tkRaehqbzw0sWFx4FLeWn0I6VeWbZ2c3rSsNlGUYdsvEfPxWneu6T/x70+/SYYpceoIBezzl0OW2uT/w7rCu6t7d6ftW/eNxxtVO2vK0D/hfkXXIsghfOf7S9Q6ejozXEUwZ4aR0cTj7u/XBM78ZpiNsP47theR2144Wd/SEWvt3yNsJwlI+8bVz5220Jn356DZeXK1itmq0DH+DwwaL/8zHPr4bTcKScaA+wf5DWTKk0PK6Nc6xrsPhRJDtWzH77wKOvF2jru7ZX2bTlE2mt3RQenLEgf4HRFKRFvjahaR0pGdm1ifIQhU7KkK8ZtqUfzTsYDGC9XgeC2WI4iMWljDHW/rgr0Gjp2qBiMeJbDVua8VACboCM4ZVobpL2LseV1fi8C7xa2lxDIXcO0fBcaGIM506+JrJSMnRz/Pi/Wq3gyy+/hF6vB6vVCvr9fr0rMVYfLc4ikywLpS74OKbw8j7OkTn0Hl2pPmhIWq1Wtczv9/swHo9hNBrB9fV1UBbvaz62l8tlvRDVnF4az0pyoomzm/MVlqntcLY4GjTZXBSF5/i26hxSmljamHNDCos5V1J5JT7R+C6GT2pbSW7F5FiOLsD7ldOei4MC5SPexzmyL1dP4s7YnPyUPnyWZAov0zo/clwxnPTfom+n6EiNhxikxoCVhljZsXFtrVvTeTVGT1mW3pHjGq2UDgtuaexJZe9Cr7f0UZu2lMY7gqU+OI4tZbfRj6VxpqelebAOzolaFFRWU+ein5+GV/1LnYglgLCDj9Jb0eicuvTdOU/De1UpbZwOXkeJZlf3QqwbbzqpDVL1c/n8dLQOevlY97AO2Lbxo4sxjTSv8HRSnOMJLQ3CbpZy8jHWEi3p8nkC59Sk8xiA+6+e/R2veJ+su1c23EFb0VqQcsq6f11fuJ2vctmcTqkOYTtYAWmh+WiYS5c+wljCiXk1vLcp6jj38/GG9NCyywi9YZyLp3Sh7OO0WPVNgMvLFVxcrEzpkYa7tH3EoC1dh1y3HMB6pNYxEtC1Lte9OR6fH2WdiZa/C90pF16XPgbIs7em1qxt1iNa2XcNmp2Hv+fYpB6gPVhlUVOcVj7n+SRbS47slGhK2bVy7CEPztgW0GaQl2UJw+HQJHABIHp8xOsy+dBdSwCyobvpTq+2EBvkTWAXAmsXIE14NO4QQXNmxOhtY2R6ABkk4yZfAOwDdqWM4VdZFO9sNoPf/OY3MJ1O4fj4uHYY3tzcwGKxgKOjo9oxlksPr4ekhEhta3WUSIprTMm10M/7eLPZwHK5rB3Vk8kEttttvXMzRufp6Sk8ffoUnjx5AtvtFs7Pz2Gz2cB6vfYcrBJfYdzV1RWMx2OYTqfegjRliNfkf85x05JCifTirnPc8Sw51ixQFNUR2fP5HH7+85/D0dERHB8fw3Q6hcFgADc3N8HJErzufIGHHxRodZUU4pRDqWvIle20fVN0UYdjURTeO+0jikvqa67bIO9qH5dhODpK6DHnGt27kq0aPol2S99zAw7KRH4vuSTjUgtwDKM/PLq7qa7O65CDC/V8HNtWmTEcDuGtt96C7XYLi8Wivv+Z1q9pfTQjG+KmZWj3YwOA91FLF4C7YmM7ru3G6LQBahcyicsE+q+1d5v+pHjwX1srSrRp9ABAtB/agja/4a6v6lftjC0KOmfij+6OBUDHKJCdelTOVP/OocGPJ3a4nOMV45BM+o648LkKd0f/hmHUgRe7IxbYe+HV39GFdXXtU/3TNnVp3DuwfBIuEMqHIE+VJn10sdSGlG4SIqaj6f04rpd5byqeNuDKcG3m11l3wldtSR2YRd2GmhMWf/QuWHcMMf73yZ2x6WOKKW0VXbIj0O2ELQMcPt3cNsDbXdr5yXeGlgEdbjy4+crRB14YL4eXSetG04ThUr5YupKVJ7/HysBdrn5bVO/V7li/vfiuWA5h+9ugazl/SPbJQ6GjKWj6TBO9AdfhMf0aw1K2z7Z28C775b738T5Aa/Nd2XO7wHtf7OWvK1jG6V2MPW6z1NaoXdDWdg2ZShd1xu7baN5GMLehUVu4xvCnJsAcejhDaYJHoim33vvuU6lsCtxAmcrfVZvHwKLctGlHy8SSwr/PyagLA1GusdyCt4lRzgoaTYe0uLgLyHGyWOSqFM8XGlr+WD9Ii4wUUJ6i/5pslgyVm80Gzs/PYbvdwmAwqB1f/GhEqWxOv9QO1jbF/LljSsMnjTcqjzUHCc2L/9KpB1oZ3MGFO2On02l9bD29LzRVN3ocMO9bXvcmyqfWxyn5h+0izXG87bBsiX4at1gsoN/v13cD4dGtMRkWi+eLdBpmbbc27Z2bjoNmbMjBGxsbTehK6TS7nOM00OZWTRZz/dUKUtqUnMzBHRsbMVqo7MT+iY03DZoYEPDDh/V6DYPBAJ4+fQqbzQaur6/h+vq6dt5bcFn0qthYj43xpuWl8pRlKd4jbsFhAdqvu4KU/MvRYXYl6yRadq3bajqENo/SpOWtcwGT4rsbm87pCeyI36psmt93it6mEGiQnWsujjo8yhqHX5aflu5CrdpCOq7Y5Yvho+X6ZQQ1EetnwVWVLx9bzNtIx41zvV+/sHxsByDtEuLkOKR4xBWma8/ftAxtV7GWz8XRRAX5L4Q0BYmTd8vafr1b3FQ35/Xxxw+QXbI+LfgszaXhuz9OghxBXv8/dMA6vCV7dzj995AuEBy3EnBapHo4eSSHSXSijJPyVM/SccypOvn12wXkzrex8K7mYctaja/vm6w9Dgly25CuowH8qym0dciu7Yz7avema7O7hpx1RhObaO56xmr/k/Q8LX3KfsjtWLm25EPox/sOTWwzMRt6rszKjW/jN2lSZmqtb7GRRJ2x+56g9l1eE8OpBYqiMJ3jjkcm0ovUNdAMMLltdhdKh9aenBZUDqS2Q+j6S3yNln0L8K6V013BPuizGoViygE39ucK7ViZhwq7NqZ13fe7WgzFFEFeXsxAyZ2NXNHAuOFwWIcvFgv4xS9+Ad/+9rfh8ePHQbl472mqLS0OrX0CdwIC2BRs/KcyHZ2hNI/UH3jPLsYtFgu4ubmB4+Nj6Pf78OTJE3j16hX85Cc/qZ20dFcVd14intVqVTs2kK5UnXIcbpZ5mvMAdw7jbjTqmMC68bt2JUC9YrVawddffw1HR0cwHo9rRzjtj7Iso+0Qqw+lBfHSXcq5jpWugTrUqJMNwK9rSsHW5ABPKxk22i4ocpy1Gn1WvLkL+yYOOAmQLzebDfR6PRgMBqr8jd3VTNNoRibeVwgxvRN5xHpPNK4BiqK6rxnLwnFJZRMF3LF/fn4Ojx49gn//7/89rFYreP78OXz66afw85//HObzefa4ShlAqLGSy+5drZM4Lmxf5IHYSQcc+J2/GtC727nM3QU4Z2H66GeNT5uUJQGfz3j/WgwpTUEyhmv4yxJgs6G7Y92Oz+q5uL0/tgorb50k220BOIQxDB1G7i7ZKg91QmHa6p/eC+vSuDtiHU50VCE+2naurnRXKyZy4a5859hDeioc/NnRQkqs48OyXBo/zLW/zy7+/bNIH8Xrtwt3UsUcfdj3RR1P60/zhc4x3sYgAmenMF0TmSU7hh3+sD/99La1bFFgPu5Axf+e917thA13ykrPFe7QGVvR68ZFNSWVQB3wbjcvzaff2UvbhqbB99jPpfN3h/rxpZheLlfbHQtBOMftnqUji/00ADwt3/GL65EwzMeFO1+B5MGds2X9zuvr17UkdOXBrufDfQHXg4fDIfT7/bptFouF14avE8TWHv1+37NXIOCJJLnOOSvsyzn2uvBvDCS7SZO8EnTVNyn7QQ4URQGj0ciz36xW8aPX74IPvgm8t2/IbdOu7chd4LPieO2OKW478GOGh5RBIxWu4ZWcB02FbI4BzYIz10mpGQM0w5+UntbfYtjuCtqU2YVzxdJuu6i7hjNV9q6UqpxyLE41azn3CXLkUSyPpRwLxAx52uIgB7eFF1OyJ2Vsj73TMK1sfF6v13B9fQ0vX76E+Xxe5+n1ep4TssnYTjmNUnm7GLOazI7Rx3nPoqyjwwLjLy8v4ezsDJbLZb1DjH/dy8tFGAwGXjq6w4wvbjRZYnVSaGCdQyT8lL9zxjG24WazqRcwlnnOwv8SDXg/7Wg0gu12G5SZgl3O7dQRkHJcaBCTuxqPa/1F+VuTlZIOps2PNG2Kp5tATEbGxrBEM6Wt1+vBeDyG9XoNV1dXxJgY8j/Pa6U3pR/wfsstSwKU9Y8ePYKiKGqZg6clSDTQjxuKojpyfDAYwMnJCUwmE09n1vRnrENM56ZpNONObLzvQvdL8RAHTo9Fb6fzcAw/H48pPo6BRTfpCrR2oMDlStP5bFcwn2/g4mIFg0EPBgP8SIiOS/+IYZ+HS6COIgwuS99BhvlijjKMp8cMo7OW0kF35HJ8krMWy0d6XfvrNPtzhHPcUlorHnPOzLRDVpZ1iJ86TSleyVErlSG1ObZJ9e7wS23D8Vbhch6eN5YmH/yyNfxSG7kwCzEFOKdu4YUXBdQ/dNa6fxdGw92zrhv4Q56+0B2xVl3T/1Vh1DFY1unoM77TfKkykF6anuIK08pp/PDQcUvjXXmyc9alLdUy6L+fnpcd3zUrQSqtRc630b9z7Ga7mPe0MqnORefH+2z/QZB03Fj9yrL0rqmR8DSlw2rP2KW+kWsf2bVOuwuI2ViaglUmWPu467ak8xd+UIkgXRd2F2P7dZAndwGSjYNDEzt0U3wx+1YuPslWw+U0wkE5Y3OMjF1BzsSQmlwo5AgGy+L5LmBf/cEnkpRB4VCFXmyisrZlV4bTruB1UFytyhjA/VbIdpEnll/j6V0ouJZx0cWCQhuz9J5GySlRlqXn7H3+/Dm8ePGiTjscDmsjsIXOfc+FucpOE0ckzxPbzVQU1S6p5XJZG85/85vfwNnZGaATa7FY1HfylqW/C5MqVEVRwHQ6BYBqp+lqtaqd5ADhnRKSc63rscPDJAVNM+Kn5kepTfHOSQAInBGUHu6oidEl1Ws4HMJoNILT01NYLpdweXkJAOAtnvYN/DSN2J2rEkj6CY2j4bhQRB6N7b7jx3VzwHwUH5VBGMbp4LRLdciBNguclB6MO9o/+OADuL6+rp2xsXbR9G1uaLPO7Xz88/HFd41bAI1eRVHA97//fZhOp3BxcQHX19fw7Nmzul95nfiu9+12C8PhEN58881ahnHQZLLkbOXjUJO/nLfQiYlxu9SbUn3HZXVO2bjrWjJISvh52U0hZSyW4nJ1gBwnLPJfSga1BdkBFKfz5cslvHq1hOGwB5NJ39sZS50M6PDw745FpyhNU3jpnKOEjhsJh78LFPHRMHx21UNcfLeqXybfBeuccNQJyh2inCaKD7wyKS0++GFSPlcnnPulXcIgto8rAxgtro0oHvceOlmlvOA59CjEHN7NwceJCJFvaDranz5f0DBOs//zw/l4rX494RnvhvXvlMW5JLwzNlZf2mdIl8RHULeBPCap7ohzEfIwfy6DfA6vv4PU9X94xLDDCSwPBDhDumh/SjhCRyyth4TL/4Vp3I5XmiZ8pycE0DScVt4v2213c/IhQ2w+2Ww29ZoPoSgKGI/Hnj7D4w8ZNP2H662SHoPt0QVI9hJe9qG3JYVDs71a4C78JW1twm1tW/1+v17HlGUJNzc3qj3jAQ4fsD+lzTJoA5Sgbf9KY33XPBNbbx2UM/YQBk8TR0Jq8Z4SPlLnoDKNMBgMvC+8OG7NWCjRmTPpcMGLRvJdANIVM4zx9BLE6iYZ3WPpKD0cb65RiBuBJT6JvWu071KB4MoeB66ANXXWdAGx9rJC106X+6bc5UCuM6OJ/NEM7xiXK8ti+Hh5miNMowvlIu7ClOJTTrRdL2Jy8UpyMAdy5icpLy336uoKVqsVoKNgvV7XR/3jvMQNTtvttnb4oBNys9nAZDKBm5sbb+dy7pzIIad9UnyYk1bLJ6Wx1jMl9yleCSddMFGcdwFII/IJ5QMum2Ltqs0viAPviZ5Op/Xx2hcXF7BcLgNZJeFKLXI5jRJOKS+my72P1apfxRxImgyl43Sz2cCrV69gNBrBj370I1gsFjCbzeDy8hJubm4CByWvf4yWVP2ccVPWbbRyLVCWJfT7fXj33Xfh9PQUvv76a3j16hV8/vnnIr0ATkfE55ubm1rn32w2Iq0x/VTjB6wbNWrw9Njux8fHAFAdD4bjpmu9huLjznFOO6U3lkaDlI6qybQmYNWPpfp0KS+1OYTzdIy/uio3BdxZ4p6RPtyN6pxHVTp5RyyQY3wxLb4D22VL/wvmAOVx+E7TuXr7ca4dZDp93C6N/+zodfn8eFo3TB860sKdm76sC52IDh8EtEjtgf1YPfvtK7cl9rPfhrxtpDhXJy+Fki4PZBHg14ums5cnJeSOWfm56ie3g9a9FwyHC/dlOy0b+cPnSw50bLhxWAJ4DlR8p2lLAMGBSn8UryvLd6QirTwfMOeoX67Lz+vF0/h5aR1Den36OG3hu5RHpsenTS/b7xMJz/n5Eq6u1rBc+tfBvG6QsvXxur/33nswnU7h+fPnnpEf7auHbK+J9WMO3bF1pmWdb7EDcrxN6Nwl3IcxYbVjp9K2AW0NIdHSlAc1/L1eDyaTSf0BJcJms0l+THkf+vd1A8saWfqwmZ8GhfH8yrEY3i5B4mNJhlloyZF9B+WMRbiLhrcYnlKdZClHAnpWP8eN4cPhEMqy+gqkaTlNoKkRnoNVYFvu2uXGkl3wSYre3DI1Wps4KboGjTaqnO2DtpSBbRc0NOVvTTjvS0mKQVdj1gqxsrjDImWoB7DJf5z46Z2ilkm0LP0drJrDJWdhQo36vV4PhsNhwAu4wzNXUdznXBiDHKWcg/bxjtZf1LmE7dvr9eD8/BzKstoBi2m0+yVp2b1eD46OjoK7cp4/fw7z+byuW9eLiS4g5szidMRw0LxNHEscKL9Lc5jkjL1r6Pf7MBgMYDwew2KxqO/yTOl+qbbCPtpsNt6RsqPRCObzucdjFJ8VJIeJNs9Iaagxtuv5yLLYiM2VRVHAarWCzz//HL7zne/Av/t3/w5evnwJn332GfzqV7+Cq6srGAwG0O/3k1/08wVdii5My3fo4/xA2yu1A1mDXq8HH330Ebzzzjvw7NkzGI1G8OMf/1iliX4gsNls4OLiAiaTCQyHQ1iv154jVJtPaRjXp3l63CWKd5nz+OFwCE+fPoWyLOHVq1f13WIcuuItiU9Txr9c/BbYp64p1XUf830T43Mbuui8E8PvHBHVHYtuZyzubg13uTqHagmSU6ks0bEJdbyfxsUBc3BSvPSoYprWr4N/fDHSTtMVzJkJynHFWD/nPPYdpRQf0ut0Ulo3jUY/n+uD0KnKjy1GPFUa2WGN9CNeHk/fKT7E6bcXkDgIwE9TqulIjgQOHXyaXZlhm/r1rNpWv4MWw7kcpo5V7mBN/YA5eR1vcB72/908g3XDerhw+ec7Zl2Yr3+6H3jPcpk+fhrm2t7vF5oW6dbSSHSGdMR2vvL6S/+xsOrd3Smrt1UcqjTPny/gq6/mqcSvJcTsWb/7u78L77zzDvyP//E/4OrqCgDc6Sz0KpdDBqt+22ZtR3FY9C/JBpZr69gF3HX5VujCZreLtd1d9mG/34fT09NgDUM/UOXA128PsHug9k8ezt8lm5Dm82kii9v0ecre3JUfTMNzkM5YabKxNoTFOJST12KMb2pcw2frXYZS2lwG4QablNGjC0gxOX/PrZPWVynhkAqzCJeYgdVSl121OcXdxHDvL8xtNFLniYQnRUOq3VO0dGlsTk0u2jN/R0cRQDPjVxPYpxKSKivmQKBglbddTrb8qyuezrro4bTT3U0Y35XxuCuloA0NUliT+uXyA4bhV5K8nTV6yrL0jjtGR6GmGGlhkkyMLT5pv2tyMCZHeHjOXE3jqcOUO8Yp7tgY1PQqLu9x5/JsNqsdPDztvgHLxQ8ljo6OYLvd1k5SCtTpRndco5OMtiV1HNF8/X4fRqMRTCaT4CjanPnLMhdrMofHpyCWrolOkKNzIIzHY3j33XehLEt48eIFTCYTGI/HUJbuCPJY2RJ/xfQPHjYejwEA6p3MEt4YP2t04Zfc0+m0vvdVS7taraAoKifpYrGA//t//2999Pfz589rZyiWrdFG20qiEU8MwCOwAdyHLfRjF6qz9Pt9b3cuXY90qdfEeIe2ndSXKeDG7Zx8Xckuiw7cBFK8j2ExYzXPG0trlQEW2RKm047+9B2xFQ3hEcM+abID18VhnSmOyjGI+Cra/HfHExUNwJy41b+/k9WVzctw4VVe+gykTHwGglff6aq/+7RhG/C0LqxKX9VBcrxim8d2EXNZ7MfTMMTpwjn/QQAaO+rsJ63ZvZxaxrrdKf/Qevt9oce5cgryjPhceFFger7DVXqWaI0DHRf+uCtZWMxRGHey0rFMy3SyhqeHIC+An9anndIf1s0PC3fZcnlG28PHKztbfdp0uuS2kd7ROavR6MoL8Xc3H6egzdrPkte61hwMBh7e1WoFb7/9NnznO9+p07z55pv1M36k2+/3YTgcwnK5hMVioa4L24Kmz0ggrcs0fDn6hFWPaaK/a/ktOvIugOsW1rrHaNzVGjYXZ2xdYylL4x1KB/c15JSZY4uNjQG83gsA6pPQyrL0TvRJ1eMQ4a5sIbsA63yD61peb34KLEC1Zl4sFlkyMxea4qE8J/G5tmaieSQ4SGcsgE9000brwmDcNWiGAIk2KSy1c9RS564m2hQOiyKgGQxSfR5L07Z+Ei4tzkKnNqk1Aam9uqxv19CF4LSODSwvZUiyTtypMlL5+CRDJ637pjxoYJ0sY4Y+CaemzEtjzmKUTfFAbFzF8tEw7Gvq8LLQloIu5sIU5BileZ5dKpY41mgZ1Bmr9QfnNzxiE/OjU02T4VqYddxaFyOW+TOm3MXy8PpJclGKa9OX6/W6dnTirmRJ39knYPnobJpMJt6dwZJsoveucZ7n9ZB2Vg6HQ+9eqpQzQ5pT8Dm2KzOli2lGnZTBIfau0SJBjiFmOBzCkydP4Pr6GsbjMYxGIxiNRrBYLGCz2dQLcKtcpuGxDx5p+QBQLwK5I92iZ0myB3e0Yn0k+jAPfsDQ6/VgtVrBJ598UvMT7tyQFoPS/BCbZ5Ef8N5QzIsfIGA83nEjxUn1leqktVMKYvoXTZNrPOXzRqpfU+E5YyFVb+vaJ1VurBx+F7DUflyPzpXdqXVT7J2WWwbOC7pzFoA6JKkzCqtP41wefkwx1PEuL3WS+vgQDz7LwHenujB8pk5fTjvSzR2f/NnR1d4h6+ONHVtMaZdo5mMUWFpu1HUtRFnBD+dzEuchUMEgapT8PGNYJ9qntH8kGuS4QnimOgC+F0LagsRj+oKlD/PJ7VGCGwt0DLgx4/4lByJ3woL4DN59srI+4o9LGuc7RGlafKZ1o+UDc+LKeCS6/R8Q5ywvw/+FNDu5JrWNX1+tTSmtnG4exsEy5+4Kmuj+OesdALeuA3DG/CdPnsC//Jf/sk4zm81gNpvVeY+Pj2s9L3b06a5AW0/m6OdS/lhaPt/ztUEOaPYNKuPvku8otNVHUzi6BktZOW2bwhfToy1rxy7alwKu13F8ojMW4yx4D4X3ONxXey+H3PaVnLHSfbGW9bqFDos8pGlz5W7MVpI7Tg7WGXvfmDXHQMTTS8Y2el42Be2S49SgyFVsDkmIUYOuBlbjeSpPG+Bf60igKV+pfBi/DwfIoUNTRbWrPFrbo+EV8VFFAsAZ+KlB9ZvahxJIC4SUs4wbnGPjIjbW2i4adtmPh8QjfPFF+4c7pZqCqqzcLrbxzk8ca9wRIS0QLy4u6uOoVqtV/ePp29DIF6CWhTXGd93HvF+k8Bg9baAsS7i6utqbDpFqP+roit13T+UN7hrEI8fRsRwroyiqI3cvLy8BAOpdjKmTT2JzSb/fh5OTkzrNcrms5w+uE0lOKq3MLqEN/6LMkPpovV7Dzc1N3R8xoE7CHNlOZcfJyUnNuwCh3inhoTxTlmW945Tuqv7Vr34F5+fn0Ov14Ouvv04u6vgzXwto82SKz2g9pPZG+un7ZrOpd5BL6wxNL2/jvOM4Y3qABLE5aL1ei3IA8aNBlubFvuV32XZlvKV8ldNuSBM+x9Lx+uC/5hjtct3E52jbWrVyQGy31VHF6AwMf5iugF7POVbBO2rVd7z6O2R9p0zoFA3x+I5JPx0wx2+VJtz9iuGhc8ynw3d80vpVaanD1+6QBfB3xLoyad0w3C/D5UE8frgrh+J07Rw6ZV1Y6CjkeWk7UTb02yWEFMvapsSwTlXeEsK2o2W7PvfLDI8rbjs1Yz/7YZQHyjodxnHH4HZb1v84H1RhWxK39d5d+vCZ/txOT323rRunpVcvN6biDk5XLzk95W+fFu055pxN1SeFX2p7uD2e3Zdz6b4v4cWL6njiq6v4lQ6HAl0460ajEfR6PVgsFjCdTuEP/uAP6usXfvCDH8C/+Tf/BpbLZT33z2Yz+Ou//mu4uLjoqBZxQN2MzrdUr4vZBJuWt8v0KdjXuu8B7KDpeRT4GqttP3K9NmX7TvHhZrPx7nmm+R6OJr4b6LK96YaJmJ2mLTRZ27QdC3ztKMHBOmN3ASkvudWoZDH2a2VIIC3ENaOStIDWDCIcZ0ogUnzWQdbVYORt0NYxouXPNXqknKXcwMXbLma8kiZIq+F+V5OOZhTMgV0qg234IsUTlvwxYY0KNgDUxku+u0Yq8z4pEJaJLKaAYXxOP2pyMEUPpSmFT+pbaZxK+CgtFqU3h9ZDB2pktSpQqXlYi6f3kKJBCNNzfuB9t1gs6vzozMWx2mQsSmNZM6qn5hAN2hjEaXtY6kdpT6W3pKX3feyar2P46ZjkjiaahgM6/aljzULHdruFxWJR797G442bAM4fdIdtWboPfmgfaPIwpVPw+KbzqyWfJoudsbj0juFF4xkeVUXTtqWP4ymKAkajUeD0lPQ7Cbc03jD9y5cvYblcwng8hsvLy2w9BGVdTO9LrQF4H9MxQdsf3ym+1WpVy0xJTy9Lt4u47SKa94mGz+LA1PqKHulN11TSOKDppLIlutvOJ1IdLPI+NgYlmnL17lj6lO4UaxO5jujQlJwbBXt2zsDy1vniypXpd/T4zlcXFx7f68eVtzRKO2AlR2gpluVwy7tNq3csK+5ow7Rph6z/T7CB7CCUd3/SevFwv15Qt2cYT8PQwRq2k8R6Uhv6ILe3BHlTSthXWIbkeKXtzctDng3LL255m0cU4Ncr3B1Ly8WyJBrcGKLjLLXLNbUzVsOFY6cEEJ2g4Y/SSPuWjnP3LsUDKSvU+aSySOuwf4eL0+/ooI5eXh/NgazV2cfJ6Wc9XT/NZmv4+uuFkm43kFrPxfI01ecoHtTRUdd+7733YDKZAADAhx9+CB988AHM5/PakXN9fe2dUJIzX8d0LC28KJwzlurxqOdy/E3bw9IPWrtb7MBN1gZt+7crsOpRuevzQ6ibBhZbFOcBrtfH1oa5doWYjivxIpZPHXSazabL0ycfYHegrckAoN5ggc8AMg9rci7X9sHBalPOAbrWSu32vTfO2EMXfFagi38OXQuQ+9he1n62Lejj5aDy1tSBIpWZ6tdUH6cEwr76tA0vxvowJiC7GOO5AjiFCyDdFlSxRqPuo0ePAuX78vKyvvMAQD+KoW073IWs7Kq8GG/QZ+nu7Db9To9gzIG7kLF3ORdypwj94ldaXHJoYnTGfun3+/WdhjSOy3CuqFNlDe9kREN/zMmTo5whDn6sbayuUllaWmm+a2qg522i5aVl8TblOLrkx13xNy7m8NhbHoflYt0k5R7po+80fr1ew/X1NSwWC+j1erUBiDuBrbpDr9eD4+NjmEwmcHJyAl999RUsFouok0oqJ1YfjN+nTOG8M5lMYLlcwk9+8hN49eoVPH/+HG5ubmo6cbzGIJd+xIeOdzxaG2my7PTH8Y66JB7DVJZl/fzpp596dUjt8pXqZTGKSQtY/ACF6h0UD/1AAe+2xXR4EgH2Ax8jRVHUXzO//fbbMBgM4Le//S1sNpv6yOeudTGKMyUzJRwxgxIdI5LRkp56gkbgfYyZ1NxI01jnKsnwJuG2GGljbdBUrhQF6tVoFCwAh3+1A9Y/drgi3z9quJqPCwAI07u0PC862vxdthimvYf/wMK8VrnlRfDi/Z2y3MlL8VD8qbRaGMcBNQ2u3hjm8yCWU9TOvZI8FyyOO2WrNDTeL4fShnnlo3UpW8lsz+c7KQ3Pb+PVcFdz2Af4ju1XjQOXjzpuOf3Su+PtkqRB3qb/bocpAHg7y32cdMfq9vaf7ob1f5tNtRO2+nfv/k/eLUt3zNJy5R8Ez45eWm8+/uNpXDuGaWib03heRkiL5T90PuMOWJ6GhrvnkLl9fvB/hw587miqI4xGI+/KGQT60S6uS2m51HagXSmE6SmtOfonjneqI1jnzK4hpS9p0KUtrWvgR5tS3WxfcJ/s6zFatY+NNd24SdmWvJyG7XYLr169qsPpB94AUK9VDpVHv2lA5aNm2+Yf6dOPaDD/crnM/qhXSt/GptFUZraBe+OM3UWj3JUwlRa5EuNIhgOL8TQW31Rw5TC2payUYbANaPmlAdbVoIsZaHm8ZpClcbvmd1nBb/+VYioOy9CMPzwtpzdFHx8jVkWA55Fo0PqWlzscDuHk5AQmkwlMp1O4uLiA2WxWL3qlcjS8TaBL3mkjNyxGRCuOGL4YX1vHvMWgKMnntmMmVXYX6S0Q43PJ+Bobv03KlMKpY4y+x8aNxC+UNvoBDu6Q4nksckyrA5dvljaJyb0UDbF+S5VFZZbkfLDgaiJrrTR2AXy+xXs3ed9rupblCgg+t5dlGSwceTmaTNHk1HA4hOl0Wju5tDpq9Ek6R9P+0sZArl7FeWe5XMLz58/h4uICrq6u6vtTm9KXUzc02tFdkzljWOpPfEcnL4bhruk2cyotQ4vDeGpsscgvjhuNHtJdowjD4bDebdLE8KLFazJdm7tz+5zmSc1pu3C+SjRo/NeFHkt1FknmS/qzhDunnSl+i8xxtPjOEKh3oHFHrORYDe9TpukLwTkV0u0ceJhWeye5QHfo0bL9+2OpY8+VEduRymn1nX8yHXoY5pPy+2XrNFW0+/j8uLAd/DpI9arSV+FcdvlpU0PTwrL8blqtLx1dhRdG247Xx/ouhbtneZczTYdjAx3rbhzwdL4D1OYozXGq+o5hTh/9+W0bdhRPJ+UL20p+d2G+EzekLXTKamXwPtLycxnqp5EcuGEdpfpa0u4LLOsnbe6xAuaTrgZDfKvVCubzOVxeXsJisaiPKr6+vq4/6KU77SSDvjQXa3Rqa1IpPBdHan1uBYtOa+mPlM0lZvPJ1WMkelGvteLT8Frj7hOk2k4KS+m1TXT6trYBPGUK8fK1ONdVH2B/YOlbiafoSWFSPqvNhdKQI0st+HgYl+FWuWjBT+EgnbGaMN83aJOHlT5tIm4DaKBCg85dQRMDSBOlqw10YRSylpMyZlPDADoCpPxdGbR3CYcwNjVoMlatxiH6PBwOvaP56D2wJycn8KMf/Qj+8A//EP7tv/238F//63+FP//zPw9w4hje5fn4rwtox1ZYITVxt8G5S7m267lQwq+FdV1uLFwztgP4d5vQ9HRxzr+aRBy4CKeymDrRrG2du5ihtEr5uHOs7UIj1r60vpKy2QS4g2ZXMq3JeKDtulwu4dWrVwAAwdGrZekft4oLA2qwiek9kuFGGkc5iwdckE4mE+/+Wqlszkt8EauNrbvWN7bbLSyXS3jx4gX81V/9VW1E6/V6MBqNoCzLetw25VNpRz+Ab5Cez+dQluGpCxw0GnCnPR8D4/HYC2t6hHfMkCY53gDAO2pbO54X32ML7LIs6/uPpfLx1IKu58K2RkMtH8cbM8TiXPKtb30LHj16BACVHPn1r39d73TexRyN/DQajWCz2TT+OEGCoihq5znVraQd1DQPwj5kBu7C22zw/uUqvNej98i644wBqFO2ALh1im231BFFjz+G2/jKcVXcOvwQl6unw4XveBwwj/chjNPuj/XzAAnH8rhDQMov7ZCFII0Uhm0Zp4WH+3FFATVd7rlgcZi2BJ+FYneuAqlvGEedxhaIDaEQh1xXR5fWt7SOvA38PqVl4jOdlzDc/8kOz+22vB0fWwDoQVHg+EY+BIIv3BlbfZBEd79u6l2weHJCFUfD5Z20fIcs/dd/ro3oOKXtRMNoXXjdsP/8NuP4wzLkMEtcfEctPrudwlC/03TuvlgX7tfN50tXn8N0TnAH7D5gNpvBZ599Bi9fvoTPP/8cXr58CRcXF/Cb3/wGzs/P4fLyEvr9Ptzc3Hg6tXayEdprcIxouj8Pk8LpVRyptDH8+wZqw9Rgl/xHbd5dt8chjpuuILZukMLpyWMWsPJ+zMnGZRc+8xOs9q2DPoAOkk2Vrq0poF2F81ZOH2r2EUkWx06f0ubJXa9ZU/hFj14Tg1eXYDVs7opGijvWgCmDdcygrOWxAjcepGjRmK+JYTMFuxCSqX6QBEMMtPaSjFAxeqT20NrVajzax9jT6M5JH6tPrgKQKn+XEDNIxxQNbZHR6/VgPB5DWZYwm83g7bffhn/+z/85/OIXv4CLiwsAqMYv7phBo1u/3zd/FZQCbUHRFprgaDvh4niiYzw1xnLK5/14KMZIXuauIFWXGC9ZFqWWslLyIgWpuazJHBHDJb1TJS9Hf6DyJNY+uW3TRum08rrV0BIbr23lQ04+NKZwGS7RCwCi4cVKG9ZZctJq+hnGoWEIoHL8LBYLmM/nHu05YyZ3brfgsspfKy2bzQZubm7qOTFloOLl0vGdGk8STfQ4u6bjRupjPn81hRQOaixzhtz4l8ZcZsXkTEq+8h/it4A2j1h4zNq2nG81XV/ju0ePHsGTJ0+gKAq4ubmB3/zmN+rc2JV+MBqN4OnTpzCbzeDy8tI7KjoGUvvz+Y8e5Yi/9XotHs+do/ukdAKpfWRDSwmbDXccAdCdrTTMlS3tMvWdcGUph4F3H6hz6lb0OdyY169GFZ+6t5XSRct1fcZpR7x+XIW7inO4+A5ZR5cEtB7SvbYVDn8nK8Xt20r89sY2xDYL6+zTWIXJO2Z5m/lx1BgIal05vhSE5WHGsL7577RNsY2kvqN19Mev4326+7QM4otCbns3P4TOUnSmascM0zD/F9LlP2u7Qd070kjzOtpl57XL4/qJ4g16sqTtEMSK6aW2p30S1t2vB6ff7yuOk3+sINcBy18ut3B1tYbFQv8IMndOTuFxdKRluWWdkMLLgX8oib/ZbAYAUNtTrq+v4eXLl/DVV1/Bixcv4Pr62sMRK0vTdS1tifTQtFQ3i+G1QFc6Rk75OfpATn9a6sJPlYutKR/ADjEdMQaxdYLGH9Zxw59T6/QUTQ+wf4jZOzBeWz9JYVZZ2cWapS004UPRGXsfGHqXNFLcOQYhDpZFMy0nR7DQL0bwzqtchuqqDTnTtxHkVkgZVnLx0PdcpbAJ5BpT9wFdGLrvGprygTWfZHDUdtJ88skn8LOf/Qz+w3/4D/Cf//N/hj/90z+FX/3qVwAAcHFxAX/1V39V3ymIRxovFovgiMsHqID2Ed/lQ/8B8nmR4kgtmDhwx8EhjIO2IDkQJGM1Gm4A8ndDWucKDvSeQx7O5+smzgFp8WAByQETS0vjcQ7X7qnkC/sU3RpdUpwFmhgJrH27r/kP24HvYMQ43k7c8RHTNSw6Q1EUop7mG/hcHDpLzs7O6nni+vq6/kKfykCtvhJtGn1dAX7BnvuFdVk6R/loNFLHeQoHQFj32GIQ43DeRV6gOwa5IShFA8fb1kEnLUQxTLrvGAHv0qIfFUiyG8NjXxTTMcPrQnfgxtLlAO3DHD6wtLU0J0jjhT6/99578OGHH8JoNIKLiwv4yU9+EtxfjtC03rzsJ0+ewL/+1/8afvOb38BPf/pTmM1msFgsgjvTUjKKy+SiKGA8Htdh2Gfr9dqrE51TLfWzrmEt65/NpoTVagObDc6NSBPmA6BHFkPtSHWOvaoMfzesi6MOEP++WYorFQficbWUFt/JCp5zlH/47Rx0SFdVz5I9YxtyfA6n3xVhGh9HmK4oHM10l6cfLtHjnH+OPgTK36EjTNoxq6WleGrqg/J0KBKOW30Iy+1P+8mnGeP5zlnqFPR1AH7/q+P5Eno9uN0BizK8Ci8K3AG7haLoQVlyfdiXdxR3uDOW/uMO2U2wOxZ3xpal2w1b3fNMnbnuPlpaJzceNWcubR8+fvVdrth3Wnpajuvn1K5WIHj9H73bVf7HfiqFZ7czttoRi7tnebmUGTk9Jbx6tYSf/vQiwrN3B7vS7/HDIQDHQ3hixenpKbzzzjv1vPbVV1/BJ598EpxigTofn0slfV5zEnC9nT5Lx3J2pXun9ByMwzbItWumdDhJ12ir51Lgd/suFosgDZ5QJ+m+XUFq3X2fQOIBvvuUx+fit66Fc+wDdM3blLYH6BasfZjiMQ6SPV0bf/1+P6BBS6vZXbqCtjLiII8p7hpyJiFNcOROZLllUWOrZHiJ4eTGH55uH0ZROnFbDb9dTZ5NjNBN8GlpeTlSuRpftaElZRxp0p9N8DQx0nQFVqU5lofmk/LzvqXGwZjxEo3Jk8kE3njjDTg9PYXT09M6jtPEDWwW0PoLjaOclkOF2ARKZYvk8KILAakPrQpDSu5aoK28OSSl37pYw7SaAZjn4X2Uo0zRY9CkciRDusVhpUGqDWIGZa1eEo3agr6LOZLjzeVziQZN7qa+NM+hVSqv7VjRHFGWfDmLctrXWh9yWRajdbVawfn5eX3fLaXFIjslPkstWjQ+kWgtS7eTl+5WkOpqAatMSKXRdGqtDm3mAFpOTJ/IxUvpk2jVxobVQEXjqf6BYyRWDuZ5++234fT0FK6vr+Hq6qo+AaSN/KI8lQJOW6xcxCvpCJJsRkCnRL/fF/W+NkY7ie/6/T6Mx2M4Pj6Go6MjODo6guVyWR+nneJhyfhr6Q8tTUqe5NSTjg9NF5jN1lCWAJPJAPp96txwDtjSc5boTlOeD9j9s1A78LhT1dUd2A5PKR6Y07MoXDqfP6hjrspTFH64XA53kmKYX65rXz+NtmPXx+0fW0zTI31aG1BctA4urWtnLY0f5vqJp+V5aJtbwO93Dnw86uVK7ULfadtTXD6v+vVwvO7bcapfQeKrOOec3cJ224OiqByylZO2AMebdGz4O2Gdfu2cqPzYYbcjlh5DzOMkx2tYJyo7wrEs7zx1bes/+2n5HBU6Zf0+lB21Pj0UZxjn14HXR3L0SnG0TyX5G4R45WrqbBP5nANanthc2gVwHR4/ZsXdr9fX13W5Z2dn9UeY9IoKPh/hs5X+1HqX69tdtkNq7RnTO7V5u6mezdsxt54xvWS73cJ4PIZ3333X6z8AgK+//tpz0vIPAduunZG2XUJqzcvfJR09hgfzxurRZL3F+Vqza9AwX+bLZUq4OK/HdPvcejxAc7Cul3PHYWxOsfYxT5eS1RqOHDlpsaVpcHDO2DYLWit0xTA5IDGGZgzMrX9Z+kes8bh9CqhdtmGsvFwjadc0SIad+wT7GHddQYxWbhCleSxgNf7hF8IWxXM6ncIbb7wBT548qY2U3NDY6/VgOBzWO6AksPYROmInk0kdhkddHjpY6qgZUbW+t/J1Spltk78J7GpM7nKsW+Vgk/JxjqNfRAO43Tv0XkhLGSknTQpPm/6WcNJdSNqC5a7AYsTnTnK+oNLa2hrOQeLjGP9xQ4HUxk11JSkf/aWuleA4ePzNzQ1cXV1Br9fz7k6y0BVbuMb4Pxc/4sI5rM1dmik6UsYmioOmzXEOSryitRfXG7jTKrdsDhZZSnkMHYb0yGeaVhsbHB/2L5VLvC16vR787u/+Lrz33ntQliU8f/4c/uEf/gEAoL7zt8kOZ26c0RbXOQt3Hm8JQ8Cjwk9OTtT7cbuS10VRwHA4hOl0Co8fP4Y33ngDHj9+XB/BKI3r3LKlnfVa/VO78K3Ax2Fsnjs7W8HFxRoePx7CaNRTHBgA1KFa/fs7Yiv8vhOkKh9I2SEOIM7cim4fv+Mv3i5VmHN++jih3jnpl+vwxJ4BgO3CLAoQcMAt3QChUxC8+iNeDKP4ML/fh/IuWZ9GXgawtP49vjRNFeYVCejEDdnEd3DKaeIgszUfW66skCbXn3zHdBVOnfg+L2BYhSf8R17Hu2ArWVrWZaPTFaC6I7ZaV6IeifzB56yqTP9OV9zVWp2gsF5voCzdfbC4QxZ3x+J9sfiPdPmO23B3rP9z49EPw7aP72AF5kTFNIjHtSEwvDzcz2eLi/1rzudQfuEO2HB3rcSTtP98+ji/3gXsc71K5w50wKEz9tNPPzXh1fQKxI9ppF2zMb1awkPrIeWjukQXtsQ2enfM4WWxd3UJlSxaw5MnT+D3f//3YTQa1Xfdl2UJf/mXfwlXV1c1bdPp1NNfpR15u7R/7BIonyNYN1ns2uaPNHVlJ+MfOx7yRpIH8CFl24ilzeXTHEdsE1kq8Z20tpfKz63bwTljdzWZa7gtjCMZyVJGri7BapzsqpxYvGZ0zK0zF96pwZLqJ82I2kWbxZSnWBrONzHDnNa2GjQxLsZo7Yq3rI60NmMk1+FjUb40wWkZEwCV0XGz2cBqtYL5fA5fffVVneb6+hq22y388Ic/hLfeegu+/PJLT4GhMBqNoCgKWCwW4jGPkrLMjcZ4NKFUr1zoWpalIDXOUSGVxj41Gkt5tbK4ESTlRKJx2lii49mqMLRVTKzQFC/nV2400vBLslJz1FCel4xT1KijfeHMQaOn6RzB6WoCdGFeFLZjOHOcB7w/Uu2izb8pHUdrdz6GNHrbyqkY36XyNelHS1pN/qT0yNi4oWOPfwRk6aOU0SmVTqML38uyMuQOBgP4zne+A9vtFn77298GjsDcMROTvRINvE4cnDFU1gkk3V/rTyk8V8+wQux461ge7SMAasikclX6IETSN/C93+/Xjkk0hk0mkzpNv9/POqIK8VrbidOk0UrD6LuVH8uyhK+//hq22y1cXV3BYrHw6oVtJx0dnFMPzIe0LRYLODs7g6urK1itVnWZeDxf7D5gjovSg8f+cRpXq1VUpjZda/A5IHWPsf8OtVOnqo/sKAHPocqdp9TRwXfIFnU57p87bJF2xOM73ar6OVyYh++I5GW6vFhvf9dplU/fLUvz0PJcWbw8H1w+qpPwuLJuQ4qXO2WldqqeiyCOvkPCMeun5XGlEC/j0MDO0kinXx8sJ2y3GH6fl6t02M7uHx2vOIYrp+wWyrK43QGLxxNXeKrdsfwqHZ8ffH2a7mx1Rw6jU9Y5Ybf1kcTSjlk8ptjNr9z5Sseq75gE5mBF2ugYxzaj6cI8ob6lpfH/HT6XhpYh72yN4eOySX7WdsYyTvHq5P/P5xv44osZXF2tvXQxOLR1JYJGv6TnSrokpkMbCt9BGPKGrDsgSLaGJiCtAax6bS7+2FrLarvm9MZsIIg3Zx2npaE2gbKs7FjD4RC+853vwMnJiddPqLfmrIlj8V3YJXcFUvs3qbeGO6U/IlCZRenia6/cMSONP75uydHVH2D3EJNfKTmT6kfJJpFj38M0fI2hrfet6xCJnhguKjuldSjH39oZm7NwvmvQJgxuLNIYKCVkpElXKo/HW0FSRrpo+yaCk+fNoSOmMKRoylU4U+l3NQmnhFCOMRXT0/9DAonOJu2560WCNj5pmhQNXBnBZzSOrVYrWCwWgTN2s9nA97//ffjoo4/g7/7u77wjdCgNo9EIhsOhd3dXapzThQf9GtAqq7qUJU1B6w8O0gLJsvsnJacsaVOKgNaGXHnVcKXKskJTWRyjIbYg43Mm7wfqNKVtgV8/Iu7tdht8SMD7Gf/LsvR4XqNZC9Pqo/EOXXRYFy8cDweqUzSZR2mZ1nlOoyMFTdJYFvCSLmBpZ1peyljA86TGYJM5TKu7pKDTeG7IwHFA0+Bzr9erjaM8XqMFx4kmlyyLeitPowz+8MMPYb1e13PgPuYVusCRDEg5Y5bmsdCuyb+2gDjpnV98ISfN4xQ4f/E4bczF1kCUt9EZizAejwNnbFmm7wyKjTltPpDAMkfzdBa9EKA6Em82m8H5+Xl9MgrNR3lPo98CiGO73cJyuYRXr17B1dUVLJfLeuwPh0MYDAZwc3NTjztet5jBpCyr+/UwDepQUl5tHs3VUahhwiLfsUz/+FPJ6SHtjMX8Ltw/3lhy4OJ76YW7ukoOSVpOlR7U44otxxQDKdfRF+6iBZLHd8BKxxFzeipaIEjnz4+0rpqj1q8LrYN79nmKtx1vP5fPbz8KEuu4NGUkXcpQH40m7U/rS+sfOsNd3/n9FspyxOX/o0PW/Vc4q12x1OHq9AI/3NWbz5P0mGK6M5Y6YOk/D6dyw3fs+uOWluWPv5K0qT82/fASKI/QeP5M0/M0FA9N6/q3ZGWEfeS3IbAfrZeUjjtgpTDKDxx83p7PN/Cb31yrRxTfNexyvavprsPhMDhBBvl0vV57OjTNx+e8JnaEGFjm1jbr/hhwHSi1LqX/Er0SbgvtsbUavf4B9ZPBYAC/8zu/U++GRdDuwbXSEaNPg330jRZvObEvZf+y2G9StEhrSx6eysdplj7yj32cvqt+iJV3l7bRfUFuPTVZEFtPpcK1cmLrOm3NKK3LLfIiJh9zbE0xkMpo7Yx9HZlU+godIM1AXU1KUrkpI8OhgNV4ZU0r5cuhwWo0iBkcmxgeOMQEncVoqOXf98R0X4Dev8oFMz3ag47ZlPGKAyoNVJk4Pj4GAIAvv/yyDvv888/h888/h6dPn6pKVUweaOGakR2g2j11dXVlHmOHKEsQqPEOj2DmCmRT5Y23OzewNmkXbR6wGiAPHTSDtbSgLIrCW2DhYhif8StmC9Av7zVaLJCae6X6xRY6qbIkPqBxkvyn5fD2tJRjdURJaa3OtyaA+KU+kOraRdm5elmsfKtDiNcPw6T2xXD6oQmXb5hH+iJck3+7NvDwOgBU8vOrr76qxyqO/9QCe1f00HfapnzXBM2jvfP0sT7tEtDAiB9a+UZen0c4PZqspnnou8Rz0gKbvz9//hw2mw28ePECzs/PawehZPjMAel4XM0IFJOzHKxjgo7L+XwOq9UKrq6uPBo2mw189NFH8K/+1b+q2+qf/umf4Le//W19Kkouf2Cely9fwt/93d/VRyTP53MoigLeeustODo6gl/+8pewWq3q3ckWXqRjNQVS2+fK5RjPWaByElXOHIDi1vHgOzYBAHq9uGO14lt0eGgOXOdYc3mrf5oWIIwLHX1SnHv2Haw0D0CIi+e9DSnDOMRdljh25fxVPHUK+zTQvEVBxxOIeRCncza6WJqf46ZpeJ38MMn4RygwsKS7s1YGH4fOp2HfUWc17UOZB2h9EIdbn1b3v1YyoLr/tXLA+nK+4v+iLpeOfxoX1tE5GsO7YumxxG5nbPXxyRboEcW+QxZx+ffLUudsVY7vjMWxHI5Pf57FMFAcseH85PqFpuF5ab/TdqF08D6S04aOZl4XP9z/wAQ/MgHibPX7zKeF64j3EXZBO64r6ZU19Gc91pWeEKKVc59A0hlz8lr0yS6gLCsnLMqZoihgMBjUNoTBYOBdEcEBTw2SaL2vQO18fO2can/tAzv6zmWn9ao2npeGafaH+9gnh2wb7RLa1HMXtobYOkWTzVS+xdZ/sfK4rSBVZhOQ2ungjineB6SMLTSO57MYRjnOmDBMGSQlfCk6LWmaDhptMk61S46hJIbXSjfNb83H81Bad21s0wyqHHi7WY3DvKxdQ0ohso4dK0h8SZ1AVEkDAG/3aIwWSamgfUCNzPTeV7zHAgDg/Pwczs/P4eTkxPvCbzQaiccRx/qIG7b4PZlIBy5KwsV7CLtWsJtAjG8ko7OUV5Kl0vhJyYqmyiTnHevc0RaajB+JntQY5byI4TSOLxywfelxizxOKzu1kMyRg9Y01n7yjWBhuMaLkqMDAMxfnMYgd96LAe0XDW8Kj8QzXKZq9FrrEVOgudzW8rUxPMTkFjdqSOOHly85MpoehZpK38VCarvdwsXFhcfHeFxujv7WBCyLLzQ2dGWwsS74tPKs+ekpAJIhVpoLY2PKum6QcNIP2ZCO8/NzKMsSLi8vvXtNU8fopmjj84mWNlWGJAc0+aut09brtXdU8Hg8BgCo7zT7gz/4gxrHV199BWdnZzCbzRqvH7bbLcxmM7i4uKjHPbb98fFxfXSfRa+gczCtK+U/7SNkmie1Kz+WtylUzhzuhEDeA3DOU+48QUejvzOWpi0KP48jV9pd6vBW9XL4qcPN3x3pf7AhsWmFW95d6spCOqoyMO4Wg1cWrQst170D8J20VZ5wlyx1rFK6+NHFfvtpdaHvodzjaWgY9omLK4R4P68EOeLed9xyOVjRU6WT52vkB1cv5BM/v6QDOV7GjxCqO2Odo7Zqi2o3bK/OA8B3y4btS+ePSkY7Oe2OJ96Sf2knbLgj1j1T3Lw88P4hcMRKO2Vde9J28eOA4AQvr4/X5fXbWnagyv2i/fu0urJkh7BfjkXvlRzIYradQkpnkmwm9H3XeiDXkaSTkyx6cJP1pNXGksKprQ2a4MrNZ8Fl1bto2lQ67C/8YBvzcv2M9isvv6u77Q8FuN7GdXJNR0eI8X4TvdqiL9N8sfWwRK8FXqf+va8Q6/NUulz5nyPv+LoYy9OuWJNo5evFpjTm8vg30hmLkMsQVLA1EQhaB9PLqrVLxw8VmhoaJGiryOwKmkwcViNJU+DtvmsF9z4ArT/uoKTvGL9er5NfPkogKT10rBZFUS9mh8OhtysEDXRlWcLV1RU8evQI/st/+S/wi1/8Av77f//vwfHCObtJqPI6HA7hu9/9bu3kOj09hQ8++AA+/vhj+PGPf1znGY/HtYHxvoBkJKXQlP+potuV7KUKQMoofSjKJZcpmmEa47TFFncW0Tz4lSsatfv9PgyHw3q83NzcROmTyqNxnMYcpwuvU6xfuHMthTdGg7TwpnJDwpfqpxygyica/OmiltOPaS3zm9T++Ex3T0p4eH/TPrE697jxh/Mjxa3xUAxoW8Ro4gsFbZFCHYV4ZDd1ekltlFpY7Atwznn16hX0+3148803YbVa1UfySx8O7Qp4/WM7h/EZZT/Kr6Io6i/xqROO81NX9FK8kiGNn+jB+VXjO/yX+JvvtKX/o9HIkwfL5bLGQ9t3vV7DT37yE+j1erBarWC73da6F8XfFLS2bqPzcnlgGS84NvGDOlq30WgEjx8/rndtHB0d1ccktqk/HgNN5QfqkFy/1CA2h2Mb4kcTAPJYuTsoYLMpYb3ewmaD9FV1rXbCuh121X2ZVa7qn++MxXHk0m639Khf3KlX4a7wlB4eBL25qQPYdxg6HBSX9RnEuCJwmvK0HJceRu+ADcM4zgpHUUCQJ4wDQh8tTzP2QgDIwi6O5+PxeXJZEuM+HfSFtg8w3tD6GEi4D+hYxTbDo4grx2rvNs+W8FEPytK1VVFUjtqiAKjukUVHrE8r1qmilx8hjDtY3Y7YSrZvoCy3sF6jI3ZDji7GI40xj3PcVuHhzlhXHs7LVb2RLkcffadpaHz4jG2E+H28IR75Pwxz+FIOWbf71+0CdvNvtQtW2ikMhEaf9/h4oDQdos2HziupNfsuabBc18TzaGHSmqtr2FVfxtYHCJb7FTnOlNNdghhetEXRay6KooAf//jH3ppotVrVea6urrzrjl43QNnM+4bbCTAt/U+B5KRKpe9iDHRttztEGfhNBc4jOWuJHF6k5VFosnZpKzckm0aTcXJQzti2RsUmeJpMGjxfzlcC3PGaYwzQQOr4WL26Fl6p9o6Vl9v+XU0ATdJyw24sr0UBygWpnXMUKImurpVlbjhrgq/ryRqfNZos8iI2xmmdERc1PF9fX8OLFy8AAOqj/NbrNTx+/BgePXpUT0LUMZqjUNFFLgDAo0eP6l0bb7/9Nnz3u9+Fly9f1mEA4CmwVkX6LqGJPJcmSS1tKo1FHklOHhpucWB1sahpAlo5qfpziI176nDgDjY6L2J8zMHQdn6PLbyl+kjA64r8ZtUHJECDPz+SWTKgUzos+C0KIm33FM6YPNWA0i8ZOnL1oJRBQGobrU9zdL5Y3l3qnin50ISOnPK1sun8ulgsYDgc1vMNHuVPeTuX5hwdMzX+NEPSaDSCsnQnSiCufr8f3BGaAs53bedU6Yh2rm+Mx+PaCKmVb9FhOcRkARqLLi8v63R4QggtNxdy1lRWWW4Zb9J44m1Ojyem4fhhET1WLwUWGYsOcdp3i8Wizm/RXaV5nM/FNNxCc856I6ZnpvCs1yWsVtvbXYG+Y9W/B5Y7M3yHKKZ38ZgGQHJauSN86U7PEoA4Z91ux7LGUeWl//7uVCzXpUdcIJZFcVLawLvD1uGgtOvvIV1hGTxMu082pIe2cVgvKd6VUYWF/YEgsQsfRvpxxNr8oiT3cNapgfaLK68I2gW8nc0hr/h4HZ+69G5nJZD7Y91u5i1p1+re2Kq80BnrxkZsZ2x4bHGVDnfDSnmlna/+jlAQ7n519QOvjq4dXBuGeHhaOteFbcnx+HRI7SPLFPk5dObS8n18fv2kumtAq1KWJSyXW1gum304k7OG4vly8EvrFyveJgbtFF5Kj2WtQfVVa5vl6FfWNVaX0BXOtjotxUF1TH4V1dnZWZ1+uVzWug9AZceifalB2zXZLsC6/ovpslw3lZyzGm6p3SS82jovVgetLjy8i/ZuY295gO7BKlct4ZZ+tJRnWQ824RltbYzr4BwInLFNJ+kuILdcjdaU0abLyUgTBJKHfjqdwnQ6BQDwDBgUer0ejEajevcegnVxj+U3BctgiA2kXCOPhYY2ZeRA17yfi2uXdXtdwaoY0J0aND32edN+jx3bhgbo//bf/hv82Z/9WV0m3VGx3W5hMpnU8oDjjQHSjUbY+XwepHny5An84R/+YdBOn3zyCVxcXNR30d2nHbIaYNvG7tLlgLt+qFKfOwa5wRFpQJwAbme2xaB/n2UANVZjPdC5wY8HpYpM0/HXpq1iiwwrPdr831SWHx0deceX39zcRPWMFL1WOlB5HAwG9Y5MzIeykx5lh+1lcaKiPoTprV8vxhaKUt0lXuI8JtGtta3Gm9KCVuKltryZmz/XIJGj21mA4uv1ejCdTuu5xeKgSul9OXRwYwKOISqHqXN4OBzCH/zBH8B6vYa///u/r+cEdCxSB21KT8T+p0473tac/3CXI+LmH22uVisRB6Y9OjqCH/zgB7Ber+HVq1dwfn4OL168qE8dwB2rKfrpGKPHDRdF4e3Q5HnwpA23W6rdKUIxGq0GUSlMa0N6ckrMYKydcLFYLODVq1fwxhtv1E793CN9Jfr5HXeoU+LRxWVZquPKMg9hPJ+P+fjRjjC2AtJiOdGAxpclwPPnSxiP+zCZ9G/5HXVnmqd6x6ausvv3wqLjttoZiztkXZrwvYBer2R4cCdxIb77zjasoxQHDZ9BibO+Q0YYD3dxVR+BGEfzFYFjkr7HxoVuG0ixoV+GDW8EG+MpCQ/vj/C/LHFMc97wj9asZKjjw2oHdwll2avrVTnWcT7zx1WFh+o04fq4wgdAjxbmzlh3XPGG7Zh1RxXznbD+jlh6H60vX6oxWNbt4uhxTsnq3X+mMsGl9Z+lHbF+ncPy5DDbzlh+By6tI8XvnNYA/q5YRyMHLoNdvQDm8w385CfnMJ9vwKhOe7APG2mX9rSubWMW/W1fePe13s9ZQ+7KB0B5QlvPISwWi0D/AcizhcfK3xe0KVPqE25D4mli61argyjGk7Q+ljVlF8DbcN99KNGQCt9FWbuALspqIzMpDimszXgHuBt7qsUWw+kKnLF3weQcJOaQwtoscpvQ0FYhoIyFxg2AygDLy5rNZt4XQNIRb3Qyy6lXikaaNtcgohk8tLItBsFYfilP036ytmEuPSnQFCJrPqsR9i6EUhfQ1PgmxccmDetkz/uLj0PeL1dXV3B+fl4bf7nRejQaeccUW4D3KcqWoihgNpvVR7ksl0sYjUbw5MkT+Oijj+Dy8rI+NlIyzh46j3Aj5y7mq5ghl/efJmuQTu6Y1JTYrhebTcFiqAcI5wkOVhzYTtTIbeHFtoZh+kz7VsOdKo/Ha3OgFodz+2AwqD+u6PV69bHNGm2cbovepJWN7/iztrF1oaTh42Mip9+1PBaDv8Uw0pVeZYVU3WML7a7Kjcm/HDy4m5QaYiVc0hjkZXPcXQDlgaIo4OTkpHa6avIgh4ac8YPH22rH+krHhtOxClDN9QDVMW/X19dBn+bI9V6vB8fHx96R8rgesa4RYuWk4rgeJc0NtE4p2aKViQYqXF+ljhrXYLlcwtnZGZRltbN6Pp93duWB1DYctzYHp3RTDQedF7rUTSRZQMuktCLgMcXu/knwflUe6d/tei0D50xRpyvLyoHn6KvyFIWLq+gNd366+LqGTFcEhqdKQx14Pg875x+th0vjl0d3z9K2c3T68VgmDwvzubS4o9Yvn49HYGlce1XpaH0oba4vwnpzWmxrZYlVm0+P1FlGeVUu0+1AxjamO5K1MVQQHCVA/WGAuyO2yotHFUt3xFbOWmxX1zah3knHYOg45f98l6zT1dGZq//88eh+Zd2uLh2dY/22d+0CQtown5SG4uGyAmrHuCTzwzpoz5JsorKOvocyTKp/+EzzLxYbWK3sntgc+1YXek5szdN2PunKVpdLR85aNzdNU9Da2apLSzpcW3qt9jMJ0L7D7QJW2iTdIlXfVHwbyMGVu3aO4dDW5TS+6TiU+MyCK8ZnqfW6hZZdgsVuvOuydgFNyoqNyTb9ERuXWpquIYfXctafKTnW+JjirgUWhdgksk/QFoqpCc/y9Qkaf8bjMXznO98J8nz22Wf10aYI2+022P3WZT80FZQanhTzSV89AcTbjxqKuVFq18CV1C6NFFp5HPY18dxHwKNNYjuv+v2+eEyi1Nb86FQJL+alCiSNQ0Mf7l7B++ewH3H3G0LOrs6y9Hda4rGQP//5z2v8jx8/hsViAR999BF897vfhb/8y7+Ejz/+GBaLRV1ur9eDyWRS40sps3cBSAtXztGI2sXODU05pUqsZJzUxinudqL5+Vde2lH1XS9ec0BbrFjlHf3ogP7onYvYFjTMumMyZlBuArH6psrVDEMUYvyCbYGOq/F4XB8zvlwu4eLiwuMhiR4JbwwkWmI72Sx9ju0l8QjnbS5v+TvlGUk2p+jB9LEP2DCehknjLlU+zWsBi3MoVgbKv5x5QsKPtOTqdTH5iO2Hx6q+fPkSVqtV3Q+r1SrgBamOiBN/w+EQiqLwdnfmgKZP4ryMND558kSkUbpXldabg8ZHEuAc8e6778LJyQlcX197jj2Ndjo+BoMBzOdz+D//5//A48eP4Xvf+x5cXV2JbZBqP2zv8XgMf/InfwKPHz+Gr7/+Gr7++mv4+7//eyjLMrgLFfs21SaHBEgP7timumPswyAqk+hc/vLlS/iHf/gHGI1GMBqN4NmzZ/WpBk3Halcg4eLzOV9PafnbrLWo/JLK0aDqE4DNZgubTVHvUkNWRjS9ntsdW4VZdsZCHSblASjq45GpI6dKT++SxX/qpClv8dF/CNLLzzStlIbH5bznhMXCXVzVFlpc1fYI1bufzg8rlHQOiqhzE9NQnSKa1AhyfcI2cH3oHLMAtD/K0vFpr8dtGfQO2W3NtxQ/vyM2djyx3wb+Dk7JGUsdr9VOWCcX3U8+2tj906P1/fIA5Lti3TsNo7I3DKfpLWl4WoCyliPuP75Llv67D0T0nbE0TXVnrNspG/aPX2daB+QzzP9NAGkdZoXYelpbU2nprSDR29RO2HZ9G5tnY3XcpS1Vax8ahh8VarSlHCBN6aBl7AMsa9uYfaHJOp2mja3ltHCez2Krz6HPaps9tPXENx34eMpZp+/Lf2MBiQ5uM+GQs96PQWNn7KE0XlegLVglsAgsyRAo4ez3+zCZTAKhNhqNYDAYmBevfMGcs1CJxWl4LMyX4hHNeCu9S5OwNKnHaE5Bqr9zJq+cMlKDPUWLZixpo1RyiOEpivD4i31PltzYj0f7UYM7dTzEnAEIEp9JxthYXbmxi+6W1CaslNGb4qZhiLfX69XOHXr0Id4rd3R0FPQXGiPxCFJOSxcKcFvgxkN8j/VTEwcVra+2sNLaI7ZYsPbpoUJK5qfmJ24AxvDNZlMfy8/71IpfgtyFTgo/pz+HHi0tvUsTy0jJWou8kcYKjZfw8fQWOWQ1lvB2i/WNlC5GR4oPuZzVdqxJ9U3pHJw2Pg/tG1L0pmS51s5SHq0PMXy5XIptLfGoVoeydHe40rnMApJepcmA7XZbnyiBZaCzDWUTH/+WttDieZqTkxN46623YDqdwnw+h8vLy9rBGasblZfr9TrYWSvRSvNLp2Ogk/rNN9+EJ0+eQFEU4j20tHxanjYure2SAmkeiaXV3pGvsb6SPqbxKpXTg8EAFosFfP7557Vz9+Lioj7uvQtoMufQfDlxqfmH/ufWLyVHY/ILHRjVvZkA0n2xZUmdePLO2OqH5TgniPzvdozSuKJA/EWdBm6dcI4/HS30zlZS45o2hxPrju1M04S7V3nzu/J8PIhLuzcWcbkw10a0DlU6205ZmhcAHYxhfaV2dWFFkFarN4c0b/L+sAHWh+PA9nZ1dm1+m0LI4/I5erH+WwDAtSK23/Z2LkT5JR9PHM5HjkY3DvDZ3QUr3RnrO2OpAze2I5Y7QekO0RKAOGKxzjS9ozt0ytJw2sVaXh5O+5DjDvtA3sGr15PLI74T1r37/eL/S1CWFR+cnS3g+nrdqUN2V+vOtnibrN9pvtR6SsLRhd7eZI0k5aWQ05aSntO03LuEGE1t6G2Sdx82sNg6ImVf0XBJ64FcsKx5dg0a7YfIt99EoOsybX1msSc1KbcrXBxHKrxLO4/njL0rA1Ib2BXNlWIaGpJwYktNEngXY8yggmmn02mwY2o6ncJkMkkedyUxvfQltkUpadOOTfoBDV7cgCQ5y/YBFuPEoY8P3m67UBwkfsPdEgDh7kV+qXxToZbDY6vVClarFQwGg3o8YN7NZlM7HwF0ISvdP6q1qfaFGK077uyZTCYAEP8CsAmgYXE6ncJ6vYbr6+s6Dh1e0+kUnj59CsPh0Ms3HA4Dg+uh8jrdUY9818WEHhoM5DZIKeXU2c4d7zH+36WSv0tIyWrkKX6HHjpYAMDbIQsgt61mWMex3XX7SYb4rvQNHKvoCMD6r9drWC6XdXtJ5aYU3lw6aZvHHBI0rVRGyoiP/R1z0iAN2DYYxvtawi2lKcsyuA9b+uBEozkFtM58p21byFl84zu/39eSV4tvooMhX5elu3OU6syaXJXwo5xHHRrn4tgdMrkLKbqb56uvvqodaHROxLurKA18nNCx0KT/3333Xfjoo4/g5uYGrq6u4NmzZ8HaQesXPnfwE2fcDiV/HEkGFqzjaDSC9957D9577z04OTmJ7u7s2khlHXc55UrzOpfBlE8l2crbGvXD8XgMl5eX3klG9L74XYA0H3BaDx1iY5LHl2V1XPFm45yx7h+Cf5q/2nkIwJ23uEO2arpwR2zVpv6OVj+Mz33OIRv+gyGsKqPwHL0QpK/6G4I8/tj2na2OPVwe0upCPiwLwN8Fig5GWi4EuOQ4Gs9pADV9GF54eQDiTiyeluK2iyxN9rm2QJxhPkmXregpivJ2dyv+A6CDtbondnsrp0oShrtnfSesGy/SUddA+FvaHevvdpV2vVLnLHXgyjtiAapjjKEuA0C/Kxbpw7DqnTpc4+GxvHJ6ecesoxcITdqP75DlYX4Z9F3iCYlH+G+9LuFXv7qGy8u4XfGu4RDmH67zHCp03VYWXaDL9fIu/QZ8Lfe6ArYhtSPydTdv49xTgiy2rVReBMvpn69zfz2ADnRdwkHyZWk2oZSfDSDOl21lfxMbWps0njP2ECbQXOiCZm6kaFKm1PG8M9HIOhwOvSPSvvjiCzg6Oqq/Qqd40JkEALXTUtrKLz1bGZHXh7eFBU+TfpCMCbEyrQOzjaKRKjc1QHdhFJH6h7aTVmbbyVDDI9GzXC49XrXmTYVb02h15btpqCHVavTjbSwZQGM083S40wd3YzQxeiNOdFxwHPi+Xq/h8vKyNpBzxwRN3+/3YTwew3q99tJ1qbi3AYkGzWDKn624pX7GcE050PoPP8iJGY6tY1WT0VLcXUCMPhq2i3po+aQ5PWXE1nBY4zQ9QnKgoBzAeR13WCGg09C6mI7RI/GgxLfcAZyCXF1Banv8RxnF+0vDycvEtBg+GAygLEsPr8Zv0gIiNVdJODkNNFzDxfPHANPnzhNN0mPbIQ/m8AOAa//cMUfL6/V68OGHH8Lx8TF89dVXMJ/PveP1tfySIYO3Mz0ivixL+Pzzz6Hf78O7774Lq9UK5vN5wH9NDE+SLOBtgA5U6hi0ANYD6cL5HuWIpLvQZ9RBuPO+LEuYz+f/P3t/tmRLcpyHwl/mGmvaY++eATSIJghKkEwSeUTT1TkX/M+13uC8gd5C97rXQ/AFZDLRTDKjURIJCRSABkA0etjoPdWuXVVr1Zoz/otcnuHh6TFkrlxVtbvLy8pWZgweHnOEf+kRmM/n6Pf71Z22Wr5C40morpuse3x7rl2Ij73ETysLOTbID4fonY7TJl5tlF4U1xcvdQ2kPbeh2Nzahl+sD2l74F4vY3fH2msQyELQAqtU/oAFXjTLWDh+cEBYenetSFEBpPZ3KzHCxWGAACAr71fV4oUtLF1Z3Xh8ngLLj4wj5XTDknw8PJeJqtP14zJyP3o37N3NP28edXej+GUiTJ12Gy4M48vXJ9YfXnDarW/UAOmyHOnDADo6O8+LrXtefWigWcK6a8T6McVu+3b3tGUf4s/60cP2Xb9X1v66/YyDr4D7sSuFJRnJjftTeRE/33N9DVZ3RxIQi5oM7q/m5vOz7i5YKz8CCK3jTfXL6+w2UGxdfptJ7jG64LNLmC7puvU1qfnbNVxsTdmknPdV5035auHl/nI4HKLX6+Hg4KDa86xWq+oqk10oVNapvFP1BaF9fSjubRnvvsuUuo/je5cUHUiszzapey29XcbeNmP7Lm219THFbyP5CqqJMowoVWHIFUaLxQKLxaK6OzLPcywWC3z55Zd4/PgxHj16VEuDW7CRZZ38uoDuPJJ5iuWrSUfQ4re5C0mjLKsfc6vJoSm0tTBdD94yfd7JY2mlKjB9ipcuB6umFOLN657AO16PTSbXrhernJ/vLsQmSiautOVusXxQHAlw0PHJdE+r1vZTyoQrYWXe6H25XOL8/Nx594E7ZOE8n88daySi614UpdRRDFhvosSV7iljpm8eIAX2crmsydhkPANupg52Hbd8blYxaMOEyqXpYixFWd10kRYby5qO3QSAcMt4ai/z+dwBZIEw6OSTkbdFDSCQ8SgMt6JrQlp+tb4hxyruzvOpWfNpaWr55Za1WZbVTiCgcPLDIflM76HNvyZD07JLbe8SyEhdg0jS+pkWpm0f4UqEoiiqD3s0IEZ7prZAZfnHf/zHeP/99/F3f/d3eP36NabTaXDe9JWLdOdtrygKfPXVVzg6OsI//af/FNPpFF9++WXSeJJaTnKjytsPrevlKSMynpY+fyclzWw2U69D4LJSH6HrDDg/YwxmsxlmsxlGo5GzD/HVYdt2fx1zmm9tRutWyrPcW/G6kmMkvVO9DYfDasxZrVbqh28+uXwKrdgYr7lfF4UULzIMp6ZKjl6vh34/x3ptkOfF1iLWoChK4IkfW0z3u1KyxvD7FS1YZv9DlrEkv2YJS3JSHA64mW1aJS8+7rnFwXm6cSTQWqblBz9RA1I5eEv+cMKgBtq6IKpL+tHF1p3LzMvHsOdM+PGwRrjJ/irzC+HP55+arx6J8U0ht6zrvN2y4+XF20XpZo98JpCVLLuthay19Ob3wdb/y7QzIUu9rfH2ztcqxmh3x2pWrxKM5WEsbxeU5aArB2V539Of7fgm8+A+63FD7q5//Tfm5+NDR6m7ea/fK8vbk95ufWk1NIb7VtCu85tP35HKOzZfda2/0mgfe4oU68Yuqe26WdIu5d1Un9CUugBnZJzRaITxeIwHDx5gOBwCAKbTaQ2MbSKvrHuf3D7+sX2RptORa2h+WpyPQvvQ6+h3dxSnproCIr7P13juMubtGjbUlttSLP53CoxtSqFJvAvim3WuBMiyDI8fP8bh4SFevnyJ+XxehWnTSH3UthN1lTaRtATgGwAZJyavVDx1mb8UJWaXvFOVFl0qY3xlnDIg0ccCMkzoWMF9klxQaGBAUx6aIjVGNOFwUIAUoQQctKlDqRyUd+QOh0M8e/YMf/VXf1WFpeObp9NpdVSzMeWRkv1+H6PRqMafnq1Cqbv2FqKUhRiXqU2/jMVLWexqfaPf7+Po6Kg6/YDuIgzx4vWpyXBTStcQaeUXktPXfnyA5j7I13+7bEMaH+qby+USh4eH+P73v1+BsM+fP8erV6+qUzPkfYOh8mm7IQvFTSl7X90bYxwwTcoqlYgUjh/LSgt14hM6konCcgBXrqcAOCAX3antu9IhZb6Tc/a+1gdaHfHfLMuq/IbaRChPbcdQrR8RDwlGpfZnik918+TJE3zve9/Ds2fP0Ov18OrVq+qqAWoj8jQHH/nWz1lmrxb54osvnOPCuUz7UAwCwNnZWdX3F4tFDRRMkYPm88lkop6YkUqbzQbL5RLffPMNFosFRqMRXr582fpYNB9dJxgr0wTc48pDeeNjmnTnzwTsxviF5Mqy+hHYmuwp7vuimHK3ad4l8XZhTHmSVK+X482bKUajDONxfwtaGQfIKuNaEKvkAXBgtX6kMQc9bVh6LwqyWLRgGgdn6RfK8cSulS6UsHCe69avKc9w3i0AWPcv/aSVrI2TZVZW3XLWxzPFj/tzmZmLygsiT/U4gExHEo2dik8wXkq/qt+Fi9pR0nVAltK28htkmXtMsQ+AtW4uCOuOA/W1nG3XdUBW3gPrA2V1PwNA3iEL5dldn7jv9We335C7Ho73WRkGkDJAkQsBv/ovB5wJbPVZBtN7OUbVGxxvg+7YasuqKIBvvpnh/HyJxcJ/bdkdpdFt3Ev76G2S9btAbfUEKXz52mk0GmE4HNY+zgTKsZ7rzdvoXLok3wefRDH5UukOgL152uUjCr6H6rIuffrUNu0+Va/WVZsmugNjAxQDpXb9QkeCsbxyj4+PcXR0hMvLS9VKTfLaF8WUQD5FnO+d4nC+3LKXysW3oY/lNSZPV7QvRZOm0A2BF03BEBm/qbtUQHHyWYveFBgL+NtrG0CWSCq3Q8SV3NwChazjJHDRpL3wOiHLGh6/1+vh7OwMz58/r9zKI996lT8pfReLBYwxFRir9fsQkBZqo0RdjVOyLXYxFu8SVstnv9+vLJ8BC/744qWAPreVUuv5OhbSoQVYStuNtfOU9H1E/Xyz2aDf7+Ojjz7CcDjEaDTCbDbD8+fPq6Mu5cYslo9UitVVCAyIycDHL21BK+NKQJZbkGugY6hdcSCAKw55fNq8EuDFP16RsskxpQlQ3RbQ1NLUNgSaXL52G5urdlnPxsJrG/QmbZeA1vv37+Odd97Bo0ePMJ/PK2tOqlMCUX0Uaouc6Fjlly9fVjLztijJt9ZNHf94uMlkUq2DtfUv7zO+NQ2NL4vFomrnqeXNZaN7el+/fo31eo3hcIg3b94k8dHy5kvnJoiDpXy84ncC+0j2RTlvhI6Vj/H0jTW+9Jrw33eZU575GLzLulv2Ixq3p9M1Nptsa33GwVjX4hU1IJTAEXtnLIXnACiJXKabOf7kl2Wcv7SItWEIlOHrf+InAVlKj8fn7lnmPvP81ePUgdT6McP1Y4vhgMKcD5gMEjwENEtda/VZ93PLUNZ3PT+8Dl33ehwtLqfm3UfOpTbdOl8K697fi8raWQKy9XBQgFpZrhKMpWfLw5MT486D9C7/fWCsfS7ggq7yH4K3TY+Di7wuXNmkjoH3y3o4G7QOykp3VzYo8qb/yrrX05DgMAeY9V8tz1RmZ2cLvHrlnpqzK3Wxl7jO/WnT+SwVrEr1u668dq0rkbzbrgm6XE+03WP7wnatQ2m6l9o1PRmv1+thPB6r/hKMjV0vlFquu+rCNPfUvfNNjCdd0nWstW8D8T1O031e7JS/FN1Kahl3pRP27V263uPegbFoDsw0aQxaWO2o0LOzM/zud7+ruV9dXVXPg8EA7733HubzOV6/fh2U36fI08K1IV+80EDMw2hHv/LNd+grdB91MYi3VXrsi5pOrDGF7q5yyMGUW3C9++67ePjwIebzefWhwXq9xunpqaPsIguOm6SUvhGLGwJtCPDMsqyyGP7e976H4+Pjyp2OHCEF8NOnT2tHjqcAI5rSmCsa+/0+BoMBBoNB1fckcAvY+2X5wo4vQLVjRSiv/NiRmyC+EI31jybu3I+UrWRxJMuIymG9XiPPcxweHiLPcwwGAxweHmKz2eD8/NyxYuP1RBRa6FA9a/FumlLGfkm7bqx9c0hMNorLFaaSZ5dlTOmQrHmeYzQa4fj4GA8fPsQXX3wBoOyrw+GwaiNdWRk1jZOyKNYWrJy4GweGqFxpLtlsNhiNRvj0009RFAWurq5wfn6Os7Mz57hhDbAgOZbLpQNs86PzyYqSjhGlj1D4naCyL/K20bTs+K8cI2Ll2jRNOS9r6ypNtts2dgA279Tmx+MxDg4OKkvNe/fuVcfv0jhcHmHadz5o4vnT+jdPT1KWZc482YZSypfLOhgMcHZ25lwnAFhLbjl2yF9NTl874uUi76snIjD2s88+q9bim83GuQO9C7oJxYXsL1QPVNaho9NC87QcO7QwMbm0PU9ow8/7io+uq4ybKElCPDhpazpjgPW6QJ5n6PUIjLX3eWYZkOdZBdQC9p38XStae2Rsee8sYIFWbhkLAMQ3A1WVWz3GcStB3PLYZCjWrbZfg/mXaZVxoMbTn8Hepb8WxhdHurmgbJ30OOSXVWCvC/TqPKoUFV6xOBL07abd8yZpjJ6uLENjeFumdzdG6VbWEdW/3cfZdx/wav1s+vX+w59t23TXO/JdA2UBYyQ4S272XljteGKAu7l92vaT8HPcTwdV3fzW/f1u/Khh+87zQnnV8izv0LVhZLsJzQluWsTju05d7ym/i7RL+dyV7X6Ir/0GgwHG43G1Jj08PKzujSWK6Sakn9Tb8nVjk3VnSloapegL3nb6NveNJh8iaPvfVLwoRYYm4X174Cb6gKb0rQFj2yjAUimm5E6lkIJH4yuVvtoGerFY4PLy0guoEI3H40qJ0lRhSm4pFFICNKVUcEQqCzlod51gzz7any+f0i/GIyWOVuddKtBIBnnX2PHxMd59911cXV1VAORiscCbN2+c+rtJS9kY+RRCWriQn7b4OTk5wcOHD6sw/PgROlqQf4gg+WtjY0hervAlCyIO8migvTweNZQWuWlgcGp8KXMoXopCk8vE3VL7S6yvxBSkFIbKnMACAM64HctTqCy7GJ9i82xojpRK55ji38cvJA9PI3UO22WcC4FXKTybrFs4kDQcDp2NF4GPKfxjY1AsXAg8SM2Hxk8+h+Tgc/2jR49QFEV1d648alWTl9zpQ4l+v++ss+iZyo/KXfKQIJOvPWrp+9y19iPLIGW+sUr7+gcCvrWEto5sW89tqOnahoiHp7par9eVRSyfM30bRDnm8zILlS8v56YUm5d5vcv6y7IMi8XCsQzn7ZiHS01DhtHWEvSrjS1FUeDi4qIKRx+PEDDblm6TwkKrc01plLLmiu0ptbHENw60lcHnd1spdc50+y8ccMJUQEgm3gECqdz1lvsPYWVq3cvfLHPDlnxIlvoxxRRe/m45VjIBBNBZN+LF49blt2HAgFAun01Xu6dWyuaWjXUr88flj1nKlmFcK2Iel8rM5t2Ws+tWt5qVYSUP6ceBrnoTa6LrSQq1TUNfv7p8qP64tSzFtW1cy1u9bVke2tjPX6lfkGy2n+jWre49sPIIYgpT5qfkq82xccvYlGcwsDXk57q7bvLZlo8Gzmqy+sL509D4hcjNv+W/XhusVkX1QUlb6nofQ5SyHm67L+uKUvU6TfncNrrt8sUottdvohe56TbXhPgalHR1gNUZ0TU8tEeQ19LI5ybk2xukhg+F0XRxRE31OG97237bqe3emMfnlKojkpTajgD/hwZtxoom/UvjHyu7WwfG3lSHa7oISVGC+tybKI2vrq5qd2Fpx+vJ+898CoLbRCTjYDBw7qokP57vxWKBxaJ+RMt1tJeuF5VtFrxSjpugLMtwcHDg3H3I70H9oz/6I/zrf/2vq0XDixcvcH5+DgC4uLjAixcvUBQFlstlzQI0RiFFpgwjZd6FYn3VpwDleSM5x+Mxjo6OAFjlJsn36tWrJFlCpCmhfdTv9ysrTtkeORi8Xq8r8IIrZvnGnBaM/X5ftbjdF/kACg2QaMtf46MpruWCc71eYzab1T4goeOK28rW1RjQBZ+6krAbasqLWwPqSqlmiyhtrg4p3GP8id98PkeWZbh37x5OTk6qr10JdKSPJQiY6aIdh0CGWJxU3iG/0FjEQdE8z6uxESjnCx9R2VBd0TtZoM9ms9r6AUB1/CuNWdI6zhjjXJeQkreUTafPremGgIAw3t75mBzjEbt31xe36VpSaxex9bKc1+ke2Nlsht/97ne4uLjA5eUlLi8vqzmG5i5jjDOmavKGyp/PY0D4/iNZ3r78aulocvB2J9dDPC15dzKfd+U81WR9GaoXOiKN2oa803lXuun9CW8nKcdca8oIbe3XdB9J4X2WylyBIOu4yV5yn0TtkbdRwK+U4B8e+PiRHz92O88zLJf0UWcGgO7ZpDqqUkae0x2xHPQqw9CdsWQ1m+fWjXgYY61lLR/rrx0/y++YzXNZL25YcjOGrGfh+BtD7Ym7o4pLYCkEoJs5wKBNh95Lvja+jcPD8bCyjni+mvhJ/1C4UHgX9A1RxgDhGM900vqobYM8jB07eDgrEz1LYJUAW/tsedr6yDz1ZtOyzyFw0QVhAX7XqWY1W8YncJBbxvI06mlpe1QNhLR+elyeBmppljLV48p4dTfN2pXGNvsLGCesdmesa80q53wrm8yzrKuiMPjmmyt8882sGvf2STetY9qVmuxdbsO8eUd3JEnqVS8vL6v9Bq2DeNuV+9wY+fYg+yC+TwlRkz3jHV0vNfkAAthfvaXoUIhC183E4sbS3BfdOjD2JmkXhZNU4Eq+WnieHoFUkpbLZW2wDSnzY4oYn79UGMWUZ7H4Mk1NDiI6NpWsAcifW8ZyhVSTepIK7VCH7kLprbmHlJxN0m2iQG8ycDUhslahZ06j0Qj37t0DULbR6XSKzWaDhw8fwhiDFy9eOLJJZTiXvQ2o0hYY0hRq8l1uIkPhfWnM53NcXV1Vx9dy6xdeFqGjJlPzoZUdv09Rkz80hkm+sky0/t9WKekjnmZKe287kcbAVymLL63Q3WkaaXlroljfJ8XmlFDY1Lkkpb5S+njXG+3Q3Cb9Q0QAIH35Oh6PkWWlVRzN6fKYTF+/alJW8r3puJpKTdcdroLQjnv8dADafGrWeFxuCVLQWoG78w9FpJ+Pj5Zmkz4Z6++pcwetfzgQrZE2hsv51Te/cb5dbrJS5getTdPHQmdnZ9Wx0nRtB/WVLMuqevWNyRpvLe3UPrWvDZpvDU59gsaI0LxD64aU+qM0Ym1Dlk8ThedtJPrwpd/vVye40Piy6x5AAqZEbdqMr63ddLn68hdrSzF+khcRvx4CAJbL8phiUwEf9ijh8p/aq7UYdS1nLQhWplO/S7aUg4NlJZ+txIynqd4J9C1BTtfitsyn5V1391u/Wj6aFSvxoHRtGRJgaWXXgFPJT8az+Xb52/AcFOTxSl6oypfySuHsu+XPw9TDcXdekFnNX8aPUbMu5aatpyXLxb7b5zqY7/b5rCpDt1yhtg0pD5y7U6lf1ddd5OZ/50ArxdcsYetpgAGitp55H+NxZRjD3OHE1fLlhk2ziJU8ZXvhPOp8XV683DS5NdLSo9/VqsB8vsF0usFs1v40ireN2sxv1xVnX7SvNeUd7U7X3U6k3kjq+0PHFe9L1n3y1fZeb1N/uC06uq7Jp0vQ/Il21bn64od0iKG9fUyeJnrNJtQm7h0YuwPtqpTgDWk+n+Pp06e1MNzaVboTtVGUdEEpaWqdVx45TF/+vPPOO849WZyyrLxbazabOV/oyzDaoN5lmcSUcb70ulDiyc1PytcfqXz31W5GoxH+yT/5J/jmm2/w+eefV0qvfr9fWUMbU975F/taPkQ+hW8bipWH1jZlHA4eUP/85S9/ifF4jD//8z/HycmJE5+I7jocj8e1u0l3odVq5VgSaWnTu085RnnyHaMqeae0K3fzGh9PUsELSpuXn1zEtjn2PKTEJz/fmM3Lht+XqcnOw0tF3U0sVJsAp21l7TJfKaCtz4+PQ12MixqP8XiMjz76CG/evMFXX31VzYG++5y7SFP6p4AzXRJvxwSiEW02G7x+/boCSYjoo5WQpTAB3KvVqirHw8PDak6hvM5mM6dO6WMvPlZwnlxu/iufJaW0PaKUr4aNKcHY8XhcrX+0+c63TqSyI3nk/ZghPk37cmzsbjI/rNdrrFYr/OIXv3DcyaKarISbfvSiycXbpa/+ZBvRxmeNZLtvs+GU62I+f0mraN6uffzkr1Z+8iPQXYFKLqN8vi4iC+SPP/4Yjx49wtnZGWazGf7whz9gtVphOBzWZNM+lCN32SbkmoPihdY2beaZ1PDXoSiiNELHV1OZUFnyj21iRHd/l3cWA69fL3F83Md43ENpGWtASRMgmmXu3bCuLPaX7ozNMjhx3HtmOdhaAp8amGbvk7XgGv/l99huJQAYiFaWDwfX6s92roIT14bT340hubm/FsfnluKu+Ul/XxgtXChsLF6I5J7HD5a1S9uWf1nu5GYBQJsmB2B5O+P9tm4BW+832n7DylC+27nDGPmeAsq6Y5luCWuBSHKHAGSbPXP+rp+WFoWhfuzKJsPWLWIpX+RevyuWfl0rWm4Jy++b5eWv1Ynr7qZ1errAb397ic3m+ufJO/r2km9NkKqv+TYCTzG6qXzH9p4p+9BUkvuClDQo/K5reYkJaOvq20p8b6OV3XVjMtex3u+CusJEZH59V1qS3uCmyiemr7kDY3eg0KDlU5aFlFtaJYYqVio4UuL7OoAGynShSOdKiFAYzeJDU45KPrIc2iihUyg2QUllS0oZ++L64mvxfIrTJhRLt8ngxeOTYms0GlVH3nGeKUcryjg+WboeYJuUrVTKSqUd0Xq9ro4goaOaKYwPlGtal6E+pvFrU27aOKeNQTEFdWpaWvtMHf8kafXYZPzwySjToHsOXQVFvZ+n5M23cdJ4dEXaOOlT2sfGNR4/5C/dUgAMn+w+/r42I93bjqUaEQ+y+FytVpVF1nq9xmKxqI7QTU2TysRXTyl50MpCjhG7tjMNmKBfCWrQIlouplPrgMozJAPlT35Q4gOo5AYxVNayL3e1aQnJGVpfEUjNLf8kHx6XtycOKvHyStloNO3nXF5qC/KofA1wlbxSxwm5Bvd91KaNBb4+5ZuneHxetk0oy7KqHmN5DJV9qI54XWv+PjeZRipdt5KCiI+xvL2FyLeXo2etLWjjqE/Zxfuaj9oqEa6rjGV+tfHQl3+fm8+/KOjfWu1xS1ljsup4YRLJGP0fzLrQDcstRA04cGotbjmIYkFetxrdMbXkZ9PmIFrpXrrJZ5KTP9s0rR/xddOSd8e66ZZ8XB42fVRhXf6AtJS1fMDyoJVN3WLWV37yrlkZtsqR0nX8TUr2V1+4GsfEcO5R0bauSzdet1aeDFIuKSPPT11mbY7lz6Zyq6/39DWg7U/1ZwjL2DoP3r/cZ5JXD1OX2xeH50eGkXLZPq+5+ccJmYYWr87flpGvPlx344Sh/83GYL2+mY9vu6Zd1g43sU74NtMuOrSbqotd0u2i/7RZszeRQeqJQuFja+eQvkhbm2r7gqa6taa6a7l+Tomzqy5i39RmXXsdMrSl2L461g5j9UX7Ws1dEl3vtgtpeeD7wSZtfFfSeN2BsRFqOsho8bR3jZrcoSl5S2WKj3z+EjySd1SlUkxhFgpDfrGO27XSIkUx1KYdhBRbTYkrSrvi2TXJwff4+LgaQM/OzmrtS96rGqN+v+8oTn0W0iG5NOp6AuN1JBW9RVHg8vKyAg4obXkfcpu+x+MSpQIyMn7qJBvinUIpcVOsx2K8UzYgfOxIKfuQ7PP53Dl2nnhnWVaz/KM2owFCbeefLkkbU30LtJRNQpv200UfbbJB4nWSCvKEeAGoPki5vLzExcUFZrNZ9b9YLLBcLp2j+mPkKnlNza8NtQFBeXptNlX9fh+DwaBKf7lcVvMGjYMp9UCWkhRGA1L55tLXz2jclta4obWJ5BUCD33kq8eUspXjFa2XRqMRjDFV2QAW/A5tsOifrHDpw6qUPKS4SX+SdzAYoNfrVXN9lmVVeyBQVl7nod0Z3bTPNmm/1010rG5RFOq9rSQzrTVic5cPFLuutWWqgmcfRG1osVhgPp9X73ytJscaOV65gIWpjRG+fZg2fqTsV1KVpNddnqEyShknZTxfGtoYWBQGqxXVmz1GlZLNMm7l6oIc3GJW3hlLYYuiBMdK9/LZys4BHysrWdmW1q0GBLbRXbRkOUu/Ww5wgbjyueRl/Y2xwKQMb/14mWvvYG7y3eeW4l76ZQ74ygEyX/vlbQYIH+scaifXtReO9S++Vi/D225gy92WEQ+rgdHWStby8N3vK+O6ctj2Kq1VjeMuwcQ6CBkCYTX+evr8ncet85HudT+eThuLWPq1FrF1N2khy+PKu2J5Xurkjh1SLyb53NEd3dG3n2L77lTwVeob5Ql9sfj7oBB/nxWjjBOymr2j/dK+9LsSAwDKuh0Oh7X6XSwWmE6nreUgWTTdY2i/3NXpo0n4RicpfQsppfB8G+6u0osNnl2mvwv5gNYQAKLd/yotOJqCUVKhGpPVFz8VvOu6rKWypumkfB11z4Exmd5qtXIGTHlHH6eYrHwQpK9ijCnvEX7//fcxHo+xXC4rPrPZDK9evari8HsHjakfuScpta+ltB8ZXvaP+XyOLCutJ2lC0sBYX5qhNqIpB0P9MMZLKvm0/u1Tfqcqd1OApNS+4asfmnRJsU1uHKRIoSZ5kuUWU5iGxi+tjEJ12wXF5qQmc5aPv2zjmsI2lb8sY62MYiBUKL0m/VAj3s4WiwWePXuG8/NznJ+fV/2fQCni6VtAhsrIF0Zru/Ko2ib1p5WZTMP3LuMsl0s8f/68ukuXyoP6bazeKQ1tzNTGKeIbGgOpLih8CEjwtR8JJvjuvo3VJwFxNIZJwEjyyrIS5D44OKjWVSnW1zJvBwcHAFCBsjItjZrOlUQ0FnOLRZonOc/UsU8DvmK0y9ouJNOuG1pedxpfLneTOUobL/dFKf1l30Ttij6Io1MJyE+Tk5Ovn2rvPI/ymZM2B8ao6d6gawr1wVS3GH9OfB4wpgRiLy9XODoCer0SkC0BTwuc2ntkS8CSA7bkTkcHyztlwe6ZpWeSA8yi062G+pGzqI4zdsvMhkEVzubdTdPy1UqK+9m8+Pja9Pl7GYfKhctLeaSwVDfWjXjrFrHSj5e/KxfJS/mv55c3CVnu0t+GSW93u3cfWUHSGtm68/KktkJl6saXR1tD8AwBsi6gV/Yd6+4+u/tVcnPdNR4hoFd/5rLxuCS7O/7yMHp6bhwNfK3LH/7XQVeZpgbeuuUv26nuLtOczzd49WqBN2+WeuSGFNtL3iTdgSp3lEIxvYMM8zaQbz0o1+EpehBNdxTjy/dYPt2TDywN5UPbR8g9Ng/rA7z2pWe/o+6obd3wPT6tSUlnr51WFOrzvr1QytxCH6xL4ie1Sj4hK91QmjF5vpNgbBulQ0rj8DUMLXxK+qENb6piTKadmu8mCp2mRAMwVwyS0i2Unizf61zIyXpoMlFch+IppOxpQrG+QUoRoA6Wz+dzXFxcVO+DwcC5G9OnONZk4NaDpEym/48++giPHz/GfD6vZHj58iWeP39epcGtrXzpa3lr4hdqi3YjVwflrq6uHAXgaDTCfD5X46eQT3kolfyyT/sUiPI9JkvXbTs25mntvK7oQi3MZrNBlmXO5LtcLlUgPEVGjfjkHRqjffFDX2KlLNL3QU3my13H6NRFVIxHSFmdkp+mwEGMZ5Zl1RhkTHl/6e9//3tcXV3h9evXuLq6AmAXqUQ0J2oKe9+awlf+KfND2/k8Nb5vnFwsFvjyyy8xHo/x8OHD6n7XkFV86hqFiD4Ak3LKzSetT2geonknNl5qcsljd33AcigvVA70EQ9vH75xDyivCaDTKfhRv3LTTO9UPpzn8fExsizD6elpBZRqcaWb3PRT3nlY2W5ITh/xkzG0/Dado7R2u8tmivh0rTzgQLx053L5xrzYOmVXajKe8vK5jnWxJGo/Z2dnOD09rfonWWT78qGVIeXFp8ji5Cv3pu1Y82+zn+2C5JpSK4vUNX+sLchxZbHYYLHYAMhwcNCrQFWyEC2tZUvQi0BYe5SxBVPIjeKTFSsBYWVyEpSVwIsFXoknKqCMA7k2LzzO1pW5QfUnEBS1Y355OPkuAVotfMgNLF0XlNWBTtcvc4BXI9414BYsT9zdB0ZbAFP3l3x0Hv74aoqJ4ThDt/yNse2Mh7N3ENu2zMtP4+WkaOSzcZ55G+Ru7vic8u6PW3/mYXg8+azzqL8bhy8P0+6uWDcv9Xg2vn5nrGHHpssyd+vCdasDuMYA0+ka//iPlw3ao598656bmC/u6I6a0HWvDa9Thtg6yaejA5qt2X3rfw5+yfAa+NSWYvocn//d+HS7aZf6obbH34Fyf+87HZbaiUzXZ1Wd2m/7/T5OTk5q7qvVytvuV6uV9zosjVLl+U6CsUBYgdhkQ931xlhT+PnSIvdQfJ97Ci+NNGVmKF0fkVKR89G+/pd8pXKWK0RSZGg7uWpKx1A4TVGZogzfhbriG1IyL5fLKh9ysPrmm28cEHU8HuPg4ADf//73G8scqk+K0+/3q+fQ2fPGGAf810jzjy2YpEw+hRL/0ifPc1xeXtaOxFsulzUleyitJu4xRbQ2xnBevnFO49OmDWr9QlNWUj2m8ON1IQEQfpF7zGLaJ5+UswtqO37te+HaRGlOwBE9c3f+HAIIAPf4Zh/IIGXkfrG22NVYzNtYSD5KK89znJycoNfr4enTp1itVpjP5/jhD3+I733vexiPx8iyDH/7t3+LN2/eOPzayEpx5NG0vvHKl06b+Z3SkWOFjxeNwdPpFOv1uppLfOMC7+e+dZOMz/1D7do3/sl8hTbSlKc8z6tTHHjfaFqmvk21zAsf2+i49NgpFfRPG3SyRv3kk08wGAzw5s0bFEVRzbmpX0trz1qZDQYDPHjwAO+//z4eP36M999/v5Ltiy++wNnZGZ4/f47FYlE7zYDnu2nbTamD1HayL8WNMaaqD94WYx/uhPYrTfplCqWMT7dBwUL5llYB/D5erb+H2lbKGOQjvp/xzUmxvepNlKtv/JPrLi4nhdVO19HmJmmprK0FjDHYbAzynPiYbdol2FX+u6AsFWeWWaCFA5v2uGK6e5aDsoaF5YCXBU8pDL3TscRuXDfMlht7BnMDCwtP+Lpf5oCwvJzLcip/Y2Cf7p5lsu7cMrXp23j2ncJycKoOzFIcwDeHl/FizZ93w266SgoTXxlaf15ONg7vS7H0tTWmm5Ztp8bxt+2+DqjSr3TnvFw/l2csLfLj4dxnPz9djpAsbjw3js/Pgq6anxvPhvXWltF/tTri+SOKrWVD5FufpKxn7uiObopS1+VNeaXE2dc6PsSfrz19ujXAv5clCunqYrKk7ANSefv2tynxtf3W3bh0OyhFpxZzaxKedBjceIn3ldRjhEO6D5mu5gaUeAcBsZr+YzAYePfcKe1+r2BsSInRZZxdqWkjkuRTBGsbzZQBT/MLHVEm3XYZBFNk6WpglAM2t4z1KYlTOpPmLiedNnmgsvXFDS/I64viNjLs0jdiSsWUxToHr2SbfPXqlXPc5tHREU5OTvDhhx9626r2LNP31a0EYzXFEOeR0kZ87SZW95ynHNhJ+UdEVnCAPQaEW8Q1bR8hQCBUjrF0fBNYTBYZx9dHZfjQWJPStyQvX/7kMZicT2h8JqVtTA6fXG3Ix5Pz3WU82ZW0sm6Sb67M9vFvKkub9prKe1eeWZbh8PAQq9UKL1++rGT98MMP8W/+zb/BeDxGURT45S9/6YCxu9St1mZ8VnW+8mvblrXwPitRskwjC1B69x2t69vwcX/fmiY092jrDOnP8+YboygPeZ5XAKKca2LlKds0T0NuRCS/zWZTbWTowyktXcmz3+9Xd5N++OGHGI1G+NnPfobValX7aCkmv5x/aNyVPHq9Hu7du4c/+ZM/wQ9/+EP89Kc/rerhb/7mb/Dll1/i8vLSKcemisYUkjx97SA2znQx9vP2xdddvvaXSlpbStmX7EJt1hL7JG0Tn/KVtVSYUZgYae2Vl4m2XtllL5dCu7RRuVZKrVcKp93nra3PtXUFj1MUBptNgV7PHv1qTHlcsQVR6T5Y2kPaXwJKXcBVAjw+y1jfkcT2HezoYhtX3gfrWr3aeFYe1IBX95mKzuWFSp6sAjp5XHpHFYfKsPyV+wWbb+sm1w7c3abB+VlZNd6Wv8vD8q24VvnRwmQi3HWTTFT2Mc0/xU3nIfPI3207lu/G6+bGcYHIWNx6PPns8nef62nzcDJ96S5ldZ/9bvV4PtDWBWwtaFuX11dX9Tqx8Tcbe//1ruRbJ+xTp7cPiq2Tm+zNdomzD7rN5X5b6abKbJc241vHdJ0OxU+VSVvHSj2ob6+YmoYWt4k+3ifnHd0eojqKjdMUlr/73IBShzGbzZxwXAeR0hbc9Un7scOY0hjNd7qZ1PMD8ZPQOO0VjG3TaW5LR2s7IKbc65SSNtFtKY8QpSgU+S+Px+PyIwJ3SU+mfRNl6FPwNrkQuimwsAu17avcEnY2m2GxWODevXvV0btFUeDnP/85Li4unLys12ssl+WdKNog2fTu4AcPHuDP/uzP8OzZM3z11VeOn91E6WXZdpDW6icEKlF7l0cw8wFeOy+/iTwhGWLE8xJSnjaxTk2llPBNFpnyl+eHrJB7vR56vR4ODw8r0CK0+LzOTcB1jltafcb6TAr52qPWZ+ScdxMKZikT5+fzl2Fi6W42G0wmE/R6PTx69AiLxQKTyaQ6UhYo75NdLpdYrVY4Pj5GURS4urpS55SUuVeWSZZZi3DfGNa23kNjg6aIp/dYW5FtUbs31ddeyU0DL5vKnzpGSp4ff/wxsizD2dlZdRxvqdBLu69abpABW26yzOh9tVo5AGZITl5G/X7fSWM0GuFf/at/hfPzc/z+97+vgNoUmaVslG8ae4fDIYBy7fDgwQP823/7b/H+++/jo48+quK+8847+Prrr/GrX/0KL1++xHg8hjGmdrT/LpTSz1N4+NrvruMRXz/4+qwFfZrNu9e5Pr4N+5nQnO5TQskw1IZD45WPYsqtFOWU9hyjXZRqGvH2nmXuBxa+9qnlLUXZznlwt6IAlkuaP/IKVCUL2TKOPYKYAFP6JSCWjjSWYfNcs6ilPFs+dDSxBYGzrbWuBXzlr03P3l3LgV3Khy0bAz8gC7ggLxweGgDbzi3FXfOT/r4wZbhMAWL94XUe10sx2UJzrw90lfUW4G60dxcY9AGA9OyG8fv7whJvfzqun08OmZaM477XwVNsrdslD/olP3vnK33Awd9dN/tLPPR0ZV3Uy89XPgaz2Qa/+90lZrON0vbrFNuj2PzX9U4pcW8Lxea4t1nPfEdvD+2jzaTqD7l/aB3XBNT0hSE+dJqSDzglXT2P00QeHob+ffI10Zvf0f6oC0yiKAr1REJtniqKQj3SuAlxPYvW3w4ODmpxRqNRLexsNsNkMqm50/VJmmwanuXTrX5njymO0ds+WV+3/KmAbGoYqcSKKUlS05dK31SFqiajxqdrarvQvI5FtqwLssA5PDzEYDCo7np99uwZZrNZrX6ltQf3Dyl2NCX2aDTCe++9h+l0qvKT720VU00UZT6SCxgpRxsFrk8hn0K+/MTyqQE9TdJrUwdNlOY+gIcf2ZllWU2pel19OsY/1d83prWRyZd2TGEqeTVtF75wTce/FMBJ4+8bG9rwjNFqtUKWZZUVLFBahQ4GAxhjqvshi6KoxlEtXQ3E1uTx1UVT4EZSCAjk6frkkW6hco+NRT5QMzRfa8+pefCRr9/cv38fvV4Pi8WiOmo31OY0eemdj72h+Yjuum1iySrToLjvvvsu+v0+vvjiiyAvCdBoRGGyrPwwie5EHY/H+JM/+RM8evQIjx8/dubJw8ND9Pv96vhqWd83tf7ylX/IPUayPTZdb8TKYl97gxDfJmNqVxSbs+T6hfep2FgUCuPbU7TZt/BwTRV3Tfh1sV6M+bdZz0oZJY+iMFivCwwGdK0BV3bUjwY2FaADcEtVC5jwI4hdN5s0yVQ/triMVwKgxJfKupS9DA8GtNlxnOfMiDJwAWDKD49n08QWzJQWt6XMBHRKq1kZh/JbukHwpvguT562W49w4to8umVu03SqeeuuW89K8jXP/apE6kCcSD0aVw+bojfxvZtau0QUWLVh3OewP+ehpSXTcJ99abqy83Zs+zlEWM16th7O5t3NX32MkHzq/j7y1wuXo/xfrQosFhucnS2wWunr9ZBb073id5F2Wdff0beDZBu4yf6Qsk7y7a+1dWsq8f2ZT3/A936htaJPH89l9KUvw8bGqH3r5d4Guok2u8te1EeavoafjNqWr0a+/kEGOfTxOQ8nr9AE7MltGh95pUpMfi3/d2DsNVPTDXCs8d/EZNIWIATSNvKhC8PbKLhu60J0n/JcFyDLiY5knEwm1VG8JANZ5hD4BZSDLz+2UFN4ybtgjTHOnat5nmM4HFZlSVY7ZFk2HA7VgbVror7YtE7zPMdoNKreYxZM+6KQoq8t0MtBkhSltiaHpFTreU40idI/HYPKefjGnJSFSEj22zbmNCHennn/TQm/y6JRU5K34dE2/VSAVm5emi5GV6uV0+6yLMOjR49gjKksBgeDQQXcaSBN6tzXpH9LkscKd92m+UKW13voDm1ZHrx/83YY2yxSenyekXXaBtwiMJ0A0Y8//hjHx8c4PDzE2dkZPvvsM+R5XoHtqRaykvjxw76+l5oHCsfvRHn16lX10RSddJHSH+ugSZnHR48e4fj4GGdnZ9U9usRvOBziyZMnODw8rHjkeY7Hjx/DGFPdyUJl1uv1vF+8XifFgNem/SWlvcr+ITeS3D3G99tKMSAWsGVFYTebTVW2vjYlx6vQ2KrtPXj/5H1Wjq/a2JWi+ArFkTJ32Sa0PHE/Psa4AGWdj9wLaLJmWYbFwmC1WqMEBDNkGR1ZbEEZ6g5kvVryLMHELENllVrGp2eAQNPy/liyluX55BaxNp3yvtpMvFM61iLX/lo/CKCX/FGzluVhSFbA5q9uJUv+JQ95d6yNY8FXWea8nXPA1nXnZOXT/SVfy9MX1o3jhvdRHRDuitKnndSAvA2kpSP96qBoGCj1g7LuWsi6heLrPEL8jQqSSj8Oguph7HPIre4nLWL50cOuZaz1I3deHlpdQAGkZZ5WqwKffXaB6XSF9br9OiZlDfRdtTAbjUaVnoV0APJDxTv6btHbvh7edc/DT6QCXB2Y5C3XuZq+U9ujc/KBsHd9MJ1uc5v11W8qaXuppjxSaTgc4t1338VgMHDcp9NppR+W+VmtVjU8oku6A2M91IXScZf42mCo8YwNgJpMPgVtCu0TiNWoSZyUdFIUJ234NokTGrRS00iR6aaAelJCE0mFcUqbpXtUNf6z2awaFPM8x3g8rniS1RnxDU30qe0lBkDxX/kseWnpNVWch+TwpamF1xRenFcTwCaF2ijxtXixNq+Vs1x0SqWgryx8bk3KxufvU0T6ZNgnaWWVMgc2yVtTebrg0UWY1P4ckllrwxyEo6Nb+v1+deyJJoesm6ZpxupLa+ehdurjn9p+m8gf8uMgAx8DQvmVY4UcE7WxwcdLyuUqMQ3G4zGOjo5wdHRUHTltjIl+KBQCWUIky9W3btTKkc/dk8kEQDnPXl1dRTfOoTSLosDBwQEePHiAyWSC1WpVpU9fqA4Gg+qkAvofDAbVR1XEl89RXYyVu65rZVtJBdGapmMBGn//66pM9kldz3O7zBcSJOTly48C18YGSpvc+LtMg8upjbOyb6XsTULE5fLJu0uZ7RpGzmcahdZ+lKf12lR3LZbzKvVFwN4PC/bOQaNM+QUs+GPDl+lbPx4ny2wYyjb5ATxM3UKWg6AcgMwECAvHWtaXFrlbK1nOzxg4cth3G8cXzuVh5XLz7lrLSlnrfcWVz3UzzK2elyqU0nTcpifbUD18d+Rv82nphgNpPKQbLz/u57Zdvkax77pf/Jhd/i55Srlsf9ItZ6UsgAtgyjCuTK5sFF+Wj40rLWR9Yepgbpzqeed+xK8oDK6u1phO7dUhbcbl2Nr9Nivy21LqHEnry1i4t5W62C/fJLXZA3bB/ybJt07nc6RvzcnDNSUff21vF9s/8zDa3vum9mZ3dHuoSRtIwQa6alNkEcv1Hev1Wr2uDrB64y7S1vpGEhh72wf6fciXyi+l8fgopDD0WUvsen42TzukNAhR6I7BXUlTYl2XwqZtWinKmBi9DQq0VJLtg9+RSiCEL478OosUtrzdf/PNN45lLLcsXa1WNUtTDga3LeNQXLkoCfEgmTh1scCSFGvrbYATSZqSWAsTUrS1VYLLeya1MpTpcgu/0OLfp+j2yUJtUx49ehPU9fhFbiHQyBcvNDeGgIXrXmu0Bbx4HOLhu8dTS3OxWFRWh1dXVzg9PcUHH3yA8XhctVWyHtTKn29+YunRmJPyZbxPAa7V5y5jiCaTBhzwOUM7DqauiPPLRWVJvLjVMV/DSCUPxYvNIXJMGo1GODw8rO5IPTo6cgDOfr8PY0xt3ksZu/lGQlO6tdkEkSUvzdm//vWvnTLZ9cvQDz74AD/60Y+qawuWyyWyLMPDhw9xcnLiWN5SHuUxQHTvbJ7nSZa6t33/kkJanfryTWMFjUVdzElaGd6mcm0ihzbe8xM8YuAr8eBzo+TL40nQ0Vdu1Lfk8XChfWITknu01LkgZX5u0xZSy4Wn4Ut/szFYLjfI8wz9frHtAxwcrNNmU1qz0m+W8f/ScpUv1SnZ0g8A6K5ZU/mXd8zae2TLcSpjYXULWbKmlQCODQeAWcJSWBsGLC6VD3+vW8DWw3M3605pum6o8k0yuGTTq6cheephJD8/ae1RArLXRS4Y2YpDJK7mz92MAoqWzzpQqr+HLGHlu+6n8fXF1Xnpfr4w+rPfMla/MzbdItZX1v7f+hHHRaHx7G5e3UU/uQ8K5S0139q6EEC1LpRznNyP0bpzs9k4p6rJPUBb+W6CbqtcdxSmEMAq26G2XpL+oXQkz5AeUvYZ+d5Wj6DJ+F211vdRE73ObSJt3Ex1A9rv4WJ85TxBYV+/fu3VWadQSO8f05Vo/TcJjL0Nm/BQml3IsguPkBI7hVIaZ4g3H9C7UL6kLog4paapKQ1Dz235hhZUKeFSqU38LhfE19kP2/Z7GS9Uv752xd352fJZ5gJzTY7SbDsppAAtqXFDYZso4WTY1Dhd5JUvHrR4TSY3n0KzqXwpCnrfxis0Rmj5IuV3274YWmC34dWk/XGKKdx9IA8PYxUv12PZuivtkgbfoPiOS9U2V1zJTm6z2Qxv3rzBxcUFsqz8am80GjkfZfnKX8okw4Ti+caj2IYgtGn0pS/TDfUt4uHrE7FFdKrSp+kagNddbFzieby4uMBms8HFxYVzhL92VFqTfhNaQ6Vu2EPpkYILsJuatn2G2vVgMEC/3682K1QO8shu/tvr9fCTn/wExhi8evUKy+Wyxt83X3a5RuIf9PA0fbSv9VlKHXS5oU9pN2+T8iCl7/L27pvjeBweVvLS+Evi/YHLGBpfeZjU+YHCNplPQu6hOY+Hkx9ayDixsVjOSTzsem2wXBoMBmYLiJZgYVEQKGrBDzs28l8OFNExxxZ4LNOyIGcZloBYaSFKblzOrHInHn43KjsrD/m7dZbVwpQySD7W4pWnY2V274SVRPnivLmbrRs4clDZWRkBeCxmbZ7ILXPCuGUi3Y3jtw1Zixui0LDV5TK0Ca9QWJl/GZa/2/HKfbduYaCV3ONh7XssvhvHhuXu1q8OYvLw1D/dX5+b62d5a9avdV71MnbL1kfuuGb5TyZrzGYbZ1zS9vC77lOuk9rs1zW/0HqCr69lfC0OffRKfLVrscg9Zc5rSjIvb9NaaV8U00XcFF133aSsw6T/LuWTwkeucVPrSlsbh6iNPvTbTk10uNdBKfsD37o8Fj8l3L7qnc8JRL69mU+OJvrzkD4LeIuOKd53w+xqsmxamSFlo/zqq0viisKUATeFn4+kEjqVV5NOLL980JSRUul5HV/k8DyHJp62yuHrpNT2oU3Osj2Tn8yrvLvPd+coxaO7F7mMXJHWNA83PQHeVN23bXd8gkndGDUZA0Lxdlk0EwAg02u6iOTtWtvk3XbyzXtdKAK0vqvNN20WOSG6zs0UjVd0LzG1qaYL0tPTU/z2t7/FwcFBda/mkydPcHZ21uncz+tX9lG5kZLKbwlE+fjH8qqBhr42wsNxeTWZtfvHU0CXVPAupCziYyCVFckyn8/xy1/+Er1eD/P5HIvFomor8/k8KmdIjtgc13ZtR3VM97QSH9/d2hrx8qHyIN79fh/D4RDz+Ryr1QpXV1eYTqeOrFzmw8ND/Lt/9+/w29/+Fv/+3/97nJ6e1tLaJ1G90hHiVIfXvV6TgF/b+fW7Slqfl/VIv8PhsGqjBP7zuNwSp9frYTgc1tYAmrUrjQ+yHuko7sViEQT7eTy51k5pD9QXNfC4CaWsmUgmLT/aGN5kj8HLbjbbYD4vMBhkyLIe8ryABUstoJjn9j5ZAlJ5mLJ+rIwE7AIZ8ty1YC1FJStYezesawlbt5Dd5n777AdpSxkt4MvLKGMAJ5dZs3q1ebX+JR/+DpZvLguEm+vOQV6Xh41Tutfj1vly8rWp1HmsWZve95CZyj8UTvOzbnVQkJ7dMaIMWw/XxBK2/m77sJtm6WfD6/5+Xq6fDrQC1EfDlrEUprRytX7cIpbClb/Wvyhk/kJlHs9Puf4z+OKLKV6/XlThb1r/8DaR1PHwtSYRfcx6dXVVW7vy+YfrkPhatStK3WNcB+2qN/m2t9HbmD9tvQek71XbpCfXarJf3cZyuqPrpy7bga+dd00SN5DpS+JXJO1C6/Xaqz9rBcZ+FzphE0A1FKepUlbjp4GNkmcqmNCF4lvbUKem1zSttl8pxBQTUomWAib70m0jx3eNfAp28qN3n3KpiZKGA/EyXd6PQgpNqWTn7rtMFClARQr/WFnG0qCwIeXYLuOCVnepAIl8p3jcKiu17mJpxMbnpvWtgTNNKTS2pfBrAqS2kY/3L54OKZpDZSpBK1+YFPl84dqOt03mMsonbUq0til5S3eK2+/3cXl5ia+++grGGBwdHeHly5eYzWaVYl8r35R6jq1JfP1Hy4sEFkLpxtKPAZv8N2W80PIUao8yDFHqBzwp4y3/SGMymSDLstqdJHy+0vhrY2cqiMLjaHng4LrGi6fRds6T9XJ2doavv/4al5eXWC6X1VHNi8UC8/kcz549w6NHj/Do0SOnvdF9Ldr8vW+S5aB9+Ee/KfNJ2/Tl3CI/XKNfbXy+Dnpb1rm+cZNAQyq/PM8xGAwqcFTOMaPRqGqTdOc3J20NENrPEC/tyo6mlFL38vqOJus+uWYPrXXatMOUfarbJwCgBDg2m8I5EriMWwKrxpTATZ5nlR8BqdStyzD8HlgCUPh9shYwBbs7lrvBayGLyq/+a/04yOm6SStTbhnLLWlLvzJtOP6UNwtMxyxlqT7k/bg2vpXVjVPnZck9Rjpj4WU4Kgt/u4g1s30MT22G2Fgc3b8O/Pnf62F5O/a5tXvnc4991+LUn2VY3U/Gs+5GSSMMyLp8dcDW/trjiutlra2X9F965v8XFytMJmvM5xvWl9wPE1Pm8NDa1KadPq7fJkrZj2nls16vq3Ul8Xn8+DFOTk5wdnaG2WxWxadrYL5LtEs7eNvaENFt6QOhPfuuusUQ+fQrbXnw99Qy5R85pMoj187ftb56G0nbz6RgX6k821BM9+aLk6or1XSWsXhN9+B7tYyNKd1uiq5Lhi4UIhTfB8Zqedn3oEX5ilmW7iqDTyHZlveuk0mbNNuCa99G0izlfBsHn38qGWNUZVYsPR+vXWS5CeqqHEP8r6s82swhvjFDTqix/tnmC8AuANmuqM1c4ANmNABVA8FCPDWAKJSexj9kmdmUpAJEm0tlHvgzB2Ob1LMxplIajEYjnJ6e4tmzZ/jFL34BoAQ+syzDYDColQEp/1MAQZmHpmXH2zBZ/6a2qVB5SEBX8ozxl+2Rnuk9Btpp7Sq2Uea8ZduXYBj905f5b968CS7oY8TbaazeQ7Lz/kdtiyz8NJC9yy+gsyzD06dP8eLFi+oueLof2RiDy8tLfPbZZ/jkk09w79696p7d9XqN5XKJxWJRO574utZN8jhpumuMr8+7WO/7SJtL+B3GfL3T5fi4D7rJOdE3xsjxoiiKamy+urqqlX+e5zg+Pka/38d4PMbl5SVevXpVtVlfmtJNG9v5HfQ+0gB3TYkfmkPX6zV6vV5tjmlCqfGkDG32rHIek+tyY8rjivO8QL9P94JTGI0f8aJ7W+39rNYiluR1gd0yrgVniyLbppVVRyRrFrIIHlXs+tFxyZQWGCDqxrfPxpRp18OF3utuOh8eLtXd9Q8BsxrApfNivllaG+SAdJfk8kzZSzblGXKT+gz5bBx36+8HWDU3/Z2PYfZdi6P727jlP393nykOt04ld9v/+K8NK++F5b8EvMp3eV8sz4NezpzqOiaZf2MMnj+f4w9/KEFBPm6H9lc8bCrd9P5zH0TlIvdANIcsFovqOgyaS//4j/8YP/7xj/EP//APePHiBQBgtVrh6dOn2Gw2tf1bFzrUb2PZ35GfblN9c1l23Zek7Dc13ZAk+jiC/Pn6VgufwvOOro+a6reuU4bYaW37/gBFy6/UncT64V7BWC0Tt2GwCiHlN0mxLw6aDKptQKc2pCkVduET4pUCmmhgixa3vpHXlRqpcviU+DFFsOZ2G/rIPslXF/JdKtplGE1BKfmFJnR5zA2/n9EnNwdDZDrXUYehthfqH10vFGO8ZB22Jb7Qi/GU9SzHA6ozqXjVNnRNiYNV2n00Pjk1f+mWWoZN+KbylEpPbu0XSqdJGj7SykC2hS7IB8Smysbdmiinebvr9XqVIj7LsuoIFXn8cUyG2HyWqvDm4B/NraS0pzbO7+7W5EndpDXJS4h84ISWjm++DpVfSFYeVuvv2toklo4WjsoeQK29xCyWuVtRFNVRu5p19z7mL2Ms8KsBh7PZDF999RWOj4/xwx/+sDrGeblcYrlc4uzsDGdnZzuN002J1gTvvfceHj58iCdPnqDf7+N//s//iclkUinxrmvNxts4X4uQH7nfZroN8smxQhu7NpuNc6yUlJusbw4ODrDZbHB4eFjxDR1X79t7UFryKCs5JnOK7Rnl2EOyHRwc4OOPP8bl5SWeP39e3eGs3WEdKkMOCmtrA0mh+TvWLuQ6Tet3i0WBzSbDYFAAyJHn3HLTTZOsY+leWftb5XAbzlrOlmJbwJXkKo8wzirwt5SzDAtmIWsMD6MBsaW8fuA29gyPn+8dapgS5AyF87tza1cdBKVy42UE4WbDum6hsHWi5nEdc0VqErFw9XLX43C38lkPb9cO9fDkxtN03dJAWHrX43BrVemfaimrg5qWr10fWVl0INaCuhx0pbUVhJu/nGPlqc0vZRr1E7skcf0GENZZ3ob5FLj5eX21WqHX6+F73/se7t+/j08++aSS6ac//Sl+9KMfYbPZ4OTkBABwdXWF58+fAwCOjo4qPuv1GqvVyplfZPtKpesYd+4onWK6mF0oZf8WCt90X3mdsu3Cn/v71quh672a6gHuaH+Uoh/por5C7em6sK1d9IG++T3Uzt+aO2O/zeQDnfY9+IQUoykNcV8LwVi+m4AMKbz2nYdU4GrfMnVFXcoXU4ilxG0LxmZZeUQoLXT4HWBt8qFNSrv2sdB40LYOmpQzL2NfHlN5NI2nxQ+Bslw+7tfv90FKyJQ6aluuBGbsYpmUAlZ10fdifUcD8GJtPNTvQjJoFKqXfY6NKYtAn5IlhTeVJ38uiqKyqBoMBsjzHNPptHLn5S77o6/N+8pPAyG09Qcp17nimx8b2/QeW01+TlKu1DqW7ZPnMxaP/1I8X7pt1hWctPvS+bOvbfsW+QQC0p2u/P4TrRy1eY2sUwFUHwOE8tJFv6NjmoG6Nel8PsfXX3+Nd999tzo+jv5XqxXOzs4qC2Oepy7Wyb55lvrnkydP8KMf/Qh/+qd/itFohF/84hc4Pz93wNh9rtdl3yUgVh41LfNxnUDx20Jam5GAK60XeB/h/lSuHIw9OjqqrLfleCfrieqGfwCxWq2Q5zk2m03SWBhai3GwUh4Pt1qtMB6P8cd//Md4+vQpnj59isFggOFwiMVi4RxfnFKWmlwxxZovbpM5QPuobrEosF5nODw0yDKDXq/kVwIwFmSUIGyZHqpfYwA6lrisL3t0sQVd6scWl4AsyVT6WZCV/7p+/LdMn7vB81wHYd241s+CopwHavF1fmB8bTjy08LXefuAWS4jIK1mffzq7i5IW4W+Af1tOE0+t6THr7tp1qz8vZ4Or6sUN/edr5ni7343/9HDvnByrcb7ZP1ZuqX/uhax1k+Wuyzvejm5fm4a7tHHDmfPeBjTAfjWZ03m/etScHdFmnzr9Rr9fh8fffQRPvzwQ/zZn/1ZNUf85Cc/wSeffILT01MMBgMAwPn5Of7u7/4OxpRXw1BZTqdTLBYL5+SRO7qjrimkF0sJm0pyb6npL2JypOo3KGyKfk+OXSm6rzuytC+MJlTeWtvx6dh9bSA0h8UodS/UhnbRV2phYvmWdAfGfkfobfu6RFPO7trhuEKE872jZtR0kR9TsDRVbO9CbSYnjULgQlOQtWmaqTKlUJOwMaAqZTPZlJrWPQEVXC4CY5fLpaO0lgsInlbMyoPnix9HQZZUb8s46yOpLA4BH74+3bbeJShIxMt5n5vk1I0EpyagEFfGy6O2SBFOv/IDEV+5+pQymoKG0tXcNb6klCDAj7dvqfiXijOZb8mbu0teGsWU+b58c/fQfNRkvtLk5+78tA4tPg/bBHzmROA93YPli8PrcTAYYDQa4eOPP8ZqtcLp6SlmsxkuLy9rp0XscxyjMRkARqMRLi8v8V/+y39BlmX40z/900qOy8tLXFxc4D/9p/+EL7/8Em/evKnu8dTy2VQG+uWAnKy3Xq+H4XCIDz74ACcnJ7h//z7Oz88r4Cx2UsYuxOsuyzJsNptauryOR6NRBST6+A2HQxiz2wdobzOlKJtWqxUmk0l1jKEMP5/PYYzBaDRCv9/Hhx9+iBcvXmA+n1dp8PZF/2RRK8d1ssTlddkkD9xdKgWMMVWaRPwDjCzLascrh6ip0kHOSdrHAz6K7f3k/GGMwWpV9uVeTx7dSzwtKMJBWP7LQm/dSytNO2aXPK3lLAGyLi+qRnKn3zJfVNfWaraUKdvyd4FbykspAw8L5x0CYC3LBoIff9fceB1nrFwsIOXWCy9Prb5ku0gJEwqrx+N1GqfUgKlru0RunnDW3Shu/vhuf5ThXADR78b7kp+vL4wxljfn6/OvP3PgUsbTwVnuxn/tMcUhP/c4YhvWdzSxtqatnoS7LINSltevF3j+fI7JRJ+bQ+Oqz/27qsOifC+XS+dEB2PKk4ZOTk6q+a3f72O9XuPw8BAPHz6seOR5jvF4jO9///t48+YNvvzyy+pkCjp15ru2Nrqj20m7fGzRBd/YvjhEtNfwkbZ/kmm+jf3wpnGXLueGLvKQoheK0a55uq3z5R0Y+y2nmDIxVaGZujBsS21BulA8qWQKKT5Dbl3nMcYv9YuXNvFugmKTfUr5+sLE6lIDdni70X59ynUpxz7KO0WRpSlPeR5D/ToVJGmSN0pPUwJK+VLBlKZ9To4FPD0NUJQK7lD70pSNMh++MYfL0pa0ce46+rqcJ3xAVcrincfR7lKQPEO8YnnXwLIu6sCXPm9XTdKT44zWdiTwqcnQtK9qcoR4cBBIysRJO769TdnH2oY2jzbtw7H0fW07lY82Dobqr0kZ8XbCjyqOEY15vV4P4/EYH330UaXIMsbg/Pz82tcQdNR1v9/HYrHAP/7jP+LTTz+t7t/M8xwvX77E6ekpPvvsMzx9+hTz+bxmPcjH9SbE51N+/DP5SX7Hx8d48OABjo6OMB6PnftE+bgj5+ldypXqjeqOZNXGDSpLrTx4mF6vd+vvlpXUxThOlLIOIdCbyl6O8XSU4WKxwNHREY6Pj3F2dlbdNyvT4Wn3+33nWgz5ERflNQS4pu6ZfOtdOc6lzqsxN+7eZC2ukZRTm9tlusZk2GzKu2M3G/tRHQdD7bHEgDH2iGLDgJMscwEfOJaw9G4tXUtZLGhJooWmDJsGgasZcy/T4uCom28OwmYOPwgrWZ0fvcMJQ8cMl2MYyVmmYfnYtMrfutz2uW4xa2Xz+7tlZ5i7bmHLm1H6dG8QafYNeDWP6+8/MbcuLWNdfvZXhtXi+o8klnHK/zoPGz52LDF3D1vGWt66JayNb5QwWvnXy5P7yTRtWAv4rtcFptM1Xr6cSwbJFNqLXjdweFv0TfQRExGVyWg0cj4wWi6XyPO8+hCt3+8jz3MMBgM8fPgQs9ms9lFj2zw2Xat0uba5Tt77orb7ru8CaXv/pvvb0L4kta2EwmnyaPsNvk9K2c/7dGuh/HfV9nfR671t/S+kS2nLo6s+3AWfXfKxrzh3YOwdvRWU2rhTFExdAxtv20B70+RbHEqFkDZBp4J5KTKQFQmn9XpdKTx9liWaUn1XAELyI5KKdlqkaHdINuFLlFKG+wZ72lAoHalUJ+L368UUgDHieeJf5Gpt9ttAWr/jCmOfv4yfooTVgDAOmBA4cXh46NQBbcpl32hbB5oC3sePW6jF8sd5Eb8QINB04yTHIgIH+MaHnxIRU+yTH8kSU7RrcoU2VvyoTi1/3I9brEu/mDIqNtZRWfnyINPxzU2+OaENaXmUbZ6eQ+0EAIbDIXq9HqbTKYbDIT755BMURYHhcIgvvviiuj9Lpr9Pku2p1+vhV7/6Ff7jf/yPODk5weHhIX7+85/j+fPneP78eWVJ2yVRH3j8+DGMMbi4uKjkomNov/zyS7x48QL/8l/+S9y7dw+ffvophsMhfvnLX2KxWDjKQBqfugI7id9oNMJ4PMZkMnGAPN7eyNIjBJKt12tMJpMqj8T/ts9VXcinzUkhhQ4HwvlYTOVYFAVevXpVWcnOZrPqOgSfhflwOMS9e/ewWq2wXC6xWq2qjxI4f14/mlzaeBeqd94ei6Ko0gXKdS/dIZ1KvrS1NRYvDz52NwFiU6koDK6uNuj3c/R6BUoQMQPp5TnYYgyQZQXyvARwyzzkyPM6IJhl9p/esT0ymJ7tPbL8jln3nS9LS9C3BIPtrwWDiX8pJ1nSUtlKK1rAWrByQNVUcpf+vN4s0GvDu2CpdXMtiTk4a9Pn/QlCFpum+y79qdwyyKrnaUniZe3mLUzd6vldsLFpmq67zqv+rgOFxug8yL3uxtfnvneXpxZO8jdG8pGgZ9waVoaR/nW3ujWsdCdLWPvrKyutnCqXWj55mZDb+fkSv/vdBMtl+ppAG/tiY+Ftn8O7JJof+QkPdG2HpM8//xy/+MUv8Ktf/QovX77E8+fPcXV1heFwiKOjI/UD013Wb03rYZ/19l1qE7eVmoDLsTVYEz2ApkuJUcpeMkRyb5zCJ1Xndtvpbe9rt72Mm4LE+wZutXbedt4IgrH7UKJfJ/99phmq5JBSMhY/ZSDuqoGl5kF7T00DqJd51x2+KQAUy8suk2ETcKtpOXRV9xpp5XVd/TO1nvjmpMmgHArLFWb8iEbfYmIXZVXbsHxhFFMgpii5UsjX/1PLXVPUcdlTQauYjJKPBEA0MIN+tfIMpePrf1KB2oZkOdyGRV0ToIuTT0nsmw/a8CIesj67ptR8d1XvlCb5+QAC/huTg/ORCv4UwJGe+SJT9oc2Msl4Go8UhX1q2BTaZV3mc0ttP6G1ojY2UJ340vbNYXSk23g8BgAcHR1VHyX5QJ6m1LQuePg3b97gN7/5De7du4fDw0P84z/+I16+fFnJJ60OtXGfk29ukmPGwcEBjDG4vLyswtCa4OrqCtPpFJPJBIvFAgcHBzg+Pr5WELMElHpVmhoYy/PlGzvIGsQY+8FZ27p6W8g3Bsl+5etLBK5L/6IosFwuMZ/PcXV1Vd0xK8dZmQZZxsorFTSZtfbsG/dC8wX3K4oC0+m0+rjBGFOBzTHlQ+peTitbuabwrc9ClDI2rdflnbHl3bCoQE76N1sQpwRisq21bGn9aQEcF1i0wItrISstY+2vTYt48HeWIxDoWgcQS7esAjUzESf0XL7zPNTTt+GzjM/rHHS11q8Un8vDeVoevJwgwlC7tunaMgZzMywOb3NuCckmY/21vlBz4r7iPbQnC/FpFl6TVwvP3eprYvkcAhb9VrCamxs2bkErw3B/99mG1cJp/Mnd/vrcQv8+a1lfPcXbgYxnxxeD+XyDq6s1Li5WIoy7BpYUWre45VIfU3VZw+siHu465/c26fE5lj6eB8o8LZfL6iMnWiNNJhOcn5/jzZs3ePPmDZ49e4b5fF6lO5vNah/f77KfvyM/dTXHd0GxPijD7qoj3acOVyOfzLE48tmno0jl6xt7UtZ+XZVXkz180zJrK89t20e1bZ8+HV0sXEiOptSkb4b2SbvwT8mfL50gGBsTbtfGdBMN8bY1/ja064AeU/p3XUZt+d3WttU1b+0+qn2nmcK/C8V2ygDHFf+SYvdmpcgQ8pOLAQnGpo5xTRTvKUTH6yyXy4pnr9fDaDRylHmahWyKYo77+5T9KXQTintf+pIPfeFHmyxugedTkvq+akqZC1NAp1A+Uug6xgFffXLwQztqOKbE5f+cnxZWpknPXL7pdArA3q9KMuwKkHD5UjcuofR8Gw9jjHNUNuAf71L6WCiMbNe+o6KlmzHu8a2xTZlv7PGVKQctQiBDChHvrvrIbdosyTqgMry6uqrKrdfrVX1BU9jRkboExk6nU/T7fQwGg2q+6ff7GI/H1fHFnEcTalNulMezszOcnp5Wee31ejg+Pq7aCr/nlI9JbWSksuz1enjvvfdQFAVOT08rfyormkO+/vprDIdDTCaTCsiiO0D5UbMpFtZNymW9Xlf/BKbSHeiUJo0dy+WysnjkZZTnOY6Pj3FycoI///M/x3w+x29+8xtcXFzg9evXnW2Ub1O/0UiuEcjNtwYm936/3C6T8pfuhgXKuWg6nTp3zfmI90E5J/J0U07xSP0Km48Z/X4f0+kUf/M3fwNjTA2Yj1Go3/nmw16vV41PVEacl5YPKpcm87kE7RaLDTYbGjMzAHnlB5RWqptNCdxmWa6CKzQly18uEj1nWxCSW8jSv3y3//auWG49awy1P2sN61rActBTs5h1/UiuLLO8t7llPFGFpzIoy64EUYlv3QIXgkfpZ935nER8DfNzarHmZtsVasTljFF4iOtW4Upl0ESOurupufF3mUbdr87DtvF6PLtecPnLflEPpx9XTO88fP3dfZY8ubsbpvzQAqjfA2vD1y1jyzjGuTNWlp0sW63MtTKRaU6na/ziF+dYLt1rFSTx/acvXL/fr/Y3xpi9nBJCsuxKTdYAbdPjHxXS3FwUBX75y19iOp3i008/rda28kPSxWJRHUk8m83w8uVLZ17iYXeR8Y7S6LavGYHddaT72Jdqe402AFEq+finrkP5fjTG97ZR2vpi/+PebSNNnxMK810jrt8KkQPGtlF+tRGqS2rLk+LJ+D73rge4pjJrilPtPaYE0Hh2XYZdUZc8u5BRaw8+wCulfbRpX7dlAE8BX/gvUWqb67pNaXVCSjEiuWCXYX18eJ54n9DChOQL8ST5NOs0Tc5QGjIvqYvM1Dbpm4xDfaYNaW1MG8991mNAs/7U5SKjy/JIqZe2gIo2t6T2zdA8lSKTHB8pPH2QAJQbcq5E5vf7tZ3T5LtvQ57S13x9S5Mvtb5SFQQ+/1j/DPn55juNd5M217R9+tY8KXW+S5/3ySnrhLfXNuvklHh8E+xbc8h5hN9ROZlMMBgMasfsarLsg7Q5kmSk+zoBYDweq0dZx+q9qzUEP8r7m2++AQC8evUKFxcXlTWhPNI8JFcbonIhIFo7ApfKj47PlelTvNFohI8++gjz+Rynp6cwprwveNf1BVHqWHHTlLp25+s6Kj9jTHU0MYXxWSpLXvyIYH70uy+eT6a2ZUn8lsulo9xP3UfGZJRzH/lxy1ueXmw9nZofbe23Xpegnz3+1z2yt7ScpftiraVr+czdyB3Vf5kGB2csEApmIZtlbhxWilt5XQtT/mv5ls+orFgNwCx03bajWau6cbLMhuXy2PLkAKtrLUu8KJ9uWva4YyofK4PbPqR8tj45AJZVYXgcG5ZJ720r9eOOuyKbZLidhpqx61cH//zvRvXnMmnu7hrNdauHD4OsOj9+vDD1Yz0sT5f3NQiLWRnWylGPY91kvDofmReZD0n1vGt5tL+bjcFiscFqVQg+7pwdWy/ysZSfDNJ2vb0LcXnb7Ku7WpcRL3n3+mq1wmQywZdffol+v+98+PP69WtcXl5W6yig3EMSsN10L3FH3dBtKetd16xN9OGhPWysL8sxo21a0i2Wf9oH+cLGqInMIR67pJGyzo3t465rrL3t1EYPuCvPJvuULihU96n7E95vfOSAsfvO2D7477Ih1eKHlKg3RakNvu0g1XUZ3kbah4yhsmyrKCc/qbC4bdRmEA5NfKEv41MGshDxOqFnUvguFgtVXu05VYYmmw0Kl+e581Wm7w4T+poTKO9HGQwGwa9pYxS6r3EXSlEg7pJuk7hN7tfV+l0KSNWUuhyPdlkct02vaVq8TJtubrjFV5aVdzsOh0MApWUgASRAaYEzHA4dQKItdTH++uKH2pSmqOZAFS8TmYYc6/j4kmVZrVx89ytLvr6y8IF9oTzLMUACihwc0BbCsi2lrn2a0nXPvaH05NwlwxKYGhsLyH+5XOLzzz9Hv9/Her3G+fk5AKuY2mVOaUO8TvkJEFmWOXdBt1E+xMJQ37i8vHQsTfk4R/ft/vVf/3VlkZFlWWXx1+/3hdLXAqZdjCHGmMpamR9VzInknc1mVfq8zZD/eDzGv/gX/6Li9/nnn+PVq1fV0X5txunroK6VuD7iawBep6TYffjwIYwpAezNZuNYcPvGb14Xy+USb968ceqGfuXYxv+18di3fw2tX3m7JYB/F5Jp8rGK0lksFlitVhiNRpWF7L7Hl6IA5nODzaZAv5/BmBzufaP0kQWVJVnGlvfM0q8GspT5tJzoucy3a4lK/nQPrJ1q7V2x1vpV/vM7Xl0L2LhlLH8GuDUt+VM8+w4mswVC9XBw+HNg17YFes/Ys7WYpfLk8e17yd9X5mDHINf9ONWtS8PhPVxaNNd6HBPw87nJfZT2bIJ+mrsdo+pp1du8dNcATR4mBMjK+H4QFnAtV3lY+2zdQhay3BJWylAvX1951utPzzcdkW5QjgPuCU68vLOsfjJAaJ4jK1B+L/11rtV21ft1ua6g9Q6A6kSKfr+P09NT/NVf/VUtPJXXcDjc+dS1mFxN8tnl2uY6ed9RnXYFqOT+XtuL7bM++VgVS6eJbo3zb0sp+3xah2uyxcZJTc+xz3Hiu0Ah/f1txTdi5JN7l/wEjym+I0ttCrnpZLwPuo2KlRRZQmH23YG7/NrDtwhvCtZ9W6ir9qgBQpynnAAkeEHKoM1mE5wsfGlz0tpCLJ+yLfANDgEUtHHo9Xp4/PhxpYQlmk6nmM1mtcWbPKrSJ7+UUVPgpeQ3lk4obiqlAo6+NGXb8Pk1JV857TJGpY4/sXCpgESKQjeFR0gmX/lLsIDcuEKcrF1JaXF1dYXNZoODgwM8evQIP/rRjzCdTvHmzRtcXFxgOp1WHzfwL6CbUCiO5ic3MPwjB6445+WkjWGcl0+hHpsbpbIkyzIcHx/j4OCguteQyjBlLEglnl8JRMnxTeaVyy4BjVSAsalfLB/yue04EZunUspa9gnuTtZ6AKqjU33tCrDHrL548aICRiaTSVVn1w3ESqL5j48NWr5lHF87SV1zLBYL9XQMvl4AoB5lp23iY2N0rO7lupGXiZYmbx9ynKDnxWKBxWKBLMswGAyq+4Ll2kLKECNexrE8pYwxWp11tRZOWb/IsZva5Hq9rsBuTanu66c8LVlG1L7v37+Pfr+P1WqFzWbjWN6GiJenNg/L+YA+ROT5SlWWaON3iIbDIY6PjytL4Pl8HgVitX4j64Onr7U5t+9mWCxK4C7PS1Awy2jstIAJATmllSxq/1CtX8Esa31hSv7YWrbKd/vr5EDxq1vAumFDzyXIyu9pte1FAq7yuGJptUtlzXlRepo7pW3rjFeXjE9pcqLm4FZzHdStl6Mb34ndwRTn5yHbYzoPfT+pvRvV3z43AWHd8L6werg68Ep+9fA6SCv5kLuURfprv27YOgCrl5Verlo495mXj01jszE4PV1gOo3vP0JrNR6G1nr7mBP3TU3msbYk5zE+H5MMXL/C11Vczi7Kt2m8fdbj29JGOO2yD+2aUtaLTSl13+ejUNwuyk7qYmLh9kkp+lYK16bvdpWHt7GfcWqj62jSxkJ79l3Sua5y9+1BUikm5x0Ye0d3dA0kO29XR6feBKUOonLBsYtSWlMyhaw6CcChuFyBStY/pBTiR9mE0o9RKliQ8hUs3Q8HlMey/uQnP8HBwUFlEQgAv/rVr/Cb3/wGBwcH1deyxL/LRWysvlOV3ZJXkwUp5526GEslqfRuEk9b/OnKlPQ+c5uoqTxNFnSyvmTbIDe6k5Hay+npKYbDIT7++GP88Ic/xP/3//1/+PLLL/G///f/xv/4H/8D/+f//B/cu3cPw+EQV1dXO1v++PKn5UfmS1u4cSVBlvnvywulId1ibffdd9/F+++/jxcvXmAymeDq6gpFUWAwGDjpkaIiJV0pr++ezCb9IbUvtuknTTepTcG1tuSrPwnwaGGpbzx+/BhFUeDFixdB62QKb4zBb3/724pXr9er7o6MfczTNcly7XL+is1N3GJ0Mpk4/ZXLQBajDx48wGAwqEAl+tjDBzCF+jGtX0L1xeUhWbklZUqeeX4mk0l1vHKv18O9e/dwfHyM4XCYfGcoz4NMM6WP3CZFmwSgtfUq1RGBsXS0M2879MvvdNbqVkuPwnz44Yc4OTmpxudnz57VjpxOWSPzdzl+AHCAWC7XLhYAvrZ/dHSEDz/8EEDZXn/729/i6uoKg8Egad4KfSAQUhhS/owxWK0yFEVpmdrrZcgy4smPNbUfEG02uiVlnpegogVzrTtVM7mV8rgWsnQUMr2XchPIUqZp74x1wdssgxO+5GstYOkIYso7pes+W1CUeJC/C7jaMO67DUflV6Ynx1deeBkLY+vGrS5Zd4Brwczja2EdF2hE9dnVlNZk6PKF9bV96WzfTc3fDesHGt1xjf9qcdJA2bqbYX6STxsQtm75qj9rVrB1dx8gK/NeL9d6+cr8c7mKwmC5LPC7311iNrMfPMb0Ia4s9Y+W6AQO37h52/aO10WUbz7X9vt9HB4e1k4cow+dZrOZ16r4u1qOd3Q76KbXxXx8ienUNB1s0717m/xqcbgOtEtqonPZl46gLe1LnhQ92L7pJspZ0+dJf41SdNN3YOwtoZgi/44s3abBjsi/sWo30eySx5Q0d+HfJG4KiJZCPmVfCNDmmyA6woYfXUGKsNBxFikgRAr5+jcpcklJLsMD5Sbs/v37uHfvHk5OTir3p0+fAig3GEVROHdptpGJKxFluCZ110TZGmsfvoUQJ7nBjYFhGnUha1fkq8Om+fD1k11lD81Rsuw5pfZVH0g3GAzQ6/Wq44ppw/3kyRMsFgt8+OGHODo6AgDn6NDUvtwkv7Hw/F9TkkvePKwGUvvkl6AuT5cfaTwYDDAej3FwcFCNNwAca0jOU5uDulqj+MrCx0tTYEnQ3sdb5iO1n/N68cnZhI/P3xc2NMfJ/BhjqnqlNqAdmSqVVgS+8jZ6E2tNXxsI5VvjIeexWFgZZrFY1PotyUJKB5prCSTT+m/KZo36py8PPCwnzfojlE/tfbPZ4OzsDL1eD1dXV5jP59Ux0PKkEN88GpKBykuOF9oaLqTQ6HKdH1p/NVEO0XrNt0YPlZMWltPBwQFOTk6qvHNgN0S+eVbOKXQ043A4rNofXwOHPhDV+IeIrPUfPHiAjz76qErn66+/xvn5Ofr9PvI8rz5ykGOUr600XW8TmS1gsl4XW0DOHllMQCaBsPxoYWKTZUBRuGAoAbNFkW0B2ZCFLB1TrFvGlrwlWFWCtFsJnPDWDY57Kb90hydO6L2JW9w92wK59lmLq8WHiKPVf0qb3abU8fSWxk8H/3zxrZsfGORhuL/7bILutq3VefF2WA/rt4Sldx6e+7vPcv3rupXTY/hYYulmjzT2A7OhsqyXlV429XzQ3AC8eDHHZLL23hNLFNsnaXOV/ACI3JvykvH2QW32TUB7/RKfK4uiwGKxqH1kxNfIGo+Q2x3dERDfv6ZSk7VhajqxcL59sy+ctubiceWJg3KtL5+1fUVKPlLGEu1Uoybki9OE120bN25CniZtte16vm2clL15G/9YXMIgQrRXMNa30b3pBpsqQyjcTeahy4Fm13hdlUOq0rIt7320w1jnlJNQLD1tAmsCbDWhNvzaLpIpvZiyMVVhFpvANUCDviiVwCtXrqXkIySD5harP7K4oPtftQ1Jnud48OABHj16hHfffbfy+9WvfgXAWtGORqNk64ZYvrgcMeVpjEKLuRjflH5Kda0p9Xy8NUBD+jVRdPv822wwtfBty132g1j9tiFfHiVAohG5p1gokRJ5tVpVymUCY+neS/pYgcBYit+2/FPnJRmOb/h5+5S8tc2L5B0j+tAkyzLnCMqiKCq/wWCA0WiEw8PDSqFDC8SYklsbj7mcMdl5fjXrWf7Lw2h3bjbZ2GlzjkZt+lrKZrJpn00ZU2SfpjQkGEttP9QO6ZQFDsakynId1HX5xeLJY2GlspPAIwJkd5GVypz4+u6A5nJwP996kvvTnXKyrWw2G5yenqLf72MymVRWvhyQ045DlP2QyyD9JRgrgT7fmCPHLa08pAxNqMkGXStjY4xz0oL0l30odc2fZRkODw9xcnLifCiTuk+VcpM7r6d+v49+v1+dhkBtkI5eXq1WtTS1dukrd+7W6/UwHo/x6NEjfPzxx9U6++DgoJqP8jyvyjI2p/jSoXxId02+oijB2PKoYoMsK++EJfCVqo4AnSxzwVj6J0tY+qUweZ45ca0cNH4AqIBXw6xfuYwu8EsgLcSxxcbAA9Si4snztj9QNtVd+mn+ZZgscwGxENhaho3N78Sv6zmtDt55Q3rCWHejuPnD8TDus86nfHbj2t96HNvX3fj8vR7GMD8b3hdW89Pc7T2wnKcLvkrAlcLye2JlmbDSUcuM+8sy4/m0spRj0TffzPDmzZKFa76218ZYvm7nfJrqLrqkfehzd9kz8zJZLBY1d43/vnRsd3Q91KSdhNaS10FdpRNb66S6x+SRHy5IPQ0HmLhhiy+N1D055xHT3/H0U8JJCumkiW9svfttpn32jS70nDGenG/T9GK6sVSSJzRo8+Zewdjb2oBTZbgNst7R7gu+fbTDu4Xb/qht3cTaCSmBtDS0CdfHSwO0UuUDUB3RBthjI+moZLJQkQsEOlaZrBuA7o/luM4FadM6TpVNbrwkABGKpynid6G3YYxoO7amLGpCi1wOHnLl7Gg0wmq1qu6wlEp9DjS9efMGb968wdXVFfI8x8OHD6u7M7taRGnyk+wxILKpglkDZkPtV1OO+xaiX3zxBV68eFHdS/jee+9hsVjg4uKi+iBEA/lCzz5ZZb60eFxu+h0OhxU4ENtI+o6vjIFVKdQGSA3xItm4XKHNqhaXlxP3o/Iia/H79+9XH+fwjbHWzzlo75P1bRjDiGT5xPqFFp+IAElSRsiy2aVc8jzH8fExHj16hOl0iul0isVigfV6XbOG1CycZfr8owWSmwO8XEnR6/Uwn8/x13/918jzvLprezKZOCCj1ga0MV8CfsaY2saTHwdI/pI3haOPRgj05vMElyEGDN4UyXE8JB/VEbWxs7OzyqKH7vTmZUuUotzXxj8CYgeDQXWqBI0fp6enlfJao6btnsaV9XqN1WqF4XBYWcPysmk6ZjdRtPA+XK6dT/Dw4X0sFhOs13NkWQ5jgPIYXuO8l/E5L/tvi4DGFt4XCajl7YD3IYCmLgJeSzm5JWwdpIVj+coBW2zdCWAuw9p82Gfy25ba1j0GyvrdSBYXzLLHGJdlSs+877rx7Lv0r7gqbvX0fET1JY8+3pVSpwCzBSZT41t3HUDk7/X1WN3PdavHk272vZTBfdfCpVnC8rCcN4Xjzzy+5i9/Lfhad9fKgedPL7vqKZA3N61nz0oQdjarn7SlkVyraH6uvMb5aK7r64ja0G2aezndVrnu6I5uimLryDb85J4kpCvQSAvTtO9qHy1q+p9UXeAd3RzFdO5Nwu9zDtD2Gk3alSZbK23+bdsAvw20jy8AuqY2MsYAqZgC+rZT1209xqtrQOimKRWskLQLICuf+aan3++rVhr0m9oH2iw6KBxXSvX7fRwcHFThyEJF8l8ul1iv1+j1etXio80dXyn5aUuxsUALt690ZV3GQHqt7lMWeCF5tHRCcnPlbUpZhpTkkm9bmZukF5M3xJMsWcfjMYD6V+ASfMiyDNPptDpmEwAODw+rjy32TbH2RL8xQEgCbm0pVAfGlPfsbjYb/NEf/VF13PlwOKzAWG2slMpyrY3I/MVAVC0+8fB9XCLHCVmeTcaQkOK/i7poGjcku5wDqN1z/qSYW61W1VxCH+7Exj9+TGmWlVbSofuL37b1iGwnbdoLUWy+T50bqP4ODw/x/vvv4/T0tALTi6Jw5nSa40kJy/ukr27Jj7cV3n/IGvGzzz4DgAr05FYkvnxz+UkODpLyuBII9pUPxeHHax8eHmIymWCxWNSOdupy37RLe4jxjbUXDkhTGLqzeL1eY7FYqBafGn8tffrlQDaB5L1eD8PhEMfHx1V6l5eXlRy++arpOEvtdrVaVVcM8Pxq844vLzzfqfUm23+/P8TR0X2s10ssFrPKsrS0mrNWrUXh3gtLxwsTYEoAjbWKpf7gHlkMWMtUW2f6McWWL33QIMuDwvrc3OcyPXoG6IhgqKAr5yv9/W4uXypzAq/cY5fLZ14WPI4LjNn3TAnjkk2v5uPEBfT4XZHl7dtfpcQt42thpZs7vshwdfCR3OtuOlgp3YzR3Orvxuj+GojJ/bh7nV8aIMvj8OOK9fKz5aSXlVteofhFYbDZGLx5s8Tz53PEKLQPCLnJ9Ti5SZLrg7brnZte7zWVwbdPkPya7lG+bXSd+5vrolS5UtY0qfqbFP1SU11QU7rpuvCVe0o/1KjNuCVPFgJcK13JW+rbfOlzOTT9yLeNmrZhX9mllGkbWZr2paZ9qul+MKRvjsmo0VtzZ+y+JgFt4fK2drTbKHdMcdAk3NtWN7vKqikibpLelvLnyksAlaKZH1vKQRsCQIna5jG2WCOZ7t+/X6V/eHiIDz74oAr7+9//Hn/4wx+cut5sNnj58mVlufLw4UO8//77ar5T24gMdxNtq4v2FAMz+OYrFFZabFwH+fq0PKa1LXHF/T77rSzjNmAUgXF05ysneYQqWb9OJhO8evUKv/vd7yrLGwpHSuB91aUvn0VR4PDwEPfu3cN0Oq2OAA1Z3u8qo+94HuJP406/38eLFy9wenpaHYnOv7Ln4yW32OKy0rMGCml5aQKEyU0P7x/0LEEMjaSfD9TYB6WkQeXm2+TFxikC1Ai8y7IML168cKzA1ut1TWkn60yWtQybmp+2tI/1hBzvUusDgPORk1YGXY0l/X4fx8fH+Oijj9Dr9bDZbLBYLLBYLJx2n2UZRqORYy1OgBqBbFxOPg5IYFfmd7lcVuukoijUI935+EHlQyTbqywbisPHGJ/Cmcp9s9ng6OgIH330EZ4+fYrJZFJ9fKaNcddBsjx4//Qpc3jdEPmOXKexudfr4dmzZ1Xb48fNc95SNumn9Vsq36urKywWi+qjjePj4+pkFgK95VhAckvFVoyKosBsNsOLFy/w2Wef4fDwEMPhsAKcyepZUuq6NLVcyLIcAMbjMR4/fozLyzeYzdYA+iDLyhJUzJHnQHl8MT+eGQAKEGBKyVF3KH9pfiKZtF8CZ/nds9S+eH641Sz9c8vXUiayviV/VBakNix/t8+o4pHcPGz5a61RbRnp4UgeW45u/NLftZjlZUPVZdswKh5uHdg4gBvW9YNTnrIJ7TrlNJkG6mF1cNDvJvuDFqfO0z5rlq31OPZXumvh4scVy2f9vRsQ1hhUxxHzd3+Z2vynlpsvj8YYPH8+wzffzHB1pR9fv8u6QZvDtY+1u6bQuult0QNJehtl3gftUg63tQxvq1xNKLQP64La8JIydVHOsbElRhJ8TZHJt/dIBWP5c5M9/k1Rl+nfJPbAdVFN9iCpwG3T+D7y8aX9VYoONgmMlRV7E41sX4uDEIAi02gqlxZ/34NZTIaUgfA667eJous2tDsNvNfCdzkQaum0pS77yr4pRVnjI76Q0TY0pPSidLqaYFPGqX6/X93Xd3BwgAcPHlRhRqNRTR46um46neLs7AyDwUC94y+0eJMT6k1NrpL2IYcEcIhS229IsdmWtPmTgzI+kvK3Ka/UcV/KGZOhCdAWk4/4aEcZyjDcWmw6neLp06c4ODjAwcEB5vN5pQyX95ekLLwpnK++NLkpDCnRe70eDg4OsFqtHFAstW/G5NPk9cWTANxsNgNQKqa1uUqCD22AENn/ZFnGFOyyf2hyaXlPacdaXtv071hbD+VRAjop85zWHjmATu2eA4my3fnS0sqgzbjZlPbFVxu3tfqQeaR/KkP6+EnOrbE1XihflAadAjAajbx3vWdZ3VpZWrxSPmL5k/7cKtrXt3z8UtdKsh3KuDxNeqaxs+srGHwycjlCfTo0hmn5S2kbcpyczWaOG7cK5mMpzW9yfPTli55pzUhzFJ20wgFLKb8236cQzc9XV1c4PT3F1dUVBoNBdZy67x6u1L5L7lQeobmVeNGHDMYA63WBzaZAluXo9SjNEmy097Va4K68D5buaS2BSLKg1SxkKUwpJ1AHxUrwFMwyNss08MiIsKWbm2frn2VUXlR/7jvA2xwcvhSW/EieEliV8vG0wfx0i1k4FqN1q9myXOR6isWowrj8eBOqp4laGJdHN+TnpwN+sbj1daYWRwcbefm47c3l7brp4WVY7Z3H8flz/lI+XxjuXnfT/Or/GvGy08vBX3ayDDcbg9WqwGSyru6ITd1TcD5EoXW4L06TPVdX+9lQHrukm9D33dHtol32JU3XdRS2rZ6lDcXS6boPpOo2Qun76iSkfwzxiOlHJNEHmsQnZf8ckrsrui06VaCbdqP1NW3+Sdk7plBoz9RUT+Or/5R2Eev/XJaYTPKqNZ9sQCIYe9snxdsu33VRbKK6K6fdKaVTheLcFO2ieH6biRSOs9msUm7JL0uBtDrapez4goOsUoDSMpYAWAA4Pj527v3jtFwu8ezZM2w2G4xGI0wmE2dS4NZSlG8tD75F07etbfjABx91tQjX+IRk8aXbVP6QPLuSb2EmlbZNZOZj0mq1qpTCXMFK7hygJaV1v9/HV199hf/wH/5DteBZLBY4OjrCcrnEfD5vVZ+pYAMPT/LSvYf37993jugsigLn5+fYbDbVndG7KlLkApkU3NpdkVJeskyiMZEr/inscDhEr9fD1dWVej+VJgvnYYxxwEEJIPG4NC4CqFntavwlD86HL4AJFPDxiJHW1tuQBuA0bZvc2pG3BwJauLwybTnua0CYT+63lZooVqi90RGum82mAo40Hlr5paRFdTCfz3F6eor5fK7eeUQkvwKnMZFk9aVN6ch+S+7yK2PZnjjR2OBTumh9XwLZkrdPMbzZbDCfz6txlNxi1NVcmUJN+q1mEau9Z1lW5VfWF//VFAcyjm+spHKkdeLz58/R6/UwGAxwdXXl8KG21WacIj7GGJyfn+P8/Lxq41Svcq3gSyMl7ZCiiMBnAJhOp3j27BkmkylWqwKLRVEBqmU9kfUrUB4XXN4hWzb/AnmeIctylBabubCMLX/NFgjyW8haC9oss/9E0r3sM8brBtDxyW48/g644Us/3ie5FSs/WpmXr3HyolnL2vxZ/pyvfZZWs5SuW3dcBl617h6cu/vakOrM4oX922wJQnH8bd33bmr+blj9+GGZFj0a449jx5v6eygM5+3GcZ9lnBT3+q99lhaxnFe9LOX4oPv78gJYi9uiMDg7W+C3v73EauU/+SE2V8qwvrUGj8f3FanUxb72jm4vXef657tA+wZktTXLbavD0P6eiK/NY+XVVK/ii0N7NPI/OTmpXWcynU7x+vXraHopdJvq5DaQNp81wRZ84fh+lqclT/lqIp9037Uu5UfTfM5uwlvTp701xxTfZvINrL5wbehtBk+0DqY931EapUxYkt6WtsLJN+inxuGKLAJg+T+nmFIoVb5YGAJCCDSgY+pokKev98n6lSaiq6srrFaris9gMHDAWEpLy38XC8tQ/NvctlI3wik8tI01L1tfGfnGv5Q2l6KwjKXXJG4KyXbVtFw1gIsDB5KnBJE48LRarXB6elqFHQwG6Pf7FT+e3q6UyocABaA8ktR3d0lqewmFkQrz1LboU/LzsqdfH6DgA/g4+cbZ0JrJN1414a/JJdtUaj10tUnuYv3H27UvjEwrdTzYpT3eNDUZUymcL4y0BvflXxvXU+baoiiwWq1weXmJ2WwW3GySO98ESmtGX55CY0HTcSIUN6ZgbuJGY2cTJU9Mhhil8NfmpLa8OEmwWvJpunZLHeM2m031gSKd4JACJqSmy+PyK0M0hQvF9435qfWjxeHluFqtMJlMsFqtkWU5SoAwR9nVDYoi2/4S+Gm297/SkcAlCEPHA5cWtBwYcsNZENIAlaWscdz4u5XbukNYxEo3slp1/V13OhrZpl/KRbJb/jYeB3StbBzorVvL6vws0EUgrkbu3a/8SGMepl7XVLcyrJ6G7t52aZwazz9WhNyMGsZ9vx4QVotrjB6ndHff634uqMplJjfub5+lWzsgNlSmbn5luNJxvTa4ulpjMlljNvN/BNfVnjs0jqesTXxzzLeRQrrGbzN92+uVaN/5bLvn66qdST6avsmXnm8NJv270Ilp8mlrRcm/aXpaPfByyLL6FXS+66na1tF3pW+lUGw9vgsgqwG6KfuPLvreLjryNvtsLV93YGwH1JWy7rbQty0/N0mkPA1ZP6RSqqLxu1h/sTyT1RVZj9F/23s55WCa8kEGfb11cXGBPM8xGo1wfHys8n/w4AGKosCrV6+wXq/x61//2kkry7KawlgeXUwycjn4MYcpCusuKEWJSX5tgL0uN1xN+ElALBSmDaUq92V6+9iAykUFWTzGQB/JI6a4Xy6X2Gw2zjGVdOSwtNACyj51eHhYva9WKywWiyR5uiReLufn57i8vMTDhw9xcnKCyWTi3PlIYUN3A2ugIvdrCoBIvkTyXlgOvJL17Hg8BgBcXV15lTrc3dcGY+2Sg+epbZiXD79PlQP2/M4OWZ5am7xpBY4sY14u9FGRPOaT50+C6GQdprUzKg9ZPrJMCPy7qbs7U4nmNw5whhSWqXMOB2h3aS8ETp2fn+Pzzz+v6oascKXM9AHWaDSqTst4+PAhjo6OcHZ2hsViUVmhcutDqkdffTX5UMXXl3nZpa5NtTAkb57nuLq6wtOnTyvr2C7mz12J+DRp+3KM10iui+S93Tyctpbj7Zf/a+ODpM1mg4uLC2cdPBwOneOr2xJPlz4upNMt5vN5dTyyr//F6oyPRVJWWaY0D/T7fVxdXWEymaDf72M0GqHfL2VYrZbI8wJZVoKppYVsDmMKALljGQvkW+s4ayFrtiARH15JDBfUxDYNG74sfwIwzdb6lt8nC/av+VlLVu0OWRmmfIYTjp5L+VxLWXJzwwDy/lxyK9/d+JSu2YJiFKfuByc+L0cbPlPc5fpIbzfXNa2H0nH90gBCCaBKfypXn3vdzXV3f6V7HWCVcVy3OtBq4+sgq3X3gbBp98RK4uXiLze9jGQ+uUXsZLLC//k/b7Be19cX2vqyKfHxnsZxzkeOzbH9AOD/oPKO7ui7SqH1oebeFeiTSk31PrG4MaA0JkcMKLuusYWfdiLHxsVigRcvXtTGyG/DuHfdev19peWbr7r4MCB1/xlLq60OS5LvrluNzx0Y2xE1UR6GqMkkEFM2xSgl3j46ZIri5m0FE2PK6hilAlepPN5W0oDOEKUM7hJQ4gPudU3WXFnGN1xcJgKdtI0YyUp+/X6/WpzI/JJylixw5bHH+wIRfTxik2AX41jT8VUDHzQgLGXyjpFMp43iel+bhKbE86K1z5R4nGjRQuACP95W8pXtVnMPzTH7HB85gLZcLjGbzbBcLtVjO/m7TyaZn5DiRWvHsbqI+QOowEy6v9LXjrV8aHmV+fGl6+Ml88hBxqIocO/eveqI5aIo8Pr1ay8Y6ZOVU6wcU3j4+DYh31ylzZW+Oa7t+Bs61va2ES+D1HGbg5i8jWvzAlHbcdcYU1np0Tv/SIwTXyfwMtfyResGrsxNkcXHT6Yb45NCsbZLZZNimRyjLttmaC0QG7sBOPNZSK7QWiQ1TtP6Co0f3C2FUvpbyoepvnzH1j+p81/pngEo74I1prwD0hgCVek+WBfcJMtYzUKWABvAWsgCZKFKgI+1fi15kDzuL8sxULOINYofzxPJX78XluK6fiWvLONlycvZiHdbPq6lq+S1ffPwpaqy/UryqxM//tjPL8jCCduEmg73bng/KOgLL8Pwd7fudL+6m+vu/uogrBvOlj0Po/v7rGilpavmHj6euOyvxomvl2W8zPWy8+W77P9FYbBeG2w2+1vrWRn8a75UvQj9G1O/nuCO2lHKvPy20W1ay3ehB/XpCLQ0fGn74qak6aOu2k7K2mhX0mSUY1KTtb9PH9AVSX2qL0yKX2p57qvcU/cZXaSl0b7HgxSdfZcypOic+HNXaad+2LpXMDZ1wbBrnNtGoQ1gWwXcHfnpptpZUyVtU2UOxZNh3vb+cVuoibKRwnPyASRNFpIE2vAL6ek+BK6IHQ6H3vTIikTKNhwOMRgMcHBwgMlkUil/m8oYoq7a4k2BjFS+PqsUThqY1IR8wFtowcvj3maK1VVsAUxW3W/evHGAP27h6FMaS6DzJsZKko0rmqfTKabTqROOQB8uW0rb9wGJcgzTFP6+NkvtnvykkpziHRwcIM/zClTm45KUlacVmvN2IQ4gccur+XyO1WqFH/zgB/jggw9wfHyM2WyG//yf/zOurq6cu2RvklLm+5C/LG/+Tkd0U7/hltgpG03fXEZz1HA4TJozb2odL+9YTSEuJx0hbIxxyo6HiwFZMTLGYLFYVPd0AmW98dMAiEgW3o/49QUkM520AZTAuQzThGTf5mOE1jb4l+q+Y2g5b58blfdyubx1+8DUcox97KCNz1SuIWtlOS5re4/UtSyF4fds7VPRw9uSvP9dUizvobWTb33F/amP8Q8u1usS1MyyAr1eeS9sr1daxcp7ZEurUQOyiAXcO2OtXJSm/5cAXvolcLVsD9pvGa+0rK1bzpagMFmlWv/ynYOVfktZ68atW33WshYc3cas3Hj+7XHJAAF4xIfXqdkCaLz63HK0FrScnwzLy9lHMdDXEyseIhBE9wuDrtZNz6d9jh9ZTG5UznU3LWz9XfIxFVDqykFhjXH58Hf6gIHc3F/NzQVj6+Vk86aVRazMZH65RawxUEFYPh7vY9+Wckesb9yj9S6t22azWas1wU1SbC1w29YK3xa6LeW6K2jXVXvvojxia7QmacgPFn37nl0/BImlLcP69BkpOo3b0N6IfLJcR3u6DeWQqqu8SdL2UrdJd8r3zzHaKxjbVmF9G+mm5OIbzDab1y7S3id1oay9yXYWKyM5CWmgQmq9yji3faDskrpqJxLQJCVY02PZuuh36/Ua0+kUf/jDH6q4p6enuLy8BGCVnKSAbQv4AsBwOMTh4WEFplA8Xg6Sn69dpigL2xJXeKeWp6Yk91HKQjgUpqlMMfeUsYDL1ibtpuOLlIUW+poieVeSstHRuOQXkp38U8BNLa1dKVYO2pjV5UIx1HZCfTgkm8ZDHu2uffjB0+P1oC2WuyBtY0e8+/0+hsMhTk5OMBgMKrkHg4FaHj5wTZPZV56x+ZrHT5m7Q+OHD4jhCkHqr3ze8PUPOaZz/gT0PXnypAI6l8tlNUf55mX5Lo/Q3Rdp661YePlMR0GHwuxKWZZFwTDKi0yTrlwgOeW6Rt5525ZkW9XaD+9zAGprC194/iyvVtDCXidp6wnpps1RoT6r9dfRoxHGj8dAVp9vFUNEGAIZOFBCbAnPo3BGxDUGZmNQrAvMns9gNvUPduQYHxqnms6xGj85b3B3LQwP5xsvQ/LJMPQBWNn+Sms3Y7LtccQAUKC8W9aCjgTOWDDTSQFU8HTXLPmX5QpgezdtnhO/8tkCqdl2/HYBWjDLVgoLx9q1DGeMBXmtTKjxgLCURdAS1hfG55bqnuqvhUsJH6L9zEH+6cF4/aWbiQCK9jkOwHJ34luPE7aCpXcfH2Okf9zP/vr8XSCUfim8JF4muns9XCjPvA7KPUY5Prx4Mcd0unas4K3c/jbVZH/M593YeOfKXedLazZaE1yHTq9rStmrStpXHrV17m0pz67XSze1/mpKTco/tL5s086apJfKN6ajSuGr7cl3rU/Ob5c9URvdGPc3xjin/E2n02ovRde68Dht+2eKriulTH18uu5fXevm2vBM0WOE2rAMH9vHppZrqG11OX778ptShnfHFF8ztVWMN/VPGeBTZGkyUTQlrUO8LQsAPjE0Haw0JUjb9PdJXaTRxWK1y0mGf41FFlV8Yg9tqtqAL6Gwq9UKl5eX+Pzzzyv32WyG+XxevdNkRHdO+mSLWQMNh0MMh0NMJpOKPwdL5FGfKcrc1HzG4sYmxhQFYIosWp7kIpWnpS0spEKvSdvsYuxMTS/W71LGdU0pGuPZNo+UFl9My3bpW+hwwEmzfOyi3LsYf1LGljYgEO/HMr0YcUBEAgzyQxBS6oT6o/TjVlDUf7rqB/xXgrGj0QgPHjzAfD6v5B4MBiiKojbe3wSlAB1UVrLMtbZC71RndDQ9gakhi2DfWob6Vq/Xw4cffoh+v1/dd3p1dZV85F2WZdVcu1qtHLCzq3ldyh0DazjJfkYfhMTmmxSlqpSNiNcHyavNPVo+5vN5dS82ufE0QmNgk7mK9y0f8b4FuKd0hJQQ1Eapzd4m0uSWY2zKfsqXdyrb8btjPPrpI2T5FnzNgIzAJQXLckBWDiCY8t3AAIV1M8W2XRWl+2a1wXq6xvx0DrNx2wMHAPh/KH8a+ZSd3HJazunymg5fmXE5uZ9vTeabnygczQMExhaFQa+Xb48YzlDeC1vA3g9bgrDG8N864FOm45aBtGwlQJWDtiRullk3fr9sybcEb8mt/K3fFUthKV1ADwNmzcpKvApb+mUizxnLuxuO8i75sZJhYXj61t9913jYMnbTkX7h+N0QnzsCoTx+hoF+vrD2PWRFWwcXJX/X3xfehtXCcX8KL59jfrp7/JhibkWr5V8vF38eZT60fBMAvNkYLBYbfPXVFPO5u+5JXQuE5t6YElf+1vMRTl+OuTe17gXS5kkKtwsQ0JRCc/sdXQ9dpy5FS+829Q1t/RUqnza6M5lWaHyJjTepdRdam8VkAur6yYuLi9ayhCikq5G6MRkvVI/7on2lk1oOPp2GJpf2QX/X+8BdyqPt/sdHKXHvwNhbQF0p2K9Dli5l8Cmgr2vwug6io2KJ5H1YQJnnJgORb8K8qYUEKVs1mUihe1tIHu1ByiEuM1cWt1l0aMogzY0moNlsVh3JSgp0In58cdO6Jf6LxQIHBwc4PDzEcrmsLGsAVEcZpo4/Uo6mi8PUsFJJSM/a4jklrdBCMrYw9CnTY0rnromDACltkCilzniYGGgTIl99pJaPVBqkyk50mzbSqRugkL82DoUADiJfG/GVDwdNZTtYLBZYrVZYr9eONR6PJ++21OpkX/2EAFbqj1dXV7i4uMByuURRFBgMBsjzvAKwrnt90bQuOPENqWwLfCyQ4TabTWUdTPcWS5BOs5QgkIzmbDpR4cmTJxiNRlXYr776qgobygfJstlsqvQ1AKgr8q2jfDL6+pVv/PStC3wbtzZ1H+rrvOyknwSRCQAH4Bwv3rS8fZvpoihwcHCA8XiMe/fuIc9zPH361DndwLduMaZ+DPRNkVaX0k1bP2jlcvTREQ4/OCxPuAVKoLViXIKtBKqOHo3QP+w7fjXZFCtXRx5T/jvtkrsZVKBsPsjRG/Tw6KePSjDWWJ4UhuIWqwLFqsDyxRKb1aZmLe4j3j43mw2Oj4/x4MGDqr28efMGi8Wiuofc1zbajJeyPWl1GKrHEnwBVqsCRUHp5zCmQJYBJbhJv2b7nzv3vhL7LKsDrNo7pV8CrCVIqr2Xd9eW6ZZArXWje20tiJpt+RtwsNUNw+cPkl8eWUxlQGXmvpd868AsD0tp22fOR/Kycd13XldOlVYyaMSr3wfa7kopw5fZgnuxuO67iYRJP5aYy6DF4eFluLQwaSAsULZd1z0OwvJnXzlp5ZBaZjJvPP2iKI8l/sMfZphMVlit6kpk+o3tMfm4pq19mszLsbGQ75341QspcfdJTcb7Xfne0beT3va69s9X4WPOYx9UdFkuTXmlXA2zj323pl+OydJWT/5twiV2oS7Gaq3tdrEv7YJ8e+xQv+2C7sDYhhQq+K4bh7aA6zKNJgrzVNCjCd0UeNglaUo4nqd+v4/Dw0PHT04epKgIKXl8fikK+K7I115IKauF1fJLcZrUPeXNJ0MKL1k3HOjk8kt5Y0BIiDSFOedTFAXm83l1x5uUxXePY0q6xH+5XOLw8BDj8Rj9fr9S0PI6ShkLZFnvsqiJ1VtIIbdLm9fSC+UjBv5dNzXJb2zeaFOOPpCN/EKARhOZtTih99BC6W1YRIfan2/T5cu/1ke0Zz5+S38ebrlcVnJIfxqfyI/7twUgYyTzQx+ykPtiscB0Oq0+fKJ7LAmQlHdzNlkHSWo7H4TKwbcJ4HG1uuNj/mazQb/fx71797BerysLYZ4G/fJjqCXPwWCA8XiM+/fv4/DwEFmW4eLiAuv1Gv1+v/ooJTSO83ufaf7Z17qPl61cP7fhFXpPje+b61LGzZC/zCdZsNM/Xzt08UEcH4uo3fT7fRwcHODhw4fI8xzPnj1zQGFt3cbXQ12PzXK/pOWhCY+aHwzIirXiyQDX8btj3P+T+8h627zl2zQZ8OYK5DBXZan5G1iQVgAKDghrbBhjDDAAzNigf9QHAbAVEEv/m/J3s9hgM9+gOC8As20/XFaB02lzUlEUGI1GePToUbXHuby8rKztnWJQ9lE+Cq1lUtaMHq5V2PXaAMiR5yXgCqA6VnizsZaxeZ45x5VmW6C2BEutJSwlTSBqGdbGz7YAJR1TDISOLSZZDQhYLS1jKU0tnM0fxbPlAnDAth429O5z4+5N/aS/L0wovBu3rJcIix2o5B2az2PuRnHT3tscS+zGs79GCdv0uGK/1azmx8OUoGwaEKtZxGrlrZWzVga+/PF6pPQ3G4PVqsDLl3NcXrpHYBL55jlXDt2/i/2rTyYOxtL6+Cb0balpxtbhu8reZA/6tusl78il1Lrfd73vAjhJvZncz8VkD62HY3Jp6/eQrPvuyzy+1J3LU4c0aqNTlfFCMr1NFNKdNQnfpA019Y/pArmbxqfpvBkLE+Obkp5M5w6MveV0nYrkpgr5rgafb9vihyu+UvLVZJF828CFXq9XWXdqRApYol02BF3nnTYqPrAihbS+IPOogWJaGCqnrjYd3EKFHxPH01ksFt468fVzfrygb5L08ZOTnVTw+uIQ+SZauahMWUjwhdu+7zK8zdR2PN913E5NV4ZL3UCkhrkp0hTYGnHFSkzZ0yRdLW1ep5SudqwvH1fomVvXSp77JH70cK/Xw/n5eQXCGmOPnSVQ9qZIAhXcTY5xvrYR6gs0ngGlJeTh4SHee+89zOdzXF5eVkfKcnCUeFFcbr1KAMpqtcLr16+xXC5xcnJSxae7eZfLZRTse/fdd/Hhhx/iD3/4A87Ozqr0uh5zU/sUD8spNn/xetPS4P3BNyc1GTe1dHzx5bpTytK2vOX8Ktc6si3LZ8mLfrs+MeU65u+8n+Odf/UOBicD5L2tMixHadWaAcP7Q/QPS6tP5+hhWRwhfIq5GXtGseOWmcy+G1OmQc+GtTdjw1S/BdwjjQs75pDFbD7I0T/oo/dPejBrg816U/nT3bPz53Msz5fBcuftQ7oD/n6UopjR5hkOQAD2zmvuJ9N3+ZSVVoKuxZYXnf5A85y1kCXQttfLYQzQ65UAKt07S1kjQJD0p/ydhyEid2Nc/zIfFujlJI8r5tawNk4J0FJ8fpQxWcby44jLdE2VPvHiebBglAW5bFxe1uTPAVJumSvrl975eFMvK2PcgnDLpX6073WQm2Zdfn/YMrx04+9Ubrq7G9/1d+MQn9C77ib5mFpYGU6G8VvIhqxhtb1fzclbFlp5aHko+3xpEfv06Qzn50tcXa0brS04tRnf2oIFPD0+1tFpWG/THve2yHqb95B3dLupbdtJse5sAgzKEwE1nWQsvdD4dxN9hPan/MO+VDl8OshdqQmA2eX4dpO4QJN0uyzn1DIN6c1u09jeGoy9baDQHXVHKYNu1wul29iWmrZxn8JcU4hJhbh81xbUWljJX8q/C6WAJKSgJEscPjFyixuuvO96EtolrgYo7kMWrW5knXepnJb8+RGFPtAkJK/mH5rkmvCVR6Sm9gVOsb7a1u+mqMsFYiwd/rtrf0oN5xurfHWhjZGpcXehJuXSZHNEblaRqStf+PgZkyG24JR9S6sHbdOl3e8h+Wr+IRl3qSttPCAZ+XHEL1++BAAv+Jjax3ztrqm8Pt4x4CplLObP9E5Wi/xjqBBAKOdq8t9sNpjNZuj1ejg4OHDAtxDAzd1PTk7w0Ucf4fLyEufn552vI33gqeaXqlhIKX/u5+sn2jutm9qO83JelHMn1Y0mh0xPy6cWRysHWvv58iLHG+23C2pSjqljad7PkfUz5/jgfJTj8INDjB6NSjA2Q3Xna5ZZ0LUqqzbDnFGeU36N+55trSort6IEhk1m7K/JYHJT+pnSDabMkzHl0cYwQLEpKstZOsJ4fbnGegtSVO3DwIK8sOOHLHPZnprWH+fj89famTbfSjcKXgIyZWH2eqUj/ZK1Hh0FTICmPd7YWqmW/ErrU4pHlq5kCUth6OjhUubSj9wJbCVLWHs8stnyp3xSPDg8SC7rTsCr7ld/t26ljFDDZZmsFzkOcoCsDqS69QVoYKtb7aZyB+DlLXm0IX8T1dZ3qTzC4Kt9NzV/Xgc8jvtslDhpVrD0rsWR/ry/2Tjuu+yTroVs/W5Yme9QGdXd4tawmnzGAOt1aRF7ebnC2Zn94KStst/K4B/j+LpUumthQ+mmzPEhCq3pJS/fuK6N+U1k0OTQ5AlRU/0dpelbu+2yZtmX7vQmKaZ32YVvilsK7VO/F1rjN+Gj8dB4h9pQavsK7Ydi4VLkDul3mvLUyJ1nwjrNFB2nrw2nlknbfUfqvHBTtI8xdl8yAP76alq2TefWrqg1GHsbFdg3SV2Xh09JtO9Ou0/F9ttGbRZykkajEe7fv1+9n5ycoCgKTKfTSnm3WCwwn8+rMPJuAK7k62Iy49RkMZVlGUajURWelLQk009+8hN8+umnFd+LiwtMJhP8+te/xmqlH/GzT3l3iUMU63N2U6l/eaa9c3fOw5d+2wmJyzSfzzGZTLBYLCpwolTs5NWvjCPllOloY4WvrEObzOFwWKW/2WwqMCXEg6cvZUkdI9tM1BTvOsZHLS83MS6nysAXxykAQKiutLbFj8T1xUlpizdJIaU096Oy5Eep+5Qb5N+WQn2HNjq+utXkbFKnu5AP/Lm6usJsNsP5+TkA12JQ2+Be10bItyGVftTONctBOWbxeFQ33CqMW63yOqR4ZA1rjFFBtc1mg+VyiVevXmE6nWI2m+Hs7KziL++SlO2Znn/wgx/gL//yL3FwcICDgwO8evUK8/m8Wj90Pa+n8NKAmZT4vI7k+MTLI1U5EZNVGxOyLMPh4SF6vR4uLy+rr8TzPMdwOMT777+PTz75BKPRCP1+H//9v/93PH/+HOPx2Jnr6QjyUPkQSavgXq9X3UdMR8/S3XTU5qg9adct7GoZ6+vLVKZ0VzT1Cb4GTen3J5+c4NE/f4Qszyzg2sswvDdE3i+B2AqEBepAbE3g7W8oWQPHGpYsXstXU3czriWstIolQDZDVlnEZqaMbwj0oOOKaWwxcI8wNgbZZhtnY2CGJSjb+7SHox8cVUcbF6sC66s1pr+fAkXZH+bzOV69euXsGfh9sU37fMp63Bj34xxKVyo55bjj9tsMxmRYr6md2jmu18u2vEtAsnwvw+d5hl6vfAa4ZXoJepby8/WPBRfp6GJeJGQha8PY33rebdgso35KebL/cCxl4fiTpSy58bA2fb91rI2HKn+AtJot+dpygcNr+8be+TqHh3H51ZuFK48sq64ozMtvmSvd7buJ+nM/99nU3KkM3XdfWJs2j6f7u2Gln8bDuvvduMyxMtPd04BpKVtRWIvYZ89mePFigcXCzl+Abecha38iH6jC993kJ6+KkHx8634fabLI/XMTSt2vaWv81DQ1GWnO1vaBbfLRhG5KB/s20XXtr/dZ5k3W4PskX/+Xa2eSh6zdKQz/+FLbo/tIroF8YXepa20/m7pH81Gv19vraVe+q/ekW2yveF26wi7T24fcb8PYGcr3rrLHyvTumOKG1GQA8Q1sPnBDhtEGYG0TG1LupcibkrZGTQZ8nyy+PL5t5Ms7HaE4GAyqyZIUQzQ58WMgiUKLzdiAoSkdfNS0rPmkTwpAosPDQzx+/LjyI/6Hh4eq4lVagqYMdm3aRmyybNNum6Qbavc87ZTJyqfUjdFms8F8Psd6vU6yyJFycUWnFjZWRlp+OYCgfdnGN6pSlhBfHx/fmBnaSMqFqq64666P+fi3odRNe8p81DSNNgoDwN8HuEI/dkdlqN+1pV15NVUexMrVVX7q7VoDK2IKFV8+Uzar+yj3FJL5IsU7za/SQlAbQ0LrtJS+3bS9a2nG+jyNv76y5uMz5X82mzlAhEwjy7LqqgG5DiEZN5sNrq6uKqBxNpvV1rgx2Q8ODvD48WM8fPgQ9+7dw5s3b3Ze66W01ZBiQZajT5naVKbQejxV1pDilN4Hg4EDbFG8PM+r8j46OsJ4PMbPf/5zBxjN8xzrtT160XcsWmgvw8fi2Wym3h/sq+OuxohQXfd6vVpeVVnyDIPjAbKeOyaMHo8wfmdc3f9aAbJ0R2zG0mdso3lTwSFjeRLYmrl+mSnTh4G1YN0egVzlzcBiGNuw1VHFOQNlyTrWmMoKlkBazeqWwiJDaUWbZ8h6GXpFD8W6tJotlgWyPCuPaV5nKDYFMABWWAFrlKCtMbV1v7csZLF5+kPqGKyNv/44vN2Xd0cCxfYuV2u9R3e+2nHEWvuVfZgfG1y6GwMYDpozINFsASJ+Z6xtDO6vXbdyPyjhyQ2KOwLhAAuW+sJzNypXv1uW2TzaZ218cAE1W8cynOVX57GVwtT5d0/h45B9flJ2GU6WJfd3n33+2pHFaVaxPEzYP80SlrvVn13w1l9WMfd0C2HbF4HNpsBstkFRlHdGT6drTKd2PSQBQD52xNaH2jo8tEZI3SNocYl/Svimc3Boj629c1marK34WmY8HgOwuiaax7nuoIkeI0Scpy9OKI93dPOUooeSbT9lv7ArdaVjpLafcr1ak3x1kW+tr7Ud03w6Ps5HA6l3TcvnF1qTxo6UblO2oTkiRd4UatMmfLrR1DmnKYXmjSb7bC3+dVMs7VZg7K5KlDtqR7GFRxva1+TTlm5bu2ra1mV5vnnzBpeXl3j//ffx4MEDAOUkMhqNKpCpy7uz9l1+ElSO0Xg8xk9/+lO8fv0aP/vZz6q8jkYjDIdDrFarJD5dkRzMQxa7TSaalKN/Q+lQGtxqhRZdbYkvEq6urjCdTqu0hsOhky9S2JNiXgKgAKKLP5knLX+SRwhYI0BB1lGWlVYv2oJNsyKnPszLgwN7Mr9N6G2eC5u0VQ1s8cWXVimp6cb622g0Qp7nuLq6qm3IQ3xvG4Xk9YFavn6itT/NjY8vfFzRFBAhuUObKy67Jv8++glPS/4Oh8Mq7RhYuI+1lY80JRYfn2Q++DHLst3Tl8EElK7X62p8nEwm+Pzzz7FcLqujimU95HmOwWCA9XrtgHPEnxRgz58/d9yJX2j+5pvnwWCAo6MjPHnyBB999BE+//xzXF5e4vDwEACcr7yvm5rM86GwVB9y3pZtj+qg3+/XFLsp6VNdHx4eYjwe4/T0tLKKzfO8Op76/v37+Oijj/D48WP8t//236q6I7B2uVxWa4KYQoHkBvQvxieTiSNjaJ6QZbiPfkdrnH6/j36/X30MyNMl6o/7+OD//gDDB6XFKwGv+SBHPswr8JWDsA4f2RRShjkJKikWsJWfEeVkbHgO1Fbx6B/uc1Zsw+SojieuLGfpd3vMrmMZC2PvlS3qz1m/BHiLQYHeuIfB8aC6V7ZYFSiW2/tlL5dVW9t1zyP7lOxH1AZDa1eNR30uLf9XK7MFZDPHMhYojyY2hixl/ZaxQF6Bh8YQ4EgWoK6lrLwzllvI0j/50a/991m++ixl5XMJ2Fo3aS2r/fJ8cpDUxuXheRw4wCwPx9f3cgyx9cNcPX7p4wuXr+2wFEpX4ynDyzD83W3HMowb3/7W49TB1TI+f+dh4v7++1d5WPdY4rq/L99xvybHNLvAb1EAFxcr/PKX59u7os3WStb2SzqFgk4D4R+u+eYvcufzJVlzrVYrh0d4DKrPn23nTG2c3IW6nLtpbUNzw3A4xI9//GP0+32cn5/j8vISz58/r8KQLor272/rPvxtp9uiA0mVoUvAqi359udaunIvwfdpqZRi5elz37Vuu76aLkahtLpup6PRCIPBoOJNH8dyWiwWuLq6CvKRY3pMR8bD3VTf22e6TerwNur92szRrcDY2zDw7ptuA8Lua3Rva/k3WVRKhY4vz9pA32X5dLFYJUWntoBPVYSH+PtoX+3Ex7fX61VKWlIW0sKajq/jRFa2tAi/bpJK0JCinsdpk07KBNLlpksDQ/hRSL7NS1swWfKI8dZAWRmXg6Tj8RjD4bCqs+VyWQOMQ+NASIluFWGukk7yjaWhxQ2Vx02Tlhetrae0iSZ9pIt2zWXz8Qv5XRfJ9EMAhK/9aW6hfGnrhNg8q8mZujFLBbG6roeUsTvVPxQmpd93BSxp442vHcu6k/4E2vGxVcpOG0h+p7gmj29uCuWX+BFASYArXyPsuv6J0T7XzFr/Cs0zPGyKPHKOlGXFTymRYUlh3Ov1MB6Pq/mTlMmcV0wWbVyIrZU05YKcD0Pl14ZCwJuk0cMR+kd9IAN6ox5GD0aldWx/K1e+5ZeJX8BiRAKIaiYsCQh7LHGJgVXWro4fYO+uzUR5bcORP90F6xhIGjiWsdJqtvrf8q6sZrf8SdZKzsI+51kOkxnkyMs0sgymKI82zvplWfaPyzGG5ComBcymXk+yvkJjvGxTobGIW+PG1v3uWrDMZDk+lhZ0FnAF8pxAJqqPOsCZZTRWW4CzKAi8pXTKCiGxjCndSv71O2Nt5TolAluxZXxK07qzhgeI95Aff08Nw92pXOt+0j3LLFDmvsf6WR3UdUnWs8IhMH3X/UJzXzoP6VZ/N1E/7m7bUD2eDE/l7L5TH9GeNR6adWvdnadfvxfWn39JWhvS8u/LL5dlszG4uFjh4mKF5bJAUaQBlU3Xs3ytxt+lnCnrWG09GNunapS6vk+Jx+PG9iextRGng4MDDAYDzOdz5yovLc22cod4xfZYWvym6b7t1OXaOrR28/WR2LuWRso+fN/Uptxov8bXMqm6vJA+ylfuTfpEKG0tHzFKGcfa1FVbXYDcN9PHr/JaQcDVd8srMnxr1VCd7JKXLinUpprGvS5qWkZt5i25L26j8707pviOOqOuFa6ktAP85+b77pe8bbRYLDCdTqt3OkKNlGm70D4Vjk1oNBrh5OSkeufPcnCiSYysl+g4xX1TaLEnN0j8qOUYryYKTY2kdZFPqZlCsc0OKcblBpHL4VN8pba12IKXE19MGmPQ7/exXq8da6l3330X77zzDowprZl/85vfVP68HfmstJos/posYr8N5MuX1vZ8gA6nmJWVr11obUYqW7X69d23dNN1lapYAerKaB9IlQqi8HbM2zXFkXfRSnk4nzaLWeKTYnHXlLcmj1S2A2knN9wEaYo0IumeZdYSUlPk0cdMi8Wiso4koo9ZQnWaZZlzSodGZG1JG1BuGRIrYwIDN5sNXr9+jcViURs79tVPUxVpEhwMzVc+ebV604ifeJGab5889PGb7L/r9Rrz+RwXFxcoigKj0Qj379/Ho0eP8OrVKyyXSxwdHVVxZFq+OT50lDEH2MlN26DydGjtq13TkUKheSvLMseCQK4tH/70IR785EF5BHGelXfA5lCtYKs0MsvfTbCN8Dy6GHeN/c3ALF6FX+3X4+fcG2sAswUQuVVsZWUrf7f3zDrWsAalxax4Nr0yXJEXpeybDL1BD71RD4PDAQ42B5Wl7Oa3G2xmbp2kKN5kPw21AR6Hn1zDPzwJ9VXOe7MxFYBU3utqLWTJMpYsYo3Jkecl6ENdhlvGlu8WGKJn7ZfCSrdSDgn48v8SfKXnuKWs/65Y7m/lsxa9Mo59t2FtGVg/6c7LhdejfTcsrltXkqek0l+O+2rQRuTnUQcJY/Hq85L27PLl7nU3vq7kfmnHFNO7MfI97Md58DDy11cOGtXDhUHsUL4p/aIA5vMNfvnLN1guiyqstrfhV/vEwAyNyvGiV3MjSlkny3net0cPhdknyfVpaC+dQnme4/j4GMPhEIvFQgVjr5u+7cDqbSG5l0sJn9q2Yvvm20x5nmM8Hld5oLV+V7SP/O+yx2uj0/GNzV2OhYPBAKPRSPXbbDY4OztzLPzpuHWgPAWqDeB3G6lJe0kFbN+2MiDaVe47MLYFpShjQmFTiCts9tU4Q0CP7BTagLbrwB1TPGvHuPRDDOXFAAEAAElEQVT7fdUa4LYQbfy1hagkrhiQ7rvmbZd2F1us0CQjZeRHymaZPdoHKIFa38TaZT022SilgCWaW8rGK6X8U/MdUhalktzs+QCd0MIlpqQO+YfqYLPZRIHv4XCIw8PD6l6673//+5hOp3j27JnT5+T45BvH6GMATS6Zl9TFXEq7SAmXklZTatrXUto5kaYE8KUr+4a2WQ+Vt1SsynkyFYDRSBufeBvisoeOvpX5j/WbpvKljluhsZzLHlPsxNKQSnLO3/ceo5iSnYejvszrZJcNWEr5NeGj8aX3JuuqlHL2pe9bnxpTfpgjrSV99ec7cirUzoqiwHz+Cm/e/ALGvMLBwQV+9KMDPHnyeKtYAJbLsZJm9eS8c2UvvWvKVqC0ZKMw0+kap6f6x3vUjiiPWp5SxhZeR7JuCSwfjUbVWomXeWpbIzknk0m1sSdQntI5Pz/H73//ezx69AgHBwfVsX5ZVlr50ckSTT6W8I3tPN+yXFIUX03mGh8PTabNZoN8lGP8gzGGGGK4GpZ3neYZDt49QG/UK++JzWDvg83cX4cnz0oGa6nK3HQhFaftna+O3PROIFeJpzn3wlZuFD5DaRVrWPzQr3xW3DKztWxFVlnZVnfUsvnC5FtAdlt+ZmNgclNayprtb2Eqy9nq7t0sw+idEYpFCdoWywLL8/LDujbjthZHzk+xtWlo/CplzpHnZDkHrNelhSwdVewCiiQPqt+ShwVEbXL1o4NdkJLiuVayZT8u+RHwa9PQLWLLMK47xXOfATvmunfdlvVf8nHD2ziUL597GU+6yzhaXF+YUPgYpbSzUNuJxPT4a+1ROtGcJv34vKi5a0Annx9DYeWcqvnLZ23urcfnbnp+NaqHqeetnldtveDKtNkYnJ4uMJ2usV7XgV25DuKni+xy7GYXc52mX0hZq3ahZ9HW1yF9p2+9IIn2dcTvvffew8nJCQaDAYqiwHQ6rX3IR/z1vpS2LtboNuoV7yhMTfdpN1nHKe2Py8cNkXq9nqNXXSwWjp6c69J4er5xp0k5aDoxmc4u/FPjNtHbtt3Da2nzcV876RFA7dh0jlc01antk7rWuXdFqXUldY6ptMu8neLG/ZqmdQfGdkS7KP98/EhBJBc734bFAs9Dv9+vKYWKoqjueOJ0cHDQiTXpPonfLcJJmxj4wO2bLENKsBRq0mZCbS3L3OOE5aTPv9LK8xyPHz+uLBbpbrh9Uwx8SAnbhlI3J0Azi61UhXAsPgexQjI0Kb9YnFQ+8qMLuoOB02g0wvHxcXVv6JMnT3B+fo5Xr16VStctGCPTCoE6ZOWVqqjbFZy5TdT1Ijm2OJJ+1E/aKDYk8JDSn5qOnxKY4soCSjclTb6x2hXY84F7WlhNDnrmxOce36Ztl7lHSzOFfONeyN0HJLaVKWXd1WZM8IF1UjYNfJXhZd3I8iA3H8BYAqXzGjCntYE27cCY0lLz8vILPH9+hSzLcHIC/F//1wkAfnKGTMu6W0Wu+0vASJk/q2S1vyVoUhQG67XBH/5w5QVjY3mK9WEq51C4oigwHA5x//59zOdznJ2dOQrI1DZJ49/r168rd74m3mw2ePnyJV68eIGDgwNkWWk5TR/E8bW19kFSSJaQn5wrYxQCUlPJ1/+MKU/Q6A17OP5nx+gf9NEb9ipL2Ap81UBYoMRoCHAVIKx9VEDamiDW3zly2Ai+23BkBZtlmRuX59O48aT1bGUBS24Uhtoms4Tld8+SpSyA6n7ZrHDvlK0sZI31qyxk8+18t7WQNb2txezGoNgUJcC7vYv36HtHpfuqBGKXF0s0xtEQBig48fEvNL/53kv+ve1atSzUXi/f9idq8xmAHECxBWnzrTVtDrKMpSGWb3erJhf4lc/234KteW4tV8lPA4D58cV27pfPNlwZx/XX3ykOHHcgY89ueAJny7wZ9mxldd/5HMTriJenNh7UnMjHefPxbELh/YTPzahh3PA+61cb3/XX3Pj6wr7LeNr8y991P/9dsDJsiOphjMe9vm6Q7pp85XqgwBdfTDGZrGo8tf07Bzl8a2l/fpoDhqGwvv1VKhiy6x52F0AhtH7ie8NPP/0U77zzDgBgOp3izZs3uLq6qp2A0+Vpal3rcO/oeuht1stosmt7Ldqb9ft93L9/vzqJ5urqCovFogrvM+7RTpbcReYuaFfd5nWSHGtSLPX7/T4ODg4A3NyY4ps3usSRQnvFfdF1l2dTMJaoSRnfgbEtyacQ60r5qqXjc0tJ07dQa9opmypZfIvEFOWVRqvVqhoUucKxiXz7JB/4RccIynBACUbJxajGt43SF2hfJtqkvl6vq3zIhfB0OsWrV68A2KNj6SiH8/PzqBy7yptKsr2E2mjsPSardhyhTF+TIVXxHevvscl4l/EqVj4pmzG5GJXjAtFqtcJ8PsdoNKoWolwBLfPgG5MBXQGdmqcQva0bghh1Nb7K8c2njPf1x9hHKylKEp62BEG4G4Ee9FFN03zzdp3af1PTiIElIV7S39dnNbl4urG2rim0umg7PnefwmsX3inhQnG1sde3+Qbco6NlW+T543MKv4+GePC1R6y+aJPJPwqT8TSZ+funn97Du++Ot8d31us+y4B33z3AaNSDr7g0ZTC50fGg/Jcrd/l7r1cCs72e2b6XFmT9vsGTJ2P8s3/2sBZ/symVs8+eLbBc1j+y8LUrOcfwZzmvGmMB8dFo5HxZrd1nyeuP1wmvb27Jy9sLjVvGGHzxxRe4vLzEq1evcHV1BcB+LMjrSBLv6/IkglC5+E7g0MLHZGhDWZ7h/o/vY3BS3v86OBpgcDxA3s+RD7bAd56pd8JWcpAoGepg7NY9XSD+mFnMhdwNStA0K5+d9GTxht6V58wIkJa/GxaHvVeg6zbvlWWs2f7mBihQPZMVLQxgsm27pyOMC5SAbV6CsKYwKHILytLv8N4QR987QrEpUGwKrC/XWE/WavuKzfGx+VTueX17YN98UrbvcowBCqxWQFGUxw8bUwKxxuTo9YAsoxM7yBq1tKYt+dt/e5Sx/9d9LnnyO2fd/FvwqrSadcFXC7Bad3m/rDEAt34tw/Cw/J3GCx7HhqPx3FaNLQOIo4d5+mV+DAvLeQASqOV1Z8ujVoVBartECcVz/YzHvR6Ov9efjdfdnUvl3OS62XnWD7hSePlcf/dbymr59efdloHPr152Ut66DLRWePFijsvL8o5YORbQr9yDSCBWUmhNTnM/P1Es9jFnaOzb5cQ8vnZI0TGk6GxCukXpprlzP1o/8GM+OdhEsu+yXmi6N9Lah4wT4ntH7SmlnlP0Tm3SS9URaRSSQVuDpPSzlPcU/dUu5dMmrk/PI+k26M9C4yCvp48//hg/+tGPauGGw2HF4+zsDD//+c9r+5x+v+9cwUj8217XEqLbUKZtqAu5ux6Pm44zoTlcI6dF+CbK20xNZO4yfym8tEVd00E31U/y7wJE6GLg1WSiMtGOFZRySXdpRcePvw1NaqkTQlvS+HOwaLFYVPfb+vLKy8dXj7tOrm0WGDzOZrOpFslyQ3F1dYWzszMA5YL54OCgOit/MpnsJLeM07T+QhuApvGajDf8V4sr27h81/LrKwOpuI2FzzLX0lkbr2J50/Ll28SklKVvU0tg7L1796oNm2bJ5VOsae+pcrXZEGi0781aaE5KTTsGnDVt+3LMT+Xp6680Z2h9KpTH1Lma94Fer+cswtusH1IVFSky8vhN0o2VlU8JIcPF8u8be7Isqx0jvCvJsSo0hvriS7nbhElJQxvTSXbfOEr+sflebvT4f+p6iK+p5KbTlUnP5w9/eIw/+ZMH6Pez6j7FrAIQ6nEta5/iuFTGl3Jga2VmKgs0HYw1MCar/MiNrMWKwmAwyPHgwbC6/5F+V6sC8/kGp6crrFZFJT/kXZ7wKxllWWmnNFD7HwwG1djiU/pKfsRDqxuaw7mSkn6fPn2Kr776qgo/HA6rtLsiva3UldySQke9p5A2bmW9DCd/dIKD9w/QP+jb+2AzlBaxWeYAsQ7gmgk5snpargAJQsqsZ6I8MpSWqsSPeRl+9K01r/XzNx436W6EnwzD/zPxXti65cAthc2MPa7YAWW34OvWcBRFVtgjjnsZ8mGOYl2gWBWYFTNsphsHSK6yk7Bm95FvnRAb5+v7MbrHGygBVm7pus0g6Gjj8qh0+lClCsW+WaDkbd+htKx7npfjn3b3K7m7/q5bmQ63nq2DsvyZj900TpflYOXbSlCF4W4Urvzlbry/Sh5Q+LthLD+bpuvmxrFlq/nvTvWmKNtWSry6xSh/L59NkrvrL/20sHVgVcaR/roffaDA3f2WsOEuXM+rL56Ul565O18nFIXBy5dzvH69rMLH1kpA/cPz2JpR6qRCc7wWR/qnjnupe2z6jc3RbdLj+fWloa2hyvVauYa5uLio9GXy9CPaT+wyF2gUWot0ndZ3kZrsn9vqZ7tsx13riDlpcqbo7LSwof27b4/soyZ5jo1bTdPpsrzbtANtv+3bw7///vv4i7/4i1r8o6OjSi/529/+Fn//93+vnvxHJ0YSFUWxFzA2RG3q+qbSD9FtGpdDssTkdMDYfQ4++6ImMndZ+W0nlRRFYReUImOK0q4LOSgtSfLYR251eXBwgE8//bT2pT0/g321WuH3v/89lstlxU87wrjJsbD7otjdXG3L/zoHImPqX+/0+/3ko0I5yWOcb3JATVXM+2RMAXochWHDcahJ/9TCpQAoXZBvrAspr2PxiSaTSQXoHBwcVEcYhXj4gJrY4lHWkdxY3xSRXFQOIcV9l2mG8qz5N12UpyjkjTGVhSrf3O8ytmv1Keud7iPl86QE0rQ2LkExXxpN5W0bV8oRK+vUzZIEfzgPcqdyBOpKrbYk07uOvtCWfOWptQteVpJCii1eD3y84tauEiiUQJ9MS9KjR2P863/9Dvr9HL2etYJ9770DHB72mWUsEEaqSElrleU2XxaIhQPKWiCB7kg0hixOqW9aEJbcKA4dX1wU1np2sym2VrQZ+v0cf/qn97Bama27qSxm1+sCr14tcHGxqmQNtS/fkdBZVn6AeHFxgfl83nhekeOJ1vfkOJllmXNnES/rEPE8yjYXGkuaKGi6nksf/fNHOPr4CIcfHKJ/2Efez4NHEteOJUb93fHT2rR0klnSopCFaoQqkFbhb+yDE9YBMA2c44m144qNMZV77bhig+q/so7NGRBbMGtZgxJ8NaYEu4vMvheogNkqPv32tnx6GfJNjmJQ4OC9AwxOBihWBYp1gdnzGQpmsS6V/SHgI7T/9rVR7i7nfqJ+v8/6WlGNMYOBgTH5dvzB1no1345DJTjb63GQiI/d9t0YAi65e+mW59zPvvv+AftBC8Wx/6V7/dnG1X5pjNeAY+JBxaW7leM35duWPQfWOIBtFP86L9edK7dr3p2TnkZzYFHzt8+6xayryOe/fktZ7b0eJw2M5e50goUvn3WK15OvrOplUQeISbbnz2c4PV1iMgkrvUNrNb7+1/z5uxaG71ma6i5T90n815cXPn42nYd96w6fzPzfF5/LZYzB69eva/yLoqjWMrdBpwfstif7rtF1lNN16de7Jnf81p/pfblcVvvo1ap+zPq+5ZPuXek3boJS9LG++2EB4PHjx7h//z6A8sShjz/+uDoN8vHjx/iHf/iHms7866+/xsXFRZU+Xcsmr5F7G9sx8HbV/22i1scUf9snoS6UCTFgIJXagkApct0UyQWZMSXQR3mleyEluDoajarBaz6f44svvnAUvP1+P7pITCW+OGwSB9CVAJLatInYglYL24RvyF1OvFxxTOAQTTx5nmO1WlX+ckIKASmp4N0uJOtV1nVque7aPrTFViy9NhTblF0XhQAsGYb7L5dLTKdTDIdDbDYbPHjwILncQ+n45ElZpIVkj7n55PJtavlz6JjlfVBKOYTkicWP+fM64mNGigJES6PpfMq/wvaloW1ImipbNJLp7VLvISV1G9m4fwiM5WH2Md5oZa+N7U3ix+LGxuRY20ztLyngljZeamNb6tjE+QwGeWXtev/+AD/+8X0Mh/n2vkSqV67YD5NUnlrFLnczgABirdKX/5d+pQI2c+JpaRoDJrNBlpV3OmZZjl6vPMaY7pMji9nVqsByWWA6XWM6XTsyxroir3Mq56IosFgssFqtHGWlrAut3aWObbJtpqwVpdwhN5mnUH/Z1xojy7LK6tVs/w4/OMS9H92zlrC9rAoXAmFrAGyG+H2wIffYEJ3BBVp9vDQ+BuVdrnTk8TZuVRfsLlrHjcelO2OxrRuz9cusn/w3RvGjvBZMVvrdlvPWSNT+8zAFkCMvLWSB8jjjLEN2nKE37mGz2KBYFli8XqBYFzbuVh5533JsTE5th3Ks1MZO2wfsHZRZlm/HFdrDUp+nfkK/FZcqXFHYsRQ1AK20+CddXTn+ZFv+2XYMsx+p0PHF5VqRZLAAKhVk3QrWVl45VtYtZLnc9fcmbtxd85P+vjC+sJL2uc+h8TsQQvGrrxG0OHXe7rPm77oRn/pca8NpYfgcpz3X3133MIXz7nN358i6O18bkPtmU/5fXKzw6lV5z5+mf+IUA2Tl3O6bM0Nzc4qugfzb6idCYXaZl5us5X3lE4o/m80c0LrX6zkfxqTIF5Inha5zj70L3aQ+97p0SLvoz1OoCx3bLmk2ibNarZyr4rosk5RxMIVHqk4nlMa+6xzQjaTkeKUZeA0GA+R5jqOjIzx8+BB5nqPf7+Pjjz+u7omdTqf44IMPaidPffPNN5UBGY1tXJeuyUBuN9HXY+Wfsk/VZG9Trzc91rV1S6HWYOw+C8VXcbcBRPSRNnDwRVtb2VMUx/LuqKbUVNm7a13keV4Bqpwnvw92NBrh+9//fu1s9ffeew8nJycAgMvLS/yv//W/qruwuqZQucQWl7KMJPCo3a3VtO5SB7cmfNu0H6rPZ8+e4eXLl5Vs/Pz8zWaDwWDg1CdNSNdN1B/lcX0S6OF1lKrMbtovUsJnWeZ8ie9LW6M2G6HUsFrZybFO2zwScM+t3I2xxzhSWJ/V9WazwbNnzzCfzzEYDJBl7nHLdAcipSvrkcsrx2uu4LstRGDgcrms5CKLzZsi39jD21uTu33keKkBanS3trwPU5ODyxibr2Sb4GOWbHu8PWoy8vhdUBNe17lG8qXlG2/a8GrKw0f7LhPZztoqe7QxVAMGQvy09uhLVwPVuMKr38/w//6/H+HJkzF6vRzDYY6Tk4FzFHEZD0hRlFvlrVRQkpv+zJW8rhtZn0llsNlayFprWmsxWwIfdGRhCXLYI43JUra0oM0xGBgMhwU++eQYH3xwiPW6BGh///sJFov6nae8DVBZ0pfVtOleLBbVh4eyzmLtJzS37jrPyziyPWoKbA6GkZtvD9Q1HX//GO/8+Tsl6JpnGD0coTfqOZawWZaVlpoofyv5M/YMOEBs+SPcJclwDSnK30C3nqU+JCxgpaWsMcZayxrrV1nGbp8J2ATgWspurXezwv5yvpVVrLFWrtVdsnSHrLSMNaayoi3yovzdFGW83vZ4475BtimtZPNBeXTxyQ9PUKwKbOYbbOYbzF9YQCUEmsh+kaJU4m2cr0219PI8x3A4xHq9xnK5hDEF1muzvaM6345NNEaBvZfHubv/NI7R3qQcV40hUNRnLWu2Vq8Q/vVn+i/zEnbj7+6zZg3rPtvy1H/rz+V4L93d9zKM5q9XaybC7h9YKeXwzbkhN6OG4f7cz32Wc2s9jv2VYWUYd551ebhWsBSnzsPmx0ex5VG9HKTupe4nZaPrCjYbg9PTBZ4+vXLma22/wX+luxYvNq5oPH37Y98d8ZKfNg9L+TQZfPorOX93STT3h056kbKRDqDX6zm6IvlB7D5ol7XSbaHr1p3ftvx3Qdexjw7pDWV/lCc+np+fV8/7OGEwdf/RdtwI7WevU4fR6/VweHhYG4+n02l1RPrBwQH+8i//ssIciH7wgx/gxz/+cXXc8IcffoiTk5PKKjaVSLerjZG3+bSvpvS2yt5E38Ld2/CsgbHX2Rl8pKXvW3g0lbVpnNSwsYGp7SAZkrcNT20Dm6JE9C3mON+mcoSo1+thPB7XQNuTkxM8fPiw4uH7miS0YI1RrFwl2ECToayrVOWstjhPpaYTog9E2TUtUjL6JhACbLmiL7UN7ToepQAxTRTpUvko3dvKy9uub8ESaiupSnsKu+s4mNpPZHpS+U9uHCCVG09adNKxLIvFogIn8zx3FqTkpoF2XI7YUUchJXcsjlYGklLaPx/H6OOGXq+nAoVdUKxdaG0xpECQvCUPja+Moyn35XtqHw+F8aXj6+tSTk12ze26N8ZN1jAxN60v7TLHtol/W9aoWv/1zSOh9h3Li9bPQvJo66BYOqNRD4eHgy0wUCq+hsMcH354iPfeO6gA2HJIlUBsJQXPGctjVoU1FWBgqjj0Tkdx0nMZR7OM5W665WyZrvbrKvXptzxWtNi+U5ss38u7H/sYjw2Wy9JS9uCgD2BdpbfZuGsI3q6pPPl6URu7Q+NjU/KtGZrEk2CUxiu2WQ35Nc5nBvSPSsVFlmcYPRrh8P3DEoyl/6z8BVCziKVfR+7M8q7ujhVpuq9KGHJPkD85/FYeB5A1jId85m78lz8D1qo2E+3N1P3oCOLMZEC+ddvKVDuumMrTKDJBPBfb/BdAbnKYzJQWsjAoUJTPmQHMVrZjoFgXZb3mQD7KgQ1gNunrz1rxKv0jBlxoYWitWfZ7WqdmyLKiAknJMpYAIq3ZZxlZr5aVZS1ZKQ1sPxwhS1hpGWs/UCF3qnxjLNBLlrJuQ4HyjCpuCb7W/VgJifdQOOnG3TU/G4bmj3A4jWcKhfil8/I1OdfdKG71MPy9/uzysL+mFseuC+y7Gy/VIlb34/L4KGULpYWp90W/3CTDZmOwWGyqj66m0zUmk7Uom/o6LXUdRs+x8SU0BzfVM2jzbpM9eUzHlMordc7W1qDcL7bfa/Ihb1NqqhuJrcualMm+qcneet+Umt+uZO5qX7jv8or1UZkP2de5NSzXm4V4p/r7wmvjT5PybroWS+HXRT1p44wcux4/flxhDkQPHjzAeDwGgOr49MFggM1mU+kiyZ14LRYLLBaL2lVNmmHFTes3JKXMK7vyD82pNzUXhMKk6vxSqQbG3mQj6AIgiPHpMn9aOrFFWpdpp7g1pdiirSu+2ln3u6ZHR8CNRqPqCxWarHa5q863KD05OcFwOMSbN28an93vW4CEyj+2uE9VAO9CkidfCPCvGbPM/eJns9nszRK26bgRKivOh+dN21xpfDTicUOKHp6+XGSRVSBZ14TydVssJmNj4WAwqD62MMbUjlyheh0MBtVC5s2bNw5/WuzweEdHRzg4OKh4P3v2zLlX2nfML9XTTc6BpNzjQPJiscDh4SH+6I/+CCcnJ3j8+DE+++wzfPbZZ1UceTw7UVcLZkqnCz6SZyqYGkuzyaZf6yN8DNDGOQ18lOOAr59r40tKHtrQTYKVIaWO/NXC7bI4bsorFfzS6l0bp6SMISV+U1klxdpTaH2oxfn003v4f/6fD6v7YMla6+hogH7fWsK6bd8vn7R+tc/1X+lGIEAZz71X1g1ft4glf2sJS3Mpt5C1lrHlvENWNNn2Lln7S0cc0nOvl2E4zPHjH9/DZmOwWm1webnGl19eqcrkLMtw//595HlebcQXi0U1rzUhWsfK0xtS1+xN2lme59XxW6enp46CgacT22uF+nyoT2g0OB7g4//fx+gf99Eb9tAb9dAb9+y9sDkbuzOUbgAkEFsDXpn4tbzIsEDQqrUGsoZ4J5JjBWsddetYCcoYUVeGKfjZc2UNy94dN1PmzbGIlffIZsya1hjHUpZ+yWoWm61subWYNb1t3I2p7pE1G4OsX/7m/Rz9wz76B32szleYfTPzjr2SYmvRVDBCjvt0BF15fcYa6/UGq1WBzQaVlX25Ji/HFG4Za8fZ0q2UsfwApFwDlmMZv0uWAFl5j2yWUVj7W/5rVrV27ObxNTd65+4pbrZMXWtW7dm6WatbLbxbT8YbJuzu64NtdA86UKiGrPnVj7nn74aBi3X3enw+J7rvblj+rsWR/tLP5V/PQxPyxa3vkeWzPz9FYXB1tcavfnWB1YqO8nT50RxWFEU1XzQBjuS821TXmDKuaDxCugIfaWs+3x5X2z/5/JvM3bSnJUr5iDimJ7lOui6d7r6py33hTe4xfXTb5OmKpJU43zesVitMp9Mojy7xAm29RfJpPOUVdSFebWVoQ7R+C417xhjM53PMZjMnzGQyqXSRWZbh+fPnmEwmTpjz83M8evSoGu9+9rOf4Ve/+tWNnQjpo9vQl1N1MN9man1M8T6oq8JuCszIBU9q/LaAb1dKQi1OinsTuZtaX4UWLqFylnGKosByuawBYjxeaAPdRlnZtN7pnHiaHOUXSiGwLkWJ0GSyTFU0+Bb9IYoBCKGwTRUgbairiTnkxtuTdJeKSd4G5aYlprSnsBxc42Bbv9/HarXCfO7efeNrazFwiMsZa6+hstA2ainEj1inY4k5KCvzxy2qObDK+x4dHzcajZDnOU5OTjCfz2vHmWvl3mbs5ZSqGI/xkPnO8xzj8RjHx8d4/PgxHj9+jCdPnuDi4qI6UiUmT4xiivU2PH3pSN5a+9PqY9e5NRReaw++o/9JZt9JAD6FSqosu9A+F62+OTc0z8ZAmRDvttSGVwqo5JsHUpVSsbA8jG8u5e58/NR4afNLOY/kePfdMXpba8IPPzzCw4dD9Hq5AxKQst3mu+ISyKlrxURgAPnJ+BxYsHm1v67i1SqF/XfJWhlL3nztaO9PLJ8zAMU2Tnnno/zNsoK5lfIeHKACZzcb4Pi4XwG8y2VRKYKzLKvuF6J5jc9fZX66U/SF2n3TuaDX66kfO7VRlMYUmsF5NwPG74wxejDC8MEQ/aMSjCUQtgbEbp/pntKKZwYVhHXSdMAiz5xHfAJ5Vfl5rGqDJLuL7137Ndtw0m37b7AdY7bPyGABVrKQLVgYIyxijXEsYw2275mxx0JTny1MBY5TPG4pqxlXkqUzDGAyY3kYwKwM+sf98ghkY1AsC5i1uw5Xi9PTzlL2/aG5r/zPkedmuy4ty4c+5jDG3iGrfRdsx1lqx+WHJDZdujPW3o1djmN0TLFBUdAdsPZ4dpLD/UUVjjcoGh+pItx3f0Mkfq6/DUPjr3SvdwY5Pmj1oY0hTTrV/kAV//Cmg5bSzR1jtXBhANZ1c8PTnKnFs3NmqoVsexA2FC8Ewkp5uYwAqo+uJpMVptM15vNNDYTV0/DvmdvuebS9q49HV2vemB7Gl25s7u5CPk0PQiTXFruk10RPFaOm5dmU303QTe+vmpCv/FPb7T7SbkKh9p6ark8G3p9iPHzr7ia635BO1659XD1KqJ5Sy7ONrnpX8pUVuS8WC5yfnzv39mZZhvPz89rHta9fv8bLly+ruJPJpNLV8fas3fu7j1PvfBSrj5hu+aboOuauNun6KCbPrQJjb4I0cOTbQqnAQrl5q1sK7WJNuiut12u8fv26dmfsRx99VLn5rMFkfvZdp/fv30dRFJhMJs7F6hwk0SYsvpEnCl3iLSe529BWJWjE2wx9eSrD3mYKAZIaSVCmaR3J+qdjLp48eYLDw0MAZTt/9OhR1TZOT0/xxRdfNMqXTJMfz0HvEtQkIMq3oJRtVda3TJPHlZTnOQ4PD7Fer3F5eemMXTwdkpvfoSEt3/v9fgVcDgYD3L9/H7PZDL/85S+jY9pt2TRReVHeCYy9f/8+PvjgAwyHQ3z44Yf427/9W3z55Zedpdsln6bAtgZwcRBvV4tvCQpqSheeNt31KHmUYFZp9c8X5ddBMVDjpkiOdbKs295nf5MUa7f8HqtYeN886Crz/f3GNwb3ej3keY75fO7wkscvSZ737w/xb//tJzg+HmA4zNHv5xgMcmQZGAhL/QNA7Q4+P+kKY0BDXEr3rBa+VAxnlRtXFvsUxfy3KPi9srqFrL1XjoAKei+q382m5NXvl+HX6xJ8Xa+LLRi7wWCQ4/Cwh/XaYLUq8OzZDK9e2U33YDCo1qp5nmMymbQay+RcyPsXD7MrybUp3ZOtySz7fUyhFFPUUDhO+TDHh//3hzh478BawvYsEEvAq3MsMeDeEQu4ftt3mSbxKr0zNYwjqwRYMwbURuKmUoplbAWgSoDGiHbiea9ZwG6fqzwUNt2aZWyWVcCuMQywLdg7uz+WAFuY7R6hKI9DNoWxvz2DbL2VIy/TJz5ZL0M+zDE4GWC9WGMz32DxcoHi0j8PU5vW1uS79h++9h0OhxgOh8jzHFdXUxTFeju2WDC0389RFNnWajbfjjvlxx72l98dW76XlrUWdKWj421f5UAsHfkO8UthLfhb5htet9hz/Ne1eJVh9OcMEFa1MtzWBdKa1h92vxRa3tj5Tw/rvoeOKDaKmxuHz4uxdx6H+7vPbcCs1HCa8ls+S7nrstJHUL/+9SVmszVCS3LfGrprwE3Oz22BEd8eRAvn4xXSRWjjX1MdSExmTvwDwtS8tZHhNtFt3be9jfRtKsdd2y31l5R+n7LOifVHyUPTwXc9jt4mevnyJf7rf/2v1Ql1QCnvixcvasYeRVE4VsHGGPU0pNtmKXtHLsX2DF3Rdx6MTSWtMlKVCTG+vgGz2ujuWPEpg7C2CGqrZE5ddIbeF4sFnj59itFohIODg8p9NptVg9dms8H9+/fVM9j5sQldTHjES1rqEihEaXJQTpZ7bAHNj6BtU+cyvSYTKw/jo5Bssck9Je3rorYDK1fYaeBDKB1ZH7G4g8GguhyeAEUAuLq6CvZhjZ9P0a+BsbE2FCKZfmx8lG2C91kJHshxMQT80t2ym82m9jFHan5C43ss3q7ExxlfXzs4OMDDhw+rNkJ1eB3AYJfzERDfUMiwMRmIV1MlK8XTrF3lHE0gmHbMDY/TNfnq9yaUEbHyleP9bdpk+eZibQyNtSXfGiNlHURtPyRPbK3J26UmvzvG5vjRj+7hyZMxTk4GODjoCxA2ZA0L9b0uGk/bF8bPo65Utr9WEeu3iuW8ShDWxs8yaRmrpUXH2JfASGlhY2CtbMv0y2NE88rirdcrkOfAycmApZtV1sd0p1CZTru+kLI+62rsoTlUtlGuZE4dV7XnEI3fGWP0cATkQG/Yw/D+EL1RD3kvLy1f6X5Y372wrP3KZ0cO8e6VNYNrDVtF9/Blbk3yrZJxeWnXe2Ymqx1bHLSMle/Sr2BxDezdsUAJvCKzv1u/6rcQ7syiladrjLXALTMh8t0r5ch75R2ymcmADZD37YcmfZTKwOKkQJZnWE/X5dHGLfdRvjG0SXzX0iGr7oxdr00FrloqUH50Ulq+loCqHZ8AIK8+KuDv7lHuPgtZ4kdHHhMozP3KfFKftu6UBslLfvaZyqsMuy0BljfrRmNvvSHLsBBpuvHsu+VRdyM++vy1D7LNTOo0QmHL8DKMOxeaoLucIzk/+2vDlo/pIKyUr4slnB840MNp+aFn+qjq/HyFq6s1VqsiCYhtMydp8189D+F1oo/kfrcp35T1tQYKh/IiKbbG1/ya8k8th6Zjc1e6p5Q2cEc6heosZT//ttIua33KO9dx8w9tuXVmSjohnXRoH+vjId2lfkIbm0L73esg3nd9RyjzU4u+/vprvH79GkB59PBoNEKv16viGmOqa9RkOhIvaPIRbmyMu+k+cZ06p+tIKyWNWJnvMlbtDMZ2AXa9jdRlR4hNUl00klj63NIsRa4UnnLxxxcyMWB2Op3i5z//OU5OTvD+++9X7m/evMGTJ08AlF+UfPDBBzg5OXHiyiNJU87Ul7JrJAdXurSb/EajUWNluQSZ2pJWpjfdL9sunG8rhRYlGpjZli9QArB0Qfx4PMZ7772H1WpVu7sgxkdb+FhljFU6ccvYJoClL/0QeEBp8UVlnufVscIpFFqEUjmNx+OK33q9rsKl3BUUAnv3QTwt+XGJJuPR0RGOjo6qNkJHSu7jK7vY2JWiBEgFUeU7H0N8PLRwofYXk4MW2QS4cp4Uj+5x0Rbz+2g3WVZaqK1WK+R5rs7X+6Cu1neaIuimSRuvNEVdCrDvm+tSlUo+xV6KAo8/83GLp03j4Hjcx1/+5Ud4//0D9Pt1S1gLAriWsKEmEFImu/HkPOS8CaUvXytyN348MVnC8qM77W95NyNZldnfEpjglrF0FLzZWp8VKIps+16693r0nNV+Nxv6zdHrFXjyJMODBwMsFgXWa4PFopTx4OBg57Yv24uvvTXpZ1rbzrKs+vBLWuOGxmVfuk37/b0f3cOjf/4IvXEPeT9H3veAsBKMJXAvY2UjgVL+zuJwYDCmrHeAXu3oY+1IYgqbQKYycRVxjPXj4GgVbtsvqmc6OtgwMNWYKpx8p2cOxNZAWgh3Ok63EBayWyC24lWIuXprIWtMGSYrsuo4YlOYyo+HMb0S2C02Jfha9Arkw7J9bO5vMPt6hvXUzsm+NXubfpgyFxpTnqhh0+9hs9ls19VkhV8ej17+5+j1AGOKLVBabMsI1dhlrWItSMatZ22/QzV+W2BXt5CV/6Ws7rsLAnOQVo9nw1K5y/tiXTfrV7ecpeqhfFED5H718HIspLoOVllnVE/Hn359zjSqH8973Z235zovLQzxs89yjnXfu6DwGkZ7l/K7fLiMdAz4119P8ebNCinUBoj1Uer6jN6bphdaA7ZdS/h0b13rNPl6IJZvWkeQ/mFXw4Q7+vbQtwljaJIP2ufLNQW3Rr3OU7lixMeqprrEm6CiKLxXfJERR1EU+Pu///vK/fDwEO++++61yHdbyQemp9Db0pd9cqbsY9vquBww1ncMVRMF6h11T10OaDRIymPsgG7qUipqZNpaeCLfUcn0NQoBD0T9fh+ffvopLi4u8MUXX9S+QqE0236JEysPOr6NnjXFFQ9L/rssMmMgtvRrW8dd9+vbPCn7KNR+uwLO+WYlVleDwQCPHj3CYrHAeDzGarXyftnVRibfsUFteGluchPMjxemvkRjk7bIDJU573fr9Rqz2QyDwQCr1ao67lmSHCNC6ezSZ3ftS71eqdB7+fIlDg4OnDo/OTnBu+++i8lkshMQq43ZXY4BWt+RZRqbH3xhNF6+tLT0ZN/TLLQ1HqG5pStFAn24QApdX9721S5T20CT9N/WNSPN3/KeVq1tpSjt5YcDMm6oDZGfbBdSlizL8Omn9/Dxx0fI8wyjUQ8PHgwxGOQVCJvnNj2rWCe3lHKJh4mRVLxKt9Az/5fAA1moau5Z5lrGWjCC7oq1bpuNzWcZJ68s3sgyNs9tHLIyXq8NgPX2+oEFimJZrQF3mWtDawWpAE3pm1oY/uW35O1L27eJjX18QO4H7x7g5JMTHH18VAKxg7wEYHsZkG/n+QzJFrEVb/FcAbCir2lgqtf6Vb4rfloZpRClWasXg8oqtbKKBasTg8qd/zpHD5O7Aqzy44q1f24hK/NZAb+aRawpw5qCxZdyZuw3Y37b/9yUFrKFKZBnOeieWwJ7szzD8NEQvcMeNrMNinWBYlFY/rBzfZs5M1SHul8pWOnHLWRLa1j7UQn5Z+D3VfNxrZxy7IcofDwrxxrqX9wvc8Yk/Z/Hda1lSV4ehviW/Zye4aQPZMo7qjhU7LbvGeZG/jaO5ePOD3YsIRcX/NWqN94NecPzU6zp+Pzr61Pt2Xj9NNC2Dr6WPOScGgZl6+HbUNpc43PT8iHlLuWczzPM5zmm0yvM5wvMZvErtVIVqb58dLHGjelTU3U2uyi1UwHkFP4yfOzjF16+vv1XCsX2ganhm9LbqMe6KdrH3nHXdG+i/uS4k7oup30/fweguvnS4mGa6rViug3ABYNJh5hCbcbXfegNUuqAf+C8WCxwenpaC7PZbGofyPOPUWLUps02KY+u2v2umFEs/L50jk35t5UzVf+jURSMbfN1Q9uKl0qEJvxrm2oR7qYRed+CKlUBndqhfYoY/ss3pDFKHQh27exZljlfwxERGHv//v0KjKW0CIw9OzvDV1995b0PUrMkkvdhhhRIPuJgklTMS37cGq/JwJu6cObkGzB52ilx+VENu4KPmly78OuKV0i5KMtLLjx4eE3B4yszLYzvQxhOw+EQ77zzDq6urjAej50vuwgoSGnHWp75ON+mrlM2cXLzRZZ+QJl/Ot6N8sJlC6XLw2dZhs1mg/l8jjzPsVwuMRqNvPFlf9TaehvFXZfU7/ex2Wzw4sUL3Lt3z/lK8uTkBE+ePKnA2OFw2Jm8Kf02BiikjK8+eVM2Ldp8T3UY6ouh8Y++RiVramOM074ovhwPYhsXmX7Kgo8+TJAfXWhrm6aKlJumXcfxWHzNv2lbkv4cjHWPowzz427aGBPrRxrRmMnjUDy+gfzxj+/jL/7iCfr9EoClY3PpuEtrAUXzXpocIZlTq9WykHOn/eXP3E3+l/m2v8ZY5b4GytrjPAF7PCcd61lavFq30jKWful40ZJPmXivV1rI8jLu9wsYs8J6XeDqao2i2KDXI6CleftNpdAcEBoneJo+q3+au2X8JvOOXCdTvIP3DvDkL55U1rDO3bB5+rHENStZDXzl7hJ81fpA5r773B03AeQ2JW7hWhaUdXfqkR1TzIFXFag1FXNwALYCYoHKyhWFiLf9p/AwKO905b88DRkXCb90RPJWjqJXIDMZCpT3ywKAKQwKlKBs1SZ6GUaPRyhWBeanc2CBEoyl7Ir9V8pYvOs8WqbXgzFFZR1bpoHKMr8EY/PtOFxsP44hwNYd46omF/jl/1vXLSjLwVY9bJmGbgVbvvMxs54ujaN2rJXvbp8nN1sf9KwDtDyvdv1oy5tXpb7mDNUWYBthMwrv033vJurP/dx50Khu2rt1k3F0/32QxtpXDjF5i8JgPs9xdtbD6ekSk8nUu/flfDjJecdtk+F1oI9Sxgq+z/bpijR/HqbJ6Ws8XyG9QwoowfdBmrw+GUL7vFQ9ZIqM+6C3YR+VQjet/96VmugQu8qrb20ca4dyTazx0vw0nRxPW+v3sv9InWVKv/blwTc2+MaiVKxolzG2Ke8ueHH5FotF7W5YANXRxaH4ofroSl/nm8fa8gGajdFN6CbHozblE4rTRf11fmfsrkLFCqgt/64r/qYV9CGKKSA1ufdlKdsVzWYzrNdr/OxnP8OXX37p+C0WC7x8+bKaGPr9vmNFe3V1Vcvzer12lE2ped2l3mMgTwxACPHzLVZuSx12KUdXvGLgiU/RqPWnlEGawmhHGtMzHY16cXGB2WyGPM9xfHzshB0MBjg8PMRyuXRkSgF0fZulFAArlVLKg34JNDCmvHchBLymgFdaepeXl+qXazyeL//XCXLJxRSlR8rvyWSCFy9e4De/+U0V59WrV9XisMuja7Vy2JWfb+yMbRp4/YQALim3lr72LoEF2lSEFBXL5bKqF2OMU/Y8P75ySy1PzvfRo0f44Q9/iKurK0wmE5ydnWE6nVbHFt3W9YiPulB07+IfA6WkOwGxVCf8o4iYDLJ9aptoLYzGg8uoyfn++wf4sz97B/1+jsEgx/e+d4ThsMdAWO1YYp5eNFuVYp6jTDxejIdsqqZStkuFMi+XUrlv32lc4GCFezwehdHe6VhiDiwQCFuGKUFc+58hy4rtLwd+DYBsO5eVxyCTZexmkwPIsNmU89xgUI4ps9kGk0nYmmeX/tHlWMDHR66k0RTLTYjHGb8zxuN/8Rjjd8boDcu7YSuL2AwlEJvVwVjnGfqz04/kO7Y8OCi7dSt/FH4M5K3lXeHhy7OPVPCAH0lcPtSAVv7ugLPbZ/5bHVNs4PCowm/vaKUjgn3grDEMlJX/2faXx4H+W8lOdbpNz+SmtIItDHLk5Tvy6vhjk5dhAMD0tnnvZxg+GKJYFuj1e9jMN1hdrsp0auPDdc2ZGfK8/Mhrs6F7YkvLeQJly2OK6aPOrPqww/23axe6f1YeV8zvi7VgKHd3jyDW/oF6mPqzZgFr1Dj8N+amP1Njqs8t9S5F/ft610NuUzIed+1dzut1P9et5O9zc+fPNIC2SwqxtH6+fNXzTB87FYXBxcUST59OkWUjZNlBZ9fJaHsv7kdEc6DcO4SIr+261M80qbvY/igkEy/jUPyQ/sC37wOsPkSCT10CK991+i6V477yet1lqOki2/TfWJ/1ub9t+gSg2zri+wR6puupJNEYRqTpE7uWzyfvrunc5rGiqzlh1/3qPqhzMPbbSuoGuUHlNBkgm8gg00jlqy0wJZij8WuT5zaLRsljuVxiuVziyy+/xPPnz2txuGL06OioOprUGKPe7Se/4okp34nIUsk32KbUUeidL5pDba6JxXoIEEhRSPvefXRd4FUbSpEnRcnvKwsJ+KT6U32Tsn8+n1fWnRz0IR6DwaCyJOdtpm3+fEegtiWrGKrnl343m0017lAfDpVr07ZEZTqbzZy0QmF9+ZB58FETGUMbVJk+WUFfXFzgm2++qer94uICi8UiqExoSl2UQyrfVH6poGYsDdl3+aZflp+vPEuQZeOE00A3riBtS7zNHh8f4wc/+AFev36Nfr+PyWRyoxsmH9AYC38bqOlcxtdIbddAPE2+1vCNkdqYJ5VUXKFFCvpHj8b45//8EUajXgXCkjI/yygvcJ6tvICKIkGGccPHFeQ8D3V/m2/LyyqP+dGc5bstC2k9ZgFX/mv5uO9cJi5HUegZMKY8RpTk5PKWwGsBayJI9z4abDblsZ+9nh1/plM6VcVfVqnE22lMCaq139BaX7rTfM0BrdQ+IcfWLMuAHjC8P8SDP32AfLC1iM2zyiqWg7HIt2Ant+YmAK9CeMowGTt+O2QV68ijWcXKMIqbz6pWdqUs0Lequ2B5WVLRZ8q7Udz579bfGFNZ2FZYLoGthoGpXOZC/BJPZv1KcQkYpTtfnV+URxUbU76zruHIVYHEsixINvIqAPTK9xw5iu1Zy5nJgA3KY63zDDgEigFdZosSjKWyiKyT9jFPlTzLO2FprCqX3GRhT/2otJDdbELjggRB+ThsgVi617qct8oxi35RO9K3bjVLYyA/uhggK1d+VHGZtmxEZTgensJKy1mSwbBndx7gwKrlk1XvvCxIjrp7jGTA+MAcGrvrfvW7WPk7zXfSz4YJHz1s41k3O4f64+za3FPmL56HurzWj+evlDNDvz/Y9pcN5nOD58+vcHAAHB31GwGFRL51uW8foJEzFwTC8nBy7pNhQpSqiwnth1LyI9+1/UxqevxdlrkWzreGaUNtdFBt6uGOuqVY+9o3tUlPa2uhcUEbi2LytBmbeDwtTmzdrsXtom+G5L2NxMu93GP3amWsWTHfhrGiCxluU/3sS5ZddZtd0LWCsV0pizVqqvRsEtZ3l6JPqUHydEkp/HwLTI14OFIyamBsShr/f/b+rFmSHEkPBT+YmbufLU5ERkQulbV0VnU3m0VWD9l1eckmb8tl81J4hQ8UuSIzwtd5mF8y/2N+AV+GMq8zIqSQFMpQhGQ3e6vqbWrLrsyszNjP5sfdzTAPMAUUagAMZm7ux09kaMgJc8OiUOyAfqbALknKf3FxgYuLi96w8/nc/l4sFqiqCtfX15NceE51v16vg0cWUJjz83MLIo9NJ7aoDFknjOGfeuc0No19t5cx1KfApPrOGbCH9v++srm9vcXl5SW++OIL26Zfv36N9XqNo6MjPH361AJyU9AuFlsp4guZ5XJp3bYh6hNXV1e9dcbTP9R2Sh+S0Bj2k5/8xN4VudlsvLurp6RY380F3w6RUm2A5sHjY/PFPY3t3JJdfrATA8f476naVVEUmM/nePDgAZRS+Oqrryz/2D3L72gchepOAqo58QF06iY2xoYUYLzNcd5Syae1xuPHC/zLf/ktPH68wNnZDGVZMCCW5jH+BCCA1FhT9deLvpuM09fcwwppmXfycwAAhTP59ZXV7q8LxPrAof9OgIUBTB0oQVazTWP86Y8DCO7eWRPe/CkURYO61i2oYuLQc7Mxz9msxGJR4va2xnJZY7lssF4fxngq1zp0bDv50Xvs2gYKF3KXNDuf4aP/9SPMH81RLkqoUhmr2MIAd1EQVgCt/Dd/SuBVKstiYKoH7AbCSuCX8/DCWodkMbi0iThYIaxhATY+cYvZ1p/7eYBn+9uCqKF30T+g4FvK0nzXgrAyrGcRS/nQzL1xv4mvF0e1foWCrrWXlwYNVKHQKHNccaPau2PbI3h12T5bcLiozP3CxbxAMSuwulhh9XKFo6MjzGYzexILHfUe+1BvSjJtkq5BaNrxGahrY3GvdQOlgKoyH9dUVdc61pxwAPuuNUDgKwGvSql2XDLjGR3JLj9EMfOCqSh3dzj9OUta391/p9/y6bvxOcsPm/s75z3mFvfnL7GxSiqx0/xDYbQAWmUY9zt8NLEEKp2/nC9Dbl0ANCXrtsRljqWRypeb3zVOTh7gX/7L/yuUmuFHP/oRFouf4ac//f+iaRpcXFxkrXlTIITpJz4PqRvj7/xD/G10fcR3iF4vpuvJ1dtssw9Jycv9YvmJrWPlOjkUb1u6j/vUd3Q/KDS28A92y7LEbDaz/qvVqnOi0hT6gRQekQukxnhwumuw/L7QuzHnHQ2lt9oyNmegyBl8cr5u6UsjtUgZKtNUFEvnLsFWKYP077unDYAFKIjoS5ahE02MiE9d197EKheTdORsLkCeSivXnfPP+appCAA7Jbiwb9BvKiK5pwCuc9oj3av65s0bu6i7urqy7W42m3Xus0ylwfvBVPU5ZGxLheNjrdwQD5XHKLv8Y8hjfTFVn1NtZseSTH+z2VglBL8vksaa3I8fxoTpW8j3hRnKcwifWJi++YWHobI7PT1F0zRYrVbZR3HuciyTvOkYfjouN2due0c+jS2voYBsB8hBfM3X9/GDdKP+XhQKZ2czPH68wCefnOHkpMJsVjBrWAnGAoCvZG9/RRXZLmxIoe679RGFo2z59wP6VrDmqey71iSns4olFEcpWEsw/gScRRh/N5Zf2ubBHAHqZNPaHdXpyp3k8EENPx65mSdZmFWVsZydz0tW32jnKo261tj2e4rQfBVrV7ltjStb6XfuOB4ag8uj0twJWxSYn89x+s1TVKeVAc8K2D8PTJX9SMFZodLvFrANgrIhkJXcuIUs/Lg2TZFeUBY4v1QZZ5HyAVbLWxt37mYBVwg/GUcph4+07kH8SQG67T/cQpbzJotX6DYslPW3z8KBttadWclq6E75QwEoWp4tf2mFqwr/uGUAxloWcMdal4ZhMS+s/PWqhqqMxXTshINtABaKl+p35l1B69qOMVrThx0mTfqII2UhK8clOZYr29Z9y1h698dJeG68kdAYRe5mbKbx180pfsOCFzY+nrs8U1j3W2Y61I9idZTuc26uSfGIx+sPE5qvY+9xa1neBn3AsgvAxkDNXLm3oS7/HBDWhJPzLblprbFaNZjNgOPj96DUHEVxiqI4QllWaJo1NpvNTte9qfEhdNVQblw5R/v1HF8bxojPy32UW15D9Zs5csbkaJpmsg9hhqxDQrJwPtvOBV93is2Fd1Ge+0yT98WiKLxjbeUJjSnZhupIY+Xbtw+Q4WIU4it59tXxkPQPqd/FdOqhMO8+INkt9elrx6wJDqG8PTC2D3Scig5JcdinJAMQ/BKcwo8Byyhu7ELusZSjcInVMR2Jyu9aPUSazWbBPI21Ph1CcsG7Xq8tKEZgj6Sp7jTJpdSiJ9XeQsBrCmQ7pD48BU2dn5zNUUypyUG2qjJHMf3oRz/ywkmrFP713VAaUvfbUs5ihn7nLNzk+B3amG4DvBwq0d0Vu6yrfdDQDQfFIYotgEPtg4eLtZvFYoEf/vCHUErh5z//OV69eoVPP/0UZVnadQDnQWN+zLpmm7qhNQLxWK1WeP36tU33bRuDp6a4MrwbLvQuw1JdpCzpY8q00L1YoXBjlFnHxyX+9b/+Lj744Bjn53OUpbL3wypF95fyPgEgCMZ2+bsw3fDdss0SHYBUyvr3wsaevhu3kHW/nZLfKfgJPKX2QO/+nYpaxCWLMncUJ1mZmbDk130WhQOFy9IHP8jSrapMHVVVgcWiwGy2we1tgzdvNths3Ji2TR/fdm6gtet8PvfkoFMZZFpEoT5n+1Sh8ME/+gAnH5+gOq5QzkrMHszs/bBKGbCMH08sgVXDSLgptp7iv8H8Oegq5gfLt3DhZPzgb5tJejh/vxD4TzEnSfBGB8JpF84eOYy2jZDlKzvq15a3sID13Phdslo8W4tVe2es7TfMQrbR3r2y8j5ZzyKWflMYXi5aPJu2zZDVLMkMbe6IrdunYu/tbwKSAVhAuamMRa2qFKrTCusXa1xdXHX6xxTzae66U6kCZWnObG4a2PEHoPGtaa3yVXuPtbKnHJBlLL8D3Pxp++6OKXa/uYUsvdO4zo83dopkUyHc38jOLWal9SyP4/yNXzeMKw8TNuzu4kh3v0zpV3/YqWgIECnduusO+TtkJdsPulK4OG1bIDG9QJ9bSHbnrrWbszYbjb/8y1e4unqOP/7j/zsAheVyOeoksJTiNhQutP5z10CU9vSc1WqFq6urZBp91AfESvlC+5a+OEPLKyd8bH/VxzcGDIXG4rvc38hx/D7vse+KttXBvA37WxozODibOx714Qt9H2jkyjeWhu5PcvGJQ6t3XpZ1XXv62b54h5aXt4GGAPoxOsS62btl7NAC2MfReylAdqoK2+Vk3jfIhRY+Uib6nTp6bFv5chePufzoSUojngd5bCuBXDSQcgVDjIYsHqcGLlOgVWqhGAMfQry2WWQOXSjIDc4u+kNOWYfKKhVnG6ByyLgS6yPkLj82kG1g6KYolG5M1hDJfhFrp7E2l2qnqfhDSNZvaJMt00gtiHMUdzEgZyz1LTzGbLSHLFhyaNu83sWiyAMIRFs+PT1FWZY4OzvDzc2NDcPvC+dtK9Wu+saWvrzz9NbrNV6+fGk3d+v1OtgPd1Geqblwl2ltE3YbGUPjM19fpMb2HJ59Y6bW4WPdOB+lFJ4+PcJ7783xwQfHePx4gapylouhO2L9+DxdIKTAzo/rx+8vC2kR5Pg6YNRZxLqyAZwyWAV+d48Q5n7cIrYoOAhMbdspiJWCPboToOOI4QEaIcU4PwLUPM17WSoABcpSQ+sCVaXRNCYQAbDzubLvsTUCJwuQJdb7Mrwry+7clmqX5Cfvyw6FJ948neqswux0hsXjBRbvLVAdVw6ELRwQiwIIga0dq1QRJvg7BKzyspRhAu88vVhcV1DoWqb2kARnvWYNOAvS0J2q0iKW+g93V6rDL2jJKp9KO2C0CVjfFm27aVw5eHfFRnhyq1mbdcVkbS1jbVnSewHDu9D+/bOFy7M9vrp0zwIFdNUCuxqoFzXUrYJeMbB3wHg+xVzo+o2rbDf2+JayLo7zN2OLagFTG8LGMTK5D0koPo1JND66Pw7O6nbsJH7uKGSX19Ddsc7P5MeN3yQ7jeHOshaMv4tL+SHiYzK9yznLufl8tK3jTjUEyM9DilLNpOuXf19syD00z/TxiOd3ur13bhn4eyz+dEDsatVgtWpQ1+bv5qbGzc0Gy6W5jqNpGpRlifl8PmpfyOei0D6eu/N5TWuNqqrseoxOpgFgwdhu3tN77FhYKYt0k/H61gg8D1Os2UP7ndhY2FcGfXvvfdOY9HN0AvukvrXfVDKG9ptT8M9Z81JaQ3ndBdF4Emsnqf4Q8ufUp0PL0bXH9hQh3rltfeyenI9Vh0TbyBMbw3edx1xd+L70OLtOayhtq5cbE7aPesFYvqHOHQD7wsWOpBg6mOyCUgWbs0AChi26pqQYEBBTLEqgu2kaq3wGEPwCJDaAD5UxJlsf0f2IpJAmfvxeq5ubGy8fRLLd9R2NIpWnObLJsEPrXC52+wbvvvro+5hh3wPkLgflXU8sgANH+Bdusc1Un2wxJaZc9ChlLPZ4GOoH24JxueM6l4eORwVcf5RhYnGHLkb6NhapfhGLw/1kPYbK/9AWEkRjPlQ6xHxMSTnrD1kGVVVBKYXVaoXNZoPj42PM53O89957dh4hJdBqtbLzIk+LLB+JUoqJmBwhomONZrMZXr9+jT/4gz+wfnS8FwcH9zG27roNDeG/rSyhOTvVhvj4QGMHjcd1XaMoCm985MBqSvlHdTmk/ZYl8K/+1Xfwve+d4+Sk8ixinbLePH2lPLWVMG8X1v3m7y6s/RXlJ6lfIe3/JkVtyC30Tn9O+e+7EwhBv2NFrTtAgwMsjAVa0QK97t5Fw7uAtJYFyFLW3R1blgbYNceYNe0cb46WXq0avHixQuYH2K28aUA2tLdKrUlCe4mqqjqnwRD1zflN0+DJ/+kJHn3/EapTYxFbzMyxxEVpjpIli1illH0nANYb3xS8MEopC9op+KCsF4cBp1F+IXfAj0+/W3fzcLy5u3tVUT9r5RoCSbSLS8cWW37auRHgCQ1rRWrdbUeB59+xiEXYQtZba1F8rQ0Iyy1jFUsbCFvKEnELWd2WSeM/5V6oUY1pL3VhgGLlwheqgG60AV5bWaylbN3KRsD/BwqzhzMsP19ic+WOOh2zJp1OuW34bDb8zljyozU/fWii2nW4Zpay7m7w7tH03GqWW8OCvcsnQEAsAb7S37i5p289C6BjNevceNywm0tfusu4cl4Kh8ulcTqa1DzCecbnv/BdsX0g5hiZpqZuOv1HL9M7nx+//HKJzz+/xXq9Rl3XqKo5Tk7mmM1m0Frj+voaWrvrZ8b0vdicGHJXylwLo7XGgwcP7OlTs9kMjx8/xps3b/Dq1atgOiGQMVde6rM0b8bk50cl5/DOlaNvDTpGByj1jWYsKqwOTuoy+F592zH2bd/zcurTX0+dzj73hUSHAtb1yUEfLS6XS+smjynOWYP3hQ/F36aMYjJxnind+depv+2Lhuogc8LuU49zn9rEPvVPnHqPKR5DQxvC0MVKikK8coGssQ04lFbfFy+56aV49MkS4yPl48QB2NQRu7vqXKS4zAlHiztZ57lg+C7zEOPd1w5iAF0qfCofU07KsY8lZDjZr/o+srirgbpvMxZ654Bdn9x9Zc/LLyesHKv4gjjGI5THMW0itUDkvMeSlD82Lvf1deKTqttQWYaAWk67AGSn4nfXC51dlU1uOx0ynoT8JCC/2WxQliVms5kHrPEwfXMvb0t9Hw7kktb+EeahcZeHPdSPCKamsfnctj74+Etgvfw4hYOxsbZHiqjUOpj8nj49wocfHrdH3RZ48uQIJyeVZxGrFLyn4en/dnkAuMKb3um3jMP9fR65ZUb5cXxJuex+E0M+P7p331/+5mF052mO7nQgA7eILQpaf5J8DgBRChaUIDe6Y5b+iI+veHbHitLdse5ZQOsGWivMZgpAgfncrH2Pjkqs1w1Wq65SV1Kq/W8zjpIbtWf+AVhozgyNw0opzM5nmD+e4+jpkT2amKxhrRUsO5q4A8S2QKcHuLLji5VivyHcWzmCICzgLCkLP6wtT9WNI3nZcAKg9QsjUfAUTQcCKbbm4VaA3DK2DeOlr104aXkrrVVlHBkuxoeDod67ieCsYHl6yn9yHqpQ7q7YxsW3lq/UV5W29UVpgbaMrbWsrc/GPQtdGLlmJqPVaQUooL6u7dHGd0G82yhlMmDujKVx1t33Cig0DVoLfxfXjat8LHMfirgwzvLVf/fd3RxC91jTeMnnCf83He1O47gbIygs9SMt5IEN5/LjLGh5GVEckqNNuVMOftl2570pyNVbaNxMvYeBV/fu55nPfWm+hvax5AtPKfkgrNYa67XGmzcrW0fX1zU2mwZ1TdcH0Dzp1lShfVu+zOG9YsxN7s+5+2az8fRjOXLk6qmkPNv474JieqEcPVXOWr0zd08oa4im0nsfil7rHRmaan+X0nnl6GtDbT61x4vpu3bZnsaU1TbyDElvjH57131v6L4qd8wfMo/E5LoL2vUHEkOxsilpXx9/ZIOxh/I1SopylFmchg5wIeuGEM8+xdoYiil/pxwQueJlCv5TUGzQG2oVlrsQnJr6AE3pN7Sf9bV1Xn67zDtfPEswPfTV4yFQrFxC4GvIj6ivDlKbldz64Up9GZ+/p2Tdtv6pHskyBoAFrca0XUk542sf5S6OQ4tcWb5yPLwrettBtRS4uauxgtc/3Qd7e3sLrc0X+EoZa3T6Gt4BXcMWhUqpURbsMaL2GfvYgLt9XQDZbfKXu1ZMxS2KArPZDOfn57i+vsbFxYU3tjdNY++6525kGUDto65r2x7lOpLo+99/hH/2zz7GYlFiNitQVYV3j2D4aGJpneQrp3l4ypeMy4vJn3OyiqpDvjLagaq+AjoGcnZBT3MnLPdzwIXvbmTm7ql0OTjB3WJh6WmmDWMlS4plrY0FkNZg1mauvM0dksB8bvxub2s8f77uKLj9cgyvLXIpJ956ve7cedwHAPOx8uzXzvD+776PcmGsYYuqMOArHU+cuh+2BevoN41/1iKWhZXgawd4tRWETlzvCZYuhJvgY/klgNjc8YlbvnrxCYR0CKsFN62lrPbfrRUs+i1juR//s/7it/1T8NOT74wvgakeMCvBZ9afbXk3XX7W+tUrJLiyb4BCF8YyFjCWsYB5b5d3qlRYvL9AdVvh6udXNsxdz5UGjAXW6007RhT2aGGzt9Ji7KIPRuh+Wdg7ZE14gN8RS/MD+dNd1tzP/XHLVvfhih8G1g3MEtZ3l3MNz2/4mfs79N7nPjQMEAMfrW80jHTr7kXk734LWSJfdvfSN6T35TlvKglb5obyQ7/dvGzmwcvLNf7yLy+gtfJONqPf1Be5ZVk3L3l7hFg4vs6iNKXFqVLKrs1MXsweYbVaoaoqeyrKrmlbUGgMb+4v98fSj8sTk1PuoXhcO7cr5e1zDkVfdB/p67D/u0uSfY/2+6kyD32kEPLbRv+xrd43pDObmu4KSN03vW35eUe7oaw7Y6fuiKGvRPYx4eZ8sdIXd4qBbSh1lAkBv6Hp5Sye9vVVToj4wldOXrlx7wMNAWtjcUILAs4rNtkPSUPyjYH2tJCWi2nyl8DEXdYVlznW3siNx8nZuMh0hvZJ7sbrUZZ7Ls+hi7OQLBxQqOu6cy9zLqU2b33tbgrqWwRyf7Jo43KFwLUx/XgIDeF3CH2KKFfu3DGhb84eOlcA/oc9m80GX3zxBY6OjtA0Dd68eWPDrNfrzmkMobRo/Asdw7XNOoLHCymRYvK8o90Qlflms8H5+Tl+4zd+A59//jlev36NsiyTH5dwMJaOyeY8ZbynT4/wW7/1CN/73rkHxJalO57SKbtI6T0ehHXy+HntKs352B0vqw52wu6MJaCyDWl5pe6MJX8O5BLw6RS/DlAYc3csQPfE0tqFW8O6tMhd5pOs0lw+nIUsPQ061LR3xzaYzQyAu1iUUAo4PW2wXmvc3nY/Ckr18dCcOmTdJf1J4Rwbf+W41jQNFo8WePi9hzj51gnKeWlAWDoqVjFL2NbaNQiM0jNguRoEVCHceF5YvBQIS++huPa3AF85WOsKMVCuAetWz09ZTxeOLAm1S9MCloxXyHLV1pcWbgSMcj8uN1mmauWBqd5RwkR0d2sj3iHcuZ/qvms4q9i2W1hA1cph0Fh3Z6wkSreEtbYFDDjboDHWsVWbXgvAzt+bo17WWL9Zbz0/j6GQApbaqdb0QYkW98Q6INWBsuiMffxDEJ4l8ucALIVxd13zpzyyneTl79LqNvzkc4orbx7OjMtcXj7fhO7SDZWnnFNCYccsk1Jxwnu40Lvu+Et5+2Tj/rJMgoNPRJ5+SsvTzZ8fnuZLwMynm43G8+e3uL6u2/nVv9phtVrZ+SZn79y3P0yBg6F4PE3a/9V17V1R1DSNtY4d8sFuCIzs288M1QlNCcJKOUjeqXSaEvTe1T7m67Qf4m3qEHWhh7xXDenL+qhPXyFPm+wDOlO6yFicHLlC493Q/h/iu2uaqn1MrUecksbqh8eEITrEsWEI7Ur+u2ofg44p3uWXDKkN/tB4IcpRXIyphDHATIpygbPUIN1XlkO+eskdgEM8UsBKKkxKptQkeVcbaKB/UU1h+ig3TKgcd/3RBHeP3WWilEJVVXZzFTq2MUZDPo6YgmIbGGkdGerDOWNGzqIzBZjLtOXHEaE2IOUaO7aF2tN8PreWsKvVqgPGxtol9+d/3D81ZsXKLVQG/O5FySdEMTCLZCJe8uMCeWzWEEX3rmmqRfMu8pG7weDtYsh8NSQMtR8OlG42G3z66af2iOLLy0sA5vj+1WoVTEPKTX8ckAuNI2Mpd60UUvrcdds8JOpT0nFKzQF1XeP09BR/9+/+XQDAX/7lX9o5kAAsyYPaA40vNF9SG5TpffTRMf7Fv/gmFosCs1lpQVh+R6yRiRTe1H8AMMtY5xayonVx/Pjdd/m7dQmWp1Ssm3w6mQgYIHfnRmUmj7LkoCyBr85yy6E9BLR2jxSmoz4N2OHSIJl8ENb9NrL5oG+sS5UlpcMtZM3xxEo10LpFnFBYGU34BnVt6rhpNG5u6iAYS7Lw30P6d2x9Eps7N5tNVGEaAmNnj2Z4+o+eGiB2zixi2V8HcOXv7RG1fEzlxwpbWSisgieflZO7c/7SjYVNWcN2gFdya393CzpS/iEPbT3DbvK3EmE0nNWpFuF0ID6FV8r5a/iAqkIXaC1YWAJowd6BDiirG2YV234AoRrlAFbtjim27Yn6NJctRSLPhTb3yza6QYEWkDXoP/RcGwvZJwtsbjZYX6xN+nuaI1Pzj1tvmqPM/Yw7S1a0Fop8uqCxzcwrQAiU9Z/+fbEOcOmGMfL5f4ADbt18Ef+dfrpG7s81roHJYvPfu2Btt3yT3r2UWsY5Px1w64bhfrnLw5D83bh8TZrHN1eGcH66+aV5mf+ua43b2xp/8zc3WK0MSEFgLI3H6/UaEsRweYlnZoxurW/+pPUZrclIJjo9Z8gaMuYX0xHm7mFTvEJpjtGr8rIZouuLpdW3d5JhDnHfcoigDhCu60Mqv0MtN05j9bmhttvXB2Jxc9LbF8XW/UNp33lIjVtDdLchvjl83mbKHZd3NX4P4XmIfUpSlmUscFhC3wXFFHRDgcE+Rd8Q8HNfDXwIuLFtWveVcvNyl3kemvYYoFG2X7L+yWnX+y6b2GIqtiGQ8WJ5jlHKvw/IzOHRFy6330qQSvIgpeyDBw+w2WwsSKW1RlVV9svhEIX4pTZtOXLnAFF9FJKLrNZmsxmOjo6s3+vXr7Fer4ObZor7No1t+6Ahc0pf+ab6ZQzYB9xHGK9evbIK0c1mYy0Xc+b6XCBvn0TpN03jHYH7jrYjOp6dvxOoSgArjaGbzQaA39aoHgi0n81mKIoCL1++RF3XKMsS7723wO/+7gf46KNjaxFblopZxHYBVaf87lrG+or0FBgbit9tu3lNmebSbjwOsJI7Kf/N04Gt9NuEc4AoIS8mLrlzwNS5+dawvuLYyaNaYNSBsAQKU9jQX6hcKD8GIHHzqlIEshhA1oAlRWvtVmA+N5ZwdW36a12bY4uXS/cRUGwczBlf+pSnPigzbKyYnc3w/m+/j+Onx+Zo4qpwVrEEwhZwQKsAYT3L1xj4qrrvHcvYhLVtyM2WC2/rst0T3/Z3x427Bwue/Q4Va8hfsXf5m4fRgSeFleG4e/tHlrBaMzCX/opueI8vD8N5MytXfverBWYLZnGLNkzDjiluZeaArWGvg/UAtGmV7KnaeMoccUzArNYautAoFyWggKMPj1Df1Ni83sha2Tt1FYgKdW2sZMtSB8BPZymL9ujiotCoazcG0binlDvKmOIQeEvjKoXlcdy8wI8j9q1z/fnHf4bcQs+wmzzOMVRmeW4pcvPSsHghMDQMWobCxxJTQflDsqXyuc1SLxTXzbndMDTf8t9aA7/61Q2urjbtWkhjvXYfqfF7yFMA565I7uNS+4bQMb1D06I5lf/JtPr2FLlp0XPb63a2qZNt9hqpPde+9jC8HEP6oEOmt1EPcQj71xS4M7bMU3HvOr/3mULlmqNTzAUav650HzCP+0RRy9jQQmTIlxxDgYd90BToeEqZyym2qBwyKKTKty/u2EEiJPeUdTUEpH7baOiEkKIpvsxJgRR9PPsAKN52eZtKbQqGftgQi7vtgobLK3lJK98hMueAQn1gNS/XnDyPJblp5PwJ1Dk7O8PNzY13p09s3Mttr0OAq1SblWPnkE0UD0sgycnJiS2Py8tLr0zedvA1p43valGa2gDH+mifPBJwIDfiQ9awgLsPlPe3vj7K05li8x77gKFPFi5TbNw9hI3tfaDQWoyDrQS+8nrhdSOtPAiMbZoGVVWhqiocHR2hLEu8evWqHXuA8/MZ/qf/6SlOTirM5+5+2IKBU6RcN+nSn3tvc2D9KKx8OoW5VKiHleAUNr8MQ/fM8fg64M77Kg/jI1GON0ejwH6bp7SGdUp+zcrNWdkSIOssadHyIT/YuxptLjRYWTurMbJ0LUtl37Wmp0GszL2xxoIIAGYzgybVdYOm0Vgu0/dRpSh3rJDKx9S6tbNXKBSq0wqPv/8Y1WmFYuaDsCGLWPnHwVIJvHrgrLSGVfABWMoPixO0nhWyWB5g8om8cnDWukmLWecRKWzxLsEbpQENH3TUXSBSa/fO/bzf7MjhqCwcVAGzYhWCdtKgsmsYr/ZdF9odKdyCsxRHK2cZqxRLiwBZaSHLu37RArSA40/ALy/L9qnaPocSFtAtmgIaGnqmAQXMH82xqTbYvNnEy2mHJNuXv8b3P0Qhoo9K5DhHgCwdG0xjkolvnlrzo9T93zKOScvF5/MMpUHjMB9H+RxCcks5/HnGyev83Ic2rny65cbnB57HcFl3XALh5borzCvl3127xeN3ZeLzZRiY7ePbN+TnLv0kAMvjuqcPwtLx2k2j8fLlCi9ehK+1Ce2j+/bwY+bA0Lpf8urT76X24DkUAmOJV86+Qu4nYrJLOXPX+WMAiCFxYmuKHDnvYo/N1x6SDknOHJpSTzGGV67uHOi2d+nepxuXcYbofQDfAGloH992jBpbRzkAYy7tow3vMo2UziS3HMbEOVQ61DFp13Sf6m0ry9hdK6FJwWUWdOO+7OpTyuZW1pC7IWKT99Q0djE4Jp13dBiUUoz1Uaq/DlHK7wOEkbQvwIvGGqXUoK9gpSKlr4xC5d23wUqVQV/cMUQL0vV6DQB4+PAhFosFAOCDDz7AP/pH/wifffYZ/uIv/sKm94tf/AJv3rzBfD737ssMHb25TVseQyFAOFSeSil7X1FZlvaPaDabYTabBfm+o3GUWvTG+sqYzWBsHUP8lFKYz+cd/z65Qzz7iB9hPJT4GBM7Ml5uLsmNPjIgy2+6D/cdDSN+BPFqtcLz589xdXUFwM0j9MfHQiKttb17e7PZ4Pj4GIvFAh999BFmM41/8A+O8eTJHKens9YitkBRgB1N3LWGlSAs+QHhY4kpHil7u7+dG+dLlNsFCcTk4bmy35Rj92hMd98qAAJtmBKYFL/uCODu0cWmLsifysL5g4GvBErwLtlNz897qAzIzViyUZ5IEdu0/gZBUso9CfQlENjI4xSYZQnc3GisVo0dF3nfvguiNlcelfjwdz/E4skCs7MZihk7mlhYxVqQlNosf/IPDQSAysNGQdj2PcS/kxZX7rF3CcZyoLUDupJ/+ztcSIkC1OEwSit7X6wNp0ScmJt88jSkO/0p95ssZKPWsDwchIwyD9CeDEorNGiMX2sZS0cray3AWyGrBZ55Oqr/WcAAr3RMMZWrLtq5v72rVikFfAxsLjdYv979nCjXpPx6F7kuIaprY/FaFA2KQqGqaD4w40x32+IqvWn88ZUDtpQOt5Kld4DcYf0AGbcLyrq5xK233biprb97h3ADAMfL5oj1FR4v5N8pjaDfkH10jp8MFNt3x/n6coY+ZHJ8Y/nNnRLicnf58LD+3Kjt89WrNX71q6Vt31dXYWtzWgOv12u7BqX99z73gzF3uc8Yo4MI7XP7ABsZpvtxRjoPY2gqECjFp0+HQfvuHNo1UOLGQx+Y2wZsu0uaUt5d531f+r5dpJ/bPvYxxr2jd/SO7gdlg7Ex6vvCbBtlZSp+jI8M3zfgjfnKS1Iqjkw/ZwEYipeKOzWNWWzkfjmWq2wfm+ZQismzDb9dLCbvAgAdSrv8kmifCzPZX6cYq0IbsqnqdJftgSuCyIILAB48eIDf+I3fwGw2w8uXL60cX3zxhd1QxcDs0OY21Q/HgLY542lIHh5fKYXFYmH/iOhYUaLQcc5DqG/z+rZSai7MHUdzx5sh83zszujQ2iL2zuNNASTnUoxvSJlA7lLuobLtWilySBSqY6XMnWKvXr3Czc2N58f/JHE/AmmLosDDhyc4O1P49V8/w4MH7mhiY2UZA2J9BbY/xnbD9QOyfPyFx6v91fHLI+oTMq5ElMgt/O4Doq79GZn5EccOWOjKa8JSHHpaX1uODgwgi1heXuRPIC4HJ+goUH4sqANZ3VHK5MatZumptUJVFZjNNBaLEut1jc1Goa7jimLZx3c9BimlUJQFTr5xgsXjRdAiFoUpcgI+Q38SZJVuEkztBVQDcTpuEPw48KrE75avefjvNs0Y8hpy1l13Dm5yXtYSVLgROKlVwCqWufP40vI19pRySf4a2isz7zhheufl1brZsiT5FEtbCRkEX28+5n6NiwsF/3jkhslFQHJhfpPVrKoUChQoT0s0mwa4gA8Ib0Gh/ibXB3IuluHIj397ZT7G4ICnG2Nc4wo0MtAHLCEwlcaw7vguw5kxL2wRS2H5mGjKwfFyYyvviy6Oybo/zpp8uzjuN9+3cZlleW4z7oXm8fx4cv7x57E43/hQ7c+HnHefLCkK5akPhN1sGmht5sfr6w1evXLX15g8hE+iAdzJIVOsIVP7xaHzXky/mROvT/eZs24O6Q5T4XN0ENuW8dC9dA4/Tjk6ub68TLW28dYdA9PY9X5oG73BmLhD42zb3lJz5tg9quTTl/6Q8DnpDvX/utOU+5QxdTgkzr4+IBgz/g/FGPrS79OF5aa5DQ2tmzH899k/k2DsLgpzCLgxRo5YAcYKlVsqxCbdsZTDK6Wo20VDuEuFaUr5fp8mpdSC4D7nY8ziicclpXIKiOjjdUgKfdogpiziYzLnLIZTAFFIsSrriftP3Ye4bMfHxyiKAp9//rl1Pz09xcnJCZ4+fYpPPvnExvnrv/5rKKVQVZX9yp/KMWcCJz6xvKaIj99cfgmuyd9KKXtfI+AseU9PT/Hbv/3bOD8/x9OnT228//Af/gOePXtmy+X2Nnz81jZ038bEKWjKvs+BmD7+IaBiCnnquraWkSQL8SRrVLJO3Sa9vjzy8YHSWq1WaJoGt7e39ijmMR8VjN0cvw1Ed2XP53O8efMG//7f/3vrVte1tfSQSkhyI6J2YupkjR/+8BTvvz/Do0cVZrMCs1kBpWAtY5WiY3Hd0ynPadx07wS4umOJeVxYfxNVWiX51qzhag4foWiyKpXQzvrVheEKeG4dqzvv3FK2qxB2d8WavPkghNYOMOV/XFZzhDFYOflWszJ/nAfFc2VZ2GOJjYUszU/sgsz2t9ZFWwZ0TGfRgrUOvOV1WFXA1RXdCRlft/Up0IYohGMbbqXa++WhUR6VKI9KA8b2WcRGjg/OtoyF+x0CWTtHGhcIxumAsoDnJgHaDuBqg7EwuRQLL/Ez6hsEpGjEn0o8KX7AStWGSbkF/jzLWZ4drdDoxk9XmTtibRsLySwsYbVmRxi3oKkNK2Vly3O6CxaAuzNWt+61hlbGOtY0h8JYxsLcHesBtEqhmBVYv1pjc5l3h+y2+8LQvonvq7r6CbO+Xq81jJWrtsfYu/GC+oX8YMR9oMIbDAFrbgzTXnw3VzjLWQnoktw+QAt0rWN9f3rn5cXDOVn5fML9jH+sqHmcXVGsnuV8R3L47nJ/xEJmipzKYy4PF06Ldz5fajbvaSyXNX72sysLyG424cSofORak89DfR8njCW+9pbp5sYfCuLId8o3t7bso1ScUBlue6/trii0p5d6EFnntL4Ixd1mfzYEnJPpyytHvo6Uo5/YVofRV0dT7zljH2EDcZxgCsD2HcXpPukV7pOs29B9y+euPqSYmoJg7NQTn6QxvIZMnrk8iM+u8snTHaPgHfM1w5RfRIUWekNoDLiWG36XHSVXhm3LZ4q4u+IbU9KlFA20gGmapnPcTKxM5cKmT+ZtlYe5NAaQ3oam6l9TLFglD7IEXa/XdiNCoENVVTg9PQXg7pKldENHc8ZkSrWBnDqX4WNgbGijJ/34ppasYmezmbUMrqqqozCQgPhUG5H7tvAZQrH5YWxfADiAo726iVFf/9mGZJsgherx8bH1J9CO+kvoww/Z/kP9QypvZX64IkcphfPzc3v89mazwdXVlddnD2EePnTiCvK6ru3d2XR3LPdPKfLIb7PZYLNZ4+yswKNHJarKWcBKS1indPatWQH32ynO4YWRYwtXiLtwMRA2ruyWRHxkXkPxCWT1lcgOTSIAAXBgAbm7vHAEy1l+8fCct6kPAhA4QODSpd+u7LpggnQ3f5r9Vu3Rww7AcHfOcitZwD+aGK1FrQFyq6pAXWtUlcJsZo4n5UpxXsZDKWfuSq0t5g/nmD+cW4tYFDDgZwumcuDT/sE9FXzrVx628w6hnBVhPB4ijBdfCT84/jwtK6PXIZzcHUBVydfEnisAmpBVaSgMt3blvKN3xUJ7VqpDrGI969c2rZAb8bZl0h6vbNtT68etXnkczo+6sGchq+O8bL0oWECV6kApxkNpC/gSSN8BYNuPB4qqgG7MhwX1ooa6VdAbxjtCY+fC1BolNJ/7a9g2s9CexWxRuLGFj4tuvHQfmTjqgrNxdy4T9TnfOlaCvl0/2DGa/N3HNvRRiisHktvPB2w+aEzmRcmrxC9iFQwzhvw0NHOT9dRNy3f3575YPlI0ZvkqAeIQnxAIu1zWqGuzdlkuG1xfb7DZpE9eIR789xg9UYrG7v+H7hWGykO/x+gW+sqR3mMfHuemk6LcPVUq/VQ6ct7mY11qPzy0XiSvqXWyMp23BaQbqu+cUi82BaCyC130Nvynom11Te/o7aFt+sm+dd77oJTOpY/2nccOGBsTYMoF06EOWvKrPOkXotAiKcU/x60vPfn7Pkz290HGryul+oVU9MuvLkP1ulwuoZTy7g2lsPehHexaxtyNz1BZZJyxX8jKeiIAkstd1zUuLy+htcb5+TkAA07QXap0J+U2cgyl2JgdAqrkuM1BMPrdNA2ur6/tfZAffvghPvroIy+cUsq7d/PruDDepr/0AYmpcSnFLzRPpmTgNHUdEji3Xq+xWCzwgx/8wN4P+otf/AJ/8id/gvl8jtlshs1mk3U3PS8ruWbhH8VQO6cPCi4uLlBVFf75P//n+OCDD/Dee+/hpz/9Kf7Nv/k3mM1mODs7w+3trf3Y4h3FicqbPjw6OjpCWZaYz+dYLpfRMpRjD42P19fXADaoqvdwcmKsYo2lk7GMjQOzXMFE87Tzj98Vy+MAAOfX5evnwf7qKSWpPJegACnzKZzy3Lii3Sluu/EJWCWAlIBPOgaY7kp0XYvACgeSUvoE5Jr7W7kiGhY4pbBclk7OtSsnfnesk7uAUtIyFuzZoGl8i1niW5aqBWYb3Nw4ICakoO0by/g4MmbcU6XCx7//Mc6+fYbZ2cwAWmVhADFmBdu5w5VbrirVfVI/ofhg1q7K8eqAsBGL2xD4GnwHPLcYGGtB1lCRcf9U2XEgEvAxsJBb6Ld0k+5a+IG5a/Fbhmki/iIs3f9q5yLKl4Y9KthaqNoOxeTixuKMr9YOkLXHDMu4vJz4H9VBA3tPrAazjNXs7lhlZKW2w+u/OC6werZCs+yfl8eSbH90lya58TU5fVjl93WFplGoa1NmVQVUlTsene6T5eHpIxR4wCnQbThuXeHmCojffF2tmb8/R/lHIbvxmuYe/zh5Pvf41rFeV+T3KrMy7JYxfwsBRS7NbvjuO8kRAk0dCO7LJOcKP3xXvrj/mPWpnBsCITy3+J2wTQP8/OeXePNmbedkYxXbzXNSosB+7D7sn2Iy9s2hcu2X2jsppewHk0VRYLVaeR+59wHH2+7LJD9OfHzaRrE9JG4MkJ4azJf8Q4YF90GP9TZSn75dAvjbgqWpDxAlST2XDLsrff19GC/f0WFTDt70daN963I9MPa+LYaGfsW2zYJgF1/VjKXQIB9TaA/lu63cXBkeWpi/rYuYIZN2LP62/W7KepdtSvpReqmPCQgQkPLlAimHvmjZll+sjHM3Oam4Q0GsFJGlMycCKOu6tvck0vG+9Jvqftt6HNq3YgppCfTJMAReEdV1jYuLC2vt+/DhQwDmWNLj42O7UeNpvI00dX+MgaWy78fmoyFALJc7FK9vTTBFndKmkGQ7OjrCyckJTk9PrZUs3zgO6cOhD8jkZpT++Gbx+PgYDx48wEcffYRXr151eI/J/1Tt5L72JaqL2EcZqTZfFAU+/PAI779/jNPTyirRSZnNny0Hj5ev8HbPOBCLThzz25fPV3r76Ur/cJlIPl3LVuJLyn5y41ZTlGfXLmV8P4zkyfm68jOgA4G2yir9lfckoILzdWHdu7SGdRa3/DhPB0i4+xa5ZawrV2oDBL42jWotZBW0NsdXmzi1VZCbMvH77pB90pC+p7XG8fvHmD+cY/HeAtVJZY8l9oBYZZ6hv5BFrB0LKYxS1prS5suWcfd3EoDlfoDvLtPg/qzZdKxhyQ2+Wy7ZuISBAZ6lqDfeE0jD72RteWitrSzSmpX4BK1fyZJVuoNZsgrZyI3f92rzouDJ4uWJ5BthIUthbXrM35Y3S9OGJYtZahutJawutG1/ZMWttXbHa7dWsuW8RLkogQZo1uwI5i0p1D9D/ZGuMuDXHXB/MzZBxDMfpBB4ZtJz6Zq5StkPS9xYR7xZhdtx0oGMLk0H2CoFb+xx/o4PjaU0flJc+m3i+OOrG39h3Sm/NE/w/Mn8shKXDs7Hy5sJy9Nwssm5zAeGu2FCMqlO+LC8YcDUhM1vhH3LMe7vt0Xn1jTAalXj5qZu25axhjXHEQ/TwwGuDfL3kPs2lNJp8DRjJNdoqfipfFI4Wn8rpbz9MefF3cqyRFVVmM/ntv/3faiZA07m5juXpuTXt1aJlXnfPq6PUmUWGp/fJj3mFPu2VB2nynZq3UJsjz9Gxx/bC4f2cUNlyqW3qZ3tglJl+67stqeY/m3fZXsXdblvHVTyzlhO2052h0o5E/CQcHdFcoK4y8Y7dLJ6R/stpyEAx5Bwki8Ht7gffVW5jRz3meTXe2PicfKUi4jfcTu2bFerVecI1cvLS/zpn/5pZ3N0fX0Nrc2Rm1S/+7pjRbYpvoHOAbZJ8UVhb29v8dlnn9k8Pnr0CFobS+D3338fX331lb0vVinzFXNOWu9oe8BtjMLgEOpFKXNiwMnJCZ48eYIvvvhiUFzZ14GuUoK3YaKiKKyFLmCs3U9OTvDJJ5/g1atXXj+563K6b3MAV6St12tsNhurQOPjoAzPqSgK/C//ywf4+3//CRaLEmWpmFVT2iLWuAGAau8lde4xMLYbl6/fwu9O/mApsN+8PbYu2oVzim+jVHa/ncLdAbPK40lhTJlyAMCF42HI+pVATfprGorDLWRJPuNHRwvzI4VNXows5Gf6jLvPleRwlrMUTz65wpvcjCVsVaG1fKVycB9SGCtfhaMjjbKssVrVUApYrabfj/SNm0/+/hO893feM/fEluaIYqWUtYT1LGNV+5s9bVj+VCp8VyzgWcbGwnUscEV6HLALgbLE37oL8JX7WTcJ0I4qbPOwRwvzNLQDF5PWsaFny8fOudp3j75rOEvVtp17oHPrp4tWXrKiheCB7m8L8MK5a62hGuV4M342be2XjyeTTLMJuDEia1mSXWmFQhV++QJQpULzsIE6Ulg/X5sji3dEtFbmV3wsFgucnp5itVq1R9lvvDs3wx+gFahrGueA+bxpjzsnS/uQxar7+IUDtZ2GZELAb/Dxd/8jG/5RCzpP89uN7c5PKsR5+PTvrlKdh3FjMfn5oKwbm7tzmT9/UX75/CZl8QFs7tcF1CVxuXOH+VS4EPBKv3nbahqgrhs8e3aLzz67YX55MoSI6wBCe+FdfZAneYbeaR9IcqTCc4rNk1VV2RNTiG5ubmxfj/EsyxLHx8f2o83NZoP1em395TqfyjPnZJ0UyXrYlt9QCpUjtQf5cf+2650cvUBOuHeUpm0/ELgrCu23U2Hf0eHQoeh83jba1dw8VIYhdNfy5lAUjA19gZRSAuYCK2M6R2xAHMIrFj+Wt6kp9wvBqShVV7GwY+tmiAx9cr0bQO+GpEJ/V+0lh38upT6YCKVx6AMy35RyZYuk0BehoTqZKr91XdsjiIn/arXCX//1X+Phw4d4+vRpZ6M0lsbKvE1bJMtXAPY+WNr0Xl9fW5levHiBzz77rD1ONJz+VG07Nk7uon6nlImHTYWPfVQQCp8r25BxSa5vctY4U7RNrbVVqhJASu2vT7kg8yhlrusaRVHg+PgYRVFgNpvh5uYG19fXqOsadV3beG/evMGbN28AAEdHR3j69CmWyyWWy+XWH08c+jg7FVE7pg9V6rrGe++9h2984xv48ssv8eLFC0+pLts/vX/722f41rdO8eGHx/ZoYqU4kMrL1JVtDFg1MnG3rlUsKbs5b6m8Diu5u35dkn3IV6j7fs4KygGxXaU9AQKOlwQIdCCcf0oLV5JLvk757yxiAQdOkJtfdmFLWQeEGzDE1AcBuxzgdccgFxZkdNa1RSHvjlXtsdUKWjsL2fm8hFIN6rppFedxq+ycfUjOWHr8wTGOPzrG0dMjc09soYx1ofJBWAl4crfUX1uqXlwFZyHrhVUuXck/Bsp2ANlO22cy8/eYG3Mn2XNJ3hsbuzOW85RuyTtjZX8MWcfSM2IlS2kF74xl+ecWq1ZOW5TKhlHKt261eCq3+HPdEElrWsX4FIButK1XXbQAL33L0ALLgAnntRPqg41CoQugBHRp4pdzM0fXixqNaqDX0+xRU+s6AkKKosDR0ZE9qjRkIRfeC1H/0ahr9zEKjVVlCVQVnzP4RyVpK1n+m7qBG2sRCEtjuwvjxnp5HL1zc+/hNBxPAFAirPP35fT5+WXuxzd+VEfduc+fTzhIGbaYlfGdnw74d+fRMRTfR3bDaG3mj1evVu19sMavrjUuLze2DcXaa58Msf0qH89T+8ht1papvRmXJ5ZGbM8Q4k1hi6LAfD63H2ASbTab6DUWtHanK3/IOpaOKw6t/XcJSvJ8Ux1NpaMbUp8pfc8Yuu96xkPTb6XkyR0vZFvL3ZvH5JFtNWc/H3PPBe5TdNd1dKg0pMx3VYax9jokXozGto1Y/u/72CXp0PJzF/002zI2RlMtBu6ado329/HepvymHLD23dFjoNldtqdD+PJjDOWWW1+4fed/ivoOLdTvUx2SUp8otiHNWcBOTXT08NHRkeW/XC7xP/7H/8D3vvc9PH36NBmfb+CmkK8PhOfPPuJ32yql7AZ4vV6jaRpcXl7asJ999hm01ri4uEjyvOvxa58UAhBTZX+fymWKepQbSq01bm5ucHJyYo8ho/uYCSzN4Rc64my9XqOqKiwWCxwdHeH8/BzPnj3DxcUF6rq2VppN0+D58+d48OABAHNk8be//W188cUX+PTTTz2A+B3FicYzKqvb21t8/PHH+P3f/338x//4H/HFF1/g+PgYVVVhtVp5dctPjfj+9x/h93//G5jPjUWss27lSkqwp/8biFvPEjBrwpgn5wHA42MxKKYQ7mBOo4dwGVFH/LRwS72Hf/t3D1JefaBVgsOuDJx/UajWElUCtg4o9cOZsBxk1doBr/7TKcBJ0U91b+6xVdayzRw5WrS8DKI0m5m7I4+OGpSlwmbTYLMBcr6lCO3bhiinHnzyAB/+7oco5gWKqjB3xLZHvaKABcM8oIvc+u6ILeC/UzyEeYTSUohYyEK8Kz+PIbcYICuByHDhxesAAKyVaCCOVUJKEFbBWYbmgLKqHasYmGrTkV1Qmz8PoFXKWalGcBIN7e50VQZU7Vi58vJQfvoeaNq06dOxwY2TQwK4FA5gICw/hlgrZ9XbutvslsqVJStTVbZPrVDMzNqwXJRQhcLmZAMUQL2p5fAxmPiaQCqc6eMdrbU9xYLm5dvb2+gVIOE+bMYmvqUwfOlDEX+cpA9IzNPEp7mOxj/imzNWK+XGt3AYCueDtBLMNfL54KoMz9Ny7mZM5iCpD6L6oKAETjm4LPPi4kO4afhVo4JhZd58/2nWyjI9Dv6SHwGsWgOrVYNf/vIGy6W7gsXJOHry93jJ+xWJN+2Dqe1PmTbxyFlj91Fszxnqj0dHRzg+PsajR4+s+83NDa6uroK8y7LE6empjV9VFY6OjjCbzVCWZfC0FSn3GL1sjCfP113rVO7T/vHrTlPsnWlfPJZon5X60HkX+kfO+x2lKVVGsY9c7htNjccM/TDhUOg+6OXvSr6twNh9A7FDvlwY85VDqtHvOq+5DcAqHRhNvWgdIouUYwy/UF3lfiUylN7Gr5dyy2XXi2o5ccg7OGNySuBxqk3XmH6y67rn6RdFYdPjoCAQP9pXytc3Lo3JT6rOJL/nz5/jj//4j+379fU1FouFtablPHZRtnzTzutcfnEtw3N5QnWSkpVbJkwxTg0pn0MEekNlHvJLbYgobKw9c3c5J4fCx9ykX6o8c7+ETcnMgVOyoPzss89wcXGB29tbfPHFF9lrC+Ilv/Tl5VAUhT3W8MmTJ7i5ubHprtdrO848f/4ci8UCP/vZz/DVV1/1tvl3FKf5fG4t6c/Pz/Hrv/7rdkyczWaeP6ePPz7BD37wHn79189RVQU7mtj1BaUQ+JMWsRyAjYGyrp06K0zOA8zNB2D9ZtEFZ/NJtu+Qgp67hxX9/C5Z/15ZAw4YBb357cYKUuBzINZ/8jhKuTtYqTw5OEEKdwOsmvAEdgOwwKwDbgmAdeMNHWNMwK1Jy9Ufgbxao7WGNXHKsoDWDarKIFezFjAyzwbrCaz2QmOZUgqLpwuc//o5Tr91imJmQFhrFcutY5UCAW4eoJr6k0cQMz62HgMArgVgRTqh35xXCKDt/Ib77QGwXv+IdIicfkJNkJd9zOpU+1au0uq1zzrWs2pVMFawESvZkJweQNoe7auVNkf88jh0fLHSXnl5gC3afBNQ2rR3uzZsPiWZlP8k61dVKAfcKuWOVAYDYluQSWllwNo2jCqUTdO2u/ZZ6AINGmchqwE9M4yr4wpN0aC+2h6MtUUb2YfQ3K61tidb8KNNc9YLqTm9rjWWSxdmPjfW9vJEBncCgJWuHWO195vGxjZ1G9a9S1mkmx9HUZtXEsw16RG46tymBWXNu/R3cobl6oKfNA9IcNb3j4O0Y4nLyvm7p8abN2u8fu2vTZpGt3fBcl75FqPklrOuDYUL7dGmpKl0S6F9J+df13UnLd6vY3nTWntWs1dXV1BKeVbxqX35kD1ibN+Us7c6FIA2RUNku8t89PWRbXkMobvSMcg+QR/GDzmauk8PENOfcQp96JwKPzUdmn5nl5QaA0Mk66ZvLJ+iX21LY9PJHV+nADjvAsPbZ7p9cowNM6X8HhjLKzUHjc/1yw2Xq/zMoSGg2xC++1gc5i5eUzRlWeW2hZQCfaxsfWkeCh2ybFNT7gSXA75wfmP6Yyhdeu/jd9ebCQnG8uNKSX65kJSbLqVU59iy2HgyRT5lWb5+/RqvX7+278fHx5jP57i9vU1uGLehVL32jVWpjYbW2quTGNGRpJJ3H92Hr8KGkOzrIYUE9x/Dm47pk+7yKceaVDvgSpQcuWTYofMvj9c0DZ49e4Y3b97g8vLSA0tzZQn9JlLKWHcfHx/j4cOHeP78ucefwNhXr15hNpvhl7/8pT2uOJXevtvt0PXnvomXCx1tDgBnZ2f41re+hQcPHtjj5ubzeVD2Dz44wj/5Jx9ai1h3P6xUhvtPIhr/6TjbeBz3m5TQFN88EfCDl46kcVXhFNxKyTUhKce7RxO7OO7d9+sCstKdlOSOh3u6+dYBtr48XcswXl7+ccX+McMGqDWS8WOLeRgOZhBQzo8npifVMx1TbJ6FB8aaZUDXfDG0Bo+tMWR4PtYevXeEJ3/vCYpFez9sqdz9sLQ+UaZ4+btCBBRlT0qjw4fihvwjVrQWoEUiTZa3kBwy/yE/7u5eVdA9Rp1jikVEbUxV/flWgK0UL/U7xDcExEbBW9ZXQkcMS1ksAIuu7JYfO3qYyoyOKrYysPRsGQkZbJxC2eOHOzLy+28ZmEvHFlsL2xbILcoCjW6gtIKqFAoUqI4q1KhtOtsQ72uxfkf9c7PZeB845lJqTUFHmtN4ada+aMcbSt/dle2PlwCNofTbgX9uXjHpu/jGzQGhoeOI3ZgLL5zJjx+WQE4K79KlOF138zsPlKV64MXowrg8tT4dPi6cjK+j/tkDh+PQddGhdzp2mP40Li7W+Pzzazd+bkFDdIBUvnxtT+vyFHiyb4qlz/MgQVnel1PHiUse5EYfThItl0sA7rSmFBCaU16x/XNoPxfT3e1DiX7Xdf+2UWieCa1zUvraWHvL0cX0tVXed2gfKz9MAJD8ED6VPj8BLRVO+k3ZDu8r0CrHhqko1A6ljiPVxmL8tpUnl3LmzV3rMrbl9bbpJVN0CHqsGHUsYw+xYmIy5SpTx9KQcgiFvauBdx9fg2w7MPYBtLH6fkd3S7scH6bchIUUjYdCfAFC+eRgLABvAco3rsfHxx3FJN0JSW580UlA7djNNo/DN4hKKZydnXXKt2ma6F04u6Ixi6EcnpLvy5cvcXV1ZZViHCjs+/AgR84cOqT23De+y48LuMKCKFYmvDxDQK/sA1KRMwRETI0TMRlSYeU730zQ5nKz2XgfLMgwMr98bKD2FxrjpOK2LEscHx9jtVqhrmt7JPLr169xc3ODf/tv/y3W6zVevnyJ1WqFqjJLQnmqwV3QISnlQkR1VZalHX/X6zUuLy/xO7/zO3j//ffxh3/4h/jiiy+C8cvS3Pc5m4WsYv0nwJ+woK20og0dU2yeIX6kGO6CsH77A/s9Zg5xymD3zvnwfqM8t+6dsoqF90EC/7cPznbjuXBkrUpheBbJIjZmGUtl7hTzvLxh72F01rPUX7ki1pUH6aYJAGka9yxLE46OLK4qDYAsZIHFomznfGPZ1N4u0EtybA6NobSuKKoCxaIw98QSEEtPGpfJSlaZIiY3C5DS8cHyGGF5XLFs+zyMtIQV7x0wmJ4SnAWTk4GCtp0H+oPDWl14v0BDTu34HgBOLG8JoKgWhKV0tAA7W37cP/TUuue4YrDwlDaBoI2zRiUg04bj4KbWzqq1Ef4NnKUqO67YWrTyMmN3u1La3CLW49cea+xZySpn6WqBVu3iwWE/Lh8EoNkPIYxlLDQMKIsCutHQ0CjmBaCA+eM56tsam9eZnSxBUhFJH00B5sjS9XqN169fY7PZ2A8BQ8rLHB1ESvm+Wun2iHPHryyBo6OCzTV8LtHeb4ojgVM+7joAlssSH/+7nWmcO59z3HzkZAnHpbBxYDYsg8chFjhBw9f4vky+FS4Br00D3N7W+NWvlmgaE2a53LRhuuvOcDr+OjsEqKQAu9DxxPeF5DobyOtzq9UKSinvWOJNz+TMT8a6vLz09p05e8DYniZn/RYCloly039Hh0dT6R/4epHcJP8YODtWBlp/8n6xC5BLtvlcYPpQdY33hVIYzzvaP4XG/j4d2aHSfWlDwWOKYx1jm8qQg/fQAoqlkVLmDqEYnyFy5pRZzgQy1cC+a2Vmn/I5J17fF1UpXqk2sU353ZfOSyQXRVOT7K9j0skB22Pht+2b92GhRItNIrLQlGDQbDbzFZToLoJDE+kUxL/yLcvSfrnIablcZn+5yGnsQl1uDPp4S6VDrmIBMHm7urpCVVUdpQIPO7bsQxsc6X8o1AfEphbWOf05NbYPnV+GtsOhNGRdZJRiRqG6XC5RVZW1nDRASvdLehobKB+kEAn1ewD2flhS+FRVZb+qpzC3t7dYLpd4/vy5jU8fhAwBoGP5HLMu65vH+trE0D4zpp+GyoS3yc1mg5ubG3z44Yd48uQJfvKTn3TA2LJUmM9LLBbOItbwAHu6P5OGe/phwmBrOhznqYQbVzDEy2pI0RnFu0lLKsHJXSqg/aOIKX2u1HdKdgrjy+bCO17ddHhcvwx0q7wnay5XjuRGZWfaBL1rr5wJkDDuFFfZd1nXfnzlWcaSVa0B7bW1kuWWslWlMJtRWxw/X3TWa4VCuSi9o4n5/bDeH8URQGcfOCotN+mdx/GUxMyNA7+dNPgT3bQ7fnD58No9j+ML2pE9hsF0wgG2SWphLW7vX20tY206Gp71KNDWlzJxLIirukpTpVoglsIxGawfyUhgLgtvLWXbtD2rXeXk4Pmy6YesaZVz52l6eRf8iIdmAKDNv2IWsUrwYH9KKQvc0vHF0jLWuun2WQJFZY4tLk9KQAGbi030Ht1tic/ntK4OWcaOWWeH2oa5V5a3Qd2OJ9p+JEJjk1v+0pgqKrDz27ybdGms9a1a3VjKAU8fZHVjMY3P3I14dq1dze/U8cVOHhrXTblw//C+tDv3oCM/ryJ5tPF25OZIoqahI/adJSxAx1LXePVq1VpE5+unQvulITRWj7YNgBOiXD1EKlzfPk/6bzYbFEWB5XLp9WlJnB9fq69Wq1Hl0Bc+tndO5S+0R9k1Dcl7rp556nZ1qJSjNx+qt0jpKULu2+pF+Nosh8e29co/sOdPSbkfr0wl1z4pJWuOnkbS0LLKpV3PRVPJINvBIenyQvLIvpbCBGPzR27a+6B9970x9bvVnbFDyFfyhAsm1UiHgrExii02dz05xyaw1ASTq+QsiiK4qN7FXZwp2aaiQ5m0tv2AYGxah0axQfmQJhQgrignv0Oj9Xpt+39ZltYqDQCOjo4s2Nk0Da6urqC1scLadmPMKbfdhSZcOj6Jy9InTx9Qt03cIWVBvOSXvrRxJp7SGpjqqSxLbDabvbWrqdLZdpwJbVCmGLeo7ci5LLQoDG30pugLuyaSj9oX/7AiRh2QIBGOxo8XL17g6urKHoO8Xq9t2dZ1bX8XRYGzszOr7JVlmBr3c8cMWlsNrZuyLFGWpS2r5XJpLYLGrB85OYXvNAouriS/vr7G559/jpOTE8zncw8UJ/rGN07wr/7Vd/DeewtxV6wEZcNAqzuW2N3vx59A10KWZCd+pHDmfubZzSfPcnd+tb865RICTFVQYS4V+JInV8jHfof48DAQ8dxT3iNL70WhrWWqvHOW8sHLnCxnqY4MiKpBYKqZy6meYBXlBVkWQnn1x5/GMtbcFWuU+UULnJCFLDCfl1AK2GyKVs4Gmw1Aet/QHiG2wZY0f2+Oj3//Y8wezlBUhX88MbdW5VavYO1YHGUc/GOWsiRXURSWn+UTCkthoDp3gHJZOPAaAmjtb/KX4Cu5UVMSNGYutKCiBGlUW0ckg3bheVhrYaqZn2L1q/0wVnYdTksrY3VqeZlm5vjIjEurWWEZq3VrxUrpyPySHPRsLVitVa5y/IgXt6rVhXYgNZzMumjTaS1ovTIngFlrC7ja44pLNvaRxXpVQCsNXRtgt6xbK/RHDZrb9g7ZgRTqZ/IoRa21/bjKysTm1RTvIWCMnKfNGGaOPb9q88b9Tk5KzOeFsJb1/0xY90cfxdC47/qeZv4ku5WScsQlb+PL8cyNy26u4TxsyDY8L2cevhvff/f5+DKExwBeVVLuXApVt1v/ujDPni3x/PltJ56pYyejbCM5a6fc+WIqGjKe7lKHwteMnPhHuSGrUdonL5dLXFxceO7AuPwN2a9LeWIn6oTi00fHPI+3t7dBIHlXNHStnqNn3hVIe8g6vBiNlTm3/cq9esoynuYc+k17Kn4q1FD5Yn65eQ7tXaduH4esU+2j0DiS0sNOSfelv4XKJ0T7yEssjft8YkUf3Yc2AiTA2L6JOpd23fCGLGpSQG9uY0zlZ9sySymd+3jschG6D4rldeyidSj1AUS77tBT8M9RRst0cgH/vjgy3pTtJnexMmQBkLOxyZ3sx2wYqH/TApQrJeV4RAAKxeUKRLlJzAUkuCw5FNq4y4V2Tl/NaVuxMDn1mxrjc9zkByzcUpA2prz+ph4fdz3ebjPOpIDYnDrPBcqGhpFy9bWffcxpOWlMvQCm/JHi9s2bN949czz/HOyg8SZkcctpzNqHeA35mp5kIEA2VwHVJ5uUMzQXDlHS8PImJdf19TW++OILnJ2d4ejoCMvl0n60UZYK5+dzvP/+Eb75zdP2WNmQItsBqG1KLE03B4TikVzmp79+6Ybz8xF7l2XV5R0sMi9MW4ptWRqlPCm4/Xd5lCWVvwsLZpkqAV8KS26hNDpSqm48nqbLv2bvPH98bpayO4ABkGCErHPNfnetbJVyIDxZ1hIw7/4Ke9x1WSp2L2T/2iA4rhcKs7MZ5o/mOPrgCOVRaUArdjy2tTjsFKz74+sb/mfTonKFs5Al904cwdP+hpBFIcjLS5PCiLYfsuCMgbDRsSlnyNI+X3uMMQNQgLZuKJiwalXUDxSClrHEh/OgdK21K60jpRUsd28jyztlyc2zkGXWs9ySl2S0cUg2BiJTfEo3KjMDdfmRzp4FLtq0RVv02kkB74hj1bR5oCOPtekHWmvzEUJ7n6yuzLHFup52PcHrnP74HJ7Tj3mY1JyXQ3Ut1/fG6t5Y59O4xMen7j2z7je8dxrvu2HTbm6e4OO2+8jH5Jv8eR+gsBQm5ef4mncV9CeSPH0/l4exy09/v2X+1mu6Q9SFubmpcX1dd+KETvPhYXLbxtA2FNunDdXp9IUforfI2bPLftOnj4utL2ldzdfAfC8Z68+pPf22+7jYWla2F3l9Us74MyXl7C/H8hzqtw3f+0BT1KnsY2PTlP1lKA1po6l6kzqeXYBW/t4qPhbssn0N5T1ETzck7n2iqcfgqctlSP2EZJH+sXVNbtgxskqZhuhnhvJPpbsPih5THKOpGsshdcgp5Ih1rlBa2wxiuXe5kTJzXw2Jp5sTpm/SHgMSvu2UuyHZps6HbGgOmbh1I6fQpiNGQzenueHlhm02m41a5M1mM8znc2s1S7z5GBECnceOvSFAeGraZlIO5TO1gAjxub29hVIKt7e3qKoKT5486SyW6b5Nis8tmsfSvufDbdKLKTFyFnQpnrnphtKQCiWp0ExteHY9pknZ6L3P0pNT3105Mp3NZoM3b94E+WttrG2kxc22C2hezsSLg7yhOLF0qV/NZjNUVeX1t6FEZVCWJZqmid7XNaZ9Nk2Di4sLGLC1xF/91V/hZz/7mbXqpbt667rGo0dz/Ot//V08fnyExaJEVamIVWz4zwfdjEx9FrH0dOWAzrv5DQDxd9/P70spMlXLldl0dKIEZH3FOTzTPT6XhfzCv3l7kZatISU/lQ0d8Wji+vEMOwfKGqtWfmcs1Qs8N7KMdWGNP037BNaaetSQlrFlqWwc87tAWRpGVWXeZzPzvliUKAqjyKrrYQot2cdmpzN88198E4v3FiiPSs8q1gNl6c7WAhbEVEqhUEXSKpaDY55bwBKWntYfEQvZED96MtmoyXi/I8CseTg/7m79hFsWKVfuHT4Wl+2xamUUtFxt+5XyBTZuBIBqdPlr+HfEKhirVfJvhBv8OGTBat+1ebd9nAFI1Hbsva6cp2rzRYBo4wOu3v2x3DK2TVtaHFswWTPrWChjEdvA3R2L9u5YmLwUytwdS08omGcNbLD93bFAfDzlRyfm6Br61gg5FFq/0d9y2WC5JCs7QKkCZQmcnhbs5AaKo1k4Pv+4J4WlMd7NB6EPd9AJZ8Z7gH9ww8NTftxvWdY+0OnmGTB+WrzL8uLzYrosc8mfP52b1hqrVYNf/OKaAbIuHF+jy7VwXdceGLgr2kZhv234FPE9w5A9uyzLGF8iuU4ifRyBTLnpEyDKP8rOkVfKFBs7hlBfGQyhQ9IB31faRRmOqdfY/JOzRySifSLfHwM+AHpX+seQ3n2fY9ih9ZMp87LNvj4njNS3HgLtWo6YXkm6x+SIzW/bYjW7wiYOpV6JQrrKPtrbMcUhkp1w7AZiSEWkGk7fBqePx5CGmlqkjmmwMdnHNNJtJ7x+Bd32X1KE0jskoHCKOhzqHwsXWpiPSesuy3fMGMDbRWqcOYSBvGkarFYr+07WVuRHG2cOyhAIGANciLZZRPaVVQ6vqdvN1JvJEFhGm+bNZoPFYoGzszP795Of/ARfffWV5RlbpAyVa58U69+yn4QWbjF3Ho+HG9I2Q2F4uFA/zu3PQxflMr+heWaIMifHTaYbCpva0KYoZ0zPHfdDG6hYGXElUO7GK6dPpfjE0kmtvYaQbIeU3maz8Y6f9+8BN5axZ2eVBV+7fO0v+5sU1VyhTf7uHdafx+V8w2F53wq9x8K5tHy5qUxcuuFqdKCoU7QTX24lqy0f4qsUKb67FrIyvuTPy1cquX1ZCYD14/E8cwW+X67SMtbNLzw/vD4onKtr/ps/nbUs3Sdr2pkOWMmivUfWWMeG6qFXiaaA2dkM1UnVAT5tX2J1z8FO66esZ+fPs2YltwB/zksp5cWT4WQc227Rlaczr4i23nnnbjkAbMxP1EVwfUrsyZK0tf6k8FrrDn/PUrV9t2Mvd+NWrczf+ok7YokPFCyoymWQ99sqpfx7bQkc5WXPwW3V9ZdhCTT17oplsspyJ/7W2pi1Ca1YnltZ7bO1hiU3KPh3x7ZPVRoLWTVTKBYF9EZPZiVLcwrvj31z55B1Sd/cGlrj+XO6H47GuPW6sWMTjVsUfzajj4fCH9U4sJTL7vzD7y6cmxf4WM7nBTnWh3nJuG7uUYEwvgxxv3xyZQtsNg1Wq8Yrb/JbrYzfZtN02oqzTDZUFAUWiwUA88Epv1ohdw0+lA5hXy0p1EfG5r9PCR3bK6fSy+m3ufuQ2P4oxlcSbx9jdEh9dIjt466obz8X20Pn6PF2qbdL8e5r7zFeUn8Q2wePkSknTJ9Fbkwn0hf2HYUpt4yG6GyIYnqKKWhIvxqjo4qFHSJ/LGyO/i6HX6h8h8S/77TNXC7pTsHYQ6GU4m4Ij5BbyH3MGfj7pn0DVjmLihiNafjvKEz3tSxDQNKuFfJT0s3NDW5vb4N+tJEuisK7p7UsS3vvJB9Tcib0sXRo7UMuhuXiPbSQ4feShkAu4rFer/H8+XN8/PHH+O53v4sf/OAH+MEPfoB/+2//Lf7gD/4AgFm4v3jxwlo5huiQPhJJ0RSLVqlEBHwr9V7lfyBMX3q54fo2TjFKKTpi/rGwUp5cYHrokU18TBgKWod4pcDk0HhK79wqNgXIhhb0m83GWpjG0gnJ01e/8l7iKUne6c1lUwqYzQrMZqVnEessYwGjNJbWrHQcLb8r1vmR8tuF50+uFEGHN7nLd6lI4Xydexz8NXkGCAylpxsfjCK+aaTymkBa3md9pTlX2vsKfKr/EJCqGG/fT8ruvzurWJdW2CKWwvrl7PJA9WT6s6sfujO2aUKWsbp9mnjm6GGTLoGx3FJW6wazWQGtDQA7nzcoS+DmRmO9zlMu8/FbQ6OoChSz1iJW3BUbAl5duwxYtso/dIFUeQct/ZZhokBsa53L3Tnw2gFhRfsPAYVt7UrcxoWV1De0kj+rEn5sr+fOjuCl5tuxloXPh4OyNhzvSlwODb+LaaQtYuVfj4WstXZtfysoNKpxvCkOt4otmJuCA2qV42HB4kJbNy631qzc2FHEHFztyNfGU1DuvlzA3R3bFMa6t72nWc81qrMKKIDN6w02F+MtZPsUzynF+hCle+7aRa6HedzQXqtpNN684TIBNDcopfDgQYnFgo5mdacCuHmKz0X+vBQ71l6mQ+MsH/fNb2kx68fp+jl/f26SYWR+Aa3jZeqTm4dcWdLTyPTy5QpffLFk/lKn5M/TMbBxNpvh137t16C1xsuXL3F1dYWXL1+iLEtUVXVv9ihT09A9Dt/fyL4R401had0pw8fW9qE+NkbmMUT7X8CdbrPNsbHbyjJ1nu9Cj9G3jwxR7MS0IeP9kLBT0VgdW0g/wNvdmHxIWVL8ZP/8uo6L72g4vWsr72gbGgzGppRyY2iqBnzfOkIIRJD+tAjiFFrMjVlQDiW5eJlqITOEzyGBQLugIQr+u6C+Rd3YL2KGLhZjYGPfVzp9fW4fxBWRRJvNBsfHx3j//fetGwewNpuNBfz44nyz2djjO2MbvX1Squz3nXaOXwwAlHJvNhssl0sopXB2dobHjx/jgw8+AACs12u8evXKAkchXndZLrugvn4jlRD0W5Lsm3wcmLJvhoDPkAwhirWREI/Q/Jiaq1NypeQcqkiIyTJ0HUHpcot8OoI3duQdV1zF0g7FI6t0fjyjBAi5uwcgifqg99DduVNRKA/kVpYK3/veOT744Lg9QrarfKbwoXdSZnffUwpsy4X58fYp40gAmNw4CKsE6OvXhytXwCmaORjrjiim3+44X4rjI0ZGkR62kKU0XT06EDYU1/E2T3/N4ADi0DsBseTmlyMp7Z2M9BcGEDhoy+tTW15dy1h31CeBsFR2BNRKcL8olD3eOEXB9VShcPZrZzh6fIRyUXrAqAXFqF0Efls3akNw6x4LjkJ1w4kwMi2ZHgdaQ3w9sA++nBxcTQGxHRCW3Nj7aPLYKHQsSBkIS1anULDWqdSkbTtnsnVAWWa9qqB8K1Zm/eq9CytZazHKwnKw1B573MoOwLeQpfSV8uJY0JiXSVt3Nm3lgFKPB8tDpzyZbLzuednJ9JRyFrIW0FXKHsvNLWPJOraoChTzAmquoDcaGIhb5M7nfK4L6QxC76k9UGgN1kcpQNZ3c2MgYCw5m4Y+NOHzjeM1mxkLWj6nOEtaCufGzk5jA421lL4DPPn8ZH76JxgEcsr8ef5THx/5soTo9rbBckl3ujr+FJ/XzfV17bl312zxdCi/BKTN53MopbBYLLxTmHZNd7nfzqHYGj+1H4nlJbSPTO0tp9CdxtbdIf6hMSEmAz9O+S51C7toN/tsi6F9C9Ddx8X2dan6zUm3b784FuDM8Q/1rW3a0Zh6G7rvl/t43s/G1sWh0ZTt/5D1W7vq5/vKY+68kxN3aHxOu8rvPsoxpWM7pDXJKMvYQ+psbyMp5R9vN/bLoLETx7v6fUe7ptAk07dA6gOCczZIRARe3sVgXFWVTbeuayyXS3z00Uf4u3/373phaAy4vLzEf/pP/wnr9Rrz+dyGWS6XuLm5AQDvThlZVoc04UxNY+owVjaxDedqtcLLly9R1zVOT0/xjW98AxcXFwBMHfz0pz9F0zT2C/Mhd/uMpUOvU9q45JZDaPOWAs1y+aY2gFMsTKccQ8Z+6JIK2yfbUEVZURSYz+cWfL25ucFqtYpuVOW9P9xSlvPk8iil0DQN6rpG0zRYr9fRcpZK4Fhb4eH23Xfm8xL/7J99jI8/PsXxccnuh+V/LjwpmklJ7Y58hBfef7owTtHdBW8Nf+Wl48JxBTgCMiqrSHcKdeXxIzKgKwdknfWUfFdKwy1xOSArlUk+mEnpyHSNIp4Ds8QvRPxuPR1452n4gCmBAn6dUVnEwNRQHF7XZC3sg7IEwJryomOJuYWsAV7pWVUKda3gTA37+zp3L6oCH/yDD3Dy8UkHjOXAZwiQ9f6KeDgbHu433UGLIsCL8ZNy2DhQ9s5Pz4/VDf/tuUXAWc+fuwXKbSx5QKtIwFrM8qOA4YeTlrIdIFaAmU74gJskBWNVCneELwBzR6tmbsoPa+9rbQSvNq60RIUGdMPG8hYQVVDWqlXBPz4ZaPcGPI8clOX1qmCsaBnAbeXQ8C1kNZz87W8Arm2VCgUKYyGrgbIp0WwalLpEfVlDr3a/h5ZK4hioxIl/3BS6yz11Px+t5+R8S7w4H/lba42bm/51ydlZidPTUoyXfB6j+N2nnEP5k88ZZn4A++3mBykWD+sale6ESxEHabUGXr9e49mzVXCd1gdY5OyTZR3QB3OLxQJlWeL09DQJxt7VvvhQqG+tLeeOWJ3weyZlfwulQ3xCa1m+dh269xlDIQB2G2DgbaBt+kUMGImB/9xtjB43Ng7nxp+Ccsqqb86KlRunITrtvrAxS+SYLOT+Tmf+jqaknHY/JGzfeED0Nrbj+7Ke8cDYMYPcPmgfBdm38BgjT6z8+OYlJ07OJiu37rat023qYminuOsOFEs/56vCbWmoknyX6acU3UOob2GZSidn8Tm0rO6qbENHWc7nczx69MiGefDggQVeX758iaIoUBSFPZYYMHf/8E311wGADY0hsbE71p5SbYk20Bz0rusaX3zxBd68eYPNZoPT01NrxXx1dWXrczabWYu+KSlWr6E65vNKavzatn30zS8dZXdAJr6JIXf54VGfnLn5kG2hr41wGXLS7JMjlJeceX0sGcAmbS0/NE0KX1UV5vM5jo6O0DSNtc7neQyVd+i3lINvmuXRaLG5I9am6F0eS77ruZWnT+N2WRYoSwdoxhTLvkI5BsJyP6moDgOxjidPTzE3DsiqDmBM4Ls7Jrnw/F26pHzmlrCNBWGVajxQFmgs2KkU0DRcUc7bBeWJ6tEo0JXyf8OCqb6SnZUqfMC3q5ynd+IRA35dufK1Er+v1uWBl7lvAcv/OIDrg7D8vSh0K5dq3YycIatYU54KR0cFyhJYLrsfQ5i8OWXvg+8+wOLpArPzmXcssQWvTHW7/CtYME8pZf34WOC507/WrQOYRoBYCfgG4/K6CfjZeKHfvE6Fu+fGQFuPthhOuEWpveNUgLMxEJa7hd7tb67ohwNAeboEcnaeIl4U7IU/Hsu4VBe6cXGo7Ky1Kx07rJTn5t1VywBqbqkLuHqVoCwHZkOArS40VNPeP0sWsG171O1x4RaMFXfHqlKhnJXQC43mpnF1OICUUh6oyedQ+SFRqA/TPCd1C7H1Smh9EPsdWneG5tJt59Tb28YbZx07Ba8rBsbhk5MSVeUAXB7O/4hG8ohZpEkXVx51rXB7a4DO9XrTCd9V/jvP5bL7sWZsDST9eH6lW6heANirHl6/fg2lFK6uruxJPyn+U+37ie5apxHbA8jfMX1Ejtxy/Znin+PP6yLVN2V8cgut/0Phx9Bd12eIdql4z+GbSl+65+wtZV3xeKG2ltpj9sWN0dDyTI0buXvRnL1yrP3F2nwsrRQAy+OGeE4xNh4KzvI2UKpP3VU5D+k/sTkpRTGdaCo8TyPWT3P779vSfu86H1EwdmgF32cakq8hm45UQw+58bvReLixg/629bUtuJPK+1T8xvJKpRHjt8uF3iFR32CdszkM+fUpE3LKd0jahzpe8SM9qc8vFgucn58DMHn64IMPcHJyYv3KssRms8FsNrN8bm9vreUZxdumfd6H9h2Sr29xPoR30zTW+o/4rNdrfPnllx4Y+/TpUwCubpQyYCwHYrdpf7F+wjfaY+fnXdRxTM5YOD6P5vThvrwNUXbE4o9ZB8j33Pl+yLg1ZjGfM9aO6S9KmWOKF4sFHjx4gKurK49fqD77lF6pMiPFcuje4b71gCwLaZmwy7GO0jGgmAFi6Z5YpUgmrix2yIDvHwNhweKHgD0X3vCX9eCn7aflg7AG3DPlT2OdBGNjChEDDhQoisZadTaNBkDHLxprTg7KktLbgalhENa8uzLwFd/SeklZvt26cuXBgQD/twNaqfx80MCBwF0/V0ek+OdhZX351mDx44qVgr07mI6+diAsAbJG9sWiQFUp3N42bfmH279SCg8+eYAHv/4As5OZA2IFQNoBK5X742GUcsArAWFefLA2JC3GxbHIXtqRdDoArNcH/PcgoNzpjwi7MXfKR8g9SdqBp1507WSyFrM8fQ139C8HccWcywFXe9Qvc+fAbRKIFccUS/DW8qfxmaXVeW/z1Anrhj/3DLkJfh4Qq9g8zurTA4GZn3f8MfVLygelz0FZBR+MBQwQqxWKqoCea5S6xKbI+xjPjQPxNVBsDg+FD11rFEuP3slt6DpLKutia73UWiBE67XGel17brn7EnNLSGHv3aZx0qQL6+be0fmdcnPyAOs1cHGhsFo1uLm5ZfFMxNAJIDyMXKM43mkgNsYvLKeJS3P269evAZh94+3tbVaZ8jTeNlCW/07lrW/9KvmE2nxOXmXfCvHpA5pC71PSlO3gEGhqnUduPQ/ZzxLlgCipONvSkD1rKkxO/5dh+nSQUs4p851KOzaW9LWDu9S13VW672haGtKGYv0pF7RNueXSXc4bsTlzVzKN6WOjjim+S+qbpIbGjdEUg2XuIiwnzX0vgnKV60P5AIc9GaRkO2S5h5BcQPQNyLENyNB05Bdo24AO95kI8KPfgNkgv3r1CoAphydPntg7SOnLZsDcXxpb9E0xXt1XGmJRGWvzHADidUTuv/rVr/BHf/RHSYXXPig1R+x6nhjLO6WYCikYckjWT4xCSsep5vactsDrRGvd+yXuNjJRGlpr78OAmPKPEy+bUDuiuJvNxh4hLNPlT/JLKZBidcc/TAvNP6mPFGL54vLseqwz853C//w/P8V3vnOG995bBIBYB7SaOC4u+bv7ZZWI11XqGjffn/j66Uh/nl5hnwTCGsvesgX3SihlLH1N2ALdcjegJSmk67qxY6o5wr1BXVPfkf0SHh9YENUBmACBncqm1Y0LC/YSmqWUdHNKe9cmwnfHOgtX8uNpEoDq5HLHEXMQ1sXp1pU8llOjKNCCqnQ8MdUR3bdrGPrHFZP1rPkIwMxjdISxKyPev7wxUQHFrEA5L6EqY/HnAajtH3+39R/4zeNwgMy15wAI27oRGCb9omkWXf6hdD3glf+29Qv3zoFCsPAsTJRi/uybARUIxK0qpb93N6tw9wBQbrmqtA0vw8XcOvlgxxRrLcK3gGUH3KQwBZwlLB0DTGEUk1GCsnTCNvX51jqWg9hKsbtnAwCrB8q2xx4DrTyFsdBFAWsRy/OndQuEFwiCsPQsdAFdagPINhrVaQUUQH1dAz6u6Jd7RDnNx9MckCg27/J0OMBDTzrhYrPZYLPZRAHEmCydsSOSt6F6kFj6fXR5WeP6uu6ET0UPzRk0btPc9+jRQ8znC9zcXGO1WuPi4gJ1rVHXBTvhAV7ZhtaHqfLry3PuHl2ucbV2x+M+f/4cALzrbHLK9j7vC/toV/sZ+s3Dh8oxtS7fBojPiSfbSR//+9AOhsp4H/IEDAMjD5mGAjz7yGds3gyln6Oz/LqPqVNQTvkMHR93rRvbN23Thsbq3N4WusuPIWI0CIzlirVdZiTFOzRY5soSCxtrlKHwfYCppD4AKleOsXQXgFefvFIhnTtB77vzDJl4pxjYdpW/Pr4hZTenbfLW3RhPV159aU290R3jH4tDm3RKe7Va4fLyEqQI5/fNyM09L8NY+ebkbQri/IeklVs3ufFkmL5NZGxhxpVIVNbkdnFxgc8++wwPHjzA0dERgPQX8DFK9YHcvhpSpEwFNEqKAV9SrqH174Mg8fE2Bub1jVuch/ydK2vfXJ9TX6l+uqtxMAZaxtY/5N40jf0IRNYpD8PB2Bi4LPmG0k+VI/cLjTOy7YR4hZSTfWUwhmIK9e985xTf//5DHB2V8O9+DfNx/iHwNObP43cV0b5/+N3MO8o+CYglqxr6IKiqylZJXVpltbs/1pUFjZ8GRKzRNHSHqQEYqTpNOAMgmrtSOyXbhjN+BKY6K1UFAjrlscQU3snlQFUvBc3bGOfn8yG/1tVLy5WlDxpLOUgGV0+OnztKU4uwvA1QGL9dFAWBsZrVIVnOUv369S/HUFWYo1YJhOUgaQdcBWtrrZtSvnWq9wfGB3684B8cPx43BfhKwFXK6aVvRVCdcJ13oCu3JM6vj5QPuALwmlJnnuJiSKtZ7k5uvD0r309B+WGY5WtHHuX/tscEt6An/1aCytamSemEZAuApiSLB8aSjPQhBFwYD2gW4CvF74CyfB4RdUwWsTELWbprlrc/XbRhWrCWjisuFgVKXaJZNtD18DVRH8XCcN1MbH0h1110HYe8EiCUVkppzd1kuH3t229vt/tIUspdliXKEjg7m2E+P8J6vcLt7QYXF2v7saZco4TeQ2uXULkO2afwuHKdLPPBP/jl7lV1t7YYd60QHbIH4XFkuNw8xHin4ufsa8buIXjbCrWz2Pr760ZTt9NtyzK2t9tFWpLXIZXDVCT7f+wj7xwA61DyxOkux9gYbbPOAYa1xZCOgN4PjVLjcYxywsr5Lactp/i8DTRWb7srGrUa20eFDNmYbKtYpfQOhYYCHLENDz/CNBZ26nyPkVcqbN/RdjTloumQ+sVdUF85jinnuq47bZ7uJH38+DEePXqEJ0+eWL8XL14cXN/IbWP7aj+h8SSltAJ8ECmmiAotyJ8/f27H1pubG2w2G2itsV6vg2PuPmmbjXNosRpTRuQq40Lvfen2ufP0hsw3Q8plyrVHbDMwhleMCPwqyxLn5+f2KO0XL17gyy+/xHq9jt5lLMtHAqdSubtcLrHZbHB7e2vvHpN5pA8ZQv1SUmjzS0Af8Viv196HKMRPrpW4co3yQeMtAYsxpfFURDLUtbFKrKpCWMUaDb/FfpQE15QF18y7DNMNT+EMP+kWCqeYZaW7D9a3hC0wm1X2rnL+rKrKA2vluNE02gL2db1BXTetBVaNpqmxXvP7ERsYpEODjiumo3QBsuhkyA8c8ErJcuDUxQuDtORP6Iq759XxcMCtD6668nSgKAeDfSAVNl1TPtqL7+oWgj+1AY2mcSCsuxuWLGPRPqkezZHPxgrWWMbWtbb+SgHn5zOs1xpXV3VnQ37+t87x6G89wvGHxyiqwj+iuLVUtXXdglSdMaLww/Cw/F0pFbWIlU8oWKtXmbZMJ+ZGVd5ZG0h/Bs5GAVjuLptUJnnxOfAJAbIq5Tfdttl2rFlVO/5FhBga3lqw+kK7bqpZGGbtCgVjzcrTbu9iJb5as3dW1jbdpn2iDatdXEpb3lMbqruQZay1nm7T8CxjSU7KR+HKSDXKt5AtFQoYi1gAKKrCyKU1inl7ik2x6YLugvh6KjSf2aLvmZ/MfFN776mwst/RuM/3JjQ+yDvXuUyp9UyO3IdEskwAs3b44osv7G+tdQvSmg/WcpSgfD0k0+njIdfbHHDn77yOYkTAu+R9V5TbNu4atJUUqoNYOE6xNX6IV07byElz23C57fS+Ul/bmlqXNpTX21TmuXmX88oY3Vds35maY9/RMDq0cVmSlG1bHcs+aYic9yVP7yhNHhgbqlSpZNkHpb4GiymHh9AUPEKUq5gdumDq+zpu6IC4zzoNyRaS+92AMp7kpHjIE+S2lMpbzC/nS8JdfPXXJw//vVwusVwuMZvNsFgscHt7i9VqBcDcWUrKcQlIpHgTTd0eOOjRF27sZlLyDymrOMn2HxsbYxvk0IZTpr9er3F1dYXr62tbN8vl0tYJgQ9Tj2Wh+psijT7FWk7Zxdxz+lyK9xCKpTW2T09V3mP6aahvxco9lmZVVTg+PsaTJ0+wWq3w/PnzzpHFueOTTI+UvvSUd1bz+NuAnaQgTil8eV5ieZBj0L7mxsWiwGJRYD4voBhIZHEf8aTfLowS7nzd5Mdxzy5/7sZisXA+yOusXYv2rtvSA18lGGssZKXyxCmIm6Zu+Zt2YupCoywNYGGAQprXZB8AONDpEB8JwqpAOOmOTjmQW6hrc/cu3zDJODJdU0a+PK7s3XHI8n5aHrdbZ34b6Pope3yxUkBVEdrVHR9nD2Y4/ugY5VHpACmv3QXWmEr85u22/U0gqn1XygvrgSACcLXx4dxCoGwH+AV7BuSUlpGdODwvXn8TjWiK4USylEcQk9Uqa9bKoI2enx0LFXunNgNhEauZhWubJr9/lfwoHrn7YisfbFTwrWARkVOAqDJPFvQV5UPHLXfuiY3wC5UzWffy+pfHHMv2QW52DcrCeG1OAapQKEoD0qpSWXA5dy1CdSPn0F3slUPr8z4Z+fgsZRrKayjtS/nL9xpa+x9ZKqUwm808eThYEFuPxMptW0qts2U6faeR9dHUyuwUr5Q+Y5t2kLvmDfkPkWOqNWdOvx9TJ9uAvm+TfilnvJuCcnUmPDylf190o7H+wNtYanzkv2UcnkZOurl7xpCeaciHCim53jbap545dx0yhM9d1NHbNFYS3TUI36eX3BVtq1vvk/Pe3Rl7iDS0MaSO9RvDOwbQ0NebnORmgMJwEOFQB5C+Sfc+LVympEOtr31SXxn0tYupy3BKflVV4YMPPsCbN2/w/Plzr5/ue7ExJF9cUTGmPEL9eVd9nOSjL8w5eFUUBU5OTvCzn/0MP//5zzuAq9YaVVXh5uYmyXtKOUM0Bvyekrap6xBxEEC2c2pbBAryOKk2EmtTORQCSHM2zUP6qA9GDVOQEEC2XC4BAE+fPsWbN2864WQZheZQ+ostfOmOOVk3UrnTp3DjbTbEj6wvV6tV7/pElhn9JutN6tu7Gj+UUhag/t3f/Rg//OH7ePJkgbJ0oJkJZ2O1cZ1fUfAnLNAp4zs+7l2WDY/Hw7m0ugCsOYa4xGxWoSxLzOdzlGWJo6MFyrKy72VZtfIXnfogwBUwR19uNsYqe71et9ZXmzZduruusU+0pnYEzjKuHrDJQU+XbteN3JXygVsKJ3lSeUmglsKYvIYsZWlc6ip7uJ9Lg9cpKenlEygK6hvuTtim0ax9xO6M1ShLYyXLj4WuKtUBlqntllWJYl4Yq1g6pphZxErwyfbVFoTy3gPhpbvHm1nfct4hvrHfQTcImeH7hUBZz8KS+7Hwba133CYh2x4ZoEnubOhK3vVKba6D9IbdLdDJjjIOPT0Zm+7TA2FJxkI5a1mybm0YH5IL2oKXqmBHKLfy2nYC5axn6S5bUZ/22Vq38uORbR3TXbaRu2NtWWsmj7w7trXwLRpjGQsN6JmJWJ1XKBYF1q/XzhJYVlNivu2bp2i9lWOlGZoz67rG1dUVtNbBjwj5O80P3C8VPpXuWMrhNYWikK+/tNaYz+f2Q1i+3oyVQawOQ/MkX+/wesxdp4T0IXL+oXoLnXgiZQzlQfK/ax3LXeg7xqQZ26enwubq4LZp50P0fNIq8Z2uKZ+GjucxHkPcd0m5428sDtcH5aYTSjO0x+uTdejcGJNlqo8s7iPtMr+hfX9f35Fz5F3PS5Le1vbxtuZrW9p27fkOjD1gmmLhG1uscwp9JbfrDheSa9u4Y8DsVNxtJvQxG6lDobFgxbbpDKFQvcnJu09ZcFfln5Jjs9ng+voaz549swvX6+trbDYba5GWs8Efmm6McnkN4ZlDYxfPQ/jE/GP8+VGvfMzkX8oPkbFvo9FHUyw+Q8BfbvnmLoRj7W7sPMPj96Ub4j9k7uFKkhTwmlsXctziirFQOeW2XwtesA1vVVXW0n69XgfTjMmfo0AKyZLTpkP1HyrfoWB0LK7My67mAZ6X4+MS7723wHxeAvCtYl34kAy+1Wo3je67C9914zy7fJXXbqRFbFmWqKqq/ZvZd6XK9q8AuBmZlYnAWONXlgZI5Up/AmLdfbNURwp0r6tSDgwNvfvoVOx3KGynVAHwcZDCOkvVeFwpH/Hy5fXLPjZ3hp7UrnUgnPLc3B+v164fHZ1d16ZeyuMSi/MFqtOqA75GAVVPbt6QXXj+tNatYP2RhyE/3hZ4MqKZheSzckTiSZC146a6YWTebByZjs90GEWalk1b00M73lrMD4q5MQtYa5XK+xGBkoCzNCUgVvmWsEELWQJJlfblYPXo3Q/LjwvWDrC19cfLvO1+Nm0uE5eN3VnL89MBY+H4e3fHwg9vx26lvfxQGGpjnuUs/2uHQvqAQRUKxawwIG9Pe5hin8/zQBQDXcidK8Zz1hl9665YPvapwJ5y/5Haa6XWJ6F1cWjN1ecfAuhSY2/o95g1dl/4qdprinIAw9Aaj8JL0Cek40rRkD3QNrqrqfiOoTF743eUT31tLbdt9/mn9jZj049RTv+RbTe3z8Xij6FUmfTtDYeMDe/6yDQk62Gbtgl05867pLH9fIwe7pBpW31niu8hlMG2Y1Y2GHsImd0VbVuZsUrQWgfvEJRffexSNq5s3/XiLkUhK90UkXIw5M5p23a5z43i141Sk2vfJDS0XvsmvEMav6Q8/C6f169f4+XLl/j000+99i/LMrTAzFEO7JqkIiIH+InFJ9pmYT+UQgoPrTVWqxVmsxmOj48tKE5+dGfsPsaPFHg6tEx4HmT99OUlZMU4JD6n2GYo1n5C8WPjS0ihlQIHY0RyDSnrnHKYqt0YYKvozJnHx8d4+vQpNpsNbm5u7LyasoThlqQAgmGVUsk5nco5pbilfFP/4TIQcCePAORhQjxT7lKeKfurVAaWpcJsZiwTyYrRtWXz5+KaPxeO+Ml3xdJyT3gAruXqxef5pXjGMtaAr0VRoqoc+LpYzFFVFY6Ojttjr4+gVAmzZSjYH6ERnMjCtYFSTXtnruFP7YravblTtkZdm3ouiqa9J5VkdQCr1ubd3efaBTulm4nTqS04sNhZ3Epekg8sUEzl7VvIyjtjSX7upzWVv4ZfvwC/p5YD0n570N5vbhnr6pWsY82fuUPW5amqCjx8WGG10ri4qHH6rVN8+E8+RHVcoSjdXbG2zVH6Ari07uIvBLISYGXDFCJuLE3pVjg5Yul5cwbJyeKQPDw/3D34HgByO4BfiKSzDrglyLs/VkQMWcZyN2mlGgwjLGJTcsQsZaNxVZeHLXd2z2zwztfCRnJAMvVLfrcrpS/qi/MhQNSCsgy45RayIctYXfhHNHcsY5mFbFEWaHQDVZk2WswLB1ZnfMwB9K9/+Tu/a12ukWOgFE+Hxl65144du8s/puFyxNaB93E/zdd5lNfNZuOVgywDfuJGrnI1Vc9Dyi12yhqXv+9qm7eBaO1Le+nb29tOfeUqg/vKKlc/MUTHd58ppf98R+NpivK7b3UQGkNzx83Ye196Kd2knAtj4y3QHYvH3HX7daMcHdIh6DUlbYvLvCNDsXIcOie+DXNQ9M7Y0AA3dcaGFvguC3aKBdEQwCmHx5CvJkJhY2DO2K8C5UQZAkly+OfGCQ3U3I3A7qmBvftGY+tzDG3LK0fWvgVSSp63qb5DwKZUjnA69E1daHGVC27JsSC3jkNtqQ+0CfGgJwFH3C0GaqV4xkjWr1Lm+FnAKRUO7QOSbdthauEtFVSyT8TCxMaRlPyhtDmfXAA2JIskyS+3T/SNdzQnrlYr3NzcWIvYFK8Y0CqPtwulRxtUCjO0bUqQXPZxPr/39X8J3scA+W2oD7Cn/uruu5XrJD+O8eNWobDxyN/xAXNHh6fLO/dXwTCUJgGyBrhzFrFlWbWW1RXKsoJSFYwlbNnypGfB0tGd3y5d3d4vW6IsG5RlAa0Llr/uX6zfKeWAUj/dLvnxw2E5Px7edzfPbvX7eeXhu/F9kJXz4OCsA4edu+Np0pNtwf9z8dwx1z74a+rbxC/KAuVRiWJWQAKWnay2Twt68nZloyn3JHnoXxvOAm8qnE4H/OThlEsjlJ4MnwRiRVremCXiB91D5ZOiUJhYPO2nTdamNg/8flbt3Cy4Sf7agZEWONUIWsRy8NCGbz8s6NzZKoFf932DAz4lmMzkVXC8OChLee+UDyt3a6EKOCvdSFlSm6Ny6JQdXJ6pndgyU0JGdn+sfBKIqxT7uKBUKBYFmqKBXoXXNrnriiFroVjcUPoxCq1h+vin1tq7+hBq17StHofzCe1rc9aKITlS+5jY/jm0fnsbiPZG6/UaADp6oW32JFNTim9qnzFkvBiTfqzN5vCSernc+O/obihWX6FwMfccndwUfSjVF2VaqXkqxZ9oNpt5xhF8HGmaxvvIg/a+72g7io07Kb1a6D3mNpRHTKYxYQ6FDlnW3HXoUIqNG1POSVPyilrG5ioR39H+KbbQ7gsXC5v7ZUJsIMvpTEMXt7GJzhyT5xYR3KrmHYVpSNlPteCX6dLkmgM07HoAPTTieeNHQ/Iv329vb73wofLg4d92ym0jUy1ClFL23kPJtw8kGpMW8VJK4eTkxLYBupNyW5qqjfQBXbK/D9kscWCGu0lQOnaCQgiYk3LnyJ6riIy5S2Ul/Q5ZnMZkyKWmaXB9fY3Ly0u8evUK19fXHcVUX5o0BpGFrLT85vEJwCNAltpm3+ZZfnBAT9mX6FhwqTzIWYdSGDlehpSV25LWxgKlKArMZjPMZpUFwkyanmTWrfunvN88nJPd8PDDqCR/wMni7ooly8nCm3fM0dbz9nmEoqhgtgpl+8dBWWXTa0vC/hnr0AZK1VbWqjJ3wZo2BZTlppWpBuDaAIU39aZAAKQ7Mti5mfJHxK2PHD+yXOXupsx0hBfFFa6qC+Jyd8qHqx+XfvcJ+7vbRtwdw7LtUN3yeqYyUQpoGoWybOMUqntPbNEFnEwe6Aesn9cGmbsERjk/bvEa+uO8pVsnbdWV1QKw/AkmB/0OAbH2wfLAw/Pqj1HAL2V52qEWLPXia1+ekOVryjLWO1645RcEVnX3eOJYeO9IZFbfQdkYKExhbJoM8LTlx38nnjY/PD5Z3moH8tq7axX8u2NJFuUAWW65a9+bNp8FoIs238xCVpXKuJUKBczdy4C5O7a5bbB+uQ4NF70k51u5rsnZh8t1ypj5T56UEZpXQzTlnnIflLNGleuUUDmE9gWpdZg8bWbbfQX/SO6+Uh8QQ+VCHx3yfTIRrW2mbH/70IPG6v+uFOsxfXBsj/Z1phydW86HGIdEOXrevvC77jfyXvMQyX7F9SmPHj3C06dP7ftms7Ft+fb2Fp999pl3V3usTO7LXLct7aLdxvSboXCh30RSrlwg9lDpPowRU9Mu8ruPuXsMjbozdsoBKFepvmseQ9JKLUr65ALCxxcYpUnRkTuWVoxiX0BK/xySk1aIZ2ghFpMrpHAdIoN0y/mia1+dbqp0djW5pxTYQ9sY999l+eYsVHn6qaNTD4X6yp/867qObmi27dt3UT6hBZYcN2JtNHdxtqt8yba+63SIKD1uGQvAAsJA/JiyXdCQsSlVZqnxJtTnZf+XCkjeZvraSaotxQA7Ui5sMxdzN9r89c2HOe2Ml427j7PAarXCF198gZubG1xeXmKz2XTaSqwc+PGFoXrhROPUkD6Rs8aR9UlK6JSVbshdKq63VUiH5OaK6Y8+OsFv/uYjfOMbx1AK9g8gEFXygHX3/ZXnJ+OHfvvhTRwZ18hMCrQCxirW3RXLAdnZbAZ3NywHYWNgLNWFOabYgYnmN7VNoGytZBurMO4q9RQIWCUnXtVKdcFWP5xi8jheBEZSuH4+Yb/uk9oXB4x52etgPJmf7pOAWOXxCMV3f9wytusOGHB29qDCe998gJOPTyw4GgJOO9WsALJ0te/Uz5g7WcLSMwjshtIgWaH8dHnakTiUrgfEKuXF9X57/QLddxmPhfErQIRJkQwWGoY5v9bfA2cJAGVgo3ckLvenu10JYGVWoRZY5Ra1HGwNrL0tWAsHoBLQai10CWRlVrAuOwwI1b4cnIeVhQBgZplry1GWQ6wcuYyszVk3iPQI+BZ/JItSyoCyZKErjjjWWpuPG7T50EFvptur8rmR76u5P19LD1FspuZE8ivL0n7gFZI9tvY7VCWYpCFKz5z1X+i9T0+SGz5HvpDSet/6kbHU1x61Nh/BPXr0CMfHx/j444+htcZnn32Gm5sbvHr1Krqeza3nnD1FTr/hvELr0KH7y33qYIZQStf0daGh5TZWz5nDN2e/FYub2i/JeSgUJ6X3ydmHA90THkPxQlf/DdUfxuTLLa+v4wcIobFtCIXaR854m8v70HTBMdlz5NxGT7RPmgoH3JZSeqWU313RKDB2SkoVwBCl3dj4OX454fusc7hMMTA2Npn0KVFjE+ZQwKavDHIVx6k8Dx28p9iQ3Ccau3CaKs1UHW/De9cbB66ACH3UkJKNy3dXJBX7ZBHWF17Gk/70+y43R6E2HWtnMcUSB3xi46pMY9v8xsbjoWH64vSRAUWc4iE3X7LPjS2PIQqnUD+XX6uGlHQ8bEzJF0uTiM8xsXkmpjRMLZJpTEmlnSIpC+/bQ+9ST/EmkLcsS/sVL1lz13Vt0+rLKwHGlO9UXyIwlo637ZOxbzMSqwuSRQK/MSVAiHdMSTCkP/b1hY8/PsL/+r9+gPnc/2q6G80fy9wf7B+PK9/DPA3fEA+XFvlxq8mCHU1cYjYjMHYOpeiO2BQYS/nRcMhS6HeDoiihlEZZFmgadzyyufdUdeT0gU1y6/5OUTiOASZzeZm6TFvIynRi9SNB1W49xZ5ULjx+t9347s5C1hwVrdA05lk9nOPh7zxBcVR6d7ZK8LMtLeakwEEtD/SkJiGbhpCJwvInd5egWQig9UBX+Gl56cMPnwJiPetK7gbBj8cLUcQrCNgGnOSRxDZuAJi1oCazeOVAYwh8pXgWWFUOfLXyaAR/e2BtwAKWy88tT4mP59aWr7WM5eVBMiuWntJefUk+yXclyo+3MwJ5W3BX+vM2aEFjBXu3rFLK3C9L/UibZ1G2FudluEH0KYxTikoJ3PF5OrTm4/NdaE1G8VPtWn4UNVTfcNf7rFxK7Vtz9gFD+XO31D5Ohk3xHKpryaVDAP+oDdP68/Hjx3j69Cn+8T/+x2iaBv/lv/wXPHv2DK9evbJr7tAaNQeM2VU+csPE9IUpnvve6w8FVL5OtO2Yt83+JDaej+Up56vUfo38QvPONgBpKB4HY8foO7ZprxKUTu1F90H3YY7lbWIMiD0EkH1Hw2iqMhsCPA/9YCOH732r+zsHY2O0bUHGFqtTTIwhHiEleVmWODk56YQN3XPaNI09aoWnFfriBzgsIGkIpRSgUukbWuDxxfR9yvfbSrmg19C+93Wp2xiYlwKI7gtx5U6Ooia1CAf8Lw9pXIwB7/squ5x2nVK0pRYQcjMzFe1qsTWmz8Y2UCFlGG3kpNKQg9RDxxgC+MaCc0PSUsrcJ/rRRx+hrmtcXFxgs9lgtVpF5/mxRPfbkFWAzGOo7nh/ojLlYbkClsKcnZ3h5OQENzc3Ni/brrNCG1o6tr1pGm8cSG1+Y2ukqfsT522ATTo+Nrxe5IAZf3e8lAjjlKsmnEMrXLyOVJG0fJDOWcWW9ji/2WyGoqiglDyWmD8L+EgFQGClSYqDse3ZoKA2bixyzZ9vDevy2M0PgadKSeDUB1Z5OeeArbFwJIdMi4PDKTlj6YTmQ9dWzPHO/pO3j254Xsdhd7RgN/0GmgbQV2vc/NlzzD86xew3HzmACa6N2CxTG1LhdwLSOk/W3mQ8+ZTpc/AvBspKINg+WfoeyMp/kz+67pyHwBm7Y01kqIvF7yUtZCFnBpRaYFbBB0QJlNVs7pTgq2rDSaCTLF0FCCxB2aw7Y9ty74C10k35cnDLWs8iNvLk9dYLyjKAlbcH8lOqC8LafJB/oaEa5bmjgAFgG+V+a2WPLS6qAnquUT00xxU3N/78BbC6Sii3ZZyYfyqcDC8/ZgopR2lNIK/okApyPq6NkffrRKF1r/zoXpahXAPz+JLXrsr3UPak1N5o3XJ+fo7Hjx/jww8/hFIK3/nOd1AUBX76059amcuyxGw2s/q3WF6m0lXIfsL5D6FcHctY/tsQB6DuSoZ3ND1tAyjydh/SZ/RR6rSvXJAmF+CTMhZFYU8io3fy51fw0BHFXEfzrs0Pp3dl9nbSFPjd122NmARjU4Wx7ZcJQxcYQ9MdQttOOlxxKf0Xi0VnUR0CY1erVee+i9ACfQjta6AbUn59MvVt4t6mL2J2qfzfJd8Q/xDoJgfUsXUS4hED8rgM96ENSEopZWLAbS7PsWPuUJJKmtRmLTU/8N/yYxe6n5H8d5Ev2T9jbXnsxwipDSwvH6kwG0qpPhHjN3aDHdss5WyiQjLGxpOQoqpvPA3JVJZlVFk4ZZuiupvNZnj69CnW6zU2mw1ubm6wXC5hwLE8i/4UceCUlKey78SAWJKBH6Mcuh+O5GyaBsfHx3j8+LG9n3a9Xg/60jXVLnhdE2DIlb59ZZXb1qcgLqv7S4V3f9yNP9Pp8fCq48ZCBvhz0M5ZptLdsXQHsDmeWMGBr/JPMf7UXzTsBYsc1SBArf3N7zR1sjiZXdicMScGtMaB0X1Sfx37+ZRhTFnoTn1THB7e/wuDsxS2ua2x/vQS5aK0wJMERkNAomr/dfNp4kogNAiiUpuF6vK0jwgoC3T4Sl4ddwrLQM6hQOwgEDanD4tA1npUxrUfByj77h1NTMAkAZncMrZNxwKgNK+xJmTXaMRHpMvDEn95JLAFPLWTi5ejHYvJTcPnIerAysOBYsWeuntEMrcA9tJmebfgqyxr1iYoDS8uuiBwB6wtFHSjXVsvnLVsURUoj82HxATGSiWwdON1HluL5ax1+tZeco0uQVX+ERQHtvidpKG9IOe1673ufaC+vKfAwVB9hvxiwMDQ9jNEvrE0RVugjxiPjo5wenqK8/NzFEWBp0+f4vXr152wtN4P6eCGyiX3qDm85DoR6PbV+6S3kHqfmF6GaEyd70Ix/3VR9ktdzLa0a5Altb+X7n2yxcbMFEkdCzf+od80J4bke0f3h/bV/3P1g5Lu4/g0ZT/I5ZWjW7wP5IGxfZU/BbASin/fGt2+J/K+9GIT1baLTRknBgaMASZyBp+QUpdfqB6KkyvDO7o/1LfADwE2h9wGdq2Y2Oc9otvQkLGClD9EBAjxI193VZ5TtSk+jt/lZnBX7S/ET25cOJDXNz+F+rkEBYn416y58x4BTxJ0DPEJpZ3bJijduq5RFIW1jNVa48WLF7i4uPCUnrn9IqTo4TKF5k/ZljkPspIkZRW3jKUyqqoKR0dHWK/XWK/X+M53voPf+Z3fwVdffYVXr17hD//wD/HmzRvMZjMAZr7OqQ+uEA5txOX6I9Q2QsoxWac8j9tQPL62VrFAF5DlgJoDyhRz5/4UBp5/vNsqL0w8LQ7Cdi1jq4qsYqUlbAqMdemZo4ULKCUBWXjh3XuXB3937ZVQHt/frwoDPBo5YmUUa48uTuq3eRo+vF609p/++EAZ050wdI8r3Q3L+6h78vII17Hv7/gVhbs/lo6CpqHTWHEb4Chk7NwBROm3aE9etSoXrhNG+PFnKrz3TmCr124EL17dLC99QCx393hz6ryqjpvHO5M6YVnT4cf8BoFG7WSQ1qjB44m5VSu1M2iPj5QlZBHrvP00PXftl6G0WLXuDMhVqscyVvl3x8pjhkPpeMcL87TZ07PSLVx52zthCw00rp3pok2zBWLpmGJyK8oCTdVYS1lViPJh87BSw44klWH4nBe6Z4/zD4EnUhaaO29vb/Hw4UN88sknNuzf/M3f4Pnz5/YDqVx5vy4gSA6l1iC5Cn55NUdo/dSX/n2sDyofWl8ul0tcXl7iF7/4BYqiwKtXr3Bzc9OJw09WCa3fh5bFkPCp8o4Bsqn6OxQlNF+n7EKWXbTPGM/7Pj7F5B+Tp6n1zgDsNTN91q+xMSx2FRF9QByKE9pP8ncOuF5dXeHzzz8PyrJer72To2LyvqN39I72R/d9zCbqBWPvEuzapoDHTj7b8pc8QkfPpDZcoUmEb6b2SaEJp+/+2pwFWShMn7KVx5XlJ+PkLmZziJd77ibnkGlXfSqnzvv4heqvj0LhcsDaUJwp+9dQfqG+FltQxt5zyngbGXOpr0760gy1CwIKyF8enbZrADo0Xo0dA1IbJj6WkfUvPcdudnc5VuUCn9Ivp/xkmL72OqaMeFvi/FPKShk/FD42xmitcXR0hLqucXR0ZEHLWJyU3Cm/HCVcim9q/qyqym5+T09P7dFw8/kcVVV1lMFDqW9zz2WMjYuhethFPyCeVVVgsSixWNBdqkB/dfqokVtnREIrHkc+U3FifLrgrFIFOzI79ofAO+CQixwaqvSMgau7pBYZSgC4XmglwVed0QYobjd/flwC7LX1o3o09SfXqboTxvj58ZULZMX2ADW/iVr+EnTzrFI5H9ZM+PzmWa8yf968vHSEHKH4HQBV+fL0AbGhLqVE2XTyHKpfWYbCr0OxpqWcvwVOCYC0OCRztx8MMOtX1nxDgKyXhmJHBYv0LeApgF0OnhJgK/Nj+YbyItKntDqWsPCBYHt3LLG17drlvQPCUhqKAb88n/zJysCTn9IIHJHM3W28Vk5VuHHWgrxbjGdD19BShxBb1/B4FKdpGsznc7z//vs2D8+ePbMfZ6XWTym6D0o0knHqvWFu+QCuTvrWfPSM1UFsXzZV3rblNXTdzsHV29tbXF9f46uvvkJRFLi4uMDNzU1HF5SzTo+5jwFut1mHj9Fh3ZVealdr632PD4cyHo3dt/TtnVIkx+8h+r5UfwntKXP2oLH0QrIN2ffzsHJsl1cF8tPX+DHFOW3zPsxvQ2hf+ZkinaHta2o6tLKaenwew29oeQwpw23Ku2+Nt6851QNjt1H4ft1IVj4HB6j8FosFjo+Ps/hdXV3h8vKykwZRboeTmylJsUaXA6bF+I0B4saEAfz7drkikd6H8MqhQ5hMD70/7mKgzwUbhoJiQzZf21Buf40BCkop74u93LFAjt+pRfM+23YIbJZ1R2VhLLOMbHTs6YMHD/Dw4UMb//z8HLe3t/jxj3+MpmmiZbWNvLsqn9BGoq5re/8mua1WKzRNY7/w1tp8Cb6tXEP7TEpu/ox99RpKi+bLvo1aKK/8SD/eh+q67p3f5CaXFDohSrWBIRtGkmmz2WC9XuP6+non66wQPy5DrFx5/XHLgdhHY6G8n56een2X98c+eaUCQK6rlHLHlEvLhticz8eYhw8f2vaxWq2skm7K/v2Nb5zgf//fv4WHD+dQSo6zIbnQ+euG5X9yjkg9Jbpgfrs8E/gaspAtUBRkDas6f8ay0rf4paI3vPvaNO/nofnKhDFu2r5PQUpxWc1v7kYoTGh8pHLN6bM+T8mDftNLqM/68vntw0eK5N2wPJz745axJh4N0UVh/jywSIX7ubWSpaLi4aiNBtz4uwRn5W8vXe6Hrp+0kPUAWnSf3m8WX7rLOB44GwJgFfMLxOn4SUoNQwRwUlDtyxmygpXgKD++t2MtG1DAen4EfKILiMo4BNiG0rIgrEInLOWHg8NRi1jxtG2EAbyddKjOuJtSXlivHkQ7lffF2mOMFXzLWG3K2lrGluZjAlUq414plMcl5mqO+rrG5mrjjTUxMDNnLTpEkRRbu6T2z8fHx/jmN7+JxWKBo6Mj/PKXv8Snn36K2WyGqqpwe3sbXMP17eNy6C6V2qE1765kCe2ReJrcjdZA/OhMvp7VWrOPqrr8d0G7LhfOn/Ya9PeLX/wCn332Gf70T//U7pPquvZOzAFgP96dUi4p2xg+Of1l6rhTE637SQ5+us5Yfu9ofzS2vMfES+mlpJVraAyT81VqbJN+qbl1s9l4p60R8XE2l9629ruvufBtKLddzoXB/cxbSF+3MgxaxvZN7kMHpDFg3ZA0hg4KUy9eYpuYpmmw2WzsMXA8vEw7prwMTT45lKc0im++UuBrTrhUvCGyybTGpH3olFrUjy2rXZCUMyXbmE2F5J2zkX9b2oCk3DzFxuypNopjKPfDjli4kLxFUdiNdVEUODo68vhN3Q76JuttFBuh+UcqWSgcAbVc0dI3RsQUeuQXAr0kjS3LKetCKqRS4aQMfcpJyTe1oQuNR7njk1SicfBXxpfhcvIRSzfGP8aHA9sx/qG62Gw2uL29zVa6DFkHkEKR1kYhoD41V1D84+NjFEVhN9nX19eTrv201lgsCjx9usBiUaGPdczfuBsEIMWD/Pxnn7Le/83jKsXbnvLkaEP18jekA7+1/XN1pzt/rm7DbWJXoGzKbSpK8eZ1kROWP1P8ZP1S/Znf2vPTqwabF0uUZzOUp7MQWxD4BMcqDjC2zaUz/lgZlNecOnxCcQPpB8EzCD/O0wkeB+ESFLOEjYGwoXwlKVTvJKv2wcqOFSyFZcf1ynce1oKqIj63oFXKHV3sWcSKPHKwNZaWBUIlKNvphy6vHQBYqY67BUxzOi/PnxL50c7fhuXxZBhq45QXJeQky1ml7NHFqlBACaiZefI88zkstDYOrT8orIyX2gvkUGh9ytee/MqQvnUkz2NOujk6h7ukfciSWhduyze0V9gXmJfbBkIgcqpdK6Vwe3uL29vbzsf6EozN7QuhthjbV+XoK0L8t/HnYcbKMDXJvY70u+80tnyH6GKGgIohnjFdbl+8sTLlhB2iXx4SdgwN6Vec5P62j8/b0N776OuQx33RUH3vfaNd998+PCKHz12XbRVyzDliLrawC9E2iuscuutCjNH19TWWyyUePHiABw8eWPf5fJ6lKI35KxU+cz9HIbqvhfdd0DaT9tD6+LpTqqy33Zzf9aZi15QD4siPM0Jf6/XRXZdj38ZC+ofu4aTf9GELYMpmNptZ5ZDWxoqUH2W8jcxTjSGcZ4g//S7LEvP5HDc3N7i4uOiEnc/n0Dr/SOa+PPD4Uy5KeF3JTXkMQA7VdR9/zo8s+sif37nalw9qU9zqMiderrwpIkURV2oCrt+P6e8kU0h+eVeqBF4p//yOV6moI77r9dp+vfzmzRv8zd/8jT26mNylAq1PZl4GPF5VVVgsFvYre1K20fHOIUWU7F9PnjzBfD5H0zR4/vw5Xrx4YUHebb7cp7RM2ZbtE0ACTHVgmRJP35/F6MSNy5L7W7Uyuqf789N0pAP5cqCee9cwR+FqAI11c38NtG6YFbZG02jWXrr9aggQq1QaaA3/5ihMViosfF7cUL1JWV0Yw9PUTxihM21Bd9qOfFIbKwpTtlS/RQE0jbGKXf3yEtd/c4kHv/M+Tn/7iQOf+B8TzbOQJTelHPhEY4VC0OI29pSAq+fWpivTt08B9AaBX8GHx++8c7cQCMvSCAKwsf6fQmRbL34kLm9mygXwjiv2QAIFZ53K3j2QlFnT2vGTQFcl2hsBj1xMDs5yvtwqldKGn54FYSl9YZ3qgb3KDxO0jKVyU4K/eOf17N01S/xYO0MBM3TROwNf6Y9by9r8F20ZFsJCtjDWsUVZoEEDVavg3bF8DqN1gPw4jz/pNwevQiAuf8aAEq5TCH2UdXNzg1/+8pd48OABzs/PsVwuvbA568ghOo4Q3fV+ZtcUWtfH6pN+0zu1AVoL8XYTWhfF+N8lUX4Wi4V3fQdZuS6XS28dGlqn0v6PeHFL2ND9xkNkiwHE/vqp26+JQuvbUDpTyLcv4mPG205TlO++62mbegm105wxOhXnEMbvlDwxvcWhjpnv6B0dEt1V/972g5NDoI7mOraoD1FsYc/pkAeuIV8sjSV+rB6nKdKTi8MhNOYLodBEG3NL8RoC2ueUU2qiHPLFxDaA7H1q91NS3yCYKoe+MPvon/umXQI9sTZ4V2UnwRzuHqKQsinFOxR/234X4xvzy40r3aQCrK5rPH36FOfn5zbc1dUVVqsVXr9+be+NTaUVSiMUntIOWdmOodgYG1KWpOSVsqY2RbKuORDbl5ZU6EjlI0+/r62myi80vtV1jdevX6MoCiyXSwu6am0soPuA9pw5X5alUs6aRQLWMl7ORpoA0bIs8ebNG3z66acoyxJaa3sSyBAwOVS3oXzIshkyfwDoWFwQjynm6hAo5uTw5T0EislqSJaHDvwmhEL6pf9M23NgrNayz8ae+yelusDurvhyN1knqbblwhiUyPgrEFDL68jwMe/EUylANQDWDdBkjJs+8uiPDwK09IBOAbDZ8DKNANDrpcfT4E8mC7eY7MiSS1Z0P08yDx0gNlk/PWQxQj//HQtSBXtccequWP7uAbIMILVhCZQUVrDEw7NS5ValupuuLR8tgFeRpudOWab4lL+UhaxSrlyUDwBLUNa7y5b4K3h5srKxcpZt18YPgcEqAOzytlkAqul+SMBJzu9D56dY/NC6Qa5vUvPuarXCl19+iYuLC7x69QqXl5d2DSmPleS8Kf1p5tjDmT+3pVi9xgC7WFhet3LtOhXtWpfBAeWiKNA0jQei0nqzqqqt9DuhNGMk96655RmqvxgAtwtgbt97fm4tL2W4S7kOgfr2JVOVxVR6shydXR84K/vjUHlC88UQfXEO/yHu7yifcvVfh05j8YKvE+XovfpoH21jirqcSkfUR0kzoqm+3kplJlUIfYuVKRczOcrit63zjQFw+4DX3I1X7CvQvkn40OvgkNrJPmTZ1UDVt+h7W4nyPdRiK6RYOaS2CPgy9o0bORuDMTTlnBIDm3Pj0tHL6/Uay+US//Sf/lP83u/9HgCjgPjJT36Cr776Cv/+3/97NE2D4+Nj1HU92mqSfz0PDG9jRCGgV+Y/pEjid9JzZZ0MT2Glu0yDg8mkFKRyzWk3JIsEJ0OWvaG4IZk48bxRWa/Xa/zkJz9BVVWoqgpXV1dQSlmrUm4tKylkOR6ygJBxiqKwd7ut12tvwxsDQiVPznu1WkEpY7H9y1/+Ej//+c9tmNlshtlsZu+QCwGgMXn5uwPr/LZDsofuDSYesu2RvNtazYfIV4CmNvt+Hv2wygsj4zCXLJlcWvRblnM3jtZUltYFgDs62Fi7krkYNx3jRJawdfts2F8NoEHT1KjrjW3vvJ75xxQkz12TUnmALIXjTxeXoU9pLjacz8+4y3r169f3JxTIWc86a1pAefGLAijLsHVPBzAlcAkOmOXApI3P2rv9x/z4swNeAUEeNn04GToych5gafE4oTQ4kBpyk+78fap9SiiK9uWwfYLS5lawlK5G97hgBc/y1QKb3E2L+2G1AD+FO6Vnx0CWLslIZcWBWA6EcuDW5ov4iPrwnjw8vVMeuSUuS9vWd4B/x/JW+XfShsJ4PBU68ZRSBoBtLWSL0gBNZC3uVTNbE9nmoJSd3+U6KDU2St7yJBQ5X/I1lJw7aU6/urrCn/zJn3hxyrK0Y3honSbve4+t74co7/alD9oH9QF+fB0aWpfF1t0pHc2h6rmapsHR0RGOj49xcXGB29tbz78sS5yenlqgNtSWYuvfMSTLIbTn4e6x/YJcV0+hxA7FS9X5lPUpea3XaxRFgdPTU2itcXNzE5ThkOltG1fugngZyT43Rs88JNx9aWf7pH222Xf94x3FKKdtpHSH95Fy9IYhml47FaFdLULG+A/58iflFwobSrNpGru4JGWxVIQ3TWOtSyS/2EI85BZTSubI3dcpuFI5RikwNrSYjcWNKeljcWNhdtWxY2V1SJPSociy7WKsT/kwhE9Mnn2Bv335yAUuiFdqox3rb7ue7KbsezRerlYrXF5eAjBHUd3c3GCz2WA+nwePVxtSjrn+oYVF35hG8ULpEEhGY+r5+Tm++c1vAnCKmJOTE3u81mKxwHq9Hg3G8nRDsuW2+VCec+pcKgI5Dzl38fAyHBHlo2kazGYzqxBomgar1coDa6XMlF6f0iUnT7G5mH7LdOi4taIoPAsAWYayHMgChSt/+oBnfjIHWd7mgNU5YweVLwc5+RphaHsKyVTXtVX4UNjYfBCbNy4uLrBcLjGbzXBzcxNtX9uSkS3l78JxN6VS4xUQRmmypeq4GKCT/+bHBDdtvWr754DVIvCbU8P8fTBWa/NsmhqbjQRifQtZI5u28g2hUPi73dtx5Gg60gBeNsCyAWoF6EKhKQs0hUZdKDSVRlMXqOsGTa1R1xq6PRIatUZ1vYbSvP0h2naVcoCTyZHqNCuv/SoxVjNAK0Yc3O2AnpwHTyuDbyAh/xlxD+YxJF+qHGJpQ+SNSDPQksdhTp2xsvVXylmqesBrCJD1GDI32VRJRNl8Wf6Jr2fRqtC15oUrL++OWUqXp8X5cQBUWseyu3F5WE921masrAoePwKzKQzx9uY9npYsY+LJ47G2qQoF3bThCuNWzAuUZyWaZQO9iX8UFfowLLRmifnZshfg0RAwl9xIPxHac3A3WtdSPL7GyUlrbNhD2e/2Uc7aKgTypQBIXi9SBzRkP7aLfWLfvog+StVaY7lc4smTJzg+PrZhbm9vsV6vcXl5Ca211Z+F9FM5+7Ep8yDX+SH/XDA8pbiWdb/LvKaI7x+1NqfjHB0d4fvf/z5ms5l3zcmnn36KX/7yl1gsFijL0u7JJL+7pqnHlUMGE3alpwzts2Np9emzQ7oAmY50S+mJ+/Z5ofixcfe+0D771V304SE6miG8ctZH7yhNOTiZpCn62rZtYoj+dgyvPj57A2PvA20LGtFCUbrXde196ScVmBQmBsaOpakHlaEbuKFhQ4vWKQb6d4Pr/abUJuU+Uk4/Si1IYzwlX3kf5X0k+rp+uVxapU5Zlnj06BEA4OjoyIJUZGE69mvobUhuKIhidU3Krfl8bsHVx48f45NPPrHxHj58iPPzc8xmM6zXaxwdHUEp5X19PFRGuitzCgopBXIoVSapsNKf5tq6rjGfz/HkyROsVit7pylZyubIl7PpCxHvo7zvhjZz1C6vrq6CfTVVL9yCkO5G5sBkSCkKwJYDAZoE7suwQxU7lGZVVR6/FAg+hKit1nWN1Wrl5Tenf3Nl9osXL1BVFebzOW5vbztlNla+bTZu4WJR1i8E2MpwEc4BNx1wd1avIUAWoDok5Rk9CWWQPDX7861h+dOAsV3LWP/eWJKJyzkNqHqXy0ClALJynYK+bDReNhz8acebipD2th9oQFP5bjSKdYNquYFqXNvqjJMMTDKv7p8XhoXN4WEtBtnTA66UD8raMBQf/m/7JJ7incsSih+lWFyRFxsk1KEDcvel6YWlauT3uBLWqJQDb9smZcFN4hUDZDkQ2fKw4xkHP+U7hDvxIBkZWOkBr2CykZzMstTOoVSmjE/seGKSwYaHnxYvNw/0ZWVB/HhdBZ/KycwBV24BG/vNAVl7f3KhUBwXmM1nWL9Yo964Y365JeyQ+TkFcsi1CekpYsQ/cqNnURSYz+f2ZJaU8qwsSywWC2gdvof+HRmSZSEBxtCpIjnlGALuhsqyL6K2eHR0hOVyicvLS/zWb/0Wvv3tbwMwZfL69Wu8fv0af/InfwKlFI6OjqxfiB+nbdafueFyAChOQwHZGOjMAftY2KnrlT7m5PTgwQP8w3/4D3F2dub5/bt/9+/w6aefYrFYYLFYYLPZjD6N6W2jux4Hp9SppcaonA8U+vjtuqyGlsVQ+d/RtHQI5TulDPvIz74wkDEf+R1CfR4CeYhgyFpz17TLithl40sp80KLbP51aGgzNERxTDyHyMRlix0R3Ecx/rnHQoZkGZK+jLfrOPugFNhwl5TTjmIAwF3T0D4g2zV/T7WbIfWVWpRyv9A9nqHF4pDN4l1SamMpFUa8PcVAJcCMN7/61a+glLLHoVKYbfIbqiNe9jF5Q/mJhUmld3t7a61/AVgAieaFqqo6XxcPmTMoLIFnUr4xC7ZY2imZQmCl/E3voeOKuUUmKbAePHiA7373u3j16hXevHmD6+trC7wNyVdM7jFjXCwOv/tKArVSabrZbDCbzfD48WMcHx/j0aNH+PLLL237lzJLN+J/dnaG4+NjXF9f4+bmxrOQ3WYO4m0x1ldCFGp3xEsqIUmhO3TOIR6kBKK/2Wxm873NeKGUwulphb/3957gww+PEdJvaw0oApmU+e3ij046QSGQlK+znBsHX7VuUNdNq2yvUVV0DDEHY6nMFfsDc5fHFHMQdoPNZm0t+2k8r2t5dywf//mcPE3pcOrj6QDhqYkhSC19uqzx4raBbuEhSpsD0xoKugAaKOhCY32kUHEBtXtay0QCZJuWV6Ohao1NcQo0rbWs1kCtcas16r9x8w8BudBAdVLh4ScPoaouCOkBlRyEYv4dsJSBmR2wUvnjhxdWsbT4kxetkE+GlTz4e1AWCcT2rcVCfMYQpcfuT/WajkLHqtUCjJr5B+6K9QBK+HE7xxOH7pEV7dc7LpnzBJNd5EkeH8zl5mElgOxZ01J4Vjf2+OPQcchcRlY+HHTu8FUMbIbvbsuIyWUB2cKVqwfIFsrI1hi+oY+X+JwYO81CzvHyWNsYaNO3Zgulo7W2d3dyWWK85NqC8vE207Z6idhv/iR3efqIBHF5PL6WDu2t+sC/u6CqqvDDH/4QDx48wOeff45f/OIX+NGPfgSlzGlB/OPcMWu4MXsdqSMgPrIOcq+D6dtryLEgFo/772q/TzoTfk1OURT48MMP8eTJE7tXffTokZXh5OQEgH+FzFTy5epeD5EOVcYp9v58HxcLH4sXkyNnnxbT3cTSC8mVCpta8x3CeLktbZOHbdtz7vyzz36Tk9ah9uMYDZ3nx65nYuFj68ox66Vc911jgDFZhuh9iaqcQDLM0Ea4q0a7bUFPrbiP8aWNDBH/slTGy5Ep1IBzyyJkgSPl4/Kk0uR+Y8oyt71JmfY1+aU2LX1lkRochgxysU2ZlGfftK0im3jsM22+WeJ9gB/lKeXaRVuTi7++9FKbsT75UmXVtynflqaa/JVSVhFE9PLly2A/m1Lhw8e83LLJDRfqz6vVCtfX15bP8fExZrOZp0yJbWRS6fI8hL5slvLLDwJy+XMeKRmGlKVUNNBmj/JCficnJ/joo49sW6FTJohH7OjcoXmLuXGZ+8LEjonmPLgipygKPHr0CI8fP8a3v/1tbDYbfP755zasbBcUn+f75OQEDx8+xNXVFVarFY6Pj1EUhVWsxPoqV+aGKHRc3Ng+IBWIvCxDHwr2tVF651aYSrm77qYY24+OSvz2b7+HBw9mA9r1OL+hRNmTPLX2y5rKl5Rmpv800JruhgWUInCWmMkxRLO/7vHEdW3A2BAQ27TH55J1LKEeRk7O3zx5tQ2pQgrbjbPb9aSGRqOBRtNv7ZWWBvCr2wa/uGn3BbyYJSkFlAC0eVaagVhtVmy7pt/sSb+bY/NBiG5a90Zjo4H1VzfOirY92lhvNBaPFjj/zrnpZy04yNOSxwxbABYiLwzIMtlx+bW/0Q3vWaKKp1XyIfyUv0NxOzJKssGU5xayFI6llU2yOSrj1rFCJRkkIMtAVw+QBTxw1rFXXbAxJgdz99JhvEwQ7edbCz+0c4uQReZB5oXniYfvhOXhKS2tXXtiZeaBzCyMl17bNrXSwaOeOTBLv22b5X+F+VON8oZQOX/JtU8fybVqaH3JKaZ/4PykG19D9SncCLjpU7YN2cvlrNXuak+co9DvW0sO2S/JtZLkwdd+9E5/UtdzSGAC7U+qqsLf+Tt/Bx999BF+8pOfeCew0HUtQ2nIniYUPvQ7tp+I7fFDfXSo7DK9HJm3Jb6foD1DVVV4+PAh3nvvPVtn5+fnVobFYgHA16+EPq4N0VAlPY9H8g6JM3QPeN+AmF318aFjVui0tiH6tpy5LUfOnLBD9Bpj0p+6f0re+2qjhzR/jKH7Lv9YysEoQuH6dMq5YwKFzR1fp2zPQ3kN6b99Y2DMfzAYe0iTUM6CZshmZF90SGU4NeUCrPsC4mJA1r4WJ0P9UyRlvsvN57aUWjSnwMdYnCGklPJALqn4v76+Dn6ccCgkFempcEMUA7umoRtQTvJYd6AL0vD6HEPyi2YCuuTJBjx9vsGIUSjf3FKP8vD69Wt8+umnVpZvfOMbWK1WNp90T24IzMtR3nHAJSQjz5PkwwG/2GZI9mkpZ99ReiFeXCap5BhDnFdojBmryAylEQufs2ahsqqqyt4tq5TC2dkZzs/P8fDhQ1xfX2O1WgXbX1EUWCwW1oLg137t1/Abv/EbuLi4wPX1tbW45mDsmE1uKE/5oGR8XI/ND1KZFRsDeTwC7bklBfXtMXL76QBFoSKAp/F3fvvaHMOTx2JzWrfuGlq7jy7quoFSBJiafm4UnxplCShF7UvBgKwSraAylJaxm/ZYYmMRu1qtsFoZQJZA2aYhUJYsdJ11rIMqu3kZ4xbyl8+xFIu/0Rr/7aslXq8amGJsQR1Kt/3/VaFQHpU+6OglQA8BXjFAFKyeLfDahpdgrH027L1xT60dUNvUDZq6wfMfPXfpsPhQwEd//yMcPT7yQag2u/Zp8bPAmi8AYAb7o3JhO0CoQvod6O+CAsDjbtH5KpSvTnYG9P02qDeusa7mHQGsjZ+1npXHErP0PcvathwJfPSsYTWzFOWWo9oBnhy45EcQWzBVKdf+2vKyxxSrgLUu1WvLw87RYE8O0NJdrW3ZeECo7WM6mE8qJxmncxSy8svLpiOOJA65WeBVA6pQ9kl/KLrreKUUTk9PUZYlyrLEer3GxcVFt3m0bY+vp/hHdiklF1FoPRXaV8j1Lc2bkgefizebDZbLpXe1glxby7zk0C731lNTzt48tm7hew5qC7xOeDnSGkZ+oDhEKXpXRPeNbjYbXF5e4rvf/S6+973v4fd+7/fwm7/5m3j69OlkaU2hMwiVf2jfNASgOUT9ZIiojXF5m6bBp59+itevX+Ps7AyPHj3CxcWFlf3m5gaPHz/GarXCzc3NoJMWY2UTA8i2WcfnjCt9dSrHNoozpR5p14DeLmmbvfs+QUU+bob6e0y+Q6uHQxs/9k2HWCf3hfa5zgrpnMbwj9V1TDe7q/6xDbZ10HfGbruYPPTOOCVImDv4hBpLSgmb22inKOuxacUAPQqbC0j1LYxzy3dsRw/la8pyDdV9StYhYbclqRQYq9DPKS9SNvMNLX11y+9sorCHOLEPHfRDeZCAR4pfXzmn+qCMM7RdUR3wcLKeJK+hm1wO0PB2MZvNsNlsomBszC1HEcOVVQBweXmJL7/8EoBRVJycnODq6srGobtQc9p8LP+U5jZ92Spse5QJvM5CoHnoPZS33PYSqsMQScWW5NtRZGfMH33Kr7HlXZalBWObprHHgdEdWxKc5LJQXMDc6/T+++/br9VDZaSUiipM+uqZh8kdg1NufWNTKK1Y35OKs1j649Y/Xd6HQFoDIZG0Jj9f8a61/3EIjTNFQSdI8DFDIl2AQSxoLHOWsXRHrAFfc+6K5UAA/WmWBnp/j5+qt5vja62xqunoXwNq3tYan99s8HxZo6gKA9gUos20xVlUiY96NAVlY5MsFs3+5Du5yVOnFfOn34UBrQiULVQBaGD5Ymnc6K82f1DA6jdXmJ3NoEvzXhQur+WsRDWvOpaCSvnvlD8f02RlxGlAd4tZyEqANBdMDY5dkbjbHFccHPuojlrentWo9sPYOYH7iXjeXBcKF7MEbduLPG7YS4+qTnflDB2T7Gc+4hbzC5C00rVWwbIM5X23tL5h7VXmnx9r7OVNuTYcsoy1+4k2rlLKALO1kaGqKsxmM8xms25+InOWBOb61qDSX64d5NwaWsfwNSB3p3Ut3RNL61u5fh+6bk+VwViacn8r13+pvMZk4FZjfP0WWmfLOg9ZLct4sd+73tenFKXz+dwCz+fn5/jkk0/wG7/xG/jN3/xNAPAA6W1kiLnltoHQ+n5bvZLkEeq3sb68K31Ejj6Il93FxQXqusavfvUrLJdLb19xc3OD+XyO29vb6GmAfWnJ34dCsTGRuynVtUKPxc3xl2Mo0D0CO1f/kEtTjZGhfWIsXI57Tvvg9ZKjj4nxk31/yFiQoiEyfZ1oirLcRg//jtI0ddkO1WNLGXJ0UzE+Y2SYgkJleNBg7CHTkMYYUw7ziTp03F8qbq4b4De0uq6zj3gJHWkc4s3zkTPxHSLlbJhy8jY073c9KNwX2ufkyq0HlTJ3U6Y2ldtQaLE7Np+hr4Bi7asPSArJmaswiZFU+uTGSYWXec4B4WIbHK01FouFHfNmsxkePXqE6+trq1ySyhMJIMTkjo3/dEfsYrHA//gf/wN//ud/bnl//PHHLSBirBwvLi6SgBGXhY/JNI4TCELWkDwMl5N/iS95y/BSjlBZTE00J5KclLZSCg8ePEBVVfjss88AmDosisIDLiWFlAyhtiIpd0ywCtiAUlLKQUD5YrHwFG1ffPEFqqrCV199ZY8ZJqsZ2YZDCtXlcomLiwtb9xL8knXXpySQROUs7zQOxe9bIOeOTSF5eBlTvNVqhaOjI3zrW9+yCs7nz5/j2bNn1vKYK42HkFLmLy7XVEoRng5DOvI52DimTTlFDp/reJt2dwSWqCqKX8ChC5w3QAhf02xaBesKdV1jtVq1H7Rs7Acl6/XGzrOm3XWBWY64aG3++Lssn1Ceu+79/dXVXyx9zsP9/tnFGv/9yxuArN+U8b2uCsxOCx+gAdJVKMXUcJaFLePOO/Uv7d49Pw3oogXX2mefpazW2lrxeZaz7A8a+PKPv0RRFv4Rx7VGs27wwW9/gG/+7jdd/uGe3m8OZjlPB+opF84CXkr5fJXyy5U31cQzBQJzP298CMkaytNQEnVv06Q6Vy6MN2Yq+PfLgs1RrazWXR5X3Mbl1qEeuAsXvwPOynQ4QEtgJ5UJywO3trWAJ6UVusvVSxCuHBS8+149kFfw88qtDWvTUAysFX82PLMWlhazFrgVPGwbVcp86NCOD7rQmD2aoTwpcfPVDbABjo6OcHR0hLOzM1xeXgav46B+Lk8eub297b2GglOuUpnP23KvL4nSXq1WXho582rfWuo+6hX6iAMGVWWOjl8ulzg/P8f3vvc9PHv2DJ9++ilmsxmqqrJztgRq+UeaoXX4oSqn+QeDR0dHePToEc7Pz3F2dgbArFG+/e1v49WrV1BKWeA2du3IrignndheaaiMh6wD4vUFmLytViv86Z/+Kcqy9D4iub29xe3t7VbphfYSU+vgpiCpU6C92xi9UaivLhYLVFVl9yuAKRs6OQtA9KPxXVEIoDzEMWZq+jrk8etIsfa7D53WfaJtMQ6ibcr1vvfBkPy9xxTveiGRQrVjyrZc2hWAItMg2jat3Pz2KYhlfM6jLMvgkZ+hO9Ro8TtU7l11lCFlsUvgTC68xqbXB8SO2UDtc9MVA2d20e+2zVNqYxpSYPQBB5LnGDlj7Sk0pvS5pd5zJj0JvIV4SoAnJLsMF5Kpr52H+oWMl8pPbriQTLz8y7LEfD7HarXKatN94zcvH65EofZ3dXWFN2/e2LB1XaOqKjs2kyIsF4AOyTP0qKgcvrL8+kjWd+6YlQpHIDN9bS+tDMbwDIUlnmMpNz1SepAF4Xq9xvX1NV69emWP/OoDdnk9rddr3NzctOBaEbxzdch6iyv+SPnAP9y6S+Ufycw/KiiKAufn59bq6Pr62spI5ZFDufO9BA+3KQofiB0SjtAC8vdBRg58GoBWsyOLawAKZUmArII5rtjw7M4L5k9rc9wx9ce6rrHZmD9uEWuOJG689LsIZH9+d0U+b7/cGg28WdXYtBaw0ECjNV6uGzxfN+4oUgvAGCiJg4npxP0wHtDWAlAECnXeOdil2bhMgFPj4hCwqpSxglVwIK0H5MK/J9bmgf4aYH21diBso9FsGuiNRr2qcf38Gjcvb6BgymXxcIFqXlm5QtQBVW2y4ztSdlwG5EmQNspPBdzHikrxZPumOhWAbJRHwL9zXDHGr9c9q9IE/45cYGkHeFA4Cw5zniF+mqULV/4eICysV730+HuqzhSTl/NSIh5rP6nfSilrDa8KB4QrpTqgB9Bdz8/nc1RVZY96ff36dTA8xYlRbD0Xm+tSIEMICAzJH3KP7Q2k3y7WFUP3KbvUedB65ezszPsAM7RH4nUl/Q9dgezP/c5QgNYNALzjlylPMcOFfdEQ/Rvgt5u+Phlr47n53Ue5hGS7ubnp+FE98Y/Zc8oulYcx/axv3NhFerF9f276qbJK6TdS428f5bS5lD6qTx+2q7YZGuuG9J+h+/+7oLvcT39daOpxYFc0FhcI0dh83VV/GrKOjdEU2GKKxpbNwVrGhhrc2AlmVwvn2MJLUuhoyRCvbRtxTB7+tdTHH3+Mb3zjGx35fvWrX1mrGaLr62vvmEx+zyan1F2AfbK9o2npLiaNPnCtL+4+ZOZtlDa7RIvFAkdHRzbcy5cvvbC5tM0EGYub6ktSNm5FTwqdmCW8vC8xR9bYBJb6Er9vgR7yC901mssntREJjbHyfbPZeF+fSzlCpwCE6iPVrmUdEc1mM3uMLAC8ePHCy3voI5pYPuSJBqEykeV8V+MxlS2XKWe+IHAHMJafX331FRaLRe8Rf7ENa2pTObRseLuQ7YPPs7IN0e+qquy9cQR0vnz5Ev/1v/5XG3az2di8ho6r4m35zZs3+PLLL1EUBU5PT23ZxfoVXzfljFP01fbt7a13hPjYsT0nXqqNaK1tH7i9vcV8PsfHH3+MBw8e4L333sNqtcIvfvELe+wzt9IcMhZrAYBqAcL68XuzxOJyRI5+C5TOjwlAtfK4cIafeVfKvDeNRlGYJ0BjmgMcCSQtS3Pna1mW9kOHoijbMdrxd2Cus7ynDwjqmoOyG2adX4OORiYgmCtnzR/sk+Qjt1jZca/Y7/6yj7trDaxqjf/Pzy/x/NYcPawUgEKh1kB1XPkgnsXl4qCeYUwP7b0DcNaO2vnJ44rtu2aAHf0mdw4k6TZe0SpJixaQJSCWWc/Ku2RV046fzDJWl84iFg2gSgU90yjmBV7//DUufnmBZtMACvjb/8ffxqPvPvKALKVUFMS0x7kG/Fggzy8G5tokJD/2jFkvR2XgdZyQcSiQ7NUVSysEyNrxmkRg1ptKKViQXsjj3d0q74aNvXOAVIu0tZMRyqUprWVt/ngeqNwDsgrB3ZOGOjk8snqUFrc8bZsfpbr82J9nEQs2p0uwWLm2Z5/Ux5TyLWOV6XdFWVgwlizJ5vO5vScecOtsurJAa40nT57gyZMn+N3f/V28evUK//k//2fvgz6vyDIU+6mrJMhNrp1C82VMUZ+jFwjpfHh6uVa/Qygmg9Qd5axJxq55Qnu5+Xxu1zC8HHl5pNYsEgDrq999U9M0uLm5sfvTZ8+e4cc//jG++c1vevm+ubnBV199ZT/kur29tR+xUpj7SmNl3wfINYSOj49RFIUFXgGz7iYL+aFEPKQVLqecvplbNtvqiHOPC84ZH0Jx+MejMbnpiHtuOb4roj37FDr6MTRmbCYK1XWfvuGugbpDGK/vM8XWDbyfSP0eAKuLOaTyPyRZtqEhepYUDdFdcl2jTHNoujFscewYOAqM3WbAzc3wkIJJFWTf5Mf95eKV/HdJfLM1dELJ3RxwoqNEeNzNZuMd+ShlS7ltA1btcxEZA7AOjcYuMA6V9l3euaB/6t7MEJAif+fkKzfvOQqJnDQ4kEmb+fV6HT26JpWfoYD6EHeZtiS+cJIgeihMiPfYdscBKjr2J3REcQ5JRVRsfOVPrmCQx2XzJ+cfojHzrFT4cP8YcBlTXOXKGAMDY8TbpTtG1RyP++LFCwvGEogpN42hfHFANDT+DinnbZRxPD2qd552XdfeUYS8XYXk5kfvXl9f4+XLl1aRm2tdEJuPQu2Bl+U281gqfF/7j/nzMuVHkYfWjrIucvPQV5y5wwcPpzXQl3QsjHQ37wZt0JrqjMYaDaUaNI1BFIrCALT8ox2lFMqya5FNdW+AVs0sY2s7hvn3xHYBWEJknEy5ZRULyOsewd+5/gDw8rbGm9saGsBtrXFRa9xojUKTBWnb9gsVBfOSJMAiD5Rqf1tQjQFoJqcC5IL2f6sWUAUDhkRYDe38GmdFa+MTCEXWipo3LPYkMK4FUBu0dxDf1PZezIvPL1x+DVqG2dkM5986zx8rsoMNHHtYeO+3XKMJ4DULiE2JEmh3nkWn75EGZGPxWTuyfTqUBsmZ0QelRWpI3s5HAEwWz1/6sT4kQeCgBS7/nZJVhPPKLVF/FsAVQLcFlXl85fMiYFa6y3e6W/Hq6so74rNpms5YDJj9/KNHj6CUwsOHD9E0DS4vL5Pr1b55PBQuFD+URu7cH9un7XPPm9p3jd1DbCM/X0PVdW1PQQmBqXJts++yi9FQHRatC+hkoF/96lf48Y9/bK9v0VpjtVrhs88+s3mWa9dt8j12rT42zi7qaJv97hiKgYL8j9xyKaZniYVLrf33rXNK7XvH6JJpfc0/qD49PcViscDZ2Zn9KHuz2eDq6sryvb6+xs3NTeekt9iph0NpKuBhbN3JdjdV343p/YdiEKk03tF+KRe76Kvjr0Nd3iU2MnS9MISGhB+b91ibGqObndQyti9Du1os9t1rGiKp5CY6hAXtrunrkMc+GrOQ2HW5DV0wvavHPJIAgdbaKj0Ac4QnLXD5sUgUd5uNbqpOcxYMfbw5/7IsvftOHzx4gJubm859mTFFyCGQUsqzAI1Z8PUtIFIgYghIIOLWCICxKCRrv1hafRuIULoxt7smDuDFZAuBtfI3hYuBWhx0HjsOE+BK9+n++Mc/9qxHyLKUyxU73rcPVB4qG1HuxlOWw3K5tF+aSx4EaPWB1gS8lmWJr776Ci9evPCsZYfIHtpsciCYPlgAELRYCAF4uyb5YcWrV68sEDvkqO+cOm19WjBvbJ/mCEQYTCVSyr3Tbw60kjvxNPkAtObzYIOmKaBUAz5OlaV5Lwpl21lZllCqQFl2LdepDZh2qVvljw/GUputa3M8sXnSRwFkpUt50oyvK9dYsTt3x8MPq71wxl97YUK8OZ8/erbEHz1bQpUKKBR0yb6YVqw8BICUQ979nL7I3Sf91pHf3I37qYB77E+Fn3wtpHULTLUWtRyk0lqjUIWJ0/rr0lnSfvqfPzV1v26gG416WePJbz3B7/zffscc72xFZgAWuk/yp3K276G+nKiKUD1x8FQeR5wLxPYCsDH5eD9veYTaSPDIYeHG68qrywAAKo8F5lajBMB7QCgQTMcDKwVYG8yLiOPx5fGVkKf9OMEUGfvAQICjHQvZVl7rxsqfg66dP+4v6ozXdQecbf8IuLUfbRTK1gmNhcvlEsvlEs+fP7f7EPKTaye6i/vhw4eYz+f45JNPUFWVPVFFWnlIkus4fqIFufE/crN1oXV0vcjnSHkCi5Qh9c7p0NbKUxKt1wCzd7u5ucGf/umf2vUb4K9n5AeGHJCVYNih7O0kaW0swZVSWCwW+Oqrr/DFF1/gj/7oj4J1LS3D39HdUKhu6LSfsZawQ9LaBU2dTmifGdtbhq7Ims/nOD4+tvF++MMf4tvf/jZ++7d/Gw8ePAAAXFxc4M/+7M/sOP+Hf/iH+O///b/j7OzMO+mNH3U+JW0zVo8Bh6fao8d4Sne5Px/C89349I7e0XAaM6bk6F+nliUlU0p3mhrnPDD20Ba7+UqwYbIPHVjHXCI/Nmwobm780N2XADzLuMVigfPzc8+fvrCSxxRvNhv7hWJMltB9a0PKa9s2NzR+DKTJLecYEBEK1ydDyF0qsGNWgqE0xgxWuXQoY0Mf+BYj6c8VFXQnKOA2vDHLy45CMiFbSNY+UDRGfXXNgREKT+CsbENSeTJ20knJN5ak8qdPoZTDT1IuCLptnnLicyUK/4KV2iDNPyFQM7bo79sM5LbLIeNcSBEXG2NjbTfVHkMbIwmy0p88gnuoPCGaYvzrW7jJdsmVbNxNbiDleBSqP2eN6I9voTKRafK2xtsrp5Alr1QMDhlvpiA+X5KlyXw+x6tXr7DZbOyR4AQky3YoKSa3dA4Bh1oD45oQISgxfmH/UFjz1NDaIBL0dMcW8/VFDa0LABso5aywDXhLCIOrW63d3MLvuOb9ku6K7R5JDMsHFnjVnvx+eaAnTD7F4r1e1fjZm7XxV8CzdYOmbEEUpYACKBQD7Nhv45CTOAVlcRm4ZPkywIaH9/q7ZnEkgKUjlrIKzgq2PY6Y/DxATgn39rhaFDBuBKBqlof2z/YnVh5kIWvDKuD2zS0++2+f2XTe+957OP+Wv0/p0NRLUiWeQ9JR8lWNl0+0BeLXB1Zy9zDbLoia/Tslo3LPGKgak4e3Y5kvw1538xiQxc41rQwUN5Zu35HNkrxjijXrcxy45eUCNiYwv+A4oWDHFSqL0MdCNH/T+2q1wtXVFZ49e4bLy0t8+umnePnyZWf+jhZbYL/ZyXdgvUHukgffV8TWhaH0QvfX83VRTGG+DcXWXOQXymsfTbmuCV31IPd3fM0S2nvmrN8PhSgvpMOKrc1z2/Y+aag8feH79J6h9f/bSFPrs3J1R2OV+7n7yJTukev8lsuldXv06BG+9a1v2atWAODy8hLX19d2rPjpT38KwJ1+CHSvyBurLyUa2nZjbrF5p093keIZ459KLyeNIfJsMz5t04/H6hXvivYpY6pOuCGf+eC4K1fIsvzrRHG9R1j3mBt/jByH3ra30SUe7J2xnO5DJRClFOQ5ceV7jiKcwlZV1XunytnZGT788EPPjd/xxen29hbPnj2z7yGwl0AfkoXf49dHdBfeIRBfBMWIb4Z5vKnloGfIgicGLOyS+vIY23yPoV3lS26uJQBLbT8ENki3kNJ+zCJsSFnl8ObtU2tt73Hk1nUUJqdOYwqhMbINodQ9MW8TyXKTwDMdJ5+zGAwpsPoAydQ8FVOchXjHeMXS4gqkkLx9vPlYw3lRu46lK/usvJ82lecpKMVP1h398Y8nOMn7oUNKU15O3KpClkFq7OLjAOch60DO+1ImqTzMKZNtiSuu1+s1vvjiC9R1jbOzMyyXS5ycnGC1WmG1WkXnr1BZxjbpWhO4RwhFmnh0Uz4KoeKgcL5fNw0/HIGtgLtD1gGwTn4NU0x+PWlNlrFlW79cQerC+3xcP9TagbJkMcuBWLKINWnxp8wPL+NcC1n3PmZq0hr48rrG//sXl+0djwVQAOW89AEV5QAlS0Oas5SNwB04QIkDsUqp8Lt2YBGBsuTHw2ntwqBo4xXsyOHWzT7pLySzFr8jz0IVFvgl8LdRjb1TFBWgSoWbFzf48f/zx2g2DZpVg+//X75vji0OFGjHLVXmgbqxfVzUX+h46ZRV7CQWsSlyXdXyDR47LIDaFHgZ4hE68jgoA8+TTqcDsDmdeIR4iegeQCvqIpIhn2eAX8dSWGnfn/oc/W7bKv/zwinGU8oCNz5498sql7adQ5hbURTQhbbKc9pT85N6aEyl+fj6+hpVVeGnP/0pXr58aS0KQ6dpRItPrDmIKC36zcOG1kn0LtdUnF9s3pd5jIFPU1EuOHFXYFdsPcbd5Fo3Zz1+qMTb9WKxQFVVKMvSu6uP7pfNsfjepZy7XK+O5X1X7fS+0r72HyldS0yGoihwdHSE29tbawijlML777+Pv/W3/ha++93v4vT0FIABa4+Pj+0+58/+7M8AGL3BfD4H0AVjpyK51wzlJRWmb885ZVvO0Z3wtIfS26yjelsopEOjq9yIjo6OrEU5UdM0eP36dcdQ7W2gVN/lNES3HZsjU33960qyTJJgbKoCdj3xxxbMoYU6V0z28QLcRc0hXqFNRIhSm4YQqJfDN7Sg7gMIedhQniR4SF/TyjChr0IkUEqbRE4yzfl8Huzk9KWWlIUfa5KbV8lbUi5wJSdhnrcQX34UI4XZph+E4vIy4MfOcqAtJn8ODRn8cjetU8UbEz6XX6hvyc2tvKg9Vb+pMWmMfEMpBB6k5BjSVkP1FwMrdjUP8LG0LMvsr9OGbkxTSgytNd68eWPvPOT85W/53qdQCqUr64hv5GIgowS5UhaPIT6xMCFrhVj4UL766oHLzYG7nIWfXPDROykhYwoqLnfo6DzuzkGlWF5zaKiyj8qAFv/8blhOZAGc4i/LwYFpkCk57AABAABJREFUAYAj0C5kGUs5ZVuTcnJFWggo39dCnB/r+OLFC/zFX/wFLi8vsVqtvHGF5yk114Zk74KI3d/cTWuAWMjfMqzx42hGaCzxgVzOk/MlXu64YsPTZLeBUgUA+qBHWT5kEevGCJkfB+LKO2HpXf6mcpDl6//2yyxUlu2vYPl1w4VpVWv84Vc3uN40gFJ4eVujmBXQCs4aVjlgpQPsEeU26W41WoDLyKtdOMABZvxdM6CLwvI/5X5LC0Nr4do48It42mfrZ3kXjq9utANUZdPk8khSQAED0BZlYdMjd0rv2V88w2a5ga41iqrAJ//0Exw9PgowjBMHUvuOjPbGLQHAdtwTrHKB2KD1ZajAeHmSnDoSlsXp3Gsq20orQx9oG3uGwqbc+NjPrV49mcHmByGrF4Zb3qbKgAGoXjmwMJ6soTQlW1GWNB6Eji723lX3NwGxqlCYPZihmBfYXGxo+LXp8TUS4I+XdJwtXWtA/kMptReVa4bQSTsUjstNT3nFB8Wv6xqz2QyPHj2y4AN98E157FvjjKG+uVxSzt5paJn3KUJD+4Wx+oZ9KjyHlq2MS/ssc+f8xvPj+pdd6x9DlLPW7stvjs4xFO6+0pg2m6rj1D5623Y+VHcwNo3cvXSKR1/cXfX5GN8hMuX0lSH68lj/SbUNLkvo5LFYWhQ3tTceqiuZmvr4jp1HxugP90WpcTU2xgLw5pjFYoEnT554/nVdY71edwzVVqsVbm9v7XvMqvZQaF+yxco6ZqCX09+Ibyr8EFk4vz45+vgP9Uvpj3stY6ceMIYs1nIbUKrQY4v5XQyEcvMQUxoP5ZkbJ9Tgpdt6vcb19XUnDQ78Ecn3kGWs5F9VlT3+j0gphaOjo07c169fewMahZWyxfxS7kPrl0AfXt4hBbhMY8pBToIEvE64lU+MxixyUpQDKHEgJRQ2BYjsc8MhF0tcZtoASkvtsRvsIfFywauh/KYOu2tKtZOyLKNfmIba3djNn6TNZuMtwmLHwIfkGbpooDg8P/LeGd7XOPjB0wttCjgPnkZKxhDQFpKZkwwTGx9jm5UQ6CXTk8AfxaFTBHK+nI/NMfyINK4YSuVlaiJZaLynL5vHfKjE+QH+/BIqa09hLtYtkmJlSOUU6of7KkNOXJb1eo3b21s8f/7cG+t5/4odcc3f/bzRMbscXIyBf13rV60BWSQUV/rFuqsfn6fRtY41ciuY44p9Pk0DKNXYcDyfReG3C0rTyerak7N69UHZ7p8cr3g7MvLH8s3d+obasL9iMgC3LRj78raBqloApCqglANcLBhronvPPrAvkLwQ0neT4Ku1RLQFDt9PWje2ZRkC0yyYCgVdaMdbhitaHoUAabnVLpOD0ota1fI4yr0X2lgFamh7l+bL/99LPP/L56iXNcp5iQ//3oc4eu+oW25jyjrXLyccr7MMIDY6/mnXhpJAK6Wp/d99IGX0iGLBUwKYsfDR45GHhKXXjDVbENyl/ComvxL9JhRfwT/KWJYnGE+ZFoS7zBePq3w+FMeux9iYMjuboTwqUV/X5m7lxF6Oz7U3Nzf48z//cyhlPkbj/mP3ejElJuk3+B5JntzB5//Ufp3WV4vFAo8fP8abN2/w5s2bzseXcu26Cxqyb95GBp5Oaj09ZJ8dkym2VzqkPR+R3PNQPuUHcaHw95GGyh8LP1Y/EYqfI1MOyDOUV6pfyb3nNvWeU1Zy3b/PdpYCJcYAB7vu531lk7uPHEOhPRlPK6VH6OM5RK8R2+PyMPeJ7qPMfUT5kbo7aiPcGGw2m+G9997z4m82G7x58yZohMbnp9ipoW8LTYHF5dCQcTBXnhy3UNoxvWwOr1jYVHgPjI0NLPeZYorMPqXbVGmn3t/R4dF9raMpFqxD6b6WVYw2m42dZKdamI9ZjOaGkzLxBUJRFLi+vvbARCnLEAXD0HzQxJN7ZHkOrxxKyZazgZBKEf6VviwD2edCCpUYmJkjmwEyuoBsDvWll/Lnmw364k+eChAC33KJAzJA/zwpyzV1n5ZUMIQUin3pHR0d2S/zp2i/Y4jykfrisq8OQ2687IhC5UEbzZiSVlLMKkfW8z4VgqH+yI9t5+AhlXPoSGaKn+rLPrDIkQkHBspo7t2Bpl1UwIXtFruP4BE/B7z6v7vxunOcyadCURikoGk0lKKPAxyQbMI5eem3BFrD4GsXjA3zkjJTOXf95e+cJvaHX97gZ29WQKFQA7hRCsW88O6FtaAJWHvi9SDBOOmPgH83W967tV5loCcBTAR2cuDT9i9p2UpPMBBVu7rWMNavFrxq/6zVrU7wJIC2cZaCZI1JT6010MCG0Y12d9Vqd1xxA/dE0x5trLSxxG3L7S/+X3+Bs2+c4W//H38b89M5OjTRUjRmTSvrLgi8JmTonR9Z9++Asn5Xten3WqNygJYNSaFjifuOHA4CuPDHxV7AN+SuXJ5D1qkda275wUBLdDR3SBZZDtZqmOdfMZ5cBuXvrYLWxsofG2QdWJ7ij8YWauuxNbacd+Xa6/T01CoWx+xT+PpWKWU/QpMfpmptrj6Zz+c4Pj7GbDbDs2fPsFwukx8Ny+OHKex6vcZsNsP7778PrTU+//xzOwfv6ijaPvBHrk9ia5WcdczYdc7QeCmg9j5SKP9vS95yKLSHSYXbx3p6rE6C3IYq2cfkbUzYGKAWk0uGnwIgkCdgzefzjhX4xcUFnj9/jufPn9u7ZGNjJP+IXOoPpqAUrxiGMPZj4vtK93G8uo8yj6V96iHeUT/x8S+15hrDc1uaik9fe4taxg5V0OfESU1w21Afch36YoFPUFNbPN7F4igXWOEK2RS/HF6hcKGJn752DSl9Ywr5IXTfBtUp5H2bJs4hbSA2WG+7QAbCluWxxWWKUqCc5BVSsIwZewFf/s1mY4/hjNHYdhiqA9nvQ8BNTnqxTVhsQ9HHMyeOBI1D+QjFDbW5EO8xG1neDnL7xxigNpQ/Pj/SuB26LzRnDpKyh+LyfkDP0OYtpw/JvOVuAnk9VVXlbWJz7ljehlJlw9s9z1/uhjilXBw6ZvaNa7F6kPnY5yI5tBYhZS8dn8zDymOhc5QZJr/0m8rEB0C7Y73T+JMlq9ZAKJmuO8V3/kb+cBz328WTMkgUxFjJUj6U/R0rBpdvehqLWFc+IfCVy++sVLl/yC+M8qTI5K1uNNa1gUq0Bj69XOPPni9RzAprkVmUhQ/CKvjAm3wPJNUBXaWb9v08N/6uIk9AVlcsy+4v5Kbhjh5m/loL4JbumQ38aQhLWS5rw/i2v5VqQVctjituNFTpl2uBwqTRaHz1o69w9eUVvvu/fdfUkQKKqjD3+E5NSjyFXwhUj1pGR8In06b+3ANuDjleN55cHNRNHlEs2ik/FrkDcvI5IgewFeGSQHHLIxpe9K+glS797FEEBUHnmExK1A/x4HLxMYWA3gDF1ln0ez6fo65re/f5UOU7jc80N8q1Hp+3aE24WCxwfHyMFy9eeGlyy8bYOoTLVpYlTk5O7N1txCdXnzGE5PojB5Adu3YfSjnruhQoF9uz5O4nD40OUea+fhjyy9Gr5eguY+v4sbqCXVJoT9kHyPJ4OXkbqjfM0RtJtxx9NR/3UrLG2kcozdBpjtfX13j9+jVevXplgVat/aO7SQZ+OlTOiXpShlR7jMnfxzNHLx1La58UKoPQXMbnkiF6rUOgXcuSo1cYKsPY+ZXrdKR1K+3/eZ1LfWnu9Wj3mabO35i6lWvMVNhtKWdcp3BTUyp/vccUfx0oBiTE3oF4RQ2xpOGK4mgFVVXwaGB5oXRs0S3dlsslXr161QkXutNVnpMeIz6AnZ2ddc5dr6oKjx8/7uTjr//6r60sWutgXrUOg8fcP7cT9XV0TmM7/ZA0hhC10UOcGLaRqw9g2Ta9qcpLyiT7m1xAj5Vvm80Vj7vZbHBxcdGRk2+Mxi6Ah8iYAoa4O1fkbDabJLDD+Q4FlFLKNsCvR542gTSho7NSgB/xGDomcGCIyxcKRxs4Hi6keJLWfnJxKsuSPqLh4FUobEi+ULvuKwPKy5jxM3Z3mZw7CFyL1RmlXxQFqqrq5DtGvF/JcaBPiRba7FHdkBVnSIY+gDCXiH9I7pQSI1Tnsf59KMTvLAX8E1K4eyjf1BcoXPdeWYCiuCdHuoy7Ut2n40NxCeB1QKjhFdpEuPBGbsfb+EvLWwJ/BXLC8hIbR0JV6YpJHtnsj9ExNxee8+iWZSjNUJm3uRb5Uvjpq1v8u5+8gaoUilmBV6sG1VFlQUBVtHkslGVh264AbXgy0iI2Br5FQTkNB9LoNhwTXWvdsZK1YGnodxvHsmx/cwtY60ZtXrhxINbyEn8WgAPs0aqepWyhjCWshrWK1VqjUC3I2lrP6sL4UXhdaDRlA107NyhgdbXCH/w//gCqUGjWDT76nY/w/f/z98Nl2kchkNSr1nzFYwqcH3x8dSB+EPhLAJmD/XN+86g96+8OSNw+k0cFB2Sz4KVi7jmk4OqX9a2Q3PbYbu3k4jw6oCmYVS2NDwrOjZeB6srOwUalWkvyNqwqlC2j0EkTsXmYrh0aM89SnKZp8PDhQ5ycnGCxWEBrjV/+8pfYbDadY4lJ96CUsidNSPlCHzHRb1Jw0lr/1atXNg+kS5D7lKksq6RMofLlacu4Ms62soT47UqH8I7G0770LnLtJRXVh0qyX0m/UF/q24tPqe+SYAyt429ubqKnTw0BJXPyk0N1XePNmzee7lVrjR/96Ed4/vw5/tt/+2+erpbn6c/+7M+glLIf5VBcyu9Upzz16ctlfuRHrzLeUF3C0HYxdiyN5ZHvz4GvB2C3K5pKB6mUuQqR4q/Xa2tBrpTCD37wA3zzm9/04mw2G1xeXlpeL168wF/91V910pJXM5BbSO77PHfH9DexI5hj4UMkyy+V3n2kKeo9eUxxirYBq3J5D1UoxtxjCkKpvB06mU7dkELphAaCGKWUpuS3Wq1weXnZSXc2m3XS56BIKk2p4KevXIlmsxnOzs46+VgsFr3nrOeUcW497GKQHDuZ9PHb1YAulf4p2kaGvo2s3ATLBZYc3GT8XW9QYn0p5h/ajEw12YzZlDRNg/V6nT12yXCp8o3lPZRWLjAlZQ+1l1QbGsI/1AdCG7Ch5T6m7w5dxA0Bu3L4peq5b4Mt5QqFDYFbQ/jG+IXcpQyh9iHbTij/IV7k3id3XxlIN6l8kW0z1NZj/SzWL1KbXlkmoXVRivo21Ie42E6Nfak2SpsSKjN35DOPy+OYd+fv3AAOkBq/eB8iOYO58dLUmsI5d+PGw5m0lXIyO8A2XV9cFlm1sv1Rfv2n7++HjeWvm346DpHJT601LlYNnt1s8DeXKxSz0h1HXBqLWA6s0NOyAGsjAnDp+Av3oFQShFLOTYhuLRLtu2KAK/stgS/7lL8h3nv+tI5YxjatbAUDX5XyAGEOBltQMsZbljcPr4CiKaBrjde/eI2mbtCsmv8/e38Sc0uSpQdin7nfe//hjTFkRGRlRkZONXAoTt0UUUWxS91Uk2iitgIaELTojbjVQlpJGwnQolsrcdcrQQsJENED1BIgtqSGmkSxBrIqk1XFzKqsiBwj3ot4L+K9979/vpObFn6P+bHjxya/fu//v4g4P374dXOzY8cGNzt2Pj9mOHr9CJefXroqO3xwiMlR+ffF2wKmEcbD01l+m/DE9JIqWxUraXO2OdZkKc0/m1c3NKXLK/t4KCyVXss/R0732ur1FfS+Neyf4pruAy4PeKYogXURn19L1nSSN6fJZIKDgwPcvXs3eJyBte1Zr/QPpHcxCa2LjGk9US4uLjCfz9W0sTBed9tQbn7a+i+lP8tnXFb68JN/IDZEX0qBF19SGWl9aiywLUePp2tsLRGjXHlia4YQlaw/Y/lJfkNBmFyij2uJ6rqGtS0Yq+Vf0v4h2Yasca21WC6XWK/XXrwXL15gvV7js88+c+Ot3HHwxYsXTk7prFNCsXLKfqnd8w+JQrtKlc5TpXbWVHtu8x5pZdbsHK8C3bZ1emg+j9l16DkHDeU233fv3sWbb77ppVmtVjg6OnLxTk9PcXZ21uN79+5dFXwN9anbUpcpitnSUhTr52PPEaU8xiLSzWLPYzphiELlG+QZGzOmjtERSxXs2FacIYVGW0Ds+wsXLndVVap36mw264GYy+XSO3ga6BY42qTF+Z6cnODFixe9fEIDIN8+iACeUBmAFuylr1yJDg8PcXh42Cvf3bt3cf/+fSf/xcVFj58xJgpGD12MaQNpaKtSHidF2ywOJeDE62LoQi03v1hYTpptZMjJd58ky0OKe2xgnkwmvbaL9amSyUmTbyhwx/nGJo+hbSoBnRySQBZfSISUxZgiEKobAlLke8XjyzbOabMYgBMjUiAluKPxl3MWHw9C9U1pQvJp3tx8iyPA3/pIqxsJ6PG2lJ6DqXEldJ8C/ORvzkPKlmqjpmlwfX3tvcOxPIYSr7fVauXagofzDyr4c14+Hpa7JZWWhqfT2lOre/lM1jPv07Lv5sg5hIbOI7wtgE427hHPPzSzljySqL2w+W9Bze6e+APGwAvn9/xsV54GoPJwsJRf4X5Tsbum6sKJl0xDv7k8/pV45Og+xLf7zbtN10/8Z/q9jK/z7CiEoBg8vVzhv3z/BJerBtO7s3Yb4s22xMaY9h6mO6OUKpKDJhtAVF7bx91vd59B0gMWSl1JT1cOUjpPV5HGgZ2b5z2PWKD1NmV83TtvWVhj27RNF+b+q1Y222y8Z6tNGtMBLdbaNl6zed7AO0+2qjYeshvPWH41tYFdWzRVe46sqdp7UxtU6wrrao2nP3yKf/6/++dYL9Zolg3+nX/87+Dd33yXVaft6jjWJLnDhQn8DkbfYnwT3dl5WloZTQH+Aq9qNsiZLWI+SJzLO3vrZf6cDYfuPFwZnfrjJq4x7TvrzWssvWw7dxbyRjbXHlxuujfiH+j3FxnH+HHkHDnEq6l0LiTA4vj42N07cU23e818PnferKSfpI5D0WSZTqdYLpf4i7/4C2drKNW3Suf6sQ2mOXUs1zekS9+9exfHx8dYLBbOS6dpmi/c+YqltGtbwZjr4ZK8uE4t9fyQ/ec2gT8la7xcmWNrPkmx9TmddU3xptMpmqbB2dmZW1/l8AmFaUfdWWuzHWlkOm57BYBPP/0UT58+7ckm76UjjBxPd2Gf57YvXs8AcHV1heVyieVyGR3bU3Jp5ZZ2G+KRe8RRLDxWz9JTcCyP4y8ipfozhclxMjQWlvCdzWa4d++ee/b48WP3cQYR6UTyPV6v197HHTdF286FOXbVUP/WwkNxS3SaXeMAoT6mUWxcCtX9UPmTvWnI5KdR6SRQUtBYh0wNtjytNCCnaMyXIJZH7oCVM7HwL1pTVNd11oDDX7T1eu22B+AU2vKYAFrtS1zAX8hoC6kcBSPUThoQEUtTsvAaSlJhDdVLLK8SBTaX57Z55ijioXYduuDOTRvrP7kGAQlOaCBGCgiK5aeVKbZoDOUVqpsYmJUrJ38/QyBOLL0mYywsRmPMTUNBndz0Q+ZE+Ts3j5K8OPjH08fe8SGLPK0MpW1R8r7I56Fw/iFFqbIr48v5KfVe8DTGGBwfHztjJwfENWNNirR61tpUe49jcueM1fsyGG37zspyUNl4HffrhNogxJ/zNr1woANwW5Z6vFTR2njax1I8Lect8yEQDWg9ZQHAsN+cdLSEZOBy+/VL5YWI0+OklVAJkzJtfhlg1Vh8crHE48slXi4brNCeT2oq450L610ZG37fm3MN+oAsl0RrrIxXwG2pqgF5vMrpt2zKzdXajdeplsbCAc9oRLhlYRu+ttrwEksj7hlrmg1QRSAtWJoK/SvJ1Gz4e0gUK/KmDKYx3T3a82KbVYOrF1ctGLtocPLTExy9foSH7z3E9Nj/8FMD2PrVm6HrlQDtrwLpr3E4LvLjF4G1A+Lnpk9664o4Emh14Kt8Tu+c6cfxsuBzCdviWMoh5+fQGiqlt4eeyflc6iV07qz80FuStd1HS/J8WU2GmMGP9C1juo+u+fPUzlkl1M1v/R1SQnN7Tp5D14+kZ2vbAA6h2wLK5VKurqbp1PumEllLwkP5hNZecn0v6ya0Ttt138hZ86eeleaTSzFblGYnoTw0u2tqHSz5DyWZP63/YmXnuyhqa5qQ3GP2DW4Ls7Y7h1zrgyX9JWbzCskheYTuc3hsS9uuS8ekEH6QynvMcSTU9jG9R0vPib8f0pZ/dXWF09NTL35d197un3x7b85Tc9JLvYu7ol3OhbG21RwxcsFoblOTPG7qw7NtcYEh/FP8bh7aj9CQibeUbuKF4nmnQKpQA3KPJ85Py4NTXdfq11qhPf1TRu2qqrwvoS4vL3tflxwdHeGtt97qecYul0vP02TIC7JNH5EKbwqk3odCy/PQtom+qf46Vr65dSiV+xgYs63yn/MeSeJehVJx3kbJDe3Nn0NcDjJuEKiUSpeiHHBL3g9dNHFesQ8kSiY83k9CHqYaLy6TjCfPaS0xymgL7RSFlFLe7pJfCGjS5h4qL50DVte12zIp99xUXtbQmbdaX0n1Lx4e6geyDqy1PY91viWc9AwmIqU819NUkyUUpr0XoX48nU7x3e9+F9ZafPbZZ7i8vMTLly+97XKBbkcP2rIq9e6l+hyvr/V63ftqnPddrU14nH0YgsYkrZ9TfREY3h+j6d+iaTh4y7co7oBOekYgJ20Z3OXFx5Owx6tE4/xxqNuGuL3y/iBRPKIOmbGW2tQHYrtm5mNHr8a8MC53Vx9lYakrJyrvxbLBf/2TU5ws1rATg7qqolsS89/0zHnCyn5ufMAtanQPAI3Ss5V7vnrvjNV5xDxjvd8xz1jyaKUxwcL3coXtzn2tNnMEnQtr0QG0m65DZ8y69566lOYhu269YKt11YVv4th1my/nw8+VNZWBmWy2ma4NmmmDP/+//zk++Gcf4O/9r/8evvKXvhJti7HA0uCW1PtQ07vXVQTvFwQtzaM3JwTKoaWX12h8fmYrGzJD3sQaCNv7mEHI6t5XXjYWwfP0hd//3Hiz+XfnL7P5VVvnDyVtjCIgdDKZ4OLiAtfX11gsFq6NNFuBMQar1ap3riGn0E4eUhZjWi+10DpF6rZaXiVUAlLw+7EBC8qXdOtXSUcai3LXiDdpo9s1yTUm0F8vlKwRQ3TTevjQMsTGv9wPNYzRd/XjZ8Vqa1K+BTCXJcdWWkrau8D50TFyoTUroDvajDFmlpA2lqYAl5y6i9WPZvMI1VGJLS2UTrb/53H8DrVJST/PtSvm8EvZTKy1nhMY112MMfjoo49wcnLipanrGoeHh+7+k08+UeUjfUiGj0k5NhupT41NoXdVs//VdY179+5lef6v12s8f/68NzaN9REa0RCb822iKBg7ZsGGTlY5aVIGdW0LiW2ppG5yjfSlnWmsuKnJK5VPaqDiRvAQ711PZqFylNZ/bp0PAZa151Ej357qblf5DeGz60lRm/C0RZKcuGRfDikymnEipGiMpfRo/ELvgmdIVZ6F0m4bX6ZJkWyLEE+eXwzk09pMa6tcpV/jo/WPVJvIOBrPkrrg7av1c/q6tq5r7+yXHOJ5x0C6kNEBCG9nkppDtTrV8uPKIz3LMfZtqwtpRkDJlxYQVVU5EPTNN990bfz8+XOcnJx45aA2AzpQduiWMLw+YkB6ah7l99LYcRuU5VydUsZtmgar1cqre2v5FQA4mEr3AAdmuzz839YCXfUQekAy0FbBMT2uvRIP4ufz7fh358ZSuk72NrzfT2NV19fveFm0tCVArBZXksEvTud4cb0GKoPLVYOrxmIN9LclNh2IxkERz9uVja0Ux3SoSg+IywL4GJjTiy+BH/6biHcB7bcVaa3C0yLuGcvT8HxpGVWx+81v8p61zQYoI29altZWLZALA8+LVs2Pl3uzTbExBo1t2rpvDCpUXrpm0WC1WOGT73+C+cs53vmb72B6OI29MuOQ9lrGX9VBNMjzc4u4u6Qx5ZC8Qls6a88kWLoJ1PujzEu2r/GvxghwF4FwOQyw+TJHPxxCmr5La/TLy0tVf+P3IX1G5qGR1BN5Xjm8xtTFYhSTp2QdqslbVRVWqxXm87ln7BxiN9i2PrYFLoby/zxRbO1cQlJPDtkSct6/bSjHBhKTaWg+pXFybSXSM4vvOCfrWFvH5lDOexSzQci8NR4pW3eqr8Tyzl0XyTLID+Y56B372GRXc5vkmVOu2JpWa4vUuz5GeUp5DJGlxO4/ZA0/VrvmvIsyDv+tHbMFdB+jUZwcvvsYb0ufj0m5ZdVs5ZQm1A67oBzbWizNmHFy7K8hCoKxoYreB5V29pjXTsjbMbTI+JJ8+rzUy7ZK35f0+afY5ELP6Sq/muTxxgRzNAUod6IbcpbFEMUN0BXWIXOInMxKF7cpGUt4yfYsnWBT4DDgb3cSkl37Ojj0hWzoXFzKi+5jeU2nU29roZiSSqSBeqXn5Qwh3l80L2p+/nBd1zg+PnZlm8/nmM/nHmA4tqId46mB3eSVPJ/PMZlM8N5772E2m+HBgwf4+c9/jg8//NCdY0LlpN905lgOUZn51970P5lMep7/Us4QT3mveee/CvOsBu6vVivXr7txiQOynTdsByBy71iwONxTFjDOK8qPD+fx2gGlm6fw0TfiYVy6ljoPWWn197ci5vnBhfv5perMu+uFdb/zPWe7egzl6qGh+IPHF/iTT69QzVovWFMbVNO4R6wDVjdnx3pxwNJIIFb+7kmmhLNm0DxjpQedlt554Xke1P49nZVK2wXzONbangcr3VuwLYcN3BmvzlOW3gvL5maLFpjlZeH3VC4CfhlfTy5j/W1cqQoqoEI7TlWoOq/e2nbn/9Yt2N6sGvzgv/gBjl4/wt//3/99TN6ZtG1a0kavCo1vExqN9uVtC9P1dw8kVbLeCszmPNk7Sv3VGMXbVv42SjgfwsWYsw8gls/tq9UKTdPg+voaxhinB/K4uYa60m38OO+QcW0XRlApw774kf5JW0KXAj9f0u0h+c5uA8hyb80c2vU7UUpDgYtS+1zIxhIC/WJbr8s1Gm/DEiBV3odkGfKeU7+Ire+GALFDSMrPbQ30wSq/18DY0n7+eaOh/eDLeeLmaZvx/VWjIX3tVeyfsbG61GYcekdTfAaDsSEj4xA0eltje2pRoPHQFhalk1lupwt9dRP64oaTBiZbq29fVDpR5zzLUazkFygy3nw+d0ZkTldXV+qB2RrvbQe/VHqt7kNgW4jGGISkUsjbNLVlVY6CWlqm20C7GNw1QFMb82J9QoubUuBD/fkmvowqWQCF5N1V/0kZZmQ8+XtofjyfHINY7mJN9gvNe3EIUCwpdMa0tkjW6paPObT4o/lGk4H4htpLhqfmmpw2JB7aNlV8bjfGeF9EG2NweHiI6XSK2WyGFy9e4OrqKuoBvCvFMtZXeJ3R/H/nzh1vSx0COquqwuHhIay1OD8/z+pDWntxUFeGy7SyjXn/kO+OBHTHJKks5+ieVM7JZILpdIqrqytnPMipO2qT7p0GuxIPbZtieGCp/9/f0pis9hSnlR3wEDv04wP83FeeN9UXkPaIBeNFPFLtx/uErDM/Dn/ul1mGtWm0+FReku8nJwv87HSOp/M1zLSCqas2ysajUoKxbVoBulZCDyCglV3bnMXvAKnPNuALbbUaTB7qipaBR9R+1r/Xrl6cyL+3nSwY2Mp5avxkvuxcWGuFlyzY+xjzjN2QaQyaumnB3BqAASpbdSAxeTbazbjTWKyXa7z//3of06MpmkWDh996iG/+1jfD9V1K/ddwXLr9armjXQKvxbwZaCq3EPaea20XAnEl0Eph2nwR6hMaCCt+m8pg+mCKal5hebpUx4/QHBczHIXmcBrnuC7Ij96Izd1S703ZTkI2EMC3W6T45OjbpRRaQ2xrl8qJW7oO3JboY0vSY6S+r8nyKtgIxqBQ34qtYbS2HNNWFdJth+SRwyMXcIzxyZWttL+l1rA0Zg35CH1bCtkPPo+UKl8piFEy5mvvXsjmFuvfOWXg77M2323zvg/tGzfZp0qBqRxZ+TFSQHjL55zdTWP6z8XFBebzuRfOP2oH2iMWQ8cyaFQ6d6dsX/sm7T2J6WmSptNp1rmx3Pu4VDaiIe+YnC+IQkdRheTIGSNyeKXIq0l5NlhIkJAQkodMs22DaGGpgnNlkxP3/Bg6qGovYw6gw+PzZyE3ei19CIwdohjF2lJOWqEyx/gtFgv8/Oc/7/E3xt/qVQ6OkldMSUvJpJGcBHIo1Gd2ZWwOGbe1uKHwsWQr6Vv7XmSm8o6NJSVjSqg+KU3q/Iht3seSuLlluomFdskiMJV+TANCzPgUmwv575A8obw0Hjky8vEnx4CUkp3icCMNLWy54Y4oRzkOlSt13rlsh1Re2mKJg4Pc0Hh0dITDw0McHR3h8vJSNUTFzuUZQqlFa6zs5I15584dHBwceOnrusZkMvFA2lJDCK/buq4xnU6xWCywWq2CILXWrtoCfMj4WkI5bST1KwJhqR/Is+9C+YQW/m0Y0DR9z9g2GgdsCSRt0YCOj/SQNY4vFbHj5+UMbLxaKQ6Ft23h/zamy8M/p5byBTgoK3mWkkzL5efPtHBed/00oj/B4Mcv5/jvPzxDNamcJ6wxrcckAAfGesDq5rcDYSvTga+i74b6cqoPkoeqJ6+xDmiJemZa9IEo2/F1/cf69xwo9QBViiu8Yx1fK/hL8JbCN32dvF0pP28saExXTpY+dCVvV2O6tuGAFXksN2jcFevWY7YxDSqzGasqixo1mmWDH/03P0KzbLA4W+Bb//638N7few+olTq3LK8Y5cYbGj+H39i0f/VvaxoD/HVetQE+brxnz40Jb3/M5cqVz5guPgyAGpg+nKK6rrA6X7ltwXMNPnwNEtNlKZz0JPJe4mEyLvHX1qRaPiGbSq5xLMYzRqn1bg6PoWuIXa9xh6zhZH0YYzCbzVx7cy+2WJqcvF41KilXbt2X6N+x/q2BP2NTrL8O6cula4+h+fD8eJ4akLJtveXY91JjIBEfV0rteKlnJXFS6VJjtjb+y7i5cpS8dyXvYKoMOXLG7A5j2ze3AfJCPEL9rNQ2HuKXY2tK5UU2B+Kn4R1kgyrpK3J8zdk1jOwpGs+xxt/c96XUtiHTlaQf0ndJj9gGjM3FacYka3U8MNS+8mMBTkOdH0P80jX5BaHQy16iCKUWAlp87TcnbhRNyTq2wsbLkztphepwNpup/EtkzgEU9kWyX5S2fSmlXu5910MJgHQbKWZUHbIwo74sB+iksXagwUFLL9sk5+zIWN+RxpaSPliiFHzRKTXG5ijqOSBBTOEzpjuHlJ9hFevrIYqBcDngNND/MEzLj5Q8aUTk76Hku1qtsFqtPKW/qipnlLzpsYzkooXJp59+isvLS8xmM1xcXADoyjefz7FcLt12grQlcM5cxBc4xrTeosfHx7h37x5evnzpvGz5OBJT/um5Ni/w/iAXN2OMD6lxio/NVB7qL2Pk3wGxYFf6LT1hu22KfRAWALiHbLeFMLURpdFRHvJi7TxsCXzt0APjxaf7Ll7Hp7vnY0S4Dvwqt4FwPx/+jH5zebXnfhkMfn66wB89vcTHl0tU0wpVXXVb1xrjth52VwJaxZUvDF34WF6xJvIsQS4u1Q/3iN0AnT3gFfABWQJfA56uPXCWdTE6B5Z7n3rpoBi+IEArlh/J0QNlN+1AoCzQgVXWbrYnNt3VgbMbIJbO4KR8m6pBbeu2P1jgxU9f4F/+p/8S7/7mu/jmv//N7PrPJu2V3IaXGpwxN20zfaXS2ow4N0W8bw55TtGMv92xanBmYKvnfWv0OD052HMH8PJ3rnBOCq3Tc9buch6W2wuXAnFcb6N5l+sZUr6Uzltq3N8GiI0ZB29ijR0jTX8i+Xmda+noA7+QvvtFXLOVrG9Dccey0QyJ96q0WQ7Is08KvUf8Gf2OAagyfah/3JSNcBc8tDE9BFBIoLzU+UXKFqvvIfOnTCvbKtUXSufJUNox6abHhNC7znf7PDw8xL1799w9HZXAabFY4OXLl65eaUcwSRKH4PlPp9NBttaSeDl8NNtEjp22JI/cdg/ZSrR4EnSt69rbVS5GqZ03vqSWkmBsjiJPdFsrNyQnNwyGjIOhzr3tC8rzjtXbUG/XXZAmq2b81cqjGT5DXr+chiyucsCJbepwbGWrRJEoXWynKKZgyvtt8ttm0ZfKe6gilNu3QgqajC+BBi2PWJ9NhaXiyOfyC/kUvxRAElNOtXRDDSpD4+5inCYq6auhdKn63UYe3je1etcWTjwOgXly6zLJR5ND9veQUStleMsJ5+Xg9Un/GojM+azXawc2Ey8CY3P6dCmVpucgadM0ePnyJZbLJQ4PD70t/fmihq68HCV6BdXddDrF3bt3nZKd8lhJLVj5b5KLt9GYFOtz2iIoZnjlcXLIWvrnQKsPxG5iemHG9NPq8reWel8eQn86FIjyaaN13q+Ul8a7fW5Yuj4fXk4dcdLmpdj9UCDWD1tbi6dXK3z/00uYugViTb0BUANnxDpglo0Z3njJwBB5lb/b2nDoSqw6/Lgyfog4H4MWvLGmS2vRApMUJruE7dK63w271+Q0LA69prTlMNt6OOjtWjG50HnHWti2TRrAVv1ti9Fs0lRsPGls21YMTOYgMdBd3e/Ns8q0Z8vayqJualw+u8T7/+x9HD48xLu/+a4D7p1nIskS6t5Dp4Ft0qb4up8FutIO0NRdbVFcTJu21+Qp8abV4qrplXZ1HyJocRL9IDQn5axTQmteuSaheFKn4/O9XG+VGPekvCGdIKSLxtZO+7YvyXLkAmrb2DRyKAQMULuG9B2i1PbEMRrD5nCbKHdtH0onn5esG3LjhuxuGo31jsTaN7cP5Ni1NJtJSfqcvHPlC9k2Y/mPuaYPycZlLE1XIteQPFM2JU3PlvaFXArNByXvRwn/GC8eti2YlvOujUnbvFechqblYCwds0S0XC578xLt3MFJc1CTxPtFXde9DwJkGu6EMCal7NOhuWcXc22OLFoabbeUxWJRBMbmxI2V9aZ0jn3lO8gzVlP2xqYxlRktXc5Lt49G2PcCYywqWaS9CpRaCIbCtqWYQvh5H3w4SWVtLIUhll+IcpRw+RVWzocFobyGKNklaXPlkHxzJ2siabQuNeLk5JETJzQ/bdunYgu2UJ45W2KUyrLLcZfzTu14wEkzCnHlq+Qr/FjfCckHwAMieRgBjXVd4/r62gGypEyW9NUUaQBlCW9aqNCXnH/+53/uQNbVauXOh+ULB7mACbWVNHzwchOPuq5xdHSE9XqNs7MzLBaL3lYt0uDH211bnMeMxGOS9gEB5UXXpmmwWCzcPV8YDqGWJ/2TlyzQNEBVdR6zTdOiMlVFY4Zxz9p/zSMWG7CU8mqfe2hcz1NW/qY66O77qIBA/eIlzqsYdHJTOn7f/20D4ZyMu35yucT/+8MzvFxszoitjPuPeb/2PGHlM+jXNlfjVU/Qw9UEnvduE4YuH3HzQEPnmWf9BXtwi+INK/c+bOJJr1hjWzDJ0wGaDS+2PTF5zNLvxjRe2spULi4sWmCXumbTyeN1SXq2+W0M44fO+xW23Z7YNpt81rbzjN141tpqc11v6qVu2/gXv/sLPPuLZ/ir//Ffxbu/+W6o4vuvFT2S43sAzM0CebV8R3jGvadz0nhjsUw3cJiOAp8j5ZFLOUBs0dbHWlvKMOrPGkvD/mVcHp5J2lolB8ANpZFhoQ/Vcg3SmlFRvkcxHeJLyiP6+JbqT34ATzpb6liGLyKNYfhOpf28AdljUgkgFQJwY8BuDMjbVj7tufahQwzs29WaPsZbG9NLbZBafC0NB8JK+/+2703M3kLybgNay90kvqSWQvXKdYX1eo3r62sXn+xEtHXxl/TFoyH4Yu4HETRehT4a0WQZI98cflEwdii4EKIhhc2JX/JFC3+WGkCHLgxKQYybJm0RFqLQgmpbAGXXlNvesfucNLHnOeXP+dpr6MSfq2jte0HsGUcDlAIYJL/cfImHppjS7xAIJ8Eq3j4lCl7MOJKamErG21Dbx8ar3EWIXPiHACmNb2wiDE2eGu9cGgrEhsK3mWdiQLT2bJt33zOwK/nxuBLY0oxnoT7C32dtPNPip/qxfL9CfVt7Z4xpt2A2xjjwjX7H+t0QyplDQvy5zLR4PTs7c+F1Xbvzvji4HQIUc9qb5JDbNtO2NGMZp0LG1214a+OZVrey/67Xa3f8Q+6XsCmjhrV07XvGitggT1fuCevhIZbiAYCBf8asjxIZ08X336/WK5bLbi3F755TGOVrnFeXL3tuc+nlDcfx+0iYj7XAxXKNxrbt+Wy+xs9OF1ibDdBWsfFmA+RxUM+FV/2xiadRQVjebwUg68IipPabVH1awZcBO84bdhPu+qYV8QgQpDCLbmtUywBZ9K9e+kp537inK+97xvp1ZFieTB4exuvbVham6X47UJm8Zm1XFqonBzSzs38BtHwAt1WxnVlcPrvE2aMzfP03vo43fvUNHN4/RD2r/fLmUvca5sVFIH5qiN1GFd9F2lu6lI16r9pIPIPsMqlpM/tAUD7Wx4Hhc2NsfSjnwFBcOUeH9O/SdVdMx5O6m6Yz5ujZKRlSlFM/mtxShpJ8hpDUe6TcIR0v9oyey7CUDLfZrjWExrT5Sb4Ub0jb76uec+wJ+25zaVdJxZOU8w7n2i9z2q7EFpqye41BchylsNAYPmZf1dZ8Jf2nZCzS8g+Fxew5OSTLmMt/X7TtHLNt+hgvOeeQ/YLbVMcaY7hdIKdf7IJKxoObpNLxU9rAQ1R6tuqYdREaG0I6cohK9d2YPKHyBcHYXSgkryLFJssS5Tv27KYH7qqqcPfu3R7Kv1gssFgs3H1oy+TY1wEpg/EuqPRFu800thGb07bAzpgyyDMi99lfOJW80zHPwbHaSRqOtbxD6balkjaOjWMlRouYIi8n0tRCpqQOYiCoZrAA+uOhtnUKyai1V6pepCGKvsDkX5mGzhPXyk6AOT+XdF8L7JBRTwtPvVt0z8+xkF4BFI/CyBuS6OrqynvOQdpdkLYglsTDqfwHBwdeH1qtVl58ay3Oz8+z5ebnvxrTeb1UVYXlcomXL19isVi4fKRBli9qNMNl6D0dg0J1yBdckvgYQe/rcrnEarXC9fV1dr+PlaFpLNZr6SHbesY2TbsVcLPx2GsaswknL9n2WVVRmg4EtQ6UJbSgf235c8RB+92/N4aDpHGEyBigX00KygEtnh7etVksTvf7emXxX/3ZCzy9WmFyOMEKgJ0YVGbjDcu2HvZA2I2nbGWqLozF4/2j17cIJJS/Xb1E5h4J2irps2kDojpW0hu2RTc7j1jm9Urxop6woXA6K7aB83ql5z0vWcUj1nnMNhugqdnIwuq0162ZZ6zbJtluPGHdi9HVqbs2nbcsgJ6HLN++uqor/PC//CE++Gcf4O/+r/4u3v71t7PaIPiaBF4zFdwtnWZdkW0vDBBjXoy3VeJsM+XbwvQifq4XqhYvVBecYsBniFwaLZ4Slrt1sfbxhvc+G+POOt7lOrWqKhwfH6NpGnf+vNQtga5Pka7Aj3SQ82XI+5LzyVmPS12X89PS1nXtbV+Ya7RPzfearvkq2BM0PT4GysZ43FYqsZWV2tViOuTnibaxN960rVKzhcn1OIXzOHztIkkrj7bG4bxk+hKb0T7qT7MrpPLW1lUxOwgQtgfLNiGbwxBbw5D3scTeRPG3aZfYOes3RUNkyLFRyHi7koXba1arVdZ7pp1XT0Rrfmkv43ZmsoeMIT+XI8VryHwcq4tt+3IOv6ZpcH5+XpR/ifdq6Xtf0idL2zhmbw+NtUNo8DbFu6YxJq6xBsXQCzRGQ4xtsMzNT4ZNJhN1oMoFDGL53YSCm1OfQwe0IQPFUBqj/ktpF+1VogjKd2vX/aeEvwZKpOKX5BMD0nicksUFzzvVH3IU122U6VDa0roMheWWM7QwKKmfWP1TO+5iDuP55o5TJAv3BgzNXzltFFtYpQxYOfKHjH8a8CfjhtJpBpebnHdz4ubOr7y/DcnLmBbsnc/nWC6XDrDXxq9Q+2r1P2b9jrXApP4/5My0Pv8WRG3bAJt/eo+Mi8NSsPA2Td+YDYB5ptI1LKb14lO8tg24B2ybd3ffxevezX5e+nCQa4CS91Z9FvwN4MXVCifzNT67XuH5fI3atOBaVVceEOtAvA3o1pZjA8LRtWLer4aBstCvgPJboV4fMixu75Ee7grsfloXj5+N6rYoNgYcqHUercxzNuT56jxTm/BzOq+Vg6Mur41XrJNH1FmLD2/yqGwL6Bom38YTkHvKUpvw394zkQYG7Rm1m3qzVfss6SFrLeanc8xfzvH8x88xPZ7iwbsPUB/4Z0o5vpQnb6PYkJF6nkv0/meAj91jq/ch/lvjsY2KnUprA3LcJtqyvYq2OU7JYML8YvqqpnPyOY/W+aW7QUidUM6pfL6PgZmaXiZllbqc1CPonoCAMQzDIZ1+CHiwS8pdS2p94jbQtvW5awAgRbG6TK2LY+uYMftXro2BKNUm2vsXe4eGlCU3LR8feFhIVvodWg8OtZ2VvF+yrnf9Pmq2tJA8obS54RrPkD0gFWcMGlKvux4bh9q1YulT72BJfiX9Q6YPxS2xeTZN422brwH8BMaWyilJ64dDyk+UM3bm2BeHlCU2b8TGmKHvnpYuBNxuY5cO2Y3GGjNycIibokFg7E3SLiezEGKuhYfOwNs3DakPGb+qKnemHicJxt5UebdRgofQTb+UN02pRfPnnYaACWPVj5zAafwhDzkADizJ6fslE2OKNKOKVP5j+Q5RHmKypBYPOQsAihv6aosbfVLypJ6X8pDgkSajD6TE+dd1jbqu1fONre3OIdXO3pSKV2oBQeeP5sQfQrLcofdVk5/iS6+OsYn30Zw+JMna7nwvDVSk3/JDDU0OyZcbZ6lvzudzXF9fezKHtvJNvbP8vRpS9jFJGqnlLgzb0nqte8bSvTHSM5a8YVsA1Vq4eHCgKgdlW3SH0nRoD0d9eBhEuHbPw2S4fDac+uOMfN7mrYcbNNbi//PTU/zo+RzraYXJ0QTVpGqBws2WwxyMpWtVVd0V6G9jDLGlMfS5LDSeeGEChJRgaxR8jVAPgHXYrPEALvXcWKpQ2/4bCE/YhgGyFK5crbVetzIwnles85rd/BFw7J0h29j2uvGO9bqpvG54ut8irucha/z/ymzOkZUeshbOQxZV+29g0Kwa/OF//oc4eniE//A/+w/x4BsP3Gukjm/a64ZIXKDbera08V3z2V6Yu7XhZ16YvEq+MswKfvKex5fxuOzW6nJp8mp5JigEfsaA0Z5X6lAQlffLnOhGz8eFb94bdy5yiSim/0EWEY1/tNPFnTt3sFwu8eLFi6DOywFPfi91WJ5XaieTmL4ueaV0hfV6jcVigaqqejaLXMrJS77XJWDbPig3T9L1Um3wJcVpKHgXo9tgQ8yhHAP/LoHYlAxAef2H+IVsGtr7U2J/3KXtegjIqsWJ2f1C4FWOrXCoZ+yX1KdXfQznc/ZyucTJyYm7DwF8cp7P+aAst9/z/hjKP+eDBO3d4PxC9qbcsFwqsb3m5quVjWw7nEJlzP0A8ItMo25TPJRPyICYa1iM0bZA5VA+RLucgIFhcpUolbkT6C4n2iFKcEhBiCl/JXV5WxSK0EKZU+4Au03+pfFCCu9Y+Q3l5YyTI9XZmO8Pr7OUnCWyDnl3U+/c0HYdYjDQ3m35PsT4pxZ4Je+VrAOtTkrGoBjQqOWZwzPU3vIDIzkPpOSQdZsDFO9qkZ8iLhd/n0Iy7NPQoim+dE0t/rU+Zq3FgwcPcHx8jJOTE8znc2eI5dv0hEDTUB1o8vBtkGVZdj1nhnRDPlaWvv8pshYMhO08y9v/Dmzty2VYHKrHjmcnUrdNMY/ToUEdKtSdLYtefOkVy3nwMMp3jLFc1lP/3vae8d9PLpb47LL1hl0AqCuxJXHFwFTDwtCFuXDe9gSYGjFm8XAGnuUsljUefoQBgJxhfZh4Orys89bsecayZ5yP5/la2eiZsZInAHd2rAScpRetA3kJZCIvWtPycB62ppOPe8ga07axbWzvmbtnHrESlO15yNbd1ViDylawkzbO6nKF+dkcj/7VI1x8eoF3/vo7qKcC4OleMb1+EZnHRNosYm2cipMdXsqnlKTMOXwLytCriyFy3+CSLWvuM+KaSbHxSc7DXMcL6YopXUjjq6WRekMsjbYe1GSgD8en0ymWy6U7RqkEcBhCWtlS+rxmRynNcxvwKccuMpS2sWvdJlBmDP20ZA2eKntJm+W0f4kNoMQOq8UpyW8b2jYPbT2Ta/fl64hSWXZtB46t/be1d4Z4lMgzFAuQfHPCQ7aLkvxz62fbds1Zzw9Jm5NmF32ypK9Zqx9JlaPT5NBYekFsbi+RaZdjwBg01PYaCxvrvU/FSbURhZXamGNzW0lfTMW9cc/YmJFW0q4ns1eJ77akKdO3Vdax6TYsAoh2qcBusxjLVeyGyi+VpW0XrqWUs5gMDeb76D+5spXKUgLklMiTS/tYiBMYkrvPf4zo66+hvEgWDajk6TUlVIJjOcCldi/TkSzaucwhRSWmwHBDmjHGGfy0c8hyZd9l/6Cv93idx4yQQ4nqYagH7jYLSLrSObNf//rX8d577+GP/uiP8Nlnn7l+PZ/PYUznIct5DmmHWP/lsu5Ll+My0Ne14+ZtsV43WK0aNE3tecR24xD3jG29WzvPWAnatl6iNNwY03rStmVpn3fWevqthcnfdA/AA2B5+2jv/ZA5JhZu1TgaIPtHH1/g9x5doDqsUR/UnUdsvXlfA2Csd90842fGEqDHf0sQNbg1sXie8ooFtutvBJiCqs70w91vqkOL/u9NN/E8Y9kZsM6b1nZptLAWD2XhBt35spbJyeTy+o9NXy2s5xXs5c/jukqC1wYVqm6rZoMWfGUeh85btTKABdarNX7/n/w+Xv/26/iP/o//EeoHNRyATfMXAbCbMjsZuZih+XlTJpKvB8rLdyEDeNQ8ZmVYCMzlfUXrNyFvXGrzQSBxxnPnXc3jin9PBgs/bii/nOGrcNofZVtihZ8xRv+YI4dHQje01nrnwQO+1yTpKcSHnxtPcfm91PE0/Ynv6hIDNHPKQ3rKvXv38M477+DTTz/FkydPgulTNBaoMwYvjUqBWKkHcj1XrmG21YE+D7aim7QD7dqOEHsnZZyUTLmg5RBAtiSdtn5OUWrNrK3NY3EBZHnjD5H1i0Lb2CU5D7nW5c/krg+loElq7P08jH+3gULjzdAdL0pp6Di8z49Pvmg0lq2sqqreOGCMwXQ6LRqDyIY2VI5c8sDYki9ZXqUBaQw5t0XFbwtZa1UjvEZyG6DVavWlu/mGxjSuhxS3nC89eLzSrz3GoqGKZ+hjgJgRP5SHtmAo/ZImxis3bxmeEz8EenBlk08s2jabu2rP0LNtAPjUAonPLTn5hPqGrP/Utmza+5YD1EljFQ8LxR/ynuQoELF6I8OZtsU+KSkchMuVUYKxVL4QGMsNRLkAs8wjVMaQXPucqymvyWSCuq5dOVerVfbiH/ANFcRXjvWhex4GAIeHh7h37x7eeOMNWGtxcnKCpmnw8OFDNE2Dq6ur4BgU6/dDxliebhvdLKdNeR3yfEvG8RjN5w1+/OMzvP76Ab7znfsbEBYOUG0aOOCz++88YrsrNiBkC8x2ovS3K3aIkF8bLoy8YLvfbXqSw39O+fpx5bMh1KW1Sph/r+ZTG2BawdRV693ItybeAHAqECv+edweACvG1tjc7wG09MwEwjfPXDqfUZhk/bDzX6np+bambZK+p6YDE0k2Br7FPGNh4LxYbbMZe4TKbyvrzmElnhowrXqykoesiEP3PQ9ZikOet7z8G89X1/XdRwu+ZyyFm6o9Z7ixTXvm8MZDFgCqSYXr02v88L/6IV7/9ut47997r0vP89Day0TulfhFIJ4SVQO3e+CkfO5eNJ23um0xjZUZ4LD/OBnB/x2QqZgGpvU+eMjlU5JXKO7mvTRGbE+cMQ2F5q/QWoyAuevr694REpSWp0mtR2Qcqdvz+T00r/JdNEJ6oNR9lsslDg4O8N577+Hg4AAAcH5+juvr62xdZB9ASaosQyikD6f0aLdVv9An92XLuY12wlyAModi/UnqyLKdQjaQmIyl8UvW87nrEk4la9AQvxQAJuPGxifJq8SelKpPrT1DPMZY4wyl0jEmFn/bMTI1V8VsNvx57F1J3ee8A1KWXPuCJuurgg3k2pg1yrGNlpLWZtvIWJrnkLRS5+HPcusox7YXi5tKG2ubmH0xlj7EM3SsVSzvHIrZCUO2KG1MqOu6uO/myplj9w3ltfNtim+SQsb3GKWUKR7vpupomxc3ZIjXeFRVhclk0ov3eaWcASsUPsQoXRo/V2GVsr0KlDsRjPXO5bS1Bn7wOFKJ04wVOYpoLB8iPoFsa0zYZpGgtUUpnxwQR8szZ9yz1npGnpRyl2orIjo7M1duzi93HtIWKFJ+zlcztmnbz1Ja7rEqFaa6rnFwcID1eo3VaqWeK6uVUeYfkpWHh3jkhPEySk8NXsYcBTOHhqZvmgZHR0c4PDzEYrHAer3GcrlUDaGxfFMKXIx4usPDQzx48ABvv/02JpMJXr58CWMMvvKVr+D6+hpXV1deOp537qI2JgdPXzqPbZMf5SmfhwwwpXR9vcYPfnCCr33tGN/85l3UtVG2LIa4SiDWONAWIM9YA1+sbrtiZ8WHgQ+g0jOAA66UXqJGxgAaKNs94/nL+U8+92X17iSWw3GP3vjd5WfqCvWshplsgNaQR6ziDasBs63cLC2/Zx6tBuyZIDU9qx7Nq62of7Go1lqfH29Cvxu4q3u3eLhlvEP37J+AWlSbsM32xKbZyNKge0bTSMX4ale+TTGFcTmMKHMDt92wi8fz3bxLJJ+3rTT16dp4dePC0QKxDRpUTesxXR/UmJ/O8Yf/+R/iG7/5DXzj737Dec462eDLY43vLevicPB8G1Ler+A8YP3fPY9X5Zr0rk3JxviFwGDu1W2t7/na84SVaW4TjS3SZpyxRtRbQZ+Rc5jc8YMT6UyXl5cA2g/F5DzI9coYD4qj6QU8f3Xs5UUVum6O/r1YLHB0dIRf/uVfxtHREaqqwi9+8QvM5/Od2GOkHp5jZwnFHVM+qePytQZfU3LjozySgq6y/l8lu8EuKce4G4qXw1e+HyF+uev9bXXzFI1d9hjv3LqXaUplDBnyY/mn4u56fROjoaBCqLzaHEPxc9o+NL/QR8pSLll3sboNjc3avTw+R5aT0oQ+UAnJetNUCqDdBiqxoY3Nd8z3MseGkGv7K5F9iF6Ro7dwkrpe7odb2rhQOmeVjp8h/TLkGauFp+owtLNLSJ5QuWLj5o1vU3ybSOsEocqLVeptVmZDHeXk5KTXQdfrdU+B1zr3F4FuUsH6ItBtqt+QcSDHs7K0DLlK7Xq97ims+1a2bptyF5ocU+3AJ+8SJTxE2kTNiW8Fl0sh4Fi2e2hukkT9h/7lYqiua/fhTe7OCVIJ2qZ/pNqB4nCjI9UFpYnVRQhY0YA5CfLmEn28tFwusVqt8NZbb+GXfumXsFqtsFgs8Gd/9me4urrCdDoFgOjWJznGiRIiMLiua5d/Xdd45513cHl5iZcvX2KxWGCxWHjAujSupoiPTfw9XK/Xgz6Oy80vtoDnfT51Zt42tF53Z8fy/7ZPtXFou2LaorhpWrSoqoBWnO7MV2sBaw2qym7CCfXprj7gCvaMfiNyz8NkuCTqA5s7FSgKpPTCrXcv01gL/OJ8iR+ezPHh5aoFYKv+tsMclOXjgQujbYmrdtvPKAjLrm0tdGBa77dBMJ2MD/T5ZdEG3HJ9WmtGk3kFOu9azoeDaRDhBJyKqwMaOTibAcraxvperFRG2wGX1loPoCIglLZUhgFstemDPF/Kg/onTAe60rux+faFzo6t0HrHVqaCbWx7nbb5Tw4nePnhS/zL/8O/xNf+B1/Dt//H3+62I5btsslXA2S9/HnbpSg0JNmOr4yX8uos3lbYMl1qA7J69/zKdREGvLqr1s+0fGU8Wd5tacyhfiAvWXeStt36WM7P/KMv+rguZpQm4scryLihdUcuaElxSI9L6ez0nB+jQKBsXde4d+8eDg8Pnd411pyeAxqFdHQZpt1vu9bVvF01nvzjRL6zEn2QeZuAhZumHNBzTDtFbE14G22LMaBsCHBSWvYc8EN7dhO2klC+u26/FBgdqveQXDxcrgtz7ARy/Zhjm0m1VQr8ls9TNgVtp7lUfpLHl5RHue8hn5uIxgaec8cFbZzOAShzbX454x3nmZNWS1dSR9uM7VrenyfaFVYR3aa4lG5SodMGyDG+lAjxuG3b9eZ2jlC90BezMZLnyhG/sQ3HuTT0hciZcMeisfnmKqQ3RTkKV4pu88IwpGDnTl65i1+pXHIwNqVcciqty1T6IYpobhreb3YxF3H+midnSqYQacBVrhwhuWM8pdxa3+NxyCBD+dKX8nKhRWAVBzpTC7dQf0kZ13j6WB5a/fG2GzIP72q+MqY904wMnw8fPsS7776Lpmkwn8/xwQcf4Orqynml8G2LNdA9JDcHkHPHWQKEqf2ttZhMJnj99dcxm82cVzTV55AzWzT5OHEDby6VzHch3YYW3A6w28G8aS1tS0xesWD/3EPWsLDwmbEAbVdMKI8PvBrT8QbCHrJtXM1LFi4O8QJaj1jKm+fFy5lTF0qoFy7j2BbzQWMtPrla4V9/et0CsRyMFR6xDpQ1fbCWvCVdW7N0wS2KY4Asy4+n6QGy/JkL4D8T6w1YcC9Ol56BU8RDu5e/vTCzOStWPPPOjoXpecRqZQTgb1dMcRsRd9MWtrFemNtGuEHnYWo2clftGbQurGo7h3rPhxMLf/tlqsONR60DpC0c2FvVGw/ZScuontW4+PQCP/gvfgBTG3zzt76Jqq5c3xON5YeJe2/7aPlOGOjhGkWASc0jNbhlMRgfGw8LyREEey2LE+Kv8QuEJ2W6LUsEWSclcvGhesCUFNMh6et/ikOGczlHa0CrfCaN6Vp+MUBQyizPpuV8tHtpMG+aBsvlEsYYHBwcuOMgSJ9K0RB9T9PL6TpEnyzV32Tc3LUL/02yyo9Hq6qKAitjGB93ZcAM5RWiFOiUs/4sbbvQGi7H9tjTN26YQu//UF5j2fBSNoZSMCJFsXoIrc/32YZD7CjS5iT7KB+HtXWr5CNtCKHfJXJxCtncjOkDxan3iNuFSrZZpWe5NqJYfd1mu2cOjdm/Y+9zztgck6m0vqWtEEDWznGUNkW5Y6ocR3LtzDFbXo5MQ+bTbWnIu6DNCaH+sM08FuuLJfqDpL14xsYaLGSwLWmMoYPYbVFwboL4VpXT6RS//Mu/7Dxm6MtTTtZafPLJJ7i+vvbCq6py6SjebaR9Lgb2Rbe1TLdRpl1SCBzJHfBDCgMPXy6Xan5DKLQQDPHMARhLeYbk2iZ9Cck6pLFQU7xIttA2xaSYaWfG5ipCmiIhw2Leu6lteGR8ildVldvuTW7xq6UPPdOMdznEt9YrUYY48EkgI/cWiJGWT9M06pnM21JVVbh//z7W67W35T+XQ3u/+PsUWhyWLGZ/8pOf4NmzZ24bavKSpe397t27B2utA4vJQBfrW9rCWiqjVA7tnI4U7VKXILm2HUuJrLVYrxus1wbrtcV6bVFVFnXdApzkEduOE3AescagBSJtC+a2HrMAYDdbF1M88pBtibY1brcztmxbY7LuU1yJ+kj0CLC22xLZWsAY2R8Bv+m0duSLN61+tHs/zePLFX7nk0u8WNpua2LxD6C/LbHwnHXvjoH/G91vQDesuxLSbyOeGx9s5PHaiwLaEZ8cxCXUTCbwO3SV3YDC5DbBEkwy6HvGgqXlaUq3K0YLiDZoAAvnZettRWzhPGkJwPUAYim7pEbEM92V1z95uxprWi9qdk4uDDBtpvjo9z/CP/tf/DP85f/JX8a3/kffcmkdaL3h7QB0Xo5NXUrAvGOiyC5IHZcs/2l7dSyBWMfDsmc2L22wjolHzODQR4j9tAxstdZCBV8pyNouP00mJU1EsF45YnJG+aXylemV98F7R+k+Y5iI2U2sbT+0ojNVrbWYz+feEQkhXZKDdaUgQkgn2Xadaq31vF6fPXuG3//933c2jJOTk9G2KU6BNpquo4EYFL6rj/a57hgCGqgNae3IAXlarxCQHaMx1vOvgk1giLF6rHLJtYAEl/a5Nh6Lbmub8/qMbVfJ46XWCbngxb5J9p2QbNrYUVUVZrOZV1d8Lag58GgAruRbKr+kFA9ppwnZdL6km6fQ2EbPSkizv21LxhgcHx/35sjJZOLhHp999lkPGwH0vhqzr409dnyR+720yQKI2jhDdVWiw6XaL/Y8CsYOaciQwW5spWWbeCH0OqSID31BbuKLghSRTHzCMsbgwYMHODo6AtACP/P53EvXNA0+/fTTXsdMte9tAQxvUoZU/wmBPzn9TiqP+6SbzDuHtjUA8Ks2ZoTGitSXMzJOzmQsv/RLgb8xpTtlRIil14iDRykZdtFPZJ45ecg0vD5D27PFxriSsV4u8ErlTcWLKXsaL1qsaNvzxsb3bQ0EsUWbRrI9tHN8d1m3Q2k2m3mySdLG+tB7yuPG+qTk8/LlS1xcXDhZqN6WyyXW6zWm06nb+ULWbapOuZwheXK2R0xRajxJGUu2UZRT9dyCqfKsWPrvPF8dNqKEWwImLADw7YqNy6OTp73vrtz71UJH77R7StvmSfx4PjLvNOKhpeFh1rtvLDBfW7yYr/HB6RJNBeeNyD1iW4G6fw+QZf/ecwJQObgKEYbImCfTynfS9NOrwGvmtKeBdnYDjvNn0guWP+95xW54eN6wm/e45yVrrVfXBFryeIAf7pWZ5GX17MpPfChf8oDd3PM0bjtgyoPLBBEu+gQMnDeu8/ytjLe9MQf2YdF6YKP1xK5shWpa4fzJOV785AXe/bvvwnna0vvA6p/aqdfGLCwIyioUHIcs/2mTryEHYmX6GG8VRMxJK64qEAzWZzJIi5valjmLvazL3HQjE3+n6H4oSQM07cChHeMQA5PkWqfEeMjTlOrlsThcz7u6usLHH3/sdnO5vr72dg4K8dbkz9VrQ6TVjRtb2bWUcnWdWDtyW4+WZsgHcjmkteNtsQONoevn1L2knL6pAbG5/DV+Mn3Jek323RLbwNB2LrF3hd67ULxcmVLr59h7edMgLK+DnHrKsd/x3RBpvKW0Wv9I1bP80CdUjhANte9ov3levE5uqj1L3pucekjZfUpoyDut6QY5Mg2xK+XkNYSMaT/6lx/SHxwcOLzEWosXL16oMqXmwtw6CdGubKwhvW7sfHLzz8lXS5MLpHK7b4hvbjul7FAh2rln7JBBgwzDmuC7HCBDvGNfkd5m0jrXarXKdrEfg257Hd1mGrqQ2wftavJL5bML/jmDe2xyiim1OQpvjow5+ZXw518LxpRjz8it5K+BYZJfLL2WXy6FFhbUpqXA1VCicVbOEyljFK+rofLJNLTNcK53Z9M0uLq6cvd1XaOua8xmM0wmE6xWK69cuQY9GTd3kca3VM5RwLepu130jW3a0VrbOwpAKvS0OODPYsS/ZiaPWKrn5XKJx48fwxjjtTN51VxdXXk7aOSMxUPP29WI6pJ76WrE6yFkSLG22xZ6yBnOKbLWYrlsUNcGq1WFum49Y9frFsmo61a+1mMW7gxZD8Mz5DELVBUHZcmo2p0f255F2/eQJfSn7TNdevq9kZbXEC8FrPVlIoC3tKpakeWcqcUBXszX+H/+/AynKwvUBlVlvO2JOfAKswFqjQLEinuAgW7GOCCvZzgigI9+G5GGpe2BryLceyYpVoc2EEeCYkb8jl1jv0MeslbE2fymbYxh4XvE0jOwsYinBYvPy6PJAXTnyzZotxzeALwyjtt2WKtK2wKnVd2eCWvqlp+pu3aqbNWCxhawppWdPGSbqkFtuy/hq0nl4nmArLVe26tjkOzzQxC/3ntje88koOi1hXjuAaObNu15zaILc/fWv/dkY/zUbYu1+KJve2kD9SbzLt4COfUsFp/JpeYbKZtaH6a7H7LWi+nvNG/yudta/7gT0g1oTszJh4dxO01s3pUUW1elaDKZOG9YXi5Nvn2QtvYB+l46Y69jQ+UGurUdHUexWq1Q1zW+/vWvO2+e2WyGO3fu4OOPP8ajR4/cx3g550CmSKv7fYG+pekBH0AfwkPq5xql1r+5NoRYHmPSmEDOromPRZL4+lDaSmg8jJG0JYTqXn7YELJf75tCc4MWj4/Z9IHu8fGxCz86OnK7LTRNg/Pzc29HKS0/bZ11m+ya2twXGr9i9oJdlSfU514Fu7ps51yZc+pS61NjzF37plDbhupKvmu5ZY7NS1pfzsHeNBtjSPYcW9VQitmFNFmG4Hlj7pYXoiwwdpcvfmxg0wzNu6ASRaiUX2mcbeuad8DQy6JNzgcHB26ireu6l1ZOujGZc+vstk8oMZAhtvjUAKncPOQiNgfoyKXSBccu2ydnsN9HPypJy9s2xXOMiWcXk1dorIv1WTlGhwwQsbQhgClH0Sw1tsQWTyHDEs8jth1wjFIL7pAMHnig1FNJvjxdyiBGz1erlceLFpbyfJhY2bj8vC5lulDbls7tvG5kHwstPnPfp22MPLy9aAtlql/eL2N1qPHiRF8o04dVPL7GRxoJefucn5+751JOzivn3Uv1s1Ky1nrny8lxIMVXkz1U76VjrYy/Wlmcni5hLTCb1cxDlsaUFtDswjSvWICs8/Qc6DxeO+9XC0KAWp7dldK3ZWrv+e9NiXlJXBhVJxWtvbde2BCSaem+sRYn8zWeXrf/cwtU08qBqj1vR+PPJ71/6YlJP7XxlcVznrJ05XF5/iK9Cs4CHn/V042BMDI+wIAey/KxcB6ysKzPGnRequzeecJSGHQvWOc5ysJJBum1B9uVmXvIkiesVy92E8f069rlJ6+wfrnYMy9Pw+5F/7DG97Sl5xTu4m3OnYVBC+ySh2xlYOzGQ3ZSwU4trl5c4cVPX+Du23cxuzPjrx+kxywAV69R8D2HlHcuOk5ZcXW3NptX0us0JFcgMg/XPFB7MlgRL/SfQ7ux/YxL1EdYn4pGLzBoNk3jnQPPSXp6pHSnITKV6l0UV9OF6Z7GB340yDbbUA4xFst0lDa2DhqaT27+IZJ2Cdp69PDwEHfu3MF0Og2uhcaiXH1tLPtETr+TOmFJuTl/njZkW8uRgd/H1tucZ2lb5bbDPijVNrm6Pf89ZLyR8bV8Y3YEfs/XzJwvtymE+IeelVBqnTOEOMg8mUzczkq0VtyFjWoXlDsWyHeqxOaUQ6VtXMJ3iJ1oCJWs/3dBmt1QGyPHlDNlrwnZMXfV3kMoJGNu2qH2+Ns0RmwjyxhtE6vHJBh7Gybum6ZSJesmiSsms9lMBVX5+ZMA3FeT9+7dA6AbgdfrNX7+85/j5cuXvfz4wi70Rdhtra8hNMakfBN0m2WT4NHQgX8I7WLyGLqojSnrKUotYvjigJ+BEFr4kTLBFXIyhIRkloYfGhtii0ui2NdH2sKVG+I1uUvGbPIQ0L40o3C+Tc/YpG3jygFHIK4Elxh7ZB68zHQGOBm4FotFcHEi5eJGCS5XyLjG49CcxPtaSHmkfGlBKPtniGL5y3dnCIhorcVisXD1eXJygg8//BDWWjcXTyYT7/wzSfxcr1Aes9kMx8fHuLq6cvlxD1atjxI/ekb1+PjxY6+8xrRfmJMXqUw/dHzS4oS+qI4ZPoboFrH3iPr5truFPH8+x+/93qf49rfv4a//9ddQbzw8ySO2rttDKTsvepKFl4PGLoDObW1l7TxbjSGjqt3wadPRFWiBV4pH5adnmxqhHDfP2/hUfJJpqFcsUVedVgkDrpYN/pufneGzeYNFZVDVG49Ys/GCrdD9Nl1b9c6MFf8ctCVQz+u/Bt1veTXiysM18NVd+uBbtF/G6tSKODbyO3SleDIMLDwGbhn4Z8cC/TNjOe/QWbFN/xmBtZVt59wKm+vmnuKFzo51YKeLKrYMBrqzYylYXCm+85Bt0HnIVlUnJ4Af/tc/xI/+Hz/Cb/1vfgtf/ztfb9uV1aUDZBl/DoB6soXaPTL0qGAqK7uX3uph3jPOi4fxeKQrbp5599jcW3/c5m3nrraLK2VTQVdKam0Q4PXy4PeyTkR4EESW74CWrXwuy6gAzWo6JY730YNJjBsBkmm4XkDtQrue1HWN4+NjLJdLXFxcBPOL6VRD1mhyHi/hEeK3zfovpBPkgJMSYNHWHCkQJkap9WMuP9LrmqbBo0ePcHx8jG9961s7W8dsQzdpnxii78u0OUd4DX2X+HPKh/ShV8U2mUM544Fmr5HASMhewO9j9a7ZorR1CT2jNjk4OEBd194ab71eYz6fJ8fZsWhs8GObdyNEQ+1jKSrlqfUTSa/y+xSifQBkY+RR0ve4PaFdI/ftidt89M/JGON2mbDWZp29HqISm4o21uXwC9VHSRvJd1bqPzEay4a9T9q3LEEwdh8TRI4BNYcP55WT5xDjYazT72NgG0IlLyp/oeq69g6nBlqFIjTYaMpRTr67ojGU033KGwMdQn1sW/l2Xb5Yn6By5ShCKb7bkDaplSyKhr73qTLkPNcmilR5YoYFTeGgMNqylsJoOyt5rrTMo2ck31Boa7MUaWWJgUSh9KG8OU++XexkMsHh4SEWiwUWi4WLLwGrEoWDX0v6YUnfCbW1lkYrx7YLG85XA/botzEGh4eHnvEqtACWvGJtGUrP65iUVG1hXbpg5PJPJhNcXV3h+fPnDuCmvsPLGGpPOfZba928TNvPEQB7eHjowtbrNS4vLwH0z+eJGSMoPyoz3y469Y7Jehpjoc3l47LRM1lHlGeon2lhKWOzjBN795rGYrFosFw2m7NjqT+02xJbq/23nq78N8C3J8bm3g/3z4ftSS7K1YK7xMtHg7oyUhV0cYkXXL5lxN9RUVcW+ORiiefzNU6XDa4bi2piHPjKPRr5b7p3cwr9sWdeXKADSSUYx/u0QQfYsnxde5uODw/jfLx47L6YrDKGuyw37boBkFw7UztCeJUKPcsY44BD54UKxQMW6Lxr5dmwXM6N5yq/9zxWOUC96XrWsvxEm3hXK+Z4wZe2GHblIu9c1gc8z1sRx5i2v9nNRw20XTKFu/OKa4P19RqryxWe/tunMLXBW3/5rdZDlpeLAGEryiLaJ4YvSgp5tHqgY69ZrB/Hdvc8vjfmRfgFQciATCr/ELF8Yx65UVA2l3gZlfDse/5IqcNgWtsP0967ZD7oz2shHYl/YCR1CTornvQGb4yI6F6llCPrGPpCqRyhOpX6gxYu9ZJtZC+pa7muisXR9CDuJV3XNQ4ODpw+SWl22Q631U5GNNT4WtJ28vdQXlKPzdF9Q+lT8qae5dorSvKI8ZJpNf6pdWMq/5z1t1bvFK6NNyW2iqE01jtGZVsul07exWLhjSVaP0rlr62/YulLyxOyjW1Dsb411E6zLWkylLaFRruY+1NjX0pOTYcplSGULqcPyqO6gPZdIDsL6Vo5vEJ5apSy1cX6eU54yTw0VE/R5I7R0Pk3t0+E4qZsRcC4Y6tGk5xI22ZcOjiOqeSGZPq8kjTi5nQoa60HNBwdHeHBgwdeHDp3RBIZs/n9q06lZRgy+efE/zzUJVFqkT+2AjUGxcag3MUV5xVbRA+RbZt0EojRiACZw8NDHB4euvTT6RRXV1f45JNPXFzyTpSLJDqXisureZ8OfedC/SY0znEjSmgb96qqcP/+fTfevfHGG/j2t7+NR48e4cMPP3S8Tk9PsVwu1bKHZE7JuC/idUagHQC3AKM4dL5mSIHR+hNfdBKPENV1jclkgocPH7qzwOT2u5JyzzDN7VOHh4eYTqeYz+dYr9de3iV6CNXBZDLBdDrFyckJnj59ir/4i78A0PcEzZXT2nYL4YODA7z++utYLpdYLpeYz+dYLBZ49913ce/ePUynU1xcXOCHP/whgBYQbgHBJvi+0zZ11M70nHvVyHFLGgW1suQYaOS9Vs8kA3mlU9vkLEhSBhoqNy+LVka6z9EprcXm7Nj2/Ni6bgHN9uzYziO2vbbA6oYDWm9WeP/EE+jqh7xgjYEDersrjYkkUwvitrw2KFcnNavDLi+/HijtkIW9FmaxtsB//9EFfnq6gDmcoJpWqOqqFUd4w9Jv72r0ew+o5f3Q9Pul9lyNS+AdgbEcYKT7rir955JkuFalRjzT7i27euw274JFB6SyuNay7XMZsOZ5lvJnG29R02zS8GG86uKo92YDfFVo01eInx3LedAj8oyld3FTHlfOyo8H2/GwVVd+8lp13qssT1O1gD7qtqyYAM26gZmY1mMXFmiA9XKN7/+fv4/ZnRn+0T/5R3jjl9/o3hOqR/Jm10BXqo8cCkVj7SXjubEpEKYCsTbCiz0L3ss8rV9G7/cmDaXrecVqZbf9dD3gWEsv5PBI63dcRijPefliukCANy+vJxd/h5X3uce+UGcIzaf0wdZsNsPR0REWi4Uzumtz+m2lXdiHpG5Dv/lzufsX1ZfUlfgaYxtD8lDjMxHJwI8jmc1meP311/HZZ5/h4ODA6b1jrsFv23o+Rtusp3MN3GOtV4jXUJvJPttlV3mVjIVa2iG2KAkuaLJo7RKzx95WovPGLy4uXNh8Pvc+yo8dXUek2Tw0O9iY882YdrZXgV7lsuaCszn6Tg7l6Dby3b26uurFubq6cufWA/7cmpJLliV01nUOv9vW9poeBOTZAnMo9m5rdZMax3P5cPvYNhTjMToYW2oYKwFNudE4h7ZRYlMv7W17CYh2pUynJricfG9rnUmSg0mOgp1S7iSl6vNVWAznUMyYHVLIti17rC1SaeTv3IVWiEIyDFloD6FS+TW5+FkhVVXhzp07arxYn5YfbGhtP2RRtY3hSFs0ES8CkYF266GHDx/i7OwMd+/exdnZGa6urpxn7DbjGjfuyLO6ueJSCm6VxpPvaW69aqAYf/9yeFRVhXfeeQfGtFtiX15eOo9S/gFQTGkNLZR5HO1Z0zTOKFlVFVarlduameRPlUPyp6+G6YvKyWSieqlyo13KQCBlJt68/0ynU29HCy53qJ9LWag8fLvknHeV15VWL1q5Qu9OyHOY6qjEEMMNotoRCrH5OtSPUguC9dpuvGPpvwVfjZFgLHpes2AesU1D4CrdG0jbh8NDrN2kba+bUPY7dE9tR/Uh+zMQ9sJNUbf1sS+zha0MmtpgUvkgKP2WwCp/poWp6YU3q3fl4QTIGeUZ8WPpe6Asf7Z53l4S9aY87nmoboA9eV4sj0sedtxzlnu0emfIchCMyek8ZG3nTcq9UKWHLPdu9Tx2lbpxsnN+kSu1n8vPQpWJt4/0muVeusaIM2MZ6Gsq40BjAnONNd022Rvv2MpWaJYN1vM1fvL//QlOfnqC9/699zA57HbQUEHZrmGHEwcgNX6Bd6x7rEXo6tbFsYG4kXx6AC/9JkCSAZPB7YNJHgnSeo8DZY/JGog3is49gIWaLxuqjcnzlC3JS+oYTdPg+PgY3/jGN3BxcYEnT57AGOMAudAxQz2xBxrX9r2uzTGGavchnY/POyFggecr4+cYWXdhw7HWegDLZDLB2dkZVqsVptOp22UlttWiLBPpVZoH9m2nXDtlbhytv8h3r0SeIfbSHHlDsgx9L1NrviHr4330IW1tFNLvQ2tcbdygHQd4utiHxWNTrv0xxYPScJBJO+IuNZam8iH5SvvJkHE9JkOM775I61u545G2/t43jdH3ND7eWi+TJ9mSJEn9JqS/yPc/1k9S9oIY/131t9AYL8NjOgmnkLyx/qbllUu5tsNcWXbFZ0ieoTTJM2O3pZjymfqqJvRS5uazbUePKeRf0pf0JXW07Tuxb0UiBBpsM2Zso5iOSbl1mRtvNpvh4OAAQOtJ99prr7n0fKESIz7WS0/ZXHlSCyT5TKah+9SYLpW/w8NDfOUrX8Hl5SVOT0/x8uVLnJ2deXGHkgRj+fnbEuzdZ38qBWKBvoEuxwBhbetJ+p3vfMeV/9NPP8Wnn36qntEbUh5z+rK2yF6v1zg8PMSDBw8wm80wn89xeno6eKtiAD3P3oODA0wmE1xfX3sgJ38PchR3oqZpsFgsnIzk1UAeudLQUNpv+AJFyhgzRMX0uZjeJo1WsYUEPy8rlQ/V8Ww2g7XWneOkAdEa0Jsire2axmK1arBatZ6xk0kFY3Qwtr3n/FpwtvV8NV78tiwE0ILlh0281rOWrq1cnbctNiAvnQ1rDD2nNgBLw+sdACwKuxBL2w+zFjC1QT2t2zNiq84T1gNEuecri8PDQ+CsHDNdOEQclr4H4LJ7CTK6fuYu/n2PDHxwztWHWNxqcQgM3fBx7zYHXyO/vfREwgvVxTem9TTlgKqBO1e1l498ZuDATWNN5w27OX/W2x6Z2qZq41E4B2PpuQOYqw7sc6As877lXrLWtl691jDPWVbvBps+RTIAqJoKDZqWn23PlCVv32patR6y/6fv48E3HuCrf+urmBxOHC8OrFEdZQPzfkP02kUL98BTUTYvzIr4ItyBp1J2y+Z4WC+uu7KMo9smc9lYHr3xludJcdl/tC7UAUepj1gc6Hy8MP6Yy4c+H68MW9IQPVDOUev1Gvfu3cNv/dZv4YMPPsDPfvYzHB8f4/j4GJeXl0nggOZUfrTDTRqwh1AMONSMvjIefVgndQ0tD67Pp/SoHPm2IQJjX758iaZp3G4ws9nM6ZOhtRkn6gP0keH19fUr1wd2QVp/kNebrqfUeplTyEaijUMxO0CJbUdbo8Vk2+YdCaUNySBl0c6DpqOb+Hu0rTdYCWnrvdAHNrE61LzpJZ/QjmCh/pVr20nRLt+h0vX+EPtADs8S/jdhHxqLQnOwvJdOCjn2Rj73EvGt+mP2jFeZQnUjvTtjekhOu8TCx3gnct+tm55Tx6KgZ+w+CjgEgMkd6MeQPwYk75OGlo0PPLG0tD0kbVN89+5dHB4eevmGto7UjJi7HsxCim1uvWzbfqH0ciHD++ZtHVRK8swZuPc5kcXGq6EgGe9bJQsITikALVVfY49dXJbYZCzl0s6NzM1bA734PRkG5IImVzmIgTKhtqf3MLbIJDCLwFag3aZ4uVxiMpng3r17PSVP8tOUSa1sxhjMZjMcHh7izp07uL6+dou5fY+pnIbMyyWy8vYhT9TFYuHOr5IArPQA1eozNsZq/ZrPjdQPaVvfWN/RykztuF6vsVgs8LWvfQ3f/e53HWj/9OlTXF1dwZj2a01a4GrjAJVTM+as12tcXV31vk6mraTIuMaBS61dcseXlGEk1ualfZfrEXLbbGoT+R+SVy625I4qUmfk7xp/FnqPY/V4crLE+++f42tfO8bbbx9ivW4NnLRNMXVtAmM7ngTMUv/re8bS2bGth2x7X1UWLeLDr+j9buuQwl1NsOfYPOfbGruS98qZQ131dPX0o5cLPLpc4cWycUAsB2GNMd5vd3aneCbTeGHoz3feVcmrd+VxYyBsoI/4geE6CoF0HMTicSTYyp878JWFp8KkVy3Jy0FTXgc9sJfVR8hT1tULA2V7+dC2wlRfBi5+VBZ6F8lrloBYwdc2tgOgN23sQFrAXW3FQNoarZfsxMBYg2qy8bBftX13U8iu7NbvK+65aM9Yu2txNSAvBA5y4LEHhErgNJJWBRw1Oa1Ix/PepNMAySBoqT2HXlaVAs9VQFYrlxXPlPi87iwUb14LdW4YAsrKuap0DqfdVubzOYwx+PrXv45vfetb+PVf/3W33p9MJjg4OMDV1ZXTPTSdlutOsXl4CI2t36YAdZJdm/dD8sTWhrJd6ErbP2s7A6VAGr5eGbom1ACki4sLLBYLXF1dYT6f4/r6OuoVK9eA9PEf39a4dNe6V5k0nZi/IzJcSxejFHAmx056FttJSbMB5IA8mm7M+fFdZ7hM0gZSUg88rfyIm/ooz497ZudQbD1O/5RvDt8c20XoiJoxKSZHbDwr4Z+ztqdryjY/tB62GQ9jPIF43YXeg5RtoCT/0mcpGmve0GzZMbm09XZJ3qn1t5w/Q7JoY3WpDkW2FMl36EcWuemG9J3UuzVknKR0MZ5j9HPJsyT+mDRGnqH6ivGOblMcM7593ihmpH8VSRtAAH0gaJoGp6enbnHWNA3u37/vxZFn6cV4agDJmODCkLbZR3tKRTe2eCvhSTxugkoHlG0G0dIySoVZU65z+IZk3UZZHJtCZcslqcjk8pN1W7I44TxWq1W0PbQFXA6FFhk5yq3WV2mxd35+7p5fXl66s2Hv3LnjbQWr8dPKplFVVZhOp7h37x7efvttPH361H1lPtZYOYRibRzTEUIGiZiivF6vsVwusVgs3GJbzh9kAKQ0of6jyablTf2R36/Xa9R1nVV27d2ZzWZYLpe4urrC22+/jd/4jd/Ahx9+iCdPnuDFixc4OTnxtkKW8mgyEm9azDdNg6urq16dkxGVzn+j/sONZaGFTYxC+lBqQcb7b8wQE1rk8jFKfqwRO4MtZPyhduXGJS29lDtUFxrR87OzFS4uLnH37hRvvDHDel1tPFp9z1hrzcartfOQNYbCrQNhu+2Mu3Cg84C1lryS23BsgNUOpIULpzK2oiqIEWS49jyfeLe2m/ufnC3xxy/mziuWADRjjAPhuFGM/3uAKfRnan/f/DTGtF6UBLYZ9hvw82D3Hr9NGg4oSiCTU9RjljVRDKyz1nZy8O2HFVBW9Yzl/V2ESbDWgZUQ2/1uZOjlYayXJgRM8niSv3b16tf6nrRyS2LeVqHtij2A18I/b5b3IQ7OWnQfA9TGbVdc1VXrrdvYzhN3I6tX9lC7K+2tRNCD5VxhlXALSIDQPbfiN4GmSlprrX6vzZFaPgHw15NNK6ftrpSnShY9eTTv3GBaJcyVlf1FZfSCbLh8hUMovZv8vmQtSHHp47arqytMJhO8/fbb+MY3voFf/dVfxePHj2FM+4HYbDbzPD7lXOzKaP0PK6fT6Y0BcdsY5bmOIef8kB6Rmy/x5LoG6bVS/8ldm25bv8SHdMjLy0ucnJw43ZIfyxIrFxmhuV42m822aotd0a5sJym9lUget0F1ra2lS9Zc2jonZquVa39ZhtRaWePPyyNl4uWj9PJ5ao2uxaFxivc/oPtYd9s1My9T6XvK02vtWzpGjrXm38b2KCm2ji8J3ybPbeyLMUqN56H8thlbhqzJb3qM1dbOKZvQNnlp/DU9JcVjG8rBUchLPIdybOra+JhrX4zFG/u9uQna1zuQU19DZAml2fk2xWMqaXIbRyLazpFTKM+UEvAqUK4Srw0iBABI+vGPf+wUq8lkgj/7sz/znltrcXp66qUl/gTiUrwc2ufA8KoOQjGFcKwylU5oJRNOiXwlY0Rq0XhbiRYMUv4h7bhN++dO6kT8APvZbIbXXnsNxrRepCHQhYgAoW3ajMay3D4SNMoDHsDDw7TfRBcXF/jwww8xn89xdXXljXdA2Zduh4eH3llLV1dX+Ct/5a/gt3/7t/HkyRN8+umn+N3f/V08evTIbRt29+5dEOh208q4RmTwo3LRlmex86d4GzVNg+fPn+Pw8BCLxaIHlOaOe/KZFk8zLFxeXsJa6xb6vL/G8qZ+aYxxQOvZ2RkmkwkODw/x+uuvo6oqt8sEGTzpS24OlvK8QmHWdlvt0jPNcDCZTDzvWk3unHOMQvWp9UHNABHjpT3XDDByXJFf4nNPHkrP3y8u03Q6xWq1Uj2T6V4Lk/Mfl5Vkojgk13K5xnLZYDptNm1iNzL5V2Paf/otq4+ek5ds01AZLYDuvvO0pfgE1lK5OOPOU5aet/UEdFscd3IBxpMrNe30u4DF+y8X+LfP5/h0ZVswK7A9sTHMG5ZvUxz5d2kRMDoy3qnfPA0HGIMArGu7QKXE6oqeWcavV3Obfma7+zap6Z5HfgfDqk0fEHFg4Lbl9cBNY7pwnpfppwHQbSfMti92nrG0nbDpQFYn0ybc29q4YuAWbX8MdEBoA+88Wb5dMXnEeqB0xYy9VZc3gO668Xw11sA0BhUq2LWFNRbVqsL1yTV+5z/7HXzlL30Ff/1/9tdRT2sPcPPO+I12gjT1xlSeTwhM5XGs9Z8p8ay1Xpgbz7V7Hp9dvTnAwnvu4oDdQ9xveGgAqOMjZVXqxN3zf/jXnkerqEOflciP110oH1H/Kqhr4D40kKTpKUOI67vr9RqfffYZXrx4gbqu8eDBA3z729/G+fk5njx5AmstDg4OevYUmvNWqxXu3LmDe/fu4ejoCNPpFI8ePcLl5aWzC+xzS85S4uN7qZE/BEjJ9Fw3oDo2xjjvU9I9OL+YXsXzGqM/kFxcZ0rpZhSX5H/99ddx//59PHz4EHVd48///M8d2C/rYNd0W207+5aJ2pVoNpuhrmu3FhvSd/gHsKH+R2HSg1Wum1J9W/Kj33y9Qx/tSn60XTatq/bV79brNY6OjnB8fOzJYq3Fs2fPvHWGZrfeB92md6Nk7X6baBdySZ4x+5g2F4fi3dY6vAnK2XkiRJrNIfS8JE/Kd4hNPZYv1yW4fjOWHrlvGkPfucl3Ycg7OvoMEVJyh5BMq+0DTosNGVcLkx01R8YS+UsbX2uYmMIfU9q1vOUe4RRPO4fvxYsXybLSV7CSuGE3Z4DT2kCjkDE9Ral6jdVxbpoU8cEkNfjmKKo5Muf2j1RZSuKmaMikk6oPOdmE8pJ8cttd8hhjYugZqyJy5tCQsSaWPiYDbbVF8fgZp9IIIXkToJjKIyRvqi/yd10bK7X7WN/Q3tnFYoHnz587L84cz0aNjDFuK16gPV9mtVrh/v37+NVf/VW89tpreOONN/DHf/zH3phKICEHxW+SQvNySAGU47HW987Pz7Farbxt8HheoToe+h7wdLTtLzciaV8yy37GyzmZTLzFd13XODo6cme5Uhzy/uXbvMm8eN+T9ShBVG5Qo62XY4sHbdyMvSul76w2xw8dOzU9Qcqm1ZvmPUv1LNPn6iEUl4ySUh7eZu2X++3Zseu1RV37nrHG0DhKZ8SazTO47YcpHmBcOAGyQBunte7zKyceJp9r91QGrQZ4HSMQR6QQcZ5dr/GjlwvUs7rd7pVAPOMDojysB9LSvwRCN38cUHXPNwAd8eN5yTw52OoBsabLLwnCMpmyaRNVeqcC6IBH8gAlYNVaODBn43Xq+uBGTt6/nR4D63maStCW0pMMzhOXpSc5HFDLvHV7ALNBj6eTVXjE8rqnuPy82573MCsvtZOLI9rHGuvydHE3YRzwdR6xvL8wL1lYwNQGy+slPvy9D7FerPHX/qd/rT+WkKjGlzu7X9h+eT2+VB/WD08CsVb85nEEfxWklGEQctp+WBA0DY0jG/mstf3yxihDrlSaHqAaq49QXgF+vM5L9X5Nb8lNxz30Li4ucHl56c4MffDgAS4vL3FxcYHDw0NMp9Petp8kK6W5f/8+7t+/j8PDQzx9+rT3QdQ+jX+lOsa2eommE4bWoFVV4d69ey7OcrnsHTER46U9j8ktn4f0ONKF5FExKaK2PT4+xhtvvIGvfvWrmEwm+MlPftJbm+T001z9K0Y3YXAdy6aprXFi+q7kI+d23ren0ymm06kD0IccOST7UcweF5N7rA80+A5JnOhjYHkUUkrmEOXYV2k8rKoKBwcHLs7BwQGsbcFYeZTSGHakV5lSdtbbUjcl78c240/uux7Lc4h9NUeWWFvEbAk56WW80DtaIhORtFMOoZx21WwTId1Ha6/Sts9pD/odOy6xhEp0qm14jjGHh3gMkS2kzw3pjyW0l891tEkoVkmhTj2ZTLyvkCgufS3Fic7DkHlq3jqhSbJkkBnSoXINgbeNtIPrY6QNjNuUPfUSlPQtbmiV8UoWbdS3eBpuFNfO771Jyln0Af2vHEvOvihVVnLiU71KgE/W71gD5dD3LzV2yIXUvuShq9xWQ5OXnzdF3qAETM7ncw+MjZ3bYq2/LSz/UGZf45szJieUGtpOme8AcH5+jj/5kz9xY8d6vXYLMCB+Vg8PlwtmGffu3btuQcfTlWyBMjaFDAU57778GEimp3NaAeCDDz5wQOVqtXLgLoGWJfpDiozxv1Sm89C5QSnXk5vak48/l5eXePr0qQOpSTkmY+dsNsP19TWeP3/ey4fiUl/jc618ZzioP5/PnYev5vnN33vKp2TBxPMNzYtcbnlGWoxvzIDL52f5/kp9Tj6jNiYDKBkeY30n9YzLoS3IiJbLBtfXa0ynNYwBJhMDayvUdesa2IKugDGAtXSQJhltfC/XtmwtcNs+h0tbVfDidv/G8aG4xKuV2bJ7APC9ZymdrJch06q1FqhMuzXxxHhescYYD0yj/u+8YoV3LAy8bYVVoJXkZWHc85E/ozCZhpdZgrw8fFNzCGJsJg7AeX2WA5IEahHQynh43q4EVprNGE3h6P/OvXryGQvTbPg3Cn/NA9Y4QZ0nrLsycIpAVte3KtvFQ5vGGNMBpXSmLNUNeb2SfKaTyYGOdlN/jen4ch6bOnO86R0j2Wz3rJpUaEzjPiawa+sAWrUtWTvytisiLb0CaMZAWHkf84jlv4NesPDv+b8by8Vz9xt+HJ6Gx+Hl0sLdby6zCHP1IutOi8PuVYBWidsrP9AvFxTZjfgtswsMsqXrbgBOnyW7yenpKT7++GN8//vfx09+8hOcn587O0loHua6wmQywdHREd555x28/vrr+NnPfobT09OevlIqZ4y2NXprxOvSGOPpBZQf1xfonig095Pes1qtUFUVvvnNb2I6nTqP5J///OduhzfSa2PrJnkvda+UrSP0XNvaNcaH6mE6neLNN9/EN77xDfzar/0ajo+P8eTJE3zyySf48MMPYW3rXR0CzjjdRtvWNkT9I7YjUIhydWzKR+rY1JYETB4eHuLg4MC1gdzNifLgOrsmD4/L85P9hz+jM6jpw+VUH+fPaK1LH4B//etfx2w2A9Cuwz/++GOVjyZ7qY2R17lWfvnOUx3wfn50dBTMe2zjvUa7GCu/aJRbh2PWc2rdyfv0PvpRDo0hR04dajaNVHr5XlKYpBKv1xwqwQxK+Q7lqW0ln6KSvLZ9D2K4iMY7NwzIe5dvy5jpgbGpCXkIhcAujVL5cYWAd3rtrLfYZCif3ZbBLUSpiT0GAMTS5cRPPY/VZU57puKmBrecPisVLi1Oqv/I+pbPS+5zgKGQXCmjdYxKBnTN+D+EX6heY+1U8n6G0gylMcaCkneA4qQW1ZxKJ/zSMkmwQXq+0xmqY+RfMkbF3t2Qwpozxmh5yw8prq+vHS8C2Hga3oaxPHuAAbovfsmbUi7mefx9zVUl77W2UM3hTwYFay0uLy8BwIGwNKdvM1+FZJaySkMiAfEUP2YE5W1IfWC5XOL09NTblpji0jEB3Eihvc+5ZaP301rrDBjyoyAtTWgO4u99yZxcooPI/LSw0PwZWpTSb16nvG3oOlTxzl0AkByLxRoXFyscHtaYTAzWawtjrDsntvOUbb1iraVFZwuK0pmxgN1cjYvTWfM1T1b+HPCRse53m2eXN21pDHRhbRn9cpXSsrG4WFlcr20Lwhrjg5uGvY8E5Bk4kNUD+Fg6+cxrC8GDe0m69Ju0Ia9aJyf89DwPFYSl8EwK6jqUJ/c63TSh5w0L4a3KvWRZGuLlecQGPGQ9+TZ5eXKA5Wu7evA8ZYH+2a9tpPYZz5ddZdeNecZ6dUF9RHlOfHg/4v3G85AV8by+udki2VTGXdeLNU4/PsXhg0McPTxyMnh1A0W2GCmvmrrNrdWfuT5k9fteuOTFeWqvfUg+Nar1fwuevflHgpeZ+Qcy93nYvpweMK0XLJ+86rR+/iUyb4jPeXx+G2JU4yAhHbvx9OlTXF1deTuRpABf0pMIcOE7vYytm4bWUbG5OEdfjK09uV5Ukr/kRfEPDg4wm81wcHDgjo9wHxwZ/SgpKQunkL4WKlesv5S0F/GhDyan0ynu3LmDu3fv4v79+zg/P+/p9F8kkn0l1EdzbWK5tpVYXyyxjcTyCuUT0sVpDRIC41Nl4+sT+nCB1mSvv/66+3iWy5EjYyy/EIXee75G4h+aL5fLno16n+v2sexg29JY5d2Gz1DbwU3VYU7/jdnB+Xszliz7HMdlnqHxRcYPUY7tXLMFbUNjvutj8AnZUUr535b5PGZn1HQlWd5cO45Gu27XoGdszBC6y8EqV1nZB42hxObQWANoLvEFACkO9PUZAKd0S6LtTobmtS3RQjC0SALCC7VchYtIlj+Vj7aAi6VPTS4pIELjfxN9aN+Ki5YfGQO0s6NLKGeBMJTvbZnMiHLGWakUAe0Xto8fPwaQ5xEaWwxpixviG3rH5XsptwKi+KX9Ur57fIFVVZUDSauqwmKxyPrqjvim6vr6+hpPnz7Fa6+9hgcPHnheuSSL5iG5C8qtOzmHyPkjJx8az4HuHaYFON9ii+KX8E+R5uFP2/DPZjNnKFutVri6uorm2TSNOxPs8PAQJycn+OM//mN3bhN5jsznc3eWbq6RgHtmaMomBx5DXqKcCKjVFnlyKzPZ11LvMjcuyrN3Nbmkd4pWrpQRmgPdPIx7XVO55JwljdyhsvEy8mtIt7HW4tGjSzx5co2/9tdew9tvV5hMqJ8bWGsY2MpdATtPV6omEoeu1lKczpPVGP7fecTy3y3wyuO3qJfPv43Du05XHUYJCxPx+MX5Cv/dk0sszcYjduMVC4Oel6rnDRvwjHV9RYJl8IEzF7cS9+y5d4V+79rbYWqGV4XfX5R6SQGzPfBTxOfgZ+/esPdiAzSFvGG1sBzPWBi4s1uhdXfyQm3EPTZXqjd2fqt7Rlf6beB7r248Y90ZrxL0JfAZXRtZ2O5cWYvWq9ai82A1Ih8C4jZnzHpnx1oANTrP2KZCA+YZO7F49uNn+G//l/8tvv33v42//Y//dq8N+dmxxRQBIj1PUHrEX1wCHiUQaVlc66fjYdQu8hm/98LYNfkbfd4yjrW2X0YrflM9WHj/FiytrEurpBF8NE/ZWHmC9QDGO0UGalqvCIm5METWWlxfXwNo9Z5nz57hn/7Tf4rr62ucnJygrmvnzahto8v1T9r147XXXnPed/fv38fLly89UPe2gAKcQkZePodowI7UPVP2BK4DnZ+fYzabeWuGmAG4RHcuib9Ne5AeR16X19fXePHiBU5PT1FVFe7fv4/Ly0u37TFdb4r2aQvR1mPcy1rqwClggQN5cgtpqedrfKy17uPh9XodtNnxOtLW+Zq9KvQ89kx7Th8jpPrIarXCL37xCxwdHeG9997De++9h9/+7d/Gixcv8PTpU8f7j/7oj/DZZ5/hzp07rv7W63VvZ0QiaXdIkZuPRLnkrmEAcHZ25njSmlim3ffYuG/b4Je0W5Lrd96+Ibt0Dt3WfhLzis1Zr98E7SLvXDyuVDfMoVL8pFSOfdBtk0dS0DN2SAWHDIax+PJ+yKHLsfPdhlIKXBiTSpSDELCTW35NUdqmrjR+Ug4tTANPchTAmEIoJyaZJ48Xklfyy6VQu8Sex9q9ZBItkZFfc+LmPJNK/VC5YiTbLHROUWoRHWrXkKEjVjZtrNvF+BCikgV9Si4NoNDI2g6ozN3iQyqKofFLKpkyboyP9l6Xjms5c5s8jyEWX+uTfH6j37RlLc1/HAim+ubnfu2acsZEOT5Za4P9IaYTaMYwMkLwd1zStmMO8eDjB+VNW/vRfc6WxfxDkPl8jhcvXjgPavpamoyV1M45Z7uGxqLQOMV55JQ7RildYUi7yPE6lO8Y+tcY7wrvnykjLaflsj0vdrlssFq1Z8iu19rZsdTPAGJhTAu4tuw1D1nKl35vLPnSrZCjYZvfbZ7tfVsmr7QsLVjc7pkflqo7YGktzle23Z7YMGCTxBJhxpieNyE95+HOoxV+HPcscs/zatkZ9V4FZdH/7YGzAqxNkSsDgUC8uTb5qF6xmzjuPd7I6sBa8miFMNygC3P3JWfICmCXy63eU/EsK5uoL352rAeYGlZ+3l8kKMu8Yz3Qmj8TfJ0cSj5uzjFQz5p1YG8FNKsGl59d4uXPX+KTP/4E9756D3fevuO1q2jwMMXeK9fk1rt3j63tx6M41r/35l+Fn7W2S8N4ufvNcwmMevzkb7mO4/nyssjfSh5e+bQ6E+FBYFaJLz1lQ+UJkia/zZiLNnHWV2usr8JHfgwlzm+9XuP09DS5hagkY4wDel68eIGqqnB1dTXoPMptSNOrS9PKMGkf2HYtS/xevnyJyWSCy8tLXF9f947XCa0TNHm5DrLPdSblDbRrlouLC7x48QKffPIJrq6ucHFxgfl87unQqXreh6xjk2b3KLEPpIivLyktXwuG1lFyjURrRmutd3yKpruW2rhKqTSNLAutfQ8ODnDnzh288cYbmE6n7kPL9XqN2Wzm1p8hoDpme0vJE7ObaTZJKkPJmmwo5bxT+3jnYlTy3u9rXBvaH3YhQ2rNnOrLsfCS+ix5J7ahfc0DlFdunFKZhtT3rsq9r/qM2fLGTl9ixw7pRPvWk4iG2OCjZ8bexAAVUuSAVh7tPAYNjKUvQV9Fihk8cztXyeJIG+xz9mjP+fpRMziT5xMRbTsiZU2dORKSawySClbsOVHsPLqbWjwNyT8Wd1dfvJYsZqiv0FhAC46cRepYiuvQ/nZTinFOueV7zxcVXGEZ0s9DoBInbQxIyR+a9GLlHQPIyyHZH61tzzfnW8YBwLNnz/Anf/IneOONN/DgwQO8fPnSS0Nb+N6GM6epPPJjmtVqhel0qgKGueevcjB0Pp9jOp06b9XSM0NiRGnk4v3y8tIzfgDttsmHh4fuK3O5yKbf/Cvsk5MTPHv2zD2jrbbm87lnJJlMJr13bsjCgBvKeJjkwcNIZwqd38wNOLy/asZBTW5p3ExRatyQRg+eDx+juDFEO7M4ZHzW6kAzfMYWwlrc+XyNq6sVptMK1nZnx1ZVA0vbr9oKpNZaCxjTXlt5uqu1HUhLHrT0zP/n3rH9eNrvNj+zCbOeDFwOkrmEqkkFcI9Y1mYEenkesNwz1nRpPLCWgWjuGXx+rdwRPjwM3bMgCCvCvWec+PMI+lbqFUthqpcsPWNgrQeqim2N+VmnDmxVAC9jzDDP2M29d44rxTFdXGsZOGxZPAPPy5a3gQR7XR70jJ8jW23qhDxkqY15PhSvafudqx+glane9K1mM3bWnYesNRa2sXj0h4/w+I8e42/+J38Tv/4f/3ofuCPxc94fDgwq4fxegqlZIKy45+EujABY7Z7ly3m4Z5ur95vHYXn17mH9NBwI5uVhdcFls/6DvhxSFl6n4rmaloWFyqnGySC7trh8dIn19caDUowHcm4dQjSXLxYLGGOcbpNzvmdd11itVjg9PcW//bf/tifLkLPJbgvxvjh0/cB5EFj053/+555uwUEkCiO9QVuzS307FHfXRDr4er3Ghx9+iMePH+PJkyc4OjrCcrnEfD73zictWc+/qsT16VjZQjqy5FVVFQ4ODhwgK70u5W4zMX5XV1cggFKuwahflfajmI4es1mWUFVV7qgaoslkgjfffBNf+cpX8Prrr+P111/HN77xDQDtmuQP/uAP8OjRI7fOisnKy6LZi0JtI9cNvO6191Zbe+2CXoV36lWQcd/E+witw4fYjoc+u0naV3/Q1vpj5R2yI2iUOxaMBeTm2GBvE4XG4ZBtN3S0w20tXy4Fwdh9FCw08YVI20ant6Bk4aGXQItbIvOuFE3PcKTw1oyRQ/PRqGQwKBmIZH6TycTb1lJux5LLP9QWGm07MW1T32NNikNl0EC2HL4xozWlK5Up9L6ORaF3PCfPbSfu3LEhtdDfdd3kUqitqN21BV6ID3/O+01oLOaL3BgYEjqfKLTYCoXHSCrLpfMFJzrfk+puOp3i8vISP/vZz/DZZ5/h6OgI5+fnve2qZL43tbjTjEFkpKmqysktv/CWPGRdDnlfpcyyD4beOzIsWms9feK1117D0dERfuVXfgVVVeHs7AwvXrzAz372M1RVhcPDw955RdqcSPxlWagdl8ulCsDKskiZQ2WXdSjBb+pDfEtvaoO6rl37afnwtuZGiZCiLGWicEqTKkepwTnlXSzHHlkmmb/Gq+Rd89sDODlZwFrg4KBGVRms1y3C1HrGms21BT8JaG0ag6qyaIFP7hHbetTy82T5GbOt1Z9fsUnfgazW8rql8sp7sDR0D8cjh67XFr+4WuGTeeMAL9f+m3sOdnrPAAesGkM+sKzvcF7o4vN8OF8vnF1VT0nGKwTIevdQ0irhPbJ+3N52tpQ/AVCbZuX9S4KyLh0/U9Z03q8EVpInLG92z0OVAb7Ulay1XlmcF67t6lO7p/y9Mon67tUVaxO+DXEPhKV79oyXhb8KQ8+O5eHGmBbEZWfHujpuLJp1g2bd+K8fa+9eW+WSTGIFH87bBuL0WIj+Ztk8ZEWe8p7Hl+l43taPJ8FKx8raHsjqlUXw9soQkjVWzeKZCsrG4sZYWwYoR/KMyiaG5V3oesQz9wM/TffN1VFuA0lZQ+uVkA4Sa3dvTBb1JOs3tYagcSa2nhqyDhmLeL85Oztzx3OsViu3FrutIMDYlNJXNT20RK+VOo6my2p5yTylrFoZQuuOEuJ9Y7lcBj/wKLXDWNt+xHx1dYWzszN3XBAAz/OXr2NC9q7UOiAUT1KqfjR7xxflvSil21ovY8ql2dDkuj9mPxsrX420vPc5l29j++GUo8to9g0KT83x3IEshFnwD9ZD771mB5FjUUoWrQy3nUJzV8gulNJ/UmG3jVIyRj1jd0khwfh5apJWq5XzEpK8JD86my0nzxLS8toV35ugmDE7Z8DWwBIZbzqd4vj42N3nfJkbopw6Sw1W29b7GO22Sxm1QTB3AZ1SSiXP3Ilh1xPITU1YISXrNrzbQ0hTcOjcyVzlgfh4xvOAQqJRzPgQM14MbXONDy36tjkPiZQ1UuyMMTg4OMDJyQmePHni4pFHKBEBuET76kshJbIFkfztlvnW1RIgp3rjYw71IdqGSvKMyaHJVNLWVVVhOp3i6urK7aBR1zX+0l/6S3jvvffwj//xP8bBwQHef/99/Kt/9a/wox/9CHfu3MG9e/dwfn4eBC6JyFhAz0lB52eX5shrrb71sxYv1iepfvg8e3h46L5C1+bfmNFVPqd7+X5L41GoPEONuZqhisumGUT5WCLzi9W1VgcheXkejx5d4unTK7z22gyTSYXlsoG1dHZsA2PIRa+CMdiAngTAWpBIdKUsre28Yx1OaML/BKa2svmgLD9Lljxf+dmxPE9WSoSay1rgZNHg//f0GgsA1bRyXrESlE2dCSvBUw+UhXjGeYTyE78leOsBIPQ7BsrCv88mI2/FXMjQGwfKMo9XHkcCqDm/JTkPWuUako88XgmoVe95MgMPUHXgL3mvWubFK+ubebJqnrHE3xjTu5cgrOxfqODOjLWNdf0SFk42WPTOkzW1H07ySiAx5h2dSyFwTwVVRZgES+X4yAFVDWB1YbDevZYnB1Z5H/bC2W/+73nF8vzgy9vLn4eJOvP4KXXYm+dYmbzysWeSp1dnokxqvgDchwJCnn0R1wmGpOXnPo61ttoXYKHlE1t75AI4mj4SM/6GiGxg8j29yTUkycD12pcvX3o7qPAzMl8l2mZ9Hmob3n78Y1pNB9bsifwdk3lp1xAPrR/SfWh9p21rXEK0lXnI+J561+Tz9XrtPox9/PgxDg8Pnf2QdiwC/I9cQ+dXp2yYMv8cQIDHkV7LqfAv6YtJsh9su6vitvbhm7ZPpt651JgJ6LZKzU4Zwi1idUAf41MceZwY8eBgLI31N123MdqXzkWUM57G4sbq81UfW4vA2ByjaClpk5pmJOPnBAzhnaKQoX9bviXxtXjbgI2hF40bC3MUjxjlACqh9iT56rruAefaF6U5L2yoDCEwI1RHIUVS4yc9BLV8hpKmZFM+KeN47rOYgTynHmJgWUgGLUy2S0yGXAVl2wG6RBEKjV+yLNtSikesX2hxuGwxD+oUH7ovGV9kf6Y0dJ9qw9D7VlrPsn0kXynfGER51nXtbU+lga+0SB5bsYvx43Uf67/Ub+hsVPqfTqew1rot8bS+QvVJgO3x8TFmsxmOjo7ceVS5ZchtF2OM93X1t771Lbz55pt455138MYbb+Ctt97C0dERrq+v8eMf/xhAt7U+7wsh4n1Yhseea3Fl+eT7wY1kMj71maZp8PDhQ9y5c8e1ySeffILlcunpVKVzZ4xCZed9ODSXpeSRz0PzCF3l/KaNLbn6a8m73/Vvg8WiwXy+xmzWyjKZtChQVdF2wu25sQCwXtsN0BoaL9vnTUMgLXnYdh613TbF3W+A+i4f0wBr6b7Ls/1Naei3VxPQqmLRWPzZywWeLxqsgR7Yyr0fe+EijP5cuABFKZ2XRuEVAmUdoAd4snnPeNhG7vbi33cNVA6+eeAdsaUtrNl5pwb+Ga7G+GCpB5zaNq3nEYt+fO9KY4oIl7J6YLD168m9y5s8vfpgbc+9Zr35FbbXT6gejDH9LYmN3za9s2P5Wa/Gj+P+gV7eMp01G7DWdls8m8q0WxjXXR19/P2PsV6u8c2/9008/ObDrp4CXcLzjpbAqkYuiojLw1l6N15Z/5m14rcIc2noGQdiWf7W+iCrB0BaoQNa9H575UiULVg3AZk1cvJE0qjyAmqdyfJyeWTZAbh3FwivT/ZhvCv12JTxeNxdrHM5pfStbfmn0pToP9paMHddwvVm/uGeBpiHdJ8cGYeS5EvHbEiZXjUas29pun1o9yaeBmjfSTrGhj6S5LxDgESof/HxJbXeKFlDhdYmMg7v+5oOLmUH/GPq6NlqtcInn3yCy8tLtwU2vRPWWncuMw8nPnLbY5KDPLpTRGsoTdbPI40x1r4KdBvLmbP+Jtq17Lm2CU5D1vCxtCVxQu2pretD9Vli69Xk2Wb+k/mXtm9um4TsMqm0MX2jRKbQB+8ldRmKO2Z75FBun8klD93cpjMMpZzCkLFaix9TBnLzjCkqoRc8N69tSFO2Qvlp9aINUHJwylFItp0MZHoNjA0ZTHl+IYUulCZGMeUz1B9CaXJBkpI6C8VNLX5y+6LkI+s6puBri49Q/qEBKxYm85CLCiK5SNUm3pwBMyZjbPLNoZJ3OId/rrEkNpHKODFjQUomLX6uAhCqW21MCs1NsYVhqo5z3jG6l32ktJ5i+dBZN5PJBHVd4+Liwts+N9SHcknr39vM71r98jY7ODjAZDLB0dFR7wvCUJ8jMPbOnTu4e/cuvvKVr+DJkyc4OzuLlp/3txKFmjxzAeDb3/42vvvd7wJotyp+6623cHx8jPV6jTfeeMPNPXR+rSa/pNA2xKGvNzmFxkFJ0uDBeVG4te3Xmq+99hq++tWvunI9ffq0dwasJhMf31MfHZUYcrh8MpzzzXnvU+95SG4ZP9doqj0PtROFL5ctGHt4WAMwWK8tgAZVRR6mHBiluuhb8AmE7XjTFsdUV0ZJ1wGtbV4ciDWbqw/MtmXsZKDipoaN+cri+8+ucbq2mBxNYDbbukqvRAfgUTiERyzg/waLxwIpHU9L95ynfM5BPHkfA2I1b0+Sw6Oc4ZXqVICZHl8GphIAS0ArAaF8S2L3bMNXArTEO+YJS9schzxkPZ6snA7M3Mjggaa2qz/+zKsvpd0prjHddsEOhOVbD1P7GJanYfKyvsb7gSsnu7ozZzdn1jqv2Q0g64GxdN203cff/xgff+9jPHj3AR5880HXLwLDiQ09EH3Ei2v7z4PesJuwHhAL/7e14rnteAbvKY2N5LN5Lvk7GTbPNE9Zr6xKmSW461WblresahnG+DmZRN3wvGJrR08uGc0oYXumkAGrVC/ch51orDxieoqmg0kdKLS2DNUlhXFdh8fV7CL00R+BSbRzjCZ3SEfR5Cmxb4V48GfGGAdyUbmG7m62a0rppyV2o5z0sp219g/xITBWfpDLy0FxQ4bt0Hok18ZTSnJ9zNcLObvN8N+83JR2tVrh448/xosXL3BycoLz83Ocnp66tLPZzB23wvnxfkplns1mqKoK8/lcbY+Y7UKOCfsY+/ZNY5Ypx+ZF8bbpf0Mop5yltq9cviFeobH2i0Ildq6QjU7jmWOfkO/2mFQyVsTmXRlniBw5cXLtokQlc4m220NpeVJ6z6tKo2xTHDMajtGpaNuLEH9twr+timGMQoZHGSf3pQp5y/Cw1EQY6/jSmxVIfwUYy2dfC7vU1o85inMOxdLnKiq5z3dFqfqKLRBjIEms38XqPjToh0CJVF43QbtaFKZ48MUS1ZccI0J1qIXtsk53lY9cOIby3mXZrPXPKiUwNhb/Jt9/7d2VoCI/42i5XGK5XDqPWJoT1uu1A52pfafTKeq6xtXVFaqqwt27d/Hd734X/+Af/AP89Kc/xQ9+8AO8//77bnsqmnPIUJUjryRp5Lq4uMCLFy9cejqX6Pj4GG+88Qbee+89LBYLnJ2doWka9xW21i6aMr1Nfwst+FLjhDHGM1BMp1McHR3hq1/9Kowx+N73vueOfIjx0mTTxt2YUUPrL3wM8gzgwogl+WlzszSaanKE6ji13aJmaJO/07qvwS9+cYG7dyf4lV95AGtbz1hrK1RVu0Ux0G5ZbC1gTAP/vNjOC9VauWVxC7626brwtv7IC7Yf3snXxWnz4GBul5Z+0zbGIbKwqCYVKgKuqB0ZwEb33FBpTOcJG7uXgKsHrmleuKafnwYKu/is3Vz7sXhdZcAHYJVqiXnIqt6SlvVlBmAC6M6BFbwJPO0wv/AWxZTOhTP+XAYNiFV50lwqwFnPo7Z7Bbr0VEa+TXEjAFFKvtk6mNrHA3mNeP8ozAhZSEbezsTLGC+OqQzQwN/WWICuPTC2ZuGbOiTALQS2JvuGaBMlkgrOemMYxZHgIb/X4lhfVwzeb9JKIzzPh4Osbnxmebl7zkvIpIGvXlkC/Hp1auHLgj6PHiAsnnn8RZnksyLA9WbUuy8sSX0gtq4MfQQeMwjTlfQe+RFfSE8h3dTa+HEWFD6bzVxZaHea0jXkkPUOeezuyoC9T8qtr5x64usdTiU2DX4OqhavZD2YKltofZHiKd+B2HpH6vKx42hC9ibyGj45OYExBnfv3nVpNOA6pMvTB8+h7S6H9OOcOuYfVOzDzvklfUn7pM9Ln47N7fwDfqDv5akdY8btHDwsND7J31pajUrqP6T3lPLZNYXG512M0beBisDYnElaUmjyKalUabyWeWqT91iAR0nnjxkkc/OKgSFDOqLkp73YY3Z6jV+OoTlUV1zGEoN1jC+vh5AHE48TMv7myqClG1JHufmE8ighbTJI9YmhfTM1MfG4PE1J+XImNI22eadvcgKQ/TXWj/jv3I8ptu0Hsfbbdb2V9DctzrbjIl+cpfKKUWpMio03JZQz79M8bEy3zTIpsPIMLD5fcwXXmPbL5a985Sv4W3/rb+HOnTtYLBZ48uQJPvroI+c1QPWnndmRIy9fwBtjcH19jfPzcwCtcevq6spt6UuA7LNnz3B+fu4p3DHFPUcOHifXIBYzGsbikmfv/fv3e4A4fxe191KO0zGdLiQLH1e0d017JmXS6oobOamPyTqKndck57dQmWNjaKzu26vBixcLXF+v8d57a0wmrWesMQ3W6xb8bBqgaVp+TdMiOVVl0TTtc74tcfsc4Ge90ivVNN29tVS+ziMWHgJH5eFbEfMySh2ly1OjtQWWjQWqFmpydbsBx6S3qeflati/dq+R6fJw/EwHtrl7agcW3iY3jge/d/HB4jO5QiBsD7CNir6J6PAg6zWLAxOpzxm0gCzFs50sHFRy/XQTn3hIoNQDomQZuFym85QluRzYyvgRiOvqx4oyUXXS9ssC6JXAqCvjJm3P05XaiPJldebqTWkzDvS6/iXqQANtvf5I/Yi2LTad/PSsWTZYXa9Qz+ounFHUIzb0yCrpqJ342MT7lHxuWbhV4vA8rJCFpxH85dXaztvVwgc+OehKfPi9Vi61fJbJoZH1/714WrgsC2dF8lml3Baq/MVr6GFqZZrtCAa8XdNQnXqbPHLX4yFwLZQHzUe8/ckzkT+TfCg+HS0ht0eVcY1pvf5IPrKRla6NKY9c/ZOXict322moDSCmG4f0ZS2vkvUJr1vZT0tl1/oNl0eTLWSbybUFSX09FEfmH5OBzoc9PDzE0dGRi391daV+7KmVWW5bnGODDBHx1dYXsmwheb6klmK2wF3mt2seJWPOLso+tJyx9ki9z9u8UzEaYpcsyT/HvsfHkaF6VY79UeOXEyfFbxvb9y7j5rZbaN75PIyno3jGjkUlnY2nuQ1esKEOdts7Sc5CI1b3KdK+glutVh64HvoKMKXASFAjJutNU0yunEk6NQDxOJoCn1MvMWP5LkmTL1VerX+UgBNj0TYLpV1RatyRoEpscQaE+5mMEyr/bakXol0pi0OJdn6gOuRAYYqoPbRdCnZBvO4mkwlms5m7v76+hrUWv/Irv4KHDx/iO9/5Dk5PT/E7v/M7mM/nLr22bTEtrh8/foz33nsPAHD37l380i/9Eu7cuQMADti9vLx0YGiOAi2JvrKu6xp37tzBo0eP8PTpUwBw2yNPJhPcuXMH1locHBzAWovz83PMZjN3nlPMICHH430T6UUkw/X1NU5PT7Fer3FwcIA333wT1lqcnJy4/iMNLdo8IMcOHp/nTfG19DJceo9w+WUe1M/pXaEPAAgg186ckn2FP5O6IzeaanOqNhZSmfg7SLLQtmhtPODiYgnAYjKp0DTVJh+APE5p2+IWcG23MQYMmsagrgFrAbMBm6qKy9KGt2fNdvfyvy1L/55fu/qxEE3VuydaW+D3PrvG03mDq9q0XoJii+IeeFqZfhj9I+zlGvqnvLx24vcMZJMesZ7XLUQYWPjmt+xHPUq98tQVHUvjwkMesF48x8Z6YaF71TNWFUuJr2wr3PO6ZfXl3Yv6cNsA281YoPGn/lKxsWTjsUpbBxMoxtvZ23J447XqnsGSA3qvnV04ut+22oBrbMtizTPWWANTt/3VNpv8rcWf/t/+FD/5736Cf/d//u/iwTce9Oo55hmrgq6bcP9WgIIyDGxeVEDE3twpw3LvLZsDWJj2W+alxZVy9fKMgaEW/fxEmXtzlZQf3bguw1XePKyx/WcpUuS6TfQq2DFKKNdArh0/pKWVa0D+mz5EzJGH7BmLxcLlH9Mv6QM77ulHenYsHy57yvYTS09xc9cer3o/yllTh3TEGPF1OOmdMe/RbYjLqfGiflVCOYbzlA2KgxxS15bjddM0uLq6cmGhusqxe+Ws0XLrIzSu7NL+8aq/U68avUr1XQIE75N2UYfG9Hff0uLEZOLxNHv0xcWFOibJPOR8mDMHhGR7VfpaLuXOazG7nqaT0bGXGoV2mrht1DszNqezhaj0hc+NP8SoOcbgExs0SvhrRs1U3NgEntNBS6gUCCshjRdtcUPPx54oSmUtWZBsI4ucHEPPUvKl4mj9NsVfM6an0m1LIRm194We5SoYY9SvVg+xui0ZL0rGhCEUGzOl4hJalGkUmx9S40ZocRUirc1j8fh9qH5vSilN9VVN+Qj1p9DcsGvFTcpIXxnLOA8fPsRXv/pV/Nqv/RqePXuGf/Nv/g0AYLFYBMf7yWSCpmlweXmJ+XyOpmkwnU5x7949TKdTAG35CNjSzgcqKYe11nmIErh7cHCA6+trnJyc4Pj4GKvVyp3fy8FXz0grKNTHc/pyafuVjOmr1QrX19dYLBYOUJ5Op24eJjCWG4NiCrJWTtkvU8ZKLmds3CS+qfGcyx+rC81YlqsDhcYablADfK+R7st54PJyhaoyuHOnQVUB63W18YDlnrGWecDS1/eWecwC1hoXvyszydLGB3P7sxYw5L0HascW+O0QA142Wf5432ysxWfzNZ7M15geTd2WwW1KH8hzYFugTrXsgl607N/zjCXeIbkN+vIZISu7SiA2V2aNPI/RNsCXC6wPb5pL85SlcPIMhYHnxcrvubeqA1tNP3/5rvH3z8dWjQOnpGzUBh5otpGF5CYglV9dd+XlIzl4PF5PvKzEj5WPg70eCE3ydq9Irw08ANl0eXh9bnPvxp4NkH76i1NcPr3E8nLZb2PqA7mkpbX+c827M+TxqepfMr7gr933dBaZN8/PoieHyluWOZQmIrPniRvjp/DUgN5QuRxwG8jLlRvw+zbYc/q9smiWjV4XAUqt81I6Ua5d5fNmEIzpYzl1FtI9QhTSi0L8Y4ZImR+NO3zb1ZCeEstTu88FsnJpX/0ox9gfszVo4blxQnpqSV2FdKKha9fSdXcpX8kvlF/O8WB0lXUg80o54ITea257lFSypk7Zy2L22zEp553KeR92mX8o7lj1o60Pd0Xb2pbHopy1NY87tJ/sok5vUp/IGVdD8smd2EL9eZs8ckjjVWr/Ssmxbb9N2Y9TcYfoFvIoCOKzrbPmvsaWHhibM2mOQTEDam7B91VJObQrMEWGlSp12u9dUo5Cs1wucXZ25t3LFybX+Fq6XYCm6OYuWm+KQuBS6CwbLb30Ogopk7FJQ6u7XX9xEsuf10tqMRzaqjK3bccaaySQRPLtikJya6AWT5PiMXQSz33/ZLybfgfHoNI+FOr7fGFZVdVe5uuQfOQZS3qDtRbvvvsufuVXfgX/8B/+Qzx//hyfffYZ3n//ffz+7/8+6rr20pAXwPHxMdbrNS4uLpwXZ1VVeOutt5zXrFbWGNAWItmXlsslrLX4O3/n7+Ctt97CP//n/xyz2QzT6RQffPAB/vRP/xR1XePBgwe4vr5259fLcZnKRjKt12vPu5Py3tdczM+Lvbi4wKeffor3338fR0dHODs7c+A4xZ1Op5jNZs6rc7FYBLf/ihk0JcXKbEz/S1L5nKflZy8BcCA5vQvaWK953qZkCenCWl/jnrfae0phy2WDH//4DA8ezHB0NMF6Xbs+ZG2bT123rnnk9Uoesq1nbHuWrLVdnRLISsBqe99d5X9bDyR/d+W/u/Lpv7X7xgLVpELdoPUUJK9X5v3KQSvP4KaF8z8nnJ6e8+FxyOvRwLgrz88DXSmMtWsPnOXPKJ37aXphIeJesJRGAmcuH+s/H+oZy38nPWNN2pPWhUnZ6F6pO89zFaL+6bppM/Jgpbgkk/OQNZ2szpuV+Cres+5asXpm7d47v5bFM9aAgGHudUvXqq7Q2MY/M3YCmEmbr6rfRDqKB+5FgMoe2Ao2PingYu/ZBiz0PD43vz1AKHRvfR4SRHJzs+1+u/vGdmkUfj3wVoRp8Tg//s/z1cK8egn88/Qqv8a2fZDKuflQxmtLo7Tnht/lo0uszlYtIJtJ+zRCv4oUq5vYeiJnrTEUbBkqk5a/MQaLxcJ9FMl1II2nZk+J2ZQ6HaNc57sp2vX7ELI/8DokW1bOkT/a2mBMkEq7J31ZW4vT8xBxHbfUbiZ195J2Glon3NPcGIPz83O3i06u/SzVp+SRQ1L/3xXl9vXb+q6OSbd9LtxVXxizzDdZd6U2eS1Ozg4NtIsEJ9r+nCjmaU8UOnN6LAq1awgXGipL6KiBHJvsbSGyI1ZV5e3UNxbvfZVZ3aZYCiAVutJJdBeF0ZSgUBxOYyo7Gu9U/rkUeul2Qam8xlg8yHRkpA4tBkr6TWn/isUfMgnkAlVD8krlEQIkh75zY76roYVLDoUWfpqhO8YjJltofEilzclnjHd1m/6h8RkKCoUW4KE5Ylsq7YNaWbSxONeQMOS5BF6416i17da82rZnpeWQ+YVk2BdpYyGV/86dO1iv1/jKV76CTz75xJWPK83SW9Zai/l8jmfPnjmjASnKdB/Kextqmgbz+Rzn5+c4Pz+HMe15XI8fP/a8SUnWELDHDTBykT6mLlS6EF+tVri6usLz588xm80wn8+xWq16czBvvxT/HBliBib5PkqdQ/vSkeJRGxwcHLh37fr62m3TV1WVt01zqdEplC+XVZaL5ArpgdYCi0WD6+s1Tk8XaJopZrMKVdV6srberJYBse02wU1DnrGedKgqi5a12Vz5GbNk+ecIgNnEobJ14dYCLagLtOLL+pJ1pZTRdCAsBze7FN0zziLmvcrjev3FoPdvjO896+6NkjexNex5L/vIWCxlH/JaUxoLB3ryMC8fOmuVtjDeNK3zot00p+uTRvDdyG9hu7NnFVmcJ61ytqsf3fc+hUV3ZixL68lv4IO9Wp2J7srL49WRQc8LuAd2GVEe+Sps8nfn1do+WO154vK+ovQ/KZfrL0Imjy+n1HTmmt5694AYy2wgTIZb+GAt+y3TePdCjp43qcg7CC5Hyut4W+vfU15WxJWyynz5fSCtA6JlnQC9cC++DciWq55YOM/YUh1h3/rEPim0FtvWLpBj2yjJh3hqek5I79B0nyHrPX6kSWp9UcJb2teGtsG+KdWepQBiimR8qQ/KME0+rb61ODFeMRua1Fm1+KV9vTRNSEaeP6+DnHUFT5sTj9bhWj9IyRYKC6Ub0o/GsGOG1h+7el/HWoOPYeu6DXPhEPvhbaIhcu+rzDnjt8Sn5Lwqn8uwFJ40dL4updQ8ocmWy6ck/RB+JXaVmN6UagvtXjo/hOTJHe/3qefs/MzYMSaYIYpC7HlOY+2SpAyhSVXzSskZkLR8Yr+H0m1X0EOT6i5fNm2w1+QKpc2hmIdPKH+eZ8yblRvBKV2onWMTkmbQjwEpu2wTbULOIf6uxRb2Q9s7xmdXY5I0AoTOa5b3sl9pEyr9LhmfSuUeymOXY5Nc8BljcHR0hLt377rw09NTz9OQb+9LHpT0vpCnXeg9vi2KvrX+FiAk9+npKV6+fInlconpdIpvfvObePLkCYC23OTpaq3F1dUV1uu1VzfPnz/H7/3e77lx6NNPPwUAzOdz7/yHkrkwRev1Gv/6X//rXp1XVYWjoyO3m0NVVeqZIJzo68vVarX1QjhnDsuhy8tLXF5e4tmzZwDgtn7uzjP1AdmU3KkFMecX68skA8+X4nPZJK3XaxweHuLdd9/Fw4cP8fbbb+MHP/gB3n//fUynU3euL+B/McvLqhmryKBJZ72FPIM1Yyr1TX6uLX2Ewct6ebnGD37wEu+8c4TDwxrWAlREAmBbMLXaALTtPZ0Zay3J2oZ1nrEtENvWI2AM3H37u/tv5dN/0z2/yt+cjGm3KUYF5xXrgCvjA1d8LvG2B2bx1QUzTDCeiy/y0HhzME29B7ww3sb0jMvTrwz2PEA90IunsX6Yiw8RZ/O8pzcoQKIHyAqwkSWEA0sz4vfyYXXm3VN8w/jleshu0lF7Ok/YTRzHk4XRObNOFnof6AxYo/xD3DPPWnf2rEXnLUue5fzs2NrArLv6MDDuuTd+sZ8x72QlsHevgZwhwBBgcoh7a/txKEy9Z3x5eC+OCNPuZfzsK5fb+r/5vwzn+WrxYvL10jcsfqOUM0a8vdj9Ta2h953vtnrRWKStufjcnruu4bqC1A24LpPaSapknWqtdR8qkvcflzVWNq7rhIyhsmxyXtZ2m9rF2v029BOimO3MG5eQt2uY7CsaXy1+KDyUluu6Uq4xdhu6SUr1EV4W+viX26a2tcdpfErXpmPV8RhrxS8KjbW2/qLTGGN0bP4JxQnJcXBw4MKWy2Vve2EZfwhJ2xfR2P1Jm3O1eUerm9RcIXncBMXwEu2orNiz9XqN5XLZizuZTDxdJTbu3+S7HwRjb9Lgu02HTk2CJZU9RtxcpaYEiCmRS1PCZR4hpbKUUkZXIu7tRPdDZdgGwMpaNGfmHwKqZH70PGfyycmvhGeu0ikHrJCSWVqGXHlCfSH1bg9py1KZc9spVbac8SIlT4pXikfOOBmTMzbZa5NsqdIQk4v4ldIuJlveL+m3McbzjCVwiAA6+Y5p71tqvlBBiz0qE1p7WmtxcXGBs7Mz56VIXqUknzHGKck09lO91HWNq6srfPDBBy7u2dmZ24aN119Iptw6IH7UVtKTkgBLHq4Rb38JFI81v4xBXB5NLyCjm9x2l6ePvfM5ZS0ds0JjFYU3TYPJZIK7d+/izp07OD4+duE5+lZs3uaGSi63NNLKtFK34vXZhgHrtcVq1WA+X2+MtQZ13fJYr1sPVQJiu7NhPemct2xXls4jtg0nQ3AL1pInbSs/ycJ/83LwsDa/7nf3HAA+WzY4XVnMN9vHbsRjkm7+jBcIAuwIlOp5ooK1kVHuN1cXn9pFaXYvD8PqTAFVVYCQ/VaBWP4sQXJ7Xw2Y5eEuPwsHdNJz+d71gNSN52hvjuKgnd/kLox7lXoeqJIflYe/C1Q+K/gxsLfnDbzh7UBR25XBGNOW33Q8tTCvfPSbe8aKf1e+DdDL70Nt3+t/m98OuN2ENU2DJ3/6BPOzOd76q29hciDOV9dA1+6hHizHWA7qWT88BMT2wsXvoIcs/RMfik882L8b78Tvntwl86OWXiu3mHettb14FmGZvCuVh6ff3EtP2F49xOTPCR9AN2m3KaGx7DBD1wAp4ETq1iH9husImqyanhWyb8ltXDV+8nnOml6LE1tnyPxDa4190E0aR0PEdeiY3SpnnR2Kl+ITsgPx3zk2qVx7UkwGTnzdkNMHQzKOsXbS8pP5pChls9Le/VdlDP6i0y7GlqHj8S7y3bctqCS/IbbHEnszPeMfz7dr4c7hIcVzWxt9CcVsoyl+Jf0ppU+Upg2lD+lLuWVMjf/Hx8eeQwvQtjXZ7Didn59jsVjcmA6TS15pSl+mXcYnCp31GKrYIZN4SBkvSX+bKFQvJR0+ZwAIKWyxNEC7ZSIHY3e9/zrPnytM3MuKy5einLPncuWh+LmTgGYwzn23Sgzm0mhfKltpe4byk3HkonZXim9KEYhRStEYqphpba+FD32nQzISz5JJezKZ9Hg0TeN9ucSVoyF58fYfOr+MRbJNqQ6I6CzRy8vL3hd1k8kEs9msNx7JduDPqN5iRpV9EIFevB2ePXuGe/fu4eLiArPZDIeHh5jNZp58l5eXuLq6cmnm8zmMab9qfPnyJf7Fv/gXLu7BwQGOjo6wWCzcvDFGWQnYnc1mODg4wMXFhedNee/ePSyXS1xcXAT7Kn8HrbVYLpeo69prG3k+0U31Vb51NskhPWOXy6W3UOF9kOpGM9xwCr27ueGhOU4Lm8/nqKoKDx48wOuvv44333wTJycn7kOAmG7Hn1H7aGWXBib+7oXyoHvtnCzKf7lscH6+hLWAMe3/ZNLyruvWm7UFV9szZK1tPWXr2sBao3rGEq+q6ni2YRSnDWtlQe9euwIdYOuHt/Rn5yv85HqNala3HoJGB74caGUcPNuP02XpAV7qlden6dLxME0Wb96kMAJpGdDq+qG7mJ6MKviaerUt4xeKb9FtSUxpeH7sueYJS/fuN+lLHKglQJQ9c+EExMJ4Hqye3mU6Prx++DMO9LryUDrNQ5a1owNLGWjL20x65GphrkybLZS1vuDOieWALM+HvHMrf5tkGLTe4JtzbE21qZdNWzXLBn/yf/0T3HnrDv6D/+1/gPor6TOtZB8JhatAvu2Hu3EpACg6MJXChB5O5fGugXAZpv2WfLx8RXwNBFbzgJCB15Xt5yH5hcrC//l5sLBozy0WcbV6irWh2p4D6KZ131eR3Dyk1FssXNo7QvGA8Jo21l6l+rzGi+syPG9tXS/fGbn+4L9lub6kPoXW5GMRbzfNDjWGDavEprQLyln/D7H9pIzxobVdDt99vxOv4pi/D5lfxXoBXl25xyKad1K29VKaTCYqQKftDiDnXTl/xuZseh6KM6bN15j+7ga3kbS60BzvQvhPrIxvvvkm7t+/74XNZjM8fPiwV9c/+MEP8Mknn6g26Rjt+51UwdhUZ8kBTmLpKC+toKUvYwloIhWlnBctN98Q35J0/L4E/MjNJ7dz5cbh/ELtqZ2blyuXXAzINDn1o7VxzP09l2JtzcGWUF3GBpqUcTqUb8m7kIpTomCm2kl7prWfxof3DQI2htBY7Z3DO1W2nC0TSif1ksk+9pyDDjm8tDiSBwEY2nYhBFSl8tMMFLfNKEDvDHkXciCG6uTevXuwtvWUXSwWODs7cyB1arFIecTu90nWWuc1ytvns88+Q1VV+N73vofZbIaPPvoIjx8/hrWtB+z19bW6xQvQjZ2z2cwLJ+B0bDLGuDIQYN6CYXWwrvn8rBneyONWblsn+dwEkczaOyuBQ01p5vElT81YqcXXZOLE52mtzoEWLG+aBi9fvsTLly9xdnaG9Xrt+s16vXZtGNpOSJMvtPiR8XrG+kh5QnR9vcbTp9d44w2LycS4s2Jb4NWgqoAWQK1gLWBM53pqbQu4tvFJ1v61+28BVR4GB8TxspL3LOUNAG0Y8fSLZ1oYgZ0V64GXXkzTA9ukV2zw3TAdENcDMV35jBffPYNIx/L38pbyQcThomn8cknG1boL8bfdveZp2iZnfdei7w274WWtv7WwMQKQJWCUA7Gb9BwM9gBd4s+8SV04ldWyeuRnx3JAFiI+lVGWgcBQisKe8zaRHrden2Hhnucvbx/2b4z/3PVDdrWV9cJce23at+fBmUNW3tp+uATzGMjI4wS9ZPlYz5+zNN49f86vIh/ueerGSBvIU/nnfDz5FTl4mKsDJV9KI8NCZVHrmvgyebxySVliJOpjKKXWBJ9XI++29SZ1GPksNvdLitW11BGlzSRnLS+fST0ktV7UdCyuU/GjHWg9Iu07nIasE2+ahtqLYrqhxrfU4DuEYu3N+0bMjqOtXSicp8tdk+bG55RT/hxbyDaUWp/lypiq5zHpVRzTh9TrtnmU0jb9cRvahmds/H8VSM7DoXknt79Iu4HW7zgGoK3hiVL1qukQsfEkNSbnyCDbu6qqrOPmNJl53Fy7cc68H8o79AERt9cQ0W54IZt/SNcK5c3bnGx8Ib6pndV2QSoYmyvEkMGU5xGrjG3z0XjuqnI1vtqLOYYMGo8SgGrMhZ9WNjnwxBY8ufnQPQexQltH5gyeISBgCGl9VfLXzuSQ96V9I2T0TsUtodxJIzQJaUpCTNbYAk9bCJcsBnPqS+O3C4VR8/bP+UBAjs+83iXAq8mvTaYhZSe33JoiQ/z5WZAEnsi0OXnxPELKzG1RRknRoy2JiWhyv3//PiaTifPEPD8/dwrHZDIJzok5BheKV6qcl8anuNxoQzyqqsKTJ09wfn6O3/3d38VsNsPz58/x6NEjAO0ZHpeXlz1wldqUlDI6VxZoz4rNBatLiJdjvV67rU9ShjCg33d5HfKtfrUPL26SYguP2NzI3+eSs7CIrxau1WWoL9M/P3O5aRq8ePECDx48wMuXL7Fer915MavVyp0VEhpb6SMQY4wKnofk1uZvmS5nfLq8XOPyco2qMjg+nihgrNl4uHZgKJ0b2+bTXa0FKAtjunvuIWsMjc12k08XH+jA2k7e7p7zhYeGWXAPwd68wgAtAsV6OoECyFI8T6/kfKS+KfLhfDxAlsnh0rH0PdnRpe2FiaoYTBoP23/GgUwP4CQA0rZtwbcV5nOlMaYHWklA1NuaGP72wB5fBox6YObm4vKyrD43fDywVfHAJS9W6RHr5AF6YLEHTBtWLl5nRtQr1Y8JlJPiib6ledlSX+TPXL/ZnEE7SJfUklj0wUf6af12CIKwEtzkcZis0XuW1oGvsXxoXLRKHIirVa68bLJ87J/nkwPKumdAnxfL2+PBwoK8MmgISB9abw2NNxbdFh0nh0LryVBYjI+ci7R6D9mDNP0kZGSUeUo9I5a/XDO58WqzXpnNZpjNZlitVu7sNfmhLKB7C+273XPsEqm+P8SuEbPpxWwgIdLaJCSH5B/qqyHbkowrzx4MeUTLsFwbBc8/RDltEKvboRTKN7eMKZ77HndvO+3ChiZ5v0p1PYa9Nqe8JTa8XJ65+Q3lFZuXc/nysayua3VMlCCfxld+fF4yZ3C+fAyO2RFyeWry5tRLat5M4RQUps1/uaTNX9QW3MYEwIGzY3kBy50FpcMHp9z2HnPcCZ4Zm0spZUOjmPdrDDnPoVDnCIXtcqLYlkrKkYqTm0fOi79r0tpQkzM18Ml0uYqkXHzcNirts7I+NeU+FFcSfw9zDM5jUajfl05uuTLyvrCrPqAtlmML29Cz0v4ea/8cWbeh3PbKHZs1uW7SKEQKhbXWA2MvLy9hjMFv/MZv4OHDh5hOp3j06BE++ugjV07a0vf6+lr1IpYLPq2cQ+azbepKMxLQF/bf+973UFUV5vM5rq+vcXBwAGOMA3BjPBeLhbvf11dq2jbI6/U6OR9Jg1xobrpp0gwGMV0sNL9qcajsoS80eR1J3pwv79c8roxPRkFjDF6+fIkf/ehHuLq6wvX1NZbLpWrklLJrH3Rp95KH3JqbZApRqu1PTpZYLs/xS790jAcPZqgqoK617YpbD1kCZNszZSsY095TNsZgk6YL7/6pbv1nlC527crSlvvxYo1HiwafEcDKASrAAVKa56kJpUGX1l2dHG18l47yMF0eHuDLeWv8TSeTCs668vrl8eJJtkPfczkcMjaun1HWBDDycMveI4M+IMuAUWsZ6Lnhxz1Ve3IxftKztY2y4Y3umXbvgaTsGcnHZaPyuTGAy8fLxHjy9vTic/lFv3D5UjyjbJtMcTnQaow7I9alr9Bud2zYltAVsDhb4E/+L3+Ch+89xC//o19GPS3brrjnaWn5TwHm2X6Y6yfWv+fAYS+M8aGw3j0fsyOgrBtDLZPdieTzjP6j+63xU/NRyi0BVQ1Y5by8OhHP3H8D2Ma22xdrJMu9kWXxbIHl6RLr67B9I7am3Rfl6NS3QcfJITmfx9YQMf0hxEPTAbX2kx9scv0xhzhPCa7FyqbpqnVdYzaboa5rrFYrXFxcBMtNPEuOShljTcYNtiE9W8o4tgzb2AtDazXe16hdeHvmGMWJ+LEj25JmDyvpn7G2uY1216H9IzRG3KQd4otCt3UuvM10G/QJSTky5Nh7rfWxIrLfyDiaY8gu6kGO7ZxkG2gYV47tK2VHvY3E64PmO3KK4DSZTPDgwQMvzBiDBw8e9LYpvn//Pn71V3+1Z9O6vr7ubVW9XC7x7NmzYN3Vda2C07uct7LA2KECxDp4KFzbPzoGxA412JfyCylSY3R2WU/SGBlLF5JBk1crm5ZvTp5aHkPrQuMzZACy1j9HI2T8jfGRL2BscZGKk3qWG98zSiX6uyZXa9TtDECx90nWvZQn5sGZUychI3xIlhhpcoYU45Q8fIG6D5ILnZLxJPYex+oz9f6HFrg5bZl6Tr+lV/A2k1vM2HBTRIoF0L0r1rbgYl3XeO+99/DOO+9gNpv15jrymOVAZIq2VQ62VULlXEWGnaZp8NOf/tTJN5lMcHh46BmgQvOHtbYHRu+qTfl7t16ve2OjHHv5ext7ZzXDWCjuvkh7H3P6T66OJetJu48tTEIGTGP6IC8fTy4vL/Ho0SP3heVqtfL4hdqHFmXUZ3mbpgxh/LdmtJJlkOXkfM7Pl7i4WOH+/RkOD2tMJgbWwp0N23rKAsY0sNagdbVzEngervRfVUDT9MNafhbtFsS0bbEB0G1hTM/aPLG5ynkK+GzR4P3LFSaHE5iJKLsRv00XZozxn6MD0Xo8jN+3DIvk6pbF6+Ut6p4Dqj2Q2Phy8Lx65dF4b0MyOet2xhh4oFzbXH6bsDC673myCiDTbS3M86e4DMzkfFw+8LcrjoKwogw8rVoPmzz4b5KZePXAYOPHIV4uDgGkwqvV42XQ93oVsrkxTHjO8nB33fBcXa/w83/xc1z86gW+8w++g2oy4FwsUV29ccn69e3Fsf171RuWh3GQVN5bP430YJV59jxfuVwyPvz8pLy9OUyEyTrS6sCLz5NyWcS/S6eE8WdFnq4WWJ4vMX8278kdTBJZ06Tm6Vj8lN1jX3pL6RpyjLxiOsKYpK2lubGV1ui5617NqB46GoPiA7qNraoqb5vAWF/hulxqDS9l3oZCIELux5olcWJ6n6bLSzuaxjMWRysbb0+tj4belSHllHqpJnNuW+fkq/HSwrZdn5aSZostSfsltbTvuhjDjrGL+ENk2nV/H9q/SyilT6TCYhQbh6y1HgAb2+I2lu+2dkktv9h9KEyzx8ZorHmQ5z9WWslD2t757mREk8kEx8fHvfTHx8e98Pv37+Pdd9/t7S74s5/9DBcXF94YcXFxgadPnwb7RwiMDdmhxqAiz9ibmGyyFloDeZTKMfYguUtFY4jxt1TZSg1oxpjeNgGps1LHrA/PiGeMCiSOkV/Im1vKoD3PodIBkAMgd+7c6cnABz1rLebzubeIS7UpH0xD7Wmt7dXLLr2M5XhQuqjX2kP72ngIyQUN/7L3pom/c3wxxmXj2w3zdPxKRHG4QkQL/TFllvLcFpJ96uLiwnm6kpfocrnEw4cPcXh46KWlvgH4Z+lOp1PXX2hr4BiVjvljE9XBnTt3AHSgdMojdhdyUL0NqZMUEKel4WMc36r4tvVVTSHmxMdvqr/YRzz0jofOZ81pd75tMPd81Yx+fGyx1vek5u8R3VN6+udjEpWNe7wSoCvHPmmoyi2b1Ae0NI8fX+LZszm+/e27uHu3/ZJzsgFuWmC22lyBpmlB2rpuUFVtPXQgawu4tlfAmPa/BWfbOEDnIdtuW2xcPHrWykt12MXpKhYtsFQxvcCIvrQBxCQA2j3u33vGXsVD0eVBIJgEaLU4huUvwTbT5S2E8crghfH8cqnHXgRwjIh7qFohg4W33a8xxgtz/cvA92TloCorFwdmjWFALEvDvXEpDpWBe91qoCwAJ4+XL/dApfrZlJV+c89Vs+nbXE5XLwSEMf4krwTSPR58a2TA6z8SnKX4sh+aitU38TRdXZvKwNQGqPR1qwcqS9KGFocv2l6cHvgoQVf+TAEQXRjX70L3lLZpr7bZxGFX/pyn6/Gw/Tg9QNhGeIiw2L0LaxQP2KaLz/97AGyzKa9FdxXlCJJsjx2SBqrwZ7dhDXJTlFsnY9qZqqpyHyS++eabuH//Pi4uLrBYLHBxcaF68uTkya9SP5Hx6cgPbpuRO9GkPDNLyjyEaK1D5amqCnfu3MF6vcbV1VUv/+l0Gjw77yapxPZTanhP5Sv1ZdKrQ2NCqt9pfS22s06pjKGwsUmzY4zN90vaL31Z959Pusl2leNxDoA6dDx5VfSwUjlDO6Vp84YWrtnvrLU4OTnB5eWlF3c+n+PDDz/s2Zop3m0fIzwwNtbxxu4suaBYrAJjMoWUoFSDxJQBLXwb5WGXnWOo8TlUHq1vSJApxFszZEreJQbNEMXk0ozO2ks+VDmUZczpZ7sgKSsBOfIZDZKpxQuvE24kJRqycOTtNHShGwJZUvxyx4BU/qUk66xk0R/qsyWLim3kj40LGnHgYxvieWp9WIt/k6TJtlqtPEMHgT51XXtgUGzrLf7FurZ98U2WOzbP0vmrZHyKfbXP040tW4xnaX5yfE/Nlfumseowd4yRhr/QOKHVV8hgFOIT6zcluh0ZkSaTiTtX9vLy0uufIY+EmAErx3gZImstLi5WuL5e4/p6jdmswnRaAWhQ11THxJfXGdChWH6YcQBT+9+ucwyahspE3q9d3BbM5XlQu7X5U9Go2O5dIHCKI18cZDMM6IIfr+ehysmI5yKti5PgzeUI8eBX165GiSPk6lGkGwbT8HRWAHR+8zo+Elh1QKYN6FYG/pbEnCnLuycT58vSeTykjJJfKr3Mj+Tl5ZBtYcWV2sSgl8b1rQ1YxvVHa61XN16XMQyA9bL3PWTlPfFrcWKxJhBFDnoJ+5H8uJIHb2sJ9gmAMBpuRR48H+uHpTxie3nItFDSav+yvFYZYy1bqwT49J5JflpZbIA3ReFjfsmwr5ULSrkS4SV6cWgu137n2F1i83FIhpumnPXEELBG1mHIcMvzPT4+xmuvvYaqqnB9fd0zGpb2hRI7hZSPzonlfEIG05w+ty1JOauqwvHxsVv/LJdL9aPUofa4HF0z9U5s28e3aVftndRsJNL2ErOf5JYnNJbkjA8hOUplGJuG2iNzbH83QWOvrW+ScuaoLzp9ntq7tBzaGKhRShcYW49JjY2ltuwx5IjJs8v8QnUfs69wms/nvQ//Dw4OcHFx4YGx1vZ32SO+k8lE9ca9KVLB2LEaJsRHKzwZxXZJOcY6MsSVKOH7eokkDVU8S0kCBHKrUfqdGmBy+lWoPLE2GaoAjQW67etMw21J25Md6LvkxxbqOYOlbI/YYDf0nd+lsrHt+5wLGI79/ubwkm0dOjshp25j4Avna23ruZYDNudSLlBzE0R1slwusyb6u3fv4s0338T5+TlOT0+xXq89hYJ7VkpD0W0qd4xy+9NNE5/rYh6t9A7FPEF3rc9olFuHOWOcNNxozyeTifuwgAOpxDOUD+dNdcj1itD7nWP4kaCoHNPW6zUePHiA119/HW+99RaOj4/xve99DycnJ85bnT6g0HRi2T+ofriOoo2hmi7UXwACJydzLJdtnUw350vWtUHTtNfJxKJpKlSVYR6yLYjanjVrUVVm87z9bYxF07RXAl/bK9zvVq52u2KAe8129538LY5gauNAs0207p+XHaLspruKiO6Zq0PTT8OfeXmw9J4cAmDzQFfD0kKUBf4zCg+CuQqpALUk3g14dOvLQsChe2ZEH7Xon6u6kYHH87xfN8+4t6oXboyXr+PHeDsgy4TvOTDJn3HvVOe9a1gehslM7w393pRXrQvW9g5kZf+eHKbzaAXQ84iFgXtujIGtGL9q037kIV6Z1luSt1ONoGdslJSoGhBJdcJBPgnchgDSHqhq+yCjer9JU+wRK8FZq/CNXHthTdgTln5r99Za5wnby6OxvbQ8L/KM5f8xYNWBz7wNBur+Q+w0OWvd26xTj0FD6lub96VeJHUSrm/IeX4ymbg1wVtvvYXvfOc7ODs7w8XFBV68eIHFYoGDgwO3fpAyhOTzxumALHVdYzqd4vXXX8dyucTZ2ZkzRtKOPSUgVMqQOpRI3rt377rdhO7evYtf//Vfx/379/Hmm2/ihz/8Ib7//e+7NPSR603SrtZjxDNko6F7qeeG1vEpvT5GOYBFKeV6Ye+CQnq4FoeoJO5todsq15e0G/oitHfuu5vSfULjdo79pgQn4jK9ChSSs2T8i/G4yXo4OjrCd7/7Xbx8+RKPHj26MTk4FW1TnKIUGDckfcqAHzMibkPbAjIhyjEy5siTSlMib8jASfe5dZx6wYYMXLHnqcFCUzZ3reyN3Y5aejmwyz4Ue2d4/nwbIMkzt2+F3vncfq3x4DLl9oXcvGS++5wUZP2Xvg85i9+c/jf2weQ59ecZtgrTvookwRmi+XyO+Xzu+t3BwQEODg7cGbLaV1xaH+WL77HkJSqd+0LjBX2kwseZfVLOfDRUP5HvsTRwvMr9Okf2kMFnTL1JGhpD/BxAUjiP0tZ39+/fx8HBgfPk1uZEnm4boy4fu0Nlur5uYMwad++2xsXptN2iuKoaABXaZMQH7trytOBN4D+ncrceru0ZsjKd2YR1XrNteroHlha43vzzskmwjrwFg56qXaC7UhoN0A0SB9iURK4M4lnUS5XHMV5AHrjK+eeUg+LIriXCvf5n2vCe/iaAUvfbdFsKa7I6MFQCtQqvpPzaveXR+7y4DG1S6+q7F5fKTHz5VdY3y5u3nQN9+RbNDMh2IC7P0/h5Uv92dcW2O7bWr7/1fI3TD09x+PAQx2/45x3FSG0zG3lu/TAOAPJ7DxiU4Cw91+4pvQB/Yzx7slpF7lD5rZ+/V05ethQvkZbLIAFZj6cVOrJV/qlsGflba9GsGqyv17Dr8FrNJYvoerH1be5aXFv/ybghfSdFOXN3DuXqAike29oWpDxa2hhfyaOua8xmM0/vp+12c9bhXA8LyUa6Bu3I8+DBAywWCxhjcH5+7n7zNZo85iGV/y5oOp1iMplgvV5jOp3i4OAA9+7dw1e/+lW8ePECT548wXw+x2KxcB+xcl1wXwCftDfl6sCaHSfHdqO9j7mU069L1nsl68ZU3pKn1rf3QSE5iGQdyd+v8trv80BD7WK5cXdFsXd/1/nsKu/S9Np4ooWVtFfOeJyrm2j5ht75mM6UMyeMQdvMDWONZTH9QcbT9Fk6MkrGlY4QV1dXeP78ec++fXFxgfl8HpRNHkd4U+P3qGDsUJIGPSLqDKHnuY0cy/emKXeRVbL4iVHuAm0skopKzmIo5G0UW3BoFNqvXMo1NsXaLtSPY191lvRTDgBZa70tTmnhR8/Ozs6C508SmJSiEsN9Lg1d6GvK85BFNecfM8yXEvdKHfIVb+5YkUPyK9vYOaTaAjenjXjdrdfrG1NyS2gsBYS2/eLtvF6v8dFHH2G9XuOdd97BZDLBu+++m6wX/tU398KPjadD3qFYWE4e0gByfX2dJcOuaOi7L5Vmfn4vPZfvhLYFs/be3BYqfY/3JUOq75HHeGiBxscczaBN7bRYLHB5eYmDgwO88cYbeOutt2CMwbNnzzyjHvGV+gfXU7gyHzLIhWTuxwGeP19gOm23KT46ass7nVawtnZesZOJxXptMJn4HrLtFRtvWLPxkKVz1wCAPGXhtivmXrJAC8p2ZZeGMeCTlcWfLhrYaQXMKh0AAwvbPDfG9OIROGaM8YHPDQjH08krB8O0PD1vV5i+nPIZbxeR3guTeSnlCQGwSQBYPHZ9xsCBOZ4Ouwl3Oopyz8+K9TxRrQ8gelgS5cfqiADHNurmt4XH2wGYyj2vU+4hS9ceoGpZvlRm5rlLZXN5sPaR98TTO/OV6sn4svB43j3j48Z2Fs/93njGGrQetMa29XT66BS/85/+Dr7+d76Ov/Gf/I302Gv5T6uGc7CUP/PGbMvagD+zfcOzB0xy4NTaIHDpeCgesb08tLBN2l7+LKz3TPIiGclDlZ9jS/eWebLSnJzyjCWv1wZOJuk1m5wflcfzZ3NcPbpCs/bXAZpuEWQr1j+hOPwq1zJ8XmuaJqpf5q6DYvFK9A7JU+NRqpukZCvRhzQDoWwPaf/guvzZ2Rk+/fRT9SPMVNm0djWmOweWt91k0pr5FosFJpMJvvOd78AYg8VigQ8++AA/+tGPMJ1OMZ1OvW37cmw1Q9ozVc/E8+joCAcHB64MJycnuHfvHu7fv4+//bf/Nv7G3/gbeP/99/H48WP84R/+IS4uLnB8fOwMtSU73I1BQ9aOsv1CazEyGpOtSPYrSUPWsNw2Ie1wqXEplh/1J76TTIhkufZlo9XqkK/dU3Kk1uG3bf33eaN99pV9yrGPMu2rb5bWTe7cIm2Z8r0N2dxTY6iUN7S25+94rk1pG4qNoaFyDOE3lt07RiFdlGi9XuPk5ERNJ2X7+OOP8cEHH6j5xPoQ6RicQruIjkVaXwmCsdpkWzLpDzWESpJeNTkK4rZ5llLuAiWWPudl1RS1EkoZCHN5xuTLMTgOoSHtnlL8Qjxi5QsNWjGKDd505XVXoujG5JXPpPyk4BOVKJ8l8mlG8SH5aJRqs5I8ZHvk8shpL814k6MMaBQbB1IGHK1cqfaQfSbEN7ffht7VXY17JTTmmMUXoUSfffYZjDF4/Pgxnj175i166Tp0HOKUo4Dxa2n6XdBYc3dIiS81WmjvqhzLct6NFIXG/pDOs00+Gr8Y8bja8QTybNXcfpebZyxtjFdsnKqqCuv1GldXV86zYjKZYDabucXcZDJRy54jL3+3cozZ/X4KrNcNLi5WaBqLw8MaTdMCrB1VqGug9Vi1ACoQa7pytrI6jAOUCHzt4lC4Xxbr0q2sxQJAZYCaoYc98DJCOR6yHEyT115S0+Xt6lOCcZqM2u+cMqjim/izGN/AayPBRR5Peoy6/rWJJ+97vM0GgOTesAyoDaUTAoKDvA6EBfwzbRV5Y/zc71CZDTyQufeM56Xx5OU3Ji6r6ZfP+pHdv+NFbc3ytY3F8nqJ1fUKPRA1Rkq03jhilXDKQz4T99b243jAq7znV3Q8XBwOxILlaeGXxfavHv+YjKF8NrJ65Rb3XJ6gZ6yQ1UIArxZqvCAJedEAzUo/lzxm7witHaNZF8z3JUZDTSca0+gY0rmG8Odzb64tZsyyEA/+0d75+TmePn3qvD+tte7DLimrlD/VL0L503qDtiyO6TVaX+PhMl4u5cZfrVYeUH16eornz5/jk08+cTyurq6wWCx6AHKujWpIGXi9pHjl2tNidav9S/6xdXvM5hKTNbbuKLH1yfVNbLvlm6BQ/ZDcJeuvnPH4SxpOY+ENJbaGofNnaVoZf6g9fej6fteUspXsSpbUvDU0X5m+xBaUOy+UUI7tSD6PheXadEIyjEUh23hovpJAalVVwQ+ByB6UmrND4/rQPqTxUsHYkIIRUtx2+RJp6HRICYrJosm+zSD7eaRYvebEy9naZgilOnwoXDsfM7agkbw0JSz1DmwzeIWU8tjiXPKS9c5d/OkLWeLPF2O0UCT+q9Uq+O4Bw8963QXFFhopOVN9YEyKfamb6ykbUixS6fkCI7ftYqBfiZIYAidyn7+KRG0tJ/q/+Iu/wEcffeRAoIuLC1dm6UkbM7iVtOOXtD3R+Mh3GqB2GWPxdpN9Pzdf8kqlvk11ctNndZVQXdeYTCbOM/b58+e4c+cO6rp258Xy+iBgVtNtcsBgbR6nuuPvsDQirlYWT55c4/CwRl0bHB3VMGaG9dpiOrVoGou6NpsxhrYW5h6yQFV1Z8fSObLGYHN2bOsxCxDIGz5D1i8PYC1Q1QamMg6A4iCXqwMw7+IIEik9UzmwyoEuCXpx0FXl2d30n5v+856Mxv89BGzN1oNlNOuHu617OWjIAclNmh4gC+bRauCBr1r+HJil/DzQU/Clsku5OADq8rbWy4uDnO5sXC7PBsDywFLWVt6WwhZ9QNWILZp5Pobxtn4c3sconncv4lC4y4OlczIYwFQGZmLc2bGubnpN75dDPPRv+di9qS8vngjrgbPitwRhe8Arv2fXQR6xFkF+wauUSfBJ3rPzZdVr06Vz8cW5sL3zYpXyJdtEjPc0ToS8ElNra+rHKaCA5yf7Pg8bYgSW8oyl05CesS2NrWMN0fOoHJPJBI8fP8ZHH33krcOn02nPQznETzNGUjr+L9f1L168wOHhIY6Ojnr2h5IjRVIGzG3IWouLiwtcX1/j6uoKQLvd4MnJCc7Pz128y8tLXF9fY7FYOHthCkTbla4tDbsxmxAfY3hc7X2UhmTN6J8qT+h5qA21NU2Mh/ahZg7F2uKmbK587Mr1Ek+tCbYFfr6kzz9pY0TIDnwbaBfyjM2Pr7Vz1us59vWUvWfXbRSyAYfCS3mPJX/pWFcSX7N9kg2H7KpEpE/RMVRDKKVXj0FF2xTvovGJNKAipITvckKL8dbKmStLqq72NUkPVUT5YBUCVHIUwpyBS1MKh9YPTy+B2ZQbekiJjsnLFzSAv4WhRhr4UkoxJZpvf0RgAid+z9OS1xCRBJVC+aeeh/rOGOliBvAU/1AeOW2SC8aMYeQokW1ovcoFIQ/T3l/aGivEk9ogVi/yeSyupNukoHLSxnxr2y2hHz9+jPV67QwLVVVhuVyq/ZWPJ0O913dJ285dOelj82foneJ1qfWt1JwlecnttjR5xgQmd6UTDO03NJeMbczaZT9Ote+LFy/c9fr6OjlWyfShPLSFNE/HF3Yhvajtcxbn52us10Bdr3BwUIOiWlsBsGin8GYDqpJnjdn8A9YCxnTXqgJaj1rjhfP/Vgb/N10t+AO6RBa6gWc8nAO3LDBNhv80argXZvz7HNli8Vxc+TiRJkkMzOT3GvApwdWgVyjnBZ9fD6BV8vOAXnQ8eFwPdAU6MJLi8XwlmGt8fg7g5OO1LJe4egAq5y3z4fXC8nFlYNsSS55eGi4PD6N2MdarFz5mJD2Frby1/XCrP/PGE4s+CEvh/LcAW11cFo/fq884L5KH/rnMFl5eLs/YP/x4PF8ZHryHnq+7wpcjxEuO2V47xppUqbdQPI24Th7SNUI6EgFzGljUk0/JT+pQWnhozgvpb5q+GwrTKGRPSIEiIZ4po60moxY/VQ8Uh+wD3GNW6h9cRyj9ANNa/ziF1WqFjz/+2O0EcnJy4mQg79xU/ctyazaMMXS65XLptk0muc7OzvDo0SPHf7FYuA/HaTeTUJ/YhnJ4lAKYpflKYDAFyqbsGVwP5c95uFwraX1e9tmqqrwP/+kD1qurK9cXQ7rzTa1peb51XaOua+9DU/6OxtLy90fW1a7Wc59n2lV/yJ1fbppCc+tYlOqbsfxz58mc/Ev4aeNWTrqQ7Sw2FqXySI2xMi8eNmafC437JXNQT48PxLsJSulYnOq6VrceDqWJ1ZFGsu+N1ZZbnRk79GXU0mlfApZW0hgUqthddch95pWibTpVzPgYC8vhWzrwakQLn5Q82oTDBynqp6GyaCBvaMIZu39rbbBYLFwYbftDNJvN3LOqqtx5skDfS09OTLltMmYZt+WTk17G2dWESfdDFfUh7wQZY1JyUViOTLQFhPQ6l30nxSsnv20Vw32SXJARGWOwXC7xs5/9zIXxbTRkWmNM7wB7zWCTorGVP7lQvwnatkypd4jXMz+Dij5qobE0ZVjgffu29VNJsfmP5pLpdHoTojkaYx4wpv2I5JNPPsFnn33mGf3kGbA5eeca1rU+oRnA6bpeG7x4scZ8blFVFuv1xKVpGvJONrC2Yh6yQF1Xnmds+4wWsm0+LSjL/7l3LMnSvzYVYOrN+BQCHAnwAruiBcR6oKkSl+K5MZSBYio/BoZJwDWLFFmj8ei2YJEYBa617mNEvzJtPBX05CCtgXpOrI8l+h61vfLZLj8XnwOc6N4PJweLT2C0fMa3XVbBSM6DhfW2J8Zm7LZQtw/ugbCbtF4dGAFEG/TrmMJJNupnMg38OJ7HMpWBzpIluUOoXWx4k+n4T2v78bTnVvxmAKT0jLU2cm/Z2GXR4+3CZR4snKdrL9aXA2FPVu235K09D/Gz1vqesY2Sls6PbRLtJNpMBXgVovdJnkHPdULN+47rMppepK3NpU4pP4jX9NcSHV0rV4zkWju1NhpDl9pGj81dg2mgFukZvIwSkOUyaTpkqk0pnOwRdV1jsVjgJz/5iYtPO4VYa3vn1moU+yh7bOIfgwOt/eL6+hrPnz/vxZ3NZphMJt4HgyndPlePzG1jTrw9ZTwOJoTk0Gw5co2d2w6d3tfZr2I7BYZsbqF7eZ1MJm6XGWMMjo6OUNc15vN5Vh/bN8m6nEwmmE6n3vnOALztsGV6Ijluf0nD6bbU4di2ky9peyrpG54+uiHplEQ8+fg6ZFfFUPi++3LItsBpiD2/hG7qvanrGsfHx71wiWkAvn4kKVQfu2rLYjA2NUnHFgGxsBBpcWMNXFKBoc6SUuJy5NCeh2TI8abJBUdyeciFGD0fSiWKYSrNkM7O+U6nU7f1j1SUcgYdWR9cWUsZ1umrDHq2Xq+9l11ucxnyjJX1NObALxf2mtxXV1feZKTtqx6qB9m3QuWi50O8yUrGldJ+Ld+1nPc4xW9oWkkp40iMf9M0DjhN5aEtFHOMKTKeXOhI4HYoacYo+WwfVGp8onfu4ODAGUC08YYvoGO8cupRM96E4mjl0tKVfDwVMxLyNDlzU8xIl9vu2ljOw+k9kdu6L5dLFTTX+Oe2TcjwsU9KjaXr9RpVVeHNN9/Eer3G6ekpjOl7xA+hUBvmGEi3HZeNMc7gEhuTQoa1GF9Af0fonhaDzsgfeN8o39Wqxmp1hKurNVarBY6PJzg4aM+SnUxagJXA2KqCu6+q0HbF3CDog7DtvzT8svfORUIfjCokz6vUwAGwGuiqpkEkf+PHJx5emyhpQ1sO55DaB7kcwYSbq+iCvbHOdHHcGLMJC3paUppNHbh3axPfXalPhPjAzyPkgcsB11Bar31Z2p7M4t4DkfljYzxvVi8/UWeUtyvrxlvWi7/5N4aBwJu0Li8ex3RbEnNZvLqFbbf2BlpQr7SPiWJ745Wli/XjWT/MpeHh7Lc3F1sRn8J4XAuftw3z7gGfAXmkLBq46sZM8TsJ0m6AVJe2EVdrvTjW9gFYPmZH5z/lUXPdYPlsifV5tybU5gi+Ruu1tcwmojdJcCB0jfGWcUO6n+SXu2YNlTG2Ho6t9UI2I2t1z9ISHWMo5dixZD1ba93ZrhR3uVy6rXg1XrL8xhhPjwX6W15r/Ti1ruJn245hP9JIey9o/aR9BFxqR0j11Vw7UYhXbA2QWr/ybX9j/TkmU+w5T09rmhS/UBzJiwB+osPDQ8xmMxUASdG+1kXynaC+lbP9Mq9Tei9Ca8ycdc0Xkcasj13Ucc68dpNUKtNtLIOkXP0hRTlzrxY3NPaW7mbJ+2PJXBnrc1IHMqb/MZ3kkRM+Nu1St5Lpm6bBYrEIxqedP4by33WdFYGxMaMpUc6kLu9zJjoeN0fJKaGxKz134i7lGTOYS/4lssRoiIFzGyqRP0T8/DfOZ7lcbiVXTn3XdY2joyMXPp/Pe4dJ87P35JeCckHBF+ZUjlIKLQ640l9VlXd2TeicvBD/oe2eWzYue8mkVvrejKGkxHjJsXNIfiVp5UIgZOCQsvHFdq48oWfcyEB9Sm5XFJO7hHLf032RZswwxrizoUKGEPne82eSZ868XCJn6FnqPYoZcPhVbpUXKn+urDlxY4YEyYNkog9nSGbAN1xohq/cOorF27cSGCJ6V8ko8fDhQywWC7e13S6McNu+u7kyURtKBT1k6A3NyyEZQ8ZRnoeWv8aj/aALWK8P0DRXWCyWoKnZGKDdrhio6zafdrviBk1jNufKtiCttRQf4NsU+/99YJbyaX8ADcnLi5XRRCoYuQHBPGAul4eJx3dxc2XT4pkwn14/keldsoL3l6JaGawDpBJwdP3SwNt6OAii8ny1PMVZsaF4rcisX1h4vz0QlT33wEo605Xl1fNcJbaRcjpZeD6m5dt717h8ULxWDUsbAKi9uqI+uek31jLAl+oPcOG51ANZuwdhABZd+V25RXxr+78lKOo9s1YFY724CSCWyyVl4vlImTz5AC9eCIiV4fTXy0tcHQ8JwIozYtU6F/VNdWKtRTNvwdhm2feCpPvYnNHjn0GheaqEX2pNE5vvQrLwsKFGu5gtKMRrbP14G5L6oGYM5mtx8u7gwJamd3Pi9gZ+T2m07RtDOqi1fUB7V/ppyL4ItPaJuq7dx3SkR5XKEtPPc9ZTqfy2qRte57Jd+JpExtUoVhZt7VG6/qI1Av8nHoeHhzg4OAjuTBeS+abWPaF3cUg6TrsaQ24j3VTbcYqNB9ranT+L0S4Bpn1R6by5Kxpi68ix38o4od1EcvPU8h6ig431Xmj6YGhsz81zbBlTNHY+1oYxnm3e6Vwe25IHxqYE0oQJfWm4jxdd27YjlG/I5Ty17UQsfNtyaobom6ahyuW+J1/enmTUTMnDtx/UFhTawnhbOjg48IBhuXDlRF+9ciJQQIZJ2bUr4J8ZK9MN/VpRUgqAGIs3tZ+cWG/bO5RDpQvHnLg5iwAtX60OtT4am6y1xWtOu2hf2efQLoCgGIXaS5sDYu1AfGJfcPH8UuVLLWjHJCmTMf0vuLV+xA2M0minGRmIUnN6icyp/kIGLj4P0IczZPSZz+dRpVfev6pjEyeqi7qu8dprr+Hq6sqrH/Iq3ff8P1a9hsBX/lzmmbtoiY3xsl9L/UOWbz6f49NPPwWwhrUrnJxMMZtN8PbbB85Ltq4NplOLujZomnbb4vW6BWQ7z9gWtG3bjwOv/MrDu7JeVcBHkwpzY3yQMlBnvS2JvUjwwTWwduDPjPLP0mi/XboYcfll3IyurK0FgvyHEAMSHT/rA69qvAgfD7iFzkN6yQKsr1I8w3ghzMuJoM2RCiCc2k6595zqRALGIu8eCGv8qzHKlsKUDiId+3dxjO3x8upMyKKCwiHyqlip783vHvjKAVrr33OgMOgdK+Laho19VvAI8ZNxWJh71vTzdVeRp/tv/DAOkGpX27Ath7lnbODq/hvxu2G/I8Cr33y+7KvlCpeXl8GxQc4J/Mplo/7F7yksdhSJnM+kfsRBnpCxT85TkoeUWStbCcmxSOaf0hFvux6WAwRMJhNMJhNcXl6q641QOmv97YdJbyupE6mHh3RdWZZtKbauoh3HQv1dS1fSB0rW5715siBNjoxUv/wMU+1d1HjLZ/zjeyLOVyNuNwuVJaeuaPtfOU7F+k6sLGMSrWFy7J/7kulVp1Ib1z5luGm5bgPt0m47BuW2kYyXu9PoTVKpXWss++9tpdswVtw0JcHY1CSspUsZLEMdMTeMP+MKYuw8C02msV+GUoop0KWGvhCvGB8tTQ7PUJqctKWKDDfky3AKyxl8pYwxfiXE+zQZ7nk/5Fu2WNt+ucH7LK8PAmK3WdiE+jgpvzw/fs8X9kPyoTBtoTYmhRb9Ofntc1LS+rlWN7I8Ml0OH/5MGknGKkNOeE6cELhR0teHvqfbgie5JNst9U6E2p8bmmL9fKx+nTPuSKNYLt/U2BAalyWPnHxiMmsGpdT7RCBtqp6Hjn23VRGlNqYtmw8ODryPhVJttm3eu6acuSMVZ9sFkGbk1tKu12unO7R6hMFkYnH//sQBrU3TAqitp2yDqjKo6+5jCWvNxmsW4J6x/bNj/X+gBWgXAJ4ZAwsg9QlXCogs8XBVEkdB3igAvAuSfFP50PNUF5HgooEKyEpgU/WClbx6WenesiHvWpWHMfCAQZ5nJP8cT12vTMbfXtmTVW45zPlxvjw+r0cJBhufJ8UDoMsMv84duM1ka1YNlpdL1NMa1TR+LtUmo36QHCOsEk5tIZ+Je2v7cTyAVN6H8mL8e7+5uJalCQCxPI6MR/Xu8bUsLufBReRrHCVP/ozz6vErVLec7GuLZt1gtVx5OyTl6PMeP9sHYIfoHqm1Euef0kM1fbDU4MjTabJoz1K0jW5caqsYQjH9QeYfOyJD6u4h/Za3U8oex/lxW8uu1/ghkmXS6mgbftrzXCA3VJc566VUHqE1S2gtnVrHldq6cvqLbAvNziR3hZOy3hQ4FOtDOWuCUNx9rulu6xpyV2Po0HYZAmqV9MPc8SIn322phN9t7T+5Mm1j24uNl6F5YQhtW78x3SuWx77G0W1oV33vtvZrjYrPjOXEDZUpSu3nXKIgyIPVc+TcNu42jRoqW0gJHjJh3CSVGOVjCmUO/5x+om3/G+KrKbqhvOU9L89sNsN3vvMdd+4qxTs/P8f5+XlSFs6TrgTsyvyHKAuaoffq6sq9k7QVEvGm8xG3JZlnauEXUpxSCyctzW2j3L6+SyNADr/UYi60KA6NZcSHQJ2hZ8aG+sMYNAY/OU69CkqQRqVznTQOUlguz5Chj6ctkSVmFEwZICk+ALf9GT9neZvzjrX3KsXrpsYza63zFv7a176Go6MjrFarpEf3baJ9v391XffOpZft150B29dPtDT8vZLG+/W6wePHV5jNKnz1q4c4PKyxXteo6wrTaYW6Nqhr6zxj2ytt2cQ9ZGmc1q7d79WkgqkAbM6c3RbcVHU5Bmi5+hAgGI/nfhsWxqMJHr10UQEz4hpdrqCsYM9luIdt2S6t6MYa0Kh6iG6uwbmIwELr85KyyDyzwN6NTF5ZWLmd9y2l5WVVyqwBnV4ZFUDX8ad0tA0yFdF0WwjzOjLG+N6uBL6KbYjlv9yq2G1TvOFHZX/2F8/wB//kD/Dub76Lb/wPv4Fs0vqH9Z95HrFgY6AVz9i9F0fMkz3vVMHT4yt/S748LOYRS1fNG9fC82CleDyOtdY7C5buPT1V84rlcUP/oj5dPUGUldeZBezcwn5iAbYU5HoTp5hBjevZR0dHAIDr62s3X+esr3P0DwL/ZJ4hXtqaVKbT4sR0tVC6ITrRbVsX0tzH9eXeGGRbr1Y66oj0i7quvb4T6kdE/HnoXDmtT0idg/oYeRFKfeQ21HFMhtA8uK2eOKaeGbLvWGu9Y6To/WyaBtfX11G7EO9jWj+JtZ98v2NrKeqvTdNgPp9jOp1iMpng7OzMyTibzQDArSVSa8199i1rrffRo7Qza9tCcxlTtEubwG149/ZFr6pd5UtKU2rup7BYH+Dr8BLiOg9QZuvZt+3vNs25t5lepfrZCowFAgaVQLyYUh/yatXC6EwCTqnFQiwOj5tSDELG5pjMMb6lRu9teZRQalHI42j1rClroTzkIjImQ4xfzjMt/1w+PL4crI+Pj3tn1S4Wi14dpT4kKFnAlgInnKRyuVqtPBlzwVDev1OL6THapbTMYy+WtN+h/HIBJrkoT5HMI5YutPjiv3Nk1tqWG11ShqBt+m1sLBqLSCa5eKX8Y/UYkjFEoXkkt78MzbeUxqh3aWjKySOWV84cnMMrZZihMVDuIMDl0AwedD9UKU8p9tuM+SVE/Z8+CprP5x4Yu+9FcU5+28gU61ehMVbTg2ILpZBuJI9dkDKFDKaLRYP12uLqag1r2/NiJxPAGMA64MlsflewFrC2KfCQpW2LCRwRZRqhG3KgVHq1brXV7w1RkVew/wAdBhvwQo2AjzKP4vQaCDqEQnxK+RPQGUpDZaF3zkAFgHtlDjWPCRhSSI5gsghfkp89W14uMT+bY/5yrjO08tYGnzkAVDxzZXAX2/1mz7Tf1to+XyWtBHg5EOvJZ20/TACx6r+Sj7VWlUtNK2Vi/14ZRT2EgGU1rqh3XkavvGuL1eUK6/k6e82pzUekj/B7mU6StmbhW9VK3T1Xt9hWt0nFKdGvQ7pWCNjKlWUXFFtHyLJba93H5fxDr5AeIetBKyvXY0L9J7R243a8femhpZTqJ4Cuh+Xq6yX9XvIsWQdJeyN9zEwkdcactaNWbt6WJe9YaP1Du+cQv8VikfSoluvumyR650LbN2/T52/L+5IzVsbSDCnHrts1x0YyxGZT0i9z4sTe05R8JXU49jy3i747pL7kuKjxCdlNUvlouNJkMvHs4rQ9fonMksZ8F7TxvUSWLwK9CnWQdWbsTU2Oxhh3xgCnw8NDTKdTL2y5XOLly5dFHrOUx1Datl5KjLVjKL4phW3MRZgcILbZx/2mXiRtgOeDNf9yjgZt2S8PDg7cF8wAcHl5ifm8NcJUVeXFz+kPqbqIPZcfPPB2Wa/XHnBMX+Dysub21aEgRGyhPLQP3MTYlduOYy1mh/LhikyukqqF03uglZmH0SJNAv0aEJGqw5TiXTKuxvjxRar8CIi+oM1ZxIbCUnLmLDB2SVS23HlV60sUrslL9cnPQgrx0GTbRiZ6pslIRgUyhPEPVTS+oY/DtHbmz0rGin0TvdOffvqp225/tVq5smrenfsw0O2zLqThMjUmhYyf/JpKq/WdWL2u1xYff3yN2azCW28dYDarcHQ0wWRiMJ1WmEyqDUhrUVVAXUvP2M47ls6VbfsmvN8rYxxgVUQWPlAlwS1xbzb5eM80QEym2wFQrJEHbggP2JhHbBawLABZAL2tc7W40TAun42DuME8JPAJBkKm0mOTljxHY0lE+T0PWvZc3YaY5FT6hOfVav2rVieuDxp4ebu53nRxuIzGME9bdFspO3mrVibTbORqIpVhodev5T9Ffdp+uBt3lPugd6xVeFA+oWfiXuPp0o7lGUu/FU9YGVd6uKres/x/raShMVq2i9aMVF5rsVqu8OLFC6ABptNpb0wPzRdaHHoH1+sW2OWeklK30KiqKsxmM7f+o/M3ab4n42OpLaWU9q3X3JQdS6OYEVn2hfV67XStmC6iAVrcZpHTnhRHc46gY5hiurS1dpQdtUqJyl6im4xtuNbe5TGI6rWua89eVNe11y9KwGQOmA55L3Jsxcvl0o0tVVU5T1gg7GWak8euKWTPuC0G/X2ssbah2y7fl/T5opTdMZcmk0nPmaqua7z99ttu3L24uMDTp097/C8vL72xOHZkJlHIBkjvT0g/5LRrHS1FX77r49HWnrEhhWYsMEXjxc8eMKbd+mK5XOLi4qLXObUtjbUvIIhSCloMwJBxc8pc8uVLSDlIAQk58twWwDNEpUo2NxRrabU+USqTNLCvVissl0t3T2G8nWjxS/f8q5tUHikqXQzIxTs/DzD0JWMubx5v6ISh5RVbxIbijkUl9Vr6PmlgTSpf3nZ8Ai/NT+YVGldC/St3zATy+0LJOKhRDDiLyZfiKcdT3h+1LcVDAI3MO2aUy5Erl3L4h+YMrT9ID4sckn19yDw5dN4s0Uv4RwOhuTSWr0ybqu+YnJJ2qQRLEHC1WnkGW4qjyXCbFPMc8FSTV6ajfk4fJIXeY/6fsxAba35q26j1Xr28XGG1asHW6ZS2mWyBV6D1nPU9Y9vn1tJv+rikvachzZgNOCLArk6IuIwqEMl4DfYmvS2UEK9I/higGknjgYoFPPgWvtlALQNZHY8WJQ1uT5zjyVsE8CLxHiXqwIG8m37onccbkTEoTygdk1ECzMkxQHncA2A38f7/7P1ZkyQ5chiOe+RdVx/TPT2zszN7av/kiiuZ7E/JTJSZ9KhHfg19Mr3qQS96kNFkpoOiRPHQLpe2y9VyOWdPT093V9eRZ0Tg95DpSIeHO+CIiKyq7hkvK4sMHA4H4AD8CAA83ON2zXfN0Ro4G10TR5CXhAV0Udocw494mSNW/If972BnLMnDd+bSXbM8rfQf4HLNMgK5htXLEuZqB+VVCeW8tI9HJjdiGP/NZUcqi6bkeC5vxvQPK7RZ0yw2iZh9w5LOAnfRqIj0xGwVsTAtLiY78z7kOiYNpzyk8c9ttCmXXzkdKTlcSiuNSSlvCtrwKOUDdGxq9PQpV3KI6bE8Df7Gj1nxSGWJl/E/ZUe4SejazxI+zlddbOI55RwCJNuTRp+Fb9qWT+FQvNLVFnXbYLEnWngmZ73NsRPH1g6pjD7a3DJPot4/Go28nR6fsfbKsU/G+NiybufaQvseIxZ8tyFb3UV5LgUHccZKxwgfCgaDAbz77ruqM5Z+sYBQFEWw6w8hNQlZ0t4k3CXhxQrW46Xb4EVcaDhG4JMnlqsZlC3A6a7rGi4vL/2uV3r3BM2zXq99GvzSURorXNiOKdRtgE/kzjlYLpdBfbRFvCuPdc1/F8aeVodch1QfwjPm4TtOEaz4UfjQ0qYUfqkcWj9UziSBq08FQhN0NMXcqizw9qFKMs+La2BZluZ1pc246GMsScKaZf3Gnf2oZMfqGitfMt6kDJF9QOpONOp4xPSYDg0ilntFpPw8PnctP6QjlhvZUH7iuzPedIjNlTimURnj99BIeansSeWLtoA4KJ/GYLOp4fnzFYzHBZydjeDoaAQnJyOYTIZ+l+x2h+yA3CFL//f9z++RHQy2d8buGwqJpATD3gEWYZGoY7JQfithN+GkRSchAOv3WNFBso7ykrILNLXTNOU8DJyPjuWTnLO0b2lwBz4X73rdbjEOcQrOZn/freAAbuyIdSFuxB84gQsnl1Ps6CGO20aXEodsUezviPXOwgKCMmA3lKI7Yyl4f6BrhAGwtqJpiYMw6YAVwsR3J+DmedlvH0by8rjGk++MrV3olK2bvxv3yToX3geL/3w3LE0n7ZCV2hfbjXQEbUvnHNSbGub/MId6VcN4NG62N8knOVARD64D1HlB7+6kOkDMXhHbJUmdIimZONfAG8NlhZSu3gX/XZFpJL3cOef1iVTfaPI0xsXyIUjyRjB2duHD4VDk2ZsEy9pjNVoDhLZLSV9NlXMoPqLXhEynU9/20uk0mp4BELZXzJ5D68L1II5PA3rUb1GEH/sjnV3tWG8K9D0XWssB2OttyDPWfH3AIeaEu2D/awup9esu1K0tHVY7mrU8Om/H7JJtQfIJ0PmUHgnfpk7WsEP3+SHXpbsKfdb3ptqvlTOWG+s4xASBGPB8uAOW5zs+PvZHv2Ia3IGYY9SXBmMfkBJu+i4vh45YuV3oy2nrtgqVpMTm5uXvbSZIi3BP0zjnYLPZ+B1GVHHebDaeBvzqkdJhcRDxdokJ2zSMC8fapGNxFGgKoBSnOcM0yB0nXcZVzKHYx4SszZdthOQYn2r8rTm7LHxmjdfGGn3PaUupnjF+s0BqfKfCaftSIwUdX/Sokr4Xc8vcrhlJtPlA+0ejhEYHnbPqulY/LomBlSe6GOR4X9D6Ie4Y0L5Go81oNALnnP/AhstGhxDiDikYanMzH4M3aUywgmWdSuWXjJ18LGPa1DqnjTtpbuZzR1tl2DmAqgJYLmuo6xLK0sHRUQ2z2RDqertD1jncIbvlf+qULQpH4nDdc1DXBdSVgSbHfnPnaVHsHVwM7tzu1x39VrruHP0AgSPytnCYdrsq6axhKTqDe12dnidKq5S3oK8G5zU6a8G25ngQkgV50UHInKQ8zOdx7Dd1kNK87D1wxPI0+NuFv4OwhCOW/wfOV2im5b+luGBep+F1M17ELdRfc4r7d4KjLmuoyiq5hrbVOelVORRvTI6lul8sXwonl6FoOK5pVh3FYiPRZKocnf2uGiaRLukIV/7MkcVi9Y7pBlI8ptH6+yYhx76l8S3GcZ0H02gycN86J00j6dI0jJ7Qwk/vsYDVFpdj90nZmqS8kp7YVYZvA3d1PsgFbDvaroPBAGazmddXq6qC1Wplmg+t/JpKq60DqTXFSoMFtPmyK37rvKfZ4SjcxPyZGu996Pht2yNmL9HmxRjEPkqJ0YKAH5LEZC+an44nnJdT/X7Ta+ZN2HA4vOnz603Rn+2Mlc7CbiMEWNPzOziLooD79+/DvXv3snBZy9PAIny2NRK/6WCZKPuqvzYx5gqVbUHbfZiC5XIJi8Ui2C1bVRUsl0tRGeUG4JQSpikg0k4++t62bawLZxv8N9WXqfJi4/vQi6hlLon1AQKOF+6A4n3Pv7qmoO2STNGYEmLaQi5OSZmmoDlF6G90MlLlg7cjx40KUJtjutsIxjnKkwRIr+R8kr7AlgwYMQXPYoykCmWXuSMwrgpl0TpiHBo5LOXi/VnT6RSqqoLFYuHpTikWOfXoGzSDJ1ccaPul+u0uQF/GQPw4in8VSz8KpG3Spi2sBk8ebzUElqWD6+sSrq8LANjAgwdjODsbwWxWw2g0gKoawmBQwHhc7HbIDmAwAOKQ3d8dS59lpcxjDgB3DnqHpKErfNoCwuctAKU9qIcGGU7aPkBdu5gDUHMm0nCrczRKj+bcDBPt43KcnnqhOi27HcJFUXhe5HnVOQKdqi5MG+yGZeXRp8i/O3xFER5LXLjC3ykbOD8pxJol8AG6RlqPz8lhqd2xJicsvlO8Ci4fTn5LTtWYg9anq8Mw6bd/53fHYv7a7Z8O/O5X+rvxT+bfhiN6/yOsH+zLLKsS6qqO3iPG5UoEuubQ+zxjp3pQ2VuTsaRTHKxA1yFphx7VNaR6WQz3sXIRt+bkeRNAkkeprkHDJR2+jQ6k6Sqx9s5pc4vD4RCg6W9t8vN3DZfVBtpGJk3ZFflYs9a3jf0iJpumdFSuL9N3Sd/gv631iuk0twlWGvqmlc4Zw+EQ3n33XX8S5HK5hFevXkFZlsEVaocAyh9t9cebtLndFFjsZjcBfMyl5p02uNv2dQp3qo0km1Qu0NPeuByTkmGqqmrYDdrCbc9jtwl3YR63QldaA2esxmBWgcQCFiEOAIKj4TgMh8PGRct1XcNkMmns9lsul1mDUROyui72XSbY3H45FB2xcmN42wiLOXljCoLmKMmhS8OFky3+Pj8/D46/pry7WCxguVwmHTNcEaDvPK/FyZMSmCUDvBaXA30IXxQs9+bwclPQpU5t8gPIwpZkvKDhtDxNOab0SHRJ8xj9jUed8S/m8d9ybK1UL25Y4HRI9GvzAI1PCUOxvFqclp+mo4owpuNtMxqNGoqnJEBqBjOpbI1WKR3+1pRmTj83+HE8lA+6zgk5vM/DJHo5Hg4Sn3Gj13g89kIzFcBjBgPEE7vzm44dPo9rxjjaRrSefQmksTU2poxZ5aI+6OEQmzP6Aizj9PQUAMB/PY73dOG8eJcUg/h8u3WiAgAsFhXUtYPr6wqGwwLu3x/vnLKDnVPWseOK98cUDwYFwKiA5b0plEdDgKJoOKcAQHdgHtiukHKiYpgYdwOK3p3cLZsJfThvu+K3Op2lfAAgpy32aTRcwV24iAuPGqZpCtfMtwvzDljiNPbfLNTyuBHp4UHc8YfBdF7ANK75Lu6idS76HuSjaZyS1+2fNE3jCGL6rOVw9Td3ppJ4Kdz/1008NF5tU9ofEts4AFc5uH56DeV1CVDJMqCke/EjYiU5RKNHkpmkciQ8VvmJHnPMZZWY/kHrIrUBlS/bzMupNtLapK2e2ifgms2vzsI6cacWh5jcrPGPxiscJ6ap6xpmsxmcnJzAeDyG0WgEL168gNVq1eokHA1SOoUkk6b4LseGJIVTO2Ss/BgN1vJ5f0njk9IijSmNNik+RY+Eg9JI256PW4nHYnp/W4jpNLcJVhr6pDU23rX0UnzsaGoLDbTcPnXXrrzyJkCsvSxygAW/JV9fOn4u9FFHDZcEiL+qqoadeTAYwNOnT/3aXJZlcJ0fxcHXwZuwjUhpNBmBht/UNaJd4S7M41boSqtpZ2zXCTmFW1o48C5NyeE0Go1gOp0GYWVZeqMqpSFGe0qwtYS3hZzJ8KbAUl6KF6zOAmt5qbQWRdVCU0yBpXj4hEbTXVxcBHF0wsNdsRK0oU9S7CXlhKeJKZ9aXFchKEe4kKAPBSeGT5snrPxqaVOtPS2GEg14n+f2E6ald3/S/LGjzKw0SmktTpWYsSTVX7kGllSduMEoNleg8k7plAxxtBxL39E2S81VWKZEt8Wow41sPE2XNUCjkdZNmsvagmQ84XTiVQeYht+NGptPcQel1C+0DaVxrtVTM8QcSh7gxqRYm70pkNtmqEydnp5CXdfeGQsADQc9z9d2fYwpu23GnQarVQ2rFRp8CxiNCphOB+DcEIbDAurawXCIO2SLYJfsYFBAPRjC69MxuNkQRuiL5dVFx4REotY0h2YtjZ4D5HsbnK6doQC9r2+jzESXNBy8RdG8g7dgv5X3YKcwhkl38hb7/8ApGgFxbnGRNG6PW3r3af0jTCulEXGSNI0dovS3C383nME8Xnk2nK9u7zQVnbBM7qG7XRvx9M5ZyanL25b1BS0X6Vk8W8D6Yh2csoBtm6v3WORuSW6IyY1Yv9j6w5+o19IrdygOSb7UfmvxnFYuE6Zo1crBNG10zkPmwX+0F3D5S2vbmPyggSZjp3DUdQ3j8RjOzs7g9PQUptMpXFxcwHw+79XhkgsxfqJ8oelgKZw8r4XHculOydsWO40mg8Zk01zdWEsfm4u08EPrNN90sOgkMTuHdsyqNudwWwOGFUXR+MDIQosGMb6x2n3azNM5ENPlU0dz59Ji6ee2NrvUutsXWPG3nSuscw29QpCWOZ/PA1zaOo0bWnJ4Lwcs86yGX6rXm2rbeVuh1Z2xVHDU4qXOb3v0oAVwkEiClEajxMhvinDwNgykNgqONV/XctsAdbbSSRvfuVIO0DySiCsQbUH6iCHH0H7T4yBGCxc4bpr3Yw4T/G01MsTCY2DpD3SmWuZmLjRPp1NwzgU8ulgsYL1eNxR3nGclHuujLilHhFXROASfSMYmrqBLTmxJmZeMSF2BK0ASSDRxwwz/MlaikdY1x9gh4ZLuyk4pTZLCqNFgaWNJaaT4aFvhjsn1eq0eYU3Ljo0ZWlYMLHzfB2hlxI4yvyuQO+6HwyEMBgNYLpcwHo/hj/7oj2C9XsN//s//GQAAptOpv9u97XxnAeSRomje5R4DyfAIIPMyAEBdA5yfb2A8LuD0dASjUQHj8XDnpB3snLLb/62CWWxdD4VQV4kN0CFRkCdmE5rqxp2YDvwuRRoW0Atbh8u3DtZMuIVpQTt+Obrbtdh+UeCcE9NJu3ABBCfcPoGcF8Nd+BvLR6ddCqTdryRSjPfj3jXfG+n9q2uGuWbaBi4nhDsX4nBhuOXpf9dhXMwpS+OCNDWjQTiquHE0sSN13NUjqK/QIQ72eY+OjmBUjvzVNJpTDUHS+TT9kMuf+EEZyiWSXIRpLeCca+zURHpQz5CchtLalXLMSGXzesYch1JYTCdLye687W5K38R6jEYjLytSmZHrbRZ8CJr8nspL5VrkLbqTN3YyzKGBy+n0if0v8WisDVMyeoqONukl3ZDH3wY/WkBzdPB+oP1jsU8cQjf+pgE6iE5PT2EymQDAdiPTYrGAxWIBq9XKp+XXsmhOw1Q/8jF5qB14bR2MUtpD8hgfu33rzl3w4Ya3GOCHyBJIdtlD2QYoXr7RCYEeCVxVlUq3BhIf4BiidEgyQgyHVIdY2LfwzYRWzliEmJNCWpxzJz2Og97xphlc6SDNLTPXkJuj2BwC2iwiuUJ5F0jRJy1SFoO0xfhuwZMCTSHUymkaPvfHUdJJnTtDaHl8kdGE3LaQyn8TiymCpEBrea1pc2mIxeeUEaM9lS6lfFiUZvydMrpgGroL1jkX7PBDoMJ6ClKKv5anrSAcE/xiY9JCU6pcrqzzPJJBSmufmFKRM39a0ms0SXWmzlj6z9u2qipVKdOE1ZQQG6tHqr2kMWAVkLmxU+MjLAeNU6h08p3lEt6uYyPXqJoqQ3vX6D2U4mpRrrW51Co78DTYL/R+pR/+8IewWCxgOBxCVVUwGm2N6aljivtoF6RF4iNNrk7RIc2Ty2UNmw3AaLTdJVtV299bnh5AXRdQ17tdsXUBMGLj2ym/WZrbcmgG5TpQnWK4O/GNcLp+q7OHUMB+56mapLkztTEednENpyz9iCCXtKLwTjs8mhhppeXXVQ3lqoTBaADFIFGYw4drhOFv7mSN7Y6NpRPTS+/Ohb/xFX+TPGIYf7pmWsmJG+BivyUcDactv3tW2CnL25PWVeoX/3tXnqscTMYTGM6G/iPdmIPQMm9rtg+6jqWMo5JOyx0gXMag7+iM5c5RSV7geCmuFGjprHoWrVssfcoGYV13cyCGi/Yj1dViebX+spSZUy/nnN8RjTygHeHbF1hwSv3G+ZnyZ0qfzpGxYzxDf6fGJadTK0eii47B3P7UgI+LtrroIXT0b8EORbE9ZRKdsZvNpnGNkpYvpgPje5v5sQ+bjEVfjPkirGkttFAcOfkPpUtbygbYn4hHaeE0bTabhlOTz6uWOuX6YWJhw+FQPNJ/PB77cjabjXoSZcymkbJZSbtnrdB1ntNkOQtI/J0rY30Lh4VOzlgKdGLWvizTJkHJmItfH1B49OgRPHnyBGazGazX6yBOG3htQDMY9w2WBeW24a4PTC4wIMQmWO1roD7bP9VuWJb09bFGT5fJmOdroyC3hb7xt6m3VfHILQcdMffu3YOiKPxX8AAA6/U6uF+AfkV8iDa34MS+l+7gLYqi8YVkVzr7qq+kIMf67VA8zZVeScnWxi2udSkDkFSmJUwzfvEyuCFOalN6FItk1EC86KQaDof+mgA6Biz9JdWDPjWI4UP6+JEygcGVlafRocVVVeWPsImtQdxYKd0BZjEA3ZZ8cNPrRZ9gMb5Rh/qDBw/g5OQEnjx5AldXV/6ed2p07JO2oij8nbT4TmmmLfsAAQAASURBVL/wbYsXQat7VQFcXlZQFABFUcJsNoSTkyGMRlsn7PbIYtg+AQAcgEQSdTqBA39PJjo5nYs7O63OUJ/OQegk4+8kzNPAnlJapL1wxd55FyMrFX9I4A4ga1oalsJhLeOGp4LGztNdP+C9rK3poU5Zdv+r5JhtjM9CSLf7HdwZ65xP99XPv4Lzvz+H7/3r78E7/793ouSJ49gp8Q4ajtlUeICD43X7dHRnKI/3v1k6DBfDhCffCeuffLcr/a3shBV3vZK0wXsVOnqD9uLtgnWBJu3zz+aweb2Bj977CEb3R8EVNdSBlVpHUVbUZAUKVKaRdEouX8R0S+3eWjyGD08Cobj41Q4ctyQLpepkhZizSFv/eXhZljCbzeDdd9+Fq6srePnyZaP9NehqI+EbCHJ39FCQ+pGH5wDKPKPRCNbrNbx8+RLm8zmMx2NYr9fqVWIAh9XDECzOD+5oSDmtOX9TsPJuzOAv8SuGpfBzfSwFOc4hrRxrWTRvF5osOL4FGVCfwDFZ1zU8e/bMrwnr9Rrm8zmUZSme1JcDXMe20BabKxBSG1K0sBjE1ohcPBpovgzMd9f05tlsBqenp0HYYDCA4+Nj31avXr2Cq6srH89PQkh9pNwH8LabTCZwdHTUoPv09NS3/+vXrwO6EWL2dgsMBgOYTCa31p/a8eFtwGJz+hZuDkyzseYEkgR6rWM1xtWEC4nZh8OhP06TOmPxji/pAuaUIS5FUwq4gdpaBi3Lkq4tSEKfdWGytIPGGzyNFt4GuGE9J0/bNJZ6tpnULA75mEJpwRlL06YPcuvZVSnLyW+lTVKWYm2szXV4PBh+lUXnH35sNS3H0u8pkHgyJSymyrI4z3IVM8scbFWUKc6YsnhTAkZsvtOU6b5BagssM7Ue8nZPhXNczu2Ps8MxQJ1LHGL8lTPHxeJy5xs0wPB8qXUL6y+tQ9o4t9Ao8TWuxSmHYJ/rrIbT2k53BZDelHyGBmcAgJOTE9hsNt7Zju1/CDkN1xF68ktbPDlQlrTuNYzHBVTV9mjiwcDBYFDAZAIwqJyoKGjOTc8PDg7rsOT46fuhy26Qohx93A7Z2wF3qB5erjASFTuamM/t0q5a73jdvXt+KPa4VxcrWLxcwPpqbZtXFGcgjW/sjnVBJhGflMaHuX246IgF8ttn2afzayXsHZYph2xQHxIn5Y3F0XC+VjfuhK2hWQep/himtCUAQLWsoLwqYfzBdkeS5tixQmxNp7xoWePwnefnODV6qTESYH81CtLQp+6Zs95KMjbNrzm/6PtgMICjo6PAtsTbNdUXGm2pdqHx3OB6CB091y6G7YAfWtIPLlNgtYHRcnJw5eoNMdxctpdw8nRS2RLvpfjECpq9lMZzGlN6gTRHxNLH6JLoiOnuHL9F9zw03HWdJgacd+fzuXdS4fGtgfxitEVp9haOK8Un0jjQ7A996FsxHH32cWz9s9QjNa6ltBbQcEjH/Q6HQ5jNZj788vJSnQe1snLt6Bp9qX6jPIe+IaQbj/pPQRtbNtphLHPmIYDKe7lgqW9f61TfIPHWoefpm1wHWt8ZS4+q6RNwl4KE+9WrV40jIJ1zcHV15Q3AHDabTdKYSMPbDM5UfM5k1keZXfPn4LdMClZ8NzmZWdLchckophTzs+zpbz4WYoLPoSCnDbnB4hC7kLRFBt9jR7egwRwB7xB85513vBCg5R2NRsFupy51SwkokoCV4nf8mIXXAQ0UMYcSL4fPoVbBwSLEaQpEn/OGpgRo9BXF9r5dAAh2QfM0PG/q6LEUSMakvkBTlDUDBBro3nnnHZjNZvD555/DYrHwa3nOPFCWpReukZfxaDyNTqnuyMtaG9Hwsiz91/AYx9NyoyXipl8g07S0TH7nl2QA5TTR/EVR+DlkOBzCer1unAxySKDt2NXwdVfWVYBwncR+ev36NYxGI/jggw+gKAr48ssvYTQadf4aVqo3HnN9dnYGp6encHJyAqPRCD777DNYLBZJI34beVWD9bqGV69q2KLD9RDg0aMJTMcDGGIo+iPcbhcpOiY4GZZmYmmci9zlCsKchw7QnaOkKBQHMJZDHLX03ljqSE06VV2IJ+r8ZeWYQGo36gjSwkkbSXhofg2XTlLTMWXNm5XGCoQnxDgtnocX7Knh8z8LwB3XjfKkrMX+uOLAIbt7FkUBxWCbJnV3rDjvuEi80EemXbE0HvM4IZ1r8ppzrhGOYaLzFVzjyODguWsTadcrTSPFibtf+f9uJ2xd1XL5dL1n7cDr58uqnE+Da/R4PAbnHKxWK3XOpieM8D7FPPiP8grXl2J6RUz34E4mLBdpwTxVVcHDhw/h8ePHfsfM9fV18OEdpuUnkvDftH5SnERbaq2zGt1Rr+H5iqLwp7tgHYbDoWmnamqdtgDKhbQN2zgSKI/RullwpeQblMfXa/0DkjYyCc2Dco9kz8vFRSFH9uQ2lNhuc638Lu1g0Zs5vwDYd9N3lRvbyvF87N1VuCs6Sleoqgpev37t3y06A/KP1b6Aawb2K59buU2J/+Y00fx8DMTGnMXGxPGnQDpBTvqNeGNtq11f1YXXYmuqZKehu6FxAxuFyWQCZ2dn2WO0rV5s2SnNceN9x9PpFEajkbe/5cJtjXHrWhuDQ14NQKFPe03fvK6F9Ql94LfWu9M5BdICrzGYZsjiwgQVHiTjFReMUTCkBk9OG5/c6ZPTL01uKaErNajaMlHfjJY7+GN0S86KWL5cOtosuilnTq7zok8HR1uILa6aYCAJ8rFFuy1dFqHOClyBbOtoyuUPHqa1E3cMYfjx8TEcHx8DAPjjLBHwGNOi2DtyLYJljPZc5ZwKvJQGrAsV0DRBOYZToqGLsNHXuMyZm61zl8bzkhNbaqO27ZIaa9q8IJUjjauYgpEqiwIee3N0dAR1XcN8Po/SHuMjjZ5Y3fi8p/GipODFlC4NuKyhKbrcyJkDqOBxGSmHb/uANvOW1Pd9yAZ95kPANn727BmMRiNYrVaBrJkyRuSuhfReOFSAHz16BLPZDL7++mtvyM9ta8kQZ6HVue3/7g0AAOoaYLWqoVjXMLMYOlxLPoygFnffRsKzy6VOMkvaNxnaDBElD/b3XQC6M9U71flRwz3gjjqBMW3hQgf9Lp8/qtixNLt0yXHu+KvQ/jwNx+mavwM85MnDG+/Oib8bjltH0pC04lNJ18BDyuVxHE/s3znixFX4WQpvtNnuv1pVUC0qqMvtWr1cLv0upADfDrjcZbE1aJCzTmj4Y/olwF6/mc1mMJlMgiMLU/qlRaeL6Rr0t+RolPJYZTjUh5xzft232mwOKXel+pS2qcRX2vpP00iQcmxwPm1jJ6LyD01P9dSULKPRLv2mMnMfemlqrEj5rTyP4Zb+j+FN9aFER1c5OlZGqrwcPTRV7iFsp21wUr7rk6acfrLyKtedc8qS1oG+eMlqu5T0eik8tc6kyonRltPHdG7TaE/lbcNTmAf9KdRuQXdOYxqtbMSVM69J6Sx6KucrPAUVbTd1XQcb7+jH9by81Dwjzald6tgnWGQQy7zbpby2c5lVHtP8AHcZYrRa27C1M9aqNODA0UA7zrALXfToHADwzgYU9HInTOk+2jeFSShYBLwc6BNXmzKlRUyDHKfSbdQrBRJNdLGhfI0LU5cFOwVdcfIFkS6SaBi/7X7g5Y/HY1itVt5QPhgM4IMPPoCHDx8CQLPdP/nkE3j16lWWAUQDutMv1a+SADUej2E8Hvv31WoFzjk4OjpSv4LT5kupfM0RRdOnIHfxTQnYEmgOOgsOXFs4r0ppEOiO6LaGAI7fksay+zr15T2FGD4s78GDB/D48WNYLBYwmUzg6uoK6rr2fGcpi3841cYISecTxBfb3cB3t+aCRCdfpzSjUmwOKIrtTg3u2B6NRjAajaKniPQJmtEvBTdJVxeg7fwnf/InUBSF332MsiM3qvP8bcoD2J6wcP/+ffjoo4/gD/7gD+Cdd96Br776CjabDSyXy2yjC/JiH7Q6B3B+voFJXcBpBeA/M4qR5CLxlqo4aNzd2thdingKfG3ubHWww7FLh+90By0tx6cXmkfbLcvDRX5ktMbqrbYHjQx+ujBMchrxcCm/QoNYbqoPpXjJyUUCLTt1ufNROhpYQtNIJ4BPU+h4UsDvJEZ89J7Yxr22Bdh2xvpulPlASsvDkrzgWLxjbU7fXTOPcy4Md2E45vXv/El2wfL4xj2w6ISond9Zq90Z63e9kh2xUhi2T1AurtmkHYLfLO3y2RKuP7v2PPDFF1/49do5F8hbKJtgXkyDtgZ6ZL3kVJJ2nrbR9yQZmpaLawkaaQG2u2em02lwIpAmq0kGZk2XlZxlXWwWMVsBtUnR9f/Zs2dQlqWX2+kOlD7uYI3R2kY/iDkR+O4Zq53EqmdKNFgdHbQs5C9sc/rhc1/yJu3LWB1i5cVkeCldCp8UnzLq0n6JOQ1o3922HYVCzBnSpzzfN7TFqdkJuoK1X/kJegBxnmxrT7XUT1rL6PinayKfsyy8QfHTsc7nbo4ntftVG898De8CfaxxHCTZgAI6MelHVXVdw9XVlX9frVZR/rHYaDSaeFjbdlyv17DZbIKPqNbrtbhjNnZiJAJddzSacFx16fvcOSFmp6W4YraKPuCQdh1tbbjr0MYGxKHbDd4CaMZRi5BDQWK8qqpgtVo18tIjelKCkmXywHjpDloNtHq3gZtmwrblHXrAa/hznShSXklRS9GTC9Y8bXiHC3lc4OA4UgtzG2N7DsQWYY0GLU/ftGq8lgrjDh8+t9AjQSzlaXFaO2mCvnYELhWqRqORPyaTC65Umc8VtDThpK85LaWstik3lzakYTab+fuCsS3xCDmO16Lsp8q0hGlxXeYiDtQoQE+rQH4ZDodewNeMBRSX1RASU4wQl5RHK08qI1bXWFkcD5/DJOGeGr444FgtyxJGoxG89957MBqNYDwew+XlJVxcXJgVIauBLFWnPqDL2s2h65wizZvX19f+N/1Q8BDGV4TNZuOPJZ5Op3B2dgZnZ2f+KMA2Bsm+2tk5gFoq37F/DMsoRnKi0jgASO9+JWV6HFKYUq6Ex1JWDnDHcAOnHVGynCBdCreT06Scl8n0LvxtciKmC2kNFodsTjpjoZ5mv35gWLELK/b9JY5xS/81glwzXHkPyiQ8I+2W9WlpPI1zzTgM545Y9R9YPhfiCsLrMNynVY49po5X6ohtlIttSNvFNX/Tunn8SEOxT4MykgSSYRc/0sEPreiRtdTOYZWBc2QkKY7LK5vNBi4vL2G5XHoaMU5aT2O6HKcptnZpcl9XBw6V75xzUJZlUC/eR1ivLqDRq8mrGCe1C43nfSDJl5pzI1Y2x2epF+cLrVxN/s+Re1J0c9x9gjZ2rGVK/GVNb02T0l84DW31d2mstxmfsfHxtkGqbbrWGccRnRssOo2FhyguC71d5mrNlqmVY4XUfEZptsx9lqsC2vRpyn5rBWlNpzZA1IGxPLy+jOOw0qqlT80/2hrB8aJzlZeJuru0jnD7i5Q/B7R+ja3ZbcvjmzvafHBlha7jPQV92YTfFujdGQtg68RYWg3W67V4Z2zf90viV5r0+GMKuQ6KnLR3UeDoqvDcNejaxn1OSH2BdFcowpved0j/oe8a0RR+LYy/U2MKQLgjH6GNgsuFBi5A8Hbhzlgcv9w5XFVVsPsKDULa/VOcTuqsbaOM9iVg5kBXhZe2571792A2mwHA/ou71WoFm82mMR7vwhwRA23NlowFWDe8px0/ksL1GXduYh6rA1QTZlPjTsMrKaBtQWuLXONITMmlgjrulLm6uoIHDx7Az372Mzg7O4OHDx/CX/3VX8Ff/dVf+TtTqPHwW9AhNddWVQWXl5c+LT5TPNwGUAEeDAYwn8/h66+/hs1mA+PxGB4/fgzr9RouLi7UjwwlwPnbakDIBuKwCHYA0nccr0WYr4FKkSljO1S9Y6eA/V2xBXF0YjJyHCzuTMQjZANnLebXdt069kT6Irt0NYdt4Ci2ADp9tAy53RrB53cFMidbThkmPksksTpC+3Ka9up83SOFYBcsQMhHpFx/B61Tdsa27GMelr0rloc5whuIk8Q30lLnCg0zPhu7Y+sQD3eo8jw8PAhj/wH9vA6s7YJ2xCz19t+V2/R0TkP5TzsZDOdAtDcMh0N49OgRAABcXFzAer2G5XLpDXDcsM4NwNzIJ41JKpPStUXSJfhpIkVRwPX1NXz55Zde7uMfbPJ1h69F0pxP26CNIZ22Se56R2nD+tAPsfgO5aKI77BsUzYFyRBN42K46PrPZUr8TQ3YOfg5jVpdNMeoZGSn9NG+7Pu0vC7Q1tantaXFdkTbibdNW5yxeki6TVeHj8YHsTzfQv+A9nHJ/tN3ORwsDlwLr6WcZim7nYVWiTYLaGO+jc3CSlMb+zPmkWwFdA13zvkPkjGfJDNY6GwLmozA33ld6DHFCHQt5LYkKk/F6Nd0aYkm3j4SH7R1xhbF9gQP2pd9+76+hduD7Nk5x8AeC0vhkeK1Y0tyJjrLMcWWgXIoYfHQQihfPCyCW47Abm272OKqlZM7WUr5rI4BC57cPG0UzZx0Fr6+baE3RqO0aNIFU1PmJRyx8DZjTNpFCgBwfX3td7HhzqblcgkvXryAFy9e+Lwx52bMcZSaT1OCWUyZwzjp6yreXjHeobRrc0pqvLcdJynocz7FelIjFBVUJ5OJFxBz6tBGuO4C3DiWEjIxLNWW19fXcH5+DvP5PNjVR/8tdFGwtgXnQXxa5owYdO0Li1Im0Ued2ZPJBD788EN4/PgxfPTRR/D06VMA2B49Pp1OYbFYiB+paes4HYtWA89NgXVdpWm6jDUeFvvgp685CvlyMBjAZDKB9XoNL168gJcvX8KDBw/g6dOn3uAN0DxuMAaSEa8PkHaLObdzhDoSrpDonHB8MM0j5aVxoOOOEK3iDJyqCt3iDtoYbsyj7ILN2e1r3m2qdLHoRKL5uQNKoEH6naIL0zTmPYF/kvXEfBY27oPVFRytnLbokOX5CvIkDvzoMcUWSPUheW+0KRm/DadrxAnL451z+zQsPghj6bxsgO0QiXOOOWOd2++S1Zyw/EkdseBC2nd/vJ0aO35hT1t5XcLq5QrKq/gd4zG5Cuf6s7Mzr0/M53NYLBbijtiUEVGT6drIQ4gLP8BbLBaB3oBrZkqWwCemp85gKotKdeMyi2ZUTbUF1SNj9PKjGaX68btNU4ZYXganQ9KjLMbiFKTaNiUrtLV74D/9wJf2XY5jxVKupX142RJ/aThjZcfkbQuk5FKpXTSbAc1v4UGaP6Wnx2jnNEo6WQwseujbAn3ULdcG2bfjxmpXzYHUB/4pOmJHDgPIPBbTk/kY4Rsd6Lv00VWsj7rODxpeDTS7nGQ7aFsGgmSLoXG58ouGW/vQjdvmcmnX5kFtfdJottirc+cCKkdJG37wgyx6ZZ5Go8T/qXWuL5DW91Q5b/OaEHXG5g4SSSCNQW6nx76Ysxqq6DZvKy05EBNAuhqG+4QcwVKiO1aXQ0xAFkgJtIcsO6dcixFZmqi0CStH4L3rQJW12J0xfSyE1jSIf7PZNIQxvBcTYOtAOTk5gRcvXsCvf/3rIJ0k8GgLo7UfpfRaO/C5meZN8aPEf5JCyOulKWkSTskIwSF2DHcMeDpq2IrVWeqfwWDgjyKmAl9RbJ1mKACl+oTTlqvMxMa81MZWZZ0aUXKV46urKxgOhzCfz4Md17xPY8feaHRaDUJc8ZT6wbIG0Hxtjuvm5aXakisneMxzUWyPw/7e974HH330Efz0pz+Fn//85wCwdcbOZjNYrVbR+iJOTSGy9HMuL2ht3GbtPbRgTg1H1LDch1KqQV3XMBwOYTKZwHK5hIuLC3jx4gXcu3cPPvvsM3j27BnMZjNRcY3Rxcd8bzS78Mkdld7RiI5IrVgH4Q7VHf3+3UHoxNy9N5ynAMGuVtW5yn5Lu2O1Xbg03vcBw9HAR2lRcMecsmJ/Of7qGuE+H+unGB5zHE+z66eUQ5g7/ESHJuJyzqdxzgXOrxiojtK2bK/ky3LIIt+Qd/ohQoCn2NeZl9eV5gAn64tGGqmvSPvzdNhfwW+Wz/cp6VvYiaD0jlgMb6QnzlbfRgS3j3OC01UK4ztjgZQHrM68vVg7+LLrrTN28flCXO8lOZcbzopiewoXAMD9+/dhMpn4/M+fPweAve2DGoRTxiy6rvE4DSScFA9exUHT8Z0ztK50ZymnR9IbpLaLAbaHcy5p20nJMJQuvA+O46G04Qkx2C4WvT5Gk3TCkcQ/MZ1Yc3jStpVk6V5lBdi3D93VTXkXacK0mg6ZAxrf5PQLr0Nu+RpPWcvX2oTGS3qURmuqzFzbUUpXs+Kw5O2TH99U6Dou6dxLj3S16tMcF6aT7DY5tjQNL31KNGo0WehuQ0ssP21TgKa9sq0zUFuDNdB4RMvL3yU7K1838D324VVsXeLv0pyfA3yN4PnpyXR9zG0SzanrJ2J2N/7bQiNPS+1EEg1cJrG2Q2rdkdLF+M8COf3PbSJ9rhOpcdYn8LKiztguAhEFi3HvpkBz6FoGXwwsOPvuzC5gXeS1RTZWl74G6SF4JFbv2KKmCQaWxdsiqFgXJkoLXQz4WfJ9K1d9gEUoaquIWSF2lHOsD2O013UNz58/B+cczGYzf7QYhTbChkZnLqASvNlsgjDkE9om+BWzlcacenHhie4oltLF5p027aMJFCkjDceBY5A7HHH3tGYU0spoK5Sm0loVJM1YFsNDhfTxeAyj0cjfI1aWJZRlGfBWrC+lPpAMSzxfjAcQJ999QedGqd21ekpxnG4Lb8UA0+JYxfzr9Rq+/PJLODs7g6Io4IMPPoA//MM/hKdPn8L5+bkXyvFYwbsw798lWScFEh9ockCfbVvXtd9BPhgM4Je//CV88skn/qMGjQ4JJH7umw88Pif8A3sWLA7Ybx+k3N+6w0Hj0Znlw13TEesgHN/UAebbh+HmaUSaOH0FfU04ZBEa1XRimzTKoq8xI4hrvjfKYL+lXYBS+Q2c/LdCrxpmgIA2Kz7Oc7F8uXQZ03vHLTphaZ8V5Im/HYCr2fhNFWatIwlTeYH1aRBG39n4585M6igFgL3D1bkwTnpS5ywNc2TtZjtiXe10Z2wlPOnOWaktKD3EMSu1DdQA5aKE+edzqBby7oygC4gMMRwO/f3v9Ool57ZHBOJHn5pOxI2MbdYlyQAuyT78HWU6pA9lLe5k03DF6JRkqZgcgc4++nGkFXIM9DwcHbWoT2Eb5NhUtPBUH2B5sbviqJxL43ib9mXMpIZ6rk9uNpvgQwKehwKn+y6DprNpY8aiqyFeq93Rorfmzg2HtBshXkknvAs6y9sOFt7S4vgcgjqLpUyaL1UeTYdzepu5uivQcRCzC8bGfh9gXRO6gLSGa6DtXI7RlOKpVP424Jwz20OkOZuDZqPqCpb1jtqF0K7GabNA3+tqylbSFdrMU13gptcgXoesY4pTQmoutFEgLAJIrDxNaJeEaatQdJeFx67thU8tX8phovXZTQhimmNBS5uaGHPi2iygMUFYUmT4Dj9N6cqhIQcsC7DWLppRsU8jcgpPH2MbF/3Xr19DWZYwmUwAABrGdItxgdOcGncWIZW2t3RXkIQnRyHrKqBwBYHv3u0T2hqstPzoSKFQVVX2F5EWQ1VXsM4/PG1KYEbjIh63So8npjg5LfS6AL5O0HlA4k0NJy9PUzA43i7tnhor2nyXUrKogbYsS3j16pW/y+Xhw4fwox/9CL7++mu4vr6G09NTGA6H39i7Y9sakGJ8dBOAczKOh88//xycczAajfxRQwCy8UrDJ/HUoeVT7gxthLtmOu7QBAfN+1slPFEfacKJysvR8JFw6gAW0wv1UEnIZS2WPuDNFrgsO0idc0Fa3p/xIkInl+Tgdc6lHY0CXTcJwS7YNnQQJ6t/d+E8RR22mFZrF5GnNbokPE7+7fkpFuZIPzqSJvbbMT5yzTgfzxy29MnTBOF1+O6cCx259FmH6aS6OCD6k+KoDWioHVTLCpbPl363L22/mHxRFIU/1QI/XsN0y+UyuBKFyw9U/knJFBajXWzt1PBTGvhRvTHg9gTJ4G3Rf/EdnbHccW3RZzRZl8ukWv0BIHBIIy1SfS3G6BxdLgWS7Mz1aqQ5V99O9Q/ixbTIy9Jxiho+qV3ayGUp3YWGp/iBppHeYzrPoWRKjX8kulPtTCFXXtT0whi0dSi8LZDT9m3GgNYndJxaaWjLv7G5lNMjlYVjCj9+seo/OTYp65pFabbmtYKFBgsPxOayGOSORdRXU3RQfFI/a9fv5PSJ1teSvBTDx+fSlB4dWzclmU0Dy7pP3yV7lkavVb7Iga48n9vXOXS3oe0urTuHu9E7AV2FKxom7USy4teE7rY0fguHAT7h9CVIWsvuG7rwFp2Q8TgGep9pjuAN0HRg9MH3FuGJwqEvIm9reJfoogaIZ8+eQVEU3qAiQUzBi5WLcxvHZXXEalAU+2O1Uot4iuaceTZGT8w5zNPmli3lbQv0nqzFYgGr1UqMR6PEoflaAhzDXepraVtaR94OtHxq9MF/6oylc452B4iVLq3e2pwYM7ZYFB8LX0sKQgyvcy6YT5bLJXzyySfw6NEjuL6+hsFgAPfu3Qs+/ECF9Zsis9wlIborYF3wWGKcm6uqgvF4DO+88w5UVQXL5dJ/8MDHT+7OoGxgTpbCFcFvgN14Yo4jdNAFxxcTR0iwuxVxFvt0we7SYu8oKbbbDnWjOe54hcI7X33anfPFz5NCGl8XgjrYmUvLdRA6krF+xW68dz1ydkevlMaPd9d8bzhGaR7N2SSE8TJEejkOoVyeNpirHHtKZSj0RfPGcChxUWdxjL4C9jy04ymA3Vgpdm1eQGOntp+3CW7pCHAzPbtwjWcajlYpjLVpwFf8N03jCI6a4CJp6ZPuiBXjHKh3xQbvsR2x5Enp5+++vrQt2Nrtagf1qobL311ud8Sy9knJp7i213UdzOHorHr69KnX6cqyhOl02jhGUPvATCuzK9A68R2xPI1U/5Qhk8ZbZDBMMx6PYTqdwmAwgM1m449P5h/6cZDkJF4+TYN1RRlrvV7D8fEx/PjHP4bhcAiDwQCePXsGz5498+8WuV8rU3NwS7+ldkN6uU4Vo6kPvqFHUiMPv//++3D//n1fl2fPnsFisYDr6+uAlyQ4hAG5i6PxULYl2peSk0MzwlPguwi76n8SnRbQyn1T5PW+2+3QeLuANue2xWWxyabWAQptj/m1gvThf2xt4CDZpNvOo7F1Ltc+25bXrE5vLEOiFeP4qY19gGSXvE3IsZX3Nfalk11RNmkDXe26MbzS+EUeS9n5cqEPXrjt+dnkjO3DsHuoinLBk5er0ZMKvynD5m1MKKlJjU8y0qSjtXlOP0t4Y0plrqCZ66DPETiteTVlsC3EyubO2JhCKk3oXWlLgSSUpYwIfQmKWEasfpohJSZ40KNq6eJjacdc/sQ01rlONFKztrDSYDX2tO2vNopBLg91aTf6xHj6AQTPb62PZS624JFo0HBq6XIBBS5NuJLaMWd+aUtbrE3bjDlt3dGMjSk8Fjroe1VVcH5+Dq9evYJXr17BfD5XP6bQINYGbdZSxBmbYw4FkhHzbQDcXYPKLBrYjo6OvAGfHsmoja/YOEvNBZJTIUSAD9dwGPndsW7ntKSOjQaayC5WB8CjxN2x+NsxfBwt0lAkylVocQXZHUujKK9jutB7u30oO36lssTyrY7YqMexiV9LH+yK1WhyJE4r1im/admuWQ/VwXtISJWj1aUNfQWEvCTU07R7OEYDD5P4hoY71t+OpaXv/DfNS/ozeNI4F4b58oU4P5cl3nne4Envo4WQJu2d/6bPuqphc7GBeh0/dUPTo9EJRZ2sKEfN53MAAH8XGOoUMeNVG90pJg+kwiV9h+ah6w+2B9enYvKppq9x+WkwGMB4PPZyOL3LNqW/SfKWxeiOTs3BYAAPHz6EyWQC4/EYrq6u/D3wKfkn1q60HEm+serydB1P8UFMv82pA5bl3NahPJ1O4ezsDB49egTT6RQAtscWX15ewnw+9+lovQ4pP2ptKr3zMKyXVa+Syub5JFkr146p0aPNQTQ+x46VyhejMwU5To1DQ2qsdLVvdIE+9Z3YHH4okOYp6Q7pWB4KXWyG0vinpxHFIGbH1MrJtePk6ug561msvBx7oNWGnJI1kjqnUv4hIUWTVtdYHeg8l8tnEm0S3rbA15FcfBIvaDICf4+VdRvrQo6NsE/6TM7YQxvXLGVLDEi/gLTi0gyI31TQhFA+OC1GV4sgaVn8+/xqgtIkXVT+tgFVSDW46fGcK0jk5s+BlDE6Fcbzo9EEhcrY7mRLPTiP0nGGiyTfBaWlp19LObe940gbfxy3VSm0Ku7cSEPLzAGLYmmlM6dsDSdfh7ANAeQdDDHh/S7NTbmCcax9Ynna8EBuu9I0dNcBOrxi6TWcOQYODW8McA0cDAawXq/hd7/7HSwWC7i4uIAXL17A119/DV9//TUUxfbuYq7Y3gRY5ss2kGrDQxrsbhIo7xdFERxZDLA9Bh0A4OTkBJxzMJ1OwbntvYLSuHHO+TwxBdii/AZtLDhBGv9AnkAcGZgfnN8RCA6Co3+9s7NBxD5fQBM6SgoIdsBiubgTsXEPLOKh5VHHLoUiLK9BKzaNIztokV62o5Y7VC2O2cYY4K/SGCH9EOyKdew3kHhCW9C/Cm5xLgTCi8grILwLuIMdu4xnxLq1ASf8Fng2SMP4uuEcFeqCfObfkd+54zXItMNfd6mggJeH0aZ1Qjsznmn0t8ZLjuV1+/DA8MTjpDtiSRwNS94Va9gZ26AH9jT7eD4mKM1YXrWnxwKSDOScg8Vi4e/TRJmR9gveDYYG4pgeYjVmIp42MpeGT/qdUwbtk5QDiUJR7I96xjVzvV57RynFrckpdK2lDnKahtJJ+2k0GsGDBw/g4cOH8P7778Pl5SX8v//3/5L15TRQOnmbaE46iR9wRy69zxefFB+VuamumnLaWuoymUygqiq4vr6G999/H3784x97/fhf/It/Ad/97nfhiy++gKdPn8K///f/Huq6hocPH8J6vYarq6tGPe8S8D6yOGVj/Mf7heqSPA0fDxaHaMyJkDPO2sDbIp9zeBvrZLmXNZaXQx92g9sAyRmZS1vMmRsbE6k24/3TdQ2X5hOtLAQun2hrkwTIJ113yFrlgq7j1CpPATT7/FA2kRik7FgxyKkrb9u2ZaLshlBVldnn04bnke7bOKVQA6kevRxTfBOLlMbkbRyrkmCVEm6kst9EoUNToKR01gF6aFpSZcaUQu7IstAScyB0gRidFHIcR1yBbNumEj19CkoxhSSm6Ej03QTEBCeJx7oIcbHyNEj1MxcuJaMHx0fTcCHDUoZGH8BeaEkZO3LGKK1bm/aj/7lrCS2bfw2P9yTh/bGHWiP6HKcWQ16buFgazg+UjpQhQ8pjKZeOV63vb2KukeoZ+73ZbODi4gI++eQTuLy8hMvLS1iv117IzFWC7zK0NRLxNouNZ60Mi2IszWHWsZiSXfjuGlonNGzGgN/Dkxob2vhqhO8cE8Fv4qyw3NXqn8QBGjhJuSMVjw4GBT/mL/YOWn/kMM3vwqOIkQZatnYccRDGHL76xt50vKmttGjKY04IozgkXKQvY7tfozjAUA8tvZCN70g0424zlWXmya2ndB9sCr8f5zm7tkMkapi2wxN/m52wmJ7GkXfnXBhGcPt3+gQ53P+uQ3weP3XEUrlRKwfp4PWi4XwcCPVwzkG5KKGaN+9mz12zpJ2cmowklRNbb3LkoT6A42yzrmK+XJuDVXeUZLxYWTHjPPbBarWCzWYTrLlWOazNuizllfQsgL2TJRhnkMc/ubyC+ZC3UT4djUZwfHwMDx488IZjpBOdyH0Cbz8r7/G0XZ0eOeX3jdNKs9TXbccvL/8m8rxtkJrvb7p8DrF1rosNqws+zS6USieVxXXvNuXzMCmtZr+K0cjDLPYpKawvGaBvm2WKr1Jtk8qTKseCT8KTu9Z0hRz6c+hpS3sfdb4Jm79ku5egi+1JKtOa9tbujNXgkMyMxi7JIMYFQkuHtJm4v4UmpCbpNnjuKmiLAxcGrIsu/6qEOhokuAk+fZPHAhfmUnWRjqq9ifqndvRJwqZkNAmMWSzOAppQS3EMBgMYjUbg3P44W4of68L52FquxRDWtn5SWmxf3L1wdHTU2IU8n89huVzCeDxW15Y3Yb5CyDWSIeCaS3mBx1HAY1oxPlYu3wFhoQ/vVx2NRrBcLht3PbftE27wa6tAS0bAoth+STifz+FXv/qVH6/SXbFv8twLkG43HGe8vlyGiK2jVsUKga6p9D92TKMEEm7Kwzj/0XR1XcN8Pk8aLTVFA2m1rmlR/ifOCfrPd7k2HBkudIr6cgoI72BFxytxyPr7Y7F8CPP5cH5nK83Hnb8FoYU4Zb1jF2Dv2MW2wzL47lrFqet3AEvONaOTjqdp9I1j4cK7tkNWxM2dUwSXn2MYD1CcgRzhGL0+mO2SZf8NepW2MAHi7St9ChfvasJ/nqcL4qjF9DWAq/f83wqkbKTNgzSMB/p2xkr84dNE7ohFByz/nbwrNrIztkEfp5O8B/VjT1c5cKWDq7+/gvK6BFc1Gzxn7eXzfcygzNcmSe65LVkyJldLsowGbeSWzWYD8/nc7zAGsB+7adk9S+Pp78FgAMvlEv7u7/4OHjx4AK9fv4ZXr17BbDYLdqm0qROVXSx4uL6PMi7uFI7xRcpWoOXR0tP5//nz5/Dy5Uv44Q9/CB999JGvywcffODlb4D9jiv8WFeS49pCbt0sZdL+wTJ4mW37HQAC+2SfY7qNY4mHp9qzjeNQsz98C3tI2Znb8DkHKb9VjzmU7mnBlzt35eK34jokaPpmX3ZeOmZzx6M03rWy+7aTHmKebFN+LKxP2rquhzlAr0vSIGWPkCCnDtwe3PWjLU2u7gp94QmcsZIgTaFPT7KGqw8PeW56KkTQiciC5003fkrQVXmKKYkxYS8FsTQ5wkQsrs1k2ocwZC0rlS+1QPexgKWMAG0Vkq60tM2rOS+ldzwKKna0Qps5TGtHLUxbVLQFR1sEpbkuZ+5P5cFdXnRnLOcfXveuAk1MWRANcy3LoeVRQQHvfsRdsVzQlWiTaM0xZrWFlJKdOtZa6zupXXl9NMNGSqmXxmaq73KMJhq9Er7YXJsrPEt9rrXRm2S8oONfU/RyxmFR2HcD9yVX5PYj1jM1hp3bO9Vxlw01+gIAzOdzv+5sNpsGb9By8UmPK5TSSPWmvMYNtZb1C3edSjtZMd454rh0LC+GFc0nOpR8GTunLz0ieJtdcMS7fXhQBuxpDZyvFKf0BJKXOmdx1y008YeNEW1KMU2j/Ul9w2An/3ZyeIMWJ5RtIdjJv51zelwMr1I/NQsvQ0on1TUFljwkjDtULQ5Wv3s0kS62a1YcnwrtQVq3f5qdsuw3TePjHYtPPBvOWeKU9WnwXdkZS521nLagHhqvSH2wi99cbaCaV1CtKqhL2UlmXZu5vBKTvbn8LsktKdm+jTxvgVg9+RwcKy/XuI/xVVV5Rywe95yi1ypTpuwU+JEU5r28vIzKNzGaUuFSe0j08uMKY7RIfZPSBbSyEfA+e5T1qqqC+XwOr1+/hufPn8PZ2Rncu3cPrq+vYTqdwmQygfV6HXwQ2YYvY+OurW2G828b3qJXouCTynfamLXaAjQ6LGlieloX3UKSL1P2EFrnlC3gmwqpsd9FL2w7PnjemK6RKo/jyx23Uj4LLTGaNBql9Ll8K635Es4YXus8zfHw97ZjLIajrSyUAosdkve71IbW/tJoy+EtDZe13jdhB5TK65LXshbTNZbah7S1921fG+7UztiYoNxFQEjh5vGpu0VvkgkkIS+mVFjiu4L1CwVpMdF2Jku/eZrcCbMNtGm3Pto7pVDH8vVBi5QvNu76bPPbBlwM8HcM8J6iq6sr2Gw2Pk9qPKaACpAWgYPuxJSM7tZypXS5PBSbW6fTqY9HYwE3nOQes8qBL/AcFzrNsc2kr71y6hxLNxwOYTwee2NDil7p3VqWBHznaaoMSVCnZaMTCN/RoIC8H6NbUwa0dDxcotsyPmJAj0hrgys2R3YZgxo9VEgdDocwm82gLEvf/n3N/4cCnKvG47E/vq7L3e0SX6XGP6a1tEvsPu4YxMZ0rNy6rmE2m8Hx8bE/erqqKiiKwp8m8NVXXwW4MFwyqOJv7f4i2j5aP9A0/jdznhSuCBwljV2kbu9IaTh40Im5c7JyJ25gFN45QYM0uzK8k9btxqGUH7bpxOOKCU0NZ6vbhwW7YEkdfb5der9rlzplYZs/aF/lyOXw1YnhgZNSaFfNieZpJOHB7lf6js5B2oc0D4tzzoX0YLmcLoqH5Q94RcHTCCP0qGlZu4VBpI0ce5fKE8rXykM+8+kL2N8hi/GkvNidsfR47Cg4IQ2nG39q/MH6iqfzfUV/Y746zE/nC+0p7ZSN7oTl79I/LQfYUwj3dadjg9JYOZh/NofVi1W8/XfQxl5h0R9w7uFzviRfvU36GQesLzpi+d2osbo31oYd8DUf00hGQYCt4xF3xH7yySdZOqRmyOX0S0ZJjOP379E2oHI6l3u09T4mw6bqgPnwvnqqZ52fn8NyuYS/+Zu/gfPzc3j//fdhtVrB/fv3oa5rePnyZWMHMN213Acfd7UfphwLmgyOu38Rx2Aw8PKv1u83NW41nS81dgDyddLcuUnj/1j6NjTdJT3pbQDk35wdbDhXSWOMb3aI6evS/NV3H/N1gdPWFp8G0saFrmPvEMDpSs2XNA3+7mN+xt+aDYznSc1nfa09Eq67Jp9pNjhpLFvvXuXtS+UT7UMlmg9356K9mLaZNsfchI/oUHDrztiYIRbBKgRhmCZwpvJZ4KYXcEv75MYD2IQuii+nDzDcKnTxsqR0bQykAO0umW5jUKdpuhr4u+a10qIpETn4rXCTk2GuU00S8uhCQif+2GSvtVVbwwwVMHGXJRV2c8ZlbPxwPDG8Mf7IFcI0enIgRWeu4ifhabPAD4dDmE6nUJZldAd1W+NHLljbQ+pfOk5Go1HDGWtZo2KGw1j6GM1t5yH6QYB0PJpEW66xqg19SBsABHcNUwVQcqDF2r8vxTEHuBBe1zWsVqtGnGX9ocDHolQvTbHvCrG7rnOPy+aACsdkMoHhcOiPF5SUGek9FsfbhENKKfV0OPDOCXTEYrh4P6qDwLnnjwFGJy4QXJiGOVKLYu90DfDSOB9MHKySYxWa734XLdKFzldo0iA6ZLGd0QGL9FFnLy2f0JoEKQmhW8RJ2jVI7/T0Ac868s+LZjjVeuxwSo5OKT3lkcAZ3Cc44Td5avXgcVG60OEPrD2BhRfQoCcY64Kjvnd+kerOw6R313xvOF8B9g5WR3iMpnHbNJQHTTtjLccU0zKBPZX6Sv2B+Nev17C53EC1aMpwKWMeTYNA1/RYnpReLc3zvM5Subl2ji5rqCSDWPRLKyDelEyZK4tS/BRQVuT6IJXVNBpiOldKppHieL/jOzr6rDagXFuRRd/D8OFwCFVVwWKxgN/97nfw6tUr+Pjjj6EsS++k1erTBlKyo/Qea2/e9rljgsvww+GwIUdKuLT2bgtWHTgVn0NLysaQAonvU2MsB27ajvs2APbDaDQKdB7+YXYXm4Y2/jgNGB+z1VIZPjXXx8rU8Fvz0DSp8c7p5yeuUdDsSlo5faSVwGofS7VTF5kjZ+7HMlJ2kxik1is+f/Vpi+jbhqrl75tuXpZk/+mjPIvtsA0coi0o3Joztu1imJuPTsZavCXspiF3IrOC5HjRyqThuQwec4KmJi9NCM7BgWn6WGisyptES04/tl2EchwdN8nb2tjTjMs3AdoCrLULFYi4cMTBMpfEDOkpurs6gfgimBJ8c8qS5pXAoA16nWM80gVEg1wL4PRp7UhpRmesc65xLylN1+auco6jDwNGStHF/hyPx427HFJ9pwmmmqMohU9q/5hxkgJdl6T7mXg/0/pb+j1Go5SWlo87x51zMB6PxXqXZZk01vA2vS15BsutqgrKsvTHd+PR5fRYPbpWaG1E5xRpraO7uBEn4stV1qR6pMYKT29Ji8cJl2UJ0+kUBoMBvH79OjiOWHtKO0gwTnLqSzwZG2u0nX0ZO+eFd37iuwudtEWxj/PpiUOWpqF3sW4LhtBptcsPjq2fxAFGdw/SO2e5wzV4Z8cSB3g4P1I6CwjyeToLRi/APo0VpKHq8OHCNDSchfk4Op/R/LQcR3gDCI+4EDdPE+Df4aBlNJyImIbwUaOqTqex0Q4AIg6eR6Ijll5Ny+qoOlg5YPzuWRT7XbMOHEAN2398j/FLaiqP0B+MdSmcjjcWxvs5CHdhHs87PI4+yY5YyhN8Fyz93XDGViQc/yV+5bzM+RpkOp1zsHqxgvnn80SjHxYsRmlNR+drnybL5hj4UvLaTUJMj9DkS5qPPi0yAl9Pi2LvZJNO+OHrsaUMSX7gbYxl8b7k8k5M3myrc0ltKrU9XqtQliX8+te/DtJr8i0+YzpEG0jpSFbZjkOMb1DOBdhfXYO6YE45KfsEpyUHt1XXovjb0KrZA7lsH6PNUv63sAVt3sgFaX0YjUYwHo8BALzuopVjLV/qW67L8Q8ZNLuSc07cHSmtexK+WD00/rbu5s9ZO3HOQKDzCT+SXqPZOl5icoAElrGaAxq/ctnFAprso+FN9VkMf1e+12jLtelZeDeWV5JdYieYaeVZbR/a6YQ5ZeVAW37sOy1Nz/Pd+s7YQ4I0Cd81sA6iN0EQsbRzjqGyK+QImn1BrjDMQaKpy8RwWzwTU6xomGXR6aufLAuI9F7XNYxGI5jNZl5AatOu/ItqfryWRCdV+KliR3EtFguoqqpxdBWvkzQfxpTQHGXdOj/FjsZMzXeSsYnHc2MDv0OpDWjrCPbBarWC0WgEk8kEnHP+KKo2YBXCaPsccn3DciaTied9ei+UlB6gneAM0NyBKPW5xYDG8dJ0lAf5+MP219YpSdmTytYMCZLR1LntEcSTyQROTk5gNBrB5eVlY1e1plRyhYWPgdw+6QOwTkdHR3B2dgbvv/8+XFxcwKtXr+D6+hpWq5W/V5k7ZCWwrmuSwt1GdpLSo4ER+YcrMHTsamVzvhoOh3BycgKnp6dQlqU3bGjzH53TOD9h3yOdvP95+TQsKbcwZ4Xf4YpOTLrDdedsDZxbu7zeUUqPHeZ50TlCyKHp0aHl81NHLAiOWMeOJ6ZNUJA2oI5d4kDzbYw0SfXmd9j6au+dPrE7P8X2xvwszLcncXxiHbR31SDF8dC0QOJJP3KHXMNpSmnkfBBEkzjOK5rXUVvqGH2N9pNo4HkYbUnnNaGT7n71fFQwXmZlnH7nFE7eO4GjR0fhuMxZzqW0Wj0B5HKEPm68C7zU4Be2IzZoJ/5Exym5/7URHzmmmP5jvqgjltJP+Y7XY1fO+vUals+XsLmIX8dgga7rrzR/x3Dlyl65cXcJNPkN4zhobcNPG+LytSZDUD2D9o3FoGqR37W68fU6Vi9JF5Fkki58w8PwagqURfhuuvV63bgy5JBg4ecYDVTfsdosVquV15kkuTBGh0VmlfQITW6V6ON1aeMEsODVaGiL/67bQfuE26xvSm8A2I6Le/fuBelQZy2KAlarlbiDk+Prynd8fFqOqrWCpR1StMXwauOE9/tsNvM2vrIs/bHwMdDm9q7tfQiI6aa5eKxzKEKbta9N+/U1li32WgxLzcOHmmNy8cZsP32sSV3B6j9oC704Y2+CwXLK6GNQS4vFoYAbUKXy+4I+8Ept2XaSumlBI+XQoWlSSkds4bSUE8OdQ5NGA4/jE3MK+nL2pMZwjJcOAVK9+G/6jkojvQNUGrOpMnmelOOHAv0iGxVdzLdcLr1jgAuhMT7k/dK1H7Q5k85pEg9a5oBYe2v5ueOXOk9ifRejR+qrzWYDzjl/LyYvNyZMUKExxzgmpY/NV23mWZpnNBp5RUDabWehWaPDymfSeMk1WmCeFK92AYqfjz+JlrquYTwew2Qygfv378PR0ZE3VtFd1ZKhUVI6uBHxpkBq0+l0Co8ePYLf//3fh88//xyqqoLlcgllWXpnbOq4JT5mpflKkptivG8dF9w4Sw1WWpvH6sJxDwYDmM1mcHJyAtfX1/5Djrqu/XijZXOjG4bxD30wXtqRk6p7VKlDJwZxXgS7RckOV7rL0ofTvAB7Z6YLjyn2aemzEN4d7B2jlntgAbxTljpTxWOWyU5YX45zgPd/UlwBHXSXL3fM+uAMxc4paZweRt+1Haocf8Pxy9ufpyH4PW9Ac97xjk3KO+AaOEUHLKWDldmgz5GyOA7pt/ROy7RMnZw3Y3gZHD06gnd+751tcn5nbKHwSAov72+l7g0eEviGvzd+0zQ0DGkgd8hKd8NSB6t3qFLnKsXnnOiMDRy7nB4gT9o2vB0o/+zwl1clLJ4uEo29x3HTxs2U7npIGSBXVupbx7eWnyPbS3KZtuZKuiIvS9spZZVXJf2T0slxYLx0nYLEm13l3VS+oii8E7IoCn8FA8p79CqGPmXvQ0BKZ9ToLssykMUAIPjwkOOn+LraqVIg5Y3xi0SnRvNN2/TeduhLh+8TkKbBYABHR0d+/qmqKji2GO/1pnn6pgGg+XEKhmE6a9mHmg85TQipD2Vo2GQy8TbI9XrdGKexOYWGSfprbr21srS5MWdusPB7au5rK5P1Zf+RgNOTY7eyyA20bItuf1N2d0n2kOqTks0OPYa1cmPhqbklZx29Mztj7/oCbh0MsfTa3SIW4f4m4KYVSo0Gali0HMfQFqQvlriBVRKkNTiE0ilNWLl9dNNjK2eivQsQU66cc/6YTXp0JDWAdxmznN85/1VVJbblaDSCxWIBy+USqqoyzS0cB43XhLcu8wG2HXcMpIDyvbaYIS6+G1gynqTK6lJHpKUsS5jP98fZDYdDGI1Gvn81hxOWzZ9aWk2A4v3YRgiNtQXesQqwNzKkeEsrJ5YnRxC3Qk5bUEFRG9fSvEzD+a7EHGPc2dkZ3L9/Hy4uLgBg+6GFcy7gc0kBo2l4uTc99xZFAbPZDJxzcH5+Du+88w6cnZ3BO++8A8vlEi4uLuDi4sK3lSac07pxxZr3k5RPoisWn0pL+1bbccLzc6B9gvPjy5cv4fz8PDgpIYZfow3zUoOf1q6o3Nd17b+wpnzT4HEHfhebP2YYd8funBh+96rbO1GCeLLrlTpw0bnpHaqYD8DvOEUa/JHCWEaxD/dOF+60xTYiO1/pLlqAsHwHhCbcZYs7X4tdHfnu3l34tljnaQgcs7QPUl41Hu1YHu5QImH0nTpBuSOKOtiwPf0TXIDLzycuTCvhDBx8lAcQJ6WJ4WzkTdWfh7M2o32i7nQV2oOXI/G0x1FAE58UxsKdc00nbBeg/EfeeTx3UAZhLkwb8JJzzfYAsN0Ry5+RnbHSTlhwAHVVi+ESv4tylVRXRtPmagPXn1yLd8RGm77HtTYlk0r2Apyv+dytyYffRJDWQ4tBVDM28/VXsh3EjuOzGJTpv7SLlOalRyVLhl8qlx4KKL9ReWaxWAQyalEUyZOcUn1j1fEskIMn1W8cFzqn6KkqFrCMWQlXbrukdDk6p8RsYqmxdUi++6bCTbYp6mqr1QpWq5UPn8/nwZigPIEnhsV2cVp5NXZscezebqk8TW/MwcHzanExHLETtmIwGo3g0aNHvtz5fA5XV1cer2ajoGWnaM7lrZgeTtevGE2x+SQXYvWLrbmHAkt9tPi7JLdJ/Rhrz0Os0Sn+vknIlau0NmntjO2rkftevK1KzCHAMiFLdZSUq0ODdfK1CMSpPLH6SI4mPtC4I0NaSHMnMQxP1S913IVEvwapdsnBZU1zkxOWRWmQxnuK37uOB6vyK8Wh0FfXtepQSzkENOUZ4yx9JOWr69orel0WJ+tc3nZ+aBh0E/m7CmC0HK3Pc4XnFG/TvkCgxoaUgU2iUYrDvtKMQimI0RHDQ52xXZxQqXyW+SFHyUiB5qiS2tei4ND1Atssh6bJZAKz2QwmkwmMRqOg/FwjDtKUkzeGM2dcjkYjqKrKH1M1Go1gOp3C8fGxrxdf62PzpBTO892UYYIaqGJ9Y1kPnXP+63E8fl4yplvp4o5YrUzkVV4W5/uiKPyOVeecd2RQR+w28e6fOEKdYztl8Z07SxEnkHGywxWkxzq4vVM03iBhOd6BGjRKGBc4hHeOM2mHrKeZ9lMBjd253rFrPaJYqFODB5wQzsOowwnpoPgoSqkdSd9GeZD2oQsdno1dqo6U7UKa9kmcTI9GZ4quWBij3QdHZIUUBB8F7AP3HyJwEuh8xngk6bAPE6thkuMVnxanLOUX8TfmoTthaRydO7jjVtsZKxxTTNPQPEE7kjoFc4nUNqyOdVlDtahg9XIV5LGsSzw+ZtewQKxMiVelcmPpbwJia1ifRjqOl5evxUv0SLpDCo8mux7CtpU68lKjj/6nPgxtY4eQcNG2pCd+xOQ2SsOheIRCjt4rtYuFRpTJ+PUSbenUZM7UPJVjm6J9o/F1rm7TZm66Kwb3uwg3OZcj4PyBJ/eknKCSLmO1vSLwuYzPLRouq82BppXGc47txrJWaGNLSq/N60Wx/eAZAU8akCDXNk5ps/CYtr5boY2um8v7XWlEyJEFu0Abe0IXOU8bSzn+hViZEq9beavrumCxw1jiYnzKZUcL8LStnbG3uUi+qQv0ZrNJXlw8HA7FLwZvY+E9NHDjNe4ko/2LR4DSPADNrzOs7ZMj4BdF4Q3HCPTYyLsONzlOcpwwtzV+U8IbB5qG73xEgTRWBseRK+B4Y3hR+N2ICMPhsKFU9y3MxAwX9LemXDu3vz9EO56pLS/k5qOOCo7DMiek+s0518Cf2tV/1+d07D/8mhsVsZgiRBWlNv3LlZrbaiPuaIsZJDBsMBjAdDr1O+k5Pg70Hmhc9+lYkuYO/B0TbmOKYVvIUZyxLfB3WZZwfX0Nzjk4OTmB8XicjbvN+q61H8ZJiq6kXGBf4Bi3KHY5CjAaNTSc3EAZw4ftXpZlw1DCjYf0XVLmfN4Cto4WdIrUAG6wd5Bs/Y2Fd2rQI4k9XgfBXbK+TrgjlR5jXOzxYpzfUUvvjgXZ+aXtgA3uppXSSM9iVx7fIUt36GI/MLoCx3RPjjXsGx4eOF6d28c5OY7vIKQONNHY65ppaDqOh+88xHyc3kYcr7uDBl820jrhH8sAFsYMeVIdGzhpvQSFW3TAbiP2HxjgUKJOWQfgKufTBrxjhRSvkL5vxFE+QXpIv/E0Eq/4NJx/BH4J+pvuiAUQd8M2wqqmE5bS3OBzFsbr59PUDqpFBRe/uYBq1fzw97aByyI3ATl6cpt8fbQrXzO1I3rxGXMyWhwKdOxTfPRJHW9FsT+qN/YhLy9buuYEQdIhtXlJKqtv/tH6Gz/Ek+QK52SnsibXapCjv/UBvO0t+jF91/RkDlJ/x5w2UrxFRrUAlxlTab+Ftwdw/kLA0+Gc2+5w/8lPfgLvvPMO/MEf/IE/Pvfq6gqeP38OAFt++8UvfgFffPGF573YbvgcoHfEYlmpOdwK2phryKHK79Q1TtL8rc2hdIzTj/3xLmqNfrQXcvv1bcJNz9OHhtz1tIudo2192q73fcsJFqdtTpmH7GOLLJUDsbWbQjBS+zCMWyvRtSEtwoeVpkMxOjeeSsZ5XPDatGFfcNPlIfAFD424PE0sD0C67zWly6KAcaUrlb9rW/Yh9Go0agKEtdyc9Kl2uEmesxjeY3kxPVWoYwpzDKwTPVfgaD48Ftei9HEare2es2DSttRokvhNa7NU32h8HDO2cDos41hy6lhBuwukD7638G7XtZy3FzUypfhe4wEpj+Sk1/LFoA+hU3NSUYjxLa5f1qO7MD1tY/qRh3WtkupykyD1N3XgbzYbOD8/h/V67XfKpgxSGGeVi6S5judLrXlSv3PFnM7DKT6XwrV6c3pj64Q0/i3rbS4/7TM3nSrBzlgH3pnUuD+VHDvM39GZWRTE4UmdV470CezTAMD+mGPEGZC7xxM4YotmXGOXbiHgoPTRO2YdRPODI461VHNr3efoTxemo44nnt6RPMDSOWjg8TxNHZikTNHZq5UtlcFxs3+6i5Y/G7gFEJ2hAg2NePLM2qnLw3j/GqZiHOPBTmyFDhmBMZz1JU8j8UUQhu8uDMewwCFKw3AOo/NHraQTjimm7w1cwMrl/MnCgnca5hxUywrKeQnlvARXsnkB0s4T3o5W2TVnDo7JJG3wpeiIrVOpdVgK6yKbxOTdmEOujS5t6UOel6+rdO1uewxlrAyLDtmXrpGClM5FaaU8nMJpkZNiYYcC69hL6clSHg1nrs2rb7DI4Ck9sAv9t1n3uwSWevfVVs65hsOPOgcHgwF85zvfgQ8++AD+8T/+xzCdTgEA4PXr13D//n2Yz+cwn89hNBo1jhO2QMqeRNNIcTQ+17auzVESTV14UaNPo5d+4M1PNWpzHH4bfrLmj9n2UnhzyonlaWtfzAGpbVN+hbZwW3N+DPqiyWI70eL6to0dQnayzEGtPpvok5n7hEPdLZoDUrtIO2UQRqMRPHnyxC9Sl5eXcH5+3sCJu0ZzIKefDjnQuzA31h3hEHRK9I3HY9ExzL9Gii3Md3GMaHBXx3TfkKonOkgwrZTfeu/LIZQIPA4XBWMuLPNyrWPPurBZ8NF4eixVCvrkv1xcKaNCDB+uO5xvtPXoTRxr/KgtgGab0XawGu1wLOGXnPzDpJsCTfkqCvneLwRp/ufGE3yX1ofhcAiTyQRWqxUsFgu4vLyE8XjsxzK2R2xOQuBOXBx7XQ2CtH5WwRdPEZhOp3B+fg7/9b/+Vz93rlYrmEwmUFWVumMkhvvQoCmtmhFU6t8chV4qnxt0JWE+NsZGo5FKK/IDftAj1YvSWbt6uyttt1vNl4uOjd0/OmgxDnfCBu+1AxjIaYq6CO5i3RKAj116ujN29853yUpPxNHY8YphuPNVS0/DSLvTcO9AJp4cGu7bNuaZpfVlYfRdcsyqu2PJe+AEow40EsedXv4pOLx4viAcQicaTdNwvjJeCpxmUhpGj+TgC9qP46T1Z3ikunAcnueKcNd2EK6NceLA93enQoIvUiAVJbUTTSvwEa+zyEcO1P5PPnFHLIYp98NKxxRzfhR5XupPnoeUXW9quPi7CyjnJUB9c06sNqDJENLzUMCNjl3ayipPdNUftPUymMeFOA0PjacyIl41QGVGSfbCPDFDeiyt1uZc7rTIfYfilxynKYbzDxk1eBP1pxzgfMZleJw/UXZu4+DQyk3h0GTgb+Htg9FoBGdnZ54nLi4u/O7MyWQCf/zHfww/+9nP4J/+03/qd8a+fv0anj17Bv/zf/5P+N//+38H+Iqi8E5beudsrt3UOeevdEGwnESVAynbRdcxQMc1ndupw5ufmISnS2F++kRbIL5TPBK0of8m5t1c+yLNl1NG38Blib4ht+0tdu6+oW/nt4V/b1J+kcrvE3/SGSs15G0IQpyOHAfDoUCaOCRhcjKZwPHxcYO2sixhNBoFk25MCbAK8JQ+CZfUn7kTXF9tKwmenAbNgCkZwmm+WHkpXpJoofk4HgnHTU+IXfvrEM7DGOQIC7kCG03TMOK0BM1wjXHWCTzl+IvlRUBHC62jJBBQum5rzmyzGKeE4VQerd5thaY2CoOWvo0wH+OR3LbtYgCztr1FoHbOwWQygaIoYDKZgHMOFotFJ0GrS7tK4VbexfqkjGUIqExSpbKua1gul3B1dQXr9Vr8gKsP3m0LKQOlFl5VlXfOYjtJY0DjoZgR2konx5NqR0zL55EcOmJzr4UWNDqgY5XzVmyO0wDLxae2gz+gcefAoE6Rxs5Y+k/pw3T4Th1OO8dJcHwwOZKY7j71Ts8dPUVBnKnsN5ZD74EFAH/MsC+P3hNL4ihOpCHYXcvbHenDeuLu3f1Wx7BfRc+ZADyZ9yc1EDadUjQ9fac4HMHF+q5Bwy4e27/xm+BBJ2zDycfxUhwKqPG03FhzUtp4nSSc1nDKny3BOQdQA+zZJI5Mddam6qj1A+lb0bFN0tB5IEhD+6GOPGl+B+ExxXx+YTthJXo0nld3NzOanXOwudpAtaigWlXbHbF83pPajIBlPZTmdDr/dtEBb9shpelVVn3jUAZzSVbWdDaEnA8nc+QgfkpKDIckc1rbiOqCFhn9JoDrWjGbCaU9xT+3zfcIMbmwb8ixL3QtB8FiT8oZC33QdGjoOiffNnShX+JnugmIf9xxfHwMx8fHUJYljMdjODk5gbIs4eTkBJ48eQI/+MEP4Fe/+hU8e/YswE9PTbLYdtvU1aLXaXWXaOjT98BtQZrcwcMo3VI6jjeHXgtY1sE2ZXa1Z1EcMd9G23Zoa9eV8nfpi9h62KYNrXKatr5p/KD5SHKBt5tGg6XuKZtrrj3NasONyXkcks7Yu7Iw8Y6RlJxDl2uhSzo+9PHjx/Dd7343CKvrGs7Pz7N2hhRFAePx2HQXYdtFKlV+n8AHmHR3mnMuOCs/hUNLk+IXbSF7U4HzZcp5fxN15l93atBGMEuBZsTW6OP52ihc3GgrLU6a8KAZhCzzEcffh7BjhUPgzsUpHfPK33k7AXTnsxyeti7mEtzW/FQU8hfanDdxDeTORgqY5sGDB3BycgLvvfcerFYr+Nu//dsbNxqlQOMT/sQTMLT5azwe+35HJxstoygKePHiBVxcXMB6vQ6OLLYKw5ri0VUw1iCloEnHWtP/tn1tMTpod8hpZWpjlK8B+E53K2uGBd4f9KQASdGWFLfZbAZlWcJisfBjMNW/MX6h98qmDJ6+zqXzu2OLYrfDdeeM0nbEijtj3d7hAgPwzk/n2PHBOyfMloi940W8M9ZtafHlajtjibPV52XpuEM2wE1xERzBe0GcsswxpPJrbAg4hof+dC4IazjUuLPKNcO5s8v3D3mK8x3NR37THay07+k7L6PRBjyO0OnryduD1LNRVwmnC/8bch6L47TngJfp8MMAgruu66aTVWWTsL+VROo75xf8rfIN++1xsLaUeKbxzNkRS99pOTtatA8PYvUI6Nztur36+ArWr9a7Jk87R/uUSyzrl1a+JLvm4D+0fNWmbm3LoWuvtuZJsg8Nw7UQ11Yr7Vi+ZLNAkOw7dC5J6YL0mdP/ElhPdOoLeL/E6D6UfHoT0EWOtYC0y1qyEdwkdO2v2HjlZVh0n1zQ5qg3kf8o9EU/tg9+LA0g3/e6Xq/hN7/5Ddy/fx9+//d/34f/7Gc/g5/+9KfwxRdf+JMeq6qCZ8+e+Y+Mi6LwxxhTXc1iq8Q0lpO0JJ00F3LG2SHXPzoHxOrR12lYbwu0nSfflPngkGuBJjfw9ZzbQW4CcubxQ66VfdT5Vm53visMnkOHpcOp4YICGpspVFXlj+rj4a9evfLv6KDMOaY4N91NCMLa4KBHqxbF/hhg55w3XFKjNR7RSuluC7H8tC/xjk48RkLCITnBbhsshlkNDjVxSc7JLsB5t0u7x9qJxlkM+xJo/NFlQbEY0zWDRI4Qy9shVm+N71J931U5shhjNLpvSqm1tNtdgpjxhBuUsG6PHj2C4XAI8/kcqqqC9XrdcOQiDIdDGI1GcHR05I9FWq/XsNls/BoQo4PS0wek1sQY/9D1TBtvqEjimoLhWM/NZhPcCW0do7Q8ejwx3vtTlmX2kcB9QOxYa2l+sK4LWrvkzCHS2kHLpvwd6/ccujW+kuQH2o/4zo0PfN3j8z6lnddFWtMkGl3p4OqzK5g+mMLZD8/ADcKdsT492b3K46mDNnCYoJMKcex2qGI8PqN3xtKdrrjDFZgDjNAX3AtLd8IiTdqRxUXonKVlIt6gLRjbNfhd8LqpOz1JWICHOap8HSSHGqYDIR7xkv6UnJ5SnN8Ji+ko/QSfxaHaGEO8LSjPMZpUJ6Vr1rvh2KN0k/cGnTnLTKGn921W73k6KKeBKrIOcDo5jhTvSP1DnZy8rdlv05Ptjk05Y4P8QGgBJ9MY4f+A1trB5mIDm6sN1Mt6n6cIccYcdxRuSmbjczgtm8pIEq2SjGA1GEuyfM46p+G7CYjpPTwcZVOUmUajkfkahVz7Cect7vjAj/BQbraUw2UQSR+msqlkNO2Tt6l+QN+p/CLxlEU+uWuQo3vk1k3Sr+jvHP0kF2J81sX4r/VxLJ63V+6Yk8r4JoCVP3gczkNXV1cwmUz88cIU6PV7aJcdDAZwcnLi8X344Yfwj/7RPwIAgOVyCV9//TWs19sPoPCKHtT1KQ2p/uXzXc5ckeLdHN7QnJ68DIqTrtf041xNH9bouss83Oe4jK1LqTmIx6f6PWVX0OarVJz2nqIpB6wyayyfBpa6afE59pw2baHxRy6f5PJs3+lv3Bl7W8L4bcKDBw/g+9//fhCGO2P5UYSr1Qo+/fRT/z6ZTAJn5G1CG4aNAf3iAo2O3Bk7GAwCQYAblWMTaC4tFCjf0GMWY4D3JsSgy6RzE3BX6UpBDm9qC6I0qWs4UamUjo5MlS0JyM6FX1hLc5f0fhPzqUQXjs8+gdenS91oO3PljTssuhiXcuBNGlucTzmP0zkYDVi4u+B73/sezGYz+Oyzz+D6+hoWi0UwtyN+NM6MRiM4OTmBqqrg0aNHMJ/PxbXxrs+deJ8zrmUA4XihbUqdsfSuLHqXDgA01v6UskDfR6MRjMdjGI/HsFwuex+vHCyKD1eieRzNp82/ljaQ5pI2RjMNFw3n9YkZQDTjsDTekI/oDlzNSKHtzuFtyo2jPK9U73pdw/kvz2H2ZAanH52CG+4dSd4xuXN8OLff2erc1gHCd8pSp5t3ejrSptRhCxA4V6Q7Y7dJ5B2xwbHEO6Ta3bK+P6T8BeGDXfn0N3XQAsDWweb2+CRoONDCyGZa14yP7YblTinTjlhg7+QZC+M4aLhPy3A3yuf157TCHofWTo26MRy0HYInhOl9WSSfCDuHa1GQXa9CvEQj7tAM0iog8aevl4Bbeg/aOJdneB/TttrVQXxy3qFOV9jVnxxZ7NuD0SU5YX19YnXBNKSMxVcLWH65NK8Ht21HiDkueDqrAaovOfsuQEwv4uuqJCdxeW00GsF0OoX1eh2cTKLh0eQZK+2I++joCIbDIdR1DavVKpCbUc+keqdGCw/DkzC4/HfIfuc6AtKf2nl8aBn1LkAOr6T4rK0h3QIpfbyrfq7RleNA+Rb6B+z3sizh5cuXcHZ2Jjpjy7L0pxZuNhvYbDYwGo3gwYMHPs3v//7v+zF9cXEB//f//t/AGTmdTsE5J17JowH/IEDauUd5V/ut4cZ0KZBO/UrZ6Oh8DwCqLdtS/l23h9w23JR9VIMce/Qh+/BNl+8A7lYdtP6y2P5jcONevpgwIRmtYkY7C/TlveZ04F2vPA39yieGdzAYwIMHDxpp+d2yaKymUFVV9OheCe4CM9PFI3ZkDrahdmceVzSs5eYAfr2FZdy7dy945zjruob5fB4IGpjupto+ZhBOQUyJtwrhNykU3IRzjDv0qOFdm59ynCU8v4ZLM8DjOOL8mHJ25ILkKJCOH+0b2vax5uyW0uQquNb+fdOBKw0IkrEEdxLgbs6zszM4OzuD1WoFk8nEn/RAjUqoyKxWKxiPxwAA3ikLAP7u2PV67Q1QbQSgGPSllEl4pDFLv4LFdwrolMX2QTwxOiXexQ+4jo6OYLPZwGq1ylJ2+wKJbn7XtZa2KIpgF0VVVdG2SBmOpHm2zZi1KE4xOmPO0dFoFDjmcV7HUzk0HLRciRaLLC3R1egT3MFaQ+Ckog4af2QxhE5ZvjM2KAdYm1LnFzq7uBOWOkKRNtCPHA52s9J0WEfeNAWhjZWB7RXwEaWlCJ3JgQOx2LelWC7Pw+MF51TMYcXjojtiMY0TnpQWFobpGvhRVmJhQX6GN+h7gIBOHs9p8HTw9lPasFEuhPTTOtE6NoA6XMmQoUdiUxifjuHonSOYPpianbEcbswZK/UzQNjnEr+wPmk4ZvmdsXRHLKWH0B3QwtKI9aD01ACbiw2sXq1gc7FpzKcxGTsFMVkhJUfgPMsNsVwu7UPfyVk3eB6rkzcFvJ2xnvz0L1zzUmt0SiZIheE7l7dwZ+xwOPSyBwd0clL+5LzKP2TVnMFFUXjDPJ6cIulXqTWeOl4xDz3SE+NoXWkZh9Lhqa2M2kgof8XGCNJq4dW7AF3075g+YZFdrX3IbRq8/C5jn/OWNO40Xs7px1R7SHXT7AOSredb2H9kTB2HVVXB3//933s76ePHj+Hhw4d+jsGPgNfrdVT/QL3n6OgI1ut1Q0+V+MG6zvaxpsfy0bkU80h2JUpHil+LomicwBjTlb/pPGqRD6xA+8aqv1tosczVljmvrzIRl9Y2XdapHODjB3FKdhzrPEDDDykrSLSk1owULyadsTEG6EP4oY2co3z0qZykypHSDQaDxk7IqqrMi/hgMIDT09PGroejo6NGOmlnrGWxkKCPdtP6LEYbTRubCJxzXhHTdqtyBSdWFk8nDVRpYNEdStPpNOgrvssLYKs84rGcAPL9CjkG/j7AqszdVehT6EiNdSmehtGjIiVjiQYWgTC1yFj6TuPvtooFz8+NFW36I1Vnq4KZu+50WSBTcJcMAH2ApHBoSgYCOs4QTk5O4P79+15RowYnHEM4ntbrNSyXS6+UHR8fQ13XMJ1O/ZG9fd9/Igl70nsOb0j5OG5+JBG+Uwclxqd2Q2phRbG9V34ymcDx8TFcXV3B9fV19o6DNoJ6jmKUGs+4xlKDYmwu5Xyq1YMbXqzAjTVdQZObcK51bu+Qx2MLLeuApPhr5aZ4ndLggd39CA6CnbHo/CiKnYOycOLOWL+LlKTB98azAO9oEe+MZb/5XbHoLPNHHAtHGO8bZV8utiGvC3XUBkcTC/3TOK7Y4UPpQ61rSZ2jTihavmO/qZNt99vLC9wJycoJZHdoytC8TO4MbeyWxTyUFiC00DZy0ORPgcYgjpYbq5tr5mu0M77SehUgl22A8fEYzj462/JOncbHd1ZLu4OjO4YV3gjySu+cf/hv8pTC/DxBjyjGY4nZO9QJPiZxAS/wMPruwnI3lxtYfL4Q9UqLocciD7RdG/jaFVsruzgLuugBGlh18FgcypAoF6Y+vkqBtf+kuRvXYPyXaJHkCnym+k4Kp+VwfJQ+KS+XfyQaqFwR+5D2UE4olOV4HfmpHLQeSBc/XvlNAt5/muyagysXYgb7lN2Dh2n0anwj5UvVwzLnaHm6gKZDWMrpwzFxV3kbnbH8XtdPPvkEFosFAGx3vX7/+9/3doDj42PvYEW7uLTu4tUr+PFLzkfDMR0y1p4p/tL0rNTYTdkptDVEst3xsmPHIafgkLx1l/lWgi4Os7tQTysNFnlWg5y0XdpE4nOUCbr4K5AnY+uRddxYgY9rae5I4Wu1M/ZNG4BdQKun5JDTgKblSgfC06dP4c///M/hxYsXSXpSAgPCIYRqpKFrfr64FEXhF/w+6NYM7pJQwNPgu0bH48eP4cmTJ0HYYrGAr7/+OtixTJ3osSOCDtFPh+r7byJQoRHfKUjj4dAKFpaLY4nSICnbViWbK/O4OI5GI//lunPbEwDwTqM+1oI2CtAhyvoW0oDth0oUN6is12tYr9dwdHTU+LiI8xY6bJ8+fQqj0QhWqxUsl0t1N8Kh6iMZpKQdBKlxFJvXuUNNWnPQYMZxBI4hApJCWpal/0KZ4rztNUEyFGI4QMgbSDfKSuiQlIDyk7WfcmnW3jn9KRwpAwL9xzDccZ6z/sT4jEPqXmZPPzpPdv/eGTkQHK31Lrwu/JG9ND7oM0wDANQp5J/1jrbB3ikjHU8M0HTIBvVIHGkMvkj5COPYb8xH3wEgOK6Y06GCgwbt3LkmOWY9X1DnFXNWBWmwT9EZ6sgTlHAHjTCKh6eh8Tx9o26u+R/wOiu7UU/ET9uGpPNxSp0buFh9Gv3QwiHry8WjfMkHAtE8NuTiu7SLFCDkBXxqPCTyjfVJd8UmdsJKa58Up9VBKntzuYH5F/PtHbGpJuxg22iz5tD1SlsnaNqu8vahnG1toSi2u1DruobFYuHrhjpH33ILN4DTcIwry9KfJkI/vuV5NKe+VR9EvHgaBsrCKHvSncIaSMZFKu9xw6TG35zv+taRTk5OYDKZ+HbFk2/KsmxcxcD1WSr7DgYDGI/Hvo1uUk/IBYucdpsQk0Ux3uKstRr+MZ0kb8Zk6JTTLFamVk4sHR8DN8Ffd403KKzXa7i4uIDNZgODwQBmsxmMx2P4i7/4C2/jfPToEbx48cKP4+l0CuPxGP7P//k/8PTpU/ijP/oj9bo9nKekDTgYTyHVh9a2jM3XFuBrKZ2LUmt17OPanPpaeFOyHfSpH38TQLO/9IFXgpx58NCAPisOfW+SsNAhveeOF5QvDjGva/4lC+90OqY4VpnUghxLa11YU3GHXEQlh6J07CA3WKPywfNeXV3Br371qyRjSZO8JEBTITw1kbRpp65tyw34RdH8atJShlQvy6SFuFN3mCDQXTonJyfw+PFjj6eqqobixPmD1qeL845DTl2t6W8CbnKsSuXllJmaSC0KLE2TM9/lQmyRsSoXfOzReUzCoQmdtyk8fAuHB+3UBrzXm9+pEhjnd4BH7r9+/dob3/CuLjru+hojkvFKeuc0x9Kk5pYcobEP3kWllrZhytDbR9nW+lnnP1QCrGs04uqiJKQEfk6/xk+cZzCfFM7z8TGScpbGwKIESHN5zGiNxxSjw4MfQQwOgl2k24zg0/i0iK/Y4eA7ZIt9vKePHIMc5KOOVo7Lk7CrE+zzOLc/0hjLNd0Zi2XuaMFwANjjQnp5e+d48FjSYKzEHFK0LEfSszTirlGaFvuTOTFVp+gOp48jdGs7TsV6Bpkj8Y6lldLTd0J3sMNXSSM6AjuCg51TsoDwPmPD8G7skpXo4kEKnzTCWP/S+KCvsf0wL+5qrUndaDrNGas4YSV+knhF5Hvah7WDuqyhWlSw+npldmZanRCNNjTEpfBK6yNdK6x6U44NJZe3reuQVR+iH6DxcPpBnEV/tvYR7+NgznQu+DBck+U0nBwvx63RhrvCqG2CgqbbSc4mbk/ha7okWx9KBy+K7Wkts9nMl7tcLhunncRkZnynd+gCND88vg3Q+IjGc3nvkDSnDM2ch2PjKVd+zImT+DZWtlWfiJXTJU1bsNiHcvAgWObxNmXyPFVV+d3p+DHEaDSCL774ws9ZL168gMlk4vU1TPOrX/0Knj9/Dj/72c/g9PRUrFNq/uHzQoqnYnO0hFuz01qAlsXnIsk2ElsLKK5U3+b0a0y/vu25syu0kWVybOYW242FxyQaUnProew0tHxrWq3cmLzVN3/l2N+Qhj7Sdxl7OdDrnbFv+sDuGz788EP4l//yX8Lp6Wnj0nK8Jw+BH3mMwJ16uRAbSG3hEP1c13VwNLBWTpuyJaUJQTpu2rn9bhQAgNlsBt///vd9P9y/f99PNlVVwZ//+Z/Dy5cvYblcBnhSSvY3HXIWUquCkVu+pAAi4Ndrli+UAWwLkGZU6CJIawaDvsY8HjNDnSKHOD62D+BtajG+3WXQBPObrJPEsymlZbPZwO9+9zu4uLgIjE0S71dVBV988YUPR9zOueAjmK6gjYuUY6wrcIMT9h/dwaDdPSMZIzC9ZvhDY+J8PlfvsE8BN1z31RZtgLZTWyO5NtdqvMDT8D6xGqtiNHMeSCmAEo4YWGiUxmPMWOdKB/WwhqIO26Koye5ZF+6UBWRtdK6ig2YAPt4VOxwQhheDwscjDu9wqbfvfmdtAf4/2G3ri48faRx7NvK7Ijz2mDkPfRtGHK/abkhp1yt/bzhZIXRO8fhgJ6gURuKCd+Wp/q4JfsrXDuQywIm0NP4BGrhSzlNeBs8jvUv0ogMx0pUm8HTUAK7a8ivnj+SYZnwmJGiWx+IoD+CzwS+UT5wL8yZ4Q3TKCseb87pweoP5z5Oj8D3+3pVZV1sn7OXvLqFe5R3R3xb6WB8tzqXY+iDN53cR0GCPO2K/853vwO/93u95Y/4vfvEL+PLLL2E2m0FRFMGJU1ZIraOaLsZ3qSK9MTm8KMLdI6gb8XtvKS38t0W/5bIH6mYoS0inoEjyp9QWfQKXIVBmxX6/f/8+bDYbrx/w8rFuHGdZlrBer/1JTXcVNBkeQOaBnPGaa0yO4dH40VJ2Kn1K9u1aZw0Hl92pnYLqWRbZvSvcls7EaeiyHvC8y+USiqKAk5OTYEf7z3/+c3j06BH84Ac/8Gl//OMfw4cffgj/63/9L5jP5+Cc807Zuq7h9evXSdp5G1J66Dyr5aVjTGoH7To8AGhclUPXIT7np+rB4a6s0XeZNoCb+YBFg5itoCvcpTbuGyR5JZauTVhXsKxlMVlJy9OWXwNpJidz2wm+z4GfoqGtg4aXwUHa5iwdsXB0dATvvfdeo9Mx/2q18jg0ZUMTVHOFKA33IfJY+1hqw65gNVBqeaT2xqM5cOEfj8fBsdMXFxfw6tWrAB/dCd1VyOV1kpxgfbTdTYJUX63v2k5ufS3euQrITdDEcWptFFvYYrRICjxdhG5SMLIoXBrE2iYFXda4vh2IqbA2INGptZU2RqV+2Gw2sFgs4OrqCubzuU/HcdCy1ut1EI9rpObwt86psblFckBZ8FvmKauhIub40sqkabXxjooj3x1B6yYpqbzN0LhXFM2dFrkQo1cK4/NOCrcEEs9ZaOe08vaR6pLTNhIdKYUgZVSI4ZLGAudBTnvjN3XMEOeItDO24eyCMAydKsEOWQC/WxUc7B2vxd5xieXtEm/TFk3nKgCEO2R3aRtHFfMdslI41h1IfoqX0cTro+14FB21EgsEPinXaNOGE4u8ez7geShPSX0klctoDn4zfSRwKFMaXZhWKovj5Ls1pXryMsQ0WpkOQnpjgH3ZQfxx4MJ2p/zdVq6K8I3W3mr7YR6t79g8wMP8nM0csMGT0yjxLKuXmI/RUa0qcNXWEVzOSyivyq3jW4GuspS0BqRwSvN+bF1MySJWo5BWRo7s0ydQWWUymcC7774Lk8kExuMx/OY3v/GOTC4DpmR/qyEwJtPSfoy1jyTHUTo0eSG2PufUFdPEPoyN6T83ZejGI4XxYwPp1BykR2pfjEM83BGitf9NQ46+Zm3zNnKzBSfFrfGe1q5d+CWXdqs8zfOn5OScfrH0a2qM9w2WtUab+7sA/QAEP57Bo4yn0yksl0t/VQ7A1l56fn4Ol5eXgR2V28w1PZTXR2p3mt4y5+fEazTxqwqlK4batLuF33PqGBsHNO6u25BjOjdN0wVy+EOyY3Utp23fW2S4PuaAQ7Rvro3TKq/mrlMp+8uheetOfVpmWVwoxBbTQwlik8kEjo6OGsoSnqlvndA2mw384he/8BegP3v2TOwsvGODQl3XjTCNoW9DIJXoaBMvGaBzwJLeufALTGxvvqgul0u/8N6/f99/3bXZbBoLMuePrjTTr3KHwyFMp9MG/sVi0err4RQcin9QyeZfv6bytOGB2GSdMnpw/ui6oFl4vQ9o6yzhIAk/q9XKC+SHgNz5myuNnE9wfObwGkD7PumjXfpaw2J42jh06G86Noqi8CcM/O53v4PRaBS0N441abwVReFPiuhLeRyPx15JTOGiBgmsU5fyrUIdfadH8uWUIwmmaLRaLpfmNVCSFXD3gXPOyxx98CU1UOI70kFpKsvSpOhY1oWYcZWni5UDsP8YKjafWA1olO+kuYumi+2G5h8ISkojhqHRAHeqTKfThrFUAucc1OsaikEBgxExpA7c1im5O3rVua0jhu5w9WmLPc+7AflAw20dobhj1t87S3fEknwA4O+S9Ue+FjsnarHfKet3yGJa5rXyDttC2BHrXMMh3Ngl6/a0bSu4T7t93TuZosfQamzHwx3IjkMXluX7UAp3LB7DwIXvyEs1ezriYNsdNxvk2fU5hktlUhqDJ8UBjDZGL/8dOAJJGA1v/NchfcHvmoVHu8+4XuzarC7rJp8o/BG9Y9g3i2uENdKw9hcdshLfcJ7AJJwveHvVrO0oiTGjGq+TY+ES7zoHrnJw+dtL2FxutnPejna+vh8KKB+nZC6p/rn05RgBLWlSulCfgHoEljObzeDdd9+Fx48fw4MHD+Cv//qvPU25jraY81ajRcNPy8G1kZ5yRuNRn6VpKHAdBcNw7eY2HUyL6SXnJaeRyxJSn0qOW/rRXV9A+Zl+mIk0o/zB9QrtN60PXoUyHo8D3UFqQ422Q9vGJB7qS7+xlJ2SZdGZhB9tSkD7UBuLEm4LcH3Imp6+S22s6UQWelP9kprX29ioUqDRf5u2XeccXF1dwXA4hHv37vnwFy9ewMXFRYO26XQKp6en/q5ogK1+ef/+fZ9ms9kER7XjbwvPaTtku6wTqfY9Pj729BfF9sRLtDWtViv49NNP/bgajUbBaZg3edc16ocI/EOY6XQKg8EALi8vTTaym5g7NbhJGeVtgrvSVpqNy8JPfc2DEo8fon1y6bpTzlhrh6TytnX+WdNypck55w1ZFNbrNZyfn6u0zedzfzRvbGcsvTdFKj9m+Lsrg1ACi3KlhfP219JaFVVsQ8kYWVUVrFYrv4BRIzE6IabTqX/nAmzb41xpH1KauJOD1jOm7Eu4Y+Va0vJ4y/iMvcfolHDnOpQwz3A4VJXmFI4+BIJDCTOSIqKlyVGCpHnoUJDiFal8ulCneN9argUO1RZ98UcOHouww39LeZbLZWOutMwhVmNbCrjhJgV0/pcMopIRTcNjpU/C17bOEt+3Mepaw9sAN1ZIfMTLxDknprhqPEgh1c7S3MHTSIZDjkOiTaNZo1Gb2/qeZ2h/4IcTnP95m7jSweZiA1ADDGfDraOD7oYF2P8uIAwndUHnCb3/1TniCHUyP9D8uOM06DvFmcrvfMWyAEC8O5bviMW6iLtsMQ4Ij6EjCyBwuFn9dWJaipPFe96QnFXUoUYdbxiPfUvS8TQUp+cP1yyX4uT4Gmm1J60Tp4PiI5EiHRyHUq8Gfpav13HnoMH/gROWFEUdsDmO3iC90LZSP2g7Yul74ESgfepAd8aSNI3ypXcr/QKt1byCalVBtdg+qc6FsoA0j2oyh0UWyeUNLne3Xa8tkMJn1ce6lplqO3ziNUV1XcN4PIbJZAKTyaRxb2swh2fosFoaTkcOcDkuR/7DPFROleRCdPDm2MakNZzGx+g5FHC7Ci9T0zt4u06nU3jw4AGUZQllWfpTyubzeeNI6BjclDNB0kPa6Dm8HXLnLek35TEq+/VhO+xrzsQ8msxu1aF4vXJthbxdc+eXVDnWebJNebEycvuD8yCewITztnP7D7TpHHR8fAzD4RDKsvR2JH7UOIZb7JsabVrb5urmUp0luwA9IRFPdUBo84F1W9BsFgj8Q14aj85ZvI5N+yCX9/1NzaEcYv2ZM7ZiebR0h65zF3lEwtM3HApvjq1Oy5+yXSLkbsrR8LRJG2u/O+WMfROgLEu/mxUBFyUOr169gp///OdBWFEUcHp6CqPRKGuijn1xqQkrWN43BTij97UQbjYb+Prrr31b3r9/P3BMvfPOOz5utVrB8+fPA4EDF2iLYkQBF/rlchnUhX5hiseE8DwxBeAmgU+SRdHtDuQ+YDQawb1792CxWMDl5aUP51+MATQFvBwnz5sKXGijc89dqXuOgG2ZB+5KvW4TuICuxdN0dV37e1XwgyTcQYkfGEl3LnOhs6/5aTgcNuZEip8q8PRJ/3l9DwkxpSImYMYcg30AKmXYPnQ3SBecCCnlEWB/1DLyk1bnmKGKh2kGcYkPsf3RIQwQHkVFceQo+5QGHDdWwN0M1HAmGaMkxZmWi/lxZ2xVVX4nuTZfltclXP/yGmYfzGB8f7x1ShZuvyOW7ZQrBvvji/1doui8dG5/V+yg2N8hu7sHdltZCO6bpfkBSFp8Sv88Dd8h60ha/u70Z4ADINxJS8LQiWsGLanm0KIOLOK80pywAZ+zeAzzaeowredz1wzju2Z5ukZZ/MnTA8lDyyL9RtPSMiX6eJ2DdNJ9puw9Bxp3CJO+cvV2B6crXfNOYzo/5hTKkwbswdoTCP+k+IXygtK2Dacsz8PrHxIn08niOb0BLfWW964/u4bl10t/JDE1PuJ6iYZivtbfpEzP5+A2kFrrukIfzhiElAETy7i6uoKPP/4YJpMJ3Lt3D46Pj+HRo0fw4sWLxulTXH6T6G/r5KL5ubwSw4lrJtUh6TiI2WMQN67rKA/gnbo0XayuEl9T4Mdo0jR99jnFo8lI+Ju3FQXUK+hGhXfffRf+8A//EC4uLuDly5c+729+8xuYz+cwm80AoJ3B9Sah6/jvyzgMsNeZNptNQJOmQx1iztE+9KZ1lXgpx4jPZQGtHik73ZtiK7A6jDnE+leaW3GH7OnpqY/jDr3ZbAbD4RBms5naB+i8XCwW4g53jVaKL3bscZt+43qvpP9h2dPp1G/KAYDA0Yx6JE1/SKC6Mt6Bjvd1S4B+iaqq/IctFCx32X8L7eFNaNvUvHCbdNC1AX/jusbztuFli92pKwTO2FxnkQVSC94hyjw0xIQhKsCUZekFRoSiKOD6+roh6OCCJgkjXdpFE75vG2IKpcYTuQqD1UirtTHeFTudTqN41ut1Y4czd1hI5WrvXIiQ4NGjR/Dhhx/Cixcvgvtq+S6iXOW/DX9I5cXw8t99jXuJP8bjcaM8dA7dxljoy5lxaJCcZzcpTPKyJWjbHjmKHOftuzqfHhK0sYttQZ30WloKmjLWxcBABSxNgafPFD5L2bnzqZY+hkfawW8xoMWUbK4YSnRg31AjSd/Cp9RfUhopnoZJaayKQ6o+1DAbkxU02mLxmhHYgl8zCNB4yahM00l1jMnlrnawXq5htB6Bq932CODdUcPSfbEYHrQHjYe9wyu4d7YI0/gdhI44OHeeGb9DFtMVDEexo7+Axq5Wvpt1h3D/jo5jviOW9BE/ZtbfHQuwd7a5fTozNPxYzFnlhHAXf8e29vlJPMZhuwbzpSM85+QwipP/9jjBiTQ26uSa6QLnOeOxRlvxMEwHRN4nvCc6eOm/EaIOVAdQjAqYHE1gOBv6DxgCaCtKSMUq/AFAxr/0TtudtlvdbCP/FJz2Ih0CreJcl6KzBqiWFawv1p7OalkFx6Fr86IUznWklIzQxxqYg6OtjaQNnX075VKySlFsHeWLxQK++OILGI1G/v7BlO6fK7+naKTvsTaPybh8bU7Rq+nqAOA/QpM+gk21q7X/UnaItoB1l2QaSXahaWK2mLOzM3jvvffg3r178PDhQ3Bu67z+7LPP4PLy0uv21A5ymzpajAbLWGtjK5HmOU0O5LqSJmvz8jkdklM9V1+26jHWvDxe6wuJ5i66Tpsx2aYsbRxJv9vgy11r6JVF9D5YfOc2b41H0YlJd9FK9ZHeufOFQ6xvYnMfjaPX1OCxy0VRwPn5ud+pj9cjSXXsU4em9HK9no9rehUN1//p+OXX9HF8tw1d26/ruOZ4JJ25Szlt6bHAbfdrbB2kYF0TY3HSmgewtalJ91WnyrVA7vpGofPO2JThqe98dxmKogi+PFmv1/DFF1800kmeeTwmgAKmuesXa3cBbxxhC6F10PZVPv6mZY9GI3j06FF0gXdue3fC69ev/Tv/AjV1TrrEC6ndoz/84Q/hj//4j+FP//RP4S//8i89Hn7uP98pe+gFAkEb37xv+xC2ucBD23s2mwU7k29izsldXO464Bh50+chPgb6XoPepjUtZcig8yZVuvi9VhYlOVaOBTBvVVWNL+LxHhf+pW1qPskxIuTSyfkkpqDh/I0KUqq9pLm17dzT98cX2viwGCmoUY+OXymv1HeWdJiWv9N1QzpuL4UjBrxPeZ/xMDR64FiLGRc47bzeNI6m1eivqgrKstw6YysHbrB1yOIO1+DOWAd+R2xRkONYHYQ7Yp3bv5P7XfG+Wb/jFCB0yroCYLBz8pCdsQGOAvxOWH//LNl5G9vByHe4Uuevc9u68PwNfI6MwRyvHtIgvEtOLd/3KSeWC9e+wHGmxZGn5oht/AYn5tUcqf4JzbRiGCmP0ivWRylbdeoS/FgXDwWhKRMcOBiNR3D8/jEUgwLqKrwzFtNIDtlOd8Za+YS1NecFsb94H9N6cDqA4FfqkKSz3o/39es1XPzmAgCgMYfRuTCmQ0tr5aGMpZSWLrLEm6xHSIAG9NevX8PLly/h888/hwcPHvgjizFN6r7UPtpFMqpKMoa2w7QoCn9kJz+dxWKkwzUbd4jhB95YJr/3j+eX8HWFLjoN7Tcqu/H25eVJdOPdlE+ePIEf//jHgTy2Xq/hF7/4BTx//hxmsxkURQHL5bIVzX0C1tWiB1nA0g8WHYGmrarK70CO2d364qk+bSOSrKzhs57s87bo8LfhYMH5Co9Wp20+m83U+YvDZDKB0WgUzH9amTyMH3vc1w55rjMNBgPYbDbw+vVrX89Xr15BURR+d2zMXta3nEHXB01vd86J1yFa5krtSOnbAk3fPRTk2Knf1PnjJmiXfCnaOLDYZ9oAHqeOH0xYypXapm9ZvJUz1mpItEDfQsqhmYkq6hTortaYUEDzopMIjWtcybDW5aYcl31ByhCeyqfllxRqqS94GfyOAlx0iqKAq6srv8gNh0MYj8fw5Zdfwnq99nn5sdWpekhpqXDCj0X84IMPPK/MZjN48uQJfPjhhz6v5GRyzvkjDSnem1pELU4cfE9BSsGgYfyLPPqRAxfW8FgeFGRiRyZp0GWMHgIkXrDQYU0XK/MmoA+DFgeqMGu8Zlnn7vKcmwupuvIxTL/elwygVqFHA8mAg31SVRUcHx/DvXv3gjx1XcOrV6+gLMvgXqTc8mJhsbzU+JQy8PF6IeD8jfVEWSFGX5exS4VO7FeNthyItXts7tGM6xoeyZlqVTKkNPhFN66/KMRLd8hqdMaMj5xO/s5xYp9IBmFaFjeucZ6k9UOlBHeTSDJTkM/B1hk7dP6YUOpopQ4tf/8qu1vWua1jBh2sFLd/kt/UKRukwXoLjtVg1ypJ03CukjDtGZRREMewUs7uh9xHli2QDhr14U427vTi7RP0F3W07X5LO0LpTlkfJjx5WBCONND+3v0QnYYkrZdboInHOeYcZXUNaKH1AJnWWF1Ep2Eb4GXtjvLdXyOcnldNzl+ehDZzhC8a79h2NYlTeMDXT3OyUtwJeoN0nC7ihC2XJSy+WkB5Xfq5CmV4zCOtGdIH0FqbS/lj623fRhkNLOXkODskOYWnja0xVlqk9ZyXhf23Wq3g1atXXo/jJ3NoeCm9kixqoZPiSKXDtRVpPD09hdPTUyjLEqqqgouLCyjL0ju6Uripzo6ncd27dw9ev34Nz58/F9OncGpxN6nb8bHDbTQWJxm2sXMOvv76a/izP/szePLkCbz//vtwdHQER0dHcHx8DLPZrLEjTZO1b1NX02Q9KR2P1/jar5vsKeXR9AdeBpdBY7glXJLOINUjppdp85LFTiiBxosSbRpOXjet/25yXUjxs6W9JL5sSw/FJ/Ud16e445WOeY0e7d1qe0y1SZs1n9Z9NBoFc5yGq0+gu3apDfnhw4e+vLOzM3j48KGn4cGDB8HxygAAL1++hF/+8pewXq8bpz3eFF/ngEZb1zbWeMwqI2jQRxv23Q+x8XUX+5yCtobQa9Q45MjKOXSk4rQ0t3ZnbN8TURfhWwqP4eHHxlBlEN8ti+NkMgGA/RdFvEzuHNKEKE77XYXYBGaZuCRhkeOxAE9HF0s+cK+urvzvyWQCR0dH8PTpU3j69KkPx6Mqcsul4bTc5XIZ3Lf4wQcfwNnZGQAAPHz4EP7JP/kn0TsdsU4cLz1+4jYn19iXW7G0Eki8QOtInbGDwcCPOYD9F2F8Vzo/6lmDmPB+CLDMK1Y8AHl0cyE2lbctrVI+Tdmx4ML8mpDE43jfH/puzhj01d85eK3zQ8xop41Zi1EwRyHEHQRlWcLR0RF89NFHQXxZlvD69Wsoy9LfVaN9KZsz3mMQ49UcGQXbgn4IxI0AVqODVKY2HiyGLI1eSxqN92JzkjTfaMYmy9xm4S9s36qqYDQawdHRkTcQ4PFZ/B47jjsGGg2p9ucGCm3MUNlRkhVoPvywDHFLvMXLrKsaiqrwDll/72sB23cotk6UQeiI9XgcNHfIAvijjzEed8hinuCYYnwHaDhQMYyDlCbHEas5eP1dshGnsG8/q6ePJWvwlmPh/B1/O+U3CRPTkf/AgUv70hE+ldJq+Nh4kXbAik5YHq7hJjwTdb5CvC4eCvbeBhxsP2Io2BxYhDyBR2+ncMXCGw7SGK+QtuJ8EXXESmVm0BrgcUI4L79yUC5KuPz4EsDt9WPNqE6Bf3AbmxNTDo8uYFlTefltyu0qNx7K0EfXF04j7oaaz+fqmm+pl1VuScnAsTzUCVNVFRwdHcHjx4/96RHz+RzW67U/Nleii6/7yKPT6RQePHgAP/rRj+CTTz4JnLESb2qynVYPyY5ySF02ZrehPK7JhdwZ+/XXX8Mf/MEfwOnpKRwdHcF0OoXj42M4OjqC6+vr4L5oaxv0pWu1sUGl+C0mr1KekuarlB4g4eI7urHttXFlKcOi+8TGhlSOVraEh4/bGKTuFKT6LZWzNZ1Eotc6RiU4pO28rQOG66UxHY/fXbparWCxWAS2SonfKF5tDU/RSOnMaUfeLpb5Ep2xuXZXXk9rPqxTUexPaADYricPHz70+up3v/td+PGPf+zzfP/73/cfsmN5v/3tb+Hjjz+Gq6srb4vuS/5py79ty9fGYJsypHgLL8TGw01CW5vvocpuU0asD2g59Nh0ritwWd9CS5v5MCf/wZ2xbQ3pOfjvIiADSIZ/vnB9C3mAAmNu31sGH18s+B3Al5eXYp6YwBwDFEC0I42Kogguoj45OYHvfe97/v3jjz+Gzz//HAC2QuL19XX0eI/bhkMYOGJAdxmjYrfZbOD8/Nwboa2g9WvsTp8+4SbnOqk+OQp7W1pz81mVFko7NZBsNhsoyxK+//3vw4MHD+Dq6sofabtareDLL78E5xyMRqNWF7/n1kVzkvUJbfBqCjc36MQUcbo7NaVYx4R2nC+n0ymUZQmr1Qo++ugj+Lf/9t/C0dERTCYT+A//4T/A3/3d38FqtYLBYABnZ2fg3PZI+UOdEKApeNrcYFESpHZIGSowjDtwNZp5Pk0pb2O81NLnGL3byEk5Sh/FT2lDA+lwOPTHbOERXJhPorcNcBo0oDtiqRykGWdxzdPAOfnoqhidm/MNvP7lazj64AiOPzje3hu72ylLjyl2bn9nLDpXCyi8g9XviCXHDONRwpgOHbv+/lXsUnSOOdji88HpHbJhA+zLDt7Jb48T43g4wP5OWepUE8LMIGWhTi8Xhgdh7N3zg4OQx2i8ECc9tbAGDgjTqU49+oRm+RYnrFYXjl9z9Kq/Td0k7NaV0u12kNfVVlb0/EzAzyd98IvEKwI/BLwi8YfGW1KZMfJ4e/JXiaYawFUOrj67gnpdw9npGdwb34Mf/v9/CK9evYJPPvlELEuSVWJriGWd6GJE7AJdbAXUMJ5rBL4NcM75431R1rbo+Zr80IehT2s7XgbqFbPZDADA6xhIv3RNBnUa0tOZ6K6m6XQK9+/fh9VqBZvNRrQhcTo47VxGp+W2bScrcP5N3RtJAU+BQVvHxcWFp/vjjz+Gqqrg5OQkOK2Mn+5hhT74vg0Ore15O8XqlGtERrmWG6bpmOPpNX6TyrCkaTs2+7QrWPQvKY7mk2w/38IWaLvg2ATYf0SN8YvFwttheD46d1nXaYDtqZNos6Ef0XZZG7SyOHD7q8ZnsXWFprMAbR/eVqPRCN59912/4eTRo0dw//59H39+fg6Xl5fw5MmTYLMKwHYDUlFsnbtUT+SO8xw6Kdy2zNEnxOTLt6meKYjZAHOgr/Yqy7Jh9z00SHJjSg5vOGPbTFKpha6tkJLKZ52gc8vtKy2nL9eBKAkz35QBnYIcYy6m71pOWZai4ZLvprXyJVXWYvEAoZDxzjvveAXl4cOH8ODBA0+fdnyhFWLKUSw8l6+1yaot/hitVABD4/Pl5WUwnlKKAu9TLtTxsSkpx7cBsfItfMIVNin8JiG21nBDBU1P0/B7C6qqgocPH8IHH3wA5+fnfoxfXl7C06dP/TikvHQo6Gv9PQRoYyVmYKFhkiEpNu5iYwiFK0zz8OFD+OlPfwoPHjyA4+Nj+I//8T/Cy5cvAWA7Z85mM6jr2h8p3+b4XW2+ksaFRC/9rY0nWo6kOFroxGNsrZBSDnPnsj75se16ZAHKu5QXOU8ir00mExgOhzAajRpygNXgbW2b2BqoyRqp9tHmb1RWqLwRg2pRwfzzOYxOR+Dedw1HFnd2qUcUs/tgMc6HkzTijlgHW6cWpks4YcVdscUubHfssB+bOwewD9/tVvTjdkeLx1XsHVZSGA3PAu+jEhxgpD18kAvT0fcgjvVFwPukP+kuVylNo98pflqO5LlzwlOqL+GlgMdJPkpfkIfR7/92vwPe3dVNc4ZanaSNdBQ3HumNILFEDptwkoLmcY025m3UcNqTtg7CrOSk5kDeNBK/1uCd165ysHyxhHpRw0l1AtOHU/jBD34Ak8kEvvjii+ADOb5OxuRAbU3LXUv6WJOsNFjWQp42ti6ljMEpaGtkjgGXtVO6dKwsfLa5fgZp0cqU+Ksotqed0StvOA5JzqCyMXdEj8djOD4+hqqq/MfDEs5YHbQ2tMosXSAlu6b4k8po9E7Kr7/+GjabDYzHYxgMBv4EnDbyah9gGW8UYnqPZGvQbCaWcrjORXHSMK4TSfTF7CWxsdoXn1l0IWl8tNEjUnZtHnYb+lFbGqS8hwBqO6XOWAqbzaZxYhXXdXL6gm5eGQ6H/uMWXoalv1MgjSutLvg7Nka4XcBStlQOhcFgAMfHx96xenJy4u/WBtg6Y8uyhMePH/swvOJuMpnAZDKBy8vLwMlsPX4/BX2PBasdxzJ3NPSNlmW2nbvfJLDMP7H6W+VZS1qOF9PTq0Q1XKl+0sacJMPn6ha3dkxxCm7aidF3eVLDS0ck5ky+34IdUoMOQFcOUoAGWYtgmAK6OOMuL/xq7Cc/+QmcnZ3B559/7o2/VVXBcrn0XzkdHR3Be++9BwDglROKmy6afV5oz+Gu8zF+8XV1dQXr9bohEMbaRROg7triKjl83hbQFFR6rDDyuubsou1RlqW/+xEAvLPl4cOHIj40krzNkDOfUeWDfuVqFWa4EJMykkp46A5XvLv70aNHcHZ2Bt/5znfg+9//PgBs+/rFixdQlqX/YGU8HkNVVZ371GLswn/kJ8vR65gHDXDcMMXT0yc3lLSdB7rkjYEkwMYMJG3nMm5k0uZwTgdvR/yqejQaNdZTqUwrrZIxF/mD8lRMlsE+0mihuCVln9NLDXLSfBAY86rtf13WMIAB1NX26Ya7sSztaq0BikHhjygOdsQOIDyaWDim2D/xd8H+B1vHrj/qGPPzYeOaeb3jF9sE45zyBBaG7ckdp4jb6tUSkjV4yglxrvke7HoUHG/BvCvFOcILwm8tjDtCAycffbI0Qf66iV9yzgZ5FFr8fw3+XmN0+vl8tQvaYd/ULebAXZ2c2x7DPTmbbI/jroizt8BH+NGAyRmrkeQIHjZuRD4Rfu+zt6t3LFyijfPH8tkSVi9WnqZBNYDBaADn5+dwcnICP/rRj+DBgwcwGAzgyy+/hKdPn6qnXWjyRrIamXN4ru7T1nhFZduUnHXb+hilM6fdUc6JGa5jgGXm7lrT2lsyqlEax+MxzOdzePr0KZycnMBoNPInakg8qR2RTcufzWbwwQcfwGQygaqq4LPPPoOrqysYjUaBw5bSqdVBq1Mwf0I+D+cAl+9j8gpNp9F1dXUF19fXPg4dOrd5rQxt1xjwtpDw0A8SkJe7zGXUyULDUYemd+ulnFYSHl4OTROT7WmdLboRBW18H7L/NTsEl6np3Z0pfDdhp7mp9UArh7cD/ZiVfzxsaY+YbloU4UkEOHbw1AX60Ya1TWKOFcoTtO/bAl9fKF46z/M60jZGXRVg29Z//dd/De+88w785Cc/gfPzc1gsFvDkyRN48OABnJ6ewnA4hO9973twcnICANsP2P/Vv/pXHuff/M3fwG9+8xuPfzwei5sack5Z+iZB32M8Z73vWgaHNmVax3QOHbmAJ4ukQBu/fbQ1t8lQuHVnbFuPd9eyOMQMXlJei8AVq1tK2Mwpqy3k1qkNzq5prcpFbh5rmVxYwkWR70BtWyalnS6oRVHAbDaD4+NjmM/nsFwu/b2x1PAwGo3g+PgYAEB0MkrCo4UWTNvXRMgVCWt7pfo2JsxyAzQKMPSIHo0WLlxbyrtrQPuP0p2a2ySwjLFDCfwomFrS5eCUvmCcTqfgnIPVanWr/dx2fHSBnHWIjwPOa1Ieje9yBbXAIbQLr6rKf7k5mUzg7OwMHjx4AJvNBlarlR/34/FYNWZ07e9DzA04V0nzpzZHcUiNS4uxpG/g9MQcpW3o6DouON/il9X8mGCtbbXyU+uV9B5bn3KMr9zQpdGiGTokIxQ6sJwLnVr0uGI8qhh3yPrwomg6g+g/NH9LO2IBtu909yzuaKXl8J2sdNdtw/FUQGO37Da4aODxdBQQpKMOW8mxFdspy3e8+raQ4mkbkrSB85U6OzlNjuHAfnRhniAvjeP9JdEn5Gk4YsEFNPDftO6UlqAOQn1peY2dsQAq7wV9RuvIwRBeDIv9vcoYhmNY4D0VYsNdansaztos1c6dQWtHRoOrHbjNfh4pr0tYv95fITIej8EVzq/js9kMTk9P4eHDh3BxcSE6pySwODC1fH1BlzVVkuUPVVaMhhw5/9BruKSfW/NagOsekjyLxv/5fA4AO35l9eZ0SnIE/sZ1ejAY+I9ENf3HKgdo8rMFcmwGUh7aFpoOasWL4Xg3Ly+vqyOkLUjGVR4PIKylEPKRBF7WUnSsWDkSLsnBQ+nP6RvrnCnNGVyXkcaMVgcLbRbQ5F8r/ptYMzjE2kebby1zcK79hOPPwSPpIpjXMqenyufHcCNerf+suLX3nPWwLcTGKNfZ6DxY1zWcn5/7E51wrTo9PYXT01OffjQa+c08R0dH8OjRI99PR0dHKj1IE+1Ty7jg495Sfw6HtFH0DTGd+pDzRYoGLD+1pmD+2FxyyP6w8Ik0NiU5S0qbCkccEk2pdYS3r9Tet+6MvYvQdYF+U+EuOppyFrougPXmDhr69Q/eU9inosfLoztb/+Ef/gFGoxGsVit499134d/9u38Hjx49CmgajUYwnU4BoF1b5SjWMbgJYUTDxyc53DUMsL+rYr1eq/zdZlK+y2NeUoRT6TWHQs6cEFMOrKCl51/BIk18hzqNk5ytaFzhZSwWC7i6uoJ79+5BWZbwZ3/2Z3B1dQVVVfW2C/5NBMsRb1TY0eagVPtxQSlWjgRXV1fw6aefwvvvvw+j0Qg++ugjOD8/h1/96ldQ1zU8efIETk5O4Pvf/z588cUX8Dd/8zcwHo/9DtkuRkNeJ14XqwGK46NKJf2nY0Q6PpsroV3qctsgzQdd5iMJP33yu9txl8v19TVcXV2Bc87vwsiZE/gahc+YAkuNcBpOpMOy65obRFMGiOSYrRzUm3rrZAK33xGLz8HuWezG1s5JW9Q7xyU9slUiuxB+0yf+445aGlazJ/lHesS7Yvm7g2bZPB6E30DepfpoIDmwOC7F6eb7S3C+Bb+pY5Y54/wTHZ5OiHNKHMOvOm21/1pO75zbxhGaAjrqcKzQ3a4ch7ozluyKbezeFLuJjUst7a7sutyOEw8FccZ3mW4DEkJ+0JywQR0PARy1xK+kfzaXG7j63dX+w45yPz+hwwBhtVrBp59+Cuv1Ojj6sBO5ik5glZ1pnlR4F5B0nZhRrEu5femFfePKgdQVQLE1jhrwtR3XqJPgbis8tQogvSMOy+PrMV6l8fz5c/j5z38O6/Uarq+vA7yarEdp4/XhaTTQ+irloIjhs5atAT3ZBOlDWwwCb5fbBJyvck/daaOrx2RDygf4Tneu3bt3Dx48eOBlx6dPn/oPCooivM6H63Wxu5A1/UPjS832IMV11dM4fm2cWuRfDKfjuGFg39kO+AYAC31t65jSmW7TqSWt6bETqto4q+q6Dvic2oqksWNd46W5tgtIthLNlgUAwZ23ueVMJhMoyxI++eQTH456LcD+FDh0ul5fXwcnx/H+mc/nMBwO4eTkpPXafpfsDLcJXeSjtraR2wTNRmj5oBLz54D2gVbqdA4rxMYlvbIiBQ1nbIwYvtB2GUw5DpCbZLA2ZcWUOUtaqwDcFVJCkFXQy4UYnjaTSZtFsS2vSgowj+8CMfzr9doLFYPBAB49egT37t3zxyauVivvZLy4uIDr6+vG16J0wsmZfDSBTptIU3U8JHABndJH64zhsXbI5SlJ2eA4JVpj0IfxIibUxUDjxbZ9mJpjUul5Xg54DwDvU2oE4H2rzcH0iM75fA6LxSLgGW444TTz40UB9oIAdfhxZZUqHBS/RdGm9bHwTA5vdZnrqOEkZjCU8liAK/qr1QrOz8/h/PwcXr9+Dev1Gpxz3mAzm83g5OQE3n//fe9UK4qts0074rANxOaD2LumJPJ6Ut7hjkMM5/yeawSgv/uYu/uUKbrk1+iIyWnYBihw4+kVMeOG1ZCDcV0NL6kycnBZ5xAAgGpVwfr1Gib3d0ewur3zi/43wmH7bOxkdex+2YLgwDQ7h47fiUreuYMpOP61gPD+1114ancqncc8HXj0MqWD9gNzdvk2dU38kcLFMMlR6HnHpd+DNdDt2y+II2HJvqR0cnzcOUjq3NgVy/mfhUkO3kY5hB7RIUz/XLgzVtyJrIGQjtPQ6Ft0NKP/tWD82nb48mZjfY+/LQ5mEVJ0afgk/kRanINqUYGrtm1WXpdQLau9QxxkeRydXl9++SXUdQ3z+dyv81Yds81aKOkWvEwq22lyj0U/SEEMR1uZH9NK7RRzlORAG/2jK1hxxdY96zqO/EBl+tS6rsXVdQ2r1Qpevnzpr1Oh97rH6InpPfS3poNYaG3jIKF42tgP+Ljijva+HSU5EKNDmgssfCHJ/Za6aXYQPpdhG06nU39akHSqGq2LRidPL6XT0ki4LXNPX/MEdTjTPrPye8zOm2NL0fQ/jtMKmm6dq2PnlpezXkjt2qVfY2szQPM45Nwy+5pbUrqjVl6q/JgeC7B1Gl1dXfkyXr586e0eePUeXueGui7C8fExvPPOO/6aN4ynbS3ZDqQ5LFZnLDvHRnUI2aIr7+fWoQ30PWdo0EWuRJpS/W7l7Vx7hzZ3x9Y0Cz4K/LjutpC1M7avSTOGv42wlgs3VUYftHQ1th26nrdVVt+QWiAQpEX+UDTQxbAsS1gul36xfP78OXz88cfeMPwXf/EX8A//8A+Nuxjwi1oLvRovUuXyLoO0MOPxkihAYDoLaIYP2haxL6AtgocEbduZL1htIKbg9wGxcZVrKBsMBnB0dOSPqOVjUxNOkScsZeAua/x6M6bo4m8cowD7uzuurq68QUWiiQsNGu9gvdve/9xWQMzhAVR4EIf0xRs3FtG1PyWkoSJAv6g9Pz+Hv/u7v4PRaASvX7+Gv/3bv4XPPvsM3nvvPbh37x783u/9HkynUxgMBrBYLABg2zez2czMDzGwtI/V8JXKLxncYuklyOGD2zCQavlSQnSOMYDiin29iM56qS36XhNpn9H7aaXxYzX6SrRSZTtVB03pu/78Gq6+uIJH//QRnHx4AoNyt4u+2vEnHslabP+943LgoKh397rWu3W72P6uoYYCChjsLnl1hdv+LnZxSAuSVCT+6Z2ztJpun8bvlGXh4m+Oi8YBiQclLBc0ZxuJa+w4jLw757xDzMdTucE102A/et4U0uDO1YYDlJfjQnxiuXWzHPxNw6U45/Y7Yxt0KDtjXe32ztJkd+yduGL/UKcn4Yu63MnyhG/MO2Mp38lE7X/myGq++AgBRnSiM5vxHMCuvUsHV39/BdWi2ofVzbUK3/Gup/F4DFdXV/Anf/InMB6P4eTkBJbLpb9XvUFTZH2U5A8JUsZ/6eQQPl9KekTOvG0Fy1qk1fU2dbuuNg4NpLVa0xGkduFrbEwGkWR4gHBXJ83PDdkU73A4hOVyCb/97W993GAwCE7N4kcn07ql5EPe3s7JpwVp+TAvyifB3EtwS+1Er1ei7YB1kk56of2Az9h9oX3bZawgOSMAZP6O0UllM4AtPwyHQyjL0nzVBI1HnRFx4d2OqP9Mp1NfBgX80FnadRzb6Znb/tLcGeNFWh+KA3nJMq8jzx8dHQV6Ku6qpHY0Pv41ulPzPs9D+Z7Xr28etuqdXfTTtiD1JwXLx6+WsUX1fMmWB9Dk3UO3hTSe25SJ4zSGvyxLb/sAAPjqq6+CcmkfvP/++/Cv//W/9nPCT37yE/jRj34E/+N//A/49NNPVTr4jmdtfYtBbv0t4+U2eLpvaDM2u8wlNy2bamlSu+Z5mLWNJFkDQK836iAccP2UYL1eB2MuBnfumOKUIbYLSBMDL/MmgQtrKehCZ5u8N9kuOX2dStuWb1LCZZuJTVPyYkoSwmg0ChwbX3zxBbx+/RoAtgvpxx9/7PNdXV15AbKLsq+NkZTAb8GXgi4TdixeM4hI6VKQO28cytDQBWKGJ23+jS1Qh6JJKpML3ShE0juY+LFWHCcVyNFZul6v4eLiwitjknGFGgNSbYN5kDapXSWelAwl+I7GD37flNZ+KYgZlySIKc0xw4JGX2xMasYlKS0ar549ewaj0QjOz8/h008/hVevXsHJyYk3ODjn4Msvv4T5fA4PHjyAoigafR2riwVSPGvBHzMmpNo5pxyt3DZ5Y5AyXnIlDY1tOXyZ01YcUoYfDWcfc19bxdtitKLppXKpgzk2J0ngw3fOLe9MQacWHgFbQPOuWH6H7M6RgztlqfMMd9yq98GSHbHBfa5F6ATCp0+/c4YhbmmXqp/veRnFjnaAIDx4L4q9A0rYAdlwgAmsqu5mpHhJnO87R95dM47iDfqd9BHduRrsXiVP0eFL8RN6g7wQ5g3Ko/xHeCEo0yXidm3TqJNzjX/VmdpocoHuSHpsU38McrHnSwAA3NUtjq/cKSFnqqa4I/zZqkyFF51zUM0rqFc7B1TloFpXewe1pTgy5+HJQPP5PPoRlXVebzsHY97UnMkdctqaIhmZ+4Y+cR5ap7HKSXz90tZGSWaX2v5QxkvOMxZZ0VKmJJPQ+mkfDNDrV1BHwY+W8Z3n4eOQ1yumw+BvDW8wL2e2AYXbtOdJ7c/BMs45H/fBKzwv/7937x4MBgOvB6dkX0nmlujXwiXdnM+PkmyaM6+neIPex4wf+ErOv5i+a9HtMSylp1nLaQMWXG1shm1pca55opmUTgKJfqs9I0ffs+DMKU/iAYs8kKP3c/uBNm5iJ4JdXFzAJ5980vjoZ71eB3o6/SBe0tm1Omhrg1bP1FGvlrGRY+/KtY21hbZrHe/X2DxMIVavHB7j+LR3S54YtGl/3kZt5rTY/MJ/4zxGP56P4eF0IhzcGRtbsNvCbQlbNwE3sRB+C+2gDS+nBjXHTxfHo6Mjv8uuKAr48z//c79T7+uvv/bn/zvn4Pr6OnDcWs8p1+Bt48OuxhcJ3rY2kgAFNk05OSRIxhNtwaOGBf5Vq4QHcR0fH8Px8TFcXl7C5eWlP7bWOSd+7SSNq9Q9jbEdblYoiu1XWWgk4X3SZrxj/fi9P0hnV6MR33mnKap8bErKi6ZI4o6By8tL+Nu//Vv45S9/6csdDAYBX1xeXsKf/umfQlmW8OMf/xiePXsGX331lZ8vU4YgSahLKYtSvLRbQsOhKXLcIMZpsgjqNw0xAwb2AT75PUI5hs5YGAXaNilDRYwHDwV9rS9WY4G1PGy3wWAAxaDYOijL7b2YAxg0dsa6wgVOKP/c3edabLe9btNRwLtgWb7GDln8p1MgvzNWuDvW/wOA38UovTvlCSwMWDh9h3245vxtgBOcfYrDSwqTdsRqv4O52QlPcPudzk7BJ+ARcUlPXja5B1bCQ8N5WLAztm6GBeFO2ZUJ8loVpNHiaDmlC+5GDvh292x9d2xBfxbN/AK+6C5YgICXrNDgQ8J7uOt1+dUSVs9Xvr58hwr/TcOoLIJHa242G7i6uhJ3dPUJfA2xGFO1cGkt/iboECno2wGA/KK1dczoKOXFd6nfUwZzq8zIZZGiKGA2mwHA3sgtyUP8Gh4sT7qeh+sLeFIM1R3wSGR6XCXFgXmLYvshI5Vl6YlAGvA25uOKf1zBnVN3RZalkNKVaDqAuMONhnOgMjLnTwtPUeD9XhQFfPTRR7Ber+GXv/wlrFYrfwJTjIe72pliYHUw5OJEJxIAwOnpqV8/8ENs/NCH5qEfQMcgpQ/G8t1F3j4U4FjH08Jia3if7ZKDyzK/W+J4upgeGdNZ2+hp/EnHK7WTcXj16hX8t//23xrhk8kkuKubnkrC68bnGMvpY5pD95DzzJsIKdtGW3wpnLSPLX2S02+p9dOKA0C2A1mBrrMafloGjqPZbCbOY1YZsHdnrFWwsEBMSH/TFi6JXnSsUdC+VslRAnNp0gZ2DhO3Zf6bUkb7UoL7FJosSijto+Vy6XmmLMvGwKfKprYbLwU0XZcJv037xvJ0nUek9755702bkxC4sMadOfjOj5891Nweo5H/Hg6HwRfa1IkjGdCocQ/vIKPHSND6WhRsiTa8mwTbEp2dVqNdCiRBoe28xNtGog2dY1JeDacWZm1L+tTyUIPuYDCAyWQCw+EQBoOB3zXz6tUrGAwG8OzZM1gsFl7Rxju2JaOdhb5UfXPmmJTwK6Wn7WLpP5rOSlMKv2So5vgtRk2OjztiaXyODIRxeKRabPzhBxOUz6V5w1IuT587NrlRlodR0PomV7awpKVK2Gg08v+DYrB1xFYDcAVxPu0coM5tjyX2Tku3i9sdIcx3xjrn9k5YeswwTUMcjd65SXbS+nyDvQOK76AN6kd21QZPV3gnMY/DMI6bvqOzljrBTI5YzEtfeR85Fu4fLmizxq5VB+FYleL4k5fHf7tmuYHTNvYvpG/s3hXwId824oHEQXM3LK+ftR9ov6l9SNrFOQd1VYcfAkDz2dghqzhVo/cbF+F4EI82LnqUeV34G/u+3tSwfrUGV5Mv0uvtvbD+zjZCh3XeovIY7lySPszrGyxGFIBQbqTvNL4vHfRthNx+tPANXT9TMoz0MR6VV6j+I63LHHJkBQ24LJIClPW0I3w5fXhU7WAwgNPTU5hMJlBVFSyXS38kriSPj8djGI/H8OTJE38SDTqxUvY6uquKx/PxI+3O5fjuCrS1ecX4hM55NB75UpITJVlT0wvW6zVcXV3BarUK7EroEEG9lfdLriybkkVjc76Wjl8bEptbY/rG5eUljMdjODo68umm0ymcnZ15fVEq32q/kOwWuXqERHef0AVfLv2Ul4bDIUwmk8DGgKdepNZOjovXI6eNLWVZ9HlNN43pcRqdMZ0st7+0uZaevEjj+D3K3PbDT0TQgM9bdI1NjU9Kc1tbtkZPDuTIbKm1720BulZL/Gvpq7Y2t7brbKxcq102pgsMBgO4d+9e4xhj6YhirQ536pjiPgzMXQbtIZUjjhuNx3wyRKGW56NHZ9zFwZ0Stm4SLOW1oanPekiLe0wgdc7BarXyvFHXtf9CCcdJbMdVypirlWuBPhTPQ4NmDMF/ScGJ4coJv2lI0WdRjLSFrI86WnHwI1IofUgLNchxQV4bY7Sv1+s1rFYrH4dKHs7NqWPwJGWLOn+kY4+kvJxOjlNSxNv2BV9XY4o23YnLcWgKBf8qv43yILUr/Y3GAkw7mUzg6OgIjo6OYDqdwldffQXX19fw9ddfw3K5hM8++8wfQ3V5eQmfffaZdyb1AZqBLmUk4MaKlPDK8aXu09Du4aLlx+QKzTipQWwtQKVbuy+NC/ic7i5KmHNN4z3Fh7vO1+t18HWu1q99gNTuMWUdQTqmnOfH3xYjl9RnsfqjM3Y2m8FkOgEHDupNDW7its5Y3Bk7dFtHZL0/GljcGVtsnUfeKYv3yToId8ZiXiQXnVyWHbHEOetx7MoOdu46EkbbFZ2yu3z+CZHfPK8VpKSO/nSqM0x69/3rGF/ReCGu8SRO3QAHzQssjP2OPS1hPFwKC3bB1hCkC9KTu2lj7R841nk8qXujXWoAVznvmASAvWMfx+nuIR1jjODz4hjCeAeig1b60KA3cHJ7YJ2rZQXXn16Dq0Lnlbb+aDIPn99oGOpA9NSLQ4Mm60g2CmmN1/B9U0Fbt7S1yKJjSmtXTKamabnuJ8lmMTxa/XJsWjQOdRGtrBhNkn7A6+mc87uaRqMRnJ2dwf379/2OyNevX0NVVQ35GO1WR0dH8MMf/hBOT0/ht7/9LVxdXcHLly+TdyBKRyZjOum0H609tLrfFki8ptkTpN8xWY1+BM31QCk9LV+LAwBYrVZwcXHR0KFR55VOTEIZ3jJ/WWRRGq/ZJyQ82C6pPo/JunVdw/n5OUyn02BX92w2g9ls5u0JEi1Ud0rpPPw9xccx+u8KWMYa1xu4HDAej+Hs7CyYj8qybHyMaykrp51ic2dsfrbOwbE8MZtUDt5YedYxMRqNRHsPHQu4exnzbDYb0w5XLAf1bkpb7MSrmH1Lmx9ywLqW91HWXYE+18UYnyIfS/fZ59CUkm0skJL72kBMFhkMBvDgwQM4OjoK0l5eXprHy51yxlrhNoWuvsAyyJ3b7/TiC75kRH4bJo5vIqBgibu6EObzuf+yYjqdwuPHj9XjPLigHhMqLPR8U0AyOgPslQ6qpFi/CHvb4FCLuQZ1XcNoNILj42MfhoLdfD6Huq7h+PjYG+PoF9xS30gGAKoES05fTeG0ChHcAct368YMMlI8rgW0Pm36BfHXdR0cMYNxRRF+GXl0dATD4RCur6+DOknGSa28GJ0pxYc677iQjvOcdJoENait12v4y7/8S3DOwcXFBazXa/9VaJ9jWZpL6JPzgJSPQ0pJ0AwymiEy1X/UaMKF35TCmDIODYdDGI1GXuHmoLWPBjh2+VfytDxKR6zNEEajEVRV5T/QOLSsqdGUUnpoOsrr1vJyacSxQsdaURQwGo7g6vkVrL9ew9kPz2B8OoZBtdv9hscUD3ZlEodqURCHK0C4qxV2DkzcYVvs0wTOqAEL505XJ6ThuxR9JZkT1pF0bv/040NwyBbF/i5bpNHvou0yzQjOwoZDDCJOQRDGO3GgcQeqT8uPJRZ+B45SIczHCWGWNBanbHAsMUmHztbgWbvmkcVS5/Bw0o5if9J2BNjeibqptx8mONg7/CmfwP7dp9nxVOH2jllKR7EP3D4Yv/J4/5EDDdsjE+stAuMp5xxszjdQXpdBmrqsGzzniyviH/5Iefo0VLaBmAHWMudquhg3UL8NNo22QOU6KuvReOl3SgaK8RjHibICyhP4oR/Ga7KUZlyX6kj7muPU9NAUUDy4PmuyjoS3qipYLBawWCxgMpn48nl+lAHoB2uz2Qzu378Pjx49gvF4DK9evQrkMK1NuKzG6cU4dIR10XduClLzksZDVtwSz8TKseiXVVUFx/FSHDE7U6p8S1wf8zjqxG2M9PyI5txy6ZNDjv7ytgOff7g+OJvNAhtbWZYwHo9hOBzCZrOJ8nKq3zUdOJY+tt5redpATN9LOa9ynPkcuI2Kh+O8wO2ellNIrPzMN6DltmHbuTRmD0G8dxluiz7tqGHpSGkAW5tKMhjaGKybVzTI4Q3cna+VJdGCG2wolGXpHa8nJyc+/3Q6hUePHgW+HA16d8b2xTAWoSMnvQXHocBquOThODFKXnietq/JqS2ubyEPpAmLTzir1coPeq5kcUEDJ7NUWTl0aSApW23K6htiCh6GpYDmQQGEt/mbtIBTiNVfE3qpocQKqblZEqa48DcYDODo6KgRjs5YFNg1gYCC5HTDfuQfQNA6S3XhddDK5nftWoyQqXqg00kyWFmB5sd1BRVvqqhSYWI0GsFyuQw+RmgjEKfagOKW6NZ4McYDRVFAWZbw93//9z4NOgUl2mKQmkesAmTOfJRLEzeASXyVU26qr/juBW0OpvfBagY7zamaokXLw2mjX/hqMhlVCOga0AZy8mnzoSUPb8uUYk/zp+I5Dknm2LzawGKxgOP3j2F0NNo6uyrwzlR8Fq4InGaFK7a7BwfOO18DXiLHD/OdscE9swMI0nqDJD32mB1XTJ2kxUC+w1U6lpjGIS6aVouX8lqhwatOiPMPt493YXxjtyyJD94doZvjoOXR/MyhGzhnSXzjycptOFRJuOScDZykvA7Ck/+HzeqCZxugx/VWqwqKARmLOwer57uiyUOclmh4UQAnNaAdnb8av0WqyXdBN/qsBti83sDqpfDRCuUxkJ0vKWfBXYUcuUeSIft2SLyNoPGNlIan52k0XYS3PZUV6PGsCBajolUvxDxct+TxXG+RcFJZHGV6/B1zhCDgTsjVagWr1Sp60gr+ozESP5i9d++e19nwqek9Wp9Ksh8/DS4lF7bVS24LpD5PgcYz9J3PO5qsi7veeJ/jjjnpA+LY/KXxpxTXtp+kcS/RkqKJjnfpY2yeXypLa1erbdoie/cNMX30pmnBXZjIC9gn+JF97E5TikPjc+2qmxik4vuy98XwSGNFq6PEr7E1w2IfoM5YisfygYZ1Doulp7YpjfYUWNduaf2OzU1d5Lcua5Nmj5TWegyP8Y2lLPoekz0kemjemBzGeQHXJSttGkgndNByEQaDQXAHsgTS+JD4E52t1G6NVzrwe8gluLM7Y98koSoFaNyjgEfD4BnTeHk8n4hevXrld2l8C3cbrAK1lI/yOt19QgXFV69e+QVxs9kEX1pYt8Kn4K6Ou1TbxhYFmjdnMdW+1LEq3W2hLR91Aapsc+ekxeF5CHrwS+zhcOj7E7+YxOO0APbCW5t2w7xccEHcXFGjQg5tlxh/aUp3jEc5vj74gfYvHqsBsD1KoyzLhvKDAvh4PPYK0nq9NvMCP6ZQogchplhTpZkanijeuq4DnuDzIQpckqKhlZsLlrlF6vs2c5MGw+EQptMpnJycwLvvvguXl5dwfn4Oy+US1ut149jpnC/EOb/iM2UkQ57TDAF8zdEMODQsZgjB/9Fo5J2pZVn6neCcL4uigNlsBqPRyH/soX3Y1DdoShSNyzXmxNYnaX2XjrGWlH76j22KdzNDsd0VV20qGJQDGMBgf1xxQZ5IDnn6Y4wHsD+6GLa7VIsBcbpSf08RKpveWVvA/sjXRoPt89H0jaOQC/YOzad4rCxtMv5OccRAc65xXE6IIw4z6d255u+GU5Q7VQl+n19Kx/G6Zrzl6X+z3a4aXufCna8+rma42W7YwElM2562M2ljBy5oD97O/r/eluUqB6502w8NdvzmwO2dswYeo+PAg88uHH0tsXwhf2jAIRjv2LYbB4svF+DK5lwAsD2SGOcCaR3AeTS2rmlz/U3Lvzmg6Uia0aoL3IYucNNA+YTLClS+A0iv/7F3LQ2XgbiM1lUGj/GFhFtb/yXapHaQZEuKB4HqPS9evIDz8/OGA5T/xviiKODzzz+H+Xzu4zabjf9Qlh53S8vl8jeV5yldAACPHz+G8XjsZYyU7mGxXdzWeOpbf+b1sNQbYN/nNL1VB5DaTtNtOS5JJtWuT0nVgYdJ8iyvY2zeKMsSXr9+3QjHHYH8DmZJr8+Z+9/2+ZwCnz9iY3Q8Hgd8ulwuG3p8bN6X2rUvu2gb6NLPbfNyJ5HmQKXlaDYTjrcPOyC3oWnA7W6S8yvVRqmPuzlNEl7JHprKcwiI2UQkuM05hq4DsT6w2AVjYbHyrWWt12uoqgomk0nDKVsUhf/Q7OrqysT7ePQ9p3c+n4vH/lNIOmNTwk8fEFvgNbCk4QvobTCoJGyiMXwymXhnrLQd2jnX2SiYMuh9C/1BF/6iggpfsPB9sVgECyd1nrT9kihmDI7RepMQo8dibAYI2zdmxOaCnaSQajRo7WJZtC2KQ2xR4/S/aaC1Hc6VmAbrV5al6iiVlKVYuZLRw5LeGh4L43ej0H8a3hVwLcE2Gw6H3hmLTip8UqMY7hxOGVVjApRV4MK00ppJjXTcyFMUhTf+SEeIAIT3y7UZw10hF69ljtLyjUYjODo6gnfffReKooDFYuF3PVjuWdLK1dovR0a0COB8HEtjQSuTpuUfc0hp6e5wfgdhjoHFAlaDmUSnpY1jc41kAO0KKLNiG9br7W7A0XS0vTsWd8Y61zh2mO6ExeOKcecs3TkY9Ee9c0Dtdr/CIHSK0Z2sosOMpOG/LSDllXbIYp15XgRTmVIS4hwMfIiOhbG60/jGGMYwHkdxMfyiw1bIR8sJdp1y3OQZ0IEO0F245JxtOFf5PEXS8/9Ge3E6IcQdgOOvu7IqB3VZQ72poS5rv+sanbD4UYF3phYAxaDYH9G9c+IXxY6H6dHFO76mu2upc5a+B32CvyuhIpSnSL1c7aBe17C53IDbxOd7i9yV4zjQ8t0EtJkXcw1lUn4t75sqz6cg5lRM8QpPm5JFY+udJvtzfs5dP2M8QfV6jR6pDjSvJONq+odmTKaAp3DRk0C4vszzXF9fw3A4hJOTE2+s5/cBSvIebxupP51zcHJyArPZLPiQLgVWWSkXcnXrtuVovC+VnztX8flZ4rE+9c4cvS8XT0PuEMBqx8IPeXke6oTlerpWNwtdsfibcup0Lcui/1M+0/qQtnFR7HdfWuUFiV+tdgcNr9W20Xb8WSHXlhVLZ7HdSHWylG9pLymPtQ1j/MPLlPq+TT8dyh5kHXN03dRkgVgdU+XE2r6rPKutU5b1K1V2G16LAT2GWwL8QISe9qHJU3jMuuR0Xa1WQbhkW4o6Y/tmxL4gVzACCAXLNvnbgDaJv/fee3B0dASDwSAQqumXQTEcMbipun0LMvTZ/jhgN5sNrNdrePHihXdKYFm03JTj/pvEG10MzpPJRPy6UjrulirWMYGnr7ZHZZniwqOGuuCnRzJLdexjLUjRFztGFOlC/ud3F0p5coALYPgVIHX8UUEh11HDlbrUfaXaOoAnKFjvDaF9NxgM/PG8s9kM/tk/+2cwGo3gF7/4Bbx8+TL4+uvk5MTfFwuw5Q/OI9gXmpCF6xl+NJISqGkYN+SgolYU4fFZlF82m01gTBoOh8Ex79gGHDfdlUHpvQnoc63A48XwTtwPPvjA34G6Xq9hPp97B6XUvlalVzOmxQyj9IhraY7JAZpXuivYOweJ85nyqWRYxVNJEDeOk75lYIuiw9Mgb3a5K4iDZvSl8VymQB4DAFgulz4c58rL313C6GgED372AEbHIyhGBRRu62iqoYbB9qzgbR3BbZ2pfHcflg2EpwZbp2dwN/huZ6qnv4Bg16u43vI0dJfsLj62M9aBC8pDOmg7BmkIuGJ3LLMVJL8Z50UnhKNzjce5MC5YZ10zn+iEldLi7zpME6RlYY1nvaclCHOw3Wnq3D4N2wXL74OleYMn7lit97hS7b2PCtuTOi9pGznnYPnVEhbPFtudsYKxSILJOxMYngy9w7YoimAnOOUtHubpEMYRrZurHSy+WEC1qhpxeyJJ2K4+3BErgSR/0bkiZqhN4e0qe/Yhd0v9KH0IiPGaDKuthd9koPqstL5RWU+Su2lbY19jPL1PMmV83Gw2gW6BZVL83HGg1YfSw+UOfj1GCjg+/DgS6UntPAEI+Y3jxv/RaATj8TjY+cpxSWXUdQ2vXr3y15jgjjYLcHrwwznstydPnsDJyQlcXFzAarWC9XoNRVGIdrJDg0Ve64pfM7rTcvq032h0Wwz1UrzFgJ5TB2lMa/RaAevGx3js4waprtqpTzyPRd7/JgGduzabDZyfnwfxdI2k85fGA/yqpRhYxpdVLrFASl9rwxPaHC6lo2uWZDPmQE+gi5Wp0RF7l0DaiBArUytfKgvtRfwDodgpGBrg3BCz+3UZ55Ld/jZOI8yB3LVI66OcOlrXHCrDSZDaCc7rNhqN4OHDh778y8tLUcZ59eoVFEUhOmPpGATY2l/5nBWVDLsqQhRiCz9Nc8hFyypIU+iqCEqTG54jLZ1r3XZCojhSab6Fw0EX/o0tcM5t77zDRUYqN1W2hba++a8PsBi1NIOQli6VhuPjhvEUXk5/X3Nb3+3LBQxpzupKO82bs6ZIAolkWKH0ScqiNO+n6kfbwCKgxhTnmLIWA0nAo/1jFQBpfF3XcO/ePTg7O4PT01P/Zfv19XWjTKo4aTyQGo+ctyzzFKWD4+QfSVDcKHhrp0xQmvBd6luNR3PWUks/t1FgYviokImGu8lkEjjVtXK0/u1r3pL4VhtDsfmSj9sY0KOVeP/T/EUh77a+DYNfil8OQZPG/7Hxzg28zjmolrsPHzb11hmFO1/JDtiiJs5StytnF0ade3SXbOEK7+R0busYk3a/irtesWqxNLR+ZMcrf1Inr+cTYWdsA2dBaM0BKbn3/TXr1nCmCk5ZbD/RocrSejzOFxCW46A5HnfhjXLRCaw4fAO66LoLjBbmCOVl4nvjScu1tDEvS8tPcbjdXbGLppIf6/tqVW0/XCiK4AMAP/7IBwHgfxbBewOoYxUAXL0dn/Wq+fGKz2KUoa3A130OkvxC1+S7IC/H5IhYem2ejsmU3zTI0UdjcmaOLK3hwngq6/K5PkafBXJ1ay6vavYkjc5UGVz34+NQwkXD1+s1LBYLWK/Xwc5Vq17CaeT1HY/HMJ1O4fT0FJxz8Pr164Deth+n9TW/URx0XEvyk9Q/MZ1R6ntOe1taaXkSxMpI1cGi91jmdkk/T+FOrTcazbG52MofOWObpzmEzawPyOG12JiXxkJRbD+upPNG7P5XmlfC3UZvk/osNo/m2G66AK9T2/Jy6OVXfnF6UmCVi1I4ctLHyufzJwWrrU6jz2pvs/SfxQak8R+dt7T12kJnKk/XuTG3vBxI2SlidPAxxp2oOCaKIvyIDuUSbH+0t+FTcsZyn40ku9ypO2Pv6qLUFnDbshTedZeDdQL5Fu4+SMJgbJcrj/um9r+2cFkFXb5Qo5DIccT6AtPhrjQeDgC97bSjE75Gdw5o9MYU/0MC3S3qnPNHB0kfH8QEZonmoiiCHYEU+PGtXCDlPMS/lIsBFxBxUUeekHBQwxBAcxcG3UGcAuec/+p9sVhAXdfwb/7Nv4EPP/wQJpMJbDYbePjwYfCll3POK0l4WT1tX2zLmHBF27APJ5Jz+2OWY3fR4hf02m4Amg+/xMcjRLA+4/G4E82WdZkbF9oo/RRw13Nd13B9fQ3L5RIWiwUMh0N455134MsvvwzSYxtIygBXIiwKg2Y0pWMFeYnSgLTTtkgpPdLcJLXler2Gsix9uVS4pmUDgP8Y4ZB3xVrbEccb3/lvySutX1peiQdiSiwC0iauO27rXBqMBjAYDwAc7HfGFgOooYYCChgU27hgNyqioVUjjinqHA12qu7wBPdzFmGcJ4+ngWJ7V+2uLMznnb+7NPSOWHonLb87VtoZq95hawHeBdRJSJvdhWH0veFodc30MUcpz9tw5DoFP4tvPMluVwx3zu13w9Lf0k5ZxE92vGJc7Mnb0T9d+N6oB6kzrystPxfK1yWUF6XHqc17bQ2NWCd+92tbQDq0uRLlNZwnsE7SiQwS0HnJYoS/CaC7N3MNYG+bXaMPoOuMtGZRORMgbHfKH/SkD4yjYNEHKW9Kx5Gmxh3dUUT5va0jJ6bbtdkxw+mhcwzdjarxttTuAABffPFFgxbcuUvzdYHpdAp/+Id/CC9fvoTnz5+Dc1snLZ4GxWm0tnmOzG2FlNxsoZHyNT61q1dyQJpDOR1cz8zFTXlM6pscSI1ZCdrwHdcDMEyaT2IyNwdJd+dl3hZ04f22ebltAGCrGy4WCx/ObQp0/k+BpCtpH/OnIKX7YJoUrrb97Fy4U5uX1WXekugejUbB2MFTojA9lecojQC2D2Is7dBlTMTmVUq/BrnypVZnzR6dKjc2V3BcqfZO1aHLuLfm72rvi9l9pDBJ3rLQQPlis9l4ezONv3//fiPf6ekpnJycwHq99rv7rdcoICyXy+Y41BL3tWB0mZA4SBOJpeNue/HjUFWVupWZgqS05gj538LNQkpQacuHtM/58xBKBeLtEyzKQJsyY86EtuVowngsr6TsUjy5C4yUfjKZwP379/3uP4T5fA7r9bqTk57Pl3zusSzyuXzIFX7+jvfCUgeFdoxWrD68L7S6Skc9U4MMp4/eYSr1Mx+rFJ/UBlI7xOjNAVpHFDzQCUmdZJiO3kmtrcWcPl4v7hBta1TFI5LpBw8xg4dFAAbYK2t4ZcBsNgMAgPPz887HfluA8kIb4wMFOjac237I8PLlS9+PVN7Q5kepT6VyLPWJ1cU65+Ua2CRa8IM4bkiU3jltffVNW9pT85x1jQFoGhFS8jWte6rOAR/XDjYXG4AaYHi0O9acHBNbDAr/GwawvQd2sN/9uvWzFt7JRXfAFq7wx/363bOwd54hLgBo7lCtAfxdtc4B3tXpwPk4mk97AoCng5fDd8lqYY32Exy1ksOVvwd9uGsjzbnq07rwN48T03Bc3BFL6XEQ/GOY+CR5HLhGGum39M5xaWWL7dts+Ea8mJYF4f2quDs8B/ixybVTPsIo5PnJR9P5G+LXZoh0RGRbno7KRRoe6w4LPjfxdfEm9d2YfGHVIzQcVr0kJhe/bSDJ/QD7+97x48WYsQt5RjJUxvgU4+kzVQank/OstG5rcoSmy2trc45OZrWVFYV+NHNq/uD0aOXlyvsIqJfMZjMvm6fw8bZP6QddxpelXtY24O1IPzxFOTZnDud8FNOXcsFqkEf8Ek/H5ldpnMTGrwW3BrmyvTaGYnon1zmseA8FXcrKsbvkQszOENPFtDmxz/GZ0mPbjKuUfGDBlWoDibZYGTGZThrTqfVVo5HGtZ2DtDXJmlejObW+UDy5+bW0qTkkhvtQ61ebMcH5IobPUt+uMkQKr/aOa+5isYCqquDo6Kghr+H6PJlMfFjsZD6+6YfDndoZewjIEV4PARJT4t0XKRqwA2n8eDw+CJ3fQnc4FA/l3jPzTYaY4A4gzwexcddGeEYa6BetOTuuJNyDwQDOzs7gZz/7GVRVFexk/PWvfw3X19cwm82CL9zafE17m3cVcGfAcrn0TmbuUJLyaoCONclYgwIB3emlLdT0q2+A/bicz+fBzjsKscWX4o4JWV0dg/y41mfPnjUcrfyYMdxhKt1tQNPFeKWPr7mLooDj42OYTqfeMDGfz7N5FAVEanhCR/QHH3wAjx8/hvfffx/quob/8l/+CywWCzg6OvLjKFfAzjHu9WV4pncJX15ewq9+9SsvMF5dXfkypH6jgibyC71/11r/NoZkSdHuCnQOR17Xvn6P0dQ3WB0Nbfggt4+w/JjxUsPN5zqPs3Rw9bsrGJ+NYXxvK6sWwwIGMABXOKiL3d2xFWwdZYXbPwvY/3vEwnMAe+ctyRPsQB1AiA/TYJuz3wGOwrAzFu9/JXSh0zbYiQvpMRBz1IpRjuQh8b6PHHl3zTjutNXCVXw0rRPKcSwtC/NPfldsHabxO03dPo1PJ9wVizgxnO+iFY03EMaJ9aI0C23kagfr8zVc/vZS7i8DpBwftwEWA7hknOIfp9EdsZadFDxPSqa/CaDrY8ogZnFKWY2Qt13vQ0HKLoOO2Ol0CgDbE08sMjbVX6w6F+1blA9jfazRG9MxUHfBMqRrI1JOG2qwtRj/LbIFdfzF7mbkZTnn/Aky9EPZvgD1v+VyCePx2Bs86Wlz/KQjLldJMjYvAyDcyX+IuZcb3PE3j0coiu0JSvgRKkCow+WUaQULr+TgbOtk4eVKY5jHae2i6ThaPE9rnZ+5I0XLx8fuXVnnbxtoG2pzPIAsa9C4nDnQQlMMYienYZjVwWYBzVYpgSR/xPgO7+iWcKRol+ZYXFM49LE+8LWL02ehl+OR4vsam5b1B39bfUISnrZ8npoDeVrnXNKmmcrfFrquKYhDa2cqk+GHYM+ePYPpdAofffSRagc9PT31vzebDSyXywaduH5Pp1PVn9PJy9Nl8utLyejLoNbVEBoDCa9z4XEAPB01nmuGr2/hZuAutnfbSanPxaZNu6QcU23oijkAtAUgJTBLYYFBELrPddr8Scf7ZrOB0WgE3/nOd+Ds7MwroIPBAC4uLuDq6ipwzLY17luEmNy5Noc/tDmSHp+lHfkTE7ByDRcWJxoCLs70+FOpbHRqUYNRztHVfYxZbD/nHLx48QLm83lA7/X1dUAflovKER4L1tYY0Na5NBgMGscI4xix7pJGXHytxXyTyQSOj4/h8ePHUNd148jxHHoRtwaaICgJ5rExp/E6wJYv67qGq6srjwd3mWM6bRe91E5UFknRwpUzy+4ord2wHbR4qmTmjFs6j3PaY3T2CbF5KXe8S/zD24Ubiqxjxqq0Bul2TrF6U0MxLMCNt07YYlAADGDreB3s0xWDItw1O9g6vIp6l97td8gCwDYN3SFLd6/WDuj9s1gOHkWcvDMWd98W+9233iGL+cjO2CAvvae2gP2uWOcCBy1NH21/6vgjbet/Uv5wLMz4LjoYiUOykceF787t0waOTRfGq09o7oj1cw3B4d/rZjx33vpnSvYzDK+Go5y2CS0fHcIZwMckQo4TiY97DMd3Ta7JmWukMS4ZGmlZ3ADIy4jN+VJ4V7AataT0fK3QDJcavjZz7zcFpDbB9kYDVqy9KA/Sp8R3lCd5mJQuFyS+wI/a8N7Tsiz9P46T2Ik8Wp1zeJHztSQrSGMT09PxLMmpvOy2fM4dHC9evPAn+MzncwBoXmXD20h7p32POJA+epWGhovH8T5LzScaHhpO6yU5pbT51gIxGVpKkxpzGg4NJH7O/ViClq/J7RLE5FieTqOVp4mBZa1JlXOTushtgUUXPlQ5CLGycux2twWxeZuGp3ia6oupPtBku9h7bl20ci1hXcqI4Y2tLTmQy1c5MnoKUnOnZd5rsw5puo617SzzYe56qwEdCwiLxaKxXvFrtwBsG2e0+INvuZMGdtvFOwdue8K0CAj4tacE9E4Mzdj/LXwLbcG64LaFNouxFWJ4c40uKaCLiPZlV44QkUrLv5per9cwGo3gRz/6EUynUyiKwtPx8uVL+PTTT8VyciBHQLnpeZXu0pN2tklGCUmgjLUNfumV235IE+0vyVjBj9jFnc1thM8ugEYIvOuJ15l/3Y+/q6qKrlUWYVHLlzI4OedgsVgEOwtOT09hNBoFu3ul/qP8wY1OlJdmsxmcnZ3BBx98AM654M7YQ/E751tuSOxyf9NoNALnHFxeXvrxw/mU37srCdhIG3fC07bhdOIuC0xj2QkVq6e2jvD24/0spZXK4HWQcBxqHaNtKLVpjlGI5pf6iuaJjZdUufbKbe+OLQYF1JPdfbGDLR/i/bHgYO/UJKR4p+YAAGrwO1N9OuwuPF5Y+qdQ7NP6u2SVfH4HrCPhDsK7YdHZS+nmT7d15Pk2FprT76RVm9DRl8bvxu7YXZnqO83rDHlZGud28TStI+EAwW5X1THL0oiOWO2uWJpHuCfW766tBXql9iVxngYIx5AfNwwHpnMVa+MMkHY8oZwXW39u21gbc85Ia4uWT5Pp+r67W3IU5RrmUsYebQ3qW256G4Gv89hm1LglGYFpWtruMf1THNusD+gJKim6+X30mB9pm0wm8N5770FVVTCfz+Hy8hJWq1VwDK2mx/C6SHRq84Qk60hh9GM73ob0g1M+T3H5jta5DVAduygK+PzzzwEA4De/+U3gKMAyYnXW2hTfadvjx8zSR5iSHBibvyR6LMZZqu9gO1jk0tx1QKMnJu9a50o6t2q4aBpLW6dAkv81m3NuW1E8OQ6UHPv2TdjC3wSQ5n4erkHbtunSptY+4baoWJ7UuIyBNK9zfk/xcMququmV0jvHmapHl7krFywytSU+h3+0tagtvi6g6f9WGlK8htClvWJgpTuXV+iczNf1y8vLBj6UIShYPuzS4ODO2JjAlBqoOROxRRiN4bSG50yOaIinuIqi8McJakKrJEh8C99C35BaGHIE0BzcWnybxY2/c7rbCrJcsEgZxjSBJlcA4nOEpKCgw6VPg5WkXNLwLjglvBJIDpyUcUVSvlLKIBVGrAqac/LOXJ4GYTQa+T6iZazXa38sjLT+abR37Qf6FZd23IbUvtpx3bHxgLi4EU1bj3k4XRuLogg+gkDjyWAwgPF4HBhwLAI/Gve0o1aGwyF897vfhel0Cs+fPweA0HHZN0iyEK2/1lcaHgSUPbCu3GhI89G+0dqEj0teFuZHR/Z6vRZ5xyLbaHwiGTDoWKZ1sBrBUvLdoWSvmPLCDVa8b2i7Uj7mY4XipXHSx32cPzRljeeR2rkoCqjXNcw/n8Pk/gSGky0PF4PC3+mKzlXvbGNFeYcshu92uPrdsMUWl3q3KzpqicOWO2JjjllfBqZBGh1JBxCEB8cXA0kfVswGkgORvyKvunhYtlOWhTm3y8vfyW+aBts/eDpQHbX0GGHnXMPxyn8HxxS7vcOWP31eYPRK7YNyBk9L2qURXgNUiwoWTxdQzvUrFDTAo1hxrnz//fdhMpnAs2fPoKoq/1FNypkHsHdaaUbpNvOYZmzTcNF1cjwew8nJCSwWC7i6uvIfrvEPjLU6WY9M7RMkeUuak+k7nf9oGDd40TC6uw9x3pQhrg/om1ZNZ5NOkOFrDu8H2pYav7alP2avkeQGLHc4HMLDhw+9Xvfll1/C5eWl/0gTT3jR1lOJZom/Yuk13qa6QRe9XqJDkw0k+ngampfqApLsR/NpbSfponhk4KNHj6AsS7i4uIgeB2yxA1hB0kNjeHGtwP6S5GCNHku/0rmZy5+0DKt9g6eRxmJML6DA82o0xPQNq+7J06bsNxLOWLtL+r2Fp2LtfKi1I4d/ugDV4aU4K2hzEI2LQeoamxgvWHgmRrMUluLN1ByaC7kf0XC7SBs5Jnfesuj1bSGnD2N0WOdErexUPitfptaHWB5eDr5b51FrH2lrKq55h4DYfEvLp0CvcUOYTCbiqbXHx8dQliWsViuxbG2uu9XLKNsMrNQEFQtDyBWa2gJdmJG5iqLwx9R0hTdJgfsWDgPSAnJTQtQhQFvQrWPWquxZwdKGkhJtUS4kWvC9qqrGHEGFHr5TNEV3Sjmn9MeE2r4gNvenFnatbjmGAgteGp4ylnAYDocwm80Cxd45553s3CCXA7lrJv/S1OJcxLWLfzVvpY+ud5QG2oYxwxO/x4T+3mw2MBgMYDKZgHPp+50pXmz7mDP2yZMn4JyDL7/8EgDy7nvmdFiVQW5EtBqtY/xI7wLTDG6aIExxWu9adc75eWmxWGTddczrww1CuTwvGZJ4Wbxc+t6nwmc1yuBvycio4aQGPa1srmDxd9qnqbXMKlu40sHq+QqgBpg9mW3veR0V26OIC/DHCbva+R2w2wLAO1CLugiPLAYIjyeud87SgeKQdRAcU1y4vTNYJ3xXdsGOKUaepA5ZpBfzsOOLxeOMHWbb1cewpbLhSCV4AEhfOPYupI06Yclu10YaFk/jnGuGN57EQRo4Ysmz8Zs4ZX0Z2jHFtY4v1qZmhzeNJ3WolhUsvlyo5Yhl73AOh0MYj8dQliU45+DJkydwdHQEX331FVRVBdPpVP1oV5qnpHHbly4g4ebzAl3rh8MhnJ2d+XWHX9VAaedzb2yNaTsvc37U8Frl5pSsqNHK2+lQhsUuoOkCh9QnpTZDeVlKp+XlcpRFx7CMEd6vNA+X8QFCQ/VgMIB79+4FJ5YAgHfGoryr4dfojYGmR/MwKt9L/JgqN6VD8HfrfCTxHW9bDJPkpxid2CeTyQSm0yk8fvwYlsslzOdzKMsyMJZyfszdtcfLlozalrZAnR8gfvdiroMg1qe5uFKgrR8xHpPWsFj5sXa16mIaHRLOVL5YXF9rcxs8OX3Yt71MyqPJEm3p0OiK6XeWtd2SPqXv5UBOGRwkuaoP0OwD2n3pVpzSb8saxvP0AdY5UOOJVFibvmgzb2ltqc25lrWUQ65sIuWX+JvaSK1zviUuJhdKc48khwLs5Teefzqd+uvVaDjiynbGWoTY24bcxec2FR8uyFnSfwtvPsQG/ptc1iHBughoEBjwWubPpcF6v5GlbE57XddwfX3tFwQ8clS6j5EbByyAda3rOnAA0+MzpTwW6MKTVOnG+Zsbs6SPWrDMlDEsBlq6sixhNpvBeDwWHaq4IwT7COPxqF1JMee/8V2j6yYAeSnWXjljBPFQ3pQER9xRPJ1O4Uc/+hH89Kc/DdIsl0v4T//pP8H5+TksFgsYDocwnU6hqirTPbz8K/vhcAjz+RzOz88BYPu1Wx9tnNMutD3wC/jpdAoA4J2avC9SfYNAjfnUAW0xYHL8NC3nDzS206Pe2ox9xM95ixtDJIE5lUYqh/+W8vYpi+UYD2MfTXCjgrRLHP/pMeypMZviC1pu6qhAAIDyqoSLX1/A0XtHcPSdIyiggMLt8DjYOisxOT4H7AkQOj/xOQB/T2xR7ByfBfj/oij8bswt+t3O1QK2u2qRT3c7bH15BYT3xu5w4zHFGMbpQUcwhqGjT1TGBe+ddIctydB492mdki6VBuNZ+/v+c7s05L3xG18txxPjE9NqO2JpOD+mmBxLjLiCf5KG1080qNKfbr+LltZNqkO1quDqH66gWlTNvjECzvV4//m9e/fg9PTUj9e2X4b3MV/FDHjSXEDnC3Ro5Ojc0k7YPmUdaX3DMH5SQI5xh9ZdOsZVWksxHX4Yxtf1uwDoQKeQoq1tf0lyIC8rZjuJraEaXbn6HR2PEl7URaQ5pqoqeP36NUynUzg6OgrSaKcfWeik4zBWx5ixFWmXyraCNCakNHVdN4yXKaDtLsl8Uvkx2iiezWYDo9EITk9PYTqdwmKxgMvLS3jx4gUMh0PP/5ItQfuYUyo7ZkuV5HicQ7Hu/GQgrR26Qmq9SemkbfhHm09TpwZqOoY2j3axQ2i0tknbx1z0tkGOnKDl5xCbM6XdbDHQeDRVlhW/hpfi5jyi8ZG0u91CY4pODV8qT58y3KFwdoE2skcMF82rAT8eN3bKqpVW6xiyzlXamM6xi/UFKftKW8BTDiX5AACyZZ1k6raTTtvF5VCDzYrzUApHDHduW6XS37UJKxdy6Y+l58r3TbfLocrrurjm4L6pdssRHKzCRpd5KCc/GpOs/YJtalGunAvvlx6NRt74rtFiAantYuMoJw0P50aqFC7JyIDKqYUXUzxLFciYgQ0BlWLsZ6qo0zIHg4F3pFFAZ6GlryWnGaU5F9oqybRtpDbiaXJxc/pQyJxMJnD//n34wQ9+AP/8n//zIN3V1RX89//+3+Hy8tIbLNDpbRWA0TCEZa7Xa+/0xPjc8U9xW2iQAPl7MBjA6ekpFMX2WDt6kgaXsSRDSGz+1pQ6qb6akVgqE/shNR9b2oXyllYvGq/NLZZyrPTwci0g9QPFkYM3ZbDFMcDvkOMfCMXGa876FaMP0xTF9rji1YsVjE5GMKtmUFe7UwGoQw5gu7vV7Xai7nbN+h2xxd6p5u9zBfDOMbozdhsc7pLFcopi7+zEXbV+B27h9kcawx5/4zhi0J2mPDy2+1W6KzbmiA3ieDLH8rtmXJDOCbgcCVfS0Xjntv1BaaNhsR2wmEeLk35rafh/cFdsDBjdvO6NNqdZnYO6qmH9ag31pt39iABNR8N4PG71MVAferc2b0syAA3jshKurfSpQWqd4mV1Bc1owsvj9FlokOZkxCHJfJyWPvrQAjF9w6pLHaJvpPaXaInpC5ie82yqz3k/aO8ol0s7J50Ld7dSHHVdw3w+9w5u6YOp2PovtbMkQ0t0p9orFZcqxwq8njm6vmQ0tdIQawO6ax8AYDabwWKxEMdmSi6O1cci2/F4PM0mZuyN6WYpiM1PEo4UTq1P+5y7Uzj5upQaM5iub1o5Pm1us+JIzYk5oMnuKX7Ixd9FD4udBCLxQZf25Xn5+sGBl4m/rfKBlM5Kd47ufEiIzXmxeVPK27b8LvlTuBHa9gudrzV7i9Q20pot2ShQb7CMNam9Y2uJVB9OnwY5awQtV8vH56UY3jY8oeWJjX0+T8VOLqL2sP+PvT95kiXL7sLxj4fHnMObX1V1TT3IqO4GCROg/skwkGHY19ggVmzZsOVP4M8BY4dhxoIlBlJL3UJq0d2oh2pVV5VqenO+nGKe/LeIPJ7HT5xzB3ePzMhX76SlRYT7Hc6999wz3oHyu9oRfUzxNie7SzHQFDLX6qmqk12DuhkI9WUVBh1SzzaZVyyE4BIyOUPSy3e70gca1KUE1QGWANkVOgpRHi1hGNIGn7Di4BNyPmOY8NMuAweQH2P1xRdfIE3T3FmXJAnOzs4Kq9dDV0hpuLTbbfT7fdy/fz9/fnJyguFwiNFoVLiYPGbFT6ysICEWk96nOIeAy6ClsSFeTUcNUz4y6sl4Bi6NaRorekdKlLYiPkThqANiHBlljtKjNvOdBNJZJdPTkYyj0Qj379/HP/tn/wx/+Id/iH/5L/9lnq7RaOD8/Bx/+Zd/id/85jf49a9/Hd0mzjdo9zIFdj/77DMcHBxgMBhgOp0W8OZ5Q4DzGeseZl4ercSfTqdIkgR/8Ad/gNu3b+Ojjz7C0dER/vZv/xaNRgN7e3uYzWaYzWZe3sJ1C14npyW56p7GnOhdKp80/3m/cKCjWfj4c17HDYn5fF7Az+ewojHT+lK23ZVGvpN94oK6ZLRGA3IHswysWmUlSYK9vb28XLqvxHLWWffKckeEy3AIOaqP+Djhs5qvsBgv0LwwOZIkQZKuA61J44I+GglWWOW/kaGwW3XjMxO/Hf8J2DHDCS53vl58Jkgud8iKvHJ3rLVLlo4kzoOw4jePv4YcT5z3Qehz9qwwdtlmmvx95nhOAUntO0uXZeJ56Ke8G5bvds2K37V7YbMsQ7Zku2Ev/i+bmhXxBquf74BV+majrfxzlQFLFMazDCyXS0wmk1wuPn/+HIPBIN9h5zqiVR5jX5deHsIHpf4t9es0TbFYLPDy5UvMZrPCwjXitVJP1XanhrYp1i6R6TVbQfocpO4i5SlvA72XMkvybH4XOz9Rgpdfdz/4ZCyXhVI34O2tg94sO8uy2WP0ZZ5Xylr+aTnStLJbrdaGXCM6IX2fgMptt9tYrVb4+OOP82PJ5/M5Op1OXgbvdx/4fB28/3y2URmbItaWpnSUj3RIWkzsu66rDJ1Z7eK0m2VZfizxkydPkCQJptNpjg/ZdHRygTU+Lv+DpuPx35z+NHz5ncka74m1eSwI0aFdZcu+8dGVnMcaDj49n0OZEwe35cfidC5liWsuXidY/NYFnH9U9Q9a8yPGT1FF7mlA9KfZSlLnkb4OFz1aC0u0NpehiyqyOaQfY/i/xD/Wvt7WHL1K4LQgN5Jo/RGSRgJdDWX510LKiuGLPr+Tq0yXTibnDekKITtY69JHXX4Pqx4NvxDfnIToY4p972JAY8Iux5Mk0hii3abA8znLtN++5753MXl2iamFGINl8saWdZUQK4TKQgiNlRGQshyrnpiyfHMmdqx5vlhmqr0vwy9ClJOY+ckVq8lkUtgBSM46F08MBRJ4tCORjtyaTCaYz+eYTCZqnhhDPwY3njdESYhtc6yyI/Nqx9FZ/zKN/C7x0BT9MnjWCdoYxPA0zXnADVSucNFK9du3b+POnTu4e/duITjV7Xbx8OFDvHjxopA/lgZ4PnJ4vHjxAuPxGMPhMA+K8rRlwMUrOT/lTiIAuHXrFu7fv4+XL19uOKw0B4hVh6veENDGjpyPWlrXkXeEM3du8WB/GXx9OmOIo0mbb7F4+EDOnapGtyyTAh6080YrX+sra5w0Y8nC1dXX1NblfInFaIEkXQdh892vKxR2SSYrdjRwdhkQS5Cs09IdrAkujynONnfC5m1nu2TX2ZLCs8Ju2kzZ8ZpdtIPeiztgC7thWVr1vey3kGheXrRShnzk+s0CjYV3xvO87y+e+QKxfKxkEFSmUfOwZzyfmSbLirtgs802mW3U+iZjdKj1JWtvskiAhZEmAPh84XyUrqLQdIEQHhX63soT45BxzXX6Tsd48TyhelxMG0LLDPURuPCsYhfwcmmBH1/Ap0Fd/RAqv1xySdM7fPaWC09rsZFVnqyrDM36bFhZHx83qadSGtKHpJzl+cfjMZLk8hoHnsc3bhpesk2Svnhal50WAlVsD84TeFm+MmU7YvUvbd7L/GTnDgYDNBqNwgk0Fo5VfXLaeMk+4mldgR1LJrh0vKq2b1U71IWbNU4u2efCJwTfqnMjtNyqc6guiOGXLtD4aFW54Mpr2YZWHvmujK1MebU0Fj+SdryrTMkDfLiEgs9nVEVml6VrV74y9r1FxxqeoXw8pp6yIH1KFm78naUjxYxhDA8t03eu5zKNJQN8Yx1aVxUZF5Jek+dVyiOI3hkbCi4GuI0O4mljVkpVdbDF4BZaV13KkwVSOa6L0VSFOvC4yvbsUt/FQJ3KXUyd2+qrugWmqw5ZT4yB72Pa3CnD76yRMJ/PMRwOg8r1AV+R3+v18O677+L4+BhPnjxBr9dDp9PBZDLBbDbL87juMqyKTyxoDhoOku6095oywu8wkzuO+Wp2iQuAPEiu3XmrAcch1sG0bSgzb+VdaFoZnU4H7Xa7cPw2AXdWzWaznA6B9VFiH3zwARaLBf7P//k/WK1W+S6D0PYQkCOUdhL+8Ic/BIA8OMvv/I0FjUfEjCvdhfvNb34TzWYTP/nJTzCbzbBYLNBut9HpdPK7+ULLJFz4J6dLzXHG+VKz2cx358/n88JiEOpL2j1i9QntDqHjobPs8lhuqy2+fqvbUJJ11+G4kDwzJGhN/e8z7KlsuoOSdnXI0xYsh5/GH+tyRFJZ0xdTzE/m2P/WPnoPe0ACNJoXPKJxEeRsYL0zNkvyzzzIRlVrnxnWO2iTzf8kYccRI9m4Vzb/x2U9hR2wSbLxGyimKfQbLuviv9eoir5P3LylcCSz2bn8qxJIlIFJsLFl7/ju0Y2ALH+fFfNvpFkpnxdpNj4j7oxdLVd5Hnou/wttkX3C+kM7nrjQJmy2L8dlCaRHKbJhhvl0jmyVbRxHZQ6VmGukV5Cu9+WXXyLLspzHSrlozV+XY2ebQPX1ej00Go38qH+uB/H+DXGIhjg3Yu1on7PHqkcrS3u3QUMsj8Vv6f7Q0WiU69bbHL8Y55WUt4SzlCVlgU4k2d/fz+9TtYDqtvwVlsy29ButXF9bSD+R5dKuSb7LU/YlPWu32/lz2slC9t1isfAuSguBbfiKOPD5yf99c4X0viRJ8gUadEoQ9Z3PZ6fZWi4dxeXUpROLSL9//PhxnkbjuZp9pt1xLevR8Pa1S5ajHYldBUJozKVvy37X7IlY0OaqnOMhvoYYndWit7psiZCdvdv2V+2C/8AFPhxj+CG3p2J4n+Z7sOQL+YIkEH/gdhiVzXHhNEGyg9Jx/1qd4OLNZcCyza8SytRp6Ww+/utLUxZIn5I8XktHctMnI0mvqAJ16B5lwcK9Ko3F4OaSJ7FtjMXbDMZaBfkcQ75noUaZVoZLwQrBrwq4mHRofplPc7RZCp5WZx0Tp45JtEuCX+vnqmWFvL8OoeSDup3UVdtYhbn5aH8bOFoKdN3zRwPNGKFji7jxXvWYOsmDuJJIRyJzJZMf2esqM7RtMfml89RloGkGnqu8WNAUNaqXH0/M67FoOLQvY4z8MhDbFyF4yzSchrhDh9KSwTMejwvHhjUaDZyenuLs7Ay9Xg8HBwc5HYbeI+zDlYKD/DjeWOPORWvaMx4wWy6XuHfvHvb39/OjHtM0Rb/fx3vvvZcHY6fTaW68+eaiDxeNl7l4qRwzCfLu7BC+HDp2IfJ4W8ZnmXKsvnU5guR8l2OlvU+SJA/Ayn4PoV/N0SzH0Hqv8TXJ94B1EG01X2ExWGDWmaHT6KzLWF6+B7C+t5WXwYKZWOHybtmLQGW+y5XuhG0Ud71qO2KpXN/dr/kn3xGb4TKwy8vOsssgbIZCoNW8OzbzOAOKkVYb5Lts87sWbAUug42FtBlra1b8vfFdpLE+C7tXWd6cRpTy5PPCs1VWLFNp98bxxKwftL7NsqyYhwVtkQGNRQONZQN3D+9i2V5icDrI9bEQ8DkeqRztPnoLfDpBiBPbeubi4XxuN5tNNJvN/L5Fl11E7118idLwtFUhROeUekiIfsnzc34Y0gcUhJc7K68bZPu4zK/ic+A8IUnWAenlcol2u50vLPPVY8kaKtdqg5ZXo28p40heu+aZtrDKpxvLeaLZEVadmu0RAnXZyBq4cMqyDN1uF+12G71eDwDyU5foLl1aPJamqRnwkOVrdFoGbz6+Gg2V1V1dtgDnEXJuuHD12RexfWDp+L76rLrL0FYMb5Z1lqmjbB+H1BEjMzhekieE4uvCxQV12GNlyw8BjSfLd5YcjsUnxD6idL5xkTzWJa9cz3xg9U8VGV2mbh+E9G1V342vzS58LTlcho7KgNV+WQ8tWuLvk2Ttm+U2SFU+GQoxYxaDQ934x+Dpm7vaMx/txfDZ4J2xVzXBrc4rcxeiBmVXLJVlGi6D0rqvpA5heJVw0/B9Da8eVFGirfJ8sG26l4ofGa91lEt8kO9ekE7Fvb099Pt9fPHFFwDWuxL5aj7Xiq6y4Ot34pmxK6lj6/GBJpRJMNOxzrwOq69czgTpINw2hCryoWn4GFFbuEOG6uT3XdCdWo8ePcLLly8xHo/z4OSHH36Ix48fY39/Hw8ePMidmHwBQZk202ensw4QTSYT1WFWF3Blr9VqodfrYTAYYDQa4V/9q3+F7373uxgOhzg/P0eSJLh79y7+7b/9t5hMJjg9PcXPf/5z/PznP8fe3h7a7TYmk4lzPnCdwnLyURCVnGHyGD6iDS3gx9vlCkxwhxM3Jny636sA8mhmcjxzoP7Q7iuyDDY6Wm8wGBTGju/Wo7Er47x18ST5THNSy/Tjx2NMnk1w+/u30b7bBhKgsWqA7o5tJA2sN1Sud8Ym2UXehNWVJSjcGct3xRKwZ0mSFI831v7lO/k7Kz6no5QBbNwhS32X9w+MvqdyY8HKk/m/53OfBTA3ArVaIJMFPel3/hzIg+kb79h3cyesfM93v1I6ZUdsvmOWt0O0jbc5rwcCD4HvRpszACugfd5Gb9XDP/n//ROkaYpms4mTkxN89dVXlexTwoNOFfDd/cTzEVwH7yS8+/0+2u02zs7OVMepdJhlWXERkpQr9KzuNvmCIzH1a05ZagPnvS7nPNHQcrnMTwi5bhmoObpj7m/0lQms5RrtjKWxH41GODs7y/vOd/oOB6Il6n9N7vBAn3bnuVYPv9t+tVrlJ3rQmNEuTzophxbGyfIlrfCTK2LHW94VLXEPCeJoer4cI5rbXNeTY8LTSByobaTr3L9/Hw8fPsQf/MEfoNfr4dNPP8Xjx4/xox/9KNdzSBfmizAl8HG1giRae11902w2N3RXWiRB4ynnAI2rVmbomMp+ItrQyqjL/pM8rkp+7V0I/cXWE8qXrblcJ0/V2iftdDkXaJwlb9BgG36s65YpIcD5SAxfDAlkxQZLygaadqGvQ+dcqK/nOiCWh4TowZZ/qKof0QJ5nYEGhDc/AdHiEbdu3UKzWQzX0Qk6JycnODo6KoUngavPq/ggt30CwasEhdGNVeo4uAydULAUEPlZFmKVOB9oiqgsXz7jea13IYrYrjLSXYC6hOJN7+MqSqlUMLcBcg648I0VylY5oWVZOGpOZ199VdNKp0KZenh+K7jCHfn9fj93DpZx4of2cWy6uow9H/B6NENL6xMe2JJygX7H0o4l+65K8a8i1+UuSSqPnA/aEUHtdhuz2QyfffYZHj16hKOjI/R6PbTbbRwdHeGrr77CZ599hsePH+fHCdNO8SpAjhAfH4oBre3ae04rdIQbOXVv376d79AdDof49NNPMRwOsbe3l99zpekgvF0aPiG8XdI9jRs/HlmW4TOMyDkX6hTi+G7TyeKCWCebzEffuSNX00WtfBovkr/5HODOQ467xTs5/fhsAI0/WQ4R0wm6AlaLFVazFRrNxjrwurzoh4udsoXAKguKJgkLema43BXbuNjFuro4Tji7CAI1Lj8BbO6OpcAuq6+wK1bsenXttOU7XXme/H3enGKglkPMPbLOZ1pwkn3Px1kLXGppsuJvSld4to6ir4FipDIAK/Nm2Lg3OMsug61WwLZQt9IP+a5Wow+c8isrtpnwGQ1GWC6W+RGzh4eHmM/nuTMlVgZpPM1yDtat62hyoIzNz2W5FURz6RDyZBefrXvVThyrf1xjQmld75fLZeGUm10BjZ9rz2JtQy5/ms1mvujO5T+xypGyi3YYk97Nj3zkZcmjLC0bl+iY6/E0Zrxe/t2nZ2l4a3Tts2H5SSgu2at9t+hXk9M+oLolz+D/PKiyWCwwn8/R7XZxeHiIt99+G/v7+1itVnj69Cl++9vfFviHBUmSFJzXobqkS9+i5z7b2hrLkHpd712O+7r5nYW3Ned9/FjmDbFDNJ2e01JInRJcPp4YHqXlkbRjjX+WZYVrnyTP0PKG2m0uCLHhtg1V9BPXeGu05vNJyHHcZh9U8S2WrUeTmWXK8ckOK19sfS5+EepPraIDhuourr6x5ruPHkPoQ8NPLogHNgPKVHen08HBwUG+kG08Hgct5LbAag8fh1A6ccmQuuZlTDlV56uPX8fqUBwq3xnrqrwOIRGiWIbUVcfAcwLyHSu0LaYcAtsSQFXL9eUvW75mKO8qbGNs6qbtusqsUv8267lqkEZ/FcWGr47Ksrj7sYHiXbSUn8qgXVbtdht37tzJ0/D6XEryVfQtN/brABfeIUqJBuQgiDEqLRm1jT510WCdc5Dv9JB0SkFHDo1GA+12G+PxGD/96U/x9ttv4/PPP8+DsV988QV+/etf47/9t/+WrwSk+0tdO499Mof6OGYu+cp1OXPknKKdEsD6+LbBYIBGo4FOp4P3338fjUYDg8EAH330Ef7sz/4Mh4eHuHfvHk5PT/NdvD6Q9EWKPd1pJp3oXPHnfasF0S2QfUT5+HPJF13KboiTuA5wGVGx9XF+mWVZvruGds8TaI6gkHGle8/4sdWWY8syQq3xDMVDyjcOhIt8vpwusZgskDST9d2xjSQPjDayC9qjoBiwufuVf8rvriFiaZLkIrjauKhHPudp6ahi/p3yZJdtzbIsrz/JWDkZC8zyI49ztC7exWyV1ZJm+ve8/2VQ0rUrVrzPMvaclbfx3Pq0dsJmlwFPvjNW7ojlefiO2LzfOJ7KpwzCFnDhbYDAfZkhW2R4efQSjUkD3W4Xt2/fxoMHD/J7ssvoYsCmg8NyEJUJmJSFGHmSJEkeWORXWIQ4nfhzCn5p9ZflvTx/TBkhfU18V9vBp+nJ8nM2myFJktqCsVVta5/szbLq10HQOPf7faRpmt8xTH0gF4qGyB3gcocIzUF5Fz2l5fczuxaxEV2TPURAwXPNJ2XNW/49NBDDcZC0S8+4ozbUUS/vOeX4hNgDsq2aDkf1yGtISK9tt9u4desW7t+/jyRJ8Id/+If48Y9/nAdjfb66JEk2dp6HHPEdoitSuZozXDrCOZ+rYqPxEwJc+Grzs6wu6svLdfGqzmsXDqHt8dWpBQAA/T7QqsBpno8J2XC0iJ3qHw6HG/OXL/LYZV/lVYBFA3X6d/inLNuiHVdZGs+0bNMQvcM3D1xzMKaftkFrIX6/kHqtuaCNW2g7QvqmzDVPVLYLH+udVpfF6/nRwy4c+/0++v1+XtbTp0/zjQpAcedtVbDmEX8WM6dC66yLH1SFUDzK8PbKwVjAP0Da8212rrW6rk5G71IcNOZaxuAKdYaF4lYV6phU2yj/Jik014mrz/irCiF84DohBKdt4O1S2kLAEnqxDF8a8lRGq9XCw4cPcfv2bQwGgzxQdnx8jLOzMwyHw+DyLXyvAmIU3hDgCnWMEI55H2KAVjXCJcQ4JGPTqc5u8Z4Hq7X+oqDs0dER/vqv/xqPHj3C4eEh/vRP/xSffPJJvmCAgoSTycQ81szXjrK06jMctGfSCCcHNB0znCQJnj59iizLMBgMsLe3h+985zvY39/H4eEher1enp/vRLKOtaT33HHE56imX7hW50sDQ6NLn8OHHHWyfi1fDL+uU2GX9bdaLbTbbUynU8znc6/DUAO+S5wcxTH4ugw/lzPWZSjy31qbLJqSczfUicb56fTlFMvJEo20AXSBJE3QyBrIkgyrdLU+rjivlBXSYM9E8DVJLu6MvQiCJsl61ywSrHdqJrjc3ZqxvCtWB3tOu2KT5DL4miSXAVbpbMmyrBB45UHXPB/VIfrNF4Td2DGb8a/ZxrPgoGymPGcByfydfJ6J5zSXV8onS5tlmb3blZ7zoKwRjC12BcPV6keOI6t7ox9EcDpbZZifzjE/m2M5WQIr4MmTJxiNRjg9PcVoNAo+VtgCTcewdEbOm+rgeVX4J+EyHo8LwVR6J8uUdfkcpDw4U1Z/42WGliEduC7928rry7NarfLjb2OdjC6cy77XcJB9Ric6xBwhzMuiANdwONw4ppoWhRHwo/p9Tk/+3po3IfKK56OALYA8wEJlTKfTXIbz/kiSpLCwTsOVf0o85fywdDStbdZcc+Gh1W3hGaKTyXnDr74YDodYLpf4kz/5E9y5cwfvv/8+er0ebt++jeFwmPfdbDbz+srkcd5l7D6Xw1jjiZJfSVrT7g120aKFm6Xf+XQ6rbwyerSrfF/eqrZWWRu7DO4uPdolW6055pLd9M+vJwm1Hy2oy9YJKTvUX1B2/Kz3kp+E4MA/y+oMLnqK7RuJi5SnIXmrQh3lhMqWMvW55I1VbgyvcemBPvkQQqtykVMVsMqgK+M49Hq9fNMMwWq1wsnJyYbPlvSUsnNDmweh8ku+c/V/iH5RBlwy1wdl5WhsnYVgrNXYMsaQld4n4LS6ywxCiGMpBA9XOdpdKrHtC1WMX8PVgVS8dxHK4lfHPK67b1yGUQy4xq3sPApVRLcJLp4SY4gQH5c7ENrtNt5++200m02cn5/n6Z88eYInT554cQmFMoI21JiuApaTx6W4ufLwNJZMsMrQyiybt24owzu0/uAONm1VO+0KPTo6wk9/+lN8+OGHaLVa+PM//3M8ffoUe3t76HQ6mE6nAJDvMqyiJ4QYWaFKmeZcA1Bw1HFnI/VBo9HAs2fPcH5+jq+++gqHh4f4N//m3yBNU+zt7aHb7RbmMCnYfLcGd3STw5Mci9oxfQQuHhNiBGk83Eor7xKWdZU17LcxJ7JsvVjl4OAgv9uP796RYI09jZe8z046HjXDUZtHPoevxEd7JyFkt4d0FMc6QrMsAzJg+nKK+fkc7TttoIH1ccXZ6vI4Ybl7VBbFArFJgwVdL97lxxU3knVQjYK0tLs1UYKy9E/1id95HsA+vvhid691bDHl3eifhH9VZAsPEm6+VH9v5GHf87ozlj4r9jmNlfzN0xWeZfqz/NO6M5Y/X4l39L/arEe2Md/RygPG/JloU+EZ7wuGY7bKMD+ZY/JkgtVshSRN8OTJE5yfn+fBWC7D6rAdQngs5/PyfVWbMSbwAmBjlz+9t8rx9ZEmO6r2aVknqvXOWrTkogMuE2hHKM9b1/jVBRx/CiTI3bEhsprLOr5zFUB+dD99p7T0nXRFjd61enx96AqESNlL6WjnCR21S7vd6JQL0rGkfRUje334UBm8fyz9zVeXy04JxTNE36N+SNMUo9EIg8EAP/zhD9Hv9/GDH/wA9+7dw/vvv4/BYJDPCQp0W/Vx3ZfAtTggRo900bLUTzlOWlBFpnHVG2JXWu94v7js1bLgqjsmf0i6kL4KAaufLP+qpN3YfrTmaZIk+QkK9Hy5XKLZbBZodlf4/XVDrIyWoPEki5e4+jx0vGPx4zqDdm95XfZrqI0eC6Fynn8vS9suGVAGt9h0MXld+ol8pvlpfHglSYJ+v4/Dw8PC88PDQ7zzzjt5GmAti37zm98UyiO9zXcFAK9PtiGU5kP4eBXaCPUN1clTY/QKS+cPgaCdsbEOMgtC8ocwDMtYCzUeXRBqOIaW5yI8SeTbcCa+hnh4PQ5rqDLf6+AXZcA1X68anzocc1qZrrpiBR3dYfjJJ59s4DoajfIjLqQDqophdFMgVFkiKNPWEOdRVYN4l4CceuTA5jS1XC4xGo3y30dHR/kuiiRJMJvNsLe3V3knkg+se++40cQNbAuks2Zvby932i0Wi7yt3AlJjkrqp5cvX6LdbqPT6aDZbKLb7WK1WmEwGBTmfJZlhWAaOTnpfrbRaJTv6tTapOFN7XMpmFJ/4Y5XVx28LmmY8v4PPRZZc6pUpZF2u41+v487d+7g3r17mM/nOD8/LzheQ4HSS2ejxmMsh5zL0JPtDpE9ZKA1m82c3mT5HH9Oa9IhGgO58brMMPpyhLSfYv+9fTTaDSABGquLnUZZst4xi0YecMsDZnyHLP2m74nymz4pEEtBWVx+5+/k87wu/h0sQMufZ8p3lkf9nbEgrWeXbN4PrmdKsLKQJhPvKH2WbX5Xfm8c5cvfZfo717HE+U7YbDNQS/+FYOllAzfbKZ4VeDWr39UmCsLOXswwPV3viG+1WkjTFL/61a/QaDQwHo8xn8/zuy9joYp+eB26dVUdmrfVtZtgW22Lcdi5gjNcJoU6uIBNGSnLpDTXAVo7SfdfLBa5nJC8P8Yx1uv10Ol08P3vfx+dTgdffvklzs/P8ejRIzQaDbRaLSyXSywWC3Q6HbTb7fwuWC43uSOTdCmu/1AajcbkTlstACDH4Pbt27h37x729/cxm83wV3/1V5jNZoWdsVJviRlHTZfk/Sz1TS7zQ04ykc+1ciQuMl+Mv4vw5WXxBWzz+Ry//vWv0el08Ktf/QqDwSA/2jUEfPNTa4fPSarp9lrf++a7L42sVy6Mdh3z7oIyvJlotkx9dUOIkz8EpM4amkem5XdGy7Jd5dLR69/+9rdxcHCAN998M6eHzz77DB9++GHhih5JXzF+lSS5vLaK53Ptzt8m1Gl7hYI2PhJi/PexfKUs+PhTKGhxkCrgartrfLfp56yjTVq5VXCS5ZYBn1+E+16fPn2Kk5MTHB4eFuTL6ekput0uut1uVH0W39+W/nnTynWBxaer4hIcjOWfPkalvbfeSYWH1xOCEy+/amfEMOCyhmmI8JDPY4TMa/j6QYzSGQp1KB11zBEqZ1cg1jAlsNrkYuohfIAb5DLQwoUGd2BQWVmW5c6OFy9ebJTN75fleUJA4uJqhzSA64BtKc3bghB6kN/rgBi6tPJo+a13WZYVgrGyPFrF12w2MRwOcXx8nNPdwcEB2u12vhupDnDxGvmd9w2fTz4aIfzJwZgkCabTKc7Pzzd2mpCRTs644XCYtzdJktxhOZvNkKbpxh2hUuFO0xTNZtOcYz56sniKxds0R1xIPRy4scH7OIRH1OkQSNMUvV4P+/v7uHPnDh49euR0qHN8LdCOOa6iu7ryav0lxypNU3S73TygpN3bGNLfls4v500hXQbMTmZIpymWD5frQGyzgQwZskaWByvzXbIs4JmsLspsrANofCcs3xlJQVvavSp3sVLg9RIlkYYfVYzMDJgWdrsmm4FVWQe1vzA2niBsXoZIVvWY4kJ/yTRZ8XeWXaaVAVlfIJZ/5+XJtDIQu5HHEYgtyBSWNn9e6IzNMnIclsBqtMLsxQyL+aJw3+STJ08KzlTJg6vCNhxRrvlZpjz5LMRGpkAF6awh/FnrizJ8neeV8sWFr6W7akelWo57+s6Ps5VQt/4bAxbeUt+hhZtlghTAeuFnr9fD3/t7fw97e3vIsgzPnj3DV199BWAtb6n8NE3zAB2dSMGB6zv8tAmfr4r0zxjodrs4ODjAw4cPc71L9lHZ+e/ylRGE6uQcl23YLWXmn6QNGq+nT5/maWjBIOA+KYTyu/yM8nmMb8Tqvzr604WrvJtW4411+0TkeMTm03ByQZX+i82r0ZxPjsj3mo7Ov8v+p9+LxQJpmuLWrVt48OABvv/97xcCpl988UV+tQGvy4WLVg991+6D5DRlgcvPIPunin2igUv++WSJC48Qv0SV8l31WeWH8HfpR3D1TxncY+ePS5/j+LqgjG7nktmhPHhbfFLzafiuw3SBph/KMvjcPj8/x2g0QqvVyvnJZDLBaDTKT5Lj5YaAr099fNKVp2yaEHyuQ0cOBU328Ocu8AZj62x4SEeHQIxBGIJTWSXawiMkL4cyd7DUAXUJ2TqFdUxZdSsJNx1e98f1Q+gY8HQhilas0JR5+TtLieBHhm0bXErTq0LHV83P64CYvg81CqSjjDv0NGOU757lQHd2lr1/PcSAyLJMNXDpfcgzAums6/V66Ha76HQ6GAwG+QII3n46TjjL1veoff7555jNZuh2uzg7O/PWw2G5XBYch/zILJezQb73Ob+A4k4nntfiNS4HMDns+c4cflyhD+o6Amw6neLZs2fY29tDu93GnTt3MJvNcHx8jNlspq5K135T24Di0X2W4i7zuRwVIc5nnpacb41GA91uF71eD/fv38f5+TmOjo7y975jkMs6n6XzOssyrKYrnH98jvZhG3vv7yFrXezuXjWQNC/SN5L1Tln6TNa7ZpNVAjSwPoY4SdaBtASbn9Zdswkud8I2LnfEJklSCP7ywHDebvY8/w39WQYH7cZ2YyZ/ZhvPnLthtWc8MJmxNFnxd5aJoCXlX11+AlgHUum5yLNxFLF1TDG7M1YNvCpB2Pw7axevf+MIY2onkfsKSKYJmi+bmJ5OMZvNAKzn63w+x2KxyFehE0+K2dW4a7qNC58Ym9pKS7yYePjh4WG+IIlOeJDBBx6wLQsunVq2WfI0F/+mxWL37t3DYrHA2dkZkqS4Q4na49MjNLy2SR+usrMsQ7vdLhyhSad00MkJ4/EYwOZiKR+QTKex/uY3v4m33noLt27dwscff4xf/vKXaLVaODw8xNnZGabT6cZiPSvgytPIcdToJ8T3kmVZQf8cDAZ4+fIl7t27l8vN5XKJ+Xye7zChI6djToyw6FD2HdFXo9FQ7xnmfSB1A1eZUifh/WGdbGLh63su53en0ynMNzl+ZfW2bdtcrh39km+FyoMsKy4Qkbq37Bt6XqWfXBBiJ+2SDLMgpo/4Ag1uMxEkSaIu4iBdud/vo91uA0C+kJiu06Hn9+7dw7e//W18+umnOD8/R7PZVGVEqAOfFtsSHlXBJx+0d8QrLRuOp4vFxVVnmbJ4XskDJdDpRwTS/rR4blmoeu9onf53l/5TBSSNxpRp6Uwh+erqG0sOxuLDP62yQsofjUb44osvcOfOnYL+0Wg00Ov1NuoNOUXD5WOwdKcQCKXvUJ+iC8rIRBff09KWPTnBBUGe9xgnZF0d4WMIGoFYzN+Hf50KTYhyvW2FMRS2waSusqxdVAivW1HdJm2FMFLNqc8hBL8QI9WXv870IcaJ9js2nyudS3hLAy2kPAlcieZ11klPmiKiQdX5UxZnyznmSmOVUQaHq5QLrrp8dBTi8HGVzf+1/JrzzVImQ6HsXLSctmVxSNMU7XYbrVarMOd4m6mdq9UKL1++RKPRwK1bt3BycmLiqjlpqDzZj6FtkA4hDegddyzIsZLlSCecVbfPYKZ0Ln5G9ZYBcsjO53Msl0u0Wi30+30Mh0Pv7hqfU8MaB2s++MqV76V+azkPaee0dApZ/eoy1iwnJJVlOnVXGRaDBRppA8vZxY6Bxjo4iiXynbFJkuRBzaxxEfhrAPnO2MZlcA4Z1jtfKcC6utzdCqCwU3adPLvcXSt20W7slOX5ss1yqTz+2/cs7xdHZFbdAXv5Uv2tHd9LeOfPZECTByu175n4DuMzE5/sHlitnPwoYnFfrHc3LGtYIT3LV8ivtefiXYoUyTJBMkmQzYpHhfLvIfcra1BGHw5NG2OnaxCqO0qZGBt0oOPzl8ulU3e1+LdlH1TVG0N0GXrf6XRyGuDyjGQ8lac58a/KRnTxXJlOjg1wGYwlxxPJwE6nk8vEGKB7cmmn6RtvvIHj4+O8n+ikD44Xx5vLTpeuTu9CZKvVH1x/mU6nGI/H+d3IdLw/1amdJqHh76tTK4PjzgMcvGzZLqkv8P7in1wH095bOIaAS0flPJROZtKOenfpnFXxi9GTXHnkOxdOmi3J69TGjZ779MYqfF7DPaQfrDRWe3zg0olleaFg8YpQCG2L5Pvj8RjD4RCTyaSgF+/t7aHb7eYBWi4/QvC2+kbjCxLvsnaQq35gMyjDeZXkLxYeMfZGKK6u8l19ydug4SB1Plm2a15Y853jFDtOFg3EQKhcDOELdZXnylOVll3g42tVy3ad/qDpa1wOUNAVWC9wnE6nuU8JuNzIIHmK1GlCwNfHof3B8ZdyLbbsGBp36VIhMu+q9HSCQjDWtxqeg1z5YqXTwFox6iuLM8iQ/C58qkzm0AGy0sWsKKgT6iSuqybUXYEYgfQa1hBLK2WcQl8nsNpuOah8/CZJklyYXyVsY/w0Re26+V7Zun2KxzaV0lDjwoeHVMQI+P1eGtAKPzrCLE1TTCaTaAdgLGiGFTmOY4xICVmW5XfE7u/v5wEwCvDRziFyctL9bD/60Y/ydHSfGqDrXxwn4PKuN3KmcnqiftR4g3T6aQY1r2e5XOY7XWazGWazGR49epQ7by0cNYMZ2NyVwQ17V3spr3Qulp0jVP/JyQk++ugjdLtd3L17F3t7e5hOp/jss88wm81y3smPHrNAM/Ct/vXhxnVc2WZucFj9vFwuMR6P8ezZs3zXuctB4cOR+p6frsBxkc5F2ZbFbIHpyymae0200Ua2ytBoru+LTVbJOnhGwddGAqRYB+4aWb5jFhlAO2IzZJdHEV8cX5w0LvBP1v+FXbDUtKT4n+9qZWny4435e/EcKL67bLDZhRvHJl++sPMEBWJ5uky8o/RZ8VnBaM420/Hn8hn/5DtfkWHzjljld6Ec2SaJM4rp5LN8ly7hqgSWs1WGVtbCm9mbmK6meDZ/lvNk2iXB7y6n8i2d67pA8hUXVMXTZ19zfwLHK0nWx+1ru2dCdAme1gL5TlscJH+TTqLpGJyfJsn6lAvux+Dv33jjDezt7eHly5eYTqcFh3yIDKtLX40tg/SMvb09AOuj8WgnMJX18OFDfOtb38LHH3+Mx48f5+PocvQRv2+32+j1ejg/P8dwOMSbb76Jly9fIkmS/Cji1WqVX6sAbN5tFrIr19fHkhb5My6LSfc7OzvDeDzO74ml8bSc82UhZL7SP7+zONbxzU8eoZNY6B2/0sLS3UnOu47d9uFCTuRer7cx56jekN25dUOojskd3ly/kTwqFN+Q3bSW/cHfxzrIvy4+lBhewenbR3eUhk7NIDg9PcWTJ08K+jAtPnjvvffw1ltv4cMPP8Tx8TH29vYKO9Ln87mJJx9jjnuZu3/rGPtms4l79+4VTjEi/QhYnypAc91Xn+uEyFiZqPkvJA4yjexDyTNdNFSmLyVfkzhx3F1lVAVrx2osLnWCz/elvaviZ9P8IJJW+Px2laONaR36wWq1wrNnzzaeP3nypIA/LaJrt9sbJ124cJc4+9LEgPTtuMoMqSfG7ybBN9ahEHLqGy/fh693Z2zMZKBKtXQuJ09MnVqaUEIPSWcxaytNCIQoTFchROtkpl8XJU7CLra7Dka/TbD6LEQAWGmrjEOZ/vLxBU350+a6T3GrOpYyv1WfNDJIYSjDV6Wg0Rwcrrwu/EPrr/JeQgy+Vl7el6HKhVW3b/zqklMcpPOLDBkrGKopG77fIbBtfkt4u2Sy697kmDaRoUoBV+pb2oGp7Qbmhi0/Rjx03tBulNi+5+MZQl+tVgvdbrdwp4nlOPABOfI1miqrvJcBMq7m8znOzs7yFai0K4pw5U76kDLL8nhNBloBihBcaC6TI0nyK01vteY0Oa75M1c7NN6YLTMsRgskjQTLzjIPflIAlXa+Fu6QTS7KW7E6k2I9CZL1LtoEhR2yhV2xF0HbjThohkL6LFunUXfW8l2xGePpYkesczcsBQs5Hj5yke+zze+FMckuP+URxRvBSiVwWdh5mnk+ZSA2yzbS8OfymdWeQpsk7mCfPI3RtmyVYTFcAEtgvBxjOp5iOp0Wdm4Wujcr7lTiQYmq+ps1b2Ns3BB7s4o+Hep81PgJyTntGgKeh9L7+pSPBa+L183T8HSazuprD/1zWc0XPGVZhl6vh1u3buWLXci5HuoQuy4bk7eLfksgHaTdbufHdYf6a6jPv/jiC2RZhm9961uYTCZotVq5rkNyn2gEgKkLhNRJUNYGIJ03yzKcn58jSZJcVpbRbWJB0pwGVQIBSXJ5fUWof4wHJlx6KE8jeRK1x+IDVt0h70N0xtAyOXB7oNfrbfCyUNuwDpvXyqvRpVWfpYfFQkj/1jFHYvwmPh9riPwLsXs4X+Ppl8slJpMJnj17lvPKVquVL051XaXi6yvL/tTKivUryPchujy3+biPwMULfD6WKr4CrQ/K+OylPHb1pTWPYunfJ7Ms35pmC8bWVxV8elqZ+qvIcA1ifYW8vy26lL7UGD4V8p7joPnfQvw7lr+wzLzw4WvlqTKnffXFlh0yt0L4lOu55ZuxoPQFgZrywc+wl2lDDTheliakfKtWY6EqMdYFdRjyr+E17DqEOFco3XUBBZ6kY6IOR1lMOinkeX7aOWgJWQ7yPhRyHnGhMZ1Og3Dj+LzKUJYXS6dJSD/ReMjdl5qT8aqBnC/9fh9Jsj5+ie/y0PSAOh3SfDfoVQF3FjWbzfyOMAqmLhaL4PHg/IN29r58+RKLxSLfmTAej/N0ZLSTgt3r9fIxoJ2zgFvn4Y621WqFyWSS/w5ZQc0NZo0euQOacGi1Wuj1euj3+5jNZvi7v/u7POAsnbyueoFLfkX9TPXF0lmog9GFT5qmGI/HeTCW2kl8tKyhrxnOlpEfGihwGYvSAMuy9U5tOpaSHIuyTbwPNccTL/vu3btoNpt4/vx58NgX5Ncsw+TpBKvpCmgArb0W0m4KZECSXgRW0wt+KO+QbSR5kC3fGZusg6zqDljj2bpw5R9Q74t17YqVgd2NXa9ULsTzUNC6NCJwqT3Psmzzu/jNn+e0sWKf2Wbawm5X385YiSvH0/rOcWF1h+yIXc1WGPx2gPlwjsezxzkOSZIUdnnTc82ZQcd9LxaLK5dXEj/CS/6uo3wOLl1ePideQPKL5BuXKS4eEcLLSU7w+kkGynTyu68OzsNWqxXOzs5y/SBN01yWz+dz3L17F++99x7efPNNjEYj/PznP8doNMJ0Ot1JvZn6n8aE7yCS+E6nU5yenqLf7+Odd97Bixcv8rtkLaB+XS6XGI1G+C//5b/g8PAQ//pf/2uMRiPcvXsXg8Egv383SRJMJhNMp9OCH0gGAfmYhTrQJF68D+QzguVyieVyiRcvXiDLLhdfxJ5wFuMwpfQkP+XOO833xvPwNLxP5HvMPI+uAAEAAElEQVS6v5n0WqsviS643kPB87JA9xBrQQ/q513ziS2XS/T7fXz3u9/FdDrF0dERzs7OcHp6WrhvOQY0vur6zfNp76r4Gb5uIGlMfue/5dha+jqwtodWqxU+/vhj3Lp1C9/+9rfzY4sJJpNJLhN5ANM1fnJucltU3iWt5bHwluAKVnCgxTnk97cWaxOf4PqUxC8UQni8vIOZ1+Wid8ve5O3iekYs/jE6k08vcuXV6rXySLooy3Nj+ImsQ8p1K42mu2l4x/K0ELqwYlJ1gFWupENt/pB+4gO6EkCeWLMN/m/xjxA/UKxcCrENXHiFptdsGk1WUztj7UAzGFuFyEiB9KUB3J0T6nzTHGJaWs0pZeGwLSXQhWeMsHwNr+GqwWc0uIDTdohzZVtCIgR4MMMFVZSkkLkeo3zwNFaZXHhoSo9VHv+MaUeMY+SqoAoNh5RbZrxJ0dOMKe78kUHeq5gfhBPt0JSr8GJwsOjZlX6bQPTpo1Man2azmR8BQ89dwB3NUqmjFfW3bt3Cw4cPMZ1OMZvNcHZ2hslkUrh/FcCGEktlxBprIXxXjq+lN3HggWIeqJU0XYUfynSSlgj3UN4aAhz/6XSKwWCwMdddThmJd1k8rDyh4+9yzvAdSfK9Sw7L9A8fPkSv18t3BVBfaY4RU+/NgOV0icX5hZMpuZh/aKx3xgLrHayUXWt6clku7TLNjyk27n/NsgxooPA+L/8i8Mp3xtK7vP8TXO6uzcS4XOQt7OrFOj29l7tlL5ti8BgleYEO+PtMScP6T+6OpXFQg7CUhz9zffLgrHJnrPxd2KULhpuCPz+yuLAT9uK3GYjFZX2z0xmWoyWW0yWypdu+5HdwA5cOPy4fpNNuY1zE723J8BCHh+WkjHHsufDXeAd3UmiyRuIvAzT8mdavljyX5cjyYh1E0+m0QL+8LfP5PA8kuo7pd/2+SuD9yY9gJaB2pWmK2WyGly9f5um4wymkDymwOBqN8PHHH2M2mxV2DxM+Uu/R5LvVjtC20qerPBcdaeVpeIToAq48mj5m4Spta8t+ozS0wIzS86sWpH6TJEl+jHGSJOh2u1itVvnx0lZ/aGDNda3e2IV425hLhBvNjV6vlweyF4sFTk9PkSRJYTOKdvQyfXfxHO291l9VIFQ/rQt8dFG1/tj8mh1B3+WmIu2KGp8NyMePeKY8wlcuWKRyQ/UDno/n1fo4dF66wJLno9EoPxmJ+o/Xq9G+D2J0pzL8VQNLB9LsPRd+2twNaY8FkieG0rpFR5Yu5eMxobSj4Wjxs7L0GJM/hD60cqTckXPVqovaH6NXuMDHa6wrqzRcY+jHl07Oi9ByqtYf03cxcsHXnhi+IvkglevCvfTOWBfI1cFlV4yFgnXvmbWLNhau2lCKnaxXDTETehfL/zrATerDXaJ1YqLaHIzFs2q7XHxAGt2AvqOXgAwMS0nQBMW2eKVV/3XylDrrlsqur2xOc9Z7eZcMrVLfJmRZlhui3IjkOLTbba+ScRPAp9xTf9NYhOozFOSSY0VOrHfffRd//Md/jKOjIxwfH+PP/uzPcHp6ilu3biFNUwyHQ5MPaSvvpEEljUoZ0NfmhTQofMYHsF7lPRwOcXh4WFgtzXe3cvykwzDUEOTlWHSnOSXKzG/eX2maYjAYYDKZ4ODgIF9l6usfqx11QlneRfcSj0ajXD6E9Jts82q1Qpqm+OCDD/DgwQO0Wi0cHR3ho48+QpZtBmOtsaPny9ESk/EE2fIyTbpaz59kuQ5qJmmCRrYO0DayBtDA5d2xjcvAZ74zlpwk2m5Y+s/Yd2y+z3fByvfY/OQ7ZvO8SDbTiGdBYJBQoT95GiOYSZ/yWZYZQUzlOT3LP60dsmJnbJYVd8QW8ACrV2kbxzfHQ+Lj2xGbZciWGYafDzE7Xsu2JElyecYdsPwYWm2XJfF3OtFAu/PtqkDKsV2xAXgwQwvIWsEf/snLcvEojVY0J7J0cErZopVJcnwwGOR6G8kJSnN+fo6XL1/i3r17wUe383quSw8m2iaZTThTAKrdbmM4HOLk5GTjZIzQugHki9p++MMfFuaYXIDmskfqsFW0cqzykiT8GP66QOpKWZZt6JF8PEP9RjTOtLObTr3h9+Fq0Gg00G6387q63S4ajQaePn1qHunua5tsn6Yb8H6/LluDcKB7g/f29tBut/OFlI8fP85PRyBw7QKy5qHkhTGw637DbYNLDvD3LjsbQH4VCcF4PI46TpuX12q1MJvN8Mknn2y8JxtJKzc0eMh3xtY5N0LLWiwWODo6QrfbxRtvvFG4SoeXpX0nCKXbunUai16kfSPtZh+4ZKzLz+bqg6ptJlvVR/v8dxV60vrAsiV3hWdp4yJ5hmYDcOD+Ags0v4prfH06Id+ZzsE6ucKSO7Hg88Fwuq4r/rdt+V/XVRBcX+TgkiNmMLYMcZDyUcU5xZ01rnQ+fKsaNSGMJBTKEvquwrZx2+W23xS4jjFy0bl85ztit8rxR2WAB8Q4XjGKbkyQ0RVw4GVpvFDixHGWCqsV8KB3pFzw9D6jxQeWcukLyJSFGEfXVQHfNWOBr82EL43P3t5enk8eW1ZV2bHkMJ+HzWYT9+/fx3w+x9HRUW4MkmPO5SzZVZ7uM4QkLwg1kq06aNcgOezb7TZu3bqFRqOBXq+HXq8H4DIAYOHmo2VpAFu81prrIUopKdm0Y+b8/DxXNufzefB8c/WrdMxKZ5VPT6U0sTxCuxeNxm80GuXPQo/SK2toV+Efsm5trDXZ4ZMBkp54WqJjuk+QjBLaSaXRnkXf8+Ecq+XFjoFeBiRAI22AdqEC688VVuujimmnKRWnBUod/0ki7o2V78HeJxf1GMHYAs3xvAw/7VneN8b9ssVO0p/xHaMbaXlwkj3Psqz4nd6JAGZeBn+mfcYEY3ndjiCsxK0QgGVtCAnETo+mmJ/PsRwXF+xqfI+OASN+J3kQ8QV5l2QIuPQzrf2uMqic0HyucjT+WocMt3iMFoRw8S2JE9c95NhQOsvhGAIyLzm/+L3v9Pv4+Biz2QyDwQAA8vuHy9RTN4TqnADyBQj8He325X6aGFrjaWlHJu0QC8FZ1mfZatrvEJysMq8SXPqzNhc0ncjHh6jfXSeu8Lq57sqDSDF6GC9P0o41t5Mk2dhhWtW3Vwb4PJjP53j+/Dk6nQ46nU5+xQ8/5ptwDQGLnkPKsOx/q7xQCMG9TNlXxd8k/w/Bh2ifghgHBwfodDrY29vDcrnE8+fPc5ub+4t4fm0ckiTJFzHQu1AatnRxl30XCqF055vjpNvTFUZyvmpla7JYA00uWP0cCr78Lv7rsu3rBpfdXsc8D/EtVoFQXhaLu2V/xuQPwYPLQUs/t3RIq/9C6SVGDli+GlcgVqMryQ+sOk2bTMFbpvG9d+FZB9Q5X11zhOSDBNfd8oVgrOxUSYCWkku/y6we0kDWWzW463rvw+M1vIavE8QIiZC5ZTE/635pwL/6yAUhgsECvtvQUoRcbbTwKIOLVobGf8lBYgVuND7OQToZKb8rEOTCUT7THP9lnDhlIYSe6xTQoUqE672ku+VyiVarhf39/Xysz87OCvf8aru5qxhpfGECldlsNvH2229jNBrlR9WtViu02+2NY5ZdZV831GUkxMxx6dAig5WCsf1+H3fu3EG/3wdQPOpXq1fSmU9x1viX5ajW5qZWH+HRbDbzADPdF8qPrbMMSp+TwcLHx6dcxoKszwUc9yRJcnqno6rp/rQQA/0qHYguGajhoO2IdeEr+QJ/TvdL013HVD5Q3JEjcdFk2/x8jvn5HI1WA7QbNmtl6+OEVxfj0lgfYUwBt/VD9pmJzwY2gqxqYFYGXnmZ8rfnM8kuj0vO+4rKV6btxlHJPpBJKQjJ32XinXiej2VW/F4Idq5EGtdnRDC2UD9vhjanBf4Fvsbfye8cvxUweT7B+Mn6rkuL99HzxWKR7yLT7pijXWH8uO+ycq6qLsL1ONf85XVUwdcCF6/jY8ZtAA1nzUEk5ZbF7yVP09L7ZJP1W94ZRzyt0WjgxYsXeP78OY6OjpCmaWG34S77FHi/Wbp6kiR5MDbLMvMeU5fMpTnS7XaRZZenrrjsBolnrO5VJ8TqhTF4WPaldO5xOtZ4FtcdLd5KehpfJMXvjpdAJ97RcaR8N6xrvmj40afcsSVpj9Jo/XOVc4kfUT+bzfDkyRN0u10cHBzkdyZT/7RaLVUvjcU3hidrvNNlI8hndfJ/nyzXcKwKPlrX6pPfadHVcrnEbDbDnTt3sL+/nwdjX7x4UeDzrrbwOuVx9TTH5BHsMSBtoBCe5PJNhOoC1rvFYoHhcLiRhnwJEg/JDzhIm5G3T55oEQKaj8dnL8vv1gILq7+s/rTSWj46ny0bA2VpLbRezdcXW0YIHtocln4JK68GFn+UO1f5Tkef/mHJ8SrgoiPtmoAYHclFZ1J3k+k0Wre+W4vuLNxcfCK0jND8ss6QvC5+4PJ9aVDLMcVpmqLf76sEQAq7RjxVA7cuZyUQ7nR9Da/hNVwtWMoHf1dGQQ2tOyZ9TPnSeSHfcSFPCh539MfUxXdpyPo13OV9QHU54UJxlQ4Fl+J0kyHW6WL1ATc8Wq0W3nrrLXQ6HfR6PXz66ac4Pz9Hq9UqOIlpR6J0boTgzOmQ8t27dw97e3t4//3382PqRqMRgHUgcW9vLw9KjUaj4B0g1wU+pU+moT6l3QAuvuUDCrQOh8MN3YdW2ZPRPp/P1buKOF7yDuiQI2nkjk9pqPrmo6QnykMBC17HNhw9/G4l6RCkOkOVYI1Xh9AH5XE5wK4bpPNFw9XXL9Y9srJ8+v3ll19iOBxiMBhgPB7neSynk8/ood/zszlW0xWQAY3OOvDaaF6cPHBxJHGSro8nTrLiJzIUjivOg7IUmIXyKYOzF8/yI46x+c4MxuIin3VEsUIuheOLfSCHMLssI3RnLA9aAhfjkm2+y+ezFpSNOJ4Yq8tyN2iQ6gTDhbdL8JS8bCVAuxGIXWVYnC4weznDcuCWU4vFAp1OB/1+H/1+H51OBy9evMBoNCo4FjiNE7++Lp2G31vL+aAPp23h6woG8OexZVq6tcbLZT9wPCRPi7ELpAzlv+lezfl8jvl8fuWn/FSFbrebOx7n8zkmkwmAYgCt2Wzmx9yH3gtPQOXMZrPcd7RYLAqLC7U8dcwtaYMRxDj9dgG4DgLocpnaal3XpcnbbrebB6Ms24Foej6fb+gYGnDcqN/5TvKbANwOy7IML1++RJqmePnyZW4PXSdu0qavYqfcdOAyOTYPXxxEPKnX6yFNU7z33nsYDod4/PgxgM0FORbwY/l5Wt98scqSfNDyu4eUbclS7b2LR9KJQRq+sXTINxfQb85nuB8rZAF4HWDRkib3XD6XqrpQVZ9dTN5YP6n0O/EyypRXln+5YkJV4Kr0+7r4tuaHcdGljxdY4BrXWH7jg+s4gc/qtzJz2JXGGYzVlDWtE9I0zVfLSUSzLMuPL+CwWCwq3+kqHYmybg7XZSD7wDVBdqnMXYavW3uvG2L62qUkahdc8/K3PaYhDLZOvsH5FQUmrTbSqmXNqJKOIMkHfe3SDHgNYpxSIUBtrgsk/mXHalvKrRWsCe1vkrXk4Lt9+zb29vZweHiIFy9eAFjvVqUAXpZl+fG3sTwxSZLCDjnC5eDgAPfv38fv/u7votls4mc/+1k+hq1WKz86GUAegCk7b7fFx60gj2vs+Jyie3n44okyQEdVnZ6e5vds8kUKfHczHf8c6tjUwOeUsfSnGKB8fId9rHNbOhDpmUYPGh/jTiheRqiBESp3ND7jCgzskk4SY+wDm23VdgTIfjg6OsqduOTAp7w+ndyaowCwHC2xmq6Q9lKkWYq0teZTSSMBUmwe9Ss/tUAp/74qfia4oJvkYixpB6sWgL0I8CaJ2P3KPgs7YzPkQdiNgClrt7kz1ppK8nneJZnaLxsB2Yz1IX+fCVqQAU75KYOyWjCW1weBk/Zb4CvxUXfD8nZR3asMi8EC8+dzrOa2k5CCBmmaotfr4e7duzg4ONhYZAAUbWLNUXoVIHVKwkUGCstAGYcxvbecumVwcNXncoRKWWAtMClr02j5aNGWXIS+S/LAAtI12+12vrBP8nmS876TIVyQZZc7ykkvomCsz5EdWoeVzkebZWR31XnGywkpXxsXzR6MBVp0SHJcw4sWbFIwVjuVx8Kd6xNcp3ZBXX1bBxC90xHkVUDqnRIs/VnrD6lDh/BnSedleZXPBg7JW9XZbdnOMq9r3kt6XC6X+cKDVquFe/fuodPp4OnTpyo+rrZaiz+stln9oQUFNVsp1vax0lh6uyxHHk9M5cTqAdIvJn1G/HesbuLygflA0xX4M58MCeFf2nwPpbG6+KOm28bml+2O9QdICNUnXHnKtIOXI/1iPt4SgqMFZcqRPhBelqYnhPCGWBy0965xqKJzu3Q0bd6F9n2ILHPJTI1uNPy054VgrLznLkRAumA6nSLLMty/f3/jMtuTk5NcqHEkaZePfM5XFmk47YqypoGL8W/DQPMpLTfBKNTAwv2q23OT+/A6IWSOlp3Ldcx/TVkD4NyhJkHes0TPLAMpSZJCMI1gOByqdFYX7fEdZhpsg59KY8cnVF914IYFX+3JnXmtVgvvv/8+er0eZrMZ+v0+9vf30e/30e12sVqtCoEPKiMWGo0GOp0OAOQB3SRJ8Pbbb+M73/kO/r//7/9Du91GlmX47W9/iw8//DDPOx6PMR6PC87HMjhcBR1oRqQLVqtVfucb3QtYFnhdb7zxBv7oj/4I3/72t9HtdvG///f/xl/8xV/gq6++QqvVyuuz8JfON8sZ7QJuxPuc3fyT8tIzXmfozugySrlP/vscr7yt0rjy4cnL4fcF8r6XRzNfp07qczq50sYaasSvvvrqq5yf0e4p+u7DUT6X/ZotM8xezpCkCabPpkj7Kfbe2UPSTJC2UiTNBI20gWR1sTOWPi92xCaNi7nSWAdXk0ZyGYS9MDeyxnpHKn26dsPSc9r1Skca80BrTndg7y7yrotj5VL/Q7xzgRymPHaZbb4XgczgQCyydT9R3szzubrMk2Wb98LyujYCztkm7taO1w0j24X3KsNqsMLy+RLZyOaPmiOJHLH9fh9JUtxlJp2fVjlXCfy0FE0+cCgjq+tqG+cTEkKcm670WZYVHLaaA5Xy0WdZR6GWngfCr1sOxADh+vLlSzQajVzfc8lNeSJLSHCNw3K5xGAw8OphmlwKccaFgOXn2kW7RJsXQNHG1OS7ZUtOJpMNnVbOHyrDwsM1djJfr9fbCK4sFgvMZjN1vsg5qjnEr3KcqE5+Byjp6rQ4AfDbFVV5gtVPMfnrxqkKXMV88/Fil14sgXSDqwTuP3HZWTRXJH6WfeTqE1fg02U3WHNU068kLrTIg9sPPhtVw8micR/fuGreX0X++OZsmXbE8POQ4BXPZ8mv0PpCQfaf1PFignOWX9iqy1eeCyw6D/Wda3mte5fLgMZXrHpC9WpLjtHz0JM9QsovCyE8SPYLP7WNA/WhxKspE9GnNekspZifiU9Ad4H0er1ceaEVNLQLR5aTJJu7p0IdZmXfbxuuS6nXJucuGhihcB2434Q+rFN5cDlxYyBUeGjPrtsokL9D8bH4J+dpfKzouetODE2gWYZ1SHvqhlAFWToKZFs0Bfm6HSIuQyIGfA4GoHiUJ/9vNBo4PDxEu90uHG9Nx8TNZrONu5vK4EmGHtEplb+/v4/Dw0N84xvfQKfTwYMHD/D8+fNCPnKm1A0ajYc8CwU5Jtr8ovlPCmFVnkj5u90u3nnnHdy+fRsA8OTJE/zN3/xNvjPB5WjRxrqKYakFIVx18aBjiHFVh0wpa7SFtD22LiqXL17kTmteb0xQoS5w0YILuKPdojlLZ0+SJD/ClZy5nU4nnztSHoY4EmW61fRyAVG2yrCcLNFoN/J7Y1dYre+PBZAfSYx1IBbAZRC0kayDhCy4igRIVhfB1YtPAOtdrSzQmmXF70mipM2KuCdJsg4Qsh28eQAXl3gW+rmunbF8vLLLT/VoYnrHvmuB1MIxxZ7jifnvAl7YxCskEJt/GkFa/i7LMmAFLGdLZJMMq/MVVou4owtj7yhz6WihdZYFqTu45lwVkPKR12Glj33vcuJIfuty+oTIBZcuLct1+Uas37tk/4cA7VAlPcRqM++TEDlnyVoKBvJxJdDKdo2dC1x2lDU/Yvrwqsa5zDzWcCPZbI2xi2eUbWur1VIX5NI9tSFzUT6/DpB38hL+tBjbxfOq9in1mZY+lIfycnYBQnDx2RZ140O+ajqlCMDGccNVfAJl0mpjX9b2iQHiD6G0GzpWPJ3mE9J8Q1VsyBh7VcqjqvVrdbigjnpC7X8XuGzBEB9AVT5Tpv/lPLHoM6RvNLqpQ7d38e+6ZV0VXhCSV7M5eN4QP4uW1zpVK7Y9oTTu4lEWvVv1abqsC2/vnbGkYEynU/XuVwDY29vDd7/73Q0COjo6wnQ6LeyKHQ6H+OKLL/LdNwSdTkfdFetqBG/sVTi5bhrcxP7YtXEMxeU68dYEzS71oQaagyvLLldYXwfw3aIxDjjAvr+a3qVpWgimJcn6eC6ZZzKZYDKZFI68K6NMuRR0TdBvY5VnkmweOQOsd17OZrON+x+1/C6om86t8lz1VK2fB7bo0yW0l8slJpMJ0jTF4eEhTk5OMJvN0Gq1KuFCDgXC51vf+hbeffddNJtNvHz5EuPxGJ1OB++//z6Oj49zHHkA96aCT7dIksuAc5W28mP/RqMRPv74YwyHQyRJgrOzs436LQcoPyIw9sgmK53rOc1hWhAwnU7z8dfurY6t24IQHsb7g8rnDkatPM3Za42rlofK7nQ6efvpnjVKz3e8hwQsJNTJ26gckinylBqqj/9LHiQdExrQznq+cEHKMdmfrvkk6+Pfl+Mlhp8O0TpsofNGB41WA41mA1lrveu10WwADaCxuvhsrD9phywaF3R0EZhNkqSwMzZvM+2MTXC52zXBZQCWO27YM74bVqYFsLFbNn+eFPtX3SFrDEFhbHgaI2i5EXilNOJ7/izbfLbxqRxTnLdX4KU+08Y7w2WAVR5DLIOyDE86lvj0b0+RLBOk2eYx/FofJsl6cfFiscDJyQmyLMPx8TFGo5HpJLkqx5kvP5fhFoQ4ckPzh+DkcnxpfLVMXa50/J3clQds6mAhdYXww+uGWLtQjhOduMJ1DEpDd8TzPHJuucad67n8aG0Np23YspbN5HMQXpdt7bNHrXmkyVwNON9w2UBlHetEK3t7e7meQHdcn56e4vz83OuclvfFXcddzNr408LVMvdXlqWlbdBg2bHdVbDsj9C+Gw6HGI1GOD09BXBJw7G7/+sEslX4tYBke/A0sq0+vrbrwG2JmDyA3daQPrF0PItXhdKYLzBj4egrJ3RHfh16VSjI02RiNttZ+Pp8nj45LU+BdZW9CzxR0pY2r63FTHXgz8vlvpbQ8uX1RtaJOCHPrhNC+5KuGQSQ+0l9bSl4ZTQnDH8nEWk0Gtjb28P+/j56vd7GZOj3+xuCK0kSTCYTtSxJTNI55INdmDSvoRrs2uQLhavE2zcfYp0lV93nLqNO4zMxQtaqj/L6yrEcCiF18Hqkw0vyVQogaA5veSxDiLLC2+lSwMuOdVk6sZz6oceZhuBl1et6H1ueD1z1aQo99YNFM1wWLxYLNJtNdLvd3OgPFfAuXCTIeyGzLMP+/j5u376drxLu9/sbsl7DvwpY5cg+9tFzDJ/0OVelLhJKX7wOfhT1YrHIdxg3m02cn5+b+Xxll1W4Y8aL6C22bEnTPojht7zd/O46KxhrlVEGOB0kiX10D6Wl+mR+F26hOPig7Fzl/ePrJzknNYNLS6PhR2klXRfmXLbeKbucLLEYLZB2L2gzARppA6tkVdipWtiJSUHSRnZ5VDEPnK4uA6p8N2weF81EevY8b1eCQv18p25+OjG/SxasDPbA3CFb6KzN3xuBTiOAae6SzdiYr4ppCu9Z4FMNylr8lONAzzTHl4KvfF94d1F3tsqwHC+xGC6wHC3X75i163OycUfneDzO5Z9rDsc6ujSoIke5wzIEFyn/qAz+LES+VbW7Lf7In4XIOg0XTWfgv6uCz0G7SxArU/j9upquZB2DyeWhj/7K2nLW81BbiX7L8kJ0hlAImctamlCasvJJ28Jla/ts41j9zQVEF3SFAdnAZMdovNUao+vwW1DdBJodYvFdaef5xjiUD4fanb53dciuXYNQ2rX6n3gYbUIifkiLGa/K38zHhnzk5C8vewSpa/74ZKgvP+WxaMxXtpVHswl4GRZo/MzHD612WO22eJJr3tapi/D6Q+a2a25Y7XPVrdUnn2k2X0zbfbaojyZ9PFTWEeNbqlOfdMk9Fw5W2hjcrLF3ybc6+aDVxhD+UGU+uXiTy/YIpTee1+Jj6s5YbeW8Bvv7+/jDP/zDwv0JHA4ODrC3t1d45jvrnkOWZRs7aF/Da7gquC6F3wdSsa+jrDJQpv5Go6EeaR4KdRnLWh8Sc+ROhroUJc0B3Ww287rJMB2NRsG4hz7noLVXU5RCDMYQkApgmXKuch5u08CSAthnDJHD4uTkBHfv3sV3vvOdDdksdwW6ynXBarXK72rpdrv4/PPP8eWXX+I//sf/iD/8wz9EmqYYDAY4ODjA/v5+dJvqhNg5HpquzNhbiqLmXKLVy61WC8PhED/5yU9yg3q5XDp313OgPifngHViCU9fFTi/0nAMXekYk0Yz3mS+LMs27rfj/cHpU6tLrtgMAeJH0uHMy9Lu5LacnGUhxJmiGeDUJz6HIS+P9HZtZxkBpZG7YfnOJ07jPnqnMqhO7R7IxWCB+WCO9r022nfbSDspGs0GGq0GkvRi1yvb/Zqkib47lu6RTS7ojt0JS88A6Dtl2XPXjtiN39DHKSgAe5nY/s2+5zTBApdWEFa+o+dBnzxwy+vRULecdVmxjI3nrB0FnAmPZYbldInhb4dYTBb5O34vYQyMRiMMh8M8aLBtJ2xZfUfjffwTKDrtQpyKmh7ocxJxfFy8V3NIuJwiVhreHq1MCyidJQNCeJPEk8uFmwiS71sOZsupql0/ZdVjnYK27fkFFB1iFm1u07YOmYNWuioOSI038LHUeEYdQAv5Xrx4gTRN8eDBA7RarZwG6EoD+W/NadknVz3nqtZXhc5dPNVXpwby3s5XEWL6W+tfLSBxHUA2GOnS/X4f0+m04Cefz+cFP7vLn2/JUv7OZytsA0iXqYseY8ae81xLpmt8WdLYNvomxP/p00G0sawTV01fcPksY/W2KiB5XRVfKgcL/zInqcXckazhIOvUcChzigOBnPuxbUySJNcTQ07xcW0C0Hz1UresUy8PpR8tny+uUODS5ByxFETtWavVKgRXpHKnXepbBjQH0auqOLyG3YCvM32FGtQyrS+f5eShvFUFRFUIMUYt412mkQq87Cep9Fl18b6SznyXImXxTGu8tkHvUrnnd6OG5pflxOTTYFvtlN8tB0pIGZwmptMphsMhXrx4kcvT09PT/KjnOmmfyloul5jP5zg+PsbTp0/z9/P5HC9evMjHcj6fF4JSu84ztXnKv/sMB6s8Vx6Lz9FckHPCwlE6y6x57HP0lQHuICPQjnWXdUhDy8WrNND4lzavfA770LJDjAM+R8j44caNpXRf19yQckaOheVo4b9dCz60vuZzidOvlF+hfN10Wq8yrJYXO2TPF+udrh0ACdDIGuvgHZkg9Jmx74nyKf6TRL83tpA+U74rn4W7Yi/y52UWGid/Jpe4K1DIz9PIoGhWfJ7ny5Tv9JMfPQz2LsPmjlif/kR4aLjnH5mOn2NHb5ZlyMYZslmG2XSG5XSJxXSB1dy/Y52+1+FwteaCBXXwBD6PXTp0qBPOxw9i8dLKcb2TZbjqlzxM6tVaXaFlhYBW76sCMfqrlPkan5dpQ+1Gy/YJ6XNfvfT7VRg3TefT+lhrv6tMXxpXPpmX7BWyXcjOsPQBqRfv+jj58NNs+RgarvqujJ7sK6MuqCp7tbIIytCOzKPJ16ukR2lHaUeLa31ntV3zB7nK4PUQWP6k0DHkdoKrXiuvBrG2X0w+F8Tk5/1ZBZfQORPCl1zlaDQUQvu+MbV8Lj5dMVTP9vFHn03r0qVDeKlG26H0GTqm2vytQssx/eijAR/flfwV2FykTT4WLSg7Go2Cr7cI6dNY8MmCmL4sBGOtHa6xEHJvjYRQZgHcDKXsNbyGbcNVzoFXdb5ZylBsGZqwTdM0P1aWnyFPQMfekZApKyg0o44rEy6lldLF3OMQCmQwSOcJgIJg3fWdBHUonTFKLvUT7cQZDAYYDof5sbbAmnaWyyUWi0UwjrGQZRl+9rOf4ejoKH+2WCzw+PHjPBA7HA4LzpRtgtVXPkeGS2nm7/hRt5ayH+OgtPAhnDqdDprNJlqtFqbTKabTqbNuWQ7NLYlznWPByyIeliSJel+cq+46DATpjPUBd8pbznl6Lo/3lPVwOqJPonsKxspjTK8LLAOAeLzWVpmPr86l0yxWqxUmk0n+nJehOa3kUWp8fhFO9KmNj8VTNUfG/HSOxfkC7fttNPeaaC6baLQaSFspkjS5vDs2bax3wNL9sRf/SJDvmM13uTaywm8A+k7ZdUR1487YQh72PD+mGMX3/NnFj8t2K8f6Fjtm87sWfPUFNfN+zy4/Y3bHbqKVyQfF5wqu+bhnm+ksHLMsw/L5EsvjJU5OTrCYLwonj7j0PIveuK4ied62oazzOBa/bTgorHr4J5eDPpALziwHsvbM5wiifDyvdg+75WiRZey6LusD3mbS0bXjiDXZwecYLd5x6QVWX/l0LGtcy9K+bE8dNmFI3THgcw5Xze/TOS0bhd6Ftofm1mAwMJ3JfF5peFbVwXcRytoSu6Bvvsqwa/TFd4tm2XpxNPkALLDkmMX/fOALclQ5JcnlMynLM7X8IYE0/s7Fjyyw+Bs9y7KsoKMCxauiQvQ5zedn4WHlu2ogfU7e22rZ9yF6VVm/g8SFnoXMB20euHSamDlWhfat02dj/SYEIXqazB9Sp6U3tNvtDX90p9PZOGV3tVphNpsV7sv2QVW9Ud5bz/OGnCjs0ocLubUE5PS1os+hxwjP53M8fvw4vwydgO6MkI4ijriE1wpIfdBqtTb6c7VaeQX8a7geCHHUW++rKpZ1zLuqRmWVOl3vfQae5YzheaTSpjFsnp6OlJOKrBZAiBGkVd7HQghtSYVxW2MfY6SHKOOa8h47l2Lmq0zLg21JkhTuHCUFNVTxkWk0xyUdfcSPY/ziiy8K95muVisMBoP8NAyiYVmuhQvRtAzmheDvm4Nau7heoYFmlEgeJZVGF56cB/jaQwE8wH19AwfOXzS8Y51ivvnIedhisdjQz2JlgmsMNVp2OUnpHfWj5LeWsh9qGFnt02hBHpXsciheBUhjiuPE37sc5lmW5QuK7ty5g+VyidPTUywWi4J+GMpLKWhtGbuuvmo0Gjg8PESaprkDajQaFfNkwHK8BJbrz0argc6dDpJmgnw3bHYRdE0vnq0ApADtXOXHFfPdrvx44jVCl+0q3Pt6kR5AIVCbv4P7O91TGw3Z5vfgYGyWFb8bAU8zCCvrNPDaCKwKHOVYBu3kXQH39u/hfv8+TqYnGKUjDAYDzGfh19v49AYtGBgDZXmA5Wjz8WvAlvNlZGgorq46eFtCZQDPJ3Vrbcx4+SE6lmaLhPJFX5myvF0HX7/w36H9K/PwTx//12S9NR+s8kLwuwmg2aeaIzhEj3bpJCH6rhwjX1la/VraELtQ0ztv4nhep17IQfZ5FRlnwbb5n8/e5HjE0mpM/bKubYGUS7SAt9Vq5b56H3+W4xzbfmlLUDlAuYX1LptWq1d7HlOX9jtEnpQZ1xD7mn8PuWs9pI5QPU/rj5B5wsff1T6y+yx5T89dV9ZIX6iWn9K5cPZB7DyQfh5ffk2PjcHN1T8yndXfPlsn5J1Vt5bP0iUpr3VVhTVPJc+x7s2ugwdrvNIHZXyYBN5Q7mKxyBm+hCzLMJlMNhyJrVZrI0o8m83wm9/8ZiOK3Wq10G63cwLy3X/2GuqFTqezsf07ZLXVa3gNZcElWK4LXEJDA1cAhyumriDQbDbL74vkAp0vTvEppzyNJfA0p0YdEKLUa8qKhUOZwE4dbfGNfagQjlHoQ5xZki64oSX7vup8ssr/6KOPNtKmaYp+v58fMRaihBKQ8lQnHfrqTpL4+yJCjRvLiRmiOBN/oAVtrjkU8sxXNwfSz0jOhzjCuO4mnaNWX7jo3XKsuxziVjnUhxIvAoknfWoGndUOC4csW+8C7Xa7WCwWOV8nutsFfZYMfe3OV5ob2vHKFGR++PAhFosFlsslRqMRJpNJYeGGzCeBG15l5n6j0cDDhw/R6XQwGq0DbicnJ0iSpIDDcrDEIrtYINJK0Gg3kHZSIEMegE0aCZLV5a7YJFsHTPldshSULdwRy3bI5sFBfg+sEoS1dsiui8hAxxRzMHfKFhIpj3if8vdKIDP4mGL2Xf30gLaj15STGdw7YQVuWZYhW2V4+/Bt/O7bv4vPk8/xvPccjx8/xmg08uLmcu7QfOcOstA7vWUduzD/dwEsx5Xm8OBjEeLA0vh0LPh0Mo3PvWpjy/u9zBUYmvz26bwyP0+r2Q4xDjJON3XYHFcFMkChycxY3mI5g13y2KXHUlnb7sMQx/JNg1AHPk/vSqu9D7VJLdxeNahDFl+nPJf1kj7e6XTQ7/cxm83UYGxZ/4V8J+WfK5Bi8VpfHXWCj0f5+sXSUULyhgKvg/wB2+CpIf4mmVazg33lWXX7yvT5TLQrNMucwKoB6fghPFbirdGFpi/K9y5/iAvP0LkcU25I2ir0uM3TYrivXEKsrIuRxdqzqnO2EDG1dr9KSNMU7777Lvb29jYCeVmW4fnz5xvGMAV0tfQ88Bdk4N9wRewqQGOe1v29Pia4Kw7F11AO6p4vVWlBM/5cNBZjRJcxuOugbWk40zO+UIX4q3b2PR05Sw7zUIHqmru+MuqgCc1Z4qrDZRyWodOyBmdo2b66OFCQ0Xesh0up09pC76yL7Ouc2xQcTJL1Mbr8TljCIUkSc0eshpvPMLQMuxiQ9dMckuMRgq/m8PM5jzWwnJlWH2RZVtiV7MKTv6PyrBWoWj2kjxGO8k5QLY9sgzU/fHRBea3jrCyewcvWDB55/K7lRNf61mVgybIIB5qPs9kMvV4P77zzDiaTCQaDAcbjMabTqXc8rsq5Ix3k/JkL5vN5ThsUmF0sFvnOeF6+NTY0B2XdlpNV4kw8icpvt9sb9auyYwnMjmZIOymaty6OLW6vjy1O0iQ/sjhZrQOnjbRR2BlbOLa4cVH2BZvKVmKnbILCd2cQln6zgK1sc94u19nE2qts83vepyzAWbhv1XNMceFTlOPFEdgc54zl4WUE4J6tMizHS4wej4Bs3V9H2RG+wld4+fIlhsNh8CkDEiyHdqzjqc60sbyB6xiuIEYMD3DhEuPolXJem7McfykfJG+QbdTKk7JLkwW0GJwvTPq62rsufV7KdT6eUu5LCNGbNXks8XHRbox943p3Vf4lXz2+d9oc8IHLGSzrrNK/vrbJ41ZduLrq8OGx62D1gc+uJpD83CrfKiPUkb3LfRwrc6vw9uuQCz7fymKxwHg8LvjQpJyV5cX0g1W/PLnLZTP75APPb/lPyvgIfHyuzHj65lRVGrF8SLItml/DlV7WQZ+uPnLxnRDQdDYtv3YnaKPRKGzq47sgs2y9AJv8piH+Eg4uG5/e+3iqFji37AUuq3x1a3lc6bT28H6OHV+Nz/h8Kb62hLTBBZIWqEyNbmj+uHRHDZcQnYjy+MbYeucCc2esK3OapnjzzTfR7/fVtCcnJ3j27FnhmRWIyLIs2niuOrC7BD6l1TIwQxgtByJc+czHdFxHDOwKlDGersrgqguq9Heo0yfWORTCyLT8Wlu0YGyswuRyHJQda5+x5KrXFWDWFCtyBmn3t2rKLA/a+pQebayq0H+VvCGKk+tdqBIh88TWUyWtq26X08mql/PvOnivpqgAl6sNW60W0jQtXFFAOCRJ8chkH/5laKWKoyVJLnfLhezctfiDnEcuJcx6zh2WGt3KZ5qTzZovmoLv6y/CicaP8JNBYK0ejiOAQrt42VZejiNvo2aYyeeSn/My+fMQXdLFf0J1S+o3UvybzSYePHiA0WiUzxvrRBmtrrqAO1p4P/P3LlkmgRu7SZKg1WrlJ9/Ilcku+gmZgxIIL34ENVA8eceal0mSANn6LtllZ4mknSBbXjiq0kZ+dDHtjE0aCVZY5fkKxxaz/szrooArBWmNoGzh0/dsowOszgp4TsFL3q/ZRZtCjyleZcVPeqfhlcmfGw/8AVjtHW9HtsZlMV5g9GiEJFvLo5P2CZ62n+L8/Dx3StYFlvMkBKrI6bI6axU7yCVfXM+0MuXvkL7g9qZsf4iuGOME4TjRvW3WqVwu/e1VBcnzCLRn9Fzm1/JYTkNepwvKzAufHbltP4BLv4wpQ+Z12aCyDmkLu+ZkDL1b4+6bi6E6SKwP4FWD0Dkh88jvks+T/sp1+LKLmOqGMvw2pJ/KpN0VcOG7Wq1yW0OzH7X8msws42/TeI0Lf4v/7/o8d+lJMl2IDiHzWM+s/tLGs4zuJ8vS6is7JjH5rGBsp9Mp0AhfZE86G9mHMaD5dvi7MrqCxmvpt/bdpQvFQkz7y8xznq8qHppvS+Km5dHuGNaOKeaLRLTyfT6QqvZe2fzOYCwZtdrFtJ9++unG5KHOCTki6jWswTdwZZR4TuxJkjidg7PZrOBcpXvP5MXiuwxX4bR41SG0PyxmFqIMxBzzVodgClUUy9aRJJvHUIbUw40eMoJcQixEIariNLwuqKLo3ZS2hvDqUGfRVRgqPACyWq3QbrfR6XTy+mnBQEj/k2NVXk0ArHWKZrNZWpEmfCxwHb/L+RfnZzQWvhW4rnHSHAmWcm4tjqKjy2OdV65+oWedTgftdhvvv/8+Go0GHj9+jMlkgtPT04JBJPvHap8LNCPH1bfkICJ9Bdg8hkbuyCb8uCOdOyPkuHLZwN9puITIM15Os9lEu91Gt9tFu902j+PmQcxQmosBbby09tAzbcEd0QHdD/vkyRM0Gg3MZrP8SGiqg9rE9cgQCHG68TShJ7TIclfzFaYvpkga692wzYMmmgfrnbJJmqDRbOT3yOZHF1/80w5ZJBf92bi4I7YBJFjvmqV3FKTM+9zYEbt+iMsjiBP+OIEZhNXACGby74U+kwFZ+T3LIAOgeVYqJ4Bd++6H1fCVeBZwWWVYTpcYfjZc3wu8Wpe3XC7x6NEjHB0d5SeLzOfzjZ3TKo4OvU3jMVcJ123TVGmvi3daThgNuNONdG3Ka/FV4luuMdNkKTn+lstlwSa+KXpmnWDppNJh5ZKhllOav9fq5TLTJZ/LgOUg3TbUgX9dPMhXzteR3ncJJN/xjXmoM9uqi5zZ5CfkunRV5/JVQ8wcuQ6ZXieE6tghfLiO8aV4gW8hnJQnMXWXPcLXlyeWDjS71lW+6528iqpuqEPmXYX/iXa5NpvN4E0HtGGAx6pWq1XB38RtUi3g5jrFruy8iOXBBD7714JQPVeWtw0eaNGKlCWudJPJZKPds9kMg8EAh4eH2N/fz8u4c+cO5vM5Tk5ONjaNWP4C61ldfKVsnxairNL5ZzkNAeD09HQDkTJOpZsi6HcZNIVMOlEBqCvfiKERyB1PFoQ40r6OUJdyUydc1dj4jDzJvHzBp7KKRIiQ8dUdApai6wqUAJsKQIySqfVJiICT70Pnd9l+DE1XpzCLAWvc6py/Fo8sa1BcBfBxz7Islw/kDNWCp775ph2vHNMHPqe5K731zhpn67lrLLU0sk9CHNRybrvya7oXL0u+o/IajQZarRbeeOMNpGmK4XAIADg+Pjb1BqvOEJBBDasczbglfPmdoK6gqwsHWYerXWV4uhYgCG2nz1GwzblvObylQ340GuV9bS0UCMW1rJOP+A857K38G3NhBawma5m7xBJIgaSdABnWO2SxDrg20ABWANKLtmQXfZBhHSBtsDbSM/4P5bv26fqu/XaBJxgrd8Hmz6lvfMHYKFSUYKtWjvzJ34tAcLbM1v+rDKvpCrPjGVbzVSHfcDjE2dkZgPXYt1qtjQUGLr5ovedprlunj9Fpy/LpKiDnnEu+as98c5m+U1pLx9Xkjat+nrbdbmM+n2M2mwXjFAq7QEOh4NJjNB3EGgstj8s2sXSmkL4L0QNddHEToIzPJYQnaLLfp1PFgK+cGLv3uvxM256/Ibq9xKcOoCAFD4Dwa+NiIFRv3ibE0EndPszr4ivWHLfmsOTnlh/JlY/np3fSr2XJ6RhepOFQpp9D6MJHD77fVjk+qMM3FuJPidUhXXLbByE8X8r65XKp+orkmFM+vojEp6tpOyh9cyMkPa/DZW9ouoiL5/sgdBwtcOnSMeVqPKNKeUDxCgP+bDqdotvtFvgMLXqXZVXRF3zjzD/LyhCNjxWCsWWFMIFrZUyo4nhTFWVgd/DnRw668Gm1Wvh3/+7f4b333gMAfPnll/jP//k/b+ygbTabBSa5zQuZbzrswvjfRHAFHuoqv06DLsbwkHcdSCWZ7okNFf4c6IhMbUGFVc5VOBilcK7bwC+LS51pY8qR7Xbd6VIFl9hx9SljfJcIle8rj3YljcdjHB4e4h//439cWPEIAH/zN3+Dr776Cp1Op7AT1OVkpU/XyknLcehzuvO6fUEyy8ikvtcMDV6uT2mXK/okbmWAAlnD4RBZluHNN99Er9fDYDBAo9HAkydPkCTr3Z109A9vL4CNXSsSHzKMZJslUD/wdkpFl4yzXq+Hbrebn84ymUxynidBK8vV17JtPI12H48mP5Lk8m7lTz/9FKPRCKenpybPpd3m9+7dy+sZjUYYj8fBMkWb41peTR/ndG3daUzlEb48/Xg8VnEsa/S46Jkb3cB6wcBwOESz2cRsNlPvOXYFEggW5wssR0uk+ykanQaavSYazQYa7fUO2UazAbpDFg3kd8eicVF+gsJuWfmZIcuDsvnuWQCXG2HXaQtH+Sb8a6I+L3aO51kea2UB0ozRiQh6Oo8O1nBR0hTa43qv4ClxybJ1AHb0+Qiz0xmy1UVAdrFpe7RardxIJ5rlq6QL5VNTPA5HLgPK0vc24KrwKNvmGIdf7DsqX0sr74jnQQZL9pOsGo/HaLfbhTu/Q5ybMXCT7ELJl+vCXcpnqdNVmWsWXWg6QZ124C5AjG/NJS/l81elf+qA65q/1jhoNrxrvF41mrdgV3yw24ZQG0DLp/na6ugzqV/F+nwsu22Xx5O3z3XVD4HPr6DZtXTii5RlsRvfquhddV6RRaAF8ix/AdUt/URS34/1I3N/Bb+SKzR/CFh+qpB7gUPLLIsn98XUCTH6Q0xbRqPRhn+bdkPLNuyyrNPaqB5TXIUIXc5OX5kxdbrKuyqBvC2jJaZ+qtdnjJDDgqDT6aDf7+Pdd9/F+++/D+By14lG1FI4Ws7GMm3gzHaXBe91QgyTdjmPQ/KHvIsdb1cwxMUzfKDRTpXyYvPIcXEFH+SzqsJKHnVJaUPKDqWnqvMxFJ9tgabwAfUH5HkbLaPChaPF+2L7v8x4+QIiVsDMKoPTZ5qmuHXrVn487ng8zq8xoEAPBWNdOPjwj1XCy5Yhx1YLbG1Df3LhHZKP3/VLwcx2u41+v58fQy0DEFrZMX0dO+dknVR2mqYFIzWmj1zjUIW38fEnY2YwGGA8HmM8HucLZLS+azQa6Pf7ef3L5RKTyQSArmvRc1mObIPloLEcAC79WabhgS56Hqv7lnEG8HaSwdVsNjGfz4NoX+3Li92WyfSi/5MlstZFPWkCukt2hRWSVbLeKZusd8qiAdB9srRbFmCfCfuH8h3Kd/Y7QWIGaS8bZTW2+F3bFZv3hwzGyvyeerU7YTeTO4KzMgh78Wy1WGE1W63fr4DFaIHlcOl0XEh+FQsuXk/OmtAjsuuo10pbptwqzlZLZy1Tns9WL2Or+IDzDpeziTvhXLLuJtumsTqJld6nF1m2im9cYwNJrmeW3h1Cty7H4K74JyxfD4cY27lMXl8ZMaDpMlqaKjz+pkBM+0LowAeUj9+zZ6Wr4lfaNmg+z1cdfLyqzvLKQFmfS4hPLaS80P5wyQsrX4xPS9pJPns51n8k05YZxxhfFYG0VXma0Dq1Mrmuxo9xJpC6OAVoLd8ML5vjp713+aEseorxacs0lr1u1cvTaDiE8ugQfSsUXHaCq16fvNFA2zBKNLMrfL+s710NxvoGow7DTkKsorstJ1sM7Ipi2G630W63Aaz78fz8fIOpvP/++/gn/+Sf5Dh/8MEHePjwIfb29rzlz+fz/J6wRqOBbre7sSpnuVw6GZkFsQ691+CGuvuwzvKqGrPboI86ywwtK0TxIUWnjJCh8svMx6qgte2qArJXUYcFoYYRD24AxdWNu2Tk1jUviIY7nQ7eeOMN/OVf/iV+9KMf5fIkTVOkaYr5fG4qtaQ405xwOfx4EM9KowWSpDIfogPJdD6HUUhgzGqftms+hl4ODg7Q6XTw8uXLPHCYpinu3LmTH0FL9fAVvpbBFYK/xe8txw8FjOWOTn6XrdZ+aYRZDnRXf0mcqL+lk576iPAkmM1meUC11Wohy4r3K/OVxc1mE/fu3UOv10O/38cnn3yCwWBQ6v5k3h8h7eRptHRyzPnuMd4vPI1VlgskH9TeEVCddKS2y3nLZY3EUcJytMRqvMIiWSBJEzRvr++RTTvp+i7ZlnGXbJLku2TlDln1/lgWjC3slL145mq7F5Qg58YOVFfg1arOMYwbgdgY/MBoROCVrTJMX0wx+nyERnIxxxb27m0Cute4jMyyHFjA+kSgfr+fnxQwm82uRaeqClep47qclxy0e6opv+VECwna0HuXs0wrbz6f48svv8zrr/MO712AKnOjTPnyZAkuI7Rd6z7Zrf2+arju+jnsgo1wlbBLfW9Bmqb5wtIyO4220UYXnUgfAZ+jnIfyMnaRL1b1K90EsGSi9V5LQ+l8ZflwCLUHLR+IBVagpg6/SJkApQ8sf5PLvtd+k75BNqO0d2VfarKVIFZmu+SttfB2W8BtZgI6mU22keNNtnu320WSrE+qWi6XuY+J8vAyWq2W6Zuoox3blM9JkmychFgVb42vh7QjyzI1rzU3YviAlla7T5bXGQoWTdcxbmUD3WowVma2jJkYJELqiYG6iF1O0m0J9m2Uy89Mt5yH7XYbd+/exb1799Dv9/O89+/fxze+8Y1CeQcHB7h3717uVOS7mHg7uLAghe4qjIOvg9J108GnOIXSiRzrWEbL63Lldb2LCUKElGkJN8spbuHge3bdhnrdQi7E+RZaR4ygrNqP1o5Pl/LMnVI+g6Qug2KbsFwuMRgMckVZBkikMyC2z2MDQaGKXKyskQ5HrUwZPIzB13KOcHwtyLIMnU4nD8g2Gg2MRiNkWZYvtAoJvMr6Q/pI069C8OXfaaGX1rcuHELkB6dFrf0uuihz9ze1ZzQa5Qtt+BE72pyPBZdOy/mKRlscyurFsXOYt1cbM447540aP7Hqt8pNkovgf7bCarrKd802mg0gwzoQu0o2g7JZku+WTZKL78k68Fr4jsvvwMU7sAAtoAdEXV1tdG+h3TIQK48fDhmiGPLzlKfhlmXrvp6fz4HVGsfFYLHeGdvYXCTjRSHbXKBB32mcNZy0OugZ390eOx9D+HxM2TFOU58slXPKh2vMWJTpJynfXPLOZVtInibLdPWL3GHxdQOfAy3EKWv5i+R89PVz6HzQ5o/UkSUd3fTxLSOLtzFvQyFG373JII8StfiXTxctCzHlWvaki/9rz3w4+/w426KJMj4IzpfqxquKnzlkXGN0A5knxDaywEcvPjutSn+H2C0WuOZkCJ1Y6UJ1Pk2Wch8C+fP5d63ckP6L8Xm58oeMUxUZovHMEBqlNNxHoAUnOe7L5XLjajg+JjHtDPFpWBAiG6w+kHasD09fnTE808LFhW+oHq/V5eN1lt+kbB3aew18uMSANxhrgXRc3VQF10VU26ynDmg2m4WjhzW4c+cO/sW/+Bf5PX6Ey4MHD/DNb36zkDZNU/yjf/SP8jtjP/30U3z00UeFNEmSoN1u58HYxWKRB2+3DTeVxm4y1OmccClk1wG+ue9jzrGGhW+Xq+YsjBEyIauj5CqmXbr/+TqN9bp5Cyl6FHyx6nDJ0Kt2HlUxGC2YTqf4u7/7OyyXS7z11lv5fZmnp6cYj8eYTqdIkvVdKS5nYKyxGeKQlnkIXIq81ieEv0wny5ZGlWt86R0tuKJn8k4T/qkB3RV7eHiIt99+G++//z7a7TZ++tOfYjKZoNfr4fT0NE/Lj3/l7SN8XXWGOGZ86XgdtJiB7oklXGjnrkzP8ZXffQ4FyzmlPZcL3uSuXa0+jud4PMann36aHxU9GAwwmUzQarXMnQchjnJpsGgGTIhzmjsFYgycmHnqKwu43BFNcrPMneoakIOD6Gg6nSJbZlicrldlZ8iQdlO07rTWAdh0fY9skiZopJe7Zenu2CRx75DNn4E9x+VvjlfUrlPZByLYyssy76HleeQUEahsBJE1HDZeKumyC/xWGRajBU5+fYJswQJpKB6HW9VhJ3eqc14m+TrJbH5yBd13PpvNouu/SeBzRFUZg1jwyQqX44zzfEs+aHxe27H5dQFLTlL/uRx+lE7TvSx9KbSPq4yFtUD9VQHLXqjiHH4N8UB0Tos5aMHjLh2dqIHmw5W6c2zwZZfbGwJX5f+tGrSisbF8RpRum+PhC5AQWLa4T54QcF1Ms9W31cZQ30GInyTUl0InhXF/vcRDuzPTkqlWsKgMbZQNprnAmheyjZIvaTY+xS20dPwaKSq/0Wig0+lE6SMWyA1pZcu0bGjXiQTc1+gLQseCteu1ankhfmfph3DlifWVbtO3Ku2JUDCDsXU5Vb6Ohs1VgZzwdIyWPMaA75yVeZfLJRaLBZ4/f46joyPcv38/d1o8e/askIeCsHUwr9ewXdCEFVCNMdchfGOV+1CwnOkxeTn4mLXmDLKcQyEOoRD8LFwsAS7HnY7/DOkXq09cOMRA3Uacb7xcimTdvEw6peSRfJZR4lKcy4CPJnzvQurVaIIbSESb5+fn+OSTT3B0dFTYgeJqH6drXxpNEbbmmG8+uRyJIaDlLTs/OG1o89znrKb30+kUo9EoN0AWiwVGoxHOz88xHo8LRxPLdsiAhXzvwyeWDnma1WpVOO5X5nUZ+HXNa9/YcRqVc5+AdtglSYI7d+7gBz/4AZ4/f47f/OY3yLIM3W43L4cC+1rgnX/XnOSu/idnRsi8kvldPIpDqFzjwS+Zn4D4SKgTxzdfNf7Gj10uwBLAFGg018G4+XS+vq92b32UcbISwVgKziaXn1ljvQs2awj9IMlARxbz4CY/vjhxbEsNua/VzJOs0+flW4FZ+Y7KcNUl3/GAMAVgswzL8yVWsxUSJFhOl+vdyKtsHdC+qMfSaTTgNKfRdaxzkhvT8/k815t2aeGaBhp9W3LM4smh81vrZzkGlgPJpXuFjrl0Olt5pfzU3tH3unXSVxWsfnGNiU/OJMl6QZt13ZGrnJCyQwMHMeXuAvB+jtE3XXPGmp9lbFZeXlld2ir3uoG3q9FoYG9vD61WC6PRCIvFQr3iYpfwlyD5N6Dz5zJtcNlass5dgJsgC6S9YaWpCpx+Y+RzCC4xPjqtrXX4RmLK0PihxXdlHWXnDgeX7I0pw+JFIX7EEAjVu0J10xgb1IIQOaTprFIfkf0Xog+50nGIoQ9pk1TpF5+vLPQ5fxfj5wypL0TP02ygELutTN9Z89nHJ613pXfGVoUYxlQHE3tVgffLdDrdWB3ig9lshuFwiF/+8pcYj8d4++2383dffvllIW2/3y+s2HkNRXhNp9VgV/rOxbxjeJavPB/T9gk8LkS1crhiQU4OiYt1d0IsPnVAjLJfZ95tAPUVXwnmG2vNyVm3MVUnWPyO6Iy39+joCI8fP8ZsNiscyyp3fpYx+LRVlD4l0qdMW45tVxoqywpQSmelnMM8rewX665Cq520WpL6eTAY4OjoCFmWodVqYTabYTAY5Auu6M553gb+T2XSp4ZTiALqokFpvCZJktMSv0tGBtBijAgNrLRagEcq93zsrCOPkuRyd95kMsE3vvEN/Pt//+/xp3/6p/irv/or7O3t4eDgAOPxGPP5POfJ0+lUNei1NvP+0trA00iHATcupfHoqs/V/xpodbtW/NJdnTQXeDBZlhsL1Ga5Wz0vawkkowTNdhPtThvTsynG4zG6b3TR7Dcvd8KmIhjb2NwlKz/XSAu8+dckfoesVs5lvJUFXvPYqLGIQ0nrBa3M7PIzp5PVOug6fTLF8nyJZrO5lhNL3fmiOcxiZTyNMXeMh5RBecbjcS7LXuX7Yn3ppDPMkv2ud/Teem7JTQs/y6nI80lZJfPwz1BdLRZuul2oLZjRxorrM7EOc9qBLueZlDGWrAp1LLtgl+wHH1jymr+Xz1w6mGu+VqXdXbPNyoLVF0S7Dx8+xOHhIX77298WTnMB1n2gnaCySxC6UDtmLHeJ770qdMghZMFTWdD4SWgfhgaeqgbY6gDN5xKLv0sHicGBvsf6QngZdc65WP0xRF74gpR10VcMkC2q8WipD8b4MuoaC42+5L24lt9L21Euwadr1wmka8eOIT+6O7a+XeH7rvmxEYzdFtI+B4oLyV0S6LsAjUajcFQw3/Emmcl4PMavfvWrnJB///d/H//wH/5DHB4eYjabqQ5HX92vQYddoVOpIIQ4yWPANZerBNPkd01IS8e4r7y6+Fmsc4FDlmUbR4lI51MZYcjTySCrDOTQ+06ng2aziel0umEs7oJiHGMMlnXCW88tJ1MMbpqTyLczNiaowcfIMp41AyFm7oSCz4FDO/zOzs7y0xmWy2Uud2TA0QUh93HK9lo0zR2v1C9W/a557zM8LP5FdKY5GjU8XO2QZUn+0u12cXBwgO985zt466238PTpU3z++ed4/vw5hsOhemyMz5EeesSMzOdzHmpzROZ1tTcEZB1Eg7IcH52F6qtU/nQ6RZqmeOutt3Dv3j3M5/M8wNxsNtHpdPKFdN1uF2maFhYtyLplWyQuxHMk/YbMfYsGtXdaP4TMUWoXBZ7J4F0sFjlv6/f76HQ6+e/hcIjVahWkg2pt4HOPju8mnDRemSTrKzkO9g8wn83XJ8+MllhNV3kgtnXYyu+QTZIEaGB9L2wjudwBS/y4wQKd1Bc8OHuxU7awixWXz/UOFX2rTINsvRUWhR2xlDdT0oaAlizD5e7ZDFiMF5i+mObPk4u/1XRVoGONXkhv4XO+DFA5Lh4P6HbNarUq2EhloQ69aluBJc5LyvAFCbHOWksfdpWlHclmyQNXPVR+zNFpZWBX7MIQcPF+nsayMWV/cz3L1Q801wDkO2Q1/VaTX5ZOYckADhK/XXHahYA1Dj7eWraeKn1TV9+G0Oe2QLMFFosFWq0WOp0O3nvvPbz77rt48803cXZ2hp/85CcYj8dXjlvou5D5wcvgundon8ektUDjP9a7UBtdlhOLw66A1pZQ+Stlv6WHyfosWy0UV618kulSNlu4a3wvljdodKTZdrF6owtny9cj8ScfhVY+nbJkjUHInLP61+VP0uqyynX5E+Q4h9RZh24q03Kdg+MlfQK8ft63obtSY/gN7xc+9t1ud4MWOp0O9vb2cHJyki+01+aypVuH8DQXb5DpfO0L1dt8eaVuE+ODs96HlBVTfhldpxCM3YYiGsJQAX+H1mHQvipARw0S0HFawOURdwTT6RR/+7d/m//+wQ9+gN/7vd8DAPMOpBjBtqtKytcVNMcyQRkFKiatz+AKNRBdSgb/7qPHEAEZC2UZfx1GiYWLplRy5Y+vNO90Ouh0OjnPoLzymNJQ4RqquF4Fn/D1hw8sRdmqQ9K7DHZo/WPhFLsqWSq01wE+mqaFQYPBAO12G3t7e3kwViqbVJ6EJLGPGYlR4rVx8tG6NX5lwUVfvvnGA/ouvi6fdzodHB4e4v3338e3v/1t/K//9b/wxRdf5MdFt9vtgvGkKaWaYu9zimq/6VmoTiifc8ep6x7mkPIsPLU5FaP3yOdk2M1mM7Tbbbz55psbwdg0TdFut/P2UTBW0q41Dlb98rurTS76C+njUF4k20T3cfLjKSlQ2uv1cPfuXcznc8znc4xGIzUYG+Js1Jwh/J4fy6nTarWwt7eHwWCANE3XwdiLwHHSSpD2UjSaF3SZZOvjixu4PMY4SfLjh5Pk8hml3wjCsvcUN82SbDMgS/l4eyHknjKc5nHFrNyLhDZk/Gt2+Yy+ZhmQAYvhAqMvRwWHW5qm+ZhTWm2VdKyuajmMSH5I/q8Z4XJ+0SJXjr+PL7h4i5W2Tvmi6SSuun3PfKDpQ672SPxiHTjymcW3+Dwn2cGdcFZZr2ETJM/m8l/bTSz1A/lcA7ouqdVq5TvmfTiF2h4h6ULnzVWDhZMl+y0+Z0FoO2P14Fd9LpHOQvpIu93GO++8g+9973v41re+hdPTU/zqV78qBGN3haYIQuSX9t5FC7H8NMRfJMsP6UfL7t+1MagKUqcNhVB9X5Zdpv+kTRnqX9Hw1XhekmyeCCTxl+VbECMDYuePZmto+cguabVa6jUq9EzaL1ZbXePsG98Qf4r2LrQfrXQhvgRfeb733O8hQdtNKvu6zLwLBekfo0009A4ADg4O8M477+Czzz7D0dFRqTpCoY42hsqN0LyavVOm7FCItUNj6722Y4pjEH3VBGhVoBUd1C8uhpJlWeHo4vPzc5ycnBTSzWaz/KgVAPjiiy/w4x//GM+fP98oM8uywhEsu36P0muoD+oSOqGGnUuJCOUJIcw61tD0lRWCU5l3sXhkWYY0TdHtdvPncrEG31FPyolLUZd4VlHO64Q6x7AMEE2SEmXRnaXghb7X6ozFsS4ILWu1WuVH6Jc56rEszuTop+/8OS/XcsRJp6Ism3+XCrrWhlAjhQIInKbke163ZpA1Gg20Wi1Mp1M8e/YMR0dHuHPnDjqdDm7fvp0HY60d9fTdMthCDL2QZzHveZoYYy/GAJf5NOXaZVTTcxoXXv/+/j7++I//GG+//Ta+8Y1v4NatWwCQH8NM9MrvG5OBhKryRUvncpZTG7QV2vTO2rUe4sCh9pH+Sac2jMdjvPvuu/jud7+LLMswm83wP//n/8TJyUkBTzl3aQc+LTRy1U30L+cA9fd8PsdkMsF4PM4D53x+YQWMn42xt7+Ht999G6enp3j69Cnah2209tc7ZrPGBQ4Jiv9AMVibIQ+wZll2GSxN1kFVmQbA5S7UPHYrnGis6RtBVwqcJpn63AQeiM3W9WdZhvHjMZbjZSHdcroZ7KZFYNzZwI+SleMaIkNdIMdX8jQfcLreln5RxdHpKrOMs6VMv8jnMVCH3qaVofErSmMtAtvGOLxKoOlQloNcgxC7h+QAv5aAp3GNq5RVmo7mqpv0rpsEso1lbXSp827DwfyqAdfNlsslxuMxBoMBkiRBt9vFv/pX/wpPnz7FD3/4Q2RZhl6vh8ViodL2LgPn9XIHI72PgW3a6tftB7gqoHbK6xdcNiuHUP9EHTiGQgwPku92jV+57GMZBNTsbG4Tyrun6bsmj8uADCz6dvvG2OnWO5dstvLG0HcdwK/SkmNWxm9n2RGaziGPTB6Px2g0Guj3+xv5Hzx4kPt4syzD8+fPMZlMCvUuFovSpzTEbIbwga/ftDT8eYx/dJdBa/e13hkr4esgROsAUv4IuNOWG5S0eo8rf7PZLJ+o5OSif0p3fHyMX/3qV6aDej6fFwSEdA6/hlcDpJFrvb9qfKQwDmHwrvK0MlxQN43X1Y9SAVytVmi1Wuj1eurdnJSG3y8pAztW3/oCWi786gItOGSl8ZVTFjfNEaUpDC5l02qHC/cq+GqONKsPqhq3VBfdb2EdvePC1defsbiE0mkM7cSMVSGgI575xl8LoFmQpmke2Do/P8f5+TmSJCnswpQyXtIpGfwyACZx0Wi4rFPPxc9jHL0+Y9Wn2GuGn689ls4ErFe3fu9738PDhw/R6/Xye3r5kbkACkFyXn+IA8wVIKkSbJB5tb4t65STzj7SQ2/duoW33347p2Paxc3bJecfOfPlXTqSRql+66h+ALnjVFtEQulmwxmSdoLb3duYnEwwO5uh0W6g2W0iSy93yq6RwzpAevGZ4PJu2EKA1fF749lF0Nb1nL/baEPg9Myy9X2vhfQXgdhsmWF2PMNisNjIo5Yj+Le809UKjmiy1qpH1kfpNd7lmkt1Gv9l+WFMXk7rkm/55mcobj4eU4UPhODC22fxA58zUJblgq+TX8KaUyH95MofWrf8l+VqjkzX/NXmgvSTWPrFTfBlSD0wxLHt4iWaTA2pPwa2aQdeNfBdavP5+ioD2t39wQcf4NatW/jxj3+M5XKZ34m8y8FY1/zV+GasfNTslhC91leehp/8/qrwcd6Hsp2ueb0tXKrYE4Cu11ntKKOj+OxA/tyl28TW66pTs3G5vLLmSV0yydUn0i9E+El8rTEM6a8Q+aLxFh+9VaVFXk8ohPJArsP49OUkKZ4EtlgscnuI56UTpPb29vJnPBDLy5PBWJdNH9IenrcslJUfoXksvdEnc6rOszJ68MYxxdsUWNsov4ow4Iqn7/m28YkpkyYhQbvdRqvVyp17wNpIOT8/d+5Ems/n+E//6T/hs88+K9yNNJlMCmURTCYTr4NkV2HbtH3V9bzqsE0BcF1AQpgfL1IntFotAMiPcwSAe/fu4R/8g3+Ae/fu4f79+wDWTub/8T/+B7788ksVRw4uRU2DWIfjTXB4cPC1P1aR8xmKsU4RK9CpOaJDFOi65ho/Wr8uRTfUGa+Vw3dh8Z3MWll85x8FJrVjbKgebc7w97z/LaegNJZcCqO1a5GCSGma4sMPP8Snn36aB/roOe8bWUen00GapphMJhv3a1pKsVVWCLgMPAJ55CjVCyAP9FvGrPytlc/v45H0Rc/lHYMuZ67WT+PxGE+ePMlPHqEjeKmPx+NxoS98ThYXzYXosiGOMZfRKPNqdxrJcuh/Pp/ntCjbQjtTHz58iP39/fxUBzlneD+122202+389BZ5EoSr/do8GgwGGI1GeXv4XbcAcufr/fv3MRgM1uWNMqyy9ek1y9VyzSsuAqSNTgPd+10gZXM+wfoo4ovvG7tncXnUMb9vNr8LFiKtfM7yXf5M1AAtIAK32fp/MV5g9MVoHZAFc4xcFLycLIN0X41eLF4aAtYiMl4fOcGtFd4+niDx13iLxtN9wPlLVbvBlb9uHasOh5fkS2XbznkJ162lw0suXnaVR3nqsOXK+hN2yY7UeDn99u0kjZkL1E9cHrgWJvF5GEtHUrYT74+1X7YBIWNv6SgEMXxM1qnpotfdJ9cNckyIn6Rpitlshul0ml/7Rcdtv/vuu2i327h79y7Ozs5wdnaGJCmedOKr57rBZeNkWZbbEDG7p3z2mjUH65A7ZWHX/MxUhnUqTQhYdiNQlKsEPt9QKEgfsixHnphi2RGu8svgtA0IlSfa/NFOdSGokx9Le1bauSH1xc5zLX/oGPAdq5rct+zZUNBODIsFbf7wd/w7P5XQAkozHA7zZ8PhEC9fvsQ3vvENvP/++wDW7T08PESSJBgMBk6bR44xPb8qCNFxfGk0f1AMLVlpt90PWvkFT0UsQ4oVJNtgeFWMOC1/HcZh3WA5JTmuUsHjQoznH4/H+fni0+kUn376KT755JNC2Wma5s4mWacFu66sb5u2XfXsmoK961BV0apKi5aBG1J2iKPH987leA+pm8/9ZrOJ/f193Lt3D2+//TaAtZOb7iXk9/S5DCEOPnp29Z+Wtk6DKaQ+X1oXTq52u5y0oXi58CjbVy7aukq+5HJCcFzqNLRcwSX+XDOMY+gg1gEfUgeVq9GBD7hyT4r5+fl54b5GXzlpmqpBMspbxmCxgia8XMvxaz2n76QHyV12IWMTQiehdMnrlPknkwmazSZGo1HuvMuyLD+amAcOpCNC9h03+MryUJ+jVcoUOYerOKgAbARPeHkUjAUujx8mA9yqN2bnfQjwHf28Tt4PRHd5vUsgm2VYTtb34GYNNm6rDMvZEkmaIGmwdiQXfcmCrWgAjWajeERxdhG8zS4CrpmgV36H60Vwlv/m6bJVhmyhjH12mYZ+L8dLzAdzZEsRjC3pCNRsExf47DLNsUBjlqZpvgCF4xszZ7SAj9UGTQ/T5IWGi0uu0DyXaWN0GSnnOJ9ytcMqN8S2seqwxlTrC4sPyzIsXliVZ/Fyyvg8Yu3CXbIXffoJUKQVS5d15ePvtUVOMq+mC8T2mavvfbpDGR01xMZz5dfy+mwOVx/KOewakzr9Oy4bxMcPt4GPVrbFC630i8UCw+EQp6enSNM0X3hK3+nobdJjLNiVeV+GxwHFcZGncWlptHJi9fXQMmJ1Dq3csvkseqpjvGNksIZDnfa2D4dYW0XaUi67MRauaq6V5aNV2+nSmULmQllfUVU5p+nPMVCH3Ar1+4SmiZEnBJp/hY6uBi7t4/v37+eLYrIsQ6fTwWKxwGQyyU9hsOZ6VTkaQ6M+HTG0HF63j8+XbV+VvGXBuWzc12lVmVkZBboqXHWdddfnKms6nRaOQNF2t/7lX/4lPvzwQwDryf7o0SOzvCyrtvKKl3PTxjmEEZQRTmXhJvbhVUBVYV2lntBypIP4KvuU7rJpNBrY398HsBboDx48wGAwwPPnz7FcLjEcDpGmab6LpKwiRFCX88tV/jb7sYzDltKT0zfkrkceXAhRLnzGiKtfrDGpqy995VRZbWg50bU+8Y2ZJdfouwtPGldZHt/tzu/7CHFkxjxzOdpcwYFms2m2V9bBd/+22+38qgM65kZrm6Rhft+JCz9XMICe8/HyrebmK/XlnNL6K9SJodXNHSp8vvN+IV6QZetA7C9+8Qvcv38f9+7dy4/UXa1WhZNJQuiC6qrCp0Lmo4veXPRj5ZHP6fgl+kzTNNdhj46O8Mknn6Db7WJvby8/cp/6WDs6mMYh9jh0aoPEV5PdSZIUVuROp1M8fvwYJycnefv5TnIO2TzD9PEUGztXgY1naSdF581OHrjNy0twGWhNUNgJmx9RTPVZ5xBnwGK4wPCLYR7Q5Y556g+qM1tleSCW3q1WK3We877in5Lvcj2D0z7/J/lo3QMp8wKXc//w8DBfnEFXsRAvlE4K17Ht8s4uwkkbY8vZwftiW7qR1BO0+eriF7F6AO9/zYEm9Z8yekaIU0TrU04/Gn3wRTuUh97Rb9f9pBa8qnZhDM0STpL2XTRH75bLZa5LcRnFd+/I8fT1gSUHOD+6yRAj+zkfkjuhALuvbgrUMR/K5v+Lv/gL/PSnP8Wbb76JW7duYTQa5Quf6U4/WX6IrVJGb60CVp00X/i8jp0/Pn2cw3XOy5vg++Jg+Wkt+tqm3iHflbFNfHZHbJl1wHXMxVCI9YGElBEDkv40WnCd0sn1NR+OWj6ZJmZMXGlDdM/YPBbwtsuri6y6tfrv37+P27dvI0mSfMH3YDDA8fGxt69i/AlampC+LztfYuRmKEh5JOk3xmeptSumP53B2G0zGWms+6COAbhqxlmmvlAByo1L2lrvctwmSYLJZFK4T4s7VCRj0yZuGYZ/HcLqqmj3qiBW4d1mndbzkHpD2lGFH2j072KSoaCVaxkVVeuKBe58I5zSNMX+/j76/f6G0xJYH0FKd8mS80MLYPjANQaudlcVrNs0uPn4VuHfmmNWc1KFgBxfq17L6esruw7YBp3HOj99PMH328XbtHnGj1RarVbmfVCWISd/a4qhD3/rGQdLSXbJdO5Al7iUmbMx/FI68avU68LFWr2vzZ8q/CBJ1sGbzz77DKenpzg+PsazZ89MY87Hn0LSuPCJzed6bo2rby7x35zO6PtoNMLR0RG+/PJL9Pv9wk5inofjQTtZt8X7pJFG3weDAabTqV83yIDVYrVRZiEJd3ZOVnnAtdvtotvrrne3pw2cn51jNp+xguijGJBV+WMGLEYLrGYr5BtgPfTP39HijlBjNYT3afW6Ai4ajpx+KJBOdpHL2WvxF5/uGFKGltdyOFZxomlzUdMbXHha4+ADWb6PfrYFLjqTciUGXDpCXTKpDGyrLzk9yraH0IZGD1oaqyxtvla1E6zyYuydq4aq4+uiW5etFzrOVfiWxNHiq1r5sXqKr/467MnpdIr5fI7j42PM53N8/vnn+WLoxWJRuA4lptyQZ3VCrI/Gop+qEGILWXms36H9v+0+rgtifVEEIbaOVVcZvGLByiftUSuPTw8oi0MoH9oWhNpuZfWbOtrjmmNV6vGNeVWI6T+XDRDSthA+5iuL3k+n03whMAHZwfyULTohqOoRzBoeBHXZNCFgyQWLP4RAHboA4SDL9YH/QqXXsDNgMbLZbFYIsEpYLpdoNpsbRw9nWVZYsbdarTCdTnOnVggur2E34CoUdJ8icJU0se266iyfVuJTuXXj3mw2CztUsizDwcEBfv/3fz+/M4/uJSR8Dg4OcPfuXTx+/DjnHSTEQpWJbStH1wFSmSir1GbZ5s4zPvbyPg5XPVXuxvAd+XaTwHKklnXSaQqTFiiycEmSBO12G81mE71eD9PpFMfHx6UUMdk213hp99aG0IdmSNLOIU3ekwOJBzMkXXOnHe8714p5bRy1gAHHmS8U8RkoEnigTo57kiSF49o1vCROBNbuX14P4dtsNjEej/Hf//t/33B0amNngcsA4c9chpDlXNecXxb/0PDQ8sTMMX6UbKvVwvPnz/Hs2TP88pe/NPHnzpkkSXIZR/OjbqC6iV5o5/iXX36J+Xyey1u5U94qxznPZyvMn82xXCwxnU5x671b+PYb38bt27fR6/bw53/+5zh5enK5Q7PB6JruqE0al88zYLlid7tmgLZx1uWIp3d7e3vY39/H6elpvmPeBxTA5Udya7TIF4jlfaFcyyL7kdMjlU+7Yul4L3JGhPBGyYMkvbvGlZdTBXxOUYt38D7iZYXqny4+6ytHo+0Y3TLUKWaVpT3XrvFx5X0V9KU6QMopl75BoM1rnx7lkmUynW/cXXPQN5/kTtxXCUJkDvUvXU/BeelNt+0sqDLXuX5H+vjR0RFevHiBzz//HMCl7Gq1WkFO9psAVRztZdpel3/pVeh7YFO31toVcp835+Mu+aj1mWbDuNKEgo9H+8CV9qrHvk590LJLNYjt92378srID0tf1Hxr1wWa3h2qX4fooz5YrVZ4/PgxXrx4sfGu1WoV+PTh4SGm0ykmk4l61yqHGDqV7VgulxvX+tQBkl8B+iltFm4x9ZTFj+MWC1sPxpZlRrFOt1dBwIaAr52yX3z9xBmbi5jqYByvYbtgKcia8zsEQufYtuaeRqsaXtazOiBUsGrfNScA/6zqdOJ5+RwmxxMdPQwAk8kEWZbh5OQEg8EAZ2dn+TNZVmhfxrQhpj2hUMUgDAWLJ7ocP6G80serQ9PGzodtKkl157fmi688wG8gUpmWAq31uVQGs2y94vDevXsYj8cYj8f5sb6x+os2pjLI4KP1GENWc6rzvqE0dHQgf28F2Xh9vAxXX2pttvDVxkoGYfjqT196rQ6Zzkqr5bXScTyIR4fgoOHieiafl3FMhDhrQ8GiCateOQd4fVKG+niGLFv7XVV2JEmSB/x4wFD2k4/HmE6HxQrZKkMjaWA6nuLo+REWswW63S6yVYZ2q43xYrwuJxMOjAxYYQUs2fPV+rl5fLGnrQRpmubGvjXGknf1ej10u10cHx/n/NGan/LfogWtbynPfD5HkiT5qUF8ZxKVIwMN/Lm8QkC2qeyuYOuZBRZPCskX0nfa8epauipzxuL1Mo10Dmvzg9pi4a3V4+NpWjqNH7nGsgovCcHzKoC3wQpIhuAY2w4+7vSdHyUt9S76Hqof0hhzHsD7PE3TXH+wePWugoZvqL5i5edl83lmzbeQ+R0CuzIPYkDiSvNGyombRFMusGwADSzdOyStlS/Gxvk6gTYmMbKpjByzdHOuO3H8YkDjT2XsGe1dXfo/QWw5sfaQj761fi5jc/hs47LzV5YdY2dL2e/S/WU5PtnmwjWUx2l2T4hOWhftyYWrhIO8n1zaVBwfa374IETvqJM3+3RwX1qXveDD3aojBJcQnfJKdsaWUbBeC9fyEDPZ5/N51CqTV0Wh/LrB133cYp3IGvjy+/rYtxqprNONC2NyOCwWCzx+/Dgv58033wQAfP755/j888/x7NkzDIfDjR1sV73ibJfpsixuMYqzVh83aIDNMfEpr5Zze1twFfMq1BiLpV/u2JV5Xfel07PFYoE0TfGtb30L5+fnOD8/z++KonShBqkMPPB6rLQaaLs7ZFpK43J2EtBxNy665Q7/UKeK5cBzGVt8TDRnOb9zTu5c1QwR3gd1gtZXjUYDrVYLaZqi2+1iOp1iNBqZRqpVTki9Fv3EypgQI8LHz2Kc25I+Go0Gut0u0jTFcDhU74ktg3OdkGXr+4DpWFyua1jBA60MbawoaNlqtXB6eoqTkxPs7e2h0+nk98BPp9NCcJMHFvgueu4krto/aZqi3W7nPNcK3BBfXS6XuH37dn5X/XA4LNxjzfkH5eOf/Lgt3l/anaA07/kuaXrOnSxJkmw4Mqg81+5ugqp9WEYntWjLlV77zqGsY9KSazJIGuLE4Gm0Xc9avrKO3thTanzp+Hi4HI1WXk6Hu+T3KEtnIeNipZc8SqYLGTufA5b4I6cvurvdZ6NtG8rSQKhtyttsObm1uUn9Y13D8RrWkGVZLpubzSaybL0omsu3q9ZP6gSpq5aRHZZOGsoruXz+uoLV91VkeFWQY0J6WFV6l/Rxk+ePhBB7OTQIVDeUmduuuSx1eVd+WVbMSRVl4k11g+ab4GDpL2VwJr+Hq36tnljf0lXNubL2SCiE+EwJYnf4VqG5WoOxoU64KuW8Kky4CpSZKJoi7XP+Wcr5a7h68DlS6oZtKDx14FpXgCzGOewykC3lIbauqoKZB1XJAXp+fp6/f/ToEX7zm9/g8ePHOD09LfACUna4EIzlMZJXuAJBVZxOLkdPbKDBKtvllKijPHKOh/JaTbGzHClaXpfidVVQFy8JkVfab21uUoBif38f7XY7V2oHg0G+m0rmsYCcfL1eL98lZylyGi5yjF38Ro5pqFNZ4quVS+8ajUa+UEsLTJBDhPrQpdjL8uV7n2zjaTR69jl8NQekliZEf/WNkc9wJKf7ZDIpBJh88sVFgxpOPn7iAkkLIbqApGX5TvavxsP4O44/P9aWlxkbjNmGfUK4akdWh9YfOneprul0iuVyiTt37qDVamGxWGA2m2EwGCDLsvw4RAD5PXaUP5SOLYiVI8QnaPz4uBHfkEeEa84365hZSq/NE21eUVp51zeHRqNRCGhrbbIgRE/kaX06TKhcCAFtvlDfhji3rT6Wz+R8l+Pga0+ofmiVo/EZiR+NMXB5aoymU2m0KNvN8/Jj/fkzjk/s1REu/m5BiP4e2mecBq0yfHLKwtslA3jdElf5nuMt65PpZRpNZss+qEt/9YGvn6z0BBqPlL81viP7V17pEKIDhOAbC1fV71VA431JkuDevXtYrVb5aTnAzWgPYDusQ2z5EHszls5lPZpNrOW/bru3TqhCO7H9oNlcdUMZ20TmC5GDPj2L1xvbxz5cQtrjk6FSNskTeLRPq35f+yQOIXaxy+b09btWp/Ze6pM+0OzRbfBel25Wpl4trS8QbfXpdDrd6NOQDQbac+K/MTTuG1cL921CqD+DgNuLZeZ3DN+sfWeshXCIMlnVWREKMcbedQjzqnVahhi/V9bHZH1wXX1TBkKVyF2EqoycoE6BVJdDCCivwFhluxSDEF4jywnJpykcmhDkaTUng9UGiYevPir77Owsf//FF19gtVrh9PQ03zHCIWQFUKxQtRxYVvqrgFDnRagxGgJaufQZctSeRRP8+VWsFL6OMfP1DR8T2Tc+5Z2O2rx16xb29/eRZevAD92b7lPo5Xu6O3Y0Gpl3pvqMJZeuRH3BnYdZZq8S1/rGcn5qbVssFhuLNuifAiyuOc4VX82pJ/mqy5GiBXOsdlh8Uho1hEsZuuZ9odVl8RLauct1MUCXDxqE4kttk3Qo+8+lc3M64/hptGTNC9czn9FKdc5ms5zmtDa42lE3yHZS/2rBO5es0PQUa45yOiUjezqd4o033sDe3h4ajQbG4zHOzs5yPkR9xE++4f3mmmuET4gTy9U2ToMaj7LuXKU+tRZ08T7jQXqLxjTHEndKSDxoB/tisciPOi7TPz6a9/GfmPnlA0uuUPtj7pfTcLTyyDSyvb5+dckL173ksi7JAxuNRr6DbTabFY7hJ+A0YrWf46vp0JK++A5Y7aj6EAeWD0LtHA343KJyiD60/vbZWdpv65ksxydnXHmtPuBym5dvOdvKQJUx9MljDi4dxKeP+MaI9D46TaCKrlQGysgdgquaO9Z4UP/ev38fy+USjx8/LuhPLoc4panShm2AxUtdtO7qyxh5ZtkWWrm8/Ovuw1Ac6vArcPDZWj4cysoPiz+H8mxeTmha13sp812ypSpfK2tDhvyWuoJLV/PZvDKN1WeaXLfSa2lcdWj1uOoA4NTzQ+dO6FyMBcsWkPWGlO3qXwt/S/5Pp9ONtPKEJAsvTf+2fofwFh9Y81Hjja55Lp9r5UqdSdqIWl0hPJrTgSWzXH1xJccUX7dQlFDn5L0qqAMXH4OMgV3qGx/EOhFuIuyC8hkLLkM+hAlzkEIhxEC1ynb1I2eoLiEpV8NruGxrvKieVquVO/9PT08xn88xm802Aix8V09ZvuBTdn3PQ+vYBriEsZZGeyfBR0MWvfqUOqufX2XwKTgynZbPUi4BoN1uo9vt4uTkBLPZDJ1OB0mS5CsLtV0T5HilOQWsHa2j0SgPHoU4GaRSaAFXIqVjTDp1Q/pKvtPq0wy5ZrOJvb29/GhUUu7lbiCp5LrAp1zzfiprPNfFO1zlUJtdPMRVTl1zOdTQDQkG0GfIohELyhiYHD/teN3r5HtaH/K7R2X/lzFWXXKTOyYajQZ6vd7GXKOFJpPJZINv8LHV6gmZa9PpFOfn595jK3nek5MTLJdLTKfTwphq8z3LMufR6LFgzQXi7zR2y+USzWYTBwcHGI/HG4smqtZJECIbtN8x+aSscI219luC5XTSyrVomuPj07VkXfIIadf8iuEPUg5beoRrblAaLdDC88ljzC28Ld1Qw9HCpQyP5PXK3Xw+nVb+1vQRi/eH4Gr1WwyPcNEe8QLu7N5lu7oKbj6ZQ+/5Qh7eb7vaJ7sA/CSKTqeDb3/725hMJjg7O8N4PMZgMMiD3BrctL7ltsKu2KO70IehOFTFtY4+j+GrLv+E9b6uq/BC/XS+csqUd5Xgs4s4H5ZpeV9LfSTEz+X67UsfkiemrKsEH11bv115rXyWjqb59yxfjARtUanEjWyckM03Pj1elq3lj+UhWl0+KOML2sa8j7GlNKgtGOtT0rbF6HaJgdYNIUyAIJSRuNLeVCgz6UPopqzjd1fgqvGONa6tdLEOV81xURWHMnm2zYti8SSljYJEckdskqzvoosxpKoKVyu9y8G3DYFcJr9PgY3hKVre0HI4X7IcrT7YdZ7mc/iGguY8lkCBCzruk+7H0mhUGjWLxSI/0pjyxwYRLBw1+WM940Ean9PdB5rTjQcuKPgjFXoNtzKKMs/rOu7ch3tMvhjcrLHiAbm6QevTMu3Sggo+fF2OmirjG5JWOhtcc+KqQTOaCWQwwpXXAqvdMqDQbDbz3bCUJknWO2RpsYS8a9FljIfQx3w+x3g8zu+q5W2SeBCMRqN8cViIfOe06rKFyjjleLmcf1IAqtvtYj6fb7TBx1uq6FA+3EP0AimftHxV5kuZ9mtz2JJTrjKpHE77oY5brVyJX4xOrzl0XL85WAtbXG2RdOqya3n/xvQP5bfeu+imLh6s6TWu+kKcypIXuwIDrjqrgqRjiWdofgt8+ol8J+ebTMP5b1md5jr8T9vS/0LrIfm2Wq2QpikePnyYn5azWCxUm2/XwKIXH86hsjiELkLlQxkoYzNfN4TyjKo2oKW/uerWdAyfHeiSuy4IaYtFq65826CJuuwUqZNbOpQmv2Q6Xx+UxctVThkdXUJs/21jfms6tmXza30g55Zrfll6rk8nkrhxIF9ViN4EFBdjEVhXrIWUVweE8iBfmjrme4w888GV7IzdZdCM7ZsmpF1wFcredffZqzReryKUHZ9QAV3GyVIGhzLtCHXQ1AFa2WmaFgIa/LixUIH6qkJdR/tSP4asXuPpXc5mjp9U/uTKfe5ct5yUu2z4u2AbyhLvR+4codXqx8fHWCwWaLVaeXptHtOz6XSKjz/+OD+CkO6jC3UYckerVR8H+t1ut5GmaT6n6XhliadWJ31qNMudxeS8TJIEnU4HzWYzT0P94wKtHTFjyueHdKheFw+T92uHBAbqgpA6fDKL4+wbC353HOc1tHiBvye+5FrcE2LsWbjKNtXV31WdJBKXJLncYRXrZAjBhebgo0eP8OTJk3y+El3SndXE33jZmuyRuGj0IdPN5/N8/EPbd9P0dArOavyHoIyDU3OOaXy6jGNMc9rx9/J7lmXq/eguZ5prTspnLt3cJRd5OzR+oo2H1lYXryD5SYESX5DBcq6RPLp9+zZu376Np0+fFna/yeOOZVt5ebxcek+/fTqeD9+rhKusW+pRsn4596TuwOdACD+rE6rqL74jb8uC5BVVZeRN4/0cysyz+XyODz/8EAcHB/jggw/Q6XRw69at/OjIWB1hmyD1Ow04z4nVV2QZWjkyHeFTh5Nbg6umx236mWODENsEixe56rfsWwll9KFQHHx41UFzVcac8nJdiPwNHGjhpfauTggZT6t+jX9I3uMqP8R2teiwap/4fHahEEpbWjqr/WSruPyQITtiQ/CSONZFa1elK/jG7zrkciEYGzoZ5LtQpH0CeNtgOR9cv+sGnwKybdhGP4fgva32ae0pwxyqCMltlR1aZt3OSa2OqnljBEAZ5TK0P6oIDk2h1nBx1WPxzlhHkoabVg8JHW5sSYFdRskta4SVqdPqZ1//uMbHVX9s+8rMEx/O8rvWVssp6nIaXrfh74JYeuc07svjoh25QIF2t7bbbWf9BHQMODeQQo51DdFH5Dv+n6bpxnFnvEyX49eHk+b0JUOPO4f5blDLWc7bZRlfPpys7zG6qMuRFGN4+PhJCB1bssSVRj6PbT/hphl1oW2i7zGrY2U6jUZ9PFQbuzqdcjH96Kqbz08qR/6Owcv1fDweF+Q58QJ+/yXnaxpePpvPokHXHU6cX9Bdupb8qaKLaXjXoZvw5y4+Zel9vrK1dxpNldFfSPaE6Jn03Fr0FaujWbzVqj/UF2DxJ9lnZXQ6a/6HlkPfl8tlHnA5OjrKd8VpuLnmgG/eS9lcBkLoqi6b1TdHYkHKDj5PXU5Il/5LfFLKxauGOuxSyw4M4WFl9JUQua3lrwJ1lhVSV0xa3t9nZ2fIsixfEGU5wi2duAwfvqp+qaNOjZfyI+gtm7eu+q8SYuy82LLqhqr8T5P9Pj7j65+QOV9mvkjQbCIX79T4YKxd67OFYvhdqE4VgpdWXhn9zQU+W6oO8I1jqO3qy1eGT4fU7fOVaPa1C6+y+j3lteZvXXpTHTYhB9cYVp1XdULlnbHEtEJ2Gfk6ZdtK8C4L7avA7aqNDC5E6mqfPHLNd2dVCOwyXYTAVRonZaBu3KRCEFK+PO6yrl2RQLH/LYdAGWXNVY6vDO701nCqQjNlHFfb5D0hTuVQKIMnvzdQ1m8pQvSd3/8hIaQd0klH+GhjbjnQd41/SENI9o+cu+TwlzuGtfLkHVjL5RLj8Ti/X1kClSv5DJXfbDaRZRmePHkCAM77OCznAq/XRX/NZhOdTgdpmqLZbBaOROZ0RP0REkjQHB1ED7zNSZLku0doV6TsJ56P8sTOJ81w18rZhkyJMaK3qa+65iYfU81ol2VoDgN5ZK1v/ss6NfrW5DA9k6u5JX5aW6x28d/bDBiEpuXzSM6Z0HLkHAzFh3amcwcm/S8Wi437YpMkyXkDLRqx+JRG25K++IIT2Z7lcolWq4W7d+9iOBzi7OwMi8Uix7XZbBb4Xgz9+cDSOeQCGf6dB85Wq9XGcfNV9AstveSV/LlMEwu8fbJMK73PYePS46Sd53Key93zsk6JM6+X71KWeWJ4MelBk8kkP1nC6muS/bK9WXZ5QgDtEj84OMA777yDJ0+e4OjoKD8mXDvuWkIIfWu6nlZOqG0jaTBEFmwbLBkidypo+qGUy1LOEVg6ZQwd1WXPASjwReLRMeDiIVraEHyvwh9XFq6SPi35qPEdoiGyCejZ3/3d3+WyZzgcAoibp5Teh1doOZqsXq1WhasOJEia5POS4+PTGzTQ+LnMq8kGLX8oXDefu2q4qvaWqeMqbbmy/RDDDy1+rMkMS7egd1yGcV3E1T4AG34lyWtC7S0Nl1CItavqkDdl/VpSt/Dl03SKkDpIb3RBaF+4dFb5W7OFJN8NwUXq4bvEQ60+K4OjpoPF2hmxEB2MdQ0aB8uBUgZ2acDLQgyT2FZ7r1rB3oaQDSkzVvkLzberECI4ZNo6GVcI+OrloKWxnKBa+b62aQaJC4e6nb9VoQydckdtTLk+Z7mkLekYiQHXmPCyLQPZwq8uvhGS38fXNd4j2xY6TjEyxOeg2XWw+tY3tiH8ne4/JKeqzKfhwssnBZU7WFwOtRCcLGd4o9FAs9nMncByB47E0+XoCAWf0q7pehrfiFFmfYZOqB4aAzFzxsJpGzpEWZkZW0dI34UYcBbIndQuGc7xCsFlF/mb1k5rPhIf4wt7LL1Ezi1tjnHDP/TeZWvecpkU2pfUFromwdK9eN1avfK9Vo/Ekf/24WjpGfP5fCMYq/HkEL02ps0uXEPwlnWGyJcYB7pVjwZlfAAazqGyQOsTC68sy/IgmA+vWF12f38fd+/eLRz/7AMfvfL5F4rvVdv7dYOvvWUcoloZgO6s9skpnl+zsUPqBS6vkuFXZoRCqH7l4r+W3lO33HSNo4XLdYJP17V8elwurFYrDIfDXAbyO9a1MsviFZNX469Sdlp1+fwBLh+ArDvGfvVBjH+pTPnXBXXNxbraG9rPIbpH3eDTMWXaOuwqXz0huFgyzOfzcuEZy1N8OmCoPyQkfQxOofaKxMVHp7E6XSitWD4qXkYdNq2W3sLJZ0f6yrbK2AUeGjuHY+zXuuoPzRscjPU5Pa8KYifRLsJNx/+qgPeTXMkhDRdaCcSNqiqOw9ewuxASHKhi4PGdJryMsoKvKsQKaA5ZdhkoumrHDM3HUKHvKyt2HKsKyrL1uZzvoXRkPb/JzjUfuJzWq9UqlwEyuEC8P0mSjfvvXAol5T0/P8dwOMzTaDuLqXy+s4c7JmjHmeZs8bWP46c5UWkOU9C12Wyi1+thPB4X6uO7fCzeFWoI8LppDtPK+dlshsVikTsQ5/O5indZmbttGg+d2zGOYEq/DTzKgM+4rgN3C39t9xs/slYapRZP5Ed+a45Oy3FxFcDxoPkvwdJDqE1pmmK5XOZHnLbbbXQ6HWRZhuPjY1V2ljHW5Q5WTT5ZDlQtncZTJS7tdjvnF7LOWOeTDyeJjwxu0L+2E1d+NptNrFYrHB8f5+Ok4eqDEMcLlzca/WrzguPrSy9xcN3lLHGqAoSbHEtLvmj5eVs4H5D5QmUbvef9Nh6PTbxC+AnJRuJ3w+EQL168wO/8zu/g+9//Pn7yk5/g5cuXalst+Uw0qt1LzvtVtqUqP981oPZYwWxJX7xvfHeWW7xImz8az6wLSFe7c+cOWq0WXrx44d2Nb42zXOS0K/66VxFc8kg+Xy6XOD4+zn/TbnqrXNdvrY6yvFprgzxO2bV7ymdHhMg/DReXrLhqHW9XgeubvD924f5hCaHjz8Gl69UJoTbxNvvUZZNpurAFXA8BNv3lIdcmEYTqU1cBUuez/PuxoOnVIfreVUFdY8B5hebXJoitZxdoIxSkXijBOqVHK0c7Pa8syDqcwdhQZ1EZA85SNHxw0xTMbTrceB2x6W9CP8pjJy2cpfEv32nfqcwqyuxVQOj8i3leJ8TwCNdvIE4AaWNJnz7HYhXQjAyXkRLqgPLBdRsiltNUPiuLpzb2Wp0x4+kLDoU673wQ66R1GfNaH7p4mMt45XXugqFmtdsyOjSHm9Y38igfl+Kl4UIBXF8fhfQfx0XD1yrXZ3i5HIMyyOByQsbMH4uX0Q5ifrweTx/qIL8uo6+KTChruPscWVelj1m820qrGXA+fkJzQI6vNodd7fb1iezTMn1Yhr9rDtHYuvnRqLSIYjabbegXLvnlAikXQsc8pEytPAlEI3TcLwEtquFluvrPN9/ouGMZtAnRiV3tkelcfDX0WUgbNdlVZtwtXLS5p7W9zHjIdmgy0IWfpedYwSWJp8+JK20DVz7+3NVmWjDRaDQwGo3w/PlzHBwcoN1uF/JzXDUdN8RO0OxVH/+8LruhLtsrhH6tdsa0W5NVFi4a/7dwt8rh9dHiv1ar5dUhLN2h3W5juVxiPp872+XCy1ePlq4KuHinZeeE6E4x/MzCK8TepPkoeYqPb/hsXVeeOoHkZ7/fR7PZzPGYTCZYrVaFqwxidHWXPRND3z57MBRi9Bae7qp0clmvBRwffj0Nf3/dNj6Huuy7WL9NTJmav8Wq4zroAbD5hKajWu/oU9K11c7QeW6VoeHkA4vn++qXaWPpReuPKrpESD2+56F8LrR/NTovq+f70mxjntTF1yz92Wc7UF5NT/TNA6t8Cc5grG/S7hLj31W4Lgbugl3EiQMRuDQsOLTb7YIioq1YiBUGryEeyjpTy/IOS9BuQ3nSBLzmENHwoTwaDZa9r7Yu5bYKhPSpxNNyREkIeRc7pnwMXKvsZNmu+uQdsdIgdyl02so+iZtWr0xL9aRpaiqT1O7lclnL3dpVIUS55mkoQErOKqk48SOB6VlIYFICOR1oJxV3TFjOQOkYkE6LmHbzdNyxaPE0WR7dSUcgv/N+jXVM8PxJsg7OjcdjTKdTjEYj835iWQbHX2vTLuqSZXjVtttSpXxNnlk8T/JreTcxBcA0g47mD72no7QpPz+umIDfy0xtdN2nzXG+Kh2P81a5E1bKOG3+yvbO53N0u110u12MRiPMZjMAl07SGL5hOV+oLssIjQE+Hj7H6HK5xHQ6zZ26HA+pF7kcAlod9CxNU3S7XUwmk8Lx8hZeGo/UjGstDdGsVV4MyDosfcHXP66yLXrw2USazmbVY8kc/lvyEi5HQp052i4g+u3DU9P5ZN9QGfx0JalnWUA00Wq1cHx8jBcvXuD+/fu4detWAUdtnDXas2Q/PY+92zQEtsFD67K9fA4uufhN473W4iANV3mqg2u+cH7ouneY6tTmBC9rb28P8/kcZ2dnAIpXXVjlrlYrtNttHB4e5ldthEDVMb9pfpWY9kq+q9GgZfsBmzttpI3G02q+gLI+lVhoNpvodrv4xje+gbt37wJY7479/PPPMRqNcHp6CqBIhyGytQpYdm9d5W0737ZAytHZbIYkSdDtdq8NHwKXnar5WSzZVjd+Fl6EQ6w/4jrB4hMcP853uO+EQ9mTXkLwkeUR3/PVY/FbrptZ+NY9RpouoL27SihjB4SUVxW0k5vkjmyCbfhEYsvUaCWmjCS5PAFPO2Wpahuj74wNbdC2hPWrDnVNlJvc3+Qc1+Du3bvo9/t4+fJl7rwC7GOdXMaR6zeHXRHIdcJVtKkuHiDHsQzz0xxHvvzcIaoplRwXC+cQw86Fs0bDLofbtowijpMvj8tJ66szxPnuK1tzTst+5M6bELxCIUTBjynTRV9UXh2O9l0EfvywxUt4+zVFUJsj1nt5nKcLpMFASppWV1ka0wyt+Xye3xlrGSc+vDnd+/onBG9enotvWPPtOnSVGIMhpI9ieUQVI9Kl17joP8RZbB1NFyLLZfncOU7OAddCJNnPlsHvw6+sXsDBkrF83ki6l3PANb77+/u4ffs2BoMBBoMB3nnnHdy6dQu3b9/GarXCL37xi/wY8ph2aePEj213tVe2TZYTUp/MQwtFeJmybyl4L/HlaWUeKfc47co0WoBGAu8jrY1JkmwEoyXNuWSViyYIbxnQCZEnmnPR1VYN71Dg5bp0PatvZJ28T1y4y8Vv2vzXcKX71cmJMp/Pc5uR3oeCNiYab8iyDF999RWGwyHOz89zp42lw2tl+3TvuuXlTdAXQ3C05q4MqkvZQrRg0bJLblL5QHEhYIieT2m63S76/T56vR6m0ynOz8+d9GnhQ9dX0KkldcEu0UcMHWg+njrbkiTJhuzSZI2mE9UBITqYBkSzq9UK0+kUR0dHmEwmePPNN9HtdnHr1i2kaYrT09PC4gS+8IHTt483UrqQ9rj4q6udLvlyU8HSJ/j7+XyeX3kh08YG+suCZk/S3OA40UkpWjskbmVs6Rias+SxVn6oLhxST2w5PntZoxHX3KjD5uTlawFgLW/ouLnS1WF7W/Vo88ylh7t4TF0+gtB3VemxbHmaTWPZPxqUGeu6gexNrsfxdxbE9llInxaCsVoHaMbhVRsDLkZ5E6AKYZVp803pJ0txdBkSb7zxBh4+fIjBYIDRaARgLRD4sVAhR3JWVYpvgqLnM+pd6cr2jybQQoVtSLk+XuBT4F3pZX3NZrOwWt7nkOQCWrvnTqP3EN5Xhf+FOK7Kli3r8fUP/x47FqFp+TvN2Ve1bPmeK8O+OjRnJqeNGLCcl9b9B7vMr6QByRUjbVW27CvaeSfLC6En2XeWM8VycEtjiO/w42lCnOAu+qFg72QyQZqmaLVa6hHBIXRUdme+xMniLS65E2pY7zqU4UnWnHc57vh7KUPqMEItHCTNuox9q528rNVqVbjDMwa/mPkcC5YupLXdhYv1TssLALdu3cL3v/99fPTRR3j+/Dm+853v4Hvf+x5+53d+B4vFAo8ePcLz58+durDGV+R3upOWaI6vZnY5FCy7T9NHrHZnWVa4A5N/8vK4Q1ve+a31IwdrpyPHWePnHAcNd/npon+f0e7ijVxXdOm0nGe4ZFEoD5LPrfSEY4xux/mUxMnSf+SOe61MeRJGSDC12Wzm8hLAhjNYG0drbK32yD799NNPczyte25l/SGOH01/tMBFCy49Yxf0RG0uagFPF9/h4OLZMl1M+6XuX8ZOy7IMvV4Pt27dQrPZxGg0wldffYUsyzYc3Xzua7TfarXQbDYxHA5zWUvtku2U311yRMNZg12gHQ5lxyNkbgHr+d3pdArP+XUDWv1S3mj0askqCw8OvDyfPbFYLAon3Dx48AD9fh937txBmqY5HfLjigk4L9baJdug6RIhup2vH14Ve4JA061cizJocS7pUNc1B7l8pt+0aJj0GzpGXabT9Cv6DKHnutvhs93L8BWrLl6Oq32aDiD1QtkGTQfQZJ7VDo3uKC294xtW5LVFvjZJfHx6qFWeNl6uukN1LdfJKC7boAqUpfGy9Wr0Y6UJfV8HHw6Rw3WVSzvIXeMsy9gGBO2MfRWE3E2EMv1+k8aKO2sI0jTFw4cPcyWv1WoVjuIYjUb46KOP8kAsAWcI1sSqwwl908Fy4tTF+CxHlc9RZEGMgVwH7UshK42RsnVwQ3rXjNa6eIbmPAgVZrEGswUhfct33mv0pfElrX7uNNaUZQ23brdboKkybaCVsI1GI7+TT1NIqf9vCt/zORe0NHyspDNYlhNCGxot+NJLZ7LMqzkoXOVLY4vXk2VZfvzndDp10hHPZxmQMX0ym80Ki0y63a66GnYXedw2wGeUa3QoDdyYubnNfuV04jLifQ404jeawSPzaI4WTV/QdArX71Dw6UJ1G9Zcbshxf/PNN/HBBx/g+9//PsbjcX7Mb7vdLtzf5sOfv1sulzg4OECv18Px8TGm0+lGXulcCXGIyN9aHroH1+VICXEEaHVQOXTUc5Zl6qIdWVeII8rlVCB8Ned0FaBdR1xuS/xcMoSDS57II1hDyiPQgjay/fLIWL4wSfIU7kgk+SlpXOpUZeYl5aOFlZajVaMFizdZ4y7LDwmyy76QUJYXlemjmwC0KE0eeU7AnxE90dG9PK10rrvGVtIJ36kty3TZv1qZADAcDpEkCW7fvo1Go4FOp1MIWsi5x8tfLBZotVrY29srpKNFOBoPk3hbclYDn8y5KXTkgtB2kIxutVqFa7NIJvloCbikSUsPlPzJkm1Jcnn3fIhOyfki2SxHR0eYzWaYz+cYj8cbvJj4mZwvWZap1wRoINsawtd5X4UcHf8qQJqmaDabhfGkeZ0kCW7dulWgt8lkkveLdVTotkDjnZwm0jR1XjXgwlXTha4KNH3lVQRt/mm6uq8/uGzUyogFV5+H+DFifB2uuix916rnOkDDQS56cF37E+v38uFSx1ype8656NslU65q3kcfU1wWqjRoF4j9quDr1FZNcWw2m7hz506+kpmOTQHWffPrX/8ajx8/3iiL3/GiGf70Tj4LAZ8Rft2wLSZiMWiXkAutMyQdd9ZsCzSFgn9yBdjnPNTyyt8kYEIcqyFGis+hc1X8hCtidc2XMvlDlHtNSeHv+aeWX3OaWiDnBvG18Xgc5ICTCgR3CDWbTSwWi0IwkjtCZZtuCmiKkWs8yLnkckJoDjsLYgx+Ggs69ssaO9kGje9Y8kk6zmazWb763mdwcCcKoO8ssUD202KxyJ0+RMtypbPE3+eYuQngmqP03cW/rWd1zUuLBiQNafTHeZhP3vAxjZFX9FxzoGuGbki/aLI1VPb5HDvSWRoqz0LxlguVkiTBvXv38M477+Cb3/wmBoMBlsslZrMZDg8Pc8c6x03rY44nBXB7vR7u3r2LwWCQO+xC8A11PFhOGam7abqRa+xC8MuydaDF5aTlOpulp4Ya/T5nTQhotCVliFzk5ZJFlozRnGca7fDnLtDmqcVTZD5+R7SPXrRx0nZKy3ZauhKBdrd5qP4Wowvwfgrhaa4jxF32hsTdx7t9cBU2bhXnGp8noXoe5Vsul/n4a3Rs4SZ1GDlvQ+RuyG+yBQ4PDwEgD8KQviV5O5enpHN2u92cvqfTKabTab5Q00cTPnnoSyt5OU/rK0uWEwox9WwLSP7QQg++C9Aac043kh5d+orrOdG2FfDS8OYLc+j32dkZZrMZWq2WepwscBlY43UQrXKctmVzclkSUt9Ns30J0jRFu93O7StOP0mS4ODgIN+VOBwOMRqNCjK2zqCYCyw/CNGUPBnFVY7v3XXbi3XxnFBbMaa8OuzKED3Iqp/S8EWRLnwsPTDEvnDpQ9azEBnn4r910F7IuJexNbU5yHk8PfctDuRpQ+0/33MrrUu3tXCS9dSlP7iehdB9XXBlwdjXYIPPSNGY7XULpliwDHX+/Y033kC/38ft27dzpn7nzh289957ebqvvvpKDcZqddCEDzFIXsPNgm0o/UQvnU6n4Bxz7ZKU+GhgOVB9YDnQ6oY6eInl7LquXZmhbaKgJnAZ6LLGWyo92i43qSS1Wq3CfS7kXKQAP69b1sHL4WnpGR3p51LGb5Kc8Cm9sv+pX6kv6NPKG1puiCM1RFkOURZDHPwcP67IcnqydAKag0SHtHqe7yiznM1aeZSX3lt3SfG66zJmbiJU4d1lZJxr9au2WEYLgGh4ULoQfi53xBFIGpbl83c8zbYdfFcFfM4eHh7i3XffxXQ6xWKxwN7e3kb6NE3R7/cxnU4xHo83ygL0oCZ3lna7XRweHuaLgGi++u4UlPOWnL0aDfBx4QEB/ttKDyCXhZLPuUDe30pA/JDXLR0RVj2UjutdGt1bOLrolT/T5LW1W43n1eq0cHClCQHZFouPabiRPKLrAzQ5KMebj5tMq/GCkHZlWVZYLEQBBi43twUuB45sj0YTryKUaZ+cD1J2ceD01Gg0sL+/j2aziXa7jdFohMFg4LSn5BjIBR70GWIPWvhL/TLLMoxGo3yBHYD8iGFKY12RQ7yeAmm0mw4Aer1ejnvIyU6WLmn5bW6qjREDIXOS/EvT6XTDBpb5Sc6RLPKdRqHVpfF7Oqa23W5jMpkE0accvzRNcXJygmaziQcPHqDZbOLNN9/EZDLBy5cvkWXrnY6dTge9Xi/PSyca0Gk9/Ah5Pne4fJMyNgRXrd0+vZDPuZsKFOinncq/93u/h8PDQ3Q6HRweHuJ73/tefkrSj370I/zoRz+6Nly18aQFI/SOrtfR9E8aZ0tn3JVxvG48XMdW80+CbeFLPIHXE1qXtP9kuXXgptmeVJ+lJ5fx9ZTBKaSvLBks509ImTRWfNGOhVsd4Grrdc+fGPDZCHX2WaVg7E3q1OsEn+IaOilD0+8ScKeGC5Ikwd7eHg4ODtDpdPK7fg4ODnD79u1cEe31eoUABt8x4FPYqjhGQ6GK0mApl1etiJR16LiEXFkcqhjw8neokkK050sXMy99BoMLyub1vQ9xcvvmjHReUHl8BbvLKArtWxcOrnnvA2kUa44PlwPDSscNC6InSi+PSdaUaDnnNScON3Q0PKTDU4NdMHJkOy2HtVTuZBCQ+lWjh1DnkYtHhPAPDf+Q+jVeYo1rrJOBcOGLAigYGwNa/1uObauvqsrfuul1W/RfpcyqOGn0FmoMWXRq0SfX71zGb6j+xdP62uXC3QUxNFi3vkjzho6WPDg4wK1bt9BqtdS66PQDV3muZyR/NH3K4m3ad3KkEZ/lu3W1cXHthNBopYwzIJSe6JPvAuJts8rl/RMauNNkmQWcn2vtl7aT5WzT5kuo7HfhLp+5gOPI69X4gIaHT8fV2qjxOFkO0eh8Pt8IUoS0LZS+QvJZeolvDl+F3arBdemGrj61HHz0nn+22220223s7+8jyzIMh0O1TF6vNZesPNpvrT1aGnpOp9sQfbp2S1s4TyaTfEdtlmUFXS8ET62NvH4fnVelE6sclx5s5dVspzIgeaCFIy2u9dmOnJ+75rzLVtBwIQe79FtIHHy29nQ6xXw+x2KxQLvdzq8h4XKT7AgCviCWl0U2geTfZW0XrXyOv8t21uhDa/8uAu/vLMtw79493Lt3DwBw7949fPDBB7mv5auvvsL/+3//Lx9Dyn+V7eN9z/k1X4hAeJWxP6u2xcfbqR5X3XXJYm0uuOwonkaWY+mGVj1amS4dK0Q/006ecOWz5FOozJJlhcJ16TUuHmWltXa4a/aI5P/aaUEWvYfSdBmdOWbuVBmbunSQ0Losfb4svN4ZuwMQSoB1Kb5XCYSr5pyRQujg4AAHBwdoNBro9/v41re+hXa7Xcjz1ltv5YrGfD7Hp59+itlsVrhDtt1uo9PpmHXFrGyNhW0wBJchVActhBguPudOGTxCHBEhz6uscLeMb0638i7UGNyoDi19qKIRUgelrVthpHpDHA5SEUuSZOMOzTqM5FgguuW7adI0RavVKuAaYri4nMx85+pyucR8Psf+/j5u3bqFfr+fvx+PxxgMBs7jXYGiU8bXPuKJ2pHLPiV8F8BlFEknhjTQeXrL0Lb6gufRjnFx0T29cwXFJd4aThJ8xgin59Dx4w6b1WqF8XgcfL8Uh729Pbz//vs4ODjAvXv38PHHH+OTTz4xlX7XnCqjz9QtC+vK49odzNvpGns57mVwCznCUZsnLgcVOVGyTN95Hjt+IXXXKb92DWg30/n5OVqtFh48eIAsy3B+fh6960rrJ86LhsNhfhccveN8w3UkO3ek9Xo9vP3227nB//TpUzx+/Di/u5ycALPZDL1eDx988AHOz8/x29/+Nt+5Y+3wCnU4yecanya+SG0l2qVjnzmuki9Tfv7P32vzWAK1UePNllyhQIylG0kcOB6ak1qTORoesj2cP/CFNjKtJXepvzivID2IdvhoMpDrLy5eou0kk3xS48Pz+Ryz2Wxj3GJ3pvnAom/rRAL6HmPzXSVcFw583kndLESX5QHJg4MDvPfee3j+/DmWy2V+XLvcsV3WESyPFHeBLFvWSVdPuGwRDafFYoGTk5P85BPi0bQAwZLtMujL67V2yGq8kuevE8qWty269Y0H7cK3FvtwmuN9B2BDZll1aDY2B1roxXGKGZvlcoknT54Uyud3l8ojiYlWeFqCxWKhBmQt0Hihlj5mnt1UkCeycVlGp47wkx7SNMUf/dEf4Xd/93fxX//rf8X//b//FwDyO6gpb91gyS++uITao8lBS3/l5UuI8YnFgIs+66YpTYdx2ba8z7huadndPtlVZl4BME86oblO46wtCgmpy+evCcVT4ifzaCd21Wl3WnjHli9P+XHBW2+9hQcPHhSeLZdLjEajvN7T01M8f/48f88X6m4TfL5M3xzYNlg2sAbbwtEbjK1bIXqVnSxlwdfHPufVTQGXMxC4VEDov9/vb+xQbLfb6Pf7AJAb2JQXKB5NRnW5IKYvt6UEhNbJweVsiS2LygvNJ400yzDw1RuqVMf0dagBG1qH5hgIhTrmqaZIWE69UHoI5TdVHEVWn23DaHeBbItUajm+EmT7LVqxHIzA5d0vtKOfgmAuXF1AxrZ0mvBdoRp+r4KBKpU13q7Q+RmaJ5RnSCd5HfTumnOWLiCNCo4Tf877UHMIavXye8n29vZw584ddLtd1TDz/ZZtqIsur4q+XcZhFRy2gb8raKGl8eEROr+0cvhcsfAIMYzrNJivGrIsy51jWZbh7OwMw+EQ0+kUk8kEk8kEo9EI0+l0Q8eyjFVr3tPv+Xyey5wYR42mY9BRiJ1Op+DwBYoBJzrqkMtX64jjULBkLu8X3+ImbivIOSv7hut9Glhy1pIB8ruUY3VAKA/R5rykI01WyDK4DcLbLXdS8fs6NZ2K93loO2OcmUDRGczL4Z+yfS67JcQmrzquZedrlbQ+faBq+hAoU5Y2X0k3psUg5Hi0bALfnOdpfPi75KpF7zHylcqkuvjR4NQm1/HGMeNm+Rpkv8k8Fu4+HaMsLdVh+5atV9pgHB+qM8ShruHpsvcl8OsEYvkPlcdP4uD2JtVPdzDTb4vGNLrhfeLzuVhtrFN2WmVdt73MdSUu34bDIZrNJvr9/oa9f+/ePdy/fx9vvPEGbt26VShHswW3AS665QvOuE4bI7tCwGU/x5Tvkv8xdYfWH8v/Y8Diz2XKq6MtGi3G4hTKB1x8xnWsvA/fUAgpMyYf6dQyf6fTyWMjBFImTSYTtNvt/DSOsjJCQmg5ll7kK/e6+bEFdfoignfGxjLCsoRdRRl71eBV7Qt+xwYFWxuNBp49e4aTkxMAl4FWy4DWgJSUV7HPtgEx/SSVuZBxcR0HV2WMNIZeJ1Mk4Cs7l8ul9561MqA5ByRYfGAbba4K14GT1W/a0amhSpXrrju5W5rTxWq1yle48zq73S6azSZ++ctf4vz8PL+/wVJAOe/j9HFwcIA333wzT3d0dITBYICzszMsFosbyfs4fXMHrjQ2XWOiOam08mVaWVaoUs+VSAoESFyksSlx0HZVWLxSrpyX7bWcHMS7pJOOO0osmiHn5Xg8Rrvdxu/8zu/g9u3bePvtt/HFF1/gxz/+sXcnHy+b7xrwOc+vAnjbY4wJ+ucBhiS5vL/NJSdi+GMV41zb9RJSrqRdPi+1fNpOfJ4/BmSeXXWUlYXVaoVut4tvfOMbWC6X+Ku/+iucnp7i7OwMDx48wN7eHv72b/8Wk8kk3/XQbDYxm83UIwI14Dyz1WphOBzmK6PplBnNYUzA+Rof89VqhcFggL29PXS7XbRaLfR6vVxH4vOg1+vh7//9v4/PP/8cf/3Xf50vSJK7PULAMsa1ldbSsOcLMykYQ4Fv2qkpZY2sm88BzoN5PZLuicdqDpNQu1g+t/TvGAe/Bj5nt2afWzoq3RlIYw2s7648ODjA+fk5RqPRRl9zOcRp12VPuO4pp2cErp3fliwK0QEkzhw0vU3rL16Gq895fSHvfVDVXqKx5jLwKkHTYQiIR1LgIk1THB8fYzwebxyjWNb+lQvELdkk6VzSi8SHjoF3laeB1B/n83nhGgqNT4Xey+hrK5+vrv6M1Xvq8g/E+C19aa1yV6tVvvuefkugk5hoo4Emf8vOSa5X065ouos+BqxdUpzuZ7MZ5vM5+v1+YUEW6Qu065zfUe+zqziE0JKEkAVeddLBVYD0N9Hi11/84hfodrv4wQ9+sJHn7t27ePDgAf7pP/2n2NvbA7AOvvzJn/wJhsNhzq99OmQsuHgJ9SW1RS7iAzb5CC/DBzFzPZbfbxMkT3btgJQ6Evl5aL6S3emSD67fLtDw4XVptCRP3LLoTdudGgMkP63TmjTcXfaHphdZui6ll/l9aWQ5Vn0uvSpJkny3O0/Dr4YjkHRx69Yt7O/v4+nTp3j58uUGLVlt/TqCJQti5FkMRB1TXMaIjIVdEITbhNg+eVUmg3QAcSOEFEgSNLPZDOPxuKCMzGazXBHkjghpxGrG/bYmz65CzBwKcX5aebhCEWtAxoLPaR7SZsvxqKWzjh4tW3dIfl9ddfVlnfw6Buri7ZqDUL4LUaokuOiD8y6N3uV7AjqmT65cl3yKlyPnJHfcEb9cLBY5f+R3ytwEiDF4qzoeeRnS8RFjCGhluJTvMnLeVwf91sqWPJk/p8ADcLnAhN652s/fJ0mS3yO1v7+PVquFyWQShJuGp/bsquk3tl7NgaaVF/I8lB9VdVDwZ2UdTS7HYUwfavqCxcs53bmMorrgquyONE3R7/cxnU4xHA4xm80wnU5xdHSER48e4c6dO4X7LWWwMwQkL7HowXI+SIcoOVTH43HuCJB3Tkvdnu5r7HQ6BWeRjwZ9ePO6tPZqv1erFfr9Pt544w0Mh0MMBoPcWa3pvzw/D6iG6BQSDyqL7Bt+z66rbXJe+ZyOfL64dHppe1k4yLH12VBSVhBttVot7O/v5wuCaGGBqyxLD5N6U4we4NMZZb/4xlY+13Q4V/+G4C51Sk0vdMG25Cp33vlkYtV6JFhzUKNrWvR9fHyMyWSSH9Op+Q20crQ6fTQS2i5ZX4i9zfFx8QCJs0Y7IfXI/tSCXhqdh9A1z6fpvrG6iizLJzustoaCNn6SB7jw1MoqA2RT0jH8VEeZK0hC5DLVSXKM6pdpfEfNauXH2A5V7CuNR++i7SxxIl1kPp/j6OgI3W4XL168yI+GbrfbODg4wK1bt/Duu+9iOBzi7Oxso2/5iUahPq4qeAOXfc4XW3McYuZFWd6r8ZQy42/xkFBw0bFL9ylbR0i9Vp2aXJWypdFo4ODgIH8mFxKQf4ofky11myqg4WXpuHJxSBm9KhbK0qpcUK3RBsd9Mpng/Pw8P4WP0rTb7Y28/Dh5Xra1uFo+i5EVvnZq+Vxll+nPkLKtuXdV4AzGxiDjGrjX8PUFYnZcOSMGTtDv99HtdgGsGfnx8THa7TaGw2G+kurZs2d4+vQpsiwr3AfrolFyAGj4hCjOXzcIMUK1dBKq3OFKUHXFfxWglatyZalGK1XwrJv2Yg3YKvUQcEUo9I7TKhDjiHSBZjy7DHWexnJKaAoTKUh3795FkqyPeiXD2VUWx4FojPhZo9HAZDLB6ekpgGrB2KuiGR9wIw0or09suy2WMcd3rnFnhZY2xhHtAho7yYM4nbZaLbTb7Ryv8XiM+Xye3y/maienQ1Lq9/b28PDhQzx79gwA8kUBtFuO3x0DFHcJWc4PTt+hbb4O4HfiWM5ECVLXkHoQPZfPyoDPsLWcHho+li7A+Zw2F2S6Mk5OyuMzDOtypF0VPdEcunfvHgaDAQaDQd7Gjz76CMfHxxiNRvnOluVyieFwWFiAWKZOQN8FKOW4TCPlyvPnz9Hv9zGfzzEYDPL5qq3IprbeuXMn341K6UMWg1j839JJpeOC55nP57hz5w7++T//5zg+PsaLFy/w6aef4vnz54Udfrxezv+0hXmyHuLDhCP/B5A7RMhhzkHuuJXtD3X+Wf0px1vOS80JEbIAQOIlaWu5XKLb7eLNN9/MA/MvX77EeDw2cdHKljRMz6Utp7VbOpmso6x9/RbS99qYye9anb4+9jmJXLzRha+V1yqHp6Ud8qRP8KPV6+Knsiwud7Sd0ZJmF4sFTk9PcXx8nN+lCqBwQoBLTnEa5fhIurD0L60sPuaUjus/SXJ572aZ4Kcsn59OZd1jrY1bCF/W6NfFoy2w2mPpKxbtyrHZll9Hsx2t/pM05IMYnYbGlxb80u5v7fSB0Hb58CKghZhcV+P5tYVOZWybKjhbZYbwKB/PvQrQdhX3+32sViv87Gc/w5dffom9vb3cR3pycoLFYoGHDx/i7t27+Oijj/JyCNI0zX2twHqTSx0nvvnGgS8GlvcaJ0mi4iDbL09gqdPfUba8qqCdnhIKPrvTlU/i4Ernkut02s83v/nNvJzBYIDhcLiR7uTkRJVpUm5L3ck397hu55u3fNMXr3sb/mZLBsm6LJ3U1w+ynKOjI7x8+RLf+ta38uB4mqbodDobeUkHIiB+TbzBsqdew/YgamdsrOCLTXddTrbXsD2wHD/cEUyrpQE9gAoAZ2dnePToUeEZD07wZwQug9gytLfBeMo4JENBMwTL1iP7S3OwxdalOa1CIUah4M/L4Mnr0+7YsHAKNRp8ddZBd7EOmLLKrFYfD75wR2QdwjzEGRVreAKXzg8NNEeDz3nG8Ww2m2i1WoUVaAA2HLu+tvExo8AX7ZxKkiRf8e87fqXMu1Ao69DT0lj04uMhcmxCHZSW88aV1sKNlHp+lLBGl5pjjp6Tw0xbTWqBxoO4cSPpzaIxCfJo1OPjY3S7XWTZekHU3bt3cXZ2hsFg4MSN18MDN/wI+JA2uXDV6gtJT2lc/J6e0w5j3p/cseC6o03DM9ThVgYsvSu2zNj6XbLXmtdybsQ68eqQoXXKYQLZFqIdfi8swWAwQJZl+M1vfpPL0DRNMZlMSgUvfXqJJd9c9JIkSR6IlUFFPh8WiwU+++wznJycOB0lEm9rrPlzl8Pb1V66q3d/fx+r1Sq/EoXGQcpjVz+7dFopx4iH0hGVVIbLweUC2RdSzwrpE1+9vDzeL765zJ2kpOcsFou8j2mHtKWraOMv+0pLI9+5+DnP4/seq9dw3EiW+9pn1U1llJFnGp4uPURL7yuv1Wqh0+nk893SQbflzPPZeNT//Lm1wIDTjXwnFwD42iPniOVncOXnAVTZ1hD704Vj6PyQefhnLMTQQCy9uOjZopEqNk/IfKRntKib2kSnSWgBchl49tkiLvy0hUN1gyUX5JyT9O+iS/5p8SwtTwxY+kMdtKHVQeWXLYPySr9mkiSYTqf45JNP0Gg08k0sn3/+Ob7//e/jjTfeMNvC+VxdtGGNLS0EpjStViu3mWazWa73lumzKriHyLhtyC1ed4xMlulC7BUrjWxbqGzi+flR61Ku8oWHnG7lFT4hY+7TUy2d1NdX2wq++kDOO83epee+tml2AACcn5/nfqMkuby2gdcjTzWTOPL0IbvntXHS7BLZlpByXlWQbXUGY0OV9W0xrddwc8E1AeW9BePx2Lsr5uXLl/lKLyqDyueMle6aBTadDj5Hz1VDiPHtyy9Xu8Zeys3T8uCUxCVWWIc42eqAOpwVPC8ZRppxH1J/WRyq9FGdxqWrPJ8TghuffD5fh4D19SU5BTX+oBkqmlJh1UeOX7q3RdbrCtpoDiEqnx/hTriPRqONVYix/R2rjJeFUIUuxNDwlUH5t83bOc3wVekhoO38ox2rdPrDYrHwlmfRLV8Fyvsi1BDhOzWSJMGjR49y52u328Xbb7+NLMvUYCzNF77yVK7ap/eyHt4Gq41XDavVCr1eD51OB5PJJO/TRqOBfr+PxWKROxZ8sG26tJzGsU49V/o6x0GTtSHzeBuOs1AI4Zmak3y1WuH09HTjmN+TkxOcnJzgq6++QpIk2NvbQ7PZxGg0yudPSFtC9TZrEZ/vrqL5fI7JZLKhw1N/0B23f/M3f1PYHWOt/vbVLWWBdaeWBTwfHZnc6/Xw1VdfodPpFBaCyn6jfreCEZInUR9wHkZ8ot1uF/QjC08X0PhqcoMvAHKVG2ITyXKtdERrXG8h3Y/+Z7MZRqMRVqsVms1mfr+idkeVr7+t3a2anmaVoTm+rO+h5cjnnC589Czpmj8P0Zd8jq265QyNEZ2SAaz1hLOzsw0bntNEqD1VBh+rbOpTWgTAT7WQ+TXdm89VzoOtk5+0sZMOVUkP1r12mi6s5Q8dY95Orn/xOlxtcoHES9PfY2gxlK75mIUuhKtDZ9DmtyZzid83m82cF2qyXMqPqriV9dtoZfloo8xxxK7yQuvW8oTU7bPfr8u+sIDwoXugCWih3s9//vN8ZzTBf/gP/wFvvPGGWd5VnjrX7Xbz3XZpmuLOnTu53nh6eoqTkxNTRhC+muzV6KOqTNF4/7b9B7EQorvFlCPllEuv4cD9EtLn3Ol00Ol0cHp6WliAyK/WajQaG0flxoIlh0PmsGX/VOnTsjaqjyfx3yF659HRkSpjuO7jWkAPbPZPyKlloWXF2KlXCdc936N2xhLsGoN6DbsHFmHz+wpoQqZpWjBGF4sFzs7O8OMf/zh//uTJE2ddBGWczppCHZLe9ywUQuvnBpo0HrmB5RIsPjzrchTLNsUo1lZ/yHbVzYdcTp6YumINBMpjtTtWoPscbKEQaoiHOLy2ITMsh0do3pjjtF1OVDnnyCE5mUzQarWQJAlevnyZ3/UKhDkcXMDvouV5fPPNcs7G0BzPYxlKLnAZWD58JX7cScWPjw1VHkNpXKuXfifJ5e5ROq2BO4gsp41WDwU5KBhLAT7Kpx27T5/acdWxzjcNJ2rLaDTCyckJvvjii/weNtcRV3JMyQCjNlBwhB+hbOEY6tTWHMC+MbZkHuFL8+yNN97AW2+9lR/H/NOf/hTT6bRQjvx+FXqBTO+qv6yjtOw8iQU+V6wAE713BW54Oh8PKYNrmTxZlmEymeCrr74q7BrkK5fJQcGD/S7Z7ponNG95Ph9fd6WVBr2mr2XZekX8kydP8jtCeXCBdnG4nGnyCFLpRHU5wDlwB/1gMMDHH3+MZrOJZrOZ7zimOd5qtZBlGUajUV62LNO164e3QermWh/HgqRVi+8RL9XsAFlGiP5jzS85L6ndWXa5KC9NU4zHYzx+/Bj9fr9wvYxWBy+7qtySvEHqZ9rc12SHBRwnrY80XUx7b82BMmDZS1VAk2mr1QpnZ2eYTCa5c530acvBWqdNSeVpPEHDXY4/34Wo4UU6CXceWxDSLotuJZ1pNEn4uuahpEHXvNH6zTWvfKdjWc95Pdbck2OhtYm3QztKXjtJJlSHdEGobeYb/2azidu3b6PX6+HOnTt52168eIHT09P8OgIqV9NF5ZjJ9su0dcw1Kl+rk3/Ka0hCcdHsNwmSbkLGxDVP5PNQuyBE7vPn2wafXvvjH/8YX3zxBV6+fInBYIDxeJzrlkmSFHbFVQmy+HAhO5wCxaRv8Z2yMi/XWTWweAbl1fhOmXaE6gB12He8vNC0IeDSm3w6lcTFkgNZlmE6neLRo0fY29vLeRxweTqcVQ9fmB0ypyx+4uobK08obfj6vSyN+HBy5eNjR/oJz8tttCRJch8kh3a7jUajseFDtGzuMsdpu+g7lO608mLkbyyEjlUdIMstBGMtw4Lela1kmxDDEKsyz12poypc11hK5ZErO3zV6XK5xGAwwM9+9rNC3th+dTHuugxXn9D2GVsh5Vvf+W+5MjS0PWUcHZSvKp27xkJzXMQaGrFpLUNXw4PjXwZceS0j0uLNIWWXoW+fIUVpeLqQu8bK4uAyqPg7rX6tLb6VdVrZMo1WDjmc+Q6i09NTZFlWCMZa9Wr4asoxOR6s8d6m0qIB52+x9YUY3NvEoUq95AyiAEsZ3Uk6WKQR43IuuPhCKM9w4UN1zGaz/LqA0WhUuAfS4tmSf1LgKcvWRx232210u10sFos8GOty8FA5LtqmOvhx8xafcPUf4UvBhfv37+M73/kObt++jeVyiV/84hcYjUbR8l9+j4UYZ4Omc5RxVmhlEsToGhIfl37kkvdVeI1W11UAzSE6yYDmOi1GpB1nSZJgMBhgtVo573WmMiVoc6OKY4n3MT9WUVsYQm18/vz5/5+9P/ux7Ejuw/E4dfe6tXRVdTebTQ5JzVCakTzQSLJkSZZhfwXBgi1AejDsBwM2/OA/y370kwHDMAzJgrVgLBka2dZoRrOTHC5Dstfaq+6+nd9D/SI7blREZOQ5596q5vADNLruOXkyIzMjY8stPMcJUK3NPTa0V5dptslgMICPPvoItra2YHt7G8bj8dJOuU6nA/P5/Np45oE/jVZL5qO88thFll3Ag1aazcrporKvqA9l2bxcd6NOwLuPR6MR3Lt379o9VR4ZQOvg5RuaR2zCmcuklFiC9dsag5LsktJI9bPsAAsxO0KDJD/yPIfLy0vI8xy63S50Op2lhWfad6mQeI7rbPqMf0Of8b+1+1PxW1ycYU2E8u8oYuNYA42RaGNcsymk+lq+oCWTsP3o+LFOJeB0aHwgjWuentqIvO8kuUp3WVH7W2unMkjNp1arwfb2Nuzv78OXvvSlQPO7774Lk8kkLFZC+j16WqKlrE1H8/fWkfKOl24tb6lcj6wqqs9iz2K0pZZRJB+Epve0vL71rW/Bt771raVn6FOifWblnwLLPsc4CJaHk0IoRzSZYeXL30ltUwRcvxVFqq9f5HurbElWpup8KZ129HmeXx2//uzZM9jf34e9vb0lX4EuzpJkN4XH1rToTPFDi+bl5VFeXpHvPPlibEIbE7VaTVwg12g0QoyJLjjjNg+AfVqShZgfX8R3L+IHlkVVujWGpclYy+j0KpJVE8yR0ujrCLysM7hTFYoaCUUgTVxQoLL2OAwWitSnKO+mOPBcsElHjmn5cqFW5XEjHoMU6afPJBr5M3qfaJHyq3DsV4F1jptVoIgyjOUHYBuCZVAm3zJ19Rqz3DnF3YR0JWoKn+N4oQEGPA4VV5tqBi8NcqQaqVaAhctiyYmxAjz8G15+bExZwWAEvZfUC9pOlkzDuvE7fLCP6E401GVSn9By6DPcRTafz8NEjRaU43QjJHkrGcZee44a8OPxGL773e/CfD6H8XgMs9kM6vX6UhvQ/LEO6IhjGy0WC2i1WrC7uwu//uu/DqPRCP77f//vMJ1Ol1bMxmiTQB1+z30nVl703Xg8hn6/D1/4whfCBNNisYDLy8to/pQ2rTxNzpSVoRK/xdJqtFk8Q9NQ2SPVS7NtPAFGXs7LhCzLwp1K+BvghWNNA2WavZQSzJGC4lzWWcEtT33o3yi/tre3YbFYwHA4VGnmtqS0i5fTRu/Ppd/SdDjxi/nX63UYj8fw0UcfLe2MpYuZ8C7sfr9/LcAhyX+p7DK+qDU2eZCNPqdHsdbr9TCpTPWRNg7LIqW+o9EILi4uwu5JGpCl/Wrly+vukaW033gbaP4Ltb+0Ra6SDYQ0aXaUJsd4uVi2xx7S6l8FLBsKacL7gLndQ9PFgnIcUjt57D5t7EjjVZIjUt7UZon52/wbmr+mq+hzvvNT+057RutnyXlpMkSCZ3zH2kXSPzxv/nfMdqfpUdbRBf0Ir99WNbDcdrsNWZaF+8mfPXsGP/dzPwdf+9rXAABgf38fvv3tb8PR0ZErX82vwjIl/ywV3m9jNgh9TvvTI89i+Xm/KSIXPe0YO5GF5yflXwUwL9T7CKQdF5wh8NoI9FtSfeQYJF06nU6DLYKnRiHq9frSVU7D4XDJ/qUnxki6WyuXPk/lgZv0J2K6LAbNvuD2Cf7t1cV0PHA7jZ50OR6P4dNPPw3v6TsAgHv37sFkMoHj4+Nwag6NXyGP8LIpLLoxraSPYjpVy0t7VgWfePyGMvYT9td4PFblWZ4vL36X8qcbbLQ8+G+PzUr9F/q8irb10Fk0z1XJiELHFFsoEyC5KePps4BVGWAexzjlW22lpeZoWwLAEsqp7WHVx5OXl+c5bdrf/JlkjGjGiVV2WXgcRZ6OB7PwWYoQ9xi/XsVttUNMkVgBkhRh7VXE3vxSIOVbxNmxflNIgYGidSvbFjFDLvZdqqzkOyWlu9I02qgTgm2Ik2B0J0IsCOFBTG9L45fTSN9bZUhpJVkmBbZifJPCxym2ihRkpN9a9w7SPCQ6eVtSpxX7N+U4KSortX4rArwv9vDwMORDjxmWZCNOxvIAQJZd7Ubb2dmBN954A/r9/pIzpR2/zeuogdebfxdzrCivYh3QgWw0GtButwOd2l1gMdosnWbxWgyaExujQ7NFPHpC0vdaALZM3WgZ3oAYx036FZIMo3WhRxby7/g3PJ3G6xyWDWNB60du0+H9rHgMsybr6HfSb6n+NB/NluSBA9zdfn5+HtLghGCWZUE+5fmLo97pjn8J3raTaIz5J5pdrelOlD100QsNoPD2tOokvdNs+RTMZrOwKI3u9i6jg700eXSf5StI4zR2fygvW6KZ/u3RASm0e+wxL3gb0HFP74WmZWu+WFFdFrOdPHWg32jtI9ncHrvY0pG0vaRxbeWlfecBl5FaudJ3qek4X1jyi6bT6u8d0/T0EylPXNS4Tp2PtKPNOxgMoN/vw+npKTx8+BA6nQ7cuXMHsixbOi0g1lfcH7LScnqqhNffozR57HfLd6JlecejR3568oq9q7p9vTRg2bjrlfpPWfbi7nZMhwtdY/ZHlcDxiXoSF09g+bg7D+BqMpbqUz6ZJ/EQ5S3JHtXAv5F0/E1Cq0Oq7uS/YzqIf8dlOv+e+rx4vSCi3W6HidaNjQ3Y2tqCer0OJycn4QTM2BiVaInZSvy3Zv9K7VDErvfAa4Pz/MvKMAD7KHJ6RaSVl6WPtXrFeLdI3Tzt7bGpUlBWp3p5StwZextR1Am8LXhZ6F8nndrZ/xpui6IsAgyYSEIWg9TetqeOiPY+xUnVHNUYYvR6diVJgbSbRqpCvu3wOByWQVYWRQJoq4THmLOcTRqsAbB3fEu7EzVI73HlIDo0s9kMzs7O1HpIeaUagdJR8l5428X6HumIga+4xv9T+SrVwMPfPMDE5RjnEykfXjbqgn6/vzQpaa1e1Mri5XiMXwvo+OOxhNTI9wTSptMpDIdDaLfbsL29Db/7u78Lr7/+OpydncHz58/DsaydTgem06k6kSPVgQceqU7V6iLlifmirp5MJnD37l148OABtFotOD8/h9FoFCagMeim5SXlHRtTKbJIqxeCHsWK7/muoNTAmlSOZENoAViP/KXP8R13Kj3tfRvtRYv3yoCPQandedDJWsgjTQzneZ60MIR+p9UX/QDpXqKYrYtpKA9zfsagDwb+8D1dQEPvVQO4vogqFVhfDCji3d/0FAteR0p3ll2/71f6jgZX6VGE3N+I6UUr8K2NbQtcP47HYxiPx4Emq205j6bIPUvX0oBiTA57ApT0OeaHOsyiGfmR/sO86AIB5Eeua1cBrZ05fQik7/LyMtCJC5bwO7RXcdcinSRIsdMkm1LqPyk/agNY9g/3x+ld8PQ5pUH6Xhsr2jeaPrX0K32fWjcNVvk8RiB9K+l3vqNGku90Bzj6Omi7IZ9I9hWfhEXZcufOnZB+NBrB8fFxpXo2RR7h8Y/NZjNcK3N0dATf+c53wqQULuTrdDpLC4FwxzmtNy58xGd4OhItE8H5oao24PzGd8R7fH2vvbnq+EDMX6LvuaxeB31eTKfTa/oB5SwALNkcyIc3gdlsBh9++GHQBVyv8Ws5vOOM60/PN978P8cyLF3A/e3xeAzT6RRarRZkWQaDwQBms1mwURGWbePpq5h+svItA2pHWvTEyrJsBM3G8ehnaRFSLCZvnUrGn2tjzatvqD1V5Vikdpckr1cVEyhbB9fO2BSleVsretMoQv9N1Jn3JXVgtXSYtgi838UCmFa+ltNjoYgTY+XFHTbuAJbJ31N+kfdeReJJL/GMFZhNrbvmCJRBan2K5q8pkFSUoYkH4Ky+0X5LsByeFFh8YTmiqWVVPeakAA1NE2tnHuigdzxYciV1bElGJHdIteBPihwvA28QV3ruHRtcpmlBytQ6S/yg0UOPypT4RXIAPLLaosNTH+o0aEcT8jwxuMZ3c+/s7MDe3h4cHx9fO54qNfgs8b7XZpDaDp0SPE55b28PZrMZzOdzuLi4CHd9SUFIjbYi9ZHo5P2o8X1qeVIdyuQp8Sh/b+lszfak72KItX1RnZ2q92JjrWiAyBus4EErKY30rqh+L2Kvcx5MoUkL9mp9QOnL83xpB5VV55htxMvDPKW74qnNZ8FKE2sL7V2MN718aOXD/Rs6OUO/l9rB4p+YvyH1kVf3FvGTPN9YfCPlg+2Uetx+DB77SaNH8qnRTmk0GkuLx7C+dMcWPo/Vw2PnF9Gp9FurH1N9Mdq/mj7jcs1jY1r0WXnz+sbyj9nN1jfaO42XYnYSykt6RLjXXga4mnhCniu7oEaj0QvObxsbGzCdTqHX64X3/AQYfoS01Dc0P5oH1/FlbRQJEn9xXZoKiSekseTJu2h/W3Yo/p1iS3r4vSqg/eKhC397ZVtZuhDYDnhtBR3nFGX6vIxOKIKy7Vcl3Sn2Wkz3adD6RrK3sywL8ht5E0/8orwnyQ6NlrL+3irGocfWpmWvkwZpjGs+Pk3L08RsNu1dqv8nIcUe9/Cu1g+cJ2O0eeny2P2Iyo8pRkJWrYA+x+oRc2A98BrTsTxWjZvkWct5iT3T8tMctFSUaRceUKD0SWlj0OiI7UxMoT9mcFQNT3DCQlmDMGZMWLRJ7c5XLN8EYsHLVMNCU9T8mccw4yv18Z8WeKJp6apoL6QAKC/DAjeWKc34T7sHnAcSLVq8TpcUMKOraYvkWwYeXkcZVMQBjhm6mmOj5VFWl9NJVeneNfwb6zydTmEymYQdsXfu3IHBYACnp6fw6aefwsbGRtgFgHTi8Vqxe90sOuk/C1pgtF6vw2w2Cyt779+/H+5//vrXvw4XFxdweXnpvt8WIS26smSS1s+xOlHQlej4fZ7nS5NP3NYrclIH5s3r4wmsSfYP5sUDTfSO4nUElTi8bZISPKD8KjnQqwC3zaQ+t2w16R3d/ZZ68s1tgzf4yu96zPOryalarQatVgsAAC4vL0UdwPuc/o+7xJDPpfvHuW6md7RJ98JJsi7WBl7ZxuUI7pScz+dLu7ooLWXuE7RsMYk2/rcVmOJ2v3TPJspQiX4ur6X+ld5jn+KJBqhfcIFSGXumrJ8h3V+P9UAepcf57+3twSuvvAKTySTcFTccDsOu2lh/SzovRbfTfPBbb9AN6bPsKUv38DKxH6078TjPWXlrJ6Vwn8Jj38eea/0k0cnHdCwPCjxmvdVqQa1WW1qk5/HbcNdfll0d/yvFHtYF3tdIk+Sr5HkOl5eX4Q5NKoOwXnmehzsWW60WbGxsQLfbhfl8DoPBwOSd1HEi0eetc9FyvLZt1ZBoQT3L9ackgzz2J+Xdm7CHtKsv1gXJ5qHHFiNw/KP9IulFmmdZaDIpxea5CVRBWxXtJ8V+KPBdv99fem7Fa4oAF0XH4kCSj1lVO1C/WUsjlc9plJ5LaTw0ac+leDxe2yKlxzgIRyzW623bVY01bWxLpwhUkbflK3tQajI2FmApm5+FVMftpnETtK6jjbQAA8LLtBSWICnK8B6no8q2ijkOHucila6YsLfK1MrTghM8uCDlr/V9ivBelyOh5XGbDTPJmPDQKgUd6HOP4aDRkBooKQJPnaUx5gkA0W9TUTbY5aVDclytoI72TRU00ndVlyehrOEXy6+sAenpM75ARAsKS4EAq2ytvBT6rXSxAA91znCHwvb2Nkyn03AnLE7S4CTDeDyGw8NDODw8DPlaEzmSbuL08mCWBM0Ro/YMbUM8Ovri4gLOzs6g1+sFGq22jAVrrXogUo7e0sqlAVr6PibrtHSWbOWyt4jTy8vDcaPZmlXJ6yoChWWdaY/tlxrgk9KnyPMYPfQ9HeepSJVzKXlpdcrzFxP7nG76Dd6vRCfFYmOCH6sao5MiNm74dzgZgXWw+i9lvHhlBU1T1K/ylJGaP+8j7Z1GD/dxuR0p8YI2NmN6n5fL+1GS31pdYnZ4quyU8qd0cJ2Fd6tvbm4CwNWChMFgINKglUGfe2SDZYda4ymmg1O+09JhPlqf8npYSPVLLb7x6qQYTfR+QOlobYkG/oweWc9tFpqHpsvwBII8z8PErNfvWwcmk0m4ZgYAlk5MsMY7/k3fox9BdxEjitSV27y8fOk3pV1qZyt97Fnq2KoalA9Ty5H0wyrgae+Y7KuaHgqJJyxZ6uE3K58y47yKfrL0kvebIjJaqrfXr7PKsuxFra8sO0Tb+c+fpcqCmK3DIfGjNnasPKQ21/jdy8tFeNgaU7H3WA/N993Y2FhaiFfVKS2cRgmxPvfIFfrOQ3dMnnryLdI+K9kZuw7cBsPqtqOKNooJhlX0A19pLtGUgpsyxLmC8zqeUj6p9Ft3DKaUT50kmg+/+0rKK9aPXho89FUJ3nbrMKpXhSraPjb+MUiZZRmMRqNkGlcFbdxY/WndC6WVUQZVyCbpeysQTg00boRp8sJadUhXEuNvToPk6HvrYtWBjlGPvLFgBeO0ci3aYuOGBzBooA4nBzA/6Rgqi7c1VCXDuNOB/+Nxn1tbW/DKK6/Ab//2b8PJyQn84Ac/gKdPn8KjR48A4AU/jcdj+Iu/+As4PT0N+eB9udQBsOrB33snSSntVn+ORiM4PT2Fd999Fz799NNwJKPloNKxRceGFzQfuiPew1u8jvzeaq1deX9i2ix7sZrUcvo1npDqxesjpefOOT0K8zbqYq+Mo+DBEI+9rfFrakACQL53m5ZBaeK7ACVgGunORV4PWl4R2nl+MT1mLW65uLgI+dA86f9bW1tQr9fh/Px86d5PrW70vuaU3QCaftZsUSpjaD2K2hZe2cLzl9qb60u6KxLbKFauJFO43pTKL4KYjNVkm2QXSPfjWjYDfSddU3CTMs/iPQrk+W63C51OBzqdDrz22mtw7949ODk5gbOzs2s6MYVHuazg+oqmkb7jz7S68jKk/tbsEppWsoUxnfS3V85KdfLS5M07BsoPeO0E9utgMICLiwvz/mSuA5DncQcp/kPbQ2tLfJdlWZjs59+sIk5QBEdHR/D06dPwG+vSbDbDKTEAII57KjOpXmk2m+H+2TLAnfhYVspVIak7kDX7XNK/HOuSgzgW8XSL4XDoKlsah556FaXxNoP6uAA2vdpOZMuf4GVR3YvfePvsZUGV9g7Px/IJJWgymX6D/iZ/jr+rsP+RhlWNMy80nwDfSW0do9NjQ8ZiEfSZ1McW+F3lw+Gw0EJbDVrfx3RKVf3rsfc946LsuEyajOVGY2qnSrjtyqQMqmSWdbUTL0tyQixaygrUMmVr31VBlxeS42wN6FTaLEFsKYHY3wD+yZOYwkmB1Vda+/DyPWVaxrHldNPAgRWEKAJvu3oMSt5XZQxQqZ4SL1DnzUNbCqo2NHnwUJMlXsdBgqWMeUBRGrNlxk6R8ViGh3m7xmSzJUv4c6/zRWnhfF/1hI1l0Ep1iAWKY+M0xVBPeR5DrM0o3VI/YfAty67ui7t79y5kWQY7OzthwhWPo/rkk0/g+PgYBoMBzOdzaDQaALB8/I0UePXQGws0clmrjVn6DQ2G8bbg32He2jGW60KWXR2P12w2w465fr+/5LjGdCMPqJSth3dc8nJrtVo4ong+n8NkMjH7sIzeSZVB+K33aF6tDSwdwuV9zEnneXltZy63+C4lTf7F6hYrt8x7CbzeXptIkg08yIcBcclfom2Ek7Yx+UK/lSDZpVZ9itgwVdtc/Fh0rqfp+LZssZQ6aPabp63ob2+ZsTGmBZroO+vbKoNeHB6/AumgfI3P6DuefrFYwHA4hMPDQ7h7924I6En+Qpk+ttJg3pxmrSyv7cPLSMmHTkBb9iF9Fis3tQ95XxaRvfQZPd4edfR8PodOpxOunIjFE/D5YrGA8XgcvrPqKfk/2L5UD6/b5qJlSvqZjgG6qM86zYD7W7jzmMpR7+JFDXQxLe0va6IlxSZPRRk7grYL/k7VB/hNs9kMmw/o8bp88jvL5EWX6+TDm+B1Cx7/1NvPq7QRU+iwyvLqVC9NRb+TxmjRumL6WCxDKlt7xuWUV4dhXpJOl8oo44ulfGfZArG+kPIq0j9Svlp8jafD2ABfuG2VVQRaH6XqrJR+5flr/JlaX8/zFJlUeGdsFQr3timO24p1ttMqFJ4XnpUQKUbVTYHSxCesJKFZZR2q6h9NkWk7gsqUX9ZY8ARAvAaDVmeab1VtzJ2sWLt6gwHrADp/N00HwhtotmAFJ2IGUiywAnB9sUMKbRaos8cDLVZ6hDXuYnxJA4q0/KJ10H57gEEKz0SsN5jt/UYK/mnfaAFWzSDVaPGirINo8QvnD7qKstlswsOHD6HZbMLjx4/h6OgIAABarRY0m0345je/uXRMGwaT6AppbUxadUp17iXjWaoj1g1plMriuh+/xXuQpLpUAas9ut0u7OzswHQ6DUdGz+fzEGDSaM7zF7sj6vU6ZFkWAqtV0aw9o3YS/t1sNqHRaEC9XofxeAy9Xk/NpwywTO8OJQQevU1tvpjs5JDqT9Pz8VZ13dety6u0faWJBgmp8hAD31mWhZ3Z9P5zSWfi2MEFA/yueIs2TiM/1t7Ko4pgYFWQdrlI8sPi59Rgf5mALB972L+Sv+m1Vygv0jRa0J77GvTY1lXA217e4CnSO5vN4OTkBJ49ewYPHjyAN998M0zU4fepvGrxvId2lOlan0r5aunoyR3eCXPOC3Ss8/cSXZ7YR8wu8shHDzgvo86r1+tLO1svLy9hMpksnaRFy+c72BaLRdDrfFet1HaSbU115037ppx+3MnKMZ1OzT6h7YSTsDgZiN/hJHjKjlYK/Bbv26W7kqsE7xdNxlfRdzSfVJ2P47rb7UK73Q53uANcnZaj3Wds1e+m+XGdsOpK9VwVNktZ/ViFHqLvV22DxfKP2Zop9EljsUwssgr9w2koGiNZhZyhz4r4/EXaRmtT/oyffoDAO2EbjcaSXSLdL5sCy8eneUpjxmq/FLuVp1/lPfJl+No1GVvFwPEY0rfFifwc6wM1LqVjBqTfUqDqZYAWuI0Fg1OhOXf0HaeDp7X6wYKl3KQyNEM1xdjAYJWnfTm4kKaC2lrlGKON16UKhc+dd/yb1t3DN972iAUNaWBDom1dKFKmZjDTengCgVJfWN9U7ZjxIK92fLg1tqQgBz6XAkb8N+U9KeCUEgCKGf1a+hQD39NnEqx0VhvysRnjK0kHWHKTfyfBI1s9ciHG3+12G2q1WjimDo/0PDg4CHfHoezAoF1sctqqlza2PHTjc+kdThq0220Yj8fw6NEj6Pf7Yv5W3nx3GE3v1Xcx2a61D8oGtKtarVaYVI0FcjEQ2mw2Ic/zMJmsjc+isizWP9QhxAAZBno9ZVq2CO8LLAftCTxyu16vw2w2W1o4wPNaLBbQbDZhe3s70Dkej2E2my0d0xWD1+7hdYzJTY8NUlYf3QZ7PMbX9FkKNjY2wtHxlFck/qc8jd/EJmy4HJN0qZYef6/a9rICIkX73hMXSIHVz9o7zh8x28/jg2KaqoJwHvvG0oUeW9YLqV5S/ig7nz59Ct/5zndgsVjA3t4enJ2dBdlaVm9Q8GOPvfZDSjvkeS4G86h+t2jTQL+hCz1o/vR/6XmKfKf8adkwUj9zuxSfXV5eQqvVCnm2220YDAbiuEmx8zX/oaj8WYestMqWeEGyD3lb0b7C+8ERVCfht6vwcyS6vN9RWqTvvYjZ3EXB4xiYFx4d3Wg0IM/z0O44UU0XI0h5Sn/fVpQdG7Fvaf4x383KoyiqtFFTxlTV8JYd89dpfvR/Tc9o39NvMU3MH9dOYsTfsTHO86d6MzU+4oU1PmJyUbM1JB2u6Tat/BTZbl0PIZ0oIfVNit0ei0vF7GmrLO8YSLHvU20JTqNVH6stopOxKY2jweo8rYzP8dkHNy75O3rPkuQE0Hc3DY0erW7ad5YgL0ILfaY5Q9JOMskJ8ygBqQzJmeLOg0RrrE6cVs4fVtvRMmgQVnO0rfJigQmvkxwzhD0y1OPU8m88RhSnAfmBt9nL5HxwvtRo9/IRfhcLHlTZLni8COZrTcZyGcBB08aO26Rym/KvJAc1Ax+feeRezLC3+jJWhyoNd63OXqMxJgtiskfLN5a2rA2G37daLdjY2ICjo6MwGVur1WB/fx86nU6gI8uuJvpwNyHnGxqckmjk44z2pbfv8FtprM7nc6jVatBut2E0GsGjR4/U+7C1MSXl7w12aPWWyuNjHEGPsgO46ps8l4PJKMuRVtz5g5OxsV2olp+Q4pDQPqQ6BmUNTsamrG61aKZth7qs0WiEyVe8Myx2Bzces31wcADj8RjG4zFMJpOl+9284zBma3GHOZWfUhBzlOnzFJlk5ZdKnyevIvWn9cmy5d3tfLcqHTcUOIFvBWGk56iDvd/Q77xBCo4ydonXxl0VYjYNT2fZ6FpghefhsaMsee/1aby2g5aHVUYZPpHajOulRqMBT58+hePjY7hz5w7cuXMHLi4urh2JV5QGWibvWz5+NZlqxRI0f4nbmzHbUypD8h1oPtKulBQ5IiFWtpRWKovbM2gjTKfTsIuw1WotnZ4UGw8SLTR9yokysfzXDdru2m7TmM1HF6WhPYSQbKKY3WghxW+pClXoH8vOjtkKXEagjh+NRpDnOezu7kKe52EXs3TSVZW+5E2gKlqL6j0NMb9Yel5ULnrh0d9VQaqbJS942ljeHrkcs3ckH57rGM1Xtejygupfjy/i6fMy8Rb6O+Y70d8x+0GS617+lfwUzke8rpqdprVFEd738mBqWTz+AgDm4mitr2K2rlfXWrZm4WOKveAd/TIpptuCdbZbrKyytMSM8o2NDeh2u9eOExqNRmGFGqZ7WXnJ24ZlnIyYILBooG3rVSLSc00R83ccXqMKd/loeaQGB/kuQ8yD3olTJTzOr4VUA9JSHqmgzghX1rdpXFrOGT63+pbfdySNMSnQg84zf1cVL9F7NQGuxsL29jb83u/9HgyHQ/jDP/xDyPMcWq0WzOfzaxNeEmhgRTPQaPlYT23MS0GyqpwditvAb5Ks40em4btYe9HnyEftdhvm83n0ODMvresIskwmE3j8+HE4lg0nM3GScDqdRo/v5O+koIsEry6U+gexWCxgMpkE/pfuVbGAR5pivjFZRGnHfqdy1qOTkT50OPA4Ypwkx0Agl2lc3+GkZLfbBQAIRxtrAS5Oh/aO8x51liQ5S/OaTCaBftxZZbWhRpf0jDqouAMC69toNGA0GkX7vtvtwmuvvbY0GUtt1hRaNVg8VPWYluSEx+7L83zpCE8Pz6wKVZSFenw2m4XxlOd5kA10YtYTYPIgZr/H8sa++6z63N5AkCRr8G/Jbovl7wW3mTRZiP97gkBV0VYFJH1BQW3J6XQKk8kEJpNJ0EF0oXWZsmmbpU7uegJ5tDwtPb8HVsoD20M6cpzvske7ARdy8KN8LRms0WnRn+on83xpPrPZDC4uLqDRaATfA/s61Z7ndg/lmZSg6csGzXbxLAjT9FBKW3CdrZ08dltRlE7adpK/2+/3w27/TqcDBwcH4T36Bjhx+zl8vlnV5a1jzJeRl7cRUn3K+hZSX1C9rdFAxx7qSy0th9fn09JWjaIy0/KFPT6AZodIvpzVDqhz+GYtiV9WEddbFawd2fgsta2qwLXJ2Cobilf0syK8Yqi6nlXl5xFMHiFcBY9IwWsACIFnbsDjLgPEyzwZW2ZMSAFZDAhJ7yzlpylhSWkWbWtKU4y3pGCSBOrYxoLa0rcapIAi/h1TRkUQ+z61bl7Exq9X8aDTxvmEBw1W3U4ANs0aH1s8ht9ZY4AHa/gzPEqTluO9XyoGdL5xN1e73Ybd3V345V/+ZTg7O4M/+qM/gjzPQ+A4FsjhtMfKRnjSFjVIq/omhf8sGRobF9R4lvKx6OJBOwrcnYeTAfTOQg1lgjGx9Nq44d/MZjM4Pj4Ox+PixBQ18unkoEQ3gjso1tjUxiPPT/qbp8F7tKyjffhzjU9S+wCDYPR4VDqWLXCbiu+Y5zRJtjoeJc3bitJBkRpsxT7kAUSpj7PsajKMHvvsdQZTdD7Ai10mdLGRp+9arRbs7u6GiYdHjx6J9ea/rXb06r6qYTnc1njh3/D067LZqywHxwLKLW0sAbzgWdq3ZQMVnrpIPsVNBz1uAyQ9KMmzlLxS7COtHI9+kGxpLyzb1Cozlh/+7dHXmD/aLfS4T2lSMlZ+0XFgpfPa9978PD6n1vfUxsmybMm/SuknrsulMrU4gvStx9ag/s1wOLx2hKvkD8b6gOt6Ohlr2cBWH6xL/5QB7wPaDlp6TKfJCsvejfGsNtZvk35JoUWrL7dHado8z2E8HsPGxgZ0Oh2o1+vh2hWAq/jkdDpdsht5mbepvapGjOeqGncxm98jq6x0KbIvRS6nwKOnY/6tlk8KiuRp9bk3VsTz4N/FTnL09osnBrBqpNhmaBMgLB6WFscViT3id57xkuKDl5XX0ntvnlZbWzEWK/YixS5SUXpn7E05fqsShJ8l3KY2svjk4OAgHGGY51crzhGTyQR6vd61o10ajUYwzukq0s8hG34xoelxOCV4DHucEKJ3nPAdUdoKKOtooqomtjQgz2lKu8j40oLYKfmUGdNVyGm6IznP8xAkR0OK7sBaFTwBLw2egE3ZI7Ew306nA81mE05PT2E8HgcaG42GKx+Oer0e7mKazWbw67/+6/DFL34RXnnlFdja2oKdnR04Ozu7RgvuVPIaoNL4kgLMlizB71PvB/PIFM+7mwC2DV2lzsF3OvDvaRoMOG1vbwdnfzgcwnA4dN+bWRVijhYGXjHt+fk5/MVf/EXo+/F4DO12O+h4zotYVzrxIektHmBOdfqk+kjggVHLKZHy5iswPQFBlA00DR7nLO1M5d/SI3GxvOFwCOfn50HmNBoNVb5hHqPRaGknKu7I5rtTNHroc2tnBQ88036VZJU1rmi5yIfW6Rmcn7HPptNpkFnIqx59hn20u7sL7XYb3n///aU60mADHee8v7y4zQFmWqey9o6W3qPHqwSVyVimJK9jK6/pu5SAkNYGVL9rRxtX0d63AZYOQtsT/44Fg1PqWYXdnLrDjNJ3E3EWTi/9n/Mtykfqx/HrM2gdYoubOKgsseQBnqyV5zn0+31RD0u6ib6nclXqL2q/8TpoQTluz/FvaBmz2Qza7TY8fPgQxuMxDAaDcNIC2nzSEamcBl6nFN+I2/oeXxu/mc/nwX7gE4mYX2wXM/dZsM2azSZsbm7CYDCAfr+/VC4i1ue3GUXoTOlXj/zgvFLENrkNkORnCpBvhsPhUvuhf03bBBecttvtsCBB4vOqbbbbqqs9+jc1v5uop+SjvyxIpd3TxrE8PfFRlO24SBhPzgKAa76W5HdJ+rMMNPnGdXcRePtA8gVS42Y0r7KQfKsi/omVVstP0tc8LY01e8oq6mNzWAsByrZ70mRskcKKVN4rFD7HFcoyx00pm3q9DvV6Hba2tsJxeIvFAnq9XjCmccUZBT/+s4ywXDViwVOA1a3Q0ZwyjQ7+O5beUmA0kE1XC1rfaO81eNPGlAmVN5ozXbRftLHJ30sTDdo3Vj5WXVOMMy0/2q8AL4ILKRMVHljjJmYMpjin3nw95fI0aHTiJEiZxQM06ErvBzo4OIA333wTvvCFL0C73YazszO4uLhYoof3mUR3zPgpAx6ULxNwqNKZ9fR3UZsnRieXsTwIyOuLu7Hq9fpSALFov0ntmSLrtXrRna7z+RyOjo7CO3S8vBNqHrkX01UcRQMEtE+k/pHy9LYZfUf/p0ea0/JSbYrZbBaO2ZV0svQ9TizS3cz4XnK4pfbXaJV0i9SvKOOQXzxOodRetCwLOG5xJzTWyavb8jwP98zS012kdF6apG+kdk7Nr0oUkaMp+sXL+zRtahmevLhTb9lpkr7z2N5avrTMFHvQylfDbfeti8jwMrpcQ4qNWtTm8foPReDhS0ne829QV0l0UZ6l8pzXJbV9pHbBf7j4ly8K9YydInqDjk/620M7tf+ovqnVarCzsxOudqDXjNC0ReUd7R+trlp7SaDthv+qWBhC2xK/bTabSwv2Jdy0TuTw+gP0b9o3VZSRgirkipTPOvpD4hkKbzsh/yMfo01IF1LjEezUJ7fs66rhtWtvwzgo4/eXhcbPlj6oMgayKqTYILH09BvNtrV4SSrPSo+6utVqLekM6oNKu/3XId+0unB4fHFPeVpesfaTUFVbefwXyyeR6OH5e9vUsqm8faXlraWX9EaMx8vIjGuTsWU6PpbWi9ugOD5HOXj68I033oA333xzyZChq8oAAJ49ewYnJyfhm1ardW3nyMugNNcJrxHPneRUoDJF0OMc8zwPEwcSfQi+u0ty/Gl5PJ8Ux4UrDQyeFtlh9jLJKO7olTHO83x5h3OtVjOP7bsN0JQ75U3c+eXRZ5yHcLKA7qTudruwvb19rW3wWOHUPsCjatHJQzqbzSZ0Oh24f/8+TCYT+A//4T/A48ePYTKZLDmNWF8pSEIDvfR/qe4WOJ9hUMxzX+1thUemeJ4B2Medcn6UHBIMRA2HQy/5a8d4PIYsy6DVaoU7lBDIC1wO07rS/70GOG9T6tB5+U4K4NDAYiyvKvgbj28cDoehz3FiT6LXAuWXwWBQiGeyLAu7cVFHUhnCg8PS0ZNakN6Sqeigb25uQqvVgufPn8NgMFjS05rNgOXduXMHNjY24OTkBPI8X5KFvF85vYPBYGlX/3g8dgXWRqMRnJychHZAPYltY+2u5e3Cg+V43KNnQqEsUgMu0nuJzqr9Qk6ntguF/s37HnnKUy/8h/pMkuf4u8iK9rK+NOpcPFqd0g0Ape7pvG2gR4in7jj19HeVwHbnd4BSID0ob/k9opKfc9M+CB97klylafjEaAwefUvz0e6n5roCx65VHwncTpHucvXyFpddqKPwnvpGowGvvPJKyO/dd9+Fn/zkJ9fqRYOhPJgttYFHztH6oBxrNBpL/n2ViLUZvp9Op8GfqtVq5sI+TPey+h0Atp0CIPN2VRMVq5YtsdjOqsvjkMZHvV5fOn1vOBzCwcEB/M7v/E6wJ7/3ve/Bt771LRgOh8HXybIsnPRHTwDSbORV4yb0BMoNbXxKttxN46b16SrgqRM/8QUh6diUfsP8aLyexskAADY3N6Hb7YaTHzjtqHfoO9yQgEg9FTOV17QT4mheRXiniL3v0eOajeO1k1Pth6rHcor+0RZ8URqqutKSym9eR0tHe+89VqPoHoe/TKNbwTQt/W0TlpRBq6ItZXCntL/EQKtsTz6ApRUujUYj7Iilhic1zukRBjxfAHnb+E1BEtJSf1YhwKsS/mWg1Yn/0/qIB1Y99GkyyApSSLKGB6i0Y58846bqdtXkicZLUtCRgzruPA/rO+l5rJ0thb9uOa7pMdoOFk1FaMXFJdJY0ILpsTpQQwDv4AJ4sTIXj3yZz+chWIxpLV7iz3i7xL6jwSD8TkORcVIkoF+kzzx0S04JDVzR316j2QM8hh/1qIdmCxptZcclHf/I/9RpirWxxyb0yOJYcE8b75I+4+9XYWvQ/sjzHHZ3d2FjYyMc/44BnZRFU5RObnt5Fs/w4z653OI0p9JEf2vOX6PRCDtMqeyU7ETO061Wa2miDd9ru4F5fTAfOvZi42M+n8NgMAgnE9DJWAnWOEwdm5Z9VUTncpvCy/uSftP4pgpo7STZRNxG9eRB683/53nS72MBjBiK9Be1EejOnO3tbWg0GtDr9cLiQ6pLbptfnQLJ/q3aRkj1s3k5lv3A7QYtHykdr2tsjMb8mrLQyi8iz6zvNNnEbTHrCGJuo6Xyg9XWkk3oqaPU53h6GNpRXK547GOvn6bpVJ5PrF6abNVo0+qv2di4AAMXr1Edjem9fp015iSa1+2/euHlN/re6hvJh9H8SM0GW2U7aTytvU/Nm35Pg/m4OK/ZbMLOzg48ePAAfuZnfibQ0O/3wzUX9F5jvtB2FX4E0nAT/OmRBUW+R3j86CrqzeVfVe3p1cup8NixnA6rr1L5UmovDZLNgWNjY2MjbOyQdpV7bfRUmovAwxOx/LX3sbwlnvSMDQ9iMlXKm+po+kyzu2Jl8rI9YyM1/pA63qy6ee1IT3+V2tKkNfIqFMJtNIAQt402qf25IFw1zShgAa4CWnhfIgUXuHgk3vb2dviW7orlyPPbs+vKI5zLCIEi9GhOlxQwo3R6lDoNsuORhqhM0UnCd7Tviyj72LPU1bp5frU6C3cVDgaDpR1c1j1zq0YKn2C7p8JqU0//II3IA/yOBymowJX2qiAZAvy9tkMkNi6ok0+/oUGfer0OnU4HsiwLK9wxmEJPAOD0ecfFZDIJd2EBABwdHcFPfvIT+OpXvwrdbhd+7dd+De7duwff+MY3YDqdwmw2g2azCa1Wy5U/1pHSJckRjYes9uO7Pl52UJ6mC04sQ9syUml+9N18Pofz8/PwDI9C1WjSjMWi9SsCpHFzczPUB/mRAtvEw1s0vQXNIfBC6xMvYjqLB4TRVsJdxf/f//f/wd27d+H09BSOj4/hr//6r0Nb8jbU5Ad3knn7xRw+1OG4K9RaXcp3CpVFnl9NFNXrddjc3IRms7l0ygbqGF4/Om663S40Gg14+vQpAFzJZek+T4AXuyMl3YWLWzy72kejETx79gwODw8hyzK4uLhYOgWBB+SkXRJYhrbL06ufaX5ldW5KQI2fMrJOO8qyM3ggH228wWAA8/lctPk0O5PbPdquQKl8Kd+yQDsQd5SjHY73Nn7ta1+DBw8ewJ/+6Z/C6ekpbG5uQp7nt/qEhRi4f4PP6G+OdQeqcQzjYjk8jo/fjS7xjnWUr7W7qmygkZZj+YKxIJqVpggtmkzR9FmWXZ3OMZ1OYTweh2Avp5XbSrwOGqiNIdkumg9EF0Jwmqlem06ncHp6CltbW7CzsxPSoOyy4h6W/yXVm0KiS8vb007aDm8PUHdRnY2TsHmeL42D09NTGI/HSwvNNBuQ63LNZ5XovS2xPj5GPTEbS2ZISLU1PgtAuYxtMp/Pod1uQ5Zl0O/34fT0FP7wD/8QvvSlL8Hv/M7vwK//+q/D7//+7wPA1dj55je/CY8ePYK/+qu/CncaNxoN6HQ6MJ/PwwlcVchpjf7bAhyj2juKVLpXyXeWrllH+atEzO+L8WXMR0cdq7WVlner1Vo6rng6nQa5PxwO4fLyMqTlehx1akostOrxtyp+KDOeY7oc4IX+KOq7e+1QK39PTCcVtHw+P1A2TsFjK/R5Sh4c9VgC611KA76swktDLGifgpiR5BGOGm2p3/LvyyCWz2KxWDryFAMiGxsbMJ1O4fnz59cmY9GRob9vE4q0twTa95LikIRBLH8t2E+/j/EE9g8G2QGugg2j0UgN/HHh5W0HLa+y/ElXYlFgABcDdEWOspKgyYSYM+0JLqVAC1CmfssnE1YtR3i5/FlsDFjBaY3nY22lORNcPtH7DrniTmkfzBfp3dnZgd3dXQAAODs7g9PT07CTjpZBHcsy/aS1oWRo8/GFO3b53d8aXatyVD0oGpBD8N1GWmBTqyOmx3xwIh/7n95n6UHRMVjkO2lcacFS+k1szHqeW+mqlEMxntXGhkQX/sYAIx7FtLm5CTs7O1Cr1ZYmXmu1mnnUY5GApwQua3j+PPjM9QFFbCzH7Gj6jzr6SCNPg0D9jUcZ8jpI5cUgOV5U5uX5i2sP6E4mWh/aVlRW00lMWjcacOb/czuOl1UlUoNnMZ27CtroGJTKwneNRiMcgd1oNGA6ncJoNHIHJWIBqZgMoGk5bbG2sWxDOm6lsm+br1QUXLfS59rYqKK8VNp4QAjlEN4JStNKgUTOR1z+pOjCquyF1HFM6+ixPz02UuzddDoNbarZSp6xIcmTGM0SNL2o0YS8Qa+o0Gi1/BAtjdcPsNokVcdIespTFv97NptBv99f2nUYk8eaHcYXNXj8Sw+tXsTaMDVWwu2yKvw+65tUm8lCVW3qgdd20fp+MplAq9WCL37xi/DKK6/Aq6++CgAQrmXZ3t6Gv/mbv1HL9NgGnxXQTTmaX2HJWfyO/l8EVfC6Zstr3xXxVVNp9Xxb1ThN8dOt/Og3k8kEer3etSvt6EJHnheehOA9ftaS31qcoGrZw3nC6xd74hhWXpJulNLzMSnR4oFHZ6XaTkUh0aLZYVY5VchpKj+09ozujNU+XPXl5KsIKKwDMeNKwqqV8jouki8CDIIAXLXN7u5uWNl4fn4O3/jGN67tpKG7G15WY0YyLqydTvR/AN8Z9jwtBvZoOVQAW8IY6cNyms0mHBwchLzw+DMO6ZiWWDkSqGORGtiTxhy9h4++f+WVV2B3dxfeeeedpftpvGWl0IDPU4LDtB3LBldSDCueF/5PAx3rktVehxkgLouzLIPZbCbyrvUtD5DwXeK4wx/gaqzge3r8Sizoy43/PM/D8UcAV/dt/72/9/fg0aNH8PHHH8NHH30Eu7u7cHl5uRRYRj5Hmi3ZwX9juZKTL+3cwjR4tNrW1lbIbzgcwtnZmVouRxX8VNSGSDkKWJKhXEZ5wNPj8dLdbncpEDUcDkP7F3W81gGkTZuA176JOeke50Qrj0+UxwJX0nPLuMf0KUE0gKvx0m63YTQawWKxgG63C3fu3IH9/f0lvY07YiR4yos5HLSOeDQy52Vtx5YUYLHKTnXwcMIaEbOBsC03NzfD7lbpnltad6nvtSsWcIEEPYo4z68mY+nRkrRt+GQyti9OGuOEPJ24oau+tZ29lO9oG6zbf5L4nga8Vy3TeZCMB11wt3Wn0wm2Xr/fh9FoBD/+8Y9hOp0u6UutDISkOzlP8W+sOhS11Wg50rUEw+EQer3ekt90W/REKri9SceR9/tV1x1PQOET43jyAcpzyi98Mpbb+Zqd7Qlmeb/x2MuxMlMDmlmWLd3vifcd8/tYvcjzHAaDwZL85pMBnDZN9nO5Sr/V2tTDi5o9g383Go1wzx7SzMeuJVOoDULrbN0Zbd23Lf2WwOW85VvQbyS9LPHRxsYGTCYTePbsWTjth6aj1xlYoD5YEXutKhTx4bW4AH33ssr2m4AmH2NH+D948AD+6T/9p3Dv3j24d+9e+O7hw4fw4Ycfwn/7b//t2kaSddtjSNNNlAtwVd9Go7Fk904mk6VFMp740U2MT012pcQbU3V1WRTpZ0nWWrqRPpf+5nRYYwvgKnbc6/Wg2+1eO72Nxq8oMGbHeUuiL5WvqB5c9byJVL6mI1c9hq268ivXPO1Slte9NmYMZXbBWn6S1Hcaz8VOFkO4dsaWESpFO+WmAwurgtSW2oBchfDWDLuq2hedTSu/ra0tODg4gN3d3eCkzufz4IQAwLVjjdGBve1HXFqGQ8wJ5s6U9J3H+Y7lL72z6KJAQ4ru/sCjiel3Fk/HAuySUJOCXFZ5CDT6Njc3A707OzvwxS9+MeT59OlTODo6gvF4DM+fPw8BVnp0HQZ0Y+0TQ8xIoO+l8Wk5XF75zfP3GpiW4cCDATeBqLL7//cnl1Exg8j7njr2mAYnNTDYpvEvz5Mfacm/HQwGcHx8vDTxO5vN4NNPP4XDw0PxLmQe0LEMP21Cki/mkMYrDSoBXAWDF4tFOF7Ma2iV1X8pQTH+PFY2dda1sWjlockBfI78ibs8kIekYNUqUbUjnDq2yspYrjs1nqdleR04HswrYqfiN5hXnudweXkJFxcX8ODBg3DkOS7yoJOhKfwdC67ysSu1hdaWMd2ujQ/NscOVz6PRCPr9/rWJCutYQWxH1NV4LBwtk8OyHdC2ofd0S6BjdDweh/SUX3CSodPphHJx8gFpo8GFjY0N2N7ehslkAufn52IduAy26rMqeGwZntbLj1ZZlNekdqDPcYzhaTxZlkGz2QyT/BjIt46ArQqcZq1+1rcI3u98F3iWZXBycgLT6RSazSZ0u92loxJfdlg2jITUvtVkF50spJNltFwc76+//nrY+dHr9eD58+fhG5y4xcUilj1RBtzW97SbpAskn8HyU7TxLkGzbWI2ofReSufxG736QdN/tA6S3vHy33g8DpOOR0dHYfcQwPId8NzWkdouphukekj1t77lut/iV28b0HJ53RaLRThJRLNDYnXDMastuKJ08O9jfnysThQxe9Mqz9IlvDxPOfQ7bVzTNrX8g1Qdo+XjyUPj3di48+SNx/7v7OzAdDoNmw96vR4AXPm3+/v7YeHs5uYm/MEf/AF88MEH8Cd/8icAAMHGqNfr4aqRqn0rxG2KxcT0Z6xfPD40TV8WUp7SGLR4W9NXq+pvXlaRMRerSyrdlp3qpS/P82Cr4jHf0uasWB4xOj1+rQdWnbU0KeXEfKYUpNjHHrr4eIjpBel7L1L6I8ZrVl4e3aHpG6ufNZS6M1ZCqjMpwQoI3TRWIfS97yXm8Awqj5Cw8koB7uCwBtju7i787M/+bPg9GAxgPB5Do9GAZrMJAFcGDv0e87Xov0kUNdY155Mf2VCEXwDklSFl+h+Dh7j7A+DFZCwNOsYuYEfaPHzO6ZIUsOakYKCz2+0G3nr48CH8w3/4D8Oukx/96Efw/vvvw/vvvw+np6eBNnr3rXTfYRnEFKsWGIkFQVLKlN5ZSsQyfFIVahWwxo0UrMDV5jStlIdlTON3Uj9I+Y1Go7D7P89frAKnE6PopCFoPjzAgGX1er1wHyJOek6nU/joo4/g7OwM2u32Unlam3DwNDToyINKkqxDAyXP83BEKN472Ww21Z3ItI0xz1U7LzEarGe07prs8dAvjfM8z8PCAXRG+JGsUj6xOmhpNXjtAU33SfmUKdv7reVQaLzvcdowHf7NdzdJQVKtLjxvOhl7dnYGW1tb8NZbb0Gn0wk7QuiOH+/x41mWiRMFHJIz5ek3yuvSccBclkiyggNtDNwFTuVFnr+YcJJO26CLGGazWVhYFZts43yBY67RaMDOzg5cXl5G7yRC2pBeLg9xweHW1lZIj/oB09JFNI1GA+7cuQO9Xg+Oj4+v6Qm+oyhmQxRFqgyWdGYV0PxCTe5wOrDvcYdiq9WCzc1NaLfbgZcwTcr9UylIlakA+kSHVF+uW2u1Gjx//jzcFYv3KKM+pt++rJBsklSk6ml+N7JkG89mM2g2m/DFL34xLAZ9/PgxPHv2bOme33q9HhbV8b6OBbk0W0XSc5LtJ/322sRWm6WMe87bFi1SOfR//E5qI2kMWXnS/CR68X/UsfQ3L4f7ULH2GY1GcHFxEdKij418E5N1vN70f81etaDZ+zwPy2637An+Df+bl8n1LC7Ux7R896tmZ/D8OZ1aW2j1KgJtTFo0WWli/a6NjRiNWj09vkaRNuK8VKadLX8iJkPr9To0m01oNpswHo+h3++HBXIXFxeQZRl8+ctfDovsut0u/Nt/+2/h3Xffhb/8y7+EyWSypGsHg0GlsSSJdk8dVwnad9wXQHlG44QpPFRmx6LHPrDkf9F2jI0zr60uydcUX5b3Cf9esrG5b+eh0SpfOqFB819wwU2324XJZALD4TBaFrchLF3E0+V5vnRyiWVTem0Tbxqr3cr6cpLOkuxKzTe2fDqNlzS9K9HjpVsqx/O9Z4xIPBEbtymbA2N1rnwytgqsW3GsA1Kd1nV8sHfAVwUq2PjxcACwtNIf4GoyodVqLQnpLLuaPKE7QV6WgIFmrEt10IxELbBrlRdzgLQ00jv+HTqAVhnoIGnpOB/g3yl3IErlSqDtx9PVajXY3t6GwWAAp6en0Gw24eHDh/D48eMwGXsbUUYxaW3h4S+pf26bjKZBEM7HWoBJmvSMOZxUucccHUoHyrG9vb2wSnY8HsPx8XHYCUZ3m0vAPOr1OgyHwxDUy7IM/viP/xhqtRr0+331e4tWjW7uVHmcClwAgbIAv8ddwtqxHSjzAV7csVZUNsTq5wksaMEhCx5aadBIC8rRiT5+PGlVsORJTN9qbagZ85jvqmA5BBrP0jpSR4yCH6dF86EyhB4t66UNn8/nc5hMJuG42svLSzg8PIQPP/wQTk9Pw4QsrqanwUfLSfbYD5LzrTnrVhvTbyVZiTTTxShSHhTD4RAmk0k4urfdbrt5CJ12utuM1ovSzMcW2gevvfZa6N9PPvkE+v1+OH7Ys4MS39M7n3GHBU7CHB8fL52ewAMB4/F46ahvpJf3URUBIw2cJ25K998mm4PqKoDlO0EtOmP6U4Ilp2OBL4Bl2x3HJz/h4mUB93U0/Wl9H0tP0/BgHZbH73jWkGUZtNtt2NnZgf39/RBcRH6p1WrQbDaDDYiLkCWdw/WVRLdVR68PXVU+ND2VW1J5vM2r5smY76TRZdn2XH9J93vTMvjfsUAhX3zujYXwsiU7W7LxpffSlVCSfgdYXhwl1ZfTwNPyPLnM4t9b7aDJBa3NNf9vnbKxqrKK6hbeJ7yttH4oQncRPVg1vHoDFxMDQJDfZ2dn8O1vfxsePHgADx8+DPnt7e3B5uYmAFwtqPhH/+gfwaNHj+D99983ZfeqcJM2k1RPHjdJ0d9afvT/MuC+i+QP8nI1umg6KR5l5WdBk2mpbchlPLcJeVmp4zy1jpPJ5NqVRlKetVot6SQDT7touoD/XvW4XaX9k0oDR1H/0hMjKtK+1KbUEPNXvXWyZJSXZvT/LbiOKY4V7HE+f9rhYcp1lL+uMpH56DFxCDzqA+m6c+dOWFVGGZY7vLc5aOClzRoP1OHWFA4VDFowziozRXjQ7/guDJoXpp3P56pC1YSRx0CNOV3S97w85J9arQaNRgPa7Xa4S6vRaMDBwcHS6kWJRl7muqA5SSnfIiSDU4NX3t/0uLQc/hRDlQcTuNMgBY08yPMXgfatrS1ot9vQarXCjid8LwX5KS34N07kjkajMGF5dHR0rR5l24PSEAvQUeDkIQ8e8R1uEn14qgIN3lU93ooY6B79o8lmKV/NSaPv6a5kS64WgVdf8GdWEK9MmR7E+FczvqXd8rwfJP5GHaKtkqXBBKk/PbQBvDiWFyfi+v0+1Go1ePLkCfT7/bDDHCdj6ZUN3E6g9ZJ0Nm9LCVU6gzRQy+Uop43bKXS3GA/4WsjzF8dbxU4/0N51u134mZ/5GZhOp+HISBq0oPd/e9qA0r+5uRl2WvT7/aU24e3Edw9Q+1CSN/SZRl/MFykjb63gbpmAU5XAtqb3SWrpJLmHbUzvKIyd+GDlHUtnfUvHKg+eIeh9w1IwLrV/bhpcdlgL2Irkjf/H4hp0N7WVrtFoQKfTgbt378Lz58+Xdl+jzYNHZkuTsZz36ASZpmck2arZXLwMKT+aPuYPpfIRXZgj+XGWfa8h5V2MXksfSjacdsUH/R3zu5Cvqa5E/RADH+Nee46/l3x2ydbh36TyhybjuP/ubUs+dry2DP2Olhv7fhU+Sio8MtxTD+1vbptRpNiLMdtdel61PeIpm/LCdDoNvIg6vtfrwfvvvw/1eh1eeeWVwCd40gYAwL179+AXfuEXIMsy+P73vx8WRdE7oS0aPivQ5AHVofxdSl5emSr9tmzhmE8X86Ewr1g/c1qK2GMe/rFsAUtu8/8tORErW0srzRNw4MlAXpmM5cbolviQvtP0WlmZJ5WzTlj2IE/npc3S01bZqTGH1PaKjSltzEtlSf61hx9jNCTvjP0sK411I5UBV0VDlUAj2qpXr9eDjz/+OPx+/PjxUuAZ7+ha187hqpBi+NNvEB6n0Pqep0Ulw+niv2NODv6/sbEB+/v74biWxWIRJoS0uqUYSNo7DFogbZpQo/yCRwwjfcfHx3BwcAD/4l/8C9jf318K4LTb7aX7ijFvTIN3Xt4UqnT2JKViGYj8900YDRQ8wJAa4OXBO6yPZSx6+Ji2Dz9inB8rhncBIk/hAgEe3MN86c4WXu/Nzc2wa46OV29gOMWA1p5JmM/n4fSD6XS6NIZp2bQ+3EmjRnARnsPv1hUssQx3iSYrH8wDeRUDczeJWPnrsGc0HRgLqMTy4DvFqM7DZxLwuTTRUYTnFosFfPzxx7CxsQEffPABLBYLGAwGS3e148561G+z2WzpfnNKg0Q3ppWOO8Q248cuSvogxV7htgQ9RpDbIvQfrQNORmM+Fq+h085pxnc4wY0T4di2SMN0OoXT09Nr9cfyse0kJ97Sq/P5HC4vL2E2m8FgMIDhcAjj8Ri2trZgZ2cHfv7nfx7q9Tr8yZ/8CbRaLfi1X/s1ePLkCTx//hzq9Tp0Op2wipwH/bFNblpOICiP8MlAgOXFllwPV1UPzoOoF589ewaDwQD29vaWxgI/2UUK4FCeoPqeTtZzfSZNOEll8LI0xPSIxPurWtx0W6DJqlWD8yrKFbTd8Ijx119/HXZ2duBnf/Zn4fj4GI6OjsICVsnXor8BIJycgmMJedXapV/Ebud5oe1GZZ2nnbFdpAUxnH89fLmqfvX4E5IuS4H1vVV3HLex/tP8Oq39pXJizz2BTC2dRFMMnN8sOi1IdYjVi/LlZ1lmerGqNig7rtYBvEaC6tAnT57A//gf/wMeP34Ml5eXsFgsYDweB1m5tbUFo9EIarUaPHz4EH7/938f3n//ffjOd74DzWYTOp1O4K3hcLiyaxFuGvx6LyrH6KJGa+EgjwOU5cNYLIfLCPo7NSbC+Zv6Vxpd65I1XIfzuq/iVDKLFqksqZ3Q95XSrJvulxUaLxfR91XBYzdweOJiXh0ei9lxHrXGsgbLRyl8TPGqmP2zbvhIwS/+TgpO8W8kpUHz0QzjVGWSChSGnFYKaxV5lmXiDkWvAr0pVBE8sr5PrV9Keqn9pPpg3zQajWBk4X2YUp6aosffGq1acEoLCkvf4Cqq8XgMeZ6HyePXXnsNtre3lwQ0DchSmui7VaOIovAoRSvgR8vm6TSFcRscJi7bvEaF1l6Wo88nBGJl8bQ0+EDvLWw0GlCv14NDwnd38D7R2p3eI4j8izyr6Q6trlI67bcEnifu5rSOG+ZygQYhaf/yXYCSk6PVoSrdYAUgU+Ut/0aSa0UCqZ5ytd8x3k4txxv4sp57yuGOukeP8jFl9UXMztJ0naZXpXrQf71ebykAyxcJUVmi5WnxDD1aOUYXD5pKejzmREllWDpeygfgumOuyeQYL1B5orXRfD6HXq8XjhOWytZsEosH8bSY0WgE5+fnYbFOlmVhl0Wz2YRutwutVgsePHhw7U5ZnEyfTCbqIpey8OqJWB5UbqO+w2OneR9JAXrLQbag+UDYb/1+P9zZSR16KygoyTXuh9Fxz59LdMfq57HtYtD8ylSU/X5d5Ul9peVXpc9F86P/DwYD6Pf7MBqNIM9z2NragsvLSwB4cVwx1SEav+B4ksrTYgJe31jSY1rd6DfWb+077T0fN97vabqifFk1P0tjn7/nOsSyQWI0Sn0h7WrV8rbKlb619Ls2/mLgepN+W8Q+SylLylsbD+uQfTFUoZ8lWGNIe+ehwWMnxvi/CE9Z9HjBrwzLsgxGoxE8evQI9vb24O7du/D666/D9vY2AEBYHI0yvtvtQrfbhZOTk/Ce+uzr0qXr5FvN5qC2Fsr6FLlmvZPGdOpvSQZZOtbbpjyt5SN48i0z/r12lTbeqipboyXVNpOuB5BsoVi+qxofMbtJe1e1bOeItUuqzNDsNy+K+jop36X4jRoPlbEJrHS37s7Y22DolIGHgbmRZwlmDZ4BfRNtyetG70HU7lXDIA3NIwW3gWdSncgygjb2reS08/eW0LeMhLOzs6UJE+rwVbF7VDLeNEjB5Hq9vrRziAdze73eksK+c+cOfPOb34Tvfve7wXhut9tQq9VC0PC2gx9jCLBaRW4FF28r8jwXd4JpAf4yoEebYjs9ePAAtra2QnAfg+9bW1swnU6XjqHFI+swkI2LAaTde6PRKNwdSftFM0QleNPysVnGcORlTKdTaDabsLe3B7PZDC4uLsK7TqcD7XYber2eeFzvOuS/1K7U+MTnMXnBv9NAJ55XMca0YIcUGPTgJnUw7YMsy67tWrSODeT50P8tpw3/5Xm+tCudl2H1Ierp2Wy2JJuw/bvdLgDA0t3LtD4Y1FksFuHeUQAIx/DjjvRYfWm5WA7Sg5O+PB9+hCLWxWObIO0S+O5Cmt90Ol3qI28Am/ZVnl8tzMLjQAGu5G29Xg+yBe2c4XAY5PBwOAxt7R2PKfLx+Pg4HE999+5d+Mf/+B9Du92Gt99+O9x/i7y2v78Pm5ub8NFHH8FkMgn6xKp/VTIkZZzXajVotVowHo9hPB7Dr/zKr8Dbb78NX//61+HJkyewubkJWZbBYDAAAAh6DI/sjgVviwDzHY1GMBwOg57B+3wtWcFPmcEFT+jXaPTS8eRBSt1S+rZoIP22weIBHjzV3nnL0IC+j5Qn9jnyy9HREfR6PTg9PYXxeAynp6fQ7/fDyQaTySTkI9kPSAsu3Gi1WlCr1ZYWkGpB5ZTAplYPTltqfIHHBPi7qlAmCFj1OCgTxOS0aKcvaHZas9mEVqsVZA4uSMbTIvhONcveW0Vg1QvtegheDspgixb6PV2Mtg6+XAeq4mGP3FuXv7XKPiiaf61Wg06nE9rg448/hk8//RSOj4/hy1/+ckjXaDTC2JPKlv5eJW46RopjGGMjGNdDfsL2on3i6R8r5sr1qacNpPhQzFcvC86LqypL0++xb6rYtU37NrbAVwMu6kefDIGbt/A+Z0+Z64TXJ76NWJUMlk7stCDFG738HLMRtEXAkmwpOx9gxVhdk7FFOuRlZkALMaHpfSalWbVDsC4DM2Y4S8/50ZwvC7S+TTFaeYA/Jb+yQXMroCnxJL3DTDJcrL6XvvHQagU1ufGFvMQnw5D2i4uLYFzMZjOYTCZwcXEBx8fHIR1+X5QXvUZfGQOsKifSCiYVyW8V4MEKCTxwRL/F93TyAN+V0W0pxj0uSqHHYWfZ1WIDHqilExye/PnOCq0dNCdQ0z3SN7H2ou8lOcFpw8DidDqFra0tePPNN2E8HsPTp0/DhAi2E6XJ0wexNoy9j30rPYvRJbVhTKalwlNvz7caXbG8eR+tA5Z88AalpTzzPA/6AIPfPK/U8SqVQb+nsoynxTz4RAAGWvndO0V0kfU3Hb9SW2rvtIBDim6X0mjfSsCy7ty5Azs7OzAYDGA8Hocdpvx4eZyMzbIsTLZ5yomNE3qMKdor8/k8BO4ajQbs7OxAu92GdrsNnU4nTMwjnbhgh/ONVGduy2j9ptXDShN7Tndw7+7uwsOHD8OdapKDnWVZ2DVStd9Jy+L3mlsT2rGypON/vfLAKtPqVyldVaD08JMqViHTU+wppIHbe5pto70r0s/SM8uGRPrwqhR6mhBO5KMs59/RvCV9I9m7Wr2lOkvtorWNVT5/zunwPF8HLB8Xn3Hd5uVHKc8UHVzUBtV0KPWB0SaQFouUGc+xMevNNyb7pLQSb0t2h2Y/ac94nSy5YtG+Tmi0VpkvR5l6cx+Vl0fTpYyLFLusLKicwCsjnj59Cp1OBzqdztKJV61WC6bTKfR6PRgMBtfinNx2uA08tWpY+oLLJEuuevOPpaPQ5A61MyjdHhtZoydl7K6aP2I+f5WQ6qvpE4sfaBoA/aoh6TuPv8PTaTRLtr8Fy/5Iae+qeSI1P41vV8EznvYtI0e9/afxURW2gXtn7E+DkpDgMRYtYRozAFORYvzGHJB1gwZa8PdN0bJqUMGaOnY0Bz+lvawVHDF+9RpAeX591ZRHoWjlxwx0nofUTpzHKEajEXz3u9+FVqu1RCceH7MKWEbHbZKpqxiLVTmIHkOVHuWpOZDUAYodP80DYhJiuoEuTsAd2W+++Sa0222xvemKbVwB6KHFCqRRWnnwDr/FoA0dy54AGjcg+U513PFB88BjNjFA+fbbb8POzg4AALz22mvw7//9v4eTkxP4v//3/8Lf/u3fwv/+3//b7TSlplkFv3uCQTG5VhUtReonBbW85VVRvqccKRDHaYg59Qi+A4/KCdQlzWYTms0m9Pv9pZXunl2SsaCWVB9c6YtlSfThkZd4hO3Dhw8hz3N4/PgxACwvmpJoonTRHU9IJ99VjHTiXauTyeTaoiX+jeWoSSehcFnC7Sf+rWVba/ILAOCf/bN/Bv/m3/wb+D//5//Au+++C3/2Z38Gz549C3IId2rm+YtdREX1I8pzgBe7ifF4YgAIi3Nw96v0/e7uLvziL/4iPHr0CD7++GPY3d29toBHsyfo7mnJXovRXtYxp+129+5deOutt6DT6QAALC0oQD7sdrtw584dOD8/h16vF/Kg5cRo8Pg6eHIK3yXFda1UtsRblt6NBQksmj26fZXASWpsH76rrkq6YjqD8kq9Xg+7//kClNR8PeB9TdtB4huAF3ciX15eBv7G9/TEKEq79IyffIN2FT6Tdv946svbRbJXqH3I68fz8pQHIMt+D1ZhU0j2bCwdgB5Ip3e5I59KeUk7fKS8LRo4KH8BvFjoiLY25i3tTIr5OJINIulhyeaS3lu2ibUwP2ZfWz4Zl6WcD/mRsdI3twWrsq9XCcsmk9r5NrU3pQ9PoVksFtBut2FrawsePXoEz58/h1/+5V+Gg4MDAIBgn3/66afwv/7X/wIAgM3NTQAAGA6HYXHnbarnuiDxL9ehVrtIcYnYN0Xamcor7h9JOkGrV9V9XMX453LcSseRUrY3rWSPSPaQJj/o9Yaz2ezarvSYfuf15O2i6QHNFvBiFePf8r3XjbL8kwIt9pBad23MenxLD8QTYmMfpVYiRYB6UbZhiyDFucC/tfp5gkYWw8acJutbLTh12wyA20YPgG/iOyaAPeOB58edee0YxJgxo9VD4iWangZYOW24wjYmrDz9KY0B6rzicSZ4PBctw3IeaQAZ+2dnZwe63W5YoQ5wFezu9XowHA6XjkO27tLT6i05nJYjlyrUV6lcyxiPsSDjOiDxKaeJ0omTnTyNtywLnA+QB5Dv6FEq4/FYPV4Y4LphUcSgoHRI72mAk/4dg0Rzs9mELMtgOp1ea1+NF1qtFuzs7MCbb74Jr7zySjja8u7du2E3GB1HFh0p76x6SbB0LpeVEr1awEp7zvO26KT9QPuSpuUyP2ZvSPTdBmhykPM6f+bJU9Pr6BRq9/V5eEbjn9j40N612+1wtG6e57CzsyMGEyUgj8SO6aN8jMe4bm9vw2g0CkcfSnXiv2P6ROo7KT8Nnras1WrhLtb9/X3Y3d2FbrcbFsYAvDharkjwRJKH2hjkO2PRzsvzHIbDIVxeXsLp6Sl0u90QLGk0GsEuozs6rSMsNVqKwPJhtPdoL6IuwJNIcIKYO6SLxQKazSbs7++HNHg0vRYYoWVZ9nCs/poc9nwr8T3noSIyyKPXyvpzll5Cu4W+s/SKRDPXibGx7fGj6ZiQFiVobaL5RzFI+fETErjO0cqT/CqetqhNQNNr47WMz1D0+6LfVS3DYvRYNp43Hfc/kU+s63a0PvOMF812NMHwvwABAABJREFUoZOv+IyXU2QMpH4n5SPZqLwMCTE72CpTyt+Sz3Q8W/ZkER+jCtw2u3wV8MQWuI1ZVsbRvD3vkD/wqpBnz54t7YAFADg6OgpXbGB8C+l8Ga7BKgKPvNB0oDdfK+9Vw9KzFk0p9Fo+bJXjv6o29IzXFJo8/jPaqNK1JhY9Hr9bssX5MysO44HHL44hJq+8fngVSI0def0i6VlsnHnlieRbenw4b1tq8Q2OlR1TzImhTpKEdSulVCG6SsSElyeQQAfcTZ6PHsNNtbEXqzCuPYJfAgZcLy8vl4IOnv4tWibyD99Zg/nRXR5WsChWhvRNll3tcMT39XodWq0WDIfDpXvTLFlBj/vDtPV6HX7u534Out3uUtnHx8fwzjvvQKPRgFarFZ7jcWKr4AMrEEPTWaAOwLrH021xAvlEJQ3s8nbhK6CtHbE0XQq0dkEjEY+nOz4+Dg4Z3hcGAEvBGuxfXIGLu35TgspSgBLHAz/CW1rooOlr6Rnmt729Dc1mE05PT0O9tEApotPpwP379+Hf/bt/B51OB370ox/BbDaDN954A77//e+H/HGHjocez7sy4AaaFOiyQO/8lED70LOTrUhw3jIKtcC7VJ703bogGcq0HYoEEGl/cpuK7mThwXUAuPYOQW0yHpjX6kPzk3Dv3j3Y3d2F4XAI4/EYXn31VZjP5/Dee+8BwJU80fQkPaYf6dP6E2XPdDqFRqMBb7zxBpycnMD5+bmYHvMDgGuTw1K70LQ0Hc3L45Rr3wJAmLi5d+8efPnLX4aHDx9eoz3LMuh0OpDnedjJFvNZOA34Dd9tKQXK+THFmP758+cwHo/hhz/8Iezv78Nv/uZvXisP71XFb7WAgFRHLHcdPsJisYDRaBQWv52fn8PTp09hY2MDut0u9Pt9WCwWQffN53PY3t6GL33pS3B5eQm9Xg9++MMfwvHxcVjoE5NNlk0qfSPxGepJS99qPpukG4rAKreobZ8C7BPc4YfP+N1ulg7h41Zb5JkK5N9Op2Pe1y39TinD0n9UPmhyjcof/B9PFZDyoml5mfQZ8qY1wachRW/H+jilTNpOHjv2Jvwaz1iX9A/lBeRH1Gd08bKkx+jC0FQauMzCZ4vFYmlBJJZB03D/qOyY5LRxmul7TY5LkHjQa+NqdOG3sXtmafvygCrNx8vPN+k7V1F+VfR79HURIF9Zvk2V4H70YrGAb33rW8HeQdRqNWi322G3XrPZVE9v+6yC97N03QuPKZYZ51y+lOEHS2em2qEanTcJLvslXZXiq5bRJVwux2ycPM+XFjpI/qRFN4Wm2y1fqUiMQcOq7Hqub1fNc159qNm3iHXoyyyzF8pp/gW3S7S+o/ZOrD5LGiG1cYoMutQ8VtEhkoElGX0aXVqgQ4O3jTyD3nIGypS9DsT4C+DmJ3uKls95yXJk+TucdERgsE4TEprDXqSvKV9ZfGQFRCk8NEgKDBUpBm1wJwjdNaLlJQU7aXAjyzI4PDy8FkA+Pz8X+6pKHkztE48spEcP4nG7WvBQ40sraJsa3Fqn40MXJXgNIdouHlqrqI8UdFwsFnBxcbE0QYuTo/TuWFo/60hOWlaZoKNGrwTNMKf8hPdXYtCRBzUkPTedTqHdbsPOzg588skn8Nd//dfw4x//eIm22wh0qiW+5AFICZ72ttLztvfIjyJ9rCFVf6/K4QC46guchESnjvK1ZihTujhwFyIGN7U+jclJ5BNcYCGl0YBlzmYz2NzchG63u7RoaWNjA+7fvw+LxQK2t7dhMpmYC3WsY4SltFmWhftL6/V6WLxEj1PHIDDN19JF+H+Mv/m3Fui3ko3UaDTgzp07sLu7C1tbW7C/vw/37t1bOt7K0osxGul7TzAGbRXc3ZxlGXzyySdweHgYrk745JNP4Pj4GAaDQQjyjUYjuLi4uHYPoNTe3B6S0sZ8iFRIgZp6vQ6ffvopLBYLOD4+DnXhZWLAkvJVGTqos4086pVtWnvyMnh63p6xto0FINal+2jZuNvg/v37AADhOODRaHTtG48/zK9esIDtxWUtDyjhXc/S9zF6YuD+kKYnYvERiYf4KQsxe8Hy/auC1mYpcQWPr+D5ft0BOQtWP1jtv729DbVaDWaz2TUZT/Pgf8doifnCFFguXcwZs9dSxk6svzz9H9NLFi1a32hjKFXuW7GbIvLkJlE2rlX0G4+tJJVD+8vSibyvqNz2+lapMSv6Gxf/Yx4Yu0JQGwbtILTlP6u7YjliuhPfc/9K+kaCRx5qPGnJrlSfg34ryVmNJ4vwYYocKmMnpIxl6Zuy6T3xDG+6FN8yRVdrsqgoNPlVRod4fRFvPjFYutNK48nH46NL0MYfyh3t5DNebgo0m0vyg6KTsUWQapR7DcAq6JM6UqIhha5YWUXT00EutSkPLNykI2+hCud43fDSJ6XzGFm0TWq1WrivDOBF4IOnw/IsB8rqb/6OT/ZaQi52TJwXGo/i5CsKKrxXjH8TM7T5KmOk+yc/+YnLMbAUqxV08gRrYvCmrdfr0Ol0wkTBcDi8drcfp1dTClp/VKkEi4KWh5Py2rG+3MjQnPIqaPIoaakNF4sFnJychN3a1Enb2NgI/Yl9SRcoUL5H2S/RpkHSJZzGIu0kGXetVgs2NjbCxBOdUMYxSb9ZLBbQ7/eh1WrBvXv34Pvf/z78p//0n64d97YOXZZiBGdZFnZs0XuEpDYpyouWw1G2PayAR2pw5DYAJwkxSE8nZRGWY0XbE3mWBlL5HZc8H6t9cMKP7oqi41+igQZ4cNFNs9mEvb29pQnEWq0GX/jCFwAAYG9vD3q9Xth1KMF7xC0AhEVh3W4Xut1uuEN3c3Mz3HtlHRGltYfHKUR5EbN5LGBf5Xkejj/f39+Hvb09ePDgAQwGA2i32668vOVx+qwgzHw+DxNcWZbBO++8E3htNpvBO++8A/1+Hy4vL8OJH/1+H0ajUXRnD8ALR9PqH0qzNe5TwO3bWq0G7777LvzgBz9YuquV8+B0OoXLy8slnc/z47+tQFuWZdcmIzyLEaw+4+1l8bLFB7G25bJmHXIXeWUymYSd8LVaDS4vL+Hs7AwuLy/D4jFvfgAQrhpBO78IsB1wwQ1doEDLuk3g/buxsQHNZnPJTvD47ZI8lfgi5q+k0utJ65XR3I7l7zRbnn5bBWL5xOjQZBJNs7e3B+12GwaDAYzHYzg+PgYAuCbTYj6VVm+UCxr/AEBYQNZoNMKR75w/PDpYotWruzW7V2s7Lb2XlzW9JpUvyWPuG72MsasiqHJ8efKV4iyUv2JxHsvf99TF+tZKyzGbzZbuLO90OkuLv1BX4VULzWYTJpNJ+C3dPfpZRaxftPFWRbm8HE4T5wcu42Lyiqf1yIjb2O8xmxghxbY8eiEVnlMjNKTa29K3RXzPVDpXFZ+8aT1VJF5l2Qz4vggsPw0Xnlon1XnsJs0flxYWS99fOyvBagBP4FYzzAF8wR+p3Bh9UtqUPL2DVKtXzKC22s1qL/rOywApTLIK3EYFkwqJl1KEgBVsoJMKeFwbBouwjxuNRjDoFosFXF5eqrtOJGWTIowtJ1ByRItCalPqUEppNEfYumeTl4H/0+A1PqdHRNEV9qkKTQuUxOSl5PxJ+eD/eNwND4TRO+SyLIsegaO1s+WAat+uCnm+fEQrPTaaK2zNYOLOWdlAVeq3NB3uTuI04C4JrEetVjPHMOd53A2IeePYoEcxx4IiHl6Xgoo0z9lsFo4xpEcU0/5BuUh3Az99+jTsIODAyTV8L42nqvjRyls6Uokiy64mZXGVNK1rnudL31uyKs/1I3FoP2lyk/OWdPwc9gXPg76neXkCKdL32rdVgtbFmqzndNK7Nz0TAyiH+FF/iNh4jTm4WuBR4kka2Gm1WvDw4cOl+xO3trZgMpnAycnJUvtY7cHpx8lfjtlsBpeXlzAcDq/RZo0fSf7G5A7Xqdb4i4HKVbzvdjKZwPb2Njx48CDYBnSCpAzvxtrbeo78WKvVYDKZwHe+850wQTmZTAKtFn2pAb5YfbX3VoBqPp9Dq9UKu5C3t7fhyZMnYVJCQq1Wg16vBx988EFwXkejkSkTKY38eGjKN/Qdv4KiqP9ZRNZ5v/HotVS/xANubz58+BC2trZgsVjAxx9/DI8ePUoum6bDiXl6QoAEfsQ5z4/aOpJulfpJ42MqWyRbXntvQUpLA5aWbyPRxusl+SiSbZdCb+xdbEGcZL9I/chtC6w79Wv4XcBV2BKxPKT28o53lD9Id7PZhE6nEyZk8aQDPNGIl6XpQ8qDUvnSN/S0JOnqBN4nKTIktR+Qbk+cyrIhPL4bjgn6v5VeKld7ptGzLn84hjL2Ec1jFTa7x36Q5GERXvN8E7M3Pc+1dNPpNCw6on1CFw1R3ly1j3TbEBv3Zb6L8ROm0d7h35J/bOkFq2zJjy6j14r6P16fxCrHw7NVjFnpmYd+PK4YAEQfINVG1+xKD30Wnfy7qnVIql8SS7NKHbdO+WfVA20mCh6zoM8t30DyV+lcA4B8zWPSzljPANGcDUp4KjyGmAVLMFtlSfloTlQsDytwQWfNNSNfEiqSgX4blfttMViLwlLeUlrtPVfMtVoNtra2lnaO8dVy3ImRlLv0Hv8uamBb9Yh950lDHXtN0VoGgbc8zIPfOYnCkQbaaZ1TaLAUqlXHGB8hXbQOrVbrmvHIxz2l3VPubZQZGGyjyLIX90JIkwW0rWMGr9VnVcgrzj/aTlYMQHGZrvENT4er3uk7fmQuz0uTKbxsDh6AoDyKgR86zniAiU7G4u/j42Oo1WphdTEtu16vw+bmZuAFWo8qdQrnlVgghk+uIl9S2qgMp8EhTV9rdYvJeyktlw+UTrrjswpUaecVLZ/zmSX/OLzOCjfUuS6hz4rwp0Y/pxGdBpyAvXv3bli8lec5bG5uQq/Xg+l0CrVa7ZpskOinNMQcFtzpw3lNGjPcF5Dkc4y/Y5BsYuk3jkeciJ1Op7C5uQkHBwdhMVyVu+9jNo01bnCcTqfTcFQ7wNXiFJx0t/LB59aEllZ2DB5fLM9fLHTY2tqCe/fuwauvvgrD4RAuLi6WHFNaj1qtBsPhEPr9fpi0m8/nrl2Y6EdxuUv/8eNh8TtJ51kBjXXIN2ks8nGzKjqwb2q1Gty7dw/29/eh0+mEYJc21jXQ9HRXtGZbW/d90XI844B+F+NdqqeldJZPEIOnbG8bWjbaKngi5qdpz6x2kvKi43xdJ6FwSHazloaD1h19tWazee2oUgDZ/uLtzG03rXzJfsed6DG/NNWf9vrb3Efg9Fs+aMx3i+lOPlaK8mQsnWTbrNP2rQI3TS/lhbJylSO1bkXjIpR+PJGHLoincSZE0dMhPkd5xOJu1KfUbIFUv1KLQa0LRf3RlO+t+IO3/inxJw4tppGqswD08Yn8wSfXLFhtUFb+lvn+JuyrKhCju0iboOz25ivJBm7j0W+4DogeU5wCjbE8jgYKOsngXpVxoOWrGf3rYFRcPcXbRjtWj0I6asvrwKzSkf8c10EdBXpXRJ7nwVnD37Tvz8/PYTQahZX89ChfHmjmvz0GhwbtqM0qIDl/WZYtHc+KRyvN5/Nw5CQ9xtUbgJGAQXVPoF6ifVXBWloG7R8+pjc3N5f6P89zGA6HMJlMxAnnWHn0vTfg46lH1Xj11Vdhd3cXPv30UxgMBksBdGrsrIqeMhMsFPTIRJrO2hHBA9cUXMlrY9tjwPAdVVbQhAZ98Nl0OoXJZBLy4t/NZrOwk5dOjpydncF7770HFxcXUKvVwu4b/A4nT1Z1woMWYJbqj+NxZ2cHGo2GqHMxEMYnB2i9sT1ioDyd6lTwoBHd8ajZXakBiNsA1Ju1Wu3aEZBaehr4LBoE4sF7bfIEd4HjYgV6hLLH+aTHluORh8PhEBqNBoxGI2i1WvD2229f29HK7QmJfgTXOxR4jDjuvEVYx5VSfqILNdD+oeVxe4Y+s9rFA6RjPp/D6ekp/OhHP4IHDx7AF7/4RciybOkI6ouLi1BfrVxvUIKmw139KBNjgTiUq1mWQbfbDbICgwAecP7WAvux/FL1Hp9oG4/H0Gw24cGDB/Dee+/BaDSCer2u7r6muoPvjpPSUdsRxz8u4Dk7OwvHjNPTI6Q68rxXjVXYKdJYSqWJ1n0+n8OzZ88AAODtt98Ox3nH/Aee52KxCBNTw+EwHLeNefGyuVyV/Bxu81XRZym6z5JN1Han3+D4xZMNaD64c4ratR6atHgL/zbG45SWMm2r2UwaXfQ3XXSONpRFc1lo+praS5KfrX2Pei7Pc3j8+DEcHh7C1tYWZFkGr776KozHY7i8vAQACHYALQ/1YqvVCjIf7XvaNpK/o9mIUn/weBOvlwcxvvDwjcRfRX1s2o4pehLLq9fr0O12l8buYrGAyWSyZKfH/KMiNH+O6vzrKlG0b9BnTfWpPmu4Sf7WyvbKF+l0GW8//rT2dwqsNrI2I9Hvf9rb2RsPSn13m+GNha0Dkh9EZT+9O5yDn84E4JyMLSKUeGBFeh/LpwzDWE5KjIai5VrOjwTpnGrv0XkIbph77ke7DYwMkLYa/7bQzBGjS3OeEDSgyZ3P0WgEw+Ew5MO3tktBCo02LSDH25YHRTmqEuI04EIdUXw3m81gNpstHWdm1dFyKnlgWHqHiAl7rzFXJo3EMygr+IRrnueBf6g8KeJccxpWCctYxuc0ALG5uQm7u7vw5MmTQvdTlK1PVe1Bd4cirKAKvpPaS5L5Eq1FAnoe8PGIu/bouOY08Hd4tO/Tp09D0JwfZTmfz0NAJLVuZepH6eXPms0mtFotGI/HIXBD6ygtlqL8LO0M08rS6kSD7zHQfMtMalvBvqKBtDKQ9GmqbLAgBal52Vr98TeV39KEqZcO3CmIE0xZlsF4PIZWqwUnJyfQaDSWJjslSHWJ8Vye52Fscl6nddTKw50CeJQ3/k7pp7K8tVgsYDQaweHhIRwdHcHx8TG0Wq3QnnmehyPGLRqqgseRzrKrHfdUB3j9Jm7HeZzY1PQS6EQGAITj67vdbuBfzzUKfBxp6ZAnsb3wXjYuizU+teRrGXht51g9aVtYflAVMpjnvVgs4Pz8HDqdTnhPT/GJAY9tXSwW0G63YWtrC/I8DwtMNVud15M+08YA/UZr59S24XXkPpMl87R8aB0sv0vK32Ojp/j5mi9pPYvli98U2fmF/CXRVbU/YvVtLK32jvZpv9+HLMuWjmsHALi4uFBtN6x/o9EQZb7Ut14/U+JB+j4V3v6IlSPJP2tslbUXuHyh/EUXu1M7y4JHT1k0rgOa3IjJD29+ZVFENkt5UKwzNsDzyfP4LquqYdkGN4Uq6SnCG3ycW3zvkfuSb1cGMRm+Cr6kKBsjrEo3c7vHimuk6OJYGi9NHlRVdpF4llZ2FXoev6uCFyU+jMUeytLkTefpv9h3+C0unuNtj3EgrZzknbEWoZazmzIIblKplA3klRXQ3BlB0GexLfKrcmBuCi9jPbyCgu6UxeAbvtMc2iLBdEoP30knnV++aqDjSfk9yzJot9uQ5zn0ej31myLw7ELT6IzBEyDV8pScCF5mvV6H+/fvL620SV2wodUj1eCpWjHj3zgWms0mvPLKK0GZtVot6HQ68Pz5c/je974Ho9EIAK523OC9iZiXtJq+CFZRR8yXT8JpQUmaxsNbGJDm+UmTghqduBsrxVmhsgN3veB4wKNT6eTQbDYLR09i4HyxWMB//s//OSwqoHcjDgaDSvrUG7zKsuzagiasB/0e/2+1WjCdTuHo6OjaLmU68STtFJaOOy6L2DgvE7SwwO9rXAeo3MU+mkwm4aQAj560HFXqFGK9kK+53qLf8PFHeYreBe+ljY4jDtxh8+d//ucAcLWAix+TRusUK4vXg7bvbDaDx48fh/SxOiBt0+kU9vb24I033oAPPvgAnj17tjSu+EQE/R/tIGtXN6dfygd37Z+dncE3v/nNsOtlMpnA06dPzXpIZXkmL5De+/fvQ71eh8FgALPZbOmYZwt5noddhFZ6uoOL8ok0tpvNJmRZptJwW3yHWPmSzYWLdtrt9rVxi4t+tLZZRb2toJLnW88zHmSs4hh6nJgAAPj2t78Nu7u7MBqN4PT0FPb392E6nYa78TQZUK/XodPpwGg0gslkAl/96lfha1/7WuC9//Jf/gscHR0FOcqP9qITUJ4gaFUBQfybT9oUyV/StY1GA7a2tmA0GsFgMIBWqwWNRiPw7OnpKSwWizBOsR898obSjv+03ecxe5DKX3qtRKyuuJjcimNo4wFpXdURnrRsLgd4kIzre9qWHvsY04xGIxiNRnB+fr6UVroTF/saT4/pdrswGAzg/Px8qf0pX0q7d6V6a6B1qlrmU53E+5TyF7c3JFq8+pLbuZYcpeVOp1M4PT2FTqcD29vbS7YGnrpAeUALtN603vwc6bjpmHMVeNnpXweK2Hibm5tLm2X6/X7Ii8upVZ3cxVEFv6bapCmxKA8k+S+BnryUGmctCklnSeXGdK/mU38WxurLUo+bmNdotVqwWCyWTgBCPtne3g4nDHFEJ2OrYv51BeiqomGVnYiGtWYoaoEl7bdXIKwDkgBaNVY1eVIVJEVWr9fDrgfJIdKcOymvFCNDCjpr+XrKTmkzDx8j6MQQTcf5K9VJK9LH1IkvKsekoJIFTnOr1VqaqMdgAZ/80OiUgiDeCSqJttR0sfpSQ6Xb7QZDaHd3F/b29uDi4gKOj49DoAKPl4zxVBGsUmZJTnyZsmnAQ+KHouCTMViWRisGDvlCD0ojzZMe2ZnnORweHoY0uAMcwD6qsky9tPpodeSBTVwQRYNiGxsb0G63w8RPCk30f+ldDF45GMuTjsPUdqf1oGOzShtSowsn63CyP9Ux1vJFXkWZ49mFzuUSDWjzsWCB8invuyx7MZk5n8/h7OxsKY12VyzPP0Xe5HkeFsGg7YKQ+po+azQasL29HSZ5UK9r9MV41cvLmBYXSsxmMzg5OYGPPvoo3B+LdZK+pWUVaa8sy6DZbEKj0Vg65SMGyebi44vT5wFfeBfjQ6vOnvaYzWYwnU7DbkgvvVIdPTaTNGGC+WjHl5eVTzHaytgTVfqsKfyLehmPVcUrU/Bofr5jTJNP+Lvb7cK9e/eg2+2GxVaSrCjC26k60qOXvMEzXgdpPNHfOBmLJ2vg4rnd3V2o1+tBjnvGJaeV04H/vAu3eXl0sYz2DX9u+WSSrc51HNd1Vdp+FiRbN9XPkfgZ7Vd6z7u3b+miL40HNVlWROaU+TaWZ6wMiYc9NEnPvWVyWxdtR4AXbY8nk2nfaGWvO+6mwZIBHtlWxiepCql2F35DYfGbV75b+d12FGnDlwVYtzL1k+xF+g79A2lhdlmk0l12LKzyW69tLdlgMZ3rkfU8bYoNYdltUv4e2cBlTMyfLQJrbFcdg+H5SrR406bkuwqk6DdN12v11XQuXqUjQZyMTRXcmiCUnJMUI5d+Y5VdJo8yjnlRxul0OuKRsymrmWm70mMcbxN4/VY10G6jkcHrO51OQx9tbGzAvXv3lhwzfgwnjhNcGYSDO8UpKUqrB9pqaY9ypGXxncFW4EAL1Kyr/8sqtBRZhTsPJODxzf1+HwaDwdLR5lyOSIaBhapXpVt9JD1rNBrw4MGDMJnw8z//8/CLv/iLsLW1Bd/97ncB4Kr+H3744VJw9zbKPy+swDt9L4HeT0rBA0P02IwUA03Khz/XgnBYjrXCEOUB3oXcbDZhNpvBYDAI71cdLOe00HaSAjgAV7uA8e63er0ODx8+hK2tLXj11Vfh448/hh/96EdQq9Wureyk+eC7PH+xqr7I6trUfiwDrS3pDqeqnVWE1ofYzrhyGeWfxKsxp4jqNZx0uHPnDuzv78PR0VG4V1RCnr9YDIHtQXd7Z1kGo9HIxY+oAzAtBnJrtRrU6/Xw9+XlJeT51QkCAC+CvzygrTmaSKvliFKdgnR7+JQuDEEnBPsEF9VIE5SSnKI7lTx8xWUc6shutwvPnj2Dx48fh3d4woK1kKWI7PTSlvKNBOQtnODChWxSHniSBA0yewMLEjgvU5mGJwZ8/PHH0Gw24fT0NLwrG+RZJYrIU8ux52MpVpeUuvJxXlW+FnB3d0oAFH3UV199FRqNRpBXUlCc18UKlNLnVhtYdugq9BTPE08B2d/fh5/92Z+Fg4MDODg4gD//8z+Hd955B/7+3//7cPfuXXj+/DmcnZ2VKtvbLzgO6TcoPzY2NuDOnTsAAHB5eRk9jYvmiQtfsuxqJzAujrLGB8oLPCqW5ofpYuXG0tE+oXf25vmLe77osfAaqL9axtbh5WBMqN/vh9gA9wE1O9tbPh1j/LqEdchYyX8vm09Z0EVtiJ2dHeh2u+H3ZDIRTw6i/bGuNtQgjXvccd/v9916LWYjrxuroOO21G1d+CzXNzXOrNkWFKjvBoPB0jUbVCaMx+NwUtttQ6wdvDqD65tVnVpB5zIo+Ikpkm3I66ktPMH0MRq0fPgGOvTL6TOt7KptzM8itL5ZlX5N8W01v0GL0WNchf6O8YC6M9b60AomeARdDCmMmxKYod9IjZzS2R4H0frWChRa4IE2D32rRtUBhtuK1DpQIcJ5Dp1WLrzz/MUKTYlPJV6LBfdSgktcaPBgLuW9ImOF58shKUAP7WVoKasoq+RtTTbRyXq6K5YHycvQt+4xSnmIBjhqtRpsbW3BnTt34O7du7C1tQW7u7tw584dOD09XTpi8WWQKxqv0/rTMczr5AlO0nt0qzL8vHreSqPpSeRfDEDRycgid2rGYC1k4dBkBH1OV9Bj0GxjYwO2trag2+1Cp9MJMlw6BYMGj7mBX9T+4byUUuei7cx5OKaLUpHiMNLJNHp/NjeYrTamdWk0GtDpdEKfYpCc19Ma3wh6vF2q/aY5idye4PXT8ud9pDm3kgMhHZUo0cjBj4nl9Ei08LaNlaH1A8+TL16U5DD/luadonNw/OMkehVHmWG/4KJOlJk4sc3liQStXbX64ztuD9I0WlmDwQAODw9hPB4HeRnrS+u9ZquiDMD7lL02Km8LiT7vJEJsTPAx62mLWJ+sAlSOLhYLODs7g9FoBOPx2Fw0jHXCUwqQ34fDIZydncFisYBGowEHBwdwfn4O5+fnKj95kOqHx+SWJ0ATo4f3K81zOp1Cr9eD+/fvw/3792F/fx92d3eDjtnd3Q2LLbmNQP3IFHmUavdQfQrwYoGFx9eQ3sfGIS+3CH8XqWOWZbC1tQVZlgXdhAugeJ7eWJdGO5/8lfLE9HiNBx2D2Iba7v6icsCSdWVki2VfxfKP+TpF7ACtTK4r8XhBnHzBu+6tUy0022FdiLUL2rI0HT/+PKUOKfEWSsMq4NUVXhun6nJpes7LL0Ps4jbB02apbSrxP5XRuAADF4/SxdUIa+PETSHV7q2iHE3Pe+JHGlLtMymNV3fT55oPLfnDmk9sle+B5j9IbVJVvCVGSxGZ5fWXpOdFbMEq7Bfv9xZ/SnyFC1mlqyGT74zVCKqSCVcNrZGL3NG56jryDuXHQ2GaVa1Y8eCmjYrbbNigU5tlL3bJ4PE39OgLvCMoz3MYDocwGAzW3q/Wqh7+TrqTrgi89aPjrOiduZbDYQXONOG+irFP+QXbJsuuVrbi0dYSndzxp448v3MwNYBVBaQxKt27iHjllVfgn/yTfxKU1r179+DNN9+EDz/8EJ48eQJ5frVrHFfeS3LxNiAml6RFObH8aJ70GF98t7GxseQcUJ6SjBwtb/5MC4JbddRkM+7oRbmIxx96V3CnAgP0khFk/aZ1pc/p3bd4DGez2YTNzU3Y29uDV199FY6Pj+H8/BxardYSr/Od67SNLJkjBZjod3Q3Jj0yOjXALAWUtXJ5m/F7VFL7kubLdypq9aBHRtN2wCNxcTcq5uEZA7VaDXZ2duD111+HZrMJzWYTjo+Pl9LQky6s+mDaMsFzbFMsE/mY06I5xBpoX9Ggr9Q2ki7x1gEn5HBHJp1YkGjh/aTxUUqAJs+vjlluNpvQ6XRCntokMa8H18nWuMHyACAEdFN3qUi0ZFkWgj/379+HnZ0daDabMBwO4fvf/z7M5/NgI0jtQm0Lqx+loAMNRPH7sfEbekcrwNVJF6enp3B0dBTuxrTuSdcg8QrdBY5jAOUylQMUvN8sGafRURY88CfZlSlBJ0tmFsmXjrdGowHT6RS++93vhudaUJLSMp/PodfrBT548uQJNJtNePvtt6Hb7cKv/MqvwP7+Pnz9618PuxJ5PrFAjVdGSPWryl+k/MP7lZ8SMZ1O4fDwEJ4/fw4HBwfw8OFD+PKXvwz1eh22tragXq/Dl7/8ZXj+/Dl85zvfgTzPw5FmUr9qdaBjwuIFKT/6zeXlJdRqNeh0OsHGoX6sRIt1byYvU+oHHNP0/tiq7EGqL3Fy7Y033oB2uw2DwQAuLi7gJz/5SSgXj6qlC6MleyzGh1n2YscttcnoP5onXZBYq9WWdpFr49uyaSQbqkiQMxUpPgamp+NFkos0fRk6JOBJHWjLb21thfvdBoNB8DH5GEi1uaoGXZxNQeUQLjoAuNrliydqFZGF666r5R9Vkec6EPM5V43bHCP1YpX0a+2DsnhzczP4kzjWut0u5HkOx8fH0Ov1KqNF8/9uApYNQf2hMrDy4DFLLYbp5Y0y7SiVLbWBNz7moUWK+fA8UcZ/VlGVzF83aCwMbYRerwf9fj/Y+xRLv6yBRxELQmjptGfecmh5KUjJRzMMq6CD0sOdEimIY/2Nv4saVJ8l3ETdLT6W+pILz9lsthS4oIa0duRqqgMVc3wxn9iY5E66Nt494GMqVrZHXnjkjnes43MtmCEFXyz5UESBSDyDAVgaANXu1aXf0nfaUbaSYZNCt6cdJODRkdj+dBcGHg+D7zGAjrQ1m02o1+sib95WUDpp0ArrzxU3h2aYYd5aWTFarDxoOSkyhweotHxQh+F9nN6x5QHNq9PpAMD1wIUUXKO/PQFQDLLh/ZOLxWLpSFYpP6+88dYR4MUqTTziTlugkBLApvXV2ozyrSfPGLh+KRqM4d9wenk+NB0u8BiNRsEhl3bYemjy8hF3ODc3N2GxWFy7twzpk3SyJ3+NXi5/suz6TpwUntHuM+ew6JZkBq+rpce0cpC+lDu2eftJ+Uu2EZ7k0Ov1lnZeFQHNFxex7OzsBF3I6aHf5HkeFqXE+F/iDW2HOS2H2rG0naxJGg+sYCZ/Jx3TTO0ES1dabVAFrdIzj5+s0ch5MXbNRyqwTNzVbR03KwH1Ua/Xg0ePHsEHH3wA4/EY+v3+0gIS7yIPCxoNqXZQFeXSfuFyAXfZ4LhAuzfPc9jb2wtjFGC53WPl0zFt1Usa83QxEx3XaJd7eEfTtwAvdljH2ht5INXX9YLb1nRM0YU2njvXrTKk/On/0nNa1nw+D7YH1clUR3PEdG3qws+ykPSMRzZKkGi2xntqHXn/jEYjOD09DddLtdttqNVqoi1m0btKfxRPFtrZ2QlljkajcHQq6nt6jRHAcrxpFfSl2Obe7zSeLyq/V9kvtxE/bfUtAm7LUtAFm2ivcH+RvuP5eqH5OVXEQ4qCy3GuQyl9HB7bVsuDjnl+XDFPx+NOZdspZkNxe5teJyh9x2MOVdGn/b5pxGxuL6QYkLesqmhIgZe3JYjb21KYOVbIKoSHl/HKdkbMMZQGXGonawPT80wKEBbFbRvMtwFl+jPG93Q1eIqjUQSUHm0FTeo4TXWOvOXwoC/d5eABDxDGaLPojd1zVAVieeF7XJVOAyN8t6OUJw2wpBz/6q1jrF2t9zzIhKvQHz9+DK+88spS2na7Ddvb2yHojMdIYT6Il0GOcYOdB6CkoFaWLd8fFRvTPHhDn8eQ0u/W2PYEiHGBwWQyiZZZBBhM3NnZgTzPwwp3PFZYMuzp/xzSeMvzPKyov7i4gOl0Cs1mc+n+RM+kT6zsGPBYtU6nA+Px2BU4Khv0xDGMzmnq3Z4SJHmlBVE10LFEj3JFnRL7djabwXA4hNPTU2i1WtButwOP0iA1loHPueOa2g5ZloW7iO/evQuz2Uy8lwiDhJ6gH9UB/DnSyetPZRD9lu+2pnXEdNgu2Oax3VKcnlRoecTaZjabLR1pa+kOrQ15GmkM93o92NjYgNPTU8jzPKyw1+ribQc8UeXg4ADa7bbLRhqPx0tlaTYEBx3XAMvHc9H8cJEh5Q+ckMPfZSFNbEiBMU0vemzXInpTA6ePt48WvPGWren6KoD91263wwKVlDLwNJfj42N4+vQp7O3twYMHD6DX68FgMAj1bzabheyAMoH42JinaWlZMTkh9TfqHTw9A3e8InBH94MHD8KuzMViAa1WCyaTydKuPI0+iTaN36m8jvG6NNY1SAFC3t4aqB4pysce+Ulpwt30FBsbG1Cv14Nt6o3PaPRQuqgtItn5iNlsBpeXl0uyE9uFT5xb9HjoXIXPxK8d4e1gjR9OWwo4L8fKo2VSe+Hi4gJ6vR40Gg2o1+vBf3jy5Mm1K6RuCrPZDJrNJjx48CDQcnh4uGQvSj4A2mhlF0jdJMrySRHchj6/TXQUwW2lPWYT9vv94M83Gg3Y2toK7/gmCen0rVT+9OpMC6tqa5ovj0Fp7RiLAVv6wLI/PDZv0Ta0YkAIesJbzE5KXfxbhNYUPfc51oOYzY5YmoyNBTVSM+d5ewVDFcxi5SHRkSq0UgQKAlfEAoAaWJfyowGv1CCBty3XoSBvWhHHnOjYM09bpvRH1Y4SD2Dj91RQSwJZ4qdU/o4ZDzxIyQMGGmLH6VlyykqL6S1lrrWdRj+vo7d/PbRiWrobVkuHz7mzRVfgx/pX4pWiBmUsDU6wjkYjmE6ncHFxAZeXl9Dv92E8HsNgMIB+v7/0Hb2n47bd10HBg174zDMx7uVLnp6W5+GVWBkIKwjO62jRz8sp8s6TJ7ZzvV6H/f39cBTy6ekpPHr0aCk4JtHt0Qk4pgCuAh+ffvppmAgdj8fuY3tjclOjjcturBNfqUm/T+U3ThsN4OJzegdVkX6z+MjSWZqcRJoorRjExCMDAV5MMkljZDabQb/fD1cKjEYjcwKf0ovyVxrnHhuhVqvB66+/DpPJBA4PDwHgBa/h5DcHtxfpPXUxIL3S85i8oXWWngNcBTOePXsGo9EougtO09PS3Twx519qa5z00XY6a3XQfnPwAMV4PA7ta429WNBYwmKxgOfPn4drLWggSLIdU3bEUFrxuFLsN7x3F/vGyg/tFvpb0y00P4+9SfOU9BP+xnxxNzG+l4IjMX/Xqqc0DlLsdyvvWLlVgfcFvY9YS6sB747d2NiAZrMJn376KRwfH8N0OhXvn005ljbm13hojAWosG0leWDpnhjeeeedMGa3trbg+fPncHx8DPv7+0vjCoN93kUTlAZJ9he1yXD804CipjeRZ7AvpftSaVrNbizC0x7ZjPqT7k7e3t4W9YElS60yaB5S3ayJMG4D4fix7G8Nln+s8XUVsOxdiW6a3moXzzfUTvDYLtIzqucAIExw8jvPqzweMlWOZ1kWjj7vdDqwvb0NnU4H9vf3A62/8Au/ANvb20tlPHnyBL797W9XrjdSUJWO9eRdFQ1e2VumTVPHdNVYNU+sg9/K1kGSM5PJJNgw8/l8aZEDP4klxc+TyrX8Dc1esewe653ly1p2jiWjNZ3CfY/YOKWyldsaUhnaCSJWPTxppZh6LA2+l2SVFBPiNjfN37JNPYjZMbF8UnVSLD+uZyV+523g9fe98MZEvGm0/LQ8JpPJtUWArosfeeOkChn6vKwwruL7KhR5EdTr9XDHIQ5ezWjlTqk2iSKtSFllHcqgakW8LmPS67B4nPOYQ6CVbwWfrO9SeEFzrLV0qYgZv1IQiypayfn3KnaNnhRFh3LQa4jE6pvqqNL32pHDCH7HG00n3S0j1YX+TR1bi8+LBFLwWFW8x2Y6nYbJ2PPz83BXIy1LumesShSVLd7vNANP+w0g85fH4E5BSlBMGr9Zll3Tbdo3q+o/pIHy6/7+PnQ6nXB/66effhrGkXQUK4C+2pPKAToOR6MRPHr0KOwgopNmVv9ypwPL4PAYzfSuM80ATzGWLUeGyk+PE+RxvpCHuKyxFi9o5fAJYpyYx6MfuSzktuF8Pod+v7+0k8VzsgR1wiRHQepj7ozVajV47bXXYDgcwne/+92lnTSaHJbK4e0pwaKVt780JmJ0oGwfDAZh11Gex3coW/DaZBL4SnZPWdyx9jiA2Fa424/zl1QHyxGX6jmfz+Ho6AiGwyFkWbYkb6T0Re9irNVqsL29HfptOBxecyalusV4j6eX8vPYUZLNiH3M24QeQYo7PS3e18qU6C2rc7XyJKSWpdUvpuep/PP6CPQdyt1arQaPHz++xjd04RDVxxaNUl2yLDPHdZH2WpWN8t5778G3vvUt+I3f+A14++234eOPP4bJZBLuVMbyZ7OZqVtjbR+T+dI3EjY2NqDT6YSrGGh6rY28MlqyT1bV7lS/ot+xsbEB3W43LJqhiPl8NF/pN9e/1G60ZAiVSdw+LbpLuYhvht+WkWuaruQ8TP/3lse/5TYktQE1Wmjb0P/p+KO2dZZlS0esS/RIdK6Cp6fTKRwdHcGdO3fCZCzuum80GvBbv/Vb8Nprry3R8bd/+7fwrW99K0n/pyDF17DSafwqtaVHHqaOYc83ZdNX/T3HKmXpbUWR+mq+GGIymYSTaObzOQwGg/AOT9lCoLzg8beYz6DZoZSmImNLK4vnJ/2W4oyxsabZ0p64ipYfptcWDAPYMQhenvWc+vk8RqDRLZ3OhzxgxVOk/Hk+UplF4RkbKTK7Ctli2X+r0lEeeMpOtVMQ0glArsnYlEKrgCfQsSrwgVMlHbjSTjM86MoRKfjmwU0y77qxbkMJ4LrgKEtDVf1lKdciZWi8F3Oqi4LfCYB58UUI1r0NqeXTcqx2om3hCTwUoUX7tmg+MaMP4eVfD89XKXsODw/hL/7iL8LOWICrvr+8vLxWZpH6xFA0H+s7aWdy6njiQYNUeL7j/IfBKwkS/amGVJUOIx4Vg5P1X/ziF2FnZwcODg6g0WjAxsZGmJBFgznFSedyEY2qer1+zVHQZL3GrzwQzaHl12g0rslCXi8eZEoF54larQatViuUhZPPReAJYHFZzREbR2hf1et12N3dDcdC4qQSD3jS3bNaGdZYpMFXq448LwCAbrcLW1tboT13d3dhNBqFu0cBXuhFvvs1FtCU7BeNVyRbgi9ekMqQFk7xYIV0aoMWJOVBaGtHK6Wf/41pab5eucPrpO0wXUUwDfNFHsa2Oz09heFwCJubm+68uGyO0ZtlGTQaDdjd3YWvfOUr0O12YXd3F7773e/Ct7/9beh0OtBoNGA0GkX7RXuXKo8s+agFfaTxsWpoNqUVqCoKr+xN0bVZ9uLUEvRJUa9KctnKR5PZrVYLWq0W3LlzJyxiwtMI6LGAWZaFHaP07lEaPIvpTw/K9gPlLylwiAsN8zyHVqsFH3zwATx9+hQGgwHM53N49uzZ0rF3VOZjvlbQUuNzKZgrvZfy6/V64bc02e31fWI7ZD11rAq46PPJkydQr9eh2WzCYrGAg4ODsGsb+d+61kKiX4LW7il6RCpD2jEtyRceMJby1ni2TF/QslPiCDStZldTXqLtSduDnnzD9QOnEcuWTsrB4/e53ZXSDlWkoXRhoB9jiovFAra3t6Hb7cLJyQmMRiP4xje+AXt7e/Crv/qrsLm5ueTH8XahV43cJsTkQaq/+dOEsvHJn2ZIMls69hvlAV+ETn9rcserO9HnprKv6iNvy8goPl9hfRObNPWULdWf7zj15ONBbPGTVd8i/cP5IiU+lVJG0W95HqvIfx024G3D0mTsZ6nylpOoDR5pIHuYyttuaDBJz+k7eudWCj43RqpFkSCDFQiUvuffciGcMialQCsvx6KJB1A1WlcVgKRBWxwP1FmVHHZpfGpj36sgUuWGVE4VY7GqwBCAXfdYYDbVScTfRfliY2MDhsMhfPjhhzCdTkMAkAYDpXJvs/4qM47xmfa3NmbLIGX8SMYjdUBi8qJKuqkMAQDY39+Hvb092NzcDMFElBuegFdMXtLAvxT8520mveN/e8CDMvx4Ut7usfK9ZdHATavVCu/oaR+peUp0UXq1QLGWD/2bf1ur1aDdbge5UqvVrh2hSusZswct/oi1h8YjzWYTms1mmDButVowm83CsZaczzRZwe0JrS6pAWb6ndQPvO/4zm/NYUYZL/Ex/Ud3u/A6xPQbT2fJWSkfTc95ZYmWLsXhxvLw/ms8fp2no/lKvOYJctKxfvfuXdjf34eHDx/CkydPAOBq0QLea1lVEFcbe6nfYd20le0xXoyVRfOy0mk86ZX/qbzqkVlcN0jtBvBCr+GkVJX2VpZdTfQ3m81wJ229XofxeBwWHWDZKBvoRCzNJ8VXitXB0o9l64/HNuMJDScnJ/D8+fPw/vj4OCwYy7IXO/A8uyEpv0u84gmeYVr6N05M4uSPJWM99Fk0We1bZXwjz68msXBxZ5Zd3Vu8u7sLAFdXTuBubrqTu4j/bLWtlifPJyZbuW6T9NJN+Eexcan57TFfm+fL2zXPX0yYSjKYj3Gpneg3XL/y9FXyphf0Cifk50ajAdvb23B5eQmDwQA+/vhjODk5ga9+9avQ6XSCr9BoNALNdFKWX61RFlWOYctv0vjoc3wOCZqOj/ma/FQPmobLL+7LxGSWxbeSjaPpBsvmXxW8cQXPGNZkRkx/VbUYj9Mm2V6aLyilk77R6lJUR3v8EOt31XY95unV/dr7VFpiMYuqUeWYcu+M9aIscdr32oCoioaiTrW3kzGIZG1J155LTHkThvVnFVW2JVXKFk/x4xGrKrtqSIGPIvRgMIWD7nbZ2NiA/f39pXuV6ZFY/X5f3N4vGSn06DON7ljQSoLXMKgCKQEmCZTWmHMrvdPSSHnFHCArKIl9nOc5dDodePPNN8NRS+fn5/D06dOwq3GxWECz2YyOr9uCmDyQAmcAqzWcvShKAw9yr1NXcWdnMpmE++7Oz8/hW9/6FoxGo7Dz0QowW0EJlFvcLsFdFNJOCj5OOK1F2xsnpzqdDgAAnJyciBMQlv3iGbvz+Rza7Ta8+uqrsLW1Bfv7+/CTn/wEPvnkkxDkKeIMpdhQCL7qWMqD3+WD6fM8h36/DxcXF+LRyLSsLFs+1lR6LznDNJ3XGcb8p9MpDAYDeO+992A+n0Ov14PpdKruEJHokoK++I4u9ovJeE6zFKyzdItEo0QTL9OiKzZWtO/5OMc0/OjBPH+x83s2m8F0OlXLknivSDBQo1kLNgJcHYs+HA7DferSrjW6mM07zjjvLhYLGA6H8OjRI2i1WnBwcABf+MIX4Ctf+Qo8f/4cTk9PE2p6HdoOVik4nzIpxesC8GIyjL/X0nv9Qq+tdFNAm5i3Mf2n8Qge4zoajWA0GhUK7mGbt1qtMPFK9XOv14PNzc0wGUbzxkmxRqMR7vHm8OqdFJqLBIOksvjYxUkUnHzGiRFaHt2B5wHnea53UoJvSAs/DUVaBGNBqz8f1zTQHDsZZBUBPNTx8/kcnj9/vjRG8J7Aer0eFpxou0elcSFdQaHRTfPi9fDu+qH5pdjfZX3NGKhPn+fLp8BptJSdFJSOO5d2vfJj7D27aSWsW9ZjeY1GAyaTCTx+/BhGo1GIk3C6O50OfOUrX4FOpwO9Xi/wyN/8zd/ABx98EPgcrwuqCtKO+FR/x8vLRWwvK6/P8dmGpC8k/wX/9sQEMF8PNP9I82cBIPij0uRj0fgspdmyZYuMCcln0+xsK/917dhHG00C1V0UUqyBy6IUH0aiKRaj0Z7HYr0xe75MbOpz+OG+M9aDoswS+16aHPAEXKR8yjpYqZAcI/q/RiP9nubDn8egOVo30QbrhLfMsnRZTq70TFI+MZ7wlhcTql5D1TJOpHKkPK3AogTqWOCxZDy4OBqNokqbKx9N2VShXCzlRctP+ZYixUHWZKEmT1LB89D610rHHTLqIOPvjY2NMPE6mUxgOBzCYDCARqOxZIQib3j0irdesXRVBOYA5PFTtq+L0hTrR657Y/ItJWjhkZ0xxPLAHZDT6RRGoxEcHx+HAIxmWGt8ytNg4AZgeXLQkoe0zbk+9/CB1v60fHpkcCqveORrs9mEbrcLd+7cgWfPnl2jyfo2ZuPF8kgFbW8M6uf5VSB8MpmEI55pek5HLFAc07seUJ7L86sV2JeXl0v3D0tBAo0eTTdrgQYJRepDdU5KH3JZaOmRMu0s8TftY5o3PwLaEwhMoU8aD1z2WMDANk4W0yNlNT6O0c7ri79xQnY2m0Gz2YROpwNbW1tweHgI0+k0lF0Emj6UeJjyQszO0nQYv9/Jw6uabE7h81g9Mc0qAh+U/pT8aQCJLlzh7y2g7MS/qe7FBco4MWlNxmn3b3E+0cbhqgNKWnmaDM7zPLQr5UOqzz11kewIfC7ZG6ntUEa/UZ0Wy5//rclqb14pwHylu0C9C0AseeT1kTFtSp15+3L5YuVP/69S9nAdotkEZf1Ribes8ablpcUMOD9aesZKUxQWL1CZjouM+v1+uDIly7JgP47HY5hOp9But+HOnTvw1ltvhZMGfvjDHwZZVKvVkif+LaBPT3UAr18KuH2p6c6yNrnlw1A6ypRxWxCjvyg/Vz0WVlVeme84UmyjWExFiw+UadeYzayltZ4VSVMVvPZnWXhiMlK/SJuuvO3K02v6WaLNU2cp5lK0z638te9T6ZZkvhcpNNwUCu+MXYWgXYdyq5LmIgxP4RHC3u9SkBokK9tmN8HwVRgQVRvVAKBOIt2UQbeOcjXnCEA+3rJWq0G324XFYhF2ewHA0opN6jDhXVL43HPGv0WTR+BL31flvKBjJNGmlU9hXT7vBW1P7Z5rKYiS4gjzwB4ezfnOO++EZzhe0Nm8KeXpDdbyZ9SIvk2KH0A3hmhfNZvNpbuEcGd6ykkVqwRvd76j5PDwMOwsxHvoMAjhGeNavzWbzaXJGnpUmDZ2ve3jkSVchs7n87BjyBMc4gEnLNMyWpEHzs/Pwz289+7dg/Pzczg/P4fxeBx4JRY85HRmWRYWYWxsbETvoKUyLmZjTadTaLVacP/+fVU3SPf9YJ2994NZtHhsWx5o6/V64XQIpENrO54/7vy2nCsp2B2Dd9zQv1Fux2SGFGTENoktvtHy8ID2MV3oQ69JSIG3fbwOPqeV7/jBRUqYPlXPVG3vprY9fiPl4WkrLqO53uV8xydjLTq0Z2VBd9jxAFtMFqfCYytq6dDuGo1GMJvNluSuB7xO0+k0HL9Oaciyq53pFxcXMJvNwoLMPM+DXmk2m0v+AqflNtlX3vaRdupJkHQqr7ckm1P8Eq5TtCtiaBrtWwprl6Ekeyjv0zGi0Vym33Ec4gTW5uYmjMdjGAwGS+m8Y1J7HwuCF9UZfJGWVA5vI6wHbdtVjR1sWyxTOr2BgvJHyl19XH5TGcHrz+uKu5roojx6Yhftd63vrH4v07YxH43aV1mWQb/fh9FoBHt7e9But+Ho6AiyLINvf/vbcHx8DHfv3oWNjQ34yle+EhY60zvnsyyDbrcb/ImiOojWGyd58bl0ulmRvHn/r9P/vE265qYQ4+2XKc7M7W3vQsyYXLfKs2SS9Z12VHIMfIxobe61R2JIiQVqkGJqMdqK7DylQL8P86F6gftbeBIaLVuqJ33m9TE88TkPJB7WdP46ZWgqPusyd2kyVhqc3FGkz+l39H8JlgHOmfcmhXLZMjwCLgZNwJcZKLG+iQURre+qUKSrhofGmFOSEqhZhVDzKrNV94WkECWl6eEprpzwuEuqCHFnGw3IWgqXt1NMgUvBbA8fWGOzaECUB+Ws8r0KvEga61uL/8o6RZKxyQOGRej3jp2y+dN8tPK0Zx6DXysvlocHUiCEHzGlBdUsWjWUMco9+WZZFnaNnZ+fh+OKiwbUeEDOukukrDzwggZN6TGC9L3VXzSdx37L8+W7vOv1OrTbbbi8vITFYnHtaMNYfhot2niy7NCikII7UhpaHpfTVmBQghXgpjxkHYGsfcedrlR+t+QN5W2tPP4dTtBLwQAJNG2e52FiZjabXbuHyVsPHlixnHz8jZMItI6W7C4y5mN2Bpc1XL5w20WyZTxlx2jF47L7/T4Mh0NYLBZh8URZWUfrYvGHJ/DH20cat1r+ZZESUMP/q+ARnibW/5qMsPxWOg48fOWNBfBvpFMAUAbiJG6V/mZZG5rbBPS9h79itoaXLs3uiH0n5YPptdM1irR9Cl1S3a12qgqoqyRZa9kfRWInqWn5GI3FXbhOkMYu1xdFwGng7SS116rkrqXfLZT1Vy2aiuRXNHYA8GLhA179g/7P8+fPIcsy+OEPfxgmE9rtNrTb7aWJ59lsFuy1GI0e/pN+S7xp5RHzM2keVp5l+tlre3jhaUPP9+tAil8Uy6eIrCzbVkWQYpul2JRaWi5Di/KqlqdEm9Z33nReOig9HjvAU16KnxODVk4Rv06SS5xOL71V6BYPH8fa0fILvM+1fKTvuX74LMBTj6XJ2DJGZcqA+Kw0sARrYBfBOhiyaP6awX0bEaMRJwERdLelN49Y+V6lTdv1JtrWcux5Ovp3TKkDXK8TBj3x7263C61Wa+mbs7MzePbsWUiHu9KkPCUayhg36wbSz3djpdRDuwswplQtx5o+twJiRfiV7/Rpt9vXaNN251aNqsebxZcerEOvSt81m01oNBqhb3AxBO6OwWMpY/dH3hSQZyeTCYzHYzg5OQmyBumnaRHcmPYaonmeLx0pixOT2j2fGs3Wc0sf0QlSKjuyLFvaHWAZ3NLECpbLn+ORaBsbG7C7uwunp6fRnTRWXbFvkA7JgaEyQrrrVaMd79bBI9za7bY4yUm/wbytRT+pwRep/7Q+pXW1Jrgl3Ye8SOlPkTdSQBX/pquAUTbQ4B13knFHG+aLckSzDfipDvP5HO7duwedTgcePXoEs9kMOp1OyEvKg9aFv8O+pUfxYVvhb8wD7wik9ZICFfR5EUdZkzF0TPB3Wp21figSuKALcfr9Prz77rtQr9fhK1/5CozHY9jd3YVGo3GtjM86pP5ICVho6Xg+sfaM2d+WDE0J0kjlptihkl6h91Yj0L5A3sXycZzj/e6anF8HeP/gb+1uZi0Ap8mpFDki+VFUX8SuKuBlUflHZRo/as/Dl5ZMs+oo2cvcJlsFJDnLF7dJdee6RALVEVzH0Hyk7zi4XUJ1qaSbaPn0H7elivrIls1KT0nBf/TIWo3eMkB6+D2wsfxpHEKSwXwsePqvCFLy4f1J7y08PT1dau/vfOc7kGUZfP3rXw/f//Zv/zb883/+z8Pvy8tLqNVqsLu76/JTPPpDGjN4HD3A9bHhBeVf2serkA+rzLNo3jdtaxWRyWXrWmWdi9prkj/IT9qS/LGqQMeJda+p1lZc1ms2gIcORBGZ5c1TalMpNkr/jp2+kAorVgDw4u5eLB/lLqXJslnL7uS1oLU1tc0pPPGCVdraKf4Px7p8gFWh8DHFHFpDeBo01oge56YIYkEODx1WfrcBLzuDrhpUidK2kgSwV4HHxoKnT1IVnAeS0x9LJ5UTczw9NPLAJk7CojLlRzKm3Dfiea6NfS1oUmQcWcaFBSmgkxLY423LadBo5HnQ76T20YIqWpDFChbQZ0WPVvbQkmp0WuV4x34RvbBOuY11oTvBGo0GdLvdYGjSu1b5zgHMg+ZF86b/p9arSNvRsjC42263l4IWNOiL4LTFJuL4TtCNjQ1ot9vQbDbDbtHBYHAtKGUFIYvUW5MztO1pXa0+0Jw1fJdlVxPcuFiJBgel45m18aCNHcqH9DmWje3NF0t5xiIG8wHg2gQcp4/SyYOXWnlSPb1peFCqqJ1ryWTtXYxuy7mXvpH6l9ZNu/+Wlodl0vslO50O1Ot1cTEQHwNa+ZoOlNrd0+fSeJPy1+rqyZ+PXa1/eHtov720YvmUN8/Pz+Gdd96By8vLcDR5Gb+H04VlcsRsCovvPYERqSyvHZ8a2KN0S3dKxeCxwWg5fOx75QDmj0el06CjxYcWvRqk8anZi8jrtO/L+t5F7BJuS1o0eNq9jG3E5UQKtL7UyqoSnrFu6faydFllUV+EjqNU31ySUx4ZLNkhnn7WfC2eh2WnS5MLFvjYBAD1+EbJFtX0Tmr5vD7SM01+c1mp0aNhVWPEW5Zki3N5OhqNwu/Dw0N4//334eLiYqkN8GSqVHmkAccQPSGF94WEsmO8jJ637AntmxidVbVn1dDqFBujFFXJ49uEqhdepPpfMX7x8pMm91IR4w/Jp4vZ0Cnl0eeW/0h9TQ0eWiW+5zpZoyEGyYaPoYzdKOlTq428tsltkmkeHc1xm+RWZZOxFqzAAP7WoDl3RZTh5/gcHGh88qNR8Q4ZhMdo5H9rKBqwSElXBKtcoWMhy7Kw+wXvhMKjCS3H03s5ugcpynuV4PWV7jerqhwJVpCgzCSpVpZUJ+/9gFqeq+6r1GCM9ryMMVc18nx5p9nm5iYcHByEO5vPzs5gPB5Dq9VaCtjQHXjIu5ITs45+kTCdTqFer8Pdu3fDiuzLy0s4OjqCRqOxdIcshzTpDABLdcSdYdPpFJrNJnS7Xbh79y689tpr8P3vfx8++ugjaLVaUKvVwtFhq4DFZ9g/APYYlmwyWu9GowF5frVLjteF7pbG3wBwbZUqD2jScijf8JWmzWYz7GrN8xzOzs7UST2pv/L86ug2vK+KTqTT+2G0vHggUXL4UoLh9E5aXi69Y88KDHodWloXrVxeb0zPg8eSAy71gUY73kGJ9pXEB/gP7xpDubS3twfz+RwePXoUeILWi9Mv1Y3mT2mSZDGtsycwgjRXAS1gaY3RmA3k6U9OA8CLBRqNRgMODw/hf/7P/xnejcdj953KGmJ6kPOYZG9r44HqJgTnFfpOOx2A58npkGA5+lUGcyzw8RgLZPEy8vxqMta6K96iS5MTUlorSES/xQUaNxkI8vqD3rauAvwO4hikMSUF7aS7eWO+Uoo/JqX1BkirbFPkdWpXxHbFxMrnJz14QHW0JjtwDGiQ+gh/cxmHaendnmVs1VqtBrPZbOluULSBuU7R7jkuC2vBKCKFh1J5et1AnkA6W60W1Ov1cLUDwIuTBxAfffQRDIdDePr06ZKeGA6HlY6r+XwO8/k82HySXeWVWTeBm/JdbwoeXS29+yy1Ebcx+fiXbMdYPh5we83SGZbcjNme0t+03JtGjN+sMYkxHWuHrHdMS76hdFJVVf6PhCJ5W75FEXkm2YX4d9Vx4TJYhY5el/xXJ2O9gZ5Uoz+Wn/W9FCC0yirSITHDcJ2CKhYw04ztz2FDCuYgMHiDzgPAlUCmjgWmWQU90rsigjOWr+asWd96xp9FDwU6vdTxpQ7wYDAIaekkkWakSMaT9p6j6nFjtR1HLFhjBVaL0OWV61Y5nrb1BMWl77n8riqYVZWsTKHBG3zkuA1yHHU73gvId8TSCUz8jWk2NzcBAJZ202C6VRv72KYoX2gAqlarQb1eXzqiWPue0kn/x79xdx4a/NPp9Joc29ragrt378JgMIDJZFKIl63xjw6iZwIBy4yVK9Ubn1OZe3Z2Bj/84Q9hOBzCcDgMwTve1zFdJI0RyjOYB/IWDZhiADGWL/1bW8Dj5cmqg0d0koSXgX+nTtxI7U5tFmvCRMoL00hBCP4c+wT7qV6vw9bWVkjHg7QWDViPu3fvwtbWFozH4xAopO1i8Zam8zzt6Wln/J8ei5iSj5eHaF1TJwS0smLyWJID2K/0WdHJWE9bxXhSChLwhSdFgoUeu0uTk1I6adwUKVODVDd6bL0HUjvRZ15/1BOopOOW563ZSdYx+CnysQrwI/VjPOChrSzdVEZIeio2lqxn/J03ziL1lTTGPTGdVdmNNH+6EMpKi5DGNn9n6Smu73h+Ujvz/sVnkn6Xxgn/FhejZVkWbOPJZJLU3rSO0+kUtre34eDgIJR5eHgIw+EwTBhqAVxN/ku2GrcxY5DkU8wW0vpMk9/auKhKJsXGAtrOnU4Hms1muCJiOBxCnufhCqiNjQ2YTqdweHgIg8Eg8E+VspPTiD4SPV3BwqrleBF5YunIGFYtx8qiivZetx6uAh77nNeLj/UUXzLmC3vtBw8fef1u6V1V/m1q2phtjHqaL+a19J6EmP+jfcN1EKffgyLxg6L9YbUntoEl+z0+YgodVdi5nnKqwrrkmBqVtAwLno6iaGDASldGaaakTaGJwhu0qYpJbqsyf1lgCW28O7bdbi8diUiPd5GCGx5nUvqmqEK1HMaiAhLpKWNoas+5AzSbza7tgMSy5vM59Pv98HsymSy1ubSTKYVW6xuueKpSIBIkY05y6LVveBoepNTgvRcglg9NR+nztJVkRKUq2SIG5aplp9U/nm9ui2zHlYWNRgMajcbSSQF5fv1eDqR7Y2MDtra2oFarwcnJydLKbJpOghQsKQqcsEPEJmOlABg3vKX8cWJCmmzd3t6GxWIBn3zyCQwGg3DXdZVGNdYNHRReH/68DOjCmZOTEzg8PAxHotHAHpaZZVnYvSjVgbY1pRePIKaOAvYfBpu04z0tWST1pRWg1t5reZUBd/A1vcT5U8pHAwZC+RHPCG+AAWmgulK6WxXvd93a2gr5DAYDGAwGpp6gPLGxsQH37t2Du3fvwpMnT6L3ARX1R6wgu/Y37RO+GtuSZWX5hvMB5wfNNpKCDx4bgKbDsY3v+PHkEp3aO6tM6V6oWF60L+hzGrix+C5m90k0pPqOEiTe0MqJySv+DecVKQ2AfiQfvrfureZ0aDa/xXceHxnHlnQEpwUuu4pAk82W7qmqbA9N0jut3CJ0xOSXxpdZdv1+UIuHU/ydorDGGLcbY3Rg/Xg6abLeEyOQxqom5yUdEKsj0kbpwt/0dJMidiq2xWw2g+3tbfjKV74S3vX7fTg/P1+6LkRrA6l+kh+XSp+mk63xbNmrkuz3+MxVg5aPu1BrtRq0Wq1wms14PIY8v5qMRVt6OBzC+fn5Ul6rkFGYL19Uz1FEnlvPtPdF5KEUY0nFbfHtPdDGp9SOknyLtak35rMuSHJXsn0l+SDJpjJ0WL+1d9aY0PpMy5P7olq+NwVpAw8+53X1yIVUHqR3c6eC0rQK20aD5W9ptBSBp81XDcvfQVQdv6kCrp2xVRNddX5VMFIZmjwDetUdf9sY6zYCgzzdbjf0V7fbhd3d3ZDm5OQERqNRcEaGw+FSEACdFWqU8SOOvZAEl8VHq+xjKpSloJ0GyTnh7yTQI4uyLINer7d0NBIa74PBIDgXUnDQUx/6vycgZOUXS1cUq3TiaJvxoJrWzzRIxwMFWtvHgorat1XuMl+1gZPaT0UCGjcpy7vdLuzs7MDGxgZcXl7C1tZWOPKq2WyGezf53aIUePwvBgJwojLF2a5iLGRZBvv7+0unHOBz6kBKwZQsezHBwuU7pQ13+eGz2WwGjUYD9vb2oNfrLX2HAbDYhBLSEXtO2wkDaFKa2GSsFrS3+kBaSWmNvZicod/Tb3CC6/XXX4ft7W3o9XowHA7h4uIi8CGtg5U/pxHf4b3Inm9SYOma2DdeZyIlqIW7i2m78vwAluVxkYCnxH/WpJ1EP9ZhPp/DBx98AI8ePYLxeBwCelz+rEon4/9cZlC+y7IsHN+e51f3so1GoyBzytCWqhtjOprSYwVkKH9J4yEWsLHyl95LgS8OWh7f9WrVg04e8/Roh1oymbeBRWuK7qJtqAU0PbZWDLQMqUyAF+OJ56tNfMZkmzQuvbJEa28+QbRKm1miiYL3vYeOddCKZaRMpsXSFaGbynDan1x2emmQ0pVtzzzPw8K84XC4lC9d+BWTk/wZ/Z8Gbr33Q/P4gjaWaNn8+oGYDuDvUE7O5/OlhSspYxbbstFowOXlJfzd3/0dPHz4EO7duwf379+HRqMBR0dHMJ1Ow/Uekm2dYnPwtkJw/UBlhTeGIKVfpb1RBpQuajOgXtva2oLpdAonJyeQZVnwzbAfbgqr9HVv2pf+rKIK/4jalrdtLFl8Y/ld9HtMe9twm9s9BaivaB0kXX0bZcBtpEmzc16G9iyK28b/S5Ox3kB21QYxR1Hjv8g7jQ7L+aS/y5abgs/KILhJZFkG7XY7OBx7e3vw6quvLt1xh+08n8/DUZv4LQZvyhjllrLW8isSlJF4OsZDWgBOA3eGPAF6gOVgfpZlS/f54c7k2WwGw+EwrOTUAu4eGnndUvKpUt7xfLmD7TXkyijF1MAHD9JbAVqef0pAtijNMb2VqmOqMqZTA3a3wdBpNptw584d6PV6MBqNwp2AuKsUF0xYu6sxLT1aFiC9XT3pYv24tbUF7XZ7KU8pqM/LRVnPn/PJEdQXmN9sNoNarQbdblcMdKzSAJQCeIhYW3p3j9Gy6IQYD0LywD+ng+tQHkjDyTvcWY3H3tHyJF0VG2teHtRoS+2/Ivo2NpHhlYWSLqYyXLub1uuoS3wmtS+/V9oDOj6fPXsWAodasFlrK/5O40WrjhbwPR7njsf/SeOiCJ9a5cZkH9LA+4mPVak+WuDaIxs8kOwe6X5GTY7xwHisDzWbD/tMm4y1ZBj/W6qf5Td67GXN1rLKjtFkyQjpG8/4SB1Xsfw0WqzfnjzKwIqF8MUhN2HHcX5KaZ8yfWaB8jnffS21lUR/VTEiiz66yBAXXHshtR3VXxLfpvjMXEdrfkKKv8/bnMpIfhy4lzfoAuvhcAgnJyewtbUF9+/fh93dXajX63B8fAzz+Rza7bYpp2J0e/Qj/VtrO03faXLY6mup7KrhyZufYJRlWdiRTK9TAYAwgY79vi65laJDy6CsT+3lD28+LzMsn7JIO3M/7iZB9bhkm9I01LavKlYk0QLg1+EpdHjsZkumlekry+fgaTy+l8STXjvRC+/3Wj2k77U6VmnXSNBsLs2X5z5sWXlaFh5b0IoFaN/cBvmsX57mhGdwpcIrnGPM7qHDMmy9HXbTiuSmsS5lmloOPc4Qj89ER6vT6cArr7wSjlF88OAB3L17d2nV5ng8hvPz85AHDxZpRxoVxU3wUZXClefl2dWBTgA/5hCP1ZEc4yIGEHWOtXdVwaMMtO+qkjHaWLFWelt0YZ70f3znWYVfhUNUJL03uEcNbAvrcMJWYeBbwHJqtRrMZjM4OzsLk1+tVisEq9CRpwEbGoTkR3TeBHjQpd/vw3Q6DYs7zs7O1OOyOF83m82lu2H5xCsALC3OyfMczs/P4ezsDC4uLiDLMuh2u64jHjm0ILwUTKL8ojkoHhQNyNBAa6fTgXq9Dq1WC+bzedjBKpWF4Dsx6S4PPt7wmFssl9JA89XGstQ+lAYp4IbPuD7jvy1H0JKhNK0ku7T+5s+sfqPjk+cvlY1tz68SiIGW02g0YLFYwNnZGdTrdWg0GuHo81hQVAI9+lGi2aKJ1kt6V0au5/nV6SitViscA0iDNZyPtLyQx2i9+P/0nWdXp4aU3c9F5UkK6LjnR59LabV29Nh3vC3pzllJpkgBw1gbpASEuJwrIn+5fOFjhdp9PMBC88Hv6Q56+r2HztTAHe03uqONy1cteHhT9gaVdZosirXhKmgqEzTjvMHHVArtXP7xoN9tiJugTZfnORwcHMAv//Ivw5MnT+CHP/yha/EQb+uUutFvYmOG23nId/xED69/R+Ud0j+fz2E0Gon18eSH1wnRRYibm5twcHAA29vbMBqN4MMPPwwLrDXExohkp+Df3K6s1+tLtrs3T2xHy0bSaPf6kqvE5eUlDIfDcMVMp9MJC0UBysmiKutW1D9O4csqULbOlt9WJVbJd9w345AWXtNvtecpvsW6xpRUR8mHqhqp8TlOZ4r/ftMyalVItT+rLpvyepWn/q0Kkv2REjspamtWhc8KH5t3xmrQAoWxoFsKvA2sBbvos6qg1cUKGmjQlBV/d5NM7sU6FWQKqCOZZVfBdVwJ2Gw2YW9vL+TZbreh0+lAp9MJgWR6dx2ngTuWqXRpdUrNT8rLm0cVwcyUoJOWtyXkeXCyDCTaLPlRBbz5lg3GWQ59TJZ65bnkoFNnvoj8L8v/FmKBH27EFi1Dyl9qDw8ttN3XJVdpABZPA9je3g5HWWEwQwt00++lAGqs7FjwidPpyRPT4cRrvV6HyWSydBe1VBf+m95VKtFEJ4bm8zlMp1MYj8fQ6/WWJn05bR6kBPTwb87zHjuqKtmaZVeLnFDPSvWn6aXxodGC+hj/aXlYugKDbDQIieV625rrC+kdLU+iQZML2jc0DX1P+1dqBykPft+6p+xUOUQngjBIS+/65W2vBak4H0s0lbHDKKRyU4IaEl9ROmP5WXws9RumQZltjSVtTLwMfkbM9vPoXaueWl9z/pL0s0aPJiMkecPHXxX6gfY75yOehtOM7YcT/XiEu7Yoo6pAiOUHUJT184vYVpa8TuENj4xOoYWXx/uiin6pKg+LTg1lZJLV1tq4bLfb8Prrr8N4PF46iUmSH1IeMXo1WWP1qVY2z8vyM/n4pvnx57iTski/a/7BxsZGWKSUegWCR/ZrdlmWLZ9uo03IWj5ICsrKpiqBdjLaDujDSEe9p6Ks3KoCMd3nTe8ph441Tx976roqvqhKF2Ne3MeI8XaqDZOCVbRZLE+rPim6PoUOj42i6Z9V2fSWfkmB5sdKeVv119JKurUM36TUW/Jl6f9l+bcK2ebhDw8fSfrWY4ukIsUHL8JHRcpaFcTJ2FRlsg6nXhP8qd9Q5Ll832dRY/RzpIMPoirand+Hhmi322En7L179+BrX/tacA4+/PBDeOedd2A4HMJisYCPPvoIBoPBEn+gY0F/F8VND/wqEAveVqWEtHwR3MHTAk1aftJzqX9WscqpKM/HFF4sKOmBtOOVGhj0PiRsZ629U43NquFpi5QgkWb0VTGuq3RoUvlrNptBv9+H0WgET548CYsh8AhxCgyuoLw9ODiAbrcbjpfVwHmhiiClRFue5+Geyel0eu0oXl4mBm4wiEHvCgfQV5OjHVGr1WBnZwcuLi7gb//2b2E2m8F8PofJZBImtss66inBpFRgPjQIH3MAsO/v3LkDW1tb8HM/93PQbDbh+9//PoxGo5CeHlXL84g5VlmWweHhIfR6vXD/MAZLKQ002JcaNJWC6rQ9aF50gVCsfVL0UQp4/vwOUwm4oIL2HX7DrwJIrRcHBgGRHhwHOLZooIeDjzNcQKcdKZ4qPzSnWdrtT+nU6rtYLGA0GoUFA9PpdOnIRiwjFhCXflN6OT/i0ch4xKbVJlSX30bbkwfOLRqz7Op+QtomAMu7Arz+qbXTgz7HMYK8HOP/Km1e3FEe25VB6055BnnRs2sC69btdqHVakG73YbxeAyffvopALzQhdbRzhRan3C6YzLyZfDJqX6j9eHPqtbXMZr4N+uCZROXaYMqx9bm5ibk+dWCoWazCffv34fZbAaXl5fw+PFjeP78eTjtQ7KBNUjyWJL73Nbw5h3LPwbLV+P0pyDPr07RQdlweHgI77zzDuzu7gaZsVgslu7oRXrQZpCOCsU0Unmcx6i9VqvVgl4ZjUZLCwS5PcFPR+H6liLlpIB1gtM6mUxgMplca29Nb/60wTPmqvTHi6AqfZFa5sbGRthRTWUfjg3p1LqYjruJ2A+nx2pPfpQ+Bdqd9CS/KlDGtkn5TrKpipb3MsmLKsaPJyYjyQkp7nEbEOtHfhqK5k/dJpTt55vm7aXJ2FgABtOsujO0/FOClB4BZ9XX2zFF26aKsqvETSh8AD1oUiYfgOW2w6NaqGFer9fD3bH4/WQygVqtBo1GA6bT6ZIBgvcf0vw9/bNO4yNGjxSALJIPD0576KqKpy3Hi5cp0cG/k/LUyuVptSCwN2Ado5fTmfptLH1MzvJ+4+2Hx1BaTqtllK/KAOX0F+E57RurTzV+KMrzRRx93q6pxjrWb7FYwHQ6FY9qswLXWZaFiU8MIEv8tCrjmPMoGpV0Ekj6XuJfzeHCdpJkQa1Wg/l8DpeXl0tBHeuOXakOEj20TD6GpXy1d0XsKwuUJjxZAvWtNl68dGM7jEajIGvoJIBH5xUZA/y3Ra8luzUZVJXTYMk3jWZvm3HetvSaplNoWi3o5wnaIB9YOilGC/3G4osi8hoDNNJR5ohUXcTrq6VBmlP0TdW+RVF9TnnNY8MB2PZnEX+sirbgfLqqcjRY8oXSxHlQ0omor9BH4uWk9rP0Df72BjWr9CE+a9DkitSv3m+rgqZDtGcxaHycSo8kQ9Cn2d7ehgcPHsD5+TkAvFh0iPZkzC636mrRpL2rQp6ktHURO53SQhc14d2x8/k8XIOCaSwfPLVuWn/y/6lNaY2bMrRIv8v6OzFYtg4ABJs5tuBp1XSm6I9UnpWQojNidmhVcjK13TU/sCp4ZBPaAxJ/x2I8sbZbNc/xMrxy2eMnp/JESl1jPB1L6/UbpN80ndcuT4Gn3dfdN0XhlTE8rhD71ku/ZV/HvinaRh7ZvA5dwsuzfG/pG/4tTRsrr2ok3xm7DsFZFhaNfIWCd7WwVEbqAIjhJtv2ZehXCxb99+7dg83NzaW+XywW4Y4TAIButwsPHz4EAIDpdArvvvtuSFuv16Hb7YYycPeDBMswuW2IKWKKFF5PDTjSvNCRs+5ZjK0y4o5Unl+/S2wV/O41kqswplLBA9exySHaL5KMbDabsLu7e23VLU56eWgo4lSkpKc7nDTwozM1IzUGKxia8q0WBPEaTlXpINoOtC34c2y7er0Og8Eg7H7DyVytHbMsvvuySN0kZ5HvKpLu/pTsgvF4vORM0nTSHaOI2WwG4/E4pE8xqjUaKZ14BFnMzvHwPwUfAzFasd+zLINerweDwQCm0ylsbm7CvXv3oF6vw9HRUQhyakc9c7qlelxeXi7JcLpIAOlYLBahXSTjWtr5SN/ztuB/08ByLCjM3/MxLo0vfk+jB7EAkiXnKY0xG0AKuuB3MXql97QvpHdUJvNxJ7Ud/y0d6YvyKjbOeL1pHvy5V97GvpX4COuuBbx4fWIOPbezvGPdg6L5aN9hvfiY0PQjTYu/8X9Lx6zaLi8S8EgJfmm8RPmG7nrnd8fi/1wW4LtOpxOthyQTMN+UgCL9lv7N78dcl6+aYst7+pjf11gVUvU8IkZDmWAdgC/gV6QduCzVeMlDu3R07auvvgpvvPEGDAYD+PDDD6FWq0Gz2YTxeBzNTwsQIp18MkzTy3Ss3pS/WCQ/1N0bGxvQbrfh9PQUjo6Owu58a3extIMc4eFV+r+0e43rUcnm52VxWriM0mi5LfEeTiPfqbVuOovwVFFerBq3gQaA9U1yzOdzGAwG0Gg0YHNzExqNRrgPOs9zODw8DHFQ9ME0WGP7ZYMUY6Dwyir6fYo/EfNbuc8pybOyWEWeVWMVcjimk6R4z22RGxKs0w81WDE9z/dVQPN1b3NbxxCdjE0RDimdkNJoKUEDKy0XIKkBFZpWKifF8bQG7G0VbrcV3DimDg86APiu2WzC66+/DgcHBwAA4Y4/gBeBB74LFvORhE0sALZKoPCpSgjGeFATvhJdPJ23nbSxEisrJoMoDd6AEf1WCmBpuCll5EXRAAgvk64Yx13n1o7Kqui15GmZwExMnpcJOHsMHJ7uNhgWlKaYAYSBLX6Mo9YnPH8trZSO06DRpJXL0/KgGZcLNIBh5YtHE2PeOBGQOl45PyCNHluI68OUgGhRnsN+v7y8BICrI+GkCWPeN1Z5MT1rfYeTEBy8Tbx2F9cX2pG50jcAy4E/r+7Rxp1WjqeNPLqUQ5MBsTFGv+G8bNVB4nManNbaMlXOW3qF8x7PWzoWGhcDSHwbK1PqF6mteXlF/RitzBQ7R8qP5xPLo6yOy7L4YoBV2WO0/WJ9kKLXpXGijVtvAKKovcrHIv/bkhkpOpvLam08SHI7Bo98XJetVSUvSnreU6akfz2yPwUeeZza5kVp8ZYzm83g7OwMdnd3YXNzE5rNZph84AvyY+NJsiE5LfiOH2lP03psvTJ+SoqdbD0HkG0cPC2CXongqZdGUwo/oU1Kv9Vs6Kp1xG3x3QB8NvdN0xvrV0v/eeAZJx47ykpThodug26i5fEy6V3SOzs70Gg04OTk5Np3AL6+pJDSp9TZ0l1F4e3LWNlW3dYRJ5Rg+T0eHRuzeVNtzVXK4puQax47vajPpoH7F7H8Nd0Qk7Mp+a3S71rHNxxl/awYrk3Gpjo6VRBRFbTdJBx5nl/b2WjVuagToEHKS2L2KsqM9WdKfxfljSLw0i31b6PRWDpe6+zsLByPc+/ePfjVX/1V2NzchCzLoN/vw/Pnz2Frawt2d3cBAGA8Hps7M18WxNrQ25cp/EH/t+jwCPUUGrjjGzM4YtAcxlgAnaZLKS8VRWUDP2rbU47HiOh0OrC5uQmTyQRGo1Fhh3sVoIFByqO0LlIwEP9fl/FchmeqlM1U3/H7Ma3gKl0ly/Ph+dO/qwjaeccl5Uv+jbVzk/MI5Z0se3GH6XA4rESHaw6rd5HDqsafxWeLxQI++eQTaLVaMBwOw+5gzjtFeFU7Xo3LI1oObysrCGXxNf9OOmXB4mPkD0ofnUiTaI/xjVXv2DdWWksva3pd07XSc4nuLHtxByhO4NM7QTEdTnTW6/WlhUCYL07WavY1H7OcNnxGd8ho41CqIz0xpapAELVzeR2oD0N5kOu6WFkS794W3X0bIMkb+rtsW8Vkj8aD/H/LTvPygwQcj5LcsyZRkWethSuUZzmd6/Q3iyCVNtoO3iBYrGzadrEdsutsV2vMlEHRNvLQMRgM4Mc//jF84QtfgP39fWi1WrC9vQ3T6RRGo1GhWIl06g5Hq9UKR/hSHShd86DZMKltzMccpxvHtbRzWMuv0Wgs6VDpW6ktqO6O+QNa/aXneJc7vscdu17cZtmDiPFkqt+wSlqKpr2NuA30e/q+CI1ZlkGz2Qy/cRzhu7feegv29/fh008/Daei0THI5YoVA1h1TCUGy16O6dGi5a0aMf/upvl2lahyXHplq+b/0jTrREobSOk8cYSXiYe89Baxo6rEtcnYIp1joagykH57GssKuCG4k6sFj6w8eX5l3nvKKIpYvkWcm3WgKN00QAcgB08wSAxwZWh0Op1wxMZHH30Ex8fHwcjA/HAyF7+hdFj9Kzkjq0JKQC02zj08L6WPGTBFjTDr+5jjpo3zWB6pfSXJGXxepVyN8ZRmJOA7T71432Lwt1arwe7ubljwgAGg+XwOFxcXlQSjPfXQaOXQeEB6VsZBqLq+UrBuXeUXgSYHuZ6l/zY2NtQrADy0Sv1VRObRYwOzLBNpkhxL2k9eXtVgBdZ5GguWDSONfToZ47GzaJvn+dXEGB5VTNtCkn3S0c5l7EOUO81mE9rtNoxGI5jP50v3wNP8JVlAA3Xe9kX6NbuRtiktj773BFklGeyhTStHqh99hrKctxmXS9L30jc0PW0TjQ4M4PKytre3odlsQrPZhPl8DkdHR+5jdj1B3VjwQstX6mMrvSdf7RtNF0h0a8e/e/hbGptYV3rMakqeGnjbSTJDkk0a3TRfznu8TC/fWG0t6WjO9/zbFLtaq4P0m5dB5ZS1SEH7djweQ57nwaajfaDtRE4dQ7Q8Cyl+jRep+scjq2OywGOnxpBSd4tHNPuJl5MiQywdpvEx2oGWDPaWh99LR7Bq+aO/srGxAePxGH7yk5+EUz0ODw8BQD7ylpdp8QTXFfgM6cRrJ/jR/UX0En+fIjes71JAaePXC+A/nGimV5lI+Ug2q3b1B7dNebvjO6lPKKQyvahCNlVZ3jrpSSmrCJ+V4Ulapjct3bGOepTKAknXx/RKTA9L0GzHqvpe6guuH/jR+rVaDV5//XXY29uDfr8Pk8kEBoPBkgwuKtcpLWX4V9PZ0nUwqfwo+Tha2bH8udySaNbo8thFlqyL0WflX4WuSPnewwtVyrvUvKwxr6Fs21n5xuxmzX4qYltI33j4NwarrNiYkMpP9TWK0mtBy988priIo+LNz2tkehVPCiTB5BFU3vRF6JHyLMsYPw2gRkOW2fcWAFw5Vv1+f8nx2draCsr5Bz/4AXzwwQfX8tPuiC2KVfXtOnjGYxisC5ZxI2EVzgnNs6r29wQdrW89gRGpHE3W4fio1+tw//79EJjFo7yOj4/h/PxcpUkK8mplpQR8yrQ1NVJ5H1ooY4R6lfW6ZL/mcHmdyZR0dJIsdYdnKt9LzpDGO1h3BHfStIAO/k3LKnoXkydQTumMOVcUUlDL0z4x4E4DXEWNAQyeLwL7nB5bJ6XzIs+vJmNrtRpsbW3BfD6H0WgUgijSmKb9xRduSfTQoB4tN4VuSxbH6offW7yNaXigkdIfK8frpFhjkdKojReLBuxPju3tbdjZ2YGDgwOYTqdwfHwMs9lMPeXBy8u8jaT2pmOPlkHvyuZ1tJzBVGhOJJUF0piT5KY25rkOkIB1ldqj6sAZH5/4t4eP8zwP7YH199gdMVro37zuMZskZaxb/ZUCWnfPHdRUztVqNRgMBksnG/B0PMAnyRlLTvDvPTq+rO2eYlOlpL1JHz2Ft6TnkkzQfld5Zy+WTYP2kuyPwdp9TcviwDrgHaa1Wg1GoxH86Ec/gidPnsDjx4/D5IKHt6021uwLlG/D4TDUfWNjI1yXpLUHHTdSn3h9DEtup44Trkuo3MYrbOhpFrgQE6+IkvjOajv0R7W68QkzKQ8Oj44pizJt/TnWA+TPRqOxZFtNJpNrvkuVft5tArd10ddD/q3VavD222/DdDqFx48fw/n5OVxcXITv6C55ybZBVDEeNF2tyTf0D6XJWEuu8PKsci0ZbOlcjeYq40FW+RItlh0n0RbzEV+WMSBBqquVVoPkr1bdLlrcQqPLazdYZWj1WEX9XlZe0ug2Z648zq9VYIwgL1LSWgIfjTRP8CDmpBSFRp81QF5GhlsnaPtQ42k8HsN0OoX5fA6NRgO++tWvwv7+PjQaDbi8vIRPPvnkWhDt8vJyadKA/u9BrL88goumi+WlBYK8hk8KtIAZzb9oAEOjjQe9ysicFBRRWKnB1ypljNfhlAwJq66cx/L8ajUzGqzeHUo8QMyDAdJzHrArAm+fYDnSziyNjlgQ1kobc1RiBn1V4OV7jOrY2LACrPx4UcsQ5Xyh0R6D5GxwWcZX/vLvPKtoOQ9rvCv1LW8DmocEa9UxpU+zdawAo5f36IQU1vX8/Bzm8/nS3ZnozPMAnXUstARK32KxgGazCXfv3oVmswmtVgt6vd6SXtKCx1znx5wHzoOS3OQ8weUODWTwHaB8FblVDn+vjQFaR4tfaZ4035S+4fTSfipiu25ubsLu7i5cXFyEhXN5nsPOzg7keQ77+/swHA7D0Wke55imwfbmz+l7gBf8TccyryvXgxK47KD2pdVGqbY/HW8Sj1B5RAP9Wj9Lz3HsURqL+ihc51BbkgYAOWiZ9B5hbEs8xQaDbpY9r/VnjGZv3TS6q/LpvLKT9r0kFyUeWSwWcHl5CXn+YqLMCkRLcirm33jqIPGYJm+raNeY3NQQ08VaurJ2Hr2jPtXviNlgALYu9ZTD24DL3slkcm0Mp5SVZVmwJzxpJTmOfzcaDZhMJnB4eBh2rHps4lSg3dRsNuHLX/4ydDodePLkCQwGAzg5OVFtRCu/oijLj5pvVK/Xw+QVvZYBF/Dh/3y3Ic2T8o61w5/zpyXred0tubIqcD5cdXkvM6qWl1K+1M5A/wVP2cETwDR6kCYPXdqpJbcNeEcst8kAAC4vL5fu0sYT0+7fvw+DwQAuLy/DN1SuU9uHL/ym487yzbQ21uQC98WzLAt1Q/uQxoyx32NlajqE0y3VS6KRl2XpQU8+qe9S/HAJmn3GyyiDVLtgVUiNV3jSrUrGaeVp/VWEtzikvsfn2hjVvk0tKwarHuvkK6ksexthiQK0DimDWB5WUAHhMdjL0lOlor3tSvs2gRtE8/l8aeXr22+/DXt7ewBwdT/MRx99dC24MBwOr+00kIKjRRw0z2DXFK8krDWD3goKWPnG6NPy1wwpKw/puRUE0ZymlDbVDAJPfbR8ixoxvC2tAKFEWxHwesVkNJ1AoO+kAIWnbPzHJ2diMruogSDpI09/xYwHqQwpX4+BZZVR1kAuCo0/PLC+wXxj9zZpbVmET7R60GBxzMinYzVWP06TBMuh1PIsI2Pwb08eOAHisbWQLpxcBQDo9XohuIoBuTxf3vWCgTgpsB/rYwTuiD04OAgyhe6I84y1mBOk0SLt6rYcc4DlCUCaP7Yf3eFDv6F5FLEFNfkiBULo5JfloHt0L29n/p1Wl42NDdjc3ISDgwOYTCZLk7Gbm5uwsbEB29vbkGUvjlLldMXqTwNEsbaxxg2+12wTTcfTNq/avqeBLqnu0k5HHlji761TDLAc7642zW625DQvT6IBJ2No0F/aIU/ziPUtT5NiC2jveL28cl3yOVKhtYO1w2wwGADA9d3XNA3Ni9uxHv6WZIX0W7KR+fOi8Ppd1reWDMBnVY13mg8Gl6U6eMuL+Qb0Wdn25r7AeDwOOyc94ONWk6eaT8XlDgXe3TocDkPaGG3WGJBAbaNGowFf+tKX4ODgAGq1GhwfH8Ph4SEsFosw4aHVhT+3bGbLx43BUx9OG+1fALjm76FsbjQa4Yhmqx0pvXzhk1QHqb8s2VIERW3zIr7VquyFz3EduFgSF3U1m81Sspvb82X1QBmf0EJsTAG8OFkQ7YE8v1r8VqvV4M6dO5Bl2dJkLM03Zkut2i5GoL5EPxRPt6Hyyguvv0rTa++1unvjDjQfqZyUfKQ8ON9Z9VylnLqpuFiViLWhx+8o0waSDREbm1a7WzLJ8reK6OQYDUVx07q10slYC6kKKMZotGNjAqZoEEvLb52o0oH7rIKutAS4mizC3Xv02OIf/OAH0G63AeBq16ykeDFQB5CuFG8SRcdWKn9VyYs0mIl5F3XIvUgNDiGdGrR3KfWQVvVq39M0sWAyLaMIsI1xLEn54MpIHvzku1GsHS60vLLymvIzN2isfqV05/ny5JGHzz5rcprvXgSopo7I7zxfK28pYI/9SfvdGhO0bJpnCt34f9HgS9mFYOj4esc9fiPRzo/3o2MP03llJa4gp0fSUVnA86GnVWSZfyKH1xEDuXQyk9cF60rHttQ+XBZRXuI8ZrUFzxef0SNlOa96dAUdA7FV+hZNAHagkqbxjhFaF85vNI22+zfPc5hMJrC/vw9f/epXYTQaQa/XWypjsVjAs2fPYLFYwPPnz69NxKaA8j1/HtP1WGar1Qp8N5/Pw5GWmh4CgCX7tNlswubmZgj8jUajsMOA0mDxhtfGoPWbTqfQaDTgrbfegslkAo8fP14KjktyCtuLL8LCd2V0AuUdOonM5Qa9VoT303w+h/l8Dtvb29But8Nuq2fPnoVAIS2Pl63R5aW/SlAZU1Rf0LakdZ/P5y55gTIJJ0vwOf6v1bnIJINlW8SCN+v0xcr4SdgPm5ub4WhWTQaVpVEqW7KJy/J9EdqQF6fTKbTbbfiVX/mVcCT24eEhfPTRR+KipRiN2u9YHTX+smwEmkdRGxLjFaPRCIbDIWxuboarX549ewY//vGPYTKZmEcwe/rPEzTXxl3KOMO0OHlFv6N2Pl8so9WH20Ve34Dnw20OaTEc/wbp9NS7DIr4EZ+jWlBbAwBUG6ff71+LU+AxxpSntZM88BvP3e2cPvr9uoG+HR87h4eHcHx8DAAA9Xo9LCShwAUWuJBFih9J45zGXlP0o3TlDJ1Qt+qIV57UajVoNBqwsbERjqSO6RrMg9NyE/3lAaeriAzSjnf+HDpWLe/XoU889sTnKI7Ck7HezrcMX3we+1ZLn+qsFWEizoCftaD7ywSL32gQC40qgOXdJUdHR0tGQ8zhKNrX9Dtv8JbT4VWaKfStSlhb9dDSprZLCqo0YiV6Y+2YKuc8gS5v2R6ktA8a5HwXG3WwqcFODWLvJIaXVg/NnqCT1Zce49sLT/kUN6VXNBqL1N3DT9R55WVKf0vwylhOU2pAVeM/K+jFafPaSZIc4H/Tf7RsTgulRypLo136W1qcRIO8NMCGz6nzTlcfA7wIyuE7azLW0oPoSNdqtWv8JAX8UvqC0+DZzS3RS597xoaURwqdsbxjkPjW8y1PK/GTRC/y0Hw+h3a7DT/zMz8Dx8fHYbcUpsMJz9lsFv6nO4dS6koDYx5I/Y/BG8rXMVroeAG42nGBCxlGo5FLV5S1LXGs7ezswHA4FOnj+UpHVsf6l6b32tGaXJXSUFmD8qRer0On04Fut7s0ec939qYELqy0MVo9el7qX02mx+wJyTem7/gkQ6xPeKCa96lUH/59qtyRnltyQ9K1qTEFLz2xWIYFnIBDWxqD1auCJjd4+2CbeewcLe9UfYWy9969e9BsNqHf74fF0RpfVeX3pNiK2rsicQHOs7VaLSxkRbmFd6JzuUInMzwyP1V3VwEqX7QxS9Ol6ANLNqTAE1vxHsPugbe/UrDufv2sg8sbeu0Bvud6hS46BZAXTUn9Q32kWPqy8ZIUX8ArUxDD4TDUudFowO7ubnhHZQDattz3kXykmB0U0+mS35lie9C+3djYcB1XLLW1l+ayqEoOWL6Zlp7rwZR+8qTzoKrxctPwtPu66ub1K1N18rrof1l5AFF4MtbjoK4DWpDLG3hLLatKpK5s/xwyFosFDIfDpd9SmpOTEzUPNK6snVNV8lMZZVIkGFAVvMFACR66PWlSykV6yzg+VUOiBVflIa10wgKdd2++1so1/r+2O5zmx51VPKYmdhRUnr/YLSvdPULTSePBMwHiCZBw55/yBD1ilZdfhbzXAtxSuqrK9MJykPB4d6SNHuUjpfeW5zHeveMuFgyXyuffc1qswLJGr1YGvR8XQJ7UkL6X2iilvT3OrQYeyMMxQunHdwAQdsXj0cR08gOf37lzB958882Q95MnT+D09HRp0ZRWD/631Ba4wwQn8LjsxPagu+piTlCZQLAUFKVBeCk4Qf958teC5DyYGBsjWtCorLMv6RC6a7rf719ri/39ffit3/otODo6gg8++ACm0yk8f/4cer0ezGYz2NnZEcuS+MSSMZSXJL0YaxOOer0OOzs7S9di8AAPwIvxgwE/PKYSAAL/IuguUdqmUt9IfUzLQd7Psiy6U0CCVh7XvxxeO1VKw4/LlnbEYjq6GCTPc9jb24ONjQ34yU9+EhaK0Tw4bTFb3BtgqhraGLfGP8CLBXLY99QG8wbPOO9WFfzDPGI+BB2HXJ5Ksr0KmorYHZ500+kUNjY2oNPpwMbGBuzs7MDp6SmcnJxAvV4vvJOG276pdoI0lmi++HeZ9qXf8rttsV0o6vU6tFqtsDMJ80D7k491uqjLQwfnPSqbEeiLaXeZ0hMOimA2m8FoNDIXoS0WCxiNRtBut2FrawuGw+GSTS7RnYKYDPHykZaPRVee5yFeg23AdxlSGriOtiDRT32rdfpXqXEKgM8nWdcNyc7a3d0NE4wbGxvw1ltvQb1ehw8++CCMwel0Go7wB7B9ySKxaC/vrzMOiGVh7Afv1MbTVQCu6G42m9e+pfJKi2fl+fKJZLjIVrPHua9N88cy2u12oLMIuI7VbFXJhrzpsZwS3/AiZi9a362LV6soq0ge0jdavKFsWRxlvpd81XXB44t4sE7+WhVWfkxx2QYumjZFGErfxjo2tV5FlOlNC/ObQMxJ0L6J7a7J81w8RoenkZASnPHSm/pNWXo8+XkVbVGhJxmWMdq1cVyFcuMOuofvuJMXgxUIpvnwewN5Guvo7Fj5nE6Lbq2+9PlkMllaGarRoQVoPW1C89CMceu39k5yzDV+9LSFBzEeony4ToNVAwY88zy/FiwsQl/RgDynk/ZdFbRIzopWfqpOwrxpGdp4qBpF5BL9lo7HWFujvJrNZqGuNOjf7XZhZ2cH7t69C+PxGAaDQdhRiMefa/lbNhymx6OopOPFYjaXxkurCNpRHSPJLk8wvYic8NZDcyLp30XHMddjdIEClS21Wg263S5MJhM4ODgIV0pgkHY0GoUyqhpLsfaOyQR6T3Ge59fuQubfSe2q3clq6VT6TuMn/h1tt3q9rh7fq5Xpoa8IYr6aFgjjMpZemQDwImio2VxWG3rpTkHMR5XkEn9H20Eas8iDOHnF627xWYwHUtuoKH9IOpnr/pjtVgSpfoYXlHaUGdad1UXhtYGRJkob5yk+Nizf0JIP0nco7y4vL2GxWEC32w2LUmKy3SPbpO80ntIgyRpetiaXNNA60Enks7MzODo6CjLszp07UKvVwlH9nH9i5ZXR+do7bx4xeqjdRt/RPozZfCllW3LeY7/ztKvczW4hpU89acv4UFVCkjc3AW63ZVkGnU4HWq0WPHz4EJrNJpyenoZFc/REMNS5CMkei8kfzS9A2vgzTe7E+DvVx5XA/bzpdBre8VOQYvlYkOqi8Qm/k5rnLelbOnEb08XUd9P8R8m2j/FCUXh0vSS/PN97y9TqKJWn/b6J+IenD4r0kRbDSPmuCO2aPSS9KwvN5kmxPbU8vfDwd+z7m9Z7EgpNxq5DoBRJRw1c6QjEVXTAbexUxG1lOguroJcfswoA4c4HRNWOcQrKKEpP8Jamrap9i+aT6lDFHDRvmZYRW8QJLQMenMaVhJRH6apsvG9qsVgsrcbUAiwaqILW+N3KA52Cfr8f0vJAAU1Ly8P6WIsmvAEH71jlaTzBZ+nOWCnfKoxbz1hYp0ziQbk8z2F3dxdarRYcHh6Guw45ranyyuo/rV1Tx6UVXIzB2nXgoQN3W2N70rtRq+xPHGOeABHvL20SAODFHYwIPG6Kl40LMvCoYbwHNs+vdj7s7u7CP/gH/wD29vbg1VdfhW9+85vwd3/3d7C5uQntdlvUyxLd1OlDuuv1ejjtQpIXUoCW31OM9bCcfsmxkd5ptNOyAZbvjKWyEyeR8vzFaQJaW6A85f0m0e+FNJ6rsokoX1E+qtVqsLOzE/pxOp3C2dkZZFkGr776KnS7XQAA2NzchFarBR9//HGY9OeTTd660d+cFzSfQQvm1mo1uHPnDsxmMzg/P4+Wj3yMtI9Go7AwgfN4LOjmqTPlD8pTtVoN9vf3l0474PeDrxKW7AGwdwVL+WDdBoNB2E3WbDaDrqJHQPP/eV6rtv+sYChdqBALEPLxSuXIwcEBvP766zAej2EymcCzZ89gMBhAs9lc0heWrPDa3Vm2fM+blJ9Ujia7tHrfNl9Wooe2F22XVqsVTgNoNBrQbreX5HfRMZcSjJL6kn+DvFfkDveU/ul2u5BlGXzjG9+AN998E/71v/7XMJlM4P/9v/93bXck5ok7KVE/UlueTwBIR74jfZi3tQOETrRg/kgL+gYox2M+jZY/HqueZRn88R//MTSbTfjVX/1V2NnZgd/7vd+DTz/9FP70T/807KLVdnZxOenVG5Q/AZYnR4uOM/zWuusW6aUTOPRbTefyOvIFTDE7UpIplp6p1+vhH5VveESrpzytHS0d8DnWjyzLgq0wmUzg/Pwcer0e/NIv/RK88cYb8Gu/9mvQ7XYhz3M4PDyE9957L1ybgf3WarWg0WgEvka+shbmabwXi+lRG5GfXMHTSj5QqrzS8sXFklgf75hIjedjmTQfLh8mk4kax2s0GtBoNK5dDYBHwuOR8VTG4glP2hHUWnvje/zt9c+1vCRw3Wi1KdVhnJYqZM4q7LJV23q3yY5MBad9XXpDizFQXtR8Kk+eElbFB1Ib3gaeiE7Gak5HKlKYyJO/1fFa46YGjb3QgjtF86sKGh23gfFSkGLUat9z58vTBrEgXgoNqWVr33jKXlf/SkLZSicZnjGHUfrbG6SKvZeCE2V5zYM8z6HRaMDDhw/DDhUegH38+DH0ej1oNBpLwYcU3rOCNWXqwxWwR6bwb7T0Fp2xPi8SmLfSacFFjb4YuLMg6QvPmFqFDOdjtNPpQKPRgFartSQ7eVtTWqx+kPox1m8Sb/F2SWmHmMFIx7/kOEp1p9+hs4N/cwfIwytVGLA8PR1zWkBWkn24YwVXgmM6Hki2jgHHtO12G1qtFrRaLQCApZ2smmNryQx8hpNz6Dhb8pvzk+bEpox3yzHX+Jt/gxPbfLI+1d4oKtNXba9aPEt3Ms5mM+j1etBsNqHRaFyb5MEJf3oMdgqtXttA4jVeDtKCvIf95ykHgWMKg7uSncTL1ejV3kl5YPCKBp+8somXSWUcDQ7y8vBbbcxpdUkJ3qG8oX1TRk9qct4jMzQ7R9P5Gi976af9QfU22o+WrE3JX3pO28XSpxSW/RfzJ3geN+HTeuxXqnMXi0UI/pbtiyJ0UZqs51af4ftYHlq+FIvFAsbjMfR6PXj69CksFgt46623oNfrhcWe/AhymqfEJ5TPYzyh0aXZllJ6fvx8ar8ijaPRCKbTKTx79ixM9g0Gg6Wj5bWAPi8rRV6m+uNF4OG3mH7zPOf0W3nG7E6+IBcn+1N9jdgzza6rQmdZ+aToUy9NVdHvwSr5lNovVJdubm7Cq6++Cp1OB7Isg0ePHsHTp0+Xvrf8e0mWUFjvYzZ/Ub7U6JXS8rJoffliFYlG6W8JqbYsXUCEfiYuasOTmLR+4PWQypRsM0vGpNCfIt+q8sdifo2G1PJT7D3+jYef123v3VYUiWFY8PCv9E6zyWJlefJeNW4LL0UnY28LoRJSVpx4lFaKEIzlxY+WWKUDxmHV9Tb3pwSPIRND7NiMKgOPZXGbaNFQhIe8Ar1o/ho8q/XKItUgXiwWsL29DX/wB38Am5ubIQ8anP2v//W/wvvvvw/b29thwpauauRGlcf5w7xjtEn5S4sZtKOTNVBHN2WCqgrEArMpDkMVKMPrVctwqW0ePHgA9+7dg5OTk6Xd2LgogK9sz7IXx+/RCRatvBikXWraSlmKWBBGckDwb9xhxQNF0l2v9BSOLMvCnVc0OI2LKKydDRrdHkdJchitvDWe4+Mb37/xxhuwu7sLP/rRj0JwkOZFxzE6wBSTyQSePHkCzWYTWq0W3L17F9566y14+vQpnJ2dqTtAKR1W0A4DZkgLtrsWzKB9YPFPkfHPy8X24HzMj8ZaLBbQ6XSg0+nAeDxeOnKZ96+0c3GVjruWf5X5np+fB3pHoxE8evQI9vf34d69eyENHj8r8Snve2+5Eo/EgibU4cS7w5CnWq0W5Hke7n312gS4e5Ee841lSvIGn2NbYCAq1ud059N8PofDw8Owg4zylpQPtjkda5gOd4TgHcDSorHPClLGAuUVL19yOe3JnwInDXDRy+bmJmRZFoLGfBED/S7FhvPQhmnxH/IO3fmLaXlQONVPv60+LR1TtA1wx/9tAZWfmg4FSG9njafoQqyjoyP4oz/6I/jiF78I/+pf/Sv4y7/8S/jrv/5raLfbUK/Xw+Qkp4PqVlzMgju/ubzWeA2/5fEFKlux33CnFKUB7d0sy4LeTvUDcYcsAMD3vvc9yLIs8Eer1Qq6T4IWF/HES6x2KgNN3km6jD6X6I/5a5xvpeNcud7EZ5Zcpvdh4re7u7vXTlaqSsd9FnVlKqpsTw9o/y8Wi2DPURlC+anZbMIv/dIvhXH/V3/1V/Dtb397ic+kq1KkKygkecO/wXeeevA88bcmE6x8pLRabATlHS6wjeVb5YQitluz2Qzjstlswuuvvx5ORXn69Cm899574Tv0x3k/oQ7gchB5gPq5Wj1S4xFVoEiMRZLz3K/8XB693CgTe0v1p6vEunXAbUPld8bGFEBRBcGfWUamF2UcDs6smnHHHQAPo5dlyNvqnEqw+IH+XtVATckzJViyalqqghaclNJwlGkPOl40g9KTB8W6hLknGETpQCNwMpksOYJ0Vyym+eijj+DJkydwcXER8paO1OJjw1N3qb+8gcIyATBtHHsmFqR0Rcrn7SP9LQVrUoOUZcfCTQEdjv39fbh//z7s7+/D9vY25PnVkV1HR0fi8UaUN/BIL3R2UgMslBYpfy0vb9un9BEG4SSaPY6OxNveY5fwmVUOT6Plb9lQFBjcxKAm7oymAXzP8VaSvcNlPZYjTeBKdZX0g9S+ll3FearouI7xLw8OSn1LsVgsoNFowNbWVjjm7Pz8HObzufvuJQqPjOeTJR49mqozJLro8X64e5Hme3l5Cd/5zndga2sLtre34dmzZ9BsNmE+n8N4PFaD4vSf1taaDqX949U/NIiHu7im02lYhCJNbPIgDwbvpIlUr2/A6fbUA3ny5OQEZrOZOkFH8+MLp2j+sV1Dmu7W+EuThVZ6TlOv14ONjQ0YjUaFjuTzBKZSZIhEK5fhmJ91h7ZFJ/7O86sFAefn52GHOW0DKpe0+mgyl9IsyXg+lqgc5BNqXt9BCip7/UKvbWvRUYUvTttmPp/DcDi8tviiSL4SUm1lr+1Zxr6l31E7BYPoFxcX8OTJE/je974Hp6enS6dn1Go1aDQasLe3BwAAg8EAZrNZmKCkuxixTa2+jMkeTiuvM7dlUKZyPWS1FW9zTMd3eXltGf5c0/cWJHslRS9Z+Wp0UX1olVWEpz32Ck/Dd8TSo/vH4/G1ndcSyo4T6duiMsKTD+8bjf6y9t9tA6+DdKrOYDCAk5MT+P73vw97e3vw+uuvw9bWFty9exe+8pWvwL/8l/8SfvjDH8L3vvc9AEhroxTfTkvHZZEFiW9j/olHNwO8mJTVjvSN0eTR55R+Wg49op7aobjI9dVXXw2xtbOzMxgOh2GsS/Rpvzldlo6I1Zum8eiLKsB5hbc594W0PDB9kfKtb2mfAFyfIP4c1cBr72FaDkuHVIEU2yWm37Qx6yn3JlD5ZOw6oAkNblCsEh6nUHJaAeTA0E8zNKVctj+tgZ2a56oVQ0pw5zZhVQJO4gmPwVPUsCkTGJFAy8eVepPJRAwO5nke7oV977334C//8i+XaJKUDB0bUp29ddG+1cZiLC9qYKa0p5S+ivHA+SeWZ5H7PcogxThaJXBy4N69e/Abv/Eb4XmtVoNOpwMff/xxWD2MoAZ0ll0tLmg0GmHRAUcVPEHf0TSpAYxYEBeDMlmWLa2I177H8qiDi/9igTVt7KU4dB5o8oG24Ww2g263C9vb2+EeJQS9Y4d/j7CcKNzZ77lzKwW07fG314gv4gzz9LQPKC2xfLE9Wq0W3LlzByaTCUwmE7i4uID5fG7eHWtBsju1QCj+rQWIqwTyFy7Y4Lrw6OgI/uzP/iz8xh3DuHNU2h0KcL2tpf6lwZdYIJXTLPUvwFX/4c5euotG2smFdNJ8cRdtKqTAGuV5Hlih7zCg/PjxY8iyqx39Eq20DJ4XzbNer4fdJDwfbLcUp17zmbQ2kO79Oz4+hsViEcZPir+l0ayNl5RggQbtONIiPhAuEHj27BlsbW1Bp9O5diqDdqcjl4Wa/uL8YO3YxyO8qcz3tIvVtik7eW8SUh2m0ylcXFwE/rxJ3JTtiXyAd66en5/D+fk5vPfee9But6Hb7cJgMIDpdBp2eH/5y18GAIDHjx/DxcUFPH36NNib9I57D29ofgaXO5JOpGOBHpkv7YDzAstttVpRe5fTmBpL4vKby5gqguCa/vPQVaZsrR95GZrPW6/Xw6Iq5K3pdBoWUGTZi7tFUycmboOPVwa8vquQvzF9uirQsU7HEy6KfPz4MXS7Xfjd3/1d+MIXvhAWLP/mb/4m/Mf/+B/DZCyC2qdV39FJ4R37lo9Zhi7Mg9q+VfC5ZIPic1oXtD/RhsXJ2DzPYTqdQrfbDYt4AAB+8IMfwPn5OWxubi75A/y6Ds2WuSn+pDRQpNDCbbn/H3v/9iNZctyH43G67tXV15nZneHu7HJXK1K8mKQl0aApQ/rKgB/kBxuCYL/4n/Af4n/Aj4YBww+GHwwLgmXJpixLMCVZEmmullzu7IWzs3PtW1XX/fZ76F/kREVHREaec6q6Z3c+wGC6zsmTGZkZGbe8aXYdL2tT9ZUWZks26kvo8PKIx+6T5P06ddi6xtZVjtlUrH0ydtNGiCd4ZQUHPIY8DwZphhkXJpJz/aIwSh7QwC0NvGGgm6aT/qa/uRL2oKiREQsE0bR58s9DixUkkuiLlZnSRp5xpH1jpbXqyL9LyQefWe2d6kxpwVzJgEFDUQu6YCCx1WoFQ7LZbEKn0wlpptNpWHmIu6WovLEChzFFpAUaJNkmrRyl/YXfeFYcxoJs0k6cPEFPi185rVq/eiE5DpRuL/3r1gdS/hJ/jkajsBstdkdjrVaDarUaeJg7ovxowjy6j6bld7GmgvYN73tufEo8FOMV5GEaoEsNSFnf5TGmrfJonvQetJ2dHdjd3YUHDx7AcrmEs7OzaB0QOJbPzs7g6OgIHj58CL1eL7yjjnMeSOMN/7cCilJ/SjaH1sax9qR0SXyllQnwvF3q9boqAy2+oOXHAo8SOO1lQHPst7e3oVqtwuHhISyXS3j27BlMJhMYDoeX0kv9mWqDaRNBnAe5LcoXDnCZEAvkaOXwv8uwzzAfynvaGKMT/ZbdhLJM4y1cxCPV0WMvcrtD2hkqyWLpSEpaN20xRBF5qvkpsT6K2aNU7kqQ9EIMWlsjP8f8CC67NH9X4y+uA/G3ZGtwGafZfZbuLeqTSfkUzYtjnYF5LyydK9loFGXTjPyMO5uWy8uTiziR/9FHH0Gn04F33nkHHj16BI8ePQq7ZnFRk7Yz1uInCskP1fweTIML9rTFh3mBdeJj3jv2NFCdgHXj9j21NYr0uVZ/j+zkMs/S/7SNrPbQ8qJ8X6lUoNFoBL329ttvw8HBQZiY/clPfgKTyST44an3xGu0bQIevqDQeEJacIrp6GIITV9YtHDfbNOgY6LX6wUfuF6vw9/8zd/AkydPYHd3F27cuAFf+tKX4O7du/CP/tE/CnLr3XffhbOzM2g2myvjTLumRqprzG5KteW1fLCtrR2yluyT0uTRw5JfY/EHp5eOXRyrAAC7u7vwyiuvhPwePHgAT58+hclkEnRHlmWXFotp5a6LH2PjEv/nbZRCkza+rTGpydUUO9Sih4OW5TlV6osMzRdBaDYPf6/lSdPxceahx1Mm1y+abtbotfJPHa+etGX6AhIKTcbGDCkrbZ5BlvqNJxiQmkfMcbbK8XwrlXndBJJl/PJgg3SEmbQ7ULqTUiq3KDyDOw+veseCxyBNLcMLSYCnCDCuhD3CKbXtNGMvr/zwOgBFFb/0baVSgWazqb5HowMNd4CLXbR4Z9ByuYTz8/MwIQawuvPO69RKillrF834wv9jbUS/5w5BzABIGReePsX/efncCLXuy7MMBE6H93sJKbq0TFDnhmI8HofJsxifYQCDtrlV79h4j5VJJ2MtXWTlqcEzFmL0owNOHbxYm/D33Pnizz0GspQ+Vm96VFun04HDw0PY3d2F6XQKvV7PdFhpGbgDr9vtQrvdhidPnsD5+Xl4RxeV5IXXUfDkw2HJxhTaUuqIu8jwaF489pYGwiQaNdrx7xQapONEy5RB2Cbb29vQbrfhrbfegsViAdPpFM7Pz1cmYzXZytvEskWl77mNKuk6yQbn3wHApWCs9L0VrJL0MUcRp1KzE/hOLk3uaLYRvqOBRpQdefkFdZE0acXb07IJccLPu8vLI0PzfJvSb9wmoXl421OytyTejh2FzGUX5X3Nf40FcqT6SO8sWawdl11Uj1jwtL9XR6BdEJNb64AmdzgkO2SdNGXZ85NHpLZGO+L+/fvwyiuvwG/+5m8GvwkXL00mk2CzUNlF6efP8Z0mfyWdQd8ByHEMrUzepjG7BeuDaSwfRAMtQ5ID2I5WPdftf2iQ6ivxIrdvYz5HrB23trag0WjAaDSC6XQKd+/ehXfeeQe2t7eh3+/DT3/603BkMU6YUzq0fPNiXe1vtadUPpdZ+DetP/9e0wkxurx2SUq+sTJ5HnjCRL/fD5N3P/nJT+D4+Bhef/11mEwmcOfOHbh9+zZ897vfBYALefDxxx/D8fFxOBoXAFbuH46Vaz3X2tgbg9DK0eSD17bRdFmKbyal8T6jdt/e3l7YvX779m145513gp3653/+5wAA4QQSjE3TBcieutLnRevogTW2UnQDl5VS2hSbRPNLpfw99oTEU5oN+0WEpo8tHtD8Py1/DTGb0dIlMb/Wqzfz+MFl2tgeO7+IzbS2nbHrMCI8QmQT8DC3dr+e9OxFEzBWW1MjBMB37KenLzfVRuss56ocGw4u/Mqqc4qBHAsEWWmkPLzGkKeueRxf7VuOarUKX/va12B/fx+ePn0K8/kcDg4OQhloGI7HY5jNZiHQgKv4Usriq7ox8CEFGrQdrV7E+k3Kl9/9x6G1u7c/LCeFG3gpRrWnXEmmpfLeuuUF3cmKGI1GcHx8HO7k+vjjj+H09BRGo1EIgPF6YGAfeZbfg2i1fQzL5TLcrYyBTPzfug8wjxEWM/ql4BbekyvxENJqQeMLLaiIf2vGtpSHVQ4F9u94PIbxeAyDwQAmkwlsb2/DdDqFJ0+ehHScJikvAAjf/cVf/AVMJpPAIxJijgMfzzRARB03+k5rF40nY85Ayjj23qGEvPz06VPo9/tR21EKUFH6Y/ZpzHnmgbeysFw+PxJsZ2cHbt26Bf/gH/wDmM1m8OzZM1gsFnB6ehpWrQNAmJCW6NfGq6V/pLrjO+v0By0/Pha0smPjXKLHCmogvZQGL2j+1FbXAiYazy6Xy3CENK1jzOHX6iwFimiemk6l6fIckUnTefqI027l6dU/y+Xzuyfz3HNLgX0yGAygUqnAZDIJ40nrW6398Tm/F5PLCEkWUeDuR2uCnMpuBLU3PPfpXhcfC0Ftbm28luWDSeOK6wLLZ4qN3U1gMpkEmV+pVIK+WC4vjnW/f/8+tFot+L3f+z24d+8evP/+++FUlvF4fGk3Nn4LcPkI49iYjwURY8AxTe81BNAnZ+iiFgAIu349MRSuhySdGdNlnwdofarZEPQ9XqFAdSJeq/Hmm2/C1tYWfP/734eHDx/Ce++9B8vlMqmPvLRyujYl07SyuGxHvgbQbUnLxqbg48zymzfVFpYc7Ha78L/+1/+C119/HXq9Hjx8+BCOjo4AAMICSim/2D3QFKl3r2p1QHA9jf9Lf1vg+RQ50rwsnTcYDGA8HkOr1YLhcAjvvfcetFot2NvbC5saEHfu3IGvfOUrAHDhVzx8+HBF5tIYgxcaT143OwTBfSav/Vo0FhcDyll+giYvax1+6YuCFFuOPo/J4RgPxKD5h2WA1rMsmVEG1jHu135McUrHe/PTgt08XRlClYMzhlSO9MwKrr6I0OinjgcaIR4FEHMSX3RYPEsVHg8IrqvuknGWEriKOTeesjk4b9AAkdRWUp6x9oopp5hiSQ3McYO3UqnAq6++Cru7uzAYDKDVaoX3dNU8TsRaxon2THrPj0zlAT/tWBBPe/J0KTxLx4XGV97219LEgn88jYeXLMPAa7iWrRtTId1zhTvUsH4nJyfw9OnTkF4D5VlpHNO/Y7KGO3yoUzDoMZ1Ow99S0I3rEq+jyWn1gAf+eD01ftacZYmmssDp5O2FdIzHY5hOpzAajcKRbLgYhAbjPZjP53B+fg6np6fhW5zAthCTdVL7Ib/QNJIDE+tr673Ga9pY1uwd+hyd0H6/D6enp9BqtUIA36LJE9yy0lnPrGNg8wLtm+XyIojZbrfhtddeC7uw+/1+KLter4cj+q3yrbFP7Qft27x6i9aJf6sFNDldVj7cLovlaUEqz2PvaTYefY5BFC4XUmwV3k/L5epxpTE6efuk2gs8XUpfWnlptqqWll7TwGlI4U0MCo9GIwC4mNShkzVcB2nyQeMRzZex5GaWZSvBTq1N+EkSlJaY7a/9tuDhcc+3VltZKOrr0Tah7RXzIzUdoekrKa2WF3+eUi+06/AYdcoP8/kcjo6O4LXXXoNvfOMb0O124b333gtXwaDtqdVbexfj29R+4X2CvE/rqNUdQe/Q48fkW0j1S2I+TizNplAGDZY8Xi4v76xGn2Y6ncL+/j60Wi146623oFKpwPvvvx98E8n288p86fdVwqKJymFeR02exMaW9ZuXa+Ufq48nLaVV0o3L5RKGwyF8+OGHMBwOYXd3F7rdLpydnQV7ne4Upm1k2XFe+r1pvH5NEXuX65i8eRWhBeD5NV6NRgOm0yk8ffoU2u02VKtVGA6HMJ1Ogy7Z39+H27dvh3588OBBmIzF05ryyHuJfou3yx7nee0dyzeleXsWweWlR6KFjiOaH+pRj+/yRYDH5tTSpPqO3vSWrE4pj+ZH/887dlJ93atAocnYVMFVtMJXOQCzLBOD10VwHRigLOBuBh5opTwyn8+DwyQB25Z+n3fVYQxFhUae8iyFbDnBqXTFxiXyMuZNnRDrm5jT7aE31Zm0FG9epb8p4MQRTm7g3SMU5+fn8P7778OzZ8/g008/DfeN9Hq9lWMqJfrLkL1lyzRv2dTgajabKytttSN9LLkhOWf0mRS0kuhLaYs87cads6I0FAG2Mx7bA3DBjw8ePFgJcAEANBqNECDiTthsNgt362C/arIVg800UKUB0+IxyBj4ODk5WQnYad9aoEEF+kzrHw58R+uC453mz78pQ/5wvuELK2J0cxpo8J8GDT/66CN48uTJJb3tCdjSdHj0Lj/WzaqXlSfPn9LtNbppn3nkikaz1zGUxjc9BeHk5AQmk0koM2bzaPKC86X2ngNtAl6udDRoXrmHRwBubW3B/fv34fT0FP7ZP/tnsLOzA9/61rfg3r178OGHHwanG3lFOs1Byl/qK6SXB31ofjwIJ6XX6p3ipGp9K41JzfmM0cD7WhtnqbtpOegpCTzwqCEWsEX+wzaSdrh5bFsEjgF6DKpHz3rldNk6G/NrNpuwXC5hNBrlslXwdCJvv2j3uGpjStPtUjmLxSLYEACrk0w8AEfL5X/T/IsGZspECg2cD5fLJTSbTajVajAajQrviPaMfc1OzrIsLKzw6vcyQO1B5A0qJ5G2RqMB8/kcfvKTn8BsNoNvfvObIY/pdBoW6FE5XeTeOa4fvLKNjxeU9Zy2GKgc1Oij5abYgDyfq/A91g3ND+TgMgZtVLxi6KOPPoKTkxP49V//dTg4OIDvf//78Eu/9EsAAPDpp5/CT37yE6jVatBqtVZ4MA+daI9eR1BdItkOKYv3JJ0m2Vxafl6bO296pBG/w4WoJycnAABh0u/DDz8Md1V/9atfhdu3b4fy+v0+VCoV6HQ6UK1Wod1uh3zx9CiEdUWbFv+U+sAj5zRb02Nn5uFNru+1k0tSZRAfN3h0eKPRCM8fPHgAZ2dn8M4778Bbb70Fv/IrvwJf/vKX4ejoCI6OjuC9995bsU2Q3phfyOnwtN11BNYR6yzpSy2OURZwoTfm+/rrr0O9Xoef/vSnK1fWIC0om/kcwUv4cZ3sZ44XZeysA67JWMshjMFSrCkGo/ebshhMKkcLnkvPUoPEmxgYeYySWH4IDHrwyR1vcIj/Tf9flzKI9WfZsMrzBuPKpkMrX/qG05Ri1Fn5p9QxFmSwsO4+1oLi9Fiq5XIZggc4QdHv9+Hk5AQePXoUAiLcGCx77Eo0YznWc/6d5ehq0Ay8PDI1FjiNOeLr5AlP0FhLswmjBOtPHUO8G7Rer0O1Wg0LNVC2S3IL7zTW2pM62zHH2gqQ82CmV8dKvGzRKn3Hn0sy0FM/jcbUAAYNFnjHkpaXRv9gMAi8QSfYpcA5z5uXj/yRZxJI0zveSRNvuhQaKGIy0JJhGATHIxa1caLlh785r0o0eYMc65KLOIar1SqcnZ0BAITFC3t7e9DpdFbSa2POyt9Dg7dutA2tQLpnzMf0t8QjKfXN01+WrLXaibaHZKOUZYul+pll2PNl0u8th7ZblmWl3KfN/SgrnRYQ1fg4BcvlcmWCgk62Udsjj4z22n78G2/eKflq31v+PrZv0YURKeVqoDJf0x/4LrWvPDEbzq9c1+Ou6l6vB71eD87Pz2E6nQb9qdl8Un5WbCEGy7aUbDKuS3m7Su0s2ZYpvO4d+7E6rdM3SsU6Yj4IyhNUXlUqFRgMBgAAcHR0BLu7u9DpdODg4ABu374NvV4P5vN52ISAvnsM62zvmL9cRjmazW+VwfnXK1c020rzGz1jxKMTOa1ZloXF8ZPJBEajEfR6vZCm1+vB7u5u4B2+GJIveEL6i/S9Zb95/T9LFnr6NUaTZjdrPjT+9tryCLroZT6fw2g0gvF4DKenp3BwcAC3bt2Cra0taDab0G63YTwew+7uLiyXyzDGsVy++FOLH/BnUlqtTaRvU9rAw8uePDSfXOsb7ZlGi4df8B8u1m02m9BsNlfyoBs0LNu0LD2xzjzLRgqNmq3Pn/H8tXy8ZVjpaNpU2RyDN9888MpYL0o9pvg6M23MMCmLdskYkxTSVRi66+wffi+KtrKIBvTpqikODEpIdzCkwjPAJZQR/NO+SREqeZxGWhaCt7fkxHohBeFouR7B7g2e8fHjaQ++KGCT421rayvcJYOrZRHz+Rw+/vhjODg4gK9//ethVRii2+1Ct9uFLMtW3lnGYV4aMV9ahgSpbM0A0IIC2GeSfMRn9O65VOQd4/R9EefIci5eFGRZBoPBIBxviM/o8T3SHY78OFHa37EgmQe4oIHvqMkbSOPfag6jBJ6O74wFeC57ygqyWG3IgxNauR7ZgTuc6XeLxQK63e5K+1t1wvLpBD/dHVVUduUZp7R/uCMn6Qgt0IP/e44NTJEFw+EwjDl63JmnraQgFg0o8vbi9Fn5oqNOeTsPaHviCSq4CxLvbaIOON53yccr0sB3QZelF/m4ovlqK+b52NfqT9PSRSUxp1azYSy+84wN2r88HZ5uw4/9lGTMS/hgHTmKwSbsj+3t7bBILyXwhXnhe08f4ZHgfLdOCrAcrh/wH+qD5XIJrVYrTDbTkyToKRdaoNMqO5YuT31S/DML3C7Ab2j+uGATba0y4a2HtLtS0oNl2rcoD6nO42Xy9nj06BH80R/9EXS7XWg0GjAcDoN9IsUTcCKXnsqB6VAWSydApNSBjgGutyT5iqfOSGOO08DtO5qGxlLob06f9k6yha87ygqQ03ahdhTuvkKMx2P4d//u38Hh4SH83u/9HjSbTXjttdfg+PgYACCc/FJmrOq6+pDUjqNy3XNXu8f3k+xUSR+kjs/Uca3pzyzLoNlshtOCED/72c/g3r17sFgswulnkm0F8Nyf5bKe2wbWIh3+LmbrYZ4aqM9uwZPGQgrdtExP+uXy4jSR4XAIT548Cc9HoxE8evQIAC749u7du7C9vQ2/8zu/A48ePYI//MM/hNlsBqPRCFqtFjSbTRgOhy6bXvqbt3Oq78W/yWvjeHm96IkcKaDxJICL+kwmk7D7/P79+1Cr1eArX/lKGEeDwQAePnwY6t7r9WAwGIincK6D3usMjw9J09LnRf36q0JRGcRxFbpW6jdxMlYLViE8ys0T5Offx4QaTZ/HIJOEYiwPzXnC762gsJS3t102gTxt6KWZtwtdNUjbVHK86PM8wrAsAWopwZjC5e9ivJxCSywfq1+LtI0ngJv6DdIUcwitwCsPSHtkEk2nfVvG+JTqNRgMoFKpwLNnz8IChtPTUxiNRmHy1lqokDdIJDn01n0Q3OFP5SmJTnTi+KIMrRyPk0F/e2WaxAtWG8TyTNF30riPBUzWIdMQtC/4sUhoJFNaJPC6WO3GZbtUP0mv0omQlPbIM45Tv9F4V+IL6VnMVtBAA3XS90X4Blejpi7akewiLqPLgqaHraAkpQefp7SVtx4xu5C+p8eyajR7YNmjvMwUmV+k7zTdvVxeTEINh0N4+PAhHB0dhXeak2i1R1F+57Yo1Zcep9fjv/C8vXSl6EF8xvW95sPQcvCZNGlITz/AI+HwXi7t7mxPnZbL5coCNvovVn+eD32eqiOksWeVLcnuGH9KZXH66ZUNvByP/sX+k9og7zjmNOA1ARKQT6QFJZS2lHK1d962j+Wbx2aUvkvRc8vlEqrVKjQaDdjb2ws7erxHJPLy88pnj2yReKksXS75rzxvtEG2trZgOp3Cs2fPViYzedxA0l8eP9PzjoL2tzbW6FgoIpMkmyUv3VaZ2rOyfJBUFBnnecuhNux8Pofj42MYj8dw7969cIIHHqOJ3+CpH3kXuVv+UFGk5untc86P3vFVFmLtHJOPHruOfotjmB6pjvqOTipJMhyvD8CFT96FjpoMiNHphRWP4OnyyJyyfT6eL7Yh9R3oRPlwOAxjdWtrC/r9fnQRDC7gieniFNtQA5c5nrRS3pbvayHPmE3Ro55xh/2HC0B3d3fD0d7VahWePn0a8qnVamGjSt6Y0IsKafznGZNFYzGa/cxtlBidqeWUAc2eWJeckiD1z6XJWOrAWytzvKspigZwrPeaARyjxwqSSJBW86Q4TPS764TUoBBCE67UqKBpUDlKA5gOXM+q4DIEbh4hkaJUaZ6e8+21vNc1dvKA3xMpIYXeokEiLkwtQ0Z7l2qIa3lL/CTde4oTkWdnZ3B2dga/+MUvLtG4tbWlBrk8xrJFJ+ZP06Ecx0lhuoNDarfUgALVE7VaDdrtNtTr9ZW7c3u93qVjSJDmGLicQSdHa0P+jWY4WOBpy1pdlqqXioC2AQbBMYiAQUKpDVPGqTctl5tcl+BzPMq1qB71fm8Z+dozKZiD72LOFm0vr2zitNKyPflQYIBhPp/DfD6Hfr8PAM+DCFL5+D8/frLsFZcxh4MGRb3gx2diPhL9ZQQDJZ1H78mMpaf0WZNfmu1uBcsw6KjRTQMdGixew/wlek9PT+E//+f/HO7Z0qDdb87HlkdH8XbAAAzA6l1KNH3s/j7+WxqjHt6h+VkLlaSyOb18lz4Hva8R0+AkB12Ig5Ol0+kURqMR7O/vw6uvvgqffvopDAYDqNfrl8rx6u/FYgHVahV2d3fDs263C+fn52H1u5QvHT90skPaoYmwgkPSuOK2CJezNA3PT8rfAqdfusMZ87J8VdxxynV4ql1F60zjAnSXId2ljmmq1eqlRV34Hu/BbbfbIT/kAYtPLT5fVxCuSHDTI38WiwVsb2/DjRs34ObNm5BlGfzoRz8KweLUcq2FlUXbiNs2ZYHylTU2kW9wB+KDBw/Ce+pjWz6SVobk46TWwSobbVce70gdh/ib5pFySocETgcf6zyfMseaN791xHw8WC6fnwQ0Ho+h1+vBf/yP/xG2t7fhzTffDHeIIprNJjQaDTg/Pxf9fw9d6xhjUjll5cVjW0XojsU3pPRlyLU8wCNva7VaOFmC7q7HyT+kEU+82N3dhZOTk5UjjtGvpbB0Ird1eX08dgbmQ/PUyvK0BcBl34CW4/EfNDpjoL4L0qtdm7RYLOD+/fsh77OzM5GmRqMB9Xpdnbi1ximtL5WnWvqrhmXH0ud5aedxDVy8ifF+yaa8c+cOHBwcAMDFSRj37t0L+W1vb0O73Ybj4+NwolSWZZdOGbyOba0hJsu08aml5enX3RYxHrpOuK40XpqM1YwDgMvK0upgr0JITet5Tt9JznNZBkMqTan5boJpyjBm6H2C1LnmzjwPosScbwwKl0EzF3aSYyYF9bx5SzRKAR3N+fHkmRfU2aVlegJEGk1asMrzLUILNErOsrXSX8s/ZaxafOiRM/wbWjbd6cAdGeRz7qin1EOix+IrPja14E3MibdolL7HgG673YZWqwXdbjfsrOHjPA//4y4DlBspckIyoDnKkscp7bopoEzmzgNC2s2SMubzGIWoW6hctoKyvCwrDU+n5ZsqIyT5roHKPU2OWPRa38RosMY35QVebh4U+Z7LRE/bWrwQ4w2PzvDysNZ+0nOqoyX9x2mgaaX6cgeYv5fAjzaOpefwpMWjsJGuDz74AAAgHDXJ8/HyYB56PflReUgdXN62Hv7hsjWFlyS6pDyk8YKTstL3+Izy0s2bN6HZbIbdZ9I3eDQjPyZMmxzw9iMuCOETqin6Q7LpUZd47/TD7+bzObRaLajX6+GeSst3sWS2RBNNg33Fj4rHNFmWrQQdad/nvXcUZT1dFEJlCv2/Wq3C9vZ2qH+/34fJZLKiozm9ND+L3+nYouXyNtO+5YuBPPX26PwY3angehvlYb1eDzs+cHddkTIoPHaOlY9mf5VtD0s+My2LvudHveexLyV5bvlz0njVxrdWL85PnjaUbMoyeVLLyyPLipa7CXhtBwqUxQg8Sns6nYZrWXCM4gIl66hofK7RksK/Hr0TK8+LmA1bRvlSGXiVBV2Ugsf7arrXooXmX8Sv4TJwPp+H08yk0yJoHAInbKvVKjSbTTHuodWD8lZMnnt0m+YnemSppaOtOljfSt+l9o9kfwBcHFN8dHQkLhYYDoeX2oBP7EqIyd8Um7UMOc5tSi2NVLbmK9JvYvXR/Gw+1jT7AWN2dHEf8ny73Ya7d++GI6gxn263eyn/PHbAdUAeHUWRoqs9dq+VLkZHTMfFvvcgr017nZF8Z6w2mPI0YoxhNmWsWUCjrCyjhv5OyfO6tAfAqiKqVCrQarXCcwQ9llhy4qQdENzIwaAdzzsvzdIzrlTzrO4rInyke3ryIGY48Pt88+5csgQ5dzjz5K19FzOgpb7UvsvjLKWCBslwZxkPVLZarUu7xmO8lyozpGfe/i97zKGs2Nvbg/39fTg6OoKjo6OQFnfXpOgSmrbZbMLu7i50u92wq48jZkBzw7wsWHy7DsckBTzAJb33OCg0fUrQTqofHTcA5e0+tmjKE3yK8ZOHDm/fxvggjw2mvee8kOpQlhkstEAnECW+S7E3pb/xt5cXioxTrsO00yi0HaIaTTx/aSU76gR69BpvvzL6czwehyPEFosF/OAHPwAAWDlWzII2RqUgktX3qXXx2CaxclGG0iPukGfLCCJIARHU91gGrkbH++2w3bIsC/dWfuUrX4HXXnsN/viP/xgGg0FYcU7HFx5TzO/v9dImPVsul+EOU6/8oW2O+fEdgpgv5unZ6ZBlWdhh+sorr8DNmzfho48+guFwGOwUDNDTPrT0ntZGOB4x8Iy2i8TrGKDGeuKYrdVqsFzKJ7JIQFro5DfWWdul02g04NVXX4VWqwXtdhs++OADePToUdiBi7vIaN7oB0rj1KLLCvhJspqe6lDGGFpHsIfzSJZlMBwOA893Oh1oNpswmUxCgL9sHZpHzlB9ZKXJY+dptHB5jvcMUxlBd9NYdFO5pfmo0k4qKk802R5rT+zvvHYB58OYn2jRo9GhjavrEm+6ClC5DvD8uhaJ38bjMYzH47CQIuWKhZQ+0fK4Cki+ch6ZogF3neJd6gAQJsJRD6aUqbVnKp9TXxjg+WIaakdRuYHvAC54aDKZQLVahZ2dHeh2u2b8T5JHWptLvo63bWI2i+ZTxWinf+OYwDikZ7FRURmENl+324Vut6ve0U0xm82CL8JtZwne+A2mXSeK+p55ZU2efuL8iosapHz29vbgu9/9LhwdHYUTMZbLJTx9+nRlh7lURooe/KIhRV4XjTsV9b83gU3FrDSIO2O5UqHvJMGSJwiYx3lPyZ/Dcu5oGs2Bloz5mPCyyiwj+LJJaHVdLpeiEE0xBGiAUILkIKfSCwBigBHzxX8eQR0Lynnp8gYcPLSsI3jgpUkqm8sJK42VliL16EUtL6utyjKckI8kx4xfPC+V55EdPJ3HOcEJLtzNAADhriou9/M4hPRbpKdSqcD29jY0Go2VtI1GA7a3t0MZPECs5U1/Y1tWq1XxmBKvkyIFU7V65UVe+UV3NVHHJk++mHfKN2Uarp5+RSD/YECXBkgs54i+swKIHuOrTLmq5eXhOylQ5u1HK/9YHpK+ssYCDxCUjZj+tHQB3e1r9aXW5rE09B1t1xQe49ACITE6rDIlvUttMMnGzbvzTisbQY9GR1o8PKnpCMxDQ2wsxGC1M9czfLxzucR3lXpo5OktvrR0HQXqTy1QhRO4tI/G4zGcnJyEnbNUNkt1suqD45IeQ093mWj8QOsoyRzpHW8Tq58o79MrVNB20torRb/SdNgOsQlVK+/ZbAadTgcODg7g9PR05ahnPrknyUd+ByetC/Ir3nHabreh2Wxe2nXtOXadLkygfUT1u1cn0n6iwfsUxGwKms6CZMfT9qO8WKlUYDKZhKMTW61W+C1d25EXXF7mtXnKtH+8kPgV5UNKjIHnZaWlfhD6cFIeXNZoKNJ/XlnC9Sd9hs/XRePnFZodO51O4dGjR+GITL64Km9ZMb87L3h+efKX5LPGa9J3see87Xg5kr61bCBLd1g60Eu/lo7SqNnTSAM/AYSmlWwViV7JL6RpU+W35F9b+lb63iqr7EXVMVDbAk9wlGwE1Meoo2m8IcYjRfUaz4vb0VIazR5OpU/zFyQ/U3pH+1uKC1iyZzabhTFAfYzhcAg//vGPYXd3F+7cuRP4rN/vX1rYR+nA/GgcUFpc+EXQdV6bx2NnW88s/9Lyf8tEmfnm1eFl0LAyGYuDnAbqEZKxR5/nJU4KbpTdaRK9WhlcGfJ3MUHJyysLKW2yTsbnwNXjqfTx+9rKNkAlUONHUsaxo5MleOjm7zZtkHAaUozRlLy9Tn5qUID+LxmGkqL3ODfSt7xORaAFpOhdbBz8zlZKGzecvGVS4FFozWZz5eilfr8PWfZ8B3VqQEYykDB9pVKBvb29S2Or2WyGydjFYgEnJyewXD5fBW+VA3DRVo1GIxxPLClSKRBFDV5PoE2rG81Pg4ePON9K72lgSCvDO5a9vOMJ4ORx+DRIfUV35Hp3c0tOML6LfesJHnh402sXWd/w9rdknVZXj9NGeVjjwVhQQKLRY2ul2g0SjZ7xJ8kACVp7UXmhBVB4Odr98JTPpDs7NbokftDqS7/TeIZ/QydG8BlOlqKDm2LXWzoV6aK7/JAGb6BBsgckSGNOGuux+/di/KzlK7U/lkfrEPue/y0hryNZrVbDYijO72gT4AkeW1tbMBqN4Pj4GCaTyUoAi9Pi0TX4/Wg0Wll4g6eJcL0A8LztsFxrcps/42OC2gNSG/L7rfhkLKVDa1+p3vQdrWPKKRQUuFOw0+nAL/3SL8HPf/5zOD09hVqttnIyizQuuXzhC8DovZw4GYs7OfH4RfzWmqTGPOlkLA18SsE2bdxxO047yjEPitr/li6nMn8ymcBwOIRPPvkEGo1G2F3M7+T1QpPxlm2s0Rmrj/U7FVIfW7ZYyrUmlk7UQPmJy2VJHlm6Lg8/er/nsovKRSk/3r7eOpSFvGOzjDGdmr9mQ08mE3j48GH4jfoB8ymDhpgfkmK/avCMNalstAEALttMFv2pZdBxJk1cWnWI2dLrwHK5FK/awHeSX6Tt8MV31gIqS45p7eLxyaz3sTJivplkg1j10Ox+aaxI45WWh4vSpNMz6CQe2lEenWf1M/8mZsdrulsqm8Oj2z3jj143qNGHf3vKlL7FtLPZbGUzRbVahcFgAMPhEP7mb/4Gtre34Vd/9VehXq8DwPPTNiUfUbKZsuz5CUB5cBU6p8h3Kf1BEbOHYnmkxgQ0GxPT0P+9eaai7L4t0n4I1zHFkhLB32UbbHkaSGNMqWNTGGcT8DgGefLcBDRlZP2WwA2OIsfoWqBCXAru4NFQ6yiPBrjob4Tk3JXFFzxQ5M3Xa+xJeVpOXh5QIR4zVLjTLEGTXXlkRQqWy6V6D5vXgYjVid+3BnBhoGBQLssy2Nvbg1qtBo8ePQp3VsVgKVGLluVyCefn57CzswM3b968VNZgMAgBPS9over1Ouzs7EC/34dutxvudSsylsvi37LGMToINEgEsB4dTBEzMuiYLEpHjK9T+gTbKfUY+NT+ssYEzYvfJ8SdNEmm8QAgRYzG1PbS8qT6Uru3Ow99XpowHZeTkmPrMbCRfjoB4KFHChLF0vFnvO2svDQdx/PV8uBlegNhNAgEAOEKCrwywjs5YNkyEs3azltN32tt5q0vb8+y9b7FI1JAgepLb3CKB594n0s8U+R6ChwzW1tbMJ1OodfrXbqbnfuIKcArG+h9fNbuC3wvyU/6DGmhE/60LgDPFx3wNs2yLByFhnabdW2Ndvx0CrRAF68PpuNjtlqtQrvdhm984xvw1a9+FT744AM4Pj4Wy8E25ztBpPIHgwE8fPgwHBdpHRFHy7DGqnYEbCw/bXxYAVcsl+bFnyHw9Bi8y8yDmE8kvcOjFLWTcoranhotHn3K8+JpyvSPNHtH0plSvbx6lJaH7yWe4ItApIkRSmPsBJayocl6Lhs8+XhtJss+pDRZ+edti3W0YUr+9MqtLHt+pY4lQ/Igbz5F/UyrH/PknfcbxHg8htlsBs1mc6WN+ckZ0l3h6+aVvMAFbEgrXXjGeQnrGNN3qcc1e+BtP4/Ph0d34zs8gWUwGETL0nwgD6w2azQa4rv5fG4uivX6CEV1dxn8m5KHZFduAqg36YQptYPoaTQUyO937tyBvb09uH///sqVhph3GfStE2XyR5njf53IG/Mpml9Z360T7jtjLUVnCU1PpWMGcx5YCipF2RQxtrzK5DoyBsKqt+YUxb612nS59N975KGRGjroVEn0c6fbUsgpQQP8Bo0rjWbJsE81aK0AnrRrQaI5BVo/SgGYlHHE5Qf/JznQnsCH9czjSJYFaYJIKidF6WryDg0enIzFMdBut8OdLLhCDR0EDRqdEmjgc7m82PXS6XSg0+lcSjuZTMKumzw8iBOy/X5/5egoqU0kPrT4mCKFd72Gu5Uv1z+z2Uw8YrpM5Gl/LUBVFmiQmDsMmqz08KrHPoj1uaXTacDZSpMa5ErVCx7Zq9FAv83bnnmh6RGJFq3vtTHtCeCk8AFN77GBYmVZ8oPaWjEezjOe6S6EZrMJABeBpOVy6b7LM8XuTw1qSumxLbRV01IeMZ4uQw5o+XDZ7hnjFi9LeWNf0vyLLFCi/UXveJJOm8mjy5E2uuMU/4/xusdWorskuI3JZQH9ezQahaNjY7IX2yOml6ktq73nNEmLYaS/cafBzZs3odPpwJMnT+Dk5ORSvbCN+LF9WltOJhMYj8dQqVSgWq1eCoBJ39C6SO/zBlw1+YYBejp5gv+n8CO2Yeq9rZo9hH/z39ppTevwO7y+fN78itBM+drabee1yS3aLLlKaeB+Z0o+PG0qLHskVg7VbUVsMk3OpNj46+DjTUOyx1DOoKyXdmp76x4bl2XZ1ZaNFbO/YvpV4kErv1gZuDibT5zRvrCO+N0UUsrCWIt22od0ZD+1E6Rxz2Wj12b1tJVln2r58XRoK2B+9XodptMpDAaDFRkl2bCcllQdTscOzY/unETgGPaWoaUrwns0z9j4SdE1KX5/HrtdyjOWB60f90lo/9B74fkd8Ts7O9BqteDhw4eXbNGyYxEvKrw2X2qsI6VsjRck3WbZemXSZiGP7ioDK5Ox0vHEMbzoTF+W0VQk7YuK2LFuHFrQrWzjiRsPtVpNDNJtgteRFo9Bk7cMCykCzXJ0aWDPyiNWtte5sHYxS/SlBnauk5Oo8UZK4EF6R4+7wyN99/f3w11fdPcJHpE2Go3Can2kIcUhlOTpYrGA8XgMw+FwZTfFYrFIPt4J82+324FH5vM5DIfD4LwhBoNB2LnjHfspumAdPMQdhOVyqR6rQ/sJj7f7vIHLII+Rz4NnkqPK88a0ZUILzEp0xGRvSpkWvE64h24r/7KhyaEUuYGyAI9nx+coHwGe6x3PpJVl/3j1vcSX2lUKKaDjgE5MYF0tmZ5lGXznO9+BV199FU5PT6HX68GPf/xjmM1mUKvVVJmk5eV5RmlGB10ax2XB0mv4TtoNBbA6XmmgyWvnWRPGUhAdf6N/RneHAPh2K/PAJV8YSOluNBpBv+K3V6VbKA9TOcTtQ2mnMf5/lbYetisPfNAxznlG+o3faf1ATxY5Pz+H+/fvw2QyCVdQjMfjMGbpsdJUTuTpYxowo3f88jpwPUyP6kV5ol0T4/Wb6I5zKZCs6TEpAJqX32k7Yn7YL5pOWS6X0O12V/ikrDu6KQ3rtneKQJJ7nm/o/6nlefKl+hLB+/Uq2tFbZlG9iWOJ20wAq7uHv2jQZLQF62SZdaGMsqh85DYv7hAGWL2yzMt3XPYiUJ4PBoOgJ6iOQdBTKpBHr6KdY6C00LEEcNnXo/WIIaar6Pgsw4aTbCwLdAINdX21WoX9/X2YTCbheixvvqnyhtrl9Do9+j+/YxTLpzp4k1fLle3reJHi55cFq6zRaATvvfceAFyOZVKexg0YCC6j6HUnL3G18Pr1VzUGrhIrk7H0vHCpIVIDYbGAnycPD7x5pAaGPGVxx++LCkmQp7Stx7HyOuVSedyI0wI3Mfr4916aPIZ7Cv/kVZgpwWteHjWyU4LNND/pOxqk4eXRPFLqYI3LF32ceusj9WGj0Vi5p5X2K3+Wt50kXsedJXgUOP5OCRBRQxwdNDSYMS90ZPiYlpwSLSjnrZ8UZE2B1b5Z9vyuPusITxrkjOVXFLzdJCfK238pZXJnldJCwXmZf5MXvG3LNBSluknPqdyN0ZQiLz062qJLen5V8jXm0HtsDO4k5GlTz3spT67vyt71HhtH9O+DgwO4c+dOeM4nOWj7pIy3mL3M+VrjNw80O4PnlzeYT+UNBiqtoA3lJYtXLdkm7Z6wyqLfS+8wLzqxQI/E97S5Jddpf+axPTVbXJogkfrTyzMSD9P8UnwDTOflYz6ONBlvlUvzmE6n0O12wxGPdCJW+t6SbbyNkVfoDh/t+FbLV8b39C5bnlbKg9LhSashxhcx3Sq1Jd/lKi140MrFxXTI13knY4v6lpbc8OrXsn3TGO1eGeVJa9kxHhvbYxeUYY/Gyigrf8mm1uppjY0y/I/rBCmO4f0utS28/oaVLtZnXjq4XUzz8PIJL1ezM5bLZTiNgvq5Ft2UBzW71NOWXB+ntJNH1nhoibVlGT6IRYulCzSaeF6oB2n/4SJyze7x0pw6/iQ7TmvXWF3LkOEeXeItJ4+/WQby2tcIq/8XiwX0er1gbyIsO0vK5zrpniK60GObpYwJTU6l6oQ85abaENcVsTiK1JZa+146pphmrq38kxww/v0mB8BVCSIJViCFC46yVr++qOBGmHe3RSzPWB603efzeTgiFZFHyUvwrqZaJ29yGqSxjM9SdjpLq49SaIkZItrYQIOqKJ9YgajU/qDK5SoUP5bJV4xRTCYTOD8/h1u3boU7W7PsYofpYDCAk5MTMXiUd0Ug3fHAcXp6CsfHxyv3lHnurEWasyyDfr8PtVoNbty4cYlXxuNxmPTdhIz1OF1eYJttbW3BV7/6VdjZ2YHt7e1Qj4cPH8K7774LjUbj0p1meRyUvMAjryliu3Pzjg1rbOE7uks4T/5eGjisMc/lFJU5mjOIz607Ezk8afIESrkt5w1irAN5gsmeuvJdfx4ZrvEBDxbRdNhHlo6t1+vhXu/FYgGnp6crdw2n2CiWba4d7wYA4T5IbSI4xb6ynEcarKHvsK7WkWHUXsGxRNsXv4s5mZSveVCA/8P8JHsKZQ8eQ6fxYOpJMjx4hGXRKwAwnZQvpZfWgQdU5/N5mLDLsgw++eSTwHutVgvG4/ElBxptPl625TNK9aNpy7DrLND88X+0EfA47uFwCACwMimGi8hona0gUpZl4b4rOmnJy7Z4i/YV/Ya3K+eBWq0Gg8EAPv74Y7h//z5UKhWYTCbiriJvm3F6zs/P4fz8/NIpAnT3Am9zyneYFk9kQRsmNiYoDVTvAzwf9/Q0Efod51Me5+DyguZF60HLQ37AxQv1ej3w0XJ5ccdu7ModzB8X3pUNqe3zfM+xDltTGv98nPG+2traCidfjcfjJJ9bol/6nspbai/Q8UTHMOWZIoHO6wQcn5K9hBNmeL883gFo2ZypvvJV+dYW8L5tzc7JY3OvE94xy9Nx3Q/w3D6j8g35g8pouisNv+dXhXGbBcvk/ygtki3JkeoL8DbI+61FlyUPLHvZUw7XUfx9ik2WCsnfQftnOp2GeEW73V6Rm3RXKsr3oicDSTrESovyKhbHQfm+qfEs9eM67WNaDkB5cqtoPltbW1Cv18Xj4PE9AKycHAQAYSxcF/kLUI4u/DwgRTZ6407XxZ6Q4jQ0RqFhxQNICfBojZmqQIqk9Thv3rSxMlMEYcw4877LA4nJymC8FEPBM9D48zIGHA92xGjC55IDVrQ/rO81gUzfbULparTwdzx4obWvVa+ikIzzFB60+r+o4U3bKQbJseHl5+FjLWBC/6d8jve0Yt9qOyeKGGZcHywWCxgOhzAajWAymajpYsAxS508/izlCDFrzJUhOz1OEO0r7As8LvLw8DAocjzOjjtgNB8+Pso2OnigmD/3jM+UsqyglscRp+mL0iLlHxsjXI5KclWCxo/aogtPgMKiz/tce1eGrKdtmaoTpQCSlC+H1W5F6mQFv6R3aLDjXYUSfZSmWJAhLy3L5TLcCSqV760Pp0MLuuH/kryiwTdOI/72Lrjx6DJedkyWWX4BPqfBJ54ntwdS5GYeW0B7Ttum3+8HHgBYPbqtLF2i8UWe76U+sPhFek5PheJ08eM4tT7CMheLRVhUgd/jWKJlYlrt2Go+Hii/S3yDaefz+aWdBFznxPwTrT0x2IWLROjY88hn7kdoOtAjd7k+tfgg1b5E/ud2lmYjYxmehSjWbyl/TT/F6sR5JNXWiJVZxFf1jKcU2qyj9z32TRm2i0WfRQem8/ZnKorKbMo/Ul4YBNdkbBk0pfjWRdOkwMs3RfXnuvpdo8vSb7Sf2+02ZNnFAh1LnvM8U+vj8fMsfzeln/L2UV7+i9mjlo9n2YAePY/5SHR66cP0MfuenobG7S0uO1L6oKgcj+lq6d0mdEfZkMa6xCOWzUGRR/9L/gYvk/M18jIuRNXkCLcFpbpsCinlFpE3PA9tPJbNoyn5SXyntU9MFmmw/CdPeulbD3+XIW8QK5OxnmPRyjo7PVX4ap1UlOE9DmRMGMXy4wLE2plg0eGpq+R8lmWMe8rO804qLyWgxAW5BjpBQwMtqdCcfi3ti4osyy7dI427JPA9KkjpWDtLoHmNdgrceYKgq+o0o8L6fRVIGSMxx1ZKT9PiSm6+6n65XMKjR4+g3++7+T+vjKJ9NBgM4IMPPlgJWnlBd2EgLXgXLZaJO7qkcY6/6f+xenH9FHMI88pZvquT8nilUoFbt26Fdjw5OYnmR53msoITWpCLfov3SY7H45U09I66PLCcbA00kLrOAIjlXPM2k44gjJWh8arXQfaWI33Pdx1p7WnZSHlhlUXptOQ+t7soD1pjWRrvMZnBdR3lWctWkGjCccTziuUj0ULL50fcSvQjPefn53B2dgYA8TtJJUeQwnJuYgEjnj/Sz+Ub15VSWVg3esyVBloGrQOC73Tmi39wwirLsrArAO89GwwGIY3UT15di/+4POF3pyG9VH/SdsI02M9HR0fw7NkzqFarGz+9RxobUv/T0yDwfzwNo9lsineyar6ZFPyh4HnRcSLlN5vNoNVqhWOC5/M5HB0drez6QP7gV0VQWqQdm5VKZWXyg1774LWruNyUeIHzM613pVKBWq0WbK08trwEWga322LxAl4XXidrTElthr4G7xd+jQddVKjJVa+sod9If/NytIlfLvMsP8zSg1obS3ouFbE6anzI7/Gbz+crPoZVntYG+B5lKR3nnLdjR2ojfalXDHjaMW9bF8mPjyNaV3zX6XQAAODs7Cy0gdR2eZDCY1fRhh7w8cqfIyzaioy1soD2A/qntVoNvvWtb0GlUoEf/vCHKzui6R3giCK2hGW7SnQiHako0sbeb/mpklJ8xlueR6/xEyskOq3F60V4D2mYTqcwmUxgNBqFHbLaqRFa7ILru7L8eulEEu+3+EzSuUVRtJ5eWD50GUipR4yf6Ql6nkWi1FfYJK6DrH7RYNmcqbD4IdY2RdpOih15celsHCsTy2HVvs9jIK3L6CyipKU8tUCQBKtdJGFu5WE5aKmwmD+VF7w0aAHGvMrHCgR4HEcPX0vpr4vAK8rPvP7ckZICMvTbFL7n5dA8suzi2FycnOJ3BXDjjecRCzZL9JQRRCoCzfgr0qe836gxgsdQ4vF5CHr8VF55HDNiizjoUntgMBL/xmNMpOBiGWVZaaUgojcP/J7u7MUJAwzij8dj+Oyzz+D09BT29vbCzjVtTHqR0rc8X6R3Z2cHOp1OCFYOh0OYTqfhvg90orxGSoymWABRCshK36UYZTEZl8JnnB5pXPDfKTyJ30v1LUv+eXT+OmSol3+4TvOMD29/W9/F3ueVR8gPMbosXR0rP8ueHzN3fHwc8sHFFbjwKsVeorqIykgrj5RgFP/Oo4No2pT+kAIuaG/w+2J5n+NxmtK44TJL8g1QJ9Tr9UB37Fj4WD0kvozJUCuPFMR4gJdl8XWtVluZHKSLAwEuL8qg5UuymP7W7En+nI9P2mfSsYzIEzQ/Xr7Gz9y+4+CLuyRbm5flked5fHtMx79FOcPtNdq/Gs9J/odHdqf41dIY4GmkeuL/mv/Ef2vvpLx5O0k0abzq6SuJLk/6MsGPApbGHy+3Xq+HHXrT6RRGo9GKfI/JeY8NI413+rclC64j8tohdNIG67e/vw8AF6f28AB5iq1QFp3rxqZtXm++qX6IVY4mR9COwVhMtVqFRqMBr7/+OsxmM6hUKjAYDMJCPq9NJslaTpP2O4V+bz5FkGLPSjpCGj9Fx5EFyeaU6LZ0jtcf5PfH0oWD9AoOXoZEs5RO0oUpPBF7z3WJ1FcpdpEHtH097ZwHkr6KjV2NP7S2iPWTRkeMboumzxOK9H0ee2RdvIaQxlZRvzKGTdkT2niyaBEvKoll8iIhZoin5qWhrB3DmwQPCABcXtHp6XuPEailsYywoqAr+5fLpbjDgztZEp3X0SGwULQttRWMUgDJWnWUQgfeaXXjxo2w0nk6nUK/3w9pcCKR0sn7JnavQBk75sqAFiTwBB9i4KvH6er9wWAA/X7/0p1a1WrVNK7ythWOJ7pLlwauvTyC7ULTLxYLGAwGIh9e9X3cKcYPtu9sNgt9hYF3DL4fHx/DH/zBH8DOzg68+eab8OjRI3jy5AnU6/WVOyU3qYcw8PXGG2/AO++8E2g4PT2Ffr8P7777Lszn88L3oWkGd94A26YQM2bxucSr2Gac76U8PeOUOnPXzSi3wIOyvN6WruK/taApLUf6htMhvef0esED4BKdqXIEv0nRc/gt3nV57949eP/998N9fAAQ7qiOgfM1pSsWeJOCvTGaY+ODpqfBH7QPU+pEfyP/of7h45Aeb4v3klJdTPORyqH9hoHO7e1tmEwml06B8CJ2fD/lAT7xHoNkO+cdD9r3NJCHaLfbsLe3FyZhHz9+DJPJJOxMte7txL7iPEDbVbqj1xN0RN1HxzXyAtXZ/H5kztOpwPy5LMA60vsfKc1a3ahdgen4DlHaPhbf0PGQZVmw5/kEnAakhZcd07EU1p3LWEekk77j/oaUN+1bTEuPq9bkSKpPQmUrz98zbovey7cu4MIKetdyTGbt7u7C4eEhdLtdGI1GMBqNwlHhKQFvXgaV47RNtZNN+O70Fy1uYAHlB8o01KNbW1vw5ptvQpZl8ODBgxU5Gts1/3lETHatq8xYOxctG/m/0WhArVZbicu02234h//wH4by79+/Dz/60Y+CnSLZtByW/sHndGxpdpKW53UB1fH0fw4tDkTtTg2aPcl1WowGKU8pbex7yptUPqBNjPfKanlyG5fbRx7+l+iM2QHSM8mv3AR/XScetlBWbJXLA2/M54ukayg8db+O8pBC6+PrTrcGK44j9dWlO2PXXWEtSGKlTflGgpU25lzxNFp6b/DIKseDosEOmg+vy7r6Pk//piDG5Fg3WmcMlkiBOC2/onxo5RPLS3LiUx2dlKCsNx8toOMBVbS8zdHwxolZakhiEFWi1RLkZRkKZcDi0zzfawY2D0D3er1wVKJ0JxwidQeUl6dT880Dj8MgjadYfl75XhStVgsajQb0ej2oVqvw5ptvQr1ehxs3bsBisYCnT5+Goy45+ITAuiCNM9zFu7u7GybfKf9ZQXEKy7DU+k2SI9o3eZDidGl6TasXdS6bzWaY9KJHoHJn1XLWaWAbn+etiwU6LjbllNJAuRc8veVgWzoi5phLtErfaXnNZjPY2tqCyWRyKfhkgQclpPexgBVvA+0uSKl9PP3hlbc8f68THssH4PkCF3oEqQbKZ5Kt5akPtiHKPZx0ozsAeNvy/qbjCnV4lj2fMAV4PtkznU6h0WjAzZs3oV6vQ6PRgMePH8Px8fGKzVRG0MITXOV+k6fPOW3Sd5VKJSwew2NKG41GOJ745OQEJpPJCm9rY5Ee3yy95/aj5gvyepepe7msRz6h8kHSOfwZ5yWvzePVY9K32jt8zyfcvIjRro3PWP9pdMbsR+ShyWSyMjY5b0lto40NTfZxfqY+rKaLLdtDS5eq78oAPVKT6kCtfbD+8/k8HGHIx31enyqWThprAPEFLy866LhFtFot+Pa3vx3007Nnz+CTTz6BWq0G1Wr10vUxL1Ee8vjmHv1NQXesT6fT4OvhCSqvv/56iNXMZjPo9Xrw6NEjePr0qavMFN/aSpunrpuEZj9rutrrByNisimmky14+4C+l/gIAFauMuKLXfLSx8uR7CaLVol23p6SHXgV8MakvPloeaX4w9I3KbpX8/143ily5Kr7adPIowtiMpP7Ddb3ZbS3ZetxGjeBlLHmjYtIea1MxnqMJc2xTRUK/NtUpRMDDzJ5mMgTlLMURqxdqHMQy8dSmmUIYu7IS6uPipaB+cQCaes2lCQFKgW414GYIKOIKRjv3QZ5hRTNUwrScvAVwJROj2JHR1o7Rg7vpuJKAYNxKXWSJt03IcylsSz1nTUGaRr6vdXGNC06wwAX9/qcnZ2ZQSktOCbVI2Xs8ny9Rq0kM63gEqfLqwN4eo0OD/1FZFqn04F2ux2O/QK4CHbcuXMHnj17Bvfv318ph/6jtJYdEKZ50/8RuKsBV09jwBiPkPROxmrgPMn7gh5NmdL+Hj72yHBPwFMzKLH/ms0mNJtNGAwGQY7ikdRSHho/Sv0vBQA4zUV4Zh3y1MvXsT6k9czjLHphyWzu0PNvZrMZLJfPJ+Ty7O7HevIAtuVUS/Ti3ZRcXnsccOs9/5t/w/OhdqqVZyxvtBvo7lStPviNtaghxjeUbrzXk07GAqwen6u1H/cr8C42ukuPLraq1+tw584d2N/fhxs3bsBf/dVfwdOnT4M9RY+2LxsxOWI5qtaY5PlubW2FO8fG4zHU63VotVpw8+ZNaDab8NFHH618p01YYptk2fP7ZvPUlUJbJJgK7qvgMyqz6S7M2C5COp64P8HTxnaKxOrmGSf4HMeExP9aWTE9G7MNYz6O1+fneczncxiPx5euZtD8Nq/u4XqDLqrgfR7ztTGdJtc88nsdehOBE6vI35L/xstG+6hWq4VJIoD47l/kE94nHt+X5iHZFohN+JibhnTPdrvdhu9973tBnv74xz+Gjz/+GCqVCjQajbBb+YsIbbxskjc0/03zazht1WoVarUajMdjGI/H0Gq1oFqtwuPHj2E2m8Hh4SFsb28DwMWx4dVqFWaz2aXJWF6WZhNLdFj2p1RXLtfWJbM80OQn9//oM21BpJQfz9ujpzXfL5VXtXaVbFukbzQaraS1fB2JrhitfDJW83c5fVY59HuvP3UdgbRbCz/LKkPKW2szzfa3eJ+m89DxEj5INo3H3/bYTx77l9vMKb6+hpicykOnlReVxR561GOKY860luEmwJ0OrzKwaC07QOFhHg2SYirLkOXOB32GQAfPE3Div6+LM4K0eHb9WXlcNaTBLB3PWwQ0qMdB73iQvqHAiVVKn2Vk4xGBWZbBjRs3oNlsrhxRjDw/HA6h3+9fMuA88Brw6wI3CDXZ5Wlfb5DAMnq8AQbPN1a70mPyMGhId6JbMpk7i1ofLperuwJidGoOjFR2jGd4ntRQp/zvcQglOZVlGezv70O73YZHjx7BcDiEwWCwcowxDTytE5bBRWnBoNnPfvazsDsJ65LX0E9xeiQn33scn3VMosd+kGiIpZFsjmq1urJ7r9/vhyP3cEInlndsUYf0jcX3Hjtg0zKWB0+lYCoiNu6Xy+UlPqF2kiR/pIA2fya91/qG0o96jh5zq9VdqkuWPd+NLvFDmbCCP0VsLPott0HQTqEnO3h5HHdUou7gd1jFZJVkJ+NzLcjhhWQXUB6Yz+cwGo2CrMVj/3l6XLjRbDbDwhh87tHtLwMY+YG8RSfgAVYDhLgwqd1uQ7vdhvPz80vHV9P8+G/Ml/KGZRvGbHFajkfPanJRs7M8QThqM1FbSPPLJbsrtUyexgp+S99L3+A4k2S9hdhpFhRaO1MdpumI2HiP9f+6fWLcJcUX20r9iTzT7/dhNptBo9EI7yRfVsqP5hnzVaX+9MaiPq+YzWbw/vvvw2effQZPnjyB27dvw3e/+12YTCbQ7/fhwYMH8OTJky/sRCzA1fNEGfyJegyPlkUb47d+67fgzp07sLe3F051QXD9h9cGjMfjpHgrtc+8/hX95rohppsk2eSNS/B88/Y3lYVF2lLSRfSZZq9460DzoeVMp9NL949rdotX13rjYi8K+DHyKTwmQYubSPkWLesl8iNFjuZB2XK3DB6x8vDSmycOrn0nfeO+zI0LSE96DwHaN5by5Y6HB1aQ0SP4NyU0MJDGy9QEnfR9LH8pWMTbwJv/Opk4LySlqwXRYnnwZ3lRxIiSgro8+OAtwypbGn8Al1fCakHXxWIBs9ls5a4eaQxLhtjOzg602+2VfPC78XgM3W7XXRduRPJyU2AZYFo7WHJFCqTkHctWWZYB7VXGRcAnJLVghoaYTObtyINikgPA87ECYHkMFrojguYn0cLT8AAkwMWYaDQacHx8DIPBAIbD4coda96AU9lOA5dBNJi6WCzgF7/4BRwdHQEAhB2ynnsZJXA7IMWR9YLWR9N9sXz5WIsZf9p3OMGCdytjf2Og0doh4nV289Afa5N1yJSUgHIe2zTm5Gtyi3+v6QSv7qFjie6glHQE5VMLdGW4xdO0LtLfFmJ0WM44z4e+j9nq2okunnJwty+dlKXj37JbJHokueSVH572pn1Hj0HlR8DT9DjpirtZcDLWuiLA21dXCa9MLUvfaT6vVRa+wzsvcbEhbVd8jjt8R6OROhkrlS3xPP+bL4rEZ1qeWj04ND/R0tExG5TaDzxfCzydV35J8pvLYFpGLB+abrlc5rJ1Yv6mNj41HUztf15P/M5jH3rHXFmQ+NvixSzLwm49PKYc31EaNTuSx0Isna61kyUz1yGXNglrLGXZxQKhzz77DGq1GgwGA6hWq3B4eAjT6RT6/T70ej24f//+xhaOvkjw2LWx7z1yDv+OyVUrP7QfcNfreDyGra0t+OpXvwp3794NPh4uRKHl07s5NT89VodU/8Ljj63LP04B99WoTSqBy3P+juZFJ8JTYxmWj2rRpOXL02ixEaleWjma7QPw3E+msXTOj9b3nCbtbw2b4imLxz0xEt6366Q7j48k0XQdfZNNIq+9xr9N9VM9+Ut5af23jpiRVI43rUWntwyv3yTBNRmbd4Bqjpsnf+yo2MShhRQFVEYdrbLz5i/R53VWvflLtHqYpwzBvS6HjuaLPIQKGoOd6yh3HcpMmuCxaPAEQ6TvrGOQeVBQakP+m66mpEFUfMd3v2rHpnnHsbTzY1PgRqV15LsUSJHy0uBxqng+2P7S2JD6Bu8v1AKSHiyXy3A3CJaBwfBYP8XGEj2KjAc6JWTZ811RdCcVDVrx8i1QQ2KxWECr1YK3334bxuMxPH36FCaTCUwmk+ikKS23Wq1CpVKB4XAIs9kM7ty5A1mWwfvvvw+j0SgcgYcTnB46NeTRe1mWQb1ehyzLVvpwOp3CcDgM90Mhz3Q6neiEsVQGp1EzGq0gmJSOgx/3ifAGY60yLfA75LA9uUzNsiycFEBPDODwGJE8+BgLIHod7KtGjEYvn2vBWS5DpXbjuxOxP+n33mBDlmWX9Fhe54seZ+k9ph/bLGXihvMZlosLCsq0t1CP4P2v0+lUncjC9uRlT6dTOD8/DztMqf7j+lHrc65LpeASDwANBgNYLlfvzk4ZQ9iOdDcvHau1Wg2m0yl89tlngYfOz8/Dc5wARHh5LA9ieWuBNe07rgvo8byTyQROT0+h0+lAtVqF7e1tmE6nMBqNAj9ynuZ3xeIzTE9PIsDnqX6n1td0fPATLjx2Ea0Lrxs/4QH5l+vsGPiufKSxUqnAbDZbGdPcRtR0C6WHpvPocQqaXrLfpDHPdS3fFULTUduXPuf55PHDvXyU2iaU1zSZRdNKz5EfsX2sq0rW7Wft7+/D9vZ2WIB4dnYm7phFSH1JT+GhPEFpt+wDXseY7RRr96Lw8E7ZdpllI6AexsXXR0dH8Nlnn8HPfvazlTshAS4WE+Ik3lX56ADXx24tA0X8cituaeVbqVSgXq/D1tYWjMdj+E//6T/B7du34Xd/93dhb28Pbt26BY1GA3q9Hnz729+Gt99+G/7qr/4KPvvsM3FCnttJsbp5/UMpjzw+b1mguo76fwCXbRFqM3rjL7RdUY7TBWEWuM2ltWeR9qO6V3vPY2f8vVS2JUvQNsxLM+aBsk4q7yp5Kg80e2CT8F63VxR5ZP3nST9cJV60ceGFpauKjiPRstUCUHkQE57Wkat5g1ApaYrA60ymtKHmVEpGSBmQHBNengVe9xRe2aQS0OpJkUK7xtcpdbIM4li6WNAK/47xDX3Pj/vTDA8a3LVWu/JgDc+brtybzWYhwJZl2aW7sKS8aZ5a8MCiS0KKs+sNAvF8Y+Xnkbe8HB6A0Oog9Q39WzNCLblOy6VBO5xstOBpJ4k+LT0N1vGdsFYZErTgC04M7O7uwnA4hNPT0xCw9ARZaZAW4PmumslkAvP5HI6Pj1eOJ8aJOUvmxIzKvDqdLmzJsiwErUejETSbzTDhjcFmunNLCmB6ZGBRPWG1j7YzooxyKTwGG7YNbx+kky/KsZzooo5IjE4p73UGImOQ2k6TiVIAn0KThVqdJRsIAw/Su1jfaAEqXi/rHeUbr0yl7Ve0D6kOj90VLclVazET1o3faUvfW+XQHRz0OH2JFsyPtgvVnSl2L572IR0P5vVn6PecZpxI6ff70Gg04OjoCMbjsYu2PPLCGu/rCGpo+nc2m8FwOITRaASNRiPoSJz8thZY0LFG27FarV46xprD6j8eWIwtqJTkN/2bf8P53hq31njQbFkqC1COVCoVqNVqsFz6FhR49UkMkr1K+0yqgzWmuHyV6p1X/lr1zdMWks2kpUFelezcGE34t2fhwbr0Oy2z2WyGxXzj8RjOzs6i5fIxII0hzSbQaJF8POlvyxYrCx55WrbMtYAyDW3+wWAA5+fncHp6eolnUabioqCrwibb5zqD2sL4m7+XQH2RxWIB9+7dg/Pzc1gul9BsNuHGjRswnU7h4OAAdnZ2YDabwU9/+tPwLX7P4zrSOLR+W7Jd+jY1TZmQ6qH5wCk6gtorPH/qL/LJX40uq1zPtxakeJHWLlJZWp/F2lazQbz6TTulIVb/TfOYtyzNVs1Dp+Tb0t8erDNWUKROXwRIY1JC0T4qKjtSy8mbv4dOjw7S8o7RtTIZm+cYkViwKgbLIfR8WzQdX+1YFHkZAgP63EDSlFMqUEHHoAU8UgfkdRJqnjvF1oV1Kps8SDFIJKcJJ4oALhzmmzdvhndnZ2dwfn4eglg80KnRoWE0GkG324X5fB523FHa+Crp1D7OO654G9LgMUKa5LHyx3zo+Aco565oaaeftQhGe2/JATrRulxe3HvId0Qh7+DxifS5FTzy9Iu164O3Kf9uPp+bvMrTI9rtNjQaDXj99dchyzK4f/8+AAB0Oh2Yz+fBSeX10WhEYDv+9Kc/heVyeWk3pNUuecaA9xvk88ViAcPhEF555RV4++234fj4GN59991wTNxwOFy5P4gfc+6haZ1yUwtccz4si4aY0Ut3IeEuYz52uOO9XC4vBU4pX3BZIi0KkAxLLz/kdeDyAumj94Vm2erdqBJoG/CJkVj/0qCGdTw0TY/vMY02GUPzwKNkqW7FutIABe3f2BiJyfc88I4H7JtWqwWTyQRGo9GK/WmNMUvH0B0EWH8+ZvkECw+sjEajMJGkBal4mXjfuXRcNd8Vzemg44rfoS2lTwmQScA2efbsGRwfH6/wIbYZL99rE6aCT4B4QNuB8jkdw/P5HIbDYXiGtsZ0OoVarRb6GL/nRwVT+vAfjjXsY7y3GwDCCS687bjvy2UxXVAg6Rw8DQZPvOB1ldoSQRe1eduX7+bm5Vn+Zq1Wg2azCXt7e8Em5zu16U78FH2CeaBdOJlMzPs5rXhBrC0obbVabUXGovzV9Cl95mlzbbxZ32v5S3KNj1troaPHlkY66akzkqxeh+6np+bM53PY3d2F27dvw40bN2AymcB/+2//DYbDYZj44/p4uVyGhSd57DauX2k+fOxL10ghJHvrusUA8kLr98lkcqmeeMUG1bF4qg/ukH2JzSIlBqiNb5xU39vbg3a7DWdnZ1Cv1+GNN96AL3/5y/DLv/zLMJ/P4f/7//4/+OCDD+DevXvwp3/6pyvfdzodmM1m0O/3S6sbwvL1tfSbhhYzor5MihzT+gtlJabRFkVKZVk2gce30OLGWEfpKhapbPo/t7Eo8Dn6a5VKBarV6so94v1+XzxZygPqb75Eufi86MeXuAzNRqdj+br3/zrsXVFeawmsgImVoSYoPQ2eUuGYMxNLR8uTAjdFaMubnis5SaFpDrukuLRyvG3EaeHprTLo/9dpsHkGljcYnRdaIC5vnlJ/aoF26Z1VX+6kNhqNkL7RaIQFBABwyUnlgStaFg+60eNU8N1sNgvGkxYQ4QEJPlY0mbZOSP3pDch45YamxKw6a8pPkvU08MWVpwTsi0ajsRLYxImMwWAAAJePby4jwMN5lILzGf9GCn556KCBmZ2dnXDPMT92ENN6JtV532CQltNkGTHeNtSChBpd9Ds68Y3HFmIAHFc90zrT9J5gaSpoAFjTWVY5WoCT558HqboEZR62c4wWjw2WErCO6fSUYIHWxqmIfauNrxT7VHqfRx5ZfC7pJ3xOjzOX+pjnKwVPisgDno9UppWvNIZQDlJZw8eodv8lhaYbJLnCZRqfEKOTqny8S7qI6kGJN6QJLk3/cxnF/+d/S/aNFxgIl47AjenxsmHJCo2WmOymE6x0ghaPweT2wHK5FCdIYrJQC15L9gTlb37ML02H36PNwBcrxeRrik2r6UXNTtb4DdPgojt6nK3Ex1KdPXRqdhxHik6TZAQArARtcfKIvpfaIkXvSTJaokOqg8Q7qW1i0avxZqz/vXnlgVTfdrsN9Xo9TNLHZImHD2I0WDI4lmeefntREPP90Nc7Pj6G+XwerlahOtIaR2Xx0aZxXemW5Ixmg3q+xd+oB/j7ra0taLVaUKlUYH9/H549ewbtdntlAg4nczlidrmEIn5ZDJvoU80mlGyXGH2W3kRdLV1TJdHE85TA9URqHKEIvHShfsc29ZzC6Rkbse+uOzbB1xwenra+T3n/ecC65A/3xTcBSzZJNgBNe5XjSvOLAGxbW4uXxHyb6J2xWubWCitemLTzySvA8+BFEowasG3ojq3l8vIuqbyIOYwe2oqW/XlEHiFqDXov+M7AmKPtoQUAwgp4gIuV5F/96leh1WoBAIS7uqQy0Pix7ijDQAjuphuNRpfuLuW00b+t4w9T+TsvT0s7n3i5mqzDNNLufG9/aeVJeVA6KL9g0BEdJgT2j7ZjgAJ3M7zxxhtw48aN0P/b29twcnICf/mXf3mpP/mRVXkMBO+OWOQ3DNzyu1To7o4YMB0GI1utFmRZFiZleVqtbrHgRrvdBgAId9+VhSKGHuryWq0GvV4P3n333XCqAwLzxt1Jmzb+KA3etBKNVhAjlp8W7Ix9hxMKNNjhcSCl8pCnrV1UdLIq1maao1q2HacZujzwI9FiAVeKU75ESHIc5SHKMnzG+cK70IKWg3nhb3oEKMpS751RVMbQ+uCkSWr/WHIpJY96vX5phzdvX8v5omPSa7toeQE83xmLbUMDhNI32h1nvP8xb/qbgudhndTC9TPlS2wP9AnKuos3Rbal6I5YOunEDmqLWA4vvScY2wV3aEljRpKNNEBH7UWcmOP8xO0zjT4JNH8+Ti37JdYvnrHBj1pG3kfZJe365UCdhOOl0WiEyeQUGYWg1x0gXXj/c7VaDROkZZ5qhHRin+Mk38HBQZjM50Frrd+kfDW7G5/xYDj/RgOngafFhQl012jM5+D0Yz70W4s2TovmB3lBdS0AQLfbhXq9Drdu3YJKpQJ7e3vhyGIrD1ofj0zjsjxlTNNyaZlevf2ig9Y7yy4mzrvdLvzX//pfw3ucoAOAcAKRhuvcXrHxf9U0pKAMfU8nVSeTCXz00UdQrVbhjTfegFarFfqcotfrQbVahdu3bwf/GfOyyue0x+oSC3rju9j7dUOSsZp85vSgrKTfU9uR+0r06gVqO/FvJZ81Zodrfi79Nua/ab6lVI72nl9VxmUNxrMQHvuZng6h+fVXgetCx0usB2X3bZE4+DridpKMXld8sEg8Q9MleeLUAKDGkQDYZKyk1DxOAhIrBSk8AcgY1tFJRZidM1AR+tA5tO6u0toyT7llKD0vDZsIvnuMAwtlC71YEChP+ZZxmTdgk6feWZbB9vY2dDodALiYJKITRWj80MAhBnwkOrgByI0pT72k3yk8GQtCeZUFL5sbuJqRK5XDy9IC1DHE0lQqFXj11VfD0VH9fh8ePXoUjHfpnrNYkBTva2s2m1Cr1WB/fz+kpzsRtHvceN2s4EhM9mqOAH1nOXJan9C2wSASOjvj8RiGw+Elh0ein4OXHWuHWP68rzivFzHocVzjynd+pCovc12QdNV1cFKs8SzJFCortWDtOvUpH38S72jQgrWeZ7E86Vgoal+ifKIOuTTpQtN7+kKjwcP7WDcaQKG7ZHk6qTypnBQ9GoMkJyUbOGb7SLJHC3DwvqZtThcZWLax9Mya/OLAPLSFVprj6nH+MB2nxTOx7xmb6FdI7Uh/a3RpsPSxlAflac9VDvx7Te7xtN7ggiVHaZ/QhX5l+F1aeXlkIoA8PjCN1R70G+24ds2/9cYDvOk4/TgJi/Tjvc4af3FZktqWdOKxUqmsHBOp5cXHjGRfxtre20ca33P5Y9mqlr+gyRErvaX3PHXSQHUILs49OzuD2Wy2sihGOi5eg9d+Sf0mRX/wPNeNddvbFJqOlo6QRl1kHU96nbGJNuX8otm8nnwkv4+Cj3Fvf8xmM5hMJiuTVNjng8EAHj58CPv7+/Dqq69CvV4PxxnjYt1KpQLT6VTUr576FfVZryM8upLrOyst2rkUXDYXkd2SHeL1xzy0pMD6FmNRCLoYBBdmxui0yky1OcrEdeJlzQ/KC8seTc0n9dtN6s+rhBRLsOods4dTvvGkLWNcWboz1Wcoiy8snavujE1xqKhjZ0ELwHvgYZoUI70shZ5HINM8KpUKNJvNS2n4xFQZSM3vKpVNDNeVrhQUHdxWAMojPPA9PypQ+25rawsODw9hb28PAJ7feYU4OzsDgIv7tmhwg+9AtPqOB8et4J8VpIvBazR6vqcBHGlnqZZ3kf6PtYEnaNpoNOC3f/u3YX9/HwAA7t27B7//+79/aaejlAefrKV8sLW1Bbdu3YJOpwOvvfYatFqtkB4n8gGe74yVgsZeeHb2ace/o0MpTVzwfqM7Z/n9uIPBAKrVKtTrdRgOh/Dw4cMk/uK6Fu8yo8cU54Gkq6zfqfnSYxb5kZixMq7a4KVt79l9kydwIZWn5U3vJsVd21l2cVdflmUwm83EO7y1/PPQK/FjHqdZstVi39JyYoGEPMA6NRoNODg4CAGhbrcbdmNROYE7pChw3CPP82MtaT0te5UHnheLBUwmE6jVaiGAgEGrVFD6cRV82bYSnwSgq/SpzSiVS9PQbyz+pc+wXriTWJOP/Lk0yULpkPpLOoLV67xKdcEApBVYw7aM3f8oHW1LoS2OicEKQKWOQapnua2ZZc937NPrKag8jtk4dEcnXQCo0a/1My0Dn/EdeJu4A1IbA7wdUF/gu1jfcB6mY47umtXGgEQbh9Qu3L6ybNIsy6DT6cCNGzdgOBzCcDiE5dKeFNfykoJ59BnyzXA4DPdB427fLMtgMBiovCSdyMB5lf/GdDFbLjWoKZ2oE4MW9LJ8/ZgO12R4KpAXa7UaHB8fw9nZGfy9v/f3wr1/GEDPskz1q4qC14P7q9THk2JedFzh703iquxqHKuVSmXFx8MTr5rNJmRZZi5+e4lVFPXPON9qYzTFV8UF+I1G49ICq9PTU/izP/szeOutt2B7ext2d3fhl3/5l+HVV18NMYbFYgHdbnfltETUZ6mxnhcZmr600nv5gZ5mR/UrnZjEPFPyp/1FbRAOKR96AlAsvUfPx04CBLjw1/B0MYCLeBe1xSeTiehTW3GSq5LrL5GOPH30RejXIvGrz5scXgdifqvWhrksWk+A1XJ8LcOfIhbUkdKm0Ov51vM9T1t0oofTRAMPeYHKVsvDM5med6KrLAFXhkDYlLC12oCPEw9NPKACcDlAFHOmtd/0OXUm6YKA1157Dfb29qBer8NisQi7/7a2tqDf78Pp6Sn0+/2QT4qhxo08nq6s8RcTklJZPMjC/5YCLLVaDVqtFszn87C636KFO0rcobfgkV0YcKIG+iuvvAL7+/vhjleAC+MVAx+eIBjmS48bbrfbsLe3B/v7++G+GFxwgmlpvnnGo8brWqAJgYFuDPrU6/VLaWiwgNNJ+3y5XMLh4SG02+1LxyrScWT1PcDlOwdp/usM/KYgJi+kQHhROSsFyiUUMSxT8/DKV0/ZEo/xBQ1UltEdBTF6tcBLbHyk0pyajwUqf/G4XqoXJN7SbEzNXqTPa7Ua1Gq1FZ3F03C7y1t/LUhv0bhcLoOu0BbixQJpkv2dp29S+JzKOewnnNjGhQX4zjq+UZsU4HwBcBEQ7HQ68NWvfhWOjo7gww8/XFn85W0767lGH38mlUV1ulWGxFdYRxqslPjIgjZJoJXPn8V0Viz9VQY0UtqpKJ2ecU/LSrV3vDrQ8i04L9LnfEJXqgv9PZ1O4fz8PNxHzOurBSol/uUT8PicnpqC4yvmm2r6QKtbLB/8m+ohvviB5013iiEkn5v/zdvF2q0cs4O8PmBML0j10L7R5Hle0PaYz+dw//59aLVaYeFv3vJi8l+TY5rcRLtXsz9of0njL0bXuqH5u0WBCzRxcSG9fsFaTPgiwSuLi+SfR1/kLYv+r9mr1rfD4RB++MMfwsHBAbz66qtQrVbhzp07oQ5f+9rXYD6fw1//9V/D6enpJVknlZNSd4+evEqbxAvNHkNdLS0OTMmbH21MZVQsPyrvNP3g8f9jY0fTMdozy/+bTCbQ6/UunciG7+lVVRgnk2wJSUek+g0vAv+ViS9afa87Uvwi6ds8fOxJWzR2ZJXhKd/rY2nvNd9Xst9j5ZiTsWU4q5qQ5v97A10aUhW3V2l7gppl0Wflm3o/lNbueZm/6KDZFK7SuclTthS4swYywtqZKH3roY1OqiyXz1elb21twa/8yq/AnTt3AOBiwurk5CQYid1uFz744IOVMj1CkvOk5aTS394V5utymmLfNhoNeOWVV+Ds7AxOTk4uHUHOgf3vlRFW/XgeuMt1NBqt9OeXv/xluH37dph8BbiYXN3e3g6rXxHSscK4Q3Q2m4W0WZaFI4pu3boV8sZ8B4PBSr5o4OeBFviy0uDkwGKxgFqtFlb3UvR6vUvHbHF6ccfqzZs3w4pfatTzY56tsSkFyay7kK8brLtfLGwi0EDLArAnJGKLnbxBXZ7O8x0PSFPH0DMZqEEL7HryQ94s4nSm9u98PodqtQrNZjM4ynRSygtPm+N9gXjnOdaV73qhMlODVndJZ0kThvjbCpbwfDGNZheWba9ZDhm1GZbLJdTrdahWq2H3MabXJla0CQpa7tbWVjhBZjgcws7ODnzve9+D9957D37+859DvV6HZrMJw+EwBINpO/IxlgJrMoJOQHnsGKsMAAg6qdvtutqLQ6qrpGek71Lo5d99EaGdqOAZJ5hOuzdXgySTpb7jvEhtTDx1QYP0LcBFcPPk5GSlHtJ9y1zvUJqpjZNl2aXFB9TWRPmPp0N4wMceL9tqMwo6iUTvENbsFk1Wc9nG+8PSxan+vnTfK68vz1uqD+drLvvoO83GKAPz+Rz+7u/+buUUGuvOrTzgu/M5pDFr8dV1xiZs7uVyGU4aQTsgy7KVBbsvMjbR13l1sQepckbiGf671+vBf/kv/wVu3rwJ3/ve92A2m8He3h50Oh1ot9vw/e9/H371V38VPv300xV7G++41uiQbOEy28SjbzcJy75EW7BIe9AYhSW7JB1Ky5ROC8D/Yz6jZjN544UpvvV4PIbxeAz1eh3q9Trs7u6Ge3MBYOW0KeuaQM6HV4lNxk0+r3jZhumI+Y9aWu83RWkqK78y6dTy056rk7HexufHS9DAhRYMKAIpLylQogUFtbpowpY/lwyGFOak+dGjxHh+9J8WKI4xPVV8MRo3YWSuq4zrIFhjNFgBR08e3AGmAU4aJJD4mAdCOF2Sg8kxmUzC0W00wCqBrjizxiWnQXtP08VAx5MmA7xC1xo72O64+xOBwfbj4+OVo+6kPrD6I+YgafKVfofHXlLcvn0bDg4OwoRHu92GyWQCf/u3fwvHx8cr39NJRcof0nG0N27cgIODA5jP5/Ds2TO4c+cOTKdT+Mu//Et4+vSpeBS2xZdlgQY8UR7S3cDNZhOazSacnJysHLvNx6gUYDw/Pw+BRQzYDYdDkQYEP6ZHCnStsz3KREyvrlMuW8FCK20Rg0tyBqVyYsELKU8tWMr/1tJYKJOXKP/HbClJ9lv8vVwuwzF3mKbb7UK/3w+yLLZrXOsDDFYDyItLUE7gd3y3O77nciyF76kswrbQjmfjeXE9jbYjncAty8nUeNGyN+k7lIXn5+fixA/tP+moeCktvm82m/Drv/7rcPv2bbh16xbcv38/lK8teqL6NgVeXYzl8zJoHQHsI04pjalH0/O8KpXKig3GwW2kdcjpLMsu7TxAWwL5tNPpwNbWFkwmE5jNZkF30qBflmVh0ZdVD4DV9sbyMR2+5+ALpngfSUFQLsP4Djnal1bZlL+8fpqUhsoFPLo7ZZEG/T62KEmzV/BvWoY07mML1AAubKRGowH9fn+lDySaNboofXRMcD5aLBZwdnYGWZatHFvI5Qk9WQZpBIBcC+eQdikwTesVk+l4XPv29jYAQFj8MplMwnjj/GXBY89RPlkHkFb0MfjpCmXJKs3Ol+ihsOQlHxeptqY2brR8uAyKYZNxEnpFgmU/XDfE5HUMEr9cF3jsdUt20/T0ig4cq+PxGP7u7/4O5vM5HB4ewu3bt+GVV14JR1S3221ot9vhmGL8vlqtuuRJqgwrM+06YNmq0nN6MkDMptXs0VjZkgzj+Wrxppj+53TwOmngdaVtwP00re4AEDYkYIxud3cXJpMJDAYDWC6XKzv6uZ7T9IRki3GUyWdXzbNl4Srr8XlpQwlFYj3S+Kf/8+cx0HGYYmN58vTAyteKUeWx2TT6NJkhPXcdU2wRKAUYPEKdfxN7hvloQbQYbbF8NRpTnGTtW14OQpuMpfXRjqyjsNrQqxwkBikysD3w5F9UIGwa3jbzCAqajjpdfMzxFW+etpGCF9q3aKjg5KK0cprCCqxKZaU4bJ77Ha1xTGkp4txj0KrRaIQdoLgLdTAYwJMnT1RDmJYvPZfuTJSCjviOG6L07iWOmzdvwt27d0M+zWYTJpMJ/OxnP1vZtYqGLr2bCeWvlO/+/j688cYbsFwu4fT0NMitH/3oR9DtdlfqgcfG8LrEEHMmKCT+XCwu7jWt1+vhfb1eh06nc2nynH5HjX/Kf4PBAObz+crdrpPJxKyXFOylwdB1y9wUaDI35ghK3xYpO9Ym3jbjQc7Yt5KTZ+nTlPbQDFXNhsqLIoalJ22KYY7yQ5okwvpWKhVotVoh/WAwCGOOT8Z6y8X86e5UTjeddKEyFfsc/3l2UNP60PphObSMPOME24JfU7Bum4j3uea8YRuNx+NLdaRtadFM39Ex0Wg04Bvf+AbcunULDg8Pw0QED6JotOeps/ZtzMeR+lyihcp+j6yz+pnzVh45VyboZFW9Xl+ZmKbXGIzHY+j3+5d2WuJYpXXhsjPmW2r2IP3HbS6LTymf02eWvadBCwZ4bXj+bWynn5YHH6NSfWl6yQ637G6+CMWydXBXKp1MlOpKabJ8iCx7PqFPxxn+3ev1Qlq+eIDyH6dRklFe0PIlWHxAxzSOI4CLydjRaLQidz100XI0PpbalMs/LmvyyhSsFwCsLDbKqy+1MqS2obRbtl6K3bNOePzhqwCNW2ltti5fJ8+YvI5t6EUR/R1rK03+0HGJcYLpdAoffvgh1Go1eP3116FarUKn0wk6qdForJx6A/B8UUtMFvLyX2RoOh//ltJoz6Q0WntZOlR6z+MfCOu4Yi1vjVaPrtD0DKUn9i36I5PJBJrNZtCb1WoVRqNRWKiFC3UtveuJCbyEDy/b7/pAGs+aHRrzuTxprTLzgNMa83nXCYsGrX5VnggnFPgH4/FYXQWKypnmk9KgXsWD9HEUvU91E7Dagx+LySdj11m3626E8sF13enNg9h4of2vHRFSBg18BwDHYrGAo6MjmE6nwfE/Pj4OOxowsJb36FkNXqNNAw3AUOMspmiktqV36dK22tnZCcEbfiQT7y8pwERX4Em0eCAZrThZyOXzm2++CTs7O9BoNKBer8OdO3fCROKzZ8/CrgSJbnrvqzaxgscR7ezsBIer1+tBr9dbCRrSvL315cEjqf74DtsBHUCcTMbv+E5V7dg17HO64+Tk5GTlLhf6txS8k44u5vz4IkLT32UFRVINdqstqSyILSqxvs8zAeDJuwgPpOoCqe4xGqwgZax8LeCL44anpRNsmtyM6U0e9MbAfK/Xg263C6PRSHW+eUAbaaR3TyP9MTokHUMnKqgc0OQbzY/mIfVZWXZBbGzzZ1zm0nd0F6xUh1S6lsslbG9vwxtvvAGDwQD6/X5YcLMpfwBliGdyivMt/Yc8dXBwALVaLZzSwBct5dUTmlySxkde0LzweNlbt25BvV6HRqMBx8fH0Ov1Vk7syLKLRWN4TC0fB8vlckUGIKgNodlMlg6g9KLPi3RzWUF1BW+rWBkxHUhlm9SWtC28QFsD7XRNhnCZqy2Ukb6l9NN6cNolbG1twauvvgoAAGdnZyGugLvpsQ5cP1n1SLUfaR1w9z4+Q/kxmUyCT4N00xgE8gdvC8/VApyX8qBarYa7z/EY9+Xy4jQJq72wLrE2s2jT+LpMvaPFe8oE7iKWFsYjHYgU2Yj961lIj/lSvuI8qh1Xv1xeLFhpt9thVzSn+/ME9GkR64g3vGgoKz6WR3biM9zBDgDw4MED+O///b/D06dP4fz8HFqtFiwWC7h37x48ePDghbqGZ1OQ7BsAEG3Z1LGt2fDUZuc0cFqkTVf4txYD8vIitUXKjIFQGTyZTFYWiVYqFdjZ2bmUHiB+DZpUDv07pvtf4iVeFGw6Llm0rDwxx+sCcTKWBquxUmjkISxnMRZAkp57QJUCF3hWICRPx3gdlLyKkbedFISg6csaFLGg4TpQNF8e2OC8abUN59NNChYNXqNXQh6jhfOWRI8WWEJHFY2ufr8fDJt+vw/Pnj1bcfAlx0gLOliBCut9CrRxhs80egHktsZ24Dtx8H4cALgUJIy1P83bU+dY39M603sXaZB8b28PDg4OAOCi33Z2dsIOT7rzVXO8EDTgjnc+4pFFABe7YXAiE4+KSw0u8j7T2kFyaNAYpwY/7TsaAKHfSDRwuvn9tzQwx+nhu4FjgZ88KNuRiSFGryZrvOVu0qBKtUPo/5psS6kL5zsvb8RoiH2jyWbpOT6LTUBYZUtpOU3cRkq1M7U2oXpuMpmEwL8VzKNBc5Qh9H9eLqfPE4ylu+Ot/HidpGBKUUj2dOo4l4I7PD2vr6c8bCsM/C2XS6jVarC3tweTySRMPJVlP1h2EadLqoeWjvcZ5a9WqwWNRmMlQC/ZLbGyKD2e/vPU01MW5jefz6HRaMD29jZsb29Dp9OBfr8Pp6enl3Y9SmVLNgKtC+exWF4xupEeutAM4PIEpWRfWPRJ74qCymGNLyqVyspRkNQOksaHZvvzvLWxzumyaM+yLJx+gAsoACAsHKxWqyu2FK2rFVvICy6P6H3h0ljl9eE0pNi3sfpwmcH7jO40r9VqSQtRUmzQFJlTJsrsb2l8euwmre5Wm6ToDEwfswGlMpAH0GfDu41jeXjt+5g+4+NSksFaPqk+BpX1/HtuI5ZpB5SNmN0qvbO+pe8sn8tqB++41vKg9vD5+Tmcn5/D4eEh7O/vh0Ui5+fn4ejqlDKx3FSejbXppmVZDJJ858/XzcseO4WPLXqahBeSjE2xkSQ5rpXPF9HiUcXtdruQv6PZmmXaetcdRceRJN95n6a05XWV+WXgOsksy8/hWFefeHVhqt4rQq/kn2ppOFaix3jcDcVsNgtOHd1FhAF+vpK46Co1zMNjvNGytTQIrQF4MN6LFIdLKp8qMQSd2OBGZyp9XiHmbb9NI6XOsUHpSSfBa9gVAR/4HqMAadF2F0rppZ1H+D/dCY8BEczr8PAQdnd34fz8HIbDYZiIxW9rtdoK7R5+SjF8rLw832rBcG1XhdRWmA+VN3t7e+GYE76KH4/CpXlJd+fxcmPjHZ1uvJNPMwSx7MlkAltbW+EoRwCAbrcLv/jFL+BXfuVXLuXf6/Xg448/Fo/a5WUtFgsYjUbh/dnZGVQqFfjWt74Fr776KjQajTAhOx6P4fHjxzCdTkOQrchdW9JzLb2km6bTKbRaLdjb2wvP8Gi8mOKmfMMnZGid6I4KLeCDefBgKS/T2w6fR1jBhZTvNtFeV+0ApMpHTQ57AmlSeTFIZUjO2GQygXq9fmnXupQfDWLyhR5YBgbVG40GzOdzePr0KQyHQ9M2sALVrVZr5V7OwWAAAKv2sRRI0ejn5Wpp6d8Y1OI2o6dcq3xeniW78gQyEEgftYFRv+ECIpoW4EI/zWYzeP/992E4HMJbb72VRLcXy+XFcci7u7swnU7DIiWaZ+zuS80mov1Ed4Rjmnq9Ds1mE9rtdtCvWh8g3/P+uC4nBdXrdTg8PIS7d+/CL/3SL8FoNILHjx+H3UzdbnfF7kTbgNqX0qIH/Cdd2UH1qXYfMeV77YQKzJsuROTjBe0FazdpWf3AnXtJfqJNVqvVVib0Oa9pNoXk5/PxFJP5MXmH9vKjR4+gVqvBm2++CcPhEO7duwez2Qz6/X5IP51OVxZfa8HqGD30H8Bln59fFcGfS8A+53xDy5QCM5p/QdvIWzfc8fP06VNoNptweHioXnGi+X9YJj2m36JLQ2oAaxO+NS8L67lYLKDT6UCj0YBOpwPz+Rw+++wzWPbDLtEAAQAASURBVC6X4hUsNB/OhzG6qZ5I1ZNUfvFy8B3aNhS4WCnluM08kAKOnrhdnjLo762trXA90Gg0gslkAtVqVaRHsuGuo+901TSV0S7YL4gPPvgA7t+/D7du3YKdnR347ne/C7PZDP7oj/4IxuMxtNvtIMNiyBMLtGRZnncxlNGGKFv4gqTZbKbeAy/RgfaRVoaHDpofxq+k2FTMT7Og7bi1kBITsPT32dlZeB873Si13BQ+uApdWDQNomxaPeP3RcA6+vFFbg8NqTZRWbaLBymx5jKwMhmrGaJ0RxEliAcK8jSQ5GBpQl/6zuocrwPh/ZYHBaw8Y8BAAxXCZXd0nv7YhLGaUs9UJ4+WUZazwf/2fpMXVjBDKk8LGscgjTXuuKPzMxwOLwX9qIGWBzHZYRk6Vh219/ic96vWflbgq1KpBGMZ2wQnbGez2cqRtfxbLyRH1JJZUl2RVqQB75Pq9/vQ7/dhPp/D+fl5OOIKd4tx45C3CeeT6XQKg8Eg3Fk1Ho8v/S21h5d38ugZLq9pkA1Bjx72BvvokcM0mMt3oli7ajQ6PShDxvA8PEGLTQX4pTGjyeJ1OdZ589DSW3zlNUJj9tCLgph8xoUmqcfNWnoBxyMAhAVFHkdayo/eaUgnEDSbVNMx1ntNb6BsoXeypeSbUk/rW2kSQsvfypu2Ff/HbWIM3J2cnECn04FerxeO85UC5amgZW5tbUGr1QonPdAFUJLNo7Wlpw1pHvP5PNhVyF+8naS2p7Svw5fIgyy7WKyHC5/q9Xp4nmXZpRM7+HHlHv4pSh/NSxuDUrma3ZsSUNR4KG/d0CZL5QFNbnnGU0wvafILF3y2Wq2V9kee4FeBePOnz/iY9PpPPC/Lv9J4h5cf8znyAvUlyorlchlklqc8TZ5o38XGSB4/OTUPTxmavYX9iTp8d3c3XL8jLRDl8RlLV1v0xXSyxEe8b6x64r2dzWYz+F1SebyvPfo7BWXY3BpoXbC+kp3Av8lDW8w2uiqk+s34jaSv8vjUGqjPixgMBtDtdoMd/9Zbb0Gj0Qj3y9Kj0mP0l4l1+VEpvGXpC2xHfpJWKrTvUvS1J2/L/9bK1OSchTzlSGkQNF5TFDH9b8HShWXDk79Xv5YRF/GkK2oXW2VpKEMeptBRVvo89rKnPG/+ecos0x5OzVfSpykyJC9dElYmY+kOKkqAtXLJG3iwCKPEaceG8tU36Lx7VyWnKiUpjxi8jIo76Xigfl1G37qE2XXEOp2BdZQTc3a97/gqbjqutIAI/k13bfIA7/Hxcbj/U4K0oqxM504Drx+XQ9R5pmWnrOKTdjoh6K7QSqUCnU4HxuMxfPbZZ5dkEtLC28oal7yP8O4yesyP9A0AXDpqD+D5CQc44fruu+/C/fv34cmTJ2H3D72viy7M0XYKU+DuoT/5kz9Zqbd0ekEe0IAthRUYwXfSDgfc5T2dTsOqU1qOBNyx9corr8Du7m5Ih+335MkTyLKLncm1Wg3q9TqMx+NwdFiqjN+EHNskPDLTo6c9+swKWqxbD2r1lILd9J3E3558i0A7ip3ThWlSHOdY4Ijmg/c2Alzsssf78KbTaZBF0kQN5oM76+r1+soKcgxOx3SSRitfiIioVCpw9+5dmE6ncHJyAsvlMuzqxHvoUgLikl7C9qaTEiibUWatc1eexBPSMylIjXXCxS7aGKY7hzAfutCF5okneHzyySdwdnYWdlfi1QlFxwc9qrZarcKtW7cCLffu3YPz83NoNpsrx+3zXXapwSyaz9bWVtg56tEZGs8ul5ePvo7x30usD5ofyvvXCphredCrMjCv6XS6MpmJ8hG/1crLExTSAv0SkN9pfahNjM/oaVyxsj3BFr7THuG58gXtQ2kHNaWDwrPLBqHpXek3ppfyns1mcHp6CpVKBW7duhUWRlKauE/kpY/Ld7r7PEZvXqzLj0e6UX9++ctfDqdwdLtdePjw4QpvUj3Ed6al0Md9SSr7MU+qJzEN8hK9kxNpo3l3Oh1ot9uwu7sL9+/fhwcPHkC9XoetrS3TZywbm9AlvC1Q5gE8txG+CLEuCZuO9fH4CuVnSsN8Poef/OQnwU5vNpthgb+FmA+xLlD5s6mYIsDFWG42mwAAKzICkdK/km7S/FKe1tLpXHdLJ49YNGh0aHXjNo60wJ3Tzif6y9BXlq6Wnn2e7erUuml8540df15QRp3X1UaW3ozJwE3KyHXAq2fWafOuTMZSA4/vGEJoRyVIglSaeNC+057xSQkU7loQMY8xkqLwPQENytQxoW3Rmceh89BYtqHGeSSvwyKl9yg96ft1CwYPnUVp4N/TI2BiwQhr7ElBGArquGs7cDAdptUUjESjZrQU4UurrS2HwYKlYDCPWq0G1WoVZrMZTKfTS3f2xGi1DLgsy8JEQrVaDUdIW+l53WgfUp45OjqCwWAAx8fHwbgdj8dh16dGH33GJygWiwWMx2M1qOBtE6ksKY1l6HP5S7+l/Dyfz4Mj79UDt2/fhtdffx16vV4I1uH9ybTPqCNFdRb9zQNd0rj1yJbUsVNkrOX9tog89Pa9BW96LMuzMydWVopNQQOfHvrKguYoWWm8wfdYvtJ7Gji3jnSk/YS/MejJd1bRsSTd0UrzpHKUfo/f1et1aLVaYfIYZbOHJ61y8T3nHcm+4jwjlZMXXvtNs3O9QR0Lls0yn89hNBrBw4cPYTwew3A4XNlBWnR80PbFKwekxaDaGLfGPh3jUh9RPrJsO8mmkvLlPEzzKBu0XJzEwIlygItrcA4ODsLkmLYAivtxmk1P20mye1JAZQjlNTqxxvUxP8LYA6mu9B1Nw99JY03Lj9seVK5JiNnoGo30mQaaDulqt9vh2PjY6SEe21BLp/nhXh/EGod5fT6v72FBGgvoe1i8IuUh8Y/kV3j4x6s7YrBkVRHdhvmh3Y8LbPDKkm63G07z4dfP4BiyjqbXfDuvXkrRXzQtHlfcbreh1WpBq9VaoZsvJrD6dBOxDQ+stsDFe9VqFXZ3d6Hb7cJ0Og16WrrmQEJKPbxySHpXdCxYNl9K3mWMHYtfuA2A6afTaTiRhi9C4Plo9uwmeI7Twv/OC06/pquorgZ4fj2WFIsvC5Zc5/RJvzXfRPpNy5NsPA8dMXpjtjMvM6VcnrboePoiw6vXNftbyoM/v279s0kZllp+zMbTvuH5WnK9zPrH+IX+LoMPJH61/AZvPoiq+BQuVoT2er2LROTs+slkcmk1Pg2CFQXfTSAF8TEALsHjoGnIyyxW0EVCnvtDitKzTqSWV9bg+CKB7tQAuDgCxtqxSvtE2k2oGUxZdnG3Hk4uaqBB75SV4FKQSfrbyx9WULLoOODH6nLZlGUZHBwcQK1Wg16vd2midLl8ft9Pnvs7siyD7e3tIH/p7lVKg0Q3vpd2Ty0WC/jggw8u9UWlUoFGo2Eas1gvgItj7bW68UBGWdCCawC6cYe8iu/H4zFUq9WVxQ383jAE73MAgN/6rd+C3/7t34a//uu/hpOTEwAAePLkCdy7dw8AAJrNJkwmk3CXJOZPeYkHIIs6f1dt5JUB7qRxPYFyJnZXvBUwTHG6rLxS8khBmfLLA371BKcF4PlY9hjeHtB8pDGLgQc+ocD1FAdOnOHOCTwKk45hustJAg2CAKy2D+Z7eHgIBwcHYRFOq9UKAVyej1Z/rT1p+0jBXNo2vKxN2VSSc0z7hu+EjS3EiZWF/zBfPHHg3XffXSmj6HihdVgsFjAcDgNPUT6gC9T4wiUpcCrVSSvfCx6wk+pAf2snDq2Dd7CPJpMJfPzxx3Dz5k0AALh79y5sbW3Bj3/8Yzg+Pl6RK5q8Q/pRh+MkryQbsGzuX9G+0+Qc6mO6CwPvfecL2fDd9vZ2oAUnmD2Bf4snkH6Jh6jMsOwgq648jUYPB20X3ldIrzYm6Pc4Mf/OO+9Aq9WC0WgEw+FQLNNqLywX7U9Pv1qQAqx0LPM7orEN6Ji37PwYio4/lFeYl2Z/5wm887pK+v9FsT+RftTX//f//l/Y39+H73//+8FeePbsGdy/fx8ajUZYcLFYLFaODaX/rD6nk6A8Tsb7Qoo/cTsIQfl6Pp/D8fFxuGv88PAw3IlIxxZd/ELL8/LspvvaKqfRaMD+/j68/vrrcHBwAH/9138Nz549A4DnE1hlB3+vElQWWfYzT48oW79L4GU0Gg2o1+swm82g2+0G+5kuGimbtusKqc34qRAofygmk8nKGEa5bvlt66AVQctDe4df1yXZn1Le2viMxQTRJuO6GMBevBuDpPNi6alO5Pz8ounGl3gJgPXNz8TGQYo+0GSKlfd11zMrk7GWYgfQhZXHAY01nicYLTVmrANTDDItKOZJK31rfZ+SdwwepeEpl6fJo0Ri+fOAe5G8rO+umwK0eJen4YFFvGcDgw6j0SgENZDPpECIFjTX2pWOabyThX4jBYek/CSH3Qoe5e0vq03z9r81jrnxTCesreBgzLCjO5FxtzH+k/qEGqK8DazfNNDHJ2h58EbqL62/qUGK+Wx6/Gn8xt/TsZJlmbqrReIDrF+tVgs7oheLBfziF7+AZ8+ehYAN311cJvLITa9zlZrW+j5Fdlt612NnSMFqT5k0yGhB04cevonRoAXjNNm9LmjBV06L9m3qeNe+kRx5iQ4anKLPeF5FdjdraDQa0Gg04NmzZzAYDNTjC6kMTbEtY2n4HanSd2XWOY8jo7UH/5sGuAHkk3f4WMD6Y6Cc6p91AeU6rweli9OryS3pe/yNAa7YTutUn4aOac6XmgzKC1rWZDKBXq8HT548WRkr2rjWIMknqQ2loJiUhj/jx01r9ZHGAgYHaXrpW40m6bui8I7XmD3ukdMAMu1W3nTXFACs3P+o5SeB97dXb/J+9HynxSi4rY+gdmas/1NlrMbblEdj9hLmE5NTedpqnaD9rdXT8k0Bnvs7k8kkXDlTq9Xg5s2bK5Mf3I5H3tVsC609PDYAl9X8nTUmp9PpymQr6hB8jr4lvrf6LXUMWnnwOhRFlmVhEdzp6SncvXsXOp0OvPHGG7CzswMPHjwQj+eO0SmNibLtKA20b6XJJD72vDpEspHLQqxdsGx+yg3V/VpdNtHm1wGc91CmjMdjaDab0Ol0YGdnZ+X948ePo8eOW/YPfy7Zhxp9GqyTAvB7y4f26H1PXrw+RcHtIA0xm/MldHhkbFG7SMPLPltFzObHdzStB572TZFZ/Js85Vnw6n2rvTw2B4c6GRsjhk7UxArnaSSBqQVTUhysIigivL2O6LqhBaXKoqUs41Sjz+prq1z+3XUx5iTDBmHRiBNx+Pfe3l4Yb5PJJOzGy7IsrMqn+Urt6+EBfg8NXcXLV+dad9XxyWRt4jAvYsESS7BbDrBWFjrddMJysVjAZDKBfr+/cr8qL4f3h+Rk07v18K5E7VtcaUqP1tUcOWm8SsdOY76cdimdVE909LMsC7THdqDlgWV8U1gOLd3JQu925G3Nj+zHtqzVaivvxuMx/Nmf/VnYpV6tVqFarYb+sQJiMZnwIhiJqUFEKx8LeY65tto+z071shDTo9ox4akGsDe9d2JECsqmTKrEAtKeILj3G4BVWafZl95yaJrt7W1ot9vw0Ucfwfn5eThNQKPByt9qO97WPGhj0VxGQJWW6W0bydbQbLrlchnsGrQnMHhnlTefz2FrawuazWYI+BUJOnrGCi7C4RMANLhIoU3uSX1KxwXdHVFk7NPvpHs5tbFYlvxD23QymcCzZ8/ggw8+CPdYSgvBkKetcYTtpY1l/lzyNyVbTPpO42PqY+F7XJQ1nU4vvZPyjZWv2a9l61m+o5r+TydLKd0pfr4HSAM/spjvSI7pJQTfwSql9fgPtM7UH/PYaFKfx4JNMdo4JF6S5Im3LG3s0PrklUfrBB2TKcDTDs7Pz8Pd9O12G+7evRtOtJFkTZZdTAYul0v15Cit/aneps88ehYXePJd8zjZOB6PL9GDE82YL/p4MVlbJsqwQ2heWXZxctdwOISzszP4+te/Dnt7e/Dtb38bRqMR/MEf/MFKnT3jjiPF5ikKlLP09CsOeq98zIbktNPYS2yceMeRt12oPM9j26fSlYJ15JmaL/YJjbPN53M4Pz+HVqsFt27dglu3bsHh4SEAXCy4+MEPfgBHR0cr4x/z8pYp0WuNk1iduI8j2T/rjv3x8vPEUrhNT59pabV3Vx27WRd/F82ff3dd7IirQFlt+BJxXPV49ODSZOxsNoPhcAjT6XRFyNKAtHR/Es2DGqOSoYC/eR40rWR44DEX/MhHTgOHFnCQnHXtGw2aE50yWPIMLInmPAooT5ll5SMFPfLQfB0EU0oAhRsMVgAzy7KwEw/g8hGAWt60fM3A4t/R8U4DEJiHFFimdaCORZZlYQVfytgsCm4IeXhLMp7ob0kW4ft+vx/e03u2ad60DC7jAFZ3A9FAJd0dOxqNLh3NrgX8pLLxf+QjyeC0HD0tb+rkacFLjtTxquUv0ZKSP5fXliE/nU7hO9/5Dvzqr/4qfOUrX4F6vQ77+/twfn4eAiXb29vhfkHkB09AQHKiyxwXRYy+dX6T0k+SvLRknfRNGbR4wfMr6ryl0ucNctD/tTw0/rHsOy1fSx7zgJFHb2hyx6JH4x0AWJHl+AzlOtq+eFQ8HnUemxilkI5MRd1K6fbqTNpeZQUBPHawxN8xmjX9gLqQT55zGqzASJF6c7mCdwAfHx+HY1TpkfOevGh+fBegBUnHaumQV2K7fzzllSH/aB7Yj9VqFXq9HnzwwQfhepnJZLJiU/FjbiltGi/ydrJsl5ivpMkpfl8bf49Bf/RLJXvesvFpXpJM8iCWXrO/PHY5l5f0Wy8kOzrLMuh0OrC7uwvb29twdHQEDx8+FK8/snRKnvbSdJ5lI3G9pNGDv7nvxP0mjS81fpeeafa7l07+LNUW8yDmn2jl5rXjU4BjFo/5/fjjj6HVasH29val63+wrbEvcTJWWzSngfJCrO9pud4yzs7O4Kc//SnMZjOYzWZQq9Wg3W7DeDwWF+F66LWeS/LUa7NxpMgVbPtarQYfffQR9Pt9+NrXvgbb29vw2muvQbPZDNfwoC63Fo/HULZPZsl6yX7w+OfYftznxL+lBfEpPopEv/bOC2/asn2zdeWZN19qd2Kb4NHO+GxnZyfYyN6j9z12PH+X6qemjomYv+ix9S1Ii6aQXz31TK2fZt+ti7+8WHf5efNPiUt4vi3b390k1tmGRRDTdyk2m+Z/aN+nwDs2PZDo9sggSbbkxcpk7HK5DCtzuBHIjzrB5zEiuDMmPeeVWS5XVwvhO3SOrTKKIDUfzUhal9DgZcSCNTFD3jtQPN9JNGkKSnIoLWxaWFoDUYPX8KEBV/rMalPcbQcAK8cVpzi2nmfWmOQraWk96Digx7SORiP3rti8hrkWYLPy1RSGxo/SfdiYtt/vB3ml0aYpN+xH6ijSdqbHFaNDLcGjEJCGarUKlUpFDTBiWikv6ujxNkt19PME0WLyjubtDWLwYIi2i2g+n8PXv/51+Ff/6l+FOu/v78NgMICtrS2oVCqws7OzckSxJxgl0ZL6zpM3T1eGTC2q0ygdMZpS9Z03aBjrj5Q6xsrT5I5WjhUM02RdjAaexnIiJaNToq2IvUPHGM/PUx98zwPe0j3p/LfEI9IkEd4HiUeT9/t9mM/nUKvVYD6fw3A4FPNL1T1SOi0P/p20Qn4TdpOnnyVZrOlKaSdzWfLKSyfqx5OTExgMBnB+fu6ejOX5cZ8JwD+RHQsY8YCrV+fxvPDbstqYypRqtQrdbheOj4/De77TkNuIXj1P01vjRZJdHp2MEzYS8DhB+jtGg6dOFijdmu+p5RvTNTwvix/zBB+4n7C9vQ2Hh4fw+uuvQ6PRgP/zf/7Pyr2cAJd353IaU8DbQuJ3qX0tu1aTtdZkrFQHqju8Pp3Vhyky2Tvuef8Vtf0oHXzcxOReGaA7sOfzOXzyySfQbDbh4OAAzs7OxG9w8pbuvqabFDRdTumXeMwjL2ifa/mfnZ3B2dkZdDod2N7ehmq1Cq1WK5wcwSfqNHBZYdl3KfJHSxuT3zxPzKtarcLHH38MH330Edy5cwd2dnbgS1/6EtRqNfjggw8A4Lnvru0Etmjy2mR5x4NUtlauV+7xca3Js6LX6Hj9cWssb9pWva6Q+mQ+n0O324UsuzhuvF6vQ6vVWmk3lEea7qCIvZf6M0+faHqV/22lsdJJ6bnP6tUnVp70meQTW/KZtnVsjHyR+X7T8NhW1xkeu30d+aeWEdPlsXJSdC995rHnU6DFiCS6OG1ee4ZiZTIWd19ZTj0PVntXnFFjkEKrKN6dRBEL+McEvEVbHsQUT9H8i+AqBI3VHtIAi7WLNBA20ZZltp3E79oAxkkdTLO7uwuNRiMc2dXv92E6nUKj0YDpdLoyQcfv6rQcdg+sO1DxWZatBr1x0pgb+ngMXZHVqRZwohrpTKl3qgLC9N473aQ8arUaVCqVsOqRpsF273Q6UK/XYbmUj8OSFI02SS+Br5RO4XlLP8QCBmVCU5QeueLNX8q73+/DaDSCer0eJmCzLHPdFet13PMocy+uoxGqyRe6yMEblOB5aGm04ExR2ZmKokYjwltv63spD04fDz564Qm0aXR6jHGcNKXPPIFHWm/pTm1pbC+XF0cD4t3tXBdyHtKOmMNvU+WYxb/rAF04ZO30TC2fLq7kbUghlbUOe4LbEKPRCCaTyUpZsfFq6UDsa+p0ShN5lB95UBW/ozskpCAUTY8o6+qAMgJJVtBMa18rYBYLttD33BdNDdyVAV4m9hW141JtWisQSPPRyo4FbK124nYo/Q4ntpHfnz17BlmWwVtvvQW7u7vwxhtvwPn5OXS73Vw2PP7zTOxjWq89kQpp8jUWF+Dtb91HimnzjGXN95TSxWRxHhsp1W5YR/9kWRbiSwcHB+GYcQ6MhbVarXD1CN2Zhm3EdYOkH4ra/JINRtPSXXX8yGJJT3rla5ntT2mWxp63rSRf4bPPPoPpdArT6TTcAUx1JC6ckxYhXxWoXKRH3HthpaXyg9ob9Luifk7RdryOfuimERtvw+EQZrMZvP7667C3twe/8Ru/Aefn5/Dpp59Cv9+Hp0+f5rIN1gmpTmXGAFPqy39T3vfqaW/enL5Ympf8/xJfVGwydrEJePzUGFYmY6fTqXrGPw3CSU4zJyLmfODfksHPA6Je49/raEjQlKKnzFhaD5OtS0lq+cYcsTyw2j9PAPdFG5ye/o8Fzeh4aDab0Gq1AODCMcSdptVqdeVOTulbS9h5+0AyWPh7Cr4inNJl3eOp0amVyZ0MNPSkIDr/Riqf9w/vF+k73jaaXJSe4e5Uelcaz7vRaECr1TJ3xALApfbWII3HImPRK1fyjmErIBHry5icsQKJ0nsu18bjMQwGA2g2m9BsNt0BzTwBGU+752lji8YyDAvt2xQepTKNBhSsNvbSbjlPsfGfgpTgpyddapmp31E7j6JsvrBgBdKtculxwvi9lofmGGtHpvIds/QIU55vlmVq0IHbtvSbPMExTValHJ2cWoZkm1tpLWB/Yft6FxRhH63DRqQTIvREoJhtQG1cS2fT+sYmVLifRMcntpP3PkveZx77yDN+YtB41NKzAJd9Q8uHiOlFqd/yBMSk8suQg1bg0Lr/lNLEf/N8rXElfYvfaLLSo88oz9FdhL1eL9z5XKvV4ODgAJbLJfR6PfF7qwxejmTL0fe0HYrap5K+xPxjtmosb29bp+ZvpdfsYFqnWBmxto3Z53nGpQXuG+Cz5fJiorVWq8FwOAzyGMvHv2u1WrDz8Zh1lLsxOzqPrJH40xrjAKt3JaNdErN/YvSU5WtYeWs6IIU/lstlOFq6VquFe9dR5uAid8/iBUt2eL9LAaWT+jv0fUx/cn6R5AfmS08eWMe9wZL+0NJZ77+ooPYcwMW9z5PJBBaLBdTrdXjrrbfCItDj4+NLk7ExPozJXk6L9H0sjZQWeZtv8vDm74kbcJ7i5XN5rF27ptl3lr2q2aZF7YyXeA5LFuaRI17eLROemEYe+62oHVsUKeVvMm6kjT8rbpFHBnnKiGFlMnY4HMJ8Pg/GJgCsHDtKhRlfYYbHvXhXa6Iw5OnxvkkOyyHl6VNWjOZlZI8xW3aZAL7jRbiRtW6F4BEwHFI9Uib+rgu0gA99n+KE0Z0ieCcVwOXxFsvHAt/dHgscxhQFH8voDGIf8932RYG7b6VgldTenvEmvbfGmrULkhp2NN96vQ5Zlq3sHsa2w11d6FjhvWq87XESPs8xQ8vlMtzbZikuK2jH60hR1q4bTk9KGquvsT94EF9Kj7utAZ7XC1eiAlwslKB9sFgsYDgchuMLY8GQVJQdoLouZdEyAfRxWNaOY6mMlG9jsBwGfM//loIr/Nt1Bi9T9JP09zp5hQdGLeAOAwret7yt6XM+Xnl9Ma+HDx8CAIhXZmTZ6uIgDgwKelaIazIaF2TRXeMYgCx6/Bwtl5ft+Y4HoaU8y8K67VkeyKUT/RxWgAmfpU4eb21tQb1eDzRRvqFjXgq+Yh8sl8sVW0wqf51+DLZhs9kMNmKZerFMUP5F8KMA8RlNz4Ph/HsKSf5QG1zTRTyQiP8wuC7pkryBKk6bxFf0Pf/Okh/o3z979gyGwyH81V/91YoNjHavdE2Rh2ap3nT8eXxMSrO2OETKh/OJVgYth9vc1uQFtV+prOd8qAWjLUhjuWjcICYfpICYxvvaN2XQVK/X4c6dO3B+fg4ffPABLBYLaDabYXMCyuFarQaz2QyOj48vjXcruBcL+lrjlNod/H/6jRUfowt/PLxdBsrsMwn0RLBKpQL379+HBw8eQJZlKztgh8Mh1Go1qNfrpfuneYD9gX783bt3YXd3F3Z3d2E0GsGPf/xjmM/nK/IPvwGQJ+olv13zQcroi6vwD1908DbT2hD7Gq8LxJj6//t//w8ePHgA3/rWt4JNSONBAM8X+SPWdRJdHqDswR380iJWDSl6iNpmsbgBlaOYN8b1tHirhxbpXVFd+hIX+LzJnutQn3XGcsrQO2XG5zSsY3xatp1Un5XJWBTsfKcWzVwr1EOYBIlID+FavrGJJa3MVDq1ho4FXmP5euBxrDbNvJoDHPtm3XSuE96BmyfYDQArE3TogFvOqmecaEa69czjWPFgFQ0upTpBMcFIgzVSmxR16r0BhJS2wyA9Gog0qEKDUQh+5CZNR/NNHT+Ww5aCq/oW4DJ/pOgLXn5Mp9FJoPF4DGdnZ9BoNKBarcJwOITj4+NguGMQgB5datGv0RR774XlFMRwHeWyJ2Bp8XaqA0XLzSPr+d88kIbP6b912APe76xANv8bf3sCDNK3sbrSvGi/ewPdvCz6tzTWrCA6bws8Bg8nQyW6LBvHkj+YXrOf8B/qQJz4pbtBiiIPH3plcJ7+w3dau5Upq2I2fWo+CIsvuWygddUmpTQ5J/GiNXbyykTrvdSGGPi3xhnnu5iczysvrb5NsY0sX4fSKT2X3nP+T+U7q31jdHjhbRdN92FwdDKZQJZlcHJyEhY70Mn6FN2i0WXxB+XRGD9aQRUr3xg0Xvf4WJ6yPLaQpdO5TErJPwWa/vT0kaVbtfwp3Wi77+zswHw+D4u56EIn6rNp5Uo0cHuU97G33Wg/0Hys/Gj7UftSotFTJw9tHHwcF/U9eDl4x/TW1hYMBgP19C16ty/mEyu7KE9bwHosFgtot9uwu7sL+/v7MBqNoqdheXXeOuuR1y6gyGNfllWXsvLK6xfS39zHQSDPYkzh5OQEZrMZ9Ho9aLfbYZIW7Sp+76w0LmP2V9mQ+pieBmfpFOl5Ku9Tu9f7vWabeXg+VsYmZIuG6zh+NOSxobz2QSry+LEpkORAKl3czlgHUto5b52scvLaEnmg2ZQpbeyxMThWJmPpjtgYyliBL0HbbWDduZUCy4FeByOvI1AUK8/j1EvHocTgmVTTHDc60UQDTBRXoaTKRJ4x4REuo9Go0Cq3mADVhLlnjMznc6jVatBoNC7VnxuImKeULz1mKaU+aFxS+tGIRWO1VquJQYc88AR4OBqNBtRqtZV0i8UiHD/D89ja2oLhcAij0WilXVJp9vCjZlhd5VjkvFhUhlr5cAVP+RNXp2IfPX78GH70ox+Fvvwf/+N/wGeffQYnJyeXystDc9HAyIuI2E4VeocnvXdOSsv/jhlE3jZGGUPHk3dnI7ddsC40X/qP2yfrGoc80Ik7wTFAqd2pqP2W8tXe8/HG62/1rxW45fTydFtbW1CtVqFer4f7xXiZXLfg0Vp04QwN1NJ6WHQjfXQVO/2W7l60QHdoVqtV6HQ6kGUZTCaTS/fllgEvH3qDFfS4XxpEksr05K/lURZ44MjTtrGAF9ad7lblPDabzVQ72UMz5ztel7x55wEPhtOxmWXZpV1AVrAH2wzHJb9zGPOnv/FvaZExlz84OcMDCvRvfmKJtMOM0glg6wtJdvGyrfQpSP2WjtUUGY3feoBB5vF4vPIc+4vugosB+4/a2xx4Mg23JZBeqvv4btfYKT+SLpCCO9r49ECyExDUX4jtxOc2TWrQjI9TLYillW3Vm+pL2p64s8qiVbPnKT04KXB0dATT6RS++c1vQq1Wg6985StwfHwMn332GVSr1Ut+7XK5DJNljUZDpSEFXB8U1d/I37ydtBM7JP8/lRfot6nfWfKe04npqY+m7WKrVCrQbDbDAtkicrMsSOUvl0s4OzuDra0t+N73vgfHx8fw4x//GAD0Y4U1G9gzNmJ97O2PTaJMevLw6SZiJNh3yKv0eq9erweDwQD+8i//Eur1ejh968aNGzAcDuHs7Cya/3XoUzwZo16vw2w2u6TzAXS+pEd5W7qGjgP0q6Sr0lL0rSc+qY0rHmO6Clzl+HmJNMTihxZ/5SmL+kaSPbwpePwe+rdlYxehgZe/Dn0o5bcyGSsd92k1TJ7GkJwIj2HtzTeGGKNLQtMyWvLQ4E1rBaD4+5RgEc/fw2wxwy1WLgYfY0EJq9xNDZSyYRnQ+J4LGmxTaQLC62hrARNP4CclIErLkwIRGi08qCAFvmLwBD6ksuh72t487xRY/Ck52rEgRJl8rSlZScFpAZZNjTNefkwOFgU3lum/+XwOjUYDdnZ2oNFohN2wo9EIHj9+HI4s48f0pwY889TrKoyl1DK1uqXkQ4OyZfY5/T9Gk8WDKYGk1HdF0qaCO62ptloM1J6M1UNzXr06wuov75H/lFbpb7rbD/PW7DArCMZ5kOtMywanO8nyBkXzAsv09ENMv19VkIJDs5li0HSmlIbySCxv/IbybJZlYTehR/5I/Mlp3jQoj2t2sQbLN/XavfhM+i3JKMrrscCaFTTQaPLIDakcjdYYHVYbW3RJfSXpB95W+IznPRgMYLm8OE4UF8dI7SDRLfWH5INI+Wk2piRHU3wRLotj9GtpvHJV+50CukDGe/KO5k/xdxJ9tH29toXWHrS9JZ1p+RJZloUTiPCKLnraBeaF98SibYQTfKh/Oc9JdEp19vrG1vineXv0vyWPuK7W6JLK1NJZ5fGyrLERk1cIOnEuTdp4dbk1/orYLrSPcNE13kncbDbhlVdegXq9Dg8fPoTlchl2/Q4GA7V8j0yRZLqGVFmSxy+8DtBsgjJka5424QsoeJ/N53PodrthQUqWZSvXlWAaT53yoqidjnwPcLHQgC6a8l5zmEqDx87mecfsLp7WQkxevIhjZxPwyqt1IFam9d6rq6RvUuxDnq/XD/fCw9+W7ecdP15o487j13jHqgav/vTkr6Wpik///x9Yq0sp+AplC9TgjwGDDl5o9/PkHcyaMSrtYNACAOsEH4AYsIkdqWM5KNo3MTo0oIOzv78Pi8UC+v1+LqeL0209Kwt5jRgpIJDCx7VaDarVati9QzGfz8NdstpOGsuxsZzovJjP5ysr3GKTJtwhjn0jpeffcLmi3enKHbe8K8M1SIrCky8eO8mNQMkojOXp4Vnrvluef1FF5oV3vKUas95+lYJ6AABf+tKX4Dd+4zfCgpK//du/hXv37kGtVoN2ux2OCB0MBkkBPE2XXFess/8tI0q66y0lUElhtbcWJNRkTqwsmgb7WqoHDYTy93RRxrrHH+4EWS6XybsstQCGtDiB18cyslPbOobpdAqDwQAqlUoIaiDouJfkLuoUvruQHinG89Oc+tgKa+tbmn+v14PF4uKuXDy2r0xI/ajZYXRykKfhO2JR13knxjeBmF2aJy/NtqZ3fVHwgHi1Wr10n3y9Xg/2obTr9LqCy1UtEG8B0+IuL8zXkut8kTH93yNjaPuinbZcLmE6narf07qtM1iN+Uq73rSJNY8OLHu3OcoE2paTyQTu3bsX6Oan2CDd1n3HGjAobdnhUvAbkdpnks9XBDGfnI6bVBuS540nG6E8Qb6WvqPjR7JnKB9qbWiNwZi+89z5i//TEyzwW9pe+Pd0OoWf/exnoT54zchsNgtyF0/UqNVq8MYbb8B0OoXHjx+He2Ut2iV/RdqJYrUPIqaHMY1UJh3/tD2oDVoEqX5iTO7S/ozlifZFvV5fsVmwLz100fYoAxLd2O64Q/DTTz+Fra0tePvtt6HdbsM3v/lNmEwmcOvWrVD/H/3oR/Dee++t1BXp9QJ5lE5Qr1NWlYVN6M7Ys7LyjqVHf4TyLI9RnZ6ehn6v1+tw48aN8A53idOd8Xlo2RTq9Tp0Op3wu9frwXA4jH7niRPy9HSscHtC09uSftM2VXjjEVI56+TxdWPTtL+o7UThjV9KuoO+K0t+b8JnlMZAmXVI9R/zoAxet75XJ2M5EV5whilj8MQM9Ng3klGbWo4Fnq9m7HjqYQVePQ5ajDYtEEr/1uojIda2jUYjBDF5HT0OyVUiVcHS4EsewUnbRdtBLAWWeB60b/MezSFB4iXqyOE/z1EePF/aRtoYSD2qnNLloYPSoiE23mg6akhbO11p4MKTJ7YxpZW3HQ9w0OCNVAbP1+KTdY3Xqx7ztO1wlWa1WoXxeAwPHjwIbXh6ehpWyksLX1KNjJixHoP2zXVxxlLtB87TlgOTomO9tHBHS2tHSxZYfKDJt5S0XtD29PKldqyTtzwNXjvMm48HNC2dMLXGhof/pPz5hKOUl5Yft8OkvsK+xBXluFOfB1+LjvvUtrf61evApMq9vD4JQLq8lcYy9rE0UWT1r5cP8H/kVzx5gQZVtePayrT5JKQ6pR65o9nLkoyU7ESpfV8Evef1CzSZhX1Bj1OP5UPziNl6Gr3amKd2Jk8vyTXJ/7QQk83tdhu2traC/UYnFimfUJlJF9VI45PrUKncMlDUduF08nGhjaUUnpB8tRQZ6qmPlSf3o/K0Ge1XtPW73W7YoVitVmF7exsmk8nKggv8O3ZtUBm8IfWbZg94eFKTHanQxnssXUqeqflI9q3GFx568/hwFl1WWZim2+1Cu90OPMY3wvBd215oevVFwVXr8E0C5QxdaIbPEVRH4caM6XQqntJz1ZB09Ww2C7artIioCP2WXWrpK0mmSfa+FluLlR3Di8zjm6a9iO9xHcaGZVdxWLFd/K3JCa+tlYqYfvXYhUX7QfJ/YvTy74rGRjy6nSMWy4tOxnLDnBp/KSgirLwoIgzLDlxpTnGKw5lKRyxYvGnBmWUZdDqdsKOMQwseaOmvGyRjg48XNDpoIA2fS8IBDRa6M0kzFuh3+Dff6ZkyMaopBAzwSfxE7xqczWYrddR4TnsW40/clQAQ30nL+0QCfZcalLJABXa9XgcACHfASXc1a0fbciMc+xdpjQWmODh/UEjBvOti0F8FHTj+Go0GHB8fww9+8IPwDulBJ+hFgeZcXCWs/pXkKX+fWoeYMSWlp6d+1Gq1cESo51teDpfdFh0x480D/Bb1jWcXHd6tOplM3G3sDbZSGqRAIv6Pskhz2lP6nu944fRkWbYyycXpQLlpGeBUNuNuGn5XsJdm1HHSrlFsj+3tbWi32yFgzGlZN1Im6i0+5zqYf2ehTJ1tlUEnNvg9lnQCPk/eCD7WkQ8nkwlUKpVwTxjlR34fKZ+cTbUPYrR65FFe/tOCxh6bUAqe0W+pzeShb1N60aoj1keSkTwPatfRO025/NHKKkq/RLsWMOHl1+t1WC79R8drdGAZN2/ehGazCd1uF0ajUdhpgzvRaHr8Bm10PHGI5015i/MSbYeYryPxOP6TjsfldFB6rLbA9o/ZKeh78PLwuSS3NX/U6rs8MkmKO0nPPbu8ad/xMpfLi0nWJ0+eQLvdhjt37kCr1YJWqwXHx8dwenoa6jcej4MN4a2zJwAYswEtfqD1p/rJgub3liUb8uSn9bcmNyQdgXYcjUtIsOTpJkDLxZ28jx49gslkAqenp5doxztvd3d3oV6vw2AwMOOLXI5I7Up/X0e/8LphE+2CvFur1cSFo5xfZ7MZnJycrMTFrjvwPlzk+9lsVsp4tHicp/PEyfJ8R+XRdYidfZHxIskxL+9KKEt+l60TvXQU7SPvWMvrk8ZiPmXiksVCAw+WY0CDVSkCjk8IeBw6L7Nqjk8sLYXlJEhOv2Uox2DVXXqvtUPeYJaUxqJdoktLj0f6HB4ehlV+6MjgN8PhcMUB1lZMXQejmUJzuhuNBgCsBipxElZyHHh+WrtKBrT0mz7nx9dpdEvl8bSWHKCTr5KDQOvtPRI4RjMPhkjgwQ4pPz6WU9rH8472K67Up3RJ/UPbi9OqOfb4HQ8Ua7R4630VxoxHHq2LLqntsb0wYAcAK31ZhCZJblv8WgTa+PbojFjaPPKZGjNan3vyLRpIipWBR1FjGePxeGV1ryXz8pTH0+JYLZMPJNlJg3saD2j0lEkf5lOv1y/tBpTSSc84PbgQ5uDgADqdDty4cQO63S50u12YTCYrk+1Yf1o2t/uo7avpJw0e24nXicuDLMtCUIMv0ikKj7y19Kn0DOnDYCn2T4xnLFrWpQNifM11tFdW5e0b7F9qQ+KxxdJ9hZROyUeIPdPg4XOrT2m/xSYMNmXve3mI9zNtbysYgHWVxovXb+Q8KPEnpamITrZsRA+tnD5MZ+kbaRLWY4Pwb3ChY6/Xg/F4DLdv34bxeAynp6cAACsBbo1+viiS29XeMeCFJT+Kjk2urzFPLS0P6FtpLblsyZlUaPrck6dEp9Qm+LxarcJisYCTk5NQ1ng8vnRikEVnrB4afVr9JB88tSzNv8zbJym2Xpl2Iean5c3LkeRKyphK8cGsdtW+pTYk/65SqcD29na4SqPZbK7kh7IMd9FaV4FY9dLqUiby0nHdsM5YCPVvarUadDqd0K+j0ejSscXL5fLSIowiev8qwE94Qf+ALpjJG7/k/kVqmxSVkQBXEzv7osDql3X7h7wcTd6XMR65vtJsq5itHkOMVkvXevwZzf7J20Zae3hiFx66Y98iUr+N2QYrk7EoCPmdBJKTjcahFijTKsIn2rQVsdIqRk+jFzUyUxpYcjI1GqRz6i1arLxi32j0SuCDQqtTjD4+QJvNJrTbbbh582aYpByNRisrt+fzeVi5jA6Rtnq1bGj94OkXaTV+lmXQarXCLlh8PhwOL91bwgV2jBbPqje+Ct66wznm2NK6affPAMg7m3lAyHIQKK95FQE+8x5xzfnZEpKpd7FoPCo5hnSlo+cYQc5f2pEuNMCg5YXPab/xZxzSOJR+pwafPM+kctYNLsdoW21tba1MxvIJnNQy+DPad9Zx1pSuvMjTruvqC43/LB7j8oLLlzKCTPTver0Oe3t7Yaw9e/YMRqPRyt1UGu0pepXWxyOzUurFf0tGrHbfPKaTZA3P3yOLPciyi8UP7XY7LNhK3YmJ/6MurFarcOvWLbhz5w587Wtfgw8//BB+/vOfQ7fbvTQZSxeEcR1F244uruF9TcvPC/49bUtcFECDGmXf84jQ9Au38y3Hhy9eoIEXqbxYfpwWS5dJ9oPmtMfqw+vstQHygNs5qIswEEvTSeOa0iKN2zwOqdU33n4rOi7KgsUzNA3Aqv1FeRhlpqazYjzh8UOQTqvN6U5t7+642HOPbtbSeb6jfoq1UFMb7/RdpVKBSqUCJycnUK/X4dd+7ddgMpnA+++/DwAXQe7pdGrabdhuuDNNGldF7A0pfdG4hSSvPD48z0OKT3DaeKxAgmRbaO+0evCy+HOuk7W0vB70e54nTnAdHx+H58hTks9Gv+W+kkeucFpQjgDIdz3jP6uuUp70/6K8hvDWL4aUPGJyh+s2GrewbFDNXrdoK8v3QLub01GpVKDT6UCj0YB2ux0WgyLq9TosFouVe+QxDz5Jx8dfEf+9THja94sCyquNRgN2dnZCP56cnFyajNVs55gvBnA1/S2VSWOktVpN3IgTs2MkfUdlpGZz4LeW7eX11VPjV1c13j7vwH73yI6y9CDmlWLXe+0nKa3mxxbRx5Y+SGlLb3r6nWSrWPnzcjzt46FDQxH+iH0rleu+MzbFwExhDI9ikZ6XHfiI5SF1dFkClzNVHqOuKA1FwNsiyzI4ODiAvb09qNVqMJ/P4ezsLASQxuMxnJ+fw3g8Nuu6ruCiBm+7YxC22WxCrVZb6TekeTabwXA4hCx7vmMWQF98wBEbR95gDebFgx3euvJAkzXutDxjR/nFgoG0brF7sax8Ut9560dlI1dOdPFBHkh9xZ9R/vOWhel436TKwc8TpAAUPd4Od8pZ90UVLf86oWjQpYyguzcgXATS+Nra2oKdnR31uDMM1MV2TGAb8KCLZvesA8vlcmXyRgoiIY38qHkOjyMTM/Dz9B+2Mx2PUlkaz1YqFWi323D79m34zne+A1mWQbfbhcFgAJPJJAQENH6L2Vg0YOqBVgdqa1v54VUGZdvBXqQGUnkAQ7vz3Juf9VujNy/vbQISXVtbW9BqtVbkh4VUu8ir87Vvs+xiAWKtVoN+v69OdGGf46I06b7bmFOPz/A4V8zLoi+2sImW7QlS0/aSfB7uv1He1nay4HscE1rZGp1FcV3Gg6f9pbS87Tz14bv0kU/a7falhQ5lI9aH/OhxCo3fpHTcH9H0F07eoy/O/QLr2xQbQAJdQMD1gySrPWOZ1l1aLMXz4nXEY7P5NzT9dDotHNDF73HxtlYXSUbmtcs9geDPi38n8U7MbvDUnV/jkxrvtNDr9eAP//APodlsQqfTgU6nAwcHB/DkyRMAuFjYP51OodVqqT47f1bmxMNLlAvsm2q1GmJFHO12G2q1GpyenoZTBK2Y+YvUz3RBAfqdVAbhQpjUeIslNyVouuzzJA+/CIjFkctC6vwW/ztls4/Xf8mDPLGKWLo8OvWqx9h1GedJl3N6mEialJDAjW/6jP9Nn1mC00tbCqygWUo5qUxpBf+s9vIYh1KwoAhdXPFl2cVdsfv7+wBwsZOs3+8HpTqdTqHX613KgytRr7BIGUxanVODgRgYxh1zaDRgPvP5HEajETQajZXJWHT+PWWnTkTwYBDPC8eOtduVP+Ntm8fYS91xKtHBHfQYHSlKwfPOCrRxOikN2skB2tjl4AE62oaUBvrPG8gCWN0BS+VrEaPeKzO9wZVNggc8+ErNPDtii9CxrjxigXjK70VoSQkgFw0yxZ579dxisYBqtQrtdnvlFAQ6vvFYv5js0AL3kp6WHEj6LmW88LS4iAVPWeBOAr7n9xRZdfPaYZR2r52GY4/rLesbXi/+vtFowP7+Pnz5y1+Gx48fw8HBAfziF7+A2WwG9Xo9ev+cZzylBK61QDHPk6bH59LOjzICb5JuitU79l6z7Sj9qYESms/nDbRdcDEj1ztSu3P5UVRmxN5l2cUO9larFU644e/xfzqG8UhZahtp9pRGh/QtT+vhG0qjNyDn4dWYHUXvHk0dcynwBiWLlpeqazli9kisbK4fYjpM4sl6vQ6z2cycjC2zb1LiBJq/6m1nzSZH3Y/H3tPnKf4ntkuK/0DbUhoDNE8PvONXq1eWZWECQNJZSAvec4i2VGofUF6nbabVR5INXrmk1d9rh2n55skvD2J9X+Z45OVq7c/L1xbxWZDoHo1G8OMf/xgajQbcuHED9vf34datW9Dr9SDLsrCzv9lsrnxLdYlUD4nuTSGPz+/p86JYF9/kAV2MwuVOs9mEZrMJ5+fnl+wF6fSeGGI6cZNYLpcrC/Toc/T/ME0qUq+RsvQPfeaBZR/y95bP/xIXWJcMS7FvUmw1+t76LtVGShm7ZbSVp86SfI8tKpXyS4kned5p6TQb7CrA6V+ZjE1x3KVvyqhUrIE/T8KrDAMOn/O/vX2Jd6LxfCxYR0l86Utfglu3bsH+/n64KzbLMtje3g5p6bEbABCOJ44pPq3+6+QDj5DrdrvBgcf3W1tbIZgP8Pyo2kajAVmWwXA4FCfWeFleY4D2I5av5c0FX+y4QP6t9E67u44HSejzvIahdScqLdMb4MozSRwbZ3Rs8XpyI1pysGl70jbnAScvYoESqb3493mwTke9THCljO00Ho/DszJ36m9qV6SE69gfXK97ZENe20NLi8G16XQKtVoNXn/9ddjd3YVbt26Fb/78z/98ZSFRqrOGQTzkNzruPAECT5r5fA61Wg22t7dhZ2cH9vf34enTp3B2drZy5G+WZSs7f/HaANRV3GD11i9PcJIfUbW7uwuvvfYaHB8fQ6/Xg+Pj45WxSAPm/L47zBsDqxS4C2hnZwfeeust+PDDDwHg4qgsPKoQ0/AgAQJlg3Z0PEXqce+ajJZ4fhPI4xNoaeg99nTSnwe2eV2L6BDJ+YqlvS5YLBYrsgbrgOMzthhk3ZAWFfIxgZOus9kMXn31Vdjb24MHDx7AYDAIR73jRLPlh1C7iMPyi/g7r3wtarfE9BeeqlD0lA1pwsBLuyRjUvxHD6hdRfWdZG9J3wLod7lqmM/n8PTp03CvMsCFvuC8gzTQI/q97cbb3dte3viGJbd42fyIYYDL48Sij5/2otFojSV6xPNyubrLSxtPkr2t0aD5/VIeki0Qg+UT8XJofbw+gZcGz7jQvqV/e9pPKi8vrsLPo+MgxTb18jd/HisjVQbQ/OgiSX4az+3bt2F/fx+ePHkC5+fnMBwO1f6lMQV8npenPs+w4jabsqkk2T4ej+Gzzz6DVqsFOzs70Gq1oNlswmuvvQaTyQQePHiw4gfRvK5Tv3rb0ZPOk0/K+ORxN+vb2H3xGKfLW9ey7a2XkMHt47Ih8YAWc7XszBSb4jpCi3NIfI/xbYR1ypxWRmpfblK+WzRI9bw0GZsq1DVBVlYwPy/yGh9lBJ84A2qOREqeWlprUFvl5S3bCpgAXByrcXh4CLVabYU3qtXqyrFEPE/PKtMiPFRGQI8Dg4qTySQEh7e2tqDRaIj3zqGjqAV4NYVv/ab0We/4e09QSgK2o5SvZtiUqQjzjBOr/zx5FDEWY0fpWeXEyk5ZgRRL55Fp2vca/xV1DjbpXFBjSnPyrTFRBHnlbxntw8duKh3etF49lVKuZPwW0R9UfuERoXt7e3Dnzp2wY4aehECPwUsBl//evpSCOFIaHKvVahU6nQ7cvHkz3L2K9x1LbUGPjaKyxWo3zbbhASH+TsqD8jZOJuOd63zltLSQSXOGsC3a7TZUq1UYjUYwn89X7ini498ToPfqhizLXA5WmQGzok6+ZZ/GbEApH+wz5At+TDFNH+PvooG0FPqvApQ+605N3sceeydmA3htHa9diukXiwU0m03Y29uDR48erQSxeJ977BTJV43J0aL+g0aDRaeVnxUIzGurU/nP08Tky7rGAy+XBkF5uV7+lPwQmq7X663oZ+lYdCk4i89Tg0JlBXlivKXxfWrZNH9p3HjlvyZ7rN16mtySyrLot/LQkJqO82qevFIQa5sYNPsqj20fiycUoTOWr/ebWJwiFXz8e2xuXq6WrzS2sTzuE6AtPp1OoVqtwvb2NtTrdahWqzAej8OCS2ks0vIs+/xFRZl2spX3poF+Al5v1mw2odVqQbVahWq1Cs1mEx49ehTSp/JmDHl9Bs2OT/me65cU+8eTrmiMyrI1Uusae3eVPGihLBvnqmG1cxn1o/yr2UdaOXmumpHKuw6gNGl0aYvVLfvTC8u38tg4ZcYJPLaUeExx3sItw8iDVCPhKoTDdaIvRsu6B6h0H2alUlmZiMVdo/R+2Nhq8HWtDMnjkODfuMuXYjAYXLpj78aNG/BP/sk/gQcPHsD//t//O9R5e3s77JS9KkOYrzyJCTytvbhw9QT5tDKkvJFWCr7aOQZvXbygQXXehlxuxeQgDfzEEBsrUnDLoh/viKL0eBHr57zj60VG3npoAdOyQfvbi5RgTOr3sW84NB2m/c4TaJDSz+dz6Pf7sL+/D51OB37+85/DT37yE3j8+DEAXNzftLW1pd4pi7LCai/ckYKTo+sYEzs7O3D37t1w9PJ4PIbJZBJ2peHufdxFSuUbTlKiwcx3sHEUDZIgPePxONBZqVSg1WqJxytL3yO/U7nYarXg7t278C/+xb+A2WwG7733HvzFX/wF/PCHPwQAgP39fRiPxzAajUT9LAXAPXXBdkVe0MahV4YDrC7AWZctsQ4+1E6zwKssTk5OAADCeEJe5PRwRylP39Dv6Xc4FtY1FvNiuVyu0MZBbRP+nAfsynQwywCXPRxSkJm2A7WlYrpCm1C6KuC949PpNCwOAdB3/9K2kGwHyic0jRWY5yg7UM/tYysYxfvVC9r3KD8+/PDDENCmi4ykbwHy+ZxWW6Xadyl2VV5I40Kz8SS56u2TWq0GOzs7MBqN4Pz8XDzGVbIVJD7B/sTTtKgfGAtoSvaXpWvpyQ20DC0PbVG19IzrMMkHTIkXcT+Z0ol2gXaKkjQhQW2+TUKKJXBY7WJ9i7pFutKB559aXh4Zaclviul0Cs+ePYPj42P49NNPw3Nq12r9JNlHGr1XbeN8XiZ3ioLrLoALX+vo6AhqtRp0Op3wHNtrOp1eOhGobN3tRdE+5PatpA+s8qgMiZ2a5x2/mg5cF7yx+qscM5IefDl+L8DlMuc5jANIC/H5KSKWXSHZEpuC5n9q8RL6np7ipMVCUJZVq9VL5UynU/H6kLx6LRajvwqsRBK9k3t5YE2OlJFvzMkro6y88AhaKeCgtVkZ7SXl7VEE2u9arQbNZhOq1WqYQFoul2EQ4ZFc0+l05ZhiroBTyufwCClv2/G8aPtsb29Dp9OB0WgU7vHAfPf29kJdMcCCK9twQle7zyDmMHjqFOP/dcEbmIjRKuXrGTeefk2RA1ifWABDkjtWW6QEXaz21N6ntEdqsCWVtzzG9HUJCluyDZEyjng/WPVMDUCkIsWpSDX0vIEM2h6eulGarUBeniAm7ROr33F3ZKVSgdlsFhbf0LQx49iye3geZfQ3zQedg3q9DvV6HWq1WjD0MUDKF3vEApE0Tarz7x336JxPJhORviy7WByFAUer3bANcJdwv9+H0WgEvV4Pjo6OYHd3F5rN5srxzZxezR7T0vLn/O5bDzzyyPOd9L4spzrPN8jveEIIHmsZ+yZV9qbaeZsKuHjlnjc/Kx9PuqKQ7rPV6MDfaAvj+PWAtoumL/F/rY3XHay0dBZPh/9zGePJV5PJWhuk1jlVh3ny4O8kn6cMvY4TUXgHaqvVEsuTwCfjNH+M5hGbFOHPUv1czzepcpjbYjE/w5sn9ZlqtVqYMKBleeilfM77wqsX89pUWlzEwz+xdzFbykqXB5x3tHFclt9Xlv1KaYqNRf6t5pt79AOFN12ROmvtj4tHsuzinlhM57nKy0sTj1tsyg+3xq82PrRYDP3musQRioD3LV6XgjtlpZMFuaxct42zSWgxGI2/y6h3nrhPqk4owrNl6QYPvHZsGXlpaegzS3/mQaxvvP3ubQfNvqVjFuMFmt5OpSlPzEIqV6PfSiPVgY5fTVZhG1Bo1y5KdmVqv1B4+KmoLailSZqMzQuPM2c5Bd4yvEZIkXK8iqBocKuIUR5jklSFIwlDnsft27fhK1/5CoxGIzg9PQ1p8O41AIDRaAQPHjwIAVZcGU7zuypDggsaOvipQ5llGfz9v//34fvf/35oy6dPn4YJ5qdPn8Lv//7vw2g0AgCATqcDBwcHK0KI3+/K4e2fmPLypPXCw/feMSzRohna0s4zjZaUe5JSghw8nbTSWypznbzsVQT8LkaeJrbbDd9tygiU6lVmgCKlfG//eQKpKeXid2WgqF6N7daRfq+L771BlNRvkL+tXej1eh06nU5YUIMTm95xuI5gmwY8Jl+azKS04N/L5XIlHd0Rm2VZWGwkGblWoESDJ+1oNIKjo6NQLvLfbDaDarUKt27dgtlsBmdnZ5Blq3cwosyj7T+dTuHx48fhHlhvQJoHYjXHiLcBpsXV65hPkVMJpDLpM9pOHv3En62TNymfLRYLODw8hEajAQCgTubxtpfqLo13rq+88sizK6hIG8W+8+arTXDEghXrCNThMeIYPLbaB8fCjRs3oNlswpMnT2A2m0Gz2SyNnqL8G7N1UmwC6Te3HbW/tTw1/cbfFQlIlA0poIY8rOlcWh9qg8T8GJR/qKfH4zFUKhWo1+swm81UWbNYLILP5kWWPT8xCWVv3vGVGqzkfCr5U7E8U2ylmHxBvsbf9Xo9yHs85ULbIYvfSW03mUxEv06S1VTnSzogFvtA/ewdM1TXxvqd2gOx4Ke371J8/3WgqL1Q5FtsT2kccNtAshWksSPFMih/UmjHK+alm74DgLBYDWNkW1tb4QQFhDQ5d9VxibIhxR6/SDg6OoKTkxOo1+tBdqDdTO19inXYeRo2IQNoGq5DrMV83riVdIKA91tNpniwybhaHpTpF6b2c8p360Ae/1z6hspotC247SKd+oibua4TNPq1fqPyCRfm4ampNK4fO7kCn0s2n6eNUnVfWb5Taj7qMcXrHgRWo0jGUZnIE9S18shLpyXsuEKNtZfm/HvaMkVQak4nDZrie7z/AAcMnZjlQbN1Gg9efqaGHwpI3NVL6atUKtButwHgQpC0Wi2o1WrhyL3xeLxyhyydcMadtFhOkckSrU4p+RUJpEvlcOMkpX85H9P+0PhZC8pK9Fp0a2nzprPGpJWnVi+Pk6WVze9v8hjrvM21fpSeawHC62x4AuSfTEwJqPLyYmM4j1GI31l8nqLfNhngoXlbgTPruxRI8gzl9WAwgMFgEO5okoJ7ecpFHbiONqRGK+oaPKIYj8n35mPxpsUfEg96+BjbuN1uQ61Wg8ViEe5jx0nWvb09qNfr0G63gz2BwSotOAFwMbn7ySefwHw+h16vFyZxcRcCp523hRW49o6lmL706Atse9oHmq64TvKW81Oz2QyLG8bjcZh4x37kx5bxOuUZO1wX5dFNV9GmHv0Sk/XWGLXyjtU3trMV+7TRaMB4PIbT01NotVrQaDTg2bNnoQwcix5IssniC56WT7hY/GXpvjy8QG1+TzC1DH7j+aX6B9KzGF3ecZp3HHvLB4CwSAdg9QhXzvuSPk+VC5aesNJ5QeV/jD5pzEu8EMtPsnuseqFOxYVPMUhxEDpGeFppjKbydYwWjbaYXU3pkuDxn7S0Fn3abw/ytF2Mz6V0Zfo6kv6m/ByzsTX9l9f384wfK3+Lzy1b3Esf1TvXyS6kSPFJvyhAWxgnMyxekOR4ETvZ095F+iTPt5Lv6Y2RafLbssUsmmMxupSx/hL5EGu7dcapeBlWvJPaWRZNksznPoNGQ0pMIi889Ft2meWD43saB8I6oU2JC/swLbcXMV9aBi23qP1d5recd8XJ2BdNOGya3jxBCwoU+p570IoaKLHBk+eOkOl0einPyWQCZ2dnK2mm0ykcHR2tDB68Hypv2XmQ0jdo8ODK3sFgcElJS2eX1+t1+MY3vgHb29vm/abdbhfG43HYvbOO1S9c+GXZ6qoSS2nw77nwks68T6WLC0ttPHn7regdw1JZHoebppH6XFJEHkMXDUMpf23nk2WI0nsUqYKM9d2m7w/S8KLpIwuSoaD9zgNqGBZ14i1aYzRI46loENgap1r+KcEH/L7RaECWZfDw4UNoNBpwenp6adcMH4cpAVx+f5210z4VqLsmkwn0+/2wc217exuq1Srcu3dP/Ib+z51+rwFLHVr8PiZbqTwajUbQbrfh1VdfDd90u13odruQZRe7kN55550wEXt6ehp25MXuRj45OYE//uM/hslkAt1uF0ajEVSr1ZXFUQCwErSX6I4FmLWAGy5Si8n9PIFJdGCQ7rwoyoPWWMM+wn46PDyEGzduwI0bN6Df78Mnn3wCi8UCarVa2D27LrmP/SjtLLluKMN5XCdidyHPZjNoNBrQbrfh+PgYHj58CL/5m78Jh4eH8Mknn4R7oZfL5cpEmcc2wbQePUXtbWknRIx3PUjx1TiPa7v6uEzw2qm83JgtQOu/iaCOlL8UOKG05L0jG4PZ9XodGo1GuF6mWq2uyHmvXWzVJSa/y5oMyZtHig+Wh4bFYgHT6RT6/b67HeliUcwTJ3M5f6Cs8PpRmEayta04jidgus6ApybTrB2Z3GbjsYXrrks4UnQAvYKDBq5jJ/pQP0lqH8yHLqi36NImWpB3ub7U+pH+r72PAW0urndelP734vNWHwTvt/l8DvV6XfUxvgigNp81biXQU+J4TBQnemg5aJMiLLuI6y+Pr0z/LhKrWTeuK13XEXxuR4LXZtd2gko6aJN9FIuNU3CbnZ/AJtURF5688sorl07GfPToUVjEi3kAwKUF9Zv0YWLwli9OxubNLPaN1FkpQUwLnm89jpCnrtpA4AK2KCylWyazFW33arUKs9kMTk5OwnM8EooKjzyBRoTHOdJ+x76nAgGPhZHa/s0334Rf//Vfh69//evwyiuvhIBKvV6H0WgEP/jBD+DTTz+9FNzFyVuusD3g/ZzXofKWS/sJjRGtPXnalMCQ9VsqS/o7LyzHPIXP6LcpPJfK/xp/Ux7F/vU4iimKPNYu2gTYVSvAq4bHYeL9IY1Rrh9j/ImyC//HADSfAEyd7JGeeQJhWFYRBzI2vlKM01j7ZVkWgoDL5RKOj4/hpz/9KZyensLx8TFMp9PQth66rT4tog+9GI1GcHx8LOrjGPIElrV+tgLd+BzvDz0/Pw/P5vN5mKCLOa1cJ+FELQbqMBDGj7vUxkWKbNV0CganpTKK6gdaV89RdakoqqcQlI+azWZw0PA+NDxpBI+klILR2rO89Hr6VEq/DlhBV4nnOe8WpSvP95725+OdBriq1WrU7qPf0b+1yQkur+hvKtNj9bDqZEErP5beC8oPRXWH5rPGJgK84LqZBl5QrtOAk+cI4lhdpO/4hAQec48LcfBkhVh+nBYck9LCXI89Hcvf+00sL9R9qD+1CTr+W/K5NL9N8itSbU1NjnU6nZUrCHBht7dNqC0MAJcWY8S+5TTi87y2LG9L2o48VuKJa9A8AS7v+rdQhj7bhA3Ly+O60MtrvN80f56XJ/G1ZD9KvyW7xfKHJD2PPJu66FzSidaYve54kWgtCskPAYhfcea1Pa4TYuNASq+9pwt7+FiVfEc6Lqgd4BlvqWPpReqTzwvy2FtlgMteWhbls1i8b910WjRoOsrSoSljeblcXXgbszkBLja9bW9vrzybz+fQ7XYv+cr0t+arWm2bYtuU0UeuydirAnc6tQbdlPHpZTStYz4PwpjXF48gm0wm8PTpUwB4Psgortv55wg6eDFwi84br8M3vvEN+Nf/+l+H3+PxGGazGdy+fRuePHkC/+bf/Bt4/PjxyjfUgaS87HHoLGcsT9AiBm7EAIC6E4imk1Ywa/TGAnDW/aYarAvPNXgDgTHwlfVSGbzfY3RJDhwtTzNGPU4pHZu8DCnYlAdS0PMl0sH7xBN4r1arUKlUVu7hQzmVEkTCciyHhAeGvXni357v6C5NyUGN6eQUHka5TwOsT548gbOzs7CoZrlcPd2B1ydFZmvpykS/34fHjx8H+rBueOyvVb4kD2I8mGqT0TLq9TosFgs4Pj4OOqXRaECj0YDhcGjKFaq7MCCJk314WkUKX3BH3uJVWmdMh5MP9L7dmH7AZym8gHnjCtKy5O66+HF7extarRYsl8vQp5VKBTqdDgwGg/BMCkp66ZZsN3ynjVv828p3U5DoxIUKaEdvOgguwdt2AKt8SRc8Wum0Z1QO0IkmTpOUXqJVC3xqdfLqrnX1jRWwkey4lPGD35RBu+RP4HOU0XgCET9W1CNvJfCdg1n2fDIWF8Zsb2+HEy/4XaZUz1k04AQh+nb4zAq0XQVqtRpUq9Ww0IXLD4D4pJSkn7gujQWurdMHtG+zLIP9/X1oNBownU5hPB7DkydPIMuyFX0X4xW8xgDv3ZQm0Pk39H9eTup4ioHGG2KyyoJmZ3BY/X0deBahyVnaD/xvapvH5LS0y1jje0+7SnIO+5WOG2kBkQZrEZH0m9cZy6aLc1HeSnriRcWLTr8X1sk9XyRo/hRduIPpJPBv8T5LTM8Xr2vfI99R+8Vj10q+30tsFuuUGXRRlNT3nvGr0ef1m/JC8us03RZ7ZvG5pL8R2nhqtVqX8plOp9DtdlcWVkoxdK0tr8sYXMtkbF5HKpY+5jSsC5ZAT3VKitRfe8eNKu/Z99yx0piVvl8ulyuTX/TYB5omtrJoU8rIY4wDPJ/EoMfdaEDam81m+Lvf76/0VbVahWazGRQ8XZHLA0g83xRQYZLSltJ3vL+koAwNsMT4RXon/S39pmV5kcpLUt08+WsOM39uOXCWkuNpaEBNajeeBx+TdDcYp08rN8WgzIProgARZdOTyrtaHp53WrrZbAaj0SjsjMWgFHXEvX1dJrx84zH28LmUPtXYpQZ0lmVBduPdpKm6PW898wLH/WAwgEajAW+88calVYTVahVqtRoAXG43jRck49yClSYW1MJAK7U1KCaTycqdsVoZAM9XoOKds7PZDKbTKYxGIzPIp9WFB+p4PfjxcxrP8LEntS3VDzSYx+E9YpfWDY86Q5oxOL3OgCzVQRaNOIHOj9YHWK0rzUc7slbCdQyCWMFgHAPVajXUD4+ssxauecdp0b5el/91nZxkCXn8MnyW1073QNLr/P91li99D/D8mEW8DxzgQibwa2Dy0EZtY26j43OUJ/P5HHZ2duDg4CAc4Z8H0+kUms0mvPbaazCfz2E0GoXFJFagzat3eD1TweVjtVoNu6q8dqX1TvP/8R291oHHDCxwmYQ7IdrtNvT7fXj69GmwEagPreUt6U/eBtI4pfWybG2PHW7VFb9DXrWuLorZCRokO69sWDa4t11itiEHH/P0ea1WC+Nd8ts5v0p/x+xeLa1VJ86vsf6jfeaJ62n50AXYVlnXWedaeFHpTkWR8fV5AK0r2sBSm0ixg9lsBltbW5dOPqQLA1FfYD60TCtmosmTWD2s79ZlW6cgVSZf93I41lmmpjt4/I3Tgzwdi0fwcspsQy3WwXWlZZPQ9PwZ1WUSzdPpFB4+fAj1eh12dnbCc9xUQlGpVODw8DBcQcXrgQvxtLG2LhmaamuvTMZqHe0NUuVxGlNQBqN5goE8vZbOur9D+yZVaMcCgFraGPixUfg9Z1ItOETzoXngQOOTjRK/lOXoc3gGAL8XDydiccKC58MFZJZl0Gg0wgpdOjELcBH0brfb4TcGI/DblKAZ0hITxJ48aPkxQ0AK9vEV7LFv8oyNmPOUAo0HPem196kOudVvXmcM02gOKP+Gfodj0eq7TeKqy78qaPxnyTGLZzU+pMHHwWAQ3tPJ2BgN6zJOrN/SOzo+1u14cnlTq9VgNpvBcDhcoStFJ0tlrANI12KxgPF4DK1WC1555ZVLbVapVMK1Ahbtmhzjsi5Ff3lkcZZl6u6i5XIJk8kEptMp7O7urkwqSrSjLsLJxuFwGGwTquc9SNU/lrzNkxf9juqBFHsSv8O7EgEunJvJZFK6U0p5A/mSTk5LOp4GTJDX8J/FL5QvU5wfTmeROpaRTvsWYHUnBA/Wa7Ycl6Fa+nUHQVJsU/xbs3Os4Da3a1NlrTbOykSsPgiNBk8QQZINUt4aTWWB9gfKXVxIk2UZzGYzOD8/FxdRWLo+ZgdzmULbcLFYQLvdhhs3bsCDBw+S64N54nHHr7/+OozH47AyfzAYrIVv8oC2k6brYnRafSDlj8/pccLeXVxSu9VqNWg2m3Dz5k2o1+uhHLoAR6MX+YMuXEnpF80GLROxWIBUpmRPeGWsZS9RGvLKBCkvLU+PXWTRo+k3gOcTLFRXeuwjT96piNWbQ/OT8vo/MRsK01j2ZFG+KAIv7WXmeZ2g8YOnL4r0W9HYmxeanWeVze0+nh/Vd6gjcEMM3z1Pjzb2LOjM0y4eOea1DTeJdfqF6yznKuDVc1YcBOdluD8UK0PKqwhSdY2VNmZz0DT4fDabwdOnT6HVakGtVjPjHFtbW7C7uwvD4RDOzs5yxVti/oY3H4k+L6I7Y3kQDWEJyes8sLyGdWo9UtsJv4mlobTwYEOsDMsh12jhz7hQkAQLvSOV78DzOhgx5DVELfC7YjFIuVwuodvthjuFXn/9dfjn//yfw2uvvQaPHz+Gg4MD2NvbEx3cer0Ot27dgslkAmdnZyv5S5d6e9vCGm8pwa51jE1v4McSktSAkibzY+XGdt1IZVJgO1qK0vrOU56XPsrrPOhhlaGNNXpkS6xOKTLjJS5D6+M8ber5hk5w1Go1uHv3LhwcHMBXvvIV+MUvfgH/83/+zyB7+ESURasmwyV+Tzl2y6ojLY/yv0WbBcrvMZ3Ey6KBVcQ6J+/KAPIB3mH+wQcfhHd44kO/37907xC2DZ8EQ1g78Ggbp8o6Csm2oWUgDScnJ2FH02AwgHq9DgDg0hfakddYfpb5TjZJDa7F4LVtpN2i2jjRsFwuVxZo4DUTuHM4hcctSAFkDIYslxe7zafTKfzsZz8LO/ipHanRLv1PTxzhgUdpsSCXCXkDed7vUvyMPLykBetj8m4TMgoD4sPhEHZ2duDmzZtwfn4eFlVw/0GjkfshKMNwzOKEi7SjzQpmUJsTn/N2y9vPlEeQHmmHEucB6e+YzuP1yQte5zKCubHvqe2PbUHtE4+M02z3FJtXWiwrAYM/r776Knzzm9+EJ0+ewLvvvmuWsWloeo6+9+YDYE/K8nRUX+Mzr9/F9e/jx4/h/PwcDg8Po7Rq/qUWt+B0Ur7D63nWHYCT/DucLJAmBlJluiZXLJQVa8mjW4uUhad8ZFkmXiWC4D4259XY2Pf4Erw8AN99vlJ/a3ooZoNIdZFiUNb3UrkSiugJb95535f1zXXEOtr7RQK3qajdr8l96d5dlBn4HK9PwBgw/V6CFnuT0n1eeE+DJfc/z3WP6TvNPqFYLBYr/pAWa+MoS2dLdBWV7dSWQZsK45Hc/vOCnjSKiMUPJLq4fXSV8tScjI01ksV4VzHo8hgPnvw87yVHkP7WHG/+zHJ6UgW5lJ62kYfxPA61dglz3rzzCnJv4EL6hgYEACAECwEAOp0OfOc734F6vQ6DwQB2dnaCUYs7kfB4KtwRi88RjUZDFKRSELootABfTPB4HbeUvtb4mvOmRJvEq+tQ8jGFYLWTh2+lvHi7aEEw6X5XCZps4PWK8ZomL7R0KTxgYZOO++cFdJzgYpJOpwOHh4fw9ttvw2QyCavK6GQsLjrRFjBoY0F6vmnnwhqnkkzLo5cQaNxJddxkkCm1LDzq7vT0NDyr1+tQqVTCpGUszxSZgSjDaKf54DNaPh4ZvbW1Fe77Q2NfsieofuX9mJdOTx6ptlXMHvPKZascLIPemVetVqHRaIjjpGybhP5DWdTr9UJ/4lURVuCe/63ZBV4ez8MDZbYL7/fYGPIG5y17aVOyi9ow0+kUdnZ2oN1uw3A4hOFwGCa/vMe/0z5HnkFofJvCz5wfUvtCSmvZZbE8eSA+1aex3qf69ak8E6ufZP9rgSfPSR6e95g/Bry8tqtkqzcaDWi1WtDpdKDX6wX9SnVN3v6yvvME9OhzytP0Wy1YbdHoKVvzbzRo9hrm0e/3V2wAOsmWRwdK5XGepHoq5XjlWN1iPrVko+B33B/25sfTxWyXonExj2+Zwt/ecmj/0lgOLZPbglJePB9P2al0xsa71U5WjMaTd4o/QdNK7ebNg+Klj18cef2WzytSeYxfR0S/4zKYHotq6SpetmZ3SmPoRe5PSZ5chzGe14/16Civ7ZkaA0DQRYGp35aBIvlq/pPmF3nsQ7rwFiFdAYX2mhTXLBInsPIosw9WJmPpqil6rCpFTCFf5UDkhkNRpDoSZeSrOUixFc+0zp7V0dL9oDFaPMIhFvAsoohShJyVR7VajToyFnAnB8DFHXb/4T/8B/jwww9he3sbsiyDTz/9NATy8Y4C/E6jKQ+0IFZR3qc7iKxjoIpAG6Oz2SwEalON/5QACNKA/3uCebysVEjBZC0vj+EnfcMVYErwRhu7mzAWr4MB9yKBHzn89ttvw/b2NkwmExiPx7C7uwuvvfYafPvb34ZHjx7BgwcPwj3WnEdSx00epzwPvGWUtZuPI2UVOUdZNkjePLa2tqDZbEZpokFOAFjZRY/le+6qspAqR2LpptMpfPbZZ6K88+QtpcsT8C8bdBwul89XfHvuIIy9Xy6XYXc0Oit4XCjABa/X63XX3chFgPXCHc1Pnz6FdrsN//gf/2MYDofw85//HI6Pj+HJkycrO/q94PzA76ah74uM73XB4mfc+TmbzdyLKqz8NwWsy9nZGfT7/ZXjRvFuPykQRv/m9iD2LU7eU5ue75SgY0Qb/7F2iY0zzWbEscuP584TEI9BCtRbduZVBAFR13Q6HZjNZvDo0SNxYh2PNMYFROgT0DRe+nHXP8DF4pNHjx7ByckJLBYL2NnZCTu0caIPgzoxPHz4EP79v//38M4778Dv/M7vwJ/+6Z/Co0ePYHt7G5rNJpydncF0Ol2bfeLBaDSC8Xi8Mjm1tbUF9Xod5vP5ysKcFGi8JC2QoOM5D89lWRbujX3nnXfg9PQUHj58GPQD96skfYH3+lKa+JjlfMjlkOZP5Y1h8Pahx2lKNHLQcmk78NMztG89k3dWmXmhtVneALqnPKuftAXPMTvRowskpLZhik7iNiPdZa3RSm1Empe0Q/uln/4S60AeG0eSoRLooi46uYP8TXUgHytaHE4qt+ik5Is4tl5EmosgNR7NIcWAJdms+fzSsfsvQh9QPy5F/43HY3j8+PFKjAoAYHd3FxqNRrDdAS58yZs3b8JwOFy5OxYXVPB5H8vnK+In5/1WvTM2FrS3iPDAM+GRJ19v+rKCpZhXnrx5MDG1HOm5pqxS4fm2qGFufc/bxTOIvPTwvqHHM9IjcvE4qk6nszLo6dEVs9kM7t+/D5988knIezKZrGzL99wtzHkm1jZ5215rO8xTCogV6eeUb7H9tQlgb0DamoDkgblUGj2QHH4Pr2v5aN9Lsph/k3dMWOVaz2P5vUQ+SO1JnzUajXBH9fb2dph4PTw8hNPT0zC2+fEgALYxgu+5M+41aiTaPTxZNv/EDGPrm6sIWhcFleN8YkoL1Ev8kLevvHojlTfoMbdZdnGvr5fOvEgJrsXSetqq7LogH3Q6HajVaiEQT3ek8nvFN4Hl8vkRtTdu3IDRaATPnj2D4XC4Iq8A4rsnAWS+5nYVtXOs71LkT5m+hFUGQJqe3qQO5jaPRA8utOPBXToxQP/n9qgWwE8J5Hn7WLPFPUE4T9ne7zQaOB97g+VS4L5sPtH84Sx7fqQ0/pZ2HublZWuMUBrwiGy6KCClHMxvPB7D6ekp3L59O0zA4gQy96Ok/L32UhFIvlSeeEps3Hj9V6s9eF7UHhkMBmESuVqt5jpG2IqTcJr4eNLajdMaq1uMxhRI5Vq6TyrDww9FY1RSW64T2NcenvXA8r1oH/C/U8qI0UXll5UG/7d0Q5ntH7NLvOm9sa+XyIcX2ZelsPxKSxZLafGfdj1BkbayxgMfA6mxt5dIQ4qdVWab543r4nMvLVTWb8KmtGDpypQ6IZbLJUwmk5VYQJZlYbMoP66YzrfwfGI0peqwshG9M/bzCq8D7UVeR9vjiOLzdShUTSBZhqy2i0RaFcsHhhVUswayB14lTO8MwOOm8He324XRaASLxQL29vbgd3/3d2F/fx8+++yzkEej0YBbt26F/E5OTuDJkyfhNz/2L29AW6tbHqfN07ZaAKEM8GAdggtP792vNN/Usbcpwz/m6ErBBKRHGyeeuvJvrcUAfHy+NABtbCLw7gWVMf1+H7a3t+Gf/tN/Cnt7e1Cr1aDdbsNrr70GR0dHAHCxcqzRaKwcEemBJif42KULVjbZTmXz7HXp4zx00EAMXUnoMTpT2nFdekID1mU4HLp2flB4dLDXiS+CGL2xenltRV5mvV6Her0Ov/ZrvwavvPIKzOdzODs7gz/+4z+GxWIRJkJ7vZ6/MgWBx01jUB0Xvb311lswmUzg448/DvJqPB6HiTxaLy94m+bta61vyhwH3O7GvHGHHd3hllLuuscqtfeRNh4URtrH4zEsl5d3H/IdCZa84vXX+nS5XIa7kOmiS063lm+svkXb1RrvUsCA61c+WVTUttWC5Km6gafHE4K63S6cn58DwIVM397ehul0CsPh8FL5KavoPfKd1wVPN6LvveVNJpNgQ52ensLf/d3fwWw2gzfffBNOT0/h7OwMlsslVKtVcXIoL9DuwoCUF6hb8P/JZHKtbX3qi45GI/iTP/kTqNfrcHh4GHglBmlCKpYe9RH68HSc0ysRJHrzticf2xa4j47leq6Kij33+M1l8Yw1SVyGrqKnkEltxtMgDTyO5Tl9LK+MzDL95DEux2O2B4830c0AAOnHkku8pNFGn3E564lnvii4LjRfFzo0xHyYdcYXU07zRP8Dd86hjWIdTW/JQEkmSDTmxXXv9xcNZbVnSj5cXmv2Os5TSN9L/LwpW06a59nEAm7qx1UqFTg7OxP1J0D63bEW8rZznv64NBlLHQdPUKgMZr6OAsbqBI/jJ+VRNlICAh4FohnzHmEjGbF5g4frhGS80v/pzsytra2w4hmPOer1elCtVmEymcBsNoPhcAi9Xm9FcOYxzlMVgxVEymssxN55DXSLTl5GSvAoJhh5G8acT6/Tqjn1scCeVUZRPpeCRzFnmbcNr1MsAJpCGwVvo+sm71MUrpR20/Wh/YWBueFwCIPBAGq1GlQqFTg/P4fRaBTuh8X0fFdsEd0RS5PSNtL3Xoem7D4oGtCO5e3NM4+MppCC+Fo+kk1XxPBM7U+v/qDpLdrXjdTAW5G8LAfOU1/cjdZut+Hg4CDcL43f4nGPKXcKp4IGHzH4QW3ELMsCXXQRgTQWqTzTZLfEJ2XUQUJRGeHhDz5RqQVmrXrnsUstmmieEjg99NsUuyVWlvR93rqmji0tbcz30XxEbpd5ZLhVfoovtw4dystH/2o0Gl06YjZGn/Ue28pbN8kGkvpIS4eya2trC0ajETx48AAajQZ89atfhXfffRf6/b5aB05T3vEo0UdlJA3cSUc8Ux+iLJmg7XTWdL/WlzzdeDwO7/DUF4DV+/+keki2gScgatlLklxLsemw3prveJ38I0mHlKHvaN9740sxeNrQ8t8lWvLWl48vKy9N7nDeSvV96DjHfK2yNZ6UxkcZPCrV0UKZ9ouHJq38MvItYmPn0Y/eb/NAGjN5yk+FJfc9ZUl9wv9Z5cby4zSmgo/HTfq2V6mDNll+nj72pvV8I9kCKfDK4xTfwfpW4m9Lv+D33KcpQoPHJ4r1X0wXclo3wY+XjileLpfiPSLXyUAsA3mcPo8hVeR+mDxMimny9o+kkCzapPd0RRGliQaQYqsnNsFf/I4wDFLSe/IsOp88eQJ/+7d/C1l2MfF8fHwMg8EgrMTgAiPPXWcSihifqbzB08f4oSxIQY9UmjwCNoUe/pvmT+8Ci5XBlZl3TFv1pAFpbWxqhqH0jtNattG8jnyv2mi8CtC+z7Is3AH77NkzGI/HYWHIgwcPLskf3LmasqPCA+1+eYDNBX/XgaJy96rhPWWA08rv2JHSUKQEJlPahcs2zvuxsorCCiJLf5cJ6rxIwWN8hu3Bd4JQ+vDewHa7Dbdu3YL9/X3Y2dkJR5bXarVg9y8WC5hOp2uR05VKBabTKfT7/ZUdY9Z3OHlDjyOiO5SQJyi9Xr7l9F1XoMzGXVuWni/DvniJYtACfN4d/Zo8tSZweHlWvjRI7+WBlLSa70y/Pz09hSzLwpGzvCypjfhuL37MssdHsGjGbzzH32KMpF6vw9HRETx+/Bj+5b/8l/C7v/u78G//7b+Fx48fX/qm7DGn6cDl8mJStFarhUU3g8Hgko1WqVSgVqvBdDo1746NyVJNHpVZ12q1Gk5KqNVqUKvV4OzsDE5PT8PR0NYCcEl3Unol/UF5jdbL2iHrQepY0sa5FuCkv/n3RYK29PvUvi1zkiJvmV7QO8wRnI8sOU7lCNIRs3X495rdKY1FqW1jwWqLbum3Jlt53WIyODXojtjUvdvrsgOtWMw6y3kJ2ybiQH8jFmO0bK+rvCO+CFDuXDUPXXX564bEQ6n68Cp9Ny7rLXvZc4pIan/H2i3LshDTiMXxkX6elvtr9IhkXp6EPLa+emdsCoo4QV6UHTCJ5ZViNHgmOfJOJmllltnOKXlJRgUfjNTZj+UfazuaR4wHYm3MjTu8f2a5vNgCP5vNVsqYzWZw7949aLVaAHARQKB3xY7H45WjrsrsH20wewxc/FZzKLRvLUXsqU/q+NQCqLxcKSAdQx6eTuFTjW+9eQDEjwj2yBL8nbd/Uo+FzlPGunRCCr+tKyi/acORGvx09wPARYDwwYMH0Gw24dmzZzAajeD8/ByePn268n2eMjmw3K2traTjBPFbqxyvjL+u0GhEg857jIpVV41XrfGHfGMFTrBfKV9Zk/c8sI/fxfhBCx56cRXjTvo7Bq9u4MFVT754csfW1hacnZ2tXL8AAGGn6Wg0unRPI+1bXFCWevyzF5S3cOEeBR6bjAv6vPXndgFtv1hbSjzH01pB0TIh0RGz/zSH1pKhXvsC06boTB6kpu2Pjq3UZxo8dmjMN0B+kxaXpNSR08v7wOorKyiu9ZVWF1omf6aV4QWXUzHdkqfvKDRelAIinL8ojSl2r8ajGn1eOYh0DgYDOD4+hmazCTdv3oRut7tydYNWT6/fz3W0RjO3DS1fRZMnecD7yROwSgHqCTxdYTabXdIlVr70nllOp8eWwrT4T5q0syD540XaiNMt6cJYrEmza7T2iMUQLHln5Uu/idVZyz9FX9G+iF1/IMlcqa24PuI6z6LZG+vRELP7rbJidkFM7ms8zeVLWbBst3XFGr4IyNtHZfatxyaXys/b797vUvL3yNwUnZFafl5s2qdeJ7y2qoZUX0kqP49t6qF7HTLVC81GkOIXVrwpdaMatqcUN7L0lSdvT6yKnsASyw+/ieXN87t0TLFlOFwlrlpQWMYUZ0LPd3ngycdbVt4gE/+OBteyLEsOzGu0FEmngQ6oLMvCJGuWXdxLMxwOw3F9ABf3Av3whz8MwZxmswmdTifkMZ/PLwXWyzY6AeKBD43vuKMW6xdtJSf/tgzjhwZdcedLLKCVtyyKFMePf4NtKq1805SoVifJMdfS0zIkxaMJfo/RV+Y9fFq6FwXr1DFSX6QY5RSLxWJFTiGm0ym89957UKvVYDAYwMnJCXzyySfJ+acGl6z0msFEkYdvrxM89GbZxWo9gLS78Kz8UkCDiDEHN8uylWNsR6P/H3v/8mNpmtyF4/Ge+yXvldXdVTXT0zPjnitmPLZBFkhImK+EhZAwEjs2iBUSfwEbdogtC1ZskNgClhEYYQmwZTP2GA2juXf3zPSturpuWXk9ee7n5Ptb5C+eihMZEU887/uezKzxhFSqPOc8l3ieJ56IT8Rzm6h6TNMfVh2xYJDVhpumsgEyq0xqWzy2utFowP7+PjSbTRgOh1cchlarBd1u1zy5vlgs4Pj4ONS9jrmHOgBPa7VarRWe5vM5PHnyBHq9HnS73ejOcuRPe9vJo5OQUpyldZIXj3hxnMcZjNXjJWmRE8ewXq+HhX7uK1nBf76AyjeySAFgCfPihgR8UoTKubetmrzg4o70G/7Nr5fWdmFb+pL3meVvplJR3JYiWzE/BseJP6Gg6SJvYCRG/HYZ6vcUKf/o6Chs3n3rrbfgZz/7mal7ixLKMD0ZjHyjXdB0KB0L+iRPKllBuXXZ6dlsBo8fPw6fcW4DxIN7VKYwLfdxpXnF8/HPmj+okdcH0PSBlt+bXqunaOCX1m2NuyQvWsDba58l/ix9xtN4fBIao7A+0/9xvmMalFFKPKZQxaZoLCs1TqiNgye/NPeLlCfx8aqT147cBn+3yvhEFbwA2Ist65AVakNjZOkoCdt5YmW/pPXQuuTFopjd9Mamb7NO5HNTiglL7edxI4twYyHHXikYIUYe/MLjXLEYA03r5W8FJdBdnN6KKJXplNtgkChJA6SBZk3gYgNRhUDF+swC2R4h1OqiTggPONC/ve0vMvZF5I8GSabT6Uof0N3ErVYrnPpaLBYwn89hOp3CeDwO6fAdm1SyghL8b2/wPFaPZ15r8k1lucoAH5UhWm6e5+GUMuefy5QHqKXOM8tQSvV5yrX6jbfH4l/TS56yafAQ+5Eu6PFAmNU+TwBByrdOKms/1smrt7+8adDhx+srl8vllcVZXPygQU7PFTx0zHlauonC0u+WztKCXVXSdeKJWKAK+4zrtHWSB+xrhAAYF2Sn06m58MDrsOTjNjoXMdtWlGdvPkyH76Viv9GFJ6usWq0GW1tbMJ1OV94qzPN8ZeH/2bNn0O/3YWdnB7rdLrz22mtwenoarqiMBXy9pKXHK5BRD6Eums1m8PDhQ+h0OtDr9eDo6AgALnUbps+yzH0KisqdJLMcS6RionWTZcvpCWf8zsIM6+Q/5hvV63XodDpho6NVBh0vHGv69Ml4PBYdeAvX5nkOzWYTXn/9dZhOp3B8fLxyA47WZxLGpIuoPDAg5ZH4omNSpR6UcBuvm//GyRs0Xi6X4un2WLm0P2hdHI9IgQ4rwBPDy5R3/lkauxScRvVHo9GAhw8fwnw+h9PTUxiPx7BcLqHZbIbNCFZZKSTJHyesE+cT2hYa4+H2wSJp/CRZWzeGxg1ttG1WvZYejI0rb3PRhetUsniV5JTKobZByaqHziG6UO3F8PQ77CeuSz32iM9tms/j91McJ7WRt4Pmof8kGaf9yuvAfGir+DXW1jyxfAcvpfhg6/CzAORrhKV2e2JVPA9PY+n3FJu3bvKO4U1gTA8PN8UXnyMxfMD1RKzsVL/Mw6/m51bVh7dJrl9F8sZIpXw0Tep4ptpiD5a16knRr968sXzevDSN9qyEVo50iyTFY9JzEhKfFt98o6yEGaz8+Jvkn3toZTE2ZQenBspjRIMHmsOl5blOioEDr/NKAZ9VvjbwMX60MahKWUtjhYTCG7vGUCrTS1K7YgDW0/aLiwuYzWYrn+kJ32azCbVaDWazGSwWi5XriKVTaV7yKkWvovMEIzzK1hPAsNIWdSrQgaTODg3aSk6PRVJfWHLBHcUUwxxLJ30fk1+PfHt4kX6nziYawmazGdJKO+Vj/VGVbvYY5SL1aOXehE0pQnzu0vHDecI3w9A8zWZz5XSGFVDy9Ie0eF/U1mgBRUu2q3Bwr8uRQd2Gi5raCVMpn4c8MlwkgIF8ow3U6ophkix7eUqABr14+pskrvtTnPUqCYN5uBjLA3kWX1mWwcbGRjgRT+0ovX74xYsX0G634etf/zq02224c+cOAAC8ePHC5C11jKz08/k8LDKjPMznc3j06BG0223o9XpwfHwMAKv+SMrpIypnWpBQcq60ORLD5rEyUsia0zToS+XBOzc5FbGFWlru5+T55UavXq8HeZ6v4GdP+fV6HVqtVsAnuLglLcBYgblGowGvv/56uIp7PB7DaDRyLaxwx1rz/ySMJaXDz1XqF0mOJT0Ww2qWnFN8yK9CT5F7tCs0H53jtJ9TfQwvVtD6y0qHny390Gg04PHjx/Dw4cPQzp2dnSun4or66TQd35AhEW4gBrjsh62tLajVaitvw6LvbvW1xx+6LsJ+pvbRm8/6TZID+jfqHPpmeRGy4kcpvh0n7I8ifHHdptmVFJ9X0g8pZdFyvPKn2QD8zdJZ/OSNVhfHs7w8ai+ovKwbT3pjRZpt4v3M7Z6nft5mzleMNwtjWdgmZn80GXpV/P+/bOTREXzMOfaMlesZd0kGNYxh6TbLp/T67p60v6Q4pdofmvY6+7+IL+ct0zOvUsiaT9b3GEfka0dWf1N8Ix3WktpYdl5qcSuvXKTI3NX7M9ZMkmPrzSPRbTGqns62nD9KMScy9lsRxR8rWyqziuuXqgz0eUk63cXfKsMTZxoPngBdUVqHTKcYFWvntXU9T4qMokLmJ1fwFBa9AkpTaB6QzqmIvNF/VjqNF/oddVw84C6FT61eiS/+IDn+zXdWx/oxBRCvk1L77DbYjCLE+caTArPZDObzOXzwwQchaBALWHmuRKVjin/Td/jKkCQv0jx6VceKn07hiyicrsMWWg5eDJ/wa/kooPXoR6r/bivdlP7iJM072s94wg8XqQ4ODmA2m13R3agH8vxyMfbi4gLee+89yLLLE8/z+Txc39poNNZ6+kcb94uLCzg/P4fRaAQnJyewWCyCXrOI7mT1YmuLF8xrBfwl/4XXX9U81njkeMSD7aw0t8lu0jYtl0vodDrhVJ/mb3DHGrHNdDoNN0Z0Oh3Y2dkBAIDRaFQoMLNOkuRGGjOP/K2bcFGW82ERjo92xXBZvql+RIwiLcrwtLEyMV9sbl1cXECz2YRutxs2yHnqK4OZaXm46Wu5XMJ0OoXPfvazcP/+/bC55yc/+Qmcn59Ds9mEPM9XFmVjvFjxBE/soSrK83zlWSDen1wn8vlTlDcpGAegL8p6YwOaXHG7b9VR5npbS79YaTjx2AnljbchNu+kOevxTyQ+6byjPHJ58ARaeVqpDRwfx3wkjl1iPGj1SpQq85YMePJJtpmWQ/WhNW8snU1J+4y630O33Rf5JV2lGI5N0bexjemeOD2Pe3F9RHnmeX9Jt4M8uvIXWVd45Dy1LJ5H0smeTVDcD8f5hriWHjLRbJhW/m3x/ZBKLcbehsZ4BYUHDul3Wtp1UEqfpQCqmBEqWq5Vn/ZeHA/4Wg6FBv6L8uQhyWnDCU7L4s44/ex92w3Tp/AnUUxhafXE6uSyLgV2pPIoEJEc4Bjx/kaiO16kgKPHmbPqjKWh6VJIcwL5dxIf0uei88IbYOIOtOaApgaLPOnLzgPNabsN9ui6qV6vQ5ZdLs4sl8twxSf+1mq1ACBdpqsGaDQQosm/J/+rRBi0RsDYbrfV9xyrbp9mj61Ak/Q3/cxPX1iBIms+vkrj6dWBKTYpZoek+qhNybKXNxssFgvIsgzOz89hsVhAq9VawSs0SDYcDqFer8OTJ0/ClZV4giq2UcDTNg1XW3YNf8eF5MViEd4CjMmPFfT1koQDPbJJ+7gKu2PVyfuV6lIJF1lY6abmXur4YL/iaXGOV6T0tC6cI6izGo0GdLtdOD8/d9fvlQVOKW21xiOVB6/flUrSHI+dxLP0ZswGeeeflJfjw6L6zMsb1QGIt1CPSWVX4QtyQn8UN9Msl0vY3d2Fz3/+8+G95HfeeQfm8zl0Oh3I8/zKCVmtfVXwVhVJOjpmQ8vGd7R6qvA9Yn4ejzt4+NS+l/CEll/DH5JvyNtAeZbex7bq5n6ClFZro4UVpLKl2EaMJD0jpcHftPZTvmkwWtM5NH2MP00/xcrwzAerHKkNEh6JlZPKk8anxAfn9Zd0uykFP8SIzklahpY2hUf6f0zfWXM8xYf8JRWnMv1cRPY85Vt2L1XOy/CnlRmL2dHvJbnW7ILHzkt9weMgUhlV+kAx3mL97eFlZTF2nY76TQfguLDzv6W0HuIdXGSRLoX4aQCrnipOL1mUYkyui2L9znnEYI8EYtF41+v1sKMYQL7O1UvrVBDcWaB1WrJgBRarHmMMWNAr6LCe5XIZTvpI5AE1scBWCvE6JEeZGxN+TY/UrymGVqpXSmO1gf6OQU1+vS3+hteqUWc7tnvJcxVkVSTp8VeRytpD7F88pdrtdgFgdYcyzilOUr2eABfWSa/f8uxsi9UrBXpuyvmoCqdIZcxmM6jX69Dv98M1+Z52lgHYlm7ndaCd63a70G63YTAYwGw2c8kQB8nWEwa3ce7epLNLxwivYpXS4E7SxWIBb7zxBmxvbwPA5dW/vP+lQC/ApQz+n//zf2C5XIbrWnHDRtV9gLtXccGYyiD+z6/x9JKleyjekU5zW3RxcQGTySTgFAnrZdnLjWR8IXidsq2NKSUt4LkuW4wLpXQTHY45YrxWqwXtdjuc5ubtuem5lxp843n42KcuINC5LZVHCe09vvWpBW9SePHYvPl8Dq1WC3Z2dsKCH2JI6wQ5raMK0uayFgy9DlujyRC+k1yk7dKCguWboe/UarXg7t278ODBA/j85z8PR0dHcHp6qtZBcX4Kb5QvKbhm8V0V8XJRt+CzEJIdpRs0MA23TTG9xMfFmmt8jPjcTu2fGIaTrv2OlUdlAP9ZbwlTPc91GCcuW1XGAWlZMV+d8k39Wvp2PU/LfWAvISawxpa+jQ5w9dpFSlZMQ2p7KlXpV0hjgv9LTyBJ88Hi0RMDSsFiki7T5nGRfqIxIGkelC23SlpHmWXL18ZDK4dvgPDGrXieMv0g4RPLdgP8csOARuuWyRS6Dh/vtpInxmfZYLSJ1N/Hp7DogYUYSTFrWuc6bvZCO83LpzFYSpqcxLDeSiQkJmSpoEoDS7eNUpQ9/80DoKwB8AZXeB6vspZAzDoUfcwRScmn/V5WhqT8VuASf6fG1WvoKWmGN1XB8fze+mLlcgfEAg5S3SljiAt+9N1dS0ekzk3p99h4eeanB6BraaT+lHi0HEiLP0//cxnXgmipDqEWVCiqD6ogqR/XbX9SQWNRfiRZzvN8ZUMJ/lbmCjNeB/8Oy5bkO5XKjE3VYN0qS6uL689GowGdTgfm8zksl8sAQM/Pz0Xw5uXJ088af9a85gE1GvCnJ2hiOiD2XcrvN0Epcpwa6LHS8MCWNqcwDS6g9vt9GA6H4pWhmiOwWCzg8PAwBK5xAZ7zWwXu4gtMMTmx9I2FDXgf0vRFcDIN1sf8gFiasiSNaSw4q5WzDvvL8THvR887ixJvND99GiPVp8jzPCwa8gUGLRiaGnTR/NyYPNO/U/CeNLfLUEzG6ZzodrthTEajEUyn09J1F5XLFOxbRX1S/VgH/k//SQEmLptlbKrmV+Dmmul0Cu12G7a2tqDVaoWAUqp/56UYdsI0ZTEb1zP0e/znLZ+WYwXPPHLjxQSpbafYoGg5KfrFIo9fx/sq1neSbFj4pyhxvMB9qZhd1eaJZr8solgbYNUf97Q9pX9jfMR+Sy1bsoepejr2vUdOJbsak1XruyKkYbaycYl1YM7UMquMeUh2OrUOj32M2Trpd4/uKsJPSpkeeaw6FnIb6LrbU3TMylKqz1GUJJkuqudTCP0HTY69/c751myD1x+zqKwspM7HwtcUeyq5CcWwboXEy055n6BofWUCuFXxkVrXdSkxL9GgUSydRhcXF1fe2ll3OzUnscq6i7z9y5WcR3kCXM6XTqdzpU38ymLc3SyVoznjZUgDg1Z6gKt31WvpJOKL/NKpWl6Wxxgh8WvE8WpKbdczntajAfqi5H17NpWuU6/x8j2O67rK1wjfhORvK1s7rWmd/G9vsHvdwAQpluc6+90KnOJ7bePxGB48eABf/epXQx995jOfgUajAf/pP/0nOD4+XslD399bJ/EAA+oeSY9KJ55Q19D0mg6htlYLAJehKjFeLNAmUdX4kutxnLv4nfR+OvJ8cHAA5+fnK/LE7QqeXhyNRuoiwTqI4y3LucH24cYF7w5XXgbKLX+PXtqYwu09f4fGW39MHq3fqRO+TqdYwxJFb89BWWw0Guqp4/l8Dqenp7BcLsP72Z6gQ57n4Qrt4XAYrqzGDSK8TXwOI0bP8xym0yl8/PHHYfMf3rzC9RmtG2UB66D6Kzb3McCOeehcjgVk1+W3limX8t1sNmFraws2NjZgc3MT3nvvPXjy5EkpH9gTVORzhI6RdYLPalMKr95YB5789gR9veV6+JPoL/7iL+Bb3/oW/It/8S/gN37jN+CHP/whzOdzOD8/XwmMpZwkkOYA5kd9ixtwLB1ate3mf0uEdgHtDF0Es/iUbAivl74RyjefaKeveD20bzEdvneN2JLfeoN5eB38b8v2pI6FtFAlEfaFNz3lp4h/Tzfs0PIA4ErfSTehcUr5XdNXPD0dW7x5A58uGQwG4ckIXgf/v0pfumyZKfn4nPPKHpULL3l1Nt5+wuN6tD4ai0Kb/kvyL4iXKYvjplRfko4j9Q2qsEEU+69zzcHb3ptYb0klLa75KvAO8Gr08W0knIdcz6YS91sp3kJ/lKctWx+1v9L409uhylB0MXYdwucBL0XrTslTxokruihgBaSscmN1FM1H81q8e0Gb5iClGqyUsUnpDy9Ik8q3eKpShqX8HIxKPFnOlxXguA5CRdlsNsX3vzD45g1OVuGQWOOa4gDQcrhTzckKAmo80LwxZ14iGtTHawOREKDOZrOVOmPv3Wj84f+p10VYcqgFMauSC0+edc8Tj2PD09PxwUCRFDDi+aT+S3V2PcTLk+aUpYfW7eRo9ZYtp16vQ6fTgX6/D91uF5rNJsxmM2i329Bqta5cMQlQnUMSwxMeOaNyJOmpKnBCGapaJrzzXwqwx/oi1ZGm81eSD/w3nU7h/PwcptNpeGeV6wT8m+oIAAiLW5r+Ljt2aD9SA57WfC86P6QxkIKc9Xodtre3oV6vB3s4HA4BAMJ3kj7T2iLV5yEPxky1FZwPrn+LzicuOzTgDPBycxe/CtLiEQDExUye3+IZ02E5o9EoLMbO5/MrDjbNZ/mmtG0axvbwl5KGl0/nlscvKVqXRrhhAk9aplCs/yh5cadHx0rj4sHVnGIxC802xuZYqtxYlOeXi3jD4RBGoxEcHx/DYDCAL3zhC9BqteDTTz+F8XgMx8fHYYNEjLhdifXDTWE21D/4/A0uwHG+vfxpmJST11ek+lfD81Zeej15jD8Nq1i/8zTafE2dc9pvnjiFRSl4RfPLU/LyemPzwCqPYx0MIuPmWlqX1ifevrLK4GloWyR8ZmELq1899VvpvHPLk4+2j/6j7ZDmCqajdo9j7F/SS6rSDmjYlf4ekz8PT14ZjWHsmB6WyJJV7fsYrli3LU4lr00tQmXba/mKReoqIwMpsQOrHF5vVaTJn4YxJCrjM3vtSwpG8MhmGXmI8VD4ZGwZum0KglMRhVFVIMsir9FP6d9UkH3bx04jdGboZ/q35qjx04o3STFAcpup2WzC3t7eyo5h2p6DgwOYTqeioxKjlHeaOcUAkJcPz2kaviPP47BK99Gn9E+WXS6CLxYLmM1m8ODBA9jd3Q1ljcdjGI/H8OzZs5Udq41GQ3xXlpdN+ZR2gVuAuQp6VeR/nZTnebguMAZwpN+1N3953irpF3nccPHgi1/8Inzxi1+E3/u934Of/exnsLGxAc1mEx49egR5nkOn01krD0jUtkngGX/DHeLcViLhiTJaJifUcdIOyFS9/otAXp2HMkPzcVuBJ5parRYcHBzAs2fP1DGlQVuq12u1GvR6PciyDMbjceFAkpQH69E2XHnICsSV1RnaPGi1WtDtduE3f/M3YXt7G3Z3d+HDDz+EP/iDP4BOpwMbGxswHo/DhqUqeOHkLY8HCgGuLpZQWvd8o5s28GQx8rlcLuH8/HzltGiM8MTQfD6H+XwOk8kklAewGkCl3+NvNB0GuE9PT6+cjNMWGZDognAMD16HTuPzH3dhU/4k3VyWt7JPHXA+tGClZZto+tT6pP4pSp4gbOy7ddNisYDz8/Ow6eB//a//BR999BH803/6T2Fvbw/+5E/+BN5//334r//1v8JgMCh9UkEiOseqkh9aNl84QarValCv12FnZwcAAM7OzoId1E6oesbIwz+tB/mKlY19w31hiTCNtz8lW8fbrPm+lHeaNmUxgevOlPiY1HcpOMpaEKHEN65qPjflwcMX/S6mw2azGWRZBr1eL9govElCaxf/nvKt2StP3IHzTN++xe9TbjvBz9jP3tsmqtAXUj/Q+cNjgRJ2aTabUK/Xw4YwxLXtdjvknc/nK7jwl7ReKoIRJL1UduFGKr+Mva8Ko1D6y+hr/5JujqR5VQVetnCKVLcWv/LG3rU8VD/QWA3G/vltnpQ8uE1djPV2VkoQ4TrJA4YxHZLHiU2Z8EWDSFU6v5QHCZx4y0mhGNjTAilWXq08CRxz0hx9DVhKjrz0t6QQeHlSsEEyvFLeIpQaFLD62wrc0++84J47nRT00zpT2i4FJSUePJSiM2L6QZPn1O+9JI0Vfkd326LjgdcjtVotaDab8Pz58xDApFc9eIjKBXeyqpJrK6/X8f1FIkvv8O94ALuqeiVKcfqvk7xzu8r68Iq5PL+8nmU0GsF8Pg9XgGNwQtvIoY2r126ntBnlAwMRWvCLB3/497Fr21+leVlWJ9NyrDJi41Skfkkn4hjxhZx1zEeOkTwywXWWpT+sxRv63qtnDNF5w4XXe/fuwcnJyUo7aL3UGVtX/1kk1YeBRvyb/2b5It5AEB1HPtdj41Wmjzw2x2pDTM5T57nVVpQlTMNP4lKepL8BfJuj8BpQ7cQwD6SX1WVY32g0gk6nE8rHK1R5G8vGDzQZlyimX4uS5l9advm67Bvng/pSiD1arRYcHx/D48ePw3u/7XYb6vU6TCYTWC6X0Ol0YLFYqBgk5qtTfZhll6dSKRbw6hZvm7Xv6EJLp9MJiyuTyQQGg4G6OciaF5JMaz6GRwa5LfESxXL0BgxaLueH/6aVq+XT0mqk6VfNH/CU5ylfy6vVK/mjVswA9bnVx5p9tfhdLpcwn8/DBgIsJwWvc1wiUSxW4YmfxPxui7R+k2TYG3vh5aQQjz/R51rwxofRaASj0WgFT2XZ5TX0nG/pPfsYzvglpZNmf3gaLeZkzXFOXptvlafJKcfQUjkSjrpuH+MvK6Xoupgu8uTRyLKVZeIBFlUhayl5pf6T2lqkz3g+z7zS8IvEk4YDLFurPSeBVMmbsbdRURQ1fClgyEPr6puUieMR6DJ8WrKgOQjrcs6kNNJbaxIQ1YgG3/Czx7inAEdvQKEqeUoFjJrSsviJOSPYp/ydnZhuwe9Sr0nzUEw2Yw4lDQRTufE4Wt7AcUo6egUxpa2trbDD8/T0FN5//32o1+vQ6/VMHi2+AeBKAEYDx0heeY69S3YbbdB1kBdkWYCiKn0s1RsLxqV+X4aXdZFXbx8cHMBisYDt7e0gz/x6dm99qQG0lKAYfftOIvouKf7PN3y8KnRb9EYqH0WuKgW4HLvxeAwActAhlRcpLQbmi7znGMMW9Dv6bgwNrlF9R9/x0wgxYqfTge3tbfiVX/kVOD09DfVRO471X9e7uxp5AttaoLVIEFSqB/vmVZrv6yLsA7wOGWVzMpmEd8qQJHzEbyUBeHnaTvI18Lpg6TYZmocHARATprYNTwgdHx9Dq9WCzc3NsKnv/PwcFotF2BmuBSqqImwfbaNnPhSpg5ZH/1+X3SgyH5EX+kZns9mETqcDz58/h9FoFE6YA1za96OjI2g0GrC3twej0QjOz89L8Y0bfLrdbtDHeOpW8wOqItT5uFiyubkJ7XYb+v0+nJ6ewvHxcdgYx6loHIvLOR03LXjK57InbsL/xzbwN7glovpZ403zJ7lP7iGUP7q4yNuXOjdT8Y2nLIpfAeQTpfQtZGmsKVFf26uH8OaHdrt9xU5rcmW1C20CluEJRMeI6xWvzyHVp9mEFAzBb7coQtRfyfN85c37fr8Pe3t78PTpUxiNRuGt4VarBVn28iYZSvV6PWxAsfD4LzFScfLoDtrvMb8mpkss/MJ58cRCfzn2xehV9C0snm9LrOE6SYspaL695mNKZdJy+LyUbjStSpYkexazjx77uYJOUwOjkmKqQuDWPQE9SpmDKq+Dya/j8vaHVJaUVzM0Vl4NPKby5AWFHkpVtKkyYaWni7OaUbXmAgVdnuAG729NQRWRe8nJ1ZxBT1mefEX4ROecK9LZbBauw8V+SnEWtL705AXQAyuxvuB8UkeI8kSDtgjqJSeaGhEuW1RWeUDIcuKpw7K3twd3796F7e1t6Ha74UTDo0ePYDAYBAeVz4NYoM/SWx59EAuGpMhDrPxfJKJOrwU2Uh1vbzpvuVZgSitHcuiroHWCYZyn3W4XhsMh/PSnP4UvfvGLkGUZ7O/vw/379+HJkychYIhXAtMFK0+faG3xBtkk0pzVIv1lBRxvG3n0vpbW4yCk2iauY63r99fRrxqfFs7X7CdPJ5WvyZ1Xdqltarfb0Gw2Q8COYgopUEb1y3Q6De/CpvTLuknqgyK4Xer3sg6qFESlC9/4G9+cpvFKy8NnFfB3L8bWiJ+U1vzVGH+0XRKe579j+7XAsYaTKL+4mY5+Ttnk4NElVvtpG/P88qThixcvYDqdwmQygVarBffu3QMAWHmH1CtfvL80Pris5fnVa901zGDxwPsd4OpNPRoPUjkp86mIXrHmtjbPLi4u4NmzZ9Dr9aDX60G/37/yewxv0THS6kz1366bpL6h7Y7Jn2QHuT9G83vGN2bn0IekesRrD1Lly7I31D/VdCL+zU8Lan6g1vYy88Lqx5ivieWgHUO++eItz2/hPV4nr5e/a4x58GQ3/s7nmDRfJV/dIk12OD7wzGnpsIKWT+LPoz+tWEOMNOw5Ho+h0WisXD3MP+NNQfv7+7BcLuH4+DiUhzcQIM1mM3WTxDp82r9spGHXmD9GZdGDSaRy+HcpesqyCV5Mf1N+yE3RbZorMZ9K+64K4nqjSptflqy5mDp+HNPTspCkeW3VF4s/xDCBhl2s/Kk+CFL0ZCyCE86sxKTFuMXYTUy6FKeU86e9PYqfMT/fTS+lT+HLoxBihiIGkGKkpcMgmCetpFiKKJHUgATWp52ojJUnjaMH8BbZxVeElzIkARVpPsfAvgYa8jxfeZ8ITw9MJpPw3iXA1auMLX49YL4IgE9xaCjfPC8Gziy+JeJGhgbTsAwpPa+HvoOyv78PX/3qV8Pvy+USxuMxfPjhhzAcDkMbtDeSU+en5ITzee91Fr2OdBG6jQDX0use3V2mTdfRH975dxvHRiIMmqATf35+Ds+ePYPf+q3fgizL4O7duzCZTOCHP/xhOJV4cXGxcloFwG+7U3CWFEQs6lh4dRf/7Anm3ESgIoZPispfSn7sG+kKG+2dTU8/ebEh/S6Ge6k98oyX5+SOxW8sPZXrTqcD/X4fZrNZeB8dg9i8PXTxIcsymEwmN6ZvimDxlMClNPdj4+wh7H96gkoKeHnmPi0TcYt0S4rWHq0d/KQWxSSa7Hl8Fi2d9NwGvYZSaq+0yEzz4DXHABA2DPD3BDUqg984ZVkW6h6Px/D8+XPY2dmBzc1NuH//PjSbTZhMJivXO6aWLxEdbzpn0eZymUvxCenfOFa0PvrvNtz6EJvv2ly7uLiAx48fQ7vdhl6vBxsbG1f0Pj+9LZWP/gR/g9wiDbuX6UcJY2CZ1vyVsBPnR4tpSTgKf5fkzvJ7pHo0wrgKPcEnlWvVH+MrZstxIZg/QaHpF5yXGi8p+MXS/9p4WBSTDz6PKNaR7Asv09JBEsbCG2joGGTZy+vfrRs4JD55OzVsz+1nil3l9RSxpxJ55rGVL1afVP5oNIJms3llMbbT6YQ8g8Eg+HGLxQJOTk5CWXThNs/zsBlJqpvaME9bPG2qiiS+bsIn4xSL/Uhy7pHlFJxQtg9SfWaJ71clFvIqk9XHEv6P2diUPBYvmu+RoitS5Kds/CO1bhpXkOyS1m/SPEnhg/sNMRznKZ/bLt6XVnnmYqxmFDXju06FUbVh8IBKqf0eoICOogS+POTN4+GjKrppowxQXgYkOZUmIXe+eXppUq9L9lOAbdG+qWpspTmF1//RgBg6GdjPsSCD15mrwtGX6pfKpJ9rtRpsb2+HN0eQTk5O4PDwMLzDStsrKX0avKYBIkvnSDKNV1nhFXkAl0E9vIosy7LwdpREGADgJxw0GUfeaB4eiNfa4dWRMVD+i0LSeHInzqMHb0JfW+BIwwhc/94GO5NC1tx89uwZvPfee/D48WM4OjoKgRcaYKcUm19eKnqFl0ZU7vBqSn5lmHWayDNXX7VxL0rSAivqS05F8aNGfK6VkROPTSoaiPM4b9zm0KAl9pt1vV6er15pvFwu4dNPP4VmswlZlsHe3h584xvfgMPDQ3j27Fm4FlbadFglaaefpKAn/mZ9LkperE372cMXdVil9uBJUAxqYlpMb50O0wKK0oY4/M0KqHA5tuSayhw91Rt7F7wMWQEKam/xZBf2I19U8xIuEmsB5+l0Gp7EiAXwaZmSDEl+l4UrOEkBJU2ePfKAv+MGD9QTqDu8C+RVUCxYhjcD4Fuws9kM/uiP/gh+9KMfwd27d+H58+fQ6XRguVzCYDAQnzHRSJqvnLB/1qUnrfZfXFzA8fExtNtt6HQ6KzaP/qPlaGXHYkESeX3V6yRsi8dWaHNA2xhm1Vk1SXG6WJv4ginvB6kPMDZBT8jijTbam71l41Kj0Ug8kRvLx9tAv6NyL9kFqe9S4k00nTcGRudRqoxk2csr/Gl9KTYAP+NvWN5gMAi2utfrhcXwxWIBo9EI5vM5PH78GJrNJjx48ADa7TZsbGyE2M7BwUF42gLg6tXXlm98G8jShTdJsZhPioxan2N2/zr7QqvvNozHL+n6qYhOflWI63Dq4wGst70p+sSK13EdFYs7aBRdjOUFaGCtDPjyBG9SB8Xihwe3rXQcuFl5aJ1eR6SMsKWAGs2oeerXwF6RsopSDHhRivHBwSktk19VxcFjEQczJt9WQEjjm1MZkIv5U4K/ltPJHYJms7kSRKMnxmMORSofPOCs8ZjiPFh50Gnr9/shAIB84Mk3fK/Iur5NC/J6gwCU0KmjJ5EXi0U4gZxl2cqiMScpgGk5PFKwQwpkSG1JAcExnX4TpAWAAdIdAE3HaQEEb70pVLYf+RhLARRel9YPrwJZMn12dgZPnjyBo6MjODs7C/M79S3NIs6gFCDRAgPWGPH0tVoN2u120CfXHaRcp2Ocap+Lli3ZEal/tICfJ6/2m3cOxtJQmdDq8NpYqZ34vxRE5Hn5fOL2iPKrte3i4gKOjo5ga2sL8jyHbrcLb775JsxmM3j48CF0Oh1oNBpr1U0ahqOBYfobn+OW/BbRHym/S/Ze0yXalblZ9vL0pYaFeH2xOavhe4lvWo8HP0rl4MIndcjr9Xp0MdY7VzjPllxr86roJgzeFmxfo9FYWeCVNvnFMBE/re4hC+vz8iVe+O8WxqVp8jwPmyuR+ObrIjaqCrtGeUDdOJ/P4Z133oFPPvkE3nrrLRiPx2GxlG7YLFKXRtINRyn+rIc02zMcDmGxWFxZEI7d1GCVi9/zujQeUv02jx1OsT0xjEFtLMcW14W/NRuv8RwrB8AeO+t0O28/91dxgda65aAMpsWr6Ol3mv6O1Us332hzT6NYG2I60sKC0pzwyD1N6/WhJVst1YGLppPJBGazGeR5Dq1WC1qtVtjQ0mg0YDabwcnJCfT7fXjw4EF4XxbLHgwGcHJyIraNUxV++m2idfpkRcnCalo6ba5I/ggly8cuQ6k+WRX13bZxXCelxmAoVR3ziPFi+f+0bA+G9dQn5fHmk9JaON2y+zxW5eVFm4daTKXo2Ei+RCz+oZWnLsbyYH7IwJwQSvxKJjwJchvJM5m4QudXyWkr4N4J8SqQBM65My9dT7yOcS/ar5hPCxLQQAl1IovyI6WreuyrLM+j6CylFQt04jWA+F2qo0rLo04V57UsQOHATOKRn3x47bXXVk7+AgCcn5/DkydPwuIn6ooyhDJKdyhrit5yqLvdLjSbTTg9PYXhcAh5nodrAbMsuxII9Y63Vq80n4o4/L9oDoxGqYHFlN+roFhQtepybzNRXYY7qc/Ozq6c0vvwww/h008/hYuLC2i1WkknUYr0B/YlX/RICcTz4BaW0Wg0YHNzcyVIgYEk7IN10m2UjxRHQXqXWyMa4OfBNWlhTuONB8Sk61u9mE1yPrS83uC3VAfHG1mWXbn+kPKBiw4AEE6veKndbkOWZfDhhx/CeDyGH/zgBzAYDGA8HocyU4OaqaThK/78AQ+gczxkBQA9jucv6ZKuIzDFsTDHiHzhE/nim/qkYKEkpxhspjYLqYhcb25uwhtvvAF/9+/+Xbhz5w5sb2/D06dP4f333w92AUnDrSn+Fie66MAp1RegizB8Zz7tVzwRiyfZut1uyBPTd1LgN4X43NfSUL6R3/F4LPpOAGljQOWN1wUA4fYdgKvX1a6L+CLbYDCA4XAI5+fncHFxAc1m08yf4o9wuaJyU8b/jOkb7kPSejU+6TM1Whup30ZlPSVYK5V9XTbGwi5WDEOai/g9fa91c3MzXEO7XC7h5OREfEOWUirGxrIpJovxKZVF53csIAyw+v5vWaLlSDoC4wyU6OlRKUbAy62SePm1Wg2GwyF88MEHsLe3B7u7u+H3119/HWazGTx9+hQ6nQ588YtfhNlsBi9evAhvcI/H4xXfD6/2xg180tvAUhu9c89q1y8qvrPkhFLV7bf8Zk3nv4rjQNtyk7zfdP23lW7DutJ14Tlu+2JUFlt7y/eOgYW3NFpZjOWGQlpk486K9E7ObacyAW9aBlcannIlMF+Ul9hglx0LizeNfws8cn4k4KHlifGS4kxy/mnZFKBadUvtTnH8tXK07zTyyF9VisoKeuDfknPEF7vpCZOi8sr70JNX63dJkWrAhOrFLLs8YYqLsfjv4uICBoNBSCMpZg2Ia3zz/uLOFg8IUEJ9jZtjMJiEi7FaQNAab+k3bUxTxyYWlJD+lnhcB3F58PDK9YgVQNHkJYXKBIW8RPuhDHDCsqS/pbRVUSygmdoeTE83oM1mMxgOh3B6egonJyfQ7XahVquFNHShJeakpwQnpLGx7IwmL/x7POGP+huDK3gayhOQvm3kce6t9GXrtIIJkt6nYxmzgZoelnS4V6d5yYNvrd+5XbLsA/YJfyPWSyjHOFePj49hMplc2UyqzcGq5N2yC/z3GBaTeON6YR1UJChVtv+kuYHkweUpWEzLy7/jGJKnlYLX/MYY/J7rC1peim9FdbN0cjHWRtpPiOObzSbcu3cPHjx4AG+++SZkWQYfffRRsj/GcazVjhifvHxLJiz9i3/Tz7gwMx6PryycWDxJGEkaR6msoj4ijlOtVgubVaSrpDFdmQ2jWNZ8Pl+RZ0vGUrEMzyvJJQCEK2Xxf+0kfhniOF2aiyn2wqMjPb6PVa5nHsTsA9dhRdviSSeVW4VfpNlJrotwIR9vu0J8YfWB17eR0lGfwLJpXtLsGtWzqT4qJcnm4P+4kYhvTJX4ob9LPMVwjcZXyhxBfXF+fg69Xg+Wy2Wwl+12O9jLer0Om5ubMBwOYblcQqPRgI2NDej3+9Dv90O94/FY7aMYRpL6wEovtf06SZLlqnzrWD1FqWo/DimGt1OxjDQ/UsopQjchQ7epfoB4HKrIWFQxfpINqVIXFiELY9DvU/pQswVavfRzDCcV1ak8jVWPhAtiY3DlZCy+v8Qz4kLDzs5OuOIST4JwJ4oaMsyLZa9TKKoi7jjR9y1pGinwI/WdB9ha6dZBReriQQOpLM1x9DpeqXlSyLp6FduGIDw2ebxjqpGltK6LrMClFDywSALY0pVwXDd4gwp0bGh59O9UGbPSUCNATxBIvLVaLbh3717Yhc7nx2KxCNdmxYLFXK9gWTyfNOcajcaVdJ1OB7a3tyHPc3j69Cn0+31oNpvwySefwHA4FG8/wPpS+uq65DZWj2Y4q6J1lImBzW63C51OB05OTsIV11X1a9lyPIE6LzgsG1SpgqxxTB3jPM9DELLX68F8PofZbAY//OEP4ac//SkMh8OVsmnAhevKlH72Eu9zKyhEv6M81et1eP311yHLLjdyTKdTOD8/D+/D0ZOJv6gkyXfqWMTGmcqHdcqYv0lVhqit5ydwi7Q55njR74rwTwMc2A/L5RImk8nKm24SJsGApxff1Ov18PbgbZRvjz1cl66lgV2sg25UowFm6R0gXACiefDNWH7Ch25cofXQNFRmsywLJ+L4FbIalcEMvP2W446Ev7daLajX6+HKWLydBGUbT2pJbdAC2+sgxN/tdhv6/T6cn5/Dw4cPIc9z2Nragm9+85uwtbUF//t//28YjUbB7y8TfKVBGSpPGBivkiRdh3EPfOf9rbfegnq9Dj/84Q+TFy8leeCxE/q7d+56ZWA6ncJ3v/tdqNVq4dp11Je0j4u2h+en2KDojRkxnMb1DNbLeeH8Yd6UYKU2h+lYaoE+yZ6mzAuce55r1ouUb6VF28dllmMFraybwvraQoZG9EQO7W+88aVer8Pdu3dhNpvB4eHhinzz0zx0vL0xsTzPwy1i9G1UyebROujtCSn+MZfJGH8eqtfr0G63odfrwWg0gvF4vPIOLuVbC6ZT/lDHW3Oal5EypyU6PT2FyWQS7DLAy5g20tbWFnzlK1+Br371q/CNb3wDHj9+HK4pHgwG8O/+3b+Dw8PDwFOz2bwSW4z5ydrv1xknLkJV+tbXSVaf0zgcxx4o35KNs/yQIvz9ktZPNx2bKkKWf7FO8sQCKDbTypDmXpETsrw8L66roq+4beI+K/1bqk9cZcjzy91AW1tbIdN0OoXlcgnNZnPl2hfJIZdAL2eGkgRgilIZJSc5RjEAwdN4gYbGexHh4/VUqUxSgnBSniL1FC0r1bmR2hZzrFKpCpnm+cvKR2paL8D31qMpyar63KqX/g9QbOEF4PK6MhrMwBNjAJeLoq1WCxaLxZWdobxuryMb41OTX3QmAS53rQ+HwxD404LLqfNI+jslnyddKsD3gISqiNuPGM/893q9HuwqLqjTd+essmK/0frwb29/pY6nJ/gcA0pV6oDrAtV0PPGphnq9Hq5qpG3CvyUnLcuylbfzLJL0hBTw0/JoMiHJG6bFJyrwxKBng4kH99x28swvyaHQHHH8OzbOHvtrzVs6llJg0sKoGl9SHirTFnnntkdmpL7EYFcKUcyPAb+DgwOYTqcwGAzCFdwevb5u8swfj24vG6zUSMMCUhDVsmseG8WDs1zHAry8rhpP6Uyn01LX4nIepb/pd5a8S2MSwxHWBg6pfv79a6+9BlmWwfHxsTtQodWHNzxQDNPr9aDT6YQFdt7X2jzy6DcNK3vnRJG8FK8gtdttaLfbsL+/D9PpFObzuTtoFJN7+r3WbknOpfJ4TAL/TafTsMHE4iNGFpaQ/rZIa0fMN5J+k+wcJZRJuiHOa78oT9TeU7muAqdbvHhxdhGK+SuWfUXsamGcMn63J59Wj2feceJl4L9erydujObz36PbLHmRfovJqGaHKD9SGV5ZSyVcrMbNRVz3W3Y+xTZ5fKWU8uiNKOiPo9zP53M4PDwMG6c7nQ7s7OwAAMD29jbM53M4Pj6GRqOxYgfxxjRN30jYskjfe2zaXzaS+taDOawYhpTeKr/oXPrleK6PON62yPL9NOzlrVv67CGP7dd0jBU3SCGLB6+d4flieWM2s2xfxrC55M9a/Gjl83rUI1+f+9zn4P/7//6/YDR+8IMfwKNHj0RGcVc6EgaVU6msYqkquGCd/NTSWMSFv4wjfJ3E+ZTAtpVeSqPVk5qnCrLaVrQcK811Gk5PfeuWQWmOpDgf/HdeznXNIYm3/f192NnZCUFBepqjVqvB/v4+DAYDODs7U8uU2mQpfGm+0aAX1o//49WhNP3BwQGMx+PoW1sp5NFnOF6pp6JpHUX4uo45h23jOyM9TiJeg9Xv90N6vP4PF/KKXL1J+ULyvC0okTeI4rlRQONtHVQElKWSJGN4dTluxsBd1RZmoDvwY+XHnMoUfMEDVVI+eltKvV6Hfr8fTvtjoEEqV+L3JsgK+tA02nexspG4Hk4lfmoW7QHX6ylzDDcM1Wo1mM/n4ZYXGpDW+LWcDCyf8uPhyxqLsjg5z/OwOAKw+mYyPz1CHTfsk16vB9PpFP7Lf/kvVwJp10XcNnqcvpuiFOeXnzryygF/DxLztVotuLi4WDmxhenm8znUajVot9tBFw+HQxgOh+LtIUXbjX/zIDNtI+WLzxuUsdiblkX4A3j5bt1sNoNmswn/6B/9I+j3+/D7v//7cHR0BMfHx5Bl6SdM0RYU6Uc+9tTvovqI4lqMJeD1uin+Fl+0l2RVusZW0kdIGxsb8Pf//t+HZ8+ewR/8wR+EhejZbKa+k4o6KNbfnsCe1n7aNuxD+m5orVaDjY2N0vLvJZyfEl2HLuOn8fFEMM63PM/DUy2Ypixfml6j77NLcgawehOGxosWCFwHxpLkCecl7Vv+HrQHU66L5yIk+dycZ9xgub+/D6PRCJ4+fXolj0ao47TTnRSLxfikvEqnpC3f2tJpVcgR5p3NZjCfz6Hb7cLOzg6Mx+NwRboXG3M+tD6U5EtqizWfsFyafrFYhJtWut1u+P7k5AT+8A//EO7evQtf/vKX4bOf/SwMh0Po9/uwvb0Nz549C/nxX7vdho2NjWAfkCx5+CVVSx676imD4wc+p+j8k94JlhZibhuuT6Ffymu1VGXMyvLfr2vMeAxD+s3KW4aoP1H1HPPEt1Jt6sqK6ec///mgRO7duwedTgfOz89hMBgEBxfBPV5xSU9/oVHj14B5O8MT0I/9XlWna52dWr4WYEoJVsbAbGq7qzBMnCxQqgWTrXqrUkqxgKL2ndSnHNx5+fMGrLR6Y6QB71Q+vXxo4FeqL9anPB//2ypfCnDFKGXeSToM68Xr9AAg7IJcLpcwHA7FxU5Nh0j9E5MB3ge8fVwvTKfTUB4PyEv5YvVSvlPmPc/nJWseWkT7swqKyY5llC0ecAfxbDYLu4jL8CGlrRKMeOdqSr0p89eS/5Q6yxKfq/wksycYQH+n//N6pDKtslLmiFanZjuKBm5uymmT9FaKTcb0KbJMbYV3zDg1m81gV1L5xTKzLAsbAqjtonViOo+u1IKuUjuKzkMtOMXHgH+WsLVVNi2HvmXP/RVJ/qu0KbFxpe2Kzb2UMbwOsnix9ApvB5U7+j290pvrUHo9Ko6xVO+6KVZfp9MJi3noZ9N8Md0TKx8XEp4/fw4bGxuwt7cHAADHx8fh99SABcYJ5vN5uI6y2+2GTWWIaXA+xcriFJMbqrM0PavpqpR28hNduNnjzp074X9+QlZqRxH/vChG1mxcij2yeCsyf7wxB2/ZqX499ePwylnsC8mP5HbE8v3XFaOgfMXqiOl93k6pDv6dVXeKzyfZbU+6qihV3rEvcQPbeDyGLMvCbTd0g4GEVSzskNJGy9ZL8ykVF0r8aXbX8k848Sfq6NWuGv6Mzec8z8MNUthWfvOY1kbarhS5RT4xzoOEi6zL5RJevHgB7733Hnz2s5+FO3fuwObmJuR5Dn/rb/0t+Pjjj+E73/lOKL9er6/Eini9sfHz2IUiWOGm/LLrpJgOS+kDy8cvQkXsPdJNj2csdvCLRBqGsXzg1DKl8lMoRb9506TykWLftDo9/eLJ69WpUvn8dzpPOTaTMCT+JmFLSU+vLMb+vb/3964w9vjxY3jnnXdCxna7DRcXF/DJJ5/AdDpdSYvGkn7Osgwmk8m1O8BVUuxEKCUrHXd46Qk2jWKGtYrgl1SuR8lboNNbT1nyKJkYn7EJ6a1HIhoE0uq3+CkKGIqk9+SRHDRvPTHjleqwlwksaI4A/Zu/C4GBslqtBsPhMID0VqsFDx48gMViAY8ePRIDgBbg4n1pyYi2I5nmp3wvl0s4OTlRT+bxtms7dK1AV4xP1Cc3YQOuCxTyeY7EA4HSb7VaDVqtFoxGI/UkNaZPIS0YWQXdFNjWQM9NkXTamM/B2Jy2gi1aEMuyY/RWklQwSgNxkszgm4bL5XLlzTAa7HuVsZ5ERTBW2T6o1WrQ7XZhuVyGzT24OJuC+/I8DwHEdrt95RQLXYT08F2kXSn4QAqC0/Zqb6fn+dVF1BhP+A83meKb77VaDZbL5crJdq1OL/6MOY6ag6b1Cf4Wa+Nt0pWUNEeW/0a/k/rBOtWIb7Dim78YyNXopvrozp07sL+/D2dnZ+GtVRqwpSdoLMzL+wfnRLvdhkajAX/0R38EGxsb8Fu/9Vuws7MD77//fsAeuLE6hSaTCUwmE3jx4gUcHh7CvXv3YDAYhPnU7/dhOp2u3JoFcFWfUnmOtZGWQe2NFHznfZLqr+Z5Hq6sRHt3fn4OW1tbcP/+fdjc3ISvfe1r8Omnn8JPf/rT8NRE7BaMKklrE30/FSC+Id7yta7D79TKiek6bh848bGnp/bw1ggA+d1Nis8p0flY1B5ymfeQFNRLIe2deeqjYtmW/FKdTfsdy6enGDmvsROgMawco6J5eT6Ui/Pzczg/Pw/4q9lshtsYJJmLbeQC0E9ESvyUja1IlGVZwHz8GSX83/JJJH7p9/QmHSoDaH8xHiyd7JXiDCiLzWYT+v1++A03vkv8WXjLIyNZlgU9ThffcWMTysd7770HP/vZz+B3fud34Jvf/CY8ePAA3n77bfjmN78JP/nJT+Af/+N/HOwfPmWF7+hyXqWF5TIxvLI+SNUxg5smbb6VaaO2WOPh4RepbylZ7bqtMuXBGVVTrC9i/fiqkObbVlm2RWXqluwq4iTvLY/SMyZS+pXFWH4tEABceSNWWgCgTPIgpNY4+nuRAFcVk4PX73XQNIDlWcjiRlhbiNJ4lH6z8sX4KZO/KHnrlZxyy6mkE6cqpaqNjwaerTI0sKml5fXG8krllKUUJaYtIKTy6QmaxJxv7buUfNImDNzdiI4E7vw8Pj6Gi4sLmEwmIYiL781RGaKf0WGR+LBkxqs3aVCIv1tShXxoepPPmZTxLBLgsMZX461K0nSz1c/0qi90+FDe8Fpbrz3i9eL3/F8skKvROoBUmfJoEPY2EA088WvLrXHE9Lhbmr5VJKXlnyWQy+cegkYtAC6VzeUG5RTfm+Nl0TZac35dmKKoPpN0ryegoxFfNPE65ZxarRY0Gg3odrtXrnnUQD0/IYi84E0IUv301JcUlEtpf9EAkGUrtLTS9xxbS0TbFQs2AlzFmpod1uopQ0UdcWybpB9RF/DyYxhU40GaLzHd7NFvKfOZyo83yOr5LkYU+8QWLng9VP4QM56fn4fFUN63dLMEzVsEJ8VwgIe4D/vRRx9Bo9GAxWIBR0dHsLm5CVmWwXg8Nq/qs/ikRE9WcXuj5U3xlTC91DfYZ2j3Tk5OoNVqwWAwCAsBRfuyyhgG8qr9hu2iN5xJ/keVPFYd35HalzIHqPxPp9OwMEsxTowv6TPnmbcjtQ+LzPUi8SOrbq0sinM1rMT9wlT/lY6rhk15Wfz/WFuKEOUJY6K4ccuz+cKDLyW/P6bTqN3lZWhtQNIwaxFMrelmvsltPB5fKV+b29g2bnPyPIdGowGdTidcf+zlOQVbaGXOZjM4PDwMv/385z+HRqMB29vb4Sr4brcLb731Frx48WJlkzXaE9qmMrJaBTbg5VF5WJffVpaqxN9aDIf/RvUP9g+9CRRlVZt7Vl9WMXZVU9Xjb43ZTcmZ1e9V8KS1zSMLWnlFeKBlp+DxqniR8FqZ2FCKj6mtWxYZ81SfU6uL13PlYdcsu3yPB6ndbkOn01ELpISBRSltEeUUI+9geJR2CsBOcQSp8uZKmgc2NX41gYmBtaJEDQsH1pysfks1ljwPB/zYl2UdixRH3QI4KbxoZXkVBOc7Ni6Y3tsHEkntswyLpOQ99Vtykqosaf9Kc1Y6sakRfwMTT89Q/dhsNsMVcBcXFzAajWCxWKzcGoCOCP6NQSXrJILkXPK28N+5A0fLx12ZeA2u1m66q9mq1/pO+z3m6FUBQtcF6mIBFktOJT1N7eRyuQwOZZZl4mksL1/0e64vJQc4Npe845viBMd0Fm/Dq0CNRiPwvlgswiJarF8ajQa0222YTCbhpL11Op2OWWx8OACljiLXg1K/87dLx+NxeOOYpuWBqOt2KqsA8pZuk2wV12sUq9RqNdc7rFIQBeDy2tJWqwUbGxswnU6v1C/xQE/4oI2Zz+dXrlijdWMwXjoVxNsbI0mGOPGyNP2YUh+1VR7d79VXKNex9J52U6pqbnC+PDjB0heefkG9YJVDNwVgniqDZhalOseppOkI2nf8M90Mp+mR4XAIABA243C9gPrEGmtebpn2WWXQ96uRv3feeQeOjo4gyy5PI+/u7sLFxQU8efKk0JvLvJ9x4xoGsPnVwbzfMY/Hf6XtlfQstZdZlsHBwQEsFgs4PDx0YzReduyazpRYBC9by4dpcIMovWWh7JwpInMpPrhGKViflo9PzFD5wXchPf3BTzp44wf42Uqv4XmtnVoaK/4i2SvPXJFO0NJyKP6hVOTZlRT/sKy/5/GDKOF75NguiqG4ryXhaq6bpLq08bDG1PPuLL3OH/UqHx9PfEEi7UaAVqsF7XYb6vU6LBYLOD4+ds13rZ/wc6vVgmazufK0j0cnpGAw2qdU308mk/BucJZl8MMf/hBOTk7gV3/1V+H1118HAIButwtf+cpX4IMPPoDHjx+H+rrdLrTb7bABO2Z3vfxKdsEaS+lvWhfK6rpiKtdFVv9STIsk+SmYTnoOA+e59tniq0y/rhvvYh3XQddVTww7eflI4bfqtkm+XKrvTPlK9SU5L7G0tJ6i/RbDTJKOLBpf0PRF0fmq9TPnZ2UxttVqBeNzcnICn3zyCZycnAAAwMbGBrRaLfjggw/g7OxsZcc+ghPqdOV5Hg38SwxzigUPrDRe8jo3GniKORaoyL2BIInWOaE9YO86KOZ80KuXqtjVu25Dpp2Wthw+j4zFHEYOqIqQlJc7Glb9kpNHedM+I1V9zVdRuea802trsiyDdrsNeZ6H9z7xRJwkyzxgTxc18FrEojJLx4YGADkPVhlesgxzrJ4Uub9J4oul1NmT5rV1YwQSzUMXyjVZ98oC/Z8uTGi63dIP0vil2AePnHmB1W0lyj+ekkfdgAvpFxcX4jVtGkkAG7/38GF9p+WV9LR1GhDfxkNdR+eIxsdtH0uNiugjenLL024toDsajcJ7hIvFAs7Pz2E6ncJwOBR1D9ofgJc6fzabhWvdOp0ONJtNODo6gslkEoJwngCkNo6SDkntM6l+7rhpfcSJBlVigRD+G9e/1ltkvE7pf14HD8LSuWbxuA6bGHPiUW9hWso/x3/0n7RIR/PGfKRU4sFvrsuQl8ViAePxGObzeak+tXSt1TYtwIFparVa4A/fsqR+eBnCvsdrFcfjMTQaDdjZ2VkZQwySe8ujbavVavDkyRMYj8fw67/+61fyYBrpRHasfEzP5zb/LVae5sdwfvhik1Rfv98PJ59qtRrs7+/DYDAI+dEGpBCdVx5s5MUUFxcXK/iE1kNPYdOyb4LWGVSm+mmxWECv14NOpwNf/OIXodvtwne+852wGYLjH9rXKUFaqg/LxL6qzEd5Su1vqqus25XofIvZ4FQqE8uIlYv/W3G/PL+8ZeTg4AAuLi7CJjl+wtEq09MfFpbitlTTlUjWaSCNL5STmO8W8/OyLAvX8aIOwhuAcJ7RU/qSTHI+F4sFDAYDaDQa0Gw2g75GfIvPEljjWEYm+cLsdDoNm9zxuz/90z+Fd999F772ta/BaDSC/f19qNfrsL29DQ8fPoSf/vSnoQy8Vc26EUkiS/9rtg771xtT4Dj8F4Es3JvSTp6W6yb8m/qDRcmaZ+uymb+o9IvWX1Xjpts216vmx7ILXl4oT+jbeHF7jK4sxmKh5+fn8NFHH4XPnU4Her0enJ+fw9HR0Uoh6EDyhlknfKTgjmRkYh1l/V4W5HqBtMfAadcTp/BDy/PyFys3BkKrqCeVJ+1vBL/UadICIl7Q4SELGGuEE1VTmBqAlb7nQf1YvZb8pshdqoLR0mt1llGOFmlBnTJEg6gYyEDHJcuysEBBg1pcfuk4Zll2ZTEDA4bSWyrS31q7qPNspbO+18hyNLUAYlV6SiuviBxp84nqGRoI5MFKHrCh8hGrG2B1l22RxVitP2KBvaLjbQUVigZe1jX/r4Nw3qNDjW1J2YBGydLb1hy3gi2afuayTz9rb+ACQNiEgqdr6BthGp+3mWJ2mesF/E4bK/zO+2SFhncx0Le5uQkAADs7O3B2dhauOpNOmqHt6HQ6sFwuw7t429vbsLW1Bd1uFwaDAQyHwyCzqUTlIQUj8/z8sybvXr3i0XUaTgS4qn+535KKJbkt4N9rJ6c5j0Wd7Vg+rd81266VIeEUHvzjJJWtpdX6MM9zc7Gcto/fOkHbWYakeW/xwvnCz7VaDabTabhJBd8mtMbQyz+tp16vh2tZe73eyhW7KRszOO4BADg8PAxtSBlLi/gcsDBS0fGk85TLM8V0lNrtdjgVV6/XYXNzM2yqtPrSaxvL6FOpHN4u/J6e8PH6p0XJGqOYrkuN72h+CZ7aww1KX/rSl2Bvbw9+8pOfwGg0EvGUhXljPHltTxWYqSz21nSslCd1w5lEZWMv68jD8/G2IabHwykAL3FYapwsBXd68ms2lf6vlcvTST4CTSvpf6nsLLu8En0+n4dnN/DNWKqDAF72Y0ye8IpjevCnXq8HfYw2VMPVVj9oZLUXMTfOieVyCT/4wQ+g2+3CxsZGuLa41+vBgwcP4OLiYmUxFvUyLsZ6cZvEX+x7T/lSXdItDla9t4GK2jXaRx7/iM576cpp7h+n8k//1sbvVfO5qySPn5JCltzc1n6m+FWi1HmPebBsWo+VlvMk/VZGd8TyavZLSsPTx3SoNQdj/S+VpfF45WjDdDqF58+fw2g0CtctAAA8fPgQDg8PV+6+z7Is7OKlzjG//owT7xTNyJRR+Cngmaf11OsNFGFaz8KIRJZwpRJ3yqzrSbwkCWeRPDRfq9USx8Qb3CnCt/R7VQpYAjJWAFHqm6qcthg4L0qSnK4DsKX2gwUiPUZK63s8/XZychLdHYNzbXd3NwRulsslDIfDcKUxBoA97fPqBBrI4lcuW3m0+vB3a8GG56k68Fkmr8dg4o5bmrZWq0Gr1bpyDROOubQ4Qk9LYjpeP5YLcHkajl9bJLXBap/UXqy3rA4pYrO837+qlOeX7xbR5wboIi1NZ5XB05TV9ShvnkVBeqKN1h0DsxLfVTtHN0lFF5itQCaWS/WBdh38xcVFOAmLi6f01KIHH7/99tvw27/927CzswPdbhf+43/8j/Czn/0MhsNhKIuOmWd+Stf8VzHmMceNBkhSeI451tTBGo1GAPDSfym7yUCThRhWoDYlFvDkn3Exj16VHqvzOkji3etnSek8m1txDOmTEa8K5fnlpgwcTyoTlh+E/UXny3g8hslkAv1+H5rNJvzxH/9xWIy9uLgIb7uWmcfj8Rh+7/d+DwAAnj17BsvlMryriGTZFotw84923TGVEc2eFg0E8U15k8kEDg4O4Pd///fDlf3Hx8fhhBzejGPRuuYh9Qtw7NHeYDusG1xuUj9ItrDImKUEDev1Orz11lvQ7/fh008/Dfif2mjJvvHgHC9f09k8mEc3a6+j73ndnrRWW9Hnxed6kG9uJ1P8lRhPKf75Om0c1oHvd3s2A10nSX6gxQcdb4pNPXVI80K6jUuiWq0Gm5ubsFwuYTKZhLw4/zkPKbLl8XG5nHtjKdr8Xi6XMJvN4OOPP4anT5+GG2p+8pOfwL179+B3f/d3YT6fw8OHD0Oe6XQaTunzp+qsYL1GRW1bjGh51Pd41UjS1/iZE6bh8iz5I/R/CZ+sG3P/ovjaRalKv5PPwVeJLF8qFXNruudVie0U4dFzuhXJkpei+ZEaPOFyuYTBYAB5nkO/3w/fD4dDePbs2UpmVEDcGcLr+nha63/emDJKLLZAoKXX0sQmbCyQJPGiCY2Vrqo+oY5GFWQ5QJ7JQUEUgjIaeANYfW/XUhQpkzE2hlWCfEl2eP2avFht0oJQsfQeYGLROhQzb781JrH8PJ9VLk/jIVyYQ/BN9aDWx+12G7rdLuR5Hq7PA1g9TRcL1Hq+09KkOsKpfVJGpsoYNEsvpBDKlxTcswKh2ve0LKkvKYDHawpTdlHG5rkUnMPPUnqrDJ7GAoASb1XMudtI9MpXOtaSPEvzg853q78peQNrWIann6XgDP9eyuMZ5yqprFOQGrjg4yfVr423Vh5iZOsmAbQR/OS1xEuWZWEDG+Vld3cXvvzlL4cNk5ubm9DtdsNJIE3uLJLSan3C+8MbZIrhrqLjH5tP9OrOKrGxd45YGDCWl+bBhSHJjln5POl42hg2jQUKYjIh9Yenn2h+XGi8bnuj4RT+G28LzndczNTSxuwvthnlut/vQ5Zl8Omnn4aF+jx/ecLH0xbpM9bx4YcfhrHBaylpGk4emaNjLelLTb7K2gksG+37fD4POnk+n8PZ2Rk0m03Y29sLi+YAoC7SpNibmDx75hX+j/0nBYqrIslPK1MH578sb9hn9KQS3ua2v78PFxcX8OjRoxWbzJ9BkvQU118x//W6KEXWUvU9ypJ2u4O3nBRMqn1fhT6XZFWTP5x3VT+hZJGG5WOxgDKxHS8/Fu6T0uLnRqOxMh+576FhPlof4hvpOnBPe6vQK8gHAIRDSpPJBJbLJTx+/BiOj4/hd3/3dwHg5QIs5uFvkWtzw4uVPe2KyVLKeHrq0HhcB6XoMW96b50U80h96iFvH3n8rKJUBWa6yfJj5Okraz6l6GKpvHXiAa8cpPDD9XKsPE+ZUp51kdVWDbNqaaV2VolTAdhibGowOIVQWVm/V9GglIHWHEQpYFqUpAl83SDdc31oLKgi5SlCqfkxmMKvwkbnWOMzlYeUcYmlk3a7IXneupXquy4Fhs6qRqnzK+ZQpZapUVX6QyvXEwChwQ/8jhIuxuIJnM3NTRiNRnB2dlapjvHsYEwNvnoMtydAVCUV0V9SfgxgZ1kWrjSkRHUPfsYApDXeFnGdiraX2uAiAXLpexpUpOms63QtoIFlVukIvEpk9TXV/R77hAFeS8do+j/W7/RtL+r8Y15LN2sg+7qcC42suVyVjtECXVKwFXUDfsZ3w7kelvij40PTS+834il61BXIT7fbhW63C1/96lfh4uIC3nvvPRiPxzCdTqHb7cLdu3fhP//n/wx/8id/EnVIsF1UBvn/GmZOJd5meopzneSRE48PpAVbsE3aPMY0KYRlSptcabmo43ExFvNIskvL5e3QAj78PVhKXK9UNRdvwl+qmiwbS08q4omze/fuwc7ODnzta1+Dp0+fwp//+Z+HAK6kx7EsxO44BnTRpGwfcjxA60WZ63Q6kOeXJ3p5EKsKeaDPg2j8caJ1SzaPfqanSbFd+Cbg2dkZLBaLUBaekDs6Orp2+eRjQT97sVlRXVQ1afyhDvW83e2hTqcD7XYbxuMxHBwcwN27d+FrX/sa7O3twaeffgrvvPNOuMJ7NpuFjbISv1rf0Tko4UA6TlL+65CjmL/Cf+MBWSpveJ36eDxOwoexwG5KP2gYu2hfamPD0+BGGaoTbiNxPUwJ7QnaCX5TCubBOUhtMepIXFjlddC6cLM56m8sj26OwHqQDwuD4cZlPlY8BiHF4KqIR9G01F/nsbMnT57Av/pX/yrogsFgsPLmMhI+LYInhYtQFboj1l/cflLf5abnQBGc6OHZg12wXslWYUygCgy2blq3b31TMlIV/3Rue24dA1iP/5Kiq9ZBVfp3lMqU6b2ZoQh5+UoZa6nMlcXY0WgUgtHYuPl8Ht6a4YVxxwzJC8K4I8nJI3Qp36c4cTepODgPVU5mLYjknWBFeLLySL9JTg6Xt1j9lmHx5vG0UXIGODipYhzpuBWd9BJQ1T57ypQCtWWco1i7ioxp0TpjOk0bY/45zy+vvmo2m9BoNFYcHvqZlmn1g9a/Gq0TAMZ0Bm+HZ35ZAYIiYFsLPNCgMf6jb0vxciwwTeckD4zFiDqjZQE7d4o5L9TRla6ajpVLg8Be0uYR769Xkag9kgIDnvxWWsnx9WCtMvaGj4lXP1rfXccYpwBmJMn2edtFiV8Jien5lZDe/sHPo9EI6vV62FWP31M9gQsGu7u7IdjV6XRgZ2cH7t69C71eD05PT+Gjjz6CO3fuQLvddmEnLfCCgYUqdBWWByAvQNO+kIjrEo+s0XnqGWsLx8VsMdVxXnvOf9NkwyqH2jRLZ1j+kIVttDycDw9OL0uS/rXmmYensjxL80b6nQd06Pyu1+vw2muvhXdYqwjq0LZr//P0sfmHmIBuAqha38f8djovPfNF0vsWPuQnXii2024AS8XpFn/S75avJaWPYdcqyeI/1W/n/V50DqCdvLi4CBu5a7Ua7O3twWw2gzfeeAPOz8+vPDMhPeXkxfWSLuWfi7SJ6/qifVI2H7Ux9CaGFJ/U4z9q81qLYcQoNndSMYdWDq+ryrnmwfge7Cn9L9Vl6RBqnzS8QecRLqR6fUmpPL4Y6xkzzrP2WyrR/sP3YwEudcfjx4+h0WhAp9OBxWKxchqY94vEn5fK6pHUMrlMl627inmRwocHR2vfWXhXwhEafquizevG1wA3txZSFXl9KAB9rCUdlCJrXtxT9XjG7KuHivBk2XirvNi89MTMyvZlmX7R2irJD0+7shj7zjvvAMCqg/Hw4UP46KOPVhZjsyyDdrsNAL4d7ejMSO9NcaEuA1hS8knvz8QmWMogSwo4NThRtC94PlqnN4hWNWjEv/kkpb/jbn4qKxywTSYTmM1mKwGNIjxZwJLSdQSTNJJOsqWWWwTUabJzHUTHhgJ8CwBZRANGUh0AEHaGIuFJNYvwbVEpLTr+eKXZ/fv3YX9/X33zCnngb2zFKAaULXmJ6RbptyInpjVwir9dJ/jA+qkertfrK9fpSYS7VnFRHXf60jKx7izLouXRfKjLNFvoJT5HJAe6Xq9DvV6H8Xi8cspEui5Ks1tcJ1GwUda2VRmsWEeZku3Cd34RD3kJ3wKsGoRnWSbqkZhD4glY4Oeyb2nedtLGggflPXM2pY/wxCueuvrxj3/ssuNZlsH29jZMp1MYDAbwta99Df75P//ncO/ePbh37154bmQ0GkXfNOR6BHXDYrGARqMB/X4/1IP6RGq3h7Isg42NDQB4eUqjzEkoOhZURi3Z50GloiSVSYPVPHjssd08XxmybC1/AoTbEfwsvdlL00lUFIcWIe5fcirzBIAU/LdIWsTBcni5+PtisYDFYgE///nPYTQawd/5O39n5UaOooRzGG0VLkrR65tj8wR5xLGv1+uwXC5hsVjA5uZmKPMmyDt3KSaSbBrGMnDuLZdLE8tl2eX18HgbAurD6/AZrWAXT4u61fLFbyOhnAG81FFFNgHRPsC++/jjj6Hb7cKXvvQl+MIXvgD/7J/9M3j33XfhP/yH/wCtVgu2trZgNBqtnJD12mK64bHoglNK22I8cf48dXFfwLrqXcJBZUjSs9xvrDpGGPuetg/7A3/X9I/WV1UGwbXxpLzG+ML5kULWe8cp+IrOcYvnFPLEr6gsAaRhA6vefr+/YjPoTXiNRgPq9Xo43DSZTFbes/eetquCPHIBsPqeIs477V1f/r7qTVDV/nSRujlJNmsdMYkqy31VaR0xJK0eaVMJ3ZiN/3N8zfmjN2QV4aNquq4+LOtve9t+nT6ohw8A+RT9iqch7U5fLpfBSSmibDVBtAxvTMFUpXC1YCPlQzNa0gKMp45USgHOWF9VQW6Ld08w1ypTIvo+mhXc8RggPqYpixxWuz2AUQtscV4lpzql3bw+TjEnXSPafknWiwZeed4UuawaZPHABC3b6mccI0lOaXukE5YIzDEQNp1OV8A48oKBIKlcT3uk31JkxBvY4n+n5vNQGVnDv625g2OxtbUFrVYL+v0+LJfLEGgcDAaBDxqELMszd/LpP0ufae30kqSPtLll6Vm+w9dDXr1bJa0LgPG5nxok5Lom1pcp7aCB11jQVgsmXRcgv06ielDDA0UCvbHfis5Tfi0nHSv8d+fOHdje3g7BHVxs2d7ehn6/H941fPDgAQwGg3CFstYXVltqtVpY2EW9SHmysLKGbbgepPpa6j8aDPIE3SxK8UU8zl8V80WyV9j3Ek9S3fx3i3evvpFkJtbeWH8VxXIWlqzCJkq4XMqT2qceyvN85RrGZrMJ29vbcHp6GhaGUtpI20LnTZ5ffUe0CK9U1jgmuC57HvNzOG7Rxs7CP1z+ef1V+ycp5PFTJXxxW+078qmdsvbgY64PcbEdN8E9efIEGo0G7O/vw+bmJuzt7cH29nZIa8UPrHql74uWkUJl5pslE1RmaB00D+oRj18Ui4lw8thdWr5WhpQmJlseXEgxi7f/rwtbx+RN0lsappDiHKnzkH7njU15MBctL+bvW+VUQRj3wQVZqss4aRiXp+E8emxXEXtE5VLSA7GyPbELTzsppYxLFTbYU5/VT+uisr5OFfQqYIeq+eJtLuPTSPqUzxmPDKfIuaWPtXK8c8BLXptgkebfenThdVPR+qNHeOr1unjqwzo9xo1jmXucY0CtqjJxUvATct56vGluWlA8AKqKOixjTr+j329sbJQOEMSoCEjz5uHvOHnk3lJUWlD9uijFybitxpkTtkeSM/5mp5SGngbilGWXpyJxoRVpOp3CcDgM9S8WC5hOp/D06dMrDiwGi6SAgEaa08S/q0qG1nEvv4e8gJTrH8tW4amGL3/5y3D//n0AuJSD4XAIp6en8KMf/WhFHlqtVlislQgdr9jiHPKnveln6WnPXOPXEEu7bjU5lpxhuvmAXklY9rrS6wpMVE14NSN95xMD6DRAQ4kHs+jmN6+8YF6L+EaOGPGgTCygnuo8VElVyIsVpEqZYxJ580lvdXKiuDvP85VxRV30zW9+E+7evQsPHz6Ew8PDK+/aNJtN+PrXvw7n5+fwP//n/4RPP/0UNjY2oFarwWQySXLE2u02vPHGG3B4eAgvXrxYyeOVXYny/OUNAXiyTNJfUr6UcZKCCVzurXxlAyIWvzgH6ftjHEfG8mIe3PSFi2NVvbtoEdcDsWv9r4uqDGLx8buO4NTGxga8/fbb8MEHH8DBwQE0m80wR7SgNZUHeoJrPp9DlmXhyvNOp7NyIrQMZdnlwvF8Pg92LfWml3UQt1WIXbA/6Mklqwx+xS2eekSZ8N6Gkkr8pAUlCWdQXEnLuC5fsqx9Rt7ou5IUv3LfyEt46rzT6UCv14PvfOc78L3vfQ86nQ68+eab8Ku/+quwtbUV6p5Op1G9qW0K8mIuirOui7zBZJQ7qQ9QlvA69VarBRcXFytvx2rE7biEka+TOPblxMdIi23x72IYOQVnrts/0nihp/y5XZfazHEbL1OKvcTGW4vF0Hz0fWYA/ckLzs86aDKZQK1WCxgbqVarhQ3eFONVwdO647dUBhBXUv0l8aPNKfxt3bYoNdbuiVdrefgcuY4YnEZl7G9K3qpk91Ui6QCCJjfczsXWsaz5ZOVLlaeq421S/dcpEyl9kBLr4PlS0mo6MUYrHsSjR4/C3xhoPD8/VwN1lDj44MEOJCnYoFFMgLEc+r+URgJG2neS0rQCNp7ApFZWGfLmj7WHfqZ/p06oFAOkgVLav2j0kSiQ4RPQGhMrnUZaebwPvYqIt0uaQ1p/l3FWJH6rGFepbFqHhx9Mr8kNl03efzF+pWAV1ifNd4CXi3bWnNbq9wSc0VmZTCawWCyg1+uZckH/t+YXr0eaHx7wFGuD1C8e0vjE38roQj6WXH/RvkPdoZ0yqtfrsLW1BY1GA2azGbRarZC21WqtBNys9ls2SUpbFaCSZBftOO0fydnS5gotqwqHhX//qhLnX7u62srD08f0szRGsXQaD9qbwdJ8jDne66AUnVeUpECRxyZz4pi2DI88L58/dBF3Pp+HK89wkQEAYDabwfHxcQguUx1A+ZTqpnM/zy8Xc2q1GmxtbUG/318pp4wNaDQaYeNSLLiOdlPrG8q7h6QxKoKLeDnSZ/p/bGwtfmO8INGrnqmu92BaXqflO8T48uANizx+lVX+Ou2LpCeqsHN0Xs7nc3j+/PnKkyxIGNj18EnxjjZeqTyirmk0GlCr1aDdbkOz2YTNzU2Yz+dwdnbmuvaxKtsh4RWNLH9D8rVwoVpatOX43DtPq2q3JIfrwlleu5bqz2l1xerw6ExJJugG+zzP4b333oPT01PY2tqC09NT2N/fF6/yj8UcNB5wgx4uGEkyVNYHWjch/2hbqJ9Kn9ug7bD0vyQDWr9oeWPl8bJSyOO/Xee8KoMpvTEXybfwxg143ELjPWXuY1r+vBQtk5cXkw2OadaFES4uLmA6nV5ZkInZxBT/T/oskebTxMYg5l8W8QGk/J60GsXiESn2VoqdeO1Qqt/otdFFfFEPVeVb8znvxWAWDzdBMTmR5Czmk8TmiKeuWJqUeIxVbkoc6bpxCm9rig9Ytq5Y+VXL8Mpi7Lvvvhv+xh1vvDLJKdEmobQLDgMrMSrqxMby0wCFlzRnjZZ3nbTOCREzcOsiHnDjhnE+n8N4PDYDTLy8dZPkZFKiC8ecH2sH4TooRXF4QExZB7LIvNHmoDcvAIQFNR6ooulip7Ot0ysWj3ii8vz8HAAAdnd3Qx9MJhMYDocrJ3NpXTHDLhlbTfaqCJCWJQ/oSC1H+w53tOF8pIuqPHj9xhtvhHcMu90uZNnliQ/8LtYG7zyzHFhanpckPcTbivJOT3QCXL2+kKaXnGKvDad0HU7wukmbg3meXzldJAUfubzweWqR9OabxIvGdwykx2xZlq1eD5vqwKSShkPKlLsuexZbhPdSrG30lNB0OoX5fA7D4RCGwyE0m82A1/HWhb29PbEcnL8x3ZNlL0/R3blzB3q9nsljSh/iAg49SafxgmXG7LInOBejon6E9kYo1ZVFbn3BE3jSeEm6lF7HGXs6hfJplSsFXST99arq9bIUkxEAvZ85lsR00+kUPvrooytjTzdWxfocZaDRaECWZeLCbhHCMrvdLrTb7fD5tddeg/l8DgcHB9G3qaukKuQO7RvqVxyLyWQCs9lMzFMEB2FdZcZB029SYLgqSu3jdesCHCMkb8B9Pp/DfD4PtvLb3/42bG9vw8bGBsznc/jMZz4DJycn4eYHCRd59DDSYrEIC5YAst9Px+269KjHx0fCDRe48Ytv5Nvc3Cx8W5Ikr1XIr9WP1vdYd2xec3/Iw7M2Nz1jXpVcWHEX2g7qL3tkJdZftE4JD2m4i89zqQ3It4SvOO9V+BExyvM8xCrR9lpEeczzvPBcwrK0+eSJlVD5l+JjGt9a+RpZfmPK/K9SX2JZHFtZPgFPuy65kuooqier9K25LfD6ON4Y2avgU1j4wMu/NJaeuVBGFtbVv+vAn+sgTe69OjSGL1Lj7tG7daS3D63CLaOnBYBSBYKDn9jgawFmjQcPKKbBGM+EiA0M7zeNL40Xz3cp/Fj1xUgDQFrZ+LA9BWAAV69b5GNngcoYxQBe0fKo3FtXq0iktS11HCynMTYXUpS7JsPe8jz56W9e5cYDhfg/gmNO/PoYqWw+z/l4S7xjMAyvg8R/GGAFeLmLcrlcht3HmnOHPKBOtmRW4pu3LcVB8ebxjBE3dl5ZpzzwuYL9QhdW+fVF+H273Q5vLGL6vb09uH//vnglf57nYZHcsodeR15qjzetls8KaODV2nzDAJ0bVD6lOpFijmKsTR4786oA8DzPV67AtoIyVr949XsV4FmrS5vXmp2MzdmyDqNWXpVktSHFiSq6GMv7nC62AsCKnsd6JGd3sVjAJ598Aqenp+G6wE8//RQ2Nzdhf38fjo+PkxdHcIyRF7yCsF6vw2w2cy308e9o38xms2BzpSs0cRHKQ1RmOYa07Iwkx5IjZtlP+o9jDe00HR13Pp+KyrknX4ojSW1CrEwJG9BxkPCSl+dY3fR/bg9T5rDVHzGfEf/nV8PGdKBkvxGjzmYzeOedd0L/HR8fR9vhqY+PRcoJVky/XC5hPp9Dp9OBLMvCExy9Xs/ER7E6UnVnyvii7sqyDDY2NqDT6cDp6SlMp9Ngw3HBjGJJxH5U76bKbdH0uMhd1c0LKXg/hdaJ16SbsHjd3rgLTYsbgKS8y+VyZeO35E9avOT5y6s8O50O5Hke3mpHOaLj6PGFqsZ9lt/K2wTwchyazebKhuE8z8P155jeklHut2objSQePKT5MFI8QPrf8r15PdLf0ndlbTrHNxI+0cbTU7cm23SsONbBMbOu44z5QmX9ZG6XKX5GPnEDfFV+iJeQLzwZz0/40iugtTEqyrM2BikyyTEcfpfKU0w/c968+ojzKKWx+tdTz7rGB/MW0QuethUtMzUd1Q2oD7xxySr5KktaX0r+DNUvnjL531hGrG3WvNEwTYyfoji0at0p9XdMntfFC5bJ/Vjer6lzxLLDWlnRxVjJYdMUIlfWGgDkZVmTQEqv/cbLkIChF0xrYBn/pjvPPe2M8Rprh8Uz/d8DylLBpsWjpKClfFYb8co6JPrWFc3P+7sM8PSQ1PcpSlKrLwVwePJ4x84qN+YYxnjwEJ+HsTal9hOduzQNnSN0gQ4BBAaXaPrY+HnmB+6Ins1mwRnn8xUdf+kqSU+7PTKf6khqabXfLd3iAf2p9VrgmJ6yQuDEHTZ895WO+c7ODrz22mthjGiAgDp1nnfEPfNEc3y1dJYdiNVXr9dX3m/j9k1zjjlIkfjWqGrdcV3ktbfYnut4nyilHI+Txuc/x2/afObyIpVxXbSuOouUqb3HV9QBou8QYzmabUOd9/jxYxgMBmFzz7Nnz2B7extee+01OD09TVqMleb7dDqFPM/DQpF3sUlrPz9tRrG3xwZrPPO8/DpXCVdQPyFFT0l2gvJi+UMWVizieKYEZ7RggGST+G9cNnhZHK9rfcHLLdpWXrdWj1YGbxP/jrZRG2dp4wDPQ+v16Nf5fA7vv/9+WJBLfffXmpO0j6jce7Eg8oJ5x+MxLBYL6Pf7SbwVwa6x72NlZVkGvV4Ptre3w2Y8PP2PPijvH/w/pc6YX+wto+pAuKSDrtt2S8RxLu13zqeGQzhZ/juPLdB/+GZss9mERqMh4iKrTtR99Xoder0eLJdLGI1GYS5bOCvV/1sXcX7oYix+RsLFWO5r8bLwb24bYu1J8SFT8FcR+fESx9Vl+JDkXsMwKeVK+SjPOOZ0AwESHUeNX02GJX8z1aeQ2so3XyM247cWafZe0rHeMdTy8BOmFN9aupl+rnK+p+oxpHq9ri5qp85frYyyuC0VT0j1WTx42q6NmTUnUuat1v5UbFCUJN+BbsDDgya3AVNUSXTeaP6AlAfT889VjBf3xbjNsfJ5cKCGn2LlcyprRyXeLIrxaPnevBzLhsX0tzY/tTJXFmNbrVYAowDxQKMEmPE3adKmXL9QRli5kGLdWZatOLXUSFoOOX0nQ+MP+4orolQDY5EGnKqmdSh2y+GnRl5S9DhmFiAoy1vq95ZilcAmEt8FyueJB7h4KJaPt8FD6zAiRfNJ/WTxxq9cj819/D42lyVDi4uwfDckOgz7+/vQbDah2WzCZDKB0WgUFgdRZriupBsUeJnrkJGyxlbS/x5KbQ8GQvnJZoDV65CyLAsn8CX+z8/P4eTkBO7duwfz+Rz++3//7/D8+XNYLBbiaWqpHuTfoliwwttnHAhq8jKfz4Pc0M0HUlnSKSaNX8/CjnS18XU5DUWprE29SSfECiRoJG0o08ZdmtM3NZ5lwb23/NS5qTlpmu3CvsbgLb4HqdkznL84bu+88w40Gg2YTCZX0n7ve9+DP/7jP4bT01MYDocwGo2g0+mouJ4HjDjNZrOgEyVZKBIc4OnoSYJY0KoIfpFIeruZppeuJCzilNIysHxNzvjtMNzGWL5IVddm0/ok/q06uCxIc6FqXRnDgFYe6YQPHR9tvBHrLZdL8TrbIjxJRP1KDH7RcimWRTzUarWg3W5Dp9MJV5vT0/VV6W769AcAhFM/FMtK/WDZmSoJxxEXXvEkLG7KG41GYl/QU3ucf3oKq0pcw+WtaNnU38iybOUGj9tMnrHnciP5YN66Wq0WzOdz+NM//dPwhMxsNgsyrcUbUL41+UW8PZlMIMsy2N7ehsVisbLxlpd9k9iR9yHvS7xuuV6vh6dcBoMBTKdTODk5WTk9XmXMK5U0XUPrTelnT/oYFqqKPH1WJB5g5aH+IvYFv8FLi6XEeLH6yCtDVE5xwzt+9lyx66WyMSt6klyLjWhpy1JK3J2OGY9FFYltp9RrxYhjvq3WT0VjK6n9rmEbTS9I7YzVmWrfqiatXrzGHgkxlnQz3atE6+JdO/RQZGMLT2utS2A9qTGQslRlP5bxuy2yMEMsZlsVrUSZ0YmSgkkSU9rAWoF4qzPLgDirw2hwRwpEaTxh+7TgBv2bO55lFkk4X7HvJN410sbLyl+0Hd58PNCEATksA4OPMVBRxlgVBWce+bcCZzytZazL8KwFgzW+rHKk9hTpv1hQzyozFdRI8kMdCG2uxYIiHEDTAC62kQaWa7Ua9Pv9lY0v6JjzU53ecfICZE0OYzrFKt/SnV6AbDmCPJ1WrnY9Cg98SzLQaDRgsVjAZDIJp1B+/vOfw9HRkcpPik7k/VgFIOek2Wa6mYVeLxazzxr/NDDJ+4ODQcsR8dZ90+SZJ5rM3TRZzm3R8pBieug2URW8pszLLMtEh0srB/US2gntqk8+vwAADg4OIM9z6Pf7K7h9uVzCp59+Cj/60Y9C/m63GxZu0S5heg+2uri4gPPz8xU8rRGVPf63lp5fWYzOfAwTpQZPNAyjpaX/F5lLEn+8XZyoHxErW/s+ldeYjNK/vf5bGV0f63NPIM6qR7JfUnkxOcfgdKyOVOJ9TjFlLD22p9FoQKvVWslbRJatMUDe6BzX+tIz16R8vL4ihFgIsXy9Xg8nFa3bvzjf2D7a7hil9DUfxzI23NrUcl2UGvzTxrisP6zp2YuLC/j444/D77joCKC/BU4/S/MJ7dh8PodGowGbm5swnU7DpimUOY6X14WrvPOQY0bKE36/sbEBd+/ehXq9DqPRCE5PT8WYl+WveXktMt+9su7VhUViHDyPx4/gv0vj4MFcnrhJjG/tOwu/aT6oVk5snDzxB0r8mQCvbo6VW5Qsvr1xGumzhcVi8QSvfsF0/H3fIr5ubJy57KzTTmk4r2idXvnlsm/5MVo+qQ0SD2V8FY0sf4W+iY6+Jz0hW0R/3gQVsTVFsZ81Z605qvmSKXrOa189WL2MjEl2RZMVS6+l+JgpepinsfziKubaymIs3z1pBWi0YFHZCVelAqGCxk8DWbs2Yu2QFmY8b8p4QZiHqtoBuC4F6RlH6igCQHjsPmWsUkgDgGXKvkkntyhVwa/llErk7eMyjo/1u/XQPL8i2Jr/vC4tCMfr47p0uVzC8fFxOHm5WCxW5F66cge/p+Vy8sgj7Q/ppCimof97y035nfa3pMukunme2WwGrVYLXnvtNWg2m2GHXp7n8OLFCxiPxyE9no6mff3lL38Z3n77bdja2lqpG99Zxfqm02lye6X0VetbT3m4UxFJu14fy+MnQjTi80rjBzfXrMNJ8FCV/X7bdL1l/3F86BvVMZKAPn7HF/JTToRbtI55QUmz+amBMq2v+dtaUj5sI30PVuLJs8hJiV4bmOeXi6rD4RDeeecdODk5UcvhbaL6ldaBJ4Ssa45jTg/VIzRI5g3k4P/Yz9apfCnQopGHB3rNHLVT2E9oP6ntpr/z/k0hrJfzGbOT+L90whr5ssYA0/CgCj359KoEWK6LOMaTbGNqWbhQyDenSoRjgr9j3tlsBs1mc+WNulTi8oPtHI1GK28/P3ny5Ep6b51eP2pd8pZlWbjNBk8R43Xs9BQj1s/fJ5Sukb4pvAPwcuNdlmXRm10A1m+DqyaqV7nNkvCJVU6WZbC5uQmLxQJGo5Grfu5DcruAdhmvO/7c5z4HWXZ5eu/Zs2fw6NGjK3zHbPF1Efc76VXpuJjcbrfh7t27oT30yRd6chJAf/8ddaQn6FuUpLL51bGYTrKTmn/jjX9c57wq2o9am2K4Liaf0hzxUmpQntpJ1Mno8+K723QspRvI1hV7jhHXARSbWWOSElNLwcWc+Bwocg2tFMPR+JHGPGZLNX4kDG75ausgrd2ar1hV7DM2f8sQvYmu1+vBvXv3YDwew/HxcbjefzabrdiFZrO5Mo63LZayDpLGXpvT9KYMbY6lxCcsWxUrK1bmdVOV/ua6cHnZcldQuhSU0hqvBfcspeilIgLAB0tqC1fGFgjRgihWcEWqJ5ZG+11rn5XGU05qGamCpRmYGB+WA58iO2X6RjOERSZZaj9YfMXyrNuoWXXE+ox/luaPNZc0Sg3cUGeZfyc59Cn1xHjGOvCaXHQc8FpivjhWdu5qfY/OZaxN3jGS8sS+q4ooaKnX6ytX8uH7rtwBoFc/IpDsdDqws7MTAnH8XdgU4G4BLol3C5B6dI7HVvDvvDxac6GInrrJYF8Rp7WK+tYZXPaUTeXL0zYv33w+rEtvalS1LGF7UoJpGi6Ufot9b5FUv/QZxxp12OnpKUwmkys4XeMzxhs/tcp5S5EdGpylMlqr1cKGGWwTvVY1poer1i9WEIjyLI0R1ZWp84n/JtXP6+H80u8sfKOV5w2SSX9L9UvfX4c9KOtLUPm2xohvMtD0bgp2sxYxpLK4TkY+KMbUdJ2Ezy0ZyLJs5ZRpnudhwxo90W7pVqnsdfsyABCwN19gpePoGYdUfKhRatDNkxbHHf/HdKlxhyKkxWAkv6Ko7deemPGWR3nE8vCZAOkUuVUGlkOJbroCuLyVol6vQ7fbhePj46jPeV1YWatHwnhUlritBljdlMZxAYAdZ+P8cFlJ7Q9v/ECzYXT8rfiE1qaqdRkv09IZ1hxP+c3Lv2WHNOLy7/GXPWVK/1vYzFtulfJnlZfiO2r5Y5jHihtoeJJif2nuaLxYvBW1ex5M5/W9Pf4ElxePrEp5PfoP88R48/5WlR2RdAPHinjTCKXFYrHy/JpmD9bB87pI6guPvUkZ0+vEACn2yoMfi8Y5pLKkdEV05Lr9irJ6G4AtxnpOTyBwbbfbV9LPZjPx3RxN6WqKqAxV2elUicSIXzHq2Y1K08d+48JuXYtV9SSuAlhK+VPeT6U727wT3xv4KAr4OXhJOUkuvVGVQl4nsSylBI/KUBH5il25SgkDL/Qk0WKxgNlsFpVND2jUdljmeR5OYu7u7kKv1wuLhVK9eCKTt7Eo1ev10GaAy53a0i5+zrNXj8TGTXNkYwaXE71mOs8v30LF93f56d7Dw8Nw6hjgchc3plksFjAYDEIdx8fH8PHHH8MXv/hF2NzchNPTUzg7OxP7yNtmD3F7UWS3aVGeYqeiLYoFJ/BvbBMFet6NAFXqlLJBzSrSeqls27ld5DJcxGmlWIMHUFKuAfOQt/1evZQ6nlowSiurKgfSCo54iKar1+uwXC7h5OQkXI+IaSg+1+qkpyH5zSWIa72BDpoP5Qi/Q3yMiyLz+Rw2Njbgc5/7HMzn87DLml6LnKI/+Ml+Hsjk16pyLOdpH/YXJ4pL+Ilayicthwbv1/G2Nh9vrX+onqb9jjzjuNLxxP7Gcmk+etsSLnZU0RYsk57c9VCKjqH/aL00QAkAK4s40+kUptPpFTwYwzwSn3SxsAp7w08mAKzekhHTZ7xN/CatInjiJujk5ARGo1EYT9QF6O+3Wq2QFvsBMbT09qoWHOZykiqjVAakejxlTqdTyLIsXFVd1p8oQrw/iug2mrfZbMLGxkbQQdPpVH0H3UN5noc5u7W1tfL+cyqvVD/GqNlsQrPZvHLrhMc/uo5ALeUjFgtDfIFXVmbZ5Ukoy65yjGrp8aqD9bwMbgtpOj6eFD9I9l/TqUWJz38pJkgpFhvSeOIbCGLk9eFj8RPsS23jkZTWkqnFYrFiuzXM4R0fSyaLymLV8uwhKbbK2x/Dnmgn8e8sy1Y2X2lEN8J7+dRwuTR3+W/eeccx3TrimbwOqX4pfxW8FI1tx8pC3T6bzWA8HsP7778PW1tb8ODBg5Ae2/bhhx/CcDiMlk033qbwdF3zB+vjdJ31W1QGX/FYx21pk0RV2tR1UNFxWFkx9AwAdTylAIJmZDiDMYemSuI8eTqLK0nqvFLjLjlnqRTr95QgKk/LA7Qp/HgBQxUKkdfJQVSRQBn/WzN62u9SX9K2WmNglauVn5rWcnJjZfN+iQFmi7cYpfZXSr9oeazyKD8xmShKtE1cd9DFWAxYUpAq6UpJjr1zjjqU3NBKfFati1P4leagBA6wLb1eLwC4fr8P29vbcHx8DPP5PPQ3vhWGp5L5dUXz+RwODw9hMplAlmUwHA5LBXdS2peaLlXP0nfgU8aXBycsgBYDb97gFKatisqUFcMG9P+Y86rZI63cIpSCIbj+swKIlkNclX7ytqGqssry5Q3gWHi4CB9S/VoZNKhC5z9/W5vjLc9CoDb2UtBQkjtNn9frdej1eiHYg5tm1mWXeFs0KopvYzLI9YiGybxBQ1q25F/FMLzVz6kygYuk3W43fMcXF73+BU+DZUgBL54ulbQ5aul87DtpAbYMlQmkYX/gTR9chuhYxfwszQ/S6pX+1tJr/VS0/6R8XMdhf/AnPzR9LeEfKuMSWViJ8pTSrpS0+GRHo9GAWq0WFvxSsFhRStUV+J30u5QX4OXTF3iiFWNQ/BS4hy+Kg2I220NSvZRfyp9kC1OwXFEqIoeYdjqdwmAwgCy7fPKl3W7DcrmE8Xi80pdSnamyV1Rey+QD0DGtlh7/5thKwtoxWtf89Pg0lu3XfAipHC+VkfUYnkml1DmR0lZJnqq2BR7cl2LLeR6qvyieS6FYfRJ/ErbjlNKfqdgiRZdYY0Cxr4fvKjDkugnbhZtqAV7eVoe/9Xq9sEnn4uJi5ekwgFX94sGfnIrkiZW1jn6PyShvv6ZbpTJSyrX4o3xSLFTFGBTBGkV9gCqoDPaTxi7Gl3p8UwOJrVbryu6F5XIpvrVhKU7puypBiFY3fQcQHSp+Gg1/4x14cXEB3W43nDYAADg9PRXfE6yS56poXY6YN5Ci1a29Ces9IejttzLKQSqj6pNBSKmAykspxi42d1OCZx6ifWnVTfm3TllgGbE00rsxKWCREwY88IQSl9s8z2EymQQgO5/P4ezszAwUVEn4LjM3tAikytZn9XdqkIMHLzg1Gg24e/duOGV17949+PznPw/n5+fw6aefhmAU2iZcCJf4+va3vw1HR0dBF+Hu2rJt4MQXO6S2xerwOh31ej28oQvw8p2P+Xyugj7J7mFZqY7zuoN+10Vl7PJ1O1OIaVLzUKc3hhM0/HYdgd6yxE9f8KCwRTywZqWTwLi3f3ha7sRpzjwn3ISCm30020/1rGfnukbcfmK53jnQaDRge3sbFosFdDodODs7C+VKb7d5356lgVAtUIpl0jyUpCsYi8o63cxKT6HS+vmiOSVLjngbuHxXrY/49bwU12xtbcFnP/vZsOHs4OAAzs7Ogg+p3UpShpeU+UyJ9jX/n1/xS/PgyTwMQF0HaXLKZRPnvoZllsvllYWhqvxw7mNrsuwlb2DSSkNx+Xw+D1g4lg9/p320WCyuXGeLc9mDlWJ1FQnEIo5fLBawu7sL+/v7sLu7C81mE7773e/CYDAIV8texwlZTb9iP0q+EU1D5zL+jYvpp6en0Ov1oNfrBZx/fn4Oi8UijCvVs/T5EY1wrsTIOkHI5QTTnZ+fQ7/fD0+iSHkkuk0BeeTz4OAAzs/Pw3d3796F5XIJH3zwgRi74DqK+z9en7sqW6bZb35bBk2r+Tb0VKAUn+DfSbZe4kX7rgyl+K4WxuffSTbIevfUSxqPMZ0t6Q3U/UhlcG4R4rxavoSF74rqAwtH0t8sbIsn+THecnp6uqIvaV9rOo2ekJXaLZGkbzX59Miali6GHbR6U9LyfsG/122P1+WX07YsFgs4OzuDbrcLvV4v1PmZz3wmpB0Oh/Dzn/8cAGDFDkpPiAG8OretrIPW5bN56CZuUEEq226vXlk3STrf6lf1zVjMzAtsNBpXwKRWgfZ9zKBbaVNIcgYkAKYBr1ardcUA4BUsCNy5UdEWDywF7QF/SNLJhdsifJyK8sWvJJWuM5YmrAZ4pN9ifci/iwFDmoeD7xTyBpOKggNP0IH+XRRQS8HPWH9K33n7QfqOB3EpTxSIehzBWH9zICrxTwPj2rVUlsKOtVUqr91uw87ODozHYxiNRlE9K80rHlzy3oLA+aLkkScMcDabzXCamN7CgO3s9Xqwt7cHu7u70O12YW9vD9544w0YjUbhOmPJlmVZBuPxGA4PD0Nafu2h1paYk261iQeaUsiaI5Zzmud5qJsvxkrp+XhaziPnL+bUrMsxiFFKwGdd9ayLUuq09LA0Ph47ymXkuik2B8oG7rAO+pliFE851vykOjVWHm8r/4zBMLQlWpn0/XLUm4hrcZMhBralxVAJQ0nfSTbUClQg//gGkXdBV7PnHp2j9SX3Hfj3XsdR4yHGG/aVtqDllWvJjkmYmvYh/o7BN3oFsTWG3B/iWLAsFdEzHkxSlKR5i3Jrzb8q67f0uNeP8fKo+WL4m/YES5kgS1HZScE5KN94ygN/429lc7+I/67VX0b+U/QMbi5HHcrxe9l5WKZNVH8UCfjhmNFTNjgO2GaP/fTybdkSzecDWL3C/OLiIvgYeKoUYHVBCNMVjZkUpZR5j2O3XC5hOp2u2Gdue+lnaf5gGvoZSZIviUcpxsD51fJWOReljWxS23gfeX3HIvhFyyfhMSmfxY9F0hhYTz5p/FjlU3zL65T6WKqbYxzO802SNGZIHvmx5IX3FSfap9jP9DeKg3FTnUePp/g1nBeNt3VgKg1LUf60ucbLkX7T/OWb9J1TScL3eX65QW04HAYMgrS5uRk2X9KFdYqxkCT/Kqbnr5NiPiylIrZcSkPlnf5v2b2yOKIK3Mrni1VOTF+VtUm8T7R+tn7nv3lkwLL/nPwPm/7/qd1ur7ypgpVwynP5GgME0vg3T5PiLKY62BwY1mq1EGigZSGop4vOVPizLIPJZALD4fBKvSlvxUrtoN9xpxYVXhlKBd+8/qqJKhf8TPsdT3KllplS97rJoxhTHMTrIKpw+MLhdREHjkXGiju49LqomBNnkTY3tJ1ceZ6HE/TS+9IUfJTdlUR10ObmJnz2s5+FJ0+ehCCARHT+8f5oNpuhXXn+8m0tuijKy0klaQxqtRq0Wi1ot9swnU4hz/Mr7ysBAGxtbcGv/dqvhbrffPNNyPMcvv/978NgMIB+v39lXBAwnpycwMnJyRW9ar0z4yHej/g3nkwAWNVtHuMvlW3xQ+V7uVxCo9EI/SCNHc2njSm/Tpv+Q6riXcB10W10vsuQF0BqDl/qSfZYX71KfVkkAEWdA+pYxvLxxVGpzKI2jhNiKHR4tRM8rVYLFosFTCYT2NzchC9/+ctBV/zgBz+A4XAY3hqUrtGX2uDBNrydmsw0Gg1ot9sruorm094YWwfRMZICedxBplTUkcT0VjBTqo/6OKkYnuIPGnRD/MLfwH0VgkfXRbQ/Wq0WNJvNgJXoczZV9pknGGDNzaJ1cVnXYgCch+smT938uSUcK75oKM3v2A0qFj6uuk+Q56reZvbWmSpXiEFTr7nEsWg2m9DpdGAymYQTmgAA29vb4SYcDy5OqRfzcb2nlYVpGo0GLJdL+Pjjj6HRaEC32w1+4Hw+h/l8Hp5a4TcF3EYslWVZ2FCMV9DT3yQ775ERzGv1LQ+C5vnLU+hFY1pF0/D0dGMb/026scGjk70xTwtzFMUeZYLfmE9aMEAdxdOmlMtvoZAOa0j14m/0/5S3mm8DxeSGxlMtm8yxG+I6vvjKPwO87DM8/Xh+fi7eckDzSIeWrPbxOvn4SjqlrD2tCichSXYf+51vMJX036tA9Mrq2WwGs9lsZayzLIM7d+5Ap9O5cooanxKj8UrsB9Sj0iZUbZyKYJF1k6W/U8vRsHzMTsR4KkpVz791koc3qle8OKGoXZVIXDmUjFmn0wlBeapg8X09uoCGDihnlO/QtILB3gZYaWLAloMnDFZLg4GGyeNMFQFzPB8fA8lxkeqPBU1TnZSyoMwibKNHBih5FE+R9nuATkp5WhlaOamgVOPPIyeUrIVJiay0UrC0iiBQipNKg4laPu2kD1WwUrDVIm3u4QYWdL75m6W0Dq19MXnmY9FqtWBvby8sRPZ6Pdjd3Q1p8V1Vfs2SVRcGq3AB0TqxRMvKMv20rzSX0bmlV+Lw3/FqssPDQ9jY2AAAgLOzMzg8PITz8/NwgwENpmEbKNGrzDxy5pVlzfbx+Y9XBcZOG3nr5DJLbS5eUyzJvkSWrdOCLNjfVJ5ii1XrBtDaHOG/3zZnPOZQYxquZzWQHNNrmqMulRn7vmhQkctUajk0vfQkhUSaPeMOdKwPpMBODI9RvlHv0cW01CAj51ni25JzvPYRrzfXxoKeCvbqLK4TOM1mMzg6OoLxeAxnZ2fiZsfU+SmdlKJ2nvaX5PfQenm/eeSGEt66gJuvtFOTnDeJijqLfD5ZMt1ut6Fer0O32w1vAgK8nFfSdax0YavRaEC/3w/4Zzwew8nJiZv/mOxbMubRndJvPD8dH82O4IIcnoRFPnAzGWImvkgu4Y0UnJk6N2h9WfZywwbd7CrhtZgOs/yOMva0quAa14l0gwl/W1vK4+FTCuJI/aFdS66Vq8lxjDA4ur+/D3t7e/D06VN48eIFHB8fmyd5PSThBe98pjLIT45bdj/P87BBPs8vn3q5f/8+vPnmm/Duu+/CkydPVtpOy8OFMqlcD7+SbeX9wbEuX8TAxSNciEW9kOcvYzteWbspjMrrxcD7wcFB4Et6N1t7gsjT/zyt1kca5o3VEysvZe7xtnM51Oq25ji3CR68jZ9jaTXZ9tohrW4sD/9Z18ym6jdMj36zZEdjfHow3HWRB0NKfRTDhpbektqqnV6ni2G4UQngUtY3NjYCxptOp3B8fLxSpjUu0lhoabzfW2liOpx+5rx5dY7nN/ye376WilWL0LpsB7VvAJd24fT0FLrd7oof2e12oVarwRe+8IWwkWo6ncJ0Og16gtp6XofUnliaouSRzxg/0u9VxsSlcmLlWrE4z9zQ6uM+E/3d05eW/ZY+W8TxvlWPVD7nV+oXLz8puCe6GIvU6XTCTjisYLlcwtnZGdRqNfFtOl4mnbBesKQ1IlVRSW1Co47UaDTEk62oKKRdQt66Oc90kDyOGV2MtSZTapDD0/dWXxcRfPp76gm0WB1esEqpyElECnhiYJDLsGVYPH0m5bPI6jNqBDGt1p5UcK4popiCjrXJ42RYbZYMhlR3Sh9YuqnT6UCv14Pj42OYzWZX8vG3alINNTfyeX4ZcHrjjTfCBpper7cS+MDFWG3+SQ4ZXhXMd7RZQJKeqKVlW4Rlt9tt1RHpdDpQq9Xg8PAQdnZ2IM9zODs7gw8//DDocel9EtT3FBRTnT+dTs1Fm1T9in3I3yXHcUgNjFkyDnDV4cW5nWUZTKfTK/InkWYr8beYvqM6xJpP63AMLJ48VES/esorWk6RgIilizyfY3NV0lNcrxchKtupsiHVTReLpE0IUj1F8IM2JxAzxtpC5xbFmXRTkUXcaZDyeDAIAEC/34ft7e2V22+ktPS7Im8UUxnCsubzORwdHcHZ2Rm8ePHiyubPIkQDSbRezgttk2b3LdvPT89xDETrwXcr+W0MFE9qfNC0lGfpd0+/SfKP/3ADbq/XW7GLPFhC+aRvrtfr9fBO4u7uLjx69OjKuFehX6U2eJx5LgcWptfsgoS9cB6jzV8ulzAYDMJivOeKRam+mC1JwdS4sDWfz2EymYTvm81m9FQl7x9rLIvo8nUSYuN2uw3D4TC6GOstk9tErS/wtgKOO62yi9BisYDZbAZ3796Fz33uc/DJJ59AvV6H4+NjWC6XQb/HsJxFXO9Z/NM6aIAfy7DsMOat1WrhZOlgMIAHDx7A3/7bfxtOT0/h6dOnIS3aesTb3mvuuQzTcbWeJJB8Z14fbrKazWZB/vBJAIpNYrGB2zKX8vzyZNNsNoPBYABZlq3c+sP1PD8tRf9P0WcxG83zWvxrFMM7Gmnvp0vlxOIMKfVq5MEPvA4JS8bKkojKqnSbBpcTqy7OH/WbNext9Ztkt8rikLJkYUuOC2N5LZvLxx0/cwxPn6bAtFRP1et12NjYCGsC5+fncHJy4pZrjcfYvJbawP8uOpa0L6RNnBq2l3iUdBHnlduU2LzgabX6tH5Yp+1AW5tlGcxmMxiNRlCv11cWYzudDnQ6Hdje3obz83N4+vQpnJychLQanzFcxdNeB1HskkIput6rw/D/KnSYZYu981XLp80n/ltsfnnaGesPDZ+nluMlj/+24g1IYA8nEO5IxMKm0yksFosVAIYOAL/SzAuGMb03nXdQUsqV+KCKYjqdwnA4vBLUjgWNPAEWrJf32XUC8KKTrgqi1yshmOU7tMvwIU3uosrdO568LAlYpCgGjVLTW0RBsxbg0YBJESXo+Y2ni42fxQeXg5ii5/Mw1q/oLCCfk8kE5vP5leArrTt1rkvpsM6Li4twqoUHTY+Pj8OONJ5XawO2A9NQna/xJO2GjRHlFU+V4GniPM9XnDosezwew2AwgOPjY5hOp9BsNuHx48dwcHAAJycnkGUZ9Pt9aDabwYZhvtPT0+B00A1EVV3xhn13cXEBm5ub0Ol0wtWgBwcHojxgPloGJ36NJM2L/OMuRLyWG2XBssc8SMZlp0odc92UElygDj/PX0V7+VjV63VxA1uM6NyUAlxFbWVKGzEtvVXk4uIiXCsupS/Cl+RsIsXK43qMnmCTyIsJPO3AOUUDw1y2NB5QP+A1sdpVwVbwhfa35eTy+a4R1R/c/mv1aN/TMvG06GAwgPl8HhYL8Hp1GujkVwFa5JlTyJel/1Mxg1YPLoJtb2/Dzs4OPH/+HA4ODsL8KbIRSJPHmw4wTqdTeP78OeR5HjZPdbvdYNO5DKWSdw5Y+blsUj1Bvy9yWr0q8tRn4W/ez3hScDwew+7uLnzta1+D8/NzGA6HcHx8DOPxOPn6T8qDdE12VUGNMoT144bCFHvL/SFKRTBBSoAxhZDH+XwO4/EYRqMRTCYTePDgAbRaLfj5z39e+qkjrV4k1HOpPnKsH7Xf3377beh2u/DTn/4UBoMBdDodALg8oYOn1rUr7VOxBOdFwiXUvtN4Rpa9fIKJ3x5nxQVuE8bm7cU2WTGjVCyq6WItjXSV5XURjVNp14PTccR/nqctLN1ZVOdwnjxUhX6SfBStfClOx8sCgLChDbFhTJ9rWJji89tgpzTyjDmdc570rVYrYG/EBFqcFL+XZLxer8PW1lYYg8lkApPJpPS8jPmQMVnRyLK9VKdoZVvXMkt+VpV0G+WT8oTYcTwew3Q6hfF4DK1WK8TA7t69G9LSK/oRl+R5HuIHfMGa+qlFDlFVTbdJX3jwZCwNtielXZK/xGOY122btTUCy45K8Qz8XTqpXfW4ryzGSgAa37ngDcJFV7pQiU4OZZgHfz0NKAqQNcKO1AKVkuOoKeLFYgGj0egKv7yNUpneN65osIumqQJAWWVowf0i9VgkgUwERLTN9BSbFWwowlcR54B+LuJgWA6L5zut7Vw+UpQyL8cTXKVlaMBI4l37HOPVQ0WCJNQ54uOpKW0tiCK1Eb/D4KNWP6/D64Tz9HQxFp0UXv5wOBSvCpTai2XxUz+NRkPkWzJs3uuJafn4d71eX1nElvQGXrM3Go3CBpmTkxN49OgRAFwCQ9yJjkEa5IuOOT2xoPV7Ef2L/+M7VxiMxneXaNu9ei3mmONNFahD+RXJ9B8vV7OBVQQDpLZUXRaSJZ9ee8r5i9nGIjYFF2NR/qzyeV56JZ6VpwhA9uI1TCctxlp5OP5KxYWp9iOGe6zvyjrYll2mtgdlAW0FBgRSFiF5X2oyzNuFPMRwDdfRUp0aWWOOC0STyQTy/PKtPfrOJt+kV4a47HNbRTf98P6JtdX6ndrPfr8Pr732GgyHQ3j27JnJb0wX87qLOL9UJ3rmvkd3LxaL8Gb7bDaDLLsM3I/HY1gsFuHmi1hdKW3wtlubz5ZtSOXR4yN45w6WJ+FozZ/ln6nNwWts2+02vPnmm3B0dARHR0cwGAxWMEORcdFkowo7XwVRWwsQHwNtHC17ZBHOZ1xAqLJfkE9cqBiPxzAej2FrawvyPA8bHHn6Kupd5/hqeK5Wq8H9+/eh1+vBhx9+CLPZLJzWms1mK1drSmUW4VvDk5aOxnoQ63kWxOn8rqpvq8TxEi7i2CM2r7y2TaufllM1xWwCxeH42bsYSxcPpfiNpDuLzlU+dzxxyFR9xsuQ5gjHmzStx1bS/qnVamEDq+X/SN9L8uL1J27ChsXq1Hi12ol58Dko/A5vQInNawC4Ehuit2PiBvqYjdSwuFa35XPEcDf/LpbP8ts0snRRqs+aklbC7VXEKbzEY0e4wIpPWeKG842NDdjb2ws8YGzu/Pw83NBiLXTz/zU7q/FokSaf0m+07pjP561f48GiVNuHc0HaDBTDMTGdoOEiqxzvHFuXjY+VK/nFHor1AU2DJN6TkwKovO/PaeUX6eAigI0Tdcba7XY4SUTT02sukXh76YkjShIo45OWL7ridzTvfD6/ckI0pZ1IXiFf93ho9WCQTZuUHrCWytd17KzhCgpJa0/M6Y+lp+XGQAiXv5TTQlUY+pTAmfWm5joodU4gbxKP2MdeMEf/9tSN40ivRcLTn1mWiU4LXkfnDZjVarWwo+3w8HDluhrtVgDunHraj9ff0E0+lPCU2Gg0gs3NTfid3/kd2NraglqtBk+fPoXvfOc7MBwOAQBge3sbOp3OynVwk8kEDg4OriyOI68avx79iQsKeZ6HxdatrS3o9XrB4cFdqFgfnjKWAJJ3kQ3TIg845ngKS9NBFmE7pIBBTG9d1xytktYZTNR0eqPRCG8x0pP0+HuM+CYzJOt0HU9jzVuPfazX69BoNKDX60Gj0YCTkxPXVdgAL/sF5TzFHqN+0ILqln0qu5jHy8P/PWNH20h39C+XS/jCF74Ar7/+Ovzf//t/YTQaheAGBjaQrLalvqOF5Q2HQ3j48CHMZjOYz+dhMR1PMHptF+XBwnJ8rOm1khaflPgpY8lmF9FFFIfyduA/btM571IAoSq8qY0/BvpxzKyTzp4gAS5moJ6SniV4FXX9dRDdDMevvdMCF3xMtJspNJ29DvLMfeSd4p918VMVZVkGGxsbkGUZnJ+fhyc48Df6P6ci8Qu0981mM1y1iyeHigQJPWnzPIdvfetb8N3vfhd++7d/G/b29uB3fud34OnTp/Ctb33LpXOxHE23ct3o4VWKGXnyZdnlZg4cp62tLXjzzTfh137t16DRaMDyb0i+AAEAAElEQVT3vve9cF2xlp/rROtdTym9Zdd4OpoG//dgD7rZVbrO/qYIF1+wDzS/j9/cY8m4xw5Jek8bA/osTlHbJMkDH0evX0bz4HzjODwW8+FB/5hPnap7Y23y4j+aXoorSOUU4RXjtbiAg6ffrWeTND5uq52qIr6G6S28gHi/0+mEZyUwD/qmtM/Qf7q4uIBPPvkEGo0GbG9vq/XTDaUx4ro1Jeatxdy8sstlltszCc+nxPMwPea3NmHx/n5VSFtEpTSbzeCTTz4JmwG3t7dhb28PDg8P4ezsDEajEeR5Dt1uF5bL5coNW7hZWaMiuLMMVrXyeWWuLFm2kaexYhSaHUmNGdK8dAO1tQHIW8e6/Iqic8zThym0shjLO5MrMwoquEMeA+NlGJWUrmbUPROEKzkElhq/NDDNlahnV5YFRDnfkkGKAXivg1AleetLmUCpxqesMrTyVlW25kBoyk8D/UX40QAK/Sy9WxAjnpbO/7JgWyLL0JQtD8Dn0FiA1huc9JTJSZNF6uhywit50QDiwiC/FivWr7iQh8HpTqezogs9sskdHw/A5tdeUWBKgxQAAJubm9BqteD8/BxOT0/h8PAw5EOnDeDl27BZloWreLj+TbFdHFxL+fM8D2/f4sILv37eGzCnfEhjz/mQ3tSRZMlyKqQ5LQG7ddqc2Bzx1u1pN8U8qXWmBOY4D5p99wRJPGTpLus3T3noIOE7zYhXpH6MOcf8txhu4vIsfeak6T+LOFbUeIqlkfjEv/Garzt37oQNGri4QW8G0LAeb7uly6S2440vZ2dn4XQu1SMa/945Q/NSnWLJg1anZRORKH/avONpeTmxOjT9oY0L/o4LADhn0N6l9gmvQ5oX2vzgMiRhN5zLeKWmZ0Mc73fMP51OVxa7yjwFwOfwdfk8WKcH42LbaTst3eTFUNrvFkm6AT8jLmo2m9BqtVY2+HBZ8vgIXj0Q4xfL4vqqqrHO81zdSJ1ShqY7AGTMgTe1pC6weXjk/FxcXMCLFy8gyy4XnPv9Puzt7YXbB1Kxk2ZjiuhvT/kaL5im2WxCv9+H119/Hfr9fvBPNN8Ey/du7I3ZNe+8lOaNhT+1W9ZieLAIeTATT0//9thiADu2kUqW3HE7avFkkQfjp/gBmgx4yDs/LPtu8Sbll3jwzBcvSbqR8sH54mnxJh6+8c+jjzn+1Hyg68AVHhygybsXQ1jzhG+2pnFw3k88H771iQu5tBxJz3rshQfLxNJqsqXNhZiekPRJrC1SGZpv4Jk3Hv/Aq1e1ueaxv16S+MONovSJEpSfTqcT/AzERovFYuWGLc3eaHaU/yb1P/6v9WnqnNJ+08ryjr1VbopN0fRhWR48eT38xcjCkVb9Vejy1PmVgqUAjMXYVqsF29vbwTnDE5q4W5oG1fF7zUlOGWwPSUA6z/Mrji/yQPmw3mrgVwXSgZ/P53B2dha9kkTjV1qE4EJKT3DRvDdBKc6aRRpQp99zh7jsCQKvc2hRilKLpeMGN6XsKg1krM4U54KDOCttrF6LrqPtXvLwwnVCioEvQ1hmq9WCt99+O7zRhuN0cnICh4eHMJ/Pw9upfBEIy0BA9Pbbb8P9+/fh+fPnMB6PodFohFNveNUi5qEnCyTyzGkMXtKTSQAQ2pHnL6/RwesO/9t/+28hLb8atd/vh12bjUYDXn/9dTg6OoKHDx8GEMjHIrZgLY2d9IYF7cvlcgn9fn8l2EnTSWPBSbOlUjARy8O08/l8xT5zHq2yeP0WLx7+Oa1zLli/U36w3bhoDrD6zmiMUkEhziEqryhbHnvJf0t96zjLXr5dxtvgdQ6xXizLcoypI59lLxejUk5+F5ET3DFvBehiPMdkQAo6aA4jTVOv18PO383NTdjc3IS9vT3Y2dmB+/fvQ7PZhKOjo7AI5iUPXtDeNgMA+Oijj+DHP/4xLBaLKzKChPah3W5Du90Oc2U8HpsbC7gcINHP8/l85W1rzBvrB8np5gEkWp4UZOIb06qkWq0GnU4HLi4u4Pj4GHZ3d+HevXsAANDpdOCTTz6Bk5OTcHVvlae3Y0SDHFTucQPX2dlZSBuTLdRFeM3006dPYTAYwMHBAYzHYwCAcOKb2qibIsmWcdnE3+gNJBphH2Lgib6DRevkMqgFEKsgWhfdlAZw+WbX/fv3YW9vD8bjcbiuuNVqQb1eDwt3VRMPCGq+h9UPqbflUFuAV/hSe4S3lqT4n5at4EGZ5XIZTnKt41Ym2ld8A+Zf/MVfwObmJty9exfOzs5C3+IGKi8/WCa+420FnqygcUqAjLZrMpmYN2/k+eW743jrDBKP20gHCgBWbxvh89PTRymxLmnOS5i83W6H+Nu6/GHcKIpzwYOXqCwArPYhn0carqWbRjlJOsLCyDRfVQFYjaT+oTdQAcj9wNtB08R0j6dNWZat4DvtVr1UOUrty9iTP5wHTSdY9fb7fej1etDtdiHLMnj27BnM53PxhCy36d6TmjEeqibeDxou4LilqF7w+HuIBfnCt4aDLi4uoNPpQKvVgvF4fOVmHy/R20SQD6sdMeLyxXV+TIdwPKLpIW1MrisWWJTK8iLFVajMDAaD8DdupELa3d0NN9nhwv7Z2Rl8+9vfDrfYIW7jmzA8eJwTzxOLjXnoumL1Ur2WbcRx0fSh9LkqnuiNfQC+OPBN6N0YWTgFSZvv3r69svrHASh1HBG004UY7ljSylMH2KO8YnljHcDLpG/jaI4DbbsnaKkpaUmRS5OC92tZRRHrxyJKRJvYkrHT2ujhzzKGnDQ+6G9FZCrGU2oZWh5PP0hgLJbP6jPP1RKc1qUsixqzonxYQFfjxXKI+JymZcTksQxQ47oCT6PQnWaxIAblB4HSxsYG9Ho92NnZgU6nE64gRSPLg9ue8Yul4faGBge4zsnzPASKOXDH7/BNlGazGRYP8D1ViWdrbmp88wU8euUsb89sNoPpdCrWKTmiVN5ic53n5Xms/B5ZK6L3PA6UpWdTAU3RQAwNjiDRt1yrcFSQcFG+1WqFjWwAtr3kv9O+44HFGC6h+alsxIJiFj6KgX2NH65zLb55Hs6HRKk8afVrc1NKE6uHtxkXSPDKd9RXqDNi+MlDXHbwCnP8HheGx+MxjEajEGzXysL2o2Ms6TDeJx5Zo2XzNFyXpVBqsIbXL82BVHmib/8i7mq1WtDv98MpaBwb7/vAEh8ee0bTxfBjqs3BslF3TqdTGA6HYUFhY2MDGo1GWKwcDoeFsZ/Fg0Sxvinqu/IyuP2nZXrGxEpfFndTnnB8+ElNae5ZvHrrLZo3pWyLuB6S+OL4netbOr7eOrE8XKTvdDqwtbUFs9kMFotFeF9NaldRn4jKHb4FvLm5Kb7nZ9kuzf7RMjSbJPUdrS+VKNaZzWahXa1WC770pS/BaDSC09NTmE6ncHp6Ko4hbau2kVLy4TgVxZj4t4VZ8Df0YXjcDctIIdoWXje3d/x32u+IU6yDDRYPtM6Y3PE83vmg2Wuvvyf9FosP4PymaSVe+ZNBGhay2sXbovkGKOPS5nmrzjI2hvubqdiLlyPZRHpohmJVCa9Y5Ujp6O9lbW0RsuZpFcRlU8KPuBkLMRy9nYfziJsQ6Lij3pI27FQtd975zPkvkobXldI2iargVypT061V4WyNJB2LeohviFosFitP4uBcbjabYbPy7u4ujEYjGAwGK22gdUlkzXkPHqd9GLNTRcc+lrfo2PP8Uvkcu0n5JbL63ENaXZ65pqWV2rYO8sydMnpbfDMW4KWTFmMO7/T27DTCgIP0ZoiHNPAn1e3pjEajEXZVUWeUvuuQ53kIIiD/Fs/aneY0vfbWBiVcTInRTTm1RUnilzuh3ICXUXYSXTfAonzztwE5P1pgQKKUAISn37W8nnqkOmL5YkrVU6enHoC0E6tegETTppxKq5roCYz5fA4/+9nPREMsgW5KuHB7//59+NKXvhT01Be+8AUAAPgf/+N/wNHREeR5vvJmIwXjMZn1tp8GabGePM9XTnXQ4ABec8IXnGu1Guzv70Or1QpXBr/22mswGAzg6OgoXBXJSXqvjQMzfjIQwaW06I1tOT09Dbv7tPki6YM8l0/PSnk44UIPBl2xLdxW0X7FBUgNqFqfU4Il6yasx3ojVcpTr9eh2+2GN3XKXGdIqdFowN7eHmxubsKDBw/g8PAwvNWJt454+KMnnLSnDOh40t36SFSuKXkCNjTIM5vNVja1ee0WOuvWyRw+16X3ZRFTavUWxQ9YntY/Ke+T8UXVWJ48z0OAvtPphDLoXObXoMYI62s0GtBqtWBnZwfG4zGcnp7CcDgMJ8WojtXKwf7HK6oBAM7Pz6+cYC3iKGlBZ47/pc2RKdhOsvdYDrefKNe4oOrVI1q62WwGJycnAHD5tjme4qK7yWO6V1owR92Ff6eeilkXPhmNRmFz2Je+9CW4c+cOzGYzOD8/hx/+8IewXC7Vk9hFiNtqjaTgosfhvg4fgp78lOrUdB1vB9U1qK/RXhwfH8OPfvSjoIuHw6HIy3W1OYWK8sPxnEW0LynWknwAr25bLBbw+c9/Hv7KX/kr8PTpUzg5OYHHjx/DaDRSb6spSohNB4MBzGYzePDgQTSP1C/Yfmp/cc7GdG+sDUVvJDg4OIAf//jH8NZbb8GdO3fgX//rfw2DwQD+8A//EN5991349//+38N8Pg8n8en77Ij7cY7h0yWIizEYzG0DlR1ue2ga3n6OHzXdzrE/bh7FMhAjFpELKw+2GQCu4DF6dSTA5Wn6fr8fTnlbJ70lHSSR9HtqkJhvpEQ/xoNnixCdJ1wHUH7oSWz8H/tb88mt224k/S7xhvVw/U/zrpOwvfymEyvuZOlS9HkuLi5W7BSewkTCG3GkK3djt6vwhXKA22H7Yrq1qI/DT7sCXOLKu3fvhr4aDAZwfHwM0+l0JSaN/YKbXrDv6NWzNG1MF3j8KKk8/n3qWEm+I+9PLV4Y02k8vZRGs7dSfRovWt2ecqsmTx2LxSIssFJ9cHFxAf1+PxwG+Rt/42/A06dP4c///M9FW8yJ+qaYTord0PxWWTydh7is0/814jJQZJz4/OG+LF9/s3Ce1h7Km/emECmv5dsWlVGvX+6hlLkWI2989Mo1xRiMoTu1EdTQwDutxOvEegBZlcSBrUTSxKFgN8/zcOUPpqd5rb85aOf10r+ltztTDJP0Ny3PS2Uc3NhvmjGiVGYSWIovdaJKAL5o38Tya7+nKH/vvMJ+SGmTxgcNUmrpYuObEhgsC4qlwBvlpez4St9r7Syr63g/UidD2uXNAwacaGB7Z2cH3n77bRgMBjAajaDVal25thXthKXjUtuIoAkdfB74oPVge7UTXFmWhZNeSCcnJzAcDl2BEYswPb8uBfU4PSmGJx/xHUh+nTOXPa+Twvnnv1PQJc0jTWYlm6nZ0bK23KPfJP1i9RW2zTOneTrEPRiMQqxj9ZdFlF++KxQXsbrdLmxtbcHp6SlMJhNTh2oOWow32n8osxhc0wB1kfHTxop+H7OB2m9SH3CdEMNZNB/+VsT2xnR5zIHH3/B06nK5hNFoFP7h9562SHzExq7VaoVr3lHmqP7yYoOYTdH49ugQa7ylz9o4l3XOPO3j8qQ5tDQtz1eWaLmxRTwpDyfJPtK+0GSb6jkqT3Suou7DDa63lSy/MEZU51vlS3ab/ibhA45jJVxrzQcug9L7pR7+rXZdBxXxZShJG2MBdNtVpA5KdIGPLgpi0JrewJFSj6XneBwDnyqRNhLFdJ2Gb+kCBs4RyX+OtcGThm7KPD09hZ/+9Kewv78PJycn8I1vfAO2trag2+2GBdgsy4KNpXKOY4A+jDQXtQ280pwti2Xp71TflPUTY+TFj/QkLPcpES/z04k8PsDLpP97sAbXfRIW8PonnI8i/ay1z+vTSQuVWKYVMOf1S+VS26thEguXe+TZi3M9mN/C8Vr/YRndbje8HT2bzcImAer703yaP0E/U7+vChypURHd6MFzKXic4jaMTSwWi5UNK6gD0I5ocw/LwY3pALCy2Z2esLXa5dFJ2rzzjpWFjTWqShY8vqokp9rnqniQ0qT0Z0rdeZ6HDZl4WII+ZYInZCU7TOcnv5mJy5EkF1z3WD6bxLs2Vla5KXNSKrvI77Hxs9pi8UnbFuu3GAbANBJvmM6Sd4/NT50vMb/Akg1P2bQ8pCtHgmq12spd3kiTyUQ8Kat1svR9le8gpQi217HkBgXg0ijj6V+LtNO5HoecGisJVKbQOoFDWYoZEmmHbBFjKRFXxFUbIi95QEbVxOXJsxiVaoAtPRCTZw1UWfWkyIW3zyVnrigQWacTjQAEnWN6ZTAABBBMHTKLEGBjnjfffBP+9t/+2/Duu+/CJ598AgBX32PFt4w87015+wJPZE0mkxVnlC4Eow3B9z2tYC5eYfbmm2/CYrGADz/88ErfUP4kPqUTeJiu3W6v1I/OCgZ4AF6eWt7e3l5ZYOEBC6ufJFASIzou9PQafpb0oTVOVcpzKjjEPF4HnwaJYnXS/u90OtBsNmE4HMLFxQWMRiMA0G+7iBHWiSfCkCjv+GbfbDaDw8PDIFNaII7vdOcOSUw+sHz+vqd2zZcGfmP4i+vpVEfZkx71QUw2taAPwOpJW6lOqS80nZrqjOJV1dPpFAaDATx//jwslPb7ffd1tZRimweyLIPNzU0AAHj06FE4XUsXyDRZ9bYR89DFK/p/zFZajo7kdHud0Zie9VAM01D9g2liT6GUJa4r6GlzihUoaU601Vf0d0+AwIs/XgWitl+6SUNK7/FFAGTdy/NSvBAjK3jhlT/+duRtJWwr3+Dm6SeKeak8080MRfjRvqebFyeTCZyenkKWZdDr9cKmvU6nc6U9VRHemoPYneLpWMALqdlsQrfbDfP66OgoXLsMcFUXefSFt5/xVHetVgvvbD98+BC+853vwO7uLvybf/Nv4Otf//pKnlqtBt1u98pV3HzTiEToV9G3d1N5ltJLviyf9zg2RTdEVEU4l9rtdrhOm/+O1153Op3gS3J5SsHktGxPOkzLD0ysS3dxfSHxgvZCw4lon5vNZjjxieS5lZCWRfng5aLvwm/ZS8XjZYjikHXQ/v4+7O/vw2c/+1mYzWbw7NkzOD4+hg8//DD0LY6JB7PQ08v4/W23g0hFfDOAlzecTafTsEDW6XRgY2MjpMHF2fPzc/VNYJyHk8kk2AXcGMP9tdR+9cY/eDoN69L/MV2qTuJtKBMH9OKWKsjL1zr4oX04mUxgMpnAzs4ObGxswMnJCZyfn0O/3w91cyyEBxrwuSd6owPqXVoXncu0XZI98uhF/pskS7E810GSH468cCwjtYn72nxeSdhb4wHTp/B902TNR89cTcUhK14lXodCgQAWJO2elYSQBiMwPx1MD2Mxg2l9lsqSFDEF2lJ5OKm9YJgOjvQ+A5YdC0ahovFc5Wx9V0XgycpfRLlIwun5O1a3R+itgF/Kbx7+aDkpgCO1T6X0sbo4T6lgSPpOC0pK5WvBPen31PI0Hj1lcz1l1c31XYzHFF48xHUa51daPOJ80gVcfg0Vjgu+FwsAMB6Pwy61Xq93ZWGR/i3p/xTCnZd0EVYrQ9LP+OYiBtXOzs6gXq+Hq2Cn06lo4yhpINurD3j7MRhkBXWwTiugR/+PEdov/IegNDbnPPqhqF2IOVSeuqWx8AIjmk8CpZTHGOBKketarQavv/46dLtd2NzchLt378Jf/at/FZ4/fw4///nP3eVgWfTKPUtnSYTzn88r+tk7vhcXF+EaZ77ozMuVrsSK9SHHjXzXf6zNKQ5uzJ56bDnvP+vKWU7Pnz+H2WwG4/E4BD1xnOncL4MTKAZttVrhFoKUsuj8wMArP+FB67X0De8vaU5qYy31a2wMpXGI6REvRrEoz/MwP/A62A8//DD8PplMVq6Rug6H1IMfpbbTceWYSes/PBVI27a5ubkyjilzlWOxGLaL/RbjH+Dlla88oCjxJsmrZF+09FWR1H66SWs6ncLjx4+DTZlMJuEqSAvTVM2jVa6E+S2M7rXZkhxrdWo8e38v4qt5yeKFbs7JspdPasR8CF4+tb+LxQI+85nPwJ07d+AHP/gBHB0dufgqgtsoUR54GxeLBTx//hzu3LkD3W4XNjY2Qpul0zUSr1l2dVMWn+8S3x4dyNuRoufQBjYaDfNK4BSS9DptN930x9PiaW68yvSTTz4JpxHRx/Doc+l3qZ+9+ElLV5Xe4uUUKVfCvmjz6Tzlp71issXL1fCS9lQcJ49d5HVb/UDlQdPVlk3F3+kiIM7LR48ewdnZGXzmM58JefE5ovF4DKPRKPQp3aSm6ScNV14HJouRF+dofenR+xcXF3B2dgbz+TzoHNpfdPzomNK4E6ZBnYAb7akuxvGU2mXxaMkb93WkNmvtxrSSTGKe1LiHV264n2tRqix6216WJJnT+pL/Nh6Pgx9ar9dhc3MTer0ebG9vQ7/fh1/5lV+B0WgEZ2dnIQ+1yzGZ5roDv+P6yNtGrR5ME5NNS94xLeezjP7hmEark6fl6WJ8U34tKtqesrbcGrPUvB79E7OJlFYWY7vdLiyXyyuPJWuVSkpSCvLxK/o4Q14nzEtceCShQ+NAJzRfxJCuKfTwqrWJLoDQvqPAaLlcXtnFpgEiD+j3UBkB9abzOKNWOR4DIo1TEeMWA9seA8PnR1VAzpqL1lwCWJU/TGuBbm+fS3x5ghke5Wb9TnmXHBfOY0q5klLVAK7V9+sC8ZYzRa8gk/jJsixcAQKg78Tt9/uwv78PeZ7DaDQKQHpra0u9wgv/LvoOVJ5fvQbHIho8wXFrt9vhhMHFxQWcnJxAlmXhCqPT01OzTG0OUx75Z2vs8zwP7zF6bkqwrgr16hWsFzf3zOdzaLVaV2TDcs4sJ0ZzVjV9kuoAeGyBR8don61+854mT6m7Xq/Dm2++GU4lfvazn4W//tf/Ovz4xz8WF2O18nHXe6PRCAEwfjLewx89WY/lxkhz3HFRGPGU1BZ+EsXSi1yG+Byn5Wg8ltG5UhkxOeb8UT45JtZ4e/z4MTx79mylDgyecR688qn1EZ4ums1m5mKsNE6YH8cdN3hI+J5jMo47OL/U9nIdxduC/cplVzvlzfniaTw6yoNRpPKoXavVajAYDOC9994Lb8fhtXDXfRKKjg8/TSu1Af/HNHS+azgUd6/z4ML29vaV4Byv0yJp7CkvtC7Pb5afhbgCF9LptZwaacENzU+pwt/l9Uu/4RjjtXA0wIUn8/m4VkFcluhnbo9ivoE2dtZ8tvS5hXs0bKf9Xqa/uGyUKYvKFs4/tCd4mjGVLzydev/+ffjKV74CH330kboYK5VRBSGu5bb1yZMnsLGxAf1+H3Z2dgLPnpsJJBvFv5fyxGy6xLtWl5YedU2tVhM3kVbRr9inWB59c5WXj6ejP//5z4e3Y4+Pj+Ho6AjyPL9yLTTtp5gvrVHMj/bMGw/+18oukxb5kWSJ+mmokym+4qeaNByEJJ3GpT4lypN02COlT8rga47bLLuLddF+Anh55ftisYCPPvoIWq0W7O/vQ7fbBYDLW47u378PBwcHcHZ2FjZna1fTSzxySu2jMqT51xZZskcJZUS6IQvgMh50enoKs9ksnIjXntGj/hy/ahsxL+bH23ewDP5kU6wvUv1+5E9KL+mfWPmSb8LLsPjR2mKNtRZHwzQazxZPVr4qicoEbyeVmzzPYTgcrsztdrsNm5ubsLm5CRsbG/D1r38dDg4Owk19eX55yILf1CD5xpKuQeK6M2aLvRQbLw++5eTBs5qsS79r/ovmt/D8qfrIsvkx/jEN70cPHx5K9SWkejnO4TzH+F1ZjB2NRqUb5nFcpA69SaLG3lKSGhVthySsVTp266KqwD8vs4iBs0CKZACqpKoU9zrJ22ZtPsYcG2ueVAHuy4KGlPZrnz1OvFSG9t26KbVObB9eT/z5z38+bES5uLiAb3/722H3KQCEIDwuBlEqM8ewrJgOzLLsyvta1JnFxeXd3V3odDrh2lfMKy1wIHGAgo6jFJRA0q71pICazxePfos5IVQusUzkX7pdATce8Xf8LB48QRCJx1gbvQ4xdfDwn+bAeW03lhMD39ICZ4pDLp0E7fV68Gu/9mvQarXg3XffhZOTE3jjjTfg4OAAAC5PHTSbTZhOp+pCAz3JnjLfKM6JUeo85mV6dt5rc0H6jn+WTqdpPGtOBZ8zGo8SUKflFl1AQyexVquFYJE3D/ISC2BJefGWA/o9LZeXoxEG5lGnWFghxSbRdtF2SKeMeR+UxXiWXvLgvdQrTmlgNLWfUMdrY0XtDpZddJPUbSUp0HAbfaYYebEj6itvUFkimgevrF0sFmu5GjfGA7bBgzUkoniDl10FcflKsTH89/l8HnDk0dERLJfLgE8xYM1v1bqOOAnFt1owE4n/9sYbb0Ce5/Ds2TNYLBbmRtB1UJ5fbnREP+XDDz+ELMvg4OBgZRFByoebFDnW9vQ3l1kL19O+SLmqFeWBvuvO5aMq2eAnMylRnNLpdGA4HMJsNoM333wzPGmGp5kWi0V4ZsYzb1JIsnOviq7X4glow/GaYipHks9YBO8DrPqxHv4A4ng6VncMl+F32kKJlBbLxrndarXCxpLBYBDdyI0LkPwqYq0Nt4li2DIlVqZhJhorWSwW8OLFC2i329DtdlWMgDpQu5Yb4KWcU3lEvwcXZS27g589viEnbdOmVEeMJP8vFqeReJRwS0xPeuYJr/M2ybDlp+OmTIw7Hh0dwfn5OZyfn6/c4Eep2+2GZ6Vi7fSMDcXWHkrp21QbmGrjaBs0Oa/aVqJ8vSo2uAjFYkuUPPjPopXF2Ng7BVUIEzUmkrL1BKlTefCAiligMmYAPWksMMWDbFqaWPuvS/mmGH5Mp+XRBNjqL/6bBCar4FuilDbfJHFFwo2zFgDSQLT0t2a4tDkupdH4pp81vj3AJzb/NaBGf+OyJsmf10nxkrd/LErRF/v7+/Dbv/3bYdcptundd9+Fd999FwAg7GxcLpcwHo+jc9mjH5G0xTEtQMDHD08Z4SJBs9mEXq93JT29AipWF3Xc8jwXgymcFxqg4acspXkRk1dLB2p1SwF+bl+0sfOCijJBVI+dzzL56iOLPPpGsyu8H3l/pIJOBPe0nFarBV/96ldhNBrBd77znfCeML6rg7uHcZFLkh3pBFHM5pUBzTEHVHMsNaLtoP1qyZE2/1NkQitDwqH8syUzsbGwbBTdRBKbQ1gXzmPp9KJVJ+afzWYh+Ed5TnX26aYA6UQs/+zRERyz8P6VnE3e7yn4LBYMoTqVfucp26Pj+PXxKYEAlB2+sE7LlzDgdZI07nTeeTBojKrG+7wP6fyvwjnn/EkyZqXXMJVVl4brUX5wg911ELet/DtOsX605n0Zu8fLj30XKwM3XuDp6uFwCJubm9DpdMIVeqkBY6tt3nZTH4frE/xdG7Pd3V1YLpfw/Pnz8I4b6iZPvTFerfHD/NPpFObzObz33nvw5MkTqNfrK+8aWuNHFwFi8YYYNrEwCf1e04n0N4qr8Z1cgOIbvyyifGuLU2ij6vU6TCYTGA6HYcMg5uv3+zCdTmE4HKrt9JLUh7w8TRdK9Xr0h5Y+1gaNV5pXww8U01FZ9dgN7XvuEwKsLsZ6sDn/XESHSnZTsq2xOjTcjvKIbRqNRuKpX14W2jsNf/C6LN6qJkvHeOQ6hne0+UJ/pzem4S2Zm5ubK8/iSPxqfZVlqxuoUZfhgizFspS0Nmj+FvfXaX0xvOklKstavEoao5R6NT0m9a9nnK+baP/H/BGqnwBeLtojPjo+Pg758cYLJLzdL3aI0JIrCd979IJFVfkjMewq4fmYbo/N/1QqahcwbxEcrc17Xr5EMSzpSevhj/s1HqzbEL91kGYEylCVCiSmpLizUIXTplGWXX3jjE/4PE+7nrMqvtaZ3ksxgFFkbKRTHlXSdY5TCnkUHJe9lP7hafk1D7weTxk0rfTmnETe/ve0jQN47kBIdd8k2ClLeX75hgdel6jpnY2NDWg2m9BqtSDLMtja2gr51hW48/Y5/Yw7LLnhe/r0KRwcHIR0uMBVlA/+HT3pAABX+pECO6te5B+Jy6NnHqF9wXde3njjDWi1WtDr9eDo6CicvOREAzEcKFsUC1xJQLEoNRqNlRMXMYfbOz+RT5Tp8XgcdvVXEfSigOzi4gIeP34cAgHI36NHj+BHP/pROMmN15TxcZFkwHr/SOIDF88Qa0inS6XgES8/pW/om0AeJwffRpMWByy7wdMUcVgpf6knWFAu6Xfe4JLGg0Xrwm441tIJHK0MXJDl7xdrQRuJH0+/I0/SojGVW83hLIrZqOzy+UFtEPabNneQN9Q3eCIu5c1ejTdaPtcf9ESxdK0w/SxdRxc7OY54ggfdaLvx9EqtVoMXL17AdDqFyWQSNgZc9/XMtA0Autxhn+V5Dm+88QZsbGzAYDCA6XQKp6enZkAO+5cG2XnwkQcKufzy+VjUlsbyoUwC2Bss1+VLWVcUa3V75nYMx3n0bWqbtWCsdKNB2f60cDElrjvp9+12G1qtVgiESu+SNhoN6Ha7MJlMwpvmi8UC7t+/D1tbW/DTn/4UJpOJeQKP48zUE3ESRkGdYtkm9HGwDAnjcD7pLTIAsOLzcH1u8R3DqbQdyBu/7hPLkfSzVGYZmdI2Oknpvv/978PGxga8/fbb0Ol0YDwew+HhIRwcHAQcJ93Qw8uJ4VcP8T7kbSjSL0XyFEkv4SWAVUwhxVZiOJLKK4+r8LxSv3H9mqpnU4nrB4kXSnRzS5Zl8PDhw8Bjr9eDe/fuhbTo79CrealdTcH7N0lUHwG8bIP2XASAvoiHuobaQEkWR6MRPH36dOU3XASXiMfS0PbF7B6XaelmqpittmSIf+e5kcY6OS3VI/WphWs0/q18KVh5HXjNSxKeBbB5sjDoZDKBFy9eXIkHlYlFVmV/NJLwSxVknZJPvZ2palqnn3CT8sxJ4yVlvlNyLcZqk8lrNIuSFSyLpY2VqznQVVEsyCHV7a0/JbBGy+e/ac6tVY8GHL18eMtIVWKWg20BXh4w9DiGkqGwHLQifZZC1rgVdfotgGLliQGFIjqDAxQ+tvxzal1SmVYZKXQTBjHGM+5IxGtBKJhB54YucKLuwrcqr8PQW3NI0l3SlYx4fRn/zXK8YnqR/oYBGwSGkhPiDfZRJ5FenaY5yRbArdfr0Ov1oN/vw97eHsxms5VF6Rh5xpbrFcpv1eDWYz+RUoNUWDbOhVigzqqL9gV3iAEAzs/PYTAYrAQARqMRfPLJJ+GtM6/MYP0pABX1M/4vyZVUr9e51fjzEE1Hr7wtg9Ok/FROY3zw7zUcQec6fk6lKvQpt1s04MLr0k4GaJjRwlCxUwZaUMKaP1Y59LPV1xzb8d+sPrcCWBIPEj7h80byCSQ9HsPFfG7E0kv2TOs3aWw0udbkgxPVU1mWhZ3suACEfcE3IMXmqpdSfRWuV3G+4OYmvIWD6nKrPG4XJDwR80Nin2kZRfwNlEVpg45VZ1XE6/T2izXHLbJ8Y/47/84Tg/DwQjfxFNX/MVzMSeovvO4TfQHtKkp8PgHT5PnlogfFZ576y8gRHwPaHrx2GBeUca5JmzClfqD/S4uSMSyeiinwN1qvhqvxd2rLvXJLv9f6gWMY/I7rL/r/4eEhnJ+fw9e//nXY3NyEZrMJ4/H4Sv4yJNkAbRzwf83epc6xVH/Ju6DH22LZdMmfitlGCYtIY6vxpsl4rP+kftfyxsaTt0NKS/s9yzI4Pz9f+Q11AeVFeteUz+112rlU0vqN37iEv3F7YM13mo6Wz9PgLWh4lSyvi+bTypf0jIZhiupTDUNQHmla3m/aRhQLa3GyMIDU11ZZPE8RHSbxJtnOdci+pKckufT6EYg7lstlOLzA9ZqXL0qSnFTRz9JnSd/QNDHeYnzGMGgK/rIwQ4wvD3l1vpa3iJ9TtW9j+SOWjZP6MPlk7LodsqrKt8rh9WggtKyCyjL92pdXmSxDlKJMihB9R5DWUWZnfRnFy+mmgFwM7FZRNoJZDXRrvFh8eurV+Jd2UWkAylOvtcOalo+BwypO51VJnjq73S60Wi3xSrTZbAZHR0ewsbEBGxsbV/JOJpOVK2zWTVJ7LCcUf8ff+ImjlN2vWA8u0NFyaeCyqlOUWCe9ugedSat8bNN8PodarQavv/56OMl89+5d+Jt/82/Cn/3Zn8H777+/sqOQk+RIWSQt8FjkCQRIddA5iYFA7SS39p0V7JIodpWXh2/pqr/BYAAnJydwcHAA7XYb7t27B48ePVpJNx6PV06RSbtsce6lXGmKGyno1bIobyllYb5UICw5WdguC5RLwRf+faxeng/7wdpURRfMKZ+pjgY90VLWycPyUnUO5QHlkI6fB6Pi+NF2oJyn9o1WPvKjBdvodzHSZAqxYoxfHqhLbUssYITpsA9T6qhCjlJI2j3O5y2/Zo7OGdRjePpyOBzC+fk5HB4ehrwAevBVoiKOOOWN/83biFec0k1Ai8UC5vN5uIpVCqrweqgNWy6XMBqNrti1VKJ+pXSqWpNtLahY5mR2UaJ9R3EHvUKyCtIwJP0/9dSxNwgWw1N5nofr+MqQNt5FdTIGPLnepYsbZfmtmvI8h48++mhFpprNZuCb2hJK8/kcAK6ePMXvcONFzFYAFPez+VXKnBcqb0X638OXFnCl2ID2R6fTgdlsBvP5HH784x/D1tYWvPXWW2LdqSd1UDd4+U6x0ZJtKWNLKFmnLD02m+pi3i4JO0vYlv4O8NIuo061/HbuW6f0h9X/XGa1Jy08Y0llkuM4Ot9HoxG8//77oT/xSmPeLiprVHdYvuJ1Yi8kHEeM3VDieosTH88UO4cb8pEHfBec3uaF/cZPmkr9hptksJxWq3WlXgn/W3x7YwsU/1PynNwvcnLamp88Hbe1kj2QytTIwidWuipJq9Pyuy1cXCUupGVRP4XG4GJxoLJ+mKRntTmBNnQ6na6kj23OkqgqX/0mKTbPAa5fT6foVylNqZOxKeQNnHnTesgyop5gjPS3Vb7FR6y8dQtOioDEePEamdRyNeLAiJfHjVfZvqzacK3b4MUMrxWQkMqjZVqBphSQWlXwDMtKkb8YcUBeRbk3YbC8c5wDy0ajAXt7e9Dv9+H4+DjskMcrySaTCQC8BFGaY12102LJKCdNxq0xpc5XbM5zR5cDZ0u+PXaIBn65rrPknbdhY2MDOp0OdDod2N3dXXmPVOKF8mM5+GXGMiUv70v6Tg0Gg1LL86Th/wCq0ys0cI7/ms1mePMEecC28kBokfZIgRnaVppOCsQh/1J+y5ZIgW1vP9KFvpjdKWpHuE6Q5N0TNOOkBW60dLSclPkm9Y3HuaJjqi0+eMaJliNhslg+jTR5jZVRRA7oGHv4lvB7bHFdsj1WOo0/ziPl+zoCKTEcyPmjJAXVcBMYXoWaZVm44rgIb2WwYGr/UTvrXWDg2DL1alapfq7HJP1cJuByHdi1iH2QcBSdh9x2WNipCttexfxDPCNtMvZQkXnJf8/zPATHG41GON1I+49jo9lsBsPhEJrN5srJU8+cKEsaZkGdgrfhaIF+j63heaS0XK68/o+Er6R5XGYOa+TB8zGdz3XPaDQKNybV63Xodrsrt2VUJRNeWef1eX0Yrb+5r6fVa/EW8xG18rV0Wh1WuR6cy2VyXWT1o9XfWh6AlwsW0sIFtwUSjpXKXHc/cLJkRBsvyc7FyqPt0vCnNo+0zzHifZ+iG1J0ttev8trwMrorBdt4x16SY6kcrYyYrijCv1WXp1wuj5oelmSoLHFcHUvLefHodW3uaP3C+4DrtiqvIpb4KzJPqiCP/Fm4xJu2KkrxPa15C8AWY72g5ToopcOLkvd9jKpIGog89915XiVv6wT4ZcrW8lpl8itW+SnJqtt6G+aGRJrCTAm8WqfdpOCiBGxTlJNEnvwW+MQyUmSRnkDj9XCerMWg6wbuRYgacJSNu3fvwj/5J/8EhsMh/MVf/AW0223o9Xrw4x//GD7++GMYjUYr+aWTiZps3ARh3Y1GI5yCi/HDwbDm6OE/fv1xKlG9VavVoNvthkVwfnU0nj6L2Yl6vQ6f+cxn4LXXXoOvfe1rYjAK09G2zmYztUzLsaFpvOA+RrTs+XweNgIAwMou3RhpjinfHZ1l2cpJVImPVEL5aLfb0Gw2od1uw8XFBZycnKh5cAzoNeH4WRuDmANO8/B34PiboLH20DGOOXw4f7LMt2Mb4FImNzY2YLFYwHA4rNTZkvjDOlPKx931eEKIB3GsK9w1h0yyoRrfsXZZ+Xm9Md6k9KiDcFw9dVXhMBZ9L7Oo7sH+4P2C8zrLMuh2uwBweVtElQGCFMeX94e0iQN5pp+5/0HnaVHeY5jsF43QbvJ36DxE/RTtlBBNi/VR+dJuqMH/bzsOpfJSVkdwbFaGqC/AdbxHB1Pbx9NLAbTrujlLw7S1Wg1msxmMRiN46623YHd3F0ajEcxmM+h0OgDw8vQopUePHsFkMoH9/f0VDDWbza5F9vi4LJfLcGoMx240GoVTnPRGFQ0zaXxb+pPzEvuej4Nkc3j6sqThthSfiPOq9cPm5iZ89atfhYODA/j444/Db3grgudkL5Zdxr/y4CFuB/F/Tx7eL5JNXTd52kjfikb/pwo9qdUj4Q9O2vyR5iCP52m3dND87XY7tBHnPU0b07887lMUf1ZBVkweT6dS304bV6+v0el0gg7F64mR6Olii7zyjxv+Y5TS79QG0/xavJPjY/obzZP6tnCZWFgqjvbYoxT/8LrJ6zfR+E3sNhc+z7kM8CcicMO/ZBOssotsoONEx/vi4gKm0yk0Go1w8pymQ0wGcNkHk8lEtZNFbj27SfrL4DviOEljIp6MLbIoplFVQVqNJJ5SwUAKUaUWU4KSIS0bqCmSRlNKHl4sWYg5FlL9sXq9QUEvaXk8BsBTX1XGrSiA52NLwUgRB0EaT/63VDcvo4icpwaJJYrNNUtf8L701mOVU4TKOHOxvPRUCj1xOBwOYTKZhN+n0ymMx+OwMCKBeYD1bewomzbmeMQWPSRQL6WN8WvNBXSmGo1GSCsFxfm8pv8oHwhWW60WbG5uwnw+h8PDQ8jzHO7evRuCbPw0JtbJnRNP8JH2VWrfWOVSoMzL9Do6MXucqiOLOkh4nfX29jZsbGzAwcEBtFotGI/HcHZ2FtLxoJ9WXkwv099i+tDjAKF88OtEPeTtYy7XWrAvxrtH7qw2a3rdAtFlSbPZVRIv27JVyI+kg3g6T7lVY3CtLCl4LOl/j8zHeO50OpBlGYzH41I+jsfOcN6ssr0BBfpbzBbyNJq+j9VF68MFTG7LvFQUa/H02glnLke1Wg2m0ykMh0Oo1WorV596eG00GtBqtWBjYyNcoyjxRGWhiP6TypHy3jaK+YwAVxeD8G96RbZ3TGh9mmxLGzQ1foqQJ38ZHCXVpdVBT6nv7u5Cu92G8Xi8gg8xANpoNGA6ncLZ2VnIh79JiyUe8uiiGIbnC0J4nWZsM2aKb2uRZi8luU6Jq0j8pPRTKnFfT+sflIvZbAbj8RiOj48hzy9PJNZqNdjZ2YHxeAzT6VTV8x4fowyek9KUHVuJrxjW5r6alLbMuFll8Lq4LdbKqQKjaXED/jv3Z6X0KTqC/h9bMNHiP1X6tKmUglfxqYsiN0fR8jke45uReFr6Oeana3V6fvfoTJ42NpZSXvqdJI+0Do8+9s4hTf6kuqX6iswTL5XVTTE9Y+XhFOPB0n2aL8P1kyTzXkrRmV57TtNLeJWXVaZ+S95TyDvWqXZbKltKW9bvScH1XpvA56uUJ/nN2KLMeMt6VSgmJFJQ3VIy62z7ugCExzhWWVfqO4Uxovmr2OFShooqDU1xlu0bdGSRUt+pu+7gT1WOA/1fS4P9rhn4qvhYR975fB4WW3FxZTgcwne/+13odrsrb8ViANHz3gzAzetvboustyk40SASOiI0PXdYMA8NSqRSs9mE3d3dUBY6VXgSQXNeMR2tl55urdVq0Ov14OnTp/CDH/wAAAB+4zd+A370ox/Bo0ePwtuxWA9+bjabMJ/PV66d0yi2cEjnUmrAhb5rKtWJi9eWPtIcKomXWNC1LA2HQ7i4uIBvfOMbsLm5Cd/97nfDDsinT5+upKXvIGkOnkQaDtP6X5Jr/hvygP3N5z+v04tvLL2J8p3iGEk6mdep4Qc+l5HorlI8YY9XTMcCDZQnj26wgmPIAy0zVpbEi1a2hyRHEOvxnnThvFQRgMRyJNm39Dz+XqRurGt3dxeyLIPDw8MgI/g7TcupigBJjD+U0dRTMFqgDckaa/o2G5Ikd3mehxPkdJOHl0/sfzo/PYst2u94mg5tHqaj/hueIDw+PobT09PwNp1nfuPVr71eD7a2tuDLX/4ynJ6ewscffwxnZ2dwdnYW6qU2SpLnsvP4tpLUfvqblB4JNxbSK/899dE+5HKI33kW8jhJc0e6RcE7husca253UJ6/8pWvwGKxgG9/+9swm82g1+vBxcUFjMfjcA3teDyG8XgMDx8+XLkpZz6fr7wnKNVJ/8e/vYFWayxQ7+GcxutyEed6rvYrqodpG7g8e+qwgqH8u5h/WgVRXCTpOexnAIDBYADn5+cwHo9DH29vb8OXvvQlePjwITx9+jS0j9okrR+4HiwyJkVwUiyfNS4SDo7hdw27eOrjZdDyrXpTNlRKabx2lvvSlk7n9XjmqORT4Ge6uRlPZKM/YREdD+6H3DRx3YJ/A1zqtXa7DfP5PNzaQ3m2MApvM5clensQf6MXqUz/WHMFf0O9juNKibcN5ZvGc7g8WSedtecCqD6k/Fm+ulQHl2ttbDx6j9sM+k8rl+NbqX+ukzx96CWpzbgRHuvS/ETedxbWjunAWBr+u7fdmq2wbGlR8rajTD2c75teg+Hk6QOv3ecktbXQm7He37zkNdQpACIFxFkBxSoFWgJSWtoi5VtptHq9eYrUnVqvlVZyViwnXgIXqVS1gquSvM5raj4A+aoZdLhj19lZc0mqP1XZx+ZUVaTVqbWvrKx5yqhCF9G21Go12N/fh1arBU+ePIFGo7Hyvujp6WkldV8XpQLLKgMzKXVKZIFRGiTkhI4X5j0+PoaNjY0VB+r09BSOj4/h/PwcAF46HDiX6cIuLvQiiPUGJzwBNWyn9h06vvV6Hfr9fnj7Fn87OjqC6XQq1u8lT4DDS1YQJcuyFccQv1ssFnBwcBD69/z83P0+rBWw499betY7TsiX5ISnBrMkHcrTatfYWWWm2miOGayTcdTG8OuivZiYjkeqvcBgEC8vRRdbgTpJ/1HnWMovlW3h9xjhAgp1mCnGsMaFk2TjtHS0LdrGG618/ncK0b6l7+p5y5R4wz7E0xG4MKrJSmrws4hel+rgNy5ImzusOmk/YZtrtVq41j9lnuBvdGNTnr88ZSnl5eOl8WnViXYX5RgDjd78SFpgSbLJHBfdNixH+ZOeDvL6CHRTlaTXsKzYPLf8dK4fOQ9aGfx3q/7rJMrDdDqF0WgEW1tbK88rUNvH2yhtYuLPPaRi8rLySXHrOmQ+Ja4k2UYNb3jqq6odKTxwzES/o39PJpOwqbPVasHOzg6cnp7CcDhcedpBqjuGW71xLC1dar958lD9WrQMmk6bY2XHnGNZqgdjssxxexGdFdO31m9andYCIJczqz+92IfbFc2HWSdRfUs3lkg4QPKltDgWLR83N9FF2JjdkuQJeS3iq2g6QsJf0njEdIvXTkt6T6PY9dmcH+s7S59IpPlq1ryTNohhWmt8i5KnD8viIS4//G+JrNiC9pn/nYrrPLgT/Th+6KLRaKz4LnhLj/SUREp/Wrg2hSxcoflOGmYvekjCwllFSJurmg0qKnOlT8bGSOsQS2lq5cS+8xpIDfgUIW+dUlu9Tqan/FeZirSRGmZpknvKqsLI3FYq0ibsC+lELDpTzWazsnvysf/LXA3BywTwgf4YIKNlIjCVdslXFVDx6Kyy5fMyGo0GfO5zn4Plcgk/+MEPrpxGTDltcN1k6X4cK4tSA0Ravfi71+nm6fFErJXfOpHMA9FPnz4Nb+YgvXjxAt57773wGQPQuNMUTzHg3Kfv7FqglLfLSuMhdAY7nQ689tprsLu7C/v7++G3733ve+JirAaUOU8a6FsH5XkeTqFT/TabzeCDDz6A+XwegDVe2RmTWQ34ewJYKXiHykGWZTCdTkMAgJal5fWQ5CziYlIKxUCv5LyjnkMeLJmhizS8nKJz3kvUzqboK56H9q/Em5RHC4hI6csQvTKd1iuVTZ3QonOZjzktJza+ZW0wdxJxQ4blH3mcOnTY6entVCqCwbz+F/YtP1WAdk068aAR9hXNo9l7bQwpn/gOWqPRWHlfTjpFz4OB9HRBEVoul8EOFMHUnqCDNseRbsL34fXzAG4RwnGVsDq1A7S9KXVxXcp1lZdHzHsbCZ9NeP3116HZbEKv14M8z6/Yf42yLFu5IeCm/Gq+MYMH9crGf2L6mpcv+Yzeeq3AZlH+tbIskhZ2+Aa18Xgc3rprt9tw584dGA6HMJ1O4cWLFzAajVZOTHt9cIu8uizF1/PIupc0LO6ViZRYZuy3Mlg0NT1vd2xsvO3gf0v5rNPcnC9v30lpr1O/Yd309D/nieMSKmfcR9baPZ/PIcsu36ZMiaNJsobj6r0dKPY71+sAVxdBNV8Yf5PsvzWOXO9ZN15qc5yXX1S3xPwiz1zj+b0+7Lpl3ZqHXpIWxGP+PbfZnvrKYnXtN0r0kAX1M1qt1koZnU4nbHYqQ5pMpMQAUuIhXFYlm4jjUubkvYf/ImUiecv29M2KF7zOiWl1On4fy2t95xlcLb+nQ71leduZ0gdl+rtqSuk3T8BMGjf+uzQptf5NMUSe9Led+HhosugtZ7lcriz+8CAEJy1gHDP4Ep9WelpHLG2ZecSdeCqffJG6SEAmpf51EI4RLrR88skn4f1YyoNn4eWmCIM+VL5xPHiAANN7KNVZ9eoayUkBgHBVLQ0G07GgABEDBPg3P2W0sbEBtVoNBoMBnJychDfpdnZ2wunSVqu1cm0TXs24WCygVqsFB0/SKd4+9DoEKXkGgwGMRiOYTCaBX62MFEp1SoqWjbL66NGjK/xTWaCfPTqRyxd3frWFHpSlmJzzU1wx3UvHkdpqT+AD4OXGgpht579LQRYNhLdaLej3+8HZmc1mK1cPW31SFHNI+WNXh5WVR2uRj9ahjQmVJQ+OSJlLVH8XmYNSHs6jpLNSbJkk21l2GaTCkz8AlxtZ6EK91+agLMSuWvfwiVfUo0zxxU1pfkhti23Akea0h3ePn3BTOGM2m5nBGoB03xTgpe5stVrQ6XTg9ddfh52dHbh79y60222YTqcwm83g8PBwxcfR5j7Hy9aubKTYHL8pKqPbUHfgBtG9vT3Y29uDyWQCs9kMTk9PYT6fhw1OHtmSxpkHU61yYnJiYel1YhAP0TaORiNYLBbw8OFDaDab6lMRNA8+cUHl11NfFTxrny37HUvjqVezK5Kc0Pksze/U/qiiDZbvXYRwQxXihdlsBmdnZ/Dpp5/CYDAQ88T64LbFZcryqPU76vHYtaL8My9L8zVjc8GDU1JjShoWT1nE4Po3FZNz0rCmpPM1+3qTtpTzSzfy4UY8jWi7eHk8DcDqJjO0tTS+gr9Zek2rJ4WK+FU0X8xOaPZYWvTleVJkQfLTpbkgzWFpnDT7wzepevxIza/39NV16OhYG/hY4/jR363nCTTfiH+X0v5U38cqEzfzU17xqQhMq12tzfNppNkP7lt72iTJjcd+WDzGYjoeXoq0IZZHa4u0KYDm0eRG1eBVKtdY0KcsWQ20yONkp/LnMQRl2+wJhEhKyluuxCsNGHjLi6XzjpukTD3OcQqfrwrFDJOmWHl+TVFp7zVSQt0gjZ9l9KS/JQVn8RhzRpE3K/DB81kBNY1vagg9ZIG7VN1VJA/NiwY9z3N49uyZOAb0xNJ1UKqhpadJqGPikT+LLPlJKVNzjulnDCjid4vFQt3hJulfCrY6nQ7U63V49uxZ2I2e5zlsbGyEq6fpNdR5/vJKIjydQ9/a4e1IkbeiaaV5mWWXQcLj4+PQV5KTnjonPLYjlTTwhgtPz549U3fjpoBDSca5o6fNA9pPXH9z3YqOjJZXIuv32LzyvP/IeeXOVAxTNBoN2NjYCKfS5vM5LJdLUfZTeECSxkjqY+uaKPo/r6+ovPP6OV+cZyTtGucyhPYly66erpH48pRnfRdzwr11ZFkG7XYbWq1WeDcUT/poV3lqZNmqGB+cqA2p1+vhZgQuY15/x9IREg+ewI31m7awWIXMxXhBeyu9cVmkfuw7vH2i2WxCp9OBO3fuwO7uLuzu7kKz2YTJZAIvXrwI+Ty+UJHfU/w/yv9tpSzLwgnwi4sL2NragjfffBNevHgBZ2dncHJyEjaXldEjFF/hZ04UK0p6mf/Pde91+6bW2GZZBpPJBCaTSehj7bQ5DTahHkd/AjeGYJnrphTdUxVpOlXzHTy4KWY7uAx5N0lJfFStV5EXxIqLxQLOz8/h+fPnaiyhaBwpJlNazIDLvtdGc6pKpqVx8PiT/HNKf0i/pcpjqvzE4jU0HdejGp/Ig4axLR4kXqiNlDCZVLfG2zpI8g1QNy+XS2g2m1duCqGk9ZHVd9gujANJT0p4ZVhKE8OQmMar36Vx0GQpRlQWrfH28peq4zj/1neavpP4pek038PrL1RJMVtg9TlvP38vmT5BUqT+2ByRyqFyw3WUFRPg3/H2ZNnlM1eNRiMcsMDvpVuvtLe6Y3ZQwrUeipXP+8VLVF8UlcmivlyMNB2Q5/kVjBbrS3Uxdh0Gxpps11F/1eSZoB6qqv1V9pnlUJShsgFED2CKlVGUD17ebZZRLwjG373Kjp6io+VKO+e0uqTTtFp6zzWu9GoybIsV1PeOHX/PDgDCVXaobIuQxzm3SDLa/DNvOw3M0p2PuDhHjTk/KXtdVHQ+4QlTDICmGvsqeJCI88BBIZcfCrxi/GMb8drws7Mz6Ha78A//4T+Eu3fvQrPZhOfPn8P3v/99ePz4MQDAygIUB63L5TJcI4YLDo1GA2azWalrQrxEFxRQv2xvb8P9+/dhMBjA8fFxSMtPYqBsx+b1TTgalOjVTxJYBtCD8pg+RedYQDB1TCnf3Kng/RrrW7QBKU5qaiBNKgM3bUiEmzu0t1esdmm2k8qlRZrDjzxlWQbz+RzG43FInxL0wKtYrbenOeFuW7QLaPOqIGwflQFpFy+15anla7eo0L7T3myV2qnpZexXyV5ybKMFV/jc8lKZ67Jo/fiPn+CTcCFP59l9TdvHv8uyy6u38jx3XbWF+RqNRpjTPAhSVLdhGQBX353U9J1WHs4dDAbhiWpcRKTPfeDJFmoDb4quy6eRZIJ+1vio1WrQ7/dhPB4HfRgjbcy0wBC9WSI2x7Q5K+l0a8f8dVBqvdg39KYW+j+df54rjMvIdopu9Fw/WYS8PnLRtmrBTBrY825Ws+rwYOQidViL9xsbG+FKRYrDtLnjpdscg+Gk2V6rr73j4OkHT31a/WXjdqllWBtKvXVbMQx+IhnnGNbJ7T/l/zbIHI91UewQ81c8ZU+n0+A70BO4WZat3F4k+dRldb3kU9LfKHnqoXJPeeYyZsUspd+pDyPZ9xgmjsUjsFwvzk8Zc8qjdgr8puMllBf6v0X8im5rs68Hx8fScYzHY9vShj7rFgSPn7FcLuHs7AwajUa4oandboebxbT6sVwqe1obaZxHknve9ipIilF5/S2tjLLkkQGPDac4Thv/tb0ZGwN9mjMGEJ8s3slRJk2MrIBYTFhjSi418BgLzHnbW1XfeZwVTFekPA3kxRz8Kuk2ADIA/3hYMomyTK8w5WODSp4CGi2oaVGKcuXgUmoTBz9e8sw7yXnBhUpPAELjqSrZkeRdK5sDOwpIceyR8LTYOskC8t78CDTo4nHsjVtLRq2+w99TABptoyWjXFengGoM+OJC6ltvvQW7u7swn8/h/Pwcnj17Bufn56FsXITFgDCtd7FYhP5DuahCVj1lUIcPZbTZbMLGxsaVjR/S5gwKeLDOMoGEFIrJAvKGi8gAoC4McsdRKi/FiZBkUOKd9x/Pp/3G+9hj+y1wH3O8Y22X+EDeqYzleb4yHlTutH5PIQ++pbxJfNMFndT3DWn52Dbr2mrer5hHCljwoAb9PpW4rHN5lGwwbyPNp/2ukZZO0h1UN0vXtWllaHNQ6zNrLkl84cKktmnNGpuYk2lhIksf8LK5fqHlSfIdGz9qpxCPpWAHy2ewZC3FliBPuNhK28mfwND4T8W0qfrxumykRJL8SMR1Al3kxtPH7XYbut3uypWpFgZI0Q9F+8Yzj28LSXqO3lRBZVXzz6S4RUxPF+EvhjW0+cr1zzp40+ry2kdJ3qge8cpNTPak/rT418qybB/6ZRcXFyG2MBqNVuxnzF+TyvXwqqX1UKoNTUljURldLPnS0m/XwVeKnGjpMY/Vn9JctvwnrY/4XLjNC7GSH5bneXQRhubHNDQ9J3q7BO9byY9IkXsrrYQ5tXHkekwr14rTeeespS95X/I2xMr3/C7JnXcMtPl43XohRrFyvDJGfV1JD2Bd1tz2+Jsp/Z3yeyxfnucwnU7h4uJi5WYS6fS6VAaPOVj1WRg6xmcZudDmeKqNTeFFikF402h5YrqO0toWY9dltGIgskx5AL6JUoUS08qosn1lJz3lI8W4aGWkknWvvoeu2+Et47gXrS/2naY0AFYDQv1+H7761a8Gh280Gq0s4nzyyScwHA5D3mazGfLO53P1HUdq6Gh96KhpAT2PoaPBdFontkFaUIzN9yy7PC1KeaUOo2T0LLpumYgRgnT65ir97bpOZ5R1OIfDoRhIBbh6ApVT0eCqx7B7A4yYBueO1O/cCaKE1wqjjsSrKf/kT/4Ems0mTKdTmEwmK3lwkf1zn/scbG9vw/7+fliEf/78OXz3u99dSYsnaGmbJR7pHC8j6+12Gz7zmc/AdDqFDz74APr9PnzhC1+AXq8H+/v7cHh4eOU0rzbO2uL8dZzyxT5A/nCxoN1uq/2jLaRg+4qewk9xNulYYkACFxEAwLy+3lsHzk266xHbjldr04AKLR8DAQByf2hBNPpGcqvVgv39/RDQ//nPf66esNKCD1VjUIuyLINms1lolyi2cT6fw2QygVardWVBiNrh1LZ4ggRF+ofqUIoFJH3PZYWOWYyobFiYg/Y7vjF8eHgY+q8KKuLwzmYzqNfr8JWvfAU2Nzdhd3cXDg4O4P/9v/8H9Xo9nDjVeMQbCKy2W5TqF9AgJ537/K0rWj7Vf6hLm80mdLtd2NnZgfv378MHH3wAH3zwwUrZqQER7VTXdWA37IdYmtQxwnG9TfgTiQePvbRYLODk5ASazSa89tpr8KUvfQl+8zd/E7a3t6Fer8O//bf/Ft5//33Y2NgAAIDz8/NSeJb2oSTr1F6+qoRtQDvM7aHH7szn8xAULKpPipBm8yXS/IUydeO8jJVJ+zCWnm/0QR15G+exRFmWwXA4hOFw6D7Jmxro5fli+jFW/jr61grqWvV6ZOkmaB1zWrP3VCel1k3nWkz+LMwn+R+3jRAT4S0ORf1Ei1Cve5/ikPpK62dqN+mNFPRzigxYvkfMN7DGuMwCUxm5ofaFj23Z+ajFFqRyPbrsNlHMN/GMp9ZmK492ErUKor6ctIkJryvHuF273YaLi4uVpyOwHN43XBas8aY2V0sTI2lOa3XxeEtsDhehVH/RG9/laVBXS2sXrsXYMobI09kSpYBsrWxvGipU3uCCxZ8HTMWMhrdOjYoIpTZW3JGQyk8Fu0UCC1a9mrLV2qGVb/Eco3WCNU3eYvMrRlQJb2xshNNy6FSj0sRFG9yNLgXMOa+0v/FvyyH1zE/+vfSbJ6iqpeN8Svx55mdMZ64L3FgOROw3fnXtbSEOROlJWDwVFLve1wMqYuml31OC0dJ8oQsBHv0RA5XHx8eQZVnYSMGDFRcXF9DpdGBjYwN2d3fDqVoK7uiCmZc8ATvKM09LT+Lh27boAOLpF6++o7pfm7OSPfPgC8mOWXqB81tkvnvArwX6pXwefGVdVx6T/9R5gfXFAifcafA6VXSONZvN8O4nfRs7JWiojXURzBCrO3ZKPeY803GU5gHy7Z0Dkh0ugp88GKAMvtHaY82fGM8oo/i2Yuq1kd65IOXT8Mvm5ibs7e3BgwcPAODlJrUydsXDr1V2yjyI4S3pu1qtBu12G7a2tsxNLhal2BL62dufVDbwiYDJZBI2OuICFq2HziVL10g4WvOxyvjvUnmcXy2N9LtGUnBIkwm8wQPxwWw2gzt37sDe3h7s7e3B06dPwwYWT70xuyjx4pV7rx+QShq2T/FBOB/4j25y8/hM/HuPT5ZqL1L7URuHov4NtZFa3XwOx2TLKpP+zuvS7LC3DVWShh3ocz6U6CZg75zyktWXvHxtXCS/IEYpslkEJ0lpvTYoli6mv6XvUSfE9KbGS4rcxsa0bH5Lt5epdx1kYVSv/2Lp5JhP6MUj+H/qfI5hLQsraVgxhuuL4LvUfub8W2nodykL62Vl9bbKfFGiuCVVT1XNh1Qux1eWLGj2A30MWheNFWA+6VasMv5SGczKKVXuJGxVVHZT4gOx/JY/xHUQ6uor2MhTaYqhlSg1qOulFIVf1En8y0brUsbSO6EWSUKdqkxSJ/irQKkgR2oXLnrgVQcAcGX3DNLm5iZ0u1348MMPIcsy6Pf7Idi3XC7D6TstoMYJ00mnSiS5iF0xQhUdBmekt8fodQ5I6DBiWbRdNHjdaDRguVyGk1seukn9wg22JN+8jdapt9tA2A56hXKr1YL79+/DZDKBp0+fhjbR03waxYJp+L+0003jT0rHd7TzxS0cG3r1tfaeEq1jNpuFk1F8jsznczg4OIAsu1x4yvMcut0uzGazK/UPBgP40z/9UxiPx5Dnl1e3ttvtMIf4VUWUbwxueec+7xvapl6vB3mew0cffQSf/exn4R/8g38Q3sPgFLPl2I+YtkrdXq/Xw8aUPL9clMlz+V0XDoxT3gTm7fAQd0w9ASIr+DIYDMK8433qIcuJkAiDd9optVg9KTLYaDSg0+mE0+WLxWKlnFTHo0gQQnME6FuFRewIfYMS5dPiG+ewVCd1HFIwGC1PGk/+7EFRR09zYrUgTRH8JOnho6MjALiKa5FiQaRUonnQvqHN6PV6cOfOHfjmN78J29vb8P3vfx8GgwGcnZ2F3dJaUAr1POeXzkPu3FvX1P0i+lUpzj495T+ZTGBrawsuLi7g+PgYBoMBPH36FI6OjuCDDz6Ai4sL6PV6AVtiXdYck+YmpevwY67LV6K2h9NisYCzszP41re+BX/2Z38G//Jf/kv49V//dfhrf+2vwcbGBnzwwQcrN/lwojKN2Bc3oErvIHuuJZf4p//z78vMFa+v5SVLZ2rpaR0aXq6KqtIr/z/23qzXkuS4D486+7l793TPxhlxZiguokGRhkABpiHBhO0/bMCAPoHf/WbA38bwowE/+dG2LFiSLVuwKFEmRYriPpyN0z293+XsW/0fLqI6TtyIyMisrHNvj+YHNPqeqqzMyMzI2HJL9fVjdXqoHEmmeOwrzXcOIdU+8fICz5OeykF9DtRZ6GvH3g3KaeJl4/uQnSLp59zIZYen0JdbRmObSvJP+i2V77mHPdRXVkCfl4P/YnfrU9+d6gNpB+Gu7R0rJhBaGEjbxdsWtM44ZjGOEJJBnvaxTqZBn4xeoQUg+2eh+khyNkWG8skv7ufQNFh/Ktet9tDqINVxlzbYiwLOE5S3UmIENE8JqbaEllcMHavVClarVeU3AFyebsljZkVxeSIb9TEwP6lMT7yTynGepk57aPEQ6VpAGlPLWV4TkNqaxt8RVyZjUxRM7krlNrw9Cjwmz+uCx7gLDQbJmA2l5+m0AJj1vZaXlZ6XyQ1sLb8YgaC15U0NKml0WX3CjV0UcChUj4+P4datW7C/v18FknB3GgaV6OQMD0CG2ooaxprhFRqjUt9rwlv6ngtzeh8SPQZKClqigdWEEx9jaKXypMYb12nkxQL75uDgoJpoxR2TNKCMQQCcmNfu5aT/a4hx4DA9p1d7rz3jv0PjnfJmWV4eL46LDHAXO+ffXq8Hw+EQjo6OAACq8d3r9aDVasFisYCyLF33OfGxLdHpDRyWZbl15Pl8PofJZAKnp6dwdna28wUDvD+p7ON3qWngssoT8Kw7BiX5FfMt56069zADbNscmqxJ1dnaWJbkOMDzo9j5IoJU3g3pICu9Vn+ui/BoXC9Qvw+HQ7h9+3b1fDwew3K53DrZQqIv1C68LK7bPcE5Kpe1dtZkoSQzeX08/C+lCclfrAsd/x45L9HreafZRZJt0+v1qqOovYECLM/SdVLQif7P7Uv6LlQ/gKv3Z/MgFqUDZdF0OoXlclmlwUVYMbzL5RK3kUPf4ndSYBCfrVYrWCwW1YkVZVnC2dkZTKfTSj9r7dIEPLxZ530OGiRIfivK8sViAU+ePKkWoOFiMuQPPraxf5BfaLBXksmpNqBUhxcBliyWxpcly2L7OqaNPHEQaUxL8kGyI1Jp57wq/bbyl+RP6rjz6oGc31NZxq+M0ewOKQ/pmxy2sYeW2Ho35TfnkBnSWNDS8L+t/HhetI8sfzAE6VvtmUWjZLvhO82uCv3tLT9ETx19H/JZrN/0e+tbasdRn4DaZSnyXaIjVI+Q/V7H3q4zviife3RgikyJsUljacZ88bmEXOVeJ2L6PtZv8toLMWNOSk/5AMcg+vkIXLxAY/uesuvYPByp/OJpH+m3FyFbNYedRMtJwZXJ2NwOg5XfdTgnXmNQ+i4mrbdTduWES+WGntFBH7sCgSvuOkCFJq1Kor81Bf6iOMFNgQccaHC33+/DN77xDbhz5w689dZbMJlM4P79+7C/vw97e3vwwx/+EN5///0rq2EwX+QPS8jh0ccA27tRAeID8lgXL1/R408Rm81m6z5N666Nsrw8Fi3mntibiJBBwg3r6xozlJd4AKjVasEXv/hFODk5AYDLXT0PHz6svu31erC3t1f9pju/KTx1S518l+qh5YdtzuVkyIiiQL5crVaw2WxgMplU7zBPDAjjs5deeglef/11+NKXvgRPnz6t7he8desWTCYTePLkCQyHQxgOhzCfz03eD+nTFD6aTCbwwQcfwGw2g/F4DPfv34enT59e6UvpbmBveZJDoqXBwC3ey4tBYLy/EOD5/bAh5wzTeuEx8qQTBni9QmOb1pXeQYQLG2g6TCtNSIf4gcv7GIdey1t7Jj1fLpdwfn4OvV6vuldQAuq7VJlI20mjl9cdf+NuRlxQgjuUvcBVsG+//TZ885vfrOTAH//xH8PHH38Mt27dquyBxWJx5W5pCtpHkvxKdeQsW47nF2tD0vwku4WnpeXtSgfGBMk83zYF1E14YsJms6nuOKc7qnFii97PHALKEFyxTY/TonZbUTzfvTgYDGCz2cD9+/dhb29v6zQUlFWxMoXytxZotOQaHQeU/qIoYDKZwGQygY8++mjLBtjlHZCSPZWC3Hzn0Ulef/2HP/whbDabyk44PT2F6XRa2YMYqMIFepvNppKzuJuA2k5/n4GLOehOHx6Qt3hB0hc30QfXAp5e8GA5gH59Aj99IhaxMu06IMkX7b5buqCHtgutpyavYgOf1xVjo+UjNFnc1KSHVA6nSaIzlB+i6XgxjwGEJuYoJBuT8xbNn/Mp/o02RR3UlTWhfGl9Qv4XtUEsuYK2WafT2Yrh4WlbfDLIMxml2b3atxh/02IkIUjt0cSkC5ZDy8K2STk9DPOkNmkuGaH5bVrcaZf+RhPILVtz6BNN/nI+4b8xLjAYDGC5XG6d/jKbzbZ8eTw1j+8sx7Iob1l0huoa4p9cuIlX6HnhaRPzmOIUppOElTQY6nScJ7gSG+jT6AyVbQWR8G8rYJWipHMbRB7HKgaaMEHHjqehNEjtpZ13rrWDJ9B1E51DCVrglhvxVlCRfsOft1otuHXrFhwfH29dKo336mGwiwMnfygtXkiGTCroOJf4BOtA76rBADQ1wCXj3GtApfBSXePAy/uSc8HTpYzxXYH3AQaGO50OdDodODk5EY/NCDlt1riK6c86QXVaXsiBiqGD5kkdzLt378Lx8XE1wdNqtWBvbw/efPNNuLi4qHbV4pixaKb/czq0ukjvqJPMeXI6ncKTJ09gPp9fCdjkAgYdAeRjqBDr9braMcydWamO3vEVYxvQ5yHHl+oFmp7yvZb/wcEBDAaD6jgqPJ7dsyJaq4/F33ynXSjfkLPI3+NEOY4F5H88xlcKFmrygZYp6Q0LUh088kMLSkrf4fG19IgvvmAKA6R0AYFUZsj+l9osdWxaPkIO55fmx/Pm73ibaH1kved5xtAWakMMgqGcvn//fmWn9ft9eO211wAA4PT0tOIJz9Hf3vFqyaUUaP1L2zhGpjaBOnUD2N79S/UdtVklvSrpE03/SuOH82iusaQB5WsOm8b6Dtuy3W7DcDiE09NT+PWvfw2vv/463L17F/7yL/8SZrNZdUSbdH8lpdlLjyaLQ7LgpoPypOZr0ueSHW3Bq8OaApcdCMuWTS3HoxskWkJpJB9Hk9m75kfJvuS0cH0eYzvVsSvo/970MfZcCm2SPotBaDzVscUsaHpa+h1rE/PnUvtY/BUqm38TE4fS6uj9Pie8sT18zr+JtQHQ3kS9TuOEmh2t6Q/NnvP4Q9JpRjTWobW/p189Pq3lM0jl8/gAp99TtkQLbUdKS4h+Dw9INqXWN9ely0OQxrnGbxJo2hi/LOS3h+RPiDbOb7iJAoFjlPczXSAq6VyPvE71ZWm+KTrJU27M2MkNy3fj6ULlm5OxIWcqlHmI+VMax/uNJuBydYjV6E2UlwMptNQ1MLhTbuUnBQz4UaOSoxMSrLGG+E2FxluSoejdfdVut+GVV16pdhsiBoMBHB0dQb/fv/IN3c0QMo45/SEHxCt8uVJBgxGBAefBYAC9Xg8Gg0GV93Q6hfPzc2i1WtUqPylYSVcUhQy+XSLF6ORIMQR3BTqupfFbFEV1pC4erW3lg99oaXg6y7Hx8AAvS5tkigkUeMcK5VlcGYfp3nnnHfjN3/xN+OCDD+CTTz6Boijg6OgIvv71r8OHH34If/mXf3lld3GovJg0mg1QFJcBXC6zRqMR3Lt3D7rdLnS73S1D06NHvLTSnTKag7VcLrfu3BgMBtBut4PHx3ocfomnvHrL+x134jRaAC4n7V999VV4+vQpjMdjePjwISwWi+ApGZKut8YgvsM2rBOw1copy7I6xrzb7VaTkI8ePYLJZFId583z4RPPFm2hwI+EGCfPC5SLnFc5cAfhYrGojqHmNNDJjlCZWvvU1VNcR4XaPsbW4+XQwI6HPtouOfWnFfxAWnG84AK6n/70p3B+fg7L5RIODw/hq1/9KpRlCR988EF1lL81GeUNUnyGvJDuDy+Kq/co8v9DQUD6jr7H/1N3avC8LdDxRJ/R/1OBbbPZbGA4HML+/j48evQI5vM5/PN//s/h5OQE/vN//s8wHo9hOBzCcrmE6XTqypu2mSbXy9J3hcNNRSh2g3d0cfsU7SS0KaneRNsth/zQeNoTc4pF7jy578JtWq+Nw8eN5APF+Pk54NWNMXEZbl9p+cXGD+vKt11A88Hr0k3tGU/A32Oroo+WEyH/V+IPGhOIGbuanySdKsTLou+47tXyvy7QPuc7SbUTlELgEzz9fl/Ufx6ZZtkumn3Ar8eg32k8FPK76dizvrV4k9fL8kVoHNvrQ1rtqemEJviwqXxzw9IT9B3tG+mO1JAPxtOmIkafSvVqt9uwv79f/Z7NZpVPT/NFG4GPo9T4u0VnKI+bKDMRTfN5SF+Zk7EWtEa1go05jSNNgGrGnaREY4PqHhpyM7gX3rpITr6Vp+RUa/lLdzzRbyUFGRJ2OKFAv7GgTdzG9uVNh9TeCCoUcWdMq9WqLv1er9fQarXga1/7Gty9excAoNohBABwdHQEjx8/hp/85Cfw4MEDALgMZKfcb0XHI+7Eoc95nej/HPwuPV4OdUx7vd4Vo3Gz2cBoNIL5fL61I1bjKX6PWSxieEobk3z8WPJVy8fiEyl902PBIyORv/FYxN/+7d+Gz3/+83Dnzh3o9Xrw5MkTGI1G8P7778Pp6WnFk5TPY5xHyltYfijQL8l//j3PIyTvMJ8QeNl0EhZ3D2OwBtvh4uICHj9+DF/4whfg+PgYhsOheJwktiG/y48jh+HCnaJutwvz+Rzee+89uLi4UNPXhSTDUD7xOzVp8BeB99Fhu8e2k2W4avKNvufpJVhyg4976W5hnlen04Gy3D6CVHJwNLoxnUf2YlrNZrBsEjrZUJbl1oIEKS9aD41uC5KswDxi+ZUGTXBHsucb6X+UA8fHxzAYDMSFVRy8T0JHlNdBKGDIy7H0hhSgsaAdX1xXz1+Xs4llob4M6T6NNmtsUVlXls+Pj8PvcEELlS3UBwjJIS5L6D+cTKZ0zedzePr06ZWJtpBckeov9ZvXd+D5cL+H+jE8QMptN639c/MS9R+8cpDWhx4pTd/HBGRy1Gm9XsN0Ot0KSg2HQ/j2t78NH330Efzt3/7tFX1F2329XsNkMgnGDOrQe9N8S66fPDKYjj1cwEN5iPp2vBwpL40m+tubX4huq1wpf4+NrpWj6QKNl2L96BAdoTw4PPoqdbxy2R5rU4Xee9vDop/3g+aH54LHJgzxkpRWSq/V1/JxJT6jdrRHt3J6PONdqo/13rL/pTIk3Rprn4f8fYnOWKT4DLw83u4SjVb6EF38aHHqI0h5eeoU8rdp3nxSlvMClTd08ZBWv9QYh6SzrHfWYmar3WLkQR1bw6PDdunTxCLkz1k2Ps9H0gu8/tKiRs84DJUvXWnA5e58Pt868VGS2fidtiPbQ4tULwoPv6XIxtA3KXwoyWvNxmySz7U2c0/GSgK1SYKl8izGp8805R9q6BQj3POtlEZTUCnKOKYfLGOqzqCU7gbR+ooHH2ha+g3ND9Pxy6k1UKeb09ME36YaUZ58OTRDVBIsvV6vSrtcLqtdQu12G772ta/B5z73Obi4uIDZbFZNXB4cHMAvfvEL+O53v1vlhbvTAOT79Gj9+d/4z7pfwmtca0eJciOn0+ls7YYFuOSds7OzykDDukj5FUX8StCQgSSNvdBvqZ218mIcSUqLREMTvByiB+Cq8YfB5S996UvwrW99CwAu+Rh59uc//3kVbEMewiNlY+SYxFsh49cyxjUlryHG6JXyQqMMA+S4k5QG5nEy9lvf+ha8+eabAABXJmMx7/V6XcmKEGKcbfoNdaZoPebzOXz44Yfm97HOvGVMUievLMtqAQt/R4F6qN/vQ1EU1RHGsaBBDl6vGKPVksVWm1BZbjnARXE5GavdpWrZV1Jay0HWbD+A546/tjOJyjT8Rkprjc9QUIKnx9+xY55/x/kwZScClyOdTgeOj4/h5ORkazLWO04se7oOpH7yfmfJ3RQatN+xoGPlOnQowGUd+GQsDVhRe4zSHMoTdQqC7ihHPY0+AOcZLDt0VDKlicsJnhfA88lYfudxqB89/BzS/ZodxoMeXAbRRS/0GzqxiWk9NocELx9rx5ZpwLJpP0tpvHxPZR/vewpLriKvo14oyxL6/T78/u//PvziF7+Av/zLv4T5fL516gXyFfYD8g+VjzF6ONXWazJ+grDkkLefLNvJe/dxTJ130S5WuRat3r6OHadWeiq3NTqssrVvOY976pZL10rvpXiNx07x2GJa2hBCsiiUZw5etnwd3odeWix9J+3g84wJLY3UH7H+Lf/W6/+F/KPYsiR+4NeAUN82FinjKzRGpLqGZIlWH/wfT0/Ak4ZCtPDy+XM+/iUbictBD5+FYI1nqxytHpq8oDawtDMRFzpqfVGWeU7jkOiTZAhvg1z8nFO3p/iOnKaQbW+ls9JL33j0Pfa1Vd5ms4HFYlFd1Ubz4zxIYzx0MxWAvkknBpotQZ95ZAL3ST3jMpVeSd5oaSzE+Dk8fwnuydi6gYW6RtB1BDY0aELX6rxYh+06kRLM0L5BAcB3efD0m81m615PKX9UZHRlrgYa0AnRmANN9KdkTEn1ApB3cYZomk6nMJlMtia3MZin7YiRjOpQ30urpnNAqnO/34e9vb1q4mB/f7/a1StN0DXt+HuVvfRdyFnW8q+DmyCXLKMTefOll16qjk7tdDowHA4rmeAxaCTgdykGfawTgvRZ95SG8qCgk2Q4CYsTqjhOnjx5AtPpFD7++GNotVrw8OFDmEwmMJvNknfApY4fzJPvYlwul1AU24H/WEdA63t0IhEvv/wy3Llzp7pT7te//nUVmMU2fPnll6tjP9EIXi6X8NOf/nRr5y533EI0I33Y/7GB8VhwQ5bfVdjpdGC5XEJZlnD79m1455134ODgAM7Pz+HJkydXJua5TsKgrHb6AToD0pFT3rvHi+LyWO2iKKrdTB5QGmazGTx69KiyIzwGdyhYEfpOSuuxsejYoA5ELHBh0jvvvAObzQbu3bsHy+Wy2unsCbRhnSQ0oTMkOf4i2dBNgsvOsrzcrf7++++7eLppmjQ+1Z5RvYXggTjkBdwh2+12YTabwQcffADj8bjSF55jO6ns0mSuZUPzb3Ic+5sKa+ziBLk2biQfzDu55oW3XVNlG+L09BTm8zn8+Z//Ofzyl7+E09NT+OSTT6pTgHq9XjVpS4OblP9CoH2cGhjaNY/kKi+kB7Sd0lJgNxc9dXSiBznoprzFg8DaggZaZg5+C10v4YHV/6ntI/lNKcF1KbDaNE9404ZkS13ekvKTyrT0WYi36Le8Tt5YUyq4/PC0lyUTeF287W/ZvjSv67C9NDShx9EGQ3+uSUjXpwD47DYK7kdpedFn9F1sbEcqH7G/vw/9fh/Oz8+3rpLhVwNIqMtbFr97eaUJXZ6KHPwtyYqQ7Ag989JW16/tdDpwcHBQ/R4MBrDZbODs7OxKvIbHvmL6OyUemmIjWLzVtK2n0ZODz710Jx1TfJ3OvoY6AjOHQ5WzTTSmTBm0uxSekjFMV8pTGqS/0XGQgM6EN4DapCO4a3BnQxJ0Fm9wwwUnsPBuOZp2vV7DbDbbEubeYC0H0kzvK6srlLU6IXBiDvmk2+1Cv9+HsizN46415eEx3qxvvAJdosdyQryGpbc8nk9OxCpRenwex3q9hvV6DXt7e9Xdpq1Wq9rVYAUerT6mgVWeT4xDLfWJZvDzPrQMO06/pBvomMB2ogbYZDKB+XwO5+fncHZ2Vk0+4sSjVrZlWGlt43HSKb10xxA9/lfLKzQGpXpg/linvb09uHPnTuVgPnjwAGazWZV2tVrBYDCAt956q8pnPB7DdDqtjlHWaIqRc/S3pN/qjEfNVrGCpnt7e3D79u3qKGbqFEs08wC+FKih98ylBgaLoqhOO5hOp0FHXar3crmsjuS3dqB58vTINYsWz7c4Njx08vJQDhTF5WT7Sy+9BOfn5/Dhhx9uHe3ptTctGngenrEaqodHR+W073L5Cbuytak8W61W8PDhw60j5nMDeTakCy3elp5rOlriAbrKe7VawbNnz7bGiFeuWL4BH0NSWsn+1OpRR357fB0rHaXT0uNSOqldYmzbkM2SkoeWZjqdwmKxgF/+8pfw9OlTWK1WcHp6usUvkt5DPWAtZuV1iOlPjx10k+Cx1yT+0Oxcnqf1PqaNUr6R6KF0STqsab3SRBma/EktS/Nr6btUaL6EJausvFLbWhrzli/pBR8XIf8kB0Jl8XShNGX5PHZCfVEqT3PaYTRPznPoH4X6htLolRGavgv5v/RvrdwmbUKr7ZvQO6gr6WSs1B8hf0LSGVrbxuQrwdOXKYht3263C/v7+zCZTKrFz1Z+obaJ6Xtt3HrbJWWM30T7R5JZHn9lF7wi6Qqt3VutVnXiC00zHo+vxO8l29dDi7f+qX6/V394fE8LKbblLmU4QMJk7C4CDViOZnCnGEMp5Wr5UTS9El0LSqUIN74LKQSp7fi3RVFUOw/5lnkpPcDVOuGOGfodX/UxGo3EOwQ1mmMH3674OoQYAYZ/WwZ+u92G5XJZ7YAFAPjGN74Bb775JpRlCY8ePYKTk5NqJ+m9e/fgr//6r2E0GgHA5d2reOQpHmmqlckd2phdCto7yQHkgRN+nxgvd7PZwOPHj6ujHYqiUI9gjVVYFlIVkzfPOtACKk1AkiEIyRjt9XqwXC63JlxWqxXM5/PKiOU8ELrf16tDJB7j33O9lEN+hILIsX2Dsr7X612ZlKWYzWbwi1/8AqbTKfR6vepZaKdsbnmJ9A4Gg+qZJdskcLlI+wd3wSyXS7h16xZ8/vOfryb2Dg8Pod1uV4Yt7qKhx1/iEe8//vGP4de//jWcn59DURQwHA4BAMRjiiX7RTM6sa4p7Up1uleGYPv0ej0oy8tgNufB4+Nj6Ha7lV7HiVRrRwfmz8tB0AVV+D+/jsAbTMKdTth3mHeqbUThuaceacX2D7VLCNypisVms4HlclmN3cePH8OPfvQjOD4+hldeeUWtg3VPsMdhlfKU0ofswhA839wUO65plGVZLRwZDAawXC7hD//wDwHgUlcul0sYDAZQlpe7Zum4sNqR23D4Py7Y0fTjZ7gE3RmMvlGr1arGJV30pI0nyzbhcpIfp2/5zXXAg5lIIz9iP0ZOIFB20rJ4wFD6nwLb4Yc//GFFEy5KoAu7MC3m0+l04Itf/CIAANy/f786+ULzIZGu2F1BNJi068Ckp0xaX2tsU1sD9S/tl1A5fAct9ddjbJgmoLVTjK6iMjOkz7kfRE8NCcloBOVlqy6S7Aj1VdO8inY5He+0HTCNFpzmtGpptL+b4rPY2JOHFildav9ofgr9LfknIXuuCdQ5dYLSrC3wlmwd/i5UBp6+QBd44vco7/h1QLvSA7nKkXgCY2ihWAqF1Z8hv0+qC+oqbROP5HNofJxbHlDeQ9k2Go1gNpvB3t4eHB8fV3XA+AIu8OaLH5C+3Dzj1QUc3njaiwIP/ZaNyuGJ+UhpQ7qO5jGdTuHBgwewv78PR0dH1fPhcLh17HVZXi5CH4/HwTpq9EnxGYkmLY21wcaLXDxm8XusPs6J6MnYlCBKCkJGsScYiH97G1gaCHUaPeXbGCEXYzh489Ho0ZwJNEJoUIAHX0N9JhkxGGBFp88LyWEPGfE5+bmOwox1+KxvuINLJ1cODw/hpZdegsePH1fHR+O/6XQKDx8+rOqBfYt3JGplWsaVRp/1rZZWypMHg+j9Zfh+sVhUu4BjaQ3BIy9i8/e2nUYHf245XLsODvGyuRxDWYJ3cuIRxIvFAlarFaxWKxiPxzAej6/IFj6eY+vo1W+WY0qfeZx/z3i2ytfGhBToArjc3XlxcQGTyQQmkwmcn59XuyBRVniM9LpyU+JTukM2p1ymZfV6PTg5OdlylinfcF1G78oej8fw5MmTSmdpRynxciV+4ROHIbnqkZlWf3FdjnoWJ+s7nQ70+/2qvjip47WbpLGj1U+zXbTxhwu+kDb8H/lWsj14OZwuLa1lM2pBMYA8Docl2z0yjI4bvFPz7OwMzs/Pq53xmFeKnIsBLcdrj+WgJzSOvPZLKI1HPubSrVJeOBGCCyoePHhQ1a3T6VTP+fFUGl3Wc8wn5EvlsqetAFSM3+MBtolHP1t58PRU9uM/7Uj2UNAtJNdi/PJQXpZs9qSpEyPw6Ajtd1EUcHZ2Jh7HxvsX07fbbbh79y60Wi2YTCYwHo+v3EGcC1we8rGUm6+byktrU3wv8RPnixx6Jfab2LzryjGPnpX4wZLRWh6hdyG/mZYr1SFUviVDPHTmhtWPofrSdJYsvu46evoFQJ7ADY1Dyz6g+YZo8NjP9LelY3LYFan5hOiW0qN/2RSfeOSU1b+x7cDtJEtfWfxlycWQDZZCs1Y+fW/xcsjGp+kkmYrpyvL5ArHDw8Nq4TldfOO9x9PTDhYt3vx2Kc9i4PXzLVixCppGep5qb4dsPEtWc+Aplrh5AMHvhqV/Sz5GTB/H+A43CVJshyL0LNbmTInjJB1TvEvUuWQ4tyDJceGxhZSAUM68Q6CKuNVqwd7e3hUngpc3n8+37pGTdlx1u91qV1ZRPD+KEOByl9azZ8+2jsbwGqFNIDQod+1waDTs7e2p/Hp6egoPHjyoVvRRtNvtakcFliEF5K0VaXWcWUtYeoza5XIJo9EITk5OYH9/H8ry6s4QXlbOACLmGVK0sbCEO77nbXRTDSkAvxP4jW98A7797W9Xd53eunULAAD+63/9r/Dw4cNqtzPA5eQR7lCMkRGWoa/xTE55mjN4LQGP6p7P57BareBP//RPt+4I5fRYi1+aNra045JDCNFFJ6nweJfhcAiDwQC+//3vw8cffwzT6RQAYOvYwv39fXjrrbeqdvnJT34CAAC3bt2qjj+n+s0TnAjt4qaINQBj+4fe0/75z38efud3fgcODg5guVzCd77zHfjggw9gMpls0Ys7h6jRz3e7UCBf8XHDf3P7AhdgrNdrePvtt+HWrVtVcAOP3P3Vr35ltkdRPD/ajO4WD4EeDYwnL9BFIlpZWI8ckOwpL9rtNsznc3jy5An8n//zf+AHP/gBvPfee3B2dgZlWVYnZuwClqzHd1qb3mTktkNTA0J1QBe/aHYX8jzKAU1nabailCeml2jh8kIKKtDfnvHGy/QEESRbiso6uuAPQD6+b7VaiScSUbkk1TM3NHuUtp9kE6G/hTqRyozY+2XpeEc/UJKbXJfwnXIx5eH1FXyCFWXg3t4e/O7v/i4cHR3Bw4cP4b333oP/9t/+WzDvmxp08iJkI3PeR37lwWXPDmEp6Km1H13QpI3VpvVCbOANQJ/0l97zWEZqWU2gaTmklRkTXI/lB49fZ9GWA6F2zW07WuD8zQPz1CbwQLJTc/m1mJelL0LfSr4HT1MXmD9e+zMcDq+0S9N9uwsek+wWCXzSh6fj8UO6e9RCyF4JfUu/85ap+SmcFuRRfhIHt++08rrdLty+fRtms1l1MiEvXxq7tE4e8Lgn7Yub7nNdBzR9Y7WVtAM8JS7M/TLJj6G7XtHmLYpi67QzLs/pAvyQHYj/pFN4JJq0evA6aNiVLrTGTmw/eWHVfUtqxBjoqfAoi9C3Xuc7xkiLrZtFi9W5Wpl0gOfsdF6GRq9WptVGtI2l/1EA0JU/NA+6u4jeKdfpdLYccAzQe4SgVf8c2IUjSMvi5XnrZfH/er2uBDiOeewD6R5Ea/LAMux3Aak8GkgCgK1gPH2fWp40bvnfEj110JRyCJXRBHifUeOYB3dw0ozfYzwajbYMEH6ftMYTXsUb8z6UFmWfJGdjdFoqUBYgbdhuKHNxl6HkmITa0ypzF8FlrQxLV+CKQjx5YTabwXg8hk6nc2WxDwYC+v1+dWQ7AFSBeJzg1qD1e2x9PAi1BU3DeQLgcsfwwcFBFVQYjUYwGo0q/YCnI0gy3+MU8qBPDHBBAcBz/YWLiSTbQqo/rTMN0mgI0RoKSGj5pb73AGnCXc77+/uwWq3g7OwMJpPJ1lH9dccn5yXO61p9YmRJKOCSarffRKT2P/Iyncy0Fo56x6AWQLP6N6V9aX6a/tG+i9WfOfley5+nTQ0e7hqaDgrJuJiAoNTXHh9a6zeaB+d7LuulOpVlCc+ePQMAgJOTEzg8PKzedzqdSudhemvxqYQYnyDGZrXK8nyXImu08W7xdeg9zx//5fDVvJB4Q/JRvLYc5snlbExdQuNDKysFTcWbUr7PRdMufFmvvMtlH2IaL//xfHksL8Zms/JNQaweDrVjU3LC8oklUPmF6bgO8eblBe3fFPuG931MjITzkZVGKk+LBcXKWKn8mLy0dvPyOLdDqC1C2xXTLZdLmM/n1dVING1o05dUV94WEv1NxBZ29T1AnD1Txw/xlBnjA0lpLVvbG4NDrNdrWCwWVQwfT4bkvke329064bIOOE2xspx+q30fy7upNkgoTmH5pzntjK3J2OtQdiFwIZObjtgBmIMWzankzOdJ1xS4QgjV1VIiGr14BCn9jTsz8Rs8WtY7GDVD4iYGP5oE3a0lCd/NZgM/+9nP4NmzZ9XEAt8RS7/jx4ZaPJhybx/nfVo234GAwJVofMWOBDxyE9OEjpSMkYWc9/nYTVlhar2LlT2WwbALWeIpsygud8Tzvry4uICPP/74Sn3xyFJsd9z9zPOk5cbQ6el/j1OGkIwjgOe80UQ/hOjs9XpQFMWWrNDy4bt+YowjLc8m62zJhPF4DB999BHcvXt36y7Ew8PDyjlC2YI7st9880147bXXqqOFcEJwNBptrdqWaJHe0eeSkZ5ab29aTaYCXI658XgM8/kcAJ7zyWg0ctkBNJiKz2KNba0dcLfsL3/5S1itVtVRrHzBlgR6DDdNG7IPrHGEfLxLOWphtVpBp9OBvb09eOONN+CLX/wifPLJJ/D06dPqBBJtJ7fkfEjQxi62o+e4Zi7/PNCOfPPKEkn+piBWdoWCJ3WB8mtvbw8Antt+3I6iwIUodX2qXfkjVvmfFoTaEfmfX/0iybGYCYBcfmyIl+h7uoiG50VPIPDoUDpxh74jthP6j9T+ovlMp1P4T//pP8Hdu3fh3/ybfwPHx8dQFJcTsfv7+zCdTreuN8HFWKgbLbpSgKdt4Ckd3tO4kL5cQVOvTsN25gteY3UipseAIgYNpZO0vMghm7h+xDxD7WztNtK+57aEpy+lOlpxEPod/f+m2C8WeLuF6mjZl5LdlmPsUB5JydfDW6l+LQfXIbw9uc+am1di+ZzalinyLtVnxXiWJIt521D5zW0v3HjS9FgLtQt974k/WH5AKA0itLjGGo91J480eSid7mfZzDStdToSXZyNOg1tBzwp5fT0FFqtFrz66qvQ6/VgMBhUi8SlMqUTWDg8PqxE84tsQ+/S/wjpfm6LW+8tWDu2pWfz+RwWiwXs7+/DYDCobFPKh51OB27dugXT6RQuLi6CNHDwmA4+k+yjVNxkWySXzrXgOqY4hYAcRFvBTc83dWiQDNw6eTZpYIUUqRYItQQ0QgqSosNklcdXYEh3ANLBjPniro31eg3T6bRySjUnRVK0HqFwkxRQaNJESgsg9xM9+ggnGjabTXXMWFmWcHZ2BkVxeYwBHlfAFb6XX2n6kBC1lFioHO+kFTWIJpNJZVyhU+/heQseWmP6M6Z8j4zQ8rEc2abHgiZ/+DukcblcVrtgR6MR3L9/v3KG6L2xoTJy0InPPbrI6kMpGGD9zmmQoDzGICOtz3XJwbr1o3R7nRBab7rLk6fB3YPvvPMOvPrqq2IQF4/P5XLJMqC9Du8u+4TWfz6fw9nZGSyXyyu7zIvi8vgbPKpSq6/mhOC7mGAazwP1GABUAWD6PsZexDpJzhP/O2QP5RirmjxMAY73i4sLuHfvHjx79gwuLi7cR4B75FhIj3qD1jmRUl5MG1syJzaYldK3oaAYDXhZzjzneQ8tWt09No80fjif0LRacFijy2MT8vag5YTyxbR4Yg9OdtPFgFp7o46gE1Yh3avJG1qGJEtTA1Mh/wkDiUVRwGKxcNn5qYEVa/JRG3OxPgLPZz6fw3w+h9lsBgcHB/CP//E/hkePHsHHH39cazKwDrxjQJMtUh+k2JghXvX6rBbfU3lF09JxJd0DvEt4dYtnbFs2YmyZkt2i2UE5bAueZ4wfEZIJoZhUTPtb5dPf3Ab0xI0s+kM0SGXwNuRpUmWpRp9HNkjfcVjyJMaelfQPL8eyF1Lpp+95vpqM0sA3D7zyyiuw2Wzg0aNH6n2OVtvEjCfpmdUumo1elvKJKjxPr97wjCX+jUQbzcvLV145kGo3eccEPqPlSDtmAS75Bo+YBbi8WmE+n1/Rn7F+Tkimch7g7z3Yha+XSw42VaZnfMT4zzG0lGV55dohzwlYnsVzsTR5+SDkC9G8PXKe6/OQ/6t9x6HpHolW7dsQ7QDGZGyqY5cL12Vwe597laWnMywFmAOWorecLMlIxB2Uof7hxzdKTi03hLvdbnXe/mKxgPv37yevtH3RUNdRwvbBXUwAUN3zhUcW4iTXRx99BB999NGV8vmdQJyuUPmhZ5bAld7hBDE1XC3jEv9utVrVRD5ORmvlphoRnHc1pI7jXLz+oowZbE+cRMB2e/ToEfzgBz/Ykg3r9XrrHkuAekaBlU/dAADmwfOVVpml6BTre2xTDATjUa8YFLYmZ0J18t63EqK7Djxl8wlXbUcoBvzKsoTz83O4e/cufPvb31bvF8U7Xai8tWjUHN3rxmq1qnTzaDSCDz/8EA4ODqodwIh2uw2Hh4ewWq3g9PS0eh4yjq3nMTyBC2pwZ5JWDs0P+1NazSzJb+oU4/tQYI3LGytAZeXD6+H9lgLrWpYlzOdz+Oijj+C9996r3vd6vdo8mGKPhZydOmgqABArr67bX5KccS0d/o+2UkzQzNIxWrCABnz43zRPbsfF6GANlL7lclldhSAF+aS64bFfZVlCv9+vdhSv1+tKB9DjoTnoHboem9ryNdFGR+TUJ5Yca7VaMBwOYT6fV3aXxeupp31gX/HyJZuOynXpmxCoTEK78/T0FF5//XX4d//u38Ef/uEfwr//9/++2s1002DpnhzjRiqPlsXLteS7pe/xHV9YRRd8p+6QTZXHnE5PPlgPXAwdu5vLYydqYyKGPpqe9+OL4isiOL1W/I62L2+LmPGdg6c8vt519FGML6fB2yf8fahNuN+cyz7g+Vt5WSe+4aYGtBN++7d/G9brNfzZn/3Z1g5MymtoG9TxESioLObP6f9cb3I7yPpWshktOy5UD64/0N7i39DvuL1Jn1s6SLOtUObGjK1Y3pPK4WXiiUYAAC+99BJ88sknMJ1Oq+9wco3atJ5yKb2e+lA+uck6Ydc6KxTnidXDCM9JUp58p9NpZZ+3Wi24detWtXgU03A6MTao3R3L7Qxp3Esyv25dUhEbl6TlecZIrK8Rw6PqZGyMQJUIiEFIgUv51R2EWoOG8s1piEhpYh11DVw5SfXlg0Yy8L2BQsyf/tPQ6XSqyZWieD4Ji0JJumPBo2hDPJvTOW0aFi9ZfcF3LdGjXBE8wKDdCavxRswzzanQvufPNV7S+Fniea29Qo6A1eYSpCBFCCEBT9vKGluhvuIG83WD1gvHPt4DWZZlZZjiPZH4zWQyueLEaHwgtZfEeyHZptEtfZPazt70Xp6U5D59Jp1UYOVTV99eh1Ev1Q/vPp1MJvDo0SMYj8cwm82uGKPL5RKePHkCt27dgldffbW6T3swGMCdO3eqHaQA9RcA1PkOUceOwDE4m83g3r170O/3odPpwHQ6rYKioZMw+N+xTqr2Dv8/Pz+H9XoNg8Fga/xbfBwbsMFgAKbHvKkzI+kOzcaSghxe27nuuEPdj+0lnQxh2XscUl1ogIGnpe/r1EOiIzbQLdUzRFNueeVxylJkwHXrc97HNJAD8Ny2DN3pjsAxKMnUkI3D86Hfd7td2Nvbg/V6DavVKmgXUJ8CZQHWC3UI5sGPDcZ8YoM3qfwRkvVan1j2r+U38vchSDJVai+en9SGXMfw96vVamsCXaOzLC9PwKBppXI7nQ70er2KZ+jOFbyqQJKBXtC8iqKA4XAInU4HDg8PYT6fw/n5eTUmQm3N28PSSyF6qO3Mg/dSPpzvY/1tOsYwn5TAmoYUekLjM9bGkfhZql+KL0C/C8VKYvOl33rbICXeYsl5Lo+9elyiW2rvXH6OBo+st9oWafTYEZiXp/3r2qshGqxvQz40pyvUb6lygvMYp80ao9RnAHg+Kfv+++9Dt9uFL3zhC9U7PPnv8ePHcHZ2diWvGFolWjTaLB+JTwRpd9vycjx8Ecs7VGdL1+QhbVJ9Y/Svlgf+r9U9Rvbx95K9U5aX8azZbAZlebkAaW9vr7I5KDR7Jta/5WlDNmooDc8/pz+i2aTab+ndrvyjOnpD0nuYZ6gNpHcor6bTqSjT9/b2qhMG6Tfa2Od6x+Nv5OYFqY6hNB69wZ9r+fD3sbakB66dsU0ZKDRvy1jlaT0ICZKQY2vVPcYI1+Dp6JiAQ4gGypTSAOX5eh0R+lxarSR9j/fxIPr9fqV8yrKsduvQoIzUlppBbrW75fg0yeceWryweJLfV4A7ieg33W53a4cR3VFhGecefrRo4wJaChLwPCxnXBOyVhm0LOm3JXClttDqG+NQWwazx1GXlEgduZUbIT2CCwbG43E1adDtduH4+BgAtukej8dbR5dL49fiIa5vJCNZgubQh/hGU/ypxopEp8bfaGBx8MA4z4+XE3JCME1I7sbKV69M99KCYw13NeHOprIsxcnYhw8fQr/fh1u3bgEAwNnZGQwGA3j55ZdhOp1uyU38lgYQQzR7ZKoFq01jxn+r1YJerwfT6RTOz8+r551Op5qMtcrh48gjr+k3/D5iCaenpzAej7fuNQd4fuKGJM9xxxpNE3K6+YIwfhctHxe0D7nDzGHJliZkc6/Xg/39/S2Hn9MXa+NLcpU6cTS/2N3/Hlsvpp08zruWrybvLUjfSO3rlYU3yR7V6kHHblE8v2sb5SDVQ1wuavKPypTQ2PC2Ubfbre5MQjtDOqaeynFKB06W4VFy9Hspn1h4fT7pGw28z6TxwGUX98O1enn81bIsqwC0dKy8RKvmf0u2vQR+2oeUL/6Nd2tZeXa7XRgOhzAej2G1WkG/36/0ymKxqO6TtRAa7ygri+Ly3ufhcAjD4RDOzs7g6dOnW4tnQ/XH/uJB9hj+ojzh/RbrKC2k8PI0He90XN0USOPHM+5pGnpXsmds0XJC6TiNu7Y3aJmx9n7IZpTkgZZGy9PS9VK6nJDaI7Zcrwz05mfRaPmUMWWH6ui1sbjNQNvC4+tY5XCdx/ML3SHKNzYsl0v4xS9+AScnJ/B7v/d7V2Jw6/V6azKW1y8ETqckJ1EHcF3O8+HvUX/iKWQckm6R0kh0SnYWBbaztIGE2pqS3Uj9Ni1/Cs7L3C4Npbfy5XxJ8+X3D2MMYr1eV3bScDjcmoylOrzO/blS+4fGnObDeOz2phDiI06T9F1qmR5ZY9Eg8VFIj2v5a7oW25+fHoi+y3A4rHwa/o0Ug+GyVzqlRKJRGsOhukh5Sm0fGo/SAlCeN5cjXjvCQoxO52mTdsbGEBPzzU0yvGONyZsGDxNIdfQwIg1Co7ONwJVhdEW81q94/BAeH4bf466jVF4KCcybxmcUllEYyoPmhQEDbFucgOXGI0eqIEoxsrni1pSBlw4skwZkuPGiGZIeR4inleoY015UWfEghhbYTzEGbjLK8jKohX8jVqvV1sQQAmWPxVOeoBHNw2o/zxgMOSXWatM6sAwSbkBZDhR/Z+XLj2ryGPU03XXAkqdeR+fjjz+GDz74AH75y1/C48ePoSzLysmmd5lK5TaFHPlbdaYGerfbhcPDQ1gsFjAej8XJfMmYTdEf3DHGiR6NRk9wRvsW4LINer0etNvt6hhvXPAR+hbg6tE43gARhcfZ99QN09BjHq0yc0MKanCbpq6+8gSu6h79VJeHpTxi7bpUePiEBrZeVPvhJvtnXN9bi59CetcKZnr0bkz/anILr/ugRzKH+JvKItzxeXp6CvP5PHonqSTbJTkTi6Ioqp2v3/nOd+Dtt9+Gl156CbrdLnzlK1+BZ8+ewdnZWWVDXVxcVEcGeuvg9S1ibETqK+Fu4F6vt7WAyPo2JojJf3uCVruScTH6PnYsIJ/TncHc1pCg7ejNEVPjba/JBE43TYvvpYDkTYFkW4bGmxbj4fWjC4W0fGJ5K4TUfHLrZ6/ekBDjJ0v6IMaX9th4Gg115VCMfETdSI8nx/LX63V1+gI/nng+n1d+FeaTej8551XqL9Ex7pnI8xzZTWMAKXZyCPR6GVoOX5AkxZapjODvLJ5K4TcOHhfh7c1jDUVRXImd0w0JrVYLjo+Pq12MnkXLOWCNZStd08jRRzcBdWzyUEyY2ijSeF+tVjCdTqvFpfitdZUfL0NL10R8M/ReG+M56NF8MfQFJBsvBpw+dTI2JbMcCt478DWjwuvMptInwauQYpyGXO815uRHAnnpKoqrKyM6nc6WAWJNhtHycDKWfrNarWAymVwpI2ZgoSLnzkeTsHgshv80A9YTqKV54G5L2o4p9x+hURETJAjtaObppTTad56AtmZ0UuXjkRvS81TjU5NpXJEirF1WknIO9ZE1Bq7buJGCR/xeWFzgEbr30OvwWXwQK2us8mN423ISYkGdMHrMrOWQeYOGOYMUofIsw5W/02iSAk6efsHdPMvlEh4/fgw/+9nP4PHjx9UiATyuUoNFz3UgxF9aG6HTiHfFdzodWK1WatA3FKTlwfwQXXwy1jNmvTxKA5cYHNlsNjCbzVzf5oA38Bdjp0lHB1u2iTdf/jcPnvH09F8KYmWNFSS6Tmfe48w2XRZtS3rk2y7sYwu8X3L3D82f2sZexPCvFiyR7ATa7iFd4UmrfetJx+nh32OAlB7RjOB2AQ1U4O9+vw+DwQAuLi5cNFntSNvTo/8l/UOBO1J++ctfAgDA06dPoSgK+NznPledKjAYDKDdbsNsNoOiKODw8LC2bKO0afl48sc8ut2uqZcxLZfdXj2J/9M+0OxobxnS9yl+shcx/ptUT8xDozfF5gvRYdnAVn6ct3jded+EfJkYePz/mBiBJAvoO0uHSfXy1O26/WJv+8TmZ8VBPHlY30hjRaLBGu8hXRfyHaTnlr8XKsMqh/oPkk2M8U2Mu6FuXK/X0O12tyYYuV6l5YbsYNoe6O/zXaIp/oVH1nl5KWa8bzabKz4MjWFQm0UrI8V2stJJ+Un0Wfnyv6lvu9lsqtM68PlwOKzSzufzygaTaMsRV7LGjaZPPLZYiv6y3od4KUbPN4065Wt+rCX3kBe0U/Fw84tnMaFVlmRXh9o91XeJ4dcY3SaNd8suojLPI1c8tiGi1mTsLhDjKHi+9aT1NGwsYoNKTcOrlCzgYKY7ZbUgBxoIuGNjsVhAr9erzsgHgK37IDWavH0T69xdFzx858mDHu1RliXM53N1tYvluMSmS6WXH5VK+QMArhyhoOVjGU3SszrHfOQcv7hbmdJFFydobW/JNO04UZoXVyBNy6QYpxcdCbr7Aidh0XGJ5cldyVyPocQRG2Sx8uF0SODHKXqNMfr/rnVY7kCVZETxo3jxGJef/OQn8Ktf/Qq+973vwWQygYuLi0oucTmCKw09NN8k8LsQB4NB9U6aaMY0s9kMZrNZJbc9TiwHl0OW4bpcLmG1WlUOKabRgizUYaS6LMTz6ASjrMFyPCshJd7KNcY9QLpxMnk+n8NkMqn6iO5uq1NGv9+Hg4ODahHdZDKBxWLhyjd3W2i8RY/NRT3yGZ4jxr7zBAH5EbTSGMXxgGMQ+YWe3oJAXvXIBou2srzcudLr9eC1116D2WwGk8kEHjx4AGdnZ9UOR6RZWtR2eHgIvV4PZrMZjEYj6Ha7UJaXJyNoR3ahDOLtS8fILuUCwFWdhQFPKuso7fgtLoyTfCwtKBmys60TaqTgnoVQcIqn4fevou09m83gpz/9KfyH//Af4PDwEE5OTsxy2+027O/vw2q1gsViUdueLsvLU2I6nQ4Mh0NYLpfQ7/evBNZCOgjrJ+3MiZH9deyZVN8Rx99yudyZPZWqoy3/WgLvk5T6WTzG86GTMTch9pQiwymk6xB4vk2U+2kAr7dkz9aVXXXykPQAQL14jbdcTS56yuaTH+hL8pjoYrGA1157Dd566y1YLpfQbrfh3r171dUrlBbM1yovpm4x8PZjk2MK6cY7LXnfoI6j8tPa9CPlLU0m8jQA/hgJ9xUBYMuuounoM6rnaDoJdBe1BIk3rP73xhc9eb5IMY8XGTFjLeb0mRi7kNuXFk2aXG8SGl9L4zyWHq8dngLXZGzMAM9hQPNGkvKMCSJ4hY5GSywkBuTlhepkCdYQg3vbRsvHI8ClYCe+5/Wgyo9OthXF82MacAfSarUylY3126I31Ca5hEQocBUyYjxBBfxN86NBFT7Ryb/VjBeOGN6nY9YjH/j//I6u0Pf4t3eM03R1HYcUSIJfW03JBX7K+JbS87GZooxSINEvyXjKE2W5HUBH2RE6go3LIyxPokODJ7hH6+H93hM0oPIyBSHDyMs7McGO3PDUP0bWhEAdKACoVhWfnp4CAMCDBw+qNPT4V02+estsEp425PTzdkA9widccaJLci4Rmn2RYovRMujkMbcttHpLupPmzeUQ1lcK/Fl1SK2fN40GHgigshMnovhdxtY9wCHQ45zX67U6QR4T0AnBq9/p77p90iR2pXtDNKTao/RdyObnuh3/5wFh6Y4h/l0MkA4M4g+Hw+oZnTDkPML7ptvtwmAwqO4Jp5MClGYpH/q/V5bwNvDAGnOS/ON2sNa+mAYDo167wOKNOn1qIcTLEn2UxuVyCYvFAs7OzuD111+Hg4ODK/em0m/QJpV8rRQURbG1kwr/4YJET51QNi8Wi6QFYnzMpcrOuvEfKc6QG5rNUFcvNWkva3TG1oEe1euRQ6mw8k5t5+vWm7lB/T6AuPrF+EGSHx7blqE4T4zPpeVRFzH8nOqr8/JoXtS2WSwWWzb37du34eWXX4Znz57Bs2fPYDqdqvEZTZdb9HK7i9sgPH0dGz3VhvX2vUQ75eWisI9e1uqOzyT/IVZW0T7z+ChUJ9PvpWf0qj+Aq/fiSvR5bLRQn1K6tXbxjA+P/e6NHYTo9OTbFJqUZVI5u04fI+dD+iGWBi+f8Tz4eLDGbyoPSb6eJ71WnjgZm6qkc6JucCqlU63vJeQyClMYLidyBIZQeXA6pdUZ9F4buoJsNptlXQnHjZubgFyCezgcwvHxMUyn0+roLIDLNsTdIEVRVLsvtaACfWYpcQ+PoOCjdz5Y9NNL6uv0vbYSHGmisBxYCdr7OvJFAj9axkOHVmePod1UgMMDpFu7C5K2Ae74xmCZBZ4XTiZJ7zg9obzqgveJ9jtUdgpdmmyOzYP+H7PqLla/7IovqaG2Wq2q4/Ynk4l41zQeZ4iB6ZuMOk42vsdxeHh4CMPh8Mqx99a39D47i0aqm6x7PpF/O53OlnHN70Xz0EbTY35audJzLP86ZWgd0OAQ/vZ+s1qttvhif38f5vM5jMfjSod5ZG4svVrg/EXFp6EOu0LqRMmLPD4B7EUkCG+ggX8rBf9yAPVGURSVvpxOp9UR97gLCP0UXBRi3fUmoSi2J0K5/YT14yfsePnh/PwcfvWrX1X3xXK9T6/QCPk6HqCfdnFx4bbV0M/COwv39vbg+PgY7ty5A++99x589NFH5n3rCO4bxizUqasHeXCd26o3afzG8A8Pwnsn07Ec+lsb49JzSx5gG7fbbeh0Olv+N55usStIdOdGqt7YJfgkmgTP5MR146aM09TYNV9sVKc+RVFUixV/9KMfVcfPdrtd+MY3vgG/9Vu/BW+//Tbs7+/DBx98AO+++241Kct1IQJta+26IbS7N5tNNbbn8/mVa+M4tHtIvfy2C75Ee0Ly9bDeKXR4rurj7WzJZDqxqtlb/Dv8TWX+crmEs7OzahEWvu/3+5X81mJ/qGewrTR+qetHcZ89No4Vwosg864LWt9xm4Omz1Emz5e+o7ajdTKOND60OQktLuONbWljMMSjofbiOiIXn4qTsVqn8mfe4FUqYgPo9LumjQMPQ9VFrEL0BPEtxzvUplJ6HDRavlyo0nRUoYQmDGKD+buaiE2ZZPDU0RJEnU4Hut0udDqdaqcWVbr8eCKr73nf0L/rOsQhB4MeO0vpl4IrngkzKcDkEdwhxcbT8zR0DPB0Eo2SwY307sJJpXTsAlY/8J0l/B3SaS0m8PSVhdSAZi6EjBHP9yGd7UGMAZwamMqZb6g8T15YZ8lJog6q5CBb8uc6UbcNuY6mR2/h6RUhR1ZzVmn6UPCJGu5WWl4G/57LVeo48DpL+sdbrlbvXQJ3Mu/v70On04FOpwOLxQJWq5V6l7uXZtqmaIPQo/YlxDg7Fk0pTr7FZ3VtG6mcFFwHr3h1qMbjFNT2BLAnvzyyGP/32nxelGW5tUjQSxemR3ubLiSlabTfTfvIEmL1kjYWqF2Ovz3tz/PjY9fKk+viOv6g9S3alLR8ulMbj1/HCWNeruSj5ACeEDWdTrcmx6Q+4j4D3nk+HA6rE6dofWkeoYWvMfxpydIcNmJOWW2VnWNMSrYkLYOmk55rtFnvY+mmNhX9F+Jn75jz2GqSzOQyWfOLvHakVX6Ttnqq/c15J0Svp72kfD3fhPLTvuF0aTad12fX6LbGbOo4TpEzmm2JOmM6nVbXqzx58gR+/etfw/7+PvT7fdjf34fDw0PY29uDsixhNptVeXKfR/OBJFuDnmiEz6k+1Rbw17GtJBmi9SXnE6u/PH5YyF7wyMg6cQ+PrxJ6xvPCEzJCp+RpNNK0IbnggWT38P6U/OZY/Zbil2pI4Wntm5sS2wGwZadXJ2jfx5Rn6WirX5ui05tOk6PePq5rJ2p6pvadsU05lanMv4tBo3VmrraoqxTrlOWpA9/14i0T707odDpbd9PFIKfjxPPcJWIEEFXIg8EA7t69e4VmdMBHo5FqmHh4FA0BSqcHNLjhTQ8AVaAY7+7iu9IsRWCt+qJ/N8EzGjy09/v9K5M7q9WqMsatbzkkx+emGA7UKJRowhXaCFxRhatUO50OrNfrrfu4NMcMJ/br3g0o7dCo46hoCPFkqDy66xfA3s1AnR+Pc4N0eWRFU+2S4xvLeZV4Et8NBgNotVrVSlSASzlFx+dNhqcNOS9wucXzWK/X8PTpU1gsFjAej7faUVsJie9oAACfYfkemYzjGxEKEuHqYH7Mk5SejnEaEIkBXSXsrVOOsUNlK70r9tatW/DlL38Zbt26BXfv3oW/+qu/gp/+9KdwcHAAnU5H3Pmt5c/HS1leTs4fHBxs7azx5EX/156l6moecKQ7Ca5rp1UT8jEVWnvyCXopWCZ9j23a7/eh3+9Dr9eDsizh6dOn1U4G/KctAsAy8Bku+uh0OtV1JXg0PE6kpmK1WsFoNKp8D5Qn0okkSCvWcTAYwK1bt2Cz2cBkMqnqSK9Y4TKT02othPAGpptAWW7vCgrJMMnGt8astPPVA6of6JimdKMNJLW/5YsgTy2XyyqforjczUTzvri4qCbicwRgvLJgsVjA+++/v7WbUhqXHn+LjkPav1KauqenpCDULlLfhvLR/t4F0AehMk1KQ+0hT/1oH+FvDy0AzxdoIXDCf71eQ6/X27qru66eTAnue/OyJi94+/A03gB2DtTJL4Ye7dQ5LUBOYZUR4y/GIAdv0b6U4lvWtzHPU4HtL/nj3/ve9+BHP/oR/KN/9I/gq1/9KgAA3Lp1C770pS/BxcUF/PSnP92yKwCu+uFUF1I5QoGn5eFd9/T75XK5daoDpfu6YucauBxHu5Ke9oD2G9qL1k5WKf9coGNF2m3s9XMpnZIP7okvUhp4/hzIq3WBfBqKI9D00jsuc+rIoJyxJMRN8ukkcPqa5Hsef6c2Ob3OC9PweBO1Rzmdudo4Nb4ak2dqzIJDnYxNZeS6BEmMJCkj67tQWk/5XsUuBZTq5p8TvDzNoJXeeYMEsQLKc9xhrsHinXyIQcw3VoDGyof3FwYLBoNBFRxAodftdmG1WlX3WQFs71iwgtcpxpGXL6Sx3G63Kwew2+1WF9iHeNAS1qE+1ng/pLTwGymdZxxrzgf/zVfo83Q8KG7lxfPhQepdGBKSfNDkDm9bHlCzJg0430jjnuevBYP5u1j5nCITOM1SP9XtL4/8kQJ9njFF2+6mgbcr/Z/+rY07aijSo1k/TdCCXdTRBYAtR5eOSc9xhppNyINoMeNNSi/1d4ohHrJtNDnh0SVeGiSaNMdVS4tHlfX7/WqhE8ob5G1ehxh66C5Diw+8MpXLE4snNJnmrQftszo+Qkq665KXKWPMm64oiuoY2tPTU9fiBy5/i6Ko7Fl05Kl+x/9TJiNwsvX8/By63S70er2tHY9SntheVO7hJB7yvzTRQsv2+KSegJRVN+sdrQMdU5Id77ExY8Yat7kXi0UVNLUCiiF5K5VBf2snrWBa5DF8b8lt2lZ14eVX/Id3DWoBWFp3rPNkMoH9/X0AuDx+fzgciosYUm3VHLDyo3aH5BNZPozWj7n9+5Ct7GmnmAUmdfUTlWE8OLrZbCobYbFYVJO0lvzWyvHSY+VnfePpe8nPD/lRHn5sUmd7ZL1kc1r18tq9Ht5KsZGkvtLGsqccq+80PyDUXxqNFn0xwDFHv1+tVtUuWfQnW60WDIfDakEUfifpZxzD3C6S2o3bLaH0WpvwtkgZy6H8vTQhD1kLdDSeiI1veOrhyc87xrTn3J5J9VmQlpw+ieWDW88A7PHE2zS3DVIHVvvdJDopLLkb8602bjWelPRnSF57/Zq6kHg+ld9y2Qe1dsbmYD7J4ZZAHRVevoVcA0NyYCldqXnuEp628wj7uoFpnDhE8KPONLq8uGnCMBXUmOt0OnDnzp3KeVqtVrBYLKDX60Gv14MHDx7AaDQCgMu2oscHajwaY8gjeABKctysvhwMBnD79u3qNz/WLkQf5h/iB/7esxPIk49FV2w+AHBlcgNhGTohg9ha5diUUxmCJbtRrlL5GlqwwXnMuxLQ4/SFxkAKL8XoKo8jSdOGeFRqe248flpkZip/a9+tVisYj8db6a5rDO0SVIdMJpPKCUbQHUOx44EHBurIU423AXynFFiwAiD4fheoy284MYsnk6AOT90JtV6vYTqdVouq8LoEgO1J1VjnyTMRk9oW0o4wL+/VCYBIeVloMvCbE3QstFotODk5gU6nAw8fPlSPKuaTAtim9KQdnCzFBQQI7RQUC5i20+nAfD6HDz/8EAaDAQwGA1gsFtDtdtX8UN7hmJnNZlCWV3cR3BS96QmyoQzDE42oLY8LjuhEeN26Uflyfn5e7TRFhNoyhgZtgoIuQsH+pHyHE8RNgPO+Na7p8fhFUWxNxoYmrfD47EePHlV3Fe7v78OdO3fg6dOnMJ1OXXbLTZA7mv7OGYhLBQ880uC4ZkNLk1C9Xg8Gg0HwnlY+EREDWibKYxoTQLl2eHgIh4eH1Ukwp6enrrtjc/UDrVuoj6Wg8i70adM8l9K/dSeTYr7PoQu0CYGYtrX83Jw+bAx9Gh+iTqF3d0q+UlEUsL+/vyVb0EanO9apLUPtJ7RTeKykKIpqYQU94YnT4NH1VC9dt46gE7GSH8bbuQl72opjUX6wJo219qbf8HuCEdjXUgxI0p0ckt6SbFvrW9q+lA5qb10H3+Ts7xj/8CYh5DN7+dHbb5wntDIxTYot4/1G0zNNI0dZ6mRsTPDfE5yyDCyLWWjnhRQGzydnZ3A6PQxS19CIgWfAad+kBLw4f0j8IgW/tDaz+E0KgnoMiJyI7Z/U/uTGJT3yggY9KbR2jaGBKk2p/6Rxao1Lnkco0ILHJsUYBlrZtD4e8HFt5Su1j0c+UR6mx/hZ4wjztGhJGbvXYTxwg5mv1gSAK7smJGh95e1vqa8s48Sj3DUnkabngTkrPwuUH3CcWPX2OKnUgE+hrQmHR4Jm9IVoteiT5Bq2w2w2+1TuiI3FfD6v5BadnJR4i0+0xBwRy8cLfybZAZ58JJmhfYeBSJRFfCI2hF06AADPgyk4kfX222/D5z73OfjmN78JFxcX8OjRoyoYn4KQ7W+923VbcBpy2/91aEmxRXJAs82QJhyvqY4yzbOpoIvHv/R+n8oX3m/opD/v97p60uOT04V4kh/lsRclGxT/D9lAkgyX0qYGWGi+vJ7cNqfPJf+Gg7evZ9Gnl+5YX8x6p/kIMfTGtD+mD9Hm+d6CR1fz8RTyUVNolOrIYw6UhzW/TcsLQXfae2DZNdJzKx5m6QVqX1u7/1NglUvThHxe3hdNy/Um4LVjtZgMynraRyH71gOJt2L0hMfP5eM8h78ZEw9IKdNaPBTS9bjbvygKuH//PgAAjMdj2Gw28Nprr4n04XUN2mkgtA6aj1WWlyc74eRur9eD/f19WC6XW4suvDIj5mSSphATh/F+a+UX0sd0fGp85ZG99G9t/IW+5c+kCTKL9z3lxMpMyW711IOn9fRvTr6UYn8pNGh2uxQTbAqW7xCyYTT9Q/OS5B9fjCC1oVa2lL9UF6k+9HmoXZvgdy94vuJkbKhxLMMnVtiFGtLa8SQZcTmdJ83QTxUKmjKtK0A8hpMnjZW/t89D+cYytlYXafDHIsZx9QqwnKC7JsuyrHahSEKvLh3ccOSCkwaltYCABk+Ahk58hIwbT35e2lIDJjFlINCYp0fU1B0vtD80+YTteV33gvCxggsLeB/jbgANoUADN4yl73PC0l2e8jWjwvpecnAtg0hKJxn+sTLkOpwxvCtG00WSPUDfSeklYxHbYj6fb6WzcJ3OaZPAdqBtaR1/jzKN7vTh762ypN9SIIRCswdS+BnrKt1PJOkiWiYNVOwqyIf0zWYzGA6H8A/+wT+AL33pS/D//X//H3znO9+BH/zgB9WJGbGg9YitE5dVnjEk5REDzgdIw676wqLrJoD3J//n+Z7iutv1JoLbyKnOfsjHkfxGyuupPMePNaTlSvlrfrEVP4j1PSmkNrBsQp4PleccKPPpYsHruEeVQ9IxFNSuDi1i5W0m2Ygp+iuW32JtNenUIM2+rQut/p6xpdVL+gbveee7ny25wOnR+kuLLWGbaYtekd+p/ZMTWttyu16qD5cv2jcxffAiA/156kvjM2tsecZ3bJtpvBaDOnLW0j1SPESK34XaReM/Xi7nS/wOZdivfvUrePfddwHgcrf6rVu3rshtXKih7XrFvOmR7hrwmpnBYACdTgf6/T6MRqNqMpa2B5ezoZgFb5cQco3BEH/SHZp14bVrpDTcDtR2D6be26rFNSRIY9TzXQy0Ng+V/WmGpot5HK4pO1Maw1LZEg2Sve+hE/nAG+NuSi54xi6m89LRVF+Jk7FahSzhFiP4LAESE/S0yktpLMlo0crXgo2cRv4sZCjEwttunIbYwJqUl8ULHnj5QEsnOWV1ypbeWenqDkgrKEDvzLlz5w4MBoPKOMPjRzabDYzHY5hOp5VhxQ1zL0J8zIMemuPD+Rz/73Q6cHh4CO12G+bz+ZbTF9M2XkdVokEDN55DeWqODqVPo5f+ppPPljNtKQkrIKGVvWt4xzQGCbQ21PRRjB7w9LHE0zH8WEf3SBNdUn15Gfyoa42uorg8fpbubozd/dk0H1F+5Tu36PvQ9x7E2Bt10rxoKMuyClzzYxw1uWLZBHQxjzSOJcQEd3ha7oxrMpLnMRgMqvR4FYC2g0WzvZpyqrxAOXp4eAhvv/02nJ2dwSeffALD4bA6stWj7ySZhkdiXlxcwGazgfl8Xu2c5t/E5AuwLWc1+yIEavugzLDy8vJeHdykQITVntj++D507FSr1YLlcgllWcJkMoFer1ftwMBrSCz/RBqT6/W6OiUF00gThCFodZzP55X9rNmHVNegvXp6egrL5fLKEb8azyLowjutLA2hdzGBEQqUjYvFAvb39+Gll16CxWIBy+USzs/PYT6fby18ksDvoPRCOqaRPg/J0lh5INkMeD/fTRmTFJwu6bc1Ls/OzuDnP/85zGazanFjt9u9cnS45I9of3uQIuNieJ/HXTQakI5ccRUNmt9lyVa0Kfb39+Ho6Kjy88/Ozqo7W7WyaB4WTfRvS/bis8ViAaPRqJqw2d/fh16vB5PJROSRugjZcNTf5e3B2zuUl6brX3SbXhqnCJTbNIYEoE9O5bRNaLwi5pvYtJb9LskxXscYvqkbO+InVWD/YPxuvV7D+++/Xy2kwatfULdSHwz7k48L/G69Xm/Jefqbt12v14PDw8NqzE0mE1iv11t2Vu7xr+WTyn8x/qCWxiNPrZia57lWjqRnJXuS/82PAuY00LpJfnCIVz00cLstJ67bf7YQo0MoNP9H4i1eVhMI2Sk8LecN6XvpuWbHhXQz59PYcRoqj5fhtRmpPsndP8E7Yy3Fh7AGuOd7SwBYeUkGp9dYtWjzwBPM8hoAdZEjSOH5rg4DaoPXcjw9+XFFUxcSDTHGSGpwBd+j4EMjqt1uw61bt6og8Xq93jpeZDabwfn5efU7FETJCc3wkN7jZOxqtap2wXrv9EPjVQr+hOqp8V0ojUdZeca/JQNCzjenV6I7JhjHaWna4fTIfgrarlTpo1GpGTRS3l7+9+gdi/4c/Of5hr6z+DVk9JVlWe1Mxt/aHX8WDZYRmQtFsX1UIAaMNBq9feEdBx55/WmCpPe89wfy8aql4ZMSoW8kukI8KsnLkJxAGdPr9SoaZ7NZ8IjfXTpQHlAnfX9/H1577TX42c9+tnVU2Ww2UydFLGAwabPZwGQyqe6dWywWYtDLQyv9Xyov1YamO0Sacp68eJHkBA2yhPylVqtV2XF4ikC73YZ2ux28d5D2Bx2LdLGhlh7TprQr5s13EUryCOXVYrGA9XpdBURxHHDaJP7ii6Q4NJ7kQYJQX8TYIdhvq9UKOp0O3L59u1pUMZlMYDqdqvRy+zd2vGvBw5T+pHxj2ZSa3c2vy5C+9Zavfe+hkb+nbRx7R/FoNIKLi4uKv9HW0yYxcWeVRiPnvRQ5GtuvUt09dsIufBpKX8x79GFR9gyHw+rddDp1XdESQxvXx1qgcrlcwmQyqdq63+9Dt9uF6XSaNV5G8+B6X4oBWWPKuwjkputdTRZK6aQ4p/QN3dFPge2Ku7B31TYhu1vyI2PkizbWuF3v/Ta3jUhtCer/0HcPHjyodBHKanrXLOpr/IYuXMI8pTtp6TdcfuIpe/h+NptV/rUEvgNXkzOpbaQhJlaixW1jy9TSSmPQQwc+s/wc3p68fElG8v6V6o/5aZPyvByMD/G68PpJdiClw2PXau/qQLNhUvJvWk5640u79ls1HqQ0Se9i3of4w5vGKjPGVo2RQVzmpdjDEm8FJ2NzwhIYHHRnoJVfHYQMGkzjwU03/OoiNShW5xutTdHYDA2gmDJvav+hoaQFLtGAo+lj8racNTRk+XjQDGCrDdfrtejg4U4FekQY57XU4E9deJ0l+t5q/9B9G7yuqfC00a7bUTNQrXbDMczvHuBp6P8x0PQOpceSQSlBGSmNxPda2TydRh+9f1lLg7uaQkf6xIyD3CjLciuA7gnKUXjoxTaiK493bQDfFND2wpXVyJ+hY865IR3SCdTGkwJIKXR7xmRRFFs7+LCem80G+v1+VLl1jPKUMjiwzTqdDiwWC/jrv/5r2Gw28M/+2T8z80vd2RYDjwPnaTPJ8Q9924ScymF/7NqGkRArQz9Ds8B+wPHoWShhBUt4ej7WOp1OlrFv2bH43mu7h3SFhxZNLoRsNf5drt0eHjtNeh9qK22ClvIRlfGWDYu6ndo9sb5zXUi+ZchuwLq1Wi0YDofQ6/VgPp9Xiwx2AY1Wy6/odrswHo/hgw8+gJdeegkODg5gb28PWq0WPHv2DNbrNfR6PQCArUWSIRo8NjzuCMfxXxSXO+7Oz8+rNkTaMSCv7ejPqce0duR8zn29WNuBlyf9vm7dnAJPTNVaFGTpDm/5yE/oJ3DeleSKFeSuK3+a6MfQSSEA8kISDmyrbrcLAM/HGq0zLnSkvhH6wjzmp4G242q1gtPTU2i329Dr9aDb7UK3273i66K/x+uTamtf93jKqcukeKlk32hth+2ag57UCaCYvK14G/07lE5qh6aQM29vXp8Wv7AJaNeMeo4R9x41rrVdbJtqMWgJEn/HQMp3S6JrgyXGmAkR4DEcOC08yEe/9cCij+dH/3nzCgnhXUJyRK+LFgke5pae5xLkluMUMrAQUtA5B7Q+Wq1WW/e4cuS8b0BKExI8UhtK41Rykler1dbz0MSvx1j38nyMQPU6CSHZ6aEhpo1DAZ/rGPexipH3c8x4ih17ocCJ18HPDa+hjoYMwNUxRtsxtLMI05Tl1ZXDPIgqlUX/bwK8LrizzyuzrN9WeZLM2UV9byJ4cNQav5ocswJomD9Nk+JkWr8lOpHn2+321nFu+B7/efVqioyNsRtCdQJ4vnL9wYMH8PTp062dNlaANlZn8H72OEseW1qSwzE2OP2GBvVC38QgZFdY72NkyC7ljNTOmvyzvpECsLE0cN9L8/skmrR0scgZvJPy20VASoIly1HWcf1KadfaxeMbpPiknrysvuJ0aTweksO5+CHWT4zx2zUavfI5lI9GW27EtDXVe91uF/r9fiV/pB1euWmObSdK73K5hOl0CoeHhwBwuTii1+tBWT5feCr5m15+53RKsQOUAbgQrdfrVRPcWLa0MzlUngYP/3v6TJNPEjx25U2Ji4VQV29Q/qPPYmIpVrwB+QXzp1cweU7Z0Hheq4eH3qbHfEhvUPCFrEVRbJ3OgWOflkXrQH3hsiy3rknQeIPbHfP5vFqIod0FjeXmXKgZo8skemLGfB3E5B0b4/J+p8nYkO2lpeFpJR7mzzTf6UWPfzTpM8RA4hOPLNmlrrJsyhBNMf5jiLdSbELpWxoXiIVEI9czMflqckJcXuMJ/uDfTTBIU4PGAiogjxJq4pz0WOxaMFLDNvaeTwqpfesEqDTjy6O86mDXRvxms4Hz8/OtAGudABiALlC9x+dq+eHfOKb29/e3jHJ6ZEas8dMk38cKVIB69NAdn5aTHSpDWv3OjXt8dl0GFe5spKuvEXhfEg/Ehla4hwyGOqB3lebkPcmBldJo8ssywHGMUUiLGzAfPP6I7rzndW1Kx2vACTJ65+Cu9S0NDLyI8Aa+vPXLyQPSxIQESy6mBLVD5aH86fV6MBwO4fj4GE5PT+H09DRY/xRnKSdvId24mn48HsO7774L5+fnW3fRoU6IobPb7VY2H22H9XoN4/EYRqORqx8RXF5JbVeH1/BbOhHxogRcm4DHyZeCgPQ97z8pePPo0aPgSmbsb54f7sbAO2cleyaXDuDHE1O68D21nfBvqjP5CSfUd+RjLHTXrScw0ST/4i6b4XAI3W4Xnj59Cufn51t3DyJCASSJd2JgraKXgvUe21gLDGE+deiN0aGxOqvb7Vb1Xi6XMJvNrlwZ4Kk72lEaP96EWEYIdEzhWNN2hmFaOkZjFlWl8kHKGEX/3ZocQWA9+H2BWrn0OU72npycwHK5hHv37kFRXO6QRXlI78NGpEzIaG34otjSWnA1hv5d+w64W1LiIR4fk+wtOlmvTQbFAK/74HeY5moXS/55y6gblOfxFU1nc1lLTyvgNi8uIpH0GrVLUK5Tm8WzU5YCx3u/39+SA9qpHKE2195JqGvfxE5+aPJIszdCtGEa7Ef0jTw0YN+mtLOWJx27oe80m0ma1/HI/06nI8YaJTp3jeuO4bzoulBCrjaNiUN4YjjSuMlBqxSX4H6xFquKlWtBCS4R4ylEawSp0SShYtGhlRNqjBhB5zFycwbncwSgYt950nocXkwn/U3zlYI72jex8AZdY43q0N+efL19rAXCcGcY3e1CHU4PDRZCxk1dIUONT1pHzeFJkQOeNKnjPtR/oWAb/1vjVWqISwEnqb28/eYdx6ngjopFBz7jPOEdwxQenolBKNgnKfsUoyJkEHtkpBaY8wYrNceA560hJiho9a3WFt1utzqyDWD7WNu6/azRxml6EQ3nOnoAEdJ3kvOmjY1YemKcGK8DKkG6MxCDWniUF58IoHnGyuMmQHUG0odXAtCJWBznsYu3eN5YJpZDg0seOUDz5eXEwup7r66uax/XRao9yu2C1IAYbUPLNpNood/hpE+323XZ37QcSjuOP+k+11B9vLpBO2XG4iVuu/LvNIfcst+8oGM8lA/XW5rsxjzxjl7cMUPlodYesWOO14PzjpUnr1NIDmsymX6XS043aRvQOwRjZDblQ1p3KR3/zeXLdegy/luSE5Q+1Gmr1erKKReYzovrsPUk/R2Cp2+oXU95Ce1qOiYsHyAW12kvS32uyRuP35zDjs4Nzd8LxS00e5rnZ/nuHto8/Bzyfb1lWXrIsvO0tkqJ/XjosdLSRV8ajZI+lmScxOe8bvgd6hRcHI931S4WC5FeqXwvPOPJI8+s31ZeEi96/AMtDY+vaH6uNeZi4itaXhI9FkI2ljT2tfbgMrZO2U3Ba2N65FEOXRjjc9wE1BmvWlqv78D5y1u+JPeaauOU+AMF/9a9nKaOktZWWEjPtQvmvYLJA0679wz+m4DUoJX3Wy3gAxC3moEjZpVlKCgSSufNz4NdOqToVOJ9duhEjUYjAGjmLiPv+5ATo/EJOsxa/yOP5V6xXaffcsoaCaG7Y2PKjXHidwEua/iuG358Ef2GQ+ILmldRPN8Rhvef5oA3OEKPGIoZHzn7ixvR9Jgoaox6xhPdDUyf8UB07PhIre+rr74KX/ziFwHgko/+9m//Fp4+fQqDwQCKosh2N5gW8HuRETJCYwIHofc0CMP5JIUGHjyXQGWox75BWYL5t9vt6n5Y3BnC69put2Fvb68K9HKZcBPkrodnO50ODAYDWC6XVT3r2kR4dQIGeAG2JwLo7ybRpLPVJHLQje27qzsSY0D1Px17VtqyLKHX68Hx8XG1GxCf0/+tfOrKb+7jYJ7S0ac3ATRQGwoM4P/IL+12G6bTKbz//vtwdHQER0dHsFgsgrt5Y+nDHTfUDqH3v1P6U9uY9psUvAbYngy+yUAa2+027O/vw2azgclkEvyG74yjutlj/2F7aXfSNgVKJ90NTH0FKT6DJ6jgLjHO503fiR4L2g+x9zaj78THpjb5gv/j8/V6DU+ePIFutwtvvfUWXFxcwL1796419pUyuSPFgPiY5rpVaiP6m8tOiZamYwJ1QXkLZW6r1VL1VsxkkNQ2vE1Rz2Osh8oi6pNi39QJYtO0uX3pEB9wvvK2oXRqCLfb0JbGu525PyXli2noSVIa3Ug72lUAl5OxvV4PDg8PYb1ew+npaUUXyhxJn2p9aI0xC9oYbRLIl/wIaYsu3u7ayQY01qXlS2NgWnn0mQSPrxyCp+25XexdgEvz/wxh8L68if7tddvPlO+uI2ZHbVVOlzRuvfRdscIkB0b62/omBE3paUonpBQleOiOgZVHTKM3NbBCwlsy0EN18gZSPUGSupCMcKn8WH6Joc0y+K18tfbhz+kkEP1HnVWp7+q2r0SHJx0HF0btdrsyUtBZoP/T+lgCNrZ+HiM6pl70m1hZYhlzGg0eR1LK4yZCcl64DAo5D7ye0thApBgvOeSX5ARZ4yhW7khtYTlqUjp+R00of824SEGojalj1O124fDwEI6Pj6HX68F4PIbpdFo5iVxWhGjzOoQWPI7nTUJKXa128gZmQjaih/dTx0YI2hil/IQLorRAnSWPr4sf0PFHB2E6ncK7774Ls9kMRqMRjMfjKl0qjVhvujtdS0N/pwRhPN945B7P1yq/KeSwO3PQoNlUls3FA0/Se66nNX3s5Ts+Hr3fNAGvDS/Z5aFvJd0aCqxJPq01DjV/gfcrTnxJC6E1urRyNdvDK3s02i0e5PC0If+ujo0Q4wt786HPPJPj0ni2IMkCSTc2rdM8sQj0GWlbhOzgoii27kBO8QssSPZnTFsVxeUimsViUelvfC4t1rX8P0t3Il10B/ytW7dcC+ub6ntJR6fYAallSuXF8IcmK5Avcy+at2xNmkabVNJ4M8Szmi7Q0kq0h9rUakst7xBSbE7+ntstHvq8/j9Na/lT2jeeunj7CWWQVL6kf+u2rfaNVxZ52jT0nMcPNNkQqktIVll6VSo7ti/pOw8tFv+E8tee83w9duKLgF3RHbJfvbGHFxEx+o1/o33H85D8LM+3Xmh2oKcc/m5rMtbDgCmKkTMX3YGD+Wnn79c5PiU3017nIIgRDh6jQHLOaXopaJ+Dtth8Yts8ReHElpXTYeB5dbtdNW2d+2FpGbxcT9qUcT8cDqvJWH4ECh677EEdRzpGpsU4UTn7vakycueTUi4tm68kqrPTHsfJfD5PJ5Aghcd3GayQnAZ6lx2+s3iYHvub467pnHzV7XZhs9nAeDyGk5MT+If/8B8CwGU9P/zwQ/joo4+q+tIFHinIHZiz8m6yrF1BM5bLstwKFFv94Q1WcpmRithgTrvd3tqBFBtQkyYKdoXNZgPz+RyKooDBYAAPHjyA//gf/2NFT+wuHArkXzzGDFfg4x1W+F4LTGIeIbssJZht2bDeIMWLDI/Osups6RlpB5bkIGNa5K+cJ1RcFzzBJGwr1MP4Pz1mGdssFKiOmRiwAnU5dY1mc6DMp+MfIbUbvRczhTZsV/ob86ftmyp/vUHm6wSVv3VplWQy9fevAx55gTyEx2oChGXbYDDY0o03rY/H4zEsFouqTviPnzoh3VFNYQXa6TicTCawt7cH77zzDnS7Xfj5z39+Ja001mjZu0RItkjjnqcNTYTwOtfhk263C51OB2az2c7vYi5LeZee5TOG8tP0PdVTXGdROcLL8dh4sXag9H1OxPoRGrTJBSkNynprxybmVbe+4/EYxuOxeI8tp02yF5EOShN/XxeetotByoSM9Y2WD7/nV6Jfq5Pm33j4J6YMC9zWorJSkw/47qbp2s9wMxDSKU2Wuyue5LJCqp9UX3UytglHn0MLmHiDN6H8Q6BCBjvLUkZa2Vb+IeRQ8DG0aAEsK+Aq5RMqx5vGE4Dw5mfloQWeLCXnUbyhoImVr8bjPNCjpaXpY9vdk9ZrtGjOETUmPc6Jx7ny5KHRZ6UJOX4WLbz9pYBHiKbY91o6zluewEUdeGRQiMbQd9RxRtB737z5WTR5+pnTEJKfXki8F+KhkDzQ3gFs3zcdcvh4nho/pThh6LwXxeWxdHRyvtVqwXA4hOFwCL/61a9gs9lAp9PZ2u0Qi1hZH8pHgoePrhOarqDPY3QxTct5j9tX2njVxlqKbAnRSidLVqsVLJfLKviJvLhareD8/Bxms9kVGqksatqBSEVZPj/qTDoeDWHZSvgPg2t4X70VeJFkg9eOl+zvptrX4xyljNsm+UEaV01Dcio12urUnfIYPUobj7eV+I7KFG08SvarZddimfQd3ZGnXb8xGAyg3+/D3t4ezGYzmM1m1f2VWKbWhk30Y8xY32w2sFqtYDweV8e+oszwBP68PNLpdKqFYGVZVjsCY3wMzUf3+OwW7fjsJulq5Ht6rHNonNGJFo0H+AQfwit3JVve24cecNtgMBhc2eHKcXBwAIPBAF5++WXodrvw9OlTmM/nMBqNai9gDvFFSt3xG6QNbY9erwftdrsaG9wfjuVRze9qtVpwfHwMX/nKV+Dp06fw9OnTaiIRTwW5qbYNRUhn03Fu+ejUbknxYxAoS7E/sR0pD6b69V7wsS35lrG+juX7STYbpQEXOUryxbI/Pf6t1P88VsB5gKfX6hyCN3ZhvbNiMxYvajZEqpyQytDojtHXPJ3V/nV9cg0aj2i/JVixGPwebUFLBkn00HdSeSFIfW/lY8nEkK+ojTspvcaDMe1g5Z+CUF/E8GDK+IqJGcS0z01DiH+leJHWNpau0OJOlszh+kFL57EDqJ7jslzSmSG7Sr0sogmDLEZZpDBjqIGl9NI9eTF5NIWmBp7F8BSeID1HXZo9jk8dI0gqL+bbnLzgCSCEJjAtY25XoDTgMWcIzSmQvpW+4e9ywmrnJmST5NRoyDGOaD675A/LwUJ4Vw1bedHgVN1x6WknzRiwjFeAsMyIcfyswIFEp5Y3TnzyPL10hZyoGKOz0+lAURQwGo2u8MXx8TG8/vrr8JOf/AQAAPr9fnW/VZ0+lwy0FyH4lANWX1NeiG0Pie/4OMV3vL1T5BNdjBHKA9+hwzwcDgEAYLFYwHw+h9VqBYPBYGtS6OHDh+I9hNSYv+k8g3fP06C+t61xwhrbGQPE1j0p2Da4MyMmEM6DA1bAzYuQDLvp/UfBHVcrcJKzTIDtID7CE5CheVhlYJrFYgHtdht6vV61y4cfnRvSe6ntIY1zHP84GUT1fVleBtnb7TYcHh7C7du34Y033oBPPvkEHj58CBcXF7BcLquxUJe+mHp4bDCafrFYwHQ6hbIsodvtXpl4lr7x2rKIwWAAe3t7VRs+fvy4krvYlpgflyeSfRZro0vt30Sf5JIrq9UK2u121S44qaHZztgn2F6cV/EZ6gM84UDLS6ub51nMe5qG2wZFUcDR0RH0+/1q8dTZ2dkV+m7fvg0vv/wy/O7v/i4cHR3BD37wA3j48CH84Ac/qLUr20t/an54NDjqzL29vapv6JjIVR7F3bt34ZVXXoG/+Zu/gYcPH0K/34dut1uVqwUxm0RTsjFkJ0oyJoUWPOkLbUlckKP1oyYr6rSzl29i6hfaTctlDYJei4N8TmW7ZUt65ShP7/Xhb4LMD7VpSOZLyF0v7md5yq8zlnLqZJqH5Fsg/8XWUcqP65miyHdceQjIR1pMx6qbZFt7bT+pLImuz/D3G5bdmCLzpXxidUUsvVp+krzT8gmNLXUyNoZQaeBLzpC2csQb+NXKp8/RibbAjX/8u67gyCV4QspMai/NqJPqan3HvwnRGHrGywo5elzQc7qsARvjoHuDSdo7r6LxCpdYQzGG10LjUwtyaGNZy1vqG+rc0fwsIcbzblqhe4J7miDH39qx3qGgssTjHucRv7VkXZPtFisbEKnHf6JjxwOjsfA4a1qfa7or1F/Su5C88chKnl7iTap3ceW2FPSi/1tyUpIVVp1DfUR3PQyHQ/jiF78IR0dHsLe3V028IhaLBaxWq2p3R4oTVQe7LCsEq1/4cyvgI+2QwXf0uDOpPI8ul+S+ty4eoxnp4AGjkOGNd8Ni/aWjAfEfzYsHnnLyRA4bFOvT7/dN21mSIfQdPVorVg7xa0hibaBQuZxe+m2Mc2aV4XXcbhpCciEkz73t59VzKWlS7ftY5Az8pZQjySrNH7P8Ivo/TYO6P1dQEOWidDqFpX/owoG7d+9Cr9eDs7Mz8ZoSy5aiMpfni+nr9mkOXtDsPMvnovXyyL/QuA59y9tOopO2Z90xHfrG0+5lWVYLHIbDIZTl5UKCt956C77yla/A0dGRmD9OMl43NL5AO0SyyTnQzompD027XC7h0aNHcPv2bXj99dfhnXfegaIo4OOPP4Znz55Bp9OBbrd75R7JXSMUX/LYBlIeMXYzt/VC32C62WxW2ZJF8Xw3unYnd4hGC9K4pzZYCiybSJNbPB1OCNMYBZU7MeV6bXqaj+W7p/J0yBaOzbeOjYn8GWtja9BshBj7UYOWh9VH3ryl/Kx0UptLvFGHRxCod/AfLszQTqXQ6q/pZi0N/vb2leV3chsAxy8/WU2SN5YsiuH5ujLSi5tgI1i46fRxhMYz5S2A8OYtLX/Kv/xvCdrYsMYA/z6Ub4reuTIZG9MIMSjLUgzuac5KKjwGLU8f05G5UcfoDKWxhG8MPdbzFLpCShjTeJRJbH/lcrZTjN06ZddxdFPpqNtW+D1dqRlybGINMg8vh+oR006aEKdlWYERKzCnjQP+rq7BWAcpZVrGnpUvNWRxzNH7D2NoqcNXFv0h/pMCW7T/LKcgFESV+EySS/iMr7LF7617P6X6pcgiDXRyfTgcwm/91m9Vxxl2u92tsnA3B223FCfYg5x1zIVcQQSvA03vEbKONbTKknhY4rGYoK9Vntf2oztQAKAKlOECAHSauT2ptQelJxWp33KHptVqbe1ySYE36CsFg6jOsuSu5WRrARutbGvXplZGLOrYIrlBdWNMevqbguseqndD+ebyDbztW1c2U77O5XNivpYd580jlIY/CwXmPMihR7UxSu21W7duweHhIfziF7+A6XQaRQe3XSQZTHdiSQjZcjn0vmXzUVj2fmxf8PEbqgedjKWyQYuf5Bp7FkJtVZZltWO41+tVE4ZvvvkmfO1rX4Pz83MYj8db4xB9BmuHYm76Y9KijsbFYZ586tgey+USnjx5Ant7e3B4eAi/8Ru/AcfHx3B+fg7379+Hw8PDyia/Dv9SgzSWvLafxU/ePtPGhOSzYT/u7e1VC4jr8p8nFkbTSbHMWPvFwwNWjITvAkKZw9tBoy9HLJbK05jxyWnyxGRTxj+lUaJbA/fzNf7wjAWrPil9IPlLVplSjMvTlpY9G/MtlhereyWfmsZUcDEGwPNTEGLs9tjxF+orSW7Sbzztb9kNvL+b1B+e8Wh9Q3+n0JlqK15HmTcFvM70fnNuz8eO/xifKzRmPLFinrc2nrx6G0DZGSsJi5xOiidN00z3IjN1LGKYMgVSYDw2UMQDijz/WEXN87d+W8hhEFr0eIylOoh1qrwCnxtq0g4i7z2xUkAX80lFarvGGtTaEVi8j1OMKm/6TyOQH/CoTBrQ22w2sFgsRP7SnBANNFhIy9UWEFBe9TplmrMVolNaRRwTqJGcKe4cW+BtkxvYhrxOw+EQXnrpJej1etDr9eD27dvw2muvVXeB0e/rlv9ptAPq1gnHG8p0bZxpEw8e2iR5l5qXBMrvdByt12uYz+fqJDHylzWJvAvZW9fZQ8ef33OpOd8a6PGhqcEZCk6DtLAmpZ3rjOVPuy71gLc51XPaMYWWrc/TWsdk077DY8OpjqVXptSdJNDykejibcCDhhhIODs7gzt37sDnPve5KsAwn89hMplUi8donWLs45zQxi+1ryS5TnnCWrgVY3PhLka8l9sbCOTg/oMlB7z2WhOIDfIWRQHL5RIuLi6u3B+r2Y7c/5bst+l0Wh2Ju1qtRHuK9i/neY3WnMAjXgEuj3w9PDyEz33uczCdTrfopWi32/B7v/d7cHBwAF//+tfho48+gm63Cw8fPoQPP/xwa1Ls0yzvJT6jxw7PZjN49913YTQaQVmW8NJLL8GdO3fgtddeg9FoBGdnZzCbzW60XcxlhTfWxHWbJONC/EzLQllIj5um6Siv8lNX6sIz7nLwObfNvfFZWk8eI4mxIamujA1uU1pSJ2JwcSP/Nlc8SQvaa7TF9kNdNBGPlGIolp0ZC6vdKHDM1hlHlLdWqxUURbG16YwDT3ijC4FDqGvzeqGNLT5+Wq0WDAaD6tlms9mSdZLtn6NfNZqbinHfNLyINMcAr7SiqKMvY/2AVL7QZLHlb0jvtiZjpSCIZPiEiLLehwimFYtpmJBjLT2/qcxtGQq5DCxrIiP0TMsT4TWOJXAe5IEC+s5LS+w3oXdeA9CiR8rPw8MpkBwWKzDraSs6PrmDi0EAbnBpCAXzJLnTRGAl9C2lQwpacbpi+1MyLG6qjIoFDepobaR9Q1dP4T967ymH5kDSZ/S3J9DEaU41EGLlYmz/S3X1yEBp/En9FVN+CHyMY2Cj3W7D8fFxtZq83+/DwcEBnJ+fq8G4GMTKOk+66wY3CDW+tXQZzaPdblcTCNK3NNAbw6M5bLPYvuBtoN2Xp43rphxJCzkCElLg2WOD8zwkemibaLwWgxi9kFpGrjw0p82jR0J57BqajsPfdPxrNGs2LbcP6TOuk/D9er2ujiKliyhi+DYVks2pBT9pkBgnL46OjmA8HsNkMqkCC1bwkdZL0rPSmPMGnySZJY1XTMcXpGn01NGHVLYOBoOtu2o9i75CYyjWvuF5pugVzicazd68aZ4Y5JT8N5rWKouPQ9R93W4XAODKcbQ0jxA/as9iYMnSorg84nUwGMCdO3dgMplAv9/fWpyBaLVa8NZbb8Err7wCBwcHcPv2bfj+978PRVHAhx9+WKXhY7Ap+r1I+TaGp7jcWa1W8PTpUyiKAg4ODmBvbw/29vbg4OAAjo6O4PT0FBaLBfR6PZfOiq2/Z6x4UFcX5LIv6diiOovKdLxaRbuLW5PTfBzTfpTkTmzsgY95KY86tq/md4booemonK7jh9LvrfGjtWGKXy3pGEzvbRNPfhp9Fm1Svlba2PaX8gvpqlSk5oH9EHOnuNUOND+pjwGe6yC64Ih+nwN1bBr+vYZut1vF5dbrNSwWi+AY0eSaBa9+8coUKW0qL3ry1pAynj6NoDJZarvYO7MpYvReiMaYtBYNFq+Jk7EWUo1Hq2FiBqeFXa80lpBqnIe+SREYIeVaF5pRUFfIaEZQjvw0aAGRFKPpJsA6w3/XsHZFNI1QfaXjXjxGJF0kAADV0UShMUfzkuQVf2atsrtpwKAedRw1GcF3UuP/kuPiletSP2nvJZqk3zHfcCOY0k2PCg45gSF4v/eO9+sYn4PBoDpKdbPZwNHREfR6Pfjggw9gs9nAb/zGb8D7778PP/7xj6sAkRSEy4FdysQcAcCUMnngwKKh3W5XRyzhLnSen6dMjtz1rtNv0kkOWp5FUVSTKzfZgUp1Wmk70N0eZVlG6R+pDW9ye6Uilo9zT2JwOWiN6euQN58hDTzYiv1K7+WS7nKmfYy7/6SdlDQfHOM5QXdCIXASYrFYwGw2g8lkAsPhEPb29mC9XsOTJ0/E3UcSQhM51O6k9hb9Pies4LgEaZxK49g7ZqVTBWJOMul0OnBwcADT6RSm0+mW7V5nEiYGmr3f7/eh0+mo+ofS1+/3YX9/HwCe7wJ96aWX4F//638NP/3pT6HVasG9e/fg/fffh263C51OJxvvv0iyFW26VqsFk8kEfvKTn8DJyQl8+ctf3koXIyNy6EILmt2qxWswrdcWovxu+ZkSn3KcnJzA/v4+lGUJvV4PvvKVr8BsNoP//b//d3DiLhVcF+TOGyA8+ReCtkPWWz5F6uRs6HSP0LcAz2UrvTezzkQgwhtHlSa0YnySXcYy65QVO1lSB5Y9QWnh76Td0tzWmM/n0G63odvtVryPC8xRt/EdsnXmQjQ/o67fSscaloH2wnA4hHa7DQcHB9WkLIKPD+nES0/Zdd7vEin99Wn0j73Aq6DoQiWOoiiqayZyze+lXHPHaZK+98g8q0zxmGJtNldDqFKSw+FNH0LKhKP1vO7gyCFAU5VrDB25hFjKJHFMnb3GV8jI8n7rMdZi+TmUJmZMpPBnriByjCErOR/cSQoZPE0jpgxJJlL6JUEcEs5eQ0lrn+tQ5BoPSI6OlpbXWXIypO+9dbYcda0fpQmrUD280IJ2XmM0JButibaYYDxPW4e/QjyLx8bhIgYMFI1GIxiNRjCbzeDs7AwePHgAi8Viy2nJyfe7HkMpcq0ujSnf47jIKYe5E0uPz+PQ5ICm12Mcu9igd1O6qI5Ot+DRy3XrVGcCMKXPciDULimBmVD+sb5MarDam6aJOqZACn7ftOAEpYcGvSV9joEoeppADLR80S6R/CBJz3OZHdLDUr2kdN46SGVhoGW5XG4t8qATbbwszTaW2kKjnf5v6ZncOk4LrqYi1d6U7EJ8h5MTeD9oDC1aW+esKx7rijvlMSi3t7dX2YMoP8bjMTx9+hQuLi5gOBxCq9WCvb09ePvtt2E+n8Obb74J4/F4a1KlyeOKdzXpIY3bUJwDx+N6vYbpdArj8Rim0+mVa1l2iVAcQBpPMbJKy5/LES4XY+lEn6bdbsNwOIS7d+/CdDqtTgFAWRWyf7RYgtYOktzUaA9Bkh0e20ii2ypDaudcMcum4m1a/CqUr+azaM9C9eS8mgu7an+aVw7ZyPvK03ccMW2p8YAUR6L1RJtXi09Z8i+lnaSxqfGsp+9pvajvjrq5LOVrZ64TMe0mtVds/Cy27LqxxdzIbQ97ypPsfIQ2VjSE0lLZoOkuTz/WjQlIeYqTsbkgOYYhgykFORg4R3CqCSbe5cCoA6tvveD3HdbdFajRJAUtOPixXXXhGeAhw9cDvmLLu5okNRAorZyts4Il1C91EMrb48Sg08XvzuJBMquskBGGZaDx5qGd0nEdQJq1MecJUFnjjN69lhJI8bZLKJhalvL9yLF0xBhqdCWuRZc3v1D6JvUY/o9BNpyExWd4hNrf/u3fwsOHD2GxWMDp6elWMK0umnJmbwp2qdc8tCDvdrtdGAwGcHBwAP1+Hz755BOYzWbufAB8gQ0OTxp+LyX99ibwiUe2xwR0QrAC+rnaQws6arQ0iZQycuvbJuop2Sb4XNJhnsC3RDf+s3ZSWYEHlEsYzKb6Tgu2pYC3Bw0eUXsW7ZlOp1PZHPhcarfz83P4xS9+AfP5HObz+VY+PC1tD36PJZYLcHVXZ8rY88gMvOsM20PjB+176VmsTYR9zu9epGXwnb38vda+NF8pQNtEUFvy5VL5lgesvH0D8Hx3Ck669no9ALicwMRdOzE7AneJzWYDH3/8Mezv78Pv/M7vwMnJCXzrW9+C999/H/7iL/6iGmP/5b/8F/izP/szGI1G8PWvfx2++c1vwv7+fuWn/ct/+S+hKAr4/ve/X9WZTz6+KHEWjlh/luPjjz+Gv/iLv4DRaLRlG87nc2i1WhW/hHbIeiY7crYxPx0CZZbHR9BkMaWV/m8FYwGe66yzszM4PT2FV199FYbDIQBc7tr+whe+AE+fPoVf//rX1Tjmi1GaQuyJAzT4bV1lhv83ff8tvgvJPK/dwvvWAo/j4DeoS6S25fEDyW6WbGtNT2j+Du0nKX+ah2ZHNQVJp/L60fshQzEjjTe88QyanssIqWwtpuO1g3kcW8ub96GVL9JK6Y6Rpzn8folWjN/kPmXlpujkJn2ym5YXgK++uctcrVawXq+rHeQIjHuu1+vKlsPdtLtGiu8p6QFLHwcnY2MFXh3EBJtSvq+TdwgeQS2V5zH4eNqUQHqqIg4xYa6gvpZP0waEh0atHySkBoy4UWD1Y1NtsWsFmDPwKhmzMcFpb1puqFn9ZjkDTeA6jBfunFnQjElP3+A/elyFt77WWKI0eBw9iX5ehuQUxQblOJ/VCepJMilmjGiOVUr703J5vhg4XC6XMJ/Pq92wUmA+Fry/vOMwZbzmlGupCPWvl9dpPtpY4WXge8kRxQDfcDiEXq9X7ZbyHlVZV/+FxlGqzRLbv5yO3LatFdyQHHMp6BQKZlk0xQS96Dep9lOTCNmmTZcda0fETtpIfGwF0ySEaJSCkZKcoL81GeKph/ZNyLbzlsnLwp2x0m7/OjatFdjVaKVjWTvaX/JHQ76jBS1ozMf0eDyGTqcDk8lk60g7yY6W6h4rT0J9WUfexI7NlLKp7WvlFaKTf6flK31bhy88tEm6EG2+1WoFs9kMHj58CMfHx3B4eAjHx8dw586d6hQV3Nn59OlTePbsWWVLdDqd6ghjnFSUaM8RgNR0OJd1N0G/IQ3tdhum0yk8fPiwsgkGg0F1D3bMWEvRD570Hhq4vojV19K4lHxV/F/TX8izuCBnMBgAAECv14N+v1/t6l4sFlf8Owtc7nvT8vpp8lTKV4pzSM8lve6xTbwI+SOSTozN04LXV+f5arIe/w7pghhflfJhrH6UaM0BSX/X9bdiywyl0/jVGkP8b54vz68stxeY0WsmAEBceBfK06LByoNCq3NM/hirwYVedIEJtS9C8qAOX+zaN7PqYo15mibWn+PpYm2X3GiiTC7HeR1j7VZPOdJvzzf87xSZK6HRnbGhwgHqd2xqp2jfxjipu4R3xd8uwMtJoS0k1HLXxeMs0DR1V/qE+EYyjC3atG95OsvIyIFUIaRBM/JzQTJuNRpCkFay0pWQMfc1aeA7NW46iqK4snNTa2uuSEOBHn4nb1EU5tGmVtlSOs/KZMnZ9hr8WIdYRS/9pnnS7zRodyFL+VKes/ouRJ8ELR0G2trtdsU/GtrtNvR6vVpHyzVpnMcGRq/bgNbakI9PepxfbBDIkoXD4RCOjo7g/PwcAABOT09hs9lsOXJWPYrCtwOCg9LEjfsY1NWv121P0sAAfRZ7L67GyzG2DSJ2bMcGnl5UxNjNHNfpu2h8JAWiENIRbp7AQ+oYpt9x3cp3aFJdfh0rsykdVn1pUAyPxqTfUXutqaAOtQNxoU1RXB5l9+6770JZltDv96s02KbURuF8EAJ+S+89pXZmE6jTZryuUp6e8Sv5ezwPj3/Jj4wGuKofmoBm567X68rWn0wm8Ed/9Efw6quvwh/8wR/A5z//ebh79y788Ic/hP/3//5fNdH1+PFj+PDDD+H8/Bx6vR4cHByY5Wr3lHnp9H5Hy8q9gygWZXm5Ex7v+zs7O4PHjx/D66+/DicnJ/Dqq6/CcrmE999//8p9hruiL+QL5Yod8fFiyRvPWOx0LsOpT548gcViAa+99hq0Wi34m7/5Gzg4OIAvfOEL8OjRI7h37151pHETdo8kV7R0Vt0kv8wKlAPYscCQfynRTeU6BddnAFBdecPLSxm7Hj6U4JUp3hiCR3bTtqOxGB7fuG57WeIdyefgY4L+bcWL6P+W7pd4QuLzUJm4W09Kh3Var9cwmUyupMUdpRjXmM/n2eLATYLHws7Pz6Hb7cLx8fFWnAbrtFwuk6/u+AzPUUeWvQiQbGDkGW6bFkUh8lTT/q4UQ42NBVn2+NZkbI4JCy2vmAC6p6w6gzvW+KmL2MCRR6GEvvW8zyEgraCA9sxjTHvT0jRcgVuKWHKCtTItGmLecePfayh4ypP6lbeBZWBYZUrvJCciJX9pbMT0vYQYwajlG8PX/HmszIhxHm+iMrbGGr6nQTcJWrvT56HAkiXbLCdQSi99E6qn9XcogGrRTx1PiXaaRpoEk9rOks9aGTGI5VMM2m42G7i4uICPP/4YRqMRTCaTLSd7l0FBjlQHnQaWU+mPka0eGcXzRt7Bf1RXIU9Rx98LLBPzRCM6Vz9y/WGNIQ6Pfgzp6ro2VMz3Me0WCrRxUB618uNpNJtGS0P/9tIVQlOOfsgviXXEmkYqP8bUUxtfvI9z+mfXyQMW6BGwi8UCHj58WE0i4Y4BDFppdi4N0vEJSZqOg+sTT/BQQ4ys4wvuLPtdogt1PN+x6+m/XPKCp885jiWfyLq+g9PC8wjJY/w71r6l41XaPe2x55vQXbwN6ETLbDaDR48eQbfbhaIoto4Cp3KH1q3T6cDBwQEcHh7C4eEhFIUcyMsJWs/Yo7V3DaRtOp1WtOBCFHqU6C7h4T0JoXFs2S38f2vMamOF3r+MdA4GA3jnnXeqO47LsoR79+6JNHvkg8emlfKW3vPvLD9Cy4OfshKS6Vq757BbaJ955aOUn5cWKgepHon1GTzt5qWHliPZHbuUN1ZZtM5W3T1xEF5eyE6VdH6K7UxtNcle4+VSuqjO5e/od3SBIi9boofSYOmYUH1j9FNZlrBYLKDdbm9dO4X6g1/zweuj0eHlH4t+b35a2lgbJ1Re6ti+bjuhKXBbiYNfm0JtKa29Y3yiHP0s5S/RFiqj8Z2xn6EerHsbYhASXjFMGTLuLKPOAg66ujsMQ4attSOuKefDG2ywgjChiSwtbc46YV45dwc0qWhieY/+DtUxVxt4DR6aNmRINSEjLLos0DEdWhkuGehYToyTxPPT0mnjRDIW6wRQtNWeWn3xG3znKZOvvuSOaQr9ocBGjrGLxvx6vYbZbAYXFxfwySefVPl3Op0s94fXpTX1e8n5y4VcsrMsLycV8Gg1CmmXdUz5NBC5WCyS5Kan/7R2thYo8LSYjwdYHj96qil4A0pSQCplzNP/6d855EfoxAAPYnY2vcjw8j7+7w1EWEEaGmTSgkYU0p3LTdnS1w1sI7Rler0ejEYjePr0aZWm2+1Cp9PZGjtSALDVakG3262ODOUnf2AaaSw2KW+47YFjDZ/T8UvpofyCaevKRqm+ockDD3igJ3XXIqfFChTlQqxs5/TgogFc6OaRL3yyIyW46QXSi2NosVjAdDqFX/ziF7C/vw/Hx8cwnU6D+QwGA3j55ZfhlVdegVdeeQVOT09hNBpF67BY+jEQPZ/PKxv2JqLT6UCn04Hz83N48uQJAFz2Vb/fr+4Tvg453uQEEpeh3LegeixUf25ndjodmM1m1bPDw0P4/d///WqBznq9hh/+8IdbZRVF3J2Lqe2BOkvzJyV5GoqDSDpA+q094+VoddMmpSikHbISH3ntaO37OtAm3eh7T6yDT+bx/LE9Y0832IUfA7C9SAXAnqyo0wfcdpXysMq2gOMJd7kj+ElxEjzx2VC9Q3yktZU0JmNB89hsNjCZTKDX61V6DnUeykNqT+a4K3sXPPoZbOSOM6NNyn1Ovvu83W67746VdEYOe0aKL9XN13VnrLeQ1EEeW4lUwWl1ShODOyYoYTG2RmddZ4jT5w3iaGkt+rRVa6FyLFh0h2iSytMmL6yyvc+lNFJa70XrddqrCV7PodS1dzF5e74J5adNXuHv0GIB+h3n91CQxjOuYvg9BnWcPC1QxscebcuYIFIuGSq1o9bfFriSt77htEvOk8d4pgYtdy40ntQMdYCrAdU6ejVFh282G1gul+I4ofSlOksUoT7V+j5FJ+awfUL8pOnzGD7kwKOjOU9oAQSLX6Wy+v0+DIdDWK1WVVB0Mpm4J9SoYY72BA1ixfZxqG+1/ozR8Rr/5uDpVKTIO09+Wv6hMri+5O+sAN+nBd7gCU+fqy1CchzldazPIfUnz0fSZ1TnSXW19JqHLsyLyg9ut0gBPK6naHo++RpDkwcxckda7MDrLPl/WhvnmJShdoo0eS+l58cYSvRp9aT8J/UZ5ls3OKjpYm9/URo0vo4Z7xrfSrR67HD8ttfrbe1yiUHI79F0MpaFgV0JnU4Her0ePHv2DO7fvw8PHjyojuG1FjAXhe/I8Rz6EY/8xftsc1xpkwouWwEu5UWv10s6mtjry+WAZZ+lBF2pPuLySLNFQrwMADCfz+F//a//BXfu3IG33noL9vb24OTkpHqPi24GgwG02233Hb0e/xb/1nhb019a/lqc0PJ/JZ4I8UjID5ag2ZixsSOPrSrRxp/RiTguXzztEVP/UN0t2X9dNrTGe9z24mmt76V30nsJVh68LK9vjWnpySiok9H24jrAe3oGp9tKb8UJcrQtTUPvd8d6dDodaLVaMBgMqklZOjao3aXRk5NPU/iDI0aufJr9VYA8ceZQn5RluRWTQr8GT6Hgk7Jav9B+8+hwK+bjsTOseJlGY/CY4hgDIdaIyx1IsN7T/5umx6LBU34OOj2CJxTciIGl6HlwJQQpACfl6+lXLDvGwOE0xg5Eqe2lQIeWzksnp8kSIBqsQEjouxSeoQpKcwC8is6ikeYvGVgSHTSNZFx5Ajv8mZQf/9aDVBkbAw+NmlyxDD/adtJ9ZVp5McYp/SZmvEtleZxP/B2qh2dMSrKB54X0ae2v8TFC2t3Ix4h313Jd0DKXy6Wapii2dxSkypwY1DWyU+nL4UhKAb6iKK6s3NNoxMlxGjC3aJRosegeDAZwcnICs9kMJpMJnJ+fw3w+r77zyDiaju4iC9k3nnpwGmg6PoFh5S/RoAViQvxSxyGto1+90GS0FXSR0lEbqQ59Xt3Cy+ffNdlmFm0h+lMCNl5o+VLdQ/WG1p6hccd5I+TMhvRjim1F73/lZUj+SsjOw+Aa3Y1SJxij1cnqe/RdMFDB/yGk3VFan4RsWC+wPOlOV54Oy0ux43g+WBbvRywjBRqPc58ixpagtPJ3/FlIvmkTAJ6xKT0vy8vAarfbFW02r+3CZb30jeZnWZOx3W63moztdrtw//596HQ68Oqrr6ptkXOhXwi8zehVEBJfxuad+i21nzBwLu048cQCcsBr/1FI4yUWfJeedeKGZNdImM1m8Kd/+qdw69Yt+Pa3vw2vvPLKlcnY9XoN+/v70O12t3bTpkCyXaiM1U634fohZBtb/Kr5DbkQ0lExPkAIGp9Z7YP/x9hpvN1T5JGlD4piN7uuvZD6itdXWmhL06G9ZcVlpO8sOih/W7qQ5y/1H68Hjge8M7wsy0qXSnWWxmIOW1LKNzTuQ/YmxWq12qIfZepgMIDNZlOdfoYLkrj+1frJqrPXj/bWzeun57AjbyLq2BOp5SGkNsXxg+/wlDz8p50QIJVj+TzWsxDNKf6nBNfZKTeJ2XJVPCdCDMwVc4jeXA7wdYIyfkz/hNoxNi+uOGMc/JBy8ASF6HPLcaAro738ARCeEPA6yjHI4QCllEVRx3kNvfMEvDQaaGBPy89zNEsM3TcNtF7YRvz4vdR8ER4DO4QYYzcn7aH8kIdCgbeQfK0TzNTgkU2pZeJuBjrGXmTQ4DN9FhNs8kAKSmi2Q4ivLMQspKJBIHRCtaAAvwMO64D/WwZvHafIcqo938XYdFIeMWU1CRxv0upsKosoPalOcO7gt6RnU/g7ZHfuCiF7jqbh/9OV+PhcCszSPOl7SV+k2NzS7+tsw10HGCzw/uCnXeA4ROAznHDCoy61oCEPYmo78IqiqIIZ3mCt5j95xrTGwzEykC8qwnIlGYCr6XMciafRo0HzC6leTrHNKB9Leo8GqDUgv9EdxzF2c7vdhn6/X/3GawekvsmN+XwOH374IbTbbej1elCWJbz22mswm83gyZMn8Morr1z5pizL6tjSTqcDx8fHsLe3B8+ePYP5fF5N8KYeUU1B7b2UMZFaZk50Op2tBSrXgZAOrGuD4LdW/EbTnZYuwckIPLaU8lSv14Nbt27Bt771LXjjjTfgpz/9Kfz617+Gi4sLWCwW0fRrddLqMhgMrkwyL5fLrSN9Y8vAcnLxYEw+VlxoOBxWv1erlbrgl3/H7SNODz2in6ax/FWLhzyxhhxty+3667SFaHugnNFO8bBiJjF14XLCOvJak9sWn1u0aHzqPWaVl8/pCMlAjTZJVki8GhsPo+n5qTOh45vr+PKfIQ+uUzbw2BT+TX0Y7bo3hCTHNXkrpeV55myPkE1Y6yKLFGOoTuViykkZzKmdEBuA0BxT+j62TXdZXy09VxKxZ2l7hHFMXnwAagpYU74aYtKHjH5OF+/7FIddgycAUDcoGTMWLGPLW56Hhpi0HqNGEtpSeosnYg2PJgKKueUpGlz0t9dZCZUXw5eetrIUOi8zl2FIgzRe3rFos+7ypOk8dMWMPStPT3la3xZFUa0YxePwPGPNixie4t+kyEVJ/9AxIeXDdUEMQnnz/62xF+NY8vSSUY2BKnxH2xUD5hgY8hwDLwWlOY11dJ2UVnJaeT4pOjI3vMFgDikIG2uLUhpC70L5amNPC0jE0nHT4OFdzb5CfcAXinHHlpdDf1vfSGVK416jPdVHkejkf0t5W/zh4TutbnUhBQY5jVK70wkoaYxiepwYozv5vfqJv5O+s3ZYWXlwmc/9ndAYD5XB2w93aODR0Va+uaD5Bpper0OHJUM9Poz0vadt8F5MxGKxCMoT/Ftqm5ixuVwuYTweV2leeukluHv3LkwmExiPx5XNiHfhLpfLasKpKC4naXq9HqzXazg9PYXVaiUuBKsDyS4H2B43N1kn0Z3rORDjk0h8keLbWbB0F/qv9DnVqZLtTNPyexHxN90N99Zbb8EXvvAFaLVasFqtYLFYVPcK16mPVj+kudvtirKbXhMj5Rcqj+fnhWXD1eE99CFpfnQyVpKBFHxxlESXZEtp+krz9y1fTUoXSoPlSaC2wU2SPbRdtEVTWhtKfWDpRN7WVruG/HOtLjFyU4qHYV0t31biS68Olez8UNtZvBUqh0+cSTvnU33opmDZytr7mzSmPg3Q5CWAflc2gM1D3rSxvGfFKKTn2jOAmpOxn3Z4DT8rXezqFw1eOuoitc4hx5kbOXXKxrSxeVgKBY8X24Ui4Iagpfw1R/664DF8LFgBoCYhBYM8Qt2TbwjXVefrQNP8WddRiy3LExDUvk0tF0GPUsR8NENdKldDu93eWsHf6XTMnYtWeVL53NBJaffrlnNIB/3bUw+6610y0GLrFmv8UxlHV6J6yi1L+X4OrfzYPhqNRlfsIdyhI0HaqUmPlIsNHNFjRKX+4Y6/Zi94eYHrdRqozXFSgFUuPWnD6ntKk3YsdVP6S2ofXjb+nUN2p+C65BDyN8pmz+6OOkA5wQM19Nhb615LnpcU9KaLM+k4orpIy48Hu2J9BS6zcLKOjw8pyIqQfBoqZ2k6Xh/eDlw2Nw3afnS8428tcMb7yhv4C4HzhEQrP2LWun9PQrvdhqOjo6qM8XgMFxcX1ZGsiFwTcV7wewUlncMDryG7j4LyrjYJyHmXTjhx3xT5YzweV8fDee48lfJK5Rccr/v7+9XddLysx48fw3q9hv/5P/8nHB0dwR//8R/D4eEhvPbaa/Dmm2/Cv/23/xb+5E/+BL73ve8l0eClE2UmYjgcVhNwm80Gnjx5Ag8ePKiO3LsJp8Dw3faSjSfJ3Fx6Ngap7RXymax64BiUfDEt/f7+Pmw2G/i///f/wve+9z34kz/5E/jWt74F/+Jf/Iuq3NFoBOfn59Ux4Fg/j0yyyqcyfrPZwGg0gk6nA3t7e1v1LsvnC1e89xjTPudHPGNbxt6BqSE2FrPZbGA+n0O73a4mZelYRLvbigUi3dpmEsl/CNGpnTqj2bxN4Drvqbag2UGeY6+1seuJv/CxTP9J9ITiGnTnNNXX9MSa0O5QL3LGSKherqOjeb1Wq1V1SgDA9qkLlu8n/ebl/H2Jp/59B+onBOUngKt6mT7zjBFu51j+Xw5YfKtOxlrExAwEr7KRHOYUpHyr1cdbz5CR51EMPJ23HK/w9BicnvdSuhijJLaPvXRr5XvbO0a5edKltLNV19DYiaUvhp46sILAMYjhG0nRh+7B8Rh1+DtEuyYzNRnHg951Za6HplQ5EArQ0zpqfVZHBnqhBQ9jytLkrfXeC24Aa2msdFbghIOmxWAw/e0ZpyGekNLEBm9yOO9NO7UhmyZn0For1yODYu00aq+k8HboGwyi4uQOLzeUVw4HzJNHDl1ojWk6weGlyYIkky3+k+RxSF+GyqbfNoE646fOWMwtT2LGFu1LyjdWH1vyKLYenCe4jIjJRwtsptgGsXJKG898HHJ6tPHJ24HTQYN6Gp1SHpw+HlDAiTAMUFAbxxPk1Z5LeVg2Rc4xoeXFafH6uLwfut1uNXk4n8+DfV5XfnlsJOu9pRc9cswaI9L39J5MTS/h/3hyBQbDYuSYt/70b8qDWA+60xl3iw+Hw2pX7Gw2g48//hiePHkCnU4H7t69C/v7+/DSSy/Byy+/DPv7++pCqBjZbKXleXc6HTg5OakmY3FRmjZ+Y1HHjgjZslpa7Zl3nMamyW1nhNpckt1e/qC8ipPvyMO/8Ru/AdPptBpLuDuW3vNNTzZIrRvSgaDHEeP70P3dofxpfTWbMtRmGk9ZPoGk6+g7bDucjPXaHhxef5rT4qFbK6Mp39XLt960OVGW8tVfHh+XptPaTrN78Fup3t4YhdWnki4P+b3S9yEaPLDagJZVl/+kaze4X1p3UUDsGEuBZfvzNJ827FIOaP3GbT9Km6UbKDSfiuefI94TA61dzZ2xuQmKVcrXheuiJaa9+XGgljPeJKQgBBe2OYKN3gFI6dB+Y551gcZsjOHsXVEMYNdP+0b7nWvsxdC0a0hGN98FwEEDEB5nsw6sABy+4+VpOwfq0pED/I46/o47ZDnGXGpgjr+T/vZ8izRowUsPcHXXZrOpnGTeVilOcgg82MaPc8oZZE1BWZaN7wCz4NERNJABYO+q4XIlNMEl6Skrb+tZKB9POfR+OfxGC7xoQQm6+0rTfRLd2hhEWmLv3OH0pn5Xd0zyMRhKG0srTe/dES3d1dsUND1ngfPHrmVWzjJCegXHCE4cYVC33+9XR29SO7ssy62jQvlxtjQdQN6Jp8+QB6vVSrRB8e/VagW9Xg9u375dnWDx8ccfw3w+h36/v7Wz1TrGy4IncC59Q/8P2c8huysGEj/jb/psvV5Dp9OBwWAA4/G4VpkhekL2phb0tfQ+70spHd9BxANZqH8lurB9cOJos9nAcrk079UDuJQznU4HptOpSK9GKy0b/08Nzt69exe+9rWvVVcgjMdjmM1m8NFHH0Gn04F33nmnSvvee+/Bd7/7Xfi7v/s7ePLkSXUPc2i3dYpfKOkovGd3Pp8n1dVCE3L8RQ80e+gP2ZoA23Y+9XUtnqVlt9ttODw8hOVyCZPJBD788EP40z/9U3j48GEl9wEAZrMZtFqt6r7TXJMLuHiB+pm4axSPHJ9Op7BYLLb4NqZcj5+dCk1mWvnjc/Sz8XQRLUaq6RKNBitmYPESb1+v3vS8q6O/bwIk2rl/Z+22DtU9VFcphp6KOrteQ7FfmiYlb+rLhGSeJ15h0USPaeffNRGPzwnPOLrOeFnMWI+VCzfZL0QblQJ9ZACbj3g8i+dBQeNMTYHT2rFeelGn827qIPQYGKnB/rqQaPSWE0s/TxNyGiWDjreHRWtsAIHTwAWPJIik/ok1PqWytfy0Nox1GELPQ/l5HWUvbTwvzUik6WJ4tk4gmhscWt1TguuaMeihy8ozJVDtyTcWnokc72RPDrlUtz28zhumDY25unS0Wq0qEERXLKfC42hYkAwcil0bZ3V1ptfJjqHHIzs940Kj0TueYt5T2Se1QSi4kIMuKZCBxzVZ9Eg0S7ouVLeieL7LM1QnKUCD6SXZHNJ3Ur4xiP2Gt4lUd2/eHkezrr+g2VGpgVRNh8Tqa6u8JsDpRpsE/0l3ffE2pLLOao+YMVCnDiGExpM2piXZoNFs8QMvN8Xv0OxeTXZosO4kpL/p7ineflKbcFpC9eHlWfC0mdWfll/WxFiTdLNUXioNXl1gpdfS1JFVIT6g9fXYLyiP6P9SuVIZmMbiBY+tCnAZLFutVjAej+Hg4AAODg6qhQrj8RharRaMRiN4+vQpfPTRR3B2dgaPHj2q7oq16KU05eBFzAcnvXu9XlXGro8p/7QgxHeczyxZrY3dmBiJlHa9XleTgQAAvV4PNpsNnJ+fVwuoBoMB7O3twWw2S4pFeMD5GP/GSVlpgYDHh7J0ap24gyWrPDKG50kn8ShfxOrGkOySnnntWy32k2KbaP6KRM91Qmojqx+t9/jOanfLp4xtZ4sGie6YbyVbPcZXD6WVZIFUttf31/x1Th/9LiRPUuHl91h9qPXNdSGG9ib0/k2yJ7j/60kvIUZWeuse42Nc2Rmbi8ma6qwmmSBnvjEz6paC53e+WAzTpDObCmwH71n5dfmPMz13qqS29qxkyuWYpSJkhHoMGm+eHoQCUlJZTbYfz1u7U9DaUS6lAfCtdNOMC3QGisJ/9GMIuQzHWGCZ6FxSaEFV6XgkmpdVjpdnJMcb252WK7W/N/gT+50GdICPj49huVzCYrEIToZ6Vypa4E4H5oVHu9VxImJwk3RTCDEBZ16vnGOd5x8r35sKblNoDhneR4UrDTmv464uDs2x9rQrPQa5LMvqCMOyLKvdAClBHes9TScFfnYBGlDHYHUOe6pukC2Up/S+TjCP//0iYRf8EgpIa4EsKz3fMUTTSAFGmpY+5zYZtZ+kvKz6Wf6QdW2FFmTn7SKdGMDtcc1fo2NVuq8d27TJ+ySRXn7vH6UTIY1ZLeAbU772DO1llNkcdWQS7yN+ykWuY/Skoxdj85DGmsafVh5UJ+E7zrPa9xSbzQYWiwWs1+vgcaq5ZBnlzeVyCa1WCw4ODuDXv/41vPfee/Ctb30LvvCFLwDAZR8+fvwYFosFzOdzWC6XcHFxAd1uF/r9vns85fCNOY6OjuD111+H8XgMP//5z2G1WsFqtap2KTYp+19UfVgHKXUuy+07VLU8pB3VfJzOZjPodrtwcnICe3t7cHx8DIeHh1vfvfXWW7BYLODHP/5xNSnKx3dokiNUT5Sh1I/Eo5EHg0E1TlAW1JV/1oQXImaiR4rjWeXRNK1WC3q9HqxWq0rXajTlhGdiivONx8e47kmY64aH11OQyg8ar4fy8pys5SnzJsb8ObgM85wu0DRucnu9CNh1+6Hs1+xVaVML6jz+jWQDcv+HPtf8lNxtEDymODXA0ZTQjA2ixeblNRI0GnIFqzxlarseYmE5Yyl97lVIXjpSvqcGjjWIc/CNNdHsMR5pWi89MZOvXofdKoPyQszEjcbLlrHqaTONBsmw1QIYHqM3lT+o8+ANHDUh4HmZlMc8dZTamU40eAOXsUjlWXzvDd56x5zUlxTWmKD92uv1oNPpwGKxqAwIlB30Dp860PpEMzi8eisXb3rbcFcTWTxAXlePS9/SvL0Ipff2R0rZFCk6ictA/iwVPJBuQTqml4+1WGjj2qI1FVIgPtX+lQL6WsBf+kYrj46XOrazB5J+aUqHe8D5O6Uc3m7Im7dv34ZOpwPdbhcWiwWMRiMA2F4IhfbmcDisZHtZllccUw9v7Er2ptoc0t+Y1sNXnN8tmm5KwJMHEKRJAmschGwS7TuOGPveoiGkgyyfJnaMYXq89gCvhaDXZ1wnYuqh8a4WxOeymZcr+WRF8fyoN3pVhDb2qMzX7mSMAZWlEo3U5wB4vohrsVhUtvT+/n71HZ46s1gsYDKZbB3nzRc55OAFSY4DPOe7p0+fwnK5rMbwb/7mb8LDhw/h/Pzcned1wmPrNO27htJ4xkgoXykv7jNbY4rbA5SvW60WTCYTuHfvHkwmk2ryFRcL0rwsXRVqE6tuuJjB8gli7H3L57f6g3/jsUGoLR/iNX7qFJ8AwkkwyQamclSLX0mw9C2Hlq+Hd7V8YnS5Rw/XHc8emZDiQ6XY2ylyJGeeCK8vbI2LWFqa8lFj+T1ki8Tk59U3uW09TV7eFD39IkPqq1yxIo+ckcZ/rHxNoQ9hTsb+fWKwXHVNWclsOUjWN02unI5tD+qUpeSTI3BJy8OgFqUpdD8Mhze4iCvkF4uFmMZjPGiONYW00jpVCedC7jJS85OM2LIsr9wVwsvi40wLSscGgXjQTKNV+lajIwdSDAc6trAtqPKSUOf4LS3oY7VljNMkjR8tvcchskANgna7DQcHB1AUBTx+/HjLAaeB1tzBIeyzdrstymjsq5DOuS7Ejr1U8ACFtGOI0uQNgFs8VIdmzDt0xDQf8x69Jo3BUAALYPtEAingE2qDUF/H2Dt4lxwP8q9Wq2qiC9+HeJ++T9nxRHkKvw3VNUamecBtCyloeNPRpHMUixztRW3IoihgOp1Ct9uFL3/5y3B0dAS3b9+Gjz76CP78z/8c+v0+DIdDWC6X1a6qoijg5Zdfhn6/D71eD0ajEXz00UfVUfh07IZsxSbaM3aMUH0ZylcLXMcGsl4ErNdrmM1mMBgMtnb2c7lCIdlOXB/soo28QdmUPLlNikA7ajQaQafTgeVyCcvlsjoqtMkdxyG6Oe34HACidQzX6ymyvCiK6q7pdrsN0+m0ki0htNvtKhZB66jRKIHXXbPzy7LcmkhCLBYLmE6n8Oabb8Jms4Ferwez2UzMAydv0RYOyUUvuF7CiSC0MX72s59Vab7xjW/AH/zBH8D/+B//A374wx+68rxuxPDhTULI9vSAx41CNre16+3evXvwd3/3dyLPUT8DJ/G1ybkUnt1sNtUR3mgbSHUJtVlMm3r9Dm6jW3XAvCT/gqabTCbV73a7XZ1KBXB5R68UD9QmjDQ5a9UF5ZWGuotYuF6n/OLRAU3rfs9409Jo7ZvT5vb473XK1PQgzc97Cl8oTmWVlwqrnVLy4rZN3fxuCm4SLS8qvG0oxWmsWFwM72K8j8t6SUfG2tee51nujI1B07PLHmhBriYHVSjQF/M8VxvGBgOlZ96gibRLRSsjBG4MWkaRx2DS6Ea0Wq2tI8kQ3AGltHBn2wP+vUZfyNCygrhW/4UM4lyTRKH8YgwOAP1IYi1PyWDl73l/xtBD+44a7qFgKKfDchCkenmMHV5OqA+oIorlZw0xfORJK/G7h07eL95vpLIlmaTl12q1YG9vr1q1bx1VQ3nQ4wRrdZHSr9frnd1ZlYpYXskRNLL0sSY/6+pKTT7R9962kHZOeejR9A/Pg/7znM4RExTIYYvheDo4OIB2uw39fh+WyyU8fvwYAKAKxFpBKOm3RX8oDw9/5EBsID41T6/+sOwvrU087c7HCB2b0viJkfE3BbE2ZFEUFb/T7wG2F0d5ZSBtx5BM0uyonAEdica6abz0SfJQs9VDtjO32Txts1gsqrsGu92uetd8Cn9b8ouni9FDqZBkAw2KWzJHkyshO53+rgukgQdaJR8xNl8KOr41Prb0Tqyupb5MjN0o1VfqRymdJN/X63V1VDKnj7YDLnDc29vb0vepp2KkgC7ow7pMJhN48uQJAAAcHh7CfD6vjn3eFV5EfVgXsXKL+4aU/+nY0XQe/sYjgLvdLvR6PTg4OIDZbLa1A50ulPX4pV5Zx+10ngbpKooClsululCFxzQ4nZL95UWMTuHle/1gKg+k8qVvNFp5GSF6pW95nlZ8SOtjXn/ejlLZls93k+DhpRTaPflqdh0+q2PThvzxWNs8pT5S2RJ9Unk54LU7Ps06qkm/6EWGd9zTNNI3dWxsPimbElPxpOd1NHfGxmT8oiIm2EERozhzIqS069Yl1WCw3gGEz8aXFJMHISVkTUx5HFd8jgYr/Y7eRcfz0PpCci5D9EmGOTWAeeAsBdr3mvHhcQI85cXQpkFyrrW+1QS2FkCz6qHxPP2OL0LwBLu0OnjbQQuUUH6x8qOBJO54hhAKRGqQHDz+TvpG67uQ0xQKKHmeh/hGqwPeeTWZTODs7Ewsx1MHiz7tKENKGwYJQmWHyvIgxvj3lBcjXzQnRAs6YPvQicZQAM+SmxpN1vtUp6gont/PGtLJnBbNgOV0890plm6XdnqH6pkKpAeDXEdHR3BwcAC3bt2C0WgET548qXQ5Bp0sGYL0pdp0u3S0NPmZSvsugjSeAIL23DPepLyarNNN8JPa7Tbs7e3Ber0WjyuWZGGoTfC9tijLa8vyNFy/1bUp60IbM5o8lCZaOK9JtgC3O6WxRvNrtVrVvX6DwQC63e5WIJ9+U6eNtHEi+TQa3bx+dWnxnLbE6x07xmN8spg86T3ptL1iyqDfWf5qyK+g/9O0MROTkr6n97NbdZDy4vWS2kayZ9frdTVxZJW5Wq2q02hWq1V1LCz2i0ZbXUi6mPLWaDSCe/fuAQDAyckJPHr0qFpksWsdskv52hRy+Sp8PPFxQvPhNjDva5oPyupOpwO9Xg96vR48e/ZsS4bjvcH4jXbSgRZf0vSHp22QpslksnVkMi9Tyk+KoVi6gae1nmnvLTnIgWnpyVP8u1AdLZqs5zF2OPe/OC/x77GPqA/D4zYU0u/rHu9S20h9wu2SEO97yqwDq+0smzoka6V61aGXt6/WpjE05gDlaU/9PPadRavXz9klQjLsJtF6U8BtaM7X+Jvb2wB+n47mR9NiXt5d3Sny1TUZ+xnCCA0er+DRlK9UXpOOhFSeBn4HRV3hmYJcBjnmZaWlqweLorhyFKKlVENKnNMqBXd4GZqRaqFO8KIucpanBfD4+LCMD08wJpdC1wJ3nCZerkYT/yfl45EpdcZPXT4MtQMGJD0ODU8n8QJ97qXR84wDlXdZlnDnzh0YDofV/X7SMWoA9tFFMTIzZHwAQCW3rssxq+NEWTTHjlFclS4FCHmeXh3uHRMhuRKSSSF5ovG/h37+DQZeQwHykNNHjWaaRpNjdYD3b/b7fTg+Pq6OKeT10PgLrzqw7oV/0cD71mtz8jzwf+/Ofk+elqyVnK0moV1x0RQo/w+HQzg5OYGvfvWr0G634a//+q+h0+lAp9OpAovYBvv7+3D79m348pe/DI8ePYK/+Iu/aJzWGFiBKUtXe2Vt7mCcx3+5buDxsFKbxY4LyksIrc9i8rYCHKH0dcY2rwcuSqB+W84jalMhtTmvt+faCsnPCfmbFOv1GubzeSVf8ChPj9+K0MahRJtkm2p9b5Xb7Xah2+3C06dPYbPZwGw2q2yUfr8vlkP1+C77ni7KffjwIUyn052V/Rl88PgUmpyVZCe9JgvHWLvdrsbYcDh02zJ15DvSQnUF3re82Wyg2+1uLVDwQou90P+tb3ka+gzpDE0uhmIDXMZLsTlerpY2Fp44oqcMSy9yvvDYQzfNnrF0TKg+WOfc9rnFT7E2J80vFAfgaVLKshAjP5rmk1i7/TpiVJ/hZkGSFZL9yO17yj91TiCxdJ70PIZng5OxHkURQ2AMQgIsVSDWgRXwzAXPXQIxThLPy0urh5l44LnJQIonP5537IDwGJhozFLjBwOSkrEnGZ2hMiUBI91xoU0eWOXETBaE3mnlNSUXQs6SxIsxhrqnfM3w845FKxCkOQVSXvxfqFwvfbx86bdGW0z/hspLke+S7AwdR+ql0XqmvUNne39/H46OjqpndfL1OLp0HGj9z++Yod+nGr+5DeYmHQKsZ6vVgk6nU5WnlSndG6EFVKlcDsl/Ds/Y5/+H5HeKDJfKk3SHJuc9vNC0w9dqteDw8BBarRbs7+9DWZYwnU6vHAunAWVKq9Uy7x8P5SOl8ejsXLBsjtgyY/kvBIlX6DiqY2PWRaiOuYIEtI5Fcbl7++DgAN5880149uxZdZ8jDzwWxeXEw9HREXz5y1+G4XAIf/VXfxU8iQZpl/720Ir/W3ZWyFaTnnsCiZo+C9mbmhyODQqlvMuRnoIG+i07MqfPa9l6njT4PqW9vbxEdTLVv4vFokqPYyi23ZoMCGp2mJdHNZkoyU0Oel1FURTQ6XSgLC939Hl8U16Oh2Yq37Vnkm1F0el0YDAYwGg0gslkAq1WC3q9XnUnpgRNnjfRt7ztN5sNrNdrOD09hdPTU7h16xbs7+9nLbMOrLHk/b6OPyghVodI36T4kVJ5Hr+Llkd5GSc/e71eteih3+9v7USt01ahMYe0oA2L4x2Px46djNV8Hw4uD7w+AZVnsXEkPq5D45zqC/p9qjzQ+sIqW6I99K30TYxvKcng60bqGJDaMNa/ipVzli1j+dGeb0JjRZI1OfyhFJvYgxz2aJP213Wjrp38aYOHD7U0ODb5grsYm0FL7/GBYvKjqL0z9kUdHLsKejWBXQbxQggZqdIK1Lq0eg06XAkdOkpJM4aKooBerycGfbTVFViuFxJdvV4Put0uDAaDykjGO+92uTOnTj/l5EdNMWEfaJMkAFcVOP+b93fMinlPGr76UWoXrAcPbmrQDGh+B2iuPrB2heUwnq28sE9C49fj+ITg+aZum85ms61d9QBQBY80Pg0ZER4jnhou/OjsF02H13GScUcRgK6bkN+sybeY+50wHZUB3jpoE/ecZjQ+aXnS36F8PI5iu92G9Xqttg9tX0nu1eU3L996ZCm+j5XBvHzvNzFIkWGUp3PbCjnrxwMmqGP4MXl1y8gt2yx7YpcoigIGgwEURQHvv/8+LBYL6Pf78Oqrr8I3v/lNuH//Prz33nvVOKXBIy6LQmNesqVeNJ3RFLg+5e0jLQTTFodJjj6ddC2KAhaLxZUjWpvoC0m2aeNyF0EjjxwP8SatB44H1PPWd4gYnQ0A1QIK1M2TySQqH6l8K/jjnaCQbMuYnaKtVgv29vag1+vBdDqF1WoFq9Wq4ldeHi0L09C+wvtcQwtIuI5dr9cwnU6h3+9Dr9e7kh7zpDqG276I65Bn0+m0og8XA1rw6LPcOs/K66bowqZhTZDxCS1pjC4Wi8q2mc/nMJvNqvS4wAp378f4/7HxB21MLhYLWK1W1U5ZL7wyx+Mb8fyo7UHlSiy4b8RjJFJa7tPR99rvGP3E4Z2Mp20jxaliY4+ImzLxw9vIn4xxswABAABJREFUw1tc53Oes7BrecV9zV3g0yaTbwKfflrQhH/cZL4WLNufy0nv/BH9H78LxVbp97Ft0MgxxV6nACBeWEgVT0VM2THlSU61N601WWQFTUJGc5PwGBoAce3NjTH63BoQdKIDJ2M99w7RtuZGj8VzktEWy+PcWMa7RQaDAXQ6HfM4k9S+rcMTmvCh76xytLQpskDiD0nwSsGa0NiT8rSMPcmRCNVNMyattLxMyvsS78XIo5iyJRrqwkO/Nw9vPWOCcFremkyivIPBIe4w0YCrVn6uuiAdFv3YnzHOqBeWfmsKoXEvpa8r06VveeBGS6c9t2SIJItzOKScL/hxzhqtvM4epKYLySbafjzgI8loKz8vz3oDKxZS2yM1KGPBy8MaQkEWbNecx0fGOF2hPud/N+1w4qQbwGXAtt/vX7F1cKHexcUFXFxcVOmOj4/h9PQU2u22OYEW4mXJ7sHnli1aR2dr5ae2dQ4do9VLk3U0HV94hek8p2NIcovuYIzVQVreUhrNxvcerdgEco85Xneaf92y8HvcdebZqS59T2mVbJIQjd40+D9fzMnbBf/HxcKLxWLrugsN6I9Lz7VxJdEo5Ud3+eFiHuRTyrOekzAs5OI/pAuPrkW7SiuTfufJe1fwjJWmbPzc/mZKfMXTN9jPm82mWrBQlpeT79ImgZx2YYhOfI4Ltui4SUHdvrbiJx556KGN+wM8jVSulZanl36H4OUxLFfzXyx66fcpNO4akt3JbVbLVrHy9frgoXxT42havjFlS+k89vxNhEZzSozF48fddN7PDY0vmuKHXfCZVR/6P55olqMMyc7xyAhKj8V7L9ydsTkM4iaYJUeenhUzniBfCi25vmkiEBhCp9OB4+NjmM/n8OzZs+o5Hj/JAyPelUlSW9O6LZfL4NGFmuKnz9rtNgwGg+r3cDiEfr9freR9+PAhzOfzKm2KAS+Vm0sp5QhOpyBVyCL4UW8hh9JbHg1gWOPKs0jAA2tlJQ1CSGm5AtPyz9l/UvtogV8eDJLy4mlDZWtBIet3CBqNuOJ9sVjAZDJRA00e2iVQY8DjZNLgcMhhuy40oUP4bgwaqKN3gpbl85XsEmLazSsXY+samrynecc6Z6kOi8SHFl1SGR6+i7VvyrKE+XwOm80Gbt26BWVZVjYC5YNQHlKQ2oMm9G0seAAgV9AspvzQOysQx9PnCvCmBPOaAgZDcfHdd7/7XXj69Cn803/6T+G1116Df/JP/gn87Gc/gx/96EcwGAyg3+9v3YGJePToEfzRH/1RtomDELQApjTxoQXTLNBdoQDb9wVr8oP/j3abFpS2nGUaxJVkP5dlTbd7zIQSpZ0/R2j047+QbemlxUNr7GSIlg9+x/uT2sK0XfC0DNxB7vEJcgLLpDTltoE8Y240GsF4PK58zF6vVx2vGlOOpG88cpvShuPcmqSkMmG1WsH9+/ehLEs4Pz+H9XoNnU5na/EY/z5GDsWk04C0cHvzRQkS72o8XAd4P3AZSXkl1AYeW2G1Wm3FEzw+tqa/UviGb5KQ4gCevo6xizAdH9NavULlcZ8j5BNZNgP9zfWmtx14XaznFKh/tJONKPipDnxHfaze0NrupsoijwzKLU8lvonVHR5odmZMfTT79NMqtz/D309w20mLZUljB/WudDQ/93s4+Glvodi0pF+0caxOxuYWMjGwAuMpQetYAyMWOYx5KyAQk1+IltjgrFeYhwIk2jNL0WgBAGo4oEHS6XRguVxGB3h5+bS+PPAhKeRQe3qDDHSyoNPpbN15s1wuYbVaXTmu1QpYWr+1b2P6OkRD6F0s6gZDebtJBg8GIfC4Hul43FwOfOxzDV4esMAVGld0XjpSZWvdvuX51KGDQpM/Ev/TbzHQgn/jb35EEgb/aD6SY+qRaZKDynncCqjGIFbG8m+bcBhCOl5qC62NQoERS6d5eMjz3tvG3u8txDh7mi6k763gSgxdoQkATjcNdgJcBrxwwRQAVLsRpPzob0n+1eH5OsGBGHkmjSPN/vDaBBK8Np30PiRTvTZkzPscqNP/MWVg/UejEYxGIyiKy/tjj46OoNfrVbtruG1ZliXMZjMYjUbVHbP8PkJMj/xtBaDpNzHBUk8dvbAcbYte/D/VpwqlzWmLcuTS0U3SaIHLIIunLHvNq4ty2Xq5xzPWjfoROLlJJ2HomERQHcvrKKW1bAOJD7Q8kS60Wbl8obID07Xb7UqnarxrtS0Pbnl1B9fN1LamOxBDejyEOjwm0c/lNqcpp89cF16d59XPHlu4Cb1ax/7C7zWfT9OfHj5O7XfKR1oeMfI/xcaMLUMqz6trPc+0cug3mvyIyc+SRdp48eh0zQ8J0SH5Vyn9kjvek+rTe8aEVUduU4T8Y68dkMrrlm0T0tu5yklBjMzUfHxrjHjql8JDls7KaQ/vQk/X1Vs0j9z8sSt4xrFko0p5WPnwdrL6O+S7aPTRsiQ61MnYVMNoFx2cGjTaRdAmBpZBYDFN3cBgjsASOpp18wmVwfMfDAbVRGVRFNUOUt6W+JvSyFd/0YHHA/CYL4I70B5YypbuqOXp+v1+FUhbr9fQ7XarO0UQuBqYOsO7xq6Fed0AE4A8SYU4OjqCfr8P9+7dg9VqBf1+P1uwJ0RP6HnI8PU4ujSdpaTpN/RbS6nlCFjQu2ljg7Z8Bw1AvjsT+U5EPNaJAtuo0+nAcDiE5XIJy+US9vf3YW9vr6rbs2fPrhxXJcl+bshq7es5Bp72303RgVL/em2OWNuE8gfAc9npoSlEBx1PfOIdn0tBmBhnBPPRwPncy/exQRikJ3QaBM075n6cGHqkMYMnYeBq8SdPnsBkMoEvfelLqn2Fu2dwNzSXQdc9XqzgANotHJz/PLD6k/I4XYnPd2YiTTFOZKxzLukdTebX6TspoIC/6+r22HwkFMXzxWNFUcB8Podf/vKXcP/+/SvlIF/jNwCX9xVKziv9LflP1z0eXhRo9lFIRwNs736NaW+qf3Z9OlEsaHukjIWQbkzxE3LpKswH74Y8ODiAbrcLr732GkwmE3j8+HFFN98laYG3FT8CG8cnzYcf0Uu/kWzM0BjfbDawXC6h1WpV/tFisYDpdLp1JZCUB12kSG38xWJRXc1j8S6nDf9GW1vy16guTznhJzbY5uFl1Jvou1N/4EVCrC4M4bp1i3QEOx8nlr3C3/HxRW0nehJGbL2lbzUd7kHo2hoKzYeJqQOVSUhrDh/Vw4+euIcmZ1LK83yDbYq2Gdr10klsVH5yuc/p5/djW/otxg73pMthe4fSUN7hG3S8/kwMUuKPqXa+dL1FCq5bpn7asOuYwE20CW5CXISD++tcHtBYicfO5zJVivfyNuD50ms0PDplazKWKnZPY8cEXTRQwegxfL2gjokUVIilj/72KqIUA1QLUmvfaMaFVba3nbmTEULIoAsZQFJedNICA6d05S4OgPl8Dsvl0mVsaeXSelvOETdyrHx5/tyYxXrRv/H3dDqtjlik31O+rhPEkH7zd00JXUswxfByKA/r23a7XTlIRVHAer2G2WymTjZ45U8MD2pppOd15K0WyOZppO8sOrW86sJqQ94OdFwgrGC6lJdFA/+ej0HMk47dXq9XLRrBI8bLstw6nkqjLcR7sQEQ6VspwBDK2+PgADw/mjl0rHedMYaow3eS/NTK94xpq/+agFaO1rex+Xralqe1ZNQu26UsS+h2u9DpdOD8/BwALschTsLzQAZ3tGNtH/wm1Vn3tjWlNxWpY4yW3Wq1qvv56Ls64PZX6vfa75T8LL7Q0nvSau/ohMV6vYbz8/PqeHupPpjPZrOB6XQaPFI0xbay6MV3oXwkmeLxQ2IDiPgN8qglI732Ged/iy/qtpMHsfawV29L9cpBi/UtbV/aJ5rvLiHG1+O+XarMssaRZrNK1wtQexKvpcHnki7V7Dc+rrzjUaKzLMtqslD7juuCUFk0vXUcJ5dzHrlB5WVRFNWxn1YZUj4piIlxcBqwnSUaY/Oj+Tb1TZ2xrvlSu0Qd+Wt9G+Mz8XystvfKY83m1uSQJfNjeId/w8eut120Onr7SxqDli3o1dXWbylf613I7uI0UX1g9WuKzZxqE0m8I9FUBxZtMTxq+aF1YLUBpy02TqO989bXM/6bgBQD4HJFG+/c/muKtpj0HrnQlD7L3RZNjFGP35ab9zxxJS4XNf1ngfsg9JlHp0jvvOWLO2ObFqy7zLMppvAiZTCkGCA5EWNc8u9y9zk6LJ1OB/b29qrn3W63cgKXyyU8ffp0a7WYlpfk+Gtlaliv17BcLtX3lnNKjatWqwVHR0dV8Ghvbw9OTk6qtA8ePIDz8/MqPd3N1Wq1to4yroO6xkBqgC+2HKksb3qJpn6/v9X+n3zyCVxcXFwpx+NkNDUec+Rb987XkCOa2ichwyMmX77aliKXTMLVqpIB2W63YW9vr3q+v78Ph4eHcP/+fXj06BEAXNan2+2K9GnOqAUrkJXSL7mApwpMJhNVju46ECOVjTspAeTdpNLqOp4HR8qubu7ceQxOycmRvqnTzjFBOhpw1dJwNOWE4THzr776KnS7XXj33XcrGqfTqUuf0UCI1A5NOjaWI0GD36F2o7LQ28ZaenpsZbfbhX6/D9PpFFar1Va/p7YL16/XJbvqoA7N2Ocoj+bzOcznc/jVr35V7aSy+nCz2cBkMoHZbJbdkbd4iAdgeH14oLYsyy1e0vKl+RfF1ZNrQvTiEaplWQYX12nBXHyOk7p01x+XC0inZCtqdd0Vj3tlOP7Pg2lN0RySqVwOpOz2DemwOgFMrc+xTGvyFYF8jbvc0a9EOri/6KWP87C3D3G88DyoT+ylg7ZNWT4/BcaySyTf22NfAEDVdoPBAAAg6q7b60BosSLAc1lG2zGUPhYvoq7NjZD9Qsd6CDH6l/K7x8a06IzZYU/p1PSgF6l+hqQnsT34DnvN9qD2sHZKDP/GS5MGapPgN5od7/XHeR5F8fwUE0tOxPS3Z7dlKIa6a3h5S/PRvGMiBl45EIO6MdibCqmtbnJ9bipddbHLet2ENvT4rta3vA7aiUMWL2snsWpQjym2CLQybSrAFoLXOdhF2aGghZUHd+qkv6W8QgGN0HuNfq0MKY03WBLKmwZBW60W9Hq9rSNe0HlFY4U6rJ4AAi+XpueBIkzD73rUIAWnuFNsBR7pb3p3LB6tKbVhnYBCHWhBLE/a2PcpoG2ORub+/n7Vx/SIa412/FbaAXRd7a7BKxc0OnM7mhosvpHGg0aDFdCxYO2Y0egNOTDtdht6vR602+1KZvEyPePFS1dMX1nOdmx/Sv3k0T1cPqcgJhDp+U6SybytJF6kjjgaXJ7dIdLfnA78bekvKy/6PJaXtPbg5dMjfK0gSYw8pGVTnvHYmpRmPnGFea1Wq2pnLI7Vfr+/VRfpugPe1jnHkFQP6bdUR/xNj3qjNNYJrHnpiU3j+QbrwvtWo4nWNYZvYsdGakAoBZjHeDyGv/mbv4GyvFxE8OTJE+h0OrDZbGCxWGzVd7FYwIcffgjj8biylXHnMqUL9RKdtN2Fr7ZrfzAFlj7LbdtJY5T7It588FuA7aCBpfdTaaa00v9zw5IN9G+tzyQd6fVB+ORXnfZD3YMnCaDvyhcXeP1pS99q9dGeae3ikZ94NDHVR1paTjOmx8WLZVlCr9eD4XAI4/F46yQATgu3E2ImKq8bIb9R62/JPrspqCNjrG+armMdGSvllRIDs+x6yQ7SximXid66pfCTRJcnj5DNxWmX/DBPGR5aaNo60Pw0D594aUIZZ50moPUJjWtZfozFkxqNnPdy2xq0PKs9LRrp36E8Ynwcj93hyS+2Xh5o/ZpKS6rO8Y4Fr1/F8961LvT0aw6a6tZHsw8l+m+iPVEHIb8tJEtSx46UXopfhOS/ezI2t5BtOt9PCzQDvel28zgAMUo8ply6A7Tb7cLBwcGVMtvt9tZEpRQ4pbRJQqgoiiv3nuFkCgUGv0KwyqF0UQNGSoMrqgeDQTXxi0fohoSthF0El64THqWyWq2g0+nA0dFRNYkvrVynwL7SJt930YbesY5GO//2uuEx7rlxy3d+aYFLDsmZs4JXXqeNB/614NJwOIRutyvuWOe8FgrM8HepvBYrF3LC4wjuCla5mkyWwO854hN4Upvyeyzptxyagy/RZAXqvH1rORm8TZDe0GKksiyvHHEo8btWNtbL69jSoCw9OpdivV7DxcVFdaLEcDiE/f19GI1G1cQWn9iUyuHYpYwtimLrmHMAqO7Jix1jHrlj8V4s3XVg9UcuGmPK1ZCbhrOzM/jjP/5jAIBqp2e324XNZlMdfY90zmYz+PGPfwwAlzZzWZZVGsofg8GgOrUA756juAk2w6cdkj1jBTbpbhWuT7mthBN9NG0TQRdOh1Sn3JDazKIptQxNl0rleeqMOzYXiwWs12vodDpX5HhKUJLT64XlD3rtB/RFU05nwsWL1BcbDodweHgIjx49gsViIS48Rjq5X4C0W3fZ04DYiybjcKegxweycF22dwq8/lkKcstBgHjd7wnqx9pwseWn6gbJ5/HYaKEyNN+AtkeoLP4tz1ezGSV7OESvFA/Q6optzesi0Y/p0UfkV4Lw9xptHJKPkENf5hxPnkkLCaFxKNGZOnZz0ZQ7ZvuiyHYE7+cmZb5V7qcdTfH5i9SOKfrO226hdvDqc/dkrEaE9ixmUMUYHd7Ob4pJchpd9L3XSQgFfmPSxzpgkgMeKtPKywuatt/vQ6fTqYIOZ2dnsFwuodvtVscHexWZlI47dFq/SPlZ9cZ8ceDu7e1t3S95eHhY7azD4+bQicfAWVEUWxOJofI9yt4yCL3jMpaHtfxD9HMnQFuRTenBgCMPYGFQvtfrwbNnz+Ds7KwKXuKEPM/f2+e7UFKhMevpi9gJD162FAiS+EniD80Is3hCUqRcHll1jtUzRSEfe4y/rYCRVb4mk0Pf0aObpGMbNb7jQStetpdXQ/yER8dr/eAJhkk0efU/dTClOpfl9o4hybnV6JFOTKD8iMe14nGDdfgwlD7FxvKAtluKXPEESTRj2AruxPJpp9OBsizh/PwciqKodC/frc7Lp/zp2bl0U8HlFtbNClZr4Pdv4s5itMHwKFhNrku0eevQtGOuBX6sgFnTwAka3AGLCxNTgjNeUN7Hib9Y2w+/kWwmavda/CFNOvLJRiqPtXLo9zFON88PQe1Bacepp60kWzW3HEnJj44Bye/hbdqE7LN0gZUuBpa/pPGUBxLP8SM2PceMa/AEeXJA0sv8WEva/zHyAX1W/KbX60FRFHBxcQGdTmdrsTX68HiFBD0ymdIk0Y3/+DHLnN5Yub4Lfy5UNi7GQV7istKDJusQa6N50JT+1dotdvzXrXOqLSTRH0NDrF2m+VPS91xPS2Owjj6RxjvmI91xbdHF00m6LtQuGo28blJfafaP9B3KAOvaH6p/JD9O8yM99eN10uqWQw7wb70nmXE7RrNNU2zCUEwjBI+sltpbo0fKN1YfeBCrJzld9O+c7VRHJ9fhTY/MzdEPMbovtY9oWbH50O92idh24Rsh+N916mzJvRTd7JqM9QpxrvBi8sTvtMqm5Od5VydfDoveWAczJHyt9LEOk1UefRdKE6JN4iOv8KWOXL/fB4BLx+38/BzW6zUMh0MACN8TE1Jy1GnWgjkeo5SXx53Evb29ynjsdrtwcnJSpZ1Op3B6elrRMJvNqp2d3CiONYS4MRsDi1djjZIUoEFKyw0pF7r7g7YdHgE9HA7h4cOH8OzZs+p7bGceCLD+zlVHC7yuVn94jT6v0tAcBI+8Tg1Chejx1lHKM0ST5nzy+nrvMQs5MZ7vPfIz5ACG6NMg0cplfqfTqY7I9PSNZgPwd7E6WPuWOqz42xvU4rKHl4VHEdLAY6wO90CTtXUdsBjnhvOVFHyQ0oeCA1L+KU4X9tPFxcVWoGY4HG7pYUqDxCcWPPKD55sD3vyovMDgTEhWSXyF+WAgGCd0kdfp3boAeYMxdXhaCxxpZWnPY5y/HEDexEUdOBkr0RnSkVpwSsvHy9NSGbytY8Y8TWP5gBJvSqD1tuxDni9vJ/wfFx3g4kgtH60NqZ1g2RMaPOXE5kP7SZLfms+XoidCPBqTVyotnA7pb8k+lIIsIf6U0sTERiQaY+AZ75wWiS5tMjZVH2NeuHN/NBpBr9eD/f396t1ms4HlcgmDwSB4nLNGNy5cprTztFzm8LQUdXV4yK60vqH1oO2Ycn/yi4gctkBsfly+avorNH7wuUcHp0KSgZp+02D5lVZ6HgeQZJwWK5DK8tpaXLfj/2gzcRpj66fRb73naXidPD4N+qLoQ8b47/i95h/y9o+VqZqdoEGyv1PHsMVfUrnatxb9krz1+qEevk3xYzldqXnwvDA/ipg+8qQN2U1anvR3U22aCxp9OWhqwufUdNAu/dsciOFVr9716gNNhubA1mRsDuauQ+B1DqzrhubUpUJzBmO/x29zMF4oD7pzA4OnaJj0er3qiDV0RDabDfR6PfHYRFTCUqBIShuiDXeAWKBHNtEADM8Hj4mjwR4KviOCBvlvGq5jzErGrYVWqwV3796Ffr8PvV4Put0u7O3tXZlcwd0+Hn7PpXC9hm1MwCCnkqLpedDKS7sHUiDMSms5G16DUnKUJKfFkoFFUcDR0RF0u91qd2Sv14OLiwt49uwZzGYzsfyYna08DecHS355jeHQtxIdGCDidVkul1u7Qbz6zMPjUl01+gCu7n6VZHKMbsPdvxcXF7BcLitau92u6eRpkAw9DEo2hdigByImqEjHVh3nPSTvKI38Ht9QeRj8wB04dW2cGFmXWtatW7fg+PgYptMpzOfz6phlXEBEd8Ret7OaA1oduLynOqkpWzU2IFUXrVYL9vb2qklwSS6UZVktTrQmblEWU57Hb2gAMAZUrqfsKvy0oA6/WbqL21mhACceX03HAR4Jm2tcUHAfURsT/F2uOIOlm3KOT6nd+fsQVqsVnJ+fb41BKqtj9X2dYKqWB8KTx3K5rE5JQEgyhNaXl4f1x8nXN998szqKfTQabR3FLtFHJx9S+/smBSLrTlr8fUQOWULHYGhMeCZypLy15yl9LZXHdUZd/4HaOvg7Vs9zvWD51vic3gFt5Yv50d+IXJMNdWIkGvDIcQn4LcYzpfw87c93zlvlYRmeU+AkXxXzzxUj9sCirUnbPOSPavXnYyl32Z40N12nSGOtCZv1M3w64gKxsPx57/eazZ0LW5OxfCDkQCif1HKaFLhWWalGlKbILAEek57Ca4zETHpI9FgMmmpkUqFMjxVst9vQ6/W27njFQINUHzT8pOPMaFoPH2mBPl4eX7kqtQG9AwK/4/dB0DrSSQ8Kj7Ea8y7WyciJ2ACH11in+e/v78NgMACA5/cN83JxB0To+NldyUZEqL48YOfNk38r0SXxe0z9Y8cZ/UaTgda3dWE5jFr6wWBQHSuJaefzOZyfn6s0pshxhHbkYU6nNKa/ONbrtXjcO35j5U3HmDfAKjnn0niQ+CoEyRnGY3DpzhFciIN0h/gmttxc8Mow6qgjYutC24L3p+VoSWVj+R4blQZopWAtPqdBcW0yVqI7VF/rPdKXiuFwCLdu3aoWDo1GI1iv19UiL2qr1C3Lg9x86tEXOXSBN5AnvduFI4uBT6pXOC3I2yiP8FQYKfiG45BPBKU45p8FSLbhacNQEC8kN6wAPP2NwV6cbNfyqRsYpLTEBkI1Ga75VlLZoW9SA5deHRWLzWYDs9ms8v8Atu/79fj7+DffaeqFJltDOl7iT5wwoKcHedpTKhf119HREYxGIxiPxzCfz8XJWJqfNbFR13eN0Quf4fpQp180/yjVfgqlbSqeQu1rKVaVawzwZ7FtZPl2VB7XlbsenyVHH6T6kSE9adHsaRuPPrZ0DLdzNT6QYiVSGqlMLd8YaPSh7SyVE8OzGp/yvLTf/Dtv/1nQbK9QX4XySaEhR9rQWPDYJjzddSOlf6V28PJqCk+l+H0vOqS4oBSbQoT8m5C9G8qPo9adsTcNLwqDeZ13q2Ol1ab8d+rqOG/Qm6ePaXuvIluv19XOD5xMAwAYjUbVDqyyvJz8sFY2xsI6Dpc61Jz2siy37ofF9zRYimi323B4eAjdbhcePXoEy+USFotFdRzaYrG4cszuTUWTATps75gAy2q1qvqBr9jGACYCd12XZbk1ESuV5+2DXLJI2gkTQ4dEV0zQKiZfDSEFpk2WhMrRAkA8b35nFQ1M1wGVAa1WC/b392Gz2cB4PIblcikeK07pijVq+X112r15XngcNSlvGsyn6XG80V1YvA4xgQ4eYAghZszRcSDJFlpXbdcrTcuPKW5SHoaA7ZXC37TeloGKaaVAEIDP9kgJeHlsDcwPJ8gpjaGyQvK/SeAONrrYDJ/jaRmeo4ZRX9IdlfweQwCfo4xtR+9tWq/Xlb0l7YyPQb/fh6IoYLFYuPrn04Zd+ixlWcJ4PK4WH3B7WdIpFFZwivMsv+sVn6UGuumdxSF9wPWPhz8p3VymcZuD1xG/0RZJ4eI/3LmKdefBuVx2GZ3El04NsuCxsWnaJuxhj7+TGvCSAs84Dvi7OvDYr9o7HkyW0nnbMkYfc/BrWqzgk/QO+Q99X/o8RM9isYDpdFrdL8tPWZHK5OOQjmEP6gbJm5TjUlwhJY+bHkNomj6rDEuepegsWib/m4+pFBtYGnuSLskNbxtaelfSrzTf2B24AJfyCm1fyTfBcnPA47t60mnfpn4nfct9AAn4DvuMxzctHt4VtDa1xq3U95JdF1vmriDZhzdZhnvA+4HiunXAdeE66KnbDjexHVMQinOlpqXfeNpJ3RnrJYQ/j3US6qbxpPcKMY/AldLUNdC056H6SIZdXSPAKkdLmzogNUOMB3joKn50tlar1dZRvtL9njxfrS5a3bTvJMOGBsA1h0kyojHQv1gsYLFYwHw+36qndnyIJ/8Q33sVfQ5DpK7QtgJXvAxsP25AlmVZ7X7Fvloul5XxSYPddY33HEoqFLjx8LU2pq00UlpLocQ6gLwfQ7vXrXw85Ws8rfF8jD6jYx7T0nsVeR/VkZuh+knOrZRO+h1r6Et5oczi5daRHzEBXwu0frGBD1o+TrjiqQ0p9d6VEavVNcYRjWkrT71y6BevnUMDDNR+wO+oruY6g4/THEEub5tr7Y76azgcVvYOnRDSZItmj0hpQ3QhQhNcHnlSFNsLZULt00RA5LqdSat8yY6x0kn58TFJF/fF2MIhhPyZFHvK0lf8t1T/WNmF/9MArscWoLJFojEUCI2l1fNdrN7U5HKKDpAm4unvGJ4KjY+649eS63Xp96JuQCw1D0uHemxH6zs6JkLtiCep4EIovCrGOylv6T2P/+NByPbOFauyyueyiLej11/KiZy2bEh3NVFG7DchPedJj/WqK5+l8vm4rNOG3jJ5elo/ixbP+xCkGE+oHh4fJUX2a74Dp9MDy6YIfWPp7JB8jEWKPPV8Y7W3ZYdJeWh8F7JtQn2X0n6SnNDkuNeW1fg7Vj/kSivJhZh24rIsVg5INIae5UBufRXTf6lyxiMvQ2XmRKy8w29i8o9FDjngKfdTtTP20wzrTP9YYRdTprWCVSpXEswhYwidsE6ns7UDYzabwXA4hLt371YO/mw2q3aP8tX4EnIJSI8Bh0FS3OXB33PnVHI2y7LcmmjOcRdXTBt40+46iKkFi0Jot9vQ6XSqI8JeeeWValfs6ekpfPTRR1t3dFAD7qbe08sRo2BSg6vad1IwyxPAvA4gXXzlrJVWalsMaOPO1+l0GtyxY+XH4TUymghW1DV+Aa5O1KQ4ot4yLWMyREOM3sTg4HA4hMPDQzg+PgaAy3um5/M5vP/++65di7l0UVHo9/x4jHZvkJ3S7AlESadThJxnzWHyOu3edLh7udVqVXfe0YBwyI7A/739mKqztPLX6zXcvXsXvv71r8N8PofZbAZnZ2cwnU6rHaZ4qkYsXXVoa0rGa864Nr5vkq7JAeRZq18wDYBuK6YEfz20fYbm4LUXNB8Cd8Pgjlwul732XQy9uFMer5PBY7O938eWZ9kDtL6pgSqer6XHvHljX9BTNGge3nxoWlpPaRIkFlIQsCzl3bqSv4/8Z4Hy5tnZGezv78OjR49gvV5Xp0V5QY9e99TrRUBoQgXrGrvzPTea1Lmpeee0BSTbI2QHe4Oz1sRkDPjkBac3Bp6YnvZMsuepDEGdJJ1UFtN2/BuEZsNrbavVIaUf+AI3XsdQzCFk49XhZ832k/qLHj8fkqnSCSGUZvpPW6AVQmrdJb0kIcYPrqs/Uuui+ftNIkd9PWVw5LZLd4kXidabik9zG4bs+6TJWBQqOQx+gJvXATkC0k0jFKDSnHlvoNQqN2R0aflrBgHPD3kLHVfq3NHjZ/GfdMyUVU/LkNTqLP3mR3VKdyXy8vDvbrcL7XYbFotF1Qa4M9MTFA69k2gItQFPWyeIkRqsltJwYz1mEqHb7VaTr9QopEdCIw/F0pYCSbZozyzUNc5z5JNq4NaZnIttJ05Tjn6lQS8uqwCeyyhedp3yAMIBSP63hRTnL+TUxgQTpfws49zbb7nGEaeN0oELPIqigIODA5hOp1U66/5RzKfO+KgTwLHaUOrzOsGwVOTUOTFjhdoTND8qNyS9rPGnV9fScqTgN/IbPbIYd2UDPA+AaAGX1LFJv0f5Rnk3doellK4sS1gul1XbhmyW2Px34VfkDPxq+efMw1osIY09q36c9z200vy8sp3abdpdYFo5mgzgdqVlH1D+p6ABRlonPvbLsoRutwtlWVa2AdcRlizn9rgHkmwLyX+kg38v0UbzjO1PCaH687SYPrZMy7bh6ay8NX1J5a1mt3r9F6nesXabRYeWR0z+Gm8AXI4POqlK2waBdjM9Upsfaeyhm9NkwWOD5YTFW1Ka0Hijz24a6siAXMgVe+BpLRmZoy/q8mSsLSLJqzr2jKfvKe+H6qr5hhKdlg7mabRype9DaT358u/q8orH3rJsNqt/NPp4mSG/J9XXSKXZeubxvzz0WPaxNw8PQrxrxSz4b88Y24WfH+snW32aUq6W53Uhpt1j6hsb//KUd5PaDWB3dk+MLePxhUN0J03G5u6cpgMasbguWqTOkiaJQkFT7x1esfXE411TvrVA7+/kWC6XMBqNqt98ogNXX1v3NsXAUqxF8fw+tqIooN/vV7TjCl9tpSo1QlutFpycnEBZlvD06dPqWQ5ch4OWu0zaTnSFIZ3w8gQNut0uHB4eQr/f33q/XC7h2bNnMJlMoCiKrZ3IANt8nhueQBCAXr/UFYaeMjVw3pSCCbuGd2U4pTN0H570jVY2503EcrmEs7OzLeXMA7+0PMtYpLI21OYxDkdT45UeGy+VHyvjYunMYYhK+Uj6FO8B/NznPgfj8Rh++MMfQlk+P2KPT8aH6PIGkKw7aes4wvhdjCzi70IOaRO2g4VcfE6d4NiAgWdc4uQ+X3i2v78fLe9pepofPXHEC6wvDZR3Oh0oyxJGo1ElA2PzRVmG+SDdeNdlyI7jPOeRj03hJvktGiiNuNM2dLx1bMCDXykSSk//5zpSGkv4vtfrbfG2VUZsEEObyJJ23KD9DwDVbnQu84vicqd6u92Gg4ODanJqOp3C2dkZAGwvpqBtkhPYFtJEvJRWgjZBtNlsKns5ZiesB6EArDRJYukg/Efv1KbBVCnAr9GhPaPtJO3Y5LZBqG5afT18wseZRncI1sSD1A+I4XAIJycnMJ1OYTabVbIH03viFLhYeTgcVt+EaEUbibbzTQ3KhoDtRXdUX6e++/uCmEBsTsRM1uXSGVROSHIwt26iekiSURqNnCYquymd2vc8BudBqP+pzKfyTNMlWn1ywVOm1Z/cnpfGAX+P/SnxjEafNVmh+VEvktxuApp947FTUsZuij+L34V4KKbMTyP+vtTzRcV16H/XZGyMg5aCJirscdB4WglNOcmeNg0FbZsMsGvlhQKuoTy0d1x4o1OFxgE1eKhDpjmGnrIlWixjBR1rmo63j7RyX3IE6d2wtM5SuR66re9i2wAR4/CnlBXKizuc0m9eLhei9CjKsiyrI2UnkwnM5/Mr/XhTlaTE1zFjLqZeltxJCXLyvK1nOYMM0jiVArkaz1NHS+JzHliaz+dbx0qGZHmMg1a3/zzvYsvR8o0NYlrjmP9tfSOV5XHyQ7Qiz6zXa+j1enBwcAD9fh9arRbcu3cPLi4uGj9CiMuooihcR9Sl6F78H68NQB08n8+vfC/pbV6u1FeazmoywEjHKn/uGRchnqRlhGiQxjovA38vFgvo9Xrw+c9/Hu7cuQP9fh/u3bsHn3zyCUyn02pigU/E0nrSYFTIhgvxDOURHoDytAEvk9pVHjolmjmNFmLkgie/FwlWsNAjK1JRV6+gP4C8TmWRJl84P+aULZofJvkFnK5YW44GUbktoulTmo6P2dS+CLUjToLRBbJUPsTIRi89vD6SfpFkKg0g17FnvbTz9xoNlh0s6Vfer3X6FyepMU9tstR6hhPzm80Ger0eHB8fw3q9htlsppbbbrdhNpvB/fv3q3ZZLBYV/2jg8ovSAPB8MrZJGyNG56XyWYpevW7kprkJGa6Vo40hTaanxkCko9SvG1yOpLa7ZF/w99a3AOHYgaePtHI0f95KF5KvIXlVF1w3ePkulrc8ukzTTWVZbi0w8/IN/T6URnpO6Zbe8b9j2oS3h6R7vflqfh/PR6OB5sPfS/6jRbe3LK1MizZv32sxg1B5KXrgRdSjdZFDb9axKXOjaf1vlelpS4nHJD9Mwo25M/YmdfiuYClTr7KVBFhqoFBK7zUWcwwSmker1YLBYHClbFo/VPxa2XyVfkjZhYJRVpqiKKrdLQCwtZtEyuf8/HzrmVQ//t1Nwy4Eo7SKMqbs+Xxe7VzodDpwdnZW7YxFaLscc4HKthQ5F+LN2LwoXbkgBQpD5Wu0xBqLWpti8JZis9m471UMOaP02Wq1qo68xu/ROcP7V3K1fWo+3nYNyUErLwymATwPisXw73UYWwiLRrwLfG9vD+7evQsHBwewXC7hv//3/17tcsJTEjz1jZEDRXH1niUe6A7l7xl39FlZltWOzf39fQAAePLkSTDYkBLESXWuYoFtRk9B4AFcDdTZTdXPvF08dV6v1zAej+Hk5AR+53d+B1577TXY39+Hd999F773ve9V9aLHnpZlKco4WqZmN8boGTrW64LuiLUCCvQZPr9OJ3sXfosW+Pr7ALrjtt1uQ7/fh9VqVU3uWHYV12e7kDE5bTWAqz6RZyFZWZZXJqKa5peiKKq7YieTyZb/RhdENlEu/xv1JZeD6A/iQhW8aiCVL+oGd/mOIo8faI39FP+IpudXutDrFzwoisvTiNbrNSyXS9jb24OXX34ZZrPZlr9LgeP64uKiOiUKALYWYofKpJMT1Fby2Pkx2LXc3aXcQvx90i0aND2Cf+eIz6TKjl1A44EYWYlppW/q6KQU/pRkKW9/D01an9G/vSfQ1O1zz7VaoZiDpz81GUS/xf/pzn2q46y25/nRfHOdFqiVWRe8DVJjHCm8wL+hsW6rffnfoXylOEKsDLDyk3BTdFDTdNyUer5I2FWsCMHHjMf2luwHrl+kOgQnY62K52gUTwDYgxQDyUN/Sv21QJcFrtClFcWhtgoZHTxQGoKULsaQ8qQJtQtOniDd0k4M/NviAU8Q1TLC6dG1nU6nchYBLleCU7r4/Wq03zgd3gBtrnFYR5HSd3URykNrl5Aw5sEN/Le3t1cFXwDgSvBaGrO5YPFYqDzNoK4b9EsJ1mjBIQmeOofeaWk5D6QGXbENtfFIYU1i8DIlOZACTkuoTUMywhMA8LShFDTkY7TueKrjsMfWQSobg3pUhnC0Wi04PDyEsny+a5QexedxQiVaJPqlow6t8ag55x56pO/KsgzuOAl9X5b1jsrMCYlXpX7w8hJPF2vXSv2Gx6NKtGp9j20sPef5c3tE4nMeVKF8Z/GY1B70ePdXX30V+v0+3L9/H+bzOcxmsyqNVAcLdFewB03p9l0htkxJ7lvtFZJTkk6T+F2Sf9a3+DfnxxzBK0qPxJtIFz82WKoL1oP6BDg+yvL5zhA6HheLRbW4hX6nyXjMU6MBITn6mLZp+xDbC/XBG2+8AZvNBn71q1+p6euWmQPcr5B8CyqLOH2SHWr51pQvaL4ef0+S0XhiBdoafNFvHb9N4stYu5rjzTffhDfeeAN+8pOfwNOnT6HX6221AT0Cn5/oQK9l4LZ2bHyhrqwO2YzecrxjS0or+Rs5cR367CZAk/f8udc+SfFFpW93IRNDMRVKE4B//MX4qJZNYKW35LcES65p9mzIj+O6NlTvVPvNajuPr+L1ZTQ7yUsrXWRk2YMh+rR+CfErb+tYP9nLo3XkeGw+MflrtrjHftfy96Sl76UxSWmhaSUZi+kk+2tXaFoXxsYGYr+Tvkltwxibq2l45Jzk30o6u65+liDFWzy+2ZXJWC9xuxwYUuW9glALRMUipb4xHU07TVJkVqAMy+LH0tVFqkMf803I0MEdSUVRbK2U9QrqlHaQ2pkOblyhj/kvl8utADX+zftVcsBDRlTsu1QejzE0Ut7FBAJC6UPGpHQP2v7+/taqb88Rn00iNLYkIZ6rvJgx4TWmJYMhxsDzlB+ipw59+D50550VbKN/W8E5CzwYSPvMu+rWMt4433kcr5DjwnWPtsswRQ9bzpLH4fbmKcGzW/Lo6AgAAM7Pz2G9Xm/tuvTAUz9uD/B70/l3HliBHk3XIP9pE8ESUO95bbHrcLiobtfkTUjnWLo4VCfJHsAxpU3IaPoa+0YLVkllhmRVUTxfBIf0hJx0qc54J2y324W3334bjo6OYLlcwunpKYzHYyiKy51VUr01ULm7S0cxprymaIulAaHxq2VjAsDWBIpFR4gmjd/p/7nbi+tkzVnG99LdywgMNtLFOnxxHy93sVhAt9utJqK0IBSWyxdycfnprbMGS+9IPor0PdKL/tnrr78O6/UaPvjgAzPwIckuL//wdLH6ImQ/cj7gfS/xDeUtqQz6PfYv5xfpbnCNVpzQx3y81xWk6LaYdBreeOMNuHv3Lty7dw8eP368tZAZd9EWxeUk82q12tIxtB84YuWqx9blqFt3/NYrE0PlWfZ5E8itu3atp73wxlCo/qB9VrcPLHksPc/FwyGbTWoXzfbU0qXGpzw+KX+mfSP1kWUXe8ehJvdD8PjCVO7F2GmhcrQxqPWj1gZamXSHrFW21y/y2oMpY9DTD9w2pQiNhVDZkt0Ta7fx/PjCbZ5v6hhMlTkxuo/74pptk0Mvv6jQxqkHXl1glVcHu9D/lI94HMRqO289Lb2m+RQePZF0TPF1DwBv+ddNp4cGSXhKR1HlYOAUYyEFOQbcer2G6XSqGiExxokVxKAOn4ay3J680/LSVtVL5eVEXWfgJjpHKZDatiwvJ8sfPXpUOfy7moi1xoFXCXgMVm//xSgbKyhaxwClkFbAeduCj2kpkCgpRq9jR50JbWJAu5PRQ38IMQZvbN5aeR6jmcoarc5NG1xe457+HTI0y7IUJ+JxMRDKjPPzc3j48CHcunWrujc2F7x97hmTUt7SmA6V2Wq1YH9/H4qigIuLC1N2xjqWPK33fjgK3tdWO6zX62ohFbbDdDqF5XK5dWoCp58H3mi5MbI3JSiF9uF0OoX333+/qsNgMIA33ngDHjx4APP5HLrdrlgm/63xTqxDx9sntl4S8GhT3PFFeZbuwKN6I0cglJZPy0wNitRJa0EL3ljpcvpA2qk0sZDsh1DQEyfoqb1OecTDg5RvcXch5hsb+AshZAukgtcdf0uLNnbl71GcnJzAv/pX/wo++eQT+O53vwvtdhv29va2Fqxy5PRtsR44UY72/nw+V2V4CiRblB87HJsf/c678A5A9v9i6oi0Y7lHR0dQFAU8ePBgSxZ7gEdS7+3twXw+hw8//LDy5b/85S/DW2+9Bd///vdhNBpt6WGsA14tgjSF7CukC09coFfO4LHZ/LjiXQQEKV5k/7qJWMVNBx/bsXe7Un4O2RFUvwH4jmbdVRtS2jzj0BProPlq8MTjPHR4ZZ9EI6fHW34oD6q3Pem1BXCYhtvCGurY6NwvxhN7vHXg5fP3/B23w3k5Mf5rTjtMouOmx12pHALw+4S7AqeH21CSr+eRH03Y3p+hPnbBa1SXcpsa4KrO9UDSE1694B1jW5OxWmZa4KUppCriEHIHwzliGY0aePg333VC33GB6p1YiKUxxYjxGqn025BRRXcZSQrY4lftqAMpP48DLTnf/Dl3LPm30jsNTQTTPOUhpADXLmlKMRIofTTAX5YljEYjAICtoHXTiiHXWLO+5QarxHdeWihCYyKVF7i8wECiV5HRcUffaflwQ1TKT+I1zfkMGbZ1oMkNniYWUv003ZHi6MTqnJDu0GSnBzE2iiTDJecAg3lFUcByuYTpdAqdTgcGg4GpZ2Lb0gvLOZXSxvCpVAcaNLXKibHbNB7QZJaUv9XeWvlFcbn7End10SC9lBc9ftSSp5yHNH2qBRhCwazFYgFPnz6Fw8NDODk5gV6vBycnJ/D48WPTDvLaGpY8jwmkWGVQ4C5Zukuq1WpV+hl3f4faJSQvvfRYfNg0vLItxN+SPM1ZH87DMfnSvrL8AKmOZfl8gar2PkQ3zbsoiorvcFcePtdseF6XUPlIM/0do5u8/hSlvY49UieARWVlv9+Hr371qzAcDuE73/kOtNvtare7dxFkrM6XgPI9NY8U/8ObL4DO5xRo01rpQjRKNojlKxRFAcPhsBofGHgPgfJAu92GXq8Hq9UKnj17VtlJX/jCF2Bvbw/+7u/+bitPSg/2m+f0DapL+T8AqCbi6YRyXb9FoqFpeOTNTUXucXQd+tmy2zhd9O8YmeqZ3AqVqyHFPpTiXaEFIthGkr7lPjinn36TWifL37LsWo9/HGNLS99yWa61lZQfp4nzWqy/y78JyTZ6YgcAmHpc6ntPfCDGl/Pk6+FrTzmSzkht/xA8+Xhs0lAaT983YftosNpY8tOkKxw5PGPrRUFI56Xo2Jj0N7X9rHbh+pT6dCljNiQ7Ldtay4O3a/TO2JzC59ME3rCeOyUkZ5EbI17DROqT67pHKzf4zuHUAFxddLtdGAwGsF6vYTKZbPVNq9WC5XK5ZeBRo6VOYIAjl3MWS09TAYpYGjxpsO2Xy6UayLPqE1tXb3qqDKSx7zVcUdmEzsav019SsNSiS/qey7LQ0a8emkJGFgaSaDsBXF3oQvPEb7hRi4EcXr7lDHoDqZifBh5AstJJf8fSEou6Rg3+ThkHqeB5Y5BBkg30OODj42O4ffs2fPWrX4U33ngDDg8PYb1ew97eHkwmE3j48CFsNhvodrtRQXeLTk6Phw+8gVpun2C7WI5cv9+Hsry8H1eTPXz1oZRnHSeJBn3/f/be7cey4zoP//a5n9P3mZ4rSQ1JUyRFXUhHtmRLSuzYiR9sCIgDw0De8pLkT8hb8pTXvCVAgBgBHCSAX/zgBE4ix7EdSbCt2KJESiIl3obDufZMT9/P/fJ76N+qWWf1qqpVe+/T3aT0AY3u3rt21aqqVetWN6vTGRpr7XYb9Xodg8HABZI53doxYbIcDZb+4nTRjld6PpvN5uyJEIhXebCsrDEkHWVLsEyrO9lOR0dHGAwGePXVV/HpT38aS0tLuH37Nh4+fJiLPqLHZxMtyl4J6fFQ+kXBmje1VWjHBfBELnLQTlLNqbSUL4/dP6+gRQHT6dQtCJ1Op273PC3goKO1adJK+ikA3DdHR0duR/toNFLvwqzX627ykvI/TV9H2i8xPUaysdFooFqt4s6dO6hWq3jppZfmJrmL0sSPbvbRSvSm5s3HJfG2dmRwCnienD5+/K5Vrmu6k2ySkA70QbvPj3i4LPk0mUzQ7/dRq9VQq9Vw9+5d3Lt3D5/+9Kdx/fp1fOUrX8H9+/fx+uuvYzweo9VqqTtYgXAfSHr5jq0sO94VrbXfWfuwPpxXusrGee6DFFhljzWYLv29kJxItT+KQtr7tMDi6OjoxKlxVrs3pWyrnOO2jSUGmyI/y27vlLxCMT8+0cDtMc3HA+YnKizlc72o8XxMX/KyfP4UvdPsTuuJE3n0dWp85bzLLckDEh8H+9sa94rhvPfVz7BYSJ0l5SGPRccWflvL439r14aG8o9OxmoKK6QYJCxEWIVhquIsglQDgP+2OhBcYFoEvi+oKb89TUGboqgsdEn6pWEaCxhreWhpfLRpfcLfUeBlMpmccGrlMW6+/grV3fetL6222s7yne//2FjLE/AI/R/7TgajYuX7glc+48TXZiE+SUnvQ1mGQswh5E5bHhpS+CDU1zL4k8JHckIgBu6c8G987aPRR/Dd2eiTUfKZlWb+HS8j1laWMSHzLxMpgcUUeooaRaEgnJVmLs9pt8fy8jLW1tYwHo8xGAzmdnJI5NXDMdkjZdyiHCy+axF4Mpmi8b5mg1jldZ6+5nn7xh0HN45Jj9MuzCzL1B2yqWNLo1HSFaqrxqM8DzpukyY0+Y5tWZ5Ggw8p7S/ps/gHwJNjJGkyYW1tDZcvX8bFixdxcHDgAmjSprLQmeKPaGny8F9efa+1lcVHIljaOoWmmCyUfKvpeCuPkaz0TSLF+kSTKSG9Iu+e5Ho1thOJ+FHyJZeBtACCt40mH7PsyZ2ydCemNr4pfbVaDdJXFu9qNpIcexb5TcfD9vt9DIdDLC8vY2VlBUtLS66t89Ia+06OH+l/xRbBpvgE/L383tcnef2YEHib5v2e0yd9WHnnX6xuPF9Kyxcn0FUAwPHip4sXL2I4HLp6kF6w0i35U445WthUhq0b4wXeRkV8W8s7K02p+Z02Un3rRcDar5b3PnucpyvbDrbYkzEU8U/IN9D0OR+TedpR+07TqzG7JWab+nSeBouNlqc9pfwK2bI+XWLhAfmtJa7j43FL32jl87qGygnVKY8889kIHBovxPzsRcmqPHW02IQ+W8dKUx7+Dsn6kO1OaSzj0mofFsVp6icNFj8/D31nXa+iiLWL1Mc+/WKBbGeffvHFRnx0AjnvjM2DIg1A31vyPG0UDYZw5HGwtHaJ7Yj1BQL4+9iOOx8tvvaw3Ikh3/n63BI8SgGvV8h5H41GJ1arh2jVwI2NFOXoa9uQIZcXeYI9ZSo9bkinjAkZ9Mmy7MRdekWCGDxvH1KdPJ/BK/mQ6I7terWOGW0chhwB+U2sb6QC1HbEyiPQQu2Wer6/lj5F4abc2ZUyXiyGdl6DKpTPaQRjNJmWEsziv/OCB8v5j5VeH/b29nB0dIRr165haWkJt27dwuHhIQaDgZqvdOSskPTIu0y1extDAQeJEC18sgSAC6hPJpPgLk3eh7y9LQEo/rc1IOtLG6s3/+7ixYu4evUqbt26hcePH0fLtYB4XZOtdCRvo9FwJ2tYg1X1eh3j8RiPHj3C9vY23nzzTVcG32Xn+57/JjqLQI6bFFkZQqVSQbPZdIH0Xq+H4XDoyrPueEgF0Z5nR1xRm+s8+DAhZFmGRqMBACdOiUhFvV5HvV5Hv98/ISdi+pAmM/v9/txkpkzHbQ1aIMOfj8djjEYjx1d0dyyng35owQaVeZ7A28wajNVkgSWoTeWl4PLly/iN3/gNvPPOO3jzzTfRbrextLSEfr9vuju2CJ/RLku6SgDAHE+k6H0fuA8n9V1K3qFgJX8fag8uv/jdyr6dTL48+v0+BoMBlpaW3G7ner1+YtGPFXn6MHQSRagczVcqC1Y9fVo4z/qiCIr61tY0lrJ8eZP80E6W4HIgzwSBps9oYc5pTCiEAszT6RTD4dDZsQT+Nz9JQitP2uCh+IU2KSbbLaa7Yvaiz1YvArkAzIdQEJ//DsUJiR8J5L/RO4s967PBtHahvqRJeLIH+ZjgdZF3DftkNOkpvrCZn1Ikvw35oZZx4vPPU2WLjOOeRozlrKHZ6mXoI8mz2r2f9JuP65/p3eL4pNaLTrbj11IRpF6Qsoc/p/T0m+SHxSYk3WmBdzI2j0MRC0BqFbAorFgQ0Jqfjwb+rVWgSoeW52+hN4+xRvlbgtsxBZFHaVlpCCElOK85uqFv+P/SaEuhIaSoQzvl5I9WZmyM+MD79bSFp2VclGWISMPdml7SYglCL6IdY3yVd6KE+kDyT5E6SGOyLMi218pIKTOV96idQmWmyBMrHSn5aXlphn3RPg7RlFpGTG8W6ecURwpIn4gk2mK6wfcdBXhHoxEmkwn29vawu7uLSqXiAvuawRayBYqO3RjdoXQhg5Le0er38XjsfuSRUVabpkhdikLLv1arodFouB/eX1rdCFI/aTylpacAho8nLDKOgl2TycQFy1OcAks5vrL5OODtEwtk+PiDAoztdhvtdvuErpATCVbeKoOXUttqUTLaCk32anJR05O+/GTevvKsMlv+5KGDxoBF5of6ULPZtTScByngKI+apcmvkC7l72R6ykv6PXxhDJUdahsrNP/Q14++Ppb/8/t3h8Ohm7xbXl5Go9FQA2u87BAsclHzE6V8tvKqhlSbg9crj00j3/nsW83OjfnWdNUABfG5/CbepFMi6JhfmhCSdbOA097r9XBwcDB3dYxW75hOsZYZ8sHPQl5b/Y8ycVrl+FBmOxfxUVLhG4uhd5pMtthFPj5dBGL0hOpG+ovkKT85R5sYKUO+S5otdk0RWGS79lz7NkWOkb1h0Xc+3R/yN1PytKTz6VSLXUHpQjaT73mof3xlavo4RFuoLaTdtkjk1Rcp8imWh8WOCcnKVPjaV/vbMvbPWv8tAkV8+U8ytHaROsuijzlC/q9FPsXy5ljIzthFKseiCAXa8pQp8wmtwiLII3S01U2++w1jzlbsGTFnCqSythhYloBJyvvY6sAU4a+lDd1nRYYnQdaP9xU5uvQNT1tkJY8WbIspyLIdodMQ/lSOb/V8al6EMvIrijwyx2JAybEZWhEaUkgyT18QTctT+5vDtzM29E0K5HHdWqCKQyppQpl3O58mLONzEfWy5snpi/FSmXKG+MJyV7HkF/qfdjPygOTdu3dx//59t/NeO8JXwjr2JE30bSyN1fnndBDNoXu1aQfw0dHRiRXLFsRkUojGReuc1dVVVKtV7O/vo9/vO9uMAtAxvUFt5qOVjudtNpsAgMPDQ28bpDjAhNOSVdqER+r3vJ3oSNMbN2649i5DRxcN5OfBWeuLFBlM0E6k8N0Ha93toZV31m1TBuhIerormxYP9Pt9ACd9Pt/doNPpFL1ez/1PNkqz2XQ7EEm+ZlmGVquF4XCI4XB4YmX3abQr93v4PaVSFrTbbdRqNTx8+BDtdhuPHj2aq2co/7KQNxDNv/flV3Zb55WfBNqRRHxCk6axNqjVarh69Somkwm2t7fd5Lm8juDx48eo1Wq4fPky2u02AGA4HKLX650Y01ZdNZvN8OGHH2I0GmEwGKDX681N8Fh1HS8v5v9SO/HTPs5CP/hgtYl/2lF2LKMMyFMXeIC2SJ9y+Vqm/kxpQ82WpW/pZImNjQ0nG8bjsTvphesF365TbWJMK5f8BoK0WXztHDqFT7uPVMujKDTaOK/44joxn0fKytACT56njyZ6HktD7+j4eeqbmF8q6aWTnur1uncnNa8rADX2GhobVr8xj3/5cZHXmhxK8St9oHYv84RBORY43SEZQnwheeiTivOoB604S9ppIwFfwA7oJ3ppslHTZdb6pIw102SsRmDewkOC9DQme2KwKCWZlqe35K0FfDksbajREKOXvg0xYFED0Ee7lmeMBywGhvbMykfWepIR4qNX9ocMdpcJax1DBnWMrlR+tOSh0Z3Cu3nb0UJ7Cp+lKJW8CkgLOPj4zFeW1rY+erT687QhnovxuXbMiLY7xAdtoinWpqkBY82pIBpD34V4uQjy6sEyaOBBBd87WV4qny9Cv9CkF3cWLIE+jc99+WdZhsePH6NaraLb7TrHVC7kKdP5kbRyviwqlzUZwfueJgHkTpaY/SLz9/EMf5Zi86Tqd+KH6XSKZrOJy5cvY2VlxR25lqefQvqV27j0wydw+J3zofy0MmmHbSgYFdOjXKZa7TL53Np/sbFH9aF8uHyXkww+2U/lxxyr04JFHsb6Tuo97e8QD6TQ6LMJUn2L0Hvej/K5dpwerxf/li989PGd5s+Q7KzX62i322i1WqjX69jd3XVyjrdHrVbDZDLBcDjE5uYmbty44cbt9vY2hsPhibYiOVMUVt7Jm6+Fx0L2oyyfTougBUuz2Qz1eh2XL1/GrVu3AMzr5xRaUyD5gp8kwPmI33dI9KfENCyyWsLq/+T1V6Qs4H2dZdncqRa1Wg2dTgeDwWBukp3TNJ1O0e/353haymcLnZQ/X9zkkwOp+ld+J3VbaLFnqq6IxTWK+nqWss9KnxVBGf7yeal3HvvC4iNKvpXpU+SNTzaktqFMz/tmNpu5xRS1Ws3pVFo4FMrHamuH/KnQ9z4ZGMs7FTE9GbKfZFtKWNuoUqmgVqu5tPwUDZ9tqLWh9izUNrHvYpC6SX5Hz2ihc8wWToEsT46t0Bj3xUR4XYr4+jG/TaMhZv/KtLE+tdITg0VWxtqLeFqOY8k3Rdv944JUf+w84TRp9Y1Z4qdQ2izLTtyDzn2fReLU7ozlCAUBOMoYYKEgjkZTrFxffhYn3HcPpLx3QdshUJQZfMEQjca8SAlGxAyE1HItkIZdijHNV9/IlcSSFr76IkRDEZStgE5D2IRg4bs8NPomEfIiJY9QWp9RxY+n42nkqlDiPz6uYytRrfRwWiwGYiiNvPMSeLJSKfatRlsRWmR6akdt1RMdtRfLw+pUUr6WdKFnobys4yNGc8jgt/CDBUWdYIlareZWagNwwUd5z2HIGAuBAsnvvPMO3n777VyBw6Kgo5JTwfV+LCDO9eLh4aHb4RSa1C6qh6wB6Lw6lR+ltrq6ildeecWlj+3yzRMQo/R8BfmFCxdQrVbdPah0FCS1Nw/iyLLp7yzL3P0nw+EQ4/HYbMdIB1YGOFLkpwyA0+/UgLpGIwXtK5UKWq0WWq0Wjo6O3N24mmwq224pO6hioZHzAf9Og6/vzgNkXTVn2Be4406vNg7oCFUAcxP4PJ1ms5BfMhwOsbq6iitXruDatWtYWVnBt7/9bXS7XbTbbbdSulKpoNFo4PDwELu7u/ilX/olfPnLX0a328Xh4SH++I//GI8ePSqjuU4VGp9osmA2m5lPkgCAg4MDAPOnz6yuruJzn/sc7t69CwDuvuzQjlkexM4zBvn44XcMU59SfnREfb1eR5Zl2N/fL3WnR9F8fOPC8q3cPU3jgfdNrVbDxsaGW1BGkyh0fzLVZWdnx+kcef+fhRYar3QXuPX7PG0pJ0BoYYBPP1qCxRKhEwIWJYM/7kHmFF14nvRYCDEbrYgdpPnyclyfxZ3IEjs7O6hWq3jqqafcfeDdbhd7e3sATt61Fysf0MdVKLYR8ku5XUo2pe+kMElDyLeWOspHi5RHWpn0t+U0RQ2VSgXLy8tOZh8dHeHw8ND971tEE7Ib6R1faKylIRvBRz/vN34ygawf7xtOZ7VaxfLyMoDj4+21uHhZE4h5fLuzQBFdIL8N3XlN8NWzTJ2kjXcO6nNul3xcT6/7Gc4ONMa5HOIxdEpDczvD4fDE2OB6aBH8NzcZGyvAGhywQCoGSzkWhIJLPuWjKVWLwcjr4HOiNNr4e2sQTL6XkzMxei0GoqSFT/TEJhJS2ovAGTwk+H3lx/rWl97X7lqgTwYeubMXMmZC/J0CrU/ot+S5soPiZdXBkkfMIQgF8Hz5+8oss14aUpybGM9JSKNaSxsKlmtl+miJvfM5QNIxsQYurP2h1deSv4R0nKXTZw2AWcvm7+QRGT5H1DImYmVLmWGlVysj9b2EJTCmpdfkHD/6ltfRKgusOoMcTr6jmybxtaMjY3InRpfvfVnyigKsAFyw2nf8MG9fLdCUOtZiafjfId3sg2VMZNnxDtV+v4+HDx9iZ2fHrei32Ek+pBroWoAklI4mI2mnsg957HHL+ODyI0XHAcf2Eqd7d3cX29vbWFpacryolW3RVaE05y1wkhr4obyK2izc1g6NXy7baEKDB8FibVq0vTX9JPtayom8OihFdvEfWkzG79/k9paVHn4lDf1UKhW3yCKFRiusfBSSIVRH/huAm2z9xje+gfX1dVy7dg1bW1tz5eaB3LUp7dsiMlvmx5+lINbvKbLI5yfzYCTp5Vicg++GzbIMu7u76HQ6uHr1KpaXl7G2toatrS08ePBALbsszGbHVzvs7u66hRFEF6cxNGmSZU8mpfKcDhLrI83H5s9brRYAnNj9l1I+z1dDGTZeHpm4KGjyuoz8Fl3HVH9B2qop8ShfLIfn4+NNCyQfh2iP0cpPZZE2er1ex4ULFzAYDNyxxZZ7UDUaOM007mezmZMR2sScNnFo6TOfzxCydUO+u6Uftby1dDHI9l9aWnILNS1xVS3uoNWDyuLPQjLYx6dygYyU6TxP6lO6JojraWlzaTSHxrDs+5QxwPOPxVPK9ME0mac9i/keKTagrGMeO1eW5WszTQ6G6KK/+cTyedF7P8P5gGYLc/tBk+O+8ZTHJ0vx2807Y0MC11pYSn6LgNaovPGtgzmmxGPlE6xH5GnM4WMin/AM1YPnwb9JEW4hBeBL47unLkSDZjxo/4fugggZIBLajkS+6pYbpxYDXgYSLJB9kzf4lFomlScRcjZCijaGmBER+84qj/I4BykIfWsxNLTgE8+b85vG5yl3vFn7xeqcWnkzb/tajcmY88SfS74J8XZRHcX7RK4I1VC0PKtM0sZtTKbnkWMp6TS+88kjei6PwObjJUXuc1pIn/BdqbRDMbZK3SoPY/2fMgZjyLLMTSQcHh7OtZXvbufQuLMGNVLHj9WOCcH3Xa/Xw/b2Nu7du4fHjx+bv/PRl4dWzlsh/iQ7g46MlrujT8MBzeuEEygAT7z0+PFjbG1t4YUXXkCz2TyRb0gOS5qILv67TKTKubJRVO9ogQpfnWazJ8e4kiyVO7DlhIjMk9P9ScNsNnPBzqWlJUynUxwdHQHwB/p8qNVqqNVqbrc8ADfGQycYLJLHJeSEH8krTbbXajUcHR3hD//wD3HlyhV86Utfcjtj85RPfC9PkJJ8XFT+aUeSlQWel9wtFtP5Pl1Av/lpTXx8cv+A5CgdZ7m9vY3JZIKXXnoJzWYTzWYTk8nETcYuCrPZ8Z2x/OjgVqvlbCg6cpNONNHqTUddU50sd4zzttROHLLSTic10GKJPLsTTzNQHPJ/UvPI62ctAlbZd9o62yrzQ2Oaf8/lXpHT6lKhxQC5XOGnzEi66vU6Ll26hN3dXbdDk77TbIOQzUyyi189Q+XLa1MIml2i2Ygh3qCxzmW1r0+1fEL+qk+3yO95Oov/SDTW63U0Gg3s7e25xS4abTJeGYtXhcq02Oj8O1pwRnzEaaQFabwsmoyl6wboHY+dxPrT1wbym6I2tkZDUfkYs6klv/jyCMW8LGX7/Ks8babFZmTevjgPpdN2ffMNUqepayWN58nv8cU9fxrBdcps5r960vct561UO8SCwscU5w2unSW0xkxx6ooYtr53pGBiefucAM148n2jBf1Cipt+x454jNEu25gcpCIK0dePWjv4gp3WIBIN4KKBYd9gXoQgL5Kfr56LpDE0ie77LmXcFqX9tJStvIeSEOJNa9tpxluK0uYGYFnGbAiLCpDJADPJuDICfJR/XtrKgDUvK52pxkhofJZhxGugoGOv18sdNJNlzmZP7kaSE5NF8y4ToeA9Oa+NRsPtkuHtyZ+RLKFJmFgwjoIkFCAu6xg1Pg5l4IevoI71BS2eAoBut4vbt2+j1+u5+1uBJwvjKEgQum/Oqndms+NdrLSTLssyNBoNzGbHx2f68tfy4XLJuttA5qGBbDCrzAvtWirKz5PJxN1FRmOOJqt42XmD6iHw+pedt7V8IH1Cr0yQDizLvpG0092ilgCTpMvne2h2DMkEbcU8Pb9+/Tqefvpp3L9/H+12G1tbWxgOh+44Ve045WvXruHSpUtu8QAFCc8D8urTRfPXpz/9abz88st47733cPv27blFrMD80cb8SMOUQIkEz18LShPa7Taq1Sr29vZME3uUN4cct0Vtg1hQlfInXUW6gdfZNyZioCC5tHFSeZzXn/TCZDJxO+7p6ojhcIhqtep2u8krJbS65EEoFpAn3tDpdNzxzqcxhlJwXmJseVCmb100vhLSv9JOlvKlzAke7tPL42dlWq38opB1kf8fHR25qwMajQZWV1fnbGgKfseOGQ6BB86ljeLLg7edpZyQLOB2RKhvZR9YZa51QS+3/YnvuJ6ktmm326jX6+j1ephMJs4n5nlxXcEXV0mbKaZbLDRL8L7h4Pohy44X6zQaDQDAYDBwPpPMi5enQfZdyE+W+YbSnCfE+E22geZDlh1nKwJJC/9bytzzQjNg9+dPi49C8eLzyssWFKHftwGG85G83oP40XcaTRmITsZaB2hqgPY0YZlcsATWykConWIrzWN0agaZ/IYrVg6piCxKTpZjgaTFpwDztncscKyltxpRWvuHvvUZIhb6YgGqlLbKOy5TlXNMwGnlxYIbVnpTnZFYQNBabl74nDz+N28jrX5SXshjGBYBHqAsowyL0Zu3rBRZQHIxdgRoLB8q19cHMSclL1/moZPKtMqHRfG+rw1DtPjypV1d3W63lEA50VGtVt1kbOzumlgfLxIaX3Pnmq9K5mn4ineikRx9iyzmR5um6JuYHKQ0Ft3ia1t+/NVgMMCjR49cgIffSUM/9E7SYmkHSQPxIAVF6vW6GnTW8pJ1k7oxZgvG2qtsHZHC29QncjU83/VL90xqO8rKsFdCtPE8F4FQvpLnitjCKXKb33FsyTuVFn4UryantHxT2p/bJvStzJfqt7GxgaeffhpXr17FYDDA1tbWHG2aLt7Y2HA7+ADMBTlTaJR9wnf8WXbkWKHxTlF+tga2geNxdOXKFbzwwgvY39/HBx984OrPdzHxvqnVahiNRlG7JGQ30g/xHOUrQQuT5D1QoTGX6pekwKrPeFlZ9mSHLNfzVl9MyhrS4Ry+uETIvuX/853NFOCivpnNZnOTsal2ZoxHfHIlNLEVK4+OzeQLzyz+uGZLlGkPLkqfl9Ufi/yWwyc7FqHLrfZrjDZf3nIsh2Jm8tmibBdeTr/fn9NZ5H/R/7QII9XHk+m4buSwxOtkm2s2Af8/Fv+UciPEA6G+9+kbXyyL0km7htKT/9JqtVzfhO6jpYlgbaGbtKVS5VTMbpU2NreBSP/QTl9KL+ufyueaPvD5y2UjJc9QLDMWF4mVQ20QsgtjcYsy5UqsTzT+43yw6HjKIrBoufxxoaEIUn0DyVOkq3z8o12BxvNeRPsV2hlrFYjnqeNTnXtremuADHiyAsin6FNW/vNyfY5MKLiiKXXN4EtpC54v/15zNkO7bUMKQP5vWQUXc5g0mkOwOL5lBFVknhacpzEXgs9ADSFP3Rbh/EqkjhEOKQ94QCuV5tgqRss40ORCCh31en0uD6qHZWd5isyV+kcG4ywguvgOOit4n1t2mNG9U7TbyyezZZvz+pUJS9CC11HjCwtixnyqruW8FHNQFoE8d5ZxpIyBvA5xqNxareYmH+g518dau+Xd8erT/xKW1fPaznWfDWrhKZoglTu1Uu3E1G/kt3khx5WUe1KW89/c4Vg0nTRWKfjz4x//GLu7u7h16xYODg5OHL0cA9nJMhiVV19+XOwlDTHfK8ueTEbRbi4OajPyS5rNJqrV6onJBsnnH8dASNnQ2oVg9Y9/8Rd/EdevX8ft27ext7eHmzdvujtpp1P9Lu/TRCjwxWUKjeFms4lut4vvfve7AIAXXnjBmzdvFzrKnHiQjiaUci0WLExBlh3fny7vq0u1IS2w7IDylRfy61P5YzweY2dnB6urq+h0OoVknzX4CzypP+0sJ7mTIvtTZY/00fkJGFmWqfJQlkX5HBwcoFqtYmVlxUzvJx1FfN5FlVMmPb4xeVoTAZrdzHfEA3a7vMy+ItkzGo2QZZmbNCP6Op3OXLm+PKR+DG1IiR1Ly2MOk8lkTq7wbyy+aGjHs9SHoXa1+CRUTko/Uh273S5qtRoajQY2NjZw6dIlAMdt1e12MRwOMRqN5haRaTSG7BTytX0xqhAs40TmSXxx//59NJtNXL58GY1GA5ubm27SdmtrC4PBwBvb5vrbF8OK2c1aWhkL8cWrzwqW9k6VAxpvxPjFmm+Mn0K2njzG/Wd+yc+QArkgROM/uUCE0mooql/npPMijapQ3mUOnpTJHIuSkGlSAm8+IUYKR6MhNLEXg085hPLT2iLVuPCliSkqre5FAuuhMihvS71kgMXS/lpw+DwgNu6KjPmy5IXkR80BKRLoTvk2NaAvjbMYLbGyLeMrNuZ8E2YWg5/n4zNiLZAyjgfi8+RnKS8EXyDZ11Y831AbpIwvCgKFvvf1kcyrrPaT7WAdK2X2X968fBOHsf9T4NP/KUHTUH7ac58MSGkni20jd4HJwKU1L0oj80j5ntPF8/OVk5oX/54WXci7COW30g5IKVNCLvaI1TNWRsxOsth+lrxC3/rSaG3A78Wt1WrY39936ehoYvk96QyNp3jdyrBjQsFAer9oZ79IvlJ+87wogJVlTyYfQvYuHR3K5YMsR6N7EfZgatqQv0ZBPn5cMi3G0PKlb/mRpFl2HHjmE3j0XMpOK1+urq7iypUr6Pf7yLLMlVev19U8LPJV2nH8mfwm1N68biHwMUp3fz569Ag7Ozs4PDx0gfGQbU+ymYLL1qODJR2xehENFGDmu3I0my1WntbWMdpCfqiVjli5UpYSptMpBoNBrp2oRUA00JUIpA/z9LMvf22scP4nOcDvovS1kwQtTpH5pU6i0Lf8dyitD0X92jJQ1La2+MynwaMhWmJjVUvr8ze1d1b6eBkpdpvMp2hbSvuMyyz6nxYS0fj2bbqQOknWS9aR61uZhsccgPnj1aV9GRvzPvlrkeGhb7S8eXqLrcXT0GKl2ex4MR0dvT+bzdBoNObuZpX118rUyvb5dSGktJNW9mw2Q6/Xc/xTq9XQbDZRq9XciTm1Wm2OLjmuuHyWPOUbp7x8X/9rPoFWb0vbyHr70qbKegt/p+bLv9XGrZY/p0l7H+KTkG7U+shXdl6UISs/jvi41ds65qw8qulaS0y2aJvl3hlb1r1gZ43QEQ0xpCgmi5NmOfaqzICHRQn7jhVLpS80EPLUKXakMzcQrceuldG2Md4p4kClwmos+lCWUlsUrLtgivRJ0Tb0fRNz4kN1404PNwh949QSMAoFMULQxo1mAFuQ1znl36fwtLXfQ23GHWMeVNICCbHAA8/rtMdeyEG0BKpC+RalgdMiUavVsLS0hNnsyR1k9GOR+1YaZCDutBFaxReiiU86xtpDBjMs+dM3dP9q3nsnreM2ZDDH8m40GhgMBvjoo49cHjHbxmLEa5O50vbY3t6eW2VZhm5P1X2cD6xtpuWj8V9obGRZ5o51vHr1KtbX1+cmwCqVCtrtNkajEbrdLhqNBjqdDvr9Pg4ODjAYDDCdTk/c603tWK1WTxy3ZmlfLcCySIQCejJYKGlbFD1c5xRFrA1lGVzWaIu0eF58YZfWjrz9pB6eTCZoNBpYX1/HYDDAe++9h8997nO4ceMGnn76abTbbbz99ttOf0ynU+zv72M8HqNSqeCjjz5ClmW4fv06rl27hi984QtYW1vDD3/4QwyHw+R2kvjLv/xLfOc738Hzzz+vTgxLlCVbQ7xVJFjH8cYbb+C9997DcDh0d2fTWKbdOtRH0+kUo9EIm5ubuHz5Mj766CPs7++foEk7gYR4WPPnSWZkWeauGuj1etja2jLd2ReDtK/Lku2pMkkbD1KOxOIQcvFLUblQlmzRQDwk77gF9GCvjz6yYTj/xHQj8dJoNHK7uT9OAcwULEo3LsK3zouyy9J0uEXO+HTbbDZziyd8x/6eBfjd3LPZDA8fPkSz2cT169exu7uL/f39uasouI9hsT99k2VyrM9mT45MbrVa7je9J/3jy5/XR9MtPD3lU1S2hXSs5B3fEc3cXqHfTz/9NNbX13Ht2jWsr68DOL6e5c6dOwCg2hhaDIfTCDzxJfl1Lj76LaDv5b20ZBPQu8ePH6PVamFtbc2Vd+nSJWxsbODOnTs4PDx0efgW1vL2k7pBq7uvX2U+3F63xPVOCxZ/2pcmNd6zSFnEjzzX7BerXM2D8yJjTxuf1Ho3Gg1kmX4aii9+G4rtWOK6KTBPxi7SqC4DIYUm4TPUY+l9+WsdG6PTVzYXOFqbWw1YK00y+BP7xlI3La2si0X5WwO0KQgZECn9WISe1AmHUBkx55q3axlBgiLvfektAcdQADP2nJ6FZMSinM0iY9laBv3WHJTQNyF6yqRVGlKp4yokV2K0pfCVJT+ZzsI/POBDToxFn8acnVj/Fg3oh4LeFljT5nXqZCAtNQ9NJsRkiPY7D+0+WB27VD3P24rSaXmn2Hmc/2kFMz0L6ZuQIauNbWt7h/qO08mPL6OV1j46Lc/ke41euoua+jTWf3l1aR66fbYZPZc2qfwuj3yh4NjR0ZE7Ro3ahgdi+FG5BHmvb2q9ZBpCzJYqw4by0bEoUFkUwOX6R6aJ9aPkgby6IORzSJp8z2PyWZPrtVoN/X4f/X7f8VWz2US73T4RZKPJwSzLMBgMcHR05O6/63Q6aLfbLsgsg+Op8vPw8BDj8RgvvfQSlpeXT9zd65MJsXLy+uwxeyrFbun1ejg8PHS7ieX3mh6gnS/NZhPNZtN7h6zGxzE/l/9Pkxv8XjptUYnPdi9iO4baQBsrvvprCMk9etfv99Hr9dDtdl07kE1gtdPz2uFyN52WVmvfvLpGQ8h2l3zi0xuWdDH9E2vDRekKCy2LpiGGmL7No4cWhVA/xuRwTK/GeD6vnM+DkHydTCYYDAYndmNyn0COB6sMtfhpXH5xH8qXpwUWm0/aM5a+8On0FJ+Jp6XTDgA4fdlqteZ8yGazOWfXaLagRuOi4bPbiD9osQv5ULTol05Y4PnIfrfYrD5+8/k9oTrI/zXd7uN7S1/k4eMUvixLpsboTNWVMdsulu9Z6bDTouGnoY5Fofmd9L/P7rXK8pidYskneWesLzjoc4BPE6FG8TkyqU60L4hgamwW+POlJ2VDR4WRYvXREtvtwoW/FFpcUdCl791ud65t8t5XFBOQ8p217ywOAxllEpYdQnmVUIiuouMghSYpaLS/yxKaZQpfzeEIGSZlOeRWaMGc1Lr70ssdIlrdfCv+ZRo+nohGy53R2nNrG4cCEZQPX5UaysdCX8xgLIs/QjSl0kVH7LRarblj4igQxvsoFExbNIiWs9gFmsJvwPyq7GazieXl5aS2sqa1pLOksdSPdvSS3h0MBnPfxXSXj45Go+GOrcqy49WBtMo5RhfnR2s9+UrlvPysObIW8MkRChKFaE0JKmvgu2m4QR+z20LHZ6VCyv288Mk27Q6tFNBxsHt7e9jb23MT4nfu3HG7myygHQ8UVNLsWv67DBAP0f2d1H8W2zWUZxEard/TGFxeXnY7ueieN5mO9DQP1tHOC87XZR0r+kkA7eqWd9T5wHmGeJl2fn/2s5/F5uYm/t//+38YDodYWlrCaDRyQdZQXilISc/lvjUwwe+oK8Ivly9fxvr6Om7evIl+vz93J6DPl021WyqVCtbW1pBlmZNLw+FQDdpY9CSHJpd88soK7suTTa0dJ+4DxRTef/99LC8vY2dnBwcHB2i32xiPx042LFI2yXFiiRuRTKK+4TvYQvWW+oC+pWPYyb6iPGq12olJeWoPOnElVO+fJmj9HPMFLfloQdOidBVFLM+YfUT8o8WmYrSSDPXd2amdDLJI+HwBusOU5AiN83q9jlqt5nwOjpjdKtu0yKSU5TvuW/K+isVRivCc5VsezyHQ32RHjMdj7O/v4+joCMvLy2i1WqjX6zg8PESz2cSNGzdwcHCAO3fuOD+T6hTTmyH9EtJ7VnCfkdcXgFs8R7ES4DieQvYsp0Pa5aG7fzXa+fMsy5zt4osdUxnSR/f5Bhova2WH3mv5LgKh/EPjMCV2GMpfswt8uiWE04yfpdJQVj+eZh19NJ+Hdg6BbMdms+nGcaVSQaPRcIsTJUI6lWRUnvi61obeO2O5821BSKiFiCjTgLUI2dB3MjAWQ0rb8B/+reZUho680eoYa0/tGVfEWZZ5HZCUvgoZ4r66yPS+4LAMdObBIoRFLM8iiikmqH1BZAtNlrJ9z1ICnhb6UwLIVoMw9M7i/KfmYf1O1lmOL+v4teRPv62y2VpmLJjEjeCikwOp9PvGmy94VwRaoMdHLy2u4Tu9pCwPBZesBn0ZjpFWdhEZVuR9CNSP1GZ0XJ107CxlWR1iC/LUSdoIWpun6mNJEw/o+PiQ/o/xFTB/tOjq6irq9fpcMKZMWOWItLH4d1R3CopR+iKI9Qe95878aQdtqU1CjgWXYaFggK++IRnBxxbZmTSRQBM3PD/avSx3xWp2tK8OVsR0mXzuCxKkIqZ7y7DhZPoUGylPGbHytb9D4zqmd0LfanYQ56fDw0M8evQIw+FwbsGrPKa1Wq2i3+9jZ2cHDx48QLPZdEHlVBki6yPbpFarod1u4/nnn8fu7q5bsJWKvHZdCNaAl2x3krUkf6TO1oLAXH/zhTU+W7OoH8L97Zgu9MnEMsaKRRaFYIljUD/QsfG020ijJW+dUmWJ1b7UxpvkNyvNVGeafKVxFvO1SI7wPEL08W8pTUxXyvR5kMcPyKNTrHkU8c9TcZpxHt9zOX5k/6eUq8lHWbZPNoZojJXL89DgkzMkXyxxghDd8jtt3MfsVeBJrDNFT2v0xHyQmJ4IoQy9TPQdHBygVqthdXXV3bVK6cjW4To2FqeJyds8usIS5+K8L3168iPotA3yN/l1JqH888qIFD0VK8enByw+/qLtjVj7hdKGdFyKXvPJUAtN5w2+MVKWripir6UiVY7G7NFUFO1zrjOITyuVytzx7VrsoUxe0+od3BlbliFofbcIFDFqUwWvVjY5lJbdmFmWzU2SannlEZqas1Wv1x3zzWYzHB0dnVgFJssLGTsy8OlDqP9D53Nb8/M5ljTgtHcWxPhoUXxtcQ4teeQ1hq3IW/88O/BSjKqi72NKLo8S9O0UsBr9BF+5KUECHw2h9Jb8TmtnpTTWuHNh+aZo2SEnncsMvjMs1P+WCZPY+7w6z+KQWe7UyoOiAdXxeIxer4fRaDR3P1GqfD9tUJvXarXkO+O1/tIcmCw7nnzlR2rSbiyZN3fMeV9repZPuH7mM5/BtWvX8KMf/cjxeCjgmBqAI3nis4183/Djk2k3DO3YssgoLdhj5VX+LQVwT/u+L2nLybs5LcEsLU9KT31ZxM4GMLfLi3anUV5nseOjSCAzlL+1rUJ6pUhQicaDRdbEEKKRQ9Zbo4m+0+SCJuNSgqLE9/1+38ms27dvo1KpYH19fW4hKt9xTQuotre38eDBA/zt3/4tHjx4gN3d3TnZl8LzMZ3U6XTw9a9/HVtbW/if//N/ot/ve+uY0vdnpd/I7qHFFbTbejabv3uPy/XxeOxWsReFHMdlj2sfX+f1afLacFKuhE4NsNKS8s2iUXa/0Q7gZrMJAC7+EbMJZrPj08OIZ7VFfzEsQreUhdMM6p5mWR9n+Gx+frXDWcFyYhcwTzefBMxz17KM50l9wkH2fr1ed4suYvaIrAu3mX32G7eFy4SUxdJG1SY67ty5g/v37+Nzn/scNjY2sLGx4dqK2oiuWKBdyz749CavqxZfLRKDkDpQ/s2xsrKC6XTqbDJa3ElXD3D6Qvn4aJDPeeyGn2Al+cTnq2r1OcsJxRSeTeXtlPiLDxb6Pg46ZNE0fhza4KwQkiPT6RTD4RC1Wg0rKyvu+cHBAYbDYTS/GFLG15wU1gQ7/ztloi1GQFkCKDZhEEojg4+p0NrFp+SlcvK1KacnFhi30s2VAk32yqP8+DEn1onYPMI59p2vbtb2CNGoBWli9FrGQRmC0MLHKd9Z0mjjNc84yFN/attUQ4CMHl8fWPi0zCBWaKxb8tYMFqsRoxl82nc+A9eXPlSeBumgpPCsNr7yyBWLvLLkI2HtB60emnNCfUtBX1rJaaXHV75FtsZAeqFerztnLaaLYtB43OfM+hzdEDh9VjslFacZNCK+4MeoZFk2F2gIyYeYPKIVf/xuJd93PnBbgZz9yWSCra2tuRXKwJNACXdirQ5xCmSQgPRELDAk7b8yHEhJk6/8MgI4ReQ5z0PSIseSJXil0SPpyrKTixNjRwFJ0OI/CobLFawpNFrGUZZl6HQ6c0f20u5Ia1Av9NyqL0P6kfpOpmk2m+4KEm0Bhibf6chdbUInj45O8QtD35cR2OF/+xZu+r6NHQVp8Xmlz8l17N7eHnZ2drC6uopWq4XDw0MMBgM0Gg2nF7SyLP6UBaGx65MLWh4xmZZqH1N5MX2VZU+Or7VOUMxmMzfhLdtY0ptlWe4jl6WtHuJlqcusNqhVl/MJ8V6v5ybJ+ZGVZUDyewiWmAnPl9OYkrecWKHJWF96zfaVY4DrhRRZVbZducg4BEdZdJ+WTX1W0HRlaCxYYxpFr4yw2BFWaDRy+8G3uIHKsV6Vwr/RbHbeHnScLd/cQfeQE32a7R2zC0Pf5PVXUmCRc1zHPHr0CP1+H9euXZu7osYXcwqVkRIbKcNe0/warnvJBm+1WifiLNIO1uIMMTtWji/iUb5IVKYLxUl848oXC/G1QxGE+qWoXS+fh9rflzYPYry2qLb8GeKwynSg3L6hXa60AUHmT7FNbkPyq3myLEOj0ZjbZEhpZBxO1kO+479j7XFiSUyIqS0OxGmiSFBPNlJZdZNptW8tzo4MmFocfR89XFnV6/UTuzO0gEzIIZH0pDhbKW0phbhGn6QjpGg1Gq2BNIuSle2gDdaiyieGIsGu04bWh75giMZvIePG+n9RFMmPnAXfvRZ5+STEhyEHLGa4+MaPNcgUgmYwx9LxtPzI9TKQ0v5agDtkqAKYOxrR166+fHzpi/APOa90YgLlxR1XH40xaPwRctApb186CW1Cuwi9Mm1Rnkopl3YQAcfttry8jNnMdueyhU7tvrYY30iHk09KXLlyBS+99BJ+8pOf4O7du+4bkmk0sW/ZceJ7FrPPJCiwHAqo8LryOvuMaolY4MZir1nL8iEkI1IhZX/M5rPSR3nTD927V6vVkGUZer1eLjkFPJno12xkjY+LtNXy8jKWl5fd7nsKNsYCEjHdWwY4D8vy2u02VldXcXBwgH6/bxqHlkAq5f8zHINsEOtEVpZlzrmndn706BGWlpZw5coVtNttd8LAyspK8O7Y00AooCaRR1b48rEsVKPyeLkpk7FHR0cAMLdryldWCu0Ebt9z2ZpHHmk2eko+RAPx6sHBwYkF2qn6NlRW2ZB6OoVOqpc8OSXkR8pytH6l60d4vqHd/pKm0/DZrX7EIsr+aQXnmTLjPj6bpwwUsfEI0+nU6SottsiD21mWuZNrrOVJv0De0Um7I/lkLJ08RN/6JmQ5fHRrtqa8tqCs2I0PlIZf8SH15d27d1Gv192d6L77rvl3pAO1974rBQgpiwR8/j3/XvoP3HclW7ZarboFNUQnl+taORaZS9+STmw0GnM7q1MXRPh0DKdlkXFhrdzTKkPzs4vSI/3v06jTz5CGWDw5L2J9Tde99Pt9dfEk+VxcnlAMkU4KaDabaDabcye80b3VC6M7lkHIMM/TwGUNGE2AhZQqQSokrmBCwTGZd0q7cAdAg9yJSgaKjwZrUNN3F06WZVhaWkK9Xnerp0ig+Y5UDrVF6EjCEH2cNql4LZAC2Rok0pztUEBVU96pQSqfk3saTlJZAYYywY0lS59L3g/RH6vraRs/VJY0/rV607PYQgyfg+drH5+RS0hZDa/JFFlH7b0vryK7TWT5cvdK3j720eTTO1Q2L3cymaDVaqHRaJxw2Og76/3gPF/exj55ktKfs9lsbuWwvC/BRws9L0NehZw8H3jbyQk/X8AihV6LPeF7J8e4T1+Qzp3Njneukd5vNpvuyFZyBmX+0uFJrYcG6w7Wp556Cp/5zGfw6quv4rOf/Sy++c1v4t1338Vbb72FXq/n6C8C3pch/ov1k7SvYvKB58cXeaQcvS75L9ZPqfqYBwQsaXhwIY989NmRIfvFWidfEEvTl2XoCqutwcvMI+N8NnPoWg9rIC5W15DcyQur7NQCTHzs8R14If/N6uf47CP6TTYBydJWq4VHjx7h6OjI7Rqm3ZHc7iIdTj7RO++8gzt37uDo6AjD4dB7pcyikKprNd6y2tt5YZHTGl38GPrDw0NMJhNcvHgRrVZLzS82BmRbabLSF5yOgZcd8iXyxkc0uyW1v2I+j8UHOmsfkVCUDh+vzGYzJxP4ffJ8B4TFxykSs8iLUEzivPTbz3AMOabljhtCzE6MyTu+APE0YxtUfllpffXQZC5/T34U6RK6soaP8dBpGNbx47PtZX6xPDjy+qg+2un/8XiMDz/8EK1WC8vLywCAl19+Gdvb29ja2koqU/KtL73mt/n8X63NpY+rfU/92ev1MBgM3OJ2Te9JPvGVJWmXtl2lUkGr1cJkMsFgMMBsdrxImiZsif/4DmS6FkRrH2uMJWbr5oE1tpHir/rGZaw83zjiaTSbSOYd08VWmsqGxV/7OJWTB9KvJ/js0hgs8oo2lVBampyl8Vmr1ZxvSNecDQYDVCoVdxIWl3mVSgXtdtvlzzdOWHzXGJ97jyn2NYD1fV5jwMpQPuFuoZEHBnyBFotCtiKkDLgjwPPVlErImdX+9wkvAGi322i1Wnjw4IE7G5sYVCIkAK2BqlDfUBtQOt/A1ZxgLZ0FPJ+YY2pxXEOwjqs8QRLLWMk7nrR8FmEY+Mq3yJOU8X7ajoo2LrmRpxmj5EzwldUSvnEdaiMLn/u+pW+0MSPptvJ6HlpixpSVBks5efLQ5Fa9XncBRc0RpICghb99cjEES7CQ+rZWq2EymXhXx+ZBjE+tjqemH3neoWPiTjswIRGqLzlxw+FwLmhQr9dRqVTQ7/eRZcc73LhjZzVeU/pNs1N8QYJLly7hy1/+Mr74xS/i1Vdfxe7uLsbjMd5++20MBgOsrKyYjx/z1Yec2dS6cDkfcuJCZfP/U3lf0s3pkG3is4ti/ZvHTqD+K2qnazKc8i0iz2O2L5VjdbaLQLPJpB5NQaw/Q89TZDHvh6K60MKnnJ6YzPXZQ76y84w77X9qD7ort16vY2dn50QwkgdruX9DTvnt27fnnG95RF5R8D5L8YNTZTxHyLfTeDbGT5o9S/+H6ORX5HS7XRwdHWF5efnEMbKhoI5sC83ul/0VokmzfTWdktJfIYRiBCnfx/RWXll0XhDiQR//+fqHdtHxOIw89UB+Jyf2NTnG5bbVVtMQst+L2uhFYyjy2/Mir84SIZlpGZc+fe+rI/+fH9FeZAxLX18rV0LWV+pen26WZfCFk9ze076XY5t+SIfSccVA+LScUP197yUNZcnMkBzR3sdonU6nePDgAWq1GtbX13HhwgU8//zzmM1mzv5JGTdWfkgdi6n2JfUlbSQi8AVWeXwSSRMvP8uOF0WPx+O502PIh5e+O/DkSFRJgzYRq7VBaLxz2hYFn01SJB/fu5iMs4y7PP1dVh19iMn7RZdznlG2/OSgE7ionF6vh8lkMmfvAXBxNb5ogh/rTmnoLmr+jPxBy7iM8WV0Z+xpY1EM5duBZAkG+AxAOYgttMfSEGOkMGkoqEHodDrY2NhwdO7t7eH+/ftuRTjPq6w+sApGvsKcjEkOeQSIZujKozC1culoPGncWFcoES2+tJoiPQ8OghUWWhc1Pi2BAW6ca0gJ/qfQouXp40MJi4yw7ujL68z7jokto60ojzImQReNvG0ood2pHcNwOHRGvDz1gMugPAEV/mP9hsqVfVapVFCr1RydRfqU5Do3iKTRo9XF4uidJa+lGlrS0QqlfeWVV7C5uYk333wTh4eHzvhLOR7FqsNTeI1WBY/HY3dfZgi0k7fb7SbvKJX8HLrHj77hz4jPUsB5j5dtaSNNvob4mIx6vrLSuktLkxUxW9WKGH/mDfbywBrRzu/uS8Fs9uTuU8vuf62N8uDChQt46qmn0Ov10O/3cXBwgNFoNHcPWajss8B0OnW08vtis+zk0YC8r3gfycUQcmf1xzEAUCZ40JeOK7WeSkFHmxJvvvHGG7h58ya63S6GwyE2NjbQ7XZxcHCg6nepUxbVF3nGKJ1UQceSy/cWWnd2dpz+0BYJc9q0k6SstMp7zmU+RdtW6rM8+aX0gdXWkJMdMf8qtezzAF/sxmLD+2IOMaTYX+fdZ4pB81VDOE+8cdrgk6QWf8GSH4BSFwRR+UXB+cJ3F7WUiVwWEeQuWcvxsPz6Cl/dKI3PT7DY8ZyWssaxbIMiMR9qO/Ll9/f33ULrvb09p1O1a354XkSXpMVCY6xdrG3nk+O+/uNjTd4jr21+0r6XmyIollGtVtHpdDAajZwvPJvN5iZvCGQfEvhEEOfR86Y3Y4jNTyxKt2m+vqRB88dTTorzoWx/LjZuPk78UBRl1nc0GuHo6AiNRsP55xx8kY6vLw8PD9Htdufoq9Vq6HQ67hlN2PIT7YrAOxlbdGKmjMEYUqaxbzTFITsgJX9LoCpGm0VpacG2VCEgjSDgmHGWl5ddv2xtbWF/f99Mn0xjUbJanqF+0RBzDmNKgZdDilDbtULQFLWPXzTapXFpQZ5xkldwnScBLycq+f+acA61aRnB6CLQ6E9ByFGh/33lhsrTvgsFXbQ2jyEP/1rpLdOgK6sNfTJLBvD4D903IC+Ozxv8In7jgb5QPXx8IOvD9YbFIdXylvlK45gbLpr85uWFxrO2e0ELROaVCRaHg6fjz2WbxfiaHLnLly/j2rVr+OEPf4jxeOyOReG7lS26Lo/t5EtP9aGJGGpjHtDkd85RWrJlijpCKXTzuqeMK8lH/LeWj+yLFLnF2yxGo+Z8hhyXGL08ja+dQnwraapUKid2a0t65DspG7X8tfYkuvlYSLGJY2X4MJvN0Gq1sLq66u7UJpnGV8D7ygrlm0pLKsbjsZuI1ewTzc6gcavJtTJoDQX2UqHxV0peXIZZ5KpsA2ory+SOfCfb+OHDh9jf38f169fd4hcK+PFV3lp+0vYIlW2VOSnQ/J7ZbHbCluBlxGRYv993xw+GJhyK2oih47K1MZAKjdc1O2XR8OkoHnMouwz5/LTqGoNml9Iz+glNzstnMh96H5K5KbI0ZoNLGrTvLWXE8smTbwwp9JZh1y4Kckxrss/iR4VkjmbfSj7S+C7Gq4sA16uafLX2sVbnmM+p5SPpkr5oqB6+vELPU9rWpx9jekIrQ9ozZC8Ph0NkWYbd3V1nU3A7XPoFKbaMVg+r/8zThr4J+QQaDXJMaGl9/iL9TfwxHo/dZh7ydfmCUjneZf9rY9UyTlPstkXrWJ9cylNuUbuK97FsU5lvjE98zzTfyPdd2TgLeV02fL5/CBbZE8qf/h+NRicWgXIZIL+hE5MIdPS5zJd/S3YinSybtx6OhtDLmOL7uIKUsFW4yW+lkZ4SfOFpSbjTO+14yFRkWYaNjY25vAGcuHuH051lmbqqJwZpyKR+T8d/0Xd8lbg1T+mk876Rq7K1QSgNMl9/puzCsOCTNqbKhGYcaf/z+/wWSUcKygpqaPeRxRw5n3FhdeQtBrYlYGl5Xha/55FbRSEN9ZBDRneL0OpU3xHAlvbQDMUU/SNBjsXy8rJzMKRelEauhb+J5+jOBvmO3vN6l7W6exGBxRQjWNsFpIHopDuNXnjhBTz33HNYX1/3pregbD2SZZmbEN7b23M83263cfXqVWxubmJtbQ3Xr1/Hzs7OiWMlrWVkWfjuaHmySR5nLmX1onSs5U5C6/fy2FHa6Zy6kjIUWIqB71KjcUnBGS1wEAIPYjWbTbTbbTdpwncF5pVJXDbINiwi63xlabRS2bVaDcPh0LVTWYgFWFJl2Hlz0EPBKC3g5wvqyABWKFAVgnTC+W70p556Cu12290Ddu/ePQDHi1cpPfFfu91GlmWOz8tYDX1aoHr4bI2UoF8K6CgvbcJT+mM07uRCh1RI/5HXnZej+YOxfOmOYb7TvAxovg4FeFNs+E+qH2kJhOaVg9PpFIPBAM1mE2tra66c/f19HB0dnSgjJSirpU3Nw4rYuP4ZFg+uu7n/w/WFdSxbQbJNxkPyBIQXCS7LgPmdpQRt96IEb2Ofj0YxTdI7tCg0Fi8I6Ue5eENbrBGLZWo6LbQ712fzhPiH6CJflPtktKCrXq+7XV10apeWL9c9PjvNVz5Pa0GuCYxabW6ShPOPz68hX47ah9sIBNnXg8EAWZZhfX3dvdvb20Ov15vrc/Lna7UaNjY2HI+PRqO5E33K3s1+FjiryUGNT8hWOg1YTuFcBM6LHF8UytZX0rdcWVnBeDzGwcHBCdv9tddew9e+9jUAxwsv/uiP/gi3bt2aSzMej7G7u+vybLfbueJdPkSPKdYa5jxNfiyqLIviKRIMl99qK9FjThgPVnBGrtVqc9uz6Rx7CiSQc8zp9h215yvT917Sq+XF21YK1DwC3meQyeOPpeD0CdJYGklzkckQWd5pw+pQpkzIFaFBGwOSDh8vUXrJcylOc5nQAo6SRgDOachztKbluQxuWvNN5UvrWMgzkeJDGf0W4rnY/yH5wScu+KSMhtQ298lQ/t7K97VaTV3Y4OtPqyzgzicPascQk8lW2mJprUgJ+lvAd9PRncJra2u4cOECOp1O1LGwBCRTxm6sfWgF9Xg8RrVaxcWLF7GxsYFGo4F6vY5arYZmszl3hyLtLEkZ60QH32FLd/PkqZu0J+hvPjZi+t0XDEkFp4X/xGy7suxvnwPL20G7TzHGa3xBnWXc+p758peyVtIfy7eIbSaDhBTMkwsdQ/IwJfCa148IgQcifZNHkjc0+4G/Sxl/Mf0Q+tuiLy3lc7uRB92yLEOz2XRHzsnytGAPyUIAc/fHWnxBH39KP48CfLSAK6+PGaKhbJ8jNPZSr7DIG1QL+QY+XgvJF5l3Edp4Plof+GRdKI0VFt7R8i5qN5UFK/38t9XOlLKGxjnFUPgxlFb9WIavHNL/5xll8Uyq/XqewMcuyR6+09rXtyF9FyqL8pIyI6+8yAs59nxyLi9C7eaLAVIbcFq097IeeWIUvnI120DSa/XV6RsLeNkcsaOZZV20/08DMb6x2JZW/yqm+7gdTfqhXq+7yZjZbOZ8VUpLfhHtuKNYkDZRH6qDtR4pfBEqKxVFfDzLN6G+tOjJsuoWkxuh9D/NsI5FnraM/GkckgykuJJ2nczKygqeeuopAMd+3eXLl9Hr9VCv1zEej/Ho0aMTMXo+lrl9KO14a728k7FWQVHWQExBilIKrbCS+cVQ1kpYHvyO0RASnMQEPNAiDbpqtYqlpSXUajXUajU8evQI9+/fVxXybHYccKWggw+kSPjKP3n8prznVXNiY4E2Otvb6gDL3YShNpUDi4KKGv0+UFvL4NzHacU8cHaTlSk0kLDzHXvG0y0yiMCDe0Ud5ul0ikajgbW1NXS73RPHhluhOXExeWJpbwktTx7cTM2vLJCRG4K1j8qgk2QCv+xdIsbHZdIjYdGfPDiVZeFdi7GyaIcZ7RL2lReD5uRrQTIqV+Z7WsZxKKiSZZm7e3U0GuH69et47bXX0Gq10Gg08PTTT6PdbrvdqD69WaQ/ZF4hmmUg4VOf+hT+2T/7Z1haWgqW0Ww2T9gAIdAYbjQa6HQ6qNfraDQa2NnZwcHBQdQG4DI/Rf9KPk+Z+NDSWhzoarWKer1+YoFGWWNdOoqkN8le046t9dW5SECPJpLkEa6h8ZGiu0M2WtnjfjAY4PDwEBcvXsTS0tLcMdw+SBm0CPkTChZUq1WMRiN33BIP2PJ7rlLoP83g7scJpOdisoe3pZQVdDLF66+/PhdIaDabpdibeZDHltYCz0WCcym2QR5wPxp4EjOgscHfaad9+PLkO5S4z6CdHkBtJuXJafZ5meN7UT4Y97+KgMYp9Wer1cJ0OsW9e/ewvLyMCxcuYG1tDevr67h//z6Ojo7cYjNLbEDuTvPVBTh/k4kW+Pr3pyEArdWd25H0/2AwcMfNVyoVNJtNZ/eX3U6SJosduihIu4jKl3Eyyf/aPZq+/LX21+63l6AAOu0QpZ2V/F5PjTb+t6Xv+D2lRK/0M3haYP4EmFD9Zb19NEn9Axzfp9jr9U7UVeqfULzW5ydxf9xn4y8yNgdgbrerLI/Xkfslmg0v60qnJ9y6dQsrKyu4du0aVldXsbKy4tLfuXNn7p5JoqfT6aDRaKDVauHBgwd4/Pixl/5Qf1rH9GmOfd+ViqnI469JXgrxrnZHdCzGKeUQ8U1Ip/+0oeh4LpM/Zd/1+30MBgMsLS25WGy1WsXa2prb5aqVX61W8fWvfx3Acbzr/v37+Df/5t/g8PBwLl2v10O/33d8QXq+3+/nqldwZ6xFKZzGwE/pbE3Zy3qEAqU8jfzfR4/MLyZItcCUte1k3twxoF0qlIb+55O/dGyUPJaSC9VYe0tlHKt/KMibt97yGafHJ1C5UI0J/xBdkjes31nTlh1IXEReIYSUXJ7+1gS+hcdiZZXVHhqPhcqkI8OHw6G7f04ajNr48NGbKndD49XqYPvGQEw5l2GM+2RUzLiKwdKOki9D9ZGOpUVPpJSvlZcHXP7H8vC1cdGAaSiYEMpDkwvnKbilyT3Ov41GA6urq+7Ik52dHWxtbWEwGJzI56yDKsBx8GJtbQ0rKytYXl5GtVrFwcEBarWa+5/oTXGqgCdHZ5GjHFtIF+IFSyDnNNpT0p96RyWQFpTi32rtw4NCmuNv4TPiC3JuarUaptOp20WkHeMUq6u1L6RDLelPySsFvM6xI7PLKi+Ut8X2tvaltD2s36fwHi8v9K1MZwXna/rbNwbonuPZbOYWRjQaDXWHN6fXoiPz2ja87fmx2Pyu2FTbWdocVn/N+s6Xp88H98kcmR9PY6HVaoP67Avf95IPSN4RD9EiaOuYsdI5m80vMDstvy0VMb6J9XVMf4X6KMZ3VpC+ookyfv8XxUh8fpi20zEllmOBbMNUO7AMf0vDeeTJRdVVwloG6RM6UUZeS1ZGGRKLskWkTonJYW0syKPii8QJUuwIX2xILojmNFnyL8JrqXZdEWh14f2o6UVpF/J8Qm2jfa/pPd93WhqLLom9k9B8IH5fOG0ukvKWrpqio4j5xFy73cZsNnMTNIPBwO2gJdqazSaWlpYwHA6TrvkL8UUZMi9Fj2q6ODZefePKNzZTYgcaf2i2A/d/Q/mlIGZH+tIuGudBF+aRYWW3GcULSAfzeS+OnZ0dvPvuu7h06RLW1tbQ6XRQrVbRarWwurqKz33ucycWsdy5cwePHj2ao5XkCC/f2g7RY4qpECvKNgZiOyp9kI1CSN01kVJmiBbtb8pXuwDYl4/8ngdL19fX3Y6aLMuwurrqXcXRarVQr9dxeHjoVotlWeYcf06fhHbXrVY3es+f01FvsbtcgfCRULJNQ2n5PYgymJcnuOajt4x74IoqpzxIdfDKQEp51p3RRXeuW2jypbHupK5UKrh+/Tqq1Sp2dnZOfKMdg2hFTDGGFEOK8SPzLIvGFEj5UUaZMRlAsmI2ix+5Ox6P5+4IOi2kBIrlylCrPLKUTbrK4rDllT0+uWB13spGbIxpqNfrWFtbw6VLl7CxsYH//J//M15//fW5O0o53+UN9uVpB34nsvz+0qVL+MIXvoDt7W2899576HQ6eP7550/cTR8D2Q+VSgVra2vnMshHSAnU+IIJ0vaxlufLzwKSV1qeMdvJl99kMpnb9VGv19Hr9TCZTHKvHJbBHl8aspfL4hUt8LQIcHkX678y76M8LVgnpbT0ZZRL5fEgmgz+ytNVms0mVlZWcOHCBRweHuKdd97x3p9GO4w/rpATh2cBX7ClLP/D5ytpk5vEEyljjeQd3ZFNE+cp7Sp1ufSPJZ0+nFbA7SzAF5DPZrPo2MvbDmRnra2tuT6N7WYl2SLjCMRLoYA56WKNH09LXn5SoU0EnSUtnU4H0+kUBwcHbmMEh6SziAzUbMSyIPOK5e0bC3LDhzbO5Lc+21TrY2nbphyPz/3VohMCmp8m78ct2lcpelOe+NBoNNx1dZxmnq+8h5iD2jZ0/65vt6/Mh95ZfCmut612RMgOHY1GzncBjn0zOpVqd3cX/X7f+93BwQHa7TY6nY4r+9KlSxiPx7h58yYGgwEePnyIdruNS5cuuRNq6NSFO3fu5D4JT6u39X1KjDMPXy7SjvP15VnJe+5jyDY77Xi6xFnrvyIguVDkShBef7oXe3l52ZvnG2+8gR/84Af47d/+bXzpS18CcGwb3rlzB81mE//yX/7LE9/+3u/9Hv7oj/7oRLk8BiYX+IVgmozlBQHlM1psgiBPuVwZhozjGB15lCb/ht/FpxnyskzrZAk/FpjubpOrOLmSGw6H6PV6GI/HaDQaQUUaUmxZls1NFskJB26IcEXr6wPNcNGcVEubAE9WuoaOAZVBI1lfXx+kPk+hu0j6FGWUQmuqEZ63XJ9hJfsplNZSr0UpqFBwlfP+8vIyms2mc8qy7HjVLCkLehaik+cXa1NtTGr5ad+ed6QaYHmM0lDZKbIglraIM2alIQTelqRXZIAy5NTI5xRIkxMPvj4r0o/a+6LOdN6ygbD+pnaRO+Hpd6VSQafTwcrKCg4ODjCZTNxRZ1o5qWM3lt4XGOJ/j8dj7OzsoNvtzjnc3//+9/Hhhx9iNBphdXU1yC8+Gmaz451qjUYDk8kER0dH7qqClLYvouN8efjK586Y5HfgSSCk1Wq5/qfgsrwPUtN3McTkoFXG8Pf8fjP+juzWarWK1dVV1090HJBcjBYa74vWMbzeReQsodvtYnd3F51Ox9m/dNc7laeNn7L0Th57mNve0h4PBcdiu/JkAMJHQ1G5a/GFiupxagvLdTaW/Kx2qMYXRE+j0XBjzXqdQZkoy8+38muqLSWDsnn8RK1snm+Ij3k633HlMR61+rghxGypPPnLce2TY6G+s/irMcgJBCtdFvp86ekbOuWBdjXRD/UzjUntOE6SJT79yelflD+qwWLPSDpTbH5fXnm+LYrTbFetTE2mkC1P12MRn+QtS+ujPHZOGbaRD9q4pTKljC0S15HpLXEjGsuxfvOVw78payzLttLqlDLutDpTGs5/cqJY5iPpo3xCE7YyH4uut/p41j6WdMv0Gp2EZrOJLMvc3ZKj0cjlMZlM3HHEdKoCHXct24J2yFL+dALL0tISKpWK2wzlq6uVD33P+HOLXPflZ4k1LlKWSJpCfCR5hH/DN1MUkTGUFz2TuyH5BjD+3JfnTwu0sZhqY6S0Ge+z8Xg8pytbrRYmk4k70Ws6neL9999HlmV47rnn3BHk0+kUR0dHaDabWF5ednk+/fTT+PznPw/gWCbcu3fPnXbJ+YKur9EW73EkTcamIq8zoP1N+aWUnXIMQMxYlUrH53iHhD4FYiVTxCA7jpQDgQK4ofz6/T62trZc+jygbd58MpYUFvW1xnQhuiRCAQ2Lk0dlhwwEcqC1PLUdJD5I4XxWsIyx0xD6FqFqNbjO0lFN/TbEr1mWOUF/4cIFN05pVSKdXW+lycJzqUaA1egK0fPTAnnPI0eqfgo5PGXD2k8UNEgFp5kcDoI2uVsEZxGgLoose3KnBKCfopBlGS5evIhnnnkG7777LrrdLrrdrluxK9OWAYsDxdt5MBjgzp07uHLlytz7//bf/hv+z//5P3j66adx6dKlE3fIW0B3xq6srGB3dxc7OzsAwveucfpksEFzQmV9ZFoZRI21ixYk5mObnLKNjQ23Ans8HqPb7bq7q7Rj6nlelrpr9qqsqwV88aDWbsPhEKurq7h69SpGoxEGgwH29/dP3JXE6eML5IrKTQ2humqBpjzY29vDZDJBvV5Hu912x9tqd41xfiwStM6TTqan3xRUooVgoROHfBNLvD6nbaP5cJY0SLupjJNZsixzx93RHeuL1nOpfWn1N/LKIMs3PKCcJ7Dmyy9EjxYjqNfrTtdJ/cPp0Wzy1ODlom3zlDYs2t6xvCiOQHd9ke5IPX0qD8bjMXq9HjqdjtspNRqNMBwOMR6PXbCNjufnOkAGZ319cJq2axFZvSh/5JMGPuY1HuUTOABO7JItWrY2SXUasJTpkxWh+K60x1PsJKmXtfIpX99uZW6Tkw1flo0doz10h6kPmu+jLY7k6fg1dVrdLPHWkM6TNqhWF983oTrKby3Q/BlAt4PJBmu32zg6OsJoNHIxDJL3tFgagFtArPUbjXsqk3bNXbhwAdPpFDdv3vTGyxc5llPleix9qi1TFCF65EIu+R2Xl7G85LcA5hZJEmhuBJj3BX6mO+PwtT/XaWXw1XA4nFtIt7q6OrcRCgD+9m//Ft/73vfwu7/7u3juuecAPIl5ra2tYXl52aV9+eWXsbS0BOB4fu0P/uAPsLe3Nzc/VqlU0Gg0MBgM5ubstJiW1yJIMSJ9DqMW8I+lC5VtgYX5U9NoCj7mJMtv+NFZMqghvwuVTUGVkGKi1ftUrlYOHcnDy9DanX9HR+wRjUQLpfMFYDU+4OlC7cG/kUEhGQQlA0MKSr4aRuaZouCLrGSM4TQCLlb4jKhY+pTAUaz9tWCGZvDF+C1UD86L8ggdq1Eo68yFLK2C5XdCkqFPyiDLMmxvb7uz7ENHr2rlaXTFxrHM0/c+BWcRFJVywGJUWfRPnrESexYrT3NQYnlZg6I+8PtRgJOTplp7as4Xf0fOpHRoU1CkPxYJacRr72OYzWbuhAoAODw8xK1bt9DpdHD58uUT45/ulM7TBlY+4rRpfBj6djQauR2s9D05q7SaOHU8yHcar6UGaYB8Ky1jgQUfuI1WqVTQ7/fdLl+6w2Q2myWPEe5MpoKv1tVkf0rduO1HkHducoeEPw/ZXCH7mK/kl9+mtofc1ST5lAf+tbtEJd38Ox898lj4UD4pSA3c5G2zPPC1m0/Gp/idWj0ssoZPlPB7IbltVgRWX9cyjlNluDUvjb4i+Vva3SdzUmWrr2xtDGtlSVpCwSBKRwt4Dw8PXfCV0tApB5Iea518Pg6g+65a3cuAVQ/E3mt+eYjXpf3O0w2Hw7kxm0p7yMehduU2MAXLNVrlLmhNlsfoKwN55Lelb2Wa82J3fxzAZQW1W7VaxcrKCn7xF38R29vb+N73vjfXxnwyjJAn5kL/y3EXSisXy+ZFzI70yWbgyRHhoaOIQ+CTLho9Mp1GX+go45Cvq8WnUn0di88fagNpK8tFyjG/UfroPj3s4yvfs1DMTqtnqH80hGxsCR8v8ryIByj+MRqN0G633eLZLMvczle+8Ibam3a80w7Zy5cvo9/vY3d3F7PZ8aI6un6s1+thOp1ieXnZLeicTCYnjuGXulD2Tars99miMf7S7KQYf4bkWUxu+fLh32p0hL73+WJ8/KT41Jxvib+Jh8iXqNfrzk9OaeNPEny8aok9xNpM+yYGfvT9bDZz9zbTCV++hX7T6RT9fh/Ly8vodDruzujNzU3U63U8ePAA4/EYP//zP49er4dGo4G9vT388Ic/dLxA83F8YYfE3GRsauVigt6CULpUwZPHqLFA7oi1GC+agRE6FoMjpJh9d6dwBUyKhHe6VHi00pNfWh5rbz4ZS0f58PItq1alAtbqIOutvdcMANq1y99zR1BOAMdolPSkCIxQPULlWL5bBEJOrC9tilGk5eGrt8Vh1MrNG0TgxgopVAukkUMOFn9Phtfm5qZ7TmlWVlbmHBJaUaMpaD5uFu0khwJaobSLRsjR4mnytk9KHfMaxzH+TkFRPiA+5HfkaffcWo1lekZ8X/TuyBSZJA3qsvmyaICSMJ1O0e12Hd8eHR3h9u3beOaZZ05cHZBlmdsZ4nOoYjQVCaz5eFUGS2kyluo3mUzmVg5byvC1n/Ze9nNszIfeW3WNzybzgd+VlGUZhsMhRqORo6WMo+pSIXWH5hhb89Hs0NBKZIIMIPn6VZPrWrAoNSghvw+lpUApPypTy88C3teLuA920TZBEcjxZ5Hjvneh71MCOwR+hzPZb75FC3lRxC8t2q+xYJjl2zLHmO++VIuusergFB6QssQnE4k+Op5/c3NzLgiXsmBKSycXg2p0ntYYl3aD5mtbZWgR8OA3LWTl+fr4JSV2QPYK6WM6uk4LjtNOWHlnecqYjqUN0V/EltPy89GzKNnw0wJqk1qthvX1dfy9v/f38O677+KNN96YW+BFiwCK6AfeD1qfaKcIaXanLzam6Y9Yn2vyUKsX7TCTtPhiDlIm8XSW2I7Mn+S37ztKo9ltZYwRX/24/I3Fe6Rck30f6688ekvmSeVaJ9VDPMV9plAePpp9p2bx/LiuJTuf9MBoNEKv13NH1FM6iqdLnp7NZu6alnq9jkajgc3NTfT7fRwdHWE4HLqjS2u1GrrdLnq9npu4pclYfrqkVkcZ3/C1pQZL22nt7fPRQvnIb8q2o306N9Xup7HhW0QXiyfItNyf146m9dkfZcepJKy22iIR6itr/CX0PuQPyvQkp8iOoxgbTczKtIR+v4/JZIJWq+ViORcuXMDGxgYODg7Q7Xbx6quvuvQ3b97Ed7/7XbfggvxMmpPT6rbQY4rLhFUYlwnNYMnrcOUZEFL4SsOh1WpheXkZs9kMR0dHjua1tTUXYBiNRtjd3XXbsflxC6FAIC+X7piVq6joHhVffWUgNRZkicEyOFOOGbQgVfifZyxKMPt4IAWynTV+WlSbxwJCHJY2lDuHGo3G3NHgdE/snTt30Ov1ooqfl50SyLUqs48TZKDImtaKMng4NY8yjLPUMn2GZxl14CCjRzuhQQskWOj2GfwxpzVv25ahR4DjiUsy8lqtFq5evep2YhDIKfQhZoNQ+4ScOs1x1vJstVqoVquu/8iZ/O53v4tms4nPf/7z2Nvb89KR0ma0+jDLMvT7fUynU7dikdeHLxYIOYk+p15COrlF+loL/KR8m1fHhejNU58ybQSus7ieDQXI6Ecb59bAgjUIoPEQfU883+l0cOnSJayuruLx48fo9XpuhTun2UdDiNZFQQtuyUllolELMNHfvrqFkGVP7tLktHzSoAVkYidLaP6E9PFosmdR7VZGvtagHKX12TihcarxZlHQuJ7Nniy6JF4lm533kfw/BVpAUuomS555bbJFIq+eojYNBVZDZVrKtfBUqEx+2sfjx49dwI7f8e7j+ZBes9B+1oHTnyE/pM4FgP39ffzpn/4pOp0Ofvd3fxePHz/GgwcP8OjRI+zu7qpHXaeUZ4lF8LT8mhTS03xB0mw2M59o4wO3N4gGn53GaSPZIN9rNhqnj3QvTSymyFatntJforbRTlMhmnx6XualXZuh+SvWtvfRpLUDpbPo1FD5Vt+K0xPKtyz/GtA3PMm8LdccEf+2221MJhPnm9IOWZq45by9urp6Ih++QWgwGKBWq7n/syzD+vo6hsMhHj9+7MYkyQNffIOfPHYaCMmCvD7raSBEd8oxwpodJ/ORz4Hjvuc7ZDldReRrCj7O9kRR2n12oHaignz/l3/5l/jxj3+Mr33ta+5qwd3dXfzt3/4tNjc38dRTT7kyXnnlFXS7XfzoRz86cV3TeDx2c3O8bA3Rydg8RqRmlKYomZgiSP3Wpxit31H6FKHjG2whJ5W/5wrDF9SgQFG323WXhNPsO303Go1weHiI8XjsVmr4jvjz1Y9m92UavqpKC9j4jCLtm5hwsra/dJ5ln8cUcOh/SX8IeQwVC4oIqLIEs9UR5WVaeE1Lk4dmzYixyh1LoFH7m39PxhqVz1fZ0bvJZILHjx+fcMIkv1rHqEZPUZRtZMUCeNY8ZB8U0TUWhGjVHJPUAAwhprt8sjbGEzEatPazfBMLEnIdFOLrkJyNjbdQ+VpaX/qQs2FZbWsBlwn1et0tmiKdTccd+mSpVQf66uqzy3y8RY4jrRqk++7v3LmD+/fvY3d3d+5OHV5GHgebjn+hVcJyJSGn1Xc0mWbjxejg9Q+dEBKyVyxjiOsHHx15ZIbPvvX1ga+NYkGakP2sQdbHYtf7dnIAfkcmlr+UIVo/ajqDFiw2Gg20220sLy/j7t272NnZOTGhIL9N1d2WesTy0nhStr/kF+1v+b8WjLD4YRRYOg3wfrTSRn/Lk4HouUXv8//lLqdQ+fw7mScPxOWBpf4+vzPmB1vz095LngrZNL48LTrfYpfQbzqmmgdV+biRv3keoXpbbF1LUFjmWaZNbrEtfUgZb6GyfbYzvQsd459ars/+4XnROKaTwwBgfX0dzWYzaJdZwOVh2b5aXqTYwj9DHNKe6Pf7+MlPfoIXXngBv/Irv4I7d+6gWq2i3+/j4ODgxC49zW7wIWWs8m9msyfHc5MM5LKI7wovIm+sMRfeZpqtoeUn7TT6xmpvyvJlemnrSL0Vy1OrQ6x/U+wrH70ptraWj++Zpu9i9m1KP1gWB1sRG0PWuEGWZW5Xm4yv83g65UPPqJ24XUlp+bUD0+kUrVYLtVoNBwcHrg1CR9tKHZSqt32Q+VjHjvzeSpfP79Dy0mjV7EUfHan5++jWxqfsC9k//NQdS9nWdB9nWGzjsm2QkM8lMZvN8OGHH+Lhw4d47bXX0Gw2UalUcHh4iMPDQ9TrdTz33HMuz6tXr2I4HOLmzZvOZqRd8rSLVtKhxWbUY4pjTkwIKUEaWe5pgYShLJ9+pxwnZnFUqUzrN5I+YF5AZ9mTI3P4cYCbm5vodDpoNBoYj8e4d++eY4YQ08cGhxQw3HGdTCYYDAZqn8aMDhkk0tKE6AL8F7Dz59YgyVmgDLpSDPjThoU22V/8Oc8nb/mzmX70MO1C8/G8pFsLDND3NKlCx2n1+/0TtNOkBV+J6gNfUGFFzABN4ZPzOl4WjbICXjxQZXVgyh7HmnPLQXqDgr9Fy+Z3HtMOnzL5KKQviuYXgtaPRdHtdvHuu+/ixo0beO2113D37l2899572NnZKa0MDh4IsNosNDlMgZuVlZUTcuuHP/wh/uZv/ga3bt3CbDbDwcGBc1itdFkcEH4yB989nBK4kv1odXw0xytWNp0mQrbX6uqqs9kGgwF6vV7wjnAgvOo2D+SRSXllz2g0wsHBwRyddMR+WbTyHQSxALoVVpuU0tBRU3QMES1GWDTy6IHzav8RZAAjNeCcMuHhS08Bskaj4YJrV69exac+9Sm0Wi08evTI+VmdTmduQuaTgNPmEc23pH4h2UfP+Gp17odrvnAeOsoC0dfv9+cmMfgpUan+ps9vLiPAmpoHjRHyyWYz/yIDmb9vsoGeW3byUJn1eh31eh1PPfUUqtUqPvjgA3UshoKtFjuH9Bad8EG+3IULF3B0dDTnx9F70gGNRsNsF/JAbZn8eJ59/48LymzDLMuwsrIyd0fc/v4+9vf359Jcv34dFy9exNtvv43d3V20221kWTZ3KkLZ/UpjmSaVJpMJhsMhWq2WOjZT7F2tHJIjfNctT6P9T/JPxj80GalNjvjyt0LLq4w4cOgbywRSLB++oIjnq7WBvCpAo0nbwUvvJd2+iSuffrD68T7aeB/JxbMptgJvB2pDokku9m23267MwWDg/AG+iIF2vNORxNeuXUO/38fW1tbcpCzF5mu1Gq5fv37Cp5bXwPiO8rZedVNWHDWU72khr00j/5Y2lsZrFr9TPqOjb3mfSp7U7F5LP54GTtue8MmORYPbccCT6z35ccX9fh9//Md/jKWlJXeNGHB8ZcZrr702l1+9XsfXvvY1R/vt27dRrVbx4x//GH/91389V660EQjqzthYA/kaK9VhtpRVNnx5a0pHU1Y+4yClTE1Jad/4HFnuKNE9ZCQAaFs8nX3P7wP01TsUwPA5VhykFHj6Mh3KECyKxmKYpRiai0QRo+60YZEJ8r02zorAwp8Wuqzl+OilCVZuHNJ45EEl7nBY6lAmv1mCE3ne+QIiviBJKsrIJ/RNGW2cEjS2pPHpH0s+IV7V5CUPluXlEc7XnK68x2qHHLWYzC9LHvoM0zJk1nQ6xdHREabTKZaWlrC9vY27d+/OHWmT14kiuvM49fxv6SyQ3UH33Ozu7mJra8sdtw7AHfVo1Qe8LHqufU/lEw2W+2jzwmKP+dJzPUcBr+l06hx0fj8Q76dYOVqAJSZzQk5j6BtLvvz+W0vbcH7U5ISPbk5TTPbwevryt8hpKcvomLLpdOpWycYQqp9GT4yGvLDqZgs0XpXvY9+nlmf5NlUHcF5qNptot9toNptucgXAibtjU2Bt31D7aTx9GkGKEMoM1MhxKmVhij9m9S1CsQZNVtJzyYekvzlkYFcelxiDlI++8lOQp3z+v/X7kA6LtaUvv2q1ilqt5oJWIXvYZydZ2o5kuQx+12o1F3zjVzuRfRLipRh87ZBnfKfyRqrMzitzUsdxWcgzXsqmkXbbA08WpdLkJF1ZVKvVnC1Bix25vi+jXyw2OC1s5BOmPn6M0eX7LtVu5rInVAfNNpL2pi+dlrc1ZmHxS0PvY347pyVFNoTsM4v9lGonhsa4j45U+Rwr16enfP2o2QSyrbVFSHxybTweq7GSwWAA4HiRDt0JKxepkj9Ii7mGw6HzC0mvcBuCaI/xb6gtNd8x1i4WWH1IbfxZ5EisnFRY84n53yFoV9f4xqF2B3XML02hWfumbB/tPMBi/2tpZL/IMT2dTvHgwQN3KhZNnj5+/BiPHz92MR0a6xcuXHALOgDgpZdewng8xu3bt9HtdtHv992VX9oCi+AxxSEDOC9SjIxYuXmMWM2J0gZ/kUCKzKtIGnpHx5xSufx/chwODg5weHgI4OTdc6RcUpws2modEvLExDLQRu8tsDpNlsvmqZ5yB0MqTdru6JhgO+tgyVkhZlzGFL7FyU6hheelCT2elsrxBQl844WMJ+Jd2gnFceXKFdy4cQPT6RTdbtcdecJBDhrBN5FxGrxVJEgbe1aWc24xWs4SFEgC7JOLWh4SqfWVKzt9DumioRmlMQcv1lbnnQc0UOADOL6LlU6UkHj8+LG7Y0LuvLA48ZTON5a1Poi1N92p9+jRI6ytreG5557Do0eP8B//4388sXMkBVp96I57GQANgWwQ38pwiwMi9X3IsKf2ih2bmxdlOp+W8SS/oXbntFAetVqtlBW8qYGfEL0c3E6czWZu4julDSzw+R68beTRp9bgWF5aNPByiWe5nT6bPdmxJmnU7svMo0fOSvcUBY0FC+9Q2/H+5m1mmZyjcshvo6B+s9l0R7jzcsoaQ1acZqCG60spb0LBxRTwkwL4OCB+TZ1Q5bRrdPL8Q8fhx5B6vHHZ0OSYVmeeRpN/KTppOBxiOByqvGDh+xivSJ1xdHSEyWSCer2Ofr+PwWAwt1svJnP5GPbVVT4/bzLyvNFjQRkyqqgcpV1zy8vL7lmtVsPa2hpWVlbw7LPP4kc/+hFu3rw5912WZWg2m3P3hpcJilPIsUBjC3iyQwiA6p8ULR+w+YH8blvp//j8HX5aDvfDU+Rs6EQWrT7WGLUsX9oHljwsZfHYQ6idY/f5Spq1ukqfSdNLkgZZF6t9JfP1QcbX5Dex/wmHh4c4OjpyentjY8PtPm40Gtjc3ES328XBwYGbdOW8OhqNcO/evRP14z7jdDrF7du3nc1HcX2SHYPBwOkdfvoQ+ePUdzQ5HDttr6i95MtTYlGxP63svHla2iGFJ0NjVMbAfGXLk7BO0972oUyfwppXaJ6gTPA+oTHGxyAAVQ+//vrr7uqBZ555Bq+99houX76Mo6MjVKtVLC0tYXNzE7/1W7+Ff/gP/yH+xb/4F/jTP/1TfPOb38QPf/hDbG1tYW9v70Q9o3fGcvgaI0UhyWdaoDGUn6YUfJ0XKoPeyyCbRoM1CGpBjCaZL08fCojwYKQWyAwFgkL1CwkZ3/2zZQwmn+MW6qOQoJOCwNoGMWj9E8NpOTl5lGDMydSe5alP0fYuCmmEhoLvwLxTTcbTbDZzu2G580BCvVqtotFouOM+adx2u11n6IXGWOpziTxBUMv4yaugiyjiRZQl4ZP5Wl6pYytv4LQsnRvLp0xdxgNWFkfPWlZIZ35cQOOdnKfBYICtrS3s7u66u9+1nbFaPj55nEqPZktwWUf58l2ek8kEh4eHGI1G7vh1S5DYYkvJ43R5GrJztO8WAYv9FALpBupvOs4y1dnyBXLyfA+EdUPIJvc5lZax7tOxFp7lafiiPF+5lgCXFpjx+Sr8CLNYOXn0rgZruxQZA3nlvrV+efhikfRQmaelQyx6KxSUlXJYfkP5WuWJz39aNHibU5sQLaRvQj6c1VbSZErIbtXkmAVykW6IZmu8gufH05YdzJT55ZWhlFdKfWJpSFfSYvJ2u41Wq4XV1VVUq1V0u91k+iRC/ULPZP/KiViLzPHZZ5L/fWk+qfC1f14bq2z4/L2U+A6PwQHHE7Q3b97ExsYGLl68iI2NDXS7XbcQezweu9O1Qv5mURDvaZOcAOaC0vKY+Ly+rkRInmky3Oc7azapfF8mH8XksMW38dnxKbawBVabSGunFDvC1wepNlne70MyI29bcn1Of5OPS23Fx6m0begZnbYg25b/L49KjfGYzCu1jUJpLLZGqm+v2alWmuQ3Fjq0/FP5IDQuLfRb+kXGVmSeZcsDAq+HtS5aHkXihZY0sow87eCz92U5Wptw3wQAer0eGo0GGo0G+v0++v0+lpeXUa1W3VUawPFikKOjIzSbTayvr7t8Lly4gHa7jdXVVe9CK/XO2LJgNZYtzkbIobJATr7IVSRW4ZMaRIvRZAUvU+56pSAfYTAYBHeHaogJWe6EZNnxSu9er5dbWFgEwWw2i674Ce1cTQF3Bk/DETiNQMii6xELaqXw1GmCHwUK+BcS8L+r1SparZb7fzKZuGABHV9A98sR304mE7cjlpyu0WiEd955B91u101ghIJQnJbUoE6ZSDXWgXKd65Tvi5aVEmShvg7Rwnm9DBotBjvnKx9Sjnr17Z6SdPkW6JSBVAcvL3xORZHyqU8I1WoVnU4H9+/fx0cffeTalU65yGvjxGiwgO5ro7qG7snc3d3Fo0ePMJlMnIzL44Rb9ECWHa/sp2OeAP/JJvSuTFhtVU5vu912u9uazSY2Nzexv7+PBw8e5KbBAp9dpDlhqTp4Npup+rMMuctpkqv3OZ38XeqdkjwP6Rdo/kWWHe9YqVar7hju84BQsP/jgrJoteZDtt956cMyYK27PArzNBa1aLYt/58mIBqNBobDIXq9XjItIbm8qHrxo+bpSN0itonPDyA5x0+fSgkMakjhF2D+FCAJote608oH4kc6Nnxvbw+DwQA3btzApUuXMJvNsLW1he9973u5dyxrkLv/Y7YX10v8zr6Yrc1B6fiklyzjZzhfyNMndJIBALz77rv4t//23+LXf/3X8Tu/8zv47Gc/i89//vP4/ve/jzt37qDb7aJarc7tpl0UptOpd9frbDZztCwtLbkYRpG4mDbZEEpr8Uv5eKPxw21SykcbX1bIgD2VF8o3JeYhZacvTV5YfRTtJK8UOrRThbidoelnSyzEch+spQ1TIW3o6XSK3d1dZFnmfONOp+PeUyyQX0nGaeJ1IHuH72qV4BupeFoJefpZDNTXoba05nVaOCtaLPE6adPydLyNpaygNBQ3JltS5m3tD5n/on2bMvvEF1PTyig6OZsHNK92584dLC8v46mnnnLvbt26hTt37mA8HuPy5ctzNF67dg2/9mu/dmLu6oUXXsDP/dzPqWV5J2O5EC3SCNJhSFHMGl2+wFtKHta8YpMjvmcpNKW0g2w7mqgkATsej51ikAMypAS1SR6+WlmjW+Yl32mg/Hz9KcuSyjgmoGICM1SerJPGp5b8iyDWdkWREqgrW9j5+tGXLvYspUzLOy0t5z/ugGvjC3hSt3q9jqWlJbTbbcxmMwwGA1ff0WjkgoHyCGXJayHetdSRv7fIsrL63GdYaEH/szb8ynJ2NLkh9WeM58vg81g/++R5iMZQn+V14KwoEmjMWz7n1bL4U44B3t4yiKB9mwqtz0L8R84j7fTnz0le0QTtgwcP3L30tKhE0moJSvie87FjkYG8XWVgVLNpQyBbKiW4SmVMp1O3MI6O8+V9ze0qoifkOKUEdniaFN0aKjPlXYwmyfMhPskz5qRNDDxxSok/Uuwf/p2PphT7PdXWT4XVDgD8fg7xps++SUVeH0/7n+cZSxtrC05Xr9dDt9vF8vIy2u026vW6s9FCQeHYOOH8pvkW8u8sy9yVMLTIVV4zs2hY5YOUZ8QrRXjGp2ulfNBkSB771SJ/QpNkMhCn2X2cPqlPUvwwLjv470UHxfizWBvH2j5Ud+1bsjU+/PBDHBwcYH19HfV6Hd1uF3t7e7h//76T9bG4QOi5j1apm0N2h0yTZceLd7Isw3A4VP06yT+yfUNjIYXf89gCpyFrYrD05yLo5OWW4UsAcCfJDAYDbG9v44MPPsAzzzyDixcv4gtf+AJarRbeeOMNDAaD4ORLKnw8lWXH103QpM/q6io6nc5cfWnCVh7lzvPxwcfbvrS+2IC1DEKKzZCnLvK5z46S5WqyT7aRRCzWY/lGK8MSE0uRGXl0b0zPa2li/BHrixgtofbk+pwm4mmhBdc/kkbNL5Z1CJ3yk2WZ6sto+cTepcKnrzW5a7GFTwtl6oRQe6bEy3g6yScpOjzUD9Iuj9kKIflzFv1VxH8tmr/PJuPfzWbHV2Xs7+/P2YaVSgW9Xu+ErzgajZw+B46vLPziF7+IDz74ADs7Oyod3mOKqUDpTFrv8PLlaUEZzJAqzCXOyvlNcWpqtdrcCp1er+fuVfE5CRbweyx9hhI54jGjQiIkMKTzw1cVyN2M2gpWool/E1MUBO3y5k8aFiFkLU6RFKR5HMQUemJ5Sd71KVtauQQ8CcyMRiP0ej33XAZiAGBpaQnPPvusS3NwcOCO16Id61k2f+9z3mDNaSKvYUVGLAVPitQtr+5ZBHxyUQbjUvLjSJGnIZo4XSmBwRSdWfbK1NDuB6JdLmSw2h1Wp46P/zJAdbLkK/WqRY/lsW+4vKQdnDIdLSZZXV3FZDLBj370IwDH7U/35fhoTwX1fWoeWhAt5jRq7UU7nPjOAYujQHTTkYrcYA/RrK2yL9v2TBnHWtlSv6XCNwEtA3x5ZTeXacST5KvwhVC8DkX1RJ5g8KICyLEyOXz9O5sd78qnaxX4zr88WFQ9y9Tvs9kMu7u7WF5exrPPPovxeIxOp+OOi085OULLm49rPmEp5RMtyut0OqhWqxgOh64P6Ki7Wq02Zy+eJahudIoDLdbh7VV2PwGYC0xK+yUPv8mdPPQ3MH90cl5fUJ4QkldPk1zLG3C2ItaOvB6yTilBxdDdgoROp4PZbIZvfetb6HQ6+Kf/9J9ieXkZzz33HH784x/j7t27aDQaaLfbc1c7FKkfQfKy1F/8bkqZP435CxcuoFar4eHDh258yHaTbXfauuHjjkXqU2mT5dUFdAcrLVy8e/cuvvWtb+FXf/VXcenSJfz2b/829vb28K/+1b/C3bt33cTtImMB1WoV7XYbw+EQg8EATz31lDtqkcrqdrv40Y9+5PQP8X2K/26pA/f1YndecvC+1/xBwB/DC01i8Oc+fyGPzJN5yDS+OGsq8k5alIm8caIYQqdzaf5yLEZtoZH6hHiJy3IAaDab6sK92ezkrley83i5kme5jeDbeV32/IjFrghNBPJvyrb7zjoOGqLBatctIrYr7QiyEYlftTifhCYfz7q9rShT92vxImobrpMGgwHu3r0755dVKhV88YtfPJHneDzG4eGh+/5zn/scvvjFL+L3f//37ZOxkmm0TvdVJvQs1HhWZk/5NjVNTMFaJnnyKGdfORbaJpPJ3MRQ7PgeqfCtdeblhb7hzqtvokJL71OuXMH5diLScwrA0XMZ6NPKl3ktWhB9HJyuEG0WPvEZn9pY1oIg8u8UxPo7pBQl3WRwhSYQeZ2Gw6Hj19FoNHdBN62O7vV6biKWhLRFdqa+XxSKBMs1ULtajyUKlR8yhEL/W9+F3pfRH9opAFbIscP5PyavU/KnPtDGuG9882ex8ix9E0sTcxok3Ra6rPmlYDY7vlMmyzJ0Oh23G40fR2ihh+fneyef+epQr9exvLzsnMadnR30+/0TdgXnA9LBvv7RZLCPJo3vaacu/eaBFy2oEqtjjBd9fZx3jFPb0N13RGej0YjuvtPo5v9zWmM2NDB/TCkA9U6jslEkb+5UEng/89MqrLA41/R7Op3OLTbIM/ZTZWBR8ACUHE/n2e48K/js0fMGshn5kWYpfi2HvHfbUrYvL196zZbhgSI+Sa0FKVMhF7MsgtdDOiX16NyQXarp9dMeuzyYzI82LGIjhwKS9P9kMsFwOJyTwXniKnl4yacbJP/6Auih8rkuy7Is9wT/z3AMbgeUMTa08UiyKcsyd0WRdeEJjR95zRaV8+GHH2I4HOLll19Gq9XCz/3cz6HVauHo6Aiz2QydTgej0cjtwCtqR3Ga6Fh1n69Efw8Gg7md3UX9pFg/ycUrllirL+bD+cPid6b4jb7vUhEqJ1a+1T/Mm3/oG2usRYtDWNtL6xNJg7QttDEcsgV4Gb64hlYvjT7gpK6SpzRxmcLz1/LWfB5fWkmvBbGYjlZGqh2olVf2N3ntkaLgulwrM9UvT7VZLeNJ62NrfERbMHcW8MUVi8pe61iXel6z/d566y1sbW0BANrtNj7/+c+j2+3inXfewfr6Oq5cueJs6RdffBGdTgcPHjw4cVWAd4uG5lAtomNCQUPf80UxSMiR8Cl77dtFOPRaUGU0Gs1dBiyPPeXpfbRqdPsCSPyuJZ8zSXySEkzW7u0CcGKFIK8L/SaDlzvz8h4xS5A29s76TUxppvJJSprTDEDEaInRrQm2sseNZkSE6qMdS0VBsBCd5LjQ98Ph0N0xATyZUKAdF41Gw3tPh9YuZaAsp5UQGuNWfiRHNW/AxVeulrZI3WOGS16+5TzGAwsWXavV1TemfLLH985KtwxS5TUoF5U+hpRgTsjpywPatddqteYCL3IHhY9uTr9voVJIn2t1oOPVm80marUatre33WIvn50WOsrPZzP52k/bcULH/tLCDeIx33HEob6RPKrtNgohhV94nbLs+O5YOrYegNuBnLrjIu+YpW9p5+hs9iTY7ms7S/4xfV9ENsrdadrJKWWMRTmegPkJFj5xXoaeitFh/V4bb/wH0G1oDWftdJ93nHX78PFQNB/OHxw+eyZv3TV7Q1sQUnQylufLj9WM0ZQXWh40yZFiu4fiDb73RW1OK108cNxsNgvdzc1/h3RMpVJxx7mGrknS6PU9swYr+XcajbPZzPlscuLft/icl8NjFpr9kof+MpA3qGkdR2UFTRfdDlq5RHuWHU/Gkg9PVxXxtBp9cuE1T//+++/jww8/xPXr13H9+nV8+tOfRqfTwQ9+8ANMp1N0Oh30+323YK4s8JNYtAUsRO90Oo2WH+qb2FiX38n2juUvv9FoCsmdUF20WGSKTE6JMebxJ0O6W7ZHTP5p19nJeoRotsT5tDys0L7X+ts3Bq1HzWsxDF+Zsmz+P39Gfpa0SXxXCGr5pNgx0r+2yEzNR/flbcmXt2dZNrOWTxk6ReZlodk3tnw2hCbLuK1Lv60ny0h+l3ZSaPzzMrUjtDnyTBAXQcz3kG2dyl957EGy7Qja/NpsNsMPfvAD9//Fixfx0ksv4ejoCG+//TZu3LiBK1euuNOOXn75Zbzwwgv4/ve/j/39/bm8TOfwpayk9QmtIp26aMcjVmZsYJwFtDaVDkHI+NGQZdncpdJamVY6FgEqqyg/acKyqNEg8w/1w8cFMUMnD2LB27OA7J96vX7iwvXxeDx3dB1NEJAS5YEYvuuVzpSnFab1ev1E3Ysch3dWsPbfZDLBysoKXn75ZTx+/BjvvfeeO3KvrEnn88JL2r0esbFfNAjpQ1n6NsXZKlPOhfIKBb58ecSMPQsNZdgxZAhXKhUsLy8jyzK3q17uMI0ZjrE28OUh60AyzCeHWq2Wm/jKsmxuUjHUJmRP8ECu1YjmTgoFtSqVilv0ouXBTzOwgOfRbDadDKf7cC22oUbDeDxGs9lEo9HAwcGBa9fxeIytrS10u130er05eREL6FrrQ3nIla0pfKsFaeh5UYQCHClBBF/e9EOT3d1uNymgcV5Rhg3Gxx7nt9nseGK+3W67cUCn7qTu+DttpLYLTU5Xq1VUq1W8//77ePz4sbtW4umnn8b29jY++ugjF5jOI/elnetb+JFlT+7ordfrqNfraDQaqNfraLVaGAwG2NvbSyp7UaB24HXjAYqiAThaKEL6Qgb1tKOeU3kzFNSVvqUMnMVA+scXzPUFfUO0+safxedOHRta4DAEGRdKCWRq8NE7mUywtbWF6XSKzc1NrK6uLsRmJuS5soh4hY6w297eRq1Ww/LyMqbTKQ4PD6N6qMwYRNkoMwbwcQD1J10rJMdyvV4/EUDXMJsdL9TOsgxLS0sYDoe4ffs2NjY2sLq6Opd2Op1ib2/P7Zg5rd3T9+7dw/7+Pp5//nlcvHgRX/7yl7G3t4fXX3/dTcaSPqLj8wkxH4XzjS/AH9MbMVlnec99rxA0nzGUdxlI0WW+NLwNZV199eAbAmL+dqhdqDy+sF7TffzbRbSjtE00/53TFMuLp5MTXnyRo4+n5OLiVB8spC9icxJF5ixS6OW85rN7Pu6w8KyvzeRYIN7h4490jUyXV+da7EoZa5CLi7Trk87baR5lzbHwdpb5cJ1FsR1Kr5V5eHiIP/mTP3FXyjz11FP4yU9+gi984Qv4zGc+E6RjbjI2RSCH0sm8rEotpGh8eUpFlodGnzJPCUpzpCicmGGREkjKE8yT6fkEU6wPfcLXsuNR4ydNYUsjg+gK0aO1Q8yYCNUzhbdiwc+ylFPZSi7GZ1bFEEtTNOCcBxblxHmnVqudmIydzWZzu9BpxxZNDnC+58GpSqXi7ovhO2J9ZS+qbU7LseB5k5HWbrdx48YNZFmGd955Z84QKVoGR16FXFbwI0/A9rT6PDVQGqJJGnNFnblYOSm00XvfaQvy21BgVBppRfmEt1mWZe6+meFwGN0dLvVgyq7OkLFJ9aIJAY1eklv0vl6vu+94oEprY3lnoqxLCJSOO/j8dA6tnqlODNFRr9fdQjRyRPh7K6gt6f7do6MjR/N4PHZHP8sdBz4doDm2vkADD37lpd8y3srIU3uXamuEaKK+jNmCljylnA5973PUisr4su1GXjcaY2T7kHzp9XpefkrVJ4tCHj6ZTCbuvtOHDx9if38fh4eHaLfbuHDhAobD4dxxjlKPWPSP/K35nLztgWNZSXIoyzKsrKygVqvh8PDQe+JRnjYoC3xcWGiI+bbEf/z4dNLlMr3WD9JflGlDeooHXLh+oWPKrfo/pMc1OaLJd01fa+0b6/88fMHbW2tf+bdPDsRoSpEd0+kU+/v76HQ6WF5eRrvdNpVVBJyXQm2vpZ9MJjg6OkKtVsPGxgZmsxmOjo5OtG2IHxeN1DIX1caclhBS6M1jP2i0UX/RD5fBMfuby5zxeIxarebs/Z2dHbRaLSwvL2M8Hjsbmk7LoZiCPOK4bBD9u7u72N/fxzPPPINms4mXX34Zu7u77sSe2WyGRqPhdshz21WTWRw+f4uXnyoTYrG/EHy2dawMS/4xvvPJDEs6X9kh2zZko1HZtKAm5Cv79KdGHy1g4N9xWjQ/J6UfYs8lXbHvNbvMl4bnp006+8qw+uoWPZriI+XRK74+9+UreVPStyidljLe88RJ+DOfz+Oru0YnB48Xc5uQp7WeFugrIwY5/rTy+Yk1IX6w0FeUD0JjwyeXeBqZj2bLhnx2Pu7pnVbeYDDAj370I/f//fv3sbe3h/X1dbzwwgsAoE50A56dsZwIbWIu9g2vQJkOO+UnBWaRHVYWxRdSeKmwGgOW8qQznMfRkXRZj0XhQQTtXWjwceEjBR3932q1cP369bkdh7PZDHfv3sXu7q7Lq9FonFi9YTVINLot98F80vHTWGcC8WCWZd7dm1mWuQC75LVGo4HpdIqjo6M5g03yKf+Gfsccm48LqtUqXnzxRSwvL6PVaqHX6+Gb3/wmut3uXDq5Yw74+PCeL6BWVr5F4TNMNH2nGZg+WqxOUKqe8/V/0bal/rHYCCFjOq8RCsR3P8eOUpLgTm+K0R7ThbPZcSCx2WxiZWUFS0tL6PV6bjU8TXBZVpdb7UFpA6RAOn6pDrome3q9XnBiWqaPYTQaodfr4ebNm8iyzC3o4TsrLE5rnjElQd80Gg0AUHcZp/bBIvQU2ZbynmAtjc9RtsIX6OL2Le2inM1mODg4cFdjnLWO5vIFeHIsIi0mI5o/jidvnFecdp9fvnwZr776Kj766CO89957ADAXQE2hh/vPi7CzaMxo955zmbuINuQTF9yX9NGRCi0vKksi7+QCz3+RgcwYZODdEv+x6HDZLjGbyMqntIDBsqgtVF6WZd4TKqx2Cp2YRPfeLi8vuwn9o6MjbG1teSf489pB5wXnxW8tm4bZbIb9/X1kWXZCl1rHKU3Q1+t1NJtN3LlzB3fv3kW9Xsfm5iZ2dnYwmUzw5S9/Gdvb2/jBD37gFvSFFiBKOq200KkXtVpNtQ+uXbuGf/2v/zW2t7fx7rvv4sMPP8Tbb7+NZrOJer2OXq/n7A0q2+dn+Xy6s+AV6q+8esjSxnlt0VRokzjA/KknfDKF9zM/RUjbIZvSNimn+vjsbQ0+WkI+Of9G0hSKV9NvX5sWgdysEYLmi+X19bR4oi8G5ItnWdvCcqytNZ502nokxXaJQbal7wqp2WyG0Wjk4swA5naWE79Iu1P2E49hFJFLciORL42vTmeNssarb8xp/E1p+TzBbDabuwt2Z2cHP/zhDwEAH3zwAYDjBZ6/8iu/gueee26ujOgxxaEBFhtcRYI8Wp4hWlI6wyfMpdOmBaa1b311k4NcDlQL3bH38g6eWLA1JnB8DkIKUoUaPeOCgALC/OhA2nFCIEOC777h9PsES+okg6WP8vC3jxdigtVnnKQgZRz5AhBlC8A836QaDjLQEKKDB1xl3/iUEr0bjUZzx8/RREaoLhynYZCU0X8yDzpmb2NjA2tra1heXsbDhw+xtbV1wunjhkfZzktRGRZCbPyl1KeMPl5EHnnz9AWytL+18hbJB2XVMQ98Y1yjz6KfrO2VIkdIplWrVTQaDbd7jIIuFpslNp4tNoj2jXRKtDSh55otp6WNBb58NoWvfLJbDg8PkWVPTgSQk4zc9grRSumsMkZL51twJssNBTsoTZFxq9mbWjk8vUwj2yImX7Q8tInVkB0Zo62oDMvTpjR2OV/JgJRFVpTpX51H+HQR3Y1NdwOXBWuAUgZXCO12G9evX8fe3l5pR5mX4S9wmSzz5WMyRCt/X8aY0QKwspzQ9yQLaKJM7oaLoSx7+qz8ASoHSNczHEX7VJPlg8EA/X7fLSCi49Spz3i5edtJ6sE8NjzpeOJDOnq83W7PTRbn5VOt3LLkbUx3pqbLS0NqPmW0gy8PPtGv2eA+v15rM4pfTadTdLtdd2Rxt9t1pyJcuHDB2dsUkLdeE5di5/MjVMn+GY1G6Pf7ODo6Qrvdxhe+8AU8fPgQ4/EYvV4Pt27dwmAwwHA4VO/Po7w1W0hrXz4ONL0c0x2UJmQ/WvUvfWORAWXqrRT4fFmtDbhdG/OZfDZiSlyDt4mvPzXaU+DjkRQ5nce+le0UShvjC19f+cpNiVlo/iT/xuejSB6R/G3l9RRfI+Qv+eqcd6zJ7335xdrb174+umOQNnSI5lAelu+0sanxgNaHPrlD7yy2tRUptoWFH6x6wDKmZTp6Tj4DgDkfnHTq7u4ubt265TY0tNttfOUrXzlx6sWcJSEL1e4NTUEeQ15COsZS6Fs7OmUHgC8II9NwRyCGMh0pafDnzcPCoFZmjuUXc5QqlQqefvpptFotAHBHhFH6hw8f4t69e27nbrPZNO04srSPJlBS+PY0nOSzKOunAdzJ4UfEzWbzxxJzB0ka78QrdL/MZDIx7TC3rDg/7+D35mRZhpdeegmXL1/GxYsX1ftx6Rt5D88ioBk6ZcvhWPkx8OC5tiMvFSltWkb7h+i15F9Eh/nyK4qUXW+0qhGI20xycQcdA3pWIHqtwQ+Sh1bDlf7nu/RknrE8aAzTKtIQv/l4yWKr8WNJ5Ti08APVk46aa7VaueV7XufT51zl3S1UFLy9pSyWd9qWBRp/tHtVTk5Kesou/+Omz8n2pbs6+/2+cxLpHunUCSltrJThC6aA6kSBZ1k2H+Oj0Qh//ud/jnq9jvF4jG63O7cIVAsgp9BBdY8dYXt0dOQmbBZ5PGUqfOOEFq4U1bnT6dRNhgNPdv765HlZ9gL1JW/rtbU1fPrTn8bh4SEeP36Mo6Mj9Ho9NwZ898CFZDa9o0CNlf4se3I3pRa7OAtZI30gC2QwO8XXrtfrmEwm+Mu//EtsbGy4HXm//Mu/jFu3buGDDz5Ap9OZs8PyQN4NJid4Y+BH2ab0r6Yjz7OPv2ieK2Oipgjo+qHhcIjBYIDPfOYzuHjxIr73ve9hf39/Li0PwgL+oyV57Gs0GmE4HKoLk4fDIXZ3d0/cL0yLIkejUem7krLs+Dj8RqOBd955Bzdv3kSlUsGNGzfwm7/5my7dc889h6tXr+Lb3/423nzzTayurqLRaMydFOKzqVJlRUpclfL3TdT5oNmfKePPOtkUmsCRssJij/r0TUgn0LPQPcfcP5N0cBq1ST7+N+lTfrw3h2x3bSLQR7+kLRZTluC2mA+c/7i/6bNDKL8Yz6fQSHTQ7xR9aZ3jCEHqpNCOYt+Rqxw+XtXoPG0fYdHl0bjyje3ZbIZ+vz+Xnk5X5HIw5sOHxk2ovUmnNBoNL7/QUfra6Y7nxVax7MxOQV4bO8sytFotVKtVtNtt9Ho9HBwc4N69e3j06BGef/551Go1/OEf/iHq9Tp+/dd/3X17YjKWC4EYoRryKGJtMPoGiqYANBpiAkyjM6SIfYrKqrytnRsSECntHvvO0j6+PLW20xRjrK9I0NPRJ61Wy03G0jcUUJtMJnNCiysAX9+cN4Ta3EJ3GYLP2qd5aDhNwZzadhy+sSgNQ35XlW+ca/Igr0F/nh1xbSxLemezGZrNJpaWltBqtZyjyoNdmkGcalxa26iIU6ihrL6RQanYJJOv7JCeixliKWPGp+8seVidVu3/FBp9PGkpk575HERL2Rbdw2kk3ZcnyCp5JxaI0Bx2/jcFlZrNptO/tJikWq2Wskgg1qYaH/iccC0N1SnWhiH7yhLECYG+045YlPT5bKYyYBkDMo0WcMmrw2R+Ennkt9ZvKfaplibk60g7QAuc+fI+bRAt/L7X1DbmMixVxltRpK3y2EZWf2k2Oz5+mo4PDd0ZXaQOvm+p7fmx5QDcbqzl5WWsrq66yajBYJCso4Dy7UvJK1JuaG0odSzXhSky0WcnaLouBlkmHd+5vLzsFlhajgqNyRECBY4qlcqJCW1NJkvbJFT2acgji27h6ULtovlL3B6mutMCifv376PVajkfg+6vL2M3u2ZPWOMgPh1Di8jk8yLXs5x2jOO8xlQ0WVIUXAaRPJ5MJlhaWkKlUsHy8jJGoxG2trZU3pW8r/U9LXDKsuPrjvr9vgt0c1ubJrPyyPoUkP1Psu727dvu7u5qtYq1tTU0m00XpyP6afEigFI37ljqKtvc59PlgcZHZdkBmowJ0eHz8zhNPjveF9+SeaX6CkWhjZE8Y9dXh5id5fOZQ99o6VLbUeaRh99T9YQlnhKqgyU+YbV7tG8s9MXKtyJUtxBdVoRiDr403Nah9/x0MkLqwv0YT1Mabn/TO/4j6eLfLVovcVjGWYocKYOHuFzgepr74LS4C4A7YYIWMAV3xhLOaiU9lR2DxSGhH2sgURPS/B1nRt+3VljpSYVFGMQCU5ay5cDQDE++gsLnjNAuwqeffhobGxvq6pp6vY6lpSU0m80gTUWNQUnfaaHMYM+iwfv6vNDNebcoDeTQ89Wus9n8DtmQc06QR/j4Vo357sM7z8iybG7H63A4PDH2sizD6uqqm9jpdrtOMXED4zSxCP4sotD5rv+8+Uh9VwZtlJ9WVooTaaUhhU5LgDakp2N55b17noJusTT0O2VXuKZrydAL3WOh5eOTXc1mE1evXsXGxgYuX76Mo6Mj7O7uIsuOV/lZ75EP0a5B0prizEpH3hJAofS0WtU6wVxUv3D65L27FrvFF2iKBYt9tNBOQeKhsnZbhHiMwAOdMchV/Hy3EullPibK1KXEJ3ynsyazT9Mh1WgE4I4/XF9fB/BktfMn4eQNwnm2i8tElmXunsC7d+/i6tWrePXVV3Hx4kVcunQJ3/nOd/CTn/wES0tLqNVq6Pf7SbpkEbtu+VjwXZsjA0A0lso6EprykRMkKfxfrVZxdHSEN954A1evXsVzzz3nJiAePXqE4XDobN+8OoFsv1qthnq9joODgxO7ba15pgaCzzOkTUkTUcSrdE/ld77zHTcmVlZW8NRTT2F7exuHh4eF7/G22iucZ0PyfzqdYnd3F41GA81m0+VPk/vNZtNNgn1S5PQiUPZkUApot+hrr72Ga9eu4Zd/+Zdx//59/Lt/9+/c0dl0opa1D8mubjab2NnZwcHBAZ5++ukTMa7xeOzumS2y8zvUfpx/V1dXMZ1O8eabb2J3dxcHBwe4cOECvvrVr+LmzZvu7jv+LS1Y2dnZSao/0cWf0Wl/8l1RhHgnNMGUdwLMUibpYlr4o73nEx6SBm4/87bj32qxMdI/vh2yPE9en9jElS+GLPMhuvP62j7EZERe+eFrf/leg+++UGu5RRCbC+B1or7T+oR4i+CbG7LIaO77+dotZUKtDGhxszLbXotx+I5354t/Ll++jCtXrgA4bq9bt26h2+1G6ffRo9noHDzGQ3eVS/A0JEdOCxa5Wjbf+OJH8h3NY/HdxfyO+M3NTffNgwcPcOfOHbzyyitYWVmZy3uuNTUhbiEyrwItwvS+gcyRGqiSoIFpycc3AKXQsShp+SxUBqfRl2eoXE2B+vJIzdtXbxkoolV2JCj43+PxGFtbWzg6OnL5pATdfW1mqV9ZyJO/1dnPYyhaEePbou1WxEACTho7PkXqey4Vo1Zfy/iI0anBErAu2zDQ8o8hNBHAsbS0hKWlJXfEBgXabt++jQcPHjgZSu+0Xe0W2kM0psqvPGWUlS4FPp6W//t41mLg+oxS33dlyNCUb1LGdYinUvK2IDWQZrUnKPgyGAycoyQDFj7E2oXTwlfoj8djjEYjDAYDdLtd8xGUku94vjJInpoX/605Nlq9fDT5vgee9GOKzSj7UuZHR47KccRpkX/H6mOli/LR6m0duxYdFeJnX5/FbDieJgXcLqAdSUXs/yJ2W5E8LOB9zO3oWOA1BFogyXmW/y4TUl+VmT+1CS10CPEA2SG0QGY0GrkrKWJ0Lwqcj2ezmZu4o91TmrxJoauoX1wmNB87Zgvyce3zjzX45AHtfOeLL2j3F19Ymdf+9/n1Vt1KR2Zz+1lC2mtFfSueb0gvpvgSMt/QM7IhpC07mUzQ6/Vw//59DAaDhR13H4LFLib7h7dbrVbDysqKu3cz1d7ISyunq6y0qeVb08ZsnxB/x+ycFNC4I1uCFhIvLS1hc3MTr776KnZ3d/Hw4UN39LC1jbMsc7qWsLu7644jPjg4mKuDhUdCcjDUB2T786tEyBfgPnun08G1a9ewtLQEAG6BerPZnJNzciE7cHLiMJXGIiiD/zRoccVUujQfRyvH4odJOSnHCT3jtjW3M6QPEqJH1iNUR18+vjx931j6hfzkWBkW2ix6lNtcqfETSUfsfRH5bbUJpK2j5SP1ssa71jpptIbqYc3fijz6wsoPWr4+f0er73A4xOHhIdrtNmq1GpaWllCtVt3EH98glGIDyzT8GGRCtVrF8vKyy5f0QbfbdROyZ+U/5I2NFLWL+bc+30NbqECLTkkWkI7d39/HYDCYT8v/oczKvpegCFIajwvi2MqfUDn8b+3ceytdJLhSy5eKNWSMhhwzy523KXT53sfqKB1PHijhqNfrqFar7kgYALhz5w6+//3vO1rJMQXmj5G11iOmJE9LyKSUs+jgj6X8vDSUHdAjfiOHJk/fE6wrqYsEdYsYlxrPngde0HD9+nXcuHHDOXez2QxHR0f4xje+gYODA1ff4XCIWq1W+srIRYLzf6jfUoOheQ11Hy+lOD0WWOgsmx9jBrI14Ch1dt7AdQiz2czddRe6cyMVWZah0+kgyzJ3PA0FaiyrEVPooBX9BwcHqFQqaDQa7t4qC7SgI3DcHrPZzB2pmTreZX7W8SJlZshO4qDJuzx9qC0w6ff7bpWr1SmnHz7BH5IBoTylzqC/U237kPOTp63kqRHkkEi9XGSXOukeTVZY7d6fRpCjDzyRs4vS05I3y8oTOB7Ls9mTndO+u+vJOQaAfr/v7u2TQWWtDAtOk9dS6JJHwFrz53ZGWX1GMokmwCVNmo1fJLAiT5OYzZ4ccdtut91kfJlyMg+t1MbVatXp/X6/r076pARC80DGUWQZPECbx8bUQOOSxjKV02g0sL+/j7/927/FxsYGLly4kFReDKHAJv3PJ8blt9QGZK9xtNttPP3009je3sbW1lZpu8LLxKLl1aJ92KL5U7/SuKPFkBxXr17FP//n/xzvvfce/uzP/gy3b9/G7du352JTPtBYoTtpl5eXAQAffPDB3MLF08RgMMBgMEC73T6xAGI8HmNvbw+rq6v40pe+hG9+85sA4PyFzc1NV2cK4hModjoYDNTTs846lqEhdNpQSr/4+FCzSUOTrURLqL24Dgv5x7TAiNtDFCfnkwYy7zz1tELz72MoEo+zgtqdZHgeHtDsGS2NLNeSry8tlZkSGyfwO0GJp+TJopJH5IJHa/8B+s7hs44LFuFln4+hgY8b7VSn3d1d7O3t4ZlnnsHGxgauXLmC6XSKg4MD9Pt9PHr0KNcYkPKE/Byu55rNJq5fv37i7uCbN29iZ2cnWN/TQMjezTNO80Ab19oiXulHLi8vYzab4aOPPjpx4tyJydg8yFNxHtDhyk9TJlqD+4xm/o2cnI3VIdV58ilbSaOsY6x+Gm38tyxLPg8dERzKX4Msg6flk1laf1Awh+fBAz6rq6tYWVlxqz54QDLkFOU1jmL19T3PO8BDxlOe/PIij8FUJOBaZj7amCSHydp+su8lf8m/LUF0TkuobvK9JSjve3aenBd+lxbwRN6urKyg1WrN3YXTaDRc0N3K+yGFK/tQG9tlja08ba4FpUK6hdLH+JSe56HHhxjvWuCT/anlyXzy5EHvaez6nNg8MjEUBI2liznbhEajEd39qsm/1AC1TM95kPKnoHQKH9D3XC74ypPfas95vr6xQLLHar+FeMtHgxV8FxMPnPOFjvyYaQ6rrZZ3PFvylvZrasBY6kzu/EkaY4EYnpeWZ6hcLX/rO8lv4/HY7XLmNm3MTl00ePnVahWXLl1yzt/jx49x7949N5HDd37Tt5zmIkGhIvQvCim6Q/bhWdhZWZY5HuNyYTKZoNvtotFo4Nq1a+h0OgDgjq6kSWTKI0+5eUHyTI5t37iQfrAvzxS6pHwNBWdS6lqv17G6uopqtYqVlRUcHh6i2+26hYR8kjAETdZRf9HYpNXysTz4/YzaiQupsLbJoseEtEHkGNDomc2OFzzt7e1hNBoVWrxDNEh9I+M3Prrl3zzvfr+P0WiEfr/v0vT7fZUWS8xjEfC1QVl5x9opb3xFQ9F2ms1mzvfPssydUHP//n0cHR1hPB7j8uXL+Dt/5++cOJJd2qG+HaH8GxrLmp2dZU9OwpHvY35UyF7Wnsm0e3t7+B//439gfX0dly5dwo0bN/CZz3wGv/ALvwAA+Na3voV79+7N1ZX4nY5hJ8iFFbLNiA7+zsqHMd5J0Sd5dYUlT185hFS950MoFiLzlpMtvP3z6OwQZJ5F8gohVobkN0t+FvmU4s+GaEqBlAeSf7W2CNn/dAIATcxrOlXebyrpSZE9sToVQUxfa7DaQr60IRlkiUHx65M0Xy3LMncSAU3qUb9ZTmYI+cjcBh2Px3j48CE6nQ5WVlZc+Wtra6jX62i1WphOp3j48OHc/ec8P6uussI3RoqMGZ5vSp4W+cX1O23YAJ7E99rt9omrB04cU1wUMSUqDVzeecRcvjO16Vt+pAYwv0OS5yG33FvqJxWUpD1mBNFvbdcdH2hUViyYE6M5JnRCeaYKSGlcWujnbTaZTOa2Zl+4cAHPPPNMtNw8A4aXb0mf972F36396nNaQsLcSm/K2JbGcqoBYy1Dg3QMNcNdC4SkKn9tLNPYJ8WmTYrI73wGr0ZfHgOhCMp26LnMnk6nalDhwoULWF1dxc7ODnq9HjqdzoljNTT68vCYj8djC29iMkumDY1xy7iUfKyll7owBh8fSrqkcybHkg9WeRgaCyn5S4PO52jEaJDfWPtPpsmrD/I8l0fENJvN4F2e8ph/Xx9rxrxPXmk0DYdDd/9kDDwoAzzZacp3zmrl8G81Pcf7L8TLsfG1CB3GaSTQ7mJqOyqLAnz8XnJNx/kg7eQQHby9LDJf9g9vc2temu0iaY7d6yrLkPKc25GcTl89ioB4dzabObuVVrLK1fKc1tME33lVr9fx9NNPu7vo3nnnHXz44YfumCuqR8xO+aQixL/WccLTc1gCLT7wsUbHl8r7JQ8ODtBut/Hss89idXUVwHF/N5tN9QSD0+pbTU/zOlGaPD6Epf14PiQT+KJejU5rPzebTayvr7udl7du3cL29rbbPWK561vSSP9zWukI6tjkbpZl7m5KukIgpT6+PK3Q+tnyLpaP5FVua8QmWLvdLnq93twCKAstIT8splslL4XqO51O0e12T9h4vgVOmp0TQhnjPFX2nUf46pCnfWh8Ek/RiTc3b97EZDLB9773Pbzwwgv4whe+cOJbfsWWrx/pnTweX0tLRyP7TucLIdQesr7a88ePH+P3f//3sbm5iddeew1f/epX8ZnPfAa/+qu/ir/7d/8u7t696yZjs+zJZHG320Wr1XKTsVxuDYdDtwtTo4MvVORtFauj1s/82zw2cKi8onGckB/G8w75gNb8tQUB9Df/n1+JwOmoVqsnruKx+Mo+P44jJHtCbR3zIbTyQ2WG+j7m+3AfyZeftIVCOiYWTwnlofnEVsxmx4uH6USALJu/K5bS8B8N2i7vFDms2dZl2bMab1h9uKKyRf7P+5Z8zUqlcmInsqS71WqhXq/PpTk4ODgRX/Xxua+evK/H4zHu3LmDixcvuntNsyzDxYsXsbm5ifX1dYzHYzx69MhNxmp1k/RrbVEGQno25dtU2qSe0mzy8Xg856PRAk867p+j9Bt4JZPF0vG/Y8YzZ1opEGgnBzeINGhnZMfoi3VS7D1f+RYT+jEQ/bG2TXGKNGgBtZT8UpUBML8rqNvt4ubNm+7+jFDf5x3gUkn6kCKkU74tAitNqQgZUdzoluWlGqMafI66/KYob/tokbtHQmlleTzAEqIpNSiUF4vgO22Byfr6Oq5evYparYatrS2sra1hNpvhvffew/7+/lzQisawVVGGEHJieHn8twYLLTIAU8b48umfPHlrOtdnEMV2HVhpsbRtnnx95RTJI9Uok/2tpaV2pKO5t7e33W5xST8/vsznxNJOp2az6SbqALh7qzQafLIqZGPI98TPo9EI+/v7GI1G0d2QJJutfa/pyCyL77qkMrjOzzv2rI6/rz1ns5kq/zj4jifqN9rtRkF8i50RosfKy5Je3l+p975o8iUlsKKBVvhyfivqeBMNWrCL/o6Nl0Xr5Y8jPg5tQvJkNpthbW3NTaaNRiPcvn3bax9Q8IhkND9y3CpvJO9z3uM7GBflC6QgDx2L6H+6kqbVarndv9r9rNQX9KysNtQCylogLAT5vS+YmmIzyu8ppgHA7aL17aw8LaTKev4dgdpE6hNup2qB3VS7ryjvWvqO20OW4yHljsqfoTjK6GsOvtOey6nBYIBms4lnn30WGxsbqFQq+L//9//io48+crtcfcfih0A8Ua/X5/wL4IkMXMT1cTymQzKYdj7xHT39ft8dUTmZTOaOWKxWq07XHh0dATiO5dEpeNpx3rFYijZGfLawZSxxPQLox9/T8xh8MimPP8Sfad+X6V8TX8ndbFQO5wWiJ0ZrqCxp9/h86Vh8hn7H6CrS/r6yLb5Bqo8mY6oWaLaPz88pAmnTSNuL7GJaROKbT/H1z3mxga2xf9/3HEW/D+Hx48fodrvY2NhwOyllW5MvfXR0NLfZTYs3WGOitVoNg8EA9+7dw+rq6tzx88CxfH/xxRedLDk4OMC9e/ei9SlbT4fKWQQ0HpbyLVQ/OilFQ+mTsUBacENzWHwNSYYRpeECigQEBcI0WgD7ZKwsN/QuJqRJCYbSyDxDbVA0YGWFVZn6vtUC0Pw9vaM60Qo64Jhpb926NReo03YtxxRzasBRy8NXP+07n6FmUdCp9FneaeliRp9PgPtQVLj62j4UPC8qbLkDTfmFVqDG6sgD8T4UpdmnyBah4LT8tOMolpeX8cwzz+Dhw4fY2dnB4eEharUa7t69i729vbn8SH6HHKwQNP6VxqOsQ97xGEKR8UDvfBNsMm1Ijml19wWGfXn48rSklY6S9TsLNMMmxudlBOp8AV/5jO6VunLlCrIsw8HBwZwhTLST3vIF9+n/Wq3mJmL5qR9y4lz2Z2p7++o2Ho9dMIV21/kg5WbePg8Ztrwc+jtUhxCk3ODfa+1bxK7iATwKbIzHY7RarejRi0SjpFOj3yrvNb1u3dVlyTOlvXi6er1+Qp8UsW1l+0nfwGKnlalDT8PhlAjZd9bvfO1QxOfQystDY8geBJ5M4CwtLWFlZQXPPPMMut2um4zVxgIPMgHzd/xofqyv/iF9EqtX7D0PHBdpf6Ix5jv5INuMP0sBycd6ve5OETg6OvLW0WfDpZbJ/9b0jmUHWkiHh77JAyqLLxgguamhrDG1CFmotZnkRc5f1A/a6Vtl2Zg+WrW/rfEBS8wk5tv6ypOQ8qks2Xxa0PjMOtbz2kDW+hH/kT6gHarD4RCNRgNXrlzBxYsX0Ww28f777zt7bzweu6PMreB+MZ2K4NuNuIj+ofLozrv19XXMZrO5o7WHwyH29vbc7i1+lHOlUsHS0hL6/T4ODg5cfTi9JMNCvE+00G+NN0L1D73nC30o/1Q5LstK/UYixcfOC6mzaTIWmD/hhdqOL5DlfaDZQvR3aIyG4g/auJTlcf8vy55MFstyfXLbirL0nbVP5TgItVPMf0nlwVhd5djjixS5HJpOp+7UndD3RektYu9Z9Eco/1Q9nEpfLI+DgwMcHh66+7yBk7HDRqNx4soS4MldtLKs0LwMoVqtYjQa4fHjx6jVaid2clarVTz11FMu7/v37+PevXsqHy/aljxt+GiwxC+0EyIJ6mRsXmeZE1UmSJHKfOWqen4BtaQpZhyVdXE0DwwQ5OCJDQYSdpL+WLta2z3GzJZyUvp4Njs+govaYX19HdevXwdwvPJjc3MTnU5HncgiYw/wH7mRF2UM6hSHwBIUPGtwQ4H3cxmrMn2Of0zeaHeT5jW46NihkHMTkgX8zmOO0G78spRHzIE/LdAK4Eqlgm63izt37pxQMr1ez62Snc1m6tFEMeQ14KwOX15a5LOU/ELjKGZoWCZwU9/n+SZV/odgOemBl8v/tjjmFqM89F6mpcVeN27cQLPZxNbW1omjfCkYwVcZz2ZPVpP68qYxVa1W3S5Vrc/zHFtGdFFZFoNcymb67fuWpw3JaN4mvjIsDiOvhyw/9i2VIY+WSRkveYJjFMjS7v0L0S6DKkXGoGZfhspMhW9sFqFbBrgl79A70s2pwdAYtF3vZ+0U8l1Y4/EYOzs7TjZtb29jZWUF0+kUg8HA2U3a2CNoQa5FwGcHWvg/9bvTQhk0kJ/LfaXd3V28/vrr2NzcxOXLl9HtdgHApVlE/VPy5PpTCzKG5OpsNnOrxDc2NjAYDNDr9U4sXrLK9KJykaPb7eLBgwdYX19392ZRsJqXkzJWuB87GAzcuM2yJzviyEaIjQXtyPfUekv72qp7OVLsp9C3KT4dTUrL3bN5ZVfK5ALwpB+1hVx80kB+k8deSKW/jLxOS68Vpfs0ZD/FGJeWlrwLrb/yla/gxo0b+O///b/jzTffxMrKCur1OnZ3d6Onv1hAvoHF10vVoVrc9vDwENVqFWtra25H1K1bt3BwcIBXXnkFzzzzDF544QUXTxkOh3jrrbdOLEal0yZWV1fRaDTw4YcfotvtOpnH21PaJ2X1K+VDO3T5MfZlnbiQR+5Ke96XrszYEZUlT4CLxUqsfoKWzvfOEivLo4dSvrH4fBY/NBU0njkPamny+kkcFj+fg64fo+uSCHRalzw5S/O3Qn2dytOLlPFF7Seeh4VPYvETesdPVtBiRmQvUqyI7oyVoAUzFHuVtpIP8l2328X29vbcu1arNXef7HQ6ndvxSTSHNiedV4Tizlp/lyWj1Qh5kcGj5aHBJ7h9ZUkGJqXCV1DGdsT6HIcUYz5WL4uS1cqKpZUK3FduTMml1C9kJKQEPbkBADy5j+fg4MBN2NAWfHJI5fGmZxH4SilTtoe1b7VvUxDKO7XN5LjXxpzMvyjtWt6yv/lqPgDBY4B8wpPy5UeGyrsXeTrfWCNDSiI0GSvzlvktIphW5H2IRqo/KXZ5Jv54PHb3JcoxrOVnpUlL65NVKYoyxcH1yaEUvREL9KS0S0gPxL615BWDVqdUWe3TJXnlvcWZs7Rx6FsKVLTbbTx69Aj9ft/JMh5Y5G1B70InZBCv0Z108jiTMh1zCz9qbcnrpem4mB1CgUrrsUY++ORmijO/SJuCt0foNBcrTSHdmypLYzTEYNFZnL+kTLDqu5AjJMcW/ZZpQnTJZzK91Hl8bGv0LTowLMuQ448m6DqdDtrtNhqNBvr9vrunzeKIa3mnwhqg4Mjbdlqf8fvP6E5OX1BD2rwES/3Ltt2It8ivpT59+PAhqtUqOp2O0wn8riZLUDEFMVtNIua7aun4mKLA33Q6xdHRkdOBXCf68oyVH6KX+zcyKDoej3F0dDQXcAJOLgj2yWWuIyWfkT9MATQpz3je8llMruVF2T6orz6+9CljqVKpoFarndjFZYVFd/l4j6eR4y7mK/j4RaMtT39Y4kOWdipTnixaJy7Cf+bgC174OCX/djKZ4OrVq7h27Rr+6q/+CsCT+7xpQWbKJgLNLpFywIei7UAykO6vppN6JpMJHj9+jO3tbVy8eBEbGxvY2NjA008/jdFo5O4sHA6HJ+y+SqXi7JFWq+ViexbbSbMjYrwZiwv57Oa88iMl5uOrsyYXYzGHIuB+iEUm+fLgdMq/U/JLKc8CjbY8/kfe9PybmD+XV37l5YmQj0OguJ2cvKP7g+UuWSB8gpdGQ2p/+sZDTA6klMXLyWNThvpRa3efnuY71kl/8JgrpSE7iC/ipz7iddDqlFK/0WiEbrfr5DmdmLC6uur6vVqtolaruUXmIeTxDznK9nfKyj9vXIOQ+5jiVGWYB1mWOWOAVgRz+HaNDIdD54hLyMmX0M6cPHWRNPEBxOEbGKcRIPQhZBCF0sj3XDBTMIjn0+/38ejRI7X/BoMBfvCDH8wNfouB6nNSF22sp6CIQVBWPVIURihdKj0UfImVxScgpFCk43/ICWo2m3MTq6m7xfhOBFqpTs8lfbQzxjeG+MSjXBgSwiJ4s2wDnmik+msT2OScvvXWW6hWq26RRVGE6hEKOqTmJ3mtLAPZJ/tD5WjBuzIgg5ykH6Xx9tOAMsfddDpFt9udW71uPa7cOlZp3GljLwQtaBjDYDDAcDj08jMdVUb32aXoDYJlnMlxogWofd/F8lsUJpPJ3OT82toaKpUKDg8P3Y4ASZPPNiwii3wyh+thGTjzLSgK5VUEVKa0DXg55GguwvbxBUR4+9C4k3btz1AcZdvmpM/29vYwm83w/PPPYzY7Pm7x4cOH7jhJmpjmfUoL/MiODO1oknLovPgXVmgyNZY+pY7S5+Xfkv3B7yekY9D4ziVKK2lOheYjUkDx+vXraLfbODw8xGg08t7llAriK98uV85nfHGopJPLnkajgaWlJbRaLRwcHBS+N3aRfrHW77xcK3xBdbKBaBcJ2UWp0IKveWwznk6751YrNxRXOU8xixh89stZ0X8aZe/v7+Ov/uqvUK/XMRqNsLq6irW1tbk0lUoFFy5cwHA4xKNHjxy/An6+ms1m6PV6qFaraDQaXruoLPCYBgXXa7UaHj9+7NJ0u13cvHkTvV4Ph4eHeOutt7C0tITf+Z3fwT/4B/8A3/72t3FwcADg2Odpt9vO/282m2g0Gq49bty4gX6/j/fffx/D4dDZw5pdJWOivE99E0rat/I5tymB+dNFUvKywicDiR+4T+6TYdZYAOXn20XN5bIv3hGaIJL3yVJ6S0zYx+8+GkNptVMhUhCz24rKD+vEJNfvfMEu0eDTRZJ+3p/a/INGUwyTyQS9Xg+1Wg2NRmNuZzldqTSZTHBwcHDCR5J2paSB3ltlm+Q57T1Hkf4ryleWskNXMMj8uE/T7XZx4cKFE/NadL0j9cmdO3fU+TKpf3z08n4EnpxM2Wq1UK/XsbOzg+FwiM997nNot9sAgNXVVbTbbXz00Uf48MMPXV7cvg3JOF7n82Q7+OQiT+vzV7QNB6G6eSdjQ8I5xLBlNyRVShu4pOhpJRel04J48rmP5pSJjJhwkMJUS2PJP9SmcmDlCRBYGDAl4Mq/54ZBlmVzxx2QAJnNZm6V8GAwwOHhoTtaTdJBsLRfSAlKmlMQMgwttGj9GlM2KTSGhGzMsJO0LSp47aNRGuEA3MIKyeepPC7rT881uRDjOVIy5MyUGZjLo5Dk+JewyDVrEEHWlQcw6e4vi9ItQkvou1jfpbSvHA9lBZiK0JJapvze97tIn6TQEwre+HT0omHRdZzfW60WlpeX0el05pwS7V4iX8BKy9dCn6QpT3uFxoNPnvl4X5OpPhvC4oBbna4YtLFmsV199oFlnEojnJxXeqfp4Bh/pCLUfkVkVehdjH6uZ3kATNPJqeVLe0CzIcpELP+yfKBUvUCLKPgEDi2q4G1t4QM5VstsR984KNJuvG40OdNut5FlGTqdjgtu87TyWx8vxvReWXqRf0O6hHaL7u3todVqYTAYuGCkJZhladO8/k9sfIbylUclkj2t9YH0p61+NecJmWY2m6Fer6PVarm7tmhBJi026na7ODg4mNt9oI0dn/6K8be0p+kbrV1pgSff4Z4CWX5RGRXzV8sog6Dxm2/Ss0jcI2YbpI7l1G9S7AxLPhwp8ZIibSifnaZ/Q3kU1SU8HwLp1Nls5iYUV1ZWsLq6CuD4tLd2u41Op4Nmszm3uMeXP3DSluB+fFFfKNQGGu/TxAvpTYqv9vt9dLtdHB4e4vDwEJVKBTs7O7h8+TJ6vZ5bfEj5kg6mydZ+v49KpYJGo+F2DFtiDZxWad/l7V+uQ0J+hxwLi/JJpUwO2ZMhH1CDz96R73keVpmVKis0ezJEUxntXlafhfR9TGZZ2pOP89Q8NB7OEyuQ7U1jWNsMJe8alot7+Xj1tZ3PX7PC0qacxpRvi6AM3cNB9JO9TFdXcTuV4uO0YMJ3fQLlR8802nmZ/DnFd6lPR6MRBoMB6vU6lpaWsLS0hM3NTezv76PT6bjFNjLPmM9SZtvlRaoM8tlA8vtY3XLvjC0bssLEWL1ez+ucPP3006jX63j//fcdc0jQ5caHh4fm1ZMxI8Zy/4cUUHmhGS4aU3N6yljBrzFi0bo0m01cunTJCfONjQ1cuHABOzs72N/fBwAXTNLoIeFjoSmk7BcZQPMhppRDDozFcC3L0c6ybG7lDT8KnMo6bVy8eBHXrl3DrVu3sLOz4wx6grzLTUOr1VJXoRNSlej6+jo6nQ62trac3MmyzB23nQpNYabgtJVYpVJxd0ocHBy4QBqN3xQ+KYunLG1gCdZROp8TE8rTB8udP3nyDZVHhrNciCSPrKRgY9Hgbl6jmueZuhK8aP9YAh58RWi/38doNMJnPvMZPPPMM+h0Orh//z52d3fdJK00xLgtQDpM0kXHu7TbbWdY06kgPvpozNF7yWOarOPBEl8anpZ+E59EDcr///QCqp/F5ioaVJVHyVmgyYFQMITnLe8NTHUqSU/II314P54VqF6pMpxDc855202nU4zH41z3iGuTF/J3mQ4xnZ4hx1bZEw55kWUZms0msizDhx9+iOl0ihdffBG1Wg3PPPOMu+tN7tT3+VSyryitVc6mtkdo3J6XNi4C7Ug3H4i3qd3b7TZ6vR6++93vOj4E4I57HI1GSTKvCHg/0cQgn/gv2y6Q3xedmIiBTrfIsgztdht7e3t4/fXXsbKygk6ng6OjI3dMND8iOgTa9S+v6+Df0qRF6OoVHzhPxcZeUR7RxmKZfcLtpBC4rs27IzYVVKa86sp36lkIfCFFKFD9SYJVjpelt7Xge5F8Z7OZu4pnOp3i/v37+L3f+z187nOfwz/+x/8YGxsbeO655/DZz34We3t7zl54+PChiVZpz1Dck/OWRX9YQX4h/eb3QNZqNbz00kvuTkK6O5tjOp3iv/7X/+oWs5A9N51OcXh4iEajgVarhaOjI7czuFar4fnnn3d3YxNoHBepq+Sv0Pe+Y+ZDJ7SEypPj33f9isUGkDRofGvhY9IvsfvHufwm+oiGFLlqiafy+lv6NhTjLUvvhGLEeWQx1U/uBNVsBcknVn88Jktj7RyyszVMJhM36ab5a5pP5KMp9f5arayQ78d1M39+XiDpJB7gk9v8vcR0OsX29jZms5lbZJtlGS5evIgrV66o31BaOkVEe689o1PP+A+d/ra2toZ+v49vf/vbuHjxIr72ta+h0+ngmWeeQaPRwOrqKt555x08ePDAlU0+KuehPKdZLhqpvCPtYf6Mj2VL/HdudFmMpVTCYml9lee7XAm0sorS03GlMgBJwUy+2jZGb+hZDLKuKcaBLw+NJilsUmiLCeaUNuL5aYqS+k0LYHFFT0dDkUPa7/dPHI8Y4imLIxQylKx5+PLj8PVJyKhILcOabx5lJw3LGM+mBOhkOTwwqAWj6X4syr9araLb7Z5YaePLPxRo5N/Sap/ZbH5Hm+S56XSKer2OlZUVFwSr1+vqQokUg4O3YSyYVVYwRUNqIC3khKQayr78Q/Iqb/6p40LjNdkX1oBK3rLzGq6xtirCT6dp5IaMozzyR76LjQvqb9JbV65ccceS0TFcZOSSwcvlio8OyeOcFkufS90d0o0WW8xnj1lsBOnQy/rIdFr+1kBdWQEpCYv9Y+EV+psmHdvtNtrtNur1OobDIR4/fqwu1PP1vWxTa3tpNFt40NLGMrAU4ouY8+9zBvmRpZZxYaHHB41PrTqxDN1cBNx24fexkeObSttp6AWLTNHehfhpNps5n498iuFwiHq97u7PpTy1Y+4oD58+L4pUm4XGGL9bFIC7x4+nK1KW9k2KT6XlY6GJ0kwmE7eQmiYtqR9SFxVb/CDJe75gCl13RDuRaeLNZzNoMs3iW1Janr8Gy+KYkCwqKqfy8Jkmj0N15Hzva09LuUUQaycfj2m+QSjf06iLFXn1phVlxj1C8NUjb7tLW5b34Wg0ws7ODn7yk59gc3MTtVoNzz77LA4PD3Hr1i3s7e2h3+87+QE8mYiP+WQyzSL7hQLtvDyKvZJfQycGkA4FgF6vN3eVGB0XTpOy5APJOw9JT5Ntwq9e0cY+h9a/vnYK+ezat5bnsu18z612qqWcMuIgso2070NtK30OK92+71J8l5R+yytTU/3KPLpUtquvzFDdtHJj8ZFYH8fKJl+MFkwAmDslhI97LQ+t3vx5qE4yj1Q/zZfGYicWQSiG4ksfspH43yQr+QK/wWDgroWbzWbutNFerxc8mSFmH0vZyXXfdDp1Jx7cu3cPy8vLboPSxYsXsb297a79yLLjUxbolJ+Y/y/p8GHR/RiDlKshXrPEJ9Sl6XkqmddgoLz5ZMlsNpu7w5Fw4cIFtwJgOp3igw8+ONG59Xr9xP0NGiyOnu8uA43B5bMyDCgtD9/gijkCPqTU0RKooG+Hw6G6+odWGFLZNGBnsxkePnzo+l0KJ3mPk+QXCzT6YwZgqK4fF/iCDgTr/aYyTyt4edVq1S2oAPSd0K1WCz/3cz/n6Lp//z7eeeedE3SH7o7gBi/vZw66mJyv/tHyGY1GuHjxIl555RXs7u5iZ2fnRL2A4zahyRjLbt3TclLLBjlg9He1WnVHJGqLX1KcHs14K8tJJ8TuqLHml1fuLBpcpvkWoJSJPPU7bzytgfPeaDRCv9/Hb/3Wb+HLX/7ynO4iVCoVtNtt9Pv94J1uJLfynmDBx58VsQABv7fI6kTw4LQc96GV1Sm0cwewyLhLCcjJgAX/ofe+OnB+GQ6H2NzcxLVr19DpdNDtdvEnf/InGI1GczrQB9mmIR3uA6fbJ2/zyqeUQIk1Lx64a7VabndgWfc5Eo2aQ2ixb88LeB1+Wu6yDfk3s9nM3WlEtsjBwQGWlpZw+fJl53dQ4JgmbClPkld8l70s97T1+KJhCY6m1j3WRzJdv993O01pp2+ekw5SaKPfNHlfpAxNfvJdZpa8ZXCN2gCAmwj2nRiVQudZIWXcUJ/LXainRX8suM3TEFLuvrPaVmeF80rXeUG1WkW9Xsf6+joePnyI//Sf/hNarRa+9rWv4dd+7dfw1a9+Ff/+3/977OzsuHjW0tKSa1daMBWCJVBeBmiskQwkufPo0SM0Gg1cvXrVTTq3221cuHABwDEf7+7uukVClUoFq6urGI1G2N/fdzETTV6Rj8SPQKY7Z1PrKnXKIhGiJ1U+cb+J01+GHeeb3Ir5m3KBqI//pF+0aPAJIW1iY9EoYvtZdIlMmzK5GoI2qcbzsNq3FBPgsaQsy9zphLRBZXd316UNxVI5H2q+faxOMl3ou9O0G1LKl89oIV7o1Ea5i5aDrnWkvK9evYpGo4H3339fPS2W0xFrP96f9Xod9Xod3W7XyfZut4tvfetbePbZZ/FLv/RLWF9fx9raGqrVKq5evTqX39HREd5+++1TOc2kbPjGiGbT+cZwLH5zYjJWG8QpAsCSVgoH/ps6/+LFiycEf6fTmdt5JgMSfJcK0UK7Z+XxFLKu9L/2jAtKrT18k3xWyDuAOH2xfLiy0uqkQabNE1RLqWOWZXP3ftbrdXevxmx2vIJjMBioq399bRFy+imtbwCltK/2Hc+zjIkIX9CY/1+G8RMan7LN+NGmRcrNsid3j0wmE6dM+OrJyWSClZUVVKtVTCYTp+jl/YsUPCHnIbZa3DcuqY7abllZ90ajgc3NTayvr88d49dqtdxxbQDcbvzzhjKMSS1PUtS8H8uAHAtyjNL41sZMTNnJOvjy89EUao/Qs/MUxJV6l/ROnuNCtAmNFPicRu39WU2UyHL4saXT6RQbGxtuhaDlex/IdplMJi5AzfV6ipzzvY/JQ06vfMedOOnYcfAd8iFY28XqpGn/+74N6Vv5jAdOYtCOwecBL3pH9xMC87slQmVodYoFq6XtaoEvCGN5nmIr8LtvisJi+5YpL7R6l12GD5p+nE6naDabeO6559But/HOO+/g8PAQjx8/xmAwmDv+Tusji54968CGDz6dEdIlvnw4Unwb+b3mQ6f6g2TfApjbkcTvhsqTf6o96Bv7pAf5UdbWYLKkQYsHpNKY932lUsHm5iZ6vR52dnbm2pOub6CJUN91Lb424rEJLuNTZJ5mI2VZ5myQsnCa45vbEhodPJ32Lq8vk7d+PKYA+O+rtZQV8zeK+GsWxHyds4TWbin27yL4N+Rnkg/85ptv4g/+4A/w7LPPuo0gdO8qyQ/aDarBN8bpx1o/yachaEdsczm3vr6Ol156Cffv38dbb72F8XiMbreLRqOBWq02d6y61m9cH8j45NLSklvcSrTwb0J97rNV+HufLJYxHk0OhexsLZ2Wf9ExbJEPGg2SlhgdKXLIZ+dY6rgIeaOVXWY5efVFqE98aX22qsbjFvvI13f0LCZTrPEw/nej0Zi7NsBaNy2WkNKGZSDPeE3RoyH5EfLnffYRyUn6ezqdYjQazR0nzBfKkN/HJ1ZT6dc2wtHc2tHREXZ3d+eO5a/X67h48SIODg7c4iP6hi9Gslz56aPJZyvwNHnh8ylT4JPVPhvQe0xxngayQlaUO3F0fwrdB0vpY6vJKpUKVlZWTjDxYDDAYDCYe+4b7L4jjanhZJukBDZ8yLIn5/Tz/GK0kKHH87bu9rUomZiiTnH+q9WqM06z7HhVzfr6ukuzt7c3t2PWl0+sfrGACEELmMb66JMO6if6W06w5XWAq9UqlpaWMJsdr5Yk3qXjyWhcr62tod1uA9DvSQYwt9gCsB3dJcEnTGkyVeZBdALA8vIyXn755RPjZnl5Ge12G7dv30aWZe6uyI8jrIYnT0cyeRE6gstAgqbMOLhszxO0iQXUYt/GvvHlb3GuLPDpCs3YkX8X2XVWtP/P65jx1YsmYWky6fr163MTtBwWnUVotVpYXl7G3t7eidWMFt5MdQwIsWscJP0xJy22KMMSUKJ03O6KIWbb8fylfCk6Bsk54kehhkB3gtVqNTSbTXXXbygPS7/HvrfKrFg5/H3K5ADZ5GXrD+KbsiZ6tfyBxQUGLKCyyU4aj8dot9v48pe/jIcPH+LP/uzP5myj2J3g0mfhY0TqUzlWzrIdzpqGkNwkaLaI1oZcfpFNOhqNUK1W0Wq1XHp+dDfpoUX3i2Z7yfuFebqyyuZ8GPPP8ta5Wq3i+vXr6PV6eO+991yAKcsytzhKs4/kbgZN/0tdxndf0/MUcL3FYwaEsu3HVFj9iFCQiuej9Wle3tL0XUqdScfHvueBNp9NHqOR67BFYBHjtAz+OU1fIMW2ieEv/uIv8Bd/8Rf4R//oH+HVV191i8vJ9+r3++qdiyGfME9bWL/hAX3+TbfbdXpndXUVX/3qV/GTn/wE/+t//S8Mh0MMh0Osrq6iVquh1WrNyTNtITrXZVQn2kVLR8BrdmNoUbsWx5U2s4+/ZfyCyxtfTFDa2la7PPYuJNvKGJ+8zaVPZvExtLZNiWtY802Bhe4yUUb8SOZTxFbh3wPh06esyKOzOWjstlot0+kdIR6S7eKL8521z0Gw0JIy1iynq/ITUyjeQr5drVZDpVJxu5QBuIWbFK/lpyBoddDolXF2vrlqMpng4OAAjx49ws2bN913zz77LK5evYoPPvjAxfhpYRL9X6vV3CR+ap+ehgwIxZnz8KBsa5n3iclYrhzzBKIlQgpHEyyXL19Gq9Wa20FHjbK5uYnNzU386Ec/wr179+bu0pH08PtIfYE67txY7paVTGsxpHg6WlVGgVyCZEaiWd4/ydP4HDpJK/9Gc6BDdfEh1Oe8Lll2vCtSCoButzsXcOZHOuZx7jXHhX+r1Zm/DwWpOPIOQImUfCzBiFCZUuhqhoHPOU6FDOLROOZCiFaI0tiUgah6ve52o+7u7uKDDz6Ym6iP8QTRwAOR/M4nrY58XGhGAYGOTtzY2MDu7i7u3buHXq93grbTdCol8vCsld6YQZGiqCSv8Oe+fH358DxiAYwUJyMGTbFqhk2MllgaTmMsKGl5R0acxZgsSzbwsrUyfJMnVuPbyueyPlJ3SJlIMokC3xyDwQDf+c53sLW15QLnRIsMUIT6L8syDIdDHB4enlh05qu/zE+zs0KOkW/Bi8UG4jpeBih9efj0r6TZAktevjytY0R+GzKiOSh4v7a2huXlZdRqNQwGA1y6dAm1Wg0XLlzA/v6+ejRbKl15g6CavNXKSMlbCzLEZHeINnIuaZHTeDzG/v6+u6+oqJ3Cd4ak2P3nAbL/KpUKer0e/uZv/ga9Xs/kp1A+/EeTJSG+D+V7miDa6X5VkunvvPMO1tbWsLm5ubBygZNjRdq09Ey+13wT7o+QjCbdwvWJtgtB0hOSfVa9ugjwehMdNBGQJzCTUi7g94M1kB8ux5HcwSXHjjVv+h2Lk2ggGkI2b0iO59UfPqTqC+1ZHn83D0Iy31qPkA/K62KVm1wnaXI71I+h9yk0aN+U4cek4jR1r+wrCmDzmAF/T3ffjcdj786eer3urjV69913TWXzZxwx30HCZx9q+U6nUzQaDVQqFXciD8dTTz2Ff/JP/gmOjo5wcHCAe/fuYXt7ey4/OumFfB5Nrs1mx9eQkS9FVzlp/o6vrhqkX0LyWqbh99VqsModq8zUfEutPEvecnzLOvMy5QJ2Los0W87S3txe5gtYU3V1yF6KQYvXxPohD22xb3n9tXFb1IfVbAktDadFpvXZ7D65YJEr8voOit/yk0hpcTHxPo/v8jK0eAuny9e/eXVCLD/5f8oiKGufSxosckaLWctxwOtAMSo+31KpVNyJpIB+tZ/P/qS85cmt4/EYWTZ/Jd/R0RFu3ryJ9fV1bGxsYGdnB71ez91bm2WZO73p4OAA9+/fn9vRW6vV5uRJih3tQ1k+jtZ3Mm+NXp8N76NrbjI2ryEcYzDfYOfChJTKxYsXsbS0BODJLhTCxYsX8cILL+AHP/jB3JZojYbxeDx3jralDr5G4qtefY4uz0MTrMDxZCxdrsxXlUkm5D8Eq7MWEsqhfrKu1g0ZC/Q31Y12i8hJ5cFgMDeJRRNmPP+iBn7MmQ3xuKVs3/cxhykEzXCWyjm1XUL9zoMSWpq8So/QbDbdalAu2OmIGy2YVK/X3f0kh4eHuH//frAMjQaehh8P5FtkQg6Y7x3Pu9lsotFo4M6dO462RTijIVj4oEigJSTfKG/tXYph4nNGZFkheigfiyEt+1GrS8hxDeUdQ1GDQLZtLD/fWObGZui+ZV9+Rfg8pL+4oXca0IxwTh+3S/gdgxLD4RB/8zd/g0ePHgHAid0qWp19/DYcDoM7lEP9r40DXpa2O9fnyPnGAC9bBgB8NITq4vtepvPRlgpOm4+fY06sBZPJBIPBAMvLy3jmmWcAHDuu7XYbrVYLa2trmE6nboGRtU5yTKc4gLF6ae2Ql64Q/6T032w2c/fLV6tVdx+ZbxdGSr4a/ZZvrPQX0b0xaG2aZRkGgwHeeOMN12Y8TczWtfoPFtry2IxltBXZk+T4T6dT3Lx5ExsbG9jY2JhrqzJo05xxbSz4bCbpR8qgCtmkFOwAcGIylq90j8EX1NFoi9Vd5hvy+7l/4Rt7s9nMLciV/oilTlp+IfgmY0PPpM7ji8V5fS3Q2tanu31yn2iQtpPMO9SOVtlXxI/gNKem9Roj6boAAOnXSURBVI3ZmH2fF2XKh1i7hfyZvPrNV6aUTWfhrwJ6APm8gGijwDA/9UnqwdnsePEIMH8MJB+39XodL7zwAh4/foz3339/7vsYHUVh0Uv8PS2A5/e8kly5fPkyvv71r+Px48e4f/8+vv3tb2N7e3vO3ufym8d7JB10DDzprXa7HT3x0Fc/TV5qdy9abVufjArFHmT+VrmrIcXW4nzp86slrOM/JkN4+4ZOn0nVO0VjJIvMT/artNVSIXmH2lSLHfh0n7ST+FxJzLbwIWa/8zFPC1FqtZqjg1/FI+UhL1vGJjR/PDTmFo1QOxSVzzHbxfdes4/4WOQyULaptinAQoOMmVC7EJ81Gg3nn/R6Pdy5cwdZlmFjYwN7e3vY29tztFSrVTQaDTzzzDN4+PAhHjx44HiWfPxFnGaVYoPmzZsjZOvwNtTomZuMTV0RkBfU8Hxn6+XLl7G8vDwXSFhZWcFzzz3nGG5lZUXNj3Zf8vwsW/iJmTmzawb5eDxW73PlDaqt1qeVIgQ6nk62szyPm9/HANidD62TNWFuYUyfwxjLzydEaTcwVxq0I1kGhlMuspflp3zro7dspDijRdOkgBtXXGjxPiraPlmWubuBK5UKhsMhtre33cTDhQsXsLS0hM3NTXQ6Haytrc1N4HMQD8nJfQvq9br7jt/L6EOr1cLP//zPY2VlBevr6xgMBupuJn6/LTkamjIrE6fFKzHnWcqDPLzC5agvGKI5xZoxl4KifB0yakIKN4XW1OCQNb/TCobkMYQsDm0e+lODbGQ8jsdj7O3tnZAVfGdStVp1uyBj+cbey7Fg1dWELDt5LBV/x523VF6QxrLF4U7pe00OpPJAKFCRkg/RYLGJQ7Lo+vXrWF1dRavVmluAxssoEsTR6OY0lY0y5Afn0ZQgVAyW8WVJl4qUsXpWkHyhBXP4+6LlFE2fYjvJOlC99vb2MBqN8NZbb2E0GuHGjRs4ODjA/v6+d1ehXHRLf5NO4LpdLiaktBzyfiZL+5INeZq6muCri3zOA9+LOhKc86pv8Vje9qHJgX6/j5WVFdRqNbzyyis4OjrCw4cPMRqNMBgMTvinVpp54JJf/8KfW0H88OjRI+zu7mIymbgJIwnOs2X5AGWgqI9+HmRrKg2WPpB6I+Q7xGCxlWL0aJMAZeq2UPmL8pfzgHx6ioVIm5pPntAC7e9///t4//338Qu/8Au4fPmy20HKv0mdvDkN22IwGGA0Grl67O/v49GjR/gP/+E/4MUXX8TXv/51l7bX62F3d9fJfzqOmXQpTdJIee2rAx3DL3ci++xYjUf4ohTpQ8nvNb8nNm4sNPggJ0tCkLRLfuG6w1K+r8xYPnIShp4R+Mkd2jU2cqyUNaZPUzZY21fanLE8NfnK/7feoRniwVjMTpafR7bQWOMnXNL1CVrZvrgY33GrjcmQzVdEJlpiF4RYOSlxmlB8Mw+0+CnnS0DfDWu1pUNzO2R/8jkuGQPzyXSeFy0CorvIZfllHMedB2XIG6qrxeZVd8b64BMgPoHvy4MYhjfy0tISNjY25gJa7XYb165dc84M7XaVq1RpZyXfYWoxekjh+epF9fAdxxBT6BTQJfR6vTkHijpK5s1XMvhW3uYd1KmGfqiOoYEqn9OEduhYDSstvnI1hRiqax7nKhV527pM4zvEu7LNNKM2NX+eJwUjKL9+vz8XlKYJ2KWlJbTbbaysrKBarbpdDtwg1QJloTpy8KCKXAjC86B86vU6nnnmGbdLHwAODw/nzugHjpUPKSCpBM/SsSzCPzE5UxY0uRsyMLX/Q/JjUbSH+F1zQGPGmiWIIstPaSueX2og7CwQCgQV6VOrsU+go1MoKEuLpPiiIuoLOk7V6rj7nDItkOCzryxtIWW7DA7EgmPy/1S5Yg3+yW+s/exrW9l2MedLG9Nl6ODl5WWsr6+7QJU87cUaKA0FtorKOc4HRXjNAp9DxmWl7ztr+1j63AqZbyofnSZS5QEhL82naRvE0nJ+Ip+Ojk58+PAhms0m1tbWXOC5Xq87uxQIL97lspIf2SVtkJAsS+ENnjf5t5yOEJ3asyL9JG0NosEahMzDWz5/3CcjYm3rkwe9Xs8tzGy327h06RKazSZ2d3edrE7pN5k2pFNS2oX4gWIH7Xb7xPUvMVr4cws02mN5FelraffIfrbEPhYhj2I+uUUPa2l4va3tWKZes8BiIy+ivKIoo524zNbkN28b2nRx//593L59G5///OfdfdN0pCPPM49taxmPWjr+zPcNLSxtNpsuTkLXHkwmE/zmb/6m00Pk//AFQ/KaJ81mCvlG5GdZoMX7KG8et5I6I6U9+LvQWAz5bjIfjf5Y/eh7H/2xfKxlheSp1Ln8G42ftRivRYemyBotbepYL0KTpsM1OykG2f70vW9xm0/++PL2pSnDXyTw+YxarTZ3R7QGS4xE0l2GDZn3O6tNHbLtpBxPscFiNp7vO423uM0eqpfGN1p6so/5BGyWzR9fzk+c5HnwY4kpJsJPSOU0n9VkrIRs0xS+tKQNb+UoCF8H8t1jHJVKBS+88AKWlpbQaDTmdrMBwPe//328/vrr2NnZCZbJj8nl5XKQYcEhFctoNDIJbKmMarUalpaWXPmj0QjD4dDlReeq7+/vz9Gq0WSBVfFamNqnoLkDTpBChh+vRfn47vXVVtCkGKqawxYzUGOO1Vmg7EAGQVuxRpB3+FrKjSkR/s3a2trcfbGho2hWVlZw8eLFOYF+eHiIt99+G91uFxsbG+j3+3P3ChOkUOT8ScE2Opee8z5XSvy+aLpcXFvI0Wg0cOHCBbz77rt49913T9BjNb5Oy5FeNIrWIyXIFULI6ORlAbaj2C3OA733rQKUq9KItrz1lYGqMhEz+PIENmJprXJYOtRljR3SP/J+8xCef/55vPLKK+h0Orh16xZWV1dPpBmPx+h2u8Fdp74go2+FqAbipdCucFpxTu8Hg8FcAEUDyUMCLTwJHZvH9XAR22XR+thXb/mMn57A2yMvfbu7u+j3+7h79y729/dx+/ZtDAaDXIY8b2eaFCo6Los4vFr+eeWF7ySMbreLTqeDRqOBlZUVDAaDuSNNy6RdBhM5b2pBwPNiQwLHNNHCz/OyICyET5IttEiQrppOp+j3+3M8n7qwiutTyqNMhAKIvncp1yUA+kRtXr+ObP333nsPS0tLePbZZ71ppR4oupPcYoNSOVbdGttVkzre8shyC/jJWJbyU3AacrlocNs6/s6bjvmkgvcH2bw0sSARC2iPx2N84xvfQKPRcPEEkuHAE7tcxiQIvliVNhFTJijPSqWC9fV1jEYj7O3tOdv10qVL+NKXvoR6vY4XX3wR//t//2989NFHrk4bGxvuuEop87mPwE+K6PV6qNVqcxtWtDpb6huybXlMssgpDhZ9K+nW7q+1xoq0kzckJI/G/DtKkweWmCtdTWfZpeyj0TchptVN842syOuv0rd8Rzjnb0qnxRGtelzOKWiQ8Sx51KvVxpb9mbJ7n7cHyc56ve52OdIVM7zv5bwMbyt5xO6irq8qIlt831rpTfF9pK2UZwxLOVLUZuW26Ww2c/e+VqtV7Ozs4PDw0J1i+fzzz2N5eXnOjl1dXcXnP/95bG1t4c6dOy5/WijL41Z5cd78S8kzmi6JTsbmNeB9z6WQarVaaLVabmfc8vIylpeX0Wq1HMGDwQDdbhdbW1u4e/fuHG2cwfiPxdkJBecon1RhQJM7cmKV6i+FtU9IWRBydMtiRJ53aBDLdg8NfF/7h4wImY/POA0ZrVZjQCJP4HRRRkGKQNXS8mCuhAxEptDI612v193RBdzBod8AnLKmo3H4EReTycQtVKD7GmPjWaOXH6Gm7W7nBhXw5N6FTqeDpaUl9x2NaTq2lB9XHGozjc6PIyQfxcZVrJ4pQQZLoC3VuLA4LFa6Ynx5mpD9E2uXIk5ZXoScLXrPf8vnRWlIyYeP7U6ng0uXLqFSqWA0GuHw8NBNqpHMopXwWjmx8i3BBy3IId+TTKtWq957sK0IBXY15OV3TT/lydfC76FAQsqzECgoTovwsux44R0FtyhA55ODoQCGZmNZsAhZpMnRspwobj9TMI0fzWlxtooE80N6PY+NdxqIHeXqaw/Ji6n6PARfP1l0VNH2pe/p6MThcOgWWRTNNxT4yztGQ+C+qDXvEC+chjyw8hGnif8dko3atzH7VOoZsu+Pjo4wnU7R7XbdbuqQH6nRQvmF6IiBj4mQjZy6Y1fSexZyyyKDtPd57I88bROCxS4rQ+/xMix+T1Gk2g+LosNaPkfMbioCi90r42I8Tvjo0SP3d5ZlbvNF2eOvLFuL/z+bzdykDgC3CGg2m6HRaODq1auYTqf4zne+M3edGh0vyY929tEr79nV6IrJCR98sYrQd1q/lMHrPt/OmrdVrvjsq6J1sI4B6VNxG9pCU4zWkG0q6fHFPPMgZL9IXZNi61h0oNUWkOk53dwm8dGlydUivCN5XV7TIcvNsvwTrprNxfOW/8f6yOL7Sz608FZZcj9kQ1n6LNVO0fjRly+VPRwOMRwO3WRsr9ebu6aDYlMrKys4OjqaW5hE9rhmR6fyo5UHQgj5E6H4je/bWDsuZGesj0Bth9yrr76KV1991W1RlmdGA8B7772Hb3zjG3PnkwNwx2rQgO73+yfysBhWmmNtZQCef6VSccescvBVdrPZDAcHB45ZLfnGyl8kLIqG3yEQgu/+nRBj+5jXsqK66AD+uCEWBPIJA+pj33Hclu99x2kDx/zf7/fx8OFDl+6pp57Cpz71KWfQr6+vu0nZUJ9bFiyQPKB7aSWtMi1HvV7HV77yFWxsbDgl0W63vWXRJHOs7Xj5Hxc+k3JxUbJGrvri5Uuk7GZMeZ9HWUuQE8vvSSgCaYxI/WmRl/xuN9mXi1p1qNFn6QtNDxflOXnsSYgWGWDhi0cIKysraDab+PM//3PcuXPHHWF8eHjorW9MpvoCElpwMWSIj8dj1Go1rK2tAXhyHHtoJyaVIxel+FbtyzayOqEWEA18FXsKUgIAPvDxYqWBtz/dH3x0dISPPvoIS0tLc3eqAMdyot1uYzabqSeH8Prkac8y+6Rsma85s5zvxuMxWq0WVlZWsLS05BZlpgSwQs+4rVmWjP646HMfaFHcYDBw/FhWvU6zbWRZdHXNgwcPXL9XKhU0m00n42L0cZlCtker1Zr7PxZI5Dy+aH8tBbHAYEhnhE67yUMHP4pZ7jixnGhihRa4pJN3vvvd77pgUZ76SX3Nj+/Uyo7lRW3AA5qkM0hPnhXovkDNRgqBt1HRUycWBdnewHzfafaJZrtqsAQ2Y7GoT4LO0XDW9QrtLKc+167ZGo1GbvdXvV5XT/6bTqduoYd1tzvRwmVSkbESa19+8oIc01tbW3j8+DE+9alP4ctf/jL+6q/+Cnt7e96xH6OTX/vCTwcM6VPuG/AfkiPye20c8e9TYd3Rz5FyxCaX+T7wq7boG5kHxcno5AfNH4hNJlhAfEzx9lg8Qk6yhNL56mahKYYUGZPS53xXPfAkHh6zX8r0RWJ+vqUcyRt5eYTb3IDeN5qcBDB3kkDID8/DI1rZRfxs/m2ormVC8ycs41njNf7MGlsFTo4J2rhEMmE2m+HmzZsnaNzY2MCLL76IS5cuYWNjAzdv3sSDBw9cGrlLmo78Py/HFQP5x2woNjw3GZviKBBBnLgULC0tYX19HZubm1hZWXlC0P8/KcuPgByPx9jf358rnyteGqxc2ISEvqYg5LM8AogYh0+8ksPP24wHO6XA8tGttbUlGF8ElnYIDV7JsEUDi6EgozX4LRFzjM4ClnJ9wpj/1r4pu76cR+j+VDIo5VjUJtJowpP+39/fx8HBgRsn4/F4zgmR9fYZmin1orKIX+v1Our1ulv8MZsdX1S/v7+Po6OjE+XkkReLcDwXkWeRIKKvHxYtt3yyyJrWB4v8CY2xlLK0tFbnyTImfGlDNMi0eXlCGn8+g7qIE2AxtLnTweULObP8Wb/fx/b2trtvcGdnB/v7+3MryGW+VloJWuAvBTI4wU8FIHuKDFyfYcvbw9cPRZ14mac2dnjbL0JuhBb98LxT6kltT/qPAnSz2ZO70ssKhFghbYKYHVm2/tAQqjfxHY1BHrAfj8cn7OYiMiIlrRwTMp/T6s8YNNmnjSXtO58+8+E81DcEGodc1tGJLDJd6H8Oa/CD+5UWOcNt5uXl5bnj0mmCPKbD84zdGE9o9QlBoyUUIJd0SB4swmPWb4m+0WiEarWKZrN5Qk6m+pcp9hvlb9VvlE4G/MoYj1a7yfc8T0xIKzOPTiprEaSEZpdpYyFkt0oZnGofWtqjiBzQytSel8VjvvHhe3YWuialPTl9NBlD/Mhjk5ostNaxzPbn5fLnUrb0+33cunULa2truHDhArIsQ6vVmotzUpyGxzpDfK6VG5L7IXr590X53jfGpA2VN++Qraj5wxadGbLZuF2h1ctHSxFofF5G/nnlQOrY9ZUVkr/kr/Dd3lq/xOgPxVesdpd8ltq/kg8t9Mr2ms1mbhEZcPKqLtk20sfPM97KGJdafjF7JlVHxMa2j899NpKPd610hPK38G1MlmubDnu9Hvb29lw6GYviixcWYc+dJkKxM1m3UnbGakwRw3PPPYe///f//tzqzizLsLKycsJZlncKNBoNVKtVDAaDuVVV2t0DHFk2f9kwPZPw3ecQQ5ZlaLfbrozRaIRer3cinWwruXPCgkUbp74+DQkAKVRjSHVwrSjbwFgEQg5VEdrlaml5f6W2U8wKTfDyFeSrq6tYXV3FeDzGaDQ6cSE3x+HhIR4+fOjo2tzcxGw2wxtvvIFer+dWku7v77v2iK1OI6Mo5qxK0NEKlMfh4eEJOXH//n389V//tZMLdC9CKjRFd9YIOSEp3+ZBTOGSYUd/U3kpbW89di4FvB9DfFmWHOLlpNxFIndn5Ck3D0L1TuWxFOS5R4/LF75im95Xq1Xcv39/7nhyuhdDy4+3d0yeU5m+3aha3imgfBuNhrOPaExZgj/SmY8hJdAoy9F2H2j5c4RWHfuC23yiz3qigZUeDdPpFPfu3cPu7u7cYqUiwXSfU2fpU05XLE0KQnaXdLpTQPxKR4NzO38Rth7lyXdJ812QPgf6LBGyeTR5VPTOy5+hPNAuQzrS68UXX0S73UalUsGjR4/w1ltvubv1aIV4mTwXGkMWea75cTyopk1AlAVLIDUPUnR4HhTNk/teRGco6J4HtVptTlYsCtrklFWu836iwB8tPCs7mEd9pgWZYz7Mz/AE50FfcoQC7nxRTAjEr1mWYWlpCbVaDYPBwN2jKk/sS5lk8MnPPDI19A1/R/b43bt38V/+y3/BL/7iL+I3fuM3TnxD8SY6GYhfLyFtXBnHiflFPhlJ9NFORDkeZV+FbLZUGZMXWvmxCY5QmRY/l+wKfmcnbXTiNOTxHXzvq9Wqu1oMOBl74eXmLacoQj5TLC7LYzDcl+Tp+Lu8NPHneXhV2vd8TBJtqTH6EA28HNpA02w23fUy0g7kdatWq3N+OD89IE+MddGQsinVB88DyX/8GS9bQ+p4KuLj8viNT37v7e3hzTffnPuGYzgcuhOMFtmmZ4GQrRjldO1jyYS+hiKjWJsdJ+NkNBrh/2vvT34kS5L7cNxexh6RkUtV1tLV23T3TPeoySGH0FASFx34BXTQRbrpqP9Bf5UOEnQQIUCASBEEKIrCcJkeDme6p5fptfaqXGJf3++QP/OysDRzN/f3XmRWd36AQkVG+HO3525um7ubT6dTl/6PnogdDAbw6aefwv3792F/f9+1g7uw6HFm3wXHPsOcfh8b2KHM1uv1vGlW8/x85y2mjqDt83K+9rQyMRPO976hv33Q6CtLuVoDrhYhVYXC1/grZHSl1h0qK40DN+a5sV80QCAFy/Eupjw/T+PT6XSg2WxCnufQ7/c3NjDgXYfSxe/U0KG0Ss48fxfJybKcjgIAZ1xIqctC89VSPze4qgiGx9aDtFAaKWJkkI//JeeSP0vHWRqzWIVdlnPG67AscIXqpTRKOsxHq2X8q1pYsxqDvjqLOr2WZ32BAHweg91oWKJzQ++rLDrWEixOOP5O5wTXd81m0zkyfMGYpn7EsYhxGqkDv40gZEg3cRp8c8Nnm2F/oOMYmkshpxQdkkePHrm7YiV5yuV+EUfIB8tc5nLN4niXNcY0AIhXjuD1HmVtdrG82zZRVv/56i3KOz5bPoV+3xzG+UnvndPaD/Eg/Y7fWUTngtWnoN/Tk06+PqB8HQO0f7WgWdExjRk3zZ+2wGIbh2w+axsxfaL5RVwXWuQlp9fSt/icJV1p6ljH6K5Q+/zdtPTRKbJMs2F530p+gnV8rM/R+c0zo/jkUKwvYfG5y5LfGiTe5e1JNkqRtooghQbNxrLAmkpYAj6HMQ2agl2LWXCa+W/aWEi85+Mr/oxP/+HmyMViAU+ePIEPP/wQdnd3odfrwb1792A8HsPnn3/uFmKpTOCn4ULQ/DH6ntpY+O7Qlvoz1tcP0a3RyO192r5lbFJpC8kOn48kfe8rk+fnm4J4PG1nZwfq9fqGz8zlXsy7SbI4xoYJvVNsnVhWy8YQku8heulzXB/SMrS9GLpD9Uh+qVZe+41uigjJL9yoQmO+VJ5QWzrW5wuVl2REGbZMEYT8CixTlX0QshGt74p2FP6PPCHFIWnWNi5XkIaifVyGHUG/T61PepfgYmxI0PiAi5Pz+VwNAo/HYxgMBnB4eLiRrhjg/J6C//Jf/gt0u1145ZVX3Pf379+H8Xjs6KMnWTidCC0Qp91XaAW+18HBgbtLSMNkMhFPyiJCxgRtL9VIREj9QScL/u2blDGBjLKEhmTwcKNHcuAuE3zibZMu7txyWE5kWUEXLBDL5RKePn3q2uh0OnD37l33+507d+Do6AgAznfEtNttR6t0RyYf2xRDn+9S1YCyAS8kl+Bb3PKNMzfOLc9cBqrcFe9zXvh3OO542pqfCvDB4lzE0GyRnbGQ6OBpvaX0itRIDcGqt/kzRXnSEgwpalhanw85xBh8oI5ADA1Fob2Hr26+Ma3X6znjd7FYbFzxgBvZuJ6PuX8u1ijWAi2W53wLMlqdkvGM81Z7HlPhSymqre9Ky+HmnV/96lewXC432veBy5YizkdsYCeWf0NBn1B79N3o/VPr9RrOzs4cTZ1OJ8rWtLRddK5eJfvyMpHiIGvlMYDXaDSczcXLSgEuyY6iug43p2TZi7TFfD5y3egLOvF0jFQPc/qovVBmsKYopMBWKLjrK5MCastTeuhGIWlsNfpiaJPGp6w7WH2BK2yHnhrzlY/N9EHbKYPfpPm3Wq2cP0TTlWK70nNSvSHfCQNzvjp8Y56iR7XgurU/uc0RsiF9Ps+3Ub9cpt6MaRfnJz/5xuHjCdxMBrCZbh6fQ3vTZ9uEaK6iP7Hter3u+uHLL7+Ep0+fQr1eh263C7/9278N77zzDvzn//yfYTgcuucwgE5PtGnZZyxzSjtkQ9+Zbg7Jss37x6VnQyjSn9p4lDWnLfUgb6XqM6utTa/owZNsCNxQBrAZxyvzrvmywPkjZnFOGg/8LjZtvq+sRY9Y/WQsz2m26lJejwZ6rQzWocU6W60W1Ot1GAwGG++qPR+ri7XfJHvdCmnMLfZqTP2a/4P1Y6yCZ8CktkeKLqH6qQhwUyn69bhuJ6HRaMDOzo7buL5YLDZOyOI7XSU/KhXSO5R6BpwKYAAQg5q9Xg/eeOMNODo6grOzM3cvI8ALZpnNZvDJJ5/Ao0eP4PDw0AWhx+OxO2UHsJmC0SdQJVAjCP/HIKVP8HFmkBZV1us1zOdzMV0G76+QQeILEsQKT/4s7TOeTkQL3Gu0SIqA94vP6bSAC3PJ2AotOKQI26KC39pukQC3bw5IvxVVFnQeNBoNd48IDabg4qz0XpgaeD6fw3K5hOfPn8NkMoHVauXuiJ7NZhv3L1v6OCYIgff64Tw9ODiAbrcL8/kchsMh9Pv9C23SIF6Rhcqr7Ghb+1rrZ6nPUhQoD8hJdfvowzr4d1xn0PYsdfJnND6w9g9/hssxmi2Cfi+9g4+OokZMyvOx41VmvSHnRjN0KfD6gyzLXDBFGnPOo1qaLR9PWDeGYR1YHgMgBwcHG6lduA7Gf5h2FQA20opp7Wh6NkRfCD6nIWYuxui3o6Mj+P73vw8PHjyA+/fvb9iBqfODpwL3BWS0uWjlf835t75DyEbw0WhBjKyPeecsyzZODNLfUtpJkTeSTVsUqbqxrHbRPkPfhy40XiZS278sukPtUn0g9S/lq1qtBu12G27cuHEhJXHIB6qKflouJbBeBZ9rdGi/WWEN+Pja5+UkX1iybS19W8Y7asiyFwugvgA0jXXk+YtNkpIPHsObRXTwwcEBZFkGp6enF06vW9qlek86GWnRO1z3a/o2BEkfbwN83MqQp7H+igU+eVKWTOT1hQLjCIxpxNSN2KY9oPE5/T3LMreonOc5fPzxxzCbzeBHP/oR9Pt9uHv3LqzXa7fhXgre8wUDukgVMz9o2fV6Dc1mE+r1Okwmk2B/aza5NqdDMlbrL/q/9G5lzueQD0E3EqxWKzHW7ouBcBmPv/viIJj5ka4FcL+xrPe3zhWrrA6V0XxMqw9C2/LFDnmMR6pfG3v6veYXWuu0QIsJSH3F34v+jxs26PVveZ7DdDpV27SgyNj7oPnfPvp8fa4946tL4z9fnbG+A7Zj4bcQ7VmWbVwxxA8FYByLb+6TbLmiMsQn9zRosRf6XRG6LizG+ozoUMfXajW3OybPc/FetX6/D++//z7UarWNExu0/tlsBr/4xS9gPB7DzZs33ffj8RiePn3qymu71iyBLr4rGgDcQpAEqoz4IisfCLzvUqMHIaVgkyaWxUDGCaMJX8kxoWVS7i/zCbQsK/cuRVqfpNCrdFAttMUqZg00YBMqy+vU+kcaC02AW8DnQLPZ3DjVTjc0aPNpNpu5O+AWiwU8fvzY7bBcLpfw7NkzAPDfDSrxgE9xcCyXy415enh4CIeHh7BYLGA0GkGv1xMNXL7zMgQ+L8tyEnkbEiQZ7uNVC32ajElBiO6QHNHq8NWrnWz1KVtfuzEOmw+0PnpqQlqMTal7m7Ixhr4Uw0iDFPiRDCZpvKV2MTBO75Wm40Ofp//HBFVwPLVnNPooL2dZBjdu3HALx5r+52k6+WIs532fg8X1VKyBjnytBWl8cjz2Ph589tatW/Cv/tW/gr/927+FBw8eJGdG4bTEOogSn1qe459DMigFmkNvhTR2ZdCJ9w/Rdni72EaoHYsjysta7qO3OuJV2ABWIM+hv0PtoCx7kWmoKp5KeTZljpRRXxmgspd/T/9HoM555ZVX3E5yTV+V9R5SXRbbR6Kfg/umITp836fQGCubearNGPj8UirrNV6w1KmV98nDGMTcPYubBfBUg2Uhm/OCL7hlAc6tw8NDd7qGZqPwxShom5xGWi6Wd/nc1mwpjS5fn1QpB6T6rP4QR9k6hNJSlS+j1U/9rlDb9ARslm0GoDk0nqH8Yn3XWJ+DPyuVoXMbAOCjjz6CTz75BO7evQv7+/vwyiuvQJ7n8Itf/GJjMVbyhfg/utE+5j3xtG6z2YRer+euf5Pg413tnXlZnw+E30k+kfQ+fPE7xQ7V/DsOXBDFdMHIl7ysFjfUbHiNVhr3azab4sncWP706bkYW883r2m9mi7w8UqIrjzPL6xV+HwUKmus7yr57ZKtJM0H3pblnfC3kKzS7DXsE/wb9TVeVQnwYgOB1nbMWEk0UPqssQvpPa26N1WPcoT0EJWvUsxGosdHV6xvoAF1IdbFDyzi4my73d448IR6BfkNY0Yan1tQlo1T5BkO78lYqxDCS7t9zkun04Ef/vCH0O/3N8odHh7C/v4+tNttNzD8NN1kMoGTkxOYz+cbAj6UQgRAV1TU4UDlHjrpJqXbODg4gE6nAwAvmIuXmc1mMJ1OL+TW9zERZTbtvQD8k4sKWV+QKJaRYhzBqsAnD+/3ywh2UUFBESMwJKOuKqSMvQQpBQ8HpgbO8xxOTk7g6OgI3nnnHTg8PHRl1us1nJ6eXpjnHNQIiIXvJBjW3e/3XYp1vBdlMBio6dBD0IR+GYjlE05LrHNdBqxGgCS3+H1VPj6x8LfFQbPQWgaoo5plGXS7XciyTE2RjaiKZmu9ZQeC6P8pz2q/aYYm/57P8+l0qtoG3P7gu4F9sAT5YvQGpndBYzY0NhhoqdfrbhNdlmUwGAy81ylIwYAYoA1kySyAbYXsGP4M/X+xWECr1YJXX30Vbty44crlee4CORgkoWmKU+c5ncPNZtPpvaJz0BdI0OiQ6kltP9ROmfqhVqvBbDZzdh3e5VsU0jiEAmxVQWtjG21fBXDZyYO5eBonJoAnzRcaAOV+FXX6aRCj1Wq54AB+j34bBo6k9q8SQgEb6buYQGcZoO3wse50Ou7kU5ErIHx6sIr3LCoPqQ9Zlo+WQkO73Xa2wXQ6hePj440U0iFYYhMWOiR5LenCk5MTqNfrcHR0BKvVCk5OTsxjoQWp6bz3IcaPsOifGFxGrCOEq0iTFaGAfKgMr4um25cWMGhZbrtqbXIZEfMOGp2+9vL8PIsGvgtd/Ox0OvCTn/wEnj9/Dh999JHT3WhHa9nRQva/1BcU4/HY2fYYP16tVs538R3UscxPTcfj+GF6Xl98xzIOsfpCkp2S/MKsb41GAxqNBhwdHTn+m81moo9n8RmoP8af4aDXSlE7HvV5WQtUZcFnM1nHxxe3Qn6xZNej9mtsnMoSo+ffa75ILLjc0NZocA7Rk9SUZ/HKJVyQw5PXUiYqK72azI2JY8VsVqsCobG1xAV8vMDHT+ufGLmFPhON1+KmU64LuTxFP1DyAaqMa2wbG4uxFsdN6jTptBjv4EajAa+//jq0Wq0NR3h3dxdu3rzp8ovTeyex3Hw+h9PT0wtpkLUUc3wwaRn8TE9hWO7NRMFBgQHzfr8Ps9nM7TLn/bhYLGA6nbpAH30HX4DYYhT66qFlNeajJ7BilKEmELbB5Hw8tbHx/W1BrHFAjccYI6wsGnxlpfmReieipNAWi8WGPJCexXTjk8kEut0uvPbaaxuKdr1ew2g0cgpXMw5iHF3+rEYb5aNmswnNZhPW6zVMJhO4f/8+rFYrpxSuEsqcb5JCr2I+SwpfK8cdMmrAWQyvmGBkioFbNmi/Y8pZXCwK0UR1L/8tFlZZHtMGH/eUAGnMu0k8RnfZcb2B70wzVtD70SS6uS4KOaiabRILqf7lchl99ytupMMNZXmeexdi8bki44h2kBakkdqj/yxAWY9jXKvV4ODgAHq9nvu9VqvBdDp144vPxNpCGjD4gHMYabG+M6/L97f0fKwDn4qydAWV1zs7O7BYLGCxWEC73d5oJ0QH/TslOFYUKeMb+r4ItH6g/I78ovGZ5ENRmlN8BzqfUafzoLX2PiFw2U99Bf4e9N3xd5Sjkt61bASLpZfXT/si9uQ9hfSc1U/woeic52MhyapGowHNZlNMVcdpKXve0Dpj6o+ZP5qMpt+F/Fr+fZnyDRdid3d3Xd18flbR9xwWfy/PcxiPx1Cv191pPXr/XEwsBctbfAduf8fySUzfSbxEkcKvsTRY+Yzbw1WgKt0e0mex70RjbHTeSODyAb/ztY+LgiHw+mLeA/1t3LxIr5ja2dmB119/HRqNBvzqV7+6MPbaQReNDqkPJKB9ePPmTbd5arFYbNxL6JPHqTqM2gpcT2gIxTxi56yvPLaFcTVcEMXN3QDgYls+un1tWOMq6OthBgW8L73I5qoUpNinIV3u+43XQXlFimvRMtJzUntS7Mf3PtKzIdki8ayF3328Tr+nd3NTnsXFWJzXdCNIqs3h86GL6KkUesrUiz4+8I2ZNi6afrL63yGfg/o1luu8NN+U/l4mqrZnNRS6MzbLso07yhAY4JIE7mw2g+fPn8Pdu3fhrbfeglar5QJVy+US/v7v/x6Oj4/d4uZwOITpdLpxoi3FyPMpfv63pe5erwe9Xs/t4OcnbfGULP5LvUwdIeXf5+Df8fu1sEzoTlwAUCcJhRSUKQt0HOhuom3tSrEob75DJyXVM5YLKUUNNOgstWdJ4x0LNKrwPkXp7mYAcPPCd6L0ww8/hEePHm3MF1+7FsfcZ0DSUw04Xjdv3oTDw0M4Pj6Gp0+fwhdffCE6EmUGOspGbEC0yLtYlXIZdTUajY3U9xhgkWSbhjIMt9jfU4BOE97RwzckUNnMnXqNHusJRKyjCplq6auyykjPWJ9DB0DqAy57QgZwkfukfaBBAL54QTEej+Hx48cAALC7uwt5nsN8Pvc6eZPJBM7Ozi5shitib1lQlOdS+OLWrVvw/vvvw69//Wt48uQJtFqtpBOx1FGg76Et+PNnU+CzOfB3a8pIBOWrGGwj6JoCaV5INlYKX1+1d5Ug+QlUZ8znc+h0OvC9730P7ty5A++88w588skn8Pnnn8Px8bHbPBqjY6lseRn6qCi0uUf7gp641eYKylvs7y+++ALOzs7g4OAAlsslvPrqqzAYDOD09NTZCJcFLiP4WMfaoSFk2Yu02RiMkzZqhmz/ENA3oHVJ46Xxdui9sd/onMqyLEpGc2ipjsvs/9VqBcPhELIsg9dffx3Ozs7g+PgYDg4OYHd3F7rdLgAAfPHFFzCbzZyt7rN/JN1VRIcUHfsQNFvb52fSu7djdDCvly5gXAX/M/Z9th3kLhuhQHEqyrCncXO47+Rcqs+E/1O5RU/z/emf/in87//9v51s2Nvbg/F47GyL09NTaLfb0Gq1Lmwu0iDxljQnUIbyOjEedXBwAIvFwl17hb9ReckPoUib0kIxOXpii5dN0dG++3sl8NiA9Gye5zAajVz8GDfYcJ2GfUk3oWn+AB8Tyb7BeJ6Wrli6N7gq+OS1RZbj/5axCcksyVekv0k+fcxcpjT6fB/eLi+P4BspNJ/A5zvzsdbmMsZ+d3Z23AZcilqtBp1OZyMd9mWAvmvVtkcKOL/4+J4/IyFUNsYeoBkVQplt0Ya6TH9nm4hejJUMRQQyJ797tV6vQ71ed5MId+TgnYzr9Rqm0ylMJhN4/PgxHB8fA8CLnOG4+0qiI8XY8D2rMRovW6/XncCgqaxoH9BTNSkKltPFlV8Ilkko/R4jWEKTmNMdA24kYXs8GBBDEy/j62+L8UjvWyjaJi9nQVEHNmZMeGAPF440pU93PXHeXSwWLv3406dPg4trZRhCtH06h9BAnc/nGwY8wAsD/2VFmQ6tRclbaaL/a8AsAlLWBR8/SPX7DH+p3DYgGTl4UpHem0HLS8aJRR6m6gutzjJQliHrG7/Qb3R+8KwV1DnneozPBT7PNB5PfWepPm1cUL6ORiNotVrQ6XRgsVh4FwUxfRS9QzsWMUZ5WQa+BVg3bhBEZ29vb8/Jdhx7HuCx0IFjz3kAbVtt0cb3d4hvte98tpIEn8NuHROLjRcTxOX1oKMesvVibBreV1XJOCu25dTT963VatDv9+HWrVvw5ptvwsnJCTx69AhOT0+DgcEYvvm2gs45nyym/1sCguPxGJrNprsvq9PpwGQyKX1BxlqXNahHZaAv6OhrA2UmBQ3aFH1/H02xNlLKnKXP+GihC85cH1nGIBWS/4bZgrrdrovdNBoNlxUM34EucND6Yv3ZlHGgPIeLJI1Gw2UrC71rKm2+OjS5uA1dY5XJMfxSlLdiEatHfPPaEhcIfV+2XpP4IdY/LeKTW2WhJHPu37/vPtdqNZfZEDcB8mxOKXyjvRP/m8eZms2m6mNI8zcki0P8Y7V5LWMj+Zo++mkZ7nvgb3jYCbPX0QyNeZ57F15jwHVHnufezWOWvtmWvJF8eq2MRbb66oixQVJ0Bo91xegozksSX/Bxi21HklnUtqWZ7+i1jtQGtC7GWnSWNK6x8YhYO6cqpM6jVPpj9Y4UW6E8ZvU1itBwVVHoZCzA5iDO53N3bxmiVqvB9773Peh0OvDkyRO1jr/+67+Gzz//HMbjsZtoy+USJpOJOvljaNS+1wx1Cks6vfF4rAaMEDG002CRJBgs6YtS0tD6diEUSZeFSFGuPpr42FzmhCzLWYkRjBYhS1Hl+PHT8FI6N9yg8PHHH8Pnn38Oi8XCLbZJRrcvvU0MaN14ch3TYBwfH8Px8fFGOtIse7EjvyyD8DIUdhnt0bStMYaQDyE5ialTQ4a8ZgjT32Pk1jaDDbHQsitQHVFG+p+qeTTFQS0LIccewa9eCAXhqKyTgqgcPueEluHBW5QhGi+sVis4OztzJ9vW63VwgbXVasHdu3ehVqu5k7G4K1FDKDDgw3w+d6fbY20KqwzN8xenUzA18YcffgjNZhN2d3eDG2yK8hxuSOSB6ph6aWovK2i6Jw2Uj6qQd2UGdfI8h7OzMwDYvm13GbbkVXHqr3G1gXOY2kexQTca8Ao9X4ROze9F+UM3PGbZi1SYqe3R/7eBMuYsj5l0u11YrVbuvnq+sMHHHpFlmdv4ntoHaPfgqTfcqIV61Po+dCxS+0cKLGt2B8YCDg4OXHrlk5MT+PTTTy9stAu1yQPEFPy+P8u7WRfeQjTxOq5hw1XSq0U2K1TRFrUHNVqoPdtqtcTNnY1GA27cuAHT6dTJDokWSRdpix+hvlmv13BycgK1Wg1u3LjhZJeG1Ow3Enz10NhtbHshGjT5RzfQcZpXqxUMBgPY3993m1CbzSYMBoONtM7UZ7FC6lP8G1NZN5tNyLLzE7Oc7pjFQ44yF3GlZ1Jo4rxL/Szqm21LHnEfUtNrPNZgWWTmGwAs/SQt/vPPw+EQ6vU63LhxA7LsfIPFYrGA2Wzm4ga+67skun2y1nIqXaOX1r+NU5yxOsPHj6ntlwHp1LXUVqov4MNVsgcQquYKCSHLImGr1YJms+n+X61WUK/XYX9/H3Z2dtwJ2PV6DcfHx3B6erpRDx+s2IXYkDAJLQhI5ehpQLp4Q4/O48SUBJd1xwVXULSsdTKkGDm+iZqyEE7bjC3ja0/rY3xO+uz720JjkWelcmU7VVqAP2TIpdbPHWQpaACwmaYCy85msw3FqsGyIBDiSS0QhMDFB/qblAq2KHy8nDKvQnNV+y6W10OBECv4woRUPxqOdKEGeUsKMNFFBZ9Mk95dexdpHlWtuCnPYSYJmv4+ZCRa6bPqn6KICTrFBJFTadHeW9KHNNgW2t0dmhOh33xzkjtxWl1Sqh9ui0g04bzgqbF9+lN63gdNJ+FirFRO+jsEaYzx3XDnPl6fIS0A4w7cohtNqDzi71GUz2Pl/TYQE4iW+sUnj5B3kadpFo6ybakUG6IoqnoXC3DTCG4i7XQ60Ov1YHd31wU0qf4N+Q0xkOQezlUA+Y45zZ6V6o6hg9Yf8iWkd+D8ym1GtH2tthPye0gObYNHaFtFxp0/L/E91yX4DD3BU9R3SfVfrfVL/j7+RmkIAd+b0sxlp9RXvI6U/uI6Gu0KaofiPF2tVtDtdqHRaLgsZ1q7MfLDKos1uVmv1931JriQjP8ojVq/+eiI8aFS5k1KXIa256sjFdZ6i8qJGGhxsZj2U+VJyGYJ6SrNh7XUmzKP6DM+X8AHKt9obITGQ9F3xSxnKEOq9uFoZsIsy9zC3+7urrvblkLrx5i4SIp/ItUj0WORQRYZS8eLjgVm32q1Wi7zJKYVxue0mIkGzRaishe/z/N8YwE3VW74bDNLOfpbyPaT/DnLsxp/SbYilpfaikFIVvDftHYkGkPv75NhPno5qM1B/QJ+sCcV2vxPnd8UVp6S6Imp3wdNXvrkHZdBRfjPSqf0HKW16BzQEPIhrbSXafOYT8ZyBSHdFcvLv/rqq7C3t7ex2HJ4eAi///u/D19++SX8xV/8BQCcvxA9NZvnuTthG1JMVlAHgrYjgaZWWK/X7q4aAIBOpwOHh4fufjVKG52E8/kcsizuVIMPqLQsd7Gk1h9bp0/w4m+WFH1a3Y1GQ62/SN74ogIXjVCtHsupZUQZQj8WRY1jOv/z/EVKbt4G/qM7W6xtWw2q2D5AOnFziHRqDN9tW/f0FBXmZdPIc/kjfZYMAbGgcwmNLQzsUIxGowtZEng9VsQYiNvY6YapmBuNhtsN2Gw24YsvvnBp4ST6fO+BeidUrkpYjDIfbUUdNKov+feSIYrf4cmSwWCglsP6ipxAKQpsF4MPk8nE3auiAWUaTQEe2k3O2+Ofrc8A2BfmU2QvtxfxPnMLsB+orZfadio/IK9SnrUGpnm5lBMAUr38u9j6tCCjJBuK2q+pTuB3Ccinu7u7sFqt4Ne//jXcu3cP3nzzTZhOp5BlmcsUgunyMV1u2XTQOYNB1NVqBePxeMO+5mlqabBAO3FuCSbyQCEPUvGgMn2O0kK/w3bxOd+ubomuPM9hMplAp9O5NL0SC0tAIjbAsVqtXHaURqPhrhDJ83xjXK4icPyRTrpYECPbaX2YQp/yO+W9MoNCPuDC62q1gslkAm+//Tbcvn0bZrMZPH36FO7fv3/B/0X6pKumYu0I+s74vOW9abrler1u8mWkU+dWPqd2DK/L0t5VgC/ILuGy9W5s+1XIEIs9Q3VOiK8kHeVrMxaxcgNlM8ZI2+22esUUPz3LD9ZIQLnoi+357qCt1+tw8+ZNZ/8+fvwYvvjiiwvl+L3jRWxHKsuRbp7VxnqoKGS3WGwafkcnbkihh4eazSY0Gg0nz589ewbT6dS8wOXjQ+mUIS6I8/gb+luY4vqyZQhC8qcA4rIhSHWG4h0+Pc/H3sez/Dd+appvcNPmAqeJ63KJr60yVeJlH38jv+LvNINXqE1NrsT47EX9+20iRl9bY6pVvzePV9A7uWkZn67U4iExiHm2THl1IQrnq5wGzulA4okMLshpahgsN51O4eHDh3BycgKLxcKlJELjWFpk8wkbH7Tgoc9xp+0hLdxZx3SskoNAHaNUunl5a0COf05pT6s3VEYKWltgEaLW+kO8G3qe97XFYLIgReDHIFSvxEMpfEGdX18faH2LdXCH2uJghMbfJ4RDTmXofaqC1REqSzlY5owlSK4pvZCRaA2Y4veSrgnRxr8P9UcVDnkMcAG6VqtBq9WCdru9sRmIB4KxXygsTr9vDCVYeS62DyXjP0aOWGnmhp21fl4XD6r56sCxinFGyuI9dJrQ3uIBAu4oYVASy9RqNWg2m9DpdFwa97I2klF6siyDO3fuQK/Xg4cPH8J0Ot2wEWNhGWd6bxzaoDs7OxunAMtAbGBLkn2SnvM5S9I8suhTXoek/zSZUYRnpfexBCm0Z7U2XnbEBsLpc5oelmww9GPo5oxUh9Zn58YGMqwBghi6pL/L0oMxz1n6Yj6fw6NHj2A6ncJkMnH3vdHnqK9ZBqr2UxBc3vnGmtvm1sBdLHx+ZhmBLV4nPsPfH4OMuEm0Xq9vZLy4DPCYDH1XlBmYVQPTmUo2h4Qi84vqI9qXuGlzvV5Do9GAmzdvwmQy2UjHKdWFCAX8Qu+ilYnhLc1XSuV1zQ+wPFeGzL3GC0g+UMyzlOcBwn1t5UvJrw7ZXJQvce7z6zm0mIyml7mdKdmd3JahWTzm8znU63VoNptOLkmHJ1LsFS3uRP+W6Axtjoy1ca2ygPfzfD6H4+NjaLfb0Ol0XN/hAYTRaOQWuGJjYhJNKIc5r4Z4qoicpWW0cqH3kuZXzJzz8Qm33/B3rW46x8qwrTW/0Ve3jz76XiEfUmrTV99sNnMHFbh8woVZ3CyqyRkfUv0sjUdTeI2WKepbW/k6xZbwPRNrX3BaJb0W6stvm42xEYmyBHH4hdyogKfTqbchdDKePn0Kz549cx09Ho9hNBq5cpiasexdMtJuTYAXu66lXYk8TSQ9MYPgCitmJ6QVIWeVl40JLmgM7xNSlvrLuFfSihCf8LGwBHq4UHjZhUBZAUqLgRYykuhdjFoZfsdB6ASaxvecD6V5ydPibuu0W5lBtKJBTloX/SyNt4/uGIOQnojloPeI4d/4v7XfQmVDBnvVQAe20WhAs9mEvb096Pf7ALCZZpae1sNFM4Q2H7clf1P6MDXgaQU6gFZjOQS62zmUVh0X/awBEqyfjyOnk5/M4jt0syyDdru9UQfuLOT1Z9n5yTiUsZhqrNvtws2bN2G5XMJwOBRPIFFjXnsvyRGlffbjH/8Y3nvvPfhv/+2/wZdffulOsEqnyEKyJrTbfr1ew3g8duXm8zmcnp5CrVaDO3fuQLvdVusvCzEOoQQpmIH9aZH9PhkeGs8U+BzVUECB0ovjK+kJa1DkuwbsM1xAwcWSsmXsNarFeDyGDz74wNkIeHIEQF5wv2qw2AX4bkWxzROFMQHHEKR7YLE/5vM5tFot2N/f30ghGaIthR5NNqAMppuZfMCNTlhWi6vE0GENUlIb7fnz5y7teq/Xg/feew/u378Pn3/++YXnLDEvC62hgLlvUUprs4pY0ncVL7vu88XkpDmSIgNSFzQAwG3gPDs7c/4E6q2Qz2SlT3pnjBXRLAS42IgnZHmGvSI00PkqLbBq8VU8XYgLnVJ5LIuyQuszarNb/Hxqd49GI/jss8/gzp078Nprr7kye3t7LisJj3nHgvoTWBeOD/0ebXtJZqYsFpUNaYEoZl7he2mb5+kJWN7PtF0al+SIiTNYyml9znW5xndFTg5zevI8h9PTU2i1WnB4eHihTK/Xg/V6DU+fPr2QwliC5pOW6f9SObptnWNtr4gvGBvv09ZRNDtPqt9qA77sCEpaSbnwQDEXEhgA4IIdg4W0U1erlUthwDu/KmYOBRJpKj8E3V1FlZs26TRlkqJkNIXv6yfrRLF+b3VALQudPmB/0sAD/u8L5mlthCa0pS7az5qx5Qsyam2WKVysArZIm2j40r7kKTWk/pBS3mqf+TtYT5qlyAp8H75zsiiKBj5jlGroGa7cKD9Kz9B+KMpPPueRziX8jIsy1PCbz+cuwEONLd9c9JWRsG1l71ssRNmHKaBQP1Jeleqj/8fQ4futjPnA+S+kc2LHK/TOdBGftkXnAfYrDYZaeUmS7z6+5PWEFq2scxADoVheC95i+5PJBFqtFty7dw+azSacnZ1tPK/BYgcgr67Xa1gul9Dr9eD27dvuTspbt25FBWdSAks8mLG7uwvvvvsu3L17V9xsU9bpI9+Yad9zWyeUGcZie0n2k1TW17dlyEPujCFCfZEKra9CgcayfY2QTKDf44mysmRuGcA5jGmKOVJ5g/pP+K7L5RKazaZLnYz3vM3nc5GPffyk8btka/p8KpQF3Eah8t63yZP7KlTuW/uO29Nl2iloY+T5+WJbUftX8rOsvi4GzDFV/Hq9htlsBovFAmaz2YZ9zvtAm98pfjavz/c7/8zL0H94F7mvf/E3Gpyu1WrQ7XbdBvjJZOLGyicny+AP3g8YqEc7HDOaDQYD2Nvbg9deew16vR4MBgMYDodwcnJirpvTzudizPvkeQ4nJycu40a9XodWq3WhTKqtbJkjNAVqbExAemepT7S6QyhD3xWloQhSdORV0akA/pgbfzc6VqG5EvObjzYar5V0KI/vYAYduuBJ/ahQbMXHS5IfpcUm8jx3GW8Azm3oTqcD9Xod7ty5A8PhcOPQj68d6Tdf3FCKsXDw+Sy1G/IludzSZItEH9pzOMaY9h/j9+gD7+3twWw2g+FwuOFDWdrW3pnafL53k55NgUVv8DGw8GEo3uRDyPfx+Xw8xTD1E0P2pcRr2kKq73leR6iPY/WMr3/QHkSgrcjLYj+FfHCLbecDbyPEBxa/9zL0KCJGT9B31vSD1i+pepjWa5U7V0nnWxG97YXu0NXuXMSTPsPhEIbDofvu9u3bF5wImpIHYHMxINSpsUzOHWsKPqlx1w49gcADupJQ0+5P4IxkZVRu1FPaixil1PjzTTDf7ixaJ9ZD88rH0ol10FRpPKhBF/9iBH2o/0N04f98QV5aiOTvoxnSWjv0s5VPfEECqyFpAV+M5fnceQCJP8PpoEEIzi/a3SBaP0v1h9rkQZ4ycFUVQciopPxN+yXG8LY4I5wfsL1Op7ORZhUAYDqdwsnJiQvUhYLWkkzTYAkqVKXYfQs/2D/dbhfyPHd3pUk7fFMCSyHEGJo+hOqw8pZWr2VXMA8ESO1hmj28g0STzdIdYpQe/Bw7Z/h3XK9Y+A8XY0P8jnWNx2PIsgzu3r0Ly+USHj58aKLZOheQh9frNXS7XXjrrbfg7t278Oqrr17QG1aExlEKiuB3+/v78Md//MfqCVy8JqOMuWStA8vRk59UjuI7pJwuokF9bqvFvqMU2CkqFzQbvAxZK/UX1X3bdnwlSPZzEf6r4p3wzljJdvaNk9VnQyyXS2i1Wu50BqbSWywWboMWtys1HsLfaXm+qVaaD5Sv8/x8kzHNlsJ9Qfxf8p34HA71ybaB74N3ZC+XS2dfFbWDUvgQ0yRimnwMBs9mM2cHxcr+MmAJAGkyjPKL9UQzldsYIN/d3XUL0vP53J0Aqfq0JKcPF16RbwaDgcsycXBwAG+99RbcuXMHnj9/Dk+ePIHT01P3TrH2n8RrXD7y+YpYr9fw/PlzaLVa0Ov1oNVquQV+rLsIf/ie9dHNUcS+xjpj69DmcEw91nlWld9UlP4QOH/5ymj0WOr3tZtSn2SX8THQ5gwtH5J5tD5c1EOZQOtB+VXkGhCpT6hO5/Yy+jQAL2yKbrcL9+7dg/v378NoNBJjR7ReDVKfcPvcJ9uxbyndvo3v3Daxwidr6N2fKLvx91arBbVaDQ4ODmA+n7uFaxorK5IxTltspPVr9EvP0c8pYxfbRqhcSBbS8lJZ2j9oO9BYep7nG3qfH4LB53ibtG0ff0rvJNHOyxX1W7j9LPnweD89ls+yTDwFy+ngMpHzHC8fM9e0fuLfpczdq+QrcMTweej7FFjquMr954NZU/qcX+p80HvL8Ld+vw9ZlsHjx483Uq9kWaamN6ZGgeZw03JaHVKdFDxYK9UlKQpJqfP+kQRgjOAqYvSF6rPUKfV/lmViWkWpHTQ4Qm0gpEW7PN9c9I5VqpzOshz1suvSUHbwsGhd3PGVfkcjgqcm4eW4QUqBxgjeP6IZMtQYln7nZbV32sZYVoEynV7el1g/vzvbRwv9n9bLy1kDe1mWXUiN74Omp3ggjAfINHleNdDYHI1GsLe3B/V6HUajEcznczU1nIXHywhQWJ6lYxkrq8qac1Ze4uWRXlwYbDQaoj0Q4xjH8EwKf8U8w09ohOgfj8dwenrq7larQibmeQ6//OUv4auvvnLXVWCwOZS+XqoLEeqX8XgMv/zlL10bZ2dncHZ2Bo8ePUp8k+qhbWJClCWjqrCPYuu02Ilcvkgy7rIdsW3bEqjL8jx3i2q7u7tOl4/HY5hOp26DI9LXbDZdX81mM3j+/Dm022148803odfrbY3+a1wEjstisQCATTtI4q+rxP9XAWXYPvR5X5+WaX/HQIpF0MUBzhNly6R2uw2NRgM++ugjePDgAfz+7/8+NJtNuHXr1kaa4kaj4Ta5SdBsdUne+0DlIF8c8oHbd9brPbY95mUENC+D5pdZHlno36au575d0brKop1uBMWND9rVQ1hegi++qpWVFlso5vM5DAYDWC6X0Gg0Nq78obDYq1wmafWEFqgs4xhaBAstfGjf4We8Z7PdbkOr1boQH8H/F4uFi+v74vCXAdqPIbmN8cRYlCF3tbUM+ncM74faCdHAv7fWzWM9FEXlCd1wSbOIoMygfIjXNmCsBjPqDAaDDRro5snQaeCi0GyYGB3oi3FfRWzD/37ZbYgYqBarFJDUdq/gb3jRMi2DJ57W6zXcv3/fnL4C6/ApyZDiCUEL3EvlAODCTiwK7V248tboC9GtKWULfEYDL6MpXO604GJbUWXsC3yH3kGqg39vXaiLQYrRWKT92AUAy7xIXTTgPCy1hfNEW0TjdfhOsoTuz5CekxSvj7euElIXllLq0cryv3Ex1vesZuzF0kTrwbH3ndiQ6pdklcS3mtNk1QtlAPkV7yev1WownU7VlEqIkMPoK1MWfE6pVrYqaLqX06Dpt/V67U7IcqSeQonh2ZhFHAtfch723aGL30+nUzg+Pr7wewgxZbIsg6+++gp+/vOfu+8w9SK3Hemz3Am0ZsegwYevv/7abRZ89OjRlVyIpe/jsym53QSg23lW29Iiw6uCz4aR3tVShw9lBFm0Oi8Lq9UK6vU6tNtt54th4ALlGtJIdepisYDBYOAWU3haz6uKlDGMGaMYeVzW2NN3oQtY9LQEpy81GEKzn/A5RvVCKAAcQqxNG1unzxZKDebE6mItqFq0r6z+peSTpdjm1vduNBrQaDTgm2++gQcPHsD3v/99ODg4gP39fRgMBs5nq9VqwSwYRWIb/Lkse3Gdh2T3a89oKCMwGtO3vviQte2iAUxtnlc1l6rys6x2MoC/32Pq0xAr3+gYSPavT+aFIMnOovYQbhxCO0PK7MP/lxDD//w9uIzL89xtBKGZDnl2LYnP+bhz/SjpYS5vtfeR4hTaO/Py0ve8D3i7Eg14DzimcUcfmJbP8xcblfnBCuwjX7w2ZZytkHghJKulMjF8XySuovWVNl6+WAV/1tq+1p6vfKgeitT+1eQR5TfKhzinUc/j+tJoNNq4XkSyabV2i0AbJy6T+Gf6vj4UkfVVYhtxRqve+zZAXIzlTCwFI1Gh+XYgYlod3I1EF1awDUxlsVgsok5fxUJjXDrYy+XyQhpcrnglGkJK1QrLpCtTgFgcWvq7VkYzTCRjKaSgNWFucaw4yhRWPH02pSVGyKc6JkUcrCICTOM9nM8oB6ghZzkRy0H7d71eb9w3bUUKj7zsKOp4a4gNMNJdrz4HxAdMDdhsNje+R/7SxtbXB7HvsQ1gwCgETNVIrwbQ6iuTB8rmJ0v/x7RpTWlE27cs3mFaRC09Oq+T1mdxnjjdPlpSFoAlJ06TiRisfPLkiZO3NKASupcuBsvlEobDIdRqNbh58ya8/vrrMBgM4MGDB+7uWut1CBZgXXj9Bc/WQkHvnynjzthUZNn5Dt9arQanp6cbGSFSA2Voa1PbO0UeUvkSus82FdhGlmUueJZlm1dWVIGq9CeFlG1CoqOqYMFVguZ3YOCP2o8+xIybJBc1nYkLnnhaH2mhm4dpwIcuOF2m/LAAZeBsNoN+vw/vvvuue9fHjx/D48ePodlsujtBEVXOEdzQiyerUF7hKab1eg3tdlvVrSG6Yngk5R0l/4gGsWl8Ad/Vyid44izlRE+ZwDmBaUB5kNRqJ2jyzZdpy2fz03p4nGaxWMCDBw82TudKfLRarYL2ONJQVWrokNzfho6SwBcQXjZ9VEbMZNvvrMW/fGVj+cX3O9WVKe+Oc7nRaFwIoBfx0S20UDkyn89dtg+a6QMPCNCU7xIsi17UdvEdKNDqlRBrn/MFn1A93K/Dz+hv4IZY6TqHVLpj9LQ1VpAyP7V4/jYQ4iEL+PoJvQMY68Lf+cJ6bFsWWiionRMbo7MA+RFjhHTeop/f6XRgtVq5dRzc5I3/qooPS+/Ms5MA2PrgqsUqLaDvX5Wt9DLaH7HYWEmVHAvNcabphinoJMUJQR07FBYI6uRalF4R+IwBrJ/Sb1VCvK+0RcWyJppFcaUE3SQFF6ojZmxijEReL+0/67j46LWAjisPSFqNx1gjU3suxLtF4WtPaoMujvG0NHyOS/XzuukzKDd87acYukXmn7TIUUbfF62nTAUVEwwJQXovSafQv3H3qtQ2n/+0/jIdbsl4rcIIoMYhNbBp+/P5HObzuZdGhFVml+H0hp6vwqDUbBMteMF/s9CEQVJrsDTksGsyNURXjMFO54LPGZbawCA4ylu+yJAKrBvbxQ15WZa54Mje3h48efLEOZVFUitq8oTugKd9T+8+QseuyH1IGqw2EdKMp4343ZQUKXovBj49LfGzVV5oDjrWw+ssEhC0BNLKRMgOj/m+LLvCl1oO2+Hzk+sgn+4v2sc+Pg5lTKLlfeX4b7y873dJRmhZdmJS4Fl0hibPioLbWQcHBy7AdXp6emETMtdTVcwl2t90QRuDpuhfcFlQVkygDGg+Gw9MxfYf3ezK25LaqwKUF6hdxHlksVi4qzUsG9mK6A2JRl4OF7LRvsCsJ1ymxehoSnvKfKjKNk5BjG9TtT7ltKTKmpS4kCVmYI3llIHUvtb8IK2elPe28go/uMChzYMy+pfaNNPpFHZ2dqDdbju6fDFGS73a96H4lqUuqRztK58/YOFrXpbaAwi0ZWh8QoMvbhKyvXg9ZYw9lx/SHLDqEwm++qz1x/B9iP6Q7au1VaVP5OsHyf6OHXfOm2hrZNmLjbv84AbnU41GyTcI0Rfy0SW5XBa/x8jlMqH5UVwG0MMHRf05X8zvZYT0ruY7Y6lTwDsDUxQvl0t3ByydKLxsq9XacMIwf32soEgxvhBSoIIHIS0K1hI40JSahanKCIry+riw0ARGCL4AhNSuj04fYicf3/2F73jVJ7GlH4oK3tT5RU8FaHOb0hn6HgNcmjGJbUoONP0d69XKxDj9MYG9ssZq24pUA09jCACmwIoPkuEova90V+disYDRaHSh/VgH1cc3EqoeD4mefr8PR0dH0Gw2YTQaXdiIQO/3kwzIbS44xPJ0mU4WD4jH0EGB8gv7rgyekkCdf3QStDp99Go0cPnnAw2epjiMFmDgM89zmEwm5gUK1APafU4SpAC9ZSwHgwE8efIEAM7vvQMA9W7mqpHnuTux0+v1ou6543IxdhOhFchjMaf+NH4uokcsjjMF9oclA8G2QIMG9Lsy6wc416fdbhf29vZgMBjAycnJRvt5nm+k9Hr27Bn87Gc/g8PDQ9jb23N3Ri+XywsBj2vIQN+R2yo+u4f3qfQsrUM6jVC2TzObzWA+n7tU17gZzIIqeCTPczg9PXX9G1t/DE2pdqYGLpOl8QrZBhLoM5LvTu9TKxMa/cvlEv7u7/4Out0uHB0dwenpabAuLdBM51EMb0vzT4K0cRjAZgPQAwjc1qhCjl8FeXsZtGwjDnKVEOIja3ys7D7h2YSkfpdsPFqOygz0WTCt6GQyMdMSK8exrdFoBMPhEPb29mB/fx9Go9GGTsMTodROxL/pok6obas/yWPMWhnaZ74FH8vikm9BaDweb1yJhDG3o6Mjd7rYirLtkdB7aWWrlg2XLXv4opcEyodowxeBxU6x9AuNh0jg2cP4/MQYNN201263L/h5mBFDs7ukd0tZg6J1aN9L88/3zFWH711Dn1Pq/TZCelf1ZKzEQBpT0cUQGrTB3zudDgCcn/TBgCq2RRd5NUWvCR8ucEJltOd8EzCkCDQa6N+872KZMyaoRRVtqF7fIpYluGspZ4U2jpS+UL9pAsCa/tpq9PLPRcuGxjekwKxtSrxq4RNaJ/8nlbXMJckopfVKxrxWdxk8WEXgwgeN32KNgRS6NZnG5b9kxGmGcJEx4AFHNLYwGMznPzWcfLTFBnT4s1UZB5QudBrx3ha6IEWdu9Q+tsrpGNkqyZkifWV5NhT8sjpsmjzxycWUuYzt+OR2Wc5ryB6Q/sb0vPQqCW2BMwTp3VFuoKM0mUzcyRlMRU5PDPE57XNgQoEKrR9msxnMZjOYTqfuTs0Y+6IqoLNJeVGzhSmK8I7lnS12Z5mOqwbf3JZkQpkBoW2gLBlAxxQ3yEoZFwA2FyWWyyWMRiNot9vQbrc3gpBXqS998jLG3q4KXIbF6NTYdgAuvnNsfdS263Q6cHBwAKPRyGWy8tkdEg1F5CfGDvBaBgqe8jdmfKuyS2h/+/xnn80iyXnu/6A9eNlzMWR74cbJfr/v+EeimevxkJ6jcyrWFuG2c6htX53Wvo+NI1hjNSH6eL1FbJqY97XMSeu8TRnj2HrKtPW4nxajm0LxnDJtUaut56O/bHmPNq+1vtQ4B2b9oXfHStfOhXgvhm+KzGefbuc0cJq1MhqN6KfR/uDxeJqtLuTL0vas/jinPaZ/Nf8kNFYSr6fIhVS7iJexyEfJ/w3B1+epctAq50II9Z2m92lsAcvyv3lMhz4rzREfQj6w1B63e8rWp1cRlviE1JeXFXO5ivBuxbfspsDAOb9jxjVQr8MPf/hD2NnZgc8++6zQvToWh8Q3WQA2T8Tmee7S5/mcDQmaYtKEfOg7Tmvqidiiga6ykfIe0h20VUPrN7rjRhL8ljp42ZBBY/3e0lZZ5aQUntgfuAsphUY+jzD4gIFyn7ywBCm2KfC/DcollX5tHPj8wT6S2sHFVwzISeMbctgsMjn0fdXI89y9K8ALeTefz12WCADYWLCKCcbFGH9FENtGWXTxdHk+w16jAx1Nn34KOaAAcX3A02RZwB0U6aRiLH/v7OzAnTt3AADg/v37G+l8+X3NRdDpdODtt9+G4XAIH3/8Mdy6dQveeecd6PV68Prrr8Pnn3+u0i3ZUdY5wAOvOzs7sFgs4Msvv3wpnBzcnc6ze0j3HlttVVq2SB+EZHHqHOcp6qX2tDZTsC0Z6QOOaZFd6y+7vWGFZbx4ejI8gXzZd25uCzH8TFNTAwB8//vfhx/96Efw//7f/4Mvv/yyKhI3QHUr2j6np6el3ldeFZDP6D1uReQJXzCs1WqwXC5dKn/UYRTaHbTS4mOZCNlEiPV67U6hYfpLq/1DN1BrgVkN+BxucMR/eN8cZnGLsad5ENjXbp7nF074SGVpELdMXVTVuHNsM3AcYzdvUyfyOIbVPsVnOKxzK2VRJdSHKeMpte+z0/P8PGtOrVaDbre7kU0x5UR8CDs7O9BsNmE4HMLZ2ZlLZYq/SXOa28laH0tZFHnZWN9UogXliZUvQv4glkGbu16vbyzCUr3TbDZdfJ/f1RnivRjepHyvHQCgsjw21iO1tQ27kNJZFl/zfuVpewEuZgSS4nCxbfIYBMCLseI0+XzUGL7QNpPSujCTDwA4uYL2LQLbwn6QMmHgWMWOlyRXJb9V6wde7ruC79K7WnBhMbZowCbLMuh2u9BqtQDghVPBBSgKDl9wR1OKtCz9TmNuyagPPZsS5JIQ049ckBUR4r4dIKExLtpuClL4LmTMlu0kWGiMaZvSL42RpNBCyi7GGYgtJykYGjjxGUf0WWpYauMlBRR8/YXPX6aAT1lkqbodCzT+sfC5z+iyLmJxg67ovKWyL4ammDJFoBmX8/kcJpNJ1OJfTHBKoyUFRewDSY/HLjBZYdVxoTZDDqevjKUey3OxwShJfmM9uOhPU/6UkdoQ22w2m1Cr1WA8HkO9Xoc333wT9vf3XfCaborD7/g7xOpQbSxxwUva2LONQKLUpmT/0vSHPhvQp5e1sQ/ZJTTQgeUpv2DaNnpKrQzZodURotcyH7Xnyw5+a3TE9s82bRfkvVqtBvP5HJ48eeIWwyaTCdTr9eh7UGPkE+U3DCZhUBbpS+mPouO6Xq+hXq9Dr9fbyM6xWq0uzMuyeYjbLanvH3oO5/KzZ8+g0+m4FOk03VtKwK4oUE7H+quXDUtf4bhwOR6y9XiZbcmIkB7Bz/g3zhUKnDfawrqmuzW/nvcH+p6+zFfWOJEG+nxM38eWl+xiySbCustAkfqtZcvyya11SHGTVL+cy53QXJXGrGqZxedjrK/nk7WhfuP9Is0xX39g/AhtDW6D+uiOBdrb6/Xa+R5ajI1/luiXeCzWX5H+9rVrnXMWWnwxEoAX9heOS9mLwfzvFNkWsu19cYZtzsvLgNY3/DefvpXq4WPF5wGWD21GovVZfDyLrQTwIrOe5LtIdMfaVz7+4fyWwmOpz1TlK6RAqzck1630bGsOW1BmH4onY32MbMG9e/fg3r17AHC+A+HTTz+9cD+AZLxT0MnFISkGn9BASLvQqZAu635E33chaEEun3DV/vYZ96F3TWF4jW5LwK0MIYRBQ/p7kd1PkjOciljhHBpbCVZjMNTfll3paEhr9Glt1Gq1jVO0tB4pkKo5ORpNIVSlgKy47PZ94PPFYkjR/+kzOGa+98Wyy+XSu6BfVn9dhdMWvncZj8fw9OlTd4euBaHg1VWBb1x9fRIKzGnlNceeIuaezSoCLZqdJele66lBTQdjWdwZ/vjx40L2jgQafO33+7BareDzzz+HH/7wh/Dv/t2/U98B74XxBXck8L6i74l6Jc/zjdPmtN3LOjGX5/mFXbuYujn2XmQOa1oxjS7JZsyyDBqNhlu453aWFVU4UUiftrGTnybehu7lPEi/T7V1y0Se5zCfz939baenp/C3f/u3rm9arRa02+0LKfOLtkmBYwYALgvKs2fP3HiuVit3srVof0mnFjXbZbVaQb/fh3fffReOj4/h4cOHLsU68j3KKh6wlKAFe7VyVevtLMvc2P7iF7+Au3fvwg9/+MML5fgGucsCjgniqtrOmrylfCLN/Rh/gOoGOj6XYevhu1Idy+mgix/8TkbNZwi1R0/XNhoNaDabMJ1OL5wa9j1LdYK134roUmuAN/RdLJCGon7PVZ1zHNJcKlNvabEI2s90c7ovvXpZMSWt3iLyIDUWZ1nk4ajVatDpdFxmBGmDYio4PRJ4f/loDr2Tbx7HyPkYmaQ9w/0pqU56opLKSFonnojFjEmaDuP0aO9UlizhdPArbyy+ndYvZcfoyn73quu1gvs3q9Vq4ypKWo7SKK3BWHjeN4dxzNfrNYxGI7fJQ+sbuumQZ+Gg9Vrap79RO57yIeWpIrK5zBhUVXyTWu/LYmdQlEmzN02x1jAy8M7ODuzu7rrfm80mtNtt2N3d3RCOKDipU6UFQrXUbFJZWi4ErQx+HwqS8EkamhSxk4RPYksdPmFsERxS+7RMjPBJQazSs5YtwznVAr2UXyT4+ECjP6UPY/gtJRBJ+1AL0vL6Y/uclsfdTNiWFlwN9T9/B8t320SKY74NcN6WxlyC5PzGBjikgKgvmBEKdPDfU53VKo2Ver3u+Hw2m8Hz58/dhiXJsbXSfBmwOKg4zlpQnIM6iBKf0HL0f61O2n5It2H5VqvldlpadJVkkIfKWiHJYis92m9lB4ekdvHzzs4OnJ2dwfPnz2EwGMB4PPYGUEOwBkdxrknfS5+3CTyNFkoNjU6vJbiE5fGdpIWiVDli6SeNxlSbzBcwiaWrauezLPlsdbh5wK3RaECWZe40OqYIbTabGz4bh8W23cYcseqGorDKDauMqRIhfRVj29CUurTusumtEpcdgERIdmVR/zQmmB0KTFp1RWz9WpsA55sqnz9/DtPp9IL9ZqnTapv5nqf0aHTi5zJ4NdW3oM9b+SaGBzRZKn1v+U6joWxo9Vt0YhGaYvon9AyCLnZpNpjm01h4VesDzSfapv/o8+vQFuHltPcpg3Y8wCMdIPCNTYze1cDHI8Z3keS4Rb7F8DGWp3RRX9naBv6ekm66DN60yN/Y+IOljph2uM1Oy8bQb+E5PqaaPPC9qyZ36TP0f/S16VoP9zmk+IivX3ygtix/J8neleqnf+Nmck5jrD0llavSB30Zcd0fmzAtxkodtlqtoFarwcHBgWP2/f19ODw8FOvI89x7EhbB09fRZywnYun3oUmdGhzyBYVT6vUFb60GN33WsoCt0YBtWgJ4VgHuQ5kGYpkT27cpoMygUcj40Z6x8ltZQSWkjc9B63ykv0upyBaLxcYuIuvd0qGghJWu7zo4fxfpL6tzQmU9/Y3riVj5RXfPX0Vln2Xnd7HgvRaTyeRC5giE5FgXbfsy5gJ10mJPJHInmsPyPrgT0noaFO8emc/nMBqNTDxt4bXU8cR+iwmGWdsI9W9ZePToEfzyl78EAHCbEKyQgnNWudBsNi+U1dIWl4nQXGu1Wqq9TMGDevxuTK1tmgnGx3dF5b1WL7ZdJm+lOsLfRtAARJZl7o7EwWDgTr1m2flJyNlsVjm/X6NcIA9bT3mEUMV8/C7D5wdZYY1NoO8VoqUIHUVt5eVyCQ8ePNhYaKF2Vwwt+HwROR5jKyFi2kxNpY5Ioc+Kovyg4bsUQKW6NbS44styo12PIcVT6O+0He15To+GlHFL5c+Qbc5T/vsQSndqna/WTC6xi5ixiO1T6zxOWdyVaKPxQi0OZ6UldjF223LFsj7wMsg6zacDkMcKx5fGE2LelfqU9NAdZgVYLBYbMTzcJErbtyBUTttYge+1XC7NmaaoHNdkE4/Ra3LbR7f0e+w8oW2/DPxZFS4rllkVNhZjNYOZ7xzIsgz29vZcOsVmswm7u7vQarUgyzJ4++234fbt2/CXf/mX8M0338BkMlEXYrX7I5fL5QV6LAusnF4NZQxikTR/mnCiDGah37roRPtEEyg+w0+qj7ZtUfIp4HRLtPJgMk9HlIIqJ7kUWI5BTJCdP2dZuAUAMeArjXXM2FLFiIobZUrsfA5tDrheiL2IkGywQFrUiHUMOejiaagea2pXTVFLc6Bqg4bLaq09Ksc4r0vBOQTfzRczT1LeI6Yu/k5lLfpIDgXtW032+Rwv7ENfmmh61yKWj9E1Gl9S3rDYAL739OlxPI2KTrYvlU8RzOdzJ9cHgwH8+te/hmfPnkGe53B2dgbj8RjW6/UFRw3fCcfDt7FCsw189xtRZ60IQg4VD9aFsF6vYTgcbiwSp9hNXH9T2lKcPjxNiRtntF3G9DnNNvX1CZ1DZaSP5rJQShdcFfD9LIHUKukom8d9ddIx1eaGNkckXuB9xK8xoL/xuS/RjP80udloNGC9XsN8Podmswl37tyBw8NDuHPnDnzyySfw9ddfO/6nz+KGYcud1FXboVa/AuVys9mE+XwO33zzDYxGI9jb24PFYuE2ilUxD32gp5ckWRHbfyH+leRUjG9lLVvGuNNA4GUH4LSYg8UWtPiTvnfEOjAOhSm3F4vFRn1UBsf0F22b3peo0cufDdXJ34P+bqWziB+XMp+K+HapsPJU0fpD0HR5SL/js1qd0mcfraE5wdvW6pH8ZF99IcS+CwLTidbrdWi1WrC7uwuHh4fODnz27JnL7EHr9vnBPlje11KXVI+FV2P0hS8OYmmD2+AhGjV7HMB/7Qk/Act5NGV++HxqXx9q9l1Z8QZOR6iMVk/ZvFI2tPHTfCbsc8wihlcOUfAYDbehfXNKAvVNpZhPzDtwG7BWqzn7AqFt5JDGVJq7Ph6itso14vFt6ruNxViLkYkTYHd319392Gg0YH9/3zHYq6++Cu+++y782Z/9GTx8+NBbd5ZlF+4GyvPzXQ0pnRwbBLPWxyEpC197FkfFWhd9hgoAn6DXgheaI+rrP9quRr/2HG8n9Ju1Tyk9ZQQSYgynIs/zsrF8GxqjGLp8fRh7Mo3zIio6Wl+tVtu4bD1lvoYMwG+LoI5FWe/tc0JRloQ28YS+981XKw9zGXgVgYEdrV/oBgUpaKjVqZWrsi/4nNPGRNJPGnxyhH7mDqG2m5G3HWofZZR0kpLSgPeg0fr5yfLYgAY6KTHl6d8AF+/H5HQDwIZdhbaXbw5bIPHdfD53dY5GI/j888/dhrzhcAgnJyfQarXEFMJIW8hx0p7z/V70XaX6yqgjz3MYj8cuAFU0mBBjm/rK8FRP/J4pXz0+Wafp6ZjTmxoNqcFEiZ6YuqTnuNONn7kjfxVhDY7g776+ssp+nw/KfRVeVpr3kt8jBW/q9boLGjUaDbh586azV589e+YWY3nAhfqqEo3aO0l8UTa0ulE2YCrrR48euTv7lsul+57qOUtbMTRI4Ite9Pmy+knzdav2G1LlSdl1WNvQENKtsXEO7Tvf87jxIcvON85xHpX0k09ucNql99DiJKl8Itk3RXV+Cnw6VLK/NBottJc5x8qK71nbouCyg//G9YCvrhBiY0Gh+n02Nf5v7VvNz/DVQX2V6XQKrVYLWq0WdDod2N/fd3UMBoMNW7hq/96nv0Lfx8g8bhNIcgZB7xqnv/G5psmlkA+lPUtBF2OlrHhZtnnyW5IRvC0OPl/ws9VP43NO4r+QXJLGM5YnLHTS+iU6sJxGkw8hX4aWC70bHw/NL8PyjUYDlsuleOiOzlvpsIHPZqe/a3Y15xvfu2qHOejzNCaNNGuyTvrbN6dDZbW6tfpifLSriDJs2suwm6qAN00xCkScYFmWwf7+PjSbzY2FlTt37sC/+Tf/xk20n//85/Bnf/Zn8MUXX2zUJ01UHoDEdKW8Y61HzUNIGTAtkJIKq2ANTcgqnTKERSj5UIbz6auzipRb2xBwKUI4pV5r/5fFS5agG03vhKcKaDCWCujQKacyab9slP0e1v4pK7gQG1RBWWqZ577fkKcsuEoKe2dn58JiVIwT7HP6L/M9y5L51Oj2LTTSsinyTuqr9XrtUkc3m01YrVYwm80u9Ld2qt8aPLY6xZxm33f8d0wdFMpAUBQ+fqSo1+tuwdu3YBFqBz9bnkUeuqx5wZ3cWq3meKxerwfTqOE4cv1I/7dCc+aKoihdIZThdMXyGW+bBnsAimXHKQJJBi0WC6dTJpOJW2TD0/BSWi/MqoApjQFeLDBeY7ugPEbloqSrXiab9yrZXEVB536M3cnrQFB/CGV8bIwBeUTaKFN13/PAOdKB91Snwmo/AQB0u11otVqwWq1gtVrB2dkZrNdrdaOX1h6ATcdI121YfSyuU6y2i6UNK6ztatBOJH2b5rkFfEz5xjU8rIJlitrfvgUUbtOVNRb0fQBs9g4+o5XFDaAAL2wSzJ64u7sLzWZzw0cA2I4ci5mL9LlYcBkt1SUtOGkyUZMtGs0SpDiqxFN0UU6jJcYno/Xw+RMDbDP0bIxeqRLWOE9Rvtf4NcRrAJtp+KVT0Ag85V6r1aDdbrvMKpPJxK3noL5AHgqNM48D+eI+/PoNSiu/SoBuLqB1+GKgSC/66D4bjc7tbfNZmXL/GtvHBWtVmpB0MrXbbWi32243Yr1eh4ODA3j77bcdk/7lX/4l/N3f/R3UajWo1+tu57DFcZFOScU4o75AUwyjUsb2KR1rXZbvtDJSO9wpDCE16FmkTa0+qQ5en3WBRgpghGgILfJw+sowwKTnrH3JnesiQj7UZsq4UllhAVWUXC6E7ju28oXUZll9mIpt0KAZCJa2YuUjfYYbH766pDnG528V8zr03TbHA4FpUQA20zVbIRl9KYE8S7mY3/jiBS0XS68UbMDvJR1SdBwxuIiLG81mE5bLJaxWq41TGDGOp9SGhtDcsdSBv2s6m6aDiqFNg9b31AHDHafo2IT6rkyZRduqUvZbg7LoFCKPoaOnBVTwGWsAOEUear/5xqkI/6SORxG+jW2T9iOdT0UC0Sm2OAWnndpR1OfiG90k2YHP0CskMIAitWWBVVekykBr+1IwUWtD0iWW4F5Z8AXJNB6LsSVDZfC0YZlZAyh4/2t8UIZs5n2Yokc0W9YaZAv5jD5+9Mm3IvMiFE+wgstEDnr6hcqRWNotdiFeddBoNNxddZK+8vmNXB7jGMfaJviMb5763kWLM/meCdGk1S+9n2anS/PWqkexnar97RjfGn+XysXqc98cposDdFGgDH9WareqPua+uJUHfHqLy1dMNY6LOdwmDi0Ca/I01SYM6d7YNkJt8/Z97cT64lb45g/6xBZo42D1/1P6VLMtfO1QpI5hEfvfEgcJ1R+yDflvfJ5oeo7TKOkGKgfwmgD0P/A739UdnC9CSBlXTrPVP+ffoS+gzRGfHkiBr+7Qc0X9yiIyJBVFbJnYdqq2RVIhLsbytAMAAPv7+9DtdqHRaEC9Xocf/OAHcPPmTfid3/kd6Pf7ovB+7733oNfrwc9+9jMYjUabDf//7wrAXVDYFr0nAKCak49WlD1oVsM1pR6rgUfrCNHD+15zQPjOOb4bxYpYI1UymCxGW9k0VQHN4Y1VJjTopoHfp0Yd6DKDI3ihupQGNDRukhPgg0+4X5YwDtFQlqIog2etdGj3P8RA2q0mGaqXNRdTIdGrpdRF+Pq9Cr611ikFrUJ10PtAkbfLOEmGC3mz2WyDLzT9BPAi/SnXaRIt7XbbLcYuFgsYjUYXnL3ZbObKAshZP7T6LeD8rukDqWwIOzs78Morr0CWZe4aiUajUer8ms/nkGXnm/dmsxl89NFHjk4L71O5EpO6FuCFHJFSIlUNbg/FnIbg/RNzskeqB+nx0crtfa2++Xx+IdCI7RTtX+qU893xsQ57qB3fPNoWrMHMEKo+6V42+HuH5icP/GBABH/jc43amXmeu40zWJbej815DReuAQBmsxk8evQIbty4Abdu3drwT69if1v4R7M3cZPMkydP4MmTJxfm+GUAZb/VVrDqFOm3kE7yoQz/COvgPhcuRKBslhYwrPVfBUiBvlj5h+XwOo/xeAzT6RQODg6g2+2q5fFkS1GE7Ey8did2bLi+tgRFQ5uWJbqRz1PkhS9IbV0wqBKaby3N7SriepbYXr1ed/KWy7iyaUTZbbWdNRmY5+dZMnDO4cYtet0Jf5betew7IYv0jcdjx0v9fv9C+zwm5eMzHuOU3qdMxI6Tj36uA6h+4mXK0s20HfodR0xMVZMVWt28HN2wCwAX1iN8cgzvLKVXHnB6LO96VfQmhTXu4kPsJktfX/h89CzL4NatWy47wHK5hIcPH5oz7vhO4mr6RrItOCwbCjhv+VIba+1rNJTNVz65fdk8zG2DsugpUo/l2Rhay3wvMdojMRHN6d9qteDWrVtw+/ZtePXVV92Ew3tnsiyDXq8H/X4fdnd33cRC4O4JTM+WkppHgtYpZSrgqmgs8ryPJosS9QV9eRnL+29LCPgcvCLYVsAl1kjAMj6DThIOsUohxmnT6ve1Jzmh0vzXyhWl57IU1VVQkBZYAwK0LDWSQ0ajZlhZZVtM8EZzGGPrKhshw03qQ9xxSAN0vmcuG9RY56lqAOxzmQd9JLlQBd0YfKDpxjDAP51OzamTQ6Dv5ZOHfH5Z52ie587WwiAJfuYbIIr2p8S3mOIZwa+5oGWlvy26ylKuTFjtKqk/+BjjBjZf0JTOAx+k+WWxK0LgfOJziCV+LopYW0SSjZIuSOmPmHfife+TfbG08PnL66LBXkv9MXI5FSl1h+wmy1jyACevn/LHer2G+XzuguZa4NNCl9U389WVOh7W8QYAd4qh0WiId7da2rLIJt4u/VvyO61tp6IMf7yK8cF6JZ0oteeTw1q7ReeiNdCm+X78NyuobMOMJaEgq08vlIEY28NHA+1Ln48f0z5/NiRnfH6YVaaVHYSN4ZdYuzuWBkud3FZL9T236beGxorapTG6LdQmbmBAewb9FbzH3WLDlDGPY8fX8p30rKUtX2wzRobF9ovvHWJjOyntSv6tVG9INnJeDulNSUfxMfD5cikI8bSl/qL9HfotpK85jbQcxk5wk1Kz2QSAi4vrse+vPWPtKyq/rPxsmfeab8H5WOJrn86XaOJ18I2tZdrEZdi3RXWt5fkYu9ZiH1tQph7eWIz1OUGvv/46vPXWWwBwfhrkD/7gD2B3d3dj9/4XX3wBv/jFL6DRaMC/+Bf/wl3qTNFsNmF/f/+CwBuNRhd231sQG6QpA9sOMNHympIvyzCy0IaMTAOqvsBQFaDpIikNGixOAf29ynRdVQLrp2NjVWpVpaaTgGn0aACMInSHHqLq/rQ6KN8GxMhSzQDgdWjzyNpvksFUpM8va6xCPBQKwjQaDTg6OoLJZALHx8eV0ekD1dkWWVHW3ECHnJ60leiSvgvd6Rq6B73f78PNmzc30njmeQ6ffvrphWwfKeB2UOwcpOWlRQMMVt67dw8ODw/dad8HDx7AfD6H9Xpd2i5rtAGy7PxEbJ7nMJlMooJjNNDKs6RI7eH/3PC39iOOK9VFljZpWxLwBIQE1Mfr9Rpms5lL8eSzOfCkttYufefLlI0+Z53SWMTWwOe00x/SggF+XwQ0WIg78NvtNmRZFn16u0xIfNNsNqHb7cLp6SkMh8MLdzTz57dpu8fiqtlX0sYJBNcp1P9NfY+iAZZt2agxwcmYwElRWN4fy1h94BhYA0mxfjraABLt+HuqLxf7nJRxRNOVRcc8yzJoNBpO5mKGFEt2D95XFlpQNtKYQ5ZlG4tFZSCmnpi+5GVjroqQ6rHEm/D3y8xsVyUs8gRtBB8wzqGdHk0JYhfRF5JPUa/XYbFYwHw+d5kMcWMwt5slvow5pbtarWC5XMLBwQHs7e3BcDiE+XzuYsPSpt6rCMtCW1XtpfCR1S631EPlSpExwjmh1cHtfM3PR0gZ2Ky4CrzG55aPx7SYnO89pLmb5xfveKV9jX2J8mAymUCj0YBms+kyV+zt7UG9XodXX30VZrMZPH782MV/ffGX2DUNqT9CelKz9yS+8fV3zFoEvrN1UTrUlvb7VeDZbaLMPoytrwxsLMZKBgGecO33+9BqtQDg3NFvtVrQarWgVqvBdDqF58+fw/HxMUwmE8iyDFqtFpydncFoNLqwIMuPt+PORkugS5poUlk+ySwCnddThtMgtUd/1wQgfT5FWFlok9q0lCnK8KF2Y+uz1GMRYCEhFjMxq5rEMcF6+j7aWFqCECEDz2L80fa4kcVp4HOw6Hj45ngIVsWXUpck7H0KwCcfy+BZ+ow2RzWZHDKENVidet6e729fvSE9USZ8dMbKKzyhYzX6tmlI+N7LwhdFZbe1vM+ewOAaprKksgh3d+7s7MB0OnWLmEUR0u8h+rV+o84DLkbv7Oy4E74og/EOXFq+SCBHohX5MDYoV9S2ia3LF6CtYh7R/vHx0lUMOmmyRZOlMXraB85TIVicUloXBvpwBzedD/z01WWOCbaNm1Mw+DWfz92p99AmkzLtGiu931bwwFeqrckhzX2LzrDYR2XBN8e4D1JVO9ZnQ7DWzYPAVT3rG2vJhortmxg7KaaeWDpifB7aHs41Hvjnc1CrU9P3tJ0YncTb9tkRWDd9hxBSYiWx9GM/ajxH+77qOW3VUSE6th1URdCFQ2kuV01XkfHh2QyxPvQbirYhxZ0AzrMrjsdjF1tGvxc3XywWC2fbXBXbOGbuSrIpJnYSq8tCctAiJ332jCbj6LNcNvto0/jCAi1uVWSOWekIxZVCPohUl+RPpeiqWL9Joo3LfS1WgDEH3EidZRns7+87X4Uu7FY5jyUbOKTvLbFGX1sS7xbxwTS6YuKdFsT4gldF3oZieViuCLZtM2wsxtKd94i33noLfud3fkcMptVqNej1evDkyRP4y7/8ywtK+le/+hU8evTIG2yaTqduATfm5cvqqNBkiQ0cp7SDiLlPL/XuAN7PVkOClqOnodFA4wHXmH4qOmlQAaRim5NuW45B6G5KXlZDkXv3pLZx84WksDSHRavb2o+X4YhZUDZdZStKPu9j6I1ZrNKckW0p/stw1vkdplL79HS4lAkA9ZM01y+L532Gos8p0PQs6pXQSfmQPAjpySzLYG9vb2PBktKMePTokbtbrwwnwrdo4gvsScBTrlQ/Yyo/re1utwt5nou2X1nY2dlxd+sCXFzYqgrWu162gSLtSvM+9nk+58qwuaQAS2r9oXvqeQA+tR1OI61rtVpBvV6Hvb29jV30q9UKzs7OYDqdqqfztwUMaKzXa7h58yZ0Oh24ceMGrNfrDV/LmllkG7gqQdNvA3z8Hzs3qrZ7YgJZLwv4AgvydsxdoSHfjI5LjH8v3bedar9rKFpHql7Ati39wReSYmwobvfhnZdWZNmLu2MtZWPpk2gNgfehRkee5+7ubMykINXFeV+7v7bKWFksqpR12oICLjygPU5T3kunrVPbRmiLCbEL/UivxMOYWYeWpW1Q3gB44ZOE6Ma5fXx8DCcnJ/Dqq686n6FWq8H+/j5MJhOYTqfuuyI+WJn84KtLsotD9rzme9K/tfhvbNxEit36aNDqkGihdGMZTff5Fpws8oTXFxP/tCBVplEZX4YNXKQeyT70LUD62uGpw6m/MZ1OnYyo1WrwxhtvwO7uLpydnW20Qa9/kvSIlRYrKB9S/U77gX72XU/C6ePtWMaJ118kJlK2nXeV8W19P/HOWACA3d1duHPnDhwdHUGtVoPFYgF5nsObb74JBwcH0Ov1NgITNO//8fGxOxVLmb/VarkJKKUgCHVyyIGhddDJkKqsfX8XBRUyfKdJiBZah2YI+N7dJ+AkYR0qt63Jge8q9Z2vPH6m0BY2pLKhuouiyoXrIkHY0PtZ5m6o/2OViNWQ/TahLEPEWpfViKBBD998KgNVGBvbXPDV2pTmJ/19Z2dnQ2/iYlsoPVpVwYfUAC/Sy2mW6tPawMAFBsgswUwAm4yg383n8wsLmPV6HVqtluN5eqK0CvD5o+knn+7DwBrA+dUSe3t7btfq119/XckCLNfN9A5CH73W33lb+C82NRa1P4vILmkux75jWXOV1hPSk7FAPkqVl5bgDrftYoMpIdtQCgBq9WCZxWIBs9nMyYOYsYoJaKWCBtLw/Xd2dtw8L2uOF9Xnmq8ijZn0Oz5L5zmvq9PpQKPRgMFgsJE+kT6L9S0Wiw0dgvIgyzKYz+cbadyHw6GT+3QeWHhZW5Qp6pOG2o5tg+pWHkyS3rUK3qZzlgbLyr7XHOvBNiU6Ytuh5YsGbrV6L8O30Wgq8ryvLH9HGizV4hC44QwXhhA8palVduM8D8WSuD6hz1B5ked5pbaiRJfPLuTzyxc/KoOelGdDfFe2fxmip6znsux8cTZm80ZRhGItnA6+kIW2j0S3xUbD8r5rzbgdjRkVa7Waiz9b4lhW2Rurt0K2Y9n+tmbH+8pLMtJCF5UJqbqL1kNpkuqyyEOfrRM797lelp6nZax9QPvWQo+lrKVfNPp9/U3b5zRZaJf0G12Qla6Zw3jP8+fPYTabQafTgU6nAzdv3oTZbAaDwUCkl9ImvbMmV6V35r9bZRV+lniHl9f60DIePt/HQqOPJitibHlLLK0KbMOXvmyoi7G3bt2CP/zDP3R/T6dTWC6X8N5778Hrr7/urfTLL7+E3/zmNxvf1Wo16Pf7G7s2MViYwgRWxDD5tlBkR0RIOEhlrfVyw8rad+h4oGMUixjlyp2HUIq/WFjp1+ou2ygrE1qwS0JsINRSp1bmqs3Pbysk48DHr9q48FM3IePIynNVQQs2bJPvkAbfjnmeGQGdFnpSCxdnZ7OZ23ldNGBmhaXPfLykndDk5TTU63VzUEsLvmnOIjobq9UKxuPxBT3QarVgf3/fpTDF1MVlweJg8HL4WeqT1WoFk8nE/d3v9+H27duws7MDs9kMPvzwwzLIVulF3ux0Oq5/Qydi0RbkgRof8LQB3lmDgdmQ8Y5jjeVw0Tg2EwR1EJHffHzhC5L66I0BLqqUJeNw7Gj9ReqSZBbn4dQ2cJMELrbRXfs8MB+iE+cQBg00YH/z3eK0LiuK2o84F6bTKZycnCTXkwof/dj3dPwxOE3lA93Ayxea6b2QvK39/X3o9/uwXq9hPB67uyTxeQzsYqCI8jTKgnq9DuPxGH71q1/BwcEBPH78GJ4/f+4WgS2n+pFuny5Mne9VBCWwX62ytyofh2/Yws1P3D+sYiEmJoBpAbe1i9rAlkBkjM66qj4qB857KTUqzmnMttRqtS7EBjR9bJH/tH3Oc3meX1hcwmsf6DO+zRg+2yTW/6b0hsaX2ru1Wm0jk4pWbywdMfD5pVeVTyX5YJFLqH9ww2eZfmhIDlj7Em13TA08m82g0WhAq9W6sMGL6zmpbWpjY9n1eu1SmdJyOOYnJydQq9Xgtddeg52dnQ3f8SrGdMsEXzT08RXnw9R+4b5kSqZBrqNDi4YUfLGNPifJb8tc87XFY9VSXKAq+BbvLH4D92NibBUtZhCyK/Afznf0lfGQHp2ftVoNms0mzOdzWK1W8NVXX0Gr1YLf/d3fhX6/DwcHB3B8fBz0q1IR6jcAUMdeqwffPaY9qX3aNs2KEBObt6BK+fgy2Y8vE1QrbLlcwmg0cn9/73vfg6OjI9jf33cDMZ/P4cmTJzAajeCVV15xZT/55JONurrdrnOAkUmWy+WF3ckaLAuPHLEKW1NmZTA1N9jKqj9WAXMF6XPwJIROuOD48pRzsYs9Vviet9TNHTdLuZg6i9RTBFogwGcocx7l4+YL7nJFLdGAnyV+KGJIWg0RqwIpatRK7YbaKqNuTZFLhpvFQLb0F6/LZ2RyGmP62SqTfXJ2W3NPekfNIcD/LUHb9Xq9cUJLqmvb0NqV7iyk76KlrfLJJwo8seTjf41e32lu/g67u7vQbDa9tkSZd8f6rinQaJD4nPb90dER3L17F27evGleyC5qZ1UtZ4vwOw34xwSviyK1DkmGSEGPMtqxBDg0ecX1DAAEbXuNByQ5HmurpiLLzhcH6T2sAOcLQ81mc2PxH09NAvjTzWvtxHxPgX1cr9eh2+3CD3/4Q/f9dDqF6XS6sXmHyuPY4E0stqmLttGWZT5YyvnKSvyPZfm44T/rJuZvCzQbsmp5oPks/NQlf4bSqNXryxISG+Cj9jqlOWSPV8VDqTpJCgBbMZ1O3TtbYkaWoK0PyAO4QYTzSoh2y++x8SJ8xje2mk7Nsov3pVvsct88jOExX7+F7IhU39Jap1ZGmnO07Tx/sVCRZS82BGn1xdKtxW+4PJDa4XRLNhbe1wpw7nft7e1Bt9uFp0+fwmQygeVy6WyMPN/ctMfjQbjIa03NvF6v4eTkxD2Li8DYp3TzJO+PsqDFRayxNA0hn4n2mzUWg31L7W9L277fLLpMi/Xhbz77hctpiTYtG4nmh9A6tHY0SLSl2hwarb7xlO6ZpllbYnwm6d3o7753sdiqoZiIxg8A51ls7t2753zxs7MzGI/HXp3v04Wh+aT9bv2e1ynpew3a3EZ5SRe46fvH2k1V28K0Lf657La1ORJrT6TSVaVtrCG4GIsEvfnmm/Bbv/VbAPCi4zGd02q1gldeecV93+12XT1Zdn46AlNN0Pp9KbQoo1fdKdtiKKuh7hOEIUHrq1MSJryOkIPCT45wgUiDnHmeq7uaLDT7oNEfQqj/Qn1cJi+WxW9c0aX0C4U05/C7UHqdFP5M6YdYJzXkzJXRXgxfhwxbaz1SOU1mxoxBLH2xfehz3ml5Sx9o8zI2CFAEVgOSfm91DADODdzJZLI1gysEH+08XRxCcvxjkee5W4zV6vfRrN2nxlGr1dyJZF+5Ku4+jXH2pPeld5bdvHkT3n///WiHwQqrDNAclxQ5HkMr1438RGwqrDKe85llzkvlQnKXvp+VPitN1rI4NjQFdMhh9QW1aKDNEjgoA1l2HlzHU/jj8RgAztN893o9V6bf78NyuYSTkxPI8xxarZZYXyhAZwngUblG+7Ver0On04F3330XdnZ24MmTJ3B6erqR0o+ffLoq+gNR1AdAVOkjlm27hiAFO+iJUUl34fzYdgChLMTafdbgalng8w8/azKK/maxdfl3vmCURJelH6RgbBFYaCli5/ENUyEZOZvNvLRYwGNOoblPs3KgzWXhXWvANcVm9skBqqOxLOULzBCCfYl1hfSUjz6LjuN1aXXw95B+u0o6Ls8vZgQqK6OOFF+U+toa+5Ds1Z2dHWg2m7BYLGA+n0O/34e7d+/CcDiEyWTiMkXgO0npSpFWunDL6dXe7/T0FLLsfIMcvSpiW7rOJzOLyDZet2+OxMRy+LwIxQYtdXL7k9ZHZZM0D2ncmMpImrXA9868Xsu4F4kv8DakfivKdz5fQOIDyu+W8YuxBUI0+Wj0laHZqaRnMWUxrgMtFguYTqfiiWyJ9yw0aM8DgGibxcYitHLWvuR3LdM54quzKtkXq6cpLPKFtlOE/pj+jqHLWmdVuLAY2+124e2333aBB47VagXL5RIePXoEk8lkQ/n++te/hg8++AC++eabC8/E4mV1KhFWgRg74a1BfE6LJNx9ZSzgR/15mrayx1AKSJUNq+FqwbaDI2UYDNq4SUYRfUZKy4Z1lXlv1lVBqpDfJsrmPTScre8cazSlwOq8SGUuY376TidaHGjcIUx3S14mfO1To1JKC6MFlDWs12vnzPvkCg+oAYALmElyDP/G1GHT6dSdiG02m7C3t+d2h3c6Hdjb24PFYmFKu5wK6gyGdF2MDfHRRx/Bs2fP3MJ+im0WAx5AscBaljvz1AaJ1YV4YqGoPEJHFHVizAlcWofvTttYlBFAKut5BJ/r0m50KQgUC+k5HmjRAj8S2u02ZFkGT548cZtCUoIf/Lcy7U6tjcuwVaR342PK5TKVefx5GsCgp2UAwKVQrNfr0Gw2YTqdwnq9drKfB4hCung0GrlUa1TOhDbobBtlBDZQtxbdXBQbCMW+XC6X0Ol0oN/vu1Pojx8/htFoZD5JVYTeogFcKrsk2Z1av5aR6CrYfinwLTz43okG/TU/U5Nx25yruGmCLzhZAp1FeMX3DL1GylovTVGL9Vv9KgB5wUn6XQO1TfkJy9DiTVFwHrPaT9pvtN/X67Xz3+gzRQLg+JkufKbYnbQutF8pjfT3e/fuwdHRkXufhw8fOl3LNw5RaBssLLTWajVot9uu/qso/1JoSuEBiSe1uVYGDVSfafOPbjKgWRKpP+6ThVimzGxT/B3ob8jf9HocrS6LHy61p9Hh03OUJm77liHnpPnsq9OiW3x+Ftp3n332GfR6PXjjjTcubEg5PDyEbrcL9+/fh8lkIqbO5/KuiIyjhxxCY0GfkWJLqXF3LMuvYEi1YSR7qiio3VCWvI2xI75L2OD4Wq0GnU4H7t696yYDTXMFAO5OruPjY2eo4XcPHz6En//85xsNlCVcQ4gRUkWDJ7E0afX6JrGvnO9d+XOS4NAEp1RvaILzgAp9TrsHTHs3H7hxmFIHR+i5soKgqUKSByN9Y1S2sy7xjI9uyUjztX8VglllBEBT37GIE2aFZd7H1pdilFIaKF1l8UBqH6Y4LTHg8zfUFp9DKPNof6NhWzZ9oXKUJg4pAMONZU1vxNKb53kwGI5laOpQXGjji4N0XHCX93K5dM5CvV6HXq/nHMt6vQ7tdtsFVKypf32gxm6sXLAEtSidjx49gocPH7o2qg6+V3VymDvU+L3moEmBCko78ou0ycEagMP6VquVejrcUg/9LoYWSgP9OxVlykWfrNFkBP89VD/A5kYMnxMp8YFPr+FvzWYT6vW624yaYmdpgbQy7E1NP5eh/1Npwvbp3xSS76DxMJ2veFckAv+mp9VoPZgmkV6j4qN5Pp/DZDKBRqPhNuNY5qNVXlhRxbhJvkWRBQJeV4xfh3IbrwbY3d2FTqcDz58/39DFse/lo5OXL9LHIb+naP0cV8F3kpBCV6zM43I+pk1tnCx6R+IbKQbBbUo+L7T2QjwT8r2lZ/hvobro92hj0/vtpGe5vyLVR989xvejthi302gd1jqtOrZo3EaiDb/TUq4WBeW1Ij46rUuLK63Xa9jb2wOAFxmbHjx44Pwj/D6mLcvCSpZlbrGX+8A+vvTVh8+WIaOL2vuh333xCm5DVxXb0OiWYheUB6gvrs2rsmgNxeQk/8DHAxbZqckgaZ6H7E7J7wu1b0Vonmj8Y9FZPpt9vV7Ds2fPYDabwb1791wZfM9utwvtdhseP3688f5l+bISuH6y6nufvqX1WuxQzY5IRYx+1SDZL9a+L9vWtSBF9l9FbCzG/uQnP3H3JSHeffddeP/99+Hw8NBd1szxxRdfwJ//+Z/DycnJxvedTsel+8rz8/SCVsVXpEOLMqJFWPrgC/5Yng8Fpyh8F1Fjm6H7ULPs4sIpF0zcOKegARGpLgk+h2Dbk6ksI75s+BydIo6CBul0a2x7VIDzdAtlLFx8m1FGMJYidMdzbN2+MZeMUAlS4NXStlSnZNhb6tumsZC6gzfLMjg4OHDpmdDRRVlrlbOhNsoE1Tc06JCaFpa+Iy5whcBTv0inapAmbAMXWKRF3jw/X8B9/vw5PH36FKbTafR7SO+FwDuZcCGB61fr7s8sy6Ddbl/gifF4DI8ePYLbt29Dv993th2moysDIXvHilgeWa/XMBqNNhZfQsDUrr1eD3q9HpycnMB4PDYH/EOOewh8c0VR5wXrKiP1XRE7h/M0n2O8nSKgC92YdhrrrdVq0Gg0oN/vQ57n8OTJk1I2BOApyd/7vd+DV199Fd577z04Pj6G//N//g/MZjOYzWbQbDah0+mUemreOib1eh3u3LkDi8UCHjx4AFl2nkpZkimpdFwVXGVnG30k1F1UXnBwnxD7mD5PfS5fIDMWVffhZQRlsN0q66GLBwCbY1UFaDpH6VQZ7eeq70+MhaV9Pjf4wp21Dh5kD7XPF2Ji6QR4oXt8GTWkuzRDtBWFz49H8CAr1ROoQzm47xbi+bLmIvZdWel9ywLnO/q9D1JZHgCPnTsoG3CxGmWSdC+1ld/n87mzrfGZhw8fwvHxMbzxxhvQ7/c3nlmv1zCfzy+cYOX1UpnGY4rYp/gO2C5mm6mCB66yPUFhHTssQw9Q0d+Qv1JlkBZn9i0g4m/SmHNIdG8bml73nQQtCkn3baMPLOsNWozPZ9dK9U0mE/inf/onaLVasLu7C/1+Hw4ODlzWmtdffx1msxl8+eWX7rAfTW1OIW3EjoVEq2X9hy9UlrWR0QrezmWtUVyjPGxENw8PD2G9XsN0OoV2uw39fh9u3rzpdjLgjqT5fO7+AQAMBgP44osvLjATGgYovPiiXYgJQ45nrOGtoazFAevzUlnLYgZvl5Yp4pRbDXepDcsCCS+bOlbWBZ/YekNliwq5yw5KWIV9arCHOsExTolvPEMK0VfXZSmlsgJj1np8MqKqPuAyJ2a+poAagZqTWlRml4UyeJC+a71eh3q9DrPZTExb46ujyjnAAwf4mf8uyftY491Xv0ST9Dx1QKX2qZ2CC8f4GYNU8/kcBoNBaY6YJjO5oyz1ndY+BjBo0Ha5XMJsNnMLVdRRL4NHpDmZUq/FNsDfaT/QO4ik+iRdjqebe70ejMfjjbvmfLAGifl48vLWYFsouBzTzzGOdojPpAUBnw6TApax9OLf9GQM/Y2ON95zlioHMYhI20G50Ov14MaNG9BsNjcyBmHbtVpt4/S9tX2fvcN5Gd8R0/RiO+12G2q1mkuz3mq1LlwdUhTb0KPfFvj6PWQ3+XinDHlQFiTepN+n1BfjH8a2U4RvLX5wmWMQqtNqS5VBG9e7RevT6qb1W57j4PrSanta2vUtNljqj6071RfX6veVxb6iixA7OzsbOsTXflmySGojtOhbtU8n2echWmJiZhIsNqI0fvQEYtH4JLersiyD2WzmUvejnQUA0Gq1IM9zl8mAn14OyaMQvTxFq9VGLSseIsmRGD9Aq0NrJzauUqYsjiknvWOKnR8b70sta7GdNF+0iO0RE9ukPB9Tv/RuPj7z8a+Fx6z6lbazWq1gMBi4TRutVsvFWPI8h06nA41GY+MZi270wSKTtWe09orqHI2mMuzn2N8tNoMvfqbVd1k+oiarNZpS4wRFILV54ajJYDCAf/zHf4Sf/OQn8B//43+EVqu1UcE333wDp6en8Nlnn7kg1qNHj8QG8Q62WJRpyKUihQarc5QCulMMjeYQHSFDAY1uCqlenyGOAagQpHqtfVzmCT+Olz2oZFGIHOhohU6sIa/RMjy4R0+r+doO0RX7DtewAR2kKurdZiAM4TMgUZ6F5GPZ4MZzGY4f3teEC4IxfXdZ84e3y2WF5fQIX9wAOD/FmXJvXJ7nsFgsIMvOT7/ShRxpTgyHQ3cKOcsyeP78OeR5Djdv3nRlptPpRoaPVNBAF25qo3Tz3cwh5HnuFl3xLksfMJjybcpWgDIJnTvUTVbniwaz8CQSpja1zj+8exZgUz9Op1PIc9sJVgxm0RMJRQPpVQfQywIP2lEbhAdMcK7G6hXNsUbdMRgM3GZUtG9986ler8Pu7q56LUeIFpSNkr0l0YkZh549ewbj8dil0t02LsOJvcYmeED7ZQPyNGY2wI3gVx3cJ+UbzVNAFxIptOCwD5INzAO2ZQe6Y2HRSfS+U/5s1fweo/dRfiMf4Ek+LZuLb9HR4ltp93n6oMV28HQvbiTK8xwODw9dBhGM9wHE9QkF5ztfjIqeqKzX63Dz5k3I8/xC5r1UWlLo9oHPz9BiC44v7wteRxnvV9Ypw0aj4TYGL5dLaLVasLe3B7/927/taHz69KnjE2x7Z2fnwl3ti8Viw76ldjradBx4opLeEX8ZkBaFyliYscq3kNxLictUAT72lgxhsT5Kat/zZ1JOu6Ys7BXRVz67wNcmf5bLoKJ0+RYR8TstOwTA+XrTeDyGTqcDrVZrY72JwkIj9a2LxACttsVVmGcUfCxS1p587/4y+heIbbxXjE0qlduwFE9OTtzkyPMc+v2+E1TD4RAmkwkcHx/DYDCA2WwGk8kEzs7O4Pj42BFCBZsk4HwLltLLhVBkQhSdTNbBtQpNrSwaZqHFrlhlYpm0oXcpIrQs9FYpAK6aMPWBT/QUQcuN/tBCbOg3X6BYUspF+EBDGWNoXQiOmdchbEuxWd7NN6ctRmBKYCTUlz7+sQQgfWWs422Fxgcpi4f4Pw3M80Um3/MxbVocO993If2hBRw0WJ21kD7lhjgNnPLFGyxLT8HiYhjtbyyDgSLrO/mAz+OCH+3PUN/j31ym09R5eFIO06jOZjO3uCTVVxZSA6UhenidtVrN3ecLABuBnFD73W4Xbt++7RbWB4PBxmlIKmuKBB99Mteqa6R0b1VAsjFi313SJRbbVXoWv7PYDTzAi59pqj76vc8Pof0dorvRaIhyycrLPjp8eqVWq22cjI1BWfN+mzYMDyJJ/bdardwGYC5TaV3S3Oa6F3/nG1b4nJDmiOb3av5V6niU2f8ptgOloWhgj849DPTv7u7CZDJxm9FSfJ4QrHX5bMYY/z4FKTGUWF1aBl1WaHNGo0uTlUV5ln5npTVkg6LM8J0mtcY9eFtlxX98sghRq9VcBpHFYgH9fh9ms5n5mg5pbHx+Av4uxRbwnlBuH1tiZ1VAeo+QbR4TeymLvtRFJsmvkMplWeZ8jTt37kCWZfDgwQPnh/jsPo2ned9a7GTL+1lkhUW/SHqvLD/Qihhda9EdVrpi+sVCi6X9mNhKDA3WfvDFO3x0aXJfeidfDCtFToRkkUZXFfDF4RaLhfO18Joo3JSHG9XxwB+/ugPp1uouKl9j7NuQf6rxRFl9HqNrtedjy6TE87aln7U2q26/6HhuLMb+9Kc/vRDIRPzyl7+Ezz77zCn39XoNg8EA/uqv/sqd7Gg0Gs54q+I0FiJmQlw2itLF707wIaQk6O/aiZiyHfwyhGIViiKGLqpEy6bFWqdPYVue5Sn36Bznu3d9bdHdR1q5UGq8bQpIbGNbgcMYFDWStN8sd2vG9Im2u9bnaJUZhNICNDxoqukuTHsC8CLVfhk0Yp1l3g+o0bRardwmqbJkd5HAqZYqNMYxtjobdEHURxOWwYXVLMs2dlquVit3QhHpAAC3SImLcfRuVcRisYDRaFS6LFkulzAej6HZbG6c3rUusiA/cJr6/T68/fbbMBqN4NGjR9BsNmG5XMLOzo4Lclcpe7chc7vdLrz++uswHA7h2bNnG3dWhU78fv/734d//a//NXzwwQfw5ZdfwgcffODujsUgkwbK5z5dGQpEWRb81uu1m/caqpz3KQEBlIdaH1A5XdbJ7Hq97k6H4qnvLDs/EY/jieNWVoruWq0Gh4eHMBgM4OzsDGq1GjSbTS/vXOMFkD+kLADS/d0AL06cId/QE8Gj0Qgmk4nT73QhFeUR6gbKA1wf4P94TYD0mxRkovRKJyX5XKLz4yr4r5y2y6LplVdegffffx8AAPb29uDTTz+FyWTi5nIVCL0rlR1WUBlX1lhL9VSZ3eKq+k4URfozZaFTWwjM89yd9sOrRWj9Vvs4lU+orONp9rU2LAsMe3t78P7778M333wDv/nNb0T7vwiwX/B0LuoELkMxUA/wws5Pze5S1iKatuiCv4fkaNlzi8doUjZrUZryfDOzFtXVq9UKfvWrX0Gv14N//s//OTx9+hSOj4/h+PgYHj9+DK1WKzljB9KMftxVuy94G6hi8Yb63jEyKVQnpS80JxCh+WGhsQydyuunNNF7tMuE1B9SnFnrH+5bhRYEeVv8d80u1dq1XgtEn6NxydVqBcfHx9DtdqHf78NkMnGxmUajAffu3YPpdApffPGF22RJM0WFwGXGNjYz035LyZJXlS9wGbZ8lWsm31ZsRO1XqxXs7e3Bb/3Wb8Gbb74Jjx8/dr9hCj8s9+jRIzg7O9twemnqU18gqooFrbKRMjFi3iukXOjfFqFrpTe0OCY5HrGw0mB1TCTHx4oYQRRa8CyycFY2QkqWIqTYtTroO2ntaZ9DdBVxEGKfDwXFi9RRpF6f0WNFqI99hrElEEHLWdr3tWeBZBxqxij+HwqkIKSUlr66U1CGDKjX6+7kU56/uLvUOl5lIGV+FpWzWvuUj/FzqC0ebKcnFUJzgvb1eDyGJ0+ewHA4LK3/sa/m8/lG2i2JPi5fLboVFzJmsxns7OzA/v4+NBoN8XTBywCuf+l4ot3ps2voZoxms+lSImHqNX5SWJJB0t8WSDKnCl3CYbWxNGc8pm3t/Sx2U6heKWCjLdRlWebG1BegDQV/lsslzOdzGI/HsFgsXJotAHCyuNfrufvRKH1lzinN7qcbitbrNTx+/NhlLRqNRq485b2iwaoY284Cy/M+u00LZlE5L/kyVQS/se7YZ6S/NZkTM5fKDq6WXX8IGLijizNVIMX/9MmOKuZ+qk1VFj0W3yBlfKzzP5YXeSCb9mMRm1YbD5Q5+HvRoD1tR+O9GNlptRlpebqoi5vbqE7d2dm58J4WfrWMJd3AQNPb4rUeq9VqY3FQGlcfLUX0AQ8uF7GZJBqKxDjoM3Szkc8/tsoIOm4ok0ejERwfH0O/34d2uw31eh36/T6888478ODBAxiPx85O0RZCpLHTZK01BsFtA8ucCcVENdpDKNtfj3mXVNs9VD+F5S5nqa6Qripqp1meD+lxa/uxejZGJsXGVCS+5+1Y38vns0r1pMiqLMvcBnn8u9frQaPR8Pr1dIw0/0ai16fLNRmgPcPjMhKNFhSxJ2Psc5/uCj1vkY+aT+Prp6r8iNC4XlVcOEL16quvwn/6T/8JTk5O4Be/+IV7sdPTU1dmPp/Dz372MxiPx9Dtdt0l7svlMpjTv4zOsTLVywhuPPH+suy4oAIjJDSqYtwygj9l0bONCZnSRhXBIYR1t2jIMULHS0qVmBJsvSrzNdVILxOh+mMNQwQaEWWd0Ikx7MsYX6sRQgMH0u/0c56Xd3euz7kto85utwuNRgNGo9HGia5vC5CvYxxaCgtf00U2ALhwb5Gvvfl87sZjNpvBs2fPgs/FADetjcfjC7/xrAW+QL2ELDtfkFqtVvD06VO4desWvP766+rzVeqgMsHnOd5d5QPqr3a77Z7vdDpwcHCQdC9hSqANUzEBvLDHiizcFIFmU5UpszWZXEa9+D9uLMC+pTzc6/Xc2MbqPzx5M5lMXErGPM9dKmK8x3s2m8H3vvc96Ha7ld+5LM1/TMOLffDLX/4S2u22W5AFeLFgG3vfeIiGa5QLKbCE39NUj5Kt7QtkXwWEZF0qX5Xl+8fKYgTXzyjziqST532RkmKU17eNGMk2fCT6LpZrOvhc0QK2ZdAGsHlaPyXrQijWkWozaLpJ833pYhvKn+FwCM1mE/r9vitXq9WcjVnmiSOeshJpzfPzU7O7u7swn89hMBiY3kdC0XiXFnROBb4fvq+Fz33AzWLNZtONY1m8lefn6UPr9To8ffoUTk5O4L333nO/Hx4ewp/8yZ/Ahx9+CMPh0J2URZ7iddFNDKE5GrpzVHqPl8W3AdgurVY/MvQMl3USz3L+sy4+hRZyJb/AV38MrHM81jeXUGb83TdmMe2EFjYBLm52R1/Lyseoa5bLpZPnWZbB/v7+hXJlyC9uD/n4EFGEl2J1kfUZK6RxSLV3r2FHETm+sRj7k5/8BG7fvg3ffPONS2M3n8/dCY71eg1fffWVOxGLJw6sk69MaIKGtqUJ6BgH0aKUioIvHmgBsxhBF/ouVJflPbUTCiG6LIstZSvZMgWftvNEK1PF4o2vLSlwg+Vo2ZDj6DtxJDl6PiWXYgCmIFYJhow+bVx98od+F/tOUr2cRv5Z23zhqzv0fZEgc8xctS680r+tMhADJNhHdFHE127M3NXGJmXc8Rl6l8ZVMZ5CAZ7QcwAX+yrUzzh+OHaW4OT+/j50Oh23I3s6nQbTG2Pb1lMNqXPbx7vozKCTIi3IFrFzzs7OYDgcwng8dm1VvZAU208WZ0EKAPb7fdjd3YVvvvkGptOpC4rScrVaDTqdzoW0af1+H46Ojtz3KC/oQl7KHOS8TlO9h+wH5PdQun+t3VSakT8tMjlkQ0u8SuevxssWJ9ny22g0gvl8fiGjQIyzVLXs5XoJAFwqLj7/Q/pKko1XRXcApC0oSPrBVwdNTZ9lmVtMp6etJJuK21D4Oy5o0xTYeZ47nYInpuk/q32CiPFLqgzWboNXNF8iy15ssBgOhzAajWA0GkGr1YLDw0N4++23YTwewzfffOPuKJTqLfMdkK4YeYGLSGi/SbbPZc3JVJulDKS+t0Yr1oUnNumGJ4AXCzd8foeC1TG+k4+2lBSFUhu0jpDNEIpbAcCGLe3za9brNZyenjq5t7e3B/1+f+PKD6wPy2s6VpvzvnemQJnQbrfh6OgIAAAODg7g+Pj4wiZJ2q6V34oETwH0xSEA8NrZoTZT5kwolkNthFSZiToRMzd9/fXXMJlM4Ec/+pGTfzj3aBur1eqCbcNjmr7xC9mF3FawxGQsuraoLWytM4UHLfX46AvZlKG66D2e9HftvmCqAzX7S4OPlir0vwRLXEj73UKbZiNo/ePTQZb2+RhZNjpJ7VtoC+lP5Imzs7ONq6QODg7cBhyUIyEbm9qHkq6xjk3ZtpKFz308ZolZxvBljI7UaIopUwYtsYjxr8pAkfo3FmP/6I/+CAAAPv/8c/cSdCfaer2Gzz77DJ48eQK9Xs8ZZ6HATJngDB2jpMtGWe9mceCtBn6RQEDMZKcGFxXiZfS3b5KWYfikwtI/2wYPbkp3YfEAE35HnQTJmQottkvKoshOWU1wliW0Y3jH56ymGLf0eQv4uEo0cWc4dJeolX+LGFkWpChr/DtkDNDfeYo72k9FjQpajs+TIo5VlmWwWCxchonLkisUdB7i6TG6eFh2O1SWodOvnYLk4354eAh37tyBk5MTd3+glq2Dy0B6p4nm3BcZD99iL+5ox5RflhNtfD74yh8fH8OjR49c4DJ1IxVvqyz+tPKRdAfW/v4+3Lp1C8bjMTx69MjxDeUZPFnBZeT+/j7kee7uI+R3RCJiHBdcqKFAmYM0aboOnVLLZgyLDMNyFrtRsgc0xC7kU36xOkipej/PcxgMBm7RrKhs5nVbYdW7dKwbjcaFzRjfFmh8GBMMsdi2rVYLarUazGazjeCgZs9ogQy8l7DRaLiNx3meuxSMNL055etYH6zqAME2kWoPNhoNmM/nMBqNYDAYbCzAHB0dwXA4hPv374uLsVWBB0dD49RoNKDZbEKeb2bXoPXFAOcLD15LdaXwXtVI4QVL8BSBJzT5iWTsL8onvO8kPaTNRUu8CeG7CiLGD6Oy0hdIt+pIKVbjo+H4+Bh2dnag1+u5e/1oNhH6jvyEo9aHGq2cz/lzZ2dnsF6vHQ3dbheyLIOnT59emJ9SPETrG6SJ93OKfSKVQR+JX4OhLWpQ4GZCSc9ptPDYHP0NbQq6mJEyP/EUONb/5ZdfwmAwgNlsBq1Wy2VJpECew/Sj1MaV6MbfpPGw8rwmNzVw/o2x98uIiZQFjc8ppLiaVpcvzou8RBfgcbNsaAOCRaZq8xNhuYqkCK9rdSJidK30DlJ9nJ98vK5lz+Djq5Wx0m/Ri6HvfXOE0nl6eurobbVa8L3vfQ8mk8nGYqzvnnD6G/KjJEPp+3NZo+lWiy9u1d8SLPEeX3lJF4Z0V+y8sMyBlHmSSk9sXUXmcNW4kKYY4JzgxWIBk8kEvvzyS/j888/d97PZbCPtG37vuxuA112kA0LCTCsbwrYHBQWpj5nLDCKFECMwQvf4FBUqqfV+V5HaL1K6LV4vN4x95coen5cxUGXpA987SQ6lBGqISQ4s7buiQRGJRmvZqkDfW5Ohs9ksuFHEZ7Bq7UkbHorAZ9RetszD9/TJfOv8l3S3by7g2FntCh/oqanVahW8TgHrjAl2WcYKbaterwc/+MEP3J0pz58/h5OTE++zvsAwbxvrffjwIZycnMB4PDbbaFcdu7u78Md//MfQ7/dhf38fFosFzGYz7zPz+Rzu378Pb731FvzBH/wB3Lp1K+k+Qi3Qap2nRdNPaigi42OCulbw4KoUgEzR7RhQzPN8oy8x8DkcDk0prK3A+lHm/+Y3v4HhcOjSNt6+fRuyLIPj4+MLNOHz28BisYCvvvoKZrOZWzgs65qCa8RDsyt8AR4tYPey2cBlAVNtzufzje/LkqEhe49C0rdljkuKPUntLhp0Liv4bKWhrH5IjXfggkAZNFWVLSRWv/J3iAmc88+hBQnalrTwguj1enD37l0X8D4+PobZbGZKzUz1qOUd8jzfuI5AKkPjEtoGDSk4LfnLtExZwI2rdAHTugAWSw+3s6Sxx+tREGUF5IfDIfzP//k/4dVXX4U//MM/hIODA/jxj38M0+kUZrMZfPDBB/Dw4UNXnp7QLgtcFhbFNmXotmHtIyuv4iYN6oNJC+3S8/hb2f5H1eC8VoRfNL3H9Zq0Qdsai9PitrQOrkN98Ro+drjZg/o9MfNQonu5XMLDhw9d6vrQNRAoU/gmAZrBBmnWroLyIWbOxG52LwNSn4fGocw45jWKYWMxlgYw5vM5DIdDePToEXz22Wfue7xgGeDFAOIEsKAKw12q38pcmsEbqj8GXJBpOy/4M1xIp0yckBK00G2p0/eMFIjz1Z8iyC07NC4DV2ERnSpIzfHI81x0aHzBJAsdmuMTA8szRTYHhJ4vUm8qQgs/PNgtPRta2L1qitgql2hZ33stFosNvYRpkvA5rR1ts4FGg/R9kXm/7YCaBJ9TwBEbGI2RFTELChrNaKTv7Ow426WKk8ehMaP2UrPZhDfeeMPZWdPp1C3GWoznkD2wXq9hPp+7RdhGo7Fx8jfmnYrCMq9jxqHT6cCPf/xj2N3dhTw/P8Vx//59tXyWnZ9IPT09hR/84Afw/vvvm9rkzjZ/h1j5WeW8DumBIvVQpNIvBRF8p1Z89hy/gxd/wztdedpKCz2+cjSI/PjxY7dTu9PpwP7+vrvShdKu9ZP0Xj5fQLLTJBrn8zk8e/YM8vx8RzkNXHxXF/MuA5aAO5cb3F6InWPbthNiF8okhPQknjzmGQ5Qbmj302l1W+SaVM5ny0m/ldE3VnC/jl7JUZQnygrYV9U+l5GhE5BYTvJlpfgMLePzf7lsttpuVr/dZ+eF5hD+bjlJrtkKtB5MG47fHR8fO9vS8k4xfjjGI3Z2drzZRGgKes1m8PU7votPbqf6cxiQx1PyVvs71jfln2kZeugjVmZaMZ1O4YMPPoDBYAC///u/D71eD9566y0AOLfVvvjiC7cYK8UurP2r2c9YV2g+aH3ns+nLirnG1uFDil1vgYX/JX5rNBqQ5+eb36XxtehlzWb2/W7thxh7ICRDtN/K8Is4f1vvH/eNDf6u+RdaGSkrk09Oog7m1/tY5o/222q1gtPT0424jebXUVlCs6dY/XWrzR6imZbFsZTarSr2ynmI0yDRWYQGS19WJa9CiLVJLttP3rAO/vt//+/uM94NMBqNxAdpuriYwEaZwb2ikCZq1bAoDt/7lbmrLLYfuXLQJmIMg8c4JRp8gbsqUBYfl4mQM+HjK58joz1jvX8zFZfVv1d9XH0GWhkyIeb9Y5xry+8WxcmNc/67dBKWn2RdLpculWkMsE0etKnCoCpDLlYB1Ps0+BeCxRiT3jnm3Q4ODuDOnTvQbrchyzJ44403oFarwf/9v/8X5vM5dDodyPMcJpNJKSdtU4Ku8/kcWq0W/N7v/R40m00YDAYXaMFUhwCwkb40dO9tkb5LQVVyX0Oz2YSdnZ2NU1LT6RQePXp04eRUs9mEZrPpHMNWq+VOJyCGwyGcnZ1deBZhnX/Sd3yx0CdntE0OeZ67DQMxfZ1KtxVc5pXlaEn3THE5W8ZJT+vJJ9QTVQDHc71eu/mOm1wfP34M0+nUpfTDd8dFKUwDl2WZC4J//PHHLghR1smuq2jjXuPlB/IUlZG4cQjnw6effgoPHz6E3/3d34W7d++W0m5VASFqt9TrdZdCdT6fw2QyiZZZWJclRX3ohLDkh0s2sy9QZ6W5jH7V9BzXqQCb/VOv16FWq124woDeTYn9xekN9TEtZ9Wtsfo1pe+o/vUFyAFe9AMubvoWtPB3TLc9GAxgb28PAM5t7E6nA48fP97I4qItYGl0U17kCzdIL7cDVqsVPH/+HPb29qDb7cLdu3eh3+/D559/Dk+ePBE3bmwTsfawb/GJ9k/M1RHWdnk7KUB7Gu+JpbZHrVaDTqcDi8Viw77GeZo6RmjjILj/qc3p74It43tHa5witOgoPY8LZvV6HXq9nrvHG68aoM9TWrT5EhMPkiD5J7Fj79OJmn5KnZucXuoH8Wu18IQnnpClJ0Cl+orwPa03dBpV+pzSHsAL/wzfEXX/YrFwPg+W9y3MhjbpYF+WdTqf87ekC7cVL/kuyLsQXsY+2FiM/eqrr7yFqeFkcRb4RL0qC7GcDp8hy4NO9DdfmVDbZbyHZdEzpR3Loh1vO8QLUv0hZcy/s/adj54UY3Tbge4Y2vBduVLmCAXzJRr4Z0mxVCn0isyVVEMu9C6aMWapO5YO7mj76NPmWApSgzMxCwGSExUDKXCPOonLF0xHSoMzqYsb1v5PqRu/i5n7VYPLXulurlRYgmLWIEen04Hbt287Huj3+85wx0CBZYHF4sjH9jtdTMqyDG7cuAE7Oztwenrq7oqlzgMGzWKDO9r3V9kotQYIsF/ou2Aq5vl87u4HxXK0D+kdnOjEYmrj6XTqTjZKJ4u0oJmPfv48hyTPpXp898/5aPHZVam61BL0sj4fqiNFN9B3D9l+IViDy3l+vmDOF/rxGXxvXz/hvYfdbhdarRY8fvwYVquVu8OY638KtPfOzs5Kd/Svgsyw2vH873q9DvV6HVqtFuzs7MBkMrlQX6zu4kFya1n6XeidiuJlCkDwPqB30A8GAzg9PYW33noL9vf3YT6fb5yO475nlX0aAqcFeS/PX1yTkXJC3WJ3+nxZS2wghiaLLIyFphd8Phb3Qal9gDzET+dwxOqvmPK+MtZ4B7eLNbkj+eAWXkBfiMcE6HN4+ny5XDo7CRfZkL+tSPFNJTt8Npu5Kwjw7thHjx5tvJdV75aB0JzA79EPwWdi5qMWY/TpIpwXVtvaAl6WyjS0r9frNUwmE3cCWIs5SWNjtRH559jYIC0TGoei/LNNnVTWu/hkiVYPLrjX63W3uXA2m234a3xjTMj+l/jDR3NZsMzPorG1WLuTPptlL64fKRrD8sVfqJ7gdjOvx9K2VebRdunzmCnFKuPz/OKd1BotZel3XsZn2/jGhMPnS2jPcD6OHbcisIy5hiJxCqmeGFhs7iphzltHU4ZIuctD8BmLRfGyOKCxQCeDvh/faVukP6UJK9WFd4TwxVju9Gxzwl81FOXrmGAPB1U4Ur2huSrtRJWe42N+1ZDihJUhtLXvfM+HjJPQYkwZp/toeyF6ypjPVSg7zrcAcCEAtre3B1mWwcnJyYV+owEdS5CVPu/b/IBjGNq05HufVBRxsjlwcavX60Ge5+4ECz3tJznEVufJ0jeWOf3qq6/Cn/zJn8Djx4/h+fPn0Gg0gvfC0rGn9IdoscLCUycnJ/DVV1+Z7rBFnqII9Qs6xxY9YIXPASqbf7Vg02q1gl6vB9///vfhgw8+gL/4i7+AWq3mFro1TCYTePToEbRaLTg6OoL/8T/+B/zTP/0TAADs7e1dSLUktV0GrKfKfZAc9TLok+ooeuJSSi2MbeW57S65EFA+bQuz2Qw++eQTODs7c/JwPp+7zQBIEwLfe7VaQaPRgE6nsxU7OWVeVuGnxYDOfylohLKQ6iHMevDmm2/C4eEhrNdrODk5gb/9278FgHNZSDe/4OYNX9AC+ZMHg7T5JgUwATb9NurXlTVny4TVL6wKOH/+8R//ET766CPY39+HPM/dyUd+B2OZfajxQgq63S6sViu3WQIXKcrWw1Xdc0rh8y+L1CnJxxCvZdn5ovdyuYTFYgG7u7suG8Z6vRYzjvD6ebA31J5k26YEcX0LDRLvxcw7Hui1+DMIX9knT57An//5n8P+/j7cvHkTZrOZGAOUTmrzeummwxSMx2P4/PPP4datW3D37t0LWUfoPa0atHFL4WXJVqX932634dVXX4XhcAhffvnlBZ0l9XvROBLKysVi4TYkIZ14D28ojqPpMfo7//7Jkyfwp3/6p+67H/7wh/DOO++438fjMezs7ECv19uwsyX9wtOOS/OnCp+G0lHW3eRVIkY3F9XhWbZ5N6g0j3B9oNPpQK/Xc78/ffrU2eaSfPDxmxQ3KjPeFosy7I2YGEhInvv8NT7PY+c0ta/4groE372zGq1SXXTDD24Kkp7j8kBq2xqzKwPavEBa+d26KfVzWPUZn2s+3XWN7UNcjJVSY3CDlP6P4IEWyaHzCcWQ0PQxStnOYkhYcoFsNeqt4M4mtpHSjm+SWoLRnA5f3bQe3wRPNXx98PWv5vgVac/Xdiw/+voeaQkpMs3IqUrA+vhMazPGkInl9VQjKSVQWVa9kgMnPa8Fnnx8kyovOS3S3I/tZ6vByelLkXetVmvjzggM0tDy2wqEam3EtlukP1LKUuMR00tpeiOmzRgZHpKJCExv2O/3YTgcun+j0cg5I9o9cxb6y3C86JxqtVoumHhycgKTyQR2dnY27jROaQPr532YsinA2h5vW0JZupC3v7Oz4xaaz87OXFCWpncGgAufF4sFNJtNqNfrMB6P4fnz53BwcOACVb6TTKn6yyfnt4Gyx16zKyyBD8mXwO8twPHhPK3pEMkPiQF9BgMDp6enAHC+AIh3wuHv6D/5eAXnJW5y1YIEFhuabtzSeDYWRfqobFj8Eto/rVYLut0uAMDG4rzP/9TGSrK3rO8pBT8QqXxI69Xa056NpdtSbwq0IBqdr3l+fqXAdDp1p8S18vhZW4BDWUXrKFNXYf2r1crxGz0VxGVlqA1f8Nn67DZs27LbiZ0TNEAsbaqIiYMgpPeJjRX44h8p+i7UHq2vDFuV0oW249nZmUtFyhdiue0Zoi1WBqNuxLvgcUMN+ifafYISP2jxktDfkl9C5VFIR9XrdXdVio9OH2Jsbm0MrDwSOw8BznXt06dPnT0zHA5hOp1Cq9WCfr8P4/F4w7ami51SHNI3D8uQOZpO4N9J+lt6f4lOjpQ56rMBY3R6aruWOvP8xfVPeMcnvW4H/6d97LNVfX2eMvbSGFvqCumkkM9RhE8t/MljC0Xa8v0t1W+dn7H2DoKnX/bJYPxOeg+NrqrsI0l2WJ7R5oTPh+TP+96J0iLpLksdRRDLp2XI+yLzMMUOLIILi7G1Ws05sgDnO12sOf5DRkkIVb9sLCwDaDVsYt4NDRVJ+IUYBJ/1PRMDrI/WqRnjVlQ52UOG0FWBJARToQlwyfAH0HcHbWOHNUCxhUINKX0oKffLQkhp0IUabVGJn1zXglMho8pX1oqY51IWitCwl+5jfO211+DevXsAcC6r7t+/7+4+z7LM3ceH/RV7Ek4yDLcR+NomsH+63a67m5DuZKbBEA3YJ5Yd8xQh3sH7QwHA3WvP0yf/wz/8A3z55ZewWCycc8hT7qS0nVJ+sVi4vms0GvDaa69Bt9t1NP/qV7+CTqfj7pujiL0jHuUgjh+OVRWogueL6kKAFwtk2AdWGzbP842AEUWs/VbGe3Da+N8+B47S4qsnhQ5pLvMAQdmgjuJ0Or3w+zb0d5ZlLk3iX/3VX0GWZYVP4na7Xdjd3YUsu7iYRAOWVN/xfsYTtvQuKY4y7U0Jl20/XXXQ+74sKNumuCrjg7KC90fV9JURsKSgchcD0aenpzAajeDmzZvQ7Xaj7owt4+4yfupQyq6T2g9VyIwYUN8npPPwdBbaraiv8LoK6Vl+BQIdD+smOWsfIf2WYKOk531ywXqaL8vOFwh5umJq2+MpxuVyCePxWK0L7/PDxReNrtirN5DORqMBeX5+uq7dbsONGzfc7/1+H46OjuD4+NjZBShfylyc8PGdJMfw/9lsBp9//jn0+3344Q9/CKenp/D06VNnR0h0xvhKVvh0P75XigyitC6XSzg+PoZ2uw39fh+Oj4/hiy++gFdeeQVu3rwJf/d3fweDwQAAzvmh3W7DcrksNZuJtkjjoz9UhtrzZdjPl4Uq4hTURj09PYVWqwW7u7sXyq1WK+eD4ZyOaQMgvu8sz1n6RJLBtO4UnRriUypveBs8JqDJptSxDr2PpF+k2LFVx1FIcSWqW0J9HBpPOpYA5d8dG2ozVZZbFwZD9V+2HWfFy0BjEUjjsLEYi5exS0IHK/Ch7A4suohYBb28b6w0SsJXEsQSTVpKHT42vpRbMX2J70V5warYKGLblJCy0Jsq9LZpKFH6LMbiNtrkAqKqoKqGmKBy7O++shZ+0QLNsTImpER9i3+h+eWbpz754oPVEE6Z675FWEl2+XgWnVpceKvX6+4+ST52Pqc6JpjiW8RGB91iiMU6kNsC5TvcjY5Gsi/FFD5bNi2UJgB5o1iz2YRerwfr9Rrm87k7ARk6Gau1F7PAJDkoNDD66quvwtHRETSbTZeKkaYz1YIwUtsxur0sOV40KJFik2Ef8rt1tWfp87R/Q8/giQvurMUEQsvg+aLOUmrAoggsgSwOi3NZlQws2scoW1KCywDg0pXO53OXLnsymcBsNoP9/X1oNpswnU435K0PVE5rOsRqbyKqdtp9utNi6/ns1Pl8DvP5HPb29qDdbm/IrVRaaWpcpMFKu9WGii37MkPrR/59lmUwnU6TdQ6vl+uv2HmhlaN30qOeQnu0KDitIR5B371Iexa/Zdv+srU91OE8oI2/0f8t7fFsB9Y4B7fRfLy2jXlP3z3E63xhv1arwXK5hOFw6OzpVqsFeX5xc5SvfuzXlAUMpGsymbhrPXCOYbAeF/boXNHem+uFUL9IssPir6Ofi/0GABubqvg1URLwPaWFEF5O+47SX7avKflnmHWm0+lsbIzQTrMjtD6VfrfYO7HgPIH1WX2fquZwmeNUVWwP/Si8zgjHHTcMTiYT7/Nl0eub52VD0i9a2xpvxbYHYDs8I/GrtS80OwD9Zak8p9H6vdY+f0Z7VrPvtHpiY91X0R6n72C1SfhzUtmYukL0herzjZtGU2rsKfVZqd3Q/An1qfT8xmIsPREbiyod96vSpmSI+wwy6TPCyhQ85R6vkxuHtK0iuz3Q+KPBYs3QuwxBVXWwqIq2YuuxBq6tgU2LAKlqLLc5XjEoOh4+/re8c5n94jOeeLkibfhgbSvLMndikDqpsXRioKTdbrvv0DF/9uwZDIfDjfqyTD/NGUO7T+7v7OzAYrFw6Suz7MVO0Kto1Fkwm81gvV5Ds9mE1WrldaoQNBjBEQpOSXXRzUHr9RrG4/GF/tzb24NWq+WC8Pv7++ouXOu8C+l3rUy9Xod6vQ6z2QxqtRr8f//f/we3b98W26UpDiXdbqWramxb3+NdcKenp+Yd9Gi3dDqd4GIWll0sFrBcLqPlsRSEirW7pEDxZepKyfGx6hYrtIBgTMD9KtoTFPwd8zx392/PZjM4PT2F9957D95//3348ssv4fj4GN555x2YTqfwj//4j24zSdU0+gK4Vbfta0+Sschz0sZT/DccDqHdbsMbb7wBeZ67IDZ/JuTP4e+4EQQXZAE2g+lF5sHLjtR5SPsMxw3HFfsVx+vk5OTCPXWxbeEpwNBJ3JSgD83AsFqt3H2NePVATF0+bIvPrjIv0z5AftGAaTPxZCtdCAII2/fIc/g/3fCZ5y9OQ1pPo1r5N8VfswT2+b3VWlyKvjui2WzCZDKBs7MzODo6gr29PTg8PITVagUPHjxwi6PcXsB+4ePEbZ6QXYk+1HK5hCdPnmycjgU4PyG7Xq/h6dOn7nQV9dOs2U5CY8R/l8aen3aV4jPoG+CmK+0QDNV5+J10Xy8HzfwkgfZH0VPE2B761mhzHx8fw8nJCbz55pvQ7/dd2fl8DrVaDVqtlrdOS2De93tZfkqKTvg2gttr3LZFW4nGBg4ODqDdbsPR0REsFgv46quv3PzU2qhC//jq5Pyi0SDJWOwHqT/oBp4UekM0U9/WJ+OoHLboCQq+4Efvcqbv67sLnPYRrdfXpoUPeJsaf/rq0vot9FwKisTXrTz6smLb/mYZdRWhWXp+YzGW/oiOiyWwxA2IIgTGIiVwJrVnUeDS875ArSZ8YgKsVgOfG2xcQcQEUbmywfo4L5TlYG7zOQ0x4+6DRelbaLC0GTIaaF1lzVGpDgtvxwT8rnLAwTqPQ2V8skQzmLhBReuSjK7Yd5RojZEZnG5NNmIgQ3qPULu+d+p0OtDr9eDevXvwyiuvwGQygfV6Dc+fP4fZbHahT2LSI/uMOQpNb0rjEfr7MoHyHu+MxXHDe5sQoXnNeVoqY3GutTIHBwfw3nvvwZtvvukW93n5PM9hNpttOIEhp8Sn1/nfMUFbHoSQFootczwEGkRMQRk6LxTUisXOzg68/vrrcPPmTRgOh25hK5QqezqduoXW8XgMX375JfR6Pej1ei59uY9+yfaJ1fPSPLDIO58MjYGPn2N0ha9tbY7H8G2oTl73tsDlBfINngBIBZ/Xz549g/F4bL4aRqIxtv1tILadlGAR/S6lzTLh49sYP4B+V3aQuQyk2pgpY0M3BtM6suzFSQ1q+5Y1/r4AWNE2MJBo1dUhHuDvLcndGJqrDvpp9iP2C19AlLBcLmE6nUK73XapdReLRXDDIPeJY3gHadR8Mq0tGpeR9CJv17oJQQp2x+h06bNG12Qy2ZgToWteQjaDFpvwjcdoNILHjx/DdDqFWq0W1MHUN9biW2WBnorP89zp8t3dXWf/n5ycuJS9PnqL0omHOeg1KVm2maI6ZJuGwMePL8hRW4b6/nzcQrYenWeajV1UVoX49WVHGXFOKr/4s+v1+sLdsZ1OBwDO9TfdtETlfNWwzB+fLOZxv5CO1dqS2vDRxOP3XG9IdITicL6YTcj2RGjXJ2nzR/M1tXfnMtRHvyXGJP2W0k9Su6k2YSgmFqLPJwtD/O6zZVPisj6EZHZMHT46QyhbrltihFJZzb68cGcsYr1eu91uPmiGVJWwtikxtIaQs8snCneeLBMyhhFCBj43YqVdcJZJFOuYcaWUAkmRXGMTRfuW/p3az0UdeV+dlzX2lnZjhHZKUFyTDaG6qPFDZRXdVcsNpG3LZV97+Lt2h6NmZNHffDK61+vBK6+8Au+88w68+eabcHx8DPP5HL7++usLdx5JhqQFof6ketPXH1dd9qHTXK/XodVqwXA4dOk5U2VTrCyhY6715a1bt+Df/tt/C7VaDWazmTq38C7Qqpy/UFANgcFC/J2e6KbgdfGd8ymy4mVHvV6Hf/bP/hkcHBzA6ekpZFkGr732mvf+ofV67XgXAODs7Aw++ugj6PV60O124fT0NNgulxWch0IyD+koE5INHDMvfbI2Fj7dRXk2tQ9CwQvaF1Z7P5UOHPvT09ONgAE9AYf8kmIbfPPNN3B2dgYAYEpRTJ+t8t6ja8RDGn9+soDruG878L1jeBtRr9edXSLZBHTOcT0Z27c4r7UrgnjZmHrpZ2q388CrpS6fr0d9rSL+5DaB/Y6Be4tcm8/nsFwuodlsQqPRgH6/D4vFQrxbnIOeXsLFKd+JQk6nhT6uo1P8X+szlI8sz6SM72g0urCJTbO7Q/OQf8/nM4CsBweDAXz99ddOJ/uCzgDg5rKUulp7JhX0xFae5+5Kg/39fTg4OIBXXnkFfv3rX4uLsXSxJUSLZezwnafTqVscw7mFG2uRZ8qynfhGCO7r85T/COsilw9lxJauY5NhaLZ2np9vPqCpzDHjJr17my7YhuqX2iiDbu136X9pwc+ypiDVzWmx8rnPtqHZGjT+tbRjiflyfyvF76T/a5nTuCzUkBqrLeJDl7moh/DdSx6io0x55eOTIrZk0bZfhvqLYGMxlqYaCeUER5TlRKascseWCwlhTbBITp9EQ5F+kNql3/G7+nx18Lq4gY60+hYK0CGhhlqZTFyWgH2ZUVV/aoZSCn9qTlYRVBUotbQb6g/JQZDmU1FY65QCA9J8DL0Lf3ct1REGIyz3T2t9KcmhkNFNF8qkU2ja+7VaLWg0GtBut+H27dvw/vvvw2QygZ/97GcbCy1ZlkGz2dyQa5xG6V0ppAWxkDGEZfC0LE0JZXG4QzQVhRQUBji/sqDT6ZSSJhMDIRrP00UODVmWuUBClmUX9CAPbCKWy+WFAEyqreHTq5QvcHGGvlOe53B6egrdbhf29vbg+fPn8Dd/8zdw//59b5uWlGQ+h1FCCi9JPL8N0LtckYZmswmvvPLKxqI2wPlYYzrtLMvcaVh85tatW26XtgYpcBoTEOO2c4p9TPta0usW2U/p8dUfg5ixT9HxXBb5HMIy271M4Dvv7e05GZdlGbTbbRc0la4qoYEKSV9+F+znq4iUvvf5Yd9W8FSFaP+V5VvwYB5+pvZXygJEKCCaZZnTQb1eL0gnAp+lJyG5jA/RGLLXU3BZCxM0NTCepgoB7xLF+wnp6T8A2W/R7H/N1qM6WNPNFFXIYnoi17LhTvN3LONK66cxKKpz6CILzoNQLE3rR14WwctNJhN49uwZ9Ho9l2mG+pK44Cel4I0NevMxtMgM/tvp6Sn8/Oc/h1u3bsG9e/dgd3cXbt26tZHimfYNBb5TTCwHfU7qg+B9uyGafeNi6TPsd5zDX3/9NTx9+tR0tY02r7S/Q3RYYZkXdNy/jfZVrC60lDs5OYHpdAo3btyAnZ0dODg4cPy+Wq1cxjJOhyRHqR4v0v/W5zU/RJqHtE7ptLfWpvZ9yBYK8avkI3LfKsXu5LJB2pAkzWGN9hhwHZbqx6Y84+OZWJ3qq9Ma69HalWxG6yZdHsuy+N8x/eibRzH1leEjVCW/rfRL5TYWYzHoVZYhXxQ+gRE7ASwBNa1OKUAXY8yl9iGdSNpChaUtaxCNG9RS8C9UT6pgkp4PleP1V9WexSEtyoMp2JaTHDMmvt9SAx5lw8c3ZSqDMuQDXzzE8hYlzmUYd8x8cl66h0H6WzOIJDlA/+dGrI8W7qRS/sDF2EajAYeHh/C9730Pfvazn8Enn3xyoY5Go1E44Bejj+i44eJhGffzFIHPoKR0dTod2Nvb23gmNG/4OEvf0We5oRjqF1yMlYD8TRfvADYXM2MMUx9CQTd6Uo4+MxqNYDgcQp7ncHJyAn/91399IYUX1hVyNGh7vKwWvEjlu9jnyuRvDCpRXqrX63Dnzh1oNBobwVa8dxffFxdj8zyHRqMBR0dH6p03Eu3cybTQGutcWerktmYV8ktqM1SPz5nXAnmaHcnlfxFH+6rCJ3u73S7UarWNxVjtHnWr02yl6TL1EaUDQF7ksj5L/7b6PCHEBlck+N6lalu3aqTwD5UBfMOSVp91EYDWIz1H7QdtY3PsnKJ6P8/PT8ItFgu3oUJ7htvnmn1CbfdQQLAsPUHb3jYoH6AdhQuAvjk2m81guVy6xTkpBbQWGJb0vqSDYuM/sbEDOjd88yA20JqqJ+jCJY4Ht4u4T4N0afYV7TvND+Xl6G8A56eh5/M5NBoNaLVaF/wMXIj0zT9aXoPmv3B6QnWNRiP49NNPIcsyuHfvHnS7XTg4OICnT5/CeDwO+lixekKys1A2IWj/SPwh8aCVn7FevONX66sYOy9GtmnzXAK+k2R3avQVtbteRqToxcFgAKPRCPr9PrTbbej3+07vTiYTl80qJPeK9j8fTwvfSTEdrc6QLR5jJ4XKSXRJ7fJyNNam9UOMTOP1Sm3H2G0S3dLf/Dlu68fKSO0ZyUaj7xWyxXjdWizG2nc+HpSekdq1tFOVLam1L9FQBSS+l2jZBh1Se2qa4hCsxmgRpDpl0jNWZR9ikNB7lxVEwkUYn7HEDdZY4RD6TkNZgY4i2KaTmBJs2NbcuEyhgu2lKNxvG2LnT+xzljnHF7YkR5SesuHGTKh+C2/5yuzu7sLt27edM//gwQM4Oztzu98laP2DJyl8Aa+bN29CrVaDjz/+eCNlmS+g4zOAkG7N0V0ul3BwcABvv/02nJ6ewvPnz2E0GpnSpV0GfAbzarXayJTBUwppi8ohXVuv1zcWKvkdtBJ4MHIwGGycekR6Wq0W/M3f/A188MEH8M0333jr3AZms5lLoedLpZtlmTvB22w2RX6WUuOWcZIoBlclDepkMoGf/vSnMJlM4NGjR+6EhAXD4RAeP37sNnAMBgPIskxNfUmBv1F5xQOTVpShD8vSqVzGaUG4VFgCW2ViG/aX1Cb9rJ1mBTiXgZYrYPL8/I7uKud4jC7cBqzt4iIBlZX4+auvvoLnz5/D3t4eLBYLuHnzJsxmM5hOpxuLLL5FAfyd/k/TUgNcvM8ypPsuq0+rRJXvg7IIxxV94lar5ewTbB/tE0smCQ7N5qPByxTax+PxRpYZLciF9lWoPo3esuXDZfKprz98sgppTrVRLGnlqY0gbZLVgO9jHSfKJ3jqlKaTxd8o+KYGXo/UhnUjpFS2Xq8nzTUp9XCeb94760sTvbOzs5Gth9ZDT96dnp6KQXp6YrNshOwcjiw7X5i9ceMGfPPNN7BYLDbeB3mNj30MPWg/SM/iplGkF30Q+g6xkE4F8tNzzWbzUvwXDZbFCPr3VYh/XnXQq6hwkzSm6l4sFjAYDKDZbMLh4aHbpOyLAwFsVy9xHoiJ6/sWv0KxdxqL03xRviDIr9CR7CCLXkxd1AzVR68CoP9b+tKXxYDqOa2/aNyK6xleD4dvrEJrLTF9J9Up1Z+6bsM3oH9XZddV9702FmNDzEBRdDE2ZdEyZRE09hnrO1kN8SII7YZFOigtvKzUHz7n0/es9P1VmNh0HK/ChKvaWfYZiZbyVlgNj6rBjeBY2RGzWByaR1UEPjgdmuERajsk+7AOKeVRSG5Y2gzJqVarBfv7+669Z8+eud8sc1hqC08TNRoNt2sbnW5c+OPGLv8cMqDo99rJBWyz3W7DzZs3Ic/zCwuxlvpDcziV/6zjS4NaaLjid9SottDM6cc6MOAQM3/p+8/nc1itVm7M6anjJ0+ewIcffggAempsWm+VwIBxvV6HZrPp5h7dDEHfiztQPl5IMfpTEJKLZYPPf97earWC6XQKx8fHcHZ2Br/5zW9gvV5fSElIgfXhs6enpxunl7ns0exBHojF72JlSuyYSW0Vtb8l+HSMJJdj7K2yaOX6v4z+TQH2v0RLyOn13ZVFwWWCJcBjGT/LuF0FO5pDek86Z3E8zs7OYDabuY0WnU4H1uu1uzde8nesPiANLqWA0snlRmyfXwXfqypQXqV2B8ptPsdCYxYqo/lUsfRSYKpU/N3CYxY7z/KbxNtFZLXEn9bApFRnyMaP6Svp2aJ9JI0b1ksXkfB36ptq/Z2is5Hnfe/kGxeLPxWKV0i8iz5CiHatTf5daLxoXXyxltrUuNBHx4U+Z6FHeo8ifhfnITzlnee5S7P84MGDoL0v/R7iJ2oTZ1nmssmgbLJuArHIE+l7Oo/oZiaepStUTyy0+Ux/056L9W2rRpm+Xox+Kdr/ND38arWC1Wp1IUZTq9U2NslJ40VpSrGTtgFOl9bPtJzP7wzVw+ukZS39o8lh6bsU26iIbWvxM7U4ia8PUQZZ39NKn9RuiGZel288NL/NR4dme/iuC/PRq9Fp4U8LtHq3hasg88XFWB8xVQWBQrAGXSSGKmpIxdCU0hZ10LW6pZNAoTqlu6Q4+Hf8Tp2rchrGghBfXlVFboWF9iKOw7ZQZBz4HKniXTWeL9MoDgHnIbal7TilTo2lP0IOugUxcg8XSujdPvw5ekoIjXSNPmnB5c6dO/BHf/RH0Gg0YDQawe7uLrz22mvws5/9DB49egSDwUBszxrM8yHLzheZu90uvPXWW7BYLODTTz+9cMdVq9VS7yu7CkCHCYF9jAufnP9idlCjE9Zut93zi8ViQ6fRAIoFvV4PfvKTn0C/34cvvvgCbt++DW+88Yb7vdPpuMCDdP1CWfDdrQxw/l4/+tGP4OjoCKbTKTx8+BCOjo5gNBpBr9eD6XTq0q3hPbM0uOdLzfeyImRfUiDvoRPxySefQK/Xg4ODg436JJmN33U6HcjzXJyXGpBn6R2zeMqZ1q+9Cw3c+uy6WNu3Kp1X1E6W6ooJDljq5XVdxrxAnmq3247vtiHLpfpjbPOq++1lt60vC1TWA1wMCpTVp9/W8UH9gKdqtEATQBrfY708VT6FZcFMAq0Pbfl2uw3r9RpGo5G5HktAryiq4h0taIl0o92M48xPtnB9SPuOpinmC1rS+6CfslwuL5wo1MbWGtjU3t06L+n94TE2gzWGk8IndFMl2jpWSCdh0c7jsTCt/jzP4ezsDE5PTze+p/Xw8gC6r0G/k+JxKXFRnNN4hcbp6Sn85je/cfxM7Uv+DpTmMrC/vw//8l/+S3jy5An8/d//PSyXS1itVmo2nqLg8QntVFrsHb4hlBWn+bbpzG2+D8ZxcC5+/vnn0Gg0YH9//8JJSQTOCV8WKYC4OVHUX6J95vP1sC20BQBk+9wnK2No1caR6zhffZwWTjddlwjR5bN/qJ6T5rl1cdPXNqWb1kvvns+yF2n2sU7tqgoNmh8V6mdevmpodi/lT4Crk+3suwbR/qR/WI28MgL5dEIUwWWuoGvwGXlSmdAE50KOB7lilZP2nWQES59932ltxNIXE7BNwVV3UmPak/htm4jtyxB92wq4WpWp5fnYOmL4XJKVobmnBThiAzZF5wnufqQLkWh07+zsQKPR2EjFGDLUsFy9XodareZS1R0eHsJisYDxeOwWSMfjMZycnFxIFVdW8BzrxPfY29uD4XAIk8nEBY7ond9SKjGOFJmu1WldNOeBKlputVq5RVMe1MLPXH/wIBPtJ/yNB15i53mtVoP9/X3odrviglds2kKLo8r1uEY3n6/tdht6vZ67xwqDIJQnuJEsBQKL8Ebqc2Xo4RSbCZ/j/TCZTFy6KyudyAeTycRELwXKrVDQ2AKf3WYJ9uEzRfSOVKcErR1OR0h3xOj6qw46fjxrTcwY0JPxi8UCJpOJC9b6grC+YNBl92PqHI8F3QSkyUP8Hm2BxWIhbsjRbI7L6k/erpWGsufZVeCnEK9we0LzkaU6Y37z+cshGn1BUclu4jaABTE2gWSzlYEiQWRfUBl/52Ptew5gM52wFkyW5EeWnS8gYJCWbzgsK2bAwcdDs/V88RmKmPGw0of18nZ4OV+sS/NNpTGS3l/qF3rKlJfT6PP1D/1N4o+UmBelZblcbmRL0k6FavXHzDPuW9XrdTg4OIDpdAqtVmujLt/dvbHgcxfBF+G4bU9p5+/CyxRByH6O0adVyQQrYmS5Zluk+DCx7z2bzdw96o1Gw8lZXhf3dVPaioHlfWLb98mQUJ1cxoTmoU82xdgpsbEP6fcQPfy3GBkT0kFa/AB/o+/IN2lJz/B2+W+Svg7pPem5KuGzI+jvVtuRP1slNF4us36tztBcKwqt/uQ7YylSjfsqgki07ioYhjoG0mm1GKDTQO/Q4wYMP5WkIUVhUGDQ0fdOVfXptnDZAYZYxAa4Xpb3szqUFEVlha9vrHxdliLiypk7L1mWubQuPno1uUAXPlFeLRYLmM/nXpq0YFDs+yJ9Ozs70O/3L9Q5mUxgOBxCu92GO3fuwOPHj2E2m11IKUxpo9jZ2YGjoyPI8xwePXoEu7u7MBwOYTwew/Hx8YU50ev1XJ/E7IQLOdi+HcXj8RgGg8GFvrtKO9FQb/sCgMg39GQIdaQANvkWYNOxr9Vq0Ol0LvQlLla3Wi21fZrSWEO/34d3333XlcO01ev12gXhLbDITjpXkVelQDC2jWP9/PlzqNVq8O6770Kv19vQ9/QZpDVEi+SwfpdBZWFsn3AHDeuLAT0tKwUBY2lBGmLGuExHgTvLZUEL0vnKXjX4sjaEgDw2m80gz3Not9vw7Nkz+NnPfuZkwmQygTzP3SYjPg5XtV9CKMNmyvPc2TB8gxcth3cK/sM//AMAvLizDP2t2J3hWnBfo5P7UUUQExguUkdMfUUh2eRS4MxHB27Gm8/nXruWt4v2C/rw3D4oY35RO4i+R2wAktLkC6rFoCq7wRrUKwIcL2zHmu4dQd+70Wg4v8jng1oDl8hX2snVmKAn/lutVhfuS+aQTiRdhl2I9FL5GsMTPppp8Bz9SgTqAb6hl99JG2ojlk5L3IL66Zo+oJk1UL/h5l6EltbXp2OazSbU63WYTCYb93cCnGcVolmEHj9+DGdnZ967elNBZTmeAkb9SDNi0bIWpMqbovLvsuyv2IUDDWXI/6J9kOe52+CNm72rwmX5ydSPSnmOn07VYFks5OV8dVgWXH2g+khboLTYxpqcpf3he1/JzsdsV/R5GovxyWmLDR7rN/P+KSu+TBGyC1L59GXHVYydmRdjQ4HpqsGFkjT5eHkNFqaX2tLaxHpCDC0ZzbxuSfj46uNlNFosAt1Xn4+W72JgeFtB0lSHu2hdRRc/tXpiAku+eRqzeKrNX+u85bTEyBoLJDmgQXMsQ3LFV19s/2rBIS0I0Ww2od/vQ7fbhW63C9Pp1BnhdNGKnl7V2kXHFO/ifO211+DWrVsA8MKYOj09heFw6HYgUwM3xhDk30k6aGdnB3q9Huzu7kK9XodGowHtdtvdg6K9RyysPGqt2ycj0KDGsUEDWhtf3rY05+v1utuNjXVzZ1yiDceOBwpWqxWcnZ1Bs9mEJ0+euN9Go1G0XvbRwIMrIXDjlgZjMMBy//59ePz48cYCt9QOpcEqF64yrE4fAucslVGLxcLd+zqbzWC5XJrnNAbhcTMBlTuULk4bPoMnF1EOpQQby7STQm1fJZuM9utVoisWnIdjnG/+HJ7Yns/nhWTVtwFc/mkyj/aTFPShsh8X6RqNRrIN6aMz5BNTmjTZR+WCZnfw52Lnz8s033y+JI43ZpXA++L5KXUfQj68RoNk/1nfx/J9ihzR8DKNtwRNr6IdqKWG5wFRX2yF2pUALzZUoW0gbbin4LLfYgtItiGlj9eP/2Ma7nq97mxJ38a9GD7yxdF89n6M7WGdN1QGWvxPWjaUwrksHyCmLsmOXK/XMJ1OoV6vb6TM5KDvJvFziAbkGyonEc1mE27cuAGz2Qym0+mFRQSaftryfj55zX9PiUFpsjiljtS4XVGZyudAjC4pU56XUZe1/zX5zH/f2dmBZrPpfDnO72UgJFc0uefzwSUdHsOrGm+WYWeUZafE0KPZqpZ+kurhtn1M+5q9xu/QtlwzZW031Z+gdIb8FEmWarYQfd7nW8TQLflZUntlQ+sDX/my7WCLHZBqKwCUtBj7bQffZaflgg+BG4tSbvzQnSBIi+8+WMtdsbys7266lyno97Ij1Vi8qrDyVKrBHtuuj4YqEDLG0FnyOf60PCoZaUc4n8PavRS0vjIWDLVAI2J3dxd+/OMfO9k1n8/h+fPncHJyAqPRyL0T7lKTUsuiEYVpaZ8+fQpHR0fwH/7Df4Ber7fxXr/+9a/hV7/61UZ/5HkO0+k0KaCJCznSYm69Xod33nnHLcjWajW4desWLBYLePbsmbmNbcI3trhoulwu4ezsTDSkObAvfYvd3W4XXnnlFfc3pue00IT3qWIbAOenyz7++GP46quv4OOPP3Y0PHr0SKyjyFzHOeybnzgntftwEMPhEP7X//pfMBqNHH82Go0L5WNOcF+l09YhxASdMfBO+2IwGMBgMIAHDx4AwIt+D51Y3NnZgU6nA61WC/r9vrtrjJ+8lvq91WpBr9dzpwyazaYog0NjX5WdVIYeq9qGqyLAsm3keR7cxR8TPLjGy41Qqmpua1HZR2UF7ta3ptR/WVAkAC3VhTp4uVxCq9VyJ6ABXtznXUbQXDpBzQN3seMU8oOWy6ULSsfQinVrQTrLs1cRkhzNsszZpvw0NB0vHD96TxzyDQf+hvW1Wi03H2naeA6LLpDAF3lDcwJt6k6n4666WK1WMB6PN9IxSxnNUoLYMbBmI5EWMrRy0sYKy7PSCVlLe7wta3uxcoBu6Hv8+DEcHh5Cv99X6+Yp2PGUtHWskJ8xKxFm4wA498e///3vw8OHD+Gbb75xz6A/0m633TNlAHkc+UXLdlS2LEqVcTyeEZs6XoK0SPddAM59lMcS2u02tNttGAwGMJ1OnVxFP0uq8yraSThXtTtYsQz9v0w+4PZlrE3gq7PIqfkyx0tb8AV48c4oa+hVUPg91em1Wg3a7fZGfbF6JwacP3g71jUkqz7lbXOUwR9XFS+jfA0uxpa1SMIHO3XQqXCXwJ1erUyITtoW/Z7Sb32HUBDXUs5yaoz+r7UjGZ5SX2qT17IwW9Tp9tVdFNsSNqm7ZELvGBq/bbwfH98Y54XWwRWftqskNfAh1VemDEstg+Bzmn7miluTN1RmaAaTxciI5aHQ2GCaJHpf6Gw2g9PTUxiNRsG2OI24M5y/IwZThsOhOy3LgxM+SPKKf5YWXfB0LqaUqtfrMBwOYT6fb/CyZaGIoor56+tjSldssNHCa7VaDbrdLrRaLciyDEajEYxGI3dqmZYPtUnTDlODG0+XTSYTt8BbpRwMzRV+X/BgMAAAgA8//NDdGSUZ1dKmhipSh20bKTYTgs4/ypvSqVapTV5XSMeiLGi32xsyxwp6ggWDaD69Y7WLY/rNYkP4dIBVJlv1aUoAwNfuNhDSlyk2kAUYVJZSEWrzh9vlKYEefJ+iNtJ3CbS/Ung8JaiCz74MCAWgL+M98LSYBXxsfTKc+hxUlmsBL1/8gtdDv5d+01A0wJYiC0K0WfqSPk/ve7bob9oO34zJ66bzFzfGD4dDd9UFbpJFu157FwtiZHLIvkR6d3Z2NrIqpNIW8s1D9Urv5ntfOhY00E/HVnve1waXqbG+TBm2V2ickcbJZALr9dr5Rf1+H1qtFpydnW3QRPtJsosku1FrezabwWeffQZHR0fwve99D2q1Guzt7bmF39FotLEZ0SpntPGXYptZpm86Cs0NiTfK0CEhPrlq+laiN8Xm89UXqjMmFkbrm81mzndHOYu/aXG7okiNH2r9wctZ2rCUtXwvyUNJBlhjKj46pXpCvjOvxzJXfXLcaoNI/UKvoqBxSJRBmCqdx8Akeevr9xAtljJSH2iIkcm0vZC+LEvOVW3nF5F1ZaKILSf1ycZirKZML1s4IiyGeErAT2vLd19dTDvoGPjaCtWHJ4O01I4AcSdksC1poSBES0gIVTlJQo6szyD8NoLfJ6Lt1k3tB6kPU4O9qe3HoCrauOzBOcLr4ounmkLPsuzCaT/6HAbzfYqS1y+dsKNlfO9t6TcrsizbuBsT6z47O4OPP/7YXA9tGxd3JVpmsxk8fPgQ2u22ux82BtwYo8C6aBlMMXV2dgbr9drdHfv48eMLMrjRaIgZEFKh8W+KzuN3leM9bBb4ZD3N7tBqtWB/fx9arRYAABwfH8NXX321QbeV9ul06oxnrrfG4zE8fvxYrDfk+MfIBLo4St+TPr9arTZ2lT99+hSePXsGn3zyCeR57hb4pJOUdO4D6LvIvwvAcZHu2LU+T+uxPlOr1aDf77tnYhZj6/W6uxsL4HwhHjdtpMKXtSQFRQI2HFUFqq6KwwUQlh+0XBF6McjebDY37HxqA1iCvZbArEZ3mfbTd8H+pigrOLxNXKbPlBowDMEXACkrPkDboYFDjhT/CYOGlGbrs0XAaQ0FXmPfKwVoR+GiFe2bEKgtrgUd8btWqwWr1QqePn0K/X4fjo6OoNVqQaPRgOl0CpPJ5EK7WgDVl7XIGsDDBQsNzWYTVquVu2PcumkvRnYDhG0f2pfWcZH86FAbtO9874DB9BjwuVuVDKRtDAYDODk5cfr+jTfegDzPXaYoBE2/zHnIwicUw+EQfvrTn8L7778PP/nJT+CNN95wJ2dXqxV8/fXXMB6PS3nPUBD6ZdKT0sLGVcZVtbvyPIfxeOwWwTqdDhweHroFMwpJD2n+nHVxKqZPeJ0+Hcx/S11A5L9ZZZLUF2XbDfQ0qcUukOyhFPtYq8fyHGZKWC6X0Gg03EZpGmOhm6xQFuN9xvR9YmkLrZH4fkOZHlqDQb2v0eJ79irKh287QmOkRrmsRjnAdgaX7p6rAiFBRxVGUYcOBZoU1EXwRbbQnX34OVY4UFicVd5GjNIp0mcWQ3wbzqFWbxnvZkGtVttIZUX5ogzlW4bhqckO5B1tESNES0qboXo1AWkdY/oeVoUoOc7UsQo57qF5SvmBntLzBSP45xSEeKfZbMLBwYEre3p6CgAgnnjVMJvN3Pssl0t49uwZNJtN6Ha77lQsAu+IxD7Vxi8UzMDv0ZjDk5iSEzwej+Hp06fuNCRtJwV8bvucFCvoc8izvr7R2uIBLrrrsFarweHhIbTbbW+6P6nPaRnUu8jLACCONX0f6/tLf0tOWJ7nLn0znsSV+ozSSO+D0mQJ7X/LWNL6ywwqa21J/eKjswp9y+vE1HztdnsjQMXp5NjZ2YF2u+0WdjE90WQygcViIW5kwfnOF8RxwxOmR8dTDnQBDe9EDqWbljIb0FMEl+U0aTqiKM9pevJlCs5pCI0Vvjc6+5jRYTabOZ9AOoGV2l4V9RQpq8lDn52DwN3tR0dHAHCehnG1WomL1vhZSvkqBbl8tPLAOOomjU7p7yIow7e2BvKkvy8DVDdSUN1TRh+jfUhldBmLBVLwmP8WW5cEn31B+TzGH6LPxPCN1raFVl/9WlBUigfxtLlWX9ZKE88cI9n/IVkW4ivJNvfZCFIbWE9McLYMlBF/8NHN7RA85c7lvubTULkSmjMWSPan9JmetpLsWby2B2Bz86VFL/p+Rz7BKzpWq5XLSETx1ltvQbfbhbOzM7cYm2Uv0nvHXoMSO8eLIGXMrHbaVUHIbgr58byuKu2J0HMUi8XCnQTH+EGv13OZo0JprRHW8YrxWa3+uCZLaB9x+kJ2cUxch/OAT/ZZoNGCkOSd7304PbG6WXqG1++rD/0q9LuwrNQfNK5H41nS2PH+ln7T+lzqD/7+VGdI7yfBxzchP4ePY4yOkb6X6LLW/W2Gz65OOnKgDbZm4PBnKEFWJgsFpiThU4bjQydDrFGivRs6gQD6PXt4lyTWs1gsLjgbFqMiRrBbnVxLu6koMxgQw2MW+PijaD2+7ykwqIyYz+diAE+6ryAFZRhvUp0UoeCWhG0Ejfj88Rk5aDBaHEm6u4wqNbobnv/NEbrjGZ/BxVirgyvVZYFFJzSbTdjf33dl8cQoptyiNGntzudzF4BdrVZwfHwMu7u7sLe3d4EOTPnoWxDRjBEJWN9sNlPv05lOpxv38PB2aJ+EDH3pM0JzzEKQDCNt7DjPS21yY5QGrG/evHnhVDAvo9FIHTC6AJplGXQ6neTTkhI0Gcd5qdPpwGQyUecTDbhY7oGm/Rarny3zuShS9WaVxjfKvVarBXmeu3mo8ShiZ2cHut2u62s89TKbzdzOWckhkhZ00KnDVMYAm3IJ4DzQIAW+KELz2iKT+DOh9rahNyVQm1Xj95fBSfP1oRSckd6J3m05Go02spnwzSs+xNr3GmL4IrVsSJ/x5wA2fUfchHHjxg0AOM+yQdPSA7zQz4hGo+F8J6mvtAAO/50vhvg2y1n52ho4KWO+hvxmzq+XJSMoDfR/gIt2ahmygtrs0ma9IjEEqz63BCFD9Wl8bIEvYBh6jgeWUmSRZCuE3pvakDzYC3DxLmdaryWWpMGyGOuDj28l+4X7nvhdltlOQ/rsypT3t/qKId9EmmeSzPfVLfknvudDSOkPLqd8cR1tzDD+d3x8bLrSJtRPUv0A53bFcrkU/dbXX38dXnnlFfjpT38KDx8+dN/jxmMrn2vyWgKlP1Q2NDcs/pNkm8WA+2qXqSN5f0jz0joX6XO+uSf1fVEsl0sYDoeuzn6/D+1228W6Q1fRUFh1ZRnQ+pbzP+e5GF61tm2hxQqJr32yGuDchiqrvy08a7WrAF5cEYbp/C0bo33ZNKx9HEOvxhN0bsfaDbHtSb65VbdZZBFHWXM19t2vCjR6vRHNKl8y1cnxQTLOUuqWAkYxBgmtjy6g8hOxFPV6HbrdLiyXywt5zCkwEECDDinvzemwvN/LECyj4ALtZQU6ga1Wyxkmq9UKlsvlhV3dWs77VBSRAWUFk2Lb5JAUnc9x8oH3rXaiMuQsZdmLBRscR7rhgpf1jaV096y1H6pAnudwdnYG3W4Xfvd3fxf6/f6GvHry5Ak8efJkY1ckgCyD8HRalr24a6bRaMBv/dZvwf7+/sai3FdffQUfffQRPHny5EI6WY1ODXinCS7GcLm+s7MD77//PhweHrpUKO12G5rNpgsWYWoUCouTKgUjLc9oTqJV/mE5DHxT8D7EYJTvlCtdnMK+9O1yleqRDODhcLjh7JycnHjT94egtWfpM76xQgu45Pl5qm2aRk0LOCEdNIBjleeXHSioCtgfmFZtsVgE+wP7AU++HhwcwGKxcAGAEHAuYJrC5XIJzWaz8Aany0KM7CmjrRRZdtXtNF8fXoatU8aYxdBdVdnvEui44b2BuBlkvV7D2dkZ5Hle6oYjHy3a31dZl+T5iw3K1N610IsLDFJ2Dakd+r+vHdTtsdcFod+2Xq9dOtx2u+1OavDyPn+Dy4SQH2KhL9ReleCbiqXAPA8iWvp/uVy6De9Zlrn5hwtQZ2dnsLe3t7GZEDfRlD0vLUHF8XgM8/kcdnd33eZVyxUWlBdirkoIBfuxvpQYGdZL5xZmPqD+BM/opPm0Pjtaa5silYclXwHnMv1Oqh8XCWhKTA30NHTMogDACz6n/vPp6Sn8/Oc/h3v37sFrr73mrpDBujBTR7fbBQD/1WgSLDJ1W/KDt3eV6rpKiBmzMiCdup5MJu4O7JBs29Y4cBknyZpYm8lis9N6rAtjEt2xawIhWjhNXC9LPh+1zzSaNHvFFwvyAeNvuJEfM1zRuEGn04E33ngDBoMB3L9//0IdrVZr4yq5lBiThWZpYVR6H03Xxc4DyzNoj15jexCtSj4BUgx5qxNgCX76FtW4oCxD+NBAaMrCLhVGyPhoaHKDGNuq1WruXhQagOXvJU1ciU7LYpDv5F3M+4Zw1QNsGspS9inCkj6H/EFPEGIwQnumrFPcPvh4DPlaMihSjfGQErE6KViPZERpdSDdXMaE5pEv2FWr1dyCFhoNVnAZTWkocj9hLHgfYh9Mp1NoNBpw69Yt6Ha7G4GS4XDo7vfkRhqX5xgw4WlrXnnlFdjb23P9tlwu4fj4GD799FMAuLiLPQZ0LtHTE5S+LMvg9u3bcOvWLXj+/LkL7qHxRjMbaPXHwDJPynQ4eX0xcwm/xwAL1XGUN6Vn6Zhpiw3osEm7GH2BQyu0wBP9h3M1NNdokBDTcUtpsyW9Ljk2/LOEbTiqlrlVpt7HscXgKD11GnpXHCtcyEXaQrQjjy0WCxiNRgDw4jRVKK055+nU8SgrqL4tSPquSppTeV2SL9vqWx/fWMv6yl11HrHC9x6absDnXpaAKdo4aNvT+wEvG0X6UBuDsngT7QtNb2o0UP/DyicxvrWPL0Nt4Ljz7EdS2xZowdzYejRbLBQcTuEf2gaXab60wDF0AbyImaBNiid8cA5Op1Po9XoAsLnZ1SdvLDGX0Hfas3jtQbvddgvHsTZBTHn6vtJz0lzj78F/840H961CdVGE0lNr7fF3iZmzlFY6v7R3lmSIJiuk+A/nP58/RunCf/QE23g8hm+++QZarRbcuHHDxZboffU4F3h7lJZUm7Sobpb6LMYfCcmrWP5JQUhGbJOWIkjhASq7qExfLpewXC5Nm6SwTSmGFwNtnmoxCUtd2nex9cS8j6bbU3nD+pz2fpIMo8+k8LfmR2t2GM1ig/KPbn5pNBpwdHQEtVoNHj165L6ncUa6GIvPWvSZ9Lv1XSUf2ldHiizWbGPans83tvCl5f1T+ogjhbariAvR4qv2IlJKT46URVMOzvC0viKOsS8VI6bN4+1T0BzmACAqKm6cVDGG21b4VoG3bVQd6OHjqPEPvy9LWujH7zUnsipIShOdaUpH7KKhr9+tQt23s1oCBn18dfK6tMABprXEtqSdVvwuJGnsfLTS32hq16rHH4MaeKcrxXw+h+PjYxgMBvDs2TO3sIEnR6UxoYaRtkg9Ho/hn/7pnyDPz9OOnpyciOX47kZaP+9vNN663e4FumazGUynU3dydDAYuMAI38mp8YBGW8jJ9iHFGaY8wVNES46IlYd4YKrRaDiHn5fDIEGoPp6yiPdryuYDqxzHjBX7+/twcHAA0+kUZrMZPHjwAKbT6YVT2xJddFFe44kUB6oMp+u7iuFwCMPh8IIMxvEcjUbuxBoC+fDg4ABarZaT5ZgyeblcQrfbhX6/75w3X4p4DVwmlG1vFAlaVIVU+7VI31CHPba/y7IDMQCKQH3HN3jRE4DSfYmXgSptYT4WOEZoM6DeOD4+3niO2l+Y/rlqObkN+8qHouNQxTj6At60r7bVb5J/gCfvyrjKBW1C6SRNiqxFG5lCW7yRng/FTIospEi2dCgmUQZoMLWMDach+lAWHx4eQr/fh8FgEK3T+UIhhWWM0MbQ+DSlj61jHwqkcxp8fpxVHmjg9gH3l4rIL98YFamP6nDtHTE7Fvc/JVr4aVrpNJpGA8VgMIAPP/wQhsMhnJ6ewu/93u/BG2+8Af/+3/97ePLkCfzX//pf4fT01JW3BNHLQNWxtbLwbQn+S6hSH/Px5dfAoD6OyUBA6yxzHDRe9LVBsziEFhBTFlktv1fBi2Xr8jIW4EJYLpdidjvpSr+DgwP4nd/5HVgsFjCfz+Hhw4fu/my8xxg3yC2XS/e9FLuqWoZVEc/H+q6CT/myInbcpfIb0k4KwlqQ+pylXs4g2s4Aa32h7630Wxz8UF38jiMJNKgv9YfWlmZwW97PtzC9rcma4rzE1KcZmNyR1fo1tr2UsiHhaF0ctJa11mUpJ5XlAc8yeCklCBD7TFmLI/S0pG8DicWJs7TP+1qTnTH87pN1Gq/l+XnqI7zfmJ4qDN2168NqtYLT01OXhkS7x5W+h8+xpL/RRWIaEKcBXkwpL903GQPqiMfC129FdUAZMgMdriLBM2ospura1PI4ZxuNhkvnReeTjy66eALgH19ax7fZIK7SWYmpd7VaifIC68B7ZClwvCkv4IYCajug7CjDBqgCRdsq06bQ6uafq4BWv2UelkUb14F0AxW2QwPPNAB72dBsPJ89kQo6Jthf2hUAfAGb01pVYGmbC7JSEDK1vy+Ll3y6fBuL5mUuCnM7wCJD+HjRcUQb+SrM81hUOSeK2EipeqvZbEK9XofJZLIxNkXakcpo/pclZaB17nNZEfKHYnyF2HkbyycpbcSA1l92/EmDFAeJiTtJfRiiD+/qPDs7g9PTU1gsFlCr1eDu3bvu5HXsO5SBkD1RVp0xzxUZ6zLr2BZS52Rs3fQ7aU5v05ayIjaGhH/z2JgUZ76qqErmISyLzamyGBdjaRxPqguzAsxmM5jNZk7+0fuy0e9A/ywlDhU7t/g7Wp4vMl5VxmYkv9A3j+hzLwNi7HytfJa/LG97jWtc4xrXuMY1rnGNa1zjGte4xjWucY1rXOMa17jGNa5xjWtc4xovEbZ3ueA1rnGNa1zjGte4xjWucY1rXOMa17jGNa5xjWtc4xrXuMY1rnGNa3yHcL0Ye41rXOMa17jGNa5xjWtc4xrXuMY1rnGNa1zjGte4xjWucY1rXOMaFeD/B1pzCcVDFr3SAAAAAElFTkSuQmCC" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "fn cast_ray(\n", - " orig: Vec3f,\n", - " dir: Vec3f,\n", - " spheres: List[Sphere],\n", - " lights: List[Light],\n", - " bg: Image,\n", - ") -> Material:\n", - " var point = Vec3f.zero()\n", - " var material = Material(Vec3f.zero())\n", - " var N = Vec3f.zero()\n", - " if not scene_intersect(orig, dir, spheres, material, point, N):\n", - " # Background\n", - " # Given a direction vector `dir` we need to find a pixel in the image\n", - " var x = dir[0]\n", - " var y = dir[1]\n", - "\n", - " # Now map x from [-1,1] to [0,w-1] and do the same for y.\n", - " var w = bg.width\n", - " var h = bg.height\n", - " var col = int((1.0 + x) * 0.5 * (w - 1))\n", - " var row = int((1.0 + y) * 0.5 * (h - 1))\n", - " return Material(bg.pixels[bg._pos_to_index(row, col)])\n", - "\n", - " var diffuse_light_intensity: Float32 = 0\n", - " var specular_light_intensity: Float32 = 0\n", - " for i in range(lights.size):\n", - " var light_dir = (lights[i].position - point).normalize()\n", - " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", - " specular_light_intensity += (\n", - " pow(\n", - " max(-reflect(-light_dir, N) @ dir, 0.0),\n", - " material.specular_component,\n", - " )\n", - " * lights[i].intensity\n", - " )\n", - "\n", - " var result = material.color * diffuse_light_intensity * material.albedo.data[\n", - " 0\n", - " ] + Vec3f(\n", - " 1.0, 1.0, 1.0\n", - " ) * specular_light_intensity * material.albedo.data[\n", - " 1\n", - " ]\n", - " var result_max = max(result[0], max(result[1], result[2]))\n", - " # Cap the resulting vector\n", - " if result_max > 1:\n", - " return result * (1.0 / result_max)\n", - " return result\n", - "\n", - "\n", - "fn create_image_with_spheres_and_specular_lights(\n", - " spheres: List[Sphere],\n", - " lights: List[Light],\n", - " height: Int,\n", - " width: Int,\n", - " bg: Image,\n", - ") -> Image:\n", - " var image = Image(height, width)\n", - "\n", - " @parameter\n", - " fn _process_row(row: Int):\n", - " var y = -((Float32(2.0) * row + 1) / height - 1)\n", - " for col in range(width):\n", - " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", - " var dir = Vec3f(x, y, -1).normalize()\n", - " image.set(\n", - " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights, bg).color\n", - " )\n", - "\n", - " parallelize[_process_row](height)\n", - "\n", - " return image\n", - "\n", - "\n", - "var bg = load_image(\"images/background.png\")\n", - "render(\n", - " create_image_with_spheres_and_specular_lights(spheres, lights, H, W, bg)\n", - ")\n" - ] + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" }, { - "attachments": {}, - "cell_type": "markdown", - "id": "766de19f-200c-4dce-8678-b36cc3c3dc93", - "metadata": {}, - "source": [ - "## Next steps\n", - "\n", - "We've only explored the basics of ray tracing here, but you can add shadows,\n", - "reflections and so much more! Fortunately these are explained in [the C++\n", - "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing),\n", - "and we leave the corresponding Mojo implementations as an exercise for you." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] } + ], + "source": [ + "fn cast_ray(\n", + " orig: Vec3f,\n", + " dir: Vec3f,\n", + " spheres: List[Sphere],\n", + " lights: List[Light],\n", + " bg: Image,\n", + ") -> Material:\n", + " var point = Vec3f.zero()\n", + " var material = Material(Vec3f.zero())\n", + " var N = Vec3f.zero()\n", + " if not scene_intersect(orig, dir, spheres, material, point, N):\n", + " # Background\n", + " # Given a direction vector `dir` we need to find a pixel in the image\n", + " var x = dir[0]\n", + " var y = dir[1]\n", + "\n", + " # Now map x from [-1,1] to [0,w-1] and do the same for y.\n", + " var w = bg.width\n", + " var h = bg.height\n", + " var col = int((1.0 + x) * 0.5 * (w - 1))\n", + " var row = int((1.0 + y) * 0.5 * (h - 1))\n", + " return Material(bg.pixels[bg._pos_to_index(row, col)])\n", + "\n", + " var diffuse_light_intensity: Float32 = 0\n", + " var specular_light_intensity: Float32 = 0\n", + " for i in range(lights.size):\n", + " var light_dir = (lights[i].position - point).normalize()\n", + " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", + " specular_light_intensity += (\n", + " pow(\n", + " max(-reflect(-light_dir, N) @ dir, 0.0),\n", + " material.specular_component,\n", + " )\n", + " * lights[i].intensity\n", + " )\n", + "\n", + " var result = material.color * diffuse_light_intensity * material.albedo.data[\n", + " 0\n", + " ] + Vec3f(\n", + " 1.0, 1.0, 1.0\n", + " ) * specular_light_intensity * material.albedo.data[\n", + " 1\n", + " ]\n", + " var result_max = max(result[0], max(result[1], result[2]))\n", + " # Cap the resulting vector\n", + " if result_max > 1:\n", + " return result * (1.0 / result_max)\n", + " return result\n", + "\n", + "\n", + "fn create_image_with_spheres_and_specular_lights(\n", + " spheres: List[Sphere],\n", + " lights: List[Light],\n", + " height: Int,\n", + " width: Int,\n", + " bg: Image,\n", + ") -> Image:\n", + " var image = Image(height, width)\n", + "\n", + " @parameter\n", + " fn _process_row(row: Int):\n", + " var y = -((Float32(2.0) * row + 1) / height - 1)\n", + " for col in range(width):\n", + " var x = ((Float32(2.0) * col + 1) / width - 1) * width / height\n", + " var dir = Vec3f(x, y, -1).normalize()\n", + " image.set(\n", + " row, col, cast_ray(Vec3f.zero(), dir, spheres, lights, bg).color\n", + " )\n", + "\n", + " parallelize[_process_row](height)\n", + "\n", + " return image\n", + "\n", + "\n", + "var bg = load_image(\"images/background.png\")\n", + "render(\n", + " create_image_with_spheres_and_specular_lights(spheres, lights, H, W, bg)\n", + ")\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "766de19f-200c-4dce-8678-b36cc3c3dc93", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "We've only explored the basics of ray tracing here, but you can add shadows,\n", + "reflections and so much more! Fortunately these are explained in [the C++\n", + "tutorial](https://github.com/ssloy/tinyraytracer/wiki/Part-1:-understandable-raytracing),\n", + "and we leave the corresponding Mojo implementations as an exercise for you." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" }, - "nbformat": 4, - "nbformat_minor": 5 + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } From a91a17156daa8354fa5b97652d52c0aa6ba5872d Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 7 Dec 2024 21:26:47 -0800 Subject: [PATCH 1998/2019] [******][GPU] Add log log2_floor in the bit module MODULAR_ORIG_COMMIT_REV_ID: 78e0005db0bb77465e11d6ce856da56116c839dc --- stdlib/src/bit/__init__.mojo | 1 + stdlib/src/bit/bit.mojo | 22 +++++++++++++++++++++- stdlib/test/bit/test_bit.mojo | 27 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index e41c3ca52c..75004d3618 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -21,6 +21,7 @@ from .bit import ( byte_swap, count_leading_zeros, count_trailing_zeros, + log2_floor, is_power_of_two, pop_count, rotate_bits_left, diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 74a428d3df..fb4170f825 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -343,10 +343,30 @@ fn bit_width[ return bitwidth - leading_zero +# ===-----------------------------------------------------------------------===# +# log2_floor +# ===-----------------------------------------------------------------------===# + + +@always_inline +fn log2_floor(val: Int) -> Int: + """Returns the floor of the base-2 logarithm of an integer value. + + Args: + val: The input value. + + Returns: + The floor of the base-2 logarithm of the input value, which is equal to + the position of the highest set bit. Returns -1 if val is 0. + """ + if val <= 1: + return 0 + return bitwidthof[Int]() - count_leading_zeros(val) - 1 + + # ===-----------------------------------------------------------------------===# # is_power_of_two # ===-----------------------------------------------------------------------===# -# reference: https://en.cppreference.com/w/cpp/numeric/has_single_bit @always_inline diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo index dee81507d1..1f1063eee0 100644 --- a/stdlib/test/bit/test_bit.mojo +++ b/stdlib/test/bit/test_bit.mojo @@ -25,7 +25,9 @@ from bit import ( pop_count, rotate_bits_left, rotate_bits_right, + log2_floor, ) +from math import log2, floor from testing import assert_equal @@ -497,6 +499,30 @@ def test_rotate_bits_simd(): assert_equal(rotate_bits_right[6](Scalar[type](96)), 129) +fn _log2_floor(n: Int) -> Int: + return int(floor(log2(float(n)))) + + +def test_log2_floor(): + assert_equal(log2_floor(0), 0) + for i in range(1, 100): + assert_equal( + log2_floor(i), + _log2_floor(i), + msg="mismatching value for the input value of " + str(i), + ) + + fn _check_alias[n: Int](expected: Int) raises: + alias res = log2_floor(n) + assert_equal(res, expected) + + _check_alias[0](0) + _check_alias[1](0) + _check_alias[2](1) + _check_alias[15](3) + _check_alias[32](5) + + def main(): test_rotate_bits_int() test_rotate_bits_simd() @@ -519,3 +545,4 @@ def main(): test_pop_count() test_pop_count_simd() test_bit_not_simd() + test_log2_floor() From 2b9db22697af4899b4b2df9d18e006b28c3de7f8 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 7 Dec 2024 23:41:55 -0800 Subject: [PATCH 1999/2019] [mojo-lang] Add support for named results using `out` syntax. This adds support for spelling "named functions results" using the same `out` syntax used by initializers (in addition to `-> T as name`). Functions may have at most one named result or return type specified with the usual `->` syntax. `out` arguments may occur anywhere in the argument list, but are typically last (except for `__init__` methods, where they are typically first). ```mojo # This function has type "fn() -> String" fn example(out result: String): result = "foo" ``` The parser still accepts the old syntax as a synonym for this, but that will eventually be deprecated and removed. This was discussed extensively here: https://github.com/modularml/mojo/issues/3623 MODULAR_ORIG_COMMIT_REV_ID: 23b3a120227a42d9550ba76d8cafb63c3a03edcf --- docs/changelog.md | 18 ++++ stdlib/src/builtin/_pybind.mojo | 12 ++- stdlib/src/builtin/coroutine.mojo | 16 ++-- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/collections/deque.mojo | 4 +- stdlib/src/memory/unsafe_pointer.mojo | 15 ++-- stdlib/src/utils/index.mojo | 103 ++++++++++++++++------- stdlib/test/os/path/test_expandvars.mojo | 2 +- 9 files changed, 120 insertions(+), 54 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3673d58fd7..0576359789 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -335,6 +335,24 @@ what we publish. release of Mojo, but will be removed in the future. Please migrate to the new syntax. +- Similarly, the spelling of "named functions results" has switched to use `out` + syntax instead of `-> T as name`. Functions may have at most one named result + or return type specified with the usual `->` syntax. `out` arguments may + occur anywhere in the argument list, but are typically last (except for + `__init__` methods, where they are typically first). + + ```mojo + # This function has type "fn() -> String" + fn example(out result: String): + result = "foo" + ``` + + The parser still accepts the old syntax as a synonym for this, but that will + eventually be deprecated and removed. + + This was [discussed extensively in a public + proposal](https://github.com/modularml/mojo/issues/3623). + - More things have been removed from the auto-exported set of entities in the `prelude` module from the Mojo standard library. - `UnsafePointer` has been removed. Please explicitly import it via diff --git a/stdlib/src/builtin/_pybind.mojo b/stdlib/src/builtin/_pybind.mojo index 24b0fdc619..5e1d8c874c 100644 --- a/stdlib/src/builtin/_pybind.mojo +++ b/stdlib/src/builtin/_pybind.mojo @@ -61,10 +61,13 @@ fn fail_initialization(owned err: Error) -> PythonObject: fn pointer_bitcast[ To: AnyType -](ptr: Pointer) -> Pointer[To, ptr.origin, ptr.address_space, *_, **_] as out: - return __type_of(out)( +]( + ptr: Pointer, + out result: Pointer[To, ptr.origin, ptr.address_space, *_, **_], +): + return __type_of(result)( _mlir_value=__mlir_op.`lit.ref.from_pointer`[ - _type = __type_of(out)._mlir_type + _type = __type_of(result)._mlir_type ]( UnsafePointer(__mlir_op.`lit.ref.to_pointer`(ptr._value)) .bitcast[To]() @@ -162,7 +165,8 @@ fn _try_convert_arg[ type_name_id: StringLiteral, py_args: TypedPythonObject["Tuple"], argidx: Int, -) raises -> T as result: + out result: T, +) raises: try: result = T.try_from_python(py_args[argidx]) except convert_err: diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 04688695c8..c2c0ee1d81 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -137,7 +137,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]: __disable_del self @always_inline - fn __await__(owned self) -> type as out: + fn __await__(owned self, out result: type): """Suspends the current coroutine until the coroutine is complete. Returns: @@ -150,9 +150,11 @@ struct Coroutine[type: AnyType, origins: OriginSet]: __disable_del self __mlir_op.`co.await`[_type=NoneType]( handle, - __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(result)), + ) + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(result) ) - __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) # ===----------------------------------------------------------------------=== # @@ -220,7 +222,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: __disable_del self @always_inline - fn __await__(owned self) raises -> type as out: + fn __await__(owned self, out result: type) raises: """Suspends the current coroutine until the coroutine is complete. Returns: @@ -233,7 +235,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: __disable_del self if __mlir_op.`co.await`[_type = __mlir_type.i1]( handle, - __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)), + __mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(result)), __mlir_op.`lit.ref.to_pointer`( __get_mvalue_as_litref(__get_nearest_error_slot()) ), @@ -242,4 +244,6 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]: __get_mvalue_as_litref(__get_nearest_error_slot()) ) __mlir_op.`lit.raise`() - __mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out)) + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(result) + ) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 2c9a13234b..92cb98be75 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1154,7 +1154,7 @@ struct Int( @doc_private @staticmethod - fn try_from_python(obj: PythonObject) raises -> Self as result: + fn try_from_python(obj: PythonObject, out result: Self) raises: """Construct an `Int` from a Python integer value. Raises: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2471fbf58a..d93be3a050 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2329,7 +2329,7 @@ struct SIMD[type: DType, size: Int]( ](self, value, Int64(offset)) @always_inline("nodebug") - fn join(self, other: Self) -> SIMD[type, 2 * size] as result: + fn join(self, other: Self, out result: SIMD[type, 2 * size]): """Concatenates the two vectors together. Args: diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index a0bb3cd262..284fb630da 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -794,7 +794,7 @@ struct Deque[ElementType: CollectionElement]( return (self._data + self._head)[] - fn pop(mut self) raises -> ElementType as element: + fn pop(mut self, out element: ElementType) raises: """Removes and returns the element from the right side of the deque. Returns: @@ -818,7 +818,7 @@ struct Deque[ElementType: CollectionElement]( return - fn popleft(mut self) raises -> ElementType as element: + fn popleft(mut self, out element: ElementType) raises: """Removes and returns the element from the left side of the deque. Returns: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f4971c68af..047bb61ac7 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -154,13 +154,14 @@ struct UnsafePointer[ @always_inline("nodebug") fn address_of( ref [address_space]arg: type, - ) -> UnsafePointer[ - type, - address_space=address_space, - alignment=1, - origin=MutableAnyOrigin - # TODO: Propagate origin of the argument. - ] as result: + out result: UnsafePointer[ + type, + address_space=address_space, + alignment=1, + origin=MutableAnyOrigin + # TODO: Propagate origin of the argument. + ], + ): """Gets the address of the argument. Args: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index b526aa84ff..0ea7685fe4 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -454,9 +454,10 @@ struct IndexList[ @always_inline("nodebug") fn canonicalize( self, - ) -> IndexList[ - size, element_bitwidth = bitwidthof[Int](), unsigned=False - ] as result: + out result: IndexList[ + size, element_bitwidth = bitwidthof[Int](), unsigned=False + ], + ): """Canonicalizes the IndexList. Returns: @@ -772,11 +773,14 @@ struct IndexList[ @always_inline fn cast[ type: DType - ](self) -> IndexList[ - size, - element_bitwidth = bitwidthof[type](), - unsigned = _is_unsigned[type](), - ] as result: + ]( + self, + out result: IndexList[ + size, + element_bitwidth = bitwidthof[type](), + unsigned = _is_unsigned[type](), + ], + ): """Casts to the target DType. Parameters: @@ -803,9 +807,12 @@ struct IndexList[ *, element_bitwidth: Int = Self.element_bitwidth, unsigned: Bool = Self.unsigned, - ](self) -> IndexList[ - size, element_bitwidth=element_bitwidth, unsigned=unsigned - ] as result: + ]( + self, + out result: IndexList[ + size, element_bitwidth=element_bitwidth, unsigned=unsigned + ], + ): """Casts to the target DType. Parameters: @@ -837,9 +844,12 @@ fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False, -](x: T0) -> IndexList[ - 1, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: T0, + out result: IndexList[ + 1, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 1-D Index from the given value. Parameters: @@ -859,9 +869,12 @@ fn Index[ @always_inline fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False -](x: UInt) -> IndexList[ - 1, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: UInt, + out result: IndexList[ + 1, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 1-D Index from the given value. Parameters: @@ -884,9 +897,13 @@ fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False, -](x: T0, y: T1) -> IndexList[ - 2, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: T0, + y: T1, + out result: IndexList[ + 2, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 2-D Index from the given values. Parameters: @@ -908,9 +925,13 @@ fn Index[ @always_inline fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False -](x: UInt, y: UInt) -> IndexList[ - 2, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: UInt, + y: UInt, + out result: IndexList[ + 2, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 2-D Index from the given values. Parameters: @@ -935,9 +956,14 @@ fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False, -](x: T0, y: T1, z: T2) -> IndexList[ - 3, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: T0, + y: T1, + z: T2, + out result: IndexList[ + 3, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 3-D Index from the given values. Parameters: @@ -967,9 +993,15 @@ fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False, -](x: T0, y: T1, z: T2, w: T3) -> IndexList[ - 4, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: T0, + y: T1, + z: T2, + w: T3, + out result: IndexList[ + 4, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 4-D Index from the given values. Parameters: @@ -1002,9 +1034,16 @@ fn Index[ *, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False, -](x: T0, y: T1, z: T2, w: T3, v: T4) -> IndexList[ - 5, element_bitwidth=element_bitwidth, unsigned=unsigned -] as result: +]( + x: T0, + y: T1, + z: T2, + w: T3, + v: T4, + out result: IndexList[ + 5, element_bitwidth=element_bitwidth, unsigned=unsigned + ], +): """Constructs a 5-D Index from the given values. Parameters: diff --git a/stdlib/test/os/path/test_expandvars.mojo b/stdlib/test/os/path/test_expandvars.mojo index 4dccce1208..fead55656d 100644 --- a/stdlib/test/os/path/test_expandvars.mojo +++ b/stdlib/test/os/path/test_expandvars.mojo @@ -22,7 +22,7 @@ from testing import assert_equal struct EnvVar: var name: String - fn __init__(out self, name: String, value: String) -> None: + fn __init__(out self, name: String, value: String): self.name = name _ = os.setenv(name, value) From 75b4b44bc0edddc1268c4d396b1621652064e8d4 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Sun, 8 Dec 2024 09:04:39 -0800 Subject: [PATCH 2000/2019] [Docs] Implicit conversion updates. Updates for the new default explicit constructors & `@implicit` decorator. MODULAR_ORIG_COMMIT_REV_ID: ef4ab402b9e2c63747f26f7ab4e6e3208859a8d7 --- docs/manual/decorators/implicit.md | 40 +++++++++++++++ docs/manual/decorators/index.mdx | 1 + docs/manual/lifecycle/life.mdx | 82 +++++++++++++++--------------- docs/manual/parameters/index.mdx | 2 +- docs/manual/variables.mdx | 16 ++++-- 5 files changed, 94 insertions(+), 47 deletions(-) create mode 100644 docs/manual/decorators/implicit.md diff --git a/docs/manual/decorators/implicit.md b/docs/manual/decorators/implicit.md new file mode 100644 index 0000000000..1e9cd07e0b --- /dev/null +++ b/docs/manual/decorators/implicit.md @@ -0,0 +1,40 @@ +--- +title: '@implicit' +description: Marks a constructor as eligible for implicit conversion. +codeTitle: true + +--- + +You can add the `@implicit` decorator on any single-argument constructor to +identify it as eligible for implicit conversion. + +For example: + +```mojo +struct MyInt: + var value: Int + + @implicit + fn __init__(out self, value: Int): + self.value = value + + fn __init__(out self, value: Float64): + self.value = int(value) + + +``` + +This implicit conversion constructor allows you to pass an `Int` to a function +that takes a `MyInt` argument, or assign an `Int` to a variable of type `MyInt`. +However, the constructor that takes a `Float64` value is **not** an implicit +conversion constructor, so it must be invoked explicitly: + +```mojo +fn func(n: MyInt): + print("MyInt value: ", n.value) + +fn main(): + func(Int(42)) # Implicit conversion from Int: OK + func(MyInt(Float64(4.2))) # Explicit conversion from Float64: OK + func(Float64(4.2)) # Error: can't convert Float64 to MyInt +``` diff --git a/docs/manual/decorators/index.mdx b/docs/manual/decorators/index.mdx index 2a65a9f213..058580e41e 100644 --- a/docs/manual/decorators/index.mdx +++ b/docs/manual/decorators/index.mdx @@ -9,6 +9,7 @@ listing: contents: - always-inline.md - copy-capture.md + - implicit.md - nonmaterializable.md - parameter.md - register-passable.md diff --git a/docs/manual/lifecycle/life.mdx b/docs/manual/lifecycle/life.mdx index 91646caa5f..c852c4c700 100644 --- a/docs/manual/lifecycle/life.mdx +++ b/docs/manual/lifecycle/life.mdx @@ -153,12 +153,16 @@ struct MyPet: Mojo supports implicit conversion from one type to another. Implicit conversion can happen when one of the following occurs: -* You assign a value of one type to a variable with a different type. -* You pass a value of one type to a function that requires a different type. +- You assign a value of one type to a variable with a different type. +- You pass a value of one type to a function that requires a different type. In both cases, implicit conversion is supported when the target type -defines a constructor that takes a single required, non-keyword argument of the -source type. For example: +defines a constructor that meets the following criteria: + +- Is declared with the `@implicit` decorator. +- Has a single required, non-keyword argument of the source type. + +For example: ```mojo var a = Source() @@ -170,7 +174,9 @@ Mojo implicitly converts the `Source` value in `a` to a `Target` value if ```mojo struct Target: - fn __init__(out self, s: Source): ... + + @implicit + fn __init__(out self, s: Source): ... ``` With implicit conversion, the assignment above is essentially identical to: @@ -179,26 +185,22 @@ With implicit conversion, the assignment above is essentially identical to: var b = Target(a) ``` +In general, types should only support implicit conversions when the conversion +lossless, and ideally inexpensive. For example, converting an integer to a +floating-point number is usually lossless (except for very large positive and +negative integers, where the conversion may be approximate), but converting a +floating-point number to an integer is very likely to lose information. So +Mojo supports implicit conversion from `Int` to `Float64`, but not the reverse. + The constructor used for implicit conversion can take optional arguments, so the following constructor would also support implicit conversion from `Source` to `Target`: ```mojo struct Target: - fn __init__(out self, s: Source, reverse: Bool = False): ... -``` - -Implicit conversion also occurs if the type doesn't declare its own constructor, -but instead uses the [`@value` decorator](#value-decorator), *and* the type -has only one field. That's because Mojo automatically creates a member-wise -constructor for each field, and when there is only one field, that synthesized -constructor works exactly like a conversion constructor. For example, this -type also can convert a `Source` value to a `Target` value: -```mojo -@value -struct Target: - var s: Source + @implicit + fn __init__(out self, s: Source, reverse: Bool = False): ... ``` Implicit conversion can fail if Mojo can't unambiguously match the conversion to @@ -209,41 +211,39 @@ convert the values: ```mojo struct A: - fn __init__(out self, s: Source): ... + @implicit + fn __init__(out self, s: Source): ... struct B: - fn __init__(out self, s: Source): ... + @implicit + fn __init__(out self, s: Source): ... -struct Target: - fn __init__(out self, a: A): ... - fn __init__(out self, b: B): ... +struct OverloadedTarget: + @implicit + fn __init__(out self, a: A): ... + @implicit + fn __init__(out self, b: B): ... -# Fails -var t = Target(Source()) +var t = OverloadedTarget(Source()) # Error: ambiguous call to '__init__': each + # candidate requires 1 implicit conversion ``` -In this case, removing either one of the target type's constructors will fix the -problem. - -If you want to define a single-argument constructor, but you **don't** want -the types to implicitly convert, you can define the constructor with a -[keyword-only argument](/mojo/manual/functions#positional-only-and-keyword-only-arguments): +In this case, you can fix the issue by explicitly casting to one of the +intermediate types. For example: ```mojo -struct Target: - # does not support implicit conversion - fn __init__(out self, *, source: Source): ... - -# the constructor must be called with a keyword -var t = Target(source=a) +var t = OverloadedTarget(A(Source())) # OK ``` -:::note +Mojo applies at most one implicit conversion to a variable. For example: -In the future we intend to provide a more explicit method of declaring whether -a constructor should support implicit conversion. +```mojo +var t: OverloadedTarget = Source() # Error: can't implicitly convert Source + # to Target +``` -::: +Would fail because there's no direct conversion from `Source` to +`OverloadedTarget`. ## Copy constructor diff --git a/docs/manual/parameters/index.mdx b/docs/manual/parameters/index.mdx index 9b3ba3250f..fae5e5f72c 100644 --- a/docs/manual/parameters/index.mdx +++ b/docs/manual/parameters/index.mdx @@ -378,7 +378,7 @@ struct MyInt: """A type that is implicitly convertible to `Int`.""" var value: Int - @always_inline("nodebug") + @implicit fn __init__(out self, _a: Int): self.value = _a diff --git a/docs/manual/variables.mdx b/docs/manual/variables.mdx index 6b77b88f0f..9247ff5c9f 100644 --- a/docs/manual/variables.mdx +++ b/docs/manual/variables.mdx @@ -183,7 +183,7 @@ its own type. For example, if you assign an integer to a variable that has a floating-point type, it converts the value instead of giving a compiler error: ```mojo -var number: Float64 = 1 +var number: Float64 = Int(1) ``` ```output @@ -191,8 +191,13 @@ var number: Float64 = 1 ``` As shown above, value assignment can be converted into a constructor call if the -target type has a constructor that takes a single argument that matches the -value being assigned. So, this code uses the `Float64` constructor that takes an +target type has a constructor that meets the following criteria: + +- It's decorated with the `@implicit` decorator. + +- It takes a single required argument that matches the value being assigned. + +So, this code uses the `Float64` constructor that takes an integer: `__init__(out self, value: Int)`. In general, implicit conversions should only be supported where the conversion @@ -200,7 +205,7 @@ is lossless. Implicit conversion follows the logic of [overloaded functions](/mojo/manual/functions#overloaded-functions). If the destination -type has a single-argument constructor that takes an argument of the source +type has a viable implicit conversion constructor for the source type, it can be invoked for implicit conversion. So assigning an integer to a `Float64` variable is exactly the same as this: @@ -215,7 +220,8 @@ implicitly convert to the required type (using one of the type's overloaded constructors). For example, you can pass an `Int` to a function that expects a `Float64`, -because `Float64` includes a constructor that takes an `Int`: +because `Float64` includes an implicit conversion constructor that takes an +`Int`: ```mojo fn take_float(value: Float64): From 62cf1d43a95829b83dfb9498999528e78d53b7c0 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sun, 8 Dec 2024 09:54:14 -0800 Subject: [PATCH 2001/2019] [******][GPU] Do not run gpu tests with assertions on MODULAR_ORIG_COMMIT_REV_ID: ae0b65162253dd2e39cf820d2be615f4678da830 --- stdlib/test/lit.cfg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/test/lit.cfg.py b/stdlib/test/lit.cfg.py index 55976ecfe6..67d1f613b2 100644 --- a/stdlib/test/lit.cfg.py +++ b/stdlib/test/lit.cfg.py @@ -78,8 +78,9 @@ def has_not(): # with assertions enabled. config.substitutions.insert(1, ("%bare-mojo", "mojo")) - # NOTE: Right now this is the same as %mojo but we should start testing + # NOTE: Right now these are the same as %mojo but we should start testing # with debug info as well + config.substitutions.insert(0, ("%mojo-no-debug-no-assert", "mojo")) config.substitutions.insert(0, ("%mojo-no-debug", base_mojo_command)) # The `mojo` nightly compiler ships with its own `stdlib.mojopkg`. For the From 638fdc8e3588e072a9acdac82371bf0bbaaeadeb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Sun, 8 Dec 2024 22:47:27 -0600 Subject: [PATCH 2002/2019] [stdlib] docs: Update changelog to reflect recent changes to `Origin` MODULAR_ORIG_COMMIT_REV_ID: 0d3fca509ce444c56e0cb65814e2696150e86b18 --- docs/changelog.md | 13 +++++++++++++ stdlib/src/builtin/type_aliases.mojo | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 0576359789..a57e0db1b2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -485,6 +485,19 @@ what we publish. for more information and rationale. As a consequence the `__lifetime_of()` operator is now named `__origin_of()`. +- `Origin` is now a complete wrapper around the MLIR origin type. + + - The `Origin.type` alias has been renamed to `_mlir_origin`. In parameter + lists, you can now write just `Origin[..]`, instead of `Origin[..].type`. + + - `ImmutableOrigin` and `MutableOrigin` are now, respectively, just aliases + for `Origin[False]` and `Origin[True]`. + + - `Origin` struct values are now supported in the brackets of a `ref [..]` + argument. + + - Added `Origin.cast_from` for casting the mutability of an origin value. + - You can now use the `+=` and `*` operators on a `StringLiteral` at compile time using the `alias` keyword: diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index b3358188b5..762746f8c2 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -67,6 +67,18 @@ struct Origin[is_mutable: Bool]: Safety: This is an UNSAFE operation if used to cast an immutable origin to a mutable origin. + + Examples: + + Cast a mutable origin to be immutable: + + ```mojo + struct Container[mut: Bool, //, origin: Origin[mut]]: + var data: Int + + fn imm_borrow(self) -> Container[ImmutableOrigin.cast_from[origin].result]: + # ... + ``` """ # ===-------------------------------------------------------------------===# From 816b3f456497a68d2ccee22ce08161afbcea75d8 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 9 Dec 2024 09:11:31 -0800 Subject: [PATCH 2003/2019] [mojo-stdlib] Minor code cleanups, NFC. This just tidies up some code to avoid using patterns that will soon be invalid: as the 'out' argument on inits is becoming more normal, you won't be able to pass a value in at a call site, it is always a result. MODULAR_ORIG_COMMIT_REV_ID: b9e5685612246c8c758b4191f3a70f61a4eecff5 --- stdlib/src/builtin/builtin_slice.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/collections/deque.mojo | 4 ++-- stdlib/src/collections/list.mojo | 2 +- stdlib/src/collections/string.mojo | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/builtin_slice.mojo b/stdlib/src/builtin/builtin_slice.mojo index 0d0fe96750..b663b9350e 100644 --- a/stdlib/src/builtin/builtin_slice.mojo +++ b/stdlib/src/builtin/builtin_slice.mojo @@ -88,7 +88,7 @@ struct Slice( Args: other: The slice to copy. """ - self.__init__(start=other.start, end=other.end, step=other.step) + self = Self(start=other.start, end=other.end, step=other.step) # ===-------------------------------------------------------------------===# # Trait implementations diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index d93be3a050..18a87f1903 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1584,7 +1584,7 @@ struct SIMD[type: DType, size: Int]( output.write(", ") _write_scalar(output, element) output.write(")") - return output + return output^ @always_inline("nodebug") fn __floor__(self) -> Self: diff --git a/stdlib/src/collections/deque.mojo b/stdlib/src/collections/deque.mojo index 284fb630da..6415979277 100644 --- a/stdlib/src/collections/deque.mojo +++ b/stdlib/src/collections/deque.mojo @@ -88,8 +88,8 @@ struct Deque[ElementType: CollectionElement]( out self, *, owned elements: Optional[List[ElementType]] = None, - capacity: Int = self.default_capacity, - min_capacity: Int = self.default_capacity, + capacity: Int = Self.default_capacity, + min_capacity: Int = Self.default_capacity, maxlen: Int = -1, shrink: Bool = True, ): diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 13ee261638..10490c16c7 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -121,7 +121,7 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( Args: other: The list to copy. """ - self.__init__(capacity=other.capacity) + self = Self(capacity=other.capacity) for e in other: self.append(e[]) diff --git a/stdlib/src/collections/string.mojo b/stdlib/src/collections/string.mojo index 86449179a3..fe9746584d 100644 --- a/stdlib/src/collections/string.mojo +++ b/stdlib/src/collections/string.mojo @@ -867,7 +867,7 @@ struct String( Args: other: The value to copy. """ - self.__copyinit__(other) + self = other # Just use the implicit copyinit. @implicit fn __init__(out self, str: StringRef): From 9c4628a667f11375ef112462ca2962fc87654ca4 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 9 Dec 2024 10:45:48 -0700 Subject: [PATCH 2004/2019] [stdlib] Skip trivial destructors in `List` If the elements are hinted as "trivial", skip running the destructors for them. Note that we don't have a checked equivalent of `std::is_trivially_destructible`, so this is just relying on the hint provided to `List`. For non-trivial types, we still run the destructors on each of the elements, of course. Future PRs will take advantage of the `hint` parameter in `copyinit`. MODULAR_ORIG_COMMIT_REV_ID: c92f98b69f1a40b7b671b6392a7b71b54cfb61b8 --- stdlib/src/collections/list.mojo | 7 +++++-- stdlib/test/collections/test_list.mojo | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 10490c16c7..bcfca0c2fa 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -212,8 +212,11 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( fn __del__(owned self): """Destroy all elements in the list and free its memory.""" - for i in range(self.size): - (self.data + i).destroy_pointee() + + @parameter + if not hint_trivial_type: + for i in range(self.size): + (self.data + i).destroy_pointee() self.data.free() # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 1def6849ea..56dab6510b 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -915,6 +915,19 @@ def test_list_dtor(): assert_equal(g_dtor_count, 1) +# Verify we skip calling destructors for the trivial elements +def test_destructor_trivial_elements(): + # explicitly reset global counter + g_dtor_count = 0 + + var l = List[DtorCounter, hint_trivial_type=True]() + l.append(DtorCounter()) + + l^.__del__() + + assert_equal(g_dtor_count, 0) + + def test_list_repr(): var l = List(1, 2, 3) assert_equal(l.__repr__(), "[1, 2, 3]") @@ -959,4 +972,5 @@ def main(): test_list_contains() test_indexing() test_list_dtor() + test_destructor_trivial_elements() test_list_repr() From da5b65f9ccce339afc4fd2cd11545b2db5b68a06 Mon Sep 17 00:00:00 2001 From: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:25:44 -0600 Subject: [PATCH 2005/2019] [External] [stdlib] Micro-optimize string `splitlines` and `isnewline` (#52307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Micro-optimize string `splitlines` and `isnewline` ## Micro-optimize string `splitlines` and `isnewline`. Benchmark results: CPU: Intel® Core™ i7-7700HQ improvement metric: markdown percentage improvement (`(old_value - new_value) / old_value`) Average improvement: 1.2% . Many of the discrepancies are likely because of runtime (nano-second scale) pipelining (and other) differences during testing. The most robust estimates are the biggest tests which show around 4.2% improvement. |Name | old_value (ms) | new_value (ms) | improvement| |:-----------------------------|---------------------:|----------------:|------:| |`bench_string_splitlines[10]` | 0.000100497059839818 | 0.00010261460376 | -2.11% | |`bench_string_splitlines[30]` | 0.000171998352418428 | 0.000174028055158599 | -1.18% | |`bench_string_splitlines[50]` | 0.000264958575880379 | 0.000251598931395228 | 5.04% | |`bench_string_splitlines[100]` | 0.000417350538881514 | 0.000415764808815687 | 0.38% | |`bench_string_splitlines[1000]` | 0.00320600819966129 | 0.00322517645847026 | -0.60% | |`bench_string_splitlines[10000]` | 0.0302790238107332 | 0.0305872863513719 | -1.02% | |`bench_string_splitlines[100000]` | 0.319274428592796 | 0.304513179279761 | 4.62% | |`bench_string_splitlines[1000000]` | 3.24098357344348 | 3.10220707742267 | 4.28% | Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#3825 MODULAR_ORIG_COMMIT_REV_ID: 433b5ea2f49164bf08572004da1541ac42f1eed2 --- .../benchmarks/collections/bench_string.mojo | 2 +- stdlib/src/utils/string_slice.mojo | 133 +++++++++--------- 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/stdlib/benchmarks/collections/bench_string.mojo b/stdlib/benchmarks/collections/bench_string.mojo index 3cee895b73..fc5dfc718e 100644 --- a/stdlib/benchmarks/collections/bench_string.mojo +++ b/stdlib/benchmarks/collections/bench_string.mojo @@ -227,7 +227,7 @@ fn bench_string_is_valid_utf8[ # ===-----------------------------------------------------------------------===# def main(): seed() - var m = Bench(BenchConfig(num_repetitions=5)) + var m = Bench(BenchConfig(num_repetitions=1)) alias filenames = ( "UN_charter_EN", "UN_charter_ES", diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index e572747d4e..03ed9be490 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -24,7 +24,7 @@ from utils import StringSlice from collections import List, Optional from collections.string import _atof, _atol, _isspace from sys import bitwidthof, simdwidthof -from sys.intrinsics import unlikely +from sys.intrinsics import unlikely, likely from bit import count_leading_zeros from memory import UnsafePointer, memcmp, memcpy, Span @@ -1010,40 +1010,22 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]( listed above, otherwise False. """ - fn _is_newline_char(s: StringSlice) -> Bool: - # sorry for readability, but this has less overhead than memcmp - # highly performance sensitive code, benchmark before touching - alias `\t` = UInt8(ord("\t")) - alias `\r` = UInt8(ord("\r")) - alias `\n` = UInt8(ord("\n")) - alias `\x1c` = UInt8(ord("\x1c")) - alias `\x1e` = UInt8(ord("\x1e")) - no_null_len = s.byte_length() - ptr = s.unsafe_ptr() - if no_null_len == 1: - v = ptr[0] - return `\t` <= v <= `\x1e` and not (`\r` < v < `\x1c`) - elif no_null_len == 2: - v0 = ptr[0] - v1 = ptr[1] - next_line = v0 == 0xC2 and v1 == 0x85 # next line: \x85 - r_n = v0 == `\r` and v1 == `\n` - return next_line or r_n - elif no_null_len == 3: - # unicode line sep or paragraph sep: \u2028 , \u2029 - v2 = ptr[2] - lastbyte = v2 == 0xA8 or v2 == 0xA9 - return ptr[0] == 0xE2 and ptr[1] == 0x80 and lastbyte - return False + var ptr = self.unsafe_ptr() + var length = self.byte_length() @parameter if single_character: - return _is_newline_char(self) + return length != 0 and _is_newline_char[include_r_n=True]( + ptr, 0, ptr[0], length + ) else: + var offset = 0 for s in self: - if not _is_newline_char(s): + var b_len = s.byte_length() + if not _is_newline_char(ptr, offset, ptr[offset], b_len): return False - return self.byte_length() != 0 + offset += b_len + return length != 0 fn splitlines[ O: ImmutableOrigin, // @@ -1063,54 +1045,41 @@ struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]( A List of Strings containing the input split by line boundaries. """ + # highly performance sensitive code, benchmark before touching alias `\r` = UInt8(ord("\r")) alias `\n` = UInt8(ord("\n")) - alias `\t` = UInt8(ord("\t")) - alias `\x1c` = UInt8(ord("\x1c")) - alias `\x1e` = UInt8(ord("\x1e")) - output = List[StringSlice[O]](capacity=128) # guessing - ptr = self.unsafe_ptr() - length = self.byte_length() - offset = 0 - @always_inline - @parameter - fn _is_newline_char(p: UnsafePointer[Byte], l: Int, b0: Byte) -> Bool: - # sorry for readability, but this has less overhead than memcmp - # highly performance sensitive code, benchmark before touching - if l == 1: - return `\t` <= b0 <= `\x1e` and not (`\r` < b0 < `\x1c`) - elif l == 2: - return b0 == 0xC2 and p[1] == 0x85 # next line: \x85 - elif l == 3: - # unicode line sep or paragraph sep: \u2028 , \u2029 - v2 = p[2] - lastbyte = v2 == 0xA8 or v2 == 0xA9 - return b0 == 0xE2 and p[1] == 0x80 and lastbyte - return False + output = List[StringSlice[O]](capacity=128) # guessing + var ptr = self.unsafe_ptr() + var length = self.byte_length() + var offset = 0 while offset < length: - eol_start = offset - eol_length = 0 + var eol_start = offset + var eol_length = 0 while eol_start < length: - b0 = ptr[eol_start] - char_len = _utf8_first_byte_sequence_length(b0) + var b0 = ptr[eol_start] + var char_len = _utf8_first_byte_sequence_length(b0) debug_assert( eol_start + char_len <= length, "corrupted sequence causing unsafe memory access", ) - isnewline = int(_is_newline_char(ptr + eol_start, char_len, b0)) - char_end = isnewline * (eol_start + char_len) - next_idx = char_end * int(char_end < length) - is_r_n = b0 == `\r` and next_idx != 0 and ptr[next_idx] == `\n` - eol_length = isnewline * char_len + int(is_r_n) - if unlikely(isnewline == 1): + var isnewline = unlikely( + _is_newline_char(ptr, eol_start, b0, char_len) + ) + var char_end = int(isnewline) * (eol_start + char_len) + var next_idx = char_end * int(char_end < length) + var is_r_n = b0 == `\r` and next_idx != 0 and ptr[ + next_idx + ] == `\n` + eol_length = int(isnewline) * char_len + int(is_r_n) + if isnewline: break eol_start += char_len - str_len = eol_start - offset + int(keepends) * eol_length - s = StringSlice[O](ptr=ptr + offset, length=str_len) + var str_len = eol_start - offset + int(keepends) * eol_length + var s = StringSlice[O](ptr=ptr + offset, length=str_len) output.append(s) offset = eol_start + eol_length @@ -1191,3 +1160,41 @@ fn _to_string_list[ return len(v) return _to_string_list[items.T, len_fn, unsafe_ptr_fn](items) + + +@always_inline +fn _is_newline_char[ + include_r_n: Bool = False +](p: UnsafePointer[Byte], eol_start: Int, b0: Byte, char_len: Int) -> Bool: + """Returns whether the char is a newline char. + + Safety: + This assumes valid utf-8 is passed. + """ + # highly performance sensitive code, benchmark before touching + alias `\r` = UInt8(ord("\r")) + alias `\n` = UInt8(ord("\n")) + alias `\t` = UInt8(ord("\t")) + alias `\x1c` = UInt8(ord("\x1c")) + alias `\x1e` = UInt8(ord("\x1e")) + + # here it's actually faster to have branching due to the branch predictor + # "realizing" that the char_len == 1 path is often taken. Using the likely + # intrinsic is to make the machine code be ordered to optimize machine + # instruction fetching, which is an optimization for the CPU front-end. + if likely(char_len == 1): + return `\t` <= b0 <= `\x1e` and not (`\r` < b0 < `\x1c`) + elif char_len == 2: + var b1 = p[eol_start + 1] + var is_next_line = b0 == 0xC2 and b1 == 0x85 # unicode next line \x85 + + @parameter + if include_r_n: + return is_next_line or (b0 == `\r` and b1 == `\n`) + else: + return is_next_line + elif char_len == 3: # unicode line sep or paragraph sep: \u2028 , \u2029 + var b1 = p[eol_start + 1] + var b2 = p[eol_start + 2] + return b0 == 0xE2 and b1 == 0x80 and (b2 == 0xA8 or b2 == 0xA9) + return False From 043456b50bbf8db3730dc52cc82924757d95444b Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Mon, 9 Dec 2024 12:27:36 -0600 Subject: [PATCH 2006/2019] [External] [stdlib] Snake casing stdlib (#48873) [External] [stdlib] Snake casing stdlib Convert camelCase code to a pythonic snake_case throughout the stdlib Co-authored-by: Manuel Saelices Closes modularml/mojo#3631 MODULAR_ORIG_COMMIT_REV_ID: f308889bd1a2221371fa2b2d85ea2f30da333667 --- stdlib/src/builtin/object.mojo | 64 ++++++++++++------------ stdlib/src/python/_cpython.mojo | 9 ++-- stdlib/src/python/python_object.mojo | 46 ++++++++--------- stdlib/src/time/time.mojo | 16 +++--- stdlib/src/utils/_serialize.mojo | 2 +- stdlib/test/builtin/test_error.mojo | 4 +- stdlib/test/python/test_python_info.mojo | 8 +-- 7 files changed, 75 insertions(+), 74 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index fce05d2476..c2337934a1 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -523,9 +523,9 @@ struct _ObjectImpl( lowest-common denominator type for performing comparisons, in order of increasing priority: bool, int, and then float. """ - var lhsId = lhs.get_type_id() - var rhsId = rhs.get_type_id() - if lhsId == rhsId: + var lhs_id = lhs.get_type_id() + var rhs_id = rhs.get_type_id() + if lhs_id == rhs_id: return @parameter @@ -538,10 +538,10 @@ struct _ObjectImpl( else: value = value.convert_int_to_float() - if lhsId > rhsId: - convert(rhs, rhsId, lhsId) + if lhs_id > rhs_id: + convert(rhs, rhs_id, lhs_id) else: - convert(lhs, lhsId, rhsId) + convert(lhs, lhs_id, rhs_id) @staticmethod fn coerce_arithmetic_type(mut lhs: _ObjectImpl, mut rhs: _ObjectImpl): @@ -1075,15 +1075,15 @@ struct object( """ lhs._comparison_type_check() rhs._comparison_type_check() - var lhsValue = lhs._value - var rhsValue = rhs._value - _ObjectImpl.coerce_comparison_type(lhsValue, rhsValue) - if lhsValue.is_float(): - return fp_func(lhsValue.get_as_float(), rhsValue.get_as_float()) - if lhsValue.is_int(): - return int_func(lhsValue.get_as_int(), rhsValue.get_as_int()) - debug_assert(lhsValue.is_bool(), "expected both values to be bool") - return bool_func(lhsValue.get_as_bool(), rhsValue.get_as_bool()) + var lhs_value = lhs._value + var rhs_value = rhs._value + _ObjectImpl.coerce_comparison_type(lhs_value, rhs_value) + if lhs_value.is_float(): + return fp_func(lhs_value.get_as_float(), rhs_value.get_as_float()) + if lhs_value.is_int(): + return int_func(lhs_value.get_as_int(), rhs_value.get_as_int()) + debug_assert(lhs_value.is_bool(), "expected both values to be bool") + return bool_func(lhs_value.get_as_bool(), rhs_value.get_as_bool()) @always_inline fn _string_compare(self, rhs: object) -> Int: @@ -1286,12 +1286,12 @@ struct object( """ lhs._arithmetic_type_check() rhs._arithmetic_type_check() - var lhsValue = lhs._value - var rhsValue = rhs._value - _ObjectImpl.coerce_arithmetic_type(lhsValue, rhsValue) - if lhsValue.is_float(): - return fp_func(lhsValue.get_as_float(), rhsValue.get_as_float()) - return int_func(lhsValue.get_as_int(), rhsValue.get_as_int()) + var lhs_value = lhs._value + var rhs_value = rhs._value + _ObjectImpl.coerce_arithmetic_type(lhs_value, rhs_value) + if lhs_value.is_float(): + return fp_func(lhs_value.get_as_float(), rhs_value.get_as_float()) + return int_func(lhs_value.get_as_int(), rhs_value.get_as_int()) @staticmethod @always_inline @@ -1310,12 +1310,12 @@ struct object( """ lhs._arithmetic_integral_type_check() rhs._arithmetic_integral_type_check() - var lhsValue = lhs._value - var rhsValue = rhs._value - _ObjectImpl.coerce_integral_type(lhsValue, rhsValue) - if lhsValue.is_int(): - return int_func(lhsValue.get_as_int(), rhsValue.get_as_int()) - return bool_func(lhsValue.get_as_bool(), rhsValue.get_as_bool()) + var lhs_value = lhs._value + var rhs_value = rhs._value + _ObjectImpl.coerce_integral_type(lhs_value, rhs_value) + if lhs_value.is_int(): + return int_func(lhs_value.get_as_int(), rhs_value.get_as_int()) + return bool_func(lhs_value.get_as_bool(), rhs_value.get_as_bool()) @always_inline fn __neg__(self) raises -> object: @@ -1360,14 +1360,14 @@ struct object( The sum or concatenated values. """ if self._value.is_str() and rhs._value.is_str(): - var lhsStr = self._value.get_as_string() - var rhsStr = rhs._value.get_as_string() - var length = lhsStr.length + rhsStr.length + var lhs_str = self._value.get_as_string() + var rhs_str = rhs._value.get_as_string() + var length = lhs_str.length + rhs_str.length var impl = _ImmutableString( UnsafePointer[UInt8].alloc(length), length ) - memcpy(impl.data, lhsStr.data, lhsStr.length) - memcpy(impl.data + lhsStr.length, rhsStr.data, rhsStr.length) + memcpy(impl.data, lhs_str.data, lhs_str.length) + memcpy(impl.data + lhs_str.length, rhs_str.data, rhs_str.length) var result = object() result._value = impl return result diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 05426aa8eb..d047c84b3d 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -1478,14 +1478,13 @@ struct CPython: return int(self.lib.call["PyObject_Hash", Int](obj)) fn PyObject_GetIter( - mut self, traversablePyObject: PyObjectPtr + mut self, traversable_py_object: PyObjectPtr ) -> PyObjectPtr: """[Reference]( https://docs.python.org/3/c-api/object.html#c.PyObject_GetIter). """ - var iterator = self.lib.call["PyObject_GetIter", PyObjectPtr]( - traversablePyObject + traversable_py_object ) self.log( @@ -1493,9 +1492,9 @@ struct CPython: " NEWREF PyObject_GetIter, refcnt:", self._Py_REFCNT(iterator), "referencing ", - traversablePyObject._get_ptr_as_int(), + traversable_py_object._get_ptr_as_int(), "refcnt of traversable: ", - self._Py_REFCNT(traversablePyObject), + self._Py_REFCNT(traversable_py_object), ) self._inc_total_rc() diff --git a/stdlib/src/python/python_object.mojo b/stdlib/src/python/python_object.mojo index a6b3a8a45f..42b2dc6bed 100644 --- a/stdlib/src/python/python_object.mojo +++ b/stdlib/src/python/python_object.mojo @@ -41,9 +41,9 @@ struct _PyIter(Sized): var iterator: PythonObject """The iterator object that stores location.""" - var preparedNextItem: PythonObject + var prepared_next_item: PythonObject """The next item to vend or zero if there are no items.""" - var isDone: Bool + var is_done: Bool """Stores True if the iterator is pointing to the last item.""" # ===-------------------------------------------------------------------===# @@ -57,8 +57,8 @@ struct _PyIter(Sized): existing: Initialized _PyIter instance. """ self.iterator = existing.iterator - self.preparedNextItem = existing.preparedNextItem - self.isDone = existing.isDone + self.prepared_next_item = existing.prepared_next_item + self.is_done = existing.is_done @implicit fn __init__(out self, iter: PythonObject): @@ -69,19 +69,19 @@ struct _PyIter(Sized): """ var cpython = _get_global_python_itf().cpython() self.iterator = iter - var maybeNextItem = cpython.PyIter_Next(self.iterator.py_object) - if maybeNextItem.is_null(): - self.isDone = True - self.preparedNextItem = PythonObject(PyObjectPtr()) + var maybe_next_item = cpython.PyIter_Next(self.iterator.py_object) + if maybe_next_item.is_null(): + self.is_done = True + self.prepared_next_item = PythonObject(PyObjectPtr()) else: - self.preparedNextItem = PythonObject(maybeNextItem) - self.isDone = False + self.prepared_next_item = PythonObject(maybe_next_item) + self.is_done = False fn __init__(out self): """Initialize an empty iterator.""" self.iterator = PythonObject(PyObjectPtr()) - self.isDone = True - self.preparedNextItem = PythonObject(PyObjectPtr()) + self.is_done = True + self.prepared_next_item = PythonObject(PyObjectPtr()) # ===-------------------------------------------------------------------===# # Trait implementations @@ -97,12 +97,12 @@ struct _PyIter(Sized): if not self.iterator: return self.iterator var cpython = _get_global_python_itf().cpython() - var current = self.preparedNextItem - var maybeNextItem = cpython.PyIter_Next(self.iterator.py_object) - if maybeNextItem.is_null(): - self.isDone = True + var current = self.prepared_next_item + var maybe_next_item = cpython.PyIter_Next(self.iterator.py_object) + if maybe_next_item.is_null(): + self.is_done = True else: - self.preparedNextItem = PythonObject(maybeNextItem) + self.prepared_next_item = PythonObject(maybe_next_item) return current @always_inline @@ -115,7 +115,7 @@ struct _PyIter(Sized): Returns: 0 if the traversal is complete and 1 otherwise. """ - if self.isDone: + if self.is_done: return 0 else: return 1 @@ -596,19 +596,19 @@ struct PythonObject( raise Error("Attribute is not found.") return PythonObject(result) - fn __setattr__(self, name: StringLiteral, newValue: PythonObject) raises: + fn __setattr__(self, name: StringLiteral, new_value: PythonObject) raises: """Set the given value for the object attribute with the given name. Args: name: The name of the object attribute to set. - newValue: The new value to be set for that attribute. + new_value: The new value to be set for that attribute. """ - return self._setattr(name, newValue.py_object) + return self._setattr(name, new_value.py_object) - fn _setattr(self, name: StringLiteral, newValue: PyObjectPtr) raises: + fn _setattr(self, name: StringLiteral, new_value: PyObjectPtr) raises: var cpython = _get_global_python_itf().cpython() var result = cpython.PyObject_SetAttrString( - self.py_object, name, newValue + self.py_object, name, new_value ) Python.throw_python_exception_if_error_state(cpython) if result < 0: diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 0167c26548..f6221590ac 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -84,21 +84,23 @@ struct _CTimeSpec(Stringable): @value @register_passable("trivial") struct _FILETIME: - var dwLowDateTime: UInt32 - var dwHighDateTime: UInt32 + var dw_low_date_time: UInt32 + var dw_high_date_time: UInt32 fn __init__(out self): - self.dwLowDateTime = 0 - self.dwHighDateTime = 0 + self.dw_low_date_time = 0 + self.dw_high_date_time = 0 fn as_nanoseconds(self) -> UInt: # AFTER subtracting windows offset the return value fits in a signed int64 # BEFORE subtracting windows offset the return value does not fit in a signed int64 # Taken from https://github.com/microsoft/STL/blob/c8d1efb6d504f6392acf8f6d01fd703f7c8826c0/stl/src/xtime.cpp#L50 - alias windowsToUnixEpochOffsetNs: Int = 0x19DB1DED53E8000 + alias windows_to_unix_epoch_offset_ns: Int = 0x19DB1DED53E8000 var interval_count: UInt64 = ( - self.dwHighDateTime.cast[DType.uint64]() << 32 - ) + self.dwLowDateTime.cast[DType.uint64]() - windowsToUnixEpochOffsetNs + self.dw_high_date_time.cast[DType.uint64]() << 32 + ) + self.dw_low_date_time.cast[ + DType.uint64 + ]() - windows_to_unix_epoch_offset_ns return int(interval_count * 100) diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 1cc0eea138..f6e5baf18c 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -134,7 +134,7 @@ fn _serialize[ if row_idx != row_elem_count: serialize_fn(",") - # Intermediate rows are filled with "..." and rowIdx is advanced to third + # Intermediate rows are filled with "..." and row_idx is advanced to third # from last. if ( row_elem_count >= _kCompactMaxElemsToPrint diff --git a/stdlib/test/builtin/test_error.mojo b/stdlib/test/builtin/test_error.mojo index 87dd096cd4..eb24ee7e4b 100644 --- a/stdlib/test/builtin/test_error.mojo +++ b/stdlib/test/builtin/test_error.mojo @@ -27,8 +27,8 @@ def test_error_raising(): def test_from_and_to_string(): - var myString: String = "FOO" - var error = Error(myString) + var my_string: String = "FOO" + var error = Error(my_string) assert_equal(str(error), "FOO") assert_equal(str(Error("bad")), "bad") diff --git a/stdlib/test/python/test_python_info.mojo b/stdlib/test/python/test_python_info.mojo index b1b7eb29e7..5e1a4a1c7c 100644 --- a/stdlib/test/python/test_python_info.mojo +++ b/stdlib/test/python/test_python_info.mojo @@ -21,10 +21,10 @@ from testing import assert_equal fn test_python_version(mut python: Python) raises: var version = "3.10.8 (main, Nov 24 2022, 08:08:27) [Clang 14.0.6 ]" - var pythonVersion = PythonVersion(version) - assert_equal(pythonVersion.major, 3) - assert_equal(pythonVersion.minor, 10) - assert_equal(pythonVersion.patch, 8) + var python_version = PythonVersion(version) + assert_equal(python_version.major, 3) + assert_equal(python_version.minor, 10) + assert_equal(python_version.patch, 8) def main(): From 2ed61234cd7acbf88da3a8eaab68937cba0ec2bc Mon Sep 17 00:00:00 2001 From: Joshua James Venter Date: Mon, 9 Dec 2024 12:29:02 -0600 Subject: [PATCH 2007/2019] [External] [docs] Prevent unwanted Latex interpretation in `Path` docs (#52407) [External] [docs] Prevent unwanted Latex interpretation in `Path` docs As title, see [docs](https://docs.modular.com/mojo/stdlib/pathlib/path/Path#home). Co-authored-by: Joshua James Venter Closes modularml/mojo#3844 MODULAR_ORIG_COMMIT_REV_ID: d3cc7796c2b2fc8991bd958984c0d2313a9bfcd5 --- stdlib/src/pathlib/path.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index c6e9016b5e..5a0f285db1 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -264,7 +264,7 @@ struct Path( return os.path.exists(self) fn expanduser(self) raises -> Path: - """Expands a prefixed `~` with $HOME on posix or $USERPROFILE on + """Expands a prefixed `~` with `$HOME` on posix or `$USERPROFILE` on windows. If environment variables are not set or the `path` is not prefixed with `~`, returns the `path` unmodified. @@ -275,7 +275,7 @@ struct Path( @staticmethod fn home() raises -> Path: - """Returns $HOME on posix or $USERPROFILE on windows. If environment + """Returns `$HOME` on posix or `$USERPROFILE` on windows. If environment variables are not set it returns `~`. Returns: From 68efa7189575a608d74d2ebd5145f6a3e3bc57d5 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:01:03 -0800 Subject: [PATCH 2008/2019] New Get started with Mojo tutorial The tutorial gives new Mojo programmers a tour of some of the language's features by guiding them through the process of creating an implementation of Conway's Game of Life from scratch. MODULAR_ORIG_COMMIT_REV_ID: 3548d7d23c6f612ef22b730106646b8332ce8dfa --- docs/manual/basics.mdx | 34 +- docs/manual/get-started.mdx | 1310 +++++++++++++++++--- docs/manual/images/game-of-life-screen.png | Bin 0 -> 85325 bytes 3 files changed, 1137 insertions(+), 207 deletions(-) create mode 100644 docs/manual/images/game-of-life-screen.png diff --git a/docs/manual/basics.mdx b/docs/manual/basics.mdx index 938701d322..edeb731899 100644 --- a/docs/manual/basics.mdx +++ b/docs/manual/basics.mdx @@ -1,24 +1,21 @@ --- -title: Introduction to Mojo +title: Mojo language basics sidebar_position: 1 +sidebar_label: Overview description: Introduction to Mojo's basic language features. --- -At this point, you should have already set up the Mojo -SDK and run ["Hello -world"](/mojo/manual/get-started). Now let's talk about how -to write Mojo code. +At this point, you should have already installed [Magic](/magic) to manage the +virtual environment and packages for your MAX and Mojo development (or +alternatively added the `max` package to a [conda](/magic/conda) development +environment). It's also a good idea to go through the [Get Started with +Mojo](/mojo/manual/get-started) tutorial to get a taste of what Mojo programming +is like. Now let's talk about how to write Mojo code. -If -you know Python, then a lot of Mojo code will look familiar. However, Mojo -is—first and foremost—designed for high-performance systems programming, with -features like strong type checking, memory safety, next-generation compiler -technologies, and more. As such, Mojo also has a lot in common with languages -like C++ and Rust. - -Yet, we've designed Mojo to be flexible, so you can incrementally adopt -systems-programming features like strong type checking as you see fit—Mojo does -not *require* strong type checking. +If you know Python, then a lot of Mojo code will look familiar. However, Mojo +incorporates features like strong type checking, memory safety, next-generation +compiler technologies, and more. As such, Mojo also has a lot in common with +languages like C++ and Rust. On this page, we'll introduce the essential Mojo syntax, so you can start coding quickly and understand other Mojo code you encounter. Subsequent @@ -181,8 +178,9 @@ each characteristic (such as each method). Each characteristic in a trait is a "requirement" for the struct, and when your struct implements each requirement, it's said to "conform" to the trait. -Currently, the only characteristics that traits can define are method signatures. Also, traits -currently cannot implement default behaviors for methods. +Currently, the only characteristics that traits can define are method +signatures. Also, traits currently cannot implement default behaviors for +methods. Using traits allows you to write generic functions that can accept any type that conforms to a trait, rather than accept only specific types. @@ -355,7 +353,7 @@ from docstrings using the [`mojo doc` command](/mojo/cli/doc). ## Python integration -Mojo does not yet adopt the full syntax of Python, but we've built a mechanism to import +Mojo supports the ability to import Python modules as-is, so you can leverage existing Python code right away. For example, here's how you can import and use NumPy (you must have Python diff --git a/docs/manual/get-started.mdx b/docs/manual/get-started.mdx index 363fef9d10..469542567d 100644 --- a/docs/manual/get-started.mdx +++ b/docs/manual/get-started.mdx @@ -1,295 +1,1227 @@ --- -title: Get started with Mojo🔥 -sidebar_label: Get started -description: Install Mojo now and start developing +title: "Get started with Mojo" +sidebar_label: "Tutorial: Get started with Mojo" +description: "Install Mojo and learn the language basics by building a complete Mojo program" +github_url: https://github.com/modularml/mojo/tree/nightly/examples/life --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import MaxInstall from '@site/src/components/MaxInstall'; +import GetMagic from '@site/src/includes/get_magic.mdx'; -On this page, we'll show you how to create the classic "Hello world" starter -program with Mojo. If you'd rather read how to write Mojo code, see the -[introduction to Mojo](/mojo/manual/basics). +Get ready to learn Mojo! This tutorial is designed to give you a tour of several +features of Mojo by building a complete program that does much more than simply +printing "Hello, world!" -By installing Mojo, you understand and agree to our [software -license](https://www.modular.com/legal/max). +In fact, we'll build a version of [Conway's Game of +Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), which is a simple +simulation to explore self-replicating systems. If you haven't heard of it +before, don't worry, it will make sense when you see it in action. Let's just +get started so you can learn Mojo programming basics, including the following: -## 1. Create a new project +- Using basic built-in types like `Int` and `String` +- Using a `List` to manage a sequence of values +- Creating custom types in the form of structs (data structures) +- Creating and importing Mojo modules +- Importing and using Python libraries -To create a new Mojo project, we'll use [Magic](/magic)—a virtual environment -manager and package manager based on conda. +This tutorial might be a little long because there's a lot to learn, but we +tried to keep the explanations simple, and we included links along the way for +you to go learn more about each topic. -1. Install Magic on macOS or Ubuntu Linux with this command: +And if you just want to see the finished code, you can [get it on +GitHub](https://github.com/modularml/mojo/tree/nightly/examples/life). - +## 1. Create a Mojo project with `magic` - Then run the `source` command printed in your terminal. +We'll start by using the [`magic` CLI](/magic) to create a virtual environment +and generate our initial project directory. -2. Create a Mojo project called "hello-world": + - ```sh - magic init hello-world --format mojoproject - ``` +In your terminal, go to the directory in which you want to create the project +and execute: - This creates a directory named `hello-world` and installs the Mojo project - dependencies—the only dependency for a Mojo project is the `max` package - (Mojo is [bundled with MAX](/max/faq#why-bundle-mojo-with-max)). +```bash +magic init life --format mojoproject +``` -3. Start a shell in the project virtual environment: +This creates a project directory named `life`. Let's go to the directory and +list its contents: - ```sh - cd hello-world && magic shell - ``` +```bash +cd life +``` -That's it! The `magic shell` command activates the virtual environment so you -can now start using Mojo. For example, you can check your Mojo version like -this: +```bash +ls -A +``` -```sh -mojo --version +```output +.gitattributes +.gitignore +.magic +magic.lock +mojoproject.toml ``` +You should see that the project directory contains: -
    - Click here to install the Mojo nightly build. -
    +- An initial `mojoproject.toml` manifest file, which defines the project + dependencies and other features +- A [lock file](/magic#the-magiclock-file) named `magic.lock`, which specifies + the transitive dependencies and actual package versions installed in the + project's virtual environment + + :::note + + Never edit the lock file directly. The `magic` command automatically updates the lock file if you edit the manifest file. + + ::: + +- A `.magic` subdirectory containing the conda virtual environment for the + project +- Initial `.gitignore` and `.gitattributes` files that you can optionally use if + you plan to use `git` version control with the project -To install the latest nightly build, specify the `max-nightly` channel when you -initialize your project: +Because we used the `--format mojoproject` option when creating the project, +`magic` automatically added the `max` package as a dependency, which includes +Mojo. Let's verify that our project is configured correctly by checking the +version of Mojo that's installed within our project's virtual environment. +`magic run` executes a command in the project's virtual environment, so let's +use it to execute `mojo --version`: -```sh -magic init hello-world-nightly --format mojoproject \ - -c conda-forge -c https://conda.modular.com/max-nightly +```bash +magic run mojo --version ``` -When you include the `-c` (`--channel`) option, you must specify all channels. -Be sure to specify the `conda-forge` channel (or another conda channel) where -Magic should look for all packages other than MAX/Mojo. +You should see a version string indicating the version of Mojo installed, which +by default should be the latest released version. -Then start a shell in the new environment: +Great! Now let's write our first Mojo program. -```sh -cd hello-world-nightly && magic shell +## 2. Create a "Hello, world" program + +You can use any editor or IDE that you like. If you're using [Visual Studio +Code](https://code.visualstudio.com/) you can take advantage of the [Mojo for +Visual Studio Code +extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo), +which provides features like syntax highlighting, code completion, and debugging +support. + +In the project directory, create a file named `life.mojo` containing the +following lines of code: + +```mojo title="life.mojo" +# My first Mojo program! +def main(): + print("Hello, World!") ``` -The nightly version of Mojo installed in this project is fully independent, so -it will not interfere with other projects that use the latest stable build. +If you've programmed before in Python, this should look familiar. -
    -
    +- We're using the `def` keyword to define a function named `main`. +- You can use any number of spaces or tabs for indentation as long as you use + the same indentation for the entire code block. We'll follow the [Python style + guide](https://peps.python.org/pep-0008/) and use 4 spaces. +- This [`print()`](/mojo/stdlib/builtin/io/print) function a Mojo built-in so it + doesn't require an import. +An executable Mojo program *requires* you to define a no-argument `main()` as +its entry point. Running the program automatically invokes the `main()` +function, and your program exits when the `main()` function returns. -## 2. Run code in the REPL +To run the program, we first need to start a shell session in our project's +virtual environment: -First, let's use the Mojo -[REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), -which allows you to write and run Mojo code in a command prompt: +```bash +magic shell +``` + +Later on, when you want to exit the virtual environment, just type `exit`. -1. To start a REPL session, type `mojo` and press Enter. +Now we can use the `mojo` command to run our program. -2. Type `print("Hello, world!")` and press Enter twice -(a blank line is required to indicate the end of an expression). +```bash +mojo life.mojo +``` - The result looks like this: +```output +Hello, World! +``` - ```text - $ mojo - Welcome to Mojo! 🔥 +Mojo is a compiled language, not an interpreted one like Python. So when we run +our program like this, `mojo` performs [just-in-time +compilation](https://en.wikipedia.org/wiki/Just-in-time_compilation) (JIT) and +then runs the result. - Expressions are delimited by a blank line. - Type `:quit` to exit the REPL and `:mojo help repl` for further assistance. +We can also compile our program into an executable file using [`mojo +build`](/mojo/cli/build) like this: - 1> print("Hello world") - Hello world - ``` +```bash +mojo build life.mojo +``` -3. To exit REPL, type `:quit` and press Enter, or press -Ctrl + D. +By default, this saves an executable file to the current directory named `life`. -You can write as much code as you want in the REPL. You can press -Enter to start a new line and continue writing code, and when you -want Mojo to evaluate the code, press Enter twice. If there's -something to print, Mojo prints it and then returns the prompt to you. +```bash +./life +``` -The REPL is primarily useful for short experiments because the code isn't -saved. So when you want to write a real program, you need to write the code in -a `.mojo` source file. +```output +Hello, World! +``` -## 3. Run a Mojo file +## 3. Create and use variables -Now let's write the code in a Mojo source file and run it with the -[`mojo`](/mojo/cli/) command: +Let's extend this basic program by prompting the user for their name and +including that in the greeting printed. The built-in +[`input()`](/mojo/stdlib/builtin/io/input) function accepts an optional +[`String`](/mojo/stdlib/collections/string/String) argument to use as a prompt, +and returns a `String` consisting of the characters the user entered (with the +newline character at the end stripped off). -1. Create a file named `hello.mojo` (or `hello.🔥`) and add the following code: +So let's declare a variable, assign the return value from `input()` to it, and +build a customized greeting. - ```mojo - fn main(): - print("Hello, world!") - ``` +```mojo title="life.mojo" +def main(): + var name: String = input("Who are you? ") + var greeting: String = "Hi, " + name + "!" + print(greeting) +``` - That's all you need. Save the file and return to your terminal. +Go ahead and run it: -2. Now run it with the `mojo` command: +```bash +mojo life.mojo +``` - ```sh - mojo hello.mojo - ``` +```output +Who are you? Edna +Hi, Edna! +``` - ```output - Hello, world! - ``` +Notice that this code uses a `String` type annotation indicating the type of +value that the variable can contain. The Mojo compiler performs [static type +checking](https://en.wikipedia.org/wiki/Type_system#Static_type_checking), which +means that you'll encounter a compile-time error if your code tries to assign a +value of one type to a variable of a different type. + +Mojo also supports implicitly declared variables, where you simply assign a +value to a new variable without using the `var` keyword or indicating its type. +So we can replace the code we just entered with the following, and it works +exactly the same. + +```mojo title="life.mojo" +def main(): + name = input("Who are you? ") + greeting = "Hi, " + name + "!" + print(greeting) +``` -## 4. Build an executable binary +However, implicitly declared variables still have a fixed type, which Mojo +automatically infers from the initial value assignment. So in this example both +`name` and `greeting` are inferred as `String` type variables. If you then try +to assign an integer value like 42 to the `name` variable, you'll get a +compile-time error because of the type mismatch. You can learn more about Mojo +variables in the [Variables](/mojo/manual/variables) section of the Mojo manual. + +## 4. Use Mojo `Int` and `List` types to represent the game state + +As originally envisioned by John Conway, the game's "world" is an infinite, +two-dimensional grid of square cells, but for our implementation we'll constrain +the grid to a finite size. A drawback to making the edges of the grid a hard +boundary is that there are fewer neighboring cells around the edges compared to +the interior, which tends to cause die offs. Therefore, we'll model the world as +a toroid (a donut shape), where the top row is considered adjacent to the bottom +row, and the left column is considered adjacent to the right column. This will +come into play later when we implement the algorithm for calculating each +subsequent generation. + +To keep track of the height and width of our grid we'll use +[`Int`](/mojo/stdlib/builtin/int/Int), which represents a signed integer of the +[word size](https://en.wikipedia.org/wiki/Word_(computer_architecture)) of the +CPU, typically 32 or 64 bits. + +To represent the state of an individual cell, we'll represent the cell state +with an `Int` value of 1 (populated) or 0 (unpopulated). Later, when we need to +determine the number of populated neighbors surrounding a cell, we can simply +add the values of the neighboring cells. + +To represent the state of the entire grid, we need a [collection +type](/mojo/manual/types#collection-types). The most appropriate for this use +case is [`List`](/mojo/stdlib/collections/list/List), which is a +dynamically-sized sequence of values. + +All of the values in a Mojo `List` must be the same type so that the Mojo +compiler can ensure type safety. (For example, when we retrieve a value from a +`List[Int]`, the compiler knows that the value is an `Int` and can verify that +we then use it correctly). Mojo collections are implemented as [generic +types](https://en.wikipedia.org/wiki/Generic_programming), so that we can +indicate the type of values the specific collection will hold by specifying a +[type parameter](/mojo/manual/parameters/#parameterized-structs) in square +brackets like this: + +```mojo +# The List in row can contain only Int values +row = List[Int]() + +# The List in names can contain only String values +names = List[String]() +``` -Finally, let's build and run that same code as an executable: +We can also create a `List` with an initial set of values and let the compiler +infer the type. -1. Create an executable file with the [`build`](/mojo/cli/build) command: +```mojo +nums = List(12, -7, 64) # A List[Int] containing 3 Int values +``` - ```sh - mojo build hello.mojo - ``` +The Mojo `List` type includes the ability to append to the list, pop values out +of the list, and access list items using subscript notation. Here's a taste of +those operations: + +```mojo +nums = List(12, -7, 64) +nums.append(-937) +print("Number of elements in the list:", len(nums)) +print("Popping last element off the list:", nums.pop()) +print("First element of the list:", nums[0]) +print("Second element of the list:", nums[1]) +print("Last element of the list:", nums[-1]) +``` - The executable file uses the same name as the `.mojo` file, but - you can change that with the `-o` option. +```output +Number of elements in the list: 4 +Popping last element off the list: -937 +First element of the list: 12 +Second element of the list: -7 +Last element of the list: 64 +``` -2. Then run the executable: +We can also nest `List`s: + +```mojo +grid = List( + List(11, 22), + List(33, 44) +) +print("Row 0, Column 0:", grid[0][0]) +print("Row 0, Column 1:", grid[0][1]) +print("Row 1, Column 0:", grid[1][0]) +print("Row 1, Column 1:", grid[1][1]) +``` - ```sh - ./hello - ``` +```output +Row 0, Column 0: 11 +Row 0, Column 1: 22 +Row 1, Column 0: 33 +Row 1, Column 1: 44 +``` - ```output - Hello, world! - ``` +This looks like a good way to represent the state of the grid for our program. +So let's update the `main()` function with the following code that defines an +8x8 grid containing the initial state of a +"[glider](https://en.wikipedia.org/wiki/Glider_(Conway%27s_Game_of_Life))" +pattern. + +```mojo title="life.mojo" +def main(): + num_rows = 8 + num_cols = 8 + glider = List( + List(0, 1, 0, 0, 0, 0, 0, 0), + List(0, 0, 1, 0, 0, 0, 0, 0), + List(1, 1, 1, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + ) +``` -The [`build`](/mojo/cli/build) command creates a statically compiled binary -file, so it contains all the code and libraries it needs to run. +## 5. Create and use a function to print the grid + +Now let's create a function to generate a string representation of the game grid +that we can print it to the terminal. + +There are actually two different keywords that we can use to define functions in +Mojo: `def` and `fn`. Using `fn` gives us finer level control over the function +definition, whereas `def` provides a good set of default behaviors for most use +cases. + +Let's add the following definition of a function named `grid_str()` to our +program. The Mojo compiler doesn't care whether we add our function before or +after `main()`, but the convention is to put `main()` at the end. + +```mojo title="life.mojo" +def grid_str(rows: Int, cols: Int, grid: List[List[Int]]) -> String: + # Create an empty String + str = String() + + # Iterate through rows 0 through rows-1 + for row in range(rows): + # Iterate through columns 0 through cols-1 + for col in range(cols): + if grid[row][col] == 1: + str += "*" # If cell is populated, append an asterisk + else: + str += " " # If cell is not populated, append a space + if row != rows-1: + str += "\n" # Add a newline between rows, but not at the end + return str +``` -You can now deactivate the virtual environment by just typing `exit`: +When we pass a value to a Mojo function, the default behavior for `def` is that +an argument is treated as a read-only reference to the value. However, if the +Mojo compiler determines that there is code in the function that can change the +value, then the argument gets a copy of the original value assigned to it. As +we'll see later, we can specify a different behavior by including an explicit +[argument convention](/mojo/manual/values/ownership#argument-conventions). In +contrast, when you define a function with `fn` Mojo simply treats each argument +as a read-only reference by default unless you provide an explicit argument +convention. + +Each argument name is followed by a type annotation indicating the type of value +you can pass to the argument. Just like when you're assigning a value to a +variable, you'll encounter a compile-time error if your code tries to pass a +value of one type to an argument of a different type. Finally, the `-> String` +following the argument list indicates that this function has a `String` type +return value. + +In the body of the function, we generate a `String` by appending an asterisk for +each populated cell and a space for each unpopulated cell, separating each row +of the grid with a newline character. We use nested `for` loops to iterate +through each row and column of the grid, using +[`range()`](/mojo/stdlib/builtin/range/range) to generate a sequence of integers +from 0 up to but not including the given end value. Then we append the correct +characters to the `String` representation. See [Control +flow](/mojo/manual/control-flow) for more information on `if`, `for`, and other +control flow structures in Mojo. -```sh -exit +:::note + +As described in [The `for` +statement](/mojo/manual/control-flow#the-for-statement) section of the Mojo +manual, it's possible to iterate over the elements of a `List` directly instead +of iterating over the values of a `range()` and then accessing the `List` +elements by their numeric index. However, iterating over a `List` directly +currently returns a *reference* to the element, which then requires using the +dereference operator, `[]`, to access the actual element value. The code looks +like this: + +```mojo +nums = List(12, -7, 64) +for value in nums: + print("Value:", value[]) ``` -Now let's try running an existing code example. +This behavior is likely to change in the future, at which point iterating over a +`List` won't require using the dereference operator. But for this tutorial, +we'll stick with iterating over a `range()` and accessing the `List` elements by +their numeric index. + +::: + +Now that we've defined our `grid_str()` function, let's invoke it from `main()`. -## 5. Run an example from GitHub +```mojo title="life.mojo" +def main(): + ... + result = grid_str(num_rows, num_cols, glider) + print(result) +``` -Our Mojo code examples in GitHub include a Magic configuration file so you can -simply clone the repo and run the code with `magic`. For example: +Then run the program to see the result: -1. Clone the Mojo repo: +```bash +mojo life.mojo +``` - ```sh - git clone https://github.com/modularml/mojo.git - ``` +```output + * + * +*** - Only if you installed the nightly build, also checkout the nightly branch: - ```sh - git checkout nightly - ``` -2. Navigate to the examples: - ```sh - cd mojo/examples - ``` -3. Run some code: +``` - ```sh - magic run mojo hello_interop.mojo - ``` +We can see that the position of the asterisks matches the location of the 1s in +the `glider` grid. - ```output - Hello Mojo 🔥! - 9 - 6 - 3 - Hello from Python! - I can even print a numpy array: [1 2 3] - ``` +## 6. Create a module and define a custom type -## 6. Install our VS Code extension (optional) +We're currently passing three arguments to `grid_str()` to describe the size and +state of the grid to print. A better approach would be to define our own custom +type that encapsulates all information about the grid. Then any function that +needs to manipulate a grid can accept just a single argument. We can do this by +defining a Mojo *struct*, which is a custom data structure. -To provide a first-class developer experience with features like code -completion, quick fixes, and hover help, we've created a [Mojo extension for -Visual Studio -Code](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). +A [Mojo struct](/mojo/manual/structs) is a custom type consisting of: -![](./images/mojo-vscode.png) +- Fields, which are variables containing the data associated with the structure +- Methods, which are functions that we can optionally define to manipulate + instances of the struct that we create +:::note -## Update Mojo +Mojo structs are similar to classes. However, Mojo structs do *not* support +inheritance. Mojo doesn't support classes at this time. -To update the Mojo version in your project, use `magic add` -and specify the `max` package version. +::: -For example, if you want to always use the latest version of Mojo, you can use -the `*` wildcard as the version and then simply run `magic update` (you must -run `magic add` within the project path): +We could define the struct in our existing `life.mojo` source file, but let's +create a separate *module* for the struct. A module is simply a Mojo source file +containing struct and function definitions that can be imported into other Mojo +source files. To learn more about creating and importing modules, see the +[Modules and packages](/mojo/manual/packages) section of the Mojo manual . + +So create a new source file named `gridv1.mojo` in the project directory +containing the following definition of a struct named `Grid` consisting of three +fields: + +```mojo title="gridv1.mojo" +@value +struct Grid(): + var rows: Int + var cols: Int + var data: List[List[Int]] +``` -```sh -cd hello-world +Mojo requires you to declare all of the fields in the struct definition. You +can't add fields dynamically at run-time. You must declare the type for each +field, but you cannot assign a value as part of the field declaration. Instead, +the [constructor](/mojo/manual/lifecycle/life#constructor) is responsible for +initializing the value of all fields. + +Mojo structs support several different [lifecycle +methods](/mojo/manual/lifecycle/) defining the behavior when an instance of the +struct is created, moved, copied, and destroyed. For structs that are basic +aggregations of other types and don't require custom resource management or +lifecycle behaviors, you can simply add the +[`@value`](/mojo/manual/structs#value-decorator) decorator to your struct +definition to have the Mojo compiler automatically generate lifecycle methods +for you. + +Because we used the `@value` decorator, `Grid` includes a "member-wise" +[constructor](/mojo/manual/lifecycle/life#constructor) . The constructor's +arguments are the same names and types as the struct's fields and appear in the +same order. So this means that we can create an instance of `Grid` like this: + +```mojo +my_grid = Grid(2, 2, List(List(0, 1), List(1, 1))) ``` -```sh -magic add max +We can then access the field values with "dot" syntax like this: + +```mojo +print("Rows:", my_grid.rows) ``` -```sh -magic update +```output +Rows: 2 ``` -:::note +## 7. Import a module and use our custom `Grid` type + +Now let's edit `life.mojo` to import `Grid` from our new module and update our +code to use it. + +```mojo title="life.mojo" +from gridv1 import Grid + +def grid_str(grid: Grid) -> String: + # Create an empty String + str = String() + + # Iterate through rows 0 through rows-1 + for row in range(grid.rows): + # Iterate through columns 0 through cols-1 + for col in range(grid.cols): + if grid.data[row][col] == 1: + str += "*" # If cell is populated, append an asterisk + else: + str += " " # If cell is not populated, append a space + if row != grid.rows - 1: + str += "\n" # Add a newline between rows, but not at the end + return str + +def main(): + glider = List( + List(0, 1, 0, 0, 0, 0, 0, 0), + List(0, 0, 1, 0, 0, 0, 0, 0), + List(1, 1, 1, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + List(0, 0, 0, 0, 0, 0, 0, 0), + ) + start = Grid(8, 8, glider) + result = grid_str(start) + print(result) +``` + +At this point we've made several changes to improve the structure of our +program, but the output should remain the same. + +```bash +mojo life.mojo +``` + +```output + * + * +*** + -Although the wildcard option allows `magic update` to always install the latest -version, it also updates the `magic.lock` file in your project with the -explicit version you've installed. This ensures that anybody else who -initializes the project also gets the same package version (until you run -`magic update` again). -::: -To be more specific with your package version, you can use any of the [Python -package version -specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#id5). -For example: -```sh -magic add "max~=24.4" ``` -```sh -magic add "max>=24.4,<24.5" +## 8. Implement `grid_str()` as a method + +Our `grid_str()` function is really a utility function unique to the `Grid` +type. So rather than defining it as a standalone function, it makes more sense +to define it as part of the `Grid` type as a method. + +To do so, move the function into `gridv1.mojo` and edit it to look like this (or +simply copy the code below into `gridv1.mojo`): + +```mojo title="gridv1.mojo" +@value +struct Grid(): + var rows: Int + var cols: Int + var data: List[List[Int]] + + def grid_str(self) -> String: + # Create an empty String + str = String() + + # Iterate through rows 0 through rows-1 + for row in range(self.rows): + # Iterate through columns 0 through cols-1 + for col in range(self.cols): + if self.data[row][col] == 1: + str += "*" # If cell is populated, append an asterisk + else: + str += " " # If cell is not populated, append a space + if row != self.rows - 1: + str += "\n" # Add a newline between rows, but not at the end + return str ``` +So aside from moving the code from one source file to another, there are a few +other changes that we made. + +- The function definition is indented to indicate that it's a method defined by + the `Grid` struct. This also changes the way that we invoke the function. + Instead of `grid_str(my_grid)` we now write `my_grid.grid_str()`. +- We've changed the argument name to `self`. When you invoke an instance method, + Mojo automatically passes the instance as the first argument, followed by any + explicit arguments that you provide. Although we could use any name we like + for this argument, the convention is to call it `self`. +- We've deleted the argument's type annotation. The compiler knows that the + first argument of the method is an instance of the struct, so it doesn't + require an explicit type annotation. + +Now that we've refactored the function into an instance method, we also need to +update the code in `life.mojo` where we invoke it from `main()`: + +```mojo title="life.mojo" +def main(): + ... + start = Grid(8, 8, glider) + print(start.grid_str()) +``` + +Once again, our refactoring has improved the structure of our code, but it still +produces the same output. You can verify that by running the program again. + +## 9. Implement support for the `StringableRaising` trait + +You can pass most Mojo types to the built-in `str()` function to produce a +`String` representation of that instance. But you'll get an error if you try to +do that with our current implementation of `Grid`. So let's fix that. + +Because the Mojo compiler performs static type checking, a function like `str()` +can accept a value only if its type implements some required behavior—in this +case, it only accepts types that can generate a `String` representation. + +To enable that, Mojo supports [*traits*](/mojo/manual/traits). A trait is a set +of requirements in the form of one or more method signatures. A type can +*conform* to that trait by implementing all of the method signatures declared in +the trait. Then we can have a function that indicates that it accepts values of +any type that conform to a specified trait. (This type of function is sometimes +referred to as a [*generic* +function](/mojo/manual/parameters/#parameters-and-generics).) + +In the case of `str()`, it requires a type to conform to either the `Stringable` +or `StringableRaising` trait. Each trait requires a conforming type to implement +a `__str__()` method that returns a `String` representation. The only difference +between the two traits is that `Stringable` requires that the method *cannot* +raise and error, whereas `StringableRaising` indicates that the method *might* +raise an error. (To learn more, read [The `Stringable`, `Representable`, and +`Writable` +traits](/mojo/manual/traits#the-stringable-representable-and-writable-traits).) + +Our `grid_str()` method already returns a `String` representation, so it looks +like we just have to rename it to `__str__()`. But we also need to indicate +which trait `Grid` conforms to. In our case, it's `StringableRaising` because we +used `def` to define the method. If you define a function or method with `def`, +the compiler *always* assumes that the function *can* raise an error. In +contrast, if you define a function or method with `fn` you must explicitly +indicate with a `raises` keyword if it can raise an error. + +So in `gridv1.mojo` we need to update the `Grid` declaration to indicate that +the type conforms to `StringableRaising` and rename the `grid_str()` method to +`__str__()`: + +```mojo title="gridv1.mojo" +@value +struct Grid(StringableRaising): + ... + def __str__(self) -> String: + ... +``` + +Now let's verify that `str()` works with an instance of `Grid`. + +```mojo title="life.mojo" +def main(): + ... + start = Grid(8, 8, glider) + print(str(start)) +``` + +If you run the program again, you should still see the same glider pattern as before. + +```bash +mojo life.mojo +``` + +```output + * + * +*** -## Next steps -- If you're new to Mojo, we suggest you learn the language basics in the - [introduction to Mojo](/mojo/manual/basics). -- To learn more about the `magic` tool, read [Get started with Magic](/magic/). -- Explore more code examples in the [the Mojo -repo](https://github.com/modularml/mojo/). In addition to several `.mojo` -examples, the repo includes [Jupyter -notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) -that teach advanced Mojo features. -- To see all the available Mojo APIs, check out the [Mojo standard library - reference](/mojo/lib). +``` + +## 10. Implement methods to support indexing + +Looking at the implementation of `__str__()` you'll notice that we use +`self.data[row][col]` to retrieve the value of a cell in the grid. And if +`my_grid` is an instance of `Grid`, we would use `my_grid.data[row][col]` to +refer to a cell in the grid. This breaks a fundamental principle of +encapsulation in that we need to know that `Grid` stores the game state in a +field called `data`, and that field is a `List[List[Int]]`. If we later decide +to change the internal implementation of `Grid`, then there could be a lot of +code that would need to be changed. + +A cleaner approach is to provide "getter" and "setter" methods to access cell +values. We could simply define methods like `get_cell()` and `set_cell()`, but +this is a good opportunity to show how we can define the behavior of built-in +operators for custom Mojo types. Specifically, we'll implement support for +indexing, so that we can refer to a cell with syntax like `my_grid[row, col]`. +This will be useful when we implement support for evolving the state of the +grid. + +As described in [Operators, expressions, and dunder +methods](/mojo/manual/operators), Mojo allows us to define the behavior of many +of the built-in operators for a custom type by implementing special *dunder* +(double underscore) methods. In the case of indexing, the two methods are +`__getitem__()` and `__setitem__()`. So let's add the following methods to the +`Grid` struct in `gridv1.mojo`: + +```mojo title="gridv1.mojo" +@value +struct Grid(StringableRaising): + ... + def __getitem__(self, row: Int, col: Int) -> Int: + return self.data[row][col] + + def __setitem__(mut self, row: Int, col: Int, value: Int) -> None: + self.data[row][col] = value +``` + +The implementation of `__getitem__()` is easy. For the given values of `row` and +`col` we just need to retrieve and return the corresponding value from the +nested `List[List[Int]]` stored in the `data` field of the instance. + +The body of `__setitem__()` is similarly straightforward. We just take the given +`value` and store it in the corresponding `row` and `col` in `data`. One thing +new in the declaration is that we set the return type to `None` to indicate that +the method doesn't have a return value. But more notable is that we've added the +`mut` [argument convention](/mojo/manual/values/ownership#argument-conventions) +to the `self` argument to explicitly tell the Mojo compiler that we want to +mutate the state of the current instance. If we were to omit `mut`, we would get +an error because the compiler would default to read-only access for the +argument. + +Now that we've implemented these methods, we can update `__str__()` to use +indexing syntax to access the cell value. + +```mojo title="gridv1.mojo" +@value +struct Grid(StringableRaising): + ... + def __str__(self) -> String: + ... + # Iterate through columns 0 through cols-1 + for col in range(self.cols): + if self[row, col] == 1: + ... +``` + +
    + Click here to see the complete `gridv1.mojo` so far: + +```mojo title="gridv1.mojo" +import random + +@value +struct Grid(StringableRaising): + var rows: Int + var cols: Int + var data: List[List[Int]] + + def __str__(self) -> String: + # Create an empty String + str = String() + + # Iterate through rows 0 through rows-1 + for row in range(self.rows): + # Iterate through columns 0 through cols-1 + for col in range(self.cols): + if self[row, col] == 1: + str += "*" # If cell is populated, append an asterisk + else: + str += " " # If cell is not populated, append a space + if row != self.rows - 1: + str += "\n" # Add a newline between rows, but not at the end + return str + + def __getitem__(self, row: Int, col: Int) -> Int: + return self.data[row][col] + + def __setitem__(mut self, row: Int, col: Int, value: Int) -> None: + self.data[row][col] = value +``` + +
    + +Our refactoring hasn't changed our program's behavior, but it's still a good +idea to run it to be sure that we don't have any errors in our code. + +## 11. Define a static method to generate random grids + +So far, we've used the glider to build the basic functionality of our `Grid` +type. But what's much more interesting is to start with a grid in a random state +and see how it evolves over time. + +Let's add a *static method* named `random()` to the `Grid` struct to generate +and return an instance of `Grid` with a random state. A static method doesn't +operate on specific instances of the type, so it can be invoked as a utility +function. We indicate that a method is a static method by using the +`@staticmethod` decorator. + +```mojo title="gridv1.mojo" +import random + +@value +struct Grid(StringableRaising): + ... + @staticmethod + def random(rows: Int, cols: Int) -> Self: + # Seed the random number generator using the current time. + random.seed() + + data = List[List[Int]]() + + for row in range(rows): + row_data = List[Int]() + for col in range(cols): + # Generate a random 0 or 1 and append it to the row. + row_data.append(int(random.random_si64(0, 1))) + data.append(row_data) + + return Self(rows, cols, data) +``` + +At the top of the file we're importing the `random` package from the Mojo +standard library. It includes several functions related to random number +generation. + +By default, the [pseudorandom number +generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) used by +the Mojo standard library currently uses a fixed seed. This means that it +generates the same sequence of numbers unless you provide a different seed, +which is useful for testing purposes. But for this application we want to call +`random.seed()` to set a seed value based on the current time, which gives us a +unique value every time. + +Then we create an empty `List[List[Int]]` that we populate with a random initial +state. For each cell, we call +[`random.random_si64()`](/mojo/stdlib/random/random/random_si64), which returns +a random integer value from the provided minimum and maximum values of 0 and 1, +respectively. This function actually returns a value of type `Int64`, which is a +signed 64-bit integer value. As described in [Numeric +types](/mojo/manual/types#numeric-types), this is *not* the same as the `Int` +type whose precision is dependent on the native word size of the system. +Therefore we're passing this value to the built-in +[`int()`](/mojo/stdlib/builtin/int/int-function/) function, which explicitly +converts a numeric value to an `Int`. + +The return type of the method is `Self`, which is an alias for the type of the +struct. This is a convenient shortcut if the actual name of the struct is long +or includes parameters. + +The last line uses `Self()` to invoke the struct's constructor and return a +newly created instance with random data. + +Now we can update the `main()` function in `life.mojo` to create a random `Grid` +and print it. + +```mojo title="life.mojo" +... + +def main(): + start = Grid.random(8, 16) + print(str(start)) +``` + +Run the program a few times to verify that it generates a different grid each +time. + +```bash +mojo life.mojo +``` + +```output +*** * **** +* **** ****** +* * ***** +* * ** ** + * * ** **** +* ** * * * *** + * * ** ** ** + * ***** ** +``` + +## 12. Implement a method to evolve the grid + +It's finally time to let our world evolve. We'll implement an `evolve()` method +to calculate the state of the grid for the next generation. One option would be +to do an in-place modification of the existing `Grid` instance. But instead +we'll have `evolve()` return a new instance of `Grid` for the next generation. + +```mojo title="gridv1.mojo" +... +struct Grid(StringableRaising): + ... + def evolve(self) -> Self: + next_generation = List[List[Int]]() + + for row in range(self.rows): + row_data = List[Int]() + + # Calculate neighboring row indices, handling "wrap-around" + row_above = (row - 1) % self.rows + row_below = (row + 1) % self.rows + + for col in range(self.cols): + # Calculate neighboring column indices, handling "wrap-around" + col_left = (col - 1) % self.cols + col_right = (col + 1) % self.cols + + # Determine number of populated cells around the current cell + num_neighbors = ( + self[row_above, col_left] + + self[row_above, col] + + self[row_above, col_right] + + self[row, col_left] + + self[row, col_right] + + self[row_below, col_left] + + self[row_below, col] + + self[row_below, col_right] + ) + + # Determine the state of the current cell for the next generation + new_state = 0 + if self[row, col] == 1 and (num_neighbors == 2 or num_neighbors == 3): + new_state = 1 + elif self[row, col] == 0 and num_neighbors == 3: + new_state = 1 + row_data.append(new_state) + + next_generation.append(row_data) + + return Self(self.rows, self.cols, next_generation) +``` + +We start out with an empty `List[List[Int]]` to represent the state of the next +generation. Then we use nested `for` loops to iterate over each row and each +column of the existing `Grid` to determine the state of each cell in the next +generation. + +For each cell in the grid we need to count the number of populated neighboring +cells. Because we're modeling the world as a toroid, we need to consider the top +and bottom rows as adjacent and the left-most and right-most columns as +adjacent. So as we iterate through each row and column, we're using the modulo +operator, `%`, to handle "wrap-around" when we calculate the indices of the rows +above and below and the columns to the left and right of the current cell. (For +example, if there are 8 rows, then `-1 % 8` is 7.) + +Then we apply the Game of Life rules that determines if the current cell is +populated (1) or unpopulated (0) for the next generation: + +- A populated cell with either 2 or 3 populated neighbors remains populated in + the next generation +- An unpopulated cell with exactly 3 populated neighbors becomes populated in + the next generation +- All other cells become unpopulated in the next generation + +After calculating the state of the next generation, we use `Self()` to create an +new instance of `Grid`, and return the newly created instance. + +Now that we can evolve the grid, let's use it in `life.mojo`. We'll add a +`run_display()` function to control the game's main loop: + +- Display the current `Grid` +- Prompt the user to continue or quit +- Break out of the loop if the user enters `q` +- Otherwise, calculate the next generation and loop again + +Then we'll update `main()` to create a random initial `Grid` and pass it to +`run_display()`. Here is the updated version of `life.mojo`: + +```mojo title="life.mojo" +from gridv1 import Grid + +def run_display(owned grid: Grid) -> None: + while True: + print(str(grid)) + print() + if input("Enter 'q' to quit or press to continue: ") == "q": + break + grid = grid.evolve() + +def main(): + start = Grid.random(16, 16) + run_display(start) +``` + +Run the program and verify that each call to `evolve()` successfully produces a +new generation. -If you have issues during install, check our [known -issues](/mojo/roadmap#mojo-sdk-known-issues). +So now we have a working version of the Game of Life, but the terminal interface +is not very pretty. Let's spice things up with a nicer graphical user interface, +using a Python library. + +## 13. Import and use a Python package + +Mojo lets you import Python modules, call Python functions, and interact with +Python objects from Mojo code. To demonstrate this capability, we're going to +use a Python package called [pygame](https://www.pygame.org) to create and +manage a graphical user interface for our Game of Life program. + +First, we need to update our `mojoproject.toml` file to add a dependency on +Python and the `pygame` package. So in the project directory, execute the +following command from the terminal: + +```bash +magic add "python>=3.11,<3.13" "pygame>=2.6.1,<3" +``` + +:::note + +When you use Python code and packages as part of your Mojo program, you create a +run-time dependency on a compatible Python runtime and packages. Building an +executable version of your program with `mojo build` does *not* incorporate a +Python runtime or Python packages into the resulting executable file. These +run-time Python dependencies must be provided by the environment where you run +the executable. The easiest way to ensure this requirement is to deploy and run +your Mojo executable in a virtual environment, such as one managed by +[Magic](/magic/) or [conda](https://docs.conda.io/). + +::: + +You can import a Python module in Mojo using +[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module). +This returns a reference to the module in the form of a `PythonObject` wrapper. +You must store the reference in a variable so that you can then access the +functions and objects in the module. For example: + +```mojo +from python import Python + +def run_display(): + # This is roughly equivalent to Python's `import pygame` + pygame = Python.import_module("pygame") + pygame.init() +``` :::note -To help us improve Mojo, we collect some basic system information and -crash reports. [Learn -more](/mojo/faq#does-the-mojo-sdk-collect-telemetry). +Because Mojo doesn't support globally scoped variables, you must either import a +Python module into each Mojo function that needs to use it or else pass the +`PythonObject` wrapped module as an argument between functions. ::: + +You can learn more about importing and using Python modules in Mojo by reading +[Python integration](/mojo/manual/python/). + +Once we import `pygame`, we can call its APIs as if we were writing Python code. +For this project, we'll use `pygame` to create a new window and draw the whole +game UI. This requires a complete rewrite of the `run_display()` function. Take +a look at the updated code for `life.mojo` and we'll explain more of it below: + +```mojo title="life.mojo" +from gridv1 import Grid +from python import Python +import time + +def run_display( + owned grid: Grid, + window_height: Int = 600, + window_width: Int = 600, + background_color: String = "black", + cell_color: String = "green", + pause: Float64 = 0.1, +) -> None: + # Import the pygame Python package + pygame = Python.import_module("pygame") + + # Initialize pygame modules + pygame.init() + + # Create a window and set its title + window = pygame.display.set_mode((window_height, window_width)) + pygame.display.set_caption("Conway's Game of Life") + + cell_height = window_height / grid.rows + cell_width = window_width / grid.cols + border_size = 1 + cell_fill_color = pygame.Color(cell_color) + background_fill_color = pygame.Color(background_color) + + running = True + while running: + # Poll for events + event = pygame.event.poll() + if event.type == pygame.QUIT: + # Quit if the window is closed + running = False + elif event.type == pygame.KEYDOWN: + # Also quit if the user presses or 'q' + if event.key == pygame.K_ESCAPE or event.key == pygame.K_q: + running = False + + # Clear the window by painting with the background color + window.fill(background_fill_color) + + # Draw each live cell in the grid + for row in range(grid.rows): + for col in range(grid.cols): + if grid[row, col]: + x = col * cell_width + border_size + y = row * cell_height + border_size + width = cell_width - border_size + height = cell_height - border_size + pygame.draw.rect( + window, cell_fill_color, (x, y, width, height) + ) + + # Update the display + pygame.display.flip() + + # Pause to let the user appreciate the scene + time.sleep(pause) + + # Next generation + grid = grid.evolve() + + # Shut down pygame cleanly + pygame.quit() + +def main(): + start = Grid.random(128, 128) + run_display(start) +``` + +Each argument for `run_display()` other than `grid` has a default value +associated with it (for example, the default `window_height` is 600 pixels). If +you don't explicitly pass a value for an argument when you invoke +`run_display()`, Mojo uses the default value specified in the function +definition. + +After importing the `pygame` module, we call `pygame.init()` to initialize all +the pygame subsystems. + +The `set_mode()` function creates and initializes a window, with the height and +width passed as a Mojo tuple of two values. This returns a +[`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) wrapper for the +window, which we can then use to call functions and set attributes to manipulate +the window. (For more information about interacting with Python objects from +Mojo, see [Python types](/mojo/manual/python/types).) + +The bulk of the `run_display()` function is a loop that uses `pygame` to poll +for events like key presses and mouse clicks. If it detects that the user +presses `q` or the `` key or closes the display window, it ends the +program with `pygame.quit()`. Otherwise, it clears the window and then iterates +through all cells in the grid to display the populated cells. After sleeping for +`pause` seconds, it evolves the grid to the next generation and loops again. + +So it's finally time to try it out. + +```bash +mojo life.mojo +``` + +Now when you run the program you should see a new window appear on screen +displaying your evolving grid. We now have a fully functional implementation of +the Game of Life with a nice interface. We've come quite a way from just +displaying a few asterisks on the terminal! + +![game_of_life_screen.png](images/game-of-life-screen.png) + +To quit the program press the `q` or `` key, or close the window. + +And now that we're done with the tutorial, exit our project's virtual +environment: + +```bash +exit +``` + +## Summary + +Congratulations on writing a complete Mojo application from scratch! Along the +way, you got a taste of: + +- Using Magic to create, build, and run a Mojo program +- Using Mojo built-in types like `Int`, `String`, and `List` +- Creating and using variables and functions +- Using control structures like `if`, `while`, and `for` +- Defining and using a custom Mojo struct +- Creating and importing a Mojo module +- Using modules from the Mojo standard library +- Importing and using a Python module + +## Next steps + +The [Mojo manual](/mojo/manual/) and the Mojo standard library [API +reference](/mojo/lib) contains much more information about these and other +features of Mojo programming. These resources will help prepare you to take full +advantage of the [MAX platform](/max/) to build and deploy high-performance AI +pipelines. diff --git a/docs/manual/images/game-of-life-screen.png b/docs/manual/images/game-of-life-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..d4e813e6081097367d3a553e55d85b1564c1cb06 GIT binary patch literal 85325 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 A5&!@I literal 0 HcmV?d00001 From bb5fe6e4f09aea44228d11e1a705a7d0cd15b418 Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:32:59 -0800 Subject: [PATCH 2009/2019] Updated the Mojo manual's Python types page to reflect support for the `in` and `is` operators with `PythonObject` values. MODULAR_ORIG_COMMIT_REV_ID: 4f58b6a25ba4138a25fddf691e7aaaf1fb333d02 --- docs/manual/python/types.mdx | 182 +++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 71 deletions(-) diff --git a/docs/manual/python/types.mdx b/docs/manual/python/types.mdx index a7d25d0f9a..4ef8bee6aa 100644 --- a/docs/manual/python/types.mdx +++ b/docs/manual/python/types.mdx @@ -12,26 +12,28 @@ method. ## Mojo types in Python -Mojo primitive types implicitly convert into Python objects. -Today we support lists, tuples, integers, floats, booleans, and strings. +Mojo primitive types implicitly convert into Python objects. Today we support +integers, floats, booleans, strings, tuples, and `ListLiteral` instances +(described below in [Mojo wrapper objects](#mojo-wrapper-objects)). -For example, given this Python function that prints Python types: +To demonstrate, the following example dynamically creates an in-memory Python +module named `py_utils` containing a `type_printer()` function, which simply +prints the type of a given value. Then you can see how different Mojo values +convert into corresponding Python types. ```mojo -%%python +from python import Python + +def main(): + py_module = """ def type_printer(value): print(type(value)) -``` - -(You can ignore the `%%python` at the start of the code sample; it's explained -in the note below.) - -You can pass this Python function Mojo types with no problem: +""" + py_utils = Python.evaluate(py_module, file=True, name="py_utils") -```mojo -type_printer(4) -type_printer(3.14) -type_printer(("Mojo", True)) + py_utils.type_printer(4) + py_utils.type_printer(3.14) + py_utils.type_printer(("Mojo", True)) ``` ```output @@ -40,15 +42,6 @@ type_printer(("Mojo", True)) ``` -:::note - -This is a simplified code example written as a set of Jupyter -notebook cells. The first cell includes the `%%python` directive so it's -interpreted as Python. The second cell includes top-level Mojo code. You'd need -to adjust this code to run it elsewhere. - -::: - ## Python types in Mojo You can also use Python objects from Mojo. For example, Mojo's @@ -58,18 +51,21 @@ heterogeneous collections. One alternative is to use a Python dictionary or list. For example, to create a Python dictionary, use the -[`dict()`](/mojo/stdlib/python/python/Python#dict) method: +[`Python.dict()`](/mojo/stdlib/python/python/Python#dict) method: ```mojo from python import Python -def use_dict(): - var dictionary = Python.dict() - dictionary["item_name"] = "whizbang" - dictionary["price"] = 11.75 - dictionary["inventory"] = 100 - print(dictionary) +def main(): + py_dict = Python.dict() + py_dict["item_name"] = "whizbang" + py_dict["price"] = 11.75 + py_dict["inventory"] = 100 + print(py_dict) +``` +```output +{'item_name': 'whizbang', 'price': 11.75, 'inventory': 100} ``` ### Mojo wrapper objects @@ -81,39 +77,69 @@ methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them through to the underlying Python object. You can explicitly create a wrapped Python object by initializing a -`PythonObject` with a Mojo literal: +`PythonObject` with a Mojo literal. Most of the time, you can treat the wrapped +object just like you'd treat it in Python. You can use dot-notation to access +attributes and call methods, and use the `[]` operator to access an item in a +sequence. For example: ```mojo from python import PythonObject -var py_list: PythonObject = [1, 2, 3, 4] +def main(): + var py_list: PythonObject = ["cat", 2, 3.14159, 4] # A ListLiteral + n = py_list[2] + print("n =", n) + py_list.append(5) + py_list[0] = "aardvark" + print(py_list) ``` -Most of the time, you can treat the wrapped object just like you'd treat it in -Python. You can use Python's `[]` operators to access an item in a list, and use -dot-notation to access attributes and call methods. For example: +```output +n = 3.14159 +['aardvark', 2, 3.14159, 4, 5] +``` -```mojo -var n = py_list[2] -py_list.append(5) +:::note + +In the example above, `["cat", 2, 3.14159, 4]` is an instance of +[`ListLiteral`](/mojo/stdlib/builtin/builtin_list/ListLiteral/), which supports +lists of heterogeneous values. The `ListLiteral` type is intended only for +Python interoperability, and it is not compatible with the native Mojo +[`List`](/mojo/stdlib/collections/list/List) type. For example, the following +line results in a compilation error: + +``` +var mojo_list: List[Int] = [1, 2, 3, 4] +``` + +```output +error: cannot implicitly convert 'ListLiteral[Int, Int, Int, Int]' value to 'List[Int]' +var mojo_list: List[Int] = [1, 2, 3, 4] + ^~~~~~~~~~~~ ``` +::: + If you want to construct a Python type that doesn't have a literal Mojo equivalent, you can also use the [`Python.evaluate()`](/mojo/stdlib/python/python/Python#evaluate) method. For example, to create a Python `set`: ```mojo -def use_py_set(): - var py_set = Python.evaluate('set([2, 3, 5, 7, 11])') - var num_items = len(py_set) - print(num_items, " items in set.") # prints "5 items in set" - print(py_set.__contains__(6)) # prints "False" +from python import Python + +def main(): + var py_set = Python.evaluate('{2, 3, 2, 7, 11, 3}') + num_items = len(py_set) + print(num_items, "items in the set.") + contained = 7 in py_set + print("Is 7 in the set:", contained) ``` -TODO: You should be able to use the expression `6 in py_set`. However, because -of the way `PythonObject` currently works, you need to call the -`__contains__()` method directly. +```output +4 items in the set. +Is 7 in the set: True +``` Some Mojo APIs handle `PythonObject` just fine, but sometimes you'll need to explicitly convert a Python value into a native Mojo value. @@ -121,12 +147,11 @@ explicitly convert a Python value into a native Mojo value. Currently `PythonObject` conforms to the [`Intable`](/mojo/stdlib/builtin/int/Intable), [`Stringable`](/mojo/stdlib/builtin/str/Stringable), and -[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) traits, which -means you can convert Python values to Mojo `Int`, `String`, and `Bool` types -using the built-in -[`int()`](/mojo/stdlib/builtin/int/int-function), -[`str()`](/mojo/stdlib/builtin/str/str), -and [`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python +[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) traits, which means you can +convert Python values to Mojo `Int`, `String`, and `Bool` types using the +built-in [`int()`](/mojo/stdlib/builtin/int/int-function), +[`str()`](/mojo/stdlib/builtin/str/str), and +[`bool()`](/mojo/stdlib/builtin/bool/bool-function) functions, and print Python values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function. `PythonObject` also provides the @@ -142,31 +167,46 @@ var f: Float64 = py_float.to_float64() ### Comparing Python types in Mojo -In conditionals, Python objects act like you'd expect them to: Python values -like `False` and `None` evaluate as false in Mojo, too. +You can use Python objects in Mojo comparison expressions, and the Mojo `is` +operator also works to compare the identity of two Python objects. Python values +like `False` and `None` evaluate as false in Mojo boolean expressions as well. If you need to know the type of the underlying Python object, you can use the [`Python.type()`](/mojo/stdlib/python/python/Python#type) method, which is -equivalent to the Python `type()` builtin. You can compare the identity of two -Python objects using the -[`Python.is_type()`](/mojo/stdlib/python/python/Python#is_type) method (which is -equivalent to the Python `is` operator): +equivalent to the Python `type()` builtin. You can test if a Python +object is of a particular type by performing an identity comparison against the +type as shown below: ```mojo -def python_types(): - from python import Python - from python import PythonObject +from python import Python +from python import PythonObject +def main(): var value1: PythonObject = 3.7 - var value2 = Python.evaluate("10/3") - var float_type = Python.evaluate("float") - - print(Python.type(value1)) # - print(Python.is_type(Python.type(value1), Python.type(value2))) # True - print(Python.is_type(Python.type(value1), float_type)) # True - print(Python.is_type(Python.type(value1), Python.none())) # False - + value2 = Python.evaluate("10/3") + + # Compare values + print("Is value1 greater than 3:", value1 > 3) + print("Is value1 greater than value2:", value1 > value2) + + # Compare identities + value3 = value2 + print("value1 is value2:", value1 is value2) + print("value2 is value3:", value2 is value3) + + # Compare types + py_float_type = Python.evaluate("float") + print("Python float type:", py_float_type) + print("value1 type:", Python.type(value1)) + print("Is value1 a Python float:", Python.type(value1) is py_float_type) ``` -One TODO item here: The `Python.is_type()` method is misleadingly named, since -it doesn't compare *types*, but object identity. +```output +Is value1 greater than 3: True +Is value1 greater than value2: True +value1 is value2: False +value2 is value3: True +Python float type: +value1 type: +Is value1 a Python float: True +``` From 95c2125d3f474d2c9c902069edeeb7edae0d93d6 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Dec 2024 16:22:39 -0800 Subject: [PATCH 2010/2019] [Docs] Origin changes: add suggestions from code review. MODULAR_ORIG_COMMIT_REV_ID: 9e019cdabf2bd6d46df078522f1633df94388920 --- docs/manual/values/lifetimes.mdx | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/manual/values/lifetimes.mdx b/docs/manual/values/lifetimes.mdx index 5dc05d799a..baf5f1e860 100644 --- a/docs/manual/values/lifetimes.mdx +++ b/docs/manual/values/lifetimes.mdx @@ -85,18 +85,16 @@ struct ParametricRef[ pass ``` -Note that `Origin` isn't an origin **value**, it's a helper for specifying a -origin **type**. Origin types carry the mutability of a reference as a -boolean parameter value, indicating whether the origin is mutable, immutable, -or even with mutability depending on a parameter specified by the enclosing API. +Origin types carry the mutability of a reference as a boolean parameter value, +indicating whether the origin is mutable, immutable, or even with mutability +depending on a parameter specified by the enclosing API. The `is_mutable` parameter here is an [infer-only -parameter](/mojo/manual/parameters/#infer-only-parameters). It can't be passed -as a positional parameter—it's either inferred from context or specified by -keyword. The `origin` value is often inferred, as well. For example, the -following code creates a [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an -existing value, but doesn't need to specify an origin—the `origin` is inferred -from the variable passed in to the `address_of()` method. +parameter](/mojo/manual/parameters/#infer-only-parameters). The `origin` value +is often inferred, as well. For example, the following code creates a +[`Pointer`](/mojo/stdlib/memory/pointer/Pointer) to an existing value, but +doesn't need to specify an origin—the `origin` is inferred from the variable +passed in to the `address_of()` method. ```mojo from memory import Pointer @@ -474,8 +472,5 @@ that's a union of both origins. ```mojo def pick_one(cond: Bool, ref a: String, ref b: String) -> ref [a, b] String: - if cond: - return a - else: - return b + return a if cond else b ``` From fc0f021701a9dd309a1e032d1d7df3c4999486c3 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Dec 2024 16:40:56 -0800 Subject: [PATCH 2011/2019] [Docs] Add doc on associated aliases. Fixes DOCS-495. MODULAR_ORIG_COMMIT_REV_ID: 20fb09bc7db26baf4f6f30bef9d67037b41fdec2 --- docs/manual/traits.mdx | 113 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/docs/manual/traits.mdx b/docs/manual/traits.mdx index 99da582c4b..4c33a10578 100644 --- a/docs/manual/traits.mdx +++ b/docs/manual/traits.mdx @@ -75,7 +75,7 @@ Quack Moo! ``` -This isn't too bad with only two classes. But the more classes you want to +This isn't too bad with only two types. But the more types you want to support, the less practical this approach is. You might notice that the Mojo versions of `make_it_quack()` don't include the @@ -101,11 +101,15 @@ keyword. Right now, a trait can only contain method signatures, and cannot include method implementations. Each method signature must be followed by three dots (`...`) to indicate that the method is unimplemented. +A trait can also include associated aliases—compile-time constant values that +must be defined by conforming structs. Associated aliases are useful for writing +traits that describe generic types. For more information, see +[Associated aliases for generics](#associated-aliases-for-generics). + :::note TODO In the future, we plan to support defining fields and default method -implementations inside a trait. Right now, though, a trait can only declare -method signatures. +implementations inside a trait. ::: @@ -286,9 +290,9 @@ copyable and movable. ## Built-in traits -The Mojo standard library currently includes a few traits. They're implemented +The Mojo standard library includes many traits. They're implemented by a number of standard library types, and you can also implement these on your -own types: +own types. These standard library traits include: * [`Absable`](/mojo/stdlib/builtin/math/Absable) * [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType) @@ -513,3 +517,102 @@ the `CollectionElement` trait, which requires a Building generic containers is an advanced topic. For an introduction, see the section on [parameterized structs](/mojo/manual/parameters/#parameterized-structs). + +### Associated aliases for generics + +In addition to methods, a trait can include _associated aliases_, which must be +defined by any conforming struct. For example: + +```mojo +trait Repeater: + alias count: Int +``` + +An implementing struct must define a concrete constant value for the alias, +using any compile-time parameter value. For example, it can use a literal +constant or a compile-time expression, including one that uses the struct's +parameters. + +```mojo +struct Doublespeak(Repeater): + alias count: Int = 2 + +struct Multispeak[verbosity: Int](Repeater): + alias count: Int = verbosity*2+1 +``` + +The `Doublespeak` struct has a constant value for the alias, but the `Multispeak` +struct lets the user set the value using a parameter: + +```mojo +repeater = Multispeak[12]() +``` + +Note that the alias is named `count`, and the `Multispeak` parameter is named +`verbosity`. Parameters and aliases are in the same namespace, so the parameter +can't have the same name as the associated alias. + +Associated aliases are most useful for writing traits for generic types. For +example, imagine that you want to write a trait that describes a generic stack +data structure that stores elements that conform to the `CollectionElement` +trait. + +By adding the element type as an associated alias to the trait, you can specify +generic methods on the trait: + +```mojo +trait Stacklike: + alias EltType: CollectionElement + + def push(mut self, owned item: Self.EltType): + pass + + def pop(mut self) -> Self.EltType: + pass +``` + +The following struct implements the `Stacklike` trait using a `List` as the +underlying storage: + +```mojo +struct MyStack[T: CollectionElement](Stacklike): + """A simple Stack built using a List.""" + alias EltType = T + alias list_type = List[Self.EltType] + + var list: Self.list_type + + fn __init__(out self): + self.list = Self.list_type() + + def push(mut self, owned item: Self.EltType): + self.list.append(item) + + def pop(mut self) -> Self.EltType: + return self.list.pop() + + def dump[U: RepresentableCollectionElement](self: MyStack[U]): + print(self.list.__repr__()) +``` + +The `MyStack` type adds a `dump()` method that prints the contents of the stack. +Because a struct that conforms to `CollectionElement` is not necessarily +printable, `MyStack` uses +[conditional conformance](/mojo/manual/parameters/#conditional-conformance) to +define a `dump()` method that works as long as the element type is +_representable_. + +The following code exercises this new trait by defining a generic method, +`add_to_stack()` that adds an item to any `Stacklike` type. + +```mojo +def add_to_stack[S: Stacklike](mut stack: S, item: S.EltType): + stack.push(item) + +def main(): + s = MyStack[Int]() + add_to_stack(s, 12) + add_to_stack(s, 33) + s.dump() # [12, 33] + print(s.pop()) # 33 +``` From 36ce9cf186ca72b3a32c9a70b359a48e5cdbf48c Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 9 Dec 2024 17:15:40 -0800 Subject: [PATCH 2012/2019] [Docs] Document named result values MODULAR_ORIG_COMMIT_REV_ID: 1d176b9be734cfcaef30f4da54bb748ccb0e1106 --- docs/manual/functions.mdx | 116 +++++++++++++++++++++++++++++-- docs/manual/index.md | 7 +- docs/manual/lifecycle/life.mdx | 2 + docs/manual/structs.mdx | 18 ++--- docs/manual/values/ownership.mdx | 19 +++-- 5 files changed, 141 insertions(+), 21 deletions(-) diff --git a/docs/manual/functions.mdx b/docs/manual/functions.mdx index 19d4c2e067..071255ec24 100644 --- a/docs/manual/functions.mdx +++ b/docs/manual/functions.mdx @@ -4,7 +4,7 @@ sidebar_position: 2 description: Introduction to Mojo `fn` and `def` functions. --- -As mentioned in [Language basics](/mojo/manual/basics), Mojo supports two +As mentioned in the [syntax overview](/mojo/manual/basics), Mojo supports two types of functions: `def` and `fn` functions. You can use either declaration with any function, including the `main()` function, but they have different default behaviors, as described on this page. @@ -556,10 +556,10 @@ that the call is ambiguous (if it can’t figure out which one to pick). If the compiler can't figure out which function to use, you can resolve the ambiguity by explicitly casting your value to a supported argument type. For -example, in the following code, we want to call the overloaded `foo()` -function, but both implementations accept an argument that supports [implicit -conversion](/mojo/manual/variables#implicit-type-conversion) from -`StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a +example, in the following code, we want to call the overloaded `foo()` function, +but both implementations accept an argument that supports [implicit +conversion](/mojo/manual/lifecycle/life#constructors-and-implicit-conversion) +from `StringLiteral`. So, the call to `foo(string)` is ambiguous and creates a compiler error. We can fix it by casting the value to the type we really want: ```mojo @@ -596,3 +596,109 @@ different from function arguments, and used for compile-time metaprogramming), you can also overload functions based on parameter types. ::: + +## Return values + +Return value types are declared in the signature using the +-> type syntax. Values are +passed using the `return` keyword, which ends the function and returns the +identified value (if any) to the caller. + +```mojo +def get_greeting() -> String: + return "Hello" +``` + +By default, the value is returned to the caller as an owned value. As with +arguments, a return value may be [implicitly +converted](/mojo/manual/lifecycle/life#constructors-and-implicit-conversion) to +the named return type. For example, the previous example calls `return` with a +string literal, `"Hello"`, which is implicitly converted to a `String`. + +:::note Returning a reference + +A function can also return a mutable or immutable reference using a `ref` return +value. For details, see +[Lifetimes, origins, and references](/mojo/manual/values/lifetimes). + +::: + +### Named results + +Named function results allow a function to return a value that can't be moved or +copied. Named result syntax lets you specify a named, uninitialized variable to +return to the caller using the `out` argument convention: + +```mojo +def get_name_tag(owned name: String, out name_tag: NameTag): + name_tag = NameTag(name^) +``` + +The `out` argument convention identifies an uninitialized variable that the +function must initialize. (This is the same as the `out` convention used in +[struct constructors](/mojo/manual/lifecycle/life#constructor).) A function can +have only one `out` argument. The `out` argument for a named result can appear +anywhere in the argument list, but by convention, it should be the last argument +in the list. + +A function with a named result argument doesn't need to include an explicit +`return` statement, as shown above. If the function terminates without a `return`, +or at a `return` statement with no value, the value of the `out` argument is +returned to the caller. If it includes a `return` statement with a value, that +value is returned to the caller, as usual. + +The fact that a function uses a named result is transparent to the caller. That +is, these two signatures are interchangeable to the caller: + +```mojo +def get_name_tag(owned name: String) -> NameTag: + ... +def get_name_tag(owned name: String, out name_tag: NameTag): + ... +``` + +In both cases, the call looks like this: + +```mojo +tag = get_name_tag("Judith") +``` + +Because the return value is assigned to this special `out` variable, it doesn't +need to be moved or copied when it's returned to the caller. This means that you +can create a function that returns a type that can't be moved or copied, and +which takes several steps to initialize: + +```mojo +struct ImmovableObject: + var name: String + + fn __init__(out self, owned name: String): + self.name = name^ + +def create_immovable_object(owned name: String, out obj: ImmovableObject): + obj = ImmovableObject(name^) + obj.name += "!" + # obj is implicitly returned + +def main(): + my_obj = create_immutable_object("Blob") +``` + +By contrast, the following function with a standard return value doesn't work: + +```mojo +def create_immovable_object2(owned name: String) -> ImmovableObject: + obj = ImmovableObject(name^) + obj.name += "!" + return obj^ # Error: ImmovableObject is not copyable or movable +``` + +Because `create_immovable_object2` uses a local variable to store the object +while it's under construction, the return call requires it to be either moved +or copied to the callee. This isn't an issue if the newly-created value is +returned immediately: + +```mojo +def create_immovable_object3(owned name: String) -> ImmovableObject: + return ImmovableObject(name^) # OK +``` diff --git a/docs/manual/index.md b/docs/manual/index.md index 7d152a1b2b..54a7c53af7 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -29,11 +29,13 @@ feedback](https://www.modular.com/community). - **Language basics** - - [Introduction to Mojo](/mojo/manual/basics) + - [Overview](/mojo/manual/basics) - [Functions](/mojo/manual/functions) - [Variables](/mojo/manual/variables) - [Types](/mojo/manual/types) + - [Operators and expressions](/mojo/manual/operators) - [Control flow](/mojo/manual/control-flow) + - [Errors and context managers](/mojo/manual/errors) - [Structs](/mojo/manual/structs) - [Modules and packages](/mojo/manual/packages) @@ -41,7 +43,8 @@ feedback](https://www.modular.com/community). - [Intro to value ownership](/mojo/manual/values/) - [Value semantics](/mojo/manual/values/value-semantics) - - [Ownership and borrowing](/mojo/manual/values/ownership) + - [Ownership](/mojo/manual/values/ownership) + - [Lifetimes, origins, and references](/mojo/manual/values/lifetimes) - **Value lifecycle** diff --git a/docs/manual/lifecycle/life.mdx b/docs/manual/lifecycle/life.mdx index c852c4c700..360ec57c33 100644 --- a/docs/manual/lifecycle/life.mdx +++ b/docs/manual/lifecycle/life.mdx @@ -155,6 +155,8 @@ can happen when one of the following occurs: - You assign a value of one type to a variable with a different type. - You pass a value of one type to a function that requires a different type. +- You return a value of one type from a function that specifies a different + return type. In both cases, implicit conversion is supported when the target type defines a constructor that meets the following criteria: diff --git a/docs/manual/structs.mdx b/docs/manual/structs.mdx index 92f0fdc1c0..7a32bb323d 100644 --- a/docs/manual/structs.mdx +++ b/docs/manual/structs.mdx @@ -50,14 +50,16 @@ struct MyPair: self.second = second ``` -Notice that the first argument in the `__init__()` method is `out self`. For -now, ignore `out` (it's an [argument -convention](/mojo/manual/values/ownership#argument-conventions) that -declares `self` as a mutable reference); all you need to know right now is that -`self` must be the first argument. It references the current struct instance -(it allows code in the method to refer to "itself"). *When you call the -constructor, you never pass a value for `self`—Mojo passes it in -automatically.* +Notice that the first argument in the `__init__()` method is `out self`. You'll +have a `self` argument as the first argument on all struct methods. It +references the current struct instance (it allows code in the method to refer to +"itself"). *When you call the constructor, you never pass a value for +`self`—Mojo passes it in automatically.* + +The `out` portion of `out self` is an [argument +convention](/mojo/manual/values/ownership#argument-conventions) that declares +`self` as a mutable reference that starts out as uninitialized and must be +initialized before the function returns. The `__init__()` method is one of many [special methods](#special-methods) (also known as "dunder methods" because they have *d*ouble *under*scores) with diff --git a/docs/manual/values/ownership.mdx b/docs/manual/values/ownership.mdx index d45ecc7c70..e8d201e8b6 100644 --- a/docs/manual/values/ownership.mdx +++ b/docs/manual/values/ownership.mdx @@ -1,5 +1,5 @@ --- -title: Ownership and borrowing +title: Ownership sidebar_position: 3 description: How Mojo shares references through function arguments. --- @@ -84,6 +84,13 @@ keyword at the beginning of an argument declaration: `ref` arguments are an advanced topic, and they're described in more detail in [Lifetimes and references](/mojo/manual/values/lifetimes). +* `out`: A special convention used for the `self` argument in + [constructors](/mojo/manual/lifecycle/life#constructor) and for + [named results](/mojo/manual/functions#named-function-results). An `out` + argument is uninitialized at the beginning of the function, and must be + initialized before the function returns. Although `out` arguments show up in + the argument list, they're never passed in by the caller. + For example, this function has one argument that's a mutable reference and one that's immutable: @@ -148,7 +155,7 @@ doesn't do any copying. In general, passing an immutable reference is much more efficient when handling large or expensive-to-copy values, because the copy constructor -and destructor are not invoked for a borrow. +and destructor are not invoked for a `read` argument. ### Compared to C++ and Rust @@ -168,9 +175,9 @@ mutability in the callee. However, the read convention differs from when compared to languages like C++ and Rust, and moves this optimization from every call site to a declaration on the type definition. -The major difference between Rust and Mojo is that Mojo does not require a -sigil on the caller side to pass by borrow. Also, Mojo is more efficient when -passing small values, and Rust defaults to moving values instead of passing +The major difference between Rust and Mojo is that Mojo does not require a sigil +on the caller side to pass by immutable reference. Also, Mojo is more efficient +when passing small values, and Rust defaults to moving values instead of passing them around by borrow. These policy and syntax decisions allow Mojo to provide an easier-to-use programming model. @@ -448,7 +455,7 @@ like `object` are cheap to copy. However, copying large types that allocate heap storage can be expensive. (For example, copying `List` or `Dict` types, or copying large numbers of strings.) -### Borrowed versus owned in `def` functions +### `read` versus `owned` in `def` functions The difference between `read` and `owned` in a `def` function may be a little subtle. In both cases, you can end up with a uniquely-owned value that's From a64ce9cfab6642423e8b6d16c98ced76669a3bcb Mon Sep 17 00:00:00 2001 From: Ken Jones <165197230+KenJones-Modular@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:17:49 -0800 Subject: [PATCH 2013/2019] Updated solution code for the life example to match recent tutorial changes MODULAR_ORIG_COMMIT_REV_ID: d87d1ea4e81ffc5625accbab753d23ef0fd03c9a --- examples/life/gridv1.mojo | 28 +++++++++++++++++++++------- examples/life/gridv2.mojo | 3 +++ examples/life/lifev1.mojo | 2 +- examples/life/lifev2.mojo | 2 +- examples/life/mojoproject.toml | 2 +- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/examples/life/gridv1.mojo b/examples/life/gridv1.mojo index b06cb51315..0eded6f24d 100644 --- a/examples/life/gridv1.mojo +++ b/examples/life/gridv1.mojo @@ -40,15 +40,19 @@ struct Grid(StringableRaising): # ===-------------------------------------------------------------------===# def __str__(self) -> String: + # Create an empty String str = String() + + # Iterate through rows 0 through rows-1 for row in range(self.rows): + # Iterate through columns 0 through cols-1 for col in range(self.cols): if self[row, col] == 1: - str += "*" + str += "*" # If cell is populated, append an asterisk else: - str += " " + str += " " # If cell is not populated, append a space if row != self.rows - 1: - str += "\n" + str += "\n" # Add a newline between rows, but not at the end return str # ===-------------------------------------------------------------------===# @@ -74,6 +78,7 @@ struct Grid(StringableRaising): if seed: random.seed(seed.value()) else: + # Seed the random number generator using the current time. random.seed() data = List[List[Int]]() @@ -81,6 +86,7 @@ struct Grid(StringableRaising): for row in range(rows): row_data = List[Int]() for col in range(cols): + # Generate a random 0 or 1 and append it to the row. row_data.append(int(random.random_si64(0, 1))) data.append(row_data) @@ -96,13 +102,16 @@ struct Grid(StringableRaising): for row in range(self.rows): row_data = List[Int]() + # Calculate neighboring row indices, handling "wrap-around" row_above = (row - 1) % self.rows row_below = (row + 1) % self.rows for col in range(self.cols): + # Calculate neighboring column indices, handling "wrap-around" col_left = (col - 1) % self.cols col_right = (col + 1) % self.cols + # Determine number of populated cells around the current cell num_neighbors = ( self[row_above, col_left] + self[row_above, col] @@ -114,10 +123,15 @@ struct Grid(StringableRaising): + self[row_below, col_right] ) - if num_neighbors | self[row, col] == 3: - row_data.append(1) - else: - row_data.append(0) + # Determine the state of the current cell for the next generation + new_state = 0 + if self[row, col] == 1 and ( + num_neighbors == 2 or num_neighbors == 3 + ): + new_state = 1 + elif self[row, col] == 0 and num_neighbors == 3: + new_state = 1 + row_data.append(new_state) next_generation.append(row_data) diff --git a/examples/life/gridv2.mojo b/examples/life/gridv2.mojo index 3e43b5988a..fa5807974f 100644 --- a/examples/life/gridv2.mojo +++ b/examples/life/gridv2.mojo @@ -98,13 +98,16 @@ struct Grid[rows: Int, cols: Int](StringableRaising): next_generation = Self() for row in range(rows): + # Calculate neighboring row indices, handling "wrap-around" row_above = (row - 1) % rows row_below = (row + 1) % rows for col in range(cols): + # Calculate neighboring column indices, handling "wrap-around" col_left = (col - 1) % cols col_right = (col + 1) % cols + # Determine number of populated cells around the current cell num_neighbors = ( self[row_above, col_left] + self[row_above, col] diff --git a/examples/life/lifev1.mojo b/examples/life/lifev1.mojo index 9056388874..04e78df7c4 100644 --- a/examples/life/lifev1.mojo +++ b/examples/life/lifev1.mojo @@ -59,7 +59,7 @@ def run_display( # Draw each live cell in the grid for row in range(grid.rows): for col in range(grid.cols): - if grid.data[row][col]: + if grid[row, col]: x = col * cell_width + border_size y = row * cell_height + border_size width = cell_width - border_size diff --git a/examples/life/lifev2.mojo b/examples/life/lifev2.mojo index ca2071ae06..e78306c0d9 100644 --- a/examples/life/lifev2.mojo +++ b/examples/life/lifev2.mojo @@ -83,4 +83,4 @@ def run_display( def main(): start = Grid[128, 128].random() - run_display(start, window_height=600, window_width=600) + run_display(start) diff --git a/examples/life/mojoproject.toml b/examples/life/mojoproject.toml index 2d38853796..4a1ffb3026 100644 --- a/examples/life/mojoproject.toml +++ b/examples/life/mojoproject.toml @@ -8,7 +8,7 @@ version = "0.1.0" [dependencies] max = "*" -python = "3.12.*" +python = ">=3.11,<3.13" pygame = ">=2.6.1,<3" [tasks] From 7cf601a2467f2bef27d3fc645e663e42094c1465 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 11 Dec 2024 16:24:04 -0800 Subject: [PATCH 2014/2019] [docs] Standardize straight quotation marks and apostrophes Replace curly quotes/apostrophes (') with straight ones (') throughout developer documentation for consistency. MODULAR_ORIG_COMMIT_REV_ID: 34216041b3d7d74bf5e5621997c3d1becd6f7360 --- docs/manual/functions.mdx | 2 +- docs/manual/lifecycle/death.mdx | 6 +++--- docs/manual/lifecycle/life.mdx | 2 +- docs/manual/pointers/index.mdx | 30 +++++++++++++++--------------- docs/manual/structs.mdx | 8 ++++---- docs/manual/types.mdx | 2 +- docs/tools/debugging.mdx | 2 +- docs/tools/testing.mdx | 8 ++++---- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/manual/functions.mdx b/docs/manual/functions.mdx index 071255ec24..46cd3f5aaa 100644 --- a/docs/manual/functions.mdx +++ b/docs/manual/functions.mdx @@ -552,7 +552,7 @@ Thus, you can also pass a `StringLiteral` to a function that expects a `String`. When resolving an overloaded function call, the Mojo compiler tries each candidate function and uses the one that works (if only one version works), or it picks the closest match (if it can determine a close match), or it reports -that the call is ambiguous (if it can’t figure out which one to pick). +that the call is ambiguous (if it can't figure out which one to pick). If the compiler can't figure out which function to use, you can resolve the ambiguity by explicitly casting your value to a supported argument type. For diff --git a/docs/manual/lifecycle/death.mdx b/docs/manual/lifecycle/death.mdx index 2f7ee808f7..864f06e365 100644 --- a/docs/manual/lifecycle/death.mdx +++ b/docs/manual/lifecycle/death.mdx @@ -105,7 +105,7 @@ the end of the code scope to destroy values): because the destructor call always happens before the tail call. Additionally, Mojo's ASAP destruction works great within Python-style `def` -functions. That's because Python doesn’t really provide scopes beyond a +functions. That's because Python doesn't really provide scopes beyond a function scope, so the Python garbage collector cleans up resources more often than a scope-based destruction policy would. However, Mojo does not use a garbage collector, so the ASAP destruction policy provides destruction @@ -276,7 +276,7 @@ object. Mojo's policy here is powerful and intentionally straight-forward: fields can be temporarily transferred, but the "whole object" must be constructed with the -aggregate type’s initializer and destroyed with the aggregate destructor. This +aggregate type's initializer and destroyed with the aggregate destructor. This means it's impossible to create an object by initializing only its fields, and it's likewise impossible to destroy an object by destroying only its fields. @@ -316,7 +316,7 @@ to a new instance (read more about the [move constructor](/mojo/manual/lifecycle/life#move-constructor)), while `__del__()` implements the deletion logic for its `self`. As such, they both need to own and transform elements of the `owned` value, and they -definitely don’t want the original `owned` value's destructor to also run—that +definitely don't want the original `owned` value's destructor to also run—that could result in a double-free error, and in the case of the `__del__()` method, it would become an infinite loop. diff --git a/docs/manual/lifecycle/life.mdx b/docs/manual/lifecycle/life.mdx index 360ec57c33..dbdd5f11cd 100644 --- a/docs/manual/lifecycle/life.mdx +++ b/docs/manual/lifecycle/life.mdx @@ -656,7 +656,7 @@ things like a single integer or floating point number. We call these types and destroyed without invoking any custom lifecycle methods. Trivial types are the most common types that surround us, and from a language -perspective, Mojo doesn’t need special support for these written in a struct. +perspective, Mojo doesn't need special support for these written in a struct. Usually, these values are so tiny that they should be passed around in CPU registers, not indirectly through memory. diff --git a/docs/manual/pointers/index.mdx b/docs/manual/pointers/index.mdx index 075fa33ea5..a254c77bc5 100644 --- a/docs/manual/pointers/index.mdx +++ b/docs/manual/pointers/index.mdx @@ -45,7 +45,7 @@ print(ptr[]) ## Pointer terminology -Before we jump into the pointer types, here are a few terms you’ll run across. Some +Before we jump into the pointer types, here are a few terms you'll run across. Some of them may already be familiar to you. - **Safe pointers**: are designed to prevent memory errors. Unless you use one @@ -54,7 +54,7 @@ of them may already be familiar to you. use-after-free. - **Nullable pointers**: can point to an invalid memory location (typically 0, -or a “null pointer”). Safe pointers aren’t nullable. +or a “null pointer”). Safe pointers aren't nullable. - **Smart pointers**: own their pointees, which means that the value they point to may be deallocated when the pointer itself is destroyed. Non-owning @@ -66,10 +66,10 @@ or a “null pointer”). Safe pointers aren’t nullable. allocation can either be implicit (that is, performed automatically when initializing a pointer with a value) or explicit. -- **Uninitialized memory**: refers to memory locations that haven’t been +- **Uninitialized memory**: refers to memory locations that haven't been initialized with a value, which may therefore contain random data. - Newly-allocated memory is uninitialized. The safe pointer types don’t allow - users to access memory that’s uninitialized. Unsafe pointers can allocate a + Newly-allocated memory is uninitialized. The safe pointer types don't allow + users to access memory that's uninitialized. Unsafe pointers can allocate a block of uninitialized memory locations and then initialize them one at a time. Being able to access uninitialized memory is unsafe by definition. @@ -93,7 +93,7 @@ The Mojo standard library includes several pointer types with different characteristics: - [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) is a safe pointer that points - to a single value that it doesn’t own. + to a single value that it doesn't own. - [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) is a smart pointer that points to a single value, and maintains exclusive ownership of @@ -139,10 +139,10 @@ The following sections provide more details on each pointer type. ## `Pointer` The [`Pointer`](/mojo/stdlib/memory/pointer/Pointer) type is a safe pointer that -points to a initialized value that it doesn’t own. Some example use cases for a +points to a initialized value that it doesn't own. Some example use cases for a `Pointer` include: -- Storing a reference to a related type. For example, a list’s iterator object +- Storing a reference to a related type. For example, a list's iterator object might hold a `Pointer` back to the original list. - Passing the memory location for a single value to external code via @@ -181,11 +181,11 @@ either `Movable`, `Copyable`, or `ExplicitlyCopyable`. Since an `OwnedPointer` is designed to enforce single ownership, the pointer itself can be moved, but not copied. -Note: Currently, you can’t create an `Optional[OwnedPointer[T]]` because the +Note: Currently, you can't create an `Optional[OwnedPointer[T]]` because the `Optional` type only works with types that are both movable and copyable. This restricts some use cases that would otherwise be a natural fit for`OwnedPointer`, including self-referential data structures like linked lists -and trees. (Until this use case is supported for `OwnedPointer`, it’s possible +and trees. (Until this use case is supported for `OwnedPointer`, it's possible to use`ArcPointer` where you need a smart pointer that can be `Optional`.) ## `ArcPointer` @@ -241,19 +241,19 @@ def main(): Note: The reference count is stored using an [`Atomic`]([/mojo/stdlib/os/atomic/Atomic](https://docs.modular-staging.com/mojo/stdlib/os/atomic/Atomic)) value to ensure that updates to the reference count are thread-safe. However, -Mojo doesn’t currently enforce exclusive access across thread boundaries, so -it’s possible to form race conditions. +Mojo doesn't currently enforce exclusive access across thread boundaries, so +it's possible to form race conditions. ## UnsafePointer [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) is a low-level pointer that can access a block of contiguous memory locations, which -might be uninitialized. It’s analogous to a raw pointer in the C and C++ +might be uninitialized. It's analogous to a raw pointer in the C and C++ programming languages. `UnsafePointer` provides unsafe methods for initializing -and destroying stored values, as well as for accessing the values once they’re +and destroying stored values, as well as for accessing the values once they're initialized. -As the name suggests, `UnsafePointer` doesn’t provide any memory safety +As the name suggests, `UnsafePointer` doesn't provide any memory safety guarantees, so you should reserve it for cases when none of the other pointer types will do the job. Here are some use cases where you might want to use an `UnsafePointer`: diff --git a/docs/manual/structs.mdx b/docs/manual/structs.mdx index 7a32bb323d..818058ba4c 100644 --- a/docs/manual/structs.mdx +++ b/docs/manual/structs.mdx @@ -195,15 +195,15 @@ Syntactically, the biggest difference compared to a Python class is that all fields in a struct must be explicitly declared with `var`. In Mojo, the structure and contents of a struct are set at compile time and -can’t be changed while the program is running. Unlike in Python, where you can -add, remove, or change attributes of an object on the fly, Mojo doesn’t allow +can't be changed while the program is running. Unlike in Python, where you can +add, remove, or change attributes of an object on the fly, Mojo doesn't allow that for structs. However, the static nature of structs helps Mojo run your code faster. The -program knows exactly where to find the struct’s information and how to use it +program knows exactly where to find the struct's information and how to use it without any extra steps or delays at runtime. -Mojo’s structs also work really well with features you might already know from +Mojo's structs also work really well with features you might already know from Python, like operator overloading (which lets you change how math symbols like `+` and `-` work with your own data, using [special methods](#special-methods)). diff --git a/docs/manual/types.mdx b/docs/manual/types.mdx index 4f276c82f9..ee6ddcf586 100644 --- a/docs/manual/types.mdx +++ b/docs/manual/types.mdx @@ -16,7 +16,7 @@ There are a some types that aren't defined as structs: signal "no value." Mojo comes with a standard library that provides a number of useful types and -utility functions. These standard types aren’t privileged. Each of the standard +utility functions. These standard types aren't privileged. Each of the standard library types is defined just like user-defined types—even basic types like [`Int`](/mojo/stdlib/builtin/int/Int) and [`String`](/mojo/stdlib/collections/string/String). But these standard library diff --git a/docs/tools/debugging.mdx b/docs/tools/debugging.mdx index 8631b5cc71..6672ded32d 100644 --- a/docs/tools/debugging.mdx +++ b/docs/tools/debugging.mdx @@ -341,7 +341,7 @@ The debugger currently has the following limitations: * When stepping out of a function, the returned value is not displayed. -* LLDB doesn’t support stopping or resuming individual threads. +* LLDB doesn't support stopping or resuming individual threads. ### Breakpoints diff --git a/docs/tools/testing.mdx b/docs/tools/testing.mdx index ef10b459cf..287e40c690 100644 --- a/docs/tools/testing.mdx +++ b/docs/tools/testing.mdx @@ -14,7 +14,7 @@ consists of a set of assertions defined as part of the ## Get started -Let’s start with a simple example of writing and running Mojo tests. +Let's start with a simple example of writing and running Mojo tests. ### 1. Write tests @@ -106,7 +106,7 @@ its error message. The Mojo standard library includes a [`testing`](/mojo/stdlib/testing/testing/) module that defines several assertion functions for implementing tests. Each -assertion returns `None` if its condition is met or raises an error if it isn’t. +assertion returns `None` if its condition is met or raises an error if it isn't. * [`assert_true()`](/mojo/stdlib/testing/testing/assert_true): Asserts that the input value is `True`. @@ -597,8 +597,8 @@ a += 1 Test suite scopes do *not* nest. In other words, the test suite scope of a module is completely independent of the test suite scope of a function or struct -defined within that module. For example, this means that if a module’s test -suite creates a variable, that variable is *not* accessible to a function’s test +defined within that module. For example, this means that if a module's test +suite creates a variable, that variable is *not* accessible to a function's test suite within the same module. ::: From 77ec5cbd7d816055616c7f18dab41b293a329a7c Mon Sep 17 00:00:00 2001 From: Alex Trotta <44127594+Ahajha@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:23:30 -0500 Subject: [PATCH 2015/2019] [24.6][Examples] Point to stable conda channel MODULAR_ORIG_COMMIT_REV_ID: ab3b585f293c179b2b61963064c5f4402a96a4df --- examples/life/mojoproject.toml | 2 +- examples/mojoproject.toml | 2 +- examples/notebooks/pixi.toml | 2 +- examples/operators/mojoproject.toml | 2 +- pixi.toml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/life/mojoproject.toml b/examples/life/mojoproject.toml index 4a1ffb3026..c4686f213e 100644 --- a/examples/life/mojoproject.toml +++ b/examples/life/mojoproject.toml @@ -1,6 +1,6 @@ [project] authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max/"] description = "Introduction to Mojo Tutorial: Conway's Game of Life" name = "life" platforms = ["osx-arm64", "linux-64", "linux-aarch64"] diff --git a/examples/mojoproject.toml b/examples/mojoproject.toml index bd1d365cd9..67dcca6146 100644 --- a/examples/mojoproject.toml +++ b/examples/mojoproject.toml @@ -3,7 +3,7 @@ name = "Mojo examples" version = "0.1.0" description = "A collection of Mojo code examples" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max/"] platforms = ["osx-arm64","linux-64","linux-aarch64"] [dependencies] diff --git a/examples/notebooks/pixi.toml b/examples/notebooks/pixi.toml index cf658ff6cf..d9a64d924b 100644 --- a/examples/notebooks/pixi.toml +++ b/examples/notebooks/pixi.toml @@ -3,7 +3,7 @@ name = "Mojo notebooks" version = "1.0.0" description = "Environment for running JupyterLab" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max/"] platforms = ["osx-arm64", "linux-aarch64", "linux-64"] [dependencies] diff --git a/examples/operators/mojoproject.toml b/examples/operators/mojoproject.toml index 961e83f6b6..e21864b82b 100644 --- a/examples/operators/mojoproject.toml +++ b/examples/operators/mojoproject.toml @@ -3,7 +3,7 @@ name = "Operators for custom Mojo struct" version = "0.1.0" description = "An example of implementing operators for a custom Mojo struct" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max/"] platforms = ["osx-arm64", "linux-64", "linux-aarch64"] [dependencies] diff --git a/pixi.toml b/pixi.toml index 0067a5d6c1..a2aa7df663 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,7 +1,7 @@ [project] name = "Mojo" authors = ["Modular "] -channels = ["conda-forge", "https://conda.modular.com/max-nightly/"] +channels = ["conda-forge", "https://conda.modular.com/max/"] platforms = ["linux-64", "linux-aarch64", "osx-arm64"] [tasks] @@ -12,4 +12,4 @@ benchmarks = { cmd = ["./stdlib/scripts/run-benchmarks.sh"], env = { MODULAR_MOJ [dependencies] python = ">=3.9,<3.13" lit = "*" -max = "*" \ No newline at end of file +max = "*" From bd8e9d75bf1c34627fa96c8ce9466e18a88bf775 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Mon, 16 Dec 2024 14:13:41 -0800 Subject: [PATCH 2016/2019] [docs] Update Mojo changelog for 24.6. MODULAR_ORIG_COMMIT_REV_ID: 8e3e29b7f9379523b8b25c73315b8d09a020a996 --- docs/changelog-released.md | 782 ++++++++++++++++++++++++++ docs/changelog.md | 687 +--------------------- stdlib/src/memory/__init__.mojo | 3 +- stdlib/src/memory/owned_pointer.mojo | 56 +- stdlib/src/memory/span.mojo | 32 +- stdlib/src/memory/unsafe.mojo | 33 +- stdlib/src/memory/unsafe_pointer.mojo | 15 +- 7 files changed, 880 insertions(+), 728 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 79b64934eb..b420128e7e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -20,6 +20,788 @@ conda](/magic/conda). ::: +## v24.6 (2024-12-17) + +### ✨ Highlights + +Here's a brief summary of some of the major changes in this release, with more +detailed information in the following sections: + +- The `inout` and `borrowed` argument conventions have been renamed to `mut` + and `read`, respectively. A new `out` convention has been added for the `self` + argument in constructors and for named results. See + [Language changes](#24-6-language-changes) for details. + +- `Lifetime` and related types in the standard library have been renamed to + [`Origin`](/mojo/stdlib/builtin/type_aliases/Origin) to better clarify that + parameters of this type indicate where a reference is derived from, not the + more complicated notion of where a variable is initialized and destroyed. As a + consequence the `__lifetime_of()` operator is now named `__origin_of()`. + + There are also a number of other origin-related improvements in this release, + including being able to specify a union of origins by listing multiple values + in the `__origin_of()` operator or inside the `ref` origin specifier + (`ref [a, b]`). For details, see [Language changes](#24-6-language-changes). + + For background information and rationale on the name change see + [the proposal](https://github.com/modularml/mojo/issues/3623). For more + information on origins, see + [Lifetimes, origins and references](/mojo/manual/values/lifetimes) in the Mojo + Manual. + +- Implicit conversions are now opt-in using the + [`@implicit`](/mojo/manual/decorators/implicit) decorator. See + [Language changes](#24-6-language-changes) for details. + +- The standard library has added several new types, including + [`Deque`](/mojo/stdlib/collections/deque/Deque) (a double-ended queue) and + [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) (safe, + single-owner, non-nullable smart pointer). See + [Standard library changes](#standard-library-changes-24-6-standard-library-changes) + for details. + +- The VS Code extension now supports setting data breakpoints and function + breakpoints, and the Mojo LLDB debugger supports symbol breakpoints, such + as `b main` or `b my_module::main`. + +- We've made a number of improvement to how information is displayed in error + messages, LSP, and generated API documentation. For details, see + [Tooling changes](#24-6-tooling-changes). + +- And we've added a number of new docs, including a brand new + [Mojo tutorial](/mojo/manual/get-started), new pages on + [operators and expressions](/mojo/manual/operators), + [error handling](/mojo/manual/errors), and + [pointers](/mojo/manual/pointers/), and many smaller additions and + improvements. + +### Language changes {#24-6-language-changes} + +- Argument convention changes: + + - The `inout` and `borrowed` argument conventions have been renamed to `mut` + (for "mutate") and `read`, respectively. These verbs reflect what the callee + can do to the argument value passed in by the caller, without requiring the + programmer to know about advanced features like references. + + For information on Mojo's argument conventions, see + [Argument conventions](/mojo/manual/values/ownership/#argument-conventions) + in the Mojo Manual. + + - The argument convention for the `self` argument in the `__init__()`, + `__copyinit__()`, and `__moveinit__()` methods has been changed from `inout` + to `out`, reflecting that a constructor method initializes its `self` value + without reading from it. This also enables spelling the type of an + initializer correctly, which was not supported before: + + ```mojo + struct Foo: + fn __init__(out self): pass + + fn test(): + # This works now + var fnPtr : fn(out x: Foo)->None = Foo.__init__ + + var someFoo : Foo + fnPtr(someFoo) # initializes someFoo. + ``` + + The previous `fn __init__(inout self)` syntax is still supported in this + release of Mojo, but will be removed in the future. Please migrate to the + new syntax. + + - Similarly, the spelling of named results has switched to use + `out` syntax instead of `-> T as name`. Functions may have at most one named + result or return type specified with the usual `->` syntax. `out` arguments + may occur anywhere in the argument list, but are typically last (except for + `__init__` methods, where they are typically first). + + ```mojo + # This function has type "fn() -> String" + fn example(out result: String): + result = "foo" + ``` + + The parser still accepts the old syntax as a synonym for this, but that will + eventually be deprecated and removed. + + This was [discussed extensively in a public + proposal](https://github.com/modularml/mojo/issues/3623). For more + information, see + [Named results](/nightly/mojo/manual/functions#named-results) in the Mojo + Manual. + +- Single argument constructors now require the + [`@implicit`](/mojo/manual/decorators/implicit) decorator to allow for + implicit conversions. Previously you could define an `__init__` that takes a + single argument: + + ```mojo + struct Foo: + var value: Int + + fn __init__(out self, value: Int): + self.value = value + ``` + + And this would allow you to pass an `Int` in the position of a `Foo`: + + ```mojo + fn func(foo: Foo): + print("implicitly converted Int to Foo:", foo.value) + + fn main(): + func(Int(42)) + ``` + + This can result in complicated errors that are difficult to debug. By default + this implicit behavior is now turned off, so you have to explicitly construct + `Foo`: + + ```mojo + fn main(): + func(Foo(42)) + ``` + + You can still opt into implicit conversions by adding the `@implicit` + decorator. For example, to enable implicit conversions from `Int` to `Foo`: + + ```mojo + struct Foo: + var value: Int + + @implicit + fn __init__(out self, value: Int): + self.value = value + ``` + + For more information see + [Constructors and implicit conversion](/mojo/manual/lifecycle/life#constructors-and-implicit-conversion) + in the Mojo Manual. + +- Origin-related changes: + + - The `AnyLifetime` type (useful for declaring origin types as parameters) has + has been renamed to [`Origin`](/mojo/stdlib/builtin/type_aliases/Origin) and + the `__lifetime_of()` operator renamed to `__origin_of()`. + + - `Origin` is now a complete wrapper around the MLIR origin type. + + - The `Origin.type` alias has been renamed to `_mlir_origin`. In parameter + lists, you can now write just `Origin[..]`, instead of `Origin[..].type`. + + - `ImmutableOrigin` and `MutableOrigin` are now, respectively, just aliases + for `Origin[False]` and `Origin[True]`. + + - `Origin` struct values are now supported in the origin specifier of a + `ref [..]` argument. + + - Added `Origin.cast_from` for casting the mutability of an origin value. + + - `ref` arguments and results now allow for providing a memory value + directly in the origin specifier, rather than requiring the use of + `__origin_of()`. It is still fine to use `__origin_of()` explicitly though, + and this is required when specifying origins for parameters (e.g. to the + `Pointer` type). For example, this is now valid without `__origin_of()`: + + ```mojo + fn return_ref(a: String) -> ref [a] String: + return a + ``` + + - Various improvements to origin handling and syntax have landed, including + support for the ternary operator and allowing multiple arguments in a `ref` + specifier (which are implicitly unions). This enables expression of simple + algorithms cleanly: + + ```mojo + fn my_min[T: Comparable](ref a: T, ref b: T) -> ref [a, b] T: + return a if a < b else b + ``` + + It is also nice that `my_min` automatically and implicitly propagates the + mutability of its arguments, so things like `my_min(str1, str2) += "foo"` is + valid. + + - `ref` function arguments without an origin clause are now treated as + `ref [_]`, which is more syntactically convenient and consistent: + + ```mojo + fn takes_and_return_ref(ref a: String) -> ref [a] String: + return a + ``` + + - The `__type_of(x)` and `__origin_of(x)` operators are much more general now: + they allow arbitrary expressions inside of them, allow referring to dynamic + values in parameter contexts, and even allow referring to raising functions + in non-raising contexts. These operations never evaluate their expression, + so any side effects that occur in the expression are never evaluated at + runtime, eliminating concerns about `__type_of(expensive())` being a + problem. + + - The destructor insertion logic in Mojo is now aware that types that take an + `MutableAnyOrigin` or `ImmutableAnyOrigin` as part of their signature could + potentially access any live value that destructor insertion is tracking, + eliminating a significant usability issue with unsafe APIs like + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer). + Consider a typical example working with strings before this change: + + ```mojo + var str = String(...) + var ptr = str.unsafe_ptr() + some_low_level_api(ptr) + _ = str^ # OLD HACK: Explicitly keep string alive until here! + ``` + + The `_ = str^` pattern was formerly required because the Mojo compiler has + no idea what "ptr" might reference. As a consequence, it had no idea that + `some_low_level_api()` might access `str` and therefore thought it was ok to + destroy the `String` before the call - this is why the explicit lifetime + extension was required. + + Mojo now knows that + [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) may + access the `MutableAnyOrigin` origin, and now assumes that any API that uses + that origin could use live values. In this case, it assumes that + `some_low_level_api()` might access `str` and because it might be using it, + it cannot destroy `str` until after the call. The consequence of this is + that the old hack is no longer needed for these cases! + + - Function types now accept an origin set parameter. This parameter represents + the origins of values captured by a parameter closure. The compiler + automatically tags parameter closures with the right set of origins. This + enables lifetimes and parameter closures to correctly compose. + + ```mojo + fn call_it[f: fn() capturing [_] -> None](): + f() + + fn test(): + var msg = String("hello world") + + @parameter + fn say_hi(): + print(msg) + + call_it[say_hi]() + # no longer need to write `_ = msg^`!! + ``` + + Note that this only works for higher-order functions which have explicitly + added `[_]` as the capture origins. By default, the compiler still assumes + a `capturing` closure does not reference any origins. This will soon change. + +- Infer-only parameters may now be explicitly bound with keywords, enabling + some important patterns in the standard library: + + ```mojo + struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]: ... + alias ImmStringSlice = StringSlice[is_mutable=False] + # This auto-parameterizes on the origin, but constrains it to being an + # immutable slice instead of a potentially mutable one. + fn take_imm_slice(a: ImmStringSlice): ... + ``` + +- The flag for turning on asserts has changed, e.g. to enable all checks: + + ```bash + mojo -D ASSERT=all main.mojo + ``` + + The levels are: + + - `none`: all assertions off + - `warn`: print assertion errors e.g. for multithreaded tests (previously `-D + ASSERT_WARNING`) + - `safe`: the default mode for standard CPU safety assertions + - `all`: turn on all assertions (previously `-D MOJO_ENABLE_ASSERTIONS`) + + You can now also pass `Stringable` args to format a message, which will have + no runtime penalty or IR bloat cost when assertions are off. Previously you + had to: + + ```mojo + x = -1 + debug_assert( + x > 0, String.format_sequence(“expected x to be more than 0 but got: ”, x) + ) + ``` + + Which can't be optimized away by the compiler in release builds, you can now + pass multiple args for a formatted message at no runtime cost: + + ```mojo + debug_assert(x > 0, “expected x to be more than 0 but got: ”, x) + ``` + +- Automatic parameterization of parameters is now supported. Specifying a + parameterized type with unbound parameters causes them to be implicitly added + to the function signature as infer-only parameters. + + ```mojo + fn foo[value: SIMD[DType.int32, _]](): + pass + + # Equivalent to + fn foo[size: Int, //, value: SIMD[DType.int32, size]](): + pass + ``` + +- Mojo can now interpret simple LLVM intrinsics in parameter expressions, + enabling things like `count_leading_zeros` to work at compile time: + [Issue #933](https://github.com/modularml/mojo/issues/933). + +- Introduced the `@explicit_destroy` annotation, the `__disable_del` keyword, + the `UnknownDestructibility` trait, and the `ImplicitlyDestructible` keyword, + for the experimental explicitly destroyed types feature. + +- Added associated types; we can now have aliases like `alias T: AnyType`, + `alias N: Int`, etc. in a trait, and then specify them in structs that conform + to that trait. For more information, see + [Associated aliases for generics](/mojo/manual/traits#associated-aliases-for-generics). + +### Standard library changes {#24-6-standard-library-changes} + +- Introduced a new [`Deque`](/mojo/stdlib/collections/deque/Deque) (double-ended + queue) collection type, based on a dynamically resizing circular buffer for + efficient O(1) additions and removals at both ends as well as O(1) direct + access to all elements. + + The `Deque` supports the full Python `collections.deque` API, ensuring that all + expected deque operations perform as in Python. + + Enhancements to the standard Python API include `peek()` and `peekleft()` + methods for non-destructive access to the last and first elements, and advanced + constructor options (`capacity`, `min_capacity`, and `shrink`) for customizing + memory allocation and performance. These options allow for optimized memory usage + and reduced buffer reallocations, providing flexibility based on application requirements. + +- The `Formatter` struct has been replaced with a + [`Writer`](/mojo/stdlib/utils/write/Writer) trait to enable buffered IO, + increasing print and file writing perf to the same speed as C. It's now more + general purpose and can write any `Span[Byte]`. To align with this the + `Formattable` trait is now named + [`Writable`](/mojo/stdlib/utils/write/Writable), and the + `String.format_sequence()` static method to initialize a new `String` has been + renamed to [`String.write()`](/mojo/stdlib/collections/string/String/#write). + Here's an example of using all of the changes: + + ```mojo + from memory import Span + + @value + struct NewString(Writer, Writable): + var s: String + + # Writer requirement to write a Span of Bytes + fn write_bytes(inout self, bytes: Span[Byte, _]): + self.s._iadd[False](bytes) + + # Writer requirement to take multiple args + fn write[*Ts: Writable](inout self, *args: *Ts): + @parameter + fn write_arg[T: Writable](arg: T): + arg.write_to(self) + + args.each[write_arg]() + + # Also make it Writable to allow `print` to write the inner String + fn write_to[W: Writer](self, inout writer: W): + writer.write(self.s) + + + @value + struct Point(Writable): + var x: Int + var y: Int + + # Pass multiple args to the Writer. The Int and StringLiteral types call + # `writer.write_bytes` in their own `write_to` implementations. + fn write_to[W: Writer](self, inout writer: W): + writer.write("Point(", self.x, ", ", self.y, ")") + + # Enable conversion to a String using `str(point)` + fn __str__(self) -> String: + return String.write(self) + + + fn main(): + var point = Point(1, 2) + var new_string = NewString(str(point)) + new_string.write("\n", Point(3, 4)) + print(new_string) + ``` + + ```output + Point(1, 2) + Point(3, 4) + ``` + +- Python interop changes: + + - Introduced + [`TypedPythonObject`](/mojo/stdlib/python/python_object/TypedPythonObject) + as a light-weight way to annotate + [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject) values with + static type information. This design will likely evolve and change + significantly. + + - Added `TypedPythonObject["Tuple].__getitem__()` for accessing the elements + of a Python tuple. + + - Added + [`Python.add_object()`](/mojo/stdlib/python/python/Python#add_object), to + add a named `PythonObject` value to a Python 'module' object instance. + + - Added + [`Python.unsafe_get_python_exception()`](/mojo/stdlib/python/python/Python#unsafe_get_python_exception), + as an efficient low-level utility to get the Mojo `Error` equivalent of the + current CPython error state. + + - Add + [`PythonObject.from_borrowed_ptr()`](/mojo/stdlib/python/python_object/PythonObject#from_borrowed_ptr), + to simplify the construction of `PythonObject` values from CPython 'borrowed + reference' pointers. + + The existing `PythonObject.__init__(PyObjectPtr)` should continue to be used + for the more common case of constructing a `PythonObject` from a + 'strong reference' pointer. + + - Support for multi-dimensional indexing and slicing for `PythonObject` + (PR [#3549](https://github.com/modularml/mojo/pull/3549), + PR [#3583](https://github.com/modularml/mojo/pull/3583)). + + ```mojo + var np = Python.import_module("numpy") + var a = np.array(PythonObject([1,2,3,4,5,6])).reshape(2,3) + print((a[0, 1])) # 2 + print((a[1][::-1])) # [6 5 4] + ``` + + Note that the syntax, `a[1, ::-1]`, is currently not supported. + + - Added + [`PythonObject.__contains__()`](/mojo/stdlib/python/python_object/PythonObject#__contains__). + ([PR #3101](https://github.com/modularml/mojo/pull/3101)) + + Example usage: + + ```mojo + x = PythonObject([1,2,3]) + if 1 in x: + print("1 in x") + ``` + +- Pointer related changes: + + - The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type + now has an `origin` parameter that can be used when the `UnsafePointer` + points to a value with a known origin. This origin is propagated through the + `ptr[]` indirection operation. This parameter and other `UnsafePointer` + parameters (other than the type) are now keyword-only. + + - You can now index into `UnsafePointer` using `SIMD` scalar integral types: + + ```mojo + p = UnsafePointer[Int].alloc(1) + i = UInt8(1) + p[i] = 42 + print(p[i]) + ``` + + - Added a new [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) + type as a safe, single-owner, non-nullable smart pointer with similar + semantics to Rust's + [`Box<>`](https://doc.rust-lang.org/std/boxed/struct.Box.html) and C++'s + [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr). + ([PR #3524](https://github.com/modularml/mojo/pull/3524)) + + - `Arc` has been renamed to [`ArcPointer`](/mojo/stdlib/memory/arc/ArcPointer), + for consistency with `OwnedPointer`. + + - [`ArcPointer`](/mojo/stdlib/memory/arc/ArcPointer) now implements + [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable), and can be + compared for pointer equivalence using `a is b`. + + - The `Reference` type has been renamed to + [`Pointer`](/mojo/stdlib/memory/pointer/Pointer): a memory safe complement + to `UnsafePointer`. This change is motivated by the fact that `Pointer` is + assignable and requires an explicit dereference with `ptr[]`. Renaming to + `Pointer` clarifies that "references" means `ref` arguments and results, and + gives us a model that is more similar to what the C++ community would + expect. + + For an overview of Mojo's pointer types, see the new + [Intro to pointers](/mojo/manual/pointers/) page in the Mojo Manual. + + - A new + [`as_noalias_ptr()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#as_noalias_ptr) + method as been added to `UnsafePointer`. This method specifies to the + compiler that the resultant pointer is a distinct identifiable object that + does not alias any other memory in the local scope. + +- Added the [`Floatable`](/mojo/stdlib/builtin/floatable/Floatable) and + [`FloatableRaising`](/mojo/stdlib/builtin/floatable/FloatableRaising) traits to + denote types that can be converted to a `Float64` value using the builtin + `float` function. Made `SIMD` and `FloatLiteral` conform to the `Floatable` + trait. ([PR #3163](https://github.com/modularml/mojo/pull/3163)) + + ```mojo + fn foo[F: Floatable](v: F): + ... + + var f = float(Int32(45)) + ``` + +- The [`rebind()`](/mojo/stdlib/builtin/rebind/rebind) standard library function + now works with memory-only types in addition to + `@register_passable("trivial")` ones, without requiring a copy. For more + information, see + [The `rebind()` builtin](/mojo/manual/parameters/#the-rebind-builtin) in the + Mojo Manual. + +- Introduced the [`random.shuffle()`](/mojo/stdlib/random/random/shuffle) + function for randomizing the elements of a `List`. + ([PR #3327](https://github.com/modularml/mojo/pull/3327)) + + Example: + + ```mojo + from random import shuffle + + var l = List[Int](1, 2, 3, 4, 5) + shuffle(l) + ``` + +- The [`Dict.__getitem__()`](/mojo/stdlib/collections/dict/Dict#__getitem__) + method now returns a reference instead of a copy of the value (or raises). + This improves the performance of common code that uses `Dict` by allowing + borrows from the `Dict` elements. + +- [`Slice.step`](/mojo/stdlib/builtin/builtin_slice/Slice#fields) is now an + `Optional[Int]`, matching the optionality of `slice.step` in Python. + ([PR #3160](https://github.com/modularml/mojo/pull/3160)) + +- There is now a [`Byte`](/mojo/stdlib/builtin/simd/#aliases) alias to better + express intent when working with a pack of bits. + ([PR #3670](https://github.com/modularml/mojo/pull/3670)). + +- Expanded [`os.path`](/mojo/stdlib/os/path/path/) with new functions: + - `os.path.expandvars()`: Expands environment variables in a path ([PR #3735](https://github.com/modularml/mojo/pull/3735)). + - `os.path.splitroot()`: Split a path into drive, root and tail. + ([PR #3780](https://github.com/modularml/mojo/pull/3780)). + +- Added a [`reserve()`](/mojo/stdlib/collections/string/String#reserve) method + and new constructor to the `String` struct to allocate additional capacity. + ([PR #3755](https://github.com/modularml/mojo/pull/3755)). + +- A new + [`StringLiteral.get[some_stringable]()`](/mojo/stdlib/builtin/string_literal/StringLiteral#get) + method is available. It allows forming a runtime-constant `StringLiteral` from + a compile-time-dynamic `Stringable` value. + +- [`Span`](/mojo/stdlib/memory/span/Span) has moved from the `utils` module to + the `memory` module. + +- [`Span`](/mojo/stdlib/memory/span/Span) now implements `__reversed__()`. This + means that one can get a reverse iterator over a `Span` using + `reversed(my_span)`. Users should currently prefer this method over + `my_span[::-1]`. + +- A new [`AsBytes`](/mojo/stdlib/memory/span/AsBytes) trait has been added to + enable taking a `Span[Byte]` from any type that implements `as_bytes()`. + `String.as_bytes()` and `String.as_bytes_slice()` have been consolidated under + `String.as_bytes()` to return a `Span[Byte]`. If you require a copy, you can + convert the `Span` to a `List` with `List(my_string.as_bytes())`. + +- [`StringSlice`](/mojo/stdlib/utils/string_slice/StringSlice) now implements + `strip()`, `rstrip()`, and `lstrip()`. + +- [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) now implements `split()` + which can be used to split a `StringRef` into a `List[StringRef]` by a + delimiter. ([PR #2705](https://github.com/modularml/mojo/pull/2705)) + +- [`StringRef`](/mojo/stdlib/utils/stringref/StringRef) is now representable so + `repr(StringRef("hello"))` will return `StringRef('hello')`. + +- More things have been removed from the auto-exported set of entities in the + `prelude` module from the Mojo standard library: + - `UnsafePointer` has been removed. Please explicitly import it via + `from memory import UnsafePointer`. + - `StringRef` has been removed. Please explicitly import it via + `from utils import StringRef`. + +- Restored implicit copyability of [`Tuple`](/mojo/stdlib/builtin/tuple/Tuple) + and [`ListLiteral`](/mojo/stdlib/builtin/builtin_list/ListLiteral). + +- The + [aliases for C foreign function interface (FFI)](/mojo/stdlib/sys/ffi/#aliases) + have been renamed: `C_int` -> `c_int`, `C_long` -> `c_long` and so on. + +- `Float32` and `Float64` are now printed and converted to strings with + roundtrip guarantee and shortest representation: + + ```plaintext + Value Old New + Float64(0.3) 0.29999999999999999 0.3 + Float32(0.3) 0.30000001192092896 0.3 + Float64(0.0001) 0.0001 0.0001 + Float32(0.0001) 9.9999997473787516e-05 0.0001 + Float64(-0.00001) -1.0000000000000001e-05 -1e-05 + Float32(-0.00001) -9.9999997473787516e-06 -1e-05 + Float32(0.00001234) 1.2339999557298142e-05 1.234e-05 + Float32(-0.00000123456) -1.2345600453045336e-06 -1.23456e-06 + Float64(1.1234567e-320) 1.1235052786429946e-320 1.1235e-320 + Float64(1.234 * 10**16) 12340000000000000.0 1.234e+16 + ``` + +- The `StaticIntTuple` data structure in the `utils` package has been renamed to + [`IndexList`](/mojo/stdlib/utils/index_/IndexList). The data structure now + allows one to specify the index bitwidth of the elements along with whether + the underlying indices are signed or unsigned. + +- Added [`DLHandle.get_symbol()`](/mojo/stdlib/sys/ffi/DLHandle#get_symbol), for + getting a pointer to a symbol in a dynamic library. This is more general + purpose than the existing methods for getting function pointers. + +### Tooling changes {#24-6-tooling-changes} + +- The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration + setting that can be used in conjunction with `mojoFile` to define the build + arguments when compiling the Mojo file. + +- The VS Code extension now supports a `Configure Build and Run Args` command + that helps set the build and run args for actions file `Run Mojo File` and + `Debug Mojo File`. A corresponding button appears in `Run and Debug` selector + in the top right corner of a Mojo File. + +- The VS Code extension now has the `mojo.run.focusOnTerminalAfterLaunch` + setting, which controls whether to focus on the terminal used by the + `Mojo: Run Mojo File` command or on the editor after launch. + [Issue #3532](https://github.com/modularml/mojo/issues/3532). + +- The VS Code extension now has the `mojo.SDK.additionalSDKs` setting, which + allows the user to provide a list of MAX SDKs that the extension can use when + determining a default SDK to use. The user can select the default SDK to use + with the `Mojo: Select the default MAX SDK` command. + +- The VS Code extension now supports setting + [data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints) + as well as + [function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints). + +- The Mojo LLDB debugger now supports symbol breakpoints, for example, `b main` + or `b my_module::main`. + +- Error messages that include type names no longer include inferred or defaulted + parameters when they aren't needed. For example, previously Mojo complained + about things like: + + ```plaintext + ... cannot be converted from 'UnsafePointer[UInt, 0, _default_alignment::AnyType](), MutableAnyOrigin]' to 'UnsafePointer[Int, 0, _default_alignment[::AnyType](), MutableAnyOrigin]' + ``` + + it now complains more helpfully that: + + ```plaintext + ... cannot be converted from 'UnsafePointer[UInt]' to 'UnsafePointer[Int]' + ``` + +- Tooling now prints the origins of `ref` arguments and results correctly, and + prints `self` instead of `self: Self` in methods. + +- The Mojo Language Server and generated documentation now print parametric + result types correctly, e.g. showing `SIMD[type, simd_width]` instead of + `SIMD[$0, $1]`. + +- Generated API documentation now shows the signatures for structs, and + identifies `@register_passable` and `@register_passable("trivial")` types. + +- The VS Code extension now allows cancelling the installation of its private + MAX SDK. + +- The VS Code extension now opens the Run and Debug tab automatically whenever + a debug session starts. + +- The `mojo debug --vscode` command now support the `--init-command` and + `--stop-on-entry` flags. Execute `mojo debug --help` for more information. + +- The Mojo LLDB debugger on VS Code now supports inspecting the raw attributes + of variables that are handled as synthetic types, e.g. `List` from Mojo or + `std::vector` from C++. + +- The VS Code extension now allows selecting a default SDK when multiple are + available. + +### ❌ Removed + +- The `UnsafePointer.bitcast()` overload for `DType` has been removed. Wrap your + `DType` in a `Scalar[my_dtype]` to call the only overload of `bitcast()` now. + +### 🛠️ Fixed + +- Lifetime tracking is now fully field sensitive, which makes the uninitialized + variable checker more precise. + +- [Issue #1310](https://github.com/modularml/mojo/issues/1310) - Mojo permits + the use of any constructor for implicit conversions + +- [Issue #1632](https://github.com/modularml/mojo/issues/1632) - Mojo produces + weird error when inout function is used in non mutating function + +- [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init + causing use of uninitialized variable + +- [Issue #3544](https://github.com/modularml/mojo/issues/3544) - Known + mutable `ref` argument are not optimized as `noalias` by LLVM. + +- [Issue #3559](https://github.com/modularml/mojo/issues/3559) - VariadicPack + doesn't extend the lifetimes of the values it references. + +- [Issue #3627](https://github.com/modularml/mojo/issues/3627) - Compiler + overlooked exclusivity violation caused by `ref [MutableAnyOrigin] T` + +- [Issue #3710](https://github.com/modularml/mojo/issues/3710) - Mojo frees + memory while reference to it is still in use. + +- [Issue #3805](https://github.com/modularml/mojo/issues/3805) - Crash When + Initializing !llvm.ptr. + +- [Issue #3816](https://github.com/modularml/mojo/issues/3816) - Ternary + if-operator doesn't propagate origin information. + +- [Issue #3815](https://github.com/modularml/mojo/issues/3815) - + [BUG] Mutability not preserved when taking the union of two origins. + +- [Issue #3829](https://github.com/modularml/mojo/issues/3829) - Poor error + message when invoking a function pointer upon an argument of the wrong origin + +- [Issue #3830](https://github.com/modularml/mojo/issues/3830) - Failures + emitting register RValues to ref arguments. + +- The VS Code extension now auto-updates its private copy of the MAX SDK. + +- The variadic initializer for `SIMD` now works in parameter expressions. + +- The VS Code extension now downloads its private copy of the MAX SDK in a way + that prevents `ETXTBSY` errors on Linux. + +- The VS Code extension now allows invoking a mojo formatter from SDK + installations that contain white spaces in their path. + +### Special thanks + +Special thanks to our community contributors: +[@soraos](https://github.com/soraros), [@jjvraw](https://github.com/jjvraw), +[@bgreni](https://github.com/bgreni), +[@thatstoasty](https://github.com/thatstoasty), +[@szbergeron](https://github.com/szbergeron), +[@rd4com](https://github.com/rd4com), +[@fknfilewalker](https://github.com/fknfilewalker), +[@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse), +[@avitkauskas](https://github.com/avitkauskas), and +[@martinvuyk](https://github.com/martinvuyk). + ## v24.5 (2024-09-13) ### ✨ Highlights diff --git a/docs/changelog.md b/docs/changelog.md index a57e0db1b2..9f6a1c4e3e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,694 +7,23 @@ what we publish. [//]: # Here's the template to use when starting a new batch of notes: [//]: ## UNRELEASED -[//]: ### ⭐️ New -[//]: ### 🦋 Changed +[//]: ### ✨ Highlights +[//]: ### Language changes +[//]: ### Standard library changes +[//]: ### Tooling changes [//]: ### ❌ Removed [//]: ### 🛠️ Fixed ## UNRELEASED -### ⭐️ New +### ✨ Highlights -- `StringRef` is now representable so `repr(StringRef("hello"))` will return - `StringRef('hello')`. +### Language changes -- Mojo can now interpret simple LLVM intrinsics in parameter expressions, - enabling things like `count_leading_zeros` to work at compile time: - [Issue #933](https://github.com/modularml/mojo/issues/933). +### Standard library changes -- The destructor insertion logic in Mojo is now aware that types that take an - `MutableAnyOrigin` or `ImmutableAnyOrigin` as part of their signature could - potentially access any live value that destructor insertion is tracking, - eliminating a significant usability issue with unsafe APIs like - `UnsafePointer`. Consider a typical example working with strings before this - change: - - ```mojo - var str = String(...) - var ptr = str.unsafe_ptr() - some_low_level_api(ptr) - _ = str^ # OLD HACK: Explicitly keep string alive until here! - ``` - - The `_ = str^` pattern was formerly required because the Mojo compiler has no - idea what "ptr" might reference. As a consequence, it had no idea that - `some_low_level_api()` might access `str` and therefore thought it was ok to - destroy the `String` before the call - this is why the explicit lifetime - extension was required. - - Mojo now knows that `UnsafePointer` may access the `MutableAnyOrigin` origin, - and now assumes that any API that uses that origin could use live values. - In this case, it assumes that `some_low_level_api()` might access `str` and - because it might be using it, it cannot destroy `str` until after the call. - The consequence of this is that the old hack is no longer needed for these - cases! - -- Various improvements to origin handling and syntax have landed, including - support for the ternary operator and allowing multiple arguments in a `ref` - specifier (which are implicitly unions). This enables expression of simple - algorithms cleanly: - - ```mojo - fn my_min[T: Comparable](ref a: T, ref b: T) -> ref [a, b] T: - return a if a < b else b - ``` - - It is also nice that `my_min` automatically and implicitly propagates the - mutability of its arguments, so things like `my_min(str1, str2) += "foo"` is - valid. - -- The `UnsafePointer` type now has an `origin` parameter that can be used when - the `UnsafePointer` is known to point to a value with a known origin. This - origin is propagated through the `ptr[]` indirection operation. - -- The VS Code Mojo Debugger now has a `buildArgs` JSON debug configuration - setting that can be used in conjunction with `mojoFile` to define the build - arguments when compiling the Mojo file. - -- The VS Code extension now supports a `Configure Build and Run Args` command - that helps set the build and run args for actions file `Run Mojo File` and - `Debug Mojo File`. A corresponding button appears in `Run and Debug` selector - in the top right corner of a Mojo File. - -- Add the `Floatable` and `FloatableRaising` traits to denote types that can - be converted to a `Float64` value using the builtin `float` function. - - Make `SIMD` and `FloatLiteral` conform to the `Floatable` trait. - - ```mojo - fn foo[F: Floatable](v: F): - ... - - var f = float(Int32(45)) - ``` - - ([PR #3163](https://github.com/modularml/mojo/pull/3163) by [@bgreni](https://github.com/bgreni)) - -- Add `DLHandle.get_symbol()`, for getting a pointer to a symbol in a dynamic - library. This is more general purpose than the existing methods for getting - function pointers. - -- Introduce `TypedPythonObject` as a light-weight way to annotate `PythonObject` - values with static type information. This design will likely evolve and - change significantly. - - - Added `TypedPythonObject["Tuple].__getitem__` for accessing the elements of - a Python tuple. - -- Added `Python.add_object()`, to add a named `PythonObject` value to a Python - 'module' object instance. - -- Added `Python.unsafe_get_python_exception()`, as an efficient low-level - utility to get the Mojo `Error` equivalent of the current CPython error state. - -- The `__type_of(x)` and `__origin_of(x)` operators are much more general now: - they allow arbitrary expressions inside of them, allow referring to dynamic - values in parameter contexts, and even allow referring to raising functions - in non-raising contexts. These operations never evaluate their expression, so - any side effects that occur in the expression are never evaluated at runtime, - eliminating concerns about `__type_of(expensive())` being a problem. - -- Add `PythonObject.from_borrowed_ptr()`, to simplify the construction of - `PythonObject` values from CPython 'borrowed reference' pointers. - - The existing `PythonObject.__init__(PyObjectPtr)` should continue to be used - for the more common case of constructing a `PythonObject` from a - 'strong reference' pointer. - -- The `rebind` standard library function now works with memory-only types in - addition to `@register_passable("trivial")` ones, without requiring a copy. - -- Introduce `random.shuffle` for `List`. - ([PR #3327](https://github.com/modularml/mojo/pull/3327) by [@jjvraw](https://github.com/jjvraw)) - - Example: - - ```mojo - from random import shuffle - - var l = List[Int](1, 2, 3, 4, 5) - shuffle(l) - ``` - -- The `Dict.__getitem__` method now returns a reference instead of a copy of - the value (or raises). This improves the performance of common code that - uses `Dict` by allowing borrows from the `Dict` elements. - -- Autoparameterization of parameters is now supported. Specifying a parameter - type with unbound parameters causes them to be implicitly added to the - function signature as inferred parameters. - - ```mojo - fn foo[value: SIMD[DType.int32, _]](): - pass - - # Equivalent to - fn foo[size: Int, //, value: SIMD[DType.int32, size]](): - pass - ``` - -- Function types now accept an origin set parameter. This parameter represents - the origins of values captured by a parameter closure. The compiler - automatically tags parameter closures with the right set of origins. This - enables lifetimes and parameter closures to correctly compose. - - ```mojo - fn call_it[f: fn() capturing [_] -> None](): - f() - - fn test(): - var msg = String("hello world") - - @parameter - fn say_hi(): - print(msg) - - call_it[say_hi]() - # no longer need to write `_ = msg^`!! - ``` - - Note that this only works for higher-order functions which have explicitly - added `[_]` as the capture origins. By default, the compiler still assumes - a `capturing` closure does not reference any origins. This will soon change. - -- The VS Code extension now has the `mojo.run.focusOnTerminalAfterLaunch` - setting, which controls whether to focus on the terminal used by the - `Mojo: Run Mojo File` command or on the editor after launch. - [Issue #3532](https://github.com/modularml/mojo/issues/3532). - -- The VS Code extension now has the `mojo.SDK.additionalSDKs` setting, which - allows the user to provide a list of MAX SDKs that the extension can use when - determining a default SDK to use. The user can select the default SDK to use - with the `Mojo: Select the default MAX SDK` command. - -- Added a new [`OwnedPointer`](/mojo/stdlib/memory/owned_pointer/OwnedPointer) - type as a safe, single-owner, non-nullable smart pointer with similar - semantics to Rust's - [`Box<>`](https://doc.rust-lang.org/std/boxed/struct.Box.html) and C++'s - [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr). - - ([PR #3524](https://github.com/modularml/mojo/pull/3524) by [@szbergeron](https://github.com/szbergeron)) - -- `ref` argument and result specifiers now allow providing a memory value - directly in the origin specifier, rather than requiring the use of - `__origin_of()`. It is still fine to use `__origin_of()` explicitly though, - and this is required when specifying origins for parameters (e.g. to the - `Pointer` type). For example, this is now valid without `__origin_of()`: - - ```mojo - fn return_ref(a: String) -> ref [a] String: - return a - ``` - -- `ref` function arguments without an origin clause are now treated as - `ref [_]`, which is more syntactically convenient and consistent: - - ```mojo - fn takes_and_return_ref(ref a: String) -> ref [a] String: - return a - ``` - -- `Slice.step` is now an `Optional[Int]`, matching the optionality of - `slice.step` in Python. - ([PR #3160](https://github.com/modularml/mojo/pull/3160) by - [@bgreni](https://github.com/bgreni)) - -- `StringRef` now implements `split()` which can be used to split a - `StringRef` into a `List[StringRef]` by a delimiter. - ([PR #2705](https://github.com/modularml/mojo/pull/2705) by [@fknfilewalker](https://github.com/fknfilewalker)) - -- Support for multi-dimensional indexing for `PythonObject` - ([PR #3583](https://github.com/modularml/mojo/pull/3583) by [@jjvraw](https://github.com/jjvraw)). - -- Support for multi-dimensional indexing and slicing for `PythonObject` - (PRs [#3549](https://github.com/modularml/mojo/pull/3549), - [#3583](https://github.com/modularml/mojo/pull/3583) by [@jjvraw](https://github.com/jjvraw)). - - ```mojo - var np = Python.import_module("numpy") - var a = np.array(PythonObject([1,2,3,4,5,6])).reshape(2,3) - print((a[0, 1])) # 2 - print((a[1][::-1])) # [6 5 4] - ``` - - Note, that the syntax, `a[1, ::-1]`, is currently not supported. - -- [`Arc`](/mojo/stdlib/memory/arc/Arc) now implements - [`Identifiable`](/mojo/stdlib/builtin/identifiable/Identifiable), and can be - compared for pointer equivalence using `a is b`. - -- There is now a [`Byte`](/mojo/stdlib/builtin/simd/Byte) alias to better - express intent when working with a pack of bits. - ([PR #3670](https://github.com/modularml/mojo/pull/3670) by [@soraos](https://github.com/soraros)). - -- The VS Code extension now supports setting [data breakpoints](https://code.visualstudio.com/docs/editor/debugging#_data-breakpoints) - as well as [function breakpoints](https://code.visualstudio.com/docs/editor/debugging#_function-breakpoints). - -- The Mojo LLDB debugger now supports symbol breakpoints, e.g. `b main` or - `b my_module::main`. - -- The VS Code extension now allows cancelling the installation of its private - MAX SDK. - -- The VS Code extension now opens the Run and Debug tab automatically whenever - a debug session starts. - -- The `mojo debug --vscode` command now support the `--init-command` and - `--stop-on-entry` flags. Execute `mojo debug --help` for more information. - -- The Mojo LLDB debugger on VS Code now supports inspecting the raw attributes - of variables that are handled as synthetic types, e.g. `List` from Mojo or - `std::vector` from C++. - -- Expanded `os.path` with new functions (by [@thatstoasty](https://github.com/thatstoasty)): - - `os.path.expandvars`: Expands environment variables in a path ([PR #3735](https://github.com/modularml/mojo/pull/3735)). - - `os.path.splitroot`: Split a path into drive, root and tail. - ([PR #3780](https://github.com/modularml/mojo/pull/3780)). - -- Added a `reserve` method and new constructor to the `String` struct to - allocate additional capacity. - ([PR #3755](https://github.com/modularml/mojo/pull/3755) by [@thatstoasty](https://github.com/thatstoasty)). - -- Introduced a new `Deque` (double-ended queue) collection type, based on a - dynamically resizing circular buffer for efficient O(1) additions and removals - at both ends as well as O(1) direct access to all elements. - - The `Deque` supports the full Python `collections.deque` API, ensuring that all - expected deque operations perform as in Python. - - Enhancements to the standard Python API include `peek()` and `peekleft()` - methods for non-destructive access to the last and first elements, and advanced - constructor options (`capacity`, `min_capacity`, and `shrink`) for customizing - memory allocation and performance. These options allow for optimized memory usage - and reduced buffer reallocations, providing flexibility based on application requirements. - -- A new `StringLiteral.get[some_stringable]()` method is available. It - allows forming a runtime-constant StringLiteral from a compile-time-dynamic - `Stringable` value. - -- `Span` now implements `__reversed__`. This means that one can get a - reverse iterator over a `Span` using `reversed(my_span)`. Users should - currently prefer this method over `my_span[::-1]`. - -- `StringSlice` now implements `strip`, `rstrip`, and `lstrip`. - -- Introduced the `@explicit_destroy` annotation, the `__disable_del` keyword, - the `UnknownDestructibility` trait, and the `ImplicitlyDestructible` keyword, - for the experimental explicitly destroyed types feature. - -- Added associated types; we can now have aliases like `alias T: AnyType`, - `alias N: Int`, etc. in a trait, and then specify them in structs that conform - to that trait. - -### 🦋 Changed - -- The `inout` and `borrowed` argument conventions have been renamed to the `mut` - and `read` argument conventions (respectively). These verbs reflect - declaratively what the callee can do to the argument value passed into the - caller, without tying in the requirement for the programmer to know about - advanced features like references. - -- The argument convention for `__init__` methods has been changed from `inout` - to `out`, reflecting that an `__init__` method initializes its `self` without - reading from it. This also enables spelling the type of an initializer - correctly, which was not supported before: - - ```mojo - struct Foo: - fn __init__(out self): pass - - fn test(): - # This works now - var fnPtr : fn(out x: Foo)->None = Foo.__init__ - - var someFoo : Foo - fnPtr(someFoo) # initializes someFoo. - ``` - - The previous `fn __init__(inout self)` syntax is still supported in this - release of Mojo, but will be removed in the future. Please migrate to the - new syntax. - -- Similarly, the spelling of "named functions results" has switched to use `out` - syntax instead of `-> T as name`. Functions may have at most one named result - or return type specified with the usual `->` syntax. `out` arguments may - occur anywhere in the argument list, but are typically last (except for - `__init__` methods, where they are typically first). - - ```mojo - # This function has type "fn() -> String" - fn example(out result: String): - result = "foo" - ``` - - The parser still accepts the old syntax as a synonym for this, but that will - eventually be deprecated and removed. - - This was [discussed extensively in a public - proposal](https://github.com/modularml/mojo/issues/3623). - -- More things have been removed from the auto-exported set of entities in the `prelude` - module from the Mojo standard library. - - `UnsafePointer` has been removed. Please explicitly import it via - `from memory import UnsafePointer`. - - `StringRef` has been removed. Please explicitly import it via - `from utils import StringRef`. - -- The `Reference` type has been renamed to `Pointer`: a memory safe complement - to `UnsafePointer`. This change is motivated by the fact that `Pointer` - is assignable and requires an explicit dereference with `ptr[]`. Renaming - to `Pointer` clarifies that "references" means `ref` arguments and results, - and gives us a model that is more similar to what the C++ community would - expect. - -- A new `as_noalias_ptr` method as been added to `UnsafePointer`. This method - specifies to the compiler that the resultant pointer is a distinct - identifiable object that does not alias any other memory in the local scope. - -- The `AnyLifetime` type (useful for declaring origin types as parameters) has - been renamed to `Origin`. - -- Restore implicit copyability of `Tuple` and `ListLiteral`. - -- The aliases for C FFI have been renamed: `C_int` -> `c_int`, `C_long` -> `c_long` - and so on. - -- The VS Code extension now allows selecting a default SDK when multiple are available. - -- The `Formatter` struct has changed to a `Writer` trait to enable buffered IO, - increasing print and file writing perf to the same speed as C. It's now more - general purpose and can write any `Span[Byte]`. To align with this the - `Formattable` trait is now named `Writable`, and the `String.format_sequence` - static methods to initialize a new `String` have been renamed to - `String.write`. Here's an example of using all the changes: - - ```mojo - from memory import Span - - @value - struct NewString(Writer, Writable): - var s: String - - # Writer requirement to write a Span of Bytes - fn write_bytes(inout self, bytes: Span[Byte, _]): - self.s._iadd[False](bytes) - - # Writer requirement to take multiple args - fn write[*Ts: Writable](inout self, *args: *Ts): - @parameter - fn write_arg[T: Writable](arg: T): - arg.write_to(self) - - args.each[write_arg]() - - # Also make it Writable to allow `print` to write the inner String - fn write_to[W: Writer](self, inout writer: W): - writer.write(self.s) - - - @value - struct Point(Writable): - var x: Int - var y: Int - - # Pass multiple args to the Writer. The Int and StringLiteral types call - # `writer.write_bytes` in their own `write_to` implementations. - fn write_to[W: Writer](self, inout writer: W): - writer.write("Point(", self.x, ", ", self.y, ")") - - # Enable conversion to a String using `str(point)` - fn __str__(self) -> String: - return String.write(self) - - - fn main(): - var point = Point(1, 2) - var new_string = NewString(str(point)) - new_string.write("\n", Point(3, 4)) - print(new_string) - ``` - - Point(1, 2) - Point(3, 4) - -- The flag for turning on asserts has changed, e.g. to enable all checks: - - ```bash - mojo -D ASSERT=all main.mojo - ``` - - The levels are: - - - none: all assertions off - - warn: print assertion errors e.g. for multithreaded tests (previously -D - ASSERT_WARNING) - - safe: the default mode for standard CPU safety assertions - - all: turn on all assertions (previously -D MOJO_ENABLE_ASSERTIONS) - - You can now also pass `Stringable` args to format a message, which will have - no runtime penalty or IR bloat cost when assertions are off. Previously you - had to: - - ```mojo - x = -1 - debug_assert( - x > 0, String.format_sequence(“expected x to be more than 0 but got: ”, x) - ) - ``` - - Which can't be optimized away by the compiler in release builds, you can now - pass multiple args for a formatted message at no runtime cost: - - ```mojo - debug_assert(x > 0, “expected x to be more than 0 but got: ”, x) - ``` - -- The `StaticIntTuple` datastructure in the `utils` package has been renamed to - `IndexList`. The datastructure now allows one to specify the index bitwidth of - the elements along with whether the underlying indices are signed or unsigned. - -- A new trait has been added `AsBytes` to enable taking a `Span[Byte]` of a - type with `s.as_bytes()`. `String.as_bytes` and `String.as_bytes_slice` have - been consolidated under `s.as_bytes` to return a `Span[Byte]`, you can convert - it to a `List` if you require a copy with `List(s.as_bytes())`. - -- `Lifetime` and related types have been renamed to `Origin` in the standard - library to better clarify that parameters of this type indicate where a - reference is derived from, not the more complicated notion of where a variable - is initialized and destroyed. Please see [the proposal](https://github.com/modularml/mojo/blob/main/proposals/lifetimes-keyword-renaming.md) - for more information and rationale. As a consequence the `__lifetime_of()` - operator is now named `__origin_of()`. - -- `Origin` is now a complete wrapper around the MLIR origin type. - - - The `Origin.type` alias has been renamed to `_mlir_origin`. In parameter - lists, you can now write just `Origin[..]`, instead of `Origin[..].type`. - - - `ImmutableOrigin` and `MutableOrigin` are now, respectively, just aliases - for `Origin[False]` and `Origin[True]`. - - - `Origin` struct values are now supported in the brackets of a `ref [..]` - argument. - - - Added `Origin.cast_from` for casting the mutability of an origin value. - -- You can now use the `+=` and `*` operators on a `StringLiteral` at compile - time using the `alias` keyword: - - ```mojo - alias original = "mojo" - alias concat = original * 3 - assert_equal("mojomojomojo", concat) - ``` - - Or inside a `fn` that is being evaluated at compile time: - - ```mojo - fn add_literal( - owned original: StringLiteral, add: StringLiteral, n: Int - ) -> StringLiteral: - for _ in range(n): - original += add - return original - - - fn main(): - alias original = "mojo" - alias concat = add_literal(original, "!", 4) - assert_equal("mojo!!!!", concat) - ``` - - These operators can't be evaluated at runtime, as a `StringLiteral` must be - written into the binary during compilation. - -- You can now index into `UnsafePointer` using SIMD scalar integral types: - - ```mojo - p = UnsafePointer[Int].alloc(1) - i = UInt8(1) - p[i] = 42 - print(p[i]) - ``` - -- Float32 and Float64 are now printed and converted to strings with roundtrip - guarantee and shortest representation: - - ```plaintext - Value Old New - Float64(0.3) 0.29999999999999999 0.3 - Float32(0.3) 0.30000001192092896 0.3 - Float64(0.0001) 0.0001 0.0001 - Float32(0.0001) 9.9999997473787516e-05 0.0001 - Float64(-0.00001) -1.0000000000000001e-05 -1e-05 - Float32(-0.00001) -9.9999997473787516e-06 -1e-05 - Float32(0.00001234) 1.2339999557298142e-05 1.234e-05 - Float32(-0.00000123456) -1.2345600453045336e-06 -1.23456e-06 - Float64(1.1234567e-320) 1.1235052786429946e-320 1.1235e-320 - Float64(1.234 * 10**16) 12340000000000000.0 1.234e+16 - ``` - -- Single argument constructors now require a `@implicit` decorator to allow - for implicit conversions. Previously you could define an `__init__` that - takes a single argument: - - ```mojo - struct Foo: - var value: Int - - fn __init__(out self, value: Int): - self.value = value - ``` - - And this would allow you to pass an `Int` in the position of a `Foo`: - - ```mojo - fn func(foo: Foo): - print("implicitly converted Int to Foo:", foo.value) - - fn main(): - func(Int(42)) - ``` - - This can result in complicated errors that are difficult to debug. By default - this implicit behavior is now turned off, so you have to explicitly construct - `Foo`: - - ```mojo - fn main(): - func(Foo(42)) - ``` - - You can still opt into implicit conversions by adding the `@implicit` - decorator. For example, to enable implicit conversions from `Int` to `Foo`: - - ```mojo - struct Foo: - var value: Int - - @implicit - fn __init__(out self, value: Int): - self.value = value - ``` - -- `Arc` has been renamed to `ArcPointer`, for consistency with `OwnedPointer`. - -- `UnsafePointer` parameters (other than the type) are now keyword-only. - -- Inferred-only parameters may now be explicitly bound with keywords, enabling - some important patterns in the standard library: - - ```mojo - struct StringSlice[is_mutable: Bool, //, origin: Origin[is_mutable]]: ... - alias ImmStringSlice = StringSlice[is_mutable=False] - # This auto-parameterizes on the origin, but constrains it to being an - # immutable slice instead of a potentially mutable one. - fn take_imm_slice(a: ImmStringSlice): ... - ``` - -- Added `PythonObject.__contains__`. - ([PR #3101](https://github.com/modularml/mojo/pull/3101) by [@rd4com](https://github.com/rd4com)) - - Example usage: - - ```mojo - x = PythonObject([1,2,3]) - if 1 in x: - print("1 in x") - -- `Span` has moved from the `utils` module to the `memory` module. +### Tooling changes ### ❌ Removed -- The `UnsafePointer.bitcast` overload for `DType` has been removed. Wrap your - `DType` in a `Scalar[my_dtype]` to call the only overload of `bitcast` now. - ### 🛠️ Fixed - -- Lifetime tracking is now fully field sensitive, which makes the uninitialized - variable checker more precise. - -- [Issue #1310](https://github.com/modularml/mojo/issues/1310) - Mojo permits - the use of any constructor for implicit conversions - -- [Issue #1632](https://github.com/modularml/mojo/issues/1632) - Mojo produces - weird error when inout function is used in non mutating function - -- [Issue #3444](https://github.com/modularml/mojo/issues/3444) - Raising init - causing use of uninitialized variable - -- [Issue #3544](https://github.com/modularml/mojo/issues/3544) - Known - mutable `ref` argument are not optimized as `noalias` by LLVM. - -- [Issue #3559](https://github.com/modularml/mojo/issues/3559) - VariadicPack - doesn't extend the lifetimes of the values it references. - -- [Issue #3627](https://github.com/modularml/mojo/issues/3627) - Compiler - overlooked exclusivity violation caused by `ref [MutableAnyOrigin] T` - -- [Issue #3710](https://github.com/modularml/mojo/issues/3710) - Mojo frees - memory while reference to it is still in use. - -- [Issue #3805](https://github.com/modularml/mojo/issues/3805) - Crash When - Initializing !llvm.ptr. - -- [Issue #3816](https://github.com/modularml/mojo/issues/3816) - Ternary - if-operator doesn't propagate origin information. - -- [Issue #3815](https://github.com/modularml/mojo/issues/3815) - - [BUG] Mutability not preserved when taking the union of two origins. - -- [Issue #3829](https://github.com/modularml/mojo/issues/3829) - Poor error - message when invoking a function pointer upon an argument of the wrong origin - -- [Issue #3830](https://github.com/modularml/mojo/issues/3830) - Failures - emitting register RValues to ref arguments. - -- The VS Code extension now auto-updates its private copy of the MAX SDK. - -- The variadic initializer for `SIMD` now works in parameter expressions. - -- The VS Code extension now downloads its private copy of the MAX SDK in a way - that prevents ETXTBSY errors on Linux. - -- The VS Code extension now allows invoking a mojo formatter from SDK - installations that contain white spaces in their path. - -- Error messages that include type names no longer include inferred or defaulted - parameters when they aren't needed. For example, previously Mojo complained - about things like: - - ```plaintext - ... cannot be converted from 'UnsafePointer[UInt, 0, _default_alignment::AnyType](), MutableAnyOrigin]' to 'UnsafePointer[Int, 0, _default_alignment[::AnyType](), MutableAnyOrigin]' - ``` - - it now complains more helpfully that: - - ```plaintext - ... cannot be converted from 'UnsafePointer[UInt]' to 'UnsafePointer[Int]' - ``` - -- Tooling now prints the origins of `ref` arguments and results correctly, and - prints `self` instead of `self: Self` in methods. - -- The LSP and generated documentation now print parametric result types - correctly, e.g. showing `SIMD[type, simd_width]` instead of `SIMD[$0, $1]`. diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 2717a5127a..80684d45b5 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -10,7 +10,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements the memory package.""" +"""The memory package provides several pointer types, as well +as utility functions for dealing with memory.""" from .arc import ArcPointer from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation diff --git a/stdlib/src/memory/owned_pointer.mojo b/stdlib/src/memory/owned_pointer.mojo index 4dd473023c..2fe28a5fcb 100644 --- a/stdlib/src/memory/owned_pointer.mojo +++ b/stdlib/src/memory/owned_pointer.mojo @@ -10,6 +10,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +"""Implements `OwnedPointer`, a safe, single-ownership smart pointer. + +You can import these APIs from the `memory` package. For example: + +```mojo +from memory import OwnedPointer +``` +""" + from memory import UnsafePointer, memcpy, stack_allocation @@ -25,7 +34,7 @@ struct OwnedPointer[T: AnyType]: pointers](/mojo/manual/pointers/) in the Mojo Manual. Parameters: - T: The type to be stored in the OwnedPointer[]. + T: The type to be stored in the `OwnedPointer`. """ var _inner: UnsafePointer[T, address_space = AddressSpace.GENERIC] @@ -35,13 +44,13 @@ struct OwnedPointer[T: AnyType]: # ===-------------------------------------------------------------------===# fn __init__[T: Movable](mut self: OwnedPointer[T], owned value: T): - """Construct a new OwnedPointer[] by moving the passed value into a new backing allocation. + """Construct a new `OwnedPointer` by moving the passed value into a new backing allocation. Parameters: T: The type of the data to store. It is restricted to `Movable` here to allow efficient move construction. Args: - value: The value to move into the OwnedPointer[]. + value: The value to move into the `OwnedPointer`. """ self._inner = UnsafePointer[T].alloc(1) self._inner.init_pointee_move(value^) @@ -49,13 +58,14 @@ struct OwnedPointer[T: AnyType]: fn __init__[ T: ExplicitlyCopyable ](mut self: OwnedPointer[T], *, copy_value: T): - """Construct a new OwnedPointer[] by explicitly copying the passed value into a new backing allocation. + """Construct a new `OwnedPointer` by explicitly copying the passed value into a new backing allocation. Parameters: - T: The type of the data to store. + T: The type of the data to store, which must be + `ExplicitlyCopyable`. Args: - copy_value: The value to explicitly copy into the OwnedPointer[]. + copy_value: The value to explicitly copy into the `OwnedPointer`. """ self._inner = UnsafePointer[T].alloc(1) self._inner.init_pointee_explicit_copy(copy_value) @@ -63,14 +73,14 @@ struct OwnedPointer[T: AnyType]: fn __init__[ T: Copyable, U: NoneType = None ](mut self: OwnedPointer[T], value: T): - """Construct a new OwnedPointer[] by copying the passed value into a new backing allocation. + """Construct a new `OwnedPointer` by copying the passed value into a new backing allocation. Parameters: T: The type of the data to store. U: A dummy type parameter, to lower the selection priority of this ctor. Args: - value: The value to copy into the OwnedPointer[]. + value: The value to copy into the `OwnedPointer`. """ self._inner = UnsafePointer[T].alloc(1) self._inner.init_pointee_copy(value) @@ -78,18 +88,18 @@ struct OwnedPointer[T: AnyType]: fn __init__[ T: ExplicitlyCopyable ](mut self: OwnedPointer[T], *, other: OwnedPointer[T],): - """Construct a new OwnedPointer[] by explicitly copying the value from another OwnedPointer[]. + """Construct a new `OwnedPointer` by explicitly copying the value from another `OwnedPointer`. Parameters: T: The type of the data to store. Args: - other: The OwnedPointer[] to copy. + other: The `OwnedPointer` to copy. """ self.__init__(copy_value=other[]) fn __moveinit__(out self, owned existing: Self): - """Move this OwnedPointer[]. + """Move this `OwnedPointer`. Args: existing: The value to move. @@ -112,7 +122,7 @@ struct OwnedPointer[T: AnyType]: """Returns a reference to the pointers's underlying data with parametric mutability. Returns: - A reference to the data underlying the OwnedPointer[]. + A reference to the data underlying the `OwnedPointer`. """ # This should have a widening conversion here that allows # the mutable ref that is always (potentially unsafely) @@ -126,25 +136,25 @@ struct OwnedPointer[T: AnyType]: # ===-------------------------------------------------------------------===# fn unsafe_ptr(self) -> UnsafePointer[T]: - """UNSAFE: returns the backing pointer for this OwnedPointer[]. + """UNSAFE: returns the backing pointer for this `OwnedPointer`. Returns: - An UnsafePointer to the backing allocation for this OwnedPointer[]. + An UnsafePointer to the backing allocation for this `OwnedPointer`. """ return self._inner fn take[T: Movable](owned self: OwnedPointer[T]) -> T: - """Move the value within the OwnedPointer[] out of it, consuming the - OwnedPointer[] in the process. + """Move the value within the `OwnedPointer` out of it, consuming the + `OwnedPointer` in the process. Parameters: - T: The type of the data backing this OwnedPointer[]. `take()` only exists for T: Movable + T: The type of the data backing this `OwnedPointer`. `take()` only exists for `T: Movable` since this consuming operation only makes sense for types that you want to avoid copying. - For types that are Copy or ExplicitlyCopy but are not Movable, you can copy them through + For types that are `Copyable` or `ExplicitlyCopyable` but are not `Movable`, you can copy them through `__getitem__` as in `var v = some_ptr_var[]`. Returns: - The data that is (was) backing the OwnedPointer[]. + The data that is (was) backing the `OwnedPointer`. """ var r = self._inner.take_pointee() self._inner.free() @@ -156,13 +166,13 @@ struct OwnedPointer[T: AnyType]: """Take ownership over the heap allocated pointer backing this `OwnedPointer`. - Safety: + **Safety:** This function is not unsafe to call, as a memory leak is not considered unsafe. - However, to avoid a memory leak, callers should ensure that the - returned pointer is eventually deinitialized and deallocated. - Failure to do so will leak memory. + However, to avoid a memory leak, callers should ensure that the + returned pointer is eventually deinitialized and deallocated. + Failure to do so will leak memory. Returns: The pointer owned by this instance. diff --git a/stdlib/src/memory/span.mojo b/stdlib/src/memory/span.mojo index 497a084c88..0bbee663fe 100644 --- a/stdlib/src/memory/span.mojo +++ b/stdlib/src/memory/span.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements the Span type. +"""Implements the `Span` type. You can import these APIs from the `memory` module. For example: @@ -55,8 +55,8 @@ struct _SpanIter[ Parameters: is_mutable: Whether the reference to the span is mutable. T: The type of the elements in the span. - origin: The origin of the Span. - forward: The iteration direction. `False` is backwards. + origin: The origin of the `Span`. + forward: The iteration direction. False is backwards. """ var index: Int @@ -98,7 +98,7 @@ struct Span[ T: CollectionElement, origin: Origin[is_mutable], ](CollectionElementNew): - """A non owning view of contiguous data. + """A non-owning view of contiguous data. Parameters: is_mutable: Whether the span is mutable. @@ -127,10 +127,10 @@ struct Span[ @always_inline fn __init__(out self, *, other: Self): - """Explicitly construct a deep copy of the provided Span. + """Explicitly construct a copy of the provided `Span`. Args: - other: The Span to copy. + other: The `Span` to copy. """ self._data = other._data self._len = other._len @@ -138,7 +138,7 @@ struct Span[ @always_inline @implicit fn __init__(out self, ref [origin]list: List[T, *_]): - """Construct a Span from a List. + """Construct a `Span` from a `List`. Args: list: The list to which the span refers. @@ -150,10 +150,10 @@ struct Span[ fn __init__[ size: Int, // ](mut self, ref [origin]array: InlineArray[T, size]): - """Construct a Span from an InlineArray. + """Construct a `Span` from an `InlineArray`. Parameters: - size: The size of the InlineArray. + size: The size of the `InlineArray`. Args: array: The array to which the span refers. @@ -217,19 +217,19 @@ struct Span[ @always_inline fn __iter__(self) -> _SpanIter[T, origin]: - """Get an iterator over the elements of the Span. + """Get an iterator over the elements of the `Span`. Returns: - An iterator over the elements of the Span. + An iterator over the elements of the `Span`. """ return _SpanIter(0, self) @always_inline fn __reversed__(self) -> _SpanIter[T, origin, forward=False]: - """Iterate backwards over the Span. + """Iterate backwards over the `Span`. Returns: - A reversed iterator of the Span elements. + A reversed iterator of the `Span` elements. """ return _SpanIter[forward=False](len(self), self) @@ -262,10 +262,10 @@ struct Span[ fn as_ref(self) -> Pointer[T, origin]: """ - Gets a Pointer to the first element of this slice. + Gets a `Pointer` to the first element of this span. Returns: - A Pointer pointing at the first element of this slice. + A `Pointer` pointing at the first element of this span. """ return Pointer[T, origin].address_of(self._data[0]) @@ -281,7 +281,7 @@ struct Span[ origin: The inferred mutable origin of the data within the Span. Args: - other: The Span to copy all elements from. + other: The `Span` to copy all elements from. """ debug_assert(len(self) == len(other), "Spans must be of equal length") for i in range(len(self)): diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 450c18d199..2d83992bfb 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements types that work with unsafe pointers. +"""Provides utility functions for unsafe manipulation of SIMD values. You can import these APIs from the `memory` package. For example: @@ -35,6 +35,23 @@ fn bitcast[ ](val: SIMD[type, width]) -> SIMD[new_type, new_width]: """Bitcasts a SIMD value to another SIMD value. + For a discussion of byte order, see + [Converting data: bitcasting and byte order](/mojo/manual/pointers/unsafe-pointers#converting-data-bitcasting-and-byte-order) + in the Mojo Manual. + + Examples: + + The following example uses `bitcast` to break a 32-bit integer into a vector + of four 8-bit integers: + + ```mojo + from memory import bitcast + + one = SIMD[DType.uint32, 1](4631) + many = bitcast[DType.uint8, 4](one) + print(one, many) # 4631 [23, 18, 0, 0] + ``` + Constraints: The bitwidth of the two types must be the same. @@ -84,7 +101,19 @@ fn pack_bits[ width: Int, //, new_type: DType = _uint(width), ](val: SIMD[DType.bool, width]) -> Scalar[new_type]: - """Packs a SIMD bool into an integer. + """Packs a SIMD vector of `bool` values into an integer. + + Examples: + + This example packs a vector of 8 `bool` values into a single 8-bit integer. + + ```mojo + from memory import pack_bits + + flags = SIMD[DType.bool, 8](1, 1, 0, 1, 0, 0, 0, 0) + i = pack_bits[DType.uint8](flags) + print(flags, i) # [True, True, False, True, False, False, False, False] 11 + ``` Constraints: The width of the bool vector must be the same as the bitwidth of the diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 047bb61ac7..04fc7c0036 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -1125,13 +1125,14 @@ struct UnsafePointer[ This transfers the value out of `self` and into `dest` using at most one `__moveinit__()` call. - Safety: - * `self` must be non-null - * `self` must contain a valid, initialized instance of `T` - * `dst` must not be null - * The contents of `dst` should be uninitialized. If `dst` was - previously written with a valid value, that value will be be - overwritten and its destructor will NOT be run. + **Safety:** + + * `self` must be non-null + * `self` must contain a valid, initialized instance of `T` + * `dst` must not be null + * The contents of `dst` should be uninitialized. If `dst` was + previously written with a valid value, that value will be be + overwritten and its destructor will NOT be run. Parameters: T: The type the pointer points to, which must be `Movable`. From fa8d0dfcf38fe21dc43ff9887e33fe52edf32800 Mon Sep 17 00:00:00 2001 From: modularbot Date: Tue, 17 Dec 2024 17:05:14 +0000 Subject: [PATCH 2017/2019] [Release] Update lockfiles to point to latest stable version: 24.6.0 --- examples/life/magic.lock | 11867 ++++++++++++++++++++++++++++++++ examples/magic.lock | 9153 ++++++++++++++++++++++++ examples/notebooks/magic.lock | 10972 +++++++++++++++++++++++++++++ examples/operators/magic.lock | 9153 ++++++++++++++++++++++++ magic.lock | 9171 ++++++++++++++++++++++++ 5 files changed, 50316 insertions(+) create mode 100644 examples/life/magic.lock create mode 100644 examples/magic.lock create mode 100644 examples/notebooks/magic.lock create mode 100644 examples/operators/magic.lock create mode 100644 magic.lock diff --git a/examples/life/magic.lock b/examples/life/magic.lock new file mode 100644 index 0000000000..6c01231839 --- /dev/null +++ b/examples/life/magic.lock @@ -0,0 +1,11867 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.13-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.2-h3394656_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.6.4-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fluidsynth-2.3.7-hd992666_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.22.5-he02047a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.22.5-he02047a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-10.1.0-h0b3b770_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.22-h7c63dc7_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.22.5-he8f35ee_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.22.5-he8f35ee_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.1.1-h1909e37_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.71-h39aace5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdb-6.2.32-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-lib-1.11.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.22.5-he02047a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.22.5-he02047a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.51-hbd13f7d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmad-0.15.1b-h0b41bf4_1001.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.9-h0b6a36f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h8d12d68_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opusfile-0.12-h3358134_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.44.2-h29eaf8c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h7c63dc7_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/portmidi-2.0.4-h7c63dc7_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hb77b528_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pygame-2.6.1-py312h4fcb14b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.6.6-he8a937b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.30.10-h63c27ac_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_image-2.8.2-h06ee604_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_mixer-2.6.3-h8830914_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2_ttf-2.22.0-h287479f_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-2.3.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.5-he73a12e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.10-h4f16b4b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/alsa-lib-1.2.13-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aom-3.9.1-hcccb83c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/attr-2.5.1-h4e544f5_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.2-h83712da_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/dav1d-1.2.1-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.13.6-h12b9eeb_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.6.4-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fluidsynth-2.3.7-h4f58cef_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.15.0-h8dda3cd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gettext-0.22.5-h0a1ffab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gettext-tools-0.22.5-h0a1ffab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.13-h2f0025b_1003.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-10.1.0-hbdc1db7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/jack-1.9.22-h5c6c0ed_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lame-3.100-h4e544f5_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libasprintf-0.22.5-h87f4aca_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libasprintf-devel-0.22.5-h87f4aca_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libavif16-1.1.1-h3b0c220_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcap-2.71-h51d75a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdb-6.2.32-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libflac-1.4.3-h2f0025b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcrypt-lib-1.11.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgettextpo-0.22.5-h0a1ffab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgettextpo-devel-0.22.5-h0a1ffab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.82.2-hc486b8e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgpg-error-1.51-h05609ea_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libmad-0.15.1b-hb4cce97_1001.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libogg-1.3.5-h0b9eccb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopus-1.3.1-hf897c2e_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsndfile-1.2.2-h79657aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsystemd0-256.9-ha536d29_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libvorbis-1.3.7-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/mpg123-1.32.9-h65af167_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/opusfile-0.12-hf55b2d5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.44-h070dd5b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.44.2-h86a87f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/portaudio-19.6.0-h5c6c0ed_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/portmidi-2.0.4-h5c6c0ed_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pulseaudio-client-17.0-h729494f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pygame-2.6.1-py312hb2c8110_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rav1e-0.6.6-h1d8f897_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2-2.30.10-h93e764a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2_image-2.8.2-hd95cb85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2_mixer-2.6.3-h422cae6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2_ttf-2.22.0-hb1608df_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/svt-av1-2.3.0-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.5-h0808dbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.10-hca56bd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.6-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.1-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrender-0.9.12-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aom-3.9.1-h7bae524_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.2-h6a3b0d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fluidsynth-2.3.7-h80fea77_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.15.0-h1383a14_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gettext-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gettext-tools-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.13-hebf3989_1003.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-10.1.0-h9df47df_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lame-3.100-h1a8c8d9_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-devel-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.1.1-h45b7238_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libflac-1.4.3-hb765f3a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-devel-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.82.2-h07bd6cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-devel-0.22.5-h8414b35_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmad-0.15.1b-h1a8c8d9_1001.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libogg-1.3.5-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopus-1.3.1-h27ca646_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsndfile-1.2.2-h9739721_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libvorbis-1.3.7-h9f76cd9_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpg123-1.32.9-hf642e45_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/opusfile-0.12-h5643135_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-h297a79d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.44.2-h2f9eb0b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/portaudio-19.6.0-h13dd4ca_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/portmidi-2.0.4-h13dd4ca_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pygame-2.6.1-py312hb14fe3b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.6.6-h69fbcac_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2-2.30.10-h994913f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2_image-2.8.2-h376e2e1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2_mixer-2.6.3-h4fe3bdc_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2_ttf-2.22.0-h443c5de_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/svt-av1-2.3.0-hf24288c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda +packages: +- kind: conda + name: _libgcc_mutex + version: '0.1' + build: conda_forge + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + size: 2562 + timestamp: 1578324546067 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23621 + timestamp: 1650670423406 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 + md5: 6168d71addc746e8f2b8d57dfd2edcea + depends: + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23712 + timestamp: 1650670790230 +- kind: conda + name: aiohappyeyeballs + version: 2.4.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + sha256: 95d4713e49ea92ae50cf42393683ede706b7875af5f7cb14c253438180afa732 + md5: 296b403617bafa89df4971567af79013 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 19351 + timestamp: 1733332029649 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h178313f_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + sha256: dc8ebdd99e9d7a07454a7063a295cdc7a86264648647fec10b2ceae97478e200 + md5: 3e92784b8e32ab7d0b95ee296ba79a99 + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 914378 + timestamp: 1733839626367 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h998013c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + sha256: 69eb9c89dce6a7ae960099172daffba9f77fef39344f37e581685a8e3c5debe6 + md5: 642356223364539ba7ba36556fcf49ee + depends: + - __osx >=11.0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 874135 + timestamp: 1733839113411 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312hcc812fe_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + sha256: df694a9fec546e575a4ea7e1c3ac476c0bda53c0fad44c046ad3ebdd5b75a0a8 + md5: a8c9ec59e6323b38418bbf04deaa0c02 + depends: + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 900931 + timestamp: 1733839037447 +- kind: conda + name: aiosignal + version: 1.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + sha256: 7de8ced1918bbdadecf8e1c1c68237fe5709c097bd9e0d254f4cad118f4345d0 + md5: 1a3981115a398535dbe3f6d5faae3d36 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 13229 + timestamp: 1734342253061 +- kind: conda + name: alsa-lib + version: 1.2.13 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/alsa-lib-1.2.13-h86ecc28_0.conda + sha256: 4141180b0304559fefa8ca66f1cc217a1d957b03aa959f955daf33718162042f + md5: f643bb02c4bbcfe7de161a8ca5df530b + depends: + - libgcc >=13 + license: LGPL-2.1-or-later + license_family: GPL + size: 591318 + timestamp: 1731489774660 +- kind: conda + name: alsa-lib + version: 1.2.13 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.13-hb9d3cd8_0.conda + sha256: f507b58f77eabc0cc133723cb7fc45c053d551f234df85e70fb3ede082b0cd53 + md5: ae1370588aa6a5157c34c73e9bbb36a0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + license_family: GPL + size: 560238 + timestamp: 1731489643707 +- kind: conda + name: annotated-types + version: 0.7.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- kind: conda + name: anyio + version: 4.7.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + sha256: 687537ee3af30f8784986bf40cac30e88138770b16e51ca9850c9c23c09aeba1 + md5: c88107912954a983c2caf25f7fd55158 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.9 + - sniffio >=1.1 + - typing_extensions >=4.5 + constrains: + - trio >=0.26.1 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 112730 + timestamp: 1733532678437 +- kind: conda + name: aom + version: 3.9.1 + build: h7bae524_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aom-3.9.1-h7bae524_0.conda + sha256: ec238f18ce8140485645252351a0eca9ef4f7a1c568a420f240a585229bc12ef + md5: 7adba36492a1bb22d98ffffe4f6fc6de + depends: + - __osx >=11.0 + - libcxx >=16 + license: BSD-2-Clause + license_family: BSD + size: 2235747 + timestamp: 1718551382432 +- kind: conda + name: aom + version: 3.9.1 + build: hac33072_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + sha256: b08ef033817b5f9f76ce62dfcac7694e7b6b4006420372de22494503decac855 + md5: 346722a0be40f6edc53f12640d301338 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 2706396 + timestamp: 1718551242397 +- kind: conda + name: aom + version: 3.9.1 + build: hcccb83c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aom-3.9.1-hcccb83c_0.conda + sha256: ac438ce5d3d3673a9188b535fc7cda413b479f0d52536aeeac1bd82faa656ea0 + md5: cc744ac4efe5bcaa8cca51ff5b850df0 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 3250813 + timestamp: 1718551360260 +- kind: conda + name: attr + version: 2.5.1 + build: h166bdaf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2 + sha256: 82c13b1772c21fc4a17441734de471d3aabf82b61db9b11f4a1bd04a9c4ac324 + md5: d9c69a24ad678ffce24c6543a0176b00 + depends: + - libgcc-ng >=12 + license: GPL-2.0-or-later + license_family: GPL + size: 71042 + timestamp: 1660065501192 +- kind: conda + name: attr + version: 2.5.1 + build: h4e544f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/attr-2.5.1-h4e544f5_1.tar.bz2 + sha256: 2c793b48e835a8fac93f1664c706442972a0206963bf8ca202e83f7f4d29a7d7 + md5: 1ef6c06fec1b6f5ee99ffe2152e53568 + depends: + - libgcc-ng >=12 + license: GPL-2.0-or-later + license_family: GPL + size: 74992 + timestamp: 1660065534958 +- kind: conda + name: attrs + version: 24.3.0 + build: pyh71513ae_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + sha256: 750186af694a7130eaf7119fbb56db0d2326d8995ad5b8eae23c622b85fea29a + md5: 356927ace43302bf6f5926e2a58dae6a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 56354 + timestamp: 1734348889193 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h2cb9fb3_15 + build_number: 15 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + sha256: 4ce859dc9ff128bf5515604c43f33fb511386022fc9765ca077990f2a3f23df5 + md5: e524686ace966acefb5b8cbc6e8b3daa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 111854 + timestamp: 1734021745104 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h8bc59a9_15 + build_number: 15 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + sha256: 0e41e56b662e76e024182adebcd91d09a4d38a83b35217c84e4967354dfff9a2 + md5: f688b8893c20ad9477a19e7ce614014a + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + license: Apache-2.0 + license_family: Apache + size: 92507 + timestamp: 1734021831330 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: hb921021_15 + build_number: 15 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + sha256: 537006ad6d5097c134494166a6a1dc1451d5d050878d7b82cef498bfda40ba8a + md5: c79d50f64cffa5ad51ecc1a81057962f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 107614 + timestamp: 1734021692519 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h1a47875_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + sha256: 095ac824ea9303eff67e04090ae531d9eb33d2bf8f82eaade39b839c421e16e8 + md5: 55a8561fdbbbd34f50f57d9be12ed084 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 47601 + timestamp: 1733991564405 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h740c5af_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + sha256: c5c7961d48ca7320ed3560c036f7aa5244df4b85d9657f70aacc5faba3e1509a + md5: 57ed2c445d7ef01d121b9bcea0522913 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 50036 + timestamp: 1733991581303 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: hc8a0bd2_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + sha256: 1f44be36e1daa17b4b081debb8aee492d13571084f38b503ad13e869fef24fe4 + md5: 8b0ce61384e5a33d2b301a64f3d22ac5 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 39925 + timestamp: 1733991649383 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a + md5: 145e5b4c9702ed279d7d68aaf096f77d + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221863 + timestamp: 1733975576886 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + sha256: 57288ec5df35781bea8fc6a8c9099cad6695b747784fc1b8862a0f9e5b3bf5ab + md5: fef806a0f6de853670c746bbece01966 + depends: + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 259031 + timestamp: 1733975520465 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 + md5: d7d4680337a14001b0e043e96529409b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 236574 + timestamp: 1733975453350 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h0f0193d_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + sha256: 3f05d19f68ef800f33d44ea2a4211003124076516c8469abc7d432236344df53 + md5: 3a1421d12435df5b4c412cc4c8fac64d + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19740 + timestamp: 1733991625201 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h4e1184b_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b + md5: 3f4c1197462a6df2be6dc8241828fe93 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19086 + timestamp: 1733991637424 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: hc8a0bd2_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + sha256: 47b2813f652ce7e64ac442f771b2a5f7d4af4ad0d07ff51f6075ea80ed2e3f09 + md5: a8b6c17732d14ed49d0e9b59c43186bc + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 18068 + timestamp: 1733991869211 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h54f970a_11 + build_number: 11 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d + md5: ba41238f8e653998d7d2f42e3a8db054 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 47078 + timestamp: 1734024749727 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h7959bf6_11 + build_number: 11 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + sha256: 10d7240c7db0c941fb1a59c4f8ea6689a434b03309ee7b766fa15a809c553c02 + md5: 9b3fb60fe57925a92f399bc3fc42eccf + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 54003 + timestamp: 1734024480949 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: hcbd8f92_11 + build_number: 11 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + sha256: 79aa363c71c891a27496c0498f8d498b2ddc87b3ccb3b6c9da5b50b05936ebb8 + md5: e0772c59af4243a9b2565baa5d79e5b6 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 55207 + timestamp: 1734024546663 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h3df160d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + sha256: 3a1d2d332945306be9d49e569b95e4cc172d825f10e88715513a172f28ebb59e + md5: 28f00aa7fd9556c4c461328cf146c20b + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 190586 + timestamp: 1734008442362 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h96aa502_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + sha256: 22e4737c8a885995b7c1ae1d79c1f6e78d489e16ec079615980fdde067aeaf76 + md5: 495c93a4f08b17deb3c04894512330e6 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 152983 + timestamp: 1734008451473 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: hefd7a92_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 + md5: 5ce4df662d32d3123ea8da15571b6f51 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 197731 + timestamp: 1734008380764 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h1a307af_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + sha256: 71f5bf891299f831dceaea12f926c393bf754569e5305387a88b77e1f94612d8 + md5: da8ab0f3eeac93449ec3d531ede92caa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 161889 + timestamp: 1734433686109 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h831e299_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + sha256: 5920009b1c6f9a2bc131a36725251894e4b4773fce29c4b1065d4213ae337abe + md5: 80dd9f0ddf935290d1dc00ec75ff3023 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 157864 + timestamp: 1734433578570 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: haba67d1_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + sha256: c0a1a2b0750225ac3dc07fd258c88c2be866bf8ac67ba3d50bb4ecec852ff8ee + md5: 4c5ff4134e76426a75b8c548984fa933 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 135729 + timestamp: 1734433832730 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h11f4f37_12 + build_number: 12 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + sha256: 512d3969426152d9d5fd886e27b13706122dc3fa90eb08c37b0d51a33d7bb14a + md5: 96c3e0221fa2da97619ee82faa341a73 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 194672 + timestamp: 1734025626798 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h24f418c_12 + build_number: 12 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd + md5: c072045a6206f88015d02fcba1705ea1 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 134371 + timestamp: 1734025379525 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h5f50e26_12 + build_number: 12 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + sha256: ffeb9100cc8fd4093e1a6fdfd938bc4a396dd77480b7fb17aa42855a4d5e2c70 + md5: 031ca33115d4b1eeb43f435d6215778c + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 169516 + timestamp: 1734025167885 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h1be5864_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + sha256: 22966164d63808689fffd35945f57756c95337327e28099b5d77b29fc6a56ecc + md5: a37bba7acb62dd70492ee01eacca3b8f + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + license: Apache-2.0 + license_family: Apache + size: 97598 + timestamp: 1734146239038 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h2080895_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + sha256: 20bc2dd60e6518d9b8215c2b652ab5c52ee8a997d3b9a5f69e2dabd28cbf26b2 + md5: ae223efa63fbb4262a2d85c3ab3bc4f5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 117641 + timestamp: 1734146239779 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: hf454442_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + sha256: c2f205a7bf64c5f40eea373b3a0a7c363c9aa9246a13dd7f3d9c6a4434c4fe2d + md5: 947c82025693bebd557f782bb5d6b469 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 114156 + timestamp: 1734146123386 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + sha256: ede8e782467c87ac80ceb9c9af9e917d121b7d8b8c698186d18e3cecd36f2210 + md5: 53e798d720dd78b78847a7b2fdb05fc9 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 58621 + timestamp: 1733994421495 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + sha256: df586f42210af1134b1c88ff4c278c3cb6d6c807c84eac48860062464b28554d + md5: a5126a90e74ac739b00564a4c7ddcc36 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 56094 + timestamp: 1733994449690 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + sha256: de98343ce42d2e569b3380292d20f47bf39bda08aadabcbb8e650d3f38fd742f + md5: 22f72f8cd7ead211304ac17d337d96e0 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 49664 + timestamp: 1733994553014 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da + md5: 3bd35b0adab3d743f09e0252cc441d6b + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72154 + timestamp: 1733994384415 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + sha256: 1ed9a332d06ad595694907fad2d6d801082916c27cd5076096fda4061e6d24a8 + md5: 74e8c3e4df4ceae34aa2959df4b28101 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72762 + timestamp: 1733994347547 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + sha256: 215086d95e8ff1d3fcb0197ada116cc9d7db1fdae7573f5e810d20fa9215b47c + md5: e70e88a357a3749b67679c0788c5b08a + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 70186 + timestamp: 1733994496998 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h19a973c_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + sha256: 8269e6746eb3a5d15b732a3983888bf98dfc1f6594e95250fc8d16b43cfd5ff9 + md5: 95714136bef3e917bd5a2942d4682b20 + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 236249 + timestamp: 1734178020924 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h8a4e35f_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + sha256: 5ba9188e0cb4e3faff9bc96774febb040aa3b802aedba29d847e00e7b5eab84e + md5: d77a9e3d7ce15399903e92825fd651b5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 283154 + timestamp: 1734177845248 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: hd92328a_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + sha256: 094cd81f1e5ba713e9e7a272ee52b5dde3ccc4842ea90f19c0354a00bbdac3d9 + md5: 02b95564257d5c3db9c06beccf711f95 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 354703 + timestamp: 1734177883319 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: h849ce1a_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + sha256: 51b9e9df8cbab4a13a1b9d39d6ef5ed162aaa29c09a745810e00bbe92e1045c1 + md5: cda7747f4398be8d1fb37362815917a7 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2920625 + timestamp: 1734093552712 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: hc430e4a_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + sha256: 2dc09f6f9c49127b5f96e7535b64a9c521b944d76d8b7d03d48ae80257ac1cea + md5: aeefac461bea1f126653c1285cf5af08 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 3060561 + timestamp: 1734093737431 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: he0ff2e4_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + sha256: 535b970aaa13be45f8cab8205c59f044b17364111c41a227f061775a5c834e18 + md5: 0981ed87098b149bdb7d99a4a3fd0e58 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2826534 + timestamp: 1734094018287 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h1887c18_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 + md5: e0c3a906a41be769f0ae20ca3e31cfc0 + depends: + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 338650 + timestamp: 1728055589907 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h5cfcd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a + md5: 0a8838771cc2e985cd295e01ae83baf1 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 345117 + timestamp: 1728053909574 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: hd50102c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + sha256: f5b91329ed59ffc0be8747784c6e4cc7e56250c54032883a83bc11808ef6a87e + md5: f093a11dcf3cdcca010b20a818fcc6dc + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 294299 + timestamp: 1728054014060 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h113e628_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + sha256: 286b31616c191486626cb49e9ceb5920d29394b9e913c23adb7eb637629ba4de + md5: 73f73f60854f325a55f1d31459f2ab73 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 232351 + timestamp: 1728486729511 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h47b0b28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + sha256: 1c72423b9beba167d2f01b80dc204da77240a8266f1edb3d89510c852b300d69 + md5: 94e73a7877743a85c57091d8afab2348 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 217132 + timestamp: 1728488096615 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: hc602bab_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + sha256: bde446b916fff5150606f8ed3e6058ffc55a3aa72381e46f1ab346590b1ae40a + md5: d7b71593a937459f2d4b67e1a4727dc2 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 166907 + timestamp: 1728486882502 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h185ecfd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 + md5: 221e1e5ecb2643e113f32b3229d5ba33 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 502934 + timestamp: 1728580241002 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h3cf044e_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 + md5: 7eb66060455c7a47d9dcdbfa9f46579b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 549342 + timestamp: 1728578123088 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h7585a09_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + sha256: 08d52d130addc0fb55d5ba10d9fa483e39be25d69bac7f4c676c2c3069207590 + md5: 704238ef05d46144dae2e6b5853df8bc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 438636 + timestamp: 1728578216193 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h1b94036_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 + md5: 793b1080ab2d958980f137a8643cd6e8 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 140832 + timestamp: 1728565334900 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h736e048_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba + md5: 13de36be8de3ae3f05ba127631599213 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 149312 + timestamp: 1728563338704 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h9ca1f76_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + sha256: 77ab04e8fe5636a2de9c718f72a43645f7502cd208868c8a91ffba385547d585 + md5: 7a187cd7b1445afc80253bb186a607cc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 121278 + timestamp: 1728563418777 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: h37d6d07_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc + md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 260547 + timestamp: 1728730924071 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: ha633028_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 + md5: 7c1980f89dd41b097549782121a73490 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 287366 + timestamp: 1728729530295 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: hcdd55da_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + sha256: f48523f8aa0b5b80f45a92f0556b388dd96f44ac2dc2f44a01d08c1822eec97d + md5: c49fbc5233fcbaa86391162ff1adef38 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 196032 + timestamp: 1728729672889 +- kind: conda + name: backoff + version: 2.2.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 + md5: a38b801f2bcc12af80c2e02a9e4ce7d9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 18816 + timestamp: 1733771192649 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h2ec8cdc_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f + md5: b0b867af6fc74b2a0aa206da29c0f3cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + size: 349867 + timestamp: 1725267732089 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h6f74592_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + sha256: 9736bf660a0e4260c68f81d2635b51067f817813e6490ac9e8abd9a835dcbf6d + md5: e1e9727063057168d95f27a032acd0a4 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 h86ecc28_2 + license: MIT + license_family: MIT + size: 356878 + timestamp: 1725267878508 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312hde4cb15_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + sha256: 254b411fa78ccc226f42daf606772972466f93e9bc6895eabb4cfda22f5178af + md5: a83c2ef76ccb11bc2349f4f17696b15d + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 339360 + timestamp: 1725268143995 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h4bc722e_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 252783 + timestamp: 1720974456583 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h68df207_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + sha256: 2258b0b33e1cb3a9852d47557984abb6e7ea58e3d7f92706ec1f8e879290c4cb + md5: 56398c28220513b9ea13d7b450acfb20 + depends: + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 189884 + timestamp: 1720974504976 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h99b78c6_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 122909 + timestamp: 1720974522888 +- kind: conda + name: c-ares + version: 1.34.4 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f + md5: c1c999a38a4303b29d75c636eaa13cf9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 179496 + timestamp: 1734208291879 +- kind: conda + name: c-ares + version: 1.34.4 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + sha256: 1187a41d4bb2afe02cb18690682edc98d1e9f5e0ccda638d8704a75ea1875bbe + md5: 356da36f35d36dcba16e43f1589d4e39 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 215979 + timestamp: 1734208193181 +- kind: conda + name: c-ares + version: 1.34.4 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 + md5: e2775acf57efd5af15b8e3d1d74d72d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 206085 + timestamp: 1734208189009 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hbcca054_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + sha256: 1afd7274cbc9a334d6d0bc62fa760acc7afdaceb0b91a8df370ec01fd75dc7dd + md5: 720523eb0d6a9b0f6120c16b2aa4e7de + license: ISC + size: 157088 + timestamp: 1734208393264 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hcefe29a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + sha256: ad7b43211051332a5a4e788bb4619a2d0ecb5be73e0f76be17f733a87d7effd1 + md5: 83b4ad1e6dc14df5891f3fcfdeb44351 + license: ISC + size: 157096 + timestamp: 1734209301744 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hf0a4a13_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + sha256: 256be633fd0882ccc1a7a32bc278547e1703f85082c0789a87a603ee3ab8fb82 + md5: 7cb381a6783d91902638e4ed1ebd478e + license: ISC + size: 157091 + timestamp: 1734208344343 +- kind: conda + name: cairo + version: 1.18.2 + build: h3394656_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.2-h3394656_1.conda + sha256: de7d0d094e53decc005cb13e527be2635b8f604978da497d4c0d282c7dc08385 + md5: b34c2833a1f56db610aeb27f206d800d + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + - xorg-libice >=1.1.1,<2.0a0 + - xorg-libsm >=1.2.4,<2.0a0 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + size: 978868 + timestamp: 1733790976384 +- kind: conda + name: cairo + version: 1.18.2 + build: h6a3b0d2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.2-h6a3b0d2_1.conda + sha256: 9a28344e806b89c87fda0cdabd2fb961e5d2ff97107dba25bac9f5dc57220cc3 + md5: 8e3666c3f6e2c3e57aa261ab103a3600 + depends: + - __osx >=11.0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libcxx >=18 + - libexpat >=2.6.4,<3.0a0 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + license: LGPL-2.1-only or MPL-1.1 + size: 894517 + timestamp: 1733791145035 +- kind: conda + name: cairo + version: 1.18.2 + build: h83712da_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.2-h83712da_1.conda + sha256: 0353e175859c4989251628e4c8f9fb2dc52546b0c031ffe4541eb087ac586573 + md5: e7b46975d2c9a4666da0e9bb8a087f28 + depends: + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + - xorg-libice >=1.1.1,<2.0a0 + - xorg-libsm >=1.2.4,<2.0a0 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + size: 980455 + timestamp: 1733791018944 +- kind: conda + name: certifi + version: 2024.12.14 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + sha256: 048c16a9cbcb1fbad02083414d3bc7c1d0eea4b39aee6aa6bf8d1d5089ca8bad + md5: 6feb87357ecd66733be3279f16a8c400 + depends: + - python >=3.9 + license: ISC + size: 161642 + timestamp: 1734380604767 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h06ac9bb_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 + md5: a861504bbea4161a9170b85d4d2be840 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 294403 + timestamp: 1725560714366 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h0fad829_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f + md5: 19a5456f72f505881ba493979777b24e + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 281206 + timestamp: 1725560813378 +- kind: conda + name: cffi + version: 1.17.1 + build: py312hac81daf_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + sha256: 1162e3ca039e7ca7c0e78f0a020ed1bde968096841b663e3f393c966eb82f0f0 + md5: 1a256e5581b1099e9295cb84d53db3ea + depends: + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 312892 + timestamp: 1725561779888 +- kind: conda + name: charset-normalizer + version: 3.4.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + sha256: 63022ee2c6a157a9f980250a66f54bdcdf5abee817348d0f9a74c2441a6fbf0e + md5: 6581a17bba6b948bb60130026404a9d6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 47533 + timestamp: 1733218182393 +- kind: conda + name: click + version: 8.1.7 + build: unix_pyh707e725_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + sha256: 1cd5fc6ccdd5141378e51252a7a3810b07fd5a7e6934a5b4a7eccba66566224b + md5: cb8e52f28f5e592598190c562e7b5bf1 + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 84513 + timestamp: 1733221925078 +- kind: conda + name: colorama + version: 0.4.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- kind: conda + name: datasets + version: 2.14.4 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + sha256: 7e09bd083a609138b780fcc4535924cb96814d2c908a36d4c64a2ba9ee3efe7f + md5: 3e087f072ce03c43a9b60522f5d0ca2f + depends: + - aiohttp + - dill >=0.3.0,<0.3.8 + - fsspec >=2021.11.1 + - huggingface_hub >=0.14.0,<1.0.0 + - importlib-metadata + - multiprocess + - numpy >=1.17 + - packaging + - pandas + - pyarrow >=8.0.0 + - python >=3.8.0 + - python-xxhash + - pyyaml >=5.1 + - requests >=2.19.0 + - tqdm >=4.62.1 + license: Apache-2.0 + license_family: Apache + size: 347303 + timestamp: 1691593908658 +- kind: conda + name: dav1d + version: 1.2.1 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/dav1d-1.2.1-h31becfc_0.conda + sha256: 33fe66d025cf5bac7745196d1a3dd7a437abcf2dbce66043e9745218169f7e17 + md5: 6e5a87182d66b2d1328a96b61ca43a62 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 347363 + timestamp: 1685696690003 +- kind: conda + name: dav1d + version: 1.2.1 + build: hb547adb_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + sha256: 93e077b880a85baec8227e8c72199220c7f87849ad32d02c14fb3807368260b8 + md5: 5a74cdee497e6b65173e10d94582fae6 + license: BSD-2-Clause + license_family: BSD + size: 316394 + timestamp: 1685695959391 +- kind: conda + name: dav1d + version: 1.2.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + sha256: 22053a5842ca8ee1cf8e1a817138cdb5e647eb2c46979f84153f6ad7bde73020 + md5: 418c6ca5929a611cbd69204907a83995 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 760229 + timestamp: 1685695754230 +- kind: conda + name: dbus + version: 1.13.6 + build: h12b9eeb_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.13.6-h12b9eeb_3.tar.bz2 + sha256: 5fe76bdf27a142cfb9da0fb3197c562e528d2622b573765bee5c9904cf5e6b6b + md5: f3d63805602166bac09386741e00935e + depends: + - expat >=2.4.2,<3.0a0 + - libgcc-ng >=9.4.0 + - libglib >=2.70.2,<3.0a0 + license: GPL-2.0-or-later + license_family: GPL + size: 672759 + timestamp: 1640113663539 +- kind: conda + name: dbus + version: 1.13.6 + build: h5008d03_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 + sha256: 8f5f995699a2d9dbdd62c61385bfeeb57c82a681a7c8c5313c395aa0ccab68a5 + md5: ecfff944ba3960ecb334b9a2663d708d + depends: + - expat >=2.4.2,<3.0a0 + - libgcc-ng >=9.4.0 + - libglib >=2.70.2,<3.0a0 + license: GPL-2.0-or-later + license_family: GPL + size: 618596 + timestamp: 1640112124844 +- kind: conda + name: deprecated + version: 1.2.15 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + sha256: a20ebf2c9b02a6eb32412ceb5c4cffaae49417db7e75414a76417538293a9402 + md5: eaef2e94d5bd76f758545d172c1fda67 + depends: + - python >=3.9 + - wrapt <2,>=1.10 + license: MIT + license_family: MIT + size: 14297 + timestamp: 1733662697343 +- kind: conda + name: dill + version: 0.3.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + sha256: 4ff20c6be028be2825235631c45d9e4a75bca1de65f8840c02dfb28ea0137c45 + md5: 5e4f3466526c52bc9af2d2353a1460bd + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + size: 87553 + timestamp: 1690101185422 +- kind: conda + name: dnspython + version: 2.7.0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 + md5: 5fbd60d61d21b4bd2f9d7a48fe100418 + depends: + - python >=3.9,<4.0.0 + - sniffio + constrains: + - aioquic >=1.0.0 + - wmi >=1.5.1 + - httpx >=0.26.0 + - trio >=0.23 + - cryptography >=43 + - httpcore >=1.0.0 + - idna >=3.7 + - h2 >=4.1.0 + license: ISC + license_family: OTHER + size: 172172 + timestamp: 1733256829961 +- kind: conda + name: email-validator + version: 2.2.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + sha256: b91a19eb78edfc2dbb36de9a67f74ee2416f1b5273dd7327abe53f2dbf864736 + md5: da16dd3b0b71339060cd44cb7110ddf9 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.9 + license: Unlicense + size: 44401 + timestamp: 1733300827551 +- kind: conda + name: email_validator + version: 2.2.0 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + sha256: e0d0fdf587aa0ed0ff08b2bce3ab355f46687b87b0775bfba01cc80a859ee6a2 + md5: 0794f8807ff2c6f020422cacb1bd7bfa + depends: + - email-validator >=2.2.0,<2.2.1.0a0 + license: Unlicense + size: 6552 + timestamp: 1733300828176 +- kind: conda + name: exceptiongroup + version: 1.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 + md5: a16662747cdeb9abbac74d0057cc976e + depends: + - python >=3.9 + license: MIT and PSF-2.0 + size: 20486 + timestamp: 1733208916977 +- kind: conda + name: expat + version: 2.6.4 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/expat-2.6.4-h5888daf_0.conda + sha256: 1848c7db9e264e3b8036ee133d570dd880422983cd20dd9585a505289606d276 + md5: 1d6afef758879ef5ee78127eb4cd2c4a + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat 2.6.4 h5888daf_0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 138145 + timestamp: 1730967050578 +- kind: conda + name: expat + version: 2.6.4 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.6.4-h5ad3122_0.conda + sha256: 13905ad49c2f43776bac0e464ffd3c9ec10ef35cc7dd7e187af6f66f843fa29a + md5: e8f1d587055376ea2419cc78696abd0b + depends: + - libexpat 2.6.4 h5ad3122_0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 130354 + timestamp: 1730967212801 +- kind: conda + name: fastapi + version: 0.115.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + sha256: d7826d537c667093c9de96411a09585a8d620c84a830a0195e58e9a0df45f018 + md5: 1b1e0c97830cdf75f1f371bd467ab657 + depends: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.5 + - httpx >=0.23.0 + - jinja2 >=2.11.2 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python >=3.9 + - python-multipart >=0.0.7 + - starlette >=0.40.0,<0.42.0 + - typing_extensions >=4.8.0 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + size: 73084 + timestamp: 1733362427885 +- kind: conda + name: fastapi-cli + version: 0.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 + md5: d960e0ea9e1c561aa928f6c4439f04c7 + depends: + - python >=3.9 + - rich-toolkit >=0.11.1 + - typer >=0.12.3 + - uvicorn-standard >=0.15.0 + license: MIT + license_family: MIT + size: 15546 + timestamp: 1734302408607 +- kind: conda + name: filelock + version: 3.16.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + sha256: 18dca6e2194732df7ebf824abaefe999e4765ebe8e8a061269406ab88fc418b9 + md5: d692e9ba6f92dc51484bf3477e36ce7c + depends: + - python >=3.9 + license: Unlicense + size: 17441 + timestamp: 1733240909987 +- kind: conda + name: fluidsynth + version: 2.3.7 + build: h4f58cef_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/fluidsynth-2.3.7-h4f58cef_0.conda + sha256: e557d4fcb40f224180d61cc5e57fe3d5d5793a255c7d5a29546e524ef2ffa298 + md5: 0207cc67431f4d12605cae60d8d323d7 + depends: + - alsa-lib >=1.2.12,<1.3.0a0 + - jack >=1.9.22,<1.10.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libstdcxx >=13 + - portaudio >=19.6.0,<19.7.0a0 + - pulseaudio-client >=17.0,<17.1.0a0 + - readline >=8.2,<9.0a0 + - sdl2 >=2.30.7,<3.0a0 + license: GPL-2.0-or-later + license_family: LGPL + size: 292770 + timestamp: 1729590405853 +- kind: conda + name: fluidsynth + version: 2.3.7 + build: h80fea77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/fluidsynth-2.3.7-h80fea77_0.conda + sha256: e4c39119797493d4f085cd6274e123372bec77e05ef93203ccf5dee714bfd9c3 + md5: dc2fc082a05af6a790d3e8e3e6489e6c + depends: + - __osx >=11.0 + - libcxx >=17 + - libglib >=2.82.2,<3.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - portaudio >=19.6.0,<19.7.0a0 + - readline >=8.2,<9.0a0 + - sdl2 >=2.30.7,<3.0a0 + license: GPL-2.0-or-later + license_family: LGPL + size: 234967 + timestamp: 1729590579216 +- kind: conda + name: fluidsynth + version: 2.3.7 + build: hd992666_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/fluidsynth-2.3.7-hd992666_0.conda + sha256: 0bf26d25ae79e6f5f01a49a00e9ba3b60b10dd4c12ec43bdba51055c26bc9dd6 + md5: dd6c7b8a1b217ef7522ca987c465651d + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.12,<1.3.0a0 + - jack >=1.9.22,<1.10.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libstdcxx >=13 + - portaudio >=19.6.0,<19.7.0a0 + - pulseaudio-client >=17.0,<17.1.0a0 + - readline >=8.2,<9.0a0 + - sdl2 >=2.30.7,<3.0a0 + license: GPL-2.0-or-later + license_family: LGPL + size: 279996 + timestamp: 1729590344462 +- kind: conda + name: font-ttf-dejavu-sans-mono + version: '2.37' + build: hab24e00_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b + md5: 0c96522c6bdaed4b1566d11387caaf45 + license: BSD-3-Clause + license_family: BSD + size: 397370 + timestamp: 1566932522327 +- kind: conda + name: font-ttf-inconsolata + version: '3.000' + build: h77eed37_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + sha256: c52a29fdac682c20d252facc50f01e7c2e7ceac52aa9817aaf0bb83f7559ec5c + md5: 34893075a5c9e55cdafac56607368fc6 + license: OFL-1.1 + license_family: Other + size: 96530 + timestamp: 1620479909603 +- kind: conda + name: font-ttf-source-code-pro + version: '2.038' + build: h77eed37_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + sha256: 00925c8c055a2275614b4d983e1df637245e19058d79fc7dd1a93b8d9fb4b139 + md5: 4d59c254e01d9cde7957100457e2d5fb + license: OFL-1.1 + license_family: Other + size: 700814 + timestamp: 1620479612257 +- kind: conda + name: font-ttf-ubuntu + version: '0.83' + build: h77eed37_3 + build_number: 3 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + sha256: 2821ec1dc454bd8b9a31d0ed22a7ce22422c0aef163c59f49dfdf915d0f0ca14 + md5: 49023d73832ef61042f6a237cb2687e7 + license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 + license_family: Other + size: 1620504 + timestamp: 1727511233259 +- kind: conda + name: fontconfig + version: 2.15.0 + build: h1383a14_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.15.0-h1383a14_1.conda + sha256: f79d3d816fafbd6a2b0f75ebc3251a30d3294b08af9bb747194121f5efa364bc + md5: 7b29f48742cea5d1ccb5edd839cb5621 + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - libexpat >=2.6.3,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 234227 + timestamp: 1730284037572 +- kind: conda + name: fontconfig + version: 2.15.0 + build: h7e30c49_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + sha256: 7093aa19d6df5ccb6ca50329ef8510c6acb6b0d8001191909397368b65b02113 + md5: 8f5b0b297b59e1ac160ad4beec99dbee + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - libexpat >=2.6.3,<3.0a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 265599 + timestamp: 1730283881107 +- kind: conda + name: fontconfig + version: 2.15.0 + build: h8dda3cd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.15.0-h8dda3cd_1.conda + sha256: fe023bb8917c8a3138af86ef537b70c8c5d60c44f93946a87d1e8bb1a6634b55 + md5: 112b71b6af28b47c624bcbeefeea685b + depends: + - freetype >=2.12.1,<3.0a0 + - libexpat >=2.6.3,<3.0a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 277832 + timestamp: 1730284967179 +- kind: conda + name: fonts-conda-ecosystem + version: '1' + build: '0' + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + sha256: a997f2f1921bb9c9d76e6fa2f6b408b7fa549edd349a77639c9fe7a23ea93e61 + md5: fee5683a3f04bd15cbd8318b096a27ab + depends: + - fonts-conda-forge + license: BSD-3-Clause + license_family: BSD + size: 3667 + timestamp: 1566974674465 +- kind: conda + name: fonts-conda-forge + version: '1' + build: '0' + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 + sha256: 53f23a3319466053818540bcdf2091f253cbdbab1e0e9ae7b9e509dcaa2a5e38 + md5: f766549260d6815b0c52253f1fb1bb29 + depends: + - font-ttf-dejavu-sans-mono + - font-ttf-inconsolata + - font-ttf-source-code-pro + - font-ttf-ubuntu + license: BSD-3-Clause + license_family: BSD + size: 4102 + timestamp: 1566932280397 +- kind: conda + name: freetype + version: 2.12.1 + build: h267a509_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + sha256: b2e3c449ec9d907dd4656cb0dc93e140f447175b125a3824b31368b06c666bb6 + md5: 9ae35c3d96db2c94ce0cef86efdfa2cb + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 634972 + timestamp: 1694615932610 +- kind: conda + name: freetype + version: 2.12.1 + build: hadb7bae_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 + md5: e6085e516a3e304ce41a8ee08b9b89ad + depends: + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 596430 + timestamp: 1694616332835 +- kind: conda + name: freetype + version: 2.12.1 + build: hf0a5ef3_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + sha256: 7af93030f4407f076dce181062360efac2cd54dce863b5d7765287a6f5382537 + md5: a5ab74c5bd158c3d5532b66d8d83d907 + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 642092 + timestamp: 1694617858496 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + sha256: 44d6d6b332421e621c029fb149f12dba1ccb5ed6ac632e2e807a9d92d6cb2864 + md5: 7960352935cc95ac23883c9b8c97f2ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 53366 + timestamp: 1729699762631 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + sha256: 7e0c12983b20f2816b3712729b5a35ecb7ee152132ca7cf805427c62395ea823 + md5: f98e36c96b2c66d9043187179ddb04f4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 60968 + timestamp: 1729699568442 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + sha256: b0a9ff3e71452eed70877b2f3175d41cd85070da6deac381c5f3f61e1f19bccb + md5: 62fc11b0738ca15e0dd19b60cf280d12 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 59967 + timestamp: 1729699642726 +- kind: conda + name: fsspec + version: 2024.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + sha256: 790a50b4f94042951518f911a914a886a837c926094c6a14ed1d9d03ce336807 + md5: 906fe13095e734cb413b57a49116cdc8 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 134726 + timestamp: 1733493445080 +- kind: conda + name: gettext + version: 0.22.5 + build: h0a1ffab_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gettext-0.22.5-h0a1ffab_3.conda + sha256: 25b0b40329537f374a7394474376b01fd226e31f3ff3aa9254e8d328b23c2145 + md5: be78ccdd273e43e27e66fc1629df6576 + depends: + - gettext-tools 0.22.5 h0a1ffab_3 + - libasprintf 0.22.5 h87f4aca_3 + - libasprintf-devel 0.22.5 h87f4aca_3 + - libgcc-ng >=12 + - libgettextpo 0.22.5 h0a1ffab_3 + - libgettextpo-devel 0.22.5 h0a1ffab_3 + - libstdcxx-ng >=12 + license: LGPL-2.1-or-later AND GPL-3.0-or-later + size: 481962 + timestamp: 1723626297896 +- kind: conda + name: gettext + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gettext-0.22.5-h8414b35_3.conda + sha256: 634e11f6e6560568ede805f823a2be8634c6a0a2fa6743880ec403d925923138 + md5: 89b31a91b3ac2b7b3b0e5bc4eb99c39d + depends: + - __osx >=11.0 + - gettext-tools 0.22.5 h8414b35_3 + - libasprintf 0.22.5 h8414b35_3 + - libasprintf-devel 0.22.5 h8414b35_3 + - libcxx >=16 + - libgettextpo 0.22.5 h8414b35_3 + - libgettextpo-devel 0.22.5 h8414b35_3 + - libiconv >=1.17,<2.0a0 + - libintl 0.22.5 h8414b35_3 + - libintl-devel 0.22.5 h8414b35_3 + license: LGPL-2.1-or-later AND GPL-3.0-or-later + size: 483255 + timestamp: 1723627203687 +- kind: conda + name: gettext + version: 0.22.5 + build: he02047a_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.22.5-he02047a_3.conda + sha256: c3d9a453f523acbf2b3e1c82a42edfc7c7111b4686a2180ab48cb9b51a274218 + md5: c7f243bbaea97cd6ea1edd693270100e + depends: + - __glibc >=2.17,<3.0.a0 + - gettext-tools 0.22.5 he02047a_3 + - libasprintf 0.22.5 he8f35ee_3 + - libasprintf-devel 0.22.5 he8f35ee_3 + - libgcc-ng >=12 + - libgettextpo 0.22.5 he02047a_3 + - libgettextpo-devel 0.22.5 he02047a_3 + - libstdcxx-ng >=12 + license: LGPL-2.1-or-later AND GPL-3.0-or-later + size: 479452 + timestamp: 1723626088190 +- kind: conda + name: gettext-tools + version: 0.22.5 + build: h0a1ffab_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gettext-tools-0.22.5-h0a1ffab_3.conda + sha256: 9846b9d2e3d081cc8cb9ac7800c7e02a7b63bceea8619e0c51cfa271f89afdb2 + md5: 5fc8dfe3163ead62e0af82d97ce6b486 + depends: + - libgcc-ng >=12 + license: GPL-3.0-or-later + license_family: GPL + size: 2954814 + timestamp: 1723626262722 +- kind: conda + name: gettext-tools + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gettext-tools-0.22.5-h8414b35_3.conda + sha256: 50b530cf2326938b80330f78cf4056492fa8c6a5c7e313d92069ebbbb2f4d264 + md5: 47071f4b2915032e1d47119f779f9d9c + depends: + - __osx >=11.0 + - libiconv >=1.17,<2.0a0 + - libintl 0.22.5 h8414b35_3 + license: GPL-3.0-or-later + license_family: GPL + size: 2467439 + timestamp: 1723627140130 +- kind: conda + name: gettext-tools + version: 0.22.5 + build: he02047a_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.22.5-he02047a_3.conda + sha256: 0fd003953ce1ce9f4569458aab9ffaa397e3be2bc069250e2f05fd93b0ad2976 + md5: fcd2016d1d299f654f81021e27496818 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: GPL-3.0-or-later + license_family: GPL + size: 2750908 + timestamp: 1723626056740 +- kind: conda + name: gflags + version: 2.2.2 + build: h5888daf_1005 + build_number: 1005 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 119654 + timestamp: 1726600001928 +- kind: conda + name: gflags + version: 2.2.2 + build: h5ad3122_1005 + build_number: 1005 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + sha256: 28fe6b40b20454106d5e4ef6947cf298c13cc72a46347bbc49b563cd3a463bfa + md5: 4ff634d515abbf664774b5e1168a9744 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 106638 + timestamp: 1726599967617 +- kind: conda + name: gflags + version: 2.2.2 + build: hf9b8971_1005 + build_number: 1005 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + size: 82090 + timestamp: 1726600145480 +- kind: conda + name: glog + version: 0.7.1 + build: h468a4a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 + md5: 08940a32c6ced3703d1412dd37df4f62 + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 145811 + timestamp: 1718284208668 +- kind: conda + name: glog + version: 0.7.1 + build: hbabe93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 143452 + timestamp: 1718284177264 +- kind: conda + name: glog + version: 0.7.1 + build: heb240a5_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + size: 112215 + timestamp: 1718284365403 +- kind: conda + name: googleapis-common-protos + version: 1.66.0 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + sha256: d8d19575a827f2c62500949b9536efdd6b5406c9f546a73b6a87ac90b03a5875 + md5: 4861e30ff0cd566ea6fb4593e3b7c22a + depends: + - protobuf >=3.20.2,<6.0.0.dev0,!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 116522 + timestamp: 1731459019854 +- kind: conda + name: graphite2 + version: 1.3.13 + build: h2f0025b_1003 + build_number: 1003 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.13-h2f0025b_1003.conda + sha256: c7585e1fb536120583790080f3b3875c04d5f2d64eafbc87e9aa39895e4118c0 + md5: f33009add6a08358bc12d114ceec1304 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LGPL-2.0-or-later + license_family: LGPL + size: 99453 + timestamp: 1711634223220 +- kind: conda + name: graphite2 + version: 1.3.13 + build: h59595ed_1003 + build_number: 1003 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda + sha256: 0595b009f20f8f60f13a6398e7cdcbd2acea5f986633adcf85f5a2283c992add + md5: f87c7b7c2cb45f323ffbce941c78ab7c + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LGPL-2.0-or-later + license_family: LGPL + size: 96855 + timestamp: 1711634169756 +- kind: conda + name: graphite2 + version: 1.3.13 + build: hebf3989_1003 + build_number: 1003 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.13-hebf3989_1003.conda + sha256: 2eadafbfc52f5e7df3da3c3b7e5bbe34d970bea1d645ffe60b0b1c3a216657f5 + md5: 339991336eeddb70076d8ca826dac625 + depends: + - libcxx >=16 + license: LGPL-2.0-or-later + license_family: LGPL + size: 79774 + timestamp: 1711634444608 +- kind: conda + name: h11 + version: 0.14.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 + md5: 7ee49e89531c0dcbba9466f6d115d585 + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 51846 + timestamp: 1733327599467 +- kind: conda + name: h2 + version: 4.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + sha256: 843ddad410c370672a8250470697027618f104153612439076d4d7b91eeb7b5c + md5: 825927dc7b0f287ef8d4d0011bb113b1 + depends: + - hpack >=4.0,<5 + - hyperframe >=6.0,<7 + - python >=3.9 + license: MIT + license_family: MIT + size: 52000 + timestamp: 1733298867359 +- kind: conda + name: harfbuzz + version: 10.1.0 + build: h0b3b770_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-10.1.0-h0b3b770_0.conda + sha256: da2b3b3c1fc34444fa484ed227e4c2d313cdff2ed3ce5a45d01f07b78f9273f8 + md5: ab1d7d56034814f4c3ed9f69f8c68806 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.2,<2.0a0 + - freetype >=2.12.1,<3.0a0 + - graphite2 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 1600521 + timestamp: 1733706966476 +- kind: conda + name: harfbuzz + version: 10.1.0 + build: h9df47df_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-10.1.0-h9df47df_0.conda + sha256: 8b56a8e0847a2a86a80211f5c5e4f19d0d7fa0be12cc1a5337e555857757cc6d + md5: bbd10a18fb41d0892fbb3aa810b4937d + depends: + - __osx >=11.0 + - cairo >=1.18.2,<2.0a0 + - freetype >=2.12.1,<3.0a0 + - graphite2 + - icu >=75.1,<76.0a0 + - libcxx >=18 + - libexpat >=2.6.4,<3.0a0 + - libglib >=2.82.2,<3.0a0 + license: MIT + license_family: MIT + size: 1357252 + timestamp: 1733707517728 +- kind: conda + name: harfbuzz + version: 10.1.0 + build: hbdc1db7_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-10.1.0-hbdc1db7_0.conda + sha256: 69a269f04f72632f5949e422c2ff673e408a76a9bf451e4e4e58a0996e1e8e65 + md5: 881e8d9b31e1a7335d4dea4d66851bc0 + depends: + - cairo >=1.18.2,<2.0a0 + - freetype >=2.12.1,<3.0a0 + - graphite2 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 1626591 + timestamp: 1733709685847 +- kind: conda + name: hpack + version: 4.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + sha256: ec89b7e5b8aa2f0219f666084446e1fb7b54545861e9caa892acb24d125761b5 + md5: 2aa5ff7fa34a81b9196532c84c10d865 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 29412 + timestamp: 1733299296857 +- kind: conda + name: httpcore + version: 1.0.7 + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df + md5: 2ca8e6dbc86525c8b95e3c0ffa26442e + depends: + - python >=3.8 + - h11 >=0.13,<0.15 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=3.0,<5.0 + - certifi + license: BSD-3-Clause + license_family: BSD + size: 48959 + timestamp: 1731707562362 +- kind: conda + name: httptools + version: 0.6.4 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + sha256: 621e7e050b888e5239d33e37ea72d6419f8367e5babcad38b755586f20264796 + md5: 8b1160b32557290b64d5be68db3d996d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101872 + timestamp: 1732707756745 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + sha256: 0bd1f30224af142711d11033a7469ae402a1147143f399f7341bbc1d8178c722 + md5: 5e70a6de59352f9a52e9caa7f3447390 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101255 + timestamp: 1732707891645 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + sha256: 5e93cda79e32e8c0039e05ea1939e688da336187dab025f699b42ef529e848be + md5: e1747a8e8d2aca5499aaea9993bf31ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 85623 + timestamp: 1732707871414 +- kind: conda + name: httpx + version: 0.28.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- kind: conda + name: huggingface_hub + version: 0.26.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + sha256: 0c75532d914a04c73222be298ed2c6868739dd475b1b1a9137c52abe79873952 + md5: 73937038e21117fe401f8ea64fbaeacc + depends: + - filelock + - fsspec >=2023.5.0 + - packaging >=20.9 + - python >=3.9 + - pyyaml >=5.1 + - requests + - tqdm >=4.42.1 + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + size: 275466 + timestamp: 1733852454004 +- kind: conda + name: hyperframe + version: 6.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + sha256: e91c6ef09d076e1d9a02819cd00fa7ee18ecf30cdd667605c853980216584d1b + md5: 566e75c90c1d0c8c459eb0ad9833dc7a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17239 + timestamp: 1733298862681 +- kind: conda + name: icu + version: '75.1' + build: he02047a_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12129203 + timestamp: 1720853576813 +- kind: conda + name: icu + version: '75.1' + build: hf9b3779_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 + md5: 268203e8b983fddb6412b36f2024e75c + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12282786 + timestamp: 1720853454991 +- kind: conda + name: icu + version: '75.1' + build: hfee45f7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 11857802 + timestamp: 1720853997952 +- kind: conda + name: idna + version: '3.10' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 + md5: 39a4f67be3286c86d696df570b1201b7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 49765 + timestamp: 1733211921194 +- kind: conda + name: importlib-metadata + version: 8.5.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 + md5: 315607a3030ad5d5227e76e0733798ff + depends: + - python >=3.9 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + size: 28623 + timestamp: 1733223207185 +- kind: conda + name: jack + version: 1.9.22 + build: h5c6c0ed_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/jack-1.9.22-h5c6c0ed_2.conda + sha256: 4b5714f0798fb38e19d0aced7e5a9069eebf3c8ee9f541d643d4e6d0edcf388f + md5: e6abd2a51bd727a1b62a54524f760864 + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libdb >=6.2.32,<6.3.0a0 + - libgcc-ng >=12 + - libopus >=1.3.1,<2.0a0 + - libstdcxx-ng >=12 + license: LGPL-2.0-only + license_family: LGPL + size: 489040 + timestamp: 1693881455137 +- kind: conda + name: jack + version: 1.9.22 + build: h7c63dc7_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.22-h7c63dc7_2.conda + sha256: 5e44a3a4b9791d1268636811628555ad40d4a8dd5c3be3334062df75580ae25b + md5: f56277b7f079f1b13cbf7fb9b4f194c4 + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libdb >=6.2.32,<6.3.0a0 + - libgcc-ng >=12 + - libopus >=1.3.1,<2.0a0 + - libstdcxx-ng >=12 + license: LGPL-2.0-only + license_family: LGPL + size: 464144 + timestamp: 1693879949990 +- kind: conda + name: jinja2 + version: 3.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + sha256: 85a7169c078b8065bd9d121b0e7b99c8b88c42a411314b6ae5fcd81c48c4710a + md5: 08cce3151bde4ecad7885bd9fb647532 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110963 + timestamp: 1733217424408 +- kind: conda + name: jupyter_client + version: 8.6.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a + md5: 4ebae00eae9705b0c3d6d1018a81d047 + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106342 + timestamp: 1733441040958 +- kind: conda + name: jupyter_core + version: 5.7.2 + build: pyh31011fe_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd + md5: 0a2980dada0dd7fd0998f0342308b1b1 + depends: + - __unix + - platformdirs >=2.5 + - python >=3.8 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 57671 + timestamp: 1727163547058 +- kind: conda + name: keyutils + version: 1.6.1 + build: h166bdaf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + sha256: 150c05a6e538610ca7c43beb3a40d65c90537497a4f6a5f4d15ec0451b6f5ebb + md5: 30186d27e2c9fa62b45fb1476b7200e3 + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 117831 + timestamp: 1646151697040 +- kind: conda + name: keyutils + version: 1.6.1 + build: h4e544f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + sha256: 6d4233d97a9b38acbb26e1268bcf8c10a8e79c2aed7e5a385ec3769967e3e65b + md5: 1f24853e59c68892452ef94ddd8afd4b + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 112327 + timestamp: 1646166857935 +- kind: conda + name: krb5 + version: 1.21.3 + build: h237132a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- kind: conda + name: krb5 + version: 1.21.3 + build: h50a48e9_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 + md5: 29c10432a2ca1472b53f299ffb2ffa37 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1474620 + timestamp: 1719463205834 +- kind: conda + name: krb5 + version: 1.21.3 + build: h659f571_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1370023 + timestamp: 1719463201255 +- kind: conda + name: lame + version: '3.100' + build: h166bdaf_1003 + build_number: 1003 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + sha256: aad2a703b9d7b038c0f745b853c6bb5f122988fe1a7a096e0e606d9cbec4eaab + md5: a8832b479f93521a9e7b5b743803be51 + depends: + - libgcc-ng >=12 + license: LGPL-2.0-only + license_family: LGPL + size: 508258 + timestamp: 1664996250081 +- kind: conda + name: lame + version: '3.100' + build: h1a8c8d9_1003 + build_number: 1003 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lame-3.100-h1a8c8d9_1003.tar.bz2 + sha256: f40ce7324b2cf5338b766d4cdb8e0453e4156a4f83c2f31bbfff750785de304c + md5: bff0e851d66725f78dc2fd8b032ddb7e + license: LGPL-2.0-only + license_family: LGPL + size: 528805 + timestamp: 1664996399305 +- kind: conda + name: lame + version: '3.100' + build: h4e544f5_1003 + build_number: 1003 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lame-3.100-h4e544f5_1003.tar.bz2 + sha256: 2502904a42df6d94bd743f7b73915415391dd6d31d5f50cb57c0a54a108e7b0a + md5: ab05bcf82d8509b4243f07e93bada144 + depends: + - libgcc-ng >=12 + license: LGPL-2.0-only + license_family: LGPL + size: 604863 + timestamp: 1664997611416 +- kind: conda + name: lcms2 + version: '2.16' + build: h922389a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + sha256: be4847b1014d3cbbc524a53bdbf66182f86125775020563e11d914c8468dd97d + md5: ffdd8267a04c515e7ce69c727b051414 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 296219 + timestamp: 1701647961116 +- kind: conda + name: lcms2 + version: '2.16' + build: ha0e7c42_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + sha256: 151e0c84feb7e0747fabcc85006b8973b22f5abbc3af76a9add0b0ef0320ebe4 + md5: 66f6c134e76fe13cce8a9ea5814b5dd5 + depends: + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 211959 + timestamp: 1701647962657 +- kind: conda + name: lcms2 + version: '2.16' + build: hb7c19ff_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 + md5: 51bb7010fc86f70eee639b4bb7a894f5 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 245247 + timestamp: 1701647787198 +- kind: conda + name: ld_impl_linux-64 + version: '2.43' + build: h712a8e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + sha256: 7c91cea91b13f4314d125d1bedb9d03a29ebbd5080ccdea70260363424646dbe + md5: 048b02e3962f066da18efe3a21b77672 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 669211 + timestamp: 1729655358674 +- kind: conda + name: ld_impl_linux-aarch64 + version: '2.43' + build: h80caac9_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + sha256: 80ec7e8f006196808fac5bd4b3773a652847f97bbf08044cd87731424ac64f8b + md5: fcbde5ea19d55468953bf588770c0501 + constrains: + - binutils_impl_linux-aarch64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 698245 + timestamp: 1729655345825 +- kind: conda + name: lerc + version: 4.0.0 + build: h27087fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + sha256: cb55f36dcd898203927133280ae1dc643368af041a48bcf7c026acb7c47b0c12 + md5: 76bbff344f0134279f225174e9064c8f + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 281798 + timestamp: 1657977462600 +- kind: conda + name: lerc + version: 4.0.0 + build: h4de3ea5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + sha256: 2d09ef9b7796d83364957e420b41c32d94e628c3f0520b61c332518a7b5cd586 + md5: 1a0ffc65e03ce81559dbcb0695ad1476 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 262096 + timestamp: 1657978241894 +- kind: conda + name: lerc + version: 4.0.0 + build: h9a09cb3_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + sha256: 6f068bb53dfb6147d3147d981bb851bb5477e769407ad4e6a68edf482fdcb958 + md5: de462d5aacda3b30721b512c5da4e742 + depends: + - libcxx >=13.0.1 + license: Apache-2.0 + license_family: Apache + size: 215721 + timestamp: 1657977558796 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + sha256: 8f91429091183c26950f1e7ffa730e8632f0627ba35d2fccd71df31628c9b4e5 + md5: e1f604644fe8d78e22660e2fec6756bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1310521 + timestamp: 1727295454064 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + sha256: 590e47dce38031a8893e70491f3b71e214de7781cab53b6f017aa6f6841cb076 + md5: 6fe6b3694c4792a8e26755d3b06f0b80 + depends: + - libgcc >=13 + - libstdcxx >=13 + constrains: + - abseil-cpp =20240722.0 + - libabseil-static =20240722.0=cxx17* + license: Apache-2.0 + license_family: Apache + size: 1328502 + timestamp: 1727295490806 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_hf9b8971_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + sha256: 90bf08a75506dfcf28a70977da8ab050bcf594cd02abd3a9d84a22c9e8161724 + md5: 706da5e791c569a7b9814877098a6a0a + depends: + - __osx >=11.0 + - libcxx >=17 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1179072 + timestamp: 1727295571173 +- kind: conda + name: libarrow + version: 18.1.0 + build: h1b535d6_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + sha256: 087b579aebf351ca41c54214121d86a15a41c92051cbd432d6f3a3f58a8c31b0 + md5: 4c0ad68efba1113ac5833975c67b565d + depends: + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8040629 + timestamp: 1733810319239 +- kind: conda + name: libarrow + version: 18.1.0 + build: h44a453e_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + sha256: abf17e99b03356a9d6248e965826c1352ff01b00d3a62cc51393bb0744d72803 + md5: 2cf6d608d6e66506f69797d5c6944c35 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8786061 + timestamp: 1733810643966 +- kind: conda + name: libarrow + version: 18.1.0 + build: h4a2f8bd_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + sha256: 9ed3ea1bc15005c0df187268ef91407afaa908cf82f36f5acbbf50ac24d7f806 + md5: 835cdd84195b84dc34d128bd5d3580b9 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libcxx >=18 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 5494797 + timestamp: 1733808145854 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + sha256: fdb70e2499e59b730084ecd53008b361a6f6090b5fb49624feda06b7e84c7b8c + md5: c50907eefe2ae22d826e7cb2e4d712f5 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 578091 + timestamp: 1733810378092 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + sha256: a32fa1d71415afc02b5cf3cd4c0a6ec0af9e749308829cc65ff79689222ce479 + md5: 143f9288b64759a6427563f058c62f2b + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 611745 + timestamp: 1733810698469 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + sha256: e1cae46409927470439ef9ae93ed09b3493d0579501ca9ebfa79ded212ee98d8 + md5: 97fc01254714e1572624baefdd7cc898 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + license: Apache-2.0 + license_family: APACHE + size: 483713 + timestamp: 1733808246880 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + sha256: 2a08f5a1017ff660c37ae0c24343a119cb2511c6edd69e23d0a5090a0967ea35 + md5: bb1548ad011c4f9107fcc4cc548473bf + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libparquet 18.1.0 hfc78867_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 559673 + timestamp: 1733810461646 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + sha256: 74eeb178070002842d3ed721769399320e3a68a0843319eaf899a092a31def26 + md5: 20ca46a6bc714a6ab189d5b3f46e66d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libparquet 18.1.0 h081d1f1_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 586627 + timestamp: 1733810842604 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + sha256: 6eba942ce926419f74e6e0a7c3994a7d78ab6be47115e6bb70e02136554736be + md5: 0774276be6659aaa0007f1b0f6ee19b0 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libparquet 18.1.0 h636d7b7_6_cpu + license: Apache-2.0 + license_family: APACHE + size: 489948 + timestamp: 1733809328231 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ee7192_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + sha256: bda6728db019dd0c409b1996ad9ef6ab0bcee3a94dc66a8045e8c1049c566055 + md5: aa313b3168caf98d00b3753f5ba27650 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libarrow-dataset 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 519989 + timestamp: 1733810903274 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ffb4b1_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + sha256: 9f78c55c5d7122e588a6f226cbf7e909c479d66ed18edc633d68324323d386b9 + md5: 5db2e6832397b8ca70a6f7b00e0c3629 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libarrow-dataset 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 515928 + timestamp: 1733810503359 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h86344ea_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + sha256: bafd9ca59ebb5ad34b77aff316ef7b59c5fb1eb8a7b6a15de8dcbdf3ce37556d + md5: c1c162f5bf569cff8bed6def705a899f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libarrow-dataset 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 451623 + timestamp: 1733809487176 +- kind: conda + name: libasprintf + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.22.5-h8414b35_3.conda + sha256: 819bf95543470658f48db53a267a3fabe1616797c4031cf88e63f451c5029e6f + md5: 472b673c083175195965a48f2f4808f8 + depends: + - __osx >=11.0 + - libcxx >=16 + license: LGPL-2.1-or-later + size: 40657 + timestamp: 1723626937704 +- kind: conda + name: libasprintf + version: 0.22.5 + build: h87f4aca_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libasprintf-0.22.5-h87f4aca_3.conda + sha256: b438814a7190a541950da68d3cde8ecbcc55629ce677eb65afbb01cfa1e4e651 + md5: 332ce64c2dec75dc0849e7ffcdf7a3a4 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LGPL-2.1-or-later + size: 42627 + timestamp: 1723626204541 +- kind: conda + name: libasprintf + version: 0.22.5 + build: he8f35ee_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.22.5-he8f35ee_3.conda + sha256: 2da5c735811cbf38c7f7844ab457ff8b25046bbf5fe5ebd5dc1c2fafdf4fbe1c + md5: 4fab9799da9571266d05ca5503330655 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LGPL-2.1-or-later + size: 42817 + timestamp: 1723626012203 +- kind: conda + name: libasprintf-devel + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-devel-0.22.5-h8414b35_3.conda + sha256: ca7322f7c3f1a68cb36630eaa88a44c774261150d42d70a4be3d77bc9ed28d5d + md5: a03ca97f9fabf5626660697c2e0b8850 + depends: + - __osx >=11.0 + - libasprintf 0.22.5 h8414b35_3 + license: LGPL-2.1-or-later + size: 34648 + timestamp: 1723626983419 +- kind: conda + name: libasprintf-devel + version: 0.22.5 + build: h87f4aca_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libasprintf-devel-0.22.5-h87f4aca_3.conda + sha256: c9eda86140a5a023b72a8997f82629f4b071df17d57d00ba75a66b65a0525a5e + md5: dbaa9d8c0030bda3e3d22d325ea48191 + depends: + - libasprintf 0.22.5 h87f4aca_3 + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 34359 + timestamp: 1723626223953 +- kind: conda + name: libasprintf-devel + version: 0.22.5 + build: he8f35ee_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.22.5-he8f35ee_3.conda + sha256: ccc7967e298ddf3124c8ad9741c7180dc6f778ae4135ec87978214f7b3c64dc2 + md5: 1091193789bb830127ed067a9e01ac57 + depends: + - __glibc >=2.17,<3.0.a0 + - libasprintf 0.22.5 he8f35ee_3 + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 34172 + timestamp: 1723626026096 +- kind: conda + name: libavif16 + version: 1.1.1 + build: h1909e37_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.1.1-h1909e37_2.conda + sha256: e06da844b007a64a9ac35d4e3dc4dbc66583f79b57d08166cf58f2f08723a6e8 + md5: 21e468ed3786ebcb2124b123aa2484b7 + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libgcc >=13 + - rav1e >=0.6.6,<1.0a0 + - svt-av1 >=2.3.0,<2.3.1.0a0 + license: BSD-2-Clause + license_family: BSD + size: 116202 + timestamp: 1730268687453 +- kind: conda + name: libavif16 + version: 1.1.1 + build: h3b0c220_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libavif16-1.1.1-h3b0c220_2.conda + sha256: 8284693096532fb5d1af7fd5c170a4d4f0a54593ba29c881667327b61a7cd7bb + md5: 58ec6027e7b6ea460a4a7ed7112842d8 + depends: + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libgcc >=13 + - rav1e >=0.6.6,<1.0a0 + - svt-av1 >=2.3.0,<2.3.1.0a0 + license: BSD-2-Clause + license_family: BSD + size: 116097 + timestamp: 1730268695528 +- kind: conda + name: libavif16 + version: 1.1.1 + build: h45b7238_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.1.1-h45b7238_2.conda + sha256: c671365e8c822d29b53f20c4573fdbc70f18b50ff9a4b5b2b6b3c8f7ad2ac2a9 + md5: 7571064a60bc193ff5c25f36ed23394a + depends: + - __osx >=11.0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - rav1e >=0.6.6,<1.0a0 + - svt-av1 >=2.3.0,<2.3.1.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96781 + timestamp: 1730268761553 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + sha256: 30bd658682b124243f8e52d8edf8a19e7be1bc31e4fe4baec30a64002dc8cd0c + md5: ac52800af2e0c0e7dac770b435ce768a + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16393 + timestamp: 1734432564346 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: df6d8ee34d45cf35609ecdd55c1ff03e32e0cd87ae41ebe4ef3747a8e09ead4d + md5: 8d900b7079a00969d70305e9aad550b7 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16477 + timestamp: 1734432576699 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + sha256: 597f9c3779caa979c8c6abbb3ba8c7191b84e1a910d6b0d10e5faf35284c450c + md5: 21be102c9ae80a67ba7de23b129aa7f6 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16714 + timestamp: 1734433054681 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 + md5: 3ee026955c688f551a9999840cff4c67 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 68982 + timestamp: 1725267774142 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 + md5: 41b599ed2b02abcfdd84302bff174b23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 68851 + timestamp: 1725267660471 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + sha256: 839dacb741bdbb25e58f42088a2001b649f4f12195aeb700b5ddfca3267749e5 + md5: d0bf1dff146b799b319ea0434b93f779 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 68426 + timestamp: 1725267943211 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c + md5: e64d0f3b59c7c4047446b97a8624a72d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 31708 + timestamp: 1725267783442 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 32696 + timestamp: 1725267669305 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + sha256: 6c6862eb274f21a7c0b60e5345467a12e6dda8b9af4438c66d496a2c1a538264 + md5: 55e66e68ce55523a6811633dd1ac74e2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 28378 + timestamp: 1725267980316 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c + md5: 0e9bd365480c72b25c71a448257b537d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 290230 + timestamp: 1725267792697 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 + md5: 06f70867945ea6a84d35836af780f1de + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 281750 + timestamp: 1725267679782 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + sha256: eeb1eb0d58b9d02bc1b98dc0a058f104ab168eb2f7d1c7bfa0570a12cfcdb7b7 + md5: 4f3a434504c67b2c42565c0b85c1885c + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 279644 + timestamp: 1725268003553 +- kind: conda + name: libcap + version: '2.71' + build: h39aace5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.71-h39aace5_0.conda + sha256: 2bbefac94f4ab8ff7c64dc843238b6c8edcc9ff1f2b5a0a48407a904dc7ccfb2 + md5: dd19e4e3043f6948bd7454b946ee0983 + depends: + - __glibc >=2.17,<3.0.a0 + - attr >=2.5.1,<2.6.0a0 + - libgcc >=13 + license: BSD-3-Clause + license_family: BSD + size: 102268 + timestamp: 1729940917945 +- kind: conda + name: libcap + version: '2.71' + build: h51d75a7_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcap-2.71-h51d75a7_0.conda + sha256: 2b66e66e6a0768e833e7edc764649679881ec0a6b37d9bf254b1ceb3b8b434ef + md5: 29f6092b6e938516ca0b042837e64fa5 + depends: + - attr >=2.5.1,<2.6.0a0 + - libgcc >=13 + license: BSD-3-Clause + license_family: BSD + size: 106877 + timestamp: 1729940936697 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + sha256: 9c74e536c9bc868e356ffd43f81c2cb398aec84b40fcadc312315b164a5500ee + md5: ebcc5f37a435aa3c19640533c82f8d76 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16336 + timestamp: 1734432570482 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: 521e78be0c4170f229c43e1a6c94337a72db3ebcbe6e5960f8413aa438dcb8f9 + md5: d77f943ae4083f3aeddca698f2d28262 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16398 + timestamp: 1734432580937 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + sha256: 27a29ef6b2fd2179bc3a0bb9db351f078ba140ca10485dca147c399639f84c93 + md5: a0e9980fe12d42f6d0c0ec009f67e948 + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16628 + timestamp: 1734433061517 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea + md5: 268ee639c17ada0002fb04dd21816cc2 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 18669 + timestamp: 1633683724891 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 20440 + timestamp: 1633683576494 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: hbdafb3b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + size: 18765 + timestamp: 1633683992603 +- kind: conda + name: libcurl + version: 8.11.1 + build: h332b0f4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + sha256: 3cd4075b2a7b5562e46c8ec626f6f9ca57aeecaa94ff7df57eca26daa94c9906 + md5: 2b3e0081006dc21e8bf53a91c83a055c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 423011 + timestamp: 1733999897624 +- kind: conda + name: libcurl + version: 8.11.1 + build: h6702fde_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + sha256: 9fc65d21a58f4aad1bc39dfb94a178893aeb035850c5cf0ed9736674279f390b + md5: 7dec1cd271c403d1636bda5aa388a55d + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 440737 + timestamp: 1733999835504 +- kind: conda + name: libcurl + version: 8.11.1 + build: h73640d1_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + sha256: f47c35938144c23278987c7d12096f6a42d7c850ffc277222b032073412383b6 + md5: 46d7524cabfdd199bffe63f8f19a552b + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 385098 + timestamp: 1734000160270 +- kind: conda + name: libcxx + version: 19.1.5 + build: ha82da77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + sha256: 7918cc0bb7a6554cdd3eee634c3dc414a1ab8ec49faeca1567367bb92118f9d7 + md5: 3c7be0df28ccda1d193ea6de56dcb5ff + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 519819 + timestamp: 1733291654212 +- kind: conda + name: libdb + version: 6.2.32 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdb-6.2.32-h01db608_0.tar.bz2 + sha256: 1f74d30c72d95c39315c6b5c3f1b328d00c4d5a2feff1e871fe5b71b4cb26811 + md5: 7cbfba14d5adfa65db945d5112909394 + depends: + - libgcc-ng >=9.3.0 + - libstdcxx-ng >=9.3.0 + license: AGPL-3.0-only + license_family: AGPL + size: 24449415 + timestamp: 1609538998176 +- kind: conda + name: libdb + version: 6.2.32 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdb-6.2.32-h9c3ff4c_0.tar.bz2 + sha256: 21fac1012ff05b131d4b5d284003dbbe7b5c4c652aa9e401b46279ed5a784372 + md5: 3f3258d8f841fbac63b36b75bdac1afd + depends: + - libgcc-ng >=9.3.0 + - libstdcxx-ng >=9.3.0 + license: AGPL-3.0-only + license_family: AGPL + size: 24409456 + timestamp: 1609539093147 +- kind: conda + name: libdeflate + version: '1.23' + build: h4ddbbb0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + sha256: 511d801626d02f4247a04fff957cc6e9ec4cc7e8622bd9acd076bcdc5de5fe66 + md5: 8dfae1d2e74767e9ce36d5fa0d8605db + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + size: 72255 + timestamp: 1734373823254 +- kind: conda + name: libdeflate + version: '1.23' + build: h5e3c512_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + sha256: 959419d87cd2b789a9055db95704c614f31aeb70bef7949fa2f734122a3a2863 + md5: 7e7ca2607b11b180120cefc2354fc0cb + depends: + - libgcc >=13 + license: MIT + size: 69862 + timestamp: 1734373858306 +- kind: conda + name: libdeflate + version: '1.23' + build: hec38601_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + sha256: 887c02deaed6d583459eba6367023e36d8761085b2f7126e389424f57155da53 + md5: 1d8b9588be14e71df38c525767a1ac30 + depends: + - __osx >=11.0 + license: MIT + size: 54132 + timestamp: 1734373971372 +- kind: conda + name: libedit + version: 3.1.20191231 + build: hc8eb9b7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca + md5: 30e4362988a2623e9eb34337b83e01f9 + depends: + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96607 + timestamp: 1597616630749 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: a57d37c236d8f7c886e01656f4949d9dcca131d2a0728609c6f7fa338b65f1cf + md5: 4d331e44109e3f0e19b4cb8f9b82f3e1 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 123878 + timestamp: 1597616541093 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: debc31fb2f07ba2b0363f90e455873670734082822926ba4a9556431ec0bf36d + md5: 29371161d77933a54fccf1bb66b96529 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 134104 + timestamp: 1597617110769 +- kind: conda + name: libev + version: '4.33' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + sha256: 973af77e297f1955dd1f69c2cbdc5ab9dfc88388a5576cd152cda178af0fd006 + md5: a9a13cb143bbaa477b1ebaefbe47a302 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 115123 + timestamp: 1702146237623 +- kind: conda + name: libev + version: '4.33' + build: h93a5062_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + size: 107458 + timestamp: 1702146414478 +- kind: conda + name: libev + version: '4.33' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 112766 + timestamp: 1702146165126 +- kind: conda + name: libevent + version: 2.1.12 + build: h2757513_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 368167 + timestamp: 1685726248899 +- kind: conda + name: libevent + version: 2.1.12 + build: h4ba1bb4_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + sha256: 01333cc7d6e6985dd5700b43660d90e9e58049182017fd24862088ecbe1458e4 + md5: 96ae6083cd1ac9f6bc81631ac835b317 + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 438992 + timestamp: 1685726046519 +- kind: conda + name: libevent + version: 2.1.12 + build: hf998b51_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 427426 + timestamp: 1685725977222 +- kind: conda + name: libexpat + version: 2.6.4 + build: h286801f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 + md5: 38d2656dd914feb0cab8c629370768bf + depends: + - __osx >=11.0 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 64693 + timestamp: 1730967175868 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 + md5: db833e03127376d461e1e13e76f09b6c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 73304 + timestamp: 1730967041968 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + sha256: f42e758009ba9db90d1fe7992bc3e60d0c52f71fb20923375d2c44ae69a5a2b3 + md5: f1b3fab36861b3ce945a13f0dfdfc688 + depends: + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 72345 + timestamp: 1730967203789 +- kind: conda + name: libffi + version: 3.4.2 + build: h3422bc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 + license: MIT + license_family: MIT + size: 39020 + timestamp: 1636488587153 +- kind: conda + name: libffi + version: 3.4.2 + build: h3557bc0_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + sha256: 7e9258a102480757fe3faeb225a3ca04dffd10fecd2a958c65cdb4cdf75f2c3c + md5: dddd85f4d52121fab0a8b099c5e06501 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 59450 + timestamp: 1636488255090 +- kind: conda + name: libffi + version: 3.4.2 + build: h7f98852_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 58292 + timestamp: 1636488182923 +- kind: conda + name: libflac + version: 1.4.3 + build: h2f0025b_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libflac-1.4.3-h2f0025b_0.conda + sha256: b54935360349d3418b0663d787f20b3cba0b7ce3fcdf3ba5e7ef02b884759049 + md5: 520b12eab32a92e19b1f239ac545ec03 + depends: + - gettext >=0.21.1,<1.0a0 + - libgcc-ng >=12 + - libogg 1.3.* + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 371550 + timestamp: 1687765491794 +- kind: conda + name: libflac + version: 1.4.3 + build: h59595ed_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + sha256: 65908b75fa7003167b8a8f0001e11e58ed5b1ef5e98b96ab2ba66d7c1b822c7d + md5: ee48bf17cc83a00f59ca1494d5646869 + depends: + - gettext >=0.21.1,<1.0a0 + - libgcc-ng >=12 + - libogg 1.3.* + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 394383 + timestamp: 1687765514062 +- kind: conda + name: libflac + version: 1.4.3 + build: hb765f3a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libflac-1.4.3-hb765f3a_0.conda + sha256: 3990b52782fe7207ab642df25368ed443094f6d1a7ea61854935c24192b388aa + md5: 356faba64411660f6c4d24ea31640733 + depends: + - gettext >=0.21.1,<1.0a0 + - libcxx >=15.0.7 + - libogg 1.3.* + - libogg >=1.3.4,<1.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 314408 + timestamp: 1687766236790 +- kind: conda + name: libgcc + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + sha256: 53eb8a79365e58849e7b1a068d31f4f9e718dc938d6f2c03e960345739a03569 + md5: 3cb76c3f10d3bc7f1105b2fc9db984df + depends: + - _libgcc_mutex 0.1 conda_forge + - _openmp_mutex >=4.5 + constrains: + - libgomp 14.2.0 h77fa898_1 + - libgcc-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 848745 + timestamp: 1729027721139 +- kind: conda + name: libgcc + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + sha256: 5d56757ccad208c79214395b00d006d8d18929a4ba49c47bd9460789a7620943 + md5: 511b511c5445e324066c3377481bcab8 + depends: + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==14.2.0=*_1 + - libgomp 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 535243 + timestamp: 1729089435134 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + sha256: 3a76969c80e9af8b6e7a55090088bc41da4cffcde9e2c71b17f44d37b7cb87f7 + md5: e39480b9ca41323497b05492a63bc35b + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54142 + timestamp: 1729027726517 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + sha256: 9b5cf168a6c7361cae869cb74b716766ee7c6d6b3f6172b32ba9bf91135efdc4 + md5: 0694c249c61469f2c0f7e2990782af21 + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54104 + timestamp: 1729089444587 +- kind: conda + name: libgcrypt-lib + version: 1.11.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcrypt-lib-1.11.0-h86ecc28_2.conda + sha256: 7b0b59e11511e1c20e4d1b89ac458b4a0799da2ef10980302a5f033ecc434dee + md5: 07c1e27a75b217e5502bff34cd23c353 + depends: + - libgcc >=13 + - libgpg-error >=1.51,<2.0a0 + license: LGPL-2.1-or-later + size: 635094 + timestamp: 1732523317415 +- kind: conda + name: libgcrypt-lib + version: 1.11.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-lib-1.11.0-hb9d3cd8_2.conda + sha256: ffc3602f9298da248786f46b00d0594d26a18feeb1b07ce88f3d7d61075e39e6 + md5: e55712ff40a054134d51b89afca57dbc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libgpg-error >=1.51,<2.0a0 + license: LGPL-2.1-or-later + size: 586185 + timestamp: 1732523190369 +- kind: conda + name: libgettextpo + version: 0.22.5 + build: h0a1ffab_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgettextpo-0.22.5-h0a1ffab_3.conda + sha256: f816747b63432def4bfe2bfa517057149b2b94a48101fe13e7fcc2c223ec2042 + md5: 263a0b8af4b3fcdb35acc4038bb5bff5 + depends: + - libgcc-ng >=12 + license: GPL-3.0-or-later + license_family: GPL + size: 199824 + timestamp: 1723626215655 +- kind: conda + name: libgettextpo + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.22.5-h8414b35_3.conda + sha256: bc446fad58155e96a01b28e99254415c2151bdddf57f9a2c00c44e6f0298bb62 + md5: c8cd7295cfb7bda5cbabea4fef904349 + depends: + - __osx >=11.0 + - libiconv >=1.17,<2.0a0 + - libintl 0.22.5 h8414b35_3 + license: GPL-3.0-or-later + license_family: GPL + size: 159800 + timestamp: 1723627007035 +- kind: conda + name: libgettextpo + version: 0.22.5 + build: he02047a_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.22.5-he02047a_3.conda + sha256: 7f2d1f4d69973e2c3c3d2b6420d5eb989982baba97d63ab2d7a2b25a92d886b4 + md5: efab66b82ec976930b96d62a976de8e7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: GPL-3.0-or-later + license_family: GPL + size: 170646 + timestamp: 1723626019265 +- kind: conda + name: libgettextpo-devel + version: 0.22.5 + build: h0a1ffab_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgettextpo-devel-0.22.5-h0a1ffab_3.conda + sha256: 677df7af241b36c6b06dff52528c2a8e4f42f8cf40d962e693caa707b563c86c + md5: 5c1498c4da030824d57072f05220aad8 + depends: + - libgcc-ng >=12 + - libgettextpo 0.22.5 h0a1ffab_3 + license: GPL-3.0-or-later + license_family: GPL + size: 36989 + timestamp: 1723626232155 +- kind: conda + name: libgettextpo-devel + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-devel-0.22.5-h8414b35_3.conda + sha256: ea3ca757bf11ed25965b39466b50411c7c2a43f3b90ab4a36fc0ef43f7ab98ac + md5: 7074dc1c9aae1bb5d7bccb4ff03746ca + depends: + - __osx >=11.0 + - libgettextpo 0.22.5 h8414b35_3 + - libiconv >=1.17,<2.0a0 + - libintl 0.22.5 h8414b35_3 + license: GPL-3.0-or-later + license_family: GPL + size: 37153 + timestamp: 1723627048279 +- kind: conda + name: libgettextpo-devel + version: 0.22.5 + build: he02047a_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.22.5-he02047a_3.conda + sha256: 0a66cdd46d1cd5201061252535cd91905b3222328a9294c1a5bcd32e85531545 + md5: 9aba7960731e6b4547b3a52f812ed801 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libgettextpo 0.22.5 he02047a_3 + license: GPL-3.0-or-later + license_family: GPL + size: 36790 + timestamp: 1723626032786 +- kind: conda + name: libgfortran + version: 5.0.0 + build: 13_2_0_hd922786_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf + depends: + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- kind: conda + name: libgfortran + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + sha256: fc9e7f22a17faf74da904ebfc4d88699013d2992e55505e4aa0eb01770290977 + md5: f1fd30127802683586f768875127a987 + depends: + - libgfortran5 14.2.0 hd5240d6_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 53997 + timestamp: 1729027752995 +- kind: conda + name: libgfortran + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + sha256: cb66e411fa32a5c6040f4e5e2a63c00897aae4c3133a9c004c2e929ccf19575b + md5: 0294b92d2f47a240bebb1e3336b495f1 + depends: + - libgfortran5 14.2.0 hb6113d0_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729089471124 +- kind: conda + name: libgfortran5 + version: 13.2.0 + build: hf226fd6_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 5.0.0 13_2_0_*_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 997381 + timestamp: 1707330687590 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hb6113d0_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + sha256: a87ff46d19916403cbf68cf1d785bf56b4d1ab7b2552468d2ea775d70782493f + md5: fc068e11b10e18f184e027782baa12b6 + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1102158 + timestamp: 1729089452640 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hd5240d6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d + md5: 9822b874ea29af082e5d36098d25427d + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1462645 + timestamp: 1729027735353 +- kind: conda + name: libglib + version: 2.82.2 + build: h07bd6cf_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.82.2-h07bd6cf_0.conda + sha256: 101fb31c509d6a69ac5d612b51d4088ddbc675fca18cf0c3589cfee26cd01ca0 + md5: 890783f64502fa6bfcdc723cfbf581b4 + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - libiconv >=1.17,<2.0a0 + - libintl >=0.22.5,<1.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.44,<10.45.0a0 + constrains: + - glib 2.82.2 *_0 + license: LGPL-2.1-or-later + size: 3635416 + timestamp: 1729191799117 +- kind: conda + name: libglib + version: 2.82.2 + build: h2ff4ddf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda + sha256: 49ee9401d483a76423461c50dcd37f91d070efaec7e4dc2828d8cdd2ce694231 + md5: 13e8e54035ddd2b91875ba399f0f7c04 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.44,<10.45.0a0 + constrains: + - glib 2.82.2 *_0 + license: LGPL-2.1-or-later + size: 3931898 + timestamp: 1729191404130 +- kind: conda + name: libglib + version: 2.82.2 + build: hc486b8e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.82.2-hc486b8e_0.conda + sha256: 6797d24de7acd298f81a86078c64e4f3fea6d551a3e8892205c9e72a37a7cc3c + md5: 47f6d85fe47b865e56c539f2ba5f4dad + depends: + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.44,<10.45.0a0 + constrains: + - glib 2.82.2 *_0 + license: LGPL-2.1-or-later + size: 4020802 + timestamp: 1729191545578 +- kind: conda + name: libgomp + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + sha256: 1911c29975ec99b6b906904040c855772ccb265a1c79d5d75c8ceec4ed89cd63 + md5: cc3573974587f12dda90d96e3e55a702 + depends: + - _libgcc_mutex 0.1 conda_forge + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 460992 + timestamp: 1729027639220 +- kind: conda + name: libgomp + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + sha256: 5aa53874a5e57a00f2e0c2e2910684eb674429cd5fcb803619b226a73e89aedf + md5: 376f0e73abbda6d23c0cb749adc195ef + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 463521 + timestamp: 1729089357313 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h3888205_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + sha256: 36af2844ce8fafd477214d51117746144461132f76759a7d29963b4583b577be + md5: a40b948bf4eabcc1ce708c40ffd7c06d + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1248560 + timestamp: 1733512309504 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h804f50b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + sha256: 126856add750013390dff664a3c3cd0f6f0cbbc683b0025a7ce9d1618968bc70 + md5: 3d96df4d6b1c88455e05b94ce8a14a53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1249557 + timestamp: 1733512191906 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h8d8be31_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + sha256: 722e49dbdc4486105d9f5b79a7ba4f9064602fe20c4015e97684c898ab8d3386 + md5: d7ab9e0eb7d55eac4943913073de61d7 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 876210 + timestamp: 1733512539476 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h0121fbd_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + sha256: d1b53d17df38b52a4bc6d1fe6af0e611d6480ce10b0af570c84bd38c8aa83b91 + md5: 877a5ec0431a5af83bf0cd0522bfe661 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h804f50b_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 782108 + timestamp: 1733512329104 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h7081f7f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + sha256: 609df2cf376ba66460f40143f835fc567cae4458df80705587cd2efd59c09bf1 + md5: 28f5ab5cf95170dfacd05d2bb301e573 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=18 + - libgoogle-cloud 2.32.0 h8d8be31_0 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 526895 + timestamp: 1733513644846 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: hb9b2b65_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + sha256: e120e7b6c9c9d25baa8ae903106babdd3c969523ae25278a615ed9de4bd0fc35 + md5: 925ab0ca33baca4fcfee585cecb94169 + depends: + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h3888205_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 737964 + timestamp: 1733512457785 +- kind: conda + name: libgpg-error + version: '1.51' + build: h05609ea_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgpg-error-1.51-h05609ea_1.conda + sha256: e819b3ba47dc7e195e8e8a9c874d0b45690cccb2fa741f1abd55b28323f9fc43 + md5: 9cabbbc1c3c8e9fa30e90748f14534dd + depends: + - libgcc >=13 + - libstdcxx >=13 + license: LGPL-2.1-only + license_family: GPL + size: 277785 + timestamp: 1731920977846 +- kind: conda + name: libgpg-error + version: '1.51' + build: hbd13f7d_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.51-hbd13f7d_1.conda + sha256: 9e0c09c1faf2151ade3ccb64e52d3c1f2dde85c00e37c6a3e6a8bced2aba68be + md5: 168cc19c031482f83b23c4eebbb94e26 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: LGPL-2.1-only + license_family: GPL + size: 268740 + timestamp: 1731920927644 +- kind: conda + name: libgrpc + version: 1.67.1 + build: h36c5df4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + sha256: 1f6673d9d866048c9cf28fd56e6874ffc7e2c53c47d7071cb367d5fc2dde16a7 + md5: b946137e362e98a55a77fdf0b20a7739 + depends: + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7131846 + timestamp: 1730236305327 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc2c308b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + sha256: 870550c1faf524e9a695262cd4c31441b18ad542f16893bd3c5dbc93106705f7 + md5: 4606a4647bfe857e3cfe21ca12ac3afb + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7362336 + timestamp: 1730236333879 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc70892a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + sha256: d2393fcd3c3584e5d58da4122f48bcf297567d2f6f14b3d1fcbd34fdd5040694 + md5: 624e27571fde34f8acc2afec840ac435 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 4882208 + timestamp: 1730236299095 +- kind: conda + name: libiconv + version: '1.17' + build: h0d3ecfb_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 + md5: 69bda57310071cf6d2b86caf11573d2d + license: LGPL-2.1-only + size: 676469 + timestamp: 1702682458114 +- kind: conda + name: libiconv + version: '1.17' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + sha256: a30e09d089cb75a0d5b8e5c354694c1317da98261185ed65aa3793e741060614 + md5: 9a8eb13f14de7d761555a98712e6df65 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705787 + timestamp: 1702684557134 +- kind: conda + name: libiconv + version: '1.17' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 + md5: d66573916ffcf376178462f1b61c941e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705775 + timestamp: 1702682170569 +- kind: conda + name: libintl + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.22.5-h8414b35_3.conda + sha256: 7c1d238d4333af385e594c89ebcb520caad7ed83a735c901099ec0970a87a891 + md5: 3b98ec32e91b3b59ad53dbb9c96dd334 + depends: + - __osx >=11.0 + - libiconv >=1.17,<2.0a0 + license: LGPL-2.1-or-later + size: 81171 + timestamp: 1723626968270 +- kind: conda + name: libintl-devel + version: 0.22.5 + build: h8414b35_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-devel-0.22.5-h8414b35_3.conda + sha256: c9d1d4fdfb5775828e54bc9fb443b1a6de9319a04b81d1bac52c26114a763154 + md5: 271646de11b018c66e81eb4c4717b291 + depends: + - __osx >=11.0 + - libiconv >=1.17,<2.0a0 + - libintl 0.22.5 h8414b35_3 + license: LGPL-2.1-or-later + size: 38584 + timestamp: 1723627022409 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + sha256: 675bc1f2a8581cd34a86c412663ec29c5f90c1d9f8d11866aa1ade5cdbdf8429 + md5: ed24e702928be089d9ba3f05618515c6 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 647126 + timestamp: 1694475003570 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hb547adb_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + sha256: a42054eaa38e84fc1e5ab443facac4bbc9d1b6b6f23f54b7bf4f1eb687e1d993 + md5: 3ff1e053dc3a2b8e36b9bfa4256a58d1 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 547541 + timestamp: 1694475104253 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f + md5: ea25936bb4080d843790b586850f82b8 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 618575 + timestamp: 1694474974816 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + sha256: b76458c36331376911e0f98fa68109e02f4d5e5ebfffa79587ac69cef748bba1 + md5: 3792604c43695d6a273bc5faaac47d48 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16338 + timestamp: 1734432576650 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + sha256: a42bd01498efe2ccf6d08d56ac3cbd3ceab79e06699ff5aac3da8e45a66738f7 + md5: a5d4e18876393633da62fd8492c00156 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16403 + timestamp: 1734432585123 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + sha256: dd6d9a21e672aee4332f019c8229ce70cf5eaf6c2f4cbd1443b105fb66c00dc5 + md5: cebad79038a75cfd28fa90d147a2d34d + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16624 + timestamp: 1734433068120 +- kind: conda + name: liblzma + version: 5.6.3 + build: h39f12f2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 + md5: b2553114a7f5e20ccd02378a77d836aa + depends: + - __osx >=11.0 + license: 0BSD + size: 99129 + timestamp: 1733407496073 +- kind: conda + name: liblzma + version: 5.6.3 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + sha256: d1cce0b7d62d1e54e2164d3e0667ee808efc6c3870256e5b47a150cd0bf46824 + md5: eb08b903681f9f2432c320e8ed626723 + depends: + - libgcc >=13 + license: 0BSD + size: 124138 + timestamp: 1733409137214 +- kind: conda + name: liblzma + version: 5.6.3 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 + md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: 0BSD + size: 111132 + timestamp: 1733407410083 +- kind: conda + name: libmad + version: 0.15.1b + build: h0b41bf4_1001 + build_number: 1001 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libmad-0.15.1b-h0b41bf4_1001.conda + sha256: 9e94cec54c4baadaa652c761179b8d32771fe7fa55faf6c78c2e35f942367f74 + md5: dc5cc4700f02ffeecc48253c9f29025b + depends: + - libgcc-ng >=12 + license: GPL-2.0-only + license_family: GPL + size: 78561 + timestamp: 1670815547616 +- kind: conda + name: libmad + version: 0.15.1b + build: h1a8c8d9_1001 + build_number: 1001 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libmad-0.15.1b-h1a8c8d9_1001.conda + sha256: 8e9209acf4bf6865760e61dc0d86116ffc9efc3e13c7f559eb1ea78532a3a625 + md5: 1eb30852ed396fbe6e301fe6d715aef9 + license: GPL-2.0-only + license_family: GPL + size: 77255 + timestamp: 1670815732700 +- kind: conda + name: libmad + version: 0.15.1b + build: hb4cce97_1001 + build_number: 1001 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libmad-0.15.1b-hb4cce97_1001.conda + sha256: f39c36ce40bc75bc2eb101e8569caf166d6a02e0b1ef95403146b4f9310df0e3 + md5: 10814f7a570b160cea0c885bc1c5e8d5 + depends: + - libgcc-ng >=12 + license: GPL-2.0-only + license_family: GPL + size: 79598 + timestamp: 1673354561940 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h161d5f1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 + md5: 19e57602824042dfd0446292ef90488b + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 647599 + timestamp: 1729571887612 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h6d7220d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 566719 + timestamp: 1729572385640 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: hc8609a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + sha256: c093c6d370aadbf0409c20b6c54c488ee2f6fea976181919fcc63e87ee232673 + md5: f52c614fa214a8bedece9421c771670d + depends: + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 714610 + timestamp: 1729571912479 +- kind: conda + name: libnsl + version: 2.0.1 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 + md5: c14f32510f694e3185704d89967ec422 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 34501 + timestamp: 1697358973269 +- kind: conda + name: libnsl + version: 2.0.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 33408 + timestamp: 1697359010159 +- kind: conda + name: libogg + version: 1.3.5 + build: h0b9eccb_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libogg-1.3.5-h0b9eccb_0.conda + sha256: e65acc318b7535fb8f2b5e994fe6eac3ae0be3bdb2acbe6037841d033c51f290 + md5: 15cb67b1b9dd0d4b37c81daba785e6ad + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 208233 + timestamp: 1719301637185 +- kind: conda + name: libogg + version: 1.3.5 + build: h4ab18f5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-h4ab18f5_0.conda + sha256: 5eda3fe92b99b25dd4737226a9485078ab405672d9f621be75edcb68f1e9026d + md5: 601bfb4b3c6f0b844443bb81a56651e0 + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 205914 + timestamp: 1719301575771 +- kind: conda + name: libogg + version: 1.3.5 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libogg-1.3.5-h99b78c6_0.conda + sha256: 685f73b7241978007dfe0cecb9cae46c6a26d87d192b6f85a09eb65023c0b99e + md5: 57b668b9b78dea2c08e44bb2385d57c0 + depends: + - __osx >=11.0 + license: BSD-3-Clause + license_family: BSD + size: 205451 + timestamp: 1719301708541 +- kind: conda + name: libopenblas + version: 0.3.28 + build: openmp_hf332438_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 + md5: 40803a48d947c8639da6704e9a44d3ce + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=13.2.0 + - llvm-openmp >=18.1.8 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4165774 + timestamp: 1730772154295 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h94d23a6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + sha256: 99ba271d8a80a1af2723f2e124ffd91d850074c0389c067e6d96d72a2dbfeabe + md5: 62857b389e42b36b686331bec0922050 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 5578513 + timestamp: 1730772671118 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h9d3fd7e_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + sha256: 30623a40764e935aa77e0d4db54c1a1589189a9bf3a03fdb445505c1e319b5a6 + md5: e8dde93dd199da3c1f2c1fcfd0042cd4 + depends: + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4793435 + timestamp: 1730773029647 +- kind: conda + name: libopus + version: 1.3.1 + build: h27ca646_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopus-1.3.1-h27ca646_1.tar.bz2 + sha256: e9912101a58cbc609a1917c5289f3bd1f600c82ed3a1c90a6dd4ca02df77958a + md5: 3d0dbee0ccd2f6d6781d270313627b62 + license: BSD-3-Clause + license_family: BSD + size: 252854 + timestamp: 1606823635137 +- kind: conda + name: libopus + version: 1.3.1 + build: h7f98852_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2 + sha256: 0e1c2740ebd1c93226dc5387461bbcf8142c518f2092f3ea7551f77755decc8f + md5: 15345e56d527b330e1cacbdf58676e8f + depends: + - libgcc-ng >=9.3.0 + license: BSD-3-Clause + license_family: BSD + size: 260658 + timestamp: 1606823578035 +- kind: conda + name: libopus + version: 1.3.1 + build: hf897c2e_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopus-1.3.1-hf897c2e_1.tar.bz2 + sha256: 92a87ade11af2cff41c35cf941f1a79390fde1f113f8e51e1cce30d31b7c8305 + md5: ac7534c50934ed25e4749d74b04c667a + depends: + - libgcc-ng >=9.3.0 + license: BSD-3-Clause + license_family: BSD + size: 328825 + timestamp: 1606823775764 +- kind: conda + name: libparquet + version: 18.1.0 + build: h081d1f1_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + sha256: c691a59f1ebb6cedbf827f49f6cf414e08b0eec911f589133e6a8321e8ac701c + md5: 68788df49ce7480187eb6387f15b2b67 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1204535 + timestamp: 1733810811118 +- kind: conda + name: libparquet + version: 18.1.0 + build: h636d7b7_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + sha256: 88c1e810bede65c54f1ebc51c14400f9e8cf0fc1f88a8c0a99210e2f5dfed582 + md5: 9b333c3a38e55f6c1b8733222e22f528 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 873134 + timestamp: 1733809271282 +- kind: conda + name: libparquet + version: 18.1.0 + build: hfc78867_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + sha256: 38aab34c422519c530d0e9a3e0ffd1624db1c1e163983c46ae341e831b2eb6b5 + md5: 1ab6d4a9a982920b9dc5f2c700777b27 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1117592 + timestamp: 1733810440129 +- kind: conda + name: libpng + version: 1.6.44 + build: hadc24fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + sha256: e5b14f7a01c2db4362d8591f42f82f336ed48d5e4079e4d1f65d0c2a3637ea78 + md5: f4cc49d7aa68316213e4b12be35308d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 290661 + timestamp: 1726234747153 +- kind: conda + name: libpng + version: 1.6.44 + build: hc14010f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + sha256: 38f8759a3eb8060deabd4db41f0f023514d853e46ddcbd0ba21768fc4e563bb1 + md5: fb36e93f0ea6a6f5d2b99984f34b049e + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 263385 + timestamp: 1726234714421 +- kind: conda + name: libpng + version: 1.6.44 + build: hc4a20ef_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + sha256: 23b5ce15cf9c6017641a8396bab00ae807dd9f662718cfa7f61de114d0c97647 + md5: 5d25802b25fcc7419fa13e21affaeb3a + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 294907 + timestamp: 1726236639270 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h029595c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + sha256: d8c7b6f851bfc53494d9b8e54d473c4f11ab26483a6e64df6f7967563df166b1 + md5: 538dbe0ad9f248e2e109abb9b6809ea5 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2802876 + timestamp: 1728564881988 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h5b01275_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + sha256: 5e8fd4aa00193c85602ce6101dd28fe31306dff85c9725048f6dc828dfa7c421 + md5: ab0bff36363bec94720275a681af8b83 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2945348 + timestamp: 1728565355702 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h8f0b736_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + sha256: f732a6fa918428e2d5ba61e78fe11bb44a002cc8f6bb74c94ee5b1297fefcfd8 + md5: d2cb5991f2fb8eb079c80084435e9ce6 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2374965 + timestamp: 1728565334796 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h18dbdb1_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + sha256: 96d4fdac28d5af38c38f90c22cb0aa9a90affae13ca8ba24bd1eb60b789df8ff + md5: f1800796b0efc4bbc5b001d845545111 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 203516 + timestamp: 1728778974654 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h2348fd5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + sha256: 6facca42cfc85a05b33e484a8b0df7857cc092db34806946d022270098d8d20f + md5: 5a7065309a66097738be6a06fd04b7ef + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 165956 + timestamp: 1728779107218 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: hbbce691_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + sha256: f8ad6a4f6d4fd54ebe3e5e712a01e663222fc57f49d16b6b8b10c30990dafb8f + md5: 2124de47357b7a516c0a3efd8f88c143 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 211096 + timestamp: 1728778964655 +- kind: conda + name: libsndfile + version: 1.2.2 + build: h79657aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsndfile-1.2.2-h79657aa_1.conda + sha256: 8fcd5e45d6fb071e8baf492ebb8710203fd5eedf0cb791e007265db373c89942 + md5: ad8e62c0faec46b1442f960489c80b49 + depends: + - lame >=3.100,<3.101.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + size: 396501 + timestamp: 1695747749825 +- kind: conda + name: libsndfile + version: 1.2.2 + build: h9739721_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsndfile-1.2.2-h9739721_1.conda + sha256: e559f2f72bb03a554aa5b74230fa19160d33c7981ed385294f1eea9a5871cc03 + md5: 77d552455cbc52e089cdb9df5b283199 + depends: + - lame >=3.100,<3.101.0a0 + - libcxx >=15.0.7 + - libflac >=1.4.3,<1.5.0a0 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + size: 317185 + timestamp: 1695747981394 +- kind: conda + name: libsndfile + version: 1.2.2 + build: hc60ed4a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + sha256: f709cbede3d4f3aee4e2f8d60bd9e256057f410bd60b8964cb8cf82ec1457573 + md5: ef1910918dd895516a769ed36b5b3a4e + depends: + - lame >=3.100,<3.101.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + size: 354372 + timestamp: 1695747735668 +- kind: conda + name: libsodium + version: 1.0.20 + build: h4ab18f5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + size: 205978 + timestamp: 1716828628198 +- kind: conda + name: libsodium + version: 1.0.20 + build: h68df207_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + sha256: 448df5ea3c5cf1af785aad46858d7a5be0522f4234a4dc9bb764f4d11ff3b981 + md5: 2e4a8f23bebdcb85ca8e5a0fbe75666a + depends: + - libgcc-ng >=12 + license: ISC + size: 177394 + timestamp: 1716828514515 +- kind: conda + name: libsodium + version: 1.0.20 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h3f77e49_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + sha256: f192f3c8973de9ec4c214990715f13b781965247a5cedf9162e7f9e699cfc3c4 + md5: 122d6f29470f1a991e85608e77e56a8a + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 850553 + timestamp: 1733762057506 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h5eb1b54_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + sha256: 885a27fa84a5a73ed9779168c02b6c386e2fc7a53f0566b32a09ceca146b42b4 + md5: d4bf59f8783a4a66c0aec568f6de3ff4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 1042182 + timestamp: 1733761913736 +- kind: conda + name: libsqlite + version: 3.47.2 + build: hee588c1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + sha256: 48af21ebc2cbf358976f1e0f4a0ab9e91dfc83d0ef337cf3837c6f5bc22fb352 + md5: b58da17db24b6e08bcbf8fed2fb8c915 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 873551 + timestamp: 1733761824646 +- kind: conda + name: libssh2 + version: 1.11.1 + build: h9cc3647_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 + md5: ddc7194676c285513706e5fc64f214d7 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279028 + timestamp: 1732349599461 +- kind: conda + name: libssh2 + version: 1.11.1 + build: ha41c0db_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + sha256: 40f2af5357457546bd11cd64a3b9043d83865180f65ce602515c35f353be35c7 + md5: aeffe03c0e598f015aab08dbb04f6ee4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 311577 + timestamp: 1732349396421 +- kind: conda + name: libssh2 + version: 1.11.1 + build: hf672d98_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 + md5: be2de152d8073ef1c01b7728475f2fe7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 304278 + timestamp: 1732349402869 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: h3f4de04_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 + md5: 37f489acd39e22b623d2d1e5ac6d195c + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3816794 + timestamp: 1729089463404 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: hc0a3c3a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + sha256: 4661af0eb9bdcbb5fb33e5d0023b001ad4be828fccdcc56500059d56f9869462 + md5: 234a5554c53625688d51062645337328 + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3893695 + timestamp: 1729027746910 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: h4852527_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + sha256: 25bb30b827d4f6d6f0522cc0579e431695503822f144043b93c50237017fffd8 + md5: 8371ac6457591af2cf6159439c1fd051 + depends: + - libstdcxx 14.2.0 hc0a3c3a_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729027780628 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: hf1166c9_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + sha256: 9f97461bd55a2745a7a0941f3502a047f15bfe7bb2952dc7fb204b3202f866fd + md5: 0e75771b8a03afae5a2c6ce71bc733f5 + depends: + - libstdcxx 14.2.0 h3f4de04_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54133 + timestamp: 1729089498541 +- kind: conda + name: libsystemd0 + version: '256.9' + build: h0b6a36f_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.9-h0b6a36f_2.conda + sha256: 28e1a3c4bd242e7eb3bd0bcd35e558680d186e7a1d61482d371dde2a0f1bfb42 + md5: 135bbeb376345b6847c065115be4221a + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.71,<2.72.0a0 + - libgcc >=13 + - libgcrypt-lib >=1.11.0,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: LGPL-2.1-or-later + size: 410566 + timestamp: 1733679350245 +- kind: conda + name: libsystemd0 + version: '256.9' + build: ha536d29_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsystemd0-256.9-ha536d29_2.conda + sha256: 2cf3b22760674612fa13ad9748164dffc993d58dd3fc05c343b646ce31375af7 + md5: 5ef220ea7aa46bd59f83ac7afe14910b + depends: + - libcap >=2.71,<2.72.0a0 + - libgcc >=13 + - libgcrypt-lib >=1.11.0,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: LGPL-2.1-or-later + size: 432708 + timestamp: 1733679350109 +- kind: conda + name: libthrift + version: 0.21.0 + build: h0e7cc3e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + sha256: ebb395232973c18745b86c9a399a4725b2c39293c9a91b8e59251be013db42f0 + md5: dcb95c0a98ba9ff737f7ae482aef7833 + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 425773 + timestamp: 1727205853307 +- kind: conda + name: libthrift + version: 0.21.0 + build: h154c74f_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + sha256: f04ab1417aca1687edff9c30d8423ace285eb8c053dc16d595c6e47cfeefb274 + md5: c28792bf37f4ecdce8e3cb9e40750650 + depends: + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 417329 + timestamp: 1727205944238 +- kind: conda + name: libthrift + version: 0.21.0 + build: h64651cc_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + sha256: 7a6c7d5f58cbbc2ccd6493b4b821639fdb0701b9b04c737a949e8cb6adf1c9ad + md5: 7ce2bd2f650f8c31ad7ba4c7bfea61b7 + depends: + - __osx >=11.0 + - libcxx >=17 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 324342 + timestamp: 1727206096912 +- kind: conda + name: libtiff + version: 4.7.0 + build: h551f018_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae + md5: a5d084a957563e614ec0c0196d890654 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=18 + - libdeflate >=1.23,<1.24.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 370600 + timestamp: 1734398863052 +- kind: conda + name: libtiff + version: 4.7.0 + build: h88f7998_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + sha256: 5888bd66ba7606ae8596856c7dac800940ecad0aed77d6aa37db69d434c81cf0 + md5: 36a0ea4a173338c8725dc0807e99cf22 + depends: + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 464699 + timestamp: 1734398752249 +- kind: conda + name: libtiff + version: 4.7.0 + build: hd9ff511_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 428173 + timestamp: 1734398813264 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h5505292_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + sha256: ea88f06e97ef8fa2490f7594f8885bb542577226edf8abba3144302d951a53c2 + md5: f777470d31c78cd0abe1903a2fda436f + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 83000 + timestamp: 1732868631531 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + sha256: 37a1833c55f9945724cd4b3eb6a1469032cc754a1dd725f191c34154ad2ba7e4 + md5: 699f155da290be3a1a64c932c6728991 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 81526 + timestamp: 1732868466862 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + sha256: 9794e6388e780c3310d46f773bbc924d4053375c3fcdb07a704b57f4616db928 + md5: 1e936bd23d737aac62a18e9a1e7f8b18 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 81500 + timestamp: 1732868419835 +- kind: conda + name: libuuid + version: 2.38.1 + build: h0b41bf4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 33601 + timestamp: 1680112270483 +- kind: conda + name: libuuid + version: 2.38.1 + build: hb4cce97_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + sha256: 616277b0c5f7616c2cdf36f6c316ea3f9aa5bb35f2d4476a349ab58b9b91675f + md5: 000e30b09db0b7c775b21695dff30969 + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 35720 + timestamp: 1680113474501 +- kind: conda + name: libuv + version: 1.49.2 + build: h7ab814d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + sha256: 0e5176af1e788ad5006cf261c4ea5a288a935fda48993b0240ddd2e562dc3d02 + md5: 4bc348e3a1a74d20a3f9beb866d75e0a + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 410500 + timestamp: 1729322654121 +- kind: conda + name: libuv + version: 1.49.2 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + sha256: adf4eca89339ac7780f2394e7e6699be81259eb91f79f9d9fdf2c1bc6b26f210 + md5: 1899e1ec2be63386c41c4db31d3056af + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 627484 + timestamp: 1729322575379 +- kind: conda + name: libuv + version: 1.49.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + sha256: a35cd81cd1a9add11024097da83cc06b0aae83186fe4124b77710876f37d8f31 + md5: 070e3c9ddab77e38799d5c30b109c633 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 884647 + timestamp: 1729322566955 +- kind: conda + name: libvorbis + version: 1.3.7 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libvorbis-1.3.7-h01db608_0.tar.bz2 + sha256: 1ade4727be5c52b287001b8094d02af66342dfe0ba13ef69222aaaf2e9be4342 + md5: c2863ff72c6d8a59054f8b9102c206e9 + depends: + - libgcc-ng >=9.3.0 + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=9.3.0 + license: BSD-3-Clause + license_family: BSD + size: 292082 + timestamp: 1610616294416 +- kind: conda + name: libvorbis + version: 1.3.7 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2 + sha256: 53080d72388a57b3c31ad5805c93a7328e46ff22fab7c44ad2a86d712740af33 + md5: 309dec04b70a3cc0f1e84a4013683bc0 + depends: + - libgcc-ng >=9.3.0 + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=9.3.0 + license: BSD-3-Clause + license_family: BSD + size: 286280 + timestamp: 1610609811627 +- kind: conda + name: libvorbis + version: 1.3.7 + build: h9f76cd9_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libvorbis-1.3.7-h9f76cd9_0.tar.bz2 + sha256: 60457217e20d8b24a8390c81338a8fa69c8656b440c067cd82f802a09da93cb9 + md5: 92a1a88d1a1d468c19d9e1659ac8d3df + depends: + - libcxx >=11.0.0 + - libogg >=1.3.4,<1.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 254839 + timestamp: 1610609991029 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + sha256: 10dded60f274e29c573cfacf6e96f5d0fc374ee431250374a44cbd773916ab9d + md5: 5fd7ab3e5f382c70607fbac6335e6e19 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 363577 + timestamp: 1713201785160 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h93a5062_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + sha256: 0d4bad713a512d79bfeb4d61821f447afab8b0792aca823f505ce6b195e9fde5 + md5: c0af0edfebe780b19940e94871f1a765 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 287750 + timestamp: 1713200194013 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + sha256: 49bc5f6b1e11cb2babf2a2a731d1a680a5e08a858280876a779dbda06c78c35f + md5: b26e8aa824079e1be0294e7152ca4559 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 438953 + timestamp: 1713199854503 +- kind: conda + name: libxcb + version: 1.17.0 + build: h262b8f6_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b + md5: cd14ee5cca2464a425b1dbfc24d90db2 + depends: + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 397493 + timestamp: 1727280745441 +- kind: conda + name: libxcb + version: 1.17.0 + build: h8a09558_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 395888 + timestamp: 1727278577118 +- kind: conda + name: libxcb + version: 1.17.0 + build: hdb1d25a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 323658 + timestamp: 1727278733917 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f + md5: b4df5d7d4b63579d081fd3a4cf99740e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 114269 + timestamp: 1702724369203 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 100393 + timestamp: 1702724383534 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h178c5d8_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 + md5: 3dc3cff0eca1640a6acbbfab2f78139e + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 582898 + timestamp: 1733443841584 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h2e0c361_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 + md5: 63410f85031930cde371dfe0ee89109a + depends: + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 732155 + timestamp: 1733443825814 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h8d12d68_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h8d12d68_1.conda + sha256: c3b05bdc40d27a9249f0bb60f3f71718f94104b8bcd200163a6c9d4ade7aa052 + md5: 1a21e49e190d1ffe58531a81b6e400e1 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 690589 + timestamp: 1733443667823 +- kind: conda + name: libzlib + version: 1.3.1 + build: h8359307_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 46438 + timestamp: 1727963202283 +- kind: conda + name: libzlib + version: 1.3.1 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 + md5: 08aad7cbe9f5a6b460d0976076b6ae64 + depends: + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 66657 + timestamp: 1727963199518 +- kind: conda + name: libzlib + version: 1.3.1 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 60963 + timestamp: 1727963148474 +- kind: conda + name: llvm-openmp + version: 19.1.5 + build: hdb05f8b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + sha256: e7ba0d8b718925efdcf1309f5e776e3264cc172d3af8d4048b39627c50a1abc0 + md5: f2c2e187a1d2637d282e34dc92021a70 + depends: + - __osx >=11.0 + constrains: + - openmp 19.1.5|19.1.5.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 281120 + timestamp: 1733376089600 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h286801f_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 167055 + timestamp: 1733741040117 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + sha256: 67e55058d275beea76c1882399640c37b5be8be4eb39354c94b610928e9a0573 + md5: 6654e411da94011e8fbe004eacb8fe11 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 184953 + timestamp: 1733740984533 +- kind: conda + name: markdown-it-py + version: 3.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a + md5: fee3164ac23dfca50cfcc8b85ddefb81 + depends: + - mdurl >=0.1,<1 + - python >=3.9 + license: MIT + license_family: MIT + size: 64430 + timestamp: 1733250550053 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h178313f_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + sha256: 4a6bf68d2a2b669fecc9a4a009abd1cf8e72c2289522ff00d81b5a6e51ae78f5 + md5: eb227c3e0bf58f5bd69c0532b157975b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24604 + timestamp: 1733219911494 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h74ce7d3_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + sha256: 1d500158262f30b9c23e37d1c861fe76e127a3926d69b3b38c25d20d3faa6f9f + md5: bc8607ab678073a0441808a31465f4fb + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 25079 + timestamp: 1733220639175 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h998013c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + sha256: 4aa997b244014d3707eeef54ab0ee497d12c0d0d184018960cce096169758283 + md5: 46e547061080fddf9cf95a0327e8aba6 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24048 + timestamp: 1733219945697 +- kind: conda + name: max + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + sha256: 0e3c1984ac7476550fd8fa5921bf1ca58950219b84ae21ecd861f650e45382ac + md5: e04b1405f630c9bb7d4cb5559840e902 + depends: + - max-core ==24.6.0 release + - max-python >=24.6.0,<25.0a0 + - mojo-jupyter ==24.6.0 release + - mblack ==24.6.0 release + license: LicenseRef-Modular-Proprietary + size: 9851 + timestamp: 1734039439696 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + sha256: 38a4128c15b230f5b05e0606a339c7866a83eb943d334a948b3a8c1d2675a917 + md5: 25e678ff7c59e36ec3154fe0cd15ebde + depends: + - mblack ==24.6.0 release + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 247670119 + timestamp: 1734039439695 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + sha256: 554f2c1a6ddfab8fabf82b9fbcc1adb87e2a615669793e463f757ea452e02016 + md5: 2ca790aa461fa28722c6f21fcd2af0b9 + depends: + - mblack ==24.6.0 release + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 251486610 + timestamp: 1734039413769 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + sha256: 434c29e35067e296db55525cd5cf38bb013a1f7a7bfa99845bf6c317de6cdc12 + md5: 4a2ead0a9010c36b6193ea32f583e996 + depends: + - mblack ==24.6.0 release + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 212001240 + timestamp: 1734039726703 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + sha256: 6fbf7330ad910e6ec9fd581fd0f8505e5b1326ccf9979d553c70c61abf4c3e54 + md5: 218ecd662f853ea1578404799d61b385 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 123785050 + timestamp: 1734039439704 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + sha256: 15f57cb436b00c510473ca1940edd9ee03df6c92d5b66d616db30a131b768b78 + md5: 97c62f17f0d34787d29128043a593877 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 127403269 + timestamp: 1734039413779 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + sha256: c888b58cfc7c767d40aa100ff2bccf5c3ab11d58d897a6accb749e6b5b7014ea + md5: 62a92bfab3b5c85c2d246672bbb8bc8d + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 112484803 + timestamp: 1734039726707 +- kind: conda + name: mblack + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + sha256: f135164020478078f4681aa77e7f6ca9f68b8e7ee02604b85342bbaf2f706f0d + md5: 77367aff981ba391ab5c047ba33ec978 + depends: + - python >=3.9,<3.13 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - python + license: MIT + size: 130668 + timestamp: 1734039439700 +- kind: conda + name: mdurl + version: 0.1.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- kind: conda + name: mojo-jupyter + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + sha256: 2fe043d98ea77f8f165b39bd252cd04942216c8533f0291c49d87d6cfd8673df + md5: b17127f3ca2cef0976496407e1cd4081 + depends: + - max-core ==24.6.0 release + - python >=3.9,<3.13 + - jupyter_client >=8.6.2,<8.7 + - python + license: LicenseRef-Modular-Proprietary + size: 22990 + timestamp: 1734039439702 +- kind: conda + name: mpg123 + version: 1.32.9 + build: h65af167_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/mpg123-1.32.9-h65af167_0.conda + sha256: d65d5a00278544639ba4f99887154be00a1f57afb0b34d80b08e5cba40a17072 + md5: cdf140c7690ab0132106d3bc48bce47d + depends: + - libgcc >=13 + - libstdcxx >=13 + license: LGPL-2.1-only + license_family: LGPL + size: 558708 + timestamp: 1730581372400 +- kind: conda + name: mpg123 + version: 1.32.9 + build: hc50e24c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + sha256: 39c4700fb3fbe403a77d8cc27352fa72ba744db487559d5d44bf8411bb4ea200 + md5: c7f302fd11eeb0987a6a5e1f3aed6a21 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: LGPL-2.1-only + license_family: LGPL + size: 491140 + timestamp: 1730581373280 +- kind: conda + name: mpg123 + version: 1.32.9 + build: hf642e45_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/mpg123-1.32.9-hf642e45_0.conda + sha256: 070bbbbb96856c325c0b6637638ce535afdc49adbaff306e2238c6032d28dddf + md5: d2b4857bdc3b76c36e23236172d09840 + depends: + - __osx >=11.0 + - libcxx >=18 + license: LGPL-2.1-only + license_family: LGPL + size: 360712 + timestamp: 1730581491116 +- kind: conda + name: multidict + version: 6.1.0 + build: py312h178313f_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + sha256: b05bc8252a6e957bf4a776ed5e0e61d1ba88cdc46ccb55890c72cc58b10371f4 + md5: 5b5e3267d915a107eca793d52e1b780a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 61507 + timestamp: 1733913288935 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hcc812fe_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + sha256: ff9f767ba4df68e9ac2a380529a83a2fb6abd985beee9eab16608f7e2c3ccc6e + md5: dcf3ae213cf0ab40ebcc10452e1ed9fa + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 63077 + timestamp: 1733913233032 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hdb8e49c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + sha256: 482fd09fb798090dc8cce2285fa69f43b1459099122eac2fb112d9b922b9f916 + md5: 0048335516fed938e4dd2c457b4c5b9b + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 55968 + timestamp: 1729065664275 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h02f2b3b_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + sha256: 8041371e3ec3fbc2ca13c71b0180672896e6382e62892d9f6b11a4c5dd675951 + md5: 910ef2223c71902175418d9163152788 + depends: + - dill >=0.3.6 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335147 + timestamp: 1695459275360 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h98912ed_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + sha256: bb612a921fafda6375a2204ffebd8811db8dd3b8f25ac9886cc9bcbff7e3664e + md5: 5a64b9f44790d9a187a85366dd0ffa8d + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335666 + timestamp: 1695459025249 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312hdd3e373_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + sha256: c53362cdf346f314e111faddc53061e3fd2ece0ba68ca303f5dd109976df158f + md5: 173a1692d2b3ddc265dc6afd21a869b3 + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 336110 + timestamp: 1695459137796 +- kind: conda + name: mypy_extensions + version: 1.0.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + sha256: 1895f47b7d68581a6facde5cb13ab8c2764c2e53a76bd746f8f98910dc4e08fe + md5: 29097e7ea634a45cc5386b95cac6568f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 10854 + timestamp: 1733230986902 +- kind: conda + name: ncurses + version: '6.5' + build: h7bae524_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc + md5: cb2b0ea909b97b3d70cd3921d1445e1a + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 802321 + timestamp: 1724658775723 +- kind: conda + name: ncurses + version: '6.5' + build: hcccb83c_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + sha256: acad4cf1f57b12ee1e42995e6fac646fa06aa026529f05eb8c07eb0a84a47a84 + md5: 91d49c85cacd92caa40cf375ef72a25d + depends: + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 924472 + timestamp: 1724658573518 +- kind: conda + name: ncurses + version: '6.5' + build: he02047a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a + md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 889086 + timestamp: 1724658547447 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h470d778_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + sha256: 23767677a7790bee5457d5e75ebd508b9a31c5354216f4310dd1acfca3f7a6f9 + md5: 9cebf5a06cb87d4569cd68df887af476 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6614296 + timestamp: 1707225994762 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h8442bc7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6073136 + timestamp: 1707226249608 +- kind: conda + name: numpy + version: 1.26.4 + build: py312heda63a1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 + md5: d8285bea2a350f63fab23bf460221f3f + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 7484186 + timestamp: 1707225809722 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h3f56577_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d + md5: 04231368e4af50d11184b50e14250993 + depends: + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 377796 + timestamp: 1733816683252 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h5fbd93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 + md5: 9e5816bc95d285c115a3ebc2f8563564 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 342988 + timestamp: 1733816638720 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h8a3d83b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + sha256: 1d59bc72ca7faac06d349c1a280f5cfb8a57ee5896f1e24225a997189d7418c7 + md5: 4b71d78648dbcf68ce8bf22bb07ff838 + depends: + - __osx >=11.0 + - libcxx >=18 + - libpng >=1.6.44,<1.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 319362 + timestamp: 1733816781741 +- kind: conda + name: openssl + version: 3.4.0 + build: h39f12f2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + sha256: bd1d58ced46e75efa3b842c61642fd12272c69e9fe4d7261078bc082153a1d53 + md5: df307bbc703324722df0293c9ca2e418 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2935176 + timestamp: 1731377561525 +- kind: conda + name: openssl + version: 3.4.0 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + sha256: 64dbbdd6384fa56338124783197f7ad9048c989a02264bcd2e07355e3570f113 + md5: b2f202b5bddafac824eb610b65dde98f + depends: + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 3474825 + timestamp: 1731379200886 +- kind: conda + name: openssl + version: 3.4.0 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + sha256: 814b9dff1847b132c676ee6cc1a8cb2d427320779b93e1b6d76552275c128705 + md5: 23cc74f77eb99315c0360ec3533147a9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 2947466 + timestamp: 1731377666602 +- kind: conda + name: opentelemetry-api + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + sha256: 296280c8ace35c0a1cf72bed1077f248b3af903c3bf92332f1783a207cb5abdb + md5: 307b05402c1a382f2f09426492dee8f8 + depends: + - deprecated >=1.2.6 + - importlib-metadata >=6.0,<=8.5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 44166 + timestamp: 1734132973331 +- kind: conda + name: opentelemetry-exporter-otlp-proto-common + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + sha256: ae9776efe52564e0d6711cfcee7c54439273e57a3999f7f796f66e862f58aae9 + md5: 0c02e74d26bce3fec93b227cf7ea6e6b + depends: + - backoff >=1.10.0,<3.0.0 + - opentelemetry-proto 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 18922 + timestamp: 1734310457116 +- kind: conda + name: opentelemetry-exporter-otlp-proto-http + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + sha256: 5d61db9d5b4f91b3932f5f2348920d5b7fdaa09e52c8ea054cf7bf3f21677c9c + md5: 223f4e56a29601c887f0dc467034af5b + depends: + - deprecated >=1.2.6 + - googleapis-common-protos >=1.52,<2.dev0 + - opentelemetry-api >=1.15,<2.dev0 + - opentelemetry-exporter-otlp-proto-common 1.29.0 + - opentelemetry-proto 1.29.0 + - opentelemetry-sdk 1.29.0 + - python >=3.9 + - requests >=2.7,<3.dev0 + license: Apache-2.0 + size: 17147 + timestamp: 1734345675510 +- kind: conda + name: opentelemetry-exporter-prometheus + version: 1.12.0rc1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + sha256: b8239230dbbdb491401e41b53bd9f21d60551cedef1a8d5807fca1bf9bdd331c + md5: 1ddc95052b31147d1e10d818cf519cf5 + depends: + - opentelemetry-api >=1.10.0 + - opentelemetry-sdk >=1.10.0 + - prometheus_client >=0.5.0,<1.0.0 + - python >=3.6 + license: Apache-2.0 + license_family: APACHE + size: 14721 + timestamp: 1695214221489 +- kind: conda + name: opentelemetry-proto + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + sha256: 200a7cb8acc8a0ddd6ef55c5460cec871b6a265929b240a0296c0ccb9c8d9758 + md5: e2a6d2ad10b813c7fdc1c64aac376128 + depends: + - protobuf <6.0,>=5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 37235 + timestamp: 1734291034372 +- kind: conda + name: opentelemetry-sdk + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + sha256: 7b36629d8b8be8a019fcfd1518d7b7f862dd25de96f8adcadb93e4fd12cf9bd6 + md5: 2a8893f06e6ebda4bfa78875bc923ea4 + depends: + - opentelemetry-api 1.29.0 + - opentelemetry-semantic-conventions 0.50b0 + - python >=3.9 + - typing-extensions >=3.7.4 + - typing_extensions >=3.7.4 + license: Apache-2.0 + license_family: APACHE + size: 77645 + timestamp: 1734297838999 +- kind: conda + name: opentelemetry-semantic-conventions + version: 0.50b0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + sha256: 6526e70368d5bf66ef0eaa51fb800d53782dde71a24bd38f40139919a6f784dc + md5: f7111fa4188d646c8108e232d024cb99 + depends: + - deprecated >=1.2.6 + - opentelemetry-api 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 86084 + timestamp: 1734208980168 +- kind: conda + name: opusfile + version: '0.12' + build: h3358134_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/opusfile-0.12-h3358134_2.conda + sha256: f4df9df880e405e5c856383f869d5b9d434f78fb7c234c9e7b099ab604fb7fc3 + md5: 5931bcae00b98f952696b6bcdd0be34b + depends: + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - openssl >=3.0.7,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 65901 + timestamp: 1670387479735 +- kind: conda + name: opusfile + version: '0.12' + build: h5643135_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/opusfile-0.12-h5643135_2.conda + sha256: 108dbee936a8e3c21d2aa5618326343844df8f1fe14067c4dc5a731d7945ecc0 + md5: e34e472ae04beeb642c5e937a2aeeebf + depends: + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - openssl >=3.0.7,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 80128 + timestamp: 1670387790769 +- kind: conda + name: opusfile + version: '0.12' + build: hf55b2d5_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/opusfile-0.12-hf55b2d5_2.conda + sha256: a0ffa8054df68fad5f3533338557c7b985480ee3cf39f0e251ee6b03ff6896cf + md5: a9a71d77aec174e4532f91f560bc413b + depends: + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - openssl >=3.0.7,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 91662 + timestamp: 1673436651852 +- kind: conda + name: orc + version: 2.0.3 + build: h3c55218_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + sha256: 154b26bc4d586de33765a155c9b79ebd7f5bb36c2bbf4b8854e1631bca8d21af + md5: 0a51a3cf028b845c46ec0d1ea2d18629 + depends: + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1165179 + timestamp: 1733509923825 +- kind: conda + name: orc + version: 2.0.3 + build: h97ab989_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + sha256: 9de7e2746fde57c9b7f08ee87142014f6bb9b2d3a506839ea3e98baa99711576 + md5: 2f46eae652623114e112df13fae311cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1189462 + timestamp: 1733509801323 +- kind: conda + name: orc + version: 2.0.3 + build: hbcee414_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + sha256: e5e72438a3cd967ebc774070e8c49500d2d6d4175f349400b327fee75d3bfc05 + md5: e808cf7819eaa1735c8790d7f9f482c7 + depends: + - __osx >=11.0 + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 437391 + timestamp: 1733510118673 +- kind: conda + name: packaging + version: '24.2' + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 + md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + size: 60164 + timestamp: 1733203368787 +- kind: conda + name: pandas + version: 2.2.3 + build: py312ha2895bd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + sha256: 585e05f95d14afe3df43ded14f86800c70da26b27e27b59de95932f8888af5d3 + md5: 80b873ac4fdf36641afa0eaafff3a664 + depends: + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15159625 + timestamp: 1726879151211 +- kind: conda + name: pandas + version: 2.2.3 + build: py312hcd31e36_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + sha256: ff0cb54b5d058c7987b4a0984066e893642d1865a7bb695294b6172e2fcdc457 + md5: c68bfa69e6086c381c74e16fd72613a8 + depends: + - __osx >=11.0 + - libcxx >=17 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 14470437 + timestamp: 1726878887799 +- kind: conda + name: pandas + version: 2.2.3 + build: py312hf9745cd_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + sha256: ad275a83bfebfa8a8fee9b0569aaf6f513ada6a246b2f5d5b85903d8ca61887e + md5: 8bce4f6caaf8c5448c7ac86d87e26b4b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15436913 + timestamp: 1726879054912 +- kind: conda + name: pathspec + version: 0.12.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee + md5: 617f15191456cc6a13db418a275435e5 + depends: + - python >=3.9 + license: MPL-2.0 + license_family: MOZILLA + size: 41075 + timestamp: 1733233471940 +- kind: conda + name: pcre2 + version: '10.44' + build: h070dd5b_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.44-h070dd5b_2.conda + sha256: e9f4b912e48514771d477f2ee955f59d4ff4ef799c3d4d16e4d0f335ce91df67 + md5: 94022de9682cb1a0bb18a99cbc3541b3 + depends: + - bzip2 >=1.0.8,<2.0a0 + - libgcc-ng >=12 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 884590 + timestamp: 1723488793100 +- kind: conda + name: pcre2 + version: '10.44' + build: h297a79d_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-h297a79d_2.conda + sha256: 83153c7d8fd99cab33c92ce820aa7bfed0f1c94fc57010cf227b6e3c50cb7796 + md5: 147c83e5e44780c7492998acbacddf52 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 618973 + timestamp: 1723488853807 +- kind: conda + name: pcre2 + version: '10.44' + build: hba22ea6_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda + sha256: 1087716b399dab91cc9511d6499036ccdc53eb29a288bebcb19cf465c51d7c0d + md5: df359c09c41cd186fffb93a2d87aa6f5 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc-ng >=12 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 952308 + timestamp: 1723488734144 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h5ab5af3_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + sha256: 3cf43a5eb1f67f3a5f3ef1ec3a685f8767019cce24dbe46c4b76fee8a54fbacf + md5: 1c4bdfe659cfdedd372685ce2494e97b + depends: + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41756471 + timestamp: 1729068045876 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h7b63e92_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + sha256: 13a464bea02c0df0199c20ef6bad24a6bc336aaf55bf8d6a133d0fe664463224 + md5: 385f46a4df6f97892503a841121a9acf + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41948418 + timestamp: 1729065846594 +- kind: conda + name: pillow + version: 11.0.0 + build: py312haf37ca6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + sha256: 727b4c3faecdb6f6809cf20c5f32d2df4af34e0d5b9146b7588383bcba7990e8 + md5: dc9b51fbd2b6f7fea9b5123458864dbb + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41737424 + timestamp: 1729065920347 +- kind: conda + name: pixman + version: 0.44.2 + build: h29eaf8c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.44.2-h29eaf8c_0.conda + sha256: 747c58db800d5583fee78e76240bf89cbaeedf7ab1ef339c2990602332b9c4be + md5: 5e2a7acfa2c24188af39e7944e1b3604 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 381072 + timestamp: 1733698987122 +- kind: conda + name: pixman + version: 0.44.2 + build: h2f9eb0b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.44.2-h2f9eb0b_0.conda + sha256: 28855d4cb2d9fc9a6bd9196dadbaecd6868ec706394cec2f88824a61ba4b1bc0 + md5: fa8e429fdb9e5b757281f69b8cc4330b + depends: + - __osx >=11.0 + - libcxx >=18 + license: MIT + license_family: MIT + size: 201076 + timestamp: 1733699127167 +- kind: conda + name: pixman + version: 0.44.2 + build: h86a87f0_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.44.2-h86a87f0_0.conda + sha256: 289c88d26530e427234adf7a8eb11e762d2beaf3c0a337c1c9887f60480e33e1 + md5: 95689fc369832398e82d17c56ff5df8a + depends: + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 288697 + timestamp: 1733700860569 +- kind: conda + name: platformdirs + version: 4.3.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + sha256: bb50f6499e8bc1d1a26f17716c97984671121608dc0c3ecd34858112bce59a27 + md5: 577852c7e53901ddccc7e6a9959ddebe + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 20448 + timestamp: 1733232756001 +- kind: conda + name: portaudio + version: 19.6.0 + build: h13dd4ca_9 + build_number: 9 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/portaudio-19.6.0-h13dd4ca_9.conda + sha256: 5ff2b55d685c29dfe632ef856796a4b862305088543d4982f0b807e8d9bb756e + md5: d325d46394b6c46d15718c855fb20b4a + depends: + - libcxx >=15.0.7 + license: MIT + license_family: MIT + size: 78863 + timestamp: 1693868663440 +- kind: conda + name: portaudio + version: 19.6.0 + build: h5c6c0ed_9 + build_number: 9 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/portaudio-19.6.0-h5c6c0ed_9.conda + sha256: a73e31c5fe9d717cd42470b394018f4e48eed4a439b9371d2c6d380c86aed591 + md5: ab049f8223bccc6f621975beaa75c624 + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 118203 + timestamp: 1693868376750 +- kind: conda + name: portaudio + version: 19.6.0 + build: h7c63dc7_9 + build_number: 9 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h7c63dc7_9.conda + sha256: c09ae032d0303abfea34c0957834538b48133b0431283852741ed3e0f66fdb36 + md5: 893f2c33af6b03cfd04820a8c31f5798 + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 115512 + timestamp: 1693868383 +- kind: conda + name: portmidi + version: 2.0.4 + build: h13dd4ca_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/portmidi-2.0.4-h13dd4ca_2.conda + sha256: b948bea6ef3203d049997ca348ba0d751a3d891579388e53d323693b20896af6 + md5: fe77195a950275fe63e560b1b855e818 + depends: + - libcxx >=15.0.7 + license: MIT + license_family: MIT + size: 47319 + timestamp: 1693882007724 +- kind: conda + name: portmidi + version: 2.0.4 + build: h5c6c0ed_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/portmidi-2.0.4-h5c6c0ed_2.conda + sha256: 64ed7bdebe45af7987f65402506fa5ad6f9fd339a93f8fe7a57c6bcce497022f + md5: 8ee28835da08f915cba9317fccb6396d + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 45266 + timestamp: 1693882668197 +- kind: conda + name: portmidi + version: 2.0.4 + build: h7c63dc7_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/portmidi-2.0.4-h7c63dc7_2.conda + sha256: f4e1245409e97b5b46e655ba301da5e9bc1d6ae64f5ae46004583dae31214868 + md5: 5b2518aa80f149ee6881f5ad959f5d59 + depends: + - alsa-lib >=1.2.10,<1.3.0.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 43765 + timestamp: 1693881734349 +- kind: conda + name: prometheus_client + version: 0.21.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + sha256: bc8f00d5155deb7b47702cb8370f233935704100dbc23e30747c161d1b6cf3ab + md5: 3e01e386307acc60b2f89af0b2e161aa + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 49002 + timestamp: 1733327434163 +- kind: conda + name: propcache + version: 0.2.1 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + sha256: 5771311fb5ded614ca349c92579a0b752af55a310f40b71fc533e20625965391 + md5: 55d5742a696d7da1c1262e99b6217ceb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52747 + timestamp: 1733391916349 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + sha256: c7f62c11ed929ccf1f3d4a1e200e28be01e8d0e0786bf8f76c5893f2ea681e1b + md5: 50ab8953e7ff1333a4a47cda32e68123 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52484 + timestamp: 1733391993461 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + sha256: f8c266c494aa1e4cfb8bf0b6fca060044b2f3d65afe4c5062ebeea382e77aa6d + md5: c84e3dd97fe25a17322c4a0f670c6750 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 48225 + timestamp: 1733392308901 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h2ec8cdc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + sha256: 4884f8161602f0148ebbc1af8d3176cec80b96c83243f68aafd651986b573817 + md5: 586bead4a9dfa46faf88deb7d3a742bb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 464548 + timestamp: 1728669645013 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h6f74592_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + sha256: f874ffd38b9ae2b810e9d2e43fd8d3b778cdeaf7dea4a3e6ee4adeafe2d936cf + md5: 4b9b22bd7c53d938b207f9d0f79db183 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 472764 + timestamp: 1728669483611 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312hf02c72a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + sha256: dbcec117510ced5c12097e3eb06ebbf4512dc255733a9ace33c4249fb7e6a364 + md5: 6fda46c82abd0a080ca33de7d16ca877 + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 447369 + timestamp: 1728669902591 +- kind: conda + name: pthread-stubs + version: '0.4' + build: h86ecc28_1002 + build_number: 1002 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba + md5: bb5a90c93e3bac3d5690acf76b4a6386 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 8342 + timestamp: 1726803319942 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hb9d3cd8_1002 + build_number: 1002 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 8252 + timestamp: 1726802366959 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hd74edd7_1002 + build_number: 1002 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 8381 + timestamp: 1726802424786 +- kind: conda + name: pulseaudio-client + version: '17.0' + build: h729494f_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pulseaudio-client-17.0-h729494f_0.conda + sha256: 209eac3123ee2c84a35401626941c4aa64e04e2c9854084ddeba6432c6078a41 + md5: f35f57712d5c2abca98c85a51a408bc1 + depends: + - dbus >=1.13.6,<2.0a0 + - libgcc-ng >=12 + - libglib >=2.78.3,<3.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libsystemd0 >=255 + constrains: + - pulseaudio 17.0 *_0 + license: LGPL-2.1-or-later + license_family: LGPL + size: 766184 + timestamp: 1705690164726 +- kind: conda + name: pulseaudio-client + version: '17.0' + build: hb77b528_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hb77b528_0.conda + sha256: b27c0c8671bd95c205a61aeeac807c095b60bc76eb5021863f919036d7a964fc + md5: 07f45f1be1c25345faddb8db0de8039b + depends: + - dbus >=1.13.6,<2.0a0 + - libgcc-ng >=12 + - libglib >=2.78.3,<3.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libsystemd0 >=255 + constrains: + - pulseaudio 17.0 *_0 + license: LGPL-2.1-or-later + license_family: LGPL + size: 757633 + timestamp: 1705690081905 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h1f38498_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + sha256: 06c0e208d5bf15051874097366c8e8e5db176dffba38526f227a34e80cc8e9bc + md5: 3710616b880b31d0c8afd8ae7e12392a + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25375 + timestamp: 1732610892198 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h7900ff3_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + sha256: 46a61c29375d3bf1933eae61c7861394c168898915d59fc99bf05e46de2ff5ad + md5: ac65b70df28687c6af4270923c020bdd + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25213 + timestamp: 1732610785600 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h8025657_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + sha256: 49db959887cb89b44053a44a98d0f35644fc0b2003587492f02b56046de0b60a + md5: 9bb7d32e96a5dcb5ea7fd90a11a83656 + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25374 + timestamp: 1732611006864 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h01725c0_0_cpu + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + sha256: 948a4161c56f846d374a3721a657e58ddbc992a29b3b3e7a6411975c30361d94 + md5: ee80934a6c280ff8635f8db5dec11e04 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4612916 + timestamp: 1732610377259 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h66f7834_0_cpu + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + sha256: e7eb062145be554c23dfefa0ebe8c5f6ae8c59635117a6921e66403d6addcda3 + md5: 3390c8b8f57e85506c92a37cf750bdd7 + depends: + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4406662 + timestamp: 1732610939832 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312hc40f475_0_cpu + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + sha256: 063eb168a29d4ce6d9ed865e9e1ad3b6e141712189955a79e06b24ddc0cbbc9c + md5: 9859e7c4b94bbf69772dbf0511101cec + depends: + - __osx >=11.0 + - libarrow 18.1.0.* *cpu + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 3909116 + timestamp: 1732610863261 +- kind: conda + name: pycparser + version: '2.22' + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- kind: conda + name: pydantic + version: 2.10.3 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + sha256: cac9eebd3d5f8d8a497a9025d756257ddc75b8b3393e6737cb45077bd744d4f8 + md5: 194ef7f91286978521350f171b117f01 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.27.1 + - python >=3.9 + - typing-extensions >=4.6.1 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + size: 317037 + timestamp: 1733316963547 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + sha256: c89741f4eff395f8de70975f42e1f20591f0e0870929d440af35b13399976b09 + md5: 114030cb28527db2c385f07038e914c8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1635156 + timestamp: 1732254225040 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + sha256: 1f59bc1914f77faed3c95217e4d093310771baee4e93a15c0479359559e3fa19 + md5: d980860b8bf193f53d30a19c5d2bf070 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1503747 + timestamp: 1732254331303 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + sha256: 5bba8de2bbbbdb39390abb1e2aff310e8cfd49646ae5a0e0ea4d6582bd1d52ba + md5: 3847a96eaf24a877b6091150ff9c4955 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1449057 + timestamp: 1732254359451 +- kind: conda + name: pydantic-settings + version: 2.7.0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + sha256: dd1ac7c8b6a189c8aa18f6c7df019d8f6df495300a259e3fbebdb542fc955c3b + md5: d9f19a7c4199249fa229891b573b6f9b + depends: + - pydantic >=2.7.0 + - python >=3.9 + - python-dotenv >=0.21.0 + license: MIT + license_family: MIT + size: 31426 + timestamp: 1734127929720 +- kind: conda + name: pygame + version: 2.6.1 + build: py312h4fcb14b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pygame-2.6.1-py312h4fcb14b_0.conda + sha256: 7a5582c3eed17d0223cbd79dfb25ebae1ec7f8b06eb550fb65e163adb5f1c75b + md5: 80c4be6aac23ad6dfc2aeca1b1ab7d1f + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.14.2,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - portmidi >=2.0.4,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - sdl2 >=2.30.7,<3.0a0 + - sdl2_image >=2.8.2,<3.0a0 + - sdl2_mixer >=2.6.3,<3.0a0 + - sdl2_ttf >=2.22.0,<3.0a0 + license: LGPL-2.1-only + size: 2984508 + timestamp: 1727636750824 +- kind: conda + name: pygame + version: 2.6.1 + build: py312hb14fe3b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pygame-2.6.1-py312hb14fe3b_0.conda + sha256: 78b91e7b4cee736829f96ec8445674d03b60c03cc51da6f74cac1a14286fe686 + md5: 3a2a4db46a57a51f36f4d79f9f9b6c97 + depends: + - __osx >=11.0 + - fontconfig >=2.14.2,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - libcxx >=17 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + - portmidi >=2.0.4,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - sdl2 >=2.30.7,<3.0a0 + - sdl2_image >=2.8.2,<3.0a0 + - sdl2_mixer >=2.6.3,<3.0a0 + - sdl2_ttf >=2.22.0,<3.0a0 + license: LGPL-2.1-only + size: 2932509 + timestamp: 1727636775263 +- kind: conda + name: pygame + version: 2.6.1 + build: py312hb2c8110_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pygame-2.6.1-py312hb2c8110_0.conda + sha256: 347dc650cbcb5b25f632ae62c87357c15db79849ff582887f6910ae245dc7d4f + md5: f4a1a0e1e2a435699366317ffeabd1bc + depends: + - fontconfig >=2.14.2,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - portmidi >=2.0.4,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - sdl2 >=2.30.7,<3.0a0 + - sdl2_image >=2.8.2,<3.0a0 + - sdl2_mixer >=2.6.3,<3.0a0 + - sdl2_ttf >=2.22.0,<3.0a0 + license: LGPL-2.1-only + size: 2979554 + timestamp: 1727636776938 +- kind: conda + name: pygments + version: 2.18.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + sha256: 0d6133545f268b2b89c2617c196fc791f365b538d4057ecd636d658c3b1e885d + md5: b38dc0206e2a530e5c2cf11dc086b31a + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 876700 + timestamp: 1733221731178 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + sha256: 6879d52fb0ec2258e2850476786a652c394220d53883c53691ed5390183ae925 + md5: f0e4a98d54477083ddc9d2f33507f848 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 181512 + timestamp: 1728714205508 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + sha256: 8a006507a4003fb01eeee2f9ba79f994478694766ea3b445273da5c11cf8e763 + md5: 798f42d9bfdf125dc80ffbec0e96e0b6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 182021 + timestamp: 1728714164706 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + sha256: 7967b94b8f0ff75847302444e9c43ac11a391d74da24cb14fba1049fac9e5ba9 + md5: 5274663cb05dfbe316db50af6da4389f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 183141 + timestamp: 1728714267954 +- kind: conda + name: pysocks + version: 1.7.1 + build: pyha55dd90_7 + build_number: 7 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 +- kind: conda + name: python + version: 3.12.8 + build: h1683364_1_cpython + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + sha256: 85573582d5b0f79923fed0a8365d3d74d21eee9f0a5fa1b9345f191e006363ab + md5: 09ec612ea05370989eaa3d81abf0f369 + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 13760816 + timestamp: 1733407890896 +- kind: conda + name: python + version: 3.12.8 + build: h9e4cc4f_1_cpython + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + sha256: 3f0e0518c992d8ccfe62b189125721309836fe48a010dc424240583e157f9ff0 + md5: 7fd2fd79436d9b473812f14e86746844 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 31565686 + timestamp: 1733410597922 +- kind: conda + name: python + version: 3.12.8 + build: hc22306f_1_cpython + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + sha256: 7586a711b1b08a9df8864e26efdc06980bdfb0e18d5ac4651d0fee30a8d3e3a0 + md5: 54ca5b5d92ef3a3ba61e195ee882a518 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 12998673 + timestamp: 1733408900971 +- kind: conda + name: python-dateutil + version: 2.9.0.post0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + sha256: a50052536f1ef8516ed11a844f9413661829aa083304dc624c5925298d078d79 + md5: 5ba79d7c71f03c678c8ead841f347d6e + depends: + - python >=3.9 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222505 + timestamp: 1733215763718 +- kind: conda + name: python-dotenv + version: 1.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 + md5: e5c6ed218664802d305e79cc2d4491de + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 24215 + timestamp: 1733243277223 +- kind: conda + name: python-json-logger + version: 2.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca + md5: a61bf9ec79426938ff785eb69dbb1960 + depends: + - python >=3.6 + license: BSD-2-Clause + license_family: BSD + size: 13383 + timestamp: 1677079727691 +- kind: conda + name: python-multipart + version: 0.0.20 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 27913 + timestamp: 1734420869885 +- kind: conda + name: python-tzdata + version: '2024.2' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + sha256: 57c9a02ec25926fb48edca59b9ede107823e5d5c473b94a0e05cc0b9a193a642 + md5: c0def296b2f6d2dd7b030c2a7f66bb1f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 142235 + timestamp: 1733235414217 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + sha256: 28204ef48f028a4d872e22040da0dad7ebd703549b010a1bb511b6dd94cf466d + md5: 266fe1ae54a7bb17990206664d0f0ae4 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 21765 + timestamp: 1725272382968 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h52516f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + sha256: 0fa5ba80073a43391ee90303814adbc9fd826175de1fdac273ba0e5b711aa255 + md5: 591c4ae6d8338dfd07b951e00433a405 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23589 + timestamp: 1725273317965 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + sha256: 20851b1e59fee127d49e01fc73195a88ab0779f103b7d6ffc90d11142a83678f + md5: 39aed2afe4d0cf76ab3d6b09eecdbea7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23162 + timestamp: 1725272139519 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 + md5: 0424ae29b104430108f5218a66db7260 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6238 + timestamp: 1723823388266 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + sha256: 5ccdad9981753cc4a2d126e356673a21c0cd5b34e209cb8d476a3947d4ad9b39 + md5: 62b20f305498284a07dc6c45fd0e5c87 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6329 + timestamp: 1723823366253 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + sha256: 49d624e4b809c799d2bf257b22c23cf3fc4460f5570d9a58e7ad86350aeaa1f4 + md5: b76f9b1c862128e56ac7aa8cd2333de9 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6278 + timestamp: 1723823099686 +- kind: conda + name: pytz + version: '2024.1' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + sha256: 1a7d6b233f7e6e3bbcbad054c8fd51e690a67b129a899a056a5e45dd9f00cb41 + md5: 3eeeeb9e4827ace8c0c1419c85d590ad + depends: + - python >=3.7 + license: MIT + license_family: MIT + size: 188538 + timestamp: 1706886944988 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + sha256: b06f1c15fb39695bbf707ae8fb554b9a77519af577b5556784534c7db10b52e3 + md5: 1ee23620cf46cb15900f70a1300bae55 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 187143 + timestamp: 1725456547263 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + sha256: a60705971e958724168f2ebbb8ed4853067f1d3f7059843df3903e3092bbcffa + md5: 549e5930e768548a89c23f595dac5a95 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 206553 + timestamp: 1725456256213 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + sha256: 8c515ebe1e7e85d972d72b75760af9dfac06fd11a9dba7e05c42d69aedbb303c + md5: dc5de424f7dbb9772da720dbb81317b2 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 199141 + timestamp: 1725456356043 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312h2427ae1_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + sha256: cfc4ea87d68b5f0ed64a61f500d5ea0a2310d1f281a4f95afa06c703ea1bdf7d + md5: 1f0779280c3dc1e72cfd86bd1e59791d + depends: + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 371730 + timestamp: 1728644030875 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hbf22597_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + sha256: bc303f9b11e04a515f79cd5ad3bfa0e84b9dfec76552626d6263b38789fe6678 + md5: 746ce19f0829ec3e19c93007b1a224d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 378126 + timestamp: 1728642454632 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hf8a1cbd_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + sha256: 2e0ca1bb9ab3af5d1f9b38548d65be7097ba0246e7e63c908c9b1323df3f45b5 + md5: 7bdaa4c2a84b744ef26c8b2ba65c3d0e + depends: + - __osx >=11.0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 361674 + timestamp: 1728642457661 +- kind: conda + name: rav1e + version: 0.6.6 + build: h1d8f897_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/rav1e-0.6.6-h1d8f897_2.conda + sha256: 093f21277dc5763cf0397e016e8291c2b796926ebbb173428dc9cdf5d012f328 + md5: 12c850a42b1ad1ed46a284a93959ee6a + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 14347236 + timestamp: 1694329141875 +- kind: conda + name: rav1e + version: 0.6.6 + build: h69fbcac_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.6.6-h69fbcac_2.conda + sha256: be6174970193cb4d0ffa7d731a93a4c9542881dbc7ab24e74b460ef312161169 + md5: e309ae86569b1cd55a0285fa4e939844 + license: BSD-2-Clause + license_family: BSD + size: 1526706 + timestamp: 1694329743011 +- kind: conda + name: rav1e + version: 0.6.6 + build: he8a937b_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.6.6-he8a937b_2.conda + sha256: 91b3c1ced90d04ee2eded1f72cf3cbc19ff05a25e41876ef0758266a5bab009f + md5: 77d9955b4abddb811cb8ab1aa7d743e4 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 15423721 + timestamp: 1694329261357 +- kind: conda + name: re2 + version: 2024.07.02 + build: h2d3a13d_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + sha256: 55e7be480bfb979fa8595a16d7f2adea3a5ac9a77b2e97cd0f7ac40e989edb6c + md5: 83f4e47229834c895a92c18383e1cd9d + depends: + - libre2-11 2024.07.02 h18dbdb1_1 + license: BSD-3-Clause + license_family: BSD + size: 26747 + timestamp: 1728778986331 +- kind: conda + name: re2 + version: 2024.07.02 + build: h77b4e00_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + sha256: c1721cb80f7201652fc9801f49c214c88aee835d957f2376e301bd40a8415742 + md5: 01093ff37c1b5e6bf9f17c0116747d11 + depends: + - libre2-11 2024.07.02 hbbce691_1 + license: BSD-3-Clause + license_family: BSD + size: 26665 + timestamp: 1728778975855 +- kind: conda + name: re2 + version: 2024.07.02 + build: hcd0e937_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + sha256: eebddde6cb10b146507810b701ef6df122d5309cd5151a39d0828aa44dc53725 + md5: 19e29f2ccc9168eb0a39dc40c04c0e21 + depends: + - libre2-11 2024.07.02 h2348fd5_1 + license: BSD-3-Clause + license_family: BSD + size: 26860 + timestamp: 1728779123653 +- kind: conda + name: readline + version: '8.2' + build: h8228510_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 + md5: 47d31b792659ce70f470b5c82fdfb7a4 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 281456 + timestamp: 1679532220005 +- kind: conda + name: readline + version: '8.2' + build: h8fc344f_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + sha256: 4c99f7417419734e3797d45bc355e61c26520e111893b0d7087a01a7fbfbe3dd + md5: 105eb1e16bf83bfb2eb380a48032b655 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 294092 + timestamp: 1679532238805 +- kind: conda + name: readline + version: '8.2' + build: h92ec313_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 + md5: 8cbb776a2f641b943d413b3e19df71f4 + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 250351 + timestamp: 1679532511311 +- kind: conda + name: regex + version: 2024.11.6 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + sha256: fcb5687d3ec5fff580b64b8fb649d9d65c999a91a5c3108a313ecdd2de99f06b + md5: 647770db979b43f9c9ca25dcfa7dc4e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 402821 + timestamp: 1730952378415 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + sha256: ec2c416860de29224e447e2031f8686a05476759c17da1f32f61d4307e540ec8 + md5: fa8b589107567f532fa1380e66f91776 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 398947 + timestamp: 1730952477463 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + sha256: dcdec32f2c7dd37986baa692bedf9db126ad34e92e5e9b64f707cba3d04d2525 + md5: e73cda1f18846b608284bd784f061eac + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 366374 + timestamp: 1730952427552 +- kind: conda + name: requests + version: 2.32.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad + md5: a9b9368f3701a417eac9edbcae7cb737 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 58723 + timestamp: 1733217126197 +- kind: conda + name: rich + version: 13.9.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 + md5: 7aed65d4ff222bfb7335997aa40b7da5 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + size: 185646 + timestamp: 1733342347277 +- kind: conda + name: rich-toolkit + version: 0.11.3 + build: pyh29332c3_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 + md5: 4ba15ae9388b67d09782798347481f69 + depends: + - python >=3.9 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 17357 + timestamp: 1733750834072 +- kind: conda + name: s2n + version: 1.5.10 + build: h5df210e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + sha256: b5e7a9f4b7b1ec5c5c3661e2defc8b47fab543b05cad6fec78739d8007612464 + md5: 3d3979efcc0f44f3f0cef3de03b296cc + depends: + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 353450 + timestamp: 1734415474615 +- kind: conda + name: s2n + version: 1.5.10 + build: hb5b8611_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + sha256: f6d451821fddc26b93f45e9313e1ea15e09e5ef049d4e137413a5225d2a5dfba + md5: 999f3673f2a011f59287f2969e3749e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 355142 + timestamp: 1734415467047 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + sha256: e44515f875c10efb5e041efcb250dfd18f2cb66ec3f268237549ead6284c6922 + md5: 3b87a00bcaab069172d6cef8124b7142 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 402547 + timestamp: 1725632183154 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + sha256: e83ebeaba4a07bbe4a1d6c7eef0b4f7ae19901ef365bca043808d16e4c8faad8 + md5: 82ef253c37308b082a478fb92924cad6 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 400284 + timestamp: 1725632278147 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312he431725_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + sha256: 93a085d0d64237db7f4ff395c446f268c575dc2c324d8e3e5c5d7d836896295e + md5: ccb978cf1e3151c25a44c4ae65c1f20e + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 353606 + timestamp: 1725632294079 +- kind: conda + name: sdl2 + version: 2.30.10 + build: h63c27ac_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.30.10-h63c27ac_0.conda + sha256: 639325326d51cd70f56a55ffd3c1fa778e61751f16d66d0baea155375f1a139c + md5: 5cecf6d327e4f8c5dfafc71b4a8556e7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - pulseaudio-client >=17.0,<17.1.0a0 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: Zlib + size: 1352990 + timestamp: 1733624788165 +- kind: conda + name: sdl2 + version: 2.30.10 + build: h93e764a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2-2.30.10-h93e764a_0.conda + sha256: 8f03f2abcb4227e95d19b28124e0a5428f634b084b92ab91f3af7c825b626403 + md5: bcbf24da778bbdd20b09cf6ed52cefc2 + depends: + - libgcc >=13 + - libstdcxx >=13 + - pulseaudio-client >=17.0,<17.1.0a0 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: Zlib + size: 1277076 + timestamp: 1733624829558 +- kind: conda + name: sdl2 + version: 2.30.10 + build: h994913f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2-2.30.10-h994913f_0.conda + sha256: 7ff3167b6482c5fe7389c6c1836343c280a0eeb160524888e661f0f991708bd8 + md5: 4001ae6f1b1886583e82ab0dac5b575b + depends: + - __osx >=11.0 + - libcxx >=18 + license: Zlib + size: 1251116 + timestamp: 1733624861414 +- kind: conda + name: sdl2_image + version: 2.8.2 + build: h06ee604_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/sdl2_image-2.8.2-h06ee604_1.conda + sha256: f18184e016e2e57306d1540dea584d38f4617d7ddb6aad4af6b5f21c52fa39ea + md5: 65e113270b460dcdfc4dc0a80bb3d11c + depends: + - libavif16 >=1.0.4,<2.0a0 + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.43,<1.7.0a0 + - libstdcxx-ng >=12 + - libtiff >=4.6.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.2.13,<2.0.0a0 + - sdl2 >=2.30.2,<3.0a0 + license: Zlib + size: 152110 + timestamp: 1716857107234 +- kind: conda + name: sdl2_image + version: 2.8.2 + build: h376e2e1_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2_image-2.8.2-h376e2e1_1.conda + sha256: 8c385478a7b6a6e34b3b3f3d48ed48f504698544987ad331f03a7b43b11d689a + md5: 247d2c5a873901ef51f378c69a2c708e + depends: + - __osx >=11.0 + - libavif16 >=1.0.4,<2.0a0 + - libcxx >=16 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.43,<1.7.0a0 + - libtiff >=4.6.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.2.13,<2.0.0a0 + - sdl2 >=2.30.2,<3.0a0 + license: Zlib + size: 118041 + timestamp: 1716857215118 +- kind: conda + name: sdl2_image + version: 2.8.2 + build: hd95cb85_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2_image-2.8.2-hd95cb85_1.conda + sha256: 8bdd72e4789616b0db5fc8e756a4156d3bd7fd35cf96c1cb892d9f6bff3f6508 + md5: 45fbcda052d64b07fa601d965a41cb69 + depends: + - libavif16 >=1.0.4,<2.0a0 + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.43,<1.7.0a0 + - libstdcxx-ng >=12 + - libtiff >=4.6.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.2.13,<2.0.0a0 + - sdl2 >=2.30.2,<3.0a0 + license: Zlib + size: 151138 + timestamp: 1716858240454 +- kind: conda + name: sdl2_mixer + version: 2.6.3 + build: h422cae6_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2_mixer-2.6.3-h422cae6_1.conda + sha256: 8fa4580bddd4d33f5fbddf5c54873613a7f8fcd9f781656fbf9fd1b27975b196 + md5: 75e56f84030bd1244d8bff3c55e8418e + depends: + - fluidsynth >=2.3.4,<2.4.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libmad >=0.15.1b,<0.16.0a0 + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + - opusfile >=0.12,<0.13.0a0 + - sdl2 >=2.28.3,<3.0a0 + license: Zlib + size: 238285 + timestamp: 1695761803447 +- kind: conda + name: sdl2_mixer + version: 2.6.3 + build: h4fe3bdc_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2_mixer-2.6.3-h4fe3bdc_1.conda + sha256: fcaaf1b589aed11498f5b9735b996fb92ff18e4673a4c804bbfa28eb00264e06 + md5: b92222911d46f08faa583df51191bd7f + depends: + - fluidsynth >=2.3.4,<2.4.0a0 + - libcxx >=15.0.7 + - libflac >=1.4.3,<1.5.0a0 + - libmad >=0.15.1b,<0.16.0a0 + - libogg >=1.3.4,<1.4.0a0 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + - opusfile >=0.12,<0.13.0a0 + - sdl2 >=2.28.3,<3.0a0 + license: Zlib + size: 188504 + timestamp: 1695762176058 +- kind: conda + name: sdl2_mixer + version: 2.6.3 + build: h8830914_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/sdl2_mixer-2.6.3-h8830914_1.conda + sha256: c3e99e222b091f26cfd1d6be22c5a2973df9e7caa020262f9d9523f340344a95 + md5: 1a2b60be4d860a0c419a87176c85c3ad + depends: + - fluidsynth >=2.3.4,<2.4.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libmad >=0.15.1b,<0.16.0a0 + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + - opusfile >=0.12,<0.13.0a0 + - sdl2 >=2.28.3,<3.0a0 + license: Zlib + size: 202966 + timestamp: 1695761744535 +- kind: conda + name: sdl2_ttf + version: 2.22.0 + build: h287479f_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/sdl2_ttf-2.22.0-h287479f_3.conda + sha256: 382554ee6135183548ae98adf984b3de31615d429751b497a509724c017d1f1f + md5: 2ab241725f0baa88ca26d53bb6eff58a + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - harfbuzz >=10.1.0,<11.0a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - sdl2 >=2.30.7,<3.0a0 + license: Zlib + size: 62581 + timestamp: 1733782803716 +- kind: conda + name: sdl2_ttf + version: 2.22.0 + build: h443c5de_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/sdl2_ttf-2.22.0-h443c5de_3.conda + sha256: 5325195729d91c0f4f6075cb70a2d4f67db7e42b3e3cf875c9fd4f7e21fdabc4 + md5: add02757e1fa038364df05ba0aaf3ca5 + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - harfbuzz >=10.1.0,<11.0a0 + - sdl2 >=2.30.10,<3.0a0 + license: Zlib + size: 46057 + timestamp: 1733782993569 +- kind: conda + name: sdl2_ttf + version: 2.22.0 + build: hb1608df_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/sdl2_ttf-2.22.0-hb1608df_3.conda + sha256: 7bf20660944556420a499c95c46e9bc57f0ed1445218d3899f85478fa8ec367c + md5: 7ac7f17a1b2bd1ccf6161644ee03ade8 + depends: + - freetype >=2.12.1,<3.0a0 + - harfbuzz >=10.1.0,<11.0a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - sdl2 >=2.30.7,<3.0a0 + license: Zlib + size: 56134 + timestamp: 1733783877446 +- kind: conda + name: shellingham + version: 1.5.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef + md5: 7c3c2a0f3ebdea2bbc35538d162b43bf + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14462 + timestamp: 1733301007770 +- kind: conda + name: six + version: 1.17.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + sha256: 41db0180680cc67c3fa76544ffd48d6a5679d96f4b71d7498a759e94edc9a2db + md5: a451d576819089b0d672f18768be0f65 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 16385 + timestamp: 1733381032766 +- kind: conda + name: snappy + version: 1.2.1 + build: h8bd8927_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + sha256: ec91e86eeb2c6bbf09d51351b851e945185d70661d2ada67204c9a6419d282d3 + md5: 3b3e64af585eadfb52bb90b553db5edf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 42739 + timestamp: 1733501881851 +- kind: conda + name: snappy + version: 1.2.1 + build: h98b9ce2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f + md5: ded86dee325290da2967a3fea3800eb5 + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-3-Clause + license_family: BSD + size: 35857 + timestamp: 1733502172664 +- kind: conda + name: snappy + version: 1.2.1 + build: hd4fb6f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + sha256: c4a07ae5def8d55128f25a567a296ef9d7bf99a3bc79d46bd5160c076a5f50af + md5: 2fcc6cd1e5550deb509073fd2e6693e1 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 43032 + timestamp: 1733501964775 +- kind: conda + name: sniffio + version: 1.3.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 + md5: bf7a226e58dfb8346c70df36065d86c9 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 15019 + timestamp: 1733244175724 +- kind: conda + name: sse-starlette + version: 2.1.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + sha256: 6d671a66333410ec7e5e7858a252565a9001366726d1fe3c3a506d7156169085 + md5: 3918255c942c242ed5599e10329e8d0e + depends: + - anyio + - python >=3.8 + - starlette + - uvicorn + license: BSD-3-Clause + license_family: BSD + size: 14712 + timestamp: 1722520112550 +- kind: conda + name: starlette + version: 0.41.3 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + sha256: b74fc76107487eb26624c01fc55bfab7eed03ae82e003333c86d8a1eeac53672 + md5: 0207dac04ae2200701fab697f0aaaac4 + depends: + - anyio >=3.4.0,<5 + - python >=3.9 + - typing_extensions >=3.10.0 + license: BSD-3-Clause + license_family: BSD + size: 58838 + timestamp: 1733344472634 +- kind: conda + name: svt-av1 + version: 2.3.0 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-2.3.0-h5888daf_0.conda + sha256: df30a9be29f1a8b5a2e314dd5b16ccfbcbd1cc6a4f659340e8bc2bd4de37bc6f + md5: 355898d24394b2af353eb96358db9fdd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 2746291 + timestamp: 1730246036363 +- kind: conda + name: svt-av1 + version: 2.3.0 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/svt-av1-2.3.0-h5ad3122_0.conda + sha256: 2fad2496a21d198ea72f5dabfdace2fae0ced5cc3ea243922cb372fcf4c18222 + md5: efb60b536bbf64772929b57f6b30298b + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 1796731 + timestamp: 1730246027014 +- kind: conda + name: svt-av1 + version: 2.3.0 + build: hf24288c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/svt-av1-2.3.0-hf24288c_0.conda + sha256: ab876ed8bdd20e22a868dcb8d03e9ce9bbba7762d7e652d49bfff6af768a5b8f + md5: 114c33e9eec335a379c9ee6c498bb807 + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-2-Clause + license_family: BSD + size: 1387330 + timestamp: 1730246134730 +- kind: conda + name: tk + version: 8.6.13 + build: h194ca79_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + sha256: 7fa27cc512d3a783f38bd16bbbffc008807372499d5b65d089a8e43bde9db267 + md5: f75105e0585851f818e0009dd1dde4dc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3351802 + timestamp: 1695506242997 +- kind: conda + name: tk + version: 8.6.13 + build: h5083fa2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 + md5: b50a57ba89c32b62428b71a875291c9b + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3145523 + timestamp: 1699202432999 +- kind: conda + name: tk + version: 8.6.13 + build: noxft_h4845f30_101 + build_number: 101 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3318875 + timestamp: 1699202167581 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312h8360d73_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + sha256: 4f504a5e9d77c6d88a8f735c4319429d8bf40b742384f908a2efe0a09acc3cc5 + md5: f953aa733207f3d37acf4a3efbedba89 + depends: + - __glibc >=2.17,<3.0.a0 + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2258007 + timestamp: 1732734202127 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312ha0d6ea1_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + sha256: ef0f4d4e2c798b1821187ea0ba4c86484e48abaa0e9a19fe68030fa7ff5dde84 + md5: 077f48c9e0c08a30d842e15c51df4143 + depends: + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2331194 + timestamp: 1732734303196 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312hf3e4074_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + sha256: 5d395333fcb22dc611140286c1f2ea8b3fa220a4931c583587cb612238091555 + md5: 4c732c74b485ef7ac8ec1c548dd45e8e + depends: + - __osx >=11.0 + - huggingface_hub >=0.16.4,<1.0 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 1931389 + timestamp: 1732734727624 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h52516f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + sha256: 4c19a544354172b2273553267e734795a6da3c78a04c2d19f8e9e159ca3178bc + md5: e28996d9d2d44d777b7e6fb12f63715b + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 841662 + timestamp: 1732616934923 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + sha256: 062a3a3a37fa8615ce57929ba7e982c76f5a5810bcebd435950f6d6c4147c310 + md5: e417822cb989e80a0d2b1b576fdd1657 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 840414 + timestamp: 1732616043734 +- kind: conda + name: tornado + version: 6.4.2 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + sha256: 964a2705a36c50040c967b18b45b9cc8de3c2aff4af546979a574e0b38e58e39 + md5: fb0605888a475d6a380ae1d1a819d976 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 842549 + timestamp: 1732616081362 +- kind: conda + name: tqdm + version: 4.67.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d + md5: 4085c9db273a148e149c03627350e22c + depends: + - colorama + - python >=3.7 + license: MPL-2.0 or MIT + size: 89484 + timestamp: 1732497312317 +- kind: conda + name: traitlets + version: 5.14.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 + md5: 019a7385be9af33791c989871317e1ed + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110051 + timestamp: 1733367480074 +- kind: conda + name: transformers + version: 4.47.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + sha256: d31821081219a0ede5c1f356b65a61ce98ac11e2df78b0eaa684c17c73389fbf + md5: 6d2ec1ddee8057d2d724a0ab0bb578a0 + depends: + - datasets !=2.5.0 + - filelock + - huggingface_hub >=0.23.0,<1.0 + - numpy >=1.17 + - packaging >=20.0 + - python >=3.9 + - pyyaml >=5.1 + - regex !=2019.12.17 + - requests + - safetensors >=0.4.1 + - tokenizers >=0.21,<0.22 + - tqdm >=4.27 + license: Apache-2.0 + license_family: APACHE + size: 3726957 + timestamp: 1733948063517 +- kind: conda + name: typer + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + sha256: ef695490e895c2ad552c77ec497b899b09fd4ad4ab07edcf5649f5994cf92a35 + md5: 170a0398946d8f5b454e592672b6fc20 + depends: + - python >=3.9 + - typer-slim-standard 0.15.1 hd8ed1ab_0 + license: MIT + license_family: MIT + size: 56175 + timestamp: 1733408582623 +- kind: conda + name: typer-slim + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + sha256: d4965516f35e0805199de6596c4ac76c4ad3d6b012be35e532102f9e53ecb860 + md5: 0218b16f5a1dd569e575a7a6415489db + depends: + - click >=8.0.0 + - python >=3.9 + - typing_extensions >=3.7.4.3 + constrains: + - rich >=10.11.0 + - typer >=0.15.1,<0.15.2.0a0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + size: 43592 + timestamp: 1733408569554 +- kind: conda + name: typer-slim-standard + version: 0.15.1 + build: hd8ed1ab_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + sha256: f31c56fe98315da8b9ce848256c17e0b9f87896b41a6ccf0c9cc74644dcef20f + md5: 4e603c43bfdfc7b533be087c3e070cc9 + depends: + - rich + - shellingham + - typer-slim 0.15.1 pyhd8ed1ab_0 + license: MIT + license_family: MIT + size: 49531 + timestamp: 1733408570063 +- kind: conda + name: typing-extensions + version: 4.12.2 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 + md5: b6a408c64b78ec7b779a3e5c7a902433 + depends: + - typing_extensions 4.12.2 pyha770c72_1 + license: PSF-2.0 + license_family: PSF + size: 10075 + timestamp: 1733188758872 +- kind: conda + name: typing_extensions + version: 4.12.2 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 + md5: d17f13df8b65464ca316cbc000a3cb64 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 39637 + timestamp: 1733188758212 +- kind: conda + name: tzdata + version: 2024b + build: hc8b5060_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + sha256: 4fde5c3008bf5d2db82f2b50204464314cc3c91c1d953652f7bd01d9e52aefdf + md5: 8ac3367aafb1cc0a068483c580af8015 + license: LicenseRef-Public-Domain + size: 122354 + timestamp: 1728047496079 +- kind: conda + name: urllib3 + version: 2.2.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + sha256: 416e30a1c3262275f01a3e22e783118d9e9d2872a739a9ed860d06fa9c7593d5 + md5: 4a2d8ef7c37b8808c5b9b750501fffce + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + size: 98077 + timestamp: 1733206968917 +- kind: conda + name: uvicorn + version: 0.34.0 + build: pyh31011fe_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa + md5: 5d448feee86e4740498ec8f8eb40e052 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.9 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + size: 48643 + timestamp: 1734293057914 +- kind: conda + name: uvicorn-standard + version: 0.34.0 + build: h31011fe_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec + md5: 32a94143a7f65d76d2d5da37dcb4ed79 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.34.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + size: 7203 + timestamp: 1734293058849 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h0bf5046_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + sha256: b1efa77aa4871d7bb09c8dd297fa9bd9070ba7f0f95f2d12ae9cdd31ce8b6b22 + md5: 4f5110253ba80ebf27e55c4ab333880a + depends: + - __osx >=11.0 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 544097 + timestamp: 1730214653726 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + sha256: 9337a80165fcf70b06b9d6ba920dad702260ca966419ae77560a15540e41ab72 + md5: 998e481e17c1b6a74572e73b06f2df08 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 701355 + timestamp: 1730214506716 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + sha256: 807eede6698bd00a1d739a3e19ee6ae6a03a66d2ddd2ef150f2dfd198c3b0292 + md5: d83e107ba16c77aba2feec47b7b666a4 + depends: + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 655266 + timestamp: 1730214606664 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + sha256: c89755d8e8f6384b3ba13e41dcabb40bf690c38b9d61512e963129badb1ad332 + md5: b76a5ad00856af6e74da9c3e85fed0cc + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 410432 + timestamp: 1733998892675 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + sha256: 9be9569c279dc6e7881e9b45fe9f0368218538c660641e2f8b0e023e72a6571c + md5: 3465c1a19634233abc2d1832ac01fd31 + depends: + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 404239 + timestamp: 1733998941045 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + sha256: b64b78a7d6384bf72a878256802c783c692fe641ab4b806fd7e9f45e18a5e3b4 + md5: 13b89e1aa72aa773806b1f59ec018b67 + depends: + - __osx >=11.0 + - anyio >=3.0.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 363162 + timestamp: 1733999215646 +- kind: conda + name: websockets + version: '14.1' + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + sha256: 5998940f91765ba991cf286c863c20bcb53db92bb976a2b5a714566b86b0e763 + md5: a79f7ce618bd0a9f4c00c59a03570fcd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242145 + timestamp: 1731498716195 +- kind: conda + name: websockets + version: '14.1' + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + sha256: c292a8badcbe4040537e225fbeb237bfaf272808eab060067d965d3da98ccd5c + md5: 7e2a0ef2a1a87f88f9745f9c7059186e + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242912 + timestamp: 1731498811466 +- kind: conda + name: websockets + version: '14.1' + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + sha256: 98fb04a1a0f53dc604378f94b5795d0b8e462fee01bf0a887cb34d0efdf5d21f + md5: 89b79a9baa7db46ce21f5738a5a3dfda + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 243131 + timestamp: 1731498944076 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + sha256: a6fc0f4e90643d0c1fd4aab669b6a79f44a305a5474256f6f2da3354d2310fb4 + md5: ddbe3bb0e1356cb9074dd848570694f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 63807 + timestamp: 1732523690292 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + sha256: b9aa760a987ccc6bc9c61f57badba6798d9a3dcbd0814e5fb8df6d8d2935af73 + md5: 120d5d1c05386d8ce3efd65a4c86431f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 64783 + timestamp: 1732523806 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + sha256: 0fb35c3d1642f9f47db87bdb33148f88ef19a3af1eb0ee99b5491551c57269c7 + md5: 73414acdb779a8694a14527865b4357a + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 61043 + timestamp: 1732523852129 +- kind: conda + name: xorg-libice + version: 1.1.2 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h86ecc28_0.conda + sha256: a2ba1864403c7eb4194dacbfe2777acf3d596feae43aada8d1b478617ce45031 + md5: c8d8ec3e00cd0fd8a231789b91a7c5b7 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 60433 + timestamp: 1734229908988 +- kind: conda + name: xorg-libice + version: 1.1.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b + md5: fb901ff28063514abb6046c9ec2c4a45 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 58628 + timestamp: 1734227592886 +- kind: conda + name: xorg-libsm + version: 1.2.5 + build: h0808dbd_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.5-h0808dbd_0.conda + sha256: 2749a32a00ccd8feaab6039d7848ed875880c13d3b2601afd1788600ce5f9075 + md5: 3983c253f53f67a9d8710fc96646950f + depends: + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - xorg-libice >=1.1.1,<2.0a0 + license: MIT + license_family: MIT + size: 28061 + timestamp: 1734232077988 +- kind: conda + name: xorg-libsm + version: 1.2.5 + build: he73a12e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.5-he73a12e_0.conda + sha256: 760f43df6c2ce8cbbbcb8f2f3b7fc0f306716c011e28d1d340f3dfa8ccf29185 + md5: 4c3e9fab69804ec6077697922d70c6e2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - xorg-libice >=1.1.2,<2.0a0 + license: MIT + license_family: MIT + size: 27198 + timestamp: 1734229639785 +- kind: conda + name: xorg-libx11 + version: 1.8.10 + build: h4f16b4b_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.10-h4f16b4b_1.conda + sha256: f53994d54f0604df881c4e984279b3cf6a1648a22d4b2113e2c89829968784c9 + md5: 125f34a17d7b4bea418a83904ea82ea6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + size: 837524 + timestamp: 1733324962639 +- kind: conda + name: xorg-libx11 + version: 1.8.10 + build: hca56bd8_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.10-hca56bd8_1.conda + sha256: 5604f295906dfc496a4590e8ec19f775ccb40c5d503e6dfbac0781b5446b5391 + md5: 6e3e980940b26a060e553266ae0181a9 + depends: + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + size: 858427 + timestamp: 1733325062374 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 13593 + timestamp: 1734229104321 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 + md5: d5397424399a66d33c80b1f2345a36a6 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 15873 + timestamp: 1734230458294 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 14780 + timestamp: 1734229004433 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + sha256: efcc150da5926cf244f757b8376d96a4db78bc15b8d90ca9f56ac6e75755971f + md5: 25a5a7b797fe6e084e04ffe2db02fc62 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 20615 + timestamp: 1727796660574 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 19901 + timestamp: 1727794976192 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hd74edd7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 + md5: 77c447f48cab5d3a15ac224edb86a968 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 18487 + timestamp: 1727795205022 +- kind: conda + name: xorg-libxext + version: 1.3.6 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.6-h57736b2_0.conda + sha256: 8e216b024f52e367463b4173f237af97cf7053c77d9ce3e958bc62473a053f71 + md5: bd1e86dd8aa3afd78a4bfdb4ef918165 + depends: + - libgcc >=13 + - xorg-libx11 >=1.8.9,<2.0a0 + license: MIT + license_family: MIT + size: 50746 + timestamp: 1727754268156 +- kind: conda + name: xorg-libxext + version: 1.3.6 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + sha256: da5dc921c017c05f38a38bd75245017463104457b63a1ce633ed41f214159c14 + md5: febbab7d15033c913d53c7a2c102309d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + size: 50060 + timestamp: 1727752228921 +- kind: conda + name: xorg-libxfixes + version: 6.0.1 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.1-h57736b2_0.conda + sha256: f5c71e0555681a82a65c483374b91d91b2cb9a9903b3a22ddc00f36719fce549 + md5: 78f8715c002cc66991d7c11e3cf66039 + depends: + - libgcc >=13 + - xorg-libx11 >=1.8.9,<2.0a0 + license: MIT + license_family: MIT + size: 20289 + timestamp: 1727796500830 +- kind: conda + name: xorg-libxfixes + version: 6.0.1 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda + sha256: 2fef37e660985794617716eb915865ce157004a4d567ed35ec16514960ae9271 + md5: 4bdb303603e9821baf5fe5fdff1dc8f8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + size: 19575 + timestamp: 1727794961233 +- kind: conda + name: xorg-libxrender + version: 0.9.12 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrender-0.9.12-h86ecc28_0.conda + sha256: ffd77ee860c9635a28cfda46163dcfe9224dc6248c62404c544ae6b564a0be1f + md5: ae2c2dd0e2d38d249887727db2af960e + depends: + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + size: 33649 + timestamp: 1734229123157 +- kind: conda + name: xorg-libxrender + version: 0.9.12 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 + md5: 96d57aba173e878a2089d5638016dc5e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + size: 33005 + timestamp: 1734229037766 +- kind: conda + name: xxhash + version: 0.8.2 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + sha256: 4c526aed70b579d80e5c20d32130b6bc8bde59b3250d43c2b5269755f4da8a9b + md5: bb9faf6857108a9f62ebb4dab6ef05da + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 102442 + timestamp: 1689951682147 +- kind: conda + name: xxhash + version: 0.8.2 + build: hb547adb_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + sha256: a70f59f7221ee72c45b39a6b36a33eb9c717ba01921cce1a3c361a4676979a2e + md5: 144cd3b88706507f332f5eb5fb83a33b + license: BSD-2-Clause + license_family: BSD + size: 97593 + timestamp: 1689951969732 +- kind: conda + name: xxhash + version: 0.8.2 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f + md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 97691 + timestamp: 1689951608120 +- kind: conda + name: yaml + version: 0.2.5 + build: h3422bc3_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 + md5: 4bb3f014845110883a3c5ee811fd84b4 + license: MIT + license_family: MIT + size: 88016 + timestamp: 1641347076660 +- kind: conda + name: yaml + version: 0.2.5 + build: h7f98852_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + sha256: a4e34c710eeb26945bdbdaba82d3d74f60a78f54a874ec10d373811a5d217535 + md5: 4cb3ad778ec2d5a7acbdf254eb1c42ae + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 89141 + timestamp: 1641346969816 +- kind: conda + name: yaml + version: 0.2.5 + build: hf897c2e_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + sha256: 8bc601d6dbe249eba44b3c456765265cd8f42ef1e778f8df9b0c9c88b8558d7e + md5: b853307650cb226731f653aa623936a4 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 92927 + timestamp: 1641347626613 +- kind: conda + name: yarl + version: 1.18.3 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + sha256: a0d93c3bef723e384cff8a29a82a2c6b7a73b39328088f3a2d97c901f56e9a63 + md5: 91df2efaa08730416bec2a4502309275 + depends: + - __glibc >=2.17,<3.0.a0 + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 151393 + timestamp: 1733428897813 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + sha256: 470b5b0f3ac89acd143095281167dc2ac1a56d4fa22e1794bd8f3b00bb604540 + md5: 0b3c640697bca798d0ab428f530ed24c + depends: + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 150004 + timestamp: 1733429056665 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + sha256: 69c7863809e11bc90c0d935c16e7f151dcc925add08b3894f06059263a8cb9ba + md5: f32f9b16361866a62d6e061fcd7eb400 + depends: + - __osx >=11.0 + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 141556 + timestamp: 1733429104990 +- kind: conda + name: zeromq + version: 4.3.5 + build: h3b0a872_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + sha256: a4dc72c96848f764bb5a5176aa93dd1e9b9e52804137b99daeebba277b31ea10 + md5: 3947a35e916fcc6b9825449affbf4214 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 335400 + timestamp: 1731585026517 +- kind: conda + name: zeromq + version: 4.3.5 + build: h5efb499_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + sha256: a6003096dc0570a86492040ba32b04ce7662b159600be2252b7a0dfb9414e21c + md5: f2f3282559a4b87b7256ecafb4610107 + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 371419 + timestamp: 1731589490850 +- kind: conda + name: zeromq + version: 4.3.5 + build: hc1bb282_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + sha256: 9e585569fe2e7d3bea71972cd4b9f06b1a7ab8fa7c5139f92a31cbceecf25a8a + md5: f7e6b65943cb73bce0143737fded08f1 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=18 + - libsodium >=1.0.20,<1.0.21.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 281565 + timestamp: 1731585108039 +- kind: conda + name: zipp + version: 3.21.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 + md5: 0c3cc595284c5e8f0f9900a9b228a332 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 21809 + timestamp: 1732827613585 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312h15fbf35_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + sha256: d00ca25c1e28fd31199b26a94f8c96574475704a825d244d7a6351ad3745eeeb + md5: a4cde595509a7ad9c13b1a3809bcfe51 + depends: + - __osx >=11.0 + - cffi >=1.11 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 330788 + timestamp: 1725305806565 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hb698573_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + sha256: 2681c2a249752bdc7978e59ee2f34fcdfcbfda80029b84b8e5fec8dbc9e3af25 + md5: ffcb8e97e62af42075e0e5f46bb9856e + depends: + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 392496 + timestamp: 1725305808244 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hef9b889_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b + md5: 8b7069e9792ee4e5b4919a7a306d2e67 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 419552 + timestamp: 1725305670210 +- kind: conda + name: zstd + version: 1.5.6 + build: h02f22dd_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c + md5: be8d5f8cf21aed237b8b182ea86b3dd6 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 539937 + timestamp: 1714723130243 +- kind: conda + name: zstd + version: 1.5.6 + build: ha6fb4c9_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 554846 + timestamp: 1714722996770 +- kind: conda + name: zstd + version: 1.5.6 + build: hb46c0d2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda + sha256: 2d4fd1ff7ee79cd954ca8e81abf11d9d49954dd1fef80f27289e2402ae9c2e09 + md5: d96942c06c3e84bfcc5efb038724a7fd + depends: + - __osx >=11.0 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 405089 + timestamp: 1714723101397 diff --git a/examples/magic.lock b/examples/magic.lock new file mode 100644 index 0000000000..4d3b044d38 --- /dev/null +++ b/examples/magic.lock @@ -0,0 +1,9153 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py311h2dc5d0c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hfdbb021_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py311hf29c0ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py311h2dc5d0c_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.11release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py311h2dc5d0c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py311h459d7ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py311h64a7726_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py311h7db5c69_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py311h49e9ac3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py311hfdbb021_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py311h38be061_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py311h4854187_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py311h9e33e62_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.11-h9e4cc4f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py311h9ecbd09_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-5_cp311.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py311h9ecbd09_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py311h7deb3e3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py311h9e33e62_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py311h182c674_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py311h9ecbd09_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py311h9e33e62_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py311h9ecbd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py311hbc35293_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py311h58d527c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py311h89d996e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py311h14e8bb7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py311ha09ea12_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.11release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py311h58d527c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py311hcd402e7_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py311h69ead2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py311h848c333_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py311hb2a0dd2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py311h89d996e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py311hfecb2dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py311ha6d2531_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py311h0ca61a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.11.11-h1683364_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py311h5487e9b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.11-5_cp311.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py311ha879c10_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py311h826da9f_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py311h0ca61a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py311h5e37e04_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py311h5487e9b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py311ha879c10_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py311h0ca61a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py311ha879c10_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py311hd5293d8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py311h4921393_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py311h3f08180_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py311h3a79f62_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py311hae2e1ce_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py311h4921393_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.11release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py311h30e7462_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py311heffc1b2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py311h7125741_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py311h9cb3ce9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py311h3894ae9_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py311h6885ffc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py311ha1ab1f8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py311he04fa90_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py311h3ff9189_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py311hae2e1ce_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.11-hc22306f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py311h460d6c5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.11-5_cp311.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py311h460d6c5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py311h730b646_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py311h481aa64_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py311h82b0fb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py311hae2e1ce_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py311h3ff9189_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py311h917b07b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py311ha60cc69_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda +packages: +- kind: conda + name: _libgcc_mutex + version: '0.1' + build: conda_forge + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + size: 2562 + timestamp: 1578324546067 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23621 + timestamp: 1650670423406 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 + md5: 6168d71addc746e8f2b8d57dfd2edcea + depends: + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23712 + timestamp: 1650670790230 +- kind: conda + name: aiohappyeyeballs + version: 2.4.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + sha256: 95d4713e49ea92ae50cf42393683ede706b7875af5f7cb14c253438180afa732 + md5: 296b403617bafa89df4971567af79013 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 19351 + timestamp: 1733332029649 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py311h2dc5d0c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py311h2dc5d0c_0.conda + sha256: 387a234321dd956e6b18aa338aae28a42fa804cb121292b5ab767410a92a5dca + md5: 7ddc4f7d7120a103af3e06cf7f7e7fb1 + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 921209 + timestamp: 1733839979846 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py311h4921393_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py311h4921393_0.conda + sha256: 27867a0d1f300689560328cda025eff7d41d0d8496a9a01631aaa930c6646d3d + md5: 6fa633c40fb67bf8f957e442a6acaaab + depends: + - __osx >=11.0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 880378 + timestamp: 1733839047160 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py311h58d527c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py311h58d527c_0.conda + sha256: ec2384157e169b6ad1e470a4855ed987bf2f19dac69a9156785f19bbb114d33b + md5: 899276b5c5a33aa77fdb738be26f83ed + depends: + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 915134 + timestamp: 1733838911893 +- kind: conda + name: aiosignal + version: 1.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + sha256: 7de8ced1918bbdadecf8e1c1c68237fe5709c097bd9e0d254f4cad118f4345d0 + md5: 1a3981115a398535dbe3f6d5faae3d36 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 13229 + timestamp: 1734342253061 +- kind: conda + name: annotated-types + version: 0.7.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- kind: conda + name: anyio + version: 4.7.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + sha256: 687537ee3af30f8784986bf40cac30e88138770b16e51ca9850c9c23c09aeba1 + md5: c88107912954a983c2caf25f7fd55158 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.9 + - sniffio >=1.1 + - typing_extensions >=4.5 + constrains: + - trio >=0.26.1 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 112730 + timestamp: 1733532678437 +- kind: conda + name: attrs + version: 24.3.0 + build: pyh71513ae_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + sha256: 750186af694a7130eaf7119fbb56db0d2326d8995ad5b8eae23c622b85fea29a + md5: 356927ace43302bf6f5926e2a58dae6a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 56354 + timestamp: 1734348889193 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h2cb9fb3_15 + build_number: 15 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + sha256: 4ce859dc9ff128bf5515604c43f33fb511386022fc9765ca077990f2a3f23df5 + md5: e524686ace966acefb5b8cbc6e8b3daa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 111854 + timestamp: 1734021745104 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h8bc59a9_15 + build_number: 15 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + sha256: 0e41e56b662e76e024182adebcd91d09a4d38a83b35217c84e4967354dfff9a2 + md5: f688b8893c20ad9477a19e7ce614014a + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + license: Apache-2.0 + license_family: Apache + size: 92507 + timestamp: 1734021831330 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: hb921021_15 + build_number: 15 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + sha256: 537006ad6d5097c134494166a6a1dc1451d5d050878d7b82cef498bfda40ba8a + md5: c79d50f64cffa5ad51ecc1a81057962f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 107614 + timestamp: 1734021692519 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h1a47875_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + sha256: 095ac824ea9303eff67e04090ae531d9eb33d2bf8f82eaade39b839c421e16e8 + md5: 55a8561fdbbbd34f50f57d9be12ed084 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 47601 + timestamp: 1733991564405 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h740c5af_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + sha256: c5c7961d48ca7320ed3560c036f7aa5244df4b85d9657f70aacc5faba3e1509a + md5: 57ed2c445d7ef01d121b9bcea0522913 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 50036 + timestamp: 1733991581303 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: hc8a0bd2_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + sha256: 1f44be36e1daa17b4b081debb8aee492d13571084f38b503ad13e869fef24fe4 + md5: 8b0ce61384e5a33d2b301a64f3d22ac5 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 39925 + timestamp: 1733991649383 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a + md5: 145e5b4c9702ed279d7d68aaf096f77d + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221863 + timestamp: 1733975576886 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + sha256: 57288ec5df35781bea8fc6a8c9099cad6695b747784fc1b8862a0f9e5b3bf5ab + md5: fef806a0f6de853670c746bbece01966 + depends: + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 259031 + timestamp: 1733975520465 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 + md5: d7d4680337a14001b0e043e96529409b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 236574 + timestamp: 1733975453350 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h0f0193d_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + sha256: 3f05d19f68ef800f33d44ea2a4211003124076516c8469abc7d432236344df53 + md5: 3a1421d12435df5b4c412cc4c8fac64d + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19740 + timestamp: 1733991625201 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h4e1184b_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b + md5: 3f4c1197462a6df2be6dc8241828fe93 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19086 + timestamp: 1733991637424 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: hc8a0bd2_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + sha256: 47b2813f652ce7e64ac442f771b2a5f7d4af4ad0d07ff51f6075ea80ed2e3f09 + md5: a8b6c17732d14ed49d0e9b59c43186bc + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 18068 + timestamp: 1733991869211 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h54f970a_11 + build_number: 11 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d + md5: ba41238f8e653998d7d2f42e3a8db054 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 47078 + timestamp: 1734024749727 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h7959bf6_11 + build_number: 11 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + sha256: 10d7240c7db0c941fb1a59c4f8ea6689a434b03309ee7b766fa15a809c553c02 + md5: 9b3fb60fe57925a92f399bc3fc42eccf + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 54003 + timestamp: 1734024480949 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: hcbd8f92_11 + build_number: 11 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + sha256: 79aa363c71c891a27496c0498f8d498b2ddc87b3ccb3b6c9da5b50b05936ebb8 + md5: e0772c59af4243a9b2565baa5d79e5b6 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 55207 + timestamp: 1734024546663 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h3df160d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + sha256: 3a1d2d332945306be9d49e569b95e4cc172d825f10e88715513a172f28ebb59e + md5: 28f00aa7fd9556c4c461328cf146c20b + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 190586 + timestamp: 1734008442362 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h96aa502_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + sha256: 22e4737c8a885995b7c1ae1d79c1f6e78d489e16ec079615980fdde067aeaf76 + md5: 495c93a4f08b17deb3c04894512330e6 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 152983 + timestamp: 1734008451473 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: hefd7a92_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 + md5: 5ce4df662d32d3123ea8da15571b6f51 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 197731 + timestamp: 1734008380764 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h1a307af_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + sha256: 71f5bf891299f831dceaea12f926c393bf754569e5305387a88b77e1f94612d8 + md5: da8ab0f3eeac93449ec3d531ede92caa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 161889 + timestamp: 1734433686109 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h831e299_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + sha256: 5920009b1c6f9a2bc131a36725251894e4b4773fce29c4b1065d4213ae337abe + md5: 80dd9f0ddf935290d1dc00ec75ff3023 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 157864 + timestamp: 1734433578570 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: haba67d1_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + sha256: c0a1a2b0750225ac3dc07fd258c88c2be866bf8ac67ba3d50bb4ecec852ff8ee + md5: 4c5ff4134e76426a75b8c548984fa933 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 135729 + timestamp: 1734433832730 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h11f4f37_12 + build_number: 12 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + sha256: 512d3969426152d9d5fd886e27b13706122dc3fa90eb08c37b0d51a33d7bb14a + md5: 96c3e0221fa2da97619ee82faa341a73 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 194672 + timestamp: 1734025626798 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h24f418c_12 + build_number: 12 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd + md5: c072045a6206f88015d02fcba1705ea1 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 134371 + timestamp: 1734025379525 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h5f50e26_12 + build_number: 12 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + sha256: ffeb9100cc8fd4093e1a6fdfd938bc4a396dd77480b7fb17aa42855a4d5e2c70 + md5: 031ca33115d4b1eeb43f435d6215778c + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 169516 + timestamp: 1734025167885 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h1be5864_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + sha256: 22966164d63808689fffd35945f57756c95337327e28099b5d77b29fc6a56ecc + md5: a37bba7acb62dd70492ee01eacca3b8f + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + license: Apache-2.0 + license_family: Apache + size: 97598 + timestamp: 1734146239038 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h2080895_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + sha256: 20bc2dd60e6518d9b8215c2b652ab5c52ee8a997d3b9a5f69e2dabd28cbf26b2 + md5: ae223efa63fbb4262a2d85c3ab3bc4f5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 117641 + timestamp: 1734146239779 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: hf454442_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + sha256: c2f205a7bf64c5f40eea373b3a0a7c363c9aa9246a13dd7f3d9c6a4434c4fe2d + md5: 947c82025693bebd557f782bb5d6b469 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 114156 + timestamp: 1734146123386 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + sha256: ede8e782467c87ac80ceb9c9af9e917d121b7d8b8c698186d18e3cecd36f2210 + md5: 53e798d720dd78b78847a7b2fdb05fc9 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 58621 + timestamp: 1733994421495 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + sha256: df586f42210af1134b1c88ff4c278c3cb6d6c807c84eac48860062464b28554d + md5: a5126a90e74ac739b00564a4c7ddcc36 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 56094 + timestamp: 1733994449690 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + sha256: de98343ce42d2e569b3380292d20f47bf39bda08aadabcbb8e650d3f38fd742f + md5: 22f72f8cd7ead211304ac17d337d96e0 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 49664 + timestamp: 1733994553014 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da + md5: 3bd35b0adab3d743f09e0252cc441d6b + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72154 + timestamp: 1733994384415 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + sha256: 1ed9a332d06ad595694907fad2d6d801082916c27cd5076096fda4061e6d24a8 + md5: 74e8c3e4df4ceae34aa2959df4b28101 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72762 + timestamp: 1733994347547 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + sha256: 215086d95e8ff1d3fcb0197ada116cc9d7db1fdae7573f5e810d20fa9215b47c + md5: e70e88a357a3749b67679c0788c5b08a + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 70186 + timestamp: 1733994496998 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h19a973c_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + sha256: 8269e6746eb3a5d15b732a3983888bf98dfc1f6594e95250fc8d16b43cfd5ff9 + md5: 95714136bef3e917bd5a2942d4682b20 + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 236249 + timestamp: 1734178020924 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h8a4e35f_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + sha256: 5ba9188e0cb4e3faff9bc96774febb040aa3b802aedba29d847e00e7b5eab84e + md5: d77a9e3d7ce15399903e92825fd651b5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 283154 + timestamp: 1734177845248 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: hd92328a_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + sha256: 094cd81f1e5ba713e9e7a272ee52b5dde3ccc4842ea90f19c0354a00bbdac3d9 + md5: 02b95564257d5c3db9c06beccf711f95 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 354703 + timestamp: 1734177883319 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: h849ce1a_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + sha256: 51b9e9df8cbab4a13a1b9d39d6ef5ed162aaa29c09a745810e00bbe92e1045c1 + md5: cda7747f4398be8d1fb37362815917a7 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2920625 + timestamp: 1734093552712 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: hc430e4a_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + sha256: 2dc09f6f9c49127b5f96e7535b64a9c521b944d76d8b7d03d48ae80257ac1cea + md5: aeefac461bea1f126653c1285cf5af08 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 3060561 + timestamp: 1734093737431 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: he0ff2e4_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + sha256: 535b970aaa13be45f8cab8205c59f044b17364111c41a227f061775a5c834e18 + md5: 0981ed87098b149bdb7d99a4a3fd0e58 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2826534 + timestamp: 1734094018287 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h1887c18_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 + md5: e0c3a906a41be769f0ae20ca3e31cfc0 + depends: + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 338650 + timestamp: 1728055589907 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h5cfcd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a + md5: 0a8838771cc2e985cd295e01ae83baf1 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 345117 + timestamp: 1728053909574 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: hd50102c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + sha256: f5b91329ed59ffc0be8747784c6e4cc7e56250c54032883a83bc11808ef6a87e + md5: f093a11dcf3cdcca010b20a818fcc6dc + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 294299 + timestamp: 1728054014060 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h113e628_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + sha256: 286b31616c191486626cb49e9ceb5920d29394b9e913c23adb7eb637629ba4de + md5: 73f73f60854f325a55f1d31459f2ab73 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 232351 + timestamp: 1728486729511 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h47b0b28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + sha256: 1c72423b9beba167d2f01b80dc204da77240a8266f1edb3d89510c852b300d69 + md5: 94e73a7877743a85c57091d8afab2348 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 217132 + timestamp: 1728488096615 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: hc602bab_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + sha256: bde446b916fff5150606f8ed3e6058ffc55a3aa72381e46f1ab346590b1ae40a + md5: d7b71593a937459f2d4b67e1a4727dc2 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 166907 + timestamp: 1728486882502 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h185ecfd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 + md5: 221e1e5ecb2643e113f32b3229d5ba33 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 502934 + timestamp: 1728580241002 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h3cf044e_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 + md5: 7eb66060455c7a47d9dcdbfa9f46579b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 549342 + timestamp: 1728578123088 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h7585a09_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + sha256: 08d52d130addc0fb55d5ba10d9fa483e39be25d69bac7f4c676c2c3069207590 + md5: 704238ef05d46144dae2e6b5853df8bc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 438636 + timestamp: 1728578216193 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h1b94036_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 + md5: 793b1080ab2d958980f137a8643cd6e8 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 140832 + timestamp: 1728565334900 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h736e048_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba + md5: 13de36be8de3ae3f05ba127631599213 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 149312 + timestamp: 1728563338704 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h9ca1f76_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + sha256: 77ab04e8fe5636a2de9c718f72a43645f7502cd208868c8a91ffba385547d585 + md5: 7a187cd7b1445afc80253bb186a607cc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 121278 + timestamp: 1728563418777 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: h37d6d07_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc + md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 260547 + timestamp: 1728730924071 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: ha633028_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 + md5: 7c1980f89dd41b097549782121a73490 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 287366 + timestamp: 1728729530295 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: hcdd55da_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + sha256: f48523f8aa0b5b80f45a92f0556b388dd96f44ac2dc2f44a01d08c1822eec97d + md5: c49fbc5233fcbaa86391162ff1adef38 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 196032 + timestamp: 1728729672889 +- kind: conda + name: backoff + version: 2.2.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 + md5: a38b801f2bcc12af80c2e02a9e4ce7d9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 18816 + timestamp: 1733771192649 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py311h3f08180_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py311h3f08180_2.conda + sha256: f507d65e740777a629ceacb062c768829ab76fde01446b191699a734521ecaad + md5: c8793a23206344faa25f4e0b5d0e7908 + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 339584 + timestamp: 1725268241628 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py311h89d996e_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py311h89d996e_2.conda + sha256: 8f299ccbda87e19f393bf9c01381415343650b06b9ef088dc2129ddcd48c05d4 + md5: c62b4c4d3eb1d13dfe16abbe648c28b7 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - libbrotlicommon 1.1.0 h86ecc28_2 + license: MIT + license_family: MIT + size: 356967 + timestamp: 1725268124383 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py311hfdbb021_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hfdbb021_2.conda + sha256: 949913bbd1f74d1af202d3e4bff2e0a4e792ec00271dc4dd08641d4221aa2e12 + md5: d21daab070d76490cb39a8f1d1729d79 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + size: 350367 + timestamp: 1725267768486 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h4bc722e_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 252783 + timestamp: 1720974456583 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h68df207_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + sha256: 2258b0b33e1cb3a9852d47557984abb6e7ea58e3d7f92706ec1f8e879290c4cb + md5: 56398c28220513b9ea13d7b450acfb20 + depends: + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 189884 + timestamp: 1720974504976 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h99b78c6_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 122909 + timestamp: 1720974522888 +- kind: conda + name: c-ares + version: 1.34.4 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f + md5: c1c999a38a4303b29d75c636eaa13cf9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 179496 + timestamp: 1734208291879 +- kind: conda + name: c-ares + version: 1.34.4 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + sha256: 1187a41d4bb2afe02cb18690682edc98d1e9f5e0ccda638d8704a75ea1875bbe + md5: 356da36f35d36dcba16e43f1589d4e39 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 215979 + timestamp: 1734208193181 +- kind: conda + name: c-ares + version: 1.34.4 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 + md5: e2775acf57efd5af15b8e3d1d74d72d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 206085 + timestamp: 1734208189009 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hbcca054_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + sha256: 1afd7274cbc9a334d6d0bc62fa760acc7afdaceb0b91a8df370ec01fd75dc7dd + md5: 720523eb0d6a9b0f6120c16b2aa4e7de + license: ISC + size: 157088 + timestamp: 1734208393264 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hcefe29a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + sha256: ad7b43211051332a5a4e788bb4619a2d0ecb5be73e0f76be17f733a87d7effd1 + md5: 83b4ad1e6dc14df5891f3fcfdeb44351 + license: ISC + size: 157096 + timestamp: 1734209301744 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hf0a4a13_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + sha256: 256be633fd0882ccc1a7a32bc278547e1703f85082c0789a87a603ee3ab8fb82 + md5: 7cb381a6783d91902638e4ed1ebd478e + license: ISC + size: 157091 + timestamp: 1734208344343 +- kind: conda + name: certifi + version: 2024.12.14 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + sha256: 048c16a9cbcb1fbad02083414d3bc7c1d0eea4b39aee6aa6bf8d1d5089ca8bad + md5: 6feb87357ecd66733be3279f16a8c400 + depends: + - python >=3.9 + license: ISC + size: 161642 + timestamp: 1734380604767 +- kind: conda + name: cffi + version: 1.17.1 + build: py311h14e8bb7_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py311h14e8bb7_0.conda + sha256: 3d220020c9782ebd4f23cd0a6148b419e4397590ee414e6e69b9be810c57d2ca + md5: 616d65d1eea809af7e2b5f7ea36350fc + depends: + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: MIT + license_family: MIT + size: 319122 + timestamp: 1725562148568 +- kind: conda + name: cffi + version: 1.17.1 + build: py311h3a79f62_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py311h3a79f62_0.conda + sha256: 253605b305cc4548b8f97eb7c2e146697e0c7672b099c4862ec5ca7e8e995307 + md5: a42272c5dbb6ffbc1a5af70f24c7b448 + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pycparser + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: MIT + license_family: MIT + size: 288211 + timestamp: 1725560745212 +- kind: conda + name: cffi + version: 1.17.1 + build: py311hf29c0ef_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py311hf29c0ef_0.conda + sha256: bc47aa39c8254e9e487b8bcd74cfa3b4a3de3648869eb1a0b89905986b668e35 + md5: 55553ecd5328336368db611f350b7039 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: MIT + license_family: MIT + size: 302115 + timestamp: 1725560701719 +- kind: conda + name: charset-normalizer + version: 3.4.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + sha256: 63022ee2c6a157a9f980250a66f54bdcdf5abee817348d0f9a74c2441a6fbf0e + md5: 6581a17bba6b948bb60130026404a9d6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 47533 + timestamp: 1733218182393 +- kind: conda + name: click + version: 8.1.7 + build: unix_pyh707e725_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + sha256: 1cd5fc6ccdd5141378e51252a7a3810b07fd5a7e6934a5b4a7eccba66566224b + md5: cb8e52f28f5e592598190c562e7b5bf1 + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 84513 + timestamp: 1733221925078 +- kind: conda + name: colorama + version: 0.4.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- kind: conda + name: datasets + version: 2.14.4 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + sha256: 7e09bd083a609138b780fcc4535924cb96814d2c908a36d4c64a2ba9ee3efe7f + md5: 3e087f072ce03c43a9b60522f5d0ca2f + depends: + - aiohttp + - dill >=0.3.0,<0.3.8 + - fsspec >=2021.11.1 + - huggingface_hub >=0.14.0,<1.0.0 + - importlib-metadata + - multiprocess + - numpy >=1.17 + - packaging + - pandas + - pyarrow >=8.0.0 + - python >=3.8.0 + - python-xxhash + - pyyaml >=5.1 + - requests >=2.19.0 + - tqdm >=4.62.1 + license: Apache-2.0 + license_family: Apache + size: 347303 + timestamp: 1691593908658 +- kind: conda + name: deprecated + version: 1.2.15 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + sha256: a20ebf2c9b02a6eb32412ceb5c4cffaae49417db7e75414a76417538293a9402 + md5: eaef2e94d5bd76f758545d172c1fda67 + depends: + - python >=3.9 + - wrapt <2,>=1.10 + license: MIT + license_family: MIT + size: 14297 + timestamp: 1733662697343 +- kind: conda + name: dill + version: 0.3.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + sha256: 4ff20c6be028be2825235631c45d9e4a75bca1de65f8840c02dfb28ea0137c45 + md5: 5e4f3466526c52bc9af2d2353a1460bd + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + size: 87553 + timestamp: 1690101185422 +- kind: conda + name: dnspython + version: 2.7.0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 + md5: 5fbd60d61d21b4bd2f9d7a48fe100418 + depends: + - python >=3.9,<4.0.0 + - sniffio + constrains: + - aioquic >=1.0.0 + - wmi >=1.5.1 + - httpx >=0.26.0 + - trio >=0.23 + - cryptography >=43 + - httpcore >=1.0.0 + - idna >=3.7 + - h2 >=4.1.0 + license: ISC + license_family: OTHER + size: 172172 + timestamp: 1733256829961 +- kind: conda + name: email-validator + version: 2.2.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + sha256: b91a19eb78edfc2dbb36de9a67f74ee2416f1b5273dd7327abe53f2dbf864736 + md5: da16dd3b0b71339060cd44cb7110ddf9 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.9 + license: Unlicense + size: 44401 + timestamp: 1733300827551 +- kind: conda + name: email_validator + version: 2.2.0 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + sha256: e0d0fdf587aa0ed0ff08b2bce3ab355f46687b87b0775bfba01cc80a859ee6a2 + md5: 0794f8807ff2c6f020422cacb1bd7bfa + depends: + - email-validator >=2.2.0,<2.2.1.0a0 + license: Unlicense + size: 6552 + timestamp: 1733300828176 +- kind: conda + name: exceptiongroup + version: 1.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 + md5: a16662747cdeb9abbac74d0057cc976e + depends: + - python >=3.9 + license: MIT and PSF-2.0 + size: 20486 + timestamp: 1733208916977 +- kind: conda + name: fastapi + version: 0.115.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + sha256: d7826d537c667093c9de96411a09585a8d620c84a830a0195e58e9a0df45f018 + md5: 1b1e0c97830cdf75f1f371bd467ab657 + depends: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.5 + - httpx >=0.23.0 + - jinja2 >=2.11.2 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python >=3.9 + - python-multipart >=0.0.7 + - starlette >=0.40.0,<0.42.0 + - typing_extensions >=4.8.0 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + size: 73084 + timestamp: 1733362427885 +- kind: conda + name: fastapi-cli + version: 0.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 + md5: d960e0ea9e1c561aa928f6c4439f04c7 + depends: + - python >=3.9 + - rich-toolkit >=0.11.1 + - typer >=0.12.3 + - uvicorn-standard >=0.15.0 + license: MIT + license_family: MIT + size: 15546 + timestamp: 1734302408607 +- kind: conda + name: filelock + version: 3.16.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + sha256: 18dca6e2194732df7ebf824abaefe999e4765ebe8e8a061269406ab88fc418b9 + md5: d692e9ba6f92dc51484bf3477e36ce7c + depends: + - python >=3.9 + license: Unlicense + size: 17441 + timestamp: 1733240909987 +- kind: conda + name: freetype + version: 2.12.1 + build: h267a509_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + sha256: b2e3c449ec9d907dd4656cb0dc93e140f447175b125a3824b31368b06c666bb6 + md5: 9ae35c3d96db2c94ce0cef86efdfa2cb + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 634972 + timestamp: 1694615932610 +- kind: conda + name: freetype + version: 2.12.1 + build: hadb7bae_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 + md5: e6085e516a3e304ce41a8ee08b9b89ad + depends: + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 596430 + timestamp: 1694616332835 +- kind: conda + name: freetype + version: 2.12.1 + build: hf0a5ef3_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + sha256: 7af93030f4407f076dce181062360efac2cd54dce863b5d7765287a6f5382537 + md5: a5ab74c5bd158c3d5532b66d8d83d907 + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 642092 + timestamp: 1694617858496 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py311h9ecbd09_0.conda + sha256: 5bde4e41dd1bdf42488f1b86039f38914e87f4a6b46c15224c217651f964de8b + md5: 75424a18fb275a18b288c099b869c3bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 60988 + timestamp: 1729699558841 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py311ha879c10_0.conda + sha256: 1b31825a689aa35a07ce4a7f1994668f2c2344cfdb7efdb05e820d8fc1ca6949 + md5: ea2f2c07a1173d0b1823fe4471203d6d + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 60923 + timestamp: 1729699681174 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py311hae2e1ce_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py311hae2e1ce_0.conda + sha256: 3df51bbf74052c5d29a33cf8c8c57302699937f883e0e4e9e506c7e0b09e45a5 + md5: 7f28e6daf0b4963be1061291cbe10bfb + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 54023 + timestamp: 1729699703032 +- kind: conda + name: fsspec + version: 2024.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + sha256: 790a50b4f94042951518f911a914a886a837c926094c6a14ed1d9d03ce336807 + md5: 906fe13095e734cb413b57a49116cdc8 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 134726 + timestamp: 1733493445080 +- kind: conda + name: gflags + version: 2.2.2 + build: h5888daf_1005 + build_number: 1005 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 119654 + timestamp: 1726600001928 +- kind: conda + name: gflags + version: 2.2.2 + build: h5ad3122_1005 + build_number: 1005 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + sha256: 28fe6b40b20454106d5e4ef6947cf298c13cc72a46347bbc49b563cd3a463bfa + md5: 4ff634d515abbf664774b5e1168a9744 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 106638 + timestamp: 1726599967617 +- kind: conda + name: gflags + version: 2.2.2 + build: hf9b8971_1005 + build_number: 1005 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + size: 82090 + timestamp: 1726600145480 +- kind: conda + name: glog + version: 0.7.1 + build: h468a4a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 + md5: 08940a32c6ced3703d1412dd37df4f62 + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 145811 + timestamp: 1718284208668 +- kind: conda + name: glog + version: 0.7.1 + build: hbabe93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 143452 + timestamp: 1718284177264 +- kind: conda + name: glog + version: 0.7.1 + build: heb240a5_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + size: 112215 + timestamp: 1718284365403 +- kind: conda + name: googleapis-common-protos + version: 1.66.0 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + sha256: d8d19575a827f2c62500949b9536efdd6b5406c9f546a73b6a87ac90b03a5875 + md5: 4861e30ff0cd566ea6fb4593e3b7c22a + depends: + - protobuf >=3.20.2,<6.0.0.dev0,!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 116522 + timestamp: 1731459019854 +- kind: conda + name: h11 + version: 0.14.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 + md5: 7ee49e89531c0dcbba9466f6d115d585 + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 51846 + timestamp: 1733327599467 +- kind: conda + name: h2 + version: 4.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + sha256: 843ddad410c370672a8250470697027618f104153612439076d4d7b91eeb7b5c + md5: 825927dc7b0f287ef8d4d0011bb113b1 + depends: + - hpack >=4.0,<5 + - hyperframe >=6.0,<7 + - python >=3.9 + license: MIT + license_family: MIT + size: 52000 + timestamp: 1733298867359 +- kind: conda + name: hpack + version: 4.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + sha256: ec89b7e5b8aa2f0219f666084446e1fb7b54545861e9caa892acb24d125761b5 + md5: 2aa5ff7fa34a81b9196532c84c10d865 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 29412 + timestamp: 1733299296857 +- kind: conda + name: httpcore + version: 1.0.7 + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df + md5: 2ca8e6dbc86525c8b95e3c0ffa26442e + depends: + - python >=3.8 + - h11 >=0.13,<0.15 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=3.0,<5.0 + - certifi + license: BSD-3-Clause + license_family: BSD + size: 48959 + timestamp: 1731707562362 +- kind: conda + name: httptools + version: 0.6.4 + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py311h917b07b_0.conda + sha256: 47af7c9e41ea0327f12757527cea28c430ef84aade923d81cc397ebb2bf9eb28 + md5: 4aca39fe9eb4224026c907e1aa8156fb + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: MIT + license_family: MIT + size: 84562 + timestamp: 1732707884099 +- kind: conda + name: httptools + version: 0.6.4 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py311h9ecbd09_0.conda + sha256: 1775083ed07111778559e9a0b47033c13cbe6f1c489eaceff204f6cf7a9e02da + md5: c16a94f3d0c6a2a495b3071cff3f598d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: MIT + license_family: MIT + size: 99955 + timestamp: 1732707791797 +- kind: conda + name: httptools + version: 0.6.4 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py311ha879c10_0.conda + sha256: f33cd0bb6db6bf8ad44bb908a0befe5564921a570c4c39784c518be81ddd6ab0 + md5: fd2dfd6afe96a5843af75eb4f085ed56 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: MIT + license_family: MIT + size: 98813 + timestamp: 1732707937311 +- kind: conda + name: httpx + version: 0.28.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- kind: conda + name: huggingface_hub + version: 0.26.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + sha256: 0c75532d914a04c73222be298ed2c6868739dd475b1b1a9137c52abe79873952 + md5: 73937038e21117fe401f8ea64fbaeacc + depends: + - filelock + - fsspec >=2023.5.0 + - packaging >=20.9 + - python >=3.9 + - pyyaml >=5.1 + - requests + - tqdm >=4.42.1 + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + size: 275466 + timestamp: 1733852454004 +- kind: conda + name: hyperframe + version: 6.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + sha256: e91c6ef09d076e1d9a02819cd00fa7ee18ecf30cdd667605c853980216584d1b + md5: 566e75c90c1d0c8c459eb0ad9833dc7a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17239 + timestamp: 1733298862681 +- kind: conda + name: icu + version: '75.1' + build: hf9b3779_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 + md5: 268203e8b983fddb6412b36f2024e75c + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12282786 + timestamp: 1720853454991 +- kind: conda + name: icu + version: '75.1' + build: hfee45f7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 11857802 + timestamp: 1720853997952 +- kind: conda + name: idna + version: '3.10' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 + md5: 39a4f67be3286c86d696df570b1201b7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 49765 + timestamp: 1733211921194 +- kind: conda + name: importlib-metadata + version: 8.5.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 + md5: 315607a3030ad5d5227e76e0733798ff + depends: + - python >=3.9 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + size: 28623 + timestamp: 1733223207185 +- kind: conda + name: jinja2 + version: 3.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + sha256: 85a7169c078b8065bd9d121b0e7b99c8b88c42a411314b6ae5fcd81c48c4710a + md5: 08cce3151bde4ecad7885bd9fb647532 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110963 + timestamp: 1733217424408 +- kind: conda + name: jupyter_client + version: 8.6.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a + md5: 4ebae00eae9705b0c3d6d1018a81d047 + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106342 + timestamp: 1733441040958 +- kind: conda + name: jupyter_core + version: 5.7.2 + build: pyh31011fe_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd + md5: 0a2980dada0dd7fd0998f0342308b1b1 + depends: + - __unix + - platformdirs >=2.5 + - python >=3.8 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 57671 + timestamp: 1727163547058 +- kind: conda + name: keyutils + version: 1.6.1 + build: h166bdaf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + sha256: 150c05a6e538610ca7c43beb3a40d65c90537497a4f6a5f4d15ec0451b6f5ebb + md5: 30186d27e2c9fa62b45fb1476b7200e3 + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 117831 + timestamp: 1646151697040 +- kind: conda + name: keyutils + version: 1.6.1 + build: h4e544f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + sha256: 6d4233d97a9b38acbb26e1268bcf8c10a8e79c2aed7e5a385ec3769967e3e65b + md5: 1f24853e59c68892452ef94ddd8afd4b + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 112327 + timestamp: 1646166857935 +- kind: conda + name: krb5 + version: 1.21.3 + build: h237132a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- kind: conda + name: krb5 + version: 1.21.3 + build: h50a48e9_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 + md5: 29c10432a2ca1472b53f299ffb2ffa37 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1474620 + timestamp: 1719463205834 +- kind: conda + name: krb5 + version: 1.21.3 + build: h659f571_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1370023 + timestamp: 1719463201255 +- kind: conda + name: lcms2 + version: '2.16' + build: h922389a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + sha256: be4847b1014d3cbbc524a53bdbf66182f86125775020563e11d914c8468dd97d + md5: ffdd8267a04c515e7ce69c727b051414 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 296219 + timestamp: 1701647961116 +- kind: conda + name: lcms2 + version: '2.16' + build: ha0e7c42_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + sha256: 151e0c84feb7e0747fabcc85006b8973b22f5abbc3af76a9add0b0ef0320ebe4 + md5: 66f6c134e76fe13cce8a9ea5814b5dd5 + depends: + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 211959 + timestamp: 1701647962657 +- kind: conda + name: lcms2 + version: '2.16' + build: hb7c19ff_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 + md5: 51bb7010fc86f70eee639b4bb7a894f5 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 245247 + timestamp: 1701647787198 +- kind: conda + name: ld_impl_linux-64 + version: '2.43' + build: h712a8e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + sha256: 7c91cea91b13f4314d125d1bedb9d03a29ebbd5080ccdea70260363424646dbe + md5: 048b02e3962f066da18efe3a21b77672 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 669211 + timestamp: 1729655358674 +- kind: conda + name: ld_impl_linux-aarch64 + version: '2.43' + build: h80caac9_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + sha256: 80ec7e8f006196808fac5bd4b3773a652847f97bbf08044cd87731424ac64f8b + md5: fcbde5ea19d55468953bf588770c0501 + constrains: + - binutils_impl_linux-aarch64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 698245 + timestamp: 1729655345825 +- kind: conda + name: lerc + version: 4.0.0 + build: h27087fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + sha256: cb55f36dcd898203927133280ae1dc643368af041a48bcf7c026acb7c47b0c12 + md5: 76bbff344f0134279f225174e9064c8f + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 281798 + timestamp: 1657977462600 +- kind: conda + name: lerc + version: 4.0.0 + build: h4de3ea5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + sha256: 2d09ef9b7796d83364957e420b41c32d94e628c3f0520b61c332518a7b5cd586 + md5: 1a0ffc65e03ce81559dbcb0695ad1476 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 262096 + timestamp: 1657978241894 +- kind: conda + name: lerc + version: 4.0.0 + build: h9a09cb3_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + sha256: 6f068bb53dfb6147d3147d981bb851bb5477e769407ad4e6a68edf482fdcb958 + md5: de462d5aacda3b30721b512c5da4e742 + depends: + - libcxx >=13.0.1 + license: Apache-2.0 + license_family: Apache + size: 215721 + timestamp: 1657977558796 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + sha256: 8f91429091183c26950f1e7ffa730e8632f0627ba35d2fccd71df31628c9b4e5 + md5: e1f604644fe8d78e22660e2fec6756bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1310521 + timestamp: 1727295454064 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + sha256: 590e47dce38031a8893e70491f3b71e214de7781cab53b6f017aa6f6841cb076 + md5: 6fe6b3694c4792a8e26755d3b06f0b80 + depends: + - libgcc >=13 + - libstdcxx >=13 + constrains: + - abseil-cpp =20240722.0 + - libabseil-static =20240722.0=cxx17* + license: Apache-2.0 + license_family: Apache + size: 1328502 + timestamp: 1727295490806 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_hf9b8971_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + sha256: 90bf08a75506dfcf28a70977da8ab050bcf594cd02abd3a9d84a22c9e8161724 + md5: 706da5e791c569a7b9814877098a6a0a + depends: + - __osx >=11.0 + - libcxx >=17 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1179072 + timestamp: 1727295571173 +- kind: conda + name: libarrow + version: 18.1.0 + build: h1b535d6_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + sha256: 087b579aebf351ca41c54214121d86a15a41c92051cbd432d6f3a3f58a8c31b0 + md5: 4c0ad68efba1113ac5833975c67b565d + depends: + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8040629 + timestamp: 1733810319239 +- kind: conda + name: libarrow + version: 18.1.0 + build: h44a453e_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + sha256: abf17e99b03356a9d6248e965826c1352ff01b00d3a62cc51393bb0744d72803 + md5: 2cf6d608d6e66506f69797d5c6944c35 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8786061 + timestamp: 1733810643966 +- kind: conda + name: libarrow + version: 18.1.0 + build: h4a2f8bd_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + sha256: 9ed3ea1bc15005c0df187268ef91407afaa908cf82f36f5acbbf50ac24d7f806 + md5: 835cdd84195b84dc34d128bd5d3580b9 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libcxx >=18 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 5494797 + timestamp: 1733808145854 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + sha256: fdb70e2499e59b730084ecd53008b361a6f6090b5fb49624feda06b7e84c7b8c + md5: c50907eefe2ae22d826e7cb2e4d712f5 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 578091 + timestamp: 1733810378092 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + sha256: a32fa1d71415afc02b5cf3cd4c0a6ec0af9e749308829cc65ff79689222ce479 + md5: 143f9288b64759a6427563f058c62f2b + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 611745 + timestamp: 1733810698469 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + sha256: e1cae46409927470439ef9ae93ed09b3493d0579501ca9ebfa79ded212ee98d8 + md5: 97fc01254714e1572624baefdd7cc898 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + license: Apache-2.0 + license_family: APACHE + size: 483713 + timestamp: 1733808246880 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + sha256: 2a08f5a1017ff660c37ae0c24343a119cb2511c6edd69e23d0a5090a0967ea35 + md5: bb1548ad011c4f9107fcc4cc548473bf + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libparquet 18.1.0 hfc78867_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 559673 + timestamp: 1733810461646 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + sha256: 74eeb178070002842d3ed721769399320e3a68a0843319eaf899a092a31def26 + md5: 20ca46a6bc714a6ab189d5b3f46e66d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libparquet 18.1.0 h081d1f1_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 586627 + timestamp: 1733810842604 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + sha256: 6eba942ce926419f74e6e0a7c3994a7d78ab6be47115e6bb70e02136554736be + md5: 0774276be6659aaa0007f1b0f6ee19b0 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libparquet 18.1.0 h636d7b7_6_cpu + license: Apache-2.0 + license_family: APACHE + size: 489948 + timestamp: 1733809328231 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ee7192_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + sha256: bda6728db019dd0c409b1996ad9ef6ab0bcee3a94dc66a8045e8c1049c566055 + md5: aa313b3168caf98d00b3753f5ba27650 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libarrow-dataset 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 519989 + timestamp: 1733810903274 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ffb4b1_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + sha256: 9f78c55c5d7122e588a6f226cbf7e909c479d66ed18edc633d68324323d386b9 + md5: 5db2e6832397b8ca70a6f7b00e0c3629 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libarrow-dataset 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 515928 + timestamp: 1733810503359 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h86344ea_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + sha256: bafd9ca59ebb5ad34b77aff316ef7b59c5fb1eb8a7b6a15de8dcbdf3ce37556d + md5: c1c162f5bf569cff8bed6def705a899f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libarrow-dataset 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 451623 + timestamp: 1733809487176 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + sha256: 30bd658682b124243f8e52d8edf8a19e7be1bc31e4fe4baec30a64002dc8cd0c + md5: ac52800af2e0c0e7dac770b435ce768a + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16393 + timestamp: 1734432564346 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: df6d8ee34d45cf35609ecdd55c1ff03e32e0cd87ae41ebe4ef3747a8e09ead4d + md5: 8d900b7079a00969d70305e9aad550b7 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16477 + timestamp: 1734432576699 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + sha256: 597f9c3779caa979c8c6abbb3ba8c7191b84e1a910d6b0d10e5faf35284c450c + md5: 21be102c9ae80a67ba7de23b129aa7f6 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16714 + timestamp: 1734433054681 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 + md5: 3ee026955c688f551a9999840cff4c67 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 68982 + timestamp: 1725267774142 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 + md5: 41b599ed2b02abcfdd84302bff174b23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 68851 + timestamp: 1725267660471 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + sha256: 839dacb741bdbb25e58f42088a2001b649f4f12195aeb700b5ddfca3267749e5 + md5: d0bf1dff146b799b319ea0434b93f779 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 68426 + timestamp: 1725267943211 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c + md5: e64d0f3b59c7c4047446b97a8624a72d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 31708 + timestamp: 1725267783442 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 32696 + timestamp: 1725267669305 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + sha256: 6c6862eb274f21a7c0b60e5345467a12e6dda8b9af4438c66d496a2c1a538264 + md5: 55e66e68ce55523a6811633dd1ac74e2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 28378 + timestamp: 1725267980316 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c + md5: 0e9bd365480c72b25c71a448257b537d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 290230 + timestamp: 1725267792697 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 + md5: 06f70867945ea6a84d35836af780f1de + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 281750 + timestamp: 1725267679782 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + sha256: eeb1eb0d58b9d02bc1b98dc0a058f104ab168eb2f7d1c7bfa0570a12cfcdb7b7 + md5: 4f3a434504c67b2c42565c0b85c1885c + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 279644 + timestamp: 1725268003553 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + sha256: 9c74e536c9bc868e356ffd43f81c2cb398aec84b40fcadc312315b164a5500ee + md5: ebcc5f37a435aa3c19640533c82f8d76 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16336 + timestamp: 1734432570482 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: 521e78be0c4170f229c43e1a6c94337a72db3ebcbe6e5960f8413aa438dcb8f9 + md5: d77f943ae4083f3aeddca698f2d28262 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16398 + timestamp: 1734432580937 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + sha256: 27a29ef6b2fd2179bc3a0bb9db351f078ba140ca10485dca147c399639f84c93 + md5: a0e9980fe12d42f6d0c0ec009f67e948 + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16628 + timestamp: 1734433061517 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea + md5: 268ee639c17ada0002fb04dd21816cc2 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 18669 + timestamp: 1633683724891 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 20440 + timestamp: 1633683576494 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: hbdafb3b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + size: 18765 + timestamp: 1633683992603 +- kind: conda + name: libcurl + version: 8.11.1 + build: h332b0f4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + sha256: 3cd4075b2a7b5562e46c8ec626f6f9ca57aeecaa94ff7df57eca26daa94c9906 + md5: 2b3e0081006dc21e8bf53a91c83a055c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 423011 + timestamp: 1733999897624 +- kind: conda + name: libcurl + version: 8.11.1 + build: h6702fde_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + sha256: 9fc65d21a58f4aad1bc39dfb94a178893aeb035850c5cf0ed9736674279f390b + md5: 7dec1cd271c403d1636bda5aa388a55d + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 440737 + timestamp: 1733999835504 +- kind: conda + name: libcurl + version: 8.11.1 + build: h73640d1_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + sha256: f47c35938144c23278987c7d12096f6a42d7c850ffc277222b032073412383b6 + md5: 46d7524cabfdd199bffe63f8f19a552b + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 385098 + timestamp: 1734000160270 +- kind: conda + name: libcxx + version: 19.1.5 + build: ha82da77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + sha256: 7918cc0bb7a6554cdd3eee634c3dc414a1ab8ec49faeca1567367bb92118f9d7 + md5: 3c7be0df28ccda1d193ea6de56dcb5ff + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 519819 + timestamp: 1733291654212 +- kind: conda + name: libdeflate + version: '1.23' + build: h4ddbbb0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + sha256: 511d801626d02f4247a04fff957cc6e9ec4cc7e8622bd9acd076bcdc5de5fe66 + md5: 8dfae1d2e74767e9ce36d5fa0d8605db + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + size: 72255 + timestamp: 1734373823254 +- kind: conda + name: libdeflate + version: '1.23' + build: h5e3c512_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + sha256: 959419d87cd2b789a9055db95704c614f31aeb70bef7949fa2f734122a3a2863 + md5: 7e7ca2607b11b180120cefc2354fc0cb + depends: + - libgcc >=13 + license: MIT + size: 69862 + timestamp: 1734373858306 +- kind: conda + name: libdeflate + version: '1.23' + build: hec38601_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + sha256: 887c02deaed6d583459eba6367023e36d8761085b2f7126e389424f57155da53 + md5: 1d8b9588be14e71df38c525767a1ac30 + depends: + - __osx >=11.0 + license: MIT + size: 54132 + timestamp: 1734373971372 +- kind: conda + name: libedit + version: 3.1.20191231 + build: hc8eb9b7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca + md5: 30e4362988a2623e9eb34337b83e01f9 + depends: + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96607 + timestamp: 1597616630749 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: a57d37c236d8f7c886e01656f4949d9dcca131d2a0728609c6f7fa338b65f1cf + md5: 4d331e44109e3f0e19b4cb8f9b82f3e1 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 123878 + timestamp: 1597616541093 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: debc31fb2f07ba2b0363f90e455873670734082822926ba4a9556431ec0bf36d + md5: 29371161d77933a54fccf1bb66b96529 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 134104 + timestamp: 1597617110769 +- kind: conda + name: libev + version: '4.33' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + sha256: 973af77e297f1955dd1f69c2cbdc5ab9dfc88388a5576cd152cda178af0fd006 + md5: a9a13cb143bbaa477b1ebaefbe47a302 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 115123 + timestamp: 1702146237623 +- kind: conda + name: libev + version: '4.33' + build: h93a5062_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + size: 107458 + timestamp: 1702146414478 +- kind: conda + name: libev + version: '4.33' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 112766 + timestamp: 1702146165126 +- kind: conda + name: libevent + version: 2.1.12 + build: h2757513_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 368167 + timestamp: 1685726248899 +- kind: conda + name: libevent + version: 2.1.12 + build: h4ba1bb4_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + sha256: 01333cc7d6e6985dd5700b43660d90e9e58049182017fd24862088ecbe1458e4 + md5: 96ae6083cd1ac9f6bc81631ac835b317 + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 438992 + timestamp: 1685726046519 +- kind: conda + name: libevent + version: 2.1.12 + build: hf998b51_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 427426 + timestamp: 1685725977222 +- kind: conda + name: libexpat + version: 2.6.4 + build: h286801f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 + md5: 38d2656dd914feb0cab8c629370768bf + depends: + - __osx >=11.0 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 64693 + timestamp: 1730967175868 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 + md5: db833e03127376d461e1e13e76f09b6c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 73304 + timestamp: 1730967041968 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + sha256: f42e758009ba9db90d1fe7992bc3e60d0c52f71fb20923375d2c44ae69a5a2b3 + md5: f1b3fab36861b3ce945a13f0dfdfc688 + depends: + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 72345 + timestamp: 1730967203789 +- kind: conda + name: libffi + version: 3.4.2 + build: h3422bc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 + license: MIT + license_family: MIT + size: 39020 + timestamp: 1636488587153 +- kind: conda + name: libffi + version: 3.4.2 + build: h3557bc0_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + sha256: 7e9258a102480757fe3faeb225a3ca04dffd10fecd2a958c65cdb4cdf75f2c3c + md5: dddd85f4d52121fab0a8b099c5e06501 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 59450 + timestamp: 1636488255090 +- kind: conda + name: libffi + version: 3.4.2 + build: h7f98852_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 58292 + timestamp: 1636488182923 +- kind: conda + name: libgcc + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + sha256: 53eb8a79365e58849e7b1a068d31f4f9e718dc938d6f2c03e960345739a03569 + md5: 3cb76c3f10d3bc7f1105b2fc9db984df + depends: + - _libgcc_mutex 0.1 conda_forge + - _openmp_mutex >=4.5 + constrains: + - libgomp 14.2.0 h77fa898_1 + - libgcc-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 848745 + timestamp: 1729027721139 +- kind: conda + name: libgcc + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + sha256: 5d56757ccad208c79214395b00d006d8d18929a4ba49c47bd9460789a7620943 + md5: 511b511c5445e324066c3377481bcab8 + depends: + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==14.2.0=*_1 + - libgomp 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 535243 + timestamp: 1729089435134 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + sha256: 3a76969c80e9af8b6e7a55090088bc41da4cffcde9e2c71b17f44d37b7cb87f7 + md5: e39480b9ca41323497b05492a63bc35b + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54142 + timestamp: 1729027726517 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + sha256: 9b5cf168a6c7361cae869cb74b716766ee7c6d6b3f6172b32ba9bf91135efdc4 + md5: 0694c249c61469f2c0f7e2990782af21 + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54104 + timestamp: 1729089444587 +- kind: conda + name: libgfortran + version: 5.0.0 + build: 13_2_0_hd922786_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf + depends: + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- kind: conda + name: libgfortran + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + sha256: fc9e7f22a17faf74da904ebfc4d88699013d2992e55505e4aa0eb01770290977 + md5: f1fd30127802683586f768875127a987 + depends: + - libgfortran5 14.2.0 hd5240d6_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 53997 + timestamp: 1729027752995 +- kind: conda + name: libgfortran + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + sha256: cb66e411fa32a5c6040f4e5e2a63c00897aae4c3133a9c004c2e929ccf19575b + md5: 0294b92d2f47a240bebb1e3336b495f1 + depends: + - libgfortran5 14.2.0 hb6113d0_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729089471124 +- kind: conda + name: libgfortran5 + version: 13.2.0 + build: hf226fd6_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 5.0.0 13_2_0_*_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 997381 + timestamp: 1707330687590 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hb6113d0_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + sha256: a87ff46d19916403cbf68cf1d785bf56b4d1ab7b2552468d2ea775d70782493f + md5: fc068e11b10e18f184e027782baa12b6 + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1102158 + timestamp: 1729089452640 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hd5240d6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d + md5: 9822b874ea29af082e5d36098d25427d + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1462645 + timestamp: 1729027735353 +- kind: conda + name: libgomp + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + sha256: 1911c29975ec99b6b906904040c855772ccb265a1c79d5d75c8ceec4ed89cd63 + md5: cc3573974587f12dda90d96e3e55a702 + depends: + - _libgcc_mutex 0.1 conda_forge + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 460992 + timestamp: 1729027639220 +- kind: conda + name: libgomp + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + sha256: 5aa53874a5e57a00f2e0c2e2910684eb674429cd5fcb803619b226a73e89aedf + md5: 376f0e73abbda6d23c0cb749adc195ef + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 463521 + timestamp: 1729089357313 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h3888205_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + sha256: 36af2844ce8fafd477214d51117746144461132f76759a7d29963b4583b577be + md5: a40b948bf4eabcc1ce708c40ffd7c06d + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1248560 + timestamp: 1733512309504 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h804f50b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + sha256: 126856add750013390dff664a3c3cd0f6f0cbbc683b0025a7ce9d1618968bc70 + md5: 3d96df4d6b1c88455e05b94ce8a14a53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1249557 + timestamp: 1733512191906 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h8d8be31_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + sha256: 722e49dbdc4486105d9f5b79a7ba4f9064602fe20c4015e97684c898ab8d3386 + md5: d7ab9e0eb7d55eac4943913073de61d7 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 876210 + timestamp: 1733512539476 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h0121fbd_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + sha256: d1b53d17df38b52a4bc6d1fe6af0e611d6480ce10b0af570c84bd38c8aa83b91 + md5: 877a5ec0431a5af83bf0cd0522bfe661 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h804f50b_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 782108 + timestamp: 1733512329104 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h7081f7f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + sha256: 609df2cf376ba66460f40143f835fc567cae4458df80705587cd2efd59c09bf1 + md5: 28f5ab5cf95170dfacd05d2bb301e573 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=18 + - libgoogle-cloud 2.32.0 h8d8be31_0 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 526895 + timestamp: 1733513644846 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: hb9b2b65_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + sha256: e120e7b6c9c9d25baa8ae903106babdd3c969523ae25278a615ed9de4bd0fc35 + md5: 925ab0ca33baca4fcfee585cecb94169 + depends: + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h3888205_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 737964 + timestamp: 1733512457785 +- kind: conda + name: libgrpc + version: 1.67.1 + build: h36c5df4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + sha256: 1f6673d9d866048c9cf28fd56e6874ffc7e2c53c47d7071cb367d5fc2dde16a7 + md5: b946137e362e98a55a77fdf0b20a7739 + depends: + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7131846 + timestamp: 1730236305327 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc2c308b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + sha256: 870550c1faf524e9a695262cd4c31441b18ad542f16893bd3c5dbc93106705f7 + md5: 4606a4647bfe857e3cfe21ca12ac3afb + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7362336 + timestamp: 1730236333879 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc70892a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + sha256: d2393fcd3c3584e5d58da4122f48bcf297567d2f6f14b3d1fcbd34fdd5040694 + md5: 624e27571fde34f8acc2afec840ac435 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 4882208 + timestamp: 1730236299095 +- kind: conda + name: libiconv + version: '1.17' + build: h0d3ecfb_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 + md5: 69bda57310071cf6d2b86caf11573d2d + license: LGPL-2.1-only + size: 676469 + timestamp: 1702682458114 +- kind: conda + name: libiconv + version: '1.17' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + sha256: a30e09d089cb75a0d5b8e5c354694c1317da98261185ed65aa3793e741060614 + md5: 9a8eb13f14de7d761555a98712e6df65 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705787 + timestamp: 1702684557134 +- kind: conda + name: libiconv + version: '1.17' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 + md5: d66573916ffcf376178462f1b61c941e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705775 + timestamp: 1702682170569 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + sha256: 675bc1f2a8581cd34a86c412663ec29c5f90c1d9f8d11866aa1ade5cdbdf8429 + md5: ed24e702928be089d9ba3f05618515c6 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 647126 + timestamp: 1694475003570 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hb547adb_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + sha256: a42054eaa38e84fc1e5ab443facac4bbc9d1b6b6f23f54b7bf4f1eb687e1d993 + md5: 3ff1e053dc3a2b8e36b9bfa4256a58d1 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 547541 + timestamp: 1694475104253 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f + md5: ea25936bb4080d843790b586850f82b8 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 618575 + timestamp: 1694474974816 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + sha256: b76458c36331376911e0f98fa68109e02f4d5e5ebfffa79587ac69cef748bba1 + md5: 3792604c43695d6a273bc5faaac47d48 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16338 + timestamp: 1734432576650 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + sha256: a42bd01498efe2ccf6d08d56ac3cbd3ceab79e06699ff5aac3da8e45a66738f7 + md5: a5d4e18876393633da62fd8492c00156 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16403 + timestamp: 1734432585123 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + sha256: dd6d9a21e672aee4332f019c8229ce70cf5eaf6c2f4cbd1443b105fb66c00dc5 + md5: cebad79038a75cfd28fa90d147a2d34d + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16624 + timestamp: 1734433068120 +- kind: conda + name: liblzma + version: 5.6.3 + build: h39f12f2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 + md5: b2553114a7f5e20ccd02378a77d836aa + depends: + - __osx >=11.0 + license: 0BSD + size: 99129 + timestamp: 1733407496073 +- kind: conda + name: liblzma + version: 5.6.3 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + sha256: d1cce0b7d62d1e54e2164d3e0667ee808efc6c3870256e5b47a150cd0bf46824 + md5: eb08b903681f9f2432c320e8ed626723 + depends: + - libgcc >=13 + license: 0BSD + size: 124138 + timestamp: 1733409137214 +- kind: conda + name: liblzma + version: 5.6.3 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 + md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: 0BSD + size: 111132 + timestamp: 1733407410083 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h161d5f1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 + md5: 19e57602824042dfd0446292ef90488b + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 647599 + timestamp: 1729571887612 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h6d7220d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 566719 + timestamp: 1729572385640 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: hc8609a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + sha256: c093c6d370aadbf0409c20b6c54c488ee2f6fea976181919fcc63e87ee232673 + md5: f52c614fa214a8bedece9421c771670d + depends: + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 714610 + timestamp: 1729571912479 +- kind: conda + name: libnsl + version: 2.0.1 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 + md5: c14f32510f694e3185704d89967ec422 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 34501 + timestamp: 1697358973269 +- kind: conda + name: libnsl + version: 2.0.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 33408 + timestamp: 1697359010159 +- kind: conda + name: libopenblas + version: 0.3.28 + build: openmp_hf332438_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 + md5: 40803a48d947c8639da6704e9a44d3ce + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=13.2.0 + - llvm-openmp >=18.1.8 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4165774 + timestamp: 1730772154295 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h94d23a6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + sha256: 99ba271d8a80a1af2723f2e124ffd91d850074c0389c067e6d96d72a2dbfeabe + md5: 62857b389e42b36b686331bec0922050 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 5578513 + timestamp: 1730772671118 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h9d3fd7e_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + sha256: 30623a40764e935aa77e0d4db54c1a1589189a9bf3a03fdb445505c1e319b5a6 + md5: e8dde93dd199da3c1f2c1fcfd0042cd4 + depends: + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4793435 + timestamp: 1730773029647 +- kind: conda + name: libparquet + version: 18.1.0 + build: h081d1f1_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + sha256: c691a59f1ebb6cedbf827f49f6cf414e08b0eec911f589133e6a8321e8ac701c + md5: 68788df49ce7480187eb6387f15b2b67 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1204535 + timestamp: 1733810811118 +- kind: conda + name: libparquet + version: 18.1.0 + build: h636d7b7_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + sha256: 88c1e810bede65c54f1ebc51c14400f9e8cf0fc1f88a8c0a99210e2f5dfed582 + md5: 9b333c3a38e55f6c1b8733222e22f528 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 873134 + timestamp: 1733809271282 +- kind: conda + name: libparquet + version: 18.1.0 + build: hfc78867_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + sha256: 38aab34c422519c530d0e9a3e0ffd1624db1c1e163983c46ae341e831b2eb6b5 + md5: 1ab6d4a9a982920b9dc5f2c700777b27 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1117592 + timestamp: 1733810440129 +- kind: conda + name: libpng + version: 1.6.44 + build: hadc24fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + sha256: e5b14f7a01c2db4362d8591f42f82f336ed48d5e4079e4d1f65d0c2a3637ea78 + md5: f4cc49d7aa68316213e4b12be35308d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 290661 + timestamp: 1726234747153 +- kind: conda + name: libpng + version: 1.6.44 + build: hc14010f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + sha256: 38f8759a3eb8060deabd4db41f0f023514d853e46ddcbd0ba21768fc4e563bb1 + md5: fb36e93f0ea6a6f5d2b99984f34b049e + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 263385 + timestamp: 1726234714421 +- kind: conda + name: libpng + version: 1.6.44 + build: hc4a20ef_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + sha256: 23b5ce15cf9c6017641a8396bab00ae807dd9f662718cfa7f61de114d0c97647 + md5: 5d25802b25fcc7419fa13e21affaeb3a + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 294907 + timestamp: 1726236639270 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h029595c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + sha256: d8c7b6f851bfc53494d9b8e54d473c4f11ab26483a6e64df6f7967563df166b1 + md5: 538dbe0ad9f248e2e109abb9b6809ea5 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2802876 + timestamp: 1728564881988 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h5b01275_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + sha256: 5e8fd4aa00193c85602ce6101dd28fe31306dff85c9725048f6dc828dfa7c421 + md5: ab0bff36363bec94720275a681af8b83 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2945348 + timestamp: 1728565355702 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h8f0b736_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + sha256: f732a6fa918428e2d5ba61e78fe11bb44a002cc8f6bb74c94ee5b1297fefcfd8 + md5: d2cb5991f2fb8eb079c80084435e9ce6 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2374965 + timestamp: 1728565334796 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h18dbdb1_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + sha256: 96d4fdac28d5af38c38f90c22cb0aa9a90affae13ca8ba24bd1eb60b789df8ff + md5: f1800796b0efc4bbc5b001d845545111 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 203516 + timestamp: 1728778974654 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h2348fd5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + sha256: 6facca42cfc85a05b33e484a8b0df7857cc092db34806946d022270098d8d20f + md5: 5a7065309a66097738be6a06fd04b7ef + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 165956 + timestamp: 1728779107218 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: hbbce691_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + sha256: f8ad6a4f6d4fd54ebe3e5e712a01e663222fc57f49d16b6b8b10c30990dafb8f + md5: 2124de47357b7a516c0a3efd8f88c143 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 211096 + timestamp: 1728778964655 +- kind: conda + name: libsodium + version: 1.0.20 + build: h4ab18f5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + size: 205978 + timestamp: 1716828628198 +- kind: conda + name: libsodium + version: 1.0.20 + build: h68df207_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + sha256: 448df5ea3c5cf1af785aad46858d7a5be0522f4234a4dc9bb764f4d11ff3b981 + md5: 2e4a8f23bebdcb85ca8e5a0fbe75666a + depends: + - libgcc-ng >=12 + license: ISC + size: 177394 + timestamp: 1716828514515 +- kind: conda + name: libsodium + version: 1.0.20 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h3f77e49_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + sha256: f192f3c8973de9ec4c214990715f13b781965247a5cedf9162e7f9e699cfc3c4 + md5: 122d6f29470f1a991e85608e77e56a8a + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 850553 + timestamp: 1733762057506 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h5eb1b54_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + sha256: 885a27fa84a5a73ed9779168c02b6c386e2fc7a53f0566b32a09ceca146b42b4 + md5: d4bf59f8783a4a66c0aec568f6de3ff4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 1042182 + timestamp: 1733761913736 +- kind: conda + name: libsqlite + version: 3.47.2 + build: hee588c1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + sha256: 48af21ebc2cbf358976f1e0f4a0ab9e91dfc83d0ef337cf3837c6f5bc22fb352 + md5: b58da17db24b6e08bcbf8fed2fb8c915 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 873551 + timestamp: 1733761824646 +- kind: conda + name: libssh2 + version: 1.11.1 + build: h9cc3647_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 + md5: ddc7194676c285513706e5fc64f214d7 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279028 + timestamp: 1732349599461 +- kind: conda + name: libssh2 + version: 1.11.1 + build: ha41c0db_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + sha256: 40f2af5357457546bd11cd64a3b9043d83865180f65ce602515c35f353be35c7 + md5: aeffe03c0e598f015aab08dbb04f6ee4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 311577 + timestamp: 1732349396421 +- kind: conda + name: libssh2 + version: 1.11.1 + build: hf672d98_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 + md5: be2de152d8073ef1c01b7728475f2fe7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 304278 + timestamp: 1732349402869 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: h3f4de04_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 + md5: 37f489acd39e22b623d2d1e5ac6d195c + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3816794 + timestamp: 1729089463404 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: hc0a3c3a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + sha256: 4661af0eb9bdcbb5fb33e5d0023b001ad4be828fccdcc56500059d56f9869462 + md5: 234a5554c53625688d51062645337328 + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3893695 + timestamp: 1729027746910 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: h4852527_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + sha256: 25bb30b827d4f6d6f0522cc0579e431695503822f144043b93c50237017fffd8 + md5: 8371ac6457591af2cf6159439c1fd051 + depends: + - libstdcxx 14.2.0 hc0a3c3a_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729027780628 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: hf1166c9_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + sha256: 9f97461bd55a2745a7a0941f3502a047f15bfe7bb2952dc7fb204b3202f866fd + md5: 0e75771b8a03afae5a2c6ce71bc733f5 + depends: + - libstdcxx 14.2.0 h3f4de04_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54133 + timestamp: 1729089498541 +- kind: conda + name: libthrift + version: 0.21.0 + build: h0e7cc3e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + sha256: ebb395232973c18745b86c9a399a4725b2c39293c9a91b8e59251be013db42f0 + md5: dcb95c0a98ba9ff737f7ae482aef7833 + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 425773 + timestamp: 1727205853307 +- kind: conda + name: libthrift + version: 0.21.0 + build: h154c74f_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + sha256: f04ab1417aca1687edff9c30d8423ace285eb8c053dc16d595c6e47cfeefb274 + md5: c28792bf37f4ecdce8e3cb9e40750650 + depends: + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 417329 + timestamp: 1727205944238 +- kind: conda + name: libthrift + version: 0.21.0 + build: h64651cc_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + sha256: 7a6c7d5f58cbbc2ccd6493b4b821639fdb0701b9b04c737a949e8cb6adf1c9ad + md5: 7ce2bd2f650f8c31ad7ba4c7bfea61b7 + depends: + - __osx >=11.0 + - libcxx >=17 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 324342 + timestamp: 1727206096912 +- kind: conda + name: libtiff + version: 4.7.0 + build: h551f018_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae + md5: a5d084a957563e614ec0c0196d890654 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=18 + - libdeflate >=1.23,<1.24.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 370600 + timestamp: 1734398863052 +- kind: conda + name: libtiff + version: 4.7.0 + build: h88f7998_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + sha256: 5888bd66ba7606ae8596856c7dac800940ecad0aed77d6aa37db69d434c81cf0 + md5: 36a0ea4a173338c8725dc0807e99cf22 + depends: + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 464699 + timestamp: 1734398752249 +- kind: conda + name: libtiff + version: 4.7.0 + build: hd9ff511_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 428173 + timestamp: 1734398813264 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h5505292_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + sha256: ea88f06e97ef8fa2490f7594f8885bb542577226edf8abba3144302d951a53c2 + md5: f777470d31c78cd0abe1903a2fda436f + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 83000 + timestamp: 1732868631531 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + sha256: 37a1833c55f9945724cd4b3eb6a1469032cc754a1dd725f191c34154ad2ba7e4 + md5: 699f155da290be3a1a64c932c6728991 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 81526 + timestamp: 1732868466862 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + sha256: 9794e6388e780c3310d46f773bbc924d4053375c3fcdb07a704b57f4616db928 + md5: 1e936bd23d737aac62a18e9a1e7f8b18 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 81500 + timestamp: 1732868419835 +- kind: conda + name: libuuid + version: 2.38.1 + build: h0b41bf4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 33601 + timestamp: 1680112270483 +- kind: conda + name: libuuid + version: 2.38.1 + build: hb4cce97_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + sha256: 616277b0c5f7616c2cdf36f6c316ea3f9aa5bb35f2d4476a349ab58b9b91675f + md5: 000e30b09db0b7c775b21695dff30969 + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 35720 + timestamp: 1680113474501 +- kind: conda + name: libuv + version: 1.49.2 + build: h7ab814d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + sha256: 0e5176af1e788ad5006cf261c4ea5a288a935fda48993b0240ddd2e562dc3d02 + md5: 4bc348e3a1a74d20a3f9beb866d75e0a + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 410500 + timestamp: 1729322654121 +- kind: conda + name: libuv + version: 1.49.2 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + sha256: adf4eca89339ac7780f2394e7e6699be81259eb91f79f9d9fdf2c1bc6b26f210 + md5: 1899e1ec2be63386c41c4db31d3056af + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 627484 + timestamp: 1729322575379 +- kind: conda + name: libuv + version: 1.49.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + sha256: a35cd81cd1a9add11024097da83cc06b0aae83186fe4124b77710876f37d8f31 + md5: 070e3c9ddab77e38799d5c30b109c633 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 884647 + timestamp: 1729322566955 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + sha256: 10dded60f274e29c573cfacf6e96f5d0fc374ee431250374a44cbd773916ab9d + md5: 5fd7ab3e5f382c70607fbac6335e6e19 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 363577 + timestamp: 1713201785160 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h93a5062_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + sha256: 0d4bad713a512d79bfeb4d61821f447afab8b0792aca823f505ce6b195e9fde5 + md5: c0af0edfebe780b19940e94871f1a765 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 287750 + timestamp: 1713200194013 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + sha256: 49bc5f6b1e11cb2babf2a2a731d1a680a5e08a858280876a779dbda06c78c35f + md5: b26e8aa824079e1be0294e7152ca4559 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 438953 + timestamp: 1713199854503 +- kind: conda + name: libxcb + version: 1.17.0 + build: h262b8f6_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b + md5: cd14ee5cca2464a425b1dbfc24d90db2 + depends: + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 397493 + timestamp: 1727280745441 +- kind: conda + name: libxcb + version: 1.17.0 + build: h8a09558_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 395888 + timestamp: 1727278577118 +- kind: conda + name: libxcb + version: 1.17.0 + build: hdb1d25a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 323658 + timestamp: 1727278733917 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f + md5: b4df5d7d4b63579d081fd3a4cf99740e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 114269 + timestamp: 1702724369203 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 100393 + timestamp: 1702724383534 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h0d44e9d_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + sha256: 306e18aa647d8208ad2cd0e62d84933222b2fbe93d2d53cd5283d2256b1d54de + md5: f5b05674697ae7d2c5932766695945e1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - icu <0.0a0 + license: MIT + license_family: MIT + size: 689993 + timestamp: 1733443678322 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h178c5d8_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 + md5: 3dc3cff0eca1640a6acbbfab2f78139e + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 582898 + timestamp: 1733443841584 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h2e0c361_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 + md5: 63410f85031930cde371dfe0ee89109a + depends: + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 732155 + timestamp: 1733443825814 +- kind: conda + name: libzlib + version: 1.3.1 + build: h8359307_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 46438 + timestamp: 1727963202283 +- kind: conda + name: libzlib + version: 1.3.1 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 + md5: 08aad7cbe9f5a6b460d0976076b6ae64 + depends: + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 66657 + timestamp: 1727963199518 +- kind: conda + name: libzlib + version: 1.3.1 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 60963 + timestamp: 1727963148474 +- kind: conda + name: llvm-openmp + version: 19.1.5 + build: hdb05f8b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + sha256: e7ba0d8b718925efdcf1309f5e776e3264cc172d3af8d4048b39627c50a1abc0 + md5: f2c2e187a1d2637d282e34dc92021a70 + depends: + - __osx >=11.0 + constrains: + - openmp 19.1.5|19.1.5.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 281120 + timestamp: 1733376089600 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h286801f_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 167055 + timestamp: 1733741040117 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + sha256: 67e55058d275beea76c1882399640c37b5be8be4eb39354c94b610928e9a0573 + md5: 6654e411da94011e8fbe004eacb8fe11 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 184953 + timestamp: 1733740984533 +- kind: conda + name: markdown-it-py + version: 3.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a + md5: fee3164ac23dfca50cfcc8b85ddefb81 + depends: + - mdurl >=0.1,<1 + - python >=3.9 + license: MIT + license_family: MIT + size: 64430 + timestamp: 1733250550053 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py311h2dc5d0c_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py311h2dc5d0c_1.conda + sha256: 0291d90706ac6d3eea73e66cd290ef6d805da3fad388d1d476b8536ec92ca9a8 + md5: 6565a715337ae279e351d0abd8ffe88a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 25354 + timestamp: 1733219879408 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py311h4921393_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py311h4921393_1.conda + sha256: 4f738a7c80e34e5e5d558e946b06d08e7c40e3cc4bdf08140bf782c359845501 + md5: 249e2f6f5393bb6b36b3d3a3eebdcdf9 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24976 + timestamp: 1733219849253 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py311ha09ea12_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py311ha09ea12_1.conda + sha256: 0af0d9357e309876adf6ca61fa574afee74741fb1628755ce1f36028d294e854 + md5: eb3611be0cc15845bf6e5075adc520ee + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 25787 + timestamp: 1733220925299 +- kind: conda + name: max + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + sha256: 0e3c1984ac7476550fd8fa5921bf1ca58950219b84ae21ecd861f650e45382ac + md5: e04b1405f630c9bb7d4cb5559840e902 + depends: + - max-core ==24.6.0 release + - max-python >=24.6.0,<25.0a0 + - mojo-jupyter ==24.6.0 release + - mblack ==24.6.0 release + license: LicenseRef-Modular-Proprietary + size: 9851 + timestamp: 1734039439696 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + sha256: 38a4128c15b230f5b05e0606a339c7866a83eb943d334a948b3a8c1d2675a917 + md5: 25e678ff7c59e36ec3154fe0cd15ebde + depends: + - mblack ==24.6.0 release + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 247670119 + timestamp: 1734039439695 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + sha256: 554f2c1a6ddfab8fabf82b9fbcc1adb87e2a615669793e463f757ea452e02016 + md5: 2ca790aa461fa28722c6f21fcd2af0b9 + depends: + - mblack ==24.6.0 release + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 251486610 + timestamp: 1734039413769 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + sha256: 434c29e35067e296db55525cd5cf38bb013a1f7a7bfa99845bf6c317de6cdc12 + md5: 4a2ead0a9010c36b6193ea32f583e996 + depends: + - mblack ==24.6.0 release + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 212001240 + timestamp: 1734039726703 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.11release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.11release.conda + sha256: a1b390e3506ccb042ad19b8c902c4996bcbbd3f93d748f49e1594b5f25dee2da + md5: f453e10461549b96638cbc030006ffbb + depends: + - max-core ==24.6.0 release + - python 3.11.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.11.* *_cp311 + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 123782288 + timestamp: 1734039439701 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.11release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.11release.conda + sha256: 54d2eb3c10c24f1dafbbf58dffb625600a4cbfaa0a1f3c3f9aee1cd1c31e10ca + md5: 27174fe25af733e8623e310e79218a3c + depends: + - max-core ==24.6.0 release + - python 3.11.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.11.* *_cp311 + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 127461682 + timestamp: 1734039413776 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.11release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.11release.conda + sha256: 8034f98d180b3482cdf519c31af166169c8d2c941e603f7765b201ba73dc5b2a + md5: d012a9a77966d684b2b82e53b2a1434a + depends: + - max-core ==24.6.0 release + - python 3.11.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.11.* *_cp311 + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 112490145 + timestamp: 1734039726706 +- kind: conda + name: mblack + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + sha256: f135164020478078f4681aa77e7f6ca9f68b8e7ee02604b85342bbaf2f706f0d + md5: 77367aff981ba391ab5c047ba33ec978 + depends: + - python >=3.9,<3.13 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - python + license: MIT + size: 130668 + timestamp: 1734039439700 +- kind: conda + name: mdurl + version: 0.1.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- kind: conda + name: mojo-jupyter + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + sha256: 2fe043d98ea77f8f165b39bd252cd04942216c8533f0291c49d87d6cfd8673df + md5: b17127f3ca2cef0976496407e1cd4081 + depends: + - max-core ==24.6.0 release + - python >=3.9,<3.13 + - jupyter_client >=8.6.2,<8.7 + - python + license: LicenseRef-Modular-Proprietary + size: 22990 + timestamp: 1734039439702 +- kind: conda + name: multidict + version: 6.1.0 + build: py311h2dc5d0c_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py311h2dc5d0c_2.conda + sha256: afaab7a028281d8b5336db2b994fd3f9694862b6ca372c079dc4e84ad768c20a + md5: bb8ca118919836624d920b4c44383a15 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 62595 + timestamp: 1733913166104 +- kind: conda + name: multidict + version: 6.1.0 + build: py311h30e7462_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py311h30e7462_1.conda + sha256: 9e7beeeef3c13e000e2e9dab38dff3a5284a8c8665019be149db50b4eff9a340 + md5: ff4227ea49745aeddbf45edef09036bc + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 56769 + timestamp: 1729065684471 +- kind: conda + name: multidict + version: 6.1.0 + build: py311h58d527c_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py311h58d527c_2.conda + sha256: d892579630fb36f03325cfe6164d625cc14956f37055d1801c9a140a87a7406f + md5: 6951744a4c40630a76a7e976fb858952 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 63847 + timestamp: 1733913235773 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py311h459d7ec_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py311h459d7ec_1.conda + sha256: eca27e6fb5fb4ee73f04ae030bce29f5daa46fea3d6abdabb91740646f0d188e + md5: cebd02a02b199549a57e0d70aed7e2dc + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 339543 + timestamp: 1695459055911 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py311hcd402e7_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py311hcd402e7_1.conda + sha256: 126190f2f981ea84cbf891a2ff6ff52e1bdd681c48392db40b79da0e9e786af8 + md5: bd07035dd460220466bcab62cefced4d + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 339518 + timestamp: 1695459050286 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py311heffc1b2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py311heffc1b2_1.conda + sha256: 1bf6f7bd6b3515f26fbd977ad26bfb7012516fb3854fe9f2d715a6fbbf28a5de + md5: 68b2ed99d42d6eea3cecd25b6a151cc9 + depends: + - dill >=0.3.6 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 339630 + timestamp: 1695459263809 +- kind: conda + name: mypy_extensions + version: 1.0.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + sha256: 1895f47b7d68581a6facde5cb13ab8c2764c2e53a76bd746f8f98910dc4e08fe + md5: 29097e7ea634a45cc5386b95cac6568f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 10854 + timestamp: 1733230986902 +- kind: conda + name: ncurses + version: '6.5' + build: h7bae524_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc + md5: cb2b0ea909b97b3d70cd3921d1445e1a + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 802321 + timestamp: 1724658775723 +- kind: conda + name: ncurses + version: '6.5' + build: hcccb83c_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + sha256: acad4cf1f57b12ee1e42995e6fac646fa06aa026529f05eb8c07eb0a84a47a84 + md5: 91d49c85cacd92caa40cf375ef72a25d + depends: + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 924472 + timestamp: 1724658573518 +- kind: conda + name: ncurses + version: '6.5' + build: he02047a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a + md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 889086 + timestamp: 1724658547447 +- kind: conda + name: numpy + version: 1.26.4 + build: py311h64a7726_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py311h64a7726_0.conda + sha256: 3f4365e11b28e244c95ba8579942b0802761ba7bb31c026f50d1a9ea9c728149 + md5: a502d7aad449a1206efb366d6a12c52d + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 8065890 + timestamp: 1707225944355 +- kind: conda + name: numpy + version: 1.26.4 + build: py311h69ead2a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py311h69ead2a_0.conda + sha256: 88800a1d9d11c2fccab09d40d36f7001616f5119eaf0ec86186562f33564e651 + md5: 3fd00dd400c8d3f9da12bf33061dd28d + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 7234391 + timestamp: 1707225781489 +- kind: conda + name: numpy + version: 1.26.4 + build: py311h7125741_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py311h7125741_0.conda + sha256: 160a52a01fea44fe9753a2ed22cf13d7b55c8a89ea0b8738546fdbf4795d6514 + md5: 3160b93669a0def35a7a8158ebb33816 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6652352 + timestamp: 1707226297967 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h3f56577_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d + md5: 04231368e4af50d11184b50e14250993 + depends: + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 377796 + timestamp: 1733816683252 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h5fbd93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 + md5: 9e5816bc95d285c115a3ebc2f8563564 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 342988 + timestamp: 1733816638720 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h8a3d83b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + sha256: 1d59bc72ca7faac06d349c1a280f5cfb8a57ee5896f1e24225a997189d7418c7 + md5: 4b71d78648dbcf68ce8bf22bb07ff838 + depends: + - __osx >=11.0 + - libcxx >=18 + - libpng >=1.6.44,<1.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 319362 + timestamp: 1733816781741 +- kind: conda + name: openssl + version: 3.4.0 + build: h39f12f2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + sha256: bd1d58ced46e75efa3b842c61642fd12272c69e9fe4d7261078bc082153a1d53 + md5: df307bbc703324722df0293c9ca2e418 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2935176 + timestamp: 1731377561525 +- kind: conda + name: openssl + version: 3.4.0 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + sha256: 64dbbdd6384fa56338124783197f7ad9048c989a02264bcd2e07355e3570f113 + md5: b2f202b5bddafac824eb610b65dde98f + depends: + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 3474825 + timestamp: 1731379200886 +- kind: conda + name: openssl + version: 3.4.0 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + sha256: 814b9dff1847b132c676ee6cc1a8cb2d427320779b93e1b6d76552275c128705 + md5: 23cc74f77eb99315c0360ec3533147a9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 2947466 + timestamp: 1731377666602 +- kind: conda + name: opentelemetry-api + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + sha256: 296280c8ace35c0a1cf72bed1077f248b3af903c3bf92332f1783a207cb5abdb + md5: 307b05402c1a382f2f09426492dee8f8 + depends: + - deprecated >=1.2.6 + - importlib-metadata >=6.0,<=8.5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 44166 + timestamp: 1734132973331 +- kind: conda + name: opentelemetry-exporter-otlp-proto-common + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + sha256: ae9776efe52564e0d6711cfcee7c54439273e57a3999f7f796f66e862f58aae9 + md5: 0c02e74d26bce3fec93b227cf7ea6e6b + depends: + - backoff >=1.10.0,<3.0.0 + - opentelemetry-proto 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 18922 + timestamp: 1734310457116 +- kind: conda + name: opentelemetry-exporter-otlp-proto-http + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + sha256: 5d61db9d5b4f91b3932f5f2348920d5b7fdaa09e52c8ea054cf7bf3f21677c9c + md5: 223f4e56a29601c887f0dc467034af5b + depends: + - deprecated >=1.2.6 + - googleapis-common-protos >=1.52,<2.dev0 + - opentelemetry-api >=1.15,<2.dev0 + - opentelemetry-exporter-otlp-proto-common 1.29.0 + - opentelemetry-proto 1.29.0 + - opentelemetry-sdk 1.29.0 + - python >=3.9 + - requests >=2.7,<3.dev0 + license: Apache-2.0 + size: 17147 + timestamp: 1734345675510 +- kind: conda + name: opentelemetry-exporter-prometheus + version: 1.12.0rc1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + sha256: b8239230dbbdb491401e41b53bd9f21d60551cedef1a8d5807fca1bf9bdd331c + md5: 1ddc95052b31147d1e10d818cf519cf5 + depends: + - opentelemetry-api >=1.10.0 + - opentelemetry-sdk >=1.10.0 + - prometheus_client >=0.5.0,<1.0.0 + - python >=3.6 + license: Apache-2.0 + license_family: APACHE + size: 14721 + timestamp: 1695214221489 +- kind: conda + name: opentelemetry-proto + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + sha256: 200a7cb8acc8a0ddd6ef55c5460cec871b6a265929b240a0296c0ccb9c8d9758 + md5: e2a6d2ad10b813c7fdc1c64aac376128 + depends: + - protobuf <6.0,>=5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 37235 + timestamp: 1734291034372 +- kind: conda + name: opentelemetry-sdk + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + sha256: 7b36629d8b8be8a019fcfd1518d7b7f862dd25de96f8adcadb93e4fd12cf9bd6 + md5: 2a8893f06e6ebda4bfa78875bc923ea4 + depends: + - opentelemetry-api 1.29.0 + - opentelemetry-semantic-conventions 0.50b0 + - python >=3.9 + - typing-extensions >=3.7.4 + - typing_extensions >=3.7.4 + license: Apache-2.0 + license_family: APACHE + size: 77645 + timestamp: 1734297838999 +- kind: conda + name: opentelemetry-semantic-conventions + version: 0.50b0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + sha256: 6526e70368d5bf66ef0eaa51fb800d53782dde71a24bd38f40139919a6f784dc + md5: f7111fa4188d646c8108e232d024cb99 + depends: + - deprecated >=1.2.6 + - opentelemetry-api 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 86084 + timestamp: 1734208980168 +- kind: conda + name: orc + version: 2.0.3 + build: h3c55218_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + sha256: 154b26bc4d586de33765a155c9b79ebd7f5bb36c2bbf4b8854e1631bca8d21af + md5: 0a51a3cf028b845c46ec0d1ea2d18629 + depends: + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1165179 + timestamp: 1733509923825 +- kind: conda + name: orc + version: 2.0.3 + build: h97ab989_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + sha256: 9de7e2746fde57c9b7f08ee87142014f6bb9b2d3a506839ea3e98baa99711576 + md5: 2f46eae652623114e112df13fae311cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1189462 + timestamp: 1733509801323 +- kind: conda + name: orc + version: 2.0.3 + build: hbcee414_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + sha256: e5e72438a3cd967ebc774070e8c49500d2d6d4175f349400b327fee75d3bfc05 + md5: e808cf7819eaa1735c8790d7f9f482c7 + depends: + - __osx >=11.0 + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 437391 + timestamp: 1733510118673 +- kind: conda + name: packaging + version: '24.2' + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 + md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + size: 60164 + timestamp: 1733203368787 +- kind: conda + name: pandas + version: 2.2.3 + build: py311h7db5c69_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py311h7db5c69_1.conda + sha256: dce121d3838996b77b810ca9097cc17068552075c761408a9b2eb788cf8fd1b0 + md5: 643f8cb35133eb1be4919fb953f0a25f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.11,<3.12.0a0 + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.11.* *_cp311 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15695466 + timestamp: 1726879158862 +- kind: conda + name: pandas + version: 2.2.3 + build: py311h848c333_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py311h848c333_1.conda + sha256: 8b368a4871bc9c8c9c582fc3539c00a44ae41eb239ca330ee55f9c8bd85a0bfd + md5: 609f8498e89280eeda12a0be33efed35 + depends: + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.11.* *_cp311 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15390929 + timestamp: 1726879096370 +- kind: conda + name: pandas + version: 2.2.3 + build: py311h9cb3ce9_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py311h9cb3ce9_1.conda + sha256: 0a08027b25e4f6034d7733c7366f44283246d61cb82d1721f8789d50ebfef287 + md5: 9ffa9dee175c76e68ea5de5aa1168d83 + depends: + - __osx >=11.0 + - libcxx >=17 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.11.* *_cp311 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 14807397 + timestamp: 1726879116250 +- kind: conda + name: pathspec + version: 0.12.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee + md5: 617f15191456cc6a13db418a275435e5 + depends: + - python >=3.9 + license: MPL-2.0 + license_family: MOZILLA + size: 41075 + timestamp: 1733233471940 +- kind: conda + name: pillow + version: 11.0.0 + build: py311h3894ae9_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py311h3894ae9_0.conda + sha256: 6d5307fed000e6b72b98d54dd1fea7b155f9a6453476a937522b89dde7b3d673 + md5: a9a4adae1c4178f50ac3d1fd5d64bb85 + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41856994 + timestamp: 1729066060042 +- kind: conda + name: pillow + version: 11.0.0 + build: py311h49e9ac3_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py311h49e9ac3_0.conda + sha256: f0f792596ae99cba01f829d064058b1e99ca84080fc89f72d925bfe473cfc1b6 + md5: 2bd3d0f839ec0d1eaca817c9d1feb7c2 + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 42421065 + timestamp: 1729065780130 +- kind: conda + name: pillow + version: 11.0.0 + build: py311hb2a0dd2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py311hb2a0dd2_0.conda + sha256: 19b3a8399b7f7d5c80c5bcef17881e7036826fc739e13ccd97d21b0212408827 + md5: 6454f9200cf6d04192bbdee9ab6a9761 + depends: + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41973113 + timestamp: 1729067980140 +- kind: conda + name: platformdirs + version: 4.3.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + sha256: bb50f6499e8bc1d1a26f17716c97984671121608dc0c3ecd34858112bce59a27 + md5: 577852c7e53901ddccc7e6a9959ddebe + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 20448 + timestamp: 1733232756001 +- kind: conda + name: prometheus_client + version: 0.21.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + sha256: bc8f00d5155deb7b47702cb8370f233935704100dbc23e30747c161d1b6cf3ab + md5: 3e01e386307acc60b2f89af0b2e161aa + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 49002 + timestamp: 1733327434163 +- kind: conda + name: propcache + version: 0.2.1 + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py311h917b07b_0.conda + sha256: 7d4185519514a4d357348e7a31974afb713ab6e0d7ea3a9f27a36f3b6515b638 + md5: f0599cb37e2cf8710eaae4b4d85f7759 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 48002 + timestamp: 1733392010497 +- kind: conda + name: propcache + version: 0.2.1 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py311h9ecbd09_0.conda + sha256: 3323f2ed707a9fe89ee142c9ea1adef0cf8f75fb005ec414b50e8cc0381b57f4 + md5: 20d1c4ad24ac50f0941c63e81e4a86b7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 53315 + timestamp: 1733391912538 +- kind: conda + name: propcache + version: 0.2.1 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py311ha879c10_0.conda + sha256: 9d6ed4a29efa5ea64b50097c450429944f116d06d883d23e5b5493eab0c68393 + md5: 15d3518828453960069fd7874fe88468 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 52970 + timestamp: 1733392060312 +- kind: conda + name: protobuf + version: 5.28.2 + build: py311h6885ffc_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py311h6885ffc_0.conda + sha256: e3c9aadc12678327f8349d1f043d90a320ab287d733eae8e00f1a5d205d6792a + md5: 1a922ee234494a42a52e3f7225920b41 + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 453427 + timestamp: 1728669805249 +- kind: conda + name: protobuf + version: 5.28.2 + build: py311h89d996e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py311h89d996e_0.conda + sha256: e31883a2a0134a337dad1cc42730a4c1c212f13d69843cb8643f30cfcdaf134c + md5: 9e0fb694cb431de5b4ed52697a629f38 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 480827 + timestamp: 1728669731679 +- kind: conda + name: protobuf + version: 5.28.2 + build: py311hfdbb021_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py311hfdbb021_0.conda + sha256: a002b1606e63dcdf8da3a6f570f73dbeeef60ce552f62c9c672711a2a31d4921 + md5: 83ce49456829de025312e1de9b4395a5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 472879 + timestamp: 1728669387714 +- kind: conda + name: pthread-stubs + version: '0.4' + build: h86ecc28_1002 + build_number: 1002 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba + md5: bb5a90c93e3bac3d5690acf76b4a6386 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 8342 + timestamp: 1726803319942 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hb9d3cd8_1002 + build_number: 1002 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 8252 + timestamp: 1726802366959 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hd74edd7_1002 + build_number: 1002 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 8381 + timestamp: 1726802424786 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py311h38be061_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py311h38be061_0.conda + sha256: 9cfd158a1bb76c4af1a51237a5c5db4a36b2e83bad625ddf6c2b65ee232c16ba + md5: 47b8624012486e05e66f6acf7267aa22 + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 25199 + timestamp: 1732610760700 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py311ha1ab1f8_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py311ha1ab1f8_0.conda + sha256: c93f3ede66be0502b5b9afc5bc3fde50d6f8c3863d29b138ff5f536a116457f3 + md5: 7a3b822fa6abb937651bee20878f087a + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 25322 + timestamp: 1732611121491 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py311hfecb2dc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py311hfecb2dc_0.conda + sha256: 2af68c0d11cf480a043b213eb4d1e0908f3edff25dc418b9c55f1002f46dc45b + md5: b66ff73d40f84cca2d7a4f755b21d956 + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: APACHE + size: 25373 + timestamp: 1732611450659 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py311h4854187_0_cpu + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py311h4854187_0_cpu.conda + sha256: db147a0cc22b55ea3c35553b39336eb0392c33371f6efd7f9fb4efed2b728e34 + md5: 830a64ee7a65e588c7ea615be84db2e3 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4562010 + timestamp: 1732610600424 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py311ha6d2531_0_cpu + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py311ha6d2531_0_cpu.conda + sha256: 3187321dc45106c2c87e5a70ab94ecbc0633f5b43caabcf173f4d372e5f6d8d8 + md5: 8cddcf0bb4b3e674ae6bbdda5266693f + depends: + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - apache-arrow-proc =*=cpu + - numpy >=1.21,<3 + license: Apache-2.0 + license_family: APACHE + size: 4484447 + timestamp: 1732610567027 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py311he04fa90_0_cpu + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py311he04fa90_0_cpu.conda + sha256: 4563e2d4f41b3874b89e8e2bdebf42588c1c819bd050ae858200f60e30bae860 + md5: 09b4a27f615d22f194466d8c274ef13e + depends: + - __osx >=11.0 + - libarrow 18.1.0.* *cpu + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 3974075 + timestamp: 1732611073316 +- kind: conda + name: pycparser + version: '2.22' + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- kind: conda + name: pydantic + version: 2.10.3 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + sha256: cac9eebd3d5f8d8a497a9025d756257ddc75b8b3393e6737cb45077bd744d4f8 + md5: 194ef7f91286978521350f171b117f01 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.27.1 + - python >=3.9 + - typing-extensions >=4.6.1 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + size: 317037 + timestamp: 1733316963547 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py311h0ca61a2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py311h0ca61a2_0.conda + sha256: 03794e4aa320059163ddeaa347cfec2dae2f5af9bcdbc0b1d7765e81523b43cb + md5: 86aee7900360de3d463d4014a8cef705 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1503196 + timestamp: 1732254269904 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py311h3ff9189_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py311h3ff9189_0.conda + sha256: fda69a0024647c988a1571a78f31d05cefb95c8580c7fea29106dc5e08b654fa + md5: 9a65f7d97aaa139bd8471429e192ac61 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1451573 + timestamp: 1732254367639 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py311h9e33e62_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py311h9e33e62_0.conda + sha256: 0ae49448c55affa0e9df0e876d02aee77ad42678500a34679f9689bf3682000e + md5: e5192dfb2dae866470c3eec81dbe5727 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1632797 + timestamp: 1732254154568 +- kind: conda + name: pydantic-settings + version: 2.7.0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + sha256: dd1ac7c8b6a189c8aa18f6c7df019d8f6df495300a259e3fbebdb542fc955c3b + md5: d9f19a7c4199249fa229891b573b6f9b + depends: + - pydantic >=2.7.0 + - python >=3.9 + - python-dotenv >=0.21.0 + license: MIT + license_family: MIT + size: 31426 + timestamp: 1734127929720 +- kind: conda + name: pygments + version: 2.18.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + sha256: 0d6133545f268b2b89c2617c196fc791f365b538d4057ecd636d658c3b1e885d + md5: b38dc0206e2a530e5c2cf11dc086b31a + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 876700 + timestamp: 1733221731178 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py311h9ecbd09_0.conda + sha256: 389847e94097f5bb4e50e79fcafb69d29536704f5d8e6197e2db15aa18ce79bb + md5: dac09216d0e2048338c501ba88708ee5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 184141 + timestamp: 1728714174689 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py311ha879c10_0.conda + sha256: cf6065cfd429b2947ed8529f7bacdc55911743cfcc77999a91376e7e31a732d1 + md5: 33339e64f325437f8b4806a84d40418b + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 186594 + timestamp: 1728714239127 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py311hae2e1ce_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py311hae2e1ce_0.conda + sha256: d54db4d4895ba9d56c81ec6ca4b431eff4b380e45baf1ca415956d978791d3f6 + md5: 03b6ef0cf70c84871b11cc017e010d12 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 184916 + timestamp: 1728714210932 +- kind: conda + name: pysocks + version: 1.7.1 + build: pyha55dd90_7 + build_number: 7 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 +- kind: conda + name: python + version: 3.11.11 + build: h1683364_1_cpython + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.11.11-h1683364_1_cpython.conda + sha256: b39a2253510b26213093cb29e27722cb33782aec213c020dfd17cd74d58f68e7 + md5: 7e8786cbe7b83e7011e681a4780c9b7f + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.11.* *_cp311 + license: Python-2.0 + size: 15234582 + timestamp: 1733407838276 +- kind: conda + name: python + version: 3.11.11 + build: h9e4cc4f_1_cpython + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.11-h9e4cc4f_1_cpython.conda + sha256: b29ce0836fce55bdff8d5c5b71c4921a23f87d3b950aea89a9e75784120b06b0 + md5: 8387070aa413ce9a8cc35a509fae938b + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.11.* *_cp311 + license: Python-2.0 + size: 30624804 + timestamp: 1733409665928 +- kind: conda + name: python + version: 3.11.11 + build: hc22306f_1_cpython + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.11-hc22306f_1_cpython.conda + sha256: 94e198f6a5affa1431401fca7e3b27fda68c59f5ee726083288bff1f6bed8c7f + md5: 8d81dcd0be5bdcdd98e0f2482bf63784 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.11.* *_cp311 + license: Python-2.0 + size: 14647146 + timestamp: 1733409012105 +- kind: conda + name: python-dateutil + version: 2.9.0.post0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + sha256: a50052536f1ef8516ed11a844f9413661829aa083304dc624c5925298d078d79 + md5: 5ba79d7c71f03c678c8ead841f347d6e + depends: + - python >=3.9 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222505 + timestamp: 1733215763718 +- kind: conda + name: python-dotenv + version: 1.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 + md5: e5c6ed218664802d305e79cc2d4491de + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 24215 + timestamp: 1733243277223 +- kind: conda + name: python-json-logger + version: 2.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca + md5: a61bf9ec79426938ff785eb69dbb1960 + depends: + - python >=3.6 + license: BSD-2-Clause + license_family: BSD + size: 13383 + timestamp: 1677079727691 +- kind: conda + name: python-multipart + version: 0.0.20 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 27913 + timestamp: 1734420869885 +- kind: conda + name: python-tzdata + version: '2024.2' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + sha256: 57c9a02ec25926fb48edca59b9ede107823e5d5c473b94a0e05cc0b9a193a642 + md5: c0def296b2f6d2dd7b030c2a7f66bb1f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 142235 + timestamp: 1733235414217 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py311h460d6c5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py311h460d6c5_1.conda + sha256: fbed29039fd5eabf7c8e55dcfba2533673e1e48346efdc16096d2a2bb785e262 + md5: f4d1c51beffde2a6612b993bb9a63622 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 21621 + timestamp: 1725272333568 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py311h5487e9b_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py311h5487e9b_1.conda + sha256: b6afb0cf56db20f58746caf340ef6506e97c393c75d3606bc24f250146c31da0 + md5: 5236c2ea626886210fd14e7c005ac732 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23601 + timestamp: 1725273164263 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py311h9ecbd09_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py311h9ecbd09_1.conda + sha256: 77d1b380b672cdcb9b0b79b9d37b7617014219246d26462f92fae0e40fa72d05 + md5: b1796d741ca619dbacb79917b20e5a05 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23052 + timestamp: 1725272142790 +- kind: conda + name: python_abi + version: '3.11' + build: 5_cp311 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-5_cp311.conda + sha256: 2660b8059b3ee854bc5d3c6b1fce946e5bd2fe8fbca7827de2c5885ead6209de + md5: 139a8d40c8a2f430df31048949e450de + constrains: + - python 3.11.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6211 + timestamp: 1723823324668 +- kind: conda + name: python_abi + version: '3.11' + build: 5_cp311 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.11-5_cp311.conda + sha256: 76974c2732919ace87b5f3a634eac93fed6900d557fcae0575787ec0a33c370e + md5: c2078141f21872cc34d9305123ba08f2 + constrains: + - python 3.11.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6300 + timestamp: 1723823316891 +- kind: conda + name: python_abi + version: '3.11' + build: 5_cp311 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.11-5_cp311.conda + sha256: adc05729b7e0aca7b436e60a86f10822a92185dfcb48d66d6444e3629d3a1f6a + md5: 3b855e3734344134cb56c410f729c340 + constrains: + - python 3.11.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6308 + timestamp: 1723823096865 +- kind: conda + name: pytz + version: '2024.1' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + sha256: 1a7d6b233f7e6e3bbcbad054c8fd51e690a67b129a899a056a5e45dd9f00cb41 + md5: 3eeeeb9e4827ace8c0c1419c85d590ad + depends: + - python >=3.7 + license: MIT + license_family: MIT + size: 188538 + timestamp: 1706886944988 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py311h460d6c5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py311h460d6c5_1.conda + sha256: 9ae182eef4e96a7c2f46cc9add19496276612663e17429500432631dce31a831 + md5: d32590e7bd388f18b036c6fc402a0cb1 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 192321 + timestamp: 1725456528007 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py311h9ecbd09_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py311h9ecbd09_1.conda + sha256: e721e5ff389a7b2135917c04b27391be3d3382e261bb60a369b1620655365c3d + md5: abeb54d40f439b86f75ea57045ab8496 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 212644 + timestamp: 1725456264282 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py311ha879c10_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py311ha879c10_1.conda + sha256: c0f373c2944cf18da2cec19bae76284ef54cef44b3925c249d53821e4021d59a + md5: ad89d09994540880f297259742a8428a + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 205817 + timestamp: 1725456351893 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py311h730b646_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py311h730b646_3.conda + sha256: 7e75589d9c3723ecf314435f15a7b486cebafa89ebf00bb616354e37587dc7ae + md5: b6f3e527de0c0384cd78cfa779bd6ddf + depends: + - __osx >=11.0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 365841 + timestamp: 1728642472021 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py311h7deb3e3_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py311h7deb3e3_3.conda + sha256: 3fdef7b3c43474b7225868776a373289a8fd92787ffdf8bed11cf7f39b4ac741 + md5: e0897de1d8979a3bb20ef031ae1f7d28 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 389074 + timestamp: 1728642373938 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py311h826da9f_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py311h826da9f_3.conda + sha256: 4bffb8caa7b44ec2974d18a2660bbf9c53553d3343c114a33442ca4a8e192f1a + md5: 2d901569f3142d9c7ea9e89f6f965369 + depends: + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 382698 + timestamp: 1728644123354 +- kind: conda + name: re2 + version: 2024.07.02 + build: h2d3a13d_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + sha256: 55e7be480bfb979fa8595a16d7f2adea3a5ac9a77b2e97cd0f7ac40e989edb6c + md5: 83f4e47229834c895a92c18383e1cd9d + depends: + - libre2-11 2024.07.02 h18dbdb1_1 + license: BSD-3-Clause + license_family: BSD + size: 26747 + timestamp: 1728778986331 +- kind: conda + name: re2 + version: 2024.07.02 + build: h77b4e00_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + sha256: c1721cb80f7201652fc9801f49c214c88aee835d957f2376e301bd40a8415742 + md5: 01093ff37c1b5e6bf9f17c0116747d11 + depends: + - libre2-11 2024.07.02 hbbce691_1 + license: BSD-3-Clause + license_family: BSD + size: 26665 + timestamp: 1728778975855 +- kind: conda + name: re2 + version: 2024.07.02 + build: hcd0e937_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + sha256: eebddde6cb10b146507810b701ef6df122d5309cd5151a39d0828aa44dc53725 + md5: 19e29f2ccc9168eb0a39dc40c04c0e21 + depends: + - libre2-11 2024.07.02 h2348fd5_1 + license: BSD-3-Clause + license_family: BSD + size: 26860 + timestamp: 1728779123653 +- kind: conda + name: readline + version: '8.2' + build: h8228510_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 + md5: 47d31b792659ce70f470b5c82fdfb7a4 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 281456 + timestamp: 1679532220005 +- kind: conda + name: readline + version: '8.2' + build: h8fc344f_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + sha256: 4c99f7417419734e3797d45bc355e61c26520e111893b0d7087a01a7fbfbe3dd + md5: 105eb1e16bf83bfb2eb380a48032b655 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 294092 + timestamp: 1679532238805 +- kind: conda + name: readline + version: '8.2' + build: h92ec313_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 + md5: 8cbb776a2f641b943d413b3e19df71f4 + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 250351 + timestamp: 1679532511311 +- kind: conda + name: regex + version: 2024.11.6 + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py311h917b07b_0.conda + sha256: 508beb88118fcb9977346cbb95ada84c478381f12108d40787f64e9355b9370e + md5: 8ee270aa3c07b51b22e5288e51dd3efb + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Python-2.0 + license_family: PSF + size: 375284 + timestamp: 1730952380791 +- kind: conda + name: regex + version: 2024.11.6 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py311h9ecbd09_0.conda + sha256: 5151d752339013a81d62632f807b370a231faaff5a6b550cb848e53fbcaf581f + md5: 08f56182b69c47595c7fbbbc195f4867 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Python-2.0 + license_family: PSF + size: 409743 + timestamp: 1730952290379 +- kind: conda + name: regex + version: 2024.11.6 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py311ha879c10_0.conda + sha256: 6de016ed6900c06b0536f7797e4056e7baebd4158663c1df2498990433da437e + md5: 8981724b818c01d6d0d2d7b9750b1d7e + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Python-2.0 + license_family: PSF + size: 404338 + timestamp: 1730952422447 +- kind: conda + name: requests + version: 2.32.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad + md5: a9b9368f3701a417eac9edbcae7cb737 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 58723 + timestamp: 1733217126197 +- kind: conda + name: rich + version: 13.9.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 + md5: 7aed65d4ff222bfb7335997aa40b7da5 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + size: 185646 + timestamp: 1733342347277 +- kind: conda + name: rich-toolkit + version: 0.11.3 + build: pyh29332c3_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 + md5: 4ba15ae9388b67d09782798347481f69 + depends: + - python >=3.9 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 17357 + timestamp: 1733750834072 +- kind: conda + name: s2n + version: 1.5.10 + build: h5df210e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + sha256: b5e7a9f4b7b1ec5c5c3661e2defc8b47fab543b05cad6fec78739d8007612464 + md5: 3d3979efcc0f44f3f0cef3de03b296cc + depends: + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 353450 + timestamp: 1734415474615 +- kind: conda + name: s2n + version: 1.5.10 + build: hb5b8611_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + sha256: f6d451821fddc26b93f45e9313e1ea15e09e5ef049d4e137413a5225d2a5dfba + md5: 999f3673f2a011f59287f2969e3749e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 355142 + timestamp: 1734415467047 +- kind: conda + name: safetensors + version: 0.4.5 + build: py311h0ca61a2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py311h0ca61a2_0.conda + sha256: ce3aa18752eb47e6e55256c0c52ef67786429fdcb2611dd0f7b490049671ef25 + md5: 7d4236d529bacc6d2217a57348965400 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 401351 + timestamp: 1725632381591 +- kind: conda + name: safetensors + version: 0.4.5 + build: py311h481aa64_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py311h481aa64_0.conda + sha256: 283f79e9fe2b09d8c21b28807e914c49ba5cbe09dce731633ee5aa088c234d54 + md5: 0b444f05b9ea222404ea115a35da9131 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 353541 + timestamp: 1725632251967 +- kind: conda + name: safetensors + version: 0.4.5 + build: py311h9e33e62_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py311h9e33e62_0.conda + sha256: 65cad4de4bf04878abdcede4363f8818ebb895d105b03e996e9b98dcc9a23d01 + md5: 5a58520e8eb4a0119b137fd67a02bb2a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 403087 + timestamp: 1725632204888 +- kind: conda + name: shellingham + version: 1.5.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef + md5: 7c3c2a0f3ebdea2bbc35538d162b43bf + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14462 + timestamp: 1733301007770 +- kind: conda + name: six + version: 1.17.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + sha256: 41db0180680cc67c3fa76544ffd48d6a5679d96f4b71d7498a759e94edc9a2db + md5: a451d576819089b0d672f18768be0f65 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 16385 + timestamp: 1733381032766 +- kind: conda + name: snappy + version: 1.2.1 + build: h8bd8927_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + sha256: ec91e86eeb2c6bbf09d51351b851e945185d70661d2ada67204c9a6419d282d3 + md5: 3b3e64af585eadfb52bb90b553db5edf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 42739 + timestamp: 1733501881851 +- kind: conda + name: snappy + version: 1.2.1 + build: h98b9ce2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f + md5: ded86dee325290da2967a3fea3800eb5 + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-3-Clause + license_family: BSD + size: 35857 + timestamp: 1733502172664 +- kind: conda + name: snappy + version: 1.2.1 + build: hd4fb6f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + sha256: c4a07ae5def8d55128f25a567a296ef9d7bf99a3bc79d46bd5160c076a5f50af + md5: 2fcc6cd1e5550deb509073fd2e6693e1 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 43032 + timestamp: 1733501964775 +- kind: conda + name: sniffio + version: 1.3.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 + md5: bf7a226e58dfb8346c70df36065d86c9 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 15019 + timestamp: 1733244175724 +- kind: conda + name: sse-starlette + version: 2.1.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + sha256: 6d671a66333410ec7e5e7858a252565a9001366726d1fe3c3a506d7156169085 + md5: 3918255c942c242ed5599e10329e8d0e + depends: + - anyio + - python >=3.8 + - starlette + - uvicorn + license: BSD-3-Clause + license_family: BSD + size: 14712 + timestamp: 1722520112550 +- kind: conda + name: starlette + version: 0.41.3 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + sha256: b74fc76107487eb26624c01fc55bfab7eed03ae82e003333c86d8a1eeac53672 + md5: 0207dac04ae2200701fab697f0aaaac4 + depends: + - anyio >=3.4.0,<5 + - python >=3.9 + - typing_extensions >=3.10.0 + license: BSD-3-Clause + license_family: BSD + size: 58838 + timestamp: 1733344472634 +- kind: conda + name: tk + version: 8.6.13 + build: h194ca79_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + sha256: 7fa27cc512d3a783f38bd16bbbffc008807372499d5b65d089a8e43bde9db267 + md5: f75105e0585851f818e0009dd1dde4dc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3351802 + timestamp: 1695506242997 +- kind: conda + name: tk + version: 8.6.13 + build: h5083fa2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 + md5: b50a57ba89c32b62428b71a875291c9b + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3145523 + timestamp: 1699202432999 +- kind: conda + name: tk + version: 8.6.13 + build: noxft_h4845f30_101 + build_number: 101 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3318875 + timestamp: 1699202167581 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py311h182c674_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py311h182c674_0.conda + sha256: 231ecde88bb291437a8060e4eaee74d87318ee635b4ceac5d74fc82343f137d0 + md5: 7e9304388022ef7f7f21b94953a5181b + depends: + - __glibc >=2.17,<3.0.a0 + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2264312 + timestamp: 1732734291587 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py311h5e37e04_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py311h5e37e04_0.conda + sha256: 02fad918a39c9e10feedac2937e3bee619fa49707734ae478e0342a85784fb98 + md5: 833844038ba171a27678bf201c3f4c74 + depends: + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2329972 + timestamp: 1732734458949 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py311h82b0fb8_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py311h82b0fb8_0.conda + sha256: 8f2a22323b67a75d57192d37bb031fb44a51f013d3232f18a0df3c453ea68ab9 + md5: d1543e49d59c7537c7c97f6d70544b00 + depends: + - __osx >=11.0 + - huggingface_hub >=0.16.4,<1.0 + - libcxx >=18 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 1935264 + timestamp: 1732734431057 +- kind: conda + name: tornado + version: 6.4.2 + build: py311h5487e9b_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py311h5487e9b_0.conda + sha256: 0619169eb95f8d7285dd267be3559d3f71af071954792cdd9591a90602992cee + md5: fe331d12b7fccca2348a114c4742a0e0 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: Apache + size: 859892 + timestamp: 1732616872562 +- kind: conda + name: tornado + version: 6.4.2 + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py311h917b07b_0.conda + sha256: 80b79a7d4ed8e16019b8c634cca66935d18fc98be358c76a6ead8c611306ee14 + md5: 183b74c576dc7f920dae168997dbd1dd + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: Apache + size: 858954 + timestamp: 1732616142626 +- kind: conda + name: tornado + version: 6.4.2 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py311h9ecbd09_0.conda + sha256: afa3489113154b5cb0724b0bf120b62df91f426dabfe5d02f2ba09e90d346b28 + md5: df3aee9c3e44489257a840b8354e77b9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: Apache + size: 855653 + timestamp: 1732616048886 +- kind: conda + name: tqdm + version: 4.67.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d + md5: 4085c9db273a148e149c03627350e22c + depends: + - colorama + - python >=3.7 + license: MPL-2.0 or MIT + size: 89484 + timestamp: 1732497312317 +- kind: conda + name: traitlets + version: 5.14.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 + md5: 019a7385be9af33791c989871317e1ed + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110051 + timestamp: 1733367480074 +- kind: conda + name: transformers + version: 4.47.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + sha256: d31821081219a0ede5c1f356b65a61ce98ac11e2df78b0eaa684c17c73389fbf + md5: 6d2ec1ddee8057d2d724a0ab0bb578a0 + depends: + - datasets !=2.5.0 + - filelock + - huggingface_hub >=0.23.0,<1.0 + - numpy >=1.17 + - packaging >=20.0 + - python >=3.9 + - pyyaml >=5.1 + - regex !=2019.12.17 + - requests + - safetensors >=0.4.1 + - tokenizers >=0.21,<0.22 + - tqdm >=4.27 + license: Apache-2.0 + license_family: APACHE + size: 3726957 + timestamp: 1733948063517 +- kind: conda + name: typer + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + sha256: ef695490e895c2ad552c77ec497b899b09fd4ad4ab07edcf5649f5994cf92a35 + md5: 170a0398946d8f5b454e592672b6fc20 + depends: + - python >=3.9 + - typer-slim-standard 0.15.1 hd8ed1ab_0 + license: MIT + license_family: MIT + size: 56175 + timestamp: 1733408582623 +- kind: conda + name: typer-slim + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + sha256: d4965516f35e0805199de6596c4ac76c4ad3d6b012be35e532102f9e53ecb860 + md5: 0218b16f5a1dd569e575a7a6415489db + depends: + - click >=8.0.0 + - python >=3.9 + - typing_extensions >=3.7.4.3 + constrains: + - rich >=10.11.0 + - typer >=0.15.1,<0.15.2.0a0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + size: 43592 + timestamp: 1733408569554 +- kind: conda + name: typer-slim-standard + version: 0.15.1 + build: hd8ed1ab_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + sha256: f31c56fe98315da8b9ce848256c17e0b9f87896b41a6ccf0c9cc74644dcef20f + md5: 4e603c43bfdfc7b533be087c3e070cc9 + depends: + - rich + - shellingham + - typer-slim 0.15.1 pyhd8ed1ab_0 + license: MIT + license_family: MIT + size: 49531 + timestamp: 1733408570063 +- kind: conda + name: typing-extensions + version: 4.12.2 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 + md5: b6a408c64b78ec7b779a3e5c7a902433 + depends: + - typing_extensions 4.12.2 pyha770c72_1 + license: PSF-2.0 + license_family: PSF + size: 10075 + timestamp: 1733188758872 +- kind: conda + name: typing_extensions + version: 4.12.2 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 + md5: d17f13df8b65464ca316cbc000a3cb64 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 39637 + timestamp: 1733188758212 +- kind: conda + name: tzdata + version: 2024b + build: hc8b5060_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + sha256: 4fde5c3008bf5d2db82f2b50204464314cc3c91c1d953652f7bd01d9e52aefdf + md5: 8ac3367aafb1cc0a068483c580af8015 + license: LicenseRef-Public-Domain + size: 122354 + timestamp: 1728047496079 +- kind: conda + name: urllib3 + version: 2.2.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + sha256: 416e30a1c3262275f01a3e22e783118d9e9d2872a739a9ed860d06fa9c7593d5 + md5: 4a2d8ef7c37b8808c5b9b750501fffce + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + size: 98077 + timestamp: 1733206968917 +- kind: conda + name: uvicorn + version: 0.34.0 + build: pyh31011fe_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa + md5: 5d448feee86e4740498ec8f8eb40e052 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.9 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + size: 48643 + timestamp: 1734293057914 +- kind: conda + name: uvicorn-standard + version: 0.34.0 + build: h31011fe_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec + md5: 32a94143a7f65d76d2d5da37dcb4ed79 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.34.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + size: 7203 + timestamp: 1734293058849 +- kind: conda + name: uvloop + version: 0.21.0 + build: py311h9ecbd09_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py311h9ecbd09_1.conda + sha256: 9421eeb1e15b99985bb15dec9cf0f337d332106cea584a147449c91c389a4418 + md5: 66890e34ed6a9bd84f1c189043a928f8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: MIT OR Apache-2.0 + size: 677289 + timestamp: 1730214493601 +- kind: conda + name: uvloop + version: 0.21.0 + build: py311ha879c10_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py311ha879c10_1.conda + sha256: 23e592499434da8a721164ab163765152e6f5f94b1a4cea5447803a0cdfd00fb + md5: 744b9c908ae05f4712bf4ea9d98e45c5 + depends: + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: MIT OR Apache-2.0 + size: 643444 + timestamp: 1730214665299 +- kind: conda + name: uvloop + version: 0.21.0 + build: py311hae2e1ce_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py311hae2e1ce_1.conda + sha256: f42e2ca33beedef252d234d3aac7642432bf8545a6d37c11e58a69f6aee36898 + md5: bc9ca85e86e305b58432c4791b732ae6 + depends: + - __osx >=11.0 + - libuv >=1.49.2,<2.0a0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: MIT OR Apache-2.0 + size: 544025 + timestamp: 1730214665776 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py311h0ca61a2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py311h0ca61a2_0.conda + sha256: acf468cfad74bc894c0f7263c7390b7b2dfe7f72c7f698a698caad2deb532c7d + md5: 02d887d09e56a37b7416c2c1da6ef8c9 + depends: + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 404921 + timestamp: 1733998940737 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py311h3ff9189_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py311h3ff9189_0.conda + sha256: 86f7ebe3cfb503fb2c80f040e03d039e3b552ac5893cd8e82225c966bac07c44 + md5: bd5b2b35eb23cfcb48be31527ff49944 + depends: + - __osx >=11.0 + - anyio >=3.0.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 366312 + timestamp: 1733999050046 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py311h9e33e62_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py311h9e33e62_0.conda + sha256: 066f5725b576dca7a1f5dfafca055836a199f9db97435e2ceca84e6296fc8358 + md5: 558a9c46ef5db2f3a6f425228bc77d43 + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 410904 + timestamp: 1733998882176 +- kind: conda + name: websockets + version: '14.1' + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py311h917b07b_0.conda + sha256: dd645f8c0b89df0c56a15a21504057d5e03ec9e782127722172ade4e1d93d8a5 + md5: c0f90c40248823402e4bdf335fab7cde + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 240817 + timestamp: 1731498829166 +- kind: conda + name: websockets + version: '14.1' + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py311h9ecbd09_0.conda + sha256: 38f67b5f2cd27da85198a973cc6113cffe1069190c69f5555fcaa9ed8f8c30bd + md5: 99cff696db893d6f22a379a90cbe90df + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 241556 + timestamp: 1731498753625 +- kind: conda + name: websockets + version: '14.1' + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py311ha879c10_0.conda + sha256: 1a05cabbf96c453627a4950c7e4983025db1499e72fba9e4d0de1b65e23ba7b1 + md5: 1c289239f321e075aee07035087652ce + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + size: 241161 + timestamp: 1731498790453 +- kind: conda + name: wrapt + version: 1.17.0 + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py311h917b07b_0.conda + sha256: fff7f86570f0a3fd90878b75b552bafddb854e8f4d68a171cd427a13e9eb160c + md5: a56950191b7efa9406bbaff925173d20 + depends: + - __osx >=11.0 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-2-Clause + license_family: BSD + size: 62309 + timestamp: 1732524074190 +- kind: conda + name: wrapt + version: 1.17.0 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py311h9ecbd09_0.conda + sha256: 8e9a7a1a69d0d59b3cb0066fbdbf16dc7a0d9554ffc2a365e67eca72230ca3e8 + md5: 452e39fb544b1ec9cc6c5b2ac9c47efa + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: BSD-2-Clause + license_family: BSD + size: 65396 + timestamp: 1732523677157 +- kind: conda + name: wrapt + version: 1.17.0 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py311ha879c10_0.conda + sha256: 33e12fc8188dc0f649ff9fbc3770bff61912722e88ebae806accff72d01ca34f + md5: e2d4e19306f4df6953c7f23d15ab3805 + depends: + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-2-Clause + license_family: BSD + size: 65780 + timestamp: 1732523794589 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 13593 + timestamp: 1734229104321 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 + md5: d5397424399a66d33c80b1f2345a36a6 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 15873 + timestamp: 1734230458294 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 14780 + timestamp: 1734229004433 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + sha256: efcc150da5926cf244f757b8376d96a4db78bc15b8d90ca9f56ac6e75755971f + md5: 25a5a7b797fe6e084e04ffe2db02fc62 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 20615 + timestamp: 1727796660574 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 19901 + timestamp: 1727794976192 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hd74edd7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 + md5: 77c447f48cab5d3a15ac224edb86a968 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 18487 + timestamp: 1727795205022 +- kind: conda + name: xxhash + version: 0.8.2 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + sha256: 4c526aed70b579d80e5c20d32130b6bc8bde59b3250d43c2b5269755f4da8a9b + md5: bb9faf6857108a9f62ebb4dab6ef05da + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 102442 + timestamp: 1689951682147 +- kind: conda + name: xxhash + version: 0.8.2 + build: hb547adb_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + sha256: a70f59f7221ee72c45b39a6b36a33eb9c717ba01921cce1a3c361a4676979a2e + md5: 144cd3b88706507f332f5eb5fb83a33b + license: BSD-2-Clause + license_family: BSD + size: 97593 + timestamp: 1689951969732 +- kind: conda + name: xxhash + version: 0.8.2 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f + md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 97691 + timestamp: 1689951608120 +- kind: conda + name: yaml + version: 0.2.5 + build: h3422bc3_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 + md5: 4bb3f014845110883a3c5ee811fd84b4 + license: MIT + license_family: MIT + size: 88016 + timestamp: 1641347076660 +- kind: conda + name: yaml + version: 0.2.5 + build: h7f98852_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + sha256: a4e34c710eeb26945bdbdaba82d3d74f60a78f54a874ec10d373811a5d217535 + md5: 4cb3ad778ec2d5a7acbdf254eb1c42ae + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 89141 + timestamp: 1641346969816 +- kind: conda + name: yaml + version: 0.2.5 + build: hf897c2e_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + sha256: 8bc601d6dbe249eba44b3c456765265cd8f42ef1e778f8df9b0c9c88b8558d7e + md5: b853307650cb226731f653aa623936a4 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 92927 + timestamp: 1641347626613 +- kind: conda + name: yarl + version: 1.18.3 + build: py311h917b07b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py311h917b07b_0.conda + sha256: 2df31b9adcd55b29985935d0a23ae6069808e319c2c24bbe212cbd3f3dca71ed + md5: 134c0091a508239d35505d9ac74d4c0f + depends: + - __osx >=11.0 + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: Apache + size: 144071 + timestamp: 1733429148299 +- kind: conda + name: yarl + version: 1.18.3 + build: py311h9ecbd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py311h9ecbd09_0.conda + sha256: 4af34cbcf4dda72aad779c8a12eb508aee6f98d0523c26174639a75ae31df180 + md5: 385d54815a5d2e74e68374d77446030b + depends: + - __glibc >=2.17,<3.0.a0 + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: Apache + size: 153749 + timestamp: 1733428888714 +- kind: conda + name: yarl + version: 1.18.3 + build: py311ha879c10_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py311ha879c10_0.conda + sha256: c60d0e75b147dc836b497b2f7c773a2b2998821056614eead6aae84fbedc7416 + md5: 049bc4ea1dd2a1db3a752fadbda1b55c + depends: + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: Apache-2.0 + license_family: Apache + size: 151968 + timestamp: 1733429000649 +- kind: conda + name: zeromq + version: 4.3.5 + build: h3b0a872_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + sha256: a4dc72c96848f764bb5a5176aa93dd1e9b9e52804137b99daeebba277b31ea10 + md5: 3947a35e916fcc6b9825449affbf4214 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 335400 + timestamp: 1731585026517 +- kind: conda + name: zeromq + version: 4.3.5 + build: h5efb499_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + sha256: a6003096dc0570a86492040ba32b04ce7662b159600be2252b7a0dfb9414e21c + md5: f2f3282559a4b87b7256ecafb4610107 + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 371419 + timestamp: 1731589490850 +- kind: conda + name: zeromq + version: 4.3.5 + build: hc1bb282_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + sha256: 9e585569fe2e7d3bea71972cd4b9f06b1a7ab8fa7c5139f92a31cbceecf25a8a + md5: f7e6b65943cb73bce0143737fded08f1 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=18 + - libsodium >=1.0.20,<1.0.21.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 281565 + timestamp: 1731585108039 +- kind: conda + name: zipp + version: 3.21.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 + md5: 0c3cc595284c5e8f0f9900a9b228a332 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 21809 + timestamp: 1732827613585 +- kind: conda + name: zstandard + version: 0.23.0 + build: py311ha60cc69_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py311ha60cc69_1.conda + sha256: d2f2f1a408e2353fc61d2bf064313270be2260ee212fe827dcf3cfd3754f1354 + md5: 29d320d6450b2948740a9be3761b2e9d + depends: + - __osx >=11.0 + - cffi >=1.11 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 332271 + timestamp: 1725305847224 +- kind: conda + name: zstandard + version: 0.23.0 + build: py311hbc35293_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py311hbc35293_1.conda + sha256: a5cf0eef1ffce0d710eb3dffcb07d9d5922d4f7a141abc96f6476b98600f718f + md5: aec590674ba365e50ae83aa2d6e1efae + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 417923 + timestamp: 1725305669690 +- kind: conda + name: zstandard + version: 0.23.0 + build: py311hd5293d8_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py311hd5293d8_1.conda + sha256: 44c4c8e718f7f50c985d9b3de23760fb01987e6307301eef0bcfc26862094690 + md5: 7a022310d8759b7d251717b09242ee13 + depends: + - cffi >=1.11 + - libgcc >=13 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 391826 + timestamp: 1725305804278 +- kind: conda + name: zstd + version: 1.5.6 + build: h02f22dd_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c + md5: be8d5f8cf21aed237b8b182ea86b3dd6 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 539937 + timestamp: 1714723130243 +- kind: conda + name: zstd + version: 1.5.6 + build: ha6fb4c9_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 554846 + timestamp: 1714722996770 +- kind: conda + name: zstd + version: 1.5.6 + build: hb46c0d2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda + sha256: 2d4fd1ff7ee79cd954ca8e81abf11d9d49954dd1fef80f27289e2402ae9c2e09 + md5: d96942c06c3e84bfcc5efb038724a7fd + depends: + - __osx >=11.0 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 405089 + timestamp: 1714723101397 diff --git a/examples/notebooks/magic.lock b/examples/notebooks/magic.lock new file mode 100644 index 0000000000..e0527ce6e6 --- /dev/null +++ b/examples/notebooks/magic.lock @@ -0,0 +1,10972 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py312h66e93f0_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.16.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.11-py312h2ec8cdc_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-8.30.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py312h7900ff3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2024.10.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhff2d567_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.2-py312h1d6d2e6_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-6.1.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.22.3-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.6.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20241206-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py312hb2c0f52_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.16.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.11-py312h6f74592_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-8.30.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py312h996f985_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2024.10.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhff2d567_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.2-py312h14eacfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-6.1.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.22.3-py312ha4e36d7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.6.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20241206-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-21.2.0-py312h024a12e_5.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.16.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.11-py312hd8f9ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh57ce528_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-8.30.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jsonpointer-3.0.0-py312h81bd7bf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2024.10.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhff2d567_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.2-py312h8ae5369_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-6.1.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-10.3.2-py312hb9d441b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-10.3.2-py312hb9d441b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.22.3-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh31c8845_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.6.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh31c8845_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20241206-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda +packages: +- kind: conda + name: _libgcc_mutex + version: '0.1' + build: conda_forge + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + size: 2562 + timestamp: 1578324546067 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23621 + timestamp: 1650670423406 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 + md5: 6168d71addc746e8f2b8d57dfd2edcea + depends: + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23712 + timestamp: 1650670790230 +- kind: conda + name: aiohappyeyeballs + version: 2.4.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + sha256: 95d4713e49ea92ae50cf42393683ede706b7875af5f7cb14c253438180afa732 + md5: 296b403617bafa89df4971567af79013 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 19351 + timestamp: 1733332029649 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h178313f_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + sha256: dc8ebdd99e9d7a07454a7063a295cdc7a86264648647fec10b2ceae97478e200 + md5: 3e92784b8e32ab7d0b95ee296ba79a99 + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 914378 + timestamp: 1733839626367 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h998013c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + sha256: 69eb9c89dce6a7ae960099172daffba9f77fef39344f37e581685a8e3c5debe6 + md5: 642356223364539ba7ba36556fcf49ee + depends: + - __osx >=11.0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 874135 + timestamp: 1733839113411 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312hcc812fe_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + sha256: df694a9fec546e575a4ea7e1c3ac476c0bda53c0fad44c046ad3ebdd5b75a0a8 + md5: a8c9ec59e6323b38418bbf04deaa0c02 + depends: + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 900931 + timestamp: 1733839037447 +- kind: conda + name: aiosignal + version: 1.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + sha256: 7de8ced1918bbdadecf8e1c1c68237fe5709c097bd9e0d254f4cad118f4345d0 + md5: 1a3981115a398535dbe3f6d5faae3d36 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 13229 + timestamp: 1734342253061 +- kind: conda + name: annotated-types + version: 0.7.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- kind: conda + name: anyio + version: 4.7.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + sha256: 687537ee3af30f8784986bf40cac30e88138770b16e51ca9850c9c23c09aeba1 + md5: c88107912954a983c2caf25f7fd55158 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.9 + - sniffio >=1.1 + - typing_extensions >=4.5 + constrains: + - trio >=0.26.1 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 112730 + timestamp: 1733532678437 +- kind: conda + name: appnope + version: 0.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda + sha256: 8f032b140ea4159806e4969a68b4a3c0a7cab1ad936eb958a2b5ffe5335e19bf + md5: 54898d0f524c9dee622d44bbb081a8ab + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 10076 + timestamp: 1733332433806 +- kind: conda + name: argon2-cffi + version: 23.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-23.1.0-pyhd8ed1ab_1.conda + sha256: 7af62339394986bc470a7a231c7f37ad0173ffb41f6bc0e8e31b0be9e3b9d20f + md5: a7ee488b71c30ada51c48468337b85ba + depends: + - argon2-cffi-bindings + - python >=3.9 + - typing-extensions + constrains: + - argon2_cffi ==999 + license: MIT + license_family: MIT + size: 18594 + timestamp: 1733311166338 +- kind: conda + name: argon2-cffi-bindings + version: 21.2.0 + build: py312h024a12e_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-21.2.0-py312h024a12e_5.conda + sha256: 0e32ddd41f273f505956254d81ffadaf982ed1cb7dfd70d9251a8c5b705c7267 + md5: 6ccaeafe1a52b0d0e7ebfbf53a374649 + depends: + - __osx >=11.0 + - cffi >=1.0.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 32838 + timestamp: 1725356954187 +- kind: conda + name: argon2-cffi-bindings + version: 21.2.0 + build: py312h66e93f0_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py312h66e93f0_5.conda + sha256: 3cbc3b026f5c3f26de696ead10607db8d80cbb003d87669ac3b02e884f711978 + md5: 1505fc57c305c0a3174ea7aae0a0db25 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.0.1 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 34847 + timestamp: 1725356749774 +- kind: conda + name: argon2-cffi-bindings + version: 21.2.0 + build: py312hb2c0f52_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/argon2-cffi-bindings-21.2.0-py312hb2c0f52_5.conda + sha256: a1a0e246c70b738e20dc01785e6bc0e497c7dfc8e586d1db142e7d77f80e0dfa + md5: c3b818a44ce51af3de80cf6523cfe216 + depends: + - cffi >=1.0.1 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 36280 + timestamp: 1725356972478 +- kind: conda + name: arrow + version: 1.3.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda + sha256: c4b0bdb3d5dee50b60db92f99da3e4c524d5240aafc0a5fcc15e45ae2d1a3cd1 + md5: 46b53236fdd990271b03c3978d4218a9 + depends: + - python >=3.9 + - python-dateutil >=2.7.0 + - types-python-dateutil >=2.8.10 + license: Apache-2.0 + license_family: Apache + size: 99951 + timestamp: 1733584345583 +- kind: conda + name: asttokens + version: 3.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.0-pyhd8ed1ab_1.conda + sha256: 93b14414b3b3ed91e286e1cbe4e7a60c4e1b1c730b0814d1e452a8ac4b9af593 + md5: 8f587de4bcf981e26228f268df374a9b + depends: + - python >=3.9 + constrains: + - astroid >=2,<4 + license: Apache-2.0 + license_family: Apache + size: 28206 + timestamp: 1733250564754 +- kind: conda + name: async-lru + version: 2.0.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.0.4-pyhd8ed1ab_1.conda + sha256: 344157f396dfdc929d1dff8fe010abe173cd168d22a56648583e616495f2929e + md5: 40c673c7d585623b8f1ee650c8734eb6 + depends: + - python >=3.9 + - typing_extensions >=4.0.0 + license: MIT + license_family: MIT + size: 15318 + timestamp: 1733584388228 +- kind: conda + name: attrs + version: 24.3.0 + build: pyh71513ae_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + sha256: 750186af694a7130eaf7119fbb56db0d2326d8995ad5b8eae23c622b85fea29a + md5: 356927ace43302bf6f5926e2a58dae6a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 56354 + timestamp: 1734348889193 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h2cb9fb3_15 + build_number: 15 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + sha256: 4ce859dc9ff128bf5515604c43f33fb511386022fc9765ca077990f2a3f23df5 + md5: e524686ace966acefb5b8cbc6e8b3daa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 111854 + timestamp: 1734021745104 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h8bc59a9_15 + build_number: 15 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + sha256: 0e41e56b662e76e024182adebcd91d09a4d38a83b35217c84e4967354dfff9a2 + md5: f688b8893c20ad9477a19e7ce614014a + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + license: Apache-2.0 + license_family: Apache + size: 92507 + timestamp: 1734021831330 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: hb921021_15 + build_number: 15 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + sha256: 537006ad6d5097c134494166a6a1dc1451d5d050878d7b82cef498bfda40ba8a + md5: c79d50f64cffa5ad51ecc1a81057962f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 107614 + timestamp: 1734021692519 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h1a47875_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + sha256: 095ac824ea9303eff67e04090ae531d9eb33d2bf8f82eaade39b839c421e16e8 + md5: 55a8561fdbbbd34f50f57d9be12ed084 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 47601 + timestamp: 1733991564405 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h740c5af_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + sha256: c5c7961d48ca7320ed3560c036f7aa5244df4b85d9657f70aacc5faba3e1509a + md5: 57ed2c445d7ef01d121b9bcea0522913 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 50036 + timestamp: 1733991581303 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: hc8a0bd2_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + sha256: 1f44be36e1daa17b4b081debb8aee492d13571084f38b503ad13e869fef24fe4 + md5: 8b0ce61384e5a33d2b301a64f3d22ac5 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 39925 + timestamp: 1733991649383 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a + md5: 145e5b4c9702ed279d7d68aaf096f77d + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221863 + timestamp: 1733975576886 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + sha256: 57288ec5df35781bea8fc6a8c9099cad6695b747784fc1b8862a0f9e5b3bf5ab + md5: fef806a0f6de853670c746bbece01966 + depends: + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 259031 + timestamp: 1733975520465 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 + md5: d7d4680337a14001b0e043e96529409b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 236574 + timestamp: 1733975453350 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h0f0193d_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + sha256: 3f05d19f68ef800f33d44ea2a4211003124076516c8469abc7d432236344df53 + md5: 3a1421d12435df5b4c412cc4c8fac64d + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19740 + timestamp: 1733991625201 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h4e1184b_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b + md5: 3f4c1197462a6df2be6dc8241828fe93 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19086 + timestamp: 1733991637424 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: hc8a0bd2_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + sha256: 47b2813f652ce7e64ac442f771b2a5f7d4af4ad0d07ff51f6075ea80ed2e3f09 + md5: a8b6c17732d14ed49d0e9b59c43186bc + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 18068 + timestamp: 1733991869211 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h54f970a_11 + build_number: 11 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d + md5: ba41238f8e653998d7d2f42e3a8db054 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 47078 + timestamp: 1734024749727 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h7959bf6_11 + build_number: 11 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + sha256: 10d7240c7db0c941fb1a59c4f8ea6689a434b03309ee7b766fa15a809c553c02 + md5: 9b3fb60fe57925a92f399bc3fc42eccf + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 54003 + timestamp: 1734024480949 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: hcbd8f92_11 + build_number: 11 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + sha256: 79aa363c71c891a27496c0498f8d498b2ddc87b3ccb3b6c9da5b50b05936ebb8 + md5: e0772c59af4243a9b2565baa5d79e5b6 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 55207 + timestamp: 1734024546663 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h3df160d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + sha256: 3a1d2d332945306be9d49e569b95e4cc172d825f10e88715513a172f28ebb59e + md5: 28f00aa7fd9556c4c461328cf146c20b + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 190586 + timestamp: 1734008442362 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h96aa502_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + sha256: 22e4737c8a885995b7c1ae1d79c1f6e78d489e16ec079615980fdde067aeaf76 + md5: 495c93a4f08b17deb3c04894512330e6 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 152983 + timestamp: 1734008451473 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: hefd7a92_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 + md5: 5ce4df662d32d3123ea8da15571b6f51 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 197731 + timestamp: 1734008380764 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h1a307af_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + sha256: 71f5bf891299f831dceaea12f926c393bf754569e5305387a88b77e1f94612d8 + md5: da8ab0f3eeac93449ec3d531ede92caa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 161889 + timestamp: 1734433686109 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h831e299_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + sha256: 5920009b1c6f9a2bc131a36725251894e4b4773fce29c4b1065d4213ae337abe + md5: 80dd9f0ddf935290d1dc00ec75ff3023 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 157864 + timestamp: 1734433578570 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: haba67d1_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + sha256: c0a1a2b0750225ac3dc07fd258c88c2be866bf8ac67ba3d50bb4ecec852ff8ee + md5: 4c5ff4134e76426a75b8c548984fa933 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 135729 + timestamp: 1734433832730 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h11f4f37_12 + build_number: 12 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + sha256: 512d3969426152d9d5fd886e27b13706122dc3fa90eb08c37b0d51a33d7bb14a + md5: 96c3e0221fa2da97619ee82faa341a73 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 194672 + timestamp: 1734025626798 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h24f418c_12 + build_number: 12 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd + md5: c072045a6206f88015d02fcba1705ea1 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 134371 + timestamp: 1734025379525 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h5f50e26_12 + build_number: 12 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + sha256: ffeb9100cc8fd4093e1a6fdfd938bc4a396dd77480b7fb17aa42855a4d5e2c70 + md5: 031ca33115d4b1eeb43f435d6215778c + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 169516 + timestamp: 1734025167885 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h1be5864_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + sha256: 22966164d63808689fffd35945f57756c95337327e28099b5d77b29fc6a56ecc + md5: a37bba7acb62dd70492ee01eacca3b8f + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + license: Apache-2.0 + license_family: Apache + size: 97598 + timestamp: 1734146239038 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h2080895_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + sha256: 20bc2dd60e6518d9b8215c2b652ab5c52ee8a997d3b9a5f69e2dabd28cbf26b2 + md5: ae223efa63fbb4262a2d85c3ab3bc4f5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 117641 + timestamp: 1734146239779 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: hf454442_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + sha256: c2f205a7bf64c5f40eea373b3a0a7c363c9aa9246a13dd7f3d9c6a4434c4fe2d + md5: 947c82025693bebd557f782bb5d6b469 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 114156 + timestamp: 1734146123386 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + sha256: ede8e782467c87ac80ceb9c9af9e917d121b7d8b8c698186d18e3cecd36f2210 + md5: 53e798d720dd78b78847a7b2fdb05fc9 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 58621 + timestamp: 1733994421495 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + sha256: df586f42210af1134b1c88ff4c278c3cb6d6c807c84eac48860062464b28554d + md5: a5126a90e74ac739b00564a4c7ddcc36 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 56094 + timestamp: 1733994449690 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + sha256: de98343ce42d2e569b3380292d20f47bf39bda08aadabcbb8e650d3f38fd742f + md5: 22f72f8cd7ead211304ac17d337d96e0 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 49664 + timestamp: 1733994553014 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da + md5: 3bd35b0adab3d743f09e0252cc441d6b + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72154 + timestamp: 1733994384415 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + sha256: 1ed9a332d06ad595694907fad2d6d801082916c27cd5076096fda4061e6d24a8 + md5: 74e8c3e4df4ceae34aa2959df4b28101 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72762 + timestamp: 1733994347547 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + sha256: 215086d95e8ff1d3fcb0197ada116cc9d7db1fdae7573f5e810d20fa9215b47c + md5: e70e88a357a3749b67679c0788c5b08a + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 70186 + timestamp: 1733994496998 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h19a973c_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + sha256: 8269e6746eb3a5d15b732a3983888bf98dfc1f6594e95250fc8d16b43cfd5ff9 + md5: 95714136bef3e917bd5a2942d4682b20 + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 236249 + timestamp: 1734178020924 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h8a4e35f_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + sha256: 5ba9188e0cb4e3faff9bc96774febb040aa3b802aedba29d847e00e7b5eab84e + md5: d77a9e3d7ce15399903e92825fd651b5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 283154 + timestamp: 1734177845248 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: hd92328a_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + sha256: 094cd81f1e5ba713e9e7a272ee52b5dde3ccc4842ea90f19c0354a00bbdac3d9 + md5: 02b95564257d5c3db9c06beccf711f95 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 354703 + timestamp: 1734177883319 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: h849ce1a_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + sha256: 51b9e9df8cbab4a13a1b9d39d6ef5ed162aaa29c09a745810e00bbe92e1045c1 + md5: cda7747f4398be8d1fb37362815917a7 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2920625 + timestamp: 1734093552712 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: hc430e4a_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + sha256: 2dc09f6f9c49127b5f96e7535b64a9c521b944d76d8b7d03d48ae80257ac1cea + md5: aeefac461bea1f126653c1285cf5af08 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 3060561 + timestamp: 1734093737431 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: he0ff2e4_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + sha256: 535b970aaa13be45f8cab8205c59f044b17364111c41a227f061775a5c834e18 + md5: 0981ed87098b149bdb7d99a4a3fd0e58 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2826534 + timestamp: 1734094018287 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h1887c18_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 + md5: e0c3a906a41be769f0ae20ca3e31cfc0 + depends: + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 338650 + timestamp: 1728055589907 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h5cfcd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a + md5: 0a8838771cc2e985cd295e01ae83baf1 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 345117 + timestamp: 1728053909574 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: hd50102c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + sha256: f5b91329ed59ffc0be8747784c6e4cc7e56250c54032883a83bc11808ef6a87e + md5: f093a11dcf3cdcca010b20a818fcc6dc + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 294299 + timestamp: 1728054014060 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h113e628_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + sha256: 286b31616c191486626cb49e9ceb5920d29394b9e913c23adb7eb637629ba4de + md5: 73f73f60854f325a55f1d31459f2ab73 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 232351 + timestamp: 1728486729511 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h47b0b28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + sha256: 1c72423b9beba167d2f01b80dc204da77240a8266f1edb3d89510c852b300d69 + md5: 94e73a7877743a85c57091d8afab2348 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 217132 + timestamp: 1728488096615 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: hc602bab_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + sha256: bde446b916fff5150606f8ed3e6058ffc55a3aa72381e46f1ab346590b1ae40a + md5: d7b71593a937459f2d4b67e1a4727dc2 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 166907 + timestamp: 1728486882502 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h185ecfd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 + md5: 221e1e5ecb2643e113f32b3229d5ba33 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 502934 + timestamp: 1728580241002 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h3cf044e_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 + md5: 7eb66060455c7a47d9dcdbfa9f46579b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 549342 + timestamp: 1728578123088 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h7585a09_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + sha256: 08d52d130addc0fb55d5ba10d9fa483e39be25d69bac7f4c676c2c3069207590 + md5: 704238ef05d46144dae2e6b5853df8bc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 438636 + timestamp: 1728578216193 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h1b94036_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 + md5: 793b1080ab2d958980f137a8643cd6e8 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 140832 + timestamp: 1728565334900 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h736e048_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba + md5: 13de36be8de3ae3f05ba127631599213 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 149312 + timestamp: 1728563338704 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h9ca1f76_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + sha256: 77ab04e8fe5636a2de9c718f72a43645f7502cd208868c8a91ffba385547d585 + md5: 7a187cd7b1445afc80253bb186a607cc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 121278 + timestamp: 1728563418777 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: h37d6d07_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc + md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 260547 + timestamp: 1728730924071 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: ha633028_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 + md5: 7c1980f89dd41b097549782121a73490 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 287366 + timestamp: 1728729530295 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: hcdd55da_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + sha256: f48523f8aa0b5b80f45a92f0556b388dd96f44ac2dc2f44a01d08c1822eec97d + md5: c49fbc5233fcbaa86391162ff1adef38 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 196032 + timestamp: 1728729672889 +- kind: conda + name: babel + version: 2.16.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/babel-2.16.0-pyhd8ed1ab_1.conda + sha256: f6205d3a62e87447e06e98d911559be0208d824976d77ab092796c9176611fcb + md5: 3e23f7db93ec14c80525257d8affac28 + depends: + - python >=3.9 + - pytz >=2015.7 + license: BSD-3-Clause + license_family: BSD + size: 6551057 + timestamp: 1733236466015 +- kind: conda + name: backoff + version: 2.2.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 + md5: a38b801f2bcc12af80c2e02a9e4ce7d9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 18816 + timestamp: 1733771192649 +- kind: conda + name: beautifulsoup4 + version: 4.12.3 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_1.conda + sha256: fca842ab7be052eea1037ebee17ac25cc79c626382dd2187b5c6e007b9d9f65f + md5: d48f7e9fdec44baf6d1da416fe402b04 + depends: + - python >=3.9 + - soupsieve >=1.2 + license: MIT + license_family: MIT + size: 118042 + timestamp: 1733230951790 +- kind: conda + name: bleach + version: 6.2.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyhd8ed1ab_1.conda + sha256: ffc8e4e53cd92aec0f0ea0bc9e28f5fd1b1e67bde46b0b298170e6fb78eecce1 + md5: 707af59db75b066217403a8f00c1d826 + depends: + - python >=3.9 + - webencodings + license: Apache-2.0 AND MIT + license_family: Apache + size: 132933 + timestamp: 1733302409510 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h2ec8cdc_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f + md5: b0b867af6fc74b2a0aa206da29c0f3cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + size: 349867 + timestamp: 1725267732089 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h6f74592_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + sha256: 9736bf660a0e4260c68f81d2635b51067f817813e6490ac9e8abd9a835dcbf6d + md5: e1e9727063057168d95f27a032acd0a4 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 h86ecc28_2 + license: MIT + license_family: MIT + size: 356878 + timestamp: 1725267878508 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312hde4cb15_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + sha256: 254b411fa78ccc226f42daf606772972466f93e9bc6895eabb4cfda22f5178af + md5: a83c2ef76ccb11bc2349f4f17696b15d + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 339360 + timestamp: 1725268143995 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h4bc722e_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 252783 + timestamp: 1720974456583 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h68df207_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + sha256: 2258b0b33e1cb3a9852d47557984abb6e7ea58e3d7f92706ec1f8e879290c4cb + md5: 56398c28220513b9ea13d7b450acfb20 + depends: + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 189884 + timestamp: 1720974504976 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h99b78c6_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 122909 + timestamp: 1720974522888 +- kind: conda + name: c-ares + version: 1.34.4 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f + md5: c1c999a38a4303b29d75c636eaa13cf9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 179496 + timestamp: 1734208291879 +- kind: conda + name: c-ares + version: 1.34.4 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + sha256: 1187a41d4bb2afe02cb18690682edc98d1e9f5e0ccda638d8704a75ea1875bbe + md5: 356da36f35d36dcba16e43f1589d4e39 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 215979 + timestamp: 1734208193181 +- kind: conda + name: c-ares + version: 1.34.4 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 + md5: e2775acf57efd5af15b8e3d1d74d72d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 206085 + timestamp: 1734208189009 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hbcca054_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + sha256: 1afd7274cbc9a334d6d0bc62fa760acc7afdaceb0b91a8df370ec01fd75dc7dd + md5: 720523eb0d6a9b0f6120c16b2aa4e7de + license: ISC + size: 157088 + timestamp: 1734208393264 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hcefe29a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + sha256: ad7b43211051332a5a4e788bb4619a2d0ecb5be73e0f76be17f733a87d7effd1 + md5: 83b4ad1e6dc14df5891f3fcfdeb44351 + license: ISC + size: 157096 + timestamp: 1734209301744 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hf0a4a13_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + sha256: 256be633fd0882ccc1a7a32bc278547e1703f85082c0789a87a603ee3ab8fb82 + md5: 7cb381a6783d91902638e4ed1ebd478e + license: ISC + size: 157091 + timestamp: 1734208344343 +- kind: conda + name: cached-property + version: 1.5.2 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 + md5: 9b347a7ec10940d3f7941ff6c460b551 + depends: + - cached_property >=1.5.2,<1.5.3.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4134 + timestamp: 1615209571450 +- kind: conda + name: cached_property + version: 1.5.2 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 + md5: 576d629e47797577ab0f1b351297ef4a + depends: + - python >=3.6 + license: BSD-3-Clause + license_family: BSD + size: 11065 + timestamp: 1615209567874 +- kind: conda + name: certifi + version: 2024.12.14 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + sha256: 048c16a9cbcb1fbad02083414d3bc7c1d0eea4b39aee6aa6bf8d1d5089ca8bad + md5: 6feb87357ecd66733be3279f16a8c400 + depends: + - python >=3.9 + license: ISC + size: 161642 + timestamp: 1734380604767 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h06ac9bb_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 + md5: a861504bbea4161a9170b85d4d2be840 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 294403 + timestamp: 1725560714366 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h0fad829_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f + md5: 19a5456f72f505881ba493979777b24e + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 281206 + timestamp: 1725560813378 +- kind: conda + name: cffi + version: 1.17.1 + build: py312hac81daf_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + sha256: 1162e3ca039e7ca7c0e78f0a020ed1bde968096841b663e3f393c966eb82f0f0 + md5: 1a256e5581b1099e9295cb84d53db3ea + depends: + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 312892 + timestamp: 1725561779888 +- kind: conda + name: charset-normalizer + version: 3.4.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + sha256: 63022ee2c6a157a9f980250a66f54bdcdf5abee817348d0f9a74c2441a6fbf0e + md5: 6581a17bba6b948bb60130026404a9d6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 47533 + timestamp: 1733218182393 +- kind: conda + name: click + version: 8.1.7 + build: unix_pyh707e725_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + sha256: 1cd5fc6ccdd5141378e51252a7a3810b07fd5a7e6934a5b4a7eccba66566224b + md5: cb8e52f28f5e592598190c562e7b5bf1 + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 84513 + timestamp: 1733221925078 +- kind: conda + name: colorama + version: 0.4.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- kind: conda + name: comm + version: 0.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.2-pyhd8ed1ab_1.conda + sha256: 7e87ef7c91574d9fac19faedaaee328a70f718c9b4ddadfdc0ba9ac021bd64af + md5: 74673132601ec2b7fc592755605f4c1b + depends: + - python >=3.9 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 12103 + timestamp: 1733503053903 +- kind: conda + name: datasets + version: 2.14.4 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + sha256: 7e09bd083a609138b780fcc4535924cb96814d2c908a36d4c64a2ba9ee3efe7f + md5: 3e087f072ce03c43a9b60522f5d0ca2f + depends: + - aiohttp + - dill >=0.3.0,<0.3.8 + - fsspec >=2021.11.1 + - huggingface_hub >=0.14.0,<1.0.0 + - importlib-metadata + - multiprocess + - numpy >=1.17 + - packaging + - pandas + - pyarrow >=8.0.0 + - python >=3.8.0 + - python-xxhash + - pyyaml >=5.1 + - requests >=2.19.0 + - tqdm >=4.62.1 + license: Apache-2.0 + license_family: Apache + size: 347303 + timestamp: 1691593908658 +- kind: conda + name: debugpy + version: 1.8.11 + build: py312h2ec8cdc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.11-py312h2ec8cdc_0.conda + sha256: 3d800be438a76d8a636219afd63a617737729867af5800d50fc72e71ac4f27f1 + md5: 0235a6da7d128c7e068973c4de62fc7b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 2668691 + timestamp: 1734159098550 +- kind: conda + name: debugpy + version: 1.8.11 + build: py312h6f74592_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/debugpy-1.8.11-py312h6f74592_0.conda + sha256: 8c5f73ea1ef9e88906968b9639be89d861b66aa48c132ec7565405293ca09f90 + md5: 3230587917725d0affd61674e74583d2 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 2609965 + timestamp: 1734159267844 +- kind: conda + name: debugpy + version: 1.8.11 + build: py312hd8f9ff3_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.11-py312hd8f9ff3_0.conda + sha256: c219e3ba0cf97fdd9fa3d8601f8d37a7fe584cc2f31e199a820fa005649871ea + md5: 0f4c9c498b7ca4f010f7de44463c5403 + depends: + - __osx >=11.0 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 2517686 + timestamp: 1734159183809 +- kind: conda + name: decorator + version: 5.1.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_1.conda + sha256: 84e5120c97502a3785e8c3241c3bf51f64b4d445f13b4d2445db00d9816fe479 + md5: d622d8d7ee8868870f9cbe259f381181 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 14068 + timestamp: 1733236549190 +- kind: conda + name: defusedxml + version: 0.7.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 + sha256: 9717a059677553562a8f38ff07f3b9f61727bd614f505658b0a5ecbcf8df89be + md5: 961b3a227b437d82ad7054484cfa71b2 + depends: + - python >=3.6 + license: PSF-2.0 + license_family: PSF + size: 24062 + timestamp: 1615232388757 +- kind: conda + name: deprecated + version: 1.2.15 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + sha256: a20ebf2c9b02a6eb32412ceb5c4cffaae49417db7e75414a76417538293a9402 + md5: eaef2e94d5bd76f758545d172c1fda67 + depends: + - python >=3.9 + - wrapt <2,>=1.10 + license: MIT + license_family: MIT + size: 14297 + timestamp: 1733662697343 +- kind: conda + name: dill + version: 0.3.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + sha256: 4ff20c6be028be2825235631c45d9e4a75bca1de65f8840c02dfb28ea0137c45 + md5: 5e4f3466526c52bc9af2d2353a1460bd + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + size: 87553 + timestamp: 1690101185422 +- kind: conda + name: dnspython + version: 2.7.0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 + md5: 5fbd60d61d21b4bd2f9d7a48fe100418 + depends: + - python >=3.9,<4.0.0 + - sniffio + constrains: + - aioquic >=1.0.0 + - wmi >=1.5.1 + - httpx >=0.26.0 + - trio >=0.23 + - cryptography >=43 + - httpcore >=1.0.0 + - idna >=3.7 + - h2 >=4.1.0 + license: ISC + license_family: OTHER + size: 172172 + timestamp: 1733256829961 +- kind: conda + name: email-validator + version: 2.2.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + sha256: b91a19eb78edfc2dbb36de9a67f74ee2416f1b5273dd7327abe53f2dbf864736 + md5: da16dd3b0b71339060cd44cb7110ddf9 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.9 + license: Unlicense + size: 44401 + timestamp: 1733300827551 +- kind: conda + name: email_validator + version: 2.2.0 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + sha256: e0d0fdf587aa0ed0ff08b2bce3ab355f46687b87b0775bfba01cc80a859ee6a2 + md5: 0794f8807ff2c6f020422cacb1bd7bfa + depends: + - email-validator >=2.2.0,<2.2.1.0a0 + license: Unlicense + size: 6552 + timestamp: 1733300828176 +- kind: conda + name: entrypoints + version: '0.4' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_1.conda + sha256: 80f579bfc71b3dab5bef74114b89e26c85cb0df8caf4c27ab5ffc16363d57ee7 + md5: 3366592d3c219f2731721f11bc93755c + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 11259 + timestamp: 1733327239578 +- kind: conda + name: exceptiongroup + version: 1.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 + md5: a16662747cdeb9abbac74d0057cc976e + depends: + - python >=3.9 + license: MIT and PSF-2.0 + size: 20486 + timestamp: 1733208916977 +- kind: conda + name: executing + version: 2.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/executing-2.1.0-pyhd8ed1ab_1.conda + sha256: 28d25ea375ebab4bf7479228f8430db20986187b04999136ff5c722ebd32eb60 + md5: ef8b5fca76806159fc25b4f48d8737eb + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 28348 + timestamp: 1733569440265 +- kind: conda + name: fastapi + version: 0.115.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + sha256: d7826d537c667093c9de96411a09585a8d620c84a830a0195e58e9a0df45f018 + md5: 1b1e0c97830cdf75f1f371bd467ab657 + depends: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.5 + - httpx >=0.23.0 + - jinja2 >=2.11.2 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python >=3.9 + - python-multipart >=0.0.7 + - starlette >=0.40.0,<0.42.0 + - typing_extensions >=4.8.0 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + size: 73084 + timestamp: 1733362427885 +- kind: conda + name: fastapi-cli + version: 0.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 + md5: d960e0ea9e1c561aa928f6c4439f04c7 + depends: + - python >=3.9 + - rich-toolkit >=0.11.1 + - typer >=0.12.3 + - uvicorn-standard >=0.15.0 + license: MIT + license_family: MIT + size: 15546 + timestamp: 1734302408607 +- kind: conda + name: filelock + version: 3.16.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + sha256: 18dca6e2194732df7ebf824abaefe999e4765ebe8e8a061269406ab88fc418b9 + md5: d692e9ba6f92dc51484bf3477e36ce7c + depends: + - python >=3.9 + license: Unlicense + size: 17441 + timestamp: 1733240909987 +- kind: conda + name: fqdn + version: 1.5.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda + sha256: 2509992ec2fd38ab27c7cdb42cf6cadc566a1cc0d1021a2673475d9fa87c6276 + md5: d3549fd50d450b6d9e7dddff25dd2110 + depends: + - cached-property >=1.3.0 + - python >=3.9,<4 + license: MPL-2.0 + license_family: MOZILLA + size: 16705 + timestamp: 1733327494780 +- kind: conda + name: freetype + version: 2.12.1 + build: h267a509_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + sha256: b2e3c449ec9d907dd4656cb0dc93e140f447175b125a3824b31368b06c666bb6 + md5: 9ae35c3d96db2c94ce0cef86efdfa2cb + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 634972 + timestamp: 1694615932610 +- kind: conda + name: freetype + version: 2.12.1 + build: hadb7bae_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 + md5: e6085e516a3e304ce41a8ee08b9b89ad + depends: + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 596430 + timestamp: 1694616332835 +- kind: conda + name: freetype + version: 2.12.1 + build: hf0a5ef3_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + sha256: 7af93030f4407f076dce181062360efac2cd54dce863b5d7765287a6f5382537 + md5: a5ab74c5bd158c3d5532b66d8d83d907 + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 642092 + timestamp: 1694617858496 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + sha256: 44d6d6b332421e621c029fb149f12dba1ccb5ed6ac632e2e807a9d92d6cb2864 + md5: 7960352935cc95ac23883c9b8c97f2ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 53366 + timestamp: 1729699762631 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + sha256: 7e0c12983b20f2816b3712729b5a35ecb7ee152132ca7cf805427c62395ea823 + md5: f98e36c96b2c66d9043187179ddb04f4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 60968 + timestamp: 1729699568442 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + sha256: b0a9ff3e71452eed70877b2f3175d41cd85070da6deac381c5f3f61e1f19bccb + md5: 62fc11b0738ca15e0dd19b60cf280d12 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 59967 + timestamp: 1729699642726 +- kind: conda + name: fsspec + version: 2024.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + sha256: 790a50b4f94042951518f911a914a886a837c926094c6a14ed1d9d03ce336807 + md5: 906fe13095e734cb413b57a49116cdc8 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 134726 + timestamp: 1733493445080 +- kind: conda + name: gflags + version: 2.2.2 + build: h5888daf_1005 + build_number: 1005 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 119654 + timestamp: 1726600001928 +- kind: conda + name: gflags + version: 2.2.2 + build: h5ad3122_1005 + build_number: 1005 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + sha256: 28fe6b40b20454106d5e4ef6947cf298c13cc72a46347bbc49b563cd3a463bfa + md5: 4ff634d515abbf664774b5e1168a9744 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 106638 + timestamp: 1726599967617 +- kind: conda + name: gflags + version: 2.2.2 + build: hf9b8971_1005 + build_number: 1005 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + size: 82090 + timestamp: 1726600145480 +- kind: conda + name: glog + version: 0.7.1 + build: h468a4a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 + md5: 08940a32c6ced3703d1412dd37df4f62 + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 145811 + timestamp: 1718284208668 +- kind: conda + name: glog + version: 0.7.1 + build: hbabe93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 143452 + timestamp: 1718284177264 +- kind: conda + name: glog + version: 0.7.1 + build: heb240a5_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + size: 112215 + timestamp: 1718284365403 +- kind: conda + name: googleapis-common-protos + version: 1.66.0 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + sha256: d8d19575a827f2c62500949b9536efdd6b5406c9f546a73b6a87ac90b03a5875 + md5: 4861e30ff0cd566ea6fb4593e3b7c22a + depends: + - protobuf >=3.20.2,<6.0.0.dev0,!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 116522 + timestamp: 1731459019854 +- kind: conda + name: h11 + version: 0.14.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 + md5: 7ee49e89531c0dcbba9466f6d115d585 + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 51846 + timestamp: 1733327599467 +- kind: conda + name: h2 + version: 4.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + sha256: 843ddad410c370672a8250470697027618f104153612439076d4d7b91eeb7b5c + md5: 825927dc7b0f287ef8d4d0011bb113b1 + depends: + - hpack >=4.0,<5 + - hyperframe >=6.0,<7 + - python >=3.9 + license: MIT + license_family: MIT + size: 52000 + timestamp: 1733298867359 +- kind: conda + name: hpack + version: 4.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + sha256: ec89b7e5b8aa2f0219f666084446e1fb7b54545861e9caa892acb24d125761b5 + md5: 2aa5ff7fa34a81b9196532c84c10d865 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 29412 + timestamp: 1733299296857 +- kind: conda + name: httpcore + version: 1.0.7 + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df + md5: 2ca8e6dbc86525c8b95e3c0ffa26442e + depends: + - python >=3.8 + - h11 >=0.13,<0.15 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=3.0,<5.0 + - certifi + license: BSD-3-Clause + license_family: BSD + size: 48959 + timestamp: 1731707562362 +- kind: conda + name: httptools + version: 0.6.4 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + sha256: 621e7e050b888e5239d33e37ea72d6419f8367e5babcad38b755586f20264796 + md5: 8b1160b32557290b64d5be68db3d996d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101872 + timestamp: 1732707756745 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + sha256: 0bd1f30224af142711d11033a7469ae402a1147143f399f7341bbc1d8178c722 + md5: 5e70a6de59352f9a52e9caa7f3447390 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101255 + timestamp: 1732707891645 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + sha256: 5e93cda79e32e8c0039e05ea1939e688da336187dab025f699b42ef529e848be + md5: e1747a8e8d2aca5499aaea9993bf31ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 85623 + timestamp: 1732707871414 +- kind: conda + name: httpx + version: 0.28.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- kind: conda + name: huggingface_hub + version: 0.26.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + sha256: 0c75532d914a04c73222be298ed2c6868739dd475b1b1a9137c52abe79873952 + md5: 73937038e21117fe401f8ea64fbaeacc + depends: + - filelock + - fsspec >=2023.5.0 + - packaging >=20.9 + - python >=3.9 + - pyyaml >=5.1 + - requests + - tqdm >=4.42.1 + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + size: 275466 + timestamp: 1733852454004 +- kind: conda + name: hyperframe + version: 6.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + sha256: e91c6ef09d076e1d9a02819cd00fa7ee18ecf30cdd667605c853980216584d1b + md5: 566e75c90c1d0c8c459eb0ad9833dc7a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17239 + timestamp: 1733298862681 +- kind: conda + name: icu + version: '75.1' + build: hf9b3779_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 + md5: 268203e8b983fddb6412b36f2024e75c + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12282786 + timestamp: 1720853454991 +- kind: conda + name: icu + version: '75.1' + build: hfee45f7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 11857802 + timestamp: 1720853997952 +- kind: conda + name: idna + version: '3.10' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 + md5: 39a4f67be3286c86d696df570b1201b7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 49765 + timestamp: 1733211921194 +- kind: conda + name: importlib-metadata + version: 8.5.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 + md5: 315607a3030ad5d5227e76e0733798ff + depends: + - python >=3.9 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + size: 28623 + timestamp: 1733223207185 +- kind: conda + name: importlib_resources + version: 6.4.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_1.conda + sha256: 461199e429a3db01f0a673f8beaac5e0be75b88895952fb9183f2ab01c5c3c24 + md5: 15798fa69312d433af690c8c42b3fb36 + depends: + - python >=3.9 + - zipp >=3.1.0 + constrains: + - importlib-resources >=6.4.5,<6.4.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 32701 + timestamp: 1733231441973 +- kind: conda + name: ipykernel + version: 6.29.5 + build: pyh3099207_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh3099207_0.conda + sha256: 33cfd339bb4efac56edf93474b37ddc049e08b1b4930cf036c893cc1f5a1f32a + md5: b40131ab6a36ac2c09b7c57d4d3fbf99 + depends: + - __linux + - comm >=0.1.1 + - debugpy >=1.6.5 + - ipython >=7.23.1 + - jupyter_client >=6.1.12 + - jupyter_core >=4.12,!=5.0.* + - matplotlib-inline >=0.1 + - nest-asyncio + - packaging + - psutil + - python >=3.8 + - pyzmq >=24 + - tornado >=6.1 + - traitlets >=5.4.0 + license: BSD-3-Clause + license_family: BSD + size: 119084 + timestamp: 1719845605084 +- kind: conda + name: ipykernel + version: 6.29.5 + build: pyh57ce528_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/ipykernel-6.29.5-pyh57ce528_0.conda + sha256: 072534d4d379225b2c3a4e38bc7730b65ae171ac7f0c2d401141043336e97980 + md5: 9eb15d654daa0ef5a98802f586bb4ffc + depends: + - __osx + - appnope + - comm >=0.1.1 + - debugpy >=1.6.5 + - ipython >=7.23.1 + - jupyter_client >=6.1.12 + - jupyter_core >=4.12,!=5.0.* + - matplotlib-inline >=0.1 + - nest-asyncio + - packaging + - psutil + - python >=3.8 + - pyzmq >=24 + - tornado >=6.1 + - traitlets >=5.4.0 + license: BSD-3-Clause + license_family: BSD + size: 119568 + timestamp: 1719845667420 +- kind: conda + name: ipython + version: 8.30.0 + build: pyh707e725_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/ipython-8.30.0-pyh707e725_0.conda + sha256: 65cdc105e5effea2943d3979cc1592590c923a589009b484d07672faaf047af1 + md5: 5d6e5cb3a4b820f61b2073f0ad5431f1 + depends: + - __unix + - decorator + - exceptiongroup + - jedi >=0.16 + - matplotlib-inline + - pexpect >4.3 + - pickleshare + - prompt-toolkit >=3.0.41,<3.1.0 + - pygments >=2.4.0 + - python >=3.10 + - stack_data + - traitlets >=5.13.0 + - typing_extensions >=4.6 + license: BSD-3-Clause + license_family: BSD + size: 600248 + timestamp: 1732897026255 +- kind: conda + name: isoduration + version: 20.11.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda + sha256: 08e838d29c134a7684bca0468401d26840f41c92267c4126d7b43a6b533b0aed + md5: 0b0154421989637d424ccf0f104be51a + depends: + - arrow >=0.15.0 + - python >=3.9 + license: MIT + license_family: MIT + size: 19832 + timestamp: 1733493720346 +- kind: conda + name: jedi + version: 0.19.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda + sha256: 92c4d217e2dc68983f724aa983cca5464dcb929c566627b26a2511159667dba8 + md5: a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9 + depends: + - parso >=0.8.3,<0.9.0 + - python >=3.9 + license: Apache-2.0 AND MIT + size: 843646 + timestamp: 1733300981994 +- kind: conda + name: jinja2 + version: 3.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + sha256: 85a7169c078b8065bd9d121b0e7b99c8b88c42a411314b6ae5fcd81c48c4710a + md5: 08cce3151bde4ecad7885bd9fb647532 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110963 + timestamp: 1733217424408 +- kind: conda + name: json5 + version: 0.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/json5-0.10.0-pyhd8ed1ab_1.conda + sha256: 61bca2dac194c44603446944745566d7b4e55407280f6f6cea8bbe4de26b558f + md5: cd170f82d8e5b355dfdea6adab23e4af + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 31573 + timestamp: 1733272196759 +- kind: conda + name: jsonpointer + version: 3.0.0 + build: py312h7900ff3_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py312h7900ff3_1.conda + sha256: 76ccb7bffc7761d1d3133ffbe1f7f1710a0f0d9aaa9f7ea522652e799f3601f4 + md5: 6b51f7459ea4073eeb5057207e2e1e3d + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 17277 + timestamp: 1725303032027 +- kind: conda + name: jsonpointer + version: 3.0.0 + build: py312h81bd7bf_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/jsonpointer-3.0.0-py312h81bd7bf_1.conda + sha256: f6fb3734e967d1cd0cde32844ee952809f6c0a49895da7ec1c8cfdf97739b947 + md5: 80f403c03290e1662be03e026fb5f8ab + depends: + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 17865 + timestamp: 1725303130815 +- kind: conda + name: jsonpointer + version: 3.0.0 + build: py312h996f985_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/jsonpointer-3.0.0-py312h996f985_1.conda + sha256: 908448e2946c8fd8e28f5c7de4ed52548d227fae2994febf1050179b2590dbdc + md5: 2257c5f33024274faadf6a88a7d62807 + depends: + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 17821 + timestamp: 1725303138276 +- kind: conda + name: jsonschema + version: 4.23.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.23.0-pyhd8ed1ab_1.conda + sha256: be992a99e589146f229c58fe5083e0b60551d774511c494f91fe011931bd7893 + md5: a3cead9264b331b32fe8f0aabc967522 + depends: + - attrs >=22.2.0 + - importlib_resources >=1.4.0 + - jsonschema-specifications >=2023.03.6 + - pkgutil-resolve-name >=1.3.10 + - python >=3.9 + - referencing >=0.28.4 + - rpds-py >=0.7.1 + license: MIT + license_family: MIT + size: 74256 + timestamp: 1733472818764 +- kind: conda + name: jsonschema-specifications + version: 2024.10.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2024.10.1-pyhd8ed1ab_1.conda + sha256: 37127133837444cf0e6d1a95ff5a505f8214ed4e89e8e9343284840e674c6891 + md5: 3b519bc21bc80e60b456f1e62962a766 + depends: + - python >=3.9 + - referencing >=0.31.0 + license: MIT + license_family: MIT + size: 16170 + timestamp: 1733493624968 +- kind: conda + name: jsonschema-with-format-nongpl + version: 4.23.0 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.23.0-hd8ed1ab_1.conda + sha256: 6e0184530011961a0802fda100ecdfd4b0eca634ed94c37e553b72e21c26627d + md5: a5b1a8065857cc4bd8b7a38d063bb728 + depends: + - fqdn + - idna + - isoduration + - jsonpointer >1.13 + - jsonschema >=4.23.0,<4.23.1.0a0 + - rfc3339-validator + - rfc3986-validator >0.1.0 + - uri-template + - webcolors >=24.6.0 + license: MIT + license_family: MIT + size: 7135 + timestamp: 1733472820035 +- kind: conda + name: jupyter-lsp + version: 2.2.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.2.5-pyhd8ed1ab_1.conda + sha256: 1565c8b1423a37fca00fe0ab2a17cd8992c2ecf23e7867a1c9f6f86a9831c196 + md5: 0b4c3908e5a38ea22ebb98ee5888c768 + depends: + - importlib-metadata >=4.8.3 + - jupyter_server >=1.1.2 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 55221 + timestamp: 1733493006611 +- kind: conda + name: jupyter_client + version: 8.6.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a + md5: 4ebae00eae9705b0c3d6d1018a81d047 + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106342 + timestamp: 1733441040958 +- kind: conda + name: jupyter_core + version: 5.7.2 + build: pyh31011fe_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd + md5: 0a2980dada0dd7fd0998f0342308b1b1 + depends: + - __unix + - platformdirs >=2.5 + - python >=3.8 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 57671 + timestamp: 1727163547058 +- kind: conda + name: jupyter_events + version: 0.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.10.0-pyhd8ed1ab_1.conda + sha256: d7fa4c627d56ce8dc02f09f358757f8fd49eb6137216dc99340a6b4efc7e0491 + md5: 62186e6383f38cc6a3466f0fadde3f2e + depends: + - jsonschema-with-format-nongpl >=4.18.0 + - python >=3.9 + - python-json-logger >=2.0.4 + - pyyaml >=5.3 + - referencing + - rfc3339-validator + - rfc3986-validator >=0.1.1 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 21434 + timestamp: 1733441420606 +- kind: conda + name: jupyter_server + version: 2.14.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.14.2-pyhd8ed1ab_1.conda + sha256: 082d3517455339c8baea245a257af249758ccec26b8832d969ac928901c234cc + md5: 81ea84b3212287f926e35b9036192963 + depends: + - anyio >=3.1.0 + - argon2-cffi >=21.1 + - jinja2 >=3.0.3 + - jupyter_client >=7.4.4 + - jupyter_core >=4.12,!=5.0.* + - jupyter_events >=0.9.0 + - jupyter_server_terminals >=0.4.4 + - nbconvert-core >=6.4.4 + - nbformat >=5.3.0 + - overrides >=5.0 + - packaging >=22.0 + - prometheus_client >=0.9 + - python >=3.9 + - pyzmq >=24 + - send2trash >=1.8.2 + - terminado >=0.8.3 + - tornado >=6.2.0 + - traitlets >=5.6.0 + - websocket-client >=1.7 + license: BSD-3-Clause + license_family: BSD + size: 324289 + timestamp: 1733428731329 +- kind: conda + name: jupyter_server_terminals + version: 0.5.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda + sha256: 0890fc79422191bc29edf17d7b42cff44ba254aa225d31eb30819f8772b775b8 + md5: 2d983ff1b82a1ccb6f2e9d8784bdd6bd + depends: + - python >=3.9 + - terminado >=0.8.3 + license: BSD-3-Clause + license_family: BSD + size: 19711 + timestamp: 1733428049134 +- kind: conda + name: jupyterlab + version: 4.3.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.3.3-pyhd8ed1ab_0.conda + sha256: 63aa00427abd4a3e7c1738257b8e296f5e0ba04a4a1ab9ff3bc186440c8b9fdc + md5: 0707e62d944a89c365ba11da4898f8af + depends: + - async-lru >=1.0.0 + - httpx >=0.25.0 + - importlib-metadata >=4.8.3 + - ipykernel >=6.5.0 + - jinja2 >=3.0.3 + - jupyter-lsp >=2.0.0 + - jupyter_core + - jupyter_server >=2.4.0,<3 + - jupyterlab_server >=2.27.1,<3 + - notebook-shim >=0.2 + - packaging + - python >=3.9 + - setuptools >=40.8.0 + - tomli >=1.2.2 + - tornado >=6.2.0 + - traitlets + license: BSD-3-Clause + license_family: BSD + size: 7972675 + timestamp: 1733836496011 +- kind: conda + name: jupyterlab_pygments + version: 0.3.0 + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda + sha256: dc24b900742fdaf1e077d9a3458fd865711de80bca95fe3c6d46610c532c6ef0 + md5: fd312693df06da3578383232528c468d + depends: + - pygments >=2.4.1,<3 + - python >=3.9 + constrains: + - jupyterlab >=4.0.8,<5.0.0 + license: BSD-3-Clause + license_family: BSD + size: 18711 + timestamp: 1733328194037 +- kind: conda + name: jupyterlab_server + version: 2.27.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda + sha256: d03d0b7e23fa56d322993bc9786b3a43b88ccc26e58b77c756619a921ab30e86 + md5: 9dc4b2b0f41f0de41d27f3293e319357 + depends: + - babel >=2.10 + - importlib-metadata >=4.8.3 + - jinja2 >=3.0.3 + - json5 >=0.9.0 + - jsonschema >=4.18 + - jupyter_server >=1.21,<3 + - packaging >=21.3 + - python >=3.9 + - requests >=2.31 + constrains: + - openapi-core >=0.18.0,<0.19.0 + license: BSD-3-Clause + license_family: BSD + size: 49449 + timestamp: 1733599666357 +- kind: conda + name: keyutils + version: 1.6.1 + build: h166bdaf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + sha256: 150c05a6e538610ca7c43beb3a40d65c90537497a4f6a5f4d15ec0451b6f5ebb + md5: 30186d27e2c9fa62b45fb1476b7200e3 + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 117831 + timestamp: 1646151697040 +- kind: conda + name: keyutils + version: 1.6.1 + build: h4e544f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + sha256: 6d4233d97a9b38acbb26e1268bcf8c10a8e79c2aed7e5a385ec3769967e3e65b + md5: 1f24853e59c68892452ef94ddd8afd4b + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 112327 + timestamp: 1646166857935 +- kind: conda + name: krb5 + version: 1.21.3 + build: h237132a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- kind: conda + name: krb5 + version: 1.21.3 + build: h50a48e9_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 + md5: 29c10432a2ca1472b53f299ffb2ffa37 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1474620 + timestamp: 1719463205834 +- kind: conda + name: krb5 + version: 1.21.3 + build: h659f571_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1370023 + timestamp: 1719463201255 +- kind: conda + name: lcms2 + version: '2.16' + build: h922389a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + sha256: be4847b1014d3cbbc524a53bdbf66182f86125775020563e11d914c8468dd97d + md5: ffdd8267a04c515e7ce69c727b051414 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 296219 + timestamp: 1701647961116 +- kind: conda + name: lcms2 + version: '2.16' + build: ha0e7c42_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + sha256: 151e0c84feb7e0747fabcc85006b8973b22f5abbc3af76a9add0b0ef0320ebe4 + md5: 66f6c134e76fe13cce8a9ea5814b5dd5 + depends: + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 211959 + timestamp: 1701647962657 +- kind: conda + name: lcms2 + version: '2.16' + build: hb7c19ff_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 + md5: 51bb7010fc86f70eee639b4bb7a894f5 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 245247 + timestamp: 1701647787198 +- kind: conda + name: ld_impl_linux-64 + version: '2.43' + build: h712a8e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + sha256: 7c91cea91b13f4314d125d1bedb9d03a29ebbd5080ccdea70260363424646dbe + md5: 048b02e3962f066da18efe3a21b77672 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 669211 + timestamp: 1729655358674 +- kind: conda + name: ld_impl_linux-aarch64 + version: '2.43' + build: h80caac9_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + sha256: 80ec7e8f006196808fac5bd4b3773a652847f97bbf08044cd87731424ac64f8b + md5: fcbde5ea19d55468953bf588770c0501 + constrains: + - binutils_impl_linux-aarch64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 698245 + timestamp: 1729655345825 +- kind: conda + name: lerc + version: 4.0.0 + build: h27087fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + sha256: cb55f36dcd898203927133280ae1dc643368af041a48bcf7c026acb7c47b0c12 + md5: 76bbff344f0134279f225174e9064c8f + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 281798 + timestamp: 1657977462600 +- kind: conda + name: lerc + version: 4.0.0 + build: h4de3ea5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + sha256: 2d09ef9b7796d83364957e420b41c32d94e628c3f0520b61c332518a7b5cd586 + md5: 1a0ffc65e03ce81559dbcb0695ad1476 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 262096 + timestamp: 1657978241894 +- kind: conda + name: lerc + version: 4.0.0 + build: h9a09cb3_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + sha256: 6f068bb53dfb6147d3147d981bb851bb5477e769407ad4e6a68edf482fdcb958 + md5: de462d5aacda3b30721b512c5da4e742 + depends: + - libcxx >=13.0.1 + license: Apache-2.0 + license_family: Apache + size: 215721 + timestamp: 1657977558796 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + sha256: 8f91429091183c26950f1e7ffa730e8632f0627ba35d2fccd71df31628c9b4e5 + md5: e1f604644fe8d78e22660e2fec6756bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1310521 + timestamp: 1727295454064 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + sha256: 590e47dce38031a8893e70491f3b71e214de7781cab53b6f017aa6f6841cb076 + md5: 6fe6b3694c4792a8e26755d3b06f0b80 + depends: + - libgcc >=13 + - libstdcxx >=13 + constrains: + - abseil-cpp =20240722.0 + - libabseil-static =20240722.0=cxx17* + license: Apache-2.0 + license_family: Apache + size: 1328502 + timestamp: 1727295490806 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_hf9b8971_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + sha256: 90bf08a75506dfcf28a70977da8ab050bcf594cd02abd3a9d84a22c9e8161724 + md5: 706da5e791c569a7b9814877098a6a0a + depends: + - __osx >=11.0 + - libcxx >=17 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1179072 + timestamp: 1727295571173 +- kind: conda + name: libarrow + version: 18.1.0 + build: h1b535d6_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + sha256: 087b579aebf351ca41c54214121d86a15a41c92051cbd432d6f3a3f58a8c31b0 + md5: 4c0ad68efba1113ac5833975c67b565d + depends: + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8040629 + timestamp: 1733810319239 +- kind: conda + name: libarrow + version: 18.1.0 + build: h44a453e_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + sha256: abf17e99b03356a9d6248e965826c1352ff01b00d3a62cc51393bb0744d72803 + md5: 2cf6d608d6e66506f69797d5c6944c35 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8786061 + timestamp: 1733810643966 +- kind: conda + name: libarrow + version: 18.1.0 + build: h4a2f8bd_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + sha256: 9ed3ea1bc15005c0df187268ef91407afaa908cf82f36f5acbbf50ac24d7f806 + md5: 835cdd84195b84dc34d128bd5d3580b9 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libcxx >=18 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 5494797 + timestamp: 1733808145854 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + sha256: fdb70e2499e59b730084ecd53008b361a6f6090b5fb49624feda06b7e84c7b8c + md5: c50907eefe2ae22d826e7cb2e4d712f5 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 578091 + timestamp: 1733810378092 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + sha256: a32fa1d71415afc02b5cf3cd4c0a6ec0af9e749308829cc65ff79689222ce479 + md5: 143f9288b64759a6427563f058c62f2b + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 611745 + timestamp: 1733810698469 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + sha256: e1cae46409927470439ef9ae93ed09b3493d0579501ca9ebfa79ded212ee98d8 + md5: 97fc01254714e1572624baefdd7cc898 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + license: Apache-2.0 + license_family: APACHE + size: 483713 + timestamp: 1733808246880 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + sha256: 2a08f5a1017ff660c37ae0c24343a119cb2511c6edd69e23d0a5090a0967ea35 + md5: bb1548ad011c4f9107fcc4cc548473bf + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libparquet 18.1.0 hfc78867_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 559673 + timestamp: 1733810461646 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + sha256: 74eeb178070002842d3ed721769399320e3a68a0843319eaf899a092a31def26 + md5: 20ca46a6bc714a6ab189d5b3f46e66d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libparquet 18.1.0 h081d1f1_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 586627 + timestamp: 1733810842604 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + sha256: 6eba942ce926419f74e6e0a7c3994a7d78ab6be47115e6bb70e02136554736be + md5: 0774276be6659aaa0007f1b0f6ee19b0 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libparquet 18.1.0 h636d7b7_6_cpu + license: Apache-2.0 + license_family: APACHE + size: 489948 + timestamp: 1733809328231 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ee7192_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + sha256: bda6728db019dd0c409b1996ad9ef6ab0bcee3a94dc66a8045e8c1049c566055 + md5: aa313b3168caf98d00b3753f5ba27650 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libarrow-dataset 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 519989 + timestamp: 1733810903274 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ffb4b1_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + sha256: 9f78c55c5d7122e588a6f226cbf7e909c479d66ed18edc633d68324323d386b9 + md5: 5db2e6832397b8ca70a6f7b00e0c3629 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libarrow-dataset 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 515928 + timestamp: 1733810503359 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h86344ea_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + sha256: bafd9ca59ebb5ad34b77aff316ef7b59c5fb1eb8a7b6a15de8dcbdf3ce37556d + md5: c1c162f5bf569cff8bed6def705a899f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libarrow-dataset 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 451623 + timestamp: 1733809487176 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + sha256: 30bd658682b124243f8e52d8edf8a19e7be1bc31e4fe4baec30a64002dc8cd0c + md5: ac52800af2e0c0e7dac770b435ce768a + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16393 + timestamp: 1734432564346 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: df6d8ee34d45cf35609ecdd55c1ff03e32e0cd87ae41ebe4ef3747a8e09ead4d + md5: 8d900b7079a00969d70305e9aad550b7 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16477 + timestamp: 1734432576699 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + sha256: 597f9c3779caa979c8c6abbb3ba8c7191b84e1a910d6b0d10e5faf35284c450c + md5: 21be102c9ae80a67ba7de23b129aa7f6 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16714 + timestamp: 1734433054681 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 + md5: 3ee026955c688f551a9999840cff4c67 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 68982 + timestamp: 1725267774142 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 + md5: 41b599ed2b02abcfdd84302bff174b23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 68851 + timestamp: 1725267660471 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + sha256: 839dacb741bdbb25e58f42088a2001b649f4f12195aeb700b5ddfca3267749e5 + md5: d0bf1dff146b799b319ea0434b93f779 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 68426 + timestamp: 1725267943211 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c + md5: e64d0f3b59c7c4047446b97a8624a72d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 31708 + timestamp: 1725267783442 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 32696 + timestamp: 1725267669305 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + sha256: 6c6862eb274f21a7c0b60e5345467a12e6dda8b9af4438c66d496a2c1a538264 + md5: 55e66e68ce55523a6811633dd1ac74e2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 28378 + timestamp: 1725267980316 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c + md5: 0e9bd365480c72b25c71a448257b537d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 290230 + timestamp: 1725267792697 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 + md5: 06f70867945ea6a84d35836af780f1de + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 281750 + timestamp: 1725267679782 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + sha256: eeb1eb0d58b9d02bc1b98dc0a058f104ab168eb2f7d1c7bfa0570a12cfcdb7b7 + md5: 4f3a434504c67b2c42565c0b85c1885c + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 279644 + timestamp: 1725268003553 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + sha256: 9c74e536c9bc868e356ffd43f81c2cb398aec84b40fcadc312315b164a5500ee + md5: ebcc5f37a435aa3c19640533c82f8d76 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16336 + timestamp: 1734432570482 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: 521e78be0c4170f229c43e1a6c94337a72db3ebcbe6e5960f8413aa438dcb8f9 + md5: d77f943ae4083f3aeddca698f2d28262 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16398 + timestamp: 1734432580937 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + sha256: 27a29ef6b2fd2179bc3a0bb9db351f078ba140ca10485dca147c399639f84c93 + md5: a0e9980fe12d42f6d0c0ec009f67e948 + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16628 + timestamp: 1734433061517 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea + md5: 268ee639c17ada0002fb04dd21816cc2 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 18669 + timestamp: 1633683724891 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 20440 + timestamp: 1633683576494 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: hbdafb3b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + size: 18765 + timestamp: 1633683992603 +- kind: conda + name: libcurl + version: 8.11.1 + build: h332b0f4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + sha256: 3cd4075b2a7b5562e46c8ec626f6f9ca57aeecaa94ff7df57eca26daa94c9906 + md5: 2b3e0081006dc21e8bf53a91c83a055c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 423011 + timestamp: 1733999897624 +- kind: conda + name: libcurl + version: 8.11.1 + build: h6702fde_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + sha256: 9fc65d21a58f4aad1bc39dfb94a178893aeb035850c5cf0ed9736674279f390b + md5: 7dec1cd271c403d1636bda5aa388a55d + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 440737 + timestamp: 1733999835504 +- kind: conda + name: libcurl + version: 8.11.1 + build: h73640d1_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + sha256: f47c35938144c23278987c7d12096f6a42d7c850ffc277222b032073412383b6 + md5: 46d7524cabfdd199bffe63f8f19a552b + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 385098 + timestamp: 1734000160270 +- kind: conda + name: libcxx + version: 19.1.5 + build: ha82da77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + sha256: 7918cc0bb7a6554cdd3eee634c3dc414a1ab8ec49faeca1567367bb92118f9d7 + md5: 3c7be0df28ccda1d193ea6de56dcb5ff + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 519819 + timestamp: 1733291654212 +- kind: conda + name: libdeflate + version: '1.23' + build: h4ddbbb0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + sha256: 511d801626d02f4247a04fff957cc6e9ec4cc7e8622bd9acd076bcdc5de5fe66 + md5: 8dfae1d2e74767e9ce36d5fa0d8605db + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + size: 72255 + timestamp: 1734373823254 +- kind: conda + name: libdeflate + version: '1.23' + build: h5e3c512_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + sha256: 959419d87cd2b789a9055db95704c614f31aeb70bef7949fa2f734122a3a2863 + md5: 7e7ca2607b11b180120cefc2354fc0cb + depends: + - libgcc >=13 + license: MIT + size: 69862 + timestamp: 1734373858306 +- kind: conda + name: libdeflate + version: '1.23' + build: hec38601_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + sha256: 887c02deaed6d583459eba6367023e36d8761085b2f7126e389424f57155da53 + md5: 1d8b9588be14e71df38c525767a1ac30 + depends: + - __osx >=11.0 + license: MIT + size: 54132 + timestamp: 1734373971372 +- kind: conda + name: libedit + version: 3.1.20191231 + build: hc8eb9b7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca + md5: 30e4362988a2623e9eb34337b83e01f9 + depends: + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96607 + timestamp: 1597616630749 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: a57d37c236d8f7c886e01656f4949d9dcca131d2a0728609c6f7fa338b65f1cf + md5: 4d331e44109e3f0e19b4cb8f9b82f3e1 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 123878 + timestamp: 1597616541093 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: debc31fb2f07ba2b0363f90e455873670734082822926ba4a9556431ec0bf36d + md5: 29371161d77933a54fccf1bb66b96529 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 134104 + timestamp: 1597617110769 +- kind: conda + name: libev + version: '4.33' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + sha256: 973af77e297f1955dd1f69c2cbdc5ab9dfc88388a5576cd152cda178af0fd006 + md5: a9a13cb143bbaa477b1ebaefbe47a302 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 115123 + timestamp: 1702146237623 +- kind: conda + name: libev + version: '4.33' + build: h93a5062_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + size: 107458 + timestamp: 1702146414478 +- kind: conda + name: libev + version: '4.33' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 112766 + timestamp: 1702146165126 +- kind: conda + name: libevent + version: 2.1.12 + build: h2757513_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 368167 + timestamp: 1685726248899 +- kind: conda + name: libevent + version: 2.1.12 + build: h4ba1bb4_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + sha256: 01333cc7d6e6985dd5700b43660d90e9e58049182017fd24862088ecbe1458e4 + md5: 96ae6083cd1ac9f6bc81631ac835b317 + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 438992 + timestamp: 1685726046519 +- kind: conda + name: libevent + version: 2.1.12 + build: hf998b51_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 427426 + timestamp: 1685725977222 +- kind: conda + name: libexpat + version: 2.6.4 + build: h286801f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 + md5: 38d2656dd914feb0cab8c629370768bf + depends: + - __osx >=11.0 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 64693 + timestamp: 1730967175868 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 + md5: db833e03127376d461e1e13e76f09b6c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 73304 + timestamp: 1730967041968 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + sha256: f42e758009ba9db90d1fe7992bc3e60d0c52f71fb20923375d2c44ae69a5a2b3 + md5: f1b3fab36861b3ce945a13f0dfdfc688 + depends: + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 72345 + timestamp: 1730967203789 +- kind: conda + name: libffi + version: 3.4.2 + build: h3422bc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 + license: MIT + license_family: MIT + size: 39020 + timestamp: 1636488587153 +- kind: conda + name: libffi + version: 3.4.2 + build: h3557bc0_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + sha256: 7e9258a102480757fe3faeb225a3ca04dffd10fecd2a958c65cdb4cdf75f2c3c + md5: dddd85f4d52121fab0a8b099c5e06501 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 59450 + timestamp: 1636488255090 +- kind: conda + name: libffi + version: 3.4.2 + build: h7f98852_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 58292 + timestamp: 1636488182923 +- kind: conda + name: libgcc + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + sha256: 53eb8a79365e58849e7b1a068d31f4f9e718dc938d6f2c03e960345739a03569 + md5: 3cb76c3f10d3bc7f1105b2fc9db984df + depends: + - _libgcc_mutex 0.1 conda_forge + - _openmp_mutex >=4.5 + constrains: + - libgomp 14.2.0 h77fa898_1 + - libgcc-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 848745 + timestamp: 1729027721139 +- kind: conda + name: libgcc + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + sha256: 5d56757ccad208c79214395b00d006d8d18929a4ba49c47bd9460789a7620943 + md5: 511b511c5445e324066c3377481bcab8 + depends: + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==14.2.0=*_1 + - libgomp 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 535243 + timestamp: 1729089435134 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + sha256: 3a76969c80e9af8b6e7a55090088bc41da4cffcde9e2c71b17f44d37b7cb87f7 + md5: e39480b9ca41323497b05492a63bc35b + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54142 + timestamp: 1729027726517 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + sha256: 9b5cf168a6c7361cae869cb74b716766ee7c6d6b3f6172b32ba9bf91135efdc4 + md5: 0694c249c61469f2c0f7e2990782af21 + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54104 + timestamp: 1729089444587 +- kind: conda + name: libgfortran + version: 5.0.0 + build: 13_2_0_hd922786_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf + depends: + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- kind: conda + name: libgfortran + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + sha256: fc9e7f22a17faf74da904ebfc4d88699013d2992e55505e4aa0eb01770290977 + md5: f1fd30127802683586f768875127a987 + depends: + - libgfortran5 14.2.0 hd5240d6_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 53997 + timestamp: 1729027752995 +- kind: conda + name: libgfortran + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + sha256: cb66e411fa32a5c6040f4e5e2a63c00897aae4c3133a9c004c2e929ccf19575b + md5: 0294b92d2f47a240bebb1e3336b495f1 + depends: + - libgfortran5 14.2.0 hb6113d0_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729089471124 +- kind: conda + name: libgfortran5 + version: 13.2.0 + build: hf226fd6_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 5.0.0 13_2_0_*_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 997381 + timestamp: 1707330687590 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hb6113d0_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + sha256: a87ff46d19916403cbf68cf1d785bf56b4d1ab7b2552468d2ea775d70782493f + md5: fc068e11b10e18f184e027782baa12b6 + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1102158 + timestamp: 1729089452640 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hd5240d6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d + md5: 9822b874ea29af082e5d36098d25427d + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1462645 + timestamp: 1729027735353 +- kind: conda + name: libgomp + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + sha256: 1911c29975ec99b6b906904040c855772ccb265a1c79d5d75c8ceec4ed89cd63 + md5: cc3573974587f12dda90d96e3e55a702 + depends: + - _libgcc_mutex 0.1 conda_forge + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 460992 + timestamp: 1729027639220 +- kind: conda + name: libgomp + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + sha256: 5aa53874a5e57a00f2e0c2e2910684eb674429cd5fcb803619b226a73e89aedf + md5: 376f0e73abbda6d23c0cb749adc195ef + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 463521 + timestamp: 1729089357313 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h3888205_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + sha256: 36af2844ce8fafd477214d51117746144461132f76759a7d29963b4583b577be + md5: a40b948bf4eabcc1ce708c40ffd7c06d + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1248560 + timestamp: 1733512309504 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h804f50b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + sha256: 126856add750013390dff664a3c3cd0f6f0cbbc683b0025a7ce9d1618968bc70 + md5: 3d96df4d6b1c88455e05b94ce8a14a53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1249557 + timestamp: 1733512191906 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h8d8be31_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + sha256: 722e49dbdc4486105d9f5b79a7ba4f9064602fe20c4015e97684c898ab8d3386 + md5: d7ab9e0eb7d55eac4943913073de61d7 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 876210 + timestamp: 1733512539476 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h0121fbd_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + sha256: d1b53d17df38b52a4bc6d1fe6af0e611d6480ce10b0af570c84bd38c8aa83b91 + md5: 877a5ec0431a5af83bf0cd0522bfe661 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h804f50b_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 782108 + timestamp: 1733512329104 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h7081f7f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + sha256: 609df2cf376ba66460f40143f835fc567cae4458df80705587cd2efd59c09bf1 + md5: 28f5ab5cf95170dfacd05d2bb301e573 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=18 + - libgoogle-cloud 2.32.0 h8d8be31_0 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 526895 + timestamp: 1733513644846 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: hb9b2b65_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + sha256: e120e7b6c9c9d25baa8ae903106babdd3c969523ae25278a615ed9de4bd0fc35 + md5: 925ab0ca33baca4fcfee585cecb94169 + depends: + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h3888205_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 737964 + timestamp: 1733512457785 +- kind: conda + name: libgrpc + version: 1.67.1 + build: h36c5df4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + sha256: 1f6673d9d866048c9cf28fd56e6874ffc7e2c53c47d7071cb367d5fc2dde16a7 + md5: b946137e362e98a55a77fdf0b20a7739 + depends: + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7131846 + timestamp: 1730236305327 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc2c308b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + sha256: 870550c1faf524e9a695262cd4c31441b18ad542f16893bd3c5dbc93106705f7 + md5: 4606a4647bfe857e3cfe21ca12ac3afb + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7362336 + timestamp: 1730236333879 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc70892a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + sha256: d2393fcd3c3584e5d58da4122f48bcf297567d2f6f14b3d1fcbd34fdd5040694 + md5: 624e27571fde34f8acc2afec840ac435 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 4882208 + timestamp: 1730236299095 +- kind: conda + name: libiconv + version: '1.17' + build: h0d3ecfb_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 + md5: 69bda57310071cf6d2b86caf11573d2d + license: LGPL-2.1-only + size: 676469 + timestamp: 1702682458114 +- kind: conda + name: libiconv + version: '1.17' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + sha256: a30e09d089cb75a0d5b8e5c354694c1317da98261185ed65aa3793e741060614 + md5: 9a8eb13f14de7d761555a98712e6df65 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705787 + timestamp: 1702684557134 +- kind: conda + name: libiconv + version: '1.17' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 + md5: d66573916ffcf376178462f1b61c941e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705775 + timestamp: 1702682170569 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + sha256: 675bc1f2a8581cd34a86c412663ec29c5f90c1d9f8d11866aa1ade5cdbdf8429 + md5: ed24e702928be089d9ba3f05618515c6 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 647126 + timestamp: 1694475003570 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hb547adb_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + sha256: a42054eaa38e84fc1e5ab443facac4bbc9d1b6b6f23f54b7bf4f1eb687e1d993 + md5: 3ff1e053dc3a2b8e36b9bfa4256a58d1 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 547541 + timestamp: 1694475104253 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f + md5: ea25936bb4080d843790b586850f82b8 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 618575 + timestamp: 1694474974816 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + sha256: b76458c36331376911e0f98fa68109e02f4d5e5ebfffa79587ac69cef748bba1 + md5: 3792604c43695d6a273bc5faaac47d48 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16338 + timestamp: 1734432576650 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + sha256: a42bd01498efe2ccf6d08d56ac3cbd3ceab79e06699ff5aac3da8e45a66738f7 + md5: a5d4e18876393633da62fd8492c00156 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16403 + timestamp: 1734432585123 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + sha256: dd6d9a21e672aee4332f019c8229ce70cf5eaf6c2f4cbd1443b105fb66c00dc5 + md5: cebad79038a75cfd28fa90d147a2d34d + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16624 + timestamp: 1734433068120 +- kind: conda + name: liblzma + version: 5.6.3 + build: h39f12f2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 + md5: b2553114a7f5e20ccd02378a77d836aa + depends: + - __osx >=11.0 + license: 0BSD + size: 99129 + timestamp: 1733407496073 +- kind: conda + name: liblzma + version: 5.6.3 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + sha256: d1cce0b7d62d1e54e2164d3e0667ee808efc6c3870256e5b47a150cd0bf46824 + md5: eb08b903681f9f2432c320e8ed626723 + depends: + - libgcc >=13 + license: 0BSD + size: 124138 + timestamp: 1733409137214 +- kind: conda + name: liblzma + version: 5.6.3 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 + md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: 0BSD + size: 111132 + timestamp: 1733407410083 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h161d5f1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 + md5: 19e57602824042dfd0446292ef90488b + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 647599 + timestamp: 1729571887612 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h6d7220d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 566719 + timestamp: 1729572385640 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: hc8609a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + sha256: c093c6d370aadbf0409c20b6c54c488ee2f6fea976181919fcc63e87ee232673 + md5: f52c614fa214a8bedece9421c771670d + depends: + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 714610 + timestamp: 1729571912479 +- kind: conda + name: libnsl + version: 2.0.1 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 + md5: c14f32510f694e3185704d89967ec422 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 34501 + timestamp: 1697358973269 +- kind: conda + name: libnsl + version: 2.0.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 33408 + timestamp: 1697359010159 +- kind: conda + name: libopenblas + version: 0.3.28 + build: openmp_hf332438_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 + md5: 40803a48d947c8639da6704e9a44d3ce + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=13.2.0 + - llvm-openmp >=18.1.8 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4165774 + timestamp: 1730772154295 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h94d23a6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + sha256: 99ba271d8a80a1af2723f2e124ffd91d850074c0389c067e6d96d72a2dbfeabe + md5: 62857b389e42b36b686331bec0922050 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 5578513 + timestamp: 1730772671118 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h9d3fd7e_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + sha256: 30623a40764e935aa77e0d4db54c1a1589189a9bf3a03fdb445505c1e319b5a6 + md5: e8dde93dd199da3c1f2c1fcfd0042cd4 + depends: + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4793435 + timestamp: 1730773029647 +- kind: conda + name: libparquet + version: 18.1.0 + build: h081d1f1_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + sha256: c691a59f1ebb6cedbf827f49f6cf414e08b0eec911f589133e6a8321e8ac701c + md5: 68788df49ce7480187eb6387f15b2b67 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1204535 + timestamp: 1733810811118 +- kind: conda + name: libparquet + version: 18.1.0 + build: h636d7b7_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + sha256: 88c1e810bede65c54f1ebc51c14400f9e8cf0fc1f88a8c0a99210e2f5dfed582 + md5: 9b333c3a38e55f6c1b8733222e22f528 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 873134 + timestamp: 1733809271282 +- kind: conda + name: libparquet + version: 18.1.0 + build: hfc78867_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + sha256: 38aab34c422519c530d0e9a3e0ffd1624db1c1e163983c46ae341e831b2eb6b5 + md5: 1ab6d4a9a982920b9dc5f2c700777b27 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1117592 + timestamp: 1733810440129 +- kind: conda + name: libpng + version: 1.6.44 + build: hadc24fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + sha256: e5b14f7a01c2db4362d8591f42f82f336ed48d5e4079e4d1f65d0c2a3637ea78 + md5: f4cc49d7aa68316213e4b12be35308d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 290661 + timestamp: 1726234747153 +- kind: conda + name: libpng + version: 1.6.44 + build: hc14010f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + sha256: 38f8759a3eb8060deabd4db41f0f023514d853e46ddcbd0ba21768fc4e563bb1 + md5: fb36e93f0ea6a6f5d2b99984f34b049e + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 263385 + timestamp: 1726234714421 +- kind: conda + name: libpng + version: 1.6.44 + build: hc4a20ef_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + sha256: 23b5ce15cf9c6017641a8396bab00ae807dd9f662718cfa7f61de114d0c97647 + md5: 5d25802b25fcc7419fa13e21affaeb3a + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 294907 + timestamp: 1726236639270 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h029595c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + sha256: d8c7b6f851bfc53494d9b8e54d473c4f11ab26483a6e64df6f7967563df166b1 + md5: 538dbe0ad9f248e2e109abb9b6809ea5 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2802876 + timestamp: 1728564881988 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h5b01275_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + sha256: 5e8fd4aa00193c85602ce6101dd28fe31306dff85c9725048f6dc828dfa7c421 + md5: ab0bff36363bec94720275a681af8b83 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2945348 + timestamp: 1728565355702 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h8f0b736_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + sha256: f732a6fa918428e2d5ba61e78fe11bb44a002cc8f6bb74c94ee5b1297fefcfd8 + md5: d2cb5991f2fb8eb079c80084435e9ce6 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2374965 + timestamp: 1728565334796 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h18dbdb1_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + sha256: 96d4fdac28d5af38c38f90c22cb0aa9a90affae13ca8ba24bd1eb60b789df8ff + md5: f1800796b0efc4bbc5b001d845545111 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 203516 + timestamp: 1728778974654 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h2348fd5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + sha256: 6facca42cfc85a05b33e484a8b0df7857cc092db34806946d022270098d8d20f + md5: 5a7065309a66097738be6a06fd04b7ef + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 165956 + timestamp: 1728779107218 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: hbbce691_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + sha256: f8ad6a4f6d4fd54ebe3e5e712a01e663222fc57f49d16b6b8b10c30990dafb8f + md5: 2124de47357b7a516c0a3efd8f88c143 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 211096 + timestamp: 1728778964655 +- kind: conda + name: libsodium + version: 1.0.20 + build: h4ab18f5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + size: 205978 + timestamp: 1716828628198 +- kind: conda + name: libsodium + version: 1.0.20 + build: h68df207_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + sha256: 448df5ea3c5cf1af785aad46858d7a5be0522f4234a4dc9bb764f4d11ff3b981 + md5: 2e4a8f23bebdcb85ca8e5a0fbe75666a + depends: + - libgcc-ng >=12 + license: ISC + size: 177394 + timestamp: 1716828514515 +- kind: conda + name: libsodium + version: 1.0.20 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h3f77e49_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + sha256: f192f3c8973de9ec4c214990715f13b781965247a5cedf9162e7f9e699cfc3c4 + md5: 122d6f29470f1a991e85608e77e56a8a + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 850553 + timestamp: 1733762057506 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h5eb1b54_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + sha256: 885a27fa84a5a73ed9779168c02b6c386e2fc7a53f0566b32a09ceca146b42b4 + md5: d4bf59f8783a4a66c0aec568f6de3ff4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 1042182 + timestamp: 1733761913736 +- kind: conda + name: libsqlite + version: 3.47.2 + build: hee588c1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + sha256: 48af21ebc2cbf358976f1e0f4a0ab9e91dfc83d0ef337cf3837c6f5bc22fb352 + md5: b58da17db24b6e08bcbf8fed2fb8c915 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 873551 + timestamp: 1733761824646 +- kind: conda + name: libssh2 + version: 1.11.1 + build: h9cc3647_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 + md5: ddc7194676c285513706e5fc64f214d7 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279028 + timestamp: 1732349599461 +- kind: conda + name: libssh2 + version: 1.11.1 + build: ha41c0db_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + sha256: 40f2af5357457546bd11cd64a3b9043d83865180f65ce602515c35f353be35c7 + md5: aeffe03c0e598f015aab08dbb04f6ee4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 311577 + timestamp: 1732349396421 +- kind: conda + name: libssh2 + version: 1.11.1 + build: hf672d98_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 + md5: be2de152d8073ef1c01b7728475f2fe7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 304278 + timestamp: 1732349402869 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: h3f4de04_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 + md5: 37f489acd39e22b623d2d1e5ac6d195c + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3816794 + timestamp: 1729089463404 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: hc0a3c3a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + sha256: 4661af0eb9bdcbb5fb33e5d0023b001ad4be828fccdcc56500059d56f9869462 + md5: 234a5554c53625688d51062645337328 + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3893695 + timestamp: 1729027746910 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: h4852527_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + sha256: 25bb30b827d4f6d6f0522cc0579e431695503822f144043b93c50237017fffd8 + md5: 8371ac6457591af2cf6159439c1fd051 + depends: + - libstdcxx 14.2.0 hc0a3c3a_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729027780628 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: hf1166c9_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + sha256: 9f97461bd55a2745a7a0941f3502a047f15bfe7bb2952dc7fb204b3202f866fd + md5: 0e75771b8a03afae5a2c6ce71bc733f5 + depends: + - libstdcxx 14.2.0 h3f4de04_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54133 + timestamp: 1729089498541 +- kind: conda + name: libthrift + version: 0.21.0 + build: h0e7cc3e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + sha256: ebb395232973c18745b86c9a399a4725b2c39293c9a91b8e59251be013db42f0 + md5: dcb95c0a98ba9ff737f7ae482aef7833 + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 425773 + timestamp: 1727205853307 +- kind: conda + name: libthrift + version: 0.21.0 + build: h154c74f_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + sha256: f04ab1417aca1687edff9c30d8423ace285eb8c053dc16d595c6e47cfeefb274 + md5: c28792bf37f4ecdce8e3cb9e40750650 + depends: + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 417329 + timestamp: 1727205944238 +- kind: conda + name: libthrift + version: 0.21.0 + build: h64651cc_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + sha256: 7a6c7d5f58cbbc2ccd6493b4b821639fdb0701b9b04c737a949e8cb6adf1c9ad + md5: 7ce2bd2f650f8c31ad7ba4c7bfea61b7 + depends: + - __osx >=11.0 + - libcxx >=17 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 324342 + timestamp: 1727206096912 +- kind: conda + name: libtiff + version: 4.7.0 + build: h551f018_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae + md5: a5d084a957563e614ec0c0196d890654 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=18 + - libdeflate >=1.23,<1.24.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 370600 + timestamp: 1734398863052 +- kind: conda + name: libtiff + version: 4.7.0 + build: h88f7998_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + sha256: 5888bd66ba7606ae8596856c7dac800940ecad0aed77d6aa37db69d434c81cf0 + md5: 36a0ea4a173338c8725dc0807e99cf22 + depends: + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 464699 + timestamp: 1734398752249 +- kind: conda + name: libtiff + version: 4.7.0 + build: hd9ff511_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 428173 + timestamp: 1734398813264 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h5505292_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + sha256: ea88f06e97ef8fa2490f7594f8885bb542577226edf8abba3144302d951a53c2 + md5: f777470d31c78cd0abe1903a2fda436f + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 83000 + timestamp: 1732868631531 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + sha256: 37a1833c55f9945724cd4b3eb6a1469032cc754a1dd725f191c34154ad2ba7e4 + md5: 699f155da290be3a1a64c932c6728991 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 81526 + timestamp: 1732868466862 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + sha256: 9794e6388e780c3310d46f773bbc924d4053375c3fcdb07a704b57f4616db928 + md5: 1e936bd23d737aac62a18e9a1e7f8b18 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 81500 + timestamp: 1732868419835 +- kind: conda + name: libuuid + version: 2.38.1 + build: h0b41bf4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 33601 + timestamp: 1680112270483 +- kind: conda + name: libuuid + version: 2.38.1 + build: hb4cce97_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + sha256: 616277b0c5f7616c2cdf36f6c316ea3f9aa5bb35f2d4476a349ab58b9b91675f + md5: 000e30b09db0b7c775b21695dff30969 + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 35720 + timestamp: 1680113474501 +- kind: conda + name: libuv + version: 1.49.2 + build: h7ab814d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + sha256: 0e5176af1e788ad5006cf261c4ea5a288a935fda48993b0240ddd2e562dc3d02 + md5: 4bc348e3a1a74d20a3f9beb866d75e0a + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 410500 + timestamp: 1729322654121 +- kind: conda + name: libuv + version: 1.49.2 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + sha256: adf4eca89339ac7780f2394e7e6699be81259eb91f79f9d9fdf2c1bc6b26f210 + md5: 1899e1ec2be63386c41c4db31d3056af + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 627484 + timestamp: 1729322575379 +- kind: conda + name: libuv + version: 1.49.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + sha256: a35cd81cd1a9add11024097da83cc06b0aae83186fe4124b77710876f37d8f31 + md5: 070e3c9ddab77e38799d5c30b109c633 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 884647 + timestamp: 1729322566955 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + sha256: 10dded60f274e29c573cfacf6e96f5d0fc374ee431250374a44cbd773916ab9d + md5: 5fd7ab3e5f382c70607fbac6335e6e19 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 363577 + timestamp: 1713201785160 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h93a5062_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + sha256: 0d4bad713a512d79bfeb4d61821f447afab8b0792aca823f505ce6b195e9fde5 + md5: c0af0edfebe780b19940e94871f1a765 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 287750 + timestamp: 1713200194013 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + sha256: 49bc5f6b1e11cb2babf2a2a731d1a680a5e08a858280876a779dbda06c78c35f + md5: b26e8aa824079e1be0294e7152ca4559 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 438953 + timestamp: 1713199854503 +- kind: conda + name: libxcb + version: 1.17.0 + build: h262b8f6_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b + md5: cd14ee5cca2464a425b1dbfc24d90db2 + depends: + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 397493 + timestamp: 1727280745441 +- kind: conda + name: libxcb + version: 1.17.0 + build: h8a09558_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 395888 + timestamp: 1727278577118 +- kind: conda + name: libxcb + version: 1.17.0 + build: hdb1d25a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 323658 + timestamp: 1727278733917 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f + md5: b4df5d7d4b63579d081fd3a4cf99740e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 114269 + timestamp: 1702724369203 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 100393 + timestamp: 1702724383534 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h0d44e9d_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + sha256: 306e18aa647d8208ad2cd0e62d84933222b2fbe93d2d53cd5283d2256b1d54de + md5: f5b05674697ae7d2c5932766695945e1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - icu <0.0a0 + license: MIT + license_family: MIT + size: 689993 + timestamp: 1733443678322 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h178c5d8_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 + md5: 3dc3cff0eca1640a6acbbfab2f78139e + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 582898 + timestamp: 1733443841584 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h2e0c361_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 + md5: 63410f85031930cde371dfe0ee89109a + depends: + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 732155 + timestamp: 1733443825814 +- kind: conda + name: libzlib + version: 1.3.1 + build: h8359307_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 46438 + timestamp: 1727963202283 +- kind: conda + name: libzlib + version: 1.3.1 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 + md5: 08aad7cbe9f5a6b460d0976076b6ae64 + depends: + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 66657 + timestamp: 1727963199518 +- kind: conda + name: libzlib + version: 1.3.1 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 60963 + timestamp: 1727963148474 +- kind: conda + name: llvm-openmp + version: 19.1.5 + build: hdb05f8b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + sha256: e7ba0d8b718925efdcf1309f5e776e3264cc172d3af8d4048b39627c50a1abc0 + md5: f2c2e187a1d2637d282e34dc92021a70 + depends: + - __osx >=11.0 + constrains: + - openmp 19.1.5|19.1.5.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 281120 + timestamp: 1733376089600 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h286801f_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 167055 + timestamp: 1733741040117 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + sha256: 67e55058d275beea76c1882399640c37b5be8be4eb39354c94b610928e9a0573 + md5: 6654e411da94011e8fbe004eacb8fe11 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 184953 + timestamp: 1733740984533 +- kind: conda + name: markdown-it-py + version: 3.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a + md5: fee3164ac23dfca50cfcc8b85ddefb81 + depends: + - mdurl >=0.1,<1 + - python >=3.9 + license: MIT + license_family: MIT + size: 64430 + timestamp: 1733250550053 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h178313f_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + sha256: 4a6bf68d2a2b669fecc9a4a009abd1cf8e72c2289522ff00d81b5a6e51ae78f5 + md5: eb227c3e0bf58f5bd69c0532b157975b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24604 + timestamp: 1733219911494 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h74ce7d3_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + sha256: 1d500158262f30b9c23e37d1c861fe76e127a3926d69b3b38c25d20d3faa6f9f + md5: bc8607ab678073a0441808a31465f4fb + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 25079 + timestamp: 1733220639175 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h998013c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + sha256: 4aa997b244014d3707eeef54ab0ee497d12c0d0d184018960cce096169758283 + md5: 46e547061080fddf9cf95a0327e8aba6 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24048 + timestamp: 1733219945697 +- kind: conda + name: matplotlib-inline + version: 0.1.7 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.7-pyhd8ed1ab_1.conda + sha256: 69b7dc7131703d3d60da9b0faa6dd8acbf6f6c396224cf6aef3e855b8c0c41c6 + md5: af6ab708897df59bd6e7283ceab1b56b + depends: + - python >=3.9 + - traitlets + license: BSD-3-Clause + license_family: BSD + size: 14467 + timestamp: 1733417051523 +- kind: conda + name: max + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + sha256: 0e3c1984ac7476550fd8fa5921bf1ca58950219b84ae21ecd861f650e45382ac + md5: e04b1405f630c9bb7d4cb5559840e902 + depends: + - max-core ==24.6.0 release + - max-python >=24.6.0,<25.0a0 + - mojo-jupyter ==24.6.0 release + - mblack ==24.6.0 release + license: LicenseRef-Modular-Proprietary + size: 9851 + timestamp: 1734039439696 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + sha256: 38a4128c15b230f5b05e0606a339c7866a83eb943d334a948b3a8c1d2675a917 + md5: 25e678ff7c59e36ec3154fe0cd15ebde + depends: + - mblack ==24.6.0 release + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 247670119 + timestamp: 1734039439695 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + sha256: 554f2c1a6ddfab8fabf82b9fbcc1adb87e2a615669793e463f757ea452e02016 + md5: 2ca790aa461fa28722c6f21fcd2af0b9 + depends: + - mblack ==24.6.0 release + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 251486610 + timestamp: 1734039413769 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + sha256: 434c29e35067e296db55525cd5cf38bb013a1f7a7bfa99845bf6c317de6cdc12 + md5: 4a2ead0a9010c36b6193ea32f583e996 + depends: + - mblack ==24.6.0 release + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 212001240 + timestamp: 1734039726703 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + sha256: 6fbf7330ad910e6ec9fd581fd0f8505e5b1326ccf9979d553c70c61abf4c3e54 + md5: 218ecd662f853ea1578404799d61b385 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 123785050 + timestamp: 1734039439704 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + sha256: 15f57cb436b00c510473ca1940edd9ee03df6c92d5b66d616db30a131b768b78 + md5: 97c62f17f0d34787d29128043a593877 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 127403269 + timestamp: 1734039413779 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + sha256: c888b58cfc7c767d40aa100ff2bccf5c3ab11d58d897a6accb749e6b5b7014ea + md5: 62a92bfab3b5c85c2d246672bbb8bc8d + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 112484803 + timestamp: 1734039726707 +- kind: conda + name: mblack + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + sha256: f135164020478078f4681aa77e7f6ca9f68b8e7ee02604b85342bbaf2f706f0d + md5: 77367aff981ba391ab5c047ba33ec978 + depends: + - python >=3.9,<3.13 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - python + license: MIT + size: 130668 + timestamp: 1734039439700 +- kind: conda + name: mdurl + version: 0.1.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- kind: conda + name: mistune + version: 3.0.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_1.conda + sha256: 0a9faaf1692b74f321cedbd37a44f108a1ec3f5d9638bc5bbf860cb3b6ff6db4 + md5: c46df05cae629e55426773ac1f85d68f + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 65901 + timestamp: 1733258822603 +- kind: conda + name: mojo-jupyter + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + sha256: 2fe043d98ea77f8f165b39bd252cd04942216c8533f0291c49d87d6cfd8673df + md5: b17127f3ca2cef0976496407e1cd4081 + depends: + - max-core ==24.6.0 release + - python >=3.9,<3.13 + - jupyter_client >=8.6.2,<8.7 + - python + license: LicenseRef-Modular-Proprietary + size: 22990 + timestamp: 1734039439702 +- kind: conda + name: multidict + version: 6.1.0 + build: py312h178313f_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + sha256: b05bc8252a6e957bf4a776ed5e0e61d1ba88cdc46ccb55890c72cc58b10371f4 + md5: 5b5e3267d915a107eca793d52e1b780a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 61507 + timestamp: 1733913288935 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hcc812fe_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + sha256: ff9f767ba4df68e9ac2a380529a83a2fb6abd985beee9eab16608f7e2c3ccc6e + md5: dcf3ae213cf0ab40ebcc10452e1ed9fa + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 63077 + timestamp: 1733913233032 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hdb8e49c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + sha256: 482fd09fb798090dc8cce2285fa69f43b1459099122eac2fb112d9b922b9f916 + md5: 0048335516fed938e4dd2c457b4c5b9b + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 55968 + timestamp: 1729065664275 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h02f2b3b_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + sha256: 8041371e3ec3fbc2ca13c71b0180672896e6382e62892d9f6b11a4c5dd675951 + md5: 910ef2223c71902175418d9163152788 + depends: + - dill >=0.3.6 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335147 + timestamp: 1695459275360 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h98912ed_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + sha256: bb612a921fafda6375a2204ffebd8811db8dd3b8f25ac9886cc9bcbff7e3664e + md5: 5a64b9f44790d9a187a85366dd0ffa8d + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335666 + timestamp: 1695459025249 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312hdd3e373_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + sha256: c53362cdf346f314e111faddc53061e3fd2ece0ba68ca303f5dd109976df158f + md5: 173a1692d2b3ddc265dc6afd21a869b3 + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 336110 + timestamp: 1695459137796 +- kind: conda + name: mypy_extensions + version: 1.0.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + sha256: 1895f47b7d68581a6facde5cb13ab8c2764c2e53a76bd746f8f98910dc4e08fe + md5: 29097e7ea634a45cc5386b95cac6568f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 10854 + timestamp: 1733230986902 +- kind: conda + name: nbclient + version: 0.10.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.1-pyhd8ed1ab_0.conda + sha256: 564e22c4048f2f00c7ee79417dea364f95cf069a1f2565dc26d5ece1fc3fd779 + md5: 3ee79082e59a28e1db11e2a9c3bcd85a + depends: + - jupyter_client >=6.1.12 + - jupyter_core >=4.12,!=5.0.* + - nbformat >=5.1 + - python >=3.8 + - traitlets >=5.4 + license: BSD-3-Clause + license_family: BSD + size: 27878 + timestamp: 1732882434219 +- kind: conda + name: nbconvert-core + version: 7.16.4 + build: pyhff2d567_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.4-pyhff2d567_2.conda + sha256: 03a1303ce135a8214b450e751d93c9048f55edb37f3f9f06c5e9d78ba3ef2a89 + md5: 0457fdf55c88e52e0e7b63691eafcc48 + depends: + - beautifulsoup4 + - bleach + - defusedxml + - entrypoints >=0.2.2 + - jinja2 >=3.0 + - jupyter_core >=4.7 + - jupyterlab_pygments + - markupsafe >=2.0 + - mistune >=2.0.3,<4 + - nbclient >=0.5.0 + - nbformat >=5.1 + - packaging + - pandocfilters >=1.4.1 + - pygments >=2.4.1 + - python >=3.8 + - tinycss2 + - traitlets >=5.0 + constrains: + - nbconvert =7.16.4=*_2 + - pandoc >=2.9.2,<4.0.0 + license: BSD-3-Clause + license_family: BSD + size: 188505 + timestamp: 1733405603619 +- kind: conda + name: nbformat + version: 5.10.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + sha256: 7a5bd30a2e7ddd7b85031a5e2e14f290898098dc85bea5b3a5bf147c25122838 + md5: bbe1963f1e47f594070ffe87cdf612ea + depends: + - jsonschema >=2.6 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-fastjsonschema >=2.15 + - traitlets >=5.1 + license: BSD-3-Clause + license_family: BSD + size: 100945 + timestamp: 1733402844974 +- kind: conda + name: ncurses + version: '6.5' + build: h7bae524_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc + md5: cb2b0ea909b97b3d70cd3921d1445e1a + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 802321 + timestamp: 1724658775723 +- kind: conda + name: ncurses + version: '6.5' + build: hcccb83c_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + sha256: acad4cf1f57b12ee1e42995e6fac646fa06aa026529f05eb8c07eb0a84a47a84 + md5: 91d49c85cacd92caa40cf375ef72a25d + depends: + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 924472 + timestamp: 1724658573518 +- kind: conda + name: ncurses + version: '6.5' + build: he02047a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a + md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 889086 + timestamp: 1724658547447 +- kind: conda + name: nest-asyncio + version: 1.6.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + sha256: bb7b21d7fd0445ddc0631f64e66d91a179de4ba920b8381f29b9d006a42788c0 + md5: 598fd7d4d0de2455fb74f56063969a97 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 11543 + timestamp: 1733325673691 +- kind: conda + name: notebook-shim + version: 0.2.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda + sha256: 7b920e46b9f7a2d2aa6434222e5c8d739021dbc5cc75f32d124a8191d86f9056 + md5: e7f89ea5f7ea9401642758ff50a2d9c1 + depends: + - jupyter_server >=1.8,<3 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 16817 + timestamp: 1733408419340 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h470d778_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + sha256: 23767677a7790bee5457d5e75ebd508b9a31c5354216f4310dd1acfca3f7a6f9 + md5: 9cebf5a06cb87d4569cd68df887af476 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6614296 + timestamp: 1707225994762 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h8442bc7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6073136 + timestamp: 1707226249608 +- kind: conda + name: numpy + version: 1.26.4 + build: py312heda63a1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 + md5: d8285bea2a350f63fab23bf460221f3f + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 7484186 + timestamp: 1707225809722 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h3f56577_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d + md5: 04231368e4af50d11184b50e14250993 + depends: + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 377796 + timestamp: 1733816683252 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h5fbd93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 + md5: 9e5816bc95d285c115a3ebc2f8563564 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 342988 + timestamp: 1733816638720 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h8a3d83b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + sha256: 1d59bc72ca7faac06d349c1a280f5cfb8a57ee5896f1e24225a997189d7418c7 + md5: 4b71d78648dbcf68ce8bf22bb07ff838 + depends: + - __osx >=11.0 + - libcxx >=18 + - libpng >=1.6.44,<1.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 319362 + timestamp: 1733816781741 +- kind: conda + name: openssl + version: 3.4.0 + build: h39f12f2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + sha256: bd1d58ced46e75efa3b842c61642fd12272c69e9fe4d7261078bc082153a1d53 + md5: df307bbc703324722df0293c9ca2e418 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2935176 + timestamp: 1731377561525 +- kind: conda + name: openssl + version: 3.4.0 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + sha256: 64dbbdd6384fa56338124783197f7ad9048c989a02264bcd2e07355e3570f113 + md5: b2f202b5bddafac824eb610b65dde98f + depends: + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 3474825 + timestamp: 1731379200886 +- kind: conda + name: openssl + version: 3.4.0 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + sha256: 814b9dff1847b132c676ee6cc1a8cb2d427320779b93e1b6d76552275c128705 + md5: 23cc74f77eb99315c0360ec3533147a9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 2947466 + timestamp: 1731377666602 +- kind: conda + name: opentelemetry-api + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + sha256: 296280c8ace35c0a1cf72bed1077f248b3af903c3bf92332f1783a207cb5abdb + md5: 307b05402c1a382f2f09426492dee8f8 + depends: + - deprecated >=1.2.6 + - importlib-metadata >=6.0,<=8.5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 44166 + timestamp: 1734132973331 +- kind: conda + name: opentelemetry-exporter-otlp-proto-common + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + sha256: ae9776efe52564e0d6711cfcee7c54439273e57a3999f7f796f66e862f58aae9 + md5: 0c02e74d26bce3fec93b227cf7ea6e6b + depends: + - backoff >=1.10.0,<3.0.0 + - opentelemetry-proto 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 18922 + timestamp: 1734310457116 +- kind: conda + name: opentelemetry-exporter-otlp-proto-http + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + sha256: 5d61db9d5b4f91b3932f5f2348920d5b7fdaa09e52c8ea054cf7bf3f21677c9c + md5: 223f4e56a29601c887f0dc467034af5b + depends: + - deprecated >=1.2.6 + - googleapis-common-protos >=1.52,<2.dev0 + - opentelemetry-api >=1.15,<2.dev0 + - opentelemetry-exporter-otlp-proto-common 1.29.0 + - opentelemetry-proto 1.29.0 + - opentelemetry-sdk 1.29.0 + - python >=3.9 + - requests >=2.7,<3.dev0 + license: Apache-2.0 + size: 17147 + timestamp: 1734345675510 +- kind: conda + name: opentelemetry-exporter-prometheus + version: 1.12.0rc1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + sha256: b8239230dbbdb491401e41b53bd9f21d60551cedef1a8d5807fca1bf9bdd331c + md5: 1ddc95052b31147d1e10d818cf519cf5 + depends: + - opentelemetry-api >=1.10.0 + - opentelemetry-sdk >=1.10.0 + - prometheus_client >=0.5.0,<1.0.0 + - python >=3.6 + license: Apache-2.0 + license_family: APACHE + size: 14721 + timestamp: 1695214221489 +- kind: conda + name: opentelemetry-proto + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + sha256: 200a7cb8acc8a0ddd6ef55c5460cec871b6a265929b240a0296c0ccb9c8d9758 + md5: e2a6d2ad10b813c7fdc1c64aac376128 + depends: + - protobuf <6.0,>=5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 37235 + timestamp: 1734291034372 +- kind: conda + name: opentelemetry-sdk + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + sha256: 7b36629d8b8be8a019fcfd1518d7b7f862dd25de96f8adcadb93e4fd12cf9bd6 + md5: 2a8893f06e6ebda4bfa78875bc923ea4 + depends: + - opentelemetry-api 1.29.0 + - opentelemetry-semantic-conventions 0.50b0 + - python >=3.9 + - typing-extensions >=3.7.4 + - typing_extensions >=3.7.4 + license: Apache-2.0 + license_family: APACHE + size: 77645 + timestamp: 1734297838999 +- kind: conda + name: opentelemetry-semantic-conventions + version: 0.50b0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + sha256: 6526e70368d5bf66ef0eaa51fb800d53782dde71a24bd38f40139919a6f784dc + md5: f7111fa4188d646c8108e232d024cb99 + depends: + - deprecated >=1.2.6 + - opentelemetry-api 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 86084 + timestamp: 1734208980168 +- kind: conda + name: orc + version: 2.0.3 + build: h3c55218_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + sha256: 154b26bc4d586de33765a155c9b79ebd7f5bb36c2bbf4b8854e1631bca8d21af + md5: 0a51a3cf028b845c46ec0d1ea2d18629 + depends: + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1165179 + timestamp: 1733509923825 +- kind: conda + name: orc + version: 2.0.3 + build: h97ab989_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + sha256: 9de7e2746fde57c9b7f08ee87142014f6bb9b2d3a506839ea3e98baa99711576 + md5: 2f46eae652623114e112df13fae311cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1189462 + timestamp: 1733509801323 +- kind: conda + name: orc + version: 2.0.3 + build: hbcee414_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + sha256: e5e72438a3cd967ebc774070e8c49500d2d6d4175f349400b327fee75d3bfc05 + md5: e808cf7819eaa1735c8790d7f9f482c7 + depends: + - __osx >=11.0 + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 437391 + timestamp: 1733510118673 +- kind: conda + name: overrides + version: 7.7.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_0.conda + sha256: 5e238e5e646414d517a13f6786c7227206ace58271e3ef63f6adca4d6a4c2839 + md5: 24fba5a9d161ad8103d4e84c0e1a3ed4 + depends: + - python >=3.6 + - typing_utils + license: Apache-2.0 + license_family: APACHE + size: 30232 + timestamp: 1706394723472 +- kind: conda + name: packaging + version: '24.2' + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 + md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + size: 60164 + timestamp: 1733203368787 +- kind: conda + name: pandas + version: 2.2.2 + build: py312h14eacfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.2-py312h14eacfc_1.conda + sha256: d24c1a6e362d3f1034be308406b05a446c06f8ec974178581c7a3a13fc0110aa + md5: ea4fd304d3cd65f0ddf0dd3c46e0703a + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1 + license: BSD-3-Clause + license_family: BSD + size: 15203830 + timestamp: 1715898319015 +- kind: conda + name: pandas + version: 2.2.2 + build: py312h1d6d2e6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.2-py312h1d6d2e6_1.conda + sha256: 80fd53b68aa89b929d03874b99621ec8cc6a12629bd8bfbdca87a95f8852af96 + md5: ae00b61f3000d2284d1f2584d4dfafa8 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1 + license: BSD-3-Clause + license_family: BSD + size: 15458981 + timestamp: 1715898284697 +- kind: conda + name: pandas + version: 2.2.2 + build: py312h8ae5369_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.2-py312h8ae5369_1.conda + sha256: 664bf370d1e254f29fab3b9834ae5f692a59f7e35c64c61d9a9b9989831fd721 + md5: b38af0cd7ae3616c90a2511272385941 + depends: + - __osx >=11.0 + - libcxx >=16 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1 + license: BSD-3-Clause + license_family: BSD + size: 14476760 + timestamp: 1715898136109 +- kind: conda + name: pandocfilters + version: 1.5.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 + sha256: 2bb9ba9857f4774b85900c2562f7e711d08dd48e2add9bee4e1612fbee27e16f + md5: 457c2c8c08e54905d6954e79cb5b5db9 + depends: + - python !=3.0,!=3.1,!=3.2,!=3.3 + license: BSD-3-Clause + license_family: BSD + size: 11627 + timestamp: 1631603397334 +- kind: conda + name: parso + version: 0.8.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.4-pyhd8ed1ab_1.conda + sha256: 17131120c10401a99205fc6fe436e7903c0fa092f1b3e80452927ab377239bcc + md5: 5c092057b6badd30f75b06244ecd01c9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 75295 + timestamp: 1733271352153 +- kind: conda + name: pathspec + version: 0.12.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee + md5: 617f15191456cc6a13db418a275435e5 + depends: + - python >=3.9 + license: MPL-2.0 + license_family: MOZILLA + size: 41075 + timestamp: 1733233471940 +- kind: conda + name: pexpect + version: 4.9.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + sha256: 202af1de83b585d36445dc1fda94266697341994d1a3328fabde4989e1b3d07a + md5: d0d408b1f18883a944376da5cf8101ea + depends: + - ptyprocess >=0.5 + - python >=3.9 + license: ISC + size: 53561 + timestamp: 1733302019362 +- kind: conda + name: pickleshare + version: 0.7.5 + build: pyhd8ed1ab_1004 + build_number: 1004 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-pyhd8ed1ab_1004.conda + sha256: e2ac3d66c367dada209fc6da43e645672364b9fd5f9d28b9f016e24b81af475b + md5: 11a9d1d09a3615fc07c3faf79bc0b943 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 11748 + timestamp: 1733327448200 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h5ab5af3_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + sha256: 3cf43a5eb1f67f3a5f3ef1ec3a685f8767019cce24dbe46c4b76fee8a54fbacf + md5: 1c4bdfe659cfdedd372685ce2494e97b + depends: + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41756471 + timestamp: 1729068045876 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h7b63e92_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + sha256: 13a464bea02c0df0199c20ef6bad24a6bc336aaf55bf8d6a133d0fe664463224 + md5: 385f46a4df6f97892503a841121a9acf + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41948418 + timestamp: 1729065846594 +- kind: conda + name: pillow + version: 11.0.0 + build: py312haf37ca6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + sha256: 727b4c3faecdb6f6809cf20c5f32d2df4af34e0d5b9146b7588383bcba7990e8 + md5: dc9b51fbd2b6f7fea9b5123458864dbb + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41737424 + timestamp: 1729065920347 +- kind: conda + name: pip + version: 24.3.1 + build: pyh8b19718_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_1.conda + sha256: 376f64a6e0882144bf9f263b47c48bab0af34d6f03a52c3a5758c5225af89d93 + md5: 6727da77383b560d43d9d48338629ff4 + depends: + - python >=3.9,<3.13.0a0 + - setuptools + - wheel + license: MIT + license_family: MIT + size: 1243486 + timestamp: 1734379069310 +- kind: conda + name: pkgutil-resolve-name + version: 1.3.10 + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_2.conda + sha256: adb2dde5b4f7da70ae81309cce6188ed3286ff280355cf1931b45d91164d2ad8 + md5: 5a5870a74432aa332f7d32180633ad05 + depends: + - python >=3.9 + license: MIT AND PSF-2.0 + size: 10693 + timestamp: 1733344619659 +- kind: conda + name: platformdirs + version: 4.3.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + sha256: bb50f6499e8bc1d1a26f17716c97984671121608dc0c3ecd34858112bce59a27 + md5: 577852c7e53901ddccc7e6a9959ddebe + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 20448 + timestamp: 1733232756001 +- kind: conda + name: prometheus_client + version: 0.21.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + sha256: bc8f00d5155deb7b47702cb8370f233935704100dbc23e30747c161d1b6cf3ab + md5: 3e01e386307acc60b2f89af0b2e161aa + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 49002 + timestamp: 1733327434163 +- kind: conda + name: prompt-toolkit + version: 3.0.48 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + sha256: 79fb7d1eeb490d4cc1b79f781bb59fe302ae38cf0a30907ecde75a7d399796cc + md5: 368d4aa48358439e07a97ae237491785 + depends: + - python >=3.9 + - wcwidth + constrains: + - prompt_toolkit 3.0.48 + license: BSD-3-Clause + license_family: BSD + size: 269848 + timestamp: 1733302634979 +- kind: conda + name: propcache + version: 0.2.1 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + sha256: 5771311fb5ded614ca349c92579a0b752af55a310f40b71fc533e20625965391 + md5: 55d5742a696d7da1c1262e99b6217ceb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52747 + timestamp: 1733391916349 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + sha256: c7f62c11ed929ccf1f3d4a1e200e28be01e8d0e0786bf8f76c5893f2ea681e1b + md5: 50ab8953e7ff1333a4a47cda32e68123 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52484 + timestamp: 1733391993461 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + sha256: f8c266c494aa1e4cfb8bf0b6fca060044b2f3d65afe4c5062ebeea382e77aa6d + md5: c84e3dd97fe25a17322c4a0f670c6750 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 48225 + timestamp: 1733392308901 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h2ec8cdc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + sha256: 4884f8161602f0148ebbc1af8d3176cec80b96c83243f68aafd651986b573817 + md5: 586bead4a9dfa46faf88deb7d3a742bb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 464548 + timestamp: 1728669645013 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h6f74592_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + sha256: f874ffd38b9ae2b810e9d2e43fd8d3b778cdeaf7dea4a3e6ee4adeafe2d936cf + md5: 4b9b22bd7c53d938b207f9d0f79db183 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 472764 + timestamp: 1728669483611 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312hf02c72a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + sha256: dbcec117510ced5c12097e3eb06ebbf4512dc255733a9ace33c4249fb7e6a364 + md5: 6fda46c82abd0a080ca33de7d16ca877 + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 447369 + timestamp: 1728669902591 +- kind: conda + name: psutil + version: 6.1.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-6.1.0-py312h0bf5046_0.conda + sha256: 143a40f9c72d803744ebd6a60801c5cd17af152b293f8d59e90111ce62b53569 + md5: 61566f5c6e1d29d1d12882eb93e28532 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 493431 + timestamp: 1729847279283 +- kind: conda + name: psutil + version: 6.1.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/psutil-6.1.0-py312h66e93f0_0.conda + sha256: 0f309b435174e037d5cfe5ed26c1c5ad8152c68cfe61af17709ec31ec3d9f096 + md5: 0524eb91d3d78d76d671c6e3cd7cee82 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 488462 + timestamp: 1729847159916 +- kind: conda + name: psutil + version: 6.1.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-6.1.0-py312hb2c0f52_0.conda + sha256: f6ac1a743440e4228ad00fd6ff1a4ed99736d215ca52318db73217d07cd7180b + md5: 98849e1e8ea2bcd57667359c0a36dd3b + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 488976 + timestamp: 1729847306692 +- kind: conda + name: pthread-stubs + version: '0.4' + build: h86ecc28_1002 + build_number: 1002 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba + md5: bb5a90c93e3bac3d5690acf76b4a6386 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 8342 + timestamp: 1726803319942 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hb9d3cd8_1002 + build_number: 1002 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 8252 + timestamp: 1726802366959 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hd74edd7_1002 + build_number: 1002 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 8381 + timestamp: 1726802424786 +- kind: conda + name: ptyprocess + version: 0.7.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + sha256: a7713dfe30faf17508ec359e0bc7e0983f5d94682492469bd462cdaae9c64d83 + md5: 7d9daffbb8d8e0af0f769dbbcd173a54 + depends: + - python >=3.9 + license: ISC + size: 19457 + timestamp: 1733302371990 +- kind: conda + name: pure_eval + version: 0.2.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + sha256: 71bd24600d14bb171a6321d523486f6a06f855e75e547fa0cb2a0953b02047f0 + md5: 3bfdfb8dbcdc4af1ae3f9a8eb3948f04 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 16668 + timestamp: 1733569518868 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h1f38498_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + sha256: 06c0e208d5bf15051874097366c8e8e5db176dffba38526f227a34e80cc8e9bc + md5: 3710616b880b31d0c8afd8ae7e12392a + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25375 + timestamp: 1732610892198 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h7900ff3_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + sha256: 46a61c29375d3bf1933eae61c7861394c168898915d59fc99bf05e46de2ff5ad + md5: ac65b70df28687c6af4270923c020bdd + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25213 + timestamp: 1732610785600 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h8025657_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + sha256: 49db959887cb89b44053a44a98d0f35644fc0b2003587492f02b56046de0b60a + md5: 9bb7d32e96a5dcb5ea7fd90a11a83656 + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25374 + timestamp: 1732611006864 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h01725c0_0_cpu + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + sha256: 948a4161c56f846d374a3721a657e58ddbc992a29b3b3e7a6411975c30361d94 + md5: ee80934a6c280ff8635f8db5dec11e04 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4612916 + timestamp: 1732610377259 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h66f7834_0_cpu + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + sha256: e7eb062145be554c23dfefa0ebe8c5f6ae8c59635117a6921e66403d6addcda3 + md5: 3390c8b8f57e85506c92a37cf750bdd7 + depends: + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4406662 + timestamp: 1732610939832 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312hc40f475_0_cpu + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + sha256: 063eb168a29d4ce6d9ed865e9e1ad3b6e141712189955a79e06b24ddc0cbbc9c + md5: 9859e7c4b94bbf69772dbf0511101cec + depends: + - __osx >=11.0 + - libarrow 18.1.0.* *cpu + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 3909116 + timestamp: 1732610863261 +- kind: conda + name: pycparser + version: '2.22' + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- kind: conda + name: pydantic + version: 2.10.3 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + sha256: cac9eebd3d5f8d8a497a9025d756257ddc75b8b3393e6737cb45077bd744d4f8 + md5: 194ef7f91286978521350f171b117f01 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.27.1 + - python >=3.9 + - typing-extensions >=4.6.1 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + size: 317037 + timestamp: 1733316963547 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + sha256: c89741f4eff395f8de70975f42e1f20591f0e0870929d440af35b13399976b09 + md5: 114030cb28527db2c385f07038e914c8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1635156 + timestamp: 1732254225040 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + sha256: 1f59bc1914f77faed3c95217e4d093310771baee4e93a15c0479359559e3fa19 + md5: d980860b8bf193f53d30a19c5d2bf070 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1503747 + timestamp: 1732254331303 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + sha256: 5bba8de2bbbbdb39390abb1e2aff310e8cfd49646ae5a0e0ea4d6582bd1d52ba + md5: 3847a96eaf24a877b6091150ff9c4955 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1449057 + timestamp: 1732254359451 +- kind: conda + name: pydantic-settings + version: 2.7.0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + sha256: dd1ac7c8b6a189c8aa18f6c7df019d8f6df495300a259e3fbebdb542fc955c3b + md5: d9f19a7c4199249fa229891b573b6f9b + depends: + - pydantic >=2.7.0 + - python >=3.9 + - python-dotenv >=0.21.0 + license: MIT + license_family: MIT + size: 31426 + timestamp: 1734127929720 +- kind: conda + name: pygments + version: 2.18.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + sha256: 0d6133545f268b2b89c2617c196fc791f365b538d4057ecd636d658c3b1e885d + md5: b38dc0206e2a530e5c2cf11dc086b31a + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 876700 + timestamp: 1733221731178 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + sha256: 6879d52fb0ec2258e2850476786a652c394220d53883c53691ed5390183ae925 + md5: f0e4a98d54477083ddc9d2f33507f848 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 181512 + timestamp: 1728714205508 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + sha256: 8a006507a4003fb01eeee2f9ba79f994478694766ea3b445273da5c11cf8e763 + md5: 798f42d9bfdf125dc80ffbec0e96e0b6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 182021 + timestamp: 1728714164706 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + sha256: 7967b94b8f0ff75847302444e9c43ac11a391d74da24cb14fba1049fac9e5ba9 + md5: 5274663cb05dfbe316db50af6da4389f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 183141 + timestamp: 1728714267954 +- kind: conda + name: pyobjc-core + version: 10.3.2 + build: py312hb9d441b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-10.3.2-py312hb9d441b_0.conda + sha256: 6c110c64e7cc0a28416414446698ab310a9261525a6aa630b2c4f50891867719 + md5: 663e894deb5a24c8931fd8224f19a1fd + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - setuptools + license: MIT + license_family: MIT + size: 484571 + timestamp: 1732987487536 +- kind: conda + name: pyobjc-framework-cocoa + version: 10.3.2 + build: py312hb9d441b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-10.3.2-py312hb9d441b_0.conda + sha256: 5a78f97cb7414cb4b78b777dcfcffb08da42ced866e8ef6455a57c2230908bfe + md5: 41e4f28d545565e48f1f819cf8dac5c7 + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pyobjc-core 10.3.2.* + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 380414 + timestamp: 1733168930888 +- kind: conda + name: pysocks + version: 1.7.1 + build: pyha55dd90_7 + build_number: 7 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 +- kind: conda + name: python + version: 3.12.8 + build: h1683364_1_cpython + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + sha256: 85573582d5b0f79923fed0a8365d3d74d21eee9f0a5fa1b9345f191e006363ab + md5: 09ec612ea05370989eaa3d81abf0f369 + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 13760816 + timestamp: 1733407890896 +- kind: conda + name: python + version: 3.12.8 + build: h9e4cc4f_1_cpython + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + sha256: 3f0e0518c992d8ccfe62b189125721309836fe48a010dc424240583e157f9ff0 + md5: 7fd2fd79436d9b473812f14e86746844 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 31565686 + timestamp: 1733410597922 +- kind: conda + name: python + version: 3.12.8 + build: hc22306f_1_cpython + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + sha256: 7586a711b1b08a9df8864e26efdc06980bdfb0e18d5ac4651d0fee30a8d3e3a0 + md5: 54ca5b5d92ef3a3ba61e195ee882a518 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 12998673 + timestamp: 1733408900971 +- kind: conda + name: python-dateutil + version: 2.9.0.post0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + sha256: a50052536f1ef8516ed11a844f9413661829aa083304dc624c5925298d078d79 + md5: 5ba79d7c71f03c678c8ead841f347d6e + depends: + - python >=3.9 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222505 + timestamp: 1733215763718 +- kind: conda + name: python-dotenv + version: 1.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 + md5: e5c6ed218664802d305e79cc2d4491de + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 24215 + timestamp: 1733243277223 +- kind: conda + name: python-fastjsonschema + version: 2.21.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.1-pyhd8ed1ab_0.conda + sha256: 1b09a28093071c1874862422696429d0d35bd0b8420698003ac004746c5e82a2 + md5: 38e34d2d1d9dca4fb2b9a0a04f604e2c + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 226259 + timestamp: 1733236073335 +- kind: conda + name: python-json-logger + version: 2.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca + md5: a61bf9ec79426938ff785eb69dbb1960 + depends: + - python >=3.6 + license: BSD-2-Clause + license_family: BSD + size: 13383 + timestamp: 1677079727691 +- kind: conda + name: python-multipart + version: 0.0.20 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 27913 + timestamp: 1734420869885 +- kind: conda + name: python-tzdata + version: '2024.2' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + sha256: 57c9a02ec25926fb48edca59b9ede107823e5d5c473b94a0e05cc0b9a193a642 + md5: c0def296b2f6d2dd7b030c2a7f66bb1f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 142235 + timestamp: 1733235414217 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + sha256: 28204ef48f028a4d872e22040da0dad7ebd703549b010a1bb511b6dd94cf466d + md5: 266fe1ae54a7bb17990206664d0f0ae4 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 21765 + timestamp: 1725272382968 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h52516f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + sha256: 0fa5ba80073a43391ee90303814adbc9fd826175de1fdac273ba0e5b711aa255 + md5: 591c4ae6d8338dfd07b951e00433a405 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23589 + timestamp: 1725273317965 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + sha256: 20851b1e59fee127d49e01fc73195a88ab0779f103b7d6ffc90d11142a83678f + md5: 39aed2afe4d0cf76ab3d6b09eecdbea7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23162 + timestamp: 1725272139519 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 + md5: 0424ae29b104430108f5218a66db7260 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6238 + timestamp: 1723823388266 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + sha256: 5ccdad9981753cc4a2d126e356673a21c0cd5b34e209cb8d476a3947d4ad9b39 + md5: 62b20f305498284a07dc6c45fd0e5c87 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6329 + timestamp: 1723823366253 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + sha256: 49d624e4b809c799d2bf257b22c23cf3fc4460f5570d9a58e7ad86350aeaa1f4 + md5: b76f9b1c862128e56ac7aa8cd2333de9 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6278 + timestamp: 1723823099686 +- kind: conda + name: pytz + version: '2024.2' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.2-pyhd8ed1ab_1.conda + sha256: 0a7c706b2eb13f7da5692d9ddf1567209964875710b471de6f2743b33d1ba960 + md5: f26ec986456c30f6dff154b670ae140f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 185890 + timestamp: 1733215766006 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + sha256: b06f1c15fb39695bbf707ae8fb554b9a77519af577b5556784534c7db10b52e3 + md5: 1ee23620cf46cb15900f70a1300bae55 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 187143 + timestamp: 1725456547263 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + sha256: a60705971e958724168f2ebbb8ed4853067f1d3f7059843df3903e3092bbcffa + md5: 549e5930e768548a89c23f595dac5a95 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 206553 + timestamp: 1725456256213 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + sha256: 8c515ebe1e7e85d972d72b75760af9dfac06fd11a9dba7e05c42d69aedbb303c + md5: dc5de424f7dbb9772da720dbb81317b2 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 199141 + timestamp: 1725456356043 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312h2427ae1_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + sha256: cfc4ea87d68b5f0ed64a61f500d5ea0a2310d1f281a4f95afa06c703ea1bdf7d + md5: 1f0779280c3dc1e72cfd86bd1e59791d + depends: + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 371730 + timestamp: 1728644030875 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hbf22597_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + sha256: bc303f9b11e04a515f79cd5ad3bfa0e84b9dfec76552626d6263b38789fe6678 + md5: 746ce19f0829ec3e19c93007b1a224d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 378126 + timestamp: 1728642454632 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hf8a1cbd_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + sha256: 2e0ca1bb9ab3af5d1f9b38548d65be7097ba0246e7e63c908c9b1323df3f45b5 + md5: 7bdaa4c2a84b744ef26c8b2ba65c3d0e + depends: + - __osx >=11.0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 361674 + timestamp: 1728642457661 +- kind: conda + name: re2 + version: 2024.07.02 + build: h2d3a13d_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + sha256: 55e7be480bfb979fa8595a16d7f2adea3a5ac9a77b2e97cd0f7ac40e989edb6c + md5: 83f4e47229834c895a92c18383e1cd9d + depends: + - libre2-11 2024.07.02 h18dbdb1_1 + license: BSD-3-Clause + license_family: BSD + size: 26747 + timestamp: 1728778986331 +- kind: conda + name: re2 + version: 2024.07.02 + build: h77b4e00_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + sha256: c1721cb80f7201652fc9801f49c214c88aee835d957f2376e301bd40a8415742 + md5: 01093ff37c1b5e6bf9f17c0116747d11 + depends: + - libre2-11 2024.07.02 hbbce691_1 + license: BSD-3-Clause + license_family: BSD + size: 26665 + timestamp: 1728778975855 +- kind: conda + name: re2 + version: 2024.07.02 + build: hcd0e937_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + sha256: eebddde6cb10b146507810b701ef6df122d5309cd5151a39d0828aa44dc53725 + md5: 19e29f2ccc9168eb0a39dc40c04c0e21 + depends: + - libre2-11 2024.07.02 h2348fd5_1 + license: BSD-3-Clause + license_family: BSD + size: 26860 + timestamp: 1728779123653 +- kind: conda + name: readline + version: '8.2' + build: h8228510_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 + md5: 47d31b792659ce70f470b5c82fdfb7a4 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 281456 + timestamp: 1679532220005 +- kind: conda + name: readline + version: '8.2' + build: h8fc344f_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + sha256: 4c99f7417419734e3797d45bc355e61c26520e111893b0d7087a01a7fbfbe3dd + md5: 105eb1e16bf83bfb2eb380a48032b655 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 294092 + timestamp: 1679532238805 +- kind: conda + name: readline + version: '8.2' + build: h92ec313_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 + md5: 8cbb776a2f641b943d413b3e19df71f4 + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 250351 + timestamp: 1679532511311 +- kind: conda + name: referencing + version: 0.35.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/referencing-0.35.1-pyhd8ed1ab_1.conda + sha256: f972eecb4dc8e06257af37642f92b0f2df04a7fe4c950f2e1045505e5e93985f + md5: 8c9083612c1bfe6878715ed5732605f8 + depends: + - attrs >=22.2.0 + - python >=3.9 + - rpds-py >=0.7.0 + license: MIT + license_family: MIT + size: 42201 + timestamp: 1733366868091 +- kind: conda + name: regex + version: 2024.11.6 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + sha256: fcb5687d3ec5fff580b64b8fb649d9d65c999a91a5c3108a313ecdd2de99f06b + md5: 647770db979b43f9c9ca25dcfa7dc4e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 402821 + timestamp: 1730952378415 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + sha256: ec2c416860de29224e447e2031f8686a05476759c17da1f32f61d4307e540ec8 + md5: fa8b589107567f532fa1380e66f91776 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 398947 + timestamp: 1730952477463 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + sha256: dcdec32f2c7dd37986baa692bedf9db126ad34e92e5e9b64f707cba3d04d2525 + md5: e73cda1f18846b608284bd784f061eac + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 366374 + timestamp: 1730952427552 +- kind: conda + name: requests + version: 2.32.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad + md5: a9b9368f3701a417eac9edbcae7cb737 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 58723 + timestamp: 1733217126197 +- kind: conda + name: rfc3339-validator + version: 0.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda + sha256: 2e4372f600490a6e0b3bac60717278448e323cab1c0fecd5f43f7c56535a99c5 + md5: 36de09a8d3e5d5e6f4ee63af49e59706 + depends: + - python >=3.9 + - six + license: MIT + license_family: MIT + size: 10209 + timestamp: 1733600040800 +- kind: conda + name: rfc3986-validator + version: 0.1.1 + build: pyh9f0ad1d_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 + sha256: 2a5b495a1de0f60f24d8a74578ebc23b24aa53279b1ad583755f223097c41c37 + md5: 912a71cc01012ee38e6b90ddd561e36f + depends: + - python + license: MIT + license_family: MIT + size: 7818 + timestamp: 1598024297745 +- kind: conda + name: rich + version: 13.9.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 + md5: 7aed65d4ff222bfb7335997aa40b7da5 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + size: 185646 + timestamp: 1733342347277 +- kind: conda + name: rich-toolkit + version: 0.11.3 + build: pyh29332c3_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 + md5: 4ba15ae9388b67d09782798347481f69 + depends: + - python >=3.9 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 17357 + timestamp: 1733750834072 +- kind: conda + name: rpds-py + version: 0.22.3 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.22.3-py312h12e396e_0.conda + sha256: e8662d21ca3c912ac8941725392b838a29458b106ef22d9489cdf0f8de145fad + md5: bfb49da0cc9098597d527def04d66f8b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 354410 + timestamp: 1733366814237 +- kind: conda + name: rpds-py + version: 0.22.3 + build: py312ha4e36d7_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.22.3-py312ha4e36d7_0.conda + sha256: bd8215aea86b57f7d036d53eee813a544736e94783faeb7b928931fca45715f8 + md5: 4d41b57d0d3933b9cab7ac8c05745123 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 348424 + timestamp: 1733368364148 +- kind: conda + name: rpds-py + version: 0.22.3 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.22.3-py312hcd83bfe_0.conda + sha256: 0a8b50bf22400004a706ba160d7cb31f82b8d8c328a59aec73a9e0d3372d1964 + md5: 2f7c4d01946fa2ce73d7ef3eeb041877 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 318920 + timestamp: 1733367225496 +- kind: conda + name: s2n + version: 1.5.10 + build: h5df210e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + sha256: b5e7a9f4b7b1ec5c5c3661e2defc8b47fab543b05cad6fec78739d8007612464 + md5: 3d3979efcc0f44f3f0cef3de03b296cc + depends: + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 353450 + timestamp: 1734415474615 +- kind: conda + name: s2n + version: 1.5.10 + build: hb5b8611_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + sha256: f6d451821fddc26b93f45e9313e1ea15e09e5ef049d4e137413a5225d2a5dfba + md5: 999f3673f2a011f59287f2969e3749e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 355142 + timestamp: 1734415467047 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + sha256: e44515f875c10efb5e041efcb250dfd18f2cb66ec3f268237549ead6284c6922 + md5: 3b87a00bcaab069172d6cef8124b7142 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 402547 + timestamp: 1725632183154 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + sha256: e83ebeaba4a07bbe4a1d6c7eef0b4f7ae19901ef365bca043808d16e4c8faad8 + md5: 82ef253c37308b082a478fb92924cad6 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 400284 + timestamp: 1725632278147 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312he431725_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + sha256: 93a085d0d64237db7f4ff395c446f268c575dc2c324d8e3e5c5d7d836896295e + md5: ccb978cf1e3151c25a44c4ae65c1f20e + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 353606 + timestamp: 1725632294079 +- kind: conda + name: send2trash + version: 1.8.3 + build: pyh0d859eb_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda + sha256: 00926652bbb8924e265caefdb1db100f86a479e8f1066efe395d5552dde54d02 + md5: 938c8de6b9de091997145b3bf25cdbf9 + depends: + - __linux + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 22736 + timestamp: 1733322148326 +- kind: conda + name: send2trash + version: 1.8.3 + build: pyh31c8845_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh31c8845_1.conda + sha256: 5282eb5b462502c38df8cb37cd1542c5bbe26af2453a18a0a0602d084ca39f53 + md5: e67b1b1fa7a79ff9e8e326d0caf55854 + depends: + - __osx + - pyobjc-framework-cocoa + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 23100 + timestamp: 1733322309409 +- kind: conda + name: setuptools + version: 75.6.0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.6.0-pyhff2d567_1.conda + sha256: abb12e1dd515b13660aacb5d0fd43835bc2186cab472df25b7716cd65e095111 + md5: fc80f7995e396cbaeabd23cf46c413dc + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 774252 + timestamp: 1732632769210 +- kind: conda + name: shellingham + version: 1.5.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef + md5: 7c3c2a0f3ebdea2bbc35538d162b43bf + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14462 + timestamp: 1733301007770 +- kind: conda + name: six + version: 1.17.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + sha256: 41db0180680cc67c3fa76544ffd48d6a5679d96f4b71d7498a759e94edc9a2db + md5: a451d576819089b0d672f18768be0f65 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 16385 + timestamp: 1733381032766 +- kind: conda + name: snappy + version: 1.2.1 + build: h8bd8927_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + sha256: ec91e86eeb2c6bbf09d51351b851e945185d70661d2ada67204c9a6419d282d3 + md5: 3b3e64af585eadfb52bb90b553db5edf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 42739 + timestamp: 1733501881851 +- kind: conda + name: snappy + version: 1.2.1 + build: h98b9ce2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f + md5: ded86dee325290da2967a3fea3800eb5 + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-3-Clause + license_family: BSD + size: 35857 + timestamp: 1733502172664 +- kind: conda + name: snappy + version: 1.2.1 + build: hd4fb6f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + sha256: c4a07ae5def8d55128f25a567a296ef9d7bf99a3bc79d46bd5160c076a5f50af + md5: 2fcc6cd1e5550deb509073fd2e6693e1 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 43032 + timestamp: 1733501964775 +- kind: conda + name: sniffio + version: 1.3.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 + md5: bf7a226e58dfb8346c70df36065d86c9 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 15019 + timestamp: 1733244175724 +- kind: conda + name: soupsieve + version: '2.5' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda + sha256: 54ae221033db8fbcd4998ccb07f3c3828b4d77e73b0c72b18c1d6a507059059c + md5: 3f144b2c34f8cb5a9abd9ed23a39c561 + depends: + - python >=3.8 + license: MIT + license_family: MIT + size: 36754 + timestamp: 1693929424267 +- kind: conda + name: sse-starlette + version: 2.1.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + sha256: 6d671a66333410ec7e5e7858a252565a9001366726d1fe3c3a506d7156169085 + md5: 3918255c942c242ed5599e10329e8d0e + depends: + - anyio + - python >=3.8 + - starlette + - uvicorn + license: BSD-3-Clause + license_family: BSD + size: 14712 + timestamp: 1722520112550 +- kind: conda + name: stack_data + version: 0.6.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + sha256: 570da295d421661af487f1595045760526964f41471021056e993e73089e9c41 + md5: b1b505328da7a6b246787df4b5a49fbc + depends: + - asttokens + - executing + - pure_eval + - python >=3.9 + license: MIT + license_family: MIT + size: 26988 + timestamp: 1733569565672 +- kind: conda + name: starlette + version: 0.41.3 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + sha256: b74fc76107487eb26624c01fc55bfab7eed03ae82e003333c86d8a1eeac53672 + md5: 0207dac04ae2200701fab697f0aaaac4 + depends: + - anyio >=3.4.0,<5 + - python >=3.9 + - typing_extensions >=3.10.0 + license: BSD-3-Clause + license_family: BSD + size: 58838 + timestamp: 1733344472634 +- kind: conda + name: terminado + version: 0.18.1 + build: pyh0d859eb_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda + sha256: b300557c0382478cf661ddb520263508e4b3b5871b471410450ef2846e8c352c + md5: efba281bbdae5f6b0a1d53c6d4a97c93 + depends: + - __linux + - ptyprocess + - python >=3.8 + - tornado >=6.1.0 + license: BSD-2-Clause + license_family: BSD + size: 22452 + timestamp: 1710262728753 +- kind: conda + name: terminado + version: 0.18.1 + build: pyh31c8845_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh31c8845_0.conda + sha256: 4daae56fc8da17784578fbdd064f17e3b3076b394730a14119e571707568dc8a + md5: 00b54981b923f5aefcd5e8547de056d5 + depends: + - __osx + - ptyprocess + - python >=3.8 + - tornado >=6.1.0 + license: BSD-2-Clause + license_family: BSD + size: 22717 + timestamp: 1710265922593 +- kind: conda + name: tinycss2 + version: 1.4.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda + sha256: cad582d6f978276522f84bd209a5ddac824742fe2d452af6acf900f8650a73a2 + md5: f1acf5fdefa8300de697982bcb1761c9 + depends: + - python >=3.5 + - webencodings >=0.4 + license: BSD-3-Clause + license_family: BSD + size: 28285 + timestamp: 1729802975370 +- kind: conda + name: tk + version: 8.6.13 + build: h194ca79_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + sha256: 7fa27cc512d3a783f38bd16bbbffc008807372499d5b65d089a8e43bde9db267 + md5: f75105e0585851f818e0009dd1dde4dc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3351802 + timestamp: 1695506242997 +- kind: conda + name: tk + version: 8.6.13 + build: h5083fa2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 + md5: b50a57ba89c32b62428b71a875291c9b + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3145523 + timestamp: 1699202432999 +- kind: conda + name: tk + version: 8.6.13 + build: noxft_h4845f30_101 + build_number: 101 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3318875 + timestamp: 1699202167581 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312h8360d73_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + sha256: 4f504a5e9d77c6d88a8f735c4319429d8bf40b742384f908a2efe0a09acc3cc5 + md5: f953aa733207f3d37acf4a3efbedba89 + depends: + - __glibc >=2.17,<3.0.a0 + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2258007 + timestamp: 1732734202127 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312ha0d6ea1_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + sha256: ef0f4d4e2c798b1821187ea0ba4c86484e48abaa0e9a19fe68030fa7ff5dde84 + md5: 077f48c9e0c08a30d842e15c51df4143 + depends: + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2331194 + timestamp: 1732734303196 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312hf3e4074_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + sha256: 5d395333fcb22dc611140286c1f2ea8b3fa220a4931c583587cb612238091555 + md5: 4c732c74b485ef7ac8ec1c548dd45e8e + depends: + - __osx >=11.0 + - huggingface_hub >=0.16.4,<1.0 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 1931389 + timestamp: 1732734727624 +- kind: conda + name: tomli + version: 2.2.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda + sha256: 18636339a79656962723077df9a56c0ac7b8a864329eb8f847ee3d38495b863e + md5: ac944244f1fed2eb49bae07193ae8215 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 19167 + timestamp: 1733256819729 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h52516f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + sha256: 4c19a544354172b2273553267e734795a6da3c78a04c2d19f8e9e159ca3178bc + md5: e28996d9d2d44d777b7e6fb12f63715b + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 841662 + timestamp: 1732616934923 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + sha256: 062a3a3a37fa8615ce57929ba7e982c76f5a5810bcebd435950f6d6c4147c310 + md5: e417822cb989e80a0d2b1b576fdd1657 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 840414 + timestamp: 1732616043734 +- kind: conda + name: tornado + version: 6.4.2 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + sha256: 964a2705a36c50040c967b18b45b9cc8de3c2aff4af546979a574e0b38e58e39 + md5: fb0605888a475d6a380ae1d1a819d976 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 842549 + timestamp: 1732616081362 +- kind: conda + name: tqdm + version: 4.67.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d + md5: 4085c9db273a148e149c03627350e22c + depends: + - colorama + - python >=3.7 + license: MPL-2.0 or MIT + size: 89484 + timestamp: 1732497312317 +- kind: conda + name: traitlets + version: 5.14.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 + md5: 019a7385be9af33791c989871317e1ed + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110051 + timestamp: 1733367480074 +- kind: conda + name: transformers + version: 4.47.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + sha256: d31821081219a0ede5c1f356b65a61ce98ac11e2df78b0eaa684c17c73389fbf + md5: 6d2ec1ddee8057d2d724a0ab0bb578a0 + depends: + - datasets !=2.5.0 + - filelock + - huggingface_hub >=0.23.0,<1.0 + - numpy >=1.17 + - packaging >=20.0 + - python >=3.9 + - pyyaml >=5.1 + - regex !=2019.12.17 + - requests + - safetensors >=0.4.1 + - tokenizers >=0.21,<0.22 + - tqdm >=4.27 + license: Apache-2.0 + license_family: APACHE + size: 3726957 + timestamp: 1733948063517 +- kind: conda + name: typer + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + sha256: ef695490e895c2ad552c77ec497b899b09fd4ad4ab07edcf5649f5994cf92a35 + md5: 170a0398946d8f5b454e592672b6fc20 + depends: + - python >=3.9 + - typer-slim-standard 0.15.1 hd8ed1ab_0 + license: MIT + license_family: MIT + size: 56175 + timestamp: 1733408582623 +- kind: conda + name: typer-slim + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + sha256: d4965516f35e0805199de6596c4ac76c4ad3d6b012be35e532102f9e53ecb860 + md5: 0218b16f5a1dd569e575a7a6415489db + depends: + - click >=8.0.0 + - python >=3.9 + - typing_extensions >=3.7.4.3 + constrains: + - rich >=10.11.0 + - typer >=0.15.1,<0.15.2.0a0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + size: 43592 + timestamp: 1733408569554 +- kind: conda + name: typer-slim-standard + version: 0.15.1 + build: hd8ed1ab_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + sha256: f31c56fe98315da8b9ce848256c17e0b9f87896b41a6ccf0c9cc74644dcef20f + md5: 4e603c43bfdfc7b533be087c3e070cc9 + depends: + - rich + - shellingham + - typer-slim 0.15.1 pyhd8ed1ab_0 + license: MIT + license_family: MIT + size: 49531 + timestamp: 1733408570063 +- kind: conda + name: types-python-dateutil + version: 2.9.0.20241206 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20241206-pyhd8ed1ab_0.conda + sha256: 8b98cd9464837174ab58aaa912fc95d5831879864676650a383994033533b8d1 + md5: 1dbc4a115e2ad9fb7f9d5b68397f66f9 + depends: + - python >=3.9 + license: Apache-2.0 AND MIT + size: 22104 + timestamp: 1733612458611 +- kind: conda + name: typing-extensions + version: 4.12.2 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 + md5: b6a408c64b78ec7b779a3e5c7a902433 + depends: + - typing_extensions 4.12.2 pyha770c72_1 + license: PSF-2.0 + license_family: PSF + size: 10075 + timestamp: 1733188758872 +- kind: conda + name: typing_extensions + version: 4.12.2 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 + md5: d17f13df8b65464ca316cbc000a3cb64 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 39637 + timestamp: 1733188758212 +- kind: conda + name: typing_utils + version: 0.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda + sha256: 3088d5d873411a56bf988eee774559335749aed6f6c28e07bf933256afb9eb6c + md5: f6d7aa696c67756a650e91e15e88223c + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 15183 + timestamp: 1733331395943 +- kind: conda + name: tzdata + version: 2024b + build: hc8b5060_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + sha256: 4fde5c3008bf5d2db82f2b50204464314cc3c91c1d953652f7bd01d9e52aefdf + md5: 8ac3367aafb1cc0a068483c580af8015 + license: LicenseRef-Public-Domain + size: 122354 + timestamp: 1728047496079 +- kind: conda + name: uri-template + version: 1.3.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda + sha256: e0eb6c8daf892b3056f08416a96d68b0a358b7c46b99c8a50481b22631a4dfc0 + md5: e7cb0f5745e4c5035a460248334af7eb + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 23990 + timestamp: 1733323714454 +- kind: conda + name: urllib3 + version: 2.2.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + sha256: 416e30a1c3262275f01a3e22e783118d9e9d2872a739a9ed860d06fa9c7593d5 + md5: 4a2d8ef7c37b8808c5b9b750501fffce + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + size: 98077 + timestamp: 1733206968917 +- kind: conda + name: uvicorn + version: 0.34.0 + build: pyh31011fe_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa + md5: 5d448feee86e4740498ec8f8eb40e052 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.9 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + size: 48643 + timestamp: 1734293057914 +- kind: conda + name: uvicorn-standard + version: 0.34.0 + build: h31011fe_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec + md5: 32a94143a7f65d76d2d5da37dcb4ed79 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.34.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + size: 7203 + timestamp: 1734293058849 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h0bf5046_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + sha256: b1efa77aa4871d7bb09c8dd297fa9bd9070ba7f0f95f2d12ae9cdd31ce8b6b22 + md5: 4f5110253ba80ebf27e55c4ab333880a + depends: + - __osx >=11.0 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 544097 + timestamp: 1730214653726 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + sha256: 9337a80165fcf70b06b9d6ba920dad702260ca966419ae77560a15540e41ab72 + md5: 998e481e17c1b6a74572e73b06f2df08 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 701355 + timestamp: 1730214506716 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + sha256: 807eede6698bd00a1d739a3e19ee6ae6a03a66d2ddd2ef150f2dfd198c3b0292 + md5: d83e107ba16c77aba2feec47b7b666a4 + depends: + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 655266 + timestamp: 1730214606664 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + sha256: c89755d8e8f6384b3ba13e41dcabb40bf690c38b9d61512e963129badb1ad332 + md5: b76a5ad00856af6e74da9c3e85fed0cc + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 410432 + timestamp: 1733998892675 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + sha256: 9be9569c279dc6e7881e9b45fe9f0368218538c660641e2f8b0e023e72a6571c + md5: 3465c1a19634233abc2d1832ac01fd31 + depends: + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 404239 + timestamp: 1733998941045 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + sha256: b64b78a7d6384bf72a878256802c783c692fe641ab4b806fd7e9f45e18a5e3b4 + md5: 13b89e1aa72aa773806b1f59ec018b67 + depends: + - __osx >=11.0 + - anyio >=3.0.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 363162 + timestamp: 1733999215646 +- kind: conda + name: wcwidth + version: 0.2.13 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.13-pyhd8ed1ab_1.conda + sha256: f21e63e8f7346f9074fd00ca3b079bd3d2fa4d71f1f89d5b6934bf31446dc2a5 + md5: b68980f2495d096e71c7fd9d7ccf63e6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 32581 + timestamp: 1733231433877 +- kind: conda + name: webcolors + version: 24.11.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda + sha256: 08315dc2e61766a39219b2d82685fc25a56b2817acf84d5b390176080eaacf99 + md5: b49f7b291e15494aafb0a7d74806f337 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 18431 + timestamp: 1733359823938 +- kind: conda + name: webencodings + version: 0.5.1 + build: pyhd8ed1ab_3 + build_number: 3 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda + sha256: 19ff205e138bb056a46f9e3839935a2e60bd1cf01c8241a5e172a422fed4f9c6 + md5: 2841eb5bfc75ce15e9a0054b98dcd64d + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 15496 + timestamp: 1733236131358 +- kind: conda + name: websocket-client + version: 1.8.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda + sha256: 1dd84764424ffc82030c19ad70607e6f9e3b9cb8e633970766d697185652053e + md5: 84f8f77f0a9c6ef401ee96611745da8f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 46718 + timestamp: 1733157432924 +- kind: conda + name: websockets + version: '14.1' + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + sha256: 5998940f91765ba991cf286c863c20bcb53db92bb976a2b5a714566b86b0e763 + md5: a79f7ce618bd0a9f4c00c59a03570fcd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242145 + timestamp: 1731498716195 +- kind: conda + name: websockets + version: '14.1' + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + sha256: c292a8badcbe4040537e225fbeb237bfaf272808eab060067d965d3da98ccd5c + md5: 7e2a0ef2a1a87f88f9745f9c7059186e + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242912 + timestamp: 1731498811466 +- kind: conda + name: websockets + version: '14.1' + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + sha256: 98fb04a1a0f53dc604378f94b5795d0b8e462fee01bf0a887cb34d0efdf5d21f + md5: 89b79a9baa7db46ce21f5738a5a3dfda + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 243131 + timestamp: 1731498944076 +- kind: conda + name: wheel + version: 0.45.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda + sha256: 1b34021e815ff89a4d902d879c3bd2040bc1bd6169b32e9427497fa05c55f1ce + md5: 75cb7132eb58d97896e173ef12ac9986 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 62931 + timestamp: 1733130309598 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + sha256: a6fc0f4e90643d0c1fd4aab669b6a79f44a305a5474256f6f2da3354d2310fb4 + md5: ddbe3bb0e1356cb9074dd848570694f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 63807 + timestamp: 1732523690292 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + sha256: b9aa760a987ccc6bc9c61f57badba6798d9a3dcbd0814e5fb8df6d8d2935af73 + md5: 120d5d1c05386d8ce3efd65a4c86431f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 64783 + timestamp: 1732523806 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + sha256: 0fb35c3d1642f9f47db87bdb33148f88ef19a3af1eb0ee99b5491551c57269c7 + md5: 73414acdb779a8694a14527865b4357a + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 61043 + timestamp: 1732523852129 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 13593 + timestamp: 1734229104321 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 + md5: d5397424399a66d33c80b1f2345a36a6 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 15873 + timestamp: 1734230458294 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 14780 + timestamp: 1734229004433 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + sha256: efcc150da5926cf244f757b8376d96a4db78bc15b8d90ca9f56ac6e75755971f + md5: 25a5a7b797fe6e084e04ffe2db02fc62 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 20615 + timestamp: 1727796660574 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 19901 + timestamp: 1727794976192 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hd74edd7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 + md5: 77c447f48cab5d3a15ac224edb86a968 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 18487 + timestamp: 1727795205022 +- kind: conda + name: xxhash + version: 0.8.2 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + sha256: 4c526aed70b579d80e5c20d32130b6bc8bde59b3250d43c2b5269755f4da8a9b + md5: bb9faf6857108a9f62ebb4dab6ef05da + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 102442 + timestamp: 1689951682147 +- kind: conda + name: xxhash + version: 0.8.2 + build: hb547adb_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + sha256: a70f59f7221ee72c45b39a6b36a33eb9c717ba01921cce1a3c361a4676979a2e + md5: 144cd3b88706507f332f5eb5fb83a33b + license: BSD-2-Clause + license_family: BSD + size: 97593 + timestamp: 1689951969732 +- kind: conda + name: xxhash + version: 0.8.2 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f + md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 97691 + timestamp: 1689951608120 +- kind: conda + name: yaml + version: 0.2.5 + build: h3422bc3_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 + md5: 4bb3f014845110883a3c5ee811fd84b4 + license: MIT + license_family: MIT + size: 88016 + timestamp: 1641347076660 +- kind: conda + name: yaml + version: 0.2.5 + build: h7f98852_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + sha256: a4e34c710eeb26945bdbdaba82d3d74f60a78f54a874ec10d373811a5d217535 + md5: 4cb3ad778ec2d5a7acbdf254eb1c42ae + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 89141 + timestamp: 1641346969816 +- kind: conda + name: yaml + version: 0.2.5 + build: hf897c2e_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + sha256: 8bc601d6dbe249eba44b3c456765265cd8f42ef1e778f8df9b0c9c88b8558d7e + md5: b853307650cb226731f653aa623936a4 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 92927 + timestamp: 1641347626613 +- kind: conda + name: yarl + version: 1.18.3 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + sha256: a0d93c3bef723e384cff8a29a82a2c6b7a73b39328088f3a2d97c901f56e9a63 + md5: 91df2efaa08730416bec2a4502309275 + depends: + - __glibc >=2.17,<3.0.a0 + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 151393 + timestamp: 1733428897813 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + sha256: 470b5b0f3ac89acd143095281167dc2ac1a56d4fa22e1794bd8f3b00bb604540 + md5: 0b3c640697bca798d0ab428f530ed24c + depends: + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 150004 + timestamp: 1733429056665 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + sha256: 69c7863809e11bc90c0d935c16e7f151dcc925add08b3894f06059263a8cb9ba + md5: f32f9b16361866a62d6e061fcd7eb400 + depends: + - __osx >=11.0 + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 141556 + timestamp: 1733429104990 +- kind: conda + name: zeromq + version: 4.3.5 + build: h3b0a872_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + sha256: a4dc72c96848f764bb5a5176aa93dd1e9b9e52804137b99daeebba277b31ea10 + md5: 3947a35e916fcc6b9825449affbf4214 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 335400 + timestamp: 1731585026517 +- kind: conda + name: zeromq + version: 4.3.5 + build: h5efb499_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + sha256: a6003096dc0570a86492040ba32b04ce7662b159600be2252b7a0dfb9414e21c + md5: f2f3282559a4b87b7256ecafb4610107 + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 371419 + timestamp: 1731589490850 +- kind: conda + name: zeromq + version: 4.3.5 + build: hc1bb282_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + sha256: 9e585569fe2e7d3bea71972cd4b9f06b1a7ab8fa7c5139f92a31cbceecf25a8a + md5: f7e6b65943cb73bce0143737fded08f1 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=18 + - libsodium >=1.0.20,<1.0.21.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 281565 + timestamp: 1731585108039 +- kind: conda + name: zipp + version: 3.21.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 + md5: 0c3cc595284c5e8f0f9900a9b228a332 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 21809 + timestamp: 1732827613585 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312h15fbf35_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + sha256: d00ca25c1e28fd31199b26a94f8c96574475704a825d244d7a6351ad3745eeeb + md5: a4cde595509a7ad9c13b1a3809bcfe51 + depends: + - __osx >=11.0 + - cffi >=1.11 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 330788 + timestamp: 1725305806565 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hb698573_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + sha256: 2681c2a249752bdc7978e59ee2f34fcdfcbfda80029b84b8e5fec8dbc9e3af25 + md5: ffcb8e97e62af42075e0e5f46bb9856e + depends: + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 392496 + timestamp: 1725305808244 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hef9b889_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b + md5: 8b7069e9792ee4e5b4919a7a306d2e67 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 419552 + timestamp: 1725305670210 +- kind: conda + name: zstd + version: 1.5.6 + build: h02f22dd_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c + md5: be8d5f8cf21aed237b8b182ea86b3dd6 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 539937 + timestamp: 1714723130243 +- kind: conda + name: zstd + version: 1.5.6 + build: ha6fb4c9_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 554846 + timestamp: 1714722996770 +- kind: conda + name: zstd + version: 1.5.6 + build: hb46c0d2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda + sha256: 2d4fd1ff7ee79cd954ca8e81abf11d9d49954dd1fef80f27289e2402ae9c2e09 + md5: d96942c06c3e84bfcc5efb038724a7fd + depends: + - __osx >=11.0 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 405089 + timestamp: 1714723101397 diff --git a/examples/operators/magic.lock b/examples/operators/magic.lock new file mode 100644 index 0000000000..5f9ed68073 --- /dev/null +++ b/examples/operators/magic.lock @@ -0,0 +1,9153 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda +packages: +- kind: conda + name: _libgcc_mutex + version: '0.1' + build: conda_forge + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + size: 2562 + timestamp: 1578324546067 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23621 + timestamp: 1650670423406 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 + md5: 6168d71addc746e8f2b8d57dfd2edcea + depends: + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23712 + timestamp: 1650670790230 +- kind: conda + name: aiohappyeyeballs + version: 2.4.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + sha256: 95d4713e49ea92ae50cf42393683ede706b7875af5f7cb14c253438180afa732 + md5: 296b403617bafa89df4971567af79013 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 19351 + timestamp: 1733332029649 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h178313f_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + sha256: dc8ebdd99e9d7a07454a7063a295cdc7a86264648647fec10b2ceae97478e200 + md5: 3e92784b8e32ab7d0b95ee296ba79a99 + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 914378 + timestamp: 1733839626367 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h998013c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + sha256: 69eb9c89dce6a7ae960099172daffba9f77fef39344f37e581685a8e3c5debe6 + md5: 642356223364539ba7ba36556fcf49ee + depends: + - __osx >=11.0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 874135 + timestamp: 1733839113411 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312hcc812fe_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + sha256: df694a9fec546e575a4ea7e1c3ac476c0bda53c0fad44c046ad3ebdd5b75a0a8 + md5: a8c9ec59e6323b38418bbf04deaa0c02 + depends: + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 900931 + timestamp: 1733839037447 +- kind: conda + name: aiosignal + version: 1.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + sha256: 7de8ced1918bbdadecf8e1c1c68237fe5709c097bd9e0d254f4cad118f4345d0 + md5: 1a3981115a398535dbe3f6d5faae3d36 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 13229 + timestamp: 1734342253061 +- kind: conda + name: annotated-types + version: 0.7.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- kind: conda + name: anyio + version: 4.7.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + sha256: 687537ee3af30f8784986bf40cac30e88138770b16e51ca9850c9c23c09aeba1 + md5: c88107912954a983c2caf25f7fd55158 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.9 + - sniffio >=1.1 + - typing_extensions >=4.5 + constrains: + - trio >=0.26.1 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 112730 + timestamp: 1733532678437 +- kind: conda + name: attrs + version: 24.3.0 + build: pyh71513ae_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + sha256: 750186af694a7130eaf7119fbb56db0d2326d8995ad5b8eae23c622b85fea29a + md5: 356927ace43302bf6f5926e2a58dae6a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 56354 + timestamp: 1734348889193 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h2cb9fb3_15 + build_number: 15 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + sha256: 4ce859dc9ff128bf5515604c43f33fb511386022fc9765ca077990f2a3f23df5 + md5: e524686ace966acefb5b8cbc6e8b3daa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 111854 + timestamp: 1734021745104 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h8bc59a9_15 + build_number: 15 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + sha256: 0e41e56b662e76e024182adebcd91d09a4d38a83b35217c84e4967354dfff9a2 + md5: f688b8893c20ad9477a19e7ce614014a + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + license: Apache-2.0 + license_family: Apache + size: 92507 + timestamp: 1734021831330 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: hb921021_15 + build_number: 15 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + sha256: 537006ad6d5097c134494166a6a1dc1451d5d050878d7b82cef498bfda40ba8a + md5: c79d50f64cffa5ad51ecc1a81057962f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 107614 + timestamp: 1734021692519 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h1a47875_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + sha256: 095ac824ea9303eff67e04090ae531d9eb33d2bf8f82eaade39b839c421e16e8 + md5: 55a8561fdbbbd34f50f57d9be12ed084 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 47601 + timestamp: 1733991564405 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h740c5af_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + sha256: c5c7961d48ca7320ed3560c036f7aa5244df4b85d9657f70aacc5faba3e1509a + md5: 57ed2c445d7ef01d121b9bcea0522913 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 50036 + timestamp: 1733991581303 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: hc8a0bd2_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + sha256: 1f44be36e1daa17b4b081debb8aee492d13571084f38b503ad13e869fef24fe4 + md5: 8b0ce61384e5a33d2b301a64f3d22ac5 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 39925 + timestamp: 1733991649383 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a + md5: 145e5b4c9702ed279d7d68aaf096f77d + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221863 + timestamp: 1733975576886 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + sha256: 57288ec5df35781bea8fc6a8c9099cad6695b747784fc1b8862a0f9e5b3bf5ab + md5: fef806a0f6de853670c746bbece01966 + depends: + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 259031 + timestamp: 1733975520465 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 + md5: d7d4680337a14001b0e043e96529409b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 236574 + timestamp: 1733975453350 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h0f0193d_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + sha256: 3f05d19f68ef800f33d44ea2a4211003124076516c8469abc7d432236344df53 + md5: 3a1421d12435df5b4c412cc4c8fac64d + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19740 + timestamp: 1733991625201 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h4e1184b_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b + md5: 3f4c1197462a6df2be6dc8241828fe93 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19086 + timestamp: 1733991637424 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: hc8a0bd2_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + sha256: 47b2813f652ce7e64ac442f771b2a5f7d4af4ad0d07ff51f6075ea80ed2e3f09 + md5: a8b6c17732d14ed49d0e9b59c43186bc + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 18068 + timestamp: 1733991869211 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h54f970a_11 + build_number: 11 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d + md5: ba41238f8e653998d7d2f42e3a8db054 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 47078 + timestamp: 1734024749727 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h7959bf6_11 + build_number: 11 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + sha256: 10d7240c7db0c941fb1a59c4f8ea6689a434b03309ee7b766fa15a809c553c02 + md5: 9b3fb60fe57925a92f399bc3fc42eccf + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 54003 + timestamp: 1734024480949 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: hcbd8f92_11 + build_number: 11 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + sha256: 79aa363c71c891a27496c0498f8d498b2ddc87b3ccb3b6c9da5b50b05936ebb8 + md5: e0772c59af4243a9b2565baa5d79e5b6 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 55207 + timestamp: 1734024546663 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h3df160d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + sha256: 3a1d2d332945306be9d49e569b95e4cc172d825f10e88715513a172f28ebb59e + md5: 28f00aa7fd9556c4c461328cf146c20b + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 190586 + timestamp: 1734008442362 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h96aa502_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + sha256: 22e4737c8a885995b7c1ae1d79c1f6e78d489e16ec079615980fdde067aeaf76 + md5: 495c93a4f08b17deb3c04894512330e6 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 152983 + timestamp: 1734008451473 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: hefd7a92_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 + md5: 5ce4df662d32d3123ea8da15571b6f51 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 197731 + timestamp: 1734008380764 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h1a307af_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + sha256: 71f5bf891299f831dceaea12f926c393bf754569e5305387a88b77e1f94612d8 + md5: da8ab0f3eeac93449ec3d531ede92caa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 161889 + timestamp: 1734433686109 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h831e299_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + sha256: 5920009b1c6f9a2bc131a36725251894e4b4773fce29c4b1065d4213ae337abe + md5: 80dd9f0ddf935290d1dc00ec75ff3023 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 157864 + timestamp: 1734433578570 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: haba67d1_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + sha256: c0a1a2b0750225ac3dc07fd258c88c2be866bf8ac67ba3d50bb4ecec852ff8ee + md5: 4c5ff4134e76426a75b8c548984fa933 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 135729 + timestamp: 1734433832730 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h11f4f37_12 + build_number: 12 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + sha256: 512d3969426152d9d5fd886e27b13706122dc3fa90eb08c37b0d51a33d7bb14a + md5: 96c3e0221fa2da97619ee82faa341a73 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 194672 + timestamp: 1734025626798 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h24f418c_12 + build_number: 12 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd + md5: c072045a6206f88015d02fcba1705ea1 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 134371 + timestamp: 1734025379525 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h5f50e26_12 + build_number: 12 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + sha256: ffeb9100cc8fd4093e1a6fdfd938bc4a396dd77480b7fb17aa42855a4d5e2c70 + md5: 031ca33115d4b1eeb43f435d6215778c + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 169516 + timestamp: 1734025167885 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h1be5864_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + sha256: 22966164d63808689fffd35945f57756c95337327e28099b5d77b29fc6a56ecc + md5: a37bba7acb62dd70492ee01eacca3b8f + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + license: Apache-2.0 + license_family: Apache + size: 97598 + timestamp: 1734146239038 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h2080895_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + sha256: 20bc2dd60e6518d9b8215c2b652ab5c52ee8a997d3b9a5f69e2dabd28cbf26b2 + md5: ae223efa63fbb4262a2d85c3ab3bc4f5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 117641 + timestamp: 1734146239779 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: hf454442_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + sha256: c2f205a7bf64c5f40eea373b3a0a7c363c9aa9246a13dd7f3d9c6a4434c4fe2d + md5: 947c82025693bebd557f782bb5d6b469 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 114156 + timestamp: 1734146123386 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + sha256: ede8e782467c87ac80ceb9c9af9e917d121b7d8b8c698186d18e3cecd36f2210 + md5: 53e798d720dd78b78847a7b2fdb05fc9 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 58621 + timestamp: 1733994421495 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + sha256: df586f42210af1134b1c88ff4c278c3cb6d6c807c84eac48860062464b28554d + md5: a5126a90e74ac739b00564a4c7ddcc36 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 56094 + timestamp: 1733994449690 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + sha256: de98343ce42d2e569b3380292d20f47bf39bda08aadabcbb8e650d3f38fd742f + md5: 22f72f8cd7ead211304ac17d337d96e0 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 49664 + timestamp: 1733994553014 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da + md5: 3bd35b0adab3d743f09e0252cc441d6b + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72154 + timestamp: 1733994384415 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + sha256: 1ed9a332d06ad595694907fad2d6d801082916c27cd5076096fda4061e6d24a8 + md5: 74e8c3e4df4ceae34aa2959df4b28101 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72762 + timestamp: 1733994347547 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + sha256: 215086d95e8ff1d3fcb0197ada116cc9d7db1fdae7573f5e810d20fa9215b47c + md5: e70e88a357a3749b67679c0788c5b08a + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 70186 + timestamp: 1733994496998 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h19a973c_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + sha256: 8269e6746eb3a5d15b732a3983888bf98dfc1f6594e95250fc8d16b43cfd5ff9 + md5: 95714136bef3e917bd5a2942d4682b20 + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 236249 + timestamp: 1734178020924 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h8a4e35f_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + sha256: 5ba9188e0cb4e3faff9bc96774febb040aa3b802aedba29d847e00e7b5eab84e + md5: d77a9e3d7ce15399903e92825fd651b5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 283154 + timestamp: 1734177845248 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: hd92328a_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + sha256: 094cd81f1e5ba713e9e7a272ee52b5dde3ccc4842ea90f19c0354a00bbdac3d9 + md5: 02b95564257d5c3db9c06beccf711f95 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 354703 + timestamp: 1734177883319 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: h849ce1a_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + sha256: 51b9e9df8cbab4a13a1b9d39d6ef5ed162aaa29c09a745810e00bbe92e1045c1 + md5: cda7747f4398be8d1fb37362815917a7 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2920625 + timestamp: 1734093552712 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: hc430e4a_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + sha256: 2dc09f6f9c49127b5f96e7535b64a9c521b944d76d8b7d03d48ae80257ac1cea + md5: aeefac461bea1f126653c1285cf5af08 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 3060561 + timestamp: 1734093737431 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: he0ff2e4_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + sha256: 535b970aaa13be45f8cab8205c59f044b17364111c41a227f061775a5c834e18 + md5: 0981ed87098b149bdb7d99a4a3fd0e58 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2826534 + timestamp: 1734094018287 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h1887c18_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 + md5: e0c3a906a41be769f0ae20ca3e31cfc0 + depends: + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 338650 + timestamp: 1728055589907 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h5cfcd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a + md5: 0a8838771cc2e985cd295e01ae83baf1 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 345117 + timestamp: 1728053909574 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: hd50102c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + sha256: f5b91329ed59ffc0be8747784c6e4cc7e56250c54032883a83bc11808ef6a87e + md5: f093a11dcf3cdcca010b20a818fcc6dc + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 294299 + timestamp: 1728054014060 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h113e628_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + sha256: 286b31616c191486626cb49e9ceb5920d29394b9e913c23adb7eb637629ba4de + md5: 73f73f60854f325a55f1d31459f2ab73 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 232351 + timestamp: 1728486729511 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h47b0b28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + sha256: 1c72423b9beba167d2f01b80dc204da77240a8266f1edb3d89510c852b300d69 + md5: 94e73a7877743a85c57091d8afab2348 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 217132 + timestamp: 1728488096615 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: hc602bab_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + sha256: bde446b916fff5150606f8ed3e6058ffc55a3aa72381e46f1ab346590b1ae40a + md5: d7b71593a937459f2d4b67e1a4727dc2 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 166907 + timestamp: 1728486882502 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h185ecfd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 + md5: 221e1e5ecb2643e113f32b3229d5ba33 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 502934 + timestamp: 1728580241002 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h3cf044e_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 + md5: 7eb66060455c7a47d9dcdbfa9f46579b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 549342 + timestamp: 1728578123088 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h7585a09_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + sha256: 08d52d130addc0fb55d5ba10d9fa483e39be25d69bac7f4c676c2c3069207590 + md5: 704238ef05d46144dae2e6b5853df8bc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 438636 + timestamp: 1728578216193 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h1b94036_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 + md5: 793b1080ab2d958980f137a8643cd6e8 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 140832 + timestamp: 1728565334900 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h736e048_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba + md5: 13de36be8de3ae3f05ba127631599213 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 149312 + timestamp: 1728563338704 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h9ca1f76_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + sha256: 77ab04e8fe5636a2de9c718f72a43645f7502cd208868c8a91ffba385547d585 + md5: 7a187cd7b1445afc80253bb186a607cc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 121278 + timestamp: 1728563418777 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: h37d6d07_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc + md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 260547 + timestamp: 1728730924071 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: ha633028_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 + md5: 7c1980f89dd41b097549782121a73490 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 287366 + timestamp: 1728729530295 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: hcdd55da_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + sha256: f48523f8aa0b5b80f45a92f0556b388dd96f44ac2dc2f44a01d08c1822eec97d + md5: c49fbc5233fcbaa86391162ff1adef38 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 196032 + timestamp: 1728729672889 +- kind: conda + name: backoff + version: 2.2.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 + md5: a38b801f2bcc12af80c2e02a9e4ce7d9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 18816 + timestamp: 1733771192649 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h2ec8cdc_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f + md5: b0b867af6fc74b2a0aa206da29c0f3cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + size: 349867 + timestamp: 1725267732089 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h6f74592_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + sha256: 9736bf660a0e4260c68f81d2635b51067f817813e6490ac9e8abd9a835dcbf6d + md5: e1e9727063057168d95f27a032acd0a4 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 h86ecc28_2 + license: MIT + license_family: MIT + size: 356878 + timestamp: 1725267878508 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312hde4cb15_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + sha256: 254b411fa78ccc226f42daf606772972466f93e9bc6895eabb4cfda22f5178af + md5: a83c2ef76ccb11bc2349f4f17696b15d + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 339360 + timestamp: 1725268143995 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h4bc722e_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 252783 + timestamp: 1720974456583 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h68df207_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + sha256: 2258b0b33e1cb3a9852d47557984abb6e7ea58e3d7f92706ec1f8e879290c4cb + md5: 56398c28220513b9ea13d7b450acfb20 + depends: + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 189884 + timestamp: 1720974504976 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h99b78c6_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 122909 + timestamp: 1720974522888 +- kind: conda + name: c-ares + version: 1.34.4 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f + md5: c1c999a38a4303b29d75c636eaa13cf9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 179496 + timestamp: 1734208291879 +- kind: conda + name: c-ares + version: 1.34.4 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + sha256: 1187a41d4bb2afe02cb18690682edc98d1e9f5e0ccda638d8704a75ea1875bbe + md5: 356da36f35d36dcba16e43f1589d4e39 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 215979 + timestamp: 1734208193181 +- kind: conda + name: c-ares + version: 1.34.4 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 + md5: e2775acf57efd5af15b8e3d1d74d72d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 206085 + timestamp: 1734208189009 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hbcca054_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + sha256: 1afd7274cbc9a334d6d0bc62fa760acc7afdaceb0b91a8df370ec01fd75dc7dd + md5: 720523eb0d6a9b0f6120c16b2aa4e7de + license: ISC + size: 157088 + timestamp: 1734208393264 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hcefe29a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + sha256: ad7b43211051332a5a4e788bb4619a2d0ecb5be73e0f76be17f733a87d7effd1 + md5: 83b4ad1e6dc14df5891f3fcfdeb44351 + license: ISC + size: 157096 + timestamp: 1734209301744 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hf0a4a13_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + sha256: 256be633fd0882ccc1a7a32bc278547e1703f85082c0789a87a603ee3ab8fb82 + md5: 7cb381a6783d91902638e4ed1ebd478e + license: ISC + size: 157091 + timestamp: 1734208344343 +- kind: conda + name: certifi + version: 2024.12.14 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + sha256: 048c16a9cbcb1fbad02083414d3bc7c1d0eea4b39aee6aa6bf8d1d5089ca8bad + md5: 6feb87357ecd66733be3279f16a8c400 + depends: + - python >=3.9 + license: ISC + size: 161642 + timestamp: 1734380604767 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h06ac9bb_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 + md5: a861504bbea4161a9170b85d4d2be840 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 294403 + timestamp: 1725560714366 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h0fad829_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f + md5: 19a5456f72f505881ba493979777b24e + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 281206 + timestamp: 1725560813378 +- kind: conda + name: cffi + version: 1.17.1 + build: py312hac81daf_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + sha256: 1162e3ca039e7ca7c0e78f0a020ed1bde968096841b663e3f393c966eb82f0f0 + md5: 1a256e5581b1099e9295cb84d53db3ea + depends: + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 312892 + timestamp: 1725561779888 +- kind: conda + name: charset-normalizer + version: 3.4.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + sha256: 63022ee2c6a157a9f980250a66f54bdcdf5abee817348d0f9a74c2441a6fbf0e + md5: 6581a17bba6b948bb60130026404a9d6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 47533 + timestamp: 1733218182393 +- kind: conda + name: click + version: 8.1.7 + build: unix_pyh707e725_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + sha256: 1cd5fc6ccdd5141378e51252a7a3810b07fd5a7e6934a5b4a7eccba66566224b + md5: cb8e52f28f5e592598190c562e7b5bf1 + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 84513 + timestamp: 1733221925078 +- kind: conda + name: colorama + version: 0.4.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- kind: conda + name: datasets + version: 2.14.4 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + sha256: 7e09bd083a609138b780fcc4535924cb96814d2c908a36d4c64a2ba9ee3efe7f + md5: 3e087f072ce03c43a9b60522f5d0ca2f + depends: + - aiohttp + - dill >=0.3.0,<0.3.8 + - fsspec >=2021.11.1 + - huggingface_hub >=0.14.0,<1.0.0 + - importlib-metadata + - multiprocess + - numpy >=1.17 + - packaging + - pandas + - pyarrow >=8.0.0 + - python >=3.8.0 + - python-xxhash + - pyyaml >=5.1 + - requests >=2.19.0 + - tqdm >=4.62.1 + license: Apache-2.0 + license_family: Apache + size: 347303 + timestamp: 1691593908658 +- kind: conda + name: deprecated + version: 1.2.15 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + sha256: a20ebf2c9b02a6eb32412ceb5c4cffaae49417db7e75414a76417538293a9402 + md5: eaef2e94d5bd76f758545d172c1fda67 + depends: + - python >=3.9 + - wrapt <2,>=1.10 + license: MIT + license_family: MIT + size: 14297 + timestamp: 1733662697343 +- kind: conda + name: dill + version: 0.3.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + sha256: 4ff20c6be028be2825235631c45d9e4a75bca1de65f8840c02dfb28ea0137c45 + md5: 5e4f3466526c52bc9af2d2353a1460bd + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + size: 87553 + timestamp: 1690101185422 +- kind: conda + name: dnspython + version: 2.7.0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 + md5: 5fbd60d61d21b4bd2f9d7a48fe100418 + depends: + - python >=3.9,<4.0.0 + - sniffio + constrains: + - aioquic >=1.0.0 + - wmi >=1.5.1 + - httpx >=0.26.0 + - trio >=0.23 + - cryptography >=43 + - httpcore >=1.0.0 + - idna >=3.7 + - h2 >=4.1.0 + license: ISC + license_family: OTHER + size: 172172 + timestamp: 1733256829961 +- kind: conda + name: email-validator + version: 2.2.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + sha256: b91a19eb78edfc2dbb36de9a67f74ee2416f1b5273dd7327abe53f2dbf864736 + md5: da16dd3b0b71339060cd44cb7110ddf9 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.9 + license: Unlicense + size: 44401 + timestamp: 1733300827551 +- kind: conda + name: email_validator + version: 2.2.0 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + sha256: e0d0fdf587aa0ed0ff08b2bce3ab355f46687b87b0775bfba01cc80a859ee6a2 + md5: 0794f8807ff2c6f020422cacb1bd7bfa + depends: + - email-validator >=2.2.0,<2.2.1.0a0 + license: Unlicense + size: 6552 + timestamp: 1733300828176 +- kind: conda + name: exceptiongroup + version: 1.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 + md5: a16662747cdeb9abbac74d0057cc976e + depends: + - python >=3.9 + license: MIT and PSF-2.0 + size: 20486 + timestamp: 1733208916977 +- kind: conda + name: fastapi + version: 0.115.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + sha256: d7826d537c667093c9de96411a09585a8d620c84a830a0195e58e9a0df45f018 + md5: 1b1e0c97830cdf75f1f371bd467ab657 + depends: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.5 + - httpx >=0.23.0 + - jinja2 >=2.11.2 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python >=3.9 + - python-multipart >=0.0.7 + - starlette >=0.40.0,<0.42.0 + - typing_extensions >=4.8.0 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + size: 73084 + timestamp: 1733362427885 +- kind: conda + name: fastapi-cli + version: 0.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 + md5: d960e0ea9e1c561aa928f6c4439f04c7 + depends: + - python >=3.9 + - rich-toolkit >=0.11.1 + - typer >=0.12.3 + - uvicorn-standard >=0.15.0 + license: MIT + license_family: MIT + size: 15546 + timestamp: 1734302408607 +- kind: conda + name: filelock + version: 3.16.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + sha256: 18dca6e2194732df7ebf824abaefe999e4765ebe8e8a061269406ab88fc418b9 + md5: d692e9ba6f92dc51484bf3477e36ce7c + depends: + - python >=3.9 + license: Unlicense + size: 17441 + timestamp: 1733240909987 +- kind: conda + name: freetype + version: 2.12.1 + build: h267a509_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + sha256: b2e3c449ec9d907dd4656cb0dc93e140f447175b125a3824b31368b06c666bb6 + md5: 9ae35c3d96db2c94ce0cef86efdfa2cb + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 634972 + timestamp: 1694615932610 +- kind: conda + name: freetype + version: 2.12.1 + build: hadb7bae_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 + md5: e6085e516a3e304ce41a8ee08b9b89ad + depends: + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 596430 + timestamp: 1694616332835 +- kind: conda + name: freetype + version: 2.12.1 + build: hf0a5ef3_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + sha256: 7af93030f4407f076dce181062360efac2cd54dce863b5d7765287a6f5382537 + md5: a5ab74c5bd158c3d5532b66d8d83d907 + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 642092 + timestamp: 1694617858496 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + sha256: 44d6d6b332421e621c029fb149f12dba1ccb5ed6ac632e2e807a9d92d6cb2864 + md5: 7960352935cc95ac23883c9b8c97f2ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 53366 + timestamp: 1729699762631 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + sha256: 7e0c12983b20f2816b3712729b5a35ecb7ee152132ca7cf805427c62395ea823 + md5: f98e36c96b2c66d9043187179ddb04f4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 60968 + timestamp: 1729699568442 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + sha256: b0a9ff3e71452eed70877b2f3175d41cd85070da6deac381c5f3f61e1f19bccb + md5: 62fc11b0738ca15e0dd19b60cf280d12 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 59967 + timestamp: 1729699642726 +- kind: conda + name: fsspec + version: 2024.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + sha256: 790a50b4f94042951518f911a914a886a837c926094c6a14ed1d9d03ce336807 + md5: 906fe13095e734cb413b57a49116cdc8 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 134726 + timestamp: 1733493445080 +- kind: conda + name: gflags + version: 2.2.2 + build: h5888daf_1005 + build_number: 1005 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 119654 + timestamp: 1726600001928 +- kind: conda + name: gflags + version: 2.2.2 + build: h5ad3122_1005 + build_number: 1005 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + sha256: 28fe6b40b20454106d5e4ef6947cf298c13cc72a46347bbc49b563cd3a463bfa + md5: 4ff634d515abbf664774b5e1168a9744 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 106638 + timestamp: 1726599967617 +- kind: conda + name: gflags + version: 2.2.2 + build: hf9b8971_1005 + build_number: 1005 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + size: 82090 + timestamp: 1726600145480 +- kind: conda + name: glog + version: 0.7.1 + build: h468a4a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 + md5: 08940a32c6ced3703d1412dd37df4f62 + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 145811 + timestamp: 1718284208668 +- kind: conda + name: glog + version: 0.7.1 + build: hbabe93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 143452 + timestamp: 1718284177264 +- kind: conda + name: glog + version: 0.7.1 + build: heb240a5_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + size: 112215 + timestamp: 1718284365403 +- kind: conda + name: googleapis-common-protos + version: 1.66.0 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + sha256: d8d19575a827f2c62500949b9536efdd6b5406c9f546a73b6a87ac90b03a5875 + md5: 4861e30ff0cd566ea6fb4593e3b7c22a + depends: + - protobuf >=3.20.2,<6.0.0.dev0,!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 116522 + timestamp: 1731459019854 +- kind: conda + name: h11 + version: 0.14.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 + md5: 7ee49e89531c0dcbba9466f6d115d585 + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 51846 + timestamp: 1733327599467 +- kind: conda + name: h2 + version: 4.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + sha256: 843ddad410c370672a8250470697027618f104153612439076d4d7b91eeb7b5c + md5: 825927dc7b0f287ef8d4d0011bb113b1 + depends: + - hpack >=4.0,<5 + - hyperframe >=6.0,<7 + - python >=3.9 + license: MIT + license_family: MIT + size: 52000 + timestamp: 1733298867359 +- kind: conda + name: hpack + version: 4.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + sha256: ec89b7e5b8aa2f0219f666084446e1fb7b54545861e9caa892acb24d125761b5 + md5: 2aa5ff7fa34a81b9196532c84c10d865 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 29412 + timestamp: 1733299296857 +- kind: conda + name: httpcore + version: 1.0.7 + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df + md5: 2ca8e6dbc86525c8b95e3c0ffa26442e + depends: + - python >=3.8 + - h11 >=0.13,<0.15 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=3.0,<5.0 + - certifi + license: BSD-3-Clause + license_family: BSD + size: 48959 + timestamp: 1731707562362 +- kind: conda + name: httptools + version: 0.6.4 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + sha256: 621e7e050b888e5239d33e37ea72d6419f8367e5babcad38b755586f20264796 + md5: 8b1160b32557290b64d5be68db3d996d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101872 + timestamp: 1732707756745 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + sha256: 0bd1f30224af142711d11033a7469ae402a1147143f399f7341bbc1d8178c722 + md5: 5e70a6de59352f9a52e9caa7f3447390 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101255 + timestamp: 1732707891645 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + sha256: 5e93cda79e32e8c0039e05ea1939e688da336187dab025f699b42ef529e848be + md5: e1747a8e8d2aca5499aaea9993bf31ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 85623 + timestamp: 1732707871414 +- kind: conda + name: httpx + version: 0.28.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- kind: conda + name: huggingface_hub + version: 0.26.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + sha256: 0c75532d914a04c73222be298ed2c6868739dd475b1b1a9137c52abe79873952 + md5: 73937038e21117fe401f8ea64fbaeacc + depends: + - filelock + - fsspec >=2023.5.0 + - packaging >=20.9 + - python >=3.9 + - pyyaml >=5.1 + - requests + - tqdm >=4.42.1 + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + size: 275466 + timestamp: 1733852454004 +- kind: conda + name: hyperframe + version: 6.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + sha256: e91c6ef09d076e1d9a02819cd00fa7ee18ecf30cdd667605c853980216584d1b + md5: 566e75c90c1d0c8c459eb0ad9833dc7a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17239 + timestamp: 1733298862681 +- kind: conda + name: icu + version: '75.1' + build: hf9b3779_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 + md5: 268203e8b983fddb6412b36f2024e75c + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12282786 + timestamp: 1720853454991 +- kind: conda + name: icu + version: '75.1' + build: hfee45f7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 11857802 + timestamp: 1720853997952 +- kind: conda + name: idna + version: '3.10' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 + md5: 39a4f67be3286c86d696df570b1201b7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 49765 + timestamp: 1733211921194 +- kind: conda + name: importlib-metadata + version: 8.5.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 + md5: 315607a3030ad5d5227e76e0733798ff + depends: + - python >=3.9 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + size: 28623 + timestamp: 1733223207185 +- kind: conda + name: jinja2 + version: 3.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + sha256: 85a7169c078b8065bd9d121b0e7b99c8b88c42a411314b6ae5fcd81c48c4710a + md5: 08cce3151bde4ecad7885bd9fb647532 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110963 + timestamp: 1733217424408 +- kind: conda + name: jupyter_client + version: 8.6.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a + md5: 4ebae00eae9705b0c3d6d1018a81d047 + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106342 + timestamp: 1733441040958 +- kind: conda + name: jupyter_core + version: 5.7.2 + build: pyh31011fe_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd + md5: 0a2980dada0dd7fd0998f0342308b1b1 + depends: + - __unix + - platformdirs >=2.5 + - python >=3.8 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 57671 + timestamp: 1727163547058 +- kind: conda + name: keyutils + version: 1.6.1 + build: h166bdaf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + sha256: 150c05a6e538610ca7c43beb3a40d65c90537497a4f6a5f4d15ec0451b6f5ebb + md5: 30186d27e2c9fa62b45fb1476b7200e3 + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 117831 + timestamp: 1646151697040 +- kind: conda + name: keyutils + version: 1.6.1 + build: h4e544f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + sha256: 6d4233d97a9b38acbb26e1268bcf8c10a8e79c2aed7e5a385ec3769967e3e65b + md5: 1f24853e59c68892452ef94ddd8afd4b + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 112327 + timestamp: 1646166857935 +- kind: conda + name: krb5 + version: 1.21.3 + build: h237132a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- kind: conda + name: krb5 + version: 1.21.3 + build: h50a48e9_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 + md5: 29c10432a2ca1472b53f299ffb2ffa37 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1474620 + timestamp: 1719463205834 +- kind: conda + name: krb5 + version: 1.21.3 + build: h659f571_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1370023 + timestamp: 1719463201255 +- kind: conda + name: lcms2 + version: '2.16' + build: h922389a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + sha256: be4847b1014d3cbbc524a53bdbf66182f86125775020563e11d914c8468dd97d + md5: ffdd8267a04c515e7ce69c727b051414 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 296219 + timestamp: 1701647961116 +- kind: conda + name: lcms2 + version: '2.16' + build: ha0e7c42_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + sha256: 151e0c84feb7e0747fabcc85006b8973b22f5abbc3af76a9add0b0ef0320ebe4 + md5: 66f6c134e76fe13cce8a9ea5814b5dd5 + depends: + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 211959 + timestamp: 1701647962657 +- kind: conda + name: lcms2 + version: '2.16' + build: hb7c19ff_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 + md5: 51bb7010fc86f70eee639b4bb7a894f5 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 245247 + timestamp: 1701647787198 +- kind: conda + name: ld_impl_linux-64 + version: '2.43' + build: h712a8e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + sha256: 7c91cea91b13f4314d125d1bedb9d03a29ebbd5080ccdea70260363424646dbe + md5: 048b02e3962f066da18efe3a21b77672 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 669211 + timestamp: 1729655358674 +- kind: conda + name: ld_impl_linux-aarch64 + version: '2.43' + build: h80caac9_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + sha256: 80ec7e8f006196808fac5bd4b3773a652847f97bbf08044cd87731424ac64f8b + md5: fcbde5ea19d55468953bf588770c0501 + constrains: + - binutils_impl_linux-aarch64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 698245 + timestamp: 1729655345825 +- kind: conda + name: lerc + version: 4.0.0 + build: h27087fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + sha256: cb55f36dcd898203927133280ae1dc643368af041a48bcf7c026acb7c47b0c12 + md5: 76bbff344f0134279f225174e9064c8f + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 281798 + timestamp: 1657977462600 +- kind: conda + name: lerc + version: 4.0.0 + build: h4de3ea5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + sha256: 2d09ef9b7796d83364957e420b41c32d94e628c3f0520b61c332518a7b5cd586 + md5: 1a0ffc65e03ce81559dbcb0695ad1476 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 262096 + timestamp: 1657978241894 +- kind: conda + name: lerc + version: 4.0.0 + build: h9a09cb3_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + sha256: 6f068bb53dfb6147d3147d981bb851bb5477e769407ad4e6a68edf482fdcb958 + md5: de462d5aacda3b30721b512c5da4e742 + depends: + - libcxx >=13.0.1 + license: Apache-2.0 + license_family: Apache + size: 215721 + timestamp: 1657977558796 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + sha256: 8f91429091183c26950f1e7ffa730e8632f0627ba35d2fccd71df31628c9b4e5 + md5: e1f604644fe8d78e22660e2fec6756bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1310521 + timestamp: 1727295454064 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + sha256: 590e47dce38031a8893e70491f3b71e214de7781cab53b6f017aa6f6841cb076 + md5: 6fe6b3694c4792a8e26755d3b06f0b80 + depends: + - libgcc >=13 + - libstdcxx >=13 + constrains: + - abseil-cpp =20240722.0 + - libabseil-static =20240722.0=cxx17* + license: Apache-2.0 + license_family: Apache + size: 1328502 + timestamp: 1727295490806 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_hf9b8971_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + sha256: 90bf08a75506dfcf28a70977da8ab050bcf594cd02abd3a9d84a22c9e8161724 + md5: 706da5e791c569a7b9814877098a6a0a + depends: + - __osx >=11.0 + - libcxx >=17 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1179072 + timestamp: 1727295571173 +- kind: conda + name: libarrow + version: 18.1.0 + build: h1b535d6_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + sha256: 087b579aebf351ca41c54214121d86a15a41c92051cbd432d6f3a3f58a8c31b0 + md5: 4c0ad68efba1113ac5833975c67b565d + depends: + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8040629 + timestamp: 1733810319239 +- kind: conda + name: libarrow + version: 18.1.0 + build: h44a453e_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + sha256: abf17e99b03356a9d6248e965826c1352ff01b00d3a62cc51393bb0744d72803 + md5: 2cf6d608d6e66506f69797d5c6944c35 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8786061 + timestamp: 1733810643966 +- kind: conda + name: libarrow + version: 18.1.0 + build: h4a2f8bd_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + sha256: 9ed3ea1bc15005c0df187268ef91407afaa908cf82f36f5acbbf50ac24d7f806 + md5: 835cdd84195b84dc34d128bd5d3580b9 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libcxx >=18 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 5494797 + timestamp: 1733808145854 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + sha256: fdb70e2499e59b730084ecd53008b361a6f6090b5fb49624feda06b7e84c7b8c + md5: c50907eefe2ae22d826e7cb2e4d712f5 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 578091 + timestamp: 1733810378092 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + sha256: a32fa1d71415afc02b5cf3cd4c0a6ec0af9e749308829cc65ff79689222ce479 + md5: 143f9288b64759a6427563f058c62f2b + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 611745 + timestamp: 1733810698469 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + sha256: e1cae46409927470439ef9ae93ed09b3493d0579501ca9ebfa79ded212ee98d8 + md5: 97fc01254714e1572624baefdd7cc898 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + license: Apache-2.0 + license_family: APACHE + size: 483713 + timestamp: 1733808246880 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + sha256: 2a08f5a1017ff660c37ae0c24343a119cb2511c6edd69e23d0a5090a0967ea35 + md5: bb1548ad011c4f9107fcc4cc548473bf + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libparquet 18.1.0 hfc78867_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 559673 + timestamp: 1733810461646 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + sha256: 74eeb178070002842d3ed721769399320e3a68a0843319eaf899a092a31def26 + md5: 20ca46a6bc714a6ab189d5b3f46e66d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libparquet 18.1.0 h081d1f1_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 586627 + timestamp: 1733810842604 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + sha256: 6eba942ce926419f74e6e0a7c3994a7d78ab6be47115e6bb70e02136554736be + md5: 0774276be6659aaa0007f1b0f6ee19b0 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libparquet 18.1.0 h636d7b7_6_cpu + license: Apache-2.0 + license_family: APACHE + size: 489948 + timestamp: 1733809328231 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ee7192_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + sha256: bda6728db019dd0c409b1996ad9ef6ab0bcee3a94dc66a8045e8c1049c566055 + md5: aa313b3168caf98d00b3753f5ba27650 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libarrow-dataset 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 519989 + timestamp: 1733810903274 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ffb4b1_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + sha256: 9f78c55c5d7122e588a6f226cbf7e909c479d66ed18edc633d68324323d386b9 + md5: 5db2e6832397b8ca70a6f7b00e0c3629 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libarrow-dataset 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 515928 + timestamp: 1733810503359 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h86344ea_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + sha256: bafd9ca59ebb5ad34b77aff316ef7b59c5fb1eb8a7b6a15de8dcbdf3ce37556d + md5: c1c162f5bf569cff8bed6def705a899f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libarrow-dataset 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 451623 + timestamp: 1733809487176 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + sha256: 30bd658682b124243f8e52d8edf8a19e7be1bc31e4fe4baec30a64002dc8cd0c + md5: ac52800af2e0c0e7dac770b435ce768a + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16393 + timestamp: 1734432564346 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: df6d8ee34d45cf35609ecdd55c1ff03e32e0cd87ae41ebe4ef3747a8e09ead4d + md5: 8d900b7079a00969d70305e9aad550b7 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16477 + timestamp: 1734432576699 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + sha256: 597f9c3779caa979c8c6abbb3ba8c7191b84e1a910d6b0d10e5faf35284c450c + md5: 21be102c9ae80a67ba7de23b129aa7f6 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16714 + timestamp: 1734433054681 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 + md5: 3ee026955c688f551a9999840cff4c67 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 68982 + timestamp: 1725267774142 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 + md5: 41b599ed2b02abcfdd84302bff174b23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 68851 + timestamp: 1725267660471 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + sha256: 839dacb741bdbb25e58f42088a2001b649f4f12195aeb700b5ddfca3267749e5 + md5: d0bf1dff146b799b319ea0434b93f779 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 68426 + timestamp: 1725267943211 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c + md5: e64d0f3b59c7c4047446b97a8624a72d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 31708 + timestamp: 1725267783442 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 32696 + timestamp: 1725267669305 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + sha256: 6c6862eb274f21a7c0b60e5345467a12e6dda8b9af4438c66d496a2c1a538264 + md5: 55e66e68ce55523a6811633dd1ac74e2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 28378 + timestamp: 1725267980316 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c + md5: 0e9bd365480c72b25c71a448257b537d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 290230 + timestamp: 1725267792697 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 + md5: 06f70867945ea6a84d35836af780f1de + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 281750 + timestamp: 1725267679782 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + sha256: eeb1eb0d58b9d02bc1b98dc0a058f104ab168eb2f7d1c7bfa0570a12cfcdb7b7 + md5: 4f3a434504c67b2c42565c0b85c1885c + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 279644 + timestamp: 1725268003553 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + sha256: 9c74e536c9bc868e356ffd43f81c2cb398aec84b40fcadc312315b164a5500ee + md5: ebcc5f37a435aa3c19640533c82f8d76 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16336 + timestamp: 1734432570482 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: 521e78be0c4170f229c43e1a6c94337a72db3ebcbe6e5960f8413aa438dcb8f9 + md5: d77f943ae4083f3aeddca698f2d28262 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16398 + timestamp: 1734432580937 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + sha256: 27a29ef6b2fd2179bc3a0bb9db351f078ba140ca10485dca147c399639f84c93 + md5: a0e9980fe12d42f6d0c0ec009f67e948 + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16628 + timestamp: 1734433061517 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea + md5: 268ee639c17ada0002fb04dd21816cc2 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 18669 + timestamp: 1633683724891 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 20440 + timestamp: 1633683576494 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: hbdafb3b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + size: 18765 + timestamp: 1633683992603 +- kind: conda + name: libcurl + version: 8.11.1 + build: h332b0f4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + sha256: 3cd4075b2a7b5562e46c8ec626f6f9ca57aeecaa94ff7df57eca26daa94c9906 + md5: 2b3e0081006dc21e8bf53a91c83a055c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 423011 + timestamp: 1733999897624 +- kind: conda + name: libcurl + version: 8.11.1 + build: h6702fde_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + sha256: 9fc65d21a58f4aad1bc39dfb94a178893aeb035850c5cf0ed9736674279f390b + md5: 7dec1cd271c403d1636bda5aa388a55d + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 440737 + timestamp: 1733999835504 +- kind: conda + name: libcurl + version: 8.11.1 + build: h73640d1_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + sha256: f47c35938144c23278987c7d12096f6a42d7c850ffc277222b032073412383b6 + md5: 46d7524cabfdd199bffe63f8f19a552b + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 385098 + timestamp: 1734000160270 +- kind: conda + name: libcxx + version: 19.1.5 + build: ha82da77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + sha256: 7918cc0bb7a6554cdd3eee634c3dc414a1ab8ec49faeca1567367bb92118f9d7 + md5: 3c7be0df28ccda1d193ea6de56dcb5ff + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 519819 + timestamp: 1733291654212 +- kind: conda + name: libdeflate + version: '1.23' + build: h4ddbbb0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + sha256: 511d801626d02f4247a04fff957cc6e9ec4cc7e8622bd9acd076bcdc5de5fe66 + md5: 8dfae1d2e74767e9ce36d5fa0d8605db + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + size: 72255 + timestamp: 1734373823254 +- kind: conda + name: libdeflate + version: '1.23' + build: h5e3c512_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + sha256: 959419d87cd2b789a9055db95704c614f31aeb70bef7949fa2f734122a3a2863 + md5: 7e7ca2607b11b180120cefc2354fc0cb + depends: + - libgcc >=13 + license: MIT + size: 69862 + timestamp: 1734373858306 +- kind: conda + name: libdeflate + version: '1.23' + build: hec38601_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + sha256: 887c02deaed6d583459eba6367023e36d8761085b2f7126e389424f57155da53 + md5: 1d8b9588be14e71df38c525767a1ac30 + depends: + - __osx >=11.0 + license: MIT + size: 54132 + timestamp: 1734373971372 +- kind: conda + name: libedit + version: 3.1.20191231 + build: hc8eb9b7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca + md5: 30e4362988a2623e9eb34337b83e01f9 + depends: + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96607 + timestamp: 1597616630749 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: a57d37c236d8f7c886e01656f4949d9dcca131d2a0728609c6f7fa338b65f1cf + md5: 4d331e44109e3f0e19b4cb8f9b82f3e1 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 123878 + timestamp: 1597616541093 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: debc31fb2f07ba2b0363f90e455873670734082822926ba4a9556431ec0bf36d + md5: 29371161d77933a54fccf1bb66b96529 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 134104 + timestamp: 1597617110769 +- kind: conda + name: libev + version: '4.33' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + sha256: 973af77e297f1955dd1f69c2cbdc5ab9dfc88388a5576cd152cda178af0fd006 + md5: a9a13cb143bbaa477b1ebaefbe47a302 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 115123 + timestamp: 1702146237623 +- kind: conda + name: libev + version: '4.33' + build: h93a5062_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + size: 107458 + timestamp: 1702146414478 +- kind: conda + name: libev + version: '4.33' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 112766 + timestamp: 1702146165126 +- kind: conda + name: libevent + version: 2.1.12 + build: h2757513_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 368167 + timestamp: 1685726248899 +- kind: conda + name: libevent + version: 2.1.12 + build: h4ba1bb4_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + sha256: 01333cc7d6e6985dd5700b43660d90e9e58049182017fd24862088ecbe1458e4 + md5: 96ae6083cd1ac9f6bc81631ac835b317 + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 438992 + timestamp: 1685726046519 +- kind: conda + name: libevent + version: 2.1.12 + build: hf998b51_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 427426 + timestamp: 1685725977222 +- kind: conda + name: libexpat + version: 2.6.4 + build: h286801f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 + md5: 38d2656dd914feb0cab8c629370768bf + depends: + - __osx >=11.0 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 64693 + timestamp: 1730967175868 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 + md5: db833e03127376d461e1e13e76f09b6c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 73304 + timestamp: 1730967041968 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + sha256: f42e758009ba9db90d1fe7992bc3e60d0c52f71fb20923375d2c44ae69a5a2b3 + md5: f1b3fab36861b3ce945a13f0dfdfc688 + depends: + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 72345 + timestamp: 1730967203789 +- kind: conda + name: libffi + version: 3.4.2 + build: h3422bc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 + license: MIT + license_family: MIT + size: 39020 + timestamp: 1636488587153 +- kind: conda + name: libffi + version: 3.4.2 + build: h3557bc0_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + sha256: 7e9258a102480757fe3faeb225a3ca04dffd10fecd2a958c65cdb4cdf75f2c3c + md5: dddd85f4d52121fab0a8b099c5e06501 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 59450 + timestamp: 1636488255090 +- kind: conda + name: libffi + version: 3.4.2 + build: h7f98852_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 58292 + timestamp: 1636488182923 +- kind: conda + name: libgcc + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + sha256: 53eb8a79365e58849e7b1a068d31f4f9e718dc938d6f2c03e960345739a03569 + md5: 3cb76c3f10d3bc7f1105b2fc9db984df + depends: + - _libgcc_mutex 0.1 conda_forge + - _openmp_mutex >=4.5 + constrains: + - libgomp 14.2.0 h77fa898_1 + - libgcc-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 848745 + timestamp: 1729027721139 +- kind: conda + name: libgcc + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + sha256: 5d56757ccad208c79214395b00d006d8d18929a4ba49c47bd9460789a7620943 + md5: 511b511c5445e324066c3377481bcab8 + depends: + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==14.2.0=*_1 + - libgomp 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 535243 + timestamp: 1729089435134 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + sha256: 3a76969c80e9af8b6e7a55090088bc41da4cffcde9e2c71b17f44d37b7cb87f7 + md5: e39480b9ca41323497b05492a63bc35b + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54142 + timestamp: 1729027726517 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + sha256: 9b5cf168a6c7361cae869cb74b716766ee7c6d6b3f6172b32ba9bf91135efdc4 + md5: 0694c249c61469f2c0f7e2990782af21 + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54104 + timestamp: 1729089444587 +- kind: conda + name: libgfortran + version: 5.0.0 + build: 13_2_0_hd922786_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf + depends: + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- kind: conda + name: libgfortran + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + sha256: fc9e7f22a17faf74da904ebfc4d88699013d2992e55505e4aa0eb01770290977 + md5: f1fd30127802683586f768875127a987 + depends: + - libgfortran5 14.2.0 hd5240d6_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 53997 + timestamp: 1729027752995 +- kind: conda + name: libgfortran + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + sha256: cb66e411fa32a5c6040f4e5e2a63c00897aae4c3133a9c004c2e929ccf19575b + md5: 0294b92d2f47a240bebb1e3336b495f1 + depends: + - libgfortran5 14.2.0 hb6113d0_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729089471124 +- kind: conda + name: libgfortran5 + version: 13.2.0 + build: hf226fd6_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 5.0.0 13_2_0_*_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 997381 + timestamp: 1707330687590 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hb6113d0_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + sha256: a87ff46d19916403cbf68cf1d785bf56b4d1ab7b2552468d2ea775d70782493f + md5: fc068e11b10e18f184e027782baa12b6 + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1102158 + timestamp: 1729089452640 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hd5240d6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d + md5: 9822b874ea29af082e5d36098d25427d + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1462645 + timestamp: 1729027735353 +- kind: conda + name: libgomp + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + sha256: 1911c29975ec99b6b906904040c855772ccb265a1c79d5d75c8ceec4ed89cd63 + md5: cc3573974587f12dda90d96e3e55a702 + depends: + - _libgcc_mutex 0.1 conda_forge + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 460992 + timestamp: 1729027639220 +- kind: conda + name: libgomp + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + sha256: 5aa53874a5e57a00f2e0c2e2910684eb674429cd5fcb803619b226a73e89aedf + md5: 376f0e73abbda6d23c0cb749adc195ef + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 463521 + timestamp: 1729089357313 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h3888205_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + sha256: 36af2844ce8fafd477214d51117746144461132f76759a7d29963b4583b577be + md5: a40b948bf4eabcc1ce708c40ffd7c06d + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1248560 + timestamp: 1733512309504 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h804f50b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + sha256: 126856add750013390dff664a3c3cd0f6f0cbbc683b0025a7ce9d1618968bc70 + md5: 3d96df4d6b1c88455e05b94ce8a14a53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1249557 + timestamp: 1733512191906 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h8d8be31_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + sha256: 722e49dbdc4486105d9f5b79a7ba4f9064602fe20c4015e97684c898ab8d3386 + md5: d7ab9e0eb7d55eac4943913073de61d7 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 876210 + timestamp: 1733512539476 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h0121fbd_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + sha256: d1b53d17df38b52a4bc6d1fe6af0e611d6480ce10b0af570c84bd38c8aa83b91 + md5: 877a5ec0431a5af83bf0cd0522bfe661 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h804f50b_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 782108 + timestamp: 1733512329104 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h7081f7f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + sha256: 609df2cf376ba66460f40143f835fc567cae4458df80705587cd2efd59c09bf1 + md5: 28f5ab5cf95170dfacd05d2bb301e573 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=18 + - libgoogle-cloud 2.32.0 h8d8be31_0 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 526895 + timestamp: 1733513644846 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: hb9b2b65_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + sha256: e120e7b6c9c9d25baa8ae903106babdd3c969523ae25278a615ed9de4bd0fc35 + md5: 925ab0ca33baca4fcfee585cecb94169 + depends: + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h3888205_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 737964 + timestamp: 1733512457785 +- kind: conda + name: libgrpc + version: 1.67.1 + build: h36c5df4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + sha256: 1f6673d9d866048c9cf28fd56e6874ffc7e2c53c47d7071cb367d5fc2dde16a7 + md5: b946137e362e98a55a77fdf0b20a7739 + depends: + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7131846 + timestamp: 1730236305327 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc2c308b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + sha256: 870550c1faf524e9a695262cd4c31441b18ad542f16893bd3c5dbc93106705f7 + md5: 4606a4647bfe857e3cfe21ca12ac3afb + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7362336 + timestamp: 1730236333879 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc70892a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + sha256: d2393fcd3c3584e5d58da4122f48bcf297567d2f6f14b3d1fcbd34fdd5040694 + md5: 624e27571fde34f8acc2afec840ac435 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 4882208 + timestamp: 1730236299095 +- kind: conda + name: libiconv + version: '1.17' + build: h0d3ecfb_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 + md5: 69bda57310071cf6d2b86caf11573d2d + license: LGPL-2.1-only + size: 676469 + timestamp: 1702682458114 +- kind: conda + name: libiconv + version: '1.17' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + sha256: a30e09d089cb75a0d5b8e5c354694c1317da98261185ed65aa3793e741060614 + md5: 9a8eb13f14de7d761555a98712e6df65 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705787 + timestamp: 1702684557134 +- kind: conda + name: libiconv + version: '1.17' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 + md5: d66573916ffcf376178462f1b61c941e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705775 + timestamp: 1702682170569 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + sha256: 675bc1f2a8581cd34a86c412663ec29c5f90c1d9f8d11866aa1ade5cdbdf8429 + md5: ed24e702928be089d9ba3f05618515c6 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 647126 + timestamp: 1694475003570 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hb547adb_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + sha256: a42054eaa38e84fc1e5ab443facac4bbc9d1b6b6f23f54b7bf4f1eb687e1d993 + md5: 3ff1e053dc3a2b8e36b9bfa4256a58d1 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 547541 + timestamp: 1694475104253 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f + md5: ea25936bb4080d843790b586850f82b8 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 618575 + timestamp: 1694474974816 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + sha256: b76458c36331376911e0f98fa68109e02f4d5e5ebfffa79587ac69cef748bba1 + md5: 3792604c43695d6a273bc5faaac47d48 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16338 + timestamp: 1734432576650 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + sha256: a42bd01498efe2ccf6d08d56ac3cbd3ceab79e06699ff5aac3da8e45a66738f7 + md5: a5d4e18876393633da62fd8492c00156 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16403 + timestamp: 1734432585123 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + sha256: dd6d9a21e672aee4332f019c8229ce70cf5eaf6c2f4cbd1443b105fb66c00dc5 + md5: cebad79038a75cfd28fa90d147a2d34d + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16624 + timestamp: 1734433068120 +- kind: conda + name: liblzma + version: 5.6.3 + build: h39f12f2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 + md5: b2553114a7f5e20ccd02378a77d836aa + depends: + - __osx >=11.0 + license: 0BSD + size: 99129 + timestamp: 1733407496073 +- kind: conda + name: liblzma + version: 5.6.3 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + sha256: d1cce0b7d62d1e54e2164d3e0667ee808efc6c3870256e5b47a150cd0bf46824 + md5: eb08b903681f9f2432c320e8ed626723 + depends: + - libgcc >=13 + license: 0BSD + size: 124138 + timestamp: 1733409137214 +- kind: conda + name: liblzma + version: 5.6.3 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 + md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: 0BSD + size: 111132 + timestamp: 1733407410083 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h161d5f1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 + md5: 19e57602824042dfd0446292ef90488b + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 647599 + timestamp: 1729571887612 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h6d7220d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 566719 + timestamp: 1729572385640 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: hc8609a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + sha256: c093c6d370aadbf0409c20b6c54c488ee2f6fea976181919fcc63e87ee232673 + md5: f52c614fa214a8bedece9421c771670d + depends: + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 714610 + timestamp: 1729571912479 +- kind: conda + name: libnsl + version: 2.0.1 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 + md5: c14f32510f694e3185704d89967ec422 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 34501 + timestamp: 1697358973269 +- kind: conda + name: libnsl + version: 2.0.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 33408 + timestamp: 1697359010159 +- kind: conda + name: libopenblas + version: 0.3.28 + build: openmp_hf332438_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 + md5: 40803a48d947c8639da6704e9a44d3ce + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=13.2.0 + - llvm-openmp >=18.1.8 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4165774 + timestamp: 1730772154295 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h94d23a6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + sha256: 99ba271d8a80a1af2723f2e124ffd91d850074c0389c067e6d96d72a2dbfeabe + md5: 62857b389e42b36b686331bec0922050 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 5578513 + timestamp: 1730772671118 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h9d3fd7e_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + sha256: 30623a40764e935aa77e0d4db54c1a1589189a9bf3a03fdb445505c1e319b5a6 + md5: e8dde93dd199da3c1f2c1fcfd0042cd4 + depends: + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4793435 + timestamp: 1730773029647 +- kind: conda + name: libparquet + version: 18.1.0 + build: h081d1f1_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + sha256: c691a59f1ebb6cedbf827f49f6cf414e08b0eec911f589133e6a8321e8ac701c + md5: 68788df49ce7480187eb6387f15b2b67 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1204535 + timestamp: 1733810811118 +- kind: conda + name: libparquet + version: 18.1.0 + build: h636d7b7_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + sha256: 88c1e810bede65c54f1ebc51c14400f9e8cf0fc1f88a8c0a99210e2f5dfed582 + md5: 9b333c3a38e55f6c1b8733222e22f528 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 873134 + timestamp: 1733809271282 +- kind: conda + name: libparquet + version: 18.1.0 + build: hfc78867_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + sha256: 38aab34c422519c530d0e9a3e0ffd1624db1c1e163983c46ae341e831b2eb6b5 + md5: 1ab6d4a9a982920b9dc5f2c700777b27 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1117592 + timestamp: 1733810440129 +- kind: conda + name: libpng + version: 1.6.44 + build: hadc24fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + sha256: e5b14f7a01c2db4362d8591f42f82f336ed48d5e4079e4d1f65d0c2a3637ea78 + md5: f4cc49d7aa68316213e4b12be35308d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 290661 + timestamp: 1726234747153 +- kind: conda + name: libpng + version: 1.6.44 + build: hc14010f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + sha256: 38f8759a3eb8060deabd4db41f0f023514d853e46ddcbd0ba21768fc4e563bb1 + md5: fb36e93f0ea6a6f5d2b99984f34b049e + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 263385 + timestamp: 1726234714421 +- kind: conda + name: libpng + version: 1.6.44 + build: hc4a20ef_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + sha256: 23b5ce15cf9c6017641a8396bab00ae807dd9f662718cfa7f61de114d0c97647 + md5: 5d25802b25fcc7419fa13e21affaeb3a + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 294907 + timestamp: 1726236639270 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h029595c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + sha256: d8c7b6f851bfc53494d9b8e54d473c4f11ab26483a6e64df6f7967563df166b1 + md5: 538dbe0ad9f248e2e109abb9b6809ea5 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2802876 + timestamp: 1728564881988 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h5b01275_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + sha256: 5e8fd4aa00193c85602ce6101dd28fe31306dff85c9725048f6dc828dfa7c421 + md5: ab0bff36363bec94720275a681af8b83 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2945348 + timestamp: 1728565355702 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h8f0b736_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + sha256: f732a6fa918428e2d5ba61e78fe11bb44a002cc8f6bb74c94ee5b1297fefcfd8 + md5: d2cb5991f2fb8eb079c80084435e9ce6 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2374965 + timestamp: 1728565334796 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h18dbdb1_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + sha256: 96d4fdac28d5af38c38f90c22cb0aa9a90affae13ca8ba24bd1eb60b789df8ff + md5: f1800796b0efc4bbc5b001d845545111 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 203516 + timestamp: 1728778974654 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h2348fd5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + sha256: 6facca42cfc85a05b33e484a8b0df7857cc092db34806946d022270098d8d20f + md5: 5a7065309a66097738be6a06fd04b7ef + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 165956 + timestamp: 1728779107218 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: hbbce691_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + sha256: f8ad6a4f6d4fd54ebe3e5e712a01e663222fc57f49d16b6b8b10c30990dafb8f + md5: 2124de47357b7a516c0a3efd8f88c143 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 211096 + timestamp: 1728778964655 +- kind: conda + name: libsodium + version: 1.0.20 + build: h4ab18f5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + size: 205978 + timestamp: 1716828628198 +- kind: conda + name: libsodium + version: 1.0.20 + build: h68df207_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + sha256: 448df5ea3c5cf1af785aad46858d7a5be0522f4234a4dc9bb764f4d11ff3b981 + md5: 2e4a8f23bebdcb85ca8e5a0fbe75666a + depends: + - libgcc-ng >=12 + license: ISC + size: 177394 + timestamp: 1716828514515 +- kind: conda + name: libsodium + version: 1.0.20 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h3f77e49_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + sha256: f192f3c8973de9ec4c214990715f13b781965247a5cedf9162e7f9e699cfc3c4 + md5: 122d6f29470f1a991e85608e77e56a8a + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 850553 + timestamp: 1733762057506 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h5eb1b54_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + sha256: 885a27fa84a5a73ed9779168c02b6c386e2fc7a53f0566b32a09ceca146b42b4 + md5: d4bf59f8783a4a66c0aec568f6de3ff4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 1042182 + timestamp: 1733761913736 +- kind: conda + name: libsqlite + version: 3.47.2 + build: hee588c1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + sha256: 48af21ebc2cbf358976f1e0f4a0ab9e91dfc83d0ef337cf3837c6f5bc22fb352 + md5: b58da17db24b6e08bcbf8fed2fb8c915 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 873551 + timestamp: 1733761824646 +- kind: conda + name: libssh2 + version: 1.11.1 + build: h9cc3647_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 + md5: ddc7194676c285513706e5fc64f214d7 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279028 + timestamp: 1732349599461 +- kind: conda + name: libssh2 + version: 1.11.1 + build: ha41c0db_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + sha256: 40f2af5357457546bd11cd64a3b9043d83865180f65ce602515c35f353be35c7 + md5: aeffe03c0e598f015aab08dbb04f6ee4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 311577 + timestamp: 1732349396421 +- kind: conda + name: libssh2 + version: 1.11.1 + build: hf672d98_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 + md5: be2de152d8073ef1c01b7728475f2fe7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 304278 + timestamp: 1732349402869 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: h3f4de04_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 + md5: 37f489acd39e22b623d2d1e5ac6d195c + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3816794 + timestamp: 1729089463404 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: hc0a3c3a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + sha256: 4661af0eb9bdcbb5fb33e5d0023b001ad4be828fccdcc56500059d56f9869462 + md5: 234a5554c53625688d51062645337328 + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3893695 + timestamp: 1729027746910 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: h4852527_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + sha256: 25bb30b827d4f6d6f0522cc0579e431695503822f144043b93c50237017fffd8 + md5: 8371ac6457591af2cf6159439c1fd051 + depends: + - libstdcxx 14.2.0 hc0a3c3a_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729027780628 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: hf1166c9_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + sha256: 9f97461bd55a2745a7a0941f3502a047f15bfe7bb2952dc7fb204b3202f866fd + md5: 0e75771b8a03afae5a2c6ce71bc733f5 + depends: + - libstdcxx 14.2.0 h3f4de04_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54133 + timestamp: 1729089498541 +- kind: conda + name: libthrift + version: 0.21.0 + build: h0e7cc3e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + sha256: ebb395232973c18745b86c9a399a4725b2c39293c9a91b8e59251be013db42f0 + md5: dcb95c0a98ba9ff737f7ae482aef7833 + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 425773 + timestamp: 1727205853307 +- kind: conda + name: libthrift + version: 0.21.0 + build: h154c74f_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + sha256: f04ab1417aca1687edff9c30d8423ace285eb8c053dc16d595c6e47cfeefb274 + md5: c28792bf37f4ecdce8e3cb9e40750650 + depends: + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 417329 + timestamp: 1727205944238 +- kind: conda + name: libthrift + version: 0.21.0 + build: h64651cc_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + sha256: 7a6c7d5f58cbbc2ccd6493b4b821639fdb0701b9b04c737a949e8cb6adf1c9ad + md5: 7ce2bd2f650f8c31ad7ba4c7bfea61b7 + depends: + - __osx >=11.0 + - libcxx >=17 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 324342 + timestamp: 1727206096912 +- kind: conda + name: libtiff + version: 4.7.0 + build: h551f018_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae + md5: a5d084a957563e614ec0c0196d890654 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=18 + - libdeflate >=1.23,<1.24.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 370600 + timestamp: 1734398863052 +- kind: conda + name: libtiff + version: 4.7.0 + build: h88f7998_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + sha256: 5888bd66ba7606ae8596856c7dac800940ecad0aed77d6aa37db69d434c81cf0 + md5: 36a0ea4a173338c8725dc0807e99cf22 + depends: + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 464699 + timestamp: 1734398752249 +- kind: conda + name: libtiff + version: 4.7.0 + build: hd9ff511_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 428173 + timestamp: 1734398813264 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h5505292_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + sha256: ea88f06e97ef8fa2490f7594f8885bb542577226edf8abba3144302d951a53c2 + md5: f777470d31c78cd0abe1903a2fda436f + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 83000 + timestamp: 1732868631531 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + sha256: 37a1833c55f9945724cd4b3eb6a1469032cc754a1dd725f191c34154ad2ba7e4 + md5: 699f155da290be3a1a64c932c6728991 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 81526 + timestamp: 1732868466862 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + sha256: 9794e6388e780c3310d46f773bbc924d4053375c3fcdb07a704b57f4616db928 + md5: 1e936bd23d737aac62a18e9a1e7f8b18 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 81500 + timestamp: 1732868419835 +- kind: conda + name: libuuid + version: 2.38.1 + build: h0b41bf4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 33601 + timestamp: 1680112270483 +- kind: conda + name: libuuid + version: 2.38.1 + build: hb4cce97_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + sha256: 616277b0c5f7616c2cdf36f6c316ea3f9aa5bb35f2d4476a349ab58b9b91675f + md5: 000e30b09db0b7c775b21695dff30969 + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 35720 + timestamp: 1680113474501 +- kind: conda + name: libuv + version: 1.49.2 + build: h7ab814d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + sha256: 0e5176af1e788ad5006cf261c4ea5a288a935fda48993b0240ddd2e562dc3d02 + md5: 4bc348e3a1a74d20a3f9beb866d75e0a + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 410500 + timestamp: 1729322654121 +- kind: conda + name: libuv + version: 1.49.2 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + sha256: adf4eca89339ac7780f2394e7e6699be81259eb91f79f9d9fdf2c1bc6b26f210 + md5: 1899e1ec2be63386c41c4db31d3056af + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 627484 + timestamp: 1729322575379 +- kind: conda + name: libuv + version: 1.49.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + sha256: a35cd81cd1a9add11024097da83cc06b0aae83186fe4124b77710876f37d8f31 + md5: 070e3c9ddab77e38799d5c30b109c633 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 884647 + timestamp: 1729322566955 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + sha256: 10dded60f274e29c573cfacf6e96f5d0fc374ee431250374a44cbd773916ab9d + md5: 5fd7ab3e5f382c70607fbac6335e6e19 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 363577 + timestamp: 1713201785160 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h93a5062_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + sha256: 0d4bad713a512d79bfeb4d61821f447afab8b0792aca823f505ce6b195e9fde5 + md5: c0af0edfebe780b19940e94871f1a765 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 287750 + timestamp: 1713200194013 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + sha256: 49bc5f6b1e11cb2babf2a2a731d1a680a5e08a858280876a779dbda06c78c35f + md5: b26e8aa824079e1be0294e7152ca4559 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 438953 + timestamp: 1713199854503 +- kind: conda + name: libxcb + version: 1.17.0 + build: h262b8f6_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b + md5: cd14ee5cca2464a425b1dbfc24d90db2 + depends: + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 397493 + timestamp: 1727280745441 +- kind: conda + name: libxcb + version: 1.17.0 + build: h8a09558_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 395888 + timestamp: 1727278577118 +- kind: conda + name: libxcb + version: 1.17.0 + build: hdb1d25a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 323658 + timestamp: 1727278733917 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f + md5: b4df5d7d4b63579d081fd3a4cf99740e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 114269 + timestamp: 1702724369203 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 100393 + timestamp: 1702724383534 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h0d44e9d_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + sha256: 306e18aa647d8208ad2cd0e62d84933222b2fbe93d2d53cd5283d2256b1d54de + md5: f5b05674697ae7d2c5932766695945e1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - icu <0.0a0 + license: MIT + license_family: MIT + size: 689993 + timestamp: 1733443678322 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h178c5d8_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 + md5: 3dc3cff0eca1640a6acbbfab2f78139e + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 582898 + timestamp: 1733443841584 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h2e0c361_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 + md5: 63410f85031930cde371dfe0ee89109a + depends: + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 732155 + timestamp: 1733443825814 +- kind: conda + name: libzlib + version: 1.3.1 + build: h8359307_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 46438 + timestamp: 1727963202283 +- kind: conda + name: libzlib + version: 1.3.1 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 + md5: 08aad7cbe9f5a6b460d0976076b6ae64 + depends: + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 66657 + timestamp: 1727963199518 +- kind: conda + name: libzlib + version: 1.3.1 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 60963 + timestamp: 1727963148474 +- kind: conda + name: llvm-openmp + version: 19.1.5 + build: hdb05f8b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + sha256: e7ba0d8b718925efdcf1309f5e776e3264cc172d3af8d4048b39627c50a1abc0 + md5: f2c2e187a1d2637d282e34dc92021a70 + depends: + - __osx >=11.0 + constrains: + - openmp 19.1.5|19.1.5.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 281120 + timestamp: 1733376089600 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h286801f_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 167055 + timestamp: 1733741040117 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + sha256: 67e55058d275beea76c1882399640c37b5be8be4eb39354c94b610928e9a0573 + md5: 6654e411da94011e8fbe004eacb8fe11 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 184953 + timestamp: 1733740984533 +- kind: conda + name: markdown-it-py + version: 3.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a + md5: fee3164ac23dfca50cfcc8b85ddefb81 + depends: + - mdurl >=0.1,<1 + - python >=3.9 + license: MIT + license_family: MIT + size: 64430 + timestamp: 1733250550053 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h178313f_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + sha256: 4a6bf68d2a2b669fecc9a4a009abd1cf8e72c2289522ff00d81b5a6e51ae78f5 + md5: eb227c3e0bf58f5bd69c0532b157975b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24604 + timestamp: 1733219911494 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h74ce7d3_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + sha256: 1d500158262f30b9c23e37d1c861fe76e127a3926d69b3b38c25d20d3faa6f9f + md5: bc8607ab678073a0441808a31465f4fb + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 25079 + timestamp: 1733220639175 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h998013c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + sha256: 4aa997b244014d3707eeef54ab0ee497d12c0d0d184018960cce096169758283 + md5: 46e547061080fddf9cf95a0327e8aba6 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24048 + timestamp: 1733219945697 +- kind: conda + name: max + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + sha256: 0e3c1984ac7476550fd8fa5921bf1ca58950219b84ae21ecd861f650e45382ac + md5: e04b1405f630c9bb7d4cb5559840e902 + depends: + - max-core ==24.6.0 release + - max-python >=24.6.0,<25.0a0 + - mojo-jupyter ==24.6.0 release + - mblack ==24.6.0 release + license: LicenseRef-Modular-Proprietary + size: 9851 + timestamp: 1734039439696 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + sha256: 38a4128c15b230f5b05e0606a339c7866a83eb943d334a948b3a8c1d2675a917 + md5: 25e678ff7c59e36ec3154fe0cd15ebde + depends: + - mblack ==24.6.0 release + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 247670119 + timestamp: 1734039439695 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + sha256: 554f2c1a6ddfab8fabf82b9fbcc1adb87e2a615669793e463f757ea452e02016 + md5: 2ca790aa461fa28722c6f21fcd2af0b9 + depends: + - mblack ==24.6.0 release + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 251486610 + timestamp: 1734039413769 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + sha256: 434c29e35067e296db55525cd5cf38bb013a1f7a7bfa99845bf6c317de6cdc12 + md5: 4a2ead0a9010c36b6193ea32f583e996 + depends: + - mblack ==24.6.0 release + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 212001240 + timestamp: 1734039726703 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + sha256: 6fbf7330ad910e6ec9fd581fd0f8505e5b1326ccf9979d553c70c61abf4c3e54 + md5: 218ecd662f853ea1578404799d61b385 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 123785050 + timestamp: 1734039439704 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + sha256: 15f57cb436b00c510473ca1940edd9ee03df6c92d5b66d616db30a131b768b78 + md5: 97c62f17f0d34787d29128043a593877 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 127403269 + timestamp: 1734039413779 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + sha256: c888b58cfc7c767d40aa100ff2bccf5c3ab11d58d897a6accb749e6b5b7014ea + md5: 62a92bfab3b5c85c2d246672bbb8bc8d + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 112484803 + timestamp: 1734039726707 +- kind: conda + name: mblack + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + sha256: f135164020478078f4681aa77e7f6ca9f68b8e7ee02604b85342bbaf2f706f0d + md5: 77367aff981ba391ab5c047ba33ec978 + depends: + - python >=3.9,<3.13 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - python + license: MIT + size: 130668 + timestamp: 1734039439700 +- kind: conda + name: mdurl + version: 0.1.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- kind: conda + name: mojo-jupyter + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + sha256: 2fe043d98ea77f8f165b39bd252cd04942216c8533f0291c49d87d6cfd8673df + md5: b17127f3ca2cef0976496407e1cd4081 + depends: + - max-core ==24.6.0 release + - python >=3.9,<3.13 + - jupyter_client >=8.6.2,<8.7 + - python + license: LicenseRef-Modular-Proprietary + size: 22990 + timestamp: 1734039439702 +- kind: conda + name: multidict + version: 6.1.0 + build: py312h178313f_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + sha256: b05bc8252a6e957bf4a776ed5e0e61d1ba88cdc46ccb55890c72cc58b10371f4 + md5: 5b5e3267d915a107eca793d52e1b780a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 61507 + timestamp: 1733913288935 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hcc812fe_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + sha256: ff9f767ba4df68e9ac2a380529a83a2fb6abd985beee9eab16608f7e2c3ccc6e + md5: dcf3ae213cf0ab40ebcc10452e1ed9fa + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 63077 + timestamp: 1733913233032 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hdb8e49c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + sha256: 482fd09fb798090dc8cce2285fa69f43b1459099122eac2fb112d9b922b9f916 + md5: 0048335516fed938e4dd2c457b4c5b9b + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 55968 + timestamp: 1729065664275 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h02f2b3b_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + sha256: 8041371e3ec3fbc2ca13c71b0180672896e6382e62892d9f6b11a4c5dd675951 + md5: 910ef2223c71902175418d9163152788 + depends: + - dill >=0.3.6 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335147 + timestamp: 1695459275360 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h98912ed_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + sha256: bb612a921fafda6375a2204ffebd8811db8dd3b8f25ac9886cc9bcbff7e3664e + md5: 5a64b9f44790d9a187a85366dd0ffa8d + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335666 + timestamp: 1695459025249 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312hdd3e373_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + sha256: c53362cdf346f314e111faddc53061e3fd2ece0ba68ca303f5dd109976df158f + md5: 173a1692d2b3ddc265dc6afd21a869b3 + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 336110 + timestamp: 1695459137796 +- kind: conda + name: mypy_extensions + version: 1.0.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + sha256: 1895f47b7d68581a6facde5cb13ab8c2764c2e53a76bd746f8f98910dc4e08fe + md5: 29097e7ea634a45cc5386b95cac6568f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 10854 + timestamp: 1733230986902 +- kind: conda + name: ncurses + version: '6.5' + build: h7bae524_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc + md5: cb2b0ea909b97b3d70cd3921d1445e1a + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 802321 + timestamp: 1724658775723 +- kind: conda + name: ncurses + version: '6.5' + build: hcccb83c_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + sha256: acad4cf1f57b12ee1e42995e6fac646fa06aa026529f05eb8c07eb0a84a47a84 + md5: 91d49c85cacd92caa40cf375ef72a25d + depends: + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 924472 + timestamp: 1724658573518 +- kind: conda + name: ncurses + version: '6.5' + build: he02047a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a + md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 889086 + timestamp: 1724658547447 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h470d778_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + sha256: 23767677a7790bee5457d5e75ebd508b9a31c5354216f4310dd1acfca3f7a6f9 + md5: 9cebf5a06cb87d4569cd68df887af476 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6614296 + timestamp: 1707225994762 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h8442bc7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6073136 + timestamp: 1707226249608 +- kind: conda + name: numpy + version: 1.26.4 + build: py312heda63a1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 + md5: d8285bea2a350f63fab23bf460221f3f + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 7484186 + timestamp: 1707225809722 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h3f56577_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d + md5: 04231368e4af50d11184b50e14250993 + depends: + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 377796 + timestamp: 1733816683252 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h5fbd93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 + md5: 9e5816bc95d285c115a3ebc2f8563564 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 342988 + timestamp: 1733816638720 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h8a3d83b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + sha256: 1d59bc72ca7faac06d349c1a280f5cfb8a57ee5896f1e24225a997189d7418c7 + md5: 4b71d78648dbcf68ce8bf22bb07ff838 + depends: + - __osx >=11.0 + - libcxx >=18 + - libpng >=1.6.44,<1.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 319362 + timestamp: 1733816781741 +- kind: conda + name: openssl + version: 3.4.0 + build: h39f12f2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + sha256: bd1d58ced46e75efa3b842c61642fd12272c69e9fe4d7261078bc082153a1d53 + md5: df307bbc703324722df0293c9ca2e418 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2935176 + timestamp: 1731377561525 +- kind: conda + name: openssl + version: 3.4.0 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + sha256: 64dbbdd6384fa56338124783197f7ad9048c989a02264bcd2e07355e3570f113 + md5: b2f202b5bddafac824eb610b65dde98f + depends: + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 3474825 + timestamp: 1731379200886 +- kind: conda + name: openssl + version: 3.4.0 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + sha256: 814b9dff1847b132c676ee6cc1a8cb2d427320779b93e1b6d76552275c128705 + md5: 23cc74f77eb99315c0360ec3533147a9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 2947466 + timestamp: 1731377666602 +- kind: conda + name: opentelemetry-api + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + sha256: 296280c8ace35c0a1cf72bed1077f248b3af903c3bf92332f1783a207cb5abdb + md5: 307b05402c1a382f2f09426492dee8f8 + depends: + - deprecated >=1.2.6 + - importlib-metadata >=6.0,<=8.5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 44166 + timestamp: 1734132973331 +- kind: conda + name: opentelemetry-exporter-otlp-proto-common + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + sha256: ae9776efe52564e0d6711cfcee7c54439273e57a3999f7f796f66e862f58aae9 + md5: 0c02e74d26bce3fec93b227cf7ea6e6b + depends: + - backoff >=1.10.0,<3.0.0 + - opentelemetry-proto 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 18922 + timestamp: 1734310457116 +- kind: conda + name: opentelemetry-exporter-otlp-proto-http + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + sha256: 5d61db9d5b4f91b3932f5f2348920d5b7fdaa09e52c8ea054cf7bf3f21677c9c + md5: 223f4e56a29601c887f0dc467034af5b + depends: + - deprecated >=1.2.6 + - googleapis-common-protos >=1.52,<2.dev0 + - opentelemetry-api >=1.15,<2.dev0 + - opentelemetry-exporter-otlp-proto-common 1.29.0 + - opentelemetry-proto 1.29.0 + - opentelemetry-sdk 1.29.0 + - python >=3.9 + - requests >=2.7,<3.dev0 + license: Apache-2.0 + size: 17147 + timestamp: 1734345675510 +- kind: conda + name: opentelemetry-exporter-prometheus + version: 1.12.0rc1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + sha256: b8239230dbbdb491401e41b53bd9f21d60551cedef1a8d5807fca1bf9bdd331c + md5: 1ddc95052b31147d1e10d818cf519cf5 + depends: + - opentelemetry-api >=1.10.0 + - opentelemetry-sdk >=1.10.0 + - prometheus_client >=0.5.0,<1.0.0 + - python >=3.6 + license: Apache-2.0 + license_family: APACHE + size: 14721 + timestamp: 1695214221489 +- kind: conda + name: opentelemetry-proto + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + sha256: 200a7cb8acc8a0ddd6ef55c5460cec871b6a265929b240a0296c0ccb9c8d9758 + md5: e2a6d2ad10b813c7fdc1c64aac376128 + depends: + - protobuf <6.0,>=5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 37235 + timestamp: 1734291034372 +- kind: conda + name: opentelemetry-sdk + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + sha256: 7b36629d8b8be8a019fcfd1518d7b7f862dd25de96f8adcadb93e4fd12cf9bd6 + md5: 2a8893f06e6ebda4bfa78875bc923ea4 + depends: + - opentelemetry-api 1.29.0 + - opentelemetry-semantic-conventions 0.50b0 + - python >=3.9 + - typing-extensions >=3.7.4 + - typing_extensions >=3.7.4 + license: Apache-2.0 + license_family: APACHE + size: 77645 + timestamp: 1734297838999 +- kind: conda + name: opentelemetry-semantic-conventions + version: 0.50b0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + sha256: 6526e70368d5bf66ef0eaa51fb800d53782dde71a24bd38f40139919a6f784dc + md5: f7111fa4188d646c8108e232d024cb99 + depends: + - deprecated >=1.2.6 + - opentelemetry-api 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 86084 + timestamp: 1734208980168 +- kind: conda + name: orc + version: 2.0.3 + build: h3c55218_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + sha256: 154b26bc4d586de33765a155c9b79ebd7f5bb36c2bbf4b8854e1631bca8d21af + md5: 0a51a3cf028b845c46ec0d1ea2d18629 + depends: + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1165179 + timestamp: 1733509923825 +- kind: conda + name: orc + version: 2.0.3 + build: h97ab989_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + sha256: 9de7e2746fde57c9b7f08ee87142014f6bb9b2d3a506839ea3e98baa99711576 + md5: 2f46eae652623114e112df13fae311cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1189462 + timestamp: 1733509801323 +- kind: conda + name: orc + version: 2.0.3 + build: hbcee414_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + sha256: e5e72438a3cd967ebc774070e8c49500d2d6d4175f349400b327fee75d3bfc05 + md5: e808cf7819eaa1735c8790d7f9f482c7 + depends: + - __osx >=11.0 + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 437391 + timestamp: 1733510118673 +- kind: conda + name: packaging + version: '24.2' + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 + md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + size: 60164 + timestamp: 1733203368787 +- kind: conda + name: pandas + version: 2.2.3 + build: py312ha2895bd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + sha256: 585e05f95d14afe3df43ded14f86800c70da26b27e27b59de95932f8888af5d3 + md5: 80b873ac4fdf36641afa0eaafff3a664 + depends: + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15159625 + timestamp: 1726879151211 +- kind: conda + name: pandas + version: 2.2.3 + build: py312hcd31e36_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + sha256: ff0cb54b5d058c7987b4a0984066e893642d1865a7bb695294b6172e2fcdc457 + md5: c68bfa69e6086c381c74e16fd72613a8 + depends: + - __osx >=11.0 + - libcxx >=17 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 14470437 + timestamp: 1726878887799 +- kind: conda + name: pandas + version: 2.2.3 + build: py312hf9745cd_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + sha256: ad275a83bfebfa8a8fee9b0569aaf6f513ada6a246b2f5d5b85903d8ca61887e + md5: 8bce4f6caaf8c5448c7ac86d87e26b4b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15436913 + timestamp: 1726879054912 +- kind: conda + name: pathspec + version: 0.12.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee + md5: 617f15191456cc6a13db418a275435e5 + depends: + - python >=3.9 + license: MPL-2.0 + license_family: MOZILLA + size: 41075 + timestamp: 1733233471940 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h5ab5af3_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + sha256: 3cf43a5eb1f67f3a5f3ef1ec3a685f8767019cce24dbe46c4b76fee8a54fbacf + md5: 1c4bdfe659cfdedd372685ce2494e97b + depends: + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41756471 + timestamp: 1729068045876 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h7b63e92_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + sha256: 13a464bea02c0df0199c20ef6bad24a6bc336aaf55bf8d6a133d0fe664463224 + md5: 385f46a4df6f97892503a841121a9acf + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41948418 + timestamp: 1729065846594 +- kind: conda + name: pillow + version: 11.0.0 + build: py312haf37ca6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + sha256: 727b4c3faecdb6f6809cf20c5f32d2df4af34e0d5b9146b7588383bcba7990e8 + md5: dc9b51fbd2b6f7fea9b5123458864dbb + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41737424 + timestamp: 1729065920347 +- kind: conda + name: platformdirs + version: 4.3.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + sha256: bb50f6499e8bc1d1a26f17716c97984671121608dc0c3ecd34858112bce59a27 + md5: 577852c7e53901ddccc7e6a9959ddebe + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 20448 + timestamp: 1733232756001 +- kind: conda + name: prometheus_client + version: 0.21.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + sha256: bc8f00d5155deb7b47702cb8370f233935704100dbc23e30747c161d1b6cf3ab + md5: 3e01e386307acc60b2f89af0b2e161aa + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 49002 + timestamp: 1733327434163 +- kind: conda + name: propcache + version: 0.2.1 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + sha256: 5771311fb5ded614ca349c92579a0b752af55a310f40b71fc533e20625965391 + md5: 55d5742a696d7da1c1262e99b6217ceb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52747 + timestamp: 1733391916349 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + sha256: c7f62c11ed929ccf1f3d4a1e200e28be01e8d0e0786bf8f76c5893f2ea681e1b + md5: 50ab8953e7ff1333a4a47cda32e68123 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52484 + timestamp: 1733391993461 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + sha256: f8c266c494aa1e4cfb8bf0b6fca060044b2f3d65afe4c5062ebeea382e77aa6d + md5: c84e3dd97fe25a17322c4a0f670c6750 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 48225 + timestamp: 1733392308901 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h2ec8cdc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + sha256: 4884f8161602f0148ebbc1af8d3176cec80b96c83243f68aafd651986b573817 + md5: 586bead4a9dfa46faf88deb7d3a742bb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 464548 + timestamp: 1728669645013 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h6f74592_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + sha256: f874ffd38b9ae2b810e9d2e43fd8d3b778cdeaf7dea4a3e6ee4adeafe2d936cf + md5: 4b9b22bd7c53d938b207f9d0f79db183 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 472764 + timestamp: 1728669483611 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312hf02c72a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + sha256: dbcec117510ced5c12097e3eb06ebbf4512dc255733a9ace33c4249fb7e6a364 + md5: 6fda46c82abd0a080ca33de7d16ca877 + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 447369 + timestamp: 1728669902591 +- kind: conda + name: pthread-stubs + version: '0.4' + build: h86ecc28_1002 + build_number: 1002 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba + md5: bb5a90c93e3bac3d5690acf76b4a6386 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 8342 + timestamp: 1726803319942 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hb9d3cd8_1002 + build_number: 1002 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 8252 + timestamp: 1726802366959 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hd74edd7_1002 + build_number: 1002 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 8381 + timestamp: 1726802424786 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h1f38498_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + sha256: 06c0e208d5bf15051874097366c8e8e5db176dffba38526f227a34e80cc8e9bc + md5: 3710616b880b31d0c8afd8ae7e12392a + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25375 + timestamp: 1732610892198 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h7900ff3_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + sha256: 46a61c29375d3bf1933eae61c7861394c168898915d59fc99bf05e46de2ff5ad + md5: ac65b70df28687c6af4270923c020bdd + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25213 + timestamp: 1732610785600 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h8025657_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + sha256: 49db959887cb89b44053a44a98d0f35644fc0b2003587492f02b56046de0b60a + md5: 9bb7d32e96a5dcb5ea7fd90a11a83656 + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25374 + timestamp: 1732611006864 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h01725c0_0_cpu + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + sha256: 948a4161c56f846d374a3721a657e58ddbc992a29b3b3e7a6411975c30361d94 + md5: ee80934a6c280ff8635f8db5dec11e04 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4612916 + timestamp: 1732610377259 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h66f7834_0_cpu + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + sha256: e7eb062145be554c23dfefa0ebe8c5f6ae8c59635117a6921e66403d6addcda3 + md5: 3390c8b8f57e85506c92a37cf750bdd7 + depends: + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4406662 + timestamp: 1732610939832 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312hc40f475_0_cpu + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + sha256: 063eb168a29d4ce6d9ed865e9e1ad3b6e141712189955a79e06b24ddc0cbbc9c + md5: 9859e7c4b94bbf69772dbf0511101cec + depends: + - __osx >=11.0 + - libarrow 18.1.0.* *cpu + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 3909116 + timestamp: 1732610863261 +- kind: conda + name: pycparser + version: '2.22' + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- kind: conda + name: pydantic + version: 2.10.3 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + sha256: cac9eebd3d5f8d8a497a9025d756257ddc75b8b3393e6737cb45077bd744d4f8 + md5: 194ef7f91286978521350f171b117f01 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.27.1 + - python >=3.9 + - typing-extensions >=4.6.1 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + size: 317037 + timestamp: 1733316963547 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + sha256: c89741f4eff395f8de70975f42e1f20591f0e0870929d440af35b13399976b09 + md5: 114030cb28527db2c385f07038e914c8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1635156 + timestamp: 1732254225040 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + sha256: 1f59bc1914f77faed3c95217e4d093310771baee4e93a15c0479359559e3fa19 + md5: d980860b8bf193f53d30a19c5d2bf070 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1503747 + timestamp: 1732254331303 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + sha256: 5bba8de2bbbbdb39390abb1e2aff310e8cfd49646ae5a0e0ea4d6582bd1d52ba + md5: 3847a96eaf24a877b6091150ff9c4955 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1449057 + timestamp: 1732254359451 +- kind: conda + name: pydantic-settings + version: 2.7.0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + sha256: dd1ac7c8b6a189c8aa18f6c7df019d8f6df495300a259e3fbebdb542fc955c3b + md5: d9f19a7c4199249fa229891b573b6f9b + depends: + - pydantic >=2.7.0 + - python >=3.9 + - python-dotenv >=0.21.0 + license: MIT + license_family: MIT + size: 31426 + timestamp: 1734127929720 +- kind: conda + name: pygments + version: 2.18.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + sha256: 0d6133545f268b2b89c2617c196fc791f365b538d4057ecd636d658c3b1e885d + md5: b38dc0206e2a530e5c2cf11dc086b31a + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 876700 + timestamp: 1733221731178 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + sha256: 6879d52fb0ec2258e2850476786a652c394220d53883c53691ed5390183ae925 + md5: f0e4a98d54477083ddc9d2f33507f848 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 181512 + timestamp: 1728714205508 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + sha256: 8a006507a4003fb01eeee2f9ba79f994478694766ea3b445273da5c11cf8e763 + md5: 798f42d9bfdf125dc80ffbec0e96e0b6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 182021 + timestamp: 1728714164706 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + sha256: 7967b94b8f0ff75847302444e9c43ac11a391d74da24cb14fba1049fac9e5ba9 + md5: 5274663cb05dfbe316db50af6da4389f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 183141 + timestamp: 1728714267954 +- kind: conda + name: pysocks + version: 1.7.1 + build: pyha55dd90_7 + build_number: 7 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 +- kind: conda + name: python + version: 3.12.8 + build: h1683364_1_cpython + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + sha256: 85573582d5b0f79923fed0a8365d3d74d21eee9f0a5fa1b9345f191e006363ab + md5: 09ec612ea05370989eaa3d81abf0f369 + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 13760816 + timestamp: 1733407890896 +- kind: conda + name: python + version: 3.12.8 + build: h9e4cc4f_1_cpython + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + sha256: 3f0e0518c992d8ccfe62b189125721309836fe48a010dc424240583e157f9ff0 + md5: 7fd2fd79436d9b473812f14e86746844 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 31565686 + timestamp: 1733410597922 +- kind: conda + name: python + version: 3.12.8 + build: hc22306f_1_cpython + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + sha256: 7586a711b1b08a9df8864e26efdc06980bdfb0e18d5ac4651d0fee30a8d3e3a0 + md5: 54ca5b5d92ef3a3ba61e195ee882a518 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 12998673 + timestamp: 1733408900971 +- kind: conda + name: python-dateutil + version: 2.9.0.post0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + sha256: a50052536f1ef8516ed11a844f9413661829aa083304dc624c5925298d078d79 + md5: 5ba79d7c71f03c678c8ead841f347d6e + depends: + - python >=3.9 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222505 + timestamp: 1733215763718 +- kind: conda + name: python-dotenv + version: 1.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 + md5: e5c6ed218664802d305e79cc2d4491de + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 24215 + timestamp: 1733243277223 +- kind: conda + name: python-json-logger + version: 2.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca + md5: a61bf9ec79426938ff785eb69dbb1960 + depends: + - python >=3.6 + license: BSD-2-Clause + license_family: BSD + size: 13383 + timestamp: 1677079727691 +- kind: conda + name: python-multipart + version: 0.0.20 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 27913 + timestamp: 1734420869885 +- kind: conda + name: python-tzdata + version: '2024.2' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + sha256: 57c9a02ec25926fb48edca59b9ede107823e5d5c473b94a0e05cc0b9a193a642 + md5: c0def296b2f6d2dd7b030c2a7f66bb1f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 142235 + timestamp: 1733235414217 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + sha256: 28204ef48f028a4d872e22040da0dad7ebd703549b010a1bb511b6dd94cf466d + md5: 266fe1ae54a7bb17990206664d0f0ae4 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 21765 + timestamp: 1725272382968 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h52516f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + sha256: 0fa5ba80073a43391ee90303814adbc9fd826175de1fdac273ba0e5b711aa255 + md5: 591c4ae6d8338dfd07b951e00433a405 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23589 + timestamp: 1725273317965 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + sha256: 20851b1e59fee127d49e01fc73195a88ab0779f103b7d6ffc90d11142a83678f + md5: 39aed2afe4d0cf76ab3d6b09eecdbea7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23162 + timestamp: 1725272139519 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 + md5: 0424ae29b104430108f5218a66db7260 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6238 + timestamp: 1723823388266 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + sha256: 5ccdad9981753cc4a2d126e356673a21c0cd5b34e209cb8d476a3947d4ad9b39 + md5: 62b20f305498284a07dc6c45fd0e5c87 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6329 + timestamp: 1723823366253 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + sha256: 49d624e4b809c799d2bf257b22c23cf3fc4460f5570d9a58e7ad86350aeaa1f4 + md5: b76f9b1c862128e56ac7aa8cd2333de9 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6278 + timestamp: 1723823099686 +- kind: conda + name: pytz + version: '2024.1' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + sha256: 1a7d6b233f7e6e3bbcbad054c8fd51e690a67b129a899a056a5e45dd9f00cb41 + md5: 3eeeeb9e4827ace8c0c1419c85d590ad + depends: + - python >=3.7 + license: MIT + license_family: MIT + size: 188538 + timestamp: 1706886944988 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + sha256: b06f1c15fb39695bbf707ae8fb554b9a77519af577b5556784534c7db10b52e3 + md5: 1ee23620cf46cb15900f70a1300bae55 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 187143 + timestamp: 1725456547263 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + sha256: a60705971e958724168f2ebbb8ed4853067f1d3f7059843df3903e3092bbcffa + md5: 549e5930e768548a89c23f595dac5a95 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 206553 + timestamp: 1725456256213 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + sha256: 8c515ebe1e7e85d972d72b75760af9dfac06fd11a9dba7e05c42d69aedbb303c + md5: dc5de424f7dbb9772da720dbb81317b2 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 199141 + timestamp: 1725456356043 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312h2427ae1_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + sha256: cfc4ea87d68b5f0ed64a61f500d5ea0a2310d1f281a4f95afa06c703ea1bdf7d + md5: 1f0779280c3dc1e72cfd86bd1e59791d + depends: + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 371730 + timestamp: 1728644030875 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hbf22597_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + sha256: bc303f9b11e04a515f79cd5ad3bfa0e84b9dfec76552626d6263b38789fe6678 + md5: 746ce19f0829ec3e19c93007b1a224d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 378126 + timestamp: 1728642454632 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hf8a1cbd_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + sha256: 2e0ca1bb9ab3af5d1f9b38548d65be7097ba0246e7e63c908c9b1323df3f45b5 + md5: 7bdaa4c2a84b744ef26c8b2ba65c3d0e + depends: + - __osx >=11.0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 361674 + timestamp: 1728642457661 +- kind: conda + name: re2 + version: 2024.07.02 + build: h2d3a13d_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + sha256: 55e7be480bfb979fa8595a16d7f2adea3a5ac9a77b2e97cd0f7ac40e989edb6c + md5: 83f4e47229834c895a92c18383e1cd9d + depends: + - libre2-11 2024.07.02 h18dbdb1_1 + license: BSD-3-Clause + license_family: BSD + size: 26747 + timestamp: 1728778986331 +- kind: conda + name: re2 + version: 2024.07.02 + build: h77b4e00_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + sha256: c1721cb80f7201652fc9801f49c214c88aee835d957f2376e301bd40a8415742 + md5: 01093ff37c1b5e6bf9f17c0116747d11 + depends: + - libre2-11 2024.07.02 hbbce691_1 + license: BSD-3-Clause + license_family: BSD + size: 26665 + timestamp: 1728778975855 +- kind: conda + name: re2 + version: 2024.07.02 + build: hcd0e937_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + sha256: eebddde6cb10b146507810b701ef6df122d5309cd5151a39d0828aa44dc53725 + md5: 19e29f2ccc9168eb0a39dc40c04c0e21 + depends: + - libre2-11 2024.07.02 h2348fd5_1 + license: BSD-3-Clause + license_family: BSD + size: 26860 + timestamp: 1728779123653 +- kind: conda + name: readline + version: '8.2' + build: h8228510_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 + md5: 47d31b792659ce70f470b5c82fdfb7a4 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 281456 + timestamp: 1679532220005 +- kind: conda + name: readline + version: '8.2' + build: h8fc344f_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + sha256: 4c99f7417419734e3797d45bc355e61c26520e111893b0d7087a01a7fbfbe3dd + md5: 105eb1e16bf83bfb2eb380a48032b655 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 294092 + timestamp: 1679532238805 +- kind: conda + name: readline + version: '8.2' + build: h92ec313_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 + md5: 8cbb776a2f641b943d413b3e19df71f4 + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 250351 + timestamp: 1679532511311 +- kind: conda + name: regex + version: 2024.11.6 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + sha256: fcb5687d3ec5fff580b64b8fb649d9d65c999a91a5c3108a313ecdd2de99f06b + md5: 647770db979b43f9c9ca25dcfa7dc4e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 402821 + timestamp: 1730952378415 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + sha256: ec2c416860de29224e447e2031f8686a05476759c17da1f32f61d4307e540ec8 + md5: fa8b589107567f532fa1380e66f91776 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 398947 + timestamp: 1730952477463 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + sha256: dcdec32f2c7dd37986baa692bedf9db126ad34e92e5e9b64f707cba3d04d2525 + md5: e73cda1f18846b608284bd784f061eac + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 366374 + timestamp: 1730952427552 +- kind: conda + name: requests + version: 2.32.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad + md5: a9b9368f3701a417eac9edbcae7cb737 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 58723 + timestamp: 1733217126197 +- kind: conda + name: rich + version: 13.9.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 + md5: 7aed65d4ff222bfb7335997aa40b7da5 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + size: 185646 + timestamp: 1733342347277 +- kind: conda + name: rich-toolkit + version: 0.11.3 + build: pyh29332c3_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 + md5: 4ba15ae9388b67d09782798347481f69 + depends: + - python >=3.9 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 17357 + timestamp: 1733750834072 +- kind: conda + name: s2n + version: 1.5.10 + build: h5df210e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + sha256: b5e7a9f4b7b1ec5c5c3661e2defc8b47fab543b05cad6fec78739d8007612464 + md5: 3d3979efcc0f44f3f0cef3de03b296cc + depends: + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 353450 + timestamp: 1734415474615 +- kind: conda + name: s2n + version: 1.5.10 + build: hb5b8611_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + sha256: f6d451821fddc26b93f45e9313e1ea15e09e5ef049d4e137413a5225d2a5dfba + md5: 999f3673f2a011f59287f2969e3749e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 355142 + timestamp: 1734415467047 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + sha256: e44515f875c10efb5e041efcb250dfd18f2cb66ec3f268237549ead6284c6922 + md5: 3b87a00bcaab069172d6cef8124b7142 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 402547 + timestamp: 1725632183154 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + sha256: e83ebeaba4a07bbe4a1d6c7eef0b4f7ae19901ef365bca043808d16e4c8faad8 + md5: 82ef253c37308b082a478fb92924cad6 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 400284 + timestamp: 1725632278147 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312he431725_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + sha256: 93a085d0d64237db7f4ff395c446f268c575dc2c324d8e3e5c5d7d836896295e + md5: ccb978cf1e3151c25a44c4ae65c1f20e + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 353606 + timestamp: 1725632294079 +- kind: conda + name: shellingham + version: 1.5.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef + md5: 7c3c2a0f3ebdea2bbc35538d162b43bf + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14462 + timestamp: 1733301007770 +- kind: conda + name: six + version: 1.17.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + sha256: 41db0180680cc67c3fa76544ffd48d6a5679d96f4b71d7498a759e94edc9a2db + md5: a451d576819089b0d672f18768be0f65 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 16385 + timestamp: 1733381032766 +- kind: conda + name: snappy + version: 1.2.1 + build: h8bd8927_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + sha256: ec91e86eeb2c6bbf09d51351b851e945185d70661d2ada67204c9a6419d282d3 + md5: 3b3e64af585eadfb52bb90b553db5edf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 42739 + timestamp: 1733501881851 +- kind: conda + name: snappy + version: 1.2.1 + build: h98b9ce2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f + md5: ded86dee325290da2967a3fea3800eb5 + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-3-Clause + license_family: BSD + size: 35857 + timestamp: 1733502172664 +- kind: conda + name: snappy + version: 1.2.1 + build: hd4fb6f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + sha256: c4a07ae5def8d55128f25a567a296ef9d7bf99a3bc79d46bd5160c076a5f50af + md5: 2fcc6cd1e5550deb509073fd2e6693e1 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 43032 + timestamp: 1733501964775 +- kind: conda + name: sniffio + version: 1.3.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 + md5: bf7a226e58dfb8346c70df36065d86c9 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 15019 + timestamp: 1733244175724 +- kind: conda + name: sse-starlette + version: 2.1.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + sha256: 6d671a66333410ec7e5e7858a252565a9001366726d1fe3c3a506d7156169085 + md5: 3918255c942c242ed5599e10329e8d0e + depends: + - anyio + - python >=3.8 + - starlette + - uvicorn + license: BSD-3-Clause + license_family: BSD + size: 14712 + timestamp: 1722520112550 +- kind: conda + name: starlette + version: 0.41.3 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + sha256: b74fc76107487eb26624c01fc55bfab7eed03ae82e003333c86d8a1eeac53672 + md5: 0207dac04ae2200701fab697f0aaaac4 + depends: + - anyio >=3.4.0,<5 + - python >=3.9 + - typing_extensions >=3.10.0 + license: BSD-3-Clause + license_family: BSD + size: 58838 + timestamp: 1733344472634 +- kind: conda + name: tk + version: 8.6.13 + build: h194ca79_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + sha256: 7fa27cc512d3a783f38bd16bbbffc008807372499d5b65d089a8e43bde9db267 + md5: f75105e0585851f818e0009dd1dde4dc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3351802 + timestamp: 1695506242997 +- kind: conda + name: tk + version: 8.6.13 + build: h5083fa2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 + md5: b50a57ba89c32b62428b71a875291c9b + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3145523 + timestamp: 1699202432999 +- kind: conda + name: tk + version: 8.6.13 + build: noxft_h4845f30_101 + build_number: 101 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3318875 + timestamp: 1699202167581 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312h8360d73_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + sha256: 4f504a5e9d77c6d88a8f735c4319429d8bf40b742384f908a2efe0a09acc3cc5 + md5: f953aa733207f3d37acf4a3efbedba89 + depends: + - __glibc >=2.17,<3.0.a0 + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2258007 + timestamp: 1732734202127 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312ha0d6ea1_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + sha256: ef0f4d4e2c798b1821187ea0ba4c86484e48abaa0e9a19fe68030fa7ff5dde84 + md5: 077f48c9e0c08a30d842e15c51df4143 + depends: + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2331194 + timestamp: 1732734303196 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312hf3e4074_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + sha256: 5d395333fcb22dc611140286c1f2ea8b3fa220a4931c583587cb612238091555 + md5: 4c732c74b485ef7ac8ec1c548dd45e8e + depends: + - __osx >=11.0 + - huggingface_hub >=0.16.4,<1.0 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 1931389 + timestamp: 1732734727624 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h52516f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + sha256: 4c19a544354172b2273553267e734795a6da3c78a04c2d19f8e9e159ca3178bc + md5: e28996d9d2d44d777b7e6fb12f63715b + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 841662 + timestamp: 1732616934923 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + sha256: 062a3a3a37fa8615ce57929ba7e982c76f5a5810bcebd435950f6d6c4147c310 + md5: e417822cb989e80a0d2b1b576fdd1657 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 840414 + timestamp: 1732616043734 +- kind: conda + name: tornado + version: 6.4.2 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + sha256: 964a2705a36c50040c967b18b45b9cc8de3c2aff4af546979a574e0b38e58e39 + md5: fb0605888a475d6a380ae1d1a819d976 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 842549 + timestamp: 1732616081362 +- kind: conda + name: tqdm + version: 4.67.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d + md5: 4085c9db273a148e149c03627350e22c + depends: + - colorama + - python >=3.7 + license: MPL-2.0 or MIT + size: 89484 + timestamp: 1732497312317 +- kind: conda + name: traitlets + version: 5.14.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 + md5: 019a7385be9af33791c989871317e1ed + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110051 + timestamp: 1733367480074 +- kind: conda + name: transformers + version: 4.47.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + sha256: d31821081219a0ede5c1f356b65a61ce98ac11e2df78b0eaa684c17c73389fbf + md5: 6d2ec1ddee8057d2d724a0ab0bb578a0 + depends: + - datasets !=2.5.0 + - filelock + - huggingface_hub >=0.23.0,<1.0 + - numpy >=1.17 + - packaging >=20.0 + - python >=3.9 + - pyyaml >=5.1 + - regex !=2019.12.17 + - requests + - safetensors >=0.4.1 + - tokenizers >=0.21,<0.22 + - tqdm >=4.27 + license: Apache-2.0 + license_family: APACHE + size: 3726957 + timestamp: 1733948063517 +- kind: conda + name: typer + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + sha256: ef695490e895c2ad552c77ec497b899b09fd4ad4ab07edcf5649f5994cf92a35 + md5: 170a0398946d8f5b454e592672b6fc20 + depends: + - python >=3.9 + - typer-slim-standard 0.15.1 hd8ed1ab_0 + license: MIT + license_family: MIT + size: 56175 + timestamp: 1733408582623 +- kind: conda + name: typer-slim + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + sha256: d4965516f35e0805199de6596c4ac76c4ad3d6b012be35e532102f9e53ecb860 + md5: 0218b16f5a1dd569e575a7a6415489db + depends: + - click >=8.0.0 + - python >=3.9 + - typing_extensions >=3.7.4.3 + constrains: + - rich >=10.11.0 + - typer >=0.15.1,<0.15.2.0a0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + size: 43592 + timestamp: 1733408569554 +- kind: conda + name: typer-slim-standard + version: 0.15.1 + build: hd8ed1ab_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + sha256: f31c56fe98315da8b9ce848256c17e0b9f87896b41a6ccf0c9cc74644dcef20f + md5: 4e603c43bfdfc7b533be087c3e070cc9 + depends: + - rich + - shellingham + - typer-slim 0.15.1 pyhd8ed1ab_0 + license: MIT + license_family: MIT + size: 49531 + timestamp: 1733408570063 +- kind: conda + name: typing-extensions + version: 4.12.2 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 + md5: b6a408c64b78ec7b779a3e5c7a902433 + depends: + - typing_extensions 4.12.2 pyha770c72_1 + license: PSF-2.0 + license_family: PSF + size: 10075 + timestamp: 1733188758872 +- kind: conda + name: typing_extensions + version: 4.12.2 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 + md5: d17f13df8b65464ca316cbc000a3cb64 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 39637 + timestamp: 1733188758212 +- kind: conda + name: tzdata + version: 2024b + build: hc8b5060_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + sha256: 4fde5c3008bf5d2db82f2b50204464314cc3c91c1d953652f7bd01d9e52aefdf + md5: 8ac3367aafb1cc0a068483c580af8015 + license: LicenseRef-Public-Domain + size: 122354 + timestamp: 1728047496079 +- kind: conda + name: urllib3 + version: 2.2.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + sha256: 416e30a1c3262275f01a3e22e783118d9e9d2872a739a9ed860d06fa9c7593d5 + md5: 4a2d8ef7c37b8808c5b9b750501fffce + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + size: 98077 + timestamp: 1733206968917 +- kind: conda + name: uvicorn + version: 0.34.0 + build: pyh31011fe_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa + md5: 5d448feee86e4740498ec8f8eb40e052 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.9 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + size: 48643 + timestamp: 1734293057914 +- kind: conda + name: uvicorn-standard + version: 0.34.0 + build: h31011fe_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec + md5: 32a94143a7f65d76d2d5da37dcb4ed79 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.34.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + size: 7203 + timestamp: 1734293058849 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h0bf5046_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + sha256: b1efa77aa4871d7bb09c8dd297fa9bd9070ba7f0f95f2d12ae9cdd31ce8b6b22 + md5: 4f5110253ba80ebf27e55c4ab333880a + depends: + - __osx >=11.0 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 544097 + timestamp: 1730214653726 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + sha256: 9337a80165fcf70b06b9d6ba920dad702260ca966419ae77560a15540e41ab72 + md5: 998e481e17c1b6a74572e73b06f2df08 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 701355 + timestamp: 1730214506716 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + sha256: 807eede6698bd00a1d739a3e19ee6ae6a03a66d2ddd2ef150f2dfd198c3b0292 + md5: d83e107ba16c77aba2feec47b7b666a4 + depends: + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 655266 + timestamp: 1730214606664 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + sha256: c89755d8e8f6384b3ba13e41dcabb40bf690c38b9d61512e963129badb1ad332 + md5: b76a5ad00856af6e74da9c3e85fed0cc + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 410432 + timestamp: 1733998892675 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + sha256: 9be9569c279dc6e7881e9b45fe9f0368218538c660641e2f8b0e023e72a6571c + md5: 3465c1a19634233abc2d1832ac01fd31 + depends: + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 404239 + timestamp: 1733998941045 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + sha256: b64b78a7d6384bf72a878256802c783c692fe641ab4b806fd7e9f45e18a5e3b4 + md5: 13b89e1aa72aa773806b1f59ec018b67 + depends: + - __osx >=11.0 + - anyio >=3.0.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 363162 + timestamp: 1733999215646 +- kind: conda + name: websockets + version: '14.1' + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + sha256: 5998940f91765ba991cf286c863c20bcb53db92bb976a2b5a714566b86b0e763 + md5: a79f7ce618bd0a9f4c00c59a03570fcd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242145 + timestamp: 1731498716195 +- kind: conda + name: websockets + version: '14.1' + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + sha256: c292a8badcbe4040537e225fbeb237bfaf272808eab060067d965d3da98ccd5c + md5: 7e2a0ef2a1a87f88f9745f9c7059186e + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242912 + timestamp: 1731498811466 +- kind: conda + name: websockets + version: '14.1' + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + sha256: 98fb04a1a0f53dc604378f94b5795d0b8e462fee01bf0a887cb34d0efdf5d21f + md5: 89b79a9baa7db46ce21f5738a5a3dfda + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 243131 + timestamp: 1731498944076 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + sha256: a6fc0f4e90643d0c1fd4aab669b6a79f44a305a5474256f6f2da3354d2310fb4 + md5: ddbe3bb0e1356cb9074dd848570694f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 63807 + timestamp: 1732523690292 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + sha256: b9aa760a987ccc6bc9c61f57badba6798d9a3dcbd0814e5fb8df6d8d2935af73 + md5: 120d5d1c05386d8ce3efd65a4c86431f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 64783 + timestamp: 1732523806 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + sha256: 0fb35c3d1642f9f47db87bdb33148f88ef19a3af1eb0ee99b5491551c57269c7 + md5: 73414acdb779a8694a14527865b4357a + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 61043 + timestamp: 1732523852129 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 13593 + timestamp: 1734229104321 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 + md5: d5397424399a66d33c80b1f2345a36a6 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 15873 + timestamp: 1734230458294 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 14780 + timestamp: 1734229004433 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + sha256: efcc150da5926cf244f757b8376d96a4db78bc15b8d90ca9f56ac6e75755971f + md5: 25a5a7b797fe6e084e04ffe2db02fc62 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 20615 + timestamp: 1727796660574 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 19901 + timestamp: 1727794976192 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hd74edd7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 + md5: 77c447f48cab5d3a15ac224edb86a968 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 18487 + timestamp: 1727795205022 +- kind: conda + name: xxhash + version: 0.8.2 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + sha256: 4c526aed70b579d80e5c20d32130b6bc8bde59b3250d43c2b5269755f4da8a9b + md5: bb9faf6857108a9f62ebb4dab6ef05da + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 102442 + timestamp: 1689951682147 +- kind: conda + name: xxhash + version: 0.8.2 + build: hb547adb_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + sha256: a70f59f7221ee72c45b39a6b36a33eb9c717ba01921cce1a3c361a4676979a2e + md5: 144cd3b88706507f332f5eb5fb83a33b + license: BSD-2-Clause + license_family: BSD + size: 97593 + timestamp: 1689951969732 +- kind: conda + name: xxhash + version: 0.8.2 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f + md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 97691 + timestamp: 1689951608120 +- kind: conda + name: yaml + version: 0.2.5 + build: h3422bc3_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 + md5: 4bb3f014845110883a3c5ee811fd84b4 + license: MIT + license_family: MIT + size: 88016 + timestamp: 1641347076660 +- kind: conda + name: yaml + version: 0.2.5 + build: h7f98852_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + sha256: a4e34c710eeb26945bdbdaba82d3d74f60a78f54a874ec10d373811a5d217535 + md5: 4cb3ad778ec2d5a7acbdf254eb1c42ae + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 89141 + timestamp: 1641346969816 +- kind: conda + name: yaml + version: 0.2.5 + build: hf897c2e_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + sha256: 8bc601d6dbe249eba44b3c456765265cd8f42ef1e778f8df9b0c9c88b8558d7e + md5: b853307650cb226731f653aa623936a4 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 92927 + timestamp: 1641347626613 +- kind: conda + name: yarl + version: 1.18.3 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + sha256: a0d93c3bef723e384cff8a29a82a2c6b7a73b39328088f3a2d97c901f56e9a63 + md5: 91df2efaa08730416bec2a4502309275 + depends: + - __glibc >=2.17,<3.0.a0 + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 151393 + timestamp: 1733428897813 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + sha256: 470b5b0f3ac89acd143095281167dc2ac1a56d4fa22e1794bd8f3b00bb604540 + md5: 0b3c640697bca798d0ab428f530ed24c + depends: + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 150004 + timestamp: 1733429056665 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + sha256: 69c7863809e11bc90c0d935c16e7f151dcc925add08b3894f06059263a8cb9ba + md5: f32f9b16361866a62d6e061fcd7eb400 + depends: + - __osx >=11.0 + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 141556 + timestamp: 1733429104990 +- kind: conda + name: zeromq + version: 4.3.5 + build: h3b0a872_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + sha256: a4dc72c96848f764bb5a5176aa93dd1e9b9e52804137b99daeebba277b31ea10 + md5: 3947a35e916fcc6b9825449affbf4214 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 335400 + timestamp: 1731585026517 +- kind: conda + name: zeromq + version: 4.3.5 + build: h5efb499_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + sha256: a6003096dc0570a86492040ba32b04ce7662b159600be2252b7a0dfb9414e21c + md5: f2f3282559a4b87b7256ecafb4610107 + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 371419 + timestamp: 1731589490850 +- kind: conda + name: zeromq + version: 4.3.5 + build: hc1bb282_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + sha256: 9e585569fe2e7d3bea71972cd4b9f06b1a7ab8fa7c5139f92a31cbceecf25a8a + md5: f7e6b65943cb73bce0143737fded08f1 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=18 + - libsodium >=1.0.20,<1.0.21.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 281565 + timestamp: 1731585108039 +- kind: conda + name: zipp + version: 3.21.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 + md5: 0c3cc595284c5e8f0f9900a9b228a332 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 21809 + timestamp: 1732827613585 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312h15fbf35_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + sha256: d00ca25c1e28fd31199b26a94f8c96574475704a825d244d7a6351ad3745eeeb + md5: a4cde595509a7ad9c13b1a3809bcfe51 + depends: + - __osx >=11.0 + - cffi >=1.11 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 330788 + timestamp: 1725305806565 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hb698573_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + sha256: 2681c2a249752bdc7978e59ee2f34fcdfcbfda80029b84b8e5fec8dbc9e3af25 + md5: ffcb8e97e62af42075e0e5f46bb9856e + depends: + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 392496 + timestamp: 1725305808244 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hef9b889_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b + md5: 8b7069e9792ee4e5b4919a7a306d2e67 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 419552 + timestamp: 1725305670210 +- kind: conda + name: zstd + version: 1.5.6 + build: h02f22dd_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c + md5: be8d5f8cf21aed237b8b182ea86b3dd6 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 539937 + timestamp: 1714723130243 +- kind: conda + name: zstd + version: 1.5.6 + build: ha6fb4c9_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 554846 + timestamp: 1714722996770 +- kind: conda + name: zstd + version: 1.5.6 + build: hb46c0d2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda + sha256: 2d4fd1ff7ee79cd954ca8e81abf11d9d49954dd1fef80f27289e2402ae9c2e09 + md5: d96942c06c3e84bfcc5efb038724a7fd + depends: + - __osx >=11.0 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 405089 + timestamp: 1714723101397 diff --git a/magic.lock b/magic.lock new file mode 100644 index 0000000000..427e22126f --- /dev/null +++ b/magic.lock @@ -0,0 +1,9171 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + - conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + - conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda +packages: +- kind: conda + name: _libgcc_mutex + version: '0.1' + build: conda_forge + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + size: 2562 + timestamp: 1578324546067 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23621 + timestamp: 1650670423406 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 + md5: 6168d71addc746e8f2b8d57dfd2edcea + depends: + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23712 + timestamp: 1650670790230 +- kind: conda + name: aiohappyeyeballs + version: 2.4.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda + sha256: 95d4713e49ea92ae50cf42393683ede706b7875af5f7cb14c253438180afa732 + md5: 296b403617bafa89df4971567af79013 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 19351 + timestamp: 1733332029649 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h178313f_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + sha256: dc8ebdd99e9d7a07454a7063a295cdc7a86264648647fec10b2ceae97478e200 + md5: 3e92784b8e32ab7d0b95ee296ba79a99 + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 914378 + timestamp: 1733839626367 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312h998013c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + sha256: 69eb9c89dce6a7ae960099172daffba9f77fef39344f37e581685a8e3c5debe6 + md5: 642356223364539ba7ba36556fcf49ee + depends: + - __osx >=11.0 + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 874135 + timestamp: 1733839113411 +- kind: conda + name: aiohttp + version: 3.11.10 + build: py312hcc812fe_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + sha256: df694a9fec546e575a4ea7e1c3ac476c0bda53c0fad44c046ad3ebdd5b75a0a8 + md5: a8c9ec59e6323b38418bbf04deaa0c02 + depends: + - aiohappyeyeballs >=2.3.0 + - aiosignal >=1.1.2 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=13 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + size: 900931 + timestamp: 1733839037447 +- kind: conda + name: aiosignal + version: 1.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + sha256: 7de8ced1918bbdadecf8e1c1c68237fe5709c097bd9e0d254f4cad118f4345d0 + md5: 1a3981115a398535dbe3f6d5faae3d36 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 13229 + timestamp: 1734342253061 +- kind: conda + name: annotated-types + version: 0.7.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- kind: conda + name: anyio + version: 4.7.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + sha256: 687537ee3af30f8784986bf40cac30e88138770b16e51ca9850c9c23c09aeba1 + md5: c88107912954a983c2caf25f7fd55158 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.9 + - sniffio >=1.1 + - typing_extensions >=4.5 + constrains: + - trio >=0.26.1 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 112730 + timestamp: 1733532678437 +- kind: conda + name: attrs + version: 24.3.0 + build: pyh71513ae_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + sha256: 750186af694a7130eaf7119fbb56db0d2326d8995ad5b8eae23c622b85fea29a + md5: 356927ace43302bf6f5926e2a58dae6a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 56354 + timestamp: 1734348889193 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h2cb9fb3_15 + build_number: 15 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + sha256: 4ce859dc9ff128bf5515604c43f33fb511386022fc9765ca077990f2a3f23df5 + md5: e524686ace966acefb5b8cbc6e8b3daa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 111854 + timestamp: 1734021745104 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: h8bc59a9_15 + build_number: 15 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + sha256: 0e41e56b662e76e024182adebcd91d09a4d38a83b35217c84e4967354dfff9a2 + md5: f688b8893c20ad9477a19e7ce614014a + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + license: Apache-2.0 + license_family: Apache + size: 92507 + timestamp: 1734021831330 +- kind: conda + name: aws-c-auth + version: 0.8.0 + build: hb921021_15 + build_number: 15 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + sha256: 537006ad6d5097c134494166a6a1dc1451d5d050878d7b82cef498bfda40ba8a + md5: c79d50f64cffa5ad51ecc1a81057962f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 107614 + timestamp: 1734021692519 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h1a47875_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + sha256: 095ac824ea9303eff67e04090ae531d9eb33d2bf8f82eaade39b839c421e16e8 + md5: 55a8561fdbbbd34f50f57d9be12ed084 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 47601 + timestamp: 1733991564405 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: h740c5af_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda + sha256: c5c7961d48ca7320ed3560c036f7aa5244df4b85d9657f70aacc5faba3e1509a + md5: 57ed2c445d7ef01d121b9bcea0522913 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 50036 + timestamp: 1733991581303 +- kind: conda + name: aws-c-cal + version: 0.8.1 + build: hc8a0bd2_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda + sha256: 1f44be36e1daa17b4b081debb8aee492d13571084f38b503ad13e869fef24fe4 + md5: 8b0ce61384e5a33d2b301a64f3d22ac5 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - openssl >=3.3.1,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 39925 + timestamp: 1733991649383 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a + md5: 145e5b4c9702ed279d7d68aaf096f77d + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221863 + timestamp: 1733975576886 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + sha256: 57288ec5df35781bea8fc6a8c9099cad6695b747784fc1b8862a0f9e5b3bf5ab + md5: fef806a0f6de853670c746bbece01966 + depends: + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 259031 + timestamp: 1733975520465 +- kind: conda + name: aws-c-common + version: 0.10.6 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 + md5: d7d4680337a14001b0e043e96529409b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 236574 + timestamp: 1733975453350 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h0f0193d_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + sha256: 3f05d19f68ef800f33d44ea2a4211003124076516c8469abc7d432236344df53 + md5: 3a1421d12435df5b4c412cc4c8fac64d + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19740 + timestamp: 1733991625201 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: h4e1184b_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b + md5: 3f4c1197462a6df2be6dc8241828fe93 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 19086 + timestamp: 1733991637424 +- kind: conda + name: aws-c-compression + version: 0.3.0 + build: hc8a0bd2_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda + sha256: 47b2813f652ce7e64ac442f771b2a5f7d4af4ad0d07ff51f6075ea80ed2e3f09 + md5: a8b6c17732d14ed49d0e9b59c43186bc + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 18068 + timestamp: 1733991869211 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h54f970a_11 + build_number: 11 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d + md5: ba41238f8e653998d7d2f42e3a8db054 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 47078 + timestamp: 1734024749727 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: h7959bf6_11 + build_number: 11 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda + sha256: 10d7240c7db0c941fb1a59c4f8ea6689a434b03309ee7b766fa15a809c553c02 + md5: 9b3fb60fe57925a92f399bc3fc42eccf + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 54003 + timestamp: 1734024480949 +- kind: conda + name: aws-c-event-stream + version: 0.5.0 + build: hcbd8f92_11 + build_number: 11 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda + sha256: 79aa363c71c891a27496c0498f8d498b2ddc87b3ccb3b6c9da5b50b05936ebb8 + md5: e0772c59af4243a9b2565baa5d79e5b6 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 55207 + timestamp: 1734024546663 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h3df160d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda + sha256: 3a1d2d332945306be9d49e569b95e4cc172d825f10e88715513a172f28ebb59e + md5: 28f00aa7fd9556c4c461328cf146c20b + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 190586 + timestamp: 1734008442362 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: h96aa502_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda + sha256: 22e4737c8a885995b7c1ae1d79c1f6e78d489e16ec079615980fdde067aeaf76 + md5: 495c93a4f08b17deb3c04894512330e6 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 152983 + timestamp: 1734008451473 +- kind: conda + name: aws-c-http + version: 0.9.2 + build: hefd7a92_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 + md5: 5ce4df662d32d3123ea8da15571b6f51 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 197731 + timestamp: 1734008380764 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h1a307af_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + sha256: 71f5bf891299f831dceaea12f926c393bf754569e5305387a88b77e1f94612d8 + md5: da8ab0f3eeac93449ec3d531ede92caa + depends: + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 161889 + timestamp: 1734433686109 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: h831e299_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + sha256: 5920009b1c6f9a2bc131a36725251894e4b4773fce29c4b1065d4213ae337abe + md5: 80dd9f0ddf935290d1dc00ec75ff3023 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + - s2n >=1.5.10,<1.5.11.0a0 + license: Apache-2.0 + license_family: Apache + size: 157864 + timestamp: 1734433578570 +- kind: conda + name: aws-c-io + version: 0.15.3 + build: haba67d1_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + sha256: c0a1a2b0750225ac3dc07fd258c88c2be866bf8ac67ba3d50bb4ecec852ff8ee + md5: 4c5ff4134e76426a75b8c548984fa933 + depends: + - __osx >=11.0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 135729 + timestamp: 1734433832730 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h11f4f37_12 + build_number: 12 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + sha256: 512d3969426152d9d5fd886e27b13706122dc3fa90eb08c37b0d51a33d7bb14a + md5: 96c3e0221fa2da97619ee82faa341a73 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 194672 + timestamp: 1734025626798 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h24f418c_12 + build_number: 12 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd + md5: c072045a6206f88015d02fcba1705ea1 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + license: Apache-2.0 + license_family: Apache + size: 134371 + timestamp: 1734025379525 +- kind: conda + name: aws-c-mqtt + version: 0.11.0 + build: h5f50e26_12 + build_number: 12 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda + sha256: ffeb9100cc8fd4093e1a6fdfd938bc4a396dd77480b7fb17aa42855a4d5e2c70 + md5: 031ca33115d4b1eeb43f435d6215778c + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 169516 + timestamp: 1734025167885 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h1be5864_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda + sha256: 22966164d63808689fffd35945f57756c95337327e28099b5d77b29fc6a56ecc + md5: a37bba7acb62dd70492ee01eacca3b8f + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + license: Apache-2.0 + license_family: Apache + size: 97598 + timestamp: 1734146239038 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: h2080895_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda + sha256: 20bc2dd60e6518d9b8215c2b652ab5c52ee8a997d3b9a5f69e2dabd28cbf26b2 + md5: ae223efa63fbb4262a2d85c3ab3bc4f5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 117641 + timestamp: 1734146239779 +- kind: conda + name: aws-c-s3 + version: 0.7.7 + build: hf454442_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda + sha256: c2f205a7bf64c5f40eea373b3a0a7c363c9aa9246a13dd7f3d9c6a4434c4fe2d + md5: 947c82025693bebd557f782bb5d6b469 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 114156 + timestamp: 1734146123386 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + sha256: ede8e782467c87ac80ceb9c9af9e917d121b7d8b8c698186d18e3cecd36f2210 + md5: 53e798d720dd78b78847a7b2fdb05fc9 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 58621 + timestamp: 1733994421495 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + sha256: df586f42210af1134b1c88ff4c278c3cb6d6c807c84eac48860062464b28554d + md5: a5126a90e74ac739b00564a4c7ddcc36 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 56094 + timestamp: 1733994449690 +- kind: conda + name: aws-c-sdkutils + version: 0.2.1 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + sha256: de98343ce42d2e569b3380292d20f47bf39bda08aadabcbb8e650d3f38fd742f + md5: 22f72f8cd7ead211304ac17d337d96e0 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 49664 + timestamp: 1733994553014 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h0f0193d_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da + md5: 3bd35b0adab3d743f09e0252cc441d6b + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72154 + timestamp: 1733994384415 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: h4e1184b_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + sha256: 1ed9a332d06ad595694907fad2d6d801082916c27cd5076096fda4061e6d24a8 + md5: 74e8c3e4df4ceae34aa2959df4b28101 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72762 + timestamp: 1733994347547 +- kind: conda + name: aws-checksums + version: 0.2.2 + build: hc8a0bd2_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda + sha256: 215086d95e8ff1d3fcb0197ada116cc9d7db1fdae7573f5e810d20fa9215b47c + md5: e70e88a357a3749b67679c0788c5b08a + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + license: Apache-2.0 + license_family: Apache + size: 70186 + timestamp: 1733994496998 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h19a973c_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda + sha256: 8269e6746eb3a5d15b732a3983888bf98dfc1f6594e95250fc8d16b43cfd5ff9 + md5: 95714136bef3e917bd5a2942d4682b20 + depends: + - __osx >=11.0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 236249 + timestamp: 1734178020924 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: h8a4e35f_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda + sha256: 5ba9188e0cb4e3faff9bc96774febb040aa3b802aedba29d847e00e7b5eab84e + md5: d77a9e3d7ce15399903e92825fd651b5 + depends: + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 283154 + timestamp: 1734177845248 +- kind: conda + name: aws-crt-cpp + version: 0.29.7 + build: hd92328a_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda + sha256: 094cd81f1e5ba713e9e7a272ee52b5dde3ccc4842ea90f19c0354a00bbdac3d9 + md5: 02b95564257d5c3db9c06beccf711f95 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-c-mqtt >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.7.7,<0.7.8.0a0 + - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 354703 + timestamp: 1734177883319 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: h849ce1a_4 + build_number: 4 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + sha256: 51b9e9df8cbab4a13a1b9d39d6ef5ed162aaa29c09a745810e00bbe92e1045c1 + md5: cda7747f4398be8d1fb37362815917a7 + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2920625 + timestamp: 1734093552712 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: hc430e4a_4 + build_number: 4 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + sha256: 2dc09f6f9c49127b5f96e7535b64a9c521b944d76d8b7d03d48ae80257ac1cea + md5: aeefac461bea1f126653c1285cf5af08 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 3060561 + timestamp: 1734093737431 +- kind: conda + name: aws-sdk-cpp + version: 1.11.458 + build: he0ff2e4_4 + build_number: 4 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + sha256: 535b970aaa13be45f8cab8205c59f044b17364111c41a227f061775a5c834e18 + md5: 0981ed87098b149bdb7d99a4a3fd0e58 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-event-stream >=0.5.0,<0.5.1.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 2826534 + timestamp: 1734094018287 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h1887c18_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 + md5: e0c3a906a41be769f0ae20ca3e31cfc0 + depends: + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 338650 + timestamp: 1728055589907 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: h5cfcd09_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a + md5: 0a8838771cc2e985cd295e01ae83baf1 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 345117 + timestamp: 1728053909574 +- kind: conda + name: azure-core-cpp + version: 1.14.0 + build: hd50102c_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + sha256: f5b91329ed59ffc0be8747784c6e4cc7e56250c54032883a83bc11808ef6a87e + md5: f093a11dcf3cdcca010b20a818fcc6dc + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 294299 + timestamp: 1728054014060 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h113e628_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda + sha256: 286b31616c191486626cb49e9ceb5920d29394b9e913c23adb7eb637629ba4de + md5: 73f73f60854f325a55f1d31459f2ab73 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 232351 + timestamp: 1728486729511 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: h47b0b28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda + sha256: 1c72423b9beba167d2f01b80dc204da77240a8266f1edb3d89510c852b300d69 + md5: 94e73a7877743a85c57091d8afab2348 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 217132 + timestamp: 1728488096615 +- kind: conda + name: azure-identity-cpp + version: 1.10.0 + build: hc602bab_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda + sha256: bde446b916fff5150606f8ed3e6058ffc55a3aa72381e46f1ab346590b1ae40a + md5: d7b71593a937459f2d4b67e1a4727dc2 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 166907 + timestamp: 1728486882502 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h185ecfd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 + md5: 221e1e5ecb2643e113f32b3229d5ba33 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 502934 + timestamp: 1728580241002 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h3cf044e_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 + md5: 7eb66060455c7a47d9dcdbfa9f46579b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 549342 + timestamp: 1728578123088 +- kind: conda + name: azure-storage-blobs-cpp + version: 12.13.0 + build: h7585a09_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + sha256: 08d52d130addc0fb55d5ba10d9fa483e39be25d69bac7f4c676c2c3069207590 + md5: 704238ef05d46144dae2e6b5853df8bc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 438636 + timestamp: 1728578216193 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h1b94036_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 + md5: 793b1080ab2d958980f137a8643cd6e8 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 140832 + timestamp: 1728565334900 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h736e048_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba + md5: 13de36be8de3ae3f05ba127631599213 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 149312 + timestamp: 1728563338704 +- kind: conda + name: azure-storage-common-cpp + version: 12.8.0 + build: h9ca1f76_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + sha256: 77ab04e8fe5636a2de9c718f72a43645f7502cd208868c8a91ffba385547d585 + md5: 7a187cd7b1445afc80253bb186a607cc + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - libcxx >=17 + - libxml2 >=2.12.7,<3.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 121278 + timestamp: 1728563418777 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: h37d6d07_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc + md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 + depends: + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 260547 + timestamp: 1728730924071 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: ha633028_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 + md5: 7c1980f89dd41b097549782121a73490 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + size: 287366 + timestamp: 1728729530295 +- kind: conda + name: azure-storage-files-datalake-cpp + version: 12.12.0 + build: hcdd55da_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + sha256: f48523f8aa0b5b80f45a92f0556b388dd96f44ac2dc2f44a01d08c1822eec97d + md5: c49fbc5233fcbaa86391162ff1adef38 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 + - libcxx >=17 + license: MIT + license_family: MIT + size: 196032 + timestamp: 1728729672889 +- kind: conda + name: backoff + version: 2.2.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 + md5: a38b801f2bcc12af80c2e02a9e4ce7d9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 18816 + timestamp: 1733771192649 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h2ec8cdc_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f + md5: b0b867af6fc74b2a0aa206da29c0f3cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + size: 349867 + timestamp: 1725267732089 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h6f74592_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda + sha256: 9736bf660a0e4260c68f81d2635b51067f817813e6490ac9e8abd9a835dcbf6d + md5: e1e9727063057168d95f27a032acd0a4 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 h86ecc28_2 + license: MIT + license_family: MIT + size: 356878 + timestamp: 1725267878508 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312hde4cb15_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda + sha256: 254b411fa78ccc226f42daf606772972466f93e9bc6895eabb4cfda22f5178af + md5: a83c2ef76ccb11bc2349f4f17696b15d + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 339360 + timestamp: 1725268143995 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h4bc722e_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 252783 + timestamp: 1720974456583 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h68df207_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda + sha256: 2258b0b33e1cb3a9852d47557984abb6e7ea58e3d7f92706ec1f8e879290c4cb + md5: 56398c28220513b9ea13d7b450acfb20 + depends: + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + size: 189884 + timestamp: 1720974504976 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h99b78c6_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 + md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 122909 + timestamp: 1720974522888 +- kind: conda + name: c-ares + version: 1.34.4 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f + md5: c1c999a38a4303b29d75c636eaa13cf9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 179496 + timestamp: 1734208291879 +- kind: conda + name: c-ares + version: 1.34.4 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + sha256: 1187a41d4bb2afe02cb18690682edc98d1e9f5e0ccda638d8704a75ea1875bbe + md5: 356da36f35d36dcba16e43f1589d4e39 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 215979 + timestamp: 1734208193181 +- kind: conda + name: c-ares + version: 1.34.4 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 + md5: e2775acf57efd5af15b8e3d1d74d72d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 206085 + timestamp: 1734208189009 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hbcca054_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + sha256: 1afd7274cbc9a334d6d0bc62fa760acc7afdaceb0b91a8df370ec01fd75dc7dd + md5: 720523eb0d6a9b0f6120c16b2aa4e7de + license: ISC + size: 157088 + timestamp: 1734208393264 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hcefe29a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda + sha256: ad7b43211051332a5a4e788bb4619a2d0ecb5be73e0f76be17f733a87d7effd1 + md5: 83b4ad1e6dc14df5891f3fcfdeb44351 + license: ISC + size: 157096 + timestamp: 1734209301744 +- kind: conda + name: ca-certificates + version: 2024.12.14 + build: hf0a4a13_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda + sha256: 256be633fd0882ccc1a7a32bc278547e1703f85082c0789a87a603ee3ab8fb82 + md5: 7cb381a6783d91902638e4ed1ebd478e + license: ISC + size: 157091 + timestamp: 1734208344343 +- kind: conda + name: certifi + version: 2024.12.14 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda + sha256: 048c16a9cbcb1fbad02083414d3bc7c1d0eea4b39aee6aa6bf8d1d5089ca8bad + md5: 6feb87357ecd66733be3279f16a8c400 + depends: + - python >=3.9 + license: ISC + size: 161642 + timestamp: 1734380604767 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h06ac9bb_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda + sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 + md5: a861504bbea4161a9170b85d4d2be840 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 294403 + timestamp: 1725560714366 +- kind: conda + name: cffi + version: 1.17.1 + build: py312h0fad829_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f + md5: 19a5456f72f505881ba493979777b24e + depends: + - __osx >=11.0 + - libffi >=3.4,<4.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 281206 + timestamp: 1725560813378 +- kind: conda + name: cffi + version: 1.17.1 + build: py312hac81daf_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + sha256: 1162e3ca039e7ca7c0e78f0a020ed1bde968096841b663e3f393c966eb82f0f0 + md5: 1a256e5581b1099e9295cb84d53db3ea + depends: + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 312892 + timestamp: 1725561779888 +- kind: conda + name: charset-normalizer + version: 3.4.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda + sha256: 63022ee2c6a157a9f980250a66f54bdcdf5abee817348d0f9a74c2441a6fbf0e + md5: 6581a17bba6b948bb60130026404a9d6 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 47533 + timestamp: 1733218182393 +- kind: conda + name: click + version: 8.1.7 + build: unix_pyh707e725_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + sha256: 1cd5fc6ccdd5141378e51252a7a3810b07fd5a7e6934a5b4a7eccba66566224b + md5: cb8e52f28f5e592598190c562e7b5bf1 + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 84513 + timestamp: 1733221925078 +- kind: conda + name: colorama + version: 0.4.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- kind: conda + name: datasets + version: 2.14.4 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda + sha256: 7e09bd083a609138b780fcc4535924cb96814d2c908a36d4c64a2ba9ee3efe7f + md5: 3e087f072ce03c43a9b60522f5d0ca2f + depends: + - aiohttp + - dill >=0.3.0,<0.3.8 + - fsspec >=2021.11.1 + - huggingface_hub >=0.14.0,<1.0.0 + - importlib-metadata + - multiprocess + - numpy >=1.17 + - packaging + - pandas + - pyarrow >=8.0.0 + - python >=3.8.0 + - python-xxhash + - pyyaml >=5.1 + - requests >=2.19.0 + - tqdm >=4.62.1 + license: Apache-2.0 + license_family: Apache + size: 347303 + timestamp: 1691593908658 +- kind: conda + name: deprecated + version: 1.2.15 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda + sha256: a20ebf2c9b02a6eb32412ceb5c4cffaae49417db7e75414a76417538293a9402 + md5: eaef2e94d5bd76f758545d172c1fda67 + depends: + - python >=3.9 + - wrapt <2,>=1.10 + license: MIT + license_family: MIT + size: 14297 + timestamp: 1733662697343 +- kind: conda + name: dill + version: 0.3.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda + sha256: 4ff20c6be028be2825235631c45d9e4a75bca1de65f8840c02dfb28ea0137c45 + md5: 5e4f3466526c52bc9af2d2353a1460bd + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + size: 87553 + timestamp: 1690101185422 +- kind: conda + name: dnspython + version: 2.7.0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 + md5: 5fbd60d61d21b4bd2f9d7a48fe100418 + depends: + - python >=3.9,<4.0.0 + - sniffio + constrains: + - aioquic >=1.0.0 + - wmi >=1.5.1 + - httpx >=0.26.0 + - trio >=0.23 + - cryptography >=43 + - httpcore >=1.0.0 + - idna >=3.7 + - h2 >=4.1.0 + license: ISC + license_family: OTHER + size: 172172 + timestamp: 1733256829961 +- kind: conda + name: email-validator + version: 2.2.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + sha256: b91a19eb78edfc2dbb36de9a67f74ee2416f1b5273dd7327abe53f2dbf864736 + md5: da16dd3b0b71339060cd44cb7110ddf9 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.9 + license: Unlicense + size: 44401 + timestamp: 1733300827551 +- kind: conda + name: email_validator + version: 2.2.0 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + sha256: e0d0fdf587aa0ed0ff08b2bce3ab355f46687b87b0775bfba01cc80a859ee6a2 + md5: 0794f8807ff2c6f020422cacb1bd7bfa + depends: + - email-validator >=2.2.0,<2.2.1.0a0 + license: Unlicense + size: 6552 + timestamp: 1733300828176 +- kind: conda + name: exceptiongroup + version: 1.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda + sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 + md5: a16662747cdeb9abbac74d0057cc976e + depends: + - python >=3.9 + license: MIT and PSF-2.0 + size: 20486 + timestamp: 1733208916977 +- kind: conda + name: fastapi + version: 0.115.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + sha256: d7826d537c667093c9de96411a09585a8d620c84a830a0195e58e9a0df45f018 + md5: 1b1e0c97830cdf75f1f371bd467ab657 + depends: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.5 + - httpx >=0.23.0 + - jinja2 >=2.11.2 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python >=3.9 + - python-multipart >=0.0.7 + - starlette >=0.40.0,<0.42.0 + - typing_extensions >=4.8.0 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + size: 73084 + timestamp: 1733362427885 +- kind: conda + name: fastapi-cli + version: 0.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 + md5: d960e0ea9e1c561aa928f6c4439f04c7 + depends: + - python >=3.9 + - rich-toolkit >=0.11.1 + - typer >=0.12.3 + - uvicorn-standard >=0.15.0 + license: MIT + license_family: MIT + size: 15546 + timestamp: 1734302408607 +- kind: conda + name: filelock + version: 3.16.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + sha256: 18dca6e2194732df7ebf824abaefe999e4765ebe8e8a061269406ab88fc418b9 + md5: d692e9ba6f92dc51484bf3477e36ce7c + depends: + - python >=3.9 + license: Unlicense + size: 17441 + timestamp: 1733240909987 +- kind: conda + name: freetype + version: 2.12.1 + build: h267a509_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + sha256: b2e3c449ec9d907dd4656cb0dc93e140f447175b125a3824b31368b06c666bb6 + md5: 9ae35c3d96db2c94ce0cef86efdfa2cb + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 634972 + timestamp: 1694615932610 +- kind: conda + name: freetype + version: 2.12.1 + build: hadb7bae_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 + md5: e6085e516a3e304ce41a8ee08b9b89ad + depends: + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 596430 + timestamp: 1694616332835 +- kind: conda + name: freetype + version: 2.12.1 + build: hf0a5ef3_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda + sha256: 7af93030f4407f076dce181062360efac2cd54dce863b5d7765287a6f5382537 + md5: a5ab74c5bd158c3d5532b66d8d83d907 + depends: + - libgcc-ng >=12 + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 642092 + timestamp: 1694617858496 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + sha256: 44d6d6b332421e621c029fb149f12dba1ccb5ed6ac632e2e807a9d92d6cb2864 + md5: 7960352935cc95ac23883c9b8c97f2ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 53366 + timestamp: 1729699762631 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + sha256: 7e0c12983b20f2816b3712729b5a35ecb7ee152132ca7cf805427c62395ea823 + md5: f98e36c96b2c66d9043187179ddb04f4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 60968 + timestamp: 1729699568442 +- kind: conda + name: frozenlist + version: 1.5.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda + sha256: b0a9ff3e71452eed70877b2f3175d41cd85070da6deac381c5f3f61e1f19bccb + md5: 62fc11b0738ca15e0dd19b60cf280d12 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 59967 + timestamp: 1729699642726 +- kind: conda + name: fsspec + version: 2024.10.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + sha256: 790a50b4f94042951518f911a914a886a837c926094c6a14ed1d9d03ce336807 + md5: 906fe13095e734cb413b57a49116cdc8 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 134726 + timestamp: 1733493445080 +- kind: conda + name: gflags + version: 2.2.2 + build: h5888daf_1005 + build_number: 1005 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 119654 + timestamp: 1726600001928 +- kind: conda + name: gflags + version: 2.2.2 + build: h5ad3122_1005 + build_number: 1005 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda + sha256: 28fe6b40b20454106d5e4ef6947cf298c13cc72a46347bbc49b563cd3a463bfa + md5: 4ff634d515abbf664774b5e1168a9744 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 106638 + timestamp: 1726599967617 +- kind: conda + name: gflags + version: 2.2.2 + build: hf9b8971_1005 + build_number: 1005 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + size: 82090 + timestamp: 1726600145480 +- kind: conda + name: glog + version: 0.7.1 + build: h468a4a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 + md5: 08940a32c6ced3703d1412dd37df4f62 + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 145811 + timestamp: 1718284208668 +- kind: conda + name: glog + version: 0.7.1 + build: hbabe93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 143452 + timestamp: 1718284177264 +- kind: conda + name: glog + version: 0.7.1 + build: heb240a5_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + size: 112215 + timestamp: 1718284365403 +- kind: conda + name: googleapis-common-protos + version: 1.66.0 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda + sha256: d8d19575a827f2c62500949b9536efdd6b5406c9f546a73b6a87ac90b03a5875 + md5: 4861e30ff0cd566ea6fb4593e3b7c22a + depends: + - protobuf >=3.20.2,<6.0.0.dev0,!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 116522 + timestamp: 1731459019854 +- kind: conda + name: h11 + version: 0.14.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 + md5: 7ee49e89531c0dcbba9466f6d115d585 + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 51846 + timestamp: 1733327599467 +- kind: conda + name: h2 + version: 4.1.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda + sha256: 843ddad410c370672a8250470697027618f104153612439076d4d7b91eeb7b5c + md5: 825927dc7b0f287ef8d4d0011bb113b1 + depends: + - hpack >=4.0,<5 + - hyperframe >=6.0,<7 + - python >=3.9 + license: MIT + license_family: MIT + size: 52000 + timestamp: 1733298867359 +- kind: conda + name: hpack + version: 4.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + sha256: ec89b7e5b8aa2f0219f666084446e1fb7b54545861e9caa892acb24d125761b5 + md5: 2aa5ff7fa34a81b9196532c84c10d865 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 29412 + timestamp: 1733299296857 +- kind: conda + name: httpcore + version: 1.0.7 + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df + md5: 2ca8e6dbc86525c8b95e3c0ffa26442e + depends: + - python >=3.8 + - h11 >=0.13,<0.15 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=3.0,<5.0 + - certifi + license: BSD-3-Clause + license_family: BSD + size: 48959 + timestamp: 1731707562362 +- kind: conda + name: httptools + version: 0.6.4 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + sha256: 621e7e050b888e5239d33e37ea72d6419f8367e5babcad38b755586f20264796 + md5: 8b1160b32557290b64d5be68db3d996d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101872 + timestamp: 1732707756745 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda + sha256: 0bd1f30224af142711d11033a7469ae402a1147143f399f7341bbc1d8178c722 + md5: 5e70a6de59352f9a52e9caa7f3447390 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 101255 + timestamp: 1732707891645 +- kind: conda + name: httptools + version: 0.6.4 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda + sha256: 5e93cda79e32e8c0039e05ea1939e688da336187dab025f699b42ef529e848be + md5: e1747a8e8d2aca5499aaea9993bf31ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 85623 + timestamp: 1732707871414 +- kind: conda + name: httpx + version: 0.28.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- kind: conda + name: huggingface_hub + version: 0.26.5 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda + sha256: 0c75532d914a04c73222be298ed2c6868739dd475b1b1a9137c52abe79873952 + md5: 73937038e21117fe401f8ea64fbaeacc + depends: + - filelock + - fsspec >=2023.5.0 + - packaging >=20.9 + - python >=3.9 + - pyyaml >=5.1 + - requests + - tqdm >=4.42.1 + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + size: 275466 + timestamp: 1733852454004 +- kind: conda + name: hyperframe + version: 6.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + sha256: e91c6ef09d076e1d9a02819cd00fa7ee18ecf30cdd667605c853980216584d1b + md5: 566e75c90c1d0c8c459eb0ad9833dc7a + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17239 + timestamp: 1733298862681 +- kind: conda + name: icu + version: '75.1' + build: hf9b3779_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 + md5: 268203e8b983fddb6412b36f2024e75c + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12282786 + timestamp: 1720853454991 +- kind: conda + name: icu + version: '75.1' + build: hfee45f7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 11857802 + timestamp: 1720853997952 +- kind: conda + name: idna + version: '3.10' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 + md5: 39a4f67be3286c86d696df570b1201b7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 49765 + timestamp: 1733211921194 +- kind: conda + name: importlib-metadata + version: 8.5.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 + md5: 315607a3030ad5d5227e76e0733798ff + depends: + - python >=3.9 + - zipp >=0.5 + license: Apache-2.0 + license_family: APACHE + size: 28623 + timestamp: 1733223207185 +- kind: conda + name: jinja2 + version: 3.1.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + sha256: 85a7169c078b8065bd9d121b0e7b99c8b88c42a411314b6ae5fcd81c48c4710a + md5: 08cce3151bde4ecad7885bd9fb647532 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110963 + timestamp: 1733217424408 +- kind: conda + name: jupyter_client + version: 8.6.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a + md5: 4ebae00eae9705b0c3d6d1018a81d047 + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106342 + timestamp: 1733441040958 +- kind: conda + name: jupyter_core + version: 5.7.2 + build: pyh31011fe_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda + sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd + md5: 0a2980dada0dd7fd0998f0342308b1b1 + depends: + - __unix + - platformdirs >=2.5 + - python >=3.8 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 57671 + timestamp: 1727163547058 +- kind: conda + name: keyutils + version: 1.6.1 + build: h166bdaf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 + sha256: 150c05a6e538610ca7c43beb3a40d65c90537497a4f6a5f4d15ec0451b6f5ebb + md5: 30186d27e2c9fa62b45fb1476b7200e3 + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 117831 + timestamp: 1646151697040 +- kind: conda + name: keyutils + version: 1.6.1 + build: h4e544f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 + sha256: 6d4233d97a9b38acbb26e1268bcf8c10a8e79c2aed7e5a385ec3769967e3e65b + md5: 1f24853e59c68892452ef94ddd8afd4b + depends: + - libgcc-ng >=10.3.0 + license: LGPL-2.1-or-later + size: 112327 + timestamp: 1646166857935 +- kind: conda + name: krb5 + version: 1.21.3 + build: h237132a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- kind: conda + name: krb5 + version: 1.21.3 + build: h50a48e9_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 + md5: 29c10432a2ca1472b53f299ffb2ffa37 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1474620 + timestamp: 1719463205834 +- kind: conda + name: krb5 + version: 1.21.3 + build: h659f571_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1370023 + timestamp: 1719463201255 +- kind: conda + name: lcms2 + version: '2.16' + build: h922389a_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + sha256: be4847b1014d3cbbc524a53bdbf66182f86125775020563e11d914c8468dd97d + md5: ffdd8267a04c515e7ce69c727b051414 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 296219 + timestamp: 1701647961116 +- kind: conda + name: lcms2 + version: '2.16' + build: ha0e7c42_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda + sha256: 151e0c84feb7e0747fabcc85006b8973b22f5abbc3af76a9add0b0ef0320ebe4 + md5: 66f6c134e76fe13cce8a9ea5814b5dd5 + depends: + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 211959 + timestamp: 1701647962657 +- kind: conda + name: lcms2 + version: '2.16' + build: hb7c19ff_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 + md5: 51bb7010fc86f70eee639b4bb7a894f5 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 245247 + timestamp: 1701647787198 +- kind: conda + name: ld_impl_linux-64 + version: '2.43' + build: h712a8e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda + sha256: 7c91cea91b13f4314d125d1bedb9d03a29ebbd5080ccdea70260363424646dbe + md5: 048b02e3962f066da18efe3a21b77672 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - binutils_impl_linux-64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 669211 + timestamp: 1729655358674 +- kind: conda + name: ld_impl_linux-aarch64 + version: '2.43' + build: h80caac9_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda + sha256: 80ec7e8f006196808fac5bd4b3773a652847f97bbf08044cd87731424ac64f8b + md5: fcbde5ea19d55468953bf588770c0501 + constrains: + - binutils_impl_linux-aarch64 2.43 + license: GPL-3.0-only + license_family: GPL + size: 698245 + timestamp: 1729655345825 +- kind: conda + name: lerc + version: 4.0.0 + build: h27087fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 + sha256: cb55f36dcd898203927133280ae1dc643368af041a48bcf7c026acb7c47b0c12 + md5: 76bbff344f0134279f225174e9064c8f + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 281798 + timestamp: 1657977462600 +- kind: conda + name: lerc + version: 4.0.0 + build: h4de3ea5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 + sha256: 2d09ef9b7796d83364957e420b41c32d94e628c3f0520b61c332518a7b5cd586 + md5: 1a0ffc65e03ce81559dbcb0695ad1476 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: Apache-2.0 + license_family: Apache + size: 262096 + timestamp: 1657978241894 +- kind: conda + name: lerc + version: 4.0.0 + build: h9a09cb3_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 + sha256: 6f068bb53dfb6147d3147d981bb851bb5477e769407ad4e6a68edf482fdcb958 + md5: de462d5aacda3b30721b512c5da4e742 + depends: + - libcxx >=13.0.1 + license: Apache-2.0 + license_family: Apache + size: 215721 + timestamp: 1657977558796 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda + sha256: 8f91429091183c26950f1e7ffa730e8632f0627ba35d2fccd71df31628c9b4e5 + md5: e1f604644fe8d78e22660e2fec6756bc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1310521 + timestamp: 1727295454064 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda + sha256: 590e47dce38031a8893e70491f3b71e214de7781cab53b6f017aa6f6841cb076 + md5: 6fe6b3694c4792a8e26755d3b06f0b80 + depends: + - libgcc >=13 + - libstdcxx >=13 + constrains: + - abseil-cpp =20240722.0 + - libabseil-static =20240722.0=cxx17* + license: Apache-2.0 + license_family: Apache + size: 1328502 + timestamp: 1727295490806 +- kind: conda + name: libabseil + version: '20240722.0' + build: cxx17_hf9b8971_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda + sha256: 90bf08a75506dfcf28a70977da8ab050bcf594cd02abd3a9d84a22c9e8161724 + md5: 706da5e791c569a7b9814877098a6a0a + depends: + - __osx >=11.0 + - libcxx >=17 + constrains: + - libabseil-static =20240722.0=cxx17* + - abseil-cpp =20240722.0 + license: Apache-2.0 + license_family: Apache + size: 1179072 + timestamp: 1727295571173 +- kind: conda + name: libarrow + version: 18.1.0 + build: h1b535d6_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda + sha256: 087b579aebf351ca41c54214121d86a15a41c92051cbd432d6f3a3f58a8c31b0 + md5: 4c0ad68efba1113ac5833975c67b565d + depends: + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8040629 + timestamp: 1733810319239 +- kind: conda + name: libarrow + version: 18.1.0 + build: h44a453e_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda + sha256: abf17e99b03356a9d6248e965826c1352ff01b00d3a62cc51393bb0744d72803 + md5: 2cf6d608d6e66506f69797d5c6944c35 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - gflags >=2.2.2,<2.3.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libgcc >=13 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 8786061 + timestamp: 1733810643966 +- kind: conda + name: libarrow + version: 18.1.0 + build: h4a2f8bd_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda + sha256: 9ed3ea1bc15005c0df187268ef91407afaa908cf82f36f5acbbf50ac24d7f806 + md5: 835cdd84195b84dc34d128bd5d3580b9 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 + - azure-core-cpp >=1.14.0,<1.14.1.0a0 + - azure-identity-cpp >=1.10.0,<1.10.1.0a0 + - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 + - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libbrotlidec >=1.1.0,<1.2.0a0 + - libbrotlienc >=1.1.0,<1.2.0a0 + - libcxx >=18 + - libgoogle-cloud >=2.32.0,<2.33.0a0 + - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libre2-11 >=2024.7.2 + - libutf8proc >=2.9.0,<2.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.0.3,<2.0.4.0a0 + - re2 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 5494797 + timestamp: 1733808145854 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda + sha256: fdb70e2499e59b730084ecd53008b361a6f6090b5fb49624feda06b7e84c7b8c + md5: c50907eefe2ae22d826e7cb2e4d712f5 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 578091 + timestamp: 1733810378092 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda + sha256: a32fa1d71415afc02b5cf3cd4c0a6ec0af9e749308829cc65ff79689222ce479 + md5: 143f9288b64759a6427563f058c62f2b + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 611745 + timestamp: 1733810698469 +- kind: conda + name: libarrow-acero + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda + sha256: e1cae46409927470439ef9ae93ed09b3493d0579501ca9ebfa79ded212ee98d8 + md5: 97fc01254714e1572624baefdd7cc898 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + license: Apache-2.0 + license_family: APACHE + size: 483713 + timestamp: 1733808246880 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: h3b568fd_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda + sha256: 2a08f5a1017ff660c37ae0c24343a119cb2511c6edd69e23d0a5090a0967ea35 + md5: bb1548ad011c4f9107fcc4cc548473bf + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libparquet 18.1.0 hfc78867_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 559673 + timestamp: 1733810461646 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hcb10f89_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda + sha256: 74eeb178070002842d3ed721769399320e3a68a0843319eaf899a092a31def26 + md5: 20ca46a6bc714a6ab189d5b3f46e66d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libparquet 18.1.0 h081d1f1_6_cpu + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 586627 + timestamp: 1733810842604 +- kind: conda + name: libarrow-dataset + version: 18.1.0 + build: hf07054f_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda + sha256: 6eba942ce926419f74e6e0a7c3994a7d78ab6be47115e6bb70e02136554736be + md5: 0774276be6659aaa0007f1b0f6ee19b0 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libparquet 18.1.0 h636d7b7_6_cpu + license: Apache-2.0 + license_family: APACHE + size: 489948 + timestamp: 1733809328231 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ee7192_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + sha256: bda6728db019dd0c409b1996ad9ef6ab0bcee3a94dc66a8045e8c1049c566055 + md5: aa313b3168caf98d00b3753f5ba27650 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libarrow-dataset 18.1.0 hcb10f89_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 519989 + timestamp: 1733810903274 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h3ffb4b1_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + sha256: 9f78c55c5d7122e588a6f226cbf7e909c479d66ed18edc633d68324323d386b9 + md5: 5db2e6832397b8ca70a6f7b00e0c3629 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h1b535d6_6_cpu + - libarrow-acero 18.1.0 h3b568fd_6_cpu + - libarrow-dataset 18.1.0 h3b568fd_6_cpu + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + size: 515928 + timestamp: 1733810503359 +- kind: conda + name: libarrow-substrait + version: 18.1.0 + build: h86344ea_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + sha256: bafd9ca59ebb5ad34b77aff316ef7b59c5fb1eb8a7b6a15de8dcbdf3ce37556d + md5: c1c162f5bf569cff8bed6def705a899f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow-acero 18.1.0 hf07054f_6_cpu + - libarrow-dataset 18.1.0 hf07054f_6_cpu + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 451623 + timestamp: 1733809487176 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda + sha256: 30bd658682b124243f8e52d8edf8a19e7be1bc31e4fe4baec30a64002dc8cd0c + md5: ac52800af2e0c0e7dac770b435ce768a + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16393 + timestamp: 1734432564346 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: df6d8ee34d45cf35609ecdd55c1ff03e32e0cd87ae41ebe4ef3747a8e09ead4d + md5: 8d900b7079a00969d70305e9aad550b7 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16477 + timestamp: 1734432576699 +- kind: conda + name: libblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda + sha256: 597f9c3779caa979c8c6abbb3ba8c7191b84e1a910d6b0d10e5faf35284c450c + md5: 21be102c9ae80a67ba7de23b129aa7f6 + depends: + - libopenblas >=0.3.28,<0.3.29.0a0 + - libopenblas >=0.3.28,<1.0a0 + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16714 + timestamp: 1734433054681 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 + md5: 3ee026955c688f551a9999840cff4c67 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 68982 + timestamp: 1725267774142 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda + sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 + md5: 41b599ed2b02abcfdd84302bff174b23 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 68851 + timestamp: 1725267660471 +- kind: conda + name: libbrotlicommon + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda + sha256: 839dacb741bdbb25e58f42088a2001b649f4f12195aeb700b5ddfca3267749e5 + md5: d0bf1dff146b799b319ea0434b93f779 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 68426 + timestamp: 1725267943211 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c + md5: e64d0f3b59c7c4047446b97a8624a72d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 31708 + timestamp: 1725267783442 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 32696 + timestamp: 1725267669305 +- kind: conda + name: libbrotlidec + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda + sha256: 6c6862eb274f21a7c0b60e5345467a12e6dda8b9af4438c66d496a2c1a538264 + md5: 55e66e68ce55523a6811633dd1ac74e2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 28378 + timestamp: 1725267980316 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c + md5: 0e9bd365480c72b25c71a448257b537d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 290230 + timestamp: 1725267792697 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda + sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 + md5: 06f70867945ea6a84d35836af780f1de + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.1.0 hb9d3cd8_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 281750 + timestamp: 1725267679782 +- kind: conda + name: libbrotlienc + version: 1.1.0 + build: hd74edd7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda + sha256: eeb1eb0d58b9d02bc1b98dc0a058f104ab168eb2f7d1c7bfa0570a12cfcdb7b7 + md5: 4f3a434504c67b2c42565c0b85c1885c + depends: + - __osx >=11.0 + - libbrotlicommon 1.1.0 hd74edd7_2 + license: MIT + license_family: MIT + size: 279644 + timestamp: 1725268003553 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda + sha256: 9c74e536c9bc868e356ffd43f81c2cb398aec84b40fcadc312315b164a5500ee + md5: ebcc5f37a435aa3c19640533c82f8d76 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - liblapack 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16336 + timestamp: 1734432570482 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda + sha256: 521e78be0c4170f229c43e1a6c94337a72db3ebcbe6e5960f8413aa438dcb8f9 + md5: d77f943ae4083f3aeddca698f2d28262 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - liblapack 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16398 + timestamp: 1734432580937 +- kind: conda + name: libcblas + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda + sha256: 27a29ef6b2fd2179bc3a0bb9db351f078ba140ca10485dca147c399639f84c93 + md5: a0e9980fe12d42f6d0c0ec009f67e948 + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapack 3.9.0 26_osxarm64_openblas + - liblapacke 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16628 + timestamp: 1734433061517 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h01db608_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea + md5: 268ee639c17ada0002fb04dd21816cc2 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 18669 + timestamp: 1633683724891 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: h9c3ff4c_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 20440 + timestamp: 1633683576494 +- kind: conda + name: libcrc32c + version: 1.1.2 + build: hbdafb3b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + size: 18765 + timestamp: 1633683992603 +- kind: conda + name: libcurl + version: 8.11.1 + build: h332b0f4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda + sha256: 3cd4075b2a7b5562e46c8ec626f6f9ca57aeecaa94ff7df57eca26daa94c9906 + md5: 2b3e0081006dc21e8bf53a91c83a055c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 423011 + timestamp: 1733999897624 +- kind: conda + name: libcurl + version: 8.11.1 + build: h6702fde_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda + sha256: 9fc65d21a58f4aad1bc39dfb94a178893aeb035850c5cf0ed9736674279f390b + md5: 7dec1cd271c403d1636bda5aa388a55d + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 440737 + timestamp: 1733999835504 +- kind: conda + name: libcurl + version: 8.11.1 + build: h73640d1_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda + sha256: f47c35938144c23278987c7d12096f6a42d7c850ffc277222b032073412383b6 + md5: 46d7524cabfdd199bffe63f8f19a552b + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.64.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: curl + license_family: MIT + size: 385098 + timestamp: 1734000160270 +- kind: conda + name: libcxx + version: 19.1.5 + build: ha82da77_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + sha256: 7918cc0bb7a6554cdd3eee634c3dc414a1ab8ec49faeca1567367bb92118f9d7 + md5: 3c7be0df28ccda1d193ea6de56dcb5ff + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 519819 + timestamp: 1733291654212 +- kind: conda + name: libdeflate + version: '1.23' + build: h4ddbbb0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + sha256: 511d801626d02f4247a04fff957cc6e9ec4cc7e8622bd9acd076bcdc5de5fe66 + md5: 8dfae1d2e74767e9ce36d5fa0d8605db + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + size: 72255 + timestamp: 1734373823254 +- kind: conda + name: libdeflate + version: '1.23' + build: h5e3c512_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda + sha256: 959419d87cd2b789a9055db95704c614f31aeb70bef7949fa2f734122a3a2863 + md5: 7e7ca2607b11b180120cefc2354fc0cb + depends: + - libgcc >=13 + license: MIT + size: 69862 + timestamp: 1734373858306 +- kind: conda + name: libdeflate + version: '1.23' + build: hec38601_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda + sha256: 887c02deaed6d583459eba6367023e36d8761085b2f7126e389424f57155da53 + md5: 1d8b9588be14e71df38c525767a1ac30 + depends: + - __osx >=11.0 + license: MIT + size: 54132 + timestamp: 1734373971372 +- kind: conda + name: libedit + version: 3.1.20191231 + build: hc8eb9b7_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca + md5: 30e4362988a2623e9eb34337b83e01f9 + depends: + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 96607 + timestamp: 1597616630749 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: a57d37c236d8f7c886e01656f4949d9dcca131d2a0728609c6f7fa338b65f1cf + md5: 4d331e44109e3f0e19b4cb8f9b82f3e1 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 123878 + timestamp: 1597616541093 +- kind: conda + name: libedit + version: 3.1.20191231 + build: he28a2e2_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + sha256: debc31fb2f07ba2b0363f90e455873670734082822926ba4a9556431ec0bf36d + md5: 29371161d77933a54fccf1bb66b96529 + depends: + - libgcc-ng >=7.5.0 + - ncurses >=6.2,<7.0.0a0 + license: BSD-2-Clause + license_family: BSD + size: 134104 + timestamp: 1597617110769 +- kind: conda + name: libev + version: '4.33' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + sha256: 973af77e297f1955dd1f69c2cbdc5ab9dfc88388a5576cd152cda178af0fd006 + md5: a9a13cb143bbaa477b1ebaefbe47a302 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 115123 + timestamp: 1702146237623 +- kind: conda + name: libev + version: '4.33' + build: h93a5062_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + size: 107458 + timestamp: 1702146414478 +- kind: conda + name: libev + version: '4.33' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 112766 + timestamp: 1702146165126 +- kind: conda + name: libevent + version: 2.1.12 + build: h2757513_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 368167 + timestamp: 1685726248899 +- kind: conda + name: libevent + version: 2.1.12 + build: h4ba1bb4_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + sha256: 01333cc7d6e6985dd5700b43660d90e9e58049182017fd24862088ecbe1458e4 + md5: 96ae6083cd1ac9f6bc81631ac835b317 + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 438992 + timestamp: 1685726046519 +- kind: conda + name: libevent + version: 2.1.12 + build: hf998b51_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 427426 + timestamp: 1685725977222 +- kind: conda + name: libexpat + version: 2.6.4 + build: h286801f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 + md5: 38d2656dd914feb0cab8c629370768bf + depends: + - __osx >=11.0 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 64693 + timestamp: 1730967175868 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5888daf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 + md5: db833e03127376d461e1e13e76f09b6c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 73304 + timestamp: 1730967041968 +- kind: conda + name: libexpat + version: 2.6.4 + build: h5ad3122_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda + sha256: f42e758009ba9db90d1fe7992bc3e60d0c52f71fb20923375d2c44ae69a5a2b3 + md5: f1b3fab36861b3ce945a13f0dfdfc688 + depends: + - libgcc >=13 + constrains: + - expat 2.6.4.* + license: MIT + license_family: MIT + size: 72345 + timestamp: 1730967203789 +- kind: conda + name: libffi + version: 3.4.2 + build: h3422bc3_5 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 + license: MIT + license_family: MIT + size: 39020 + timestamp: 1636488587153 +- kind: conda + name: libffi + version: 3.4.2 + build: h3557bc0_5 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + sha256: 7e9258a102480757fe3faeb225a3ca04dffd10fecd2a958c65cdb4cdf75f2c3c + md5: dddd85f4d52121fab0a8b099c5e06501 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 59450 + timestamp: 1636488255090 +- kind: conda + name: libffi + version: 3.4.2 + build: h7f98852_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 58292 + timestamp: 1636488182923 +- kind: conda + name: libgcc + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + sha256: 53eb8a79365e58849e7b1a068d31f4f9e718dc938d6f2c03e960345739a03569 + md5: 3cb76c3f10d3bc7f1105b2fc9db984df + depends: + - _libgcc_mutex 0.1 conda_forge + - _openmp_mutex >=4.5 + constrains: + - libgomp 14.2.0 h77fa898_1 + - libgcc-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 848745 + timestamp: 1729027721139 +- kind: conda + name: libgcc + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda + sha256: 5d56757ccad208c79214395b00d006d8d18929a4ba49c47bd9460789a7620943 + md5: 511b511c5445e324066c3377481bcab8 + depends: + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==14.2.0=*_1 + - libgomp 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 535243 + timestamp: 1729089435134 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda + sha256: 3a76969c80e9af8b6e7a55090088bc41da4cffcde9e2c71b17f44d37b7cb87f7 + md5: e39480b9ca41323497b05492a63bc35b + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54142 + timestamp: 1729027726517 +- kind: conda + name: libgcc-ng + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda + sha256: 9b5cf168a6c7361cae869cb74b716766ee7c6d6b3f6172b32ba9bf91135efdc4 + md5: 0694c249c61469f2c0f7e2990782af21 + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54104 + timestamp: 1729089444587 +- kind: conda + name: libgfortran + version: 5.0.0 + build: 13_2_0_hd922786_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf + depends: + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- kind: conda + name: libgfortran + version: 14.2.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda + sha256: fc9e7f22a17faf74da904ebfc4d88699013d2992e55505e4aa0eb01770290977 + md5: f1fd30127802683586f768875127a987 + depends: + - libgfortran5 14.2.0 hd5240d6_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 53997 + timestamp: 1729027752995 +- kind: conda + name: libgfortran + version: 14.2.0 + build: he9431aa_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda + sha256: cb66e411fa32a5c6040f4e5e2a63c00897aae4c3133a9c004c2e929ccf19575b + md5: 0294b92d2f47a240bebb1e3336b495f1 + depends: + - libgfortran5 14.2.0 hb6113d0_1 + constrains: + - libgfortran-ng ==14.2.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729089471124 +- kind: conda + name: libgfortran5 + version: 13.2.0 + build: hf226fd6_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 5.0.0 13_2_0_*_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 997381 + timestamp: 1707330687590 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hb6113d0_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + sha256: a87ff46d19916403cbf68cf1d785bf56b4d1ab7b2552468d2ea775d70782493f + md5: fc068e11b10e18f184e027782baa12b6 + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1102158 + timestamp: 1729089452640 +- kind: conda + name: libgfortran5 + version: 14.2.0 + build: hd5240d6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d + md5: 9822b874ea29af082e5d36098d25427d + depends: + - libgcc >=14.2.0 + constrains: + - libgfortran 14.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1462645 + timestamp: 1729027735353 +- kind: conda + name: libgomp + version: 14.2.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + sha256: 1911c29975ec99b6b906904040c855772ccb265a1c79d5d75c8ceec4ed89cd63 + md5: cc3573974587f12dda90d96e3e55a702 + depends: + - _libgcc_mutex 0.1 conda_forge + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 460992 + timestamp: 1729027639220 +- kind: conda + name: libgomp + version: 14.2.0 + build: he277a41_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda + sha256: 5aa53874a5e57a00f2e0c2e2910684eb674429cd5fcb803619b226a73e89aedf + md5: 376f0e73abbda6d23c0cb749adc195ef + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 463521 + timestamp: 1729089357313 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h3888205_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda + sha256: 36af2844ce8fafd477214d51117746144461132f76759a7d29963b4583b577be + md5: a40b948bf4eabcc1ce708c40ffd7c06d + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1248560 + timestamp: 1733512309504 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h804f50b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda + sha256: 126856add750013390dff664a3c3cd0f6f0cbbc683b0025a7ce9d1618968bc70 + md5: 3d96df4d6b1c88455e05b94ce8a14a53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1249557 + timestamp: 1733512191906 +- kind: conda + name: libgoogle-cloud + version: 2.32.0 + build: h8d8be31_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda + sha256: 722e49dbdc4486105d9f5b79a7ba4f9064602fe20c4015e97684c898ab8d3386 + md5: d7ab9e0eb7d55eac4943913073de61d7 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libgrpc >=1.67.1,<1.68.0a0 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - openssl >=3.4.0,<4.0a0 + constrains: + - libgoogle-cloud 2.32.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 876210 + timestamp: 1733512539476 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h0121fbd_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda + sha256: d1b53d17df38b52a4bc6d1fe6af0e611d6480ce10b0af570c84bd38c8aa83b91 + md5: 877a5ec0431a5af83bf0cd0522bfe661 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h804f50b_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 782108 + timestamp: 1733512329104 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: h7081f7f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda + sha256: 609df2cf376ba66460f40143f835fc567cae4458df80705587cd2efd59c09bf1 + md5: 28f5ab5cf95170dfacd05d2bb301e573 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=18 + - libgoogle-cloud 2.32.0 h8d8be31_0 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 526895 + timestamp: 1733513644846 +- kind: conda + name: libgoogle-cloud-storage + version: 2.32.0 + build: hb9b2b65_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda + sha256: e120e7b6c9c9d25baa8ae903106babdd3c969523ae25278a615ed9de4bd0fc35 + md5: 925ab0ca33baca4fcfee585cecb94169 + depends: + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=13 + - libgoogle-cloud 2.32.0 h3888205_0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 737964 + timestamp: 1733512457785 +- kind: conda + name: libgrpc + version: 1.67.1 + build: h36c5df4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + sha256: 1f6673d9d866048c9cf28fd56e6874ffc7e2c53c47d7071cb367d5fc2dde16a7 + md5: b946137e362e98a55a77fdf0b20a7739 + depends: + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7131846 + timestamp: 1730236305327 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc2c308b_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + sha256: 870550c1faf524e9a695262cd4c31441b18ad542f16893bd3c5dbc93106705f7 + md5: 4606a4647bfe857e3cfe21ca12ac3afb + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 7362336 + timestamp: 1730236333879 +- kind: conda + name: libgrpc + version: 1.67.1 + build: hc70892a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + sha256: d2393fcd3c3584e5d58da4122f48bcf297567d2f6f14b3d1fcbd34fdd5040694 + md5: 624e27571fde34f8acc2afec840ac435 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libre2-11 >=2024.7.2 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.67.1 + license: Apache-2.0 + license_family: APACHE + size: 4882208 + timestamp: 1730236299095 +- kind: conda + name: libiconv + version: '1.17' + build: h0d3ecfb_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 + md5: 69bda57310071cf6d2b86caf11573d2d + license: LGPL-2.1-only + size: 676469 + timestamp: 1702682458114 +- kind: conda + name: libiconv + version: '1.17' + build: h31becfc_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + sha256: a30e09d089cb75a0d5b8e5c354694c1317da98261185ed65aa3793e741060614 + md5: 9a8eb13f14de7d761555a98712e6df65 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705787 + timestamp: 1702684557134 +- kind: conda + name: libiconv + version: '1.17' + build: hd590300_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 + md5: d66573916ffcf376178462f1b61c941e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + size: 705775 + timestamp: 1702682170569 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + sha256: 675bc1f2a8581cd34a86c412663ec29c5f90c1d9f8d11866aa1ade5cdbdf8429 + md5: ed24e702928be089d9ba3f05618515c6 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 647126 + timestamp: 1694475003570 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hb547adb_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda + sha256: a42054eaa38e84fc1e5ab443facac4bbc9d1b6b6f23f54b7bf4f1eb687e1d993 + md5: 3ff1e053dc3a2b8e36b9bfa4256a58d1 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 547541 + timestamp: 1694475104253 +- kind: conda + name: libjpeg-turbo + version: 3.0.0 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f + md5: ea25936bb4080d843790b586850f82b8 + depends: + - libgcc-ng >=12 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 618575 + timestamp: 1694474974816 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linux64_openblas + build_number: 26 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda + sha256: b76458c36331376911e0f98fa68109e02f4d5e5ebfffa79587ac69cef748bba1 + md5: 3792604c43695d6a273bc5faaac47d48 + depends: + - libblas 3.9.0 26_linux64_openblas + constrains: + - libcblas 3.9.0 26_linux64_openblas + - liblapacke 3.9.0 26_linux64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16338 + timestamp: 1734432576650 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_linuxaarch64_openblas + build_number: 26 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda + sha256: a42bd01498efe2ccf6d08d56ac3cbd3ceab79e06699ff5aac3da8e45a66738f7 + md5: a5d4e18876393633da62fd8492c00156 + depends: + - libblas 3.9.0 26_linuxaarch64_openblas + constrains: + - blas * openblas + - liblapacke 3.9.0 26_linuxaarch64_openblas + - libcblas 3.9.0 26_linuxaarch64_openblas + license: BSD-3-Clause + size: 16403 + timestamp: 1734432585123 +- kind: conda + name: liblapack + version: 3.9.0 + build: 26_osxarm64_openblas + build_number: 26 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda + sha256: dd6d9a21e672aee4332f019c8229ce70cf5eaf6c2f4cbd1443b105fb66c00dc5 + md5: cebad79038a75cfd28fa90d147a2d34d + depends: + - libblas 3.9.0 26_osxarm64_openblas + constrains: + - liblapacke 3.9.0 26_osxarm64_openblas + - libcblas 3.9.0 26_osxarm64_openblas + - blas * openblas + license: BSD-3-Clause + size: 16624 + timestamp: 1734433068120 +- kind: conda + name: liblzma + version: 5.6.3 + build: h39f12f2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 + md5: b2553114a7f5e20ccd02378a77d836aa + depends: + - __osx >=11.0 + license: 0BSD + size: 99129 + timestamp: 1733407496073 +- kind: conda + name: liblzma + version: 5.6.3 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + sha256: d1cce0b7d62d1e54e2164d3e0667ee808efc6c3870256e5b47a150cd0bf46824 + md5: eb08b903681f9f2432c320e8ed626723 + depends: + - libgcc >=13 + license: 0BSD + size: 124138 + timestamp: 1733409137214 +- kind: conda + name: liblzma + version: 5.6.3 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 + md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: 0BSD + size: 111132 + timestamp: 1733407410083 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h161d5f1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 + md5: 19e57602824042dfd0446292ef90488b + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 647599 + timestamp: 1729571887612 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: h6d7220d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 + depends: + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 566719 + timestamp: 1729572385640 +- kind: conda + name: libnghttp2 + version: 1.64.0 + build: hc8609a4_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda + sha256: c093c6d370aadbf0409c20b6c54c488ee2f6fea976181919fcc63e87ee232673 + md5: f52c614fa214a8bedece9421c771670d + depends: + - c-ares >=1.32.3,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 714610 + timestamp: 1729571912479 +- kind: conda + name: libnsl + version: 2.0.1 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 + md5: c14f32510f694e3185704d89967ec422 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 34501 + timestamp: 1697358973269 +- kind: conda + name: libnsl + version: 2.0.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 33408 + timestamp: 1697359010159 +- kind: conda + name: libopenblas + version: 0.3.28 + build: openmp_hf332438_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 + md5: 40803a48d947c8639da6704e9a44d3ce + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=13.2.0 + - llvm-openmp >=18.1.8 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4165774 + timestamp: 1730772154295 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h94d23a6_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + sha256: 99ba271d8a80a1af2723f2e124ffd91d850074c0389c067e6d96d72a2dbfeabe + md5: 62857b389e42b36b686331bec0922050 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 5578513 + timestamp: 1730772671118 +- kind: conda + name: libopenblas + version: 0.3.28 + build: pthreads_h9d3fd7e_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda + sha256: 30623a40764e935aa77e0d4db54c1a1589189a9bf3a03fdb445505c1e319b5a6 + md5: e8dde93dd199da3c1f2c1fcfd0042cd4 + depends: + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.2.0 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4793435 + timestamp: 1730773029647 +- kind: conda + name: libparquet + version: 18.1.0 + build: h081d1f1_6_cpu + build_number: 6 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda + sha256: c691a59f1ebb6cedbf827f49f6cf414e08b0eec911f589133e6a8321e8ac701c + md5: 68788df49ce7480187eb6387f15b2b67 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0 h44a453e_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1204535 + timestamp: 1733810811118 +- kind: conda + name: libparquet + version: 18.1.0 + build: h636d7b7_6_cpu + build_number: 6 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda + sha256: 88c1e810bede65c54f1ebc51c14400f9e8cf0fc1f88a8c0a99210e2f5dfed582 + md5: 9b333c3a38e55f6c1b8733222e22f528 + depends: + - __osx >=11.0 + - libarrow 18.1.0 h4a2f8bd_6_cpu + - libcxx >=18 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 873134 + timestamp: 1733809271282 +- kind: conda + name: libparquet + version: 18.1.0 + build: hfc78867_6_cpu + build_number: 6 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda + sha256: 38aab34c422519c530d0e9a3e0ffd1624db1c1e163983c46ae341e831b2eb6b5 + md5: 1ab6d4a9a982920b9dc5f2c700777b27 + depends: + - libarrow 18.1.0 h1b535d6_6_cpu + - libgcc >=13 + - libstdcxx >=13 + - libthrift >=0.21.0,<0.21.1.0a0 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1117592 + timestamp: 1733810440129 +- kind: conda + name: libpng + version: 1.6.44 + build: hadc24fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda + sha256: e5b14f7a01c2db4362d8591f42f82f336ed48d5e4079e4d1f65d0c2a3637ea78 + md5: f4cc49d7aa68316213e4b12be35308d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 290661 + timestamp: 1726234747153 +- kind: conda + name: libpng + version: 1.6.44 + build: hc14010f_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda + sha256: 38f8759a3eb8060deabd4db41f0f023514d853e46ddcbd0ba21768fc4e563bb1 + md5: fb36e93f0ea6a6f5d2b99984f34b049e + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 263385 + timestamp: 1726234714421 +- kind: conda + name: libpng + version: 1.6.44 + build: hc4a20ef_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda + sha256: 23b5ce15cf9c6017641a8396bab00ae807dd9f662718cfa7f61de114d0c97647 + md5: 5d25802b25fcc7419fa13e21affaeb3a + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 294907 + timestamp: 1726236639270 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h029595c_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda + sha256: d8c7b6f851bfc53494d9b8e54d473c4f11ab26483a6e64df6f7967563df166b1 + md5: 538dbe0ad9f248e2e109abb9b6809ea5 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2802876 + timestamp: 1728564881988 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h5b01275_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda + sha256: 5e8fd4aa00193c85602ce6101dd28fe31306dff85c9725048f6dc828dfa7c421 + md5: ab0bff36363bec94720275a681af8b83 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2945348 + timestamp: 1728565355702 +- kind: conda + name: libprotobuf + version: 5.28.2 + build: h8f0b736_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda + sha256: f732a6fa918428e2d5ba61e78fe11bb44a002cc8f6bb74c94ee5b1297fefcfd8 + md5: d2cb5991f2fb8eb079c80084435e9ce6 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2374965 + timestamp: 1728565334796 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h18dbdb1_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + sha256: 96d4fdac28d5af38c38f90c22cb0aa9a90affae13ca8ba24bd1eb60b789df8ff + md5: f1800796b0efc4bbc5b001d845545111 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 203516 + timestamp: 1728778974654 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: h2348fd5_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + sha256: 6facca42cfc85a05b33e484a8b0df7857cc092db34806946d022270098d8d20f + md5: 5a7065309a66097738be6a06fd04b7ef + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcxx >=17 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 165956 + timestamp: 1728779107218 +- kind: conda + name: libre2-11 + version: 2024.07.02 + build: hbbce691_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + sha256: f8ad6a4f6d4fd54ebe3e5e712a01e663222fc57f49d16b6b8b10c30990dafb8f + md5: 2124de47357b7a516c0a3efd8f88c143 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - re2 2024.07.02.* + license: BSD-3-Clause + license_family: BSD + size: 211096 + timestamp: 1728778964655 +- kind: conda + name: libsodium + version: 1.0.20 + build: h4ab18f5_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + size: 205978 + timestamp: 1716828628198 +- kind: conda + name: libsodium + version: 1.0.20 + build: h68df207_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda + sha256: 448df5ea3c5cf1af785aad46858d7a5be0522f4234a4dc9bb764f4d11ff3b981 + md5: 2e4a8f23bebdcb85ca8e5a0fbe75666a + depends: + - libgcc-ng >=12 + license: ISC + size: 177394 + timestamp: 1716828514515 +- kind: conda + name: libsodium + version: 1.0.20 + build: h99b78c6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h3f77e49_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + sha256: f192f3c8973de9ec4c214990715f13b781965247a5cedf9162e7f9e699cfc3c4 + md5: 122d6f29470f1a991e85608e77e56a8a + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 850553 + timestamp: 1733762057506 +- kind: conda + name: libsqlite + version: 3.47.2 + build: h5eb1b54_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + sha256: 885a27fa84a5a73ed9779168c02b6c386e2fc7a53f0566b32a09ceca146b42b4 + md5: d4bf59f8783a4a66c0aec568f6de3ff4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 1042182 + timestamp: 1733761913736 +- kind: conda + name: libsqlite + version: 3.47.2 + build: hee588c1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + sha256: 48af21ebc2cbf358976f1e0f4a0ab9e91dfc83d0ef337cf3837c6f5bc22fb352 + md5: b58da17db24b6e08bcbf8fed2fb8c915 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + size: 873551 + timestamp: 1733761824646 +- kind: conda + name: libssh2 + version: 1.11.1 + build: h9cc3647_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 + md5: ddc7194676c285513706e5fc64f214d7 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279028 + timestamp: 1732349599461 +- kind: conda + name: libssh2 + version: 1.11.1 + build: ha41c0db_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + sha256: 40f2af5357457546bd11cd64a3b9043d83865180f65ce602515c35f353be35c7 + md5: aeffe03c0e598f015aab08dbb04f6ee4 + depends: + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 311577 + timestamp: 1732349396421 +- kind: conda + name: libssh2 + version: 1.11.1 + build: hf672d98_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 + md5: be2de152d8073ef1c01b7728475f2fe7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 304278 + timestamp: 1732349402869 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: h3f4de04_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 + md5: 37f489acd39e22b623d2d1e5ac6d195c + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3816794 + timestamp: 1729089463404 +- kind: conda + name: libstdcxx + version: 14.2.0 + build: hc0a3c3a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + sha256: 4661af0eb9bdcbb5fb33e5d0023b001ad4be828fccdcc56500059d56f9869462 + md5: 234a5554c53625688d51062645337328 + depends: + - libgcc 14.2.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3893695 + timestamp: 1729027746910 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: h4852527_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda + sha256: 25bb30b827d4f6d6f0522cc0579e431695503822f144043b93c50237017fffd8 + md5: 8371ac6457591af2cf6159439c1fd051 + depends: + - libstdcxx 14.2.0 hc0a3c3a_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54105 + timestamp: 1729027780628 +- kind: conda + name: libstdcxx-ng + version: 14.2.0 + build: hf1166c9_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda + sha256: 9f97461bd55a2745a7a0941f3502a047f15bfe7bb2952dc7fb204b3202f866fd + md5: 0e75771b8a03afae5a2c6ce71bc733f5 + depends: + - libstdcxx 14.2.0 h3f4de04_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 54133 + timestamp: 1729089498541 +- kind: conda + name: libthrift + version: 0.21.0 + build: h0e7cc3e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda + sha256: ebb395232973c18745b86c9a399a4725b2c39293c9a91b8e59251be013db42f0 + md5: dcb95c0a98ba9ff737f7ae482aef7833 + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 425773 + timestamp: 1727205853307 +- kind: conda + name: libthrift + version: 0.21.0 + build: h154c74f_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda + sha256: f04ab1417aca1687edff9c30d8423ace285eb8c053dc16d595c6e47cfeefb274 + md5: c28792bf37f4ecdce8e3cb9e40750650 + depends: + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 417329 + timestamp: 1727205944238 +- kind: conda + name: libthrift + version: 0.21.0 + build: h64651cc_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda + sha256: 7a6c7d5f58cbbc2ccd6493b4b821639fdb0701b9b04c737a949e8cb6adf1c9ad + md5: 7ce2bd2f650f8c31ad7ba4c7bfea61b7 + depends: + - __osx >=11.0 + - libcxx >=17 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 324342 + timestamp: 1727206096912 +- kind: conda + name: libtiff + version: 4.7.0 + build: h551f018_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae + md5: a5d084a957563e614ec0c0196d890654 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=18 + - libdeflate >=1.23,<1.24.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 370600 + timestamp: 1734398863052 +- kind: conda + name: libtiff + version: 4.7.0 + build: h88f7998_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + sha256: 5888bd66ba7606ae8596856c7dac800940ecad0aed77d6aa37db69d434c81cf0 + md5: 36a0ea4a173338c8725dc0807e99cf22 + depends: + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 464699 + timestamp: 1734398752249 +- kind: conda + name: libtiff + version: 4.7.0 + build: hd9ff511_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 + - libwebp-base >=1.4.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: HPND + size: 428173 + timestamp: 1734398813264 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h5505292_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda + sha256: ea88f06e97ef8fa2490f7594f8885bb542577226edf8abba3144302d951a53c2 + md5: f777470d31c78cd0abe1903a2fda436f + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 83000 + timestamp: 1732868631531 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: h86ecc28_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + sha256: 37a1833c55f9945724cd4b3eb6a1469032cc754a1dd725f191c34154ad2ba7e4 + md5: 699f155da290be3a1a64c932c6728991 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 81526 + timestamp: 1732868466862 +- kind: conda + name: libutf8proc + version: 2.9.0 + build: hb9d3cd8_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + sha256: 9794e6388e780c3310d46f773bbc924d4053375c3fcdb07a704b57f4616db928 + md5: 1e936bd23d737aac62a18e9a1e7f8b18 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 81500 + timestamp: 1732868419835 +- kind: conda + name: libuuid + version: 2.38.1 + build: h0b41bf4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 33601 + timestamp: 1680112270483 +- kind: conda + name: libuuid + version: 2.38.1 + build: hb4cce97_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda + sha256: 616277b0c5f7616c2cdf36f6c316ea3f9aa5bb35f2d4476a349ab58b9b91675f + md5: 000e30b09db0b7c775b21695dff30969 + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 35720 + timestamp: 1680113474501 +- kind: conda + name: libuv + version: 1.49.2 + build: h7ab814d_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda + sha256: 0e5176af1e788ad5006cf261c4ea5a288a935fda48993b0240ddd2e562dc3d02 + md5: 4bc348e3a1a74d20a3f9beb866d75e0a + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 410500 + timestamp: 1729322654121 +- kind: conda + name: libuv + version: 1.49.2 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda + sha256: adf4eca89339ac7780f2394e7e6699be81259eb91f79f9d9fdf2c1bc6b26f210 + md5: 1899e1ec2be63386c41c4db31d3056af + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 627484 + timestamp: 1729322575379 +- kind: conda + name: libuv + version: 1.49.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda + sha256: a35cd81cd1a9add11024097da83cc06b0aae83186fe4124b77710876f37d8f31 + md5: 070e3c9ddab77e38799d5c30b109c633 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 884647 + timestamp: 1729322566955 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + sha256: 10dded60f274e29c573cfacf6e96f5d0fc374ee431250374a44cbd773916ab9d + md5: 5fd7ab3e5f382c70607fbac6335e6e19 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 363577 + timestamp: 1713201785160 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: h93a5062_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + sha256: 0d4bad713a512d79bfeb4d61821f447afab8b0792aca823f505ce6b195e9fde5 + md5: c0af0edfebe780b19940e94871f1a765 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 287750 + timestamp: 1713200194013 +- kind: conda + name: libwebp-base + version: 1.4.0 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + sha256: 49bc5f6b1e11cb2babf2a2a731d1a680a5e08a858280876a779dbda06c78c35f + md5: b26e8aa824079e1be0294e7152ca4559 + depends: + - libgcc-ng >=12 + constrains: + - libwebp 1.4.0 + license: BSD-3-Clause + license_family: BSD + size: 438953 + timestamp: 1713199854503 +- kind: conda + name: libxcb + version: 1.17.0 + build: h262b8f6_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b + md5: cd14ee5cca2464a425b1dbfc24d90db2 + depends: + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 397493 + timestamp: 1727280745441 +- kind: conda + name: libxcb + version: 1.17.0 + build: h8a09558_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 395888 + timestamp: 1727278577118 +- kind: conda + name: libxcb + version: 1.17.0 + build: hdb1d25a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 323658 + timestamp: 1727278733917 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: h31becfc_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f + md5: b4df5d7d4b63579d081fd3a4cf99740e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 114269 + timestamp: 1702724369203 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 100393 + timestamp: 1702724383534 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h0d44e9d_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda + sha256: 306e18aa647d8208ad2cd0e62d84933222b2fbe93d2d53cd5283d2256b1d54de + md5: f5b05674697ae7d2c5932766695945e1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - icu <0.0a0 + license: MIT + license_family: MIT + size: 689993 + timestamp: 1733443678322 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h178c5d8_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 + md5: 3dc3cff0eca1640a6acbbfab2f78139e + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 582898 + timestamp: 1733443841584 +- kind: conda + name: libxml2 + version: 2.13.5 + build: h2e0c361_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 + md5: 63410f85031930cde371dfe0ee89109a + depends: + - icu >=75.1,<76.0a0 + - libgcc >=13 + - libiconv >=1.17,<2.0a0 + - liblzma >=5.6.3,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 732155 + timestamp: 1733443825814 +- kind: conda + name: libzlib + version: 1.3.1 + build: h8359307_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 46438 + timestamp: 1727963202283 +- kind: conda + name: libzlib + version: 1.3.1 + build: h86ecc28_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 + md5: 08aad7cbe9f5a6b460d0976076b6ae64 + depends: + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 66657 + timestamp: 1727963199518 +- kind: conda + name: libzlib + version: 1.3.1 + build: hb9d3cd8_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 60963 + timestamp: 1727963148474 +- kind: conda + name: lit + version: 19.1.5 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda + sha256: 07854df4ab39a333155b4813338caf8e0f6fe5a9abc84518d9409aa5cd91f94c + md5: ad3f4f4e25b666610c281c6fb92f06f9 + depends: + - python >=3 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 128621 + timestamp: 1733310809397 +- kind: conda + name: llvm-openmp + version: 19.1.5 + build: hdb05f8b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + sha256: e7ba0d8b718925efdcf1309f5e776e3264cc172d3af8d4048b39627c50a1abc0 + md5: f2c2e187a1d2637d282e34dc92021a70 + depends: + - __osx >=11.0 + constrains: + - openmp 19.1.5|19.1.5.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 281120 + timestamp: 1733376089600 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h286801f_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5888daf_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 167055 + timestamp: 1733741040117 +- kind: conda + name: lz4-c + version: 1.10.0 + build: h5ad3122_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda + sha256: 67e55058d275beea76c1882399640c37b5be8be4eb39354c94b610928e9a0573 + md5: 6654e411da94011e8fbe004eacb8fe11 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 184953 + timestamp: 1733740984533 +- kind: conda + name: markdown-it-py + version: 3.0.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a + md5: fee3164ac23dfca50cfcc8b85ddefb81 + depends: + - mdurl >=0.1,<1 + - python >=3.9 + license: MIT + license_family: MIT + size: 64430 + timestamp: 1733250550053 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h178313f_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + sha256: 4a6bf68d2a2b669fecc9a4a009abd1cf8e72c2289522ff00d81b5a6e51ae78f5 + md5: eb227c3e0bf58f5bd69c0532b157975b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24604 + timestamp: 1733219911494 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h74ce7d3_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda + sha256: 1d500158262f30b9c23e37d1c861fe76e127a3926d69b3b38c25d20d3faa6f9f + md5: bc8607ab678073a0441808a31465f4fb + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 25079 + timestamp: 1733220639175 +- kind: conda + name: markupsafe + version: 3.0.2 + build: py312h998013c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda + sha256: 4aa997b244014d3707eeef54ab0ee497d12c0d0d184018960cce096169758283 + md5: 46e547061080fddf9cf95a0327e8aba6 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 24048 + timestamp: 1733219945697 +- kind: conda + name: max + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/max-24.6.0-release.conda + sha256: 0e3c1984ac7476550fd8fa5921bf1ca58950219b84ae21ecd861f650e45382ac + md5: e04b1405f630c9bb7d4cb5559840e902 + depends: + - max-core ==24.6.0 release + - max-python >=24.6.0,<25.0a0 + - mojo-jupyter ==24.6.0 release + - mblack ==24.6.0 release + license: LicenseRef-Modular-Proprietary + size: 9851 + timestamp: 1734039439696 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda + sha256: 38a4128c15b230f5b05e0606a339c7866a83eb943d334a948b3a8c1d2675a917 + md5: 25e678ff7c59e36ec3154fe0cd15ebde + depends: + - mblack ==24.6.0 release + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 247670119 + timestamp: 1734039439695 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda + sha256: 554f2c1a6ddfab8fabf82b9fbcc1adb87e2a615669793e463f757ea452e02016 + md5: 2ca790aa461fa28722c6f21fcd2af0b9 + depends: + - mblack ==24.6.0 release + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 251486610 + timestamp: 1734039413769 +- kind: conda + name: max-core + version: 24.6.0 + build: release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda + sha256: 434c29e35067e296db55525cd5cf38bb013a1f7a7bfa99845bf6c317de6cdc12 + md5: 4a2ead0a9010c36b6193ea32f583e996 + depends: + - mblack ==24.6.0 release + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 212001240 + timestamp: 1734039726703 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-64 + url: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda + sha256: 6fbf7330ad910e6ec9fd581fd0f8505e5b1326ccf9979d553c70c61abf4c3e54 + md5: 218ecd662f853ea1578404799d61b385 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: x86_64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 123785050 + timestamp: 1734039439704 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: linux-aarch64 + url: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda + sha256: 15f57cb436b00c510473ca1940edd9ee03df6c92d5b66d616db30a131b768b78 + md5: 97c62f17f0d34787d29128043a593877 + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: aarch64 + platform: linux + license: LicenseRef-Modular-Proprietary + size: 127403269 + timestamp: 1734039413779 +- kind: conda + name: max-python + version: 24.6.0 + build: 3.12release + subdir: osx-arm64 + url: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda + sha256: c888b58cfc7c767d40aa100ff2bccf5c3ab11d58d897a6accb749e6b5b7014ea + md5: 62a92bfab3b5c85c2d246672bbb8bc8d + depends: + - max-core ==24.6.0 release + - python 3.12.* + - fastapi + - httpx + - huggingface_hub + - numpy >=1.18,<2.0 + - opentelemetry-api + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.48b0 + - opentelemetry-sdk >=1.27.0 + - pillow + - pydantic-settings >=2.4.0,<3 + - pydantic >=2.4.0,<3 + - pyinstrument + - python-json-logger + - sse-starlette >=2.1.3,<3 + - transformers + - typing_extensions + - uvicorn + - python_abi 3.12.* *_cp312 + arch: arm64 + platform: osx + license: LicenseRef-Modular-Proprietary + size: 112484803 + timestamp: 1734039726707 +- kind: conda + name: mblack + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda + sha256: f135164020478078f4681aa77e7f6ca9f68b8e7ee02604b85342bbaf2f706f0d + md5: 77367aff981ba391ab5c047ba33ec978 + depends: + - python >=3.9,<3.13 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - python + license: MIT + size: 130668 + timestamp: 1734039439700 +- kind: conda + name: mdurl + version: 0.1.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- kind: conda + name: mojo-jupyter + version: 24.6.0 + build: release + subdir: noarch + noarch: python + url: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda + sha256: 2fe043d98ea77f8f165b39bd252cd04942216c8533f0291c49d87d6cfd8673df + md5: b17127f3ca2cef0976496407e1cd4081 + depends: + - max-core ==24.6.0 release + - python >=3.9,<3.13 + - jupyter_client >=8.6.2,<8.7 + - python + license: LicenseRef-Modular-Proprietary + size: 22990 + timestamp: 1734039439702 +- kind: conda + name: multidict + version: 6.1.0 + build: py312h178313f_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda + sha256: b05bc8252a6e957bf4a776ed5e0e61d1ba88cdc46ccb55890c72cc58b10371f4 + md5: 5b5e3267d915a107eca793d52e1b780a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 61507 + timestamp: 1733913288935 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hcc812fe_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda + sha256: ff9f767ba4df68e9ac2a380529a83a2fb6abd985beee9eab16608f7e2c3ccc6e + md5: dcf3ae213cf0ab40ebcc10452e1ed9fa + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 63077 + timestamp: 1733913233032 +- kind: conda + name: multidict + version: 6.1.0 + build: py312hdb8e49c_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda + sha256: 482fd09fb798090dc8cce2285fa69f43b1459099122eac2fb112d9b922b9f916 + md5: 0048335516fed938e4dd2c457b4c5b9b + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 55968 + timestamp: 1729065664275 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h02f2b3b_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + sha256: 8041371e3ec3fbc2ca13c71b0180672896e6382e62892d9f6b11a4c5dd675951 + md5: 910ef2223c71902175418d9163152788 + depends: + - dill >=0.3.6 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335147 + timestamp: 1695459275360 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312h98912ed_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda + sha256: bb612a921fafda6375a2204ffebd8811db8dd3b8f25ac9886cc9bcbff7e3664e + md5: 5a64b9f44790d9a187a85366dd0ffa8d + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335666 + timestamp: 1695459025249 +- kind: conda + name: multiprocess + version: 0.70.15 + build: py312hdd3e373_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda + sha256: c53362cdf346f314e111faddc53061e3fd2ece0ba68ca303f5dd109976df158f + md5: 173a1692d2b3ddc265dc6afd21a869b3 + depends: + - dill >=0.3.6 + - libgcc-ng >=12 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 336110 + timestamp: 1695459137796 +- kind: conda + name: mypy_extensions + version: 1.0.0 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda + sha256: 1895f47b7d68581a6facde5cb13ab8c2764c2e53a76bd746f8f98910dc4e08fe + md5: 29097e7ea634a45cc5386b95cac6568f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 10854 + timestamp: 1733230986902 +- kind: conda + name: ncurses + version: '6.5' + build: h7bae524_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc + md5: cb2b0ea909b97b3d70cd3921d1445e1a + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 802321 + timestamp: 1724658775723 +- kind: conda + name: ncurses + version: '6.5' + build: hcccb83c_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + sha256: acad4cf1f57b12ee1e42995e6fac646fa06aa026529f05eb8c07eb0a84a47a84 + md5: 91d49c85cacd92caa40cf375ef72a25d + depends: + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 924472 + timestamp: 1724658573518 +- kind: conda + name: ncurses + version: '6.5' + build: he02047a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a + md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + size: 889086 + timestamp: 1724658547447 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h470d778_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + sha256: 23767677a7790bee5457d5e75ebd508b9a31c5354216f4310dd1acfca3f7a6f9 + md5: 9cebf5a06cb87d4569cd68df887af476 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6614296 + timestamp: 1707225994762 +- kind: conda + name: numpy + version: 1.26.4 + build: py312h8442bc7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6073136 + timestamp: 1707226249608 +- kind: conda + name: numpy + version: 1.26.4 + build: py312heda63a1_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 + md5: d8285bea2a350f63fab23bf460221f3f + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 7484186 + timestamp: 1707225809722 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h3f56577_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d + md5: 04231368e4af50d11184b50e14250993 + depends: + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 377796 + timestamp: 1733816683252 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h5fbd93e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 + md5: 9e5816bc95d285c115a3ebc2f8563564 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libpng >=1.6.44,<1.7.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 342988 + timestamp: 1733816638720 +- kind: conda + name: openjpeg + version: 2.5.3 + build: h8a3d83b_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + sha256: 1d59bc72ca7faac06d349c1a280f5cfb8a57ee5896f1e24225a997189d7418c7 + md5: 4b71d78648dbcf68ce8bf22bb07ff838 + depends: + - __osx >=11.0 + - libcxx >=18 + - libpng >=1.6.44,<1.7.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 319362 + timestamp: 1733816781741 +- kind: conda + name: openssl + version: 3.4.0 + build: h39f12f2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + sha256: bd1d58ced46e75efa3b842c61642fd12272c69e9fe4d7261078bc082153a1d53 + md5: df307bbc703324722df0293c9ca2e418 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 2935176 + timestamp: 1731377561525 +- kind: conda + name: openssl + version: 3.4.0 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + sha256: 64dbbdd6384fa56338124783197f7ad9048c989a02264bcd2e07355e3570f113 + md5: b2f202b5bddafac824eb610b65dde98f + depends: + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 3474825 + timestamp: 1731379200886 +- kind: conda + name: openssl + version: 3.4.0 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + sha256: 814b9dff1847b132c676ee6cc1a8cb2d427320779b93e1b6d76552275c128705 + md5: 23cc74f77eb99315c0360ec3533147a9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 2947466 + timestamp: 1731377666602 +- kind: conda + name: opentelemetry-api + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + sha256: 296280c8ace35c0a1cf72bed1077f248b3af903c3bf92332f1783a207cb5abdb + md5: 307b05402c1a382f2f09426492dee8f8 + depends: + - deprecated >=1.2.6 + - importlib-metadata >=6.0,<=8.5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 44166 + timestamp: 1734132973331 +- kind: conda + name: opentelemetry-exporter-otlp-proto-common + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda + sha256: ae9776efe52564e0d6711cfcee7c54439273e57a3999f7f796f66e862f58aae9 + md5: 0c02e74d26bce3fec93b227cf7ea6e6b + depends: + - backoff >=1.10.0,<3.0.0 + - opentelemetry-proto 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 18922 + timestamp: 1734310457116 +- kind: conda + name: opentelemetry-exporter-otlp-proto-http + version: 1.29.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda + sha256: 5d61db9d5b4f91b3932f5f2348920d5b7fdaa09e52c8ea054cf7bf3f21677c9c + md5: 223f4e56a29601c887f0dc467034af5b + depends: + - deprecated >=1.2.6 + - googleapis-common-protos >=1.52,<2.dev0 + - opentelemetry-api >=1.15,<2.dev0 + - opentelemetry-exporter-otlp-proto-common 1.29.0 + - opentelemetry-proto 1.29.0 + - opentelemetry-sdk 1.29.0 + - python >=3.9 + - requests >=2.7,<3.dev0 + license: Apache-2.0 + size: 17147 + timestamp: 1734345675510 +- kind: conda + name: opentelemetry-exporter-prometheus + version: 1.12.0rc1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda + sha256: b8239230dbbdb491401e41b53bd9f21d60551cedef1a8d5807fca1bf9bdd331c + md5: 1ddc95052b31147d1e10d818cf519cf5 + depends: + - opentelemetry-api >=1.10.0 + - opentelemetry-sdk >=1.10.0 + - prometheus_client >=0.5.0,<1.0.0 + - python >=3.6 + license: Apache-2.0 + license_family: APACHE + size: 14721 + timestamp: 1695214221489 +- kind: conda + name: opentelemetry-proto + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda + sha256: 200a7cb8acc8a0ddd6ef55c5460cec871b6a265929b240a0296c0ccb9c8d9758 + md5: e2a6d2ad10b813c7fdc1c64aac376128 + depends: + - protobuf <6.0,>=5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 37235 + timestamp: 1734291034372 +- kind: conda + name: opentelemetry-sdk + version: 1.29.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda + sha256: 7b36629d8b8be8a019fcfd1518d7b7f862dd25de96f8adcadb93e4fd12cf9bd6 + md5: 2a8893f06e6ebda4bfa78875bc923ea4 + depends: + - opentelemetry-api 1.29.0 + - opentelemetry-semantic-conventions 0.50b0 + - python >=3.9 + - typing-extensions >=3.7.4 + - typing_extensions >=3.7.4 + license: Apache-2.0 + license_family: APACHE + size: 77645 + timestamp: 1734297838999 +- kind: conda + name: opentelemetry-semantic-conventions + version: 0.50b0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda + sha256: 6526e70368d5bf66ef0eaa51fb800d53782dde71a24bd38f40139919a6f784dc + md5: f7111fa4188d646c8108e232d024cb99 + depends: + - deprecated >=1.2.6 + - opentelemetry-api 1.29.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 86084 + timestamp: 1734208980168 +- kind: conda + name: orc + version: 2.0.3 + build: h3c55218_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + sha256: 154b26bc4d586de33765a155c9b79ebd7f5bb36c2bbf4b8854e1631bca8d21af + md5: 0a51a3cf028b845c46ec0d1ea2d18629 + depends: + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1165179 + timestamp: 1733509923825 +- kind: conda + name: orc + version: 2.0.3 + build: h97ab989_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + sha256: 9de7e2746fde57c9b7f08ee87142014f6bb9b2d3a506839ea3e98baa99711576 + md5: 2f46eae652623114e112df13fae311cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1189462 + timestamp: 1733509801323 +- kind: conda + name: orc + version: 2.0.3 + build: hbcee414_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + sha256: e5e72438a3cd967ebc774070e8c49500d2d6d4175f349400b327fee75d3bfc05 + md5: e808cf7819eaa1735c8790d7f9f482c7 + depends: + - __osx >=11.0 + - libcxx >=18 + - libprotobuf >=5.28.2,<5.28.3.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - tzdata + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 437391 + timestamp: 1733510118673 +- kind: conda + name: packaging + version: '24.2' + build: pyhd8ed1ab_2 + build_number: 2 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 + md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + size: 60164 + timestamp: 1733203368787 +- kind: conda + name: pandas + version: 2.2.3 + build: py312ha2895bd_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + sha256: 585e05f95d14afe3df43ded14f86800c70da26b27e27b59de95932f8888af5d3 + md5: 80b873ac4fdf36641afa0eaafff3a664 + depends: + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15159625 + timestamp: 1726879151211 +- kind: conda + name: pandas + version: 2.2.3 + build: py312hcd31e36_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + sha256: ff0cb54b5d058c7987b4a0984066e893642d1865a7bb695294b6172e2fcdc457 + md5: c68bfa69e6086c381c74e16fd72613a8 + depends: + - __osx >=11.0 + - libcxx >=17 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 14470437 + timestamp: 1726878887799 +- kind: conda + name: pandas + version: 2.2.3 + build: py312hf9745cd_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + sha256: ad275a83bfebfa8a8fee9b0569aaf6f513ada6a246b2f5d5b85903d8ca61887e + md5: 8bce4f6caaf8c5448c7ac86d87e26b4b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - numpy >=1.19,<3 + - numpy >=1.22.4 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.8.1 + - python-tzdata >=2022a + - python_abi 3.12.* *_cp312 + - pytz >=2020.1,<2024.2 + license: BSD-3-Clause + license_family: BSD + size: 15436913 + timestamp: 1726879054912 +- kind: conda + name: pathspec + version: 0.12.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee + md5: 617f15191456cc6a13db418a275435e5 + depends: + - python >=3.9 + license: MPL-2.0 + license_family: MOZILLA + size: 41075 + timestamp: 1733233471940 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h5ab5af3_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + sha256: 3cf43a5eb1f67f3a5f3ef1ec3a685f8767019cce24dbe46c4b76fee8a54fbacf + md5: 1c4bdfe659cfdedd372685ce2494e97b + depends: + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41756471 + timestamp: 1729068045876 +- kind: conda + name: pillow + version: 11.0.0 + build: py312h7b63e92_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + sha256: 13a464bea02c0df0199c20ef6bad24a6bc336aaf55bf8d6a133d0fe664463224 + md5: 385f46a4df6f97892503a841121a9acf + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41948418 + timestamp: 1729065846594 +- kind: conda + name: pillow + version: 11.0.0 + build: py312haf37ca6_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + sha256: 727b4c3faecdb6f6809cf20c5f32d2df4af34e0d5b9146b7588383bcba7990e8 + md5: dc9b51fbd2b6f7fea9b5123458864dbb + depends: + - __osx >=11.0 + - freetype >=2.12.1,<3.0a0 + - lcms2 >=2.16,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.4.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openjpeg >=2.5.2,<3.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + license: HPND + size: 41737424 + timestamp: 1729065920347 +- kind: conda + name: platformdirs + version: 4.3.6 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + sha256: bb50f6499e8bc1d1a26f17716c97984671121608dc0c3ecd34858112bce59a27 + md5: 577852c7e53901ddccc7e6a9959ddebe + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 20448 + timestamp: 1733232756001 +- kind: conda + name: prometheus_client + version: 0.21.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda + sha256: bc8f00d5155deb7b47702cb8370f233935704100dbc23e30747c161d1b6cf3ab + md5: 3e01e386307acc60b2f89af0b2e161aa + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 49002 + timestamp: 1733327434163 +- kind: conda + name: propcache + version: 0.2.1 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda + sha256: 5771311fb5ded614ca349c92579a0b752af55a310f40b71fc533e20625965391 + md5: 55d5742a696d7da1c1262e99b6217ceb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52747 + timestamp: 1733391916349 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda + sha256: c7f62c11ed929ccf1f3d4a1e200e28be01e8d0e0786bf8f76c5893f2ea681e1b + md5: 50ab8953e7ff1333a4a47cda32e68123 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 52484 + timestamp: 1733391993461 +- kind: conda + name: propcache + version: 0.2.1 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda + sha256: f8c266c494aa1e4cfb8bf0b6fca060044b2f3d65afe4c5062ebeea382e77aa6d + md5: c84e3dd97fe25a17322c4a0f670c6750 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 48225 + timestamp: 1733392308901 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h2ec8cdc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + sha256: 4884f8161602f0148ebbc1af8d3176cec80b96c83243f68aafd651986b573817 + md5: 586bead4a9dfa46faf88deb7d3a742bb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 464548 + timestamp: 1728669645013 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312h6f74592_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + sha256: f874ffd38b9ae2b810e9d2e43fd8d3b778cdeaf7dea4a3e6ee4adeafe2d936cf + md5: 4b9b22bd7c53d938b207f9d0f79db183 + depends: + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 472764 + timestamp: 1728669483611 +- kind: conda + name: protobuf + version: 5.28.2 + build: py312hf02c72a_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + sha256: dbcec117510ced5c12097e3eb06ebbf4512dc255733a9ace33c4249fb7e6a364 + md5: 6fda46c82abd0a080ca33de7d16ca877 + depends: + - __osx >=11.0 + - libcxx >=17 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libprotobuf 5.28.2 + license: BSD-3-Clause + license_family: BSD + size: 447369 + timestamp: 1728669902591 +- kind: conda + name: pthread-stubs + version: '0.4' + build: h86ecc28_1002 + build_number: 1002 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba + md5: bb5a90c93e3bac3d5690acf76b4a6386 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 8342 + timestamp: 1726803319942 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hb9d3cd8_1002 + build_number: 1002 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 8252 + timestamp: 1726802366959 +- kind: conda + name: pthread-stubs + version: '0.4' + build: hd74edd7_1002 + build_number: 1002 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 8381 + timestamp: 1726802424786 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h1f38498_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda + sha256: 06c0e208d5bf15051874097366c8e8e5db176dffba38526f227a34e80cc8e9bc + md5: 3710616b880b31d0c8afd8ae7e12392a + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25375 + timestamp: 1732610892198 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h7900ff3_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda + sha256: 46a61c29375d3bf1933eae61c7861394c168898915d59fc99bf05e46de2ff5ad + md5: ac65b70df28687c6af4270923c020bdd + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25213 + timestamp: 1732610785600 +- kind: conda + name: pyarrow + version: 18.1.0 + build: py312h8025657_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + sha256: 49db959887cb89b44053a44a98d0f35644fc0b2003587492f02b56046de0b60a + md5: 9bb7d32e96a5dcb5ea7fd90a11a83656 + depends: + - libarrow-acero 18.1.0.* + - libarrow-dataset 18.1.0.* + - libarrow-substrait 18.1.0.* + - libparquet 18.1.0.* + - pyarrow-core 18.1.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25374 + timestamp: 1732611006864 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h01725c0_0_cpu + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + sha256: 948a4161c56f846d374a3721a657e58ddbc992a29b3b3e7a6411975c30361d94 + md5: ee80934a6c280ff8635f8db5dec11e04 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4612916 + timestamp: 1732610377259 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312h66f7834_0_cpu + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + sha256: e7eb062145be554c23dfefa0ebe8c5f6ae8c59635117a6921e66403d6addcda3 + md5: 3390c8b8f57e85506c92a37cf750bdd7 + depends: + - libarrow 18.1.0.* *cpu + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 4406662 + timestamp: 1732610939832 +- kind: conda + name: pyarrow-core + version: 18.1.0 + build: py312hc40f475_0_cpu + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + sha256: 063eb168a29d4ce6d9ed865e9e1ad3b6e141712189955a79e06b24ddc0cbbc9c + md5: 9859e7c4b94bbf69772dbf0511101cec + depends: + - __osx >=11.0 + - libarrow 18.1.0.* *cpu + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + size: 3909116 + timestamp: 1732610863261 +- kind: conda + name: pycparser + version: '2.22' + build: pyh29332c3_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- kind: conda + name: pydantic + version: 2.10.3 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda + sha256: cac9eebd3d5f8d8a497a9025d756257ddc75b8b3393e6737cb45077bd744d4f8 + md5: 194ef7f91286978521350f171b117f01 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.27.1 + - python >=3.9 + - typing-extensions >=4.6.1 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + size: 317037 + timestamp: 1733316963547 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda + sha256: c89741f4eff395f8de70975f42e1f20591f0e0870929d440af35b13399976b09 + md5: 114030cb28527db2c385f07038e914c8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1635156 + timestamp: 1732254225040 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda + sha256: 1f59bc1914f77faed3c95217e4d093310771baee4e93a15c0479359559e3fa19 + md5: d980860b8bf193f53d30a19c5d2bf070 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1503747 + timestamp: 1732254331303 +- kind: conda + name: pydantic-core + version: 2.27.1 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda + sha256: 5bba8de2bbbbdb39390abb1e2aff310e8cfd49646ae5a0e0ea4d6582bd1d52ba + md5: 3847a96eaf24a877b6091150ff9c4955 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.6.0,!=4.7.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1449057 + timestamp: 1732254359451 +- kind: conda + name: pydantic-settings + version: 2.7.0 + build: pyh3cfb1c2_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda + sha256: dd1ac7c8b6a189c8aa18f6c7df019d8f6df495300a259e3fbebdb542fc955c3b + md5: d9f19a7c4199249fa229891b573b6f9b + depends: + - pydantic >=2.7.0 + - python >=3.9 + - python-dotenv >=0.21.0 + license: MIT + license_family: MIT + size: 31426 + timestamp: 1734127929720 +- kind: conda + name: pygments + version: 2.18.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + sha256: 0d6133545f268b2b89c2617c196fc791f365b538d4057ecd636d658c3b1e885d + md5: b38dc0206e2a530e5c2cf11dc086b31a + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 876700 + timestamp: 1733221731178 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h0bf5046_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + sha256: 6879d52fb0ec2258e2850476786a652c394220d53883c53691ed5390183ae925 + md5: f0e4a98d54477083ddc9d2f33507f848 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 181512 + timestamp: 1728714205508 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + sha256: 8a006507a4003fb01eeee2f9ba79f994478694766ea3b445273da5c11cf8e763 + md5: 798f42d9bfdf125dc80ffbec0e96e0b6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 182021 + timestamp: 1728714164706 +- kind: conda + name: pyinstrument + version: 5.0.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda + sha256: 7967b94b8f0ff75847302444e9c43ac11a391d74da24cb14fba1049fac9e5ba9 + md5: 5274663cb05dfbe316db50af6da4389f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 183141 + timestamp: 1728714267954 +- kind: conda + name: pysocks + version: 1.7.1 + build: pyha55dd90_7 + build_number: 7 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 +- kind: conda + name: python + version: 3.12.8 + build: h1683364_1_cpython + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda + sha256: 85573582d5b0f79923fed0a8365d3d74d21eee9f0a5fa1b9345f191e006363ab + md5: 09ec612ea05370989eaa3d81abf0f369 + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 13760816 + timestamp: 1733407890896 +- kind: conda + name: python + version: 3.12.8 + build: h9e4cc4f_1_cpython + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda + sha256: 3f0e0518c992d8ccfe62b189125721309836fe48a010dc424240583e157f9ff0 + md5: 7fd2fd79436d9b473812f14e86746844 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - liblzma >=5.6.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 31565686 + timestamp: 1733410597922 +- kind: conda + name: python + version: 3.12.8 + build: hc22306f_1_cpython + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda + sha256: 7586a711b1b08a9df8864e26efdc06980bdfb0e18d5ac4651d0fee30a8d3e3a0 + md5: 54ca5b5d92ef3a3ba61e195ee882a518 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.4,<3.0a0 + - libffi >=3.4,<4.0a0 + - liblzma >=5.6.3,<6.0a0 + - libsqlite >=3.47.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.4.0,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 12998673 + timestamp: 1733408900971 +- kind: conda + name: python-dateutil + version: 2.9.0.post0 + build: pyhff2d567_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda + sha256: a50052536f1ef8516ed11a844f9413661829aa083304dc624c5925298d078d79 + md5: 5ba79d7c71f03c678c8ead841f347d6e + depends: + - python >=3.9 + - six >=1.5 + license: Apache-2.0 + license_family: APACHE + size: 222505 + timestamp: 1733215763718 +- kind: conda + name: python-dotenv + version: 1.0.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 + md5: e5c6ed218664802d305e79cc2d4491de + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 24215 + timestamp: 1733243277223 +- kind: conda + name: python-json-logger + version: 2.0.7 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca + md5: a61bf9ec79426938ff785eb69dbb1960 + depends: + - python >=3.6 + license: BSD-2-Clause + license_family: BSD + size: 13383 + timestamp: 1677079727691 +- kind: conda + name: python-multipart + version: 0.0.20 + build: pyhff2d567_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 27913 + timestamp: 1734420869885 +- kind: conda + name: python-tzdata + version: '2024.2' + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + sha256: 57c9a02ec25926fb48edca59b9ede107823e5d5c473b94a0e05cc0b9a193a642 + md5: c0def296b2f6d2dd7b030c2a7f66bb1f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 142235 + timestamp: 1733235414217 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + sha256: 28204ef48f028a4d872e22040da0dad7ebd703549b010a1bb511b6dd94cf466d + md5: 266fe1ae54a7bb17990206664d0f0ae4 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 21765 + timestamp: 1725272382968 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h52516f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + sha256: 0fa5ba80073a43391ee90303814adbc9fd826175de1fdac273ba0e5b711aa255 + md5: 591c4ae6d8338dfd07b951e00433a405 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23589 + timestamp: 1725273317965 +- kind: conda + name: python-xxhash + version: 3.5.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + sha256: 20851b1e59fee127d49e01fc73195a88ab0779f103b7d6ffc90d11142a83678f + md5: 39aed2afe4d0cf76ab3d6b09eecdbea7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - xxhash >=0.8.2,<0.8.3.0a0 + license: BSD-2-Clause + license_family: BSD + size: 23162 + timestamp: 1725272139519 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 + md5: 0424ae29b104430108f5218a66db7260 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6238 + timestamp: 1723823388266 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda + sha256: 5ccdad9981753cc4a2d126e356673a21c0cd5b34e209cb8d476a3947d4ad9b39 + md5: 62b20f305498284a07dc6c45fd0e5c87 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6329 + timestamp: 1723823366253 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda + sha256: 49d624e4b809c799d2bf257b22c23cf3fc4460f5570d9a58e7ad86350aeaa1f4 + md5: b76f9b1c862128e56ac7aa8cd2333de9 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6278 + timestamp: 1723823099686 +- kind: conda + name: pytz + version: '2024.1' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda + sha256: 1a7d6b233f7e6e3bbcbad054c8fd51e690a67b129a899a056a5e45dd9f00cb41 + md5: 3eeeeb9e4827ace8c0c1419c85d590ad + depends: + - python >=3.7 + license: MIT + license_family: MIT + size: 188538 + timestamp: 1706886944988 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h024a12e_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + sha256: b06f1c15fb39695bbf707ae8fb554b9a77519af577b5556784534c7db10b52e3 + md5: 1ee23620cf46cb15900f70a1300bae55 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 187143 + timestamp: 1725456547263 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + sha256: a60705971e958724168f2ebbb8ed4853067f1d3f7059843df3903e3092bbcffa + md5: 549e5930e768548a89c23f595dac5a95 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 206553 + timestamp: 1725456256213 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + sha256: 8c515ebe1e7e85d972d72b75760af9dfac06fd11a9dba7e05c42d69aedbb303c + md5: dc5de424f7dbb9772da720dbb81317b2 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 199141 + timestamp: 1725456356043 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312h2427ae1_3 + build_number: 3 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + sha256: cfc4ea87d68b5f0ed64a61f500d5ea0a2310d1f281a4f95afa06c703ea1bdf7d + md5: 1f0779280c3dc1e72cfd86bd1e59791d + depends: + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 371730 + timestamp: 1728644030875 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hbf22597_3 + build_number: 3 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + sha256: bc303f9b11e04a515f79cd5ad3bfa0e84b9dfec76552626d6263b38789fe6678 + md5: 746ce19f0829ec3e19c93007b1a224d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 378126 + timestamp: 1728642454632 +- kind: conda + name: pyzmq + version: 26.2.0 + build: py312hf8a1cbd_3 + build_number: 3 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + sha256: 2e0ca1bb9ab3af5d1f9b38548d65be7097ba0246e7e63c908c9b1323df3f45b5 + md5: 7bdaa4c2a84b744ef26c8b2ba65c3d0e + depends: + - __osx >=11.0 + - libcxx >=17 + - libsodium >=1.0.20,<1.0.21.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 361674 + timestamp: 1728642457661 +- kind: conda + name: re2 + version: 2024.07.02 + build: h2d3a13d_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + sha256: 55e7be480bfb979fa8595a16d7f2adea3a5ac9a77b2e97cd0f7ac40e989edb6c + md5: 83f4e47229834c895a92c18383e1cd9d + depends: + - libre2-11 2024.07.02 h18dbdb1_1 + license: BSD-3-Clause + license_family: BSD + size: 26747 + timestamp: 1728778986331 +- kind: conda + name: re2 + version: 2024.07.02 + build: h77b4e00_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + sha256: c1721cb80f7201652fc9801f49c214c88aee835d957f2376e301bd40a8415742 + md5: 01093ff37c1b5e6bf9f17c0116747d11 + depends: + - libre2-11 2024.07.02 hbbce691_1 + license: BSD-3-Clause + license_family: BSD + size: 26665 + timestamp: 1728778975855 +- kind: conda + name: re2 + version: 2024.07.02 + build: hcd0e937_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + sha256: eebddde6cb10b146507810b701ef6df122d5309cd5151a39d0828aa44dc53725 + md5: 19e29f2ccc9168eb0a39dc40c04c0e21 + depends: + - libre2-11 2024.07.02 h2348fd5_1 + license: BSD-3-Clause + license_family: BSD + size: 26860 + timestamp: 1728779123653 +- kind: conda + name: readline + version: '8.2' + build: h8228510_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 + md5: 47d31b792659ce70f470b5c82fdfb7a4 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 281456 + timestamp: 1679532220005 +- kind: conda + name: readline + version: '8.2' + build: h8fc344f_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda + sha256: 4c99f7417419734e3797d45bc355e61c26520e111893b0d7087a01a7fbfbe3dd + md5: 105eb1e16bf83bfb2eb380a48032b655 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 294092 + timestamp: 1679532238805 +- kind: conda + name: readline + version: '8.2' + build: h92ec313_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda + sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 + md5: 8cbb776a2f641b943d413b3e19df71f4 + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 250351 + timestamp: 1679532511311 +- kind: conda + name: regex + version: 2024.11.6 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda + sha256: fcb5687d3ec5fff580b64b8fb649d9d65c999a91a5c3108a313ecdd2de99f06b + md5: 647770db979b43f9c9ca25dcfa7dc4e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 402821 + timestamp: 1730952378415 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda + sha256: ec2c416860de29224e447e2031f8686a05476759c17da1f32f61d4307e540ec8 + md5: fa8b589107567f532fa1380e66f91776 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 398947 + timestamp: 1730952477463 +- kind: conda + name: regex + version: 2024.11.6 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda + sha256: dcdec32f2c7dd37986baa692bedf9db126ad34e92e5e9b64f707cba3d04d2525 + md5: e73cda1f18846b608284bd784f061eac + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Python-2.0 + license_family: PSF + size: 366374 + timestamp: 1730952427552 +- kind: conda + name: requests + version: 2.32.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda + sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad + md5: a9b9368f3701a417eac9edbcae7cb737 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 58723 + timestamp: 1733217126197 +- kind: conda + name: rich + version: 13.9.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 + md5: 7aed65d4ff222bfb7335997aa40b7da5 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.9 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + size: 185646 + timestamp: 1733342347277 +- kind: conda + name: rich-toolkit + version: 0.11.3 + build: pyh29332c3_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 + md5: 4ba15ae9388b67d09782798347481f69 + depends: + - python >=3.9 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 17357 + timestamp: 1733750834072 +- kind: conda + name: s2n + version: 1.5.10 + build: h5df210e_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda + sha256: b5e7a9f4b7b1ec5c5c3661e2defc8b47fab543b05cad6fec78739d8007612464 + md5: 3d3979efcc0f44f3f0cef3de03b296cc + depends: + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 353450 + timestamp: 1734415474615 +- kind: conda + name: s2n + version: 1.5.10 + build: hb5b8611_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda + sha256: f6d451821fddc26b93f45e9313e1ea15e09e5ef049d4e137413a5225d2a5dfba + md5: 999f3673f2a011f59287f2969e3749e4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - openssl >=3.4.0,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 355142 + timestamp: 1734415467047 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + sha256: e44515f875c10efb5e041efcb250dfd18f2cb66ec3f268237549ead6284c6922 + md5: 3b87a00bcaab069172d6cef8124b7142 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 402547 + timestamp: 1725632183154 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + sha256: e83ebeaba4a07bbe4a1d6c7eef0b4f7ae19901ef365bca043808d16e4c8faad8 + md5: 82ef253c37308b082a478fb92924cad6 + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 400284 + timestamp: 1725632278147 +- kind: conda + name: safetensors + version: 0.4.5 + build: py312he431725_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + sha256: 93a085d0d64237db7f4ff395c446f268c575dc2c324d8e3e5c5d7d836896295e + md5: ccb978cf1e3151c25a44c4ae65c1f20e + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 353606 + timestamp: 1725632294079 +- kind: conda + name: shellingham + version: 1.5.4 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef + md5: 7c3c2a0f3ebdea2bbc35538d162b43bf + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14462 + timestamp: 1733301007770 +- kind: conda + name: six + version: 1.17.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda + sha256: 41db0180680cc67c3fa76544ffd48d6a5679d96f4b71d7498a759e94edc9a2db + md5: a451d576819089b0d672f18768be0f65 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 16385 + timestamp: 1733381032766 +- kind: conda + name: snappy + version: 1.2.1 + build: h8bd8927_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda + sha256: ec91e86eeb2c6bbf09d51351b851e945185d70661d2ada67204c9a6419d282d3 + md5: 3b3e64af585eadfb52bb90b553db5edf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 42739 + timestamp: 1733501881851 +- kind: conda + name: snappy + version: 1.2.1 + build: h98b9ce2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f + md5: ded86dee325290da2967a3fea3800eb5 + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-3-Clause + license_family: BSD + size: 35857 + timestamp: 1733502172664 +- kind: conda + name: snappy + version: 1.2.1 + build: hd4fb6f5_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda + sha256: c4a07ae5def8d55128f25a567a296ef9d7bf99a3bc79d46bd5160c076a5f50af + md5: 2fcc6cd1e5550deb509073fd2e6693e1 + depends: + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 43032 + timestamp: 1733501964775 +- kind: conda + name: sniffio + version: 1.3.1 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 + md5: bf7a226e58dfb8346c70df36065d86c9 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 15019 + timestamp: 1733244175724 +- kind: conda + name: sse-starlette + version: 2.1.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda + sha256: 6d671a66333410ec7e5e7858a252565a9001366726d1fe3c3a506d7156169085 + md5: 3918255c942c242ed5599e10329e8d0e + depends: + - anyio + - python >=3.8 + - starlette + - uvicorn + license: BSD-3-Clause + license_family: BSD + size: 14712 + timestamp: 1722520112550 +- kind: conda + name: starlette + version: 0.41.3 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + sha256: b74fc76107487eb26624c01fc55bfab7eed03ae82e003333c86d8a1eeac53672 + md5: 0207dac04ae2200701fab697f0aaaac4 + depends: + - anyio >=3.4.0,<5 + - python >=3.9 + - typing_extensions >=3.10.0 + license: BSD-3-Clause + license_family: BSD + size: 58838 + timestamp: 1733344472634 +- kind: conda + name: tk + version: 8.6.13 + build: h194ca79_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + sha256: 7fa27cc512d3a783f38bd16bbbffc008807372499d5b65d089a8e43bde9db267 + md5: f75105e0585851f818e0009dd1dde4dc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3351802 + timestamp: 1695506242997 +- kind: conda + name: tk + version: 8.6.13 + build: h5083fa2_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda + sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 + md5: b50a57ba89c32b62428b71a875291c9b + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3145523 + timestamp: 1699202432999 +- kind: conda + name: tk + version: 8.6.13 + build: noxft_h4845f30_101 + build_number: 101 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3318875 + timestamp: 1699202167581 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312h8360d73_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda + sha256: 4f504a5e9d77c6d88a8f735c4319429d8bf40b742384f908a2efe0a09acc3cc5 + md5: f953aa733207f3d37acf4a3efbedba89 + depends: + - __glibc >=2.17,<3.0.a0 + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2258007 + timestamp: 1732734202127 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312ha0d6ea1_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda + sha256: ef0f4d4e2c798b1821187ea0ba4c86484e48abaa0e9a19fe68030fa7ff5dde84 + md5: 077f48c9e0c08a30d842e15c51df4143 + depends: + - huggingface_hub >=0.16.4,<1.0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2331194 + timestamp: 1732734303196 +- kind: conda + name: tokenizers + version: 0.21.0 + build: py312hf3e4074_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda + sha256: 5d395333fcb22dc611140286c1f2ea8b3fa220a4931c583587cb612238091555 + md5: 4c732c74b485ef7ac8ec1c548dd45e8e + depends: + - __osx >=11.0 + - huggingface_hub >=0.16.4,<1.0 + - libcxx >=18 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 1931389 + timestamp: 1732734727624 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h52516f5_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + sha256: 4c19a544354172b2273553267e734795a6da3c78a04c2d19f8e9e159ca3178bc + md5: e28996d9d2d44d777b7e6fb12f63715b + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 841662 + timestamp: 1732616934923 +- kind: conda + name: tornado + version: 6.4.2 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + sha256: 062a3a3a37fa8615ce57929ba7e982c76f5a5810bcebd435950f6d6c4147c310 + md5: e417822cb989e80a0d2b1b576fdd1657 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 840414 + timestamp: 1732616043734 +- kind: conda + name: tornado + version: 6.4.2 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + sha256: 964a2705a36c50040c967b18b45b9cc8de3c2aff4af546979a574e0b38e58e39 + md5: fb0605888a475d6a380ae1d1a819d976 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 842549 + timestamp: 1732616081362 +- kind: conda + name: tqdm + version: 4.67.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d + md5: 4085c9db273a148e149c03627350e22c + depends: + - colorama + - python >=3.7 + license: MPL-2.0 or MIT + size: 89484 + timestamp: 1732497312317 +- kind: conda + name: traitlets + version: 5.14.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 + md5: 019a7385be9af33791c989871317e1ed + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110051 + timestamp: 1733367480074 +- kind: conda + name: transformers + version: 4.47.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + sha256: d31821081219a0ede5c1f356b65a61ce98ac11e2df78b0eaa684c17c73389fbf + md5: 6d2ec1ddee8057d2d724a0ab0bb578a0 + depends: + - datasets !=2.5.0 + - filelock + - huggingface_hub >=0.23.0,<1.0 + - numpy >=1.17 + - packaging >=20.0 + - python >=3.9 + - pyyaml >=5.1 + - regex !=2019.12.17 + - requests + - safetensors >=0.4.1 + - tokenizers >=0.21,<0.22 + - tqdm >=4.27 + license: Apache-2.0 + license_family: APACHE + size: 3726957 + timestamp: 1733948063517 +- kind: conda + name: typer + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + sha256: ef695490e895c2ad552c77ec497b899b09fd4ad4ab07edcf5649f5994cf92a35 + md5: 170a0398946d8f5b454e592672b6fc20 + depends: + - python >=3.9 + - typer-slim-standard 0.15.1 hd8ed1ab_0 + license: MIT + license_family: MIT + size: 56175 + timestamp: 1733408582623 +- kind: conda + name: typer-slim + version: 0.15.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda + sha256: d4965516f35e0805199de6596c4ac76c4ad3d6b012be35e532102f9e53ecb860 + md5: 0218b16f5a1dd569e575a7a6415489db + depends: + - click >=8.0.0 + - python >=3.9 + - typing_extensions >=3.7.4.3 + constrains: + - rich >=10.11.0 + - typer >=0.15.1,<0.15.2.0a0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + size: 43592 + timestamp: 1733408569554 +- kind: conda + name: typer-slim-standard + version: 0.15.1 + build: hd8ed1ab_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda + sha256: f31c56fe98315da8b9ce848256c17e0b9f87896b41a6ccf0c9cc74644dcef20f + md5: 4e603c43bfdfc7b533be087c3e070cc9 + depends: + - rich + - shellingham + - typer-slim 0.15.1 pyhd8ed1ab_0 + license: MIT + license_family: MIT + size: 49531 + timestamp: 1733408570063 +- kind: conda + name: typing-extensions + version: 4.12.2 + build: hd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda + sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 + md5: b6a408c64b78ec7b779a3e5c7a902433 + depends: + - typing_extensions 4.12.2 pyha770c72_1 + license: PSF-2.0 + license_family: PSF + size: 10075 + timestamp: 1733188758872 +- kind: conda + name: typing_extensions + version: 4.12.2 + build: pyha770c72_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda + sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 + md5: d17f13df8b65464ca316cbc000a3cb64 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 39637 + timestamp: 1733188758212 +- kind: conda + name: tzdata + version: 2024b + build: hc8b5060_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda + sha256: 4fde5c3008bf5d2db82f2b50204464314cc3c91c1d953652f7bd01d9e52aefdf + md5: 8ac3367aafb1cc0a068483c580af8015 + license: LicenseRef-Public-Domain + size: 122354 + timestamp: 1728047496079 +- kind: conda + name: urllib3 + version: 2.2.3 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + sha256: 416e30a1c3262275f01a3e22e783118d9e9d2872a739a9ed860d06fa9c7593d5 + md5: 4a2d8ef7c37b8808c5b9b750501fffce + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + size: 98077 + timestamp: 1733206968917 +- kind: conda + name: uvicorn + version: 0.34.0 + build: pyh31011fe_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa + md5: 5d448feee86e4740498ec8f8eb40e052 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.9 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + size: 48643 + timestamp: 1734293057914 +- kind: conda + name: uvicorn-standard + version: 0.34.0 + build: h31011fe_0 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec + md5: 32a94143a7f65d76d2d5da37dcb4ed79 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.34.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + size: 7203 + timestamp: 1734293058849 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h0bf5046_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + sha256: b1efa77aa4871d7bb09c8dd297fa9bd9070ba7f0f95f2d12ae9cdd31ce8b6b22 + md5: 4f5110253ba80ebf27e55c4ab333880a + depends: + - __osx >=11.0 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 544097 + timestamp: 1730214653726 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312h66e93f0_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + sha256: 9337a80165fcf70b06b9d6ba920dad702260ca966419ae77560a15540e41ab72 + md5: 998e481e17c1b6a74572e73b06f2df08 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 701355 + timestamp: 1730214506716 +- kind: conda + name: uvloop + version: 0.21.0 + build: py312hb2c0f52_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda + sha256: 807eede6698bd00a1d739a3e19ee6ae6a03a66d2ddd2ef150f2dfd198c3b0292 + md5: d83e107ba16c77aba2feec47b7b666a4 + depends: + - libgcc >=13 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 655266 + timestamp: 1730214606664 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h12e396e_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda + sha256: c89755d8e8f6384b3ba13e41dcabb40bf690c38b9d61512e963129badb1ad332 + md5: b76a5ad00856af6e74da9c3e85fed0cc + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 410432 + timestamp: 1733998892675 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312h8cbf658_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda + sha256: 9be9569c279dc6e7881e9b45fe9f0368218538c660641e2f8b0e023e72a6571c + md5: 3465c1a19634233abc2d1832ac01fd31 + depends: + - anyio >=3.0.0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 404239 + timestamp: 1733998941045 +- kind: conda + name: watchfiles + version: 1.0.3 + build: py312hcd83bfe_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda + sha256: b64b78a7d6384bf72a878256802c783c692fe641ab4b806fd7e9f45e18a5e3b4 + md5: 13b89e1aa72aa773806b1f59ec018b67 + depends: + - __osx >=11.0 + - anyio >=3.0.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 363162 + timestamp: 1733999215646 +- kind: conda + name: websockets + version: '14.1' + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda + sha256: 5998940f91765ba991cf286c863c20bcb53db92bb976a2b5a714566b86b0e763 + md5: a79f7ce618bd0a9f4c00c59a03570fcd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242145 + timestamp: 1731498716195 +- kind: conda + name: websockets + version: '14.1' + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda + sha256: c292a8badcbe4040537e225fbeb237bfaf272808eab060067d965d3da98ccd5c + md5: 7e2a0ef2a1a87f88f9745f9c7059186e + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 242912 + timestamp: 1731498811466 +- kind: conda + name: websockets + version: '14.1' + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda + sha256: 98fb04a1a0f53dc604378f94b5795d0b8e462fee01bf0a887cb34d0efdf5d21f + md5: 89b79a9baa7db46ce21f5738a5a3dfda + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 243131 + timestamp: 1731498944076 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + sha256: a6fc0f4e90643d0c1fd4aab669b6a79f44a305a5474256f6f2da3354d2310fb4 + md5: ddbe3bb0e1356cb9074dd848570694f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 63807 + timestamp: 1732523690292 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + sha256: b9aa760a987ccc6bc9c61f57badba6798d9a3dcbd0814e5fb8df6d8d2935af73 + md5: 120d5d1c05386d8ce3efd65a4c86431f + depends: + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 64783 + timestamp: 1732523806 +- kind: conda + name: wrapt + version: 1.17.0 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + sha256: 0fb35c3d1642f9f47db87bdb33148f88ef19a3af1eb0ee99b5491551c57269c7 + md5: 73414acdb779a8694a14527865b4357a + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + size: 61043 + timestamp: 1732523852129 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h5505292_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 13593 + timestamp: 1734229104321 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: h86ecc28_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 + md5: d5397424399a66d33c80b1f2345a36a6 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 15873 + timestamp: 1734230458294 +- kind: conda + name: xorg-libxau + version: 1.0.12 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 14780 + timestamp: 1734229004433 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: h57736b2_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + sha256: efcc150da5926cf244f757b8376d96a4db78bc15b8d90ca9f56ac6e75755971f + md5: 25a5a7b797fe6e084e04ffe2db02fc62 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 20615 + timestamp: 1727796660574 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 19901 + timestamp: 1727794976192 +- kind: conda + name: xorg-libxdmcp + version: 1.1.5 + build: hd74edd7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda + sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 + md5: 77c447f48cab5d3a15ac224edb86a968 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 18487 + timestamp: 1727795205022 +- kind: conda + name: xxhash + version: 0.8.2 + build: h31becfc_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda + sha256: 4c526aed70b579d80e5c20d32130b6bc8bde59b3250d43c2b5269755f4da8a9b + md5: bb9faf6857108a9f62ebb4dab6ef05da + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 102442 + timestamp: 1689951682147 +- kind: conda + name: xxhash + version: 0.8.2 + build: hb547adb_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda + sha256: a70f59f7221ee72c45b39a6b36a33eb9c717ba01921cce1a3c361a4676979a2e + md5: 144cd3b88706507f332f5eb5fb83a33b + license: BSD-2-Clause + license_family: BSD + size: 97593 + timestamp: 1689951969732 +- kind: conda + name: xxhash + version: 0.8.2 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f + md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 97691 + timestamp: 1689951608120 +- kind: conda + name: yaml + version: 0.2.5 + build: h3422bc3_2 + build_number: 2 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 + md5: 4bb3f014845110883a3c5ee811fd84b4 + license: MIT + license_family: MIT + size: 88016 + timestamp: 1641347076660 +- kind: conda + name: yaml + version: 0.2.5 + build: h7f98852_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + sha256: a4e34c710eeb26945bdbdaba82d3d74f60a78f54a874ec10d373811a5d217535 + md5: 4cb3ad778ec2d5a7acbdf254eb1c42ae + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 89141 + timestamp: 1641346969816 +- kind: conda + name: yaml + version: 0.2.5 + build: hf897c2e_2 + build_number: 2 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 + sha256: 8bc601d6dbe249eba44b3c456765265cd8f42ef1e778f8df9b0c9c88b8558d7e + md5: b853307650cb226731f653aa623936a4 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 92927 + timestamp: 1641347626613 +- kind: conda + name: yarl + version: 1.18.3 + build: py312h66e93f0_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + sha256: a0d93c3bef723e384cff8a29a82a2c6b7a73b39328088f3a2d97c901f56e9a63 + md5: 91df2efaa08730416bec2a4502309275 + depends: + - __glibc >=2.17,<3.0.a0 + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 151393 + timestamp: 1733428897813 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hb2c0f52_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + sha256: 470b5b0f3ac89acd143095281167dc2ac1a56d4fa22e1794bd8f3b00bb604540 + md5: 0b3c640697bca798d0ab428f530ed24c + depends: + - idna >=2.0 + - libgcc >=13 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 150004 + timestamp: 1733429056665 +- kind: conda + name: yarl + version: 1.18.3 + build: py312hea69d52_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + sha256: 69c7863809e11bc90c0d935c16e7f151dcc925add08b3894f06059263a8cb9ba + md5: f32f9b16361866a62d6e061fcd7eb400 + depends: + - __osx >=11.0 + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + size: 141556 + timestamp: 1733429104990 +- kind: conda + name: zeromq + version: 4.3.5 + build: h3b0a872_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + sha256: a4dc72c96848f764bb5a5176aa93dd1e9b9e52804137b99daeebba277b31ea10 + md5: 3947a35e916fcc6b9825449affbf4214 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 335400 + timestamp: 1731585026517 +- kind: conda + name: zeromq + version: 4.3.5 + build: h5efb499_7 + build_number: 7 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda + sha256: a6003096dc0570a86492040ba32b04ce7662b159600be2252b7a0dfb9414e21c + md5: f2f3282559a4b87b7256ecafb4610107 + depends: + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libsodium >=1.0.20,<1.0.21.0a0 + - libstdcxx >=13 + license: MPL-2.0 + license_family: MOZILLA + size: 371419 + timestamp: 1731589490850 +- kind: conda + name: zeromq + version: 4.3.5 + build: hc1bb282_7 + build_number: 7 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda + sha256: 9e585569fe2e7d3bea71972cd4b9f06b1a7ab8fa7c5139f92a31cbceecf25a8a + md5: f7e6b65943cb73bce0143737fded08f1 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=18 + - libsodium >=1.0.20,<1.0.21.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 281565 + timestamp: 1731585108039 +- kind: conda + name: zipp + version: 3.21.0 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 + md5: 0c3cc595284c5e8f0f9900a9b228a332 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 21809 + timestamp: 1732827613585 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312h15fbf35_1 + build_number: 1 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + sha256: d00ca25c1e28fd31199b26a94f8c96574475704a825d244d7a6351ad3745eeeb + md5: a4cde595509a7ad9c13b1a3809bcfe51 + depends: + - __osx >=11.0 + - cffi >=1.11 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 330788 + timestamp: 1725305806565 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hb698573_1 + build_number: 1 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + sha256: 2681c2a249752bdc7978e59ee2f34fcdfcbfda80029b84b8e5fec8dbc9e3af25 + md5: ffcb8e97e62af42075e0e5f46bb9856e + depends: + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 392496 + timestamp: 1725305808244 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hef9b889_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b + md5: 8b7069e9792ee4e5b4919a7a306d2e67 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 419552 + timestamp: 1725305670210 +- kind: conda + name: zstd + version: 1.5.6 + build: h02f22dd_0 + subdir: linux-aarch64 + url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c + md5: be8d5f8cf21aed237b8b182ea86b3dd6 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 539937 + timestamp: 1714723130243 +- kind: conda + name: zstd + version: 1.5.6 + build: ha6fb4c9_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 554846 + timestamp: 1714722996770 +- kind: conda + name: zstd + version: 1.5.6 + build: hb46c0d2_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda + sha256: 2d4fd1ff7ee79cd954ca8e81abf11d9d49954dd1fef80f27289e2402ae9c2e09 + md5: d96942c06c3e84bfcc5efb038724a7fd + depends: + - __osx >=11.0 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + size: 405089 + timestamp: 1714723101397 From b3a199c93fb3a8cbfd4e245a56bb80140123e8a0 Mon Sep 17 00:00:00 2001 From: rcghpge Date: Thu, 23 Jan 2025 04:54:13 -0600 Subject: [PATCH 2018/2019] FAQ docs reformat - minute typos --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index d3e11a6a59..3720c66164 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -29,7 +29,7 @@ name for a language that brings magical powers to Python, including unlocking an innovative programming model for accelerators and other heterogeneous systems pervasive in AI today. -### Why does mojo have the 🔥 file extension? +### Why does Mojo have the 🔥file extension? We paired Mojo with fire emoji 🔥 as a fun visual way to impart onto users that Mojo empowers them to get their Mojo on—to develop faster and more efficiently From 25ab22f1afa03869f7efbfd84220f4ff591ecdc0 Mon Sep 17 00:00:00 2001 From: rcghpge Date: Thu, 23 Jan 2025 04:58:26 -0600 Subject: [PATCH 2019/2019] FAQ docs reformat - minute typos --- docs/faq.md | 2 +- magic.lock | 6993 ++++++++++++++++----------------------------------- pixi.toml | 6 +- 3 files changed, 2240 insertions(+), 4761 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 3720c66164..f634c6ece3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -29,7 +29,7 @@ name for a language that brings magical powers to Python, including unlocking an innovative programming model for accelerators and other heterogeneous systems pervasive in AI today. -### Why does Mojo have the 🔥file extension? +### Why does Mojo have the 🔥 file extension? We paired Mojo with fire emoji 🔥 as a fun visual way to impart onto users that Mojo empowers them to get their Mojo on—to develop faster and more efficiently diff --git a/magic.lock b/magic.lock index 427e22126f..ed22ad1875 100644 --- a/magic.lock +++ b/magic.lock @@ -1,4 +1,4 @@ -version: 5 +version: 6 environments: default: channels: @@ -9,24 +9,24 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.11-py312h178313f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.1-h205f482_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h173a860_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.9-he1b24dc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.2-h4e1184b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.9-he0e7f3f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-h4d475cb_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda @@ -39,8 +39,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda @@ -49,26 +49,26 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.12.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.27.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 @@ -76,11 +76,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_hbbce691_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-19.0.0-h34b28d8_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-19.0.0-hcb10f89_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-19.0.0-hcb10f89_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-19.0.0-h08228c5_5_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda @@ -89,7 +89,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20240808-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda @@ -99,9 +99,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.34.0-h2b5623c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.34.0-h0121fbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-h25350d4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda @@ -109,26 +109,28 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.18.0-hfcad708_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.18.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-19.0.0-h081d1f1_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.45-h943b412_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.3-h6128344_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.48.0-hee588c1_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.10.0-h4c51ac1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda @@ -141,10 +143,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.11.3-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-h7b32b05_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda @@ -152,23 +155,24 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h12ee42a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py312h80c1187_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.3-py312h2ec8cdc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-19.0.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-19.0.0-py312h01725c0_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.5-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.2-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda @@ -176,75 +180,76 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.11-h072c03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.5.2-py312h12e396e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.45.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.48.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.4-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.2-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h178313f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda linux-aarch64: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.11-py312hcc812fe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.1-hb7ec8d5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-hfd5ba81_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.9-h05b070f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.2-h0f0193d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.9-hab3ce66_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h88461e4_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda @@ -257,8 +262,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda @@ -267,27 +272,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.12.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.27.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 @@ -295,11 +300,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h18dbdb1_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h4065667_12_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_12_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_12_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h1e9d426_12_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda @@ -308,7 +313,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20240808-pl5321h976ea20_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda @@ -318,9 +323,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.34.0-hccf9d24_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.34.0-hb9b2b65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-hf7ccdd3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda @@ -328,26 +333,26 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_12_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.45-hec79eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.3-h44a3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.48.0-h5eb1b54_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.10.0-ha346350_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.50.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.5.0-h0886dbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda @@ -360,10 +365,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-hd08dc88_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda @@ -371,23 +376,23 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-hdd485aa_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.1.0-py312h719f0cf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.3-py312h6f74592_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.5-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.2-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda @@ -395,74 +400,74 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hcc812fe_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-haa97905_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.11-h3caee7a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.5.2-py312h8cbf658_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.45.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.48.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.4-py312h8cbf658_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.2-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.2-py312hb2c0f52_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hcc812fe_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.11-py312h998013c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.1-hfc2798a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.9-hf37e03c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.2-hc8a0bd2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.9-ha81f72f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-h0e5014b_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda @@ -475,8 +480,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda @@ -485,37 +490,37 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.12.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.27.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-19.0.0-h0ec2bd1_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-19.0.0-hf07054f_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-19.0.0-hf07054f_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-19.0.0-h4239455_5_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda @@ -523,41 +528,43 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.7-ha82da77_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20240808-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.34.0-hdbe95d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.34.0-h7081f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.18.0-h0c05b2d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.18.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-19.0.0-h636d7b7_5_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.45-h3783ad8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.48.0-h3f77e49_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.10.0-hda25de7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.50.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.5.0-h2471fea_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.7-hdb05f8b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda @@ -570,10 +577,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.11.3-h00cdb27_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h81ee809_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda @@ -581,23 +589,24 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-h0ff2369_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.1.0-py312h50aef2c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.3-py312hd8f9ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-19.0.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-19.0.0-py312hc40f475_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.5-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.2-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda @@ -605,72 +614,63 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.5.2-py312hcd83bfe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.45.2-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.48.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.4-py312hcd83bfe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.2-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.2-py312hea69d52_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312h998013c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda packages: -- kind: conda - name: _libgcc_mutex - version: '0.1' - build: conda_forge - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 md5: d7c89558ba9fa0495403155b64376d81 license: None size: 2562 timestamp: 1578324546067 -- kind: conda - name: _openmp_mutex - version: '4.5' - build: 2_gnu +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 build_number: 16 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 md5: 73aaf86a425cc6e73fcf236a5a46396d depends: @@ -682,13 +682,8 @@ packages: license_family: BSD size: 23621 timestamp: 1650670423406 -- kind: conda - name: _openmp_mutex - version: '4.5' - build: 2_gnu +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 build_number: 16 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 md5: 6168d71addc746e8f2b8d57dfd2edcea depends: @@ -699,14 +694,7 @@ packages: license_family: BSD size: 23712 timestamp: 1650670790230 -- kind: conda - name: aiohappyeyeballs - version: 2.4.4 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.4.4-pyhd8ed1ab_1.conda sha256: 95d4713e49ea92ae50cf42393683ede706b7875af5f7cb14c253438180afa732 md5: 296b403617bafa89df4971567af79013 depends: @@ -715,14 +703,9 @@ packages: license_family: PSF size: 19351 timestamp: 1733332029649 -- kind: conda - name: aiohttp - version: 3.11.10 - build: py312h178313f_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.10-py312h178313f_0.conda - sha256: dc8ebdd99e9d7a07454a7063a295cdc7a86264648647fec10b2ceae97478e200 - md5: 3e92784b8e32ab7d0b95ee296ba79a99 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.11.11-py312h178313f_0.conda + sha256: 2e817805e8a4fed33f23f116ff5649f8651af693328e9ed82d9d11a951338693 + md5: 8219afa093757bbe07b9825eb1973ed9 depends: - __glibc >=2.17,<3.0.a0 - aiohappyeyeballs >=2.3.0 @@ -737,22 +720,17 @@ packages: - yarl >=1.17.0,<2.0 license: MIT AND Apache-2.0 license_family: Apache - size: 914378 - timestamp: 1733839626367 -- kind: conda - name: aiohttp - version: 3.11.10 - build: py312h998013c_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.10-py312h998013c_0.conda - sha256: 69eb9c89dce6a7ae960099172daffba9f77fef39344f37e581685a8e3c5debe6 - md5: 642356223364539ba7ba36556fcf49ee + size: 915358 + timestamp: 1734597073870 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.11-py312hcc812fe_0.conda + sha256: f28e81e458d19df4ca0002f8a92d7f647fa25e8179887a8676801dfe44edb1f2 + md5: 11fa88136d9bf39d2136b2378f7c10be depends: - - __osx >=11.0 - aiohappyeyeballs >=2.3.0 - aiosignal >=1.1.2 - attrs >=17.3.0 - frozenlist >=1.1.1 + - libgcc >=13 - multidict >=4.5,<7.0 - propcache >=0.2.0 - python >=3.12,<3.13.0a0 @@ -761,22 +739,17 @@ packages: - yarl >=1.17.0,<2.0 license: MIT AND Apache-2.0 license_family: Apache - size: 874135 - timestamp: 1733839113411 -- kind: conda - name: aiohttp - version: 3.11.10 - build: py312hcc812fe_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aiohttp-3.11.10-py312hcc812fe_0.conda - sha256: df694a9fec546e575a4ea7e1c3ac476c0bda53c0fad44c046ad3ebdd5b75a0a8 - md5: a8c9ec59e6323b38418bbf04deaa0c02 + size: 902422 + timestamp: 1734597104529 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aiohttp-3.11.11-py312h998013c_0.conda + sha256: 446f078e7a7b892894d7f4851a278b7834ffb4f5632313646a55c3abe13690d4 + md5: c69c904691364cfb27d15aa7153e9c29 depends: + - __osx >=11.0 - aiohappyeyeballs >=2.3.0 - aiosignal >=1.1.2 - attrs >=17.3.0 - frozenlist >=1.1.1 - - libgcc >=13 - multidict >=4.5,<7.0 - propcache >=0.2.0 - python >=3.12,<3.13.0a0 @@ -785,15 +758,9 @@ packages: - yarl >=1.17.0,<2.0 license: MIT AND Apache-2.0 license_family: Apache - size: 900931 - timestamp: 1733839037447 -- kind: conda - name: aiosignal - version: 1.3.2 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda + size: 875711 + timestamp: 1734597277258 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.3.2-pyhd8ed1ab_0.conda sha256: 7de8ced1918bbdadecf8e1c1c68237fe5709c097bd9e0d254f4cad118f4345d0 md5: 1a3981115a398535dbe3f6d5faae3d36 depends: @@ -803,14 +770,7 @@ packages: license_family: APACHE size: 13229 timestamp: 1734342253061 -- kind: conda - name: annotated-types - version: 0.7.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 md5: 2934f256a8acfe48f6ebb4fce6cde29c depends: @@ -820,15 +780,9 @@ packages: license_family: MIT size: 18074 timestamp: 1733247158254 -- kind: conda - name: anyio - version: 4.7.0 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/anyio-4.7.0-pyhd8ed1ab_0.conda - sha256: 687537ee3af30f8784986bf40cac30e88138770b16e51ca9850c9c23c09aeba1 - md5: c88107912954a983c2caf25f7fd55158 +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda + sha256: f1455d2953e3eb6d71bc49881c8558d8e01888469dfd21061dd48afb6183e836 + md5: 848d25bfbadf020ee4d4ba90e5668252 depends: - exceptiongroup >=1.0.2 - idna >=2.8 @@ -840,15 +794,9 @@ packages: - uvloop >=0.21 license: MIT license_family: MIT - size: 112730 - timestamp: 1733532678437 -- kind: conda - name: attrs - version: 24.3.0 - build: pyh71513ae_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda + size: 115305 + timestamp: 1736174485476 +- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.3.0-pyh71513ae_0.conda sha256: 750186af694a7130eaf7119fbb56db0d2326d8995ad5b8eae23c622b85fea29a md5: 356927ace43302bf6f5926e2a58dae6a depends: @@ -857,74 +805,50 @@ packages: license_family: MIT size: 56354 timestamp: 1734348889193 -- kind: conda - name: aws-c-auth - version: 0.8.0 - build: h2cb9fb3_15 - build_number: 15 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.0-h2cb9fb3_15.conda - sha256: 4ce859dc9ff128bf5515604c43f33fb511386022fc9765ca077990f2a3f23df5 - md5: e524686ace966acefb5b8cbc6e8b3daa +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.1-h205f482_0.conda + sha256: ebe5e33249f37f6bb481de99581ebdc92dbfcf1b6915609bcf3c9e78661d6352 + md5: 9c500858e88df50af3cc883d194de78a depends: + - __glibc >=2.17,<3.0.a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - aws-c-sdkutils >=0.2.2,<0.2.3.0a0 - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 111854 - timestamp: 1734021745104 -- kind: conda - name: aws-c-auth - version: 0.8.0 - build: h8bc59a9_15 - build_number: 15 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-h8bc59a9_15.conda - sha256: 0e41e56b662e76e024182adebcd91d09a4d38a83b35217c84e4967354dfff9a2 - md5: f688b8893c20ad9477a19e7ce614014a + size: 108111 + timestamp: 1737509831651 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-auth-0.8.1-hb7ec8d5_0.conda + sha256: e1e6c9267a4d9af30b1d28c11a9041b17b7840ff83ef08614145a2a4f444f5b4 + md5: 2630f030652970a5531e492f6b2a6dd3 depends: - - __osx >=11.0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - aws-c-sdkutils >=0.2.2,<0.2.3.0a0 + - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 92507 - timestamp: 1734021831330 -- kind: conda - name: aws-c-auth - version: 0.8.0 - build: hb921021_15 - build_number: 15 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-hb921021_15.conda - sha256: 537006ad6d5097c134494166a6a1dc1451d5d050878d7b82cef498bfda40ba8a - md5: c79d50f64cffa5ad51ecc1a81057962f + size: 112658 + timestamp: 1737509863269 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.1-hfc2798a_0.conda + sha256: 5a60d196a585b25d1446fb973009e4e648e8d70beaa2793787243ede6da0fd9a + md5: 0abd67c0f7b60d50348fbb32fef50b65 depends: - - __glibc >=2.17,<3.0.a0 + - __osx >=11.0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 - - libgcc >=13 + - aws-c-sdkutils >=0.2.2,<0.2.3.0a0 license: Apache-2.0 license_family: Apache - size: 107614 - timestamp: 1734021692519 -- kind: conda - name: aws-c-cal - version: 0.8.1 - build: h1a47875_3 - build_number: 3 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda + size: 92562 + timestamp: 1737509877079 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.1-h1a47875_3.conda sha256: 095ac824ea9303eff67e04090ae531d9eb33d2bf8f82eaade39b839c421e16e8 md5: 55a8561fdbbbd34f50f57d9be12ed084 depends: @@ -936,13 +860,7 @@ packages: license_family: Apache size: 47601 timestamp: 1733991564405 -- kind: conda - name: aws-c-cal - version: 0.8.1 - build: h740c5af_3 - build_number: 3 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-cal-0.8.1-h740c5af_3.conda sha256: c5c7961d48ca7320ed3560c036f7aa5244df4b85d9657f70aacc5faba3e1509a md5: 57ed2c445d7ef01d121b9bcea0522913 depends: @@ -953,13 +871,7 @@ packages: license_family: Apache size: 50036 timestamp: 1733991581303 -- kind: conda - name: aws-c-cal - version: 0.8.1 - build: hc8a0bd2_3 - build_number: 3 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda sha256: 1f44be36e1daa17b4b081debb8aee492d13571084f38b503ad13e869fef24fe4 md5: 8b0ce61384e5a33d2b301a64f3d22ac5 depends: @@ -970,26 +882,17 @@ packages: license_family: Apache size: 39925 timestamp: 1733991649383 -- kind: conda - name: aws-c-common - version: 0.10.6 - build: h5505292_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda - sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a - md5: 145e5b4c9702ed279d7d68aaf096f77d +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda + sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 + md5: d7d4680337a14001b0e043e96529409b depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 221863 - timestamp: 1733975576886 -- kind: conda - name: aws-c-common - version: 0.10.6 - build: h86ecc28_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda + size: 236574 + timestamp: 1733975453350 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-common-0.10.6-h86ecc28_0.conda sha256: 57288ec5df35781bea8fc6a8c9099cad6695b747784fc1b8862a0f9e5b3bf5ab md5: fef806a0f6de853670c746bbece01966 depends: @@ -998,28 +901,27 @@ packages: license_family: Apache size: 259031 timestamp: 1733975520465 -- kind: conda - name: aws-c-common - version: 0.10.6 - build: hb9d3cd8_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.10.6-hb9d3cd8_0.conda - sha256: 496e92f2150fdc351eacf6e236015deedb3d0d3114f8e5954341cbf9f3dda257 - md5: d7d4680337a14001b0e043e96529409b +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda + sha256: 3bde135c8e74987c0f79ecd4fa17ec9cff0d658b3090168727ca1af3815ae57a + md5: 145e5b4c9702ed279d7d68aaf096f77d + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 221863 + timestamp: 1733975576886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda + sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b + md5: 3f4c1197462a6df2be6dc8241828fe93 depends: - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 236574 - timestamp: 1733975453350 -- kind: conda - name: aws-c-compression - version: 0.3.0 - build: h0f0193d_5 - build_number: 5 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda + size: 19086 + timestamp: 1733991637424 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-compression-0.3.0-h0f0193d_5.conda sha256: 3f05d19f68ef800f33d44ea2a4211003124076516c8469abc7d432236344df53 md5: 3a1421d12435df5b4c412cc4c8fac64d depends: @@ -1029,30 +931,7 @@ packages: license_family: Apache size: 19740 timestamp: 1733991625201 -- kind: conda - name: aws-c-compression - version: 0.3.0 - build: h4e1184b_5 - build_number: 5 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-h4e1184b_5.conda - sha256: 62ca84da83585e7814a40240a1e750b1563b2680b032a471464eccc001c3309b - md5: 3f4c1197462a6df2be6dc8241828fe93 - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.10.6,<0.10.7.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - size: 19086 - timestamp: 1733991637424 -- kind: conda - name: aws-c-compression - version: 0.3.0 - build: hc8a0bd2_5 - build_number: 5 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda sha256: 47b2813f652ce7e64ac442f771b2a5f7d4af4ad0d07ff51f6075ea80ed2e3f09 md5: a8b6c17732d14ed49d0e9b59c43186bc depends: @@ -1062,32 +941,7 @@ packages: license_family: Apache size: 18068 timestamp: 1733991869211 -- kind: conda - name: aws-c-event-stream - version: 0.5.0 - build: h54f970a_11 - build_number: 11 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda - sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d - md5: ba41238f8e653998d7d2f42e3a8db054 - depends: - - __osx >=11.0 - - aws-c-common >=0.10.6,<0.10.7.0a0 - - aws-c-io >=0.15.3,<0.15.4.0a0 - - aws-checksums >=0.2.2,<0.2.3.0a0 - - libcxx >=18 - license: Apache-2.0 - license_family: Apache - size: 47078 - timestamp: 1734024749727 -- kind: conda - name: aws-c-event-stream - version: 0.5.0 - build: h7959bf6_11 - build_number: 11 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h7959bf6_11.conda sha256: 10d7240c7db0c941fb1a59c4f8ea6689a434b03309ee7b766fa15a809c553c02 md5: 9b3fb60fe57925a92f399bc3fc42eccf depends: @@ -1101,13 +955,7 @@ packages: license_family: Apache size: 54003 timestamp: 1734024480949 -- kind: conda - name: aws-c-event-stream - version: 0.5.0 - build: hcbd8f92_11 - build_number: 11 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-event-stream-0.5.0-hcbd8f92_11.conda sha256: 79aa363c71c891a27496c0498f8d498b2ddc87b3ccb3b6c9da5b50b05936ebb8 md5: e0772c59af4243a9b2565baa5d79e5b6 depends: @@ -1120,13 +968,34 @@ packages: license_family: Apache size: 55207 timestamp: 1734024546663 -- kind: conda - name: aws-c-http - version: 0.9.2 - build: h3df160d_4 - build_number: 4 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda + sha256: f0667935f4e0d4c25e0e51da035640310b5ceeb8f723156734439bde8b848d7d + md5: ba41238f8e653998d7d2f42e3a8db054 + depends: + - __osx >=11.0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 47078 + timestamp: 1734024749727 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda + sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 + md5: 5ce4df662d32d3123ea8da15571b6f51 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 + - aws-c-common >=0.10.6,<0.10.7.0a0 + - aws-c-compression >=0.3.0,<0.3.1.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 197731 + timestamp: 1734008380764 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-http-0.9.2-h3df160d_4.conda sha256: 3a1d2d332945306be9d49e569b95e4cc172d825f10e88715513a172f28ebb59e md5: 28f00aa7fd9556c4c461328cf146c20b depends: @@ -1139,13 +1008,7 @@ packages: license_family: Apache size: 190586 timestamp: 1734008442362 -- kind: conda - name: aws-c-http - version: 0.9.2 - build: h96aa502_4 - build_number: 4 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda sha256: 22e4737c8a885995b7c1ae1d79c1f6e78d489e16ec079615980fdde067aeaf76 md5: 495c93a4f08b17deb3c04894512330e6 depends: @@ -1158,87 +1021,43 @@ packages: license_family: Apache size: 152983 timestamp: 1734008451473 -- kind: conda - name: aws-c-http - version: 0.9.2 - build: hefd7a92_4 - build_number: 4 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.2-hefd7a92_4.conda - sha256: 4a330206bd51148f6c13ca0b7a4db40f29a46f090642ebacdeb88b8a4abd7f99 - md5: 5ce4df662d32d3123ea8da15571b6f51 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h173a860_6.conda + sha256: 335d822eead0a097ffd23677a288e1f18ea22f47a92d4f877419debb93af0e81 + md5: 9a063178f1af0a898526cc24ba7be486 depends: - __glibc >=2.17,<3.0.a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - - aws-c-compression >=0.3.0,<0.3.1.0a0 - - aws-c-io >=0.15.3,<0.15.4.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - size: 197731 - timestamp: 1734008380764 -- kind: conda - name: aws-c-io - version: 0.15.3 - build: h1a307af_5 - build_number: 5 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-h1a307af_5.conda - sha256: 71f5bf891299f831dceaea12f926c393bf754569e5305387a88b77e1f94612d8 - md5: da8ab0f3eeac93449ec3d531ede92caa - depends: - - aws-c-cal >=0.8.1,<0.8.2.0a0 - - aws-c-common >=0.10.6,<0.10.7.0a0 - libgcc >=13 - - s2n >=1.5.10,<1.5.11.0a0 + - s2n >=1.5.11,<1.5.12.0a0 license: Apache-2.0 license_family: Apache - size: 161889 - timestamp: 1734433686109 -- kind: conda - name: aws-c-io - version: 0.15.3 - build: h831e299_5 - build_number: 5 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.3-h831e299_5.conda - sha256: 5920009b1c6f9a2bc131a36725251894e4b4773fce29c4b1065d4213ae337abe - md5: 80dd9f0ddf935290d1dc00ec75ff3023 + size: 157263 + timestamp: 1737207617838 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-io-0.15.3-hfd5ba81_6.conda + sha256: d8adfde05cee11057d99c3802e84b2300430a7c7c3c9936165d1d4b25ef59d91 + md5: 4e6771b45cb2b035c62d023dbf0dc000 depends: - - __glibc >=2.17,<3.0.a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - libgcc >=13 - - s2n >=1.5.10,<1.5.11.0a0 + - s2n >=1.5.11,<1.5.12.0a0 license: Apache-2.0 license_family: Apache - size: 157864 - timestamp: 1734433578570 -- kind: conda - name: aws-c-io - version: 0.15.3 - build: haba67d1_5 - build_number: 5 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_5.conda - sha256: c0a1a2b0750225ac3dc07fd258c88c2be866bf8ac67ba3d50bb4ecec852ff8ee - md5: 4c5ff4134e76426a75b8c548984fa933 + size: 160933 + timestamp: 1737207637279 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_6.conda + sha256: 73722dd175af78b6cbfa033066f0933351f5382a1a737f6c6d9b8cfa84022161 + md5: d02e8f40ff69562903e70a1c6c48b009 depends: - __osx >=11.0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 license: Apache-2.0 license_family: Apache - size: 135729 - timestamp: 1734433832730 -- kind: conda - name: aws-c-mqtt - version: 0.11.0 - build: h11f4f37_12 - build_number: 12 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda + size: 136048 + timestamp: 1737207681224 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h11f4f37_12.conda sha256: 512d3969426152d9d5fd886e27b13706122dc3fa90eb08c37b0d51a33d7bb14a md5: 96c3e0221fa2da97619ee82faa341a73 depends: @@ -1251,31 +1070,7 @@ packages: license_family: Apache size: 194672 timestamp: 1734025626798 -- kind: conda - name: aws-c-mqtt - version: 0.11.0 - build: h24f418c_12 - build_number: 12 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda - sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd - md5: c072045a6206f88015d02fcba1705ea1 - depends: - - __osx >=11.0 - - aws-c-common >=0.10.6,<0.10.7.0a0 - - aws-c-http >=0.9.2,<0.9.3.0a0 - - aws-c-io >=0.15.3,<0.15.4.0a0 - license: Apache-2.0 - license_family: Apache - size: 134371 - timestamp: 1734025379525 -- kind: conda - name: aws-c-mqtt - version: 0.11.0 - build: h5f50e26_12 - build_number: 12 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-mqtt-0.11.0-h5f50e26_12.conda sha256: ffeb9100cc8fd4093e1a6fdfd938bc4a396dd77480b7fb17aa42855a4d5e2c70 md5: 031ca33115d4b1eeb43f435d6215778c depends: @@ -1287,36 +1082,24 @@ packages: license_family: Apache size: 169516 timestamp: 1734025167885 -- kind: conda - name: aws-c-s3 - version: 0.7.7 - build: h1be5864_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.7-h1be5864_0.conda - sha256: 22966164d63808689fffd35945f57756c95337327e28099b5d77b29fc6a56ecc - md5: a37bba7acb62dd70492ee01eacca3b8f +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda + sha256: 96575ea1dd2a9ea94763882e40a66dcbff9c41f702bf37c9514c4c719b3c11dd + md5: c072045a6206f88015d02fcba1705ea1 depends: - __osx >=11.0 - - aws-c-auth >=0.8.0,<0.8.1.0a0 - - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - - aws-checksums >=0.2.2,<0.2.3.0a0 license: Apache-2.0 license_family: Apache - size: 97598 - timestamp: 1734146239038 -- kind: conda - name: aws-c-s3 - version: 0.7.7 - build: h2080895_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.7-h2080895_0.conda - sha256: 20bc2dd60e6518d9b8215c2b652ab5c52ee8a997d3b9a5f69e2dabd28cbf26b2 - md5: ae223efa63fbb4262a2d85c3ab3bc4f5 - depends: - - aws-c-auth >=0.8.0,<0.8.1.0a0 + size: 134371 + timestamp: 1734025379525 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.9-he1b24dc_1.conda + sha256: 15fbdedc56850f8be5be7a5bcaea1af09c97590e631c024ae089737fc932fc42 + md5: caafc32928a5f7f3f7ef67d287689144 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.1,<0.8.2.0a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 @@ -1326,19 +1109,13 @@ packages: - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 117641 - timestamp: 1734146239779 -- kind: conda - name: aws-c-s3 - version: 0.7.7 - build: hf454442_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.7-hf454442_0.conda - sha256: c2f205a7bf64c5f40eea373b3a0a7c363c9aa9246a13dd7f3d9c6a4434c4fe2d - md5: 947c82025693bebd557f782bb5d6b469 + size: 115413 + timestamp: 1737558687616 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-s3-0.7.9-h05b070f_1.conda + sha256: 9e7857fbc33eddc291fa09890ce468a92485d3e3e127bc00bbd1803e34121f84 + md5: e0a2869195f069db88b8932f5b00bee5 depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-auth >=0.8.0,<0.8.1.0a0 + - aws-c-auth >=0.8.1,<0.8.2.0a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 @@ -1348,80 +1125,55 @@ packages: - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 114156 - timestamp: 1734146123386 -- kind: conda - name: aws-c-sdkutils - version: 0.2.1 - build: h0f0193d_4 - build_number: 4 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.1-h0f0193d_4.conda - sha256: ede8e782467c87ac80ceb9c9af9e917d121b7d8b8c698186d18e3cecd36f2210 - md5: 53e798d720dd78b78847a7b2fdb05fc9 + size: 117875 + timestamp: 1737558720047 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.9-hf37e03c_1.conda + sha256: 92e8ca4eefcbbdf4189584c9410382884a06ed3030e5ecaac656dab8c95e6a80 + md5: de65f5e4ab5020103fe70a0eba9432a0 depends: + - __osx >=11.0 + - aws-c-auth >=0.8.1,<0.8.2.0a0 + - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - - libgcc >=13 + - aws-c-http >=0.9.2,<0.9.3.0a0 + - aws-c-io >=0.15.3,<0.15.4.0a0 + - aws-checksums >=0.2.2,<0.2.3.0a0 license: Apache-2.0 license_family: Apache - size: 58621 - timestamp: 1733994421495 -- kind: conda - name: aws-c-sdkutils - version: 0.2.1 - build: h4e1184b_4 - build_number: 4 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.1-h4e1184b_4.conda - sha256: df586f42210af1134b1c88ff4c278c3cb6d6c807c84eac48860062464b28554d - md5: a5126a90e74ac739b00564a4c7ddcc36 + size: 98731 + timestamp: 1737558731831 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.2-h4e1184b_0.conda + sha256: 0424e380c435ba03b5948d02e8c958866c4eee50ed29e57f99473a5f795a4cfc + md5: dcd498d493818b776a77fbc242fbf8e4 depends: - __glibc >=2.17,<3.0.a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 56094 - timestamp: 1733994449690 -- kind: conda - name: aws-c-sdkutils - version: 0.2.1 - build: hc8a0bd2_4 - build_number: 4 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.1-hc8a0bd2_4.conda - sha256: de98343ce42d2e569b3380292d20f47bf39bda08aadabcbb8e650d3f38fd742f - md5: 22f72f8cd7ead211304ac17d337d96e0 + size: 55911 + timestamp: 1736535960724 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-c-sdkutils-0.2.2-h0f0193d_0.conda + sha256: fba38e469457764afcb94aa84d4d7788e6b5fa1554d34b05c904d2245fdd3c81 + md5: a78928881c652facde2a13ec6e776f3c depends: - - __osx >=11.0 - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 49664 - timestamp: 1733994553014 -- kind: conda - name: aws-checksums - version: 0.2.2 - build: h0f0193d_4 - build_number: 4 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda - sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da - md5: 3bd35b0adab3d743f09e0252cc441d6b + size: 58221 + timestamp: 1736536003041 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.2-hc8a0bd2_0.conda + sha256: ea4f0f1e99056293c69615f581a997d65ba7e229e296e402e0d8ef750648a5b5 + md5: e7b5498ac7b7ab921a907be38f3a8080 depends: + - __osx >=11.0 - aws-c-common >=0.10.6,<0.10.7.0a0 - - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 72154 - timestamp: 1733994384415 -- kind: conda - name: aws-checksums - version: 0.2.2 - build: h4e1184b_4 - build_number: 4 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda + size: 49872 + timestamp: 1736536152332 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.2-h4e1184b_4.conda sha256: 1ed9a332d06ad595694907fad2d6d801082916c27cd5076096fda4061e6d24a8 md5: 74e8c3e4df4ceae34aa2959df4b28101 depends: @@ -1432,13 +1184,17 @@ packages: license_family: Apache size: 72762 timestamp: 1733994347547 -- kind: conda - name: aws-checksums - version: 0.2.2 - build: hc8a0bd2_4 - build_number: 4 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-checksums-0.2.2-h0f0193d_4.conda + sha256: 9f1e3635a587bcf92b61d88c7af7d24cd89c147c6d0ae58afbde08e65bcf48da + md5: 3bd35b0adab3d743f09e0252cc441d6b + depends: + - aws-c-common >=0.10.6,<0.10.7.0a0 + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + size: 72154 + timestamp: 1733994384415 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda sha256: 215086d95e8ff1d3fcb0197ada116cc9d7db1fdae7573f5e810d20fa9215b47c md5: e70e88a357a3749b67679c0788c5b08a depends: @@ -1448,96 +1204,73 @@ packages: license_family: Apache size: 70186 timestamp: 1733994496998 -- kind: conda - name: aws-crt-cpp - version: 0.29.7 - build: h19a973c_7 - build_number: 7 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.7-h19a973c_7.conda - sha256: 8269e6746eb3a5d15b732a3983888bf98dfc1f6594e95250fc8d16b43cfd5ff9 - md5: 95714136bef3e917bd5a2942d4682b20 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.9-he0e7f3f_2.conda + sha256: c1930569713bd5231d48d885a5e3707ac917b428e8f08189d14064a2bb128adc + md5: 8a4e6fc8a3b285536202b5456a74a940 depends: - - __osx >=11.0 - - aws-c-auth >=0.8.0,<0.8.1.0a0 + - __glibc >=2.17,<3.0.a0 + - aws-c-auth >=0.8.1,<0.8.2.0a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-event-stream >=0.5.0,<0.5.1.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - aws-c-mqtt >=0.11.0,<0.11.1.0a0 - - aws-c-s3 >=0.7.7,<0.7.8.0a0 - - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 - - libcxx >=18 + - aws-c-s3 >=0.7.9,<0.7.10.0a0 + - aws-c-sdkutils >=0.2.2,<0.2.3.0a0 + - libgcc >=13 + - libstdcxx >=13 license: Apache-2.0 license_family: Apache - size: 236249 - timestamp: 1734178020924 -- kind: conda - name: aws-crt-cpp - version: 0.29.7 - build: h8a4e35f_7 - build_number: 7 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.7-h8a4e35f_7.conda - sha256: 5ba9188e0cb4e3faff9bc96774febb040aa3b802aedba29d847e00e7b5eab84e - md5: d77a9e3d7ce15399903e92825fd651b5 - depends: - - aws-c-auth >=0.8.0,<0.8.1.0a0 + size: 353222 + timestamp: 1737565463079 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-crt-cpp-0.29.9-hab3ce66_2.conda + sha256: c3884fb10e0fd9be5c13256cabcda1afa9d2fcf8878c67214d02fc344509857d + md5: 875968ebffe992b68faf2caebbf32f02 + depends: + - aws-c-auth >=0.8.1,<0.8.2.0a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-event-stream >=0.5.0,<0.5.1.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - aws-c-mqtt >=0.11.0,<0.11.1.0a0 - - aws-c-s3 >=0.7.7,<0.7.8.0a0 - - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 + - aws-c-s3 >=0.7.9,<0.7.10.0a0 + - aws-c-sdkutils >=0.2.2,<0.2.3.0a0 - libgcc >=13 - libstdcxx >=13 license: Apache-2.0 license_family: Apache - size: 283154 - timestamp: 1734177845248 -- kind: conda - name: aws-crt-cpp - version: 0.29.7 - build: hd92328a_7 - build_number: 7 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.7-hd92328a_7.conda - sha256: 094cd81f1e5ba713e9e7a272ee52b5dde3ccc4842ea90f19c0354a00bbdac3d9 - md5: 02b95564257d5c3db9c06beccf711f95 + size: 283812 + timestamp: 1737565480034 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.9-ha81f72f_2.conda + sha256: ed5f1d19aad53787fdebe13db4709c97eae2092536cc55d3536eba320c4286e1 + md5: c9c034d3239bf25687ca4dd985007ecd depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-auth >=0.8.0,<0.8.1.0a0 + - __osx >=11.0 + - aws-c-auth >=0.8.1,<0.8.2.0a0 - aws-c-cal >=0.8.1,<0.8.2.0a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-event-stream >=0.5.0,<0.5.1.0a0 - aws-c-http >=0.9.2,<0.9.3.0a0 - aws-c-io >=0.15.3,<0.15.4.0a0 - aws-c-mqtt >=0.11.0,<0.11.1.0a0 - - aws-c-s3 >=0.7.7,<0.7.8.0a0 - - aws-c-sdkutils >=0.2.1,<0.2.2.0a0 - - libgcc >=13 - - libstdcxx >=13 + - aws-c-s3 >=0.7.9,<0.7.10.0a0 + - aws-c-sdkutils >=0.2.2,<0.2.3.0a0 + - libcxx >=18 license: Apache-2.0 license_family: Apache - size: 354703 - timestamp: 1734177883319 -- kind: conda - name: aws-sdk-cpp - version: 1.11.458 - build: h849ce1a_4 - build_number: 4 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h849ce1a_4.conda - sha256: 51b9e9df8cbab4a13a1b9d39d6ef5ed162aaa29c09a745810e00bbe92e1045c1 - md5: cda7747f4398be8d1fb37362815917a7 + size: 235976 + timestamp: 1737565563139 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-h4d475cb_6.conda + sha256: 2309d96d537b5c3810c6e9fbf5b6bb7e06ce907a609d172063ab504b2cc67f30 + md5: 6139e84bbb6fdb27ca49c2981613a5fa depends: + - __glibc >=2.17,<3.0.a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-event-stream >=0.5.0,<0.5.1.0a0 - aws-checksums >=0.2.2,<0.2.3.0a0 - - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-crt-cpp >=0.29.9,<0.29.10.0a0 - libcurl >=8.11.1,<9.0a0 - libgcc >=13 - libstdcxx >=13 @@ -1545,23 +1278,16 @@ packages: - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 2920625 - timestamp: 1734093552712 -- kind: conda - name: aws-sdk-cpp - version: 1.11.458 - build: hc430e4a_4 - build_number: 4 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.458-hc430e4a_4.conda - sha256: 2dc09f6f9c49127b5f96e7535b64a9c521b944d76d8b7d03d48ae80257ac1cea - md5: aeefac461bea1f126653c1285cf5af08 + size: 3069914 + timestamp: 1737049585807 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/aws-sdk-cpp-1.11.458-h88461e4_6.conda + sha256: f97e071ffb87bc33923f798761ec6a854614ce193913d34d3248515d18357baa + md5: 0d7f1ee2e5572bf9d1fd78f0f2ab6f6b depends: - - __glibc >=2.17,<3.0.a0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-event-stream >=0.5.0,<0.5.1.0a0 - aws-checksums >=0.2.2,<0.2.3.0a0 - - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-crt-cpp >=0.29.9,<0.29.10.0a0 - libcurl >=8.11.1,<9.0a0 - libgcc >=13 - libstdcxx >=13 @@ -1569,72 +1295,51 @@ packages: - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 3060561 - timestamp: 1734093737431 -- kind: conda - name: aws-sdk-cpp - version: 1.11.458 - build: he0ff2e4_4 - build_number: 4 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-he0ff2e4_4.conda - sha256: 535b970aaa13be45f8cab8205c59f044b17364111c41a227f061775a5c834e18 - md5: 0981ed87098b149bdb7d99a4a3fd0e58 + size: 2919798 + timestamp: 1737049659061 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.458-h0e5014b_6.conda + sha256: 8c21ce4692d1631b96adea099d7c2b905fc5d078db63bf754d85600dcf184651 + md5: a3b7b9a81d8c6a8f963f619d9e362c39 depends: - __osx >=11.0 - aws-c-common >=0.10.6,<0.10.7.0a0 - aws-c-event-stream >=0.5.0,<0.5.1.0a0 - aws-checksums >=0.2.2,<0.2.3.0a0 - - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-crt-cpp >=0.29.9,<0.29.10.0a0 - libcurl >=8.11.1,<9.0a0 - libcxx >=18 - libzlib >=1.3.1,<2.0a0 - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 2826534 - timestamp: 1734094018287 -- kind: conda - name: azure-core-cpp - version: 1.14.0 - build: h1887c18_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda - sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 - md5: e0c3a906a41be769f0ae20ca3e31cfc0 + size: 2834057 + timestamp: 1737126583846 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda + sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a + md5: 0a8838771cc2e985cd295e01ae83baf1 depends: + - __glibc >=2.17,<3.0.a0 - libcurl >=8.10.1,<9.0a0 - libgcc >=13 - libstdcxx >=13 - openssl >=3.3.2,<4.0a0 license: MIT license_family: MIT - size: 338650 - timestamp: 1728055589907 -- kind: conda - name: azure-core-cpp - version: 1.14.0 - build: h5cfcd09_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda - sha256: fe07debdb089a3db17f40a7f20d283d75284bb4fc269ef727b8ba6fc93f7cb5a - md5: 0a8838771cc2e985cd295e01ae83baf1 + size: 345117 + timestamp: 1728053909574 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-core-cpp-1.14.0-h1887c18_0.conda + sha256: 8967b3ccee4d74e61f6ec82dd8efb9deb854ee7ba012dfe767b7a92e0ac77724 + md5: e0c3a906a41be769f0ae20ca3e31cfc0 depends: - - __glibc >=2.17,<3.0.a0 - libcurl >=8.10.1,<9.0a0 - libgcc >=13 - libstdcxx >=13 - openssl >=3.3.2,<4.0a0 license: MIT license_family: MIT - size: 345117 - timestamp: 1728053909574 -- kind: conda - name: azure-core-cpp - version: 1.14.0 - build: hd50102c_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda + size: 338650 + timestamp: 1728055589907 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda sha256: f5b91329ed59ffc0be8747784c6e4cc7e56250c54032883a83bc11808ef6a87e md5: f093a11dcf3cdcca010b20a818fcc6dc depends: @@ -1646,12 +1351,7 @@ packages: license_family: MIT size: 294299 timestamp: 1728054014060 -- kind: conda - name: azure-identity-cpp - version: 1.10.0 - build: h113e628_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda sha256: 286b31616c191486626cb49e9ceb5920d29394b9e913c23adb7eb637629ba4de md5: 73f73f60854f325a55f1d31459f2ab73 depends: @@ -1664,12 +1364,7 @@ packages: license_family: MIT size: 232351 timestamp: 1728486729511 -- kind: conda - name: azure-identity-cpp - version: 1.10.0 - build: h47b0b28_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-identity-cpp-1.10.0-h47b0b28_0.conda sha256: 1c72423b9beba167d2f01b80dc204da77240a8266f1edb3d89510c852b300d69 md5: 94e73a7877743a85c57091d8afab2348 depends: @@ -1681,12 +1376,7 @@ packages: license_family: MIT size: 217132 timestamp: 1728488096615 -- kind: conda - name: azure-identity-cpp - version: 1.10.0 - build: hc602bab_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda sha256: bde446b916fff5150606f8ed3e6058ffc55a3aa72381e46f1ab346590b1ae40a md5: d7b71593a937459f2d4b67e1a4727dc2 depends: @@ -1698,50 +1388,32 @@ packages: license_family: MIT size: 166907 timestamp: 1728486882502 -- kind: conda - name: azure-storage-blobs-cpp - version: 12.13.0 - build: h185ecfd_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda - sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 - md5: 221e1e5ecb2643e113f32b3229d5ba33 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda + sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 + md5: 7eb66060455c7a47d9dcdbfa9f46579b depends: + - __glibc >=2.17,<3.0.a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 - libgcc >=13 - libstdcxx >=13 license: MIT license_family: MIT - size: 502934 - timestamp: 1728580241002 -- kind: conda - name: azure-storage-blobs-cpp - version: 12.13.0 - build: h3cf044e_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda - sha256: 2606260e5379eed255bcdc6adc39b93fb31477337bcd911c121fc43cd29bf394 - md5: 7eb66060455c7a47d9dcdbfa9f46579b + size: 549342 + timestamp: 1728578123088 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-blobs-cpp-12.13.0-h185ecfd_1.conda + sha256: 280ec70009a92626054f58e45b168fce393e71a9710587488bd8401628cda481 + md5: 221e1e5ecb2643e113f32b3229d5ba33 depends: - - __glibc >=2.17,<3.0.a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 - libgcc >=13 - libstdcxx >=13 license: MIT license_family: MIT - size: 549342 - timestamp: 1728578123088 -- kind: conda - name: azure-storage-blobs-cpp - version: 12.13.0 - build: h7585a09_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda + size: 502934 + timestamp: 1728580241002 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda sha256: 08d52d130addc0fb55d5ba10d9fa483e39be25d69bac7f4c676c2c3069207590 md5: 704238ef05d46144dae2e6b5853df8bc depends: @@ -1753,16 +1425,11 @@ packages: license_family: MIT size: 438636 timestamp: 1728578216193 -- kind: conda - name: azure-storage-common-cpp - version: 12.8.0 - build: h1b94036_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda - sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 - md5: 793b1080ab2d958980f137a8643cd6e8 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda + sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba + md5: 13de36be8de3ae3f05ba127631599213 depends: + - __glibc >=2.17,<3.0.a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - libgcc >=13 - libstdcxx >=13 @@ -1770,19 +1437,12 @@ packages: - openssl >=3.3.2,<4.0a0 license: MIT license_family: MIT - size: 140832 - timestamp: 1728565334900 -- kind: conda - name: azure-storage-common-cpp - version: 12.8.0 - build: h736e048_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda - sha256: 273475f002b091b66ce7366da04bf164c3732c03f8692ab2ee2d23335b6a82ba - md5: 13de36be8de3ae3f05ba127631599213 + size: 149312 + timestamp: 1728563338704 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-common-cpp-12.8.0-h1b94036_1.conda + sha256: 146e76aac169e3dbdce5d3b142b7930ac643795c765e7655d1989905ec7d3231 + md5: 793b1080ab2d958980f137a8643cd6e8 depends: - - __glibc >=2.17,<3.0.a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - libgcc >=13 - libstdcxx >=13 @@ -1790,15 +1450,9 @@ packages: - openssl >=3.3.2,<4.0a0 license: MIT license_family: MIT - size: 149312 - timestamp: 1728563338704 -- kind: conda - name: azure-storage-common-cpp - version: 12.8.0 - build: h9ca1f76_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda + size: 140832 + timestamp: 1728565334900 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda sha256: 77ab04e8fe5636a2de9c718f72a43645f7502cd208868c8a91ffba385547d585 md5: 7a187cd7b1445afc80253bb186a607cc depends: @@ -1811,16 +1465,11 @@ packages: license_family: MIT size: 121278 timestamp: 1728563418777 -- kind: conda - name: azure-storage-files-datalake-cpp - version: 12.12.0 - build: h37d6d07_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda - sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc - md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda + sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 + md5: 7c1980f89dd41b097549782121a73490 depends: + - __glibc >=2.17,<3.0.a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 @@ -1828,19 +1477,12 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT - size: 260547 - timestamp: 1728730924071 -- kind: conda - name: azure-storage-files-datalake-cpp - version: 12.12.0 - build: ha633028_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda - sha256: 5371e4f3f920933bb89b926a85a67f24388227419abd6e99f6086481e5e8d5f2 - md5: 7c1980f89dd41b097549782121a73490 + size: 287366 + timestamp: 1728729530295 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/azure-storage-files-datalake-cpp-12.12.0-h37d6d07_1.conda + sha256: 4079c617a75682e49bae63670d58fd6078ccfbbe55ca1f994acab3a74ab6bbcc + md5: b724f3b4b7f4e9b36c58cbe3ed8610a2 depends: - - __glibc >=2.17,<3.0.a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 - azure-storage-common-cpp >=12.8.0,<12.8.1.0a0 @@ -1848,15 +1490,9 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT - size: 287366 - timestamp: 1728729530295 -- kind: conda - name: azure-storage-files-datalake-cpp - version: 12.12.0 - build: hcdd55da_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda + size: 260547 + timestamp: 1728730924071 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda sha256: f48523f8aa0b5b80f45a92f0556b388dd96f44ac2dc2f44a01d08c1822eec97d md5: c49fbc5233fcbaa86391162ff1adef38 depends: @@ -1869,14 +1505,7 @@ packages: license_family: MIT size: 196032 timestamp: 1728729672889 -- kind: conda - name: backoff - version: 2.2.1 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 md5: a38b801f2bcc12af80c2e02a9e4ce7d9 depends: @@ -1885,13 +1514,7 @@ packages: license_family: MIT size: 18816 timestamp: 1733771192649 -- kind: conda - name: brotli-python - version: 1.1.0 - build: py312h2ec8cdc_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f md5: b0b867af6fc74b2a0aa206da29c0f3cf depends: @@ -1906,13 +1529,7 @@ packages: license_family: MIT size: 349867 timestamp: 1725267732089 -- kind: conda - name: brotli-python - version: 1.1.0 - build: py312h6f74592_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.1.0-py312h6f74592_2.conda sha256: 9736bf660a0e4260c68f81d2635b51067f817813e6490ac9e8abd9a835dcbf6d md5: e1e9727063057168d95f27a032acd0a4 depends: @@ -1927,13 +1544,7 @@ packages: license_family: MIT size: 356878 timestamp: 1725267878508 -- kind: conda - name: brotli-python - version: 1.1.0 - build: py312hde4cb15_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312hde4cb15_2.conda sha256: 254b411fa78ccc226f42daf606772972466f93e9bc6895eabb4cfda22f5178af md5: a83c2ef76ccb11bc2349f4f17696b15d depends: @@ -1948,13 +1559,7 @@ packages: license_family: MIT size: 339360 timestamp: 1725268143995 -- kind: conda - name: bzip2 - version: 1.0.8 - build: h4bc722e_7 - build_number: 7 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d md5: 62ee74e96c5ebb0af99386de58cf9553 depends: @@ -1964,13 +1569,7 @@ packages: license_family: BSD size: 252783 timestamp: 1720974456583 -- kind: conda - name: bzip2 - version: 1.0.8 - build: h68df207_7 - build_number: 7 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda sha256: 2258b0b33e1cb3a9852d47557984abb6e7ea58e3d7f92706ec1f8e879290c4cb md5: 56398c28220513b9ea13d7b450acfb20 depends: @@ -1979,13 +1578,7 @@ packages: license_family: BSD size: 189884 timestamp: 1720974504976 -- kind: conda - name: bzip2 - version: 1.0.8 - build: h99b78c6_7 - build_number: 7 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda sha256: adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91 md5: fc6948412dbbbe9a4c9ddbbcfe0a79ab depends: @@ -1994,26 +1587,17 @@ packages: license_family: BSD size: 122909 timestamp: 1720974522888 -- kind: conda - name: c-ares - version: 1.34.4 - build: h5505292_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda - sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f - md5: c1c999a38a4303b29d75c636eaa13cf9 - depends: - - __osx >=11.0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda + sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 + md5: e2775acf57efd5af15b8e3d1d74d72d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: MIT license_family: MIT - size: 179496 - timestamp: 1734208291879 -- kind: conda - name: c-ares - version: 1.34.4 - build: h86ecc28_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda + size: 206085 + timestamp: 1734208189009 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/c-ares-1.34.4-h86ecc28_0.conda sha256: 1187a41d4bb2afe02cb18690682edc98d1e9f5e0ccda638d8704a75ea1875bbe md5: 356da36f35d36dcba16e43f1589d4e39 depends: @@ -2022,61 +1606,34 @@ packages: license_family: MIT size: 215979 timestamp: 1734208193181 -- kind: conda - name: c-ares - version: 1.34.4 - build: hb9d3cd8_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.4-hb9d3cd8_0.conda - sha256: d4f28d87b6339b94f74762c0076e29c8ef8ddfff51a564a92da2843573c18320 - md5: e2775acf57efd5af15b8e3d1d74d72d3 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda + sha256: 09c0c8476e50b2955f474a4a1c17c4c047dd52993b5366b6ea8e968e583b921f + md5: c1c999a38a4303b29d75c636eaa13cf9 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 license: MIT license_family: MIT - size: 206085 - timestamp: 1734208189009 -- kind: conda - name: ca-certificates - version: 2024.12.14 - build: hbcca054_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda + size: 179496 + timestamp: 1734208291879 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.12.14-hbcca054_0.conda sha256: 1afd7274cbc9a334d6d0bc62fa760acc7afdaceb0b91a8df370ec01fd75dc7dd md5: 720523eb0d6a9b0f6120c16b2aa4e7de license: ISC size: 157088 timestamp: 1734208393264 -- kind: conda - name: ca-certificates - version: 2024.12.14 - build: hcefe29a_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ca-certificates-2024.12.14-hcefe29a_0.conda sha256: ad7b43211051332a5a4e788bb4619a2d0ecb5be73e0f76be17f733a87d7effd1 md5: 83b4ad1e6dc14df5891f3fcfdeb44351 license: ISC size: 157096 timestamp: 1734209301744 -- kind: conda - name: ca-certificates - version: 2024.12.14 - build: hf0a4a13_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda sha256: 256be633fd0882ccc1a7a32bc278547e1703f85082c0789a87a603ee3ab8fb82 md5: 7cb381a6783d91902638e4ed1ebd478e license: ISC size: 157091 timestamp: 1734208344343 -- kind: conda - name: certifi - version: 2024.12.14 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.12.14-pyhd8ed1ab_0.conda sha256: 048c16a9cbcb1fbad02083414d3bc7c1d0eea4b39aee6aa6bf8d1d5089ca8bad md5: 6feb87357ecd66733be3279f16a8c400 depends: @@ -2084,12 +1641,7 @@ packages: license: ISC size: 161642 timestamp: 1734380604767 -- kind: conda - name: cffi - version: 1.17.1 - build: py312h06ac9bb_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py312h06ac9bb_0.conda sha256: cba6ea83c4b0b4f5b5dc59cb19830519b28f95d7ebef7c9c5cf1c14843621457 md5: a861504bbea4161a9170b85d4d2be840 depends: @@ -2103,84 +1655,53 @@ packages: license_family: MIT size: 294403 timestamp: 1725560714366 -- kind: conda - name: cffi - version: 1.17.1 - build: py312h0fad829_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda - sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f - md5: 19a5456f72f505881ba493979777b24e +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda + sha256: 1162e3ca039e7ca7c0e78f0a020ed1bde968096841b663e3f393c966eb82f0f0 + md5: 1a256e5581b1099e9295cb84d53db3ea depends: - - __osx >=11.0 - libffi >=3.4,<4.0a0 + - libgcc >=13 - pycparser - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: MIT license_family: MIT - size: 281206 - timestamp: 1725560813378 -- kind: conda - name: cffi - version: 1.17.1 - build: py312hac81daf_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/cffi-1.17.1-py312hac81daf_0.conda - sha256: 1162e3ca039e7ca7c0e78f0a020ed1bde968096841b663e3f393c966eb82f0f0 - md5: 1a256e5581b1099e9295cb84d53db3ea + size: 312892 + timestamp: 1725561779888 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py312h0fad829_0.conda + sha256: 8d91a0d01358b5c3f20297c6c536c5d24ccd3e0c2ddd37f9d0593d0f0070226f + md5: 19a5456f72f505881ba493979777b24e depends: + - __osx >=11.0 - libffi >=3.4,<4.0a0 - - libgcc >=13 - pycparser - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: MIT license_family: MIT - size: 312892 - timestamp: 1725561779888 -- kind: conda - name: charset-normalizer - version: 3.4.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_1.conda - sha256: 63022ee2c6a157a9f980250a66f54bdcdf5abee817348d0f9a74c2441a6fbf0e - md5: 6581a17bba6b948bb60130026404a9d6 + size: 281206 + timestamp: 1725560813378 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.1-pyhd8ed1ab_0.conda + sha256: 4e0ee91b97e5de3e74567bdacea27f0139709fceca4db8adffbe24deffccb09b + md5: e83a31202d1c0a000fce3e9cf3825875 depends: - python >=3.9 license: MIT license_family: MIT - size: 47533 - timestamp: 1733218182393 -- kind: conda - name: click - version: 8.1.7 - build: unix_pyh707e725_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_1.conda - sha256: 1cd5fc6ccdd5141378e51252a7a3810b07fd5a7e6934a5b4a7eccba66566224b - md5: cb8e52f28f5e592598190c562e7b5bf1 + size: 47438 + timestamp: 1735929811779 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda + sha256: c920d23cd1fcf565031c679adb62d848af60d6fbb0edc2d50ba475cea4f0d8ab + md5: f22f4d4970e09d68a10b922cbb0408d3 depends: - __unix - python >=3.9 license: BSD-3-Clause license_family: BSD - size: 84513 - timestamp: 1733221925078 -- kind: conda - name: colorama - version: 0.4.6 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + size: 84705 + timestamp: 1734858922844 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 md5: 962b9857ee8e7018c22f2776ffa0b2d7 depends: @@ -2189,13 +1710,7 @@ packages: license_family: BSD size: 27011 timestamp: 1733218222191 -- kind: conda - name: datasets - version: 2.14.4 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/datasets-2.14.4-pyhd8ed1ab_0.conda sha256: 7e09bd083a609138b780fcc4535924cb96814d2c908a36d4c64a2ba9ee3efe7f md5: 3e087f072ce03c43a9b60522f5d0ca2f depends: @@ -2218,14 +1733,7 @@ packages: license_family: Apache size: 347303 timestamp: 1691593908658 -- kind: conda - name: deprecated - version: 1.2.15 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.2.15-pyhd8ed1ab_1.conda sha256: a20ebf2c9b02a6eb32412ceb5c4cffaae49417db7e75414a76417538293a9402 md5: eaef2e94d5bd76f758545d172c1fda67 depends: @@ -2235,13 +1743,7 @@ packages: license_family: MIT size: 14297 timestamp: 1733662697343 -- kind: conda - name: dill - version: 0.3.7 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.3.7-pyhd8ed1ab_0.conda sha256: 4ff20c6be028be2825235631c45d9e4a75bca1de65f8840c02dfb28ea0137c45 md5: 5e4f3466526c52bc9af2d2353a1460bd depends: @@ -2250,14 +1752,7 @@ packages: license_family: BSD size: 87553 timestamp: 1690101185422 -- kind: conda - name: dnspython - version: 2.7.0 - build: pyhff2d567_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 md5: 5fbd60d61d21b4bd2f9d7a48fe100418 depends: @@ -2276,14 +1771,7 @@ packages: license_family: OTHER size: 172172 timestamp: 1733256829961 -- kind: conda - name: email-validator - version: 2.2.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda sha256: b91a19eb78edfc2dbb36de9a67f74ee2416f1b5273dd7327abe53f2dbf864736 md5: da16dd3b0b71339060cd44cb7110ddf9 depends: @@ -2293,14 +1781,7 @@ packages: license: Unlicense size: 44401 timestamp: 1733300827551 -- kind: conda - name: email_validator - version: 2.2.0 - build: hd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: generic - url: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda sha256: e0d0fdf587aa0ed0ff08b2bce3ab355f46687b87b0775bfba01cc80a859ee6a2 md5: 0794f8807ff2c6f020422cacb1bd7bfa depends: @@ -2308,14 +1789,7 @@ packages: license: Unlicense size: 6552 timestamp: 1733300828176 -- kind: conda - name: exceptiongroup - version: 1.2.2 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 md5: a16662747cdeb9abbac74d0057cc976e depends: @@ -2323,15 +1797,9 @@ packages: license: MIT and PSF-2.0 size: 20486 timestamp: 1733208916977 -- kind: conda - name: fastapi - version: 0.115.6 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.6-pyhd8ed1ab_0.conda - sha256: d7826d537c667093c9de96411a09585a8d620c84a830a0195e58e9a0df45f018 - md5: 1b1e0c97830cdf75f1f371bd467ab657 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.7-pyhd8ed1ab_0.conda + sha256: 0324701b34662b98d6e9d2973cdd375592562a20822ca77d732172d246a31f00 + md5: aca90140ae63542cdbf749804b8426b7 depends: - email_validator >=2.0.0 - fastapi-cli >=0.0.5 @@ -2340,20 +1808,13 @@ packages: - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 - python >=3.9 - python-multipart >=0.0.7 - - starlette >=0.40.0,<0.42.0 + - starlette >=0.40.0,<0.46.0 - typing_extensions >=4.8.0 - uvicorn-standard >=0.12.0 license: MIT - license_family: MIT - size: 73084 - timestamp: 1733362427885 -- kind: conda - name: fastapi-cli - version: 0.0.7 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + size: 73620 + timestamp: 1737596314193 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 md5: d960e0ea9e1c561aa928f6c4439f04c7 depends: @@ -2365,28 +1826,15 @@ packages: license_family: MIT size: 15546 timestamp: 1734302408607 -- kind: conda - name: filelock - version: 3.16.1 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_1.conda - sha256: 18dca6e2194732df7ebf824abaefe999e4765ebe8e8a061269406ab88fc418b9 - md5: d692e9ba6f92dc51484bf3477e36ce7c +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.17.0-pyhd8ed1ab_0.conda + sha256: 006d7e5a0c17a6973596dd86bfc80d74ce541144d2aee2d22d46fd41df560a63 + md5: 7f402b4a1007ee355bc50ce4d24d4a57 depends: - python >=3.9 license: Unlicense - size: 17441 - timestamp: 1733240909987 -- kind: conda - name: freetype - version: 2.12.1 - build: h267a509_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda + size: 17544 + timestamp: 1737517924333 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda sha256: b2e3c449ec9d907dd4656cb0dc93e140f447175b125a3824b31368b06c666bb6 md5: 9ae35c3d96db2c94ce0cef86efdfa2cb depends: @@ -2396,28 +1844,7 @@ packages: license: GPL-2.0-only OR FTL size: 634972 timestamp: 1694615932610 -- kind: conda - name: freetype - version: 2.12.1 - build: hadb7bae_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda - sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 - md5: e6085e516a3e304ce41a8ee08b9b89ad - depends: - - libpng >=1.6.39,<1.7.0a0 - - libzlib >=1.2.13,<2.0.0a0 - license: GPL-2.0-only OR FTL - size: 596430 - timestamp: 1694616332835 -- kind: conda - name: freetype - version: 2.12.1 - build: hf0a5ef3_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.12.1-hf0a5ef3_2.conda sha256: 7af93030f4407f076dce181062360efac2cd54dce863b5d7765287a6f5382537 md5: a5ab74c5bd158c3d5532b66d8d83d907 depends: @@ -2427,29 +1854,16 @@ packages: license: GPL-2.0-only OR FTL size: 642092 timestamp: 1694617858496 -- kind: conda - name: frozenlist - version: 1.5.0 - build: py312h0bf5046_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda - sha256: 44d6d6b332421e621c029fb149f12dba1ccb5ed6ac632e2e807a9d92d6cb2864 - md5: 7960352935cc95ac23883c9b8c97f2ff +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda + sha256: 791673127e037a2dc0eebe122dc4f904cb3f6e635bb888f42cbe1a76b48748d9 + md5: e6085e516a3e304ce41a8ee08b9b89ad depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - size: 53366 - timestamp: 1729699762631 -- kind: conda - name: frozenlist - version: 1.5.0 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda + - libpng >=1.6.39,<1.7.0a0 + - libzlib >=1.2.13,<2.0.0a0 + license: GPL-2.0-only OR FTL + size: 596430 + timestamp: 1694616332835 +- conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.5.0-py312h66e93f0_0.conda sha256: 7e0c12983b20f2816b3712729b5a35ecb7ee152132ca7cf805427c62395ea823 md5: f98e36c96b2c66d9043187179ddb04f4 depends: @@ -2461,12 +1875,7 @@ packages: license_family: APACHE size: 60968 timestamp: 1729699568442 -- kind: conda - name: frozenlist - version: 1.5.0 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/frozenlist-1.5.0-py312hb2c0f52_0.conda sha256: b0a9ff3e71452eed70877b2f3175d41cd85070da6deac381c5f3f61e1f19bccb md5: 62fc11b0738ca15e0dd19b60cf280d12 depends: @@ -2478,29 +1887,28 @@ packages: license_family: APACHE size: 59967 timestamp: 1729699642726 -- kind: conda - name: fsspec - version: 2024.10.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhd8ed1ab_1.conda - sha256: 790a50b4f94042951518f911a914a886a837c926094c6a14ed1d9d03ce336807 - md5: 906fe13095e734cb413b57a49116cdc8 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/frozenlist-1.5.0-py312h0bf5046_0.conda + sha256: 44d6d6b332421e621c029fb149f12dba1ccb5ed6ac632e2e807a9d92d6cb2864 + md5: 7960352935cc95ac23883c9b8c97f2ff + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 53366 + timestamp: 1729699762631 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.12.0-pyhd8ed1ab_0.conda + sha256: 3320970c4604989eadf908397a9475f9e6a96a773c185915111399cbfbe47817 + md5: e041ad4c43ab5e10c74587f95378ebc7 depends: - python >=3.9 license: BSD-3-Clause license_family: BSD - size: 134726 - timestamp: 1733493445080 -- kind: conda - name: gflags - version: 2.2.2 - build: h5888daf_1005 - build_number: 1005 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + size: 137756 + timestamp: 1734650349242 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a md5: d411fc29e338efb48c5fd4576d71d881 depends: @@ -2511,13 +1919,7 @@ packages: license_family: BSD size: 119654 timestamp: 1726600001928 -- kind: conda - name: gflags - version: 2.2.2 - build: h5ad3122_1005 - build_number: 1005 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gflags-2.2.2-h5ad3122_1005.conda sha256: 28fe6b40b20454106d5e4ef6947cf298c13cc72a46347bbc49b563cd3a463bfa md5: 4ff634d515abbf664774b5e1168a9744 depends: @@ -2527,13 +1929,7 @@ packages: license_family: BSD size: 106638 timestamp: 1726599967617 -- kind: conda - name: gflags - version: 2.2.2 - build: hf9b8971_1005 - build_number: 1005 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 md5: 57a511a5905caa37540eb914dfcbf1fb depends: @@ -2543,44 +1939,29 @@ packages: license_family: BSD size: 82090 timestamp: 1726600145480 -- kind: conda - name: glog - version: 0.7.1 - build: h468a4a4_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda - sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 - md5: 08940a32c6ced3703d1412dd37df4f62 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca depends: - gflags >=2.2.2,<2.3.0a0 - libgcc-ng >=12 - libstdcxx-ng >=12 license: BSD-3-Clause license_family: BSD - size: 145811 - timestamp: 1718284208668 -- kind: conda - name: glog - version: 0.7.1 - build: hbabe93e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda - sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 - md5: ff862eebdfeb2fd048ae9dc92510baca + size: 143452 + timestamp: 1718284177264 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glog-0.7.1-h468a4a4_0.conda + sha256: 920795d4f775a9f47e91c2223e64847f0b212b3fedc56c137c5889e32efe8ba0 + md5: 08940a32c6ced3703d1412dd37df4f62 depends: - gflags >=2.2.2,<2.3.0a0 - libgcc-ng >=12 - libstdcxx-ng >=12 license: BSD-3-Clause license_family: BSD - size: 143452 - timestamp: 1718284177264 -- kind: conda - name: glog - version: 0.7.1 - build: heb240a5_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + size: 145811 + timestamp: 1718284208668 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 depends: @@ -2591,13 +1972,7 @@ packages: license_family: BSD size: 112215 timestamp: 1718284365403 -- kind: conda - name: googleapis-common-protos - version: 1.66.0 - build: pyhff2d567_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.66.0-pyhff2d567_0.conda sha256: d8d19575a827f2c62500949b9536efdd6b5406c9f546a73b6a87ac90b03a5875 md5: 4861e30ff0cd566ea6fb4593e3b7c22a depends: @@ -2607,14 +1982,7 @@ packages: license_family: APACHE size: 116522 timestamp: 1731459019854 -- kind: conda - name: h11 - version: 0.14.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 md5: 7ee49e89531c0dcbba9466f6d115d585 depends: @@ -2624,14 +1992,7 @@ packages: license_family: MIT size: 51846 timestamp: 1733327599467 -- kind: conda - name: h2 - version: 4.1.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda sha256: 843ddad410c370672a8250470697027618f104153612439076d4d7b91eeb7b5c md5: 825927dc7b0f287ef8d4d0011bb113b1 depends: @@ -2642,30 +2003,16 @@ packages: license_family: MIT size: 52000 timestamp: 1733298867359 -- kind: conda - name: hpack - version: 4.0.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - sha256: ec89b7e5b8aa2f0219f666084446e1fb7b54545861e9caa892acb24d125761b5 - md5: 2aa5ff7fa34a81b9196532c84c10d865 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e depends: - python >=3.9 license: MIT license_family: MIT - size: 29412 - timestamp: 1733299296857 -- kind: conda - name: httpcore - version: 1.0.7 - build: pyh29332c3_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df md5: 2ca8e6dbc86525c8b95e3c0ffa26442e depends: @@ -2679,12 +2026,7 @@ packages: license_family: BSD size: 48959 timestamp: 1731707562362 -- kind: conda - name: httptools - version: 0.6.4 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda sha256: 621e7e050b888e5239d33e37ea72d6419f8367e5babcad38b755586f20264796 md5: 8b1160b32557290b64d5be68db3d996d depends: @@ -2696,12 +2038,7 @@ packages: license_family: MIT size: 101872 timestamp: 1732707756745 -- kind: conda - name: httptools - version: 0.6.4 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/httptools-0.6.4-py312hb2c0f52_0.conda sha256: 0bd1f30224af142711d11033a7469ae402a1147143f399f7341bbc1d8178c722 md5: 5e70a6de59352f9a52e9caa7f3447390 depends: @@ -2713,12 +2050,7 @@ packages: license_family: MIT size: 101255 timestamp: 1732707891645 -- kind: conda - name: httptools - version: 0.6.4 - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py312hea69d52_0.conda sha256: 5e93cda79e32e8c0039e05ea1939e688da336187dab025f699b42ef529e848be md5: e1747a8e8d2aca5499aaea9993bf31ff depends: @@ -2730,13 +2062,7 @@ packages: license_family: MIT size: 85623 timestamp: 1732707871414 -- kind: conda - name: httpx - version: 0.28.1 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 md5: d6989ead454181f4f9bc987d3dc4e285 depends: @@ -2749,16 +2075,9 @@ packages: license_family: BSD size: 63082 timestamp: 1733663449209 -- kind: conda - name: huggingface_hub - version: 0.26.5 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.26.5-pyhd8ed1ab_1.conda - sha256: 0c75532d914a04c73222be298ed2c6868739dd475b1b1a9137c52abe79873952 - md5: 73937038e21117fe401f8ea64fbaeacc +- conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.27.1-pyhd8ed1ab_0.conda + sha256: 4597d7aa720f4acdddacc27b3f9e8d4336cb79477c53aee2d7ab96d136169cdb + md5: 8c9a53ecd0c3c278efbdac567dd12ed0 depends: - filelock - fsspec >=2023.5.0 @@ -2771,30 +2090,18 @@ packages: - typing_extensions >=3.7.4.3 license: Apache-2.0 license_family: APACHE - size: 275466 - timestamp: 1733852454004 -- kind: conda - name: hyperframe - version: 6.0.1 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - sha256: e91c6ef09d076e1d9a02819cd00fa7ee18ecf30cdd667605c853980216584d1b - md5: 566e75c90c1d0c8c459eb0ad9833dc7a + size: 278363 + timestamp: 1736350219225 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac depends: - python >=3.9 license: MIT license_family: MIT - size: 17239 - timestamp: 1733298862681 -- kind: conda - name: icu - version: '75.1' - build: hf9b3779_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda + size: 17397 + timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 md5: 268203e8b983fddb6412b36f2024e75c depends: @@ -2804,12 +2111,7 @@ packages: license_family: MIT size: 12282786 timestamp: 1720853454991 -- kind: conda - name: icu - version: '75.1' - build: hfee45f7_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 md5: 5eb22c1d7b3fc4abb50d92d621583137 depends: @@ -2818,14 +2120,7 @@ packages: license_family: MIT size: 11857802 timestamp: 1720853997952 -- kind: conda - name: idna - version: '3.10' - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 md5: 39a4f67be3286c86d696df570b1201b7 depends: @@ -2834,14 +2129,7 @@ packages: license_family: BSD size: 49765 timestamp: 1733211921194 -- kind: conda - name: importlib-metadata - version: 8.5.0 - build: pyha770c72_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 md5: 315607a3030ad5d5227e76e0733798ff depends: @@ -2851,31 +2139,17 @@ packages: license_family: APACHE size: 28623 timestamp: 1733223207185 -- kind: conda - name: jinja2 - version: 3.1.4 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_1.conda - sha256: 85a7169c078b8065bd9d121b0e7b99c8b88c42a411314b6ae5fcd81c48c4710a - md5: 08cce3151bde4ecad7885bd9fb647532 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.5-pyhd8ed1ab_0.conda + sha256: 98977694b9ecaa3218662f843425f39501f81973c450f995eec68f1803ed71c3 + md5: 2752a6ed44105bfb18c9bef1177d9dcd depends: - markupsafe >=2.0 - python >=3.9 license: BSD-3-Clause license_family: BSD - size: 110963 - timestamp: 1733217424408 -- kind: conda - name: jupyter_client - version: 8.6.3 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + size: 112561 + timestamp: 1734824044952 +- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a md5: 4ebae00eae9705b0c3d6d1018a81d047 depends: @@ -2890,14 +2164,7 @@ packages: license_family: BSD size: 106342 timestamp: 1733441040958 -- kind: conda - name: jupyter_core - version: 5.7.2 - build: pyh31011fe_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.7.2-pyh31011fe_1.conda sha256: 732b1e8536bc22a5a174baa79842d79db2f4956d90293dd82dc1b3f6099bcccd md5: 0a2980dada0dd7fd0998f0342308b1b1 depends: @@ -2909,12 +2176,7 @@ packages: license_family: BSD size: 57671 timestamp: 1727163547058 -- kind: conda - name: keyutils - version: 1.6.1 - build: h166bdaf_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2 sha256: 150c05a6e538610ca7c43beb3a40d65c90537497a4f6a5f4d15ec0451b6f5ebb md5: 30186d27e2c9fa62b45fb1476b7200e3 depends: @@ -2922,12 +2184,7 @@ packages: license: LGPL-2.1-or-later size: 117831 timestamp: 1646151697040 -- kind: conda - name: keyutils - version: 1.6.1 - build: h4e544f5_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2 sha256: 6d4233d97a9b38acbb26e1268bcf8c10a8e79c2aed7e5a385ec3769967e3e65b md5: 1f24853e59c68892452ef94ddd8afd4b depends: @@ -2935,30 +2192,21 @@ packages: license: LGPL-2.1-or-later size: 112327 timestamp: 1646166857935 -- kind: conda - name: krb5 - version: 1.21.3 - build: h237132a_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b - md5: c6dc8a0fdec13a0565936655c33069a1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 depends: - - __osx >=11.0 - - libcxx >=16 + - keyutils >=1.6.1,<2.0a0 - libedit >=3.1.20191231,<3.2.0a0 - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT - size: 1155530 - timestamp: 1719463474401 -- kind: conda - name: krb5 - version: 1.21.3 - build: h50a48e9_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda + size: 1370023 + timestamp: 1719463201255 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 md5: 29c10432a2ca1472b53f299ffb2ffa37 depends: @@ -2972,31 +2220,31 @@ packages: license_family: MIT size: 1474620 timestamp: 1719463205834 -- kind: conda - name: krb5 - version: 1.21.3 - build: h659f571_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 - md5: 3f43953b7d3fb3aaa1d0d0723d91e368 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 depends: - - keyutils >=1.6.1,<2.0a0 + - __osx >=11.0 + - libcxx >=16 - libedit >=3.1.20191231,<3.2.0a0 - libedit >=3.1.20191231,<4.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT - size: 1370023 - timestamp: 1719463201255 -- kind: conda - name: lcms2 - version: '2.16' - build: h922389a_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda + size: 1155530 + timestamp: 1719463474401 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda + sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 + md5: 51bb7010fc86f70eee639b4bb7a894f5 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.6.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 245247 + timestamp: 1701647787198 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda sha256: be4847b1014d3cbbc524a53bdbf66182f86125775020563e11d914c8468dd97d md5: ffdd8267a04c515e7ce69c727b051414 depends: @@ -3007,12 +2255,7 @@ packages: license_family: MIT size: 296219 timestamp: 1701647961116 -- kind: conda - name: lcms2 - version: '2.16' - build: ha0e7c42_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda sha256: 151e0c84feb7e0747fabcc85006b8973b22f5abbc3af76a9add0b0ef0320ebe4 md5: 66f6c134e76fe13cce8a9ea5814b5dd5 depends: @@ -3022,29 +2265,7 @@ packages: license_family: MIT size: 211959 timestamp: 1701647962657 -- kind: conda - name: lcms2 - version: '2.16' - build: hb7c19ff_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda - sha256: 5c878d104b461b7ef922abe6320711c0d01772f4cd55de18b674f88547870041 - md5: 51bb7010fc86f70eee639b4bb7a894f5 - depends: - - libgcc-ng >=12 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - license: MIT - license_family: MIT - size: 245247 - timestamp: 1701647787198 -- kind: conda - name: ld_impl_linux-64 - version: '2.43' - build: h712a8e2_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda sha256: 7c91cea91b13f4314d125d1bedb9d03a29ebbd5080ccdea70260363424646dbe md5: 048b02e3962f066da18efe3a21b77672 depends: @@ -3055,13 +2276,7 @@ packages: license_family: GPL size: 669211 timestamp: 1729655358674 -- kind: conda - name: ld_impl_linux-aarch64 - version: '2.43' - build: h80caac9_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda sha256: 80ec7e8f006196808fac5bd4b3773a652847f97bbf08044cd87731424ac64f8b md5: fcbde5ea19d55468953bf588770c0501 constrains: @@ -3070,12 +2285,7 @@ packages: license_family: GPL size: 698245 timestamp: 1729655345825 -- kind: conda - name: lerc - version: 4.0.0 - build: h27087fc_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2 sha256: cb55f36dcd898203927133280ae1dc643368af041a48bcf7c026acb7c47b0c12 md5: 76bbff344f0134279f225174e9064c8f depends: @@ -3085,12 +2295,7 @@ packages: license_family: Apache size: 281798 timestamp: 1657977462600 -- kind: conda - name: lerc - version: 4.0.0 - build: h4de3ea5_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-h4de3ea5_0.tar.bz2 sha256: 2d09ef9b7796d83364957e420b41c32d94e628c3f0520b61c332518a7b5cd586 md5: 1a0ffc65e03ce81559dbcb0695ad1476 depends: @@ -3100,12 +2305,7 @@ packages: license_family: Apache size: 262096 timestamp: 1657978241894 -- kind: conda - name: lerc - version: 4.0.0 - build: h9a09cb3_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 sha256: 6f068bb53dfb6147d3147d981bb851bb5477e769407ad4e6a68edf482fdcb958 md5: de462d5aacda3b30721b512c5da4e742 depends: @@ -3114,15 +2314,9 @@ packages: license_family: Apache size: 215721 timestamp: 1657977558796 -- kind: conda - name: libabseil - version: '20240722.0' - build: cxx17_h5888daf_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_h5888daf_1.conda - sha256: 8f91429091183c26950f1e7ffa730e8632f0627ba35d2fccd71df31628c9b4e5 - md5: e1f604644fe8d78e22660e2fec6756bc +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_hbbce691_4.conda + sha256: 143a586aa67d50622ef703de57b9d43f44945836d6568e0e7aa174bd8c45e0d4 + md5: 488f260ccda0afaf08acb286db439c2f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -3132,17 +2326,11 @@ packages: - abseil-cpp =20240722.0 license: Apache-2.0 license_family: Apache - size: 1310521 - timestamp: 1727295454064 -- kind: conda - name: libabseil - version: '20240722.0' - build: cxx17_h5ad3122_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h5ad3122_1.conda - sha256: 590e47dce38031a8893e70491f3b71e214de7781cab53b6f017aa6f6841cb076 - md5: 6fe6b3694c4792a8e26755d3b06f0b80 + size: 1311599 + timestamp: 1736008414161 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libabseil-20240722.0-cxx17_h18dbdb1_4.conda + sha256: bb6c5fb3b8de5f90735c5252b57efb3c268ee222c755569dac18065f05147670 + md5: 633b9fe454ffea2aaf29e191d946a83b depends: - libgcc >=13 - libstdcxx >=13 @@ -3151,56 +2339,47 @@ packages: - libabseil-static =20240722.0=cxx17* license: Apache-2.0 license_family: Apache - size: 1328502 - timestamp: 1727295490806 -- kind: conda - name: libabseil - version: '20240722.0' - build: cxx17_hf9b8971_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_hf9b8971_1.conda - sha256: 90bf08a75506dfcf28a70977da8ab050bcf594cd02abd3a9d84a22c9e8161724 - md5: 706da5e791c569a7b9814877098a6a0a + size: 1334844 + timestamp: 1736008472455 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda + sha256: 05fa5e5e908962b9c5aba95f962e2ca81d9599c4715aebe5e4ddb72b309d1770 + md5: c2d95bd7aa8d564a9bd7eca5e571a5b3 depends: - __osx >=11.0 - - libcxx >=17 + - libcxx >=18 constrains: - libabseil-static =20240722.0=cxx17* - abseil-cpp =20240722.0 license: Apache-2.0 license_family: Apache - size: 1179072 - timestamp: 1727295571173 -- kind: conda - name: libarrow - version: 18.1.0 - build: h1b535d6_6_cpu - build_number: 6 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h1b535d6_6_cpu.conda - sha256: 087b579aebf351ca41c54214121d86a15a41c92051cbd432d6f3a3f58a8c31b0 - md5: 4c0ad68efba1113ac5833975c67b565d - depends: - - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + size: 1178260 + timestamp: 1736008642885 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-19.0.0-h34b28d8_5_cpu.conda + build_number: 5 + sha256: 3c180b7ff70c7c4683bee9186fc44c1880b0c7121eef96e37e0ce9b2ce1fccda + md5: e9cc67cb7fe2cec529c63ce0a66802bd + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.29.9,<0.29.10.0a0 - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-identity-cpp >=1.10.0,<1.10.1.0a0 - azure-storage-blobs-cpp >=12.13.0,<12.13.1.0a0 - azure-storage-files-datalake-cpp >=12.12.0,<12.12.1.0a0 - bzip2 >=1.0.8,<2.0a0 - - gflags >=2.2.2,<2.3.0a0 - glog >=0.7.1,<0.8.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libbrotlidec >=1.1.0,<1.2.0a0 - libbrotlienc >=1.1.0,<1.2.0a0 - libgcc >=13 - - libgoogle-cloud >=2.32.0,<2.33.0a0 - - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libgoogle-cloud >=2.34.0,<2.35.0a0 + - libgoogle-cloud-storage >=2.34.0,<2.35.0a0 + - libopentelemetry-cpp >=1.18.0,<1.19.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libre2-11 >=2024.7.2 - libstdcxx >=13 - - libutf8proc >=2.9.0,<2.10.0a0 + - libutf8proc >=2.10.0,<2.11.0a0 - libzlib >=1.3.1,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - orc >=2.0.3,<2.0.4.0a0 @@ -3209,24 +2388,17 @@ packages: - zstd >=1.5.6,<1.6.0a0 constrains: - parquet-cpp <0.0a0 - - arrow-cpp <0.0a0 - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 license: Apache-2.0 - license_family: APACHE - size: 8040629 - timestamp: 1733810319239 -- kind: conda - name: libarrow - version: 18.1.0 - build: h44a453e_6_cpu - build_number: 6 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.1.0-h44a453e_6_cpu.conda - sha256: abf17e99b03356a9d6248e965826c1352ff01b00d3a62cc51393bb0744d72803 - md5: 2cf6d608d6e66506f69797d5c6944c35 + size: 8977710 + timestamp: 1737613391062 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-18.1.0-h4065667_12_cpu.conda + build_number: 12 + sha256: 0ac8aab370ef7a21e912cc312e5cbfa59afea8e004f5a372f2f25f96de0c9e9a + md5: 4789bb7ae93fe12bfd81169157896f49 depends: - - __glibc >=2.17,<3.0.a0 - - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-crt-cpp >=0.29.9,<0.29.10.0a0 - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-identity-cpp >=1.10.0,<1.10.1.0a0 @@ -3240,11 +2412,11 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 - libbrotlienc >=1.1.0,<1.2.0a0 - libgcc >=13 - - libgoogle-cloud >=2.32.0,<2.33.0a0 - - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libgoogle-cloud >=2.34.0,<2.35.0a0 + - libgoogle-cloud-storage >=2.34.0,<2.35.0a0 - libre2-11 >=2024.7.2 - libstdcxx >=13 - - libutf8proc >=2.9.0,<2.10.0a0 + - libutf8proc >=2.10.0,<2.11.0a0 - libzlib >=1.3.1,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - orc >=2.0.3,<2.0.4.0a0 @@ -3252,25 +2424,20 @@ packages: - snappy >=1.2.1,<1.3.0a0 - zstd >=1.5.6,<1.6.0a0 constrains: - - parquet-cpp <0.0a0 - arrow-cpp <0.0a0 - apache-arrow-proc =*=cpu + - parquet-cpp <0.0a0 license: Apache-2.0 license_family: APACHE - size: 8786061 - timestamp: 1733810643966 -- kind: conda - name: libarrow - version: 18.1.0 - build: h4a2f8bd_6_cpu - build_number: 6 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h4a2f8bd_6_cpu.conda - sha256: 9ed3ea1bc15005c0df187268ef91407afaa908cf82f36f5acbbf50ac24d7f806 - md5: 835cdd84195b84dc34d128bd5d3580b9 + size: 8051570 + timestamp: 1737451538096 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-19.0.0-h0ec2bd1_5_cpu.conda + build_number: 5 + sha256: 3d539ac17fe2dbb03fd8b23957f9d70332b3cd0090fd1cf7cb9407d251742298 + md5: bc994885db2f1a143562ccec03e2a07c depends: - __osx >=11.0 - - aws-crt-cpp >=0.29.7,<0.29.8.0a0 + - aws-crt-cpp >=0.29.9,<0.29.10.0a0 - aws-sdk-cpp >=1.11.458,<1.11.459.0a0 - azure-core-cpp >=1.14.0,<1.14.1.0a0 - azure-identity-cpp >=1.10.0,<1.10.1.0a0 @@ -3283,10 +2450,12 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 - libbrotlienc >=1.1.0,<1.2.0a0 - libcxx >=18 - - libgoogle-cloud >=2.32.0,<2.33.0a0 - - libgoogle-cloud-storage >=2.32.0,<2.33.0a0 + - libgoogle-cloud >=2.34.0,<2.35.0a0 + - libgoogle-cloud-storage >=2.34.0,<2.35.0a0 + - libopentelemetry-cpp >=1.18.0,<1.19.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libre2-11 >=2024.7.2 - - libutf8proc >=2.9.0,<2.10.0a0 + - libutf8proc >=2.10.0,<2.11.0a0 - libzlib >=1.3.1,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - orc >=2.0.3,<2.0.4.0a0 @@ -3294,197 +2463,140 @@ packages: - snappy >=1.2.1,<1.3.0a0 - zstd >=1.5.6,<1.6.0a0 constrains: - - apache-arrow-proc =*=cpu - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu - parquet-cpp <0.0a0 license: Apache-2.0 - license_family: APACHE - size: 5494797 - timestamp: 1733808145854 -- kind: conda - name: libarrow-acero - version: 18.1.0 - build: h3b568fd_6_cpu - build_number: 6 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_6_cpu.conda - sha256: fdb70e2499e59b730084ecd53008b361a6f6090b5fb49624feda06b7e84c7b8c - md5: c50907eefe2ae22d826e7cb2e4d712f5 - depends: - - libarrow 18.1.0 h1b535d6_6_cpu + size: 5559846 + timestamp: 1737609666072 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-19.0.0-hcb10f89_5_cpu.conda + build_number: 5 + sha256: 1f14748d81a4f0b21d9de8b568462b9f0eadb46dd50df3389a91b30251ea495a + md5: b0046d80d201ab6b905ad3e5925f5cc2 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 19.0.0 h34b28d8_5_cpu - libgcc >=13 - libstdcxx >=13 license: Apache-2.0 - license_family: APACHE - size: 578091 - timestamp: 1733810378092 -- kind: conda - name: libarrow-acero - version: 18.1.0 - build: hcb10f89_6_cpu - build_number: 6 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.1.0-hcb10f89_6_cpu.conda - sha256: a32fa1d71415afc02b5cf3cd4c0a6ec0af9e749308829cc65ff79689222ce479 - md5: 143f9288b64759a6427563f058c62f2b + size: 637780 + timestamp: 1737613451549 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-acero-18.1.0-h3b568fd_12_cpu.conda + build_number: 12 + sha256: c93daa943342eb848e5a44d4f48749f96decbde7b0f7d830fe56d0d506c03576 + md5: 8ec3804980e19b70ebffbeb623ce084b depends: - - __glibc >=2.17,<3.0.a0 - - libarrow 18.1.0 h44a453e_6_cpu + - libarrow 18.1.0 h4065667_12_cpu - libgcc >=13 - libstdcxx >=13 license: Apache-2.0 license_family: APACHE - size: 611745 - timestamp: 1733810698469 -- kind: conda - name: libarrow-acero - version: 18.1.0 - build: hf07054f_6_cpu - build_number: 6 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_6_cpu.conda - sha256: e1cae46409927470439ef9ae93ed09b3493d0579501ca9ebfa79ded212ee98d8 - md5: 97fc01254714e1572624baefdd7cc898 + size: 579114 + timestamp: 1737451608994 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-19.0.0-hf07054f_5_cpu.conda + build_number: 5 + sha256: 7664c098c35c6456ff251c064fdf7245d9836a4b43f3779fcba75d71deedc7ae + md5: 9e992fded28d32b40625a3ad91fbb53c depends: - __osx >=11.0 - - libarrow 18.1.0 h4a2f8bd_6_cpu + - libarrow 19.0.0 h0ec2bd1_5_cpu - libcxx >=18 license: Apache-2.0 - license_family: APACHE - size: 483713 - timestamp: 1733808246880 -- kind: conda - name: libarrow-dataset - version: 18.1.0 - build: h3b568fd_6_cpu - build_number: 6 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_6_cpu.conda - sha256: 2a08f5a1017ff660c37ae0c24343a119cb2511c6edd69e23d0a5090a0967ea35 - md5: bb1548ad011c4f9107fcc4cc548473bf - depends: - - libarrow 18.1.0 h1b535d6_6_cpu - - libarrow-acero 18.1.0 h3b568fd_6_cpu - - libgcc >=13 - - libparquet 18.1.0 hfc78867_6_cpu + size: 498783 + timestamp: 1737609764681 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-19.0.0-hcb10f89_5_cpu.conda + build_number: 5 + sha256: bbd40d3861e2c86408bb5454576046556d15ba58069484569e1d36e1505d8863 + md5: 4a00c59b20cd57b6f4dc4106f9fd5a20 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 19.0.0 h34b28d8_5_cpu + - libarrow-acero 19.0.0 hcb10f89_5_cpu + - libgcc >=13 + - libparquet 19.0.0 h081d1f1_5_cpu - libstdcxx >=13 license: Apache-2.0 - license_family: APACHE - size: 559673 - timestamp: 1733810461646 -- kind: conda - name: libarrow-dataset - version: 18.1.0 - build: hcb10f89_6_cpu - build_number: 6 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.1.0-hcb10f89_6_cpu.conda - sha256: 74eeb178070002842d3ed721769399320e3a68a0843319eaf899a092a31def26 - md5: 20ca46a6bc714a6ab189d5b3f46e66d8 + size: 604410 + timestamp: 1737613612875 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-dataset-18.1.0-h3b568fd_12_cpu.conda + build_number: 12 + sha256: 40bbdb394db6f405e6fae56bcbfc88fa433130668d2b39d1f109b7d6ffb8be76 + md5: e9b9a9941d35dff0a168741bdd54877e depends: - - __glibc >=2.17,<3.0.a0 - - libarrow 18.1.0 h44a453e_6_cpu - - libarrow-acero 18.1.0 hcb10f89_6_cpu + - libarrow 18.1.0 h4065667_12_cpu + - libarrow-acero 18.1.0 h3b568fd_12_cpu - libgcc >=13 - - libparquet 18.1.0 h081d1f1_6_cpu + - libparquet 18.1.0 hfc78867_12_cpu - libstdcxx >=13 license: Apache-2.0 license_family: APACHE - size: 586627 - timestamp: 1733810842604 -- kind: conda - name: libarrow-dataset - version: 18.1.0 - build: hf07054f_6_cpu - build_number: 6 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_6_cpu.conda - sha256: 6eba942ce926419f74e6e0a7c3994a7d78ab6be47115e6bb70e02136554736be - md5: 0774276be6659aaa0007f1b0f6ee19b0 + size: 559928 + timestamp: 1737451706623 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-19.0.0-hf07054f_5_cpu.conda + build_number: 5 + sha256: a9809d87e651eaf21abcf3e2cd60965e764d6b6376af17b2f9fd5dbe42dd19f6 + md5: f8f0e17c37357aee5574d01e06b918c9 depends: - __osx >=11.0 - - libarrow 18.1.0 h4a2f8bd_6_cpu - - libarrow-acero 18.1.0 hf07054f_6_cpu + - libarrow 19.0.0 h0ec2bd1_5_cpu + - libarrow-acero 19.0.0 hf07054f_5_cpu - libcxx >=18 - - libparquet 18.1.0 h636d7b7_6_cpu + - libparquet 19.0.0 h636d7b7_5_cpu license: Apache-2.0 - license_family: APACHE - size: 489948 - timestamp: 1733809328231 -- kind: conda - name: libarrow-substrait - version: 18.1.0 - build: h3ee7192_6_cpu - build_number: 6 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.1.0-h3ee7192_6_cpu.conda - sha256: bda6728db019dd0c409b1996ad9ef6ab0bcee3a94dc66a8045e8c1049c566055 - md5: aa313b3168caf98d00b3753f5ba27650 + size: 500746 + timestamp: 1737610843123 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-19.0.0-h08228c5_5_cpu.conda + build_number: 5 + sha256: 3394dbb1136a449c29dd52e7fd4254ac6781dd1a6518d8b2ca3aa2706f6b168f + md5: ce814aa64a0afc42ecb6f13f61f0b884 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libarrow 18.1.0 h44a453e_6_cpu - - libarrow-acero 18.1.0 hcb10f89_6_cpu - - libarrow-dataset 18.1.0 hcb10f89_6_cpu + - libarrow 19.0.0 h34b28d8_5_cpu + - libarrow-acero 19.0.0 hcb10f89_5_cpu + - libarrow-dataset 19.0.0 hcb10f89_5_cpu - libgcc >=13 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libstdcxx >=13 license: Apache-2.0 - license_family: APACHE - size: 519989 - timestamp: 1733810903274 -- kind: conda - name: libarrow-substrait - version: 18.1.0 - build: h3ffb4b1_6_cpu - build_number: 6 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h3ffb4b1_6_cpu.conda - sha256: 9f78c55c5d7122e588a6f226cbf7e909c479d66ed18edc633d68324323d386b9 - md5: 5db2e6832397b8ca70a6f7b00e0c3629 + size: 523328 + timestamp: 1737613684989 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libarrow-substrait-18.1.0-h1e9d426_12_cpu.conda + build_number: 12 + sha256: 6216a1c48e41d3b304e87b19a8ff37c10e51ceca83bff5e942d763c93f6f55a1 + md5: f647dcbf46aebc86f9975ff3c856de45 depends: - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libarrow 18.1.0 h1b535d6_6_cpu - - libarrow-acero 18.1.0 h3b568fd_6_cpu - - libarrow-dataset 18.1.0 h3b568fd_6_cpu + - libarrow 18.1.0 h4065667_12_cpu + - libarrow-acero 18.1.0 h3b568fd_12_cpu + - libarrow-dataset 18.1.0 h3b568fd_12_cpu - libgcc >=13 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libstdcxx >=13 license: Apache-2.0 license_family: APACHE - size: 515928 - timestamp: 1733810503359 -- kind: conda - name: libarrow-substrait - version: 18.1.0 - build: h86344ea_6_cpu - build_number: 6 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-18.1.0-h86344ea_6_cpu.conda - sha256: bafd9ca59ebb5ad34b77aff316ef7b59c5fb1eb8a7b6a15de8dcbdf3ce37556d - md5: c1c162f5bf569cff8bed6def705a899f + size: 516135 + timestamp: 1737451754178 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-19.0.0-h4239455_5_cpu.conda + build_number: 5 + sha256: e2f1dbf6d38648b979ae62ff0339e77c9d75f81f6797d8aba26b6f15516209c3 + md5: db52457c1550b8904c95ea6dffa1fd55 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libarrow 18.1.0 h4a2f8bd_6_cpu - - libarrow-acero 18.1.0 hf07054f_6_cpu - - libarrow-dataset 18.1.0 hf07054f_6_cpu + - libarrow 19.0.0 h0ec2bd1_5_cpu + - libarrow-acero 19.0.0 hf07054f_5_cpu + - libarrow-dataset 19.0.0 hf07054f_5_cpu - libcxx >=18 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 license: Apache-2.0 - license_family: APACHE - size: 451623 - timestamp: 1733809487176 -- kind: conda - name: libblas - version: 3.9.0 - build: 26_linux64_openblas + size: 449841 + timestamp: 1737611005423 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda build_number: 26 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-26_linux64_openblas.conda sha256: 30bd658682b124243f8e52d8edf8a19e7be1bc31e4fe4baec30a64002dc8cd0c md5: ac52800af2e0c0e7dac770b435ce768a depends: @@ -3496,15 +2608,11 @@ packages: - liblapacke 3.9.0 26_linux64_openblas - blas * openblas license: BSD-3-Clause + license_family: BSD size: 16393 timestamp: 1734432564346 -- kind: conda - name: libblas - version: 3.9.0 - build: 26_linuxaarch64_openblas +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda build_number: 26 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-26_linuxaarch64_openblas.conda sha256: df6d8ee34d45cf35609ecdd55c1ff03e32e0cd87ae41ebe4ef3747a8e09ead4d md5: 8d900b7079a00969d70305e9aad550b7 depends: @@ -3516,15 +2624,11 @@ packages: - libcblas 3.9.0 26_linuxaarch64_openblas - liblapack 3.9.0 26_linuxaarch64_openblas license: BSD-3-Clause + license_family: BSD size: 16477 timestamp: 1734432576699 -- kind: conda - name: libblas - version: 3.9.0 - build: 26_osxarm64_openblas +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda build_number: 26 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda sha256: 597f9c3779caa979c8c6abbb3ba8c7191b84e1a910d6b0d10e5faf35284c450c md5: 21be102c9ae80a67ba7de23b129aa7f6 depends: @@ -3536,30 +2640,10 @@ packages: - libcblas 3.9.0 26_osxarm64_openblas - blas * openblas license: BSD-3-Clause + license_family: BSD size: 16714 timestamp: 1734433054681 -- kind: conda - name: libbrotlicommon - version: 1.1.0 - build: h86ecc28_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda - sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 - md5: 3ee026955c688f551a9999840cff4c67 - depends: - - libgcc >=13 - license: MIT - license_family: MIT - size: 68982 - timestamp: 1725267774142 -- kind: conda - name: libbrotlicommon - version: 1.1.0 - build: hb9d3cd8_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda sha256: d9db2de60ea917298e658143354a530e9ca5f9c63471c65cf47ab39fd2f429e3 md5: 41b599ed2b02abcfdd84302bff174b23 depends: @@ -3569,13 +2653,16 @@ packages: license_family: MIT size: 68851 timestamp: 1725267660471 -- kind: conda - name: libbrotlicommon - version: 1.1.0 - build: hd74edd7_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda + sha256: 64112af913974b309d67fd342e065fd184347043a6387933b3db796778a28019 + md5: 3ee026955c688f551a9999840cff4c67 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 68982 + timestamp: 1725267774142 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda sha256: 839dacb741bdbb25e58f42088a2001b649f4f12195aeb700b5ddfca3267749e5 md5: d0bf1dff146b799b319ea0434b93f779 depends: @@ -3584,31 +2671,9 @@ packages: license_family: MIT size: 68426 timestamp: 1725267943211 -- kind: conda - name: libbrotlidec - version: 1.1.0 - build: h86ecc28_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda - sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c - md5: e64d0f3b59c7c4047446b97a8624a72d - depends: - - libbrotlicommon 1.1.0 h86ecc28_2 - - libgcc >=13 - license: MIT - license_family: MIT - size: 31708 - timestamp: 1725267783442 -- kind: conda - name: libbrotlidec - version: 1.1.0 - build: hb9d3cd8_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda - sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf - md5: 9566f0bd264fbd463002e759b8a82401 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda + sha256: 2892d512cad096cb03f1b66361deeab58b64e15ba525d6592bb6d609e7045edf + md5: 9566f0bd264fbd463002e759b8a82401 depends: - __glibc >=2.17,<3.0.a0 - libbrotlicommon 1.1.0 hb9d3cd8_2 @@ -3617,13 +2682,17 @@ packages: license_family: MIT size: 32696 timestamp: 1725267669305 -- kind: conda - name: libbrotlidec - version: 1.1.0 - build: hd74edd7_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda + sha256: 94c808d9ca3eb6ef30976a9843e27f027cf3a1e84e8c6835cbb696b7bdb35c4c + md5: e64d0f3b59c7c4047446b97a8624a72d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 31708 + timestamp: 1725267783442 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda sha256: 6c6862eb274f21a7c0b60e5345467a12e6dda8b9af4438c66d496a2c1a538264 md5: 55e66e68ce55523a6811633dd1ac74e2 depends: @@ -3633,29 +2702,7 @@ packages: license_family: MIT size: 28378 timestamp: 1725267980316 -- kind: conda - name: libbrotlienc - version: 1.1.0 - build: h86ecc28_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda - sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c - md5: 0e9bd365480c72b25c71a448257b537d - depends: - - libbrotlicommon 1.1.0 h86ecc28_2 - - libgcc >=13 - license: MIT - license_family: MIT - size: 290230 - timestamp: 1725267792697 -- kind: conda - name: libbrotlienc - version: 1.1.0 - build: hb9d3cd8_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda sha256: 779f58174e99de3600e939fa46eddb453ec5d3c60bb46cdaa8b4c127224dbf29 md5: 06f70867945ea6a84d35836af780f1de depends: @@ -3666,13 +2713,17 @@ packages: license_family: MIT size: 281750 timestamp: 1725267679782 -- kind: conda - name: libbrotlienc - version: 1.1.0 - build: hd74edd7_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda + sha256: 41385e17bc73834b235c5aff12d6d82eccb534acb3c30986996f9dad92a0d54c + md5: 0e9bd365480c72b25c71a448257b537d + depends: + - libbrotlicommon 1.1.0 h86ecc28_2 + - libgcc >=13 + license: MIT + license_family: MIT + size: 290230 + timestamp: 1725267792697 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda sha256: eeb1eb0d58b9d02bc1b98dc0a058f104ab168eb2f7d1c7bfa0570a12cfcdb7b7 md5: 4f3a434504c67b2c42565c0b85c1885c depends: @@ -3682,13 +2733,8 @@ packages: license_family: MIT size: 279644 timestamp: 1725268003553 -- kind: conda - name: libcblas - version: 3.9.0 - build: 26_linux64_openblas +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda build_number: 26 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-26_linux64_openblas.conda sha256: 9c74e536c9bc868e356ffd43f81c2cb398aec84b40fcadc312315b164a5500ee md5: ebcc5f37a435aa3c19640533c82f8d76 depends: @@ -3698,15 +2744,11 @@ packages: - liblapacke 3.9.0 26_linux64_openblas - blas * openblas license: BSD-3-Clause + license_family: BSD size: 16336 timestamp: 1734432570482 -- kind: conda - name: libcblas - version: 3.9.0 - build: 26_linuxaarch64_openblas +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda build_number: 26 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-26_linuxaarch64_openblas.conda sha256: 521e78be0c4170f229c43e1a6c94337a72db3ebcbe6e5960f8413aa438dcb8f9 md5: d77f943ae4083f3aeddca698f2d28262 depends: @@ -3716,15 +2758,11 @@ packages: - liblapacke 3.9.0 26_linuxaarch64_openblas - liblapack 3.9.0 26_linuxaarch64_openblas license: BSD-3-Clause + license_family: BSD size: 16398 timestamp: 1734432580937 -- kind: conda - name: libcblas - version: 3.9.0 - build: 26_osxarm64_openblas +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda build_number: 26 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda sha256: 27a29ef6b2fd2179bc3a0bb9db351f078ba140ca10485dca147c399639f84c93 md5: a0e9980fe12d42f6d0c0ec009f67e948 depends: @@ -3734,44 +2772,30 @@ packages: - liblapacke 3.9.0 26_osxarm64_openblas - blas * openblas license: BSD-3-Clause + license_family: BSD size: 16628 timestamp: 1734433061517 -- kind: conda - name: libcrc32c - version: 1.1.2 - build: h01db608_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 - sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea - md5: 268ee639c17ada0002fb04dd21816cc2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 depends: - libgcc-ng >=9.4.0 - libstdcxx-ng >=9.4.0 license: BSD-3-Clause license_family: BSD - size: 18669 - timestamp: 1633683724891 -- kind: conda - name: libcrc32c - version: 1.1.2 - build: h9c3ff4c_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 - md5: c965a5aa0d5c1c37ffc62dff36e28400 + size: 20440 + timestamp: 1633683576494 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcrc32c-1.1.2-h01db608_0.tar.bz2 + sha256: b8b8c57a87da86b3ea24280fd6aa8efaf92f4e684b606bf2db5d3cb06ffbe2ea + md5: 268ee639c17ada0002fb04dd21816cc2 depends: - libgcc-ng >=9.4.0 - libstdcxx-ng >=9.4.0 license: BSD-3-Clause license_family: BSD - size: 20440 - timestamp: 1633683576494 -- kind: conda - name: libcrc32c - version: 1.1.2 - build: hbdafb3b_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + size: 18669 + timestamp: 1633683724891 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 md5: 32bd82a6a625ea6ce090a81c3d34edeb depends: @@ -3780,12 +2804,7 @@ packages: license_family: BSD size: 18765 timestamp: 1633683992603 -- kind: conda - name: libcurl - version: 8.11.1 - build: h332b0f4_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.11.1-h332b0f4_0.conda sha256: 3cd4075b2a7b5562e46c8ec626f6f9ca57aeecaa94ff7df57eca26daa94c9906 md5: 2b3e0081006dc21e8bf53a91c83a055c depends: @@ -3801,12 +2820,7 @@ packages: license_family: MIT size: 423011 timestamp: 1733999897624 -- kind: conda - name: libcurl - version: 8.11.1 - build: h6702fde_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcurl-8.11.1-h6702fde_0.conda sha256: 9fc65d21a58f4aad1bc39dfb94a178893aeb035850c5cf0ed9736674279f390b md5: 7dec1cd271c403d1636bda5aa388a55d depends: @@ -3821,12 +2835,7 @@ packages: license_family: MIT size: 440737 timestamp: 1733999835504 -- kind: conda - name: libcurl - version: 8.11.1 - build: h73640d1_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda sha256: f47c35938144c23278987c7d12096f6a42d7c850ffc277222b032073412383b6 md5: 46d7524cabfdd199bffe63f8f19a552b depends: @@ -3841,114 +2850,87 @@ packages: license_family: MIT size: 385098 timestamp: 1734000160270 -- kind: conda - name: libcxx - version: 19.1.5 - build: ha82da77_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.5-ha82da77_0.conda - sha256: 7918cc0bb7a6554cdd3eee634c3dc414a1ab8ec49faeca1567367bb92118f9d7 - md5: 3c7be0df28ccda1d193ea6de56dcb5ff +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.7-ha82da77_0.conda + sha256: 776092346da87a2a23502e14d91eb0c32699c4a1522b7331537bd1c3751dcff5 + md5: 5b3e1610ff8bd5443476b91d618f5b77 depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache - size: 519819 - timestamp: 1733291654212 -- kind: conda - name: libdeflate - version: '1.23' - build: h4ddbbb0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda + size: 523505 + timestamp: 1736877862502 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h4ddbbb0_0.conda sha256: 511d801626d02f4247a04fff957cc6e9ec4cc7e8622bd9acd076bcdc5de5fe66 md5: 8dfae1d2e74767e9ce36d5fa0d8605db depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT + license_family: MIT size: 72255 timestamp: 1734373823254 -- kind: conda - name: libdeflate - version: '1.23' - build: h5e3c512_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-h5e3c512_0.conda sha256: 959419d87cd2b789a9055db95704c614f31aeb70bef7949fa2f734122a3a2863 md5: 7e7ca2607b11b180120cefc2354fc0cb depends: - libgcc >=13 license: MIT + license_family: MIT size: 69862 timestamp: 1734373858306 -- kind: conda - name: libdeflate - version: '1.23' - build: hec38601_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda sha256: 887c02deaed6d583459eba6367023e36d8761085b2f7126e389424f57155da53 md5: 1d8b9588be14e71df38c525767a1ac30 depends: - __osx >=11.0 license: MIT + license_family: MIT size: 54132 timestamp: 1734373971372 -- kind: conda - name: libedit - version: 3.1.20191231 - build: hc8eb9b7_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 - sha256: 3912636197933ecfe4692634119e8644904b41a58f30cad9d1fc02f6ba4d9fca - md5: 30e4362988a2623e9eb34337b83e01f9 - depends: - - ncurses >=6.2,<7.0.0a0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20240808-pl5321h7949ede_0.conda + sha256: 4d0d69ddf9cc7d724a1ccf3a9852e44c8aea9825692582bac2c4e8d21ec95ccd + md5: 8247f80f3dc464d9322e85007e307fe8 + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD - size: 96607 - timestamp: 1597616630749 -- kind: conda - name: libedit - version: 3.1.20191231 - build: he28a2e2_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 - sha256: a57d37c236d8f7c886e01656f4949d9dcca131d2a0728609c6f7fa338b65f1cf - md5: 4d331e44109e3f0e19b4cb8f9b82f3e1 - depends: - - libgcc-ng >=7.5.0 - - ncurses >=6.2,<7.0.0a0 + size: 134657 + timestamp: 1736191912705 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20240808-pl5321h976ea20_0.conda + sha256: 031daea98cf278f858b7957ad5dc475f1b5673cd5e718850529401ced64cef2c + md5: 0be40129d3dd1a152fff29a85f0785d0 + depends: + - ncurses + - libgcc >=13 + - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD - size: 123878 - timestamp: 1597616541093 -- kind: conda - name: libedit - version: 3.1.20191231 - build: he28a2e2_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 - sha256: debc31fb2f07ba2b0363f90e455873670734082822926ba4a9556431ec0bf36d - md5: 29371161d77933a54fccf1bb66b96529 - depends: - - libgcc-ng >=7.5.0 - - ncurses >=6.2,<7.0.0a0 + size: 148120 + timestamp: 1736192137151 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20240808-pl5321hafb1f1b_0.conda + sha256: fb934d7a03279ec8eae4bf1913ac9058fcf6fed35290d8ffa6e04157f396a3b1 + md5: af89aa84ffb5ee551ce0c137b951a3b5 + depends: + - ncurses + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + size: 107634 + timestamp: 1736192034117 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 license: BSD-2-Clause license_family: BSD - size: 134104 - timestamp: 1597617110769 -- kind: conda - name: libev - version: '4.33' - build: h31becfc_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda + size: 112766 + timestamp: 1702146165126 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libev-4.33-h31becfc_2.conda sha256: 973af77e297f1955dd1f69c2cbdc5ab9dfc88388a5576cd152cda178af0fd006 md5: a9a13cb143bbaa477b1ebaefbe47a302 depends: @@ -3957,56 +2939,24 @@ packages: license_family: BSD size: 115123 timestamp: 1702146237623 -- kind: conda - name: libev - version: '4.33' - build: h93a5062_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f md5: 36d33e440c31857372a72137f78bacf5 license: BSD-2-Clause license_family: BSD size: 107458 timestamp: 1702146414478 -- kind: conda - name: libev - version: '4.33' - build: hd590300_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 - md5: 172bf1cd1ff8629f2b1179945ed45055 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d depends: - libgcc-ng >=12 - license: BSD-2-Clause - license_family: BSD - size: 112766 - timestamp: 1702146165126 -- kind: conda - name: libevent - version: 2.1.12 - build: h2757513_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda - sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 - md5: 1a109764bff3bdc7bdd84088347d71dc - depends: - openssl >=3.1.1,<4.0a0 license: BSD-3-Clause license_family: BSD - size: 368167 - timestamp: 1685726248899 -- kind: conda - name: libevent - version: 2.1.12 - build: h4ba1bb4_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda + size: 427426 + timestamp: 1685725977222 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libevent-2.1.12-h4ba1bb4_1.conda sha256: 01333cc7d6e6985dd5700b43660d90e9e58049182017fd24862088ecbe1458e4 md5: 96ae6083cd1ac9f6bc81631ac835b317 depends: @@ -4016,44 +2966,16 @@ packages: license_family: BSD size: 438992 timestamp: 1685726046519 -- kind: conda - name: libevent - version: 2.1.12 - build: hf998b51_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda - sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 - md5: a1cfcc585f0c42bf8d5546bb1dfb668d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc depends: - - libgcc-ng >=12 - openssl >=3.1.1,<4.0a0 license: BSD-3-Clause license_family: BSD - size: 427426 - timestamp: 1685725977222 -- kind: conda - name: libexpat - version: 2.6.4 - build: h286801f_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda - sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 - md5: 38d2656dd914feb0cab8c629370768bf - depends: - - __osx >=11.0 - constrains: - - expat 2.6.4.* - license: MIT - license_family: MIT - size: 64693 - timestamp: 1730967175868 -- kind: conda - name: libexpat - version: 2.6.4 - build: h5888daf_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda + size: 368167 + timestamp: 1685726248899 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 md5: db833e03127376d461e1e13e76f09b6c depends: @@ -4065,12 +2987,7 @@ packages: license_family: MIT size: 73304 timestamp: 1730967041968 -- kind: conda - name: libexpat - version: 2.6.4 - build: h5ad3122_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.6.4-h5ad3122_0.conda sha256: f42e758009ba9db90d1fe7992bc3e60d0c52f71fb20923375d2c44ae69a5a2b3 md5: f1b3fab36861b3ce945a13f0dfdfc688 depends: @@ -4081,26 +2998,27 @@ packages: license_family: MIT size: 72345 timestamp: 1730967203789 -- kind: conda - name: libffi - version: 3.4.2 - build: h3422bc3_5 - build_number: 5 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 - sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca - md5: 086914b672be056eb70fd4285b6783b6 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda + sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 + md5: 38d2656dd914feb0cab8c629370768bf + depends: + - __osx >=11.0 + constrains: + - expat 2.6.4.* license: MIT license_family: MIT - size: 39020 - timestamp: 1636488587153 -- kind: conda - name: libffi - version: 3.4.2 - build: h3557bc0_5 - build_number: 5 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 + size: 64693 + timestamp: 1730967175868 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + size: 58292 + timestamp: 1636488182923 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.2-h3557bc0_5.tar.bz2 sha256: 7e9258a102480757fe3faeb225a3ca04dffd10fecd2a958c65cdb4cdf75f2c3c md5: dddd85f4d52121fab0a8b099c5e06501 depends: @@ -4109,28 +3027,14 @@ packages: license_family: MIT size: 59450 timestamp: 1636488255090 -- kind: conda - name: libffi - version: 3.4.2 - build: h7f98852_5 - build_number: 5 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 - sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e - md5: d645c6d2ac96843a2bfaccd2d62b3ac3 - depends: - - libgcc-ng >=9.4.0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 + sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca + md5: 086914b672be056eb70fd4285b6783b6 license: MIT license_family: MIT - size: 58292 - timestamp: 1636488182923 -- kind: conda - name: libgcc - version: 14.2.0 - build: h77fa898_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda + size: 39020 + timestamp: 1636488587153 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda sha256: 53eb8a79365e58849e7b1a068d31f4f9e718dc938d6f2c03e960345739a03569 md5: 3cb76c3f10d3bc7f1105b2fc9db984df depends: @@ -4143,13 +3047,7 @@ packages: license_family: GPL size: 848745 timestamp: 1729027721139 -- kind: conda - name: libgcc - version: 14.2.0 - build: he277a41_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_1.conda sha256: 5d56757ccad208c79214395b00d006d8d18929a4ba49c47bd9460789a7620943 md5: 511b511c5445e324066c3377481bcab8 depends: @@ -4161,13 +3059,7 @@ packages: license_family: GPL size: 535243 timestamp: 1729089435134 -- kind: conda - name: libgcc-ng - version: 14.2.0 - build: h69a702a_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda sha256: 3a76969c80e9af8b6e7a55090088bc41da4cffcde9e2c71b17f44d37b7cb87f7 md5: e39480b9ca41323497b05492a63bc35b depends: @@ -4176,13 +3068,7 @@ packages: license_family: GPL size: 54142 timestamp: 1729027726517 -- kind: conda - name: libgcc-ng - version: 14.2.0 - build: he9431aa_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_1.conda sha256: 9b5cf168a6c7361cae869cb74b716766ee7c6d6b3f6172b32ba9bf91135efdc4 md5: 0694c249c61469f2c0f7e2990782af21 depends: @@ -4191,28 +3077,7 @@ packages: license_family: GPL size: 54104 timestamp: 1729089444587 -- kind: conda - name: libgfortran - version: 5.0.0 - build: 13_2_0_hd922786_3 - build_number: 3 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda - sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b - md5: 4a55d9e169114b2b90d3ec4604cd7bbf - depends: - - libgfortran5 13.2.0 hf226fd6_3 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - size: 110233 - timestamp: 1707330749033 -- kind: conda - name: libgfortran - version: 14.2.0 - build: h69a702a_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda sha256: fc9e7f22a17faf74da904ebfc4d88699013d2992e55505e4aa0eb01770290977 md5: f1fd30127802683586f768875127a987 depends: @@ -4223,13 +3088,7 @@ packages: license_family: GPL size: 53997 timestamp: 1729027752995 -- kind: conda - name: libgfortran - version: 14.2.0 - build: he9431aa_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_1.conda sha256: cb66e411fa32a5c6040f4e5e2a63c00897aae4c3133a9c004c2e929ccf19575b md5: 0294b92d2f47a240bebb1e3336b495f1 depends: @@ -4240,30 +3099,27 @@ packages: license_family: GPL size: 54105 timestamp: 1729089471124 -- kind: conda - name: libgfortran5 - version: 13.2.0 - build: hf226fd6_3 - build_number: 3 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda - sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a - md5: 66ac81d54e95c534ae488726c1f698ea +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda + sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b + md5: 4a55d9e169114b2b90d3ec4604cd7bbf depends: - - llvm-openmp >=8.0.0 + - libgfortran5 13.2.0 hf226fd6_3 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 110233 + timestamp: 1707330749033 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda + sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d + md5: 9822b874ea29af082e5d36098d25427d + depends: + - libgcc >=14.2.0 constrains: - - libgfortran 5.0.0 13_2_0_*_3 + - libgfortran 14.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - size: 997381 - timestamp: 1707330687590 -- kind: conda - name: libgfortran5 - version: 14.2.0 - build: hb6113d0_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda + size: 1462645 + timestamp: 1729027735353 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_1.conda sha256: a87ff46d19916403cbf68cf1d785bf56b4d1ab7b2552468d2ea775d70782493f md5: fc068e11b10e18f184e027782baa12b6 depends: @@ -4274,30 +3130,18 @@ packages: license_family: GPL size: 1102158 timestamp: 1729089452640 -- kind: conda - name: libgfortran5 - version: 14.2.0 - build: hd5240d6_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda - sha256: d149a37ca73611e425041f33b9d8dbed6e52ec506fe8cc1fc0ee054bddeb6d5d - md5: 9822b874ea29af082e5d36098d25427d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a + md5: 66ac81d54e95c534ae488726c1f698ea depends: - - libgcc >=14.2.0 + - llvm-openmp >=8.0.0 constrains: - - libgfortran 14.2.0 + - libgfortran 5.0.0 13_2_0_*_3 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - size: 1462645 - timestamp: 1729027735353 -- kind: conda - name: libgomp - version: 14.2.0 - build: h77fa898_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda + size: 997381 + timestamp: 1707330687590 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda sha256: 1911c29975ec99b6b906904040c855772ccb265a1c79d5d75c8ceec4ed89cd63 md5: cc3573974587f12dda90d96e3e55a702 depends: @@ -4306,248 +3150,187 @@ packages: license_family: GPL size: 460992 timestamp: 1729027639220 -- kind: conda - name: libgomp - version: 14.2.0 - build: he277a41_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_1.conda sha256: 5aa53874a5e57a00f2e0c2e2910684eb674429cd5fcb803619b226a73e89aedf md5: 376f0e73abbda6d23c0cb749adc195ef license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL size: 463521 timestamp: 1729089357313 -- kind: conda - name: libgoogle-cloud - version: 2.32.0 - build: h3888205_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.32.0-h3888205_0.conda - sha256: 36af2844ce8fafd477214d51117746144461132f76759a7d29963b4583b577be - md5: a40b948bf4eabcc1ce708c40ffd7c06d +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.34.0-h2b5623c_0.conda + sha256: 348ee1dddd82dcef5a185c86e65dda8acfc9b583acc425ccb9b661f2d433b2cc + md5: 2a5142c88dd6132eaa8079f99476e922 depends: + - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libcurl >=8.10.1,<9.0a0 + - libcurl >=8.11.1,<9.0a0 - libgcc >=13 - libgrpc >=1.67.1,<1.68.0a0 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libstdcxx >=13 - openssl >=3.4.0,<4.0a0 constrains: - - libgoogle-cloud 2.32.0 *_0 + - libgoogle-cloud 2.34.0 *_0 license: Apache-2.0 license_family: Apache - size: 1248560 - timestamp: 1733512309504 -- kind: conda - name: libgoogle-cloud - version: 2.32.0 - build: h804f50b_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.32.0-h804f50b_0.conda - sha256: 126856add750013390dff664a3c3cd0f6f0cbbc683b0025a7ce9d1618968bc70 - md5: 3d96df4d6b1c88455e05b94ce8a14a53 + size: 1256795 + timestamp: 1737286199784 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-2.34.0-hccf9d24_0.conda + sha256: 54267dda8fafc2a2d379ef77b6029d8240e0628d4b29758f788fb903f84397a3 + md5: 1ce0fd876001c40801b40fea22987e41 depends: - - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libcurl >=8.10.1,<9.0a0 + - libcurl >=8.11.1,<9.0a0 - libgcc >=13 - libgrpc >=1.67.1,<1.68.0a0 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libstdcxx >=13 - openssl >=3.4.0,<4.0a0 constrains: - - libgoogle-cloud 2.32.0 *_0 + - libgoogle-cloud 2.34.0 *_0 license: Apache-2.0 license_family: Apache - size: 1249557 - timestamp: 1733512191906 -- kind: conda - name: libgoogle-cloud - version: 2.32.0 - build: h8d8be31_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.32.0-h8d8be31_0.conda - sha256: 722e49dbdc4486105d9f5b79a7ba4f9064602fe20c4015e97684c898ab8d3386 - md5: d7ab9e0eb7d55eac4943913073de61d7 + size: 1256586 + timestamp: 1737285242684 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.34.0-hdbe95d5_0.conda + sha256: 919d8cbcd47d5bd2244c55b2bb87e2bd2eed8215996aab8435cb7123ffd9d20e + md5: 69826544e7978fcaa6bc8c1962d96ad6 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libcurl >=8.10.1,<9.0a0 + - libcurl >=8.11.1,<9.0a0 - libcxx >=18 - libgrpc >=1.67.1,<1.68.0a0 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - openssl >=3.4.0,<4.0a0 constrains: - - libgoogle-cloud 2.32.0 *_0 + - libgoogle-cloud 2.34.0 *_0 license: Apache-2.0 license_family: Apache - size: 876210 - timestamp: 1733512539476 -- kind: conda - name: libgoogle-cloud-storage - version: 2.32.0 - build: h0121fbd_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.32.0-h0121fbd_0.conda - sha256: d1b53d17df38b52a4bc6d1fe6af0e611d6480ce10b0af570c84bd38c8aa83b91 - md5: 877a5ec0431a5af83bf0cd0522bfe661 + size: 878217 + timestamp: 1737284441192 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.34.0-h0121fbd_0.conda + sha256: aa1b3b30ae6b2eab7c9e6a8e2fd8ec3776f25d2e3f0b6f9dc547ff8083bf25fa + md5: 9f0c43225243c81c6991733edcaafff5 depends: - __glibc >=2.17,<3.0.a0 - libabseil - libcrc32c >=1.1.2,<1.2.0a0 - libcurl - libgcc >=13 - - libgoogle-cloud 2.32.0 h804f50b_0 + - libgoogle-cloud 2.34.0 h2b5623c_0 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - openssl license: Apache-2.0 license_family: Apache - size: 782108 - timestamp: 1733512329104 -- kind: conda - name: libgoogle-cloud-storage - version: 2.32.0 - build: h7081f7f_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.32.0-h7081f7f_0.conda - sha256: 609df2cf376ba66460f40143f835fc567cae4458df80705587cd2efd59c09bf1 - md5: 28f5ab5cf95170dfacd05d2bb301e573 + size: 785792 + timestamp: 1737286406612 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.34.0-hb9b2b65_0.conda + sha256: 4ad4fb7c02dcfa4c86dcf9591e0131a01fc0f2c3f2729c12882b944ddf2b8a9d + md5: 0732a5988f7f556f2c1d1f51026fc1be depends: - - __osx >=11.0 - libabseil - libcrc32c >=1.1.2,<1.2.0a0 - libcurl - - libcxx >=18 - - libgoogle-cloud 2.32.0 h8d8be31_0 + - libgcc >=13 + - libgoogle-cloud 2.34.0 hccf9d24_0 + - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - openssl license: Apache-2.0 license_family: Apache - size: 526895 - timestamp: 1733513644846 -- kind: conda - name: libgoogle-cloud-storage - version: 2.32.0 - build: hb9b2b65_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-2.32.0-hb9b2b65_0.conda - sha256: e120e7b6c9c9d25baa8ae903106babdd3c969523ae25278a615ed9de4bd0fc35 - md5: 925ab0ca33baca4fcfee585cecb94169 + size: 739678 + timestamp: 1737285399565 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.34.0-h7081f7f_0.conda + sha256: 79f6b93fb330728530036b2b38764e9d42e0eedd3ae7e549ac7eae49acd1e52b + md5: f09cb03f9cf847f1dc41b4c1f65c97c2 depends: + - __osx >=11.0 - libabseil - libcrc32c >=1.1.2,<1.2.0a0 - libcurl - - libgcc >=13 - - libgoogle-cloud 2.32.0 h3888205_0 - - libstdcxx >=13 + - libcxx >=18 + - libgoogle-cloud 2.34.0 hdbe95d5_0 - libzlib >=1.3.1,<2.0a0 - openssl license: Apache-2.0 license_family: Apache - size: 737964 - timestamp: 1733512457785 -- kind: conda - name: libgrpc - version: 1.67.1 - build: h36c5df4_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-h36c5df4_0.conda - sha256: 1f6673d9d866048c9cf28fd56e6874ffc7e2c53c47d7071cb367d5fc2dde16a7 - md5: b946137e362e98a55a77fdf0b20a7739 + size: 529202 + timestamp: 1737285376801 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-h25350d4_1.conda + sha256: 014627485b3cf0ea18e04c0bab07be7fb98722a3aeeb58477acc7e1c3d2f911e + md5: 0c6497a760b99a926c7c12b74951a39c depends: - - c-ares >=1.32.3,<2.0a0 + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.4,<2.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libgcc >=13 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libre2-11 >=2024.7.2 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 + - openssl >=3.4.0,<4.0a0 - re2 constrains: - grpc-cpp =1.67.1 license: Apache-2.0 license_family: APACHE - size: 7131846 - timestamp: 1730236305327 -- kind: conda - name: libgrpc - version: 1.67.1 - build: hc2c308b_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda - sha256: 870550c1faf524e9a695262cd4c31441b18ad542f16893bd3c5dbc93106705f7 - md5: 4606a4647bfe857e3cfe21ca12ac3afb + size: 7792251 + timestamp: 1735584856826 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.67.1-hf7ccdd3_1.conda + sha256: 3fa173dc1d7456aac2b26937daae86a08432a31640e3d6569c62edc661fc9bbe + md5: 8fb41a425bebaeb3d0fa568503612e64 depends: - - __glibc >=2.17,<3.0.a0 - - c-ares >=1.32.3,<2.0a0 + - c-ares >=1.34.4,<2.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libgcc >=13 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libre2-11 >=2024.7.2 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 + - openssl >=3.4.0,<4.0a0 - re2 constrains: - grpc-cpp =1.67.1 license: Apache-2.0 license_family: APACHE - size: 7362336 - timestamp: 1730236333879 -- kind: conda - name: libgrpc - version: 1.67.1 - build: hc70892a_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-hc70892a_0.conda - sha256: d2393fcd3c3584e5d58da4122f48bcf297567d2f6f14b3d1fcbd34fdd5040694 - md5: 624e27571fde34f8acc2afec840ac435 + size: 7430006 + timestamp: 1735585769731 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_1.conda + sha256: 630edf63981818ff590367cb95fddbed0f5a390464d0952c90ec81de899e84a6 + md5: 8a3cba079d6ac985e7d73c76a678fbb4 depends: - __osx >=11.0 - - c-ares >=1.34.2,<2.0a0 + - c-ares >=1.34.4,<2.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libcxx >=17 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libcxx >=18 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libre2-11 >=2024.7.2 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 + - openssl >=3.4.0,<4.0a0 - re2 constrains: - grpc-cpp =1.67.1 license: Apache-2.0 license_family: APACHE - size: 4882208 - timestamp: 1730236299095 -- kind: conda - name: libiconv - version: '1.17' - build: h0d3ecfb_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda - sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 - md5: 69bda57310071cf6d2b86caf11573d2d + size: 5311706 + timestamp: 1735585137716 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda + sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 + md5: d66573916ffcf376178462f1b61c941e + depends: + - libgcc-ng >=12 license: LGPL-2.1-only - size: 676469 - timestamp: 1702682458114 -- kind: conda - name: libiconv - version: '1.17' - build: h31becfc_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda + size: 705775 + timestamp: 1702682170569 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.17-h31becfc_2.conda sha256: a30e09d089cb75a0d5b8e5c354694c1317da98261185ed65aa3793e741060614 md5: 9a8eb13f14de7d761555a98712e6df65 depends: @@ -4555,27 +3338,23 @@ packages: license: LGPL-2.1-only size: 705787 timestamp: 1702684557134 -- kind: conda - name: libiconv - version: '1.17' - build: hd590300_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda - sha256: 8ac2f6a9f186e76539439e50505d98581472fedb347a20e7d1f36429849f05c9 - md5: d66573916ffcf376178462f1b61c941e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda + sha256: bc7de5097b97bcafcf7deaaed505f7ce02f648aac8eccc0d5a47cc599a1d0304 + md5: 69bda57310071cf6d2b86caf11573d2d + license: LGPL-2.1-only + size: 676469 + timestamp: 1702682458114 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda + sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f + md5: ea25936bb4080d843790b586850f82b8 depends: - libgcc-ng >=12 - license: LGPL-2.1-only - size: 705775 - timestamp: 1702682170569 -- kind: conda - name: libjpeg-turbo - version: 3.0.0 - build: h31becfc_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 618575 + timestamp: 1694474974816 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.0.0-h31becfc_1.conda sha256: 675bc1f2a8581cd34a86c412663ec29c5f90c1d9f8d11866aa1ade5cdbdf8429 md5: ed24e702928be089d9ba3f05618515c6 depends: @@ -4585,13 +3364,7 @@ packages: license: IJG AND BSD-3-Clause AND Zlib size: 647126 timestamp: 1694475003570 -- kind: conda - name: libjpeg-turbo - version: 3.0.0 - build: hb547adb_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda sha256: a42054eaa38e84fc1e5ab443facac4bbc9d1b6b6f23f54b7bf4f1eb687e1d993 md5: 3ff1e053dc3a2b8e36b9bfa4256a58d1 constrains: @@ -4599,29 +3372,8 @@ packages: license: IJG AND BSD-3-Clause AND Zlib size: 547541 timestamp: 1694475104253 -- kind: conda - name: libjpeg-turbo - version: 3.0.0 - build: hd590300_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda - sha256: b954e09b7e49c2f2433d6f3bb73868eda5e378278b0f8c1dd10a7ef090e14f2f - md5: ea25936bb4080d843790b586850f82b8 - depends: - - libgcc-ng >=12 - constrains: - - jpeg <0.0.0a - license: IJG AND BSD-3-Clause AND Zlib - size: 618575 - timestamp: 1694474974816 -- kind: conda - name: liblapack - version: 3.9.0 - build: 26_linux64_openblas +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda build_number: 26 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-26_linux64_openblas.conda sha256: b76458c36331376911e0f98fa68109e02f4d5e5ebfffa79587ac69cef748bba1 md5: 3792604c43695d6a273bc5faaac47d48 depends: @@ -4631,15 +3383,11 @@ packages: - liblapacke 3.9.0 26_linux64_openblas - blas * openblas license: BSD-3-Clause + license_family: BSD size: 16338 timestamp: 1734432576650 -- kind: conda - name: liblapack - version: 3.9.0 - build: 26_linuxaarch64_openblas +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda build_number: 26 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-26_linuxaarch64_openblas.conda sha256: a42bd01498efe2ccf6d08d56ac3cbd3ceab79e06699ff5aac3da8e45a66738f7 md5: a5d4e18876393633da62fd8492c00156 depends: @@ -4649,15 +3397,11 @@ packages: - liblapacke 3.9.0 26_linuxaarch64_openblas - libcblas 3.9.0 26_linuxaarch64_openblas license: BSD-3-Clause + license_family: BSD size: 16403 timestamp: 1734432585123 -- kind: conda - name: liblapack - version: 3.9.0 - build: 26_osxarm64_openblas +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda build_number: 26 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda sha256: dd6d9a21e672aee4332f019c8229ce70cf5eaf6c2f4cbd1443b105fb66c00dc5 md5: cebad79038a75cfd28fa90d147a2d34d depends: @@ -4667,57 +3411,41 @@ packages: - libcblas 3.9.0 26_osxarm64_openblas - blas * openblas license: BSD-3-Clause + license_family: BSD size: 16624 timestamp: 1734433068120 -- kind: conda - name: liblzma - version: 5.6.3 - build: h39f12f2_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda - sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 - md5: b2553114a7f5e20ccd02378a77d836aa +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda + sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 + md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz ==5.6.3=*_1 license: 0BSD - size: 99129 - timestamp: 1733407496073 -- kind: conda - name: liblzma - version: 5.6.3 - build: h86ecc28_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda + size: 111132 + timestamp: 1733407410083 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.6.3-h86ecc28_1.conda sha256: d1cce0b7d62d1e54e2164d3e0667ee808efc6c3870256e5b47a150cd0bf46824 md5: eb08b903681f9f2432c320e8ed626723 depends: - libgcc >=13 + constrains: + - xz ==5.6.3=*_1 license: 0BSD size: 124138 timestamp: 1733409137214 -- kind: conda - name: liblzma - version: 5.6.3 - build: hb9d3cd8_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.3-hb9d3cd8_1.conda - sha256: e6e425252f3839e2756e4af1ea2074dffd3396c161bf460629f9dfd6a65f15c6 - md5: 2ecf2f1c7e4e21fcfe6423a51a992d84 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda + sha256: d863b8257406918ffdc50ae65502f2b2d6cede29404d09a094f59509d6a0aaf1 + md5: b2553114a7f5e20ccd02378a77d836aa depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 + constrains: + - xz ==5.6.3=*_1 license: 0BSD - size: 111132 - timestamp: 1733407410083 -- kind: conda - name: libnghttp2 - version: 1.64.0 - build: h161d5f1_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda + size: 99129 + timestamp: 1733407496073 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda sha256: b0f2b3695b13a989f75d8fd7f4778e1c7aabe3b36db83f0fe80b2cd812c0e975 md5: 19e57602824042dfd0446292ef90488b depends: @@ -4733,32 +3461,7 @@ packages: license_family: MIT size: 647599 timestamp: 1729571887612 -- kind: conda - name: libnghttp2 - version: 1.64.0 - build: h6d7220d_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda - sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f - md5: 3408c02539cee5f1141f9f11450b6a51 - depends: - - __osx >=11.0 - - c-ares >=1.34.2,<2.0a0 - - libcxx >=17 - - libev >=4.33,<4.34.0a0 - - libev >=4.33,<5.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 - license: MIT - license_family: MIT - size: 566719 - timestamp: 1729572385640 -- kind: conda - name: libnghttp2 - version: 1.64.0 - build: hc8609a4_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnghttp2-1.64.0-hc8609a4_0.conda sha256: c093c6d370aadbf0409c20b6c54c488ee2f6fea976181919fcc63e87ee232673 md5: f52c614fa214a8bedece9421c771670d depends: @@ -4773,26 +3476,22 @@ packages: license_family: MIT size: 714610 timestamp: 1729571912479 -- kind: conda - name: libnsl - version: 2.0.1 - build: h31becfc_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda - sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 - md5: c14f32510f694e3185704d89967ec422 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda + sha256: 00cc685824f39f51be5233b54e19f45abd60de5d8847f1a56906f8936648b72f + md5: 3408c02539cee5f1141f9f11450b6a51 depends: - - libgcc-ng >=12 - license: LGPL-2.1-only - license_family: GPL - size: 34501 - timestamp: 1697358973269 -- kind: conda - name: libnsl - version: 2.0.1 - build: hd590300_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - __osx >=11.0 + - c-ares >=1.34.2,<2.0a0 + - libcxx >=17 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: MIT + license_family: MIT + size: 566719 + timestamp: 1729572385640 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 depends: @@ -4801,33 +3500,16 @@ packages: license_family: GPL size: 33408 timestamp: 1697359010159 -- kind: conda - name: libopenblas - version: 0.3.28 - build: openmp_hf332438_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda - sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 - md5: 40803a48d947c8639da6704e9a44d3ce +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda + sha256: fd18c2b75d7411096428d36a70b36b1a17e31f7b8956b6905d145792d49e97f8 + md5: c14f32510f694e3185704d89967ec422 depends: - - __osx >=11.0 - - libgfortran 5.* - - libgfortran5 >=13.2.0 - - llvm-openmp >=18.1.8 - constrains: - - openblas >=0.3.28,<0.3.29.0a0 - license: BSD-3-Clause - license_family: BSD - size: 4165774 - timestamp: 1730772154295 -- kind: conda - name: libopenblas - version: 0.3.28 - build: pthreads_h94d23a6_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + size: 34501 + timestamp: 1697358973269 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_1.conda sha256: 99ba271d8a80a1af2723f2e124ffd91d850074c0389c067e6d96d72a2dbfeabe md5: 62857b389e42b36b686331bec0922050 depends: @@ -4841,13 +3523,7 @@ packages: license_family: BSD size: 5578513 timestamp: 1730772671118 -- kind: conda - name: libopenblas - version: 0.3.28 - build: pthreads_h9d3fd7e_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_1.conda sha256: 30623a40764e935aa77e0d4db54c1a1589189a9bf3a03fdb445505c1e319b5a6 md5: e8dde93dd199da3c1f2c1fcfd0042cd4 depends: @@ -4860,116 +3536,146 @@ packages: license_family: BSD size: 4793435 timestamp: 1730773029647 -- kind: conda - name: libparquet - version: 18.1.0 - build: h081d1f1_6_cpu - build_number: 6 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.1.0-h081d1f1_6_cpu.conda - sha256: c691a59f1ebb6cedbf827f49f6cf414e08b0eec911f589133e6a8321e8ac701c - md5: 68788df49ce7480187eb6387f15b2b67 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda + sha256: 62bb669c37a845129096f73d446cdb6bb170e4927f2fea2b661329680dbbc373 + md5: 40803a48d947c8639da6704e9a44d3ce + depends: + - __osx >=11.0 + - libgfortran 5.* + - libgfortran5 >=13.2.0 + - llvm-openmp >=18.1.8 + constrains: + - openblas >=0.3.28,<0.3.29.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4165774 + timestamp: 1730772154295 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.18.0-hfcad708_1.conda + sha256: 4ea235e08676f16b0d3c3380befe1478c0fa0141512ee709b011005c55c9619f + md5: 1f5a5d66e77a39dc5bd639ec953705cf + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgrpc >=1.67.1,<1.68.0a0 + - libopentelemetry-cpp-headers 1.18.0 ha770c72_1 + - libprotobuf >=5.28.3,<5.28.4.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.18.0 + license: Apache-2.0 + license_family: APACHE + size: 801927 + timestamp: 1735643375271 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.18.0-h0c05b2d_1.conda + sha256: c6bcbd53d62a9e0d8c667e560db0ca2ecb7679277cbb3c23457aabe74fcb8cba + md5: 19c46cc18825f3924251c39ec1b0d983 + depends: + - libabseil * cxx17* + - libabseil >=20240722.0,<20240723.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgrpc >=1.67.1,<1.68.0a0 + - libopentelemetry-cpp-headers 1.18.0 hce30654_1 + - libprotobuf >=5.28.3,<5.28.4.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.18.0 + license: Apache-2.0 + license_family: APACHE + size: 529588 + timestamp: 1735643889612 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.18.0-ha770c72_1.conda + sha256: aa1f7dea79ea8513ff77339ba7c6e9cf10dfa537143e7718b1cfb3af52b649f2 + md5: 4fb055f57404920a43b147031471e03b + license: Apache-2.0 + license_family: APACHE + size: 320359 + timestamp: 1735643346175 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.18.0-hce30654_1.conda + sha256: 82e5f5ba64debbaab3c601b265dfc0cdb4d2880feba9bada5fd2e67b9f91ada5 + md5: e965dad955841507549fdacd8f7f94c0 + license: Apache-2.0 + license_family: APACHE + size: 320565 + timestamp: 1735643673319 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-19.0.0-h081d1f1_5_cpu.conda + build_number: 5 + sha256: 582e1e47131be438f107ddb1cada5f53857f3da96b412a16df3b87de5f9e7688 + md5: 48e2ffd05532f9176b28769bbcdab6fa depends: - __glibc >=2.17,<3.0.a0 - - libarrow 18.1.0 h44a453e_6_cpu + - libarrow 19.0.0 h34b28d8_5_cpu - libgcc >=13 - libstdcxx >=13 - libthrift >=0.21.0,<0.21.1.0a0 - openssl >=3.4.0,<4.0a0 license: Apache-2.0 - license_family: APACHE - size: 1204535 - timestamp: 1733810811118 -- kind: conda - name: libparquet - version: 18.1.0 - build: h636d7b7_6_cpu - build_number: 6 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_6_cpu.conda - sha256: 88c1e810bede65c54f1ebc51c14400f9e8cf0fc1f88a8c0a99210e2f5dfed582 - md5: 9b333c3a38e55f6c1b8733222e22f528 + size: 1241994 + timestamp: 1737613576283 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_12_cpu.conda + build_number: 12 + sha256: 224da9fb417e1dc569700cddf6dd7febe9a6e5c12547a389a5b934b8d385c4f8 + md5: 517447be85cda09f9410ca609c69499d depends: - - __osx >=11.0 - - libarrow 18.1.0 h4a2f8bd_6_cpu - - libcxx >=18 + - libarrow 18.1.0 h4065667_12_cpu + - libgcc >=13 + - libstdcxx >=13 - libthrift >=0.21.0,<0.21.1.0a0 - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: APACHE - size: 873134 - timestamp: 1733809271282 -- kind: conda - name: libparquet - version: 18.1.0 - build: hfc78867_6_cpu - build_number: 6 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libparquet-18.1.0-hfc78867_6_cpu.conda - sha256: 38aab34c422519c530d0e9a3e0ffd1624db1c1e163983c46ae341e831b2eb6b5 - md5: 1ab6d4a9a982920b9dc5f2c700777b27 - depends: - - libarrow 18.1.0 h1b535d6_6_cpu - - libgcc >=13 - - libstdcxx >=13 + size: 1116762 + timestamp: 1737451682325 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-19.0.0-h636d7b7_5_cpu.conda + build_number: 5 + sha256: f48445a10dcbceee9f8ef738945f9daf144d31bf715276d192e3679c6aa866b4 + md5: 9ac736aecf1b6c985e1a72e94c61c8e6 + depends: + - __osx >=11.0 + - libarrow 19.0.0 h0ec2bd1_5_cpu + - libcxx >=18 - libthrift >=0.21.0,<0.21.1.0a0 - openssl >=3.4.0,<4.0a0 license: Apache-2.0 - license_family: APACHE - size: 1117592 - timestamp: 1733810440129 -- kind: conda - name: libpng - version: 1.6.44 - build: hadc24fc_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.44-hadc24fc_0.conda - sha256: e5b14f7a01c2db4362d8591f42f82f336ed48d5e4079e4d1f65d0c2a3637ea78 - md5: f4cc49d7aa68316213e4b12be35308d1 + size: 894523 + timestamp: 1737610778027 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.45-h943b412_0.conda + sha256: b8f5b5ba9a14dedf7c97c01300de492b1b52b68eacbc3249a13fdbfa82349a2f + md5: 85cbdaacad93808395ac295b5667d25b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement - size: 290661 - timestamp: 1726234747153 -- kind: conda - name: libpng - version: 1.6.44 - build: hc14010f_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.44-hc14010f_0.conda - sha256: 38f8759a3eb8060deabd4db41f0f023514d853e46ddcbd0ba21768fc4e563bb1 - md5: fb36e93f0ea6a6f5d2b99984f34b049e + size: 289426 + timestamp: 1736339058310 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.45-hec79eb8_0.conda + sha256: a06daa523a0d5775acb96e6e3f083adc2ab1383eb8f664513dbc663f439c9cca + md5: 9a8716c16b40acc7148263de1d0a403b depends: - - __osx >=11.0 + - libgcc >=13 - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement - size: 263385 - timestamp: 1726234714421 -- kind: conda - name: libpng - version: 1.6.44 - build: hc4a20ef_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.44-hc4a20ef_0.conda - sha256: 23b5ce15cf9c6017641a8396bab00ae807dd9f662718cfa7f61de114d0c97647 - md5: 5d25802b25fcc7419fa13e21affaeb3a + size: 299051 + timestamp: 1736344007986 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.45-h3783ad8_0.conda + sha256: ddcc81c049b32fb5eb3ac1f9a6d3a589c08325c8ec6f89eb912208b19330d68c + md5: d554c806d065b1763cb9e1cb1d25741d depends: - - libgcc >=13 + - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement - size: 294907 - timestamp: 1726236639270 -- kind: conda - name: libprotobuf - version: 5.28.2 - build: h029595c_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.2-h029595c_0.conda - sha256: d8c7b6f851bfc53494d9b8e54d473c4f11ab26483a6e64df6f7967563df166b1 - md5: 538dbe0ad9f248e2e109abb9b6809ea5 + size: 263151 + timestamp: 1736339184358 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.3-h6128344_1.conda + sha256: 51125ebb8b7152e4a4e69fd2398489c4ec8473195c27cde3cbdf1cb6d18c5493 + md5: d8703f1ffe5a06356f06467f1d0b9464 depends: + - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libgcc >=13 @@ -4977,18 +3683,12 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD - size: 2802876 - timestamp: 1728564881988 -- kind: conda - name: libprotobuf - version: 5.28.2 - build: h5b01275_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda - sha256: 5e8fd4aa00193c85602ce6101dd28fe31306dff85c9725048f6dc828dfa7c421 - md5: ab0bff36363bec94720275a681af8b83 + size: 2960815 + timestamp: 1735577210663 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libprotobuf-5.28.3-h44a3b7b_1.conda + sha256: ecb69f2b1668e784b41ba667493be846662d5ef702bef64fb2e013bb1364cdc4 + md5: 68f807f7cc13951652bbe048253fd405 depends: - - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libgcc >=13 @@ -4996,36 +3696,26 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD - size: 2945348 - timestamp: 1728565355702 -- kind: conda - name: libprotobuf - version: 5.28.2 - build: h8f0b736_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.2-h8f0b736_0.conda - sha256: f732a6fa918428e2d5ba61e78fe11bb44a002cc8f6bb74c94ee5b1297fefcfd8 - md5: d2cb5991f2fb8eb079c80084435e9ce6 + size: 2788074 + timestamp: 1735576315676 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda + sha256: f58a16b13ad53346903c833e266f83c3d770a43a432659b98710aed85ca885e7 + md5: bdbfea4cf45ae36652c6bbcc2e7ebe91 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libcxx >=17 + - libcxx >=18 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD - size: 2374965 - timestamp: 1728565334796 -- kind: conda - name: libre2-11 - version: 2024.07.02 - build: h18dbdb1_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_1.conda - sha256: 96d4fdac28d5af38c38f90c22cb0aa9a90affae13ca8ba24bd1eb60b789df8ff - md5: f1800796b0efc4bbc5b001d845545111 + size: 2271580 + timestamp: 1735576361997 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_2.conda + sha256: 4420f8362c71251892ba1eeb957c5e445e4e1596c0c651c28d0d8b415fe120c7 + md5: b2fede24428726dd867611664fb372e8 depends: + - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libgcc >=13 @@ -5034,55 +3724,37 @@ packages: - re2 2024.07.02.* license: BSD-3-Clause license_family: BSD - size: 203516 - timestamp: 1728778974654 -- kind: conda - name: libre2-11 - version: 2024.07.02 - build: h2348fd5_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h2348fd5_1.conda - sha256: 6facca42cfc85a05b33e484a8b0df7857cc092db34806946d022270098d8d20f - md5: 5a7065309a66097738be6a06fd04b7ef + size: 209793 + timestamp: 1735541054068 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libre2-11-2024.07.02-h18dbdb1_2.conda + sha256: 862c20de0120f802e618dcb25913d00c5b82f91f4be60b2d46a774e851adc2f6 + md5: 9a7dbbaab49f76a6f36e5c9d98e323a7 depends: - - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libcxx >=17 + - libgcc >=13 + - libstdcxx >=13 constrains: - re2 2024.07.02.* license: BSD-3-Clause license_family: BSD - size: 165956 - timestamp: 1728779107218 -- kind: conda - name: libre2-11 - version: 2024.07.02 - build: hbbce691_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda - sha256: f8ad6a4f6d4fd54ebe3e5e712a01e663222fc57f49d16b6b8b10c30990dafb8f - md5: 2124de47357b7a516c0a3efd8f88c143 + size: 204305 + timestamp: 1735540986919 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda + sha256: 112a73ad483353751d4c5d63648c69a4d6fcebf5e1b698a860a3f5124fc3db96 + md5: 6b1e3624d3488016ca4f1ca0c412efaa depends: - - __glibc >=2.17,<3.0.a0 + - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - - libgcc >=13 - - libstdcxx >=13 + - libcxx >=18 constrains: - re2 2024.07.02.* license: BSD-3-Clause license_family: BSD - size: 211096 - timestamp: 1728778964655 -- kind: conda - name: libsodium - version: 1.0.20 - build: h4ab18f5_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + size: 167155 + timestamp: 1735541067807 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 md5: a587892d3c13b6621a6091be690dbca2 depends: @@ -5090,12 +3762,7 @@ packages: license: ISC size: 205978 timestamp: 1716828628198 -- kind: conda - name: libsodium - version: 1.0.20 - build: h68df207_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsodium-1.0.20-h68df207_0.conda sha256: 448df5ea3c5cf1af785aad46858d7a5be0522f4234a4dc9bb764f4d11ff3b981 md5: 2e4a8f23bebdcb85ca8e5a0fbe75666a depends: @@ -5103,12 +3770,7 @@ packages: license: ISC size: 177394 timestamp: 1716828514515 -- kind: conda - name: libsodium - version: 1.0.20 - build: h99b78c6_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 md5: a7ce36e284c5faaf93c220dfc39e3abd depends: @@ -5116,70 +3778,47 @@ packages: license: ISC size: 164972 timestamp: 1716828607917 -- kind: conda - name: libsqlite - version: 3.47.2 - build: h3f77e49_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.47.2-h3f77e49_0.conda - sha256: f192f3c8973de9ec4c214990715f13b781965247a5cedf9162e7f9e699cfc3c4 - md5: 122d6f29470f1a991e85608e77e56a8a +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.48.0-hee588c1_1.conda + sha256: 22853d289ef6ec8a5b20f1aa261895b06525439990d3b139f8bfd0b5c5e32a3a + md5: 3fa05c528d8a1e2a67bbf1e36f22d3bc depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 - libzlib >=1.3.1,<2.0a0 license: Unlicense - size: 850553 - timestamp: 1733762057506 -- kind: conda - name: libsqlite - version: 3.47.2 - build: h5eb1b54_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.2-h5eb1b54_0.conda - sha256: 885a27fa84a5a73ed9779168c02b6c386e2fc7a53f0566b32a09ceca146b42b4 - md5: d4bf59f8783a4a66c0aec568f6de3ff4 + size: 878223 + timestamp: 1737564987837 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.48.0-h5eb1b54_1.conda + sha256: 81dd9bf66c7f1d9064e007e2c787133100c406e7ca2de61dba9fb7b87372f255 + md5: 4f3a61fe206f20b27c385ee608bcdfda depends: - libgcc >=13 - libzlib >=1.3.1,<2.0a0 license: Unlicense - size: 1042182 - timestamp: 1733761913736 -- kind: conda - name: libsqlite - version: 3.47.2 - build: hee588c1_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.2-hee588c1_0.conda - sha256: 48af21ebc2cbf358976f1e0f4a0ab9e91dfc83d0ef337cf3837c6f5bc22fb352 - md5: b58da17db24b6e08bcbf8fed2fb8c915 + size: 1044879 + timestamp: 1737565049785 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.48.0-h3f77e49_1.conda + sha256: 17c06940cc2a13fd6a17effabd6881b1477db38b2cd3ee2571092d293d3fdd75 + md5: 4c55169502ecddf8077973a987d08f08 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: Unlicense - size: 873551 - timestamp: 1733761824646 -- kind: conda - name: libssh2 - version: 1.11.1 - build: h9cc3647_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda - sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 - md5: ddc7194676c285513706e5fc64f214d7 + size: 852831 + timestamp: 1737564996616 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda + sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 + md5: be2de152d8073ef1c01b7728475f2fe7 depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 - libzlib >=1.3.1,<2.0a0 - openssl >=3.4.0,<4.0a0 license: BSD-3-Clause license_family: BSD - size: 279028 - timestamp: 1732349599461 -- kind: conda - name: libssh2 - version: 1.11.1 - build: ha41c0db_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda + size: 304278 + timestamp: 1732349402869 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libssh2-1.11.1-ha41c0db_0.conda sha256: 40f2af5357457546bd11cd64a3b9043d83865180f65ce602515c35f353be35c7 md5: aeffe03c0e598f015aab08dbb04f6ee4 depends: @@ -5190,45 +3829,17 @@ packages: license_family: BSD size: 311577 timestamp: 1732349396421 -- kind: conda - name: libssh2 - version: 1.11.1 - build: hf672d98_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hf672d98_0.conda - sha256: 0407ac9fda2bb67e11e357066eff144c845801d00b5f664efbc48813af1e7bb9 - md5: be2de152d8073ef1c01b7728475f2fe7 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.4.0,<4.0a0 - license: BSD-3-Clause - license_family: BSD - size: 304278 - timestamp: 1732349402869 -- kind: conda - name: libstdcxx - version: 14.2.0 - build: h3f4de04_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda - sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 - md5: 37f489acd39e22b623d2d1e5ac6d195c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda + sha256: f7047c6ed44bcaeb04432e8c74da87591940d091b0a3940c0d884b7faa8062e9 + md5: ddc7194676c285513706e5fc64f214d7 depends: - - libgcc 14.2.0 he277a41_1 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - size: 3816794 - timestamp: 1729089463404 -- kind: conda - name: libstdcxx - version: 14.2.0 - build: hc0a3c3a_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279028 + timestamp: 1732349599461 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda sha256: 4661af0eb9bdcbb5fb33e5d0023b001ad4be828fccdcc56500059d56f9869462 md5: 234a5554c53625688d51062645337328 depends: @@ -5237,13 +3848,16 @@ packages: license_family: GPL size: 3893695 timestamp: 1729027746910 -- kind: conda - name: libstdcxx-ng - version: 14.2.0 - build: h4852527_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_1.conda + sha256: 519556d2c93f1b487091ce046d62e762286177f4a670ec10e16005177d0bcab3 + md5: 37f489acd39e22b623d2d1e5ac6d195c + depends: + - libgcc 14.2.0 he277a41_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3816794 + timestamp: 1729089463404 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda sha256: 25bb30b827d4f6d6f0522cc0579e431695503822f144043b93c50237017fffd8 md5: 8371ac6457591af2cf6159439c1fd051 depends: @@ -5252,13 +3866,7 @@ packages: license_family: GPL size: 54105 timestamp: 1729027780628 -- kind: conda - name: libstdcxx-ng - version: 14.2.0 - build: hf1166c9_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda sha256: 9f97461bd55a2745a7a0941f3502a047f15bfe7bb2952dc7fb204b3202f866fd md5: 0e75771b8a03afae5a2c6ce71bc733f5 depends: @@ -5267,12 +3875,7 @@ packages: license_family: GPL size: 54133 timestamp: 1729089498541 -- kind: conda - name: libthrift - version: 0.21.0 - build: h0e7cc3e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda sha256: ebb395232973c18745b86c9a399a4725b2c39293c9a91b8e59251be013db42f0 md5: dcb95c0a98ba9ff737f7ae482aef7833 depends: @@ -5286,12 +3889,7 @@ packages: license_family: APACHE size: 425773 timestamp: 1727205853307 -- kind: conda - name: libthrift - version: 0.21.0 - build: h154c74f_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libthrift-0.21.0-h154c74f_0.conda sha256: f04ab1417aca1687edff9c30d8423ace285eb8c053dc16d595c6e47cfeefb274 md5: c28792bf37f4ecdce8e3cb9e40750650 depends: @@ -5304,12 +3902,7 @@ packages: license_family: APACHE size: 417329 timestamp: 1727205944238 -- kind: conda - name: libthrift - version: 0.21.0 - build: h64651cc_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda sha256: 7a6c7d5f58cbbc2ccd6493b4b821639fdb0701b9b04c737a949e8cb6adf1c9ad md5: 7ce2bd2f650f8c31ad7ba4c7bfea61b7 depends: @@ -5322,35 +3915,24 @@ packages: license_family: APACHE size: 324342 timestamp: 1727206096912 -- kind: conda - name: libtiff - version: 4.7.0 - build: h551f018_3 - build_number: 3 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda - sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae - md5: a5d084a957563e614ec0c0196d890654 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda + sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 + md5: 0ea6510969e1296cc19966fad481f6de depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 - lerc >=4.0.0,<5.0a0 - - libcxx >=18 - libdeflate >=1.23,<1.24.0a0 + - libgcc >=13 - libjpeg-turbo >=3.0.0,<4.0a0 - liblzma >=5.6.3,<6.0a0 + - libstdcxx >=13 - libwebp-base >=1.4.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.6,<1.6.0a0 license: HPND - size: 370600 - timestamp: 1734398863052 -- kind: conda - name: libtiff - version: 4.7.0 - build: h88f7998_3 - build_number: 3 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda + size: 428173 + timestamp: 1734398813264 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_3.conda sha256: 5888bd66ba7606ae8596856c7dac800940ecad0aed77d6aa37db69d434c81cf0 md5: 36a0ea4a173338c8725dc0807e99cf22 depends: @@ -5366,81 +3948,51 @@ packages: license: HPND size: 464699 timestamp: 1734398752249 -- kind: conda - name: libtiff - version: 4.7.0 - build: hd9ff511_3 - build_number: 3 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_3.conda - sha256: b224e16b88d76ea95e4af56e2bc638c603bd26a770b98d117d04541d3aafa002 - md5: 0ea6510969e1296cc19966fad481f6de +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda + sha256: 91417846157e04992801438a496b151df89604b2e7c6775d6f701fcd0cbed5ae + md5: a5d084a957563e614ec0c0196d890654 depends: - - __glibc >=2.17,<3.0.a0 + - __osx >=11.0 - lerc >=4.0.0,<5.0a0 + - libcxx >=18 - libdeflate >=1.23,<1.24.0a0 - - libgcc >=13 - libjpeg-turbo >=3.0.0,<4.0a0 - liblzma >=5.6.3,<6.0a0 - - libstdcxx >=13 - libwebp-base >=1.4.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.6,<1.6.0a0 license: HPND - size: 428173 - timestamp: 1734398813264 -- kind: conda - name: libutf8proc - version: 2.9.0 - build: h5505292_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda - sha256: ea88f06e97ef8fa2490f7594f8885bb542577226edf8abba3144302d951a53c2 - md5: f777470d31c78cd0abe1903a2fda436f + size: 370600 + timestamp: 1734398863052 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.10.0-h4c51ac1_0.conda + sha256: 8e41563ee963bf8ded06da45f4e70bf42f913cb3c2e79364eb3218deffa3cd74 + md5: aeccfff2806ae38430638ffbb4be9610 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: MIT license_family: MIT - size: 83000 - timestamp: 1732868631531 -- kind: conda - name: libutf8proc - version: 2.9.0 - build: h86ecc28_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.9.0-h86ecc28_1.conda - sha256: 37a1833c55f9945724cd4b3eb6a1469032cc754a1dd725f191c34154ad2ba7e4 - md5: 699f155da290be3a1a64c932c6728991 + size: 82745 + timestamp: 1737244366901 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libutf8proc-2.10.0-ha346350_0.conda + sha256: 9c333e07b76ae1ea2c882db764be52dc82f632be66d993a50b35ca9c5475074a + md5: c5166bcfb8348e8fc31ee16ec3981a5e depends: - libgcc >=13 license: MIT license_family: MIT - size: 81526 - timestamp: 1732868466862 -- kind: conda - name: libutf8proc - version: 2.9.0 - build: hb9d3cd8_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.9.0-hb9d3cd8_1.conda - sha256: 9794e6388e780c3310d46f773bbc924d4053375c3fcdb07a704b57f4616db928 - md5: 1e936bd23d737aac62a18e9a1e7f8b18 + size: 82679 + timestamp: 1737329054400 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.10.0-hda25de7_0.conda + sha256: aca3ef31d3dff5cefd3790742a5ee6548f1cf0201d0e8cee08b01da503484eb6 + md5: 5f741aed1d8d393586a5fdcaaa87f45c depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 license: MIT license_family: MIT - size: 81500 - timestamp: 1732868419835 -- kind: conda - name: libuuid - version: 2.38.1 - build: h0b41bf4_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + size: 83628 + timestamp: 1737244450097 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 md5: 40b61aab5c7ba9ff276c41cfffe6b80b depends: @@ -5449,12 +4001,7 @@ packages: license_family: BSD size: 33601 timestamp: 1680112270483 -- kind: conda - name: libuuid - version: 2.38.1 - build: hb4cce97_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda sha256: 616277b0c5f7616c2cdf36f6c316ea3f9aa5bb35f2d4476a349ab58b9b91675f md5: 000e30b09db0b7c775b21695dff30969 depends: @@ -5463,136 +4010,94 @@ packages: license_family: BSD size: 35720 timestamp: 1680113474501 -- kind: conda - name: libuv - version: 1.49.2 - build: h7ab814d_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.49.2-h7ab814d_0.conda - sha256: 0e5176af1e788ad5006cf261c4ea5a288a935fda48993b0240ddd2e562dc3d02 - md5: 4bc348e3a1a74d20a3f9beb866d75e0a +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda + sha256: b4a8890023902aef9f1f33e3e35603ad9c2f16c21fdb58e968fa6c1bd3e94c0b + md5: 771ee65e13bc599b0b62af5359d80169 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: MIT license_family: MIT - size: 410500 - timestamp: 1729322654121 -- kind: conda - name: libuv - version: 1.49.2 - build: h86ecc28_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.49.2-h86ecc28_0.conda - sha256: adf4eca89339ac7780f2394e7e6699be81259eb91f79f9d9fdf2c1bc6b26f210 - md5: 1899e1ec2be63386c41c4db31d3056af + size: 891272 + timestamp: 1737016632446 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuv-1.50.0-h86ecc28_0.conda + sha256: 67914c7f171d343059144d804c2f17fcd621a94e45f179a0fd843b8c1618823e + md5: 915db044076cbbdffb425170deb4ce38 depends: - libgcc >=13 license: MIT license_family: MIT - size: 627484 - timestamp: 1729322575379 -- kind: conda - name: libuv - version: 1.49.2 - build: hb9d3cd8_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda - sha256: a35cd81cd1a9add11024097da83cc06b0aae83186fe4124b77710876f37d8f31 - md5: 070e3c9ddab77e38799d5c30b109c633 + size: 621056 + timestamp: 1737016626950 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.50.0-h5505292_0.conda + sha256: d13fb49d4c8262bf2c44ffb2c77bb2b5d0f85fc6de76bdb75208efeccb29fce6 + md5: 20717343fb30798ab7c23c2e92b748c1 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 license: MIT license_family: MIT - size: 884647 - timestamp: 1729322566955 -- kind: conda - name: libwebp-base - version: 1.4.0 - build: h31becfc_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda - sha256: 10dded60f274e29c573cfacf6e96f5d0fc374ee431250374a44cbd773916ab9d - md5: 5fd7ab3e5f382c70607fbac6335e6e19 + size: 418890 + timestamp: 1737016751326 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda + sha256: c45283fd3e90df5f0bd3dbcd31f59cdd2b001d424cf30a07223655413b158eaf + md5: 63f790534398730f59e1b899c3644d4a depends: - - libgcc-ng >=12 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 constrains: - - libwebp 1.4.0 + - libwebp 1.5.0 license: BSD-3-Clause license_family: BSD - size: 363577 - timestamp: 1713201785160 -- kind: conda - name: libwebp-base - version: 1.4.0 - build: h93a5062_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda - sha256: 0d4bad713a512d79bfeb4d61821f447afab8b0792aca823f505ce6b195e9fde5 - md5: c0af0edfebe780b19940e94871f1a765 + size: 429973 + timestamp: 1734777489810 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.5.0-h0886dbf_0.conda + sha256: b3d881a0ae08bb07fff7fa8ead506c8d2e0388733182fe4f216f3ec5d61ffcf0 + md5: 95ef4a689b8cc1b7e18b53784d88f96b + depends: + - libgcc >=13 constrains: - - libwebp 1.4.0 + - libwebp 1.5.0 license: BSD-3-Clause license_family: BSD - size: 287750 - timestamp: 1713200194013 -- kind: conda - name: libwebp-base - version: 1.4.0 - build: hd590300_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda - sha256: 49bc5f6b1e11cb2babf2a2a731d1a680a5e08a858280876a779dbda06c78c35f - md5: b26e8aa824079e1be0294e7152ca4559 + size: 362623 + timestamp: 1734779054659 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.5.0-h2471fea_0.conda + sha256: f8bdb876b4bc8cb5df47c28af29188de8911c3fea4b799a33743500149de3f4a + md5: 569466afeb84f90d5bb88c11cc23d746 depends: - - libgcc-ng >=12 + - __osx >=11.0 constrains: - - libwebp 1.4.0 + - libwebp 1.5.0 license: BSD-3-Clause license_family: BSD - size: 438953 - timestamp: 1713199854503 -- kind: conda - name: libxcb - version: 1.17.0 - build: h262b8f6_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda - sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b - md5: cd14ee5cca2464a425b1dbfc24d90db2 + size: 290013 + timestamp: 1734777593617 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - pthread-stubs - xorg-libxau >=1.0.11,<2.0a0 - xorg-libxdmcp license: MIT license_family: MIT - size: 397493 - timestamp: 1727280745441 -- kind: conda - name: libxcb - version: 1.17.0 - build: h8a09558_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa - md5: 92ed62436b625154323d40d5f2f11dd7 + size: 395888 + timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda + sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b + md5: cd14ee5cca2464a425b1dbfc24d90db2 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - pthread-stubs - xorg-libxau >=1.0.11,<2.0a0 - xorg-libxdmcp license: MIT license_family: MIT - size: 395888 - timestamp: 1727278577118 -- kind: conda - name: libxcb - version: 1.17.0 - build: hdb1d25a_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + size: 397493 + timestamp: 1727280745441 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 md5: af523aae2eca6dfa1c8eec693f5b9a79 depends: @@ -5604,27 +4109,7 @@ packages: license_family: MIT size: 323658 timestamp: 1727278733917 -- kind: conda - name: libxcrypt - version: 4.4.36 - build: h31becfc_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda - sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f - md5: b4df5d7d4b63579d081fd3a4cf99740e - depends: - - libgcc-ng >=12 - license: LGPL-2.1-or-later - size: 114269 - timestamp: 1702724369203 -- kind: conda - name: libxcrypt - version: 4.4.36 - build: hd590300_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c md5: 5aa797f8787fe7a17d1b0821485b5adc depends: @@ -5632,13 +4117,15 @@ packages: license: LGPL-2.1-or-later size: 100393 timestamp: 1702724383534 -- kind: conda - name: libxml2 - version: 2.13.5 - build: h0d44e9d_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda + sha256: 6b46c397644091b8a26a3048636d10b989b1bf266d4be5e9474bf763f828f41f + md5: b4df5d7d4b63579d081fd3a4cf99740e + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + size: 114269 + timestamp: 1702724369203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.5-h0d44e9d_1.conda sha256: 306e18aa647d8208ad2cd0e62d84933222b2fbe93d2d53cd5283d2256b1d54de md5: f5b05674697ae7d2c5932766695945e1 depends: @@ -5653,68 +4140,45 @@ packages: license_family: MIT size: 689993 timestamp: 1733443678322 -- kind: conda - name: libxml2 - version: 2.13.5 - build: h178c5d8_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda - sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 - md5: 3dc3cff0eca1640a6acbbfab2f78139e +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda + sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 + md5: 63410f85031930cde371dfe0ee89109a depends: - - __osx >=11.0 - icu >=75.1,<76.0a0 + - libgcc >=13 - libiconv >=1.17,<2.0a0 - liblzma >=5.6.3,<6.0a0 - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT - size: 582898 - timestamp: 1733443841584 -- kind: conda - name: libxml2 - version: 2.13.5 - build: h2e0c361_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.5-h2e0c361_1.conda - sha256: dc0e86d35a836af6e99d18f50c6551fc64c53ed3a3da5a9fea90e78763cf14b4 - md5: 63410f85031930cde371dfe0ee89109a + size: 732155 + timestamp: 1733443825814 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda + sha256: d7af3f25a4cece170502acd38f2dafbea4521f373f46dcb28a37fbe6ac2da544 + md5: 3dc3cff0eca1640a6acbbfab2f78139e depends: + - __osx >=11.0 - icu >=75.1,<76.0a0 - - libgcc >=13 - libiconv >=1.17,<2.0a0 - liblzma >=5.6.3,<6.0a0 - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT - size: 732155 - timestamp: 1733443825814 -- kind: conda - name: libzlib - version: 1.3.1 - build: h8359307_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b - md5: 369964e85dc26bfe78f41399b366c435 + size: 582898 + timestamp: 1733443841584 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 constrains: - zlib 1.3.1 *_2 license: Zlib license_family: Other - size: 46438 - timestamp: 1727963202283 -- kind: conda - name: libzlib - version: 1.3.1 - build: h86ecc28_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda + size: 60963 + timestamp: 1727963148474 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 md5: 08aad7cbe9f5a6b460d0976076b6ae64 depends: @@ -5725,78 +4189,38 @@ packages: license_family: Other size: 66657 timestamp: 1727963199518 -- kind: conda - name: libzlib - version: 1.3.1 - build: hb9d3cd8_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 - md5: edb0dca6bc32e4f4789199455a1dbeb8 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 constrains: - zlib 1.3.1 *_2 license: Zlib license_family: Other - size: 60963 - timestamp: 1727963148474 -- kind: conda - name: lit - version: 19.1.5 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.5-pyhd8ed1ab_0.conda - sha256: 07854df4ab39a333155b4813338caf8e0f6fe5a9abc84518d9409aa5cd91f94c - md5: ad3f4f4e25b666610c281c6fb92f06f9 + size: 46438 + timestamp: 1727963202283 +- conda: https://conda.anaconda.org/conda-forge/noarch/lit-19.1.7-pyhd8ed1ab_0.conda + sha256: 3527dcdd45af61cc00fb7efe4c454a956d421dde4b459de3beb0993352a4e0cb + md5: 665a0aafb15ba7a5771cca614146c718 depends: - python >=3 license: Apache-2.0 WITH LLVM-exception license_family: Apache - size: 128621 - timestamp: 1733310809397 -- kind: conda - name: llvm-openmp - version: 19.1.5 - build: hdb05f8b_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.5-hdb05f8b_0.conda - sha256: e7ba0d8b718925efdcf1309f5e776e3264cc172d3af8d4048b39627c50a1abc0 - md5: f2c2e187a1d2637d282e34dc92021a70 + size: 128580 + timestamp: 1736894117712 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.7-hdb05f8b_0.conda + sha256: b92a669f2059874ebdcb69041b6c243d68ffc3fb356ac1339cec44aeb27245d7 + md5: c4d54bfd3817313ce758aa76283b118d depends: - __osx >=11.0 constrains: - - openmp 19.1.5|19.1.5.* + - openmp 19.1.7|19.1.7.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE - size: 281120 - timestamp: 1733376089600 -- kind: conda - name: lz4-c - version: 1.10.0 - build: h286801f_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda - sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 - md5: 01511afc6cc1909c5303cf31be17b44f - depends: - - __osx >=11.0 - - libcxx >=18 - license: BSD-2-Clause - license_family: BSD - size: 148824 - timestamp: 1733741047892 -- kind: conda - name: lz4-c - version: 1.10.0 - build: h5888daf_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + size: 280830 + timestamp: 1736986295869 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 md5: 9de5350a85c4a20c685259b889aa6393 depends: @@ -5807,13 +4231,7 @@ packages: license_family: BSD size: 167055 timestamp: 1733741040117 -- kind: conda - name: lz4-c - version: 1.10.0 - build: h5ad3122_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lz4-c-1.10.0-h5ad3122_1.conda sha256: 67e55058d275beea76c1882399640c37b5be8be4eb39354c94b610928e9a0573 md5: 6654e411da94011e8fbe004eacb8fe11 depends: @@ -5823,14 +4241,17 @@ packages: license_family: BSD size: 184953 timestamp: 1733740984533 -- kind: conda - name: markdown-it-py - version: 3.0.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a md5: fee3164ac23dfca50cfcc8b85ddefb81 depends: @@ -5840,13 +4261,7 @@ packages: license_family: MIT size: 64430 timestamp: 1733250550053 -- kind: conda - name: markupsafe - version: 3.0.2 - build: py312h178313f_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda sha256: 4a6bf68d2a2b669fecc9a4a009abd1cf8e72c2289522ff00d81b5a6e51ae78f5 md5: eb227c3e0bf58f5bd69c0532b157975b depends: @@ -5860,13 +4275,7 @@ packages: license_family: BSD size: 24604 timestamp: 1733219911494 -- kind: conda - name: markupsafe - version: 3.0.2 - build: py312h74ce7d3_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.2-py312h74ce7d3_1.conda sha256: 1d500158262f30b9c23e37d1c861fe76e127a3926d69b3b38c25d20d3faa6f9f md5: bc8607ab678073a0441808a31465f4fb depends: @@ -5879,13 +4288,7 @@ packages: license_family: BSD size: 25079 timestamp: 1733220639175 -- kind: conda - name: markupsafe - version: 3.0.2 - build: py312h998013c_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py312h998013c_1.conda sha256: 4aa997b244014d3707eeef54ab0ee497d12c0d0d184018960cce096169758283 md5: 46e547061080fddf9cf95a0327e8aba6 depends: @@ -5899,13 +4302,8 @@ packages: license_family: BSD size: 24048 timestamp: 1733219945697 -- kind: conda - name: max - version: 24.6.0 - build: release - subdir: noarch +- conda: https://conda.modular.com/max/noarch/max-24.6.0-release.conda noarch: python - url: https://conda.modular.com/max/noarch/max-24.6.0-release.conda sha256: 0e3c1984ac7476550fd8fa5921bf1ca58950219b84ae21ecd861f650e45382ac md5: e04b1405f630c9bb7d4cb5559840e902 depends: @@ -5916,57 +4314,31 @@ packages: license: LicenseRef-Modular-Proprietary size: 9851 timestamp: 1734039439696 -- kind: conda - name: max-core - version: 24.6.0 - build: release - subdir: linux-64 - url: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda +- conda: https://conda.modular.com/max/linux-64/max-core-24.6.0-release.conda sha256: 38a4128c15b230f5b05e0606a339c7866a83eb943d334a948b3a8c1d2675a917 md5: 25e678ff7c59e36ec3154fe0cd15ebde depends: - mblack ==24.6.0 release - arch: x86_64 - platform: linux license: LicenseRef-Modular-Proprietary size: 247670119 timestamp: 1734039439695 -- kind: conda - name: max-core - version: 24.6.0 - build: release - subdir: linux-aarch64 - url: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda +- conda: https://conda.modular.com/max/linux-aarch64/max-core-24.6.0-release.conda sha256: 554f2c1a6ddfab8fabf82b9fbcc1adb87e2a615669793e463f757ea452e02016 md5: 2ca790aa461fa28722c6f21fcd2af0b9 depends: - mblack ==24.6.0 release - arch: aarch64 - platform: linux license: LicenseRef-Modular-Proprietary size: 251486610 timestamp: 1734039413769 -- kind: conda - name: max-core - version: 24.6.0 - build: release - subdir: osx-arm64 - url: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda +- conda: https://conda.modular.com/max/osx-arm64/max-core-24.6.0-release.conda sha256: 434c29e35067e296db55525cd5cf38bb013a1f7a7bfa99845bf6c317de6cdc12 md5: 4a2ead0a9010c36b6193ea32f583e996 depends: - mblack ==24.6.0 release - arch: arm64 - platform: osx license: LicenseRef-Modular-Proprietary size: 212001240 timestamp: 1734039726703 -- kind: conda - name: max-python - version: 24.6.0 - build: 3.12release - subdir: linux-64 - url: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda +- conda: https://conda.modular.com/max/linux-64/max-python-24.6.0-3.12release.conda sha256: 6fbf7330ad910e6ec9fd581fd0f8505e5b1326ccf9979d553c70c61abf4c3e54 md5: 218ecd662f853ea1578404799d61b385 depends: @@ -5990,17 +4362,10 @@ packages: - typing_extensions - uvicorn - python_abi 3.12.* *_cp312 - arch: x86_64 - platform: linux license: LicenseRef-Modular-Proprietary size: 123785050 timestamp: 1734039439704 -- kind: conda - name: max-python - version: 24.6.0 - build: 3.12release - subdir: linux-aarch64 - url: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda +- conda: https://conda.modular.com/max/linux-aarch64/max-python-24.6.0-3.12release.conda sha256: 15f57cb436b00c510473ca1940edd9ee03df6c92d5b66d616db30a131b768b78 md5: 97c62f17f0d34787d29128043a593877 depends: @@ -6024,17 +4389,10 @@ packages: - typing_extensions - uvicorn - python_abi 3.12.* *_cp312 - arch: aarch64 - platform: linux license: LicenseRef-Modular-Proprietary size: 127403269 timestamp: 1734039413779 -- kind: conda - name: max-python - version: 24.6.0 - build: 3.12release - subdir: osx-arm64 - url: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda +- conda: https://conda.modular.com/max/osx-arm64/max-python-24.6.0-3.12release.conda sha256: c888b58cfc7c767d40aa100ff2bccf5c3ab11d58d897a6accb749e6b5b7014ea md5: 62a92bfab3b5c85c2d246672bbb8bc8d depends: @@ -6058,18 +4416,11 @@ packages: - typing_extensions - uvicorn - python_abi 3.12.* *_cp312 - arch: arm64 - platform: osx license: LicenseRef-Modular-Proprietary size: 112484803 timestamp: 1734039726707 -- kind: conda - name: mblack - version: 24.6.0 - build: release - subdir: noarch +- conda: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda noarch: python - url: https://conda.modular.com/max/noarch/mblack-24.6.0-release.conda sha256: f135164020478078f4681aa77e7f6ca9f68b8e7ee02604b85342bbaf2f706f0d md5: 77367aff981ba391ab5c047ba33ec978 depends: @@ -6083,14 +4434,7 @@ packages: license: MIT size: 130668 timestamp: 1734039439700 -- kind: conda - name: mdurl - version: 0.1.2 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 md5: 592132998493b3ff25fd7479396e8351 depends: @@ -6099,13 +4443,8 @@ packages: license_family: MIT size: 14465 timestamp: 1733255681319 -- kind: conda - name: mojo-jupyter - version: 24.6.0 - build: release - subdir: noarch +- conda: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda noarch: python - url: https://conda.modular.com/max/noarch/mojo-jupyter-24.6.0-release.conda sha256: 2fe043d98ea77f8f165b39bd252cd04942216c8533f0291c49d87d6cfd8673df md5: b17127f3ca2cef0976496407e1cd4081 depends: @@ -6116,13 +4455,7 @@ packages: license: LicenseRef-Modular-Proprietary size: 22990 timestamp: 1734039439702 -- kind: conda - name: multidict - version: 6.1.0 - build: py312h178313f_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.1.0-py312h178313f_2.conda sha256: b05bc8252a6e957bf4a776ed5e0e61d1ba88cdc46ccb55890c72cc58b10371f4 md5: 5b5e3267d915a107eca793d52e1b780a depends: @@ -6134,13 +4467,7 @@ packages: license_family: APACHE size: 61507 timestamp: 1733913288935 -- kind: conda - name: multidict - version: 6.1.0 - build: py312hcc812fe_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multidict-6.1.0-py312hcc812fe_2.conda sha256: ff9f767ba4df68e9ac2a380529a83a2fb6abd985beee9eab16608f7e2c3ccc6e md5: dcf3ae213cf0ab40ebcc10452e1ed9fa depends: @@ -6152,13 +4479,7 @@ packages: license_family: APACHE size: 63077 timestamp: 1733913233032 -- kind: conda - name: multidict - version: 6.1.0 - build: py312hdb8e49c_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/multidict-6.1.0-py312hdb8e49c_1.conda sha256: 482fd09fb798090dc8cce2285fa69f43b1459099122eac2fb112d9b922b9f916 md5: 0048335516fed938e4dd2c457b4c5b9b depends: @@ -6170,31 +4491,7 @@ packages: license_family: APACHE size: 55968 timestamp: 1729065664275 -- kind: conda - name: multiprocess - version: 0.70.15 - build: py312h02f2b3b_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda - sha256: 8041371e3ec3fbc2ca13c71b0180672896e6382e62892d9f6b11a4c5dd675951 - md5: 910ef2223c71902175418d9163152788 - depends: - - dill >=0.3.6 - - python >=3.12.0rc3,<3.13.0a0 - - python >=3.12.0rc3,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - size: 335147 - timestamp: 1695459275360 -- kind: conda - name: multiprocess - version: 0.70.15 - build: py312h98912ed_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.15-py312h98912ed_1.conda sha256: bb612a921fafda6375a2204ffebd8811db8dd3b8f25ac9886cc9bcbff7e3664e md5: 5a64b9f44790d9a187a85366dd0ffa8d depends: @@ -6206,13 +4503,7 @@ packages: license_family: BSD size: 335666 timestamp: 1695459025249 -- kind: conda - name: multiprocess - version: 0.70.15 - build: py312hdd3e373_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/multiprocess-0.70.15-py312hdd3e373_1.conda sha256: c53362cdf346f314e111faddc53061e3fd2ece0ba68ca303f5dd109976df158f md5: 173a1692d2b3ddc265dc6afd21a869b3 depends: @@ -6225,14 +4516,19 @@ packages: license_family: BSD size: 336110 timestamp: 1695459137796 -- kind: conda - name: mypy_extensions - version: 1.0.0 - build: pyha770c72_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.15-py312h02f2b3b_1.conda + sha256: 8041371e3ec3fbc2ca13c71b0180672896e6382e62892d9f6b11a4c5dd675951 + md5: 910ef2223c71902175418d9163152788 + depends: + - dill >=0.3.6 + - python >=3.12.0rc3,<3.13.0a0 + - python >=3.12.0rc3,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 335147 + timestamp: 1695459275360 +- conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda sha256: 1895f47b7d68581a6facde5cb13ab8c2764c2e53a76bd746f8f98910dc4e08fe md5: 29097e7ea634a45cc5386b95cac6568f depends: @@ -6241,57 +4537,55 @@ packages: license_family: MIT size: 10854 timestamp: 1733230986902 -- kind: conda - name: ncurses - version: '6.5' - build: h7bae524_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - sha256: 27d0b9ff78ad46e1f3a6c96c479ab44beda5f96def88e2fe626e0a49429d8afc - md5: cb2b0ea909b97b3d70cd3921d1445e1a +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_2.conda + sha256: 17fe6afd8a00446010220d52256bd222b1e4fcb93bd587e7784b03219f3dc358 + md5: 04b34b9a40cdc48cfdab261ab176ff74 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: X11 AND BSD-3-Clause - size: 802321 - timestamp: 1724658775723 -- kind: conda - name: ncurses - version: '6.5' - build: hcccb83c_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda - sha256: acad4cf1f57b12ee1e42995e6fac646fa06aa026529f05eb8c07eb0a84a47a84 - md5: 91d49c85cacd92caa40cf375ef72a25d + size: 894452 + timestamp: 1736683239706 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_2.conda + sha256: 9fd726174dde993c560dd6fa1a383e61d546d380e98e0b0348d22512e5d86e24 + md5: 779046fb585c71373e8a051be06c6011 depends: - - libgcc-ng >=12 + - libgcc >=13 license: X11 AND BSD-3-Clause - size: 924472 - timestamp: 1724658573518 -- kind: conda - name: ncurses - version: '6.5' - build: he02047a_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a - md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + size: 928402 + timestamp: 1736683192463 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_2.conda + sha256: b45c73348ec9841d5c893acc2e97adff24127548fe8c786109d03c41ed564e91 + md5: f6f7c5b7d0983be186c46c4f6f8f9af8 + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 796754 + timestamp: 1736683572099 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.11.3-he02047a_1.conda + sha256: ce4bcced4f8eea71b7cac8bc3daac097abf7a5792f278cd811dedada199500c1 + md5: e46f7ac4917215b49df2ea09a694a3fa depends: - __glibc >=2.17,<3.0.a0 - libgcc-ng >=12 - license: X11 AND BSD-3-Clause - size: 889086 - timestamp: 1724658547447 -- kind: conda - name: numpy - version: 1.26.4 - build: py312h470d778_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda - sha256: 23767677a7790bee5457d5e75ebd508b9a31c5354216f4310dd1acfca3f7a6f9 - md5: 9cebf5a06cb87d4569cd68df887af476 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 122743 + timestamp: 1723652407663 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.11.3-h00cdb27_1.conda + sha256: 3f4e6a4fa074bb297855f8111ab974dab6d9f98b7d4317d4dd46f8687ee2363b + md5: d2dee849c806430eee64d3acc98ce090 + depends: + - __osx >=11.0 + - libcxx >=16 + license: MIT + license_family: MIT + size: 123250 + timestamp: 1723652704997 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 + md5: d8285bea2a350f63fab23bf460221f3f depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 @@ -6299,27 +4593,22 @@ packages: - liblapack >=3.9.0,<4.0a0 - libstdcxx-ng >=12 - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD - size: 6614296 - timestamp: 1707225994762 -- kind: conda - name: numpy - version: 1.26.4 - build: py312h8442bc7_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda - sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 - md5: d83fc83d589e2625a3451c9a7e21047c + size: 7484186 + timestamp: 1707225809722 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-1.26.4-py312h470d778_0.conda + sha256: 23767677a7790bee5457d5e75ebd508b9a31c5354216f4310dd1acfca3f7a6f9 + md5: 9cebf5a06cb87d4569cd68df887af476 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - - libcxx >=16 + - libgcc-ng >=12 - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 @@ -6327,39 +4616,30 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD - size: 6073136 - timestamp: 1707226249608 -- kind: conda - name: numpy - version: 1.26.4 - build: py312heda63a1_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda - sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 - md5: d8285bea2a350f63fab23bf460221f3f + size: 6614296 + timestamp: 1707225994762 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - - libgcc-ng >=12 + - libcxx >=16 - liblapack >=3.9.0,<4.0a0 - - libstdcxx-ng >=12 - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD - size: 7484186 - timestamp: 1707225809722 -- kind: conda - name: openjpeg - version: 2.5.3 - build: h3f56577_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda - sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d - md5: 04231368e4af50d11184b50e14250993 + size: 6073136 + timestamp: 1707226249608 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda + sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 + md5: 9e5816bc95d285c115a3ebc2f8563564 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libpng >=1.6.44,<1.7.0a0 - libstdcxx >=13 @@ -6367,18 +4647,12 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD - size: 377796 - timestamp: 1733816683252 -- kind: conda - name: openjpeg - version: 2.5.3 - build: h5fbd93e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda - sha256: 5bee706ea5ba453ed7fd9da7da8380dd88b865c8d30b5aaec14d2b6dd32dbc39 - md5: 9e5816bc95d285c115a3ebc2f8563564 + size: 342988 + timestamp: 1733816638720 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda + sha256: 92d310033e20538e896f4e4b1ea4205eb6604eee7c5c651c4965a0d8d3ca0f1d + md5: 04231368e4af50d11184b50e14250993 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libpng >=1.6.44,<1.7.0a0 - libstdcxx >=13 @@ -6386,14 +4660,9 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD - size: 342988 - timestamp: 1733816638720 -- kind: conda - name: openjpeg - version: 2.5.3 - build: h8a3d83b_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda + size: 377796 + timestamp: 1733816683252 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda sha256: 1d59bc72ca7faac06d349c1a280f5cfb8a57ee5896f1e24225a997189d7418c7 md5: 4b71d78648dbcf68ce8bf22bb07ff838 depends: @@ -6406,60 +4675,38 @@ packages: license_family: BSD size: 319362 timestamp: 1733816781741 -- kind: conda - name: openssl - version: 3.4.0 - build: h39f12f2_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h39f12f2_0.conda - sha256: bd1d58ced46e75efa3b842c61642fd12272c69e9fe4d7261078bc082153a1d53 - md5: df307bbc703324722df0293c9ca2e418 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-h7b32b05_1.conda + sha256: f62f6bca4a33ca5109b6d571b052a394d836956d21b25b7ffd03376abf7a481f + md5: 4ce6875f75469b2757a65e10a5d05e31 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 - ca-certificates + - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 2935176 - timestamp: 1731377561525 -- kind: conda - name: openssl - version: 3.4.0 - build: h86ecc28_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-h86ecc28_0.conda - sha256: 64dbbdd6384fa56338124783197f7ad9048c989a02264bcd2e07355e3570f113 - md5: b2f202b5bddafac824eb610b65dde98f + size: 2937158 + timestamp: 1736086387286 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.4.0-hd08dc88_1.conda + sha256: 60d34454b861501d7355f25a7b39fdb5de8d56fca49b5bcbe8b8142b7d82dce4 + md5: e21c4767e783a58c373fdb99de6211bf depends: - ca-certificates - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 3474825 - timestamp: 1731379200886 -- kind: conda - name: openssl - version: 3.4.0 - build: hb9d3cd8_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.0-hb9d3cd8_0.conda - sha256: 814b9dff1847b132c676ee6cc1a8cb2d427320779b93e1b6d76552275c128705 - md5: 23cc74f77eb99315c0360ec3533147a9 + size: 3469279 + timestamp: 1736088141230 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h81ee809_1.conda + sha256: 97772762abc70b3a537683ca9fc3ff3d6099eb64e4aba3b9c99e6fce48422d21 + md5: 22f971393637480bda8c679f374d8861 depends: - - __glibc >=2.17,<3.0.a0 + - __osx >=11.0 - ca-certificates - - libgcc >=13 license: Apache-2.0 license_family: Apache - size: 2947466 - timestamp: 1731377666602 -- kind: conda - name: opentelemetry-api - version: 1.29.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda + size: 2936415 + timestamp: 1736086108693 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.29.0-pyhd8ed1ab_1.conda sha256: 296280c8ace35c0a1cf72bed1077f248b3af903c3bf92332f1783a207cb5abdb md5: 307b05402c1a382f2f09426492dee8f8 depends: @@ -6470,13 +4717,7 @@ packages: license_family: APACHE size: 44166 timestamp: 1734132973331 -- kind: conda - name: opentelemetry-exporter-otlp-proto-common - version: 1.29.0 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.29.0-pyhd8ed1ab_0.conda sha256: ae9776efe52564e0d6711cfcee7c54439273e57a3999f7f796f66e862f58aae9 md5: 0c02e74d26bce3fec93b227cf7ea6e6b depends: @@ -6487,14 +4728,7 @@ packages: license_family: APACHE size: 18922 timestamp: 1734310457116 -- kind: conda - name: opentelemetry-exporter-otlp-proto-http - version: 1.29.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.29.0-pyhd8ed1ab_1.conda sha256: 5d61db9d5b4f91b3932f5f2348920d5b7fdaa09e52c8ea054cf7bf3f21677c9c md5: 223f4e56a29601c887f0dc467034af5b depends: @@ -6507,15 +4741,10 @@ packages: - python >=3.9 - requests >=2.7,<3.dev0 license: Apache-2.0 + license_family: APACHE size: 17147 timestamp: 1734345675510 -- kind: conda - name: opentelemetry-exporter-prometheus - version: 1.12.0rc1 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-1.12.0rc1-pyhd8ed1ab_0.conda sha256: b8239230dbbdb491401e41b53bd9f21d60551cedef1a8d5807fca1bf9bdd331c md5: 1ddc95052b31147d1e10d818cf519cf5 depends: @@ -6527,13 +4756,7 @@ packages: license_family: APACHE size: 14721 timestamp: 1695214221489 -- kind: conda - name: opentelemetry-proto - version: 1.29.0 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.29.0-pyhd8ed1ab_0.conda sha256: 200a7cb8acc8a0ddd6ef55c5460cec871b6a265929b240a0296c0ccb9c8d9758 md5: e2a6d2ad10b813c7fdc1c64aac376128 depends: @@ -6543,13 +4766,7 @@ packages: license_family: APACHE size: 37235 timestamp: 1734291034372 -- kind: conda - name: opentelemetry-sdk - version: 1.29.0 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.29.0-pyhd8ed1ab_0.conda sha256: 7b36629d8b8be8a019fcfd1518d7b7f862dd25de96f8adcadb93e4fd12cf9bd6 md5: 2a8893f06e6ebda4bfa78875bc923ea4 depends: @@ -6562,13 +4779,7 @@ packages: license_family: APACHE size: 77645 timestamp: 1734297838999 -- kind: conda - name: opentelemetry-semantic-conventions - version: 0.50b0 - build: pyh3cfb1c2_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.50b0-pyh3cfb1c2_0.conda sha256: 6526e70368d5bf66ef0eaa51fb800d53782dde71a24bd38f40139919a6f784dc md5: f7111fa4188d646c8108e232d024cb99 depends: @@ -6579,18 +4790,13 @@ packages: license_family: APACHE size: 86084 timestamp: 1734208980168 -- kind: conda - name: orc - version: 2.0.3 - build: h3c55218_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-h3c55218_1.conda - sha256: 154b26bc4d586de33765a155c9b79ebd7f5bb36c2bbf4b8854e1631bca8d21af - md5: 0a51a3cf028b845c46ec0d1ea2d18629 +- conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h12ee42a_2.conda + sha256: dff5cc8023905782c86b3459055f26d4b97890e403b0698477c9fed15d8669cc + md5: 4f6f9f3f80354ad185e276c120eac3f0 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 @@ -6599,21 +4805,14 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 license_family: Apache - size: 1165179 - timestamp: 1733509923825 -- kind: conda - name: orc - version: 2.0.3 - build: h97ab989_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.3-h97ab989_1.conda - sha256: 9de7e2746fde57c9b7f08ee87142014f6bb9b2d3a506839ea3e98baa99711576 - md5: 2f46eae652623114e112df13fae311cf + size: 1188881 + timestamp: 1735630209320 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/orc-2.0.3-hdd485aa_2.conda + sha256: b6c67542352a86cdf143c3066d5cda855b74454a156eedcd8958b494c6a32a83 + md5: d19f01b42e5d6a2908b65df435aff42f depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 @@ -6622,21 +4821,15 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 license_family: Apache - size: 1189462 - timestamp: 1733509801323 -- kind: conda - name: orc - version: 2.0.3 - build: hbcee414_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-hbcee414_1.conda - sha256: e5e72438a3cd967ebc774070e8c49500d2d6d4175f349400b327fee75d3bfc05 - md5: e808cf7819eaa1735c8790d7f9f482c7 + size: 1167714 + timestamp: 1735630248837 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-h0ff2369_2.conda + sha256: cca330695f3bdb8c0e46350c29cd4af3345865544e36f1d7c9ba9190ad22f5f4 + md5: 24b1897c0d24afbb70704ba998793b78 depends: - __osx >=11.0 - libcxx >=18 - - libprotobuf >=5.28.2,<5.28.3.0a0 + - libprotobuf >=5.28.3,<5.28.4.0a0 - libzlib >=1.3.1,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - snappy >=1.2.1,<1.3.0a0 @@ -6644,16 +4837,9 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 license_family: Apache - size: 437391 - timestamp: 1733510118673 -- kind: conda - name: packaging - version: '24.2' - build: pyhd8ed1ab_2 - build_number: 2 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda + size: 438520 + timestamp: 1735630624140 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa depends: @@ -6662,42 +4848,30 @@ packages: license_family: APACHE size: 60164 timestamp: 1733203368787 -- kind: conda - name: pandas - version: 2.2.3 - build: py312ha2895bd_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_1.conda - sha256: 585e05f95d14afe3df43ded14f86800c70da26b27e27b59de95932f8888af5d3 - md5: 80b873ac4fdf36641afa0eaafff3a664 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda + sha256: ad275a83bfebfa8a8fee9b0569aaf6f513ada6a246b2f5d5b85903d8ca61887e + md5: 8bce4f6caaf8c5448c7ac86d87e26b4b depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libstdcxx >=13 - numpy >=1.19,<3 - numpy >=1.22.4 - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - python-dateutil >=2.8.1 - python-tzdata >=2022a - python_abi 3.12.* *_cp312 - pytz >=2020.1,<2024.2 license: BSD-3-Clause license_family: BSD - size: 15159625 - timestamp: 1726879151211 -- kind: conda - name: pandas - version: 2.2.3 - build: py312hcd31e36_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda - sha256: ff0cb54b5d058c7987b4a0984066e893642d1865a7bb695294b6172e2fcdc457 - md5: c68bfa69e6086c381c74e16fd72613a8 + size: 15436913 + timestamp: 1726879054912 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pandas-2.2.3-py312ha2895bd_2.conda + sha256: a34b10077de97eea72c81cb96e3ddc7d48320c0fc7d9b28ba8d9d2bead1d8297 + md5: 39a91ac336d350513de6aad56da5a920 depends: - - __osx >=11.0 - - libcxx >=17 + - libgcc >=13 + - libstdcxx >=13 - numpy >=1.19,<3 - numpy >=1.22.4 - python >=3.12,<3.13.0a0 @@ -6706,42 +4880,55 @@ packages: - python-tzdata >=2022a - python_abi 3.12.* *_cp312 - pytz >=2020.1,<2024.2 + constrains: + - fsspec >=2022.11.0 + - s3fs >=2022.11.0 + - fastparquet >=2022.12.0 + - pyreadstat >=1.2.0 + - qtpy >=2.3.0 + - scipy >=1.10.0 + - beautifulsoup4 >=4.11.2 + - gcsfs >=2022.11.0 + - numexpr >=2.8.4 + - sqlalchemy >=2.0.0 + - pyxlsb >=1.0.10 + - numba >=0.56.4 + - lxml >=4.9.2 + - matplotlib >=3.6.3 + - psycopg2 >=2.9.6 + - tzdata >=2022.7 + - bottleneck >=1.3.6 + - xarray >=2022.12.0 + - xlsxwriter >=3.0.5 + - zstandard >=0.19.0 + - blosc >=1.21.3 + - pytables >=3.8.0 + - openpyxl >=3.1.0 + - pyqt5 >=5.15.8 + - tabulate >=0.9.0 license: BSD-3-Clause license_family: BSD - size: 14470437 - timestamp: 1726878887799 -- kind: conda - name: pandas - version: 2.2.3 - build: py312hf9745cd_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda - sha256: ad275a83bfebfa8a8fee9b0569aaf6f513ada6a246b2f5d5b85903d8ca61887e - md5: 8bce4f6caaf8c5448c7ac86d87e26b4b + size: 15162992 + timestamp: 1736811533875 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda + sha256: ff0cb54b5d058c7987b4a0984066e893642d1865a7bb695294b6172e2fcdc457 + md5: c68bfa69e6086c381c74e16fd72613a8 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 + - __osx >=11.0 + - libcxx >=17 - numpy >=1.19,<3 - numpy >=1.22.4 - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython - python-dateutil >=2.8.1 - python-tzdata >=2022a - python_abi 3.12.* *_cp312 - pytz >=2020.1,<2024.2 license: BSD-3-Clause license_family: BSD - size: 15436913 - timestamp: 1726879054912 -- kind: conda - name: pathspec - version: 0.12.1 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + size: 14470437 + timestamp: 1726878887799 +- conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee md5: 617f15191456cc6a13db418a275435e5 depends: @@ -6750,88 +4937,66 @@ packages: license_family: MOZILLA size: 41075 timestamp: 1733233471940 -- kind: conda - name: pillow - version: 11.0.0 - build: py312h5ab5af3_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py312h5ab5af3_0.conda - sha256: 3cf43a5eb1f67f3a5f3ef1ec3a685f8767019cce24dbe46c4b76fee8a54fbacf - md5: 1c4bdfe659cfdedd372685ce2494e97b +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py312h80c1187_0.conda + sha256: 5c347962202b55ae4d8a463e0555c5c6ca33396266a08284bf1384399894e541 + md5: d3894405f05b2c0f351d5de3ae26fa9c depends: + - __glibc >=2.17,<3.0.a0 - freetype >=2.12.1,<3.0a0 - lcms2 >=2.16,<3.0a0 - libgcc >=13 - libjpeg-turbo >=3.0.0,<4.0a0 - libtiff >=4.7.0,<4.8.0a0 - - libwebp-base >=1.4.0,<2.0a0 + - libwebp-base >=1.5.0,<2.0a0 - libxcb >=1.17.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - - openjpeg >=2.5.2,<3.0a0 + - openjpeg >=2.5.3,<3.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tk >=8.6.13,<8.7.0a0 license: HPND - size: 41756471 - timestamp: 1729068045876 -- kind: conda - name: pillow - version: 11.0.0 - build: py312h7b63e92_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda - sha256: 13a464bea02c0df0199c20ef6bad24a6bc336aaf55bf8d6a133d0fe664463224 - md5: 385f46a4df6f97892503a841121a9acf + size: 42749785 + timestamp: 1735929845390 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.1.0-py312h719f0cf_0.conda + sha256: 7559556ffc44bda777f85c2e5acd6b5756fa5822c0271b329b7b9a3c6bb20349 + md5: 77e0ec0a6fc847d317f204aa15b59f6b depends: - - __glibc >=2.17,<3.0.a0 - freetype >=2.12.1,<3.0a0 - lcms2 >=2.16,<3.0a0 - libgcc >=13 - libjpeg-turbo >=3.0.0,<4.0a0 - libtiff >=4.7.0,<4.8.0a0 - - libwebp-base >=1.4.0,<2.0a0 + - libwebp-base >=1.5.0,<2.0a0 - libxcb >=1.17.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - - openjpeg >=2.5.2,<3.0a0 + - openjpeg >=2.5.3,<3.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tk >=8.6.13,<8.7.0a0 license: HPND - size: 41948418 - timestamp: 1729065846594 -- kind: conda - name: pillow - version: 11.0.0 - build: py312haf37ca6_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.0.0-py312haf37ca6_0.conda - sha256: 727b4c3faecdb6f6809cf20c5f32d2df4af34e0d5b9146b7588383bcba7990e8 - md5: dc9b51fbd2b6f7fea9b5123458864dbb + size: 41362848 + timestamp: 1735932311857 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.1.0-py312h50aef2c_0.conda + sha256: b29b7c915053e06a7a5b4118760202c572c9c35d23bd6ce8e73270b6a50e50ee + md5: 94d6ba8cd468668a9fb04193b0f4b36e depends: - __osx >=11.0 - freetype >=2.12.1,<3.0a0 - lcms2 >=2.16,<3.0a0 - libjpeg-turbo >=3.0.0,<4.0a0 - libtiff >=4.7.0,<4.8.0a0 - - libwebp-base >=1.4.0,<2.0a0 + - libwebp-base >=1.5.0,<2.0a0 - libxcb >=1.17.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - - openjpeg >=2.5.2,<3.0a0 + - openjpeg >=2.5.3,<3.0a0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - tk >=8.6.13,<8.7.0a0 license: HPND - size: 41737424 - timestamp: 1729065920347 -- kind: conda - name: platformdirs - version: 4.3.6 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda + size: 42852329 + timestamp: 1735930118976 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda sha256: bb50f6499e8bc1d1a26f17716c97984671121608dc0c3ecd34858112bce59a27 md5: 577852c7e53901ddccc7e6a9959ddebe depends: @@ -6840,13 +5005,34 @@ packages: license_family: MIT size: 20448 timestamp: 1733232756001 -- kind: conda - name: prometheus_client - version: 0.21.1 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + sha256: 013669433eb447548f21c3c6b16b2ed64356f726b5f77c1b39d5ba17a8a4b8bc + md5: a83f6a2fdc079e643237887a37460668 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + size: 199544 + timestamp: 1730769112346 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + sha256: 851a77ae1a8e90db9b9f3c4466abea7afb52713c3d98ceb0d37ba6ff27df2eff + md5: 7172339b49c94275ba42fec3eaeda34f + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + size: 173220 + timestamp: 1730769371051 +- conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.21.1-pyhd8ed1ab_0.conda sha256: bc8f00d5155deb7b47702cb8370f233935704100dbc23e30747c161d1b6cf3ab md5: 3e01e386307acc60b2f89af0b2e161aa depends: @@ -6855,12 +5041,7 @@ packages: license_family: Apache size: 49002 timestamp: 1733327434163 -- kind: conda - name: propcache - version: 0.2.1 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.2.1-py312h66e93f0_0.conda sha256: 5771311fb5ded614ca349c92579a0b752af55a310f40b71fc533e20625965391 md5: 55d5742a696d7da1c1262e99b6217ceb depends: @@ -6872,12 +5053,7 @@ packages: license_family: APACHE size: 52747 timestamp: 1733391916349 -- kind: conda - name: propcache - version: 0.2.1 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/propcache-0.2.1-py312hb2c0f52_0.conda sha256: c7f62c11ed929ccf1f3d4a1e200e28be01e8d0e0786bf8f76c5893f2ea681e1b md5: 50ab8953e7ff1333a4a47cda32e68123 depends: @@ -6889,12 +5065,7 @@ packages: license_family: APACHE size: 52484 timestamp: 1733391993461 -- kind: conda - name: propcache - version: 0.2.1 - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/propcache-0.2.1-py312hea69d52_0.conda sha256: f8c266c494aa1e4cfb8bf0b6fca060044b2f3d65afe4c5062ebeea382e77aa6d md5: c84e3dd97fe25a17322c4a0f670c6750 depends: @@ -6906,14 +5077,9 @@ packages: license_family: APACHE size: 48225 timestamp: 1733392308901 -- kind: conda - name: protobuf - version: 5.28.2 - build: py312h2ec8cdc_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.2-py312h2ec8cdc_0.conda - sha256: 4884f8161602f0148ebbc1af8d3176cec80b96c83243f68aafd651986b573817 - md5: 586bead4a9dfa46faf88deb7d3a742bb +- conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-5.28.3-py312h2ec8cdc_0.conda + sha256: acb2e0ee948e3941f8ed191cb77f654e06538638aed8ccd71cbc78a15242ebbb + md5: 9d7e427d159c1b2d516cc047ff177c48 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -6921,19 +5087,14 @@ packages: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - - libprotobuf 5.28.2 + - libprotobuf 5.28.3 license: BSD-3-Clause license_family: BSD - size: 464548 - timestamp: 1728669645013 -- kind: conda - name: protobuf - version: 5.28.2 - build: py312h6f74592_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.2-py312h6f74592_0.conda - sha256: f874ffd38b9ae2b810e9d2e43fd8d3b778cdeaf7dea4a3e6ee4adeafe2d936cf - md5: 4b9b22bd7c53d938b207f9d0f79db183 + size: 464794 + timestamp: 1731366525051 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/protobuf-5.28.3-py312h6f74592_0.conda + sha256: 9c575d5035c7ecb114ab9e17906c0a54087d9598dd6a2104c02fe33f0a29dd46 + md5: 06513608c94fb1c1b17136ace77063a9 depends: - libgcc >=13 - libstdcxx >=13 @@ -6941,53 +5102,27 @@ packages: - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - - libprotobuf 5.28.2 + - libprotobuf 5.28.3 license: BSD-3-Clause license_family: BSD - size: 472764 - timestamp: 1728669483611 -- kind: conda - name: protobuf - version: 5.28.2 - build: py312hf02c72a_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.2-py312hf02c72a_0.conda - sha256: dbcec117510ced5c12097e3eb06ebbf4512dc255733a9ace33c4249fb7e6a364 - md5: 6fda46c82abd0a080ca33de7d16ca877 + size: 473242 + timestamp: 1731366577844 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.3-py312hd8f9ff3_0.conda + sha256: 9d572a97419bdace14d7c7cc8cc8c4bf2dcb22b56965dac87a27fbdb5061b926 + md5: 5afbe52a59f04dd1fe566d0d17590d7e depends: - __osx >=11.0 - - libcxx >=17 + - libcxx >=18 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - - libprotobuf 5.28.2 + - libprotobuf 5.28.3 license: BSD-3-Clause license_family: BSD - size: 447369 - timestamp: 1728669902591 -- kind: conda - name: pthread-stubs - version: '0.4' - build: h86ecc28_1002 - build_number: 1002 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda - sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba - md5: bb5a90c93e3bac3d5690acf76b4a6386 - depends: - - libgcc >=13 - license: MIT - license_family: MIT - size: 8342 - timestamp: 1726803319942 -- kind: conda - name: pthread-stubs - version: '0.4' - build: hb9d3cd8_1002 - build_number: 1002 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + size: 448803 + timestamp: 1731367010746 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 md5: b3c17d95b5a10c6e64a21fa17573e70e depends: @@ -6997,13 +5132,16 @@ packages: license_family: MIT size: 8252 timestamp: 1726802366959 -- kind: conda - name: pthread-stubs - version: '0.4' - build: hd74edd7_1002 - build_number: 1002 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda + sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba + md5: bb5a90c93e3bac3d5690acf76b4a6386 + depends: + - libgcc >=13 + license: MIT + license_family: MIT + size: 8342 + timestamp: 1726803319942 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 md5: 415816daf82e0b23a736a069a75e9da7 depends: @@ -7012,52 +5150,22 @@ packages: license_family: MIT size: 8381 timestamp: 1726802424786 -- kind: conda - name: pyarrow - version: 18.1.0 - build: py312h1f38498_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-18.1.0-py312h1f38498_0.conda - sha256: 06c0e208d5bf15051874097366c8e8e5db176dffba38526f227a34e80cc8e9bc - md5: 3710616b880b31d0c8afd8ae7e12392a - depends: - - libarrow-acero 18.1.0.* - - libarrow-dataset 18.1.0.* - - libarrow-substrait 18.1.0.* - - libparquet 18.1.0.* - - pyarrow-core 18.1.0 *_0_* - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - size: 25375 - timestamp: 1732610892198 -- kind: conda - name: pyarrow - version: 18.1.0 - build: py312h7900ff3_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.1.0-py312h7900ff3_0.conda - sha256: 46a61c29375d3bf1933eae61c7861394c168898915d59fc99bf05e46de2ff5ad - md5: ac65b70df28687c6af4270923c020bdd - depends: - - libarrow-acero 18.1.0.* - - libarrow-dataset 18.1.0.* - - libarrow-substrait 18.1.0.* - - libparquet 18.1.0.* - - pyarrow-core 18.1.0 *_0_* +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-19.0.0-py312h7900ff3_0.conda + sha256: 7d98e626ec65b882341482ad15ecb7a670ee41dbaf375aa660ba8b7d0a940504 + md5: 14f86e63b5c214dd9fb34e5472d4bafc + depends: + - libarrow-acero 19.0.0.* + - libarrow-dataset 19.0.0.* + - libarrow-substrait 19.0.0.* + - libparquet 19.0.0.* + - pyarrow-core 19.0.0 *_0_* - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE - size: 25213 - timestamp: 1732610785600 -- kind: conda - name: pyarrow - version: 18.1.0 - build: py312h8025657_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda + size: 25289 + timestamp: 1737128438818 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-18.1.0-py312h8025657_0.conda sha256: 49db959887cb89b44053a44a98d0f35644fc0b2003587492f02b56046de0b60a md5: 9bb7d32e96a5dcb5ea7fd90a11a83656 depends: @@ -7072,35 +5180,40 @@ packages: license_family: APACHE size: 25374 timestamp: 1732611006864 -- kind: conda - name: pyarrow-core - version: 18.1.0 - build: py312h01725c0_0_cpu - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.1.0-py312h01725c0_0_cpu.conda - sha256: 948a4161c56f846d374a3721a657e58ddbc992a29b3b3e7a6411975c30361d94 - md5: ee80934a6c280ff8635f8db5dec11e04 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-19.0.0-py312h1f38498_0.conda + sha256: 9d693901833c2ff4e5d67e1f2f6df50f699e1cec2f580c26d42299654830855a + md5: bd5e025292ff1127aa1534b59e55c4d0 + depends: + - libarrow-acero 19.0.0.* + - libarrow-dataset 19.0.0.* + - libarrow-substrait 19.0.0.* + - libparquet 19.0.0.* + - pyarrow-core 19.0.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + size: 25428 + timestamp: 1737128284082 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-19.0.0-py312h01725c0_0_cpu.conda + sha256: 81178d0de0ac851a0a78e09c81ad92274cf770a38b28acdf53a0cfb2122d15aa + md5: 7ab1143b9ac1af5cc4a630706f643627 depends: - __glibc >=2.17,<3.0.a0 - - libarrow 18.1.0.* *cpu + - libarrow 19.0.0.* *cpu - libgcc >=13 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - - numpy >=1.21,<3 - apache-arrow-proc =*=cpu + - numpy >=1.21,<3 license: Apache-2.0 license_family: APACHE - size: 4612916 - timestamp: 1732610377259 -- kind: conda - name: pyarrow-core - version: 18.1.0 - build: py312h66f7834_0_cpu - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda + size: 5230953 + timestamp: 1737128097002 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-18.1.0-py312h66f7834_0_cpu.conda sha256: e7eb062145be554c23dfefa0ebe8c5f6ae8c59635117a6921e66403d6addcda3 md5: 3390c8b8f57e85506c92a37cf750bdd7 depends: @@ -7118,37 +5231,25 @@ packages: license_family: APACHE size: 4406662 timestamp: 1732610939832 -- kind: conda - name: pyarrow-core - version: 18.1.0 - build: py312hc40f475_0_cpu - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-18.1.0-py312hc40f475_0_cpu.conda - sha256: 063eb168a29d4ce6d9ed865e9e1ad3b6e141712189955a79e06b24ddc0cbbc9c - md5: 9859e7c4b94bbf69772dbf0511101cec +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-19.0.0-py312hc40f475_0_cpu.conda + sha256: 6303fe1c3e6d36273b72f0eeb3f19897d2376d57fe8c757f55dcbfbaa5cd6840 + md5: df502157843a7b1d90af04803767be15 depends: - __osx >=11.0 - - libarrow 18.1.0.* *cpu + - libarrow 19.0.0.* *cpu - libcxx >=18 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - - numpy >=1.21,<3 - apache-arrow-proc =*=cpu + - numpy >=1.21,<3 license: Apache-2.0 license_family: APACHE - size: 3909116 - timestamp: 1732610863261 -- kind: conda - name: pycparser - version: '2.22' - build: pyh29332c3_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + size: 4393075 + timestamp: 1737128225546 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 md5: 12c566707c80111f9799308d9e265aef depends: @@ -7158,33 +5259,22 @@ packages: license_family: BSD size: 110100 timestamp: 1733195786147 -- kind: conda - name: pydantic - version: 2.10.3 - build: pyh3cfb1c2_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.3-pyh3cfb1c2_0.conda - sha256: cac9eebd3d5f8d8a497a9025d756257ddc75b8b3393e6737cb45077bd744d4f8 - md5: 194ef7f91286978521350f171b117f01 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.5-pyh3cfb1c2_0.conda + sha256: 0f32c30ddc610cd1113335d8b4f311f20f4d72754b7c1a5d0d9493f597cf11d2 + md5: e8ea30925c8271c4128375810d7d3d7a depends: - annotated-types >=0.6.0 - - pydantic-core 2.27.1 + - pydantic-core 2.27.2 - python >=3.9 - typing-extensions >=4.6.1 - typing_extensions >=4.12.2 license: MIT license_family: MIT - size: 317037 - timestamp: 1733316963547 -- kind: conda - name: pydantic-core - version: 2.27.1 - build: py312h12e396e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.1-py312h12e396e_0.conda - sha256: c89741f4eff395f8de70975f42e1f20591f0e0870929d440af35b13399976b09 - md5: 114030cb28527db2c385f07038e914c8 + size: 296805 + timestamp: 1736458364196 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.2-py312h12e396e_0.conda + sha256: 81602a4592ad2ac1a1cb57372fd25214e63b1c477d5818b0c21cde0f1f85c001 + md5: bae01b2563030c085f5158c518b84e86 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -7195,16 +5285,11 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT - size: 1635156 - timestamp: 1732254225040 -- kind: conda - name: pydantic-core - version: 2.27.1 - build: py312h8cbf658_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.1-py312h8cbf658_0.conda - sha256: 1f59bc1914f77faed3c95217e4d093310771baee4e93a15c0479359559e3fa19 - md5: d980860b8bf193f53d30a19c5d2bf070 + size: 1641402 + timestamp: 1734571789895 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.27.2-py312h8cbf658_0.conda + sha256: 623e0f3846f15d035ce7ab7ae445fc8d9e547b6684ab55858b6f44510d24b097 + md5: 9677f6ab4bf27ba3c2aee70d08c7b27c depends: - libgcc >=13 - python >=3.12,<3.13.0a0 @@ -7215,16 +5300,11 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT - size: 1503747 - timestamp: 1732254331303 -- kind: conda - name: pydantic-core - version: 2.27.1 - build: py312hcd83bfe_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.1-py312hcd83bfe_0.conda - sha256: 5bba8de2bbbbdb39390abb1e2aff310e8cfd49646ae5a0e0ea4d6582bd1d52ba - md5: 3847a96eaf24a877b6091150ff9c4955 + size: 1505076 + timestamp: 1734571966615 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.2-py312hcd83bfe_0.conda + sha256: cfa7201f890d5d08ce29ff70e65a96787d5793a1718776733666b44bbd4a1205 + md5: dcb307e02f17d38c6e1cbfbf8c602852 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 @@ -7235,64 +5315,29 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - size: 1449057 - timestamp: 1732254359451 -- kind: conda - name: pydantic-settings - version: 2.7.0 - build: pyh3cfb1c2_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.0-pyh3cfb1c2_0.conda - sha256: dd1ac7c8b6a189c8aa18f6c7df019d8f6df495300a259e3fbebdb542fc955c3b - md5: d9f19a7c4199249fa229891b573b6f9b + size: 1593461 + timestamp: 1734571986644 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.7.1-pyh3cfb1c2_0.conda + sha256: 082fb1ec29917d2c9ed6a862cb8eb9beb88c208ea62c9fef1aeb5f4f3e0e0b06 + md5: d71d76b62bed332b037d7adfc0f3989a depends: - pydantic >=2.7.0 - python >=3.9 - python-dotenv >=0.21.0 license: MIT license_family: MIT - size: 31426 - timestamp: 1734127929720 -- kind: conda - name: pygments - version: 2.18.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_1.conda - sha256: 0d6133545f268b2b89c2617c196fc791f365b538d4057ecd636d658c3b1e885d - md5: b38dc0206e2a530e5c2cf11dc086b31a + size: 31822 + timestamp: 1735650532951 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + sha256: 28a3e3161390a9d23bc02b4419448f8d27679d9e2c250e29849e37749c8de86b + md5: 232fb4577b6687b2d503ef8e254270c9 depends: - python >=3.9 license: BSD-2-Clause license_family: BSD - size: 876700 - timestamp: 1733221731178 -- kind: conda - name: pyinstrument - version: 5.0.0 - build: py312h0bf5046_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda - sha256: 6879d52fb0ec2258e2850476786a652c394220d53883c53691ed5390183ae925 - md5: f0e4a98d54477083ddc9d2f33507f848 - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - size: 181512 - timestamp: 1728714205508 -- kind: conda - name: pyinstrument - version: 5.0.0 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda + size: 888600 + timestamp: 1736243563082 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.0.0-py312h66e93f0_0.conda sha256: 8a006507a4003fb01eeee2f9ba79f994478694766ea3b445273da5c11cf8e763 md5: 798f42d9bfdf125dc80ffbec0e96e0b6 depends: @@ -7304,12 +5349,7 @@ packages: license_family: BSD size: 182021 timestamp: 1728714164706 -- kind: conda - name: pyinstrument - version: 5.0.0 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyinstrument-5.0.0-py312hb2c0f52_0.conda sha256: 7967b94b8f0ff75847302444e9c43ac11a391d74da24cb14fba1049fac9e5ba9 md5: 5274663cb05dfbe316db50af6da4389f depends: @@ -7321,14 +5361,19 @@ packages: license_family: BSD size: 183141 timestamp: 1728714267954 -- kind: conda - name: pysocks - version: 1.7.1 - build: pyha55dd90_7 - build_number: 7 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.0.0-py312h0bf5046_0.conda + sha256: 6879d52fb0ec2258e2850476786a652c394220d53883c53691ed5390183ae925 + md5: f0e4a98d54477083ddc9d2f33507f848 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 181512 + timestamp: 1728714205508 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 md5: 461219d1a5bd61342293efa2c0c90eac depends: @@ -7338,18 +5383,14 @@ packages: license_family: BSD size: 21085 timestamp: 1733217331982 -- kind: conda - name: python - version: 3.12.8 - build: h1683364_1_cpython +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda - sha256: 85573582d5b0f79923fed0a8365d3d74d21eee9f0a5fa1b9345f191e006363ab - md5: 09ec612ea05370989eaa3d81abf0f369 + sha256: 3f0e0518c992d8ccfe62b189125721309836fe48a010dc424240583e157f9ff0 + md5: 7fd2fd79436d9b473812f14e86746844 depends: + - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - - ld_impl_linux-aarch64 >=2.36.1 + - ld_impl_linux-64 >=2.36.1 - libexpat >=2.6.4,<3.0a0 - libffi >=3.4,<4.0a0 - libgcc >=13 @@ -7366,22 +5407,18 @@ packages: - tzdata constrains: - python_abi 3.12.* *_cp312 + arch: x86_64 + platform: linux license: Python-2.0 - size: 13760816 - timestamp: 1733407890896 -- kind: conda - name: python - version: 3.12.8 - build: h9e4cc4f_1_cpython + size: 31565686 + timestamp: 1733410597922 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.8-h1683364_1_cpython.conda build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda - sha256: 3f0e0518c992d8ccfe62b189125721309836fe48a010dc424240583e157f9ff0 - md5: 7fd2fd79436d9b473812f14e86746844 + sha256: 85573582d5b0f79923fed0a8365d3d74d21eee9f0a5fa1b9345f191e006363ab + md5: 09ec612ea05370989eaa3d81abf0f369 depends: - - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - - ld_impl_linux-64 >=2.36.1 + - ld_impl_linux-aarch64 >=2.36.1 - libexpat >=2.6.4,<3.0a0 - libffi >=3.4,<4.0a0 - libgcc >=13 @@ -7398,16 +5435,13 @@ packages: - tzdata constrains: - python_abi 3.12.* *_cp312 + arch: aarch64 + platform: linux license: Python-2.0 - size: 31565686 - timestamp: 1733410597922 -- kind: conda - name: python - version: 3.12.8 - build: hc22306f_1_cpython + size: 13760816 + timestamp: 1733407890896 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda sha256: 7586a711b1b08a9df8864e26efdc06980bdfb0e18d5ac4651d0fee30a8d3e3a0 md5: 54ca5b5d92ef3a3ba61e195ee882a518 depends: @@ -7425,17 +5459,12 @@ packages: - tzdata constrains: - python_abi 3.12.* *_cp312 + arch: arm64 + platform: osx license: Python-2.0 size: 12998673 timestamp: 1733408900971 -- kind: conda - name: python-dateutil - version: 2.9.0.post0 - build: pyhff2d567_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda sha256: a50052536f1ef8516ed11a844f9413661829aa083304dc624c5925298d078d79 md5: 5ba79d7c71f03c678c8ead841f347d6e depends: @@ -7445,14 +5474,7 @@ packages: license_family: APACHE size: 222505 timestamp: 1733215763718 -- kind: conda - name: python-dotenv - version: 1.0.1 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 md5: e5c6ed218664802d305e79cc2d4491de depends: @@ -7461,13 +5483,7 @@ packages: license_family: BSD size: 24215 timestamp: 1733243277223 -- kind: conda - name: python-json-logger - version: 2.0.7 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca md5: a61bf9ec79426938ff785eb69dbb1960 depends: @@ -7476,13 +5492,7 @@ packages: license_family: BSD size: 13383 timestamp: 1677079727691 -- kind: conda - name: python-multipart - version: 0.0.20 - build: pyhff2d567_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca md5: a28c984e0429aff3ab7386f7de56de6f depends: @@ -7491,48 +5501,29 @@ packages: license_family: Apache size: 27913 timestamp: 1734420869885 -- kind: conda - name: python-tzdata - version: '2024.2' - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_1.conda - sha256: 57c9a02ec25926fb48edca59b9ede107823e5d5c473b94a0e05cc0b9a193a642 - md5: c0def296b2f6d2dd7b030c2a7f66bb1f +- conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.1-pyhd8ed1ab_0.conda + sha256: 1597d6055d34e709ab8915091973552a0b8764c8032ede07c4e99670da029629 + md5: 392c91c42edd569a7ec99ed8648f597a depends: - python >=3.9 license: Apache-2.0 license_family: APACHE - size: 142235 - timestamp: 1733235414217 -- kind: conda - name: python-xxhash - version: 3.5.0 - build: py312h024a12e_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda - sha256: 28204ef48f028a4d872e22040da0dad7ebd703549b010a1bb511b6dd94cf466d - md5: 266fe1ae54a7bb17990206664d0f0ae4 + size: 143794 + timestamp: 1737541204030 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda + sha256: 20851b1e59fee127d49e01fc73195a88ab0779f103b7d6ffc90d11142a83678f + md5: 39aed2afe4d0cf76ab3d6b09eecdbea7 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - xxhash >=0.8.2,<0.8.3.0a0 license: BSD-2-Clause license_family: BSD - size: 21765 - timestamp: 1725272382968 -- kind: conda - name: python-xxhash - version: 3.5.0 - build: py312h52516f5_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda + size: 23162 + timestamp: 1725272139519 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-xxhash-3.5.0-py312h52516f5_1.conda sha256: 0fa5ba80073a43391ee90303814adbc9fd826175de1fdac273ba0e5b711aa255 md5: 591c4ae6d8338dfd07b951e00433a405 depends: @@ -7544,32 +5535,21 @@ packages: license_family: BSD size: 23589 timestamp: 1725273317965 -- kind: conda - name: python-xxhash - version: 3.5.0 - build: py312h66e93f0_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.5.0-py312h66e93f0_1.conda - sha256: 20851b1e59fee127d49e01fc73195a88ab0779f103b7d6ffc90d11142a83678f - md5: 39aed2afe4d0cf76ab3d6b09eecdbea7 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.5.0-py312h024a12e_1.conda + sha256: 28204ef48f028a4d872e22040da0dad7ebd703549b010a1bb511b6dd94cf466d + md5: 266fe1ae54a7bb17990206664d0f0ae4 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - __osx >=11.0 - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - xxhash >=0.8.2,<0.8.3.0a0 license: BSD-2-Clause license_family: BSD - size: 23162 - timestamp: 1725272139519 -- kind: conda - name: python_abi - version: '3.12' - build: 5_cp312 + size: 21765 + timestamp: 1725272382968 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda build_number: 5 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 md5: 0424ae29b104430108f5218a66db7260 constrains: @@ -7578,13 +5558,8 @@ packages: license_family: BSD size: 6238 timestamp: 1723823388266 -- kind: conda - name: python_abi - version: '3.12' - build: 5_cp312 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda build_number: 5 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.12-5_cp312.conda sha256: 5ccdad9981753cc4a2d126e356673a21c0cd5b34e209cb8d476a3947d4ad9b39 md5: 62b20f305498284a07dc6c45fd0e5c87 constrains: @@ -7593,13 +5568,8 @@ packages: license_family: BSD size: 6329 timestamp: 1723823366253 -- kind: conda - name: python_abi - version: '3.12' - build: 5_cp312 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda build_number: 5 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda sha256: 49d624e4b809c799d2bf257b22c23cf3fc4460f5570d9a58e7ad86350aeaa1f4 md5: b76f9b1c862128e56ac7aa8cd2333de9 constrains: @@ -7608,13 +5578,7 @@ packages: license_family: BSD size: 6278 timestamp: 1723823099686 -- kind: conda - name: pytz - version: '2024.1' - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda sha256: 1a7d6b233f7e6e3bbcbad054c8fd51e690a67b129a899a056a5e45dd9f00cb41 md5: 3eeeeb9e4827ace8c0c1419c85d590ad depends: @@ -7623,73 +5587,50 @@ packages: license_family: MIT size: 188538 timestamp: 1706886944988 -- kind: conda - name: pyyaml - version: 6.0.2 - build: py312h024a12e_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda - sha256: b06f1c15fb39695bbf707ae8fb554b9a77519af577b5556784534c7db10b52e3 - md5: 1ee23620cf46cb15900f70a1300bae55 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda + sha256: 159cba13a93b3fe084a1eb9bda0a07afc9148147647f0d437c3c3da60980503b + md5: cf2485f39740de96e2a7f2bb18ed2fee depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT - size: 187143 - timestamp: 1725456547263 -- kind: conda - name: pyyaml - version: 6.0.2 - build: py312h66e93f0_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda - sha256: a60705971e958724168f2ebbb8ed4853067f1d3f7059843df3903e3092bbcffa - md5: 549e5930e768548a89c23f595dac5a95 + size: 206903 + timestamp: 1737454910324 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hcc812fe_2.conda + sha256: dc78e41d51300722ba35ac4a10d37339ceffbe22d7501c71dfd3f633a4f8e79a + md5: 4de4a5ff81c941674e08595244e7cd61 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT - size: 206553 - timestamp: 1725456256213 -- kind: conda - name: pyyaml - version: 6.0.2 - build: py312hb2c0f52_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.2-py312hb2c0f52_1.conda - sha256: 8c515ebe1e7e85d972d72b75760af9dfac06fd11a9dba7e05c42d69aedbb303c - md5: dc5de424f7dbb9772da720dbb81317b2 + size: 199172 + timestamp: 1737454840766 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda + sha256: ad225ad24bfd60f7719709791345042c3cb32da1692e62bd463b084cf140e00d + md5: 68149ed4d4e9e1c42d2ba1f27f08ca96 depends: - - libgcc >=13 + - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT - license_family: MIT - size: 199141 - timestamp: 1725456356043 -- kind: conda - name: pyzmq - version: 26.2.0 - build: py312h2427ae1_3 - build_number: 3 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda - sha256: cfc4ea87d68b5f0ed64a61f500d5ea0a2310d1f281a4f95afa06c703ea1bdf7d - md5: 1f0779280c3dc1e72cfd86bd1e59791d + license_family: MIT + size: 192148 + timestamp: 1737454886351 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda + sha256: bc303f9b11e04a515f79cd5ad3bfa0e84b9dfec76552626d6263b38789fe6678 + md5: 746ce19f0829ec3e19c93007b1a224d3 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libsodium >=1.0.20,<1.0.21.0a0 - libstdcxx >=13 @@ -7698,19 +5639,12 @@ packages: - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD - size: 371730 - timestamp: 1728644030875 -- kind: conda - name: pyzmq - version: 26.2.0 - build: py312hbf22597_3 - build_number: 3 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-26.2.0-py312hbf22597_3.conda - sha256: bc303f9b11e04a515f79cd5ad3bfa0e84b9dfec76552626d6263b38789fe6678 - md5: 746ce19f0829ec3e19c93007b1a224d3 + size: 378126 + timestamp: 1728642454632 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyzmq-26.2.0-py312h2427ae1_3.conda + sha256: cfc4ea87d68b5f0ed64a61f500d5ea0a2310d1f281a4f95afa06c703ea1bdf7d + md5: 1f0779280c3dc1e72cfd86bd1e59791d depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libsodium >=1.0.20,<1.0.21.0a0 - libstdcxx >=13 @@ -7719,15 +5653,9 @@ packages: - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD - size: 378126 - timestamp: 1728642454632 -- kind: conda - name: pyzmq - version: 26.2.0 - build: py312hf8a1cbd_3 - build_number: 3 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda + size: 371730 + timestamp: 1728644030875 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-26.2.0-py312hf8a1cbd_3.conda sha256: 2e0ca1bb9ab3af5d1f9b38548d65be7097ba0246e7e63c908c9b1323df3f45b5 md5: 7bdaa4c2a84b744ef26c8b2ba65c3d0e depends: @@ -7742,58 +5670,34 @@ packages: license_family: BSD size: 361674 timestamp: 1728642457661 -- kind: conda - name: re2 - version: 2024.07.02 - build: h2d3a13d_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-h2d3a13d_1.conda - sha256: 55e7be480bfb979fa8595a16d7f2adea3a5ac9a77b2e97cd0f7ac40e989edb6c - md5: 83f4e47229834c895a92c18383e1cd9d +- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda + sha256: d213c44958d49ce7e0d4d5b81afec23640cce5016685dbb2d23571a99caa4474 + md5: e84ddf12bde691e8ec894b00ea829ddf depends: - - libre2-11 2024.07.02 h18dbdb1_1 + - libre2-11 2024.07.02 hbbce691_2 license: BSD-3-Clause license_family: BSD - size: 26747 - timestamp: 1728778986331 -- kind: conda - name: re2 - version: 2024.07.02 - build: h77b4e00_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda - sha256: c1721cb80f7201652fc9801f49c214c88aee835d957f2376e301bd40a8415742 - md5: 01093ff37c1b5e6bf9f17c0116747d11 + size: 26786 + timestamp: 1735541074034 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/re2-2024.07.02-haa97905_2.conda + sha256: 040848655df9119bae5a549fb5c8956a5537120859416c1d9d0712b7bac9f12e + md5: 1bf0135339b4a7419a198a795d2d4be0 depends: - - libre2-11 2024.07.02 hbbce691_1 + - libre2-11 2024.07.02 h18dbdb1_2 license: BSD-3-Clause license_family: BSD - size: 26665 - timestamp: 1728778975855 -- kind: conda - name: re2 - version: 2024.07.02 - build: hcd0e937_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-hcd0e937_1.conda - sha256: eebddde6cb10b146507810b701ef6df122d5309cd5151a39d0828aa44dc53725 - md5: 19e29f2ccc9168eb0a39dc40c04c0e21 + size: 26830 + timestamp: 1735540999398 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda + sha256: 4d3799c05f8f662922a0acd129d119774760a3281b883603678e128d1cb307fb + md5: 7a8b4ad8c58a3408ca89d78788c78178 depends: - - libre2-11 2024.07.02 h2348fd5_1 + - libre2-11 2024.07.02 h07bc746_2 license: BSD-3-Clause license_family: BSD - size: 26860 - timestamp: 1728779123653 -- kind: conda - name: readline - version: '8.2' - build: h8228510_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + size: 26861 + timestamp: 1735541088455 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 md5: 47d31b792659ce70f470b5c82fdfb7a4 depends: @@ -7803,13 +5707,7 @@ packages: license_family: GPL size: 281456 timestamp: 1679532220005 -- kind: conda - name: readline - version: '8.2' - build: h8fc344f_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8fc344f_1.conda sha256: 4c99f7417419734e3797d45bc355e61c26520e111893b0d7087a01a7fbfbe3dd md5: 105eb1e16bf83bfb2eb380a48032b655 depends: @@ -7819,13 +5717,7 @@ packages: license_family: GPL size: 294092 timestamp: 1679532238805 -- kind: conda - name: readline - version: '8.2' - build: h92ec313_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda sha256: a1dfa679ac3f6007362386576a704ad2d0d7a02e98f5d0b115f207a2da63e884 md5: 8cbb776a2f641b943d413b3e19df71f4 depends: @@ -7834,12 +5726,7 @@ packages: license_family: GPL size: 250351 timestamp: 1679532511311 -- kind: conda - name: regex - version: 2024.11.6 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2024.11.6-py312h66e93f0_0.conda sha256: fcb5687d3ec5fff580b64b8fb649d9d65c999a91a5c3108a313ecdd2de99f06b md5: 647770db979b43f9c9ca25dcfa7dc4e4 depends: @@ -7851,12 +5738,7 @@ packages: license_family: PSF size: 402821 timestamp: 1730952378415 -- kind: conda - name: regex - version: 2024.11.6 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2024.11.6-py312hb2c0f52_0.conda sha256: ec2c416860de29224e447e2031f8686a05476759c17da1f32f61d4307e540ec8 md5: fa8b589107567f532fa1380e66f91776 depends: @@ -7868,12 +5750,7 @@ packages: license_family: PSF size: 398947 timestamp: 1730952477463 -- kind: conda - name: regex - version: 2024.11.6 - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2024.11.6-py312hea69d52_0.conda sha256: dcdec32f2c7dd37986baa692bedf9db126ad34e92e5e9b64f707cba3d04d2525 md5: e73cda1f18846b608284bd784f061eac depends: @@ -7885,14 +5762,7 @@ packages: license_family: PSF size: 366374 timestamp: 1730952427552 -- kind: conda - name: requests - version: 2.32.3 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda sha256: d701ca1136197aa121bbbe0e8c18db6b5c94acbd041c2b43c70e5ae104e1d8ad md5: a9b9368f3701a417eac9edbcae7cb737 depends: @@ -7907,14 +5777,7 @@ packages: license_family: APACHE size: 58723 timestamp: 1733217126197 -- kind: conda - name: rich - version: 13.9.4 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 md5: 7aed65d4ff222bfb7335997aa40b7da5 depends: @@ -7926,13 +5789,7 @@ packages: license_family: MIT size: 185646 timestamp: 1733342347277 -- kind: conda - name: rich-toolkit - version: 0.11.3 - build: pyh29332c3_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 md5: 4ba15ae9388b67d09782798347481f69 depends: @@ -7945,45 +5802,30 @@ packages: license_family: MIT size: 17357 timestamp: 1733750834072 -- kind: conda - name: s2n - version: 1.5.10 - build: h5df210e_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.10-h5df210e_0.conda - sha256: b5e7a9f4b7b1ec5c5c3661e2defc8b47fab543b05cad6fec78739d8007612464 - md5: 3d3979efcc0f44f3f0cef3de03b296cc +- conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.11-h072c03f_0.conda + sha256: cfdd98c8f9a1e5b6f9abce5dac6d590cc9fe541a08466c9e4a26f90e00b569e3 + md5: 5e8060d52f676a40edef0006a75c718f depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 353450 - timestamp: 1734415474615 -- kind: conda - name: s2n - version: 1.5.10 - build: hb5b8611_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.10-hb5b8611_0.conda - sha256: f6d451821fddc26b93f45e9313e1ea15e09e5ef049d4e137413a5225d2a5dfba - md5: 999f3673f2a011f59287f2969e3749e4 + size: 356213 + timestamp: 1737146304079 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/s2n-1.5.11-h3caee7a_0.conda + sha256: 5a7ae66f96be24c3b2007139b6c3701ab015b4b4eb4eccfdd07be97710243a47 + md5: 1517c0518f8a06a48a15f41d94252874 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - openssl >=3.4.0,<4.0a0 license: Apache-2.0 license_family: Apache - size: 355142 - timestamp: 1734415467047 -- kind: conda - name: safetensors - version: 0.4.5 - build: py312h12e396e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.4.5-py312h12e396e_0.conda - sha256: e44515f875c10efb5e041efcb250dfd18f2cb66ec3f268237549ead6284c6922 - md5: 3b87a00bcaab069172d6cef8124b7142 + size: 352811 + timestamp: 1737146319512 +- conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.5.2-py312h12e396e_0.conda + sha256: 98b8dfa5eec083e0b3ace00906a7f7e748b1e2446dca17e87473f43278fcc036 + md5: 999ca9d87d2bb8b4c01e62c755b928cf depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -7993,16 +5835,11 @@ packages: - __glibc >=2.17 license: Apache-2.0 license_family: APACHE - size: 402547 - timestamp: 1725632183154 -- kind: conda - name: safetensors - version: 0.4.5 - build: py312h8cbf658_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.4.5-py312h8cbf658_0.conda - sha256: e83ebeaba4a07bbe4a1d6c7eef0b4f7ae19901ef365bca043808d16e4c8faad8 - md5: 82ef253c37308b082a478fb92924cad6 + size: 424409 + timestamp: 1736383159339 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/safetensors-0.5.2-py312h8cbf658_0.conda + sha256: 3e230060c1366cbaf03f4315b021dfe47f5147f3af88f17975d661c08fe15ad3 + md5: 2c77c961c4e813b1d05122ac4d803d80 depends: - libgcc >=13 - python >=3.12,<3.13.0a0 @@ -8012,16 +5849,11 @@ packages: - __glibc >=2.17 license: Apache-2.0 license_family: APACHE - size: 400284 - timestamp: 1725632278147 -- kind: conda - name: safetensors - version: 0.4.5 - build: py312he431725_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.4.5-py312he431725_0.conda - sha256: 93a085d0d64237db7f4ff395c446f268c575dc2c324d8e3e5c5d7d836896295e - md5: ccb978cf1e3151c25a44c4ae65c1f20e + size: 408166 + timestamp: 1736383184569 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.5.2-py312hcd83bfe_0.conda + sha256: 0aeb3e654095ca0261d560d1fc05912d0e94d547a7dc435d7f4cedeba966d176 + md5: fc0383682805e293eba9b8afc9ad0931 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 @@ -8031,16 +5863,9 @@ packages: - __osx >=11.0 license: Apache-2.0 license_family: APACHE - size: 353606 - timestamp: 1725632294079 -- kind: conda - name: shellingham - version: 1.5.4 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + size: 378060 + timestamp: 1736383410115 +- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda sha256: 0557c090913aa63cdbe821dbdfa038a321b488e22bc80196c4b3b1aace4914ef md5: 7c3c2a0f3ebdea2bbc35538d162b43bf depends: @@ -8049,13 +5874,7 @@ packages: license_family: MIT size: 14462 timestamp: 1733301007770 -- kind: conda - name: six - version: 1.17.0 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda sha256: 41db0180680cc67c3fa76544ffd48d6a5679d96f4b71d7498a759e94edc9a2db md5: a451d576819089b0d672f18768be0f65 depends: @@ -8064,13 +5883,7 @@ packages: license_family: MIT size: 16385 timestamp: 1733381032766 -- kind: conda - name: snappy - version: 1.2.1 - build: h8bd8927_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda sha256: ec91e86eeb2c6bbf09d51351b851e945185d70661d2ada67204c9a6419d282d3 md5: 3b3e64af585eadfb52bb90b553db5edf depends: @@ -8081,29 +5894,7 @@ packages: license_family: BSD size: 42739 timestamp: 1733501881851 -- kind: conda - name: snappy - version: 1.2.1 - build: h98b9ce2_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda - sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f - md5: ded86dee325290da2967a3fea3800eb5 - depends: - - __osx >=11.0 - - libcxx >=18 - license: BSD-3-Clause - license_family: BSD - size: 35857 - timestamp: 1733502172664 -- kind: conda - name: snappy - version: 1.2.1 - build: hd4fb6f5_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/snappy-1.2.1-hd4fb6f5_1.conda sha256: c4a07ae5def8d55128f25a567a296ef9d7bf99a3bc79d46bd5160c076a5f50af md5: 2fcc6cd1e5550deb509073fd2e6693e1 depends: @@ -8113,14 +5904,17 @@ packages: license_family: BSD size: 43032 timestamp: 1733501964775 -- kind: conda - name: sniffio - version: 1.3.1 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda + sha256: 4242f95b215127a006eb664fe26ed5a82df87e90cbdbc7ce7ff4971f0720997f + md5: ded86dee325290da2967a3fea3800eb5 + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-3-Clause + license_family: BSD + size: 35857 + timestamp: 1733502172664 +- conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda sha256: c2248418c310bdd1719b186796ae50a8a77ce555228b6acd32768e2543a15012 md5: bf7a226e58dfb8346c70df36065d86c9 depends: @@ -8129,48 +5923,39 @@ packages: license_family: Apache size: 15019 timestamp: 1733244175724 -- kind: conda - name: sse-starlette - version: 2.1.3 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.1.3-pyhd8ed1ab_0.conda - sha256: 6d671a66333410ec7e5e7858a252565a9001366726d1fe3c3a506d7156169085 - md5: 3918255c942c242ed5599e10329e8d0e +- conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-2.2.1-pyhd8ed1ab_0.conda + sha256: 3c6a476e7afb702d841e23c61a0c4cc491929d2e39376d329e67e94c40a236cc + md5: c1ef6bc13dd2caa4b406fb3cb06c2791 depends: - - anyio - - python >=3.8 - - starlette - - uvicorn + - anyio >=4.7.0 + - python >=3.9 + - starlette >=0.41.3 license: BSD-3-Clause license_family: BSD - size: 14712 - timestamp: 1722520112550 -- kind: conda - name: starlette - version: 0.41.3 - build: pyha770c72_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/starlette-0.41.3-pyha770c72_1.conda - sha256: b74fc76107487eb26624c01fc55bfab7eed03ae82e003333c86d8a1eeac53672 - md5: 0207dac04ae2200701fab697f0aaaac4 + size: 15324 + timestamp: 1735126414893 +- conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.45.2-pyha770c72_0.conda + sha256: 2c429dbbd5e7256517ef6cdfc30664b0c0e87f90f3c526afe3b97681aafb5623 + md5: acd5901cdd0365e18129f4748e524615 depends: - - anyio >=3.4.0,<5 + - anyio >=3.6.2,<5 - python >=3.9 - typing_extensions >=3.10.0 license: BSD-3-Clause license_family: BSD - size: 58838 - timestamp: 1733344472634 -- kind: conda - name: tk - version: 8.6.13 - build: h194ca79_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda + size: 58040 + timestamp: 1736016898891 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + size: 3318875 + timestamp: 1699202167581 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda sha256: 7fa27cc512d3a783f38bd16bbbffc008807372499d5b65d089a8e43bde9db267 md5: f75105e0585851f818e0009dd1dde4dc depends: @@ -8180,13 +5965,7 @@ packages: license_family: BSD size: 3351802 timestamp: 1695506242997 -- kind: conda - name: tk - version: 8.6.13 - build: h5083fa2_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 md5: b50a57ba89c32b62428b71a875291c9b depends: @@ -8195,28 +5974,7 @@ packages: license_family: BSD size: 3145523 timestamp: 1699202432999 -- kind: conda - name: tk - version: 8.6.13 - build: noxft_h4845f30_101 - build_number: 101 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda - sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e - md5: d453b98d9c83e71da0741bb0ff4d76bc - depends: - - libgcc-ng >=12 - - libzlib >=1.2.13,<2.0.0a0 - license: TCL - license_family: BSD - size: 3318875 - timestamp: 1699202167581 -- kind: conda - name: tokenizers - version: 0.21.0 - build: py312h8360d73_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.21.0-py312h8360d73_0.conda sha256: 4f504a5e9d77c6d88a8f735c4319429d8bf40b742384f908a2efe0a09acc3cc5 md5: f953aa733207f3d37acf4a3efbedba89 depends: @@ -8233,12 +5991,7 @@ packages: license_family: APACHE size: 2258007 timestamp: 1732734202127 -- kind: conda - name: tokenizers - version: 0.21.0 - build: py312ha0d6ea1_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tokenizers-0.21.0-py312ha0d6ea1_0.conda sha256: ef0f4d4e2c798b1821187ea0ba4c86484e48abaa0e9a19fe68030fa7ff5dde84 md5: 077f48c9e0c08a30d842e15c51df4143 depends: @@ -8255,12 +6008,7 @@ packages: license_family: APACHE size: 2331194 timestamp: 1732734303196 -- kind: conda - name: tokenizers - version: 0.21.0 - build: py312hf3e4074_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.21.0-py312hf3e4074_0.conda sha256: 5d395333fcb22dc611140286c1f2ea8b3fa220a4931c583587cb612238091555 md5: 4c732c74b485ef7ac8ec1c548dd45e8e depends: @@ -8276,45 +6024,30 @@ packages: license_family: APACHE size: 1931389 timestamp: 1732734727624 -- kind: conda - name: tornado - version: 6.4.2 - build: py312h52516f5_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda - sha256: 4c19a544354172b2273553267e734795a6da3c78a04c2d19f8e9e159ca3178bc - md5: e28996d9d2d44d777b7e6fb12f63715b +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda + sha256: 062a3a3a37fa8615ce57929ba7e982c76f5a5810bcebd435950f6d6c4147c310 + md5: e417822cb989e80a0d2b1b576fdd1657 depends: + - __glibc >=2.17,<3.0.a0 - libgcc >=13 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - size: 841662 - timestamp: 1732616934923 -- kind: conda - name: tornado - version: 6.4.2 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py312h66e93f0_0.conda - sha256: 062a3a3a37fa8615ce57929ba7e982c76f5a5810bcebd435950f6d6c4147c310 - md5: e417822cb989e80a0d2b1b576fdd1657 + size: 840414 + timestamp: 1732616043734 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py312h52516f5_0.conda + sha256: 4c19a544354172b2273553267e734795a6da3c78a04c2d19f8e9e159ca3178bc + md5: e28996d9d2d44d777b7e6fb12f63715b depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=13 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - size: 840414 - timestamp: 1732616043734 -- kind: conda - name: tornado - version: 6.4.2 - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda + size: 841662 + timestamp: 1732616934923 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda sha256: 964a2705a36c50040c967b18b45b9cc8de3c2aff4af546979a574e0b38e58e39 md5: fb0605888a475d6a380ae1d1a819d976 depends: @@ -8326,29 +6059,16 @@ packages: license_family: Apache size: 842549 timestamp: 1732616081362 -- kind: conda - name: tqdm - version: 4.67.1 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda - sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d - md5: 4085c9db273a148e149c03627350e22c +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + sha256: 11e2c85468ae9902d24a27137b6b39b4a78099806e551d390e394a8c34b48e40 + md5: 9efbfdc37242619130ea42b1cc4ed861 depends: - colorama - - python >=3.7 + - python >=3.9 license: MPL-2.0 or MIT - size: 89484 - timestamp: 1732497312317 -- kind: conda - name: traitlets - version: 5.14.3 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + size: 89498 + timestamp: 1735661472632 +- conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 md5: 019a7385be9af33791c989871317e1ed depends: @@ -8357,16 +6077,9 @@ packages: license_family: BSD size: 110051 timestamp: 1733367480074 -- kind: conda - name: transformers - version: 4.47.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/transformers-4.47.0-pyhd8ed1ab_1.conda - sha256: d31821081219a0ede5c1f356b65a61ce98ac11e2df78b0eaa684c17c73389fbf - md5: 6d2ec1ddee8057d2d724a0ab0bb578a0 +- conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.48.1-pyhd8ed1ab_0.conda + sha256: 92fc16e2449fc81f516d31630fd18c3f33b95bc0c069655a3c0042fbd11a09a4 + md5: 08f62b2c92d1fa610896fc7b16b05031 depends: - datasets !=2.5.0 - filelock @@ -8382,15 +6095,9 @@ packages: - tqdm >=4.27 license: Apache-2.0 license_family: APACHE - size: 3726957 - timestamp: 1733948063517 -- kind: conda - name: typer - version: 0.15.1 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda + size: 3405362 + timestamp: 1737429408302 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.1-pyhd8ed1ab_0.conda sha256: ef695490e895c2ad552c77ec497b899b09fd4ad4ab07edcf5649f5994cf92a35 md5: 170a0398946d8f5b454e592672b6fc20 depends: @@ -8400,13 +6107,7 @@ packages: license_family: MIT size: 56175 timestamp: 1733408582623 -- kind: conda - name: typer-slim - version: 0.15.1 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.1-pyhd8ed1ab_0.conda sha256: d4965516f35e0805199de6596c4ac76c4ad3d6b012be35e532102f9e53ecb860 md5: 0218b16f5a1dd569e575a7a6415489db depends: @@ -8421,13 +6122,7 @@ packages: license_family: MIT size: 43592 timestamp: 1733408569554 -- kind: conda - name: typer-slim-standard - version: 0.15.1 - build: hd8ed1ab_0 - subdir: noarch - noarch: generic - url: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.1-hd8ed1ab_0.conda sha256: f31c56fe98315da8b9ce848256c17e0b9f87896b41a6ccf0c9cc74644dcef20f md5: 4e603c43bfdfc7b533be087c3e070cc9 depends: @@ -8438,14 +6133,8 @@ packages: license_family: MIT size: 49531 timestamp: 1733408570063 -- kind: conda - name: typing-extensions - version: 4.12.2 - build: hd8ed1ab_1 - build_number: 1 - subdir: noarch +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 md5: b6a408c64b78ec7b779a3e5c7a902433 depends: @@ -8454,14 +6143,7 @@ packages: license_family: PSF size: 10075 timestamp: 1733188758872 -- kind: conda - name: typing_extensions - version: 4.12.2 - build: pyha770c72_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 md5: d17f13df8b65464ca316cbc000a3cb64 depends: @@ -8470,28 +6152,15 @@ packages: license_family: PSF size: 39637 timestamp: 1733188758212 -- kind: conda - name: tzdata - version: 2024b - build: hc8b5060_0 - subdir: noarch - noarch: generic - url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - sha256: 4fde5c3008bf5d2db82f2b50204464314cc3c91c1d953652f7bd01d9e52aefdf - md5: 8ac3367aafb1cc0a068483c580af8015 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda + sha256: c4b1ae8a2931fe9b274c44af29c5475a85b37693999f8c792dad0f8c6734b1de + md5: dbcace4706afdfb7eb891f7b37d07c04 license: LicenseRef-Public-Domain - size: 122354 - timestamp: 1728047496079 -- kind: conda - name: urllib3 - version: 2.2.3 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_1.conda - sha256: 416e30a1c3262275f01a3e22e783118d9e9d2872a739a9ed860d06fa9c7593d5 - md5: 4a2d8ef7c37b8808c5b9b750501fffce + size: 122921 + timestamp: 1737119101255 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.3.0-pyhd8ed1ab_0.conda + sha256: 114919ffa80c328127dab9c8e7a38f9d563c617691fb81fccb11c1e86763727e + md5: 32674f8dbfb7b26410ed580dd3c10a29 depends: - brotli-python >=1.0.9 - h2 >=4,<5 @@ -8500,15 +6169,9 @@ packages: - zstandard >=0.18.0 license: MIT license_family: MIT - size: 98077 - timestamp: 1733206968917 -- kind: conda - name: uvicorn - version: 0.34.0 - build: pyh31011fe_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda + size: 100102 + timestamp: 1734859520452 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa md5: 5d448feee86e4740498ec8f8eb40e052 depends: @@ -8521,13 +6184,7 @@ packages: license_family: BSD size: 48643 timestamp: 1734293057914 -- kind: conda - name: uvicorn-standard - version: 0.34.0 - build: h31011fe_0 - subdir: noarch - noarch: generic - url: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec md5: 32a94143a7f65d76d2d5da37dcb4ed79 depends: @@ -8543,31 +6200,7 @@ packages: license_family: BSD size: 7203 timestamp: 1734293058849 -- kind: conda - name: uvloop - version: 0.21.0 - build: py312h0bf5046_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda - sha256: b1efa77aa4871d7bb09c8dd297fa9bd9070ba7f0f95f2d12ae9cdd31ce8b6b22 - md5: 4f5110253ba80ebf27e55c4ab333880a - depends: - - __osx >=11.0 - - libuv >=1.49.2,<2.0a0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: MIT OR Apache-2.0 - size: 544097 - timestamp: 1730214653726 -- kind: conda - name: uvloop - version: 0.21.0 - build: py312h66e93f0_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda sha256: 9337a80165fcf70b06b9d6ba920dad702260ca966419ae77560a15540e41ab72 md5: 998e481e17c1b6a74572e73b06f2df08 depends: @@ -8579,13 +6212,7 @@ packages: license: MIT OR Apache-2.0 size: 701355 timestamp: 1730214506716 -- kind: conda - name: uvloop - version: 0.21.0 - build: py312hb2c0f52_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/uvloop-0.21.0-py312hb2c0f52_1.conda sha256: 807eede6698bd00a1d739a3e19ee6ae6a03a66d2ddd2ef150f2dfd198c3b0292 md5: d83e107ba16c77aba2feec47b7b666a4 depends: @@ -8597,14 +6224,21 @@ packages: license: MIT OR Apache-2.0 size: 655266 timestamp: 1730214606664 -- kind: conda - name: watchfiles - version: 1.0.3 - build: py312h12e396e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.3-py312h12e396e_0.conda - sha256: c89755d8e8f6384b3ba13e41dcabb40bf690c38b9d61512e963129badb1ad332 - md5: b76a5ad00856af6e74da9c3e85fed0cc +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py312h0bf5046_1.conda + sha256: b1efa77aa4871d7bb09c8dd297fa9bd9070ba7f0f95f2d12ae9cdd31ce8b6b22 + md5: 4f5110253ba80ebf27e55c4ab333880a + depends: + - __osx >=11.0 + - libuv >=1.49.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + size: 544097 + timestamp: 1730214653726 +- conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.4-py312h12e396e_0.conda + sha256: b728f525dcae2c10524f9942255346eba62aee9c820ff269d7dd4f7caffb7ffb + md5: df87129c4cb7afc4a3cbad71a1b9e223 depends: - __glibc >=2.17,<3.0.a0 - anyio >=3.0.0 @@ -8615,16 +6249,11 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT - size: 410432 - timestamp: 1733998892675 -- kind: conda - name: watchfiles - version: 1.0.3 - build: py312h8cbf658_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.3-py312h8cbf658_0.conda - sha256: 9be9569c279dc6e7881e9b45fe9f0368218538c660641e2f8b0e023e72a6571c - md5: 3465c1a19634233abc2d1832ac01fd31 + size: 410192 + timestamp: 1736550568524 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/watchfiles-1.0.4-py312h8cbf658_0.conda + sha256: 45193910f6bafc287c784442d173745161b18f96223f0f990a9a744fda753787 + md5: ed958a27e610c31de625e167d4c11a04 depends: - anyio >=3.0.0 - libgcc >=13 @@ -8635,16 +6264,11 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT - size: 404239 - timestamp: 1733998941045 -- kind: conda - name: watchfiles - version: 1.0.3 - build: py312hcd83bfe_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.3-py312hcd83bfe_0.conda - sha256: b64b78a7d6384bf72a878256802c783c692fe641ab4b806fd7e9f45e18a5e3b4 - md5: 13b89e1aa72aa773806b1f59ec018b67 + size: 403791 + timestamp: 1736550743174 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.4-py312hcd83bfe_0.conda + sha256: 84122e3712f2263e12c9d2be75d122eaf2d269801183df4b73aadcb670943b17 + md5: 946eb0208d09b811a671fad9b2831f4e depends: - __osx >=11.0 - anyio >=3.0.0 @@ -8655,16 +6279,11 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - size: 363162 - timestamp: 1733999215646 -- kind: conda - name: websockets - version: '14.1' - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.1-py312h66e93f0_0.conda - sha256: 5998940f91765ba991cf286c863c20bcb53db92bb976a2b5a714566b86b0e763 - md5: a79f7ce618bd0a9f4c00c59a03570fcd + size: 363822 + timestamp: 1736550859472 +- conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-14.2-py312h66e93f0_0.conda + sha256: 52092f1f811fddcbb63e4e8e1c726f32a0a1ea14c36b70982fc2021a3c010e48 + md5: 279166352304d5d4b63429e9c86fa3dc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -8672,16 +6291,11 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - size: 242145 - timestamp: 1731498716195 -- kind: conda - name: websockets - version: '14.1' - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.1-py312hb2c0f52_0.conda - sha256: c292a8badcbe4040537e225fbeb237bfaf272808eab060067d965d3da98ccd5c - md5: 7e2a0ef2a1a87f88f9745f9c7059186e + size: 242949 + timestamp: 1737358315063 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/websockets-14.2-py312hb2c0f52_0.conda + sha256: 5b8273df10b85a667b4fe71788a12c33a9626723650e28f582fd56c87bad0471 + md5: d7535d5d2f8d49d625071f305d6112a1 depends: - libgcc >=13 - python >=3.12,<3.13.0a0 @@ -8689,16 +6303,11 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - size: 242912 - timestamp: 1731498811466 -- kind: conda - name: websockets - version: '14.1' - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.1-py312hea69d52_0.conda - sha256: 98fb04a1a0f53dc604378f94b5795d0b8e462fee01bf0a887cb34d0efdf5d21f - md5: 89b79a9baa7db46ce21f5738a5a3dfda + size: 244675 + timestamp: 1737358397158 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-14.2-py312hea69d52_0.conda + sha256: e5ad8c983a1669d06a6648990c0491d5469143f02003c8fd2ae7d066d7d4b086 + md5: 8757561d3ea10ba178fb7fb888f33e3a depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 @@ -8706,16 +6315,11 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - size: 243131 - timestamp: 1731498944076 -- kind: conda - name: wrapt - version: 1.17.0 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.0-py312h66e93f0_0.conda - sha256: a6fc0f4e90643d0c1fd4aab669b6a79f44a305a5474256f6f2da3354d2310fb4 - md5: ddbe3bb0e1356cb9074dd848570694f9 + size: 246269 + timestamp: 1737358485546 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.2-py312h66e93f0_0.conda + sha256: ed3a1700ecc5d38c7e7dc7d2802df1bc1da6ba3d6f6017448b8ded0affb4ae00 + md5: 669e63af87710f8d52fdec9d4d63b404 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -8723,16 +6327,11 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-2-Clause license_family: BSD - size: 63807 - timestamp: 1732523690292 -- kind: conda - name: wrapt - version: 1.17.0 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.0-py312hb2c0f52_0.conda - sha256: b9aa760a987ccc6bc9c61f57badba6798d9a3dcbd0814e5fb8df6d8d2935af73 - md5: 120d5d1c05386d8ce3efd65a4c86431f + size: 63590 + timestamp: 1736869574299 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wrapt-1.17.2-py312hb2c0f52_0.conda + sha256: cc28914462a21b2f64d9b763a9733bfcbc811dd2975d0d2e6e429e35f5b6d59c + md5: 8a5c6e3f809bae085be369b62dc5d06a depends: - libgcc >=13 - python >=3.12,<3.13.0a0 @@ -8740,16 +6339,11 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-2-Clause license_family: BSD - size: 64783 - timestamp: 1732523806 -- kind: conda - name: wrapt - version: 1.17.0 - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.0-py312hea69d52_0.conda - sha256: 0fb35c3d1642f9f47db87bdb33148f88ef19a3af1eb0ee99b5491551c57269c7 - md5: 73414acdb779a8694a14527865b4357a + size: 63967 + timestamp: 1736869675870 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.2-py312hea69d52_0.conda + sha256: 6a3e68b57de29802e8703d1791dcacb7613bfdc17bbb087c6b2ea2796e6893ef + md5: e49608c832fcf438f70cbcae09c3adc5 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 @@ -8757,28 +6351,19 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-2-Clause license_family: BSD - size: 61043 - timestamp: 1732523852129 -- kind: conda - name: xorg-libxau - version: 1.0.12 - build: h5505292_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda - sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d - md5: 50901e0764b7701d8ed7343496f4f301 + size: 61198 + timestamp: 1736869673767 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda + sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 + md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: MIT license_family: MIT - size: 13593 - timestamp: 1734229104321 -- kind: conda - name: xorg-libxau - version: 1.0.12 - build: h86ecc28_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda + size: 14780 + timestamp: 1734229004433 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 md5: d5397424399a66d33c80b1f2345a36a6 depends: @@ -8787,27 +6372,26 @@ packages: license_family: MIT size: 15873 timestamp: 1734230458294 -- kind: conda - name: xorg-libxau - version: 1.0.12 - build: hb9d3cd8_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda - sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 - md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda + sha256: f33e6f013fc36ebc200f09ddead83468544cb5c353a3b50499b07b8c34e28a8d + md5: 50901e0764b7701d8ed7343496f4f301 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 13593 + timestamp: 1734229104321 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda + sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee + md5: 8035c64cb77ed555e3f150b7b3972480 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT license_family: MIT - size: 14780 - timestamp: 1734229004433 -- kind: conda - name: xorg-libxdmcp - version: 1.1.5 - build: h57736b2_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda + size: 19901 + timestamp: 1727794976192 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda sha256: efcc150da5926cf244f757b8376d96a4db78bc15b8d90ca9f56ac6e75755971f md5: 25a5a7b797fe6e084e04ffe2db02fc62 depends: @@ -8816,27 +6400,7 @@ packages: license_family: MIT size: 20615 timestamp: 1727796660574 -- kind: conda - name: xorg-libxdmcp - version: 1.1.5 - build: hb9d3cd8_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda - sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee - md5: 8035c64cb77ed555e3f150b7b3972480 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - license: MIT - license_family: MIT - size: 19901 - timestamp: 1727794976192 -- kind: conda - name: xorg-libxdmcp - version: 1.1.5 - build: hd74edd7_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda sha256: 9939a166d780700d81023546759102b33fdc2c5f11ef09f5f66c77210fd334c8 md5: 77c447f48cab5d3a15ac224edb86a968 depends: @@ -8845,12 +6409,16 @@ packages: license_family: MIT size: 18487 timestamp: 1727795205022 -- kind: conda - name: xxhash - version: 0.8.2 - build: h31becfc_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda + sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f + md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 97691 + timestamp: 1689951608120 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xxhash-0.8.2-h31becfc_0.conda sha256: 4c526aed70b579d80e5c20d32130b6bc8bde59b3250d43c2b5269755f4da8a9b md5: bb9faf6857108a9f62ebb4dab6ef05da depends: @@ -8859,52 +6427,14 @@ packages: license_family: BSD size: 102442 timestamp: 1689951682147 -- kind: conda - name: xxhash - version: 0.8.2 - build: hb547adb_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.2-hb547adb_0.conda sha256: a70f59f7221ee72c45b39a6b36a33eb9c717ba01921cce1a3c361a4676979a2e md5: 144cd3b88706507f332f5eb5fb83a33b license: BSD-2-Clause license_family: BSD size: 97593 timestamp: 1689951969732 -- kind: conda - name: xxhash - version: 0.8.2 - build: hd590300_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda - sha256: 6fe74a8fd84ab0dc25e4dc3e0c22388dd8accb212897a208b14fe5d4fbb8fc2f - md5: f08fb5c89edfc4aadee1c81d4cfb1fa1 - depends: - - libgcc-ng >=12 - license: BSD-2-Clause - license_family: BSD - size: 97691 - timestamp: 1689951608120 -- kind: conda - name: yaml - version: 0.2.5 - build: h3422bc3_2 - build_number: 2 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 - sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 - md5: 4bb3f014845110883a3c5ee811fd84b4 - license: MIT - license_family: MIT - size: 88016 - timestamp: 1641347076660 -- kind: conda - name: yaml - version: 0.2.5 - build: h7f98852_2 - build_number: 2 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 sha256: a4e34c710eeb26945bdbdaba82d3d74f60a78f54a874ec10d373811a5d217535 md5: 4cb3ad778ec2d5a7acbdf254eb1c42ae depends: @@ -8913,13 +6443,7 @@ packages: license_family: MIT size: 89141 timestamp: 1641346969816 -- kind: conda - name: yaml - version: 0.2.5 - build: hf897c2e_2 - build_number: 2 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-hf897c2e_2.tar.bz2 sha256: 8bc601d6dbe249eba44b3c456765265cd8f42ef1e778f8df9b0c9c88b8558d7e md5: b853307650cb226731f653aa623936a4 depends: @@ -8928,14 +6452,16 @@ packages: license_family: MIT size: 92927 timestamp: 1641347626613 -- kind: conda - name: yarl - version: 1.18.3 - build: py312h66e93f0_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h66e93f0_0.conda - sha256: a0d93c3bef723e384cff8a29a82a2c6b7a73b39328088f3a2d97c901f56e9a63 - md5: 91df2efaa08730416bec2a4502309275 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + sha256: 93181a04ba8cfecfdfb162fc958436d868cc37db504c58078eab4c1a3e57fbb7 + md5: 4bb3f014845110883a3c5ee811fd84b4 + license: MIT + license_family: MIT + size: 88016 + timestamp: 1641347076660 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.18.3-py312h178313f_1.conda + sha256: 6b054c93dd19fd7544af51b41a8eacca2ab62271f6c0c5a2a0cffe80dc37a0ce + md5: 6822c49f294d4355f19d314b8b6063d8 depends: - __glibc >=2.17,<3.0.a0 - idna >=2.0 @@ -8946,16 +6472,11 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - size: 151393 - timestamp: 1733428897813 -- kind: conda - name: yarl - version: 1.18.3 - build: py312hb2c0f52_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hb2c0f52_0.conda - sha256: 470b5b0f3ac89acd143095281167dc2ac1a56d4fa22e1794bd8f3b00bb604540 - md5: 0b3c640697bca798d0ab428f530ed24c + size: 152305 + timestamp: 1737575898300 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yarl-1.18.3-py312hcc812fe_1.conda + sha256: bfa211c0dd5dbb308788f055277dc428b7923ceb2de6a22ea98bc17cf306f9f5 + md5: d14c78abdd6109e2b7162f53b6cc1e77 depends: - idna >=2.0 - libgcc >=13 @@ -8966,16 +6487,11 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - size: 150004 - timestamp: 1733429056665 -- kind: conda - name: yarl - version: 1.18.3 - build: py312hea69d52_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312hea69d52_0.conda - sha256: 69c7863809e11bc90c0d935c16e7f151dcc925add08b3894f06059263a8cb9ba - md5: f32f9b16361866a62d6e061fcd7eb400 + size: 149654 + timestamp: 1737576065314 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yarl-1.18.3-py312h998013c_1.conda + sha256: 48821d23567ca0f853eee6f7812c74392867e123798b5b3c44f58758d8eb580e + md5: 092d3b40acc67c470f379049be343a7a depends: - __osx >=11.0 - idna >=2.0 @@ -8986,15 +6502,9 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - size: 141556 - timestamp: 1733429104990 -- kind: conda - name: zeromq - version: 4.3.5 - build: h3b0a872_7 - build_number: 7 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda + size: 145543 + timestamp: 1737576074753 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda sha256: a4dc72c96848f764bb5a5176aa93dd1e9b9e52804137b99daeebba277b31ea10 md5: 3947a35e916fcc6b9825449affbf4214 depends: @@ -9007,13 +6517,7 @@ packages: license_family: MOZILLA size: 335400 timestamp: 1731585026517 -- kind: conda - name: zeromq - version: 4.3.5 - build: h5efb499_7 - build_number: 7 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zeromq-4.3.5-h5efb499_7.conda sha256: a6003096dc0570a86492040ba32b04ce7662b159600be2252b7a0dfb9414e21c md5: f2f3282559a4b87b7256ecafb4610107 depends: @@ -9025,13 +6529,7 @@ packages: license_family: MOZILLA size: 371419 timestamp: 1731589490850 -- kind: conda - name: zeromq - version: 4.3.5 - build: hc1bb282_7 - build_number: 7 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-hc1bb282_7.conda sha256: 9e585569fe2e7d3bea71972cd4b9f06b1a7ab8fa7c5139f92a31cbceecf25a8a md5: f7e6b65943cb73bce0143737fded08f1 depends: @@ -9043,14 +6541,7 @@ packages: license_family: MOZILLA size: 281565 timestamp: 1731585108039 -- kind: conda - name: zipp - version: 3.21.0 - build: pyhd8ed1ab_1 - build_number: 1 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 md5: 0c3cc595284c5e8f0f9900a9b228a332 depends: @@ -9059,34 +6550,43 @@ packages: license_family: MIT size: 21809 timestamp: 1732827613585 -- kind: conda - name: zstandard - version: 0.23.0 - build: py312h15fbf35_1 - build_number: 1 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda - sha256: d00ca25c1e28fd31199b26a94f8c96574475704a825d244d7a6351ad3745eeeb - md5: a4cde595509a7ad9c13b1a3809bcfe51 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + sha256: 5d7c0e5f0005f74112a34a7425179f4eb6e73c92f5d109e6af4ddeca407c92ab + md5: c9f075ab2f33b3bbee9e62d4ad0a6cd8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib 1.3.1 hb9d3cd8_2 + license: Zlib + license_family: Other + size: 92286 + timestamp: 1727963153079 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.1-h8359307_2.conda + sha256: 58f8860756680a4831c1bf4f294e2354d187f2e999791d53b1941834c4b37430 + md5: e3170d898ca6cb48f1bb567afb92f775 depends: - __osx >=11.0 + - libzlib 1.3.1 h8359307_2 + license: Zlib + license_family: Other + size: 77606 + timestamp: 1727963209370 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b + md5: 8b7069e9792ee4e5b4919a7a306d2e67 + depends: + - __glibc >=2.17,<3.0.a0 - cffi >=1.11 + - libgcc >=13 - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - zstd >=1.5.6,<1.5.7.0a0 - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD - size: 330788 - timestamp: 1725305806565 -- kind: conda - name: zstandard - version: 0.23.0 - build: py312hb698573_1 - build_number: 1 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda + size: 419552 + timestamp: 1725305670210 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstandard-0.23.0-py312hb698573_1.conda sha256: 2681c2a249752bdc7978e59ee2f34fcdfcbfda80029b84b8e5fec8dbc9e3af25 md5: ffcb8e97e62af42075e0e5f46bb9856e depends: @@ -9101,65 +6601,44 @@ packages: license_family: BSD size: 392496 timestamp: 1725305808244 -- kind: conda - name: zstandard - version: 0.23.0 - build: py312hef9b889_1 - build_number: 1 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda - sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b - md5: 8b7069e9792ee4e5b4919a7a306d2e67 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.23.0-py312h15fbf35_1.conda + sha256: d00ca25c1e28fd31199b26a94f8c96574475704a825d244d7a6351ad3745eeeb + md5: a4cde595509a7ad9c13b1a3809bcfe51 depends: - - __glibc >=2.17,<3.0.a0 + - __osx >=11.0 - cffi >=1.11 - - libgcc >=13 - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - zstd >=1.5.6,<1.5.7.0a0 - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD - size: 419552 - timestamp: 1725305670210 -- kind: conda - name: zstd - version: 1.5.6 - build: h02f22dd_0 - subdir: linux-aarch64 - url: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda - sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c - md5: be8d5f8cf21aed237b8b182ea86b3dd6 + size: 330788 + timestamp: 1725305806565 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 depends: - libgcc-ng >=12 - libstdcxx-ng >=12 - libzlib >=1.2.13,<2.0.0a0 license: BSD-3-Clause license_family: BSD - size: 539937 - timestamp: 1714723130243 -- kind: conda - name: zstd - version: 1.5.6 - build: ha6fb4c9_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda - sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b - md5: 4d056880988120e29d75bfff282e0f45 + size: 554846 + timestamp: 1714722996770 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda + sha256: 484f9d0722c77685ae379fbff3ccd662af9ead7e59eb39cd6d0c677cdf25ff6c + md5: be8d5f8cf21aed237b8b182ea86b3dd6 depends: - libgcc-ng >=12 - libstdcxx-ng >=12 - libzlib >=1.2.13,<2.0.0a0 license: BSD-3-Clause license_family: BSD - size: 554846 - timestamp: 1714722996770 -- kind: conda - name: zstd - version: 1.5.6 - build: hb46c0d2_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda + size: 539937 + timestamp: 1714723130243 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda sha256: 2d4fd1ff7ee79cd954ca8e81abf11d9d49954dd1fef80f27289e2402ae9c2e09 md5: d96942c06c3e84bfcc5efb038724a7fd depends: diff --git a/pixi.toml b/pixi.toml index a2aa7df663..2a453cd4b2 100644 --- a/pixi.toml +++ b/pixi.toml @@ -10,6 +10,6 @@ examples = "./examples/run-examples.sh" benchmarks = { cmd = ["./stdlib/scripts/run-benchmarks.sh"], env = { MODULAR_MOJO_NIGHTLY_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo" } } [dependencies] -python = ">=3.9,<3.13" -lit = "*" -max = "*" +python = ">=3.12.8,<3.13" +lit = ">=19.1.7,<20" +max = ">=24.6.0,<25"

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 literal 0 HcmV?d00001 diff --git a/docs/manual/images/pointer-offset-dark.png b/docs/manual/images/pointer-offset-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..731a635fb4768741fb5570757dc81cd73890c98c GIT binary patch literal 15226 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 diff --git a/docs/manual/images/pointer-offset-dark.png b/docs/manual/images/pointer-offset-dark.png deleted file mode 100644 index 731a635fb4768741fb5570757dc81cd73890c98c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15226 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 literal 0 HcmV?d00001 diff --git a/docs/manual/images/pointer-offset-dark.png b/docs/manual/images/pointer-offset-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..731a635fb4768741fb5570757dc81cd73890c98c GIT binary patch literal 15226 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�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 - - - If you're using Bash, run this command: - - ```sh - MOJO_PATH=$(modular config mojo.path) \ - && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \ - && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> "$BASHRC" \ - && source "$BASHRC" - ``` - - - - - If you're using ZSH, run this command: - - ```sh - MOJO_PATH=$(modular config mojo.path) \ - && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> ~/.zshrc \ - && echo 'export PATH="'$MOJO_PATH'/bin:$PATH"' >> ~/.zshrc \ - && source ~/.zshrc - ``` - - - - - If you're using fish, run this command: - - ```sh - set MOJO_PATH (modular config mojo.path) \ - && set -Ux MODULAR_HOME $HOME/.modular \ - && fish_add_path $MOJO_PATH/bin - ``` - - - - -Next, get started with **[Hello, world!](hello-world.html)** - -If you have issues during install, check our [known -issues](/mojo/roadmap.html#mojo-sdk-known-issues). - -:::note - -To help us improve Mojo, we collect some basic system information and -crash reports. [Learn -more](/mojo/faq.html#does-the-mojo-sdk-collect-telemetry). - -::: - -### Update Mojo - -Mojo is a work in progress and we will release regular updates to the -Mojo language and SDK tools. For information about each release, see the -[Mojo changelog](/mojo/changelog.html). - -To check your current Mojo version, use the `--version` option: - -```sh -mojo --version -``` - -To update to the latest Mojo version, use the `modular update` command: - -```sh -modular update mojo -``` - -### Update the Modular CLI - -We may also release updates to the `modular` tool. Run the following -commands to update the CLI on your system. - -Linux: - -```sh -sudo apt update -``` - -```sh -sudo apt install modular -``` - -Mac: - -```sh -brew update -``` - -```sh -brew upgrade modular -``` - -## Develop in the Mojo Playground - -Instead of downloading the Mojo SDK, you can also experiment with Mojo in our -online [Playground](/mojo/playground). - -### What to expect - -The Mojo Playground is a simple online editor where you can test out Mojo -code for yourself. - -- We've included a handful of code examples to show you Mojo basics and - demonstrate its capabilities. - -- This is an online sandbox and not useful for benchmarking. - -- You can download your code or share it as a gist, but there's no mechanism - for saving code in the Playground itself. Any changes will be lost when you - switch code examples (as well as in the event of a server refresh or update). - If you come up with something you want to save—save it locally! - -- The Playground environment doesn't include any Python packages. In the future - we intend to make some common Python packages available to import in the - Playground. - -- There might be some bugs. Please [report issues and feedback on - GitHub](https://github.com/modularml/mojo/issues/new/choose). - -### Caveats - -- The Mojo environment does not have network access, and you cannot install any - Mojo or Python packages. You only have access to Mojo and the Mojo standard - library. - -- For a general list of things that don't work yet in Mojo or have pain-points, - see the [Mojo roadmap and sharp edges](/mojo/roadmap.html). diff --git a/docs/manual/get-started/images/mojo-vscode.png b/docs/manual/images/mojo-vscode.png similarity index 100% rename from docs/manual/get-started/images/mojo-vscode.png rename to docs/manual/images/mojo-vscode.png diff --git a/docs/manual/index.md b/docs/manual/index.md index effdd8ac84..460756b70f 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -24,8 +24,8 @@ feedback](/mojo/community.html). - **Get started** - - [Get started with Mojo](/mojo/manual/get-started/index.html) - - [Hello World!](/mojo/manual/get-started/hello-world.html) + - [Why Mojo](/mojo/manual/why-mojo) + - [Get started with Mojo](/mojo/manual/get-started) - **Language basics** From 8ab0207f684b577fab110fc1be6d541ec9ed05e8 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 7 May 2024 16:54:11 -0500 Subject: [PATCH 0447/2019] [External] [tools] Fix all typos found in #2295 (#39503) [External] [tools] Fix all typos found in #2295 The PR https://github.com/modularml/mojo/pull/2295 started to get a lot of conflicts. To avoid being in a hurry to merge https://github.com/modularml/mojo/pull/2295 because of future conflicts, I opened a PR with only the typo fixes. I'll be able to then rebase #2295 on top of nightly, and we'll have less conflicts in the future and more time to merge the pre-commit config. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2580 MODULAR_ORIG_COMMIT_REV_ID: bf3364014861a0b899dc1eff24384de0f881c494 --- docs/changelog-released.md | 10 +++++----- proposals/byte-as-uint8.md | 2 +- proposals/inferred-parameters.md | 2 +- stdlib/docs/development.md | 2 +- stdlib/scripts/check-licenses.mojo | 2 +- stdlib/src/builtin/_math.mojo | 4 ++-- stdlib/src/builtin/_startup.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/constrained.mojo | 2 +- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/builtin/hex.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/builtin/string.mojo | 10 +++++----- stdlib/src/collections/dict.mojo | 4 ++-- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/memory/reference.mojo | 4 ++-- stdlib/src/memory/unsafe_pointer.mojo | 4 ++-- stdlib/src/os/fstat.mojo | 2 +- stdlib/src/pathlib/path.mojo | 6 +++--- stdlib/src/utils/index.mojo | 6 +++--- stdlib/src/utils/inlined_string.mojo | 4 ++-- stdlib/src/utils/static_tuple.mojo | 2 +- stdlib/src/utils/stringref.mojo | 8 ++++---- stdlib/src/utils/variant.mojo | 2 +- 25 files changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 075945df62..f58e79157f 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -63,7 +63,7 @@ modular update mojo - Improvements to variadic arguments support. - - Heterogenous variadic pack arguments now work reliably even with memory types, + - Heterogeneous variadic pack arguments now work reliably even with memory types, and have a more convenient API to use, as defined by the [`VariadicPack`](/mojo/stdlib/builtin/builtin_list/VariadicPack) type. For example, a simplified version of `print` can be implemented like this: @@ -1607,7 +1607,7 @@ experience without dedicated sugar. `num_physical_cores()`, `num_logical_cores()`, and `num_performance_cores()` functions. -- Homogenous variadic arguments consisting of memory-only types, such as +- Homogeneous variadic arguments consisting of memory-only types, such as `String` are more powerful and easier to use. These arguments are projected into a [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem). @@ -1651,7 +1651,7 @@ experience without dedicated sugar. i[] += " world" ``` - Heterogenous variadic arguments have not yet been moved to the new model, but + Heterogeneous variadic arguments have not yet been moved to the new model, but will in future updates. Note that for variadic arguments of register-passable types like `Int`, the @@ -2072,7 +2072,7 @@ experience without dedicated sugar. ``` Traits can also inherit from other traits, which simply requires that - implementors of the child trait also conform to all parent traits. + implementers of the child trait also conform to all parent traits. ```mojo trait Parent: @@ -3980,7 +3980,7 @@ busy this week. - 📚 Memcpy and memcmp now consistently use count as the byte count. -- 📚 Add a variadic sting join on strings. +- 📚 Add a variadic string join on strings. - 📚 Introduce a `reduce_bit_count` method to count the number of 1 across all elements in a SIMD vector. diff --git a/proposals/byte-as-uint8.md b/proposals/byte-as-uint8.md index 342007be80..fea7496d72 100644 --- a/proposals/byte-as-uint8.md +++ b/proposals/byte-as-uint8.md @@ -9,7 +9,7 @@ hash(bytes: DTypePointer[DType.int8], n: Int) -> Int:`. ## Motivation Logically a byte is an integer value between `0` and `255`. Lots of algorithms -make use of arithmetics ground by this assumption. A signed 8 bit integer on +make use of arithmetic ground by this assumption. A signed 8 bit integer on the contrary represents values between `-128` and `127`. This introduces very subtle bugs, when an algorithm written for unsigned 8 bit integer is used on a signed 8 bit integer. diff --git a/proposals/inferred-parameters.md b/proposals/inferred-parameters.md index 704f590bb5..b9ae5a7451 100644 --- a/proposals/inferred-parameters.md +++ b/proposals/inferred-parameters.md @@ -119,7 +119,7 @@ example would be expressed as: fn scalar_param[x: Scalar[dt], dt: DType](): pass ``` -Where any parameter is inferrable from any previous parameter. The benefits of +Where any parameter is inferable from any previous parameter. The benefits of this approach are that the order of parameters at the callsite match the order in the declaration: `scalar_param[Int32()]()` diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 5cf444ffcf..3db28ef576 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -341,4 +341,4 @@ library, test your changes, and raise a PR. If you're still having troubles make sure to reach out on [GitHub](https://github.com/modularml/mojo/discussions/new?category=general) or -[Discord](modul.ar/discord)! +[Discord](https://modul.ar/discord)! diff --git a/stdlib/scripts/check-licenses.mojo b/stdlib/scripts/check-licenses.mojo index 08664df7b3..cc0d5126e4 100644 --- a/stdlib/scripts/check-licenses.mojo +++ b/stdlib/scripts/check-licenses.mojo @@ -42,5 +42,5 @@ def main(): print("The following files have missing licences 💥 💔 💥") for file in files_without_license: print(file[]) - print("Please add the license to each file before commiting.") + print("Please add the license to each file before committing.") sys.exit(1) diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 6171a3716f..834573cd1a 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -88,7 +88,7 @@ trait Floorable: trait CeilDivable: """ - The `CeilDivable` trait describes a type that defines a ceil divison + The `CeilDivable` trait describes a type that defines a ceil division operation. Types that conform to `CeilDivable` will work with the `math.ceildiv` @@ -127,7 +127,7 @@ trait CeilDivable: trait CeilDivableRaising: """ - The `CeilDivable` trait describes a type that define a floor divison and + The `CeilDivable` trait describes a type that define a floor division and negation operation that can raise. Types that conform to `CeilDivableRaising` will work with the `//` operator diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 0c83daf7d2..25f1b028be 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -31,7 +31,7 @@ fn _get_global[ fn _init_global_runtime( ignored: UnsafePointer[NoneType], ) -> UnsafePointer[NoneType]: - """Intialize the global runtime. This is a singleton that handle the common + """Initialize the global runtime. This is a singleton that handle the common case where the runtime has the same number of threads as the number of cores. """ return external_call[ diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 85ea8b4285..6888e978de 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -25,7 +25,7 @@ from memory.unsafe_pointer import destroy_pointee @value struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): - """The type of a literal heterogenous list expression. + """The type of a literal heterogeneous list expression. A list consists of zero or more values, separated by commas. diff --git a/stdlib/src/builtin/constrained.mojo b/stdlib/src/builtin/constrained.mojo index a4db80d3c2..4b4a03a990 100644 --- a/stdlib/src/builtin/constrained.mojo +++ b/stdlib/src/builtin/constrained.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements compile time contraints. +"""Implements compile time constraints. These are Mojo built-ins, so you don't need to import them. """ diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 2b16d16f71..73184cce53 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -244,7 +244,7 @@ struct FileHandle: # Skip 2 elements _ = file.seek(2 * sizeof[DType.float32](), os.SEEK_CUR) - # Allocate and load 8 more elements from file hande seek position + # Allocate and load 8 more elements from file handle seek position var ptr2 = DTypePointer[DType.float32].alloc(8) var bytes2 = file.read(ptr2, 8) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index f4f9eed4d3..6e7476dbc9 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -16,7 +16,7 @@ There are a few main tools in this module: - `Hashable` trait for types implementing `__hash__(self) -> Int` - `hash[T: Hashable](hashable: T) -> Int` built-in function. -- A `hash()` implementation for abritrary byte strings, +- A `hash()` implementation for arbitrary byte strings, `hash(data: DTypePointer[DType.int8], n: Int) -> Int`, is the workhorse function, which implements efficient hashing via SIMD vectors. See the documentation of this function for more details on the hash diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index e57aa390d9..35fa9ec3ca 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -24,7 +24,7 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @always_inline fn hex[T: Intable](value: T) -> String: - """Returns the hex string represention of the given integer. + """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f613b0cd58..da5f676a71 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2868,7 +2868,7 @@ fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): var buf = _ArrayMem[Int8, size]() # TODO(MOCO-268): - # Remove this rebind(..) once compiler type comparision bug is fixed. + # Remove this rebind(..) once compiler type comparison bug is fixed. var buf_ptr: UnsafePointer[Int8] = rebind[UnsafePointer[Int8]]( buf.unsafe_ptr() ) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index ed4896663e..f390f5ac0e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -26,7 +26,7 @@ from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf # ===----------------------------------------------------------------------===# -# Utilties +# Utilities # ===----------------------------------------------------------------------===# @@ -920,7 +920,7 @@ struct String( # FIXME: # String.__iadd__ currently only accepts a String, meaning this - # RHS will allocate unneccessarily. + # RHS will allocate unnecessarily. ptr[] += strref return Formatter( @@ -1164,7 +1164,7 @@ struct String( new: The substring to replace with. Returns: - The string where all occurences of `old` are replaced with `new`. + The string where all occurrences of `old` are replaced with `new`. """ if not old: return self._interleave(new) @@ -1280,7 +1280,7 @@ struct String( converted to lowercase. Returns: - A new string where cased letters have been convered to lowercase. + A new string where cased letters have been converted to lowercase. """ # TODO(#26444): @@ -1412,7 +1412,7 @@ struct String( n : The number of times to concatenate the string. Returns: - The string concantenated `n` times. + The string concatenated `n` times. """ if n <= 0: return "" diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 91fd7c9749..3483d7898a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -516,7 +516,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( return self.find(key).__bool__() fn __len__(self) -> Int: - """The number of elements currenly stored in the dictionary.""" + """The number of elements currently stored in the dictionary.""" return self.size fn __bool__(self) -> Bool: @@ -887,7 +887,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn __len__(self) -> Int: - """The number of elements currenly stored in the keyword dictionary.""" + """The number of elements currently stored in the keyword dictionary.""" return len(self._dict) @always_inline("nodebug") diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 1a2703a1f4..bf9b545695 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -353,7 +353,7 @@ struct OptionalReg[T: AnyRegType](Boolable): """Return true if the optional has a value. Returns: - True if the optional has a valu and False otherwise. + True if the optional has a value and False otherwise. """ return __mlir_op.`kgen.variant.is`[index = Int(0).value](self._value) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index d95093492c..c0d6946840 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -120,7 +120,7 @@ struct AddressSpace(EqualityComparable): @always_inline("nodebug") fn __init__(inout self, value: Int): - """Initializes the address space from the underlying integeral value. + """Initializes the address space from the underlying integral value. Args: value: The address space value. @@ -129,7 +129,7 @@ struct AddressSpace(EqualityComparable): @always_inline("nodebug") fn __init__(inout self, value: _GPUAddressSpace): - """Initializes the address space from the underlying integeral value. + """Initializes the address space from the underlying integral value. Args: value: The address space value. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 82fa64502f..cbf2481d7c 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -396,7 +396,7 @@ fn initialize_pointee_move[ The pointer memory location is assumed to contain uninitialized data, and consequently the current contents of this pointer are not destructed before writing `value`. Similarly, ownership of `value` is logically - transfered into the pointer location. + transferred into the pointer location. When compared to `initialize_pointee_copy`, this avoids an extra copy on the caller side when the value is an `owned` rvalue. @@ -418,7 +418,7 @@ fn initialize_pointee_copy[T: Copyable](ptr: UnsafePointer[T, _], value: T): The pointer memory location is assumed to contain uninitialized data, and consequently the current contents of this pointer are not destructed before writing `value`. Similarly, ownership of `value` is logically - transfered into the pointer location. + transferred into the pointer location. When compared to `initialize_pointee_move`, this avoids an extra move on the callee side when the value must be copied. diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index 655d60fc8f..3c70b94e91 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -115,7 +115,7 @@ struct stat_result(Stringable): st_rdev: Int, st_flags: Int, ): - """Intialize the stat_result structure. + """Initialize the stat_result structure. Args: st_mode: File mode: file type and file mode bits (permissions). diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 963bb26eb9..1e27dd0629 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -138,7 +138,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """Returns a string representation of the path. Returns: - A string represntation of the path. + A string representation of the path. """ return self.path @@ -147,7 +147,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """Returns a string representation of the path. Returns: - A string represntation of the path. + A string representation of the path. """ return str(self) @@ -155,7 +155,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """Returns a printable representation of the path. Returns: - A printable represntation of the path. + A printable representation of the path. """ return str(self) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index b285f0927c..5f8abe86d5 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -724,7 +724,7 @@ fn Index[ Args: x: The 1st initial value. y: The 2nd initial value. - z: The 3nd initial value. + z: The 3rd initial value. Returns: The constructed StaticIntTuple. @@ -747,7 +747,7 @@ fn Index[ Args: x: The 1st initial value. y: The 2nd initial value. - z: The 3nd initial value. + z: The 3rd initial value. w: The 4th initial value. Returns: @@ -772,7 +772,7 @@ fn Index[ Args: x: The 1st initial value. y: The 2nd initial value. - z: The 3nd initial value. + z: The 3rd initial value. w: The 4th initial value. v: The 5th initial value. diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index ec45ef515e..8afc3625a2 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -224,7 +224,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): fn _is_small(self) -> Bool: """Returns True if this string is currently in the small-string - opimization layout.""" + optimization layout.""" var res: Bool = self._storage.isa[_FixedString[Self.SMALL_CAP]]() return res @@ -460,7 +460,7 @@ struct _FixedString[CAP: Int]( @value struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): - """A fixed-sized, homogenous, contiguous, inline collection type. + """A fixed-sized, homogeneous, contiguous, inline collection type. Parameters: ElementType: The type of the elements in the array. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index ab5eb10f26..30b58c5632 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -249,7 +249,7 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): @value struct InlineArray[ElementType: CollectionElement, size: Int](Sized): - """A fixed-size sequence of size homogenous elements where size is a constant expression. + """A fixed-size sequence of size homogeneous elements where size is a constant expression. Parameters: ElementType: The type of the elements in the array. diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 8fa088afb5..f3ea469788 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -76,7 +76,7 @@ struct StringRef( return self # TODO: #2317 Drop support for this constructor when we have fully - # transitionned to UInt8 as the main byte type. + # transitioned to UInt8 as the main byte type. @always_inline fn __init__(ptr: DTypePointer[DType.int8], len: Int) -> StringRef: """Construct a StringRef value given a (potentially non-0 terminated @@ -116,7 +116,7 @@ struct StringRef( return Self {data: ptr.bitcast[DType.int8](), length: len} # TODO: #2317 Drop support for this constructor when we have fully - # transitionned to UInt8 as the main byte type. + # transitioned to UInt8 as the main byte type. @always_inline fn __init__(ptr: UnsafePointer[Int8]) -> StringRef: """Construct a StringRef value given a null-terminated string. @@ -148,7 +148,7 @@ struct StringRef( return DTypePointer[DType.uint8](ptr) # TODO: #2317 Drop support for this constructor when we have fully - # transitionned to UInt8 as the main byte type. + # transitioned to UInt8 as the main byte type. @always_inline fn __init__(ptr: DTypePointer[DType.int8]) -> StringRef: """Construct a StringRef value given a null-terminated string. @@ -188,7 +188,7 @@ struct StringRef( return StringRef(ptr.bitcast[DType.int8](), len) # TODO: #2317 Drop support for this method when we have fully - # transitionned to UInt8 as the main byte type. + # transitioned to UInt8 as the main byte type. @always_inline fn unsafe_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 1ffefc1fd8..c9f72f29d6 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -242,7 +242,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): T: The type to take. Returns: - The undelying data as an owned value. + The underlying data as an owned value. """ debug_assert( Self._check[T]() == self._get_state()[], "taking wrong type" From d04edf0427750ea144cd5d4ef058ec4e7bf6fe5a Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 7 May 2024 15:37:29 -0700 Subject: [PATCH 0448/2019] [Docs] Edit contributed docs. Make some formatting fixes that weren't flagged by external CI and did some light editing for style. MODULAR_ORIG_COMMIT_REV_ID: ddb951458fa9a02f49595977d499c1e25712f1a3 --- docs/manual/decorators/index.md | 4 ++ docs/tools/testing.ipynb | 86 ++++++++++++++++++++++----------- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/docs/manual/decorators/index.md b/docs/manual/decorators/index.md index ba5b6373af..45936416a2 100644 --- a/docs/manual/decorators/index.md +++ b/docs/manual/decorators/index.md @@ -25,9 +25,13 @@ actually calling the higher-order function, you simply add the decorator (such as the `@value` decorator) above your code (such as a struct). The Mojo compiler then uses the decorator function to modify your code at compile time. +:::note No custom decorators + The creation of custom decorators is not yet supported. The available ones are built directly into the compiler. +::: + The following pages describe each built-in decorator with examples. :::{#docs} diff --git a/docs/tools/testing.ipynb b/docs/tools/testing.ipynb index 3de573b171..5a4b78043f 100644 --- a/docs/tools/testing.ipynb +++ b/docs/tools/testing.ipynb @@ -8,50 +8,82 @@ "title: Testing\n", "sidebar_position: 2\n", "description: Testing Mojo programs.\n", - "css: /static/styles/page-navigation.css\n", - "website:\n", - " open-graph:\n", - " image: /static/images/mojo-social-card.png\n", - " twitter-card:\n", - " image: /static/images/mojo-social-card.png\n", "---" - ], - "id": "e622ec78c00f1839" + ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Built-in test runner\n", + "The Mojo CLI has a built-in test runner that can be used to test Mojo programs. \n", + "The `mojo test` command recursively searches the `test` directory for any files\n", + "with `test` in the filename. For example, given the following directory\n", + "structure:\n", "\n", - "## File and folder conventions\n", - "\n", - "The Mojo CLI has a built-in test runner that can be used to test Mojo programs. `mojo test` will recursively search the `test` directory for any files with `test` in the filename. So in the following example:\n", "```bash\n", - "ls ./test\n", - "test_users_controller.mojo\n", - "factories.mojo\n", - "./test/models:\n", - "user_model_tests.mojo\n", + "test/\n", + " test_users_controller.mojo\n", + " factories.mojo\n", + " models/\n", + " user_model_tests.mojo\n", "```\n", - "... the `mojo` CLI will search `test_users_controller.mojo` and `user_model_tests.mojo` for tests, but not `factories.mojo`. \n", + "\n", + "The `mojo` CLI will search `test_users_controller.mojo` and \n", + "`user_model_tests.mojo` for tests, but not `factories.mojo`. \n", "\n", "## Test functions\n", "\n", - "Test functions must be named `test_*` and take no arguments. Use `def` notation rather than `fn` notation. If you use `fn` notation then, in the presence of any assertion functions, the compiler will complain unless you wrap the body in a `try/exception` block. So:\n", - "```mojo\n", + "Test functions must be named `test_*` and take no arguments. Use `def` notation\n", + "rather than `fn` notation. By definition, every test function should be capable\n", + "of raising an error. So:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from testing import assert_equal\n", + "\n", + "\n", "def test_foo():\n", - " assert_equal(1, 1)\n", - "```\n", + " assert_equal(1, 1)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "Not:\n", + "\n", "```mojo\n", - "fn test_foo():\n", + "fn test_foo() raises:\n", " assert_equal(1, 1)\n", "```\n", - "\n" - ], - "id": "1d92ef1d2dd77643" + "\n", + "The [testing module](/mojo/stdlib/testing/testing) defines a set of test-related\n", + "utilities. " + ] } - ] + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 } From 6e33914a2711021adaf637e5a9bebbb8a7b02610 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 7 May 2024 15:43:24 -0700 Subject: [PATCH 0449/2019] [Docs] Add Mojo docstring style guide for stdlib. Documents existing docstring conventions. MODULAR_ORIG_COMMIT_REV_ID: eab549cba8bfb0419f777b5458e26c7c9bdb5b62 --- stdlib/docs/docstring-style-guide.md | 238 +++++++++++++++++++++++++++ stdlib/docs/style-guide.md | 3 + 2 files changed, 241 insertions(+) create mode 100644 stdlib/docs/docstring-style-guide.md diff --git a/stdlib/docs/docstring-style-guide.md b/stdlib/docs/docstring-style-guide.md new file mode 100644 index 0000000000..d273672f99 --- /dev/null +++ b/stdlib/docs/docstring-style-guide.md @@ -0,0 +1,238 @@ +# Mojo docstring style guide + +This is a language style guide for Mojo API docs (code comments known as +“docstrings”). The Mojo docstring style is based on the +[Google Python docstring style](https://google.github.io/styleguide/pyguide.html#381-docstrings), +with the addition of the Mojo-specific section headings, `Parameters:` and +`Constraints:`. + +This is a brief set of guidelines that cover most situations. If you have +questions that are not answered here, refer to the more comprehensive [Google +Style Guide for API reference code +comments](https://developers.google.com/style/api-reference-comments). + +For information on validating docstrings, see +[API docstrings](style-guide.md#api-docstrings) in the Coding standards and +style guide. + +## Basics + +- Docstrings support Markdown formatting. + +- End all sentences with a period (including sentence fragments). + + - As you’ll see, most API descriptions are sentence fragments (they are + often missing a subject because we don’t repeat the struct, function, or + argument name in the first sentence). + +- Use code font for all API names (structs, functions, attributes, argument and + parameter names, etc.). + + - Create code font with backticks (\`Int\`). + + - Include empty parentheses for function/method names, regardless of + argument length, but don't include square brackets (even if the function + takes parameters) If it's crucial to identify a specific function overload, + add argument names, and/or a parameter list. + + For example: + + - Call the `erase()` method. + + - Use `pop(index)` to pop a specific element from the list. + + - If you know the power at compile time, you can use the `pow[n](x)` version + of this function. + +## Functions/Methods + +### Description + +- The first sentence is a brief description of what the *function* *does*. The + first word should be a present tense verb ("Gets", "Sets", "Checks", + "Converts", "Performs", "Adds", etc.). + +- If you’re unsure how to phrase a description, just answer the the question, + “What does this function do?” Your answer should complete the sentence, “This + function ____” (but without saying “this function”). + + - A blank line follows the first sentence. + +- If there are any prerequisites, specify them with the second + sentence. Then provide a more detailed description, if necessary. ) + +### Parameters, arguments, and return values + +- Use a noun phrase to describe what the *argument or parameter is.* This + description should be formatted as a sentence (capitalize the first word, add + a period at the end), even though it’s usually a sentence fragment. It should + not be necessary to list the type, since this is added by the API doc + generator. Add additional sentences for further description, as appropriate. + +- Should usually begin with “The” or “A.” + +### Errors + +You can use the `Raises` keyword to describe error conditions for a function. +Note that this isn’t currently supported by the Mojo API doc tooling, and will +render as regular text in the function description, not as a separate section. + +```plaintext +Raises: + An error if the named file doesn't exist. +``` + +### Code examples + +Add an `Examples:` header, then include each code sample as a markdown fenced +code block, specifying the language name. The examples section should go after +any other sections (`Parameters:`,`Args:`, `Constraints:` `Returns:`, +`Raises:`). + +### Example + +```mojo +fn select[ + result_type: DType +]( + self, + true_case: SIMD[result_type, size], + false_case: SIMD[result_type, size], +) -> SIMD[result_type, size]: + """Produces a new vector by selecting values from the input vectors based on + the current boolean values of this SIMD vector. + + Parameters: + result_type: The element type of the input and output SIMD vectors. + + Args: + true_case: The values selected if the positional value is True. + false_case: The values selected if the positional value is False. + + Returns: + A new vector of the form + `[true_case[i] if elem else false_case[i] for i, elem in enumerate(self)]`. + + Examples: + + ```mojo + v1 = SIMD[DType.bool, 4](0, 1, 0, 1) + true_case = SIMD[DType.int32, 4](1, 2, 3, 4) + false_case = SIMD[DType.int32, 4](0, 0, 0, 0) + output = v1.select[DType.int32](true_case, false_case) + print(output) + ``` + + """ +``` + +## Structs/Traits + +- Do not repeat the name in the first sentence. + +- Use a noun phrase to describe what the type *is* (”An unordered collection of + items.”). + +- Or, similar to function descriptions, use a present tense verb (when possible) + to describe what an instance does or what the data represents (“Specifies,” + “Provides,” “Configures,” etc.). + +- Optionally include code examples, as with functions. + +- Docstrings for traits follow the same rules as docstrings for structs, except + that traits can't have parameters or fields—only method definitions. + +### Example + +```mojo +struct RuntimeConfig: +"""Specifies the Inference Engine configuration. + +Configuration properties include the number threads, enabling telemetry, +logging level, etc. +""" +``` + +## Fields or aliases + +Be descriptive even when the name seems obvious. + +### Example + +```mojo +var label: Int +"""The class label ID.""" + +var score: Float64 +"""The prediction score.""" +``` + +### Parameters + +Structs can have parameters, which follow the same rules as function parameters. + +## Constraints + +Mojo functions can have compile-time *constraints,* defined using the +[`constrained()`](https://docs.modular.com/mojo/stdlib/builtin/constrained#constrained) +function. If the constraint isn’t met, compilation fails. Constraints can be +based on anything known at compile time, like a parameter value. You can't +create a constraint on an *argument*, because argument values are only known at +runtime. + +Document constraints using the `Constraints` keyword: + +### Example + +```plaintext +Constraints: + The system must be x86 and `x.type` must be floating point. +``` + +If the only constraints are simple limits on single parameters, they should be +documented as part of the parameter description: + +Example: + +```plaintext +Parameters: + size: The size of the SIMD vector. Constraints: Must be positive and a + power of two. +``` + +For consistency, use the plural “Constraints” even when documenting the +constraint inline in the parameter description. When describing a constraint on + a single parameter, use a sentence fragment omitting the subject: + +```plaintext +# AVOID + type: The DType of the data. Constraints: This type must be integral. + +# PREFER + type: The DType of the data. Constraints: Must be integral. +``` + +Always use the standalone “Constraints” keyword if the constraint doesn’t neatly +fit into the description of a single parameter. For example, the constraints on +a struct method may be based on parameters on the struct itself, or on the +machine architecture the code is compiled for. + +**Don’t** use the term “constraints” for runtime limitations or error +conditions. Wherever possible, be specific about what happens when a runtime +value is out of range (error, undefined behavior, etc.). + +```plaintext +# AVOID +Arguments: + value: The input value. Constraints: Must be non-negative. + +# PREFER +Raises: + An error if `value` is negative. + +# OR + +Returns: + The factorial of `value`. Results are undefined if `value` + is negative. +``` diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 1e2aa8455d..55419b73f4 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -304,6 +304,9 @@ fn add_param_arg[foo: Int](bar: Int) -> Int: return foo + bar ``` +For more detailed style guidelines, see the +[Mojo docstring style guide](docstring-style-guide.md). + ### Testing #### Unit test filenames From 8e9998ce3947e982cf71387c2857e1507527e4f1 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 7 May 2024 18:19:33 -0500 Subject: [PATCH 0450/2019] [External] [stdlib] Make `repr()` work with `DType` (#39511) [External] [stdlib] Make `repr()` work with `DType` This PR is a part of https://github.com/modularml/mojo/pull/2482. This is because https://github.com/modularml/mojo/pull/2482 has become non-trivial to do due to the introduction of the Formatter struct. I'll need time to rework the `__repr__` for the SIMD struct. In the meantime we can merge the implementation for DType. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2581 MODULAR_ORIG_COMMIT_REV_ID: 7ac170a65d45211aa0a18a204cb17889802b86c4 --- stdlib/src/builtin/dtype.mojo | 11 ++++++++++- stdlib/test/builtin/test_dtype.mojo | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 107db3dd3c..7df85f5d35 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -27,7 +27,7 @@ alias _mIsFloat = UInt8(1 << 6) @value @register_passable("trivial") -struct DType(Stringable, KeyElement): +struct DType(Stringable, Representable, KeyElement): """Represents DType and provides methods for working with it.""" alias type = __mlir_type.`!kgen.dtype` @@ -126,6 +126,15 @@ struct DType(Stringable, KeyElement): return "address" return "<>" + @always_inline("nodebug") + fn __repr__(self) -> String: + """Gets the representation of the DType e.g. `"DType.float32"`. + + Returns: + The representation of the dtype. + """ + return "DType." + str(self) + @always_inline("nodebug") fn get_value(self) -> __mlir_type.`!kgen.dtype`: """Gets the associated internal kgen.dtype value. diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index bd12bbecbb..08315aef65 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -22,6 +22,14 @@ fn test_stringable() raises: assert_equal("int64", str(DType.int64)) +fn test_representable() raises: + assert_equal(repr(DType.float32), "DType.float32") + assert_equal(repr(DType.int64), "DType.int64") + assert_equal(repr(DType.bool), "DType.bool") + assert_equal(repr(DType.address), "DType.address") + assert_equal(repr(DType.index), "DType.index") + + fn test_key_element() raises: var set = Set[DType]() set.add(DType.bool) @@ -33,4 +41,5 @@ fn test_key_element() raises: fn main() raises: test_stringable() + test_representable() test_key_element() From f4dbae9ab64536f252f50ce4f4feaa4b8f0678d0 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Tue, 7 May 2024 18:00:12 -0700 Subject: [PATCH 0451/2019] [mojo] Update changelog with `@deprecated` decorator Udpates changelog with new `@deprecated` decorator and adds an example. MODULAR_ORIG_COMMIT_REV_ID: 8d42deca224df41cc22c8a035c7b0979bd39a5e5 --- docs/changelog.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 1025905a5d..eee3b79cf2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,28 @@ what we publish. ### ⭐️ New +- Mojo now supports adding a `@deprecated` decorator on structs, functions, + traits, aliases, and global variables. The decorator marks the attached decl + as deprecated and causes a warning to be emitted when the deprecated decl is + referenced in user code. The decorator requires a deprecation message to be + specified as a string literal. + + ```mojo + @deprecated("Foo is deprecated, use Bar instead") + struct Foo: + pass + + fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead + pass + + @deprecated("use another function!") + fn bar(): + pass + + fn techdebt(): + bar() # warning: use another function! + ``` + - `int()` can now take a string and a specified base to parse an integer from a string: `int("ff", 16)` returns `255`. Additionally, if a base of zero is specified, the string will be parsed as if it was an integer literal, with the From 931f6390ea3cdf9ed06177f06ae7e7321fb4a5e5 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Tue, 7 May 2024 20:16:28 -0500 Subject: [PATCH 0452/2019] [External] [stdlib] [bugfix] Make memcmp function robust against overflow and provide correct SIMD implementation (#39463) [External] [stdlib] [bugfix] Make memcmp function robust against overflow and provide correct SIMD implementation The bugs are reproduced in the unit tests. Co-authored-by: Maxim Zaks Closes modularml/mojo#2524 MODULAR_ORIG_COMMIT_REV_ID: 3a71a8adeca0ad82fbc127bf80d94e19b6f6aec0 --- stdlib/src/memory/memory.mojo | 33 +++++++++++++------- stdlib/test/memory/test_memory.mojo | 48 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 014aeebdb5..e3677edb9b 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -49,19 +49,28 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: for i in range(count): var s1i = s1[i] var s2i = s2[i] - var diff = s1i - s2i - if diff: - return 1 if diff > 0 else -1 + if s1i != s2i: + return 1 if s1i > s2i else -1 return 0 + var iota = llvm_intrinsic[ + "llvm.experimental.stepvector", + SIMD[DType.uint8, simd_width], + has_side_effect=False, + ]() + var vector_end_simd = _align_down(count, simd_width) for i in range(0, vector_end_simd, simd_width): var s1i = s1.load[width=simd_width](i) var s2i = s2.load[width=simd_width](i) - var diff = s1i - s2i - if (diff != 0).reduce_or(): - for j in range(simd_width): - return 1 if diff[j] > 0 else -1 + var diff = s1i != s2i + if diff.reduce_or(): + var index = int( + diff.select( + iota, SIMD[DType.uint8, simd_width](255) + ).reduce_min() + ) + return -1 if s1i[index] < s2i[index] else 1 var last = count - simd_width if last <= 0: @@ -69,10 +78,12 @@ fn _memcmp_impl(s1: DTypePointer, s2: __type_of(s1), count: Int) -> Int: var s1i = s1.load[width=simd_width](last) var s2i = s2.load[width=simd_width](last) - var diff = s1i - s2i - if (diff != 0).reduce_or(): - for j in range(simd_width): - return 1 if diff[j] > 0 else -1 + var diff = s1i != s2i + if diff.reduce_or(): + var index = int( + diff.select(iota, SIMD[DType.uint8, simd_width](255)).reduce_min() + ) + return -1 if s1i[index] < s2i[index] else 1 return 0 diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 3165704608..e59d993f4d 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -129,6 +129,52 @@ def test_memcmp(): assert_equal(errors2, 0) +def test_memcmp_overflow(): + var p1 = DTypePointer[DType.int8].alloc(1) + var p2 = DTypePointer[DType.int8].alloc(1) + p1.store(-120) + p2.store(120) + + var c = memcmp(p1, p2, 1) + assert_equal(c, -1, "-120 is smaller than 120") + + c = memcmp(p2, p1, 1) + assert_equal(c, 1, "120 is bigger than -120") + + +def test_memcmp_simd(): + var length = simdwidthof[DType.int8]() + 10 + + var p1 = DTypePointer[DType.int8].alloc(length) + var p2 = DTypePointer[DType.int8].alloc(length) + memset_zero(p1, length) + memset_zero(p2, length) + p1.store(120) + p1.store(1, 100) + p2.store(120) + p2.store(1, 90) + + var c = memcmp(p1, p2, length) + assert_equal(c, 1, "[120, 100, 0, ...] is bigger than [120, 90, 0, ...]") + + c = memcmp(p2, p1, length) + assert_equal(c, -1, "[120, 90, 0, ...] is smaller than [120, 100, 0, ...]") + + memset_zero(p1, length) + memset_zero(p2, length) + + p1.store(length - 2, 120) + p1.store(length - 1, 100) + p2.store(length - 2, 120) + p2.store(length - 1, 90) + + c = memcmp(p1, p2, length) + assert_equal(c, 1, "[..., 0, 120, 100] is bigger than [..., 0, 120, 90]") + + c = memcmp(p2, p1, length) + assert_equal(c, -1, "[..., 0, 120, 90] is smaller than [..., 120, 100]") + + def test_memcmp_extensive[ type: DType, extermes: StringLiteral = "" ](count: Int): @@ -413,6 +459,8 @@ def main(): test_memcpy() test_memcpy_dtype() test_memcmp() + test_memcmp_overflow() + test_memcmp_simd() test_memcmp_extensive() test_memset() From f9bc0ccd238827d87c2046abace5d3a4420e6ccc Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Tue, 7 May 2024 20:36:26 -0500 Subject: [PATCH 0453/2019] [External] [stdlib] Add `Dict.get(key) -> Optional[T]` and `Dict.get(key, default) -> T` methods (#39462) [External] [stdlib] Add `Dict.get(key)` and `Dict.get(key, default)` Add both functions to keep compatibility with python. ``` fn get(self, key: K) -> Optional[V]: ... fn get(self, key: K, default: V) -> V: ... ``` ORIGINAL_AUTHOR=martinvuyk <110240700+martinvuyk@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2519 Co-authored-by: martinvuyk <110240700+martinvuyk@users.noreply.github.com> Closes modularml/mojo#2519 MODULAR_ORIG_COMMIT_REV_ID: 39b02dacb43990dec45eb4050d5c3021a0b0057a --- docs/changelog.md | 3 +++ stdlib/src/collections/dict.mojo | 24 ++++++++++++++++++++++++ stdlib/test/collections/test_dict.mojo | 10 ++++++++++ 3 files changed, 37 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index eee3b79cf2..544e4f7a0e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -131,6 +131,9 @@ what we publish. requirement that the decl has documentation (e.g. when used with --diagnose-missing-doc-strings). +- `Dict` now implements `get(key)` and `get(key, default)` functions. + ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 3483d7898a..61bcc5a186 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -607,6 +607,30 @@ struct Dict[K: KeyElement, V: CollectionElement]( return ev.value()[].value return None + fn get(self, key: K) -> Optional[V]: + """Get a value from the dictionary by key. + + Args: + key: The key to search for in the dictionary. + + Returns: + An optional value containing a copy of the value if it was present, + otherwise an empty Optional. + """ + return self.find(key) + + fn get(self, key: K, default: V) -> V: + """Get a value from the dictionary by key. + + Args: + key: The key to search for in the dictionary. + default: Default value to return. + + Returns: + A copy of the value if it was present, otherwise default. + """ + return self.find(key).or_else(default) + fn pop(inout self, key: K, owned default: Optional[V] = None) raises -> V: """Remove a value from the dictionary by key. diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index c83536422d..51867624ce 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -394,9 +394,19 @@ def test_owned_kwargs_dict(): test_taking_owned_kwargs_dict(owned_kwargs^) +def test_find_get(): + var some_dict = Dict[String, Int]() + some_dict["key"] = 1 + assert_equal(some_dict.find("key").take(), 1) + assert_equal(some_dict.get("key").take(), 1) + assert_equal(some_dict.find("not_key").or_else(0), 0) + assert_equal(some_dict.get("not_key", 0), 0) + + def main(): test_dict() test_dict_string_representation_string_int() test_dict_string_representation_int_int() test_owned_kwargs_dict() test_bool_conversion() + test_find_get() From 1eef8a59c2bbad295340575fbccc9ad7358eddeb Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 7 May 2024 19:14:11 -0700 Subject: [PATCH 0454/2019] [mojo-stdlib] Move OptionalReg off -> Self inits It was a weird hybrid, not sure how that could work. MODULAR_ORIG_COMMIT_REV_ID: 84555aa9937ef1512749e411a55d729402d6cdf0 --- stdlib/src/collections/optional.mojo | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index bf9b545695..e93cf62039 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -284,35 +284,25 @@ struct OptionalReg[T: AnyRegType](Boolable): """Create an optional with a value of None.""" self = Self(None) - fn __init__(value: T) -> Self: + fn __init__(inout self, value: T): """Create an optional with a value. Args: value: The value. - - Returns: - The optional. """ - return Self { - _value: __mlir_op.`kgen.variant.create`[ - _type = Self._type, index = Int(0).value - ](value) - } + self._value = __mlir_op.`kgen.variant.create`[ + _type = Self._type, index = Int(0).value + ](value) - fn __init__(value: NoneType) -> Self: + fn __init__(inout self, value: NoneType): """Create an optional without a value from a None literal. Args: value: The None value. - - Returns: - The optional without a value. """ - return Self { - _value: __mlir_op.`kgen.variant.create`[ - _type = Self._type, index = Int(1).value - ](__mlir_attr.false) - } + self._value = __mlir_op.`kgen.variant.create`[ + _type = Self._type, index = Int(1).value + ](__mlir_attr.false) @always_inline fn value(self) -> T: From 50fe33367d2719e38ade004e88714f629c1bca9b Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 22:49:02 -0500 Subject: [PATCH 0455/2019] [External] [mojo-stdlib] Used explicit str in `_location.mojo` (#39536) [External] [mojo-stdlib] Used explicit str in `_location.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2562 MODULAR_ORIG_COMMIT_REV_ID: 75423dfbd50a1b023a440647e321ffb6f056e92d --- stdlib/src/builtin/_location.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/_location.mojo b/stdlib/src/builtin/_location.mojo index d803d62586..32354be21d 100644 --- a/stdlib/src/builtin/_location.mojo +++ b/stdlib/src/builtin/_location.mojo @@ -35,7 +35,7 @@ struct _SourceLocation(Stringable): Args: msg: The message to attach the prefix to. """ - return "At " + str(self) + ": " + msg + return "At " + str(self) + ": " + str(msg) @always_inline("nodebug") From b9ab50c89d1eff0f410d729ca76e9ed3033f77d3 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:04:45 -0500 Subject: [PATCH 0456/2019] [External] [mojo-stdlib] Used explicit str in `string.mojo` (#39538) [External] [mojo-stdlib] Used explicit str in `string.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2566 MODULAR_ORIG_COMMIT_REV_ID: 77aa7252d390bf90132a75de70e191efa4f2f0f8 --- stdlib/src/builtin/string.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index f390f5ac0e..a8d452f076 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -944,9 +944,9 @@ struct String( """ if len(elems) == 0: return "" - var curr = String(elems[0]) + var curr = str(elems[0]) for i in range(1, len(elems)): - curr += self + String(elems[i]) + curr += self + str(elems[i]) return curr fn join[*Types: Stringable](self, *elems: *Types) -> String: From 1f2e15a003f0208bcdb306a5120bfea4c86d1007 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:05:01 -0500 Subject: [PATCH 0457/2019] [External] [mojo-stdlib] Used explicit str in `os.mojo` (#39537) [External] [mojo-stdlib] Used explicit str in `os.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2565 MODULAR_ORIG_COMMIT_REV_ID: 951bca4ef2333752aa17ae91296517e5b945e6bc --- stdlib/src/builtin/io.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index bd740f8133..651fd480ac 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -294,7 +294,7 @@ fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): _put(", ") _put("]") else: - _put(String(x)) + _put(str(x)) @no_inline @@ -374,7 +374,7 @@ fn print[ @parameter fn print_with_separator[i: Int, T: Stringable](value: T): - _put(value) + _put(str(value)) @parameter if i < values.__len__() - 1: From addb129952d3ad2045a37dba83c849588258f3c7 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:06:40 -0500 Subject: [PATCH 0458/2019] [External] [mojo-stdlib] Used explicit str in `list.mojo` (#39539) [External] [mojo-stdlib] Used explicit str in `list.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2567 MODULAR_ORIG_COMMIT_REV_ID: fc6a1ddbce08981aaed8b0929395be6a1ec51fbb --- stdlib/src/collections/list.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1b054a7f5a..9959524b6d 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -456,9 +456,9 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if not self.size: raise "Cannot find index of a value in an empty list." if normalized_start >= self.size: - raise "Given 'start' parameter (" + String( + raise "Given 'start' parameter (" + str( normalized_start - ) + ") is out of range. List only has " + String( + ) + ") is out of range. List only has " + str( self.size ) + " elements." From 234f8685610a8fd83f763d2aee4a865f5964c26f Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:06:56 -0500 Subject: [PATCH 0459/2019] [External] [mojo-stdlib] Used explicit str in `inlined_string.mojo` (#39540) [External] [mojo-stdlib] Used explicit str in `inlined_string.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2569 MODULAR_ORIG_COMMIT_REV_ID: 7ed435bfe7233d7c5d498b0c9a034421422cdcf2 --- stdlib/src/utils/inlined_string.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 8afc3625a2..4770eeec8a 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -302,7 +302,7 @@ struct _FixedString[CAP: Int]( "String literal (len=" + str(len(literal)) + ") is longer than FixedString capacity (" - + CAP + + str(CAP) + ")" ) From 987d3770cdcd1919311fd63419299cb4f05e79dc Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:08:26 -0500 Subject: [PATCH 0460/2019] [External] [mojo-stdlib] Used explicit str in `test_object.mojo` (#39543) [External] [mojo-stdlib] Used explicit str in `test_object.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2572 MODULAR_ORIG_COMMIT_REV_ID: d7a24d0a9fc67b9295e04058374ee53f2a1cf99c --- stdlib/test/builtin/test_object.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 446d142101..02cdc4ea30 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -193,7 +193,7 @@ def test_function_raises(borrowed a) -> object: def test_object_function(): var a: object = test_function assert_true(str(a).startswith("Function at address 0x")) - assert_equal(a(1, 2), 3) + assert_equal(str(a(1, 2)), str(3)) a = test_function_raises with assert_raises(contains="Error from function type"): a(1) From 95b06accfba9395d720e8399e4f0cbacd0545c27 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:09:42 -0500 Subject: [PATCH 0461/2019] [External] [mojo-stdlib] Used explicit str in `test_string.mojo` (#39545) [External] [mojo-stdlib] Used explicit str in `test_string.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2574 MODULAR_ORIG_COMMIT_REV_ID: 0a2b96736fca6e817f355136cd5c520226b721b6 --- stdlib/test/builtin/test_string.mojo | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 6b491a88ac..031bb17223 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -35,14 +35,14 @@ struct AString(Stringable): fn test_stringable() raises: assert_equal("hello", str("hello")) - assert_equal("0", str(String(0))) + assert_equal("0", str(0)) assert_equal("AAA", str(StringRef("AAA"))) - assert_equal("a string", str(String(AString()))) + assert_equal("a string", str(AString())) fn test_representable() raises: assert_equal(repr(String("hello")), "'hello'") - assert_equal(repr(String(0)), "'0'") + assert_equal(repr(str(0)), "'0'") # TODO: Add more complex cases with "'", escape characters, etc # and make String.__repr__ more robust to handle those cases. @@ -53,12 +53,12 @@ fn test_constructors() raises: assert_true(not String()) # Construction from Int - var s0 = String(0) - assert_equal("0", str(String(0))) + var s0 = str(0) + assert_equal("0", str(0)) assert_equal(1, len(s0)) - var s1 = String(123) - assert_equal("123", str(String(123))) + var s1 = str(123) + assert_equal("123", str(123)) assert_equal(3, len(s1)) # Construction from StringLiteral @@ -78,7 +78,7 @@ fn test_constructors() raises: fn test_copy() raises: var s0 = String("find") - var s1 = String(s0) + var s1 = str(s0) s1._buffer[3] = ord("e") assert_equal("find", s0) assert_equal("fine", s1) @@ -126,7 +126,7 @@ fn test_add() raises: var s8 = String("abc is ") var s9 = AString() - assert_equal("abc is a string", s8 + s9) + assert_equal("abc is a string", str(s8) + str(s9)) fn test_string_join() raises: From f40fe2392e7998248bdfd049b6fca574744dc389 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:12:11 -0500 Subject: [PATCH 0462/2019] [External] [mojo-stdlib] Used explicit str in `test_format.mojo` (#39549) [External] [mojo-stdlib] Used explicit str in `test_format.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2578 MODULAR_ORIG_COMMIT_REV_ID: b1e6aba712887b77dd6834a778ec96f220d942e0 --- stdlib/test/utils/test_format.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 9427f2787d..4f614b378f 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -74,4 +74,4 @@ fn test_formatter_of_fixed_string() raises: var s1 = _FixedString[100]() var s1_fmt = Formatter(s1) write_to(s1_fmt, "Hello, World!") - assert_equal(s1, "Hello, World!") + assert_equal(str(s1), "Hello, World!") From d01ea58d971002b5b99418cc70390d42c4a30e3d Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:27:34 -0500 Subject: [PATCH 0463/2019] [External] [mojo-stdlib] Used explicit str in `testing.mojo` (#39541) [External] [mojo-stdlib] Used explicit str in `testing.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2568 MODULAR_ORIG_COMMIT_REV_ID: 15211f45d149db72021d8c265fd26f47a4e0edcb --- stdlib/src/testing/testing.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index bcc48ab732..838123c85d 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -270,7 +270,7 @@ fn assert_almost_equal[ if not almost_equal: var err = str(lhs) + " is not close to " + str( rhs - ) + " with a diff of " + abs(lhs - rhs) + ) + " with a diff of " + str(abs(lhs - rhs)) if msg: err += " (" + msg + ")" From 1bc58ecc0322e12429b535195a93961e63f354c3 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:27:53 -0500 Subject: [PATCH 0464/2019] [External] [mojo-stdlib] Used explicit str in `test_simd.mojo` (#39544) [External] [mojo-stdlib] Used explicit str in `test_simd.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2573 MODULAR_ORIG_COMMIT_REV_ID: 7b8120157bb7403296f4d058a732e3c109d51b48 --- stdlib/test/builtin/test_simd.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 5e6501c10e..d424d813c2 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -532,7 +532,9 @@ def test_join(): def test_interleave(): - assert_equal(Int32(0).interleave(Int32(1)), SIMD[DType.index, 2](0, 1)) + assert_equal( + str(Int32(0).interleave(Int32(1))), str(SIMD[DType.index, 2](0, 1)) + ) assert_equal( SIMD[DType.index, 2](0, 2).interleave(SIMD[DType.index, 2](1, 3)), From 2304e72c61c1a0fa021f77082d9f9e798785ad0e Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:28:03 -0500 Subject: [PATCH 0465/2019] [External] [mojo-stdlib] Used explicit str in `test_remove.mojo` (#39547) [External] [mojo-stdlib] Used explicit str in `test_remove.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2576 MODULAR_ORIG_COMMIT_REV_ID: 6004257105e389eb628c911794ced9c172e11a5b --- stdlib/test/os/test_remove.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index b316092f99..11cc19e8cd 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -71,7 +71,7 @@ fn test_remove() raises: create_file_and_test_delete_path[unlink, "unlink"](my_file_path) # test with relative path - my_file_name = Path("my_relative_file.test") + my_file_name = str(Path("my_relative_file.test")) create_file_and_test_delete_string[remove, "remove"](my_file_name) From 44f4da77b818392c6ec0e08076b24c0c7a87fae4 Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:28:12 -0500 Subject: [PATCH 0466/2019] [External] [mojo-stdlib] Used explicit str in `test_dict.mojo` (#39546) [External] [mojo-stdlib] Used explicit str in `test_dict.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2575 MODULAR_ORIG_COMMIT_REV_ID: 03991baf2bd27b9f08e2db1dc46453e59dedcb58 --- stdlib/test/collections/test_dict.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 51867624ce..5a3eec59b2 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -49,9 +49,9 @@ def test_bool_conversion(): assert_true(dict) dict["b"] = 2 assert_true(dict) - dict.pop("a") + _ = dict.pop("a") assert_true(dict) - dict.pop("b") + _ = dict.pop("b") assert_false(dict) @@ -217,7 +217,7 @@ def test_dict_copy_add_new_item(): # test there are two copies of dict and # they don't share underlying memory copy["b"] = 2 - assert_false(2 in orig) + assert_false(str(2) in orig) def test_dict_copy_calls_copy_constructor(): From 8235f829a9b1d96cf07999a85c32a97390d1977a Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Tue, 7 May 2024 23:28:22 -0500 Subject: [PATCH 0467/2019] [External] [mojo-stdlib] Used explicit str in `test_python_interop.mojo` (#39548) [External] [mojo-stdlib] Used explicit str in `test_python_interop.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2577 MODULAR_ORIG_COMMIT_REV_ID: 97d063cb8cbc16e22ef236e1c0103f54bc2ce427 --- stdlib/test/python/test_python_interop.mojo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index 8e8809cd32..f052ea6f4d 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -26,9 +26,9 @@ alias TEST_DIR = env_get_string["TEST_DIR"]() fn test_execute_python_string(inout python: Python) -> String: try: _ = Python.evaluate("print('evaluated by PyRunString')") - return Python.evaluate("'a' + 'b'") + return str(Python.evaluate("'a' + 'b'")) except e: - return e + return str(e) fn test_local_import(inout python: Python) -> String: @@ -38,10 +38,10 @@ fn test_local_import(inout python: Python) -> String: if my_module: var foo = my_module.Foo("apple") foo.bar = "orange" - return foo.bar + return str(foo.bar) return "no module, no fruit" except e: - return e + return str(e) fn test_call(inout python: Python) -> String: @@ -59,7 +59,7 @@ fn test_call(inout python: Python) -> String: ) ) except e: - return e + return str(e) def main(): From 9fd54048c2f28a1db051518ac10376af814a84da Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 7 May 2024 23:39:06 -0500 Subject: [PATCH 0468/2019] [External] [stdlib] Use UInt8 as the storage type for `StringRef` (#39455) [External] [stdlib] Use UInt8 as the storage type for `StringRef` Note to the maintainers: Something we can see here is that it's a pain to have other structs/methods/functions access the attributes or our containers directly. Here since I changed the `.data` attribute, a lot of code broke, but it should not have been the case since methods `unsafe_ptr()` and `unsafe_uint8_ptr()` were provided. I would advise to make the attributes of our struct private and to provide public getters and setters to allow other functions to use the pointers and such in a future-proof way. Long story short, for `StringRef`, that would mean: * `var data: DTypePointer[DType.uint8]` -> `var _data: DTypePointer[DType.uint8]` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2481 MODULAR_ORIG_COMMIT_REV_ID: 9349e9b939aec9892506af7779313e7dc3534b42 --- stdlib/src/builtin/file.mojo | 2 +- stdlib/src/builtin/hash.mojo | 21 ++++++++++++++ stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/string.mojo | 6 ++-- stdlib/src/python/_cpython.mojo | 14 +++++----- stdlib/src/utils/inlined_string.mojo | 42 ++++++++++++++++++++++++---- stdlib/src/utils/stringref.mojo | 10 +++---- 7 files changed, 75 insertions(+), 22 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 73184cce53..093c90ddc2 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -423,7 +423,7 @@ struct FileHandle: Args: data: The data to write to the file. """ - self._write(data.data, len(data)) + self._write(data.unsafe_ptr(), len(data)) @always_inline fn _write[ diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 6e7476dbc9..c140556f10 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -195,6 +195,27 @@ fn _hash_int8[size: Int](data: SIMD[DType.uint8, size]) -> Int: return int(hash_data) ^ _HASH_SECRET() +fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: + """Hash a byte array using a SIMD-modified DJBX33A hash algorithm. + + Similar to `hash(bytes: DTypePointer[DType.int8], n: Int) -> Int` but + takes a `DTypePointer[DType.uint8]` instead of `DTypePointer[DType.int8]`. + See the overload for a complete description of the algorithm. + + Args: + bytes: The byte array to hash. + n: The length of the byte array. + + Returns: + A 64-bit integer hash. This hash is _not_ suitable for + cryptographic purposes, but will have good low-bit + hash collision statistical properties for common data structures. + """ + return hash(bytes.bitcast[DType.int8](), n) + + +# TODO: Remove this overload once we have finished the transition to uint8 +# for bytes. See https://github.com/modularml/mojo/issues/2317 fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: """Hash a byte array using a SIMD-modified DJBX33A hash algorithm. diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 6de517c859..c49a8affbd 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -772,7 +772,7 @@ struct object(IntableRaising, Boolable, Stringable): var impl = _ImmutableString( UnsafePointer[Int8].alloc(value.length), value.length ) - memcpy(impl.data, value.data, value.length) + memcpy(impl.data, value.unsafe_ptr(), value.length) self._value = impl @always_inline diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index a8d452f076..5c9788a29a 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -531,7 +531,7 @@ struct String( var length = len(str) var buffer = Self._buffer_type() buffer.resize(length + 1, 0) - memcpy(rebind[DTypePointer[DType.int8]](buffer.data), str.data, length) + memcpy(rebind[DTypePointer[DType.uint8]](buffer.data), str.data, length) buffer[length] = 0 self._buffer = buffer^ @@ -983,7 +983,7 @@ struct String( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.unsafe_ptr(), length: len(self)} + return StringRef {data: self.unsafe_uint8_ptr(), length: len(self)} fn _strref_keepalive(self): """ @@ -997,7 +997,7 @@ struct String( fn unsafe_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. - Note that you should use `_as_uint8_ptr()` if you need to access the + Note that you should use `unsafe_uint8_ptr()` if you need to access the pointer as we are now storing the bytes as UInt8. See https://github.com/modularml/mojo/issues/2317 for more information. diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index a84db2f4b5..40114ce151 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -301,7 +301,7 @@ struct CPython: name: StringRef, ) -> PyObjectPtr: var r = self.lib.get_function[ - fn (DTypePointer[DType.int8]) -> PyObjectPtr + fn (DTypePointer[DType.uint8]) -> PyObjectPtr ]("PyImport_ImportModule")(name.data) if self.logging_enabled: print( @@ -325,7 +325,7 @@ struct CPython: raised an exception. """ var status = self.lib.get_function[ - fn (DTypePointer[DType.int8]) -> Int + fn (DTypePointer[DType.uint8]) -> Int ](StringRef("PyRun_SimpleString"))(strref.data) # PyRun_SimpleString returns 0 on success and -1 if an exception was # raised. @@ -341,7 +341,7 @@ struct CPython: var result = PyObjectPtr( self.lib.get_function[ fn ( - DTypePointer[DType.int8], Int32, PyObjectPtr, PyObjectPtr + DTypePointer[DType.uint8], Int32, PyObjectPtr, PyObjectPtr ) -> DTypePointer[DType.int8] ]("PyRun_String")(strref.data, Int32(run_mode), globals, locals) ) @@ -364,7 +364,7 @@ struct CPython: name: StringRef, ) -> PyObjectPtr: var r = self.lib.get_function[ - fn (PyObjectPtr, DTypePointer[DType.int8]) -> PyObjectPtr + fn (PyObjectPtr, DTypePointer[DType.uint8]) -> PyObjectPtr ]("PyObject_GetAttrString")(obj, name.data) if self.logging_enabled: print( @@ -383,7 +383,7 @@ struct CPython: inout self, obj: PyObjectPtr, name: StringRef, new_value: PyObjectPtr ) -> Int: var r = self.lib.get_function[ - fn (PyObjectPtr, DTypePointer[DType.int8], PyObjectPtr) -> Int + fn (PyObjectPtr, DTypePointer[DType.uint8], PyObjectPtr) -> Int ]("PyObject_SetAttrString")(obj, name.data, new_value) if self.logging_enabled: print( @@ -493,7 +493,7 @@ struct CPython: fn PyString_FromStringAndSize(inout self, strref: StringRef) -> PyObjectPtr: var r = self.lib.get_function[ fn ( - DTypePointer[DType.int8], Int, DTypePointer[DType.int8] + DTypePointer[DType.uint8], Int, DTypePointer[DType.int8] ) -> PyObjectPtr ](StringRef("PyUnicode_DecodeUTF8"))( strref.data, strref.length, "strict".unsafe_ptr() @@ -532,7 +532,7 @@ struct CPython: fn PyImport_AddModule(inout self, name: StringRef) -> PyObjectPtr: var value = self.lib.get_function[ - fn (DTypePointer[DType.int8]) -> DTypePointer[DType.int8] + fn (DTypePointer[DType.uint8]) -> DTypePointer[DType.int8] ]("PyImport_AddModule")(name.data) return PyObjectPtr {value: value} diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 4770eeec8a..4df5fc9f12 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -155,12 +155,14 @@ struct InlinedString(Sized, Stringable, CollectionElement): # string. var buffer = List[Int8](capacity=total_len) - var buffer_ptr = rebind[DTypePointer[DType.int8]](buffer.data) + var buffer_ptr = rebind[DTypePointer[DType.uint8]](buffer.data) # Copy the bytes from the current small string layout memcpy( buffer_ptr, - self._storage.get[_FixedString[Self.SMALL_CAP]]()[].as_ptr(), + self._storage.get[ + _FixedString[Self.SMALL_CAP] + ]()[].as_uint8_ptr(), len(self), ) @@ -229,6 +231,8 @@ struct InlinedString(Sized, Stringable, CollectionElement): return res + # TODO: Remove this when we have transitioned to uint8 for bytes. + # See https://github.com/modularml/mojo/issues/2317 for details fn as_ptr(self) -> DTypePointer[DType.int8]: """Returns a pointer to the bytes of string data. @@ -241,6 +245,20 @@ struct InlinedString(Sized, Stringable, CollectionElement): else: return self._storage.get[String]()[].unsafe_ptr() + fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + """Returns a pointer to the bytes of string data. + + Returns: + The pointer to the underlying memory. + """ + + if self._is_small(): + return self._storage.get[ + _FixedString[Self.SMALL_CAP] + ]()[].as_uint8_ptr() + else: + return self._storage.get[String]()[].unsafe_uint8_ptr() + fn _strref_dangerous(self) -> StringRef: """ Returns an inner pointer to the string as a StringRef. @@ -248,7 +266,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.as_ptr(), length: len(self)} + return StringRef {data: self.as_uint8_ptr(), length: len(self)} fn _strref_keepalive(self): """ @@ -366,7 +384,11 @@ struct _FixedString[CAP: Int]( ) # Append the bytes from `strref` at the end of the current string - memcpy(self.buffer.as_ptr() + len(self), strref.data, len(strref)) + memcpy( + self.buffer.as_ptr().bitcast[UInt8]() + len(self), + strref.data, + len(strref), + ) self.size = total_len @@ -427,6 +449,8 @@ struct _FixedString[CAP: Int]( return output^ + # TODO: Remove this when we have transitionned to uint8 for bytes. + # See https://github.com/modularml/mojo/issues/2317 for details fn as_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. @@ -435,6 +459,14 @@ struct _FixedString[CAP: Int]( """ return self.buffer.as_ptr() + fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: + """Retrieves a pointer to the underlying memory. + + Returns: + The pointer to the underlying memory. + """ + return self.buffer.as_ptr().bitcast[UInt8]() + fn _strref_dangerous(self) -> StringRef: """ Returns an inner pointer to the string as a StringRef. @@ -442,7 +474,7 @@ struct _FixedString[CAP: Int]( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.as_ptr(), length: len(self)} + return StringRef {data: self.as_uint8_ptr(), length: len(self)} fn _strref_keepalive(self): """ diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index f3ea469788..2f85e18409 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -50,7 +50,7 @@ struct StringRef( and a length, which need not be null terminated. """ - var data: DTypePointer[DType.int8] + var data: DTypePointer[DType.uint8] """A pointer to the beginning of the string data being referenced.""" var length: Int """The length of the string being referenced.""" @@ -96,7 +96,7 @@ struct StringRef( Constructed `StringRef` object. """ - return Self {data: ptr, length: len} + return Self {data: ptr.bitcast[DType.uint8](), length: len} @always_inline fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> StringRef: @@ -113,7 +113,7 @@ struct StringRef( Constructed `StringRef` object. """ - return Self {data: ptr.bitcast[DType.int8](), length: len} + return Self {data: ptr, length: len} # TODO: #2317 Drop support for this constructor when we have fully # transitioned to UInt8 as the main byte type. @@ -198,7 +198,7 @@ struct StringRef( Returns: The DTypePointer to the underlying memory. """ - return self.data + return self.data.bitcast[DType.int8]() @always_inline fn unsafe_uint8_ptr(self) -> DTypePointer[DType.uint8]: @@ -207,7 +207,7 @@ struct StringRef( Returns: The DTypePointer to the underlying memory. """ - return self.data.bitcast[DType.uint8]() + return self.data @always_inline fn __bool__(self) -> Bool: From 59c591851848f3cc1125936ab4f2093e570bf90b Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Wed, 8 May 2024 00:11:40 -0500 Subject: [PATCH 0469/2019] [External] [mojo-stdlib] Used explicit str in `test_error.mojo` (#39550) [External] [mojo-stdlib] Used explicit str in `test_error.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2570 MODULAR_ORIG_COMMIT_REV_ID: 91fd049de95fd605ca2c1f3246fb68bfeb2a69e2 --- stdlib/test/builtin/test_error.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_error.mojo b/stdlib/test/builtin/test_error.mojo index 1ee890241e..8b5d8e92e0 100644 --- a/stdlib/test/builtin/test_error.mojo +++ b/stdlib/test/builtin/test_error.mojo @@ -29,7 +29,7 @@ def test_error_raising(): def test_from_and_to_string(): var myString: String = "FOO" var error = Error(myString) - assert_equal(error, "FOO") + assert_equal(str(error), "FOO") assert_equal(str(Error("bad")), "bad") From e4b8af300ed08f644f4f26a7ffa474a4ccccaddc Mon Sep 17 00:00:00 2001 From: Kaushal Phulgirkar Date: Wed, 8 May 2024 00:15:23 -0500 Subject: [PATCH 0470/2019] [External] [mojo-stdlib] Used explicit str in `test_file.mojo` (#39542) [External] [mojo-stdlib] Used explicit str in `test_file.mojo` Changed for https://github.com/modularml/mojo/issues/2169 Co-authored-by: Kaushal Phulgirkar Closes modularml/mojo#2571 MODULAR_ORIG_COMMIT_REV_ID: 8a15bc21fef9a1897961de4b8ce9739e819d6e97 --- stdlib/test/builtin/test_file.mojo | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 8720ef2c60..c2bbe2cf67 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -129,7 +129,7 @@ def test_file_seek(): pos = f.seek(-16, os.SEEK_END) assert_equal(pos, 938) - f.read(6) + _ = f.read(6) # Seek from current possition, skip the space pos = f.seek(1, os.SEEK_CUR) @@ -200,12 +200,14 @@ def test_file_read_to_dtype_pointer(): var ptr = DTypePointer[DType.int8].alloc(8) var data = f.read(ptr, 8) - assert_equal(ptr.load[width=8](0), "[76, 111, 114, 101, 109, 32, 105, 112]") + assert_equal( + str(ptr.load[width=8](0)), "[76, 111, 114, 101, 109, 32, 105, 112]" + ) var ptr2 = DTypePointer[DType.int8].alloc(8) var data2 = f.read(ptr2, 8) assert_equal( - ptr2.load[width=8](0), "[115, 117, 109, 32, 100, 111, 108, 111]" + str(ptr2.load[width=8](0)), "[115, 117, 109, 32, 100, 111, 108, 111]" ) From 42f4c3d63d52b8fafcd768bd941629d4e6111c17 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 8 May 2024 11:46:09 -0500 Subject: [PATCH 0471/2019] [External] [stdlib] Enhance Handling of Infinity and NaN in `assert_almost_equal` (#39452) [External] [stdlib] Enhance Handling of Infinity and NaN in `assert_almost_equal` This PR enhances the `assert_almost_equal` function to correctly handle cases involving infinity and NaN. According to `test_assert_almost_equal` added to `/test/testing/test_assertion.mojo`, the current implementation of `assert_almost_equal` results in errors in the following cases: ```mojo alias float_type = DType.float32 alias _inf = inf[float_type]() alias _nan = nan[float_type]() ... _should_succeed( SIMD[float_type, 2](-_inf, _inf), SIMD[float_type, 2](-_inf, _inf) ) ... _should_fail( SIMD[float_type, 2](-_inf, 0.0), SIMD[float_type, 2](_inf, 0.0), rtol=0.1, ) _should_fail( SIMD[float_type, 2](_inf, 0.0), SIMD[float_type, 2](0.0, 0.0), rtol=0.1, ) ... _should_fail( SIMD[float_type, 2](_nan, 0.0), SIMD[float_type, 2](0.0, 0.0), equal_nan=True, ) ``` This PR also: - Eliminates the use of `and` and `or` in the `_isclose` function due to the issue outlined in #2374. - Explicitly reduces boolean vectors to boolean scalar values instead of counting on implicit conversions for clarity. - Avoids arithmetic operations in `_isclose` and `assert_almost_equal` when the type is boolean, as these operations are not supported in this case. - Clarifies the behavior of `assert_almost_equal` in the docstring, highlighting differences from similar functions such as `numpy.testing.assert_allclose`. - Adds the `inf` function to `utils/_numerics` along with corresponding tests in `test/utils/test_numerics.mojo`. ORIGINAL_AUTHOR=Leandro Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2375 Co-authored-by: Leandro Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> Closes modularml/mojo#2375 MODULAR_ORIG_COMMIT_REV_ID: 8e0c2394d01d12015816f242e98d6f457af9cdfd --- stdlib/src/testing/testing.mojo | 59 ++++++++---- stdlib/src/utils/_numerics.mojo | 54 +++++++++++ stdlib/test/testing/test_assertion.mojo | 114 ++++++++++++++++++++++-- stdlib/test/utils/test_numerics.mojo | 21 ++++- 4 files changed, 224 insertions(+), 24 deletions(-) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 838123c85d..b2b03a8c0a 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,7 +19,7 @@ from testing import assert_true ``` """ from collections import Optional -from utils._numerics import isnan +from utils._numerics import isfinite, isnan from builtin._location import __call_location, _SourceLocation # ===----------------------------------------------------------------------=== # @@ -36,21 +36,29 @@ fn _isclose( rtol: Scalar[a.type], equal_nan: Bool, ) -> SIMD[DType.bool, a.size]: + constrained[ + a.type.is_bool() or a.type.is_integral() or a.type.is_floating_point(), + "input type must be boolean, integral, or floating-point", + ]() + @parameter if a.type.is_bool() or a.type.is_integral(): return a == b + else: + var both_nan = isnan(a) & isnan(b) + if equal_nan and both_nan.reduce_and(): + return True + + var res = (a == b) + var atol_vec = SIMD[a.type, a.size](atol) + var rtol_vec = SIMD[a.type, a.size](rtol) + res |= ( + isfinite(a) + & isfinite(b) + & (abs(a - b) <= (atol_vec.max(rtol_vec * abs(a).max(abs(b))))) + ) - if equal_nan and isnan(a) and isnan(b): - return True - - var atol_vec = SIMD[a.type, a.size](atol) - var rtol_vec = SIMD[a.type, a.size](rtol) - var res = abs(a - b) <= (atol_vec.max(rtol_vec * abs(a).max(abs(b)))) - - if not equal_nan: - return res - - return res.select(res, isnan(a) and isnan(b)) + return res | both_nan if equal_nan else res # ===----------------------------------------------------------------------=== # @@ -249,6 +257,14 @@ fn assert_almost_equal[ """Asserts that the input values are equal up to a tolerance. If it is not then an Error is raised. + When the type is boolean or integral, then equality is checked. When the + type is floating-point, then this checks if the two input values are + numerically the close using the $abs(lhs - rhs) <= max(rtol * max(abs(lhs), + abs(rhs)), atol)$ formula. + + Constraints: + The type must be boolean, integral, or floating-point. + Parameters: type: The dtype of the left- and right-hand-side SIMD vectors. size: The width of the left- and right-hand-side SIMD vectors. @@ -257,23 +273,32 @@ fn assert_almost_equal[ lhs: The lhs of the equality. rhs: The rhs of the equality. msg: The message to print. - atol: The _absolute tolerance. + atol: The absolute tolerance. rtol: The relative tolerance. equal_nan: Whether to treat nans as equal. Raises: An Error with the provided message if assert fails and `None` otherwise. """ + constrained[ + type.is_bool() or type.is_integral() or type.is_floating_point(), + "type must be boolean, integral, or floating-point", + ]() + var almost_equal = _isclose( lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan ) - if not almost_equal: - var err = str(lhs) + " is not close to " + str( - rhs - ) + " with a diff of " + str(abs(lhs - rhs)) + + if not almost_equal.reduce_and(): + var err = str(lhs) + " is not close to " + str(rhs) + + @parameter + if type.is_integral() or type.is_floating_point(): + err += " with a diff of " + str(abs(lhs - rhs)) if msg: err += " (" + msg + ")" + raise _assert_error(err, __call_location()) diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index db9dd0b752..27991fafab 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -647,6 +647,60 @@ fn isnan[ ](val.value, (signaling_nan_test | quiet_nan_test).value) +# ===----------------------------------------------------------------------===# +# inf +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn inf[type: DType]() -> Scalar[type]: + """Gets a +inf value for the given dtype. + + Constraints: + Can only be used for FP dtypes. + + Parameters: + type: The value dtype. + + Returns: + The +inf value of the given dtype. + """ + + @parameter + if type == DType.float16: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type == DType.bfloat16: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type == DType.float32: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + elif type == DType.float64: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], + ]() + ) + else: + constrained[False, "+inf only support on floating point types"]() + + return 0 + + # ===----------------------------------------------------------------------===# # isinf # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index 5a50096ba1..e9e8737c43 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -13,13 +13,14 @@ # RUN: %mojo -debug-level full %s from testing import ( + assert_almost_equal, assert_equal, + assert_false, assert_not_equal, assert_raises, assert_true, - assert_false, - assert_almost_equal, ) +from utils._numerics import inf, nan @value @@ -61,22 +62,122 @@ def test_assert_messages(): try: assert_true(False) except e: - assert_true("test_assertion.mojo:62:20: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:63:20: AssertionError:" in str(e)) try: assert_false(True) except e: - assert_true("test_assertion.mojo:67:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:68:21: AssertionError:" in str(e)) try: assert_equal(1, 0) except e: - assert_true("test_assertion.mojo:72:21: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:73:21: AssertionError:" in str(e)) try: assert_not_equal(0, 0) except e: - assert_true("test_assertion.mojo:77:25: AssertionError:" in str(e)) + assert_true("test_assertion.mojo:78:25: AssertionError:" in str(e)) + + +def test_assert_almost_equal(): + alias float_type = DType.float32 + alias _inf = inf[float_type]() + alias _nan = nan[float_type]() + + @parameter + def _should_succeed[ + type: DType, size: Int + ]( + lhs: SIMD[type, size], + rhs: SIMD[type, size], + *, + atol: Scalar[type] = 0, + rtol: Scalar[type] = 0, + equal_nan: Bool = False, + ): + var msg = "`test_assert_almost_equal` should have succeeded" + assert_almost_equal( + lhs, rhs, msg=msg, atol=atol, rtol=rtol, equal_nan=equal_nan + ) + + _should_succeed[DType.bool, 1](True, True) + _should_succeed(SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, 1)) + _should_succeed( + SIMD[float_type, 2](-_inf, _inf), SIMD[float_type, 2](-_inf, _inf) + ) + _should_succeed( + SIMD[float_type, 2](-_nan, _nan), + SIMD[float_type, 2](-_nan, _nan), + equal_nan=True, + ) + _should_succeed( + SIMD[float_type, 2](1.0, -1.1), + SIMD[float_type, 2](1.1, -1.0), + atol=0.11, + ) + _should_succeed( + SIMD[float_type, 2](1.0, -1.1), + SIMD[float_type, 2](1.1, -1.0), + rtol=0.10, + ) + + @parameter + def _should_fail[ + type: DType, size: Int + ]( + lhs: SIMD[type, size], + rhs: SIMD[type, size], + *, + atol: Scalar[type] = 0, + rtol: Scalar[type] = 0, + equal_nan: Bool = False, + ): + var msg = "`test_assert_almost_equal` should have failed" + with assert_raises(contains=msg): + assert_almost_equal( + lhs, rhs, msg=msg, atol=atol, rtol=rtol, equal_nan=equal_nan + ) + + _should_fail[DType.bool, 1](True, False) + _should_fail( + SIMD[DType.int32, 2](0, 1), SIMD[DType.int32, 2](0, -1), atol=5.0 + ) + _should_fail( + SIMD[float_type, 2](-_inf, 0.0), + SIMD[float_type, 2](_inf, 0.0), + rtol=0.1, + ) + _should_fail( + SIMD[float_type, 2](_inf, 0.0), + SIMD[float_type, 2](0.0, 0.0), + rtol=0.1, + ) + _should_fail( + SIMD[float_type, 2](_nan, 0.0), + SIMD[float_type, 2](_nan, 0.0), + equal_nan=False, + ) + _should_fail( + SIMD[float_type, 2](_nan, 0.0), + SIMD[float_type, 2](0.0, 0.0), + equal_nan=False, + ) + _should_fail( + SIMD[float_type, 2](_nan, 0.0), + SIMD[float_type, 2](0.0, 0.0), + equal_nan=True, + ) + _should_fail( + SIMD[float_type, 2](1.0, 0.0), + SIMD[float_type, 2](1.1, 0.0), + atol=0.05, + ) + _should_fail( + SIMD[float_type, 2](-1.0, 0.0), + SIMD[float_type, 2](-1.1, 0.0), + rtol=0.05, + ) def main(): @@ -84,3 +185,4 @@ def main(): test_assert_not_equal_is_generic() test_assert_equal_with_simd() test_assert_messages() + test_assert_almost_equal() diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index d39d52dea8..f5dc97f9d6 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -12,8 +12,9 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from utils._numerics import FPUtils +from sys.info import has_neon from testing import assert_equal, assert_true, assert_false +from utils._numerics import FPUtils, inf, isinf alias FPU64 = FPUtils[DType.float64] @@ -45,5 +46,23 @@ fn test_numerics() raises: assert_equal(FPU64.get_mantissa(FPU64.pack(True, 6, 12)), 12) +fn test_inf() raises: + @parameter + fn _test_inf[type: DType]() raises: + var val = inf[type]() + var msg = "`test_inf` failed for `type == " + str(type) + "`" + assert_true((val > 0.0) & isinf(val), msg=msg) + + @parameter + if not has_neon(): + # "bf16 is not supported for ARM architectures" + _test_inf[DType.bfloat16]() + + _test_inf[DType.float16]() + _test_inf[DType.float32]() + _test_inf[DType.float64]() + + def main(): test_numerics() + test_inf() From 6463091ae9c35c668c474e69be5b7dd464a207ee Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 8 May 2024 14:12:47 -0400 Subject: [PATCH 0472/2019] [stdlib] Rename `Variant.take()` to `unsafe_take()` `Variant.take1 does no non-debug checking that the `Variant` is initialized with a value of the specified type. Additionally, it leaves the `Variant` in an uninitialized state. `Optional.take` is also renamed as its underlying implementation uses `Variant.take`. MODULAR_ORIG_COMMIT_REV_ID: 4fc8a36224ba921eebae6849a1aca6dcbc618c19 --- stdlib/src/collections/optional.mojo | 6 +++--- stdlib/src/utils/variant.mojo | 4 ++-- stdlib/test/collections/test_dict.mojo | 4 ++-- stdlib/test/collections/test_optional.mojo | 2 +- stdlib/test/utils/test_variant.mojo | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index e93cf62039..1187d04f7c 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -128,7 +128,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): debug_assert(self.__bool__(), ".value() on empty Optional") return self._value.get[T]()[] - fn take(owned self) -> T: + fn unsafe_take(owned self) -> T: """Unsafely move the value out of the Optional. The caller takes ownership over the new value, and the Optional is @@ -142,8 +142,8 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): Returns: The contained data of the option as an owned T value. """ - debug_assert(self.__bool__(), ".take() on empty Optional") - return self._value.take[T]() + debug_assert(self.__bool__(), ".unsafe_take() on empty Optional") + return self._value.unsafe_take[T]() fn or_else(self, default: T) -> T: """Return the underlying value contained in the Optional or a default value if the Optional's underlying value is not present. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index c9f72f29d6..2191eb8f61 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -106,7 +106,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): You can - use `isa[T]()` to check what type a variant is - - use `take[T]()` to take a value from the variant + - use `unsafe_take[T]()` to take a value from the variant - use `get[T]()` to get a value out of a variant - This currently does an extra copy/move until we have lifetimes - It also temporarily requires the value to be mutable @@ -227,7 +227,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): unroll[each, len(VariadicList(Ts))]() - fn take[T: CollectionElement](owned self) -> T: + fn unsafe_take[T: CollectionElement](owned self) -> T: """Take the current value of the variant as the provided type. The caller takes ownership of the underlying value. The variant diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 5a3eec59b2..39ee4ef806 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -397,8 +397,8 @@ def test_owned_kwargs_dict(): def test_find_get(): var some_dict = Dict[String, Int]() some_dict["key"] = 1 - assert_equal(some_dict.find("key").take(), 1) - assert_equal(some_dict.get("key").take(), 1) + assert_equal(some_dict.find("key").unsafe_take(), 1) + assert_equal(some_dict.get("key").unsafe_take(), 1) assert_equal(some_dict.find("not_key").or_else(0), 0) assert_equal(some_dict.get("not_key", 0), 0) diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 828bc0b65b..ecc75854b7 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -50,7 +50,7 @@ def test_basic(): assert_equal(1, a1) assert_equal(2, b1) - assert_equal(1, (a^).take()) + assert_equal(1, (a^).unsafe_take()) # TODO: this currently only checks for mutable references. # We may want to come back and add an immutable test once diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 9bfd591e7e..e883b01d3c 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -165,7 +165,7 @@ def test_take_doesnt_call_deleter(): ObservableDel(UnsafePointer.address_of(deleted)) ) assert_false(deleted) - var v2 = v1.take[ObservableDel]() + var v2 = v1.unsafe_take[ObservableDel]() assert_false(deleted) _ = v2 assert_true(deleted) From 37db7addcd963c343504731798ed5454f518a5b7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 8 May 2024 12:23:44 -0600 Subject: [PATCH 0473/2019] [stdlib] Fix using wrong variable in `test_float_literal` It was meant to be testing `f3`, not `f2` again. Fix this, which fixes an unused variable warning from the LSP. MODULAR_ORIG_COMMIT_REV_ID: 134f4147d427d7f129bba8d1e89fc8a0cc0cef49 --- stdlib/test/builtin/test_float_literal.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 1974eb0301..c04c04b6e8 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -105,7 +105,7 @@ def test_boolean_comparable(): assert_true(f2) var f3 = FloatLiteral(1.0) - assert_true(f2) + assert_true(f3) def test_equality(): From 2d97d7e8303203099112f1c50f136c33c3e64f61 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 8 May 2024 11:30:27 -0700 Subject: [PATCH 0474/2019] Functions like `initialize_pointee_move` need to invoke methods like T.__moveinit__ and pass the pointee reference in as the destination argument. The destination argument has an inout argument convention which is always in the default address space, so these need to constrain the pointer they work on to being in the default address space. These functions never worked in other address spaces (they'd explode at elaboration time) so narrow the interfaces to make them more type safe. MODULAR_ORIG_COMMIT_REV_ID: 3aabb670430124f739965944e3941f2b0b7422d1 --- stdlib/src/memory/unsafe_pointer.mojo | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index cbf2481d7c..c6e1764325 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -345,13 +345,15 @@ struct UnsafePointer[ # ===----------------------------------------------------------------------=== # # UnsafePointer extensions # ===----------------------------------------------------------------------=== # -# TODO: These should be methods when we have conditional conformance. + +# TODO: These should be methods when we have conditional conformance. None of +# these can work with pointers in generic address spaces, because they need to +# invoke methods like del or moveinit or copyinit, which take borrowed arguments +# in the corresponding traits. -# This isn't a method because destructors only work in the default address -# space. @always_inline -fn destroy_pointee(ptr: UnsafePointer[_, AddressSpace.GENERIC]): +fn destroy_pointee(ptr: UnsafePointer[_]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed @@ -364,7 +366,7 @@ fn destroy_pointee(ptr: UnsafePointer[_, AddressSpace.GENERIC]): @always_inline -fn move_from_pointee[T: Movable](ptr: UnsafePointer[T, _]) -> T: +fn move_from_pointee[T: Movable](ptr: UnsafePointer[T]) -> T: """Move the value at the pointer out. The pointer must not be null, and the pointer memory location is assumed @@ -388,9 +390,7 @@ fn move_from_pointee[T: Movable](ptr: UnsafePointer[T, _]) -> T: @always_inline -fn initialize_pointee_move[ - T: Movable -](ptr: UnsafePointer[T, _], owned value: T): +fn initialize_pointee_move[T: Movable](ptr: UnsafePointer[T], owned value: T): """Emplace a new value into the pointer location, moving from `value`. The pointer memory location is assumed to contain uninitialized data, @@ -412,7 +412,7 @@ fn initialize_pointee_move[ @always_inline -fn initialize_pointee_copy[T: Copyable](ptr: UnsafePointer[T, _], value: T): +fn initialize_pointee_copy[T: Copyable](ptr: UnsafePointer[T], value: T): """Emplace a copy of `value` into the pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -434,7 +434,7 @@ fn initialize_pointee_copy[T: Copyable](ptr: UnsafePointer[T, _], value: T): @always_inline -fn move_pointee[T: Movable](*, src: UnsafePointer[T, _], dst: UnsafePointer[T]): +fn move_pointee[T: Movable](*, src: UnsafePointer[T], dst: UnsafePointer[T]): """Moves the value `src` points to into the memory location pointed to by `dest`. From 7f5d68063090c334256a2b94c322741be04274e1 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 8 May 2024 13:42:42 -0500 Subject: [PATCH 0475/2019] [External] [stdlib] remove uses of StaticTuple where possible (#39559) [External] [stdlib] remove uses of StaticTuple where possible Fix for #2514 ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2583 --------- Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2583 MODULAR_ORIG_COMMIT_REV_ID: 690cbe5148e583e1f86638f251fab93a1c72a948 --- stdlib/src/builtin/hash.mojo | 5 ++++- stdlib/src/builtin/simd.mojo | 1 - stdlib/src/builtin/string.mojo | 2 +- stdlib/src/collections/vector.mojo | 7 +++---- stdlib/src/os/_linux_aarch64.mojo | 2 +- stdlib/src/os/_linux_x86.mojo | 2 +- stdlib/src/os/_macos.mojo | 2 +- stdlib/src/os/os.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 14 ++++++++++++-- 9 files changed, 24 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index c140556f10..4568c7a3e1 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -30,6 +30,9 @@ from sys.ffi import _get_global from memory import memcpy, memset_zero, stack_allocation +# TODO remove this import onece InlineArray is moved to collections +from utils import InlineArray + # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # @@ -307,7 +310,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: # 3. Copy the tail data (smaller than the SIMD register) into # a final hash state update vector that's stack-allocated. if r != 0: - var remaining = StaticTuple[Int8, stride]() + var remaining = InlineArray[Int8, stride](uninitialized=True) var ptr = DTypePointer[DType.int8]( UnsafePointer.address_of(remaining).bitcast[Int8]() ) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index da5f676a71..2b60384dc6 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -34,7 +34,6 @@ from utils._numerics import isnan as _isnan from utils._numerics import nan as _nan from utils._visualizers import lldb_formatter_wrapping_type from utils.inlined_string import _ArrayMem -from utils import StaticTuple from .dtype import _integral_type_of, _get_dtype_printf_format from .io import _snprintf_scalar, _snprintf, _printf diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 5c9788a29a..e8a0087b70 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -20,7 +20,7 @@ from sys import llvm_intrinsic, bitwidthof from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy -from utils import StringRef, StaticIntTuple, StaticTuple +from utils import StringRef, StaticIntTuple from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 58e1cd641f..1beb9eb2bd 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -20,8 +20,7 @@ from collections.vector import InlinedFixedVector """ from memory import UnsafePointer, Reference - -from utils import StaticTuple +from utils import InlineArray # ===----------------------------------------------------------------------===# # _VecIter @@ -97,7 +96,7 @@ struct InlinedFixedVector[ """ alias static_size: Int = size - alias static_data_type = StaticTuple[type, size] + alias static_data_type = InlineArray[type, size] var static_data: Self.static_data_type """The underlying static storage, used for small vectors.""" var dynamic_data: UnsafePointer[type] @@ -116,7 +115,7 @@ struct InlinedFixedVector[ Args: capacity: The requested maximum capacity of the vector. """ - self.static_data = Self.static_data_type() # Undef initialization + self.static_data = Self.static_data_type(uninitialized=True) self.dynamic_data = UnsafePointer[type]() if capacity > Self.static_size: self.dynamic_data = UnsafePointer[type].alloc(capacity - size) diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index 15551b4218..67258ad01b 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils import StaticIntTuple +from utils import StaticTuple from .fstat import stat_result diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index 4c39a5da60..d8eb1ca3c0 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils.index import StaticIntTuple +from utils.index import StaticTuple from .fstat import stat_result diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index fcdc12ac87..6f1601e23e 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils import StaticIntTuple +from utils import StaticTuple from .fstat import stat_result diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 65b2f19ab4..5abd23157f 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -27,7 +27,7 @@ from memory import ( ) from memory.unsafe_pointer import move_from_pointee -from utils import StringRef +from utils import StringRef, StaticTuple from .path import isdir from .pathlike import PathLike diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 30b58c5632..ded0216113 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -270,12 +270,22 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): constrained[ False, ( - "Initialize with either a variadic list of arguments or a" - " default fill element." + "Initialize with either a variadic list of arguments, a default" + " fill element or pass the keyword argument 'uninitialized'." ), ]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + @always_inline + fn __init__(inout self, *, uninitialized: Bool): + """Constructs an empty with uninitized data. + + Args: + uninitialized: Unused, exists just to make uninitialized + case explicit. + """ + self._array = __mlir_op.`kgen.undef`[_type = Self.type]() + @always_inline fn __init__(inout self, fill: Self.ElementType): """Constructs an empty array where each element is the supplied `fill`. From 37db3c257996cc98353f1f720f48dddf9e834eec Mon Sep 17 00:00:00 2001 From: Helehex Date: Wed, 8 May 2024 13:59:37 -0500 Subject: [PATCH 0476/2019] [External] [stdlib] add `SIMD._Mask` alias (#39460) [External] [stdlib] add `SIMD._Mask` alias adds an alias for a boolean simd vector, and applies it throughout the `simd.mojo` file. --------- Co-authored-by: Helehex Closes modularml/mojo#2510 MODULAR_ORIG_COMMIT_REV_ID: 3040da4bacd659a98fc5f947e304c3286920a531 --- stdlib/src/builtin/simd.mojo | 41 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2b60384dc6..46fef4559e 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -145,6 +145,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size: The size of the SIMD vector. """ + alias _Mask = SIMD[DType.bool, size] + alias element_type = type var value: __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] """The underlying storage for the vector.""" @@ -599,10 +601,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type == DType.bool: - return ( - rebind[SIMD[DType.bool, size]](self) - & rebind[SIMD[DType.bool, size]](rhs) - ).cast[type]() + return (rebind[Self._Mask](self) & rebind[Self._Mask](rhs)).cast[ + type + ]() constrained[type.is_numeric(), "the SIMD type must be numeric"]() return __mlir_op.`pop.mul`(self.value, rhs.value) @@ -748,7 +749,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return _pow(self, rhs) @always_inline("nodebug") - fn __lt__(self, rhs: Self) -> SIMD[DType.bool, size]: + fn __lt__(self, rhs: Self) -> Self._Mask: """Compares two SIMD vectors using less-than comparison. Args: @@ -765,7 +766,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn __le__(self, rhs: Self) -> SIMD[DType.bool, size]: + fn __le__(self, rhs: Self) -> Self._Mask: """Compares two SIMD vectors using less-than-or-equal comparison. Args: @@ -782,7 +783,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn __eq__(self, rhs: Self) -> SIMD[DType.bool, size]: + fn __eq__(self, rhs: Self) -> Self._Mask: """Compares two SIMD vectors using equal-to comparison. Args: @@ -805,7 +806,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn __ne__(self, rhs: Self) -> SIMD[DType.bool, size]: + fn __ne__(self, rhs: Self) -> Self._Mask: """Compares two SIMD vectors using not-equal comparison. Args: @@ -828,7 +829,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn __gt__(self, rhs: Self) -> SIMD[DType.bool, size]: + fn __gt__(self, rhs: Self) -> Self._Mask: """Compares two SIMD vectors using greater-than comparison. Args: @@ -845,7 +846,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn __ge__(self, rhs: Self) -> SIMD[DType.bool, size]: + fn __ge__(self, rhs: Self) -> Self._Mask: """Compares two SIMD vectors using greater-than-or-equal comparison. Args: @@ -1118,7 +1119,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # ===-------------------------------------------------------------------===# @always_inline - fn add_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): + fn add_with_overflow(self, rhs: Self) -> (Self, Self._Mask): """Computes `self + rhs` and a mask of which indices overflowed. Args: @@ -1134,7 +1135,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if type.is_signed(): var result = llvm_intrinsic[ "llvm.sadd.with.overflow", - _RegisterPackType[Self, SIMD[DType.bool, size]], + _RegisterPackType[Self, Self._Mask], Self, Self, ](self, rhs) @@ -1142,14 +1143,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( else: var result = llvm_intrinsic[ "llvm.uadd.with.overflow", - _RegisterPackType[Self, SIMD[DType.bool, size]], + _RegisterPackType[Self, Self._Mask], Self, Self, ](self, rhs) return (result[0], result[1]) @always_inline - fn sub_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): + fn sub_with_overflow(self, rhs: Self) -> (Self, Self._Mask): """Computes `self - rhs` and a mask of which indices overflowed. Args: @@ -1165,7 +1166,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if type.is_signed(): var result = llvm_intrinsic[ "llvm.ssub.with.overflow", - _RegisterPackType[Self, SIMD[DType.bool, size]], + _RegisterPackType[Self, Self._Mask], Self, Self, ](self, rhs) @@ -1173,14 +1174,14 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( else: var result = llvm_intrinsic[ "llvm.usub.with.overflow", - _RegisterPackType[Self, SIMD[DType.bool, size]], + _RegisterPackType[Self, Self._Mask], Self, Self, ](self, rhs) return (result[0], result[1]) @always_inline - fn mul_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): + fn mul_with_overflow(self, rhs: Self) -> (Self, Self._Mask): """Computes `self * rhs` and a mask of which indices overflowed. Args: @@ -1196,7 +1197,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if type.is_signed(): var result = llvm_intrinsic[ "llvm.smul.with.overflow", - _RegisterPackType[Self, SIMD[DType.bool, size]], + _RegisterPackType[Self, Self._Mask], Self, Self, ](self, rhs) @@ -1204,7 +1205,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( else: var result = llvm_intrinsic[ "llvm.umul.with.overflow", - _RegisterPackType[Self, SIMD[DType.bool, size]], + _RegisterPackType[Self, Self._Mask], Self, Self, ](self, rhs) @@ -2250,7 +2251,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ constrained[type.is_bool(), "the simd dtype must be bool"]() return __mlir_op.`pop.simd.select`( - rebind[SIMD[DType.bool, size]](self).value, + rebind[Self._Mask](self).value, true_case.value, false_case.value, ) From 8347a19b252e655061dfd570b8520dcc138e44ba Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 8 May 2024 15:29:52 -0400 Subject: [PATCH 0477/2019] [stdlib] Added changelog for [Internal link] MODULAR_ORIG_COMMIT_REV_ID: b576bdba0855394c74c3867908337043c4ef616d --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 544e4f7a0e..5d65ab3016 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -145,6 +145,9 @@ what we publish. - The `--warn-missing-doc-strings` flag for `mojo` has been renamed to `--diagnose-missing-doc-strings`. +- The `take` function in `Variant` and `Optional` has been renamed to +`unsafe_take`. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the From bff8102c32161ab62ea4dd08dd1e2f464fa6e13e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 8 May 2024 16:49:04 -0400 Subject: [PATCH 0478/2019] [stdlib] Make `FloatLiteral.__str__` implementation use explicit conversions So that it doesn't cause problems when the implicit `String` conversion from `Stringable` is removed. See https://github.com/modularml/mojo/pull/2563 for more context. MODULAR_ORIG_COMMIT_REV_ID: ee9667a448d32bbffa340e6eaf1bce3adad9364c --- stdlib/src/builtin/float_literal.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 1b42ea9a29..48e8fc6a51 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -135,7 +135,7 @@ struct FloatLiteral( Returns: A string representation. """ - return self + return str(Float64(self)) @always_inline("nodebug") fn __int_literal__(self) -> IntLiteral: From 940b83a6bbed6edfccac186f0575a9f6ed7ad3f4 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 8 May 2024 15:20:28 -0700 Subject: [PATCH 0479/2019] [mojo-stdlib] Move `SIMD.__init__` off of `-> Self`. (#39235) This moves this struct off of deprecated syntax. MODULAR_ORIG_COMMIT_REV_ID: 3244a8cafa777f40f6080af252a9312f5f0f2330 --- stdlib/src/builtin/float_literal.mojo | 26 +----- stdlib/src/builtin/simd.mojo | 88 +++++++-------------- stdlib/test/builtin/test_float_literal.mojo | 48 +++++------ 3 files changed, 57 insertions(+), 105 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 48e8fc6a51..8c9a4bcf28 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -46,40 +46,22 @@ struct FloatLiteral( # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __init__(value: FloatLiteral) -> Self: - """Forwarding constructor. - - Args: - value: The FloatLiteral value. - - Returns: - The value. - """ - return value - - @always_inline("nodebug") - fn __init__(value: Self.fp_type) -> Self: + fn __init__(inout self, value: Self.fp_type): """Create a FloatLiteral value from a kgen.float_literal value. Args: value: The float value. - - Returns: - A FloatLiteral value. """ - return Self {value: value} + self.value = value @always_inline("nodebug") - fn __init__(value: IntLiteral) -> Self: + fn __init__(inout self, value: IntLiteral): """Convert an IntLiteral to a FloatLiteral value. Args: value: The IntLiteral value. - - Returns: - The integer value as a FloatLiteral. """ - return Self(__mlir_op.`kgen.int_literal.to_float_literal`(value.value)) + self.value = __mlir_op.`kgen.int_literal.to_float_literal`(value.value) alias nan = Self(__mlir_attr.`#kgen.float_literal`) alias infinity = Self(__mlir_attr.`#kgen.float_literal`) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 46fef4559e..120106ec32 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -164,19 +164,16 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """Returns the minimum (lowest) finite value of SIMD value.""" @always_inline("nodebug") - fn __init__() -> Self: + fn __init__(inout self): """Default initializer of the SIMD vector. By default the SIMD vectors are initialized to all zeros. - - Returns: - SIMD vector whose elements are 0. """ _simd_construction_checks[type, size]() - return _unchecked_zero[type, size]() + self = _unchecked_zero[type, size]() @always_inline("nodebug") - fn __init__(value: SIMD[DType.float64, 1]) -> Self: + fn __init__(inout self, value: SIMD[DType.float64, 1]): """Initializes the SIMD vector with a float. The value is splatted across all the elements of the SIMD @@ -184,9 +181,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Args: value: The input value. - - Returns: - SIMD vector whose elements have the specified value. """ _simd_construction_checks[type, size]() @@ -196,10 +190,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var vec = __mlir_op.`pop.simd.splat`[ _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] ](casted) - return Self {value: vec} + self.value = vec @always_inline("nodebug") - fn __init__(value: Int) -> Self: + fn __init__(inout self, value: Int): """Initializes the SIMD vector with an integer. The integer value is splatted across all the elements of the SIMD @@ -207,9 +201,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Args: value: The input value. - - Returns: - SIMD vector whose elements have the specified value. """ _simd_construction_checks[type, size]() @@ -219,13 +210,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] ](t0) - var vec = __mlir_op.`pop.simd.splat`[ + self.value = __mlir_op.`pop.simd.splat`[ _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] ](casted) - return Self {value: vec} @always_inline("nodebug") - fn __init__(value: IntLiteral) -> Self: + fn __init__(inout self, value: IntLiteral): """Initializes the SIMD vector with an integer. The integer value is splatted across all the elements of the SIMD @@ -233,9 +223,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Args: value: The input value. - - Returns: - SIMD vector whose elements have the specified value. """ _simd_construction_checks[type, size]() @@ -248,52 +235,45 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] ](t0) - var vec = __mlir_op.`pop.simd.splat`[ + self.value = __mlir_op.`pop.simd.splat`[ _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] ](casted) - return Self {value: vec} @always_inline("nodebug") - fn __init__(value: Bool) -> Self: + fn __init__(inout self, value: Bool): """Initializes the SIMD vector with a bool value. The bool value is splatted across all elements of the SIMD vector. Args: value: The bool value. - - Returns: - SIMD vector whose elements have the specified value. """ _simd_construction_checks[type, size]() var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] ](value.value) - var vec = __mlir_op.`pop.simd.splat`[ + self.value = __mlir_op.`pop.simd.splat`[ _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] ](casted) - return Self {value: vec} @always_inline("nodebug") fn __init__( - value: __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] - ) -> Self: + inout self, + value: __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`], + ): """Initializes the SIMD vector with the underlying mlir value. Args: value: The input value. - - Returns: - SIMD vector using the specified value. """ _simd_construction_checks[type, size]() - return Self {value: value} + self.value = value # Construct via a variadic type which has the same number of elements as # the SIMD value. @always_inline("nodebug") - fn __init__(*elems: Scalar[type]) -> Self: + fn __init__(inout self, *elems: Scalar[type]): """Constructs a SIMD vector via a variadic list of elements. If there is just one input value, then it is splatted to all elements @@ -307,37 +287,31 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Args: elems: The variadic list of elements from which the SIMD vector is constructed. - - Returns: - The constructed SIMD vector. """ _simd_construction_checks[type, size]() var num_elements: Int = len(elems) if num_elements == 1: # Construct by broadcasting a scalar. - return Self { - value: __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[ - `!pop.simd<`, - size.value, - `, `, - type.value, - `>`, - ] - ](elems[0].value) - } + self.value = __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, + size.value, + `, `, + type.value, + `>`, + ] + ](elems[0].value) + return debug_assert(size == num_elements, "mismatch in the number of elements") - var result = Self() + self = Self() @unroll for i in range(size): - result[i] = elems[i] - - return result + self[i] = elems[i] @always_inline("nodebug") - fn __init__(value: FloatLiteral) -> Self: + fn __init__(inout self, value: FloatLiteral): """Initializes the SIMD vector with a float. The value is splatted across all the elements of the SIMD @@ -345,9 +319,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Args: value: The input value. - - Returns: - SIMD vector whose elements have the specified value. """ _simd_construction_checks[type, size]() @@ -360,10 +331,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] ](t0) - var vec = __mlir_op.`pop.simd.splat`[ + self.value = __mlir_op.`pop.simd.splat`[ _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] ](casted) - return Self {value: vec} @always_inline("nodebug") fn __len__(self) -> Int: diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index c04c04b6e8..d2372a4e34 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -60,15 +60,15 @@ fn round10(x: Float64) -> Float64: def test_round10(): - assert_equal(round10(FloatLiteral(4.4) % 0.5), 0.4) - assert_equal(round10(FloatLiteral(-4.4) % 0.5), 0.1) - assert_equal(round10(FloatLiteral(4.4) % -0.5), -0.1) - assert_equal(round10(FloatLiteral(-4.4) % -0.5), -0.4) - assert_equal(round10(FloatLiteral(3.1) % 1.0), 0.1) + assert_equal(round10(4.4 % 0.5), 0.4) + assert_equal(round10(-4.4 % 0.5), 0.1) + assert_equal(round10(4.4 % -0.5), -0.1) + assert_equal(round10(-4.4 % -0.5), -0.4) + assert_equal(round10(3.1 % 1.0), 0.1) def test_division(): - assert_equal(FloatLiteral(4.4) / 0.5, 8.8) + assert_equal(4.4 / 0.5, 8.8) alias f1 = 4.4 // 0.5 assert_equal(f1, 8.0) @@ -81,37 +81,37 @@ def test_division(): def test_power(): - assert_almost_equal(FloatLiteral(4.5) ** 2.5, 42.95673695) - assert_almost_equal(FloatLiteral(4.5) ** -2.5, 0.023279235) + assert_almost_equal(4.5**2.5, 42.95673695) + assert_almost_equal(4.5**-2.5, 0.023279235) # TODO (https://github.com/modularml/modular/issues/33045): Float64/SIMD has # issues with negative numbers raised to fractional powers. - # assert_almost_equal(FloatLiteral(-4.5) ** 2.5, -42.95673695) - # assert_almost_equal(FloatLiteral(-4.5) ** -2.5, -0.023279235) + # assert_almost_equal((-4.5) ** 2.5, -42.95673695) + # assert_almost_equal((-4.5) ** -2.5, -0.023279235) def test_int_conversion(): - assert_equal(int(FloatLiteral(-4.0)), -4) - assert_equal(int(FloatLiteral(-4.5)), -4) - assert_equal(int(FloatLiteral(-4.3)), -4) - assert_equal(int(FloatLiteral(4.5)), 4) - assert_equal(int(FloatLiteral(4.0)), 4) + assert_equal(int(-4.0), -4) + assert_equal(int(-4.5), -4) + assert_equal(int(-4.3), -4) + assert_equal(int(4.5), 4) + assert_equal(int(4.0), 4) def test_boolean_comparable(): - var f1 = FloatLiteral(0.0) + var f1 = 0.0 assert_false(f1) - var f2 = FloatLiteral(2.0) + var f2 = 2.0 assert_true(f2) - var f3 = FloatLiteral(1.0) + var f3 = 1.0 assert_true(f3) def test_equality(): - var f1 = FloatLiteral(4.4) - var f2 = FloatLiteral(4.4) - var f3 = FloatLiteral(42.0) + var f1 = 4.4 + var f2 = 4.4 + var f3 = 42.0 assert_equal(f1, f2) assert_not_equal(f1, f3) @@ -124,9 +124,9 @@ def test_is_special_value(): def test_abs(): - assert_equal(FloatLiteral(-4.4).__abs__(), 4.4) - assert_equal(FloatLiteral(4.4).__abs__(), 4.4) - assert_equal(FloatLiteral(0.0).__abs__(), 0.0) + assert_equal((-4.4).__abs__(), 4.4) + assert_equal((4.4).__abs__(), 4.4) + assert_equal((0.0).__abs__(), 0.0) assert_true(FloatLiteral.__abs__(nan).is_nan()) assert_false(FloatLiteral.__abs__(neg_zero).is_neg_zero()) From 02559c278d10ef5f584710bcde5771e15942be69 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 8 May 2024 16:24:37 -0700 Subject: [PATCH 0480/2019] [stdlib] Migrate coroutine.mojo Pointer -> UnsafePointer Migrating all callsites in coroutine.mojo from Pointer to UnsafePointer. MODULAR_ORIG_COMMIT_REV_ID: 212b889334309c64899e16761edd314178d75d24 --- stdlib/src/builtin/coroutine.mojo | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 127ccb2790..e568256da2 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import sizeof -from memory import Pointer +from memory import UnsafePointer # ===----------------------------------------------------------------------=== # # _suspend_async @@ -97,14 +97,14 @@ struct Coroutine[type: AnyRegType]: var _handle: AnyCoroutine @always_inline - fn _get_promise(self) -> Pointer[type]: + fn _get_promise(self) -> UnsafePointer[type]: """Return the pointer to the beginning of the memory where the async function results are stored. Returns: The coroutine promise. """ - var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`[ + var promise: UnsafePointer[Self._promise_type] = __mlir_op.`co.promise`[ _type = __mlir_type[`!kgen.pointer<`, Self._promise_type, `>`] ](self._handle) return promise.bitcast[type]() @@ -116,10 +116,10 @@ struct Coroutine[type: AnyRegType]: Returns: The value of the fulfilled promise. """ - return self._get_promise().load() + return LegacyPointer(self._get_promise().address).load() @always_inline - fn _get_ctx[ctx_type: AnyRegType](self) -> Pointer[ctx_type]: + fn _get_ctx[ctx_type: AnyRegType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. Parameters: @@ -162,7 +162,7 @@ struct Coroutine[type: AnyRegType]: @always_inline @parameter fn await_body(parent_hdl: AnyCoroutine): - self._get_ctx[_CoroutineContext]().store( + LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl } @@ -174,7 +174,7 @@ struct Coroutine[type: AnyRegType]: # Never call this method. fn _deprecated_direct_resume(self) -> type: - self._get_ctx[_CoroutineContext]().store( + LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( _CoroutineContext { _resume_fn: _coro_resume_noop_callback, _parent_hdl: self._handle, @@ -207,14 +207,14 @@ struct RaisingCoroutine[type: AnyRegType]: var _handle: AnyCoroutine @always_inline - fn _get_promise(self) -> Pointer[Self._var_type]: + fn _get_promise(self) -> UnsafePointer[Self._var_type]: """Return the pointer to the beginning of the memory where the async function results are stored. Returns: The coroutine promise. """ - var promise: Pointer[Self._promise_type] = __mlir_op.`co.promise`[ + var promise: UnsafePointer[Self._promise_type] = __mlir_op.`co.promise`[ _type = __mlir_type[`!kgen.pointer<`, Self._promise_type, `>`] ](self._handle) return promise.bitcast[Self._var_type]() @@ -226,13 +226,13 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The value of the fulfilled promise. """ - var variant = self._get_promise().load() + var variant = LegacyPointer(self._get_promise().address).load() if __mlir_op.`kgen.variant.is`[index = Int(0).value](variant): raise __mlir_op.`kgen.variant.take`[index = Int(0).value](variant) return __mlir_op.`kgen.variant.take`[index = Int(1).value](variant) @always_inline - fn _get_ctx[ctx_type: AnyRegType](self) -> Pointer[ctx_type]: + fn _get_ctx[ctx_type: AnyRegType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. Parameters: @@ -272,7 +272,7 @@ struct RaisingCoroutine[type: AnyRegType]: @always_inline @parameter fn await_body(parent_hdl: AnyCoroutine): - self._get_ctx[_CoroutineContext]().store( + LegacyPointer(self._get_ctx[_CoroutineContext]().address).store( _CoroutineContext { _resume_fn: _coro_resume_callback, _parent_hdl: parent_hdl } From febbe3f713a908fedb3d647b295cf8527e3ed689 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 8 May 2024 20:58:55 -0400 Subject: [PATCH 0481/2019] [stdlib] Return `IntLiteral` from `simdbitwidth` and `simdbytewidth` in `sys.info` This way it can be used in arbitrary precision parameter computations. MODULAR_ORIG_COMMIT_REV_ID: 462d05fc26b7c9b22deb033b8696e269c7290a09 --- stdlib/src/sys/info.mojo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 5f6b74a356..c57ec4c7a7 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -475,7 +475,7 @@ fn is_64bit[target: __mlir_type.`!kgen.target` = _current_target()]() -> Bool: @always_inline("nodebug") fn simdbitwidth[ target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the vector size (in bits) of the host system. Parameters: @@ -488,14 +488,14 @@ fn simdbitwidth[ `#kgen.param.expr : index`, + `> : !kgen.int_literal`, ] @always_inline("nodebug") fn simdbytewidth[ target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the vector size (in bytes) of the host system. Parameters: From ce8683bfaf57a59805a9654a55859fb31e6a1b64 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 8 May 2024 18:39:30 -0700 Subject: [PATCH 0482/2019] Fix links to Mojo get started guide Because it moved. MODULAR_ORIG_COMMIT_REV_ID: 9deb59c34fd4f0f9a3590975e62580885f78dbfb --- docs/changelog-released.md | 4 ++-- docs/faq.md | 10 ---------- docs/index.md | 2 +- docs/manual/basics.ipynb | 8 ++++---- docs/manual/get-started.md | 2 +- docs/roadmap.md | 2 +- 6 files changed, 9 insertions(+), 19 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index f58e79157f..faa318fa04 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -11,7 +11,7 @@ It doesn't include all internal implementation changes. ## Update Mojo If you don't have Mojo yet, see the [get started -guide](/mojo/manual/get-started/#get-the-mojo-sdk). +guide](/mojo/manual/get-started). To see your Mojo version, run this: @@ -3506,7 +3506,7 @@ All earlier releases were considered version 0.1. - You can now share `.ipynb` notebook files in Mojo Playground. Just save a file in the `shared` directory, and then right-click the file and select **Copy Sharable link**. To open a shared notebook, you must already have - [access to Mojo Playground](/mojo/manual/get-started/#develop-in-the-mojo-playground); + access to Mojo Playground; when you open a shared notebook, click **Import** at the top of the notebook to save your own copy. For more details about this feature, see the instructions inside the `help` directory, in the Mojo Playground file browser. diff --git a/docs/faq.md b/docs/faq.md index 475c7500ce..59e986cde3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -430,13 +430,3 @@ our GitHub channels where you can report issues and discuss new features. To get in touch with the Mojo team and developer community, use the resources on our [Mojo community page](/mojo/community.html). - -### Can I share Mojo code from the Mojo Playground? - -Yes! You’re welcome and encouraged to share your Mojo code any way you like. -We've added a feature in the Mojo Playground to make this easier, and you can -learn more in the Mojo Playground by opening the `help` directory in the file -browser. - -However, the [Mojo SDK is also now available](/mojo/manual/get-started/), so -you can also share `.mojo` source files and `.ipynb` notebooks to run locally! diff --git a/docs/index.md b/docs/index.md index 8f432cee2e..0cc92c0946 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ anchor-sections: false listing: - id: docs contents: - - manual/get-started/index.md + - manual/get-started.md - why-mojo.md - manual/basics.ipynb - lib.md diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index 706b69ac37..2ca082a648 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -20,9 +20,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "At this point, you should have already set up the [Mojo\n", - "SDK](/mojo/manual/get-started/) and run [\"Hello\n", - "world\"](/mojo/manual/get-started/hello-world.html). Now let's talk about how\n", + "At this point, you should have already set up the Mojo\n", + "SDK and run [\"Hello\n", + "world\"](/mojo/manual/get-started). Now let's talk about how\n", "to write Mojo code.\n", "\n", "You probably already know that Mojo is designed as a superset of Python. So if\n", @@ -135,7 +135,7 @@ ":::note\n", "\n", "You don't need a `main()` function when coding in the\n", - "[REPL](/mojo/manual/get-started/hello-world.html#run-code-in-the-repl) or in a\n", + "[REPL](/mojo/manual/get-started#run-code-in-the-repl) or in a\n", "[Jupyter\n", "notebook](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme).\n", "\n", diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index 62aaeaadfc..edbb0aca63 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -88,7 +88,7 @@ Now let's write the code in a Mojo source file and run it with the If this didn't work for you, double-check your code looks exactly like the code in step 1, and make sure you correctly [installed -Mojo](/mojo/manual/get-started/#install-mojo). +MAX](/max/install) (it includes Mojo). ## 4. Build an executable binary diff --git a/docs/roadmap.md b/docs/roadmap.md index 925389dffd..a79069aa4e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -85,7 +85,7 @@ structured language evolution features. ## Mojo SDK known issues -The [Mojo SDK](/mojo/manual/get-started/) is still in early development +The Mojo SDK is still in early development and currently only available for Ubuntu Linux and macOS (Apple silicon) systems. Here are some of the notable issues that we plan to fix: From e099f39fdd651f1211bfec71e9af2d17378917fb Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 8 May 2024 21:56:18 -0400 Subject: [PATCH 0483/2019] [stdlib] Make the `IntLiteral.__str__` implementation use explicit conversions So that it doesn't cause problems when the implicit `String` conversion from `Stringable` is removed. See https://github.com/modularml/mojo/pull/2564 for more context. MODULAR_ORIG_COMMIT_REV_ID: 12e7ad4a474d9b180135ccc459b5207aef75e986 --- stdlib/src/builtin/int_literal.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 7a571d31d8..b04ddaf395 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -84,7 +84,7 @@ struct IntLiteral( Returns: The value as a string. """ - return self + return str(Int(self)) @always_inline("nodebug") fn __as_mlir_index(self) -> __mlir_type.index: From 90114b514733abb3ee7db1b75e8e19967662b1e4 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 8 May 2024 18:58:56 -0700 Subject: [PATCH 0484/2019] [mojo-examples] mandelbrot migrate from `Tensor` to custom `Matrix` - Custom Matrix for speedup - Deduplicate code - Remove non-functional custom `Runtime` - Other cleanup like removing unused imports - Save time by reusing benchmark results from previous cells MODULAR_ORIG_COMMIT_REV_ID: 495e3c6a8b072cbe3e4b37e2c9012bcb57f55ae5 --- examples/mandelbrot.mojo | 81 +++++---- examples/notebooks/Mandelbrot.ipynb | 247 ++++++++++------------------ 2 files changed, 128 insertions(+), 200 deletions(-) diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index ae936cb462..90729625cb 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -13,24 +13,19 @@ # RUN: %mojo %s | FileCheck %s -from math import iota -from sys import num_logical_cores - import benchmark +from math import iota +from sys import num_physical_cores from algorithm import parallelize, vectorize from complex import ComplexFloat64, ComplexSIMD -from python import Python -from runtime.llcl import Runtime -from tensor import Tensor - -from utils import Index -alias float_type = DType.float64 -alias int_type = DType.int64 +alias float_type = DType.float32 +alias int_type = DType.int32 alias simd_width = 2 * simdwidthof[float_type]() +alias unit = benchmark.Unit.ms -alias width = 960 -alias height = 960 +alias cols = 960 +alias rows = 960 alias MAX_ITERS = 200 alias min_x = -2.0 @@ -39,6 +34,16 @@ alias min_y = -1.5 alias max_y = 1.5 +struct Matrix[type: DType, rows: Int, cols: Int]: + var data: DTypePointer[type] + + fn __init__(inout self): + self.data = DTypePointer[type].alloc(rows * cols) + + fn store[nelts: Int](self, row: Int, col: Int, val: SIMD[type, nelts]): + self.data.store[width=nelts](row * cols + col, val) + + fn mandelbrot_kernel_SIMD[ simd_width: Int ](c: ComplexSIMD[float_type, simd_width]) -> SIMD[int_type, simd_width]: @@ -49,9 +54,9 @@ fn mandelbrot_kernel_SIMD[ var y = SIMD[float_type, simd_width](0) var y2 = SIMD[float_type, simd_width](0) var iters = SIMD[int_type, simd_width](0) - var t: SIMD[DType.bool, simd_width] = True - for i in range(MAX_ITERS): + + for _ in range(MAX_ITERS): if not t.reduce_or(): break y2 = y * y @@ -63,49 +68,41 @@ fn mandelbrot_kernel_SIMD[ fn main() raises: - var t = Tensor[int_type](height, width) + var matrix = Matrix[int_type, rows, cols]() @parameter fn worker(row: Int): - var scale_x = (max_x - min_x) / width - var scale_y = (max_y - min_y) / height + var scale_x = (max_x - min_x) / cols + var scale_y = (max_y - min_y) / rows - @__copy_capture(scale_x, scale_y) @parameter fn compute_vector[simd_width: Int](col: Int): """Each time we operate on a `simd_width` vector of pixels.""" var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x var cy = min_y + row * scale_y var c = ComplexSIMD[float_type, simd_width](cx, cy) - t.unsafe_ptr().store[width=simd_width]( - row * width + col, mandelbrot_kernel_SIMD[simd_width](c) - ) + matrix.store(row, col, mandelbrot_kernel_SIMD(c)) - # Vectorize the call to compute_vector where call gets a chunk of pixels. - vectorize[compute_vector, simd_width, size=width]() + # Vectorize the call to compute_vector with a chunk of pixels. + vectorize[compute_vector, simd_width, size=cols]() @parameter - fn bench[simd_width: Int](): - for row in range(height): + fn bench(): + for row in range(rows): worker(row) - var vectorized = benchmark.run[bench[simd_width]]( - max_runtime_secs=0.5 - ).mean() - print("Number of threads:", num_logical_cores()) - print("Vectorized:", vectorized, "s") + @parameter + fn bench_parallel(): + parallelize[worker](rows, rows) - with Runtime(num_logical_cores()) as rt: - # Parallelized - @parameter - fn bench_parallel[simd_width: Int](): - parallelize[worker](height, height) + print("Number of physical cores:", num_physical_cores()) + + var vectorized = benchmark.run[bench]().mean(unit) + print("Vectorized:", vectorized, unit) + var parallelized = benchmark.run[bench_parallel]().mean(unit) + print("Parallelized:", parallelized, unit) - var parallelized = benchmark.run[bench_parallel[simd_width]]( - max_runtime_secs=0.5 - ).mean() - print("Parallelized:", parallelized, "s") - # CHECK: Parallel speedup - print("Parallel speedup:", vectorized / parallelized) + # CHECK: Parallel speedup + print("Parallel speedup:", vectorized / parallelized) - _ = t # Make sure tensor isn't destroyed before benchmark is finished + matrix.data.free() diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 1789701aec..25190ce265 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -48,16 +48,16 @@ "source": [ "#|code-fold: true\n", "import benchmark\n", - "from complex import ComplexSIMD, ComplexFloat64\n", "from math import iota\n", - "from python import Python\n", "from sys import num_physical_cores\n", "from algorithm import parallelize, vectorize\n", - "from tensor import Tensor\n", - "from utils import Index\n", + "from complex import ComplexFloat64, ComplexSIMD\n", + "from python import Python\n", "\n", - "alias float_type = DType.float64\n", - "alias simd_width = 2 * simdwidthof[float_type]()" + "alias float_type = DType.float32\n", + "alias int_type = DType.int32\n", + "alias simd_width = 2 * simdwidthof[float_type]()\n", + "alias unit = benchmark.Unit.ms" ] }, { @@ -83,6 +83,33 @@ "alias max_y = 1.5" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we define a simple `Matrix` struct:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "@value\n", + "struct Matrix[type: DType, rows: Int, cols: Int]:\n", + " var data: DTypePointer[type]\n", + "\n", + " fn __init__(inout self):\n", + " self.data = DTypePointer[type].alloc(rows * cols)\n", + "\n", + " fn __getitem__(self, row: Int, col: Int) -> Scalar[type]:\n", + " return self.data.load(row * cols + col)\n", + "\n", + " fn store[width: Int = 1](self, row: Int, col: Int, val: SIMD[type, width]):\n", + " self.data.store[width=width](row * cols + col, val)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -94,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -108,9 +135,9 @@ " return MAX_ITERS\n", "\n", "\n", - "def compute_mandelbrot() -> Tensor[float_type]:\n", + "def compute_mandelbrot() -> Matrix[float_type, height, width]:\n", " # create a matrix. Each element of the matrix corresponds to a pixel\n", - " t = Tensor[float_type](height, width)\n", + " matrix = Matrix[float_type, height, width]()\n", "\n", " dx = (max_x - min_x) / width\n", " dy = (max_y - min_y) / height\n", @@ -119,10 +146,10 @@ " for row in range(height):\n", " x = min_x\n", " for col in range(width):\n", - " t[Index(row, col)] = mandelbrot_kernel(ComplexFloat64(x, y))\n", + " matrix.store(row, col, mandelbrot_kernel(ComplexFloat64(x, y)))\n", " x += dx\n", " y += dy\n", - " return t" + " return matrix" ] }, { @@ -136,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -155,7 +182,7 @@ "def install_if_missing(name: str):\n", " if find_spec(name):\n", " return\n", - "\n", + " print(\"missing\", name)\n", " print(f\"{name} not found, installing...\")\n", " try:\n", " if shutil.which('python3'): python = \"python3\"\n", @@ -171,12 +198,12 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { - "image/png": "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" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -190,7 +217,7 @@ } ], "source": [ - "def show_plot(tensor: Tensor[float_type]):\n", + "def show_plot[type: DType](matrix: Matrix[type, height, width]):\n", " alias scale = 10\n", " alias dpi = 64\n", "\n", @@ -202,7 +229,7 @@ "\n", " for row in range(height):\n", " for col in range(width):\n", - " numpy_array.itemset((col, row), tensor[col, row])\n", + " numpy_array.itemset((row, col), matrix[row, col])\n", "\n", " fig = plt.figure(1, [scale, scale * height // width], dpi)\n", " ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], False, 1)\n", @@ -228,26 +255,26 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "fn mandelbrot_kernel_SIMD[\n", " simd_width: Int\n", - "](c: ComplexSIMD[float_type, simd_width]) -> SIMD[float_type, simd_width]:\n", + "](c: ComplexSIMD[float_type, simd_width]) -> SIMD[int_type, simd_width]:\n", " \"\"\"A vectorized implementation of the inner mandelbrot computation.\"\"\"\n", " var cx = c.re\n", " var cy = c.im\n", " var x = SIMD[float_type, simd_width](0)\n", " var y = SIMD[float_type, simd_width](0)\n", " var y2 = SIMD[float_type, simd_width](0)\n", - " var iters = SIMD[float_type, simd_width](0)\n", + " var iters = SIMD[int_type, simd_width](0)\n", "\n", " var t: SIMD[DType.bool, simd_width] = True\n", - " for i in range(MAX_ITERS):\n", + " for _ in range(MAX_ITERS):\n", " if not t.reduce_or():\n", " break\n", - " y2 = y*y\n", + " y2 = y * y\n", " y = x.fma(y + y, cy)\n", " t = x.fma(x, y2) <= 4\n", " x = x.fma(x, cx - y2)\n", @@ -259,24 +286,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The above function is parameterized on the `simd_width` and processes simd_width pixels. It only escapes once all pixels within the vector lane are done. We can use the same iteration loop as above, but this time we vectorize within each row instead. We use the `vectorize` generator to make this a simple function call." + "The above function is parameterized on the `simd_width` and processes simd_width pixels. It only escapes once all pixels within the vector lane are done. We can use the same iteration loop as above, but this time we vectorize within each row instead. We use the `vectorize` generator to make this a simple function call. The benchmark can run in parallel or just vectorized." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Vectorized : 12.177345000000001 ms\n" - ] - }, { "data": { - "image/png": "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" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -285,50 +305,52 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n" + "\n", + "Vectorized: 13.224016129032258 ms\n" ] } ], "source": [ - "fn vectorized():\n", - " var t = Tensor[float_type](height, width)\n", + "fn run_mandelbrot(parallel: Bool) raises -> Float64:\n", + " var matrix = Matrix[int_type, height, width]()\n", "\n", " @parameter\n", " fn worker(row: Int):\n", " var scale_x = (max_x - min_x) / width\n", " var scale_y = (max_y - min_y) / height\n", "\n", - " @__copy_capture(scale_x, scale_y)\n", " @parameter\n", " fn compute_vector[simd_width: Int](col: Int):\n", " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", - " t.unsafe_ptr().store(\n", - " row * width + col, mandelbrot_kernel_SIMD[simd_width](c)\n", - " )\n", + " matrix.store(row, col, mandelbrot_kernel_SIMD[simd_width](c))\n", "\n", " # Vectorize the call to compute_vector where call gets a chunk of pixels.\n", " vectorize[compute_vector, simd_width](width)\n", "\n", " @parameter\n", - " fn bench[simd_width: Int]():\n", + " fn bench():\n", " for row in range(height):\n", " worker(row)\n", "\n", - " var vectorized = benchmark.run[bench[simd_width]](\n", - " max_runtime_secs=0.5\n", - " ).mean(benchmark.Unit.ms)\n", + " @parameter\n", + " fn bench_parallel():\n", + " parallelize[worker](height, height)\n", "\n", - " print(\"Vectorized\", \":\", vectorized, \"ms\")\n", + " var time: Float64 = 0\n", + " if parallel:\n", + " time = benchmark.run[bench_parallel](max_runtime_secs=0.5).mean(unit)\n", + " else:\n", + " time = benchmark.run[bench](max_runtime_secs=0.5).mean(unit)\n", "\n", - " try:\n", - " _ = show_plot(t)\n", - " except e:\n", - " print(\"failed to show plot:\", e)\n", + " show_plot(matrix)\n", + " matrix.data.free()\n", + " return time\n", "\n", - "vectorized()" + "vectorized = run_mandelbrot(parallel=False)\n", + "print(\"Vectorized:\", vectorized, unit)" ] }, { @@ -336,24 +358,17 @@ "metadata": {}, "source": [ "## Parallelizing Mandelbrot\n", - "While the vectorized implementation above is efficient, we can get better performance by parallelizing on the cols. This again is simple in Mojo using the `parallelize` higher order function. Only the function that performs the invocation needs to change." + "While the vectorized implementation above is efficient, we can get better performance by parallelizing on the cols. This again is simple in Mojo using the `parallelize` higher order function:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Parallelized: 1.4245639999999999 ms\n" - ] - }, { "data": { - "image/png": "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" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -362,48 +377,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n" + "\n", + "Parallelized: 2.1289554621848739 ms\n" ] } ], "source": [ - "fn parallelized():\n", - " var t = Tensor[float_type](height, width)\n", - "\n", - " @parameter\n", - " fn worker(row: Int):\n", - " var scale_x = (max_x - min_x) / width\n", - " var scale_y = (max_y - min_y) / height\n", - "\n", - " @__copy_capture(scale_x, scale_y)\n", - " @parameter\n", - " fn compute_vector[simd_width: Int](col: Int):\n", - " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", - " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", - " var cy = min_y + row * scale_y\n", - " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", - " t.unsafe_ptr().store(row * width + col, mandelbrot_kernel_SIMD[simd_width](c))\n", - "\n", - " # Vectorize the call to compute_vector where call gets a chunk of pixels.\n", - " vectorize[compute_vector, simd_width](width)\n", - "\n", - "\n", - " @parameter\n", - " fn bench_parallel[simd_width: Int]():\n", - " parallelize[worker](height, height)\n", - "\n", - " var parallelized = benchmark.run[bench_parallel[simd_width]](\n", - " max_runtime_secs=0.5\n", - " ).mean(benchmark.Unit.ms)\n", - "\n", - " print(\"Parallelized:\", parallelized, benchmark.Unit.ms)\n", - "\n", - " try:\n", - " _ = show_plot(t)\n", - " except e:\n", - " print(\"failed to show plot:\", e)\n", - "\n", - "parallelized()" + "parallelized = run_mandelbrot(parallel=True)\n", + "print(\"Parallelized:\", parallelized, unit)" ] }, { @@ -412,60 +393,7 @@ "source": [ "## Benchmarking\n", "\n", - "In this section we increase the size to 4096x4096 and run 1000 iterations for a larger test to stress the CPU " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "fn compare():\n", - " var t = Tensor[float_type](height, width)\n", - "\n", - " @parameter\n", - " fn worker(row: Int):\n", - " var scale_x = (max_x - min_x) / width\n", - " var scale_y = (max_y - min_y) / height\n", - "\n", - " @__copy_capture(scale_x, scale_y)\n", - " @parameter\n", - " fn compute_vector[simd_width: Int](col: Int):\n", - " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", - " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", - " var cy = min_y + row * scale_y\n", - " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", - " t.unsafe_ptr().store(\n", - " row * width + col, mandelbrot_kernel_SIMD[simd_width](c)\n", - " )\n", - "\n", - " # Vectorize the call to compute_vector where call gets a chunk of pixels.\n", - " vectorize[compute_vector, simd_width](width)\n", - "\n", - " @parameter\n", - " fn bench[simd_width: Int]():\n", - " for row in range(height):\n", - " worker(row)\n", - "\n", - " var vectorized = benchmark.run[bench[simd_width]](\n", - " max_runtime_secs=0.5\n", - " ).mean(benchmark.Unit.ms)\n", - " print(\"Number of threads:\", num_physical_cores())\n", - " print(\"Vectorized:\", vectorized, \"ms\")\n", - "\n", - " # Parallelized\n", - " @parameter\n", - " fn bench_parallel[simd_width: Int]():\n", - " parallelize[worker](height, height)\n", - "\n", - " var parallelized = benchmark.run[bench_parallel[simd_width]](\n", - " max_runtime_secs=0.5\n", - " ).mean(benchmark.Unit.ms)\n", - " print(\"Parallelized:\", parallelized, \"ms\")\n", - " print(\"Parallel speedup:\", vectorized / parallelized)\n", - "\n", - " _ = t # Make sure tensor isn't destroyed before benchmark is finished" + "In this section we compare the vectorized speed to the parallelized speed " ] }, { @@ -477,24 +405,27 @@ "name": "stdout", "output_type": "stream", "text": [ - "Number of threads: 16\n", - "Vectorized: 12.171849 ms\n", - "Parallelized: 1.3043979999999999 ms\n", - "Parallel speedup: 9.3313919524562294\n" + "Number of physical cores: 12\n", + "Vectorized: 13.224016129032258 ms\n", + "Parallelized: 2.1289554621848739 ms\n", + "Parallel speedup: 6.2115043569116777\n" ] } ], "source": [ + "print(\"Number of physical cores:\", num_physical_cores())\n", + "print(\"Vectorized:\", vectorized, \"ms\")\n", + "print(\"Parallelized:\", parallelized, \"ms\")\n", "#| CHECK: speedup\n", - "compare()" + "print(\"Parallel speedup:\", vectorized / parallelized)" ] } ], "metadata": { "kernelspec": { - "display_name": "Mojo", + "display_name": "Mojo (nightly)", "language": "mojo", - "name": "mojo-jupyter-kernel" + "name": "mojo-nightly-jupyter-kernel" }, "language_info": { "codemirror_mode": { From c814859abf7d68ea709ede00ec2213e99d128cfe Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 8 May 2024 18:59:11 -0700 Subject: [PATCH 0485/2019] [mojo-examples] reduce.mojo: Migrate from `Tensor` to `Buffer` Move to `Buffer` instead of `Tensor` to avoid conversion MODULAR_ORIG_COMMIT_REV_ID: 5a69d2eebf4c7b56ba01d09c8ee6f4bdfb43f3c1 --- examples/reduce.mojo | 50 +++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 91a54580e7..69a5d4c63c 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -17,13 +17,11 @@ # Reductions and scans are common algorithm patterns in parallel computing. from time import now - from algorithm import sum from benchmark import Unit, benchmark, keep from buffer import Buffer -from tensor import Tensor from python import Python -from tensor import rand +from random import rand # Change these numbers to reduce on different sizes alias size_small: Int = 1 << 21 @@ -31,24 +29,24 @@ alias size_large: Int = 1 << 27 # Datatype for Tensor/Array alias type = DType.float32 +alias scalar = Scalar[DType.float32] # Use the https://en.wikipedia.org/wiki/Kahan_summation_algorithm # Simple summation of the array elements -fn naive_reduce_sum[size: Int](array: Tensor[type]) -> Float32: - var A = array - var my_sum = array[0] - var c: Float32 = 0.0 - for i in range(array.dim(0)): - var y = array[i] - c +fn naive_reduce_sum[size: Int](buffer: Buffer[type, size]) -> scalar: + var my_sum: scalar = 0 + var c: scalar = 0 + for i in range(buffer.size): + var y = buffer[i] - c var t = my_sum + y c = (t - my_sum) - y my_sum = t return my_sum -fn stdlib_reduce_sum[size: Int](array: Tensor[type]) -> Float32: - var my_sum = sum(array._to_buffer()) +fn stdlib_reduce_sum[size: Int](array: Buffer[type, size]) -> scalar: + var my_sum = sum(array) return my_sum @@ -62,13 +60,13 @@ fn pretty_print(name: StringLiteral, elements: Int, time: Float64) raises: fn bench[ - func: fn[size: Int] (array: Tensor[type]) -> Float32, + func: fn[size: Int] (buffer: Buffer[type, size]) -> scalar, size: Int, name: StringLiteral, -](array: Tensor[type]) raises: +](buffer: Buffer[type, size]) raises: @parameter fn runner(): - var result = func[size](array) + var result = func[size](buffer) keep(result) var ms = benchmark.run[runner](max_runtime_secs=0.5).mean(Unit.ms) @@ -80,13 +78,23 @@ fn main() raises: "Sum all values in a small array and large array\n" "Shows algorithm.sum from stdlib with much better performance\n" ) - # Create two 1-dimensional tensors i.e. arrays - var small_array = rand[type](size_small) - var large_array = rand[type](size_large) + # Allocate and randomize data, then create two buffers + var ptr_small = DTypePointer[type].alloc(size_small) + var ptr_large = DTypePointer[type].alloc(size_large) + + rand(ptr_small, size_small) + rand(ptr_large, size_large) - bench[naive_reduce_sum, size_small, "naive"](small_array) - bench[naive_reduce_sum, size_large, "naive"](large_array) + var buffer_small = Buffer[type, size_small](ptr_small) + var buffer_large = Buffer[type, size_large](ptr_large) + print(naive_reduce_sum(buffer_small)) + print(stdlib_reduce_sum(buffer_small)) - bench[stdlib_reduce_sum, size_small, "stdlib"](small_array) + bench[naive_reduce_sum, size_small, "naive"](buffer_small) + bench[naive_reduce_sum, size_large, "naive"](buffer_large) + bench[stdlib_reduce_sum, size_small, "stdlib"](buffer_small) # CHECK: stdlib elements - bench[stdlib_reduce_sum, size_large, "stdlib"](large_array) + bench[stdlib_reduce_sum, size_large, "stdlib"](buffer_large) + + ptr_small.free() + ptr_large.free() From d59a04eba98b6e72a79590d2836db519fc55ea72 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 8 May 2024 21:01:54 -0700 Subject: [PATCH 0486/2019] [stdlib] Change `Bool` to be backed by an `i1` (NFC) This PR changes the `Bool` builtin type to be backed by an `i1` instead of a `!pop.scalar`. The main benefit of this is that it makes the `__mlir_i1__` implementation trivial, which significantly reduces IR bloat and the amount of back-and-forth casting to `!pop.scalar`. MODULAR_ORIG_COMMIT_REV_ID: 4ab8172a81c6f185059b5573d5518f02db2466fc --- stdlib/src/builtin/bool.mojo | 50 +++++++++++++++++++++++------------- stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/utils/index.mojo | 2 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 70cb1c1e9f..3d152cd1a1 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -61,7 +61,7 @@ struct Bool( ): """The primitive Bool scalar value used in Mojo.""" - var value: __mlir_type.`!pop.scalar` + var value: __mlir_type.i1 """The underlying storage of the boolean value.""" @always_inline("nodebug") @@ -74,9 +74,19 @@ struct Bool( Returns: The constructed Bool value. """ - return __mlir_op.`pop.cast_from_builtin`[ - _type = __mlir_type.`!pop.scalar` - ](value) + return Self {value: value} + + @always_inline("nodebug") + fn __init__(value: __mlir_type.`!pop.scalar`) -> Bool: + """Construct a Bool value given a !pop.scalar value. + + Args: + value: The initial value. + + Returns: + The constructed Bool value. + """ + return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.i1](value) @always_inline("nodebug") fn __init__[boolable: Boolable](value: boolable) -> Bool: @@ -114,9 +124,13 @@ struct Bool( Returns: The underlying value for the Bool. """ - return __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.i1]( - self.value - ) + return self.value + + @always_inline("nodebug") + fn _as_scalar_bool(self) -> __mlir_type.`!pop.scalar`: + return __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type.`!pop.scalar` + ](self.value) fn __str__(self) -> String: """Get the bool as a string. @@ -133,11 +147,7 @@ struct Bool( Returns: 1 if the Bool is True, 0 otherwise. """ - return Int( - __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( - self.value - ) - ) + return __mlir_op.`pop.select`[_type=Int](self.value, Int(1), Int(0)) @always_inline("nodebug") fn __eq__(self, rhs: Bool) -> Bool: @@ -153,7 +163,7 @@ struct Bool( True if the two values match and False otherwise. """ return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - self.value, rhs.value + self._as_scalar_bool(), rhs._as_scalar_bool() ) @always_inline("nodebug") @@ -171,7 +181,7 @@ struct Bool( False if the two values do match and True otherwise. """ return __mlir_op.`pop.cmp`[pred = __mlir_attr.`#pop`]( - self.value, rhs.value + self._as_scalar_bool(), rhs._as_scalar_bool() ) # ===-------------------------------------------------------------------===# @@ -189,7 +199,7 @@ struct Bool( _type = __mlir_type.`!pop.scalar`, value = __mlir_attr.`#pop.simd : !pop.scalar`, ]() - return __mlir_op.`pop.xor`(self.value, true) + return __mlir_op.`pop.xor`(self._as_scalar_bool(), true) @always_inline("nodebug") fn __and__(self, rhs: Bool) -> Bool: @@ -204,7 +214,9 @@ struct Bool( Returns: `self & rhs`. """ - return __mlir_op.`pop.and`(self.value, rhs.value) + return __mlir_op.`pop.and`( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) @always_inline("nodebug") fn __iand__(inout self, rhs: Bool): @@ -240,7 +252,7 @@ struct Bool( Returns: `self | rhs`. """ - return __mlir_op.`pop.or`(self.value, rhs.value) + return __mlir_op.`pop.or`(self._as_scalar_bool(), rhs._as_scalar_bool()) @always_inline("nodebug") fn __ior__(inout self, rhs: Bool): @@ -276,7 +288,9 @@ struct Bool( Returns: `self ^ rhs`. """ - return __mlir_op.`pop.xor`(self.value, rhs.value) + return __mlir_op.`pop.xor`( + self._as_scalar_bool(), rhs._as_scalar_bool() + ) @always_inline("nodebug") fn __ixor__(inout self, rhs: Bool): diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 120106ec32..6e2063356a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -252,7 +252,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var casted = __mlir_op.`pop.cast`[ _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] - ](value.value) + ](value._as_scalar_bool()) self.value = __mlir_op.`pop.simd.splat`[ _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] ](casted) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 5f8abe86d5..a230c104dd 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -130,7 +130,7 @@ fn _int_tuple_compare[ fn do_compare[idx: Int](): var a_elem: Int = a.__getitem__[idx]() var b_elem: Int = b.__getitem__[idx]() - c.__setitem__[idx](comp_fn(a_elem, b_elem).value) + c.__setitem__[idx](comp_fn(a_elem, b_elem)._as_scalar_bool()) unroll[do_compare, size]() From ec5a9fe005dfec06b1a99f0044da6383ae216313 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Wed, 8 May 2024 23:39:04 -0500 Subject: [PATCH 0487/2019] [External] [docs] `entr` and pre commit issue (#39652) [External] [docs] `entr` and pre commit issue Warn about the use of `entr` and `pre-commit`. ORIGINAL_AUTHOR=artemiogr97 <57588855+artemiogr97@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2531 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> Closes modularml/mojo#2531 MODULAR_ORIG_COMMIT_REV_ID: b8239da7fd2e1b1bb48d5b7848ee246f73eeca8e --- stdlib/docs/development.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 3db28ef576..267274dec7 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -263,6 +263,10 @@ ls **/*.mojo | entr sh -c "./scripts/build-stdlib.sh && mojo main.mojo" Now, every time you save a Mojo file, it packages the standard library and runs `main.mojo`. +**Note**: you should stop `entr` while doing commits, otherwise you could have +some issues, this is because some pre-commit hooks use mojo scripts +and will try to load the standard library while it is being compiled by `entr`. + ### Running tests If you haven't already, follow the steps at: From f5d7cd0028b4733ea22a27ac5b38068262727fb6 Mon Sep 17 00:00:00 2001 From: Peyman Barazandeh Date: Thu, 9 May 2024 00:01:03 -0500 Subject: [PATCH 0488/2019] [External] [stdlib] Modify SIMD's tests to directly invoke __rmod__ (#39641) [External] [stdlib] Modify SIMD's tests to directly invoke __rmod__ The unit tests for the SIMD's ` __rmod__` have been modified to directly invoke the dunder function. This change ensures that the tests invoke the magic function, and prevents any interference from conversion rules. Additionally, a new test has been introduced for a SIMD of floating-point type, and all `__rmod__` tests have been consolidated into their own test function for better organization. Co-authored-by: Peyman Barazandeh Closes modularml/mojo#2588 MODULAR_ORIG_COMMIT_REV_ID: 083adad7cd9cc82d24bd1813ecb2083ce780d53d --- stdlib/test/builtin/test_simd.mojo | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index d424d813c2..90af9a000f 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -247,10 +247,6 @@ def test_mod(): assert_equal(UInt32(99) % UInt32(1), 0) assert_equal(UInt32(99) % UInt32(3), 0) - assert_equal(Int(4) % Int32(3), 1) - assert_equal( - Int(78) % SIMD[DType.int32, 2](78, 78), SIMD[DType.int32, 2](0, 0) - ) assert_equal( SIMD[DType.int32, 2](7, 7) % Int(4), SIMD[DType.int32, 2](3, 3) ) @@ -314,6 +310,19 @@ def test_mod(): ) +def test_rmod(): + assert_equal(Int32(3).__rmod__(Int(4)), 1) + + alias I = SIMD[DType.int32, 2] + var i = I(78, 78) + assert_equal(i.__rmod__(Int(78)), I(0, 0)) + + alias F = SIMD[DType.float32, 4] + var f = F(3, -4, 1, 5) + assert_equal(f.__rmod__(3), F(0, -1, 0, 3)) + assert_equal(f.__rmod__(Float32(3)), F(0, -1, 0, 3)) + + def test_rotate(): alias simd_width = 4 alias type = DType.uint32 @@ -889,6 +898,7 @@ def main(): test_floordiv() test_rfloordiv() test_mod() + test_rmod() test_rotate() test_shift() test_shuffle() From 09dfe71ca5ea145b2d2bcaba9d5fc8674809cc5e Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Thu, 9 May 2024 00:09:05 -0500 Subject: [PATCH 0489/2019] [External] [stdlib] Add or operator support to Dict (#39653) [External] [stdlib] Add or operator support to Dict Add `__or__` and `__ior__` functions to `Dict. Fixes https://github.com/modularml/mojo/issues/2545 Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2551 MODULAR_ORIG_COMMIT_REV_ID: 52270a9c37f9018e03c83e23dfbee2578861062f --- stdlib/src/collections/dict.mojo | 21 +++++++++ stdlib/test/collections/test_dict.mojo | 61 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 61bcc5a186..85d9231076 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -721,6 +721,27 @@ struct Dict[K: KeyElement, V: CollectionElement]( for entry in other.items(): self[entry[].key] = entry[].value + fn __or__(self, other: Self) -> Self: + """Merge self with other and return the result as a new dict. + + Args: + other: The dictionary to merge with. + + Returns: + The result of the merge. + """ + var result = Dict(self) + result.update(other) + return result^ + + fn __ior__(inout self, other: Self): + """Merge self with other in place. + + Args: + other: The dictionary to merge with. + """ + self.update(other) + @staticmethod @always_inline fn _new_entries(reserved: Int) -> List[Optional[DictEntry[K, V]]]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 39ee4ef806..a7e4e8b4f7 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -260,6 +260,66 @@ def test_dict_update_empty_origin(): assert_equal(orig["c"], 4) +def test_dict_or(): + var orig = Dict[String, Int]() + var new = Dict[String, Int]() + + new["b"] = 3 + new["c"] = 4 + orig["d"] = 5 + orig["b"] = 8 + + var out = orig | new + + assert_equal(out["b"], 3) + assert_equal(out["c"], 4) + assert_equal(out["d"], 5) + + orig |= new + + assert_equal(orig["b"], 3) + assert_equal(orig["c"], 4) + assert_equal(orig["d"], 5) + + orig = Dict[String, Int]() + new = Dict[String, Int]() + new["b"] = 3 + new["c"] = 4 + + orig |= new + + assert_equal(orig["b"], 3) + assert_equal(orig["c"], 4) + + orig = Dict[String, Int]() + orig["a"] = 1 + orig["b"] = 2 + + new = Dict[String, Int]() + + orig = orig | new + + assert_equal(orig["a"], 1) + assert_equal(orig["b"], 2) + assert_equal(len(orig), 2) + + orig = Dict[String, Int]() + new = Dict[String, Int]() + orig["a"] = 1 + orig["b"] = 2 + new["c"] = 3 + new["d"] = 4 + orig |= new + assert_equal(orig["a"], 1) + assert_equal(orig["b"], 2) + assert_equal(orig["c"], 3) + assert_equal(orig["d"], 4) + + orig = Dict[String, Int]() + new = Dict[String, Int]() + assert_equal(len(orig | new), 0) + + def test_dict_update_empty_new(): var orig = Dict[String, Int]() orig["a"] = 1 @@ -343,6 +403,7 @@ def test_dict(): test["test_dict_update_empty_origin", test_dict_update_empty_origin]() test["test_dict_update_empty_new", test_dict_update_empty_new]() test["test_mojo_issue_1729", test_mojo_issue_1729]() + test["test dict or", test_dict_or]() def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): From 4fd898b16f9aef911886d855f2f4d3aa12bf605d Mon Sep 17 00:00:00 2001 From: Helehex Date: Thu, 9 May 2024 10:56:07 -0500 Subject: [PATCH 0490/2019] [External] [stdlib] handle escape characters with `repr()` (#39457) [External] [stdlib] handle escape characters with `repr()` guarantees a printable representation of the string when using `repr()` Closes modularml/mojo#2493 MODULAR_ORIG_COMMIT_REV_ID: 57bdf9ec42cfcfa0b976b481019a70f5e6f34d2e --- stdlib/src/builtin/hex.mojo | 5 +- stdlib/src/builtin/string.mojo | 108 +++++++++++++++++-- stdlib/src/builtin/string_literal.mojo | 11 ++ stdlib/test/builtin/test_string.mojo | 26 +++-- stdlib/test/builtin/test_string_literal.mojo | 21 ++++ 5 files changed, 156 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 35fa9ec3ca..c8033b7269 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -23,7 +23,7 @@ alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @always_inline -fn hex[T: Intable](value: T) -> String: +fn hex[T: Intable](value: T, prefix: StringLiteral = "0x") -> String: """Returns the hex string representation of the given integer. The hexadecimal representation is a base-16 encoding of the integer value. @@ -36,13 +36,14 @@ fn hex[T: Intable](value: T) -> String: Args: value: The integer value to format. + prefix: The prefix of the formatted int. Returns: A string containing the hex representation of the given integer. """ try: - return _format_int(int(value), 16, prefix="0x") + return _format_int(int(value), 16, prefix=prefix) except e: # This should not be reachable as _format_int only throws if we pass # incompatible radix and custom digit chars, which we aren't doing diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index e8a0087b70..a729e78719 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -107,10 +107,7 @@ fn chr(c: Int) -> String: # 4: 00000000 000aaabb bbbbcccc ccdddddd -> 11110aaa 10bbbbbb 10cccccc 10dddddd a >> 18 | 0b11110000, b >> 12 | 0b10000000, c >> 6 | 0b10000000, d | 0b10000000 if (c >> 7) == 0: # This is 1 byte ASCII char - var p = DTypePointer[DType.int8].alloc(2) - p.store(c) - p.store(1, 0) - return String(p, 2) + return _chr_ascii(c) @always_inline fn _utf8_len(val: Int) -> Int: @@ -135,6 +132,71 @@ fn chr(c: Int) -> String: return String(p.bitcast[DType.int8](), num_bytes + 1) +# ===----------------------------------------------------------------------===# +# ascii +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn _chr_ascii(c: Int8) -> String: + """Returns a string based on the given ASCII code point. + + Args: + c: An integer that represents a code point. + + Returns: + A string containing a single character based on the given code point. + """ + return String(String._buffer_type(c, 0)) + + +@always_inline("nodebug") +fn _repr_ascii(c: Int8) -> String: + """Returns a printable representation of the given ASCII code point. + + Args: + c: An integer that represents a code point. + + Returns: + A string containing a representation of the given code point. + """ + alias ord_tab = ord("\t") + alias ord_new_line = ord("\n") + alias ord_carriage_return = ord("\r") + alias ord_back_slash = ord("\\") + + if c == ord_back_slash: + return r"\\" + elif isprintable(c): + return _chr_ascii(c) + elif c == ord_tab: + return r"\t" + elif c == ord_new_line: + return r"\n" + elif c == ord_carriage_return: + return r"\r" + else: + var uc = c.cast[DType.uint8]() + if uc < 16: + return hex(uc, r"\x0") + else: + return hex(uc, r"\x") + + +# TODO: This is currently the same as repr, should change with unicode strings +@always_inline("nodebug") +fn ascii(value: String) -> String: + """Get the ASCII representation of the object. + + Args: + value: The object to get the ASCII representation of. + + Returns: + A string containing the ASCII representation of the object. + """ + return value.__repr__() + + # ===----------------------------------------------------------------------===# # strtol # ===----------------------------------------------------------------------===# @@ -420,12 +482,32 @@ fn isspace(c: Int8) -> Bool: return c == ord_space or ord_tab <= int(c) <= ord_carriage_return +# ===----------------------------------------------------------------------===# +# isprintable +# ===----------------------------------------------------------------------===# + + +fn isprintable(c: Int8) -> Bool: + """Determines whether the given character is a printable character. + + Args: + c: The character to check. + + Returns: + True if the character is a printable character, otherwise False. + """ + alias ord_space = ord(" ") + alias ord_tilde = ord("~") + return ord_space <= int(c) <= ord_tilde + + # ===----------------------------------------------------------------------===# # String # ===----------------------------------------------------------------------===# struct String( Sized, Stringable, + Representable, IntableRaising, KeyElement, Boolable, @@ -447,11 +529,23 @@ struct String( """Return a Mojo-compatible representation of the `String` instance. You don't need to call this method directly, use `repr(my_string)` instead. + + Returns: + A new representation of the string. """ - if "'" in self: - return '"' + self + "'" + alias ord_squote = ord("'") + var result = String() + var use_dquote = False + + for idx in range(len(self._buffer) - 1): + var char = self._buffer[idx] + result += _repr_ascii(char) + use_dquote = use_dquote or (char == ord_squote) + + if use_dquote: + return '"' + result + '"' else: - return "'" + self + "'" + return "'" + result + "'" # ===------------------------------------------------------------------===# # Initializers diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 67c5b67105..cba810c609 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -34,6 +34,7 @@ struct StringLiteral( Sized, IntableRaising, Stringable, + Representable, KeyElement, Boolable, Formattable, @@ -144,6 +145,16 @@ struct StringLiteral( """ return self + fn __repr__(self) -> String: + """Return a representation of the `StringLiteral` instance. + + You don't need to call this method directly, use `repr("...")` instead. + + Returns: + A new representation of the string. + """ + return self.__str__().__repr__() + fn format_to(self, inout writer: Formatter): """ Formats this string literal to the provided formatter. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 031bb17223..aca8f22a65 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -40,11 +40,25 @@ fn test_stringable() raises: assert_equal("a string", str(AString())) -fn test_representable() raises: - assert_equal(repr(String("hello")), "'hello'") - assert_equal(repr(str(0)), "'0'") - # TODO: Add more complex cases with "'", escape characters, etc - # and make String.__repr__ more robust to handle those cases. +fn test_repr() raises: + # Usual cases + assert_equal(String.__repr__("hello"), "'hello'") + assert_equal(String.__repr__(str(0)), "'0'") + + # Escape cases + assert_equal(String.__repr__("\0"), r"'\x00'") + assert_equal(String.__repr__("\x06"), r"'\x06'") + assert_equal(String.__repr__("\x09"), r"'\t'") + assert_equal(String.__repr__("\n"), r"'\n'") + assert_equal(String.__repr__("\x0d"), r"'\r'") + assert_equal(String.__repr__("\x0e"), r"'\x0e'") + assert_equal(String.__repr__("\x1f"), r"'\x1f'") + assert_equal(String.__repr__(" "), "' '") + assert_equal(String.__repr__("'"), '"\'"') + assert_equal(String.__repr__("A"), "'A'") + assert_equal(String.__repr__("\\"), r"'\\'") + assert_equal(String.__repr__("~"), "'~'") + assert_equal(String.__repr__("\x7f"), r"'\x7f'") fn test_constructors() raises: @@ -705,7 +719,7 @@ def main(): test_equality_operators() test_add() test_stringable() - test_representable() + test_repr() test_string_join() test_stringref() test_stringref_from_dtypepointer() diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 43bedb9615..bee8942916 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -94,6 +94,26 @@ def test_intable(): _ = int("hi") +fn test_repr() raises: + # Usual cases + assert_equal(StringLiteral.__repr__("hello"), "'hello'") + + # Escape cases + assert_equal(StringLiteral.__repr__("\0"), r"'\x00'") + assert_equal(StringLiteral.__repr__("\x06"), r"'\x06'") + assert_equal(StringLiteral.__repr__("\x09"), r"'\t'") + assert_equal(StringLiteral.__repr__("\n"), r"'\n'") + assert_equal(StringLiteral.__repr__("\x0d"), r"'\r'") + assert_equal(StringLiteral.__repr__("\x0e"), r"'\x0e'") + assert_equal(StringLiteral.__repr__("\x1f"), r"'\x1f'") + assert_equal(StringLiteral.__repr__(" "), "' '") + assert_equal(StringLiteral.__repr__("'"), '"\'"') + assert_equal(StringLiteral.__repr__("A"), "'A'") + assert_equal(StringLiteral.__repr__("\\"), r"'\\'") + assert_equal(StringLiteral.__repr__("~"), "'~'") + assert_equal(StringLiteral.__repr__("\x7f"), r"'\x7f'") + + def main(): test_basics() test_contains() @@ -101,3 +121,4 @@ def main(): test_rfind() test_hash() test_intable() + test_repr() From 2d955dbfc826a163f4d69f9c3ad97824b17d7620 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Thu, 9 May 2024 13:56:27 -0400 Subject: [PATCH 0491/2019] [stdlib] Replaced `Variant.get[T]()` with `__refitem__` Using `__refitem__` allows cleaner syntax: `v[T]` rather than `v.get[T]()` from before. MODULAR_ORIG_COMMIT_REV_ID: 1410ca0a5f5f038cf50dd97bf32c631aa3b39b67 --- docs/changelog.md | 5 ++++- stdlib/src/collections/optional.mojo | 6 +++--- stdlib/src/utils/inlined_string.mojo | 26 +++++++++++--------------- stdlib/src/utils/variant.mojo | 12 ++++++------ stdlib/test/utils/test_variant.mojo | 16 ++++++++-------- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5d65ab3016..7e2639eb62 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -146,7 +146,10 @@ what we publish. `--diagnose-missing-doc-strings`. - The `take` function in `Variant` and `Optional` has been renamed to -`unsafe_take`. + `unsafe_take`. + +- The `get` function in `Variant` has been replaced by `__refitem__`. That is, + `v.get[T]()` should be replaced with `v[T]`. ### ❌ Removed diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 1187d04f7c..e39a799ed1 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -114,7 +114,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): A reference to the contained data of the option as a Reference[T]. """ debug_assert(self[].__bool__(), ".value() on empty Optional") - return self[]._value.get[T]() + return self[]._value[T] @always_inline fn _value_copy(self) -> T: @@ -126,7 +126,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): """ debug_assert(self.__bool__(), ".value() on empty Optional") - return self._value.get[T]()[] + return self._value[T] fn unsafe_take(owned self) -> T: """Unsafely move the value out of the Optional. @@ -155,7 +155,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): The underlying value contained in the Optional or a default value. """ if self.__bool__(): - return self._value.get[T]()[] + return self._value[T] return default fn __is__(self, other: NoneType) -> Bool: diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 4df5fc9f12..d14f85642b 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -96,19 +96,19 @@ struct InlinedString(Sized, Stringable, CollectionElement): fn __len__(self) -> Int: if self._is_small(): - return len(self._storage.get[_FixedString[Self.SMALL_CAP]]()[]) + return len(self._storage[_FixedString[Self.SMALL_CAP]]) else: debug_assert( self._storage.isa[String](), "expected non-small string variant to be String", ) - return len(self._storage.get[String]()[]) + return len(self._storage[String]) fn __str__(self) -> String: if self._is_small(): - return str(self._storage.get[_FixedString[Self.SMALL_CAP]]()[]) + return str(self._storage[_FixedString[Self.SMALL_CAP]]) else: - return self._storage.get[String]()[] + return self._storage[String] fn __iadd__(inout self, literal: StringLiteral): """Appends another string to this string. @@ -138,10 +138,10 @@ struct InlinedString(Sized, Stringable, CollectionElement): # length is shorter than the small capacity. if not self._is_small(): - self._storage.get[String]()[] += strref + self._storage[String] += strref elif total_len < Self.SMALL_CAP: try: - self._storage.get[_FixedString[Self.SMALL_CAP]]()[] += strref + self._storage[_FixedString[Self.SMALL_CAP]] += strref except e: abort( "unreachable: InlinedString append to FixedString failed: " @@ -160,9 +160,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): # Copy the bytes from the current small string layout memcpy( buffer_ptr, - self._storage.get[ - _FixedString[Self.SMALL_CAP] - ]()[].as_uint8_ptr(), + self._storage[_FixedString[Self.SMALL_CAP]].as_uint8_ptr(), len(self), ) @@ -241,9 +239,9 @@ struct InlinedString(Sized, Stringable, CollectionElement): """ if self._is_small(): - return self._storage.get[_FixedString[Self.SMALL_CAP]]()[].as_ptr() + return self._storage[_FixedString[Self.SMALL_CAP]].as_ptr() else: - return self._storage.get[String]()[].unsafe_ptr() + return self._storage[String].unsafe_ptr() fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: """Returns a pointer to the bytes of string data. @@ -253,11 +251,9 @@ struct InlinedString(Sized, Stringable, CollectionElement): """ if self._is_small(): - return self._storage.get[ - _FixedString[Self.SMALL_CAP] - ]()[].as_uint8_ptr() + return self._storage[_FixedString[Self.SMALL_CAP]].as_uint8_ptr() else: - return self._storage.get[String]()[].unsafe_uint8_ptr() + return self._storage[String].unsafe_uint8_ptr() fn _strref_dangerous(self) -> StringRef: """ diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 2191eb8f61..71f496e8dd 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -20,9 +20,9 @@ from utils import Variant alias IntOrString = Variant[Int, String] fn to_string(inout x: IntOrString) -> String: if x.isa[String](): - return x.get[String]()[] + return x[String][] # x.isa[Int]() - return str(x.get[Int]())[] + return str(x[Int])[] # They have to be mutable for now, and implement CollectionElement var an_int = IntOrString(4) @@ -107,7 +107,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): You can - use `isa[T]()` to check what type a variant is - use `unsafe_take[T]()` to take a value from the variant - - use `get[T]()` to get a value out of a variant + - use `[T]` to get a value out of a variant - This currently does an extra copy/move until we have lifetimes - It also temporarily requires the value to be mutable - use `set[T](owned new_value: T)` to reset the variant to a new value @@ -118,9 +118,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): alias IntOrString = Variant[Int, String] fn to_string(inout x: IntOrString) -> String: if x.isa[String](): - return x.get[String]()[] + return x[String][] # x.isa[Int]() - return str(x.get[Int]()[]) + return str(x[Int][]) # They have to be mutable for now, and implement CollectionElement var an_int = IntOrString(4) @@ -280,7 +280,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): alias idx = Self._check[T]() return self._get_state()[] == idx - fn get[ + fn __refitem__[ T: CollectionElement ](self: Reference[Self, _, _]) -> Reference[ T, self.is_mutable, self.lifetime diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index e883b01d3c..d1dba611e9 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -88,8 +88,8 @@ def test_basic(): assert_false(s.isa[Int]()) # get - assert_equal(4, i.get[Int]()[]) - assert_equal("4", s.get[String]()[]) + assert_equal(4, i[Int]) + assert_equal("4", s[String]) # we don't test what happens when you `get` the wrong type. # have fun! @@ -97,14 +97,14 @@ def test_basic(): i.set[String]("i") assert_false(i.isa[Int]()) assert_true(i.isa[String]()) - assert_equal("i", i.get[String]()[]) + assert_equal("i", i[String]) def test_copy(): var v1 = TestVariant(TestCounter()) var v2 = v1 assert_true( - v2.get[TestCounter]()[].copied > v1.get[TestCounter]()[].copied, + v2[TestCounter].copied > v1[TestCounter].copied, "didn't call copyinit", ) # test that we didn't call the other copyinit too! @@ -115,7 +115,7 @@ def test_move(): var v1 = TestVariant(TestCounter()) var v2 = v1 assert_true( - v2.get[TestCounter]()[].moved > v1.get[TestCounter]()[].moved, + v2[TestCounter].moved > v1[TestCounter].moved, "didn't call moveinit", ) # test that we didn't call the other moveinit too! @@ -175,7 +175,7 @@ def test_take_doesnt_call_deleter(): def test_get_returns_mutable_reference(): var v1: Variant[Int, String] = 42 - var x = v1.get[Int]()[] + var x = v1[Int] assert_equal(42, x) x = 100 assert_equal(100, x) @@ -183,8 +183,8 @@ def test_get_returns_mutable_reference(): assert_equal(100, x) # the x reference is still valid var v2: Variant[Int, String] = String("something") - v2.get[String]()[] = "something else" - assert_equal(v2.get[String]()[], "something else") + v2[String] = "something else" + assert_equal(v2[String], "something else") var v3: Variant[Int, String] = 4 assert_equal( From 73a82f4199fdbddd2fe42625598221e4eb3f1e02 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 9 May 2024 11:03:29 -0700 Subject: [PATCH 0492/2019] Fix links for moved files MODULAR_ORIG_COMMIT_REV_ID: 9833d9796b11c01f24832c5b06b6ecf7611edc4c --- docs/manual/get-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index edbb0aca63..0ddee12d32 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -19,7 +19,7 @@ If you already installed Mojo, see the [update guide](/max/update). Mojo is now bundled with MAX, which provides everything to compile, run, debug, and package Mojo code ([read -why](/engine/faq#why-bundle-mojo-with-max)). +why](/max/faq#why-bundle-mojo-with-max)). To install Mojo, [see the MAX install guide](/max/install). From 7e62988fca7d3a27eb76f2dfb62aa6df6d454599 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 9 May 2024 11:07:53 -0700 Subject: [PATCH 0493/2019] [stdlib] Mark `Arc` as `@register_passable` `Arc` should be register-passable because it's 8 bytes (pointer width) in size and has no requirement to have a fixed address. MODULAR_ORIG_COMMIT_REV_ID: 12faddba10ab50c907167c68d468c35b731e38ec --- stdlib/src/builtin/object.mojo | 14 +++++++++----- stdlib/src/memory/arc.mojo | 9 +-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index c49a8affbd..99761071bb 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -621,20 +621,24 @@ struct _ObjectImpl(CollectionElement, Stringable): @always_inline fn list_append(self, value: Self): - self.get_list_ptr()[].append(value.value) + var ptr = self.get_list_ptr() + ptr[].append(value.value) @always_inline fn get_list_length(self) -> Int: - return len(self.get_list_ptr()[]) + var ptr = self.get_list_ptr() + return len(ptr[]) @always_inline fn get_list_element(self, i: Int) -> _ObjectImpl: - return self.get_list_ptr()[][i].copy() + var ptr = self.get_list_ptr() + return ptr[][i].copy() @always_inline fn set_list_element(self, i: Int, value: _ObjectImpl): - self.get_list_ptr()[][i].destroy() - self.get_list_ptr()[][i] = value + var ptr = self.get_list_ptr() + ptr[][i].destroy() + ptr[][i] = value # ===------------------------------------------------------------------=== # # Object Attribute Functions diff --git a/stdlib/src/memory/arc.mojo b/stdlib/src/memory/arc.mojo index aba4fb02a3..3fef8175cc 100644 --- a/stdlib/src/memory/arc.mojo +++ b/stdlib/src/memory/arc.mojo @@ -45,6 +45,7 @@ struct _ArcInner[T: Movable]: return self.refcount.fetch_sub(1) == 1 +@register_passable struct Arc[T: Movable](CollectionElement): """Atomic reference-counted pointer. @@ -87,14 +88,6 @@ struct Arc[T: Movable](CollectionElement): existing._inner[].add_ref() self._inner = existing._inner - fn __moveinit__(inout self, owned existing: Self): - """Move an existing reference. - - Args: - existing: The existing reference. - """ - self._inner = existing._inner - fn __del__(owned self): """Delete the smart pointer reference. From 17ffc6a419c24ac793e9487c9b15cf979ad020d9 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 9 May 2024 12:09:41 -0600 Subject: [PATCH 0494/2019] [stdlib] Move `sort` to builtin Move various functions from `algorithm` module to be builtin, such as `sort`. This will allow others to improve the `sort` algorithm without breaking our internal uses. Note: - There is *a lot* of room for improvement on the existing sorting algorithm and its tests. MODULAR_ORIG_COMMIT_REV_ID: cfb7a4fceaa7b478129c40d7cf21b42a83324c81 --- docs/changelog.md | 5 + stdlib/src/builtin/sort.mojo | 414 ++++++++++++ stdlib/src/builtin/swap.mojo | 32 + stdlib/test/builtin/test_sort.mojo | 598 ++++++++++++++++++ stdlib/test/builtin/test_sort_issue_1018.mojo | 47 ++ stdlib/test/builtin/test_swap.mojo | 85 +++ 6 files changed, 1181 insertions(+) create mode 100644 stdlib/src/builtin/sort.mojo create mode 100644 stdlib/src/builtin/swap.mojo create mode 100644 stdlib/test/builtin/test_sort.mojo create mode 100644 stdlib/test/builtin/test_sort_issue_1018.mojo create mode 100644 stdlib/test/builtin/test_swap.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 7e2639eb62..95ac6fcaec 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -151,6 +151,11 @@ what we publish. - The `get` function in `Variant` has been replaced by `__refitem__`. That is, `v.get[T]()` should be replaced with `v[T]`. +- Various functions in the `algorithm` module are now moved to be + builtin-functions. This includes `sort`, `swap`, and `partition`. + `swap` and `partition` will likely shuffle around as we're reworking + our builtnin `sort` function and optimizing it. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo new file mode 100644 index 0000000000..e57229fb82 --- /dev/null +++ b/stdlib/src/builtin/sort.mojo @@ -0,0 +1,414 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the built-in `sort` function. + +These are Mojo built-ins, so you don't need to import them. +""" + +from collections import List +from memory import Pointer, UnsafePointer +from sys import bitwidthof + +# ===----------------------------------------------------------------------===# +# sort +# ===----------------------------------------------------------------------===# + +alias _cmp_fn_type = fn[type: AnyRegType] (type, type) capturing -> Bool + + +fn _insertion_sort[ + type: AnyRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], start: Int, end: Int): + """Sort the array[start:end] slice""" + + for i in range(start + 1, end): + var value = array[i] + var j = i + + # Find the placement of the value in the array, shifting as we try to + # find the position. Throughout, we assume array[start:i] has already + # been sorted. + while j > start and not cmp_fn[type](array[j - 1], value): + array[j] = array[j - 1] + j -= 1 + + array[j] = value + + +fn _insertion_sort[ + type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool +](array: UnsafePointer[type], start: Int, end: Int): + """Sort the array[start:end] slice""" + + for i in range(start + 1, end): + var value = array[i] + var j = i + + # Find the placement of the value in the array, shifting as we try to + # find the position. Throughout, we assume array[start:i] has already + # been sorted. + while j > start and not cmp_fn(array[j - 1], value): + array[j] = array[j - 1] + j -= 1 + + array[j] = value + + +@always_inline +fn _partition[ + type: AnyRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], start: Int, end: Int) -> Int: + if start == end: + return end + + var pivot = start + (end - start) // 2 + + var pivot_value = array[pivot] + + var left = start + var right = end - 2 + + swap(array[pivot], array[end - 1]) + + while left < right: + if cmp_fn[type](array[left], pivot_value): + left += 1 + elif not cmp_fn[type](array[right], pivot_value): + right -= 1 + else: + swap(array[left], array[right]) + + if cmp_fn[type](array[right], pivot_value): + right += 1 + swap(array[end - 1], array[right]) + return right + + +@always_inline +fn _partition[ + type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool +](array: UnsafePointer[type], start: Int, end: Int) -> Int: + if start == end: + return end + + var pivot = start + (end - start) // 2 + + var pivot_value = array[pivot] + + var left = start + var right = end - 2 + + swap(array[pivot], array[end - 1]) + + while left < right: + if cmp_fn(array[left], pivot_value): + left += 1 + elif not cmp_fn(array[right], pivot_value): + right -= 1 + else: + swap(array[left], array[right]) + + if cmp_fn(array[right], pivot_value): + right += 1 + swap(array[end - 1], array[right]) + return right + + +# ===----------------------------------------------------------------------===# +# ctlz +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn _ctlz(val: Int) -> Int: + """Counts the number of leading zeros of an integer. + + Args: + val: The input value. + + Returns: + The number of leading zeros of the input. + """ + return llvm_intrinsic["llvm.ctlz", Int, has_side_effect=False](val, False) + + +fn _estimate_initial_height(size: Int) -> Int: + # Compute the log2 of the size rounded upward. + var log2 = int((bitwidthof[DType.index]() - 1) ^ _ctlz(size | 1)) + return max(2, log2) + + +fn _quicksort[ + type: AnyRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], size: Int): + if size == 0: + return + + var stack = List[Int](capacity=_estimate_initial_height(size)) + stack.append(0) + stack.append(size) + while len(stack) > 0: + var end = stack.pop() + var start = stack.pop() + + var len = end - start + if len < 2: + continue + + if len == 2: + _small_sort[2, type, cmp_fn](array + start) + continue + + if len == 3: + _small_sort[3, type, cmp_fn](array + start) + continue + + if len == 4: + _small_sort[4, type, cmp_fn](array + start) + continue + + if len == 5: + _small_sort[5, type, cmp_fn](array + start) + continue + + if len < 32: + _insertion_sort[type, cmp_fn](array, start, end) + continue + + var pivot = _partition[type, cmp_fn](array, start, end) + + stack.append(pivot + 1) + stack.append(end) + + stack.append(start) + stack.append(pivot) + + +fn _quicksort[ + type: CollectionElement, cmp_fn: fn (type, type) capturing -> Bool +](array: UnsafePointer[type], size: Int): + if size == 0: + return + + var stack = List[Int](capacity=_estimate_initial_height(size)) + stack.append(0) + stack.append(size) + while len(stack) > 0: + var end = stack.pop() + var start = stack.pop() + + var len = end - start + if len < 2: + continue + + if len < 8: + _insertion_sort[type, cmp_fn](array, start, end) + continue + + var pivot = _partition[type, cmp_fn](array, start, end) + + stack.append(pivot + 1) + stack.append(end) + + stack.append(start) + stack.append(pivot) + + +# ===----------------------------------------------------------------------===# +# partition +# ===----------------------------------------------------------------------===# +fn partition[ + type: AnyRegType, cmp_fn: _cmp_fn_type +](buff: Pointer[type], k: Int, size: Int): + """Partition the input vector inplace such that first k elements are the + largest (or smallest if cmp_fn is <= operator) elements. + The ordering of the first k elements is undefined. + + Parameters: + type: DType of the underlying data. + cmp_fn: Comparison functor of type, type) capturing -> Bool type. + + Args: + buff: Input buffer. + k: Index of the partition element. + size: The length of the buffer. + """ + var stack = List[Int](capacity=_estimate_initial_height(size)) + stack.append(0) + stack.append(size) + while len(stack) > 0: + var end = stack.pop() + var start = stack.pop() + var pivot = _partition[type, cmp_fn](buff, start, end) + if pivot == k: + break + elif k < pivot: + stack.append(start) + stack.append(pivot) + else: + stack.append(pivot + 1) + stack.append(end) + + +# ===----------------------------------------------------------------------===# +# sort +# ===----------------------------------------------------------------------===# + + +fn sort(inout buff: Pointer[Int], len: Int): + """Sort the vector inplace. + The function doesn't return anything, the vector is updated inplace. + + Args: + buff: Input buffer. + len: The length of the buffer. + """ + + @parameter + fn _less_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) <= rebind[Int](rhs) + + _quicksort[Int, _less_than_equal](buff, len) + + +fn sort[type: DType](inout buff: Pointer[Scalar[type]], len: Int): + """Sort the vector inplace. + The function doesn't return anything, the vector is updated inplace. + + Parameters: + type: DType of the underlying data. + + Args: + buff: Input buffer. + len: The length of the buffer. + """ + + @parameter + fn _less_than_equal[ty: AnyRegType](lhs: ty, rhs: ty) -> Bool: + return rebind[Scalar[type]](lhs) <= rebind[Scalar[type]](rhs) + + _quicksort[Scalar[type], _less_than_equal](buff, len) + + +fn sort(inout v: List[Int]): + """Sort the vector inplace. + The function doesn't return anything, the vector is updated inplace. + + Args: + v: Input integer vector to sort. + """ + # Downcast any pointer to register-passable pointer. + var ptr = rebind[Pointer[Int]](v.data) + sort(ptr, len(v)) + + +fn sort[type: DType](inout v: List[Scalar[type]]): + """Sort the vector inplace. + The function doesn't return anything, the vector is updated inplace. + + Parameters: + type: DType of the underlying data. + + Args: + v: Input vector to sort. + """ + + var ptr = rebind[Pointer[Scalar[type]]](v.data) + sort[type](ptr, len(v)) + + +fn sort[ + type: CollectionElement, + cmp_fn: fn (type, type) capturing -> Bool, +](inout v: List[type]): + """Sort the vector inplace. + The function doesn't return anything, the vector is updated inplace. + + Parameters: + type: DType of the underlying data. + cmp_fn: The comparison function. + + Args: + v: Input vector to sort. + """ + + _quicksort[type, cmp_fn](v.data, len(v)) + + +# ===----------------------------------------------------------------------===# +# sort networks +# ===----------------------------------------------------------------------===# + + +@always_inline +fn _sort2[ + type: AnyRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], offset0: Int, offset1: Int): + var a = array[offset0] + var b = array[offset1] + if not cmp_fn[type](a, b): + array[offset0] = b + array[offset1] = a + + +@always_inline +fn _sort_partial_3[ + type: AnyRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type], offset0: Int, offset1: Int, offset2: Int): + var a = array[offset0] + var b = array[offset1] + var c = array[offset2] + var r = cmp_fn[type](c, a) + var t = c if r else a + if r: + array[offset2] = a + if cmp_fn[type](b, t): + array[offset0] = b + array[offset1] = t + elif r: + array[offset0] = t + + +@always_inline +fn _small_sort[ + n: Int, type: AnyRegType, cmp_fn: _cmp_fn_type +](array: Pointer[type]): + @parameter + if n == 2: + _sort2[type, cmp_fn](array, 0, 1) + return + + @parameter + if n == 3: + _sort2[type, cmp_fn](array, 1, 2) + _sort_partial_3[type, cmp_fn](array, 0, 1, 2) + return + + @parameter + if n == 4: + _sort2[type, cmp_fn](array, 0, 2) + _sort2[type, cmp_fn](array, 1, 3) + _sort2[type, cmp_fn](array, 0, 1) + _sort2[type, cmp_fn](array, 2, 3) + _sort2[type, cmp_fn](array, 1, 2) + return + + @parameter + if n == 5: + _sort2[type, cmp_fn](array, 0, 1) + _sort2[type, cmp_fn](array, 3, 4) + _sort_partial_3[type, cmp_fn](array, 2, 3, 4) + _sort2[type, cmp_fn](array, 1, 4) + _sort_partial_3[type, cmp_fn](array, 0, 2, 3) + _sort_partial_3[type, cmp_fn](array, 1, 2, 3) + return diff --git a/stdlib/src/builtin/swap.mojo b/stdlib/src/builtin/swap.mojo new file mode 100644 index 0000000000..3c3fcc6e22 --- /dev/null +++ b/stdlib/src/builtin/swap.mojo @@ -0,0 +1,32 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the built-in `swap` function. + +These are Mojo built-ins, so you don't need to import them. +""" + + +@always_inline +fn swap[T: Movable](inout lhs: T, inout rhs: T): + """Swaps the two given arguments. + + Parameters: + T: Constrained to Copyable types. + + Args: + lhs: Argument value swapped with rhs. + rhs: Argument value swapped with lhs. + """ + var tmp = lhs^ + lhs = rhs^ + rhs = tmp^ diff --git a/stdlib/test/builtin/test_sort.mojo b/stdlib/test/builtin/test_sort.mojo new file mode 100644 index 0000000000..c46e1303e4 --- /dev/null +++ b/stdlib/test/builtin/test_sort.mojo @@ -0,0 +1,598 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo %s | FileCheck %s +from collections import List +from random import random_si64, seed + +from builtin.sort import _quicksort, _small_sort + + +# CHECK-LABEL: test_sort_small_3 +fn test_sort_small_3(): + print("== test_sort_small_3") + alias length = 3 + + var list = List[Int]() + + list.append(9) + list.append(1) + list.append(2) + + @parameter + fn _less_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) <= rebind[Int](rhs) + + var ptr = rebind[Pointer[Int]](list.data) + _small_sort[length, Int, _less_than_equal](ptr) + + # CHECK: 1 + # CHECK: 2 + # CHECK: 9 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort_small_5 +fn test_sort_small_5(): + print("== test_sort_small_5") + alias length = 5 + + var list = List[Int]() + + list.append(9) + list.append(1) + list.append(2) + list.append(3) + list.append(4) + + @parameter + fn _less_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) <= rebind[Int](rhs) + + var ptr = rebind[Pointer[Int]](list.data) + _small_sort[length, Int, _less_than_equal](ptr) + + # CHECK: 1 + # CHECK: 2 + # CHECK: 3 + # CHECK: 4 + # CHECK: 9 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort0 +fn test_sort0(): + print("== test_sort0") + + var list = List[Int]() + + sort(list) + + +# CHECK-LABEL: test_sort2 +fn test_sort2(): + print("== test_sort2") + + alias length = 2 + var list = List[Int]() + + list.append(-1) + list.append(0) + + sort(list) + + # CHECK: -1 + # CHECK: 0 + for i in range(length): + print(list[i]) + + list[0] = 2 + list[1] = -2 + + sort(list) + + # CHECK: -2 + # CHECK: 2 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort3 +fn test_sort3(): + print("== test_sort3") + + alias length = 3 + var list = List[Int]() + + list.append(-1) + list.append(0) + list.append(1) + + sort(list) + + # CHECK: -1 + # CHECK: 0 + # CHECK: 1 + for i in range(length): + print(list[i]) + + list[0] = 2 + list[1] = -2 + list[2] = 0 + + sort(list) + + # CHECK: -2 + # CHECK: 0 + # CHECK: 2 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL test_sort3_dupe_elements +fn test_sort3_dupe_elements(): + print("== test_sort3_dupe_elements") + + alias length = 3 + + fn test[ + cmp_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, + ](): + var list = List[Int](capacity=3) + list.append(5) + list.append(3) + list.append(3) + + var ptr = rebind[Pointer[Int]](list.data) + _quicksort[Int, cmp_fn](ptr, len(list)) + + # CHECK: 3 + # CHECK: 3 + # CHECK: 5 + for i in range(length): + print(list[i]) + + @parameter + fn _lt[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) < rebind[Int](rhs) + + @parameter + fn _leq[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) <= rebind[Int](rhs) + + test[_lt]() + test[_leq]() + + +# CHECK-LABEL: test_sort4 +fn test_sort4(): + print("== test_sort4") + + alias length = 4 + var list = List[Int]() + + list.append(-1) + list.append(0) + list.append(1) + list.append(2) + + sort(list) + + # CHECK: -1 + # CHECK: 0 + # CHECK: 1 + # CHECK: 2 + for i in range(length): + print(list[i]) + + list[0] = 2 + list[1] = -2 + list[2] = 0 + list[3] = -4 + + sort(list) + + # CHECK: -4 + # CHECK: -2 + # CHECK: 0 + # CHECK: 2 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort5 +fn test_sort5(): + print("== test_sort5") + + alias length = 5 + var list = List[Int]() + + for i in range(5): + list.append(i) + + sort(list) + + # CHECK: 0 + # CHECK: 1 + # CHECK: 2 + # CHECK: 3 + # CHECK: 4 + for i in range(length): + print(list[i]) + + list[0] = 2 + list[1] = -2 + list[2] = 0 + list[3] = -4 + list[4] = 1 + + sort(list) + + # CHECK: -4 + # CHECK: -2 + # CHECK: 0 + # CHECK: 1 + # CHECK: 2 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort_reverse +fn test_sort_reverse(): + print("== test_sort_reverse") + + alias length = 5 + var list = List[Int](capacity=length) + + for i in range(length): + list.append(length - i - 1) + + sort(list) + + # CHECK: 0 + # CHECK: 1 + # CHECK: 2 + # CHECK: 3 + # CHECK: 4 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort_semi_random +fn test_sort_semi_random(): + print("== test_sort_semi_random") + + alias length = 8 + var list = List[Int](capacity=length) + + for i in range(length): + if i % 2: + list.append(-i) + else: + list.append(i) + + sort(list) + + # CHECK: 7 + # CHECK: 5 + # CHECK: 3 + # CHECK: 1 + # CHECK: 0 + # CHECK: 2 + # CHECK: 4 + # CHECK: 6 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort9 +fn test_sort9(): + print("== test_sort9") + + alias length = 9 + var list = List[Int](capacity=length) + + for i in range(length): + list.append(length - i - 1) + + sort(list) + + # CHECK: 0 + # CHECK: 1 + # CHECK: 2 + # CHECK: 3 + # CHECK: 4 + # CHECK: 5 + # CHECK: 6 + # CHECK: 7 + # CHECK: 8 + for i in range(length): + print(list[i]) + + +# CHECK-LABEL: test_sort103 +fn test_sort103(): + print("== test_sort103") + + alias length = 103 + var list = List[Int](capacity=length) + + for i in range(length): + list.append(length - i - 1) + + sort(list) + + # CHECK-NOT: unsorted + for i in range(1, length): + if list[i - 1] > list[i]: + print("error: unsorted") + + +# CHECK-LABEL: test_sort_any_103 +fn test_sort_any_103(): + print("== test_sort_any_103") + + alias length = 103 + var list = List[Float32](capacity=length) + + for i in range(length): + list.append(length - i - 1) + + sort[DType.float32](list) + + # CHECK-NOT: unsorted + for i in range(1, length): + if list[i - 1] > list[i]: + print("error: unsorted") + + +fn test_quick_sort_repeated_val(): + print("== test_quick_sort_repeated_val") + + alias length = 36 + var list = List[Float32](capacity=length) + + for i in range(0, length // 4): + list.append(i + 1) + list.append(i + 1) + list.append(i + 1) + list.append(i + 1) + + @parameter + fn _greater_than[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Float32](lhs) > rebind[Float32](rhs) + + var ptr = rebind[Pointer[Float32]](list.data) + _quicksort[Float32, _greater_than](ptr, len(list)) + + # CHECK: 9.0 + # CHECK: 9.0 + # CHECK: 9.0 + # CHECK: 9.0 + # CHECK: 8.0 + # CHECK: 8.0 + # CHECK: 8.0 + # CHECK: 8.0 + # CHECK: 7.0 + # CHECK: 7.0 + # CHECK: 7.0 + # CHECK: 7.0 + # CHECK: 6.0 + # CHECK: 6.0 + # CHECK: 6.0 + # CHECK: 6.0 + # CHECK: 5.0 + # CHECK: 5.0 + # CHECK: 5.0 + # CHECK: 5.0 + # CHECK: 4.0 + # CHECK: 4.0 + # CHECK: 4.0 + # CHECK: 4.0 + # CHECK: 3.0 + # CHECK: 3.0 + # CHECK: 3.0 + # CHECK: 3.0 + # CHECK: 2.0 + # CHECK: 2.0 + # CHECK: 2.0 + # CHECK: 2.0 + # CHECK: 1.0 + # CHECK: 1.0 + # CHECK: 1.0 + # CHECK: 1.0 + for i in range(0, length): + print(list[i]) + + @parameter + fn _less_than[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Float32](lhs) < rebind[Float32](rhs) + + # CHECK: 1.0 + # CHECK: 1.0 + # CHECK: 1.0 + # CHECK: 1.0 + # CHECK: 2.0 + # CHECK: 2.0 + # CHECK: 2.0 + # CHECK: 2.0 + # CHECK: 3.0 + # CHECK: 3.0 + # CHECK: 3.0 + # CHECK: 3.0 + # CHECK: 4.0 + # CHECK: 4.0 + # CHECK: 4.0 + # CHECK: 4.0 + # CHECK: 5.0 + # CHECK: 5.0 + # CHECK: 5.0 + # CHECK: 5.0 + # CHECK: 6.0 + # CHECK: 6.0 + # CHECK: 6.0 + # CHECK: 6.0 + # CHECK: 7.0 + # CHECK: 7.0 + # CHECK: 7.0 + # CHECK: 7.0 + # CHECK: 8.0 + # CHECK: 8.0 + # CHECK: 8.0 + # CHECK: 8.0 + # CHECK: 9.0 + # CHECK: 9.0 + # CHECK: 9.0 + # CHECK: 9.0 + var sptr = rebind[Pointer[Float32]](list.data) + _quicksort[Float32, _less_than](sptr, len(list)) + for i in range(0, length): + print(list[i]) + + +fn test_partition_top_k(length: Int, k: Int): + print("== test_partition_top_k_", end="") + print(length, end="") + print("_", end="") + print(k, end="") + print("") + + var list = List[Float32](capacity=length) + + for i in range(0, length): + list.append(i) + + @parameter + fn _great_than_equal[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Float32](lhs) >= rebind[Float32](rhs) + + var ptr = rebind[Pointer[Float32]](list.data) + _ = partition[Float32, _great_than_equal](ptr, k, len(list)) + + for i in range(0, k): + if list[i] < length - k: + print("error: incorrect top-k element", list[i]) + + +# CHECK-LABEL: test_sort_stress +fn test_sort_stress(): + print("== test_sort_stress") + var lens = VariadicList[Int](3, 100, 117, 223, 500, 1000, 1500, 2000, 3000) + var random_seed = 0 + seed(random_seed) + + @__copy_capture(random_seed) + @parameter + fn test[ + cmp_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, + check_fn: fn[type: AnyRegType] (type, type) capturing -> Bool, + ](length: Int): + var list = List[Int](capacity=length) + for i in range(length): + list.append(int(random_si64(-length, length))) + + var ptr = rebind[Pointer[Int]](list.data) + _quicksort[Int, cmp_fn](ptr, len(list)) + + # CHECK-NOT: error + for i in range(length - 1): + if not check_fn[Int](list[i], list[i + 1]): + print("error: unsorted, seed is", random_seed) + return + + @parameter + @always_inline + fn _gt[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) > rebind[Int](rhs) + + @parameter + @always_inline + fn _geq[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) >= rebind[Int](rhs) + + @parameter + @always_inline + fn _lt[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) < rebind[Int](rhs) + + @parameter + @always_inline + fn _leq[type: AnyRegType](lhs: type, rhs: type) -> Bool: + return rebind[Int](lhs) <= rebind[Int](rhs) + + for i in range(len(lens)): + var length = lens[i] + test[_gt, _geq](length) + test[_geq, _geq](length) + test[_lt, _leq](length) + test[_leq, _leq](length) + + +@value +struct MyStruct: + var val: Int + + +# CHECK-LABEL: test_sort_custom +fn test_sort_custom(): + print("== test_sort_custom") + + alias length = 103 + var list = List[MyStruct](capacity=length) + + for i in range(length): + list.append(MyStruct(length - i - 1)) + + @parameter + fn compare_fn(lhs: MyStruct, rhs: MyStruct) -> Bool: + return lhs.val <= rhs.val + + sort[MyStruct, compare_fn](list) + + # CHECK-NOT: unsorted + for i in range(1, length): + if list[i - 1].val > list[i].val: + print("error: unsorted") + + +fn main(): + test_sort_small_3() + test_sort_small_5() + test_sort0() + test_sort2() + test_sort3() + test_sort3_dupe_elements() + test_sort4() + test_sort5() + test_sort_reverse() + test_sort_semi_random() + test_sort9() + test_sort103() + test_sort_any_103() + test_quick_sort_repeated_val() + + test_sort_stress() + + test_sort_custom() + + # CHECK-LABEL: test_partition_top_k_7_5 + # CHECK-NOT: incorrect top-k + test_partition_top_k(7, 5) + # CHECK-LABEL: test_partition_top_k_11_2 + # CHECK-NOT: incorrect top-k + test_partition_top_k(11, 2) + # CHECK-LABEL: test_partition_top_k_4_1 + # CHECK-NOT: incorrect top-k + test_partition_top_k(4, 1) diff --git a/stdlib/test/builtin/test_sort_issue_1018.mojo b/stdlib/test/builtin/test_sort_issue_1018.mojo new file mode 100644 index 0000000000..2fe1a9c014 --- /dev/null +++ b/stdlib/test/builtin/test_sort_issue_1018.mojo @@ -0,0 +1,47 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +# RUN: %mojo %s | FileCheck %s + +from random import rand + + +fn sort_test[D: DType](size: Int, max: Int, name: StringLiteral) raises: + var p = Pointer[SIMD[D, 1]].alloc(size) + rand[D](p, size) + sort[D](p, size) + for i in range(1, size - 1): + if p[i] < p[i - 1]: + print(name, "size:", size, "max:", max, "incorrect sort") + print("p[", end="") + print(i - 1, end="") + print("] =", p.load(i - 1)) + print("p[", end="") + print(i, end="") + print("] =", p.load(i)) + print() + p.free() + raise "Failed" + p.free() + + +fn main(): + try: + sort_test[DType.int8](300, 3_000, "int8") + sort_test[DType.float32](3_000, 3_000, "float32") + sort_test[DType.float64](300_000, 3_000_000_000, "float64") + # CHECK: Success + print("Success") + except e: + # CHECK-NOT: Failed + print(e) diff --git a/stdlib/test/builtin/test_swap.mojo b/stdlib/test/builtin/test_swap.mojo new file mode 100644 index 0000000000..6d834d4b71 --- /dev/null +++ b/stdlib/test/builtin/test_swap.mojo @@ -0,0 +1,85 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from test_utils import MoveOnly +from testing import assert_equal + + +def test_swap_Int(): + var a: Int = 42 + var b: Int = 24 + + swap(a, b) + + assert_equal(a, 24) + assert_equal(b, 42) + + +def test_swap_MoveOnlyInt(): + var a: MoveOnly[Int] = 42 + var b: MoveOnly[Int] = 24 + + swap(a, b) + + assert_equal(a.data, 24) + assert_equal(b.data, 42) + + +def test_swap_String(): + var a: String = "Hello" + var b: String = "World" + + swap(a, b) + + assert_equal(a, "World") + assert_equal(b, "Hello") + + +def test_swap_Tuple_Int(): + var a = (1, 2, 3, 4) + var b = (5, 6, 7, 8) + + swap(a, b) + + assert_equal(a[0], 5) + assert_equal(a[1], 6) + assert_equal(a[2], 7) + assert_equal(a[3], 8) + + assert_equal(b[0], 1) + assert_equal(b[1], 2) + assert_equal(b[2], 3) + assert_equal(b[3], 4) + + +def test_swap_Tuple_Mixed(): + var a = (1, String("Hello"), 3) + var b = (4, String("World"), 6) + + swap(a, b) + + assert_equal(a[0], 4) + assert_equal(a[1], "World") + assert_equal(a[2], 6) + + assert_equal(b[0], 1) + assert_equal(b[1], "Hello") + assert_equal(b[2], 3) + + +def main(): + test_swap_Int() + test_swap_String() + test_swap_Tuple_Int() + test_swap_Tuple_Mixed() From 52bdadb55d8b685fa40a948ce92b696058b74177 Mon Sep 17 00:00:00 2001 From: Billy Zhu Date: Thu, 9 May 2024 11:38:47 -0700 Subject: [PATCH 0495/2019] [mojo][debugger] Update changelog for enabling breakpoints on inlined callsites Debugger users can now set breakpoints on function calls in O0 builds even if the call has been inlined by the compiler. MODULAR_ORIG_COMMIT_REV_ID: 8ef63292c0bc12067d81fac0a1fe353fa7019321 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 95ac6fcaec..a70c944dd2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -134,6 +134,9 @@ what we publish. - `Dict` now implements `get(key)` and `get(key, default)` functions. ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) +- Debugger users can now set breakpoints on function calls in O0 builds even if + the call has been inlined by the compiler. + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no From 6ac297d1da5007d65ce587b524817bac9622c84a Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 9 May 2024 14:59:37 -0400 Subject: [PATCH 0496/2019] [stdlib] Return `IntLiteral` from several `sys.info` functions These include: `sizeof`, `alignof`, `bitwidthof`, and `simdwidthof`. This way these can be used in arbitrary precision parameter computations. MODULAR_ORIG_COMMIT_REV_ID: 9170da35c5da41190bef2a939a92776d6067be52 --- stdlib/src/memory/unsafe.mojo | 2 +- stdlib/src/sys/info.mojo | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index f75912e493..950c42db9f 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -405,7 +405,7 @@ struct LegacyPointer[ # Store a simd value into the pointer. The address must be properly # aligned, 64B for avx512, 32B for avx2, and 16B for avx. __mlir_op.`pop.store`[ - alignment = (8 * simdwidthof[type]()).value, + alignment = int(8 * simdwidthof[type]()).value, nonTemporal = __mlir_attr.unit, ](value, self.address) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index c57ec4c7a7..9a5ef63156 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -511,7 +511,7 @@ fn simdbytewidth[ @always_inline("nodebug") fn sizeof[ type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the size of (in bytes) of the type. Parameters: @@ -526,14 +526,14 @@ fn sizeof[ type, `> : !kgen.type,`, target, - `> : index`, + `> : !kgen.int_literal`, ] @always_inline("nodebug") fn sizeof[ type: DType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the size of (in bytes) of the dtype. Parameters: @@ -550,14 +550,14 @@ fn sizeof[ `>`, `> : !kgen.type,`, target, - `> : index`, + `> : !kgen.int_literal`, ] @always_inline("nodebug") fn alignof[ type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the align of (in bytes) of the type. Parameters: @@ -572,14 +572,14 @@ fn alignof[ type, `> : !kgen.type,`, target, - `> : index`, + `> : !kgen.int_literal`, ] @always_inline("nodebug") fn alignof[ type: DType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the align of (in bytes) of the dtype. Parameters: @@ -596,14 +596,14 @@ fn alignof[ `>`, `> : !kgen.type,`, target, - `> : index`, + `> : !kgen.int_literal`, ] @always_inline("nodebug") fn bitwidthof[ type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the size of (in bits) of the type. Parameters: @@ -620,7 +620,7 @@ fn bitwidthof[ @always_inline("nodebug") fn bitwidthof[ type: DType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the size of (in bits) of the dtype. Parameters: @@ -638,7 +638,7 @@ fn bitwidthof[ @always_inline("nodebug") fn simdwidthof[ type: AnyRegType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the vector size of the type on the host system. Parameters: @@ -654,7 +654,7 @@ fn simdwidthof[ @always_inline("nodebug") fn simdwidthof[ type: DType, target: __mlir_type.`!kgen.target` = _current_target() -]() -> Int: +]() -> IntLiteral: """Returns the vector size of the type on the host system. Parameters: From 08528fc201746be94c395f9570b881d0c21d8bfe Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 9 May 2024 12:10:24 -0700 Subject: [PATCH 0497/2019] Fix docsite by coding code Our website parses HTML-looking things (actually MDX/JSX), so things like this must be put in code font. MODULAR_ORIG_COMMIT_REV_ID: af37bfd42eeb45f17187b49026c0439fb64527dd --- stdlib/src/builtin/bool.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 3d152cd1a1..c4f9f3ef5c 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -78,7 +78,7 @@ struct Bool( @always_inline("nodebug") fn __init__(value: __mlir_type.`!pop.scalar`) -> Bool: - """Construct a Bool value given a !pop.scalar value. + """Construct a Bool value given a `!pop.scalar` value. Args: value: The initial value. From c0d668fd7147c79b19f635d2b5ea36db4fd58754 Mon Sep 17 00:00:00 2001 From: soraros Date: Thu, 9 May 2024 14:10:51 -0500 Subject: [PATCH 0498/2019] [External] [stdlib] Remove `__and__`/`__or__`/... from `Optional` and `OptionalReg` (#39456) [External] [stdlib] Remove `__and__`/`__or__`/... from `Optional` and `OptionalReg` We now have guaranteed implicit conversion from `Boolable` to `Bool` which renders them useless. Closes modularml/mojo#2491 MODULAR_ORIG_COMMIT_REV_ID: ebaa59d6caf8e05dff3bec3e0ad428a46cd24344 --- stdlib/src/collections/optional.mojo | 128 --------------------------- 1 file changed, 128 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index e39a799ed1..88dc58a15b 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -200,66 +200,6 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): """ return not self - fn __and__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value and the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if both inputs are True after boolean coercion. - """ - return self.__bool__() and other.__bool__() - - fn __rand__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value and the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if both inputs are True after boolean coercion. - """ - return self.__bool__() and other.__bool__() - - fn __or__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value or the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if either inputs is True after boolean coercion. - """ - return self.__bool__() or other.__bool__() - - fn __ror__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value or the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if either inputs is True after boolean coercion. - """ - return self.__bool__() or other.__bool__() - # ===----------------------------------------------------------------------===# # OptionalReg @@ -346,71 +286,3 @@ struct OptionalReg[T: AnyRegType](Boolable): True if the optional has a value and False otherwise. """ return __mlir_op.`kgen.variant.is`[index = Int(0).value](self._value) - - fn __invert__(self) -> Bool: - """Return False if the optional has a value. - - Returns: - False if the optional has a value and True otherwise. - """ - return not self.__bool__() - - fn __and__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value and the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if both inputs are True after boolean coercion. - """ - return self.__bool__() and other.__bool__() - - fn __rand__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value and the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if both inputs are True after boolean coercion. - """ - return self.__bool__() and other.__bool__() - - fn __or__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value or the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if either inputs is True after boolean coercion. - """ - return self.__bool__() or other.__bool__() - - fn __ror__[type: Boolable](self, other: type) -> Bool: - """Return true if self has a value or the other value is coercible to - True. - - Parameters: - type: Type coercible to Bool. - - Args: - other: Value to compare to. - - Returns: - True if either inputs is True after boolean coercion. - """ - return self.__bool__() or other.__bool__() From 5fe245b8e38a70c7e239e3305251f8c63561e0c1 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Thu, 9 May 2024 18:13:07 -0700 Subject: [PATCH 0499/2019] [stdlib] Remove extra copying from Dict Paired with @ConnorGray Dictionary performance is pretty bad. Much of this poor performance comes from accidental copies that survived from before Mojo had references. Previously a dict with ~20k strings would take O(minutes) to construct, now dictionaries with O(10m) strings are usable. This is just low-hanging fruit, there's still plenty to optimize for further performance. - Adds a `Dict.__get_ref(i)` which allows refitem semantics without a copy - Updates dict insert and __setitem__ to take owned values - Updatessdict insert, pop, resize and compact to remove any copies - Changes `Optional.take` and `Variant.take` to take `inout self` rather than `owned self` - Changes `Optional.take` to reset the state to empty - `Variant.take` leaves the variant in an uninitialized state, should be renamed to make it explicitly unsafe. Leaving as followup. - Updates the dict tests to validate that creation, insertion, and access do the minimal required copies. - Fixes an existing uncaught bug with dictionary compaction, adds a unit test for that case. MODULAR_ORIG_COMMIT_REV_ID: e1fadfc0a4ab9f7a1afba35dea4e26a99287a387 --- stdlib/src/collections/dict.mojo | 90 +++++++++++++++------- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/utils/variant.mojo | 2 +- stdlib/test/collections/test_dict.mojo | 23 +++++- stdlib/test/collections/test_optional.mojo | 2 +- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 85d9231076..2f054b3082 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -490,19 +490,33 @@ struct Dict[K: KeyElement, V: CollectionElement]( Raises: "KeyError" if the key isn't present. """ - var value = self.find(key) - if value: - return value.value()[] - raise "KeyError" + return self._find_ref(key)[] + + # TODO(MSTDL-452): rename to __refitem__ + fn __get_ref( + self: Reference[Self, _, _], key: K + ) raises -> Reference[V, self.is_mutable, self.lifetime]: + """Retrieve a value out of the dictionary. + + Args: + key: The key to retrieve. + + Returns: + The value associated with the key, if it's present. + + Raises: + "KeyError" if the key isn't present. + """ + return self[]._find_ref(key) - fn __setitem__(inout self, key: K, value: V): + fn __setitem__(inout self, owned key: K, owned value: V): """Set a value in the dictionary by key. Args: key: The key to associate with the specified value. value: The data to store in the dictionary. """ - self._insert(key, value) + self._insert(key^, value^) fn __contains__(self, key: K) -> Bool: """Check if a given key is in the dictionary or not. @@ -596,16 +610,34 @@ struct Dict[K: KeyElement, V: CollectionElement]( An optional value containing a copy of the value if it was present, otherwise an empty Optional. """ + try: # TODO(MOCO-604): push usage through + return self._find_ref(key)[] + except: + return None + + # TODO(MOCO-604): Return Optional[Reference] instead of raising + fn _find_ref( + self: Reference[Self, _, _], key: K + ) raises -> Reference[V, self.is_mutable, self.lifetime]: + """Find a value in the dictionary by key. + + Args: + key: The key to search for in the dictionary. + + Returns: + An optional value containing a reference to the value if it is + present, otherwise an empty Optional. + """ var hash = hash(key) var found: Bool var slot: Int var index: Int - found, slot, index = self._find_index(hash, key) + found, slot, index = self[]._find_index(hash, key) if found: - var ev = self._entries.__get_ref(index)[] - debug_assert(ev.__bool__(), "entry in index must be full") - return ev.value()[].value - return None + var entry = self[]._entries.__get_ref(index) + debug_assert(entry[].__bool__(), "entry in index must be full") + return Reference(entry[].value()[].value) + raise "KeyError" fn get(self, key: K) -> Optional[V]: """Get a value from the dictionary by key. @@ -654,11 +686,12 @@ struct Dict[K: KeyElement, V: CollectionElement]( found, slot, index = self._find_index(hash, key) if found: self._set_index(slot, Self.REMOVED) - var entry = self._entries.__get_ref(index)[] - self._entries[index] = None + var entry = self._entries.__get_ref(index) + var entry_value = entry[].unsafe_take() + entry[] = None self.size -= 1 - debug_assert(entry.__bool__(), "entry in index must be full") - return entry.value()[].value + debug_assert(entry[].__bool__(), "entry in index must be full") + return entry_value.value^ elif default: return default.value()[] raise "KeyError" @@ -797,10 +830,12 @@ struct Dict[K: KeyElement, V: CollectionElement]( elif index == Self.REMOVED: return (False, slot, self._n_entries) else: - var ev = self._entries.__get_ref(index)[] - debug_assert(ev.__bool__(), "entry in index must be full") - var entry = ev.value()[] - if hash == entry.hash and key == entry.key: + var entry = self._entries.__get_ref(index) + debug_assert(entry[].__bool__(), "entry in index must be full") + if ( + hash == entry[].value()[].hash + and key == entry[].value()[].key + ): return (True, slot, index) self._next_index_slot(slot, perturb) @@ -823,9 +858,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._entries = self._new_entries(self._reserved) for i in range(len(old_entries)): - var entry = old_entries.__get_ref(i)[] - if entry: - self._insert(entry.value()[]) + var entry = old_entries.__get_ref(i) + if entry[]: + self._insert(entry[].unsafe_take()) fn _compact(inout self): self._index = _DictIndex(self._reserved) @@ -834,13 +869,14 @@ struct Dict[K: KeyElement, V: CollectionElement]( while not self._entries.__get_ref(right)[]: right += 1 debug_assert(right < self._reserved, "Invalid dict state") - var entry = self._entries.__get_ref(right)[] - debug_assert(entry.__bool__(), "Logic error") - var slot = self._find_empty_index(entry.value()[].hash) + var entry = self._entries.__get_ref(right) + debug_assert(entry[].__bool__(), "Logic error") + var slot = self._find_empty_index(entry[].value()[].hash) self._set_index(slot, left) if left != right: - self._entries[left] = entry - self._entries[right] = None + self._entries[left] = entry[].unsafe_take() + entry[] = None + right += 1 self._n_entries = self.size diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 88dc58a15b..4b8335192d 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -128,7 +128,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): debug_assert(self.__bool__(), ".value() on empty Optional") return self._value[T] - fn unsafe_take(owned self) -> T: + fn unsafe_take(inout self) -> T: """Unsafely move the value out of the Optional. The caller takes ownership over the new value, and the Optional is diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 71f496e8dd..3675b60284 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -227,7 +227,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): unroll[each, len(VariadicList(Ts))]() - fn unsafe_take[T: CollectionElement](owned self) -> T: + fn unsafe_take[T: CollectionElement](inout self) -> T: """Take the current value of the variant as the provided type. The caller takes ownership of the underlying value. The variant diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index a7e4e8b4f7..27fd1523ec 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -97,6 +97,18 @@ def test_compact(): assert_equal(0, len(dict)) +def test_compact_with_elements(): + var dict = Dict[String, Int]() + for i in range(5): + var key = "key" + str(i) + dict[key] = i + 1 + for i in range(5, 20): + var key = "key" + str(i) + dict[key] = i + 1 + _ = dict.pop(key) + assert_equal(5, len(dict)) + + def test_pop_default(): var dict = Dict[String, Int]() dict["a"] = 1 @@ -228,8 +240,10 @@ def test_dict_copy_calls_copy_constructor(): var copy = Dict(orig) # I _may_ have thoughts about where our performance issues # are coming from :) - assert_equal(4, orig["a"].copy_count) - assert_equal(5, copy["a"].copy_count) + assert_equal(1, orig["a"].copy_count) + assert_equal(2, copy["a"].copy_count) + assert_equal(0, orig.__get_ref("a")[].copy_count) + assert_equal(1, copy.__get_ref("a")[].copy_count) def test_dict_update_nominal(): @@ -385,6 +399,7 @@ def test_dict(): test["test_multiple_resizes", test_multiple_resizes]() test["test_big_dict", test_big_dict]() test["test_compact", test_compact]() + test["test_compact_with_elements", test_compact_with_elements]() test["test_pop_default", test_pop_default]() test["test_key_error", test_key_error]() test["test_iter", test_iter]() @@ -458,8 +473,8 @@ def test_owned_kwargs_dict(): def test_find_get(): var some_dict = Dict[String, Int]() some_dict["key"] = 1 - assert_equal(some_dict.find("key").unsafe_take(), 1) - assert_equal(some_dict.get("key").unsafe_take(), 1) + assert_equal(some_dict.find("key").value()[], 1) + assert_equal(some_dict.get("key").value()[], 1) assert_equal(some_dict.find("not_key").or_else(0), 0) assert_equal(some_dict.get("not_key", 0), 0) diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index ecc75854b7..9cecda2424 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -50,7 +50,7 @@ def test_basic(): assert_equal(1, a1) assert_equal(2, b1) - assert_equal(1, (a^).unsafe_take()) + assert_equal(1, a.unsafe_take()) # TODO: this currently only checks for mutable references. # We may want to come back and add an immutable test once From ba704a9b12cab76f3bceb3a4e85a83b6c503d650 Mon Sep 17 00:00:00 2001 From: Peyman Barazandeh Date: Thu, 9 May 2024 22:20:46 -0500 Subject: [PATCH 0500/2019] [External] [stdlib] Refactor SIMD tests to directly call __floordiv__ (#39730) [External] [stdlib] Refactor SIMD tests to directly call __floordiv__ Modify the SIMD tests for the `__floordiv__` to directly invoke this dunder method instead of using the // operator. This approach is recommended for unit testing magic methods of basic numeric types to prevent unintentional implicit conversions. Co-authored-by: Peyman Barazandeh Closes modularml/mojo#2602 MODULAR_ORIG_COMMIT_REV_ID: 202bd98f7a272895409df7152d3827754b84d216 --- stdlib/test/builtin/test_simd.mojo | 41 +++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 90af9a000f..9ce121c28a 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -209,18 +209,35 @@ def test_roundeven(): def test_floordiv(): - assert_equal(Int32(2) // Int32(2), 1) - assert_equal(Int32(2) // Int32(3), 0) - assert_equal(Int32(2) // Int32(-2), -1) - assert_equal(Int32(99) // Int32(-2), -50) - - assert_equal(UInt32(2) // UInt32(2), 1) - assert_equal(UInt32(2) // UInt32(3), 0) - - assert_equal(Float32(2) // Float32(2), 1) - assert_equal(Float32(2) // Float32(3), 0) - assert_equal(Float32(2) // Float32(-2), -1) - assert_equal(Float32(99) // Float32(-2), -50) + assert_equal(Int32(2).__floordiv__(2), 1) + assert_equal(Int32(2).__floordiv__(Int32(2)), 1) + assert_equal(Int32(2).__floordiv__(Int32(3)), 0) + + assert_equal(Int32(2).__floordiv__(-2), -1) + assert_equal(Int32(2).__floordiv__(Int32(-2)), -1) + assert_equal(Int32(99).__floordiv__(Int32(-2)), -50) + + assert_equal(UInt32(2).__floordiv__(2), 1) + assert_equal(UInt32(2).__floordiv__(UInt32(2)), 1) + assert_equal(UInt32(2).__floordiv__(UInt32(3)), 0) + + assert_equal(Float32(2).__floordiv__(2), 1) + assert_equal(Float32(2).__floordiv__(Float32(2)), 1) + assert_equal(Float32(2).__floordiv__(Float32(3)), 0) + + assert_equal(Float32(2).__floordiv__(-2), -1) + assert_equal(Float32(2).__floordiv__(Float32(-2)), -1) + assert_equal(Float32(99).__floordiv__(Float32(-2)), -50) + + alias I = SIMD[DType.int32, 4] + var i = I(2, 4, -2, -4) + assert_equal(i.__floordiv__(2), I(1, 2, -1, -2)) + assert_equal(i.__floordiv__(Int32(2)), I(1, 2, -1, -2)) + + alias F = SIMD[DType.float32, 4] + var f = F(3, -4, 1, 5) + assert_equal(f.__floordiv__(3), F(1, -2, 0, 1)) + assert_equal(f.__floordiv__(Float32(3)), F(1, -2, 0, 1)) def test_rfloordiv(): From 51bef53d1274e8eaee377f4a2e9cd944081990b0 Mon Sep 17 00:00:00 2001 From: Helehex Date: Thu, 9 May 2024 23:09:20 -0500 Subject: [PATCH 0501/2019] [External] [stdlib] add `Comparable` trait (#39732) [External] [stdlib] Add `Comparable` trait Add a `Comparable` trait for comparison testing conformance. Explicitly conform `FloatLiteral`, `IntLiteral`, `Int`, and `Set`. Co-authored-by: Helehex Closes modularml/mojo#2517 MODULAR_ORIG_COMMIT_REV_ID: 23fe6cfb61861f4115d38fa195876bce48f248cd --- stdlib/src/builtin/comparable.mojo | 60 +++++++++++++++++++++++++++ stdlib/src/builtin/float_literal.mojo | 2 +- stdlib/src/builtin/int.mojo | 1 + stdlib/src/builtin/int_literal.mojo | 2 +- stdlib/src/collections/set.mojo | 2 +- 5 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 stdlib/src/builtin/comparable.mojo diff --git a/stdlib/src/builtin/comparable.mojo b/stdlib/src/builtin/comparable.mojo new file mode 100644 index 0000000000..7d74b062fd --- /dev/null +++ b/stdlib/src/builtin/comparable.mojo @@ -0,0 +1,60 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + + +trait Comparable(EqualityComparable): + """A type which can be compared with other instances of itself.""" + + fn __lt__(self, rhs: Self) -> Bool: + """Define whether `self` is less than `rhs`. + + Args: + rhs: The right hand side of the comparison. + + Returns: + True if `self` is less than `rhs`. + """ + ... + + fn __le__(self, rhs: Self) -> Bool: + """Define whether `self` is less than or equal to `rhs`. + + Args: + rhs: The right hand side of the comparison. + + Returns: + True if `self` is less than or equal to `rhs`. + """ + ... + + fn __gt__(self, rhs: Self) -> Bool: + """Define whether `self` is greater than `rhs`. + + Args: + rhs: The right hand side of the comparison. + + Returns: + True if `self` is greater than `rhs`. + """ + ... + + fn __ge__(self, rhs: Self) -> Bool: + """Define whether `self` is greater than or equal to `rhs`. + + Args: + rhs: The right hand side of the comparison. + + Returns: + True if `self` is greater than or equal to `rhs`. + """ + ... diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 8c9a4bcf28..624827f110 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -30,7 +30,7 @@ struct FloatLiteral( Boolable, Ceilable, CeilDivable, - EqualityComparable, + Comparable, Floorable, Intable, Stringable, diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 662ee0b331..1491319880 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -198,6 +198,7 @@ struct Int( Boolable, Ceilable, CeilDivable, + Comparable, Floorable, Formattable, Intable, diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index b04ddaf395..d7c06132c9 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -23,7 +23,7 @@ struct IntLiteral( Boolable, Ceilable, CeilDivable, - EqualityComparable, + Comparable, Floorable, Intable, Roundable, diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index d653b40aa2..ab629c121a 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -15,7 +15,7 @@ from .dict import Dict, KeyElement, _DictEntryIter, _DictKeyIter -struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): +struct Set[T: KeyElement](Sized, Comparable, Hashable, Boolable): """A set data type. O(1) average-case amortized add, remove, and membership check. From e1f6e5ea8915b05fd2238605fd66be5c69b830c0 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 10 May 2024 09:29:22 -0400 Subject: [PATCH 0502/2019] [stdlib] Moved Constructors to the top and fixed formats MODULAR_ORIG_COMMIT_REV_ID: 523b0571580544d0f8728d645b5954c06ab121ef --- stdlib/src/utils/variant.mojo | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 3675b60284..a3a9f8ac2f 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -145,18 +145,6 @@ struct Variant[*Ts: CollectionElement](CollectionElement): ] var _impl: Self._type - fn _get_ptr[T: CollectionElement](self) -> UnsafePointer[T]: - constrained[ - Self._check[T]() != Self._sentinel, "not a union element type" - ]() - return UnsafePointer.address_of(self._impl).bitcast[T]() - - fn _get_state( - self: Reference[Self, _, _] - ) -> Reference[Int8, self.is_mutable, self.lifetime]: - var int8_self = UnsafePointer(self).bitcast[Int8]() - return (int8_self + _UnionSize[Ts].compute())[] - fn __init__[T: CollectionElement](inout self, owned value: T): """Create a variant with one of the types. @@ -171,7 +159,6 @@ struct Variant[*Ts: CollectionElement](CollectionElement): self._get_state()[] = Self._check[T]() initialize_pointee_move(self._get_ptr[T](), value^) - @always_inline fn __copyinit__(inout self, other: Self): """Creates a deep copy of an existing variant. @@ -192,7 +179,6 @@ struct Variant[*Ts: CollectionElement](CollectionElement): unroll[each, len(VariadicList(Ts))]() - @always_inline fn __moveinit__(inout self, owned other: Self): """Move initializer for the variant. @@ -215,6 +201,18 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """Destroy the variant.""" self._call_correct_deleter() + fn _get_ptr[T: CollectionElement](self) -> UnsafePointer[T]: + constrained[ + Self._check[T]() != Self._sentinel, "not a union element type" + ]() + return UnsafePointer.address_of(self._impl).bitcast[T]() + + fn _get_state( + self: Reference[Self, _, _] + ) -> Reference[Int8, self.is_mutable, self.lifetime]: + var int8_self = UnsafePointer(self).bitcast[Int8]() + return (int8_self + _UnionSize[Ts].compute())[] + @always_inline fn _call_correct_deleter(inout self): @parameter @@ -247,9 +245,8 @@ struct Variant[*Ts: CollectionElement](CollectionElement): debug_assert( Self._check[T]() == self._get_state()[], "taking wrong type" ) - self._get_state()[] = ( - Self._sentinel - ) # don't call the variant's deleter later + # don't call the variant's deleter later + self._get_state()[] = Self._sentinel return move_from_pointee(self._get_ptr[T]()) fn set[T: CollectionElement](inout self, owned value: T): From b9ee9062558a595a1a2b9cc507a606f14684b620 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 10 May 2024 09:29:39 -0400 Subject: [PATCH 0503/2019] [stdlib] Removed `_get_ptr` in `test_variant.mojo` This is an internal only implementation detail, not something that should be tested. MODULAR_ORIG_COMMIT_REV_ID: 45d09e96eb287a040e32dc4df2507fcf32332290 --- stdlib/test/utils/test_variant.mojo | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index d1dba611e9..7b01e6758e 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -186,12 +186,6 @@ def test_get_returns_mutable_reference(): v2[String] = "something else" assert_equal(v2[String], "something else") - var v3: Variant[Int, String] = 4 - assert_equal( - int(v3._get_ptr[Int]()), - int(Reference(v3)[]._get_ptr[Int]()), - ) - def main(): test_basic() From 921470eae17f19effb5eabb0fd0423b6bcdda300 Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Fri, 10 May 2024 08:40:21 -0500 Subject: [PATCH 0504/2019] [External] [Proposal] Improve the hash module (#39553) [External] [Proposal] Improve the hash module This proposal is based on discussion started in https://github.com/modularml/mojo/issues/1744 Co-authored-by: Maxim Zaks Closes modularml/mojo#2250 MODULAR_ORIG_COMMIT_REV_ID: 692c7d5940b8c88e83ef895b0be26a33a06ad941 --- proposals/improved-hash-module.md | 294 ++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 proposals/improved-hash-module.md diff --git a/proposals/improved-hash-module.md b/proposals/improved-hash-module.md new file mode 100644 index 0000000000..2d8714b0c1 --- /dev/null +++ b/proposals/improved-hash-module.md @@ -0,0 +1,294 @@ +# Improve hash module + +Current implementation of the hash module in standard library reflex the +implementation of the Python hash module, which by itself is a good idea, +but it has some flaws, which we should correct in Mojo. + +## Flaws of the `Hashable` trait + +The `Hashable` trait is designed as following: + +```mojo +trait Hashable: + fn __hash__(self) -> Int: + ... +``` + +Which implies that a developer, who writes a new hashable struct needs +to return an `Int`. + +- This API does not provide guidance to how this `Int` value needs to be computed +- It is impossible to exchange hashing algorithms on demand +- `Int` as a type is variable length (based on the CPU arch), which might cause issues +- Such trait design follows the call return principle which, when applied on complex +types, will lead to unnecessary computations and memory allocations + +## Example + +```mojo +@value +struct Person(Hashable): + var name: String + var age: UInt8 + var friends_names: List[String] + + fn __hash__(self) -> Int: + var hashes = List[Int]() + hashes.append(hash(self.name)) + hashes.append(hash(self.age)) + for friend in self.friends_names: + hashes.append(hash(friend[])) + + # How to combine a hash of hashes ??? +``` + +As you can see above we, computed hashes for all of the struct fields, +but we are uncertain how to combine those values in a way which produces +a good (non compromised) hash value. Python [docs](https://docs.python.org/3/reference/datamodel.html#object.__hash__) +suggest to pack fileds into a tuple and hash the tuple, but this is not +possible at this point in time in Mojo. + +## Proposal + +In order to improve the hash module and address the flaws of the `Hashable` +trait, we need to apply following steps. + +## Introduce a `Hasher` trait + +By introducing a `Hasher` trait we define an abstraction for the hashing algorithm +itself, which allows streaming creation of the hash value. Below is a possible API +of the `Hashable` trait. + +```mojo +trait Hasher: + """Trait which every hash function implementer needs to implement.""" + fn __init__(inout self): + """Expects a no argument instantiation.""" + ... + fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + """Conribute to the hash value based on a sequence of bytes. Use only for complex types which are not just a composition of Hashable types.""" + ... + fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + """Contribute to the hash value with a compile time know fix size value. Used inside of std lib to avoid runtime branching.""" + ... + fn update[T: Hashable](inout self, value: T): + """Contribute to the hash value with a Hashable value. Should be used by implementors of Hashable types which are a composition of Hashable types.""" + ... + fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: + """Used internally to generate the final hash value, should be simplified to `_finish(owned self) -> Scalar[hash_value_dt]` + once trait declarations support parameters and we can switch to `trait Hasher[hash_value_dt: DType]`. + This is beneficial as hash functions have different implementations based on the type """ + ... +``` + +## Implement a default `Hasher` in standard library + +The standard library should provide a default `Hasher` implementation, +but it would be possible for the developers to implement, or choose other +hash algorithms, if they better fit their use case. + +Bellow you can see a dummy implementation of a `DefaultHasher` + +```mojo +struct DefaultHasher(Hasher): + var hash: UInt64 + + fn __init__(inout self): + self.hash = 42 + fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + ... + fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + ... + fn update[T: Hashable](inout self, value: T): + ... + fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: + return self.hash.cast[dt]() +``` + +## Redesign the `Hashable` trait to follow the data flow principles + +Given the `Hasher` trait, we can define the `Hashable` trait to adopt the +data flow paradigm instead of call return. + +```mojo +trait Hashable: + fn hash_with[H: Hasher](self, inout hasher: H): + ... +``` + +## Example for `Hashable` struct implementation + +The implementation of `Hashable`, where all the fields are `Hashable` is trivial +and could be easily synthesized by the compiler. But even if not, the data flow +API guides the developer toward a very simple solution. + +```mojo +@value +struct Person(Hashable): + var name: String + var age: Int + + fn __hash__[H: Hasher](self, inout hasher: H): + hasher.update(self.name) + hasher.update(self.age) +``` + +## Parameterized the `hash` function with `Hasher` type + +The `hash` function can be parameterized with the `Hasher` type, which gives the +users control over the hashing algorithm. + +```mojo +fn hash[T: Hashable, H: Hasher = DefaultHasher](value: T) -> UInt64: + var hasher = hasher_type() + hasher.update(value) + return hasher^._finish() +``` + +## Prove of concept + +Bellow you can find a fully working POC implementation: + +```mojo +from os.env import getenv, setenv +from random import random_si64 + +trait Hashable: + """Trait which every hashable type needs to implement.""" + fn __hash__[H: Hasher](self, inout hasher: H): + ... + +trait Hasher: + """Trait which every hash function implementer needs to implement.""" + fn __init__(inout self): + """Expects a no argument instantiation.""" + ... + fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + """Conribute to the hash value based on a sequence of bytes. Use only for complex types which are not just a composition of Hashable types.""" + ... + fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + """Contribute to the hash value with a compile time know fix size value. Used inside of std lib to avoid runtime branching.""" + ... + fn update[T: Hashable](inout self, value: T): + """Contribute to the hash value with a Hashable value. Should be used by implementors of Hashable types which are a composition of Hashable types.""" + ... + fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: + """Used internally to generate the final hash value, should be simplified to `_finish(owned self) -> Scalar[hash_value_dt]` + once trait declarations support parameters and we can switch to `trait Hasher[hash_value_dt: DType]`. + This is beneficial as hash functions have different implementations based on the type """ + ... + +@value +struct MyInt(Hashable): + """An example for the Int type.""" + var value: Int + + @always_inline + fn __hash__[H: Hasher](self, inout hasher: H): + hasher._update_with_simd(Int64(self.value)) + +@value +struct MyString(Hashable): + """An example for the String type.""" + var value: StringLiteral + + @always_inline + fn __hash__[H: Hasher](self, inout hasher: H): + hasher.update(MyInt(len(self.value))) + hasher._update_with_bytes(self.value.data().bitcast[DType.uint8](), len(self.value)) + +@value +struct Person(Hashable): + """An example for a type composing Hashable types.""" + var name: MyString + var age: MyInt + + fn __hash__[H: Hasher](self, inout hasher: H): + hasher.update(self.name) + hasher.update(self.age) + +alias DefaultHasher = DJBX33A_Hasher[0] + +@always_inline +fn my_hash[V: Hashable, hasher_type: Hasher = DefaultHasher](value: V) -> UInt64: + """Example how the `hash` function should look like.""" + var hasher = hasher_type() + hasher.update(value) + return hasher^._finish() + +@always_inline +fn _DJBX33A_SECRET() -> UInt64: + """Example how secret and seed can be stored and retrieved.""" + try: + var secret_string = getenv("DJBX33A_SECRET", "") + return bitcast[DType.uint64](Int64(int(secret_string))) + except: + var value = random_si64(Int64.MIN, Int64.MAX) + _ = setenv("DJBX33A_SECRET", str(value)) + return bitcast[DType.uint64](value) + +struct DJBX33A_Hasher[custom_secret: UInt64 = 0](Hasher): + """Example of a simple Hasher, with an option to provide a custom secret at compile time. + When custom secret is set to 0 the secret will be looked up in env var DJBX33A_SECRET. + In case env var DJBX33A_SECRET is not set a random int will be generated.""" + var hash_data: UInt64 + var secret: UInt64 + + @always_inline + fn __init__(inout self): + self.hash_data = 5361 + @parameter + if custom_secret != 0: + self.secret = custom_secret + else: + self.secret = _DJBX33A_SECRET() + + @always_inline + fn _update_with_bytes(inout self, bytes: DTypePointer[DType.uint8], n: Int): + """The algorithm is not optimal.""" + for i in range(n): + self.hash_data = self.hash_data * 33 + bytes.load(i).cast[DType.uint64]() + + @always_inline + fn _update_with_simd[dt: DType, size: Int](inout self, value: SIMD[dt, size]): + """The algorithm is not optimal.""" + alias size_in_bytes = size * dt.sizeof() + var bytes = bitcast[DType.uint8, size_in_bytes](value) + @unroll + for i in range(size_in_bytes): + self.hash_data = self.hash_data * 33 + bytes[i].cast[DType.uint64]() + + @always_inline + fn update[T: Hashable](inout self, value: T): + value.__hash__(self) + + @always_inline + fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: + return (self.hash_data ^ self.secret).cast[dt]() + +fn main() raises: + var p = Person("Maxim", 43) + print(p.name.value, p.age.value) + + var hasher = DJBX33A_Hasher() + p.age.__hash__(hasher) + print("My hasher 43", hasher^._finish()) + print("Std hash 43", hash(p.age.value)) + + hasher = DJBX33A_Hasher() + p.__hash__(hasher) + print("Person", hasher^._finish()) + + var h1 = my_hash(p) + var h2 = my_hash[hasher_type=DJBX33A_Hasher[77777]](p) + var h3 = my_hash(p) + print("Person", h1, h2, h3) +``` + +## Compiler limitations + +Current compiler does not allow parameters on trait definition. +A parametrization on Hasher trait for for hash value dtype would be +beneficial as a hashing algorithm might differ. +For example in [Fowler–Noll–Vo hash function](https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function#FNV_hash_parameters) +parameters prime and offset basis dependend on hash value width. From 220643c40771e17f4c77c8da3108478fbf3fee25 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Fri, 10 May 2024 08:46:51 -0500 Subject: [PATCH 0505/2019] [External] [stdlib] Add `InlineList` struct (stack-allocated List) (#39560) [External] [stdlib] Add `InlineList` struct (stack-allocated List) This struc is very useful to implement SSO, it's related to * https://github.com/modularml/mojo/issues/2467 * https://github.com/modularml/mojo/pull/2507 If this is merged, I can take advantage of this in my PR that has the SSO POC About `InlineFixedVector`: `InlineList` is different. Notably, `InlineList` have its capacity decided at compile-time, and there is no heap allocation (unless the elements have heap-allocated data of course). `InlineFixedVector` stores the first N element on the stack, and the next elements on the heap. Since not all elements are not in the same spot, it makes it hard to work with pointers there as the data is not contiguous. Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2587 MODULAR_ORIG_COMMIT_REV_ID: 86df7b19f0f38134fbaeb8a23fe9aef27e47c554 --- stdlib/src/collections/__init__.mojo | 1 + stdlib/src/collections/inline_list.mojo | 98 +++++++++++++++++++ stdlib/src/utils/static_tuple.mojo | 18 +++- stdlib/test/collections/test_inline_list.mojo | 96 ++++++++++++++++++ 4 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 stdlib/src/collections/inline_list.mojo create mode 100644 stdlib/test/collections/test_inline_list.mojo diff --git a/stdlib/src/collections/__init__.mojo b/stdlib/src/collections/__init__.mojo index cca96b5763..ce76accb8c 100644 --- a/stdlib/src/collections/__init__.mojo +++ b/stdlib/src/collections/__init__.mojo @@ -13,6 +13,7 @@ """Implements the collections package.""" from .dict import Dict, KeyElement +from .inline_list import InlineList from .list import List from .optional import Optional, OptionalReg from .set import Set diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo new file mode 100644 index 0000000000..c2dd0432e9 --- /dev/null +++ b/stdlib/src/collections/inline_list.mojo @@ -0,0 +1,98 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Defines the `InlineList` type. + +You can import these APIs from the `collections` package. For example: + +```mojo +from collections import InlineList +``` +""" + +from utils import InlineArray + +# ===----------------------------------------------------------------------===# +# InlineList +# ===----------------------------------------------------------------------===# + + +# TODO: Provide a smarter default for the capacity. +struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): + """A list allocated on the stack with a maximum size known at compile time. + + It is backed by an `InlineArray` and an `Int` to represent the size. + This struct has the same API as a regular `List`, but it is not possible to change the + capacity. In other words, it has a fixed maximum size. + + This is typically faster than a `List` as it is only stack-allocated and does not require + any dynamic memory allocation. + + Parameters: + ElementType: The type of the elements in the list. + capacity: The maximum number of elements that the list can hold. + """ + + var _array: InlineArray[ElementType, capacity] + var _size: Int + + @always_inline + fn __init__(inout self): + """This constructor creates an empty InlineList.""" + self._array = InlineArray[ElementType, capacity](uninitialized=True) + self._size = 0 + + @always_inline + fn __len__(self) -> Int: + """Returns the length of the list.""" + return self._size + + @always_inline + fn append(inout self, owned value: ElementType): + """Appends a value to the list. + + Args: + value: The value to append. + """ + debug_assert(self._size < capacity, "List is full.") + self._array[self._size] = value^ + self._size += 1 + + @always_inline + fn __refitem__[ + IntableType: Intable, + ](self: Reference[Self, _, _], index: IntableType) -> Reference[ + Self.ElementType, self.is_mutable, self.lifetime + ]: + """Get a `Reference` to the element at the given index. + + Args: + index: The index of the item. + + Returns: + A reference to the item at the given index. + """ + var i = int(index) + debug_assert( + -self[]._size <= i < self[]._size, "Index must be within bounds." + ) + + if i < 0: + i += len(self[]) + + return self[]._array[i] + + @always_inline + fn __del__(owned self): + """Destroy all the elements in the list and free the memory.""" + for i in range(self._size): + destroy_pointee(UnsafePointer(self._array[i])) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index ded0216113..cf612df27f 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -278,11 +278,23 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): @always_inline fn __init__(inout self, *, uninitialized: Bool): - """Constructs an empty with uninitized data. + """Create an InlineArray with uninitialized memory. + + Note that this is highly unsafe and should be used with caution. + + We recommend to use the `InlineList` instead if all the objects + are not available when creating the array. + + If despite those workarounds, one still needs an uninitialized array, + it is possible with: + + ```mojo + var uninitialized_array = InlineArray[Int, 10](uninitialized=True) + ``` Args: - uninitialized: Unused, exists just to make uninitialized - case explicit. + uninitialized: A boolean to indicate if the array should be initialized. + Always set to `True` (it's not actually used inside the constructor). """ self._array = __mlir_op.`kgen.undef`[_type = Self.type]() diff --git a/stdlib/test/collections/test_inline_list.mojo b/stdlib/test/collections/test_inline_list.mojo new file mode 100644 index 0000000000..af97f655e9 --- /dev/null +++ b/stdlib/test/collections/test_inline_list.mojo @@ -0,0 +1,96 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from collections import InlineList, Set +from testing import assert_equal, assert_false, assert_true, assert_raises +from test_utils import MoveCounter + + +def test_list(): + var list = InlineList[Int]() + + for i in range(5): + list.append(i) + + assert_equal(5, len(list)) + assert_equal(0, list[0]) + assert_equal(1, list[1]) + assert_equal(2, list[2]) + assert_equal(3, list[3]) + assert_equal(4, list[4]) + + assert_equal(0, list[-5]) + assert_equal(3, list[-2]) + assert_equal(4, list[-1]) + + list[2] = -2 + assert_equal(-2, list[2]) + + list[-5] = 5 + assert_equal(5, list[-5]) + list[-2] = 3 + assert_equal(3, list[-2]) + list[-1] = 7 + assert_equal(7, list[-1]) + + +def test_append_triggers_a_move(): + var inline_list = InlineList[MoveCounter[Int], capacity=32]() + + var nb_elements_to_add = 8 + for index in range(nb_elements_to_add): + inline_list.append(MoveCounter(index)) + + # Using .append() should trigger a move and not a copy+delete. + for i in range(nb_elements_to_add): + assert_equal(inline_list[i].move_count, 1) + + +@value +struct ValueToCountDestructor(CollectionElement): + var value: Int + var destructor_counter: UnsafePointer[List[Int]] + + fn __del__(owned self): + self.destructor_counter[].append(self.value) + + +def test_destructor(): + """Ensure we delete the right number of elements.""" + var destructor_counter = List[Int]() + alias capacity = 32 + var inline_list = InlineList[ValueToCountDestructor, capacity=capacity]() + + for index in range(capacity): + inline_list.append( + ValueToCountDestructor(index, UnsafePointer(destructor_counter)) + ) + + # Private api use here: + inline_list._size = 8 + + # This is the last use of the inline list, so it should be destroyed here, along with each element. + # It's important that we only destroy the first 8 elements, and not the 32 elements. + # This is because we assume that the last 24 elements are not initialized (not true in this case, + # but if we ever run the destructor on the fake 24 uninitialized elements, + # it will be accounted for in destructor_counter). + assert_equal(len(destructor_counter), 8) + for i in range(8): + assert_equal(destructor_counter[i], i) + + +def main(): + test_list() + test_append_triggers_a_move() + test_destructor() From 8590f0f7a1e4d8013a76be8e7138f7d17b65f09a Mon Sep 17 00:00:00 2001 From: Artemio Garza Reyna Date: Fri, 10 May 2024 09:16:12 -0500 Subject: [PATCH 0506/2019] [External] [stdlib] Implement `mkdir` and `rmdir` (#39230) [External] [stdlib] Implement `mkdir` and `rmdir` Add functionality to the `os` module for creating and removing directories via `mkdir` and `rmdir`. Signed-off-by: Artemio Garza Reyna Co-authored-by: Artemio Garza Reyna Closes modularml/mojo#2430 MODULAR_ORIG_COMMIT_REV_ID: 8571848227dfd72f1672699a227e21354d5cf3e1 --- docs/changelog.md | 4 + stdlib/src/os/__init__.mojo | 12 ++- stdlib/src/os/os.mojo | 65 +++++++++++- stdlib/test/os/test_mkdir_and_rmdir.mojo | 122 +++++++++++++++++++++++ 4 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 stdlib/test/os/test_mkdir_and_rmdir.mojo diff --git a/docs/changelog.md b/docs/changelog.md index a70c944dd2..41bd6f3a0e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -137,6 +137,10 @@ what we publish. - Debugger users can now set breakpoints on function calls in O0 builds even if the call has been inlined by the compiler. +- The `os` module now provides functionalty for adding and removing directories + using `mkdir` and `rmdir`. + ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index 634a38eaf2..771d2e22fb 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -15,5 +15,15 @@ from .atomic import Atomic from .env import setenv, getenv from .fstat import lstat, stat, stat_result -from .os import abort, listdir, remove, unlink, SEEK_SET, SEEK_CUR, SEEK_END +from .os import ( + abort, + listdir, + remove, + unlink, + SEEK_SET, + SEEK_CUR, + SEEK_END, + mkdir, + rmdir, +) from .pathlike import PathLike diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 5abd23157f..06f14b3913 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -277,7 +277,7 @@ fn remove(path: String) raises: path: The path to the file. """ - var error = external_call["unlink", Int](path.unsafe_ptr()) + var error = external_call["unlink", Int32](path.unsafe_ptr()) if error != 0: # TODO get error message, the following code prints it @@ -327,3 +327,66 @@ fn unlink[pathlike: os.PathLike](path: pathlike) raises: """ remove(path.__fspath__()) + + +# ===----------------------------------------------------------------------=== # +# mkdir/rmdir +# ===----------------------------------------------------------------------=== # + + +fn mkdir(path: String, mode: Int = 0o777) raises: + """Creates a directory at the specified path. + If the directory can not be created an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Args: + path: The path to the directory. + mode: The mode to create the directory with. + """ + + var error = external_call["mkdir", Int32](path.unsafe_ptr(), mode) + if error != 0: + raise Error("Can not create directory: " + path) + + +fn mkdir[pathlike: os.PathLike](path: pathlike, mode: Int = 0o777) raises: + """Creates a directory at the specified path. + If the directory can not be created an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Parameters: + pathlike: The a type conforming to the os.PathLike trait. + + Args: + path: The path to the directory. + mode: The mode to create the directory with. + """ + + mkdir(path.__fspath__(), mode) + + +fn rmdir(path: String) raises: + """Removes the specified directory. + If the path is not a directory or it can not be deleted, an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Args: + path: The path to the directory. + """ + var error = external_call["rmdir", Int32](path.unsafe_ptr()) + if error != 0: + raise Error("Can not remove directory: " + path) + + +fn rmdir[pathlike: os.PathLike](path: pathlike) raises: + """Removes the specified directory. + If the path is not a directory or it can not be deleted, an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Parameters: + pathlike: The a type conforming to the os.PathLike trait. + + Args: + path: The path to the directory. + """ + rmdir(path.__fspath__()) diff --git a/stdlib/test/os/test_mkdir_and_rmdir.mojo b/stdlib/test/os/test_mkdir_and_rmdir.mojo new file mode 100644 index 0000000000..ff39af36a1 --- /dev/null +++ b/stdlib/test/os/test_mkdir_and_rmdir.mojo @@ -0,0 +1,122 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from os import mkdir, rmdir, remove +from os.path import exists +from pathlib import Path + +from testing import assert_true, assert_false, assert_raises + + +fn create_dir_and_test_delete_string[ + func_create: fn (String, Int) raises -> None, + func_delete: fn (String) raises -> None, +](dir_name: String) raises: + # verify that the test dir does not exist before starting the test + assert_false( + exists(dir_name), + "Unexpected dir " + dir_name + " it should not exist", + ) + + func_create(dir_name, 0o777) + assert_true(exists(dir_name)) + + func_delete(dir_name) + # trying to delete non existing dir + with assert_raises(contains="Can not remove directory: "): + func_delete(dir_name) + + +fn create_dir_and_test_delete_path[ + func_create: fn[pathlike: PathLike] (pathlike, Int) raises -> None, + func_delete: fn[pathlike: PathLike] (pathlike) raises -> None, +](dir_path: Path) raises: + # verify that the test dir does not exist before starting the test + assert_false( + exists(dir_path), + "Unexpected dir " + dir_path.__fspath__() + " it should not exist", + ) + + func_create(dir_path, 0o777) + assert_true(exists(dir_path)) + + func_delete(dir_path) + # trying to delete non existing dir + with assert_raises(contains="Can not remove directory: "): + func_delete(dir_path) + + +fn test_mkdir_and_rmdir() raises: + var cwd_path = Path() + var my_dir_path = cwd_path / "my_dir" + var my_dir_name = str(my_dir_path) + + create_dir_and_test_delete_path[mkdir, rmdir](my_dir_path) + create_dir_and_test_delete_string[mkdir, rmdir](my_dir_name) + + # test relative path + create_dir_and_test_delete_string[mkdir, rmdir]("my_relative_dir") + create_dir_and_test_delete_path[mkdir, rmdir](Path("my_relative_dir")) + + +fn test_mkdir_mode() raises: + var cwd_path = Path() + var my_dir_path = cwd_path / "my_dir" + var file_name = my_dir_path / "file.txt" + + assert_false( + exists(my_dir_path), + "Unexpected dir " + my_dir_path.__fspath__() + " it should not exist", + ) + + # creating dir without writing permission + mkdir(my_dir_path, 0o111) + + # TODO: This test is failing on Graviton internally in CI, revisit. + # with assert_raises(contains="Permission denied"): + # var file = open(file_name, "w") + # file.close() + # if exists(file_name): + # remove(file_name) + + if exists(my_dir_path): + rmdir(my_dir_path) + + +fn test_rmdir_not_empty() raises: + var cwd_path = Path() + var my_dir_path = cwd_path / "my_dir" + var file_name = my_dir_path / "file.txt" + + assert_false( + exists(my_dir_path), + "Unexpected dir " + my_dir_path.__fspath__() + " it should not exist", + ) + + mkdir(my_dir_path) + with open(file_name, "w"): + pass + + with assert_raises(contains="Can not remove directory: "): + rmdir(my_dir_path) + + remove(file_name) + rmdir(my_dir_path) + assert_false(exists(my_dir_path), "Failed to remove dir") + + +def main(): + test_mkdir_and_rmdir() + test_mkdir_mode() + test_rmdir_not_empty() From 30fc7a6fc8359f77a44087e4bbc617e4d69a493b Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Fri, 10 May 2024 11:05:01 -0500 Subject: [PATCH 0507/2019] [External] [stdlib] Support `__add__` and `__mul__` operators for `List` (#39734) [External] [stdlib] Support `__add__` and `__mul__` operators for `List` Fixes https://github.com/modularml/mojo/issues/2589 Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2590 MODULAR_ORIG_COMMIT_REV_ID: e08d28e0b92394a419ff4d5b231b969ec74635e1 --- stdlib/src/collections/list.mojo | 69 ++++++++++++++++++++++++++ stdlib/test/collections/test_list.mojo | 54 ++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9959524b6d..e17c694d0d 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -243,6 +243,75 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): earlier_idx -= 1 later_idx -= 1 + @always_inline + fn __mul(inout self, x: Int): + """Appends the original elements of this list x-1 times. + + ```mojo + var a = List[Int](1, 2) + a.__mul(2) # a = [1, 2, 1, 2] + ``` + + Args: + x: The multiplier number. + """ + if x == 0: + self.clear() + return + var orig = List(self) + self.reserve(len(self) * x) + for i in range(x - 1): + self.extend(orig) + + @always_inline("nodebug") + fn __mul__(self, x: Int) -> Self: + """Multiplies the list by x and returns a new list. + + Args: + x: The multiplier number. + + Returns: + The new list. + """ + # avoid the copy since it would be cleared immediately anyways + if x == 0: + return Self() + var result = List(self) + result.__mul(x) + return result^ + + @always_inline("nodebug") + fn __imul__(inout self, x: Int): + """Multiplies the list by x in place. + + Args: + x: The multiplier number. + """ + self.__mul(x) + + @always_inline("nodebug") + fn __add__(self, owned other: Self) -> Self: + """Concatenates self with other and returns the result as a new list. + + Args: + other: List whose elements will be combined with the elements of self. + + Returns: + The newly created list. + """ + var result = List(self) + result.extend(other^) + return result^ + + @always_inline("nodebug") + fn __iadd__(inout self, owned other: Self): + """Appends the elements of other into self. + + Args: + other: List whose elements will be appended to self. + """ + self.extend(other^) + @always_inline fn extend(inout self, owned other: List[T]): """Extends this list by consuming the elements of `other`. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index a65d070c44..fe9b642c36 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -689,6 +689,58 @@ def test_list_count(): assert_equal(0, __type_of(list2).count(list2, 1)) +def test_list_add(): + var a = List[Int](1, 2, 3) + var b = List[Int](4, 5, 6) + var c = a + b + assert_equal(len(c), 6) + # check that original values aren't modified + assert_equal(len(a), 3) + assert_equal(len(b), 3) + assert_equal(__type_of(c).__str__(c), "[1, 2, 3, 4, 5, 6]") + + a += b + assert_equal(len(a), 6) + assert_equal(__type_of(a).__str__(a), "[1, 2, 3, 4, 5, 6]") + assert_equal(len(b), 3) + + a = List[Int](1, 2, 3) + a += b^ + assert_equal(len(a), 6) + assert_equal(__type_of(a).__str__(a), "[1, 2, 3, 4, 5, 6]") + + var d = List[Int](1, 2, 3) + var e = List[Int](4, 5, 6) + var f = d + e^ + assert_equal(len(f), 6) + assert_equal(__type_of(f).__str__(f), "[1, 2, 3, 4, 5, 6]") + + var l = List[Int](1, 2, 3) + l += List[Int]() + assert_equal(len(l), 3) + + +def test_list_mult(): + var a = List[Int](1, 2, 3) + var b = a * 2 + assert_equal(len(b), 6) + assert_equal(__type_of(b).__str__(b), "[1, 2, 3, 1, 2, 3]") + b = a * 3 + assert_equal(len(b), 9) + assert_equal(__type_of(b).__str__(b), "[1, 2, 3, 1, 2, 3, 1, 2, 3]") + a *= 2 + assert_equal(len(a), 6) + assert_equal(__type_of(a).__str__(a), "[1, 2, 3, 1, 2, 3]") + + var l = List[Int](1, 2) + l *= 1 + assert_equal(len(l), 2) + + l *= 0 + assert_equal(len(l), 0) + assert_equal(len(List[Int](1, 2, 3) * 0), 0) + + def main(): test_mojo_issue_698() test_list() @@ -714,3 +766,5 @@ def main(): test_constructor_from_other_list_through_pointer() test_converting_list_to_string() test_list_count() + test_list_add() + test_list_mult() From 541d761dab10c3a5e3bfcb0acbfd14429e170af8 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 10 May 2024 09:15:25 -0700 Subject: [PATCH 0508/2019] [stdlib] Replace direct coroutine callback access with new operation This operation directly accesses the callback pointer inside the coroutine frame, so the stdlib doesn't have to perform the offset computation. This abstracts the location of the callback from stdlib code. MODULAR_ORIG_COMMIT_REV_ID: d8bef12e4230e6a8c04d2ecd5139091e7d6f140d --- stdlib/src/builtin/coroutine.mojo | 64 ++++++++++++------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index e568256da2..2b8b8ebd1f 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -93,31 +93,8 @@ struct Coroutine[type: AnyRegType]: type: Type of value returned upon completion of the coroutine. """ - alias _promise_type = __mlir_type[`!kgen.struct<(`, type, `)>`] var _handle: AnyCoroutine - @always_inline - fn _get_promise(self) -> UnsafePointer[type]: - """Return the pointer to the beginning of the memory where the async - function results are stored. - - Returns: - The coroutine promise. - """ - var promise: UnsafePointer[Self._promise_type] = __mlir_op.`co.promise`[ - _type = __mlir_type[`!kgen.pointer<`, Self._promise_type, `>`] - ](self._handle) - return promise.bitcast[type]() - - @always_inline - fn get(self) -> type: - """Get the value of the fulfilled coroutine promise. - - Returns: - The value of the fulfilled promise. - """ - return LegacyPointer(self._get_promise().address).load() - @always_inline fn _get_ctx[ctx_type: AnyRegType](self) -> UnsafePointer[ctx_type]: """Returns the pointer to the coroutine context. @@ -132,7 +109,22 @@ struct Coroutine[type: AnyRegType]: sizeof[_CoroutineContext]() == sizeof[ctx_type](), "context size must be 16 bytes", ]() - return self._get_promise().bitcast[ctx_type]() - 1 + return __mlir_op.`co.get_callback_ptr`[ + _type = __mlir_type[`!kgen.pointer<`, ctx_type, `>`] + ](self._handle) + + @always_inline + fn get(self) -> type: + """Get the value of the fulfilled coroutine promise. + + Returns: + The value of the fulfilled promise. + """ + return UnsafePointer( + __mlir_op.`co.promise`[ + _type = __mlir_type[`!kgen.pointer<`, type, `>`] + ](self._handle) + )[] @always_inline fn __init__(handle: AnyCoroutine) -> Coroutine[type]: @@ -203,22 +195,8 @@ struct RaisingCoroutine[type: AnyRegType]: """ alias _var_type = __mlir_type[`!kgen.variant<`, Error, `, `, type, `>`] - alias _promise_type = __mlir_type[`!kgen.struct<(`, Self._var_type, `)>`] var _handle: AnyCoroutine - @always_inline - fn _get_promise(self) -> UnsafePointer[Self._var_type]: - """Return the pointer to the beginning of the memory where the async - function results are stored. - - Returns: - The coroutine promise. - """ - var promise: UnsafePointer[Self._promise_type] = __mlir_op.`co.promise`[ - _type = __mlir_type[`!kgen.pointer<`, Self._promise_type, `>`] - ](self._handle) - return promise.bitcast[Self._var_type]() - @always_inline fn get(self) raises -> type: """Get the value of the fulfilled coroutine promise. @@ -226,7 +204,11 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The value of the fulfilled promise. """ - var variant = LegacyPointer(self._get_promise().address).load() + var variant = UnsafePointer( + __mlir_op.`co.promise`[ + _type = __mlir_type[`!kgen.pointer<`, Self._var_type, `>`] + ](self._handle) + )[] if __mlir_op.`kgen.variant.is`[index = Int(0).value](variant): raise __mlir_op.`kgen.variant.take`[index = Int(0).value](variant) return __mlir_op.`kgen.variant.take`[index = Int(1).value](variant) @@ -245,7 +227,9 @@ struct RaisingCoroutine[type: AnyRegType]: sizeof[_CoroutineContext]() == sizeof[ctx_type](), "context size must be 16 bytes", ]() - return self._get_promise().bitcast[ctx_type]() - 1 + return __mlir_op.`co.get_callback_ptr`[ + _type = __mlir_type[`!kgen.pointer<`, ctx_type, `>`] + ](self._handle) @always_inline fn __init__(inout self, handle: AnyCoroutine): From a4f592f811e59d53693cee8aa0659212f8ffe016 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 10 May 2024 10:39:18 -0600 Subject: [PATCH 0509/2019] [stdlib] Remove unused `StaticIntTuple` import Remove unused import in `builtin/int.mojo`. MODULAR_ORIG_COMMIT_REV_ID: 96f1f293285541ef4737e2155ba2ac82592786ef --- stdlib/src/builtin/int.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 1491319880..8de21a3367 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -24,7 +24,6 @@ from builtin.io import _snprintf from builtin.hex import _try_write_int from utils._visualizers import lldb_formatter_wrapping_type -from utils import StaticIntTuple from utils._format import Formattable, Formatter from utils.inlined_string import _ArrayMem From 9ca72c88288ed6882361455141ad11aebc17b5e9 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 10 May 2024 09:39:48 -0700 Subject: [PATCH 0510/2019] [mojo-examples] Small fixes for reduce.mojo - Use `def` instead of `fn` for Python pretty print - Remove printing results - Pass `type` to `scalar` MODULAR_ORIG_COMMIT_REV_ID: 02564b680fd7448f122de1fa38ce736e98b940d9 --- examples/reduce.mojo | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 69a5d4c63c..ff8240ed25 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -29,7 +29,7 @@ alias size_large: Int = 1 << 27 # Datatype for Tensor/Array alias type = DType.float32 -alias scalar = Scalar[DType.float32] +alias scalar = Scalar[type] # Use the https://en.wikipedia.org/wiki/Kahan_summation_algorithm @@ -50,9 +50,9 @@ fn stdlib_reduce_sum[size: Int](array: Buffer[type, size]) -> scalar: return my_sum -fn pretty_print(name: StringLiteral, elements: Int, time: Float64) raises: - var py = Python.import_module("builtins") - _ = py.print( +def pretty_print(name: StringLiteral, elements: Int, time: Float64): + py = Python.import_module("builtins") + py.print( py.str("{:<16} {:>11,} {:>8.2f}ms").format( String(name) + " elements:", elements, time ) @@ -87,8 +87,6 @@ fn main() raises: var buffer_small = Buffer[type, size_small](ptr_small) var buffer_large = Buffer[type, size_large](ptr_large) - print(naive_reduce_sum(buffer_small)) - print(stdlib_reduce_sum(buffer_small)) bench[naive_reduce_sum, size_small, "naive"](buffer_small) bench[naive_reduce_sum, size_large, "naive"](buffer_large) From 1aa7667aa5ae1711ad0c3f883e6a9a2b2d8c936e Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 10 May 2024 10:41:19 -0700 Subject: [PATCH 0511/2019] [stdlib] Access coroutine results using `co.get_results` This removes uses of the `co.promise` operation from the stdlib and uses `co.get_results` to access the register-passable results of a coroutine. This is more succinct. MODULAR_ORIG_COMMIT_REV_ID: c1057fde5204a1913a33f96b294544e2fcdad04e --- stdlib/src/builtin/coroutine.mojo | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 2b8b8ebd1f..3163cc0fc7 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -120,11 +120,7 @@ struct Coroutine[type: AnyRegType]: Returns: The value of the fulfilled promise. """ - return UnsafePointer( - __mlir_op.`co.promise`[ - _type = __mlir_type[`!kgen.pointer<`, type, `>`] - ](self._handle) - )[] + return __mlir_op.`co.get_results`[_type=type](self._handle) @always_inline fn __init__(handle: AnyCoroutine) -> Coroutine[type]: @@ -204,11 +200,9 @@ struct RaisingCoroutine[type: AnyRegType]: Returns: The value of the fulfilled promise. """ - var variant = UnsafePointer( - __mlir_op.`co.promise`[ - _type = __mlir_type[`!kgen.pointer<`, Self._var_type, `>`] - ](self._handle) - )[] + var variant = __mlir_op.`co.get_results`[_type = Self._var_type]( + self._handle + ) if __mlir_op.`kgen.variant.is`[index = Int(0).value](variant): raise __mlir_op.`kgen.variant.take`[index = Int(0).value](variant) return __mlir_op.`kgen.variant.take`[index = Int(1).value](variant) From d49a2b6861578f94d0214b9405e5f4c2c517d364 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Fri, 10 May 2024 11:33:02 -0700 Subject: [PATCH 0512/2019] [stdlib] Improve Dict performance with a new integer hash implementation [Internal link] [Internal link] Switches the hash algorithm for SIMD values from DJB33XA to the [ankerl::unordered_dense::hash](https://martin.ankerl.com/2022/08/27/hashmap-bench-01/#ankerlunordered_densehash-). This substantially improves the distribution of hash values on sequential values, resulting in dramatically shorter probe sequences. In the above plots, performance is measured and averaged on string keys and sequential integer keys, first for the 24.3 dict/hash implementations, then with the changes to Dict in [Internal link] and finally after this change (called 24.4). Large maps should see 10x or better improvements with most key types, and even moderate sized maps with integer keys should see even much larger improvements. MODULAR_ORIG_COMMIT_REV_ID: a8383f6ed9271a737b635942cbe87361ddf3a5c5 --- docs/changelog.md | 5 ++ stdlib/src/builtin/hash.mojo | 139 ++++++++++++----------------- stdlib/src/builtin/int.mojo | 3 +- stdlib/src/collections/dict.mojo | 4 +- stdlib/test/builtin/test_hash.mojo | 14 +-- 5 files changed, 74 insertions(+), 91 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 41bd6f3a0e..7d476c09e2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -141,6 +141,8 @@ what we publish. using `mkdir` and `rmdir`. ([PR #2430](https://github.com/modularml/mojo/pull/2430) by [@artemiogr97](https://github.com/artemiogr97)) +- `Dict.__get_ref(key)`, allowing to get references to dictionary values. + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no @@ -190,3 +192,6 @@ what we publish. simple trait definitions. - [#1787](https://github.com/modularml/mojo/issues/1787) Fix error when using `//` on `FloatLiteral` in alias expression. +- Made several improvements to dictionary performance. Dicts with integer keys + are most heavily affected, but large dicts and dicts with large values + will also see large improvements. diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 4568c7a3e1..d7390d5b99 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -25,6 +25,7 @@ There are a few main tools in this module: These are useful helpers to specialize for the general bytes implementation. """ +from builtin.dtype import _uint_type_of_width import random from sys.ffi import _get_global @@ -126,10 +127,32 @@ fn _djbx33a_hash_update[ return data * 33 + next +# Based on the hash function used by ankerl::unordered_dense::hash +# https://martin.ankerl.com/2022/08/27/hashmap-bench-01/#ankerl__unordered_dense__hash +fn _ankerl_init[type: DType, size: Int]() -> SIMD[type, size]: + alias int_type = _uint_type_of_width[type.bitwidth()]() + alias init = Int64(-7046029254386353131).cast[int_type]() + return SIMD[type, size](bitcast[type, 1](init)) + + +fn _ankerl_hash_update[ + type: DType, size: Int +](data: SIMD[type, size], next: SIMD[type, size]) -> SIMD[type, size]: + # compute the hash as though the type is uint + alias int_type = _uint_type_of_width[type.bitwidth()]() + var data_int = bitcast[int_type, size](data) + var next_int = bitcast[int_type, size](next) + var result = (data_int * next_int) ^ next_int + return bitcast[type, size](result) + + alias _HASH_INIT = _djbx33a_init alias _HASH_UPDATE = _djbx33a_hash_update +# This is incrementally better than DJBX33A, in that it fixes some of the +# performance issue we've been seeing with Dict. It's still not ideal as +# a long-term hash function. @always_inline fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: """Hash a SIMD byte vector using direct DJBX33A hash algorithm. @@ -148,54 +171,26 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: cryptographic purposes, but will have good low-bit hash collision statistical properties for common data structures. """ - # Some types will have non-integer ratios, eg. DType.bool - alias int8_size = _div_ceil_positive( - type.bitwidth(), DType.uint8.bitwidth() - ) * size - # Stack allocate bytes for `data` and load it into that memory. - # Then reinterpret as int8 and pass to the specialized int8 hash function. - # - Ensure that the alignment matches both types, otherwise - # an aligned load or store will be offset and cause - # nondeterminism (read) or memory corruption (write) - # TODO(#31160): use math.lcm - # Technically this is LCM, but alignments should always be multiples of 2. - alias alignment = max( - alignof[SIMD[type, size]](), alignof[SIMD[DType.uint8, int8_size]]() - ) - var bytes = stack_allocation[int8_size, DType.uint8, alignment=alignment]() - memset_zero(bytes, int8_size) - bytes.bitcast[type]().store[width=size](data) - return _hash_int8(bytes.load[width=int8_size]()) - - -fn _hash_int8[size: Int](data: SIMD[DType.uint8, size]) -> Int: - """Hash a SIMD byte vector using direct DJBX33A hash algorithm. - This naively implements DJBX33A, with a hash secret appended at the end. - The hash secret is computed randomly at compile time, so different executions - will use different secrets, and thus have different hash outputs. This is - useful in preventing DDOS attacks against hash functions using a - non-cryptographic hash function like DJBX33A. + @parameter + if type == DType.bool: + return _hash_simd(data.cast[DType.int8]()) - See `hash(bytes, n)` documentation for more details. + var hash_data = _ankerl_init[type, size]() + hash_data = _ankerl_hash_update(hash_data, data) - Parameters: - size: The SIMD width of the input data. + alias int_type = _uint_type_of_width[type.bitwidth()]() + var final_data = bitcast[int_type, 1](hash_data[0]).cast[DType.uint64]() - Args: - data: The input data to hash. + @parameter + fn hash_value[i: Int](): + final_data = _ankerl_hash_update( + final_data, + bitcast[int_type, 1](hash_data[i + 1]).cast[DType.uint64](), + ) - Returns: - A 64-bit integer hash. This hash is _not_ suitable for - cryptographic purposes, but will have good low-bit - hash collision statistical properties for common data structures. - """ - var hash_data = _HASH_INIT[DType.int64, 1]() - for i in range(size): - hash_data = _HASH_UPDATE(hash_data, data[i].cast[DType.int64]()) - # TODO(27659): 'lit.globalvar.ref' error - # return int(hash_data) ^ HASH_SECRET - return int(hash_data) ^ _HASH_SECRET() + unroll[hash_value, size - 1]() + return int(final_data) fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: @@ -220,37 +215,25 @@ fn hash(bytes: DTypePointer[DType.uint8], n: Int) -> Int: # TODO: Remove this overload once we have finished the transition to uint8 # for bytes. See https://github.com/modularml/mojo/issues/2317 fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: - """Hash a byte array using a SIMD-modified DJBX33A hash algorithm. - - The DJBX33A algorithm is commonly used for data structures that rely - on well-distributed hashing for performance. The low order bits of the - result depend on each byte in the input, meaning that single-byte changes - will result in a changed hash even when masking out most bits eg. for small - dictionaries. + """Hash a byte array using a SIMD-modified hash algorithm. _This hash function is not suitable for cryptographic purposes._ The algorithm is easy to reverse and produce deliberate hash collisions. - We _do_ however initialize a random hash secret which is mixed into - the final hash output. This can help prevent DDOS attacks on applications - which make use of this function for dictionary hashing. As a consequence, - hash values are deterministic within an individual runtime instance ie. - a value will always hash to the same thing, but in between runs this value - will change based on the hash secret. - - Standard DJBX33A is: - - - Set _hash_ = 5361 - - For each byte: _hash_ = 33 * _hash_ + _byte_ - - Instead, for all bytes except trailing bytes that don't align - to the max SIMD vector width, we: - - - Interpret those bytes as a SIMD vector. - - Apply a vectorized hash: _v_ = 33 * _v_ + _bytes_as_simd_value_ - - Call [`reduce_add()`](/mojo/stdlib/builtin/simd/SIMD#reduce_add) on the - final result to get a single hash value. - - Use this value in fallback for the remaining suffix bytes - with standard DJBX33A. + The hash function is designed to have relatively good mixing and statistical + properties for use in hash-based data structures. We _do_ however initialize + a random hash secret which is mixed into the final hash output. This can help + prevent DDOS attacks on applications which make use of this function for + dictionary hashing. As a consequence, hash values are deterministic within an + individual runtime instance ie. a value will always hash to the same thing, + but in between runs this value will change based on the hash secret. + + We take advantage of Mojo's first-class SIMD support to create a + SIMD-vectorized hash function, using some simple hash algorithm as a base. + + - Interpret those bytes as a SIMD vector, padded with zeros to align + to the system SIMD width. + - Apply the simple hash function parallelized across SIMD vectors. + - Hash the final SIMD vector state to reduce to a single value. Python uses DJBX33A with a hash secret for smaller strings, and then the SipHash algorithm for longer strings. The arguments and tradeoffs @@ -281,7 +264,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: cryptographic purposes, but will have good low-bit hash collision statistical properties for common data structures. """ - alias type = DType.int64 + alias type = DType.uint64 alias type_width = type.bitwidth() // DType.int8.bitwidth() alias simd_width = simdwidthof[type]() # stride is the byte length of the whole SIMD vector @@ -296,12 +279,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: # 1. Reinterpret the underlying data as a larger int type var simd_data = bytes.bitcast[type]() - # 2. Compute DJBX33A, but strided across the SIMD vector width. - # This is almost the same as DBJX33A, except: - # - The order in which bytes of data update the hash is permuted - # - For larger inputs, a small constant number of bytes from the - # beginning of the string (3/4 of the first vector load) - # have a slightly different power of 33 as a coefficient. + # 2. Compute the hash, but strided across the SIMD vector width. var hash_data = _HASH_INIT[type, simd_width]() for i in range(k): var update = simd_data.load[width=simd_width](i * simd_width) @@ -319,8 +297,5 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: var last_value = ptr.bitcast[type]().load[width=simd_width]() hash_data = _HASH_UPDATE(hash_data, last_value) - # Now finally, hash the final SIMD vector state. This will also use - # DJBX33A to make sure that higher-order bits of the vector will - # mix and impact the low-order bits, and is mathematically necessary - # for this function to equate to naive DJBX33A. + # Now finally, hash the final SIMD vector state. return _hash_simd(hash_data) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 8de21a3367..83817e228d 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -1026,4 +1026,5 @@ struct Int( uses. Its intended usage is for data structures. See the `hash` builtin documentation for more details. """ - return _hash_simd(Scalar[DType.index](self)) + # TODO(MOCO-636): switch to DType.index + return _hash_simd(Scalar[DType.int64](self)) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 2f054b3082..5fd6f39ce0 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -687,10 +687,10 @@ struct Dict[K: KeyElement, V: CollectionElement]( if found: self._set_index(slot, Self.REMOVED) var entry = self._entries.__get_ref(index) + debug_assert(entry[].__bool__(), "entry in index must be full") var entry_value = entry[].unsafe_take() entry[] = None self.size -= 1 - debug_assert(entry[].__bool__(), "entry in index must be full") return entry_value.value^ elif default: return default.value()[] @@ -828,7 +828,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( if index == Self.EMPTY: return (False, slot, self._n_entries) elif index == Self.REMOVED: - return (False, slot, self._n_entries) + pass else: var entry = self._entries.__get_ref(index) debug_assert(entry[].__bool__(), "entry in index must be full") diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index d896444fc6..4c21abdf2e 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -22,9 +22,9 @@ from builtin.hash import _hash_simd from testing import assert_equal, assert_not_equal, assert_true -def same_low_bits(i1: Int, i2: Int, bits: Int = 4) -> Int: +def same_low_bits(i1: Int, i2: Int, bits: Int = 5) -> Int: var mask = (1 << bits) - 1 - return 1 if i1 & mask == i2 & mask else 0 + return int(not (i1 ^ i2) & mask) def test_hash_byte_array(): @@ -55,10 +55,10 @@ def test_hash_byte_array(): hash("c".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1) ) - assert_true(num_same < 2, "too little entropy in hash fn low bits") + assert_true(num_same < 3, "too little entropy in hash fn low bits") -def _test_hash_int_simd[type: DType](bits: Int = 4): +def _test_hash_int_simd[type: DType](bits: Int = 4, max_num_same: Int = 2): var a = Scalar[type](0) var b = Scalar[type](1) var c = Scalar[type](2) @@ -79,7 +79,9 @@ def _test_hash_int_simd[type: DType](bits: Int = 4): num_same += same_low_bits(_hash_simd(b), _hash_simd(d), bits) num_same += same_low_bits(_hash_simd(c), _hash_simd(d), bits) - assert_true(num_same < 2, "too little entropy in hash fn low bits") + assert_true( + num_same < max_num_same, "too little entropy in hash fn low bits" + ) def test_hash_simd(): @@ -91,7 +93,7 @@ def test_hash_simd(): # this could affect performance of small dicts some. Let's punt and see # if this is an issue in practice, if so we can specialize the float # hash implementation. - _test_hash_int_simd[DType.float32](bits=7) + _test_hash_int_simd[DType.float32](max_num_same=7) # TODO: test hashing different NaNs. # Test a couple other random things From 20486b8a14bda75da4b25ccf5e1cf06c0684801f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 10 May 2024 13:16:05 -0600 Subject: [PATCH 0513/2019] [stdlib] Add `__neg__` to `Bool` Add `__neg__` to `Bool` so `-True` and `-False` work. MODULAR_ORIG_COMMIT_REV_ID: 0d3fe5daae5232416f71d14ff822480417cbbbc6 --- stdlib/src/builtin/bool.mojo | 9 +++++++++ stdlib/test/builtin/test_bool.mojo | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index c4f9f3ef5c..e56f2b3eae 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -313,6 +313,15 @@ struct Bool( """ return lhs ^ self + @always_inline("nodebug") + fn __neg__(self) -> Int: + """Defines the unary `-` operation. + + Returns: + 0 for -False and -1 for -True. + """ + return __mlir_op.`index.casts`[_type = __mlir_type.index](self.value) + # ===----------------------------------------------------------------------=== # # bool diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index 0a7f9bd410..ab13481124 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -95,9 +95,15 @@ def test_bitwise(): assert_false(value) +def test_neg(): + assert_equal(-1, -True) + assert_equal(0, -False) + + def main(): test_bool_cast_to_int() test_bool_none() test_convert_from_boolable() test_bool_to_string() test_bitwise() + test_neg() From debeec761b5ead298f7ab00cc5cc83fe637f4792 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 10 May 2024 15:47:21 -0400 Subject: [PATCH 0514/2019] [stdlib] Reduced the usage of `__get_address_as_owned_value` Replaced `__get_address_as_owned_value` with `{move_from, destroy}_pointee`. MODULAR_ORIG_COMMIT_REV_ID: e189c2b0762bf0103d58377cc185affcf18b1c5b --- stdlib/src/builtin/tuple.mojo | 6 +++--- stdlib/src/utils/variant.mojo | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index f9a5a5c205..5a8bd260da 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -20,6 +20,7 @@ from utils._visualizers import lldb_formatter_wrapping_type from memory.unsafe_pointer import ( initialize_pointee_move, initialize_pointee_copy, + move_pointee, ) # ===----------------------------------------------------------------------===# @@ -128,9 +129,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): var existing_elt_ptr = UnsafePointer(existing[idx]).address - initialize_pointee_move( - UnsafePointer(self[idx]), - __get_address_as_owned_value(existing_elt_ptr), + move_pointee( + src=UnsafePointer(existing[idx]), dst=UnsafePointer(self[idx]) ) unroll[initialize_elt, Self.__len__()]() diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index a3a9f8ac2f..3b62a5dcf5 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -219,9 +219,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn each[i: Int](): if self._get_state()[] == i: alias q = Ts[i] - __get_address_as_owned_value( - self._get_ptr[q]().address - ).__del__() + destroy_pointee(self._get_ptr[q]().address) unroll[each, len(VariadicList(Ts))]() From 1e8c2a122f6321085d2c37e73ae0c1b8ff0a2cb4 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Fri, 10 May 2024 16:01:14 -0400 Subject: [PATCH 0515/2019] [stdlib] Renamed `_type` to `_mlir_type` in `Variant` for consistency MODULAR_ORIG_COMMIT_REV_ID: 2ac07276b3dde9ee2a4e650c4f11d45d8740d957 --- stdlib/src/collections/optional.mojo | 8 ++++---- stdlib/src/utils/variant.mojo | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 4b8335192d..62c5467f95 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -217,8 +217,8 @@ struct OptionalReg[T: AnyRegType](Boolable): T: The type of value stored in the Optional. """ - alias _type = __mlir_type[`!kgen.variant<`, T, `, i1>`] - var _value: Self._type + alias _mlir_type = __mlir_type[`!kgen.variant<`, T, `, i1>`] + var _value: Self._mlir_type fn __init__(inout self): """Create an optional with a value of None.""" @@ -231,7 +231,7 @@ struct OptionalReg[T: AnyRegType](Boolable): value: The value. """ self._value = __mlir_op.`kgen.variant.create`[ - _type = Self._type, index = Int(0).value + _type = Self._mlir_type, index = Int(0).value ](value) fn __init__(inout self, value: NoneType): @@ -241,7 +241,7 @@ struct OptionalReg[T: AnyRegType](Boolable): value: The None value. """ self._value = __mlir_op.`kgen.variant.create`[ - _type = Self._type, index = Int(1).value + _type = Self._mlir_type, index = Int(1).value ](__mlir_attr.false) @always_inline diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 3b62a5dcf5..d176c75545 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -140,10 +140,10 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ alias _sentinel: Int = -1 - alias _type = __mlir_type[ + alias _mlir_type = __mlir_type[ `!kgen.variant<[rebind(:`, __type_of(Ts), ` `, Ts, `)]>` ] - var _impl: Self._type + var _impl: Self._mlir_type fn __init__[T: CollectionElement](inout self, owned value: T): """Create a variant with one of the types. @@ -155,7 +155,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Args: value: The value to initialize the variant with. """ - self._impl = __mlir_attr[`#kgen.unknown : `, self._type] + self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] self._get_state()[] = Self._check[T]() initialize_pointee_move(self._get_ptr[T](), value^) @@ -165,7 +165,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Args: other: The variant to copy from. """ - self._impl = __mlir_attr[`#kgen.unknown : `, self._type] + self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] self._get_state()[] = other._get_state()[] @parameter @@ -185,7 +185,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Args: other: The variant to move. """ - self._impl = __mlir_attr[`#kgen.unknown : `, self._type] + self._impl = __mlir_attr[`#kgen.unknown : `, self._mlir_type] self._get_state()[] = other._get_state()[] @parameter From 0b369c25790e9e033c1ef71c1c7b9a7962441f97 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Fri, 10 May 2024 15:43:39 -0500 Subject: [PATCH 0516/2019] [External] [stdlib] Introduce non owning collection type (#39731) [External] [stdlib] Introduce non owning collection type This PR introduces a non owning collection type for contiguous arrays of `CollectionElement`. With this type, we will be able to tie together the array types in the standard library, and reduce necessary copies in some places. ### Motivation The pointer & length data structure is common across most mainstream programming languages at this point ([Zig](https://ziglang.org/documentation/master/#Slices), [Rust](https://doc.rust-lang.org/std/primitive.slice.html), [C++](https://en.cppreference.com/w/cpp/container/span)). In order to mirror APIs developers are used to with the performance characteristic they expect, this type will be required. Notably, it now allows functions that can work on `List` _or_ `Array` without an overload: ```mojo fn copy[ T: CollectionElement, lifetime: MutLifetime ](owned dst: Span[T, __mlir_attr.`1: i1`, lifetime], src: Span[T, _, _],): for i in range(len(src)): dst[i] = src[i] fn main(): var l = List[Int](1, 2, 3) var a = InlineArray[Int, 3](4, 5, 6) copy(Span(l), Span(a)) for i in l: print(i[]) ``` This code keeps the `List` and `InlineArray` alive long enough for the copy to work, which is not true for `Buffer` or `DTypePointer`. This type will be especially necessary for traits such as `Read` and `Write`, which will want to take a `Span` as the relevant buffer, since taking a `List` or `Array` is use case dependent for such functions. Another motivation for this change is to reduce unnecessary copies on methods such as slicing a `List`, which currently allocates a new list. Ideally, we would just return a `Span` without making a copy. The same could be said for `String.as_bytes()`, which does not need to allocate a `List`. **Note about naming** I'm using the name `Span` to mirror C++'s `std::span`. Rust, Go, and Zig call this type a `slice`, but that term has an overloaded meaning in Python. It's also nice that it's short as a name. Similarly, Mojo already has the `Buffer` type. I'm open to other options if `Span` is not the right fit. Co-authored-by: Lukas Hermann Closes modularml/mojo#2595 MODULAR_ORIG_COMMIT_REV_ID: 2fb5385d4d2f141150088ebd3e107cebdba761d2 --- stdlib/src/utils/__init__.mojo | 1 + stdlib/src/utils/span.mojo | 230 +++++++++++++++++++++++++++++++ stdlib/test/utils/test_span.mojo | 117 ++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 stdlib/src/utils/span.mojo create mode 100644 stdlib/test/utils/test_span.mojo diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index c20317abe5..3ecb9619c0 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -19,6 +19,7 @@ from .index import ( ) from .inlined_string import InlinedString from .loop import unroll +from .span import Span from .static_tuple import StaticTuple, InlineArray from .stringref import StringRef from .variant import Variant diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo new file mode 100644 index 0000000000..18a5adbc96 --- /dev/null +++ b/stdlib/src/utils/span.mojo @@ -0,0 +1,230 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +"""Implements the Span type. + +You can import these APIs from the `utils.span` module. For example: + +```mojo +from utils import Span +``` +""" + +from . import InlineArray + + +@value +struct _SpanIter[ + T: CollectionElement, + is_mutable: __mlir_type.`i1`, + lifetime: AnyLifetime[is_mutable].type, + forward: Bool = True, +]: + """Iterator for Span. + + Parameters: + T: The type of the elements in the span. + is_mutable: Whether the reference to the span is mutable. + lifetime: The lifetime of the Span. + forward: The iteration direction. `False` is backwards. + """ + + var index: Int + var src: Span[T, is_mutable, lifetime] + + @always_inline + fn __iter__(self) -> Self: + return self + + @always_inline + fn __next__( + inout self, + ) -> Reference[T, is_mutable, lifetime]: + @parameter + if forward: + self.index += 1 + return self.src._refitem__(self.index - 1) + else: + self.index -= 1 + return self.src._refitem__(self.index) + + @always_inline + fn __len__(self) -> Int: + @parameter + if forward: + return len(self.src) - self.index + else: + return self.index + + +@value +struct Span[ + T: CollectionElement, + is_mutable: __mlir_type.i1, + lifetime: AnyLifetime[is_mutable].type, +]: + """A non owning view of contiguous data. + + Parameters: + T: The type of the elements in the span. + is_mutable: Whether the span is mutable. + lifetime: The lifetime of the Span. + """ + + var _data: UnsafePointer[T] + var _len: Int + + @always_inline + fn __init__(inout self, *, unsafe_ptr: UnsafePointer[T], len: Int): + """Unsafe construction from a pointer and length. + + Args: + unsafe_ptr: The underlying pointer of the span. + len: The length of the view. + """ + self._data = unsafe_ptr + self._len = len + + @always_inline + fn __init__(inout self, list: Reference[List[T], is_mutable, lifetime]): + """Construct a Span from a List. + + Args: + list: The list to which the span refers. + """ + self._data = list[].data + self._len = len(list[]) + + @always_inline + fn __init__[ + size: Int + ](inout self, array: Reference[InlineArray[T, size], is_mutable, lifetime]): + """Construct a Span from an InlineArray. + + Parameters: + size: The size of the InlineArray. + + Args: + array: The array to which the span refers. + """ + self._data = UnsafePointer(array).bitcast[T]() + self._len = size + + @always_inline + fn __len__(self) -> Int: + """Returns the length of the span. This is a known constant value. + + Returns: + The size of the span. + """ + return self._len + + @always_inline + fn _refitem__[ + intable: Intable + ](self, index: intable) -> Reference[T, is_mutable, lifetime]: + debug_assert( + -self._len <= int(index) < self._len, "index must be within bounds" + ) + + var offset = int(index) + if offset < 0: + offset += len(self) + return (self._data + offset)[] + + @always_inline + fn _adjust_span(self, span: Slice) -> Slice: + """Adjusts the span based on the list length.""" + var adjusted_span = span + + if adjusted_span.start < 0: + adjusted_span.start = len(self) + adjusted_span.start + + if not adjusted_span._has_end(): + adjusted_span.end = len(self) + elif adjusted_span.end < 0: + adjusted_span.end = len(self) + adjusted_span.end + + if span.step < 0: + var tmp = adjusted_span.end + adjusted_span.end = adjusted_span.start - 1 + adjusted_span.start = tmp - 1 + + return adjusted_span + + @always_inline + fn __getitem__[IntableType: Intable](self, index: IntableType) -> T: + """Get a `Reference` to the element at the given index. + + Parameters: + IntableType: The inferred type of an intable argument. + + Args: + index: The index of the item. + + Returns: + A reference to the item at the given index. + """ + # note that self._refitem__ is already bounds checking + return self._refitem__(index)[] + + @always_inline + fn __setitem__[ + IntableType: Intable + ](inout self, index: IntableType, value: T): + """Get a `Reference` to the element at the given index. + + Parameters: + IntableType: The inferred type of an intable argument. + + Args: + index: The index of the item. + value: The value to set at the given index. + """ + # note that self._refitem__ is already bounds checking + var ref = Reference[T, __mlir_attr.`1: i1`, __lifetime_of(self)]( + UnsafePointer(self._refitem__(index))[] + ) + ref[] = value + + @always_inline + fn __getitem__(self, slice: Slice) -> Self: + """Get a new span from a slice of the current span. + + Args: + slice: The slice specifying the range of the new subslice. + + Returns: + A new span that points to the same data as the current span. + """ + var adjusted_span = self._adjust_span(slice) + debug_assert( + 0 <= adjusted_span.start < self._len + and 0 <= adjusted_span.end < self._len, + "Slice must be within bounds.", + ) + var res = Self( + unsafe_ptr=(self._data + adjusted_span.start), + len=len(adjusted_span), + ) + + return res + + @always_inline + fn __iter__(self) -> _SpanIter[T, is_mutable, lifetime]: + """Get an iterator over the elements of the span. + + Returns: + An iterator over the elements of the span. + """ + return _SpanIter(0, self) diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo new file mode 100644 index 0000000000..af5d8afd89 --- /dev/null +++ b/stdlib/test/utils/test_span.mojo @@ -0,0 +1,117 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from utils import InlineArray, Span +from collections.list import List +from testing import assert_equal + + +def test_span_list_int(): + var l = List[Int](1, 2, 3, 4, 5, 6, 7) + var s = Span(l) + assert_equal(len(s), len(l)) + for i in range(len(s)): + assert_equal(l[i], s[i]) + # subslice + var s2 = s[2:] + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) + assert_equal(s[-1], l[-1]) + + # Test mutation + s[0] = 9 + assert_equal(s[0], 9) + assert_equal(l[0], 9) + + s[-1] = 0 + assert_equal(s[-1], 0) + assert_equal(l[-1], 0) + + +def test_span_list_str(): + var l = List[String]("a", "b", "c", "d", "e", "f", "g") + var s = Span(l) + assert_equal(len(s), len(l)) + for i in range(len(s)): + assert_equal(l[i], s[i]) + # subslice + var s2 = s[2:] + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) + + # Test mutation + s[0] = "h" + assert_equal(s[0], "h") + assert_equal(l[0], "h") + + s[-1] = "i" + assert_equal(s[-1], "i") + assert_equal(l[-1], "i") + + +def test_span_array_int(): + var l = InlineArray[Int, 7](1, 2, 3, 4, 5, 6, 7) + var s = Span(l) + assert_equal(len(s), len(l)) + for i in range(len(s)): + assert_equal(l[i], s[i]) + # subslice + var s2 = s[2:] + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) + + # Test mutation + s[0] = 9 + assert_equal(s[0], 9) + assert_equal(l[0], 9) + + s[-1] = 0 + assert_equal(s[-1], 0) + assert_equal(l[-1], 0) + + +def test_span_array_str(): + var l = InlineArray[String, 7]("a", "b", "c", "d", "e", "f", "g") + var s = Span(l) + assert_equal(len(s), len(l)) + for i in range(len(s)): + assert_equal(l[i], s[i]) + # subslice + var s2 = s[2:] + assert_equal(s2[0], l[2]) + assert_equal(s2[1], l[3]) + assert_equal(s2[2], l[4]) + assert_equal(s2[3], l[5]) + + # Test mutation + s[0] = "h" + assert_equal(s[0], "h") + assert_equal(l[0], "h") + + s[-1] = "i" + assert_equal(s[-1], "i") + assert_equal(l[-1], "i") + + +def main(): + test_span_list_int() + test_span_list_str() + test_span_array_int() + test_span_array_str() From cb0f273a2c31d94990afba8a1d5e541683ac3fda Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Fri, 10 May 2024 13:54:46 -0700 Subject: [PATCH 0517/2019] [stdlib] Relax test_hash.mojo to pass on AMD/Intel - Test currently failing on AMD/Intel: [Internal link] - Basically these tests are really bad, they're just basic attempts to verify that the hash function isn't totally degenerate, but because hash values vary between architectures and these test only a couple values, there's a high amount of variance in the results and they often flake on related changes and need to be tweaked. - Making this particular test case _much_ more relaxed for now. MODULAR_ORIG_COMMIT_REV_ID: f9bc833ac24a7b11c70fca67cb0d1a3449b7b78e --- stdlib/test/builtin/test_hash.mojo | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index 4c21abdf2e..b9daf26b42 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -55,7 +55,12 @@ def test_hash_byte_array(): hash("c".unsafe_ptr(), 1), hash("d".unsafe_ptr(), 1) ) - assert_true(num_same < 3, "too little entropy in hash fn low bits") + # This test is just really bad. We really need to re-evaluate the + # right way to test these. Hash function behavior varies a bit based + # on architecture, so these tests as-is end up being really flaky. + # Making this _much_ more relaxed for now, but at least still testing + # that at least the hash function returns _some_ different things. + assert_true(num_same < 6, "too little entropy in hash fn low bits") def _test_hash_int_simd[type: DType](bits: Int = 4, max_num_same: Int = 2): From e326da5fa8ff052b75590b336b7a4534df262ca2 Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Fri, 10 May 2024 16:47:34 -0500 Subject: [PATCH 0518/2019] [External] [stdlib] String comparisons implemented (#39768) [External] [stdlib] String comparisons implemented For issue #2346 (as an alternative to #2378). All four comparisons (`__lt__`, `__le__`, `__gt__`, & `__ge__`) uses a single `__lt__` comparison (instead of checking less/greater than + potentially another "equals to"-check, for `__le__` & `__ge__`). Sorry if this is considered a duplicate PR, I only meant to give an alternative suggestion. This is my first ever PR on GitHub. StringLiterals also get comparisons. ORIGINAL_AUTHOR=Simon Hellsten <56205346+siitron@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2409 --------- Co-authored-by: Simon Hellsten <56205346+siitron@users.noreply.github.com> Closes modularml/mojo#2409 MODULAR_ORIG_COMMIT_REV_ID: b2ed4756c2741fd27387fa295515f4a7222e0ca5 --- stdlib/src/builtin/string.mojo | 54 ++++++++++++++++++++ stdlib/src/builtin/string_literal.mojo | 54 ++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 41 +++++++++++++++ stdlib/test/builtin/test_string_literal.mojo | 29 +++++++++++ 4 files changed, 178 insertions(+) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index a729e78719..e9a0f62abf 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -890,6 +890,60 @@ struct String( """ return not (self == other) + @always_inline + fn __lt__(self, rhs: String) -> Bool: + """Compare this String to the RHS using LT comparison. + + Args: + rhs: The other String to compare against. + + Returns: + True if this String is strictly less than the RHS String and False otherwise. + """ + var len1 = len(self) + var len2 = len(rhs) + + if len1 < len2: + return memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len1) <= 0 + else: + return memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len2) < 0 + + @always_inline + fn __le__(self, rhs: String) -> Bool: + """Compare this String to the RHS using LE comparison. + + Args: + rhs: The other String to compare against. + + Returns: + True if this String is less than or equal to the RHS String and False otherwise. + """ + return not (rhs < self) + + @always_inline + fn __gt__(self, rhs: String) -> Bool: + """Compare this String to the RHS using GT comparison. + + Args: + rhs: The other String to compare against. + + Returns: + True if this String is strictly greater than the RHS String and False otherwise. + """ + return rhs < self + + @always_inline + fn __ge__(self, rhs: String) -> Bool: + """Compare this String to the RHS using GE comparison. + + Args: + rhs: The other String to compare against. + + Returns: + True if this String is greater than or equal to the RHS String and False otherwise. + """ + return not (self < rhs) + @always_inline fn __add__(self, other: String) -> String: """Creates a string by appending another string at the end. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index cba810c609..ffac052ba4 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -127,6 +127,60 @@ struct StringLiteral( """ return not self == rhs + @always_inline("nodebug") + fn __lt__(self, rhs: StringLiteral) -> Bool: + """Compare this StringLiteral to the RHS using LT comparison. + + Args: + rhs: The other StringLiteral to compare against. + + Returns: + True if this StringLiteral is strictly less than the RHS StringLiteral and False otherwise. + """ + var len1 = len(self) + var len2 = len(rhs) + + if len1 < len2: + return _memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len1) <= 0 + else: + return _memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len2) < 0 + + @always_inline("nodebug") + fn __le__(self, rhs: StringLiteral) -> Bool: + """Compare this StringLiteral to the RHS using LE comparison. + + Args: + rhs: The other StringLiteral to compare against. + + Returns: + True if this StringLiteral is less than or equal to the RHS StringLiteral and False otherwise. + """ + return not (rhs < self) + + @always_inline("nodebug") + fn __gt__(self, rhs: StringLiteral) -> Bool: + """Compare this StringLiteral to the RHS using GT comparison. + + Args: + rhs: The other StringLiteral to compare against. + + Returns: + True if this StringLiteral is strictly greater than the RHS StringLiteral and False otherwise. + """ + return rhs < self + + @always_inline("nodebug") + fn __ge__(self, rhs: StringLiteral) -> Bool: + """Compare this StringLiteral to the RHS using GE comparison. + + Args: + rhs: The other StringLiteral to compare against. + + Returns: + True if this StringLiteral is greater than or equal to the RHS StringLiteral and False otherwise. + """ + return not (self < rhs) + fn __hash__(self) -> Int: """Hash the underlying buffer using builtin hash. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index aca8f22a65..c94145c9f7 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -119,6 +119,46 @@ fn test_equality_operators() raises: assert_not_equal(s0, "notabc") +fn test_comparison_operators() raises: + var abc = String("abc") + var de = String("de") + var ABC = String("ABC") + var ab = String("ab") + var abcd = String("abcd") + + # Test less than and greater than + assert_true(String.__lt__(abc, de)) + assert_false(String.__lt__(de, abc)) + assert_false(String.__lt__(abc, abc)) + assert_true(String.__lt__(ab, abc)) + assert_true(String.__gt__(abc, ab)) + assert_false(String.__gt__(abc, abcd)) + + # Test less than or equal to and greater than or equal to + assert_true(String.__le__(abc, de)) + assert_true(String.__le__(abc, abc)) + assert_false(String.__le__(de, abc)) + assert_true(String.__ge__(abc, abc)) + assert_false(String.__ge__(ab, abc)) + assert_true(String.__ge__(abcd, abc)) + + # Test case sensitivity in comparison (assuming ASCII order) + assert_true(String.__gt__(abc, ABC)) + assert_false(String.__le__(abc, ABC)) + + # Testing with implicit conversion + assert_true(String.__lt__(abc, "defgh")) + assert_false(String.__gt__(abc, "xyz")) + assert_true(String.__ge__(abc, "abc")) + assert_false(String.__le__(abc, "ab")) + + # Test comparisons involving empty strings + assert_true(String.__lt__("", abc)) + assert_false(String.__lt__(abc, "")) + assert_true(String.__le__("", "")) + assert_true(String.__ge__("", "")) + + fn test_add() raises: var s1 = String("123") var s2 = String("abc") @@ -717,6 +757,7 @@ def main(): test_constructors() test_copy() test_equality_operators() + test_comparison_operators() test_add() test_stringable() test_repr() diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index bee8942916..e4501043d4 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -78,6 +78,34 @@ def test_rfind(): assert_equal(-1, "abc".rfind("abcd")) +fn test_comparison_operators() raises: + # Test less than and greater than + assert_true(StringLiteral.__lt__("abc", "def")) + assert_false(StringLiteral.__lt__("def", "abc")) + assert_false(StringLiteral.__lt__("abc", "abc")) + assert_true(StringLiteral.__lt__("ab", "abc")) + assert_true(StringLiteral.__gt__("abc", "ab")) + assert_false(StringLiteral.__gt__("abc", "abcd")) + + # Test less than or equal to and greater than or equal to + assert_true(StringLiteral.__le__("abc", "def")) + assert_true(StringLiteral.__le__("abc", "abc")) + assert_false(StringLiteral.__le__("def", "abc")) + assert_true(StringLiteral.__ge__("abc", "abc")) + assert_false(StringLiteral.__ge__("ab", "abc")) + assert_true(StringLiteral.__ge__("abcd", "abc")) + + # Test case sensitivity in comparison (assuming ASCII order) + assert_true(StringLiteral.__gt__("abc", "ABC")) + assert_false(StringLiteral.__le__("abc", "ABC")) + + # Test comparisons involving empty strings + assert_true(StringLiteral.__lt__("", "abc")) + assert_false(StringLiteral.__lt__("abc", "")) + assert_true(StringLiteral.__le__("", "")) + assert_true(StringLiteral.__ge__("", "")) + + def test_hash(): # Test a couple basic hash behaviors. # `test_hash.test_hash_bytes` has more comprehensive tests. @@ -119,6 +147,7 @@ def main(): test_contains() test_find() test_rfind() + test_comparison_operators() test_hash() test_intable() test_repr() From f0d64b769ad2db0493e711f365795651223dd8a7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 10 May 2024 16:38:01 -0600 Subject: [PATCH 0519/2019] [stdlib] Disable flaky test in `test_hash.mojo` Disable this flaky entropy test so we can get CI green again. We'll come back to this test soon. MODULAR_ORIG_COMMIT_REV_ID: fbd356c1f5e9fc537b04d9947aa1d4ab0c103e5f --- stdlib/test/builtin/test_hash.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index b9daf26b42..3653ef3292 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -60,7 +60,9 @@ def test_hash_byte_array(): # on architecture, so these tests as-is end up being really flaky. # Making this _much_ more relaxed for now, but at least still testing # that at least the hash function returns _some_ different things. - assert_true(num_same < 6, "too little entropy in hash fn low bits") + + # TODO(MSTDL-472): fix this flaky check + # assert_true(num_same < 6, "too little entropy in hash fn low bits") def _test_hash_int_simd[type: DType](bits: Int = 4, max_num_same: Int = 2): From 898540293b3427a492405f92ad12acd5bff90490 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 10 May 2024 16:42:37 -0600 Subject: [PATCH 0520/2019] [stdlib] Use `InlineArray` over `StaticTuple` in `os` module Replace uses of `StaticTuple` with `InlineArray`. Soon, `StaticTuple` will be deprecated. This requires removing some reg-passable annotations on types since `StaticTuple` was register passable trivial, but `InlineArray` is of course not. As a bonus, this should speed up compile times a little bit since in some cases, we were using a `StaticTuple` of size `1024` which would not compile very fast. See https://github.com/modularml/mojo/issues/2425. MODULAR_ORIG_COMMIT_REV_ID: 004b5334e3bd78a8a8054d6762501efc557df716 --- stdlib/src/os/_linux_aarch64.mojo | 7 +++---- stdlib/src/os/_linux_x86.mojo | 7 +++---- stdlib/src/os/_macos.mojo | 7 +++---- stdlib/src/os/os.mojo | 8 +++----- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index 67258ad01b..d2a0c30f03 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils import StaticTuple +from utils import InlineArray from .fstat import stat_result @@ -29,7 +29,6 @@ alias blksize_t = Int32 @value -@register_passable("trivial") struct _c_stat(Stringable): var st_dev: dev_t # ID of device containing file var st_ino: Int64 # File serial number @@ -47,7 +46,7 @@ struct _c_stat(Stringable): var st_mtimespec: _CTimeSpec # time of last data modification var st_ctimespec: _CTimeSpec # time of last status change var st_birthtimespec: _CTimeSpec # time of file creation(birth) - var unused: StaticTuple[Int64, 2] # RESERVED: DO NOT USE! + var unused: InlineArray[Int64, 2] # RESERVED: DO NOT USE! fn __init__(inout self): self.st_dev = 0 @@ -66,7 +65,7 @@ struct _c_stat(Stringable): self.st_mtimespec = _CTimeSpec() self.st_ctimespec = _CTimeSpec() self.st_birthtimespec = _CTimeSpec() - self.unused = StaticTuple[Int64, 2](0, 0) + self.unused = InlineArray[Int64, 2](0, 0) fn __str__(self) -> String: var res = String("{\n") diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index d8eb1ca3c0..ab36f40c95 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils.index import StaticTuple +from utils.index import InlineArray from .fstat import stat_result @@ -29,7 +29,6 @@ alias blksize_t = Int64 @value -@register_passable("trivial") struct _c_stat(Stringable): var st_dev: dev_t # ID of device containing file var st_ino: Int64 # File serial number @@ -46,7 +45,7 @@ struct _c_stat(Stringable): var st_mtimespec: _CTimeSpec # time of last data modification var st_ctimespec: _CTimeSpec # time of last status change var st_birthtimespec: _CTimeSpec # time of file creation(birth) - var unused: StaticTuple[Int64, 3] # RESERVED: DO NOT USE! + var unused: InlineArray[Int64, 3] # RESERVED: DO NOT USE! fn __init__(inout self): self.st_dev = 0 @@ -64,7 +63,7 @@ struct _c_stat(Stringable): self.st_mtimespec = _CTimeSpec() self.st_ctimespec = _CTimeSpec() self.st_birthtimespec = _CTimeSpec() - self.unused = StaticTuple[Int64, 3](0, 0, 0) + self.unused = InlineArray[Int64, 3](0, 0, 0) fn __str__(self) -> String: var res = String("{\n") diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 6f1601e23e..7c13100c99 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils import StaticTuple +from utils import InlineArray from .fstat import stat_result @@ -30,7 +30,6 @@ alias blksize_t = Int32 @value -@register_passable("trivial") struct _c_stat(Stringable): var st_dev: dev_t # ID of device containing file var st_mode: mode_t # Mode of file @@ -49,7 +48,7 @@ struct _c_stat(Stringable): var st_flags: UInt32 # user defined flags for file var st_gen: UInt32 # file generation number var st_lspare: Int32 # RESERVED: DO NOT USE! - var st_qspare: StaticTuple[Int64, 2] # RESERVED: DO NOT USE! + var st_qspare: InlineArray[Int64, 2] # RESERVED: DO NOT USE! fn __init__(inout self): self.st_dev = 0 @@ -69,7 +68,7 @@ struct _c_stat(Stringable): self.st_flags = 0 self.st_gen = 0 self.st_lspare = 0 - self.st_qspare = StaticTuple[Int64, 2](0, 0) + self.st_qspare = InlineArray[Int64, 2](0, 0) fn __str__(self) -> String: var res = String("{\n") diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 06f14b3913..bdaa870354 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -27,7 +27,7 @@ from memory import ( ) from memory.unsafe_pointer import move_from_pointee -from utils import StringRef, StaticTuple +from utils import StringRef, InlineArray from .path import isdir from .pathlike import PathLike @@ -52,7 +52,6 @@ alias SEEK_END: UInt8 = 2 @value -@register_passable("trivial") struct _dirent_linux: alias MAX_NAME_SIZE = 256 var d_ino: Int64 @@ -63,12 +62,11 @@ struct _dirent_linux: """Length of the record.""" var d_type: Int8 """Type of file.""" - var name: StaticTuple[Int8, Self.MAX_NAME_SIZE] + var name: InlineArray[Int8, Self.MAX_NAME_SIZE] """Name of entry.""" @value -@register_passable("trivial") struct _dirent_macos: alias MAX_NAME_SIZE = 1024 var d_ino: Int64 @@ -81,7 +79,7 @@ struct _dirent_macos: """Length of the name.""" var d_type: Int8 """Type of file.""" - var name: StaticTuple[Int8, Self.MAX_NAME_SIZE] + var name: InlineArray[Int8, Self.MAX_NAME_SIZE] """Name of entry.""" From d8a56470801441eb8dc7f241b718896bdbb05fdb Mon Sep 17 00:00:00 2001 From: GeauxEric Date: Fri, 10 May 2024 18:44:29 -0500 Subject: [PATCH 0521/2019] [External] [stdlib] Support print to stderr (#39796) [External] [stdlib] Support print to stderr Add keyword argument to `print` function to support stream to stderr. Fixes https://github.com/modularml/mojo/issues/2453. Signed-off-by: Yun Ding Co-authored-by: GeauxEric Closes modularml/mojo#2457 MODULAR_ORIG_COMMIT_REV_ID: 8530deea5047dbea191b6e87f8d113549bf9d121 --- stdlib/src/builtin/io.mojo | 51 +++++++++++++--------- stdlib/src/sys/__init__.mojo | 1 + stdlib/src/sys/_io.mojo | 15 +++++++ stdlib/test/builtin/test_print.mojo | 1 + stdlib/test/builtin/test_print_stderr.mojo | 34 +++++++++++++++ 5 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 stdlib/src/sys/_io.mojo create mode 100644 stdlib/test/builtin/test_print_stderr.mojo diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 651fd480ac..99e3f4b0db 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -15,7 +15,13 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys import os_is_windows, triple_is_nvidia_cuda, external_call +from sys import ( + bitwidthof, + os_is_windows, + triple_is_nvidia_cuda, + external_call, + stdout, +) from builtin.dtype import _get_dtype_printf_format from builtin.builtin_list import _LITRefPackHelper @@ -79,8 +85,8 @@ struct _fdopen: @no_inline -fn _flush(): - with _fdopen(_fdopen.STDOUT) as fd: +fn _flush(file: Int = stdout): + with _fdopen(file) as fd: _ = external_call["fflush", Int32](fd) @@ -90,7 +96,9 @@ fn _flush(): @no_inline -fn _printf[*types: AnyType](fmt: StringLiteral, *arguments: *types): +fn _printf[ + *types: AnyType +](fmt: StringLiteral, *arguments: *types, file: Int = stdout): # The argument pack will contain references for each value in the pack, # but we want to pass their values directly into the C snprintf call. Load # all the members of the pack. @@ -106,7 +114,7 @@ fn _printf[*types: AnyType](fmt: StringLiteral, *arguments: *types): fmt.unsafe_ptr(), UnsafePointer.address_of(loaded_pack) ) else: - with _fdopen(_fdopen.STDOUT) as fd: + with _fdopen(file) as fd: _ = __mlir_op.`pop.external_call`[ func = "KGEN_CompilerRT_fprintf".value, variadicType = __mlir_attr[ @@ -231,13 +239,14 @@ fn _float_repr(buffer: UnsafePointer[Int8], size: Int, x: Float64) -> Int: @no_inline -fn _put(x: Int): +fn _put(x: Int, file: Int = stdout): """Prints a scalar value. Args: x: The value to print. + file: The output stream. """ - _printf(_get_dtype_printf_format[DType.index](), x) + _printf(_get_dtype_printf_format[DType.index](), x, file=file) @no_inline @@ -298,13 +307,13 @@ fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): @no_inline -fn _put(x: String): +fn _put(x: String, file: Int = stdout): # 'x' is borrowed, so we know it will outlive the call to print. - _put(x._strref_dangerous()) + _put(x._strref_dangerous(), file=file) @no_inline -fn _put(x: StringRef): +fn _put(x: StringRef, file: Int = stdout): # Avoid printing "(null)" for an empty/default constructed `String` var str_len = len(x) @@ -323,26 +332,26 @@ fn _put(x: StringRef): # The string can be printed, so that's fine. if str_len < MAX_STR_LEN: - _printf("%.*s", x.length, x.data) + _printf("%.*s", x.length, x.data, file=file) return # The string is large, then we need to chunk it. var p = x.data while str_len: var ll = min(str_len, MAX_STR_LEN) - _printf("%.*s", ll, p) + _printf("%.*s", ll, p, file=file) str_len -= ll p += ll @no_inline -fn _put(x: StringLiteral): - _put(StringRef(x)) +fn _put(x: StringLiteral, file: Int = stdout): + _put(StringRef(x), file=file) @no_inline -fn _put(x: DType): - _put(str(x)) +fn _put(x: DType, file: Int = stdout): + _put(str(x), file=file) # ===----------------------------------------------------------------------=== # @@ -358,6 +367,7 @@ fn print[ sep: StringLiteral = " ", end: StringLiteral = "\n", flush: Bool = False, + file: Int = stdout, ): """Prints elements to the text stream. Each element is separated by `sep` and followed by `end`. @@ -370,21 +380,22 @@ fn print[ sep: The separator used between elements. end: The String to write after printing the elements. flush: If set to true, then the stream is forcibly flushed. + file: The output stream. """ @parameter fn print_with_separator[i: Int, T: Stringable](value: T): - _put(str(value)) + _put(str(value), file=file) @parameter if i < values.__len__() - 1: - _put(sep) + _put(sep, file=file) values.each_idx[print_with_separator]() - _put(end) + _put(end, file=file) if flush: - _flush() + _flush(file=file) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index c82137af02..911f9513cb 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -14,6 +14,7 @@ from .arg import argv from .debug import breakpointhook +from ._io import stderr, stdout from .ffi import RTLD, DEFAULT_RTLD, DLHandle, external_call from .info import ( is_x86, diff --git a/stdlib/src/sys/_io.mojo b/stdlib/src/sys/_io.mojo new file mode 100644 index 0000000000..105ef599c7 --- /dev/null +++ b/stdlib/src/sys/_io.mojo @@ -0,0 +1,15 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""IO constants and functions.""" +alias stdout: Int = 1 +alias stderr: Int = 2 diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 6ffb68f9c8..1a23fbfc45 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -16,6 +16,7 @@ from memory import DTypePointer from utils import StringRef, StaticIntTuple +import sys # CHECK-LABEL: test_print diff --git a/stdlib/test/builtin/test_print_stderr.mojo b/stdlib/test/builtin/test_print_stderr.mojo new file mode 100644 index 0000000000..f053a86588 --- /dev/null +++ b/stdlib/test/builtin/test_print_stderr.mojo @@ -0,0 +1,34 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s 2>&1 1>/dev/null | FileCheck %s --check-prefix=CHECK-STDERR + + +import sys + + +# CHECK-LABEL: test_print_stderr +fn test_print_stderr(): + # CHECK-STDERR: stderr + print("stderr", file=sys.stderr) + # CHECK-STDERR: a/b/c + print("a", "b", "c", sep="/", file=sys.stderr) + # CHECK-STDERR: world + print("world", flush=True, file=sys.stderr) + # CHECK-STDERR: helloworld + print("hello", end="world", file=sys.stderr) + # CHECK-STDERR: hello world + print(String("hello world"), file=sys.stderr) + + +fn main(): + test_print_stderr() From b89ba79881905f7655379438afb9f10a5eaeb9df Mon Sep 17 00:00:00 2001 From: Ilya Lubenets Date: Sat, 11 May 2024 10:59:42 -0500 Subject: [PATCH 0522/2019] [External] [stdlib] now `strip` can take `chars` argument and `String` now have useful aliases (#39814) [External] [stdlib] now `strip` can take `chars` argument and `String` now have useful aliases Two major changes: 1. `strip`, `lstrip` and `rstrip` now can remove custom chars 2. `String` now have useful aliases like Python's https://github.com/python/cpython/blob/0085c3ae8f067abd4f6540d0f6dd2fb13107618e/Lib/string.py#L24C1-L32C62 - Naming is identical to python's - Defaults to whitespace ` \n\t\r\f\v` without casting to Int8 - Tests are updated Co-authored-by: Ilya Lubenets Closes modularml/mojo#2555 MODULAR_ORIG_COMMIT_REV_ID: ca166203ba387950a70e9b053f46bd9687ee6936 --- docs/changelog.md | 5 ++ stdlib/src/builtin/string.mojo | 44 ++++++++++------ stdlib/test/builtin/test_string.mojo | 76 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7d476c09e2..f09922d799 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -143,6 +143,11 @@ what we publish. - `Dict.__get_ref(key)`, allowing to get references to dictionary values. +- `String.strip()`, `lstrip()` and `rstrip()` can now remove custom characters + other than whitespace. In addition, there are now several useful aliases for + whitespace, ASCII lower/uppercase, and so on. + ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index e9a0f62abf..9b4c3011a1 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -520,6 +520,17 @@ struct String( var _buffer: Self._buffer_type """The underlying storage for the string.""" + """ Useful string aliases. """ + alias WHITESPACE = String(" \n\t\r\f\v") + alias ASCII_LOWERCASE = String("abcdefghijklmnopqrstuvwxyz") + alias ASCII_UPPERCASE = String("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + alias ASCII_LETTERS = String.ASCII_LOWERCASE + String.ASCII_UPPERCASE + alias DIGITS = String("0123456789") + alias HEX_DIGITS = String.DIGITS + String("abcdef") + String("ABCDEF") + alias OCT_DIGITS = String("01234567") + alias PUNCTUATION = String("""!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~""") + alias PRINTABLE = String.DIGITS + String.ASCII_LETTERS + String.PUNCTUATION + String.WHITESPACE + @always_inline fn __str__(self) -> String: return self @@ -1360,43 +1371,46 @@ struct String( res.append(0) return String(res^) - fn strip(self) -> String: - """Return a copy of the string with leading and trailing whitespace characters removed. + fn strip(self, chars: String = String.WHITESPACE) -> String: + """Return a copy of the string with leading and trailing characters removed. - See `isspace` for a list of whitespace characters + Args: + chars: A set of characters to be removed. Defaults to whitespace. Returns: - A copy of the string with no leading or trailing whitespace characters. + A copy of the string with no leading or trailing characters. """ - return self.lstrip().rstrip() + return self.lstrip(chars).rstrip(chars) - fn rstrip(self) -> String: - """Return a copy of the string with trailing whitespace characters removed. + fn rstrip(self, chars: String = String.WHITESPACE) -> String: + """Return a copy of the string with trailing characters removed. - See `isspace` for a list of whitespace characters + Args: + chars: A set of characters to be removed. Defaults to whitespace. Returns: - A copy of the string with no trailing whitespace characters. + A copy of the string with no trailing characters. """ var r_idx = len(self) - while r_idx > 0 and isspace(ord(self[r_idx - 1])): + while r_idx > 0 and self[r_idx - 1] in chars: r_idx -= 1 return self[:r_idx] - fn lstrip(self) -> String: - """Return a copy of the string with leading whitespace characters removed. + fn lstrip(self, chars: String = String.WHITESPACE) -> String: + """Return a copy of the string with leading characters removed. - See `isspace` for a list of whitespace characters + Args: + chars: A set of characters to be removed. Defaults to whitespace. Returns: - A copy of the string with no leading whitespace characters. + A copy of the string with no leading characters. """ var l_idx = 0 - while l_idx < len(self) and isspace(ord(self[l_idx])): + while l_idx < len(self) and self[l_idx] in chars: l_idx += 1 return self[l_idx:] diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index c94145c9f7..4deebb84e2 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -631,7 +631,55 @@ fn test_isspace() raises: assert_false(isspace(ord("."))) +fn test_ascii_aliases() raises: + var whitespaces = String(" \n\t\r\f\v") + for i in range(len(whitespaces)): + assert_true(whitespaces[i] in String.WHITESPACE) + + assert_true(String("a") in String.ASCII_LOWERCASE) + assert_true(String("b") in String.ASCII_LOWERCASE) + assert_true(String("y") in String.ASCII_LOWERCASE) + assert_true(String("z") in String.ASCII_LOWERCASE) + + assert_true(String("A") in String.ASCII_UPPERCASE) + assert_true(String("B") in String.ASCII_UPPERCASE) + assert_true(String("Y") in String.ASCII_UPPERCASE) + assert_true(String("Z") in String.ASCII_UPPERCASE) + + assert_true(String("a") in String.ASCII_LETTERS) + assert_true(String("b") in String.ASCII_LETTERS) + assert_true(String("y") in String.ASCII_LETTERS) + assert_true(String("z") in String.ASCII_LETTERS) + assert_true(String("A") in String.ASCII_LETTERS) + assert_true(String("B") in String.ASCII_LETTERS) + assert_true(String("Y") in String.ASCII_LETTERS) + assert_true(String("Z") in String.ASCII_LETTERS) + + assert_true(String("0") in String.DIGITS) + assert_true(String("9") in String.DIGITS) + + assert_true(String("0") in String.HEX_DIGITS) + assert_true(String("9") in String.HEX_DIGITS) + assert_true(String("A") in String.HEX_DIGITS) + assert_true(String("F") in String.HEX_DIGITS) + + assert_true(String("7") in String.OCT_DIGITS) + assert_false(String("8") in String.OCT_DIGITS) + + assert_true(String(",") in String.PUNCTUATION) + assert_true(String(".") in String.PUNCTUATION) + assert_true(String("\\") in String.PUNCTUATION) + assert_true(String("@") in String.PUNCTUATION) + assert_true(String('"') in String.PUNCTUATION) + assert_true(String("'") in String.PUNCTUATION) + + var text = String("I love my Mom and Dad so much!!!\n") + for i in range(len(text)): + assert_true(text[i] in String.PRINTABLE) + + fn test_rstrip() raises: + # with default rstrip chars var empty_string = String("") assert_true(empty_string.rstrip() == "") @@ -647,8 +695,17 @@ fn test_rstrip() raises: var str2 = String("something \t\n\t\v\f") assert_true(str2.rstrip() == "something") + # with custom chars for rstrip + var str3 = String("mississippi") + assert_true(str3.rstrip("sip") == "m") + + var str4 = String("mississippimississippi \n ") + assert_true(str4.rstrip("sip ") == "mississippimississippi \n") + assert_true(str4.rstrip("sip \n") == "mississippim") + fn test_lstrip() raises: + # with default lstrip chars var empty_string = String("") assert_true(empty_string.lstrip() == "") @@ -664,8 +721,17 @@ fn test_lstrip() raises: var str2 = String(" \t\n\t\v\fsomething") assert_true(str2.lstrip() == "something") + # with custom chars for lstrip + var str3 = String("mississippi") + assert_true(str3.lstrip("mis") == "ppi") + + var str4 = String(" \n mississippimississippi") + assert_true(str4.lstrip("mis ") == "\n mississippimississippi") + assert_true(str4.lstrip("mis \n") == "ppimississippi") + fn test_strip() raises: + # with default strip chars var empty_string = String("") assert_true(empty_string.strip() == "") @@ -681,6 +747,15 @@ fn test_strip() raises: var str2 = String(" \t\n\t\v\fsomething \t\n\t\v\f") assert_true(str2.strip() == "something") + # with custom strip chars + var str3 = String("mississippi") + assert_true(str3.strip("mips") == "") + assert_true(str3.strip("mip") == "ssiss") + + var str4 = String(" \n mississippimississippi \n ") + assert_true(str4.strip(" ") == "\n mississippimississippi \n") + assert_true(str4.strip("\nmip ") == "ssissippimississ") + fn test_hash() raises: fn assert_hash_equals_literal_hash(s: StringLiteral) raises: @@ -784,6 +859,7 @@ def main(): # TODO(37393): Re-enable once we debug why we are depending on some debug behavior # on graviton. Showing an error in our O3 LLVM pipeline; could be a bug in LLVM. # test_isspace() + test_ascii_aliases() test_rstrip() test_lstrip() test_strip() From b2c78c49d448d4ce7c844ff5074a7459819f5212 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 11 May 2024 13:07:38 -0700 Subject: [PATCH 0523/2019] [mojo-stdlib] Improve doc comments for UnsafePointer. Improve doc comment on `destroy_pointee`. MODULAR_ORIG_COMMIT_REV_ID: 95499ed80cc4686ffbe3fbb3860e32a6763ed7ac --- stdlib/src/memory/unsafe_pointer.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index c6e1764325..04aa3c7601 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -357,7 +357,9 @@ fn destroy_pointee(ptr: UnsafePointer[_]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed - to contain a valid initialized instance of `T`. + to contain a valid initialized instance of `T`. This is equivalent to + `_ = move_from_pointee(ptr)` but doesn't require `Movable` and is more + efficient becase it doesn't invoke `__moveinit__`. Args: ptr: The pointer whose pointee this destroys. From a626cb7f6ca0dd09962e2615f14e798947ea74ab Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 11 May 2024 15:03:56 -0700 Subject: [PATCH 0524/2019] [mojo-stdlib] Change all `__refitem__` impls to return `Reference` Some were returning the MLIR internal reference type directly. MODULAR_ORIG_COMMIT_REV_ID: 98305f910e93f75b4c8c6f0fd9bb9ca2ae45bdc8 --- stdlib/src/memory/reference.mojo | 4 ++-- stdlib/src/memory/unsafe.mojo | 14 +++++++------- stdlib/src/memory/unsafe_pointer.mojo | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index c0d6946840..45da75b3bb 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -232,13 +232,13 @@ struct Reference[ # ===------------------------------------------------------------------===# @always_inline("nodebug") - fn __refitem__(self) -> Self._mlir_type: + fn __refitem__(self) -> Self: """Enable subscript syntax `ref[]` to access the element. Returns: The MLIR reference for the Mojo compiler to use. """ - return self.value + return self @always_inline("nodebug") fn __mlir_ref__(self) -> Self._mlir_type: diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 950c42db9f..c41683196a 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -179,12 +179,12 @@ struct LegacyPointer[ var address: Self._mlir_type """The pointed-to address.""" - alias _mlir_ref_type = Reference[ + alias _ref_type = Reference[ type, __mlir_attr.`1: i1`, __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, address_space, - ]._mlir_type + ] @always_inline("nodebug") fn __init__() -> Self: @@ -281,18 +281,18 @@ struct LegacyPointer[ ) @always_inline("nodebug") - fn __refitem__(self) -> Self._mlir_ref_type: + fn __refitem__(self) -> Self._ref_type: """Enable subscript syntax `ref[]` to access the element. Returns: The MLIR reference for the Mojo compiler to use. """ - return __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_ref_type]( - self.address - ) + return __mlir_op.`lit.ref.from_pointer`[ + _type = Self._ref_type._mlir_type + ](self.address) @always_inline("nodebug") - fn __refitem__[T: Intable](self, offset: T) -> Self._mlir_ref_type: + fn __refitem__[T: Intable](self, offset: T) -> Self._ref_type: """Enable subscript syntax `ref[idx]` to access the element. Parameters: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 04aa3c7601..0ae95b0fa0 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -319,7 +319,7 @@ struct UnsafePointer[ @always_inline fn __refitem__( self, - ) -> Self._ref_type._mlir_type: + ) -> Self._ref_type: """Return a reference to the underlying data, offset by the offset index. Returns: @@ -330,7 +330,7 @@ struct UnsafePointer[ ](self.address) @always_inline - fn __refitem__(self, offset: Int) -> Self._ref_type._mlir_type: + fn __refitem__(self, offset: Int) -> Self._ref_type: """Return a reference to the underlying data, offset by the offset index. Args: From 2d95403b4a2b24dda6927ebc44ff686b922b224e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 12 May 2024 14:42:43 -0700 Subject: [PATCH 0525/2019] [mojo-lang] Implementation mechanics for Reference autoderef. This is more progress towards implementing automatic deference for `Reference`. This: 1) Introduces a new `@automatically_dereference` struct decorator which signs a type up for autoderef and requires the type to have an `__mlir_ref__` member. 2) Adds type checking logic for that. 3) Adds `@automatically_dereference` to the various references in the testsuite and stdlib. 4) Switches some testcases from using `Reference(x).value` to use `__get_mvalue_as_litref(x)` instead. The former will autoderef in the future and be synonymous for `x.value` so move these test cases, to reduce future diffs. For what it's worth, I tried to do this without a decorator, but that suffers from a big problem: it means that we cannot determine what the RValue type of a value is without body resolving the type of the expression. We currently are lazy on this, and I don't want to start resolving lots of more types just because of references - a decorator allows us to just signature resolve the target struct, which we're already doing. Autoderef is not implemented yet, this is just progress towards it. MODULAR_ORIG_COMMIT_REV_ID: ab40ebf86f06bb1dd841293fd0197deb88b1ee3d --- stdlib/src/memory/reference.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 45da75b3bb..29f97c5402 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -185,6 +185,7 @@ struct AddressSpace(EqualityComparable): @value +@automatically_dereference @register_passable("trivial") struct Reference[ type: AnyType, From 1ed3fc05ea7ceceb2064bd60ec2cbdaae7ea0504 Mon Sep 17 00:00:00 2001 From: Jay Zhan Date: Sun, 12 May 2024 18:58:32 -0500 Subject: [PATCH 0526/2019] [External] [stdlib] Support `reversed` for `Dict.items()|values()` (#39351) [External] [stdlib] Support `reversed` for `Dict.items()|values()` Follow up PR on https://github.com/modularml/mojo/pull/2327. `Dict` now supports `reversed` for `dict.items()` and `dict.values()`. Co-authored-by: Jay Zhan Closes https://github.com/modularml/mojo/pull/2340 MODULAR_ORIG_COMMIT_REV_ID: 416c016f35d7e4062297656c99c1172d101bd488 --- docs/changelog.md | 3 + stdlib/src/builtin/reversed.mojo | 77 +++++++++++++++++++++++++- stdlib/src/collections/dict.mojo | 14 ++++- stdlib/test/builtin/test_reversed.mojo | 74 +++++++++++++++++++++---- 4 files changed, 153 insertions(+), 15 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f09922d799..3396edc605 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -148,6 +148,9 @@ what we publish. whitespace, ASCII lower/uppercase, and so on. ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) +- `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. + ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) + ### 🦋 Changed - The `abs` and `round` functions have moved from `math` to `builtin`, so you no diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 1e33320912..f96d6239ac 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -19,7 +19,7 @@ from .range import _StridedRangeIterator from collections.list import _ListIter -from collections.dict import _DictKeyIter +from collections.dict import _DictKeyIter, _DictValueIter, _DictEntryIter # ===----------------------------------------------------------------------=== # # Reversible @@ -119,6 +119,79 @@ fn reversed[ value: The dict to get the reversed iterator of. Returns: - The reversed iterator of the dict. + The reversed iterator of the dict keys. """ return value[].__reversed__() + + +fn reversed[ + mutability: __mlir_type.`i1`, + self_life: AnyLifetime[mutability].type, + K: KeyElement, + V: CollectionElement, + dict_mutability: __mlir_type.`i1`, + dict_lifetime: AnyLifetime[dict_mutability].type, +]( + value: Reference[ + _DictValueIter[K, V, dict_mutability, dict_lifetime], + mutability, + self_life, + ]._mlir_type, +) -> _DictValueIter[K, V, dict_mutability, dict_lifetime, False]: + """Get a reversed iterator of the input dict values. + + **Note**: iterators are currently non-raising. + + Parameters: + mutability: Whether the reference to the dict is mutable. + self_life: The lifetime of the dict. + K: The type of the keys in the dict. + V: The type of the values in the dict. + dict_mutability: Whether the reference to the dict values is mutable. + dict_lifetime: The lifetime of the dict values. + + Args: + value: The dict values to get the reversed iterator of. + + Returns: + The reversed iterator of the dict values. + """ + return Reference(value)[].__reversed__[mutability, self_life]() + + +fn reversed[ + mutability: __mlir_type.`i1`, + self_life: AnyLifetime[mutability].type, + K: KeyElement, + V: CollectionElement, + dict_mutability: __mlir_type.`i1`, + dict_lifetime: AnyLifetime[dict_mutability].type, +]( + value: Reference[ + _DictEntryIter[K, V, dict_mutability, dict_lifetime], + mutability, + self_life, + ]._mlir_type, +) -> _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]: + """Get a reversed iterator of the input dict items. + + **Note**: iterators are currently non-raising. + + Parameters: + mutability: Whether the reference to the dict is mutable. + self_life: The lifetime of the dict. + K: The type of the keys in the dict. + V: The type of the values in the dict. + dict_mutability: Whether the reference to the dict items is mutable. + dict_lifetime: The lifetime of the dict items. + + Args: + value: The dict items to get the reversed iterator of. + + Returns: + The reversed iterator of the dict items. + """ + var src = Reference(value)[].src + return _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( + src[]._reserved, 0, src + ) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 5fd6f39ce0..db9bfa56df 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -163,6 +163,7 @@ struct _DictValueIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, + forward: Bool = True, ]: """Iterator over Dict value references. These are mutable if the dict is mutable. @@ -172,15 +173,26 @@ struct _DictValueIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List + forward: The iteration direction. `False` is backwards. """ alias ref_type = Reference[V, dict_mutability, dict_lifetime] - var iter: _DictEntryIter[K, V, dict_mutability, dict_lifetime] + var iter: _DictEntryIter[K, V, dict_mutability, dict_lifetime, forward] fn __iter__(self) -> Self: return self + fn __reversed__[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ](self) -> _DictValueIter[K, V, dict_mutability, dict_lifetime, False]: + var src = self.iter.src + return _DictValueIter( + _DictEntryIter[K, V, dict_mutability, dict_lifetime, False]( + src[]._reserved, 0, src + ) + ) + fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() # Cast through a pointer to grant additional mutability because diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index a5bb3fffb1..1c08dc2ac0 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -30,7 +30,7 @@ def test_reversed_dict(): dict["b"] = 2 dict["c"] = 3 dict["d"] = 4 - dict["a"] = 5 + dict["a"] = 1 var keys = String("") for key in reversed(dict): @@ -38,11 +38,27 @@ def test_reversed_dict(): assert_equal(keys, "dcba") + var check: Int = 4 + for val in reversed(dict.values()): + assert_equal(val[], check) + check -= 1 + + keys = String("") + check = 4 + for item in reversed(dict.items()): + keys += item[].key + assert_equal(item[].value, check) + check -= 1 + + assert_equal(keys, "dcba") + # Order preserved _ = dict.pop("a") _ = dict.pop("c") + # dict: {'b': 2, 'd': 4} + keys = String("") for key in dict: keys += key[] @@ -55,29 +71,63 @@ def test_reversed_dict(): assert_equal(keys, "db") + # got 4 and 2 + check = 4 + for val in reversed(dict.values()): + assert_equal(val[], check) + check -= 2 + + keys = String("") + check = 4 + for item in reversed(dict.items()): + keys += item[].key + assert_equal(item[].value, check) + check -= 2 + + assert_equal(keys, "db") + + # Refill dict + dict["c"] = 2 + dict["a"] = 1 + dict["b"] = 4 + dict["d"] = 3 + + # dict: {'b': 4, 'd': 3, 'c': 2, 'a': 1} + + keys = String("") + check = 1 + for item in reversed(dict.items()): + keys += item[].key + assert_equal(item[].value, check) + check += 1 + + assert_equal(keys, "acdb") + # Empty dict is iterable - _ = dict.pop("b") - _ = dict.pop("d") + var empty_dict = Dict[String, Int]() keys = String("") - for key in reversed(dict): + for key in reversed(empty_dict): keys += key[] assert_equal(keys, "") - # Refill dict + check = 0 + for val in reversed(empty_dict.values()): + # values is empty, should not reach here + check += 1 - dict["d"] = 4 - dict["a"] = 1 - dict["b"] = 2 - dict["e"] = 3 + assert_equal(check, 0) keys = String("") - for key in reversed(dict): - keys += key[] + check = 0 + for item in reversed(empty_dict.items()): + keys += item[].key + check += item[].value - assert_equal(keys, "ebad") + assert_equal(keys, "") + assert_equal(check, 0) def main(): From 1e3c6de0002c853e789507afc45c6faabdb93121 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 13 May 2024 09:49:24 -0700 Subject: [PATCH 0527/2019] [mojo-lang] Let's let `let` be an identifier once more `let` has been returned to the wilds, free to be an identifier once again. This PR completes removal of `let` from the language by removing the token kind entirely. This removes the warning that `let` is no longer supported. It has been sufficient time. MODULAR_ORIG_COMMIT_REV_ID: a9c26c315329ee74b33321740d92c36a9ff24f38 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 3396edc605..c05f0a5dc0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -153,6 +153,10 @@ what we publish. ### 🦋 Changed +- The `let` keyword has been completely removed from the language. We previously + removed `let` declarations but still provided an error message to users. Now, + it is completely gone from the grammar. Long live `var`! + - The `abs` and `round` functions have moved from `math` to `builtin`, so you no longer need to do `from math import abs, round`. From 87422a1265f84679fc0faa4a90fd5d45c9256eb7 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 13 May 2024 12:57:55 -0400 Subject: [PATCH 0528/2019] [stdlib] Clean up weird uses of `assert_equal` in testscases Changed `assert_equal({True, False}, ...)` to `assert_true` and `assert_false`. MODULAR_ORIG_COMMIT_REV_ID: c57cc9172b13b76afe5b79fa643c3e2f697b1e1b --- stdlib/test/builtin/test_simd.mojo | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 9ce121c28a..449d555b6f 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -14,7 +14,7 @@ from sys import has_neon -from testing import assert_equal, assert_not_equal, assert_true +from testing import assert_equal, assert_not_equal, assert_true, assert_false def test_cast(): @@ -122,16 +122,16 @@ def test_truthy(): @parameter fn test_dtype[type: DType]() raises: # # Scalars of 0-values are false-y, 1-values are truth-y - assert_equal(False, Scalar[type](False).__bool__()) - assert_equal(True, Scalar[type](True).__bool__()) + assert_false(Scalar[type](False).__bool__()) + assert_true(Scalar[type](True).__bool__()) # # SIMD vectors are truth-y if _all_ values are truth-y - assert_equal(True, SIMD[type, 2](True, True).__bool__()) + assert_true(SIMD[type, 2](True, True).__bool__()) # # SIMD vectors are false-y if _any_ values are false-y - assert_equal(False, SIMD[type, 2](False, True).__bool__()) - assert_equal(False, SIMD[type, 2](True, False).__bool__()) - assert_equal(False, SIMD[type, 2](False, False).__bool__()) + assert_false(SIMD[type, 2](False, True).__bool__()) + assert_false(SIMD[type, 2](True, False).__bool__()) + assert_false(SIMD[type, 2](False, False).__bool__()) @parameter fn test_dtype_unrolled[i: Int]() raises: From ad815638e3720daa8e52beb2e3c42991a611969c Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 13 May 2024 12:44:30 -0600 Subject: [PATCH 0529/2019] [docs] Fix changelog for `repr` functionality This was added by Gabriel, so give proper attrition. This looks to just be an oversight originally. MODULAR_ORIG_COMMIT_REV_ID: 3471128703755234b9ba618a624b6717517992c7 --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index c05f0a5dc0..86f054df25 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -100,7 +100,7 @@ what we publish. ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) - Add `repr()` function and `Representable` trait. - ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@mikowals](https://github.com/gabrieldemarmiesse)) + ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) - Add `SIMD.shuffle()` with `StaticIntTuple` mask. ([PR #2315](https://github.com/modularml/mojo/pull/2315) by [@mikowals](https://github.com/mikowals)) From 206970dc82026611076f04c7c193bea084598eab Mon Sep 17 00:00:00 2001 From: Chad Date: Mon, 13 May 2024 14:58:07 -0400 Subject: [PATCH 0530/2019] [stdlib] Optimizes the range function Optimizes the range function in some cases. MODULAR_ORIG_COMMIT_REV_ID: dabb154b20f978e0407fbce16f5bd77aee65b87e --- stdlib/src/builtin/range.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 7d58fff846..2d30acb3c0 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -65,7 +65,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange): @always_inline("nodebug") fn __init__(inout self, end: Int): self.curr = max(0, end) - self.end = end + self.end = self.curr @always_inline("nodebug") fn __iter__(self) -> Self: From 020537b44e3c7bc1ccec71129d012627912d6aa0 Mon Sep 17 00:00:00 2001 From: Steffi Stumpos Date: Mon, 13 May 2024 14:51:31 -0600 Subject: [PATCH 0531/2019] [stdlib] String Constructor Does Not Depend On StringRef Enable StringRef Dependency on String MODULAR_ORIG_COMMIT_REV_ID: 3685dcb055d89f4d78b405db5c4ade6ad4ce35a2 --- stdlib/src/builtin/string.mojo | 7 +++---- stdlib/src/builtin/string_literal.mojo | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 9b4c3011a1..483c984c76 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -641,14 +641,13 @@ struct String( self._buffer = buffer^ @always_inline - fn __init__(inout self, str: StringLiteral): + fn __init__(inout self, literal: StringLiteral): """Constructs a String value given a constant string. Args: - str: The input constant string. + literal: The input constant string. """ - - self = String(StringRef(str)) + self = literal.__str__() fn __init__[stringable: Stringable](inout self, value: stringable): """Creates a string from a value that conforms to Stringable trait. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index ffac052ba4..8452c31939 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -197,7 +197,22 @@ struct StringLiteral( Returns: A new string. """ - return self + var string = String() + var length: Int = __mlir_op.`pop.string.size`(self.value) + var buffer = String._buffer_type() + var new_capacity = length + 1 + buffer._realloc(new_capacity) + buffer.size = new_capacity + var uint8Ptr = __mlir_op.`pop.pointer.bitcast`[ + _type = __mlir_type.`!kgen.pointer>` + ](__mlir_op.`pop.string.address`(self.value)) + var data: DTypePointer[DType.uint8] = DTypePointer[DType.uint8]( + uint8Ptr + ) + memcpy(rebind[DTypePointer[DType.uint8]](buffer.data), data, length) + initialize_pointee_move(buffer.data + length, 0) + string._buffer = buffer^ + return string fn __repr__(self) -> String: """Return a representation of the `StringLiteral` instance. From c152fdb2b057a09b25149d62cf1a9965da6afd36 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Mon, 13 May 2024 18:23:31 -0400 Subject: [PATCH 0532/2019] [stdlib] Added tests for `memset` The current tests for `memset` only include `memset` with 0 in the argument. The additional tests also check validity when `memset` is called with non-zero values. MODULAR_ORIG_COMMIT_REV_ID: b54197f903fee8482e10ec6fc03d907db78a4995 --- stdlib/test/memory/test_memory.mojo | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index e59d993f4d..d42488a471 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -14,7 +14,7 @@ from sys import sizeof -from memory import memcmp, memcpy, memset_zero, DTypePointer, Pointer +from memory import memcmp, memcpy, memset, memset_zero, DTypePointer, Pointer from utils._numerics import nan from testing import ( assert_almost_equal, @@ -293,6 +293,16 @@ def test_memset(): assert_equal(pair.lo, 0) assert_equal(pair.hi, 0) + var buf0 = DTypePointer[DType.int32].alloc(2) + memset(buf0, 1, 2) + assert_equal(buf0.load(0), 16843009) + memset(buf0, -1, 2) + assert_equal(buf0.load(0), -1) + + var buf1 = DTypePointer[DType.int8].alloc(2) + memset(buf1, 5, 2) + assert_equal(buf1.load(0), 5) + def test_pointer_string(): var nullptr = Pointer[Int]() From 75c9f26d15fb67b160c3e39ecb02b54624a01773 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 13 May 2024 18:00:49 -0600 Subject: [PATCH 0533/2019] [stdlib] Remove unused import in python tests Remove some unused imports in a few test files. MODULAR_ORIG_COMMIT_REV_ID: c42bbfc0f593a6063729aedde761b7ccd2564680 --- stdlib/test/python/test_ownership.mojo | 1 - stdlib/test/python/test_python_error_handling.mojo | 1 - stdlib/test/python/test_python_interop.mojo | 1 - stdlib/test/python/test_python_object.mojo | 1 - 4 files changed, 4 deletions(-) diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index 83fcc3341e..396d84e51e 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -15,7 +15,6 @@ from sys import env_get_string -from python._cpython import CPython, PyObjectPtr from python import PythonObject, Python from testing import assert_equal diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index 53ed77e8dc..a945000726 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -16,7 +16,6 @@ from sys import env_get_string from python import Python, PythonObject -from python._cpython import CPython, PyObjectPtr from testing import assert_equal, assert_raises diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index f052ea6f4d..1ff266319d 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -15,7 +15,6 @@ from sys import env_get_string -from python._cpython import CPython, PyObjectPtr from python.python import Python, _get_global_python_itf, PythonObject from testing import assert_equal diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index b89838541a..7a1c6fd42d 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -13,7 +13,6 @@ # XFAIL: asan && !system-darwin # RUN: %mojo %s -from python._cpython import CPython, PyObjectPtr from python import PythonObject, Python from testing import assert_false, assert_raises, assert_true, assert_equal From 387062d929fa6dc8afa331f51fc758dfc055a612 Mon Sep 17 00:00:00 2001 From: Helehex Date: Mon, 13 May 2024 19:39:18 -0500 Subject: [PATCH 0534/2019] [External] [stdlib] Add `Defaultable` trait (#39797) [External] [stdlib] Add `Defaultable` trait Add a `Defaultable` trait for describing a type which has a constructor with no arguments. Co-authored-by: Helehex Closes modularml/mojo#2526 MODULAR_ORIG_COMMIT_REV_ID: aed69a25ecf531873f944466a1cb5d7652dd7425 --- stdlib/src/builtin/value.mojo | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 909a8197d4..1029c42909 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -99,6 +99,40 @@ trait Copyable: ... +trait Defaultable: + """The `Defaultable` trait describes a type with a default constructor. + + Implementing the `Defaultable` trait requires the type to define + an `__init__` method with no arguments: + + ```mojo + struct Foo(Defaultable): + var s: String + + fn __init__(inout self): + self.s = "default" + ``` + + You can now construct a generic `Defaultable` type: + + ```mojo + fn default_init[T: Defaultable]() -> T: + return T() + + var foo = default_init[Foo]() + print(foo.s) + ``` + + ```plaintext + default + ``` + """ + + fn __init__(inout self): + """Create a default instance of the value.""" + ... + + trait CollectionElement(Copyable, Movable): """The CollectionElement trait denotes a trait composition of the `Copyable` and `Movable` traits. From 270de8e010b674d43e8a76f211ec52d7a19188a3 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Tue, 14 May 2024 07:21:38 -0500 Subject: [PATCH 0535/2019] [External] [stdlib] Add method `unsafe_ptr()` to `InlineArray` (#39871) [External] [stdlib] Add method `unsafe_ptr()` to `InlineArray` This is pretty useful to implement short string optimization. See https://github.com/modularml/mojo/issues/2467 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2642 MODULAR_ORIG_COMMIT_REV_ID: 5739e8a67742c1841ca3c33efcd23bcc45048b86 --- stdlib/src/utils/static_tuple.mojo | 17 +++++++++++++++++ stdlib/test/utils/test_tuple.mojo | 25 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index cf612df27f..7c91e231f2 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -404,3 +404,20 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): normalized_idx += size return self[]._get_reference_unsafe(normalized_idx) + + @always_inline + fn unsafe_ptr( + self: Reference[Self, _, _] + ) -> UnsafePointer[Self.ElementType]: + """Get an `UnsafePointer` to the underlying array. + + That pointer is unsafe but can be used to read or write to the array. + Be careful when using this. As opposed to a pointer to a `List`, + this pointer becomes invalid when the `InlineArray` is moved. + + Make sure to refresh your pointer every time the `InlineArray` is moved. + + Returns: + An `UnsafePointer` to the underlying array. + """ + return UnsafePointer(self[]._array).bitcast[Self.ElementType]() diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index c28e68baf4..327f23450d 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -162,9 +162,34 @@ def test_array_str(): assert_equal(arr3[0], "hi") +def test_array_int_pointer(): + var arr = InlineArray[Int, 3](0, 10, 20) + + var ptr = arr.unsafe_ptr() + assert_equal(ptr[0], 0) + assert_equal(ptr[1], 10) + assert_equal(ptr[2], 20) + + ptr[0] = 0 + ptr[1] = 1 + ptr[2] = 2 + + assert_equal(arr[0], 0) + assert_equal(arr[1], 1) + assert_equal(arr[2], 2) + + assert_equal(ptr[0], 0) + assert_equal(ptr[1], 1) + assert_equal(ptr[2], 2) + + # We make sure it lives long enough + _ = arr + + def main(): test_static_tuple() test_static_int_tuple() test_tuple_literal() test_array_int() test_array_str() + test_array_int_pointer() From dd89c9e96ab9a6f904613a1acd1de2b2a142a686 Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 14 May 2024 12:25:07 -0400 Subject: [PATCH 0536/2019] [stdlib] Provide safe `Variant` and `Optional` `take/get` methods The safe version of `take/get` aborts if type checking fails. MODULAR_ORIG_COMMIT_REV_ID: 240b627f31860a674e5878a1467e3c39feadce1b --- stdlib/src/collections/optional.mojo | 45 ++++++++++++++++++++-- stdlib/src/utils/variant.mojo | 56 +++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 62c5467f95..cf20159fdc 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -102,6 +102,25 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): @always_inline fn value( self: Reference[Self, _, _] + ) -> Reference[T, self.is_mutable, self.lifetime]: + """Retrieve a reference to the value of the Optional. + + This check to see if the optional contains a value. + If you call this without first verifying the optional with __bool__() + eg. by `if my_option:` or without otherwise knowing that it contains a + value (for instance with `or_else`), the program will abort + + Returns: + A reference to the contained data of the option as a Reference[T]. + """ + if not self[].__bool__(): + abort(".value() on empty Optional") + + return self[].unsafe_value() + + @always_inline + fn unsafe_value( + self: Reference[Self, _, _] ) -> Reference[T, self.is_mutable, self.lifetime]: """Unsafely retrieve a reference to the value of the Optional. @@ -128,16 +147,36 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): debug_assert(self.__bool__(), ".value() on empty Optional") return self._value[T] + fn take(inout self) -> T: + """Move the value out of the Optional. + + The caller takes ownership over the new value, which is moved + out of the Optional, and the Optional is left in an empty state. + destroyed. + + This check to see if the optional contains a value. + If you call this without first verifying the optional with __bool__() + eg. by `if my_option:` or without otherwise knowing that it contains a + value (for instance with `or_else`), you'll get garbage unsafe data out. + + Returns: + The contained data of the option as an owned T value. + """ + if not self.__bool__(): + abort(".take() on empty Optional") + return self.unsafe_take() + fn unsafe_take(inout self) -> T: """Unsafely move the value out of the Optional. - The caller takes ownership over the new value, and the Optional is + The caller takes ownership over the new value, which is moved + out of the Optional, and the Optional is left in an empty state. destroyed. - This doesn't check to see if the optional contains a value. + This check to see if the optional contains a value. If you call this without first verifying the optional with __bool__() eg. by `if my_option:` or without otherwise knowing that it contains a - value (for instance with `or_else`), you'll get garbage unsafe data out. + value (for instance with `or_else`), the program will abort! Returns: The contained data of the option as an owned T value. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index d176c75545..6595c7ddbf 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -223,12 +223,35 @@ struct Variant[*Ts: CollectionElement](CollectionElement): unroll[each, len(VariadicList(Ts))]() - fn unsafe_take[T: CollectionElement](inout self) -> T: + @always_inline + fn take[T: CollectionElement](inout self) -> T: """Take the current value of the variant as the provided type. The caller takes ownership of the underlying value. The variant type is consumed without calling any deleters. + This explicitly check that your value is of that type! + If you haven't verified the type correctness at runtime, the program + will abort! + + Parameters: + T: The type to take. + + Returns: + The underlying data as an owned value. + """ + if not self.isa[T](): + abort("taking the wrong type!") + + return self.unsafe_take[T]() + + @always_inline + fn unsafe_take[T: CollectionElement](inout self) -> T: + """Unsafely take the current value of the variant as the provided type. + + The caller takes ownership of the underlying value. The variant + type is consumed without calling any deleters. + This doesn't explicitly check that your value is of that type! If you haven't verified the type correctness at runtime, you'll get a type that _looks_ like your type, but has potentially unsafe @@ -240,9 +263,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Returns: The underlying data as an owned value. """ - debug_assert( - Self._check[T]() == self._get_state()[], "taking wrong type" - ) + debug_assert(self.isa[T](), "taking wrong type") # don't call the variant's deleter later self._get_state()[] = Self._sentinel return move_from_pointee(self._get_ptr[T]()) @@ -275,7 +296,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): alias idx = Self._check[T]() return self._get_state()[] == idx - fn __refitem__[ + fn unsafe_get[ T: CollectionElement ](self: Reference[Self, _, _]) -> Reference[ T, self.is_mutable, self.lifetime @@ -299,6 +320,31 @@ struct Variant[*Ts: CollectionElement](CollectionElement): debug_assert(self[].isa[T](), "get: wrong variant type") return self[]._get_ptr[T]()[] + fn __refitem__[ + T: CollectionElement + ](self: Reference[Self, _, _]) -> Reference[ + T, self.is_mutable, self.lifetime + ]: + """Get the value out of the variant as a type-checked type. + + This explicitly check that your value is of that type! + If you haven't verified the type correctness at runtime, the program + will abort! + + For now this has the limitations that it + - requires the variant value to be mutable + + Parameters: + T: The type of the value to get out. + + Returns: + The internal data represented as a `Reference[T]`. + """ + if not self[].isa[T](): + abort("get: wrong variant type") + + return self[].unsafe_get[T]() + @staticmethod fn _check[T: CollectionElement]() -> Int8: return _UnionTypeIndex[T, Ts].compute() From 5cfdd2dc970d5b28a4c981d7ed108061c20ab71b Mon Sep 17 00:00:00 2001 From: N <47500890+avi-cenna@users.noreply.github.com> Date: Tue, 14 May 2024 12:32:01 -0500 Subject: [PATCH 0537/2019] [External] [docs] Correct typo in types.ipynb (#39888) [External] [docs] Correct typo in types.ipynb Correcting typo. Co-authored-by: N <47500890+avi-cenna@users.noreply.github.com> Closes modularml/mojo#2646 MODULAR_ORIG_COMMIT_REV_ID: aef3bb9dca96efdc6badc7b9369f14583ea2c71a --- docs/manual/types.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 79169dde45..09fda5d6fc 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -831,7 +831,7 @@ "### Optional\n", "\n", "The [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", - "value that may or may not be present. Like the other collectypetion types, it is\n", + "value that may or may not be present. Like the other collection types, it is\n", "generic, and can hold any type that conforms to the\n", "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait." ] From 2b9055262cff7eb05208d1bcddc5fccba5f2b965 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 14 May 2024 14:34:57 -0400 Subject: [PATCH 0538/2019] [stdlib] Make `trunc` generic This is done by introducing a `Truncable` trait. Implementations of `__trunc__` are added for the core numeric types. MODULAR_ORIG_COMMIT_REV_ID: 0b70cb6eaf05651ceb676eda40db6d364b0c9995 --- docs/changelog.md | 16 +++++---- stdlib/src/builtin/_math.mojo | 33 +++++++++++++++++ stdlib/src/builtin/float_literal.mojo | 20 ++++++++++- stdlib/src/builtin/int.mojo | 12 ++++++- stdlib/src/builtin/int_literal.mojo | 12 ++++++- stdlib/src/builtin/simd.mojo | 39 +++++++++++++-------- stdlib/test/builtin/test_float_literal.mojo | 16 +++++++++ stdlib/test/builtin/test_int.mojo | 7 ++++ stdlib/test/builtin/test_int_literal.mojo | 7 ++++ stdlib/test/builtin/test_simd.mojo | 24 +++++++++++++ 10 files changed, 162 insertions(+), 24 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 86f054df25..2faf304d63 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -71,16 +71,17 @@ what we publish. `builtin`, so you no longer need to do `from math import abs, round, min, max, divmod`. -- Mojo now allows types to opt in to use the `floor()` and `ceil()` functions in - the `math` module by implementing the `__floor__()` and `__ceil__()` methods - (and so conforming to the new `math.Floorable` and `math.Ceilable` traits, - respectively). For example: +- Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` + functions in the `math` module by implementing the `__floor__()`, + `__ceil__()`, and `__trunc__()` methods (and so conforming to the new + `math.Floorable`, `math.Ceilable`, and `math.Truncable` traits, respectively). + For example: ```mojo - from math import Ceilable, Floorable, ceil, floor + from math import Ceilable, Floorable, Truncable, ceil, floor, trunc @value - struct Complex(Ceilable, Floorable): + struct Complex(Ceilable, Floorable, Truncable): var re: Float64 var im: Float64 @@ -89,6 +90,9 @@ what we publish. fn __floor__(self) -> Self: return Self(floor(re), floor(im)) + + fn __trunc__(self) -> Self: + return Self(trunc(re), trunc(im)) ``` - Add an `InlinedArray` type that works on memory-only types. diff --git a/stdlib/src/builtin/_math.mojo b/stdlib/src/builtin/_math.mojo index 834573cd1a..8250806ce7 100644 --- a/stdlib/src/builtin/_math.mojo +++ b/stdlib/src/builtin/_math.mojo @@ -162,3 +162,36 @@ trait CeilDivableRaising: fn __neg__(self) raises -> Self: ... + + +# ===----------------------------------------------------------------------=== # +# Truncable +# ===----------------------------------------------------------------------=== # + + +trait Truncable: + """ + The `Truncable` trait describes a type that defines a truncation operation. + + Types that conform to `Truncable` will work with the builtin `trunc` + function. The truncation operation always returns the same type as the + input. + + For example: + ```mojo + from math import Truncable, trunc + + @value + struct Complex(Truncable): + var re: Float64 + var im: Float64 + + fn __trunc__(self) -> Self: + return Self(trunc(re), trunc(im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __trunc__(self) -> Self: + ... diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 624827f110..3ab8aea135 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from builtin._math import Ceilable, CeilDivable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable, Truncable # ===----------------------------------------------------------------------===# # FloatLiteral @@ -34,6 +34,7 @@ struct FloatLiteral( Floorable, Intable, Stringable, + Truncable, ): """Mojo floating point literal type.""" @@ -221,6 +222,23 @@ struct FloatLiteral( return truncated return truncated + 1 + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Truncates the floating point literal. If there is a fractional + component, then the value is truncated towards zero. + + For example, `(4.5).__trunc__()` returns `4.0`, and `(-3.7).__trunc__()` + returns `-3.0`. + + Returns: + The truncated FloatLiteral value. + """ + + # Handle special values first. + if not self._is_normal(): + return self + return Self(self.__int_literal__()) + # TODO: implement __round__ # ===------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 83817e228d..e51ec10d30 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import KeyElement -from builtin._math import Ceilable, CeilDivable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf @@ -204,6 +204,7 @@ struct Int( KeyElement, Roundable, Stringable, + Truncable, ): """This type represents an integer value.""" @@ -532,6 +533,15 @@ struct Int( """ return self + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Return the truncated Int value, which is itself. + + Returns: + The Int value itself. + """ + return self + @always_inline("nodebug") fn __invert__(self) -> Int: """Return ~self. diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index d7c06132c9..e4a13b06e8 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements the IntLiteral class.""" -from builtin._math import Ceilable, CeilDivable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable, Truncable @value @@ -28,6 +28,7 @@ struct IntLiteral( Intable, Roundable, Stringable, + Truncable, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and @@ -258,6 +259,15 @@ struct IntLiteral( """ return self + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Return the truncated of the IntLiteral value, which is itself. + + Returns: + The IntLiteral value itself. + """ + return self + @always_inline("nodebug") fn __invert__(self) -> Self: """Return ~self. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6e2063356a..dd0b30cdec 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -25,7 +25,7 @@ from sys import ( _RegisterPackType, ) -from builtin._math import Ceilable, CeilDivable, Floorable +from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from memory import bitcast @@ -132,6 +132,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Roundable, Sized, Stringable, + Truncable, ): """Represents a small vector that is backed by a hardware vector element. @@ -903,9 +904,11 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return (self < 0).select(-self, self) - fn _floor_ceil_impl[intrinsic: StringLiteral](self) -> Self: + fn _floor_ceil_trunc_impl[intrinsic: StringLiteral](self) -> Self: constrained[ - intrinsic == "llvm.floor" or intrinsic == "llvm.ceil", + intrinsic == "llvm.floor" + or intrinsic == "llvm.ceil" + or intrinsic == "llvm.trunc", "unsupported intrinsic", ]() @@ -917,13 +920,11 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if has_neon() and type == DType.bfloat16: return ( self.cast[DType.float32]() - ._floor_ceil_impl[intrinsic]() + ._floor_ceil_trunc_impl[intrinsic]() .cast[type]() ) - return llvm_intrinsic[ - intrinsic, __type_of(self), has_side_effect=False - ](self) + return llvm_intrinsic[intrinsic, Self, has_side_effect=False](self) @always_inline("nodebug") fn __floor__(self) -> Self: @@ -932,7 +933,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Returns: The elementwise floor of this SIMD vector. """ - return self._floor_ceil_impl["llvm.floor"]() + return self._floor_ceil_trunc_impl["llvm.floor"]() @always_inline("nodebug") fn __ceil__(self) -> Self: @@ -941,7 +942,17 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Returns: The elementwise ceiling of this SIMD vector. """ - return self._floor_ceil_impl["llvm.ceil"]() + return self._floor_ceil_trunc_impl["llvm.ceil"]() + + @always_inline("nodebug") + fn __trunc__(self) -> Self: + """Performs elementwise truncation on the elements of a SIMD vector. + + Returns: + The elementwise truncated values of this SIMD vector. + """ + + return self._floor_ceil_trunc_impl["llvm.trunc"]() fn clamp(self, lower_bound: Self, upper_bound: Self) -> Self: """Clamps the values in a SIMD vector to be in a certain range. @@ -972,9 +983,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Returns: The elementwise banker's rounding of this SIMD vector. """ - return llvm_intrinsic[ - "llvm.roundeven", __type_of(self), has_side_effect=False - ](self) + return llvm_intrinsic["llvm.roundeven", Self, has_side_effect=False]( + self + ) @always_inline("nodebug") fn __round__(self) -> Self: @@ -985,9 +996,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Returns: The elementwise rounded value of this SIMD vector. """ - return llvm_intrinsic[ - "llvm.round", __type_of(self), has_side_effect=False - ](self) + return llvm_intrinsic["llvm.round", Self, has_side_effect=False](self) # ===-------------------------------------------------------------------===# # In place operations. diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index d2372a4e34..d0a8406491 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -54,6 +54,21 @@ def test_floor(): assert_equal(FloatLiteral.__floor__(neg_inf), neg_inf) +def test_trunc(): + pass + # assert_equal(FloatLiteral.__trunc__(1.5), 1.0) + # assert_equal(FloatLiteral.__trunc__(1.6), 1.0) + # assert_equal(FloatLiteral.__trunc__(-1.5), -1.0) + # assert_equal(FloatLiteral.__trunc__(-3.6), -3.0) + # assert_equal(FloatLiteral.__trunc__(3.0), 3.0) + # assert_equal(FloatLiteral.__trunc__(0.0), 0.0) + + # assert_true(FloatLiteral.__trunc__(nan).is_nan()) + # assert_true(FloatLiteral.__trunc__(neg_zero).is_neg_zero()) + # assert_equal(FloatLiteral.__trunc__(inf), inf) + # assert_equal(FloatLiteral.__trunc__(neg_inf), neg_inf) + + fn round10(x: Float64) -> Float64: # TODO: implement __div__ on FloatLiteral? return (round(Float64(x * 10)) / 10).value @@ -138,6 +153,7 @@ def test_abs(): def main(): test_ceil() test_floor() + test_trunc() test_round10() test_division() test_power() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 1b4220fdb0..7fbe49c04d 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -67,6 +67,12 @@ def test_round(): assert_equal(Int.__round__(Int(-5)), -5) +def test_trunc(): + assert_equal(Int.__trunc__(Int(5)), 5) + assert_equal(Int.__trunc__(Int(0)), 0) + assert_equal(Int.__trunc__(Int(-5)), -5) + + def test_floordiv(): assert_equal(1, Int(2) // Int(2)) assert_equal(0, Int(2) // Int(3)) @@ -147,6 +153,7 @@ def main(): test_ceil() test_floor() test_round() + test_trunc() test_floordiv() test_mod() test_divmod() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 5c74aab764..697034cf47 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -40,6 +40,12 @@ def test_round(): assert_equal(IntLiteral.__round__(-5), -5) +def test_trunc(): + assert_equal(IntLiteral.__trunc__(5), 5) + assert_equal(IntLiteral.__trunc__(0), 0) + assert_equal(IntLiteral.__trunc__(-5), -5) + + def test_floordiv(): assert_equal(2 // 2, 1) assert_equal(2 // 3, 0) @@ -77,6 +83,7 @@ def main(): test_ceil() test_floor() test_round() + test_trunc() test_floordiv() test_mod() test_bit_width() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 449d555b6f..c1ace27899 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -192,6 +192,29 @@ def test_floor(): assert_equal(B.__floor__(b), b) +def test_trunc(): + assert_equal(Float32.__trunc__(Float32(1.5)), 1.0) + assert_equal(Float32.__trunc__(Float32(-1.5)), -1.0) + assert_equal(Float32.__trunc__(Float32(3.0)), 3.0) + + alias F = SIMD[DType.float32, 4] + assert_equal( + F.__trunc__(F(0.0, 1.6, -42.5, -12.4)), F(0.0, 1.0, -42.0, -12.0) + ) + + alias I = SIMD[DType.int32, 4] + var i = I(0, 2, -42, -12) + assert_equal(I.__trunc__(i), i) + + alias U = SIMD[DType.uint32, 4] + var u = U(0, 2, 42, 12) + assert_equal(U.__trunc__(u), u) + + alias B = SIMD[DType.bool, 4] + var b = B(True, False, True, False) + assert_equal(B.__trunc__(b), b) + + def test_round(): assert_equal(Float32.__round__(Float32(2.5)), 3.0) assert_equal(Float32.__round__(Float32(-3.5)), -4.0) @@ -910,6 +933,7 @@ def main(): test_truthy() test_ceil() test_floor() + test_trunc() test_round() test_roundeven() test_floordiv() From e7460677ce7aa03aba5c9bf703feb8560d585dad Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 14 May 2024 13:21:49 -0700 Subject: [PATCH 0539/2019] [mojo-stdlib] Make `Tuple`'s constructor move its input elements. (#39904) This changes `Tuple` to take its input pack as 'owned' and then move from the pack into it storage. This unearthed some bugs handling owned packs which were causing multiple destructions, as a consequence of VariadicPack's ctor moving off of `-> Self`. Fix these bugs, make the variadics.mojo more thorough, and make the IR pattern matching more "fail fast" for when the IR changes again next time. MODULAR_ORIG_COMMIT_REV_ID: b11da619445ce8330e7fef7b98e20860421ce1e6 --- stdlib/src/builtin/builtin_list.mojo | 4 ++-- stdlib/src/builtin/tuple.mojo | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 6888e978de..fdb0b2b7ab 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -37,13 +37,13 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): """The underlying storage for the list.""" @always_inline("nodebug") - fn __init__(inout self, *args: *Ts): + fn __init__(inout self, owned *args: *Ts): """Construct the list literal from the given values. Args: args: The init values. """ - self.storage = Tuple(storage=args) + self.storage = Tuple(storage=args^) @always_inline("nodebug") fn __len__(self) -> Int: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 5a8bd260da..41b935d5d9 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -50,19 +50,19 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): """The underlying storage for the tuple.""" @always_inline("nodebug") - fn __init__(inout self, *args: *element_types): + fn __init__(inout self, owned *args: *element_types): """Construct the tuple. Args: args: Initial values. """ - self = Self(storage=args) + self = Self(storage=args^) @always_inline("nodebug") fn __init__( inout self, *, - storage: VariadicPack[_, _, CollectionElement, element_types], + owned storage: VariadicPack[_, _, CollectionElement, element_types], ): """Construct the tuple from a low-level internal representation. @@ -76,15 +76,17 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): - # TODO: We could be fancier and take the values out of an owned - # pack. For now just keep everything simple and copy the element. - initialize_pointee_copy( - UnsafePointer(self[idx]), - storage[idx], + move_pointee( + dst=UnsafePointer(self[idx]), + src=UnsafePointer(storage[idx]), ) + # Move each element into the tuple storage. unroll[initialize_elt, Self.__len__()]() + # Mark the elements as already destroyed. + storage._is_owned = False + fn __del__(owned self): """Destructor that destroys all of the elements.""" From 400f9af239a20b73501a8d48d4d3f2dd0ae46803 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Tue, 14 May 2024 13:32:06 -0700 Subject: [PATCH 0540/2019] [Docs] Fix a broken description of undeclared variables Also, some general corrections and rewrites. (And one very trivial typo fix in another file.) MODULAR_ORIG_COMMIT_REV_ID: 70f8469144f5e17c9836fbd748a8b440707d53df --- docs/manual/parameters/index.ipynb | 8 +- docs/manual/variables.ipynb | 234 ++++++++++++++++++++--------- 2 files changed, 168 insertions(+), 74 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 48073e73c9..e5cabf5cf5 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -544,7 +544,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -576,7 +576,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -584,11 +584,11 @@ "struct Bar[v: Int]:\n", " pass\n", "\n", - "fn foo[a: Int = 3, msg: StringLiteral = \"woof\"](bar: Bar[a]):\n", + "fn speak[a: Int = 3, msg: StringLiteral = \"woof\"](bar: Bar[a]):\n", " print(msg, a)\n", "\n", "fn use_inferred():\n", - " foo(Bar[9]()) # prints 'woof 9'" + " speak(Bar[9]()) # prints 'woof 9'" ] }, { diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 391204ebd6..7115d3a5f5 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -20,17 +20,74 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "A variable is a name that holds a value or object. All variables in Mojo are mutable—their value can be changed. (If you want to define a constant value that can't change at runtime, see the \n", + "A variable is a name that holds a value or object. All variables in Mojo are \n", + "mutable—their value can be changed. (If you want to define a constant value that\n", + "can't change at runtime, see the \n", "[`alias` keyword](/mojo/manual/parameters/index.html#alias-named-parameter-expressions).)\n", "\n", + "Mojo has two kinds of variables:\n", + "\n", + "- Declared variables are created with the `var` keyword, and may include\n", + " [type annotations](#type-annotation).\n", + "\n", + " ```mojo\n", + " var a = 5\n", + " var b: Float64 = 3.14\n", + " ```\n", + " \n", + "- Undeclared variables are created with an assignment statement:\n", + "\n", + " ```mojo\n", + " a = 5\n", + " b = 3.14\n", + " ```\n", + "\n", + "Both types of variables are strongly-typed: the variable receives a type when\n", + "it's created, and the type never changes. You can't assign a variable a value of\n", + "a different type:\n", + "\n", + "```mojo\n", + "count = 8 # count is type Int\n", + "count = \"Nine?\" # Error: can't implicitly convert 'StringLiteral' to 'Int'\n", + "```\n", + "\n", + "Some types support [_implicit conversions_](#implicit-type-conversion) from\n", + "other types. For example, an integer value can implicitly convert to a\n", + "floating-point value:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "99.0\n" + ] + } + ], + "source": [ + "var temperature: Float64 = 99\n", + "print(temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, the `temperature` variable is explicitly typed as `Float64`,\n", + "but assigned an integer value, so the value is implicitly converted to a \n", + "`Float64`. \n", + "\n", ":::note\n", "\n", "Mojo formerly supported the `let` keyword for declaring immutable variables.\n", "This has been removed to simplify the language, and for other reasons\n", "[discussed\n", "elsewhere](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md).\n", - "To simplify the migration of older code, `let` declarations are currently\n", - "supported, but function the same as `var` declarations.\n", "\n", ":::" ] @@ -47,19 +104,29 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "name = \"Sam\"" + "name = str(\"Sam\")\n", + "user_id = 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "A variable declared without `var` follows s.\n", - "\n", + "Undeclared variables are strongly typed: they take the type from the first value\n", + "assigned to them. For example, the `user_id` variable above is type `Int`, while\n", + "the `name` variable is type `String`. You can't assign a string to `user_id` or an\n", + "integer to `name`. \n", + "\n", + "Undeclared variables are scoped at the function level. You create an undeclared\n", + "variable the first time you assign a value to a given name inside a function. \n", + "Any subsequent references to that name inside the function refer to the same\n", + "variable. For more information, see [Variable scopes](#variable-scopes), which\n", + "describes how variable scoping differs between declared and undeclared\n", + "variables.\n", "\n", ":::note\n", "\n", @@ -84,7 +151,7 @@ "metadata": {}, "outputs": [], "source": [ - "var name = \"Sam\"\n", + "var name = str(\"Sam\")\n", "var user_id: Int" ] }, @@ -93,24 +160,38 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `name` variable is initialized to the string \"Sam\". The `user_id` variable is uninitialized, but it has a declared type, `Int` for an integer value. All\n", + "The `name` variable is initialized to the string \"Sam\". The `user_id` variable \n", + "is uninitialized, but it has a declared type, `Int` for an integer value. All\n", "declared values are typed—either explicitly with a \n", - "[type annotation](#type-annotations) or implicitly when they're initialized with a value.\n", + "[type annotation](#type-annotations) or implicitly when they're initialized with\n", + "a value.\n", "\n", - "Since declared variables are strongly typed, you can't assign a variable a\n", + "Since variables are strongly typed, you can't assign a variable a\n", "value of a different type, unless those types can be \n", - "[implicitly converted](#implicit-type-conversion). For example, this code will not compile:\n", + "[implicitly converted](#implicit-type-conversion). For example, this code will\n", + "not compile:\n", "\n", "```mojo\n", "var user_id: Int = \"Sam\"\n", "```\n", "\n", - "In addition to typing, declared variables also follow \n", - "[lexical scoping](#variable-scopes), unlike undeclared variables.\n", + "There are several main differences between declared variables and undeclared\n", + "variables:\n", + "\n", + "- A declared variable can be declared without initializing it:\n", + "\n", + " ```mojo\n", + " var value: Float64\n", + " ```\n", "\n", - "Finally, using `var` helps prevent runtime errors caused by typos. For example,\n", + "- Declared variables follow [lexical scoping](#variable-scopes), unlike \n", + " undeclared variables.\n", + "\n", + "- Declared variables can be used in both `def` and `fn` functions.\n", + "\n", + "Using `var` can help prevent runtime errors caused by typos. For example,\n", "if you misspell the name of an [undeclared variable](#undeclared-variables),\n", - "Mojo simply instantiates a new variable using the misspelled name. But when all\n", + "Mojo simply creates a new variable using the misspelled name. But when all\n", "mutable variables must be first declared with `var` (which is the case inside\n", "an `fn` function), then misspellings such as the following are caught by the\n", "compiler:\n", @@ -125,7 +206,6 @@ "realized only when used inside an `fn` function, where the Mojo compiler will\n", "flag undeclared variables (such as the above `nane`) as unknown declarations.\n", "\n", - "\n", ":::note\n", "\n", "When using Mojo in a REPL environment, top-level variables (variables\n", @@ -146,30 +226,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Although Mojo supports dynamic variable types (it can infer a value type at\n", - "runtime), it also supports static type annotations on variables. This enables\n", - "strong compile-time type checking for variables, which can make your code more\n", - "predictable, manageable, and secure (especially when combined with type\n", - "checking in [`fn` functions](/mojo/manual/functions.html#fn-functions)).\n", + "Although Mojo can infer a variable type from from the first value assigned to a \n", + "variable, it also supports static type annotations on variables. Type \n", + "annotations provide a more explicit way of specifying the variable's type.\n", "\n", - "To specify the type for a variable, add a colon followed by the type name:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "var name: String = \"Sam\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This way, `name` can never be assigned a value that's not a string (or that\n", - "cannot be [implicitly converted](#implicit-type-conversion) to a string).\n", + "To specify the type for a variable, add a colon followed by the type name:\n", + "\n", + "```mojo\n", + "var name: String = get_name()\n", + "```\n", + "\n", + "This makes it clear that `name` is type `String`, without knowing what the \n", + "`get_name()` function returns. The `get_name()` function may return a `String`,\n", + "or a value that's implicitly convertible to a `String`.\n", "\n", ":::note\n", "\n", @@ -217,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -233,6 +302,19 @@ " return 3.14" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you try to pass an uninitialized variable to a function or use\n", + "it on the right-hand side of an assignment statement, compilation fails.\n", + "\n", + "```mojo\n", + "var z: Float32\n", + "var y = z # Error: use of uninitialized value 'z'\n", + "```" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -257,17 +339,25 @@ "metadata": {}, "source": [ "Some types include built-in type conversion (type casting) from one type into\n", - "its own type. For example, if you assign a number to a `String`, it creates the\n", - "string `\"1\"` instead of a compiler error:" + "its own type. For example, if you assign an integer to a variable that has a \n", + "floating-point type, it converts the value instead of giving a compiler error:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], "source": [ - "var number: String = 1" + "var number: Float64 = 1" ] }, { @@ -276,14 +366,18 @@ "source": [ "As shown above, value assignment can be converted into a constructor call if the \n", "target type has a constructor that takes a single argument that matches the\n", - "value being assigned. So, this code uses the \n", - "[`String`](/mojo/stdlib/builtin/string/String) constructor that takes an\n", - "integer: `__init__(inout self, num: Int)`.\n", + "value being assigned. So, this code uses the `Float64` constructor that takes an\n", + "integer: `__init__(inout self, value: Int)`.\n", + "\n", + "In general, implicit conversions should only be supported where the conversion\n", + "is lossless.\n", "\n", "Implicit conversion follows the logic of [overloaded\n", - "functions](/mojo/manual/functions.html#overloaded-functions), because\n", - "that's exactly what's happening here: assigning a number to a `String` variable\n", - "is exactly the same as this:" + "functions](/mojo/manual/functions.html#overloaded-functions). If the destination\n", + "type has a single-argument constructor that takes an argument of the source\n", + "type, it can be invoked for implicit conversion. \n", + "\n", + "So assigning an integer to a `Float64` variable is exactly the same as this:" ] }, { @@ -292,34 +386,34 @@ "metadata": {}, "outputs": [], "source": [ - "var number = String(1)" + "var number = Float64(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Thus, if you call a function that requires an argument of a certain type (such\n", - "as `String`), you can pass in any value as long as that value type can\n", + "Similarly, if you call a function that requires an argument of a certain type \n", + "(such as `Float64`), you can pass in any value as long as that value type can\n", "implicitly convert to the required type (using one of the type's overloaded\n", "constructors).\n", "\n", - "For example, you can pass an `Int` to a function that expects a `String`,\n", - "because `String` includes a constructor that takes an `Int`:" + "For example, you can pass an `Int` to a function that expects a `Float64`,\n", + "because `Float64` includes a constructor that takes an `Int`:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "fn take_string(version: String):\n", - " print(version)\n", + "fn take_float(value: Float64):\n", + " print(value)\n", "\n", "fn pass_integer():\n", - " var version: Int = 1\n", - " take_string(version)" + " var value: Int = 1\n", + " take_float(value)" ] }, { @@ -349,29 +443,29 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "num: 10\n", - "num: 20\n", - "num: 10\n", + "num: 1\n", + "num: 2\n", + "num: 1\n", "dig: 2\n" ] } ], "source": [ "def lexical_scopes():\n", - " var num = 10\n", + " var num = 1\n", " var dig = 1\n", - " if True:\n", + " if num == 1:\n", " print(\"num:\", num) # Reads the outer-scope \"num\"\n", - " var num = 20 # Creates new inner-scope \"num\"\n", + " var num = 2 # Creates new inner-scope \"num\"\n", " print(\"num:\", num) # Reads the inner-scope \"num\"\n", - " dig = 2 # Edits the outer-scope \"dig\"\n", + " dig = 2 # Updates the outer-scope \"dig\"\n", " print(\"num:\", num) # Reads the outer-scope \"num\"\n", " print(\"dig:\", dig) # Reads the outer-scope \"dig\"\n", "\n", From c32ba4e334073c54496a77be664d2fbfbb32dd0a Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Tue, 14 May 2024 16:39:32 -0400 Subject: [PATCH 0541/2019] [stdlib] Added `Variant.replace` (#39900) [stdlib] Added `Variant.replace` BEGING_PUBLIC [stdlib] Added `Variant.replace` The `replace` method serve the same purpose as `take` before, but enforces placement of a new element immediately. This also fix the problem where `Optional.take` does not actually take out the value. Test cases were updated accordingly END_PUBLIC --------- Co-authored-by: Connor Gray MODULAR_ORIG_COMMIT_REV_ID: 0a35284f47a9a8fcf58c2a25e3288725ac426e37 --- stdlib/src/collections/optional.mojo | 4 +- stdlib/src/utils/variant.mojo | 74 +++++++++++++++++++--- stdlib/test/collections/test_optional.mojo | 13 ++++ stdlib/test/utils/test_variant.mojo | 10 ++- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index cf20159fdc..479a7282c9 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -152,7 +152,6 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): The caller takes ownership over the new value, which is moved out of the Optional, and the Optional is left in an empty state. - destroyed. This check to see if the optional contains a value. If you call this without first verifying the optional with __bool__() @@ -171,7 +170,6 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): The caller takes ownership over the new value, which is moved out of the Optional, and the Optional is left in an empty state. - destroyed. This check to see if the optional contains a value. If you call this without first verifying the optional with __bool__() @@ -182,7 +180,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): The contained data of the option as an owned T value. """ debug_assert(self.__bool__(), ".unsafe_take() on empty Optional") - return self._value.unsafe_take[T]() + return self._value.unsafe_replace[_NoneType, T](_NoneType()) fn or_else(self, default: T) -> T: """Return the underlying value contained in the Optional or a default value if the Optional's underlying value is not present. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 6595c7ddbf..0b653db9e1 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -225,20 +225,19 @@ struct Variant[*Ts: CollectionElement](CollectionElement): @always_inline fn take[T: CollectionElement](inout self) -> T: - """Take the current value of the variant as the provided type. + """Take the current value of the variant with the provided type. - The caller takes ownership of the underlying value. The variant - type is consumed without calling any deleters. + The caller takes ownership of the underlying value. This explicitly check that your value is of that type! If you haven't verified the type correctness at runtime, the program will abort! Parameters: - T: The type to take. + T: The type to take out. Returns: - The underlying data as an owned value. + The underlying data to be taken out as an owned value. """ if not self.isa[T](): abort("taking the wrong type!") @@ -247,10 +246,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): @always_inline fn unsafe_take[T: CollectionElement](inout self) -> T: - """Unsafely take the current value of the variant as the provided type. + """Unsafely take the current value of the variant with the provided type. - The caller takes ownership of the underlying value. The variant - type is consumed without calling any deleters. + The caller takes ownership of the underlying value. This doesn't explicitly check that your value is of that type! If you haven't verified the type correctness at runtime, you'll get @@ -258,16 +256,72 @@ struct Variant[*Ts: CollectionElement](CollectionElement): and garbage member data. Parameters: - T: The type to take. + T: The type to take out. Returns: - The underlying data as an owned value. + The underlying data to be taken out as an owned value. """ debug_assert(self.isa[T](), "taking wrong type") # don't call the variant's deleter later self._get_state()[] = Self._sentinel return move_from_pointee(self._get_ptr[T]()) + @always_inline + fn replace[ + Tin: CollectionElement, Tout: CollectionElement + ](inout self, value: Tin) -> Tout: + """Replace the current value of the variant with the provided type. + + The caller takes ownership of the underlying value. + + This explicitly check that your value is of that type! + If you haven't verified the type correctness at runtime, the program + will abort! + + Parameters: + Tin: The type to put in. + Tout: The type to take out. + + Args: + value: The value to put in. + + Returns: + The underlying data to be taken out as an owned value. + """ + if not self.isa[Tout](): + abort("taking out the wrong type!") + + return self.unsafe_replace[Tin, Tout](value) + + @always_inline + fn unsafe_replace[ + Tin: CollectionElement, Tout: CollectionElement + ](inout self, value: Tin) -> Tout: + """Unsafely replace the current value of the variant with the provided type. + + The caller takes ownership of the underlying value. + + This doesn't explicitly check that your value is of that type! + If you haven't verified the type correctness at runtime, you'll get + a type that _looks_ like your type, but has potentially unsafe + and garbage member data. + + Parameters: + Tin: The type to put in. + Tout: The type to take out. + + Args: + value: The value to put in. + + Returns: + The underlying data to be taken out as an owned value. + """ + debug_assert(self.isa[Tout](), "taking out the wrong type!") + + var x = self.unsafe_take[Tout]() + self.set[Tin](value) + return x^ + fn set[T: CollectionElement](inout self, owned value: T): """Set the variant value. diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 9cecda2424..37c78eb3b8 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -108,6 +108,18 @@ def test_optional_reg_isnot(): assert_false(a is not None) +def test_optional_take_mutates(): + var opt1 = Optional[Int](5) + + assert_true(opt1) + + var value: Int = opt1.take() + + assert_equal(value, 5) + # The optional should now be empty + assert_false(opt1) + + def main(): test_basic() test_optional_reg_basic() @@ -115,3 +127,4 @@ def main(): test_optional_isnot() test_optional_reg_is() test_optional_reg_isnot() + test_optional_take_mutates() diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 7b01e6758e..cf78f1edfc 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -158,6 +158,13 @@ def test_set_calls_deleter(): assert_no_poison() +def test_replace(): + var v1: Variant[Int, String] = 998 + var x = v1.replace[String, Int]("hello") + + assert_equal(x, 998) + + def test_take_doesnt_call_deleter(): alias TestDeleterVariant = Variant[ObservableDel, Poison] var deleted: Bool = False @@ -193,5 +200,6 @@ def main(): test_copy() test_move() test_del() - test_set_calls_deleter() test_take_doesnt_call_deleter() + test_set_calls_deleter() + test_replace() From 1e6f5d602ef8f7eff8c2f88a82df21015500f3cf Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 15 May 2024 05:48:20 +0800 Subject: [PATCH 0542/2019] [Stdlib] Fix the abs implementation (#39100) The abs function was evaluating both branches even if we can prove the value is positive. Switch the conditions to avoid that.y Co-authored-by: Abdul Dakkak MODULAR_ORIG_COMMIT_REV_ID: ee080ab8be7b8838d918734ee032fec34d12860b --- stdlib/src/builtin/int.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index e51ec10d30..af64739040 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -504,7 +504,7 @@ struct Int( Returns: The absolute value. """ - return self if self > 0 else -self + return -self if self < 0 else self @always_inline("nodebug") fn __ceil__(self) -> Self: From 39e5a5a2fe6c20608f07827349d17c10a9f0e554 Mon Sep 17 00:00:00 2001 From: Brian M Johnson <1DT21AI037@dsatm.edu.in> Date: Tue, 14 May 2024 17:25:26 -0500 Subject: [PATCH 0543/2019] [External] [docs] Add constraint documentation for various `reduce`-like functions in simd.mojo (#39872) Fixes #2237 for `reduce`-like functions in simd.mojo: - Adds documentation for constraint on value of `size_out` not exceeding width of input vector in `reduce_add`, `reduce_mul`, `reduce_max` and `reduce_min`. Signed-off-by: Brian-M-J Co-authored-by: Brian M Johnson <1DT21AI037@dsatm.edu.in> Closes modularml/mojo#2461 MODULAR_ORIG_COMMIT_REV_ID: b151847dcc2e89c9b87fec873941bf8b20a534c7 --- stdlib/src/builtin/simd.mojo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index dd0b30cdec..813740e27a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2015,6 +2015,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size_out: The width of the reduction. Constraints: + `size_out` must not exceed width of the vector. The element type of the vector must be integer or FP. Returns: @@ -2072,6 +2073,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size_out: The width of the reduction. Constraints: + `size_out` must not exceed width of the vector. The element type of the vector must be integer or FP. Returns: @@ -2128,6 +2130,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Parameters: size_out: The width of the reduction. + Constraints: + `size_out` must not exceed width of the vector. + Returns: The sum of all vector elements. @@ -2150,6 +2155,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( size_out: The width of the reduction. Constraints: + `size_out` must not exceed width of the vector. The element type of the vector must be integer or FP. Returns: From d0d87a7bd97eab86a4917f1d63c9823af5584965 Mon Sep 17 00:00:00 2001 From: Peyman Barazandeh Date: Tue, 14 May 2024 17:30:56 -0500 Subject: [PATCH 0544/2019] [External] [stdlib] Add unit tests for SIMD add, radd and iadd operations (#39931) Add unit tests for the `__add__()`, `__radd__()`, and `__iadd__()` methods. Co-authored-by: Peyman Barazandeh Closes modularml/mojo#2611 MODULAR_ORIG_COMMIT_REV_ID: b1616eedeb11b99a9e8f62df6f58cd708c587d78 --- stdlib/test/builtin/test_simd.mojo | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index c1ace27899..92c4f49eb0 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -146,6 +146,66 @@ def test_truthy(): test_dtype[DType.bfloat16]() +def test_add(): + alias I = SIMD[DType.int32, 4] + var i = I(-2, -4, 0, 1) + assert_equal(i.__add__(0), I(-2, -4, 0, 1)) + assert_equal(i.__add__(Int32(0)), I(-2, -4, 0, 1)) + assert_equal(i.__add__(2), I(0, -2, 2, 3)) + assert_equal(i.__add__(Int32(2)), I(0, -2, 2, 3)) + + var i1 = I(1, -4, -3, 2) + var i2 = I(2, 5, 3, 1) + assert_equal(i1.__add__(i2), I(3, 1, 0, 3)) + + alias F = SIMD[DType.float32, 8] + var f1 = F(1, -1, 1, -1, 1, -1, 1, -1) + var f2 = F(-1, 1, -1, 1, -1, 1, -1, 1) + assert_equal(f1.__add__(f2), F(0, 0, 0, 0, 0, 0, 0, 0)) + + +def test_radd(): + alias I = SIMD[DType.int32, 4] + var i = I(-2, -4, 0, 1) + assert_equal(i.__radd__(0), I(-2, -4, 0, 1)) + assert_equal(i.__radd__(Int32(0)), I(-2, -4, 0, 1)) + assert_equal(i.__radd__(2), I(0, -2, 2, 3)) + assert_equal(i.__radd__(Int32(2)), I(0, -2, 2, 3)) + + var i1 = I(1, -4, -3, 2) + var i2 = I(2, 5, 3, 1) + assert_equal(i1.__radd__(i2), I(3, 1, 0, 3)) + + alias F = SIMD[DType.float32, 8] + var f1 = F(1, -1, 1, -1, 1, -1, 1, -1) + var f2 = F(-1, 1, -1, 1, -1, 1, -1, 1) + assert_equal(f1.__radd__(f2), F(0, 0, 0, 0, 0, 0, 0, 0)) + + +def test_iadd(): + alias I = SIMD[DType.int32, 4] + var i = I(-2, -4, 0, 1) + i.__iadd__(0) + assert_equal(i, I(-2, -4, 0, 1)) + i.__iadd__(Int32(0)) + assert_equal(i, I(-2, -4, 0, 1)) + i.__iadd__(2) + assert_equal(i, I(0, -2, 2, 3)) + i.__iadd__(I(0, -2, 2, 3)) + assert_equal(i, I(0, -4, 4, 6)) + + var i1 = I(1, -4, -3, 2) + var i2 = I(2, 5, 3, 1) + i1.__iadd__(i2) + assert_equal(i1, I(3, 1, 0, 3)) + + alias F = SIMD[DType.float32, 8] + var f1 = F(1, -1, 1, -1, 1, -1, 1, -1) + var f2 = F(-1, 1, -1, 1, -1, 1, -1, 1) + f1.__iadd__(f2) + assert_equal(f1, F(0, 0, 0, 0, 0, 0, 0, 0)) + + def test_ceil(): assert_equal(Float32.__ceil__(Float32(1.5)), 2.0) assert_equal(Float32.__ceil__(Float32(-1.5)), -1.0) @@ -931,6 +991,9 @@ def main(): test_convert_simd_to_string() test_issue_20421() test_truthy() + test_add() + test_radd() + test_iadd() test_ceil() test_floor() test_trunc() From d0cf98a8a5ec36a56ddbf4796129c033d6875cfa Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 14 May 2024 20:02:42 -0400 Subject: [PATCH 0545/2019] [stdlib] Re-enable `FloatLiteral.__trunc__` tests These were accidentally disabled when the original patch landed. MODULAR_ORIG_COMMIT_REV_ID: 0debdbd812763b50aefedc3aed34b51593fdd8c7 --- stdlib/test/builtin/test_float_literal.mojo | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index d0a8406491..7d146c92d3 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -55,18 +55,17 @@ def test_floor(): def test_trunc(): - pass - # assert_equal(FloatLiteral.__trunc__(1.5), 1.0) - # assert_equal(FloatLiteral.__trunc__(1.6), 1.0) - # assert_equal(FloatLiteral.__trunc__(-1.5), -1.0) - # assert_equal(FloatLiteral.__trunc__(-3.6), -3.0) - # assert_equal(FloatLiteral.__trunc__(3.0), 3.0) - # assert_equal(FloatLiteral.__trunc__(0.0), 0.0) - - # assert_true(FloatLiteral.__trunc__(nan).is_nan()) - # assert_true(FloatLiteral.__trunc__(neg_zero).is_neg_zero()) - # assert_equal(FloatLiteral.__trunc__(inf), inf) - # assert_equal(FloatLiteral.__trunc__(neg_inf), neg_inf) + assert_equal(FloatLiteral.__trunc__(1.5), 1.0) + assert_equal(FloatLiteral.__trunc__(1.6), 1.0) + assert_equal(FloatLiteral.__trunc__(-1.5), -1.0) + assert_equal(FloatLiteral.__trunc__(-3.6), -3.0) + assert_equal(FloatLiteral.__trunc__(3.0), 3.0) + assert_equal(FloatLiteral.__trunc__(0.0), 0.0) + + assert_true(FloatLiteral.__trunc__(nan).is_nan()) + assert_true(FloatLiteral.__trunc__(neg_zero).is_neg_zero()) + assert_equal(FloatLiteral.__trunc__(inf), inf) + assert_equal(FloatLiteral.__trunc__(neg_inf), neg_inf) fn round10(x: Float64) -> Float64: From 7b594435e450be7247f72d1f070e7a9dc9fd0c21 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 14 May 2024 19:05:35 -0500 Subject: [PATCH 0546/2019] [stdlib] feature: Add initial string byte slicing * Add String.as_bytes_slice() -> Span[Int8, _, _] * Add StringLiteral.as_bytes_slice() -> Span[Int8, 0, ImmStaticLifetime] * Add new ImmStaticLifetime and MutStaticLifetime helpers * Add _byte_length() methods to String and StringLiteral * Change Span.__getitem__() to return a Reference, making the doc comment accurate * Add Span.unsafe_ptr() * Fix missing declaration of utils/span in CMakeLists.txt MODULAR_ORIG_COMMIT_REV_ID: e7c7f512b0d968f1cbb1e2098f824b621046659a --- docs/changelog.md | 8 ++ stdlib/src/builtin/string.mojo | 36 ++++++- stdlib/src/builtin/string_literal.mojo | 29 ++++++ stdlib/src/builtin/type_aliases.mojo | 10 +- stdlib/src/memory/unsafe.mojo | 2 +- stdlib/src/memory/unsafe_pointer.mojo | 2 +- stdlib/src/utils/span.mojo | 32 +++++- stdlib/test/utils/test_span.mojo | 74 +++++++------- stdlib/test/utils/test_string_slice.mojo | 118 +++++++++++++++++++++++ 9 files changed, 267 insertions(+), 44 deletions(-) create mode 100644 stdlib/test/utils/test_string_slice.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 2faf304d63..9120ac88fc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -135,6 +135,14 @@ what we publish. requirement that the decl has documentation (e.g. when used with --diagnose-missing-doc-strings). +- Added a new `Span` type for taking slices of contiguous collections. + ([PR #2595](https://github.com/modularml/mojo/pull/2595) by [lsh](https://github.com/lsh)) + +- Added new `as_bytes_slice()` methods to `String` and `StringLiteral`, which + returns a `Span` of the bytes owned by the string. + +- Add new `ImmStaticLifetime` and `MutStaticLifetime` helpers + - `Dict` now implements `get(key)` and `get(key, default)` functions. ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 483c984c76..c2b4462b85 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -20,7 +20,7 @@ from sys import llvm_intrinsic, bitwidthof from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy -from utils import StringRef, StaticIntTuple +from utils import StringRef, StaticIntTuple, Span from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf @@ -1195,6 +1195,40 @@ struct String( return copy + fn as_bytes_slice( + self: Reference[Self, _, _] + ) -> Span[Int8, self.is_mutable, self.lifetime]: + """ + Returns a contiguous slice of the bytes owned by this string. + + This does not include the trailing null terminator. + + Returns: + A contiguous slice pointing to the bytes owned by this string. + """ + + return Span[Int8, self.is_mutable, self.lifetime]( + unsafe_ptr=self[]._buffer.unsafe_ptr(), + # Does NOT include the NUL terminator. + len=self[]._byte_length(), + ) + + fn _byte_length(self) -> Int: + """Get the string length in bytes. + + This does not include the trailing null terminator in the count. + + Returns: + The length of this StringLiteral in bytes, excluding null terminator. + """ + + var buffer_len = len(self._buffer) + + if buffer_len > 0: + return buffer_len - 1 + else: + return buffer_len + fn _steal_ptr(inout self) -> DTypePointer[DType.int8]: """Transfer ownership of pointer to the underlying memory. The caller is responsible for freeing up the memory. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 8452c31939..7add0faf4c 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -67,6 +67,18 @@ struct StringLiteral( Returns: The length of this StringLiteral. """ + # TODO(MSTDL-160): + # Properly count Unicode codepoints instead of returning this length + # in bytes. + return self._byte_length() + + @always_inline + fn _byte_length(self) -> Int: + """Get the string length in bytes. + + Returns: + The length of this StringLiteral in bytes. + """ return __mlir_op.`pop.string.size`(self.value) @always_inline("nodebug") @@ -224,6 +236,23 @@ struct StringLiteral( """ return self.__str__().__repr__() + fn as_bytes_slice( + self: Reference[Self, _, _] + ) -> Span[Int8, __mlir_attr.`0: i1`, ImmStaticLifetime]: + """ + Returns a contiguous slice of the bytes owned by this string. + + Returns: + A contiguous slice pointing to the bytes owned by this string. + """ + + var ptr = rebind[UnsafePointer[Int8]](self[].unsafe_ptr()) + + return Span[Int8, __mlir_attr.`0: i1`, ImmStaticLifetime]( + unsafe_ptr=ptr, + len=self[]._byte_length(), + ) + fn format_to(self, inout writer: Formatter): """ Formats this string literal to the provided formatter. diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 8fd6cfb215..64920f4e50 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -22,10 +22,16 @@ alias NoneType = __mlir_type.`!kgen.none` """Represents the absence of a value.""" alias ImmLifetime = __mlir_type.`!lit.lifetime<0>` -"""Immutable lifetime reference.""" +"""Immutable lifetime reference type.""" alias MutLifetime = __mlir_type.`!lit.lifetime<1>` -"""Mutable lifetime reference.""" +"""Mutable lifetime reference type.""" + +alias ImmStaticLifetime = __mlir_attr.`#lit.lifetime<0>: !lit.lifetime<0>` +"""The immutable lifetime that lasts for the entire duration of program execution.""" + +alias MutStaticLifetime = __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` +"""The mutable lifetime that lasts for the entire duration of program execution.""" # Helper to build !lit.lifetime type. diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index c41683196a..163a40d667 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -182,7 +182,7 @@ struct LegacyPointer[ alias _ref_type = Reference[ type, __mlir_attr.`1: i1`, - __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, + MutStaticLifetime, address_space, ] diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 0ae95b0fa0..4ec0b36098 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -51,7 +51,7 @@ struct UnsafePointer[ alias _ref_type = Reference[ T, __mlir_attr.`1: i1`, - __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, + MutStaticLifetime, address_space, ] diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 18a5adbc96..389db6c0fe 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -84,6 +84,10 @@ struct Span[ var _data: UnsafePointer[T] var _len: Int + # ===------------------------------------------------------------------===# + # Initializers + # ===------------------------------------------------------------------===# + @always_inline fn __init__(inout self, *, unsafe_ptr: UnsafePointer[T], len: Int): """Unsafe construction from a pointer and length. @@ -120,6 +124,10 @@ struct Span[ self._data = UnsafePointer(array).bitcast[T]() self._len = size + # ===------------------------------------------------------------------===# + # Trait impls + # ===------------------------------------------------------------------===# + @always_inline fn __len__(self) -> Int: """Returns the length of the span. This is a known constant value. @@ -129,6 +137,10 @@ struct Span[ """ return self._len + # ===------------------------------------------------------------------===# + # Operator dunders + # ===------------------------------------------------------------------===# + @always_inline fn _refitem__[ intable: Intable @@ -163,7 +175,9 @@ struct Span[ return adjusted_span @always_inline - fn __getitem__[IntableType: Intable](self, index: IntableType) -> T: + fn __getitem__[ + IntableType: Intable + ](self, index: IntableType) -> Reference[T, is_mutable, lifetime]: """Get a `Reference` to the element at the given index. Parameters: @@ -176,7 +190,7 @@ struct Span[ A reference to the item at the given index. """ # note that self._refitem__ is already bounds checking - return self._refitem__(index)[] + return self._refitem__(index) @always_inline fn __setitem__[ @@ -228,3 +242,17 @@ struct Span[ An iterator over the elements of the span. """ return _SpanIter(0, self) + + # ===------------------------------------------------------------------===# + # Methods + # ===------------------------------------------------------------------===# + + fn unsafe_ptr(self) -> UnsafePointer[T]: + """ + Gets a pointer to the first element of this slice. + + Returns: + A pointer pointing at the first element of this slice. + """ + + return self._data diff --git a/stdlib/test/utils/test_span.mojo b/stdlib/test/utils/test_span.mojo index af5d8afd89..f0b180a865 100644 --- a/stdlib/test/utils/test_span.mojo +++ b/stdlib/test/utils/test_span.mojo @@ -22,22 +22,22 @@ def test_span_list_int(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i]) + assert_equal(l[i], s[i][]) # subslice var s2 = s[2:] - assert_equal(s2[0], l[2]) - assert_equal(s2[1], l[3]) - assert_equal(s2[2], l[4]) - assert_equal(s2[3], l[5]) - assert_equal(s[-1], l[-1]) + assert_equal(s2[0][], l[2]) + assert_equal(s2[1][], l[3]) + assert_equal(s2[2][], l[4]) + assert_equal(s2[3][], l[5]) + assert_equal(s[-1][], l[-1]) # Test mutation - s[0] = 9 - assert_equal(s[0], 9) + s[0][] = 9 + assert_equal(s[0][], 9) assert_equal(l[0], 9) - s[-1] = 0 - assert_equal(s[-1], 0) + s[-1][] = 0 + assert_equal(s[-1][], 0) assert_equal(l[-1], 0) @@ -46,21 +46,21 @@ def test_span_list_str(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i]) + assert_equal(l[i], s[i][]) # subslice var s2 = s[2:] - assert_equal(s2[0], l[2]) - assert_equal(s2[1], l[3]) - assert_equal(s2[2], l[4]) - assert_equal(s2[3], l[5]) + assert_equal(s2[0][], l[2]) + assert_equal(s2[1][], l[3]) + assert_equal(s2[2][], l[4]) + assert_equal(s2[3][], l[5]) # Test mutation - s[0] = "h" - assert_equal(s[0], "h") + s[0][] = "h" + assert_equal(s[0][], "h") assert_equal(l[0], "h") - s[-1] = "i" - assert_equal(s[-1], "i") + s[-1][] = "i" + assert_equal(s[-1][], "i") assert_equal(l[-1], "i") @@ -69,21 +69,21 @@ def test_span_array_int(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i]) + assert_equal(l[i], s[i][]) # subslice var s2 = s[2:] - assert_equal(s2[0], l[2]) - assert_equal(s2[1], l[3]) - assert_equal(s2[2], l[4]) - assert_equal(s2[3], l[5]) + assert_equal(s2[0][], l[2]) + assert_equal(s2[1][], l[3]) + assert_equal(s2[2][], l[4]) + assert_equal(s2[3][], l[5]) # Test mutation - s[0] = 9 - assert_equal(s[0], 9) + s[0][] = 9 + assert_equal(s[0][], 9) assert_equal(l[0], 9) - s[-1] = 0 - assert_equal(s[-1], 0) + s[-1][] = 0 + assert_equal(s[-1][], 0) assert_equal(l[-1], 0) @@ -92,21 +92,21 @@ def test_span_array_str(): var s = Span(l) assert_equal(len(s), len(l)) for i in range(len(s)): - assert_equal(l[i], s[i]) + assert_equal(l[i], s[i][]) # subslice var s2 = s[2:] - assert_equal(s2[0], l[2]) - assert_equal(s2[1], l[3]) - assert_equal(s2[2], l[4]) - assert_equal(s2[3], l[5]) + assert_equal(s2[0][], l[2]) + assert_equal(s2[1][], l[3]) + assert_equal(s2[2][], l[4]) + assert_equal(s2[3][], l[5]) # Test mutation - s[0] = "h" - assert_equal(s[0], "h") + s[0][] = "h" + assert_equal(s[0][], "h") assert_equal(l[0], "h") - s[-1] = "i" - assert_equal(s[-1], "i") + s[-1][] = "i" + assert_equal(s[-1][], "i") assert_equal(l[-1], "i") diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo new file mode 100644 index 0000000000..d24b453cf1 --- /dev/null +++ b/stdlib/test/utils/test_string_slice.mojo @@ -0,0 +1,118 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal + +from utils import Span + + +fn test_string_literal_byte_slice() raises: + var string: StringLiteral = "Hello" + var slice = string.as_bytes_slice() + + assert_equal(len(slice), 5) + assert_equal(slice[0][], ord("H")) + assert_equal(slice[1][], ord("e")) + assert_equal(slice[2][], ord("l")) + assert_equal(slice[3][], ord("l")) + assert_equal(slice[4][], ord("o")) + + +fn test_string_byte_slice() raises: + var string = String("Hello") + var str_slice = string.as_bytes_slice() + + assert_equal(len(str_slice), 5) + assert_equal(str_slice[0][], ord("H")) + assert_equal(str_slice[1][], ord("e")) + assert_equal(str_slice[2][], ord("l")) + assert_equal(str_slice[3][], ord("l")) + assert_equal(str_slice[4][], ord("o")) + + # ---------------------------------- + # Test subslicing + # ---------------------------------- + + # Slice the whole thing + var sub1 = str_slice[:5] + assert_equal(len(sub1), 5) + assert_equal(sub1[0][], ord("H")) + assert_equal(sub1[1][], ord("e")) + assert_equal(sub1[2][], ord("l")) + assert_equal(sub1[3][], ord("l")) + assert_equal(sub1[4][], ord("o")) + + # Slice the end + var sub2 = str_slice[2:5] + assert_equal(len(sub2), 3) + assert_equal(sub2[0][], ord("l")) + assert_equal(sub2[1][], ord("l")) + assert_equal(sub2[2][], ord("o")) + + # Slice the first element + var sub3 = str_slice[0:1] + assert_equal(len(sub3), 1) + assert_equal(sub3[0][], ord("H")) + + # + # Test mutation through slice + # + + sub1[0][] = ord("J") + assert_equal(string, "Jello") + + sub2[2][] = ord("y") + assert_equal(string, "Jelly") + + # ---------------------------------- + # Test empty subslicing + # ---------------------------------- + + var sub4 = str_slice[0:0] + assert_equal(len(sub4), 0) + + var sub5 = str_slice[2:2] + assert_equal(len(sub5), 0) + + # Empty slices still have a pointer value + assert_equal(int(sub5.unsafe_ptr()) - int(sub4.unsafe_ptr()), 2) + + # ---------------------------------- + # Test invalid slicing + # ---------------------------------- + + # TODO: Improve error reporting for invalid slice bounds. + + # assert_equal( + # # str_slice[3:6] + # str_slice._try_slice(slice(3, 6)).unwrap[String](), + # String("Slice end is out of bounds"), + # ) + + # assert_equal( + # # str_slice[5:6] + # str_slice._try_slice(slice(5, 6)).unwrap[String](), + # String("Slice start is out of bounds"), + # ) + + # assert_equal( + # # str_slice[5:5] + # str_slice._try_slice(slice(5, 5)).unwrap[String](), + # String("Slice start is out of bounds"), + # ) + + +fn main() raises: + test_string_literal_byte_slice() + test_string_byte_slice() From feac9be001ac8ccdc43686552a2ce9ff9a51e29d Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 14 May 2024 21:55:39 -0500 Subject: [PATCH 0547/2019] [stdlib] Change `Reference.is_mutable` to `Bool` (from `i1`) With the recent change to `Bool` to use an `i1` as its representation, many of the errors holding up moving `Reference.is_mutable` to `Bool` were resolved. Co-authored-by: Chris Lattner MODULAR_ORIG_COMMIT_REV_ID: 980a32f9b676905284a58b454c3f5e25a7280b21 --- stdlib/src/builtin/builtin_list.mojo | 54 +++++++++++++++++----------- stdlib/src/builtin/reversed.mojo | 8 ++--- stdlib/src/builtin/type_aliases.mojo | 5 ++- stdlib/src/collections/dict.mojo | 14 ++++---- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/reference.mojo | 2 +- stdlib/src/utils/span.mojo | 4 +-- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index fdb0b2b7ab..5fa1fdb2c6 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -162,7 +162,7 @@ struct VariadicList[type: AnyRegType](Sized): @value struct _VariadicListMemIter[ elt_type: AnyType, - elt_is_mutable: __mlir_type.i1, + elt_is_mutable: Bool, elt_lifetime: AnyLifetime[elt_is_mutable].type, list_lifetime: ImmLifetime, ]: @@ -176,13 +176,11 @@ struct _VariadicListMemIter[ """ alias variadic_list_type = VariadicListMem[ - elt_type, elt_is_mutable, elt_lifetime + elt_type, elt_is_mutable.value, elt_lifetime ] var index: Int - var src: Reference[ - Self.variadic_list_type, __mlir_attr.`0: i1`, list_lifetime - ] + var src: Reference[Self.variadic_list_type, False, list_lifetime] fn __next__(inout self) -> Self.variadic_list_type.reference_type: self.index += 1 @@ -197,25 +195,31 @@ struct _VariadicListMemIter[ # Helper to compute the union of two lifetimes: # TODO: parametric aliases would be nice. struct _lit_lifetime_union[ - is_mutable: __mlir_type.i1, + is_mutable: Bool, a: AnyLifetime[is_mutable].type, b: AnyLifetime[is_mutable].type, ]: alias result = __mlir_attr[ - `#lit.lifetime.union<`, a, `,`, b, `> : !lit.lifetime<`, is_mutable, `>` + `#lit.lifetime.union<`, + a, + `,`, + b, + `> : !lit.lifetime<`, + is_mutable.value, + `>`, ] struct _lit_mut_cast[ - is_mutable: __mlir_type.i1, + is_mutable: Bool, operand: AnyLifetime[is_mutable].type, - result_mutable: __mlir_type.i1, + result_mutable: Bool, ]: alias result = __mlir_attr[ `#lit.lifetime.mutcast<`, operand, `> : !lit.lifetime<`, - +result_mutable, + +result_mutable.value, `>`, ] @@ -223,7 +227,7 @@ struct _lit_mut_cast[ struct VariadicListMem[ element_type: AnyType, elt_is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[elt_is_mutable].type, + lifetime: __mlir_type[`!lit.lifetime<`, elt_is_mutable, `>`], ](Sized): """A utility class to access variadic function arguments of memory-only types that may have ownership. It exposes references to the elements in a @@ -236,7 +240,9 @@ struct VariadicListMem[ lifetime: The reference lifetime of the underlying elements. """ - alias reference_type = Reference[element_type, elt_is_mutable, lifetime] + alias reference_type = Reference[ + element_type, Bool {value: elt_is_mutable}, lifetime + ] alias _mlir_ref_type = Self.reference_type._mlir_type alias _mlir_type = __mlir_type[ `!kgen.variadic<`, Self._mlir_ref_type, `, borrow_in_mem>` @@ -363,15 +369,15 @@ struct VariadicListMem[ self, index: Int ) -> Reference[ element_type, - elt_is_mutable, + Bool {value: elt_is_mutable}, _lit_lifetime_union[ - elt_is_mutable, + Bool {value: elt_is_mutable}, lifetime, # cast mutability of self to match the mutability of the element, # since that is what we want to use in the ultimate reference and # the union overall doesn't matter. _lit_mut_cast[ - __mlir_attr.`0: i1`, __lifetime_of(self), elt_is_mutable + False, __lifetime_of(self), Bool {value: elt_is_mutable} ].result, ].result, ]: @@ -389,7 +395,10 @@ struct VariadicListMem[ fn __iter__( self, ) -> _VariadicListMemIter[ - element_type, elt_is_mutable, lifetime, __lifetime_of(self) + element_type, + Bool {value: elt_is_mutable}, + lifetime, + __lifetime_of(self), ]: """Iterate over the list. @@ -397,7 +406,10 @@ struct VariadicListMem[ An iterator to the start of the list. """ return _VariadicListMemIter[ - element_type, elt_is_mutable, lifetime, __lifetime_of(self) + element_type, + Bool {value: elt_is_mutable}, + lifetime, + __lifetime_of(self), ](0, self) @@ -412,7 +424,7 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @value struct _LITRefPackHelper[ is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[is_mutable].type, + lifetime: AnyLifetime[Bool {value: is_mutable}].type, address_space: __mlir_type.index, element_trait: _AnyTypeMetaType, *element_types: element_trait, @@ -480,7 +492,7 @@ struct _LITRefPackHelper[ @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[elt_is_mutable].type, + lifetime: __mlir_type[`!lit.lifetime<`, elt_is_mutable, `>`], element_trait: _AnyTypeMetaType, *element_types: element_trait, ](Sized): @@ -574,7 +586,7 @@ struct VariadicPack[ index: Int ](self) -> Reference[ element_types[index.value], - Self.elt_is_mutable, + Bool {value: Self.elt_is_mutable}, Self.lifetime, ]: """Return a reference to an element of the pack. @@ -595,7 +607,7 @@ struct VariadicPack[ # element_types[index] expression is erased to AnyType for Reference. alias result_ref = Reference[ element_types[index.value], - Self.elt_is_mutable, + Bool {value: Self.elt_is_mutable}, Self.lifetime, ] return rebind[result_ref._mlir_type](ref_elt) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index f96d6239ac..25986069ef 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -125,11 +125,11 @@ fn reversed[ fn reversed[ - mutability: __mlir_type.`i1`, + mutability: Bool, self_life: AnyLifetime[mutability].type, K: KeyElement, V: CollectionElement, - dict_mutability: __mlir_type.`i1`, + dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, ]( value: Reference[ @@ -160,11 +160,11 @@ fn reversed[ fn reversed[ - mutability: __mlir_type.`i1`, + mutability: Bool, self_life: AnyLifetime[mutability].type, K: KeyElement, V: CollectionElement, - dict_mutability: __mlir_type.`i1`, + dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, ]( value: Reference[ diff --git a/stdlib/src/builtin/type_aliases.mojo b/stdlib/src/builtin/type_aliases.mojo index 64920f4e50..740b104a4a 100644 --- a/stdlib/src/builtin/type_aliases.mojo +++ b/stdlib/src/builtin/type_aliases.mojo @@ -36,8 +36,7 @@ alias MutStaticLifetime = __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` # Helper to build !lit.lifetime type. # TODO: Should be a parametric alias. -# TODO: Should take a Bool, not an i1. -struct AnyLifetime[is_mutable: __mlir_type.i1]: +struct AnyLifetime[is_mutable: Bool]: """This represents a lifetime reference of potentially parametric type. TODO: This should be replaced with a parametric type alias. @@ -47,6 +46,6 @@ struct AnyLifetime[is_mutable: __mlir_type.i1]: alias type = __mlir_type[ `!lit.lifetime<`, - is_mutable, + is_mutable.value, `>`, ] diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index db9bfa56df..09cd460193 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -56,7 +56,7 @@ trait RepresentableKeyElement(KeyElement, Representable): struct _DictEntryIter[ K: KeyElement, V: CollectionElement, - dict_mutability: __mlir_type.`i1`, + dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, forward: Bool = True, ]: @@ -73,9 +73,7 @@ struct _DictEntryIter[ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[ - DictEntry[K, V], __mlir_attr.`0: i1`, Self.imm_dict_lifetime - ] + alias ref_type = Reference[DictEntry[K, V], False, Self.imm_dict_lifetime] var index: Int var seen: Int @@ -122,7 +120,7 @@ struct _DictEntryIter[ struct _DictKeyIter[ K: KeyElement, V: CollectionElement, - dict_mutability: __mlir_type.`i1`, + dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, forward: Bool = True, ]: @@ -139,7 +137,7 @@ struct _DictKeyIter[ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[K, __mlir_attr.`0: i1`, Self.imm_dict_lifetime] + alias ref_type = Reference[K, False, Self.imm_dict_lifetime] alias dict_entry_iter = _DictEntryIter[ K, V, dict_mutability, dict_lifetime, forward @@ -161,7 +159,7 @@ struct _DictKeyIter[ struct _DictValueIter[ K: KeyElement, V: CollectionElement, - dict_mutability: __mlir_type.`i1`, + dict_mutability: Bool, dict_lifetime: AnyLifetime[dict_mutability].type, forward: Bool = True, ]: @@ -184,7 +182,7 @@ struct _DictValueIter[ return self fn __reversed__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + mutability: Bool, self_life: AnyLifetime[mutability].type ](self) -> _DictValueIter[K, V, dict_mutability, dict_lifetime, False]: var src = self.iter.src return _DictValueIter( diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index e17c694d0d..abbc0758a1 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -32,7 +32,7 @@ from .optional import Optional @value struct _ListIter[ T: CollectionElement, - list_mutability: __mlir_type.`i1`, + list_mutability: Bool, list_lifetime: AnyLifetime[list_mutability].type, forward: Bool = True, ]: diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 29f97c5402..cd02236411 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -189,7 +189,7 @@ struct AddressSpace(EqualityComparable): @register_passable("trivial") struct Reference[ type: AnyType, - is_mutable: __mlir_type.i1, + is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type, address_space: AddressSpace = AddressSpace.GENERIC, ]: diff --git a/stdlib/src/utils/span.mojo b/stdlib/src/utils/span.mojo index 389db6c0fe..19a40296b0 100644 --- a/stdlib/src/utils/span.mojo +++ b/stdlib/src/utils/span.mojo @@ -26,7 +26,7 @@ from . import InlineArray @value struct _SpanIter[ T: CollectionElement, - is_mutable: __mlir_type.`i1`, + is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type, forward: Bool = True, ]: @@ -70,7 +70,7 @@ struct _SpanIter[ @value struct Span[ T: CollectionElement, - is_mutable: __mlir_type.i1, + is_mutable: Bool, lifetime: AnyLifetime[is_mutable].type, ]: """A non owning view of contiguous data. From 3ac7f579287015b4f03a2a0288801fd9d3960572 Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Wed, 15 May 2024 09:10:25 -0400 Subject: [PATCH 0548/2019] [stdlib] Update LLVM vector intrinsic spelling LLVM has dropped the "experimental" from several intrinsic instructions as of https://github.com/llvm/llvm-project/pull/88748. This updates the Mojo standard library to use the new spelling. MODULAR_ORIG_COMMIT_REV_ID: 4bcf10cd6eec7524990823e0b3d33276f0c427c8 --- stdlib/src/builtin/simd.mojo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 813740e27a..08ea08e5ba 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1900,7 +1900,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return SIMD[type, 2 * size](self[0], other[0]) return llvm_intrinsic[ - "llvm.experimental.vector.interleave2", + "llvm.vector.interleave2", SIMD[type, 2 * size], has_side_effect=False, ](self, other) @@ -1932,7 +1932,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) var res = llvm_intrinsic[ - "llvm.experimental.vector.deinterleave2", + "llvm.vector.deinterleave2", _RegisterPackType[Self._SIMDHalfType, Self._SIMDHalfType], has_side_effect=False, ](self) @@ -2272,7 +2272,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[shift == 0, "for scalars the shift must be 0"]() return self return llvm_intrinsic[ - "llvm.experimental.vector.splice", Self, has_side_effect=False + "llvm.vector.splice", Self, has_side_effect=False ](self, self, Int32(shift)) @always_inline @@ -2341,7 +2341,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( alias zero_simd = Self() return llvm_intrinsic[ - "llvm.experimental.vector.splice", Self, has_side_effect=False + "llvm.vector.splice", Self, has_side_effect=False ](self, zero_simd, Int32(shift)) @always_inline @@ -2381,7 +2381,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( alias zero_simd = Self() return llvm_intrinsic[ - "llvm.experimental.vector.splice", Self, has_side_effect=False + "llvm.vector.splice", Self, has_side_effect=False ](zero_simd, self, Int32(-shift)) From cce2342b98871bac50e11faead029109146c9223 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Wed, 15 May 2024 10:43:06 -0500 Subject: [PATCH 0549/2019] [External] [stdlib] Rename `uninitialized` -> `unsafe_uninitialized` in `InlineArray` Unsafe methods should be explicit in the code. Uninitialized memory is highly unsafe (can't run destructor on it, etc) , thus it should have a name that reflects it. Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: e554696fb72e0d08dbaf0d73ee7f63af13f7409f --- stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/collections/inline_list.mojo | 4 +++- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index d7390d5b99..143b333662 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -288,7 +288,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: # 3. Copy the tail data (smaller than the SIMD register) into # a final hash state update vector that's stack-allocated. if r != 0: - var remaining = InlineArray[Int8, stride](uninitialized=True) + var remaining = InlineArray[Int8, stride](unsafe_uninitialized=True) var ptr = DTypePointer[DType.int8]( UnsafePointer.address_of(remaining).bitcast[Int8]() ) diff --git a/stdlib/src/collections/inline_list.mojo b/stdlib/src/collections/inline_list.mojo index c2dd0432e9..8519899d49 100644 --- a/stdlib/src/collections/inline_list.mojo +++ b/stdlib/src/collections/inline_list.mojo @@ -48,7 +48,9 @@ struct InlineList[ElementType: CollectionElement, capacity: Int = 16](Sized): @always_inline fn __init__(inout self): """This constructor creates an empty InlineList.""" - self._array = InlineArray[ElementType, capacity](uninitialized=True) + self._array = InlineArray[ElementType, capacity]( + unsafe_uninitialized=True + ) self._size = 0 @always_inline diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 1beb9eb2bd..edd2ec0dc6 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -115,7 +115,7 @@ struct InlinedFixedVector[ Args: capacity: The requested maximum capacity of the vector. """ - self.static_data = Self.static_data_type(uninitialized=True) + self.static_data = Self.static_data_type(unsafe_uninitialized=True) self.dynamic_data = UnsafePointer[type]() if capacity > Self.static_size: self.dynamic_data = UnsafePointer[type].alloc(capacity - size) diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 7c91e231f2..ea74e9f744 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -277,7 +277,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): self._array = __mlir_op.`kgen.undef`[_type = Self.type]() @always_inline - fn __init__(inout self, *, uninitialized: Bool): + fn __init__(inout self, *, unsafe_uninitialized: Bool): """Create an InlineArray with uninitialized memory. Note that this is highly unsafe and should be used with caution. @@ -289,11 +289,11 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): it is possible with: ```mojo - var uninitialized_array = InlineArray[Int, 10](uninitialized=True) + var uninitialized_array = InlineArray[Int, 10](unsafe_uninitialized=True) ``` Args: - uninitialized: A boolean to indicate if the array should be initialized. + unsafe_uninitialized: A boolean to indicate if the array should be initialized. Always set to `True` (it's not actually used inside the constructor). """ self._array = __mlir_op.`kgen.undef`[_type = Self.type]() From c48e5187b1bc223ca2791d8aa7fd1881f968fc07 Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Wed, 15 May 2024 11:50:04 -0400 Subject: [PATCH 0550/2019] [mojo-tooling] JSON diagnostic output option Add a command line option, `--diagnostic-format`, that controls the format with which diagnostics are output. The option takes one of two values: 1. "text": this outputs diagnostics as before, and is the default value. 2. "json": this outputs diagnostics as structured JSON, making them easier to parse by other tools. The option impacts not only Mojo language diagnostics output during compilation, but also error messages printed by the driver, such as ones about invalid command line arguments. This mimics other tools, such as `rustc --error-format=json`. For example, when this invalid Rust program is compiled with `rustc`, two error diagnostics are output as JSON: one for the Rust source code error, and another from the driver indicating it is aborting compilation: ``` $ rustc --error-format=json foo.rs {"$message_type":"diagnostic","message":"expected one of `!` or `::`, found `f`","code":null,"level":"error","spans":[{"file_name":"foo.rs","byte_start":2,"byte_end":3,"line_start":1,"line_end":1,"column_start":3,"column_end":4,"is_primary":true,"text":[{"text":"f f() {}","highlight_start":3,"highlight_end":4}],"label":"expected one of `!` or `::`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"error: expected one of `!` or `::`, found `f`\n --> foo.rs:1:3\n |\n1 | f f() {}\n | ^ expected one of `!` or `::`\n\n"} {"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error\n\n"} ``` MODULAR_ORIG_COMMIT_REV_ID: 56295be03943065736b7ac5bf8f9c1d9b3a59184 --- docs/changelog.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9120ac88fc..0e27839cdc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -130,6 +130,21 @@ what we publish. - A new `--validate-doc-strings` option has been added to `mojo` to emit errors on invalid doc strings instead of warnings. +- Several `mojo` subcommands now support a `--diagnostic-format` option that + changes the format with which errors, warnings, and other diagnostics are + printed. By specifying `--diagnostic-format json` on the command line, errors + and other diagnostics will be output in a structured + [JSON Lines](https://jsonlines.org) format that is easier for machines to + parse. + + The full list of subcommands that support `--diagnostic-format` is as follows: + `mojo build`, `mojo doc`, `mojo run`, `mojo package`, and `mojo test`. + Further, the `mojo test --json` option has been subsumed into this new option; + for the same behavior, run `mojo test --diagnostic-format json`. + + Note that the format of the JSON output may change; we don't currently + guarantee its stability across releases of Mojo. + - A new decorator, `@doc_private`, was added that can be used to hide a decl from being generated in the output of `mojo doc`. It also removes the requirement that the decl has documentation (e.g. when used with From 03122416eca04ef443fb04f384ff4c51f378c9b3 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 12:16:56 -0400 Subject: [PATCH 0551/2019] [stdlib] Move `math.bit` to top-level `bit` module The patch also removes some methods in this module that have equivalent counterparts in `SIMD`. MODULAR_ORIG_COMMIT_REV_ID: f3c002e96d8ded77d2ac33405a672220ba2e3ab9 --- docs/changelog.md | 6 + stdlib/src/bit/__init__.mojo | 23 +++ stdlib/src/bit/bit.mojo | 287 +++++++++++++++++++++++++++++++++++ stdlib/src/builtin/simd.mojo | 3 + 4 files changed, 319 insertions(+) create mode 100644 stdlib/src/bit/__init__.mojo create mode 100644 stdlib/src/bit/bit.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 0e27839cdc..b42fca594b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -204,6 +204,8 @@ what we publish. `swap` and `partition` will likely shuffle around as we're reworking our builtnin `sort` function and optimizing it. +- The `math.bit` module has been moved to a new top-level `bit` module. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the @@ -225,6 +227,10 @@ what we publish. - The `math.div_ceil` function has been removed in favor of the `math.ceildiv` function. +- The `math.bit.select` and `math.bit.bit_and` functions have been removed. The + same functionality is available in the builtin `SIMD.select` and + `SIMD.__and__` methods, respectively. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo new file mode 100644 index 0000000000..c43f1c4b35 --- /dev/null +++ b/stdlib/src/bit/__init__.mojo @@ -0,0 +1,23 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the bit package.""" + +from .bit import ( + ctlz, + cttz, + bitreverse, + bswap, + ctpop, + bit_not, + bit_length, +) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo new file mode 100644 index 0000000000..6680e3bc1a --- /dev/null +++ b/stdlib/src/bit/bit.mojo @@ -0,0 +1,287 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provides functions for bit manipulation. + +You can import these APIs from the `bit` package. For example: + +```mojo +from bit import ctlz +``` +""" + +from sys import llvm_intrinsic +from sys.info import bitwidthof + +# ===----------------------------------------------------------------------===# +# ctlz +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn ctlz(val: Int) -> Int: + """Counts the number of leading zeros of an integer. + + Args: + val: The input value. + + Returns: + The number of leading zeros of the input. + """ + return llvm_intrinsic["llvm.ctlz", Int, has_side_effect=False](val, False) + + +@always_inline("nodebug") +fn ctlz[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Counts the per-element number of leading zeros in a SIMD vector. + + Parameters: + type: `DType` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` contains the number of + leading zeros at position `i` of the input value. + """ + constrained[type.is_integral(), "must be integral"]() + return llvm_intrinsic["llvm.ctlz", __type_of(val), has_side_effect=False]( + val, False + ) + + +# ===----------------------------------------------------------------------===# +# cttz +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn cttz(val: Int) -> Int: + """Counts the number of trailing zeros for an integer. + + Args: + val: The input value. + + Returns: + The number of trailing zeros of the input. + """ + return llvm_intrinsic["llvm.cttz", Int, has_side_effect=False](val, False) + + +@always_inline("nodebug") +fn cttz[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Counts the per-element number of trailing zeros in a SIMD vector. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` contains the number of + trailing zeros at position `i` of the input value. + """ + constrained[type.is_integral(), "must be integral"]() + return llvm_intrinsic["llvm.cttz", __type_of(val), has_side_effect=False]( + val, False + ) + + +# ===----------------------------------------------------------------------===# +# bitreverse +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn bitreverse[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Element-wise reverses the bitpattern of an integral value. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Args: + val: The input value. + + Constraints: + The element type of the input vector must be integral. + + Returns: + A SIMD value where the element at position `i` has a reversed bitpattern + of an integer value of the element at position `i` of the input value. + """ + constrained[type.is_integral(), "must be integral"]() + return llvm_intrinsic[ + "llvm.bitreverse", __type_of(val), has_side_effect=False + ](val) + + +# ===----------------------------------------------------------------------===# +# bswap +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn bswap[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Byte-swaps a value. + + Byte swap an integer value or vector of integer values with an even number + of bytes (positive multiple of 16 bits). This is equivalent to `llvm.bswap` + intrinsic that has the following semantics: + + The `llvm.bswap.i16` intrinsic returns an i16 value that has the high and + low byte of the input i16 swapped. Similarly, the `llvm.bswap.i32` intrinsic + returns an i32 value that has the four bytes of the input i32 swapped, so + that if the input bytes are numbered 0, 1, 2, 3 then the returned i32 will + have its bytes in 3, 2, 1, 0 order. The `llvm.bswap.i48`, `llvm.bswap.i64` + and other intrinsics extend this concept to additional even-byte lengths (6 + bytes, 8 bytes and more, respectively). + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be an integral type with an + even number of bytes (Bitwidth % 16 == 0). + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` is the value of the + element at position `i` of the input value with its bytes swapped. + """ + constrained[type.is_integral(), "must be integral"]() + return llvm_intrinsic["llvm.bswap", __type_of(val), has_side_effect=False]( + val + ) + + +# ===----------------------------------------------------------------------===# +# ctpop +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn ctpop[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Counts the number of bits set in a value. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` contains the number of + bits set in the element at position `i` of the input value. + """ + constrained[type.is_integral(), "must be integral"]() + return llvm_intrinsic["llvm.ctpop", __type_of(val), has_side_effect=False]( + val + ) + + +# ===----------------------------------------------------------------------===# +# bit_not +# ===----------------------------------------------------------------------===# + + +@always_inline("nodebug") +fn bit_not[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Performs a bitwise NOT operation on an integral. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` is computed as a bitwise + NOT of the integer value at position `i` of the input value. + """ + constrained[type.is_integral(), "must be integral"]() + var neg_one = SIMD[type, simd_width].splat(-1) + return __mlir_op.`pop.xor`(val.value, neg_one.value) + + +# ===----------------------------------------------------------------------===# +# bit_length +# ===----------------------------------------------------------------------===# + + +@always_inline +fn bit_length[ + type: DType, simd_width: Int +](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the number of digits required to represent the integer. + + Parameters: + type: `dtype` used for the computation. + simd_width: SIMD width used for the computation. + + Constraints: + The element type of the input vector must be integral. + + Args: + val: The input value. + + Returns: + A SIMD value where the element at position `i` equals to the number of + digits required to represent the integer at position `i` of the input + value. + """ + + constrained[type.is_integral(), "must be integral"]() + + alias bitwidth = bitwidthof[type]() + + @parameter + if type.is_unsigned(): + return bitwidth - ctlz(val) + else: + var leading_zero_pos = ctlz(val) + var leading_zero_neg = ctlz(bit_not(val)) + var leading_zero = (val > 0).select(leading_zero_pos, leading_zero_neg) + return bitwidth - leading_zero diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 08ea08e5ba..7149a26e59 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2230,6 +2230,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( true_case: The values selected if the positional value is True. false_case: The values selected if the positional value is False. + Constraints: + The element type of the vector must be boolean. + Returns: A new vector of the form `[true_case[i] if elem else false_case[i] for i, elem in enumerate(self)]`. From 2262cc5b5945eb60ec8978919c1d0778ea17ae9e Mon Sep 17 00:00:00 2001 From: Wangshu Jiang <59179986+Dan13llljws@users.noreply.github.com> Date: Wed, 15 May 2024 12:33:13 -0400 Subject: [PATCH 0552/2019] [stdlib] Move `_ObjectImpl` to use `Variant` instead of `!kgen.variant` Refactored `_ObjectImpl` to use higher level `Variant` class instead of `!kgen.variant`. Removed reg-passability for now. MODULAR_ORIG_COMMIT_REV_ID: 4feedb5e0bf1df611be78b38f6817ecb8fcd187b --- stdlib/src/builtin/object.mojo | 91 +++++++++++----------------------- 1 file changed, 30 insertions(+), 61 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 99761071bb..e5eeb5cb3a 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -24,7 +24,7 @@ from memory import memcmp, memcpy, DTypePointer from memory import Arc from memory.unsafe_pointer import move_from_pointee -from utils import StringRef, unroll +from utils import StringRef, unroll, Variant from .io import _printf, _put @@ -239,7 +239,6 @@ struct _Function: ) -@register_passable struct _ObjectImpl(CollectionElement, Stringable): """This class is the underlying implementation of the value of an `object`. It is a variant of primitive types and pointers to implementations of more @@ -249,24 +248,15 @@ struct _ObjectImpl(CollectionElement, Stringable): TODO: These should be BigInt and BigFloat one day. """ - alias type = __mlir_type[ - `!kgen.variant<`, + alias type = Variant[ _NoneMarker, - `, `, Bool, - `, `, Int64, - `, `, Float64, - `, `, _ImmutableString, - `, `, _RefCountedListRef, - `, `, _Function, - `, `, _RefCountedAttrsDictRef, - `>`, ] """The variant value type.""" var value: Self.type @@ -302,56 +292,44 @@ struct _ObjectImpl(CollectionElement, Stringable): @always_inline fn __init__(inout self): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.none.value - ](_NoneMarker {}) + self.value = Self.type(_NoneMarker {}) @always_inline fn __init__(inout self, value: Bool): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.bool.value - ](value) + self.value = Self.type(value) @always_inline fn __init__[dt: DType](inout self, value: SIMD[dt, 1]): @parameter if dt.is_integral(): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.int.value - ](value.cast[DType.int64]()) + self.value = Self.type(value) else: - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.float.value - ](value.cast[DType.float64]()) + self.value = Self.type(value) @always_inline fn __init__(inout self, value: _ImmutableString): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.str.value - ](value) + self.value = Self.type(value) @always_inline fn __init__(inout self, value: _RefCountedListRef): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.list.value - ](value) + self.value = Self.type(value) @always_inline fn __init__(inout self, value: _Function): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.function.value - ](value) + self.value = Self.type(value) @always_inline fn __init__(inout self, value: _RefCountedAttrsDictRef): - self.value = __mlir_op.`kgen.variant.create`[ - _type = Self.type, index = Self.obj.value - ](value) + self.value = Self.type(value) @always_inline fn __copyinit__(inout self, existing: Self): self = existing.value + @always_inline + fn __moveinit__(inout self, owned other: Self): + self = other.value^ + @always_inline fn copy(self) -> Self: if self.is_str(): @@ -382,27 +360,27 @@ struct _ObjectImpl(CollectionElement, Stringable): @always_inline fn is_none(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.none.value](self.value) + return self.value.isa[_NoneMarker]() @always_inline fn is_bool(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.bool.value](self.value) + return self.value.isa[Bool]() @always_inline fn is_int(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.int.value](self.value) + return self.value.isa[Int64]() @always_inline fn is_float(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.float.value](self.value) + return self.value.isa[Float64]() @always_inline fn is_str(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.str.value](self.value) + return self.value.isa[_ImmutableString]() @always_inline fn is_list(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.list.value](self.value) + return self.value.isa[_RefCountedListRef]() @always_inline fn is_dict(self) -> Bool: @@ -410,49 +388,40 @@ struct _ObjectImpl(CollectionElement, Stringable): @always_inline fn is_func(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.function.value]( - self.value - ) + return self.value.isa[_Function]() @always_inline fn is_obj(self) -> Bool: - return __mlir_op.`kgen.variant.is`[index = Self.obj.value](self.value) + return self.value.isa[_RefCountedAttrsDictRef]() + # get a copy @always_inline fn get_as_bool(self) -> Bool: - return __mlir_op.`kgen.variant.take`[index = Self.bool.value]( - self.value - ) + return self.value[Bool] @always_inline fn get_as_int(self) -> Int64: - return __mlir_op.`kgen.variant.take`[index = Self.int.value](self.value) + return self.value[Int64] @always_inline fn get_as_float(self) -> Float64: - return __mlir_op.`kgen.variant.take`[index = Self.float.value]( - self.value - ) + return self.value[Float64] @always_inline fn get_as_string(self) -> _ImmutableString: - return __mlir_op.`kgen.variant.take`[index = Self.str.value](self.value) + return self.value[_ImmutableString] @always_inline fn get_as_list(self) -> _RefCountedListRef: - return __mlir_op.`kgen.variant.take`[index = Self.list.value]( - self.value - ) + return self.value[_RefCountedListRef] @always_inline fn get_as_func(self) -> _Function: - return __mlir_op.`kgen.variant.take`[index = Self.function.value]( - self.value - ) + return self.value[_Function] @always_inline fn get_obj_attrs(self) -> _RefCountedAttrsDictRef: - return __mlir_op.`kgen.variant.take`[index = Self.obj.value](self.value) + return self.value[_RefCountedAttrsDictRef] @always_inline fn get_type_id(self) -> Int: From fae0cda143cc6e27af686312d3d06392ebdd69e1 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 13:03:35 -0400 Subject: [PATCH 0553/2019] [stdlib] Deduplicate `ctlz` and `cttz` implementations Now that the `bit` module is available, we can just import these from it. MODULAR_ORIG_COMMIT_REV_ID: 98103436277d0947767d3dfb4a34ccd3e067ade7 --- stdlib/src/builtin/sort.mojo | 21 ++------------------- stdlib/src/builtin/string.mojo | 22 +++------------------- stdlib/src/utils/stringref.mojo | 18 +++--------------- 3 files changed, 8 insertions(+), 53 deletions(-) diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index e57229fb82..bfe053479f 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -15,6 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ +from bit import ctlz from collections import List from memory import Pointer, UnsafePointer from sys import bitwidthof @@ -124,27 +125,9 @@ fn _partition[ return right -# ===----------------------------------------------------------------------===# -# ctlz -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn _ctlz(val: Int) -> Int: - """Counts the number of leading zeros of an integer. - - Args: - val: The input value. - - Returns: - The number of leading zeros of the input. - """ - return llvm_intrinsic["llvm.ctlz", Int, has_side_effect=False](val, False) - - fn _estimate_initial_height(size: Int) -> Int: # Compute the log2 of the size rounded upward. - var log2 = int((bitwidthof[DType.index]() - 1) ^ _ctlz(size | 1)) + var log2 = int((bitwidthof[DType.index]() - 1) ^ ctlz(size | 1)) return max(2, log2) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index c2b4462b85..8db69ce153 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -15,6 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ +from bit import ctlz from collections import List, KeyElement from sys import llvm_intrinsic, bitwidthof @@ -25,23 +26,6 @@ from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf -# ===----------------------------------------------------------------------===# -# Utilities -# ===----------------------------------------------------------------------===# - - -@always_inline -fn _ctlz(val: Int) -> Int: - return llvm_intrinsic["llvm.ctlz", Int, has_side_effect=False](val, False) - - -@always_inline("nodebug") -fn _ctlz(val: SIMD) -> __type_of(val): - return llvm_intrinsic["llvm.ctlz", __type_of(val), has_side_effect=False]( - val, False - ) - - # ===----------------------------------------------------------------------===# # ord # ===----------------------------------------------------------------------===# @@ -70,7 +54,7 @@ fn ord(s: String) -> Int: if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(len(s) == 1, "input string length must be 1") return int(b1) - var num_bytes = _ctlz(~b1) + var num_bytes = ctlz(~b1) debug_assert(len(s) == int(num_bytes), "input string must be one character") var shift = int((6 * (num_bytes - 1))) var b1_mask = 0b11111111 >> (num_bytes + 1) @@ -1689,7 +1673,7 @@ fn _calc_initial_buffer_size_int32(n0: Int) -> Int: 42949672960, ) var n = UInt32(n0) - var log2 = int((bitwidthof[DType.uint32]() - 1) ^ _ctlz(n | 1)) + var log2 = int((bitwidthof[DType.uint32]() - 1) ^ ctlz(n | 1)) return (n0 + lookup_table[int(log2)]) >> 32 diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 2f85e18409..0aa758f85a 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -13,7 +13,7 @@ """Implements the StringRef class. """ - +from bit import cttz from builtin.dtype import _uint_type_of_width from builtin.string import _atol from memory import DTypePointer, UnsafePointer @@ -443,18 +443,6 @@ struct StringRef( # ===----------------------------------------------------------------------===# -@always_inline("nodebug") -fn _cttz(val: Int) -> Int: - return llvm_intrinsic["llvm.cttz", Int, has_side_effect=False](val, False) - - -@always_inline("nodebug") -fn _cttz(val: SIMD) -> __type_of(val): - return llvm_intrinsic["llvm.cttz", __type_of(val), has_side_effect=False]( - val, False - ) - - @always_inline fn _memchr[ type: DType @@ -471,7 +459,7 @@ fn _memchr[ var bool_mask = source.load[width=bool_mask_width](i) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) if mask: - return source + i + _cttz(mask) + return source + i + cttz(mask) for i in range(vectorized_end, len): if source[i] == char: @@ -504,7 +492,7 @@ fn _memmem[ var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: - var offset = i + _cttz(mask) + var offset = i + cttz(mask) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) From d4e0335c2f2bd6d5183c02737345b59d0b225d9e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 13:40:37 -0400 Subject: [PATCH 0554/2019] [stdlib] Move `math.rotate_bits_{left,right}` to the `bit` module Also, the `math.rotate_{left,right}` functions are removed, since they are redundant. MODULAR_ORIG_COMMIT_REV_ID: 2dd5f26f26dd57404ebe8330e0df094f8b318ae3 --- docs/changelog.md | 8 ++ stdlib/src/bit/__init__.mojo | 2 + stdlib/src/bit/bit.mojo | 154 ++++++++++++++++++++++++++++++++++ stdlib/test/bit/test_bit.mojo | 68 +++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 stdlib/test/bit/test_bit.mojo diff --git a/docs/changelog.md b/docs/changelog.md index b42fca594b..261eb7cd85 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -206,6 +206,9 @@ what we publish. - The `math.bit` module has been moved to a new top-level `bit` module. +- The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been + moved to the `bit` module. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the @@ -231,6 +234,11 @@ what we publish. same functionality is available in the builtin `SIMD.select` and `SIMD.__and__` methods, respectively. +- The `math.rotate_left` and `math.rotate_right` functions have been removed. + The same functionality is available in the builtin `SIMD.rotate_{left,right}` + methods for `SIMD` types, and the `bit.rotate_bits_{left,right}` methods for + `Int`. + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index c43f1c4b35..dd51d43aaf 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -20,4 +20,6 @@ from .bit import ( ctpop, bit_not, bit_length, + rotate_bits_left, + rotate_bits_right, ) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 6680e3bc1a..8aba1b76a9 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -285,3 +285,157 @@ fn bit_length[ var leading_zero_neg = ctlz(bit_not(val)) var leading_zero = (val > 0).select(leading_zero_pos, leading_zero_neg) return bitwidth - leading_zero + + +# ===----------------------------------------------------------------------===# +# rotate_bits_left +# ===----------------------------------------------------------------------===# + + +@always_inline +fn rotate_bits_left[shift: Int](x: Int) -> Int: + """Shifts the bits of an input to the left by `shift` bits (with + wrap-around). + + Constraints: + `-size <= shift < size` + + Parameters: + shift: The number of bit positions by which to rotate the bits of the + integer to the left (with wrap-around). + + Args: + x: The input value. + + Returns: + The input rotated to the left by `shift` elements (with wrap-around). + """ + constrained[ + shift >= -sizeof[Int]() and shift < sizeof[Int](), + "Constraints: -sizeof[Int]() <= shift < sizeof[Int]()", + ]() + + @parameter + if shift == 0: + return x + elif shift < 0: + return rotate_bits_right[-shift](x) + else: + return llvm_intrinsic["llvm.fshl", Int, has_side_effect=False]( + x, x, shift + ) + + +fn rotate_bits_left[ + shift: Int, type: DType, width: Int +](x: SIMD[type, width]) -> SIMD[type, width]: + """Shifts bits to the left by `shift` positions (with wrap-around) for each + element of a SIMD vector. + + Constraints: + `0 <= shift < size` + + Parameters: + shift: The number of positions by which to shift left the bits for each + element of a SIMD vector to the left (with wrap-around). + type: The `dtype` of the input and output SIMD vector. + Constraints: must be integral and unsigned. + width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector to perform the operation on. + + Returns: + The SIMD vector with each element's bits shifted to the left by `shift` + bits (with wrap-around). + """ + + constrained[type.is_unsigned(), "Only unsigned types can be rotated."]() + + @parameter + if shift == 0: + return x + elif shift < 0: + return rotate_bits_right[-shift, type, width](x) + else: + return llvm_intrinsic["llvm.fshl", __type_of(x), has_side_effect=False]( + x, x, SIMD[type, width](shift) + ) + + +# ===----------------------------------------------------------------------===# +# rotate_bits_right +# ===----------------------------------------------------------------------===# + + +@always_inline +fn rotate_bits_right[shift: Int](x: Int) -> Int: + """Shifts the bits of an input to the right by `shift` bits (with + wrap-around). + + Constraints: + `-size <= shift < size` + + Parameters: + shift: The number of bit positions by which to rotate the bits of the + integer to the right (with wrap-around). + + Args: + x: The input value. + + Returns: + The input rotated to the right by `shift` elements (with wrap-around). + """ + constrained[ + shift >= -sizeof[Int]() and shift < sizeof[Int](), + "Constraints: -sizeof[Int]() <= shift < sizeof[Int]()", + ]() + + @parameter + if shift == 0: + return x + elif shift < 0: + return rotate_bits_left[-shift](x) + else: + return llvm_intrinsic["llvm.fshr", Int, has_side_effect=False]( + x, x, shift + ) + + +fn rotate_bits_right[ + shift: Int, + type: DType, + width: Int, +](x: SIMD[type, width]) -> SIMD[type, width]: + """Shifts bits to the right by `shift` positions (with wrap-around) for each + element of a SIMD vector. + + Constraints: + `0 <= shift < size` + + Parameters: + shift: The number of positions by which to shift right the bits for each + element of a SIMD vector to the left (with wrap-around). + type: The `dtype` of the input and output SIMD vector. + Constraints: must be integral and unsigned. + width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector to perform the operation on. + + Returns: + The SIMD vector with each element's bits shifted to the right by `shift` + bits (with wrap-around). + """ + + constrained[type.is_unsigned(), "Only unsigned types can be rotated."]() + + @parameter + if shift == 0: + return x + elif shift < 0: + return rotate_bits_left[-shift, type, width](x) + else: + return llvm_intrinsic["llvm.fshr", __type_of(x), has_side_effect=False]( + x, x, SIMD[type, width](shift) + ) diff --git a/stdlib/test/bit/test_bit.mojo b/stdlib/test/bit/test_bit.mojo new file mode 100644 index 0000000000..106a73efcf --- /dev/null +++ b/stdlib/test/bit/test_bit.mojo @@ -0,0 +1,68 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from bit import rotate_bits_left, rotate_bits_right + +from testing import assert_equal + + +def test_rotate_bits_int(): + assert_equal(rotate_bits_left[0](104), 104) + assert_equal(rotate_bits_left[2](104), 416) + assert_equal(rotate_bits_left[-2](104), 26) + + assert_equal(rotate_bits_right[0](104), 104) + assert_equal(rotate_bits_right[2](104), 26) + assert_equal(rotate_bits_right[-2](104), 416) + + +def test_rotate_bits_simd(): + alias simd_width = 1 + alias type = DType.uint8 + + assert_equal(rotate_bits_left[0](UInt64(104)), 104) + assert_equal(rotate_bits_left[0](SIMD[type, simd_width](104)), 104) + assert_equal( + rotate_bits_left[2](SIMD[type, 2](104)), SIMD[type, 2](161, 161) + ) + + assert_equal(rotate_bits_left[2](Scalar[type](104)), 161) + assert_equal(rotate_bits_left[11](Scalar[type](15)), 120) + assert_equal(rotate_bits_left[0](Scalar[type](96)), 96) + assert_equal(rotate_bits_left[1](Scalar[type](96)), 192) + assert_equal(rotate_bits_left[2](Scalar[type](96)), 129) + assert_equal(rotate_bits_left[3](Scalar[type](96)), 3) + assert_equal(rotate_bits_left[4](Scalar[type](96)), 6) + assert_equal(rotate_bits_left[5](Scalar[type](96)), 12) + + assert_equal(rotate_bits_right[0](UInt64(104)), 104) + assert_equal(rotate_bits_right[0](SIMD[type, simd_width](104)), 104) + assert_equal( + rotate_bits_right[2](SIMD[type, 2](104)), SIMD[type, 2](26, 26) + ) + + assert_equal(rotate_bits_right[2](Scalar[type](104)), 26) + assert_equal(rotate_bits_right[11](Scalar[type](15)), 225) + assert_equal(rotate_bits_right[0](Scalar[type](96)), 96) + assert_equal(rotate_bits_right[1](Scalar[type](96)), 48) + assert_equal(rotate_bits_right[2](Scalar[type](96)), 24) + assert_equal(rotate_bits_right[3](Scalar[type](96)), 12) + assert_equal(rotate_bits_right[4](Scalar[type](96)), 6) + assert_equal(rotate_bits_right[5](Scalar[type](96)), 3) + assert_equal(rotate_bits_right[6](Scalar[type](96)), 129) + + +def main(): + test_rotate_bits_int() + test_rotate_bits_simd() From 033e4a08a71cebf92bbc52ad46399f5d819f88b9 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 15 May 2024 12:52:27 -0500 Subject: [PATCH 0555/2019] [stdlib] Remove Copyable from Tuple and ListLiteral This changes `Tuple` and `ListLiteral` to require only `Movable` elements. Consequently, `Tuple` and `ListLiteral` themselves are no longer copyable. MODULAR_ORIG_COMMIT_REV_ID: 63a4db7a547d3c55be450b6eaeda39846d40812a --- docs/changelog.md | 3 +++ stdlib/src/builtin/builtin_list.mojo | 14 ++++++++++--- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 31 +++++++--------------------- stdlib/src/python/object.mojo | 4 ++-- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 261eb7cd85..d64b3b24d3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -204,6 +204,9 @@ what we publish. `swap` and `partition` will likely shuffle around as we're reworking our builtnin `sort` function and optimizing it. +- `ListLiteral` and `Tuple` now only requires that element types be `Copyable`. + Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. + - The `math.bit` module has been moved to a new top-level `bit` module. - The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 5fa1fdb2c6..804b826961 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -23,8 +23,7 @@ from memory.unsafe_pointer import destroy_pointee # ===----------------------------------------------------------------------===# -@value -struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): +struct ListLiteral[*Ts: Movable](Sized, Movable): """The type of a literal heterogeneous list expression. A list consists of zero or more values, separated by commas. @@ -45,6 +44,15 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): """ self.storage = Tuple(storage=args^) + fn __moveinit__(inout self, owned existing: Self): + """Move construct the list. + + Args: + existing: The value to move from. + """ + + self.storage = existing.storage^ + @always_inline("nodebug") fn __len__(self) -> Int: """Get the list length. @@ -55,7 +63,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): return len(self.storage) @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](self) -> T: + fn get[i: Int, T: Movable](self) -> T: """Get a list element at the given index. Parameters: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index e5eeb5cb3a..31ce913b23 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -749,7 +749,7 @@ struct object(IntableRaising, Boolable, Stringable): self._value = impl @always_inline - fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: Movable](inout self, value: ListLiteral[Ts]): """Initializes the object from a list literal. Parameters: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 41b935d5d9..a61ba80680 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -29,7 +29,7 @@ from memory.unsafe_pointer import ( @lldb_formatter_wrapping_type -struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): +struct Tuple[*element_types: Movable](Sized, Movable): """The type of a literal tuple expression. A tuple consists of zero or more values, separated by commas. @@ -40,7 +40,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): alias _mlir_type = __mlir_type[ `!kgen.pack<:!kgen.variadic<`, - CollectionElement, + Movable, `> `, +element_types, `>`, @@ -62,14 +62,15 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): fn __init__( inout self, *, - owned storage: VariadicPack[_, _, CollectionElement, element_types], + owned storage: VariadicPack[_, _, Movable, element_types], ): """Construct the tuple from a low-level internal representation. Args: storage: The variadic pack storage to construct from. """ - # Mark 'storage' as being initialized so we can work on it. + + # Mark 'self.storage' as being initialized so we can work on it. __mlir_op.`lit.ownership.mark_initialized`( __get_mvalue_as_litref(self.storage) ) @@ -98,24 +99,6 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): unroll[destroy_elt, Self.__len__()]() - @always_inline("nodebug") - fn __copyinit__(inout self, existing: Self): - """Copy construct the tuple. - - Args: - existing: The value to copy from. - """ - # Mark 'storage' as being initialized so we can work on it. - __mlir_op.`lit.ownership.mark_initialized`( - __get_mvalue_as_litref(self.storage) - ) - - @parameter - fn initialize_elt[idx: Int](): - initialize_pointee_copy(UnsafePointer(self[idx]), existing[idx]) - - unroll[initialize_elt, Self.__len__()]() - @always_inline("nodebug") fn __moveinit__(inout self, owned existing: Self): """Move construct the tuple. @@ -148,7 +131,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn variadic_size( - x: __mlir_type[`!kgen.variadic<`, CollectionElement, `>`] + x: __mlir_type[`!kgen.variadic<`, Movable, `>`] ) -> Int: return __mlir_op.`pop.variadic.size`(x) @@ -184,7 +167,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO(#38268): Remove this method when references and parameter expressions # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") - fn get[i: Int, T: CollectionElement](self) -> T: + fn get[i: Int, T: Movable](self) -> T: """Get a tuple element and rebind to the specified type. Parameters: diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 44bd922bca..658a7912b5 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -209,7 +209,7 @@ struct PythonObject( self.py_object = cpython.toPython(string._strref_dangerous()) string._strref_keepalive() - fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: Movable](inout self, value: ListLiteral[Ts]): """Initialize the object from a list literal. Parameters: @@ -250,7 +250,7 @@ struct PythonObject( unroll[fill, len(VariadicList(Ts))]() - fn __init__[*Ts: CollectionElement](inout self, value: Tuple[Ts]): + fn __init__[*Ts: Movable](inout self, value: Tuple[Ts]): """Initialize the object from a tuple literal. Parameters: From e6955d55410ba64b85184ee4084294aadf13c727 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 14:03:49 -0400 Subject: [PATCH 0556/2019] [stdlib] Make some methods in `utils._numerics` return `IntLiteral` This makes them a bit easier to use at compile time. MODULAR_ORIG_COMMIT_REV_ID: 903b1566ac9b8a7d8ae2a1a61f20eb0b21eabbed --- stdlib/src/utils/_numerics.mojo | 68 +++++++++++++++------------------ 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index 27991fafab..b36090216e 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -31,7 +31,7 @@ from memory import UnsafePointer, bitcast @always_inline("nodebug") -fn _digits[type: DType]() -> Int: +fn _digits[type: DType]() -> IntLiteral: """Returns the number of digits in base-radix that can be represented by the type without change. @@ -52,29 +52,24 @@ fn _digits[type: DType]() -> Int: @parameter if type == DType.bool: return 1 + elif type.is_integral(): - @parameter - if type.is_integral(): - var bitwidth = bitwidthof[mlir_type]() - return bitwidth - 1 if type.is_signed() else bitwidth - - @parameter - if type == DType.float16: + @parameter + if type.is_signed(): + return bitwidthof[mlir_type]() - 1 + else: + return bitwidthof[mlir_type]() + elif type == DType.float16: return 11 - - @parameter - if type == DType.bfloat16: + elif type == DType.bfloat16: return 8 - - @parameter - if type == DType.float32: + elif type == DType.float32: return 24 - - @parameter - if type == DType.float64: + elif type == DType.float64: return 53 - # Unreachable. - return -1 + else: + constrained[False, "unsupported DType"]() + return -1 # ===----------------------------------------------------------------------===# @@ -142,7 +137,7 @@ struct FPUtils[type: DType]: @staticmethod @always_inline("nodebug") - fn mantissa_width() -> Int: + fn mantissa_width() -> IntLiteral: """Returns the mantissa width of a floating point type. Returns: @@ -156,7 +151,7 @@ struct FPUtils[type: DType]: @staticmethod @always_inline("nodebug") - fn max_exponent() -> Int: + fn max_exponent() -> IntLiteral: """Returns the max exponent of a floating point type. Returns: @@ -172,13 +167,13 @@ struct FPUtils[type: DType]: return 16 elif type == DType.float32 or type == DType.bfloat16: return 128 - - debug_assert(type == DType.float64, "must be float64") - return 1024 + else: + debug_assert(type == DType.float64, "must be float64") + return 1024 @staticmethod @always_inline("nodebug") - fn exponent_width() -> Int: + fn exponent_width() -> IntLiteral: """Returns the exponent width of a floating point type. Returns: @@ -194,9 +189,9 @@ struct FPUtils[type: DType]: return 5 elif type == DType.float32 or type == DType.bfloat16: return 8 - - debug_assert(type == DType.float64, "must be float64") - return 11 + else: + debug_assert(type == DType.float64, "must be float64") + return 11 @staticmethod @always_inline @@ -214,7 +209,7 @@ struct FPUtils[type: DType]: @staticmethod @always_inline - fn exponent_bias() -> Int: + fn exponent_bias() -> IntLiteral: """Returns the exponent bias of a floating point type. Returns: @@ -239,7 +234,8 @@ struct FPUtils[type: DType]: type.is_floating_point(), "dtype must be a floating point type", ]() - return 1 << (Self.exponent_width() + Self.mantissa_width()) + alias shift = int(Self.exponent_width() + Self.mantissa_width()) + return 1 << shift @staticmethod @always_inline @@ -289,7 +285,7 @@ struct FPUtils[type: DType]: type.is_floating_point(), "dtype must be a floating point type", ]() - var mantissa_width_val = Self.mantissa_width() + alias mantissa_width_val = Self.mantissa_width() return (1 << Self.exponent_width() - 1) << mantissa_width_val + ( 1 << (mantissa_width_val - 1) ) @@ -380,8 +376,8 @@ struct FPUtils[type: DType]: Returns: Returns the exponent bits. """ - - return Self.get_exponent(value) - Self.exponent_bias() + alias bias = int(Self.exponent_bias()) + return Self.get_exponent(value) - bias @staticmethod @always_inline @@ -602,8 +598,7 @@ fn nan[type: DType]() -> Scalar[type]: ) else: constrained[False, "nan only support on floating point types"]() - - return 0 + return 0 # ===----------------------------------------------------------------------===# @@ -697,8 +692,7 @@ fn inf[type: DType]() -> Scalar[type]: ) else: constrained[False, "+inf only support on floating point types"]() - - return 0 + return 0 # ===----------------------------------------------------------------------===# From 03126bc6ff51915f0deee6cc7f9cc06f5560afb5 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 14:18:06 -0400 Subject: [PATCH 0557/2019] [stdlib] Fix start/stop semantics in `List.index` The semantics of these should match the semantics of slice boundaries. This means that we shouldn't do any explicit bounds checking: if an equivalent slice yields an empty sublist, we should just fail with the same error as if the element was not found. This patch also removes the extra check for empty list in this method, allowing more convenient and python error propagation. MODULAR_ORIG_COMMIT_REV_ID: 8a695f69dbcfed8db699dec723effd26a407b435 --- stdlib/src/collections/list.mojo | 54 ++++++++++---------------- stdlib/test/collections/test_list.mojo | 54 ++++++++++---------------- 2 files changed, 42 insertions(+), 66 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index abbc0758a1..ba07c44736 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -479,11 +479,11 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn index[ C: ComparableCollectionElement ]( - self: List[C], value: C, start: Int = 0, end: Optional[Int] = None + self: List[C], value: C, start: Int = 0, stop: Optional[Int] = None ) raises -> Int: """ - Returns the index of the first occurrence of a value in a list, starting from the specified - index (default 0). Raises an Error if the value is not found. + Returns the index of the first occurrence of a value in a list + restricted by the range given the start and stop bounds. ```mojo var my_list = List[Int](1, 2, 3) @@ -493,45 +493,33 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): Args: self: The list to search in. value: The value to search for. - start: The starting index of the search (default 0). - end: The ending index of the search (default None, which means the end of the list). + start: The starting index of the search, treated as a slice index + (defaults to 0). + stop: The ending index of the search, treated as a slice index + (defaults to None, which means the end of the list). Parameters: - C: The type of the elements in the list. Must implement the `ComparableCollectionElement` trait. + C: The type of the elements in the list. Must implement the + `ComparableCollectionElement` trait. Returns: The index of the first occurrence of the value in the list. Raises: - ValueError If the value is not found in the list. - + ValueError: If the value is not found in the list. """ - var normalized_start = (self.size + start) if start < 0 else start - # TODO: Once the min() and max() functions are available in Mojo, - # TODO: we can simplify the entire if-else block into a single line using the ternary operator: - # var normalized_end = self.size if end is None else min(max(end, 0), self.size) - var normalized_end: Int - if end is None: - normalized_end = self.size - else: - if end.value()[] < 0: - normalized_end = self.size + end.value()[] + var normalized_start = max(self.size + start, 0) if start < 0 else start + + @parameter + fn normalized_stop() -> Int: + if stop is None: + return self.size + elif stop.value()[] < 0: + return min(stop.value()[] + self.size, self.size) else: - if end.value()[] > self.size: - normalized_end = self.size - else: - normalized_end = end.value()[] - - if not self.size: - raise "Cannot find index of a value in an empty list." - if normalized_start >= self.size: - raise "Given 'start' parameter (" + str( - normalized_start - ) + ") is out of range. List only has " + str( - self.size - ) + " elements." - - for i in range(normalized_start, normalized_end): + return stop.value()[] + + for i in range(normalized_start, normalized_stop()): if self[i] == value: return i raise "ValueError: Given element is not in list" diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index fe9b642c36..9b4ffda368 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -364,59 +364,47 @@ def test_list_index(): assert_equal(__type_of(test_list_a).index(test_list_a, 30, start=-4), 2) with assert_raises(contains="ValueError: Given element is not in list"): __type_of(test_list_a).index(test_list_a, 30, start=3) - with assert_raises( - contains=( - "Given 'start' parameter (5) is out of range. List only has 5" - " elements." - ) - ): + with assert_raises(contains="ValueError: Given element is not in list"): __type_of(test_list_a).index(test_list_a, 30, start=5) # Tests With Start and End Parameters assert_equal( - __type_of(test_list_a).index(test_list_a, 30, start=1, end=3), 2 + __type_of(test_list_a).index(test_list_a, 30, start=1, stop=3), 2 ) assert_equal( - __type_of(test_list_a).index(test_list_a, 30, start=-4, end=-2), 2 + __type_of(test_list_a).index(test_list_a, 30, start=-4, stop=-2), 2 ) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, start=1, end=2) + __type_of(test_list_a).index(test_list_a, 30, start=1, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, start=3, end=1) + __type_of(test_list_a).index(test_list_a, 30, start=3, stop=1) # Tests With End Parameter Only - assert_equal(__type_of(test_list_a).index(test_list_a, 30, end=3), 2) - assert_equal(__type_of(test_list_a).index(test_list_a, 30, end=-2), 2) + assert_equal(__type_of(test_list_a).index(test_list_a, 30, stop=3), 2) + assert_equal(__type_of(test_list_a).index(test_list_a, 30, stop=-2), 2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, end=1) + __type_of(test_list_a).index(test_list_a, 30, stop=1) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, end=2) + __type_of(test_list_a).index(test_list_a, 30, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 60, end=50) + __type_of(test_list_a).index(test_list_a, 60, stop=50) # Edge Cases and Special Conditions assert_equal( - __type_of(test_list_a).index(test_list_a, 10, start=-5, end=-1), 0 + __type_of(test_list_a).index(test_list_a, 10, start=-5, stop=-1), 0 ) assert_equal( - __type_of(test_list_a).index(test_list_a, 10, start=0, end=50), 0 + __type_of(test_list_a).index(test_list_a, 10, start=0, stop=50), 0 ) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 50, start=-5, end=-1) + __type_of(test_list_a).index(test_list_a, 50, start=-5, stop=-1) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 50, start=0, stop=-1) + with assert_raises(contains="ValueError: Given element is not in list"): + __type_of(test_list_a).index(test_list_a, 10, start=-4, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 50, start=0, end=-1) + __type_of(test_list_a).index(test_list_a, 10, start=5, stop=50) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 10, start=-4, end=-1) - with assert_raises( - contains=( - "Given 'start' parameter (5) is out of range. List only has 5" - " elements." - ) - ): - __type_of(test_list_a).index(test_list_a, 10, start=5, end=50) - with assert_raises( - contains="Cannot find index of a value in an empty list." - ): __type_of(List[Int]()).index(List[Int](), 10) var test_list_b = List[Int](10, 20, 30, 20, 10) @@ -430,16 +418,16 @@ def test_list_index(): # Test constraining search with start and end, excluding last occurrence with assert_raises(contains="ValueError: Given element is not in list"): - _ = __type_of(test_list_b).index(test_list_b, 10, start=1, end=4) + _ = __type_of(test_list_b).index(test_list_b, 10, start=1, stop=4) # Test search within a range that includes multiple occurrences assert_equal( - __type_of(test_list_b).index(test_list_b, 20, start=1, end=4), 1 + __type_of(test_list_b).index(test_list_b, 20, start=1, stop=4), 1 ) # Verify error when constrained range excludes occurrences with assert_raises(contains="ValueError: Given element is not in list"): - _ = __type_of(test_list_b).index(test_list_b, 20, start=4, end=5) + _ = __type_of(test_list_b).index(test_list_b, 20, start=4, stop=5) def test_list_extend(): From aa5865a3f7227c5f4c37e03fe688d43a8e616588 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Wed, 15 May 2024 11:26:34 -0700 Subject: [PATCH 0558/2019] [examples] Fix usage of `@unroll` decorator This fixes the example so that the `@unroll` decorator actually unrolls the loop. MODULAR_ORIG_COMMIT_REV_ID: 3c672dafe26698ce59c6d983b6e04186165a0186 --- examples/matmul.mojo | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 3b4142414b..0b60a48d40 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -162,8 +162,9 @@ fn matmul_unrolled(inout C: Matrix, A: Matrix, B: Matrix): fn calc_row(m: Int): @parameter fn calc_tile[tile_x: Int, tile_y: Int](x: Int, y: Int): - @unroll(tile_y) - for k in range(y, y + tile_y): + @unroll + for _k in range(tile_y): + var k = _k + y @parameter fn dot[nelts: Int](n: Int): From 97522448582b70eebfd0f224cf155547cb95da9c Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Wed, 15 May 2024 14:38:15 -0400 Subject: [PATCH 0559/2019] [stdlib] Add workaround for fpow on GPU targets LLVM is missing the fpow instruction in PTX and fails with the following while lowering fpow instructions: LLVM ERROR: Cannot select: t12: f32 = fpow t6, t11 This is a stop-gap until we get that implemented. MODULAR_ORIG_COMMIT_REV_ID: d17d0876f9397189973baa3fff79ccb639fa003a --- stdlib/src/builtin/simd.mojo | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7149a26e59..b74353d55a 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -28,7 +28,6 @@ from sys import ( from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from memory import bitcast - from utils._numerics import FPUtils from utils._numerics import isnan as _isnan from utils._numerics import nan as _nan @@ -2444,11 +2443,20 @@ fn _pow[ var result = SIMD[lhs_type, simd_width]() - @unroll - for i in range(simd_width): - result[i] = llvm_intrinsic[ - "llvm.pow", Scalar[lhs_type], has_side_effect=False - ](lhs[i], rhs[i]) + @parameter + if triple_is_nvidia_cuda(): + print( + "ABORT: pow with two floating point operands is not supported" + " on GPU" + ) + abort() + else: + + @unroll + for i in range(simd_width): + result[i] = llvm_intrinsic[ + "llvm.pow", Scalar[lhs_type], has_side_effect=False + ](lhs[i], rhs[i]) return result elif rhs_type.is_integral(): From 0904c0a1ba0072677f49f71315f93d5829fca877 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 14:47:33 -0400 Subject: [PATCH 0560/2019] [stdlib] Make naming consistent in the `bit` module The new names roughly follow the C++ `` header, to provide a familiar feel (since there is no precedent for these in Python). A few exceptions: we use `byte_reverse` instead of `byteswap` and `pop_count` instead of `popcount` to improve consistency with standard Pythonic naming schemes. MODULAR_ORIG_COMMIT_REV_ID: 67d4729b27d519a2b90878a740e001023754661d --- docs/changelog.md | 9 +++++++- stdlib/src/bit/__init__.mojo | 12 +++++----- stdlib/src/bit/bit.mojo | 40 +++++++++++++++++---------------- stdlib/src/builtin/sort.mojo | 4 ++-- stdlib/src/builtin/string.mojo | 6 ++--- stdlib/src/utils/stringref.mojo | 6 ++--- 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d64b3b24d3..c3d65c8de3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -207,7 +207,14 @@ what we publish. - `ListLiteral` and `Tuple` now only requires that element types be `Copyable`. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. -- The `math.bit` module has been moved to a new top-level `bit` module. +- The `math.bit` module has been moved to a new top-level `bit` module. The + following functions in this module have been renamed: + - `ctlz` -> `countl_zero` + - `cttz` -> `countr_zero` + - `bit_length` -> `bit_width` + - `ctpop` -> `pop_count` + - `bswap` -> `byte_reverse` + - `bitreverse` -> `bit_reverse` - The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been moved to the `bit` module. diff --git a/stdlib/src/bit/__init__.mojo b/stdlib/src/bit/__init__.mojo index dd51d43aaf..98c17fdf64 100644 --- a/stdlib/src/bit/__init__.mojo +++ b/stdlib/src/bit/__init__.mojo @@ -13,13 +13,13 @@ """Implements the bit package.""" from .bit import ( - ctlz, - cttz, - bitreverse, - bswap, - ctpop, + countl_zero, + countr_zero, + bit_reverse, + byte_reverse, + pop_count, bit_not, - bit_length, + bit_width, rotate_bits_left, rotate_bits_right, ) diff --git a/stdlib/src/bit/bit.mojo b/stdlib/src/bit/bit.mojo index 8aba1b76a9..adc685e517 100644 --- a/stdlib/src/bit/bit.mojo +++ b/stdlib/src/bit/bit.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `bit` package. For example: ```mojo -from bit import ctlz +from bit import countl_zero ``` """ @@ -23,12 +23,12 @@ from sys import llvm_intrinsic from sys.info import bitwidthof # ===----------------------------------------------------------------------===# -# ctlz +# countl_zero # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn ctlz(val: Int) -> Int: +fn countl_zero(val: Int) -> Int: """Counts the number of leading zeros of an integer. Args: @@ -41,7 +41,7 @@ fn ctlz(val: Int) -> Int: @always_inline("nodebug") -fn ctlz[ +fn countl_zero[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Counts the per-element number of leading zeros in a SIMD vector. @@ -67,12 +67,12 @@ fn ctlz[ # ===----------------------------------------------------------------------===# -# cttz +# countr_zero # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn cttz(val: Int) -> Int: +fn countr_zero(val: Int) -> Int: """Counts the number of trailing zeros for an integer. Args: @@ -85,7 +85,7 @@ fn cttz(val: Int) -> Int: @always_inline("nodebug") -fn cttz[ +fn countr_zero[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Counts the per-element number of trailing zeros in a SIMD vector. @@ -111,12 +111,12 @@ fn cttz[ # ===----------------------------------------------------------------------===# -# bitreverse +# bit_reverse # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn bitreverse[ +fn bit_reverse[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Element-wise reverses the bitpattern of an integral value. @@ -142,12 +142,12 @@ fn bitreverse[ # ===----------------------------------------------------------------------===# -# bswap +# byte_reverse # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn bswap[ +fn byte_reverse[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Byte-swaps a value. @@ -186,12 +186,12 @@ fn bswap[ # ===----------------------------------------------------------------------===# -# ctpop +# pop_count # ===----------------------------------------------------------------------===# @always_inline("nodebug") -fn ctpop[ +fn pop_count[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: """Counts the number of bits set in a value. @@ -246,16 +246,18 @@ fn bit_not[ return __mlir_op.`pop.xor`(val.value, neg_one.value) +# TODO: implement bit_ceil, bit_floor, has_single_bit, et al + # ===----------------------------------------------------------------------===# -# bit_length +# bit_width # ===----------------------------------------------------------------------===# @always_inline -fn bit_length[ +fn bit_width[ type: DType, simd_width: Int ](val: SIMD[type, simd_width]) -> SIMD[type, simd_width]: - """Computes the number of digits required to represent the integer. + """Computes the minimum number of digits required to represent the integer. Parameters: type: `dtype` used for the computation. @@ -279,10 +281,10 @@ fn bit_length[ @parameter if type.is_unsigned(): - return bitwidth - ctlz(val) + return bitwidth - countl_zero(val) else: - var leading_zero_pos = ctlz(val) - var leading_zero_neg = ctlz(bit_not(val)) + var leading_zero_pos = countl_zero(val) + var leading_zero_neg = countl_zero(bit_not(val)) var leading_zero = (val > 0).select(leading_zero_pos, leading_zero_neg) return bitwidth - leading_zero diff --git a/stdlib/src/builtin/sort.mojo b/stdlib/src/builtin/sort.mojo index bfe053479f..1c2aebf96e 100644 --- a/stdlib/src/builtin/sort.mojo +++ b/stdlib/src/builtin/sort.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from bit import ctlz +from bit import countl_zero from collections import List from memory import Pointer, UnsafePointer from sys import bitwidthof @@ -127,7 +127,7 @@ fn _partition[ fn _estimate_initial_height(size: Int) -> Int: # Compute the log2 of the size rounded upward. - var log2 = int((bitwidthof[DType.index]() - 1) ^ ctlz(size | 1)) + var log2 = int((bitwidthof[DType.index]() - 1) ^ countl_zero(size | 1)) return max(2, log2) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 8db69ce153..905ac95b12 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from bit import ctlz +from bit import countl_zero from collections import List, KeyElement from sys import llvm_intrinsic, bitwidthof @@ -54,7 +54,7 @@ fn ord(s: String) -> Int: if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(len(s) == 1, "input string length must be 1") return int(b1) - var num_bytes = ctlz(~b1) + var num_bytes = countl_zero(~b1) debug_assert(len(s) == int(num_bytes), "input string must be one character") var shift = int((6 * (num_bytes - 1))) var b1_mask = 0b11111111 >> (num_bytes + 1) @@ -1673,7 +1673,7 @@ fn _calc_initial_buffer_size_int32(n0: Int) -> Int: 42949672960, ) var n = UInt32(n0) - var log2 = int((bitwidthof[DType.uint32]() - 1) ^ ctlz(n | 1)) + var log2 = int((bitwidthof[DType.uint32]() - 1) ^ countl_zero(n | 1)) return (n0 + lookup_table[int(log2)]) >> 32 diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0aa758f85a..7af4252c52 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -13,7 +13,7 @@ """Implements the StringRef class. """ -from bit import cttz +from bit import countr_zero from builtin.dtype import _uint_type_of_width from builtin.string import _atol from memory import DTypePointer, UnsafePointer @@ -459,7 +459,7 @@ fn _memchr[ var bool_mask = source.load[width=bool_mask_width](i) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) if mask: - return source + i + cttz(mask) + return source + i + countr_zero(mask) for i in range(vectorized_end, len): if source[i] == char: @@ -492,7 +492,7 @@ fn _memmem[ var bool_mask = haystack.load[width=bool_mask_width](i) == first_needle var mask = bitcast[_uint_type_of_width[bool_mask_width]()](bool_mask) while mask: - var offset = i + cttz(mask) + var offset = i + countr_zero(mask) if memcmp(haystack + offset + 1, needle + 1, needle_len - 1) == 0: return haystack + offset mask = mask & (mask - 1) From 4278327a95452a87bc6e3ff32eaa886e1ea642fb Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 15 May 2024 16:14:47 -0500 Subject: [PATCH 0561/2019] [stdlib] feature: Add initial `StringSlice` type `StringSlice` is a non-owning view to UTF-8 encoded string data. `StringSlice` holds a lifetime tied to the underlying string slice, ensuring that the `StringSlice` cannot outlive the string data it points to. This makes `StringSlice` a much safer alternative to `StringRef`. MODULAR_ORIG_COMMIT_REV_ID: 78051b79f3daf522fa7dea87fa22958feb33d36e --- stdlib/src/builtin/string.mojo | 42 +++++++++++- stdlib/src/builtin/string_literal.mojo | 22 ++++++- stdlib/src/memory/unsafe.mojo | 7 +- stdlib/src/memory/unsafe_pointer.mojo | 7 +- stdlib/src/utils/__init__.mojo | 1 + stdlib/src/utils/string_slice.mojo | 81 ++++++++++++++++++++++++ stdlib/test/utils/test_string_slice.mojo | 14 ++++ 7 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 stdlib/src/utils/string_slice.mojo diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 905ac95b12..d9c5934c11 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -21,7 +21,7 @@ from sys import llvm_intrinsic, bitwidthof from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy -from utils import StringRef, StaticIntTuple, Span +from utils import StringRef, StaticIntTuple, Span, StringSlice from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf @@ -624,6 +624,29 @@ struct String( buffer[length] = 0 self._buffer = buffer^ + @always_inline + fn __init__(inout self, str_slice: StringSlice): + """Construct a string from a string slice. + + This will allocate a new string that copies the string contents from + the provided string slice `str_slice`. + + Args: + str_slice: The string slice from which to construct this string. + """ + + # Calculate length in bytes + var length = len(str_slice.as_bytes_slice()) + var buffer = Self._buffer_type() + buffer.resize(length + 1, 0) + memcpy( + DTypePointer(buffer.data), + DTypePointer(str_slice.as_bytes_slice().unsafe_ptr()), + length, + ) + buffer[length] = 0 + self._buffer = buffer^ + @always_inline fn __init__(inout self, literal: StringLiteral): """Constructs a String value given a constant string. @@ -1179,6 +1202,7 @@ struct String( return copy + @always_inline fn as_bytes_slice( self: Reference[Self, _, _] ) -> Span[Int8, self.is_mutable, self.lifetime]: @@ -1197,6 +1221,22 @@ struct String( len=self[]._byte_length(), ) + @always_inline + fn as_string_slice( + self: Reference[Self, _, _] + ) -> StringSlice[self.is_mutable, self.lifetime]: + """Returns a string slice of the data owned by this string. + + Returns: + A string slice pointing to the data owned by this string. + """ + var bytes = self[].as_bytes_slice() + + # FIXME(MSTDL-160): + # Enforce UTF-8 encoding in String so this is actually + # guaranteed to be valid. + return StringSlice(unsafe_from_utf8=bytes) + fn _byte_length(self) -> Int: """Get the string length in bytes. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 7add0faf4c..cde609618a 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -236,9 +236,27 @@ struct StringLiteral( """ return self.__str__().__repr__() + @always_inline + fn as_string_slice( + self: Reference[Self, _, _] + ) -> StringSlice[False, ImmStaticLifetime]: + """Returns a string slice of this static string literal. + + Returns: + A string slice pointing to this static string literal. + """ + + var bytes = self[].as_bytes_slice() + + # FIXME(MSTDL-160): + # Enforce UTF-8 encoding in StringLiteral so this is actually + # guaranteed to be valid. + return StringSlice(unsafe_from_utf8=bytes) + + @always_inline fn as_bytes_slice( self: Reference[Self, _, _] - ) -> Span[Int8, __mlir_attr.`0: i1`, ImmStaticLifetime]: + ) -> Span[Int8, False, ImmStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -248,7 +266,7 @@ struct StringLiteral( var ptr = rebind[UnsafePointer[Int8]](self[].unsafe_ptr()) - return Span[Int8, __mlir_attr.`0: i1`, ImmStaticLifetime]( + return Span[Int8, False, ImmStaticLifetime]( unsafe_ptr=ptr, len=self[]._byte_length(), ) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 163a40d667..04ff7d7efc 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -179,12 +179,7 @@ struct LegacyPointer[ var address: Self._mlir_type """The pointed-to address.""" - alias _ref_type = Reference[ - type, - __mlir_attr.`1: i1`, - MutStaticLifetime, - address_space, - ] + alias _ref_type = Reference[type, True, MutStaticLifetime, address_space] @always_inline("nodebug") fn __init__() -> Self: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 4ec0b36098..ce65ab59e7 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -48,12 +48,7 @@ struct UnsafePointer[ # We're unsafe, so we can have unsafe things. References we make have # an immortal mutable lifetime, since we can't come up with a meaningful # lifetime for them anyway. - alias _ref_type = Reference[ - T, - __mlir_attr.`1: i1`, - MutStaticLifetime, - address_space, - ] + alias _ref_type = Reference[T, True, MutStaticLifetime, address_space] """The underlying pointer type.""" var address: Self._mlir_type diff --git a/stdlib/src/utils/__init__.mojo b/stdlib/src/utils/__init__.mojo index 3ecb9619c0..6bbddb24eb 100644 --- a/stdlib/src/utils/__init__.mojo +++ b/stdlib/src/utils/__init__.mojo @@ -22,4 +22,5 @@ from .loop import unroll from .span import Span from .static_tuple import StaticTuple, InlineArray from .stringref import StringRef +from .string_slice import StringSlice from .variant import Variant diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo new file mode 100644 index 0000000000..27518d1792 --- /dev/null +++ b/stdlib/src/utils/string_slice.mojo @@ -0,0 +1,81 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +"""Implements the StringSlice type. + +You can import these APIs from the `utils.string_slice` module. For example: + +```mojo +from utils import StringSlice +``` +""" + +from utils import Span + + +struct StringSlice[ + is_mutable: Bool, + lifetime: AnyLifetime[is_mutable].type, +](Stringable): + """ + A non-owning view to encoded string data. + + TODO: + The underlying string data is guaranteed to be encoded using UTF-8. + + Parameters: + is_mutable: Whether the slice is mutable. + lifetime: The lifetime of the underlying string data. + """ + + var _slice: Span[Int8, is_mutable, lifetime] + + # ===------------------------------------------------------------------===# + # Initializers + # ===------------------------------------------------------------------===# + + fn __init__( + inout self, owned unsafe_from_utf8: Span[Int8, is_mutable, lifetime] + ): + """ + Construct a new StringSlice from a sequence of UTF-8 encoded bytes. + + Safety: + `unsafe_from_utf8` MUST be valid UTF-8 encoded data. + + Args: + unsafe_from_utf8: A slice of bytes encoded in UTF-8. + """ + + self._slice = unsafe_from_utf8^ + + # ===------------------------------------------------------------------===# + # Trait implementations + # ===------------------------------------------------------------------===# + + fn __str__(self) -> String: + return String(str_slice=self) + + # ===------------------------------------------------------------------===# + # Methods + # ===------------------------------------------------------------------===# + + @always_inline + fn as_bytes_slice(self) -> Span[Int8, is_mutable, lifetime]: + """ + Get the sequence of encoded bytes as a slice of the underlying string. + + Returns: + A slice containing the underlying sequence of encoded bytes. + """ + return self._slice diff --git a/stdlib/test/utils/test_string_slice.mojo b/stdlib/test/utils/test_string_slice.mojo index d24b453cf1..463c2119a2 100644 --- a/stdlib/test/utils/test_string_slice.mojo +++ b/stdlib/test/utils/test_string_slice.mojo @@ -113,6 +113,20 @@ fn test_string_byte_slice() raises: # ) +fn test_heap_string_from_string_slice() raises: + var string_lit: StringLiteral = "Hello" + + var static_str: StringSlice[ + False, ImmStaticLifetime + ] = string_lit.as_string_slice() + + var heap_string = String(static_str) + + assert_equal(heap_string, "Hello") + + fn main() raises: test_string_literal_byte_slice() test_string_byte_slice() + + test_heap_string_from_string_slice() From e6269b45aaa64025585a25c160b1ed882b9da6d3 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 17:18:27 -0400 Subject: [PATCH 0562/2019] [stdlib] Remove `@staticmethod` from `List.index` and document it Following @rd4com's suggestion in https://github.com/modularml/mojo/issues/2658, this method can take a reference type as self, allowing the callsite to use a natural syntax. This patch also adds a changelog entry for `List.index`. MODULAR_ORIG_COMMIT_REV_ID: 291dd5f544d7d5b463188e7c986b2ee3c8416662 --- docs/changelog.md | 8 +++ stdlib/src/collections/list.mojo | 23 +++++---- stdlib/test/collections/test_list.mojo | 70 +++++++++++--------------- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index c3d65c8de3..fe02954942 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -178,6 +178,14 @@ what we publish. - `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) +- `List` now has an `index` method that allows one to find the (first) location + of an element in a `List` of `EqualityComparable` types. For example: + + ```mojo + var my_list = List[Int](2, 3, 5, 7, 3) + print(my_list.index(3)) # prints 1 + ``` + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index ba07c44736..a6b347020b 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -474,12 +474,14 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): earlier_idx += 1 later_idx -= 1 - # TODO: Modify this to be regular method when issue 1876 is resolved - @staticmethod + # TODO: Remove explicit self type when issue 1876 is resolved. fn index[ C: ComparableCollectionElement ]( - self: List[C], value: C, start: Int = 0, stop: Optional[Int] = None + self: Reference[List[C]], + value: C, + start: Int = 0, + stop: Optional[Int] = None, ) raises -> Int: """ Returns the index of the first occurrence of a value in a list @@ -487,11 +489,10 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): ```mojo var my_list = List[Int](1, 2, 3) - print(__type_of(my_list).index(my_list, 2)) # Output: 1 + print(my_list.index(2)) # prints `1` ``` Args: - self: The list to search in. value: The value to search for. start: The starting index of the search, treated as a slice index (defaults to 0). @@ -508,19 +509,19 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): Raises: ValueError: If the value is not found in the list. """ - var normalized_start = max(self.size + start, 0) if start < 0 else start + var size = self[].size + var normalized_start = max(size + start, 0) if start < 0 else start @parameter fn normalized_stop() -> Int: if stop is None: - return self.size - elif stop.value()[] < 0: - return min(stop.value()[] + self.size, self.size) + return size else: - return stop.value()[] + var end = stop.value()[] + return end if end > 0 else min(end + size, size) for i in range(normalized_start, normalized_stop()): - if self[i] == value: + if self[][i] == value: return i raise "ValueError: Given element is not in list" diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 9b4ffda368..e2d48462dd 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -353,81 +353,71 @@ def test_list_index(): var test_list_a = List[Int](10, 20, 30, 40, 50) # Basic Functionality Tests - assert_equal(__type_of(test_list_a).index(test_list_a, 10), 0) - assert_equal(__type_of(test_list_a).index(test_list_a, 30), 2) - assert_equal(__type_of(test_list_a).index(test_list_a, 50), 4) + assert_equal(test_list_a.index(10), 0) + assert_equal(test_list_a.index(30), 2) + assert_equal(test_list_a.index(50), 4) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 60) + test_list_a.index(60) # Tests With Start Parameter - assert_equal(__type_of(test_list_a).index(test_list_a, 30, start=1), 2) - assert_equal(__type_of(test_list_a).index(test_list_a, 30, start=-4), 2) + assert_equal(test_list_a.index(30, start=1), 2) + assert_equal(test_list_a.index(30, start=-4), 2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, start=3) + test_list_a.index(30, start=3) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, start=5) + test_list_a.index(30, start=5) # Tests With Start and End Parameters - assert_equal( - __type_of(test_list_a).index(test_list_a, 30, start=1, stop=3), 2 - ) - assert_equal( - __type_of(test_list_a).index(test_list_a, 30, start=-4, stop=-2), 2 - ) + assert_equal(test_list_a.index(30, start=1, stop=3), 2) + assert_equal(test_list_a.index(30, start=-4, stop=-2), 2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, start=1, stop=2) + test_list_a.index(30, start=1, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, start=3, stop=1) + test_list_a.index(30, start=3, stop=1) # Tests With End Parameter Only - assert_equal(__type_of(test_list_a).index(test_list_a, 30, stop=3), 2) - assert_equal(__type_of(test_list_a).index(test_list_a, 30, stop=-2), 2) + assert_equal(test_list_a.index(30, stop=3), 2) + assert_equal(test_list_a.index(30, stop=-2), 2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, stop=1) + test_list_a.index(30, stop=1) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 30, stop=2) + test_list_a.index(30, stop=2) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 60, stop=50) + test_list_a.index(60, stop=50) # Edge Cases and Special Conditions - assert_equal( - __type_of(test_list_a).index(test_list_a, 10, start=-5, stop=-1), 0 - ) - assert_equal( - __type_of(test_list_a).index(test_list_a, 10, start=0, stop=50), 0 - ) + assert_equal(test_list_a.index(10, start=-5, stop=-1), 0) + assert_equal(test_list_a.index(10, start=0, stop=50), 0) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 50, start=-5, stop=-1) + test_list_a.index(50, start=-5, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 50, start=0, stop=-1) + test_list_a.index(50, start=0, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 10, start=-4, stop=-1) + test_list_a.index(10, start=-4, stop=-1) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(test_list_a).index(test_list_a, 10, start=5, stop=50) + test_list_a.index(10, start=5, stop=50) with assert_raises(contains="ValueError: Given element is not in list"): - __type_of(List[Int]()).index(List[Int](), 10) + List[Int]().index(10) var test_list_b = List[Int](10, 20, 30, 20, 10) # Test finding the first occurrence of an item - assert_equal(__type_of(test_list_b).index(test_list_b, 10), 0) - assert_equal(__type_of(test_list_b).index(test_list_b, 20), 1) + assert_equal(test_list_b.index(10), 0) + assert_equal(test_list_b.index(20), 1) # Test skipping the first occurrence with a start parameter - assert_equal(__type_of(test_list_b).index(test_list_b, 20, start=2), 3) + assert_equal(test_list_b.index(20, start=2), 3) # Test constraining search with start and end, excluding last occurrence with assert_raises(contains="ValueError: Given element is not in list"): - _ = __type_of(test_list_b).index(test_list_b, 10, start=1, stop=4) + _ = test_list_b.index(10, start=1, stop=4) # Test search within a range that includes multiple occurrences - assert_equal( - __type_of(test_list_b).index(test_list_b, 20, start=1, stop=4), 1 - ) + assert_equal(test_list_b.index(20, start=1, stop=4), 1) # Verify error when constrained range excludes occurrences with assert_raises(contains="ValueError: Given element is not in list"): - _ = __type_of(test_list_b).index(test_list_b, 20, start=4, stop=5) + _ = test_list_b.index(20, start=4, stop=5) def test_list_extend(): From b54af3b0c807ef7a6b8076a221015cd84d15e75b Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 17:42:14 -0400 Subject: [PATCH 0563/2019] [stdlib] Move numeric limit helpers from `simd` to `utils._numerics` This is where the rest of the numeric limit helpers live already, and this allows us to remove some duplicate code. No public API is changed. MODULAR_ORIG_COMMIT_REV_ID: 7d65658c9e9baf8c848474bfb8570883fc694413 --- stdlib/src/builtin/simd.mojo | 222 +++---------------------------- stdlib/src/utils/_numerics.mojo | 227 ++++++++++++++++++++++++++++---- 2 files changed, 225 insertions(+), 224 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index b74353d55a..7be5dbb746 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -28,9 +28,16 @@ from sys import ( from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from memory import bitcast -from utils._numerics import FPUtils -from utils._numerics import isnan as _isnan -from utils._numerics import nan as _nan + +from utils._numerics import ( + FPUtils, + isnan as _isnan, + nan as _nan, + max_finite as _max_finite, + min_finite as _min_finite, + max_or_inf as _max_or_inf, + min_or_neg_inf as _min_or_neg_inf, +) from utils._visualizers import lldb_formatter_wrapping_type from utils.inlined_string import _ArrayMem @@ -151,11 +158,11 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var value: __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] """The underlying storage for the vector.""" - alias MAX = Self(_inf[type]()) - """Gets a +inf value for the SIMD value.""" + alias MAX = Self(_max_or_inf[type]()) + """Gets the maximum value for the SIMD value, potentially +inf.""" - alias MIN = Self(_neginf[type]()) - """Gets a -inf value for the SIMD value.""" + alias MIN = Self(_min_or_neg_inf[type]()) + """Gets the minimum value for the SIMD value, potentially -inf.""" alias MAX_FINITE = Self(_max_finite[type]()) """Returns the maximum finite value of SIMD value.""" @@ -2500,9 +2507,9 @@ fn _powi[type: DType](lhs: Scalar[type], rhs: Int32) -> __type_of(lhs): return res -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # bfloat16 -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # alias _fp32_bf16_mantissa_diff = FPUtils[ DType.float32 @@ -2591,198 +2598,9 @@ fn _f32_to_bfloat16[ return _simd_apply[wrapper_fn, DType.bfloat16, size](val) -# ===----------------------------------------------------------------------===# -# Limits -# ===----------------------------------------------------------------------===# - - -# ===----------------------------------------------------------------------===# -# inf -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn _inf[type: DType]() -> Scalar[type]: - """Gets a +inf value for the given dtype. - - Constraints: - Can only be used for FP dtypes. - - Parameters: - type: The value dtype. - - Returns: - The +inf value of the given dtype. - """ - - @parameter - if type == DType.float16: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - elif type == DType.bfloat16: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - elif type == DType.float32: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - elif type == DType.float64: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"inf"> : !pop.scalar`], - ]() - ) - return _max_finite[type]() - - -# ===----------------------------------------------------------------------===# -# neginf -# ===----------------------------------------------------------------------===# - - -@always_inline("nodebug") -fn _neginf[type: DType]() -> Scalar[type]: - """Gets a -inf value for the given dtype. - - Constraints: - Can only be used for FP dtypes. - - Parameters: - type: The value dtype. - - Returns: - The -inf value of the given dtype. - """ - - @parameter - if type == DType.float16: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], - ]() - ) - elif type == DType.bfloat16: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], - ]() - ) - elif type == DType.float32: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], - ]() - ) - elif type == DType.float64: - return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], - ]() - ) - return _min_finite[type]() - - -# ===----------------------------------------------------------------------===# -# max_finite -# ===----------------------------------------------------------------------===# - - -@always_inline -fn _max_finite[type: DType]() -> Scalar[type]: - """Returns the maximum finite value of type. - - Parameters: - type: The value dtype. - - Returns: - The maximum representable value of the type. Does not include infinity for - floating-point types. - """ - - @parameter - if type == DType.int8: - return 127 - elif type == DType.uint8: - return 255 - elif type == DType.int16: - return 32767 - elif type == DType.uint16: - return 65535 - elif type == DType.int32 or type.is_index32(): - return 2147483647 - elif type == DType.uint32: - return 4294967295 - elif type == DType.int64 or type.is_index64(): - return 9223372036854775807 - elif type == DType.uint64: - return 18446744073709551615 - elif type == DType.float16: - return 65504 - elif type == DType.bfloat16: - return 3.38953139e38 - elif type == DType.float32: - return 3.40282346638528859812e38 - elif type == DType.float64: - return 1.79769313486231570815e308 - else: - constrained[False, "max_finite() called on unsupported type"]() - return 0 - - -# ===----------------------------------------------------------------------===# -# min_finite -# ===----------------------------------------------------------------------===# - - -@always_inline -fn _min_finite[type: DType]() -> Scalar[type]: - """Returns the minimum (lowest) finite value of type. - - Parameters: - type: The value dtype. - - Returns: - The minimum representable value of the type. Does not include negative - infinity for floating-point types. - """ - - @parameter - if type.is_unsigned(): - return 0 - elif type == DType.int8: - return -128 - elif type == DType.int16: - return -32768 - elif type == DType.int32 or type.is_index32(): - return -2147483648 - elif type == DType.int64 or type.is_index64(): - return -9223372036854775808 - elif type.is_floating_point(): - return -_max_finite[type]() - else: - constrained[False, "min_finite() called on unsupported type"]() - return 0 - - -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _simd_apply -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline @@ -2852,9 +2670,9 @@ fn _simd_apply[ return result -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _format_scalar -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index b36090216e..5e2f70b37f 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -25,9 +25,9 @@ from sys._assembly import inlined_assembly from builtin.dtype import _integral_type_of from memory import UnsafePointer, bitcast -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _digits -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -72,9 +72,9 @@ fn _digits[type: DType]() -> IntLiteral: return -1 -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _fp_bitcast_to_integer -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline @@ -94,9 +94,9 @@ fn _fp_bitcast_to_integer[type: DType](value: Scalar[type]) -> Int: return int(bitcast[integer_type, 1](value)) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # _fp_bitcast_from_integer -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline @@ -117,9 +117,9 @@ fn _fp_bitcast_from_integer[type: DType](value: Int) -> Scalar[type]: return bitcast[type, 1](int_val) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # FPUtils -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # struct FPUtils[type: DType]: @@ -447,9 +447,9 @@ struct FPUtils[type: DType]: return res -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # FlushDenormals -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # struct FlushDenormals: @@ -548,9 +548,9 @@ struct FlushDenormals: return fpcr64.cast[DType.int32]() -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # nan -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -601,9 +601,9 @@ fn nan[type: DType]() -> Scalar[type]: return 0 -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # isnan -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -642,9 +642,9 @@ fn isnan[ ](val.value, (signaling_nan_test | quiet_nan_test).value) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # inf -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -695,9 +695,192 @@ fn inf[type: DType]() -> Scalar[type]: return 0 -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # +# neg_inf +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn neg_inf[type: DType]() -> Scalar[type]: + """Gets a -inf value for the given dtype. + + Constraints: + Can only be used for FP dtypes. + + Parameters: + type: The value dtype. + + Returns: + The -inf value of the given dtype. + """ + + @parameter + if type == DType.float16: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], + ]() + ) + elif type == DType.bfloat16: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], + ]() + ) + elif type == DType.float32: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], + ]() + ) + elif type == DType.float64: + return rebind[__mlir_type[`!pop.scalar<`, type.value, `>`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[`#pop.simd<"-inf"> : !pop.scalar`], + ]() + ) + else: + constrained[False, "+inf only support on floating point types"]() + return 0 + + +# ===----------------------------------------------------------------------=== # +# max_finite +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn max_finite[type: DType]() -> Scalar[type]: + """Returns the maximum finite value of type. + + Parameters: + type: The value dtype. + + Returns: + The maximum representable value of the type. Does not include infinity + for floating-point types. + """ + + @parameter + if type == DType.int8: + return 127 + elif type == DType.uint8: + return 255 + elif type == DType.int16: + return 32767 + elif type == DType.uint16: + return 65535 + elif type == DType.int32 or type.is_index32(): + return 2147483647 + elif type == DType.uint32: + return 4294967295 + elif type == DType.int64 or type.is_index64(): + return 9223372036854775807 + elif type == DType.uint64: + return 18446744073709551615 + elif type == DType.float16: + return 65504 + elif type == DType.bfloat16: + return 3.38953139e38 + elif type == DType.float32: + return 3.40282346638528859812e38 + elif type == DType.float64: + return 1.79769313486231570815e308 + else: + constrained[False, "max_finite() called on unsupported type"]() + return 0 + + +# ===----------------------------------------------------------------------=== # +# min_finite +# ===----------------------------------------------------------------------=== # + + +@always_inline +fn min_finite[type: DType]() -> Scalar[type]: + """Returns the minimum (lowest) finite value of type. + + Parameters: + type: The value dtype. + + Returns: + The minimum representable value of the type. Does not include negative + infinity for floating-point types. + """ + + @parameter + if type.is_unsigned(): + return 0 + elif type == DType.int8: + return -128 + elif type == DType.int16: + return -32768 + elif type == DType.int32 or type.is_index32(): + return -2147483648 + elif type == DType.int64 or type.is_index64(): + return -9223372036854775808 + elif type.is_floating_point(): + return -max_finite[type]() + else: + constrained[False, "min_finite() called on unsupported type"]() + return 0 + + +# ===----------------------------------------------------------------------=== # +# max_or_inf +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn max_or_inf[type: DType]() -> Scalar[type]: + """Returns the maximum (potentially infinite) value of type. + + Parameters: + type: The value dtype. + + Returns: + The maximum representable value of the type. Can include infinity for + floating-point types. + """ + + @parameter + if type.is_floating_point(): + return inf[type]() + else: + return max_finite[type]() + + +# ===----------------------------------------------------------------------=== # +# min_or_neg_inf +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn min_or_neg_inf[type: DType]() -> Scalar[type]: + """Returns the minimum (potentially negative infinite) value of type. + + Parameters: + type: The value dtype. + + Returns: + The minimum representable value of the type. Can include negative + infinity for floating-point types. + """ + + @parameter + if type.is_floating_point(): + return neg_inf[type]() + else: + return min_finite[type]() + + +# ===----------------------------------------------------------------------=== # # isinf -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -730,9 +913,9 @@ fn isinf[ ) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # isfinite -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline("nodebug") @@ -763,9 +946,9 @@ fn isfinite[ ) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # get_accum_type -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline From 5cd41c7279052369edb59eb1384cef114910994c Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 15 May 2024 15:14:26 -0700 Subject: [PATCH 0564/2019] [stdlib] Replace uses of StaticTuple with InlineArray in inlined_string.mojo MODULAR_ORIG_COMMIT_REV_ID: 05893c6ff664877841e54bf97516e23e9a4f620b --- stdlib/src/utils/inlined_string.mojo | 8 ++++---- stdlib/test/utils/test_tuple.mojo | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index d14f85642b..ca29f034d6 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -21,7 +21,7 @@ from memory import memcpy, LegacyPointer, UnsafePointer from collections import Optional -from utils import StaticTuple, Variant +from utils import InlineArray, Variant from utils._format import ToFormatter @@ -495,7 +495,7 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): SIZE: The fixed number of elements stored in the array. """ - var storage: StaticTuple[ElementType, SIZE] + var storage: InlineArray[ElementType, SIZE] """The underlying storage for this array value.""" # ===------------------------------------------------------------------===# @@ -506,7 +506,7 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): fn __init__(inout self): """Constructs an empty (undefined) array.""" - self.storage = StaticTuple[ElementType, SIZE]() + self.storage = InlineArray[ElementType, SIZE](unsafe_uninitialized=True) # ===------------------------------------------------------------------=== # # Trait Interfaces @@ -522,7 +522,7 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): fn __setitem__(inout self, index: Int, owned value: ElementType): var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer(Reference(self.storage.array)).address, index.value + UnsafePointer(Reference(self.storage._array)).address, index.value ) __mlir_op.`pop.store`(value, ptr) diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 327f23450d..6eedeffb83 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -118,7 +118,10 @@ def test_array_int(): assert_equal(arr2[2], 5) var arr3 = InlineArray[Int, 1](5) - assert_equal(arr2[0], 5) + assert_equal(arr3[0], 5) + + var arr4 = InlineArray[UInt8, 1](42) + assert_equal(arr4[0], 42) def test_array_str(): From 2109bd8d76b9ce25651f7457ddcccd3b6180eb60 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 15 May 2024 15:43:00 -0700 Subject: [PATCH 0565/2019] [CI] Disable test_reversed (#40010) This test crashes ~5% of the time on CI MODULAR_ORIG_COMMIT_REV_ID: 552eed788e569b1e29030416d893f28da25701b4 --- stdlib/test/builtin/test_reversed.mojo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index 1c08dc2ac0..da9de000cf 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -10,6 +10,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +# REQUIRES: disabled +# https://github.com/modularml/mojo/issues/2369 # RUN: %mojo %s from testing import assert_equal From ebde5da0370eb49185d134685e9f8a8e8bb1dbf2 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 18:51:52 -0400 Subject: [PATCH 0566/2019] [stdlib] Expose implementation of `mat.ulp` and `math.nextafter` in `utils._numerics` This allows the stdlib to start using these. The `math` module just re-exports these. MODULAR_ORIG_COMMIT_REV_ID: d37c1efeccac65420f86d076ecf18e6b83b8a39b --- stdlib/src/utils/_numerics.mojo | 99 ++++++++++++++++++++++++++++ stdlib/test/utils/test_numerics.mojo | 31 ++++++--- 2 files changed, 120 insertions(+), 10 deletions(-) diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index 5e2f70b37f..11bfe6c0a8 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -20,9 +20,11 @@ from utils._numerics import FPUtils """ from sys import llvm_intrinsic, bitwidthof, has_neon, has_sse4 +from sys.ffi import _external_call_const from sys._assembly import inlined_assembly from builtin.dtype import _integral_type_of +from builtin.simd import _simd_apply from memory import UnsafePointer, bitcast # ===----------------------------------------------------------------------=== # @@ -968,3 +970,100 @@ fn get_accum_type[type: DType]() -> DType: """ return DType.float32 if type.is_half_float() else type + + +# ===----------------------------------------------------------------------=== # +# nextafter +# ===----------------------------------------------------------------------=== # + + +fn nextafter[ + type: DType, simd_width: Int +](arg0: SIMD[type, simd_width], arg1: SIMD[type, simd_width]) -> SIMD[ + type, simd_width +]: + """Computes next representable value of `arg0` in the direction of `arg1`. + + Constraints: + The element type of the input must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + arg0: The first input argument. + arg1: The second input argument. + + Returns: + The `nextafter` of the inputs. + """ + + @always_inline("nodebug") + @parameter + fn _float32_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["nextafterf", SIMD[result_type, 1]]( + arg0, arg1 + ) + + @always_inline("nodebug") + @parameter + fn _float64_dispatch[ + lhs_type: DType, rhs_type: DType, result_type: DType + ](arg0: SIMD[lhs_type, 1], arg1: SIMD[rhs_type, 1]) -> SIMD[result_type, 1]: + return _external_call_const["nextafter", SIMD[result_type, 1]]( + arg0, arg1 + ) + + constrained[type.is_floating_point(), "input type must be floating point"]() + + @parameter + if type == DType.float64: + return _simd_apply[_float64_dispatch, type, simd_width](arg0, arg1) + return _simd_apply[_float32_dispatch, type, simd_width](arg0, arg1) + + +# ===----------------------------------------------------------------------=== # +# ulp +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn ulp[ + type: DType, simd_width: Int +](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]: + """Computes the ULP (units of last place) or (units of least precision) of + the number. + + Constraints: + The element type of the inpiut must be a floating-point type. + + Parameters: + type: The `dtype` of the input and output SIMD vector. + simd_width: The width of the input and output SIMD vector. + + Args: + x: SIMD vector input. + + Returns: + The ULP of x. + """ + + constrained[type.is_floating_point(), "the type must be floating point"]() + + var nan_mask = isnan(x) + var xabs = abs(x) + var inf_mask = isinf(xabs) + alias inf_val = SIMD[type, simd_width](inf[type]()) + var x2 = nextafter(xabs, inf_val) + var x2_inf_mask = isinf(x2) + + return nan_mask.select( + x, + inf_mask.select( + xabs, + x2_inf_mask.select(xabs - nextafter(xabs, -inf_val), x2 - xabs), + ), + ) diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index f5dc97f9d6..be47361be0 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -13,20 +13,19 @@ # RUN: %mojo %s from sys.info import has_neon -from testing import assert_equal, assert_true, assert_false -from utils._numerics import FPUtils, inf, isinf +from testing import assert_equal, assert_true, assert_false, assert_almost_equal +from utils._numerics import FPUtils, inf, isinf, isnan, max_finite, nan, ulp -alias FPU64 = FPUtils[DType.float64] - -fn test_numerics() raises: +# TODO: improve coverage and organization of these tests +def test_FPUtils(): assert_equal(FPUtils[DType.float32].mantissa_width(), 23) - - assert_equal(FPUtils[DType.float64].mantissa_width(), 52) - assert_equal(FPUtils[DType.float32].exponent_bias(), 127) - assert_equal(FPUtils[DType.float64].exponent_bias(), 1023) + alias FPU64 = FPUtils[DType.float64] + + assert_equal(FPU64.mantissa_width(), 52) + assert_equal(FPU64.exponent_bias(), 1023) assert_equal(FPU64.get_exponent(FPU64.set_exponent(1, 2)), 2) assert_equal(FPU64.get_mantissa(FPU64.set_mantissa(1, 3)), 3) @@ -63,6 +62,18 @@ fn test_inf() raises: _test_inf[DType.float64]() +def test_ulp(): + assert_true(isnan(ulp(nan[DType.float32]()))) + assert_true(isinf(ulp(inf[DType.float32]()))) + assert_true(isinf(ulp(-inf[DType.float32]()))) + assert_almost_equal(ulp(Float64(0)), 5e-324) + assert_equal(ulp(max_finite[DType.float64]()), 1.99584030953472e292) + assert_equal(ulp(Float64(5)), 8.881784197001252e-16) + assert_equal(ulp(Float64(-5)), 8.881784197001252e-16) + + def main(): - test_numerics() + test_FPUtils() test_inf() + # TODO: test nextafter + test_ulp() From b4b7474e0b30e30ab78e2bbcfa4b20647a693936 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 20:22:34 -0400 Subject: [PATCH 0567/2019] [stdlib] Make the `utils._numerics` module public By moving it to `utils.numerics`. The public functions in this method were already widely used, so there is no reason to keep this module private. MODULAR_ORIG_COMMIT_REV_ID: ce45a56c7fa2059f2e0ed088ed5fcf6da3c0f3c1 --- docs/changelog.md | 5 +++++ stdlib/src/builtin/simd.mojo | 2 +- stdlib/src/testing/testing.mojo | 2 +- stdlib/src/utils/{_numerics.mojo => numerics.mojo} | 2 +- stdlib/test/memory/test_memory.mojo | 2 +- stdlib/test/testing/test_assertion.mojo | 2 +- stdlib/test/utils/test_numerics.mojo | 2 +- 7 files changed, 11 insertions(+), 6 deletions(-) rename stdlib/src/utils/{_numerics.mojo => numerics.mojo} (99%) diff --git a/docs/changelog.md b/docs/changelog.md index fe02954942..2acf76f8ed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -227,6 +227,11 @@ what we publish. - The `math.rotate_bits_left` and `math.rotate_bits_right` functions have been moved to the `bit` module. +- The implementation of the following functions have been moved from the `math` + module to the new `utils.numerics` module: `isfinite`, `isinf`, `isnan`, + `nan`, `nextafter`, and `ulp`. The functions continue to be exposed in the + `math` module. + ### ❌ Removed - The method `object.print()` has been removed. Since now, `object` has the diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7be5dbb746..f71bf1a3ca 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -29,7 +29,7 @@ from builtin._math import Ceilable, CeilDivable, Floorable, Truncable from builtin.hash import _hash_simd from memory import bitcast -from utils._numerics import ( +from utils.numerics import ( FPUtils, isnan as _isnan, nan as _nan, diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index b2b03a8c0a..57071fb011 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,7 +19,7 @@ from testing import assert_true ``` """ from collections import Optional -from utils._numerics import isfinite, isnan +from utils.numerics import isfinite, isnan from builtin._location import __call_location, _SourceLocation # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/numerics.mojo similarity index 99% rename from stdlib/src/utils/_numerics.mojo rename to stdlib/src/utils/numerics.mojo index 11bfe6c0a8..de29497604 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/numerics.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `utils` package. For example: ```mojo -from utils._numerics import FPUtils +from utils.numerics import FPUtils ``` """ diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index d42488a471..c65a75a805 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -15,7 +15,7 @@ from sys import sizeof from memory import memcmp, memcpy, memset, memset_zero, DTypePointer, Pointer -from utils._numerics import nan +from utils.numerics import nan from testing import ( assert_almost_equal, assert_equal, diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index e9e8737c43..0fdc41aa4f 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -20,7 +20,7 @@ from testing import ( assert_raises, assert_true, ) -from utils._numerics import inf, nan +from utils.numerics import inf, nan @value diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index be47361be0..556abc8401 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -14,7 +14,7 @@ from sys.info import has_neon from testing import assert_equal, assert_true, assert_false, assert_almost_equal -from utils._numerics import FPUtils, inf, isinf, isnan, max_finite, nan, ulp +from utils.numerics import FPUtils, inf, isinf, isnan, max_finite, nan, ulp # TODO: improve coverage and organization of these tests From 96e968258518710b62aed1b7e7b31e11caefda67 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 20:31:55 -0400 Subject: [PATCH 0568/2019] [stdlib] Move `abs`, `divmod`, and `round` to `builtin.math` Since these are implicitly imported regardless of their location, we might as well store them in one place. MODULAR_ORIG_COMMIT_REV_ID: 64ade523db6502125fc55ba01468a4b8ed3db9da --- docs/changelog.md | 4 +- stdlib/src/builtin/abs.mojo | 89 ---------------- stdlib/src/builtin/divmod.mojo | 31 ------ stdlib/src/builtin/math.mojo | 164 +++++++++++++++++++++++++++-- stdlib/src/builtin/round.mojo | 58 ---------- stdlib/test/builtin/test_math.mojo | 3 + 6 files changed, 161 insertions(+), 188 deletions(-) delete mode 100644 stdlib/src/builtin/abs.mojo delete mode 100644 stdlib/src/builtin/divmod.mojo delete mode 100644 stdlib/src/builtin/round.mojo diff --git a/docs/changelog.md b/docs/changelog.md index 2acf76f8ed..8bbebb6e30 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -67,8 +67,8 @@ what we publish. return Self(round(self.re), round(self.im)) ``` -- The `abs, round, min, max, and divmod` functions have moved from `math` to - `builtin`, so you no longer need to do +- The `abs`, `round`, `min`, `max`, and `divmod` functions have moved from + `math` to `builtin`, so you no longer need to do `from math import abs, round, min, max, divmod`. - Mojo now allows types to opt in to use the `floor()`, `ceil()`, and `trunc()` diff --git a/stdlib/src/builtin/abs.mojo b/stdlib/src/builtin/abs.mojo deleted file mode 100644 index e6e8030b81..0000000000 --- a/stdlib/src/builtin/abs.mojo +++ /dev/null @@ -1,89 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -"""Provides the `abs` function. - -These are Mojo built-ins, so you don't need to import them. -""" - - -trait Absable: - """ - The `Absable` trait describes a type that defines an absolute value - operation. - - Types that conform to `Absable` will work with the builtin `abs` function. - The absolute value operation always returns the same type as the input. - - For example: - ```mojo - struct Point(Absable): - var x: Float64 - var y: Float64 - - fn __abs__(self) -> Self: - return sqrt(self.x * self.x + self.y * self.y) - ``` - """ - - # TODO(MOCO-333): Reconsider the signature when we have parametric traits or - # associated types. - fn __abs__(self) -> Self: - ... - - -@always_inline -fn abs[T: Absable](value: T) -> T: - """Get the absolute value of the given object. - - Parameters: - T: The type conforming to Absable. - - Args: - value: The object to get the absolute value of. - - Returns: - The absolute value of the object. - """ - return value.__abs__() - - -# TODO: https://github.com/modularml/modular/issues/38694 -# TODO: https://github.com/modularml/modular/issues/38695 -# TODO: Remove this -@always_inline -fn abs(value: IntLiteral) -> IntLiteral: - """Get the absolute value of the given IntLiteral. - - Args: - value: The IntLiteral to get the absolute value of. - - Returns: - The absolute value of the IntLiteral. - """ - return value.__abs__() - - -# TODO: https://github.com/modularml/modular/issues/38694 -# TODO: https://github.com/modularml/modular/issues/38695 -# TODO: Remove this -@always_inline -fn abs(value: FloatLiteral) -> FloatLiteral: - """Get the absolute value of the given FloatLiteral. - - Args: - value: The FloatLiteral to get the absolute value of. - - Returns: - The absolute value of the FloatLiteral. - """ - return value.__abs__() diff --git a/stdlib/src/builtin/divmod.mojo b/stdlib/src/builtin/divmod.mojo deleted file mode 100644 index 66e28f7ca9..0000000000 --- a/stdlib/src/builtin/divmod.mojo +++ /dev/null @@ -1,31 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # - - -fn divmod(numerator: Int, denominator: Int) -> Tuple[Int, Int]: - """Performs integer division and returns the quotient and the remainder. - - Currently supported only for integers. Support for more standard library - types like Int8, Int16... is planned. - - This method calls `a.__divmod__(b)`, thus, the actual implementation of - divmod should go in the `__divmod__` method of the struct of `a`. - - Args: - numerator: The dividend. - denominator: The divisor. - - Returns: - A `Tuple` containing the quotient and the remainder. - """ - return numerator.__divmod__(denominator) diff --git a/stdlib/src/builtin/math.mojo b/stdlib/src/builtin/math.mojo index cdbc4783ab..9b13abd80e 100644 --- a/stdlib/src/builtin/math.mojo +++ b/stdlib/src/builtin/math.mojo @@ -10,15 +10,116 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Defines basic math functions for use in the open -source parts of the standard library since the `math` -package is currently closed source and cannot be depended -on in the open source parts of the standard library. +"""Defines basic math functions for use in the open source parts of the standard +library since the `math` package is currently closed source and cannot be +depended on in the open source parts of the standard library. + +These are Mojo built-ins, so you don't need to import them. """ -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # +# abs +# ===----------------------------------------------------------------------=== # + + +trait Absable: + """ + The `Absable` trait describes a type that defines an absolute value + operation. + + Types that conform to `Absable` will work with the builtin `abs` function. + The absolute value operation always returns the same type as the input. + + For example: + ```mojo + struct Point(Absable): + var x: Float64 + var y: Float64 + + fn __abs__(self) -> Self: + return sqrt(self.x * self.x + self.y * self.y) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __abs__(self) -> Self: + ... + + +@always_inline +fn abs[T: Absable](value: T) -> T: + """Get the absolute value of the given object. + + Parameters: + T: The type conforming to Absable. + + Args: + value: The object to get the absolute value of. + + Returns: + The absolute value of the object. + """ + return value.__abs__() + + +# TODO: https://github.com/modularml/modular/issues/38694 +# TODO: Remove this +@always_inline +fn abs(value: IntLiteral) -> IntLiteral: + """Get the absolute value of the given IntLiteral. + + Args: + value: The IntLiteral to get the absolute value of. + + Returns: + The absolute value of the IntLiteral. + """ + return value.__abs__() + + +# TODO: https://github.com/modularml/modular/issues/38694 +# TODO: Remove this +@always_inline +fn abs(value: FloatLiteral) -> FloatLiteral: + """Get the absolute value of the given FloatLiteral. + + Args: + value: The FloatLiteral to get the absolute value of. + + Returns: + The absolute value of the FloatLiteral. + """ + return value.__abs__() + + +# ===----------------------------------------------------------------------=== # +# divmod +# ===----------------------------------------------------------------------=== # + + +fn divmod(numerator: Int, denominator: Int) -> Tuple[Int, Int]: + """Performs integer division and returns the quotient and the remainder. + + Currently supported only for integers. Support for more standard library + types like Int8, Int16... is planned. + + This method calls `a.__divmod__(b)`, thus, the actual implementation of + divmod should go in the `__divmod__` method of the struct of `a`. + + Args: + numerator: The dividend. + denominator: The divisor. + + Returns: + A `Tuple` containing the quotient and the remainder. + """ + return numerator.__divmod__(denominator) + + +# ===----------------------------------------------------------------------=== # # max -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline @@ -60,9 +161,9 @@ fn max[ return x.max(y) -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # # min -# ===----------------------------------------------------------------------===# +# ===----------------------------------------------------------------------=== # @always_inline @@ -102,3 +203,50 @@ fn min[ A SIMD vector containing the elementwise minimum of x and y. """ return x.min(y) + + +# ===----------------------------------------------------------------------=== # +# round +# ===----------------------------------------------------------------------=== # + + +trait Roundable: + """ + The `Roundable` trait describes a type that defines an rounded value + operation. + + Types that conform to `Roundable` will work with the builtin `round` + function. The round operation always returns the same type as the input. + + For example: + ```mojo + @value + struct Complex(Roundable): + var re: Float64 + var im: Float64 + + fn __round__(self) -> Self: + return Self(round(re), round(im)) + ``` + """ + + # TODO(MOCO-333): Reconsider the signature when we have parametric traits or + # associated types. + fn __round__(self) -> Self: + ... + + +@always_inline +fn round[T: Roundable](value: T) -> T: + """Get the rounded value of the given object. + + Parameters: + T: The type conforming to Roundable. + + Args: + value: The object to get the rounded value of. + + Returns: + The rounded value of the object. + """ + return value.__round__() diff --git a/stdlib/src/builtin/round.mojo b/stdlib/src/builtin/round.mojo deleted file mode 100644 index 6193d76cff..0000000000 --- a/stdlib/src/builtin/round.mojo +++ /dev/null @@ -1,58 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -"""Provides the `round` function. - -These are Mojo built-ins, so you don't need to import them. -""" - - -trait Roundable: - """ - The `Roundable` trait describes a type that defines an rounded value - operation. - - Types that conform to `Roundable` will work with the builtin `round` - function. The round operation always returns the same type as the input. - - For example: - ```mojo - @value - struct Complex(Roundable): - var re: Float64 - var im: Float64 - - fn __round__(self) -> Self: - return Self(round(re), round(im)) - ``` - """ - - # TODO(MOCO-333): Reconsider the signature when we have parametric traits or - # associated types. - fn __round__(self) -> Self: - ... - - -@always_inline -fn round[T: Roundable](value: T) -> T: - """Get the rounded value of the given object. - - Parameters: - T: The type conforming to Roundable. - - Args: - value: The object to get the rounded value of. - - Returns: - The rounded value of the object. - """ - return value.__round__() diff --git a/stdlib/test/builtin/test_math.mojo b/stdlib/test/builtin/test_math.mojo index 8b801a96c3..9b88b5d299 100644 --- a/stdlib/test/builtin/test_math.mojo +++ b/stdlib/test/builtin/test_math.mojo @@ -40,3 +40,6 @@ def test_max(): def main(): test_min() test_max() + # TODO: add tests for abs, divmod, round. These tests should be simple; they + # test the free functions, so it's not needed to cover all corner cases of + # the underlying implementations. From bc0b7d93d29ae6ff47e4183a31551cb9ba5c1ca5 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 15 May 2024 17:56:12 -0700 Subject: [PATCH 0569/2019] Add info about the nightly branch When users checkout the repo it's not obvious that main isn't latest. MODULAR_ORIG_COMMIT_REV_ID: 329db6111101d0374b7b6d1563ad7209c65d78c0 --- README.md | 18 +++++++++++++++++- docs/manual/get-started.md | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 97f655a891..ebeb9c8c7e 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,23 @@ program](https://docs.modular.com/mojo/manual/get-started/hello-world). The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk -and be patient! Install them using the instructions [here](./CONTRIBUTING.md). +and be patient! + +To get nightly builds, see the same instructions to [install the Mojo +SDK](https://docs.modular.com/mojo/manual/get-started/#install-mojo), but use +the command shown there to install `nightly/mojo`. + +When you clone this repo, be sure you switch to the `nightly` branch, because +the `main` branch is for stable releases and might not be compatible with +nightly builds: + +```bash +git clone https://github.com/modularml/mojo.git +``` + +```bash +git checkout nightly +``` ## Contributing diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index 0ddee12d32..b94916066e 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -133,6 +133,12 @@ repo](https://github.com/modularml/mojo/) to try our code examples: git clone https://github.com/modularml/mojo.git ``` + If you installed the nightly build, also checkout the nightly branch: + + ```sh + git checkout nightly + ``` + In addition to several `.mojo` examples, the repo includes [Jupyter notebooks](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme) that teach advanced Mojo features. From 68eb0af1a3c9b1d1151e51af0c5509a3f08a4ab1 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 15 May 2024 18:56:45 -0600 Subject: [PATCH 0570/2019] [stdlib] Simplify `Variant.set` Just call the constructor (which will also call the destructor on the old value). This does one small extra operation that was not previously done: marking the underlying `!kgen.variant` with `#kgen.unknown` attributes for the new constructed `Variant`. MODULAR_ORIG_COMMIT_REV_ID: 95dbbf4cce85ca23b21055a8dfd22e481b31fb94 --- stdlib/src/utils/variant.mojo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 0b653db9e1..8cc2513dda 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -334,9 +334,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Args: value: The new value to set the variant to. """ - self._call_correct_deleter() - self._get_state()[] = Self._check[T]() - initialize_pointee_move(self._get_ptr[T](), value^) + self = Self(value^) fn isa[T: CollectionElement](self) -> Bool: """Check if the variant contains the required type. From ff75c1cf7efca20c579bd33114aa5f3c14c3dae5 Mon Sep 17 00:00:00 2001 From: Helehex Date: Wed, 15 May 2024 20:20:31 -0500 Subject: [PATCH 0571/2019] [External] [stdlib] Guard `SIMD.cast()` against equal types (#40022) [External] [stdlib] Guard `SIMD.cast()` against equal types Checks if types are already equal when calling `SIMD.cast()`. This avoids unnecessary code when generically casting simd vectors. But, this could be used in place of rebind when you know the types are already equal. if that's not what we want, then maybe we should constrain the types to be different, instead of doing this. Co-authored-by: Helehex Closes modularml/mojo#2669 MODULAR_ORIG_COMMIT_REV_ID: 737b3d0a79e304d131a79af7e21cb985dc4267ad --- stdlib/src/builtin/simd.mojo | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f71bf1a3ca..0ecdf0586d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -398,6 +398,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( element type. """ + @parameter + if type == target: + return rebind[SIMD[target, size]](self) + @parameter if has_neon() and (type == DType.bfloat16 or target == DType.bfloat16): # BF16 support on neon systems is not supported. From fc8671a0f4a69b0959f8ed5b59df3b390adf08ae Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Wed, 15 May 2024 20:37:15 -0500 Subject: [PATCH 0572/2019] [External] [stdlib] FloatLiteral missing operators to conform Python float types (#39733) [External] [stdlib] FloatLiteral missing operators to conform Python float types There are some missing operators in the `FloatLiteral` struct that we would need to implement to conform the Python `float` type: * `__round__(ndigits)` * `__mod__(rhs)` * `__divmod__(rhs)` This PR implements them and adds some tests to check the methods. --------- Co-authored-by: Manuel Saelices Closes modularml/mojo#2558 MODULAR_ORIG_COMMIT_REV_ID: ce34e9e2ad55590ccad85563098ab88146c198a2 --- stdlib/src/builtin/float_literal.mojo | 82 ++++++++++++++++++++- stdlib/test/builtin/test_float_literal.mojo | 55 ++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 3ab8aea135..db8e2876f9 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -33,6 +33,7 @@ struct FloatLiteral( Comparable, Floorable, Intable, + Roundable, Stringable, Truncable, ): @@ -239,7 +240,60 @@ struct FloatLiteral( return self return Self(self.__int_literal__()) - # TODO: implement __round__ + fn __round__(self) -> Self: + """Return the rounded value of the FloatLiteral. + + Returns: + The rounded value. + """ + # Handle special values first. + if not self._is_normal(): + return self + + var truncated: IntLiteral = self.__int_literal__() + var result: Self + if abs(self) - abs(truncated) <= 0.5: + result = Self(truncated) + elif self > 0: + result = Self(truncated + 1) + else: + result = Self(truncated - 1) + return result + + @always_inline("nodebug") + fn __round__(self, ndigits: IntLiteral) -> Self: + """Return the rounded value of the FloatLiteral. + + Args: + ndigits: The number of digits to round to. Defaults to 0. + + Returns: + The rounded value. + """ + # Handle special values first. + if not self._is_normal(): + return self + + alias one = __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` + alias ten = __mlir_attr.`#kgen.int_literal<10> : !kgen.int_literal` + var multiplier = one + # TODO: Use IntLiteral.__pow__() when it's implemented. + for _ in range(ndigits): + multiplier = __mlir_op.`kgen.int_literal.binop`[ + oper = __mlir_attr.`#kgen` + ](multiplier, ten) + var target: Self = self * Self(multiplier) + var truncated: Self = target.__int_literal__() + var result: Self + if abs(target) - abs(truncated) <= 0.5: + result = truncated + elif self > 0: + result = truncated + 1 + else: + result = truncated - 1 + if ndigits > 0: + result /= Self(multiplier) + return result # ===------------------------------------------------------------------===# # Arithmetic Operators @@ -315,6 +369,31 @@ struct FloatLiteral( return self.__truediv__(rhs).__floor__() @always_inline("nodebug") + fn __mod__(self, rhs: Self) -> Self: + """Return the remainder of self divided by rhs. + + Args: + rhs: The value to divide on. + + Returns: + The remainder of dividing self by rhs. + """ + return self.__divmod__(rhs)[1] + + @always_inline("nodebug") + fn __divmod__(self, rhs: Self) -> Tuple[Self, Self]: + """Return a tuple with the quotient and the remainder of self divided by rhs. + + Args: + rhs: The value to divide on. + + Returns: + The tuple with the dividend and the remainder + """ + var quotient: Self = self.__floordiv__(rhs) + var remainder: Self = self - (quotient * rhs) + return quotient, remainder + fn __rfloordiv__(self, rhs: Self) -> Self: """Returns rhs divided by self, rounded down to the nearest integer. @@ -326,7 +405,6 @@ struct FloatLiteral( """ return rhs // self - # TODO - maybe __mod__? # TODO - maybe __pow__? # ===------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_float_literal.mojo b/stdlib/test/builtin/test_float_literal.mojo index 7d146c92d3..e4d5b352f2 100644 --- a/stdlib/test/builtin/test_float_literal.mojo +++ b/stdlib/test/builtin/test_float_literal.mojo @@ -68,6 +68,34 @@ def test_trunc(): assert_equal(FloatLiteral.__trunc__(neg_inf), neg_inf) +def test_round(): + assert_equal(FloatLiteral.__round__(1.5), 1.0) + assert_equal(FloatLiteral.__round__(1.6), 2.0) + assert_equal(FloatLiteral.__round__(-1.5), -1.0) + assert_equal(FloatLiteral.__round__(-3.6), -4.0) + assert_equal(FloatLiteral.__round__(3.0), 3.0) + assert_equal(FloatLiteral.__round__(0.0), 0.0) + + assert_true(FloatLiteral.__round__(nan).is_nan()) + assert_true(FloatLiteral.__round__(neg_zero).is_neg_zero()) + assert_equal(FloatLiteral.__round__(inf), inf) + assert_equal(FloatLiteral.__round__(neg_inf), neg_inf) + + assert_equal(FloatLiteral.__round__(1.5, 0), 1.0) + assert_equal(FloatLiteral.__round__(2.5, 0), 2.0) + assert_equal(FloatLiteral.__round__(1.6, 0), 2.0) + assert_equal(FloatLiteral.__round__(-2.5, 0), -2.0) + + assert_equal(FloatLiteral.__round__(1.5, 1), 1.5) + assert_equal(FloatLiteral.__round__(1.123, 1), 1.1) + assert_equal(FloatLiteral.__round__(1.198, 2), 1.2) + assert_equal(FloatLiteral.__round__(1.123, 2), 1.12) + assert_equal(FloatLiteral.__round__(-1.5, 1), -1.5) + assert_equal(FloatLiteral.__round__(-1.123, 1), -1.1) + assert_equal(FloatLiteral.__round__(-1.198, 2), -1.2) + assert_equal(FloatLiteral.__round__(-1.123, 2), -1.12) + + fn round10(x: Float64) -> Float64: # TODO: implement __div__ on FloatLiteral? return (round(Float64(x * 10)) / 10).value @@ -103,6 +131,30 @@ def test_power(): # assert_almost_equal((-4.5) ** -2.5, -0.023279235) +def test_mod(): + assert_equal(4.5 % 2, 0.5) + assert_equal(-4.5 % 2, 1.5) + assert_equal(6 % 2.5, 1.0) + + +def test_div_mod(): + var t: Tuple[FloatLiteral, FloatLiteral] = FloatLiteral.__divmod__(4.5, 2.0) + assert_equal(t[0], 2.0) + assert_equal(t[1], 0.5) + + t = FloatLiteral.__divmod__(-4.5, 2.0) + assert_equal(t[0], -3.0) + assert_equal(t[1], 1.5) + + t = FloatLiteral.__divmod__(4.5, -2.0) + assert_equal(t[0], -3.0) + assert_equal(t[1], -1.5) + + t = FloatLiteral.__divmod__(6.0, 2.5) + assert_equal(t[0], 2.0) + assert_equal(t[1], 1.0) + + def test_int_conversion(): assert_equal(int(-4.0), -4) assert_equal(int(-4.5), -4) @@ -153,9 +205,12 @@ def main(): test_ceil() test_floor() test_trunc() + test_round() test_round10() test_division() test_power() + test_mod() + test_div_mod() test_int_conversion() test_boolean_comparable() test_equality() From 879eff05b88188bcdad811fa754c23d46a625415 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 15 May 2024 21:38:28 -0400 Subject: [PATCH 0573/2019] [stdlib] Remove the `math.limit` module The same functionality is now available in `utils.numerics`. Tests are extended and improved to use these. MODULAR_ORIG_COMMIT_REV_ID: f633b2cb2b0d95b3776d58ce1519eb84813cf0ae --- docs/changelog.md | 7 ++ docs/manual/types.ipynb | 2 +- examples/notebooks/RayTracing.ipynb | 2 +- stdlib/test/builtin/test_simd.mojo | 130 +++++++++++++-------------- stdlib/test/utils/test_numerics.mojo | 79 +++++++++++++--- 5 files changed, 136 insertions(+), 84 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 8bbebb6e30..c61120a9b0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -262,6 +262,13 @@ what we publish. methods for `SIMD` types, and the `bit.rotate_bits_{left,right}` methods for `Int`. +- The `math.limit` module has been removed. The same functionality is available + as follows: + - `math.limit.inf`: use `utils.numerics.max_or_inf` + - `math.limit.neginf`: use `utils.numerics.min_or_neg_inf` + - `math.limit.max_finite`: use `utils.numerics.max_finite` + - `math.limit.min_finite`: use `utils.numerics.min_finite` + ### 🛠️ Fixed - [#2363](https://github.com/modularml/mojo/issues/2363) Fix LSP crashing on diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index 09fda5d6fc..be2e84e9ed 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -346,7 +346,7 @@ } ], "source": [ - "from math.limit import max_finite, min_finite\n", + "from utils.numerics import max_finite, min_finite\n", "\n", "def describeDType[dtype: DType]():\n", " print(dtype, \"is floating point:\", dtype.is_floating_point())\n", diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 374ddc2927..de32b13a20 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -584,7 +584,7 @@ ], "source": [ "from algorithm import parallelize\n", - "from math.limit import inf\n", + "from utils.numerics import inf\n", "from collections import List\n", "\n", "\n", diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 92c4f49eb0..8b1d18ffe0 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from sys import has_neon +from utils.numerics import isfinite, isinf, isnan from testing import assert_equal, assert_not_equal, assert_true, assert_false @@ -291,6 +292,24 @@ def test_roundeven(): assert_equal(F(1.5, 2.5, -2.5, -3.5).roundeven(), F(2.0, 2.0, -2.0, -4.0)) +def test_div(): + assert_false(isfinite(Float32(33).__truediv__(0))) + assert_false(isfinite(Float32(0).__truediv__(0))) + + assert_true(isinf(Float32(33).__truediv__(0))) + assert_false(isinf(Float32(0).__truediv__(0))) + + assert_false(isnan(Float32(33).__truediv__(0))) + assert_true(isnan(Float32(0).__truediv__(0))) + + alias F32 = SIMD[DType.float32, 4] + var res = F32.__truediv__(F32(1, 0, 3, -1), F32(0, 0, 1, 0)) + alias B = SIMD[DType.bool, 4] + assert_equal(isfinite(res), B(False, False, True, False)) + assert_equal(isinf(res), B(True, False, False, True)) + assert_equal(isnan(res), B(False, True, False, False)) + + def test_floordiv(): assert_equal(Int32(2).__floordiv__(2), 1) assert_equal(Int32(2).__floordiv__(Int32(2)), 1) @@ -713,207 +732,179 @@ def test_limits(): def test_add_with_overflow(): - # TODO: replace all the aliases with math.limit.max_finite() - # and math.limit.min_finite() - alias uint8_min = 0 - alias uint8_max = 255 var value_u8: UInt8 var overflowed_u8: Scalar[DType.bool] - value_u8, overflowed_u8 = UInt8(uint8_max).add_with_overflow(1) - assert_equal(value_u8, uint8_min) + value_u8, overflowed_u8 = UInt8(UInt8.MAX).add_with_overflow(1) + assert_equal(value_u8, UInt8.MIN) assert_equal(overflowed_u8, True) var value_u8x4: SIMD[DType.uint8, 4] var overflowed_u8x4: SIMD[DType.bool, 4] value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( - 1, uint8_max, 1, uint8_max + 1, UInt8.MAX, 1, UInt8.MAX ).add_with_overflow(SIMD[DType.uint8, 4](0, 1, 0, 1)) - assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, uint8_min, 1, uint8_min)) + assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, UInt8.MIN, 1, UInt8.MIN)) assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) - alias int8_min = -128 - alias int8_max = 127 var value_i8: Int8 var overflowed_i8: Scalar[DType.bool] - value_i8, overflowed_i8 = Int8(int8_max).add_with_overflow(1) - assert_equal(value_i8, int8_min) + value_i8, overflowed_i8 = Int8(Int8.MAX).add_with_overflow(1) + assert_equal(value_i8, Int8.MIN) assert_equal(overflowed_i8, True) var value_i8x4: SIMD[DType.int8, 4] var overflowed_i8x4: SIMD[DType.bool, 4] value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( - 1, int8_max, 1, int8_max + 1, Int8.MAX, 1, Int8.MAX ).add_with_overflow(SIMD[DType.int8, 4](0, 1, 0, 1)) - assert_equal(value_i8x4, SIMD[DType.int8, 4](1, int8_min, 1, int8_min)) + assert_equal(value_i8x4, SIMD[DType.int8, 4](1, Int8.MIN, 1, Int8.MIN)) assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) - alias uint32_min = 0 - alias uint32_max = 4294967295 var value_u32: UInt32 var overflowed_u32: Scalar[DType.bool] - value_u32, overflowed_u32 = UInt32(uint32_max).add_with_overflow(1) - assert_equal(value_u32, uint32_min) + value_u32, overflowed_u32 = UInt32(UInt32.MAX).add_with_overflow(1) + assert_equal(value_u32, UInt32.MIN) assert_equal(overflowed_u32, True) var value_u32x4: SIMD[DType.uint32, 4] var overflowed_u32x4: SIMD[DType.bool, 4] value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( - 1, uint32_max, 1, uint32_max + 1, UInt32.MAX, 1, UInt32.MAX ).add_with_overflow(SIMD[DType.uint32, 4](0, 1, 0, 1)) assert_equal( - value_u32x4, SIMD[DType.uint32, 4](1, uint32_min, 1, uint32_min) + value_u32x4, SIMD[DType.uint32, 4](1, UInt32.MIN, 1, UInt32.MIN) ) assert_equal( overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) ) - alias int32_min = -2147483648 - alias int32_max = 2147483647 var value_i32: Int32 var overflowed_i32: Scalar[DType.bool] - value_i32, overflowed_i32 = Int32(int32_max).add_with_overflow(1) - assert_equal(value_i32, int32_min) + value_i32, overflowed_i32 = Int32(Int32.MAX).add_with_overflow(1) + assert_equal(value_i32, Int32.MIN) assert_equal(overflowed_i32, True) var value_i32x4: SIMD[DType.int32, 4] var overflowed_i32x4: SIMD[DType.bool, 4] value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( - 1, int32_max, 1, int32_max + 1, Int32.MAX, 1, Int32.MAX ).add_with_overflow(SIMD[DType.int32, 4](0, 1, 0, 1)) - assert_equal(value_i32x4, SIMD[DType.int32, 4](1, int32_min, 1, int32_min)) + assert_equal(value_i32x4, SIMD[DType.int32, 4](1, Int32.MIN, 1, Int32.MIN)) assert_equal( overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) ) def test_sub_with_overflow(): - # TODO: replace all the aliases with math.limit.max_finite() - # and math.limit.min_finite() - alias uint8_min = 0 - alias uint8_max = 255 var value_u8: UInt8 var overflowed_u8: Scalar[DType.bool] - value_u8, overflowed_u8 = UInt8(uint8_min).sub_with_overflow(1) - assert_equal(value_u8, uint8_max) + value_u8, overflowed_u8 = UInt8(UInt8.MIN).sub_with_overflow(1) + assert_equal(value_u8, UInt8.MAX) assert_equal(overflowed_u8, True) var value_u8x4: SIMD[DType.uint8, 4] var overflowed_u8x4: SIMD[DType.bool, 4] value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( - 1, uint8_min, 1, uint8_min + 1, UInt8.MIN, 1, UInt8.MIN ).sub_with_overflow(SIMD[DType.uint8, 4](0, 1, 0, 1)) - assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, uint8_max, 1, uint8_max)) + assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, UInt8.MAX, 1, UInt8.MAX)) assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) - alias int8_min = -128 - alias int8_max = 127 var value_i8: Int8 var overflowed_i8: Scalar[DType.bool] - value_i8, overflowed_i8 = Int8(int8_min).sub_with_overflow(1) - assert_equal(value_i8, int8_max) + value_i8, overflowed_i8 = Int8(Int8.MIN).sub_with_overflow(1) + assert_equal(value_i8, Int8.MAX) assert_equal(overflowed_i8, True) var value_i8x4: SIMD[DType.int8, 4] var overflowed_i8x4: SIMD[DType.bool, 4] value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( - 1, int8_min, 1, int8_min + 1, Int8.MIN, 1, Int8.MIN ).sub_with_overflow(SIMD[DType.int8, 4](0, 1, 0, 1)) - assert_equal(value_i8x4, SIMD[DType.int8, 4](1, int8_max, 1, int8_max)) + assert_equal(value_i8x4, SIMD[DType.int8, 4](1, Int8.MAX, 1, Int8.MAX)) assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) - alias uint32_min = 0 - alias uint32_max = 4294967295 var value_u32: UInt32 var overflowed_u32: Scalar[DType.bool] - value_u32, overflowed_u32 = UInt32(uint32_min).sub_with_overflow(1) - assert_equal(value_u32, uint32_max) + value_u32, overflowed_u32 = UInt32(UInt32.MIN).sub_with_overflow(1) + assert_equal(value_u32, UInt32.MAX) assert_equal(overflowed_u32, True) var value_u32x4: SIMD[DType.uint32, 4] var overflowed_u32x4: SIMD[DType.bool, 4] value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( - 1, uint32_min, 1, uint32_min + 1, UInt32.MIN, 1, UInt32.MIN ).sub_with_overflow(SIMD[DType.uint32, 4](0, 1, 0, 1)) assert_equal( - value_u32x4, SIMD[DType.uint32, 4](1, uint32_max, 1, uint32_max) + value_u32x4, SIMD[DType.uint32, 4](1, UInt32.MAX, 1, UInt32.MAX) ) assert_equal( overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) ) - alias int32_min = -2147483648 - alias int32_max = 2147483647 var value_i32: Int32 var overflowed_i32: Scalar[DType.bool] - value_i32, overflowed_i32 = Int32(int32_min).sub_with_overflow(1) - assert_equal(value_i32, int32_max) + value_i32, overflowed_i32 = Int32(Int32.MIN).sub_with_overflow(1) + assert_equal(value_i32, Int32.MAX) assert_equal(overflowed_i32, True) var value_i32x4: SIMD[DType.int32, 4] var overflowed_i32x4: SIMD[DType.bool, 4] value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( - 1, int32_min, 1, int32_min + 1, Int32.MIN, 1, Int32.MIN ).sub_with_overflow(SIMD[DType.int32, 4](0, 1, 0, 1)) - assert_equal(value_i32x4, SIMD[DType.int32, 4](1, int32_max, 1, int32_max)) + assert_equal(value_i32x4, SIMD[DType.int32, 4](1, Int32.MAX, 1, Int32.MAX)) assert_equal( overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) ) def test_mul_with_overflow(): - # TODO: replace all the aliases with math.limit.max_finite() - # and math.limit.min_finite() - alias uint8_min = 0 - alias uint8_max = 255 alias uint8_max_x2 = 254 var value_u8: UInt8 var overflowed_u8: Scalar[DType.bool] - value_u8, overflowed_u8 = UInt8(uint8_max).mul_with_overflow(2) + value_u8, overflowed_u8 = UInt8(UInt8.MAX).mul_with_overflow(2) assert_equal(value_u8, uint8_max_x2) assert_equal(overflowed_u8, True) var value_u8x4: SIMD[DType.uint8, 4] var overflowed_u8x4: SIMD[DType.bool, 4] value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( - 1, uint8_max, 1, uint8_max + 1, UInt8.MAX, 1, UInt8.MAX ).mul_with_overflow(SIMD[DType.uint8, 4](0, 2, 0, 2)) assert_equal( value_u8x4, SIMD[DType.uint8, 4](0, uint8_max_x2, 0, uint8_max_x2) ) assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) - alias int8_min = -128 - alias int8_max = 127 alias int8_max_x2 = -2 var value_i8: Int8 var overflowed_i8: Scalar[DType.bool] - value_i8, overflowed_i8 = Int8(int8_max).mul_with_overflow(2) + value_i8, overflowed_i8 = Int8(Int8.MAX).mul_with_overflow(2) assert_equal(value_i8, int8_max_x2) assert_equal(overflowed_i8, True) var value_i8x4: SIMD[DType.int8, 4] var overflowed_i8x4: SIMD[DType.bool, 4] value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( - 1, int8_max, 1, int8_max + 1, Int8.MAX, 1, Int8.MAX ).mul_with_overflow(SIMD[DType.int8, 4](0, 2, 0, 2)) assert_equal( value_i8x4, SIMD[DType.int8, 4](0, int8_max_x2, 0, int8_max_x2) ) assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) - alias uint32_min = 0 - alias uint32_max = 4294967295 alias uint32_max_x2 = 4294967294 var value_u32: UInt32 var overflowed_u32: Scalar[DType.bool] - value_u32, overflowed_u32 = UInt32(uint32_max).mul_with_overflow(2) + value_u32, overflowed_u32 = UInt32(UInt32.MAX).mul_with_overflow(2) assert_equal(value_u32, uint32_max_x2) assert_equal(overflowed_u32, True) var value_u32x4: SIMD[DType.uint32, 4] var overflowed_u32x4: SIMD[DType.bool, 4] value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( - 1, uint32_max, 1, uint32_max + 1, UInt32.MAX, 1, UInt32.MAX ).mul_with_overflow(SIMD[DType.uint32, 4](0, 2, 0, 2)) assert_equal( value_u32x4, SIMD[DType.uint32, 4](0, uint32_max_x2, 0, uint32_max_x2) @@ -922,19 +913,17 @@ def test_mul_with_overflow(): overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) ) - alias int32_min = -2147483648 - alias int32_max = 2147483647 alias int32_max_x2 = -2 var value_i32: Int32 var overflowed_i32: Scalar[DType.bool] - value_i32, overflowed_i32 = Int32(int32_max).mul_with_overflow(2) + value_i32, overflowed_i32 = Int32(Int32.MAX).mul_with_overflow(2) assert_equal(value_i32, int32_max_x2) assert_equal(overflowed_i32, True) var value_i32x4: SIMD[DType.int32, 4] var overflowed_i32x4: SIMD[DType.bool, 4] value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( - 1, int32_max, 1, int32_max + 1, Int32.MAX, 1, Int32.MAX ).mul_with_overflow(SIMD[DType.int32, 4](0, 2, 0, 2)) assert_equal( value_i32x4, SIMD[DType.int32, 4](0, int32_max_x2, 0, int32_max_x2) @@ -999,6 +988,7 @@ def main(): test_trunc() test_round() test_roundeven() + test_div() test_floordiv() test_rfloordiv() test_mod() diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index 556abc8401..ce9d9fdf42 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -14,7 +14,17 @@ from sys.info import has_neon from testing import assert_equal, assert_true, assert_false, assert_almost_equal -from utils.numerics import FPUtils, inf, isinf, isnan, max_finite, nan, ulp +from utils.numerics import ( + FPUtils, + inf, + isfinite, + isinf, + isnan, + max_finite, + nan, + ulp, + neg_inf, +) # TODO: improve coverage and organization of these tests @@ -45,21 +55,64 @@ def test_FPUtils(): assert_equal(FPU64.get_mantissa(FPU64.pack(True, 6, 12)), 12) -fn test_inf() raises: +def test_isfinite(): + assert_true(isfinite(Float32(33))) + @parameter - fn _test_inf[type: DType]() raises: - var val = inf[type]() - var msg = "`test_inf` failed for `type == " + str(type) + "`" - assert_true((val > 0.0) & isinf(val), msg=msg) + if not has_neon(): + assert_false(isfinite(inf[DType.bfloat16]())) + assert_false(isfinite(neg_inf[DType.bfloat16]())) + assert_false(isfinite(nan[DType.bfloat16]())) + + assert_false(isfinite(inf[DType.float16]())) + assert_false(isfinite(inf[DType.float32]())) + assert_false(isfinite(inf[DType.float64]())) + assert_false(isfinite(neg_inf[DType.float16]())) + assert_false(isfinite(neg_inf[DType.float32]())) + assert_false(isfinite(neg_inf[DType.float64]())) + assert_false(isfinite(nan[DType.float16]())) + assert_false(isfinite(nan[DType.float32]())) + assert_false(isfinite(nan[DType.float64]())) + + +def test_isinf(): + assert_false(isinf(Float32(33))) + + @parameter + if not has_neon(): + assert_true(isinf(inf[DType.bfloat16]())) + assert_true(isinf(neg_inf[DType.bfloat16]())) + assert_false(isinf(nan[DType.bfloat16]())) + + assert_true(isinf(inf[DType.float16]())) + assert_true(isinf(inf[DType.float32]())) + assert_true(isinf(inf[DType.float64]())) + assert_true(isinf(neg_inf[DType.float16]())) + assert_true(isinf(neg_inf[DType.float32]())) + assert_true(isinf(neg_inf[DType.float64]())) + assert_false(isinf(nan[DType.float16]())) + assert_false(isinf(nan[DType.float32]())) + assert_false(isinf(nan[DType.float64]())) + + +def test_isnan(): + assert_false(isnan(Float32(33))) @parameter if not has_neon(): - # "bf16 is not supported for ARM architectures" - _test_inf[DType.bfloat16]() + assert_false(isnan(inf[DType.bfloat16]())) + assert_false(isnan(neg_inf[DType.bfloat16]())) + assert_true(isnan(nan[DType.bfloat16]())) - _test_inf[DType.float16]() - _test_inf[DType.float32]() - _test_inf[DType.float64]() + assert_false(isnan(inf[DType.float16]())) + assert_false(isnan(inf[DType.float32]())) + assert_false(isnan(inf[DType.float64]())) + assert_false(isnan(neg_inf[DType.float16]())) + assert_false(isnan(neg_inf[DType.float32]())) + assert_false(isnan(neg_inf[DType.float64]())) + assert_true(isnan(nan[DType.float16]())) + assert_true(isnan(nan[DType.float32]())) + assert_true(isnan(nan[DType.float64]())) def test_ulp(): @@ -74,6 +127,8 @@ def test_ulp(): def main(): test_FPUtils() - test_inf() + test_isfinite() + test_isinf() + test_isnan() # TODO: test nextafter test_ulp() From 00d87c3cc5b29bdf2cda1a7e5c30c60b4219008d Mon Sep 17 00:00:00 2001 From: Helehex Date: Wed, 15 May 2024 20:59:32 -0500 Subject: [PATCH 0574/2019] [External] [stdlib] Add builtin `any()`/`all()` functions (#39815) [External] [stdlib] Add builtin `any()`/`all()` functions Adds builtin functions for testing if there are truthy elements in `List`, `Set`, `SIMD`. Should change to check iterators eventually. Also mentioned [here](https://github.com/modularml/mojo/pull/2502) --------- Co-authored-by: Helehex Closes modularml/mojo#2600 MODULAR_ORIG_COMMIT_REV_ID: 894768635c30c41fa359a69e5110b60adce735f8 --- docs/changelog.md | 14 +++ stdlib/src/builtin/bool.mojo | 114 ++++++++++++++++++ stdlib/src/builtin/simd.mojo | 27 +++++ stdlib/src/builtin/value.mojo | 24 ++++ stdlib/test/builtin/test_any_all.mojo | 160 ++++++++++++++++++++++++++ 5 files changed, 339 insertions(+) create mode 100644 stdlib/test/builtin/test_any_all.mojo diff --git a/docs/changelog.md b/docs/changelog.md index c61120a9b0..76368603e7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -95,6 +95,20 @@ what we publish. return Self(trunc(re), trunc(im)) ``` +- Add builtin `any()` and `all()` functions to check for truthy elements in a collection. + This also works to get the truthy value of a SIMD vector. + ([PR #2600](https://github.com/modularml/mojo/pull/2600) by [@helehex](https://github.com/helehex)) + For example: + + ```mojo + fn truthy_simd(): + var vec = SIMD[DType.int32, 4](0, 1, 2, 3) + if any(vec): + print("any elements are truthy") + if all(vec): + print("all elements are truthy") + ``` + - Add an `InlinedArray` type that works on memory-only types. Compare with the existing `StaticTuple` type, which is conceptually an array type, but only worked on `AnyRegType`. diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index e56f2b3eae..1068d7fd4b 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -17,6 +17,8 @@ These are Mojo built-ins, so you don't need to import them. from utils._visualizers import lldb_formatter_wrapping_type +from collections import Set + # ===----------------------------------------------------------------------=== # # Boolable @@ -355,3 +357,115 @@ fn bool[T: Boolable](value: T) -> Bool: The bool representation of the object. """ return value.__bool__() + + +# ===----------------------------------------------------------------------=== # +# any +# ===----------------------------------------------------------------------=== # + + +# TODO: Combine these into Iterators over Boolable elements + + +fn any[T: BoolableCollectionElement](list: List[T]) -> Bool: + """Checks if **any** elements in the list are truthy. + + Parameters: + T: The type of elements to check. + + Args: + list: The list to check. + + Returns: + Returns `True` if **any** elements in the list are truthy, `False` otherwise. + """ + for item in list: + if item[]: + return True + return False + + +fn any[T: BoolableKeyElement](set: Set[T]) -> Bool: + """Checks if **any** elements in the set are truthy. + + Parameters: + T: The type of elements to check. + + Args: + set: The set to check. + + Returns: + Returns `True` if **any** elements in the set are truthy, `False` otherwise. + """ + for item in set: + if item[]: + return True + return False + + +fn any(value: SIMD) -> Bool: + """Checks if **any** elements in the simd vector are truthy. + + Args: + value: The simd vector to check. + + Returns: + Returns `True` if **any** elements in the simd vector are truthy, `False` otherwise. + """ + return value._reduce_any() + + +# ===----------------------------------------------------------------------=== # +# all +# ===----------------------------------------------------------------------=== # + + +# TODO: Combine these into Iterators over Boolable elements + + +fn all[T: BoolableCollectionElement](list: List[T]) -> Bool: + """Checks if **all** elements in the list are truthy. + + Parameters: + T: The type of elements to check. + + Args: + list: The list to check. + + Returns: + Returns `True` if **all** elements in the list are truthy, `False` otherwise. + """ + for item in list: + if not item[]: + return False + return True + + +fn all[T: BoolableKeyElement](set: Set[T]) -> Bool: + """Checks if **all** elements in the set are truthy. + + Parameters: + T: The type of elements to check. + + Args: + set: The set to check. + + Returns: + Returns `True` if **all** elements in the set are truthy, `False` otherwise. + """ + for item in set: + if not item[]: + return False + return True + + +fn all(value: SIMD) -> Bool: + """Checks if **all** elements in the simd vector are truthy. + + Args: + value: The simd vector to check. + + Returns: + Returns `True` if **all** elements in the simd vector are truthy, `False` otherwise. + """ + return value._reduce_all() diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 0ecdf0586d..03e27e002f 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2217,6 +2217,33 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( "llvm.vector.reduce.or", Scalar[DType.bool], has_side_effect=False ](self) + @always_inline + fn _reduce_all(self) -> Bool: + """Returns whether **all** elements in this vector are non-zero. + + Returns: + `True` if and only if **all** elements in this vector are non-zero. + """ + + @parameter + if type == DType.bool: + return self.reduce_and() + return self.cast[DType.bool]().reduce_and() + + @always_inline + fn _reduce_any(self) -> Bool: + """Returns whether this vector contains **any** non-zero elements. + + Returns: + `True` if this vector contains **any** non-zero elements, `False` + otherwise. + """ + + @parameter + if type == DType.bool: + return self.reduce_or() + return self.cast[DType.bool]().reduce_or() + # ===-------------------------------------------------------------------===# # select # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 1029c42909..c03b2e7865 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -179,3 +179,27 @@ trait RepresentableCollectionElement(CollectionElement, Representable): """ pass + + +trait BoolableCollectionElement(Boolable, CollectionElement): + """The BoolableCollectionElement trait denotes a trait composition + of the `Boolable` and `CollectionElement` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `Boolable & CollectionElement` in the parameter. + """ + + pass + + +trait BoolableKeyElement(Boolable, KeyElement): + """The BoolableKeyElement trait denotes a trait composition + of the `Boolable` and `KeyElement` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `Boolable & KeyElement` in the parameter. + """ + + pass diff --git a/stdlib/test/builtin/test_any_all.mojo b/stdlib/test/builtin/test_any_all.mojo new file mode 100644 index 0000000000..a65ccfcc51 --- /dev/null +++ b/stdlib/test/builtin/test_any_all.mojo @@ -0,0 +1,160 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_true, assert_false +from collections.set import Set + + +def test_list_any(): + # List[Int] + assert_true(any(List(-1, 2))) + assert_true(any(List(-0, 2, 3))) + assert_true(any(List(-0, 0, 3))) + assert_false(any(List(0, 0, 0, 0))) + assert_false(any(List[Int]())) + + # List[Float] + assert_true(any(List(-1.0, 2.0, 3.0))) + assert_true(any(List(-1.0, 0.0, 3.0))) + assert_true(any(List(-0.0, 2.0, 0.0))) + assert_false(any(List(0.0, 0.0, 0.0))) + assert_false(any(List[Float64]())) + + # List[Bool] + assert_true(any(List(True))) + assert_true(any(List(True, True))) + assert_true(any(List(True, False))) + assert_true(any(List(False, True))) + assert_false(any(List(False, False))) + assert_false(any(List(False))) + assert_false(any(List[Bool]())) + + +def test_list_all(): + # List[Int] + assert_true(all(List(-1, 2, 3))) + assert_false(all(List(1, 2, 0))) + assert_false(all(List(1, 0, 0))) + assert_false(all(List(0, 0, 0))) + assert_true(all(List[Int]())) + + # List[Float] + assert_true(all(List(-1.0, 2.0, 3.0, 4.0))) + assert_false(all(List(1.0, 0.0, 3.0))) + assert_false(all(List(0.0, 2.0, 0.0))) + assert_false(all(List(0.0, 0.0))) + assert_true(all(List[Float64]())) + + # List[Bool] + assert_true(all(List(True))) + assert_true(all(List(True, True))) + assert_false(all(List(True, False))) + assert_false(all(List(False, True))) + assert_false(all(List(False, False))) + assert_false(all(List(False))) + assert_true(all(List[Bool]())) + + +def test_set_any(): + # Set[Int] + assert_true(any(Set(-1))) + assert_true(any(Set(-1, 0, 3))) + assert_false(any(Set(0))) + assert_false(any(Set[Int]())) + + # Set[String] + assert_true(any(Set[String]("any"))) + assert_true(any(Set[String]("bleep", "bloop"))) + assert_true(any(Set[String]("", ":]"))) + assert_false(any(Set[String](""))) + assert_false(any(Set[String]())) + + +def test_set_all(): + # Set[Int] + assert_true(all(Set(-1))) + assert_false(all(Set(0, 1, 3))) + assert_false(all(Set(0))) + assert_true(all(Set[Int]())) + + # Set[String] + assert_true(all(Set[String]("all"))) + assert_true(all(Set[String]("0", "1"))) + assert_false(all(Set[String]("mojo", ""))) + assert_false(all(Set[String](""))) + assert_true(all(Set[String]())) + + +def test_simd_any(): + @parameter + def _test_dtype[type: DType](): + assert_true(any(SIMD[type, 1](1))) + assert_false(any(SIMD[type, 1](0))) + assert_true(any(SIMD[type, 4](1, 2, 3, 4))) + assert_true(any(SIMD[type, 4](0, 2, 3, 4))) + assert_true(any(SIMD[type, 4](1, 2, 3, 0))) + assert_true(any(SIMD[type, 4](0, 2, 3, 0))) + assert_true(any(SIMD[type, 4](1, 0, 0, 4))) + assert_false(any(SIMD[type, 4](0, 0, 0, 0))) + + _test_dtype[DType.bool]() + _test_dtype[DType.int8]() + _test_dtype[DType.int16]() + _test_dtype[DType.int32]() + _test_dtype[DType.int64]() + _test_dtype[DType.uint8]() + _test_dtype[DType.uint16]() + _test_dtype[DType.uint32]() + _test_dtype[DType.uint64]() + _test_dtype[DType.float16]() + _test_dtype[DType.float32]() + _test_dtype[DType.float64]() + + +def test_simd_all(): + @parameter + def _test_dtype[type: DType](): + assert_true(all(SIMD[type, 1](1))) + assert_false(all(SIMD[type, 1](0))) + assert_true(all(SIMD[type, 4](1, 2, 3, 4))) + assert_false(all(SIMD[type, 4](0, 2, 3, 4))) + assert_false(all(SIMD[type, 4](1, 2, 3, 0))) + assert_false(all(SIMD[type, 4](0, 2, 3, 0))) + assert_false(all(SIMD[type, 4](1, 0, 0, 4))) + assert_false(all(SIMD[type, 4](0, 0, 0, 0))) + + _test_dtype[DType.bool]() + _test_dtype[DType.int8]() + _test_dtype[DType.int16]() + _test_dtype[DType.int32]() + _test_dtype[DType.int64]() + _test_dtype[DType.uint8]() + _test_dtype[DType.uint16]() + _test_dtype[DType.uint32]() + _test_dtype[DType.uint64]() + _test_dtype[DType.float16]() + _test_dtype[DType.float32]() + _test_dtype[DType.float64]() + + +def main(): + # any + test_list_any() + test_set_any() + test_simd_any() + + # all + test_list_all() + test_set_all() + test_simd_all() From 6c010fc535a1ff033ccfdf435cd5dcd7aa308457 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 16 May 2024 10:19:18 -0500 Subject: [PATCH 0575/2019] [External] [stdlib] Implement List.__repr__() method (#40018) [External] [stdlib] Implement List.__repr__() method Based on the `List.__str__()` one, to allow to call `repr(some_list)`. It's not currently passing because of the following error, but I think the code is correct and it's a limitation of the mojo compiler, which does not understand that, if `self` is made of `RepresentableCollectionElement`, the `__repr__` method can call the `__str__` one, as `__type_of(self)` will also be `List[RepresentableCollectionElement]` ![image](https://github.com/modularml/mojo/assets/136875/b4fc0292-d2db-426e-8b61-33003e98de3d) Co-authored-by: Manuel Saelices Closes modularml/mojo#2634 MODULAR_ORIG_COMMIT_REV_ID: b939d766211d3666fe65eea198f4bf24e734a180 --- stdlib/src/collections/list.mojo | 28 ++++++++++++++++++++++++++++ stdlib/test/builtin/test_list.mojo | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index a6b347020b..cccbb4d2a2 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -703,6 +703,34 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): result += "]" return result + @staticmethod + fn __repr__[U: RepresentableCollectionElement](self: List[U]) -> String: + """Returns a string representation of a `List`. + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + var my_list = List[Int](1, 2, 3) + print(__type_of(my_list).__repr__(my_list)) + ``` + + When the compiler supports conditional methods, then a simple `repr(my_list)` will + be enough. + + The elements' type must implement the `__repr__()` for this to work. + + Args: + self: The list to represent as a string. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Representable` and `CollectionElement`. + + Returns: + A string representation of the list. + """ + return __type_of(self).__str__(self) + @staticmethod fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 79bc5666b9..5464bd1217 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -31,6 +31,14 @@ fn test_variadic_list() raises: check_list(5, 8, 6) +fn test_repr_list() raises: + var l = List(1, 2, 3) + assert_equal(__type_of(l).__repr__(l), "[1, 2, 3]") + var empty = List[Int]() + assert_equal(__type_of(empty).__repr__(empty), "[]") + + def main(): test_list() test_variadic_list() + test_repr_list() From b4f91656ee302f95655af693941b1746a6325b39 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 16 May 2024 08:34:32 -0700 Subject: [PATCH 0576/2019] [mojo] Changelog entry for `inferred` keyword This PR adds a changelog entry for the new `inferred` keyword in mojo. It affects parameters declarations in parameter lists. MODULAR_ORIG_COMMIT_REV_ID: 2215681590b33ec6451f7c7a22e607a322316c0d --- docs/changelog.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 76368603e7..e4f579f1eb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,26 @@ what we publish. ### ⭐️ New +- Mojo added support for the `inferred` passing kind on parameters. `inferred` + parameters must appear at the beginning of the parameter list and cannot be + explicitly specified by the user. This allows users to define functions with + dependent parameters to be called without the caller specifying all the + necessary parameters. For example: + + ```mojo + fn parameter_simd[inferred dt: DType, value: Scalar[dt]](): + print(value) + + fn call_it(): + parameter_simd[Int32(42)]() + ``` + + In the above example, `Int32(42)` is passed directly into `value`, the first + non-inferred parameter. `dt` is inferred from the parameter itself to + `DType.int32`. + + Note that this only works on function parameter lists at the moment. + - Mojo now supports adding a `@deprecated` decorator on structs, functions, traits, aliases, and global variables. The decorator marks the attached decl as deprecated and causes a warning to be emitted when the deprecated decl is From 9e21295afd9616c486d475db3310999218edbadd Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 16 May 2024 11:07:33 -0500 Subject: [PATCH 0577/2019] [External] [stdlib] Allow the simplified syntax `my_list.__str__()` (#40046) [External] [stdlib] Allow the simplified syntax `my_list.__str__()` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2673 MODULAR_ORIG_COMMIT_REV_ID: d9b2b6bba825f6c7a94166a297326c146ce4e4c2 --- docs/changelog.md | 11 +++++++++++ stdlib/docs/development.md | 2 +- stdlib/src/collections/list.mojo | 6 +----- stdlib/test/collections/test_list.mojo | 20 +++++++++----------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e4f579f1eb..872d1e95de 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -220,6 +220,17 @@ what we publish. print(my_list.index(3)) # prints 1 ``` +- `List` can now be converted to a `String` with a simplified syntax: + + ```mojo + var my_list = List[Int](2, 3) + print(my_list.__str__()) # prints [2, 3] + ``` + + Note that `List` doesn't conform to the `Stringable` trait yet so you cannot + use `str(my_list)` yet. + ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 267274dec7..89b04f72b7 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -226,7 +226,7 @@ import os def main(): all_paths = os.get_cwd_and_paths() - print(__type_of(all_paths).__str__(all_paths)) + print(all_paths.__str__()) ``` We also need to set the following environment variable that tells Mojo to diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index cccbb4d2a2..1986f624de 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -660,7 +660,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ return _ListIter[forward=False](len(self[]), self) - @staticmethod fn __str__[U: RepresentableCollectionElement](self: List[U]) -> String: """Returns a string representation of a `List`. @@ -669,7 +668,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): ```mojo var my_list = List[Int](1, 2, 3) - print(__type_of(my_list).__str__(my_list)) + print(my_list.__str__()) ``` When the compiler supports conditional methods, then a simple `str(my_list)` will @@ -677,9 +676,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): The elements' type must implement the `__repr__()` for this to work. - Args: - self: The list to represent as a string. - Parameters: U: The type of the elements in the list. Must implement the traits `Representable` and `CollectionElement`. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index e2d48462dd..b6e8a138c2 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -649,12 +649,10 @@ def test_constructor_from_other_list_through_pointer(): def test_converting_list_to_string(): var my_list = List[Int](1, 2, 3) - assert_equal(__type_of(my_list).__str__(my_list), "[1, 2, 3]") + assert_equal(my_list.__str__(), "[1, 2, 3]") var my_list4 = List[String]("a", "b", "c", "foo") - assert_equal( - __type_of(my_list4).__str__(my_list4), "['a', 'b', 'c', 'foo']" - ) + assert_equal(my_list4.__str__(), "['a', 'b', 'c', 'foo']") def test_list_count(): @@ -675,23 +673,23 @@ def test_list_add(): # check that original values aren't modified assert_equal(len(a), 3) assert_equal(len(b), 3) - assert_equal(__type_of(c).__str__(c), "[1, 2, 3, 4, 5, 6]") + assert_equal(c.__str__(), "[1, 2, 3, 4, 5, 6]") a += b assert_equal(len(a), 6) - assert_equal(__type_of(a).__str__(a), "[1, 2, 3, 4, 5, 6]") + assert_equal(a.__str__(), "[1, 2, 3, 4, 5, 6]") assert_equal(len(b), 3) a = List[Int](1, 2, 3) a += b^ assert_equal(len(a), 6) - assert_equal(__type_of(a).__str__(a), "[1, 2, 3, 4, 5, 6]") + assert_equal(a.__str__(), "[1, 2, 3, 4, 5, 6]") var d = List[Int](1, 2, 3) var e = List[Int](4, 5, 6) var f = d + e^ assert_equal(len(f), 6) - assert_equal(__type_of(f).__str__(f), "[1, 2, 3, 4, 5, 6]") + assert_equal(f.__str__(), "[1, 2, 3, 4, 5, 6]") var l = List[Int](1, 2, 3) l += List[Int]() @@ -702,13 +700,13 @@ def test_list_mult(): var a = List[Int](1, 2, 3) var b = a * 2 assert_equal(len(b), 6) - assert_equal(__type_of(b).__str__(b), "[1, 2, 3, 1, 2, 3]") + assert_equal(b.__str__(), "[1, 2, 3, 1, 2, 3]") b = a * 3 assert_equal(len(b), 9) - assert_equal(__type_of(b).__str__(b), "[1, 2, 3, 1, 2, 3, 1, 2, 3]") + assert_equal(b.__str__(), "[1, 2, 3, 1, 2, 3, 1, 2, 3]") a *= 2 assert_equal(len(a), 6) - assert_equal(__type_of(a).__str__(a), "[1, 2, 3, 1, 2, 3]") + assert_equal(a.__str__(), "[1, 2, 3, 1, 2, 3]") var l = List[Int](1, 2) l *= 1 From a6927e0d9dc47f38ae87a9f0604fcc880f83a0d1 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 16 May 2024 11:39:04 -0500 Subject: [PATCH 0578/2019] [External] [stdlib] Allow the use of `my_dict.__str__()` (#40047) [External] [stdlib] Allow the use of `my_dict.__str__()` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2674 MODULAR_ORIG_COMMIT_REV_ID: 119fed1c16153554a3659cc1202d66e9adf76aa5 --- docs/changelog.md | 5 +++++ stdlib/src/collections/dict.mojo | 6 +----- stdlib/test/collections/test_dict.mojo | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 872d1e95de..bb1c6b7e3d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -212,6 +212,11 @@ what we publish. - `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) +- `Dict` now has a simplified conversion to `String` with `my_dict.__str__()`. + Note that `Dict` does not conform to the `Stringable` trait so `str(my_dict)` + is not possible yet. + ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + - `List` now has an `index` method that allows one to find the (first) location of an element in a `List` of `EqualityComparable` types. For example: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 09cd460193..7fe0fe7d7a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -551,7 +551,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return len(self).__bool__() - @staticmethod fn __str__[ T: RepresentableKeyElement, U: RepresentableCollectionElement ](self: Dict[T, U]) -> String: @@ -564,7 +563,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var my_dict = Dict[Int, Float64]() my_dict[1] = 1.1 my_dict[2] = 2.2 - dict_as_string = __type_of(my_dict).__str__(my_dict) + dict_as_string = my_dict.__str__() print(dict_as_string) # prints "{1: 1.1, 2: 2.2}" ``` @@ -575,9 +574,6 @@ struct Dict[K: KeyElement, V: CollectionElement]( Note that both they keys and values' types must implement the `__repr__()` method for this to work. See the `Representable` trait for more information. - Args: - self: The Dict to represent as a string. - Parameters: T: The type of the keys in the Dict. Must implement the traits `Representable` and `KeyElement`. diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 27fd1523ec..bcd71b1699 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -66,7 +66,7 @@ def test_dict_string_representation_string_int(): var some_dict = Dict[String, Int]() some_dict["a"] = 1 some_dict["b"] = 2 - dict_as_string = __type_of(some_dict).__str__(some_dict) + dict_as_string = some_dict.__str__() assert_true( some_dict._minimum_size_of_string_representation() <= len(dict_as_string) @@ -80,7 +80,7 @@ def test_dict_string_representation_int_int(): some_dict[4] = 2 some_dict[5] = 3 some_dict[6] = 4 - dict_as_string = __type_of(some_dict).__str__(some_dict) + dict_as_string = some_dict.__str__() # one char per key and value, we should have the minimum size of string possible assert_equal( some_dict._minimum_size_of_string_representation(), len(dict_as_string) From 73d1aab1b547b325456e2ca542ddd9b0d303b64a Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 16 May 2024 12:37:40 -0500 Subject: [PATCH 0579/2019] [External] [stdlib] Allow the syntax `my_list.count(x)` (#40048) [External] [stdlib] Allow the syntax `my_list.count(x)` Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2675 MODULAR_ORIG_COMMIT_REV_ID: 62cc2b90cbeba798e01eedebcd5718a57b53f138 --- docs/changelog.md | 3 +++ stdlib/src/collections/list.mojo | 4 +--- stdlib/test/collections/test_list.mojo | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bb1c6b7e3d..3e6794a808 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -209,6 +209,9 @@ what we publish. whitespace, ASCII lower/uppercase, and so on. ([PR #2555](https://github.com/modularml/mojo/pull/2555) by [@toiletsandpaper](https://github.com/toiletsandpaper)) +- `List` has a simplified syntax to call the `count` method: `my_list.count(x)`. + ([PR #2675](https://github.com/modularml/mojo/pull/2675) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) + - `Dict()` now supports `reversed` for `dict.items()` and `dict.values()`. ([PR #2340](https://github.com/modularml/mojo/pull/2340) by [@jayzhan211](https://github.com/jayzhan211)) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 1986f624de..43bb5d5374 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -727,7 +727,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ return __type_of(self).__str__(self) - @staticmethod fn count[T: ComparableCollectionElement](self: List[T], value: T) -> Int: """Counts the number of occurrences of a value in the list. Note that since we can't condition methods on a trait yet, @@ -735,7 +734,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): ```mojo var my_list = List[Int](1, 2, 3) - print(__type_of(my_list).count(my_list, 1)) + print(my_list.count(1)) ``` When the compiler supports conditional methods, then a simple `my_list.count(1)` will @@ -746,7 +745,6 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): traits `EqualityComparable` and `CollectionElement`. Args: - self: The list to search. value: The value to count. Returns: diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index b6e8a138c2..cdf206341d 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -657,12 +657,12 @@ def test_converting_list_to_string(): def test_list_count(): var list = List[Int](1, 2, 3, 2, 5, 6, 7, 8, 9, 10) - assert_equal(1, __type_of(list).count(list, 1)) - assert_equal(2, __type_of(list).count(list, 2)) - assert_equal(0, __type_of(list).count(list, 4)) + assert_equal(1, list.count(1)) + assert_equal(2, list.count(2)) + assert_equal(0, list.count(4)) var list2 = List[Int]() - assert_equal(0, __type_of(list2).count(list2, 1)) + assert_equal(0, list2.count(1)) def test_list_add(): From ddff47cf9dfd784ca7f810626a050cb54198de04 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 16 May 2024 13:15:38 -0500 Subject: [PATCH 0580/2019] [stdlib] feature: Add `memcpy()` for `UnsafePointer[Scalar[_]]` This switches over memcpy calls mostly in string.mojo that were forced to do casts to DTypePointer to call memcpy. Also: * Add a straggler @always_inline on StringSlice.__init__(unsafe_from_utf8). * Remove unnecessary explicit self type in StringLiteral slice methods MODULAR_ORIG_COMMIT_REV_ID: f9f0fe82d60478c1cbd82cf8179f797a5092a85f --- docs/changelog.md | 2 ++ stdlib/src/builtin/object.mojo | 6 +++- stdlib/src/builtin/string.mojo | 33 +++++++++++-------- stdlib/src/builtin/string_literal.mojo | 14 +++----- stdlib/src/memory/memory.mojo | 21 ++++++++++++ stdlib/src/utils/string_slice.mojo | 1 + stdlib/test/memory/test_memory.mojo | 44 ++++++++++++++++++++++++++ 7 files changed, 98 insertions(+), 23 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3e6794a808..fc0d8700c6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -192,6 +192,8 @@ what we publish. - Add new `ImmStaticLifetime` and `MutStaticLifetime` helpers +- Add new `memcpy` overload for `UnsafePointer[Scalar[_]]` pointers. + - `Dict` now implements `get(key)` and `get(key, default)` functions. ([PR #2519](https://github.com/modularml/mojo/pull/2519) by [@martinvuyk](https://github.com/martinvuyk)) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 31ce913b23..413f6b60ad 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -337,7 +337,11 @@ struct _ObjectImpl(CollectionElement, Stringable): var impl = _ImmutableString( UnsafePointer[Int8].alloc(str.length), str.length ) - memcpy(impl.data, DTypePointer[DType.int8](str.data), str.length) + memcpy( + dest=impl.data, + src=str.data, + count=str.length, + ) return impl if self.is_list(): return self.get_as_list().copy() diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index d9c5934c11..472c70a964 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -620,7 +620,14 @@ struct String( var length = len(str) var buffer = Self._buffer_type() buffer.resize(length + 1, 0) - memcpy(rebind[DTypePointer[DType.uint8]](buffer.data), str.data, length) + memcpy( + # TODO(modularml/mojo#2317): + # Remove this bitcast after transition to UInt8 for string data + # is complete. + dest=buffer.data.bitcast[UInt8](), + src=str.data, + count=length, + ) buffer[length] = 0 self._buffer = buffer^ @@ -640,9 +647,9 @@ struct String( var buffer = Self._buffer_type() buffer.resize(length + 1, 0) memcpy( - DTypePointer(buffer.data), - DTypePointer(str_slice.as_bytes_slice().unsafe_ptr()), - length, + dest=buffer.data, + src=str_slice.as_bytes_slice().unsafe_ptr(), + count=length, ) buffer[length] = 0 self._buffer = buffer^ @@ -981,14 +988,14 @@ struct String( var buffer = Self._buffer_type() buffer.resize(total_len + 1, 0) memcpy( - DTypePointer(buffer.data), - self.unsafe_ptr(), - self_len, + dest=buffer.data, + src=self.unsafe_ptr(), + count=self_len, ) memcpy( - DTypePointer(buffer.data + self_len), - other.unsafe_ptr(), - other_len + 1, # Also copy the terminator + dest=buffer.data + self_len, + src=other.unsafe_ptr(), + count=other_len + 1, # Also copy the terminator ) return Self(buffer^) @@ -1641,9 +1648,9 @@ struct String( buf.resize(count, 0) for i in range(n): memcpy( - rebind[DTypePointer[DType.int8]](buf.data) + len_self * i, - self.unsafe_ptr(), - len_self, + dest=buf.data + len_self * i, + src=self.unsafe_ptr(), + count=len_self, ) return String(buf^) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index cde609618a..3d39762047 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -237,16 +237,14 @@ struct StringLiteral( return self.__str__().__repr__() @always_inline - fn as_string_slice( - self: Reference[Self, _, _] - ) -> StringSlice[False, ImmStaticLifetime]: + fn as_string_slice(self) -> StringSlice[False, ImmStaticLifetime]: """Returns a string slice of this static string literal. Returns: A string slice pointing to this static string literal. """ - var bytes = self[].as_bytes_slice() + var bytes = self.as_bytes_slice() # FIXME(MSTDL-160): # Enforce UTF-8 encoding in StringLiteral so this is actually @@ -254,9 +252,7 @@ struct StringLiteral( return StringSlice(unsafe_from_utf8=bytes) @always_inline - fn as_bytes_slice( - self: Reference[Self, _, _] - ) -> Span[Int8, False, ImmStaticLifetime]: + fn as_bytes_slice(self) -> Span[Int8, False, ImmStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -264,11 +260,11 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - var ptr = rebind[UnsafePointer[Int8]](self[].unsafe_ptr()) + var ptr = rebind[UnsafePointer[Int8]](self.unsafe_ptr()) return Span[Int8, False, ImmStaticLifetime]( unsafe_ptr=ptr, - len=self[]._byte_length(), + len=self._byte_length(), ) fn format_to(self, inout writer: Formatter): diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index e3677edb9b..f3bc30e4ef 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -309,6 +309,27 @@ fn memcpy(dest: DTypePointer, src: __type_of(dest), count: Int): memcpy(dest.address, src.address, count) +@always_inline +fn memcpy[ + inferred dtype: DType +](*, dest: UnsafePointer[Scalar[dtype]], src: __type_of(dest), count: Int): + """Copies a memory area. + + Parameters: + dtype: *Inferred* The dtype of the data to copy. + + Args: + dest: The destination pointer. + src: The source pointer. + count: The number of elements to copy (not bytes!). + """ + memcpy( + dest=DTypePointer(dest), + src=DTypePointer(src), + count=count, + ) + + # ===----------------------------------------------------------------------===# # memset # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 27518d1792..973cc59772 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -44,6 +44,7 @@ struct StringSlice[ # Initializers # ===------------------------------------------------------------------===# + @always_inline fn __init__( inout self, owned unsafe_from_utf8: Span[Int8, is_mutable, lifetime] ): diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index c65a75a805..55eedb1426 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -465,6 +465,49 @@ def test_dtypepointer_scatter(): ptr.free() +def test_memcpy_unsafe_pointer(): + # Tests memcpy for the UnsafePointer type + # Note: + # Eventually as DTypePointer and LegacyPointer are fully replaced with + # UnsafePointer, this test will be redundant as all the other tests in + # this file will have been updated to use `UnsafePointer`. + + var list_a = List[Int8](capacity=10) + var list_b = List[Int8](1, 2, 3, 4, 5) + + assert_equal(len(list_b), 5) + + var dest_ptr: UnsafePointer[Int8] = list_a.unsafe_ptr() + + memcpy( + dest=dest_ptr, + src=list_b.unsafe_ptr(), + count=len(list_b), + ) + memcpy( + dest=dest_ptr + 5, + src=list_b.unsafe_ptr(), + count=len(list_b), + ) + + _ = list_b^ + + # Mark the initialized size of list_a. + list_a.size = 10 + + assert_equal(len(list_a), 10) + assert_equal(list_a[0], 1) + assert_equal(list_a[1], 2) + assert_equal(list_a[2], 3) + assert_equal(list_a[3], 4) + assert_equal(list_a[4], 5) + assert_equal(list_a[5], 1) + assert_equal(list_a[6], 2) + assert_equal(list_a[7], 3) + assert_equal(list_a[8], 4) + assert_equal(list_a[9], 5) + + def main(): test_memcpy() test_memcpy_dtype() @@ -472,6 +515,7 @@ def main(): test_memcmp_overflow() test_memcmp_simd() test_memcmp_extensive() + test_memcpy_unsafe_pointer() test_memset() test_dtypepointer_string() From 974fb2fc25091aba39fada3e533cf6c0c88f8ebc Mon Sep 17 00:00:00 2001 From: Brian M Johnson <1DT21AI037@dsatm.edu.in> Date: Thu, 16 May 2024 13:35:14 -0500 Subject: [PATCH 0581/2019] [External] [docs] Mojo Manual typo fixes (#40056) [External] [docs] Mojo Manual typo fixes Fix various typos in the Mojo Manual: `functions.ipynb`: - Changed "matter personal taste" to "matter of personal taste" - Changed "Current there are some differences" to "Currently there are some differences" `why-mojo.md` - Changed "here in 2023" to "here in the present" (to make it age better as time goes on) - Changed "an embedded domain-specific languages" to "embedded domain-specific languages" - Added information about Python 3.13 to the `Improving CPython and JIT compiling Python` section - Changed "the Python package ecosystems has" to "the Python package ecosystem has" - Changed "but don't see" to "but we don't see" `basics.ipynb`: - Changed "we try not assume" to "we try not to assume" `get-started.md`: - Changed "double-check your code looks" to "double-check that your code looks" `types.ipynb`: - Changed "to string" to "to a string" - Changed "They dictionary's key type" to "The dictionary's key type" - Changed "`Set` type represent" to "`Set` type represents" - Changed "The `Optional`" to "An `Optional`" - Changed ",where" to ", where" - Changed "is For" to "For" `structs.ipynb`: - Changed "declares `self` is a" to "declares `self` as a" - Changed "the section about Value lifecycle" to "the Value lifecycle section" `packages.md`: - Changed "Mojo module may" to "A Mojo module may" `ownership.ipynb`: - Changed "who's" to "whose" - Changed "the `def` and `fn` functions are interchangeable" to "`def` and `fn` functions are interchangeable" `death.ipynb`: - Changed "OK in the this case" to "OK in this case" - Changed "field technically no longer used" to "field is technically no longer used" `debugging.ipynb`: - Changed "If this a a" to "If this is a" Co-authored-by: Brian M Johnson <1DT21AI037@dsatm.edu.in> Closes modularml/mojo#2668 MODULAR_ORIG_COMMIT_REV_ID: 47e07b692bbef190e420345791adcfcda6da9388 --- docs/manual/basics.ipynb | 2 +- docs/manual/functions.ipynb | 4 ++-- docs/manual/get-started.md | 2 +- docs/manual/lifecycle/death.ipynb | 4 ++-- docs/manual/packages.md | 2 +- docs/manual/structs.ipynb | 6 +++--- docs/manual/types.ipynb | 12 ++++++------ docs/manual/values/ownership.ipynb | 4 ++-- docs/tools/debugging.ipynb | 2 +- docs/why-mojo.md | 21 +++++++++++++-------- 10 files changed, 32 insertions(+), 27 deletions(-) diff --git a/docs/manual/basics.ipynb b/docs/manual/basics.ipynb index 2ca082a648..59e6caffae 100644 --- a/docs/manual/basics.ipynb +++ b/docs/manual/basics.ipynb @@ -48,7 +48,7 @@ "Mojo is a young language and there are many [features still\n", "missing](/mojo/roadmap.html). As such, Mojo is currently **not** meant for\n", "beginners. Even this basics section assumes some programming experience.\n", - "However, throughout the Mojo Manual, we try not assume experience with any\n", + "However, throughout the Mojo Manual, we try not to assume experience with any\n", "particular language.\n", "\n", ":::" diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index 1597cea83b..4f50c6053c 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -26,7 +26,7 @@ "default behaviors, as described on this page.\n", "\n", "We believe both `def` and `fn` have good use cases and don't consider either to\n", - "be better than the other. Deciding which to use is a matter personal taste as\n", + "be better than the other. Deciding which to use is a matter of personal taste as\n", "to which style best fits a given task.\n", "\n", "We believe Mojo's flexibility in this regard is a superpower that allows you to\n", @@ -347,7 +347,7 @@ "metadata": {}, "source": [ "Inside the function body, the variadic argument is available as an iterable list\n", - "for ease of use. Current there are some differences in handling the list \n", + "for ease of use. Currently there are some differences in handling the list \n", "depending on whether the arguments are register-passable types (such as `Int`)\n", "or memory-only types (such as `String`). TODO: We hope to remove these\n", "differences in the future.\n", diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index b94916066e..cb265a15de 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -86,7 +86,7 @@ Now let's write the code in a Mojo source file and run it with the Hello, world! ``` -If this didn't work for you, double-check your code looks exactly like the code +If this didn't work for you, double-check that your code looks exactly like the code in step 1, and make sure you correctly [installed MAX](/max/install) (it includes Mojo). diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 51299e0b29..4df4001373 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -232,7 +232,7 @@ "\n", "So in the `HeapArray` example above, calling `free()` on the pointer releases\n", "the memory, but doesn't call the destructors on the stored `Int` values. That's\n", - "OK in the this case, because trivial types like `Int` have no-op destructors.\n", + "OK in this case, because trivial types like `Int` have no-op destructors.\n", "However, in the general case—if you're storing values that _might_ have\n", "functional destructors—it's not enough to call `free()` on the pointer. You\n", "should also ensure that Mojo completely destroys all the allocated types\n", @@ -524,7 +524,7 @@ "\n", "For instance, perhaps you're building a type with a field that carries a pointer\n", "to another field. The Mojo compiler won't be able to reason about the pointer,\n", - "so it might destroy a field (`obj1`) when that field technically no longer\n", + "so it might destroy a field (`obj1`) when that field is technically no longer\n", "used, even though another field (`obj2`) still holds a pointer to part of it.\n", "So, you might need to keep `obj1` alive until you can execute some special\n", "logic in the destructor or move initializer.\n", diff --git a/docs/manual/packages.md b/docs/manual/packages.md index b1b4db3030..aec79c6583 100644 --- a/docs/manual/packages.md +++ b/docs/manual/packages.md @@ -74,7 +74,7 @@ package, as described in the next section. :::note -Mojo module may include a `main()` function and may also be +A Mojo module may include a `main()` function and may also be executable, but that's generally not the practice and modules typically include APIs to be imported and used in other Mojo programs. diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index ace17b025c..2067504437 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -93,7 +93,7 @@ "Notice that the first argument in the `__init__()` method is `inout self`. For\n", "now, ignore `inout` (it's an [argument\n", "convention](/mojo/manual/values/ownership.html#argument-conventions) that\n", - "declares `self` is a mutable reference); all you need to know right now is that\n", + "declares `self` as a mutable reference); all you need to know right now is that\n", "`self` must be the first argument. It references the current struct instance\n", "(it allows code in the method to refer to \"itself\"). *When you call the\n", "constructor, you never pass a value for `self`—Mojo passes it in \n", @@ -353,8 +353,8 @@ " methods define the behavior for other lifecycle events such as how to copy or\n", " move a value.\n", "\n", - "You can learn all about the lifecycle special methods in the section about\n", - "[Value lifecycle](/mojo/manual/lifecycle/). However, most structs are simple\n", + "You can learn all about the lifecycle special methods in the [Value\n", + "lifecycle](/mojo/manual/lifecycle/) section. However, most structs are simple\n", "aggregations of other types, so unless your type requires custom behaviors when\n", "an instance is created, copied, moved, or destroyed, you can synthesize the\n", "essential lifecycle methods you need (and save yourself some time) by adding\n", diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index be2e84e9ed..ece8b8fb55 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -121,7 +121,7 @@ "\n", "- Space between consecutive numbers. The space between consecutive numbers is\n", " variable across the range of a floating-point number format. For numbers close\n", - " to zero, the distance between consecutive numbers is very small. is For large\n", + " to zero, the distance between consecutive numbers is very small. For large\n", " positive and negative numbers, the space between consecutive numbers is\n", " greater than 1, so it may not be possible to represent consecutive integers.\n", "\n", @@ -403,7 +403,7 @@ "Most standard library types conform to the \n", "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, which represents\n", "a type that can be converted to a string. Use `String(value)` or `str(value)` to\n", - "explicitly convert a value to string.\n", + "explicitly convert a value to a string.\n", "\n", "When concatenating values to a string using the `+` operator, `Stringable` types\n", "implicitly convert to `String`:" @@ -744,7 +744,7 @@ "var values = Dict[String, Float64]()\n", "```\n", "\n", - "They dictionary's key type must conform to the \n", + "The dictionary's key type must conform to the \n", "[`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, and value \n", "elements must conform to the \n", "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait.\n", @@ -788,7 +788,7 @@ "source": [ "### Set\n", "\n", - "The [`Set`](/mojo/stdlib/collections/set/Set) type represent a set of unique\n", + "The [`Set`](/mojo/stdlib/collections/set/Set) type represents a set of unique\n", "values. You can add and remove elements from the set, test whether a value \n", "exists in the set, and perform set algebra operations, like unions and \n", "intersections between two sets. \n", @@ -830,7 +830,7 @@ "source": [ "### Optional\n", "\n", - "The [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", + "An [`Optional`](/mojo/stdlib/collections/optional/Optional) represents a \n", "value that may or may not be present. Like the other collection types, it is\n", "generic, and can hold any type that conforms to the\n", "[`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait." @@ -978,7 +978,7 @@ "```\n", "\n", "You can read this as `any_type_function` has an argument, `value` of type\n", - "`ValueType`,where `ValueType` is a register-passable type, determined at\n", + "`ValueType`, where `ValueType` is a register-passable type, determined at\n", "compile time. \n", "\n", "There is still some code like this in the standard library, but it's gradually\n", diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index 5201c34924..20674db040 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -139,7 +139,7 @@ "\n", "Because Mojo does not allow a mutable reference to overlap with another mutable\n", "or immutable reference, it provides a predictable programming model about which\n", - "references are and aren't valid (an invalid reference is one who's lifetime has\n", + "references are and aren't valid (an invalid reference is one whose lifetime has\n", "ended, perhaps because the value ownership was transferred). Importantly, this\n", "logic allows Mojo to immediately [destroy\n", "values](/mojo/manual/lifecycle/death.html) when their lifetime ends." @@ -469,7 +469,7 @@ "## Comparing `def` and `fn` argument conventions\n", "\n", "As mentioned in the section about\n", - "[functions](/mojo/manual/functions.html), the `def` and `fn` functions\n", + "[functions](/mojo/manual/functions.html), `def` and `fn` functions\n", "are interchangeable, as far as a caller is concerned, and they can both\n", "accomplish the same things. It's only the inside that differs, and Mojo's `def`\n", "function is essentially just sugaring for the `fn` function:\n", diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 9167270a84..216f5cb11e 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -266,7 +266,7 @@ "restart the debug session. Otherwise, detach from the target process and\n", "reattach to it. \n", "\n", - "- **Stop**: If this a a `launch` session, terminate the current program. Otherwise,\n", + "- **Stop**: If this is a `launch` session, terminate the current program. Otherwise,\n", "detach from the target process without killing it.\n", "\n", "The debugger currently has the following limitations:\n", diff --git a/docs/why-mojo.md b/docs/why-mojo.md index 7afe2bf093..6b89d1e946 100644 --- a/docs/why-mojo.md +++ b/docs/why-mojo.md @@ -59,7 +59,7 @@ systems-level code for AI workloads. ## A member of the Python family Our core mission for Mojo includes innovations in compiler internals and -support for current and emerging accelerators, but don't see any need to +support for current and emerging accelerators, but we don't see any need to innovate in language _syntax_ or _community_. So we chose to embrace the Python ecosystem because it is so widely used, it is loved by the AI ecosystem, and because we believe it is a really nice language. @@ -206,7 +206,7 @@ which makes Python single-threaded. While there are many active projects underway to improve these challenges, the issues brought by Python go deeper and are particularly impactful in the AI field. Instead of talking about those technical limitations in detail, we'll talk about their implications here in -2023. +the present. Note that everywhere we refer to Python in this section is referring to the CPython implementation. We'll talk about other implementations later. @@ -233,7 +233,7 @@ significant challenges in this regard. Beyond the fundamental nature of how the two-world problem creates system complexity, it makes everything else in the ecosystem more complicated. Debuggers generally can't step across Python and C code, and those that can -aren't widely accepted. It's painful that the Python package ecosystems has to +aren't widely accepted. It's painful that the Python package ecosystem has to deal with C/C++ code in addition to Python. Projects like PyTorch, with significant C++ investments, are intentionally trying to move more of their codebase to Python because they know it gains usability. @@ -283,9 +283,14 @@ This work is fantastic because it incrementally improves the current CPython implementation. For example, Python 3.11 has increased performance 10-60% over Python 3.10 through internal improvements, and [Python 3.12](https://github.com/faster-cpython/ideas/wiki/Python-3.12-Goals) aims to -go further with a trace optimizer. Many other projects are attempting to tame -the GIL, and projects like PyPy (among many others) have used JIT compilation -and tracing approaches to speed up Python. +go further with a trace optimizer. [Python +3.13](https://github.com/faster-cpython/ideas/blob/main/3.13/README.md) adds a +[JIT compiler](https://peps.python.org/pep-0744/) to CPython, enables the use +of [multiple subinterpreters](https://peps.python.org/pep-0554/) in a single +Python process (thus sidestepping the GIL) and speeds up memory management. +Many other projects are attempting to tame the GIL, and projects like PyPy +(among many others) have used JIT compilation and tracing approaches to speed +up Python. While we are fans of these great efforts, and feel they are valuable and exciting to the community, they unfortunately do not satisfy our needs at @@ -344,8 +349,8 @@ wide range of hardware. ### Embedded DSLs in Python -Another common approach is to build an embedded domain-specific languages -(DSLs) in Python, typically installed with a Python decorator. There are many +Another common approach is to build embedded domain-specific languages (DSLs) +in Python, typically installed with a Python decorator. There are many examples of this (the `@tf.function` decorator in TensorFlow, the `@triton.jit` in OpenAI's Triton programming model, etc.). A major benefit of these systems is that they maintain compatibility with the Python From a4f176d275f003f4778b4b0875b0d9275cde26a5 Mon Sep 17 00:00:00 2001 From: Manuel Saelices Date: Thu, 16 May 2024 13:39:11 -0500 Subject: [PATCH 0582/2019] [External] [stdlib] Support for negative indexes in `List._reverse(start)` method and out of bound validation (#39464) [External] [stdlib] Support for negative indexes in `List._reverse(start)` method and out of bound validation ## Changes * The `_reverse(start)` private method in the `collections.List` struct does not support negative indexes. * Check out-of-bound indexes in the `List._reverse()` method * Tests ## Ref Fixes #2496 --------- Co-authored-by: Manuel Saelices Co-authored-by: Connor Gray Closes modularml/mojo#2520 MODULAR_ORIG_COMMIT_REV_ID: 2662cfba2e6c1a602e8fa11880f29f8add446506 --- stdlib/src/collections/list.mojo | 24 ++++++++++----------- stdlib/test/collections/test_list.mojo | 29 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 43bb5d5374..391e65b56f 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -438,29 +438,27 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn reverse(inout self): """Reverses the elements of the list.""" - - self._reverse() + try: + self._reverse() + except: + abort("unreachable: default _reverse start unexpectedly fails") # This method is private to avoid exposing the non-Pythonic `start` argument. @always_inline - fn _reverse(inout self, start: Int = 0): + fn _reverse(inout self, start: Int = 0) raises: """Reverses the elements of the list at positions after `start`. Args: - start: A non-negative integer indicating the position after which to reverse elements. + start: An integer indicating the position after which to reverse elements. """ + var start_idx = start if start >= 0 else len(self) + start + if start_idx < 0 or start_idx > len(self): + raise "IndexError: start index out of range." - # TODO(polish): Support a negative slice-like start position here that - # counts from the end. - debug_assert( - start >= 0, - "List reverse start position must be non-negative", - ) - - var earlier_idx = start + var earlier_idx = start_idx var later_idx = len(self) - 1 - var effective_len = len(self) - start + var effective_len = len(self) - start_idx var half_len = effective_len // 2 for _ in range(half_len): diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index cdf206341d..ea15745c3a 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -221,6 +221,35 @@ def test_list_reverse(): assert_equal(vec[3], 4) assert_equal(vec[4], 3) + # + # Test reversing the list [1, 2, 3] with negative indexes + # + + vec = List[Int]() + vec.append(1) + vec.append(2) + vec.append(3) + + vec._reverse(start=-2) + + assert_equal(len(vec), 3) + assert_equal(vec[0], 1) + assert_equal(vec[1], 3) + assert_equal(vec[2], 2) + + # + # Test reversing the list [1, 2] with out of bounds indexes + # + vec = List[Int]() + vec.append(1) + vec.append(2) + + with assert_raises(contains="IndexError"): + vec._reverse(start=-3) + + with assert_raises(contains="IndexError"): + vec._reverse(start=3) + # # Test edge case of reversing the list [1, 2, 3] but starting after the # last element. From d83dc16df87babd0104b37f2f627da65001a23f0 Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Thu, 16 May 2024 15:00:51 -0500 Subject: [PATCH 0583/2019] [External] [stdlib] Add `List.__contains__` (#40064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [External] [stdlib] Add `List.__contains__` The conformance is done by hand with `constrained`, `_type_is_eq` and `rebind` so that users can do: ```mojo var x = List[Int](1,2,3) if 3 in x: print("ok") ``` If value would be T2, we would not be able to do it. Note: this is PR as https://github.com/modularml/mojo/pull/2662 and https://github.com/modularml/mojo/pull/2665 — started over again. Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2667 MODULAR_ORIG_COMMIT_REV_ID: 71b7db77de295c088f2207b2ea83d23c6397c4dd --- docs/changelog.md | 3 +++ stdlib/src/collections/list.mojo | 28 ++++++++++++++++++++++++++ stdlib/test/collections/test_list.mojo | 14 +++++++++++++ 3 files changed, 45 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index fc0d8700c6..f1a032807a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -222,6 +222,9 @@ what we publish. is not possible yet. ([PR #2674](https://github.com/modularml/mojo/pull/2674) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) +- `List()` now supports `__contains__`. + ([PR #2667](https://github.com/modularml/mojo/pull/2667) by [@rd4com](https://github.com/rd4com/)) + - `List` now has an `index` method that allows one to find the (first) location of an element in a `List` of `EqualityComparable` types. For example: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 391e65b56f..b57989363e 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -22,6 +22,7 @@ from collections import List from memory import UnsafePointer, Reference from memory.unsafe_pointer import move_pointee, move_from_pointee +from sys.intrinsics import _type_is_eq from .optional import Optional # ===----------------------------------------------------------------------===# @@ -762,3 +763,30 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): The UnsafePointer to the underlying memory. """ return self.data + + @always_inline + fn __contains__[ + T2: ComparableCollectionElement + ](self: List[T2], value: T) -> Bool: + """Verify if a given value is present in the list. + + ```mojo + var x = List[Int](1,2,3) + if 3 in x: print("x contains 3") + ``` + Parameters: + T2: The type of the elements in the list. Must implement the + traits `EqualityComparable` and `CollectionElement`. + + Args: + value: The value to find. + + Returns: + True if the value is contained in the list, False otherwise. + """ + + constrained[_type_is_eq[T, T2](), "value type is not self.T"]() + for i in self: + if i[] == rebind[T2](value): + return True + return False diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index ea15745c3a..873803f122 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -746,6 +746,19 @@ def test_list_mult(): assert_equal(len(List[Int](1, 2, 3) * 0), 0) +def test_list_contains(): + var x = List[Int](1, 2, 3) + assert_false(0 in x) + assert_true(1 in x) + assert_false(4 in x) + + # TODO: implement List.__eq__ for Self[ComparableCollectionElement] + # var y = List[List[Int]]() + # y.append(List(1,2)) + # assert_equal(List(1,2) in y,True) + # assert_equal(List(0,1) in y,False) + + def main(): test_mojo_issue_698() test_list() @@ -773,3 +786,4 @@ def main(): test_list_count() test_list_add() test_list_mult() + test_list_contains() From a88e7ffdf875a9283264b53eb15562a7e0d92fd6 Mon Sep 17 00:00:00 2001 From: Gabriel de Marmiesse Date: Thu, 16 May 2024 15:46:39 -0500 Subject: [PATCH 0584/2019] [External] [stdlib] Fix incorrect number of elements in `StaticTuple` (#40083) [External] [stdlib] Fix incorrect number of elements in `StaticTuple` We could technically have a check at compile-time but I'm not sure it's worth the effort as `StaticTuple` is deprecated in favor of `InlineArray`. Related to https://github.com/modularml/mojo/issues/2687 Co-authored-by: Gabriel de Marmiesse Closes modularml/mojo#2689 MODULAR_ORIG_COMMIT_REV_ID: b965330e55c49074b61d9603414c033580a9e096 --- stdlib/test/utils/test_tuple.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 6eedeffb83..275528d507 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -21,7 +21,7 @@ def test_static_tuple(): var tup1 = StaticTuple[Int, 1](1) assert_equal(tup1[0], 1) - var tup2 = StaticTuple[Int, 2](1) + var tup2 = StaticTuple[Int, 2](1, 1) assert_equal(tup2[0], 1) assert_equal(tup2[1], 1) From fe9d3f5426eff0c28a4b63eee7d7a6e6420ae124 Mon Sep 17 00:00:00 2001 From: Kern Handa Date: Thu, 16 May 2024 16:16:22 -0500 Subject: [PATCH 0585/2019] [External] [mojo-stdlib] Add b16encode and b16decode to base64 module (#39561) [External] [mojo-stdlib] Add b16encode and b16decode to base64 module This change adds `b16encode` and `b16decode` to the `base64` module, as well as relevant tests. The initial implementation is simple and can be optimized in future iterations (if needed). #474 Co-authored-by: Kern Handa Closes modularml/mojo#2584 MODULAR_ORIG_COMMIT_REV_ID: 341c7f3656c041f37d76e6c8b382b92c8762f74e --- docs/changelog.md | 4 ++ stdlib/src/base64/__init__.mojo | 2 +- stdlib/src/base64/base64.mojo | 82 +++++++++++++++++++++++++++++ stdlib/test/base64/test_base64.mojo | 38 ++++++++++++- 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f1a032807a..ba66c01c6a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -137,6 +137,10 @@ what we publish. - Base64 decoding support has been added. ([PR #2364](https://github.com/modularml/mojo/pull/2364) by [@mikowals](https://github.com/mikowals)) +- Add Base16 encoding and decoding support. + ([PR #2584](https://github.com/modularml/mojo/pull/2584) + by [@kernhanda](https://github.com/kernhanda)) + - Add `repr()` function and `Representable` trait. ([PR #2361](https://github.com/modularml/mojo/pull/2361) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) diff --git a/stdlib/src/base64/__init__.mojo b/stdlib/src/base64/__init__.mojo index d76ce79f8f..adf2f51b40 100644 --- a/stdlib/src/base64/__init__.mojo +++ b/stdlib/src/base64/__init__.mojo @@ -12,4 +12,4 @@ # ===----------------------------------------------------------------------=== # """Implements the base64 package.""" -from .base64 import b64encode, b64decode +from .base64 import b64encode, b64decode, b16encode, b16decode diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 6ac3e811c6..5f854dc023 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -151,3 +151,85 @@ fn b64decode(str: String) -> String: p.append(0) return p + + +# ===----------------------------------------------------------------------===# +# b16encode +# ===----------------------------------------------------------------------===# + + +fn b16encode(str: String) -> String: + """Performs base16 encoding on the input string. + + Args: + str: The input string. + + Returns: + Base16 encoding of the input string. + """ + alias lookup = "0123456789ABCDEF" + var b16chars = lookup.unsafe_ptr() + + var length = len(str) + var out = List[Int8](capacity=length * 2 + 1) + + @parameter + @always_inline + fn str_bytes(idx: Int) -> Int: + return int(str.unsafe_ptr().bitcast[DType.uint8]()[idx]) + + for i in range(length): + var str_byte = str_bytes(i) + var hi = str_byte >> 4 + var lo = str_byte & 0b1111 + out.append(b16chars[hi]) + out.append(b16chars[lo]) + + out.append(0) + + return String(out^) + + +# ===----------------------------------------------------------------------===# +# b16decode +# ===----------------------------------------------------------------------===# + + +@always_inline +fn b16decode(str: String) -> String: + """Performs base16 decoding on the input string. + + Args: + str: A base16 encoded string. + + Returns: + The decoded string. + """ + + # TODO: Replace with dict literal when possible + @parameter + @always_inline + fn decode(c: String) -> Int: + var char_val = ord(c) + + if ord("A") <= char_val <= ord("Z"): + return char_val - ord("A") + 10 + elif ord("a") <= char_val <= ord("z"): + return char_val - ord("a") + 10 + elif ord("0") <= char_val <= ord("9"): + return char_val - ord("0") + + return -1 + + var n = len(str) + debug_assert(n % 2 == 0, "Input length must be divisible by 2") + + var p = List[Int8](capacity=int(n / 2) + 1) + + for i in range(0, n, 2): + var hi = str[i] + var lo = str[i + 1] + p.append(decode(hi) << 4 | decode(lo)) + + p.append(0) + return p diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 8b6e078775..4f7006abd0 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from base64 import b64encode, b64decode +from base64 import b64encode, b64decode, b16encode, b16decode from testing import assert_equal @@ -52,6 +52,42 @@ def test_b64decode(): assert_equal(b64decode("QUJDREVGYWJjZGVm"), "ABCDEFabcdef") +def test_b16encode(): + assert_equal(b16encode("a"), "61") + + assert_equal(b16encode("fo"), "666F") + + assert_equal(b16encode("Hello Mojo!!!"), "48656C6C6F204D6F6A6F212121") + + assert_equal(b16encode("Hello 🔥!!!"), "48656C6C6F20F09F94A5212121") + + assert_equal( + b16encode("the quick brown fox jumps over the lazy dog"), + "74686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F67", + ) + + assert_equal(b16encode("ABCDEFabcdef"), "414243444546616263646566") + + +def test_b16decode(): + assert_equal(b16decode("61"), "a") + + assert_equal(b16decode("666F"), "fo") + + assert_equal(b16decode("48656C6C6F204D6F6A6F212121"), "Hello Mojo!!!") + + assert_equal(b16decode("48656C6C6F20F09F94A5212121"), "Hello 🔥!!!") + + assert_equal( + b16encode("the quick brown fox jumps over the lazy dog"), + "74686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F67", + ) + + assert_equal(b16decode("414243444546616263646566"), "ABCDEFabcdef") + + def main(): test_b64encode() test_b64decode() + test_b16encode() + test_b16decode() From f01f35c7be26219c7fe8d7994906808f8c8c0c07 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 16 May 2024 14:36:50 -0700 Subject: [PATCH 0586/2019] [mojo-stdlib] Change `__reversed__` to return a `_StridedRange` (NFC) (#40099) BEGIN_PUBLIC [stdlib] Change `__reversed__` to return a `_StridedRange` (NFC) This PR changes the `__reversed__` method on the `ReversibleRange` trait to reutrn a `_StridedRange` instead of a `_StridedRangeIterator`. This allows the `__iter__` method to be removed from `_StridedRangeIterator`, since it didn't make a lot of sense to return an iterator where a range was required. This is required deck chair rearranging to support `@parameter for`. END_PUBILC MODULAR_ORIG_COMMIT_REV_ID: bb9c9ea1ff161280c75e70233ef5c860368238fa --- stdlib/src/builtin/range.mojo | 16 ++++++---------- stdlib/src/builtin/reversed.mojo | 6 +++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 2d30acb3c0..a953425fb1 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -86,8 +86,8 @@ struct _ZeroStartingRange(Sized, ReversibleRange): return idx @always_inline("nodebug") - fn __reversed__(self) -> _StridedRangeIterator: - return _StridedRangeIterator(self.end - 1, -1, -1) + fn __reversed__(self) -> _StridedRange: + return range(self.end - 1, -1, -1) @value @@ -117,8 +117,8 @@ struct _SequentialRange(Sized, ReversibleRange): return self.start + idx @always_inline("nodebug") - fn __reversed__(self) -> _StridedRangeIterator: - return _StridedRangeIterator(self.end - 1, self.start - 1, -1) + fn __reversed__(self) -> _StridedRange: + return range(self.end - 1, self.start - 1, -1) @value @@ -128,10 +128,6 @@ struct _StridedRangeIterator(Sized): var end: Int var step: Int - @always_inline("nodebug") - fn __iter__(self) -> Self: - return self - @always_inline fn __len__(self) -> Int: if self.step > 0 and self.start < self.end: @@ -189,12 +185,12 @@ struct _StridedRange(Sized, ReversibleRange): return self.start + idx * self.step @always_inline("nodebug") - fn __reversed__(self) -> _StridedRangeIterator: + fn __reversed__(self) -> _StridedRange: var shifted_end = self.end - _sign(self.step) var start = shifted_end - ((shifted_end - self.start) % self.step) var end = self.start - self.step var step = -self.step - return _StridedRangeIterator(start, end, step) + return range(start, end, step) @always_inline("nodebug") diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 25986069ef..c449cc5479 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from .range import _StridedRangeIterator +from .range import _StridedRange from collections.list import _ListIter @@ -43,7 +43,7 @@ trait ReversibleRange: # iterators currently check __len__() instead of raising an exception # so there is no ReversibleRaising trait yet. - fn __reversed__(self) -> _StridedRangeIterator: + fn __reversed__(self) -> _StridedRange: """Get a reversed iterator for the type. **Note**: iterators are currently non-raising. @@ -59,7 +59,7 @@ trait ReversibleRange: # ===----------------------------------------------------------------------=== # -fn reversed[T: ReversibleRange](value: T) -> _StridedRangeIterator: +fn reversed[T: ReversibleRange](value: T) -> _StridedRange: """Get a reversed iterator of the input range. **Note**: iterators are currently non-raising. From 8da9885ae1733252fb739523de995ac8db4d359c Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Thu, 16 May 2024 14:36:58 -0700 Subject: [PATCH 0587/2019] [stdlib] Add stub for `parameter_for` implementation This PR adds stubs for the compiler implementation of `@parameter for`. Due to a bug with implicit conformance and packaging, the range types have to explicitly conform to the internal traits. MODULAR_ORIG_COMMIT_REV_ID: 54365d12dda7487edaff26d1720b55b0c0baf451 --- stdlib/src/builtin/_stubs.mojo | 70 ++++++++++++++++++++++++++++++++++ stdlib/src/builtin/range.mojo | 9 +++-- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/_stubs.mojo b/stdlib/src/builtin/_stubs.mojo index 7ab6beeb78..758dcc8c16 100644 --- a/stdlib/src/builtin/_stubs.mojo +++ b/stdlib/src/builtin/_stubs.mojo @@ -11,7 +11,77 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # +from builtin.range import _StridedRangeIterator + +# ===----------------------------------------------------------------------===# +# __MLIRType +# ===----------------------------------------------------------------------===# + @register_passable("trivial") struct __MLIRType[T: AnyRegType](Movable, Copyable): var value: T + + +# ===----------------------------------------------------------------------===# +# parameter_for +# ===----------------------------------------------------------------------===# + + +trait _IntNext(Copyable): + fn __next__(inout self) -> Int: + ... + + +trait _IntIter(_IntNext): + fn __len__(self) -> Int: + ... + + +trait _IntIterable(_IntIter): + fn __iter__(self) -> Self: + ... + + +trait _StridedIterable(_IntIter): + fn __iter__(self) -> _StridedRangeIterator: + ... + + +@value +struct _NextEval[T: _IntNext]: + var iter: T + var i: Int + + +fn _next_eval[T: _IntNext](iter: T) -> _NextEval[T]: + var copy = iter + var next = copy.__next__() + return _NextEval(copy, next) + + +@always_inline +fn _gen_next[ + inferred T: _IntIter, iter: T, f: fn[i: Int] () capturing -> None +](): + @parameter + if iter.__len__() == 0: + return + else: + alias next = _next_eval(iter) + f[next.i]() + _gen_next[next.iter, f]() + + +@always_inline +fn parameter_for[ + inferred T: _IntIterable, iter: T, f: fn[i: Int] () capturing -> None +](): + _gen_next[iter.__iter__(), f]() + + +@always_inline +fn parameter_for[ + inferred T: _StridedIterable, iter: T, f: fn[i: Int] () capturing -> None +](): + _gen_next[iter.__iter__(), f]() diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index a953425fb1..ff6bf16dc7 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -18,6 +18,9 @@ These are Mojo built-ins, so you don't need to import them. from python import PythonObject +# FIXME(MOCO-658): Explicit conformance to these traits shouldn't be needed. +from builtin._stubs import _IntIterable, _StridedIterable + # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # @@ -58,7 +61,7 @@ fn _sign(x: Int) -> Int: @register_passable("trivial") -struct _ZeroStartingRange(Sized, ReversibleRange): +struct _ZeroStartingRange(Sized, ReversibleRange, _IntIterable): var curr: Int var end: Int @@ -92,7 +95,7 @@ struct _ZeroStartingRange(Sized, ReversibleRange): @value @register_passable("trivial") -struct _SequentialRange(Sized, ReversibleRange): +struct _SequentialRange(Sized, ReversibleRange, _IntIterable): var start: Int var end: Int @@ -146,7 +149,7 @@ struct _StridedRangeIterator(Sized): @value @register_passable("trivial") -struct _StridedRange(Sized, ReversibleRange): +struct _StridedRange(Sized, ReversibleRange, _StridedIterable): var start: Int var end: Int var step: Int From f7b3ee77b328e4c38a95622ee9727902f0540fcf Mon Sep 17 00:00:00 2001 From: Peyman Barazandeh Date: Thu, 16 May 2024 16:42:40 -0500 Subject: [PATCH 0588/2019] [External] [stdlib] Add unit tests for SIMD's __len__() method (#40093) [External] [stdlib] Add unit tests for SIMD's __len__() method Add unit tests for SIMD's __len__() method to improve coverage of the SIMD library. --------- Co-authored-by: Peyman Barazandeh Closes modularml/mojo#2688 MODULAR_ORIG_COMMIT_REV_ID: fb3c2d92fef13c080d98a8b32ae41d3089171d2d --- stdlib/test/builtin/test_simd.mojo | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8b1d18ffe0..e627224692 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -147,6 +147,41 @@ def test_truthy(): test_dtype[DType.bfloat16]() +def test_len(): + var i1 = Int32(0) + assert_equal(i1.__len__(), 1) + + alias I32 = SIMD[DType.int32, 4] + var i2 = I32(-1, 0, 1) + assert_equal(4, i2.__len__()) + var i3 = I32(-1, 0, 1, 3) + assert_equal(4, i3.__len__()) + + alias I8 = SIMD[DType.int8, 1] + var i4 = I8(1) + assert_equal(1, i4.__len__()) + + alias UI64 = SIMD[DType.uint64, 16] + var i5 = UI64(10, 20, 30, 40) + assert_equal(16, i5.__len__()) + var i6 = UI64(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + assert_equal(16, i6.__len__()) + + @parameter + if not has_neon(): + alias BF16 = SIMD[DType.bfloat16, 2] + var f1 = BF16(0.0) + assert_equal(2, f1.__len__()) + var f2 = BF16(0.1, 0.2) + assert_equal(2, f2.__len__()) + + alias F = SIMD[DType.float64, 8] + var f3 = F(1.0) + assert_equal(8, f3.__len__()) + var f4 = F(0, -1.0, 1.0, -1.111, 1.111, -2.2222, 2.2222, 3.1415) + assert_equal(8, f4.__len__()) + + def test_add(): alias I = SIMD[DType.int32, 4] var i = I(-2, -4, 0, 1) @@ -980,6 +1015,7 @@ def main(): test_convert_simd_to_string() test_issue_20421() test_truthy() + test_len() test_add() test_radd() test_iadd() From 68099552903890914872e428caade2de1432d722 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 16 May 2024 15:43:01 -0600 Subject: [PATCH 0589/2019] [scripts] Add markdown scripts for public repo Add our markdownlint config file used internally along with a simple bash script for invoking `markdownlint` using said config. This will eventually be incorporated into the external contributor workflows upstream and in its CI to avoid differences in markdown linting when importing PRs from upstream. MODULAR_ORIG_COMMIT_REV_ID: f2d3f3c0d6123dd9e02833bfd758860277361feb --- stdlib/scripts/.markdownlint.yaml | 46 +++++++++++++++++++++++++++++++ stdlib/scripts/markdownlint.sh | 34 +++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 stdlib/scripts/.markdownlint.yaml create mode 100755 stdlib/scripts/markdownlint.sh diff --git a/stdlib/scripts/.markdownlint.yaml b/stdlib/scripts/.markdownlint.yaml new file mode 100644 index 0000000000..de1b6b947e --- /dev/null +++ b/stdlib/scripts/.markdownlint.yaml @@ -0,0 +1,46 @@ +##===----------------------------------------------------------------------===## +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +##===----------------------------------------------------------------------===## + +# This configures the lint rules for markdownlint, used in markdownlint.sh +# It overrides for the default lint config here: +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml +# Also see the definition for all markdownlint rules here: +# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md + +# MD004: Unordered list style +ul-style: + style: dash # Always use - for bullet lists to avoid confusion/inconsistency + +# MD013: Allow lines >80 char if in heading, table, or code block +line-length: + line_length: 80 + headings: false # But pleaaase keep them as short as possible + tables: false + code_blocks: false + +# MD024: Headings with the same title are okay if they're not siblings +no-duplicate-heading: + siblings_only: true + +# MD033: HTML is okay +no-inline-html: false + +# MD045: Images don't require alt text +no-alt-text: false + +# MD051: Disable link checker because it false-flags custom anchors +# https://github.com/DavidAnson/markdownlint/issues/570 +link-fragments: false + +# MD041: Don't force first line to be H1 as this prevents branded image headers +first-line-h1: false diff --git a/stdlib/scripts/markdownlint.sh b/stdlib/scripts/markdownlint.sh new file mode 100755 index 0000000000..327b234b8a --- /dev/null +++ b/stdlib/scripts/markdownlint.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +##===----------------------------------------------------------------------===## +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +##===----------------------------------------------------------------------===## + +# This runs markdownlint on any files passed to it, using a custom config. +# It's really just a pass-through to `markdownlint` to enforce the config. + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly LINT_CONFIG="${SCRIPT_DIR}/.markdownlint.yaml" + +# Check for markdownlint. +if ! command -v markdownlint &>/dev/null; then + echo "Error: markdownlint is not installed." + echo " Please install via npm install markdownlint-cli" + exit 1 +fi + +# Check for arguments. +if [ $# -eq 0 ]; then + echo "ERROR: You must pass files/directories/globs for MD files to lint." + exit 1 +fi + +markdownlint --config "${LINT_CONFIG}" "$@" From 73fb1c874458c3cc115b82e83ec36b02e2cd82ed Mon Sep 17 00:00:00 2001 From: bgreni <42788181+bgreni@users.noreply.github.com> Date: Thu, 16 May 2024 17:29:00 -0500 Subject: [PATCH 0590/2019] [External] [stdlib] Add Indexer trait (#40103) [External] [stdlib] Add Indexer trait First half of the split off from #2384. Partially addresses #2337 Introduces the `Indexer` trait and applies to `Int`, `IntLiteral`, `Bool`, and integral scalar `SIMD` types. --------- Co-authored-by: bgreni <42788181+bgreni@users.noreply.github.com> Closes modularml/mojo#2685 MODULAR_ORIG_COMMIT_REV_ID: 224c700e9ff9b28abf9e7d72ff3b3271d7e9de09 --- docs/changelog.md | 27 ++++++++++++- stdlib/src/builtin/bool.mojo | 16 +++++++- stdlib/src/builtin/int.mojo | 47 +++++++++++++++++++++++ stdlib/src/builtin/int_literal.mojo | 1 + stdlib/src/builtin/simd.mojo | 17 ++++++++ stdlib/test/builtin/test_bool.mojo | 6 +++ stdlib/test/builtin/test_int.mojo | 6 +++ stdlib/test/builtin/test_int_literal.mojo | 6 +++ stdlib/test/builtin/test_simd.mojo | 8 ++++ 9 files changed, 131 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ba66c01c6a..2241ca9541 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -238,16 +238,39 @@ what we publish. ``` - `List` can now be converted to a `String` with a simplified syntax: - + ```mojo var my_list = List[Int](2, 3) - print(my_list.__str__()) # prints [2, 3] + print(my_list.__str__()) # prints [2, 3] ``` Note that `List` doesn't conform to the `Stringable` trait yet so you cannot use `str(my_list)` yet. ([PR #2673](https://github.com/modularml/mojo/pull/2673) by [@gabrieldemarmiesse](https://github.com/gabrieldemarmiesse)) +- Added the `Indexer` trait to denote types that implement the `__index__()` + method which allow these types to be accepted in common `__getitem__` and + `__setitem__` implementations, as well as allow a new builtin `index` function + to be called on them. For example: + + ```mojo + @value + struct AlwaysZero(Indexer): + fn __index__(self) -> Int: + return 0 + + struct MyList: + var data: List[Int] + + fn __init__(inout self): + self.data = List[Int](1, 2, 3, 4) + + fn __getitem__[T: Indexer](self, idx: T) -> T: + return self.data[index(idx)] + + print(MyList()[AlwaysZero()]) # prints `1` + ``` + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 1068d7fd4b..bcc45bb128 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -59,7 +59,12 @@ trait Boolable: @value @register_passable("trivial") struct Bool( - Stringable, CollectionElement, Boolable, EqualityComparable, Intable + Stringable, + CollectionElement, + Boolable, + EqualityComparable, + Intable, + Indexer, ): """The primitive Bool scalar value used in Mojo.""" @@ -151,6 +156,15 @@ struct Bool( """ return __mlir_op.`pop.select`[_type=Int](self.value, Int(1), Int(0)) + @always_inline("nodebug") + fn __index__(self) -> Int: + """Convert this Bool to an integer for indexing purposes. + + Returns: + 1 if the Bool is True, 0 otherwise. + """ + return self.__int__() + @always_inline("nodebug") fn __eq__(self, rhs: Bool) -> Bool: """Compare this Bool to RHS. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index af64739040..52940b08a9 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -27,6 +27,52 @@ from utils._visualizers import lldb_formatter_wrapping_type from utils._format import Formattable, Formatter from utils.inlined_string import _ArrayMem +# ===----------------------------------------------------------------------=== # +# Indexer +# ===----------------------------------------------------------------------=== # + + +trait Indexer: + """This trait denotes a type that can be used to index a container that + handles integral index values. + + This solves the issue of being able to index data structures such as `List` + with the various integral types without being too broad and allowing types + that are coercible to `Int` (e.g. floating point values that have `__int__` + method). In contrast to `Intable`, types conforming to `Indexer` must be + convertible to `Int` in a lossless way. + """ + + fn __index__(self) -> Int: + """Return the index value. + + Returns: + The index value of the object. + """ + ... + + +# ===----------------------------------------------------------------------=== # +# index +# ===----------------------------------------------------------------------=== # + + +@always_inline("nodebug") +fn index[T: Indexer](idx: T) -> Int: + """Returns the value of `__index__` for the given value. + + Parameters: + T: A type conforming to the `Indexer` trait. + + Args: + idx: The value. + + Returns: + An `Int` respresenting the index value. + """ + return idx.__index__() + + # ===----------------------------------------------------------------------=== # # Intable # ===----------------------------------------------------------------------=== # @@ -205,6 +251,7 @@ struct Int( Roundable, Stringable, Truncable, + Indexer, ): """This type represents an integer value.""" diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index e4a13b06e8..d8691e0446 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -29,6 +29,7 @@ struct IntLiteral( Roundable, Stringable, Truncable, + Indexer, ): """This type represents a static integer literal value with infinite precision. They can't be materialized at runtime and diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 03e27e002f..9d2f1efb42 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -139,6 +139,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Sized, Stringable, Truncable, + Indexer, ): """Represents a small vector that is backed by a hardware vector element. @@ -179,6 +180,22 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( _simd_construction_checks[type, size]() self = _unchecked_zero[type, size]() + @always_inline("nodebug") + fn __index__(self) -> Int: + """Returns the value as an int if it is an integral value. + + Constraints: + Must be a scalar integral value. + + Returns: + The value as an integer. + """ + constrained[ + type.is_integral() or type.is_bool(), + "expected integral or bool type", + ]() + return self.__int__() + @always_inline("nodebug") fn __init__(inout self, value: SIMD[DType.float64, 1]): """Initializes the SIMD vector with a float. diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index ab13481124..17a4347afc 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -100,6 +100,11 @@ def test_neg(): assert_equal(0, -False) +def test_indexer(): + assert_equal(1, Bool.__index__(True)) + assert_equal(0, Bool.__index__(False)) + + def main(): test_bool_cast_to_int() test_bool_none() @@ -107,3 +112,4 @@ def main(): test_bool_to_string() test_bitwise() test_neg() + test_indexer() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 7fbe49c04d..07e9ac5c25 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -143,6 +143,11 @@ def test_int_representation(): assert_equal(repr(Int(-100)), "-100") +def test_indexer(): + assert_equal(5, Int(5).__index__()) + assert_equal(987, Int(987).__index__()) + + def main(): test_constructors() test_properties() @@ -160,3 +165,4 @@ def main(): test_abs() test_string_conversion() test_int_representation() + test_indexer() diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 697034cf47..19a459eb11 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -78,6 +78,11 @@ def test_abs(): assert_equal(abs(0), 0) +def test_indexer(): + assert_equal(1, IntLiteral.__index__(1)) + assert_equal(88, IntLiteral.__index__(88)) + + def main(): test_int() test_ceil() @@ -88,3 +93,4 @@ def main(): test_mod() test_bit_width() test_abs() + test_indexer() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index e627224692..bf04d29d75 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -1009,6 +1009,13 @@ def test_min_max_clamp(): assert_equal(i.clamp(-7, 4), I(-7, -5, 4, 4)) +def test_indexer(): + assert_equal(5, Int8(5).__index__()) + assert_equal(56, UInt32(56).__index__()) + assert_equal(1, Scalar[DType.bool](True).__index__()) + assert_equal(0, Scalar[DType.bool](False).__index__()) + + def main(): test_cast() test_simd_variadic() @@ -1044,3 +1051,4 @@ def main(): test_mul_with_overflow() test_abs() test_min_max_clamp() + test_indexer() From afdd750048a44802effbef175766f6e67680470c Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Thu, 16 May 2024 16:09:24 -0700 Subject: [PATCH 0591/2019] [mojo-stdlib] Move intel_amx test to LinAlg This test doesn't purely depend on the stdlib, so it makes more sense with the other LinAlg tests. It was also using the wrong REQUIRES so it was never running, this updates to make it pass again. MODULAR_ORIG_COMMIT_REV_ID: dcd34acabcfd64336e1232c1c5c6adee1df7ab69 --- stdlib/test/sys/lit.local.cfg | 6 ----- stdlib/test/sys/test_has_intel_amx.mojo | 34 ------------------------- 2 files changed, 40 deletions(-) delete mode 100644 stdlib/test/sys/test_has_intel_amx.mojo diff --git a/stdlib/test/sys/lit.local.cfg b/stdlib/test/sys/lit.local.cfg index 63129a7220..75cc65c739 100644 --- a/stdlib/test/sys/lit.local.cfg +++ b/stdlib/test/sys/lit.local.cfg @@ -6,12 +6,6 @@ from pathlib import Path if platform.machine() in ("aarch64", "arm64"): config.available_features.add("aarch64") -if ( - platform.system() == "Linux" - and "amx_tile" in Path("/proc/cpuinfo").read_text() -): - config.available_features.add("intel_amx") - def is_apple_silicon() -> bool: return platform.system() == "Darwin" and platform.processor() == "arm" diff --git a/stdlib/test/sys/test_has_intel_amx.mojo b/stdlib/test/sys/test_has_intel_amx.mojo deleted file mode 100644 index 248067007b..0000000000 --- a/stdlib/test/sys/test_has_intel_amx.mojo +++ /dev/null @@ -1,34 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# -# This file is only run on linux targets with amx_tile -# -# ===----------------------------------------------------------------------=== # -# REQUIRES: linux -# REQUIRES: amx_tile -# RUN: %mojo %s - -from sys import has_intel_amx, os_is_linux -from testing import assert_false, assert_true - -from LinAlg.intel_amx import init_intel_amx - - -fn test_has_intel_amx(): - assert_true(os_is_linux()) - assert_true(has_intel_amx()) - assert_true(init_intel_amx()) - - -fn main(): - test_has_intel_amx() From 63dd69cb7f4cb9d1463c0941deefd8f64730e71c Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Thu, 16 May 2024 19:16:35 -0500 Subject: [PATCH 0592/2019] [External] [stdlib] Add method `strip()` to `StringRef` (#40108) [External] [stdlib] Add method `strip()` to `StringRef` This PR adds a `strip` method to `StringRef`. This PR is helpful for #2649. Where can I find the test cases for StringRef? ORIGINAL_AUTHOR=Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2683 Co-authored-by: Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> Closes modularml/mojo#2683 MODULAR_ORIG_COMMIT_REV_ID: 5ac8b1f3b45c75a964c5d9368e3871e7fc617a88 --- docs/changelog.md | 4 ++++ stdlib/src/utils/stringref.mojo | 17 +++++++++++++++++ stdlib/test/builtin/test_string.mojo | 12 ++++++++++++ 3 files changed, 33 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 2241ca9541..5276e46786 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -271,6 +271,10 @@ what we publish. print(MyList()[AlwaysZero()]) # prints `1` ``` +- `StringRef` now implements `strip()` which can be used to remove leading and + trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) + by [@fknfilewalker](https://github.com/fknfilewalker)) + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 7af4252c52..fafa9af67d 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -425,6 +425,23 @@ struct StringRef( return StringRef(data, length) + fn strip(self) -> StringRef: + """Gets a StringRef with leading and trailing whitespaces removed. + + For example, `" mojo "` returns `"mojo"`. + + Returns: + A StringRef with leading and trailing whitespaces removed. + """ + var start: Int = 0 + var end: Int = len(self) + var ptr = self.unsafe_ptr() + while start < end and isspace(ptr[start]): + start += 1 + while end > start and isspace(ptr[end - 1]): + end -= 1 + return StringRef(ptr + start, end - start) + fn __int__(self) raises -> Int: """Parses the given string as a base-10 integer and returns that value. diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 4deebb84e2..785c5d6243 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -223,6 +223,17 @@ fn test_stringref_from_dtypepointer() raises: assert_equal(a, b) +fn test_stringref_strip() raises: + var a = StringRef(" mojo rocks ") + var b = StringRef("mojo ") + var c = StringRef(" mojo") + var d = StringRef("") + assert_equal(a.strip(), "mojo rocks") + assert_equal(b.strip(), "mojo") + assert_equal(c.strip(), "mojo") + assert_equal(d.strip(), "") + + fn test_ord() raises: # Regular ASCII assert_equal(ord("A"), 65) @@ -839,6 +850,7 @@ def main(): test_string_join() test_stringref() test_stringref_from_dtypepointer() + test_stringref_strip() test_ord() test_chr() test_string_indexing() From e8cd20e33b20e3321839d783d3ac4da3d4f39642 Mon Sep 17 00:00:00 2001 From: Mostafa Hagog <46081083+hagogmostafa@users.noreply.github.com> Date: Thu, 16 May 2024 18:04:49 -0700 Subject: [PATCH 0593/2019] Updated matmul example with use of physical/logical/performance core counts in parallel implementation to showcase users how they can use the number of workers to tune for different systems with different types of cores. this answers issue [1877](https://github.com/modularml/mojo/issues/1877). MODULAR_ORIG_COMMIT_REV_ID: 2841d16d9aae49717f87e680d5cae7ef88a5f147 --- examples/matmul.mojo | 77 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 0b60a48d40..b1a1010f0e 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -19,6 +19,7 @@ from random import rand import benchmark from algorithm import Static2DTileUnitFunc as Tile2DFunc from algorithm import parallelize, vectorize +from sys import info from memory import memset_zero from python import Python @@ -109,7 +110,10 @@ fn matmul_vectorized(inout C: Matrix, A: Matrix, B: Matrix): # Parallelize the code by using the builtin parallelize function +# num_workers is the number of worker threads to use in parallalize fn matmul_parallelized(inout C: Matrix, A: Matrix, B: Matrix): + var num_workers = C.rows + @parameter fn calc_row(m: Int): for k in range(A.cols): @@ -122,7 +126,7 @@ fn matmul_parallelized(inout C: Matrix, A: Matrix, B: Matrix): vectorize[dot, nelts, size = C.cols]() - parallelize[calc_row](C.rows, C.rows) + parallelize[calc_row](C.rows, num_workers) # Perform 2D tiling on the iteration space defined by end_x and end_y @@ -133,7 +137,10 @@ fn tile[tiled_fn: Tile2DFunc, tile_x: Int, tile_y: Int](end_x: Int, end_y: Int): # Use the above tile function to perform tiled matmul +# Also parallelize with num_workers threads fn matmul_tiled(inout C: Matrix, A: Matrix, B: Matrix): + var num_workers = C.rows + @parameter fn calc_row(m: Int): @parameter @@ -153,11 +160,22 @@ fn matmul_tiled(inout C: Matrix, A: Matrix, B: Matrix): tile[calc_tile, tile_n, tile_k](C.cols, B.rows) - parallelize[calc_row](C.rows, C.rows) + parallelize[calc_row](C.rows, num_workers) # Unroll the vectorized loop by a constant factor -fn matmul_unrolled(inout C: Matrix, A: Matrix, B: Matrix): +# Also parallelize with num_workers threads +fn matmul_unrolled[mode: Int](inout C: Matrix, A: Matrix, B: Matrix): + var num_workers: Int + if mode == 1: + num_workers = info.num_physical_cores() + elif mode == 2: + num_workers = info.num_logical_cores() + elif mode == 3: + num_workers = info.num_performance_cores() + else: + num_workers = C.rows + @parameter fn calc_row(m: Int): @parameter @@ -181,13 +199,13 @@ fn matmul_unrolled(inout C: Matrix, A: Matrix, B: Matrix): tile[calc_tile, tile_n, tile_k](C.cols, B.rows) - parallelize[calc_row](C.rows, C.rows) + parallelize[calc_row](C.rows, num_workers) @always_inline fn bench[ func: fn (inout Matrix, Matrix, Matrix) -> None, name: StringLiteral -](base_gflops: Float64, numpy_gflops: Float64) raises: +](base_gflops: Float64) raises: var A = Matrix[M, K].rand() var B = Matrix[K, N].rand() var C = Matrix[M, N]() @@ -205,7 +223,6 @@ fn bench[ var gflops = ((2 * M * N * K) / secs) / 1e9 var speedup: Float64 = gflops / base_gflops - var numpy_speedup: Float64 = gflops / numpy_gflops var py = Python.import_module("builtins") _ = py.print( @@ -244,8 +261,35 @@ fn test_all() raises: raise Error("Parallelize output does not match naive implementation") if not test_matrix_equal[matmul_tiled](C, A, B): raise Error("Tiled output does not match naive implementation") - if not test_matrix_equal[matmul_unrolled](C, A, B): + if not test_matrix_equal[matmul_unrolled[0]](C, A, B): raise Error("Unroll output does not match naive implementation") + if not test_matrix_equal[matmul_unrolled[1]]( + C, + A, + B, + ): + raise Error( + "Unroll with workers as physical cores output does not match naive" + " implementation" + ) + if not test_matrix_equal[matmul_unrolled[2]]( + C, + A, + B, + ): + raise Error( + "Unroll with workers as logical cores output does not match naive" + " implementation" + ) + if not test_matrix_equal[matmul_unrolled[3]]( + C, + A, + B, + ): + raise Error( + "Unroll with workers as performance cores output does not match" + " naive implementation" + ) A.data.free() B.data.free() @@ -261,8 +305,17 @@ fn main() raises: var python_gflops = run_matmul_python() var numpy_gflops = run_matmul_numpy() - bench[matmul_naive, "Naive:"](python_gflops, numpy_gflops) - bench[matmul_vectorized, "Vectorized: "](python_gflops, numpy_gflops) - bench[matmul_parallelized, "Parallelized:"](python_gflops, numpy_gflops) - bench[matmul_tiled, "Tiled:"](python_gflops, numpy_gflops) - bench[matmul_unrolled, "Unrolled:"](python_gflops, numpy_gflops) + bench[matmul_naive, "Naive:"](python_gflops) + bench[matmul_vectorized, "Vectorized: "](python_gflops) + bench[matmul_parallelized, "Parallelized:"](python_gflops) + bench[matmul_tiled, "Tiled:"](python_gflops) + bench[matmul_unrolled[0], "Unrolled:"](python_gflops) + bench[matmul_unrolled[1], "Unrolled w/ workers == physical cores:"]( + python_gflops + ) + bench[matmul_unrolled[2], "Unrolled w/ workers == logical cores:"]( + python_gflops + ) + bench[matmul_unrolled[3], "Unrolled w/ workers == performance cores:"]( + python_gflops + ) From 62be12e5a789bd6108aeb33358d9bba3c3a97956 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 16 May 2024 18:11:29 -0700 Subject: [PATCH 0594/2019] [Docs] Fixes some typos mentioned in mojo#2668 Plus a little more cleanup to several files. MODULAR_ORIG_COMMIT_REV_ID: fff2ae833af1b8716e121fae812055819e7c3177 --- docs/manual/parameters/index.ipynb | 2 +- docs/manual/python/types.ipynb | 71 ++++++++++++++---------------- docs/manual/types.ipynb | 4 +- docs/roadmap.md | 35 +++------------ 4 files changed, 44 insertions(+), 68 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index e5cabf5cf5..1803fe8e35 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -406,7 +406,7 @@ " pass\n", "\n", " fn foo(inout self):\n", - " print(\"calling instance menthod\")\n", + " print(\"calling instance method\")\n", "\n", " @staticmethod\n", " fn foo():\n", diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index 28842ac562..24533e268a 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -56,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -95,25 +95,30 @@ "source": [ "## Python types in Mojo\n", "\n", - "You can also use Python objects from Mojo. For example, Mojo doesn't have a\n", - "standard dictionary type yet, but you can work with Python dictionaries in Mojo. \n", - "To create a Python dictionary, use the \n", + "You can also use Python objects from Mojo. For example, Mojo's \n", + "[`Dict`](/mojo/stdlib/collections/dict/Dict) and \n", + "[`List`](/mojo/stdlib/collections/list/List) types don't natively support\n", + "heterogeneous collections. One alternative is to use a Python dictionary or\n", + "list.\n", + "\n", + "For example, to create a Python dictionary, use the \n", "[`dict()`](/mojo/stdlib/python/python/Python#dict) method:" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "from python import Python\n", "\n", - "fn use_dict() raises:\n", + "def use_dict():\n", " var dictionary = Python.dict()\n", - " dictionary[\"fruit\"] = \"apple\"\n", - " dictionary[\"starch\"] = \"potato\"\n", - " print(\"Fruit: \", dictionary[\"fruit\"])" + " dictionary[\"item_name\"] = \"whizbang\"\n", + " dictionary[\"price\"] = 11.75\n", + " dictionary[\"inventory\"] = 100\n", + " print(dictionary)\n" ] }, { @@ -139,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -159,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -180,11 +185,11 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "fn use_py_set() raises:\n", + "def use_py_set():\n", " var py_set = Python.evaluate('set([2, 3, 5, 7, 11])')\n", " var num_items = len(py_set)\n", " print(num_items, \" items in set.\") # prints \"5 items in set\"\n", @@ -203,35 +208,27 @@ "explicitly convert a Python value into a native Mojo value. \n", "\n", "Currently `PythonObject` conforms to the \n", - "[`Intable`](/mojo/stdlib/builtin/int/Intable) and \n", - "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) traits, which means you\n", - "can convert Python values to Mojo `Int` and `String` types using the built-in \n", - "[`int()`](/mojo/stdlib/builtin/int/int-function) and\n", - "[`str()`](/mojo/stdlib/builtin/str/str) functions, and print Python values\n", - "using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", + "[`Intable`](/mojo/stdlib/builtin/int/Intable), \n", + "[`Stringable`](/mojo/stdlib/builtin/str/Stringable), and \n", + "[`Boolable`](/mojo/stdlib/builtin/bool/Boolable) traits, which \n", + "means you can convert Python values to Mojo `Int`, `String`, and `Bool` types\n", + "using the built-in \n", + "[`int()`](/mojo/stdlib/builtin/int/int-function),\n", + "[`str()`](/mojo/stdlib/builtin/str/str),\n", + "and [`bool()`](/mojo/stdlib/builtin/bool/bool) functions, and print Python \n", + "values using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", - "[`__bool__()`](/mojo/stdlib/python/object/PythonObject#__bool__) and \n", - "[`to_float64()`](/mojo/stdlib/python/object/PythonObject#to_float64) methods for \n", - "converting to boolean and floating point values, respectively.\n", + "[`to_float64()`](/mojo/stdlib/python/object/PythonObject#to_float64) for \n", + "converting to a Mojo floating point value.\n", "\n", "```mojo\n", "var i: Int = int(py_int)\n", "var s: String = str(py_string)\n", - "var b: Bool = py_bool.__bool__()\n", + "var b: Bool = bool(py_bool)\n", "var f: Float64 = py_float.to_float64()\n", "```\n", "\n", - ":::note\n", - "\n", - "We mentioned that Python types get wrapped in `PythonObject` wrapper. \n", - "There is currently one exception to this: Python dictionaries have their own\n", - "specialized Mojo wrapper type, `Dictionary`. Despite the name, it's not a true\n", - "dictionary type, just another kind of wrapper. Most of the time this is\n", - "just an implementation detail, but you may notice that the types are different.\n", - "\n", - ":::\n", - "\n", "### Comparing Python types in Mojo\n", "\n", "In conditionals, Python objects act like you'd expect them to: Python values \n", @@ -247,11 +244,11 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "fn python_types() raises:\n", + "def python_types():\n", " from python import Python\n", " from python import PythonObject\n", "\n", @@ -262,7 +259,7 @@ " print(Python.type(value1)) # \n", " print(Python.is_type(Python.type(value1), Python.type(value2))) # True\n", " print(Python.is_type(Python.type(value1), float_type)) # True\n", - " print(Python.is_type(Python.type(value1), Python.none())) # False" + " print(Python.is_type(Python.type(value1), Python.none())) # False\n" ] }, { diff --git a/docs/manual/types.ipynb b/docs/manual/types.ipynb index ece8b8fb55..6ceffe596b 100644 --- a/docs/manual/types.ipynb +++ b/docs/manual/types.ipynb @@ -247,8 +247,10 @@ ] }, { - "cell_type": "raw", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ "var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)" ] diff --git a/docs/roadmap.md b/docs/roadmap.md index a79069aa4e..51e982c8ae 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -201,7 +201,7 @@ in the next couple of months. The basic support for ownership includes features like: - Capture declarations in closures. -- Borrow checker: complain about invalid mutable references. +- Lifetime checker: complain about invalid mutable references. Mojo has support for a safe `Reference` type, and it is used in the standard library, but it is still under active development and not very pretty or nice @@ -231,7 +231,7 @@ We plan to expand traits support in future releases. Planned features include: - Support for a feature like Swift's extensions, allowing you to add a trait to a preexisting type. -- Add support for conditional conformances. +- Add support for conditional conformance. ## Classes @@ -307,8 +307,7 @@ documented here. ### No list or dict comprehensions Mojo does not yet support Python list or dictionary comprehension expressions, -like `[x for x in range(10)]`, because Mojo's standard library has not yet -grown a standard list or dictionary type. +like `[x for x in range(10)]`. ### No `lambda` syntax @@ -603,13 +602,13 @@ fn call_it(): ### The standard library has limited exceptions use For historic and performance reasons, core standard library types typically do -not use exceptions. For instance, `DynamicVector` will not raise an +not use exceptions. For instance, `List` will not raise an out-of-bounds access (it will crash), and `Int` does not throw on divide by zero. In other words, most standard library types are considered "unsafe". ```mojo -var v = DynamicVector[Int](capacity=0) -print(v[1]) # could crash or print garbage values (undefined behaviour) +var l = List[Int](capacity=0) +print(l[1]) # could crash or print garbage values (undefined behaviour) print(1//0) # does not raise and could print anything (undefined behaviour) ``` @@ -643,28 +642,6 @@ The upstream dialects available in the Playground are the [`index`](https://mlir.llvm.org/docs/Dialects/IndexOps/) dialect and the [`LLVM`](https://mlir.llvm.org/docs/Dialects/LLVM/) dialect. -### `@value` is limited with trait conformance check - -Structs with `@value` decorator still need to explicitly provide dunder -methods such as `__init__`, `__copyinit__`, and `__moveinit__` when -both of the following are true: - -- The struct has one or more fields that are self referencing - (such as `Pointer[Self]`). -- The struct declares conformance to a trait that requires these dunder - methods. - -```mojo -# test.mojo -@value -struct A(CollectionElement): - # error: 'DynamicVector' parameter #0 has 'CollectionElement' type, but value has type 'A' - var a: DynamicVector[Self] -``` - -In the example above, adding the `__moveinit__()` and `__copyinit__()` methods -required by `CollectionElement` resolves this error. - ### `or` expression is statically typed Because Mojo has static typing, the `or` expression can't currently mimic the From 216e85e8923a3dd356ad33519d145441855fe8fa Mon Sep 17 00:00:00 2001 From: modularbot <116839051+modularbot@users.noreply.github.com> Date: Thu, 16 May 2024 21:01:32 -0500 Subject: [PATCH 0595/2019] [External] [stdlib] Delegate string comparisons to `StringRef` (#40107) [External] [stdlib] Delegate string comparisons to `StringRef` This is a follow-up to #2409. String comparisons for `StringRef` are implemented. `StringRef` make use of `memory.memcmp` for all of its 6 comparisons now, hopefully this change is ok. `String`'s and `StringLiteral`'s comparisons (`__eq__`, `__ne__`, `__lt__`, `__le__`, `__gt__`, & `__ge__`) are delegated to `StringRef`. As a result: 1. `String` comparisons uses its `_strref_dangerous`-method. And, 2. `StringLiteral`'s local `_memcmp`-function is removed (which was used "rather than memory.memcpy to avoid #31139 and #25100"). The base logic for all 18 comparisons are implemented in `StringRef`'s `__ne__` and `__lt__` methods. All other comparisons uses some variation/negation of either `__ne__` or `__lt__`. ORIGINAL_AUTHOR=Simon Hellsten <56205346+siitron@users.noreply.github.com> PUBLIC_PR_LINK=modularml/mojo#2620 --------- Co-authored-by: Simon Hellsten <56205346+siitron@users.noreply.github.com> Closes modularml/mojo#2620 MODULAR_ORIG_COMMIT_REV_ID: ed03ee44f06caed7cad77c9c909e83d3c2c2d3cc --- stdlib/src/builtin/string.mojo | 18 +----- stdlib/src/builtin/string_literal.mojo | 32 +--------- stdlib/src/utils/stringref.mojo | 78 ++++++++++++++++++++++--- stdlib/test/builtin/test_stringref.mojo | 39 ++++++++++++- 4 files changed, 112 insertions(+), 55 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 472c70a964..0e8834d95e 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -894,13 +894,7 @@ struct String( Returns: True if the Strings are equal and False otherwise. """ - if len(self) != len(other): - return False - - if int(self.unsafe_ptr()) == int(other.unsafe_ptr()): - return True - - return memcmp(self.unsafe_ptr(), other.unsafe_ptr(), len(self)) == 0 + return not (self != other) @always_inline fn __ne__(self, other: String) -> Bool: @@ -912,7 +906,7 @@ struct String( Returns: True if the Strings are not equal and False otherwise. """ - return not (self == other) + return self._strref_dangerous() != other._strref_dangerous() @always_inline fn __lt__(self, rhs: String) -> Bool: @@ -924,13 +918,7 @@ struct String( Returns: True if this String is strictly less than the RHS String and False otherwise. """ - var len1 = len(self) - var len2 = len(rhs) - - if len1 < len2: - return memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len1) <= 0 - else: - return memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len2) < 0 + return self._strref_dangerous() < rhs._strref_dangerous() @always_inline fn __le__(self, rhs: String) -> Bool: diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 3d39762047..1d382e8ac4 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -121,11 +121,7 @@ struct StringLiteral( Returns: True if they are equal. """ - var length = len(self) - if length != len(rhs): - return False - - return _memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), length) == 0 + return not (self != rhs) @always_inline("nodebug") fn __ne__(self, rhs: StringLiteral) -> Bool: @@ -137,7 +133,7 @@ struct StringLiteral( Returns: True if they are not equal. """ - return not self == rhs + return StringRef(self) != StringRef(rhs) @always_inline("nodebug") fn __lt__(self, rhs: StringLiteral) -> Bool: @@ -149,13 +145,7 @@ struct StringLiteral( Returns: True if this StringLiteral is strictly less than the RHS StringLiteral and False otherwise. """ - var len1 = len(self) - var len2 = len(rhs) - - if len1 < len2: - return _memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len1) <= 0 - else: - return _memcmp(self.unsafe_ptr(), rhs.unsafe_ptr(), len2) < 0 + return StringRef(self) < StringRef(rhs) @always_inline("nodebug") fn __le__(self, rhs: StringLiteral) -> Bool: @@ -328,19 +318,3 @@ struct StringLiteral( An integer value that represents the string, or otherwise raises. """ return _atol(self) - - -# Use a local memcmp rather than memory.memcpy to avoid #31139 and #25100. -@always_inline("nodebug") -fn _memcmp( - s1: DTypePointer[DType.int8], s2: DTypePointer[DType.int8], count: Int -) -> Int: - for i in range(count): - var s1i = s1[i] - var s2i = s2[i] - if s1i == s2i: - continue - if s1i > s2i: - return 1 - return -1 - return 0 diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index fafa9af67d..e17a99c1f3 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,7 +16,7 @@ from bit import countr_zero from builtin.dtype import _uint_type_of_width from builtin.string import _atol -from memory import DTypePointer, UnsafePointer +from memory import DTypePointer, UnsafePointer, memcmp # ===----------------------------------------------------------------------=== # @@ -227,7 +227,7 @@ struct StringRef( """ return self.length - @always_inline("nodebug") + @always_inline fn __eq__(self, rhs: StringRef) -> Bool: """Compares two strings are equal. @@ -237,14 +237,20 @@ struct StringRef( Returns: True if the strings match and False otherwise. """ - if len(self) != len(rhs): - return False - for i in range(len(self)): - if self.data.load(i) != rhs.data.load(i): - return False - return True + return not (self != rhs) + # Use a local memcmp rather than memory.memcpy to avoid indirect recursions. @always_inline("nodebug") + fn _memcmp(self, other: StringRef, count: Int) -> Int: + for i in range(count): + var s1i = self.data[i] + var s2i = other.data[i] + if s1i == s2i: + continue + return 1 if s1i > s2i else -1 + return 0 + + @always_inline fn __ne__(self, rhs: StringRef) -> Bool: """Compares two strings are not equal. @@ -254,7 +260,61 @@ struct StringRef( Returns: True if the strings do not match and False otherwise. """ - return not (self == rhs) + return len(self) != len(rhs) or self._memcmp(rhs, len(self)) + + @always_inline + fn __lt__(self, rhs: StringRef) -> Bool: + """Compare this StringRef to the RHS using LT comparison. + + Args: + rhs: The other StringRef to compare against. + + Returns: + True if this string is strictly less than the RHS string and False + otherwise. + """ + var len1 = len(self) + var len2 = len(rhs) + return self._memcmp(rhs, min(len1, len2)) < int(len1 < len2) + + @always_inline + fn __le__(self, rhs: StringRef) -> Bool: + """Compare this StringRef to the RHS using LE comparison. + + Args: + rhs: The other StringRef to compare against. + + Returns: + True if this string is less than or equal to the RHS string and + False otherwise. + """ + return not (rhs < self) + + @always_inline + fn __gt__(self, rhs: StringRef) -> Bool: + """Compare this StringRef to the RHS using GT comparison. + + Args: + rhs: The other StringRef to compare against. + + Returns: + True if this string is strictly greater than the RHS string and + False otherwise. + """ + return rhs < self + + @always_inline + fn __ge__(self, rhs: StringRef) -> Bool: + """Compare this StringRef to the RHS using GE comparison. + + Args: + rhs: The other StringRef to compare against. + + Returns: + True if this string is greater than or equal to the RHS string and + False otherwise. + """ + return not (self < rhs) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> StringRef: diff --git a/stdlib/test/builtin/test_stringref.mojo b/stdlib/test/builtin/test_stringref.mojo index 257b593450..fc85265237 100644 --- a/stdlib/test/builtin/test_stringref.mojo +++ b/stdlib/test/builtin/test_stringref.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_equal, assert_raises +from testing import assert_equal, assert_true, assert_false, assert_raises from utils import StringRef @@ -34,13 +34,48 @@ def test_strref_from_start(): assert_equal(str._from_start(-10), "Hello") +fn test_comparison_operators() raises: + var abc = StringRef("abc") + var de = StringRef("de") + var ABC = StringRef("ABC") + var ab = StringRef("ab") + var abcd = StringRef("abcd") + + # Test less than and greater than + assert_true(StringRef.__lt__(abc, de)) + assert_false(StringRef.__lt__(de, abc)) + assert_false(StringRef.__lt__(abc, abc)) + assert_true(StringRef.__lt__(ab, abc)) + assert_true(StringRef.__gt__(abc, ab)) + assert_false(StringRef.__gt__(abc, abcd)) + + # Test less than or equal to and greater than or equal to + assert_true(StringRef.__le__(abc, de)) + assert_true(StringRef.__le__(abc, abc)) + assert_false(StringRef.__le__(de, abc)) + assert_true(StringRef.__ge__(abc, abc)) + assert_false(StringRef.__ge__(ab, abc)) + assert_true(StringRef.__ge__(abcd, abc)) + + # Test case sensitivity in comparison (assuming ASCII order) + assert_true(StringRef.__gt__(abc, ABC)) + assert_false(StringRef.__le__(abc, ABC)) + + # Test comparisons involving empty strings + assert_true(StringRef.__lt__("", abc)) + assert_false(StringRef.__lt__(abc, "")) + assert_true(StringRef.__le__("", "")) + assert_true(StringRef.__ge__("", "")) + + def test_intable(): assert_equal(int(StringRef("123")), 123) with assert_raises(): - int(StringRef("hi")) + _ = int(StringRef("hi")) def main(): test_strref_from_start() + test_comparison_operators() test_intable() From 8e0aed2f8ecd21cd81a129b99a46fae1eaa1e1c9 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 16 May 2024 22:16:37 -0500 Subject: [PATCH 0596/2019] [stdlib] feature: Remove `_ArrayMem` in favor of `InlineArray` This removes the bespoke `_ArrayMem` type in favor of using the more complete `InlineArray` type everywhere `_ArrayMem` had been used. MODULAR_ORIG_COMMIT_REV_ID: 2c48d90de8f586dc69c863e58822348fadcc5431 --- stdlib/src/builtin/hex.mojo | 4 +- stdlib/src/builtin/int.mojo | 6 +- stdlib/src/builtin/simd.mojo | 10 +-- stdlib/src/utils/inlined_string.mojo | 86 ++++--------------------- stdlib/src/utils/static_tuple.mojo | 23 +++++-- stdlib/test/collections/test_array.mojo | 38 ----------- 6 files changed, 36 insertions(+), 131 deletions(-) delete mode 100644 stdlib/test/collections/test_array.mojo diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index c8033b7269..c13e5195c7 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import List, Optional -from utils.inlined_string import _ArrayMem +from utils import InlineArray alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @@ -145,7 +145,7 @@ fn _try_write_int( # Stack allocate enough bytes to store any formatted 64-bit integer alias CAPACITY: Int = 64 - var buf = _ArrayMem[Int8, CAPACITY]() + var buf = InlineArray[Int8, CAPACITY](unsafe_uninitialized=True) # Start the buf pointer at the end. We will write the least-significant # digits later in the buffer, and then decrement the pointer to move diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 52940b08a9..3f7bf92e41 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -25,7 +25,7 @@ from builtin.hex import _try_write_int from utils._visualizers import lldb_formatter_wrapping_type from utils._format import Formattable, Formatter -from utils.inlined_string import _ArrayMem +from utils import InlineArray # ===----------------------------------------------------------------------=== # # Indexer @@ -377,7 +377,7 @@ struct Int( # Stack allocate enough bytes to store any formatted 64-bit integer alias size: Int = 32 - var buf = _ArrayMem[Int8, size]() + var buf = InlineArray[Int8, size](unsafe_uninitialized=True) # Format the integer to the local byte array var len = _snprintf( @@ -394,7 +394,7 @@ struct Int( # # SAFETY: # `buf` is kept alive long enough for the use of this StringRef. - writer.write_str(StringRef(buf.as_ptr(), len)) + writer.write_str(StringRef(buf.unsafe_ptr(), len)) # Keep buf alive until we've finished with the StringRef _ = buf^ diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9d2f1efb42..212ab13cdf 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -39,7 +39,7 @@ from utils.numerics import ( min_or_neg_inf as _min_or_neg_inf, ) from utils._visualizers import lldb_formatter_wrapping_type -from utils.inlined_string import _ArrayMem +from utils import InlineArray from .dtype import _integral_type_of, _get_dtype_printf_format from .io import _snprintf_scalar, _snprintf, _printf @@ -2728,12 +2728,8 @@ fn _format_scalar[dtype: DType](inout writer: Formatter, value: Scalar[dtype]): # type. alias size: Int = _calc_format_buffer_size[dtype]() - var buf = _ArrayMem[Int8, size]() - # TODO(MOCO-268): - # Remove this rebind(..) once compiler type comparison bug is fixed. - var buf_ptr: UnsafePointer[Int8] = rebind[UnsafePointer[Int8]]( - buf.unsafe_ptr() - ) + var buf = InlineArray[Int8, size](unsafe_uninitialized=True) + var buf_ptr = buf.unsafe_ptr() var wrote = _snprintf_scalar[dtype]( buf_ptr, diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index ca29f034d6..6216a4bd51 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -290,7 +290,7 @@ struct _FixedString[CAP: Int]( CAP: The fixed-size count of bytes of string storage capacity available. """ - var buffer: _ArrayMem[Int8, CAP] + var buffer: InlineArray[Int8, CAP] """The underlying storage for the fixed string.""" var size: Int """The number of elements in the vector.""" @@ -301,7 +301,7 @@ struct _FixedString[CAP: Int]( fn __init__(inout self): """Constructs a new empty string.""" - self.buffer = _ArrayMem[Int8, CAP]() + self.buffer = InlineArray[Int8, CAP](unsafe_uninitialized=True) self.size = 0 @always_inline @@ -320,10 +320,14 @@ struct _FixedString[CAP: Int]( + ")" ) - self.buffer = _ArrayMem[Int8, CAP]() + self.buffer = InlineArray[Int8, CAP](unsafe_uninitialized=True) self.size = len(literal) - memcpy(self.buffer.as_ptr(), literal.unsafe_ptr(), len(literal)) + memcpy( + DTypePointer(self.buffer.unsafe_ptr()), + literal.unsafe_ptr(), + len(literal), + ) # ===------------------------------------------------------------------=== # # Trait Interfaces @@ -381,7 +385,7 @@ struct _FixedString[CAP: Int]( # Append the bytes from `strref` at the end of the current string memcpy( - self.buffer.as_ptr().bitcast[UInt8]() + len(self), + DTypePointer(self.buffer.unsafe_ptr().bitcast[UInt8]() + len(self)), strref.data, len(strref), ) @@ -453,7 +457,7 @@ struct _FixedString[CAP: Int]( Returns: The pointer to the underlying memory. """ - return self.buffer.as_ptr() + return self.buffer.unsafe_ptr() fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: """Retrieves a pointer to the underlying memory. @@ -461,7 +465,7 @@ struct _FixedString[CAP: Int]( Returns: The pointer to the underlying memory. """ - return self.buffer.as_ptr().bitcast[UInt8]() + return self.buffer.unsafe_ptr().bitcast[UInt8]() fn _strref_dangerous(self) -> StringRef: """ @@ -479,71 +483,3 @@ struct _FixedString[CAP: Int]( without the string getting deallocated early. """ pass - - -# ===----------------------------------------------------------------------===# -# _ArrayMem -# ===----------------------------------------------------------------------===# - - -@value -struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): - """A fixed-sized, homogeneous, contiguous, inline collection type. - - Parameters: - ElementType: The type of the elements in the array. - SIZE: The fixed number of elements stored in the array. - """ - - var storage: InlineArray[ElementType, SIZE] - """The underlying storage for this array value.""" - - # ===------------------------------------------------------------------===# - # Constructors - # ===------------------------------------------------------------------===# - - @always_inline - fn __init__(inout self): - """Constructs an empty (undefined) array.""" - - self.storage = InlineArray[ElementType, SIZE](unsafe_uninitialized=True) - - # ===------------------------------------------------------------------=== # - # Trait Interfaces - # ===------------------------------------------------------------------=== # - - fn __len__(self) -> Int: - """Returns the length of the array. This is a known constant value. - - Returns: - The length of the array - """ - return SIZE - - fn __setitem__(inout self, index: Int, owned value: ElementType): - var ptr = __mlir_op.`pop.array.gep`( - UnsafePointer(Reference(self.storage._array)).address, index.value - ) - __mlir_op.`pop.store`(value, ptr) - - # ===------------------------------------------------------------------=== # - # Methods - # ===------------------------------------------------------------------=== # - - fn as_ptr(self) -> LegacyPointer[ElementType]: - """Get a pointer to the elements contained by this array. - - Returns: - A pointer to the elements contained by this array. - """ - - return LegacyPointer.address_of(self.storage).bitcast[ElementType]() - - fn unsafe_ptr(inout self) -> UnsafePointer[ElementType]: - """Get a pointer to the elements contained by this array. - - Returns: - A pointer to the elements contained by this array. - """ - - return UnsafePointer.address_of(self.storage).bitcast[ElementType]() diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index ea74e9f744..54c7b05df8 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -262,6 +262,10 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): var _array: Self.type """The underlying storage for the array.""" + # ===------------------------------------------------------------------===# + # Initializers + # ===------------------------------------------------------------------===# + @always_inline fn __init__(inout self): """This constructor will always cause a compile time error if used. @@ -271,7 +275,8 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): False, ( "Initialize with either a variadic list of arguments, a default" - " fill element or pass the keyword argument 'uninitialized'." + " fill element or pass the keyword argument" + " 'unsafe_uninitialized'." ), ]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() @@ -331,12 +336,16 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): UnsafePointer[Self.ElementType](ref), elems[i] ) + # ===------------------------------------------------------------------=== # + # Trait Interfaces + # ===------------------------------------------------------------------=== # + @always_inline("nodebug") fn __len__(self) -> Int: """Returns the length of the array. This is a known constant value. Returns: - The size of the list. + The size of the array. """ return size @@ -354,6 +363,10 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): ) return UnsafePointer(ptr)[] + # ===------------------------------------------------------------------===# + # Operator dunders + # ===------------------------------------------------------------------===# + @always_inline("nodebug") fn __refitem__[ IntableType: Intable, @@ -406,9 +419,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): return self[]._get_reference_unsafe(normalized_idx) @always_inline - fn unsafe_ptr( - self: Reference[Self, _, _] - ) -> UnsafePointer[Self.ElementType]: + fn unsafe_ptr(self) -> UnsafePointer[Self.ElementType]: """Get an `UnsafePointer` to the underlying array. That pointer is unsafe but can be used to read or write to the array. @@ -420,4 +431,4 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): Returns: An `UnsafePointer` to the underlying array. """ - return UnsafePointer(self[]._array).bitcast[Self.ElementType]() + return UnsafePointer(self._array).bitcast[Self.ElementType]() diff --git a/stdlib/test/collections/test_array.mojo b/stdlib/test/collections/test_array.mojo deleted file mode 100644 index d7afab9dcb..0000000000 --- a/stdlib/test/collections/test_array.mojo +++ /dev/null @@ -1,38 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# RUN: %mojo --debug-level full %s - -from testing import assert_equal - -from utils.inlined_string import _ArrayMem - - -def main(): - test_array_mem() - - -def test_array_mem(): - var array = _ArrayMem[Int, 4](1) - - assert_equal(array.SIZE, 4) - assert_equal(len(array), 4) - - # ================================== - # Test pointer operations - # ================================== - - var ptr = array.unsafe_ptr() - assert_equal(ptr[0], 1) - assert_equal(ptr[1], 1) - assert_equal(ptr[2], 1) - assert_equal(ptr[3], 1) From d87861d7d0586618b5bcf56c773ae979ef76c7a1 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 16 May 2024 23:03:03 -0500 Subject: [PATCH 0597/2019] [stdlib] cleanup: Change `StringLiteral.unsafe_ptr()` to `UnsafePointer` (was `DTypePointer`) Also add comment headers to UnsafePointer MODULAR_ORIG_COMMIT_REV_ID: 6569da050be6da0afbe1bdea8a74f2591b3465b5 --- stdlib/src/base64/base64.mojo | 16 +++++------ stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/hex.mojo | 2 +- stdlib/src/builtin/string_literal.mojo | 8 ++++-- stdlib/src/memory/unsafe_pointer.mojo | 39 +++++++++++++++++++++++++- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 5f854dc023..c40f47e9df 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -86,21 +86,21 @@ fn b64encode(str: String) -> String: var si = s(i) var si_1 = s(i + 1) var si_2 = s(i + 2) - out.append(b64chars.load(si // 4)) - out.append(b64chars.load(((si * 16) % 64) + si_1 // 16)) - out.append(b64chars.load(((si_1 * 4) % 64) + si_2 // 64)) - out.append(b64chars.load(si_2 % 64)) + out.append(b64chars[si // 4]) + out.append(b64chars[((si * 16) % 64) + si_1 // 16]) + out.append(b64chars[((si_1 * 4) % 64) + si_2 // 64]) + out.append(b64chars[si_2 % 64]) if end < length: var si = s(end) - out.append(b64chars.load(si // 4)) + out.append(b64chars[si // 4]) if end == length - 1: - out.append(b64chars.load((si * 16) % 64)) + out.append(b64chars[(si * 16) % 64]) out.append(ord("=")) elif end == length - 2: var si_1 = s(end + 1) - out.append(b64chars.load(((si * 16) % 64) + si_1 // 16)) - out.append(b64chars.load((si_1 * 4) % 64)) + out.append(b64chars[((si * 16) % 64) + si_1 // 16]) + out.append(b64chars[(si_1 * 4) % 64]) out.append(ord("=")) out.append(0) return String(out^) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 352451e700..be4e4360b7 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -62,7 +62,7 @@ struct Error(Stringable, Boolable): The constructed Error object. """ return Error { - data: value.unsafe_ptr().bitcast[DType.uint8](), + data: DTypePointer(value.unsafe_ptr()).bitcast[DType.uint8](), loaded_length: len(value), } diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index c13e5195c7..4e450d93a8 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -165,7 +165,7 @@ fn _try_write_int( # Write the char representing the value of the least significant # digit. - buf[offset] = digit_chars_array[digit_value] + buf[offset] = digit_chars_array[int(digit_value)] # Position the offset to write the next digit. offset -= 1 diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 1d382e8ac4..6349a0c39f 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -82,13 +82,17 @@ struct StringLiteral( return __mlir_op.`pop.string.size`(self.value) @always_inline("nodebug") - fn unsafe_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> UnsafePointer[Int8]: """Get raw pointer to the underlying data. Returns: The raw pointer to the data. """ - return __mlir_op.`pop.string.address`(self.value) + var ptr = DTypePointer[DType.int8]( + __mlir_op.`pop.string.address`(self.value) + ) + + return UnsafePointer[Int8]._from_dtype_ptr(ptr) @always_inline("nodebug") fn __bool__(self) -> Bool: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index ce65ab59e7..877c2a4a60 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -54,6 +54,10 @@ struct UnsafePointer[ var address: Self._mlir_type """The underlying pointer.""" + # ===-------------------------------------------------------------------===# + # Initializers + # ===-------------------------------------------------------------------===# + @always_inline fn __init__() -> Self: """Create a null pointer. @@ -103,6 +107,19 @@ struct UnsafePointer[ ) } + # ===-------------------------------------------------------------------===# + # Factory methods + # ===-------------------------------------------------------------------===# + + @staticmethod + fn _from_dtype_ptr[ + dtype: DType + ](ptr: DTypePointer[dtype]) -> UnsafePointer[Scalar[dtype]]: + # TODO: + # Is there a better way to create an UnsafePointer from a + # DTypePointer? + return UnsafePointer[Scalar[dtype]](address=int(ptr)) + @staticmethod @always_inline("nodebug") fn get_null() -> Self: @@ -147,6 +164,10 @@ struct UnsafePointer[ """ return Self(arg) + # ===-------------------------------------------------------------------===# + # Methods + # ===-------------------------------------------------------------------===# + @always_inline fn free(self): """Free the memory referenced by the pointer.""" @@ -172,6 +193,18 @@ struct UnsafePointer[ _type = UnsafePointer[new_type, address_space]._mlir_type, ](self.address) + @always_inline + fn offset(self, offset: Int) -> Self: + """Return a pointer at an offset from the current one. + + Args: + offset: The offset index. + + Returns: + An offset pointer. + """ + return Self(address=int(self) + offset * sizeof[T]()) + @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. @@ -186,6 +219,10 @@ struct UnsafePointer[ fn __str__(self) -> String: return hex(self) + # ===-------------------------------------------------------------------===# + # Operator dunders + # ===-------------------------------------------------------------------===# + @always_inline fn __bool__(self) -> Bool: """Return true if the pointer is non-null. @@ -205,7 +242,7 @@ struct UnsafePointer[ Returns: An offset pointer. """ - return Self(address=int(self) + offset * sizeof[T]()) + return self.offset(offset) @always_inline fn __sub__(self, offset: Int) -> Self: From 821fc174ffe1a84d1b9a3a3a9e98fc2251ae931f Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 16 May 2024 23:54:00 -0500 Subject: [PATCH 0598/2019] [stdlib] cleanup: Switch inlined_string.mojo to UnsafePointer[UInt8] * Change InlinedString.as_ptr() and _FixedString.as_ptr(): - Rename to unsafe_ptr() - Return UnsafePointer instead of DTypePointer - Return unsigned UInt8 instead of signed DType.int8 * Delete InlinedString.as_uint8_ptr() and _FixedString.as_uint8_ptr() * Change _FixedString.buffer element from Int8 to UInt8 MODULAR_ORIG_COMMIT_REV_ID: d2b2eef03cadd160434a0185f48b2ac218c240b5 --- docs/changelog.md | 8 ++++ stdlib/src/utils/inlined_string.mojo | 65 +++++++++++----------------- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5276e46786..cc6e9b9bac 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -304,6 +304,14 @@ what we publish. - `ListLiteral` and `Tuple` now only requires that element types be `Copyable`. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. +- Continued transition to `UnsafePointer` and unsigned byte type for strings: + - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was + `DTypePointer`). + - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now + returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + +- Added `UnsafePointer.offset()` method. + - The `math.bit` module has been moved to a new top-level `bit` module. The following functions in this module have been renamed: - `ctlz` -> `countl_zero` diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 6216a4bd51..776563fc43 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -153,19 +153,23 @@ struct InlinedString(Sized, Stringable, CollectionElement): # Begin by heap allocating enough space to store the combined # string. - var buffer = List[Int8](capacity=total_len) - - var buffer_ptr = rebind[DTypePointer[DType.uint8]](buffer.data) + var buffer = List[UInt8](capacity=total_len) # Copy the bytes from the current small string layout memcpy( - buffer_ptr, - self._storage[_FixedString[Self.SMALL_CAP]].as_uint8_ptr(), - len(self), + dest=buffer.unsafe_ptr(), + src=self._storage[_FixedString[Self.SMALL_CAP]].unsafe_ptr(), + count=len(self), ) # Copy the bytes from the additional string. - memcpy(buffer_ptr + len(self), strref.data, len(strref)) + memcpy( + dest=buffer.unsafe_ptr() + len(self), + src=UnsafePointer[Int8]._from_dtype_ptr( + strref.unsafe_uint8_ptr() + ), + count=len(strref), + ) # Record that we've initialized `total_len` count of elements # in `buffer` @@ -231,7 +235,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): # TODO: Remove this when we have transitioned to uint8 for bytes. # See https://github.com/modularml/mojo/issues/2317 for details - fn as_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Returns a pointer to the bytes of string data. Returns: @@ -239,21 +243,11 @@ struct InlinedString(Sized, Stringable, CollectionElement): """ if self._is_small(): - return self._storage[_FixedString[Self.SMALL_CAP]].as_ptr() + return self._storage[_FixedString[Self.SMALL_CAP]].unsafe_ptr() else: - return self._storage[String].unsafe_ptr() - - fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: - """Returns a pointer to the bytes of string data. - - Returns: - The pointer to the underlying memory. - """ + var string_ptr = self._storage[String].unsafe_uint8_ptr() - if self._is_small(): - return self._storage[_FixedString[Self.SMALL_CAP]].as_uint8_ptr() - else: - return self._storage[String].unsafe_uint8_ptr() + return UnsafePointer[UInt8]._from_dtype_ptr(string_ptr) fn _strref_dangerous(self) -> StringRef: """ @@ -262,7 +256,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.as_uint8_ptr(), length: len(self)} + return StringRef {data: self.unsafe_ptr(), length: len(self)} fn _strref_keepalive(self): """ @@ -290,7 +284,7 @@ struct _FixedString[CAP: Int]( CAP: The fixed-size count of bytes of string storage capacity available. """ - var buffer: InlineArray[Int8, CAP] + var buffer: InlineArray[UInt8, CAP] """The underlying storage for the fixed string.""" var size: Int """The number of elements in the vector.""" @@ -301,7 +295,7 @@ struct _FixedString[CAP: Int]( fn __init__(inout self): """Constructs a new empty string.""" - self.buffer = InlineArray[Int8, CAP](unsafe_uninitialized=True) + self.buffer = InlineArray[UInt8, CAP](unsafe_uninitialized=True) self.size = 0 @always_inline @@ -320,13 +314,14 @@ struct _FixedString[CAP: Int]( + ")" ) - self.buffer = InlineArray[Int8, CAP](unsafe_uninitialized=True) + self.buffer = InlineArray[UInt8, CAP](unsafe_uninitialized=True) self.size = len(literal) memcpy( - DTypePointer(self.buffer.unsafe_ptr()), - literal.unsafe_ptr(), - len(literal), + dest=self.buffer.unsafe_ptr(), + # TODO: Remove bitcast after string transition to UInt8 is complete. + src=literal.unsafe_ptr().bitcast[UInt8](), + count=len(literal), ) # ===------------------------------------------------------------------=== # @@ -449,9 +444,7 @@ struct _FixedString[CAP: Int]( return output^ - # TODO: Remove this when we have transitionned to uint8 for bytes. - # See https://github.com/modularml/mojo/issues/2317 for details - fn as_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. Returns: @@ -459,14 +452,6 @@ struct _FixedString[CAP: Int]( """ return self.buffer.unsafe_ptr() - fn as_uint8_ptr(self) -> DTypePointer[DType.uint8]: - """Retrieves a pointer to the underlying memory. - - Returns: - The pointer to the underlying memory. - """ - return self.buffer.unsafe_ptr().bitcast[UInt8]() - fn _strref_dangerous(self) -> StringRef: """ Returns an inner pointer to the string as a StringRef. @@ -474,7 +459,7 @@ struct _FixedString[CAP: Int]( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.as_uint8_ptr(), length: len(self)} + return StringRef {data: self.unsafe_ptr(), length: len(self)} fn _strref_keepalive(self): """ From 87af4c20a161b70ad19691ae09276af7c60c899b Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 17 May 2024 00:08:33 -0700 Subject: [PATCH 0599/2019] [mojo] Introduce `@parameter for` and replace all `@unroll` This PR deprecates and removes the `@unroll` feature and replaces with it `@parameter for`. The `@unroll` decorator was an experiment in compiler directives to implement performance semantics, but in practice proved brittle. The new feature leverages the parameter system to actually guarantee that the loop is unrolled. For real this time. It does so by requiring that the iterator value is a parameter value, and it also makes the induction variable a parameter value as well. MODULAR_ORIG_COMMIT_REV_ID: 0b1427fd52bc531f94c8a47a75e4f7ab08ac2540 --- docs/manual/decorators/unroll.ipynb | 324 ++++++++++++++-------------- docs/manual/parameters/index.ipynb | 2 +- examples/matmul.mojo | 2 +- proposals/improved-hash-module.md | 18 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/builtin/simd.mojo | 14 +- stdlib/src/memory/memory.mojo | 2 +- stdlib/src/utils/index.mojo | 4 +- stdlib/src/utils/static_tuple.mojo | 4 +- 9 files changed, 186 insertions(+), 186 deletions(-) diff --git a/docs/manual/decorators/unroll.ipynb b/docs/manual/decorators/unroll.ipynb index e6f3d8138b..d0300178ac 100644 --- a/docs/manual/decorators/unroll.ipynb +++ b/docs/manual/decorators/unroll.ipynb @@ -1,164 +1,164 @@ { - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "title: '`@unroll`'\n", - "description: Unrolls a loop at compile time.\n", - "---" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can add the `@unroll` decorator on any loop (such as `for` and `while`) to\n", - "make the Mojo compiler [unroll the\n", - "loop](https://en.wikipedia.org/wiki/Loop_unrolling), either fully or with a\n", - "given unroll factor.\n", - "\n", - "For example, the compiler will unroll all 10 iterations of the following loop\n", - "into 10 consecutive calls to `print()` (removing the `for` loop entirely):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@unroll\n", - "for i in range(10):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The decorator also accepts an \"unroll factor\" argument, which specifies how\n", - "many iterations to unroll at once. For example, the unroll above is equivalent\n", - "to `@unroll(10)` because it unrolls all iterations of the loop. So if you pass\n", - "a number smaller than the loop bounds, the compiler creates multiple unrolls.\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Unroll every 2 iterations, leaving a loop with 5 iterations.\n", - "@unroll(2)\n", - "for i in range (10):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is equivalent to this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(0, 10, 2):\n", - " print(i)\n", - " print(i+1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, the compiler can unroll a loop only when the following statements are\n", - "true:\n", - "\n", - "- The loop's lower bound, upper bound, and induction step size are compile-time\n", - "constants (they do not vary at runtime). For example, in the above code\n", - "`range(0, 10, 2)`, `0` is the lower bound, `10` is the upper bound, and `2`\n", - "is the induction step size—these could instead be defined with variable names,\n", - "but the values cannot vary at runtime.\n", - "\n", - "- Likewise, there are no early exits in the loop that make the loop count\n", - "variable at runtime." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Compared to `unroll()`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Mojo standard library also includes a function called\n", - "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", - "given function that you want to call repeatedly, but has some important\n", - "differences when compared to the `@unroll` decorator:\n", - "\n", - "- The `@unroll` decorator operates on loop expressions only, not on functions\n", - " like the `unroll()` function does.\n", - "\n", - "- The `@unroll` decorator determines how to unroll the loop based on the\n", - " induction variable (`i`), the value of which _is not_ known when compilation\n", - " begins. Whereas, the `unroll()` function calls upon your looping function\n", - " (`func`) with the `Int` loop index parameter that _is_ known at compile time.\n", - "\n", - " This means two things:\n", - "\n", - " - Within a loop using the `@unroll` decorator, the `i` induction variable is \n", - " still a runtime variable, so you _cannot_ use it as a parameter value (such\n", - " as for `SIMD[Int8, i]`). Whereas, within the `func` callback used with the\n", - " `unroll()` function, the `Int` loop index is known at compile time, so you\n", - " _can_ use it as a parameter value.\n", - "\n", - " - The `unroll()` function unrolls at the beginning of compilation, which\n", - " might explode the program size that still needs to be compiled, depending\n", - " on the amount of code that's unrolled. Whereas, the `@unroll` decorator\n", - " performs unrolling later in the compilation, after the compiler is able to\n", - " evaluate the induction variable (`i`), which avoids early explosion of the\n", - " program size that still needs compilation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Mojo", - "language": "mojo", - "name": "mojo-jupyter-kernel" - }, - "language_info": { - "codemirror_mode": { - "name": "mojo" - }, - "file_extension": ".mojo", - "mimetype": "text/x-mojo", - "name": "mojo" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: '`@parameter`'\n", + "description: Unrolls a loop at compile time.\n", + "---" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can add the `@parameter` decorator on any loop (such as `for` and `while`) to\n", + "make the Mojo compiler [unroll the\n", + "loop](https://en.wikipedia.org/wiki/Loop_unrolling), either fully or with a\n", + "given unroll factor.\n", + "\n", + "For example, the compiler will unroll all 10 iterations of the following loop\n", + "into 10 consecutive calls to `print()` (removing the `for` loop entirely):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@parameter\n", + "for i in range(10):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The decorator also accepts an \"unroll factor\" argument, which specifies how\n", + "many iterations to unroll at once. For example, the unroll above is equivalent\n", + "to `@parameter(10)` because it unrolls all iterations of the loop. So if you pass\n", + "a number smaller than the loop bounds, the compiler creates multiple unrolls.\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Unroll every 2 iterations, leaving a loop with 5 iterations.\n", + "# @parameter(2)\n", + "for i in range (10):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The result is equivalent to this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(0, 10, 2):\n", + " print(i)\n", + " print(i+1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, the compiler can unroll a loop only when the following statements are\n", + "true:\n", + "\n", + "- The loop's lower bound, upper bound, and induction step size are compile-time\n", + "constants (they do not vary at runtime). For example, in the above code\n", + "`range(0, 10, 2)`, `0` is the lower bound, `10` is the upper bound, and `2`\n", + "is the induction step size—these could instead be defined with variable names,\n", + "but the values cannot vary at runtime.\n", + "\n", + "- Likewise, there are no early exits in the loop that make the loop count\n", + "variable at runtime." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compared to `unroll()`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Mojo standard library also includes a function called\n", + "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", + "given function that you want to call repeatedly, but has some important\n", + "differences when compared to the `@parameter` decorator:\n", + "\n", + "- The `@parameter` decorator operates on loop expressions only, not on functions\n", + " like the `unroll()` function does.\n", + "\n", + "- The `@parameter` decorator determines how to unroll the loop based on the\n", + " induction variable (`i`), the value of which _is not_ known when compilation\n", + " begins. Whereas, the `unroll()` function calls upon your looping function\n", + " (`func`) with the `Int` loop index parameter that _is_ known at compile time.\n", + "\n", + " This means two things:\n", + "\n", + " - Within a loop using the `@parameter` decorator, the `i` induction variable is \n", + " still a runtime variable, so you _cannot_ use it as a parameter value (such\n", + " as for `SIMD[Int8, i]`). Whereas, within the `func` callback used with the\n", + " `unroll()` function, the `Int` loop index is known at compile time, so you\n", + " _can_ use it as a parameter value.\n", + "\n", + " - The `unroll()` function unrolls at the beginning of compilation, which\n", + " might explode the program size that still needs to be compiled, depending\n", + " on the amount of code that's unrolled. Whereas, the `@parameter` decorator\n", + " performs unrolling later in the compilation, after the compiler is able to\n", + " evaluate the induction variable (`i`), which avoids early explosion of the\n", + " program size that still needs compilation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Mojo", + "language": "mojo", + "name": "mojo-jupyter-kernel" + }, + "language_info": { + "codemirror_mode": { + "name": "mojo" + }, + "file_extension": ".mojo", + "mimetype": "text/x-mojo", + "name": "mojo" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 1803fe8e35..816a0bb4d1 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -47,7 +47,7 @@ "outputs": [], "source": [ "fn repeat[count: Int](msg: String):\n", - " @unroll\n", + " @parameter\n", " for i in range(count):\n", " print(msg)" ] diff --git a/examples/matmul.mojo b/examples/matmul.mojo index b1a1010f0e..47f04f8b06 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -180,7 +180,7 @@ fn matmul_unrolled[mode: Int](inout C: Matrix, A: Matrix, B: Matrix): fn calc_row(m: Int): @parameter fn calc_tile[tile_x: Int, tile_y: Int](x: Int, y: Int): - @unroll + @parameter for _k in range(tile_y): var k = _k + y diff --git a/proposals/improved-hash-module.md b/proposals/improved-hash-module.md index 2d8714b0c1..1c82233c5a 100644 --- a/proposals/improved-hash-module.md +++ b/proposals/improved-hash-module.md @@ -38,7 +38,7 @@ struct Person(Hashable): hashes.append(hash(self.age)) for friend in self.friends_names: hashes.append(hash(friend[])) - + # How to combine a hash of hashes ??? ``` @@ -75,8 +75,8 @@ trait Hasher: """Contribute to the hash value with a Hashable value. Should be used by implementors of Hashable types which are a composition of Hashable types.""" ... fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: - """Used internally to generate the final hash value, should be simplified to `_finish(owned self) -> Scalar[hash_value_dt]` - once trait declarations support parameters and we can switch to `trait Hasher[hash_value_dt: DType]`. + """Used internally to generate the final hash value, should be simplified to `_finish(owned self) -> Scalar[hash_value_dt]` + once trait declarations support parameters and we can switch to `trait Hasher[hash_value_dt: DType]`. This is beneficial as hash functions have different implementations based on the type """ ... ``` @@ -173,8 +173,8 @@ trait Hasher: """Contribute to the hash value with a Hashable value. Should be used by implementors of Hashable types which are a composition of Hashable types.""" ... fn _finish[dt: DType = DType.uint64](owned self) -> Scalar[dt]: - """Used internally to generate the final hash value, should be simplified to `_finish(owned self) -> Scalar[hash_value_dt]` - once trait declarations support parameters and we can switch to `trait Hasher[hash_value_dt: DType]`. + """Used internally to generate the final hash value, should be simplified to `_finish(owned self) -> Scalar[hash_value_dt]` + once trait declarations support parameters and we can switch to `trait Hasher[hash_value_dt: DType]`. This is beneficial as hash functions have different implementations based on the type """ ... @@ -182,7 +182,7 @@ trait Hasher: struct MyInt(Hashable): """An example for the Int type.""" var value: Int - + @always_inline fn __hash__[H: Hasher](self, inout hasher: H): hasher._update_with_simd(Int64(self.value)) @@ -229,7 +229,7 @@ fn _DJBX33A_SECRET() -> UInt64: struct DJBX33A_Hasher[custom_secret: UInt64 = 0](Hasher): """Example of a simple Hasher, with an option to provide a custom secret at compile time. - When custom secret is set to 0 the secret will be looked up in env var DJBX33A_SECRET. + When custom secret is set to 0 the secret will be looked up in env var DJBX33A_SECRET. In case env var DJBX33A_SECRET is not set a random int will be generated.""" var hash_data: UInt64 var secret: UInt64 @@ -254,10 +254,10 @@ struct DJBX33A_Hasher[custom_secret: UInt64 = 0](Hasher): """The algorithm is not optimal.""" alias size_in_bytes = size * dt.sizeof() var bytes = bitcast[DType.uint8, size_in_bytes](value) - @unroll + @parameter for i in range(size_in_bytes): self.hash_data = self.hash_data * 33 + bytes[i].cast[DType.uint64]() - + @always_inline fn update[T: Hashable](inout self, value: T): value.__hash__(self) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 99e3f4b0db..2d670545d9 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -296,7 +296,7 @@ fn _put[type: DType, simd_width: Int](x: SIMD[type, simd_width]): elif type.is_integral(): _put("[") - @unroll + @parameter for i in range(simd_width): _put_simd_scalar(x[i]) if i != simd_width - 1: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 212ab13cdf..ecdcfed2ea 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -330,7 +330,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( debug_assert(size == num_elements, "mismatch in the number of elements") self = Self() - @unroll + @parameter for i in range(size): self[i] = elems[i] @@ -1829,7 +1829,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if offset % simdwidthof[type](): var tmp = SIMD[type, output_width]() - @unroll + @parameter for i in range(output_width): tmp[i] = self[i + offset] return tmp @@ -1877,7 +1877,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if offset % simdwidthof[type](): var tmp = self - @unroll + @parameter for i in range(input_width): tmp[i + offset] = value[i] return tmp @@ -2507,7 +2507,7 @@ fn _pow[ abort() else: - @unroll + @parameter for i in range(simd_width): result[i] = llvm_intrinsic[ "llvm.pow", Scalar[lhs_type], has_side_effect=False @@ -2523,7 +2523,7 @@ fn _pow[ var result = SIMD[lhs_type, simd_width]() - @unroll + @parameter for i in range(simd_width): result[i] = _powi(lhs[i], rhs[i].cast[DType.int32]()) return result @@ -2676,7 +2676,7 @@ fn _simd_apply[ """ var result = SIMD[result_type, simd_width]() - @unroll + @parameter for i in range(simd_width): result[i] = func[x.type, result_type](x[i]) @@ -2711,7 +2711,7 @@ fn _simd_apply[ """ var result = SIMD[result_type, simd_width]() - @unroll + @parameter for i in range(simd_width): result[i] = func[x.type, y.type, result_type](x[i], y[i]) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index f3bc30e4ef..dbb3cd7db3 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -183,7 +183,7 @@ fn memcpy[count: Int](dest: LegacyPointer, src: __type_of(dest)): @parameter if n < 5: - @unroll + @parameter for i in range(n): dest_data[i] = src_data[i] return diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index a230c104dd..cc6c22f245 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -297,7 +297,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): var tup = Self() - @unroll + @parameter for idx in range(size): tup[idx] = elems[idx] @@ -392,7 +392,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): """ var length: Int = 1 - @unroll + @parameter for i in range(size): length *= self[i] diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 54c7b05df8..c588d1cd28 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -313,7 +313,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): _static_tuple_construction_checks[size]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - @unroll + @parameter for i in range(size): var ptr = self._get_reference_unsafe(i) initialize_pointee_copy(UnsafePointer[Self.ElementType](ptr), fill) @@ -329,7 +329,7 @@ struct InlineArray[ElementType: CollectionElement, size: Int](Sized): _static_tuple_construction_checks[size]() self._array = __mlir_op.`kgen.undef`[_type = Self.type]() - @unroll + @parameter for i in range(size): var ref = self._get_reference_unsafe(i) initialize_pointee_move( From 14af4a0d4d9c5ed7b774b7e4bac1a8bc2a9974ad Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Fri, 17 May 2024 00:27:03 -0700 Subject: [PATCH 0600/2019] [mojo] Changelog entry for `@parameter for` This PR updates the changelog with a new feature. MODULAR_ORIG_COMMIT_REV_ID: 004a48471338b688719f8ce6407752fa346481b4 --- docs/changelog.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index cc6e9b9bac..96a68f5cf1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,24 @@ what we publish. ### ⭐️ New +- Mojo has introduced `@parameter for`, a new feature for compile-time + programming. `@parameter for` defines a for loop where the sequence must be a + parameter value and the induction variables are also parameter values in that + sequence. For example: + + ```mojo + fn parameter_for[max: Int](): + @parameter + for i in range(max) + @parameter + if i == 10: + print("found 10!") + ``` + + Currently, `@parameter for` does not allow early exits in the body (`return`, + `break`, `continue`, etc.) and requires the sequence's `__iter__` method to + return a `_StridedRangeIterator`. These restrictions will be lifted soon. + - Mojo added support for the `inferred` passing kind on parameters. `inferred` parameters must appear at the beginning of the parameter list and cannot be explicitly specified by the user. This allows users to define functions with @@ -331,6 +349,14 @@ what we publish. ### ❌ Removed +- The `@unroll` decorator has been deprecated and removed. The decorator was + supposed to guarantee that a decorated loop would be unrolled, or else the + compiler would error. In practice, this guarantee was eroded over time, as + a compiler-based approach cannot be as robust as the Mojo parameter system. + In addition, the `@unroll` decorator did not make the loop induction variables + parameter values, limiting its usefulness. Please see `@parameter for` for a + replacement! + - The method `object.print()` has been removed. Since now, `object` has the `Stringable` trait, you can use `print(my_object)` instead. From 9558f82ab5f63022ce7f517bcf736d72a381a2e6 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 17 May 2024 02:38:21 -0500 Subject: [PATCH 0601/2019] [stdlib] cleanup: Switch stringref.mojo to `UnsafePointer[UInt8]` * Change `StringRef.data` to `UnsafePointer` (was `DTypePointer`) * Change `StringRef.unsafe_ptr()` to `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`) * Remove `StringRef.unsafe_uint8_ptr()` * Add isspace(UInt8) MODULAR_ORIG_COMMIT_REV_ID: 26f2642e72bc345900393312cedfa646cf3a6659 --- docs/changelog.md | 7 +++++ stdlib/src/builtin/error.mojo | 6 ++++- stdlib/src/builtin/file.mojo | 3 ++- stdlib/src/builtin/object.mojo | 7 ++++- stdlib/src/builtin/string.mojo | 14 ++++++---- stdlib/src/utils/inlined_string.mojo | 4 +-- stdlib/src/utils/stringref.mojo | 39 +++++++++++----------------- 7 files changed, 45 insertions(+), 35 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 96a68f5cf1..a7b11d5f5d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -327,6 +327,13 @@ what we publish. `DTypePointer`). - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now returns an `UnsafePointer[UInt8]` (was `DTypePointer[DType.int8]`). + - `StringRef.data` is now an `UnsafePointer` (was `DTypePointer`) + - `StringRef.unsafe_ptr()` now returns an `UnsafePointer[UInt8]` (was + `DTypePointer[DType.int8]`). + - Removed `StringRef.unsafe_uint8_ptr()`. The `unsafe_ptr()` method now has + the same behavior. + +- Changed `isspace(..)` to take an `Int`. - Added `UnsafePointer.offset()` method. diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index be4e4360b7..28acdab800 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -94,7 +94,11 @@ struct Error(Stringable, Boolable): """ var length = len(src) var dest = Self.StorageType.alloc(length + 1) - memcpy(dest, src.data.bitcast[DType.uint8](), length) + memcpy( + dest, + DTypePointer(src.unsafe_ptr()).bitcast[DType.uint8](), + length, + ) dest[length] = 0 return Error {data: dest, loaded_length: -length} diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 093c90ddc2..0296101e61 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -423,7 +423,8 @@ struct FileHandle: Args: data: The data to write to the file. """ - self._write(data.unsafe_ptr(), len(data)) + # TODO: Remove cast when transition to UInt8 strings is complete. + self._write(data.unsafe_ptr().bitcast[Int8](), len(data)) @always_inline fn _write[ diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 413f6b60ad..39116fc368 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -749,7 +749,12 @@ struct object(IntableRaising, Boolable, Stringable): var impl = _ImmutableString( UnsafePointer[Int8].alloc(value.length), value.length ) - memcpy(impl.data, value.unsafe_ptr(), value.length) + memcpy( + impl.data, + # TODO: Remove bitcast once transition to UInt8 strings is complete. + value.unsafe_ptr().bitcast[Int8](), + value.length, + ) self._value = impl @always_inline diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 0e8834d95e..fafa5e0b29 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -220,7 +220,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: var buff = str_ref.unsafe_ptr() for pos in range(start, str_len): - if isspace(buff[pos]): + if isspace(int(buff[pos])): continue if str_ref[pos] == "-": @@ -287,7 +287,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: break else: raise Error(_atol_error(base, str_ref)) - if pos + 1 < str_len and not isspace(buff[pos + 1]): + if pos + 1 < str_len and not isspace(int(buff[pos + 1])): var nextresult = result * real_base if nextresult < result: raise Error( @@ -301,7 +301,7 @@ fn _atol(str_ref: StringRef, base: Int = 10) raises -> Int: if has_space_after_number: for pos in range(start, str_len): - if not isspace(buff[pos]): + if not isspace(int(buff[pos])): raise Error(_atol_error(base, str_ref)) if is_negative: result = -result @@ -446,7 +446,8 @@ fn _is_ascii_lowercase(c: Int8) -> Bool: # ===----------------------------------------------------------------------===# -fn isspace(c: Int8) -> Bool: +# TODO(MSTDL-160): Make this take a Unicode codepoint type +fn isspace(c: Int) -> Bool: """Determines whether the given character is a whitespace character. This currently only respects the default "C" locale, i.e. returns True only if the character specified is one of @@ -1143,7 +1144,10 @@ struct String( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef {data: self.unsafe_uint8_ptr(), length: len(self)} + return StringRef { + data: UnsafePointer[UInt8]._from_dtype_ptr(self.unsafe_uint8_ptr()), + length: len(self), + } fn _strref_keepalive(self): """ diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 776563fc43..9eeec6edaf 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -165,9 +165,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): # Copy the bytes from the additional string. memcpy( dest=buffer.unsafe_ptr() + len(self), - src=UnsafePointer[Int8]._from_dtype_ptr( - strref.unsafe_uint8_ptr() - ), + src=strref.unsafe_ptr(), count=len(strref), ) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index e17a99c1f3..9a83f5b2b8 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -50,7 +50,7 @@ struct StringRef( and a length, which need not be null terminated. """ - var data: DTypePointer[DType.uint8] + var data: UnsafePointer[UInt8] """A pointer to the beginning of the string data being referenced.""" var length: Int """The length of the string being referenced.""" @@ -95,8 +95,9 @@ struct StringRef( Returns: Constructed `StringRef` object. """ + var unsafe_ptr = UnsafePointer[Int8]._from_dtype_ptr(ptr) - return Self {data: ptr.bitcast[DType.uint8](), length: len} + return Self {data: unsafe_ptr.bitcast[UInt8](), length: len} @always_inline fn __init__(ptr: DTypePointer[DType.uint8], len: Int) -> StringRef: @@ -112,8 +113,9 @@ struct StringRef( Returns: Constructed `StringRef` object. """ + var unsafe_ptr = UnsafePointer[UInt8]._from_dtype_ptr(ptr) - return Self {data: ptr, length: len} + return Self {data: unsafe_ptr, length: len} # TODO: #2317 Drop support for this constructor when we have fully # transitioned to UInt8 as the main byte type. @@ -187,25 +189,14 @@ struct StringRef( return StringRef(ptr.bitcast[DType.int8](), len) - # TODO: #2317 Drop support for this method when we have fully - # transitioned to UInt8 as the main byte type. @always_inline - fn unsafe_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. Prefer to use `as_uint8_ptr()` instead. Returns: - The DTypePointer to the underlying memory. - """ - return self.data.bitcast[DType.int8]() - - @always_inline - fn unsafe_uint8_ptr(self) -> DTypePointer[DType.uint8]: - """Retrieves a pointer to the underlying memory. - - Returns: - The DTypePointer to the underlying memory. + The pointer to the underlying memory. """ return self.data @@ -400,16 +391,16 @@ struct StringRef( var haystack_str = self._from_start(start) var loc = _memmem( - haystack_str.unsafe_uint8_ptr(), + haystack_str.unsafe_ptr(), len(haystack_str), - substr.unsafe_uint8_ptr(), + substr.unsafe_ptr(), len(substr), ) if not loc: return -1 - return int(loc) - int(self.unsafe_uint8_ptr()) + return int(loc) - int(self.unsafe_ptr()) fn rfind(self, substr: StringRef, start: Int = 0) -> Int: """Finds the offset of the last occurrence of `substr` starting at @@ -433,16 +424,16 @@ struct StringRef( var haystack_str = self._from_start(start) var loc = _memrmem( - haystack_str.unsafe_uint8_ptr(), + haystack_str.unsafe_ptr(), len(haystack_str), - substr.unsafe_uint8_ptr(), + substr.unsafe_ptr(), len(substr), ) if not loc: return -1 - return int(loc) - int(self.unsafe_uint8_ptr()) + return int(loc) - int(self.unsafe_ptr()) fn _from_start(self, start: Int) -> StringRef: """Gets the StringRef pointing to the substring after the specified slice start position. @@ -496,9 +487,9 @@ struct StringRef( var start: Int = 0 var end: Int = len(self) var ptr = self.unsafe_ptr() - while start < end and isspace(ptr[start]): + while start < end and isspace(int(ptr[start])): start += 1 - while end > start and isspace(ptr[end - 1]): + while end > start and isspace(int(ptr[end - 1])): end -= 1 return StringRef(ptr + start, end - start) From eecacc57f3943c3762a6c1078a3579dff882cf31 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 17 May 2024 03:10:43 -0500 Subject: [PATCH 0602/2019] [stdlib] cleanup: Switch error.mojo to `UnsafePointer` * Change `Error.data` to `UnsafePointer` MODULAR_ORIG_COMMIT_REV_ID: 4d14fa5449f4c6a6f27dd311dd27bc024c9ce775 --- stdlib/src/builtin/error.mojo | 32 ++++++++++++++++++-------------- stdlib/src/builtin/file.mojo | 8 +++++++- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 28acdab800..41529689ea 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from sys import alignof, sizeof from memory.memory import _free -from memory import memcmp, memcpy, DTypePointer +from memory import memcmp, memcpy, UnsafePointer # ===----------------------------------------------------------------------===# # Error @@ -29,9 +29,7 @@ from memory import memcmp, memcpy, DTypePointer struct Error(Stringable, Boolable): """This type represents an Error.""" - alias StorageType = DTypePointer[DType.uint8] - - var data: Self.StorageType + var data: UnsafePointer[UInt8] """A pointer to the beginning of the string data being referenced.""" var loaded_length: Int @@ -49,7 +47,7 @@ struct Error(Stringable, Boolable): Returns: The constructed Error object. """ - return Error {data: Self.StorageType(), loaded_length: 0} + return Error {data: UnsafePointer[UInt8](), loaded_length: 0} @always_inline("nodebug") fn __init__(value: StringLiteral) -> Error: @@ -62,7 +60,8 @@ struct Error(Stringable, Boolable): The constructed Error object. """ return Error { - data: DTypePointer(value.unsafe_ptr()).bitcast[DType.uint8](), + # TODO: Remove cast once string UInt8 transition is complete. + data: value.unsafe_ptr().bitcast[UInt8](), loaded_length: len(value), } @@ -77,8 +76,13 @@ struct Error(Stringable, Boolable): The constructed Error object. """ var length = len(src) - var dest = Self.StorageType.alloc(length + 1) - memcpy(dest, src.unsafe_ptr().bitcast[DType.uint8](), length) + var dest = UnsafePointer[UInt8].alloc(length + 1) + memcpy( + dest=dest, + # TODO: Remove cast once string UInt8 transition is complete. + src=src.unsafe_ptr().bitcast[DType.uint8](), + count=length, + ) dest[length] = 0 return Error {data: dest, loaded_length: -length} @@ -93,11 +97,11 @@ struct Error(Stringable, Boolable): The constructed Error object. """ var length = len(src) - var dest = Self.StorageType.alloc(length + 1) + var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy( - dest, - DTypePointer(src.unsafe_ptr()).bitcast[DType.uint8](), - length, + dest=dest, + src=src.unsafe_ptr(), + count=length, ) dest[length] = 0 return Error {data: dest, loaded_length: -length} @@ -116,7 +120,7 @@ struct Error(Stringable, Boolable): """ if existing.loaded_length < 0: var length = -existing.loaded_length - var dest = Self.StorageType.alloc(length + 1) + var dest = UnsafePointer[UInt8].alloc(length + 1) memcpy(dest, existing.data, length) dest[length] = 0 return Error {data: dest, loaded_length: existing.loaded_length} @@ -162,4 +166,4 @@ struct Error(Stringable, Boolable): var length = self.loaded_length if length < 0: length = -length - return String(StringRef(self.data.bitcast[DType.int8](), length)) + return String(StringRef(self.data, length)) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 0296101e61..65d395a6f0 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -54,7 +54,13 @@ struct _OwnedStringRef(Boolable): # Don't free self.data in our dtor. self.data = DTypePointer[DType.int8]() var length = self.length - return Error {data: data.bitcast[DType.uint8](), loaded_length: -length} + return Error { + data: UnsafePointer[UInt8]._from_dtype_ptr( + # TODO: Remove cast once string UInt8 transition is complete. + data.bitcast[DType.uint8]() + ), + loaded_length: -length, + } fn __bool__(self) -> Bool: return self.length != 0 From 312689ecb1d42a9a151049c1bc61b7320709c97b Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 17 May 2024 04:26:41 -0500 Subject: [PATCH 0603/2019] [stdlib] cleanup: Switch string.mojo to `UnsafePointer` * Change `String.unsafe_ptr()` to return `UnsafePointer` (was `DTypePointer`) * Change `String.unsafe_uint8_ptr()` to return `UnsafePointer` (was `DTypePointer`) MODULAR_ORIG_COMMIT_REV_ID: 51d64eda90c0e248638854a8b9e4dfa8654bbd1c --- docs/changelog.md | 3 +++ stdlib/src/base64/base64.mojo | 6 +++-- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/string.mojo | 39 ++++++++++++---------------- stdlib/src/utils/inlined_string.mojo | 4 +-- 5 files changed, 26 insertions(+), 28 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a7b11d5f5d..43e433e3d6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -323,6 +323,9 @@ what we publish. Consequently, `ListLiteral` and `Tuple` are themselves no longer `Copyable`. - Continued transition to `UnsafePointer` and unsigned byte type for strings: + - `String.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`) + - `String.unsafe_uint8_ptr()` now returns `UnsafePointer` (was + `DTypePointer`) - `StringLiteral.unsafe_ptr()` now returns an `UnsafePointer` (was `DTypePointer`). - `InlinedString.as_ptr()` has been renamed to `unsafe_ptr()` and now diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index c40f47e9df..3bf20f904c 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -78,7 +78,8 @@ fn b64encode(str: String) -> String: @parameter @always_inline fn s(idx: Int) -> Int: - return int(str.unsafe_ptr().bitcast[DType.uint8]()[idx]) + # TODO: Remove cast once transition to UInt8 string types is complete. + return int(str.unsafe_ptr().bitcast[UInt8]()[idx]) # This algorithm is based on https://arxiv.org/abs/1704.00605 var end = length - (length % 3) @@ -176,7 +177,8 @@ fn b16encode(str: String) -> String: @parameter @always_inline fn str_bytes(idx: Int) -> Int: - return int(str.unsafe_ptr().bitcast[DType.uint8]()[idx]) + # TODO: Remove cast once transition to UInt8 string types is complete. + return int(str.unsafe_ptr().bitcast[UInt8]()[idx]) for i in range(length): var str_byte = str_bytes(i) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 41529689ea..2342a5115d 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -80,7 +80,7 @@ struct Error(Stringable, Boolable): memcpy( dest=dest, # TODO: Remove cast once string UInt8 transition is complete. - src=src.unsafe_ptr().bitcast[DType.uint8](), + src=src.unsafe_ptr().bitcast[UInt8](), count=length, ) dest[length] = 0 diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index fafa5e0b29..f0ef393833 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -49,8 +49,8 @@ fn ord(s: String) -> Int: # 2: 110aaaaa 10bbbbbb -> 00000000 00000000 00000aaa aabbbbbb a << 6 | b # 3: 1110aaaa 10bbbbbb 10cccccc -> 00000000 00000000 aaaabbbb bbcccccc a << 12 | b << 6 | c # 4: 11110aaa 10bbbbbb 10cccccc 10dddddd -> 00000000 000aaabb bbbbcccc ccdddddd a << 18 | b << 12 | c << 6 | d - var p = s.unsafe_ptr().bitcast[DType.uint8]() - var b1 = p.load() + var p = s.unsafe_ptr().bitcast[UInt8]() + var b1 = p[] if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(len(s) == 1, "input string length must be 1") return int(b1) @@ -62,7 +62,7 @@ fn ord(s: String) -> Int: for i in range(1, num_bytes): p += 1 shift -= 6 - result |= int(p.load() & 0b00111111) << shift + result |= int(p[] & 0b00111111) << shift return result @@ -1018,9 +1018,9 @@ struct String( self._buffer.resize(total_len + 1, 0) # Copy the data alongside the terminator. memcpy( - self.unsafe_uint8_ptr() + self_len, - other.unsafe_uint8_ptr(), - other_len + 1, + dest=self.unsafe_ptr() + self_len, + src=other.unsafe_ptr(), + count=other_len + 1, ) # ===------------------------------------------------------------------=== # @@ -1144,10 +1144,7 @@ struct String( strings. Using this requires the use of the _strref_keepalive() method to keep the underlying string alive long enough. """ - return StringRef { - data: UnsafePointer[UInt8]._from_dtype_ptr(self.unsafe_uint8_ptr()), - length: len(self), - } + return StringRef(self.unsafe_ptr(), len(self)) fn _strref_keepalive(self): """ @@ -1158,7 +1155,7 @@ struct String( pass # TODO: Remove this method when #2317 is done - fn unsafe_ptr(self) -> DTypePointer[DType.int8]: + fn unsafe_ptr(self) -> UnsafePointer[Int8]: """Retrieves a pointer to the underlying memory. Note that you should use `unsafe_uint8_ptr()` if you need to access the @@ -1169,17 +1166,15 @@ struct String( Returns: The pointer to the underlying memory. """ - return rebind[DTypePointer[DType.int8]](self._buffer.data) + return self._buffer.data - fn unsafe_uint8_ptr(self) -> DTypePointer[DType.uint8]: + fn unsafe_uint8_ptr(self) -> UnsafePointer[UInt8]: """Retrieves a pointer to the underlying memory. Returns: The pointer to the underlying memory. """ - return rebind[DTypePointer[DType.uint8]]( - self._buffer.data.bitcast[UInt8]() - ) + return self._buffer.data.bitcast[UInt8]() fn as_bytes(self) -> List[Int8]: """Retrieves the underlying byte sequence encoding the characters in @@ -1408,20 +1403,20 @@ struct String( # Copy preceding unchanged chars for _ in range(curr_offset, idx): - res.append(self_ptr.load()) + res.append(self_ptr[]) self_ptr += 1 # Insert a copy of the new replacement string for i in range(new_len): - res.append(new_ptr.load(i)) + res.append(new_ptr[i]) self_ptr += old_len while True: - var val = self_ptr.load() + var val = self_ptr[] if val == 0: break - res.append(self_ptr.load()) + res.append(self_ptr[]) self_ptr += 1 res.append(0) @@ -1488,8 +1483,8 @@ struct String( res.reserve(len(val) * len(self) + 1) for i in range(len(self)): for j in range(len(val)): - res.append(val_ptr.load(j)) - res.append(self_ptr.load(i)) + res.append(val_ptr[j]) + res.append(self_ptr[i]) res.append(0) return String(res^) diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 9eeec6edaf..ed57fcabf6 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -243,9 +243,7 @@ struct InlinedString(Sized, Stringable, CollectionElement): if self._is_small(): return self._storage[_FixedString[Self.SMALL_CAP]].unsafe_ptr() else: - var string_ptr = self._storage[String].unsafe_uint8_ptr() - - return UnsafePointer[UInt8]._from_dtype_ptr(string_ptr) + return self._storage[String].unsafe_uint8_ptr() fn _strref_dangerous(self) -> StringRef: """ From 197e67e8f55bf6685bf49db7248012bb36456fea Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 17 May 2024 05:15:42 -0500 Subject: [PATCH 0604/2019] [stdlib] polish: Change `StringSlice` to use `UInt8` for byte type MODULAR_ORIG_COMMIT_REV_ID: 5be5516f9f2cd9255b5487db35ff533ede524b72 --- stdlib/src/builtin/string.mojo | 10 ++++++---- stdlib/src/builtin/string_literal.mojo | 7 ++++--- stdlib/src/utils/string_slice.mojo | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index f0ef393833..30eae027f7 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -649,7 +649,8 @@ struct String( buffer.resize(length + 1, 0) memcpy( dest=buffer.data, - src=str_slice.as_bytes_slice().unsafe_ptr(), + # TODO: Remove cast after transition to UInt8 strings is complete. + src=str_slice.as_bytes_slice().unsafe_ptr().bitcast[Int8](), count=length, ) buffer[length] = 0 @@ -1199,7 +1200,7 @@ struct String( @always_inline fn as_bytes_slice( self: Reference[Self, _, _] - ) -> Span[Int8, self.is_mutable, self.lifetime]: + ) -> Span[UInt8, self.is_mutable, self.lifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -1209,8 +1210,9 @@ struct String( A contiguous slice pointing to the bytes owned by this string. """ - return Span[Int8, self.is_mutable, self.lifetime]( - unsafe_ptr=self[]._buffer.unsafe_ptr(), + return Span[UInt8, self.is_mutable, self.lifetime]( + # TODO: Remove cast after transition to UInt8 strings is complete. + unsafe_ptr=self[]._buffer.unsafe_ptr().bitcast[UInt8](), # Does NOT include the NUL terminator. len=self[]._byte_length(), ) diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 6349a0c39f..84983a9b3f 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -246,7 +246,7 @@ struct StringLiteral( return StringSlice(unsafe_from_utf8=bytes) @always_inline - fn as_bytes_slice(self) -> Span[Int8, False, ImmStaticLifetime]: + fn as_bytes_slice(self) -> Span[UInt8, False, ImmStaticLifetime]: """ Returns a contiguous slice of the bytes owned by this string. @@ -254,9 +254,10 @@ struct StringLiteral( A contiguous slice pointing to the bytes owned by this string. """ - var ptr = rebind[UnsafePointer[Int8]](self.unsafe_ptr()) + # TODO: Remove cast after transition to UInt8 strings is complete. + var ptr = self.unsafe_ptr().bitcast[UInt8]() - return Span[Int8, False, ImmStaticLifetime]( + return Span[UInt8, False, ImmStaticLifetime]( unsafe_ptr=ptr, len=self._byte_length(), ) diff --git a/stdlib/src/utils/string_slice.mojo b/stdlib/src/utils/string_slice.mojo index 973cc59772..e37d9fefd9 100644 --- a/stdlib/src/utils/string_slice.mojo +++ b/stdlib/src/utils/string_slice.mojo @@ -38,7 +38,7 @@ struct StringSlice[ lifetime: The lifetime of the underlying string data. """ - var _slice: Span[Int8, is_mutable, lifetime] + var _slice: Span[UInt8, is_mutable, lifetime] # ===------------------------------------------------------------------===# # Initializers @@ -46,7 +46,7 @@ struct StringSlice[ @always_inline fn __init__( - inout self, owned unsafe_from_utf8: Span[Int8, is_mutable, lifetime] + inout self, owned unsafe_from_utf8: Span[UInt8, is_mutable, lifetime] ): """ Construct a new StringSlice from a sequence of UTF-8 encoded bytes. @@ -72,7 +72,7 @@ struct StringSlice[ # ===------------------------------------------------------------------===# @always_inline - fn as_bytes_slice(self) -> Span[Int8, is_mutable, lifetime]: + fn as_bytes_slice(self) -> Span[UInt8, is_mutable, lifetime]: """ Get the sequence of encoded bytes as a slice of the underlying string. From 63d766adfcd55b47e7e5c089a267fcf5ebfd5470 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Sat, 18 May 2024 00:33:07 +0800 Subject: [PATCH 0605/2019] [Stdlib] Extend the float cast to be able to cast to non-f64 types This allows one to cast the `FloatLiteral` constant tO types other than `f64`. This enables us to avoid numerics issues that might arise due to upcasting and then downcasting and instead perform all the operations in the arbitrary precision domain. Due to weakness in the mojo parameterization, we have to duplicate a bunch of code, but all of this is under a param if, so does not cause issues at runtime. MODULAR_ORIG_COMMIT_REV_ID: 35f6cd11726838375908ab408c2a871172a7dc34 --- stdlib/src/builtin/simd.mojo | 95 +++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index ecdcfed2ea..5884468b87 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -346,18 +346,89 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( """ _simd_construction_checks[type, size]() - var tn1 = __mlir_op.`kgen.float_literal.convert`[ - _type = __mlir_type.f64 - ](value.value) - var t0 = __mlir_op.`pop.cast_from_builtin`[ - _type = __mlir_type.`!pop.scalar` - ](tn1) - var casted = __mlir_op.`pop.cast`[ - _type = __mlir_type[`!pop.simd<1,`, type.value, `>`] - ](t0) - self.value = __mlir_op.`pop.simd.splat`[ - _type = __mlir_type[`!pop.simd<`, size.value, `, `, type.value, `>`] - ](casted) + # TODO (#36686): This introduces uneeded casts here to work around + # parameter if issues. + @parameter + if type == DType.float16: + self = SIMD[type, size]( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f16 + ](value.value) + ) + ) + ) + ) + elif type == DType.bfloat16: + self = Self( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.bf16 + ](value.value) + ) + ) + ) + ) + elif type == DType.float32: + self = Self( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f32 + ](value.value) + ) + ) + ) + ) + else: + self = Self( + __mlir_op.`pop.simd.splat`[ + _type = __mlir_type[ + `!pop.simd<`, size.value, `,`, type.value, `>` + ] + ]( + __mlir_op.`pop.cast`[ + _type = __mlir_type[`!pop.scalar<`, type.value, `>`] + ]( + __mlir_op.`pop.cast_from_builtin`[ + _type = __mlir_type[`!pop.scalar`] + ]( + __mlir_op.`kgen.float_literal.convert`[ + _type = __mlir_type.f64 + ](value.value) + ) + ) + ) + ) @always_inline("nodebug") fn __len__(self) -> Int: From 4cff611d0ee03dc0acdfdcc7b0bf5d266f0fa15f Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Fri, 17 May 2024 14:45:15 -0400 Subject: [PATCH 0606/2019] [stdlib] change `print` to `_print_fmt` for NVPTX targets Patches a workaround for pow ops with float exponents on GPUs MODULAR_ORIG_COMMIT_REV_ID: 634719fba64c57c736906ea60712ec20c30d4438 --- stdlib/src/builtin/simd.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 5884468b87..d7ac0838de 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -42,7 +42,7 @@ from utils._visualizers import lldb_formatter_wrapping_type from utils import InlineArray from .dtype import _integral_type_of, _get_dtype_printf_format -from .io import _snprintf_scalar, _snprintf, _printf +from .io import _snprintf_scalar, _snprintf, _printf, _print_fmt from .string import _calc_initial_buffer_size, _calc_format_buffer_size # ===------------------------------------------------------------------------===# @@ -2571,7 +2571,7 @@ fn _pow[ @parameter if triple_is_nvidia_cuda(): - print( + _print_fmt( "ABORT: pow with two floating point operands is not supported" " on GPU" ) From 5e6a934bc69be5832b675acec3af0440f22a7919 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 17 May 2024 12:57:45 -0600 Subject: [PATCH 0607/2019] [benchmarks] Make `_bencher` module public Make the `_bencher` module public so users can see the doc strings for the `Bencher` type and friends. This is important to users know the API to program against when writing benchmarks soon, specifically within their own mojo programs and in the public Mojo standard library. MODULAR_ORIG_COMMIT_REV_ID: 54e3fb196a567710d18eaec7a18c746af823a0e3 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 43e433e3d6..0e960d36d2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -293,6 +293,11 @@ what we publish. trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683) by [@fknfilewalker](https://github.com/fknfilewalker)) +- The `bencher` module as part of the `benchmark` package is now public + and documented. This module provides types such as `Bencher` which provides + the ability to execute a `Benchmark` and allows for benchmarking configuration + via the `BenchmarkConfig` struct. + ### 🦋 Changed - The `let` keyword has been completely removed from the language. We previously From f5a5b4ee81a7d3ff8e35ee732408a684d7e8c9fe Mon Sep 17 00:00:00 2001 From: rd4com <144297616+rd4com@users.noreply.github.com> Date: Fri, 17 May 2024 13:58:50 -0500 Subject: [PATCH 0608/2019] [External] [stdlib] Add calling location for `assert_raises` (#40160) [External] [stdlib] Add calling location for `assert_raises` Add source location calling info for `assert_raises` like we have for other `assert_*` functions in the `testing` module. Fixes https://github.com/modularml/mojo/issues/2716 Co-authored-by: rd4com <144297616+rd4com@users.noreply.github.com> Closes modularml/mojo#2714 MODULAR_ORIG_COMMIT_REV_ID: ba0ed12ecb450f26230f3dabf69943d7a9a1dbbf --- docs/changelog.md | 2 ++ stdlib/src/testing/testing.mojo | 11 ++++++++++- stdlib/test/testing/test_assert_raises.mojo | 8 +++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0e960d36d2..d969eb3e6d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -416,3 +416,5 @@ what we publish. - Made several improvements to dictionary performance. Dicts with integer keys are most heavily affected, but large dicts and dicts with large values will also see large improvements. +- [#2692](https://github.com/modularml/mojo/issues/2692) Fix `assert_raises` + to include calling location. diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 57071fb011..65f18c8248 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -360,10 +360,16 @@ struct assert_raises: var message_contains: Optional[String] """If present, check that the error message contains this literal string.""" + var call_location: _SourceLocation + """Assigned the value returned by __call_locations() at Self.__init__.""" + + @always_inline fn __init__(inout self): """Construct a context manager with no message pattern.""" self.message_contains = None + self.call_location = __call_location() + @always_inline fn __init__(inout self, *, contains: String): """Construct a context manager matching specific errors. @@ -372,6 +378,7 @@ struct assert_raises: includes the literal text passed. """ self.message_contains = contains + self.call_location = __call_location() fn __enter__(self): """Enter the context manager.""" @@ -383,7 +390,9 @@ struct assert_raises: Raises: AssertionError: Always. The block must raise to pass the test. """ - raise Error("AssertionError: Didn't raise") + raise Error( + "AssertionError: Didn't raise at " + str(self.call_location) + ) fn __exit__(self, error: Error) raises -> Bool: """Exit the context manager with an error. diff --git a/stdlib/test/testing/test_assert_raises.mojo b/stdlib/test/testing/test_assert_raises.mojo index 0d555bd8c6..5c8c20ab28 100644 --- a/stdlib/test/testing/test_assert_raises.mojo +++ b/stdlib/test/testing/test_assert_raises.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from testing import assert_raises, assert_equal +from testing import assert_raises, assert_equal, assert_true fn test_assert_raises_catches_error() raises: @@ -35,11 +35,13 @@ fn test_assert_raises_catches_matched_error() raises: fn test_assert_raises_no_error() raises: try: - with assert_raises(): + with assert_raises(): # col 27 pass raise Error("This should not be reachable.") except e: - assert_equal(str(e), "AssertionError: Didn't raise") + assert_true(str(e).startswith("AssertionError: Didn't raise")) + assert_true(str(e).endswith(":27")) # col 27 + assert_true(str(e) != "This should not be reachable.") fn test_assert_raises_no_match() raises: From b3b9783aa5bdcf96de134a2ee007eaffdd8fbf93 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 17 May 2024 12:36:51 -0700 Subject: [PATCH 0609/2019] Restore the Mojo install procedure MODULAR_ORIG_COMMIT_REV_ID: faa8d15556b7316ddeda05cf21675d742ecf484c --- docs/manual/get-started.md | 365 ++++++++++++++++++++++++++++++++++++- 1 file changed, 356 insertions(+), 9 deletions(-) diff --git a/docs/manual/get-started.md b/docs/manual/get-started.md index cb265a15de..15fb479331 100644 --- a/docs/manual/get-started.md +++ b/docs/manual/get-started.md @@ -4,24 +4,316 @@ sidebar_label: Get started description: Install Mojo now and start developing --- -On this page, we'll show you how to create the classic "Hello world" starter -program with Mojo, in three different ways. If you'd rather read how to write -Mojo code beyond just printing text, see the [introduction to +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +On this page, we'll show you how to install Mojo and create the classic "Hello +world" starter program with Mojo, in three different ways. If you'd rather read +how to write Mojo code beyond just printing text, see the [introduction to Mojo](/mojo/manual/basics). :::tip Updating? -If you already installed Mojo, see the [update guide](/max/update). +If you already installed Mojo, see [how to update](#update-mojo). ::: +## Requirements + + + + +- Apple silicon (M1 or M2 processor) +- macOS Ventura (12) or later +- Python 3.8 - 3.11 +- Xcode or Xcode Command Line Tools +- [Homebrew](https://brew.sh) + + + + +- Ubuntu 20.04/22.04 LTS +- x86-64 CPU (with [SSE4.2 or + newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) + or AWS Graviton2/3 CPU +- Minimum 8 GiB RAM +- Python 3.8 - 3.11 +- g++ or clang++ C++ compiler + + + + +Windows support is still in development. + +In the meantime, you can use Mojo on Windows [with +WSL](https://learn.microsoft.com/en-us/windows/wsl/install), using a compatible +version of Ubuntu (see our requirements for Linux). + + + + ## 1. Install Mojo -Mojo is now bundled with MAX, which provides everything to compile, -run, debug, and package Mojo code ([read -why](/max/faq#why-bundle-mojo-with-max)). +If you already [installed MAX](/max/install), you can [skip to the next +section](#2-run-code-in-the-repl) because MAX includes Mojo. + +The Mojo SDK is available as either a stable build or a nightly build. +We strive to release stable builds once a month and release nightly builds as +often as possible (not necessarily every day). + + +{/*############################*/} +{/*#### STABLE BUILD SETUP ####*/} +{/*############################*/} + + +1. Open a terminal and install the [`modular`](/cli/) command line tool with + this helper script: + + ```sh + curl -s https://get.modular.com | sh - + ``` + +

l{f2&Ty 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 k25lr`2YX_ literal 0 HcmV?d00001 From 27ff8857901659ce1e4e6fb205403ca66da59ea4 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 26 Mar 2024 20:58:56 -0600 Subject: [PATCH 0028/2019] [mojo-stdlib] Remove stale `TODO` (#35904) We already plumbed a separator through everywhere for `print`. So, remove the stale `TODO` to avoid confusion. MODULAR_ORIG_COMMIT_REV_ID: 8ae8d2522179fe4d42b476efb0093e0da9112804 --- stdlib/src/builtin/io.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index dfa3556329..8a78e24caa 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -364,7 +364,6 @@ struct _StringableTuple[*Ts: Stringable](Sized): @always_inline fn _print[i: Int](inout self, /, *, sep: StringLiteral = " "): - # TODO: Allow controlling this separator from the caller. _put(sep) _put(self._at[i]()) From abd0692be0b9ebe30136bf4da90a5af09e1025b9 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 26 Mar 2024 22:20:23 -0500 Subject: [PATCH 0029/2019] [Docs] Add tutorial to the development guide (#35830) [Docs] Add tutorial to the development guide This adds a end-to-end tutorial to the development guide for users that struggle to comprehend how everything fits together. Also some modifcations to the rest of the doc, to fill in any gaps in knowledge. MODULAR_ORIG_COMMIT_REV_ID: 6a58f91ef5ba91653dfe44cdb2c550c142c5a319 --- stdlib/docs/development.md | 286 ++++++++++++++++-- .../docs/{ => images}/nightly-extension.png | Bin 2 files changed, 259 insertions(+), 27 deletions(-) rename stdlib/docs/{ => images}/nightly-extension.png (100%) diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 3d37b4ec97..9c25aef826 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -10,6 +10,7 @@ standard library only guarantees compatibility with the latest nightly `mojo` compiler. ```bash +curl https://get.modular.com | sh - modular auth modular install nightly/mojo ``` @@ -21,24 +22,26 @@ Then, follow the instructions from the `modular` tool in adding the `mojo` compiler to your `PATH` such as: ```bash -echo 'export MODULAR_HOME="/Users/joe/.modular"' >> ~/.zshrc -echo 'export PATH="/Users/joe/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc +echo 'export MODULAR_HOME="$HOME/.modular"' >> ~/.zshrc +echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc source ~/.zshrc ``` +If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. + +## Mojo nightly vscode extension + Install the nightly Mojo extension by searching for `Mojo nightly` in the extensions marketplace: -![mojo-nightly-extension](nightly-extension.png) +![mojo-nightly-extension](./images/nightly-extension.png) You can only have one Mojo extension enabled at a time, remember to switch back when using the stable release! -## Cloning the repository +## Fork and clone the repository -```bash -git clone https://github.com/modularml/mojo/ -``` +Follow the steps in [CONTRIBUTING.md](../../CONTRIBUTING.md#fork-and-clone-the-repo) ## Building the standard library @@ -55,35 +58,46 @@ inside the `stdlib` directory. This will create a build artifacts directory, ### Installing unit test dependencies -To run the unit tests, you first need to install a few dependencies: +To run the unit tests, you first need to install `lit`: -1. [`lit`](https://llvm.org/docs/CommandGuide/lit.html) - can be downloaded via - `python3 -m pip install lit` -2. [`FileCheck`](https://llvm.org/docs/CommandGuide/FileCheck.html) - is part of - the LLVM distribution you can obtain from your package manager +```bash +python3 -m pip install lit +``` -When you download `lit`, make sure you add the path to `lit` to your `PATH` if -needed. Some of the tests use `FileCheck` or `not` binaries that ship with LLVM. -For example, if you download LLVM via `homebrew`, these would be in -`/opt/homebrew/Cellar/llvm//bin`. You need to add this path -to your `PATH` in order to run these tests. In the near future, we will be -moving away from `FileCheck` in favor of writing the unit tests using our own -`testing` module and remove this dependency requirement for contributors. We -are happy to welcome contributions in this area! +And make sure that `FileCheck` from LLVM is on path. If your are on macOS, you +can `brew install llvm` and add it to your path in `~/.zshrc` or `~/.bashrc`: -### Running the standard library tests +```bash +export PATH="/opt/homebrew/opt/llvm/bin:$PATH" +``` + +If you are on Ubuntu you can: + +```bash +sudo apt update +sudo apt install llvm +``` -We provide a simple Bash script to build the standard library package and -`test_utils` package that is used by the test suite. Just run -`./stdlib/scripts/run-tests.sh` which will produce the necessary -`mojopkg` files inside your `build` directory and then run -`lit -sv stdlib/test`. +And it will be available in `PATH`. + +In the near future, we will be moving away from `FileCheck` in favor of writing +the unit tests using our own `testing` module and remove this dependency +requirement for contributors. We are happy to welcome contributions in this +area! + +### Running the standard library tests ```bash ./stdlib/scripts/run-tests.sh ``` -### Running a subset of the standard library unit tests +This will produce the necessary `mojopkg` files inside your `build` directory +and then run `lit -sv stdlib/test`. All the tests should pass on the `nightly` +branch with the nightly mojo compiler. If you've pulled the latest changes and +they're still failing please +[open a GitHub issue](https://github.com/modularml/mojo/issues/new?assignees=&labels=bug%2Cmojo&projects=&template=mojo_bug_report.yaml&title=%5BBUG%5D). + +### Running a subset of the Standard Library Unit Tests If you’d like to run just a subset of the tests, feel free to use all of the normal options that the `lit` tool provides. For example, to run just the @@ -127,3 +141,221 @@ git diff origin/main --name-only -- '*.mojo' | xargs mojo format You can also consider setting up your editor to automatically format Mojo files upon saving. + +## Tutorial + +If you're having trouble figuring out how everything fits together, this +tutorial will take you on a journey for making a change and raising a PR from +start to finish. + +Prerequisites: + +- [Get the Nightly Mojo Compiler](#getting-the-nightly-mojo-compiler). +- [Fork and clone the repo](../../CONTRIBUTING.md#fork-and-clone-the-repo) + +__IMPORTANT__ We'll be in the `mojo/stdlib` folder for this tutorial, check and +make sure you're in that location if anything goes wrong: +`cd [path-to-repo]/stdlib` + +Let's try adding a small piece of functionality to `path.mojo`: + +```mojo +# ./stdlib/src/pathlib/path.mojo + +fn print_cwd() raises: + print("cwd:", cwd()) +``` + +And make sure you're importing it from the module root: + +```mojo +# ./stdblib/src/pathlib/__init__.mojo + +from .path import ( + DIR_SEPARATOR, + cwd, + print_cwd, + Path, +) +``` + +Now you can create a temporary file named `main.mojo` for trying out the new +behaviour. You wouldn't commit this, it's just to experiment with the +functionality before you write tests: + +```mojo +# ./stdlib/main.mojo + +from src import pathlib + +def main(): + pathlib.print_cwd() +``` + +Now when you run `mojo main.mojo` it'll reflect the changes: + +```plaintext +cwd: /Users/jack/src/mojo/stdlib +``` + +### Standard library dependencies + +This works alright for one file, but what if you're modifying multiple standard +library files that depend on each other? Try adding this to `os.mojo` which +depends on what we just added to `pathlib.mojo`: + +```mojo +# ./stdlib/src/os/os.mojo + +import pathlib + +fn print_paths() raises: + pathlib.print_cwd() + for path in cwd().listdir(): + print(path[]) +``` + +This won't work because it's importing `pathlib` from the `stdlib.mojopkg` that +comes with Mojo. We'll need to build our just-modified standard library running +the command: + +```bash +./scripts/build-stdlib.sh +``` + +This builds the standard library and places it at the root of the repo in +`../build/stdlib.mojopkg`, now we can edit `main.mojo` to use the normal import +os syntax: + +```mojo +import os + +def main(): + os.print_paths() +``` + +And use the env var below to instruct Mojo to prioritize imports from the +standard library we just built, over the one that ships with Mojo: + +```bash +MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build mojo main.mojo +``` + +Which now outputs: + +```plaintext +cwd: /Users/jack/src/mojo/stdlib +main.mojo +test +docs +scripts +src +``` + +### Bonus tip: fast feedback loop + +If you like a fast feedback loop, try installing the file watcher `entr`, which +you can get with `sudo apt install entr` on Linux, or `brew install entr` on +macOS. Now run: + +```bash +export MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build + +ls **/*.mojo | entr sh -c "./scripts/build-stdlib.sh && mojo main.mojo" +``` + +Now every time you save a Mojo file, it will package the standard library and +run `main.mojo`. + +### Running tests + +If you haven't already follow the steps at: +[Installing unit test dependencies](#installing-unit-test-dependencies) + +### Adding a test + +This will show you how the `FileCheck` utility works, first turn it on by adding +it to the end of line 7 in `./stdlib/test/pathlib/test_pathlib.mojo`: + +```mojo +# RUN: %mojo -debug-level full -D TEMP_FILE=%t %s | FileCheck %s +``` + +Now we can add the test and force it to fail: + +```mojo +# CHECK-LABEL: test_print_cwd + +def test_print_cwd(): + print("== test_print_cwd") + + # CHECK: This will fail + print_cwd() +``` + +Don't forget to call it from `main()`: + +```bash +def main(): + test_print_cwd() +``` + +Now instead of testing all the modules, we can just test pathlib: + +```bash +lit -sv test/pathlib +``` + +This will give you an error showing exactly what went wrong: + +```plaintext +/Users/jack/src/mojo-toy-2/stdlib/test/pathlib/test_pathlib.mojo:27:11: +error: CHECK: expected string not found in input + +# CHECK: This will fail + ^ +:1:18: note: scanning from here +== test_print_cwd + ^ +``` + +Lets fix the test that we just added: + +```plaintext +# CHECK-LABEL: test_print_cwd + +def test_print_cwd(): + print("== test_print_cwd") + + # CHECK: cwd: + print_cwd() +``` + +We're now checking that `print_cwd` is prefixed with `cwd:` just like the function +we added. Run the test again: + +```plaintext +Testing Time: 0.65s + +Total Discovered Tests: 1 + Passed: 1 (100.00%) +``` + +Success! Now we have a test for our new function. + +The last step is to [run mojo format](#formatting-changes) on all the files. + +### Raising a PR + +Make sure that you've had a look at all the materials from the standard library +[README.md](../README.md). This change wouldn't be accepted because it's missing +tests, and doesn't add useful functionality that warrants new functions. If you +did have a worthwhile change you wanted to raise, follow the steps in the +[CONTRIBUTING.md](../../CONTRIBUTING.md) + +Congratulations! You've now got an idea on how to contribute to the standard +library, test your changes, and raise a PR. + +If you're still having troubles make sure to reach out on +[GitHub](https://github.com/modularml/mojo/discussions/new?category=general) or +[Discord](modul.ar/discord)! diff --git a/stdlib/docs/nightly-extension.png b/stdlib/docs/images/nightly-extension.png similarity index 100% rename from stdlib/docs/nightly-extension.png rename to stdlib/docs/images/nightly-extension.png From 9210411630acfce57ce8e3f15f33ff03dafcd647 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Tue, 26 Mar 2024 23:18:11 -0500 Subject: [PATCH 0030/2019] [Docs] Move nightly instructions to CONTRIBUTING.md (#35882) ### [Docs] Move nightly instructions to CONTRIBUTING.md Because users need to follow the steps for installing the nightly compiler when raising PR's against examples, this is better off in the root of the repo with links back to it. MODULAR_ORIG_COMMIT_REV_ID: 2f7530610f3f2913d606af460afb8596a6e67084 --- CONTRIBUTING.md | 44 ++++++++++++++- .../images/nightly-extension.png | Bin stdlib/docs/development.md | 53 ++++-------------- 3 files changed, 54 insertions(+), 43 deletions(-) rename {stdlib/docs => docs/oss-material}/images/nightly-extension.png (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 113bed1cf0..20aaaac320 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -200,20 +200,60 @@ git remote add upstream git@github.com:modularml/mojo.git git fetch upstream ``` -Branch off `nightly` to work on your PR: +#### Branching off nightly + +Make sure to branch off `nightly` to work on your PR: ```bash git checkout upstream/nightly git checkout -b my-fix-pr ``` -Before raising a PR make sure you've synched the latest changes: +You should periodically make sure you've synced the latest changes, especially +before raising a PR: ```bash git fetch upstream git rebase upstream/nightly ``` +#### Getting the nightly Mojo compiler + +Now that you're on the nightly branch, you need to install the latest nightly +Mojo compiler: + +```bash +curl https://get.modular.com | sh - + +modular auth + +modular install nightly/mojo +``` + +If you already have an older `nightly/mojo` compiler, replace +`modular install nightly/mojo` with `modular update nightly/mojo`. + +Then, follow the instructions from the `modular` tool in adding the `mojo` +compiler to your `PATH` such as: + +```bash +echo export MODULAR_HOME="$HOME/.modular" >> ~/.zshrc +echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc +``` + +If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. + +#### Mojo nightly vscode extension + +Install the nightly Mojo extension by searching for `Mojo nightly` in the +extensions marketplace: + +![mojo-nightly-extension](./images/nightly-extension.png) + +You can only have one Mojo extension enabled at a time, remember to switch back +when using the stable release! + #### Create a pull request If your change is one of the improvements described above or it has been diff --git a/stdlib/docs/images/nightly-extension.png b/docs/oss-material/images/nightly-extension.png similarity index 100% rename from stdlib/docs/images/nightly-extension.png rename to docs/oss-material/images/nightly-extension.png diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 9c25aef826..ac60035181 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -3,45 +3,19 @@ This document covers the essentials of getting started developing for the standard library. -## Getting the nightly Mojo compiler +## Prerequisites -To get started, you need to install the latest nightly mojo compiler. The -standard library only guarantees compatibility with the latest nightly `mojo` -compiler. +If this is your first time contributing, you should read everything in +[CONTRIBUTING.md](../../CONTRIBUTING.md#fork-and-clone-the-repo). Logistically +you need to do the following: -```bash -curl https://get.modular.com | sh - -modular auth -modular install nightly/mojo -``` - -If you already have an older `nightly/mojo` compiler, replace -`modular install nightly/mojo` with `modular update nightly/mojo`. - -Then, follow the instructions from the `modular` tool in adding the `mojo` -compiler to your `PATH` such as: - -```bash -echo 'export MODULAR_HOME="$HOME/.modular"' >> ~/.zshrc -echo 'export PATH="$HOME/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"' >> ~/.zshrc -source ~/.zshrc -``` - -If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. - -## Mojo nightly vscode extension - -Install the nightly Mojo extension by searching for `Mojo nightly` in the -extensions marketplace: - -![mojo-nightly-extension](./images/nightly-extension.png) - -You can only have one Mojo extension enabled at a time, remember to switch back -when using the stable release! +- [Fork and clone the repo](../../CONTRIBUTING.md#fork-and-clone-the-repo) +- [Branch off nightly](../../CONTRIBUTING.md#branching-off-nightly) +- [Install the nightly Mojo compiler](../../CONTRIBUTING.md#getting-the-nightly-mojo-compiler) -## Fork and clone the repository +And if you're using vscode: -Follow the steps in [CONTRIBUTING.md](../../CONTRIBUTING.md#fork-and-clone-the-repo) +- [Install the nightly vscode extension](../../CONTRIBUTING.md#mojo-nightly-vscode-extension) ## Building the standard library @@ -148,10 +122,7 @@ If you're having trouble figuring out how everything fits together, this tutorial will take you on a journey for making a change and raising a PR from start to finish. -Prerequisites: - -- [Get the Nightly Mojo Compiler](#getting-the-nightly-mojo-compiler). -- [Fork and clone the repo](../../CONTRIBUTING.md#fork-and-clone-the-repo) +First follow everything in the [prerequisites](#prerequisites) __IMPORTANT__ We'll be in the `mojo/stdlib` folder for this tutorial, check and make sure you're in that location if anything goes wrong: @@ -350,8 +321,8 @@ The last step is to [run mojo format](#formatting-changes) on all the files. Make sure that you've had a look at all the materials from the standard library [README.md](../README.md). This change wouldn't be accepted because it's missing tests, and doesn't add useful functionality that warrants new functions. If you -did have a worthwhile change you wanted to raise, follow the steps in the -[CONTRIBUTING.md](../../CONTRIBUTING.md) +did have a worthwhile change you wanted to raise, follow the steps to +[create a pull request](../../CONTRIBUTING.md#create-a-pull-request). Congratulations! You've now got an idea on how to contribute to the standard library, test your changes, and raise a PR. From 31a7c4582e9103ead8f13a4a06c8c20c5fe82846 Mon Sep 17 00:00:00 2001 From: ematejska Date: Tue, 26 Mar 2024 21:56:39 -0700 Subject: [PATCH 0031/2019] [Docs] Update README.md (#35870) Suggested changes to the top-level readme file for the stdlib OSS release. --------- Co-authored-by: Arthur Evans MODULAR_ORIG_COMMIT_REV_ID: 28155320dd8fd94f4961980a77015e44c74154b7 --- README.md | 62 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 09ad90aed7..fe65959e21 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,6 @@ and production by combining Python syntax and ecosystem with systems programming and metaprogramming features. Mojo is still young, but it is designed to become a superset of Python over time. -To use Mojo, you can install the MAX SDK or the standalone Mojo SDK: - -- [Get the MAX SDK.](https://docs.modular.com/engine/get-started) -- [Get the Mojo SDK.](https://docs.modular.com/mojo/manual/get-started/) - -Then follow the docs to [write your first Mojo -program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). -When you want to report issues or request features, [please create a GitHub -issue here](https://github.com/modularml/mojo/issues). See the [Mojo -Contributor Guide](https://www.notion.so/f527254bc46b4cd3ba4b34bd949d4e57?pvs=21) -for guidelines on filing good bugs. - This repo includes source code for: - Mojo examples @@ -30,28 +18,54 @@ This repo includes source code for: This repo has two primary branches: - The [`main`](https://github.com/modularml/mojo/tree/main) branch, which is in -sync with the last released version of Mojo. Use the examples here if you’re -using a release build of Mojo. +sync with the last stable released version of Mojo. Use the examples here if you’re +using a [release build of Mojo](#latest-released). - The [`nightly`](https://github.com/modularml/mojo/tree/nightly) branch, which is in sync with the Mojo nightly build and subject to breakage. Use this branch -for [contributions](./CONTRIBUTING.md). +for [contributions](./CONTRIBUTING.md), or if you're using the latest +[nightly build of Mojo](#latest-nightly). + +To learn more about Mojo, see the +[Mojo Manual](https://docs.modular.com/mojo/manual/). + +## Installing Mojo + +### Latest Released + +To install the last released build of Mojo, you can install the MAX SDK +or the standalone Mojo SDK: -This repo represents the Mojo open source effort. We are continuing to open -parts of the Mojo codebase. The challenge is that we use Mojo pervasively -inside Modular and we need to make sure that community contributions can -proceed smoothly with good build and testing tools that will allow this repo to -become the source of truth (right now it is not). We'll progressively improve -the tools and add more source code over time. +- [Get the MAX SDK](https://docs.modular.com/engine/get-started) +- [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) -If you’d like to contribute to Mojo, please first read our [Contributor +Then follow the docs to [write your first Mojo +program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). + +### Latest Nightly + +The nightly Mojo builds are subject to breakage and provide an inside +view of how the development of Mojo is progressing. Use at your own risk +and be patient! Intall them using the instructions [here](./CONTRIBUTING.md). + +## Contributing + +When you want to report issues or request features, [please create a GitHub +issue here](https://github.com/modularml/mojo/issues). +See [here](./CONTRIBUTING.md) for guidelines on filing good bugs. + +We welcome contributions to this repo on the +[`nightly`](https://github.com/modularml/mojo/tree/nightly) +branch. If you’d like to contribute to Mojo, please first read our [Contributor Guide](https://github.com/modularml/mojo/blob/main/CONTRIBUTING.md). For more general questions or to chat with other Mojo developers, check out our [Discord](https://discord.gg/modular). -To learn more about Mojo, see the -[Mojo Manual](https://docs.modular.com/mojo/manual/). +## License + +This repository is licensed under the Apache License v2.0 with LLVM Exceptions +(see the LLVM [License](https://llvm.org/LICENSE.txt)). ## Thanks to our contributors From e152ba1f039040b72296cc3471dcdb1faa6df8aa Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 27 Mar 2024 07:56:40 -0700 Subject: [PATCH 0032/2019] [Stdlib] Add sys.exit functions to the stdlib (#35921) This adds sys.exit, but unlike Python does not throw (because we really cannot have basic functions like this throw). Closes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: a23c5461eee07caf8f8bd5bb7be25007e35189bd --- docs/changelog.md | 3 +++ stdlib/src/sys/__init__.mojo | 1 + stdlib/src/sys/_exit.mojo | 36 ++++++++++++++++++++++++++++++++ stdlib/test/sys/test_exit_0.mojo | 19 +++++++++++++++++ stdlib/test/sys/test_exit_1.mojo | 20 ++++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 stdlib/src/sys/_exit.mojo create mode 100644 stdlib/test/sys/test_exit_0.mojo create mode 100644 stdlib/test/sys/test_exit_1.mojo diff --git a/docs/changelog.md b/docs/changelog.md index f0b0f8ad0b..a5d991ac6d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -166,6 +166,9 @@ and tools. Please add any significant user-visible changes here. execution. This allows for writing additional code within a doc string example that is only used to ensure the example is runnable/testable. +- The `sys` module now contains an `exit` function that would exit a Mojo + program with the specified error code. + ### 🦋 Changed - Mojo now warns about unused values in both `def` and `fn` declarations, diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 597dbecda6..01ee0211ef 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -14,6 +14,7 @@ from .arg import argv from .debug import breakpointhook +from ._exit import exit from .ffi import RTLD, DEFAULT_RTLD, DLHandle, external_call from .info import ( is_x86, diff --git a/stdlib/src/sys/_exit.mojo b/stdlib/src/sys/_exit.mojo new file mode 100644 index 0000000000..319263f6fd --- /dev/null +++ b/stdlib/src/sys/_exit.mojo @@ -0,0 +1,36 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""This module includes the exit functions.""" + + +from .ffi import external_call + + +fn exit(): + """Exist from Mojo. Unlike the Python implementation this does not raise + an exception to exit. + """ + exit(0) + + +fn exit[intable: Intable](code: intable): + """Exist from Mojo. Unlike the Python implementation this does not raise + an exception to exit. + + Params: + intable: The type of the code. + + Args: + code: The exit code. + """ + external_call["exit", NoneType](Int32(int(code))) diff --git a/stdlib/test/sys/test_exit_0.mojo b/stdlib/test/sys/test_exit_0.mojo new file mode 100644 index 0000000000..6547d79461 --- /dev/null +++ b/stdlib/test/sys/test_exit_0.mojo @@ -0,0 +1,19 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo -debug-level full %s + +import sys + + +fn main(): + sys.exit(0) diff --git a/stdlib/test/sys/test_exit_1.mojo b/stdlib/test/sys/test_exit_1.mojo new file mode 100644 index 0000000000..d374a537f1 --- /dev/null +++ b/stdlib/test/sys/test_exit_1.mojo @@ -0,0 +1,20 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# REQUIRES: has_not +# RUN: not %mojo -debug-level full %s + +import sys + + +fn main(): + sys.exit(1) From 7f47f76b9a4a7a6691f349a22ffd2f89c938d0a4 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 09:37:25 -0700 Subject: [PATCH 0033/2019] [docs] Misc copy edits to the Mojo stdlib docs (#35890) Simply focused on consistent style and additional clarity. MODULAR_ORIG_COMMIT_REV_ID: 4a6c0f9d57ba57a434fda225a87dcc4075d99038 --- CONTRIBUTING.md | 6 +-- stdlib/docs/development.md | 93 ++++++++++++++++++++++---------------- stdlib/docs/faq.md | 34 +++++++------- stdlib/docs/roadmap.md | 19 ++++---- stdlib/docs/style-guide.md | 68 ++++++++++++++-------------- stdlib/docs/vision.md | 56 ++++++++++++----------- 6 files changed, 146 insertions(+), 130 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20aaaac320..c4fc579813 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -246,10 +246,10 @@ If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. #### Mojo nightly vscode extension -Install the nightly Mojo extension by searching for `Mojo nightly` in the -extensions marketplace: +Install the [Mojo nightly VS Code +extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly): -![mojo-nightly-extension](./images/nightly-extension.png) + You can only have one Mojo extension enabled at a time, remember to switch back when using the stable release! diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index ac60035181..e9f2d19a00 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -1,27 +1,27 @@ # Mojo standard library development -This document covers the essentials of getting started developing for the -standard library. +This document covers the essentials of developing for the standard library. ## Prerequisites -If this is your first time contributing, you should read everything in -[CONTRIBUTING.md](../../CONTRIBUTING.md#fork-and-clone-the-repo). Logistically +If this is your first time contributing, first read everything in +[CONTRIBUTING.md](../../CONTRIBUTING.md#fork-and-clone-the-repo). Logistically, you need to do the following: -- [Fork and clone the repo](../../CONTRIBUTING.md#fork-and-clone-the-repo) -- [Branch off nightly](../../CONTRIBUTING.md#branching-off-nightly) -- [Install the nightly Mojo compiler](../../CONTRIBUTING.md#getting-the-nightly-mojo-compiler) +1. [Fork and clone the repo](../../CONTRIBUTING.md#fork-and-clone-the-repo) +2. [Branch off nightly](../../CONTRIBUTING.md#branching-off-nightly) +3. [Install the nightly Mojo compiler](../../CONTRIBUTING.md#getting-the-nightly-mojo-compiler) -And if you're using vscode: +And if you're using VS Code: -- [Install the nightly vscode extension](../../CONTRIBUTING.md#mojo-nightly-vscode-extension) +- [Install the nightly VS Code + extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly) ## Building the standard library -To build the standard library, you can run the script -[`build-stdlib.sh`](../scripts/build-stdlib.sh) from the `scripts` directory -inside the `stdlib` directory. This will create a build artifacts directory, +To build the standard library, you can run the +[`build-stdlib.sh`](../scripts/build-stdlib.sh) script from the +`mojo/stdlib/scripts/` directory. This will create a build artifacts directory, `build`, in the top-level of the repo and produce the `stdlib.mojopkg` inside. ```bash @@ -49,6 +49,7 @@ If you are on Ubuntu you can: ```bash sudo apt update + sudo apt install llvm ``` @@ -61,21 +62,29 @@ area! ### Running the standard library tests +We provide a simple Bash script to build the standard library package and +`test_utils` package that is used by the test suite. + +Just run `./stdlib/scripts/run-tests.sh` which will produce the necessary +`mojopkg` files inside your `build` directory, and then run `lit -sv +stdlib/test`. + ```bash ./stdlib/scripts/run-tests.sh + +lit -sv stdlib/test ``` -This will produce the necessary `mojopkg` files inside your `build` directory -and then run `lit -sv stdlib/test`. All the tests should pass on the `nightly` -branch with the nightly mojo compiler. If you've pulled the latest changes and -they're still failing please -[open a GitHub issue](https://github.com/modularml/mojo/issues/new?assignees=&labels=bug%2Cmojo&projects=&template=mojo_bug_report.yaml&title=%5BBUG%5D). +All the tests should pass on the `nightly` branch with the nightly Mojo +compiler. If you've pulled the latest changes and they're still failing please +[open a GitHub +issue](https://github.com/modularml/mojo/issues/new?assignees=&labels=bug%2Cmojo&projects=&template=mojo_bug_report.yaml&title=%5BBUG%5D). ### Running a subset of the Standard Library Unit Tests If you’d like to run just a subset of the tests, feel free to use all of the normal options that the `lit` tool provides. For example, to run just the -builtin and collections tests, you can +`builtin` and `collections` tests: ```bash lit -sv stdlib/test/builtin stdlib/test/collections @@ -98,7 +107,7 @@ a look. ## Formatting changes -Please make sure your changes are formatted before submitting a Pull Request. +Please make sure your changes are formatted before submitting a pull request. Otherwise, CI will fail in its lint and formatting checks. The `mojo` compiler provides a `format` command. So, you can format your changes like so: @@ -118,16 +127,17 @@ Mojo files upon saving. ## Tutorial -If you're having trouble figuring out how everything fits together, this -tutorial will take you on a journey for making a change and raising a PR from -start to finish. +Here is a complete walkthrough, showing how to make a change to the Mojo +standard library, test it, and raise a PR. -First follow everything in the [prerequisites](#prerequisites) +First, follow everything in the [prerequisites](#prerequisites). __IMPORTANT__ We'll be in the `mojo/stdlib` folder for this tutorial, check and make sure you're in that location if anything goes wrong: `cd [path-to-repo]/stdlib` +### A simple change + Let's try adding a small piece of functionality to `path.mojo`: ```mojo @@ -151,7 +161,7 @@ from .path import ( ``` Now you can create a temporary file named `main.mojo` for trying out the new -behaviour. You wouldn't commit this, it's just to experiment with the +behavior. You wouldn't commit this file, it's just to experiment with the functionality before you write tests: ```mojo @@ -169,11 +179,13 @@ Now when you run `mojo main.mojo` it'll reflect the changes: cwd: /Users/jack/src/mojo/stdlib ``` -### Standard library dependencies +### A change with dependencies + +Here's a more tricky example that modifies multiple standard library files that +depend on each other. -This works alright for one file, but what if you're modifying multiple standard -library files that depend on each other? Try adding this to `os.mojo` which -depends on what we just added to `pathlib.mojo`: +Try adding this to `os.mojo`, which depends on what we just added to +`pathlib.mojo`: ```mojo # ./stdlib/src/os/os.mojo @@ -195,8 +207,8 @@ the command: ``` This builds the standard library and places it at the root of the repo in -`../build/stdlib.mojopkg`, now we can edit `main.mojo` to use the normal import -os syntax: +`../build/stdlib.mojopkg`. Now we can edit `main.mojo` to use the normal import +syntax: ```mojo import os @@ -205,8 +217,9 @@ def main(): os.print_paths() ``` -And use the env var below to instruct Mojo to prioritize imports from the -standard library we just built, over the one that ships with Mojo: +We also need to set the following environment variable that tells Mojo to +prioritize imports from the standard library we just built, over the one that +ships with Mojo: ```bash MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build mojo main.mojo @@ -235,18 +248,18 @@ export MODULAR_MOJO_NIGHTLY_IMPORT_PATH=../build ls **/*.mojo | entr sh -c "./scripts/build-stdlib.sh && mojo main.mojo" ``` -Now every time you save a Mojo file, it will package the standard library and -run `main.mojo`. +Now, every time you save a Mojo file, it packages the standard library and +runs `main.mojo`. ### Running tests -If you haven't already follow the steps at: +If you haven't already, follow the steps at: [Installing unit test dependencies](#installing-unit-test-dependencies) ### Adding a test -This will show you how the `FileCheck` utility works, first turn it on by adding -it to the end of line 7 in `./stdlib/test/pathlib/test_pathlib.mojo`: +This will show you how the `FileCheck` utility works. First, turn it on by +adding it to the end of line 7 in `./stdlib/test/pathlib/test_pathlib.mojo`: ```mojo # RUN: %mojo -debug-level full -D TEMP_FILE=%t %s | FileCheck %s @@ -271,7 +284,7 @@ def main(): test_print_cwd() ``` -Now instead of testing all the modules, we can just test pathlib: +Now, instead of testing all the modules, we can just test `pathlib`: ```bash lit -sv test/pathlib @@ -302,8 +315,8 @@ def test_print_cwd(): print_cwd() ``` -We're now checking that `print_cwd` is prefixed with `cwd:` just like the function -we added. Run the test again: +We're now checking that `print_cwd` is prefixed with `cwd:` just like the +function we added. Run the test again: ```plaintext Testing Time: 0.65s diff --git a/stdlib/docs/faq.md b/stdlib/docs/faq.md index 183b2419dc..be9780fa05 100644 --- a/stdlib/docs/faq.md +++ b/stdlib/docs/faq.md @@ -1,39 +1,37 @@ # Frequently asked questions -A lot of questions about Mojo as a whole can be answered on the +A lot of questions about Mojo as a whole are answered in the [FAQ on our website](https://docs.modular.com/mojo/faq). -This document is specifically focused on the standard library with contributors +This FAQ is specifically focused on the standard library with contributors in mind. -## Mojo standard library +## Contributing & development -### Contributing & development +### 1. What platforms does Mojo support? -#### 1. What platforms does Mojo support? - -The nightly Mojo compiler currently works on Linux and macOS. The Standard -Library works on both platforms too in conjunction with the compiler. Windows is +The nightly Mojo compiler currently works on Linux and macOS. The standard +library works on both platforms too in conjunction with the compiler. Windows is currently not a supported platform. -#### 2. I hit a bug! What do I do? +### 2. I hit a bug! What do I do? Don’t Panic! 😃 Check out our [bug submission guide](../../CONTRIBUTING.md#submitting-bugs) to make sure you include all the essential information to avoid unnecessary delays in getting your issues resolved. -### Standard library code +## Standard library code -#### 1. Why do we have both `AnyRegType` and `AnyType`? +### 1. Why do we have both `AnyRegType` and `AnyType`? This is largely a historical thing as the library only worked on `AnyRegType` when it was first written. As we introduced the notion of memory-only types and -traits, `AnyType` was born. Over time in Q2 2024, we expect to rewrite nearly +traits, `AnyType` was born. Over time, we expect to rewrite nearly the entire library to have everything work on `AnyType` and be generalized to -not just work on `AnyRegType`. Several things need to be worked in tandem with +not just work on `AnyRegType`. Several things need to happen in tandem with the compiler team to make this possible. -#### 2. Are the MLIR dialects private? +### 2. Are the MLIR dialects private? The standard library makes use of internal MLIR dialects such as `pop`, `kgen`, and `lit`. Currently, these are private, undocumented APIs. We provide @@ -42,7 +40,7 @@ These particular areas of the compiler and standard library are in active development and we are exploring how we can release them when their public-facing API has stabilized. -#### 3. What is the compiler-runtime? +### 3. What is the compiler-runtime? Mojo depends on certain features that are still written in C++, collectively called "the compiler runtime." This may manifest in the standard library code @@ -51,7 +49,7 @@ dialects, the compiler runtime is currently private and undocumented. We plan on reducing the C++ dependencies in the future. -#### 4. Why are some standard library modules missing from the open-source code? +### 4. Why are some standard library modules missing from the open-source code? When we were preparing to open source the standard library, we realized that some modules weren't ready for open-source release. For example: @@ -60,11 +58,11 @@ some modules weren't ready for open-source release. For example: stabilize. - Some modules are too tightly integrated into other portions of MAX and need to be refactored. -- Some modules may have proprietary aspects that require additional review and +- Some modules have proprietary aspects that require additional review and refinement. For the short term, we've left these modules as closed source. The shipped -Mojo SDK contains the pre-built Mojo packages for these closed source modules +Mojo SDK contains the pre-built Mojo packages for these closed-source modules in addition to the open-source modules, so Mojo users still have the full set of primitives available to them. diff --git a/stdlib/docs/roadmap.md b/stdlib/docs/roadmap.md index 7682358192..3ff96a1c24 100644 --- a/stdlib/docs/roadmap.md +++ b/stdlib/docs/roadmap.md @@ -1,6 +1,6 @@ # Mojo standard library roadmap -## Roadmap cadence +This is a high-level plan for the Mojo standard library. We plan to update this roadmap approximately every 6 months, in alignment with Modular's internal workflows. The roadmap updates act as a forcing function for @@ -10,21 +10,22 @@ both internally and externally are aligned on the future technical direction. ## 2024 Q2+ roadmap The following are high-level themes the Mojo standard library team will be -working on over the next 6-months. Keep in mind that Mojo and the Mojo Standard -Library are in early development with many features landing over the months -ahead. Currently, that means we are focused on the core system programming -features that are essential to Mojo's mission. +working on over the next 6 months. Keep in mind that Mojo and the Mojo standard +library are still in early development and many features will land in the +months ahead. Currently, that means we are focused on the core system +programming features that are essential to [Mojo's +mission](https://docs.modular.com/mojo/why-mojo). ### Core library improvements - Remove `AnyRegType` in the standard library in favor of `AnyType`. -- Unify Pointer and AnyPointer. +- Unify `Pointer` and `AnyPointer`. - Apply `Reference` types and lifetimes throughout APIs and types. - Design API conventions and expected behavior for core collection types such - as `List`, `String`, `Dict` . + as `List`, `String`, and `Dict`. ### Generic programming improvements @@ -32,8 +33,8 @@ features that are essential to Mojo's mission. - Define an iterator model and implement iterator support for core types. -- Standardize collection meta type names (eg. *element_type*, *key_type*, and - *value_type*). +- Standardize collection meta type names (such as *element_type*, *key_type*, + and *value_type*). ### Improve Python interop diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index 4135e31c9b..a7aff2e206 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -1,7 +1,7 @@ # Coding standards & style guide This document describes conventions that Mojo standard library code should -adhere to. Its coverages range from non-semantic conventions like code +adhere to. Its coverage ranges from non-semantic conventions like code formatting, to semantics like value lifecycle behavior that standard library types should generally conform to. @@ -11,10 +11,12 @@ types should generally conform to. #### File structure -The Mojo standard library uses the following high-level organization. Group -related functions within the same file. Group related files within the same -directory. Do not add dependencies to the `stdlib` module because, by -definition, it is required to be a leaf dependency. +The Mojo standard library uses the following high-level organization: + +- Group related functions within the same file. +- Group related files within the same directory. +- Do not add dependencies to the `stdlib` module because, by definition, it is +required to be a leaf dependency. ```text > stdlib # stdlib root directory @@ -34,7 +36,7 @@ All Mojo source files must end with the extension `.mojo` or `.🔥`. Mojo provides a command line formatting utility, `mojo format`, designed to automatically format your code according to the official Mojo style guidelines. -It adjusts indentation, spacing, and line breaks making code more readable and +It adjusts indentation, spacing, and line breaks, making code more readable and consistent. ```bash @@ -56,7 +58,7 @@ produced by `mojo format`. #### Column limit -Mojo code has a column limit of 80 characters. +Mojo code has a column limit (line length) of 80 characters. #### File license header @@ -91,7 +93,7 @@ defined on structs. struct MyStruct(Sized, Stringable): - """This is MyStruct.""" + """Description goes here.""" var field: Int @@ -121,10 +123,13 @@ struct MyStruct(Sized, Stringable): ### Identifier naming conventions -The following are the recommended types of `case styles` used in Mojo Standard -Library code. +There are several ways to capitalize and separate words, known as "case +styles." By following the same set of case styles in our code, Mojo developers +ensure their code is accessible and understandable to others in the community. + +This first table is just a definition of the various "case styles." -| Case Style | Description | Example +| Case style | Description | Example |------------------------|-------------------------------------------|----------------- | `snake_case` | All lowercase with underscores | `variable_name` | `PascalCase` | Each word starts with an uppercase letter | `StructName` @@ -132,11 +137,9 @@ Library code. | `kebab-case` | All lowercase with hyphens | `project-name` | `flatcase` | All lowercase without separators | `basename` -The following table outlines the appropriate use of various casing styles in the -Mojo standard library. By following these conventions, Mojo developers ensure -their code is accessible and understandable to others in the community. +The following table shows our preferred use of different case styles. -| Item Kind | Example | Case Convention +| Code kind | Example | Case style |----------------------|--------------------------------|--------------------------- | `fn` / `def` | `fn engage_hyperdrive()` | `snake_case` | `struct` | `struct Point` | `PascalCase` @@ -154,13 +157,12 @@ their code is accessible and understandable to others in the community. | `fn` type parameter | `fn do_it[Action: Actionable](action: Action)` | `PascalCase` | `fn` value parameter | `fn repeat[Count: Int]()` | `PascalCase` -The demonstrated style choices intend to illustrate the various naming -conventions used in the standard library. However, these choices may not match -the existing style in all code in its current state. When preparing a change, it -is important to adhere to the style and naming conventions already established -in that module. Therefore, if the module you are working on uses a different -style, continue using that style to maintain consistency. We are not currently -accepting pull requests that propose extensive formatting or renaming changes. +Although these are our style conventions, not all code currently adheres to it. +When preparing a new change, it is important to adhere to the style and naming +conventions already established in that module. Therefore, if the module you +are working on uses a different style, continue using that style to maintain +consistency. We are not currently accepting pull requests that propose +extensive formatting or renaming changes. ### Naming guidelines @@ -178,7 +180,7 @@ struct Array[LENGTH: Int, ElementType: Movable] # 🔴 Avoid struct Array[ElementType: Movable, Length: Int] # 🟢 Preferred ``` -### Container Lifecycle Semantics +### Container lifecycle semantics #### ℹ️ Prefer explicit copy constructors; avoid allowing implicit copies @@ -203,7 +205,7 @@ When designing a new type, don’t allow implicit copies unless the copy is trivial (order `O(1)`). In other words, don’t define a `__copyinit__()` function if the copy is expensive. Instead, define an *explicit* copy constructor: an `__init__()` constructor that takes a value of -the same type. +the same type: ```mojo struct MyStruct: @@ -214,27 +216,27 @@ struct MyStruct: ### Import statements -- Explicitly import entities (functions, structs, aliases) used rather - than relying on transitive imports. -- Import only what you use; in general, prefer not to use +- Explicitly import entities used (functions, structs, aliases), rather + than rely on transitive imports. +- Import only what you use; in general, avoid using `from some_package import *`. - Import statements should be sorted lexicographically. ### API docstrings -Every public function and public struct (including data fields) in the Standard -Library must have doc strings. There is tooling to ensure public functions -adhere to the doc string validation. +Every public function and public struct (including data fields) in the standard +library must have docstrings (code comments that describe the API behavior). +Mojo includes tooling to ensure that public functions include docstrings. You can run `./stdlib/scripts/check-doc-strings.sh` to validate -doc strings. If the command exits with a 0 exit code, the doc strings are +docstrings. If the command exits with a `0` exit code, the docstrings are compliant; otherwise, an error will be shown. This is also enforced by the LSP with warnings for anything that doesn’t conform, you can generate docstrings based on the signature using an LSP Quick Fix: -![VS Code documentation lint quick fix](./images/doc-lint-quick-fix.png) + -We follow the Google convention for +We follow Google's Python convention for [docstrings outlined here](https://google.github.io/styleguide/pyguide.html#383-functions-and-methods) which looks like this: diff --git a/stdlib/docs/vision.md b/stdlib/docs/vision.md index 507bfd77f1..a015be8c79 100644 --- a/stdlib/docs/vision.md +++ b/stdlib/docs/vision.md @@ -1,23 +1,26 @@ # Vision +This page outlines the principles we aspire to follow and the more concrete +objectives and goals we aim to accomplish in the Mojo standard library. + ## Principles The following are “North Star” principles for the Mojo standard library. -These principles will inform multiple future decisions from what features we +These principles will inform multiple future decisions, from what features we work on to what bugs we prioritize during triage. In short, the standard library vision is the ideal we may never reach, but we collectively show up every day to work towards it. - **Foster a vibrant community collaborating globally.** The community is -encour aged to engage with the standard library and language evolution. We +encouraged to engage with the standard library and language evolution. We intend to ignite enthusiasm to contribute to the expanding Mojo package ecosystem. - The standard library prioritizes - **Performance > Safety > Portability > Debuggability.** + **Performance `>` Safety `>` Portability `>` Debuggability.** -- **Respectable performance by default.** standard library intends to provide -respectable performance by default — but not perfect performance. We support +- **Respectable performance by default.** The standard library should provide +respectable performance by default, but not perfect performance. We support low-level controls that enable performance tuning by systems engineers. While providing consistently strong performance over time, we do so with minimal regressions. @@ -25,25 +28,24 @@ regressions. - **Portability by default.** The use of MLIR enables portability across a variety of platforms without writing per-platform code in the standard library. -- **The standard library does not have special privileges.** standard library -types are not special and do not enjoy elevated privileges over user-contributed -code. This empowers Mojicians to create primitives equally as expressive as core -language primitives. +- **The standard library does not have special privileges.** The standard +library types are not special and do not enjoy elevated privileges over +user-contributed code. This empowers Mojicians to create primitives equally as +expressive as core language primitives. -- **Fully utilize available hardware.** The standard library should not inhibit -using any available hardware on the system. On the contrary, standard library -primitives will enable users to maximize the utility of all available system +- **Fully utilize available hardware.** The standard library should not restrict +the use of available hardware on the system. On the contrary, standard library +primitives should enable users to maximize the utility of all available system hardware. -- **standard library features prioritize AI workload optimizations.** Mojo +- **Standard library features prioritize AI workload optimizations.** Mojo ultimately aims to be a multi-purpose programming language used to solve problems in the systems programming domain. However, we will prioritize standard library features and optimizations that improve the state of the art for AI. -## What's not in the vision +### Non-principles -We reject the following vision statements, and the reasoning for each is written -inline. +We reject the following principle statements: - **Tensor operations are first-class citizens.** Mojo has a prime directive to optimize AI workloads. However, certain core AI primitives are tightly @@ -52,7 +54,7 @@ inline. ## Objectives and goals -- Make unsafe or risky things explicit. Software using unsafe constructs is +- **Make unsafe or risky things explicit.** Software using unsafe constructs is inevitable, but it must be minimized and explicit to the reader. Safe things shouldn’t look like unsafe ones and unsafe constructs should leave artifacts to see in the code. @@ -65,19 +67,19 @@ inline. out of the box for kernel developers to use. The language runtime provides a decently performing default global allocator implementation (eg. thread local caches, automatic slab-size scaling based on heuristics, virtual memory-based - defragmentation, and more…). + defragmentation, and more). - **First-class support for parallelism and concurrency.** To fully utilize available hardware, the standard library will provide a complete suite of - primitives maximizing the parallelism potential of the system. + primitives that maximize the parallelism potential of the system. - **First-class debugging features.** Integration with Mojo debugger by incorporating LLDB visualizers for the standard library types and collections to make debugging easy. -- **Consistent API and behavior across all stdlib primitives.** A higher-level - goal of the stdlib is to be an example of well-written mojo code. For example, - all collections should behave consistently with: +- **Consistent API and behavior across all primitives.** A higher-level goal of + the standard library is to be an example of well-written mojo code. For + example, all collections should behave consistently with: - Default-constructed collections do not allocate memory unless they are holding an element. @@ -87,15 +89,15 @@ inline. - Naming of public aliases/parameters common among collections (akin to `value_type` and friends in C++). -- **Interoperability with Python code** allows progressively migrating code to +- **Interoperability with Python code**. Allows progressively migrating code to Mojo over time to not force an entire rewrite just to improve the performance of code where it matters most. -## Non-goals +### Non-goals -While some of these may be common goals of modern programming languages, the -value doesn’t outweigh the costs for us right now as we are moving fast to build -the language. While we don’t actively attempt to break the following we provide +While some of the following may be common goals of other languages, the +values don't outweigh the costs for us right now as we are moving fast to build +the language. While we don’t actively attempt to break the following, we provide no guarantees that they work — especially over multiple releases. - Stable ABI between language/compiler and library. From 84b8a4822cf882e34e62f4b93ca3650254249890 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 27 Mar 2024 10:25:19 -0700 Subject: [PATCH 0034/2019] [Docs] Update changelog for 24.2. (#35920) Trying something new here, dividing the items up a little differently to see if we can make them a little more manageable. MODULAR_ORIG_COMMIT_REV_ID: e2347968921765e0e0f00203e7dff081a61e69ec --- docs/changelog-released.md | 407 +++++++++++++++++++++++++++++++++++++ docs/changelog.md | 287 -------------------------- 2 files changed, 407 insertions(+), 287 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 7e16034919..2ebbfc8f4a 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -30,6 +30,413 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## v24.2 (2024-03-28) + +### 🔥 Legendary + +- The Mojo standard library is now open source! Check out the + [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) + for everything you need to get started. + +- Structs and other nominal types are now allowed to implicitly conform to + traits. A struct implicitly conforms to a trait if it implements all the + requirements for the trait. For example, any struct that implements the + `__str__()` method implicitly conforms to `Stringable`, and is usable with + the `str()` built-in function. + + ```mojo + @value + struct Foo: + fn __str__(self) -> String: + return "foo!" + + fn main(): + print(str(Foo())) # prints 'foo!' + ``` + + We still strongly encourage you to explicitly list the traits a struct + conforms to when possible: + + ```mojo + @value + struct Foo(Stringable): ... + ``` + + Not only is this useful for documentation and for communicating intentions, + but in the future, explicit conformance will be useful for features like + default methods and extensions. + +- Mojo's Python interoperability now supports passing keyword arguments to + Python functions: + + ```mojo + from python import Python + + def main(): + plt = Python.import_module("matplotlib.pyplot") + plt.plot((5, 10), (10, 15), color="red") + plt.show() + ``` + +### Language changes + +#### ⭐️ New + +- Mojo now has support for variadic keyword arguments, often referred to as + `**kwargs`. This means you can now declare and call functions like this: + + ```mojo + fn print_nicely(**kwargs: Int) raises: + for key in kwargs.keys(): + print(key[], "=", kwargs[key[]]) + + # prints: + # `a = 7` + # `y = 8` + print_nicely(a=7, y=8) + ``` + + There are currently a few limitations: + + - The ownership semantics of variadic keyword arguments are always `owned`. + This is applied implicitly, and cannot be declared otherwise: + + ```mojo + # Not supported yet. + fn borrowed_var_kwargs(borrowed **kwargs: Int): ... + ``` + + - Functions with variadic keyword arguments cannot have default values for + keyword-only arguments. For example: + + ```mojo + # Not allowed yet, because `b` is keyword-only with a default. + fn not_yet(*, b: Int = 9, **kwargs: Int): ... + + # Okay, because `c` is positional-or-keyword, so it can have a default. + fn still_works(c: Int = 5, **kwargs: Int): ... + ``` + + - Dictionary unpacking is not supported yet: + + ```mojo + fn takes_dict(d: Dict[String, Int]): + print_nicely(**d) # Not supported yet. + ``` + + - Variadic keyword _parameters_ are not supported yet: + + ```mojo + # Not supported yet. + fn var_kwparams[**kwparams: Int](): ... + ``` + +#### 🦋 Changed or removed + +- `let` declarations now produce a compile time error instead of a warning, + our next step in [removing let + declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md). + The compiler still recognizes the `let` keyword for now in order to produce + a good error message, but that will be removed in subsequent releases. + +- Mojo now warns about unused values in both `def` and `fn` declarations, + instead of completely disabling the warning in `def`s. It never warns about + unused `object` or `PythonObject` values, tying the warning to these types + instead of the kind of function they are unused in. This will help catch API + usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. + +- For the time being, dynamic type values will be disabled in the language. For + example, the following will now fail with an error: + + ```mojo + var t = Int # dynamic type values not allowed + + struct SomeType: ... + + takes_type(SomeType) # dynamic type values not allowed + ``` + + We want to take a step back and (re)design type valued variables, + existentials, and other dynamic features. This does not affect type valued + **parameters**, so the following works as before: + + ```mojo + alias t = Int # still 🔥 + + struct SomeType: ... + + takes_type[SomeType]() # already 🔥 + + >fn uses_trait[T: SomeTrait](value: T): ... # still 🔥 + ``` + +- The `*_` expression in parameter expressions is now required to occur at the + end of a positional parameter list, instead of being allowed in the middle. + + ```mojo + # No longer supported + alias FirstUnbound = SomeStruct[*_, 42] + alias MidUnbound = SomeStruct[7, *_, 6] + # Still supported + alias LastUnbound = SomeStruct[42, *_] + ``` + + We narrowed this because we want to encourage type designers + to get the order of parameters right, and want to extend `*_` to support + keyword parameters as well in the future. + +### Standard library changes + +#### ⭐️ New + +- `DynamicVector` has been renamed to + [`List`](/mojo/stdlib/collections/list.html#list), and has moved from the + `collections.vector` module to the `collections.list` module. In addition: + + - You can now construct a `List` from a variadic number of values. For + example: + + ```mojo + var numbers = List[Int](1, 2, 3) + ``` + + - `List` and + [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + types now support negative indexing. This means that you can write `vec[-1]` + which is equivalent to `vec[len(vec)-1]`. + + - `List.push_back()` has been removed. Please use the `append()` function + instead. + +- The [`print()`](/mojo/stdlib/builtin/io#print) function now takes `sep` and + `end` keyword arguments. This means that you can write: + + ```mojo + print("Hello", "Mojo", sep=", ", end="!!!\n") # prints Hello, Mojo!!! + ``` + + `sep` defaults to the empty string and `end` defaults to "\n". + + Also, the `print_no_newline()` function has been removed. Please use + `print(end="")` instead. + +- The [`FloatLiteral`](/mojo/stdlib/builtin/float_literal#floatliteral) type is + now an infinite-precision nonmaterializable type. This means you can do + compile-time calculations using `FloatLiteral` without rounding errors. When + materialized at runtime, a `FloatLiteral` value is converted to a + [`Float64`](/mojo/stdlib/builtin/simd). + + ```mojo + # third is an infinite-precision FloatLiteral value + alias third = 1.0 / 3.0 + # t is a Float64 + var t = third + ``` + +- String types all conform to the + [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means + that you can now call `int("123")` to get the integer `123`. If the integer + cannot be parsed from the string, then an error is raised. + +- The `Tensor` type now has [`argmax()`](/mojo/stdlib/tensor/tensor#argmax) and + [`argmin()`](/mojo/stdlib/tensor/tensor#argmin) functions to compute the + position of the max or min value. + +- Added a new + [`collections.OptionalReg`](/mojo/stdlib/collections/optional#optionalreg) + type, a register-passable alternative to + [`Optional`](/mojo/stdlib/collections/optional#optional). + +- The [`ulp()`](/mojo/stdlib/math/math#ulp) function has been added to the + `math` module. This allows you to get the units of least precision (or units + of last place) of a floating point value. + +#### 🦋 Changed + +- The + [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable#equalitycomparable) + trait now requires the `__ne__()` method for conformance in addition to the + previously required `__eq__()` method. + +- Many types now declare conformance to `EqualityComparable` trait. + +- [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) parameter order + has changed to `StaticTuple[type, size]` for consistency with `SIMD` and + similar collection types. + +- The signature of the + [`elementwise()`](/mojo/stdlib/algorithm/functional#elementwise) function has + been changed. The new order is is `function`, `simd_width`, and then `rank`. + As a result, the rank parameter can now be inferred and one can call + `elementwise()` without it: + + ```mojo + elementwise[func, simd_width](shape) + ``` + +- `PythonObject` is now register-passable. + +- `PythonObject.__iter__()` now works correctly on more types of iterable Python + objects. Attempting to iterate over non-iterable objects will now raise an + exception instead of behaving as if iterating over an empty sequence. + `__iter__()` also now borrows `self` rather than requiring `inout`, allowing + code like: + + ```mojo + for value in my_dict.values(): + ... + ``` + +#### 🚚 Moved + +- We took the opportunity to rehome some modules into their correct package + as we were going through the process of open-sourcing the Mojo standard + library. Specifically, the following are some breaking changes worth + calling out. Please update your import statements accordingly. + + - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), + [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer), and friends have moved + from the `memory` package into a new `buffer` package. + + ```mojo + from buffer import Buffer, NDBuffer + ``` + + - `utils.list`, including the [`Dim`](/mojo/stdlib/buffer/list#dim) and + [`DimList`](/mojo/stdlib/buffer/list#dimlist) types, has moved to + the `buffer` package. + + ```mojo + from buffer import Dim, DimList + ``` + + - The [`parallel_memcpy()`](/mojo/stdlib/buffer/memory#parallel_memcpy) + function has moved from the `memory` package into the `buffer` package. + + ```mojo + from buffer import parallel_memcpy + ``` + + - The [`rand()`](/mojo/stdlib/tensor/random#rand) and + [`randn()`](/mojo/stdlib/tensor/random#randn) functions from the `random` + package that return a `Tensor` have moved to the `tensor` package. Note that + the overloads that write to a `DTypePointer` remain in the `random` package. + + If you happen to be using both versions in the same source file, you can + import them both using the `import as` syntax: + + ```mojo + from tensor import rand + from random import rand as rand_dt + ``` + + - The `trap()` function has been renamed to + [`abort()`](/mojo/stdlib/os/os#abort). It also has moved from the `debug` + module to the `os` module. + + ```mojo + from os import abort + ``` + + - The [`isinf()`](/mojo/stdlib/math/math#isinf) and + [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from + `math.limits` to the `math` module. + + ```mojo + from math import ininf, isfinite + ``` + +### Tooling changes + +#### ⭐️ New + +- Docstring code blocks can now use `%#` to hide lines of code from + documentation generation. + + For example: + + ```mojo + var value = 5 + %# print(value) + ``` + + Will generate documentation of the form: + + ```mojo + var value = 5 + ``` + + Hidden lines are processed as if they were normal code lines during test + execution. This allows for writing additional code within a docstring + example that is only used to ensure the example is runnable/testable. + +- The Mojo LSP server now allow you to specify additional search paths to use + when resolving imported modules in a document. You can specify search paths + on the command line, using the `-I` option, or you can add them to the + `mojo.lsp.includeDirs` setting in the VS Code extension. + +### ❌ Removed + +- The `__get_address_as_lvalue` magic function has been removed. You can now + get an LValue from a `Pointer` or `Reference` by using the dereference + operator (`[]`): + + ```mojo + var ptr: Pointer[MyRecord] + ... + # Doesn't work + __get_address_as_lvalue(ptr.value) = MyRecord(3, 5) + # Works + ptr[] = MyRecord(3, 5) + ``` + +- The type parameter for the `memcpy` function is now automatically inferred. + This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` will + no longer work and the user would have to change the code to `memcpy(...)`. + +- The [`memcpy()`](/mojo/stdlib/memory/memory#memcpy) overload that worked on + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types has been removed in favor + of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe#pointer) and + [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer): + + ```mojo + # Doesn't work + memcpy(destBuffer, srcBuffer, count) + # Works + memcpy(destBuffer.data, srcBuffer.data, count) + ``` + +- The functions `max_or_inf()`, `min_or_neginf()` have been removed from + `math.limit`. These functions were only used by the SIMD type. + +- As mentioned previously, the `print_no_newline()` function has been removed. + Please use `print(end="")` instead. + +### 🛠️ Fixed + +- [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference + now recursively matches function types. +- [#951](https://github.com/modularml/mojo/issues/951) - Functions that were + both `async` and `@always_inline` incorrectly errored. +- [#1858](https://github.com/modularml/mojo/issues/1858) - Trait with parametric + methods regression. +- [#1892](https://github.com/modularml/mojo/issues/1892) - Forbid unsupported + decorators on traits. +- [#1735](https://github.com/modularml/mojo/issues/1735) - Trait-typed values + are incorrectly considered equal. +- [#1909](https://github.com/modularml/mojo/issues/1909) - Crash due to nested + import in unreachable block. +- [#1921](https://github.com/modularml/mojo/issues/1921) - Parser crashes + binding Reference to lvalue with subtype lifetime. +- [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` + should return `T` instead of `Optional[T]`. +- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` + to floating point or integral types. +- [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` + does not work when specifying `end=""` +- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods + correctly handle edge cases where `size_out >= size`. + ## v24.1.1 (2024-03-18) This release includes installer improvements and enhanced error reporting for diff --git a/docs/changelog.md b/docs/changelog.md index a5d991ac6d..7a557e9c17 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,300 +13,13 @@ and tools. Please add any significant user-visible changes here. ### 🔥 Legendary -- The Mojo standard library is now open source! Check out the - [README](https://github.com/modularml/mojo/blob/nightly/stdlib/README.md) - for everything you need to get started. - -- Structs and other nominal types are now allowed to implicitly conform to - traits. A struct implicitly conforms to a trait if it implements all the - requirements for the trait. For example, any struct that implements `__str__` - will implicitly conform to `Stringable`, and then is usable with `str`. - - ```mojo - @value - struct Foo: - fn __str__(self) -> String: - return "foo!" - - fn main(): - print(str(Foo())) # prints 'foo!' - ``` - - Explicit conformance is still strongly encouraged where possible, because it - is useful for documentation and for communicating intentions. In the future, - explicit conformance will still be useful for features like default methods - and extensions. - -- Mojo's Python interoperability now supports passing keyword arguments to - Python callables: - - ```mojo - from python import Python - - def main(): - plt = Python.import_module("matplotlib.pyplot") - plt.plot((5, 10), (10, 15), color="red") - plt.show() - ``` - ### ⭐️ New -- String types all conform to the - [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means - that you can now call `int("123")` to get the integer `123`. If the integer - cannot be parsed from the string, then an error is raised. - -- The `Tensor` type now has an `argmax` and `argmin` function to compute the - position of the max or min value. - -- The `FloatLiteral` type is now an infinite precision nonmaterializable type. - When materialized, `FloatLiteral` is converted to `Float64`. - -- The [`List`](/mojo/stdlib/collections/list.html#list) type now supports - construction from a variadic number of values. For example, - `List[Int](1, 2, 3)` works now. - -- The `print` function now takes a `sep` and `end` as keyword. This means that - one can write `print("Hello", "Mojo", sep="/", end="!!!\n")` to print the - message `Hello/Mojo!!!\n` to the screen. - -- The Mojo LSP server, via the new `-I` argument, now allows specifying - additional search paths to use when resolving imported modules in a document. - A corresponding `mojo.lsp.includeDirs` setting was added to the VS Code - extension as well. - -- Mojo now has support for variadic keyword argument, often referred to as - `**kwargs`. This means you can now declare and call functions like this: - - ```mojo - fn print_nicely(**kwargs: Int) raises: - for key in kwargs.keys(): - print(key[], "=", kwargs[key[]]) - - # prints: - # `a = 7` - # `y = 8` - print_nicely(a=7, y=8) - ``` - - There are currently a few limitations: - - The ownership semantics of variadic keyword arguments are always `owned`. - This is applied implicitly, and cannot be declared otherwise: - - ```mojo - # Not supported yet. - fn borrowed_var_kwargs(borrowed **kwargs: Int): ... - ``` - - - Functions with variadic keyword arguments cannot have default values for - keyword-only arguments, e.g. - - ```mojo - # Not allowed yet, because `b` is keyword-only with a default. - fn not_yet(*, b: Int = 9, **kwargs: Int): ... - - # Okay, because `c` is positional-or-keyword, so it can have a default. - fn still_works(c: Int = 5, **kwargs: Int): ... - ``` - - - Dictionary unpacking is not supported yet: - - ```mojo - fn takes_dict(d: Dict[String, Int]): - print_nicely(**d) # Not supported yet. - ``` - - - Variadic keyword parameters are not supported yet: - - ```mojo - # Not supported yet. - fn var_kwparams[**kwparams: Int](): ... - ``` - -- Added new `collections.OptionalReg` type, a register-passable alternative - to `Optional`. - - - Doc string code blocks can now `%#` to hide lines of code from documentation - generation. - - For example: - - ```mojo - var value = 5 - %# print(value) - ``` - - will generate documentation of the form: - - ```mojo - var value = 5 - ``` - - Hidden lines are processed as if they were normal code lines during test - execution. This allows for writing additional code within a doc string - example that is only used to ensure the example is runnable/testable. - - - Doc string code blocks can now `%#` to hide lines of code from documentation - generation. - - For example: - - ```mojo - var value = 5 - %# print(value) - ``` - - will generate documentation of the form: - - ```mojo - var value = 5 - ``` - - Hidden lines are processed as if they were normal code lines during test - execution. This allows for writing additional code within a doc string - example that is only used to ensure the example is runnable/testable. - - The `sys` module now contains an `exit` function that would exit a Mojo program with the specified error code. ### 🦋 Changed -- Mojo now warns about unused values in both `def` and `fn` declarations, - instead of completely disabling the warning in `def`s. It never warns about - unused `object` or `PythonObject` values, tying the warning to these types - instead of the kind of function they are unused in. This will help catch API - usage bugs in `def`s and make imported Python APIs more ergonomic in `fn`s. - -- The [`DynamicVector`](/mojo/stdlib/collections/list#list) and - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) - types now support negative indexing. This means that you can write `vec[-1]` - which is equivalent to `vec[len(vec)-1]`. - -- The [`isinf()`](/mojo/stdlib/math/math#isinf) and - [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from - `math.limits` to the `math` module. - -- The `ulp` function has been added to the `math` module. This allows one to get - the units of least precision (or units of last place) of a floating point - value. - -- `EqualityComparable` trait now requires `__ne__` function for conformance in addition - to the previously existing `__eq__` function. - -- Many types now declare conformance to `EqualityComparable` trait. - -- `DynamicVector` has been renamed to `List`. It has also moved from the `collections.vector` - module to `collections.list` module. - -- `StaticTuple` parameter order has changed to `StaticTuple[type, size]` for - consistency with `SIMD` and similar collection types. - -- The signature of the elementwise function has been changed. The new order is - is `function`, `simd_width`, and then `rank`. As a result, the rank parameter - can now be inferred and one can call elementwise via: - - ```mojo - elementwise[func, simd_width](shape) - ``` - -- For the time being, dynamic type value will be disabled in the language, e.g. - the following will now fail with an error: - - ```mojo - var t = Int # dynamic type values not allowed - - struct SomeType: ... - - takes_type(SomeType) # dynamic type values not allowed - ``` - - We want to take a step back and (re)design type valued variables, - existentials, and other dynamic features for more 🔥. This does not affect - type valued parameters, so the following will work as before: - - ```mojo - alias t = Int # still 🔥 - - struct SomeType: ... - - takes_type[SomeType]() # already 🔥 - ``` - -- `PythonObject` is now register-passable. - -- `PythonObject.__iter__` now works correctly on more types of iterable Python - objects. Attempting to iterate over non-iterable objects will now raise an - exception instead of behaving as if iterating over an empty sequence. - `__iter__` also now borrows `self` rather than requiring `inout`, allowing - code like `for value in my_dict.values():`. - -- `List.push_back` has been removed. Please use the `append` function instead. - -- We took the opportunity to rehome some modules into their correct package - as we were going through the process of open-sourcing the Mojo Standard - Library. Specifically, the following are some breaking changes worth - calling out. Please update your import statements accordingly. - - `utils.list` has moved to `buffer.list`. - - `rand` and `randn` functions in the `random` package that return a `Tensor` - have moved to the `tensor` package. - - `Buffer`, `NDBuffer`, and friends have moved from the `memory` package - into a new `buffer` package. - - The `trap` function has been renamed to `abort`. It also has moved from the - `debug` module to the `os` module. - - `parallel_memcpy` has moved from the `memory` package into - the `buffer` package. - -- The `*_` expression in parameter expressions is now required to occur at the - end of a positional parameter list, instead of being allowed in the middle. - This is no longer supported: `SomeStruct[*_, 42]` but `SomeStruct[42, *_]` is - still allowed. We narrowed this because we want to encourage type designers - to get the order of parameters right, and want to extend `*_` to support - keyword parameters as well in the future. - ### ❌ Removed -- `let` declarations now produce a compile time error instead of a warning, - our next step in [removing let - declarations](https://github.com/modularml/mojo/blob/main/proposals/remove-let-decls.md). - The compiler still recognizes the `let` keyword for now in order to produce - a good error message, but that will be removed in subsequent releases. - -- The `__get_address_as_lvalue` magic function has been removed. You can now - get an LValue from a `Pointer` or `Reference` by using the `ptr[]` operator. - -- The type parameter for the `memcpy` function is now automatically inferred. - This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` would - no longer work and the user would have to change the code to `memcpy(...)`. - -- `print_no_newline` has been removed. Please use `print(end="")` instead. - -- `memcpy` on `Buffer` has been removed in favor of just overloads for `Pointer` - and `DTypePointer`. - -- The functions `max_or_inf`, `min_or_neginf` have been removed from - `math.limit` and just inlined into their use in SIMD. - ### 🛠️ Fixed - -- [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference - now recursively matches function types. -- [#951](https://github.com/modularml/mojo/issues/951) - Functions that were - both `async` and `@always_inline` incorrectly errored. -- [#1858](https://github.com/modularml/mojo/issues/1858) - Trait with parametric - methods regression. -- [#1892](https://github.com/modularml/mojo/issues/1892) - Forbid unsupported - decorators on traits. -- [#1735](https://github.com/modularml/mojo/issues/1735) - Trait-typed values - are incorrectly considered equal. -- [#1909](https://github.com/modularml/mojo/issues/1909) - Crash due to nested - import in unreachable block. -- [#1921](https://github.com/modularml/mojo/issues/1921) - Parser crashes - binding Reference to lvalue with subtype lifetime. -- [#1945](https://github.com/modularml/mojo/issues/1945) - `Optional[T].or_else()` - should return `T` instead of `Optional[T]`. -- [#1940](https://github.com/modularml/mojo/issues/1940) - Constrain `math.copysign` - to floating point or integral types. -- [#1838](https://github.com/modularml/mojo/issues/1838) - Variadic `print` - does not work when specifying `end=""` -- [#1826](https://github.com/modularml/mojo/issues/1826) - The `SIMD.reduce` methods - correctly handle edge cases where `size_out >= size`. From cde7e8e29b88a076498fba5b6cb5f585ce6431c9 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 11:26:55 -0700 Subject: [PATCH 0035/2019] Revert "[docs] Update requirements to allow Graviton4" (#35971) Reverts modularml/modular#35573 MODULAR_ORIG_COMMIT_REV_ID: c916e887eda3965039cc57ff4bc8fc2c3f0679fe --- docs/manual/get-started/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 0fcb56014a..3fe41ff422 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -55,7 +55,7 @@ Linux: - Ubuntu 20.04/22.04 LTS - x86-64 CPU (with [SSE4.2 or newer](https://www.intel.com/content/www/us/en/support/articles/000057621/processors.html)) - or AWS Graviton2 CPU or newer + or AWS Graviton2/3 CPU - Minimum 8 GiB RAM - Python 3.8 - 3.11 - g++ or clang++ C++ compiler From 5641093d45e0bec0ff72f1a1c1bff1ba7e2f3e3f Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Wed, 27 Mar 2024 16:36:44 -0500 Subject: [PATCH 0036/2019] [Docs] Move images into folder that gets pulled into public repo (#36008) These are broken links in the public repos, as the root images folder isn't copied across. Good to keep them out of the root so moving to `stdlib/docs/images` with the other images, to keep them out of the way. MODULAR_ORIG_COMMIT_REV_ID: ec143768aeec32aa00967247dd0e141132f48806 --- CONTRIBUTING.md | 6 +++--- stdlib/docs/development.md | 2 +- .../docs}/images/base-branch.png | Bin .../docs}/images/create-fork.png | Bin .../docs}/images/nightly-extension.png | Bin 5 files changed, 4 insertions(+), 4 deletions(-) rename {docs/oss-material => stdlib/docs}/images/base-branch.png (100%) rename {docs/oss-material => stdlib/docs}/images/create-fork.png (100%) rename {docs/oss-material => stdlib/docs}/images/nightly-extension.png (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4fc579813..7608951c78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -184,7 +184,7 @@ for more details. Go to the [Mojo repo](https://github.com/modularml/mojo) and click the fork button: -![Create Fork](./images/create-fork.png) +![Create Fork](stdlib/docs/images/create-fork.png) Clone your forked repo locally with the command: @@ -249,7 +249,7 @@ If you're using bash, replace the three `~/.zshrc` above with `~/.bashrc`. Install the [Mojo nightly VS Code extension](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo-nightly): - + You can only have one Mojo extension enabled at a time, remember to switch back when using the stable release! @@ -275,7 +275,7 @@ remote: https://github.com/jackos/mojo/pull/new/my-fix-pr Make sure you point it to the `nightly` branch: -![Base Branch](images/base-branch.png) +![Base Branch](stdlib/docs/images/base-branch.png) Now fill out the details: diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index e9f2d19a00..362da08323 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -305,7 +305,7 @@ error: CHECK: expected string not found in input Lets fix the test that we just added: -```plaintext +```mojo # CHECK-LABEL: test_print_cwd def test_print_cwd(): diff --git a/docs/oss-material/images/base-branch.png b/stdlib/docs/images/base-branch.png similarity index 100% rename from docs/oss-material/images/base-branch.png rename to stdlib/docs/images/base-branch.png diff --git a/docs/oss-material/images/create-fork.png b/stdlib/docs/images/create-fork.png similarity index 100% rename from docs/oss-material/images/create-fork.png rename to stdlib/docs/images/create-fork.png diff --git a/docs/oss-material/images/nightly-extension.png b/stdlib/docs/images/nightly-extension.png similarity index 100% rename from docs/oss-material/images/nightly-extension.png rename to stdlib/docs/images/nightly-extension.png From 58ce2b71c1c235c5a6aec474ab0c5660421a8fbf Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 27 Mar 2024 15:13:34 -0700 Subject: [PATCH 0037/2019] [Stdlib] Make the constructors for tensor more consistent, NFC (#36010) This makes the constructions via a pointer have the same ordering as the other constructions ... and is more consistent. MODULAR_ORIG_COMMIT_REV_ID: de6126e1ddb496636347b1974dd0bf5cd4ee6b05 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7a557e9c17..e1de3741c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,10 @@ and tools. Please add any significant user-visible changes here. - The `sys` module now contains an `exit` function that would exit a Mojo program with the specified error code. +- The constructors for `tensor.Tensor` have been changed to be more consistent. + As a result, one has to pass in the shape as first argument (instead of the + second) when constructing a tensor with pointer data. + ### 🦋 Changed ### ❌ Removed From 3aa5d7ea8277c55cb429321b044c06eb65c551a3 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 27 Mar 2024 16:00:54 -0700 Subject: [PATCH 0038/2019] [Stdlib] Fix printing of DType.address values (#36032) We were not properly printing the values for DType.address because we were either not catching the special case and/or had a bad format printf. This fixes both cases. MODULAR_ORIG_COMMIT_REV_ID: f5aefaf9891c6f3f55d391e9ee0691a73c541f2a --- stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/io.mojo | 6 ++---- stdlib/test/builtin/test_print.mojo | 6 ++++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index b6f09018ff..16d2c5223b 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -679,7 +679,7 @@ fn _get_dtype_printf_format[type: DType]() -> StringLiteral: return _index_printf_format() elif type == DType.address: - return "%zx" + return "%p" elif type.is_floating_point(): return "%.17g" diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 8a78e24caa..f8602a4810 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -171,7 +171,7 @@ fn _snprintf_scalar[ return _snprintf(buffer, size, "True") else: return _snprintf(buffer, size, "False") - elif type.is_integral(): + elif type.is_integral() or type == DType.address: return _snprintf(buffer, size, format, x) elif ( type == DType.float16 or type == DType.bfloat16 or type == DType.float32 @@ -246,7 +246,7 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): @parameter if type == DType.bool: _put("True") if x else _put("False") - elif type.is_integral(): + elif type.is_integral() or type == DType.address: _printf(format, x) elif type.is_floating_point(): @@ -255,8 +255,6 @@ fn _put_simd_scalar[type: DType](x: Scalar[type]): _printf(format, x.cast[DType.float64]()) else: _put(str(x)) - elif type == DType.address: - _printf(format, x) else: constrained[False, "invalid dtype"]() diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 81315b64f3..c9507d767b 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -54,6 +54,12 @@ fn test_print(): # CHECK: 184467440737095516 print(UInt64(-1)) + # CHECK: 0x16 + print(Scalar[DType.address](22)) + + # CHECK: 0xdeadbeaf + print(Scalar[DType.address](0xDEADBEAF)) + var hello: StringRef = "Hello," var world: String = "world!" var f: Bool = False From 8296cd3b4b385db14e41e7d6ba9f816bda276eec Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 27 Mar 2024 17:07:33 -0700 Subject: [PATCH 0039/2019] [Stdlib] Make SIMD slice and insert take offset as a param (#33664) Followup of [Internal link] based on [Internal link] LLVM expects the offsets to be immediate values. We were getting lucky here because all of our uses the values are constant after elaboration. Rather than rely on that behavior, just make the offsets parameters. --------- Co-authored-by: Tracy Sharpe MODULAR_ORIG_COMMIT_REV_ID: 1445015035446507a22464b1e76f7e0116d81eb6 --- stdlib/src/builtin/simd.mojo | 34 +++++++++++++----------------- stdlib/test/builtin/test_simd.mojo | 21 ++++++++++-------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 5f106b410e..8ab473ac0d 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1475,8 +1475,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn slice[ - output_width: Int - ](self, offset: Int = 0) -> SIMD[type, output_width]: + output_width: Int, /, *, offset: Int = 0 + ](self) -> SIMD[type, output_width]: """Returns a slice of the vector of the specified width with the given offset. @@ -1486,8 +1486,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Parameters: output_width: The output SIMD vector size. - - Args: offset: The given offset for the slice. Returns: @@ -1495,16 +1493,16 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( `self[offset:offset+output_width]`. """ constrained[ - 0 < output_width <= size, + 0 < output_width + offset <= size, "output width must be a positive integer less than simd size", ]() - debug_assert(output_width + offset <= size, "slice is out of range") @parameter if output_width == 1: return self[offset] - if offset._positive_rem(simdwidthof[type]()) != 0: + @parameter + if offset % simdwidthof[type](): var tmp = SIMD[type, output_width]() @unroll @@ -1517,28 +1515,25 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ) @always_inline("nodebug") - fn insert[ - input_width: Int - ](self, value: SIMD[type, input_width], offset: Int = 0) -> Self: + fn insert[*, offset: Int = 0](self, value: SIMD[type, _]) -> Self: """Returns a the vector where the elements between `offset` and `offset + input_width` have been replaced with the elements in `value`. Parameters: - input_width: The width of the value input that is going to be - inserted. + offset: The offset to insert at. Args: value: The value to be inserted. - offset: The offset to insert at. Returns: A new vector whose elements at `self[offset:offset+input_width]` contain the values of `value`. """ - debug_assert( + alias input_width = value.size + constrained[ 0 < input_width + offset <= size, "insertion position must not exceed the size of the vector", - ) + ]() @parameter if size == 1: @@ -1552,7 +1547,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # so resort to a for loop. Note that this can be made more intelligent # by dividing the problem into the offset, offset+val, val+input_width # where val is a value to align the offset to the simdwidth. - if offset._positive_rem(simdwidthof[type]()) != 0: + @parameter + if offset % simdwidthof[type](): var tmp = self @unroll @@ -1660,7 +1656,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var res = SIMD[type, 2 * size]() res = res.insert(self) - return res.insert(other, size) + return res.insert[offset=size](other) @always_inline("nodebug") fn interleave(self, other: Self) -> SIMD[type, 2 * size]: @@ -1770,8 +1766,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return rebind[SIMD[Self.type, size_out]](self) else: alias half_size: Int = size // 2 - var lhs = self.slice[half_size](0) - var rhs = self.slice[half_size](half_size) + var lhs = self.slice[half_size, offset=0]() + var rhs = self.slice[half_size, offset=half_size]() @parameter if half_size != size_out: diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index ab6571b206..60c767a9c9 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -291,20 +291,22 @@ fn test_insert(): # CHECK: [0, 9, 6, 3] print( - SIMD[DType.index, 4](0, 1, 2, 3).insert(SIMD[DType.index, 2](9, 6), 1) + SIMD[DType.index, 4](0, 1, 2, 3).insert[offset=1]( + SIMD[DType.index, 2](9, 6) + ) ) # CHECK: [0, 1, 2, 3, 9, 6, 3, 7] print( - SIMD[DType.index, 8](0, 1, 2, 3, 5, 6, 7, 8).insert( - SIMD[DType.index, 4](9, 6, 3, 7), 4 + SIMD[DType.index, 8](0, 1, 2, 3, 5, 6, 7, 8).insert[offset=4]( + SIMD[DType.index, 4](9, 6, 3, 7) ) ) # CHECK: [0, 1, 2, 9, 6, 3, 7, 8] print( - SIMD[DType.index, 8](0, 1, 2, 3, 5, 6, 7, 8).insert( - SIMD[DType.index, 4](9, 6, 3, 7), 3 + SIMD[DType.index, 8](0, 1, 2, 3, 5, 6, 7, 8).insert[offset=3]( + SIMD[DType.index, 4](9, 6, 3, 7) ) ) @@ -333,7 +335,8 @@ def test_address(): def test_extract(): - assert_equal(Int64(99).slice[1](0), 99) + assert_equal(Int64(99).slice[1](), 99) + assert_equal(Int64(99).slice[1, offset=0](), 99) assert_equal( SIMD[DType.index, 4](99, 1, 2, 4).slice[4](), @@ -341,17 +344,17 @@ def test_extract(): ) assert_equal( - SIMD[DType.index, 4](99, 1, 2, 4).slice[2](0), + SIMD[DType.index, 4](99, 1, 2, 4).slice[2, offset=0](), SIMD[DType.index, 2](99, 1), ) assert_equal( - SIMD[DType.index, 4](99, 1, 2, 4).slice[2](2), + SIMD[DType.index, 4](99, 1, 2, 4).slice[2, offset=2](), SIMD[DType.index, 2](2, 4), ) assert_equal( - SIMD[DType.index, 4](99, 1, 2, 4).slice[2](1), + SIMD[DType.index, 4](99, 1, 2, 4).slice[2, offset=1](), SIMD[DType.index, 2](1, 2), ) From 1c2df22b1e2184a55d9a0fb50a078fb81a41b253 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 27 Mar 2024 17:09:30 -0700 Subject: [PATCH 0040/2019] [Stdlib] Allow one to initialize fill the Tensor type (#36034) This allows one to intialize the Tensor type with some scalar constant. This enables one to do fill / zeros operations easily. Closes #35950 MODULAR_ORIG_COMMIT_REV_ID: 3aa6b8cbf4b5bc9618960fca0b3781446841c60a --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e1de3741c0..1c1d94a945 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -22,6 +22,11 @@ and tools. Please add any significant user-visible changes here. As a result, one has to pass in the shape as first argument (instead of the second) when constructing a tensor with pointer data. +- The constructor for `tensor.Tensor` will now splat a scalar if its passed in. + For example, `Tensor[DType.float32](TensorShape(2,2), 0)` will construct a + `2x2` tensor which is initialized with all zeros. This provides an easy way + to fill the data of a tensor. + ### 🦋 Changed ### ❌ Removed From f925cb531f34f175737df127b0fee0d7b952db00 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 27 Mar 2024 17:14:44 -0700 Subject: [PATCH 0041/2019] [Docs] Add section on implicit trait conformance. (#36050) MODULAR_ORIG_COMMIT_REV_ID: b48891bb406ac9dd914535f66a779277a8b5c2bd --- docs/manual/traits.ipynb | 63 ++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 0052dc3630..40fdb69ba3 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -36,18 +36,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Quack.\n", - "Moo!\n" - ] - } - ], + "outputs": [], "source": [ "%%python\n", "class Duck:\n", @@ -128,10 +119,10 @@ "This isn't too bad with only two classes. But the more classes you want to\n", "support, the less practical this approach is.\n", "\n", - "You might notice that the Mojo versions dosn't include the `try/except` \n", - "statement. We don't need it because Mojo's static type checking ensures that\n", - "you can only pass instances of `Duck` or `StealthCow` into the `make_it_quack()`\n", - "function.\n", + "You might notice that the Mojo versions of `make_it_quack()` don't include the\n", + "`try/except` statement. We don't need it because Mojo's static type checking\n", + "ensures that you can only pass instances of `Duck` or `StealthCow` into the \n", + "`make_it_quack()`function.\n", "\n", "## Using traits\n", "\n", @@ -178,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -283,8 +274,48 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Implicit trait conformance\n", "\n", + "Mojo also supports _implicit_ trait conformance. That is, if a type implements\n", + "all of the methods required for a trait, it's treated as conforming to the\n", + "trait, even if it doesn't explicitly include the trait in its declaration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "struct RubberDucky:\n", + " fn quack(self):\n", + " print(\"Squeak!\")\n", + "\n", + "make_it_quack(RubberDucky())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implicit conformance can be handy if you're defining a trait and you want it to\n", + "work with types that you don't control—such as types from the standard library,\n", + "or a third-party library.\n", "\n", + "However, we still strongly recommend explicit trait conformance wherever\n", + "possible. This has two advantages:\n", + "\n", + "- Documentation. It makes it clear that the type conforms to the trait, without\n", + " having to scan all of its methods.\n", + "\n", + "- Future feature support. When default method implementations are added to\n", + " traits, they'll only work for types that explicitly conform to traits." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "## Trait inheritance\n", "\n", "Traits can inherit from other traits. A trait that inherits from another trait\n", From 0f69b735ebd60916809f032631dcbc4353122e15 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 27 Mar 2024 17:16:49 -0700 Subject: [PATCH 0042/2019] [Docs] Add variadic keyword args section (#36044) Plus: - small updates to changelog - adding missing changelog note about removal of simd_load and simd_store - update to variadic parameter doc - `*_` unbound expression limitation in parameters doc. MODULAR_ORIG_COMMIT_REV_ID: 69a90a6ed17fb158fe2c9edf58b7c86c0b15ede3 --- docs/changelog-released.md | 60 ++++++++------------ docs/manual/functions.ipynb | 88 ++++++++++++++++++++++++++---- docs/manual/parameters/index.ipynb | 76 ++++++++++++++++++-------- 3 files changed, 151 insertions(+), 73 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 2ebbfc8f4a..bdd2bc5bbf 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -96,40 +96,9 @@ modular update mojo print_nicely(a=7, y=8) ``` - There are currently a few limitations: - - - The ownership semantics of variadic keyword arguments are always `owned`. - This is applied implicitly, and cannot be declared otherwise: - - ```mojo - # Not supported yet. - fn borrowed_var_kwargs(borrowed **kwargs: Int): ... - ``` - - - Functions with variadic keyword arguments cannot have default values for - keyword-only arguments. For example: - - ```mojo - # Not allowed yet, because `b` is keyword-only with a default. - fn not_yet(*, b: Int = 9, **kwargs: Int): ... - - # Okay, because `c` is positional-or-keyword, so it can have a default. - fn still_works(c: Int = 5, **kwargs: Int): ... - ``` - - - Dictionary unpacking is not supported yet: - - ```mojo - fn takes_dict(d: Dict[String, Int]): - print_nicely(**d) # Not supported yet. - ``` - - - Variadic keyword _parameters_ are not supported yet: - - ```mojo - # Not supported yet. - fn var_kwparams[**kwparams: Int](): ... - ``` + For more details (and a list of current limitations), see [Variadic keyword + arguments](/mojo/manual/functions#variadic-keyword-arguments) in the Mojo + manual. #### 🦋 Changed or removed @@ -240,7 +209,9 @@ modular update mojo - The `Tensor` type now has [`argmax()`](/mojo/stdlib/tensor/tensor#argmax) and [`argmin()`](/mojo/stdlib/tensor/tensor#argmin) functions to compute the - position of the max or min value. + position of the max or min value. Note: this should return a `Tensor[Int]` + but currently the output tensor is the same type as the input tensor. This + will be fixed in a future release. - Added a new [`collections.OptionalReg`](/mojo/stdlib/collections/optional#optionalreg) @@ -375,7 +346,9 @@ modular update mojo on the command line, using the `-I` option, or you can add them to the `mojo.lsp.includeDirs` setting in the VS Code extension. -### ❌ Removed +### Other changes + +#### ❌ Removed - The `__get_address_as_lvalue` magic function has been removed. You can now get an LValue from a `Pointer` or `Reference` by using the dereference @@ -406,13 +379,26 @@ modular update mojo memcpy(destBuffer.data, srcBuffer.data, count) ``` +- The `simd_load()` and `simd_store()` methods on + [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and + [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been removed in favor + of `load()` and `store()`: + + ```mojo + # Doesn't work + my_simd = my_buffer.simd_load[simd_width](index) + # Works + my_simd = my_buffer.load[simd_width, alignment](index) + ``` + - The functions `max_or_inf()`, `min_or_neginf()` have been removed from `math.limit`. These functions were only used by the SIMD type. - As mentioned previously, the `print_no_newline()` function has been removed. Please use `print(end="")` instead. -### 🛠️ Fixed +#### 🛠️ Fixed - [#1362](https://github.com/modularml/mojo/issues/1362) - Parameter inference now recursively matches function types. diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index cd145d97f5..a29f6061dc 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -312,24 +312,17 @@ "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", "Mojo in the future.\n", "\n", - ":::note No variadic keyword-only arguments\n", - "\n", - "Variadic keyword-only arguments (`**kwargs`) are not yet supported in Mojo. We\n", - "plan to support these in the future.\n", - "\n", - ":::\n", - "\n", - "Inside the function, the variadic argument is projected into an iterable list\n", + "Inside the function body, the variadic argument is available an iterable list\n", "for ease of use. But there are some differences in handling the list depending\n", "on whether the arguments are register-passable types (such as `Int`) or\n", "memory-only types (such as `String`).\n", "\n", - "Register-passable types, such as `Int`, are projected into a \n", + "Register-passable types, such as `Int`, are available as a \n", "[`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", "loop.\n", "\n", - "Memory-only types, such as `String`, are projected into a \n", + "Memory-only types, such as `String`, are available as a \n", "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", "[`Reference`](/mojo/stdlib/memory/unsafe#reference) for each value instead\n", @@ -373,12 +366,83 @@ "\n", "Mojo [parameters](/mojo/manual/parameters/) are distinct from arguments\n", "(parameters are used for compile-time metaprogramming). However, most rules\n", - "that apply to argument lists also apply to parameter lists, including variadics.\n", - "Variadic parameters are declared and used like variadic arguments.\n", + "that apply to argument lists also apply to parameter lists. Variadic parameters\n", + "are supported, but with some limitations—for details see \n", + "[variadic parameters](/mojo/manual/parameters/#variadic-parameters).\n", "\n", ":::\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Variadic keyword arguments\n", + "\n", + "Mojo functions also support variadic keyword arguments (`**kwargs`). Variadic\n", + "keyword arguments allow the user to pass an arbitrary number of keyword\n", + "arguments. To define a function that takes a variadic keyword argument, use the\n", + "variadic keyword argument syntax **kw_argument_name:\n", + "\n", + " ```mojo\n", + " fn print_nicely(**kwargs: Int) raises:\n", + " for key in kwargs.keys():\n", + " print(key[], \"=\", kwargs[key[]])\n", + "\n", + " # prints:\n", + " # `a = 7`\n", + " # `y = 8`\n", + " print_nicely(a=7, y=8)\n", + " ```\n", + "\n", + " In this example, the argument name `kwargs` is a placeholder that accepts any\n", + " number of keyword arguments. Inside the body of the function, you can access\n", + " the arguments as a [`Dict`](/mojo/stdlib/collections/dict) of keywords and\n", + " argument values.\n", + " \n", + " There are currently a few limitations:\n", + "\n", + " - Variadic keyword arguments are always implicitly treated as if they\n", + " were declared with the `owned` [argument \n", + " convention](/mojo/manual/values/ownership.html#argument-conventions), and\n", + " can't be declared otherwise:\n", + "\n", + " ```mojo\n", + " # Not supported yet.\n", + " fn borrowed_var_kwargs(borrowed **kwargs: Int): ...\n", + " ```\n", + "\n", + " - All the variadic keyword arguments must have the same type, and this\n", + " determines the type of the argument dictionary. For example, if the argument\n", + " is `**kwargs: Float64` then the argument dictionary will be a \n", + " `Dict[String, Float64]`.\n", + "\n", + " - Functions with variadic keyword arguments can't have default values for\n", + " keyword-only arguments. For example:\n", + "\n", + " ```mojo\n", + " # Not allowed yet, because `b` is keyword-only with a default.\n", + " fn not_yet(*, b: Int = 9, **kwargs: Int): ...\n", + "\n", + " # Okay, because `c` is positional-or-keyword, so it can have a default.\n", + " fn still_works(c: Int = 5, **kwargs: Int): ...\n", + " ```\n", + "\n", + " - Dictionary unpacking is not supported yet:\n", + "\n", + " ```mojo\n", + " fn takes_dict(d: Dict[String, Int]):\n", + " print_nicely(**d) # Not supported yet.\n", + " ```\n", + "\n", + " - Variadic keyword _parameters_ are not supported yet:\n", + "\n", + " ```mojo\n", + " # Not supported yet.\n", + " fn var_kwparams[**kwparams: Int](): ...\n", + " ```" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index d644bf3741..77704d5d60 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -127,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -193,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -291,7 +291,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -351,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -435,7 +435,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -484,7 +484,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -546,7 +546,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -578,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -626,8 +626,6 @@ "Mojo supports positional-only and keyword-only parameters, following the same\n", "rules as [positional-only and keyword-only\n", "arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).\n", - "As with arguments, variadic keyword arguments (for example, `**kwparams`) are\n", - "not supported yet.\n", "\n", ":::" ] @@ -638,13 +636,13 @@ "source": [ "## Variadic parameters\n", "\n", - "Mojo also supports variadic parameters, following the same rules described for \n", + "Mojo also supports variadic parameters, similar to \n", "[Variadic arguments](/mojo/manual/functions#variadic-arguments):" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -656,7 +654,33 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As with arguments, variadic keyword parameters (for example, `**kwparams`) are\n", + "Variadic parameters have some limitations that variadic arguments don't have:\n", + "\n", + "- Only variadic parameters of register-passable types are supported currently.\n", + "\n", + "- The parameters aren't automatically projected into a `VariadicList`, so you\n", + " need to construct the list explicitly:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn sum_params[*values: Int]() -> Int:\n", + " alias list = VariadicList(values)\n", + " var sum = 0\n", + " for v in list:\n", + " sum += v\n", + " return sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Variadic keyword parameters (for example, `**kwparams`) are\n", "not supported yet." ] }, @@ -846,6 +870,18 @@ " func(i)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "attachments": {}, "cell_type": "markdown", @@ -986,21 +1022,13 @@ " ```\n", "\n", "You can also use the star-underscore expression `*_` to unbind an arbitrary\n", - "number of positional parameters at the start, middle, or end of a parameter\n", + "number of positional parameters at the end of a parameter\n", "list.\n", "\n", "```mojo\n", "# These two types are equivalent\n", "MyType[\"Hello\", *_]\n", "MyType[\"Hello\", _, _, _]\n", - "\n", - "# These two types are equivalent\n", - "MyType[*_, False]\n", - "MyType[_, _, _, False]\n", - "\n", - "# These two types are equivalent\n", - "MyType[*_]\n", - "MyType[_, _, _, _]\n", "```\n", "\n", "When a parameter is explicitly unbound with the `_` or `*_` expression, you\n", From bff74b6f45348ad5c98f3e1f8ca587dfdeb22a21 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 27 Mar 2024 21:02:34 -0700 Subject: [PATCH 0043/2019] [docs] Fix docsite build (#36081) Docsite began to fail becuase it attempted to read lines from this code block that is empty. Clearly a mistake to have an empty code cell, but also added a code check for this: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: ef45fcea4a5f331486cacc4d6d05c4d6e06d02a7 --- docs/manual/parameters/index.ipynb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 77704d5d60..31a4480582 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -870,18 +870,6 @@ " func(i)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "attachments": {}, "cell_type": "markdown", From a0152b4116cc1e7e310883477e00c95e4aff2035 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Thu, 28 Mar 2024 10:07:53 -0700 Subject: [PATCH 0044/2019] [Docs] Update load()/store() item and move it to top of removed section. (#36110) MODULAR_ORIG_COMMIT_REV_ID: 038e34dca15cff2a2bdb22285f6d90628737a66d --- docs/changelog-released.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index bdd2bc5bbf..0b83b56536 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -224,6 +224,25 @@ modular update mojo #### 🦋 Changed +- The `simd_load()`, `simd_store()`, `aligned_simd_load()`, and + `aligned_simd_store()` methods on + [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), + [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and + [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been merged into + a more expressive set of `load()` and `store()` methods with keyword-only + `width` and `alignment` parameters: + + ```mojo + # Doesn't work + my_simd = my_buffer.simd_load[simd_width](index) + # Works + my_simd = my_buffer.load[width=simd_width](index) + # Doesn't work + my_buffer.aligned_simd_store[width, alignment](my_simd) + # Works + my_buffer.store[width=width, alignment=alignment](my_simd) + ``` + - The [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable#equalitycomparable) trait now requires the `__ne__()` method for conformance in addition to the @@ -379,19 +398,6 @@ modular update mojo memcpy(destBuffer.data, srcBuffer.data, count) ``` -- The `simd_load()` and `simd_store()` methods on - [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and - [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been removed in favor - of `load()` and `store()`: - - ```mojo - # Doesn't work - my_simd = my_buffer.simd_load[simd_width](index) - # Works - my_simd = my_buffer.load[simd_width, alignment](index) - ``` - - The functions `max_or_inf()`, `min_or_neginf()` have been removed from `math.limit`. These functions were only used by the SIMD type. From 8830d929e1107b9a03c8d0f5620e0c65492a07e4 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 28 Mar 2024 14:10:06 -0700 Subject: [PATCH 0045/2019] [mojo-stdlib] Move a few more regpassable inits to 'inout self'. (#36150) This just cleans up a few more of these in builtin_list. MODULAR_ORIG_COMMIT_REV_ID: 0f88978e8005a0e0aae4c2e279b57cbca8b101c7 --- stdlib/src/builtin/builtin_list.mojo | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 200e79d97b..f37890a35a 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -36,16 +36,13 @@ struct ListLiteral[*Ts: AnyRegType](Sized): """The underlying storage for the list.""" @always_inline("nodebug") - fn __init__(*args: *Ts) -> Self: + fn __init__(inout self, *args: *Ts): """Construct the list literal from the given values. Args: args: The init values. - - Returns: - The constructed ListLiteral. """ - return Self {storage: args} + self.storage = args @always_inline("nodebug") fn __len__(self) -> Int: @@ -113,29 +110,23 @@ struct VariadicList[type: AnyRegType](Sized): alias IterType = _VariadicListIter[type] @always_inline - fn __init__(*value: type) -> Self: + fn __init__(inout self, *value: type): """Constructs a VariadicList from a variadic list of arguments. Args: value: The variadic argument list to construct the variadic list with. - - Returns: - The VariadicList constructed. """ - return value + self = value @always_inline - fn __init__(value: Self.storage_type) -> Self: + fn __init__(inout self, value: Self.storage_type): """Constructs a VariadicList from a variadic argument type. Args: value: The variadic argument to construct the list with. - - Returns: - The VariadicList constructed. """ - return Self {value: value} + self.value = value @always_inline fn __len__(self) -> Int: From 88fd1daf3c103633e03e5bfe844f0f2af589f46d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 28 Mar 2024 15:20:32 -0700 Subject: [PATCH 0046/2019] [mojo-lang] Change mlir-ification of values of trait type. (#36166) This changes substituteMLIRMagic to include the trait type in the expanded string for a value of trait type in a KGEN type context. This makes it easier and less error prone to work with trait values. If there is a reason to get the raw type without a preceding type, then you can use the unary plus hack like for other attribute values. MODULAR_ORIG_COMMIT_REV_ID: 09c8a037b73d016d139e8391bb37d31c943ed9b4 --- stdlib/src/memory/anypointer.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 8 ++------ stdlib/src/utils/variant.mojo | 4 +--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index ba23dc84c3..251f1275a4 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -36,7 +36,7 @@ struct AnyPointer[T: Movable]( T: The pointer element type, which must be movable. """ - alias pointer_type = __mlir_type[`!kgen.pointer<:`, Movable, ` `, T, `>`] + alias pointer_type = __mlir_type[`!kgen.pointer<`, T, `>`] """The underlying pointer type.""" var value: Self.pointer_type """The underlying pointer.""" diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 817ae838b5..c5a0540482 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -435,9 +435,7 @@ struct _LITRef[ addr_space: __mlir_type.index = Int(0).__mlir_index__(), ]: alias type = __mlir_type[ - `!lit.ref<:`, - AnyType, - ` `, + `!lit.ref<`, element_type, `, `, lifetime, @@ -538,9 +536,7 @@ struct Reference[ # to KGEN pointer. var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[ - `!kgen.pointer<:`, AnyType, ` `, new_element_type, `>` - ] + _type = __mlir_type[`!kgen.pointer<`, new_element_type, `>`] ](kgen_ptr) return __mlir_op.`lit.ref.from_pointer`[ _type = _LITRef[new_element_type, is_mutable, lifetime].type diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 4677f12ae1..66c473cfab 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -154,9 +154,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): var ptr = Reference(self._impl).get_unsafe_pointer().address var result = AnyPointer[T]() result.value = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[ - `!kgen.pointer<:`, CollectionElement, ` `, T, `>` - ] + _type = __mlir_type[`!kgen.pointer<`, T, `>`] ](ptr) return result From d50cca2f76b5a21f895075d8c89632e3545a0617 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Thu, 28 Mar 2024 18:01:22 -0600 Subject: [PATCH 0047/2019] [mojo] Error out on defining `main` in a package (#36062) The semantics of this are not well defined for mojo right now, so it's cleaner to just disallow it. We can loosen this up when we have cleaner semantics around entry points in packages. Closes https://github.com/modularml/mojo/issues/1987 MODULAR_ORIG_COMMIT_REV_ID: 3203283b41bfd984cd183ba1a314c0c3ba13925d --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 1c1d94a945..c7854c4ce9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -32,3 +32,7 @@ and tools. Please add any significant user-visible changes here. ### ❌ Removed ### 🛠️ Fixed + +- [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` + in a Mojo package is an error, for now. This is not intended to work yet, + erroring for now will help to prevent accidental undefined behavior. From 76cfc586016716f8c68fbb1a4f94e65ceeb0d7d5 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Thu, 28 Mar 2024 18:02:52 -0600 Subject: [PATCH 0048/2019] [mojo-stdlib] Remove always_inline from debug_assert (#36065) This was required originally because of issues related to packaging, but with the removal of separate package codegen, this is no longer necessary. Closes #24957 MODULAR_ORIG_COMMIT_REV_ID: 79be2c2c74f2aefac95625115a8b7baf291c25ad --- stdlib/src/builtin/debug_assert.mojo | 3 --- 1 file changed, 3 deletions(-) diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index dd4ca9b885..454bf42c64 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -22,7 +22,6 @@ from sys.info import triple_is_nvidia_cuda from sys.param_env import is_defined -@always_inline fn debug_assert(cond: Bool, msg: StringLiteral): """Asserts that the condition is true. @@ -40,7 +39,6 @@ fn debug_assert(cond: Bool, msg: StringLiteral): _debug_assert_impl(cond, msg) -@always_inline fn debug_assert[boolable: Boolable](cond: boolable, msg: StringLiteral): """Asserts that the condition is true. @@ -61,7 +59,6 @@ fn debug_assert[boolable: Boolable](cond: boolable, msg: StringLiteral): _debug_assert_impl(cond, msg) -@always_inline fn _debug_assert_impl[boolable: Boolable](cond: boolable, msg: StringLiteral): """Asserts that the condition is true.""" From adbc89b5b8aee5630176d8401db95c0c8ceaa0ac Mon Sep 17 00:00:00 2001 From: River Riddle Date: Thu, 28 Mar 2024 18:09:11 -0600 Subject: [PATCH 0049/2019] [mojo-driver] Add a `-g` option that aliases `--debug-level full` (#36069) This provides a more familiar way to enable full debugging that matches many other languages. Closes #24171 MODULAR_ORIG_COMMIT_REV_ID: 7d9038b935bbf86b775237a33b0cb294798200af --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c7854c4ce9..d4b11565ad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -27,6 +27,10 @@ and tools. Please add any significant user-visible changes here. `2x2` tensor which is initialized with all zeros. This provides an easy way to fill the data of a tensor. +- The `mojo build` and `mojo run` commands now support a `-g` option. This + shorter alias is equivalent to writing `--debug-level full`. This option is + also available in the `mojo debug` command, but is already the default. + ### 🦋 Changed ### ❌ Removed From 6779126bb251e7b27c2054dc8d0e6bd104102756 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 29 Mar 2024 07:00:27 -0700 Subject: [PATCH 0050/2019] [Stdlib] Reconcile the div_ceil and ceildiv functions (#36203) We only need a single one of these, so just remove the div_ceil function in place of the ceildiv one (since the ceildiv is what it's called in Python). Closes #33323 MODULAR_ORIG_COMMIT_REV_ID: 627e5eef1aa20d7c03f0330ad608b2dbbe24a945 --- docs/changelog.md | 4 ++++ stdlib/src/builtin/hash.mojo | 4 ++-- stdlib/src/builtin/io.mojo | 4 ++-- stdlib/src/builtin/range.mojo | 4 ++-- stdlib/src/builtin/tuple.mojo | 4 ++-- stdlib/src/utils/stringref.mojo | 4 ++-- stdlib/src/utils/variant.mojo | 4 ++-- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d4b11565ad..dad07f2a45 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -31,6 +31,10 @@ and tools. Please add any significant user-visible changes here. shorter alias is equivalent to writing `--debug-level full`. This option is also available in the `mojo debug` command, but is already the default. +- Due to an oversight there was a `ceildiv` and `div_ceil` function in the + `math` module. These two functions have been reconciled with the `div_ceil` + being removed. + ### 🦋 Changed ### ❌ Removed diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index de4624a9fc..c5db156fff 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -36,7 +36,7 @@ from memory import memcpy, memset_zero, stack_allocation @always_inline -fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: +fn _ceildiv_positive(numerator: Int, denominator: Int) -> Int: return (numerator + denominator - 1)._positive_div(denominator) @@ -149,7 +149,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: hash collision statistical properties for common data structures. """ # Some types will have non-integer ratios, eg. DType.bool - alias int8_size = _div_ceil_positive( + alias int8_size = _ceildiv_positive( type.bitwidth(), DType.uint8.bitwidth() ) * size # Stack allocate bytes for `data` and load it into that memory. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index f8602a4810..ca95071121 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -30,8 +30,8 @@ from utils import StringRef, unroll @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment + var ceildiv = (value + alignment - 1)._positive_div(alignment) + return ceildiv * alignment # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 1d49d688eb..f4c817916d 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -24,7 +24,7 @@ from python.object import PythonObject @always_inline -fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: +fn _ceildiv_positive(numerator: Int, denominator: Int) -> Int: """Divides an integer by another integer, and round up to the nearest integer. @@ -161,7 +161,7 @@ struct _StridedRange(Sized): @always_inline("nodebug") fn __len__(self) -> Int: - return _div_ceil_positive(_abs(self.start - self.end), _abs(self.step)) + return _ceildiv_positive(_abs(self.start - self.end), _abs(self.step)) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 1c94d58ad4..dd97e4779e 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -103,5 +103,5 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment + var ceildiv = (value + alignment - 1)._positive_div(alignment) + return ceildiv * alignment diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0fda1fa47e..bb9136cb50 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -25,8 +25,8 @@ from memory.unsafe import DTypePointer, Pointer @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment + var ceildiv = (value + alignment - 1)._positive_div(alignment) + return ceildiv * alignment @always_inline diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 66c473cfab..0a657c7b0e 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -53,8 +53,8 @@ from utils.static_tuple import StaticTuple @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment + var ceildiv = (value + alignment - 1)._positive_div(alignment) + return ceildiv * alignment @always_inline From c90401608deadf31e90945f27cddd5481dc7a464 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 29 Mar 2024 09:23:44 -0500 Subject: [PATCH 0051/2019] [mojo-examples] Fix incorrect logic in vectorize loop (#36196) The logic was incorrect if there was a remainder from C.cols % nelts MODULAR_ORIG_COMMIT_REV_ID: 347157c2401cdacd24f97f1a42027245438e168d --- examples/notebooks/Matmul.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/Matmul.ipynb b/examples/notebooks/Matmul.ipynb index 12483029b7..eff5e1f81d 100644 --- a/examples/notebooks/Matmul.ipynb +++ b/examples/notebooks/Matmul.ipynb @@ -589,7 +589,7 @@ "fn matmul_vectorized_0(C: Matrix, A: Matrix, B: Matrix):\n", " for m in range(C.rows):\n", " for k in range(A.cols):\n", - " for nv in range(0, C.cols, nelts):\n", + " for nv in range(0, C.cols - nelts + 1, nelts):\n", " C.store(m, nv, C.load[nelts](m, nv) + A[m, k] * B.load[nelts](k, nv))\n", "\n", " # Handle remaining elements with scalars.\n", From d7031f11b14f58ef0524bd42ac7088fbf2d41958 Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Fri, 29 Mar 2024 11:51:58 -0400 Subject: [PATCH 0052/2019] [CHANGELOG] Update for `mojo build` change (#36210) Update the changelog to reflect #36207, since it represents a slight change in the `mojo` tool's behavior. MODULAR_ORIG_COMMIT_REV_ID: 0e954f4d4768c3674dfd4f8989f67050bec140cf --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index dad07f2a45..89683816a2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -37,6 +37,11 @@ and tools. Please add any significant user-visible changes here. ### 🦋 Changed +- The behavior of `mojo build` when invoked without an output `-o` argument has + changed slightly: `mojo build ./test-dir/program.mojo` now outputs an + executable to the path `./program`, whereas before it would output to the path + `./test-dir/program`. + ### ❌ Removed ### 🛠️ Fixed From 76f9e050e7fc0b2257bae67d62d87882d81c8bce Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 29 Mar 2024 11:05:18 -0700 Subject: [PATCH 0053/2019] [docs] Cross-link the vision and roadmap docs (#36142) Maybe that wasn't the doc you were looking for. MODULAR_ORIG_COMMIT_REV_ID: fb7d62c22be4e0edf05c49e4624c9fa4a1f880f8 --- stdlib/docs/roadmap.md | 2 ++ stdlib/docs/vision.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/stdlib/docs/roadmap.md b/stdlib/docs/roadmap.md index 3ff96a1c24..7e05f86f0d 100644 --- a/stdlib/docs/roadmap.md +++ b/stdlib/docs/roadmap.md @@ -7,6 +7,8 @@ Modular's internal workflows. The roadmap updates act as a forcing function for discussions with the Mojo community to ensure the standard library contributors both internally and externally are aligned on the future technical direction. +For more about our long-term aspirations, check out our [Vision doc](vision.md). + ## 2024 Q2+ roadmap The following are high-level themes the Mojo standard library team will be diff --git a/stdlib/docs/vision.md b/stdlib/docs/vision.md index a015be8c79..b335e239af 100644 --- a/stdlib/docs/vision.md +++ b/stdlib/docs/vision.md @@ -3,6 +3,8 @@ This page outlines the principles we aspire to follow and the more concrete objectives and goals we aim to accomplish in the Mojo standard library. +For details about our near-term objectives, see the [Roadmap doc](roadmap.md). + ## Principles The following are “North Star” principles for the Mojo standard library. From dca412f770c6f4c4f4bc62cd006604711a917af7 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 29 Mar 2024 15:08:11 -0700 Subject: [PATCH 0054/2019] Revert "[Stdlib] Reconcile the div_ceil and ceildiv functions" (#36256) Breaks some pytorch model verification. To repro do ``` pytest -svv PyTorch/integration-test/python/pytorch_op_tests/test_ops_jit.py -k test_variant_consistency_jit_bucketize_cpu_float32 ``` Reverts modularml/modular#36203 MODULAR_ORIG_COMMIT_REV_ID: 44d3a3560edfbd4510e7a906d895a5b01c04b606 --- docs/changelog.md | 4 ---- stdlib/src/builtin/hash.mojo | 4 ++-- stdlib/src/builtin/io.mojo | 4 ++-- stdlib/src/builtin/range.mojo | 4 ++-- stdlib/src/builtin/tuple.mojo | 4 ++-- stdlib/src/utils/stringref.mojo | 4 ++-- stdlib/src/utils/variant.mojo | 4 ++-- 7 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 89683816a2..a0033f6c9b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -31,10 +31,6 @@ and tools. Please add any significant user-visible changes here. shorter alias is equivalent to writing `--debug-level full`. This option is also available in the `mojo debug` command, but is already the default. -- Due to an oversight there was a `ceildiv` and `div_ceil` function in the - `math` module. These two functions have been reconciled with the `div_ceil` - being removed. - ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index c5db156fff..de4624a9fc 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -36,7 +36,7 @@ from memory import memcpy, memset_zero, stack_allocation @always_inline -fn _ceildiv_positive(numerator: Int, denominator: Int) -> Int: +fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: return (numerator + denominator - 1)._positive_div(denominator) @@ -149,7 +149,7 @@ fn _hash_simd[type: DType, size: Int](data: SIMD[type, size]) -> Int: hash collision statistical properties for common data structures. """ # Some types will have non-integer ratios, eg. DType.bool - alias int8_size = _ceildiv_positive( + alias int8_size = _div_ceil_positive( type.bitwidth(), DType.uint8.bitwidth() ) * size # Stack allocate bytes for `data` and load it into that memory. diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index ca95071121..f8602a4810 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -30,8 +30,8 @@ from utils import StringRef, unroll @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var ceildiv = (value + alignment - 1)._positive_div(alignment) - return ceildiv * alignment + var div_ceil = (value + alignment - 1)._positive_div(alignment) + return div_ceil * alignment # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index f4c817916d..1d49d688eb 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -24,7 +24,7 @@ from python.object import PythonObject @always_inline -fn _ceildiv_positive(numerator: Int, denominator: Int) -> Int: +fn _div_ceil_positive(numerator: Int, denominator: Int) -> Int: """Divides an integer by another integer, and round up to the nearest integer. @@ -161,7 +161,7 @@ struct _StridedRange(Sized): @always_inline("nodebug") fn __len__(self) -> Int: - return _ceildiv_positive(_abs(self.start - self.end), _abs(self.step)) + return _div_ceil_positive(_abs(self.start - self.end), _abs(self.step)) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index dd97e4779e..1c94d58ad4 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -103,5 +103,5 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var ceildiv = (value + alignment - 1)._positive_div(alignment) - return ceildiv * alignment + var div_ceil = (value + alignment - 1)._positive_div(alignment) + return div_ceil * alignment diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index bb9136cb50..0fda1fa47e 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -25,8 +25,8 @@ from memory.unsafe import DTypePointer, Pointer @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var ceildiv = (value + alignment - 1)._positive_div(alignment) - return ceildiv * alignment + var div_ceil = (value + alignment - 1)._positive_div(alignment) + return div_ceil * alignment @always_inline diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 0a657c7b0e..66c473cfab 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -53,8 +53,8 @@ from utils.static_tuple import StaticTuple @always_inline fn _align_up(value: Int, alignment: Int) -> Int: - var ceildiv = (value + alignment - 1)._positive_div(alignment) - return ceildiv * alignment + var div_ceil = (value + alignment - 1)._positive_div(alignment) + return div_ceil * alignment @always_inline From deb37e4fcb2a554e72306fdf48c630e56dbf01b3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 29 Mar 2024 15:59:56 -0700 Subject: [PATCH 0055/2019] [mojo-stdlib] Move DTypePointer inits to `inout Self` (#36262) This is split off of #33595 MODULAR_ORIG_COMMIT_REV_ID: d745e5673d768ab862661f0ada2adb91f34e81e2 --- stdlib/src/memory/unsafe.mojo | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index c5a0540482..ccd05eab31 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -1061,63 +1061,51 @@ struct DTypePointer[ """The pointed-to address.""" @always_inline("nodebug") - fn __init__() -> Self: - """Constructs a null `DTypePointer` from the given type. - - Returns: - Constructed `DTypePointer` object. - """ + fn __init__(inout self): + """Constructs a null `DTypePointer` from the given type.""" - return Self {address: Self.pointer_type()} + self.address = Self.pointer_type() @always_inline("nodebug") fn __init__( + inout self, value: __mlir_type[ `!kgen.pointer,`, address_space.value().value, `>`, - ] - ) -> Self: + ], + ): """Constructs a `DTypePointer` from a scalar pointer of the same type. Args: value: The scalar pointer. - - Returns: - Constructed `DTypePointer`. """ - return Pointer[ + self = Pointer[ __mlir_type[`!pop.scalar<`, type.value, `>`], address_space ](value).bitcast[Scalar[type]]() @always_inline("nodebug") - fn __init__(value: Pointer[Scalar[type], address_space]) -> Self: + fn __init__(inout self, value: Pointer[Scalar[type], address_space]): """Constructs a `DTypePointer` from a scalar pointer of the same type. Args: value: The scalar pointer. - - Returns: - Constructed `DTypePointer`. """ - return Self {address: value} + self.address = value @always_inline("nodebug") - fn __init__(value: Scalar[DType.address]) -> Self: + fn __init__(inout self, value: Scalar[DType.address]): """Constructs a `DTypePointer` from the value of scalar address. Args: value: The input pointer index. - - Returns: - Constructed `DTypePointer` object. """ var address = __mlir_op.`pop.index_to_pointer`[ _type = Self.pointer_type.pointer_type ](value.cast[DType.index]().value) - return Self {address: address} + self.address = address @staticmethod @always_inline("nodebug") From c9f5b57355af3bf6b8fb4da7f99294c9ed6e3a6a Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Sat, 30 Mar 2024 11:03:07 -0700 Subject: [PATCH 0056/2019] [mojo][repl] Fix erroneous destructor call emission in generated REPL source (#35595) The REPL is doing dirty tricks with variables, turning them into heap allocations and tracking them in indirect structs. However, CheckLifetimes is onto its tricks, which causes it to reject these as incorrect values. The right solution is to make more invasive changes to the REPL, but that isn't in the short term plans. Workaround this by introducing a horrible hack (tm) op `RefFromPointerUntrackedOp` that creates a reference that isn't tracked by CheckLifetimes. This pokes a hole in our nice reference system which makes me very sad. A comment saying "don't use this" will prevent other people from using this for other things ... right??? --------- Co-authored-by: Chris Lattner MODULAR_ORIG_COMMIT_REV_ID: 7a6b7339522d2657f72d7e8a3a5b51e4d299167f --- docs/changelog.md | 6 ++++++ examples/notebooks/BoolMLIR.ipynb | 3 ++- examples/notebooks/HelloMojo.ipynb | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a0033f6c9b..b6d02e059d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -37,6 +37,12 @@ and tools. Please add any significant user-visible changes here. changed slightly: `mojo build ./test-dir/program.mojo` now outputs an executable to the path `./program`, whereas before it would output to the path `./test-dir/program`. +- The REPL no longer allows type level variable declarations to be + uninitialized, e.g. it will reject `var s: String`. This is because it does + not do proper lifetime tracking (yet!) across cells, and so such code would + lead to a crash. You can work around this by initializing to a dummy value + and overwriting later. This limitation only applies to top level variables, + variables in functions work as they always have. ### ❌ Removed diff --git a/examples/notebooks/BoolMLIR.ipynb b/examples/notebooks/BoolMLIR.ipynb index a541c44c36..91613f5fe5 100644 --- a/examples/notebooks/BoolMLIR.ipynb +++ b/examples/notebooks/BoolMLIR.ipynb @@ -105,7 +105,8 @@ "metadata": {}, "outputs": [], "source": [ - "var a: OurBool" + "fn uninitialized_our_bool():\n", + " var a: OurBool\n" ] }, { diff --git a/examples/notebooks/HelloMojo.ipynb b/examples/notebooks/HelloMojo.ipynb index a60f594dd6..0159f99c22 100644 --- a/examples/notebooks/HelloMojo.ipynb +++ b/examples/notebooks/HelloMojo.ipynb @@ -65,7 +65,6 @@ } ], "source": [ - "#| XFAIL: *\n", "#| CHECK: Hello Mojo!\n", "print(\"Hello Mojo!\")" ] From e95f5fe8df5a23fa5a9dbb11cfa9f908edb45f63 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 31 Mar 2024 14:04:25 -0700 Subject: [PATCH 0057/2019] [mojo-lang] Expand `VariadicPack` to take parametric trait base (#36319) This patch is a big step towards enabling VariadicPack to model packs with a non-AnyType bound (e.g. all members must be Stringable). It expands `VariadicPack` with a new `element_trait` member that indicates what all the elements are, and teaches the parser to pass down the element type. The magic enabling this is support for metatype bound type expressions which is almost working, but not quite there yet. As such, this is another step, but isn't enough to declare success, which is why the integration test isn't using it yet. :-/ MODULAR_ORIG_COMMIT_REV_ID: d45b597eef0f58fc20770c2b7ec860216e9ecc6c --- stdlib/src/builtin/builtin_list.mojo | 40 ++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index f37890a35a..5ed799cc82 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -410,15 +410,16 @@ struct VariadicListMem[ # VariadicPack # ===----------------------------------------------------------------------===# +alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] + -# TODO: We need to genericize VariadicPack over the kinds of types it holds, -# instead of erasing them to AnyType. This would allow packs of values known to -# be Stringable for example. @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, lifetime: AnyLifetime[elt_is_mutable].type, - *element_types: AnyType, + element_trait: _AnyTypeMetaType, + *element_types: element_trait, + # TODO: Add address_space when Reference supports it. ](Sized): """A utility class to access variadic pack arguments and provide an API for doing things with them. @@ -427,12 +428,13 @@ struct VariadicPack[ elt_is_mutable: True if the elements of the list are mutable for an inout or owned argument pack. lifetime: The reference lifetime of the underlying elements. + element_trait: The trait that each element of the pack conforms to. element_types: The list of types held by the argument pack. """ alias _mlir_pack_type = __mlir_type[ `!lit.ref.pack<:variadic<`, - AnyType, + element_trait, `> `, element_types, `, `, @@ -502,7 +504,15 @@ struct VariadicPack[ fn get_element[ index: Int ](self) -> Reference[ - element_types[index.value], Self.elt_is_mutable, Self.lifetime + # FIXME: Shouldn't need a rebind here. + __mlir_attr[ + `#kgen.param.expr: `, + AnyType, + ], + Self.elt_is_mutable, + Self.lifetime, ]: """Return a reference to an element of the pack. @@ -514,8 +524,22 @@ struct VariadicPack[ mutability of the pack argument convention. """ - return __mlir_op.`lit.ref.pack.get`[index = index.value](self._value) - + return rebind[ + Reference[ + # FIXME: Shouldn't need a rebind here. + __mlir_attr[ + `#kgen.param.expr: `, + AnyType, + ], + Self.elt_is_mutable, + Self.lifetime, + ] + ](__mlir_op.`lit.ref.pack.get`[index = index.value](self._value)) + + # FIXME!: the T in the function should be element_trait bound not AnyType + # bound. @always_inline fn each[func: fn[T: AnyType] (T) -> None](self): """Apply a function to each element of the pack in order. This applies From b03a4ed6f2366e81febb2dd75a7be07b88fcf45e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 1 Apr 2024 12:36:36 -0700 Subject: [PATCH 0058/2019] [mojo-lang] Finally add support for AnyTrait param conversion. (#36362) This teaches `canConvertWithRebind` that it is safe to rebind an parametric expression of type `AnyTrait[SomeTrait]` to `SomeTrait` which gets us in the parametric types game, and eliminates some grunge from `VariadicPack` implementation. MODULAR_ORIG_COMMIT_REV_ID: c34dc6183d5853c6960e92ff538256aa3e511f29 --- stdlib/src/builtin/builtin_list.mojo | 33 +++++++++++----------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 5ed799cc82..50ab6a44f8 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -504,13 +504,7 @@ struct VariadicPack[ fn get_element[ index: Int ](self) -> Reference[ - # FIXME: Shouldn't need a rebind here. - __mlir_attr[ - `#kgen.param.expr: `, - AnyType, - ], + element_types[index.value], Self.elt_is_mutable, Self.lifetime, ]: @@ -523,20 +517,19 @@ struct VariadicPack[ A reference to the element. The Reference's mutability follows the mutability of the pack argument convention. """ + var ref_elt = __mlir_op.`lit.ref.pack.get`[index = index.value]( + self._value + ) - return rebind[ - Reference[ - # FIXME: Shouldn't need a rebind here. - __mlir_attr[ - `#kgen.param.expr: `, - AnyType, - ], - Self.elt_is_mutable, - Self.lifetime, - ] - ](__mlir_op.`lit.ref.pack.get`[index = index.value](self._value)) + # Rebind the !lit.ref to agree on the element type. This is needed + # because we're getting a low level rebind to AnyType when the + # element_types[index] expression is erased to AnyType for Reference. + alias result_ref = Reference[ + element_types[index.value], + Self.elt_is_mutable, + Self.lifetime, + ] + return rebind[result_ref.mlir_ref_type](ref_elt) # FIXME!: the T in the function should be element_trait bound not AnyType # bound. From 901df50d9c24823df9113e2a0808aeb4d7cc4ad3 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 1 Apr 2024 17:15:23 -0600 Subject: [PATCH 0059/2019] [stdlib] Use variadic initializer for `List` in tests (#36410) Update the tests to make use of the variadic initializer of `List`. Upstream commit: https://github.com/modularml/mojo/commit/cbcb4b015b922df3dc56c1144d70d24518cf1521 MODULAR_ORIG_COMMIT_REV_ID: eb3d9197014a73fef737574578429b68bed10808 --- stdlib/test/collections/test_list.mojo | 5 +---- stdlib/test/utils/issue_13632.mojo | 7 +------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 1b11735dbf..c634af2570 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -130,10 +130,7 @@ def test_list_reverse(): # Test reversing the list ["one", "two", "three"] # - vec2 = List[String]() - vec2.append("one") - vec2.append("two") - vec2.append("three") + vec2 = List[String]("one", "two", "three") assert_equal(len(vec2), 3) assert_equal(vec2[0], "one") diff --git a/stdlib/test/utils/issue_13632.mojo b/stdlib/test/utils/issue_13632.mojo index acf4117543..5299211e89 100644 --- a/stdlib/test/utils/issue_13632.mojo +++ b/stdlib/test/utils/issue_13632.mojo @@ -23,12 +23,7 @@ fn sum_items(data: List[Int8]) -> Int: fn make_abcd_vector() -> List[Int8]: - var v = List[Int8]() - v.append(97) - v.append(98) - v.append(99) - v.append(100) - return v + return List[Int8](97, 98, 99, 100) fn main(): From 4e0b3a488702606b06bd4de619fd8e097cb7b09f Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 1 Apr 2024 16:48:00 -0700 Subject: [PATCH 0060/2019] [KGEN] Delete `POC::VariadicSize` (#36239) This was introduced as part of the previous attempt at managing autotuning (parameter-based variadic construction to feed into `kgen.param.fork`). This had one use in the standard library, but it is redundant with `pop.variadic.size`, so it's been replaced. MODULAR_ORIG_COMMIT_REV_ID: 2d1553790e03d2cc2ee5e24fe840b71728db155f --- stdlib/src/builtin/builtin_list.mojo | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 50ab6a44f8..850dd06a4c 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -487,9 +487,15 @@ struct VariadicPack[ Returns: The number of elements in the variadic pack. """ - return __mlir_attr[ - `#kgen.param.expr : index` - ] + + @parameter + fn variadic_size( + x: __mlir_type[`!kgen.variadic<`, AnyType, `>`] + ) -> Int: + return __mlir_op.`pop.variadic.size`(x) + + alias result = variadic_size(element_types) + return result @always_inline fn __len__(self) -> Int: From 7e5859611c9e2b8f04da6c0f8d98f8b8c62ecdf7 Mon Sep 17 00:00:00 2001 From: Jeff Niu Date: Mon, 1 Apr 2024 17:30:43 -0700 Subject: [PATCH 0061/2019] [mojo-stdlib] Hotfix merge conflict in main This fixes a merge conflict due to the change from VariadicPack working with general element types. MODULAR_ORIG_COMMIT_REV_ID: f62bb0a10e06107271ebf4dfa9e2db968bee4333 --- stdlib/src/builtin/builtin_list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 850dd06a4c..cc0bf0fc5a 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -490,7 +490,7 @@ struct VariadicPack[ @parameter fn variadic_size( - x: __mlir_type[`!kgen.variadic<`, AnyType, `>`] + x: __mlir_type[`!kgen.variadic<`, element_trait, `>`] ) -> Int: return __mlir_op.`pop.variadic.size`(x) From d565cb2913c29b84f730b2d916c0e71c6bd58b75 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Apr 2024 09:18:40 -0400 Subject: [PATCH 0062/2019] [mojo-stdlib] Make `PythonObject` conform to `KeyElement` (#36354) To do this, some dunder methods needed to be changed so that they don't raise (but abort instead) until `raises`-ness of functions can be parametric. This patch also adds a bit of glue to allow `Dict` to be used conveniently instead of a Python dictionary. MODULAR_ORIG_COMMIT_REV_ID: 612718161d13c7a074e0f1f98962788b8b0828da --- stdlib/src/python/_cpython.mojo | 5 +++ stdlib/src/python/object.mojo | 45 +++++++++++++++++++--- stdlib/test/python/test_python_object.mojo | 17 ++++++-- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 35ad5e6a9b..a3d5425025 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -453,6 +453,11 @@ struct CPython: ) ) + fn PyObject_Hash(inout self, obj: PyObjectPtr) -> Int: + return int( + self.lib.get_function[fn (PyObjectPtr) -> Int]("PyObject_Hash")(obj) + ) + fn PyTuple_New(inout self, count: Int) -> PyObjectPtr: var r = self.lib.get_function[fn (Int) -> PyObjectPtr]( StringRef("PyTuple_New") diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 36461bcb95..4a1082a2cb 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -101,7 +101,7 @@ struct _PyIter(Sized): @register_passable struct PythonObject( - Intable, Stringable, SizedRaising, Boolable, CollectionElement + Intable, Stringable, SizedRaising, Boolable, CollectionElement, KeyElement ): """A Python object.""" @@ -295,6 +295,19 @@ struct PythonObject( unroll[fill, len(types)]() + fn __init__(inout self, value: Dict[Self, Self]): + """Initialize the object from a dictionary of PythonObjects. + + Args: + value: The dictionary value. + """ + var cpython = _get_global_python_itf().cpython() + self.py_object = cpython.PyDict_New() + for entry in value.items(): + var result = cpython.PyDict_SetItem( + self.py_object, entry[].key.py_object, entry[].value.py_object + ) + fn __copyinit__(inout self, existing: Self): """Copy the object. @@ -413,6 +426,18 @@ struct PythonObject( raise Error("object has no len()") return result + fn __hash__(self) -> Int: + """Returns the length of the object. + + Returns: + The length of the object. + """ + var cpython = _get_global_python_itf().cpython() + var result = cpython.PyObject_Length(self.py_object) + # TODO: make this function raise when we can raise parametrically. + debug_assert(result != -1, "object is not hashable") + return result + fn __getitem__(self, *args: PythonObject) raises -> PythonObject: """Return the value for the given key or keys. @@ -953,7 +978,7 @@ struct PythonObject( """ return self._call_single_arg_method("__ge__", rhs) - fn __eq__(self, rhs: PythonObject) raises -> PythonObject: + fn __eq__(self, rhs: PythonObject) -> Bool: """Equality comparator. This compares the elements of strings and lists. Args: @@ -962,9 +987,14 @@ struct PythonObject( Returns: True if the objects are equal. """ - return self._call_single_arg_method("__eq__", rhs) + # TODO: make this function raise when we can raise parametrically. + try: + return self._call_single_arg_method("__eq__", rhs).__bool__() + except e: + debug_assert(False, "object doesn't implement __eq__") + return False - fn __ne__(self, rhs: PythonObject) raises -> PythonObject: + fn __ne__(self, rhs: PythonObject) -> Bool: """Inequality comparator. This compares the elements of strings and lists. @@ -974,7 +1004,12 @@ struct PythonObject( Returns: True if the objects are not equal. """ - return self._call_single_arg_method("__ne__", rhs) + # TODO: make this function raise when we can raise parametrically. + try: + return self._call_single_arg_method("__ne__", rhs).__bool__() + except e: + debug_assert(False, "object doesn't implement __eq__") + return False fn __pos__(self) raises -> PythonObject: """Positive. diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 16b9025e0d..e2243f1ab2 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -430,14 +430,14 @@ def test_is(): # CHECK-LABEL: test_iter -fn test_iter() raises -> None: +fn test_iter() raises: print("=== test_iter ===") - var list: PythonObject = ["apple", "orange", "banana"] + var list_obj: PythonObject = ["apple", "orange", "banana"] # CHECK: I like to eat apple # CHECK: I like to eat orange # CHECK: I like to eat banana - for fruit in list: + for fruit in list_obj: print("I like to eat", fruit) var list2: PythonObject = [] @@ -453,6 +453,16 @@ fn test_iter() raises -> None: assert_false(True) +fn test_dict() raises: + var d = Dict[PythonObject, PythonObject]() + d["food"] = 123 + d["fries"] = "yes" + + var dd = PythonObject(d) + # CHECK: {'food': 123, 'fries': 'yes'} + print(dd) + + def main(): # initializing Python instance calls init_python var python = Python() @@ -463,3 +473,4 @@ def main(): test_len() test_is() test_iter() + test_dict() From d88c649f468d769a771f0a518184ec4009f7d020 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 2 Apr 2024 08:09:00 -0600 Subject: [PATCH 0063/2019] [stdlib] Add `String.removeprefix` and `String.removesuffix` (#36411) The two methods `removeprefix` and `removesuffix` are present in python. See the docs: * https://docs.python.org/3/library/stdtypes.html#str.removeprefix * https://docs.python.org/3/library/stdtypes.html#str.removesuffix Both were introduced in python 3.9. The documentation was added, the arguments were made positional-only like in CPython. Unit tests were added in the corresponding module. The docstrings were taken from the python documentation. Note: - Had minor formatting fixes to please the internal doc-string validation since that is not yet enabled on the public CI. Upstream commit: https://github.com/modularml/mojo/commit/b5fdb38f4e3e12f9da0934e2746474e69783d1f0 MODULAR_ORIG_COMMIT_REV_ID: 16a471fe050c17a236f5086adb092622aa7ac0ff --- stdlib/src/builtin/string.mojo | 42 ++++++++++++++++++++++++++++ stdlib/test/builtin/test_string.mojo | 16 +++++++++++ 2 files changed, 58 insertions(+) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index ad35249844..280c0137dd 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -999,6 +999,48 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): return self._endswith_impl(suffix, start) return self[start:end]._endswith_impl(suffix) + fn removeprefix(self, prefix: String, /) -> String: + """If the string starts with the prefix string, return `string[len(prefix):]`. + Otherwise, return a copy of the original string. + + ```mojo + print(String('TestHook').removeprefix('Test')) + # 'Hook' + print(String('BaseTestCase').removeprefix('Test')) + # 'BaseTestCase' + ``` + + Args: + prefix: The prefix to remove from the string. + + Returns: + A new string with the prefix removed if it was present. + """ + if self.startswith(prefix): + return self[len(prefix) :] + return self + + fn removesuffix(self, suffix: String, /) -> String: + """If the string ends with the suffix string, return `string[:-len(suffix)]`. + Otherwise, return a copy of the original string. + + ```mojo + print(String('TestHook').removesuffix('Hook')) + # 'Test' + print(String('BaseTestCase').removesuffix('Test')) + # 'BaseTestCase' + ``` + + Args: + suffix: The suffix to remove from the string. + + Returns: + A new string with the suffix removed if it was present. + """ + if self.endswith(suffix): + return self[: -len(suffix)] + return self + @always_inline fn _endswith_impl(self, suffix: String, start: Int = 0) -> Bool: return self.rfind(suffix, start) + len(suffix) == len(self) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 65297ce6ad..2cbfc5db2a 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -545,6 +545,20 @@ fn test_endswith() raises: assert_false(str.endswith("llo", 2, 3)) +def test_removeprefix(): + assert_equal(String("hello world").removeprefix("hello"), " world") + assert_equal(String("hello world").removeprefix("world"), "hello world") + assert_equal(String("hello world").removeprefix("hello world"), "") + assert_equal(String("hello world").removeprefix("llo wor"), "hello world") + + +def test_removesuffix(): + assert_equal(String("hello world").removesuffix("world"), "hello ") + assert_equal(String("hello world").removesuffix("hello"), "hello world") + assert_equal(String("hello world").removesuffix("hello world"), "") + assert_equal(String("hello world").removesuffix("llo wor"), "hello world") + + def test_intable(): assert_equal(int(String("123")), 123) @@ -590,5 +604,7 @@ def main(): test_hash() test_startswith() test_endswith() + test_removeprefix() + test_removesuffix() test_intable() test_string_mul() From 1c986bd0cb66aaafcf737f8b796222d353b3d57e Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 2 Apr 2024 10:25:19 -0400 Subject: [PATCH 0064/2019] [mojo-stdlib] Teach `PythonObject` about `__setitem__` (#36355) This enables users to conveniently set elements of Python lists and dicts, among other things. MODULAR_ORIG_COMMIT_REV_ID: 815d912bf5ff0e726abdfc0868d374eee3972ab2 --- stdlib/src/python/object.mojo | 26 ++++++++++++++++++++++ stdlib/test/python/test_python_object.mojo | 18 ++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 4a1082a2cb..a16cbca4c5 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -466,6 +466,32 @@ struct PythonObject( Python.throw_python_exception_if_error_state(cpython) return PythonObject(result) + fn __setitem__(inout self, *args: PythonObject) raises: + """Set the value with the given key or keys. + + Args: + args: The key or keys to set on this object, followed by the value. + """ + var size = len(args) + debug_assert(size > 0, "must provide at least a value to __setitem__") + + var cpython = _get_global_python_itf().cpython() + var tuple_obj = cpython.PyTuple_New(size) + for i in range(size): + var arg_value = args[i].py_object + cpython.Py_IncRef(arg_value) + var result = cpython.PyTuple_SetItem(tuple_obj, i, arg_value) + if result != 0: + raise Error("internal error: PyTuple_SetItem failed") + + var callable_obj = cpython.PyObject_GetAttrString( + self.py_object, "__setitem__" + ) + var result = cpython.PyObject_CallObject(callable_obj, tuple_obj) + cpython.Py_DecRef(callable_obj) + cpython.Py_DecRef(tuple_obj) + Python.throw_python_exception_if_error_state(cpython) + fn _call_zero_arg_method( self, method_name: StringRef ) raises -> PythonObject: diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index e2243f1ab2..b88f001e1b 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -453,15 +453,30 @@ fn test_iter() raises: assert_false(True) +fn test_setitem() raises: + var ll = PythonObject([1, 2, 3, "food"]) + # CHECK: [1, 2, 3, 'food'] + print(ll) + ll[1] = "nomnomnom" + # CHECK: [1, 'nomnomnom', 3, 'food'] + print(ll) + + fn test_dict() raises: var d = Dict[PythonObject, PythonObject]() - d["food"] = 123 + d["food"] = "remove this" d["fries"] = "yes" + d["food"] = 123 # intentionally replace to ensure keys stay in order var dd = PythonObject(d) # CHECK: {'food': 123, 'fries': 'yes'} print(dd) + dd["food"] = "salad" + dd[42] = Python.evaluate("[4, 2]") + # CHECK: {'food': 'salad', 'fries': 'yes', 42: [4, 2]} + print(dd) + def main(): # initializing Python instance calls init_python @@ -473,4 +488,5 @@ def main(): test_len() test_is() test_iter() + test_setitem() test_dict() From 729d3f5a7c7197d7b2ea23a4e02b598f16b82d91 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Tue, 2 Apr 2024 11:47:55 -0600 Subject: [PATCH 0065/2019] [mojo-lsp] Fix hover snippet for functions with functional args/params/result (#36391) The LSP was using `->` as a heuristic for the separation between the arguments and results of a function signature, which isn't correct in the presence of functional types. This PR fixes the rendering to properly track the offset of a return, to ensure the hover is always right. Closes https://github.com/modularml/mojo/issues/1215 Closes https://github.com/modularml/mojo/issues/1949 MODULAR_ORIG_COMMIT_REV_ID: ed82e2e607cda9c8d7a018d0d545a453443962c3 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b6d02e059d..51695bb718 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -51,3 +51,8 @@ and tools. Please add any significant user-visible changes here. - [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` in a Mojo package is an error, for now. This is not intended to work yet, erroring for now will help to prevent accidental undefined behavior. + +- [#1215](https://github.com/modularml/mojo/issues/1215) and + [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no + longer cuts off hover previews for functions with functional arguments, + parameters, or results. From cd1e3547cbb218943e8e6cb1ae7f1cf92e78ac94 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Tue, 2 Apr 2024 13:19:59 -0600 Subject: [PATCH 0066/2019] [mojo-tooling] Fix processing of argument conventions in lsp/docs (#36408) This PR fixes the detection and processing of argument conventions, allowing properly handling and display of inout/owned/borrowed arguments (also respecting the defaults for fn vs def). Closes https://github.com/modularml/mojo/issues/1901 MODULAR_ORIG_COMMIT_REV_ID: 83af6de2ec5e2091b3309ef12e5add3eaba2bb34 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 51695bb718..d427ac5ab2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -56,3 +56,6 @@ and tools. Please add any significant user-visible changes here. [#1949](https://github.com/modularml/mojo/issues/1949) The Mojo LSP server no longer cuts off hover previews for functions with functional arguments, parameters, or results. + +- [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and + documentation generation handling of inout arguments. From d5b7a61c433e7af7c85145fe2a30f076e4c9a5cd Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Tue, 2 Apr 2024 18:38:07 -0400 Subject: [PATCH 0067/2019] [SDK][Debug] Enable JIT debugging in the mac sdk (#36220) Closes [Internal link] This also enables the self test on mac, which passes in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 05cb1a0f5b903d09186a2e48554237d03de0f17b --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index d427ac5ab2..5f5fcc1aeb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -59,3 +59,6 @@ and tools. Please add any significant user-visible changes here. - [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and documentation generation handling of inout arguments. + +- [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac + has been fixed. From c78be45bcc417cb30f905302539438635c88909b Mon Sep 17 00:00:00 2001 From: akirchhoff-modular Date: Tue, 2 Apr 2024 17:38:37 -0700 Subject: [PATCH 0068/2019] [KGEN] Fix crash when parsing `0__` (#35899) There was an assumption that if a literal starts with `0` and is at least 3 characters long, it has to be because it started with `0b`, `0x`, etc., and hence was safe to strip off 2 characters. When you do this with `0__`, you end up with `_`, which after filtering underscores, is an empty string, which can't be parsed as an integer, causing an assertion error. Instead, the first two characters only if it's one of the prefixes we expect. Fixes modularml/mojo#1913. MODULAR_ORIG_COMMIT_REV_ID: 75cce9ad8716af3f950a5e36fa855a343f65ba16 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5f5fcc1aeb..fb95f5f982 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -60,5 +60,8 @@ and tools. Please add any significant user-visible changes here. - [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and documentation generation handling of inout arguments. +- [#1913](https://github.com/modularml/mojo/issues/1913) - `0__` no longer + crashes the Mojo parser. + - [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac has been fixed. From d1957572f0091548fadf577b1f483ec24fce6d22 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 2 Apr 2024 17:54:46 -0700 Subject: [PATCH 0069/2019] [Stdlib] Add a load method to Atomic (#36495) This gives the current underlying value for the atomic. Closes #36470 MODULAR_ORIG_COMMIT_REV_ID: d5c0d002492b896d91600e8af6306301f1219a80 --- stdlib/src/os/atomic.mojo | 11 ++++++++++- stdlib/test/os/test_atomic.mojo | 7 +++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index f75175ab27..ec5c0ab592 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -55,7 +55,16 @@ struct Atomic[type: DType]: Args: value: Initial value represented as `mlir.index` type. """ - self.__init__(Scalar[type](value)) + self.value = value + + @always_inline + fn load(inout self) -> Scalar[type]: + """Loads the current value from the atomic. + + Returns: + The current value of the atomic. + """ + return self.fetch_add(0) @staticmethod @always_inline diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index c1546d0da6..563231e662 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -21,6 +21,9 @@ fn test_atomic(): var atom: Atomic[DType.index] = 3 + # CHECK: 3 + print(atom.load()) + # CHECK: 3 print(atom.value) @@ -52,7 +55,7 @@ fn test_atomic(): # CHECK-LABEL: test_atomic_floating_point -fn test_atomic_floating_poInt__(): +fn test_atomic_floating_point(): print("== test_atomic_floating_point") var atom: Atomic[DType.float32] = Float32(3.0) @@ -89,4 +92,4 @@ fn test_atomic_floating_poInt__(): fn main(): test_atomic() - test_atomic_floating_poInt__() + test_atomic_floating_point() From 6d1fe1671070cc8769c485775d76453cd1eaa414 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 2 Apr 2024 23:21:51 -0700 Subject: [PATCH 0070/2019] [mojo-lang] Fix meta-trait type parameter rebinding logic. (#36584) This enhances type checking of type parameter expressions of AnyTrait type to look through downcasts of the type itself and allows downcast from a value of `AnyTrait[SomeTrait]` to `SomeTrait`. This is enough to enable us to type check the fancy dependently typed `each` method on `VariadicPack` which makes VariadicPack useful for non-AnyType trait requirements as shown in the integration test!!! This bumps up against a bug in the KGEN ParameterVerifier. I believe the fix is simple, but am not 100% confident about it, so I temporarily disabled it and filed #36583 to discuss this. MODULAR_ORIG_COMMIT_REV_ID: 816c623595bce4a995afc795bf5fd551c0c83900 --- stdlib/src/builtin/builtin_list.mojo | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index cc0bf0fc5a..648907336e 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -537,10 +537,8 @@ struct VariadicPack[ ] return rebind[result_ref.mlir_ref_type](ref_elt) - # FIXME!: the T in the function should be element_trait bound not AnyType - # bound. @always_inline - fn each[func: fn[T: AnyType] (T) -> None](self): + fn each[func: fn[T: element_trait] (T) -> None](self): """Apply a function to each element of the pack in order. This applies the specified function (which must be parametric on the element type) to each element of the pack, from the first element to the last, passing @@ -552,6 +550,6 @@ struct VariadicPack[ @parameter fn unrolled[i: Int](): - func(self.get_element[i]()[]) + func[element_types[i.value]](self.get_element[i]()[]) unroll[unrolled, Self.__len__()]() From 6affebf63f29a1db725c072bbdadc41f2b1a7439 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 3 Apr 2024 09:07:10 -0400 Subject: [PATCH 0071/2019] [mojo-stdlib] Add back `Python.dict()` (#36499) This was previously removed, but is actually used by our manual and also is generally useful. Since `Dict` can now be used by the interop, implementing it is trivial. MODULAR_ORIG_COMMIT_REV_ID: cd0cc86e047f4e49c57969942132f9dda03f2e4f --- stdlib/src/python/python.mojo | 9 +++++++++ stdlib/test/python/test_python_object.mojo | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index d8af8ba9ea..86063e4ecc 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -170,6 +170,15 @@ struct Python: Python.throw_python_exception_if_error_state(cpython) return PythonObject(module_maybe) + @staticmethod + fn dict() -> PythonObject: + """Construct an empty Python dictionary. + + Returns: + The constructed empty Python dictionary. + """ + return PythonObject(Dict[PythonObject, PythonObject]()) + fn __str__(inout self, str_obj: PythonObject) -> StringRef: """Return a string representing the given Python object. diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index b88f001e1b..f430adbd97 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -477,6 +477,11 @@ fn test_dict() raises: # CHECK: {'food': 'salad', 'fries': 'yes', 42: [4, 2]} print(dd) + # Also test that Python.dict() creates the right object. + var empty = Python.dict() + # CHECK: empty: {} + print("empty:", empty) + def main(): # initializing Python instance calls init_python From 1220859a12a18a2cd6db8bbf17a642c0276c61c0 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 3 Apr 2024 09:12:17 -0400 Subject: [PATCH 0072/2019] [Mojo][Docs] Update changelog with fix to Python dict interop (#36446) MODULAR_ORIG_COMMIT_REV_ID: ec6fa3cea82422f85f5be7f08f0d93f3222ea1d1 --- docs/changelog.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index fb95f5f982..f6fb4d3ae3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -31,6 +31,19 @@ and tools. Please add any significant user-visible changes here. shorter alias is equivalent to writing `--debug-level full`. This option is also available in the `mojo debug` command, but is already the default. +- `PythonObject` now conforms to the `KeyElement` trait, meaning that it can be + used as key type for `Dict`. This allows on to easily build and interact with + Python dictionaries in mojo: + + ```mojo + def main(): + d = PythonObject(Dict[PythonObject, PythonObject]()) + d["foo"] = 12 + d[7] = "bar" + d["foo"] = [1, 2, "something else"] + print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` + ``` + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From ca977b11ed783a481d977a1af4ea070cb447507f Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 3 Apr 2024 10:00:20 -0400 Subject: [PATCH 0073/2019] [mojo-stdlib] Add `Python.list()` method (#36504) To make it easier to create an empty python list. MODULAR_ORIG_COMMIT_REV_ID: ab43b2587bb32db7195a8f45cafb71279fb7e35a --- stdlib/src/python/python.mojo | 9 +++++++++ stdlib/test/python/test_python_object.mojo | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 86063e4ecc..5ceb5093cb 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -179,6 +179,15 @@ struct Python: """ return PythonObject(Dict[PythonObject, PythonObject]()) + @staticmethod + fn list() -> PythonObject: + """Construct an empty Python list. + + Returns: + The constructed empty Python list. + """ + return PythonObject([]) + fn __str__(inout self, str_obj: PythonObject) -> StringRef: """Return a string representing the given Python object. diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index f430adbd97..d291319cda 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -394,7 +394,7 @@ fn test_string_conversions() -> None: # CHECK-LABEL: test_len def test_len(): print("=== test_len ===") - var empty_list = Python.evaluate("[]") + var empty_list = Python.list() # CHECK: 0 print(len(empty_list)) From b82c3ef0dae9b72a3a62b2f56e43f662c9b67f23 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 3 Apr 2024 10:46:42 -0400 Subject: [PATCH 0074/2019] [mojo-stdlib] Simplify `Python.none()` (#36505) `PythonObject` already implements the logic to create a Python `None`, so there this method can just simply delegate. The patch also adds a test case for this. MODULAR_ORIG_COMMIT_REV_ID: 629355fcfd56b1dba249550c894193f69f71891c --- stdlib/src/python/python.mojo | 5 +---- stdlib/test/python/test_python_object.mojo | 8 ++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 5ceb5093cb..546acdce4d 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -249,7 +249,4 @@ struct Python: Returns: `PythonObject` representing `None`. """ - var cpython = _get_global_python_itf().cpython() - var none = cpython.Py_None() - cpython.Py_IncRef(none) - return PythonObject(none) + return PythonObject(None) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index d291319cda..0b200f9388 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -483,6 +483,13 @@ fn test_dict() raises: print("empty:", empty) +fn test_none() raises: + var n = Python.none() + # CHECK: None from Python: None + print("None from Python: ", n) + assert_true(n is None) + + def main(): # initializing Python instance calls init_python var python = Python() @@ -495,3 +502,4 @@ def main(): test_iter() test_setitem() test_dict() + test_none() From 2140c0d445f874f11122009eedcdfc110a30b764 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 10:05:42 -0600 Subject: [PATCH 0075/2019] [stdlib] Fix base64 encode for non-ASCII chars (#36598) This change fixes following bug: https://github.com/modularml/mojo/issues/1630. Upstream commit: https://github.com/modularml/mojo/commit/8544d8bbc7a0b28917e56cde559a15e243a0466b MODULAR_ORIG_COMMIT_REV_ID: ab9ccb453b1c928fde1d8ac69c382d2dfa925a6b --- stdlib/src/base64/base64.mojo | 2 +- stdlib/test/base64/test_base64.mojo | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 9fbd0c933e..fe02bbb26b 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -47,7 +47,7 @@ fn b64encode(str: String) -> String: @parameter @always_inline fn s(idx: Int) -> Int: - return int(str._buffer[idx]) + return int(str._as_ptr().bitcast[DType.uint8]()[idx]) # This algorithm is based on https://arxiv.org/abs/1704.00605 var end = length - (length % 3) diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 310a606892..934a87e57c 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -28,6 +28,9 @@ fn test_b64encode(): # CHECK: SGVsbG8gTW9qbyEhIQ== print(b64encode("Hello Mojo!!!")) + # CHECK: SGVsbG8g8J+UpSEhIQ== + print(b64encode("Hello 🔥!!!")) + # CHECK: dGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw== print(b64encode("the quick brown fox jumps over the lazy dog")) From bb1b41e5e7d5a5c43b2e25f82544b6ba800bcad4 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 3 Apr 2024 12:14:43 -0400 Subject: [PATCH 0076/2019] [mojo-stdlib] Introduce `OwnedKwargsDict` (#36468) This new dictionary type is a wrapper over `Dict`, specializing on `String` keys. In a subsequent patch, this type will be used by the parser to pass variadic keyword arguments that are declared with `owned` ownership semantics. MODULAR_ORIG_COMMIT_REV_ID: ed73c202fa15d38f543f1baee525635b6aa989b4 --- stdlib/src/collections/dict.mojo | 210 ++++++++++++++++++++++++- stdlib/test/collections/test_dict.mojo | 62 +++++++- 2 files changed, 267 insertions(+), 5 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 426745c31d..bdde7534c5 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -493,7 +493,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): self._insert(key, value) fn __contains__(self, key: K) -> Bool: - """Check if a given value is in the dictionary or not. + """Check if a given key is in the dictionary or not. Args: key: The key to check. @@ -749,3 +749,211 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): self._entries[right] = None self._n_entries = self.size + + +struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): + """Container used to pass owned variadic keyword arguments to functions. + + This type mimics the interface of a dictionary with `String` keys, and + should be usable more-or-less like a dictionary. Notably, however, this type + should not be instantiated directly by users. + + Parameters: + V: The value type of the dictionary. Currently must be CollectionElement. + """ + + alias key_type = String + + var _dict: Dict[Self.key_type, V] + + fn __init__(inout self): + """Initialize an empty keyword dictionary.""" + self._dict = Dict[Self.key_type, V]() + + fn __copyinit__(inout self, existing: Self): + """Copy an existing keyword dictionary. + + Args: + existing: The existing keyword dictionary. + """ + self._dict = existing._dict + + fn __moveinit__(inout self, owned existing: Self): + """Move data of an existing keyword dictionary into a new one. + + Args: + existing: The existing keyword dictionary. + """ + self._dict = existing._dict^ + + @always_inline("nodebug") + fn __getitem__(self, key: Self.key_type) raises -> V: + """Retrieve a value out of the keyword dictionary. + + Args: + key: The key to retrieve. + + Returns: + The value associated with the key, if it's present. + + Raises: + "KeyError" if the key isn't present. + """ + return self._dict[key] + + @always_inline("nodebug") + fn __setitem__(inout self, key: Self.key_type, value: V): + """Set a value in the keyword dictionary by key. + + Args: + key: The key to associate with the specified value. + value: The data to store in the dictionary. + """ + self._dict[key] = value + + @always_inline("nodebug") + fn __contains__(self, key: Self.key_type) -> Bool: + """Check if a given key is in the keyword dictionary or not. + + Args: + key: The key to check. + + Returns: + True if there key exists in the keyword dictionary, False + otherwise. + """ + return key in self._dict + + @always_inline("nodebug") + fn __len__(self) -> Int: + """The number of elements currenly stored in the keyword dictionary.""" + return len(self._dict) + + @always_inline("nodebug") + fn find(self, key: Self.key_type) -> Optional[V]: + """Find a value in the keyword dictionary by key. + + Args: + key: The key to search for in the dictionary. + + Returns: + An optional value containing a copy of the value if it was present, + otherwise an empty Optional. + """ + return self._dict.find(key) + + @always_inline("nodebug") + fn pop( + inout self, key: self.key_type, owned default: Optional[V] = None + ) raises -> V: + """Remove a value from the keyword dictionary by key. + + Args: + key: The key to remove from the dictionary. + default: Optionally provide a default value to return if the key + was not found instead of raising. + + Returns: + The value associated with the key, if it was in the dictionary. + If it wasn't, return the provided default value instead. + + Raises: + "KeyError" if the key was not present in the dictionary and no + default value was provided. + """ + return self._dict.pop(key, default^) + + fn __iter__[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ]( + self: Reference[Self, mutability, self_life].mlir_ref_type, + ) -> _DictKeyIter[Self.key_type, V, mutability, self_life]: + """Iterate over the keyword dict's keys as immutable references. + + Parameters: + mutability: Whether the dict is mutable. + self_life: The dict's lifetime. + + Returns: + An iterator of immutable references to the dictionary keys. + """ + # TODO(#36448): Use this instead of the current workaround + # return self._dict.__iter__() + return _DictKeyIter( + _DictEntryIter[Self.key_type, V, mutability, self_life]( + 0, 0, Reference(self)[]._dict + ) + ) + + fn keys[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ]( + self: Reference[Self, mutability, self_life].mlir_ref_type, + ) -> _DictKeyIter[Self.key_type, V, mutability, self_life]: + """Iterate over the keyword dict's keys as immutable references. + + Parameters: + mutability: Whether the dict is mutable. + self_life: The dict's lifetime. + + Returns: + An iterator of immutable references to the dictionary keys. + """ + # TODO(#36448): Use this instead of the current workaround + # return self._dict.keys() + return Self.__iter__(self) + + fn values[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ]( + self: Reference[Self, mutability, self_life].mlir_ref_type, + ) -> _DictValueIter[Self.key_type, V, mutability, self_life]: + """Iterate over the keyword dict's values as references. + + Parameters: + mutability: Whether the dict is mutable. + self_life: The dict's lifetime. + + Returns: + An iterator of references to the dictionary values. + """ + # TODO(#36448): Use this instead of the current workaround + # return self._dict.values() + return _DictValueIter( + _DictEntryIter[Self.key_type, V, mutability, self_life]( + 0, 0, Reference(self)[]._dict + ) + ) + + fn items[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ]( + self: Reference[Self, mutability, self_life].mlir_ref_type, + ) -> _DictEntryIter[Self.key_type, V, mutability, self_life]: + """Iterate over the keyword dictionary's entries as immutable references. + + These can't yet be unpacked like Python dict items, but you can + access the key and value as attributes ie. + + ```mojo + for e in dict.items(): + print(e[].key, e[].value) + ``` + + Parameters: + mutability: Whether the dict is mutable. + self_life: The dict's lifetime. + + Returns: + An iterator of immutable references to the dictionary entries. + """ + + # TODO(#36448): Use this instead of the current workaround + # return Reference(self)[]._dict.items() + return _DictEntryIter[Self.key_type, V, mutability, self_life]( + 0, 0, Reference(self)[]._dict + ) + + @always_inline("nodebug") + fn _insert(inout self, owned key: Self.key_type, owned value: V): + self._dict._insert(key^, value^) diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index ac4ccfb83f..f4afc36612 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -13,7 +13,7 @@ # RUN: %mojo -debug-level full %s from collections import Optional -from collections.dict import Dict, KeyElement +from collections.dict import Dict, KeyElement, OwnedKwargsDict from test_utils import CopyCounter from testing import * @@ -53,7 +53,7 @@ def test_compact(): for i in range(20): var key = "key" + str(i) dict[key] = i + 1 - dict.pop(key) + _ = dict.pop(key) assert_equal(0, len(dict)) @@ -182,7 +182,7 @@ def test_dict_copy_add_new_item(): def test_dict_copy_calls_copy_constructor(): var orig = Dict[String, CopyCounter]() - orig["a"] = CopyCounter()^ + orig["a"] = CopyCounter() # test values copied to new Dict var copy = Dict(orig) @@ -203,7 +203,7 @@ fn test[name: String, test_fn: fn () raises -> object]() raises: print("PASS") -def main(): +def test_dict(): test_dict_construction() test["test_basic", test_basic]() test["test_multiple_resizes", test_multiple_resizes]() @@ -223,3 +223,57 @@ def main(): "test_dict_copy_calls_copy_constructor", test_dict_copy_calls_copy_constructor, ]() + + +def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): + assert_equal(len(kwargs), 2) + + assert_true("fruit" in kwargs) + assert_equal(kwargs["fruit"], 8) + assert_equal(kwargs["fruit"], 8) + + assert_true("dessert" in kwargs) + assert_equal(kwargs["dessert"], 9) + assert_equal(kwargs["dessert"], 9) + + var keys = String("") + for key in kwargs.keys(): + keys += key[] + assert_equal(keys, "fruitdessert") + + var sum = 0 + for val in kwargs.values(): + sum += val[] + assert_equal(sum, 17) + + assert_false(kwargs.find("salad").__bool__()) + with assert_raises(contains="KeyError"): + _ = kwargs["salad"] + + kwargs["salad"] = 10 + assert_equal(kwargs["salad"], 10) + + assert_equal(kwargs.pop("fruit"), 8) + assert_equal(kwargs.pop("fruit", 2), 2) + with assert_raises(contains="KeyError"): + _ = kwargs.pop("fruit") + + keys = String("") + sum = 0 + for entry in kwargs.items(): + keys += entry[].key + sum += entry[].value + assert_equal(keys, "dessertsalad") + assert_equal(sum, 19) + + +def test_owned_kwargs_dict(): + var owned_kwargs = OwnedKwargsDict[Int]() + owned_kwargs._insert("fruit", 8) + owned_kwargs._insert("dessert", 9) + test_taking_owned_kwargs_dict(owned_kwargs^) + + +def main(): + test_dict() + test_owned_kwargs_dict() From 579e7c72d510119f883964d118227d8ab3cb897f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 10:43:15 -0600 Subject: [PATCH 0077/2019] [stdlib] Add `list.pop(index)` API (#36597) Add `list.pop(index)` API to the stdlib as requested by https://github.com/modularml/mojo/issues/2017. This API should handle the same functionality as the Python `list.pop`. Unit tests includes: - popping both positive and negative index - making sure returned value is consistent - making sure list len is consistent Upstream commit: https://github.com/modularml/mojo/commit/0e94191609a286eecddafe6e149240c50f07c8dd MODULAR_ORIG_COMMIT_REV_ID: 2077b615fd49acdce1287ce587a21c8342f0da1d --- stdlib/src/collections/list.mojo | 25 ++++++++++++++++++++ stdlib/test/collections/test_list.mojo | 32 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index be94ae7ac1..a56be9b7b6 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -239,6 +239,31 @@ struct List[T: CollectionElement](CollectionElement, Sized): self._realloc(self.capacity // 2) return ret_val^ + @always_inline + fn pop(inout self, i: Int = -1) -> T: + """Pops a value from the list at the given index. + + Args: + i: The index of the value to pop. + + Returns: + The popped value. + """ + debug_assert(-self.size <= i < self.size, "pop index out of range") + + var normalized_idx = i + if i < 0: + normalized_idx += len(self) + + var ret_val = (self.data + normalized_idx).take_value() + for j in range(normalized_idx + 1, self.size): + (self.data + j).move_into(self.data + j - 1) + self.size -= 1 + if self.size * 4 < self.capacity: + if self.capacity > 1: + self._realloc(self.capacity // 2) + return ret_val^ + @always_inline fn reserve(inout self, new_capacity: Int): """Reserves the requested capacity. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index c634af2570..6e3de70d17 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -85,6 +85,37 @@ def test_list(): assert_equal(2, list.capacity) +def test_list_pop(): + var list = List[Int]() + # Test pop with index + for i in range(6): + list.append(i) + + # try poping from index 3 for 3 times + for i in range(3, 6): + assert_equal(i, list.pop(3)) + + # list should have 3 elements now + assert_equal(3, len(list)) + assert_equal(0, list[0]) + assert_equal(1, list[1]) + assert_equal(2, list[2]) + + # Test pop with negative index + for i in range(0, 2): + assert_equal(i, list.pop(-len(list))) + + # test default index as well + assert_equal(2, list.pop()) + list.append(2) + assert_equal(2, list.pop()) + + # list should be empty now + assert_equal(0, len(list)) + # capacity should be 1 according to shrink_to_fit behavior + assert_equal(1, list.capacity) + + def test_list_variadic_constructor(): var l = List[Int](2, 4, 6) assert_equal(3, len(l)) @@ -450,6 +481,7 @@ def test_list_span(): def main(): test_mojo_issue_698() test_list() + test_list_pop() test_list_variadic_constructor() test_list_reverse() test_list_reverse_move_count() From 4c32139031307728205c794c37b48879b5c0a100 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 12:20:30 -0600 Subject: [PATCH 0078/2019] [stdlib] Remove `List.pop_back()` (#36612) Now that `List` has a `pop(index)` API, remove `pop_back()` from `List`. `pop()` without any arguments has the same semantics. Fix up the call sites internally in the standard library. While here, adjust `pop(index)` to consistently use `len(self)`. Add a changelog entry for the new API in `List` and the removal of `pop_back()`. MODULAR_ORIG_COMMIT_REV_ID: 76c8b15ac5c2dd2da879de4fd1f438dad3572c7d --- docs/changelog.md | 7 ++++++ stdlib/src/builtin/string.mojo | 2 +- stdlib/src/collections/list.mojo | 16 +------------ stdlib/test/collections/test_list.mojo | 32 +++++++------------------- 4 files changed, 17 insertions(+), 40 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f6fb4d3ae3..253a01a55a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -44,6 +44,10 @@ and tools. Please add any significant user-visible changes here. print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` ``` +- `List` collection now has a `pop(index)` API for removing an element + at a particular index. By default, `List.pop()` removes the last element + in the list. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has @@ -59,6 +63,9 @@ and tools. Please add any significant user-visible changes here. ### ❌ Removed +- `List.pop_back()` has been removed. Use `List.pop()` instead which defaults + to popping the last element in the list. + ### 🛠️ Fixed - [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 280c0137dd..269f210349 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -681,7 +681,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): # TODO(lifetimes): Return a reference rather than a copy var copy = self._buffer - var last = copy.pop_back() + var last = copy.pop() debug_assert( last == 0, "expected last element of String buffer to be null terminator", diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index a56be9b7b6..21387a5cdd 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -225,20 +225,6 @@ struct List[T: CollectionElement](CollectionElement, Sized): # list. self.size = final_size - @always_inline - fn pop_back(inout self) -> T: - """Pops a value from the back of this list. - - Returns: - The popped value. - """ - var ret_val = (self.data + (self.size - 1)).take_value() - self.size -= 1 - if self.size * 4 < self.capacity: - if self.capacity > 1: - self._realloc(self.capacity // 2) - return ret_val^ - @always_inline fn pop(inout self, i: Int = -1) -> T: """Pops a value from the list at the given index. @@ -249,7 +235,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): Returns: The popped value. """ - debug_assert(-self.size <= i < self.size, "pop index out of range") + debug_assert(-len(self) <= i < len(self), "pop index out of range") var normalized_idx = i if i < 0: diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 6e3de70d17..de74a5286f 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -57,32 +57,15 @@ def test_list(): list[-1] = 7 assert_equal(7, list[-1]) - # pop_back shall return the last element - # and adjust the size - assert_equal(7, list.pop_back()) - assert_equal(4, len(list)) - - # Verify that capacity shrinks as the list goes smaller - while list.size > 1: - _ = list.pop_back() - - assert_equal(1, len(list)) - assert_equal( - 1, list.size - ) # pedantically ensure len and size refer to the same thing - assert_equal(4, list.capacity) - - # Verify that capacity doesn't become 0 when the list gets empty. - _ = list.pop_back() - assert_equal(0, len(list)) - - # FIXME: revisit that pop_back is actually doing shrink_to_fit behavior - # under the hood which will be surprising to users - assert_equal(2, list.capacity) +def test_list_clear(): + var list = List[Int](1, 2, 3) + assert_equal(len(list), 3) + assert_equal(list.capacity, 3) list.clear() - assert_equal(0, len(list)) - assert_equal(2, list.capacity) + + assert_equal(len(list), 0) + assert_equal(list.capacity, 3) def test_list_pop(): @@ -481,6 +464,7 @@ def test_list_span(): def main(): test_mojo_issue_698() test_list() + test_list_clear() test_list_pop() test_list_variadic_constructor() test_list_reverse() From cd213f835c086e1bed68b278c794c8f38f64d389 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 3 Apr 2024 11:36:36 -0700 Subject: [PATCH 0079/2019] [mojo-lang] Teach parameter inference about type rebinds. (#36587) We get type rebinds when metatypes are downcast - look through these to infer from the original type before mapping. In the example, we are allowed to infer that the argument has type `element_types[i]` even though the Reference type erased it down to `AnyType`. MODULAR_ORIG_COMMIT_REV_ID: 8212deaf37c4b83a75195a86ae08bb2b931d4997 --- stdlib/src/builtin/builtin_list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 648907336e..9913d106d8 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -550,6 +550,6 @@ struct VariadicPack[ @parameter fn unrolled[i: Int](): - func[element_types[i.value]](self.get_element[i]()[]) + func(self.get_element[i]()[]) unroll[unrolled, Self.__len__()]() From e9e5996e8a7545eb0f2cbe3641d4b4e7c2d796ea Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 3 Apr 2024 12:25:36 -0700 Subject: [PATCH 0080/2019] [Docs] Fix changelog formatting. Fixes #36539 (#36634) Why an extra blank line inside a fenced code block causes our MD processor to lose its mind is another question, but it does, and this fixes the issue in this instance. MODULAR_ORIG_COMMIT_REV_ID: 785e243082cedb89a5e5d0719ca33205bef441c2 --- docs/changelog-released.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 0b83b56536..7860d298d9 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -1161,7 +1161,6 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. fn __copyinit__(inout self: Self, existing: Self): self.vec = existing.vec - fn main(): var foo = Foo() print(len(foo.vec)) From f2793dd83bc1e1ae03d64af37299a16622e2ffb5 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 3 Apr 2024 18:18:10 -0400 Subject: [PATCH 0081/2019] [mojo-lang][mojo-stdlib] Use `OwnedKwargsDict` for emitting variadic keyword arguments (#36540) Instead of using a `Dict`, the parser will use a more specialized type for passing variadic keyword arguments into callees. The patch also simplifies some of the emission logic by moving `String` creation for the keys into ``OwnedKwargsDict`. MODULAR_ORIG_COMMIT_REV_ID: bc0bc6edced025903ee095fa5ca3bea248690e66 --- stdlib/src/collections/dict.mojo | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index bdde7534c5..40f81e563a 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -957,3 +957,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn _insert(inout self, owned key: Self.key_type, owned value: V): self._dict._insert(key^, value^) + + @always_inline("nodebug") + fn _insert(inout self, key: StringLiteral, owned value: V): + self._insert(String(key), value^) From f159dbf902480ef771b231343227dc923774017f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 21:20:00 -0600 Subject: [PATCH 0082/2019] [stdlib] Avoid `import *` in tests (#36678) Change the tests to explicitly import what they use rather than using `from import *`. Upstream commit: https://github.com/modularml/mojo/commit/48b91b75242ae28185fa76fc73a6f29e3b9ae91a MODULAR_ORIG_COMMIT_REV_ID: d7ad4220c1e478180d3e300b39f8ad815ffbd8a8 --- examples/deviceinfo.mojo | 22 ++++++++++++++++++-- stdlib/test/builtin/test_bfloat16.mojo | 2 +- stdlib/test/builtin/test_dtype.mojo | 2 +- stdlib/test/builtin/test_hash.mojo | 2 +- stdlib/test/builtin/test_int.mojo | 2 +- stdlib/test/builtin/test_issue_1505.mojo | 2 +- stdlib/test/builtin/test_location.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/builtin/test_slice.mojo | 2 +- stdlib/test/builtin/test_str.mojo | 2 +- stdlib/test/builtin/test_string.mojo | 8 ++++++- stdlib/test/builtin/test_string_literal.mojo | 8 ++++++- stdlib/test/builtin/test_stringref.mojo | 2 +- stdlib/test/collections/test_dict.mojo | 2 +- stdlib/test/collections/test_list.mojo | 2 +- stdlib/test/collections/test_optional.mojo | 2 +- stdlib/test/collections/test_set.mojo | 2 +- stdlib/test/collections/test_vector.mojo | 2 +- stdlib/test/memory/test_anypointer.mojo | 2 +- stdlib/test/memory/test_arc.mojo | 2 +- stdlib/test/os/path/test_exists.mojo | 2 +- stdlib/test/os/path/test_isdir.mojo | 2 +- stdlib/test/os/path/test_isfile.mojo | 2 +- stdlib/test/os/path/test_islink.mojo | 2 +- stdlib/test/os/test_listdir.mojo | 4 ++-- stdlib/test/os/test_stat.mojo | 4 ++-- stdlib/test/pathlib/test_pathlib.mojo | 4 ++-- stdlib/test/test_utils/__init__.mojo | 2 +- stdlib/test/utils/test_variant.mojo | 2 +- 29 files changed, 63 insertions(+), 33 deletions(-) diff --git a/examples/deviceinfo.mojo b/examples/deviceinfo.mojo index 62428a9b53..f2c79fa3e1 100644 --- a/examples/deviceinfo.mojo +++ b/examples/deviceinfo.mojo @@ -14,8 +14,26 @@ # This sample prints the current host system information using APIs from the # sys module. -from sys.info import * -from sys.info import _current_cpu, _current_target, _triple_attr +from sys.info import ( + _current_cpu, + _current_target, + _triple_attr, + os_is_linux, + os_is_macos, + os_is_windows, + has_sse4, + has_avx, + has_avx2, + has_avx512f, + has_vnni, + has_intel_amx, + has_neon, + is_apple_m1, + is_apple_m2, + is_apple_m3, + num_physical_cores, + num_logical_cores, +) def main(): diff --git a/stdlib/test/builtin/test_bfloat16.mojo b/stdlib/test/builtin/test_bfloat16.mojo index bc3b5f10b5..3b341e4ba7 100644 --- a/stdlib/test/builtin/test_bfloat16.mojo +++ b/stdlib/test/builtin/test_bfloat16.mojo @@ -15,7 +15,7 @@ from random import randn_float64 from sys.info import has_neon -from testing import * +from testing import assert_equal, assert_almost_equal def test_methods(): diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index b0fc366792..5e996d6677 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -14,7 +14,7 @@ from collections import Set -from testing import * +from testing import assert_equal, assert_false, assert_true fn test_stringable() raises: diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index 934b2c15b8..1bd5aac2d9 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -19,7 +19,7 @@ # specific. But for now they test behavior and reproducibility. from builtin.hash import _hash_simd -from testing import * +from testing import assert_equal, assert_not_equal, assert_true def same_low_bits(i1: Int, i2: Int, bits: Int = 4) -> Int: diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 82b5046c20..c983817c19 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import * +from testing import assert_equal def test_constructors(): diff --git a/stdlib/test/builtin/test_issue_1505.mojo b/stdlib/test/builtin/test_issue_1505.mojo index aea2405573..8c1ef6eb20 100644 --- a/stdlib/test/builtin/test_issue_1505.mojo +++ b/stdlib/test/builtin/test_issue_1505.mojo @@ -16,7 +16,7 @@ from random import random_ui64 -from testing import * +from testing import assert_equal fn gen_perm() -> StaticIntTuple[64]: diff --git a/stdlib/test/builtin/test_location.mojo b/stdlib/test/builtin/test_location.mojo index ec5b730ecd..500230e460 100644 --- a/stdlib/test/builtin/test_location.mojo +++ b/stdlib/test/builtin/test_location.mojo @@ -13,7 +13,7 @@ # RUN: mojo --debug-level full %s from builtin._location import _SourceLocation -from testing import * +from testing import assert_equal def main(): diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 60c767a9c9..c94d06b7a6 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -14,7 +14,7 @@ from sys.info import has_neon, simdwidthof -from testing import * +from testing import assert_equal, assert_not_equal, assert_true # CHECK-LABEL: test_cast diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 1370bf88d5..0a405c0be5 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s | FileCheck %s -from testing import * +from testing import assert_equal, assert_false # CHECK-LABEL: test_none_end_folds diff --git a/stdlib/test/builtin/test_str.mojo b/stdlib/test/builtin/test_str.mojo index 3e8dbd3c85..5694d97d22 100644 --- a/stdlib/test/builtin/test_str.mojo +++ b/stdlib/test/builtin/test_str.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import * +from testing import assert_equal def test_str_none(): diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 2cbfc5db2a..2da62facd5 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -16,7 +16,13 @@ from builtin.string import ( _calc_initial_buffer_size_int32, _calc_initial_buffer_size_int64, ) -from testing import * +from testing import ( + assert_equal, + assert_false, + assert_not_equal, + assert_raises, + assert_true, +) from utils import StringRef diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 0299683f94..4473dadcc6 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -12,7 +12,13 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import * +from testing import ( + assert_equal, + assert_not_equal, + assert_true, + assert_false, + assert_raises, +) def test_basics(): diff --git a/stdlib/test/builtin/test_stringref.mojo b/stdlib/test/builtin/test_stringref.mojo index fa4c62e890..bddfb751f4 100644 --- a/stdlib/test/builtin/test_stringref.mojo +++ b/stdlib/test/builtin/test_stringref.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from testing import * +from testing import assert_equal, assert_raises from utils import StringRef diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index f4afc36612..dadd2bf2c3 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -16,7 +16,7 @@ from collections import Optional from collections.dict import Dict, KeyElement, OwnedKwargsDict from test_utils import CopyCounter -from testing import * +from testing import assert_equal, assert_false, assert_raises, assert_true def test_dict_construction(): diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index de74a5286f..fc5364ce31 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -15,7 +15,7 @@ from collections import List from test_utils import CopyCounter, MoveCounter -from testing import * +from testing import assert_equal def test_mojo_issue_698(): diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 1eff5ae14c..774af69f3c 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -14,7 +14,7 @@ from collections.optional import Optional, OptionalReg -from testing import * +from testing import assert_true, assert_false, assert_equal def test_basic(): diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 74ef6409f7..4e94a06d7e 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -14,7 +14,7 @@ from collections.set import Set -from testing import * +from testing import assert_raises, assert_true, assert_false fn assert_equal[T: EqualityComparable](lhs: T, rhs: T) raises: diff --git a/stdlib/test/collections/test_vector.mojo b/stdlib/test/collections/test_vector.mojo index 406a782d84..b242b792f8 100644 --- a/stdlib/test/collections/test_vector.mojo +++ b/stdlib/test/collections/test_vector.mojo @@ -15,7 +15,7 @@ from collections.vector import InlinedFixedVector from test_utils import MoveCounter -from testing import * +from testing import assert_equal def test_inlined_fixed_vector(): diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_anypointer.mojo index ce9377089f..52c0d0583f 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_anypointer.mojo @@ -14,7 +14,7 @@ from memory.anypointer import AnyPointer from test_utils import MoveCounter -from testing import * +from testing import assert_equal, assert_not_equal, assert_true struct MoveOnlyType(Movable): diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index c2748d1cfd..2e30349136 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -15,7 +15,7 @@ from collections import List from memory._arc import Arc -from testing import * +from testing import assert_equal, assert_false, assert_true def test_basic(): diff --git a/stdlib/test/os/path/test_exists.mojo b/stdlib/test/os/path/test_exists.mojo index 347e1f1c79..966f022f91 100644 --- a/stdlib/test/os/path/test_exists.mojo +++ b/stdlib/test/os/path/test_exists.mojo @@ -16,7 +16,7 @@ from os.path import exists, lexists from pathlib import Path, cwd -from testing import * +from testing import assert_true, assert_false def main(): diff --git a/stdlib/test/os/path/test_isdir.mojo b/stdlib/test/os/path/test_isdir.mojo index c539d2f310..b5a936f0fd 100644 --- a/stdlib/test/os/path/test_isdir.mojo +++ b/stdlib/test/os/path/test_isdir.mojo @@ -15,7 +15,7 @@ from os.path import isdir from pathlib import Path, cwd -from testing import * +from testing import assert_true, assert_false def main(): diff --git a/stdlib/test/os/path/test_isfile.mojo b/stdlib/test/os/path/test_isfile.mojo index 4e5b07152f..012a251b6d 100644 --- a/stdlib/test/os/path/test_isfile.mojo +++ b/stdlib/test/os/path/test_isfile.mojo @@ -15,7 +15,7 @@ from os.path import isfile from pathlib import Path -from testing import * +from testing import assert_true, assert_false def main(): diff --git a/stdlib/test/os/path/test_islink.mojo b/stdlib/test/os/path/test_islink.mojo index a63d15a72d..f8d55d5909 100644 --- a/stdlib/test/os/path/test_islink.mojo +++ b/stdlib/test/os/path/test_islink.mojo @@ -20,7 +20,7 @@ from os.path import isdir, islink from pathlib import Path from sys.param_env import env_get_string -from testing import * +from testing import assert_true, assert_false alias TEMP_DIR = env_get_string["TEMP_DIR"]() diff --git a/stdlib/test/os/test_listdir.mojo b/stdlib/test/os/test_listdir.mojo index 6737bd8504..945bdf6b3a 100644 --- a/stdlib/test/os/test_listdir.mojo +++ b/stdlib/test/os/test_listdir.mojo @@ -12,10 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from os import * +from os import listdir from pathlib import Path -from testing import * +from testing import assert_true def test_listdir(): diff --git a/stdlib/test/os/test_stat.mojo b/stdlib/test/os/test_stat.mojo index 6e45452905..1e1a3d5ffb 100644 --- a/stdlib/test/os/test_stat.mojo +++ b/stdlib/test/os/test_stat.mojo @@ -12,10 +12,10 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s -from os import * +from os import stat from stat import S_ISREG -from testing import * +from testing import assert_not_equal, assert_true def main(): diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 9f67761117..b19d5599f0 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -13,10 +13,10 @@ # REQUIRES: !windows # RUN: %mojo -debug-level full -D TEMP_FILE=%t %s -from pathlib import * +from pathlib import cwd, Path, DIR_SEPARATOR from sys.param_env import env_get_string -from testing import * +from testing import assert_true, assert_false, assert_equal, assert_not_equal alias TEMP_FILE = env_get_string["TEMP_FILE"]() diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index 291c1de528..976e8db514 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -4,5 +4,5 @@ # # ===----------------------------------------------------------------------=== # -from .test_utils import * +from .test_utils import libm_call from .types import CopyCounter, MoveCounter, MoveOnly diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 709dee59e6..c9c4ec3048 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -15,7 +15,7 @@ from sys.ffi import _get_global from memory.unsafe import Pointer -from testing import * +from testing import assert_equal, assert_false, assert_true from utils.variant import Variant From 7d579c5409beab20c608b7b3e07fce99d8ae7247 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 21:20:27 -0600 Subject: [PATCH 0083/2019] [stdlib] Add `Dict.update()` (#36677) [Here](https://docs.python.org/3/library/stdtypes.html#dict.update) is the python documentation about this method. There should be another overload: `fn update(self, **kwargs: V)` which we can't do yet since it would only work with keys that are strings and we don't yet have conditional conformance (see https://github.com/modularml/mojo/issues/1876). This is why we force the argument to be positional-only. If we allowed it to be passed by keyword, it would break when we will introduce the other overload of `Dict.update`. Upstream commit: https://github.com/modularml/mojo/commit/b3a2f91c71fa0bc236f97e2c68ecd3e49dd7c232 MODULAR_ORIG_COMMIT_REV_ID: d3fcf8609c6d97b4ea4d2bf866987d89e9e30a86 --- docs/changelog.md | 4 ++- stdlib/src/collections/dict.mojo | 10 ++++++ stdlib/test/collections/test_dict.mojo | 45 ++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 253a01a55a..1fb5fbef4d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -44,10 +44,12 @@ and tools. Please add any significant user-visible changes here. print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` ``` -- `List` collection now has a `pop(index)` API for removing an element +- `List` now has a `pop(index)` API for removing an element at a particular index. By default, `List.pop()` removes the last element in the list. +- `Dict` now has a `update()` method to update keys/values from another `Dict`. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 40f81e563a..e826162147 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -638,6 +638,16 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): 0, 0, Reference(self) ) + fn update(inout self, other: Self, /): + """Update the dictionary with the key/value pairs from other, overwriting existing keys. + The argument must be positional only. + + Args: + other: The dictionary to update from. + """ + for entry in other.items(): + self[entry[].key] = entry[].value + @staticmethod @always_inline fn _new_entries(reserved: Int) -> List[Optional[DictEntry[K, V]]]: diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index dadd2bf2c3..47bde31cbe 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -192,6 +192,48 @@ def test_dict_copy_calls_copy_constructor(): assert_equal(6, copy["a"].copy_count) +def test_dict_update_nominal(): + var orig = Dict[String, Int]() + orig["a"] = 1 + orig["b"] = 2 + + var new = Dict[String, Int]() + new["b"] = 3 + new["c"] = 4 + + orig.update(new) + + assert_equal(orig["a"], 1) + assert_equal(orig["b"], 3) + assert_equal(orig["c"], 4) + + +def test_dict_update_empty_origin(): + var orig = Dict[String, Int]() + var new = Dict[String, Int]() + new["b"] = 3 + new["c"] = 4 + + orig.update(new) + + assert_equal(orig["b"], 3) + assert_equal(orig["c"], 4) + + +def test_dict_update_empty_new(): + var orig = Dict[String, Int]() + orig["a"] = 1 + orig["b"] = 2 + + var new = Dict[String, Int]() + + orig.update(new) + + assert_equal(orig["a"], 1) + assert_equal(orig["b"], 2) + assert_equal(len(orig), 2) + + fn test[name: String, test_fn: fn () raises -> object]() raises: var name_val = name # FIXME(#26974): Can't pass 'name' directly. print("Test", name_val, "...", end="") @@ -223,6 +265,9 @@ def test_dict(): "test_dict_copy_calls_copy_constructor", test_dict_copy_calls_copy_constructor, ]() + test["test_dict_update_nominal", test_dict_update_nominal]() + test["test_dict_update_empty_origin", test_dict_update_empty_origin]() + test["test_dict_update_empty_new", test_dict_update_empty_new]() def test_taking_owned_kwargs_dict(owned kwargs: OwnedKwargsDict[Int]): From 491725aa72e393d0b342ff6714fecda7350905fe Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 21:20:56 -0600 Subject: [PATCH 0084/2019] [stdlib] Replace `__init__ -> Self` use in `os` module (#36675) The `__init__ -> Self` is getting deprecated. Switch over to "normal" `__init__` constructors that accept `Self` as an `inout` argument instead within the `os` module. Upstream commit: https://github.com/modularml/mojo/commit/57b0b57f581eaba67affa9e483b7eb14de3448ba MODULAR_ORIG_COMMIT_REV_ID: 3382fe2636ae361e732a1b94a0c0dbdc4aef03a4 --- stdlib/src/os/_linux_aarch64.mojo | 38 ++++++++++++++--------------- stdlib/src/os/_linux_x86.mojo | 36 +++++++++++++--------------- stdlib/src/os/_macos.mojo | 40 +++++++++++++++---------------- 3 files changed, 54 insertions(+), 60 deletions(-) diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index f801bdf9f5..d25d1a3b9b 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -49,26 +49,24 @@ struct _c_stat(Stringable): var st_birthtimespec: _CTimeSpec # time of file creation(birth) var unused: StaticTuple[Int64, 2] # RESERVED: DO NOT USE! - fn __init__() -> Self: - return Self { - st_dev: 0, - st_mode: 0, - st_nlink: 0, - st_ino: 0, - st_uid: 0, - st_gid: 0, - __pad0: 0, - st_rdev: 0, - st_size: 0, - st_blksize: 0, - __pad1: 0, - st_blocks: 0, - st_atimespec: _CTimeSpec(), - st_mtimespec: _CTimeSpec(), - st_ctimespec: _CTimeSpec(), - st_birthtimespec: _CTimeSpec(), - unused: StaticTuple[Int64, 2](0, 0), - } + fn __init__(inout self): + self.st_dev = 0 + self.st_mode = 0 + self.st_nlink = 0 + self.st_ino = 0 + self.st_uid = 0 + self.st_gid = 0 + self.__pad0 = 0 + self.st_rdev = 0 + self.st_size = 0 + self.st_blksize = 0 + self.__pad1 = 0 + self.st_blocks = 0 + self.st_atimespec = _CTimeSpec() + self.st_mtimespec = _CTimeSpec() + self.st_ctimespec = _CTimeSpec() + self.st_birthtimespec = _CTimeSpec() + self.unused = StaticTuple[Int64, 2](0, 0) fn __str__(self) -> String: var res = String("{\n") diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index a94f66a54e..b0490520e1 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -48,25 +48,23 @@ struct _c_stat(Stringable): var st_birthtimespec: _CTimeSpec # time of file creation(birth) var unused: StaticTuple[Int64, 3] # RESERVED: DO NOT USE! - fn __init__() -> Self: - return Self { - st_dev: 0, - st_mode: 0, - st_nlink: 0, - st_ino: 0, - st_uid: 0, - st_gid: 0, - __pad0: 0, - st_rdev: 0, - st_size: 0, - st_blksize: 0, - st_blocks: 0, - st_atimespec: _CTimeSpec(), - st_mtimespec: _CTimeSpec(), - st_ctimespec: _CTimeSpec(), - st_birthtimespec: _CTimeSpec(), - unused: StaticTuple[Int64, 3](0, 0, 0), - } + fn __init__(inout self): + self.st_dev = 0 + self.st_mode = 0 + self.st_nlink = 0 + self.st_ino = 0 + self.st_uid = 0 + self.st_gid = 0 + self.__pad0 = 0 + self.st_rdev = 0 + self.st_size = 0 + self.st_blksize = 0 + self.st_blocks = 0 + self.st_atimespec = _CTimeSpec() + self.st_mtimespec = _CTimeSpec() + self.st_ctimespec = _CTimeSpec() + self.st_birthtimespec = _CTimeSpec() + self.unused = StaticTuple[Int64, 3](0, 0, 0) fn __str__(self) -> String: var res = String("{\n") diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index f0525e5477..374a79d904 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -51,27 +51,25 @@ struct _c_stat(Stringable): var st_lspare: Int32 # RESERVED: DO NOT USE! var st_qspare: StaticTuple[Int64, 2] # RESERVED: DO NOT USE! - fn __init__() -> Self: - return Self { - st_dev: 0, - st_mode: 0, - st_nlink: 0, - st_ino: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_atimespec: _CTimeSpec(), - st_mtimespec: _CTimeSpec(), - st_ctimespec: _CTimeSpec(), - st_birthtimespec: _CTimeSpec(), - st_size: 0, - st_blocks: 0, - st_blksize: 0, - st_flags: 0, - st_gen: 0, - st_lspare: 0, - st_qspare: StaticTuple[Int64, 2](0, 0), - } + fn __init__(inout self): + self.st_dev = 0 + self.st_mode = 0 + self.st_nlink = 0 + self.st_ino = 0 + self.st_uid = 0 + self.st_gid = 0 + self.st_rdev = 0 + self.st_atimespec = _CTimeSpec() + self.st_mtimespec = _CTimeSpec() + self.st_ctimespec = _CTimeSpec() + self.st_birthtimespec = _CTimeSpec() + self.st_size = 0 + self.st_blocks = 0 + self.st_blksize = 0 + self.st_flags = 0 + self.st_gen = 0 + self.st_lspare = 0 + self.st_qspare = StaticTuple[Int64, 2](0, 0) fn __str__(self) -> String: var res = String("{\n") From 2a8c43a82e0ddbf7d9eff3dfee64eb99e8b5e0f7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 3 Apr 2024 21:30:47 -0600 Subject: [PATCH 0085/2019] [stdlib] Add `is` and `isnot` functions to `Optional` (#36674) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just as the title says, it's a very common idiom in python, is even recommended by PEP8: https://peps.python.org/pep-0008/#programming-recommendations. Comparisons to singletons like `None` should always be done with is or is not, never the equality operators. Also, beware of writing if x when you really mean if x is not None – e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context! While the warning does not apply to Mojo, Python users are quite used to it and it's a small quality of life improvement which is inexpensive to support. Upstream commit: https://github.com/modularml/mojo/commit/6308519882f67b9dc7e7ea2ad584e3f35da2e009 MODULAR_ORIG_COMMIT_REV_ID: 0961d7b5804b574df61806b8c433471e72cfcd67 --- stdlib/src/collections/optional.mojo | 26 ++++++++++++++++++++++ stdlib/test/collections/test_optional.mojo | 18 +++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index f218c0ede8..2bf494da43 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -147,6 +147,32 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): return self._value.get[T]()[] return default + fn __is__(self, other: NoneType) -> Bool: + """Return `True` if the Optional has no value. + + It allows you to use the following syntax: `if my_optional is None:` + + Args: + other: The value to compare to (None). + + Returns: + True if the Optional has no value and False otherwise. + """ + return not self.__bool__() + + fn __isnot__(self, other: NoneType) -> Bool: + """Return `True` if the Optional has a value. + + It allows you to use the following syntax: `if my_optional is not None:`. + + Args: + other: The value to compare to (None). + + Returns: + True if the Optional has a value and False otherwise. + """ + return self.__bool__() + fn __bool__(self) -> Bool: """Return true if the Optional has a value. diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 774af69f3c..22a6858c34 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -71,6 +71,24 @@ def test_optional_reg_basic(): assert_true(True and val) +def test_optional_is(): + a = Optional(1) + assert_false(a is None) + + a = Optional[Int](None) + assert_true(a is None) + + +def test_optional_isnot(): + a = Optional(1) + assert_true(a is not None) + + a = Optional[Int](None) + assert_false(a is not None) + + def main(): test_basic() test_optional_reg_basic() + test_optional_is() + test_optional_isnot() From 348740fc7362f5465062972222a6c3f7dd1ebe31 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 3 Apr 2024 22:39:55 -0700 Subject: [PATCH 0086/2019] [Stdlib] Constrain the mask for shuffle to be within the right domain (#36719) Disallow negative and invalid values in the shuffle mask value. Closes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: dfac330b52860cf4418851b31807bfac0ccac2c8 --- stdlib/src/builtin/simd.mojo | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8ab473ac0d..7b801eefde 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1333,7 +1333,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( *mask: Int, output_size: Int = size ](self, other: Self) -> SIMD[type, output_size]: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). + the `other` value using the specified mask (permutation). The mask values + must be within `2*len(self)`. Parameters: mask: The permutation to use in the shuffle. @@ -1368,6 +1369,10 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter fn fill[idx: Int](): alias val = mask[idx] + constrained[ + 0 <= val < 2 * size, + "invalid index in the shuffle operation", + ]() var ptr = __mlir_op.`pop.array.gep`( Pointer.address_of(array).address, idx.value ) @@ -1391,7 +1396,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn shuffle[*mask: Int](self) -> Self: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). + the `other` value using the specified mask (permutation). The mask values + must be within `2*len(self)`. Parameters: mask: The permutation to use in the shuffle. @@ -1405,7 +1411,8 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @always_inline("nodebug") fn shuffle[*mask: Int](self, other: Self) -> Self: """Shuffles (also called blend) the values of the current vector with - the `other` value using the specified mask (permutation). + the `other` value using the specified mask (permutation). The mask values + must be within `2*len(self)`. Parameters: mask: The permutation to use in the shuffle. From c401d6331bd8841217f5a8d22e95c5189ff4629e Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 3 Apr 2024 22:59:17 -0700 Subject: [PATCH 0087/2019] [******] Rename apple and intel amx files in linalg package, NFC (#36727) MODULAR_ORIG_COMMIT_REV_ID: acf071cdc4ebba1ce41a3a059b5a2ef2077d896f --- stdlib/test/sys/test_has_intel_amx.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/sys/test_has_intel_amx.mojo b/stdlib/test/sys/test_has_intel_amx.mojo index 9b8c526fb6..68cb8bc527 100644 --- a/stdlib/test/sys/test_has_intel_amx.mojo +++ b/stdlib/test/sys/test_has_intel_amx.mojo @@ -21,7 +21,7 @@ from sys.info import has_intel_amx, os_is_linux -from IntelAMX import init_intel_amx +from LinAlg.intel_amx import init_intel_amx # CHECK-LABEL: test_has_intel_amx From af900812e27f5a1df77fe203481193d1c49aac50 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 4 Apr 2024 09:14:54 -0600 Subject: [PATCH 0088/2019] [stdlib] Add move constructor to `Atomic` (#36718) Fixes [Internal link] Upstream commit: https://github.com/modularml/mojo/commit/bf5bdbd2850dae5920b61b8e414bcde4c5ee1b95 MODULAR_ORIG_COMMIT_REV_ID: 7654fad7242cdb9d88759558a1ba7ca360889144 --- stdlib/src/os/atomic.mojo | 9 +++++++++ stdlib/test/os/test_atomic.mojo | 22 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index ec5c0ab592..a16a079c4c 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -57,6 +57,15 @@ struct Atomic[type: DType]: """ self.value = value + @always_inline + fn __moveinit__(inout self, owned existing: Self): + """Moves Constructor. + + Args: + existing: The existing Atomic. + """ + self.value = existing.value + @always_inline fn load(inout self) -> Scalar[type]: """Loads the current value from the atomic. diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 563231e662..61c7c11ccc 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -13,6 +13,7 @@ # RUN: %mojo -debug-level full %s | FileCheck %s from os.atomic import Atomic +from testing import assert_equal # CHECK-LABEL: test_atomic @@ -90,6 +91,25 @@ fn test_atomic_floating_point(): print(atom.value) -fn main(): +def test_atomic_move_constructor(): + var atom: Atomic[DType.index] = 3 + var atom2 = atom^ + assert_equal(atom2.value, 3) + atom2 += 4 + assert_equal(atom2.value, 7) + atom2 -= 4 + assert_equal(atom2.value, 3) + atom2.max(0) + assert_equal(atom2.value, 3) + atom2.max(42) + assert_equal(atom2.value, 42) + atom2.min(3) + assert_equal(atom2.value, 3) + atom2.min(0) + assert_equal(atom2.value, 0) + + +def main(): test_atomic() test_atomic_floating_point() + test_atomic_move_constructor() From 06eda0684b327d652116ad8ea6f7698010829dec Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 4 Apr 2024 09:07:18 -0700 Subject: [PATCH 0089/2019] [mojo-lang] Enable `VariadicPack` for borrowed memory packs (#36680) This is the big one: `VariadicPack` is now ready to take on the heavy load for all non-register-passable packs, which notably includes `print` and `String.join`. This means deleting our old friend `_StringableTuple`, replacing the logic with stuff that is much simpler. These examples use capturing values, so switch the `VariadicPack.each` method to work with a capturing closure This patch fixes a ton of bugs, because variadic packs of memory-only type now work! MODULAR_ORIG_COMMIT_REV_ID: 436b86a7496642a6a8f4c4921ef7234436618123 --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/io.mojo | 76 ++++--------------------- stdlib/src/builtin/string.mojo | 28 +++++---- stdlib/test/memory/test_anypointer.mojo | 3 +- 4 files changed, 25 insertions(+), 84 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 9913d106d8..e26d643c79 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -538,7 +538,7 @@ struct VariadicPack[ return rebind[result_ref.mlir_ref_type](ref_elt) @always_inline - fn each[func: fn[T: element_trait] (T) -> None](self): + fn each[func: fn[T: element_trait] (T) capturing -> None](self): """Apply a function to each element of the pack in order. This applies the specified function (which must be parametric on the element type) to each element of the pack, from the first element to the last, passing diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index f8602a4810..ac461cd088 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -337,70 +337,6 @@ fn _put(x: DType): # ===----------------------------------------------------------------------=== # -struct _StringableTuple[*Ts: Stringable](Sized): - alias _type = __mlir_type[ - `!kgen.pack<:variadic<`, Stringable, `> `, Ts, `>` - ] - var storage: Self._type - - fn __init__(inout self, value: Self._type): - self.storage = value - - @staticmethod - fn _offset[i: Int]() -> Int: - constrained[i >= 0, "index must be positive"]() - - @parameter - if i == 0: - return 0 - else: - return _align_up( - Self._offset[i - 1]() - + _align_up(sizeof[Ts[i - 1]](), alignof[Ts[i - 1]]()), - alignof[Ts[i]](), - ) - - @always_inline - fn _print[i: Int](inout self, /, *, sep: StringLiteral = " "): - _put(sep) - _put(self._at[i]()) - - fn _at[i: Int](inout self) -> String: - alias offset = Self._offset[i]() - var i8ptr = Pointer.address_of(self).bitcast[Int8]().offset(offset) - return str(Reference(i8ptr[]).bitcast_element[Ts[i]]()[]) - - fn __len__(self) -> Int: - return len(VariadicList(Ts)) - - -@always_inline -fn _print_elements[ - T: Stringable, *Ts: Stringable -]( - first: T, - inout rest: _StringableTuple[Ts], - sep: StringLiteral = " ", - end: StringLiteral = "\n", - flush: Bool = False, -): - _put(str(first)) - - @parameter - fn each[i: Int](): - rest._print[i](sep=sep) - - unroll[each, len(VariadicList(Ts))]() - _put(end) - if flush: - _flush() - - -# ===----------------------------------------------------------------------=== # -# print -# ===----------------------------------------------------------------------=== # - - @no_inline fn print( *, sep: StringLiteral = " ", end: StringLiteral = "\n", flush: Bool = False @@ -441,7 +377,15 @@ fn print[ end: The String to write after printing the elements. flush: If set to true, then the stream is forcibly flushed. """ - var vals = _StringableTuple[Ts](rest) - _print_elements(first, vals, sep=sep, end=end) + _put(str(first)) + + @parameter + fn print_elt[T: Stringable](a: T): + _put(sep) + _put(a) + + rest.each[print_elt]() + + _put(end) if flush: _flush() diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 269f210349..db3d8ce9bc 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -28,7 +28,7 @@ from utils import StringRef from utils.index import StaticIntTuple from utils.static_tuple import StaticTuple -from .io import _snprintf, _snprintf_scalar, _StringableTuple +from .io import _snprintf, _snprintf_scalar # ===----------------------------------------------------------------------===# # Utilties @@ -615,11 +615,11 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): curr += self + String(elems[i]) return curr - fn join[*Stringables: Stringable](self, *elems: *Stringables) -> String: + fn join[*Types: Stringable](self, *elems: *Types) -> String: """Joins string elements using the current string as a delimiter. Parameters: - Stringables: The Stringable types. + Types: The types of the elements. Args: elems: The input values. @@ -627,21 +627,19 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): Returns: The joined string. """ - alias types = VariadicList(Stringables) - alias count = len(types) - var args = _StringableTuple(elems) - - if count == 0: - return "" - - var result = args._at[0]() + var result: String = "" + var is_first = True @parameter - fn each[i: Int](): - result += self + args._at[i + 1]() - - unroll[each, count - 1]() + fn add_elt[T: Stringable](a: T): + if is_first: + is_first = False + else: + result += self + result += str(a) + + elems.each[add_elt]() return result fn _strref_dangerous(self) -> StringRef: diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_anypointer.mojo index 52c0d0583f..19a14ce401 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_anypointer.mojo @@ -40,10 +40,9 @@ fn test_anypointer_of_move_only_type(): ptr.emplace_value(MoveOnlyType(42)) # CHECK: moved 42 var value = ptr.take_value() - # NOTE: Destructor is called before `print`. - # CHECK: deleted 42 # CHECK: value 42 print("value", value.value) + # CHECK: deleted 42 ptr.free() From 787a585cc490fc6a9259f12ffca8f01613d194f1 Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Thu, 4 Apr 2024 10:00:10 -0700 Subject: [PATCH 0090/2019] [mojo-stdlib] Parameterize `Reference` on `AddressSpace` (#36561) Fixes [Internal link] - Adds an `address_space` parameter to `Reference` - Wires `address_space` through `Reference` methods - Updates `_LITRef` helper to speak `AddressSpace` types instead of an MLIR type --------- Co-authored-by: Chris Lattner MODULAR_ORIG_COMMIT_REV_ID: 887d6b06b1fa807fc4092fefafb627ed657bb524 --- stdlib/src/memory/memory.mojo | 4 +-- stdlib/src/memory/unsafe.mojo | 57 +++++++++++++++++++++++++++-------- stdlib/src/utils/variant.mojo | 4 +-- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 3604762f15..0cc9152742 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -445,14 +445,14 @@ fn stack_allocation[ count = count.value, _type = Pointer[type, address_space].pointer_type, alignment = alignment.value, - address_space = address_space.value().value, + address_space = address_space._value.value, ]() else: return __mlir_op.`pop.stack_allocation`[ count = count.value, _type = Pointer[type, address_space].pointer_type, alignment = alignment.value, - address_space = address_space.value().value, + address_space = address_space._value.value, ]() diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index ccd05eab31..6416a22434 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -432,7 +432,7 @@ struct _LITRef[ element_type: AnyType, elt_is_mutable: __mlir_type.i1, lifetime: AnyLifetime[elt_is_mutable].type, - addr_space: __mlir_type.index = Int(0).__mlir_index__(), + address_space: AddressSpace = AddressSpace.GENERIC, ]: alias type = __mlir_type[ `!lit.ref<`, @@ -440,7 +440,7 @@ struct _LITRef[ `, `, lifetime, `, `, - addr_space, + address_space._value.value, `>`, ] @@ -451,6 +451,7 @@ struct Reference[ type: AnyType, is_mutable: __mlir_type.i1, lifetime: AnyLifetime[is_mutable].type, + address_space: AddressSpace = AddressSpace.GENERIC, ]: """Defines a non-nullable safe reference. @@ -458,9 +459,12 @@ struct Reference[ type: Type of the underlying data. is_mutable: Whether the referenced data may be mutated through this. lifetime: The lifetime of the reference. + address_space: The address space of the referenced data. """ - alias mlir_ref_type = _LITRef[type, is_mutable, lifetime].type + alias mlir_ref_type = _LITRef[ + type, is_mutable, lifetime, address_space + ].type var value: Self.mlir_ref_type """The underlying MLIR reference.""" @@ -495,7 +499,7 @@ struct Reference[ # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType # disagreement. @always_inline("nodebug") - fn get_unsafe_pointer(self) -> Pointer[type]: + fn get_unsafe_pointer(self) -> Pointer[type, address_space]: """Constructs a Pointer from a safe reference. Returns: @@ -504,7 +508,7 @@ struct Reference[ var ptr_with_trait = __mlir_op.`lit.ref.to_pointer`(self.value) # Work around AnyRefType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ - _type = Pointer[type].pointer_type + _type = Pointer[type, address_space].pointer_type ](ptr_with_trait) @always_inline("nodebug") @@ -522,7 +526,7 @@ struct Reference[ @always_inline("nodebug") fn bitcast_element[ new_element_type: AnyType - ](self) -> Reference[new_element_type, is_mutable, lifetime]: + ](self) -> Reference[new_element_type, is_mutable, lifetime, address_space]: """Cast the reference to one of another element type, but the same lifetime, mutability, and address space. @@ -536,10 +540,18 @@ struct Reference[ # to KGEN pointer. var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[`!kgen.pointer<`, new_element_type, `>`] + _type = __mlir_type[ + `!kgen.pointer<`, + new_element_type, + `,`, + address_space._value.value, + `>`, + ] ](kgen_ptr) return __mlir_op.`lit.ref.from_pointer`[ - _type = _LITRef[new_element_type, is_mutable, lifetime].type + _type = _LITRef[ + new_element_type, is_mutable, lifetime, address_space + ].type ](dest_ptr) fn destroy_element_unsafe(self): @@ -554,11 +566,30 @@ struct Reference[ is_mutable, "cannot use 'unsafe_destroy_element' on immutable references", ]() + + # This method can only work on address space 0, because the __del__ + # method that we need to invoke will take 'self' in address space zero. + constrained[ + address_space == AddressSpace.GENERIC, + "cannot use 'destroy_element_unsafe' on arbitrary address spaces", + ]() + # Project to an owned raw pointer, allowing the compiler to know it is to # be destroyed. - # TODO: Use AnyPointer, but it requires a Movable element. var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) - _ = __get_address_as_owned_value(kgen_ptr) + + # Bitcast to address space zero since the inserted __del__ call will only + # work with address space zero. + var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ + _type = __mlir_type[ + `!kgen.pointer<`, + type, + `>`, + ] + ](kgen_ptr) + + # TODO: Use AnyPointer, but it requires a Movable element. + _ = __get_address_as_owned_value(dest_ptr) # FIXME: This should be a method on Reference, it is placed here because we need @@ -601,7 +632,7 @@ struct Pointer[ """ alias pointer_type = __mlir_type[ - `!kgen.pointer<`, type, `,`, address_space.value().value, `>` + `!kgen.pointer<`, type, `,`, address_space._value.value, `>` ] var address: Self.pointer_type @@ -611,7 +642,7 @@ struct Pointer[ type, __mlir_attr.`1: i1`, __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, - address_space.value().value, + address_space, ].type @always_inline("nodebug") @@ -1073,7 +1104,7 @@ struct DTypePointer[ `!kgen.pointer,`, - address_space.value().value, + address_space._value.value, `>`, ], ): diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 66c473cfab..6daa969745 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -160,9 +160,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn _get_state[ is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] - ](self: _LITRef[Self, is_mut, lt, Int(0).value].type) -> Reference[ - Int8, is_mut, lt - ]: + ](self: _LITRef[Self, is_mut, lt].type) -> Reference[Int8, is_mut, lt]: return ( Reference(self) .bitcast_element[Int8]() From a6bdb4034ca9d00eaf0552e4f44d40ac1e291486 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 4 Apr 2024 10:19:15 -0700 Subject: [PATCH 0091/2019] [mojo-lang] Document variadic packs work in changelog. (#36781) MODULAR_ORIG_COMMIT_REV_ID: 1b22c60e8429217ec8f3e37a28e81f970a04a6c2 --- docs/changelog.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 1fb5fbef4d..5df61b88e9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -15,6 +15,21 @@ and tools. Please add any significant user-visible changes here. ### ⭐️ New +- Heterogenous variadic pack arguments now work reliably even with memory types, + and have a more convenient API to use, as defined on the `VariadicPack` type. + For example, a simplified version of `print` can be implemented as: + + ```mojo + fn print[T: Stringable, *Ts: Stringable](first: T, *rest: *Ts): + print_string(str(first)) + + @parameter + fn print_elt[T: Stringable](a: T): + print_string(" ") + print_string(a) + rest.each[print_elt]() + ``` + - The `sys` module now contains an `exit` function that would exit a Mojo program with the specified error code. From 0a71335108450dfaf18491f1f18646772928908f Mon Sep 17 00:00:00 2001 From: James Decker Date: Thu, 4 Apr 2024 16:53:58 -0600 Subject: [PATCH 0092/2019] [mblack] Fix tokenization error in mblack (#36826) We goofed when adding backtick support and caused mblack to parse `!` incorrectly. Running mblack on the repo after making this change also caused a bunch of other files to need formatting, so this PR includes those as well. Fixes https://github.com/modularml/mojo/issues/1963. MODULAR_ORIG_COMMIT_REV_ID: daad7e9267589460a01cdf9612d8d34134479de9 --- docs/changelog.md | 3 +++ stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/simd.mojo | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5df61b88e9..502d60bd97 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -102,3 +102,6 @@ and tools. Please add any significant user-visible changes here. - [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac has been fixed. + +- [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed + and formatted correctly by `mojo format`. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 297e3e2ef1..ce8859e921 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -616,7 +616,7 @@ struct Int(Intable, Stringable, KeyElement, Boolable): var x = self var n = rhs while n > 0: - if n&1 != 0: + if n & 1 != 0: res *= x x *= x n >>= 1 diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7b801eefde..7c23117ed2 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2225,7 +2225,7 @@ fn _pow[ var x = lhs[i] var n = rhs[i] while n > 0: - if n&1 != 0: + if n & 1 != 0: res *= x x *= x n >>= 1 From 3ff9db255f678cf6a12a4e4ca263315b68db6803 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 4 Apr 2024 16:40:17 -0700 Subject: [PATCH 0093/2019] [docs] Delete playground deprecation notice (#36842) That playground is gone now. MODULAR_ORIG_COMMIT_REV_ID: 097e1bd349e1a181cf1526732d9f4f8ec0c3071a --- docs/manual/get-started/index.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/manual/get-started/index.md b/docs/manual/get-started/index.md index 3fe41ff422..0f226917f7 100644 --- a/docs/manual/get-started/index.md +++ b/docs/manual/get-started/index.md @@ -188,13 +188,6 @@ brew upgrade modular Instead of downloading the Mojo SDK, you can also experiment with Mojo in our online [Playground](/mojo/playground). -:::note - -The older [JupyterLab-based Playground](https://playground.modular.com) is still -available until March 20th. - -::: - ### What to expect The Mojo Playground is a simple online editor where you can test out Mojo From 54d8033647c5a93c8c63dc238a0a9403932650d8 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 4 Apr 2024 23:32:01 -0700 Subject: [PATCH 0094/2019] [mojo-lang] Move testsuite off `AnyRegType` packs. (#36884) This moves the testsuite off of `AnyRegType` packs and onto `AnyType` packs as an initial step to making `AnyRegType` packs invalid. MODULAR_ORIG_COMMIT_REV_ID: 79b5189ec41b113965c829837258680b9dcdbbec --- stdlib/src/builtin/builtin_list.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index e26d643c79..4dab05f7f6 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -419,7 +419,6 @@ struct VariadicPack[ lifetime: AnyLifetime[elt_is_mutable].type, element_trait: _AnyTypeMetaType, *element_types: element_trait, - # TODO: Add address_space when Reference supports it. ](Sized): """A utility class to access variadic pack arguments and provide an API for doing things with them. From 425df0c5f9b9a843905120c3795d6bc6c2caebbe Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 5 Apr 2024 06:48:33 -0700 Subject: [PATCH 0095/2019] [stdlib] Adding AddressSpace support to AnyPointer. (#36552) Adding an AddressSpace parameter to AnyPointer to make it usable in GPU contexts. This change includes fixes to the Dict implementation that resolve compilation failures. Also, fixes to Mojo parser tests that are validating generated mlir that changed due to the addition of the AddressSpace parameter. Closes #31636 MODULAR_ORIG_COMMIT_REV_ID: 8ecb40292c2113cd19fc99c590910a0952b542c4 --- stdlib/src/collections/dict.mojo | 34 +++++++++++++++++++------ stdlib/src/memory/anypointer.mojo | 33 ++++++++++++++++-------- stdlib/test/memory/test_anypointer.mojo | 10 ++++++++ 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e826162147..3311edbfb9 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -51,6 +51,7 @@ struct _DictEntryIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, + address_space: AddressSpace = AddressSpace.GENERIC, ]: """Iterator over immutable DictEntry references. @@ -59,6 +60,7 @@ struct _DictEntryIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the dictionary is mutable. dict_lifetime: The lifetime of the List + address_space: the address_space of the list """ alias imm_dict_lifetime = __mlir_attr[ @@ -101,6 +103,7 @@ struct _DictKeyIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, + address_space: AddressSpace = AddressSpace.GENERIC, ]: """Iterator over immutable Dict key references. @@ -109,14 +112,21 @@ struct _DictKeyIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List + address_space: The address space of the List """ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[K, __mlir_attr.`0: i1`, Self.imm_dict_lifetime] + alias ref_type = Reference[ + K, __mlir_attr.`0: i1`, Self.imm_dict_lifetime, address_space + ] - var iter: _DictEntryIter[K, V, dict_mutability, dict_lifetime] + alias dict_entry_iter = _DictEntryIter[ + K, V, dict_mutability, dict_lifetime, address_space + ] + + var iter: Self.dict_entry_iter fn __iter__(self) -> Self: return self @@ -126,9 +136,13 @@ struct _DictKeyIter[ var mlir_ptr = __mlir_op.`lit.ref.to_pointer`( Reference(entry_ref[].key).value ) - var key_ptr = AnyPointer[K] { + var key_ptr = AnyPointer[ + K, address_space = Self.dict_entry_iter.address_space + ] { value: __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[K].pointer_type + _type = AnyPointer[ + K, address_space = Self.dict_entry_iter.address_space + ].pointer_type ](mlir_ptr) } return __mlir_op.`lit.ref.from_pointer`[ @@ -145,6 +159,7 @@ struct _DictValueIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, + address_space: AddressSpace = AddressSpace.GENERIC, ]: """Iterator over Dict value references. These are mutable if the dict is mutable. @@ -154,11 +169,14 @@ struct _DictValueIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List + address_space: The address space of the List """ - alias ref_type = Reference[V, dict_mutability, dict_lifetime] + alias ref_type = Reference[V, dict_mutability, dict_lifetime, address_space] - var iter: _DictEntryIter[K, V, dict_mutability, dict_lifetime] + var iter: _DictEntryIter[ + K, V, dict_mutability, dict_lifetime, address_space + ] fn __iter__(self) -> Self: return self @@ -168,9 +186,9 @@ struct _DictValueIter[ var mlir_ptr = __mlir_op.`lit.ref.to_pointer`( Reference(entry_ref[].value).value ) - var value_ptr = AnyPointer[V] { + var value_ptr = AnyPointer[V, address_space] { value: __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[V].pointer_type + _type = AnyPointer[V, address_space].pointer_type ](mlir_ptr) } return __mlir_op.`lit.ref.from_pointer`[ diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index 251f1275a4..f06c0c8fd6 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -23,20 +23,24 @@ from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc +from memory.unsafe import _LITRef @register_passable("trivial") -struct AnyPointer[T: Movable]( - Boolable, CollectionElement, Stringable, Intable, EqualityComparable -): +struct AnyPointer[ + T: Movable, address_space: AddressSpace = AddressSpace.GENERIC +](Boolable, CollectionElement, Stringable, Intable, EqualityComparable): """This is a pointer type that can point to any generic value that is movable. Parameters: T: The pointer element type, which must be movable. + address_space: The address space associated with the AnyPointer allocated memory. """ - alias pointer_type = __mlir_type[`!kgen.pointer<`, T, `>`] + alias pointer_type = __mlir_type[ + `!kgen.pointer<`, T, `,`, address_space._value.value, `>` + ] """The underlying pointer type.""" var value: Self.pointer_type """The underlying pointer.""" @@ -76,7 +80,11 @@ struct AnyPointer[T: Movable]( The pointer to the newly allocated array. """ return Self.__from_index( - int(_malloc[Int8](sizeof[T]() * count, alignment=alignof[T]())) + int( + _malloc[Int8, address_space=address_space]( + sizeof[T]() * count, alignment=alignof[T]() + ) + ) ) @staticmethod @@ -97,10 +105,12 @@ struct AnyPointer[T: Movable]( @always_inline fn free(self): """Free the memory referenced by the pointer.""" - Pointer[Int8].__from_index(int(self)).free() + Pointer[Int8, address_space=address_space].__from_index( + int(self) + ).free() @always_inline - fn bitcast[new_type: Movable](self) -> AnyPointer[new_type]: + fn bitcast[new_type: Movable](self) -> AnyPointer[new_type, address_space]: """Bitcasts the pointer to a different type. Parameters: @@ -113,10 +123,10 @@ struct AnyPointer[T: Movable]( @parameter if _mlirtype_is_eq[T, new_type](): - return rebind[AnyPointer[new_type]](self) + return rebind[AnyPointer[new_type, address_space]](self) return __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[new_type].pointer_type + _type = AnyPointer[new_type, address_space].pointer_type ](self.value) @always_inline @@ -312,7 +322,10 @@ struct AnyPointer[T: Movable]( # an immortal mutable lifetime, since we can't come up with a meaningful # lifetime for them anyway. alias mlir_ref_type = Reference[ - T, __mlir_attr.`1: i1`, __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>` + T, + __mlir_attr.`1: i1`, + __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, + address_space, ].mlir_ref_type @always_inline diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_anypointer.mojo index 19a14ce401..63606cb47e 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_anypointer.mojo @@ -135,6 +135,14 @@ def test_comparisons(): p1.free() +def test_anypointer_address_space(): + var p1 = AnyPointer[Int, AddressSpace(0)].alloc(1) + p1.free() + + var p2 = AnyPointer[Int, AddressSpace.GENERIC].alloc(1) + p2.free() + + def main(): test_address_of() @@ -148,3 +156,5 @@ def main(): test_anypointer_string() test_eq() test_comparisons() + + test_anypointer_address_space() From b03e0c87c251954d2752e2118b01a8897600f645 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 5 Apr 2024 09:22:35 -0700 Subject: [PATCH 0096/2019] [mojo-lang] Reject `AnyRegType` variadic packs, pushing to `AnyType` (#36906) `AnyRegType` variadic packs are a legacy feature that predated traits (and possibly memory only types in general). We are unifying on a single consistent representation built around `VariadicPack`. This takes the step of banning the old `AnyRegType` form in the parser. That said, we still have a few uses of the old form in the tree, so it keeps them alive when explicitly declared "borrowed". This will allow us to work through removing/replacing these cases incrementally. MODULAR_ORIG_COMMIT_REV_ID: 8e7642b4396f704020adabf48c2e7dfb01ec6e9d --- docs/changelog.md | 11 +++++++++++ stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/io.mojo | 9 +++++++-- stdlib/src/builtin/string.mojo | 5 ++++- stdlib/src/builtin/tuple.mojo | 2 +- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 502d60bd97..4fa3ee9248 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -80,6 +80,17 @@ and tools. Please add any significant user-visible changes here. ### ❌ Removed +- Support for "register only" variadic packs has been removed. Instead of + `AnyRegType`, please upgrade your code to `AnyType` in examples like this: + + ```mojo + fn [*Types: AnyRegType](*args: *Ts): ... + ``` + + This move gives you access to nicer API and has the benefit of being memory + safe and correct for non-trivial types. If you need specific APIs on the + types, please use the correct trait bound instead of `AnyType`. + - `List.pop_back()` has been removed. Use `List.pop()` instead which defaults to popping the last element in the list. diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 4dab05f7f6..56cdf5ac8e 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -36,7 +36,7 @@ struct ListLiteral[*Ts: AnyRegType](Sized): """The underlying storage for the list.""" @always_inline("nodebug") - fn __init__(inout self, *args: *Ts): + fn __init__(inout self, borrowed *args: *Ts): """Construct the list literal from the given values. Args: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index ac461cd088..7dfd4b0765 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -99,7 +99,7 @@ fn _flush(): @no_inline -fn _printf[*types: AnyRegType](fmt: StringLiteral, *arguments: *types): +fn _printf[*types: AnyRegType](fmt: StringLiteral, borrowed *arguments: *types): with _fdopen(_fdopen.STDOUT) as fd: _ = __mlir_op.`pop.external_call`[ func = "KGEN_CompilerRT_fprintf".value, @@ -121,7 +121,12 @@ fn _printf[*types: AnyRegType](fmt: StringLiteral, *arguments: *types): @no_inline fn _snprintf[ *types: AnyRegType -](str: Pointer[Int8], size: Int, fmt: StringLiteral, *arguments: *types) -> Int: +]( + str: Pointer[Int8], + size: Int, + fmt: StringLiteral, + borrowed *arguments: *types, +) -> Int: """Writes a format string into an output pointer. Args: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index db3d8ce9bc..f282c37330 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1088,7 +1088,10 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): fn _vec_fmt[ *types: AnyRegType ]( - str: AnyPointer[Int8], size: Int, fmt: StringLiteral, *arguments: *types + str: AnyPointer[Int8], + size: Int, + fmt: StringLiteral, + borrowed *arguments: *types, ) -> Int: return _snprintf(rebind[Pointer[Int8]](str), size, fmt, arguments) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 1c94d58ad4..068a9195bd 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -37,7 +37,7 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): """The underlying storage for the tuple.""" @always_inline("nodebug") - fn __init__(*args: *Ts) -> Self: + fn __init__(borrowed *args: *Ts) -> Self: """Construct the tuple. Args: From 633843d4f6048931a0cdca125326f03fef4403a2 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 5 Apr 2024 13:35:50 -0500 Subject: [PATCH 0097/2019] [Docs] Add section to style guide on inheriting Python inconsistencies (#36908) To avoid bikeshedding on the topic and have a place to point users to whenever the discussion comes up. MODULAR_ORIG_COMMIT_REV_ID: d35bcddaea49e7fcaa4ec5ec3ef7f88aa7b3ca42 --- stdlib/docs/style-guide.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index a7aff2e206..bf7b167727 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -121,6 +121,13 @@ struct MyStruct(Sized, Stringable): ## Code conventions +### Python Standard Library + +We want to be a good member of the Python family and aim to become a full +superset, so we inherit naming from the Python standard library, including any +inconsistencies. These naming inconsistencies are the only exceptions to the +naming conventions outlined below. + ### Identifier naming conventions There are several ways to capitalize and separate words, known as "case From 0cb789c0240b6ae852c1bdae639af21ce08af6f9 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Fri, 5 Apr 2024 22:59:58 -0700 Subject: [PATCH 0098/2019] [******][GPU] Constrain malloc/free on non-generic address spaces (#36966) Malloc and free do not make any sense except for the generic address space, so constrain the functions so that folks cannot call them. Also fixup the free in layout tensor which we can elide if we are not operating on generic address space MODULAR_ORIG_COMMIT_REV_ID: 112b8ac3ffb2da86ceaf1c99dd4737d29f444d6a --- stdlib/src/memory/memory.mojo | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 0cc9152742..bcf60b51f4 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -470,6 +470,10 @@ fn _malloc[ ](size: Int, /, *, alignment: Int = -1) -> Pointer[type, address_space]: @parameter if triple_is_nvidia_cuda(): + constrained[ + address_space == AddressSpace.GENERIC, + "address space must be generic", + ]() return external_call["malloc", Pointer[NoneType, address_space]]( size ).bitcast[type]() @@ -488,6 +492,10 @@ fn _malloc[ fn _free(ptr: Pointer): @parameter if triple_is_nvidia_cuda(): + constrained[ + ptr.address_space == AddressSpace.GENERIC, + "address space must be generic", + ]() external_call["free", NoneType](ptr.bitcast[NoneType]()) else: __mlir_op.`pop.aligned_free`(ptr.address) From 067edf556a4a57cdfd32a0c713a1b11348814a2f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Apr 2024 17:53:15 -0700 Subject: [PATCH 0099/2019] [mojo-lang] Simplify some code in `emitMLIROperatorCall`, NFC. (#37002) This just allows emitConstructorCall to infer the type arguments of TupleType instead of doing it manually. MODULAR_ORIG_COMMIT_REV_ID: dc48124f1dd3f259dd1a344c6260a3b2334ecc22 --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4fa3ee9248..88d612d11e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -84,7 +84,7 @@ and tools. Please add any significant user-visible changes here. `AnyRegType`, please upgrade your code to `AnyType` in examples like this: ```mojo - fn [*Types: AnyRegType](*args: *Ts): ... + fn your_function[*Types: AnyRegType](*args: *Ts): ... ``` This move gives you access to nicer API and has the benefit of being memory From 5ab0c66b4e4856fd8099de1f30349a0ce882abdc Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Apr 2024 18:31:49 -0700 Subject: [PATCH 0100/2019] [mojo-lang] Rename `(lit.ref|kgen).pack.get` -> `...pack.extract` (#37004) This renames this operation to align with the naming used by structs and LLVM. MODULAR_ORIG_COMMIT_REV_ID: 72fd9a2853c97de64e96f218c28372d9bdc6bbe6 --- stdlib/src/builtin/builtin_list.mojo | 4 ++-- stdlib/src/builtin/tuple.mojo | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 56cdf5ac8e..6320c715af 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -65,7 +65,7 @@ struct ListLiteral[*Ts: AnyRegType](Sized): The element at the given index. """ return rebind[T]( - __mlir_op.`kgen.pack.get`[index = i.value](self.storage) + __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) ) @@ -522,7 +522,7 @@ struct VariadicPack[ A reference to the element. The Reference's mutability follows the mutability of the pack argument convention. """ - var ref_elt = __mlir_op.`lit.ref.pack.get`[index = index.value]( + var ref_elt = __mlir_op.`lit.ref.pack.extract`[index = index.value]( self._value ) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 068a9195bd..2fbca8e8bb 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -78,7 +78,7 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): The tuple element at the requested index. """ return rebind[T]( - __mlir_op.`kgen.pack.get`[index = i.value](self.storage) + __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) ) @staticmethod From 3f5f24bf30b7a81f7556091962a0217e9e3ab5f9 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Apr 2024 20:48:28 -0700 Subject: [PATCH 0101/2019] [mojo-stdlib] Move `Tuple` to modern init syntax, replacing `-> Self` (#37007) This is a cleanup with no user visible change. MODULAR_ORIG_COMMIT_REV_ID: 9fd0a09e6b5e9a7e7fbd49b0a53f510638db03e2 --- stdlib/src/builtin/tuple.mojo | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 2fbca8e8bb..0cefa71207 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -37,25 +37,22 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): """The underlying storage for the tuple.""" @always_inline("nodebug") - fn __init__(borrowed *args: *Ts) -> Self: + fn __init__(inout self, borrowed *args: *Ts): """Construct the tuple. Args: args: Initial values. - - Returns: - Constructed tuple. """ - return Self {storage: args} + self.storage = args @always_inline("nodebug") - fn __copyinit__(existing: Self) -> Self: + fn __copyinit__(inout self, existing: Self): """Copy construct the tuple. - Returns: - Constructed tuple. + Args: + existing: The value to copy from. """ - return Self {storage: existing.storage} + self.storage = existing.storage @always_inline("nodebug") fn __len__(self) -> Int: From 206a13a204c4a1b53d008983d505b5fcd04e0ca9 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Apr 2024 21:25:44 -0700 Subject: [PATCH 0102/2019] [mojo-stdlib] Move `llvm_intrinsic` uses off of `Tuple`. (#37009) Multiresult uses of `llvm_intrinsic` are currently using Tuple to represent the result list, which conveniently works out given how lowering happens. However, `Tuple` will move to a new representation and needs to be memory only to do it. Introduce a stopgap solution to get these uses off of it. MODULAR_ORIG_COMMIT_REV_ID: 9ff10499f5787c411b2b0b8471acc424aa03eb76 --- stdlib/src/builtin/simd.mojo | 4 ++-- stdlib/src/sys/__init__.mojo | 1 + stdlib/src/sys/intrinsics.mojo | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 7c23117ed2..f7d4ff7678 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys import llvm_intrinsic +from sys import llvm_intrinsic, _RegisterPackType from sys.info import has_neon, is_x86, simdwidthof from builtin.hash import _hash_simd @@ -1701,7 +1701,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var res = llvm_intrinsic[ "llvm.experimental.vector.deinterleave2", - (SIMD[type, size // 2], SIMD[type, size // 2]), + _RegisterPackType[SIMD[type, size // 2], SIMD[type, size // 2]], ](self) return StaticTuple[SIMD[type, size // 2], 2]( res.get[0, SIMD[type, size // 2]](), diff --git a/stdlib/src/sys/__init__.mojo b/stdlib/src/sys/__init__.mojo index 01ee0211ef..53d70901cd 100644 --- a/stdlib/src/sys/__init__.mojo +++ b/stdlib/src/sys/__init__.mojo @@ -62,6 +62,7 @@ from .intrinsics import ( compressed_store, strided_load, strided_store, + _RegisterPackType, ) from .param_env import ( is_defined, diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index e861d85ac2..909d0fccae 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1271,3 +1271,28 @@ fn _mlirtype_is_eq[t1: AnyRegType, t2: AnyRegType]() -> Bool: `> : !kgen.type`, `> : i1`, ] + + +# ===----------------------------------------------------------------------=== # +# Transitional type used for llvm_intrinsic +# ===----------------------------------------------------------------------=== # + + +@register_passable +struct _RegisterPackType[*a: AnyRegType]: + var storage: __mlir_type[`!kgen.pack<`, a, `>`] + + @always_inline("nodebug") + fn get[i: Int, T: AnyRegType](self) -> T: + """Get the element. + + Parameters: + i: The element index. + T: The element type. + + Returns: + The tuple element at the requested index. + """ + return rebind[T]( + __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) + ) From 430ff0383d7065737259a8b255192e4dc7814b34 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Apr 2024 22:36:14 -0700 Subject: [PATCH 0103/2019] [mojo-stdlib] Make `Tuple` a memory type. (#37010) With other steps done, we can name drop register passability of Tuple. This is necessary because we want Tuple to be able to handle other non-register types as well. MODULAR_ORIG_COMMIT_REV_ID: 5786484c47525437b4376b1e18e0d4af422fc7fd --- stdlib/src/builtin/tuple.mojo | 84 ++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 0cefa71207..4a35a329b2 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -23,7 +23,6 @@ from utils._visualizers import lldb_formatter_wrapping_type @lldb_formatter_wrapping_type -@register_passable struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): """The type of a literal tuple expression. @@ -36,6 +35,89 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): var storage: __mlir_type[`!kgen.pack<`, Ts, `>`] """The underlying storage for the tuple.""" + @always_inline("nodebug") + fn __init__(inout self, borrowed *args: *Ts): + """Construct the tuple. + + Args: + args: Initial values. + """ + self.storage = args + + @always_inline("nodebug") + fn __copyinit__(inout self, existing: Self): + """Copy construct the tuple. + + Args: + existing: The value to copy from. + """ + self.storage = existing.storage + + @always_inline("nodebug") + fn __moveinit__(inout self, owned existing: Self): + """Move construct the tuple. + + Args: + existing: The value to move from. + """ + self.storage = existing.storage + + @always_inline("nodebug") + fn __len__(self) -> Int: + """Get the number of elements in the tuple. + + Returns: + The tuple length. + """ + return __mlir_op.`pop.variadic.size`(Ts) + + @always_inline("nodebug") + fn get[i: Int, T: AnyRegType](self) -> T: + """Get a tuple element. + + Parameters: + i: The element index. + T: The element type. + + Returns: + The tuple element at the requested index. + """ + return rebind[T]( + __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) + ) + + @staticmethod + fn _offset[i: Int]() -> Int: + constrained[i >= 0, "index must be positive"]() + + @parameter + if i == 0: + return 0 + else: + return _align_up( + Self._offset[i - 1]() + + _align_up(sizeof[Ts[i - 1]](), alignof[Ts[i - 1]]()), + alignof[Ts[i]](), + ) + + +# ===----------------------------------------------------------------------===# +# _DeprecatedRegisterTuple +# ===----------------------------------------------------------------------===# + + +@register_passable +struct _DeprecatedRegisterTuple[*Ts: AnyRegType](Sized, CollectionElement): + """This is a deprecated register-passable tuple type, maintained to keep + compatibility with older code that can't handle memory-only tuple type yet. + + Parameters: + Ts: The elements type. + """ + + var storage: __mlir_type[`!kgen.pack<`, Ts, `>`] + """The underlying storage for the tuple.""" + @always_inline("nodebug") fn __init__(inout self, borrowed *args: *Ts): """Construct the tuple. From 3c1705cdcb477ec7050e685155a2b42fd803a343 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 6 Apr 2024 23:34:01 -0700 Subject: [PATCH 0104/2019] [mojo-stdlib] Remove the need type in `yourIntTuple.get[1, Int]()` (#37014) It is very common to work with tuples of known element types along with statically known indexes. The Mojo type system is strong enough to propagate this through parametric types. Add an overload of get that doesn't take a type parameter - but keep that form for when a rebind is needed. MODULAR_ORIG_COMMIT_REV_ID: 60020ed2c3837dd3161473638083beb90d49adf8 --- stdlib/src/builtin/tuple.mojo | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 4a35a329b2..8f043c765a 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -73,7 +73,7 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): @always_inline("nodebug") fn get[i: Int, T: AnyRegType](self) -> T: - """Get a tuple element. + """Get a tuple element and rebind to the specified type. Parameters: i: The element index. @@ -82,9 +82,19 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): Returns: The tuple element at the requested index. """ - return rebind[T]( - __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) - ) + return rebind[T](self.get[i]()) + + @always_inline("nodebug") + fn get[i: Int](self) -> Ts[i.value]: + """Get a tuple element. + + Parameters: + i: The element index. + + Returns: + The tuple element at the requested index. + """ + return __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) @staticmethod fn _offset[i: Int]() -> Int: From 0fe35f8cbe7e39e6c072c3409e38dfc009a6bbf3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 7 Apr 2024 10:02:05 -0700 Subject: [PATCH 0105/2019] [mojo-docs] Mention `Tuple` improvement in changelog (#37023) MODULAR_ORIG_COMMIT_REV_ID: 0c3f29839c864a180800b43e39b1855aaf5e872e --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 88d612d11e..b7d8f0592c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -65,6 +65,10 @@ and tools. Please add any significant user-visible changes here. - `Dict` now has a `update()` method to update keys/values from another `Dict`. +- `Tuple.get` now supports a form that just takes an element index but does not + require you to specify the result type. Instead of `tup.get[1, Int]()` you + can now just use `tup.get[1]()`. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From 591075465399aaa8b5032bc283f48b1b36e674bd Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 7 Apr 2024 10:18:09 -0700 Subject: [PATCH 0106/2019] [******] Move `LoopBoundSpec` off `_DeprecatedRegisterTuple` NFC (#37022) This switches to using `StaticIntTuple` as recommended by Abdul. MODULAR_ORIG_COMMIT_REV_ID: 080285b64caf7e90cdec72027420d95de6dc65c6 --- stdlib/src/builtin/tuple.mojo | 74 ----------------------------------- 1 file changed, 74 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 8f043c765a..f3c7783bbe 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -111,80 +111,6 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): ) -# ===----------------------------------------------------------------------===# -# _DeprecatedRegisterTuple -# ===----------------------------------------------------------------------===# - - -@register_passable -struct _DeprecatedRegisterTuple[*Ts: AnyRegType](Sized, CollectionElement): - """This is a deprecated register-passable tuple type, maintained to keep - compatibility with older code that can't handle memory-only tuple type yet. - - Parameters: - Ts: The elements type. - """ - - var storage: __mlir_type[`!kgen.pack<`, Ts, `>`] - """The underlying storage for the tuple.""" - - @always_inline("nodebug") - fn __init__(inout self, borrowed *args: *Ts): - """Construct the tuple. - - Args: - args: Initial values. - """ - self.storage = args - - @always_inline("nodebug") - fn __copyinit__(inout self, existing: Self): - """Copy construct the tuple. - - Args: - existing: The value to copy from. - """ - self.storage = existing.storage - - @always_inline("nodebug") - fn __len__(self) -> Int: - """Get the number of elements in the tuple. - - Returns: - The tuple length. - """ - return __mlir_op.`pop.variadic.size`(Ts) - - @always_inline("nodebug") - fn get[i: Int, T: AnyRegType](self) -> T: - """Get a tuple element. - - Parameters: - i: The element index. - T: The element type. - - Returns: - The tuple element at the requested index. - """ - return rebind[T]( - __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) - ) - - @staticmethod - fn _offset[i: Int]() -> Int: - constrained[i >= 0, "index must be positive"]() - - @parameter - if i == 0: - return 0 - else: - return _align_up( - Self._offset[i - 1]() - + _align_up(sizeof[Ts[i - 1]](), alignof[Ts[i - 1]]()), - alignof[Ts[i]](), - ) - - # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # From f14c28cf8b1c1e24deae7a4b25aabe4263b86417 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 7 Apr 2024 14:29:14 -0700 Subject: [PATCH 0107/2019] [mojo-stdlib] Improvements to `Reference` related things. (#37028) This patch: 1) gives an `address_space_cast` method to `Reference` and `AnyPointer` 2) gives a constructor to `AnyPointer` that converts from a Reference. 3) cleans up some code in dict.mojo to use this stuff. 4) fixes a compiler bug where we didn't consider conversion from guaranteed mutable lifetime to conditionally mutable lifetime to be safe. MODULAR_ORIG_COMMIT_REV_ID: 1f14b5dcca8075f409055493bfef63811446fe98 --- docs/changelog.md | 6 ++++++ stdlib/src/collections/dict.mojo | 32 ++++++------------------------ stdlib/src/memory/anypointer.mojo | 30 ++++++++++++++++++++++++++++ stdlib/src/memory/unsafe.mojo | 33 ++++++++++++++++++++++++++++++- stdlib/src/utils/variant.mojo | 7 +------ 5 files changed, 75 insertions(+), 33 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b7d8f0592c..885720f150 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -69,6 +69,12 @@ and tools. Please add any significant user-visible changes here. require you to specify the result type. Instead of `tup.get[1, Int]()` you can now just use `tup.get[1]()`. +- `Reference` interoperates with unsafe code better: `AnyPointer` now has a + constructor that forms it from `Reference` directly (inferring element type + and address space). `AnyPointer` can convert to an immortal mutable + `Reference` with `yourptr[]`. Both `Reference` and `AnyPointer` now have an + `address_space_cast` method like `Pointer`. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 3311edbfb9..2715d40fc7 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -133,21 +133,8 @@ struct _DictKeyIter[ fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() - var mlir_ptr = __mlir_op.`lit.ref.to_pointer`( - Reference(entry_ref[].key).value - ) - var key_ptr = AnyPointer[ - K, address_space = Self.dict_entry_iter.address_space - ] { - value: __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[ - K, address_space = Self.dict_entry_iter.address_space - ].pointer_type - ](mlir_ptr) - } - return __mlir_op.`lit.ref.from_pointer`[ - _type = Self.ref_type.mlir_ref_type - ](key_ptr.value) + var anyptr = AnyPointer(Reference(entry_ref[].key)) + return anyptr.address_space_cast[Self.dict_entry_iter.address_space]()[] fn __len__(self) -> Int: return self.iter.__len__() @@ -183,17 +170,10 @@ struct _DictValueIter[ fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() - var mlir_ptr = __mlir_op.`lit.ref.to_pointer`( - Reference(entry_ref[].value).value - ) - var value_ptr = AnyPointer[V, address_space] { - value: __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[V, address_space].pointer_type - ](mlir_ptr) - } - return __mlir_op.`lit.ref.from_pointer`[ - _type = Self.ref_type.mlir_ref_type - ](value_ptr.value) + # Cast through a pointer to grant additional mutability and switch + # address spaces out. + var anyptr = AnyPointer(Reference(entry_ref[].value)) + return anyptr.address_space_cast[address_space]()[] fn __len__(self) -> Int: return self.iter.__len__() diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index f06c0c8fd6..21b378346d 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -68,6 +68,18 @@ struct AnyPointer[ """ return Self {value: value} + @always_inline + fn __init__(value: Reference[T, _, _, address_space]) -> Self: + """Create an unsafe AnyPointer from a safe Reference. + + Args: + value: The input pointer to construct with. + + Returns: + A null pointer. + """ + return Self {value: __mlir_op.`lit.ref.to_pointer`(value.value)} + @staticmethod @always_inline fn alloc(count: Int) -> Self: @@ -129,6 +141,24 @@ struct AnyPointer[ _type = AnyPointer[new_type, address_space].pointer_type ](self.value) + @always_inline + fn address_space_cast[ + new_address_space: AddressSpace + ](self) -> AnyPointer[T, new_address_space]: + """Bitcasts the pointer to a different address space. + + Parameters: + new_address_space: The address space of the result. + + Returns: + A new pointer with the same type and address, but a new address + space. + """ + + return __mlir_op.`pop.pointer.bitcast`[ + _type = AnyPointer[T, new_address_space].pointer_type + ](self.value) + @always_inline fn take_value(self) -> T: """Move the value at the pointer out. diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 6416a22434..61f878aea2 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -497,7 +497,7 @@ struct Reference[ return self.value # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType - # disagreement. + # disagreement. Use AnyPointer instead! @always_inline("nodebug") fn get_unsafe_pointer(self) -> Pointer[type, address_space]: """Constructs a Pointer from a safe reference. @@ -538,6 +538,7 @@ struct Reference[ """ # We don't have a generalized lit.ref.cast operation, so convert through # to KGEN pointer. + # FIXME: We can't use AnyPointer here, because it requires T <- Movable. var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ _type = __mlir_type[ @@ -554,6 +555,36 @@ struct Reference[ ].type ](dest_ptr) + @always_inline + fn address_space_cast[ + new_address_space: AddressSpace + ](self) -> Reference[type, is_mutable, lifetime, new_address_space]: + """Cast the reference to one of another address space, but the same + element type, lifetime, and mutability. + + Parameters: + new_address_space: The address space of the result. + + Returns: + The new reference. + """ + # We don't have a generalized lit.ref.cast operation, so convert through + # to KGEN pointer. + # FIXME: We can't use AnyPointer here, because it requires T <- Movable. + var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) + var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ + _type = __mlir_type[ + `!kgen.pointer<`, + type, + `,`, + new_address_space._value.value, + `>`, + ] + ](kgen_ptr) + return __mlir_op.`lit.ref.from_pointer`[ + _type = _LITRef[type, is_mutable, lifetime, new_address_space].type + ](dest_ptr) + fn destroy_element_unsafe(self): """This unsafe operation runs the destructor of the element addressed by this reference. This is equivalent to `x->~Type()` syntax in C++. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 6daa969745..49775b0bbf 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -151,12 +151,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): constrained[ Self._check[T]() != Self._sentinel, "not a union element type" ]() - var ptr = Reference(self._impl).get_unsafe_pointer().address - var result = AnyPointer[T]() - result.value = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[`!kgen.pointer<`, T, `>`] - ](ptr) - return result + return Reference(self._impl).bitcast_element[T]() fn _get_state[ is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] From 998f260bd25034a33f4b45bb65c903e27b01f67d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 7 Apr 2024 15:59:13 -0700 Subject: [PATCH 0108/2019] [mojo-lang] Introduce a `__get_mvalue_as_litref` magic function. (#37031) This introduces another magic function, which exposes the underlying `!lit.ref` of an mvalue. This can be used to mark fields as being initialized even when they are not using `lit.ownership.mark_uninitialized`. MODULAR_ORIG_COMMIT_REV_ID: 9fdc2db89aad179ac2f473e0f9bd9bfc1a0477b6 --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 885720f150..7a53e69067 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -75,6 +75,12 @@ and tools. Please add any significant user-visible changes here. `Reference` with `yourptr[]`. Both `Reference` and `AnyPointer` now have an `address_space_cast` method like `Pointer`. +- A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to + the underlying memory representation as a `!lit.ref` value without checking + initialization status of the underlying value. This is useful in very + low-level logic but isn't designed for general usability and will likely + change in the future. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From 4b1c7f8aa7f4abb88138000fc2b6d20ac52d0452 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 13:53:25 -0700 Subject: [PATCH 0109/2019] [mojo-stdlib] Remove `Movable` requirement from `AnyPointer` (#37059) This removes the `Movable` requirement from `AnyPointer`, allowing it to be adopted in place of `Pointer`. This requires moving a few methods to being global functions. While doing this, rename them for clarity. This also removes the `Movable` requirement from `abort()`. This adds a `destroy_pointee` function which is more clear, but also more efficient than the `_ = ptr.takeValue()` idiom: it just calls del, not moveinit+del. MODULAR_ORIG_COMMIT_REV_ID: 2ddef633fe90115c713b48b4be556972ec5c20db --- docs/changelog.md | 7 + docs/manual/lifecycle/death.ipynb | 2 +- stdlib/src/collections/list.mojo | 30 ++--- stdlib/src/memory/anypointer.mojo | 168 +++++++++++++++--------- stdlib/src/os/os.mojo | 9 +- stdlib/src/utils/variant.mojo | 11 +- stdlib/test/memory/test_anypointer.mojo | 16 +-- 7 files changed, 145 insertions(+), 98 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7a53e69067..bd9867e745 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -93,6 +93,13 @@ and tools. Please add any significant user-visible changes here. lead to a crash. You can work around this by initializing to a dummy value and overwriting later. This limitation only applies to top level variables, variables in functions work as they always have. +- The `AnyPointer` type has several changes, including: + 1) The element type can now be `AnyType`, it doesn't require `Movable`. + 2) Because of this, the `take_value`, `emplace_value`, and `move_into` methods + have been changed to be top-level functions, and were renamed to + `move_from_pointee`, `initialize_pointee` and `move_pointee` respectively. + 3) A new `destroy_pointee` function runs the destructor on the pointee. + 4) `AnyPointer` can be initialized from a `Reference` as mentioned above. ### ❌ Removed diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 88ee371147..6fea545a95 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -284,7 +284,7 @@ "```mojo\n", "fn __del__(owned self):\n", " for i in range(self.size):\n", - " _ = (self.data + i).take_value()\n", + " destroy_pointee(self.data + i)\n", " self.data.free()\n", "```" ] diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 21387a5cdd..31b1017c30 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,7 +20,7 @@ from collections import List """ -from memory.anypointer import AnyPointer +from memory.anypointer import * from memory.unsafe import Reference # ===----------------------------------------------------------------------===# @@ -148,7 +148,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): fn __del__(owned self): """Destroy all elements in the list and free its memory.""" for i in range(self.size): - _ = (self.data + i).take_value() + destroy_pointee(self.data + i) if self.data: self.data.free() @@ -165,7 +165,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): var new_data = AnyPointer[T].alloc(new_capacity) for i in range(self.size): - (new_data + i).emplace_value((self.data + i).take_value()) + move_pointee(src=self.data + i, dst=new_data + i) if self.data: self.data.free() @@ -181,7 +181,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): """ if self.size >= self.capacity: self._realloc(_max(1, self.capacity * 2)) - (self.data + self.size).emplace_value(value^) + initialize_pointee(self.data + self.size, value^) self.size += 1 @always_inline @@ -217,7 +217,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): # `other` list into this list using a single `T.__moveinit()__` # call, without moving into an intermediate temporary value # (avoiding an extra redundant move constructor call). - src_ptr.move_into(dest_ptr) + move_pointee(src=src_ptr, dst=dest_ptr) dest_ptr = dest_ptr + 1 @@ -241,9 +241,9 @@ struct List[T: CollectionElement](CollectionElement, Sized): if i < 0: normalized_idx += len(self) - var ret_val = (self.data + normalized_idx).take_value() + var ret_val = move_from_pointee(self.data + normalized_idx) for j in range(normalized_idx + 1, self.size): - (self.data + j).move_into(self.data + j - 1) + move_pointee(src=self.data + j, dst=self.data + j - 1) self.size -= 1 if self.size * 4 < self.capacity: if self.capacity > 1: @@ -278,9 +278,9 @@ struct List[T: CollectionElement](CollectionElement, Sized): """ self.reserve(new_size) for i in range(new_size, self.size): - _ = (self.data + i).take_value() + destroy_pointee(self.data + i) for i in range(self.size, new_size): - (self.data + i).emplace_value(value) + initialize_pointee(self.data + i, value) self.size = new_size fn reverse(inout self): @@ -314,9 +314,9 @@ struct List[T: CollectionElement](CollectionElement, Sized): var earlier_ptr = self.data + earlier_idx var later_ptr = self.data + later_idx - var tmp = earlier_ptr.take_value() - later_ptr.move_into(earlier_ptr) - later_ptr.emplace_value(tmp^) + var tmp = move_from_pointee(earlier_ptr) + move_pointee(src=later_ptr, dst=earlier_ptr) + initialize_pointee(later_ptr, tmp^) earlier_idx += 1 later_idx -= 1 @@ -324,7 +324,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): fn clear(inout self): """Clears the elements in the list.""" for i in range(self.size): - _ = (self.data + i).take_value() + destroy_pointee(self.data + i) self.size = 0 fn steal_data(inout self) -> AnyPointer[T]: @@ -352,8 +352,8 @@ struct List[T: CollectionElement](CollectionElement, Sized): if i < 0: normalized_idx += len(self) - _ = (self.data + normalized_idx).take_value() - (self.data + normalized_idx).emplace_value(value^) + destroy_pointee(self.data + normalized_idx) + initialize_pointee(self.data + normalized_idx, value^) @always_inline fn _adjust_span(self, span: Slice) -> Slice: diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index 21b378346d..039e1b8e26 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -28,13 +28,13 @@ from memory.unsafe import _LITRef @register_passable("trivial") struct AnyPointer[ - T: Movable, address_space: AddressSpace = AddressSpace.GENERIC + T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ](Boolable, CollectionElement, Stringable, Intable, EqualityComparable): """This is a pointer type that can point to any generic value that is movable. Parameters: - T: The pointer element type, which must be movable. + T: The type the pointer points to. address_space: The address space associated with the AnyPointer allocated memory. """ @@ -61,10 +61,10 @@ struct AnyPointer[ """Create a pointer with the input value. Args: - value: The input pointer to construct with. + value: The MLIR value of the pointer to construct with. Returns: - A null pointer. + The pointer. """ return Self {value: value} @@ -73,10 +73,10 @@ struct AnyPointer[ """Create an unsafe AnyPointer from a safe Reference. Args: - value: The input pointer to construct with. + value: The input reference to construct with. Returns: - A null pointer. + The pointer. """ return Self {value: __mlir_op.`lit.ref.to_pointer`(value.value)} @@ -159,65 +159,6 @@ struct AnyPointer[ _type = AnyPointer[T, new_address_space].pointer_type ](self.value) - @always_inline - fn take_value(self) -> T: - """Move the value at the pointer out. - - The pointer must not be null, and the pointer memory location is assumed - to contain a valid initialized instance of `T`. - - This performs a _consuming_ move, ending the lifetime of the value stored - in this pointer memory location. Subsequent reads of this pointer are - not valid. If a new valid value is stored using `emplace_value()`, then - reading from this pointer becomes valid again. - - Returns: - The value at the pointer. - """ - return __get_address_as_owned_value(self.value) - - @always_inline - fn emplace_value(self, owned value: T): - """Emplace a new value into the pointer location. - - The pointer memory location is assumed to contain uninitialized data, - and consequently the current contents of this pointer are not destructed - before writing `value`. Similarly, ownership of `value` is logically - transfered into the pointer location. - - Args: - value: The value to emplace. - """ - __get_address_as_uninit_lvalue(self.value) = value^ - - @always_inline - fn move_into(self, dest: AnyPointer[T]): - """Moves the value contained in this pointer into the memory location - pointed to by `dest`. - - This performs a consuming move (using `__moveinit__()`) out of the - memory location pointed to by this pointer. Subsequent reads of this - pointer are not valid unless and until a new, valid value has been - moved into this pointer's memory location using `emplace_value()`. - - This transfers the value out of `self` and into `dest` using at most one - `__moveinit__()` call. - - Safety: - * `self` must not be null - * `self` must contain a valid, initialized instance of `T` - * `dest` must not be null - * The contents of `dest` should be uninitialized. If `dest` was - previously written with a valid value, that value will be be - overwritten and its destructor will NOT be run. - - Args: - dest: Destination pointer that the value will be moved into. - """ - __get_address_as_uninit_lvalue( - dest.value - ) = __get_address_as_owned_value(self.value) - @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. @@ -382,3 +323,100 @@ struct AnyPointer[ An offset reference. """ return (self + offset).__refitem__() + + +# ===----------------------------------------------------------------------=== # +# AnyPointer extensions +# ===----------------------------------------------------------------------=== # +# TODO: These should be methods when we have conditional conformance. + + +# This isn't a method because destructors only work in the default address +# space. +@always_inline +fn destroy_pointee(ptr: AnyPointer[_, AddressSpace.GENERIC]): + """Destroy the pointed-to value. + + The pointer must not be null, and the pointer memory location is assumed + to contain a valid initialized instance of `T`. + + Args: + ptr: The pointer whose pointee this destroys. + """ + _ = __get_address_as_owned_value(ptr.value) + + +@always_inline +fn move_from_pointee[T: Movable](ptr: AnyPointer[T, _]) -> T: + """Move the value at the pointer out. + + The pointer must not be null, and the pointer memory location is assumed + to contain a valid initialized instance of `T`. + + This performs a _consuming_ move, ending the lifetime of the value stored + in this pointer memory location. Subsequent reads of this pointer are + not valid. If a new valid value is stored using `initialize_pointee()`, then + reading from this pointer becomes valid again. + + Parameters: + T: The type the pointer points to, which must be `Movable`. + + Args: + ptr: The pointer whose pointee this moves from. + + Returns: + The value at the pointer. + """ + return __get_address_as_owned_value(ptr.value) + + +@always_inline +fn initialize_pointee[T: Movable](ptr: AnyPointer[T, _], owned value: T): + """Emplace a new value into the pointer location. + + The pointer memory location is assumed to contain uninitialized data, + and consequently the current contents of this pointer are not destructed + before writing `value`. Similarly, ownership of `value` is logically + transfered into the pointer location. + + Parameters: + T: The type the pointer points to, which must be `Movable`. + + Args: + ptr: The pointer to initialize through. + value: The value to emplace. + """ + __get_address_as_uninit_lvalue(ptr.value) = value^ + + +@always_inline +fn move_pointee[T: Movable](*, src: AnyPointer[T, _], dst: AnyPointer[T]): + """Moves the value `src` points to into the memory location pointed to by + `dest`. + + This performs a consuming move (using `__moveinit__()`) out of the + memory location pointed to by `src`. Subsequent reads of this + pointer are not valid unless and until a new, valid value has been + moved into this pointer's memory location using `initialize_pointee()`. + + This transfers the value out of `self` and into `dest` using at most one + `__moveinit__()` call. + + Safety: + * `src` must not be null + * `src` must contain a valid, initialized instance of `T` + * `dst` must not be null + * The contents of `dst` should be uninitialized. If `dst` was + previously written with a valid value, that value will be be + overwritten and its destructor will NOT be run. + + Parameters: + T: The type the pointer points to, which must be `Movable`. + + Args: + src: Source pointer that the value will be moved from. + dst: Destination pointer that the value will be moved into. + """ + __get_address_as_uninit_lvalue(dst.value) = __get_address_as_owned_value( + src.value + ) diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index d74423e844..dcf63eb668 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,6 +23,7 @@ from collections import List from sys.info import os_is_linux, os_is_windows, triple_is_nvidia_cuda from memory.unsafe import DTypePointer, Pointer +from memory.anypointer import move_from_pointee from utils import StringRef @@ -207,7 +208,7 @@ fn listdir[pathlike: os.PathLike](path: pathlike) raises -> List[String]: @always_inline("nodebug") -fn abort[result: Movable = NoneType]() -> result: +fn abort[result: AnyType = NoneType]() -> result: """Calls a target dependent trap instruction if available. Parameters: @@ -219,12 +220,14 @@ fn abort[result: Movable = NoneType]() -> result: __mlir_op.`llvm.intr.trap`() - return AnyPointer[result]().take_value() + # We need to satisfy the noreturn checker. + while True: + pass @always_inline("nodebug") fn abort[ - result: Movable = NoneType, *, stringable: Stringable + result: AnyType = NoneType, *, stringable: Stringable ](message: stringable) -> result: """Calls a target dependent trap instruction if available. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 49775b0bbf..f2cda4f646 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -42,6 +42,7 @@ from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.unsafe import _LITRef, emplace_ref_unsafe +from memory.anypointer import * from utils.loop import unroll from utils.static_tuple import StaticTuple @@ -174,7 +175,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ self._impl = __mlir_attr[`#kgen.unknown : `, self._type] self._get_state()[] = Self._check[T]() - self._get_ptr[T]().emplace_value(value^) + initialize_pointee(self._get_ptr[T](), value^) @always_inline fn __copyinit__(inout self, other: Self): @@ -212,9 +213,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): if self._get_state()[] == i: alias T = Ts[i] # Calls the correct __moveinit__ - self._get_ptr[T]().emplace_value( - other._get_ptr[T]().take_value() - ) + move_pointee(src=other._get_ptr[T](), dst=self._get_ptr[T]()) unroll[each, len(VariadicList(Ts))]() @@ -255,7 +254,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): self._get_state()[] = ( Self._sentinel ) # don't call the variant's deleter later - return self._get_ptr[T]().take_value() + return move_from_pointee(self._get_ptr[T]()) fn set[T: CollectionElement](inout self, owned value: T): """Set the variant value. @@ -271,7 +270,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ self._call_correct_deleter() self._get_state()[] = Self._check[T]() - self._get_ptr[T]().emplace_value(value^) + initialize_pointee(self._get_ptr[T](), value^) fn isa[T: CollectionElement](self) -> Bool: """Check if the variant contains the required type. diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_anypointer.mojo index 63606cb47e..e3a2fced74 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_anypointer.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s | FileCheck %s --dump-input=always -from memory.anypointer import AnyPointer +from memory.anypointer import * from test_utils import MoveCounter from testing import assert_equal, assert_not_equal, assert_true @@ -37,31 +37,31 @@ fn test_anypointer_of_move_only_type(): var ptr = AnyPointer[MoveOnlyType].alloc(1) # CHECK: moved 42 - ptr.emplace_value(MoveOnlyType(42)) + initialize_pointee(ptr, MoveOnlyType(42)) # CHECK: moved 42 - var value = ptr.take_value() + var value = move_from_pointee(ptr) # CHECK: value 42 print("value", value.value) # CHECK: deleted 42 ptr.free() -def test_anypointer_move_into_move_count(): +def test_anypointer_move_pointee_move_count(): var ptr = AnyPointer[MoveCounter[Int]].alloc(1) var value = MoveCounter(5) assert_equal(0, value.move_count) - ptr.emplace_value(value^) + initialize_pointee(ptr, value^) # ----- - # Test that `AnyPointer.move_into` performs exactly one move. + # Test that `AnyPointer.move_pointee` performs exactly one move. # ----- assert_equal(1, ptr[].move_count) var ptr_2 = AnyPointer[MoveCounter[Int]].alloc(1) - ptr.move_into(ptr_2) + move_pointee(src=ptr, dst=ptr_2) assert_equal(2, ptr_2[].move_count) @@ -150,7 +150,7 @@ def main(): test_refitem_offset() test_anypointer_of_move_only_type() - test_anypointer_move_into_move_count() + test_anypointer_move_pointee_move_count() test_bitcast() test_anypointer_string() From b4ad478c2adb21b541117ac05e9826172d0f25de Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 17:58:54 -0700 Subject: [PATCH 0110/2019] [mojo-stdlib] Rewrite `Tuple` to support memory-only elements. (#37032) With enough blocking work out of the way, we can finally rewrite `Tuple` to support memory only elements. It does so by having a `!kgen.pack` storage container, and manually copying/moving the elements provided to its copy/move constructor into its storage, and explicitly destroys them when it is done. This is a great step forward, but hasn't been fully tested yet, that will come in a follow-on. MODULAR_ORIG_COMMIT_REV_ID: 88306e320be862b8caffebe3749a83f591ec3332 --- stdlib/src/builtin/builtin_list.mojo | 21 +++++ stdlib/src/builtin/tuple.mojo | 128 ++++++++++++++++++++++++--- stdlib/src/memory/unsafe.mojo | 2 +- stdlib/src/python/object.mojo | 21 +++-- stdlib/src/sys/intrinsics.mojo | 23 +++++ 5 files changed, 170 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 6320c715af..4493f0b32a 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -505,6 +505,8 @@ struct VariadicPack[ """ return Self.__len__() + # TODO: This should be __getitem__ but Mojo doesn't know how to invoke that, + # we need this for tuple as well. @always_inline fn get_element[ index: Int @@ -552,3 +554,22 @@ struct VariadicPack[ func(self.get_element[i]()[]) unroll[unrolled, Self.__len__()]() + + @always_inline + fn each_idx[ + func: fn[idx: Int, T: element_trait] (T) capturing -> None + ](self): + """Apply a function to each element of the pack in order. This applies + the specified function (which must be parametric on the element type) to + each element of the pack, from the first element to the last, passing + in each element as a borrowed argument. + + Parameters: + func: The function to apply to each element. + """ + + @parameter + fn unrolled[i: Int](): + func[i, element_types[i.value]](self.get_element[i]()[]) + + unroll[unrolled, Self.__len__()]() diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index f3c7783bbe..5d7f76d855 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from utils._visualizers import lldb_formatter_wrapping_type +from memory.unsafe import emplace_ref_unsafe # ===----------------------------------------------------------------------===# # Tuple @@ -23,26 +24,56 @@ from utils._visualizers import lldb_formatter_wrapping_type @lldb_formatter_wrapping_type -struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): +struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): """The type of a literal tuple expression. A tuple consists of zero or more values, separated by commas. Parameters: - Ts: The elements type. + element_types: The elements type. """ - var storage: __mlir_type[`!kgen.pack<`, Ts, `>`] + var storage: __mlir_type[ + `!kgen.pack<:!kgen.variadic<`, + CollectionElement, + `> `, + +element_types, + `>`, + ] """The underlying storage for the tuple.""" @always_inline("nodebug") - fn __init__(inout self, borrowed *args: *Ts): + fn __init__(inout self, *args: *element_types): """Construct the tuple. Args: args: Initial values. """ - self.storage = args + # Mark 'storage' as being initialized so we can work on it. + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(self.storage) + ) + + @parameter + fn initialize_elt[idx: Int](): + # TODO: We could be fancier and take the values out of an owned + # pack. For now just keep everything simple and copy the element. + emplace_ref_unsafe( + self._refitem__[idx](), args.get_element[idx]()[] + ) + + unroll[initialize_elt, Self.__len__()]() + + fn __del__(owned self): + """Destructor that destroyes all of the elements.""" + + # Run the destructor on each member, the destructor of !kgen.pack is + # trivial and won't do anything. + @parameter + fn destroy_elt[idx: Int](): + self._refitem__[idx]().destroy_element_unsafe() + + unroll[destroy_elt, Self.__len__()]() @always_inline("nodebug") fn __copyinit__(inout self, existing: Self): @@ -51,7 +82,21 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): Args: existing: The value to copy from. """ - self.storage = existing.storage + # Mark 'storage' as being initialized so we can work on it. + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(self.storage) + ) + + @parameter + fn initialize_elt[idx: Int](): + var existing_elt_ptr = AnyPointer(existing._refitem__[idx]()) + + emplace_ref_unsafe( + self._refitem__[idx](), + __get_address_as_owned_value(existing_elt_ptr.value), + ) + + unroll[initialize_elt, Self.__len__()]() @always_inline("nodebug") fn __moveinit__(inout self, owned existing: Self): @@ -60,7 +105,37 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): Args: existing: The value to move from. """ - self.storage = existing.storage + # Mark 'storage' as being initialized so we can work on it. + __mlir_op.`lit.ownership.mark_initialized`( + __get_mvalue_as_litref(self.storage) + ) + + @parameter + fn initialize_elt[idx: Int](): + emplace_ref_unsafe( + self._refitem__[idx](), + existing._refitem__[idx]()[], + ) + + unroll[initialize_elt, Self.__len__()]() + + @always_inline + @staticmethod + fn __len__() -> Int: + """Return the number of elements in the tuple. + + Returns: + The tuple length. + """ + + @parameter + fn variadic_size( + x: __mlir_type[`!kgen.variadic<`, CollectionElement, `>`] + ) -> Int: + return __mlir_op.`pop.variadic.size`(x) + + alias result = variadic_size(element_types) + return result @always_inline("nodebug") fn __len__(self) -> Int: @@ -69,10 +144,34 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): Returns: The tuple length. """ - return __mlir_op.`pop.variadic.size`(Ts) + return Self.__len__() + # TODO: Mojo's small brain can't handle a __refitem__ like this yet. + @always_inline("nodebug") + fn _refitem__[ + idx: Int, + mutability: __mlir_type.`i1`, + self_life: AnyLifetime[mutability].type, + ]( + self_lit: Reference[Self, mutability, self_life].mlir_ref_type + ) -> Reference[element_types[idx.value], mutability, self_life]: + # Return a reference to an element at the specified index, propagating + # mutability of self. + var storage_kgen_ptr = Reference( + Reference(self_lit)[].storage + ).get_unsafe_pointer().address + + # Pointer to the element. + var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( + storage_kgen_ptr + ) + # Convert to an immortal mut reference, which conforms to self_life. + return AnyPointer(elt_kgen_ptr)[] + + # TODO: Remove the get methods in favor of __refitem__ some day. This will + # be annoying if we don't have autoderef though. @always_inline("nodebug") - fn get[i: Int, T: AnyRegType](self) -> T: + fn get[i: Int, T: CollectionElement](self) -> T: """Get a tuple element and rebind to the specified type. Parameters: @@ -85,7 +184,7 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): return rebind[T](self.get[i]()) @always_inline("nodebug") - fn get[i: Int](self) -> Ts[i.value]: + fn get[i: Int](self) -> element_types[i.value]: """Get a tuple element. Parameters: @@ -94,7 +193,7 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): Returns: The tuple element at the requested index. """ - return __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) + return self._refitem__[i]()[] @staticmethod fn _offset[i: Int]() -> Int: @@ -106,8 +205,11 @@ struct Tuple[*Ts: AnyRegType](Sized, CollectionElement): else: return _align_up( Self._offset[i - 1]() - + _align_up(sizeof[Ts[i - 1]](), alignof[Ts[i - 1]]()), - alignof[Ts[i]](), + + _align_up( + sizeof[element_types[i - 1]](), + alignof[element_types[i - 1]](), + ), + alignof[element_types[i]](), ) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 61f878aea2..3149903bff 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -506,7 +506,7 @@ struct Reference[ Constructed Pointer object. """ var ptr_with_trait = __mlir_op.`lit.ref.to_pointer`(self.value) - # Work around AnyRefType vs AnyType. + # Work around AnyRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ _type = Pointer[type, address_space].pointer_type ](ptr_with_trait) diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index a16cbca4c5..6db0eb3858 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -19,7 +19,7 @@ from python.object import PythonObject ``` """ -from sys.intrinsics import _mlirtype_is_eq +from sys.intrinsics import _mlirtype_is_eq, _type_is_eq from utils import StringRef, unroll @@ -252,7 +252,7 @@ struct PythonObject( unroll[fill, len(types)]() - fn __init__[*Ts: AnyRegType](inout self, value: Tuple[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: Tuple[Ts]): """Initialize the object from a tuple literal. Parameters: @@ -262,28 +262,27 @@ struct PythonObject( value: The tuple value. """ var cpython = _get_global_python_itf().cpython() - alias types = VariadicList(Ts) - alias length = len(types) + alias length = len(VariadicList(Ts)) self.py_object = cpython.PyTuple_New(length) @parameter fn fill[i: Int](): # We need to rebind the element to one we know how to convert from. # FIXME: This doesn't handle implicit conversions or nested lists. - alias T = types[i] + alias T = Ts[i] var obj: PythonObject @parameter - if _mlirtype_is_eq[T, Int](): + if _type_is_eq[T, Int](): obj = value.get[i, Int]() - elif _mlirtype_is_eq[T, Float64](): + elif _type_is_eq[T, Float64](): obj = value.get[i, Float64]() - elif _mlirtype_is_eq[T, Bool](): + elif _type_is_eq[T, Bool](): obj = value.get[i, Bool]() - elif _mlirtype_is_eq[T, StringRef](): + elif _type_is_eq[T, StringRef](): obj = value.get[i, StringRef]() - elif _mlirtype_is_eq[T, StringLiteral](): + elif _type_is_eq[T, StringLiteral](): obj = value.get[i, StringLiteral]() else: obj = PythonObject(0) @@ -293,7 +292,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyTuple_SetItem(self.py_object, i, obj.py_object) - unroll[fill, len(types)]() + unroll[fill, length]() fn __init__(inout self, value: Dict[Self, Self]): """Initialize the object from a dictionary of PythonObjects. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 909d0fccae..175d1664a2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1273,6 +1273,29 @@ fn _mlirtype_is_eq[t1: AnyRegType, t2: AnyRegType]() -> Bool: ] +fn _type_is_eq[t1: AnyType, t2: AnyType]() -> Bool: + """Compares the two type for equality. + + Parameters: + t1: The LHS of the type comparison. + t2: The RHS of the type comparison. + + Returns: + Returns True if t1 and t2 are the same type and False otherwise. + """ + return __mlir_attr[ + `#kgen.param.expr : !kgen.type`, + `,`, + `#kgen.parameterizedtype.constant<`, + +t2, + `> : !kgen.type`, + `> : i1`, + ] + + # ===----------------------------------------------------------------------=== # # Transitional type used for llvm_intrinsic # ===----------------------------------------------------------------------=== # From 59aa54aed60e7e097c8a5b0666d79cb9da73fd7c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 18:31:52 -0700 Subject: [PATCH 0111/2019] [mojo-stdlib] Remove unnecessary backticks from mlir stuff, NFC. (#37132) Backticks are only required when the thing isn't an identifier. MODULAR_ORIG_COMMIT_REV_ID: fd75b13315bfde1653494a7b59d9699e171eaefc --- docs/changelog-released.md | 4 ++-- examples/notebooks/BoolMLIR.ipynb | 24 ++++++++++++------------ stdlib/src/builtin/dtype.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/collections/optional.mojo | 2 +- stdlib/src/collections/set.mojo | 2 +- stdlib/src/memory/_arc.mojo | 2 +- stdlib/src/utils/variant.mojo | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 7860d298d9..a794444e10 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -2263,9 +2263,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ```mojo # Old syntax, now fails. - __mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]() + __mlir_op.`index.bool.constant`[value : __mlir_attr.false]() # New syntax. - __mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]() + __mlir_op.`index.bool.constant`[value=__mlir_attr.false]() ``` - You can now print the `Error` object directly. The `message()` method diff --git a/examples/notebooks/BoolMLIR.ipynb b/examples/notebooks/BoolMLIR.ipynb index 91613f5fe5..37abdd8c3a 100644 --- a/examples/notebooks/BoolMLIR.ipynb +++ b/examples/notebooks/BoolMLIR.ipynb @@ -135,7 +135,7 @@ "\n", " fn __init__(inout self):\n", " self.value = __mlir_op.`index.bool.constant`[\n", - " value=__mlir_attr.`false`,\n", + " value=__mlir_attr.false,\n", " ]()" ] }, @@ -194,7 +194,7 @@ " fn __init__() -> Self:\n", " return Self {\n", " value: __mlir_op.`index.bool.constant`[\n", - " value=__mlir_attr.`false`,\n", + " value=__mlir_attr.false,\n", " ]()\n", " }" ] @@ -258,7 +258,7 @@ "metadata": {}, "outputs": [], "source": [ - "alias OurTrue = OurBool(__mlir_attr.`true`)" + "alias OurTrue = OurBool(__mlir_attr.true)" ] }, { @@ -276,7 +276,7 @@ }, "outputs": [], "source": [ - "alias OurFalse: OurBool = __mlir_attr.`false`" + "alias OurFalse: OurBool = __mlir_attr.false" ] }, { @@ -296,8 +296,8 @@ }, "outputs": [], "source": [ - "alias OurTrue = OurBool(__mlir_attr.`true`)\n", - "alias OurFalse: OurBool = __mlir_attr.`false`\n", + "alias OurTrue = OurBool(__mlir_attr.true)\n", + "alias OurFalse: OurBool = __mlir_attr.false\n", "\n", "\n", "@register_passable(\"trivial\")\n", @@ -361,8 +361,8 @@ }, "outputs": [], "source": [ - "alias OurTrue = OurBool(__mlir_attr.`true`)\n", - "alias OurFalse: OurBool = __mlir_attr.`false`\n", + "alias OurTrue = OurBool(__mlir_attr.true)\n", + "alias OurFalse: OurBool = __mlir_attr.false\n", "\n", "\n", "@register_passable(\"trivial\")\n", @@ -435,8 +435,8 @@ "metadata": {}, "outputs": [], "source": [ - "alias OurTrue = OurBool(__mlir_attr.`true`)\n", - "alias OurFalse: OurBool = __mlir_attr.`false`\n", + "alias OurTrue = OurBool(__mlir_attr.true)\n", + "alias OurFalse: OurBool = __mlir_attr.false\n", "\n", "\n", "@register_passable(\"trivial\")\n", @@ -501,8 +501,8 @@ "metadata": {}, "outputs": [], "source": [ - "alias OurTrue = OurBool(__mlir_attr.`true`)\n", - "alias OurFalse: OurBool = __mlir_attr.`false`\n", + "alias OurTrue = OurBool(__mlir_attr.true)\n", + "alias OurFalse: OurBool = __mlir_attr.false\n", "\n", "\n", "@register_passable(\"trivial\")\n", diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 16d2c5223b..91ff69246e 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -142,7 +142,7 @@ struct DType(Stringable, KeyElement): @staticmethod fn _from_ui8(ui8: __mlir_type.`!pop.scalar`) -> DType: return DType._from_ui8( - __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.`ui8`](ui8) + __mlir_op.`pop.cast_to_builtin`[_type = __mlir_type.ui8](ui8) ) @always_inline("nodebug") diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 5d7f76d855..d2a5ac3a21 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -150,7 +150,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @always_inline("nodebug") fn _refitem__[ idx: Int, - mutability: __mlir_type.`i1`, + mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type, ]( self_lit: Reference[Self, mutability, self_life].mlir_ref_type diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 2bf494da43..e77d23806d 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -304,7 +304,7 @@ struct OptionalReg[T: AnyRegType](Boolable): return Self { _value: __mlir_op.`kgen.variant.create`[ _type = Self._type, index = Int(1).value - ](__mlir_attr.`false`) + ](__mlir_attr.false) } @always_inline diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index dbcfc60161..2f48dc4746 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -226,7 +226,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): self.remove_all(other) fn __iter__[ - mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type ]( self: Reference[Self, mutability, self_life].mlir_ref_type, ) -> _DictKeyIter[T, NoneType, mutability, self_life]: diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index bc423d0f2f..32ff995e3c 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -138,7 +138,7 @@ struct Arc[T: Movable](CollectionElement): self._inner[].data = new_value^ fn __refitem__[ - mutability: __mlir_type.`i1`, + mutability: __mlir_type.i1, lifetime: AnyLifetime[mutability].type, ](self: Reference[Self, mutability, lifetime].mlir_ref_type) -> Reference[ T, mutability, lifetime diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index f2cda4f646..598cc69d29 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -286,7 +286,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn get[ T: CollectionElement, - mutability: __mlir_type.`i1`, + mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type, ](self: Reference[Self, mutability, self_life].mlir_ref_type) -> Reference[ T, mutability, self_life From 7cadcdca2e514e574967f2e0bfcaab3db4d2b39d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 19:55:55 -0700 Subject: [PATCH 0112/2019] [mojo-stdlib] Sundry improvements to pointer/reference. (#37138) This makes use of `AnyPointer`'s new-found generality to `AnyType`, allowing use of it to simplify a bunch of code. As part of this, we remove `emplace_ref_unsafe` and the `destroy_element_unsafe` method in favor of the `AnyPointer` versions. MODULAR_ORIG_COMMIT_REV_ID: 014e0bced52a154aceac00eb64d11f8f501822fb --- docs/changelog.md | 10 ++- stdlib/src/builtin/builtin_list.mojo | 9 +- stdlib/src/builtin/tuple.mojo | 17 ++-- stdlib/src/memory/__init__.mojo | 1 - stdlib/src/memory/anypointer.mojo | 8 +- stdlib/src/memory/unsafe.mojo | 110 +++--------------------- stdlib/src/utils/variant.mojo | 6 +- stdlib/test/memory/test_anypointer.mojo | 4 +- 8 files changed, 40 insertions(+), 125 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bd9867e745..bacd0dc008 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -72,8 +72,7 @@ and tools. Please add any significant user-visible changes here. - `Reference` interoperates with unsafe code better: `AnyPointer` now has a constructor that forms it from `Reference` directly (inferring element type and address space). `AnyPointer` can convert to an immortal mutable - `Reference` with `yourptr[]`. Both `Reference` and `AnyPointer` now have an - `address_space_cast` method like `Pointer`. + `Reference` with `yourptr[]`. - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking @@ -100,6 +99,13 @@ and tools. Please add any significant user-visible changes here. `move_from_pointee`, `initialize_pointee` and `move_pointee` respectively. 3) A new `destroy_pointee` function runs the destructor on the pointee. 4) `AnyPointer` can be initialized from a `Reference` as mentioned above. + 5) It has some new methods like `address_space_cast`. +- The `Reference` type has several changes, including: + 1) `Reference` now has an unsafe `address_space_cast` method like `Pointer`. + 2) The `destroy_element_unsafe` method has been remove, do this with + `AnyPointer/destroy_pointee`, which is more obviously unsafe. + 3) The `emplace_ref_unsafe` function has been removed in favor of + `AnyPointer/initialize_pointee`. ### ❌ Removed diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 4493f0b32a..40806599b0 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -16,6 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from memory.unsafe import Reference, _LITRef +from memory.anypointer import * # ===----------------------------------------------------------------------===# # ListLiteral @@ -331,11 +332,7 @@ struct VariadicListMem[ # destroy in backwards order to match how arguments are normally torn # down when CheckLifetimes is left to its own devices. for i in range(len(self), 0, -1): - # This cannot use Reference(self[i - 1]) because the subscript - # will return a BValue, not an LValue. We need to maintain the - # parametric mutability by keeping the Reference returned by - # refitem exposed. - self.__refitem__(i - 1).destroy_element_unsafe() + destroy_pointee(AnyPointer(Reference(self[i - 1]))) @always_inline fn __len__(self) -> Int: @@ -474,7 +471,7 @@ struct VariadicPack[ @parameter fn destroy_elt[i: Int](): # destroy the elements in reverse order. - self.get_element[len - i - 1]().destroy_element_unsafe() + destroy_pointee(AnyPointer(self.get_element[len - i - 1]())) unroll[destroy_elt, len]() diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index d2a5ac3a21..daa6f5b2d2 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -16,7 +16,6 @@ These are Mojo built-ins, so you don't need to import them. """ from utils._visualizers import lldb_formatter_wrapping_type -from memory.unsafe import emplace_ref_unsafe # ===----------------------------------------------------------------------===# # Tuple @@ -58,20 +57,20 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): fn initialize_elt[idx: Int](): # TODO: We could be fancier and take the values out of an owned # pack. For now just keep everything simple and copy the element. - emplace_ref_unsafe( - self._refitem__[idx](), args.get_element[idx]()[] + initialize_pointee( + AnyPointer(self._refitem__[idx]()), args.get_element[idx]()[] ) unroll[initialize_elt, Self.__len__()]() fn __del__(owned self): - """Destructor that destroyes all of the elements.""" + """Destructor that destroys all of the elements.""" # Run the destructor on each member, the destructor of !kgen.pack is # trivial and won't do anything. @parameter fn destroy_elt[idx: Int](): - self._refitem__[idx]().destroy_element_unsafe() + destroy_pointee(AnyPointer(self._refitem__[idx]())) unroll[destroy_elt, Self.__len__()]() @@ -91,8 +90,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): fn initialize_elt[idx: Int](): var existing_elt_ptr = AnyPointer(existing._refitem__[idx]()) - emplace_ref_unsafe( - self._refitem__[idx](), + initialize_pointee( + AnyPointer(self._refitem__[idx]()), __get_address_as_owned_value(existing_elt_ptr.value), ) @@ -112,8 +111,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): - emplace_ref_unsafe( - self._refitem__[idx](), + initialize_pointee( + AnyPointer(self._refitem__[idx]()), existing._refitem__[idx]()[], ) diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 303bf9c872..995839b726 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -28,7 +28,6 @@ from .unsafe import ( bitcast, AddressSpace, Reference, - emplace_ref_unsafe, Pointer, DTypePointer, ) diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index 039e1b8e26..b4364f74e6 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -122,7 +122,9 @@ struct AnyPointer[ ).free() @always_inline - fn bitcast[new_type: Movable](self) -> AnyPointer[new_type, address_space]: + fn bitcast_element[ + new_type: AnyType + ](self) -> AnyPointer[new_type, address_space]: """Bitcasts the pointer to a different type. Parameters: @@ -133,10 +135,6 @@ struct AnyPointer[ the original pointer. """ - @parameter - if _mlirtype_is_eq[T, new_type](): - return rebind[AnyPointer[new_type, address_space]](self) - return __mlir_op.`pop.pointer.bitcast`[ _type = AnyPointer[new_type, address_space].pointer_type ](self.value) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 3149903bff..4b0ce4fe93 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -98,13 +98,16 @@ fn bitcast[ @always_inline("nodebug") fn bitcast[ - new_type: Movable, src_type: Movable -](ptr: AnyPointer[src_type]) -> AnyPointer[new_type]: + new_type: AnyType, src_type: AnyType, address_space: AddressSpace +](ptr: AnyPointer[src_type, address_space]) -> AnyPointer[ + new_type, address_space +]: """Bitcasts an AnyPointer to a different type. Parameters: new_type: The target type. src_type: The source type. + address_space: The shared address space. Args: ptr: The source pointer. @@ -113,7 +116,7 @@ fn bitcast[ A new Pointer with the specified type and the same address, as the original Pointer. """ - return ptr.bitcast[new_type]() + return ptr.bitcast_element[new_type]() @always_inline("nodebug") @@ -505,11 +508,10 @@ struct Reference[ Returns: Constructed Pointer object. """ - var ptr_with_trait = __mlir_op.`lit.ref.to_pointer`(self.value) # Work around AnyRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ _type = Pointer[type, address_space].pointer_type - ](ptr_with_trait) + ](AnyPointer(self).value) @always_inline("nodebug") fn offset(self, offset: Int) -> Self: @@ -536,24 +538,9 @@ struct Reference[ Returns: The new reference. """ - # We don't have a generalized lit.ref.cast operation, so convert through - # to KGEN pointer. - # FIXME: We can't use AnyPointer here, because it requires T <- Movable. - var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) - var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[ - `!kgen.pointer<`, - new_element_type, - `,`, - address_space._value.value, - `>`, - ] - ](kgen_ptr) - return __mlir_op.`lit.ref.from_pointer`[ - _type = _LITRef[ - new_element_type, is_mutable, lifetime, address_space - ].type - ](dest_ptr) + # We don't have a `lit.ref.cast`` operation, so convert through a KGEN + # pointer. + return AnyPointer(self).bitcast_element[new_element_type]()[] @always_inline fn address_space_cast[ @@ -568,80 +555,9 @@ struct Reference[ Returns: The new reference. """ - # We don't have a generalized lit.ref.cast operation, so convert through - # to KGEN pointer. - # FIXME: We can't use AnyPointer here, because it requires T <- Movable. - var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) - var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[ - `!kgen.pointer<`, - type, - `,`, - new_address_space._value.value, - `>`, - ] - ](kgen_ptr) - return __mlir_op.`lit.ref.from_pointer`[ - _type = _LITRef[type, is_mutable, lifetime, new_address_space].type - ](dest_ptr) - - fn destroy_element_unsafe(self): - """This unsafe operation runs the destructor of the element addressed by - this reference. This is equivalent to `x->~Type()` syntax in C++. - """ - - # This should only work with mutable references. - # FIXME: This should be a precondition checked by the Mojo type checker, - # not delayed to elaboration! - constrained[ - is_mutable, - "cannot use 'unsafe_destroy_element' on immutable references", - ]() - - # This method can only work on address space 0, because the __del__ - # method that we need to invoke will take 'self' in address space zero. - constrained[ - address_space == AddressSpace.GENERIC, - "cannot use 'destroy_element_unsafe' on arbitrary address spaces", - ]() - - # Project to an owned raw pointer, allowing the compiler to know it is to - # be destroyed. - var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(self.value) - - # Bitcast to address space zero since the inserted __del__ call will only - # work with address space zero. - var dest_ptr = __mlir_op.`pop.pointer.bitcast`[ - _type = __mlir_type[ - `!kgen.pointer<`, - type, - `>`, - ] - ](kgen_ptr) - - # TODO: Use AnyPointer, but it requires a Movable element. - _ = __get_address_as_owned_value(dest_ptr) - - -# FIXME: This should be a method on Reference, it is placed here because we need -# it constrained on mutability and copyability of value. -fn emplace_ref_unsafe[ - type: Movable, lifetime: MutLifetime -](dest: Reference[type, __mlir_attr.`1: i1`, lifetime], owned value: type): - """This unsafe operation assumes the memory pointed to by the reference - is uninitialized and overwrites it with an owned version of the - specified value. This is equivalent to `new(ptr) Type(v)` syntax in C++. - - Parameters: - type: Type of the underlying data. - lifetime: The lifetime of the reference. - - Args: - dest: The reference to uninitialized memory to overwrite. - value: The value to write into it. - """ - var kgen_ptr = __mlir_op.`lit.ref.to_pointer`(dest.value) - __get_address_as_uninit_lvalue(kgen_ptr) = value^ + # We don't have a `lit.ref.cast`` operation, so convert through a KGEN + # pointer. + return AnyPointer(self).address_space_cast[new_address_space]()[] # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 598cc69d29..4bc1f3b964 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -41,7 +41,7 @@ print(to_string(who_knows)) from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq -from memory.unsafe import _LITRef, emplace_ref_unsafe +from memory.unsafe import _LITRef from memory.anypointer import * from utils.loop import unroll @@ -191,8 +191,8 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn each[i: Int](): if self._get_state()[] == i: alias T = Ts[i] - emplace_ref_unsafe[T]( - Reference(self._impl).bitcast_element[T](), + initialize_pointee[T]( + AnyPointer(Reference(self._impl).bitcast_element[T]()), Reference(other._impl).bitcast_element[T]()[], ) diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_anypointer.mojo index e3a2fced74..dddaf0856a 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_anypointer.mojo @@ -91,9 +91,9 @@ def test_address_of(): def test_bitcast(): var local = 1 var ptr = AnyPointer[Int].address_of(local) - var aliased_ptr = ptr.bitcast[SIMD[DType.uint8, 4]]() + var aliased_ptr = ptr.bitcast_element[SIMD[DType.uint8, 4]]() - assert_equal(int(ptr), int(ptr.bitcast[Int]())) + assert_equal(int(ptr), int(ptr.bitcast_element[Int]())) assert_equal(int(ptr), int(aliased_ptr)) From a54650730c8720b8929a4096b4402ddef1023495 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 20:08:32 -0700 Subject: [PATCH 0113/2019] [mojo-docs] Document that Tuple now works with memory values. (#37147) This closes out a lot of public issues. MODULAR_ORIG_COMMIT_REV_ID: beb4c0fe8c431f2bce95cbeacc67b382cb32cbac --- docs/changelog.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bacd0dc008..6d1629569e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,6 +13,11 @@ and tools. Please add any significant user-visible changes here. ### 🔥 Legendary +- Tuple now works with memory-only element types like String. Also, `Tuple.get` + now supports a form that just takes an element index but does not + require you to specify the result type. Instead of `tup.get[1, Int]()` you + can now just use `tup.get[1]()`. + ### ⭐️ New - Heterogenous variadic pack arguments now work reliably even with memory types, @@ -65,10 +70,6 @@ and tools. Please add any significant user-visible changes here. - `Dict` now has a `update()` method to update keys/values from another `Dict`. -- `Tuple.get` now supports a form that just takes an element index but does not - require you to specify the result type. Instead of `tup.get[1, Int]()` you - can now just use `tup.get[1]()`. - - `Reference` interoperates with unsafe code better: `AnyPointer` now has a constructor that forms it from `Reference` directly (inferring element type and address space). `AnyPointer` can convert to an immortal mutable @@ -102,7 +103,7 @@ and tools. Please add any significant user-visible changes here. 5) It has some new methods like `address_space_cast`. - The `Reference` type has several changes, including: 1) `Reference` now has an unsafe `address_space_cast` method like `Pointer`. - 2) The `destroy_element_unsafe` method has been remove, do this with + 2) The `destroy_element_unsafe` method has been removed, do this with `AnyPointer/destroy_pointee`, which is more obviously unsafe. 3) The `emplace_ref_unsafe` function has been removed in favor of `AnyPointer/initialize_pointee`. @@ -125,6 +126,15 @@ and tools. Please add any significant user-visible changes here. ### 🛠️ Fixed +- [#516](https://github.com/modularml/mojo/issues/516) and + [#1817](https://github.com/modularml/mojo/issues/1817) and many others, e.g. + "Can't create a function that returns two strings" + +- [#1178](https://github.com/modularml/mojo/issues/1178) (os/kern) failure (5) + +- [#1609](https://github.com/modularml/mojo/issues/1609) alias with + `DynamicVector[Tuple[Int]]` fails. + - [#1987](https://github.com/modularml/mojo/issues/1987) Defining `main` in a Mojo package is an error, for now. This is not intended to work yet, erroring for now will help to prevent accidental undefined behavior. From 582cb5ba3f7fb2cf8cc3730836f008bd552969f1 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Mon, 8 Apr 2024 21:48:31 -0600 Subject: [PATCH 0114/2019] [stdlib] Fix doc strings for `Arc.__refitem__` (#36999) `__refitem__` returns a `Reference`, not a copy to the managed value. Update the API doc strings to avoid confusion. MODULAR_ORIG_COMMIT_REV_ID: 513d6c72e249ff6a3df0b62a9347ad8eb76af017 --- stdlib/src/memory/_arc.mojo | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 32ff995e3c..55c1ece611 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -143,12 +143,10 @@ struct Arc[T: Movable](CollectionElement): ](self: Reference[Self, mutability, lifetime].mlir_ref_type) -> Reference[ T, mutability, lifetime ]: - """Get a copy of the managed value. - - When we have lifetimes this will not have to copy. + """Returns a Reference to the managed value. Returns: - A copy of the managed value. + A Reference to the managed value. """ alias RefType = Reference[T, mutability, lifetime] return RefType( From 9581ec6a84d632012cbf690b06b9ac51195d7eb2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 21:19:10 -0700 Subject: [PATCH 0115/2019] [mojo-lang] Remove `lit.ref.offset` and `Reference.offset` (#37148) Now that `AnyPointer` is reasonable to work with, we don't need to add these super unsafe methods and mlir ops for transporting references around. This fixes #34065 MODULAR_ORIG_COMMIT_REV_ID: b32608f5366d6ecdcf8681c76a7a866d3f928917 --- docs/changelog.md | 2 ++ stdlib/src/memory/unsafe.mojo | 12 ------------ stdlib/src/utils/variant.mojo | 7 ++----- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6d1629569e..562f9918ba 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -107,6 +107,8 @@ and tools. Please add any significant user-visible changes here. `AnyPointer/destroy_pointee`, which is more obviously unsafe. 3) The `emplace_ref_unsafe` function has been removed in favor of `AnyPointer/initialize_pointee`. + 4) The `offset` method has been removed, it was unsafe and belongs on + `AnyPointer`. ### ❌ Removed diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 4b0ce4fe93..2103fa7c6e 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -513,18 +513,6 @@ struct Reference[ _type = Pointer[type, address_space].pointer_type ](AnyPointer(self).value) - @always_inline("nodebug") - fn offset(self, offset: Int) -> Self: - """Offset the reference like an array. - - Args: - offset: The integer offset. - - Returns: - A new reference. - """ - return __mlir_op.`lit.ref.offset`(self.value, offset.value) - @always_inline("nodebug") fn bitcast_element[ new_element_type: AnyType diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 4bc1f3b964..ccfaa5a728 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -157,11 +157,8 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn _get_state[ is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] ](self: _LITRef[Self, is_mut, lt].type) -> Reference[Int8, is_mut, lt]: - return ( - Reference(self) - .bitcast_element[Int8]() - .offset(_UnionSize[Ts].compute()) - ) + var int8_self = AnyPointer(Reference(self).bitcast_element[Int8]()) + return (int8_self + _UnionSize[Ts].compute())[] fn __init__[T: CollectionElement](inout self, owned value: T): """Create a variant with one of the types. From e09e87c971565c174ac2e783a4d4de3c690d9e0d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 23:08:59 -0700 Subject: [PATCH 0116/2019] [mojo-stdlib] Split `Reference` out to `memory.reference` module. (#37151) This is a long overdue move, which requires updating some import lines. MODULAR_ORIG_COMMIT_REV_ID: 21878a62b78b015abda9c0301589f73ff953bade --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/file.mojo | 3 +- stdlib/src/memory/anypointer.mojo | 2 +- stdlib/src/memory/memory.mojo | 3 +- stdlib/src/memory/reference.mojo | 309 +++++++++++++++++++++++++++ stdlib/src/memory/unsafe.mojo | 295 +------------------------ stdlib/src/sys/intrinsics.mojo | 3 +- stdlib/src/utils/_serialize.mojo | 3 +- stdlib/src/utils/variant.mojo | 2 +- 9 files changed, 323 insertions(+), 299 deletions(-) create mode 100644 stdlib/src/memory/reference.mojo diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 40806599b0..a6857f3539 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory.unsafe import Reference, _LITRef +from memory.reference import Reference, _LITRef from memory.anypointer import * # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 446faad7ca..b1f3e3197d 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -34,7 +34,8 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call -from memory.unsafe import AddressSpace, DTypePointer, Pointer +from memory.reference import AddressSpace +from memory.unsafe import DTypePointer, Pointer @register_passable diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index b4364f74e6..e997602a72 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -23,7 +23,7 @@ from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc -from memory.unsafe import _LITRef +from memory.reference import _LITRef @register_passable("trivial") diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index bcf60b51f4..45c0d362b4 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -24,7 +24,8 @@ from sys import llvm_intrinsic from sys.info import sizeof, triple_is_nvidia_cuda from builtin.dtype import _integral_type_of -from .unsafe import AddressSpace, DTypePointer, Pointer, _GPUAddressSpace +from memory.reference import AddressSpace, _GPUAddressSpace +from .unsafe import DTypePointer, Pointer # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo new file mode 100644 index 0000000000..92f406741b --- /dev/null +++ b/stdlib/src/memory/reference.mojo @@ -0,0 +1,309 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements the Reference type. + +You can import these APIs from the `memory` package. For example: + +```mojo +from memory.reference import Reference +``` +""" + +# ===----------------------------------------------------------------------===# +# AddressSpace +# ===----------------------------------------------------------------------===# + + +@value +@register_passable("trivial") +struct _GPUAddressSpace(EqualityComparable): + var _value: Int + + # See https://docs.nvidia.com/cuda/nvvm-ir-spec/#address-space + alias GENERIC = AddressSpace(0) + """Generic address space.""" + alias GLOBAL = AddressSpace(1) + """Global address space.""" + alias CONSTANT = AddressSpace(2) + """Constant address space.""" + alias SHARED = AddressSpace(3) + """Shared address space.""" + alias PARAM = AddressSpace(4) + """Param address space.""" + alias LOCAL = AddressSpace(5) + """Local address space.""" + + @always_inline("nodebug") + fn __init__(value: Int) -> Self: + return Self {_value: value} + + @always_inline("nodebug") + fn value(self) -> Int: + """The integral value of the address space. + + Returns: + The integral value of the address space. + """ + return self._value + + @always_inline("nodebug") + fn __int__(self) -> Int: + """The integral value of the address space. + + Returns: + The integral value of the address space. + """ + return self._value + + @always_inline("nodebug") + fn __eq__(self, other: Self) -> Bool: + """The True if the two address spaces are equal and False otherwise. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() == other.value() + + @always_inline("nodebug") + fn __eq__(self, other: AddressSpace) -> Bool: + """The True if the two address spaces are equal and False otherwise. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() == other.value() + + @always_inline("nodebug") + fn __ne__(self, other: Self) -> Bool: + """True if the two address spaces are inequal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are inequal and False otherwise. + """ + return not self == other + + @always_inline("nodebug") + fn __ne__(self, other: AddressSpace) -> Bool: + """True if the two address spaces are inequal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are inequal and False otherwise. + """ + return not self == other + + +@value +@register_passable("trivial") +struct AddressSpace(EqualityComparable): + """Address space of the pointer.""" + + var _value: Int + + alias GENERIC = AddressSpace(0) + """Generic address space.""" + + @always_inline("nodebug") + fn __init__(value: Int) -> Self: + """Initializes the address space from the underlying integeral value. + + Args: + value: The address space value. + + Returns: + The address space. + """ + return Self {_value: value} + + @always_inline("nodebug") + fn __init__(value: _GPUAddressSpace) -> Self: + """Initializes the address space from the underlying integeral value. + + Args: + value: The address space value. + + Returns: + The address space. + """ + return Self {_value: int(value)} + + @always_inline("nodebug") + fn value(self) -> Int: + """The integral value of the address space. + + Returns: + The integral value of the address space. + """ + return self._value + + @always_inline("nodebug") + fn __int__(self) -> Int: + """The integral value of the address space. + + Returns: + The integral value of the address space. + """ + return self._value + + @always_inline("nodebug") + fn __eq__(self, other: Self) -> Bool: + """True if the two address spaces are equal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are equal and False otherwise. + """ + return self.value() == other.value() + + @always_inline("nodebug") + fn __ne__(self, other: Self) -> Bool: + """True if the two address spaces are inequal and False otherwise. + + Args: + other: The other address space value. + + Returns: + True if the two address spaces are inequal and False otherwise. + """ + return not self == other + + +# ===----------------------------------------------------------------------===# +# Reference +# ===----------------------------------------------------------------------===# + + +# Helper to build !lit.ref types. +# TODO: parametric aliases would be nice. +struct _LITRef[ + element_type: AnyType, + elt_is_mutable: __mlir_type.i1, + lifetime: AnyLifetime[elt_is_mutable].type, + address_space: AddressSpace = AddressSpace.GENERIC, +]: + alias type = __mlir_type[ + `!lit.ref<`, + element_type, + `, `, + lifetime, + `, `, + address_space._value.value, + `>`, + ] + + +@value +@register_passable("trivial") +struct Reference[ + type: AnyType, + is_mutable: __mlir_type.i1, + lifetime: AnyLifetime[is_mutable].type, + address_space: AddressSpace = AddressSpace.GENERIC, +]: + """Defines a non-nullable safe reference. + + Parameters: + type: Type of the underlying data. + is_mutable: Whether the referenced data may be mutated through this. + lifetime: The lifetime of the reference. + address_space: The address space of the referenced data. + """ + + alias mlir_ref_type = _LITRef[ + type, is_mutable, lifetime, address_space + ].type + + var value: Self.mlir_ref_type + """The underlying MLIR reference.""" + + @always_inline("nodebug") + fn __init__(inout self, value: Self.mlir_ref_type): + """Constructs a Reference from the MLIR reference. + + Args: + value: The MLIR reference. + """ + self.value = value + + @always_inline("nodebug") + fn __refitem__(self) -> Self.mlir_ref_type: + """Enable subscript syntax `ref[]` to access the element. + + Returns: + The MLIR reference for the Mojo compiler to use. + """ + return self.value + + @always_inline("nodebug") + fn __mlir_ref__(self) -> Self.mlir_ref_type: + """Enable the Mojo compiler to see into `Reference`. + + Returns: + The MLIR reference for the Mojo compiler to use. + """ + return self.value + + # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType + # disagreement. Use AnyPointer instead! + @always_inline("nodebug") + fn get_unsafe_pointer(self) -> Pointer[type, address_space]: + """Constructs a Pointer from a safe reference. + + Returns: + Constructed Pointer object. + """ + # Work around AnyRegType vs AnyType. + return __mlir_op.`pop.pointer.bitcast`[ + _type = Pointer[type, address_space].pointer_type + ](AnyPointer(self).value) + + @always_inline("nodebug") + fn bitcast_element[ + new_element_type: AnyType + ](self) -> Reference[new_element_type, is_mutable, lifetime, address_space]: + """Cast the reference to one of another element type, but the same + lifetime, mutability, and address space. + + Parameters: + new_element_type: The result type. + + Returns: + The new reference. + """ + # We don't have a `lit.ref.cast`` operation, so convert through a KGEN + # pointer. + return AnyPointer(self).bitcast_element[new_element_type]()[] + + @always_inline + fn address_space_cast[ + new_address_space: AddressSpace + ](self) -> Reference[type, is_mutable, lifetime, new_address_space]: + """Cast the reference to one of another address space, but the same + element type, lifetime, and mutability. + + Parameters: + new_address_space: The address space of the result. + + Returns: + The new reference. + """ + # We don't have a `lit.ref.cast`` operation, so convert through a KGEN + # pointer. + return AnyPointer(self).address_space_cast[new_address_space]()[] diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 2103fa7c6e..7ae31a943c 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -10,12 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -"""Implements classes for working with unsafe pointers. +"""Implements types that work with unsafe pointers. You can import these APIs from the `memory` package. For example: ```mojo -from memory.unsafe import Pointer, AnyLifetime +from memory.unsafe import Pointer ``` """ @@ -32,6 +32,7 @@ from sys.intrinsics import prefetch as _prefetch from sys.intrinsics import strided_load, strided_store from .memory import _free, _malloc +from memory.reference import _LITRef # ===----------------------------------------------------------------------===# # Utilities @@ -258,296 +259,6 @@ fn bitcast[ ](val.value) -# ===----------------------------------------------------------------------===# -# AddressSpace -# ===----------------------------------------------------------------------===# - - -@value -@register_passable("trivial") -struct _GPUAddressSpace(EqualityComparable): - var _value: Int - - # See https://docs.nvidia.com/cuda/nvvm-ir-spec/#address-space - alias GENERIC = AddressSpace(0) - """Generic address space.""" - alias GLOBAL = AddressSpace(1) - """Global address space.""" - alias CONSTANT = AddressSpace(2) - """Constant address space.""" - alias SHARED = AddressSpace(3) - """Shared address space.""" - alias PARAM = AddressSpace(4) - """Param address space.""" - alias LOCAL = AddressSpace(5) - """Local address space.""" - - @always_inline("nodebug") - fn __init__(value: Int) -> Self: - return Self {_value: value} - - @always_inline("nodebug") - fn value(self) -> Int: - """The integral value of the address space. - - Returns: - The integral value of the address space. - """ - return self._value - - @always_inline("nodebug") - fn __int__(self) -> Int: - """The integral value of the address space. - - Returns: - The integral value of the address space. - """ - return self._value - - @always_inline("nodebug") - fn __eq__(self, other: Self) -> Bool: - """The True if the two address spaces are equal and False otherwise. - - Returns: - True if the two address spaces are equal and False otherwise. - """ - return self.value() == other.value() - - @always_inline("nodebug") - fn __eq__(self, other: AddressSpace) -> Bool: - """The True if the two address spaces are equal and False otherwise. - - Returns: - True if the two address spaces are equal and False otherwise. - """ - return self.value() == other.value() - - @always_inline("nodebug") - fn __ne__(self, other: Self) -> Bool: - """True if the two address spaces are inequal and False otherwise. - - Args: - other: The other address space value. - - Returns: - True if the two address spaces are inequal and False otherwise. - """ - return not self == other - - @always_inline("nodebug") - fn __ne__(self, other: AddressSpace) -> Bool: - """True if the two address spaces are inequal and False otherwise. - - Args: - other: The other address space value. - - Returns: - True if the two address spaces are inequal and False otherwise. - """ - return not self == other - - -@value -@register_passable("trivial") -struct AddressSpace(EqualityComparable): - """Address space of the pointer.""" - - var _value: Int - - alias GENERIC = AddressSpace(0) - """Generic address space.""" - - @always_inline("nodebug") - fn __init__(value: Int) -> Self: - """Initializes the address space from the underlying integeral value. - - Args: - value: The address space value. - - Returns: - The address space. - """ - return Self {_value: value} - - @always_inline("nodebug") - fn __init__(value: _GPUAddressSpace) -> Self: - """Initializes the address space from the underlying integeral value. - - Args: - value: The address space value. - - Returns: - The address space. - """ - return Self {_value: int(value)} - - @always_inline("nodebug") - fn value(self) -> Int: - """The integral value of the address space. - - Returns: - The integral value of the address space. - """ - return self._value - - @always_inline("nodebug") - fn __int__(self) -> Int: - """The integral value of the address space. - - Returns: - The integral value of the address space. - """ - return self._value - - @always_inline("nodebug") - fn __eq__(self, other: Self) -> Bool: - """True if the two address spaces are equal and False otherwise. - - Args: - other: The other address space value. - - Returns: - True if the two address spaces are equal and False otherwise. - """ - return self.value() == other.value() - - @always_inline("nodebug") - fn __ne__(self, other: Self) -> Bool: - """True if the two address spaces are inequal and False otherwise. - - Args: - other: The other address space value. - - Returns: - True if the two address spaces are inequal and False otherwise. - """ - return not self == other - - -# ===----------------------------------------------------------------------===# -# Reference -# ===----------------------------------------------------------------------===# - - -# Helper to build !lit.ref types. -# TODO: parametric aliases would be nice. -struct _LITRef[ - element_type: AnyType, - elt_is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[elt_is_mutable].type, - address_space: AddressSpace = AddressSpace.GENERIC, -]: - alias type = __mlir_type[ - `!lit.ref<`, - element_type, - `, `, - lifetime, - `, `, - address_space._value.value, - `>`, - ] - - -@value -@register_passable("trivial") -struct Reference[ - type: AnyType, - is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[is_mutable].type, - address_space: AddressSpace = AddressSpace.GENERIC, -]: - """Defines a non-nullable safe reference. - - Parameters: - type: Type of the underlying data. - is_mutable: Whether the referenced data may be mutated through this. - lifetime: The lifetime of the reference. - address_space: The address space of the referenced data. - """ - - alias mlir_ref_type = _LITRef[ - type, is_mutable, lifetime, address_space - ].type - - var value: Self.mlir_ref_type - """The underlying MLIR reference.""" - - @always_inline("nodebug") - fn __init__(inout self, value: Self.mlir_ref_type): - """Constructs a Reference from the MLIR reference. - - Args: - value: The MLIR reference. - """ - self.value = value - - @always_inline("nodebug") - fn __refitem__(self) -> Self.mlir_ref_type: - """Enable subscript syntax `ref[]` to access the element. - - Returns: - The MLIR reference for the Mojo compiler to use. - """ - return self.value - - @always_inline("nodebug") - fn __mlir_ref__(self) -> Self.mlir_ref_type: - """Enable the Mojo compiler to see into `Reference`. - - Returns: - The MLIR reference for the Mojo compiler to use. - """ - return self.value - - # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType - # disagreement. Use AnyPointer instead! - @always_inline("nodebug") - fn get_unsafe_pointer(self) -> Pointer[type, address_space]: - """Constructs a Pointer from a safe reference. - - Returns: - Constructed Pointer object. - """ - # Work around AnyRegType vs AnyType. - return __mlir_op.`pop.pointer.bitcast`[ - _type = Pointer[type, address_space].pointer_type - ](AnyPointer(self).value) - - @always_inline("nodebug") - fn bitcast_element[ - new_element_type: AnyType - ](self) -> Reference[new_element_type, is_mutable, lifetime, address_space]: - """Cast the reference to one of another element type, but the same - lifetime, mutability, and address space. - - Parameters: - new_element_type: The result type. - - Returns: - The new reference. - """ - # We don't have a `lit.ref.cast`` operation, so convert through a KGEN - # pointer. - return AnyPointer(self).bitcast_element[new_element_type]()[] - - @always_inline - fn address_space_cast[ - new_address_space: AddressSpace - ](self) -> Reference[type, is_mutable, lifetime, new_address_space]: - """Cast the reference to one of another address space, but the same - element type, lifetime, and mutability. - - Parameters: - new_address_space: The address space of the result. - - Returns: - The new reference. - """ - # We don't have a `lit.ref.cast`` operation, so convert through a KGEN - # pointer. - return AnyPointer(self).address_space_cast[new_address_space]()[] - - # ===----------------------------------------------------------------------===# # Pointer # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 175d1664a2..ae3c7a7da2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -21,7 +21,8 @@ from sys.intrinsics import PrefetchLocality from sys.info import sizeof -from memory.unsafe import AddressSpace, DTypePointer +from memory.reference import AddressSpace +from memory.unsafe import DTypePointer # ===----------------------------------------------------------------------===# # llvm_intrinsic diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index 6b68c4727b..c14b53bee2 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -13,7 +13,8 @@ from pathlib import Path -from memory.unsafe import AddressSpace, DTypePointer, bitcast +from memory.reference import AddressSpace +from memory.unsafe import DTypePointer, bitcast alias _kStartTensorMarker = "[" alias _kEndTensorMarker = "]" diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index ccfaa5a728..0fbc673029 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -41,7 +41,7 @@ print(to_string(who_knows)) from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq -from memory.unsafe import _LITRef +from memory.reference import _LITRef from memory.anypointer import * from utils.loop import unroll From 962c93a4309d83ec70ad0f3cc9855b424baadccd Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 8 Apr 2024 23:24:27 -0700 Subject: [PATCH 0117/2019] [mojo-docs] Note that `Reference` moved to a new submodule (#37152) MODULAR_ORIG_COMMIT_REV_ID: 213db9cc6a8e05784be5efab9e772367f180ac96 --- docs/changelog.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 562f9918ba..277f3b6637 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -102,12 +102,13 @@ and tools. Please add any significant user-visible changes here. 4) `AnyPointer` can be initialized from a `Reference` as mentioned above. 5) It has some new methods like `address_space_cast`. - The `Reference` type has several changes, including: - 1) `Reference` now has an unsafe `address_space_cast` method like `Pointer`. - 2) The `destroy_element_unsafe` method has been removed, do this with + 1) It is now located in `memory.reference` instead of `memory.unsafe`. + 2) `Reference` now has an unsafe `address_space_cast` method like `Pointer`. + 3) The `destroy_element_unsafe` method has been removed, do this with `AnyPointer/destroy_pointee`, which is more obviously unsafe. - 3) The `emplace_ref_unsafe` function has been removed in favor of + 4) The `emplace_ref_unsafe` function has been removed in favor of `AnyPointer/initialize_pointee`. - 4) The `offset` method has been removed, it was unsafe and belongs on + 5) The `offset` method has been removed, it was unsafe and belongs on `AnyPointer`. ### ❌ Removed From 94db3d421a7e6f68e6cb5a13400591516a95db2f Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 9 Apr 2024 09:43:43 -0700 Subject: [PATCH 0118/2019] [stdlib] Initial changes for AnyPointer to UnsafePointer stdlib migration (#37135) This change migrates the uses of `AnyPointer` to `UnsafePointer` within the Stdlib plus some other low-hanging fruit use cases. I didn't change the `AnyPointer` implementation to `UnsafePointer` because it generated compiler errors outside of the stdlib that I will resolve in a future change. This change adds an alias for `UnsafePointer` that will be removed in the future as it is only temporarily useful during the migration. This change is in support of #37054. MODULAR_ORIG_COMMIT_REV_ID: d72ab52e12df6d7c7ce90c61a9e06b9c20a5e507 --- stdlib/src/builtin/builtin_list.mojo | 4 +- stdlib/src/builtin/string.mojo | 8 +-- stdlib/src/builtin/tuple.mojo | 12 ++--- stdlib/src/collections/dict.mojo | 6 +-- stdlib/src/collections/list.mojo | 14 +++--- stdlib/src/memory/__init__.mojo | 1 + stdlib/src/memory/_arc.mojo | 6 +-- stdlib/src/memory/anypointer.mojo | 2 + stdlib/src/memory/reference.mojo | 8 +-- stdlib/src/utils/variant.mojo | 6 +-- ...nypointer.mojo => test_unsafepointer.mojo} | 50 +++++++++---------- 11 files changed, 60 insertions(+), 57 deletions(-) rename stdlib/test/memory/{test_anypointer.mojo => test_unsafepointer.mojo} (70%) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index a6857f3539..b0f7ac7a63 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -332,7 +332,7 @@ struct VariadicListMem[ # destroy in backwards order to match how arguments are normally torn # down when CheckLifetimes is left to its own devices. for i in range(len(self), 0, -1): - destroy_pointee(AnyPointer(Reference(self[i - 1]))) + destroy_pointee(UnsafePointer(Reference(self[i - 1]))) @always_inline fn __len__(self) -> Int: @@ -471,7 +471,7 @@ struct VariadicPack[ @parameter fn destroy_elt[i: Int](): # destroy the elements in reverse order. - destroy_pointee(AnyPointer(self.get_element[len - i - 1]())) + destroy_pointee(UnsafePointer(self.get_element[len - i - 1]())) unroll[destroy_elt, len]() diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index f282c37330..bd0abcbbba 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -20,7 +20,7 @@ from collections.dict import KeyElement from sys import llvm_intrinsic from sys.info import bitwidthof -from memory.anypointer import AnyPointer +from memory.anypointer import UnsafePointer from memory.memory import memcmp, memcpy from memory.unsafe import DTypePointer, Pointer @@ -352,7 +352,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): len: The length of the buffer, including the null terminator. """ self._buffer = Self._buffer_type() - self._buffer.data = rebind[AnyPointer[Int8]](ptr) + self._buffer.data = rebind[UnsafePointer[Int8]](ptr) self._buffer.size = len @always_inline @@ -695,7 +695,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): The pointer to the underlying memory. """ var ptr = self._as_ptr() - self._buffer.data = AnyPointer[Int8]() + self._buffer.data = UnsafePointer[Int8]() self._buffer.size = 0 self._buffer.capacity = 0 return ptr @@ -1088,7 +1088,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): fn _vec_fmt[ *types: AnyRegType ]( - str: AnyPointer[Int8], + str: UnsafePointer[Int8], size: Int, fmt: StringLiteral, borrowed *arguments: *types, diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index daa6f5b2d2..c31cd613e2 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -58,7 +58,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO: We could be fancier and take the values out of an owned # pack. For now just keep everything simple and copy the element. initialize_pointee( - AnyPointer(self._refitem__[idx]()), args.get_element[idx]()[] + UnsafePointer(self._refitem__[idx]()), args.get_element[idx]()[] ) unroll[initialize_elt, Self.__len__()]() @@ -70,7 +70,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # trivial and won't do anything. @parameter fn destroy_elt[idx: Int](): - destroy_pointee(AnyPointer(self._refitem__[idx]())) + destroy_pointee(UnsafePointer(self._refitem__[idx]())) unroll[destroy_elt, Self.__len__()]() @@ -88,10 +88,10 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): - var existing_elt_ptr = AnyPointer(existing._refitem__[idx]()) + var existing_elt_ptr = UnsafePointer(existing._refitem__[idx]()) initialize_pointee( - AnyPointer(self._refitem__[idx]()), + UnsafePointer(self._refitem__[idx]()), __get_address_as_owned_value(existing_elt_ptr.value), ) @@ -112,7 +112,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): initialize_pointee( - AnyPointer(self._refitem__[idx]()), + UnsafePointer(self._refitem__[idx]()), existing._refitem__[idx]()[], ) @@ -165,7 +165,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): storage_kgen_ptr ) # Convert to an immortal mut reference, which conforms to self_life. - return AnyPointer(elt_kgen_ptr)[] + return UnsafePointer(elt_kgen_ptr)[] # TODO: Remove the get methods in favor of __refitem__ some day. This will # be annoying if we don't have autoderef though. diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 2715d40fc7..03fc4f15d7 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -31,7 +31,7 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ -from memory.anypointer import AnyPointer +from memory.anypointer import UnsafePointer from .optional import Optional @@ -133,7 +133,7 @@ struct _DictKeyIter[ fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() - var anyptr = AnyPointer(Reference(entry_ref[].key)) + var anyptr = UnsafePointer(Reference(entry_ref[].key)) return anyptr.address_space_cast[Self.dict_entry_iter.address_space]()[] fn __len__(self) -> Int: @@ -172,7 +172,7 @@ struct _DictValueIter[ var entry_ref = self.iter.__next__() # Cast through a pointer to grant additional mutability and switch # address spaces out. - var anyptr = AnyPointer(Reference(entry_ref[].value)) + var anyptr = UnsafePointer(Reference(entry_ref[].value)) return anyptr.address_space_cast[address_space]()[] fn __len__(self) -> Int: diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 31b1017c30..9ade2a2406 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -79,7 +79,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): T: The type of the elements. """ - var data: AnyPointer[T] + var data: UnsafePointer[T] """The underlying storage for the list.""" var size: Int """The number of elements in the list.""" @@ -88,7 +88,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): fn __init__(inout self): """Constructs an empty list.""" - self.data = AnyPointer[T]() + self.data = UnsafePointer[T]() self.size = 0 self.capacity = 0 @@ -108,7 +108,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): Args: capacity: The requested capacity of the list. """ - self.data = AnyPointer[T].alloc(capacity) + self.data = UnsafePointer[T].alloc(capacity) self.size = 0 self.capacity = capacity @@ -162,7 +162,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): @always_inline fn _realloc(inout self, new_capacity: Int): - var new_data = AnyPointer[T].alloc(new_capacity) + var new_data = UnsafePointer[T].alloc(new_capacity) for i in range(self.size): move_pointee(src=self.data + i, dst=new_data + i) @@ -327,14 +327,14 @@ struct List[T: CollectionElement](CollectionElement, Sized): destroy_pointee(self.data + i) self.size = 0 - fn steal_data(inout self) -> AnyPointer[T]: + fn steal_data(inout self) -> UnsafePointer[T]: """Take ownership of the underlying pointer from the list. Returns: The underlying data. """ var ptr = self.data - self.data = AnyPointer[T]() + self.data = UnsafePointer[T]() self.size = 0 self.capacity = 0 return ptr @@ -440,7 +440,7 @@ struct List[T: CollectionElement](CollectionElement, Sized): # Mutability gets set to the local mutability of this # pointer value, ie. because we defined it with `let` it's now an # "immutable" reference regardless of the mutability of `self`. - # This means we can't just use `AnyPointer.__refitem__` here + # This means we can't just use `UnsafePointer.__refitem__` here # because the mutability won't match. var base_ptr = Reference(self)[].data return __mlir_op.`lit.ref.from_pointer`[ diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 995839b726..e8fec4a990 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -22,6 +22,7 @@ from .memory import ( from .anypointer import ( AnyPointer, + UnsafePointer, ) from .unsafe import ( diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 55c1ece611..75e750f406 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -23,7 +23,7 @@ print(3 == p.get()) ``` """ -from memory.anypointer import AnyPointer +from memory.anypointer import UnsafePointer from memory.memory import stack_allocation @@ -155,8 +155,8 @@ struct Arc[T: Movable](CollectionElement): ) ) - fn _data_ptr(self) -> AnyPointer[T]: - return AnyPointer.address_of(self._inner[].data) + fn _data_ptr(self) -> UnsafePointer[T]: + return UnsafePointer.address_of(self._inner[].data) fn _bitcast[T2: Movable](self) -> Arc[T2]: constrained[ diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/anypointer.mojo index e997602a72..783def190f 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/anypointer.mojo @@ -25,6 +25,8 @@ from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc from memory.reference import _LITRef +alias UnsafePointer = AnyPointer + @register_passable("trivial") struct AnyPointer[ diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 92f406741b..a126eb8973 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -261,7 +261,7 @@ struct Reference[ return self.value # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType - # disagreement. Use AnyPointer instead! + # disagreement. Use UnsafePointer instead! @always_inline("nodebug") fn get_unsafe_pointer(self) -> Pointer[type, address_space]: """Constructs a Pointer from a safe reference. @@ -272,7 +272,7 @@ struct Reference[ # Work around AnyRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ _type = Pointer[type, address_space].pointer_type - ](AnyPointer(self).value) + ](UnsafePointer(self).value) @always_inline("nodebug") fn bitcast_element[ @@ -289,7 +289,7 @@ struct Reference[ """ # We don't have a `lit.ref.cast`` operation, so convert through a KGEN # pointer. - return AnyPointer(self).bitcast_element[new_element_type]()[] + return UnsafePointer(self).bitcast_element[new_element_type]()[] @always_inline fn address_space_cast[ @@ -306,4 +306,4 @@ struct Reference[ """ # We don't have a `lit.ref.cast`` operation, so convert through a KGEN # pointer. - return AnyPointer(self).address_space_cast[new_address_space]()[] + return UnsafePointer(self).address_space_cast[new_address_space]()[] diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 0fbc673029..caed7c67b2 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -148,7 +148,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): ] var _impl: Self._type - fn _get_ptr[T: CollectionElement](self) -> AnyPointer[T]: + fn _get_ptr[T: CollectionElement](self) -> UnsafePointer[T]: constrained[ Self._check[T]() != Self._sentinel, "not a union element type" ]() @@ -157,7 +157,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn _get_state[ is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] ](self: _LITRef[Self, is_mut, lt].type) -> Reference[Int8, is_mut, lt]: - var int8_self = AnyPointer(Reference(self).bitcast_element[Int8]()) + var int8_self = UnsafePointer(Reference(self).bitcast_element[Int8]()) return (int8_self + _UnionSize[Ts].compute())[] fn __init__[T: CollectionElement](inout self, owned value: T): @@ -189,7 +189,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): if self._get_state()[] == i: alias T = Ts[i] initialize_pointee[T]( - AnyPointer(Reference(self._impl).bitcast_element[T]()), + UnsafePointer(Reference(self._impl).bitcast_element[T]()), Reference(other._impl).bitcast_element[T]()[], ) diff --git a/stdlib/test/memory/test_anypointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo similarity index 70% rename from stdlib/test/memory/test_anypointer.mojo rename to stdlib/test/memory/test_unsafepointer.mojo index dddaf0856a..77c4a19f1e 100644 --- a/stdlib/test/memory/test_anypointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -31,11 +31,11 @@ struct MoveOnlyType(Movable): print("deleted", self.value) -fn test_anypointer_of_move_only_type(): - # CHECK-LABEL: === test_anypointer - print("=== test_anypointer") +fn test_unsafepointer_of_move_only_type(): + # CHECK-LABEL: === test_unsafepointer + print("=== test_unsafepointer") - var ptr = AnyPointer[MoveOnlyType].alloc(1) + var ptr = UnsafePointer[MoveOnlyType].alloc(1) # CHECK: moved 42 initialize_pointee(ptr, MoveOnlyType(42)) # CHECK: moved 42 @@ -46,20 +46,20 @@ fn test_anypointer_of_move_only_type(): ptr.free() -def test_anypointer_move_pointee_move_count(): - var ptr = AnyPointer[MoveCounter[Int]].alloc(1) +def test_unsafepointer_move_pointee_move_count(): + var ptr = UnsafePointer[MoveCounter[Int]].alloc(1) var value = MoveCounter(5) assert_equal(0, value.move_count) initialize_pointee(ptr, value^) # ----- - # Test that `AnyPointer.move_pointee` performs exactly one move. + # Test that `UnsafePointer.move_pointee` performs exactly one move. # ----- assert_equal(1, ptr[].move_count) - var ptr_2 = AnyPointer[MoveCounter[Int]].alloc(1) + var ptr_2 = UnsafePointer[MoveCounter[Int]].alloc(1) move_pointee(src=ptr, dst=ptr_2) @@ -67,7 +67,7 @@ def test_anypointer_move_pointee_move_count(): def test_refitem(): - var ptr = AnyPointer[Int].alloc(1) + var ptr = UnsafePointer[Int].alloc(1) ptr[0] = 0 ptr[] += 1 assert_equal(ptr[], 1) @@ -75,7 +75,7 @@ def test_refitem(): def test_refitem_offset(): - var ptr = AnyPointer[Int].alloc(5) + var ptr = UnsafePointer[Int].alloc(5) for i in range(5): ptr[i] = i for i in range(5): @@ -85,7 +85,7 @@ def test_refitem_offset(): def test_address_of(): var local = 1 - assert_not_equal(0, int(AnyPointer[Int].address_of(local))) + assert_not_equal(0, int(UnsafePointer[Int].address_of(local))) def test_bitcast(): @@ -98,11 +98,11 @@ def test_bitcast(): assert_equal(int(ptr), int(aliased_ptr)) -def test_anypointer_string(): - var nullptr = AnyPointer[Int]() +def test_unsafepointer_string(): + var nullptr = UnsafePointer[Int]() assert_equal(str(nullptr), "0x0") - var ptr = AnyPointer[Int].alloc(1) + var ptr = UnsafePointer[Int].alloc(1) assert_true(str(ptr).startswith("0x")) assert_not_equal(str(ptr), "0x0") ptr.free() @@ -110,20 +110,20 @@ def test_anypointer_string(): def test_eq(): var local = 1 - var p1 = AnyPointer[Int].address_of(local) + var p1 = UnsafePointer[Int].address_of(local) var p2 = p1 assert_equal(p1, p2) var other_local = 2 - var p3 = AnyPointer[Int].address_of(other_local) + var p3 = UnsafePointer[Int].address_of(other_local) assert_not_equal(p1, p3) - var p4 = AnyPointer[Int].address_of(local) + var p4 = UnsafePointer[Int].address_of(local) assert_equal(p1, p4) def test_comparisons(): - var p1 = AnyPointer[Int].alloc(1) + var p1 = UnsafePointer[Int].alloc(1) assert_true((p1 - 1) < p1) assert_true((p1 - 1) <= p1) @@ -135,11 +135,11 @@ def test_comparisons(): p1.free() -def test_anypointer_address_space(): - var p1 = AnyPointer[Int, AddressSpace(0)].alloc(1) +def test_unsafepointer_address_space(): + var p1 = UnsafePointer[Int, AddressSpace(0)].alloc(1) p1.free() - var p2 = AnyPointer[Int, AddressSpace.GENERIC].alloc(1) + var p2 = UnsafePointer[Int, AddressSpace.GENERIC].alloc(1) p2.free() @@ -149,12 +149,12 @@ def main(): test_refitem() test_refitem_offset() - test_anypointer_of_move_only_type() - test_anypointer_move_pointee_move_count() + test_unsafepointer_of_move_only_type() + test_unsafepointer_move_pointee_move_count() test_bitcast() - test_anypointer_string() + test_unsafepointer_string() test_eq() test_comparisons() - test_anypointer_address_space() + test_unsafepointer_address_space() From 10c3c959266992ccfa9d4c694fd71c94b1ea04fc Mon Sep 17 00:00:00 2001 From: River Riddle Date: Tue, 9 Apr 2024 10:47:35 -0600 Subject: [PATCH 0119/2019] [mojo] Remove compiled env from packages/package links (#36821) We've moved to a model where packages grab the current compilation environment from the use of the package, the env is the last bit of state that we were still giving the illusion of being useful during the packaging step. MODULAR_ORIG_COMMIT_REV_ID: 7a4e3a2c137eb132f33dfa1cf76eaf996b5aa8a5 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 277f3b6637..f5d58d01a7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -111,6 +111,10 @@ and tools. Please add any significant user-visible changes here. 5) The `offset` method has been removed, it was unsafe and belongs on `AnyPointer`. +- The `mojo package` command no longer supports the `-D` flag. All compilation + environment flags should be provided at the point of package use + (e.g. `mojo run` or `mojo build`). + ### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of From 98d47df0016bcc2e4ca54d2c56ef277798579c14 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 9 Apr 2024 13:55:38 -0400 Subject: [PATCH 0120/2019] [mojo-lang] Null the underlying data when deleting a decl (#37130) `SharedState::deleteDecl` calls erase the underlying op for the `ASTDecl`, but anyone who might have a reference to the decl itself might still dereference a pointer to the erased op after that. This patch fixes that. Closes https://github.com/modularml/mojo/issues/1676. MODULAR_ORIG_COMMIT_REV_ID: ccaa41803f0da936f16adc8b1051699806ff4ca3 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index f5d58d01a7..6020911794 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -162,3 +162,6 @@ and tools. Please add any significant user-visible changes here. - [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed and formatted correctly by `mojo format`. + +- [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to + `@value` decorator and structs with empty body. From 5ab3b8e539be5efb04cc8a78c94b41cae1f16928 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 9 Apr 2024 11:49:30 -0700 Subject: [PATCH 0121/2019] [docs] Add separate changelog for "released" changes (#37209) `max/changelog.md` is still the place for the team to add notes. This follows the Mojo changelog pattern to avoid conflicts and make it easier to update released changelog for patch releases. MODULAR_ORIG_COMMIT_REV_ID: f9e16bea5ec58c101dbcfdb6bbc03d074049de3a --- docs/changelog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6020911794..19697b62c1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,9 @@ +# Mojo unreleased changelog -This is a running list of significant UNRELEASED changes for the Mojo language -and tools. Please add any significant user-visible changes here. +This is a list of UNRELEASED changes for the Mojo language and tools. + +When we cut a release, these notes move to `changelog-released.md` and that's +what we publish. [//]: # Here's the template to use when starting a new batch of notes: [//]: ## UNRELEASED From 5fba9cd9d81bb3d7b2b4e3fe9396bd9bac79ecf0 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Tue, 9 Apr 2024 15:20:02 -0400 Subject: [PATCH 0122/2019] [mojo-lang] Fix crash when creating tuple that has element with syntax error (#37163) Closes https://github.com/modularml/mojo/issues/1917. MODULAR_ORIG_COMMIT_REV_ID: 4b90ac2261a6056c488310a4fd0e6e12c3b76cac --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 19697b62c1..6dc5014d46 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -168,3 +168,6 @@ what we publish. - [#1676](https://github.com/modularml/mojo/issues/1676) Fix a crash related to `@value` decorator and structs with empty body. + +- [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after + syntax error during tuple creation From 536a2d16c2a9140ced761e54043017f4a002c5a5 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 9 Apr 2024 13:32:19 -0700 Subject: [PATCH 0123/2019] [stdlib] Renaming AnyPointer to UnsafePointer (#37173) This change renames AnyPointer to UnsafePointer and introduces a backwards compatibility alias for AnyPointer. This also does a file rename from `anypointer.mojo` to `unsafepointer.mojo` including all the required fix-ups. This change is in support of [Internal link] MODULAR_ORIG_COMMIT_REV_ID: c3c2388a84bfc2aac9d9a7acfcbfd0654fd6c103 --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/string.mojo | 2 +- stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/_arc.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 4 +-- .../{anypointer.mojo => unsafepointer.mojo} | 30 +++++++++---------- stdlib/src/os/os.mojo | 2 +- stdlib/src/utils/variant.mojo | 2 +- 10 files changed, 25 insertions(+), 25 deletions(-) rename stdlib/src/memory/{anypointer.mojo => unsafepointer.mojo} (93%) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index b0f7ac7a63..2b1c9a776a 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -16,7 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from memory.reference import Reference, _LITRef -from memory.anypointer import * +from memory.unsafepointer import * # ===----------------------------------------------------------------------===# # ListLiteral diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index bd0abcbbba..c6cc3b3a2f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -20,7 +20,7 @@ from collections.dict import KeyElement from sys import llvm_intrinsic from sys.info import bitwidthof -from memory.anypointer import UnsafePointer +from memory.unsafepointer import UnsafePointer from memory.memory import memcmp, memcpy from memory.unsafe import DTypePointer, Pointer diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 03fc4f15d7..5227c05439 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -31,7 +31,7 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ -from memory.anypointer import UnsafePointer +from memory.unsafepointer import UnsafePointer from .optional import Optional diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9ade2a2406..d485890268 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,7 +20,7 @@ from collections import List """ -from memory.anypointer import * +from memory.unsafepointer import * from memory.unsafe import Reference # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index e8fec4a990..6d7acb1594 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -20,7 +20,7 @@ from .memory import ( stack_allocation, ) -from .anypointer import ( +from .unsafepointer import ( AnyPointer, UnsafePointer, ) diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 75e750f406..e8a0de0996 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -23,7 +23,7 @@ print(3 == p.get()) ``` """ -from memory.anypointer import UnsafePointer +from memory.unsafepointer import UnsafePointer from memory.memory import stack_allocation diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 7ae31a943c..2fee07f8f3 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -100,10 +100,10 @@ fn bitcast[ @always_inline("nodebug") fn bitcast[ new_type: AnyType, src_type: AnyType, address_space: AddressSpace -](ptr: AnyPointer[src_type, address_space]) -> AnyPointer[ +](ptr: UnsafePointer[src_type, address_space]) -> UnsafePointer[ new_type, address_space ]: - """Bitcasts an AnyPointer to a different type. + """Bitcasts an UnsafePointer to a different type. Parameters: new_type: The target type. diff --git a/stdlib/src/memory/anypointer.mojo b/stdlib/src/memory/unsafepointer.mojo similarity index 93% rename from stdlib/src/memory/anypointer.mojo rename to stdlib/src/memory/unsafepointer.mojo index 783def190f..2399517943 100644 --- a/stdlib/src/memory/anypointer.mojo +++ b/stdlib/src/memory/unsafepointer.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory.anypointer import AnyPointer +from memory.unsafepointer import UnsafePointer ``` """ @@ -25,11 +25,11 @@ from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc from memory.reference import _LITRef -alias UnsafePointer = AnyPointer +alias AnyPointer = UnsafePointer @register_passable("trivial") -struct AnyPointer[ +struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ](Boolable, CollectionElement, Stringable, Intable, EqualityComparable): """This is a pointer type that can point to any generic value that is @@ -37,7 +37,7 @@ struct AnyPointer[ Parameters: T: The type the pointer points to. - address_space: The address space associated with the AnyPointer allocated memory. + address_space: The address space associated with the UnsafePointer allocated memory. """ alias pointer_type = __mlir_type[ @@ -72,7 +72,7 @@ struct AnyPointer[ @always_inline fn __init__(value: Reference[T, _, _, address_space]) -> Self: - """Create an unsafe AnyPointer from a safe Reference. + """Create an unsafe UnsafePointer from a safe Reference. Args: value: The input reference to construct with. @@ -110,7 +110,7 @@ struct AnyPointer[ arg: The value to get the address of. Returns: - An AnyPointer which contains the address of the argument. + An UnsafePointer which contains the address of the argument. """ return __mlir_op.`pop.pointer.bitcast`[_type = Self.pointer_type]( __get_lvalue_as_address(arg) @@ -126,7 +126,7 @@ struct AnyPointer[ @always_inline fn bitcast_element[ new_type: AnyType - ](self) -> AnyPointer[new_type, address_space]: + ](self) -> UnsafePointer[new_type, address_space]: """Bitcasts the pointer to a different type. Parameters: @@ -138,13 +138,13 @@ struct AnyPointer[ """ return __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[new_type, address_space].pointer_type + _type = UnsafePointer[new_type, address_space].pointer_type ](self.value) @always_inline fn address_space_cast[ new_address_space: AddressSpace - ](self) -> AnyPointer[T, new_address_space]: + ](self) -> UnsafePointer[T, new_address_space]: """Bitcasts the pointer to a different address space. Parameters: @@ -156,7 +156,7 @@ struct AnyPointer[ """ return __mlir_op.`pop.pointer.bitcast`[ - _type = AnyPointer[T, new_address_space].pointer_type + _type = UnsafePointer[T, new_address_space].pointer_type ](self.value) @always_inline @@ -326,7 +326,7 @@ struct AnyPointer[ # ===----------------------------------------------------------------------=== # -# AnyPointer extensions +# UnsafePointer extensions # ===----------------------------------------------------------------------=== # # TODO: These should be methods when we have conditional conformance. @@ -334,7 +334,7 @@ struct AnyPointer[ # This isn't a method because destructors only work in the default address # space. @always_inline -fn destroy_pointee(ptr: AnyPointer[_, AddressSpace.GENERIC]): +fn destroy_pointee(ptr: UnsafePointer[_, AddressSpace.GENERIC]): """Destroy the pointed-to value. The pointer must not be null, and the pointer memory location is assumed @@ -347,7 +347,7 @@ fn destroy_pointee(ptr: AnyPointer[_, AddressSpace.GENERIC]): @always_inline -fn move_from_pointee[T: Movable](ptr: AnyPointer[T, _]) -> T: +fn move_from_pointee[T: Movable](ptr: UnsafePointer[T, _]) -> T: """Move the value at the pointer out. The pointer must not be null, and the pointer memory location is assumed @@ -371,7 +371,7 @@ fn move_from_pointee[T: Movable](ptr: AnyPointer[T, _]) -> T: @always_inline -fn initialize_pointee[T: Movable](ptr: AnyPointer[T, _], owned value: T): +fn initialize_pointee[T: Movable](ptr: UnsafePointer[T, _], owned value: T): """Emplace a new value into the pointer location. The pointer memory location is assumed to contain uninitialized data, @@ -390,7 +390,7 @@ fn initialize_pointee[T: Movable](ptr: AnyPointer[T, _], owned value: T): @always_inline -fn move_pointee[T: Movable](*, src: AnyPointer[T, _], dst: AnyPointer[T]): +fn move_pointee[T: Movable](*, src: UnsafePointer[T, _], dst: UnsafePointer[T]): """Moves the value `src` points to into the memory location pointed to by `dest`. diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index dcf63eb668..b81d35fbbf 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,7 +23,7 @@ from collections import List from sys.info import os_is_linux, os_is_windows, triple_is_nvidia_cuda from memory.unsafe import DTypePointer, Pointer -from memory.anypointer import move_from_pointee +from memory.unsafepointer import move_from_pointee from utils import StringRef diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index caed7c67b2..a942f3dff6 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -42,7 +42,7 @@ from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.reference import _LITRef -from memory.anypointer import * +from memory.unsafepointer import * from utils.loop import unroll from utils.static_tuple import StaticTuple From 508619307fe2f0525d481fc4f0268c4f4d2fe639 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 9 Apr 2024 13:36:22 -0700 Subject: [PATCH 0124/2019] [stdlib] Renaming Pointer to LegacyPointer (#37210) This change renames Pointer to LegacyPointer and introduces a backwards compatibility alias for Pointer. This change is in support of [Internal link] MODULAR_ORIG_COMMIT_REV_ID: fef37faaa8550e8f95b8b99d44849258359b2292 --- stdlib/src/memory/__init__.mojo | 1 + stdlib/src/memory/unsafe.mojo | 82 +++++++++++++++++---------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 6d7acb1594..cefb4418d8 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -30,6 +30,7 @@ from .unsafe import ( AddressSpace, Reference, Pointer, + LegacyPointer, DTypePointer, ) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 2fee07f8f3..3a1ad3722b 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -260,16 +260,18 @@ fn bitcast[ # ===----------------------------------------------------------------------===# -# Pointer +# LegacyPointer # ===----------------------------------------------------------------------===# +alias Pointer = LegacyPointer + @value @register_passable("trivial") -struct Pointer[ +struct LegacyPointer[ type: AnyRegType, address_space: AddressSpace = AddressSpace.GENERIC ](Boolable, CollectionElement, Intable, Stringable, EqualityComparable): - """Defines a Pointer struct that contains the address of a register passable + """Defines a LegacyPointer struct that contains the address of a register passable type. Parameters: @@ -304,46 +306,46 @@ struct Pointer[ @always_inline("nodebug") fn __init__() -> Self: - """Constructs a null Pointer from the value of pop.pointer type. + """Constructs a null LegacyPointer from the value of pop.pointer type. Returns: - Constructed Pointer object. + Constructed LegacyPointer object. """ return Self.get_null() @always_inline("nodebug") fn __init__(address: Self) -> Self: - """Constructs a Pointer from the address. + """Constructs a LegacyPointer from the address. Args: address: The input pointer. Returns: - Constructed Pointer object. + Constructed LegacyPointer object. """ return address @always_inline("nodebug") fn __init__(address: Self.pointer_type) -> Self: - """Constructs a Pointer from the address. + """Constructs a LegacyPointer from the address. Args: address: The input pointer address. Returns: - Constructed Pointer object. + Constructed LegacyPointer object. """ return Self {address: address} @always_inline("nodebug") fn __init__(value: Scalar[DType.address]) -> Self: - """Constructs a Pointer from the value of scalar address. + """Constructs a LegacyPointer from the value of scalar address. Args: value: The input pointer index. Returns: - Constructed Pointer object. + Constructed LegacyPointer object. """ var address = __mlir_op.`pop.index_to_pointer`[ _type = Self.pointer_type @@ -353,10 +355,10 @@ struct Pointer[ @staticmethod @always_inline("nodebug") fn get_null() -> Self: - """Constructs a Pointer representing nullptr. + """Constructs a LegacyPointer representing nullptr. Returns: - Constructed nullptr Pointer object. + Constructed nullptr LegacyPointer object. """ return __mlir_attr[`#interp.pointer<0> : `, Self.pointer_type] @@ -371,10 +373,10 @@ struct Pointer[ @always_inline("nodebug") fn __bool__(self) -> Bool: - """Checks if the Pointer is null. + """Checks if the LegacyPointer is null. Returns: - Returns False if the Pointer is null and True otherwise. + Returns False if the LegacyPointer is null and True otherwise. """ return self != Self.get_null() @@ -387,7 +389,7 @@ struct Pointer[ arg: The value to get the address of. Returns: - A Pointer struct which contains the address of the argument. + A LegacyPointer struct which contains the address of the argument. """ return __mlir_op.`pop.pointer.bitcast`[_type = Self.pointer_type]( __get_lvalue_as_address(arg) @@ -395,7 +397,7 @@ struct Pointer[ @always_inline("nodebug") fn __getitem__[T: Intable](self, offset: T) -> type: - """Loads the value the Pointer object points to with the given offset. + """Loads the value the LegacyPointer object points to with the given offset. Parameters: T: The Intable type of the offset. @@ -410,7 +412,7 @@ struct Pointer[ @always_inline("nodebug") fn __setitem__[T: Intable](self, offset: T, val: type): - """Stores the specified value to the location the Pointer object points + """Stores the specified value to the location the LegacyPointer object points to with the given offset. Parameters: @@ -430,7 +432,7 @@ struct Pointer[ @always_inline("nodebug") fn load[*, alignment: Int = Self._default_alignment](self) -> type: - """Loads the value the Pointer object points to. + """Loads the value the LegacyPointer object points to. Parameters: alignment: The minimal alignment of the address. @@ -444,7 +446,7 @@ struct Pointer[ fn load[ T: Intable, *, alignment: Int = Self._default_alignment ](self, offset: T) -> type: - """Loads the value the Pointer object points to with the given offset. + """Loads the value the LegacyPointer object points to with the given offset. Parameters: T: The Intable type of the offset. @@ -464,7 +466,7 @@ struct Pointer[ fn store[ T: Intable, /, *, alignment: Int = Self._default_alignment ](self, offset: T, value: type): - """Stores the specified value to the location the Pointer object points + """Stores the specified value to the location the LegacyPointer object points to with the given offset. Parameters: @@ -479,7 +481,7 @@ struct Pointer[ @always_inline("nodebug") fn store[*, alignment: Int = Self._default_alignment](self, value: type): - """Stores the specified value to the location the Pointer object points + """Stores the specified value to the location the LegacyPointer object points to. Parameters: @@ -541,7 +543,7 @@ struct Pointer[ alignment: The alignment used for the allocation. Returns: - A new Pointer object which has been allocated on the heap. + A new LegacyPointer object which has been allocated on the heap. """ return _malloc[type, address_space=address_space]( count * sizeof[type](), alignment=alignment @@ -557,45 +559,47 @@ struct Pointer[ # ===------------------------------------------------------------------=== # @always_inline("nodebug") - fn bitcast[new_type: AnyRegType](self) -> Pointer[new_type, address_space]: - """Bitcasts a Pointer to a different type. + fn bitcast[ + new_type: AnyRegType + ](self) -> LegacyPointer[new_type, address_space]: + """Bitcasts a LegacyPointer to a different type. Parameters: new_type: The target type. Returns: - A new Pointer object with the specified type and the same address, - as the original Pointer. + A new LegacyPointer object with the specified type and the same address, + as the original LegacyPointer. """ @parameter if _mlirtype_is_eq[type, new_type](): - return rebind[Pointer[new_type, address_space]](self) + return rebind[LegacyPointer[new_type, address_space]](self) return __mlir_op.`pop.pointer.bitcast`[ - _type = Pointer[new_type, address_space].pointer_type, + _type = LegacyPointer[new_type, address_space].pointer_type, ](self.address) @always_inline("nodebug") fn address_space_cast[ new_address_space: AddressSpace - ](self) -> Pointer[type, new_address_space]: - """Casts a Pointer to a different address space. + ](self) -> LegacyPointer[type, new_address_space]: + """Casts a LegacyPointer to a different address space. Parameters: new_address_space: The address space. Returns: - A new Pointer object with the specified type and the same address, - as the original Pointer but located in a different address space. + A new LegacyPointer object with the specified type and the same address, + as the original LegacyPointer but located in a different address space. """ @parameter if address_space == new_address_space: - return rebind[Pointer[type, new_address_space]](self) + return rebind[LegacyPointer[type, new_address_space]](self) return __mlir_op.`pop.pointer.addrspacecast`[ - _type = Pointer[type, new_address_space].pointer_type, + _type = LegacyPointer[type, new_address_space].pointer_type, ](self.address) # ===------------------------------------------------------------------=== # @@ -640,7 +644,7 @@ struct Pointer[ return int(self) < int(rhs) # ===------------------------------------------------------------------=== # - # Pointer Arithmetic + # LegacyPointer Arithmetic # ===------------------------------------------------------------------=== # @always_inline("nodebug") @@ -654,7 +658,7 @@ struct Pointer[ idx: The offset. Returns: - The new Pointer shifted by the offset. + The new LegacyPointer shifted by the offset. """ # Returns a new pointer shifted by the specified offset. return __mlir_op.`pop.offset`(self.address, int(idx).value) @@ -670,7 +674,7 @@ struct Pointer[ rhs: The offset. Returns: - The new Pointer shifted by the offset. + The new LegacyPointer shifted by the offset. """ return self.offset(rhs) @@ -685,7 +689,7 @@ struct Pointer[ rhs: The offset. Returns: - The new Pointer shifted back by the offset. + The new LegacyPointer shifted back by the offset. """ return self.offset(-int(rhs)) From ca9eccd8f3e97bebe403f6beed8e64fe6295bc2d Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 9 Apr 2024 14:04:55 -0700 Subject: [PATCH 0125/2019] [stdlib] Renaming unsafepointer.mojo to unsafe_pointer.mojo (#37224) Follow up change due to feedback in [Internal link] Supporting [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 881df5c54fa307b8365e43aeab7fd73b46a5aee3 --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/string.mojo | 2 +- stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/__init__.mojo | 2 +- stdlib/src/memory/_arc.mojo | 2 +- stdlib/src/memory/{unsafepointer.mojo => unsafe_pointer.mojo} | 2 +- stdlib/src/os/os.mojo | 2 +- stdlib/src/utils/variant.mojo | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename stdlib/src/memory/{unsafepointer.mojo => unsafe_pointer.mojo} (99%) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 2b1c9a776a..432190249d 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -16,7 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ from memory.reference import Reference, _LITRef -from memory.unsafepointer import * +from memory.unsafe_pointer import * # ===----------------------------------------------------------------------===# # ListLiteral diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index c6cc3b3a2f..8671d1ffd4 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -20,7 +20,7 @@ from collections.dict import KeyElement from sys import llvm_intrinsic from sys.info import bitwidthof -from memory.unsafepointer import UnsafePointer +from memory.unsafe_pointer import UnsafePointer from memory.memory import memcmp, memcpy from memory.unsafe import DTypePointer, Pointer diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 5227c05439..9f59e5ed1d 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -31,7 +31,7 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ -from memory.unsafepointer import UnsafePointer +from memory.unsafe_pointer import UnsafePointer from .optional import Optional diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index d485890268..f68b49c542 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,7 +20,7 @@ from collections import List """ -from memory.unsafepointer import * +from memory.unsafe_pointer import * from memory.unsafe import Reference # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index cefb4418d8..e23c3e95b8 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -20,7 +20,7 @@ from .memory import ( stack_allocation, ) -from .unsafepointer import ( +from .unsafe_pointer import ( AnyPointer, UnsafePointer, ) diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index e8a0de0996..003cdd8e72 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -23,7 +23,7 @@ print(3 == p.get()) ``` """ -from memory.unsafepointer import UnsafePointer +from memory.unsafe_pointer import UnsafePointer from memory.memory import stack_allocation diff --git a/stdlib/src/memory/unsafepointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo similarity index 99% rename from stdlib/src/memory/unsafepointer.mojo rename to stdlib/src/memory/unsafe_pointer.mojo index 2399517943..a7fe39a7ca 100644 --- a/stdlib/src/memory/unsafepointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory.unsafepointer import UnsafePointer +from memory.unsafe_pointer import UnsafePointer ``` """ diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index b81d35fbbf..943a8ae5bf 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -23,7 +23,7 @@ from collections import List from sys.info import os_is_linux, os_is_windows, triple_is_nvidia_cuda from memory.unsafe import DTypePointer, Pointer -from memory.unsafepointer import move_from_pointee +from memory.unsafe_pointer import move_from_pointee from utils import StringRef diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index a942f3dff6..56b8c3166e 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -42,7 +42,7 @@ from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.reference import _LITRef -from memory.unsafepointer import * +from memory.unsafe_pointer import * from utils.loop import unroll from utils.static_tuple import StaticTuple From 912c2696210a68762cd73feec29dfffee785d14f Mon Sep 17 00:00:00 2001 From: Stef Lindall Date: Tue, 9 Apr 2024 14:58:19 -0700 Subject: [PATCH 0126/2019] [mojo-stdlib] Make `List` `Boolable` (#37236) Stacked PRs: * #37237 * __->__#37236 --- --- --- ### [mojo-stdlib] Make `List` `Boolable` - Adds `Boolable` trait to `List` - Python-like behavior (empty list is False-y, everything else is True-y) MODULAR_ORIG_COMMIT_REV_ID: 33c5f14def8fd45b15c16008e9593afa5f01c870 --- stdlib/src/collections/list.mojo | 10 +++++++++- stdlib/test/collections/test_list.mojo | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index f68b49c542..b2f4adcb25 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -69,7 +69,7 @@ struct _ListIter[ return len(self.src[]) - self.index -struct List[T: CollectionElement](CollectionElement, Sized): +struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """The `List` type is a dynamically-allocated list. It supports pushing and popping from the back resizing the underlying @@ -160,6 +160,14 @@ struct List[T: CollectionElement](CollectionElement, Sized): """ return self.size + fn __bool__(self) -> Bool: + """Checks whether the list has any elements or not. + + Returns: + False for an empty list, True otherwise. + """ + return len(self) > 0 + @always_inline fn _realloc(inout self, new_capacity: Int): var new_data = UnsafePointer[T].alloc(new_capacity) diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index fc5364ce31..3511d49719 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -15,7 +15,7 @@ from collections import List from test_utils import CopyCounter, MoveCounter -from testing import assert_equal +from testing import assert_equal, assert_false, assert_true def test_mojo_issue_698(): @@ -461,6 +461,11 @@ def test_list_span(): assert_equal(len(es), 3) +def test_list_boolable(): + assert_true(List[Int](1)) + assert_false(List[Int]()) + + def main(): test_mojo_issue_698() test_list() @@ -477,3 +482,4 @@ def main(): test_list_iter() test_list_iter_mutable() test_list_span() + test_list_boolable() From c01ef6628a7a4ce491b2f236ae855f44a254a662 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 9 Apr 2024 17:29:17 -0700 Subject: [PATCH 0127/2019] [mojo-stdlib] Move pointer bitcast operations onto pointers. (#37222) This gives each of our pointer types a `Pointer(address: someInt)` constructor for converting from an integer, and uses the existing `bitcast` method instead of replicating it as a global function. MODULAR_ORIG_COMMIT_REV_ID: aa4fbc2353e06a14b20587ad5a3c4f933fc14299 --- stdlib/src/builtin/file.mojo | 3 +- stdlib/src/memory/unsafe.mojo | 82 ++++++++------------------- stdlib/src/memory/unsafe_pointer.mojo | 35 +++++++----- 3 files changed, 45 insertions(+), 75 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index b1f3e3197d..b7b577146e 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -188,8 +188,7 @@ struct FileHandle: raise (err_msg^).consume_as_error() var list = List[Int8](capacity=int(size_copy)) - - var list_ptr = Pointer[Int8].__from_index(int(list.data)) + var list_ptr = Pointer[Int8](address=int(list.data)) # Initialize the List elements and set the initialized size memcpy(list_ptr, buf, int(size_copy)) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 3a1ad3722b..9c1312c935 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -97,29 +97,6 @@ fn bitcast[ return bitcast[Scalar[type], address_space](val) -@always_inline("nodebug") -fn bitcast[ - new_type: AnyType, src_type: AnyType, address_space: AddressSpace -](ptr: UnsafePointer[src_type, address_space]) -> UnsafePointer[ - new_type, address_space -]: - """Bitcasts an UnsafePointer to a different type. - - Parameters: - new_type: The target type. - src_type: The source type. - address_space: The shared address space. - - Args: - ptr: The source pointer. - - Returns: - A new Pointer with the specified type and the same address, as the - original Pointer. - """ - return ptr.bitcast_element[new_type]() - - @always_inline("nodebug") fn bitcast[ new_type: AnyRegType, src_type: AnyRegType, address_space: AddressSpace @@ -141,29 +118,6 @@ fn bitcast[ return ptr.bitcast[new_type]() -@always_inline("nodebug") -fn bitcast[ - new_type: DType, src_type: DType, address_space: AddressSpace -](ptr: DTypePointer[src_type, address_space]) -> DTypePointer[ - new_type, address_space -]: - """Bitcasts a DTypePointer to a different type. - - Parameters: - new_type: The target type. - src_type: The source type. - address_space: The address space the pointer is in. - - Args: - ptr: The source pointer. - - Returns: - A new DTypePointer with the specified type and the same address, as - the original DTypePointer. - """ - return ptr.bitcast[new_type]() - - @always_inline("nodebug") fn bitcast[ new_type: DType, new_width: Int, src_type: DType, src_width: Int @@ -313,6 +267,7 @@ struct LegacyPointer[ """ return Self.get_null() + # FIXME: Why do we have an identity constructor? @always_inline("nodebug") fn __init__(address: Self) -> Self: """Constructs a LegacyPointer from the address. @@ -352,6 +307,20 @@ struct LegacyPointer[ ](value.cast[DType.index]().value) return Self {address: address} + @always_inline("nodebug") + fn __init__(*, address: Int) -> Self: + """Constructs a Pointer from an address in an integer. + + Args: + address: The input address. + + Returns: + Constructed Pointer object. + """ + return __mlir_op.`pop.index_to_pointer`[_type = Self.pointer_type]( + Scalar[DType.index](address).value + ) + @staticmethod @always_inline("nodebug") fn get_null() -> Self: @@ -520,13 +489,6 @@ struct LegacyPointer[ _type = __mlir_type.`!pop.scalar` ](self.address) - @staticmethod - @always_inline - fn __from_index(value: Int) -> Self: - return __mlir_op.`pop.index_to_pointer`[_type = Self.pointer_type]( - Scalar[DType.index](value).value - ) - # ===------------------------------------------------------------------=== # # Allocate/Free # ===------------------------------------------------------------------=== # @@ -788,6 +750,15 @@ struct DTypePointer[ ](value.cast[DType.index]().value) self.address = address + @always_inline + fn __init__(inout self, *, address: Int): + """Constructs a `DTypePointer` from an integer address. + + Args: + address: The input address. + """ + self.address = Self.pointer_type(address=address) + @staticmethod @always_inline("nodebug") fn get_null() -> Self: @@ -1144,11 +1115,6 @@ struct DTypePointer[ """ return int(self.address) - @staticmethod - @always_inline - fn __from_index(value: Int) -> Self: - return Self.pointer_type.__from_index(value) - @always_inline fn is_aligned[alignment: Int](self) -> Bool: """Checks if the pointer is aligned. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index a7fe39a7ca..f4d1de711d 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -82,6 +82,22 @@ struct UnsafePointer[ """ return Self {value: __mlir_op.`lit.ref.to_pointer`(value.value)} + @always_inline + fn __init__(*, address: Int) -> Self: + """Create an unsafe AnyPointer from an address in an integer. + + Args: + address: The address to construct the pointer with. + + Returns: + The pointer. + """ + return Self { + value: __mlir_op.`pop.index_to_pointer`[_type = Self.pointer_type]( + Scalar[DType.index](address).value + ) + } + @staticmethod @always_inline fn alloc(count: Int) -> Self: @@ -93,8 +109,8 @@ struct UnsafePointer[ Returns: The pointer to the newly allocated array. """ - return Self.__from_index( - int( + return Self( + address=int( _malloc[Int8, address_space=address_space]( sizeof[T]() * count, alignment=alignof[T]() ) @@ -119,9 +135,7 @@ struct UnsafePointer[ @always_inline fn free(self): """Free the memory referenced by the pointer.""" - Pointer[Int8, address_space=address_space].__from_index( - int(self) - ).free() + Pointer[Int8, address_space=address_space](address=int(self)).free() @always_inline fn bitcast_element[ @@ -170,15 +184,6 @@ struct UnsafePointer[ _type = __mlir_type.`!pop.scalar` ](self.value) - @staticmethod - @always_inline - fn __from_index(value: Int) -> Self: - return Self { - value: __mlir_op.`pop.index_to_pointer`[_type = Self.pointer_type]( - Scalar[DType.index](value).value - ) - } - fn __str__(self) -> String: return hex(self) @@ -201,7 +206,7 @@ struct UnsafePointer[ Returns: An offset pointer. """ - return Self.__from_index(int(self) + offset * sizeof[T]()) + return Self(address=int(self) + offset * sizeof[T]()) @always_inline fn __sub__(self, offset: Int) -> Self: From ec7e0c8cce8730e273e7de9332a8b969c6c67622 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Tue, 9 Apr 2024 21:50:00 -0600 Subject: [PATCH 0128/2019] [stdlib] Replace most uses of `__get_lvalue_as_address` with `Reference` (#2035) (#36428) Replace most uses of `__get_lvalue_as_address` with `Reference`. The two remaining uses at `memory/anypointer.mojo` and `memory/unsafe.mojo` can't switch over to using `Reference` since register passable things don't carry a lifetime value (and thus cannot be used with `Reference`). They are current rejected by the compiler with the error: ```mojo error: cannot form a reference to an argument that might instantiate to @register_passable type ``` MODULAR_ORIG_COMMIT_REV_ID: ff7e21a5cbee8c3359fa63fa6f8049690f051653 --- stdlib/src/builtin/object.mojo | 14 +++++++++----- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/sys/ffi.mojo | 9 ++++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 31ea14a0d7..ce1c4399f4 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -231,7 +231,7 @@ struct _Function: fn __init__[FnT: AnyRegType](value: FnT) -> Self: # FIXME: No "pointer bitcast" for signature function pointers. var f = Pointer[Int16]() - Pointer(__get_lvalue_as_address(f)).bitcast[FnT]().store(value) + Reference(f).get_unsafe_pointer().bitcast[FnT]().store(value) return Self {value: f} alias fn0 = fn () raises -> object @@ -246,7 +246,8 @@ struct _Function: @always_inline fn invoke(owned self) raises -> object: return ( - Pointer(__get_lvalue_as_address(self.value)) + Reference(self.value) + .get_unsafe_pointer() .bitcast[Self.fn0]() .load()() ) @@ -254,7 +255,8 @@ struct _Function: @always_inline fn invoke(owned self, arg0: object) raises -> object: return ( - Pointer(__get_lvalue_as_address(self.value)) + Reference(self.value) + .get_unsafe_pointer() .bitcast[Self.fn1]() .load()(arg0) ) @@ -262,7 +264,8 @@ struct _Function: @always_inline fn invoke(owned self, arg0: object, arg1: object) raises -> object: return ( - Pointer(__get_lvalue_as_address(self.value)) + Reference(self.value) + .get_unsafe_pointer() .bitcast[Self.fn2]() .load()(arg0, arg1) ) @@ -272,7 +275,8 @@ struct _Function: owned self, arg0: object, arg1: object, arg2: object ) raises -> object: return ( - Pointer(__get_lvalue_as_address(self.value)) + Reference(self.value) + .get_unsafe_pointer() .bitcast[Self.fn3]() .load()(arg0, arg1, arg2) ) diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 914306eb15..c97ec435b4 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -243,5 +243,5 @@ struct InlinedFixedVector[ An iterator to the start of the vector. """ return Self._iterator( - 0, self.current_size, __get_lvalue_as_address(self) + 0, self.current_size, Reference(self).get_unsafe_pointer() ) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 86d9373d7e..9a76b96091 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -126,7 +126,8 @@ struct DLHandle(CollectionElement): "dlsym", DTypePointer[DType.int8] ](self.handle.address, name) return ( - Pointer(__get_lvalue_as_address(opaque_function_ptr)) + Reference(opaque_function_ptr) + .get_unsafe_pointer() .bitcast[result_type]() .load() ) @@ -198,7 +199,8 @@ fn _get_dylib_function[ var func_ptr = _get_global_or_null[func_cache_name]() if func_ptr: return ( - Pointer(__get_lvalue_as_address(func_ptr)) + Reference(func_ptr) + .get_unsafe_pointer() .bitcast[result_type]() .load() ) @@ -207,7 +209,8 @@ fn _get_dylib_function[ var new_func = dylib._get_function[func_name, result_type]() external_call["KGEN_CompilerRT_InsertGlobal", NoneType]( StringRef(func_cache_name), - Pointer(__get_lvalue_as_address(new_func)) + Reference(new_func) + .get_unsafe_pointer() .bitcast[Pointer[NoneType]]() .load(), ) From 541bedfd1e4f12f0c46c6e00854498a5b0cc32fb Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 9 Apr 2024 22:07:01 -0700 Subject: [PATCH 0129/2019] [Stdlib] Assign result in the int conversion test, NFC (#37276) Otherwise one gets ``` /home/adakkak/code/modular/******/test/stdlib/builtin/test_string_literal.mojo:97:12: warning: 'Int' value is unused int("hi") ~~~^~~~~~ _ = ``` MODULAR_ORIG_COMMIT_REV_ID: c00dc45cb979e444d46ff69152a4dd0aeb5ab5b7 --- stdlib/test/builtin/test_string_literal.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 4473dadcc6..6937c7f819 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -91,7 +91,7 @@ def test_intable(): assert_equal(int("123"), 123) with assert_raises(): - int("hi") + _ = int("hi") def main(): From 2c4231e218fbf5f1f50192c5829173e49e35e6c1 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Wed, 10 Apr 2024 06:41:00 -0600 Subject: [PATCH 0130/2019] [stdlib] Replace `__init__ -> Self` use in `utils` module (#36715) The `__init__ -> Self` is getting deprecated. Switch over to "normal" `__init__` constructors that accept `Self` as an inout argument instead within the `util` module. Co-authored-by: Mert MODULAR_ORIG_COMMIT_REV_ID: 074e4a68dca04fca39c7cf5cc036db3918c4a568 --- stdlib/src/utils/index.mojo | 65 +++++++++--------------------- stdlib/src/utils/static_tuple.mojo | 24 ++++------- 2 files changed, 26 insertions(+), 63 deletions(-) diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 2851bb1275..51fbd5af0f 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -191,36 +191,26 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): """The underlying storage of the tuple value.""" @always_inline - fn __init__() -> Self: - """Constructs a static int tuple of the given size. - - Returns: - The constructed tuple. - """ - return 0 + fn __init__(inout self): + """Constructs a static int tuple of the given size.""" + self = 0 @always_inline - fn __init__(value: __mlir_type.index) -> Self: + fn __init__(inout self, value: __mlir_type.index): """Constructs a sized 1 static int tuple of given the element value. Args: value: The initial value. - - Returns: - The constructed tuple. """ constrained[size == 1]() - return Int(value) + self = Int(value) @always_inline - fn __init__(elems: Tuple[Int, Int]) -> Self: + fn __init__(inout self, elems: Tuple[Int, Int]): """Constructs a static int tuple given a tuple of integers. Args: elems: The tuple to copy from. - - Returns: - The constructed tuple. """ var num_elements = len(elems) @@ -238,17 +228,14 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): unroll[fill, 2]() - return tup + self = tup @always_inline - fn __init__(elems: Tuple[Int, Int, Int]) -> Self: + fn __init__(inout self, elems: Tuple[Int, Int, Int]): """Constructs a static int tuple given a tuple of integers. Args: elems: The tuple to copy from. - - Returns: - The constructed tuple. """ var num_elements = len(elems) @@ -266,17 +253,14 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): unroll[fill, 3]() - return tup + self = tup @always_inline - fn __init__(elems: Tuple[Int, Int, Int, Int]) -> Self: + fn __init__(inout self, elems: Tuple[Int, Int, Int, Int]): """Constructs a static int tuple given a tuple of integers. Args: elems: The tuple to copy from. - - Returns: - The constructed tuple. """ var num_elements = len(elems) @@ -294,17 +278,14 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): unroll[fill, 4]() - return tup + self = tup @always_inline - fn __init__(*elems: Int) -> Self: + fn __init__(inout self, *elems: Int): """Constructs a static int tuple given a set of arguments. Args: elems: The elements to construct the tuple. - - Returns: - The constructed tuple. """ var num_elements = len(elems) @@ -320,37 +301,29 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): for idx in range(size): tup[idx] = elems[idx] - return tup + self = tup @always_inline - fn __init__(elem: Int) -> Self: + fn __init__(inout self, elem: Int): """Constructs a static int tuple given a set of arguments. Args: elem: The elem to splat into the tuple. - - Returns: - The constructed tuple. """ - return StaticIntTuple[size] { - data: __mlir_op.`pop.array.repeat`[ - _type = __mlir_type[`!pop.array<`, size.value, `, `, Int, `>`] - ](elem) - } + self.data = __mlir_op.`pop.array.repeat`[ + _type = __mlir_type[`!pop.array<`, size.value, `, `, Int, `>`] + ](elem) @always_inline - fn __init__(values: VariadicList[Int]) -> Self: + fn __init__(inout self, values: VariadicList[Int]): """Creates a tuple constant using the specified values. Args: values: The list of values. - - Returns: - A tuple with the values filled in. """ constrained[size > 0]() - return Self {data: values} + self.data = values @always_inline("nodebug") fn __len__(self) -> Int: diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 54a68e8fb8..5f05d5b12a 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -126,40 +126,30 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): """The underlying storage for the static tuple.""" @always_inline - fn __init__() -> Self: - """Constructs an empty (undefined) tuple. - - Returns: - The tuple. - """ + fn __init__(inout self): + """Constructs an empty (undefined) tuple.""" _static_tuple_construction_checks[size]() - return Self {array: __mlir_op.`kgen.undef`[_type = Self.type]()} + self.array = __mlir_op.`kgen.undef`[_type = Self.type]() @always_inline - fn __init__(*elems: Self.element_type) -> Self: + fn __init__(inout self, *elems: Self.element_type): """Constructs a static tuple given a set of arguments. Args: elems: The element types. - - Returns: - The tuple. """ _static_tuple_construction_checks[size]() - return Self {array: _create_array[size](elems)} + self.array = _create_array[size](elems) @always_inline - fn __init__(values: VariadicList[Self.element_type]) -> Self: + fn __init__(inout self, values: VariadicList[Self.element_type]): """Creates a tuple constant using the specified values. Args: values: The list of values. - - Returns: - A tuple with the values filled in. """ _static_tuple_construction_checks[size]() - return Self {array: _create_array[size, Self.element_type](values)} + self.array = _create_array[size, Self.element_type](values) @always_inline("nodebug") fn __len__(self) -> Int: From 3a127a89fedcafee280afaafe4f07b12e18c6fac Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Wed, 10 Apr 2024 09:39:24 -0700 Subject: [PATCH 0131/2019] [stdlib] Removing `alias AnyPointer = UnsafePointer`. (#37269) Removing the `alias AnyPointer = UnsafePointer` and some final clean up. This commit removes the type AnyPointer from the code. Supports [Internal link] MODULAR_ORIG_COMMIT_REV_ID: ed1f298979d48aea1eb88b7100b5eeac4077e22e --- docs/changelog.md | 2 ++ stdlib/src/memory/__init__.mojo | 1 - stdlib/src/memory/unsafe_pointer.mojo | 10 +++++----- stdlib/test/memory/test_unsafepointer.mojo | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6dc5014d46..4b86a7bd44 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -113,6 +113,8 @@ what we publish. `AnyPointer/initialize_pointee`. 5) The `offset` method has been removed, it was unsafe and belongs on `AnyPointer`. +- `AnyPointer` was renamed to `UnsafePointer`. This is part of our continuing + effort to unify our pointer types in the Standard Library. - The `mojo package` command no longer supports the `-D` flag. All compilation environment flags should be provided at the point of package use diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index e23c3e95b8..2222a998b6 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -21,7 +21,6 @@ from .memory import ( ) from .unsafe_pointer import ( - AnyPointer, UnsafePointer, ) diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f4d1de711d..c2ca23cafe 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -25,15 +25,15 @@ from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc from memory.reference import _LITRef -alias AnyPointer = UnsafePointer - +# ===----------------------------------------------------------------------=== # +# UnsafePointer +# ===----------------------------------------------------------------------=== # @register_passable("trivial") struct UnsafePointer[ T: AnyType, address_space: AddressSpace = AddressSpace.GENERIC ](Boolable, CollectionElement, Stringable, Intable, EqualityComparable): - """This is a pointer type that can point to any generic value that is - movable. + """This is a pointer type that can point to any generic value that is movable. Parameters: T: The type the pointer points to. @@ -84,7 +84,7 @@ struct UnsafePointer[ @always_inline fn __init__(*, address: Int) -> Self: - """Create an unsafe AnyPointer from an address in an integer. + """Create an unsafe UnsafePointer from an address in an integer. Args: address: The address to construct the pointer with. diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 77c4a19f1e..709a1a0d7c 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo -debug-level full %s | FileCheck %s --dump-input=always -from memory.anypointer import * +from memory.unsafe_pointer import * from test_utils import MoveCounter from testing import assert_equal, assert_not_equal, assert_true @@ -90,7 +90,7 @@ def test_address_of(): def test_bitcast(): var local = 1 - var ptr = AnyPointer[Int].address_of(local) + var ptr = UnsafePointer[Int].address_of(local) var aliased_ptr = ptr.bitcast_element[SIMD[DType.uint8, 4]]() assert_equal(int(ptr), int(ptr.bitcast_element[Int]())) From 9a27002f37c0b344a681b181bbdce536e8d4c6c6 Mon Sep 17 00:00:00 2001 From: Arthur Evans Date: Wed, 10 Apr 2024 11:25:39 -0700 Subject: [PATCH 0132/2019] [Docs] Update changelog for 24.2.1. (#37320) Just note that there are no changes. MODULAR_ORIG_COMMIT_REV_ID: af993974af8aea07872d71ff2ad32f0d7c725a82 --- docs/changelog-released.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index a794444e10..1170f0153e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -30,6 +30,10 @@ To update Mojo, first [update `modular`](/cli/#description), and then run this: modular update mojo ``` +## v24.2.1 (2024-04-11) + +This release doesn't include any changes to Mojo. + ## v24.2 (2024-03-28) ### 🔥 Legendary From 1c4bda632b7c487bc7f4f708eedf9bed7bf014f4 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 10 Apr 2024 20:49:20 -0700 Subject: [PATCH 0133/2019] [Mojo] Remove `--debug-level full` from the ****** test files (#37375) These are now going to built on a daily cadence as part of the daily ****** debug builds. This will ensure that we have quick cycle time when developing and testing ****** (which is orthogonal to testing whether debug symbols are being generated). MODULAR_ORIG_COMMIT_REV_ID: 85890b55909b9d73e307d7f60f06e3283a451a04 --- examples/deviceinfo.mojo | 2 +- "examples/hello.\360\237\224\245" | 2 +- examples/mandelbrot.mojo | 2 +- examples/nbody.mojo | 2 +- examples/reduce.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 5 +++-- stdlib/test/base64/test_base64.mojo | 2 +- stdlib/test/builtin/test_bfloat16.mojo | 2 +- stdlib/test/builtin/test_debug_assert-error.mojo | 2 +- stdlib/test/builtin/test_dtype.mojo | 2 +- stdlib/test/builtin/test_error.mojo | 2 +- stdlib/test/builtin/test_hash.mojo | 4 ++-- stdlib/test/builtin/test_hex.mojo | 2 +- stdlib/test/builtin/test_int.mojo | 2 +- stdlib/test/builtin/test_int_literal.mojo | 2 +- stdlib/test/builtin/test_issue_1004.mojo | 2 +- stdlib/test/builtin/test_issue_1505.mojo | 2 +- stdlib/test/builtin/test_list.mojo | 2 +- stdlib/test/builtin/test_object.mojo | 2 +- stdlib/test/builtin/test_print.mojo | 2 +- stdlib/test/builtin/test_print_long_string.mojo | 2 +- stdlib/test/builtin/test_range.mojo | 2 +- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/builtin/test_slice.mojo | 2 +- stdlib/test/builtin/test_str.mojo | 2 +- stdlib/test/builtin/test_string.mojo | 7 +++++-- stdlib/test/builtin/test_string_literal.mojo | 2 +- stdlib/test/builtin/test_stringref.mojo | 2 +- stdlib/test/collections/test_dict.mojo | 2 +- stdlib/test/collections/test_list.mojo | 2 +- stdlib/test/collections/test_optional.mojo | 2 +- stdlib/test/collections/test_set.mojo | 2 +- stdlib/test/collections/test_vector.mojo | 2 +- stdlib/test/memory/test_bitcast.mojo | 2 +- stdlib/test/memory/test_memory.mojo | 2 +- stdlib/test/memory/test_unsafepointer.mojo | 2 +- stdlib/test/os/path/test_exists.mojo | 2 +- stdlib/test/os/path/test_isdir.mojo | 2 +- stdlib/test/os/path/test_isfile.mojo | 2 +- stdlib/test/os/path/test_islink.mojo | 2 +- stdlib/test/os/test_atomic.mojo | 2 +- stdlib/test/os/test_getenv_setenv.mojo | 2 +- stdlib/test/os/test_listdir.mojo | 2 +- stdlib/test/os/test_no_trap.mojo | 2 +- stdlib/test/os/test_stat.mojo | 2 +- stdlib/test/pathlib/test_pathlib.mojo | 2 +- stdlib/test/python/test_python_info.mojo | 2 +- stdlib/test/python/test_python_object.mojo | 2 +- stdlib/test/python/test_python_object_len_raises.mojo | 2 +- stdlib/test/python/test_python_to_mojo.mojo | 2 +- stdlib/test/random/test_random.mojo | 2 +- stdlib/test/sys/test_build_info_debug.mojo | 2 +- stdlib/test/sys/test_exit_0.mojo | 2 +- stdlib/test/sys/test_exit_1.mojo | 2 +- stdlib/test/sys/test_has_intel_amx.mojo | 2 +- stdlib/test/sys/test_intrinsics.mojo | 2 +- stdlib/test/sys/test_linux_target.mojo | 2 +- stdlib/test/sys/test_macos_target.mojo | 2 +- stdlib/test/sys/test_targetinfo.mojo | 2 +- stdlib/test/testing/test_assert_raises_basic.mojo | 2 +- stdlib/test/testing/test_assert_raises_no_error.mojo | 2 +- stdlib/test/testing/test_assert_raises_no_match.mojo | 2 +- stdlib/test/time/test_time.mojo | 2 +- stdlib/test/utils/issue_13632.mojo | 2 +- stdlib/test/utils/test_numerics.mojo | 2 +- stdlib/test/utils/test_tuple.mojo | 2 +- stdlib/test/utils/test_unroll.mojo | 2 +- stdlib/test/utils/test_variant.mojo | 2 +- 68 files changed, 75 insertions(+), 71 deletions(-) diff --git a/examples/deviceinfo.mojo b/examples/deviceinfo.mojo index f2c79fa3e1..e81486c29e 100644 --- a/examples/deviceinfo.mojo +++ b/examples/deviceinfo.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # This sample prints the current host system information using APIs from the # sys module. diff --git "a/examples/hello.\360\237\224\245" "b/examples/hello.\360\237\224\245" index cf483be26b..e40adce971 100644 --- "a/examples/hello.\360\237\224\245" +++ "b/examples/hello.\360\237\224\245" @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # This sample demonstrates some basic Mojo # Range and print functions available as builtins diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 101ce29f26..5b70f70ad7 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from math import iota from sys.info import num_logical_cores diff --git a/examples/nbody.mojo b/examples/nbody.mojo index 028529ad4a..91cfc2d0ac 100644 --- a/examples/nbody.mojo +++ b/examples/nbody.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s # This sample implements the nbody benchmarking in # https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/nbody.html diff --git a/examples/reduce.mojo b/examples/reduce.mojo index 74dab2ede0..91a54580e7 100644 --- a/examples/reduce.mojo +++ b/examples/reduce.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # This sample implements a simple reduction operation on a # large array of values to produce a single result. diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 5f05d5b12a..c82b9a1b8b 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -234,6 +234,7 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): ) Pointer(ptr).store(val) + @always_inline("nodebug") fn as_ptr(inout self) -> Pointer[Self.element_type]: """Get a mutable pointer to the elements contained by this tuple. @@ -241,6 +242,6 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): A pointer to the elements contained by this tuple. """ - var base_ptr = Pointer[Self.type].address_of(self.array).address - var ptr = __mlir_op.`pop.array.gep`(base_ptr, Int(0).value) + var base_ptr = Pointer.address_of(self.array) + var ptr = __mlir_op.`pop.array.gep`(base_ptr.address, Int(0).value) return Pointer(ptr) diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 934a87e57c..39db2d1128 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from base64 import b64encode diff --git a/stdlib/test/builtin/test_bfloat16.mojo b/stdlib/test/builtin/test_bfloat16.mojo index 3b341e4ba7..7b5f719ae2 100644 --- a/stdlib/test/builtin/test_bfloat16.mojo +++ b/stdlib/test/builtin/test_bfloat16.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from random import randn_float64 from sys.info import has_neon diff --git a/stdlib/test/builtin/test_debug_assert-error.mojo b/stdlib/test/builtin/test_debug_assert-error.mojo index 4e1cd6bd15..4095ca4229 100644 --- a/stdlib/test/builtin/test_debug_assert-error.mojo +++ b/stdlib/test/builtin/test_debug_assert-error.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not --crash %mojo -debug-level full -D KERNELS_BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL +# RUN: not --crash %mojo -D KERNELS_BUILD_TYPE=debug %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL # CHECK-FAIL-LABEL: test_fail diff --git a/stdlib/test/builtin/test_dtype.mojo b/stdlib/test/builtin/test_dtype.mojo index 5e996d6677..bd12bbecbb 100644 --- a/stdlib/test/builtin/test_dtype.mojo +++ b/stdlib/test/builtin/test_dtype.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from collections import Set diff --git a/stdlib/test/builtin/test_error.mojo b/stdlib/test/builtin/test_error.mojo index 7f28263121..7572cfd636 100644 --- a/stdlib/test/builtin/test_error.mojo +++ b/stdlib/test/builtin/test_error.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s def raise_an_error(): diff --git a/stdlib/test/builtin/test_hash.mojo b/stdlib/test/builtin/test_hash.mojo index 1bd5aac2d9..de8ca14cfa 100644 --- a/stdlib/test/builtin/test_hash.mojo +++ b/stdlib/test/builtin/test_hash.mojo @@ -10,8 +10,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s -# RUN: %mojo -debug-level full -O0 %s +# RUN: %mojo %s +# RUN: %mojo -O0 %s # Issue #31111 -- run this test with -O0 also. diff --git a/stdlib/test/builtin/test_hex.mojo b/stdlib/test/builtin/test_hex.mojo index c500bb846d..36ed72f5b6 100644 --- a/stdlib/test/builtin/test_hex.mojo +++ b/stdlib/test/builtin/test_hex.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from builtin.hex import _format_int from testing import assert_equal diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index c983817c19..5e0b44ace6 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from testing import assert_equal diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index f2465f3738..91b715fbc4 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # CHECK-LABEL: test_int diff --git a/stdlib/test/builtin/test_issue_1004.mojo b/stdlib/test/builtin/test_issue_1004.mojo index 996ab80a53..759e1d3977 100644 --- a/stdlib/test/builtin/test_issue_1004.mojo +++ b/stdlib/test/builtin/test_issue_1004.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # Test for https://github.com/modularml/mojo/issues/1004 from testing import assert_equal diff --git a/stdlib/test/builtin/test_issue_1505.mojo b/stdlib/test/builtin/test_issue_1505.mojo index 8c1ef6eb20..70b6512e22 100644 --- a/stdlib/test/builtin/test_issue_1505.mojo +++ b/stdlib/test/builtin/test_issue_1505.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: disabled -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s # Test for https://github.com/modularml/mojo/issues/1505 from random import random_ui64 diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 4c0c66aa8a..67a8183d1f 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # CHECK-LABEL: test_list diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 34052cfee6..a2fac471c7 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from random import random_float64 diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index c9507d767b..12e74c863c 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from memory.unsafe import DTypePointer diff --git a/stdlib/test/builtin/test_print_long_string.mojo b/stdlib/test/builtin/test_print_long_string.mojo index 2cda3110c3..34ec28f52b 100644 --- a/stdlib/test/builtin/test_print_long_string.mojo +++ b/stdlib/test/builtin/test_print_long_string.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s > %t +# RUN: %mojo %s > %t # RUN: wc -c %t | FileCheck %s diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index ad99471669..723f2ade1f 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s # CHECK-LABEL: test_range_len diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index c94d06b7a6..8f1c2b538a 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.info import has_neon, simdwidthof diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index 0a405c0be5..faada7c197 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from testing import assert_equal, assert_false diff --git a/stdlib/test/builtin/test_str.mojo b/stdlib/test/builtin/test_str.mojo index 5694d97d22..434915d8b3 100644 --- a/stdlib/test/builtin/test_str.mojo +++ b/stdlib/test/builtin/test_str.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from testing import assert_equal diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 2da62facd5..9dbdca43f8 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -10,7 +10,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# TODO(37393): Reenabl once we debug why we are depending on some debug behavior +# on graviton. +# REQUIRES: Disabled +# RUN: %mojo %s from builtin.string import ( _calc_initial_buffer_size_int32, @@ -29,7 +32,7 @@ from utils import StringRef @value struct AString(Stringable): - fn __str__(borrowed self: Self) -> String: + fn __str__(self: Self) -> String: return "a string" diff --git a/stdlib/test/builtin/test_string_literal.mojo b/stdlib/test/builtin/test_string_literal.mojo index 6937c7f819..43bedb9615 100644 --- a/stdlib/test/builtin/test_string_literal.mojo +++ b/stdlib/test/builtin/test_string_literal.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from testing import ( assert_equal, diff --git a/stdlib/test/builtin/test_stringref.mojo b/stdlib/test/builtin/test_stringref.mojo index bddfb751f4..257b593450 100644 --- a/stdlib/test/builtin/test_stringref.mojo +++ b/stdlib/test/builtin/test_stringref.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from testing import assert_equal, assert_raises diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 47bde31cbe..64901fe0a2 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from collections import Optional from collections.dict import Dict, KeyElement, OwnedKwargsDict diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 3511d49719..c2957a1321 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from collections import List diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 22a6858c34..cea2560fac 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from collections.optional import Optional, OptionalReg diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 4e94a06d7e..80b8ff5661 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from collections.set import Set diff --git a/stdlib/test/collections/test_vector.mojo b/stdlib/test/collections/test_vector.mojo index b242b792f8..9750141bd8 100644 --- a/stdlib/test/collections/test_vector.mojo +++ b/stdlib/test/collections/test_vector.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from collections.vector import InlinedFixedVector diff --git a/stdlib/test/memory/test_bitcast.mojo b/stdlib/test/memory/test_bitcast.mojo index 92f2e5820c..ebec35fdad 100644 --- a/stdlib/test/memory/test_bitcast.mojo +++ b/stdlib/test/memory/test_bitcast.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from memory.unsafe import bitcast diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index a2535eec78..b4451f588c 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from sys.info import sizeof diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 709a1a0d7c..893c913ab0 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s --dump-input=always +# RUN: %mojo %s | FileCheck %s --dump-input=always from memory.unsafe_pointer import * from test_utils import MoveCounter diff --git a/stdlib/test/os/path/test_exists.mojo b/stdlib/test/os/path/test_exists.mojo index 966f022f91..4086aec969 100644 --- a/stdlib/test/os/path/test_exists.mojo +++ b/stdlib/test/os/path/test_exists.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from os.path import exists, lexists diff --git a/stdlib/test/os/path/test_isdir.mojo b/stdlib/test/os/path/test_isdir.mojo index b5a936f0fd..872f891604 100644 --- a/stdlib/test/os/path/test_isdir.mojo +++ b/stdlib/test/os/path/test_isdir.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from os.path import isdir from pathlib import Path, cwd diff --git a/stdlib/test/os/path/test_isfile.mojo b/stdlib/test/os/path/test_isfile.mojo index 012a251b6d..74f0d25454 100644 --- a/stdlib/test/os/path/test_isfile.mojo +++ b/stdlib/test/os/path/test_isfile.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from os.path import isfile from pathlib import Path diff --git a/stdlib/test/os/path/test_islink.mojo b/stdlib/test/os/path/test_islink.mojo index f8d55d5909..f4646c96c2 100644 --- a/stdlib/test/os/path/test_islink.mojo +++ b/stdlib/test/os/path/test_islink.mojo @@ -14,7 +14,7 @@ # REQUIRES: DISABLED # RUN: rm -rf %t && mkdir -p %t # RUN: ln -s %S %t/tmp -# RUN: %mojo -debug-level full -D TEMP_DIR=%t/tmp %s +# RUN: %mojo -D TEMP_DIR=%t/tmp %s from os.path import isdir, islink from pathlib import Path diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 61c7c11ccc..3430b8efe2 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from os.atomic import Atomic from testing import assert_equal diff --git a/stdlib/test/os/test_getenv_setenv.mojo b/stdlib/test/os/test_getenv_setenv.mojo index 719f9a89c7..f24077571e 100644 --- a/stdlib/test/os/test_getenv_setenv.mojo +++ b/stdlib/test/os/test_getenv_setenv.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: linux || darwin -# RUN: TEST_MYVAR=MyValue %mojo -debug-level full %s +# RUN: TEST_MYVAR=MyValue %mojo %s from os import getenv, setenv diff --git a/stdlib/test/os/test_listdir.mojo b/stdlib/test/os/test_listdir.mojo index 945bdf6b3a..8b029590a1 100644 --- a/stdlib/test/os/test_listdir.mojo +++ b/stdlib/test/os/test_listdir.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from os import listdir from pathlib import Path diff --git a/stdlib/test/os/test_no_trap.mojo b/stdlib/test/os/test_no_trap.mojo index 0e9ed7c6f4..e0359b01de 100644 --- a/stdlib/test/os/test_no_trap.mojo +++ b/stdlib/test/os/test_no_trap.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s -an_argument | FileCheck %s +# RUN: %mojo %s -an_argument | FileCheck %s # We pass an_argument here to avoid the compiler from optimizing the code # away. diff --git a/stdlib/test/os/test_stat.mojo b/stdlib/test/os/test_stat.mojo index 1e1a3d5ffb..353df7671f 100644 --- a/stdlib/test/os/test_stat.mojo +++ b/stdlib/test/os/test_stat.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from os import stat from stat import S_ISREG diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index b19d5599f0..69fb5cc039 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: !windows -# RUN: %mojo -debug-level full -D TEMP_FILE=%t %s +# RUN: %mojo -D TEMP_FILE=%t %s from pathlib import cwd, Path, DIR_SEPARATOR from sys.param_env import env_get_string diff --git a/stdlib/test/python/test_python_info.mojo b/stdlib/test/python/test_python_info.mojo index 0ad253962b..b78286450c 100644 --- a/stdlib/test/python/test_python_info.mojo +++ b/stdlib/test/python/test_python_info.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo-no-debug %s | FileCheck %s from python._cpython import PythonVersion diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 0b200f9388..4e16b46918 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo-no-debug %s | FileCheck %s from memory.unsafe import Pointer from python._cpython import CPython, PyObjectPtr diff --git a/stdlib/test/python/test_python_object_len_raises.mojo b/stdlib/test/python/test_python_object_len_raises.mojo index 082394eb3d..662088aa67 100644 --- a/stdlib/test/python/test_python_object_len_raises.mojo +++ b/stdlib/test/python/test_python_object_len_raises.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo %s +# RUN: %mojo-no-debug %s from python.object import PythonObject from python.python import Python diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 5efafe7a50..f22c2bb874 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo-no-debug %s | FileCheck %s from python.object import PythonObject from python.python import Python diff --git a/stdlib/test/random/test_random.mojo b/stdlib/test/random/test_random.mojo index 27c57ed132..4695f32211 100644 --- a/stdlib/test/random/test_random.mojo +++ b/stdlib/test/random/test_random.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from random import randn_float64, random_float64, random_si64, random_ui64, seed diff --git a/stdlib/test/sys/test_build_info_debug.mojo b/stdlib/test/sys/test_build_info_debug.mojo index ac39274b34..3f4b0a3749 100644 --- a/stdlib/test/sys/test_build_info_debug.mojo +++ b/stdlib/test/sys/test_build_info_debug.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: is_debug -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys._build import is_debug_build, is_release_build diff --git a/stdlib/test/sys/test_exit_0.mojo b/stdlib/test/sys/test_exit_0.mojo index 6547d79461..d7134a7150 100644 --- a/stdlib/test/sys/test_exit_0.mojo +++ b/stdlib/test/sys/test_exit_0.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s import sys diff --git a/stdlib/test/sys/test_exit_1.mojo b/stdlib/test/sys/test_exit_1.mojo index d374a537f1..ad8311018e 100644 --- a/stdlib/test/sys/test_exit_1.mojo +++ b/stdlib/test/sys/test_exit_1.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not %mojo -debug-level full %s +# RUN: not %mojo %s import sys diff --git a/stdlib/test/sys/test_has_intel_amx.mojo b/stdlib/test/sys/test_has_intel_amx.mojo index 68cb8bc527..337a6ed9a1 100644 --- a/stdlib/test/sys/test_has_intel_amx.mojo +++ b/stdlib/test/sys/test_has_intel_amx.mojo @@ -16,7 +16,7 @@ # ===----------------------------------------------------------------------=== # # REQUIRES: linux # REQUIRES: amx_tile -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.info import has_intel_amx, os_is_linux diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index df19e3feaf..2743086802 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.intrinsics import ( compressed_store, diff --git a/stdlib/test/sys/test_linux_target.mojo b/stdlib/test/sys/test_linux_target.mojo index a883379f37..fbe3ddd82e 100644 --- a/stdlib/test/sys/test_linux_target.mojo +++ b/stdlib/test/sys/test_linux_target.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: linux -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.info import os_is_linux, os_is_macos diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 738a4e36e6..47d757cae6 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: darwin -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.info import ( diff --git a/stdlib/test/sys/test_targetinfo.mojo b/stdlib/test/sys/test_targetinfo.mojo index 238b041480..7fa5d1b964 100644 --- a/stdlib/test/sys/test_targetinfo.mojo +++ b/stdlib/test/sys/test_targetinfo.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.info import ( alignof, diff --git a/stdlib/test/testing/test_assert_raises_basic.mojo b/stdlib/test/testing/test_assert_raises_basic.mojo index 0d32cbcbf6..8e00966b4c 100644 --- a/stdlib/test/testing/test_assert_raises_basic.mojo +++ b/stdlib/test/testing/test_assert_raises_basic.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from testing import assert_raises diff --git a/stdlib/test/testing/test_assert_raises_no_error.mojo b/stdlib/test/testing/test_assert_raises_no_error.mojo index d682b63ce8..95d0190e06 100644 --- a/stdlib/test/testing/test_assert_raises_no_error.mojo +++ b/stdlib/test/testing/test_assert_raises_no_error.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not %mojo -debug-level full %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL +# RUN: not %mojo %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL from testing import assert_raises diff --git a/stdlib/test/testing/test_assert_raises_no_match.mojo b/stdlib/test/testing/test_assert_raises_no_match.mojo index 53264bcb70..897ae3772d 100644 --- a/stdlib/test/testing/test_assert_raises_no_match.mojo +++ b/stdlib/test/testing/test_assert_raises_no_match.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: has_not -# RUN: not %mojo -debug-level full %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL +# RUN: not %mojo %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL from testing import assert_raises diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 8d50223701..833c8eb492 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from sys.info import os_is_windows from time import now, sleep, time_function diff --git a/stdlib/test/utils/issue_13632.mojo b/stdlib/test/utils/issue_13632.mojo index 5299211e89..02435e8121 100644 --- a/stdlib/test/utils/issue_13632.mojo +++ b/stdlib/test/utils/issue_13632.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from collections import List diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index 6ff5d90e1f..aee81c4ee5 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from utils._numerics import FPUtils diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 016989afcc..c383d1fb10 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from testing import assert_equal, assert_false, assert_true diff --git a/stdlib/test/utils/test_unroll.mojo b/stdlib/test/utils/test_unroll.mojo index 6e3912ac44..b96c5087d8 100644 --- a/stdlib/test/utils/test_unroll.mojo +++ b/stdlib/test/utils/test_unroll.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s | FileCheck %s +# RUN: %mojo %s | FileCheck %s from utils.index import StaticIntTuple from utils.loop import unroll diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index c9c4ec3048..908d2cab91 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -debug-level full %s +# RUN: %mojo %s from sys.ffi import _get_global From ecd86fb5ba830ed44cf11fd514ccf31e29b4d744 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 10 Apr 2024 20:49:43 -0700 Subject: [PATCH 0134/2019] [Stdlib] Add an equal_nan flag to isclose functions (#37392) This allows one to compare values that have nan in the values and matches a capability available in numpy and python. MODULAR_ORIG_COMMIT_REV_ID: 1c5d471c63f4a6dde60483d7525b43fb0d8638ed --- docs/changelog.md | 3 +++ stdlib/src/testing/testing.mojo | 24 +++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4b86a7bd44..3eae87ce1b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -84,6 +84,9 @@ what we publish. low-level logic but isn't designed for general usability and will likely change in the future. +- The `testing.assert_almost_equal` and `math.isclose` functions now have an + `equal_nan` flag. When set to True, then NaNs are considered equal. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index a3e12fc900..a4269c7d82 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -19,6 +19,7 @@ from testing import assert_true ``` """ from collections import Optional +from utils._numerics import isnan # ===----------------------------------------------------------------------=== # # Utilities @@ -32,15 +33,28 @@ fn _abs(x: SIMD) -> __type_of(x): @always_inline fn _isclose( - a: SIMD, b: __type_of(a), *, atol: Scalar[a.type], rtol: Scalar[a.type] + a: SIMD, + b: __type_of(a), + *, + atol: Scalar[a.type], + rtol: Scalar[a.type], + equal_nan: Bool, ) -> SIMD[DType.bool, a.size]: @parameter if a.type.is_bool() or a.type.is_integral(): return a == b + if equal_nan and isnan(a) and isnan(b): + return True + var atol_vec = SIMD[a.type, a.size](atol) var rtol_vec = SIMD[a.type, a.size](rtol) - return _abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b)))) + var res = _abs(a - b) <= (atol_vec.max(rtol_vec * _abs(a).max(_abs(b)))) + + if not equal_nan: + return res + + return res.select(res, isnan(a) and isnan(b)) # ===----------------------------------------------------------------------=== # @@ -217,6 +231,7 @@ fn assert_almost_equal[ msg: String = "", atol: Scalar[type] = 1e-08, rtol: Scalar[type] = 1e-05, + equal_nan: Bool = False, ) raises: """Asserts that the input values are equal up to a tolerance. If it is not then an Error is raised. @@ -231,11 +246,14 @@ fn assert_almost_equal[ msg: The message to print. atol: The _absolute tolerance. rtol: The relative tolerance. + equal_nan: Whether to treat nans as equal. Raises: An Error with the provided message if assert fails and `None` otherwise. """ - var almost_equal = _isclose(lhs, rhs, atol=atol, rtol=rtol) + var almost_equal = _isclose( + lhs, rhs, atol=atol, rtol=rtol, equal_nan=equal_nan + ) if not almost_equal: var err = "AssertionError: " + str(lhs) + " is not close to " + str( rhs From 7ed490cce2652594628a06a750b471355fb46657 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 11 Apr 2024 10:28:19 -0600 Subject: [PATCH 0135/2019] [mojo-stdlib] Improve `ord` and `chr` functions (#36862) Improve `ord` and `chr` functions to accept any character (rather than just ASCII). This can be seen as a first step towards https://github.com/modularml/mojo/issues/1616. Upstream commit: https://github.com/modularml/mojo/commit/9a119f6a6ed44b10e0c74bfd217147f4ae7df844#diff-1772d7ed0fe677cccbb28fa0cf9ae076ca5d30a1ca21f4638c165e794d3d8d74 Note: - I added some tests for verifying the new `ord` function works in the parameter domain for both single and multi-byte characters, which previously was crashing in the elaborator when we originally imported this PR from upstream. Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: 914006a4bb418bdeb12ff7193094b29bb5f8582e --- stdlib/src/builtin/string.mojo | 73 ++++++++++++++++++++++------ stdlib/test/builtin/test_string.mojo | 23 +++++++-- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 8671d1ffd4..92920b49bd 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -63,20 +63,38 @@ fn _ctlz(val: SIMD) -> __type_of(val): fn ord(s: String) -> Int: """Returns an integer that represents the given one-character string. - Given a string representing one ASCII character, return an integer + Given a string representing one character, return an integer representing the code point of that character. For example, `ord("a")` returns the integer `97`. This is the inverse of the `chr()` function. - Currently, extended ASCII characters are not supported in this function. - Args: s: The input string, which must contain only a single character. Returns: An integer representing the code point of the given character. """ - debug_assert(len(s) == 1, "input string length must be 1") - return int(s._buffer[0]) + # UTF-8 to Unicode conversion: (represented as UInt32 BE) + # 1: 0aaaaaaa -> 00000000 00000000 00000000 0aaaaaaa a + # 2: 110aaaaa 10bbbbbb -> 00000000 00000000 00000aaa aabbbbbb a << 6 | b + # 3: 1110aaaa 10bbbbbb 10cccccc -> 00000000 00000000 aaaabbbb bbcccccc a << 12 | b << 6 | c + # 4: 11110aaa 10bbbbbb 10cccccc 10dddddd -> 00000000 000aaabb bbbbcccc ccdddddd a << 18 | b << 12 | c << 6 | d + var p = s._as_ptr().bitcast[DType.uint8]() + var b1 = p.load() + if (b1 >> 7) == 0: # This is 1 byte ASCII char + debug_assert(len(s) == 1, "input string length must be 1") + return b1.to_int() + var num_bytes = _ctlz(~b1) + debug_assert( + len(s) == num_bytes.to_int(), "input string must be one character" + ) + var shift = (6 * (num_bytes - 1)).to_int() + var b1_mask = 0b11111111 >> (num_bytes + 1) + var result = int(b1 & b1_mask) << shift + for i in range(1, num_bytes): + p += 1 + shift -= 6 + result |= (p.load() & 0b00111111).to_int() << shift + return result # ===----------------------------------------------------------------------===# @@ -87,22 +105,49 @@ fn ord(s: String) -> Int: fn chr(c: Int) -> String: """Returns a string based on the given Unicode code point. - Returns the string representing a character whose code point (which must be a - positive integer between 0 and 255) is the integer `i`. For example, - `chr(97)` returns the string `"a"`. This is the inverse of the `ord()` + Returns the string representing a character whose code point is the integer `c`. + For example, `chr(97)` returns the string `"a"`. This is the inverse of the `ord()` function. Args: - c: An integer between 0 and 255 that represents a code point. + c: An integer that represents a code point. Returns: A string containing a single character based on the given code point. """ - debug_assert(0 <= c <= 255, "input ordinal must be in range") - var buf = Pointer[Int8].alloc(2) - buf[0] = c - buf[1] = 0 - return String(buf, 2) + # Unicode (represented as UInt32 BE) to UTF-8 conversion : + # 1: 00000000 00000000 00000000 0aaaaaaa -> 0aaaaaaa a + # 2: 00000000 00000000 00000aaa aabbbbbb -> 110aaaaa 10bbbbbb a >> 6 | 0b11000000, b | 0b10000000 + # 3: 00000000 00000000 aaaabbbb bbcccccc -> 1110aaaa 10bbbbbb 10cccccc a >> 12 | 0b11100000, b >> 6 | 0b10000000, c | 0b10000000 + # 4: 00000000 000aaabb bbbbcccc ccdddddd -> 11110aaa 10bbbbbb 10cccccc 10dddddd a >> 18 | 0b11110000, b >> 12 | 0b10000000, c >> 6 | 0b10000000, d | 0b10000000 + + if (c >> 7) == 0: # This is 1 byte ASCII char + var p = DTypePointer[DType.int8].alloc(2) + p.store(c) + p.store(1, 0) + return String(p, 2) + + @always_inline + fn _utf8_len(val: Int) -> Int: + debug_assert(val > 0x10FFFF, "Value is not a valid Unicode code point") + alias sizes = SIMD[DType.int32, 4]( + 0, 0b1111_111, 0b1111_1111_111, 0b1111_1111_1111_1111 + ) + var values = SIMD[DType.int32, 4](val) + var mask = values > sizes + return mask.cast[DType.uint8]().reduce_add().to_int() + + var num_bytes = _utf8_len(c) + var p = DTypePointer[DType.uint8].alloc(num_bytes + 1) + var shift = 6 * (num_bytes - 1) + var mask = UInt8(0xFF) >> (num_bytes + 1) + var num_bytes_marker = UInt8(0xFF) << (8 - num_bytes) + p.store(((c >> shift) & mask) | num_bytes_marker) + for i in range(1, num_bytes): + shift -= 6 + p.store(i, ((c >> shift) & 0b00111111) | 0b10000000) + p.store(num_bytes, 0) + return String(p.bitcast[DType.int8](), num_bytes + 1) # ===----------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index 9dbdca43f8..f2b8749662 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -166,15 +166,32 @@ fn test_ord() raises: assert_equal(ord("z"), 122) assert_equal(ord("!"), 33) - # FIXME(#26881): Extended ASCII is not yet supported - # This should be `assert_equal` when extended ASCII is supported - assert_not_equal(ord("α"), 224) + # Multi byte character + assert_equal(ord("α"), 945) + assert_equal(ord("➿"), 10175) + assert_equal(ord("🔥"), 128293) + + # Make sure they work in the parameter domain too + alias single_byte = ord("A") + assert_equal(single_byte, 65) + alias single_byte2 = ord("!") + assert_equal(single_byte2, 33) + + alias multi_byte = ord("α") + assert_equal(multi_byte, 945) + alias multi_byte2 = ord("➿") + assert_equal(multi_byte2, 10175) + alias multi_byte3 = ord("🔥") + assert_equal(multi_byte3, 128293) fn test_chr() raises: assert_equal("A", chr(65)) assert_equal("a", chr(97)) assert_equal("!", chr(33)) + assert_equal("α", chr(945)) + assert_equal("➿", chr(10175)) + assert_equal("🔥", chr(128293)) fn test_string_indexing() raises: From 3476fa6fdf00d7f1bdbacb2e0d0e88478be8804e Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 11 Apr 2024 11:25:32 -0600 Subject: [PATCH 0136/2019] [stdlib] Move `parallel_memcpy` to `algorithm` package (#37432) Currently, the `buffer` package depends on the `algorithm` package which depends on the `buffer` package, which creates a package cycle. Package cycles are bad for a lot of reasons. So, to fix the cycle, just move `parallel_memcpy` function into the `algorithm` package. Previously, the `buffer` package only depended on the `algorithm` package for one function: `sync_parallelize` which was only used in the `parallel_memcpy` function. So, this is the easiest solution. Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 0bb8e52a2ac532133149f0a954692309777009d4 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 3eae87ce1b..f88aa3143c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -123,6 +123,9 @@ what we publish. environment flags should be provided at the point of package use (e.g. `mojo run` or `mojo build`). +- `parallel_memcpy` function has moved from the `buffer` package to the `algorithm` + package. Please update your imports accordingly. + ### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of From 79c9d67fe35f5f2d6258756566d9512eaf3d1752 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Thu, 11 Apr 2024 16:35:17 -0600 Subject: [PATCH 0137/2019] [stdlib] Remove `SIMD.to_int()` in favor of `int(...)` (#37427) Remove `SIMD.to_int(value)` function in favor of using `int(value)` which still calls the dunder `int` method. Update callers everywhere to use `int(value)`. Fixes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 777d2c8b8dab4c90b66f4677c89fc19890c5bfeb --- docs/changelog.md | 2 ++ docs/manual/parameters/index.ipynb | 4 ++-- stdlib/src/builtin/hash.mojo | 4 ++-- stdlib/src/builtin/simd.mojo | 13 ------------- stdlib/src/builtin/string.mojo | 12 +++++------- stdlib/src/collections/dict.mojo | 8 ++++---- 6 files changed, 15 insertions(+), 28 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f88aa3143c..eab64bf61c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -142,6 +142,8 @@ what we publish. - `List.pop_back()` has been removed. Use `List.pop()` instead which defaults to popping the last element in the list. +- `SIMD.to_int(value)` has been removed. Use `int(value)` instead. + ### 🛠️ Fixed - [#516](https://github.com/modularml/mojo/issues/516) and diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index 31a4480582..bafd5b35c6 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -773,9 +773,9 @@ "fn reduce_add[ty: DType, size: Int](x: SIMD[ty, size]) -> Int:\n", " @parameter\n", " if size == 1:\n", - " return x[0].to_int()\n", + " return int(x[0])\n", " elif size == 2:\n", - " return x[0].to_int() + x[1].to_int()\n", + " return int(x[0]) + int(x[1])\n", "\n", " # Extract the top/bottom halves, add them, sum the elements.\n", " alias half_size = size // 2\n", diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index de4624a9fc..90264175bd 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -194,8 +194,8 @@ fn _hash_int8[size: Int](data: SIMD[DType.uint8, size]) -> Int: for i in range(size): hash_data = _HASH_UPDATE(hash_data, data[i].cast[DType.int64]()) # TODO(27659): 'lit.globalvar.ref' error - # return hash_data.to_int() ^ HASH_SECRET - return hash_data.to_int() ^ _HASH_SECRET() + # return int(hash_data) ^ HASH_SECRET + return int(hash_data) ^ _HASH_SECRET() fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f7d4ff7678..5260971c19 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -549,19 +549,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( buf.size += 1 # for the null terminator. return String(buf^) - @always_inline("nodebug") - fn to_int(self) -> Int: - """Casts to the value to an Int. If there is a fractional component, - then the value is truncated towards zero. - - Constraints: - The size of the SIMD vector must be 1. - - Returns: - The value of the single integer element in the SIMD vector. - """ - return self.__int__() - @always_inline("nodebug") fn __add__(self, rhs: Self) -> Self: """Computes `self + rhs`. diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 92920b49bd..2e321433f1 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -82,18 +82,16 @@ fn ord(s: String) -> Int: var b1 = p.load() if (b1 >> 7) == 0: # This is 1 byte ASCII char debug_assert(len(s) == 1, "input string length must be 1") - return b1.to_int() + return int(b1) var num_bytes = _ctlz(~b1) - debug_assert( - len(s) == num_bytes.to_int(), "input string must be one character" - ) - var shift = (6 * (num_bytes - 1)).to_int() + debug_assert(len(s) == int(num_bytes), "input string must be one character") + var shift = int((6 * (num_bytes - 1))) var b1_mask = 0b11111111 >> (num_bytes + 1) var result = int(b1 & b1_mask) << shift for i in range(1, num_bytes): p += 1 shift -= 6 - result |= (p.load() & 0b00111111).to_int() << shift + result |= int(p.load() & 0b00111111) << shift return result @@ -135,7 +133,7 @@ fn chr(c: Int) -> String: ) var values = SIMD[DType.int32, 4](val) var mask = values > sizes - return mask.cast[DType.uint8]().reduce_add().to_int() + return int(mask.cast[DType.uint8]().reduce_add()) var num_bytes = _utf8_len(c) var p = DTypePointer[DType.uint8].alloc(num_bytes + 1) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 9f59e5ed1d..3f799da2b6 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -274,16 +274,16 @@ struct _DictIndex: fn get_index(self, reserved: Int, slot: Int) -> Int: if reserved <= 128: var data = self.data.bitcast[DType.int8]() - return data.load(slot % reserved).to_int() + return int(data.load(slot % reserved)) elif reserved <= 2**16 - 2: var data = self.data.bitcast[DType.int16]() - return data.load(slot % reserved).to_int() + return int(data.load(slot % reserved)) elif reserved <= 2**32 - 2: var data = self.data.bitcast[DType.int32]() - return data.load(slot % reserved).to_int() + return int(data.load(slot % reserved)) else: var data = self.data.bitcast[DType.int64]() - return data.load(slot % reserved).to_int() + return int(data.load(slot % reserved)) fn set_index(inout self, reserved: Int, slot: Int, value: Int): if reserved <= 128: From b666d560be39fd8e62268da882f20878ba5222c0 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Fri, 12 Apr 2024 10:05:12 -0700 Subject: [PATCH 0138/2019] [mojo-examples] Remove numpy comparison from matmul output (#37564) Results vary dramatically on different CPU's, removing for now MODULAR_ORIG_COMMIT_REV_ID: fd9f1f48561b21e28d3277a30ac6ecd279944ec5 --- examples/matmul.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/matmul.mojo b/examples/matmul.mojo index 719f20a8a9..3b4142414b 100644 --- a/examples/matmul.mojo +++ b/examples/matmul.mojo @@ -208,8 +208,8 @@ fn bench[ var py = Python.import_module("builtins") _ = py.print( - py.str("{:<13}{:>8.3f} GFLOPS {:>9.2f}x Python {:>5.2f}x Numpy").format( - name, gflops, speedup, numpy_speedup + py.str("{:<13}{:>8.3f} GFLOPS {:>9.2f}x Python").format( + name, gflops, speedup ) ) From 4c12c8547f5f4a216f5a4376545b95909ea9e8ea Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 12 Apr 2024 10:18:43 -0700 Subject: [PATCH 0139/2019] [stdlib] Migrating Pointer to UnsafePointer - 1/n (#37334) This is starting to push `UnsafePointer` throughout the code replacing `Pointer`. The following files are migrated in this change: - arg.mojo - time.mojo - _arc.mojo - stringref.mojo Supporting [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 52e839568773132853697e23af31cc1190c8a592 --- stdlib/src/base64/base64.mojo | 2 -- stdlib/src/memory/_arc.mojo | 12 ++++----- stdlib/src/memory/unsafe_pointer.mojo | 38 ++++++++++++++++++++++++--- stdlib/src/sys/arg.mojo | 4 +-- stdlib/src/time/time.mojo | 18 +++++++------ stdlib/src/utils/stringref.mojo | 11 ++++---- 6 files changed, 59 insertions(+), 26 deletions(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index fe02bbb26b..6bb1da0cc4 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -22,8 +22,6 @@ from base64 import b64encode from collections import List from sys.info import simdwidthof -from memory.unsafe import DTypePointer - # ===----------------------------------------------------------------------===# # b64encode # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 003cdd8e72..34f2ee51d7 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -80,7 +80,7 @@ struct Arc[T: Movable](CollectionElement): """ alias _type = _ArcInner[T] - var _inner: Pointer[Self._type] + var _inner: UnsafePointer[Self._type] fn __init__(inout self, owned value: T): """Construct a new thread-safe, reference-counted smart pointer, @@ -89,11 +89,11 @@ struct Arc[T: Movable](CollectionElement): Args: value: The value to manage. """ - self._inner = Pointer[Self._type].alloc(1) - __get_address_as_uninit_lvalue(self._inner.address) = Self._type(value^) + self._inner = UnsafePointer[Self._type].alloc(1) + __get_address_as_uninit_lvalue(self._inner.value) = Self._type(value^) _ = self._inner[].increment() - fn __init__(inout self, *, owned inner: Pointer[Self._type]): + fn __init__(inout self, *, owned inner: UnsafePointer[Self._type]): """Copy an existing reference. Increment the refcount to the object.""" _ = inner[].increment() self._inner = inner @@ -117,7 +117,7 @@ struct Arc[T: Movable](CollectionElement): var rc = self._inner[].decrement() if rc < 1: # Call inner destructor, then free the memory - _ = __get_address_as_owned_value(self._inner.address) + _ = __get_address_as_owned_value(self._inner.value) self._inner.free() fn set(self, owned new_value: T): @@ -175,7 +175,7 @@ struct Arc[T: Movable](CollectionElement): ), ]() - var ptr: Pointer[_ArcInner[T]] = self._inner + var ptr: UnsafePointer[_ArcInner[T]] = self._inner # Add a +1 to the ref count, since we're creating a new `Arc` instance # pointing at the same data. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index c2ca23cafe..bb0000f083 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -54,9 +54,7 @@ struct UnsafePointer[ Returns: A null pointer. """ - return Self { - value: __mlir_attr[`#interp.pointer<0> : `, Self.pointer_type] - } + return Self.get_null() @always_inline fn __init__(value: Self.pointer_type) -> Self: @@ -98,6 +96,18 @@ struct UnsafePointer[ ) } + @staticmethod + @always_inline("nodebug") + fn get_null() -> Self: + """Constructs a UnsafePointer representing nullptr. + + Returns: + Constructed nullptr UnsafePointer object. + """ + return Self { + value: __mlir_attr[`#interp.pointer<0> : `, Self.pointer_type] + } + @staticmethod @always_inline fn alloc(count: Int) -> Self: @@ -137,6 +147,28 @@ struct UnsafePointer[ """Free the memory referenced by the pointer.""" Pointer[Int8, address_space=address_space](address=int(self)).free() + @always_inline("nodebug") + fn bitcast[ + new_type: AnyType + ](self) -> UnsafePointer[new_type, address_space]: + """Bitcasts a UnsafePointer to a different type. + + Parameters: + new_type: The target type. + + Returns: + A new UnsafePointer object with the specified type and the same address, + as the original UnsafePointer. + """ + + @parameter + if _mlirtype_is_eq[T, new_type](): + return rebind[UnsafePointer[new_type, address_space]](self) + + return __mlir_op.`pop.pointer.bitcast`[ + _type = UnsafePointer[new_type, address_space].pointer_type, + ](self.value) + @always_inline fn bitcast_element[ new_type: AnyType diff --git a/stdlib/src/sys/arg.mojo b/stdlib/src/sys/arg.mojo index 8c49e63784..9c45f70f6a 100644 --- a/stdlib/src/sys/arg.mojo +++ b/stdlib/src/sys/arg.mojo @@ -22,7 +22,7 @@ from sys import argv from sys import external_call -from memory.unsafe import Pointer +from memory.unsafe_pointer import UnsafePointer from utils import StringRef @@ -36,6 +36,6 @@ fn argv() -> VariadicList[StringRef]: """ var result = VariadicList[StringRef]("") external_call["KGEN_CompilerRT_GetArgV", NoneType]( - Pointer[VariadicList[StringRef]].address_of(result) + UnsafePointer[VariadicList[StringRef]].address_of(result) ) return result diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 99513886b8..390c1ee2e8 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -23,7 +23,7 @@ from sys import external_call from sys.info import os_is_linux, os_is_windows from builtin.simd import _floor -from memory.unsafe import Pointer +from memory.unsafe_pointer import UnsafePointer # ===----------------------------------------------------------------------===# # Utilities @@ -99,7 +99,7 @@ fn _clock_gettime(clockid: Int) -> _CTimeSpec: # Call libc's clock_gettime. _ = external_call["clock_gettime", Int32]( - Int32(clockid), Pointer.address_of(ts) + Int32(clockid), UnsafePointer.address_of(ts) ) return ts @@ -130,7 +130,7 @@ fn _monotonic_nanoseconds() -> Int: if os_is_windows(): var ft = _FILETIME() external_call["GetSystemTimePreciseAsFileTime", NoneType]( - Pointer.address_of(ft) + UnsafePointer.address_of(ft) ) return ft.as_nanoseconds() @@ -188,17 +188,19 @@ fn _time_function_windows[func: fn () capturing -> None]() -> Int: """Calculates elapsed time in Windows""" var ticks_per_sec: _WINDOWS_LARGE_INTEGER = 0 - var ticks_per_sec_ptr = Pointer[_WINDOWS_LARGE_INTEGER].address_of( + var ticks_per_sec_ptr = UnsafePointer[_WINDOWS_LARGE_INTEGER].address_of( ticks_per_sec ) external_call["QueryPerformanceFrequency", NoneType](ticks_per_sec_ptr) var starting_tick_count: _WINDOWS_LARGE_INTEGER = 0 - var start_ptr = Pointer[_WINDOWS_LARGE_INTEGER].address_of( + var start_ptr = UnsafePointer[_WINDOWS_LARGE_INTEGER].address_of( starting_tick_count ) var ending_tick_count: _WINDOWS_LARGE_INTEGER = 0 - var end_ptr = Pointer[_WINDOWS_LARGE_INTEGER].address_of(ending_tick_count) + var end_ptr = UnsafePointer[_WINDOWS_LARGE_INTEGER].address_of( + ending_tick_count + ) external_call["QueryPerformanceCounter", NoneType](start_ptr) func() @@ -250,8 +252,8 @@ fn sleep(sec: Float64): int(total_secs.cast[DType.index]()), int((sec - total_secs) * NANOSECONDS_IN_SECOND), ) - var req = Pointer[_CTimeSpec].address_of(tv_spec) - var rem = Pointer[_CTimeSpec].get_null() + var req = UnsafePointer[_CTimeSpec].address_of(tv_spec) + var rem = UnsafePointer[_CTimeSpec].get_null() _ = external_call["nanosleep", Int32](req, rem) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0fda1fa47e..0a316f70be 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,7 +16,8 @@ from builtin.dtype import _uint_type_of_width from builtin.string import _atol -from memory.unsafe import DTypePointer, Pointer +from memory.unsafe import DTypePointer +from memory.unsafe_pointer import UnsafePointer # ===----------------------------------------------------------------------=== # # Utilities @@ -98,7 +99,7 @@ struct StringRef( The constructor takes a raw pointer and a length. Args: - ptr: Pointer to the string. + ptr: UnsafePointer to the string. len: The length of the string. Returns: @@ -125,17 +126,17 @@ struct StringRef( return Self {data: ptr, length: len} @always_inline - fn __init__(ptr: Pointer[Int8]) -> StringRef: + fn __init__(ptr: UnsafePointer[Int8]) -> StringRef: """Construct a StringRef value given a null-terminated string. Args: - ptr: Pointer to the string. + ptr: UnsafePointer to the string. Returns: Constructed `StringRef` object. """ - return DTypePointer[DType.int8](ptr.address) + return DTypePointer[DType.int8](ptr.value) @always_inline fn __init__(ptr: DTypePointer[DType.int8]) -> StringRef: From ad326b7898ab7492a4c745285815c8fc2a6fd9d0 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 12 Apr 2024 11:53:29 -0700 Subject: [PATCH 0140/2019] [mojo][docs] Generate separate page for struct/trait/function (#37536) Creates a separate template for module/package summaries, structs/traits, and functions. This provides a much more scanable overview on each module page, so it's easy to see all stucts, traits, and functions with their summaries. This should help disambiguate search results because a module with several structs currently gets just one search result and it's usually not clear that is the page you want. Instead having a separate URL with a distinct title makes it much easier to search and find the API you're looking for. It also makes it a lot easier to identify/distinguish traits from structs. See stage here: https://docsite-v2-git-scott-staging-mojodoc2-modular-ai.vercel.app/mojo/stdlib/builtin/int/ resolves: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 730346773d4ce2a405a3363709b5d6a117cc481a --- docs/lib.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/lib.md b/docs/lib.md index 27d36c61af..93ff45569b 100644 --- a/docs/lib.md +++ b/docs/lib.md @@ -1,17 +1,11 @@ --- title: Mojo🔥 modules sidebar_label: Module index -toc: false hide_table_of_contents: true description: A list of all modules in the Mojo standard library. -website: - open-graph: - image: /static/images/mojo-social-card.png - twitter-card: - image: /static/images/mojo-social-card.png listing: - id: stdlib - contents: "stdlib/**/!(index.md)" + contents: "stdlib/*/*/index.md" type: grid page-size: 99 --- From 5487c7bd2d7ed0aadbf14304d1dba005fc7040ec Mon Sep 17 00:00:00 2001 From: River Riddle Date: Fri, 12 Apr 2024 15:27:07 -0600 Subject: [PATCH 0141/2019] [mojo-lsp] Support signature types with named parameters/arguments (#37596) This PR adds proper support for signature types by synthesizing a FunctionDeclView from a parsed signature type. This allows for tracking references for named parameters/arguments in the signature type, and also fixes various crashes around them. This functionality will be used in a followup as a basis for supporting closures. Fixes https://github.com/modularml/mojo/issues/1997 Fixes https://github.com/modularml/mojo/issues/2006 Fixes https://github.com/modularml/mojo/issues/2007 Fixes #37126 MODULAR_ORIG_COMMIT_REV_ID: b16ffac01d7bfb904e16da93731ed9e8f6016a77 --- docs/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index eab64bf61c..7682b644dd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -181,3 +181,10 @@ what we publish. - [#1917](https://github.com/modularml/mojo/issues/1917) Fix a crash after syntax error during tuple creation + +- [#2006](https://github.com/modularml/mojo/issues/2006) The Mojo LSP now + properly supports signature types with named arguments and parameters. + +- [#2007](https://github.com/modularml/mojo/issues/2007) and + [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer + crashes on certain types of closures. From c7f9fa8345810abd639a66c5dc28e089e429c843 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 12 Apr 2024 15:30:13 -0700 Subject: [PATCH 0142/2019] [docs] Update links to stdlib as per new file structure (#37595) This follows the mojodoc structure changes from [Internal link] Example... Previously: https://docs.modular.com/mojo/stdlib/builtin/int.html#intableraising Now: https://docs.staging.modular.com/mojo/stdlib/builtin/int/intableraising There are bound to be some mistakes in this find/replace but this mostly looks effective. But there's also been a lot of refactoring going on in the stdlib, so I already noticed during my review that some of these links don't work due to that refactoring and not these URL changes. I'll run a link checker post-publish to catch any issues. MODULAR_ORIG_COMMIT_REV_ID: efce16f59f2fff6f8437774f0474dd17335fd2f9 --- docs/changelog-released.md | 271 ++++++++++++----------- docs/manual/decorators/unroll.ipynb | 2 +- docs/manual/functions.ipynb | 10 +- docs/manual/lifecycle/death.ipynb | 4 +- docs/manual/lifecycle/index.ipynb | 6 +- docs/manual/lifecycle/life.ipynb | 10 +- docs/manual/parameters/index.ipynb | 8 +- docs/manual/python/index.ipynb | 4 +- docs/manual/python/types.ipynb | 24 +- docs/manual/structs.ipynb | 2 +- docs/manual/traits.ipynb | 40 ++-- docs/manual/values/ownership.ipynb | 2 +- docs/manual/values/value-semantics.ipynb | 4 +- docs/manual/variables.ipynb | 2 +- docs/roadmap.md | 6 +- docs/upgrade-guide.md | 6 +- stdlib/src/builtin/hash.mojo | 2 +- stdlib/src/builtin/int.mojo | 10 +- stdlib/src/builtin/len.mojo | 10 +- stdlib/src/builtin/str.mojo | 12 +- 20 files changed, 218 insertions(+), 217 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 1170f0153e..215bfadf6e 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -163,7 +163,7 @@ This release doesn't include any changes to Mojo. #### ⭐️ New - `DynamicVector` has been renamed to - [`List`](/mojo/stdlib/collections/list.html#list), and has moved from the + [`List`](/mojo/stdlib/collections/list/List), and has moved from the `collections.vector` module to the `collections.list` module. In addition: - You can now construct a `List` from a variadic number of values. For @@ -174,14 +174,14 @@ This release doesn't include any changes to Mojo. ``` - `List` and - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + [`InlinedFixedVector`](/mojo/stdlib/collections/vector/InlinedFixedVector) types now support negative indexing. This means that you can write `vec[-1]` which is equivalent to `vec[len(vec)-1]`. - `List.push_back()` has been removed. Please use the `append()` function instead. -- The [`print()`](/mojo/stdlib/builtin/io#print) function now takes `sep` and +- The [`print()`](/mojo/stdlib/builtin/io/print) function now takes `sep` and `end` keyword arguments. This means that you can write: ```mojo @@ -193,7 +193,7 @@ This release doesn't include any changes to Mojo. Also, the `print_no_newline()` function has been removed. Please use `print(end="")` instead. -- The [`FloatLiteral`](/mojo/stdlib/builtin/float_literal#floatliteral) type is +- The [`FloatLiteral`](/mojo/stdlib/builtin/float_literal/FloatLiteral) type is now an infinite-precision nonmaterializable type. This means you can do compile-time calculations using `FloatLiteral` without rounding errors. When materialized at runtime, a `FloatLiteral` value is converted to a @@ -207,22 +207,23 @@ This release doesn't include any changes to Mojo. ``` - String types all conform to the - [`IntableRaising`](/mojo/stdlib/builtin/int#intableraising) trait. This means + [`IntableRaising`](/mojo/stdlib/builtin/int/IntableRaising) trait. This means that you can now call `int("123")` to get the integer `123`. If the integer cannot be parsed from the string, then an error is raised. -- The `Tensor` type now has [`argmax()`](/mojo/stdlib/tensor/tensor#argmax) and - [`argmin()`](/mojo/stdlib/tensor/tensor#argmin) functions to compute the - position of the max or min value. Note: this should return a `Tensor[Int]` - but currently the output tensor is the same type as the input tensor. This - will be fixed in a future release. +- The `Tensor` type now has +[`argmax()`](/mojo/stdlib/tensor/tensor/Tensor#argmax) and +[`argmin()`](/mojo/stdlib/tensor/tensor/Tensor#argmin) functions to compute the +position of the max or min value. Note: this should return a `Tensor[Int]` but +currently the output tensor is the same type as the input tensor. This will be +fixed in a future release. - Added a new - [`collections.OptionalReg`](/mojo/stdlib/collections/optional#optionalreg) + [`collections.OptionalReg`](/mojo/stdlib/collections/optional/OptionalReg) type, a register-passable alternative to - [`Optional`](/mojo/stdlib/collections/optional#optional). + [`Optional`](/mojo/stdlib/collections/optional/Optional). -- The [`ulp()`](/mojo/stdlib/math/math#ulp) function has been added to the +- The [`ulp()`](/mojo/stdlib/math/math/ulp) function has been added to the `math` module. This allows you to get the units of least precision (or units of last place) of a floating point value. @@ -230,9 +231,9 @@ This release doesn't include any changes to Mojo. - The `simd_load()`, `simd_store()`, `aligned_simd_load()`, and `aligned_simd_store()` methods on - [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer), - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), and - [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) have been merged into + [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer), + [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer), and + [`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) have been merged into a more expressive set of `load()` and `store()` methods with keyword-only `width` and `alignment` parameters: @@ -248,18 +249,18 @@ This release doesn't include any changes to Mojo. ``` - The - [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable#equalitycomparable) + [`EqualityComparable`](/mojo/stdlib/builtin/equality_comparable/EqualityComparable) trait now requires the `__ne__()` method for conformance in addition to the previously required `__eq__()` method. - Many types now declare conformance to `EqualityComparable` trait. -- [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) parameter order +- [`StaticTuple`](/mojo/stdlib/utils/static_tuple/StaticTuple) parameter order has changed to `StaticTuple[type, size]` for consistency with `SIMD` and similar collection types. - The signature of the - [`elementwise()`](/mojo/stdlib/algorithm/functional#elementwise) function has + [`elementwise()`](/mojo/stdlib/algorithm/functional/elementwise) function has been changed. The new order is is `function`, `simd_width`, and then `rank`. As a result, the rank parameter can now be inferred and one can call `elementwise()` without it: @@ -288,31 +289,31 @@ This release doesn't include any changes to Mojo. library. Specifically, the following are some breaking changes worth calling out. Please update your import statements accordingly. - - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer), - [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer), and friends have moved + - [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer), + [`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer), and friends have moved from the `memory` package into a new `buffer` package. ```mojo from buffer import Buffer, NDBuffer ``` - - `utils.list`, including the [`Dim`](/mojo/stdlib/buffer/list#dim) and - [`DimList`](/mojo/stdlib/buffer/list#dimlist) types, has moved to + - `utils.list`, including the [`Dim`](/mojo/stdlib/buffer/list/Dim) and + [`DimList`](/mojo/stdlib/buffer/list/DimList) types, has moved to the `buffer` package. ```mojo from buffer import Dim, DimList ``` - - The [`parallel_memcpy()`](/mojo/stdlib/buffer/memory#parallel_memcpy) + - The [`parallel_memcpy()`](/mojo/stdlib/algorithm/memory/parallel_memcpy) function has moved from the `memory` package into the `buffer` package. ```mojo from buffer import parallel_memcpy ``` - - The [`rand()`](/mojo/stdlib/tensor/random#rand) and - [`randn()`](/mojo/stdlib/tensor/random#randn) functions from the `random` + - The [`rand()`](/mojo/stdlib/tensor/random/rand) and + [`randn()`](/mojo/stdlib/tensor/random/randn) functions from the `random` package that return a `Tensor` have moved to the `tensor` package. Note that the overloads that write to a `DTypePointer` remain in the `random` package. @@ -325,15 +326,15 @@ This release doesn't include any changes to Mojo. ``` - The `trap()` function has been renamed to - [`abort()`](/mojo/stdlib/os/os#abort). It also has moved from the `debug` + [`abort()`](/mojo/stdlib/os/os/abort). It also has moved from the `debug` module to the `os` module. ```mojo from os import abort ``` - - The [`isinf()`](/mojo/stdlib/math/math#isinf) and - [`isfinite()`](/mojo/stdlib/math/math#isfinite) methods have been moved from + - The [`isinf()`](/mojo/stdlib/math/math/isinf) and + [`isfinite()`](/mojo/stdlib/math/math/isfinite) methods have been moved from `math.limits` to the `math` module. ```mojo @@ -390,10 +391,10 @@ This release doesn't include any changes to Mojo. This means that calls to `memcpy` of the form `memcpy[Dtype.xyz](...)` will no longer work and the user would have to change the code to `memcpy(...)`. -- The [`memcpy()`](/mojo/stdlib/memory/memory#memcpy) overload that worked on - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types has been removed in favor - of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe#pointer) and - [`DTypePointer`](/mojo/stdlib/memory/unsafe#dtypepointer): +- The [`memcpy()`](/mojo/stdlib/memory/memory/memcpy) overload that worked on + [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) types has been removed in favor + of just overloads for [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer) and + [`DTypePointer`](/mojo/stdlib/memory/unsafe/dtypepointer): ```mojo # Doesn't work @@ -454,7 +455,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### ⭐️ New -- We now have a [`Set`](/mojo/stdlib/collections/set.html#set) type in our +- We now have a [`Set`](/mojo/stdlib/collections/set/Set) type in our collections! `Set` is backed by a `Dict`, so it has fast add, remove, and `in` checks, and requires member elements to conform to the `KeyElement` trait. @@ -585,9 +586,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. and [automatic parameterization of functions](/mojo/manual/parameters/#automatic-parameterization-of-functions). -- [`DynamicVector`](/mojo/stdlib/collections/list#list) now +- [`DynamicVector`](/mojo/stdlib/collections/list/List) now supports iteration. Iteration values are instances of - [Reference](/mojo/stdlib/memory/unsafe#reference) and require dereferencing: + [Reference](/mojo/stdlib/memory/unsafe/Reference) and require dereferencing: ```mojo var v: DynamicVector[String]() @@ -601,8 +602,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - `DynamicVector` now has - [`reverse()`](/mojo/stdlib/collections/vector.html#reverse) and - [`extend()`](/mojo/stdlib/collections/vector.html#extend) methods. + [`reverse()`](/mojo/stdlib/collections/list/List#reverse) and + [`extend()`](/mojo/stdlib/collections/list/List#extend) methods. - The `mojo package` command now produces compilation agnostic packages. Compilation options such as O0, or --debug-level, are no longer needed or @@ -648,7 +649,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. build. The implementation of Mojo async is undergoing a rework 🚧. - The standard library `slice` type has been renamed to - [`Slice`](/mojo/stdlib/builtin/builtin_slice#slice), and a `slice` + [`Slice`](/mojo/stdlib/builtin/builtin_slice/Slice), and a `slice` function has been introduced. This makes Mojo closer to Python and makes the `Slice` type follow the naming conventions of other types like `Int`. @@ -672,38 +673,38 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. `UnusualSlice` constructor. - The `__refitem__()` accessor method may now return a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) instead of having to + [`Reference`](/mojo/stdlib/memory/unsafe/reference) instead of having to return an MLIR internal reference type. -- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer.html#move_into) +- Added [`AnyPointer.move_into()`](/mojo/stdlib/memory/anypointer/AnyPointer#move_into) method, for moving a value from one pointer memory location to another. -- Added built-in [`hex()`](/mojo/stdlib/builtin/hex#hex) function, which can be +- Added built-in [`hex()`](/mojo/stdlib/builtin/hex/hex) function, which can be used to format any value whose type implements the - [`Intable`](/mojo/stdlib/builtin/int#intable) trait as a hexadecimal string. + [`Intable`](/mojo/stdlib/builtin/int/Intable) trait as a hexadecimal string. -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now implements +- [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now implements `__is__` and `__isnot__` so that you can use expressions of the form `x is y` and `x is not y` with `PythonObject`. -- [`PythonObject`](/mojo/stdlib/python/object#pythonobject) now conforms to the +- [`PythonObject`](/mojo/stdlib/python/object/PythonObject) now conforms to the `SizedRaising` trait. This means the built-in - [`len()`](/mojo/stdlib/builtin/len#len) function now works on `PythonObject`. + [`len()`](/mojo/stdlib/builtin/len/len) function now works on `PythonObject`. -- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat#stat) - and [`lstat()`](/mojo/stdlib/os/fstat#lstat) functions. +- The `os` package now contains the [`stat()`](/mojo/stdlib/os/fstat/stat) + and [`lstat()`](/mojo/stdlib/os/fstat/lstat) functions. - A new [`os.path`](/mojo/stdlib/os/path/path) package now allows you to query properties on paths. - The `os` package now has a - [`PathLike`](/mojo/stdlib/os/pathlike.html#pathlike) trait. A struct conforms + [`PathLike`](/mojo/stdlib/os/pathlike/PathLike) trait. A struct conforms to the `PathLike` trait by implementing the `__fspath__()` function. -- The [`pathlib.Path`](/mojo/stdlib/pathlib/path#path) now has functions to +- The [`pathlib.Path`](/mojo/stdlib/pathlib/path/Path) now has functions to query properties of the path. -- The [`listdir()`](/mojo/stdlib/pathlib/path#listdir) method now exists on +- The [`listdir()`](/mojo/stdlib/pathlib/path/Path#listdir) method now exists on [`pathlib.Path`](/mojo/stdlib/pathlib/path) and also exists in the `os` module to work on `PathLike` structs. For example, the following sample lists all the directories in the `/tmp` directory: @@ -734,11 +735,11 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. print(files[i]) ``` -- The [`find()`](/mojo/stdlib/builtin/string_literal#find), - [`rfind()`](/mojo/stdlib/builtin/string_literal#rfind), - [`count()`](/mojo/stdlib/builtin/string_literal#count), and - [`__contains__()`](/mojo/stdlib/builtin/string_literal#__contains__) methods - now work on string literals. This means that you can write: +- The [`find()`](/mojo/stdlib/builtin/string_literal/StringLiteral#find), + [`rfind()`](/mojo/stdlib/builtin/string_literal/StringLiteral#rfind), + [`count()`](/mojo/stdlib/builtin/string_literal/StringLiteral#count), and + [`__contains__()`](/mojo/stdlib/builtin/string_literal/StringLiteral#__contains__) + methods now work on string literals. This means that you can write: ```mojo if "Mojo" in "Hello Mojo": @@ -746,12 +747,12 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - Breakpoints can now be inserted programmatically within the code using the - builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint#breakpoint) function. + builtin [`breakpoint()`](/mojo/stdlib/builtin/breakpoint/breakpoint) function. Note: on Graviton instances, the debugger might not be able to resume after hitting this kind of breakpoint. -- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool#boolable) trait that +- Added a builtin [`Boolable`](/mojo/stdlib/builtin/bool/Boolable) trait that describes a type that can be represented as a boolean value. To conform to the trait, a type must implement the `__bool__()` method. @@ -763,9 +764,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Trivial types, like MLIR types and function types, can now be bound implicitly to traits that require copy constructors or move constructors, such as - [`Movable`](/mojo/stdlib/builtin/value.html#movable), - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable), and - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement). + [`Movable`](/mojo/stdlib/builtin/value/Movable), + [`Copyable`](/mojo/stdlib/builtin/value/Copyable), and + [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement). - A new magic `__lifetime_of(expr)` call will yield the lifetime of a memory value. We hope and expect that this will eventually be replaced by @@ -811,7 +812,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - `vectorize_unroll` has been removed, and - [`vectorize`](/mojo/stdlib/algorithm/functional#vectorize) now has a parameter + [`vectorize`](/mojo/stdlib/algorithm/functional/vectorize) now has a parameter named `unroll_factor` with a default value of 1. Increasing `unroll_factor` may improve performance at the cost of binary size. See the [loop unrolling blog here](https://www.modular.com/blog/what-is-loop-unrolling-how-you-can-speed-up-mojo) @@ -835,8 +836,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. unroll[func, unroll_count]() ``` -- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) and - [`Buffer`](/mojo/stdlib/buffer/buffer#buffer) types have changed. Now, both +- The signature of the [`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) and + [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) types have changed. Now, both take the type as the first parameter and no longer require the shape parameter. This allows you to use these types and have sensible defaults. For example: @@ -862,24 +863,24 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. but 1 was specified`) the missing arguments are now described by name (e.g. `missing 2 required positional arguments: 'b', 'c'`). -- The [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) trait +- The [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait is now a built-in trait and has been removed from `collections.vector`. - The `DynamicVector(capacity: Int)` constructor has been changed to take `capacity` as a keyword-only argument to prevent implicit conversion from `Int`. -- [`Variant.get[T]()`](/mojo/stdlib/utils/variant#get) now returns a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) to the value rather than a +- [`Variant.get[T]()`](/mojo/stdlib/utils/variant/Variant#get) now returns a + [`Reference`](/mojo/stdlib/memory/unsafe/reference) to the value rather than a copy. -- The [`String`](/mojo/stdlib/builtin/string.html#string) methods `tolower()` +- The [`String`](/mojo/stdlib/builtin/string/String) methods `tolower()` and `toupper()` have been renamed to `str.lower()` and `str.upper()`. - The `ref` and `mutref` identifiers are no longer reserved as Mojo keywords. We originally thought about using those as language sugar for references, but we believe that generic language features combined with the - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type will provide a good + [`Reference`](/mojo/stdlib/memory/unsafe/reference) type will provide a good experience without dedicated sugar. ### 🛠️ Fixed @@ -959,9 +960,9 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - A new Mojo-native dictionary type, [`Dict`](/mojo/stdlib/collections/dict.html) for storing key-value pairs. `Dict` stores values that conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) + [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) trait. Keys need to conform to the new - [`KeyElement`](/mojo/stdlib/collections/dict.html#keyelement) trait, which is + [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement) trait, which is not yet implemented by other standard library types. In the short term, you can create your own wrapper types to use as keys. For example, the following sample defines a `StringKey` type and uses it to create a dictionary that maps @@ -1018,7 +1019,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - Homogenous variadic arguments consisting of memory-only types, such as `String` are more powerful and easier to use. These arguments are projected into a - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem). + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem). (Previous releases made it easier to use variadic lists of register-passable types, like `Int`.) @@ -1048,7 +1049,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. Note that subscripting the variadic list works nicely as above, but iterating over the variadic list directly with a `for` loop produces a - [`Reference`](/mojo/stdlib/memory/unsafe#reference) (described below) instead + [`Reference`](/mojo/stdlib/memory/unsafe/reference) (described below) instead of the desired value, so an extra subscript is required; We intend to fix this in the future. @@ -1074,7 +1075,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - Mojo now has a prototype version of a safe - [`Reference`](/mojo/stdlib/memory/unsafe#reference) type. The compiler's + [`Reference`](/mojo/stdlib/memory/unsafe/reference) type. The compiler's lifetime tracking pass can reason about references to safely extend local variable lifetime, and check indirect access safety. The `Reference` type is brand new (and currently has no syntactic sugar) so it must be explicitly @@ -1193,12 +1194,12 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - The `_OldDynamicVector` type that worked only on register passable element types has been removed. Please migrate uses to - [`DynamicVector`](/mojo/stdlib/collections/list#list) which + [`DynamicVector`](/mojo/stdlib/collections/list/List) which works on both register passable and memory types. - The `UnsafeFixedVector` in `utils.vector` has been removed. We recommend using - either [`DynamicVector`](/mojo/stdlib/collections/list#list) - or [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + either [`DynamicVector`](/mojo/stdlib/collections/list/List) + or [`InlinedFixedVector`](/mojo/stdlib/collections/vector/InlinedFixedVector) instead. - The `@adaptive` decorator has been removed from the language. Any uses of the @@ -1298,8 +1299,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. [#1587](https://github.com/modularml/mojo/issues/1587), the `polynomial_evaluate` function has also been extended so that the `coefficients` parameter can take either a either a - [`StaticTuple`](/mojo/stdlib/utils/static_tuple#statictuple) or a - [`VariadicList`](/mojo/stdlib/builtin/builtin_list#variadiclist). + [`StaticTuple`](/mojo/stdlib/utils/static_tuple/statictuple) or a + [`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList). - As a tiny step towards removing `let` declarations, this release removes the warning: `'var' was never mutated, consider switching to a 'let'`. @@ -1365,20 +1366,20 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - `%cd -`: pop the directory stack and change to the last visited directory. - Structs decorated with `@value` now automatically conform to the - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - and [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) built-in traits. + [`Movable`](/mojo/stdlib/builtin/value/Movable) + and [`Copyable`](/mojo/stdlib/builtin/value/Copyable) built-in traits. -- [`String`](/mojo/stdlib/builtin/string.html#string) now has new - [`toupper()`](/mojo/stdlib/builtin/string.html#toupper) and - [`tolower()`](/mojo/stdlib/builtin/string.html#tolower) methods analogous, +- [`String`](/mojo/stdlib/builtin/string/String) now has new + [`toupper()`](/mojo/stdlib/builtin/string/String#toupper) and + [`tolower()`](/mojo/stdlib/builtin/string/String#tolower) methods analogous, respectively, to Python's `str.toupper()` and `str.tolower()`. -- Added a [`hash()`](/mojo/stdlib/builtin/hash.html#hash) built-in function and - [`Hashable`](/mojo/stdlib/builtin/hash.html#Hashable) trait for types +- Added a [`hash()`](/mojo/stdlib/builtin/hash/hash) built-in function and + [`Hashable`](/mojo/stdlib/builtin/hash/Hashable) trait for types implementing the `__hash__()` method. Future releases will add `Hashable` support to Standard Library types. In the meantime, the `hash` module includes a version of the `hash()` function that works on arbitrary byte strings. To - generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) types, you + generate hashes for [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) types, you use the internal `_hash_simd()` function: ```mojo @@ -1390,13 +1391,13 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - Several standard library types now conform to the - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - trait. These types include [`Bool`](/mojo/stdlib/builtin/bool.html#bool), - [`StringLiteral`](/mojo/stdlib/builtin/string_literal.html#stringliteral), - [`DynamicVector`](/mojo/stdlib/collections/list#list), - [`Tensor`](/mojo/stdlib/tensor/tensor.html#tensor), - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html#tensor_shape), - and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec.html#tensor_spec). + [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) + trait. These types include [`Bool`](/mojo/stdlib/builtin/bool/Bool), + [`StringLiteral`](/mojo/stdlib/builtin/string_literal/StringLiteral), + [`DynamicVector`](/mojo/stdlib/collections/list/List), + [`Tensor`](/mojo/stdlib/tensor/tensor/Tensor), + [`TensorShape`](/mojo/stdlib/tensor/tensor_shape/TensorShape), + and [`TensorSpec`](/mojo/stdlib/tensor/tensor_spec/TensorSpec). ### 🦋 Changed @@ -1520,21 +1521,21 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - We've added some traits to the standard library, you can implement these on your own types: - - [`Destructable`](/mojo/stdlib/builtin/anytype.html#anytype) - - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable) - - [`Movable`](/mojo/stdlib/builtin/value.html#movable) - - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) - - [`Intable`](/mojo/stdlib/builtin/int.html#intable) - - [`Sized`](/mojo/stdlib/builtin/len.html#sized) - - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement) - -- We added built-in [`len()`](/mojo/stdlib/builtin/len.html#len), - [`str()`](/mojo/stdlib/builtin/str.html#str), and - [`int()`](/mojo/stdlib/builtin/int.html#int-1) functions, which work with + - [`Destructable`](/mojo/stdlib/builtin/anytype/AnyType) + - [`Copyable`](/mojo/stdlib/builtin/value/Copyable) + - [`Movable`](/mojo/stdlib/builtin/value/Movable) + - [`Stringable`](/mojo/stdlib/builtin/str/Stringable) + - [`Intable`](/mojo/stdlib/builtin/int/Intable) + - [`Sized`](/mojo/stdlib/builtin/len/Sized) + - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement) + +- We added built-in [`len()`](/mojo/stdlib/builtin/len/len), + [`str()`](/mojo/stdlib/builtin/str/str), and + [`int()`](/mojo/stdlib/builtin/int/int-function) functions, which work with types that implement the `Sized`, `Stringable`, and `Intable` traits, respectively. -- [`DynamicVector`](/mojo/stdlib/collections/list#list) is now a +- [`DynamicVector`](/mojo/stdlib/collections/list/List) is now a proper generic collection that can use any type that implements the `Movable` and `Copyable` traits. This means you can now write, for example, `DynamicVector[String]`. Also, `DynamicVector` now invokes its element @@ -1659,7 +1660,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - Mojo now has the ability to read raw bytes from a file using the - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) method. + [`read_bytes()`](/mojo/stdlib/builtin/file/FileHandle#read_bytes) method. For example: ```mojo @@ -1668,10 +1669,10 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - A size argument was added to the - [`read()`](/mojo/stdlib/builtin/file.html#read) and - [`read_bytes()`](/mojo/stdlib/builtin/file.html#read_bytes) methods on the - builtin `file.FileHandle`. The size argument defaults to -1 and maintains the - previous "read to EOF" behavior when size is negative. +[`read()`](/mojo/stdlib/builtin/file/FileHandle#read) and +[`read_bytes()`](/mojo/stdlib/builtin/file/FileHandle#read_bytes) methods on +the builtin `file.FileHandle`. The size argument defaults to -1 and maintains +the previous "read to EOF" behavior when size is negative. ```mojo with open("file.binary", "r") as f: @@ -1679,7 +1680,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. data2 = f.read_bytes(256) ``` -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has `read_bytes()` and +- [`Path`](/mojo/stdlib/pathlib/path/path) now has `read_bytes()` and `read_text()` methods to read file contents from a path: ```mojo @@ -1702,8 +1703,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - Subscripting added to - [`DTypePointer`](/mojo/stdlib/memory/unsafe.html#dtypepointer) and - [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer): + [`DTypePointer`](/mojo/stdlib/memory/unsafe/dtypepointer) and + [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer): ```mojo let p = DTypePointer[DType.float16].alloc(4) @@ -1714,15 +1715,15 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - `file.FileHandle` now has a `seek()` method. -- [`String`](/mojo/stdlib/builtin/string.html#string) now has an - [`rfind()`](/mojo/stdlib/builtin/string.html#rfind) method analogous to +- [`String`](/mojo/stdlib/builtin/string/String) now has an + [`rfind()`](/mojo/stdlib/builtin/string/String#rfind) method analogous to Python's `str.rfind()`. -- `String` now has an [`split()`](/mojo/stdlib/builtin/string.html#split) method - analogous to Python's `str.split()`. +- `String` now has an [`split()`](/mojo/stdlib/builtin/string/String#split) + method analogous to Python's `str.split()`. -- [`Path`](/mojo/stdlib/pathlib/path.html#path) now has a - [`suffix()`](/mojo/stdlib/pathlib/path.html#suffix) method analogous to +- [`Path`](/mojo/stdlib/pathlib/path/path) now has a + [`suffix()`](/mojo/stdlib/pathlib/path/Path#suffix) method analogous to Python's `pathlib.Path.suffix`. - The Mojo REPL now supports indented expressions, making it a bit easier to @@ -1752,8 +1753,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### 🦋 Changed - Variadic list types - [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) + [`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem) are now iterable. Variadic arguments are automatically projected into one of these types inside the function body, so var args can be iterated: @@ -1788,7 +1789,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - The `to_string()` function has been removed from - [`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) in favor of + [`PythonObject`](/mojo/stdlib/python/object/pythonobject) in favor of the new `__str__()` function. This composes better with traits so it can be used with the generic `str()` function. @@ -1832,12 +1833,12 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### ⭐️ New -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now defaults to the +- The [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type now defaults to the architectural SIMD width of the type. This means you can write `SIMD[DType.float32]` which is equivalent to `SIMD[DType.float32, simdwidthof[DType.float32]()]`. -- The [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) type now contains a `join()` +- The [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type now contains a `join()` function that allows you to concatenate two `SIMD` values together and produce a new `SIMD` value. @@ -2022,11 +2023,11 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. - [`TensorShape`](/mojo/stdlib/tensor/tensor_shape.html) and [`TensorSpec`](/mojo/stdlib/tensor/tensor_shape.html) now have constructors - that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list#list) - and [`StaticIntTuple`](/mojo/stdlib/utils/index_.html#staticinttuple) to + that take [`DynamicVector[Int]`](/mojo/stdlib/collections/list/List) + and [`StaticIntTuple`](/mojo/stdlib/utils/index_/StaticIntTuple) to initialize shapes. -- The [`String`](/mojo/stdlib/builtin/string.html#string) type now has the +- The [`String`](/mojo/stdlib/builtin/string/String) type now has the `count()` and `find()` methods to enable counting the number of occurrences or finding the offset index of a substring in a string. @@ -2035,8 +2036,8 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ### 🦋 Changed -- [`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem) +- [`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) and + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem) moved under builtins, and no longer need to be imported. - Variadic arguments are now automatically projected into a `VariadicList` or @@ -2052,14 +2053,14 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. ``` - The parameters for - [`InlinedFixedVector`](/mojo/stdlib/collections/vector.html#inlinedfixedvector) + [`InlinedFixedVector`](/mojo/stdlib/collections/vector/InlinedFixedVector) have been switched. The parameters are now `[type, size]` instead of `[size, type]`. The `InlinedFixedVector` now has a default size which means that one can just use `InlinedFixedVector` as `InlinedFixedVector[Float32]` and the default size is used. -- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer.html#buffer) - and [`NDBuffer`](/mojo/stdlib/buffer/buffer.html#ndbuffer) is renamed to +- `write_file()` method in [`Buffer`](/mojo/stdlib/buffer/buffer/Buffer) + and [`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) is renamed to `tofile()` to match the Python naming. - Mojo will now utilize all available cores across all NUMA sockets on the host @@ -2662,7 +2663,7 @@ All earlier releases were considered version 0.1. #### ⭐️ New -- A new [`Tensor`](/mojo/stdlib/tensor/tensor#tensor) type has been introduced. +- A new [`Tensor`](/mojo/stdlib/tensor/tensor/Tensor) type has been introduced. This tensor type manages its own data (unlike `NDBuffer` and `Buffer` which are just views). Therefore, the tensor type performs its own allocation and free. Here is a simple example of using the tensor type to represent an RGB @@ -2883,7 +2884,7 @@ All earlier releases were considered version 0.1. argument type, including `DType`. - The `inf`, `neginf`, `nan`, `isinf`, `isfinite`, and `isnan` functions were - moved from the `Numerics` module to the [`Math`](/mojo/MojoStdlib/Math.html) + moved from the `Numerics` module to the [`Math`](/mojo/stdlib/math/math/) module, to better align with Python's library structure. #### 🛠️ Fixed diff --git a/docs/manual/decorators/unroll.ipynb b/docs/manual/decorators/unroll.ipynb index 55eb8ac50b..c7c6b2a1ab 100644 --- a/docs/manual/decorators/unroll.ipynb +++ b/docs/manual/decorators/unroll.ipynb @@ -111,7 +111,7 @@ "metadata": {}, "source": [ "The Mojo standard library also includes a function called\n", - "[`unroll()`](/mojo/stdlib/algorithm/functional.html#unroll) that unrolls a\n", + "[`unroll()`](/mojo/stdlib/utils/loop/unroll) that unrolls a\n", "given function that you want to call repeatedly, but has some important\n", "differences when compared to the `@unroll` decorator:\n", "\n", diff --git a/docs/manual/functions.ipynb b/docs/manual/functions.ipynb index a29f6061dc..77d965493d 100644 --- a/docs/manual/functions.ipynb +++ b/docs/manual/functions.ipynb @@ -122,7 +122,7 @@ "### The `object` type\n", "\n", "If you don't declare the type for an argument or return value in a `def`, it\n", - "becomes an [`object`](/mojo/stdlib/builtin/object.html#object), which is unlike\n", + "becomes an [`object`](/mojo/stdlib/builtin/object/object), which is unlike\n", "any other type in the standard library.\n", "\n", "The `object` type allows for dynamic typing because it can actually represent\n", @@ -307,7 +307,7 @@ "\n", "Currently variadic arguments must be a single type—all `Int`, or all `String`,\n", "for example. A few standard library APIs, such as\n", - "[`print()`](/mojo/stdlib/builtin/io.html#print), support mixed-type, or\n", + "[`print()`](/mojo/stdlib/builtin/io/print), support mixed-type, or\n", "heterogeneous, variadic arguments, but this currently requires working with\n", "undocumented MLIR APIs. We plan to support heterogeneous variadic arguments in\n", "Mojo in the future.\n", @@ -318,14 +318,14 @@ "memory-only types (such as `String`).\n", "\n", "Register-passable types, such as `Int`, are available as a \n", - "[`VariadicList`](/mojo/stdlib/builtin/builtin_list.html#variadiclist) type. As\n", + "[`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) type. As\n", "shown in the previous example, you can iterate over the values using a `for..in`\n", "loop.\n", "\n", "Memory-only types, such as `String`, are available as a \n", - "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list.html#variadiclistmem).\n", + "[`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem).\n", "Iterating over this list directly with a `for..in` loop currently produces a\n", - "[`Reference`](/mojo/stdlib/memory/unsafe#reference) for each value instead\n", + "[`Reference`](/mojo/stdlib/memory/unsafe/reference) for each value instead\n", "of the value itself. You must add an empty subscript operator `[]` to\n", "dereference the reference and retrieve the value:\n" ] diff --git a/docs/manual/lifecycle/death.ipynb b/docs/manual/lifecycle/death.ipynb index 6fea545a95..01885c9128 100644 --- a/docs/manual/lifecycle/death.ipynb +++ b/docs/manual/lifecycle/death.ipynb @@ -104,7 +104,7 @@ "\n", "The `String` value is a little more complicated. Mojo strings are mutable. The\n", "`String` object has an internal buffer—a\n", - "[`List`](/mojo/stdlib/collections/list.html#list) field,\n", + "[`List`](/mojo/stdlib/collections/list/List) field,\n", "which holds the characters that make up the string. A `List` stores\n", "its contents in dynamically allocated memory on the heap, so the string can\n", "grow or shrink. The string itself doesn't have any special destructor logic,\n", @@ -202,7 +202,7 @@ "allocate memory. \n", "\n", "Whereas, the following struct must define the `__del__()` method to free the\n", - "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe.html#pointer):" + "memory allocated for its [`Pointer`](/mojo/stdlib/memory/unsafe/Pointer):" ] }, { diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index a93f2090cd..4fb6ee2e12 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -30,9 +30,9 @@ "garbage collector.\n", "\n", "Mojo also has no built-in data types with special privileges. All data types\n", - "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool.html#bool),\n", - "[`Int`](/mojo/stdlib/builtin/int.html#int), and\n", - "[`String`](/mojo/stdlib/builtin/string.html#string)) are implemented as\n", + "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/bool),\n", + "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", + "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", "[structs](/mojo/manual/structs.html). You can actually write your own\n", "replacements for these types by using low-level primitives provided by\n", "[MLIR dialects](/mojo/notebooks/BoolMLIR.html).\n", diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 1b92ed5316..2ba0569091 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -28,11 +28,11 @@ "destroyed](/mojo/manual/lifecycle/death.html).)\n", "\n", "All data types in Mojo—including basic types in the standard library such as\n", - "[`Bool`](/mojo/stdlib/builtin/bool.html#bool),\n", - "[`Int`](/mojo/stdlib/builtin/int.html#int), and\n", - "[`String`](/mojo/stdlib/builtin/string.html#string), up to complex types such\n", - "as [`SIMD`](/mojo/stdlib/builtin/simd.html#simd) and\n", - "[`object`](/mojo/stdlib/builtin/object.html#object)—are defined as a\n", + "[`Bool`](/mojo/stdlib/builtin/bool/bool),\n", + "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", + "[`String`](/mojo/stdlib/builtin/string/String), up to complex types such\n", + "as [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) and\n", + "[`object`](/mojo/stdlib/builtin/object/object)—are defined as a\n", "[struct](/mojo/manual/structs.html). This means the creation and\n", "destruction of any piece of data follows the same lifecycle rules, and you can\n", "define your own data types that work exactly the same way.\n", diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index bafd5b35c6..f9c16a8b88 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -246,7 +246,7 @@ "### Case study: the SIMD type\n", "\n", "For a real-world example of a parameterized type, let's look at the \n", - "[`SIMD`](/mojo/stdlib/builtin/simd.html) type from Mojo's standard library.\n", + "[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type from Mojo's standard library.\n", "\n", "[Single instruction, multiple data (SIMD)](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) is a parallel processing technology built into many modern CPUs,\n", "GPUs, and custom accelerators. SIMD allows you to perform a single operation on\n", @@ -261,7 +261,7 @@ "floating point numbers, so it's not practical to define all of the possible SIMD\n", "variations. \n", "\n", - "Mojo's [`SIMD`](/mojo/stdlib/builtin/simd.html) type (defined as a struct)\n", + "Mojo's [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type (defined as a struct)\n", "exposes the common SIMD operations through its methods, and makes the SIMD data type\n", "and size values parametric. This allows you to directly map your data to the \n", "SIMD vectors on any hardware.\n", @@ -517,7 +517,7 @@ "\n", "The Mojo compiler is also smart about type inference with parameters. Note\n", "that the above function is able to call the parametric\n", - "[`sqrt[]()`](https://docs.modular.com/mojo/stdlib/math/math.html#sqrt) function\n", + "[`sqrt[]()`](https://docs.modular.com/mojo/stdlib/math/math/sqrt) function\n", "without specifying the parameters—the compiler infers its parameters based on\n", "the parametric `x` value passed into it, as if you\n", "wrote `sqrt[dt, width](x)` explicitly. Also note that `rsqrt()` chose to\n", @@ -893,7 +893,7 @@ "runtime value, we need a way to define a\n", "compile-time temporary value. For this, Mojo uses an `alias` declaration. \n", "\n", - "For example, the [`DType`](/mojo/stdlib/builtin/dtype.html#dtype) struct \n", + "For example, the [`DType`](/mojo/stdlib/builtin/dtype/DType) struct \n", "implements a simple enum using aliases for the enumerators like this (the actual\n", "`DType` implementation details vary a bit):\n", "\n", diff --git a/docs/manual/python/index.ipynb b/docs/manual/python/index.ipynb index 819274e75c..3c8dfc0095 100644 --- a/docs/manual/python/index.ipynb +++ b/docs/manual/python/index.ipynb @@ -39,7 +39,7 @@ "## Import a Python module\n", "\n", "To import a Python module in Mojo, just call \n", - "[`Python.import_module()`](/mojo/stdlib/python/python.html#import_module) \n", + "[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) \n", "with the module name:" ] }, @@ -150,7 +150,7 @@ "```\n", "\n", "Both absolute and relative paths work with \n", - "[`add_to_path()`](/mojo/stdlib/python/python.html#add_to_path). For example, you\n", + "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example, you\n", "can import from the local directory like this:\n", "\n", "```mojo\n", diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index 8c5cd8e9e4..9f62f5f077 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -100,7 +100,7 @@ "You can also use Python objects from Mojo. For example, Mojo doesn't have a\n", "standard dictionary type yet, but you can work with Python dictionaries in Mojo. \n", "To create a Python dictionary, use the \n", - "[`dict()`](/mojo/stdlib/python/python.html#dict) method:" + "[`dict()`](/mojo/stdlib/python/python/Python#dict) method:" ] }, { @@ -125,7 +125,7 @@ "### Mojo wrapper objects\n", "\n", "When you use Python objects in your Mojo code, Mojo adds the \n", - "[`PythonObject`](/mojo/stdlib/python/object.html#pythonobject) wrapper around\n", + "[`PythonObject`](/mojo/stdlib/python/object/pythonobject) wrapper around\n", "the Python object. This object exposes a number of common double underscore\n", "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n", "through to the underlying Python object. " @@ -176,7 +176,7 @@ "\n", "If you want to construct a Python type that doesn't have a literal Mojo \n", "equivalent, you can also use the \n", - "[`Python.evaluate()`](/mojo/stdlib/python/python.html#evaluate) method. For\n", + "[`Python.evaluate()`](/mojo/stdlib/python/python/Python#evaluate) method. For\n", "example, to create a Python `set`:" ] }, @@ -205,16 +205,16 @@ "explicitly convert a Python value into a native Mojo value. \n", "\n", "Currently `PythonObject` conforms to the \n", - "[`Intable`](/mojo/stdlib/builtin/int.html#intable) and \n", - "[`Stringable`](/mojo/stdlib/builtin/str.html#stringable) traits, which means you\n", + "[`Intable`](/mojo/stdlib/builtin/int/Intable) and \n", + "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) traits, which means you\n", "can convert Python values to Mojo `Int` and `String` types using the built-in \n", - "[`int()`](/mojo/stdlib/builtin/int.html#int-1) and\n", - "[`str()`](/mojo/stdlib/builtin/str.html#str) functions, and print Python values\n", - "using the built-in [`print()`](/mojo/stdlib/builtin/io.html#print) function.\n", + "[`int()`](/mojo/stdlib/builtin/int/int-function) and\n", + "[`str()`](/mojo/stdlib/builtin/str/str) functions, and print Python values\n", + "using the built-in [`print()`](/mojo/stdlib/builtin/io/print) function.\n", " \n", "`PythonObject` also provides the\n", - "[`__bool__()`](/mojo/stdlib/python/object.html#bool__) and \n", - "[`to_float64()`](/mojo/stdlib/python/object.html#to_float64) methods for \n", + "[`__bool__()`](/mojo/stdlib/python/object/PythonObject#__bool__) and \n", + "[`to_float64()`](/mojo/stdlib/python/object/PythonObject#to_float64) methods for \n", "converting to boolean and floating point values, respectively.\n", "\n", "```mojo\n", @@ -240,10 +240,10 @@ "like `False` and `None` evaluate as false in Mojo, too.\n", "\n", "If you need to know the type of the underlying Python object, you can use the \n", - "[`Python.type()`](/mojo/stdlib/python/python.html#type) method, which is \n", + "[`Python.type()`](/mojo/stdlib/python/python/PythonObject#type) method, which is \n", "equivalent to the Python `type()` builtin. You can compare the identity of two\n", "Python objects using the\n", - "[`Python.is_type()`](/mojo/stdlib/python/python.html#is_type) method (which is\n", + "[`Python.is_type()`](/mojo/stdlib/python/python/PythonObject#is_type) method (which is\n", "equivalent to the Python `is` operator):" ] }, diff --git a/docs/manual/structs.ipynb b/docs/manual/structs.ipynb index ace0fe9bfc..18001717b5 100644 --- a/docs/manual/structs.ipynb +++ b/docs/manual/structs.ipynb @@ -346,7 +346,7 @@ "- Operator overloading: A lot of special methods are designed to overload\n", " operators such as `<` (less-than), `+` (add), and `|` (or) so they work\n", " appropriately with each type. For example, look at the methods listed for Mojo's\n", - " [`Int` type](/mojo/stdlib/builtin/int#int). One such method is `__lt__()`, which\n", + " [`Int` type](/mojo/stdlib/builtin/int/Int). One such method is `__lt__()`, which\n", " Mojo calls to perform a less-than comparison between two integers (for example,\n", " `num1 < num2`).\n", "\n", diff --git a/docs/manual/traits.ipynb b/docs/manual/traits.ipynb index 40fdb69ba3..1758df3e34 100644 --- a/docs/manual/traits.ipynb +++ b/docs/manual/traits.ipynb @@ -378,7 +378,7 @@ "\n", "For example, the following code creates a `MassProducible` trait. A \n", "`MassProducible` type has a default (no-argument) constructor and can be moved.\n", - "It uses the built-in [`Movable`](/mojo/stdlib/builtin/value.html#movable) trait,\n", + "It uses the built-in [`Movable`](/mojo/stdlib/builtin/value/Movable) trait,\n", "which requires the type to have a [move \n", "constructor](/mojo/manual/lifecycle/life.html#move-constructor).\n", "\n", @@ -443,27 +443,27 @@ "by a number of standard library types, and you can also implement these on your\n", "own types:\n", "\n", - " - [`AnyType`](/mojo/stdlib/builtin/anytype.html#anytype)\n", - " - [`Boolable`](/mojo/stdlib/builtin/bool#boolable)\n", - " - [`CollectionElement`](/mojo/stdlib/builtin/value#collectionelement)\n", - " - [`Copyable`](/mojo/stdlib/builtin/value.html#copyable)\n", - " - [`Intable`](/mojo/stdlib/builtin/int.html#intable)\n", - " - [`KeyElement`](/mojo/stdlib/collections/dict#keyelement)\n", - " - [`Movable`](/mojo/stdlib/builtin/value.html#movable)\n", - " - [`PathLike`](/mojo/stdlib/os/pathlike#pathlike)\n", - " - [`Sized`](/mojo/stdlib/builtin/len.html#sized)\n", - " - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable)\n", + " - [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType)\n", + " - [`Boolable`](/mojo/stdlib/builtin/bool/Boolable)\n", + " - [`CollectionElement`](/mojo/stdlib/builtin/value/CollectionElement)\n", + " - [`Copyable`](/mojo/stdlib/builtin/value/Copyable)\n", + " - [`Intable`](/mojo/stdlib/builtin/int/Intable)\n", + " - [`KeyElement`](/mojo/stdlib/collections/dict/KeyElement)\n", + " - [`Movable`](/mojo/stdlib/builtin/value/Movable)\n", + " - [`PathLike`](/mojo/stdlib/os/pathlike/PathLike)\n", + " - [`Sized`](/mojo/stdlib/builtin/len/Sized)\n", + " - [`Stringable`](/mojo/stdlib/builtin/str/Stringable)\n", "\n", "The API reference docs linked above include usage examples for each trait. The\n", "following sections discuss a few of these traits.\n", "\n", "### The `Sized` trait\n", "\n", - "The [`Sized`](/mojo/stdlib/builtin/len.html#sized) trait identifies types that\n", + "The [`Sized`](/mojo/stdlib/builtin/len/Sized) trait identifies types that\n", "have a measurable length, like strings and arrays. \n", "\n", "Specifically, `Sized` requires a type to implement the `__len__()` method. \n", - "This trait is used by the built-in [`len()`](/mojo/stdlib/builtin/len.html#len) \n", + "This trait is used by the built-in [`len()`](/mojo/stdlib/builtin/len/len) \n", "function. For example, if you're writing a custom list type, you could \n", "implement this trait so your type works with `len()`:" ] @@ -501,13 +501,13 @@ "source": [ "### The `Intable` and `Stringable` traits\n", "\n", - "The [`Intable`](/mojo/stdlib/builtin/int.html#intable) and \n", - "[`Stringable`](/mojo/stdlib/builtin/str.html#stringable) traits identify types\n", + "The [`Intable`](/mojo/stdlib/builtin/int/Intable) and \n", + "[`Stringable`](/mojo/stdlib/builtin/str/Stringable) traits identify types\n", "that can be implicitly converted to `Int` and `String`, respectively. \n", "\n", "Any type that conforms to `Stringable` works with the built-in\n", - "[`print()`](/mojo/stdlib/builtin/io.html#print) and \n", - "[`str()`](/mojo/stdlib/builtin/str.html#str) functions:" + "[`print()`](/mojo/stdlib/builtin/io/print) and \n", + "[`str()`](/mojo/stdlib/builtin/str/str) functions:" ] }, { @@ -541,8 +541,8 @@ "metadata": {}, "source": [ "Similarly, an `Intable` type works with the built-in \n", - "[`int`](/mojo/stdlib/builtin/int.html#int-1) function. You can find an example\n", - "in the [`Intable` API reference](/mojo/stdlib/builtin/int.html#intable)." + "[`int`](/mojo/stdlib/builtin/int/int-function) function. You can find an example\n", + "in the [`Intable` API reference](/mojo/stdlib/builtin/int/Intable)." ] }, { @@ -560,7 +560,7 @@ "that must be called to free the allocated memory. But not all types have a \n", "destructor, and your Mojo code has no way to determine which is which.\n", "\n", - "The [`AnyType`](/mojo/stdlib/builtin/anytype#anytype) trait solves this\n", + "The [`AnyType`](/mojo/stdlib/builtin/anytype/AnyType) trait solves this\n", "issue: every trait implicitly inherits from `AnyType`, and all structs conform\n", "to `AnyType`, which guarantees that the type has a destructor. For types that \n", "don't have one, Mojo adds a no-op destructor. This means you can call the \n", diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index bb02f2c660..a544155c03 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -478,7 +478,7 @@ "function is essentially just sugaring for the `fn` function:\n", "\n", "- A `def` argument without a type annotation defaults to\n", - " [`object`](/mojo/stdlib/builtin/object#object) type (whereas as `fn`\n", + " [`object`](/mojo/stdlib/builtin/object/object) type (whereas as `fn`\n", " requires all types be explicitly declared).\n", "\n", "- A `def` argument without a convention keyword (`borrowed`, `inout`, or\n", diff --git a/docs/manual/values/value-semantics.ipynb b/docs/manual/values/value-semantics.ipynb index a822a75587..1f08eceaaa 100644 --- a/docs/manual/values/value-semantics.ipynb +++ b/docs/manual/values/value-semantics.ipynb @@ -277,7 +277,7 @@ "If you will always use strict type declarations, you\n", "can skip this section because it only applies to Mojo code using `def`\n", "functions without type declarations (or values declared as\n", - "[`object`](/mojo/stdlib/builtin/object.html#object)).\n", + "[`object`](/mojo/stdlib/builtin/object/object)).\n", "\n", ":::\n", "\n", @@ -384,7 +384,7 @@ "metadata": {}, "source": [ "Although we haven't finished implementing the\n", - "[`object`](/mojo/stdlib/builtin/object.html#object) type to represent any Mojo\n", + "[`object`](/mojo/stdlib/builtin/object/object) type to represent any Mojo\n", "type, our intention is to do so, and enable \"pass by object reference\" as\n", "described above for all dynamic types in a `def` function.\n", "\n", diff --git a/docs/manual/variables.ipynb b/docs/manual/variables.ipynb index 197ca22a4a..9e1f5c633d 100644 --- a/docs/manual/variables.ipynb +++ b/docs/manual/variables.ipynb @@ -280,7 +280,7 @@ "As shown above, value assignment can be converted into a constructor call if the \n", "target type has a constructor that takes a single argument that matches the\n", "value being assigned. So, this code uses the \n", - "[`String`](/mojo/stdlib/builtin/string.html#string) constructor that takes an\n", + "[`String`](/mojo/stdlib/builtin/string/String) constructor that takes an\n", "integer: `__init__(inout self, num: Int)`.\n", "\n", "Implicit conversion follows the logic of [overloaded\n", diff --git a/docs/roadmap.md b/docs/roadmap.md index 34b4eaeccb..a3e026e0bb 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -544,10 +544,10 @@ print(One()) # prints '1' ``` Mojo currently supports this feature through the -[`Stringable`](/mojo/stdlib/builtin/str.html#stringable) trait, so that +[`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait, so that `print()` works on all `Stringable` types. Similar support exists for the -[`int()`](/mojo/stdlib/builtin/int.html#int-1) and -[`len()`](/mojo/stdlib/builtin/len.html#len) functions. We'll continue to +[`int()`](/mojo/stdlib/builtin/int/int-function) and +[`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. ### Lifetime tracking inside collections diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index a7cd64ac3c..9a82c8cba5 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -64,7 +64,7 @@ vectorize[$3, $1, $2] ### `DynamicVector` constructor `capacity` now keyword-only -The [`DynamicVector`](/mojo/stdlib/collections/list#list) struct had +The [`DynamicVector`](/mojo/stdlib/collections/list/List) struct had a constructor that took a single integer value for the vector's capacity. This had the effect of allowing an implicit conversion from `Int` to `DynamicVector`. This was not intended to support implicit conversion, so `capacity` is now a @@ -82,7 +82,7 @@ Which in VS Code looks like this: ### `NDBuffer` signature change The shape of an -[`NDBuffer`](/mojo/stdlib/buffer/buffer#ndbuffer) can +[`NDBuffer`](/mojo/stdlib/buffer/buffer/NDBuffer) can now default to being unknown, so the parameter list has been rearranged to accommodate this: @@ -113,7 +113,7 @@ NDBuffer[$3, $1, $2] ### Dereference `Variant` with `[]` -Previously, using [`Variant`](/mojo/stdlib/utils/variant#variant) +Previously, using [`Variant`](/mojo/stdlib/utils/variant/Variant) was unsafe with heap allocated objects, it now returns a reference. If you had code that looks like this: diff --git a/stdlib/src/builtin/hash.mojo b/stdlib/src/builtin/hash.mojo index 90264175bd..3bd44c749f 100644 --- a/stdlib/src/builtin/hash.mojo +++ b/stdlib/src/builtin/hash.mojo @@ -226,7 +226,7 @@ fn hash(bytes: DTypePointer[DType.int8], n: Int) -> Int: - Interpret those bytes as a SIMD vector. - Apply a vectorized hash: _v_ = 33 * _v_ + _bytes_as_simd_value_ - - Call [`reduce_add()`](/mojo/stdlib/builtin/simd.html#reduce_add) on the + - Call [`reduce_add()`](/mojo/stdlib/builtin/simd/SIMD#reduce_add) on the final result to get a single hash value. - Use this value in fallback for the remaining suffix bytes with standard DJBX33A. diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index ce8859e921..349041aa5e 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -32,8 +32,8 @@ trait Intable: """The `Intable` trait describes a type that can be converted to an Int. Any type that conforms to `Intable` or - [`IntableRaising`](/mojo/stdlib/builtin/int.html#intableraising) works with - the built-in [`int()`](/mojo/stdlib/builtin/int.html#int-1) function. + [`IntableRaising`](/mojo/stdlib/builtin/int/intableraising) works with + the built-in [`int()`](/mojo/stdlib/builtin/int/int-function) function. This trait requires the type to implement the `__int__()` method. For example: @@ -60,7 +60,7 @@ trait Intable: ``` **Note:** If the `__int__()` method can raise an error, use the - [`IntableRaising`](/mojo/stdlib/builtin/int.html#intableraising) trait + [`IntableRaising`](/mojo/stdlib/builtin/int/intableraising) trait instead. """ @@ -78,9 +78,9 @@ trait IntableRaising: The `IntableRaising` trait describes a type can be converted to an Int, but the conversion might raise an error. - Any type that conforms to [`Intable`](/mojo/stdlib/builtin/int.html#intable) + Any type that conforms to [`Intable`](/mojo/stdlib/builtin/int/intable) or `IntableRaising` works with the built-in - [`int()`](/mojo/stdlib/builtin/int.html#int-1) function. + [`int()`](/mojo/stdlib/builtin/int/int-function) function. This trait requires the type to implement the `__int__()` method, which can raise an error. For example: diff --git a/stdlib/src/builtin/len.mojo b/stdlib/src/builtin/len.mojo index 5420358143..887c907c13 100644 --- a/stdlib/src/builtin/len.mojo +++ b/stdlib/src/builtin/len.mojo @@ -25,8 +25,8 @@ trait Sized: string or array). Any type that conforms to `Sized` or - [`SizedRaising`](/mojo/stdlib/builtin/len.html#sizedraising) works with the - built-in [`len()`](/mojo/stdlib/builtin/len.html#len) function. + [`SizedRaising`](/mojo/stdlib/builtin/len/sizedraising) works with the + built-in [`len()`](/mojo/stdlib/builtin/len/len) function. The `Sized` trait requires a type to implement the `__len__()` method. For example: @@ -53,7 +53,7 @@ trait Sized: ``` **Note:** If the `__len__()` method can raise an error, use the - [`SizedRaising`](/mojo/stdlib/builtin/len.html#sizedraising) trait instead. + [`SizedRaising`](/mojo/stdlib/builtin/len/sizedraising) trait instead. """ @@ -70,9 +70,9 @@ trait SizedRaising: """The `SizedRaising` trait describes a type that has an integer length, which might raise an error if the length can't be determined. - Any type that conforms to [`Sized`](/mojo/stdlib/builtin/len.html#sized) or + Any type that conforms to [`Sized`](/mojo/stdlib/builtin/len/sized) or `SizedRaising` works with the built-in - [`len()`](/mojo/stdlib/builtin/len.html#len) function. + [`len()`](/mojo/stdlib/builtin/len/len) function. The `SizedRaising` trait requires a type to implement the `__len__()` method, which can raise an error. For example: diff --git a/stdlib/src/builtin/str.mojo b/stdlib/src/builtin/str.mojo index 17126897af..cbc8584994 100644 --- a/stdlib/src/builtin/str.mojo +++ b/stdlib/src/builtin/str.mojo @@ -26,8 +26,8 @@ trait Stringable: [`String`](https://docs.modular.com/mojo/stdlib/builtin/string.html). Any type that conforms to `Stringable` or - [`StringableRaising`](/mojo/stdlib/builtin/str.html#stringableraising) works - with the built-in [`print()`](/mojo/stdlib/builtin/io.html#print) and + [`StringableRaising`](/mojo/stdlib/builtin/str/stringableraising) works + with the built-in [`print()`](/mojo/stdlib/builtin/io/print) and [`str()`](/mojo/stdlib/builtin/str.html) functions. The `Stringable` trait requires the type to define the `__str__()` method. @@ -55,7 +55,7 @@ trait Stringable: ``` **Note:** If the `__str__()` method might raise an error, use the - [`StringableRaising`](/mojo/stdlib/builtin/str.html#stringableraising) + [`StringableRaising`](/mojo/stdlib/builtin/str/stringableraising) trait, instead. """ @@ -73,10 +73,10 @@ trait StringableRaising: [`String`](https://docs.modular.com/mojo/stdlib/builtin/string.html). Any type that conforms to - [`Stringable`](/mojo/stdlib/builtin/str.html#stringable) or + [`Stringable`](/mojo/stdlib/builtin/str/stringable) or `StringableRaising` works with the built-in - [`print()`](/mojo/stdlib/builtin/io.html#print) and - [`str()`](/mojo/stdlib/builtin/str.html#str) functions. + [`print()`](/mojo/stdlib/builtin/io/print) and + [`str()`](/mojo/stdlib/builtin/str/str) functions. The `StringableRaising` trait requires the type to define the `__str__()` method, which can raise an error. For example: From 807e9996376f733577d82c37b73e18c9ccc89ced Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 12 Apr 2024 15:50:51 -0700 Subject: [PATCH 0143/2019] [docs] more link fixes (#37618) missed a few; these capitalizations aren't required, they will quickly redirect, but the linkchecker flags them and they cause a flicker during redirect Still more broken due to refactor that I don't know how to handle yet; best to wait till near release. MODULAR_ORIG_COMMIT_REV_ID: 9bf9094253e6dd69e7dc7ba7e816babbe1f6db4d --- docs/changelog-released.md | 12 ++++++------ docs/manual/lifecycle/index.ipynb | 2 +- docs/manual/python/types.ipynb | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/changelog-released.md b/docs/changelog-released.md index 215bfadf6e..f423cad6d3 100644 --- a/docs/changelog-released.md +++ b/docs/changelog-released.md @@ -1299,7 +1299,7 @@ installation issues. Otherwise it is functionally identical to Mojo 24.1. [#1587](https://github.com/modularml/mojo/issues/1587), the `polynomial_evaluate` function has also been extended so that the `coefficients` parameter can take either a either a - [`StaticTuple`](/mojo/stdlib/utils/static_tuple/statictuple) or a + [`StaticTuple`](/mojo/stdlib/utils/static_tuple/StaticTuple) or a [`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList). - As a tiny step towards removing `let` declarations, this release removes the @@ -1680,7 +1680,7 @@ the previous "read to EOF" behavior when size is negative. data2 = f.read_bytes(256) ``` -- [`Path`](/mojo/stdlib/pathlib/path/path) now has `read_bytes()` and +- [`Path`](/mojo/stdlib/pathlib/path/Path) now has `read_bytes()` and `read_text()` methods to read file contents from a path: ```mojo @@ -1722,7 +1722,7 @@ the previous "read to EOF" behavior when size is negative. - `String` now has an [`split()`](/mojo/stdlib/builtin/string/String#split) method analogous to Python's `str.split()`. -- [`Path`](/mojo/stdlib/pathlib/path/path) now has a +- [`Path`](/mojo/stdlib/pathlib/path/Path) now has a [`suffix()`](/mojo/stdlib/pathlib/path/Path#suffix) method analogous to Python's `pathlib.Path.suffix`. @@ -1754,7 +1754,7 @@ the previous "read to EOF" behavior when size is negative. - Variadic list types [`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem) + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem) are now iterable. Variadic arguments are automatically projected into one of these types inside the function body, so var args can be iterated: @@ -1789,7 +1789,7 @@ the previous "read to EOF" behavior when size is negative. ``` - The `to_string()` function has been removed from - [`PythonObject`](/mojo/stdlib/python/object/pythonobject) in favor of + [`PythonObject`](/mojo/stdlib/python/object/PythonObject) in favor of the new `__str__()` function. This composes better with traits so it can be used with the generic `str()` function. @@ -2037,7 +2037,7 @@ the previous "read to EOF" behavior when size is negative. ### 🦋 Changed - [`VariadicList`](/mojo/stdlib/builtin/builtin_list/VariadicList) and - [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListmem) + [`VariadicListMem`](/mojo/stdlib/builtin/builtin_list/VariadicListMem) moved under builtins, and no longer need to be imported. - Variadic arguments are now automatically projected into a `VariadicList` or diff --git a/docs/manual/lifecycle/index.ipynb b/docs/manual/lifecycle/index.ipynb index 4fb6ee2e12..661262d9f1 100644 --- a/docs/manual/lifecycle/index.ipynb +++ b/docs/manual/lifecycle/index.ipynb @@ -30,7 +30,7 @@ "garbage collector.\n", "\n", "Mojo also has no built-in data types with special privileges. All data types\n", - "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/bool),\n", + "in the standard library (such as [`Bool`](/mojo/stdlib/builtin/bool/Bool),\n", "[`Int`](/mojo/stdlib/builtin/int/Int), and\n", "[`String`](/mojo/stdlib/builtin/string/String)) are implemented as\n", "[structs](/mojo/manual/structs.html). You can actually write your own\n", diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index 9f62f5f077..23612ebe59 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -125,7 +125,7 @@ "### Mojo wrapper objects\n", "\n", "When you use Python objects in your Mojo code, Mojo adds the \n", - "[`PythonObject`](/mojo/stdlib/python/object/pythonobject) wrapper around\n", + "[`PythonObject`](/mojo/stdlib/python/object/PythonObject) wrapper around\n", "the Python object. This object exposes a number of common double underscore\n", "methods (dunder methods) like `__getitem__()` and `__getattr__()`, passing them\n", "through to the underlying Python object. " @@ -240,10 +240,10 @@ "like `False` and `None` evaluate as false in Mojo, too.\n", "\n", "If you need to know the type of the underlying Python object, you can use the \n", - "[`Python.type()`](/mojo/stdlib/python/python/PythonObject#type) method, which is \n", + "[`Python.type()`](/mojo/stdlib/python/python/Python#type) method, which is \n", "equivalent to the Python `type()` builtin. You can compare the identity of two\n", "Python objects using the\n", - "[`Python.is_type()`](/mojo/stdlib/python/python/PythonObject#is_type) method (which is\n", + "[`Python.is_type()`](/mojo/stdlib/python/python/Python#is_type) method (which is\n", "equivalent to the Python `is` operator):" ] }, From e768b13a89de5bab69f338f28868d2cb70a2036d Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Fri, 12 Apr 2024 19:01:47 -0400 Subject: [PATCH 0144/2019] [mojo-lang] Ensure `@value` fails gracefully if duplicate field names are present (#37063) When duplicate field names are present, we throw an error, but don't stop processing the `@value` decorator (or the rest of the struct body). This patch ensures that decorator resolution checks if the struct fields are resolved successfully, and terminates otherwise (since decorators will eventually have to assume that the body of the struct has been resolved successfully). Closes https://github.com/modularml/mojo/issues/1675. MODULAR_ORIG_COMMIT_REV_ID: 65d677597b98a63941845f877a9fc1d40c8b6c49 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7682b644dd..adf4d43934 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -188,3 +188,6 @@ what we publish. - [#2007](https://github.com/modularml/mojo/issues/2007) and [#1997](https://github.com/modularml/mojo/issues/1997) The Mojo LSP no longer crashes on certain types of closures. + +- [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` + decorator fails gracefully after duplicate field error. From 27c4fb8623178823e10142a12cfdf65f77552cdf Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 12 Apr 2024 17:33:29 -0700 Subject: [PATCH 0145/2019] [stdlib] converting `get_unsafe_pointer` to `get_legacy_pointer` (#37594) To avoid confusion, this changes callsites that fetch a `Pointer` or `LegacyPointer` from an accessor function to use the method consistent with the type they are retrieving. MODULAR_ORIG_COMMIT_REV_ID: 2c8afc23ee8af5213fa4afa0c8be9df190959cce --- stdlib/src/builtin/object.mojo | 10 +++++----- stdlib/src/builtin/tuple.mojo | 2 +- stdlib/src/collections/vector.mojo | 2 +- stdlib/src/memory/reference.mojo | 11 ++++++++++- stdlib/src/sys/ffi.mojo | 6 +++--- stdlib/src/utils/inlined_string.mojo | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index ce1c4399f4..3201fad3da 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -231,7 +231,7 @@ struct _Function: fn __init__[FnT: AnyRegType](value: FnT) -> Self: # FIXME: No "pointer bitcast" for signature function pointers. var f = Pointer[Int16]() - Reference(f).get_unsafe_pointer().bitcast[FnT]().store(value) + Reference(f).get_legacy_pointer().bitcast[FnT]().store(value) return Self {value: f} alias fn0 = fn () raises -> object @@ -247,7 +247,7 @@ struct _Function: fn invoke(owned self) raises -> object: return ( Reference(self.value) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[Self.fn0]() .load()() ) @@ -256,7 +256,7 @@ struct _Function: fn invoke(owned self, arg0: object) raises -> object: return ( Reference(self.value) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[Self.fn1]() .load()(arg0) ) @@ -265,7 +265,7 @@ struct _Function: fn invoke(owned self, arg0: object, arg1: object) raises -> object: return ( Reference(self.value) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[Self.fn2]() .load()(arg0, arg1) ) @@ -276,7 +276,7 @@ struct _Function: ) raises -> object: return ( Reference(self.value) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[Self.fn3]() .load()(arg0, arg1, arg2) ) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index c31cd613e2..ca6b1e7a66 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -158,7 +158,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # mutability of self. var storage_kgen_ptr = Reference( Reference(self_lit)[].storage - ).get_unsafe_pointer().address + ).get_legacy_pointer().address # Pointer to the element. var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index c97ec435b4..2f01e7f4a6 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -243,5 +243,5 @@ struct InlinedFixedVector[ An iterator to the start of the vector. """ return Self._iterator( - 0, self.current_size, Reference(self).get_unsafe_pointer() + 0, self.current_size, Reference(self).get_legacy_pointer() ) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index a126eb8973..563e12f453 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -263,7 +263,7 @@ struct Reference[ # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType # disagreement. Use UnsafePointer instead! @always_inline("nodebug") - fn get_unsafe_pointer(self) -> Pointer[type, address_space]: + fn get_legacy_pointer(self) -> Pointer[type, address_space]: """Constructs a Pointer from a safe reference. Returns: @@ -274,6 +274,15 @@ struct Reference[ _type = Pointer[type, address_space].pointer_type ](UnsafePointer(self).value) + @always_inline("nodebug") + fn get_unsafe_pointer(self) -> UnsafePointer[type, address_space]: + """Constructs a UnsafePointer from a safe reference. + + Returns: + Constructed UnsafePointer object. + """ + return UnsafePointer(self).value + @always_inline("nodebug") fn bitcast_element[ new_element_type: AnyType diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 9a76b96091..171b010750 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -127,7 +127,7 @@ struct DLHandle(CollectionElement): ](self.handle.address, name) return ( Reference(opaque_function_ptr) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[result_type]() .load() ) @@ -200,7 +200,7 @@ fn _get_dylib_function[ if func_ptr: return ( Reference(func_ptr) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[result_type]() .load() ) @@ -210,7 +210,7 @@ fn _get_dylib_function[ external_call["KGEN_CompilerRT_InsertGlobal", NoneType]( StringRef(func_cache_name), Reference(new_func) - .get_unsafe_pointer() + .get_legacy_pointer() .bitcast[Pointer[NoneType]]() .load(), ) diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index 85a39942ed..f6d63184d5 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -440,7 +440,7 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): var base_ptr = Reference( self.storage.array - ).get_unsafe_pointer().address + ).get_legacy_pointer().address # var base_ptr = Pointer[ElementType].address_of(self.array).address # TODO: Is the `gep` here necessary, or could this be a bitcast? From 007bad3c4f6d5bfb9c20c3b7f497e92a6cef1d6d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 17:37:44 -0700 Subject: [PATCH 0146/2019] [mojo-lang] Teach parameter inference about implicit conversions. (#37625) A long-standing weakness of our parameter inference system is that it doesn't infer parameters when implicit conversions are required to line things up. This rectifies this, unblocking the address_of changes that depend on it. Note: I find it deeply unsatisfying that parameter inference has to duplicate all the call resolution and value conversion logic for parameters. This whole thing needs a rethink! MODULAR_ORIG_COMMIT_REV_ID: 299af9b663cc6e8aaae31eb11b73fd526557b8a0 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index adf4d43934..175e3f8a93 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -164,6 +164,9 @@ what we publish. longer cuts off hover previews for functions with functional arguments, parameters, or results. +- [#1245](https://github.com/modularml/mojo/issues/1245) [Feature Request] + Parameter Inference from Other Parameters. + - [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and documentation generation handling of inout arguments. From 1d46e8bd3a8752ff4b8abb17d8b5202a4fbc4548 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 17:47:03 -0700 Subject: [PATCH 0147/2019] [mojo-stdlib] Remove extraneous `Pointer.__init__` (#37627) It isn't conventional or needed to define identity copy ctors. Remove it. MODULAR_ORIG_COMMIT_REV_ID: 8b29260a17d22a1309bb53bf48407d95986b9d91 --- stdlib/src/memory/unsafe.mojo | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 9c1312c935..8902c4b19f 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -267,19 +267,6 @@ struct LegacyPointer[ """ return Self.get_null() - # FIXME: Why do we have an identity constructor? - @always_inline("nodebug") - fn __init__(address: Self) -> Self: - """Constructs a LegacyPointer from the address. - - Args: - address: The input pointer. - - Returns: - Constructed LegacyPointer object. - """ - return address - @always_inline("nodebug") fn __init__(address: Self.pointer_type) -> Self: """Constructs a LegacyPointer from the address. From a1503476d84b310d8ca5733c7c75fa835e5ffaaa Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 12 Apr 2024 17:51:27 -0700 Subject: [PATCH 0148/2019] [stdlib] Converting _macos.mojo to UnsafePointer (#37628) Converting _macos.mojo usages of Pointer to UnsafePointer. MODULAR_ORIG_COMMIT_REV_ID: f0e89b44833fac270de1fca5573bef97cf37e015 --- stdlib/src/os/_macos.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 374a79d904..14435036da 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -115,7 +115,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["stat", Int32]( - path._as_ptr(), Pointer.address_of(stat) + path._as_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -126,7 +126,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["lstat", Int32]( - path._as_ptr(), Pointer.address_of(stat) + path._as_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" From baec0036fc0c1473e992e53589e24a0e0cc6ef63 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 20:38:40 -0700 Subject: [PATCH 0149/2019] [mojo-stdlib] Fix `address_of` member on pointer-like things. (#37629) It isn't safe to take the address of an inout/borrowed argument that might expand to a register type, because those arguments will eventually be passed as a register (which doesn't have an address!) when IR generation is done. One of the biggest violators of this has been the `Pointer.address_of` sorts of methods. Fix them to take a `Reference` instead of taking the address of an inout argument, which is also nice because they now don't require the target to be mutable (e.g. they'll work with borrowed memory arguments). This requires propagating the reference out through similar inout arguments and fixing some type inference holes. This fixes #32654 and eliminates some last uses of `__get_lvalue_as_address`, which didn't do the proper checking for this problem. MODULAR_ORIG_COMMIT_REV_ID: e81cf8dd2e48bcd74bc6e86923fa8b0b5d536009 --- stdlib/src/memory/unsafe.mojo | 10 ++++----- stdlib/src/memory/unsafe_pointer.mojo | 6 ++---- stdlib/src/utils/static_tuple.mojo | 31 +++++++++++---------------- stdlib/test/utils/test_tuple.mojo | 19 ---------------- 4 files changed, 19 insertions(+), 47 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 8902c4b19f..5790e0b10d 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -338,7 +338,7 @@ struct LegacyPointer[ @staticmethod @always_inline("nodebug") - fn address_of(inout arg: type) -> Self: + fn address_of(arg: Reference[type, _, _, address_space]) -> Self: """Gets the address of the argument. Args: @@ -347,9 +347,7 @@ struct LegacyPointer[ Returns: A LegacyPointer struct which contains the address of the argument. """ - return __mlir_op.`pop.pointer.bitcast`[_type = Self.pointer_type]( - __get_lvalue_as_address(arg) - ) + return arg.get_legacy_pointer() @always_inline("nodebug") fn __getitem__[T: Intable](self, offset: T) -> type: @@ -776,7 +774,7 @@ struct DTypePointer[ @staticmethod @always_inline("nodebug") - fn address_of(inout arg: Scalar[type]) -> Self: + fn address_of(arg: Reference[Scalar[type], _, _, address_space]) -> Self: """Gets the address of the argument. Args: @@ -785,7 +783,7 @@ struct DTypePointer[ Returns: A DTypePointer struct which contains the address of the argument. """ - return Self.pointer_type.address_of(arg) + return arg.get_legacy_pointer() @always_inline("nodebug") fn __getitem__[T: Intable](self, offset: T) -> Scalar[type]: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index bb0000f083..0096cea47c 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -129,7 +129,7 @@ struct UnsafePointer[ @staticmethod @always_inline("nodebug") - fn address_of(inout arg: T) -> Self: + fn address_of(arg: Reference[T, _, _, address_space]) -> Self: """Gets the address of the argument. Args: @@ -138,9 +138,7 @@ struct UnsafePointer[ Returns: An UnsafePointer which contains the address of the argument. """ - return __mlir_op.`pop.pointer.bitcast`[_type = Self.pointer_type]( - __get_lvalue_as_address(arg) - ) + return Self(arg) @always_inline fn free(self): diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index c82b9a1b8b..783c696fdf 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -35,7 +35,11 @@ fn _set_array_elem[ type: AnyRegType, ]( val: type, - inout array: __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], + array: Reference[ + __mlir_type[`!pop.array<`, size.value, `, `, type, `>`], + __mlir_attr.`1 : i1`, + _, + ], ): """Sets the array element at position `index` with the value `val`. @@ -49,9 +53,9 @@ fn _set_array_elem[ array: the array which is captured by reference. """ var ptr = __mlir_op.`pop.array.gep`( - Pointer.address_of(array).address, index.value + array.get_legacy_pointer().address, index.value ) - __mlir_op.`pop.store`(val, ptr) + Pointer(ptr).store(val) @always_inline @@ -188,7 +192,9 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): val: The value to store. """ constrained[index < size]() - _set_array_elem[index, size, Self.element_type](val, self.array) + var tmp = self + _set_array_elem[index, size, Self.element_type](val, tmp.array) + self = tmp @always_inline("nodebug") fn __getitem__[intable: Intable](self, index: intable) -> Self.element_type: @@ -207,7 +213,6 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): debug_assert(offset < size, "index must be within bounds") # Copy the array so we can get its address, because we can't take the # address of 'self' in a non-mutating method. - # TODO(Ownership): we should be able to get const references. var arrayCopy = self.array var ptr = __mlir_op.`pop.array.gep`( Pointer.address_of(arrayCopy).address, offset.value @@ -229,19 +234,9 @@ struct StaticTuple[element_type: AnyRegType, size: Int](Sized): """ var offset = int(index) debug_assert(offset < size, "index must be within bounds") + var tmp = self var ptr = __mlir_op.`pop.array.gep`( - Pointer.address_of(self.array).address, offset.value + Pointer.address_of(tmp.array).address, offset.value ) Pointer(ptr).store(val) - - @always_inline("nodebug") - fn as_ptr(inout self) -> Pointer[Self.element_type]: - """Get a mutable pointer to the elements contained by this tuple. - - Returns: - A pointer to the elements contained by this tuple. - """ - - var base_ptr = Pointer.address_of(self.array) - var ptr = __mlir_op.`pop.array.gep`(base_ptr.address, Int(0).value) - return Pointer(ptr) + self = tmp diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index c383d1fb10..6e847a4acc 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -81,26 +81,7 @@ def test_tuple_literal(): assert_equal(len(()), 0) -def test_tuple_pointer(): - var tup = StaticTuple[Int, 3](0, 0, 0) - - assert_equal(tup[0], 0) - assert_equal(tup[1], 0) - assert_equal(tup[2], 0) - - # Test mutating a tuple through its `as_ptr()` pointer - var ptr = tup.as_ptr() - ptr[0] = 1 - ptr[1] = 2 - ptr[2] = 3 - - assert_equal(tup[0], 1) - assert_equal(tup[1], 2) - assert_equal(tup[2], 3) - - def main(): test_static_tuple() test_static_int_tuple() test_tuple_literal() - test_tuple_pointer() From a85a765eba52e582d3d2e7e83016b7a92c4f0086 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 21:10:42 -0700 Subject: [PATCH 0150/2019] [mojo-lang] remove `__get_lvalue_as_address` entirely! (#37640) Thanks to the hard work of a lot of people, we can now remove this, huzzah. This fixes #34441 MODULAR_ORIG_COMMIT_REV_ID: 79430d2ff7221b33a85c1354f066def8db717a53 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 175e3f8a93..6db55b2814 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -144,6 +144,10 @@ what we publish. - `SIMD.to_int(value)` has been removed. Use `int(value)` instead. +- The `__get_lvalue_as_address(x)` magic function has been removed. To get a + reference to a value use `Reference(x)` and if you need an unsafe pointer, you + can use `UnsafePointer.address_of(x)`. + ### 🛠️ Fixed - [#516](https://github.com/modularml/mojo/issues/516) and From 285e68fcbcae54d244a46e576faf4535c04f3126 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 21:31:04 -0700 Subject: [PATCH 0151/2019] [mojo-stdlib] Remove the `Reference.get_unsafe_pointer()` method. (#37641) This operation is already available as `UnsafePointer(ref)` MODULAR_ORIG_COMMIT_REV_ID: d3a039294c229ce7cd0c43f07e8f3551e0ce3aa5 --- stdlib/src/memory/reference.mojo | 9 --------- 1 file changed, 9 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 563e12f453..95799f7831 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -274,15 +274,6 @@ struct Reference[ _type = Pointer[type, address_space].pointer_type ](UnsafePointer(self).value) - @always_inline("nodebug") - fn get_unsafe_pointer(self) -> UnsafePointer[type, address_space]: - """Constructs a UnsafePointer from a safe reference. - - Returns: - Constructed UnsafePointer object. - """ - return UnsafePointer(self).value - @always_inline("nodebug") fn bitcast_element[ new_element_type: AnyType From f87f847b8d6147e5f1a73265aaf06c7ad7a2aeca Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 22:08:19 -0700 Subject: [PATCH 0152/2019] [mojo-stdlib] Various pointer cleanups, remove `_LITRef`. (#37642) Several cleanups: 1) remove `_LITRef` struct - we can use `Reference` for this now. 2) Normalize the alias inside Reference, VariadicListMem, and the Pointers to be named `_mlir_type` instead of `pointer` or other names. This hides it from docgen and provides a consistent name that can be generalized to other types in the future. 3) Move the Pointer.refitem method to a more logical place in the method list. MODULAR_ORIG_COMMIT_REV_ID: ef09ffddbf312730438be0407ed71b7b0795c992 --- stdlib/src/builtin/builtin_list.mojo | 42 +++++++++--------- stdlib/src/builtin/tuple.mojo | 6 +-- stdlib/src/collections/dict.mojo | 32 +++++++++----- stdlib/src/collections/list.mojo | 6 +-- stdlib/src/collections/set.mojo | 6 ++- stdlib/src/memory/_arc.mojo | 4 +- stdlib/src/memory/memory.mojo | 6 +-- stdlib/src/memory/reference.mojo | 22 ++++++---- stdlib/src/memory/unsafe.mojo | 63 +++++++++++++-------------- stdlib/src/memory/unsafe_pointer.mojo | 49 ++++++++++----------- stdlib/src/utils/variant.mojo | 9 ++-- 11 files changed, 128 insertions(+), 117 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 432190249d..c1b92affcc 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory.reference import Reference, _LITRef +from memory.reference import Reference from memory.unsafe_pointer import * # ===----------------------------------------------------------------------===# @@ -104,8 +104,8 @@ struct VariadicList[type: AnyRegType](Sized): type: The type of the elements in the list. """ - alias storage_type = __mlir_type[`!kgen.variadic<`, type, `>`] - var value: Self.storage_type + alias _mlir_type = __mlir_type[`!kgen.variadic<`, type, `>`] + var value: Self._mlir_type """The underlying storage for the variadic list.""" alias IterType = _VariadicListIter[type] @@ -121,7 +121,7 @@ struct VariadicList[type: AnyRegType](Sized): self = value @always_inline - fn __init__(inout self, value: Self.storage_type): + fn __init__(inout self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -239,12 +239,12 @@ struct VariadicListMem[ """ alias reference_type = Reference[element_type, elt_is_mutable, lifetime] - alias mlir_ref_type = _LITRef[element_type, elt_is_mutable, lifetime].type - alias storage_type = __mlir_type[ - `!kgen.variadic<`, Self.mlir_ref_type, `, borrow_in_mem>` + alias _mlir_ref_type = Self.reference_type._mlir_type + alias _mlir_type = __mlir_type[ + `!kgen.variadic<`, Self._mlir_ref_type, `, borrow_in_mem>` ] - var value: Self.storage_type + var value: Self._mlir_type """The underlying storage, a variadic list of references to elements of the given type.""" @@ -254,7 +254,7 @@ struct VariadicListMem[ # Provide support for borrowed variadic arguments. @always_inline - fn __init__(inout self, value: Self.storage_type): + fn __init__(inout self, value: Self._mlir_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -266,12 +266,12 @@ struct VariadicListMem[ # Provide support for variadics of *inout* arguments. The reference will # automatically be inferred to be mutable, and the !kgen.variadic will have # convention=byref. - alias inout_storage_type = __mlir_type[ - `!kgen.variadic<`, Self.mlir_ref_type, `, byref>` + alias _inout_variadic_type = __mlir_type[ + `!kgen.variadic<`, Self._mlir_ref_type, `, byref>` ] @always_inline - fn __init__(inout self, value: Self.inout_storage_type): + fn __init__(inout self, value: Self._inout_variadic_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -280,18 +280,18 @@ struct VariadicListMem[ var tmp = value # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. - self.value = Pointer.address_of(tmp).bitcast[Self.storage_type]().load() + self.value = Pointer.address_of(tmp).bitcast[Self._mlir_type]().load() self._is_owned = False # Provide support for variadics of *owned* arguments. The reference will # automatically be inferred to be mutable, and the !kgen.variadic will have # convention=owned_in_mem. - alias owned_storage_type = __mlir_type[ - `!kgen.variadic<`, Self.mlir_ref_type, `, owned_in_mem>` + alias _owned_variadic_type = __mlir_type[ + `!kgen.variadic<`, Self._mlir_ref_type, `, owned_in_mem>` ] @always_inline - fn __init__(inout self, value: Self.owned_storage_type): + fn __init__(inout self, value: Self._owned_variadic_type): """Constructs a VariadicList from a variadic argument type. Args: @@ -300,7 +300,7 @@ struct VariadicListMem[ var tmp = value # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. - self.value = Pointer.address_of(tmp).bitcast[Self.storage_type]().load() + self.value = Pointer.address_of(tmp).bitcast[Self._mlir_type]().load() self._is_owned = True @always_inline @@ -428,7 +428,7 @@ struct VariadicPack[ element_types: The list of types held by the argument pack. """ - alias _mlir_pack_type = __mlir_type[ + alias _mlir_type = __mlir_type[ `!lit.ref.pack<:variadic<`, element_trait, `> `, @@ -438,11 +438,11 @@ struct VariadicPack[ `>`, ] - var _value: Self._mlir_pack_type + var _value: Self._mlir_type var _is_owned: Bool @always_inline - fn __init__(inout self, value: Self._mlir_pack_type, is_owned: Bool): + fn __init__(inout self, value: Self._mlir_type, is_owned: Bool): """Constructs a VariadicPack from the internal representation. Args: @@ -533,7 +533,7 @@ struct VariadicPack[ Self.elt_is_mutable, Self.lifetime, ] - return rebind[result_ref.mlir_ref_type](ref_elt) + return rebind[result_ref._mlir_type](ref_elt) @always_inline fn each[func: fn[T: element_trait] (T) capturing -> None](self): diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index ca6b1e7a66..df0d5fe9df 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -151,9 +151,9 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): idx: Int, mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type, - ]( - self_lit: Reference[Self, mutability, self_life].mlir_ref_type - ) -> Reference[element_types[idx.value], mutability, self_life]: + ](self_lit: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ + element_types[idx.value], mutability, self_life + ]: # Return a reference to an element at the specified index, propagating # mutability of self. var storage_kgen_ptr = Reference( diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 3f799da2b6..6c994def70 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -561,8 +561,10 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): fn __iter__[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, - ) -> _DictKeyIter[K, V, mutability, self_life]: + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _DictKeyIter[ + K, V, mutability, self_life + ]: """Iterate over the dict's keys as immutable references. Parameters: @@ -579,8 +581,10 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): fn keys[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, - ) -> _DictKeyIter[K, V, mutability, self_life]: + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _DictKeyIter[ + K, V, mutability, self_life + ]: """Iterate over the dict's keys as immutable references. Parameters: @@ -595,7 +599,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): fn values[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, + self: Reference[Self, mutability, self_life]._mlir_type, ) -> _DictValueIter[K, V, mutability, self_life]: """Iterate over the dict's values as references. @@ -613,7 +617,7 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): fn items[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, + self: Reference[Self, mutability, self_life]._mlir_type, ) -> _DictEntryIter[K, V, mutability, self_life]: """Iterate over the dict's entries as immutable references. @@ -874,8 +878,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn __iter__[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, - ) -> _DictKeyIter[Self.key_type, V, mutability, self_life]: + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _DictKeyIter[ + Self.key_type, V, mutability, self_life + ]: """Iterate over the keyword dict's keys as immutable references. Parameters: @@ -896,8 +902,10 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn keys[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, - ) -> _DictKeyIter[Self.key_type, V, mutability, self_life]: + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _DictKeyIter[ + Self.key_type, V, mutability, self_life + ]: """Iterate over the keyword dict's keys as immutable references. Parameters: @@ -914,7 +922,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn values[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, + self: Reference[Self, mutability, self_life]._mlir_type, ) -> _DictValueIter[Self.key_type, V, mutability, self_life]: """Iterate over the keyword dict's values as references. @@ -936,7 +944,7 @@ struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): fn items[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, + self: Reference[Self, mutability, self_life]._mlir_type, ) -> _DictEntryIter[Self.key_type, V, mutability, self_life]: """Iterate over the keyword dictionary's entries as immutable references. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index b2f4adcb25..8c2590b82c 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -430,7 +430,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): fn __get_ref[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, + self: Reference[Self, mutability, self_life]._mlir_type, i: Int, ) -> Reference[T, mutability, self_life]: """Gets a reference to the list element at the given index. @@ -452,13 +452,13 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # because the mutability won't match. var base_ptr = Reference(self)[].data return __mlir_op.`lit.ref.from_pointer`[ - _type = Reference[T, mutability, self_life].mlir_ref_type + _type = Reference[T, mutability, self_life]._mlir_type ]((base_ptr + normalized_idx).value) fn __iter__[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, + self: Reference[Self, mutability, self_life]._mlir_type, ) -> _ListIter[ T, mutability, self_life ]: diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 2f48dc4746..755769b534 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -228,8 +228,10 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): fn __iter__[ mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type ]( - self: Reference[Self, mutability, self_life].mlir_ref_type, - ) -> _DictKeyIter[T, NoneType, mutability, self_life]: + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _DictKeyIter[ + T, NoneType, mutability, self_life + ]: """Iterate over elements of the set, returning immutable references. Returns: diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 34f2ee51d7..bab487007d 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -140,7 +140,7 @@ struct Arc[T: Movable](CollectionElement): fn __refitem__[ mutability: __mlir_type.i1, lifetime: AnyLifetime[mutability].type, - ](self: Reference[Self, mutability, lifetime].mlir_ref_type) -> Reference[ + ](self: Reference[Self, mutability, lifetime]._mlir_type) -> Reference[ T, mutability, lifetime ]: """Returns a Reference to the managed value. @@ -150,7 +150,7 @@ struct Arc[T: Movable](CollectionElement): """ alias RefType = Reference[T, mutability, lifetime] return RefType( - __mlir_op.`lit.ref.from_pointer`[_type = RefType.mlir_ref_type]( + __mlir_op.`lit.ref.from_pointer`[_type = RefType._mlir_type]( Reference(self)[]._data_ptr().value ) ) diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 45c0d362b4..5ec78668b2 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -444,14 +444,14 @@ fn stack_allocation[ if triple_is_nvidia_cuda() and address_space == _GPUAddressSpace.SHARED: return __mlir_op.`pop.global_alloc`[ count = count.value, - _type = Pointer[type, address_space].pointer_type, + _type = Pointer[type, address_space]._mlir_type, alignment = alignment.value, address_space = address_space._value.value, ]() else: return __mlir_op.`pop.stack_allocation`[ count = count.value, - _type = Pointer[type, address_space].pointer_type, + _type = Pointer[type, address_space]._mlir_type, alignment = alignment.value, address_space = address_space._value.value, ]() @@ -480,7 +480,7 @@ fn _malloc[ ).bitcast[type]() else: return __mlir_op.`pop.aligned_alloc`[ - _type = Pointer[type, address_space].pointer_type + _type = Pointer[type, address_space]._mlir_type ](alignment.value, size.value) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 95799f7831..2de75565b1 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -226,15 +226,21 @@ struct Reference[ address_space: The address space of the referenced data. """ - alias mlir_ref_type = _LITRef[ - type, is_mutable, lifetime, address_space - ].type + alias _mlir_type = __mlir_type[ + `!lit.ref<`, + type, + `, `, + lifetime, + `, `, + address_space._value.value, + `>`, + ] - var value: Self.mlir_ref_type + var value: Self._mlir_type """The underlying MLIR reference.""" @always_inline("nodebug") - fn __init__(inout self, value: Self.mlir_ref_type): + fn __init__(inout self, value: Self._mlir_type): """Constructs a Reference from the MLIR reference. Args: @@ -243,7 +249,7 @@ struct Reference[ self.value = value @always_inline("nodebug") - fn __refitem__(self) -> Self.mlir_ref_type: + fn __refitem__(self) -> Self._mlir_type: """Enable subscript syntax `ref[]` to access the element. Returns: @@ -252,7 +258,7 @@ struct Reference[ return self.value @always_inline("nodebug") - fn __mlir_ref__(self) -> Self.mlir_ref_type: + fn __mlir_ref__(self) -> Self._mlir_type: """Enable the Mojo compiler to see into `Reference`. Returns: @@ -271,7 +277,7 @@ struct Reference[ """ # Work around AnyRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ - _type = Pointer[type, address_space].pointer_type + _type = Pointer[type, address_space]._mlir_type ](UnsafePointer(self).value) @always_inline("nodebug") diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 5790e0b10d..d6ed330b33 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -32,7 +32,6 @@ from sys.intrinsics import prefetch as _prefetch from sys.intrinsics import strided_load, strided_store from .memory import _free, _malloc -from memory.reference import _LITRef # ===----------------------------------------------------------------------===# # Utilities @@ -74,7 +73,7 @@ fn bitcast[ A new Pointer with the specified address. """ return __mlir_op.`pop.index_to_pointer`[ - _type = Pointer[type, address_space].pointer_type + _type = Pointer[type, address_space]._mlir_type ](Scalar[DType.index](val).value) @@ -233,30 +232,19 @@ struct LegacyPointer[ address_space: The address space the pointer is in. """ - alias pointer_type = __mlir_type[ + alias _mlir_type = __mlir_type[ `!kgen.pointer<`, type, `,`, address_space._value.value, `>` ] - var address: Self.pointer_type + var address: Self._mlir_type """The pointed-to address.""" - alias _mlir_ref_type = _LITRef[ + alias _mlir_ref_type = Reference[ type, __mlir_attr.`1: i1`, __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, address_space, - ].type - - @always_inline("nodebug") - fn __refitem__(self) -> Self._mlir_ref_type: - """Enable subscript syntax `ref[]` to access the element. - - Returns: - The MLIR reference for the Mojo compiler to use. - """ - return __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_ref_type]( - self.address - ) + ]._mlir_type @always_inline("nodebug") fn __init__() -> Self: @@ -268,7 +256,7 @@ struct LegacyPointer[ return Self.get_null() @always_inline("nodebug") - fn __init__(address: Self.pointer_type) -> Self: + fn __init__(address: Self._mlir_type) -> Self: """Constructs a LegacyPointer from the address. Args: @@ -289,9 +277,9 @@ struct LegacyPointer[ Returns: Constructed LegacyPointer object. """ - var address = __mlir_op.`pop.index_to_pointer`[ - _type = Self.pointer_type - ](value.cast[DType.index]().value) + var address = __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( + value.cast[DType.index]().value + ) return Self {address: address} @always_inline("nodebug") @@ -304,7 +292,7 @@ struct LegacyPointer[ Returns: Constructed Pointer object. """ - return __mlir_op.`pop.index_to_pointer`[_type = Self.pointer_type]( + return __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( Scalar[DType.index](address).value ) @@ -316,7 +304,7 @@ struct LegacyPointer[ Returns: Constructed nullptr LegacyPointer object. """ - return __mlir_attr[`#interp.pointer<0> : `, Self.pointer_type] + return __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] fn __str__(self) -> String: """Format this pointer as a hexadecimal string. @@ -349,6 +337,17 @@ struct LegacyPointer[ """ return arg.get_legacy_pointer() + @always_inline("nodebug") + fn __refitem__(self) -> Self._mlir_ref_type: + """Enable subscript syntax `ref[]` to access the element. + + Returns: + The MLIR reference for the Mojo compiler to use. + """ + return __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_ref_type]( + self.address + ) + @always_inline("nodebug") fn __getitem__[T: Intable](self, offset: T) -> type: """Loads the value the LegacyPointer object points to with the given offset. @@ -524,7 +523,7 @@ struct LegacyPointer[ return rebind[LegacyPointer[new_type, address_space]](self) return __mlir_op.`pop.pointer.bitcast`[ - _type = LegacyPointer[new_type, address_space].pointer_type, + _type = LegacyPointer[new_type, address_space]._mlir_type, ](self.address) @always_inline("nodebug") @@ -546,7 +545,7 @@ struct LegacyPointer[ return rebind[LegacyPointer[type, new_address_space]](self) return __mlir_op.`pop.pointer.addrspacecast`[ - _type = LegacyPointer[type, new_address_space].pointer_type, + _type = LegacyPointer[type, new_address_space]._mlir_type, ](self.address) # ===------------------------------------------------------------------=== # @@ -591,7 +590,7 @@ struct LegacyPointer[ return int(self) < int(rhs) # ===------------------------------------------------------------------=== # - # LegacyPointer Arithmetic + # Pointer Arithmetic # ===------------------------------------------------------------------=== # @always_inline("nodebug") @@ -684,15 +683,15 @@ struct DTypePointer[ """ alias element_type = Scalar[type] - alias pointer_type = Pointer[Scalar[type], address_space] - var address: Self.pointer_type + alias _mlir_type = Pointer[Scalar[type], address_space] + var address: Self._mlir_type """The pointed-to address.""" @always_inline("nodebug") fn __init__(inout self): """Constructs a null `DTypePointer` from the given type.""" - self.address = Self.pointer_type() + self.address = Self._mlir_type() @always_inline("nodebug") fn __init__( @@ -731,7 +730,7 @@ struct DTypePointer[ value: The input pointer index. """ var address = __mlir_op.`pop.index_to_pointer`[ - _type = Self.pointer_type.pointer_type + _type = Self._mlir_type._mlir_type ](value.cast[DType.index]().value) self.address = address @@ -742,7 +741,7 @@ struct DTypePointer[ Args: address: The input address. """ - self.address = Self.pointer_type(address=address) + self.address = Self._mlir_type(address=address) @staticmethod @always_inline("nodebug") @@ -752,7 +751,7 @@ struct DTypePointer[ Returns: Constructed *nullptr* `DTypePointer` object. """ - return Self.pointer_type() + return Self._mlir_type() fn __str__(self) -> String: """Format this pointer as a hexadecimal string. diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 0096cea47c..f8b5124a63 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -23,7 +23,6 @@ from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc -from memory.reference import _LITRef # ===----------------------------------------------------------------------=== # @@ -40,11 +39,22 @@ struct UnsafePointer[ address_space: The address space associated with the UnsafePointer allocated memory. """ - alias pointer_type = __mlir_type[ + alias _mlir_type = __mlir_type[ `!kgen.pointer<`, T, `,`, address_space._value.value, `>` ] + + # We're unsafe, so we can have unsafe things. References we make have + # an immortal mutable lifetime, since we can't come up with a meaningful + # lifetime for them anyway. + alias _mlir_ref_type = Reference[ + T, + __mlir_attr.`1: i1`, + __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, + address_space, + ]._mlir_type + """The underlying pointer type.""" - var value: Self.pointer_type + var value: Self._mlir_type """The underlying pointer.""" @always_inline @@ -57,7 +67,7 @@ struct UnsafePointer[ return Self.get_null() @always_inline - fn __init__(value: Self.pointer_type) -> Self: + fn __init__(value: Self._mlir_type) -> Self: """Create a pointer with the input value. Args: @@ -91,7 +101,7 @@ struct UnsafePointer[ The pointer. """ return Self { - value: __mlir_op.`pop.index_to_pointer`[_type = Self.pointer_type]( + value: __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( Scalar[DType.index](address).value ) } @@ -105,7 +115,7 @@ struct UnsafePointer[ Constructed nullptr UnsafePointer object. """ return Self { - value: __mlir_attr[`#interp.pointer<0> : `, Self.pointer_type] + value: __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] } @staticmethod @@ -158,13 +168,8 @@ struct UnsafePointer[ A new UnsafePointer object with the specified type and the same address, as the original UnsafePointer. """ - - @parameter - if _mlirtype_is_eq[T, new_type](): - return rebind[UnsafePointer[new_type, address_space]](self) - return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, address_space].pointer_type, + _type = UnsafePointer[new_type, address_space]._mlir_type, ](self.value) @always_inline @@ -182,7 +187,7 @@ struct UnsafePointer[ """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, address_space].pointer_type + _type = UnsafePointer[new_type, address_space]._mlir_type ](self.value) @always_inline @@ -200,7 +205,7 @@ struct UnsafePointer[ """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[T, new_address_space].pointer_type + _type = UnsafePointer[T, new_address_space]._mlir_type ](self.value) @always_inline @@ -324,31 +329,21 @@ struct UnsafePointer[ """ return int(self) >= int(rhs) - # We're unsafe, so we can have unsafe things. References we make have - # an immortal mutable lifetime, since we can't come up with a meaningful - # lifetime for them anyway. - alias mlir_ref_type = Reference[ - T, - __mlir_attr.`1: i1`, - __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, - address_space, - ].mlir_ref_type - @always_inline fn __refitem__( self, - ) -> Self.mlir_ref_type: + ) -> Self._mlir_ref_type: """Return a reference to the underlying data, offset by the offset index. Returns: A reference to the value. """ - return __mlir_op.`lit.ref.from_pointer`[_type = Self.mlir_ref_type]( + return __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_ref_type]( self.value ) @always_inline - fn __refitem__(self, offset: Int) -> Self.mlir_ref_type: + fn __refitem__(self, offset: Int) -> Self._mlir_ref_type: """Return a reference to the underlying data, offset by the offset index. Args: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 56b8c3166e..933e3e1cfb 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -41,7 +41,6 @@ print(to_string(who_knows)) from sys.info import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq -from memory.reference import _LITRef from memory.unsafe_pointer import * from utils.loop import unroll @@ -156,7 +155,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn _get_state[ is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] - ](self: _LITRef[Self, is_mut, lt].type) -> Reference[Int8, is_mut, lt]: + ](self: Reference[Self, is_mut, lt]._mlir_type) -> Reference[ + Int8, is_mut, lt + ]: var int8_self = UnsafePointer(Reference(self).bitcast_element[Int8]()) return (int8_self + _UnionSize[Ts].compute())[] @@ -285,7 +286,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): T: CollectionElement, mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type, - ](self: Reference[Self, mutability, self_life].mlir_ref_type) -> Reference[ + ](self: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ T, mutability, self_life ]: """Get the value out of the variant as a type-checked type. @@ -308,7 +309,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ debug_assert(Reference(self)[].isa[T](), "get: wrong variant type") return __mlir_op.`lit.ref.from_pointer`[ - _type = Reference[T, mutability, self_life].mlir_ref_type + _type = Reference[T, mutability, self_life]._mlir_type ](Reference(self)[]._get_ptr[T]().value) @staticmethod From 104d450bd80d732f899c2d8915b6fb233f701371 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 23:14:18 -0700 Subject: [PATCH 0153/2019] [mojo-stdlib] Improve Pointer.bitcast/addrspace_cast. (#37644) This: 1) enhances the `bitcast` methods to support changing the address space at the same time. 2) renames the 'Reference.bitcast_element' -> `unsafe_bitcast` since Reference is a safe type and this is a (very) unsafe operation. 3) renames `address_space_cast` -> `address_space_bitcast` for consistency. 4) Simplifies some use of these apis. MODULAR_ORIG_COMMIT_REV_ID: 583aeed2a68dd57d2b2ee591012ca8e8c7e86fbb --- docs/changelog.md | 4 +-- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/collections/dict.mojo | 12 ++++--- stdlib/src/collections/set.mojo | 2 +- stdlib/src/memory/reference.mojo | 20 ++++++----- stdlib/src/memory/unsafe.mojo | 39 ++++++++-------------- stdlib/src/memory/unsafe_pointer.mojo | 32 ++++-------------- stdlib/src/utils/variant.mojo | 8 ++--- stdlib/test/memory/test_unsafepointer.mojo | 4 +-- 9 files changed, 48 insertions(+), 75 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6db55b2814..2ed8cd651d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -106,10 +106,10 @@ what we publish. `move_from_pointee`, `initialize_pointee` and `move_pointee` respectively. 3) A new `destroy_pointee` function runs the destructor on the pointee. 4) `AnyPointer` can be initialized from a `Reference` as mentioned above. - 5) It has some new methods like `address_space_cast`. + 5) It has some new methods like `address_space_bitcast`. - The `Reference` type has several changes, including: 1) It is now located in `memory.reference` instead of `memory.unsafe`. - 2) `Reference` now has an unsafe `address_space_cast` method like `Pointer`. + 2) `Reference` now has an unsafe `address_space_bitcast` method like `Pointer`. 3) The `destroy_element_unsafe` method has been removed, do this with `AnyPointer/destroy_pointee`, which is more obviously unsafe. 4) The `emplace_ref_unsafe` function has been removed in favor of diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index c1b92affcc..29b95b7c87 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -332,7 +332,7 @@ struct VariadicListMem[ # destroy in backwards order to match how arguments are normally torn # down when CheckLifetimes is left to its own devices. for i in range(len(self), 0, -1): - destroy_pointee(UnsafePointer(Reference(self[i - 1]))) + destroy_pointee(UnsafePointer.address_of(self[i - 1])) @always_inline fn __len__(self) -> Int: diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 6c994def70..5c9c30eb72 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -90,7 +90,7 @@ struct _DictEntryIter[ self.seen += 1 # Super unsafe, but otherwise we have to do a bunch of super # unsafe reference lifetime casting. - return opt_entry_ref.bitcast_element[DictEntry[K, V]]() + return opt_entry_ref.unsafe_bitcast[DictEntry[K, V]]() self.index += 1 fn __len__(self) -> Int: @@ -133,8 +133,10 @@ struct _DictKeyIter[ fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() - var anyptr = UnsafePointer(Reference(entry_ref[].key)) - return anyptr.address_space_cast[Self.dict_entry_iter.address_space]()[] + var anyptr = UnsafePointer.address_of(entry_ref[].key) + return anyptr.address_space_bitcast[ + Self.dict_entry_iter.address_space + ]()[] fn __len__(self) -> Int: return self.iter.__len__() @@ -172,8 +174,8 @@ struct _DictValueIter[ var entry_ref = self.iter.__next__() # Cast through a pointer to grant additional mutability and switch # address spaces out. - var anyptr = UnsafePointer(Reference(entry_ref[].value)) - return anyptr.address_space_cast[address_space]()[] + var anyptr = UnsafePointer.address_of(entry_ref[].value) + return anyptr.address_space_bitcast[address_space]()[] fn __len__(self) -> Int: return self.iter.__len__() diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 755769b534..222d10e7a2 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -245,7 +245,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): NoneType, mutability, self_life, - ](0, 0, Reference(self).bitcast_element[Dict[T, NoneType]]()) + ](0, 0, Reference(self).unsafe_bitcast[Dict[T, NoneType]]()) ) fn add(inout self, t: T): diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 2de75565b1..1816b46430 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -281,24 +281,30 @@ struct Reference[ ](UnsafePointer(self).value) @always_inline("nodebug") - fn bitcast_element[ - new_element_type: AnyType - ](self) -> Reference[new_element_type, is_mutable, lifetime, address_space]: + fn unsafe_bitcast[ + new_element_type: AnyType, + new_address_space: AddressSpace = address_space, + ](self) -> Reference[ + new_element_type, is_mutable, lifetime, new_address_space + ]: """Cast the reference to one of another element type, but the same lifetime, mutability, and address space. Parameters: new_element_type: The result type. + new_address_space: The address space of the result. Returns: The new reference. """ # We don't have a `lit.ref.cast`` operation, so convert through a KGEN # pointer. - return UnsafePointer(self).bitcast_element[new_element_type]()[] + return UnsafePointer(self).bitcast[ + new_element_type, new_address_space + ]()[] @always_inline - fn address_space_cast[ + fn unsafe_address_space_bitcast[ new_address_space: AddressSpace ](self) -> Reference[type, is_mutable, lifetime, new_address_space]: """Cast the reference to one of another address space, but the same @@ -310,6 +316,4 @@ struct Reference[ Returns: The new reference. """ - # We don't have a `lit.ref.cast`` operation, so convert through a KGEN - # pointer. - return UnsafePointer(self).address_space_cast[new_address_space]()[] + return self.unsafe_bitcast[type, new_address_space]() diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index d6ed330b33..b97dc0d9dc 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -506,28 +506,24 @@ struct LegacyPointer[ @always_inline("nodebug") fn bitcast[ - new_type: AnyRegType - ](self) -> LegacyPointer[new_type, address_space]: + new_type: AnyRegType, new_address_space: AddressSpace = address_space + ](self) -> LegacyPointer[new_type, new_address_space]: """Bitcasts a LegacyPointer to a different type. Parameters: new_type: The target type. + new_address_space: The address space of the result. Returns: A new LegacyPointer object with the specified type and the same address, as the original LegacyPointer. """ - - @parameter - if _mlirtype_is_eq[type, new_type](): - return rebind[LegacyPointer[new_type, address_space]](self) - return __mlir_op.`pop.pointer.bitcast`[ - _type = LegacyPointer[new_type, address_space]._mlir_type, + _type = LegacyPointer[new_type, new_address_space]._mlir_type, ](self.address) @always_inline("nodebug") - fn address_space_cast[ + fn address_space_bitcast[ new_address_space: AddressSpace ](self) -> LegacyPointer[type, new_address_space]: """Casts a LegacyPointer to a different address space. @@ -539,14 +535,7 @@ struct LegacyPointer[ A new LegacyPointer object with the specified type and the same address, as the original LegacyPointer but located in a different address space. """ - - @parameter - if address_space == new_address_space: - return rebind[LegacyPointer[type, new_address_space]](self) - - return __mlir_op.`pop.pointer.addrspacecast`[ - _type = LegacyPointer[type, new_address_space]._mlir_type, - ](self.address) + return self.bitcast[type, new_address_space]() # ===------------------------------------------------------------------=== # # Comparisons @@ -885,20 +874,23 @@ struct DTypePointer[ # ===------------------------------------------------------------------=== # @always_inline("nodebug") - fn bitcast[new_type: DType](self) -> DTypePointer[new_type, address_space]: + fn bitcast[ + new_type: DType, new_address_space: AddressSpace = address_space + ](self) -> DTypePointer[new_type, new_address_space]: """Bitcasts `DTypePointer` to a different dtype. Parameters: new_type: The target dtype. + new_address_space: The address space of the result. Returns: A new `DTypePointer` object with the specified dtype and the same address, as the original `DTypePointer`. """ - return self.address.bitcast[SIMD[new_type, 1]]() + return self.address.bitcast[SIMD[new_type, 1], new_address_space]() @always_inline("nodebug") - fn address_space_cast[ + fn address_space_bitcast[ new_address_space: AddressSpace ](self) -> DTypePointer[type, new_address_space]: """Casts a Pointer to a different address space. @@ -910,12 +902,7 @@ struct DTypePointer[ A new Pointer object with the specified type and the same address, as the original Pointer but located in a different address space. """ - - @parameter - if address_space == new_address_space: - return rebind[DTypePointer[type, new_address_space]](self) - - return self.address.address_space_cast[new_address_space]() + return self.bitcast[type, new_address_space]() @always_inline("nodebug") fn _as_scalar_pointer(self) -> Pointer[Scalar[type], address_space]: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f8b5124a63..f04fff4782 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -157,41 +157,24 @@ struct UnsafePointer[ @always_inline("nodebug") fn bitcast[ - new_type: AnyType - ](self) -> UnsafePointer[new_type, address_space]: + new_type: AnyType, new_address_space: AddressSpace = address_space + ](self) -> UnsafePointer[new_type, new_address_space]: """Bitcasts a UnsafePointer to a different type. Parameters: new_type: The target type. + new_address_space: The address space of the result. Returns: A new UnsafePointer object with the specified type and the same address, as the original UnsafePointer. """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, address_space]._mlir_type, + _type = UnsafePointer[new_type, new_address_space]._mlir_type, ](self.value) @always_inline - fn bitcast_element[ - new_type: AnyType - ](self) -> UnsafePointer[new_type, address_space]: - """Bitcasts the pointer to a different type. - - Parameters: - new_type: The target type. - - Returns: - A new pointer with the specified type and the same address, as - the original pointer. - """ - - return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, address_space]._mlir_type - ](self.value) - - @always_inline - fn address_space_cast[ + fn address_space_bitcast[ new_address_space: AddressSpace ](self) -> UnsafePointer[T, new_address_space]: """Bitcasts the pointer to a different address space. @@ -203,10 +186,7 @@ struct UnsafePointer[ A new pointer with the same type and address, but a new address space. """ - - return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[T, new_address_space]._mlir_type - ](self.value) + return self.bitcast[T, new_address_space]() @always_inline fn __int__(self) -> Int: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 933e3e1cfb..f70bd92386 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -151,14 +151,14 @@ struct Variant[*Ts: CollectionElement](CollectionElement): constrained[ Self._check[T]() != Self._sentinel, "not a union element type" ]() - return Reference(self._impl).bitcast_element[T]() + return UnsafePointer.address_of(self._impl).bitcast[T]() fn _get_state[ is_mut: __mlir_type.i1, lt: __mlir_type[`!lit.lifetime<`, is_mut, `>`] ](self: Reference[Self, is_mut, lt]._mlir_type) -> Reference[ Int8, is_mut, lt ]: - var int8_self = UnsafePointer(Reference(self).bitcast_element[Int8]()) + var int8_self = UnsafePointer.address_of(self).bitcast[Int8]() return (int8_self + _UnionSize[Ts].compute())[] fn __init__[T: CollectionElement](inout self, owned value: T): @@ -190,8 +190,8 @@ struct Variant[*Ts: CollectionElement](CollectionElement): if self._get_state()[] == i: alias T = Ts[i] initialize_pointee[T]( - UnsafePointer(Reference(self._impl).bitcast_element[T]()), - Reference(other._impl).bitcast_element[T]()[], + UnsafePointer.address_of(self._impl).bitcast[T](), + Reference(other._impl).unsafe_bitcast[T]()[], ) unroll[each, len(VariadicList(Ts))]() diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 893c913ab0..ed812f0ca7 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -91,9 +91,9 @@ def test_address_of(): def test_bitcast(): var local = 1 var ptr = UnsafePointer[Int].address_of(local) - var aliased_ptr = ptr.bitcast_element[SIMD[DType.uint8, 4]]() + var aliased_ptr = ptr.bitcast[SIMD[DType.uint8, 4]]() - assert_equal(int(ptr), int(ptr.bitcast_element[Int]())) + assert_equal(int(ptr), int(ptr.bitcast[Int]())) assert_equal(int(ptr), int(aliased_ptr)) From ecadc0fabcff3e0b19a1cdafc26f10c3e8fd0132 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 12 Apr 2024 23:53:57 -0700 Subject: [PATCH 0154/2019] [mojo-stdlib] Further tidy bitcast, remove `pop.pointer.addrspacecast` (#37646) This implements Connor's idea from #37644 to merge the ugly address_space_bitcast method into bitcast using named arguments, which is pretty swank. This allows one to use any of `ptr.bitcast[NewEltTy]()` or `ptr.bitcast[NewEltTy, NewAddrSpace]()` or `ptr.bitcast[address_space=NewAddrSpace]()` depending on what is needed. This also removes `pop.pointer.addrspacecast` since `pop.pointer.bitcast` can and is used to do addr space conversions now. MODULAR_ORIG_COMMIT_REV_ID: d96dd6f4bbcfb55928891cfc90e3e76bbb35135a --- stdlib/src/collections/dict.mojo | 6 ++-- stdlib/src/memory/reference.mojo | 34 +++++------------- stdlib/src/memory/unsafe.mojo | 50 +++++++-------------------- stdlib/src/memory/unsafe_pointer.mojo | 25 ++++---------- 4 files changed, 29 insertions(+), 86 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 5c9c30eb72..80f25817fa 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -134,8 +134,8 @@ struct _DictKeyIter[ fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() var anyptr = UnsafePointer.address_of(entry_ref[].key) - return anyptr.address_space_bitcast[ - Self.dict_entry_iter.address_space + return anyptr.bitcast[ + address_space = Self.dict_entry_iter.address_space ]()[] fn __len__(self) -> Int: @@ -175,7 +175,7 @@ struct _DictValueIter[ # Cast through a pointer to grant additional mutability and switch # address spaces out. var anyptr = UnsafePointer.address_of(entry_ref[].value) - return anyptr.address_space_bitcast[address_space]()[] + return anyptr.bitcast[address_space=address_space]()[] fn __len__(self) -> Int: return self.iter.__len__() diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 1816b46430..05ac8a94fc 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -282,38 +282,20 @@ struct Reference[ @always_inline("nodebug") fn unsafe_bitcast[ - new_element_type: AnyType, - new_address_space: AddressSpace = address_space, - ](self) -> Reference[ - new_element_type, is_mutable, lifetime, new_address_space - ]: - """Cast the reference to one of another element type, but the same - lifetime, mutability, and address space. + new_element_type: AnyType = type, + /, + address_space: AddressSpace = Self.address_space, + ](self) -> Reference[new_element_type, is_mutable, lifetime, address_space]: + """Cast the reference to one of another element type and AddressSpace, + but the same lifetime and mutability. Parameters: new_element_type: The result type. - new_address_space: The address space of the result. + address_space: The address space of the result. Returns: The new reference. """ # We don't have a `lit.ref.cast`` operation, so convert through a KGEN # pointer. - return UnsafePointer(self).bitcast[ - new_element_type, new_address_space - ]()[] - - @always_inline - fn unsafe_address_space_bitcast[ - new_address_space: AddressSpace - ](self) -> Reference[type, is_mutable, lifetime, new_address_space]: - """Cast the reference to one of another address space, but the same - element type, lifetime, and mutability. - - Parameters: - new_address_space: The address space of the result. - - Returns: - The new reference. - """ - return self.unsafe_bitcast[type, new_address_space]() + return UnsafePointer(self).bitcast[new_element_type, address_space]()[] diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index b97dc0d9dc..6bdb84a9f4 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -506,37 +506,24 @@ struct LegacyPointer[ @always_inline("nodebug") fn bitcast[ - new_type: AnyRegType, new_address_space: AddressSpace = address_space - ](self) -> LegacyPointer[new_type, new_address_space]: + new_type: AnyRegType = type, + /, + address_space: AddressSpace = Self.address_space, + ](self) -> LegacyPointer[new_type, address_space]: """Bitcasts a LegacyPointer to a different type. Parameters: new_type: The target type. - new_address_space: The address space of the result. + address_space: The address space of the result. Returns: A new LegacyPointer object with the specified type and the same address, as the original LegacyPointer. """ return __mlir_op.`pop.pointer.bitcast`[ - _type = LegacyPointer[new_type, new_address_space]._mlir_type, + _type = LegacyPointer[new_type, address_space]._mlir_type, ](self.address) - @always_inline("nodebug") - fn address_space_bitcast[ - new_address_space: AddressSpace - ](self) -> LegacyPointer[type, new_address_space]: - """Casts a LegacyPointer to a different address space. - - Parameters: - new_address_space: The address space. - - Returns: - A new LegacyPointer object with the specified type and the same address, - as the original LegacyPointer but located in a different address space. - """ - return self.bitcast[type, new_address_space]() - # ===------------------------------------------------------------------=== # # Comparisons # ===------------------------------------------------------------------=== # @@ -875,34 +862,21 @@ struct DTypePointer[ @always_inline("nodebug") fn bitcast[ - new_type: DType, new_address_space: AddressSpace = address_space - ](self) -> DTypePointer[new_type, new_address_space]: + new_type: DType = type, + /, + address_space: AddressSpace = Self.address_space, + ](self) -> DTypePointer[new_type, address_space]: """Bitcasts `DTypePointer` to a different dtype. Parameters: new_type: The target dtype. - new_address_space: The address space of the result. + address_space: The address space of the result. Returns: A new `DTypePointer` object with the specified dtype and the same address, as the original `DTypePointer`. """ - return self.address.bitcast[SIMD[new_type, 1], new_address_space]() - - @always_inline("nodebug") - fn address_space_bitcast[ - new_address_space: AddressSpace - ](self) -> DTypePointer[type, new_address_space]: - """Casts a Pointer to a different address space. - - Parameters: - new_address_space: The address space. - - Returns: - A new Pointer object with the specified type and the same address, - as the original Pointer but located in a different address space. - """ - return self.bitcast[type, new_address_space]() + return self.address.bitcast[SIMD[new_type, 1], address_space]() @always_inline("nodebug") fn _as_scalar_pointer(self) -> Pointer[Scalar[type], address_space]: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index f04fff4782..5d40c3fff4 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -157,37 +157,24 @@ struct UnsafePointer[ @always_inline("nodebug") fn bitcast[ - new_type: AnyType, new_address_space: AddressSpace = address_space - ](self) -> UnsafePointer[new_type, new_address_space]: + new_type: AnyType = T, + /, + address_space: AddressSpace = Self.address_space, + ](self) -> UnsafePointer[new_type, address_space]: """Bitcasts a UnsafePointer to a different type. Parameters: new_type: The target type. - new_address_space: The address space of the result. + address_space: The address space of the result. Returns: A new UnsafePointer object with the specified type and the same address, as the original UnsafePointer. """ return __mlir_op.`pop.pointer.bitcast`[ - _type = UnsafePointer[new_type, new_address_space]._mlir_type, + _type = UnsafePointer[new_type, address_space]._mlir_type, ](self.value) - @always_inline - fn address_space_bitcast[ - new_address_space: AddressSpace - ](self) -> UnsafePointer[T, new_address_space]: - """Bitcasts the pointer to a different address space. - - Parameters: - new_address_space: The address space of the result. - - Returns: - A new pointer with the same type and address, but a new address - space. - """ - return self.bitcast[T, new_address_space]() - @always_inline fn __int__(self) -> Int: """Returns the pointer address as an integer. From 6a00171d9f3f2ea00b096d3ac2b9c77c9b1527c5 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 13 Apr 2024 10:16:54 -0700 Subject: [PATCH 0155/2019] [mojo-stdlib] Remove global bitcast Int->Pointer functions. (#37645) This remove two extraneous global `bitcast` operations that are redundant with the constructors on the related Pointer types. Beyond being redundant, there is nothing in the old form that indicated anything about pointers happening: ``` a = bitcast[NoneType](1) # Before a = Pointer[NoneType](address=1) # After ``` This also removes the global bitcast function that is redundant with the bitcast method. MODULAR_ORIG_COMMIT_REV_ID: b3be5f7114d2de3e77037a8b64192d84e0831345 --- stdlib/src/memory/unsafe.mojo | 61 ----------------------------------- stdlib/src/os/atomic.mojo | 2 +- 2 files changed, 1 insertion(+), 62 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 6bdb84a9f4..3f89ad3974 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -56,67 +56,6 @@ fn _is_power_of_2(val: Int) -> Bool: # ===----------------------------------------------------------------------===# -@always_inline("nodebug") -fn bitcast[ - type: AnyRegType, address_space: AddressSpace = AddressSpace.GENERIC -](val: Int) -> Pointer[type, address_space]: - """Bitcasts an integer to a pointer. - - Parameters: - type: The target type. - address_space: The address space the pointer is in. - - Args: - val: The pointer address. - - Returns: - A new Pointer with the specified address. - """ - return __mlir_op.`pop.index_to_pointer`[ - _type = Pointer[type, address_space]._mlir_type - ](Scalar[DType.index](val).value) - - -@always_inline("nodebug") -fn bitcast[ - type: DType, address_space: AddressSpace = AddressSpace.GENERIC -](val: Int) -> DTypePointer[type, address_space]: - """Bitcasts an integer to a pointer. - - Parameters: - type: The target type. - address_space: The address space the pointer is in. - - Args: - val: The pointer address. - - Returns: - A new Pointer with the specified address. - """ - return bitcast[Scalar[type], address_space](val) - - -@always_inline("nodebug") -fn bitcast[ - new_type: AnyRegType, src_type: AnyRegType, address_space: AddressSpace -](ptr: Pointer[src_type, address_space]) -> Pointer[new_type, address_space]: - """Bitcasts a Pointer to a different type. - - Parameters: - new_type: The target type. - src_type: The source type. - address_space: The address space the pointer is in. - - Args: - ptr: The source pointer. - - Returns: - A new Pointer with the specified type and the same address, as the - original Pointer. - """ - return ptr.bitcast[new_type]() - - @always_inline("nodebug") fn bitcast[ new_type: DType, new_width: Int, src_type: DType, src_width: Int diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index a16a079c4c..3686e26429 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -100,7 +100,7 @@ struct Atomic[type: DType]: ordering = __mlir_attr.`#pop`, _type = __mlir_type[`!pop.scalar<`, type.value, `>`], ]( - bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]](ptr).address, + ptr.bitcast[__mlir_type[`!pop.scalar<`, type.value, `>`]]().address, rhs.value, ) From 111b004692f11ea69066bf641d16866db14909f9 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 13 Apr 2024 11:09:37 -0700 Subject: [PATCH 0156/2019] [mojo-stdlib] Remove address space support from `Dict` (#37651) Address spaces are really important for low-level types like the pointers and things like NDBuffer built on top of them, but aren't something that Mojo supports in full generality. In particular, argument conventions like 'borrowed' and 'inout' are implicitly references with address space zero. The consequence of this is that you can't delete one of these (because the self of `__del__` is always in address space zero) which means that address spaces are irrelevant to things using traits. It is theoretically possible we'll re-evaluate this in the future, but until then, lets not address-spacify everything: it just adds complexity. MODULAR_ORIG_COMMIT_REV_ID: 7ba0d6225a00cd36552092c8ff05ebb595d1d7ea --- stdlib/src/collections/dict.mojo | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 80f25817fa..92c0c49bf3 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -51,7 +51,6 @@ struct _DictEntryIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, - address_space: AddressSpace = AddressSpace.GENERIC, ]: """Iterator over immutable DictEntry references. @@ -60,7 +59,6 @@ struct _DictEntryIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the dictionary is mutable. dict_lifetime: The lifetime of the List - address_space: the address_space of the list """ alias imm_dict_lifetime = __mlir_attr[ @@ -103,7 +101,6 @@ struct _DictKeyIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, - address_space: AddressSpace = AddressSpace.GENERIC, ]: """Iterator over immutable Dict key references. @@ -112,19 +109,14 @@ struct _DictKeyIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List - address_space: The address space of the List """ alias imm_dict_lifetime = __mlir_attr[ `#lit.lifetime.mutcast<`, dict_lifetime, `> : !lit.lifetime<1>` ] - alias ref_type = Reference[ - K, __mlir_attr.`0: i1`, Self.imm_dict_lifetime, address_space - ] + alias ref_type = Reference[K, __mlir_attr.`0: i1`, Self.imm_dict_lifetime] - alias dict_entry_iter = _DictEntryIter[ - K, V, dict_mutability, dict_lifetime, address_space - ] + alias dict_entry_iter = _DictEntryIter[K, V, dict_mutability, dict_lifetime] var iter: Self.dict_entry_iter @@ -132,11 +124,7 @@ struct _DictKeyIter[ return self fn __next__(inout self) -> Self.ref_type: - var entry_ref = self.iter.__next__() - var anyptr = UnsafePointer.address_of(entry_ref[].key) - return anyptr.bitcast[ - address_space = Self.dict_entry_iter.address_space - ]()[] + return self.iter.__next__()[].key fn __len__(self) -> Int: return self.iter.__len__() @@ -148,7 +136,6 @@ struct _DictValueIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, - address_space: AddressSpace = AddressSpace.GENERIC, ]: """Iterator over Dict value references. These are mutable if the dict is mutable. @@ -158,24 +145,19 @@ struct _DictValueIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List - address_space: The address space of the List """ - alias ref_type = Reference[V, dict_mutability, dict_lifetime, address_space] + alias ref_type = Reference[V, dict_mutability, dict_lifetime] - var iter: _DictEntryIter[ - K, V, dict_mutability, dict_lifetime, address_space - ] + var iter: _DictEntryIter[K, V, dict_mutability, dict_lifetime] fn __iter__(self) -> Self: return self fn __next__(inout self) -> Self.ref_type: var entry_ref = self.iter.__next__() - # Cast through a pointer to grant additional mutability and switch - # address spaces out. - var anyptr = UnsafePointer.address_of(entry_ref[].value) - return anyptr.bitcast[address_space=address_space]()[] + # Cast through a pointer to grant additional mutability. + return UnsafePointer.address_of(entry_ref[].value)[] fn __len__(self) -> Int: return self.iter.__len__() From 38129b3f06a3234b722466b0679b8e36d5d80502 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Taei" Date: Sat, 13 Apr 2024 11:32:22 -0700 Subject: [PATCH 0157/2019] [******][gpu] Make id intrinsic with no side effects (#37480) - Add has_side_effect parameter to llvm_intrinsic to allow library level side_effects annotation. - Mark `gpu.id intrinsics as no side-effects, which fixes issue case in #37264 MODULAR_ORIG_COMMIT_REV_ID: f3c2cea150e0b91d7a6dba635b361ec88026cd3e --- stdlib/src/sys/intrinsics.mojo | 315 ++++++++++++++++++++++++++++----- 1 file changed, 270 insertions(+), 45 deletions(-) diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index ae3c7a7da2..4e1e831f8f 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -32,7 +32,9 @@ from memory.unsafe import DTypePointer @always_inline("nodebug") -fn llvm_intrinsic[intrin: StringLiteral, type: AnyRegType]() -> type: +fn llvm_intrinsic[ + intrin: StringLiteral, type: AnyRegType, has_side_effect: Bool = True +]() -> type: """Calls an LLVM intrinsic with no arguments. Calls an LLVM intrinsic with the name intrin and return type type. @@ -40,6 +42,7 @@ fn llvm_intrinsic[intrin: StringLiteral, type: AnyRegType]() -> type: Parameters: intrin: The name of the llvm intrinsic. type: The return type of the intrinsic. + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Returns: The result of calling the llvm intrinsic with no arguments. @@ -47,17 +50,42 @@ fn llvm_intrinsic[intrin: StringLiteral, type: AnyRegType]() -> type: @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]() + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + ]() + return rebind[type](None) + + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ]() return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=type, + ]() return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ]() @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, type: AnyRegType, T0: AnyRegType + intrin: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0) -> type: """Calls an LLVM intrinsic with one argument. @@ -68,6 +96,7 @@ fn llvm_intrinsic[ intrin: The name of the llvm intrinsic. type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: arg0: The argument to call the LLVM intrinsic with. The type of arg0 @@ -79,19 +108,40 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0) @always_inline("nodebug") fn llvm_intrinsic[ - intrin: StringLiteral, type: AnyRegType, T0: AnyRegType, T1: AnyRegType + intrin: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0, arg1: T1) -> type: """Calls an LLVM intrinsic with two arguments. @@ -103,6 +153,7 @@ fn llvm_intrinsic[ type: The return type of the intrinsic. T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: arg0: The first argument to call the LLVM intrinsic with. The type of @@ -116,13 +167,29 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1) return rebind[type](None) else: + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1) + return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1) @@ -133,6 +200,7 @@ fn llvm_intrinsic[ T0: AnyRegType, T1: AnyRegType, T2: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2) -> type: """Calls an LLVM intrinsic with three arguments. @@ -145,6 +213,7 @@ fn llvm_intrinsic[ T0: The type of the first argument to the intrinsic (arg0). T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: arg0: The first argument to call the LLVM intrinsic with. The type of @@ -161,13 +230,30 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1, arg2) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2) @@ -179,6 +265,7 @@ fn llvm_intrinsic[ T1: AnyRegType, T2: AnyRegType, T3: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3) -> type: """Calls an LLVM intrinsic with four arguments. @@ -192,6 +279,7 @@ fn llvm_intrinsic[ T1: The type of the second argument to the intrinsic (arg1). T2: The type of the third argument to the intrinsic (arg2). T3: The type of the fourth argument to the intrinsic (arg3). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: arg0: The first argument to call the LLVM intrinsic with. The type of @@ -210,13 +298,30 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1, arg2, arg3) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3) @@ -229,6 +334,7 @@ fn llvm_intrinsic[ T2: AnyRegType, T3: AnyRegType, T4: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> type: """Calls an LLVM intrinsic with five arguments. @@ -243,6 +349,8 @@ fn llvm_intrinsic[ T2: The type of the third argument to the intrinsic (arg2). T3: The type of the fourth argument to the intrinsic (arg3). T4: The type of the fifth argument to the intrinsic (arg4). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. + Args: arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. @@ -257,13 +365,30 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3, arg4 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3, arg4) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3, arg4) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1, arg2, arg3, arg4) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3, arg4) @@ -277,6 +402,7 @@ fn llvm_intrinsic[ T3: AnyRegType, T4: AnyRegType, T5: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: """Calls an LLVM intrinsic with six arguments. @@ -292,6 +418,8 @@ fn llvm_intrinsic[ T3: The type of the fourth argument to the intrinsic (arg3). T4: The type of the fifth argument to the intrinsic (arg4). T5: The type of the sixth argument to the intrinsic (arg5). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. + Args: arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. @@ -307,13 +435,31 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3, arg4, arg5) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3, arg4, arg5) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=type, + ](arg0, arg1, arg2, arg3, arg4, arg5) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3, arg4, arg5) @@ -328,6 +474,7 @@ fn llvm_intrinsic[ T4: AnyRegType, T5: AnyRegType, T6: AnyRegType, + has_side_effect: Bool = True, ](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: """Calls an LLVM intrinsic with seven arguments. @@ -344,6 +491,8 @@ fn llvm_intrinsic[ T4: The type of the fifth argument to the intrinsic (arg4). T5: The type of the sixth argument to the intrinsic (arg5). T6: The type of the seventh argument to the intrinsic (arg6). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. + Args: arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. @@ -360,13 +509,30 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3, arg4, arg5, arg6) @@ -382,6 +548,7 @@ fn llvm_intrinsic[ T5: AnyRegType, T6: AnyRegType, T7: AnyRegType, + has_side_effect: Bool = True, ]( arg0: T0, arg1: T1, @@ -408,6 +575,7 @@ fn llvm_intrinsic[ T5: The type of the sixth argument to the intrinsic (arg5). T6: The type of the seventh argument to the intrinsic (arg6). T7: The type of the eighth argument to the intrinsic (arg7). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. @@ -425,13 +593,30 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) @@ -448,6 +633,7 @@ fn llvm_intrinsic[ T6: AnyRegType, T7: AnyRegType, T8: AnyRegType, + has_side_effect: Bool = True, ]( arg0: T0, arg1: T1, @@ -476,6 +662,7 @@ fn llvm_intrinsic[ T6: The type of the seventh argument to the intrinsic (arg6). T7: The type of the eighth argument to the intrinsic (arg7). T8: The type of the ninth argument to the intrinsic (arg8). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. Args: arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. @@ -494,13 +681,31 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=type, + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) @@ -518,6 +723,7 @@ fn llvm_intrinsic[ T7: AnyRegType, T8: AnyRegType, T9: AnyRegType, + has_side_effect: Bool = True, ]( arg0: T0, arg1: T1, @@ -548,6 +754,8 @@ fn llvm_intrinsic[ T7: The type of the eighth argument to the intrinsic (arg7). T8: The type of the ninth argument to the intrinsic (arg8). T9: The type of the tenth argument to the intrinsic (arg9). + has_side_effect: If `True` the intrinsic will have side effects, otherwise its pure. + Args: arg0: The first argument to call the LLVM intrinsic with. The type of arg0 must be T0. @@ -567,13 +775,30 @@ fn llvm_intrinsic[ @parameter if _mlirtype_is_eq[type, NoneType](): - __mlir_op.`pop.call_llvm_intrinsic`[intrin = intrin.value, _type=None]( - arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 - ) + + @parameter + if has_side_effect: + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=None + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + return rebind[type](None) + __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, + _type=None, + hasSideEffects = __mlir_attr.false, + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) return rebind[type](None) else: + + @parameter + if has_side_effect: + return __mlir_op.`pop.call_llvm_intrinsic`[ + intrin = intrin.value, _type=type + ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) return __mlir_op.`pop.call_llvm_intrinsic`[ - intrin = intrin.value, _type=type + intrin = intrin.value, + _type=type, + hasSideEffects = __mlir_attr.false, ](arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) From 1d9417fbe72642879b2bf287c00135f682177fd3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 13 Apr 2024 11:49:30 -0700 Subject: [PATCH 0158/2019] [mojo-docs] Update changelog for pointer changes. (#37653) MODULAR_ORIG_COMMIT_REV_ID: 05f2ac3588aa5011119833ecd0c0dbb00e7fe952 --- docs/changelog.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2ed8cd651d..ca46494144 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -73,11 +73,6 @@ what we publish. - `Dict` now has a `update()` method to update keys/values from another `Dict`. -- `Reference` interoperates with unsafe code better: `AnyPointer` now has a - constructor that forms it from `Reference` directly (inferring element type - and address space). `AnyPointer` can convert to an immortal mutable - `Reference` with `yourptr[]`. - - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very @@ -99,25 +94,25 @@ what we publish. lead to a crash. You can work around this by initializing to a dummy value and overwriting later. This limitation only applies to top level variables, variables in functions work as they always have. -- The `AnyPointer` type has several changes, including: - 1) The element type can now be `AnyType`, it doesn't require `Movable`. +- `AnyPointer` got renamed to `UnsafePointer` and is now Mojo's preferred unsafe + pointer type. It has several enhancements, including: + 1) The element type can now be `AnyType`: it doesn't require `Movable`. 2) Because of this, the `take_value`, `emplace_value`, and `move_into` methods have been changed to be top-level functions, and were renamed to `move_from_pointee`, `initialize_pointee` and `move_pointee` respectively. 3) A new `destroy_pointee` function runs the destructor on the pointee. - 4) `AnyPointer` can be initialized from a `Reference` as mentioned above. - 5) It has some new methods like `address_space_bitcast`. + 4) `UnsafePointer` can be initialized directly from a `Reference` with + `UnsafePointer(someRef)` and can convert to an immortal reference with + `yourPointer[]`. Both infer element type and address space. +- All of the pointers got a pass of cleanup to make them more consistent, for + example the `unsafe.bitcast` global function is now a consistent `bitcast` + method on the pointers, which can convert element type and address space. - The `Reference` type has several changes, including: 1) It is now located in `memory.reference` instead of `memory.unsafe`. - 2) `Reference` now has an unsafe `address_space_bitcast` method like `Pointer`. - 3) The `destroy_element_unsafe` method has been removed, do this with - `AnyPointer/destroy_pointee`, which is more obviously unsafe. - 4) The `emplace_ref_unsafe` function has been removed in favor of - `AnyPointer/initialize_pointee`. - 5) The `offset` method has been removed, it was unsafe and belongs on - `AnyPointer`. -- `AnyPointer` was renamed to `UnsafePointer`. This is part of our continuing - effort to unify our pointer types in the Standard Library. + 2) `Reference` now has an unsafe `unsafe_bitcast` method like `UnsafePointer`. + 3) Several unsafe methods were removed, including `offset`, + `destroy_element_unsafe` and `emplace_ref_unsafe`. This is because + `Reference` is a safe type - use `UnsafePointer` to do unsafe operations. - The `mojo package` command no longer supports the `-D` flag. All compilation environment flags should be provided at the point of package use From d28717807abdf67b18a456e4012222594b1dc089 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 13 Apr 2024 22:28:40 -0700 Subject: [PATCH 0159/2019] [mojo-stdlib] Remove unneeded 'borrowed' specifiers. NFC. (#37667) This has been the default argument convention forever, there is no need to specify it. This leaves it in tests specifically checking it in the mojo testsuite. MODULAR_ORIG_COMMIT_REV_ID: dc90191df748a05cbc0a8eb06138aee48d045309 --- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/string.mojo | 2 +- stdlib/test/builtin/test_object.mojo | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 7cfacf914d..3428b6cdfa 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -78,7 +78,7 @@ struct Error(Stringable, Boolable): return Error {data: dest, loaded_length: -length} @always_inline("nodebug") - fn __init__(borrowed src: StringRef) -> Error: + fn __init__(src: StringRef) -> Error: """Construct an Error object with a given string ref. Args: diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 2e321433f1..8c2b43d6af 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -28,7 +28,7 @@ from utils import StringRef from utils.index import StaticIntTuple from utils.static_tuple import StaticTuple -from .io import _snprintf, _snprintf_scalar +from .io import _snprintf # ===----------------------------------------------------------------------===# # Utilties diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index a2fac471c7..36ff5285d6 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -98,10 +98,14 @@ def test_arithmetic_ops(): assert_true(lhs == concatted) +# These are all marked borrowed because 'object' doesn't support function +# types with owned arguments. def test_function(borrowed lhs, borrowed rhs) -> object: return lhs + rhs +# These are all marked borrowed because 'object' doesn't support function +# types with owned arguments. def test_function_raises(borrowed a) -> object: raise Error("Error from function type") @@ -125,15 +129,21 @@ def test_non_object_getattr(): print(e) +# These are all marked borrowed because 'object' doesn't support function +# types with owned arguments. def matrix_getitem(borrowed self, borrowed i) -> object: return self.value[i] +# These are all marked borrowed because 'object' doesn't support function +# types with owned arguments. def matrix_setitem(borrowed self, borrowed i, borrowed value) -> object: self.value[i] = value return None +# These are all marked borrowed because 'object' doesn't support function +# types with owned arguments. def matrix_append(borrowed self, borrowed value) -> object: var impl = self.value impl.append(value) From d774a5b03a814c99c203f8bab9a6d0761d782aeb Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 14 Apr 2024 10:16:44 -0700 Subject: [PATCH 0160/2019] [mojo-lang] Fix non-trivial register variadics. (#37671) VariadicList is implemented with AnyRegType so it can't invoke copy constructors. This means we can't use it for non-trivial types like PythonObject. Switch variadics of non-trivial register type to use VariadicListMem instead: we'd eventually like to remove VariadicList but are not quite ready for that. This fixes variadics with things like PythonObject and Fixes #37362 and Fixes https://github.com/modularml/mojo/issues/1941 MODULAR_ORIG_COMMIT_REV_ID: fd3cdc841773c009ccce94110e722961fa2a1b82 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index ca46494144..dc473d7c86 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -175,6 +175,9 @@ what we publish. - [#1924](https://github.com/modularml/mojo/issues/1924) JIT debugging on Mac has been fixed. +- [#1941](https://github.com/modularml/mojo/issues/1941) Mojo variadics don't + work with non-trivial register-only types. + - [#1963](https://github.com/modularml/mojo/issues/1963) `a!=0` is now parsed and formatted correctly by `mojo format`. From e04c70979997bba606cdd7de654d10f584818b18 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 14 Apr 2024 12:14:01 -0700 Subject: [PATCH 0161/2019] [mojo-stdlib] Move `_snprintf` onto new-packs, remove `_vec_fmt`. (#37674) This moves `_snprintf` off old-fangled `!kgen.pack` onto the new `!lit.ref.pack` used by `VariadicPack`. To do this, we use the new `kgen.pack.load` op to load all the elements when passing into the low level C snprintf call. While here, upgrade some stuff from LegacyPointer to UnsafePointer, eliminating a bunch of rebinds, and eliminating the need for _vec_fmt. We don't have unpacking yet, so we can't have things like `_vec_fmt` and this is simpler overall anyway. This is progress towards #36916 MODULAR_ORIG_COMMIT_REV_ID: f0dc4bff3bd108da945f0504452051ed9878d2ce --- stdlib/src/builtin/int.mojo | 5 ++- stdlib/src/builtin/io.mojo | 53 +++++++++++++++------------ stdlib/src/builtin/simd.mojo | 12 +++--- stdlib/src/builtin/string.mojo | 6 +-- stdlib/src/memory/unsafe_pointer.mojo | 18 +++++++++ stdlib/src/utils/index.mojo | 14 +++---- 6 files changed, 67 insertions(+), 41 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 349041aa5e..0771da2871 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -18,7 +18,8 @@ These are Mojo built-ins, so you don't need to import them. from collections.dict import KeyElement from builtin.hash import _hash_simd -from builtin.string import _calc_initial_buffer_size, _vec_fmt +from builtin.string import _calc_initial_buffer_size +from builtin.io import _snprintf from utils._visualizers import lldb_formatter_wrapping_type from utils.index import StaticIntTuple @@ -309,7 +310,7 @@ struct Int(Intable, Stringable, KeyElement, Boolable): var buf = String._buffer_type() var initial_buffer_size = _calc_initial_buffer_size(self) buf.reserve(initial_buffer_size) - buf.size += _vec_fmt(buf.data, initial_buffer_size, "%li", self.value) + buf.size += _snprintf(buf.data, initial_buffer_size, "%li", self.value) buf.size += 1 # for the null terminator. return buf^ diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 7dfd4b0765..b824a81a5f 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -120,12 +120,12 @@ fn _printf[*types: AnyRegType](fmt: StringLiteral, borrowed *arguments: *types): @no_inline fn _snprintf[ - *types: AnyRegType + *types: AnyType ]( - str: Pointer[Int8], + str: UnsafePointer[Int8], size: Int, fmt: StringLiteral, - borrowed *arguments: *types, + *arguments: *types, ) -> Int: """Writes a format string into an output pointer. @@ -138,6 +138,24 @@ fn _snprintf[ Returns: The number of bytes written into the output string. """ + + alias NoAnyType = rebind[__mlir_type.`!kgen.variadic`](types) + alias VariadicType = __mlir_attr[ + `#kgen.param.expr: !kgen.variadic`, + ] + # This is the !lit.ref.pack<:variadic> in memory. + var packInMemory = arguments._value + + # Cast the !lit.ref.pack to a !kgen.pack> + var ptrPack = UnsafePointer.address_of(packInMemory).bitcast[ + __mlir_type[ + `!kgen.pack<:variadic<`, AnyRegType, `> `, VariadicType, `>` + ] + ]()[] + var regPack = __mlir_op.`kgen.pack.load`(ptrPack) + return int( __mlir_op.`pop.external_call`[ func = "snprintf".value, @@ -149,25 +167,14 @@ fn _snprintf[ `) -> !pop.scalar`, ], _type=Int32, - ](str, size, fmt.data(), arguments) - ) - - -@no_inline -fn _snprintf_int( - buffer: Pointer[Int8], - size: Int, - x: Int, -) -> Int: - return _snprintf( - buffer, size, _get_dtype_printf_format[DType.index](), x.value + ](str, size, fmt.data(), regPack) ) @no_inline fn _snprintf_scalar[ type: DType -](buffer: Pointer[Int8], size: Int, x: Scalar[type],) -> Int: +](buffer: UnsafePointer[Int8], size: Int, x: Scalar[type],) -> Int: alias format = _get_dtype_printf_format[type]() @parameter @@ -194,7 +201,7 @@ fn _snprintf_scalar[ @no_inline -fn _float_repr(buffer: Pointer[Int8], size: Int, x: Float64) -> Int: +fn _float_repr(buffer: UnsafePointer[Int8], size: Int, x: Float64) -> Int: # Using `%.17g` with decimal check is equivalent to CPython's fallback path # when its more complex dtoa library (forked from # https://github.com/dtolnay/dtoa) is not available. @@ -207,17 +214,17 @@ fn _float_repr(buffer: Pointer[Int8], size: Int, x: Float64) -> Int: var p = buffer alias minus = ord("-") alias dot = ord(".") - if p.load() == minus: + if p[] == minus: p += 1 - while p.load() != 0 and isdigit(p.load()): + while p[] != 0 and isdigit(p[]): p += 1 - if p.load(): + if p[]: return n - p.store(dot) + p[] = dot p += 1 - p.store(ord("0")) + p[] = ord("0") p += 1 - p.store(0) + p[] = 0 return n + 2 diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 5260971c19..6491e26998 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -28,8 +28,8 @@ from utils._visualizers import lldb_formatter_wrapping_type from utils.static_tuple import StaticTuple from .dtype import _integral_type_of -from .io import _snprintf_scalar -from .string import _calc_initial_buffer_size, _vec_fmt +from .io import _snprintf_scalar, _snprintf +from .string import _calc_initial_buffer_size # ===------------------------------------------------------------------------===# # Type Aliases @@ -527,16 +527,16 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # Print an opening `[`. @parameter if size > 1: - buf.size += _vec_fmt(buf.data, 2, "[") + buf.size += _snprintf(buf.data, 2, "[") # Print each element. for i in range(size): var element = self[i] # Print separators between each element. if i != 0: - buf.size += _vec_fmt(buf.data + buf.size, 3, ", ") + buf.size += _snprintf(buf.data + buf.size, 3, ", ") buf.size += _snprintf_scalar[type]( - rebind[Pointer[Int8]](buf.data + buf.size), + buf.data + buf.size, _calc_initial_buffer_size(element), element, ) @@ -544,7 +544,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( # Print a closing `]`. @parameter if size > 1: - buf.size += _vec_fmt(buf.data + buf.size, 2, "]") + buf.size += _snprintf(buf.data + buf.size, 2, "]") buf.size += 1 # for the null terminator. return String(buf^) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 8c2b43d6af..787815af16 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -1129,14 +1129,14 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): @always_inline fn _vec_fmt[ - *types: AnyRegType + *types: AnyType ]( str: UnsafePointer[Int8], size: Int, fmt: StringLiteral, - borrowed *arguments: *types, + *arguments: *types, ) -> Int: - return _snprintf(rebind[Pointer[Int8]](str), size, fmt, arguments) + return _snprintf(str, size, fmt, arguments) fn _toggle_ascii_case(char: Int8) -> Int8: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 5d40c3fff4..9106e9f402 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -222,6 +222,24 @@ struct UnsafePointer[ """ return self + (-offset) + @always_inline + fn __iadd__(inout self, offset: Int): + """Add an offset to this pointer. + + Args: + offset: The offset index. + """ + self = Self(address=int(self) + offset * sizeof[T]()) + + @always_inline + fn __isub__(inout self, offset: Int): + """Subtract an offset from this pointer. + + Args: + offset: The offset index. + """ + self.__iadd__(-offset) + @always_inline("nodebug") fn __eq__(self, rhs: Self) -> Bool: """Returns True if the two pointers are equal. diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 51fbd5af0f..4b2fc8f96e 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -20,8 +20,8 @@ from utils.index import StaticIntTuple ``` """ -from builtin.io import _get_dtype_printf_format -from builtin.string import _calc_initial_buffer_size, _vec_fmt +from builtin.io import _get_dtype_printf_format, _snprintf +from builtin.string import _calc_initial_buffer_size from . import unroll from .static_tuple import StaticTuple @@ -640,12 +640,12 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): buf.reserve(initial_buffer_size) # Print an opening `(`. - buf.size += _vec_fmt(buf.data, 2, "(") + buf.size += _snprintf(buf.data, 2, "(") for i in range(size): # Print separators between each element. if i != 0: - buf.size += _vec_fmt(buf.data + buf.size, 3, ", ") - buf.size += _vec_fmt( + buf.size += _snprintf(buf.data + buf.size, 3, ", ") + buf.size += _snprintf( buf.data + buf.size, _calc_initial_buffer_size(self[i]), _get_dtype_printf_format[DType.index](), @@ -653,9 +653,9 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): ) # Single element tuples should be printed with a trailing comma. if size == 1: - buf.size += _vec_fmt(buf.data + buf.size, 2, ",") + buf.size += _snprintf(buf.data + buf.size, 2, ",") # Print a closing `)`. - buf.size += _vec_fmt(buf.data + buf.size, 2, ")") + buf.size += _snprintf(buf.data + buf.size, 2, ")") buf.size += 1 # for the null terminator. return buf^ From 8c90a038171ad833e40f7bad2f5073d9b6c8ddac Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 14 Apr 2024 12:26:29 -0700 Subject: [PATCH 0162/2019] [mojo-stdlib] Remove the `_LITRef` alias (#37675) This has been subsumed by `Reference[stuff]._mlir_type`. I apparently forgot to remove this when I removed all the uses. MODULAR_ORIG_COMMIT_REV_ID: cd1cd62c8152183a970203067048148e492f1264 --- stdlib/src/memory/reference.mojo | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 05ac8a94fc..05a3341d93 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -190,25 +190,6 @@ struct AddressSpace(EqualityComparable): # ===----------------------------------------------------------------------===# -# Helper to build !lit.ref types. -# TODO: parametric aliases would be nice. -struct _LITRef[ - element_type: AnyType, - elt_is_mutable: __mlir_type.i1, - lifetime: AnyLifetime[elt_is_mutable].type, - address_space: AddressSpace = AddressSpace.GENERIC, -]: - alias type = __mlir_type[ - `!lit.ref<`, - element_type, - `, `, - lifetime, - `, `, - address_space._value.value, - `>`, - ] - - @value @register_passable("trivial") struct Reference[ From 4c1dc5513d6df4c4d23df4cfbfec742fd712d0a0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 14 Apr 2024 13:42:57 -0700 Subject: [PATCH 0163/2019] [mojo-stdlib] Move `_printf` to new packs. (#37676) This refactors the stuff for working with `!lit.ref.pack` into a `_LITRefPackHelper` struct, allowing easier reuse by both printf wrappers, then adopts it in _printf. MODULAR_ORIG_COMMIT_REV_ID: 02acb5450d6bf0a2b6f138bfc14bdc0b8da4cfb3 --- stdlib/src/builtin/builtin_list.mojo | 71 +++++++++++++++++++++++++++- stdlib/src/builtin/io.mojo | 39 +++++++-------- stdlib/src/builtin/object.mojo | 2 +- 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 29b95b7c87..ebd158c39c 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -404,12 +404,81 @@ struct VariadicListMem[ # ===----------------------------------------------------------------------===# -# VariadicPack +# _LITRefPackHelper # ===----------------------------------------------------------------------===# + alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] +@value +struct _LITRefPackHelper[ + element_trait: _AnyTypeMetaType, + *element_types: element_trait, + is_mutable: __mlir_type.i1, + lifetime: AnyLifetime[is_mutable].type, + address_space: __mlir_type.index, +]: + """This struct mirrors the !lit.ref.pack type, and provides aliases and + methods that are useful for working with it.""" + + alias _mlir_type = __mlir_type[ + `!lit.ref.pack<:variadic<`, + element_trait, + `> `, + element_types, + `, `, + lifetime, + `, `, + +address_space, + `>`, + ] + + var storage: Self._mlir_type + + # This is the element_types list lowered to `variadic` type for kgen. + alias _kgen_element_types = rebind[ + __mlir_type.`!kgen.variadic` + ](Self.element_types) + + # Use variadic_ptr_map to construct the type list of the !kgen.pack that the + # !lit.ref.pack will lower to. It exposes the pointers introduced by the + # references. + alias _variadic_pointer_types = __mlir_attr[ + `#kgen.param.expr: !kgen.variadic`, + ] + + # This is the !kgen.pack type with pointer elements. + alias kgen_pack_with_pointer_type = __mlir_type[ + `!kgen.pack<:variadic `, Self._variadic_pointer_types, `>` + ] + + # This rebinds `in_pack` to the equivalent `!kgen.pack` with kgen pointers. + fn get_as_kgen_pack(self) -> Self.kgen_pack_with_pointer_type: + return rebind[Self.kgen_pack_with_pointer_type](self.storage) + + # This is the `!kgen.pack` type that happens if one loads all the elements + # of the pack. + alias loaded_kgen_pack_type = __mlir_type[ + `!kgen.pack<:variadic `, Self._kgen_element_types, `>` + ] + + # This returns the stored KGEN pack after loading all of the elements. + # FIXME(37129): This doesn't actually work because vtables aren't getting + # removed from TypeConstants correctly. + fn get_loaded_kgen_pack(self) -> Self.loaded_kgen_pack_type: + return rebind[Self.loaded_kgen_pack_type]( + __mlir_op.`kgen.pack.load`(self.get_as_kgen_pack()) + ) + + +# ===----------------------------------------------------------------------===# +# VariadicPack +# ===----------------------------------------------------------------------===# + + @register_passable struct VariadicPack[ elt_is_mutable: __mlir_type.i1, diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index b824a81a5f..d773f1de32 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -19,6 +19,7 @@ from sys import external_call from sys.info import bitwidthof, os_is_windows, triple_is_nvidia_cuda from builtin.dtype import _get_dtype_printf_format +from builtin.builtin_list import _LITRefPackHelper from memory.unsafe import Pointer from utils import StringRef, unroll @@ -99,7 +100,16 @@ fn _flush(): @no_inline -fn _printf[*types: AnyRegType](fmt: StringLiteral, borrowed *arguments: *types): +fn _printf[*types: AnyType](fmt: StringLiteral, *arguments: *types): + # The argument pack will contain references for each value in the pack, + # but we want to pass their values directly into the C snprintf call. Load + # all the members of the pack. + var kgen_pack = _LITRefPackHelper(arguments._value).get_as_kgen_pack() + + # FIXME(37129): Cannot use get_loaded_kgen_pack because vtables on types + # aren't stripped off correctly. + var loaded_pack = __mlir_op.`kgen.pack.load`(kgen_pack) + with _fdopen(_fdopen.STDOUT) as fd: _ = __mlir_op.`pop.external_call`[ func = "KGEN_CompilerRT_fprintf".value, @@ -110,7 +120,7 @@ fn _printf[*types: AnyRegType](fmt: StringLiteral, borrowed *arguments: *types): `) -> !pop.scalar`, ], _type=Int32, - ](fd, fmt.data(), arguments) + ](fd, fmt.data(), loaded_pack) # ===----------------------------------------------------------------------=== # @@ -138,23 +148,14 @@ fn _snprintf[ Returns: The number of bytes written into the output string. """ + # The argument pack will contain references for each value in the pack, + # but we want to pass their values directly into the C snprintf call. Load + # all the members of the pack. + var kgen_pack = _LITRefPackHelper(arguments._value).get_as_kgen_pack() - alias NoAnyType = rebind[__mlir_type.`!kgen.variadic`](types) - alias VariadicType = __mlir_attr[ - `#kgen.param.expr: !kgen.variadic`, - ] - # This is the !lit.ref.pack<:variadic> in memory. - var packInMemory = arguments._value - - # Cast the !lit.ref.pack to a !kgen.pack> - var ptrPack = UnsafePointer.address_of(packInMemory).bitcast[ - __mlir_type[ - `!kgen.pack<:variadic<`, AnyRegType, `> `, VariadicType, `>` - ] - ]()[] - var regPack = __mlir_op.`kgen.pack.load`(ptrPack) + # FIXME(37129): Cannot use get_loaded_kgen_pack because vtables on types + # aren't stripped off correctly. + var loaded_pack = __mlir_op.`kgen.pack.load`(kgen_pack) return int( __mlir_op.`pop.external_call`[ @@ -167,7 +168,7 @@ fn _snprintf[ `) -> !pop.scalar`, ], _type=Int32, - ](str, size, fmt.data(), regPack) + ](str, size, fmt.data(), loaded_pack) ) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 3201fad3da..f6bf651330 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1716,7 +1716,7 @@ struct object(IntableRaising, Boolable, Stringable): object(self._value.get_list_element(j)).print() _put("]") elif self._value.is_func(): - _printf("function at %p", self._value.get_as_func().value.address) + _printf("function at %p", self._value.get_as_func().value) else: _put("{") var ptr = self._value.get_obj_attrs_ptr() From f8e448ff2a913ef117afea9fd77b7aae0ad31478 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 14 Apr 2024 18:00:56 -0700 Subject: [PATCH 0164/2019] [mojo-stdlib] Rewrite ListLiteral to wrap `Tuple` (#37681) This rewrites the `ListLiteral` type to wrap a `Tuple` instead of a kgen.pack directly. This enables it to have proper values semantics for the contained operation, and work with any `CollectionElement` type, instead of just `AnyRegType` types. This also moves one random testcase off borrowed packs. This is the last known thing using legacy-packs, so this fixes #36916! MODULAR_ORIG_COMMIT_REV_ID: fcead2d1981eb29a46e53db9ac97e43be917de2b --- stdlib/src/builtin/builtin_list.mojo | 22 ++++++++++------------ stdlib/src/builtin/object.mojo | 19 +++++++++---------- stdlib/src/builtin/tuple.mojo | 20 ++++++++++++++++++-- stdlib/src/python/object.mojo | 22 ++++++++++------------ 4 files changed, 47 insertions(+), 36 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index ebd158c39c..632293f203 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -23,8 +23,8 @@ from memory.unsafe_pointer import * # ===----------------------------------------------------------------------===# -@register_passable -struct ListLiteral[*Ts: AnyRegType](Sized): +@value +struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): """The type of a literal heterogenous list expression. A list consists of zero or more values, separated by commas. @@ -33,17 +33,17 @@ struct ListLiteral[*Ts: AnyRegType](Sized): Ts: The type of the elements. """ - var storage: __mlir_type[`!kgen.pack<`, Ts, `>`] + var storage: Tuple[Ts] """The underlying storage for the list.""" @always_inline("nodebug") - fn __init__(inout self, borrowed *args: *Ts): + fn __init__(inout self, *args: *Ts): """Construct the list literal from the given values. Args: args: The init values. """ - self.storage = args + self.storage = Tuple(storage=args) @always_inline("nodebug") fn __len__(self) -> Int: @@ -52,10 +52,10 @@ struct ListLiteral[*Ts: AnyRegType](Sized): Returns: The length of this ListLiteral. """ - return __mlir_op.`pop.variadic.size`(Ts) + return len(self.storage) @always_inline("nodebug") - fn get[i: Int, T: AnyRegType](self) -> T: + fn get[i: Int, T: CollectionElement](self) -> T: """Get a list element at the given index. Parameters: @@ -65,9 +65,7 @@ struct ListLiteral[*Ts: AnyRegType](Sized): Returns: The element at the given index. """ - return rebind[T]( - __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) - ) + return self.storage.get[i, T]() # ===----------------------------------------------------------------------===# @@ -413,11 +411,11 @@ alias _AnyTypeMetaType = __mlir_type[`!lit.anytrait<`, AnyType, `>`] @value struct _LITRefPackHelper[ - element_trait: _AnyTypeMetaType, - *element_types: element_trait, is_mutable: __mlir_type.i1, lifetime: AnyLifetime[is_mutable].type, address_space: __mlir_type.index, + element_trait: _AnyTypeMetaType, + *element_types: element_trait, ]: """This struct mirrors the !lit.ref.pack type, and provides aliases and methods that are useful for working with it.""" diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index f6bf651330..a3b10c9d53 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -17,7 +17,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import Dict, List from os.atomic import Atomic -from sys.intrinsics import _mlirtype_is_eq +from sys.intrinsics import _type_is_eq from memory import memcmp, memcpy from memory.unsafe import DTypePointer, Pointer @@ -807,7 +807,7 @@ struct object(IntableRaising, Boolable, Stringable): self._value = impl @always_inline - fn __init__[*Ts: AnyRegType](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): """Initializes the object from a list literal. Parameters: @@ -817,32 +817,31 @@ struct object(IntableRaising, Boolable, Stringable): value: The list value. """ self._value = _RefCountedListRef() - alias types = VariadicList(Ts) @parameter @always_inline fn append[i: Int](): # We need to rebind the element to one we know how to convert from. # FIXME: This doesn't handle implicit conversions or nested lists. - alias T = types[i] + alias T = Ts[i] @parameter - if _mlirtype_is_eq[T, Int](): + if _type_is_eq[T, Int](): self._append(value.get[i, Int]()) - elif _mlirtype_is_eq[T, Float64](): + elif _type_is_eq[T, Float64](): self._append(value.get[i, Float64]()) - elif _mlirtype_is_eq[T, Bool](): + elif _type_is_eq[T, Bool](): self._append(value.get[i, Bool]()) - elif _mlirtype_is_eq[T, StringRef](): + elif _type_is_eq[T, StringRef](): self._append(value.get[i, StringRef]()) - elif _mlirtype_is_eq[T, StringLiteral](): + elif _type_is_eq[T, StringLiteral](): self._append(value.get[i, StringLiteral]()) else: constrained[ False, "cannot convert nested list element to object" ]() - unroll[append, len(types)]() + unroll[append, len(VariadicList(Ts))]() @always_inline fn __init__(inout self, func: Self.nullary_function): diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index df0d5fe9df..79081ead77 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -32,13 +32,15 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): element_types: The elements type. """ - var storage: __mlir_type[ + alias _mlir_type = __mlir_type[ `!kgen.pack<:!kgen.variadic<`, CollectionElement, `> `, +element_types, `>`, ] + + var storage: Self._mlir_type """The underlying storage for the tuple.""" @always_inline("nodebug") @@ -48,6 +50,19 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): Args: args: Initial values. """ + self = Self(storage=args) + + @always_inline("nodebug") + fn __init__( + inout self, + *, + storage: VariadicPack[_, _, CollectionElement, element_types], + ): + """Construct the tuple from a low-level internal representation. + + Args: + storage: The variadic pack storage to construct from. + """ # Mark 'storage' as being initialized so we can work on it. __mlir_op.`lit.ownership.mark_initialized`( __get_mvalue_as_litref(self.storage) @@ -58,7 +73,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO: We could be fancier and take the values out of an owned # pack. For now just keep everything simple and copy the element. initialize_pointee( - UnsafePointer(self._refitem__[idx]()), args.get_element[idx]()[] + UnsafePointer(self._refitem__[idx]()), + storage.get_element[idx]()[], ) unroll[initialize_elt, Self.__len__()]() diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index 6db0eb3858..ca16c8c7f5 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -19,7 +19,7 @@ from python.object import PythonObject ``` """ -from sys.intrinsics import _mlirtype_is_eq, _type_is_eq +from sys.intrinsics import _type_is_eq from utils import StringRef, unroll @@ -209,7 +209,7 @@ struct PythonObject( self.py_object = cpython.toPython(string._strref_dangerous()) string._strref_keepalive() - fn __init__[*Ts: AnyRegType](inout self, value: ListLiteral[Ts]): + fn __init__[*Ts: CollectionElement](inout self, value: ListLiteral[Ts]): """Initialize the object from a list literal. Parameters: @@ -219,28 +219,26 @@ struct PythonObject( value: The list value. """ var cpython = _get_global_python_itf().cpython() - alias types = VariadicList(Ts) - alias length = len(types) - self.py_object = cpython.PyList_New(length) + self.py_object = cpython.PyList_New(len(value)) @parameter fn fill[i: Int](): # We need to rebind the element to one we know how to convert from. # FIXME: This doesn't handle implicit conversions or nested lists. - alias T = types[i] + alias T = Ts[i] var obj: PythonObject @parameter - if _mlirtype_is_eq[T, Int](): + if _type_is_eq[T, Int](): obj = value.get[i, Int]() - elif _mlirtype_is_eq[T, Float64](): + elif _type_is_eq[T, Float64](): obj = value.get[i, Float64]() - elif _mlirtype_is_eq[T, Bool](): + elif _type_is_eq[T, Bool](): obj = value.get[i, Bool]() - elif _mlirtype_is_eq[T, StringRef](): + elif _type_is_eq[T, StringRef](): obj = value.get[i, StringRef]() - elif _mlirtype_is_eq[T, StringLiteral](): + elif _type_is_eq[T, StringLiteral](): obj = value.get[i, StringLiteral]() else: obj = PythonObject(0) @@ -250,7 +248,7 @@ struct PythonObject( cpython.Py_IncRef(obj.py_object) _ = cpython.PyList_SetItem(self.py_object, i, obj.py_object) - unroll[fill, len(types)]() + unroll[fill, len(VariadicList(Ts))]() fn __init__[*Ts: CollectionElement](inout self, value: Tuple[Ts]): """Initialize the object from a tuple literal. From 55696c6d9faff90c8e89304626861a9745f40a3c Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 15 Apr 2024 07:37:15 -0700 Subject: [PATCH 0165/2019] [Stdlib] Fix dispatch for Int.MIN (#37694) There was an unhandled case in the _min_finite for index dtype. This fixes that and adds a test case. MODULAR_ORIG_COMMIT_REV_ID: 94bb64b19bb89edfd11813bf2b99d173315728c6 --- stdlib/src/builtin/simd.mojo | 17 +++++++++++------ stdlib/test/builtin/test_int.mojo | 7 +++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 6491e26998..94591c0462 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2459,6 +2459,11 @@ fn _neginf[type: DType]() -> Scalar[type]: # ===----------------------------------------------------------------------===# +@always_inline("nodebug") +fn _is_32_bit_system() -> Bool: + return sizeof[DType.index]() == sizeof[DType.int32]() + + @always_inline fn _max_finite[type: DType]() -> Scalar[type]: """Returns the maximum finite value of type. @@ -2480,16 +2485,14 @@ fn _max_finite[type: DType]() -> Scalar[type]: return 32767 elif type == DType.uint16: return 65535 - elif type == DType.int32 or ( - type == DType.index and sizeof[DType.index]() == sizeof[DType.int32]() - ): + elif type == DType.int32 or (type == DType.index and _is_32_bit_system()): return 2147483647 elif type == DType.uint32: return 4294967295 elif type == DType.float32: return 3.40282346638528859812e38 elif type == DType.int64 or ( - type == DType.index and sizeof[DType.index]() == sizeof[DType.int64]() + type == DType.index and not _is_32_bit_system() ): return 9223372036854775807 elif type == DType.uint64: @@ -2527,11 +2530,13 @@ fn _min_finite[type: DType]() -> Scalar[type]: return -128 elif type == DType.int16: return -32768 - elif type == DType.int32: + elif type == DType.int32 or (type == DType.index and _is_32_bit_system()): return -2147483648 elif type == DType.float32: return -_max_finite[type]() - elif type == DType.int64: + elif type == DType.int64 or ( + type == DType.index and not _is_32_bit_system() + ): return -9223372036854775808 elif type == DType.float64: return -_max_finite[type]() diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 5e0b44ace6..389c30838d 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -13,6 +13,7 @@ # RUN: %mojo %s from testing import assert_equal +from sys.info import bitwidthof def test_constructors(): @@ -20,6 +21,11 @@ def test_constructors(): var i2 = Int(Int(5)) # Constructible from Int +def test_properties(): + assert_equal(Int.MAX, (1 << bitwidthof[DType.index]() - 1) - 1) + assert_equal(Int.MIN, -(1 << bitwidthof[DType.index]() - 1)) + + def test_add(): assert_equal(6, Int(3) + Int(3)) @@ -65,6 +71,7 @@ def test_mod(): def main(): test_constructors() + test_properties() test_add() test_sub() test_div() From a6fe2d616fa665ace647d96c36763cf776607531 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 15 Apr 2024 08:25:19 -0700 Subject: [PATCH 0166/2019] [Stdlib] Improve the Atomic.compare_exchange functionality (#37696) This improves the Atomic.compare_exchange by more closely following the C semantics and writing to expected the atomic value. A subsequent PR would cleanup the API a bit more. Closes #36977 and #36978 MODULAR_ORIG_COMMIT_REV_ID: eab91de7bd766cec22a4ff3d183f7ad72ad5a6a6 --- stdlib/src/os/atomic.mojo | 41 ++++++++++++++++++++++++--------- stdlib/test/os/test_atomic.mojo | 26 ++++++++++++++++++++- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 3686e26429..0e39d9dcfa 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -177,13 +177,22 @@ struct Atomic[type: DType]: _ = self.fetch_sub(rhs) @always_inline - fn _compare_exchange_weak( - inout self, expected: Scalar[type], desired: Scalar[type] + fn compare_exchange_weak( + inout self, inout expected: Scalar[type], desired: Scalar[type] ) -> Bool: - constrained[ - type.is_integral() or type.is_floating_point(), - "the input type must be arithmetic", - ]() + """Atomically compares the self value with that of the expected value. + If the values are equal, then the self value is replaced with the + desired value and True is returned. Otherwise, False is returned the + the expected value is rewritten with the self value. + + Args: + expected: The expected value. + desired: The desired value. + + Returns: + True if self == expected and False otherwise. + """ + constrained[type.is_numeric(), "the input type must be arithmetic"]() @parameter if type.is_integral(): @@ -197,9 +206,14 @@ struct Atomic[type: DType]: expected.value, desired.value, ) - return __mlir_op.`kgen.struct.extract`[ - index = __mlir_attr.`1:index` - ](cmpxchg_res) + var ok = Bool( + __mlir_op.`kgen.struct.extract`[index = __mlir_attr.`1:index`]( + cmpxchg_res + ) + ) + if not ok: + expected = self.load() + return ok # For the floating point case, we need to bitcast the floating point # values to their integral representation and perform the atomic @@ -221,9 +235,14 @@ struct Atomic[type: DType]: expected_integral.value, desired_integral.value, ) - return __mlir_op.`kgen.struct.extract`[index = __mlir_attr.`1:index`]( - cmpxchg_res + var ok = Bool( + __mlir_op.`kgen.struct.extract`[index = __mlir_attr.`1:index`]( + cmpxchg_res + ) ) + if not ok: + expected = self.load() + return ok @always_inline fn max(inout self, rhs: Scalar[type]): diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 3430b8efe2..41d0c736f9 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s | FileCheck %s from os.atomic import Atomic -from testing import assert_equal +from testing import assert_equal, assert_true, assert_false # CHECK-LABEL: test_atomic @@ -109,7 +109,31 @@ def test_atomic_move_constructor(): assert_equal(atom2.value, 0) +def test_compare_exchange_weak(): + var atom: Atomic[DType.int64] = 3 + var expected = Int64(3) + var desired = Int64(3) + var ok = atom.compare_exchange_weak(expected, desired) + + assert_equal(expected, 3) + assert_true(ok) + + expected = Int64(4) + ok = atom.compare_exchange_weak(expected, desired) + + assert_equal(expected, 3) + assert_false(ok) + + expected = Int64(4) + desired = Int64(6) + ok = atom.compare_exchange_weak(expected, desired) + + assert_equal(expected, 3) + assert_false(ok) + + def main(): test_atomic() test_atomic_floating_point() test_atomic_move_constructor() + test_compare_exchange_weak() From 9ad8a836d050e2081f33593157dd04d56e56cef9 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Mon, 15 Apr 2024 08:25:28 -0700 Subject: [PATCH 0167/2019] [Stdlib] Add float16 max / min value support (#37699) Before this would just return an error since it was not implemented. Closes #37697 MODULAR_ORIG_COMMIT_REV_ID: 4236e5b71bb4696002acf87f5f9623b99109c76d --- stdlib/src/builtin/simd.mojo | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 94591c0462..544ff94df7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -2489,18 +2489,20 @@ fn _max_finite[type: DType]() -> Scalar[type]: return 2147483647 elif type == DType.uint32: return 4294967295 - elif type == DType.float32: - return 3.40282346638528859812e38 elif type == DType.int64 or ( type == DType.index and not _is_32_bit_system() ): return 9223372036854775807 elif type == DType.uint64: return 18446744073709551615 - elif type == DType.float64: - return 1.79769313486231570815e308 + elif type == DType.float16: + return 65504 elif type == DType.bfloat16: return 3.38953139e38 + elif type == DType.float32: + return 3.40282346638528859812e38 + elif type == DType.float64: + return 1.79769313486231570815e308 else: constrained[False, "max_finite() called on unsupported type"]() return 0 @@ -2532,15 +2534,11 @@ fn _min_finite[type: DType]() -> Scalar[type]: return -32768 elif type == DType.int32 or (type == DType.index and _is_32_bit_system()): return -2147483648 - elif type == DType.float32: - return -_max_finite[type]() elif type == DType.int64 or ( type == DType.index and not _is_32_bit_system() ): return -9223372036854775808 - elif type == DType.float64: - return -_max_finite[type]() - elif type == DType.bfloat16: + elif type.is_floating_point(): return -_max_finite[type]() else: constrained[False, "min_finite() called on unsupported type"]() From edbf9cd9665bebaafbec1193e8fd6533ce4eca58 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Mon, 15 Apr 2024 10:19:01 -0700 Subject: [PATCH 0168/2019] [stdlib] Migrating Pointer to UnsafePointer - 3/n (#37654) Converting Pointer to UnsafePointer in the following files: - _numerics.mojo - stringref.mojo - file.mojo - _linux_x86.mojo - _linux_aarch64.mojo - object.mojo - _cpython.mojo Supporting [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 37a3e9657556c796888f8063637f7009dc4e8f4b --- stdlib/src/builtin/error.mojo | 4 +-- stdlib/src/builtin/file.mojo | 26 +++++++++--------- stdlib/src/builtin/object.mojo | 44 ++++++++++++++++++------------- stdlib/src/memory/unsafe.mojo | 9 +++++++ stdlib/src/os/_linux_aarch64.mojo | 4 +-- stdlib/src/os/_linux_x86.mojo | 4 +-- stdlib/src/python/_cpython.mojo | 38 ++++++++++++++------------ stdlib/src/utils/_numerics.mojo | 7 ++--- stdlib/src/utils/stringref.mojo | 12 ++++----- 9 files changed, 85 insertions(+), 63 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 3428b6cdfa..2c87671df8 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -72,7 +72,7 @@ struct Error(Stringable, Boolable): The constructed Error object. """ var length = len(src) - var dest = Pointer[Int8].alloc(length + 1) + var dest = UnsafePointer[Int8].alloc(length + 1) memcpy(dest, src._as_ptr(), length) dest[length] = 0 return Error {data: dest, loaded_length: -length} @@ -107,7 +107,7 @@ struct Error(Stringable, Boolable): """ if existing.loaded_length < 0: var length = -existing.loaded_length - var dest = Pointer[Int8].alloc(length + 1) + var dest = UnsafePointer[Int8].alloc(length + 1) memcpy(dest, existing.data, length) dest[length] = 0 return Error {data: dest, loaded_length: existing.loaded_length} diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index b7b577146e..50fedf354b 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -35,7 +35,7 @@ from os import PathLike from sys import external_call from memory.reference import AddressSpace -from memory.unsafe import DTypePointer, Pointer +from memory.unsafe import DTypePointer @register_passable @@ -93,7 +93,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() var handle = external_call[ "KGEN_CompilerRT_IO_FileOpen", DTypePointer[DType.invalid] - ](path, mode, Pointer.address_of(err_msg)) + ](path, mode, UnsafePointer.address_of(err_msg)) if err_msg: self.handle = DTypePointer[DType.invalid]() @@ -116,7 +116,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() external_call["KGEN_CompilerRT_IO_FileClose", NoneType]( - self.handle, Pointer.address_of(err_msg) + self.handle, UnsafePointer.address_of(err_msg) ) if err_msg: @@ -149,10 +149,12 @@ struct FileHandle: var size_copy: Int64 = size var err_msg = _OwnedStringRef() - var buf = external_call["KGEN_CompilerRT_IO_FileRead", Pointer[Int8]]( + var buf = external_call[ + "KGEN_CompilerRT_IO_FileRead", UnsafePointer[Int8] + ]( self.handle, - Pointer.address_of(size_copy), - Pointer.address_of(err_msg), + UnsafePointer.address_of(size_copy), + UnsafePointer.address_of(err_msg), ) if err_msg: @@ -177,18 +179,18 @@ struct FileHandle: var err_msg = _OwnedStringRef() var buf = external_call[ - "KGEN_CompilerRT_IO_FileReadBytes", Pointer[Int8] + "KGEN_CompilerRT_IO_FileReadBytes", UnsafePointer[Int8] ]( self.handle, - Pointer.address_of(size_copy), - Pointer.address_of(err_msg), + UnsafePointer.address_of(size_copy), + UnsafePointer.address_of(err_msg), ) if err_msg: raise (err_msg^).consume_as_error() var list = List[Int8](capacity=int(size_copy)) - var list_ptr = Pointer[Int8](address=int(list.data)) + var list_ptr = UnsafePointer[Int8](address=int(list.data)) # Initialize the List elements and set the initialized size memcpy(list_ptr, buf, int(size_copy)) @@ -214,7 +216,7 @@ struct FileHandle: var err_msg = _OwnedStringRef() var pos = external_call["KGEN_CompilerRT_IO_FileSeek", UInt64]( - self.handle, offset, Pointer.address_of(err_msg) + self.handle, offset, UnsafePointer.address_of(err_msg) ) if err_msg: @@ -268,7 +270,7 @@ struct FileHandle: self.handle, ptr.address, len, - Pointer.address_of(err_msg), + UnsafePointer.address_of(err_msg), ) if err_msg: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index a3b10c9d53..d58c359195 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -20,7 +20,7 @@ from os.atomic import Atomic from sys.intrinsics import _type_is_eq from memory import memcmp, memcpy -from memory.unsafe import DTypePointer, Pointer +from memory.unsafe import DTypePointer, LegacyPointer from utils import StringRef from utils.loop import unroll @@ -56,15 +56,19 @@ struct _ImmutableString: pointer and integer pair. Memory will be dynamically allocated. """ - var data: Pointer[Int8] + var data: LegacyPointer[Int8] """The pointer to the beginning of the string contents. It is not null-terminated.""" var length: Int """The length of the string.""" @always_inline - fn __init__(data: Pointer[Int8], length: Int) -> Self: - return Self {data: data, length: length} + fn __init__(data: LegacyPointer[Int8], length: Int) -> Self: + return Self {data: data.address, length: length} + + @always_inline + fn __init__(data: UnsafePointer[Int8], length: Int) -> Self: + return Self {data: data.value, length: length} @always_inline fn string_compare(self, rhs: _ImmutableString) -> Int: @@ -96,13 +100,13 @@ struct _RefCountedList: @register_passable("trivial") struct _RefCountedListRef: # FIXME(#3335): Use indirection to avoid a recursive struct definition. - var lst: Pointer[NoneType] + var lst: UnsafePointer[NoneType] """The reference to the list.""" @always_inline fn __init__() -> Self: - var ptr = Pointer[_RefCountedList].alloc(1) - __get_address_as_uninit_lvalue(ptr.address) = _RefCountedList() + var ptr = UnsafePointer[_RefCountedList].alloc(1) + __get_address_as_uninit_lvalue(ptr.value) = _RefCountedList() return Self {lst: ptr.bitcast[NoneType]()} @always_inline @@ -117,7 +121,7 @@ struct _RefCountedListRef: return # Run the destructor on the list elements and then destroy the list. - var list = __get_address_as_owned_value(ptr.address).impl + var list = __get_address_as_owned_value(ptr.value).impl for i in range(len(list)): list[i].destroy() ptr.free() @@ -191,13 +195,13 @@ struct Attr: struct _RefCountedAttrsDictRef: # FIXME(#3335): Use indirection to avoid a recursive struct definition. # FIXME(#12604): Distinguish this type from _RefCountedListRef. - var attrs: Pointer[Int8] + var attrs: UnsafePointer[Int8] """The reference to the dictionary.""" @always_inline fn __init__(values: VariadicListMem[Attr, _, _]) -> Self: - var ptr = Pointer[_RefCountedAttrsDict].alloc(1) - __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() + var ptr = UnsafePointer[_RefCountedAttrsDict].alloc(1) + __get_address_as_uninit_lvalue(ptr.value) = _RefCountedAttrsDict() # Elements can only be added on construction. for i in range(len(values)): ptr[].impl._insert(values[i].key, values[i].value._value.copy()) @@ -224,13 +228,13 @@ struct _Function: # The MLIR function type has two arguments: # 1. The self value, or the single argument. # 2. None, or an additional argument. - var value: Pointer[Int16] + var value: UnsafePointer[Int16] """The function pointer.""" @always_inline fn __init__[FnT: AnyRegType](value: FnT) -> Self: # FIXME: No "pointer bitcast" for signature function pointers. - var f = Pointer[Int16]() + var f = UnsafePointer[Int16]() Reference(f).get_legacy_pointer().bitcast[FnT]().store(value) return Self {value: f} @@ -400,7 +404,7 @@ struct _ObjectImpl(CollectionElement, Stringable): if self.is_str(): var str = self.get_as_string() var impl = _ImmutableString( - Pointer[Int8].alloc(str.length), str.length + UnsafePointer[Int8].alloc(str.length), str.length ) memcpy(impl.data, DTypePointer[DType.int8](str.data), str.length) return impl @@ -647,7 +651,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===------------------------------------------------------------------=== # @always_inline - fn get_list_ptr(self) -> Pointer[_RefCountedList]: + fn get_list_ptr(self) -> UnsafePointer[_RefCountedList]: return self.get_as_list().lst.bitcast[_RefCountedList]() @always_inline @@ -672,7 +676,7 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===------------------------------------------------------------------=== # @always_inline - fn get_obj_attrs_ptr(self) -> Pointer[_RefCountedAttrsDict]: + fn get_obj_attrs_ptr(self) -> UnsafePointer[_RefCountedAttrsDict]: return self.get_obj_attrs().attrs.bitcast[_RefCountedAttrsDict]() @always_inline @@ -801,7 +805,7 @@ struct object(IntableRaising, Boolable, Stringable): value: The string value. """ var impl = _ImmutableString( - Pointer[Int8].alloc(value.length), value.length + UnsafePointer[Int8].alloc(value.length), value.length ) memcpy(impl.data, value.data, value.length) self._value = impl @@ -1261,7 +1265,9 @@ struct object(IntableRaising, Boolable, Stringable): var lhsStr = self._value.get_as_string() var rhsStr = rhs._value.get_as_string() var length = lhsStr.length + rhsStr.length - var impl = _ImmutableString(Pointer[Int8].alloc(length), length) + var impl = _ImmutableString( + UnsafePointer[Int8].alloc(length), length + ) memcpy(impl.data, lhsStr.data, lhsStr.length) memcpy(impl.data.offset(lhsStr.length), rhsStr.data, rhsStr.length) var result = object() @@ -1579,7 +1585,7 @@ struct object(IntableRaising, Boolable, Stringable): raise Error("TypeError: can only index into lists and strings") var index = Self._convert_index_to_int(i) if self._value.is_str(): - var impl = _ImmutableString(Pointer[Int8].alloc(1), 1) + var impl = _ImmutableString(UnsafePointer[Int8].alloc(1), 1) impl.data.store( self._value.get_as_string().data.offset(index).load() ) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 3f89ad3974..1d7d5ff721 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -637,6 +637,15 @@ struct DTypePointer[ """ self.address = value + @always_inline("nodebug") + fn __init__(inout self, value: UnsafePointer[Scalar[type], address_space]): + """Constructs a `DTypePointer` from a scalar pointer of the same type. + + Args: + value: The scalar pointer. + """ + self.address = value.value + @always_inline("nodebug") fn __init__(inout self, value: Scalar[DType.address]): """Constructs a `DTypePointer` from the value of scalar address. diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index d25d1a3b9b..a7cedfdc86 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -110,7 +110,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path._as_ptr(), Pointer.address_of(stat) + Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -121,7 +121,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path._as_ptr(), Pointer.address_of(stat) + Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/os/_linux_x86.mojo b/stdlib/src/os/_linux_x86.mojo index b0490520e1..6b8a1f178a 100644 --- a/stdlib/src/os/_linux_x86.mojo +++ b/stdlib/src/os/_linux_x86.mojo @@ -108,7 +108,7 @@ struct _c_stat(Stringable): fn _stat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__xstat", Int32]( - Int32(0), path._as_ptr(), Pointer.address_of(stat) + Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to stat '" + path + "'" @@ -119,7 +119,7 @@ fn _stat(path: String) raises -> _c_stat: fn _lstat(path: String) raises -> _c_stat: var stat = _c_stat() var err = external_call["__lxstat", Int32]( - Int32(0), path._as_ptr(), Pointer.address_of(stat) + Int32(0), path._as_ptr(), UnsafePointer.address_of(stat) ) if err == -1: raise "unable to lstat '" + path + "'" diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index a3d5425025..0289747704 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -15,7 +15,7 @@ from os import getenv from sys import external_call from sys.ffi import DLHandle -from memory.unsafe import DTypePointer, Pointer +from memory.unsafe import DTypePointer from utils import StringRef from utils.index import StaticIntTuple @@ -157,7 +157,7 @@ struct CPython: var dict_type: PyObjectPtr var logging_enabled: Bool var version: PythonVersion - var total_ref_count: Pointer[Int] + var total_ref_count: LegacyPointer[Int] fn __init__(inout self: CPython): var logging_enabled = getenv("MODULAR_CPYTHON_LOGGING") == "ON" @@ -173,7 +173,7 @@ struct CPython: var null_pointer = DTypePointer[DType.int8].get_null() self.lib = DLHandle(python_lib) - self.total_ref_count = Pointer[Int].alloc(1) + self.total_ref_count = LegacyPointer[Int].alloc(1) self.none_value = PyObjectPtr(null_pointer) self.dict_type = PyObjectPtr(null_pointer) self.logging_enabled = logging_enabled @@ -636,8 +636,8 @@ struct CPython: fn PyUnicode_AsUTF8AndSize(inout self, py_object: PyObjectPtr) -> StringRef: var result = self.lib.get_function[ - fn (PyObjectPtr, Pointer[Int]) -> DTypePointer[DType.int8] - ]("PyUnicode_AsUTF8AndSize")(py_object, Pointer[Int]()) + fn (PyObjectPtr, UnsafePointer[Int]) -> DTypePointer[DType.int8] + ]("PyUnicode_AsUTF8AndSize")(py_object, UnsafePointer[Int]()) return StringRef(result) fn PyErr_Clear(inout self): @@ -654,16 +654,18 @@ struct CPython: var value = DTypePointer[DType.int8]() var traceback = DTypePointer[DType.int8]() - var type_ptr = Pointer[DTypePointer[DType.int8]].address_of(type) - var value_ptr = Pointer[DTypePointer[DType.int8]].address_of(value) - var traceback_ptr = Pointer[DTypePointer[DType.int8]].address_of( + var type_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of(type) + var value_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( + value + ) + var traceback_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( traceback ) var func = self.lib.get_function[ fn ( - Pointer[DTypePointer[DType.int8]], - Pointer[DTypePointer[DType.int8]], - Pointer[DTypePointer[DType.int8]], + UnsafePointer[DTypePointer[DType.int8]], + UnsafePointer[DTypePointer[DType.int8]], + UnsafePointer[DTypePointer[DType.int8]], ) -> None ]("PyErr_Fetch")(type_ptr, value_ptr, traceback_ptr) var r = PyObjectPtr {value: value} @@ -771,15 +773,17 @@ struct CPython: var key = DTypePointer[DType.int8].get_null() var value = DTypePointer[DType.int8].get_null() var v = p - var position = Pointer[Int].address_of(v) - var value_ptr = Pointer[DTypePointer[DType.int8]].address_of(value) - var key_ptr = Pointer[DTypePointer[DType.int8]].address_of(key) + var position = LegacyPointer[Int].address_of(v) + var value_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of( + value + ) + var key_ptr = UnsafePointer[DTypePointer[DType.int8]].address_of(key) var result = self.lib.get_function[ fn ( PyObjectPtr, - Pointer[Int], - Pointer[DTypePointer[DType.int8]], - Pointer[DTypePointer[DType.int8]], + LegacyPointer[Int], + UnsafePointer[DTypePointer[DType.int8]], + UnsafePointer[DTypePointer[DType.int8]], ) -> Int ]("PyDict_Next")( dictionary, diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index 21a83edcb4..c92437a4eb 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -24,7 +24,8 @@ from sys._assembly import inlined_assembly from sys.info import bitwidthof, has_neon, has_sse4 from builtin.dtype import _integral_type_of -from memory.unsafe import Pointer, bitcast +from memory.unsafe import bitcast +from memory.unsafe_pointer import UnsafePointer # ===----------------------------------------------------------------------===# # _digits @@ -501,7 +502,7 @@ struct FlushDenormals: mxcsr |= 0x8000 # flush to zero mxcsr |= 0x40 # denormals are zero llvm_intrinsic["llvm.x86.sse.ldmxcsr", NoneType]( - Pointer[Int32].address_of(mxcsr) + UnsafePointer[Int32].address_of(mxcsr) ) return @@ -539,7 +540,7 @@ struct FlushDenormals: if has_sse4(): var mxcsr = Int32() llvm_intrinsic["llvm.x86.sse.stmxcsr", NoneType]( - Pointer[Int32].address_of(mxcsr) + UnsafePointer[Int32].address_of(mxcsr) ) return mxcsr diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0a316f70be..0812c01fbb 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,7 +16,7 @@ from builtin.dtype import _uint_type_of_width from builtin.string import _atol -from memory.unsafe import DTypePointer +from memory.unsafe import LegacyPointer, DTypePointer from memory.unsafe_pointer import UnsafePointer # ===----------------------------------------------------------------------=== # @@ -92,7 +92,7 @@ struct StringRef( return self @always_inline - fn __init__(ptr: Pointer[Int8], len: Int) -> StringRef: + fn __init__(ptr: LegacyPointer[Int8], len: Int) -> StringRef: """Construct a StringRef value given a (potentially non-0 terminated string). @@ -116,7 +116,7 @@ struct StringRef( The constructor takes a raw pointer and a length. Args: - ptr: Pointer to the string. + ptr: DTypePointer to the string. len: The length of the string. Returns: @@ -136,14 +136,14 @@ struct StringRef( Constructed `StringRef` object. """ - return DTypePointer[DType.int8](ptr.value) + return DTypePointer[DType.int8](ptr) @always_inline fn __init__(ptr: DTypePointer[DType.int8]) -> StringRef: """Construct a StringRef value given a null-terminated string. Args: - ptr: Pointer to the string. + ptr: DTypePointer to the string. Returns: Constructed `StringRef` object. @@ -160,7 +160,7 @@ struct StringRef( """Retrieves a pointer to the underlying memory. Returns: - The pointer to the underlying memory. + The DTypePointer to the underlying memory. """ return self.data From 6fbf168416dd645951fa45e762d9ed2be15f65f1 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 15 Apr 2024 16:25:19 -0700 Subject: [PATCH 0169/2019] [mojo-stdlib] Add `whence` to arguments in `file.seek()` (#37435) This allows the user to seek from current position in FileHandle or to seek from the end of the file. MODULAR_ORIG_COMMIT_REV_ID: 97252aff0648eb5fef551d93fd4c6dfe195b1dd4 --- docs/changelog.md | 25 ++++++++++++++++++---- stdlib/src/builtin/file.mojo | 33 +++++++++++++++++++++++++++--- stdlib/src/os/__init__.mojo | 2 +- stdlib/src/os/os.mojo | 14 +++++++++++++ stdlib/test/builtin/test_file.mojo | 18 +++++++++++----- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index dc473d7c86..d1bb193340 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -115,11 +115,28 @@ what we publish. `Reference` is a safe type - use `UnsafePointer` to do unsafe operations. - The `mojo package` command no longer supports the `-D` flag. All compilation - environment flags should be provided at the point of package use - (e.g. `mojo run` or `mojo build`). + environment flags should be provided at the point of package use (e.g. + `mojo run` or `mojo build`). -- `parallel_memcpy` function has moved from the `buffer` package to the `algorithm` - package. Please update your imports accordingly. +- `parallel_memcpy` function has moved from the `buffer` package to the + `algorithm` package. Please update your imports accordingly. + +- `FileHandle.seek()` now has a whence argument that defaults to `os.SEEK_SET` + to seek from the beginning of the file. You can now set to `os.SEEK_CUR` to + offset by the current `FileHandle` seek position: + + ```mojo + var f = open("/tmp/example.txt") + # Skip 32 bytes + f.seek(os.SEEK_CUR, 32) + ``` + + Or `os.SEEK_END` to offset from the end of file: + + ```mojo + # Start from 32 bytes before the end of the file + f.seek(os.SEEK_END, -32) + ``` ### ❌ Removed diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 50fedf354b..666e96f650 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -198,11 +198,15 @@ struct FileHandle: return list - fn seek(self, offset: UInt64) raises -> UInt64: + fn seek(self, offset: UInt64, whence: UInt8 = os.SEEK_SET) raises -> UInt64: """Seeks to the given offset in the file. Args: - offset: The byte offset to seek to from the start of the file. + offset: The byte offset to seek to. + whence: The reference point for the offset: + os.SEEK_SET = 0: start of file (Default). + os.SEEK_CUR = 1: current position. + os.SEEK_END = 2: end of file. Raises: An error if this file handle is invalid, or if file seek returned a @@ -210,13 +214,36 @@ struct FileHandle: Returns: The resulting byte offset from the start of the file. + + Examples: + + Skip 32 bytes from the current read position: + + ```mojo + import os + var f = open("/tmp/example.txt", "r") + f.seek(os.SEEK_CUR, 32) + ``` + + Start from 32 bytes from the end of the file: + + ```mojo + import os + var f = open("/tmp/example.txt", "r") + f.seek(os.SEEK_END, -32) + ``` + . """ if not self.handle: raise "invalid file handle" + debug_assert( + whence >= 0 and whence < 3, + "Second argument to `seek` must be between 0 and 2.", + ) var err_msg = _OwnedStringRef() var pos = external_call["KGEN_CompilerRT_IO_FileSeek", UInt64]( - self.handle, offset, UnsafePointer.address_of(err_msg) + self.handle, offset, whence, UnsafePointer.address_of(err_msg) ) if err_msg: diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index 0eaa7005f6..c0afa1c331 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -15,5 +15,5 @@ from .atomic import Atomic from .env import setenv, getenv from .fstat import lstat, stat, stat_result -from .os import abort, listdir +from .os import abort, listdir, SEEK_SET, SEEK_CUR, SEEK_END from .pathlike import PathLike diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 943a8ae5bf..834241fd74 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -30,6 +30,20 @@ from utils import StringRef from .path import isdir from .pathlike import PathLike + +# ===----------------------------------------------------------------------=== # +# SEEK Constants +# ===----------------------------------------------------------------------=== # + + +alias SEEK_SET: UInt8 = 0 +"""Seek from the beginning of the file.""" +alias SEEK_CUR: UInt8 = 1 +"""Seek from the current position.""" +alias SEEK_END: UInt8 = 2 +"""Seek from the end of the file.""" + + # ===----------------------------------------------------------------------=== # # Utilities # ===----------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 1ec89a23f2..6832abd5cc 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -123,18 +123,26 @@ def test_file_read_context(): def test_file_seek(): print("== test_file_seek") - with open( - Path(CURRENT_DIR) / "test_file_dummy_input.txt", - "r", - ) as f: + with open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") as f: var pos = f.seek(6) assert_equal(pos, 6) alias expected_msg1 = "ipsum dolor sit amet, consectetur adipiscing elit." assert_equal(f.read(len(expected_msg1)), expected_msg1) + # Seek from the end of the file + pos = f.seek(-16, 2) + assert_equal(pos, 938) + + print(f.read(6)) + + # Seek from current possition, skip the space + pos = f.seek(1, 1) + assert_equal(pos, 945) + assert_equal(f.read(7), "rhoncus") + try: - f.seek(-12) + _ = f.seek(-12) except e: alias expected_msg = "seek error" assert_equal(str(e)[: len(expected_msg)], expected_msg) From 58d2763d8fac4c52fa600f774ae7fa7df078208e Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 15 Apr 2024 16:57:41 -0700 Subject: [PATCH 0170/2019] [mojo-stdlib] Remove redundant Int constructor. (#37725) There's no need to define T->T constructors in Mojo. MODULAR_ORIG_COMMIT_REV_ID: 19d38082bf82391e1126ef775127dab5ae7a32a8 --- stdlib/src/builtin/int.mojo | 12 ------------ stdlib/test/builtin/test_int.mojo | 1 - 2 files changed, 13 deletions(-) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 0771da2871..dc89c768e8 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -192,18 +192,6 @@ struct Int(Intable, Stringable, KeyElement, Boolable): value: __mlir_op.`index.constant`[value = __mlir_attr.`0:index`]() } - @always_inline("nodebug") - fn __init__(value: Int) -> Int: - """Construct Int from another Int value. - - Args: - value: The init value. - - Returns: - The constructed Int object. - """ - return Self {value: value.value} - @always_inline("nodebug") fn __init__(value: __mlir_type.index) -> Int: """Construct Int from the given index value. diff --git a/stdlib/test/builtin/test_int.mojo b/stdlib/test/builtin/test_int.mojo index 389c30838d..2bce6c953c 100644 --- a/stdlib/test/builtin/test_int.mojo +++ b/stdlib/test/builtin/test_int.mojo @@ -18,7 +18,6 @@ from sys.info import bitwidthof def test_constructors(): var i1 = Int(3) # Constructible from IntLiteral - var i2 = Int(Int(5)) # Constructible from Int def test_properties(): From 23410245df12e5474e66c8ecd24b0cfee3d3a559 Mon Sep 17 00:00:00 2001 From: Jack Clayton Date: Mon, 15 Apr 2024 17:21:03 -0700 Subject: [PATCH 0171/2019] [mojo-stdlib] Add file read method that reads data into `DTypePointer` (#37441) Allows the user to pass in an allocated `DTypePointer` to read data into the memory location from the position set by the seek pointer. Previously could only receive back a `String` or `List[Int8]`. The use-case this supports is `llm.c` which makes heavy use of C's `fread` and offsetting pointers while reading chunks of data into a base memory location. The API here is similar to `fread`, but fits in with Python having a `read` method on a `FileHandle`, it also matches the rest of Mojo where `size` is the number of elements, and you don't have to pass in the number of bytes for each element like in C. MODULAR_ORIG_COMMIT_REV_ID: cc52fb7e604c72c803e074b30a18a1c8b6de626a --- docs/changelog.md | 12 ++ stdlib/src/builtin/file.mojo | 169 +++++++++++++++++++++++++++-- stdlib/test/builtin/test_file.mojo | 48 +++++++- 3 files changed, 213 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d1bb193340..a6516c8beb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -138,6 +138,18 @@ what we publish. f.seek(os.SEEK_END, -32) ``` + - `FileHandle.read()` can now read straight into a `DTypePointer`: + + ```mojo + var file = open("/tmp/example.txt", "r") + + # Allocate and load 8 elements + var ptr = DTypePointer[DType.float32].alloc(8) + var bytes = file.read(ptr, 8) + print("bytes read", bytes) + print(ptr.load[width=8]()) + ``` + ### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 666e96f650..da8afb7450 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -111,7 +111,7 @@ struct FileHandle: fn close(inout self) raises: """Closes the file handle.""" - if self.handle == DTypePointer[DType.invalid](): + if not self.handle: return var err_msg = _OwnedStringRef() @@ -135,15 +135,55 @@ struct FileHandle: @always_inline fn read(self, size: Int64 = -1) raises -> String: - """Reads the data from the file. + """Reads data from a file and sets the file handle seek position. If + size is left as the default of -1, it will read to the end of the file. + Setting size to a number larger than what's in the file will set + String.size to the total number of bytes, and read all the data. Args: - size: Requested number of bytes to read. + size: Requested number of bytes to read (Default: -1 = EOF). Returns: The contents of the file. + + Raises: + An error if this file handle is invalid, or if the file read + returned a failure. + + Examples: + + Read the entire file into a String: + + ```mojo + var file = open("/tmp/example.txt", "r") + var string = file.read() + print(string) + ``` + + Read the first 8 bytes, skip 2 bytes, and then read the next 8 bytes: + + ```mojo + import os + var file = open("/tmp/example.txt", "r") + var word1 = file.read(8) + print(word1) + _ = file.seek(2, os.SEEK_CUR) + var word2 = file.read(8) + print(word2) + ``` + + Read the last 8 bytes in the file, then the first 8 bytes + ```mojo + _ = file.seek(-8, os.SEEK_END) + var last_word = file.read(8) + print(last_word) + _ = file.seek(8, os.SEEK_SET) # os.SEEK_SET is the default start of file + var first_word = file.read(8) + print(first_word) + ``` + . """ - if self.handle == DTypePointer[DType.invalid](): + if not self.handle: raise Error("invalid file handle") var size_copy: Int64 = size @@ -162,17 +202,126 @@ struct FileHandle: return String(buf, int(size_copy) + 1) + @always_inline + fn read[ + type: DType + ](self, ptr: DTypePointer[type], size: Int64 = -1) raises -> Int64: + """Read data from the file into the pointer. Setting size will read up + to `sizeof(type) * size`. The default value of `size` is -1 which + will read to the end of the file. Starts reading from the file handle + seek pointer, and after reading adds `sizeof(type) * size` bytes to the + seek pointer. + + Parameters: + type: The type that will the data will be represented as. + + Args: + ptr: The pointer where the data will be read to. + size: Requested number of elements to read. + + Returns: + The total amount of data that was read in bytes. + + Raises: + An error if this file handle is invalid, or if the file read + returned a failure. + + Examples: + + ```mojo + import os + + alias file_name = "/tmp/example.txt" + var file = open(file_name, "r") + + # Allocate and load 8 elements + var ptr = DTypePointer[DType.float32].alloc(8) + var bytes = file.read(ptr, 8) + print("bytes read", bytes) + + var first_element = ptr.load(0) + print(first_element) + + # Skip 2 elements + _ = file.seek(2 * sizeof[DType.float32](), os.SEEK_CUR) + + # Allocate and load 8 more elements from file hande seek position + var ptr2 = DTypePointer[DType.float32].alloc(8) + var bytes2 = file.read(ptr2, 8) + + var eleventh_element = ptr2[0] + var twelvth_element = ptr2[1] + print(eleventh_element, twelvth_element) + ``` + . + """ + + if not self.handle: + raise Error("invalid file handle") + + var size_copy = size * sizeof[type]() + var err_msg = _OwnedStringRef() + + external_call["KGEN_CompilerRT_IO_FileReadToAddress", NoneType]( + self.handle, + ptr, + Pointer.address_of(size_copy), + Pointer.address_of(err_msg), + ) + + if err_msg: + raise (err_msg^).consume_as_error() + return size_copy + fn read_bytes(self, size: Int64 = -1) raises -> List[Int8]: - """Read from file buffer until we have `size` characters or we hit EOF. - If `size` is negative or omitted, read until EOF. + """Reads data from a file and sets the file handle seek position. If + size is left as default of -1, it will read to the end of the file. + Setting size to a number larger than what's in the file will be handled + and set the List.size to the total number of bytes in the file. Args: - size: Requested number of bytes to read. + size: Requested number of bytes to read (Default: -1 = EOF). Returns: - The contents of the file. + The contents of the file. + + Raises: + An error if this file handle is invalid, or if the file read + returned a failure. + + Examples: + + Reading the entire file into a List[Int8]: + + ```mojo + var file = open("/tmp/example.txt", "r") + var string = file.read_bytes() + ``` + + Reading the first 8 bytes, skipping 2 bytes, and then reading the next + 8 bytes: + + ```mojo + import os + var file = open("/tmp/example.txt", "r") + var list1 = file.read(8) + _ = file.seek(2, os.SEEK_CUR) + var list2 = file.read(8) + ``` + + Reading the last 8 bytes in the file, then the first 8 bytes: + + ```mojo + import os + var file = open("/tmp/example.txt", "r") + _ = file.seek(-8, os.SEEK_END) + var last_data = file.read(8) + _ = file.seek(8, os.SEEK_SET) # os.SEEK_SET is the default start of file + var first_data = file.read(8) + ``` + . """ - if self.handle == DTypePointer[DType.invalid](): + if not self.handle: raise Error("invalid file handle") var size_copy: Int64 = size @@ -289,7 +438,7 @@ struct FileHandle: ptr: The pointer to the data to write. len: The length of the pointer (in bytes). """ - if self.handle == DTypePointer[DType.invalid](): + if not self.handle: raise Error("invalid file handle") var err_msg = _OwnedStringRef() diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index 6832abd5cc..fbeb5f15af 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -61,10 +61,7 @@ def test_file_read_multi(): def test_file_read_bytes_multi(): print("== test_file_read_bytes_multi") - var f = open( - Path(CURRENT_DIR) / "test_file_dummy_input.txt", - "r", - ) + var f = open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") # CHECK: Lorem ipsum var bytes1 = f.read_bytes(12) @@ -122,6 +119,7 @@ def test_file_read_context(): # CHECK-LABEL: test_file_seek def test_file_seek(): print("== test_file_seek") + import os with open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") as f: var pos = f.seek(6) @@ -131,13 +129,13 @@ def test_file_seek(): assert_equal(f.read(len(expected_msg1)), expected_msg1) # Seek from the end of the file - pos = f.seek(-16, 2) + pos = f.seek(-16, os.SEEK_END) assert_equal(pos, 938) print(f.read(6)) # Seek from current possition, skip the space - pos = f.seek(1, 1) + pos = f.seek(1, os.SEEK_CUR) assert_equal(pos, 945) assert_equal(f.read(7), "rhoncus") @@ -188,6 +186,43 @@ def test_file_write_again(): read_file.close() +@value +@register_passable +struct Word: + var first_letter: Int8 + var second_letter: Int8 + var third_letter: Int8 + var fourth_letter: Int8 + var fith_letter: Int8 + + fn __str__(self) -> String: + var word = List[Int8](capacity=6) + word.append(self.first_letter) + word.append(self.second_letter) + word.append(self.third_letter) + word.append(self.fourth_letter) + word.append(self.fith_letter) + word.append(0) + return word + + +# CHECK-LABEL: test_file_read_to_dtype_pointer +def test_file_read_to_dtype_pointer(): + print("== test_file_read_to_dtype_pointer") + + var f = open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") + + var ptr = DTypePointer[DType.int8].alloc(8) + var data = f.read(ptr, 8) + assert_equal(ptr.load[width=8](0), "[76, 111, 114, 101, 109, 32, 105, 112]") + + var ptr2 = DTypePointer[DType.int8].alloc(8) + var data2 = f.read(ptr2, 8) + assert_equal( + ptr2.load[width=8](0), "[115, 117, 109, 32, 100, 111, 108, 111]" + ) + + def main(): test_file_read() test_file_read_multi() @@ -199,3 +234,4 @@ def main(): test_file_open_nodir() test_file_write() test_file_write_again() + test_file_read_to_dtype_pointer() From 85b8bfb2f6670b79ac8fbd5821db6e997e944639 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 16 Apr 2024 05:22:18 -0700 Subject: [PATCH 0172/2019] [mojo-docs] remove bug from changelog. (#37802) I incorrectly added this because there was a tree of issues that went back to this, but it isn't done. MODULAR_ORIG_COMMIT_REV_ID: 9b54fe1ce340375e0206c9c916bfb1dfab2029cf --- docs/changelog.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index a6516c8beb..223703f1a4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -192,9 +192,6 @@ what we publish. longer cuts off hover previews for functions with functional arguments, parameters, or results. -- [#1245](https://github.com/modularml/mojo/issues/1245) [Feature Request] - Parameter Inference from Other Parameters. - - [#1901](https://github.com/modularml/mojo/issues/1901) Fixed Mojo LSP and documentation generation handling of inout arguments. From eca95db4441388c3c04039cdacaa3b70c1dd862d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 16 Apr 2024 05:36:58 -0700 Subject: [PATCH 0173/2019] [mojo-docs] Remove some sharp edges from roadmap. (#37803) This generally dusts off the roadmap doc a bit. MODULAR_ORIG_COMMIT_REV_ID: cca7c2dda8fb3faaecc4eb62d234e2db5f99820b --- docs/roadmap.md | 94 +++++++------------------------------------------ 1 file changed, 12 insertions(+), 82 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index a3e026e0bb..afcb0084f1 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -96,8 +96,8 @@ systems. Here are some of the notable issues that we plan to fix: - Missing native support for Windows, Intel Macs, and Linux distributions other than Ubuntu. Currently, we support Ubuntu systems with x86-64 - processors only. Support for more Linux distributions (including Debian - and RHEL) and Windows is in progress. + processors and Apple Silicon macOS. Support for more Linux distributions + (including Debian and RHEL) and Windows is in progress. - Python interoperability might fail when running a compiled Mojo program, with the message @@ -127,10 +127,6 @@ systems. Here are some of the notable issues that we plan to fix: `MODULAR_AUTH` value shown for the `curl` command on [the download page](https://developer.modular.com/download). -- `modular install mojo` is slow and might appear unresponsive (as the - installer is downloading packages in the background). We will add a progress - bar in a future release. - - If you attempt to uninstall Mojo with `modular uninstall`, your subsequent attempt to install Mojo might fail with an HTTP 500 error code. If so, run `modular clean` and try again. @@ -197,15 +193,11 @@ the language fully, but which don't depend strongly on other features. These include things like: - Improved package management support. -- Many standard library features, including canonical arrays and dictionary - types, copy-on-write data structures, etc. +- Many standard library features, including copy-on-write data structures. - Support for "top level code" at file scope. - Algebraic data types like `enum` in Swift/Rust, and pattern matching. -- Many standard library types, including `Optional[T]` and `Result[T, Error]` - types when we have algebraic datatypes and basic traits. -- Support for keyword-only arguments and variadic keyword arguments - (`**kwargs`). -- Support for passing keyword arguments when calling Python functions. +- Many standard library types need refinement, including `Optional[T]` and + `Result[T, Error]`. ## Ownership and Lifetimes @@ -216,10 +208,9 @@ like: - Capture declarations in closures. - Borrow checker: complain about invalid mutable references. -The next step in this is to bring proper lifetime support in. This will add the -ability to return references and store references in structures safely. In the -immediate future, one can use the unsafe `Pointer` struct to do this like in -C++. +Mojo has support for a safe `Reference` type, and it is used in the standard +library, but it is still under active development and not very pretty or nice +to use right now. ## Traits support @@ -240,14 +231,13 @@ already implemented in the standard library. We plan to expand traits support in future releases. Planned features include: -- More traits built in to the standard library, and expanded use of traits - throughout the standard library. - - Support for default implementations of required methods. - Support for a feature like Swift's extensions, allowing you to add a trait to a preexisting type. +- Add support for conditional conformances. + ## Classes Mojo still doesn't support classes, the primary thing Python programmers use @@ -260,14 +250,12 @@ When we get here, we will discuss what the right default is: for example, is full Python hash-table dynamism the default? Or do we use a more efficient model by default (e.g. vtable-based dispatch and explicitly declared stored properties) and allow opt'ing into dynamism with a `@dynamic` decorator on the -class. The latter approach worked well for Swift (its [`@objc` -attribute](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/#objc)), -but we'll have to prototype to better understand the tradeoffs. +class. More discussion is [in this proposal](https://github.com/modularml/mojo/blob/main/proposals/mojo-and-dynamism.md). ## C/C++ Interop Integration to transparently import Clang C/C++ modules. Mojo's type system -and C++'s are pretty compatible, so we should be able to have something pretty +and C++'s are very compatible, so we should be able to have something pretty nice here. Mojo can leverage Clang to transparently generate a foreign function interface between C/C++ and Mojo, with the ability to directly import functions: @@ -550,64 +538,6 @@ Mojo currently supports this feature through the [`len()`](/mojo/stdlib/builtin/len/len) functions. We'll continue to add traits support to the standard library to enable common use cases like this. -### Lifetime tracking inside collections - -With traits, it is now possible to build collection types like lists, maps, and -sets that invoke element destructors. However, most standard library collection -types haven't yet been extended to use traits. - -For collections of trivial types, like `Int`, this is no problem, but for -collections of types with lifetimes, like `String`, the elements have to be -manually destructed. Doing so requires quite an ugly pattern, shown in the next -section. - -The `List` type has been updated to use traits, and invokes destructors -properly. - -### No safe value references - -Mojo does not have proper lifetime marker support yet, and that means it cannot -reason about returned references, so Mojo doesn't support them. You can return -or keep unsafe references by passing explicit pointers around. - -```mojo -struct StringRef: - var ref: Pointer[SI8] - var size: Int - # ... - -fn bar(x: StringRef): pass - -fn foo(): - var s: String = "1234" - var ref: StringRef = s # unsafe reference - bar(ref) - _ = s # keep the backing memory alive! -``` - -Mojo will destruct objects as soon as it thinks it can. That means the lifetime -of objects to which there are unsafe references must be manually extended. See -the [Death of a value](/mojo/manual/lifecycle/death.html) -for more details. This disables the RAII pattern in Mojo. Context managers and -`with` statements are your friends in Mojo. - -No lvalue returns also mean that implementing certain patterns require magic -keywords until proper lifetime support is built. One such pattern is retrieving -an unsafe reference from an object. - -```mojo -struct UnsafeIntRef: - var ptr: Pointer[Int] - -fn printIntRef(x: UnsafeIntRef): - # "deference" operator - print(__get_address_as_lvalue(x.ptr)) # Pointer[Int] -> &Int - -var c: Int = 10 -# "reference" operator -var ref = UnsafeIntRef(__get_lvalue_as_address(c)) # &Int -> Pointer[Int] -``` - ### Parameter closure captures are unsafe references You may have seen nested functions, or "closures", annotated with the From 333458d09ba605cceb4dd00629e29bb792daa793 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 16 Apr 2024 10:52:18 -0700 Subject: [PATCH 0174/2019] [docs] Explain implicit convertion via value decorator (#37430) MODULAR_ORIG_COMMIT_REV_ID: 82237a53b5ea85aa196b7f369f03ddc719915cde --- docs/manual/lifecycle/life.ipynb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/manual/lifecycle/life.ipynb b/docs/manual/lifecycle/life.ipynb index 2ba0569091..bbfa43b310 100644 --- a/docs/manual/lifecycle/life.ipynb +++ b/docs/manual/lifecycle/life.ipynb @@ -274,6 +274,19 @@ " fn __init__(inout self, s: Source, reverse: Bool = False): ...\n", "```\n", "\n", + "Implicit conversion also occurs if the type doesn't declare its own constructor,\n", + "but instead uses the [`@value` decorator](#value-decorator), _and_ the type\n", + "has only one field. That's because Mojo automatically creates a member-wise\n", + "constructor for each field, and when there is only one field, that synthesized\n", + "constructor works exactly like a conversion constructor. For example, this\n", + "type also can convert a `Source` value to a `Target` value:\n", + "\n", + "```mojo\n", + "@value\n", + "struct Target:\n", + " var s: Source\n", + "```\n", + "\n", "Implicit conversion can fail if Mojo can't unambiguously match the conversion to\n", "a constructor. For example, if the target type has two overloaded constructors\n", "that take different types, and each of those types supports an implicit\n", From c876524a3d1e0669d7af1b24fcc0b83b5d3d5cf3 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 16 Apr 2024 10:52:25 -0700 Subject: [PATCH 0175/2019] [docs] Update FAQ about Mojo Playground (#36852) The jupyter playground is gone, so add info about the new playground here, which originally comes from the Mojo Get Started guide (but should instead move here because that get started guide will soon focus on local development only). MODULAR_ORIG_COMMIT_REV_ID: 7a1ef0e5484c08ba51a0a29d7c8f2ad5b4342438 --- docs/faq.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 22cfffe74f..11a21e5fab 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -285,8 +285,30 @@ You can [get the Mojo SDK here](https://developer.modular.com/download)! ### Is the Mojo Playground still available? -Yes, you can [get access today](https://www.modular.com/get-started) -to the Mojo Playground, a hosted set of Mojo-supported Jupyter notebooks. +Yes, but it's different. When we first announced Mojo, it was available +only through login, in a JupyterLab environment. Now that Mojo is available +for local development, we've shut down that service (you can instead [run +Mojo notebooks +locally](https://github.com/modularml/mojo/tree/main/examples/notebooks#readme)). + +The new [Mojo Playground](/mojo/playground) is built into the docs website +and does not require login. + +- It provides access to Mojo and the Mojo standard library. It does not have + network access, so you can't install additional Mojo or Python packages. + +- It doesn't include any Python packages by default. In the future, + we intend to make some common Python packages available to import in the + Playground. + +- You can download your code or share it as a gist, but there's no mechanism + for saving code in the Playground itself. Any changes will be lost when you + switch code examples (as well as in the event of a server refresh or update). + If you come up with something you want to save, download it or share it + using buttons in the Playground toolbar. + +- There might be some bugs. Please [report issues and feedback on + GitHub](https://github.com/modularml/mojo/issues/new/choose). ### What are the license terms for the SDK? From e348a69788965f2333f08c26b0ce5ad902826a19 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 16 Apr 2024 11:32:42 -0700 Subject: [PATCH 0176/2019] [******] Mark some of the llvm_intrnsics as non side-effecting, NFC (#37828) Mark some of the llvm_intrnsics as pure so that they can be CSE-ed. MODULAR_ORIG_COMMIT_REV_ID: 6054775f49a37fe31b4073974fbcb94bef236ecc --- stdlib/src/builtin/simd.mojo | 97 ++++++++++++++++++++++----------- stdlib/src/builtin/string.mojo | 6 +- stdlib/src/sys/intrinsics.mojo | 8 ++- stdlib/src/utils/_numerics.mojo | 6 +- stdlib/src/utils/stringref.mojo | 6 +- 5 files changed, 82 insertions(+), 41 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 544ff94df7..9061a2d349 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -672,7 +672,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type.is_floating_point(): - div = llvm_intrinsic["llvm.trunc", Self](div) + div = llvm_intrinsic["llvm.trunc", Self, has_side_effect=False]( + div + ) var mod = self - div * rhs var mask = ((rhs < 0) ^ (self < 0)) & (mod != 0) @@ -1504,9 +1506,11 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( tmp[i] = self[i + offset] return tmp - return llvm_intrinsic["llvm.vector.extract", SIMD[type, output_width]]( - self, offset - ) + return llvm_intrinsic[ + "llvm.vector.extract", + SIMD[type, output_width], + has_side_effect=False, + ](self, offset) @always_inline("nodebug") fn insert[*, offset: Int = 0](self, value: SIMD[type, _]) -> Self: @@ -1550,7 +1554,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( tmp[i + offset] = value[i] return tmp - return llvm_intrinsic["llvm.vector.insert", Self](self, value, offset) + return llvm_intrinsic[ + "llvm.vector.insert", Self, has_side_effect=False + ](self, value, offset) @always_inline("nodebug") fn join(self, other: Self) -> SIMD[type, 2 * size]: @@ -1668,7 +1674,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( return SIMD[type, 2 * size](self[0], other[0]) return llvm_intrinsic[ - "llvm.experimental.vector.interleave2", SIMD[type, 2 * size] + "llvm.experimental.vector.interleave2", + SIMD[type, 2 * size], + has_side_effect=False, ](self, other) @always_inline("nodebug") @@ -1689,6 +1697,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var res = llvm_intrinsic[ "llvm.experimental.vector.deinterleave2", _RegisterPackType[SIMD[type, size // 2], SIMD[type, size // 2]], + has_side_effect=False, ](self) return StaticTuple[SIMD[type, size // 2], 2]( res.get[0, SIMD[type, size // 2]](), @@ -1803,16 +1812,26 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type.is_floating_point(): return rebind[SIMD[type, size_out]]( - llvm_intrinsic["llvm.vector.reduce.fmax", Scalar[type]](self) + llvm_intrinsic[ + "llvm.vector.reduce.fmax", + Scalar[type], + has_side_effect=False, + ](self) ) @parameter if type.is_unsigned(): return rebind[SIMD[type, size_out]]( - llvm_intrinsic["llvm.vector.reduce.umax", Scalar[type]](self) + llvm_intrinsic[ + "llvm.vector.reduce.umax", + Scalar[type], + has_side_effect=False, + ](self) ) return rebind[SIMD[type, size_out]]( - llvm_intrinsic["llvm.vector.reduce.smax", Scalar[type]](self) + llvm_intrinsic[ + "llvm.vector.reduce.smax", Scalar[type], has_side_effect=False + ](self) ) @always_inline("nodebug") @@ -1850,16 +1869,26 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if type.is_floating_point(): return rebind[SIMD[type, size_out]]( - llvm_intrinsic["llvm.vector.reduce.fmin", Scalar[type]](self) + llvm_intrinsic[ + "llvm.vector.reduce.fmin", + Scalar[type], + has_side_effect=False, + ](self) ) @parameter if type.is_unsigned(): return rebind[SIMD[type, size_out]]( - llvm_intrinsic["llvm.vector.reduce.umin", Scalar[type]](self) + llvm_intrinsic[ + "llvm.vector.reduce.umin", + Scalar[type], + has_side_effect=False, + ](self) ) return rebind[SIMD[type, size_out]]( - llvm_intrinsic["llvm.vector.reduce.smin", Scalar[type]](self) + llvm_intrinsic[ + "llvm.vector.reduce.smin", Scalar[type], has_side_effect=False + ](self) ) @always_inline @@ -1920,9 +1949,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if size == 1: return self.cast[DType.bool]()[0].value - return llvm_intrinsic["llvm.vector.reduce.and", Scalar[DType.bool]]( - self - ) + return llvm_intrinsic[ + "llvm.vector.reduce.and", Scalar[DType.bool], has_side_effect=False + ](self) @always_inline fn reduce_or(self) -> Bool: @@ -1938,7 +1967,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if size == 1: return self.cast[DType.bool]()[0].value - return llvm_intrinsic["llvm.vector.reduce.or", Scalar[DType.bool]](self) + return llvm_intrinsic[ + "llvm.vector.reduce.or", Scalar[DType.bool], has_side_effect=False + ](self) # ===-------------------------------------------------------------------===# # select @@ -2004,9 +2035,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( if size == 1: constrained[shift == 0, "for scalars the shift must be 0"]() return self - return llvm_intrinsic["llvm.experimental.vector.splice", Self]( - self, self, Int32(shift) - ) + return llvm_intrinsic[ + "llvm.experimental.vector.splice", Self, has_side_effect=False + ](self, self, Int32(shift)) @always_inline fn rotate_right[shift: Int](self) -> Self: @@ -2073,9 +2104,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( alias zero_simd = Self() - return llvm_intrinsic["llvm.experimental.vector.splice", Self]( - self, zero_simd, Int32(shift) - ) + return llvm_intrinsic[ + "llvm.experimental.vector.splice", Self, has_side_effect=False + ](self, zero_simd, Int32(shift)) @always_inline fn shift_right[shift: Int](self) -> Self: @@ -2113,9 +2144,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( alias zero_simd = Self() - return llvm_intrinsic["llvm.experimental.vector.splice", Self]( - zero_simd, self, Int32(-shift) - ) + return llvm_intrinsic[ + "llvm.experimental.vector.splice", Self, has_side_effect=False + ](zero_simd, self, Int32(-shift)) # ===-------------------------------------------------------------------===# @@ -2176,9 +2207,9 @@ fn _pow[ @unroll for i in range(simd_width): - result[i] = llvm_intrinsic["llvm.pow", Scalar[lhs_type]]( - lhs[i], rhs[i] - ) + result[i] = llvm_intrinsic[ + "llvm.pow", Scalar[lhs_type], has_side_effect=False + ](lhs[i], rhs[i]) return result elif rhs_type.is_integral(): @@ -2195,9 +2226,9 @@ fn _pow[ @unroll for i in range(simd_width): - result[i] = llvm_intrinsic["llvm.powi", Scalar[lhs_type]]( - lhs[i], rhs[i].cast[DType.int32]() - ) + result[i] = llvm_intrinsic[ + "llvm.powi", Scalar[lhs_type], has_side_effect=False + ](lhs[i], rhs[i].cast[DType.int32]()) else: for i in range(simd_width): if rhs[i] < 0: @@ -2253,7 +2284,9 @@ fn _floor[ if has_neon() and type == DType.bfloat16: return _floor(x.cast[DType.float32]()).cast[type]() - return llvm_intrinsic["llvm.floor", SIMD[type, simd_width]](x) + return llvm_intrinsic[ + "llvm.floor", SIMD[type, simd_width], has_side_effect=False + ](x) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 787815af16..7205488fed 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -47,12 +47,14 @@ fn _abs(x: SIMD) -> __type_of(x): @always_inline fn _ctlz(val: Int) -> Int: - return llvm_intrinsic["llvm.ctlz", Int](val, False) + return llvm_intrinsic["llvm.ctlz", Int, has_side_effect=False](val, False) @always_inline("nodebug") fn _ctlz(val: SIMD) -> __type_of(val): - return llvm_intrinsic["llvm.ctlz", __type_of(val)](val, False) + return llvm_intrinsic["llvm.ctlz", __type_of(val), has_side_effect=False]( + val, False + ) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 4e1e831f8f..2fb633b115 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1354,7 +1354,9 @@ fn strided_load[ return addr.load() if mask else Scalar[type]() var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", SIMD[DType.index, simd_width] + "llvm.experimental.stepvector", + SIMD[DType.index, simd_width], + has_side_effect=False, ]() var offset = (int(addr) + stride * iota * sizeof[type]()) var passthrough = SIMD[type, simd_width]() @@ -1433,7 +1435,9 @@ fn strided_store[ return var iota = llvm_intrinsic[ - "llvm.experimental.stepvector", SIMD[DType.index, simd_width] + "llvm.experimental.stepvector", + SIMD[DType.index, simd_width], + has_side_effect=False, ]() var offset = int(addr) + stride * iota * sizeof[type]() scatter[type, simd_width](value, offset.cast[DType.address](), mask) diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index c92437a4eb..d3c48d445e 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -644,9 +644,9 @@ fn isnan[ alias signaling_nan_test: UInt32 = 0x0001 alias quiet_nan_test: UInt32 = 0x0002 - return llvm_intrinsic["llvm.is.fpclass", SIMD[DType.bool, simd_width]]( - val.value, (signaling_nan_test | quiet_nan_test).value - ) + return llvm_intrinsic[ + "llvm.is.fpclass", SIMD[DType.bool, simd_width], has_side_effect=False + ](val.value, (signaling_nan_test | quiet_nan_test).value) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 0812c01fbb..a782a61ea5 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -400,12 +400,14 @@ struct StringRef( @always_inline("nodebug") fn _cttz(val: Int) -> Int: - return llvm_intrinsic["llvm.cttz", Int](val, False) + return llvm_intrinsic["llvm.cttz", Int, has_side_effect=False](val, False) @always_inline("nodebug") fn _cttz(val: SIMD) -> __type_of(val): - return llvm_intrinsic["llvm.cttz", __type_of(val)](val, False) + return llvm_intrinsic["llvm.cttz", __type_of(val), has_side_effect=False]( + val, False + ) @always_inline From c51eb39e18d2c4aff6be9b761ccc22cc432555ba Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 16 Apr 2024 17:28:11 -0700 Subject: [PATCH 0177/2019] [stdlib] Migrating Pointer to UnsafePointer - 5/n (#37791) UnsafePointer.`value` renamed to UnsafePointer.`address`. Also, migrated the following files to use UnsafePointer. - _startup.mojo - anytype.mojo - builtin_list.mojo - file.mojo - tuple.mojo - inline_string.mojo MODULAR_ORIG_COMMIT_REV_ID: dda6218864941ed8649cc6dae25bcc85a3eb3a22 --- stdlib/src/builtin/_startup.mojo | 22 ++++++++++++--------- stdlib/src/builtin/anytype.mojo | 4 ++-- stdlib/src/builtin/builtin_list.mojo | 8 ++++++-- stdlib/src/builtin/file.mojo | 4 ++-- stdlib/src/builtin/object.mojo | 8 ++++---- stdlib/src/builtin/string.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 4 ++-- stdlib/src/collections/list.mojo | 2 +- stdlib/src/memory/_arc.mojo | 6 +++--- stdlib/src/memory/reference.mojo | 2 +- stdlib/src/memory/unsafe.mojo | 6 +++--- stdlib/src/memory/unsafe_pointer.mojo | 28 ++++++++++++++------------- stdlib/src/utils/inlined_string.mojo | 4 ++-- stdlib/src/utils/variant.mojo | 6 ++++-- 14 files changed, 59 insertions(+), 47 deletions(-) diff --git a/stdlib/src/builtin/_startup.mojo b/stdlib/src/builtin/_startup.mojo index 9b29110118..0c83daf7d2 100644 --- a/stdlib/src/builtin/_startup.mojo +++ b/stdlib/src/builtin/_startup.mojo @@ -18,30 +18,34 @@ from sys import external_call @always_inline fn _get_global[ name: StringLiteral, - init_fn: fn (Pointer[NoneType]) -> Pointer[NoneType], - destroy_fn: fn (Pointer[NoneType]) -> None, -](payload: Pointer[NoneType] = Pointer[NoneType]()) -> Pointer[NoneType]: + init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], + destroy_fn: fn (UnsafePointer[NoneType]) -> None, +]( + payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]() +) -> UnsafePointer[NoneType]: return external_call[ - "KGEN_CompilerRT_GetGlobalOrCreate", Pointer[NoneType] + "KGEN_CompilerRT_GetGlobalOrCreate", UnsafePointer[NoneType] ](StringRef(name), payload, init_fn, destroy_fn) -fn _init_global_runtime(ignored: Pointer[NoneType]) -> Pointer[NoneType]: +fn _init_global_runtime( + ignored: UnsafePointer[NoneType], +) -> UnsafePointer[NoneType]: """Intialize the global runtime. This is a singleton that handle the common case where the runtime has the same number of threads as the number of cores. """ return external_call[ - "KGEN_CompilerRT_LLCL_CreateRuntime", Pointer[NoneType] + "KGEN_CompilerRT_LLCL_CreateRuntime", UnsafePointer[NoneType] ](0) -fn _destroy_global_runtime(ptr: Pointer[NoneType]): +fn _destroy_global_runtime(ptr: UnsafePointer[NoneType]): """Destroy the global runtime if ever used.""" external_call["KGEN_CompilerRT_LLCL_DestroyRuntime", NoneType](ptr) @always_inline -fn _get_current_or_global_runtime() -> Pointer[NoneType]: +fn _get_current_or_global_runtime() -> UnsafePointer[NoneType]: """Returns the current runtime, or returns the Mojo singleton global runtime, creating it if it does not already exist. When Mojo is used within the Modular Execution Engine the current runtime will be that already @@ -51,7 +55,7 @@ fn _get_current_or_global_runtime() -> Pointer[NoneType]: is created with number of threads equal to the number of cores. """ var current_runtime = external_call[ - "KGEN_CompilerRT_LLCL_GetCurrentRuntime", Pointer[NoneType] + "KGEN_CompilerRT_LLCL_GetCurrentRuntime", UnsafePointer[NoneType] ]() if current_runtime: return current_runtime diff --git a/stdlib/src/builtin/anytype.mojo b/stdlib/src/builtin/anytype.mojo index 9f776a5b25..2d34ffc441 100644 --- a/stdlib/src/builtin/anytype.mojo +++ b/stdlib/src/builtin/anytype.mojo @@ -43,11 +43,11 @@ trait AnyType: ```mojo @value struct Foo(AnyType): - var p: Pointer[Int] + var p: UnsafePointer[Int] var size: Int fn __init__(inout self, size: Int): - self.p = Pointer[Int].alloc(size) + self.p = UnsafePointer[Int].alloc(size) self.size = size fn __del__(owned self): diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 632293f203..0c0daa9714 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -278,7 +278,9 @@ struct VariadicListMem[ var tmp = value # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. - self.value = Pointer.address_of(tmp).bitcast[Self._mlir_type]().load() + self.value = ( + LegacyPointer.address_of(tmp).bitcast[Self._mlir_type]().load() + ) self._is_owned = False # Provide support for variadics of *owned* arguments. The reference will @@ -298,7 +300,9 @@ struct VariadicListMem[ var tmp = value # We need to bitcast different argument conventions to a consistent # representation. This is ugly but effective. - self.value = Pointer.address_of(tmp).bitcast[Self._mlir_type]().load() + self.value = ( + LegacyPointer.address_of(tmp).bitcast[Self._mlir_type]().load() + ) self._is_owned = True @always_inline diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index da8afb7450..23a40d80a3 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -265,8 +265,8 @@ struct FileHandle: external_call["KGEN_CompilerRT_IO_FileReadToAddress", NoneType]( self.handle, ptr, - Pointer.address_of(size_copy), - Pointer.address_of(err_msg), + UnsafePointer.address_of(size_copy), + UnsafePointer.address_of(err_msg), ) if err_msg: diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index d58c359195..3a0ff9c410 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -68,7 +68,7 @@ struct _ImmutableString: @always_inline fn __init__(data: UnsafePointer[Int8], length: Int) -> Self: - return Self {data: data.value, length: length} + return Self {data: data.address, length: length} @always_inline fn string_compare(self, rhs: _ImmutableString) -> Int: @@ -106,7 +106,7 @@ struct _RefCountedListRef: @always_inline fn __init__() -> Self: var ptr = UnsafePointer[_RefCountedList].alloc(1) - __get_address_as_uninit_lvalue(ptr.value) = _RefCountedList() + __get_address_as_uninit_lvalue(ptr.address) = _RefCountedList() return Self {lst: ptr.bitcast[NoneType]()} @always_inline @@ -121,7 +121,7 @@ struct _RefCountedListRef: return # Run the destructor on the list elements and then destroy the list. - var list = __get_address_as_owned_value(ptr.value).impl + var list = __get_address_as_owned_value(ptr.address).impl for i in range(len(list)): list[i].destroy() ptr.free() @@ -201,7 +201,7 @@ struct _RefCountedAttrsDictRef: @always_inline fn __init__(values: VariadicListMem[Attr, _, _]) -> Self: var ptr = UnsafePointer[_RefCountedAttrsDict].alloc(1) - __get_address_as_uninit_lvalue(ptr.value) = _RefCountedAttrsDict() + __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() # Elements can only be added on construction. for i in range(len(values)): ptr[].impl._insert(values[i].key, values[i].value._value.copy()) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 7205488fed..0f73a5985b 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -523,7 +523,7 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): var adjusted_span = self._adjust_span(span) if adjusted_span.step == 1: return StringRef( - (self._buffer.data + span.start).value, + (self._buffer.data + span.start).address, len(adjusted_span), ) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 79081ead77..2092f2df27 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -108,7 +108,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): initialize_pointee( UnsafePointer(self._refitem__[idx]()), - __get_address_as_owned_value(existing_elt_ptr.value), + __get_address_as_owned_value(existing_elt_ptr.address), ) unroll[initialize_elt, Self.__len__()]() @@ -176,7 +176,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): Reference(self_lit)[].storage ).get_legacy_pointer().address - # Pointer to the element. + # KGenPointer to the element. var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( storage_kgen_ptr ) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 8c2590b82c..515866e6da 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -453,7 +453,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var base_ptr = Reference(self)[].data return __mlir_op.`lit.ref.from_pointer`[ _type = Reference[T, mutability, self_life]._mlir_type - ]((base_ptr + normalized_idx).value) + ]((base_ptr + normalized_idx).address) fn __iter__[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index bab487007d..4d6bd0d334 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -90,7 +90,7 @@ struct Arc[T: Movable](CollectionElement): value: The value to manage. """ self._inner = UnsafePointer[Self._type].alloc(1) - __get_address_as_uninit_lvalue(self._inner.value) = Self._type(value^) + __get_address_as_uninit_lvalue(self._inner.address) = Self._type(value^) _ = self._inner[].increment() fn __init__(inout self, *, owned inner: UnsafePointer[Self._type]): @@ -117,7 +117,7 @@ struct Arc[T: Movable](CollectionElement): var rc = self._inner[].decrement() if rc < 1: # Call inner destructor, then free the memory - _ = __get_address_as_owned_value(self._inner.value) + _ = __get_address_as_owned_value(self._inner.address) self._inner.free() fn set(self, owned new_value: T): @@ -151,7 +151,7 @@ struct Arc[T: Movable](CollectionElement): alias RefType = Reference[T, mutability, lifetime] return RefType( __mlir_op.`lit.ref.from_pointer`[_type = RefType._mlir_type]( - Reference(self)[]._data_ptr().value + Reference(self)[]._data_ptr().address ) ) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 05a3341d93..a7ac6ed515 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -259,7 +259,7 @@ struct Reference[ # Work around AnyRegType vs AnyType. return __mlir_op.`pop.pointer.bitcast`[ _type = Pointer[type, address_space]._mlir_type - ](UnsafePointer(self).value) + ](UnsafePointer(self).address) @always_inline("nodebug") fn unsafe_bitcast[ diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 1d7d5ff721..47ef2cf496 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -638,13 +638,13 @@ struct DTypePointer[ self.address = value @always_inline("nodebug") - fn __init__(inout self, value: UnsafePointer[Scalar[type], address_space]): + fn __init__(inout self, other: UnsafePointer[Scalar[type], address_space]): """Constructs a `DTypePointer` from a scalar pointer of the same type. Args: - value: The scalar pointer. + other: The scalar pointer. """ - self.address = value.value + self.address = other.address @always_inline("nodebug") fn __init__(inout self, value: Scalar[DType.address]): diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 9106e9f402..5399f52672 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -43,6 +43,8 @@ struct UnsafePointer[ `!kgen.pointer<`, T, `,`, address_space._value.value, `>` ] + alias type = T + # We're unsafe, so we can have unsafe things. References we make have # an immortal mutable lifetime, since we can't come up with a meaningful # lifetime for them anyway. @@ -54,7 +56,7 @@ struct UnsafePointer[ ]._mlir_type """The underlying pointer type.""" - var value: Self._mlir_type + var address: Self._mlir_type """The underlying pointer.""" @always_inline @@ -76,7 +78,7 @@ struct UnsafePointer[ Returns: The pointer. """ - return Self {value: value} + return Self {address: value} @always_inline fn __init__(value: Reference[T, _, _, address_space]) -> Self: @@ -88,7 +90,7 @@ struct UnsafePointer[ Returns: The pointer. """ - return Self {value: __mlir_op.`lit.ref.to_pointer`(value.value)} + return Self {address: __mlir_op.`lit.ref.to_pointer`(value.value)} @always_inline fn __init__(*, address: Int) -> Self: @@ -101,7 +103,7 @@ struct UnsafePointer[ The pointer. """ return Self { - value: __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( + address: __mlir_op.`pop.index_to_pointer`[_type = Self._mlir_type]( Scalar[DType.index](address).value ) } @@ -115,7 +117,7 @@ struct UnsafePointer[ Constructed nullptr UnsafePointer object. """ return Self { - value: __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] + address: __mlir_attr[`#interp.pointer<0> : `, Self._mlir_type] } @staticmethod @@ -173,7 +175,7 @@ struct UnsafePointer[ """ return __mlir_op.`pop.pointer.bitcast`[ _type = UnsafePointer[new_type, address_space]._mlir_type, - ](self.value) + ](self.address) @always_inline fn __int__(self) -> Int: @@ -184,7 +186,7 @@ struct UnsafePointer[ """ return __mlir_op.`pop.pointer_to_index`[ _type = __mlir_type.`!pop.scalar` - ](self.value) + ](self.address) fn __str__(self) -> String: return hex(self) @@ -324,7 +326,7 @@ struct UnsafePointer[ A reference to the value. """ return __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_ref_type]( - self.value + self.address ) @always_inline @@ -358,7 +360,7 @@ fn destroy_pointee(ptr: UnsafePointer[_, AddressSpace.GENERIC]): Args: ptr: The pointer whose pointee this destroys. """ - _ = __get_address_as_owned_value(ptr.value) + _ = __get_address_as_owned_value(ptr.address) @always_inline @@ -382,7 +384,7 @@ fn move_from_pointee[T: Movable](ptr: UnsafePointer[T, _]) -> T: Returns: The value at the pointer. """ - return __get_address_as_owned_value(ptr.value) + return __get_address_as_owned_value(ptr.address) @always_inline @@ -401,7 +403,7 @@ fn initialize_pointee[T: Movable](ptr: UnsafePointer[T, _], owned value: T): ptr: The pointer to initialize through. value: The value to emplace. """ - __get_address_as_uninit_lvalue(ptr.value) = value^ + __get_address_as_uninit_lvalue(ptr.address) = value^ @always_inline @@ -432,6 +434,6 @@ fn move_pointee[T: Movable](*, src: UnsafePointer[T, _], dst: UnsafePointer[T]): src: Source pointer that the value will be moved from. dst: Destination pointer that the value will be moved into. """ - __get_address_as_uninit_lvalue(dst.value) = __get_address_as_owned_value( - src.value + __get_address_as_uninit_lvalue(dst.address) = __get_address_as_owned_value( + src.address ) diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index f6d63184d5..f12c7e75f0 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -431,7 +431,7 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): # Methods # ===------------------------------------------------------------------=== # - fn as_ptr(self) -> Pointer[ElementType]: + fn as_ptr(self) -> LegacyPointer[ElementType]: """Get a pointer to the elements contained by this array. Returns: @@ -445,4 +445,4 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): # var base_ptr = Pointer[ElementType].address_of(self.array).address # TODO: Is the `gep` here necessary, or could this be a bitcast? var ptr = __mlir_op.`pop.array.gep`(base_ptr, Int(0).value) - return Pointer(ptr) + return LegacyPointer(ptr) diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index f70bd92386..dd669bf082 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -225,7 +225,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn each[i: Int](): if self._get_state()[] == i: alias q = Ts[i] - __get_address_as_owned_value(self._get_ptr[q]().value).__del__() + __get_address_as_owned_value( + self._get_ptr[q]().address + ).__del__() unroll[each, len(VariadicList(Ts))]() @@ -310,7 +312,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): debug_assert(Reference(self)[].isa[T](), "get: wrong variant type") return __mlir_op.`lit.ref.from_pointer`[ _type = Reference[T, mutability, self_life]._mlir_type - ](Reference(self)[]._get_ptr[T]().value) + ](Reference(self)[]._get_ptr[T]().address) @staticmethod fn _check[T: CollectionElement]() -> Int8: From f30d9836bf29f9b0446b597cc82c3216519713dd Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Tue, 16 Apr 2024 20:30:23 -0700 Subject: [PATCH 0178/2019] [Stdlib] Add a _macos_version helper function (#37882) This gives the macOS version triple and is used to constrain features in a followup PR. Closes [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 164427ca1011db75d2f506e37300f9f41693f2b8 --- stdlib/src/sys/info.mojo | 48 +++++++++++++++++++++++++- stdlib/test/sys/test_macos_target.mojo | 18 +++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 1aee78dba0..7cec3ce179 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -19,7 +19,7 @@ from sys.info import is_x86 ``` """ -from .ffi import _external_call_const +from .ffi import external_call, _external_call_const @always_inline("nodebug") @@ -672,3 +672,49 @@ fn num_performance_cores() -> Int: Int: The number of physical performance cores on the system. """ return _external_call_const["KGEN_CompilerRT_NumPerformanceCores", Int]() + + +@always_inline +fn _macos_version() raises -> Tuple[Int, Int, Int]: + """Gets the macOS version. + + Returns: + The version triple of macOS. + """ + + constrained[os_is_macos(), "the operating system must be macOS"]() + + alias INITIAL_CAPACITY = 32 + + var buf = List[Int8](capacity=INITIAL_CAPACITY) + var buf_len = Int(INITIAL_CAPACITY) + + var err = external_call["sysctlbyname", Int32]( + "kern.osproductversion".data(), + buf.data, + Pointer.address_of(buf_len), + Pointer[NoneType](), + Int(0), + ) + + if err: + raise "Unable to query macOS version" + + var osver = String(buf.steal_data(), buf_len) + + var major = 0 + var minor = 0 + var patch = 0 + + if "." in osver: + major = int(osver[: osver.find(".")]) + osver = osver[osver.find(".") + 1 :] + + if "." in osver: + minor = int(osver[: osver.find(".")]) + osver = osver[osver.find(".") + 1 :] + + if "." in osver: + patch = int(osver[: osver.find(".")]) + + return (major, minor, patch) diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 47d757cae6..453d47f72e 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -24,8 +24,11 @@ from sys.info import ( os_is_linux, os_is_macos, os_is_windows, + _macos_version, ) +from testing import assert_true + # CHECK-LABEL: test_os_query fn test_os_query(): @@ -50,5 +53,18 @@ fn test_os_query(): print(is_big_endian()) -fn main(): +def test_os_version(): + var major = 0 + var minor = 0 + var patch = 0 + + major, minor, patch = _macos_version() + + assert_true(major >= 13) + assert_true(minor >= 0) + assert_true(patch >= 0) + + +def main(): test_os_query() + test_os_version() From f4f4e99cdd65dffa6793f5d38af83f4760de203f Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 17 Apr 2024 06:55:16 -0700 Subject: [PATCH 0179/2019] [Stdlib] Add more overloads for the external call operations (#37896) Add more overloads to the external_call methods to be able to call functions with upto 15 arguments. We really need to use variadics or tuple here (#37895). MODULAR_ORIG_COMMIT_REV_ID: abef2d32b08c0aebf82c3ab8acf921266fabe83f --- stdlib/src/sys/ffi.mojo | 813 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 813 insertions(+) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 171b010750..8d39f62d26 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -426,6 +426,819 @@ fn external_call[ ) +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, +](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, arg1, arg2, arg3, arg4, arg5 + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, arg1, arg2, arg3, arg4, arg5 + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6 + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6 + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, + T9: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, + arg9: T9, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + T9: The tenth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + arg9: The tenth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, + T9: AnyRegType, + T10: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, + arg9: T9, + arg10: T10, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + T9: The tenth argument type. + T10: The eleventh argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + arg9: The tenth argument. + arg10: The eleventh argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, + T9: AnyRegType, + T10: AnyRegType, + T11: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, + arg9: T9, + arg10: T10, + arg11: T11, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + T9: The tenth argument type. + T10: The eleventh argument type. + T11: The twelfth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + arg9: The tenth argument. + arg10: The eleventh argument. + arg11: The twelfth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, + T9: AnyRegType, + T10: AnyRegType, + T11: AnyRegType, + T12: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, + arg9: T9, + arg10: T10, + arg11: T11, + arg12: T12, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + T9: The tenth argument type. + T10: The eleventh argument type. + T11: The twelfth argument type. + T12: The thirteenth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + arg9: The tenth argument. + arg10: The eleventh argument. + arg11: The twelfth argument. + arg12: The thirteenth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, + T9: AnyRegType, + T10: AnyRegType, + T11: AnyRegType, + T12: AnyRegType, + T13: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, + arg9: T9, + arg10: T10, + arg11: T11, + arg12: T12, + arg13: T13, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + T9: The tenth argument type. + T10: The eleventh argument type. + T11: The twelfth argument type. + T12: The thirteenth argument type. + T13: The fourteenth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + arg9: The tenth argument. + arg10: The eleventh argument. + arg11: The twelfth argument. + arg12: The thirteenth argument. + arg13: The fourteenth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + ) + + +@always_inline("nodebug") +fn external_call[ + callee: StringLiteral, + type: AnyRegType, + T0: AnyRegType, + T1: AnyRegType, + T2: AnyRegType, + T3: AnyRegType, + T4: AnyRegType, + T5: AnyRegType, + T6: AnyRegType, + T7: AnyRegType, + T8: AnyRegType, + T9: AnyRegType, + T10: AnyRegType, + T11: AnyRegType, + T12: AnyRegType, + T13: AnyRegType, + T14: AnyRegType, +]( + arg0: T0, + arg1: T1, + arg2: T2, + arg3: T3, + arg4: T4, + arg5: T5, + arg6: T6, + arg7: T7, + arg8: T8, + arg9: T9, + arg10: T10, + arg11: T11, + arg12: T12, + arg13: T13, + arg14: T14, +) -> type: + """Calls an external function. + + Parameters: + callee: The name of the external function. + type: The return type. + T0: The first argument type. + T1: The second argument type. + T2: The third argument type. + T3: The fourth argument type. + T4: The fifth argument type. + T5: The sixth argument type. + T6: The seventh argument type. + T7: The eighth argument type. + T8: The ninth argument type. + T9: The tenth argument type. + T10: The eleventh argument type. + T11: The twelfth argument type. + T12: The thirteenth argument type. + T13: The fourteenth argument type. + T14: The fifteenth argument type. + + Args: + arg0: The first argument. + arg1: The second argument. + arg2: The third argument. + arg3: The fourth argument. + arg4: The fifth argument. + arg5: The sixth argument. + arg6: The seventh argument. + arg7: The eighth argument. + arg8: The ninth argument. + arg9: The tenth argument. + arg10: The eleventh argument. + arg11: The twelfth argument. + arg12: The thirteenth argument. + arg13: The fourteenth argument. + arg14: The fifteenth argument. + + Returns: + The external call result. + """ + + @parameter + if _mlirtype_is_eq[type, NoneType](): + __mlir_op.`pop.external_call`[func = callee.value, _type=None]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + arg14, + ) + return rebind[type](None) + else: + return __mlir_op.`pop.external_call`[func = callee.value, _type=type]( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + arg14, + ) + + # ===----------------------------------------------------------------------===# # _external_call_const # ===----------------------------------------------------------------------===# From 97fc0505be1649bf787c17bcb9ef623404d354fc Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 17 Apr 2024 07:33:43 -0700 Subject: [PATCH 0180/2019] [******][macOS] Add initial cblas accelerate bindings (#37897) This adds basic bindings to the accelerate library to allow us to use cblas_gemm for the f32 matmul. Part of #37891 --------- Co-authored-by: Tracy Sharpe <99041446+raiseirql@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: a31db788a38f40c910251d5ee2f1561ec071c802 --- stdlib/src/sys/ffi.mojo | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 8d39f62d26..b98bfb768f 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -484,15 +484,7 @@ fn external_call[ T4: AnyRegType, T5: AnyRegType, T6: AnyRegType, -]( - arg0: T0, - arg1: T1, - arg2: T2, - arg3: T3, - arg4: T4, - arg5: T5, - arg6: T6, -) -> type: +](arg0: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) -> type: """Calls an external function. Parameters: From 8a852acb3d6cc9e50cff257222b01526bc05112a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 17 Apr 2024 08:54:56 -0700 Subject: [PATCH 0181/2019] [mojo-stdlib] Simplify `throw_python_exception_if_error_state`, NFC (#37908) The existing `__str__` method does the heavy lifting, just noticed by inspection. MODULAR_ORIG_COMMIT_REV_ID: d9329cadbd0e1c2ede33d32947fecd561f269849 --- stdlib/src/python/python.mojo | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 546acdce4d..7ab6326730 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -207,12 +207,9 @@ struct Python: cpython: The cpython instance we wish to error check. """ if cpython.PyErr_Occurred(): - var error = PythonObject(cpython.PyErr_Fetch()).__getattr__( - "__str__" - )() - var err: Error = cpython.PyUnicode_AsUTF8AndSize(error.py_object) + var error: Error = str(PythonObject(cpython.PyErr_Fetch())) cpython.PyErr_Clear() - raise err + raise error @staticmethod fn is_type(x: PythonObject, y: PythonObject) -> Bool: From 391de19cdade651fd9fb287f02550ba698553a83 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Wed, 17 Apr 2024 13:10:45 -0700 Subject: [PATCH 0182/2019] [Stdlib] Make DLHandle boolable (#37935) This switches DLHandle to be boolable so that we can use it as condition in if conditions. MODULAR_ORIG_COMMIT_REV_ID: f80d886398b237a78094de493474fe11a6f5bcfb --- stdlib/src/sys/ffi.mojo | 10 +++++++++- stdlib/test/sys/test_dlhandle.mojo | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/sys/test_dlhandle.mojo diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index b98bfb768f..1f17471a43 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -41,7 +41,7 @@ alias DEFAULT_RTLD = RTLD.NOW | RTLD.GLOBAL @value @register_passable -struct DLHandle(CollectionElement): +struct DLHandle(CollectionElement, Boolable): """Represents a dynamically linked library that can be loaded and unloaded. The library is loaded on initialization and unloaded on deletion of the object. @@ -85,6 +85,14 @@ struct DLHandle(CollectionElement): _ = external_call["dlclose", Int](self.handle) self.handle = DTypePointer[DType.int8].get_null() + fn __bool__(self) -> Bool: + """Checks if the handle is valid. + + Returns: + True if the DLHandle is not null and False otherwise. + """ + return self.handle.__bool__() + # TODO(#15590): Implement support for windows and remove the always_inline. @always_inline fn get_function[result_type: AnyRegType](self, name: String) -> result_type: diff --git a/stdlib/test/sys/test_dlhandle.mojo b/stdlib/test/sys/test_dlhandle.mojo new file mode 100644 index 0000000000..2aadf441ae --- /dev/null +++ b/stdlib/test/sys/test_dlhandle.mojo @@ -0,0 +1,26 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from sys.ffi import DLHandle +from testing import assert_false + + +def check_invalid_dlhandle(): + assert_false( + DLHandle("/an/invalid/library"), "the library is not valid location" + ) + + +def main(): + check_invalid_dlhandle() From 494a485e8ac90a6d62b023926b3832f15d94ed98 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Wed, 17 Apr 2024 16:31:57 -0500 Subject: [PATCH 0183/2019] [External][stdlib] Fixing a typo in the README.md (#37964) mojo-orig-commit: 66268b49e7a614a517105b1dad1113010e6ea033 Co-authored-by: Rob Parolin MODULAR_ORIG_COMMIT_REV_ID: 8448c434a0eceb4ca8c73d4768b1420962335f99 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe65959e21..0c83c48036 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). The nightly Mojo builds are subject to breakage and provide an inside view of how the development of Mojo is progressing. Use at your own risk -and be patient! Intall them using the instructions [here](./CONTRIBUTING.md). +and be patient! Install them using the instructions [here](./CONTRIBUTING.md). ## Contributing From d0ad79d85d8baa1f77a89f5e8f91e4582b0b7b33 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Wed, 17 Apr 2024 16:32:26 -0500 Subject: [PATCH 0184/2019] [External][stdlib] fix "write your first Mojo program" link (#37967) Signed-off-by: Ali Yousefi iamaliyousefi@gmail.com mojo-orig-commit: 8adddb088a21d3bacad10b4592740bb26087de7f Co-authored-by: Ali Yousefi Sabzevar MODULAR_ORIG_COMMIT_REV_ID: ad9c3b90eaa5e1a55eb8cfc40be782d949eea950 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c83c48036..6738ac4d40 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ or the standalone Mojo SDK: - [Get the Mojo SDK](https://docs.modular.com/mojo/manual/get-started/) Then follow the docs to [write your first Mojo -program](https://https://docs.modular.com/mojo/manual/get-started/hello-world). +program](https://docs.modular.com/mojo/manual/get-started/hello-world). ### Latest Nightly From 66b189178cf724061633f4777f9e86015e2c632f Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 18 Apr 2024 11:22:41 -0500 Subject: [PATCH 0185/2019] [stdlib] feature: Add non-allocating formatting abstraction (#37001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This abstraction will allow types to print themselves to a buffer (like String) or a stream (like stdout) without any wasteful intermediate String allocations. Currently Mojo's infrastructure for formatting types is built around the `Stringable` trait. Types format themselves by converting themselves to a String. When printing a type composed from other types, the `__str__` implementation has to first convert its fields to String values, and then concatenate those values together. This means that a statement as simple as: print(my_type) might end up creating dozens of intermediate String allocations even though ultimately the formatted data is simply being written to the stdout stream. ## Overview The framework added in this PR is made of several new items: * `Formattable` — trait implemented by types that can format themselves to a string - `Formattable.format_to(self, inout Formatter)` method; type formats itself by writing to the provided `Formatter`. * `Formatter` — struct that types write to; the ultimate output destination could be a String, file handle, stream like stdout, etc. With some non-fundamental, nice to have utilites built on top: * `String.format_seq` — method that returns a new string by concatenating `Formattable` arguments * `write_to` — convenience function for writing `Formattable` arguments to an arbitrary `Formatter` * `_print_fmt` — a temporary alternative to `print(..)` that uses `Formattable` instead of `Stringable` ## Example Usage ```mojo # Define a struct that knows how to format itself @value struct Point(Formattable): var x: Int var y: Int fn format_to(self, inout writer: Formatter): write_to(writer, "Point(", self.x, ", ", self.y, ")") ``` High-level API, construct a String from `Formattable` args: ```mojo var point = Point(2, 7) var s1 = String.format_sequence("Point is ", point) assert_equal(s1, "Point is Point(2, 7)") ``` For a type that knows how to format itself generically, it's only one line to base that types' `Stringable` implementation on its format logic: ```mojo struct Point(Formattable, Stringable): # ... fn __str__(self): return String.format_seq(self) fn format_to(self, inout writer: Formatter): ... ``` MODULAR_ORIG_COMMIT_REV_ID: 350589e46cf2284c7f343267893658958f0f7bd3 --- stdlib/src/builtin/int.mojo | 45 ++++++- stdlib/src/builtin/io.mojo | 52 ++++++++ stdlib/src/builtin/string.mojo | 86 ++++++++++++- stdlib/src/builtin/string_literal.mojo | 15 +++ stdlib/src/utils/_format.mojo | 129 +++++++++++++++++++ stdlib/src/utils/inlined_string.mojo | 9 +- stdlib/test/utils/test_format.mojo | 67 ++++++++++ stdlib/test/utils/test_format_to_stdout.mojo | 55 ++++++++ 8 files changed, 442 insertions(+), 16 deletions(-) create mode 100644 stdlib/src/utils/_format.mojo create mode 100644 stdlib/test/utils/test_format.mojo create mode 100644 stdlib/test/utils/test_format_to_stdout.mojo diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index dc89c768e8..b20804e30e 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -23,6 +23,8 @@ from builtin.io import _snprintf from utils._visualizers import lldb_formatter_wrapping_type from utils.index import StaticIntTuple +from utils._format import Formattable, Formatter +from utils.inlined_string import _ArrayMem # ===----------------------------------------------------------------------=== # # Intable @@ -169,7 +171,7 @@ fn int[T: IntableRaising](value: T) raises -> Int: @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct Int(Intable, Stringable, KeyElement, Boolable): +struct Int(Intable, Stringable, KeyElement, Boolable, Formattable): """This type represents an integer value.""" var value: __mlir_type.index @@ -295,12 +297,41 @@ struct Int(Intable, Stringable, KeyElement, Boolable): Returns: A string representation. """ - var buf = String._buffer_type() - var initial_buffer_size = _calc_initial_buffer_size(self) - buf.reserve(initial_buffer_size) - buf.size += _snprintf(buf.data, initial_buffer_size, "%li", self.value) - buf.size += 1 # for the null terminator. - return buf^ + + return String.format_sequence(self) + + fn format_to(self, inout writer: Formatter): + """ + Formats this integer to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # Stack allocate enough bytes to store any formatted 64-bit integer + alias size: Int = 32 + + var buf = _ArrayMem[Int8, size]() + + # Format the integer to the local byte array + var len = _snprintf( + rebind[UnsafePointer[Int8]](buf.as_ptr()), + size, + "%li", + self.value, + ) + + # Create a StringRef that does NOT include the NUL terminator written + # to the buffer. + # + # Write the formatted integer to the formatter. + # + # SAFETY: + # `buf` is kept alive long enough for the use of this StringRef. + writer.write_str(StringRef(buf.as_ptr(), len)) + + # Keep buf alive until we've finished with the StringRef + _ = buf^ @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index d773f1de32..ddaea62833 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -23,6 +23,7 @@ from builtin.builtin_list import _LITRefPackHelper from memory.unsafe import Pointer from utils import StringRef, unroll +from utils._format import Formattable, Formatter, write_to # ===----------------------------------------------------------------------=== # # Utilities @@ -402,3 +403,54 @@ fn print[ _put(end) if flush: _flush() + + +# ===----------------------------------------------------------------------=== # +# print_fmt +# ===----------------------------------------------------------------------=== # + + +# TODO: +# Finish transition to using non-allocating formatting abstractions by +# default, replace `print` with this function. +@no_inline +fn _print_fmt[ + T: Formattable, *Ts: Formattable +]( + first: T, + *rest: *Ts, + sep: StringLiteral = " ", + end: StringLiteral = "\n", + flush: Bool = False, +): + """Prints elements to the text stream. Each element is separated by `sep` + and followed by `end`. + + This print function does not perform unnecessary intermediate String + allocations during formatting. + + Parameters: + T: The first element type. + Ts: The remaining element types. + + Args: + first: The first element. + rest: The remaining elements. + sep: The separator used between elements. + end: The String to write after printing the elements. + flush: If set to true, then the stream is forcibly flushed. + """ + var writer = Formatter.stdout() + + write_to(writer, first) + + @parameter + fn print_elt[T: Formattable](a: T): + write_to(writer, sep, a) + + rest.each[print_elt]() + + write_to(writer, end) + + if flush: + _flush() diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 0f73a5985b..f789d06a53 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -27,6 +27,7 @@ from memory.unsafe import DTypePointer, Pointer from utils import StringRef from utils.index import StaticIntTuple from utils.static_tuple import StaticTuple +from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf @@ -314,7 +315,15 @@ fn isspace(c: Int8) -> Bool: # ===----------------------------------------------------------------------===# # String # ===----------------------------------------------------------------------===# -struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): +struct String( + Sized, + Stringable, + IntableRaising, + KeyElement, + Boolable, + Formattable, + ToFormatter, +): """Represents a mutable string.""" alias _buffer_type = List[Int8] @@ -325,6 +334,10 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): fn __str__(self) -> String: return self + # ===------------------------------------------------------------------===# + # Initializers + # ===------------------------------------------------------------------===# + @always_inline fn __init__(inout self, owned impl: Self._buffer_type): """Construct a string from a buffer of bytes. @@ -462,6 +475,10 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): return String(buff^) + # ===------------------------------------------------------------------===# + # Operator dunders + # ===------------------------------------------------------------------===# + @always_inline fn __bool__(self) -> Bool: """Checks if the string is not empty. @@ -640,6 +657,73 @@ struct String(Sized, Stringable, IntableRaising, KeyElement, Boolable): # Copy the data alongside the terminator. memcpy(self._as_ptr() + self_len, other._as_ptr(), other_len + 1) + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # + + @staticmethod + fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + """ + Construct a string by concatenating a sequence of formattable arguments. + + Args: + args: A sequence of formattable arguments. + + Parameters: + Ts: The types of the arguments to format. Each type must be satisfy + `Formattable`. + + Returns: + A string formed by formatting the argument sequence. + """ + + var output = String() + var writer = output._unsafe_to_formatter() + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(writer) + + args.each[write_arg]() + + return output^ + + fn format_to(self, inout writer: Formatter): + """ + Formats this string to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # SAFETY: + # Safe because `self` is borrowed, so its lifetime + # extends beyond this function. + writer.write_str(self._strref_dangerous()) + + fn _unsafe_to_formatter(inout self) -> Formatter: + """ + Constructs a formatter that will write to this mutable string. + + Safety: + The returned `Formatter` holds a mutable pointer to this `String` + value. This `String` MUST outlive the `Formatter` instance. + """ + + fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): + var ptr: UnsafePointer[String] = ptr0.bitcast[String]() + + # FIXME: + # String.__iadd__ currently only accepts a String, meaning this + # RHS will allocate unneccessarily. + ptr[] += strref + + return Formatter( + write_to_string, + # Arg data + UnsafePointer.address_of(self).bitcast[NoneType](), + ) + fn join[rank: Int](self, elems: StaticIntTuple[rank]) -> String: """Joins the elements from the tuple using the current string as a delimiter. diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 90296d4784..5970ea4105 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -19,6 +19,7 @@ from memory.unsafe import DTypePointer from utils import StringRef from utils._visualizers import lldb_formatter_wrapping_type +from utils._format import Formattable, Formatter from .string import _atol @@ -35,6 +36,7 @@ struct StringLiteral( Stringable, KeyElement, Boolable, + Formattable, ): """This type represents a string literal. @@ -145,6 +147,19 @@ struct StringLiteral( """ return self + fn format_to(self, inout writer: Formatter): + """ + Formats this string literal to the provided formatter. + + Args: + writer: The formatter to write to. + """ + + # SAFETY: + # Safe because `self` is borrowed, so the lifetime of this + # StringRef extends beyond this function. + writer.write_str(StringRef(self)) + fn __contains__(self, substr: StringLiteral) -> Bool: """Returns True if the substring is contained within the current string. diff --git a/stdlib/src/utils/_format.mojo b/stdlib/src/utils/_format.mojo new file mode 100644 index 0000000000..f7a4b5411c --- /dev/null +++ b/stdlib/src/utils/_format.mojo @@ -0,0 +1,129 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Implements a formatter abstraction for objects that can format +themselves to a string. +""" + +from builtin.io import _put + +# ===----------------------------------------------------------------------===# +# Interface traits +# ===----------------------------------------------------------------------===# + + +trait Formattable: + """ + The `Formattable` trait describes a type that can be converted to a stream + of UTF-8 encoded data by writing to a formatter object. + """ + + fn format_to(self, inout writer: Formatter): + ... + + +trait ToFormatter: + """ + The `ToFormatter` trait describes a type that can be written to by a + `Formatter` object. + """ + + fn _unsafe_to_formatter(inout self) -> Formatter: + ... + + +# ===----------------------------------------------------------------------===# +# Formatter +# ===----------------------------------------------------------------------===# + + +struct Formatter: + """ + A `Formatter` is used by types implementing the `Formattable` trait to write + bytes to the underlying formatter output buffer or stream. + """ + + # FIXME(#37996): + # This manual implementation of a closure function ptr + closure data + # arg is needed to workaround a bug with `escaping` closure capture values + # seemingly getting clobbered in between when the closure was constructed + # and first called. Once that bug is fixed, this should be replaced with + # an `escaping` closure again. + var _write_func: fn (UnsafePointer[NoneType], StringRef) -> None + var _write_func_arg: UnsafePointer[NoneType] + """Closure argument passed to `_write_func`.""" + + # ===------------------------------------------------------------------===# + # Initializers + # ===------------------------------------------------------------------===# + + fn __init__[F: ToFormatter](inout self, inout output: F): + self = output._unsafe_to_formatter() + + fn __init__( + inout self, + func: fn (UnsafePointer[NoneType], StringRef) -> None, + arg: UnsafePointer[NoneType], + ): + """ + Constructs a formatter from any closure that accepts string refs. + """ + self._write_func = func + self._write_func_arg = arg + + fn __moveinit__(inout self, owned other: Self): + self._write_func = other._write_func + self._write_func_arg = other._write_func_arg + + # ===------------------------------------------------------------------=== # + # Methods + # ===------------------------------------------------------------------=== # + + @always_inline + fn write_str(inout self, strref: StringRef): + """ + Write a string to this formatter. + + Args: + strref: The string to write to this formatter. Must NOT be null + terminated. + """ + self._write_func(self._write_func_arg, strref) + + # ===------------------------------------------------------------------=== # + # Factory methods + # ===------------------------------------------------------------------=== # + + @always_inline + @staticmethod + fn stdout() -> Self: + """ + Constructs a formatter that writes directly to stdout. + """ + + @always_inline + fn write_to_stdout(_data: UnsafePointer[NoneType], strref: StringRef): + _put(strref) + + return Formatter(write_to_stdout, UnsafePointer[NoneType]()) + + +fn write_to[*Ts: Formattable](inout writer: Formatter, *args: *Ts): + """ + Write a sequence of formattable arguments to the provided formatter. + """ + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(writer) + + args.each[write_arg]() diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index f12c7e75f0..fd6415fe75 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -438,11 +438,4 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): A pointer to the elements contained by this array. """ - var base_ptr = Reference( - self.storage.array - ).get_legacy_pointer().address - - # var base_ptr = Pointer[ElementType].address_of(self.array).address - # TODO: Is the `gep` here necessary, or could this be a bitcast? - var ptr = __mlir_op.`pop.array.gep`(base_ptr, Int(0).value) - return LegacyPointer(ptr) + return LegacyPointer.address_of(self.storage).bitcast[ElementType]() diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo new file mode 100644 index 0000000000..0ad832f629 --- /dev/null +++ b/stdlib/test/utils/test_format.mojo @@ -0,0 +1,67 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo -debug-level full %s + +from utils._format import Formattable, write_to, Formatter +from testing import assert_equal + + +fn main() raises: + test_formatter_of_string() + test_string_format_seq() + test_stringable_based_on_format() + + +@value +struct Point(Formattable, Stringable): + var x: Int + var y: Int + + fn format_to(self, inout writer: Formatter): + write_to(writer, "Point(", self.x, ", ", self.y, ")") + + fn __str__(self) -> String: + return String.format_sequence(self) + + +fn test_formatter_of_string() raises: + # + # Test format_to(String) + # + var s1 = String() + var s1_fmt = Formatter(s1) + Point(2, 7).format_to(s1_fmt) + assert_equal(s1, "Point(2, 7)") + + # + # Test write_to(String, ..) + # + var s2 = String() + var s2_fmt = Formatter(s2) + write_to(s2_fmt, Point(3, 8)) + assert_equal(s2, "Point(3, 8)") + + +fn test_string_format_seq() raises: + var s1 = String.format_sequence("Hello, ", "World!") + assert_equal(s1, "Hello, World!") + + var s2 = String.format_sequence("point = ", Point(2, 7)) + assert_equal(s2, "point = Point(2, 7)") + + var s3 = String.format_sequence() + assert_equal(s3, "") + + +fn test_stringable_based_on_format() raises: + assert_equal(str(Point(10, 11)), "Point(10, 11)") diff --git a/stdlib/test/utils/test_format_to_stdout.mojo b/stdlib/test/utils/test_format_to_stdout.mojo new file mode 100644 index 0000000000..bc0adecb59 --- /dev/null +++ b/stdlib/test/utils/test_format_to_stdout.mojo @@ -0,0 +1,55 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo -debug-level full %s + +from utils._format import Formatter, write_to, Formattable + +from builtin.io import _print_fmt + + +fn main() raises: + test_write_to_stdout() + + +@value +struct Point(Formattable): + var x: Int + var y: Int + + fn format_to(self, inout writer: Formatter): + write_to(writer, "Point(", self.x, ", ", self.y, ")") + + +# CHECK-LABEL: test_write_to_stdout +fn test_write_to_stdout(): + print("== test_write_to_stdout") + + var stdout = Formatter.stdout() + + # CHECK: Hello, World! + write_to(stdout, "Hello, World!") + + # CHECK: point = Point(1, 1) + var point = Point(1, 1) + write_to(stdout, "point = ", point) + + +# CHECK-LABEL: test_print_fmt() +fn test_print_fmt(): + print("== test_print_fmt") + + # CHECK: The quick brown fox... + _print_fmt("The quick brown fox...") + + # CHECK: ...jumps over the lazy ..Point(2, 5) ??? + _print_fmt("...jumps over the lazy ..", Point(2, 5), " ???") From e4950413371013f667559813e62507c54b5f3cad Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Thu, 18 Apr 2024 11:56:15 -0500 Subject: [PATCH 0186/2019] [mojo-lang] Support declaring functions with both optional and variadic arguments (#37819) We supported variadics and optional arguments and parameters, but not in the same signature. This patch fixes that by ensuring that variadics can have a sentinel default value associated with them that never gets accessed, but allows us to maintain the invariant of "default correspond to the trailing arguments". The patch enables both positional and keyword variadics in the presence of optional arguments. Positional variadic parameters after optional parameters are also enabled. Closes https://github.com/modularml/mojo/issues/1918. MODULAR_ORIG_COMMIT_REV_ID: f37e9334c32fe0d3bf5e751e43886344e3b09bee --- docs/changelog.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 223703f1a4..d64b587c36 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -82,6 +82,25 @@ what we publish. - The `testing.assert_almost_equal` and `math.isclose` functions now have an `equal_nan` flag. When set to True, then NaNs are considered equal. +- Mojo now supports declaring functions that have both optional and variadic + arguments, both positional and keyword-only. E.g. this now works: + + ```mojo + fn variadic_arg_after_default( + a: Int, b: Int = 3, *args: Int, c: Int, d: Int = 1, **kwargs: Int + ): ... + ``` + + Positional variadic parameters also work in the presence of optional + parameters, i.e.: + + ```mojo + fn variadic_param_after_default[e: Int, f: Int = 2, *params: Int](): + pass + ``` + + Note that variadic keyword parameters are not supported yet. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From e1cceed8cc04913a9838a9b6bbbbc1381f9615c9 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:23:47 -0500 Subject: [PATCH 0187/2019] [External][stdlib] export InlinedFixedVector from collections (#37968) mojo-orig-commit: 133e0a59c1d909e544c1e351053da30566342aa1 Co-authored-by: farhan MODULAR_ORIG_COMMIT_REV_ID: 249f7f8b6cbad4abdf60c40247d92221b5d2a289 --- stdlib/src/collections/__init__.mojo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/src/collections/__init__.mojo b/stdlib/src/collections/__init__.mojo index fe68dca517..cca96b5763 100644 --- a/stdlib/src/collections/__init__.mojo +++ b/stdlib/src/collections/__init__.mojo @@ -16,4 +16,7 @@ from .dict import Dict, KeyElement from .list import List from .optional import Optional, OptionalReg from .set import Set -from .vector import CollectionElement +from .vector import ( + CollectionElement, + InlinedFixedVector, +) From 722be15d55e5783f8ec593a9be7a4afba0448d4e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:24:09 -0500 Subject: [PATCH 0188/2019] [External][stdlib] Fix small typo time.mojo (#2067) (#37969) * Update time.mojo mojo-orig-commit: c9d58a745a1b0a7c6ef66dca6625e2ef34848340 Co-authored-by: visserle <99926564+visserle@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 78d1502e2d1d04551cb1ec81d30a175c9f1ce1b7 --- stdlib/src/time/time.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 390c1ee2e8..5d57175720 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -44,7 +44,7 @@ alias _MSEC_PER_SEC = 1000 alias _NSEC_PER_SEC = _NSEC_PER_USEC * _USEC_PER_MSEC * _MSEC_PER_SEC # LARGE_INTEGER in Windows represent a signed 64 bit integer. Internally it -# is implemented as a union of of one 64 bit integer or two 32 bit integers +# is implemented as a union of one 64 bit integer or two 32 bit integers # for 64/32 bit compilers. # https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-large_integer-r1 alias _WINDOWS_LARGE_INTEGER = Int64 @@ -83,7 +83,7 @@ struct _FILETIME: fn as_nanoseconds(self) -> Int: # AFTER subtracting windows offset the return value fits in a signed int64 - # BEFORE subtracting windows offset the return value does not fit in a signed int64 + # BEFORE subtracting windows offset the return value does not fit in a signed int64 # Taken from https://github.com/microsoft/STL/blob/c8d1efb6d504f6392acf8f6d01fd703f7c8826c0/stl/src/xtime.cpp#L50 alias windowsToUnixEpochOffsetNs: Int = 0x19DB1DED53E8000 var interval_count: UInt64 = ( From ec1092f6804fa6e4082216950b74057b5c80dc18 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:24:30 -0500 Subject: [PATCH 0189/2019] [External][stdlib] Check compiler versions before building (#37971) There's often breaking changes in the compiler that mandate an update to the latest `nightly/mojo` compiler in order to work with the latest standard library. Currently, if a user installs a newer `nightly/mojo` compiler but hasn't updated the library source code, they'll get pages of bad errors depending on the breaking change, often "cryptic" MLIR errors. This is not a great user experience, and does not tell contributors what they can/should do. To improve this user experience, check the current installed compiler version in the script when building the standard library. If there is a mismatch between the expected compiler version (stored in a file in the repo) and the installed compiler, give a good error message telling the user to update their `nightly/mojo` compiler. Note: - An alternative approach is to query to find the "latest compiler version released" and check against that, but that has the caveat that just because internally we publish a new `nightly/mojo` package, if someone hasn't updated their local copy of the standard library source code, they shouldn't be prevented from building just because something out-of-band changed/got updated. This approach keeps the local flow working until they update their copy of the library source code. - We'll probably want to rewrite these bash scripts into Python for a bit better maintenance soon. mojo-orig-commit: 1a8f912cb19722621b386a018bfd9a5be800e07a Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: 5b11e102a29285f1336f1a8cefe3fbc07f8842e2 --- stdlib/scripts/build-stdlib.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stdlib/scripts/build-stdlib.sh b/stdlib/scripts/build-stdlib.sh index 3821bec079..8776ba67ef 100755 --- a/stdlib/scripts/build-stdlib.sh +++ b/stdlib/scripts/build-stdlib.sh @@ -19,6 +19,17 @@ REPO_ROOT="${SCRIPT_DIR}"/../.. BUILD_DIR="${REPO_ROOT}"/build mkdir -p "${BUILD_DIR}" +ACTUAL_COMPILER_VERSION=$(mojo --version | tr " " "\n" | sed -n 2p) +EXPECTED_COMPILER_VERSION=$(<"${REPO_ROOT}"/stdlib/COMPATIBLE_COMPILER_VERSION) + +if [ "${EXPECTED_COMPILER_VERSION}" != "${ACTUAL_COMPILER_VERSION}" ]; then + echo "Mismatch in compiler versions! Cannot build the standard library." + echo "Expected compiler version: ${EXPECTED_COMPILER_VERSION}" + echo "Current installed compiler version: ${ACTUAL_COMPILER_VERSION}" + echo "Please run modular update nightly/mojo to get the latest compiler." + exit 1 +fi + STDLIB_PATH="${REPO_ROOT}/stdlib/src" echo "Packaging up the Standard Library." From f5cb6bbda671e41de896b147e70a06b79e606e85 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:25:14 -0500 Subject: [PATCH 0190/2019] [External][Utils] Remove doc string scripts (#2118) (#38019) These doc strings scripts for doing API doc string validation don't yet work. Remove them for now to avoid any confusion for other contributors. mojo-orig-commit: b58f12dfb0e260e52d04229077b731191a717c06 Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: eda17f7b1f6cb4a57a992d4921a207f40ac58482 --- stdlib/scripts/check-doc-strings.sh | 34 -------------------- stdlib/scripts/check-file-is-empty.py | 45 --------------------------- 2 files changed, 79 deletions(-) delete mode 100755 stdlib/scripts/check-doc-strings.sh delete mode 100755 stdlib/scripts/check-file-is-empty.py diff --git a/stdlib/scripts/check-doc-strings.sh b/stdlib/scripts/check-doc-strings.sh deleted file mode 100755 index a497ce56cd..0000000000 --- a/stdlib/scripts/check-doc-strings.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -##===----------------------------------------------------------------------===## -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -##===----------------------------------------------------------------------===## - -set -euo pipefail - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -REPO_ROOT="${SCRIPT_DIR}/../.." - -check_doc_string() { - local pkg=$1 - echo "Checking API doc string conformance for package ${pkg}" - - local warnings_file="${BUILD_DIR}/${pkg}_warnings.txt" - rm -f "${warnings_file}" - mojo doc -warn-missing-doc-strings -o /dev/null "${REPO_ROOT}/${pkg}" > "${warnings_file}" 2>&1 - python3 "${SCRIPT_DIR}"/check-file-is-empty.py "${warnings_file}" -} - -BUILD_DIR="${REPO_ROOT}"/build -mkdir -p "${BUILD_DIR}" - -check_doc_string stdlib -check_doc_string test_utils diff --git a/stdlib/scripts/check-file-is-empty.py b/stdlib/scripts/check-file-is-empty.py deleted file mode 100755 index fc856ed2f5..0000000000 --- a/stdlib/scripts/check-file-is-empty.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # - -import argparse -import os -import sys - - -def main(): - parser = argparse.ArgumentParser( - description=( - "Exits successfully if the file at the given path is empty or does" - " not exist. Otherwise, prints the file's contents, then exits" - " unsuccessfully." - ) - ) - parser.add_argument("path") - args = parser.parse_args() - - if not os.path.exists(args.path): - return - - with open(args.path, "r") as f: - content = f.read().strip() - if content: - print( - f"error: '{args.path}' is not empty:\n{content}", - file=sys.stderr, - ) - exit(1) - - -if __name__ == "__main__": - main() From 764a788fedd9812c635291b2f0736e31686c4b42 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:26:06 -0500 Subject: [PATCH 0191/2019] [External][mojo-stdlib] Replace register_passable __init__ -> Self usages in stdlib/src/collections (#2089) (#38023) Signed-off-by: Mert mojo-orig-commit: 7172a728b32bdd6162c3ec0d6a06663b5b2cb29d Co-authored-by: Mert MODULAR_ORIG_COMMIT_REV_ID: 6e3d64666fe53b74b29fa57273e7777e3246f1f1 --- stdlib/src/collections/optional.mojo | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index e77d23806d..6e25266bc2 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -269,13 +269,9 @@ struct OptionalReg[T: AnyRegType](Boolable): alias _type = __mlir_type[`!kgen.variant<`, T, `, i1>`] var _value: Self._type - fn __init__() -> Self: - """Create an optional without a value. - - Returns: - The optional. - """ - return Self(None) + fn __init__(inout self): + """Create an optional with a value of None.""" + self = Self(None) fn __init__(value: T) -> Self: """Create an optional with a value. From 03ce9310a9bd3a2258fd465a2ce5e6219a197966 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:26:34 -0500 Subject: [PATCH 0192/2019] [External][docs] Fix minor typos in API doc strings (#2079) (#38026) Fix a few minor typos in some API doc strings. Signed-off-by: Jalin Wang mojo-orig-commit: 6943251f8d5bef775bff6d51b4a2b2f5e684830b Co-authored-by: Jalin Wang MODULAR_ORIG_COMMIT_REV_ID: cb0f3b74878dcc858a3e83214641917daa3d7612 --- stdlib/src/pathlib/path.mojo | 4 ++-- stdlib/src/random/random.mojo | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 2be4751f29..6a004634fa 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -290,7 +290,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): pathsegments: The path segments. Returns: - The path concatination with the pathsegments using the + The path concatenation with the pathsegments using the directory separator. """ if len(pathsegments) == 0: @@ -307,7 +307,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): """Gets the list of entries contained in the path provided. Returns: - Returns the list of entries in the path provided. + The list of entries in the path provided. """ var ls = listdir(self) diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 6f9a701063..23b2ef2c67 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -162,7 +162,7 @@ fn rand[type: DType](ptr: DTypePointer[type], size: Int): fn randn_float64(mean: Float64 = 0.0, variance: Float64 = 1.0) -> Float64: - """Returns a random double sampled from Normal(mean, variance) distribution. + """Returns a random double sampled from a Normal(mean, variance) distribution. Args: mean: Normal distribution mean. From 6e61ba9fc7c70b9237eaf905682633ba32d4fe66 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:26:52 -0500 Subject: [PATCH 0193/2019] [External][stdlib] Resolve `../..` in `build-stdlib.sh` (#2180) (#38028) Prior to this patch, running `build-stdlib.sh` from the `modularml/mojo` repository root resulted in the following output: ``` $ ./stdlib/scripts/build-stdlib.sh Packaging up the Standard Library. Successfully created /Users/brian/src/mojo/stdlib/scripts/../../build/stdlib.mojopkg ``` With this patch, the printed path is simpler: ``` $ ./stdlib/scripts/build-stdlib.sh Packaging up the Standard Library. Successfully created /Users/brian/src/mojo/build/stdlib.mojopkg ``` Signed-off-by: Brian Gesiak mojo-orig-commit: 1af3078dad059064e62d04a044021c147551b811 Co-authored-by: Brian Gesiak MODULAR_ORIG_COMMIT_REV_ID: adcc9335e8cc78e0fc765cc75fbee573fd47cb09 --- stdlib/scripts/build-stdlib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/scripts/build-stdlib.sh b/stdlib/scripts/build-stdlib.sh index 8776ba67ef..aa4e086da6 100755 --- a/stdlib/scripts/build-stdlib.sh +++ b/stdlib/scripts/build-stdlib.sh @@ -15,7 +15,7 @@ set -euo pipefail SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -REPO_ROOT="${SCRIPT_DIR}"/../.. +REPO_ROOT=$(realpath "${SCRIPT_DIR}/../..") BUILD_DIR="${REPO_ROOT}"/build mkdir -p "${BUILD_DIR}" From c204a2beae62a15ecf47c04c5c8de0109b98629a Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:27:25 -0500 Subject: [PATCH 0194/2019] [External][mojo-stdlib] Fix example in docstring for `Optional` (#2176) (#38029) Signed-off-by: Yiwu Chen <210at85@gmail.com> mojo-orig-commit: 997a917fbfea3822b7aa9490e9966e3f223ab630 Co-authored-by: soraros MODULAR_ORIG_COMMIT_REV_ID: 04b4c916b2435267474d17b986f4147c9f086fe3 --- stdlib/src/collections/optional.mojo | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 6e25266bc2..cdce056c4c 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -22,12 +22,12 @@ var a = Optional(1) var b = Optional[Int](None) if a: print(a.value()) # prints 1 -if b: # b is False, so no print +if b: # bool(b) is False, so no print print(b.value()) var c = a.or_else(2) var d = b.or_else(2) -print(c.value()) # prints 1 -print(d.value()) # prints 2 +print(c) # prints 1 +print(d) # prints 2 ``` """ @@ -62,12 +62,12 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): var b = Optional[Int](None) if a: print(a.value()) # prints 1 - if b: # b is False, so no print + if b: # bool(b) is False, so no print print(b.value()) var c = a.or_else(2) var d = b.or_else(2) - print(c.value()) # prints 1 - print(d.value()) # prints 2 + print(c) # prints 1 + print(d) # prints 2 ``` Parameters: From 09412f7a3de7410dc30adab93926c1c5e487590b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:48:15 -0500 Subject: [PATCH 0195/2019] [External][mojo-stdlib] Include width and alignment constraint checks for pointer store and load (#2156) (#38032) * Include non-negative constraint check for pointer store and load * Fix formatting issue * Address comments mojo-orig-commit: a8f7393a0e1e3e0a307391b844e1f00e9c4a05d9 Co-authored-by: Ehsan M. Kermani <6980212+ehsanmok@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: cc9cd4f3c5ab91dd8a584be7e40515a5646358dd --- stdlib/src/memory/unsafe.mojo | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 47ef2cf496..2cf9e7297f 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -326,6 +326,9 @@ struct LegacyPointer[ fn load[*, alignment: Int = Self._default_alignment](self) -> type: """Loads the value the LegacyPointer object points to. + Constraints: + The alignment must be a positive integer value. + Parameters: alignment: The minimal alignment of the address. @@ -340,6 +343,9 @@ struct LegacyPointer[ ](self, offset: T) -> type: """Loads the value the LegacyPointer object points to with the given offset. + Constraints: + The alignment must be a positive integer value. + Parameters: T: The Intable type of the offset. alignment: The minimal alignment of the address. @@ -350,6 +356,9 @@ struct LegacyPointer[ Returns: The loaded value. """ + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() return __mlir_op.`pop.load`[alignment = alignment.value]( self.offset(offset).address ) @@ -361,6 +370,9 @@ struct LegacyPointer[ """Stores the specified value to the location the LegacyPointer object points to with the given offset. + Constraints: + The alignment must be a positive integer value. + Parameters: T: The Intable type of the offset. alignment: The minimal alignment of the address. @@ -376,12 +388,18 @@ struct LegacyPointer[ """Stores the specified value to the location the LegacyPointer object points to. + Constraints: + The alignment value must be a positive integer. + Parameters: alignment: The minimal alignment of the address. Args: value: The value to store. """ + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() __mlir_op.`pop.store`[alignment = alignment.value](value, self.address) @always_inline("nodebug") @@ -859,6 +877,9 @@ struct DTypePointer[ ](self) -> SIMD[type, width]: """Loads the value the Pointer object points to. + Constraints: + The width and alignment must be positive integer values. + Parameters: width: The SIMD width. alignment: The minimal alignment of the address. @@ -874,6 +895,9 @@ struct DTypePointer[ ](self, offset: T) -> SIMD[type, width]: """Loads the value the Pointer object points to with the given offset. + Constraints: + The width and alignment must be positive integer values. + Parameters: T: The Intable type of the offset. width: The SIMD width. @@ -902,6 +926,9 @@ struct DTypePointer[ ](self, offset: T, val: SIMD[type, width]): """Stores a single element value at the given offset. + Constraints: + The width and alignment must be positive integer values. + Parameters: T: The Intable type of the offset. width: The SIMD width. @@ -919,6 +946,9 @@ struct DTypePointer[ ](self, val: SIMD[type, width]): """Stores a single element value. + Constraints: + The width and alignment must be positive integer values. + Parameters: width: The SIMD width. alignment: The minimal alignment of the address. @@ -926,6 +956,10 @@ struct DTypePointer[ Args: val: The value to store. """ + constrained[width > 0, "width must be a positive integer value"]() + constrained[ + alignment > 0, "alignment must be a positive integer value" + ]() self.address.bitcast[SIMD[type, width]]().store[alignment=alignment]( val ) From 0f826dd90a7dd26f102c0fbd616074878b180125 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:48:30 -0500 Subject: [PATCH 0196/2019] [External][mojo-stdlib] Make assert_equal more generic by using traits (#2150) (#38036) Adds a Testable trait that requires EqualityComparable and Stringable for any type being compared via a testing function. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: f72199c1bbd4ce7f1ed19d5911671dff31e20473 --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: 899b77cd7bb0e81b2bd59ff30974d66c546368f1 --- stdlib/src/testing/testing.mojo | 20 ++++++++--- stdlib/test/testing/test_assertion.mojo | 48 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 stdlib/test/testing/test_assertion.mojo diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index a4269c7d82..8d29cc4f10 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -104,13 +104,21 @@ fn assert_false[ raise Error("AssertionError: " + msg) -# TODO: Collapse these two overloads for generic T that has the -# Equality Comparable trait. +trait Testable(EqualityComparable, Stringable): + """A trait that a struct should conform to if we do equality testing on it. + """ + + pass + + @always_inline -fn assert_equal(lhs: Int, rhs: Int, msg: String = "") raises: +fn assert_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: """Asserts that the input values are equal. If it is not then an Error is raised. + Parameters: + T: A Testable type. + Args: lhs: The lhs of the equality. rhs: The rhs of the equality. @@ -123,6 +131,7 @@ fn assert_equal(lhs: Int, rhs: Int, msg: String = "") raises: raise _assert_equal_error(str(lhs), str(rhs), msg=msg) +# TODO: Remove the String and SIMD overloads once we have more powerful traits. @always_inline fn assert_equal(lhs: String, rhs: String, msg: String = "") raises: """Asserts that the input values are equal. If it is not then an Error @@ -164,10 +173,13 @@ fn assert_equal[ @always_inline -fn assert_not_equal(lhs: Int, rhs: Int, msg: String = "") raises: +fn assert_not_equal[T: Testable](lhs: T, rhs: T, msg: String = "") raises: """Asserts that the input values are not equal. If it is not then an Error is raised. + Parameters: + T: A Testable type. + Args: lhs: The lhs of the inequality. rhs: The rhs of the inequality. diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo new file mode 100644 index 0000000000..cb7cc69ff7 --- /dev/null +++ b/stdlib/test/testing/test_assertion.mojo @@ -0,0 +1,48 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo -debug-level full %s + +from testing import assert_equal, assert_not_equal, assert_raises + + +@value +struct DummyStruct: + var value: Int + + fn __eq__(self, other: Self) -> Bool: + return self.value == other.value + + fn __ne__(self, other: Self) -> Bool: + return self.value != other.value + + fn __str__(self) -> String: + return "Dummy" # Can't be used for equality + + +def test_assert_equal_is_generic(): + assert_equal(DummyStruct(1), DummyStruct(1)) + + with assert_raises(): + assert_equal(DummyStruct(1), DummyStruct(2)) + + +def test_assert_not_equal_is_generic(): + assert_not_equal(DummyStruct(1), DummyStruct(2)) + + with assert_raises(): + assert_not_equal(DummyStruct(1), DummyStruct(1)) + + +def main(): + test_assert_equal_is_generic() + test_assert_not_equal_is_generic() From d44711b082c6793e80fe3863b519127bb7c02565 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 12:53:33 -0500 Subject: [PATCH 0197/2019] [External][stdlib] Replace every instance of foo.str() with str(foo) (#37966) Replace every instance of foo.str() with str(foo) mojo-orig-commit: d8daadb58974316b5c44f2e2ed553149d3907bd2 --------- Co-authored-by: Parth Seth MODULAR_ORIG_COMMIT_REV_ID: f8f87827a64cb86578fe4701b996727eafc90843 --- docs/manual/values/ownership.ipynb | 2 +- stdlib/src/builtin/error.mojo | 2 +- stdlib/src/builtin/io.mojo | 2 +- stdlib/src/pathlib/path.mojo | 2 +- stdlib/test/python/test_ownership.mojo | 10 +++++----- stdlib/test/python/test_python_to_mojo.mojo | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/manual/values/ownership.ipynb b/docs/manual/values/ownership.ipynb index a544155c03..c15e11987f 100644 --- a/docs/manual/values/ownership.ipynb +++ b/docs/manual/values/ownership.ipynb @@ -182,7 +182,7 @@ "\n", "def print_shape(borrowed tensor: Tensor[DType.float32]):\n", " shape = tensor.shape()\n", - " print(shape.__str__())\n", + " print(str(shape))\n", "\n", "var tensor = Tensor[DType.float32](256, 256)\n", "print_shape(tensor)" diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 2c87671df8..341f68494a 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -138,7 +138,7 @@ struct Error(Stringable, Boolable): Returns: A printable representation of the error message. """ - return self.__str__() + return str(self) @always_inline fn _message(self) -> String: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index ddaea62833..e65d5bbdd2 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -343,7 +343,7 @@ fn _put(x: StringLiteral): @no_inline fn _put(x: DType): - _put(x.__str__()) + _put(str(x)) # ===----------------------------------------------------------------------=== # diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 6a004634fa..0c88df1afa 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -168,7 +168,7 @@ struct Path(Stringable, CollectionElement, PathLike, KeyElement): Returns: True if the paths are equal and False otherwise. """ - return self.__str__() == other.__str__() + return str(self) == str(other) fn __ne__(self, other: Self) -> Bool: """Returns True if the two paths are not equal. diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index e0db3531fc..3bebbd8503 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -38,7 +38,7 @@ fn test_list(inout python: Python) raises -> String: try: var b: PythonObject = Python.import_module("builtins") var my_list = PythonObject([1, 2.34, "False"]) - var py_string = my_list.__str__() + var py_string = str(my_list) return String(python.__str__(py_string)) except e: return str(e) @@ -48,7 +48,7 @@ fn test_tuple(inout python: Python) raises -> String: try: var b: PythonObject = Python.import_module("builtins") var my_tuple = PythonObject((1, 2.34, "False")) - var py_string = my_tuple.__str__() + var py_string = str(my_tuple) return String(python.__str__(py_string)) except e: return str(e) @@ -56,7 +56,7 @@ fn test_tuple(inout python: Python) raises -> String: fn test_call_ownership(inout python: Python) raises -> String: var obj: PythonObject = [1, "5"] - var py_string = obj.__str__() + var py_string = str(obj) var string = python.__str__(py_string) return String(string) @@ -64,7 +64,7 @@ fn test_call_ownership(inout python: Python) raises -> String: fn test_getitem_ownership(inout python: Python) raises -> String: try: var obj: PythonObject = [1, "5"] - var py_string = obj[1].__str__() + var py_string = str(obj[1]) var string = python.__str__(py_string) return String(string) except e: @@ -76,7 +76,7 @@ fn test_getattr_ownership(inout python: Python) raises -> String: Python.add_to_path(TEST_DIR) var my_module: PythonObject = Python.import_module("my_module") var obj = my_module.Foo(4) - var py_string = obj.bar.__str__() + var py_string = str(obj.bar) var string = python.__str__(py_string) return String(string) except e: diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index f22c2bb874..19ffd7ab98 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -21,7 +21,7 @@ fn test_string_to_python_to_mojo(inout python: Python) raises: var py_string_capitalized = py_string.capitalize() # CHECK: Mojo - var cap_mojo_string = py_string_capitalized.__str__() + var cap_mojo_string = str(py_string_capitalized) print(cap_mojo_string) From 8225d08c5b8d13cfe375e3bd99766ca06ae55e25 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 13:02:04 -0500 Subject: [PATCH 0198/2019] [External][Scripts] Add override for compiler version checking (#2195) (#38031) Add an override ability for doing the compiler version checking. This is opt-in based on the environment variable `MOJO_OVERRIDE_COMPILER_VERSION_CHECK`. By default, we still do the compiler version checking. Internally, this allows us to test to make sure the workflow upstream works for unreleased compilers that, by definition, would have a different compiler version than the one in the `COMPATIBLE_COMPILER_VERSION` file. Signed-off-by: Joe Loser mojo-orig-commit: 679da8a362dfb905c318b0a37b9c2f66ee9b2d8e Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: db3dac41e59d8d99ed3e5aa2a1eef0e4a8b21205 --- stdlib/scripts/build-stdlib.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/stdlib/scripts/build-stdlib.sh b/stdlib/scripts/build-stdlib.sh index aa4e086da6..8389f2d307 100755 --- a/stdlib/scripts/build-stdlib.sh +++ b/stdlib/scripts/build-stdlib.sh @@ -22,12 +22,14 @@ mkdir -p "${BUILD_DIR}" ACTUAL_COMPILER_VERSION=$(mojo --version | tr " " "\n" | sed -n 2p) EXPECTED_COMPILER_VERSION=$(<"${REPO_ROOT}"/stdlib/COMPATIBLE_COMPILER_VERSION) -if [ "${EXPECTED_COMPILER_VERSION}" != "${ACTUAL_COMPILER_VERSION}" ]; then - echo "Mismatch in compiler versions! Cannot build the standard library." - echo "Expected compiler version: ${EXPECTED_COMPILER_VERSION}" - echo "Current installed compiler version: ${ACTUAL_COMPILER_VERSION}" - echo "Please run modular update nightly/mojo to get the latest compiler." - exit 1 +if [ -z "${MOJO_OVERRIDE_COMPILER_VERSION_CHECK:-}" ]; then + if [ "${EXPECTED_COMPILER_VERSION}" != "${ACTUAL_COMPILER_VERSION}" ]; then + echo "Mismatch in compiler versions! Cannot build the standard library." + echo "Expected compiler version: ${EXPECTED_COMPILER_VERSION}" + echo "Current installed compiler version: ${ACTUAL_COMPILER_VERSION}" + echo "Please run modular update nightly/mojo to get the latest compiler." + exit 1 + fi fi STDLIB_PATH="${REPO_ROOT}/stdlib/src" From 0c4add1b337fd5a383b79a727dd65e27591f187e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 13:46:05 -0500 Subject: [PATCH 0199/2019] [External][stdlib] Handle whitespaces in `atol` (#2225) (#38042) `atol` function now handles whitespaces before and after the number to parse such that `int(String(" 10 "))` now gives `10` instead of raising an error. Signed-off-by: Artemio Gazra Reyna artemiogr97@gmail.com mojo-orig-commit: 7ddcd2dee744c14d198bd179e9c4f27e6e259557 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 981e80a21162af00d0f80d770f62139626c9ab00 --- stdlib/src/builtin/string.mojo | 34 ++++++++++++++++++++-------- stdlib/test/builtin/test_string.mojo | 10 ++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index f789d06a53..fda1690764 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -158,7 +158,7 @@ fn chr(c: Int) -> String: # TODO: this is hard coded for decimal base @always_inline -fn _atol(str: StringRef) raises -> Int: +fn _atol(str_ref: StringRef) raises -> Int: """Parses the given string as a base-10 integer and returns that value. For example, `atol("19")` returns `19`. If the given string cannot be parsed @@ -166,37 +166,53 @@ fn _atol(str: StringRef) raises -> Int: error. Args: - str: A string to be parsed as a base-10 integer. + str_ref: A string to be parsed as a base-10 integer. Returns: An integer value that represents the string, or otherwise raises. """ - if not str: + if not str_ref: raise Error("Empty String cannot be converted to integer.") var result = 0 var is_negative: Bool = False var start: Int = 0 - if str[0] == "-": - is_negative = True - start = 1 + var str_len = len(str_ref) + var buff = str_ref._as_ptr() + for pos in range(start, str_len): + if isspace(buff[pos]): + continue + + if str_ref[pos] == "-": + is_negative = True + start = pos + 1 + else: + start = pos + break alias ord_0 = ord("0") alias ord_9 = ord("9") - var buff = str._as_ptr() - var str_len = len(str) + var has_space_after_number = False for pos in range(start, str_len): var digit = int(buff[pos]) if ord_0 <= digit <= ord_9: result += digit - ord_0 + elif isspace(digit): + has_space_after_number = True + start = pos + 1 + break else: raise Error("String is not convertible to integer.") - if pos + 1 < str_len: + if pos + 1 < str_len and not isspace(buff[pos + 1]): var nextresult = result * 10 if nextresult < result: raise Error( "String expresses an integer too large to store in Int." ) result = nextresult + if has_space_after_number: + for pos in range(start, str_len): + if not isspace(buff[pos]): + raise Error("String is not convertible to integer.") if is_negative: result = -result return result diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index f2b8749662..d8294e4e20 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -216,7 +216,11 @@ fn test_string_indexing() raises: fn test_atol() raises: assert_equal(375, atol(String("375"))) assert_equal(1, atol(String("001"))) + assert_equal(5, atol(String(" 005"))) + assert_equal(13, atol(String(" 013 "))) assert_equal(-89, atol(String("-89"))) + assert_equal(-52, atol(String(" -52"))) + assert_equal(-69, atol(String(" -69 "))) # Negative cases try: @@ -225,6 +229,12 @@ fn test_atol() raises: except e: assert_equal(str(e), "String is not convertible to integer.") + try: + _ = atol(String(" 10 1")) + raise Error("Failed to raise when converting string to integer.") + except e: + assert_equal(str(e), "String is not convertible to integer.") + try: _ = atol(String("")) raise Error("Failed to raise when converting empty string to integer.") From ba0f830e764e307b4dcd8fead7d88c27db7bbb08 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 13:46:51 -0500 Subject: [PATCH 0200/2019] [External][stdlib] Allow `is None` and `is not None` with `OptionalReg` (#2163) (#38044) Follow-up on #2082. Do the same for `OptionalReg` that was done for `Optional`. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 959820d286086e8ad8b5686aa9c5f7a2ed92447c Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 942944bf6c30779881cb32bfb3b1982467642757 --- stdlib/src/collections/optional.mojo | 26 ++++++++++++++++++++++ stdlib/test/collections/test_optional.mojo | 18 +++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index cdce056c4c..a46bf8951a 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -312,6 +312,32 @@ struct OptionalReg[T: AnyRegType](Boolable): """ return __mlir_op.`kgen.variant.take`[index = Int(0).value](self._value) + fn __is__(self, other: NoneType) -> Bool: + """Return `True` if the Optional has no value. + + It allows you to use the following syntax: `if my_optional is None:` + + Args: + other: The value to compare to (None). + + Returns: + True if the Optional has no value and False otherwise. + """ + return not self.__bool__() + + fn __isnot__(self, other: NoneType) -> Bool: + """Return `True` if the Optional has a value. + + It allows you to use the following syntax: `if my_optional is not None:` + + Args: + other: The value to compare to (None). + + Returns: + True if the Optional has a value and False otherwise. + """ + return self.__bool__() + fn __bool__(self) -> Bool: """Return true if the optional has a value. diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index cea2560fac..cc4c28f6db 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -87,8 +87,26 @@ def test_optional_isnot(): assert_false(a is not None) +def test_optional_reg_is(): + a = OptionalReg(1) + assert_false(a is None) + + a = OptionalReg[Int](None) + assert_true(a is None) + + +def test_optional_reg_isnot(): + a = OptionalReg(1) + assert_true(a is not None) + + a = OptionalReg[Int](None) + assert_false(a is not None) + + def main(): test_basic() test_optional_reg_basic() test_optional_is() test_optional_isnot() + test_optional_reg_is() + test_optional_reg_isnot() From e6d2857dd82a75e9b517c2d07dd794f680bc01df Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 13:47:11 -0500 Subject: [PATCH 0201/2019] [External][stdlib] Fix typos in docstring for `FloatLiteral` (#2202) (#38045) Signed-off-by: Michael Wytock mojo-orig-commit: 25347b9dfb4848ac6aec8dd88eb378a449edd699 Co-authored-by: mwytock0812 MODULAR_ORIG_COMMIT_REV_ID: 2824b2523adda4bcc71906e04df22b5624846033 --- stdlib/src/builtin/float_literal.mojo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/src/builtin/float_literal.mojo b/stdlib/src/builtin/float_literal.mojo index 5b289705b9..4ee2b66faf 100644 --- a/stdlib/src/builtin/float_literal.mojo +++ b/stdlib/src/builtin/float_literal.mojo @@ -94,7 +94,7 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): fractional component, then the value is truncated towards zero. Eg. `(4.5).__int_literal__()` returns `4`, and `(-3.7).__int_literal__()` - returns `3`. + returns `-3`. Returns: The value as an integer. @@ -108,7 +108,7 @@ struct FloatLiteral(Intable, Stringable, Boolable, EqualityComparable): """Converts the FloatLiteral value to an Int. If there is a fractional component, then the value is truncated towards zero. - Eg. `(4.5).__int__()` returns `4`, and `(-3.7).__int__()` returns `3`. + Eg. `(4.5).__int__()` returns `4`, and `(-3.7).__int__()` returns `-3`. Returns: The value as an integer. From 744c2660af3a2c66542f329706a7463584f4fd8e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 14:50:48 -0500 Subject: [PATCH 0202/2019] [External][stdlib] Add the `gather` and `scatter` methods to `DTypePointer` (#2268) (#38068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces the `gather` and `scatter` methods to the `DTypePointer` struct, as outlined in feature request #1626. **Key Changes:** - Implemented the `gather` method for `DTypePointer`. - Implemented the `scatter` method for `DTypePointer`. - Added corresponding unit tests to validate the functionality of these methods. - Updated the docstring in `sys.intrinsics.scatter` to align with the LLVM masked scatter intrinsic documentation [[link](https://llvm.org/docs/LangRef.html#llvm-masked-scatter-intrinsics)]. These changes better reflect the behavior of the scatter intrinsic with overlapping addresses. **Naming Consideration:** - While staying consistent with the corresponding LLVM intrinsics is important, it’s also crucial to focus on the primary audience of Mojo. If Mojo developers are primarily expected to have a comfort with Python, using `default` instead of `passthru` might reduce the learning curve and avoid confusion. For developers familiar with Python, `default` conveys the idea that an argument serves as a fallback or standard value when some condition fails. This naming choice aims to enhance clarity and accessibility, making the API more intuitive for our primary user base. Aware that this feature had been assigned to a maintainer since January, I reached out through comments on the original feature request to ask about any progress and to express my willingness to contribute. In the absence of updates or responses, I proceeded with this implementation. _I am fully prepared to adjust or withdraw my contribution should there be existing work that overlaps with what is presented here_. Signed-off-by: Leandro Augusto Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> mojo-orig-commit: 1a14a1e6f8ad9273db2c2bf67fcaef3011011195 Co-authored-by: Leandro Lacerda Campos <15185896+leandrolcampos@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 6694a757d111c750899336b0830bc6f276df2a8f --- stdlib/src/memory/unsafe.mojo | 164 +++++++++++++++++++++++++++- stdlib/src/sys/intrinsics.mojo | 4 +- stdlib/test/memory/test_memory.mojo | 124 ++++++++++++++++++++- 3 files changed, 288 insertions(+), 4 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 2cf9e7297f..da84d5e6e0 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -29,7 +29,7 @@ from sys.info import ( ) from sys.intrinsics import PrefetchOptions, _mlirtype_is_eq from sys.intrinsics import prefetch as _prefetch -from sys.intrinsics import strided_load, strided_store +from sys.intrinsics import gather, scatter, strided_load, strided_store from .memory import _free, _malloc @@ -1033,6 +1033,168 @@ struct DTypePointer[ # aligned, 64B for avx512, 32B for avx2, and 16B for avx. self.address.bitcast[SIMD[type, width]]().nt_store(val) + # ===------------------------------------------------------------------=== # + # Gather/Scatter + # ===------------------------------------------------------------------=== # + + @always_inline("nodebug") + fn gather[ + *, width: Int = 1, alignment: Int = Self._default_alignment + ](self, offset: SIMD[_, width]) -> SIMD[type, width]: + """Gathers a SIMD vector from offsets of the current pointer. + + This method loads from memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to gather from. + + Returns: + The SIMD vector containing the gathered values. + """ + var mask = SIMD[DType.bool, width](True) + var default = SIMD[type, width]() + return self.gather[width=width, alignment=alignment]( + offset, mask, default + ) + + @always_inline("nodebug") + fn gather[ + *, width: Int = 1, alignment: Int = Self._default_alignment + ]( + self, + offset: SIMD[_, width], + mask: SIMD[DType.bool, width], + default: SIMD[type, width], + ) -> SIMD[type, width]: + """Gathers a SIMD vector from offsets of the current pointer. + + This method loads from memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector, + or takes from the `default` SIMD vector, depending on the values of + the `mask` SIMD vector. + + If a mask element is `True`, the respective result element is given + by the current pointer and the `offset` SIMD vector; otherwise, the + result element is taken from the `default` SIMD vector. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to gather from. + mask: The SIMD vector of boolean values, indicating for each + element whether to load from memory or to take from the + `default` SIMD vector. + default: The SIMD vector providing default values to be taken + where the `mask` SIMD vector is `False`. + + Returns: + The SIMD vector containing the gathered values. + """ + constrained[ + offset.type.is_integral(), + "offset type must be an integral type", + ]() + constrained[ + _is_power_of_2(alignment), + "alignment must be a power of two integer value", + ]() + + var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) + return gather(base.cast[DType.address](), mask, default, alignment) + + @always_inline("nodebug") + fn scatter[ + *, width: Int = 1, alignment: Int = Self._default_alignment + ](self, offset: SIMD[_, width], val: SIMD[type, width]): + """Scatters a SIMD vector into offsets of the current pointer. + + This method stores at memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector. + + If the same offset is targeted multiple times, the values are stored + in the order they appear in the `val` SIMD vector, from the first to + the last element. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to scatter into. + val: The SIMD vector containing the values to be scattered. + """ + var mask = SIMD[DType.bool, width](True) + self.scatter[width=width, alignment=alignment](offset, val, mask) + + @always_inline("nodebug") + fn scatter[ + *, width: Int = 1, alignment: Int = Self._default_alignment + ]( + self, + offset: SIMD[_, width], + val: SIMD[type, width], + mask: SIMD[DType.bool, width], + ): + """Scatters a SIMD vector into offsets of the current pointer. + + This method stores at memory addresses calculated by appropriately + shifting the current pointer according to the `offset` SIMD vector, + depending on the values of the `mask` SIMD vector. + + If a mask element is `True`, the respective element in the `val` SIMD + vector is stored at the memory address defined by the current pointer + and the `offset` SIMD vector; otherwise, no action is taken for that + element in `val`. + + If the same offset is targeted multiple times, the values are stored + in the order they appear in the `val` SIMD vector, from the first to + the last element. + + Constraints: + The offset type must be an integral type. + The alignment must be a power of two integer value. + + Parameters: + width: The SIMD width. + alignment: The minimal alignment of the address. + + Args: + offset: The SIMD vector of offsets to scatter into. + val: The SIMD vector containing the values to be scattered. + mask: The SIMD vector of boolean values, indicating for each + element whether to store at memory or not. + """ + constrained[ + offset.type.is_integral(), + "offset type must be an integral type", + ]() + constrained[ + _is_power_of_2(alignment), + "alignment must be a power of two integer value", + ]() + + var base = offset.cast[DType.index]().fma(sizeof[type](), int(self)) + scatter(val, base.cast[DType.address](), mask, alignment) + @always_inline("nodebug") fn __int__(self) -> Int: """Returns the pointer address as an integer. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 2fb633b115..ce765964a2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -901,8 +901,8 @@ fn scatter[ `mask` and the `value` operand must have the same number of vector elements. - The behavior of the _scatter is undefined if the op stores into - the same memory location more than once. + Scatter with overlapping addresses is guaranteed to be ordered from + least-significant to most-significant element. In general, for some vector %value, vector of pointers %base, and mask %mask instructions of the form: diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index b4451f588c..6f82ceb633 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -17,7 +17,12 @@ from sys.info import sizeof from memory import memcmp, memcpy, memset_zero from memory.unsafe import DTypePointer, Pointer from utils._numerics import nan -from testing import assert_equal, assert_not_equal, assert_true +from testing import ( + assert_almost_equal, + assert_equal, + assert_not_equal, + assert_true, +) from utils.index import Index @@ -295,6 +300,120 @@ def test_pointer_refitem_pair(): ptr.free() +def test_dtypepointer_gather(): + var ptr = DTypePointer[DType.float32].alloc(4) + ptr.store(0, SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0)) + + @parameter + def _test_gather[ + width: Int + ](offset: SIMD[_, width], desired: SIMD[ptr.type, width]): + var actual = ptr.gather(offset) + assert_almost_equal( + actual, desired, msg="_test_gather", atol=0.0, rtol=0.0 + ) + + @parameter + def _test_masked_gather[ + width: Int + ]( + offset: SIMD[_, width], + mask: SIMD[DType.bool, width], + default: SIMD[ptr.type, width], + desired: SIMD[ptr.type, width], + ): + var actual = ptr.gather(offset, mask, default) + assert_almost_equal( + actual, desired, msg="_test_masked_gather", atol=0.0, rtol=0.0 + ) + + var offset = SIMD[DType.int64, 8](3, 0, 2, 1, 2, 0, 3, 1) + var desired = SIMD[ptr.type, 8](3.0, 0.0, 2.0, 1.0, 2.0, 0.0, 3.0, 1.0) + + _test_gather[1](UInt16(2), 2.0) + _test_gather(offset.cast[DType.uint32]().slice[2](), desired.slice[2]()) + _test_gather(offset.cast[DType.uint64]().slice[4](), desired.slice[4]()) + + var mask = (offset >= 0) & (offset < 3) + var default = SIMD[ptr.type, 8](-1.0) + desired = SIMD[ptr.type, 8](-1.0, 0.0, 2.0, 1.0, 2.0, 0.0, -1.0, 1.0) + + _test_masked_gather[1](Int16(2), False, -1.0, -1.0) + _test_masked_gather[1](Int32(2), True, -1.0, 2.0) + _test_masked_gather(offset, mask, default, desired) + + ptr.free() + + +def test_dtypepointer_scatter(): + var ptr = DTypePointer[DType.float32].alloc(4) + ptr.store(0, SIMD[ptr.type, 4](0.0)) + + @parameter + def _test_scatter[ + width: Int + ]( + offset: SIMD[_, width], + val: SIMD[ptr.type, width], + desired: SIMD[ptr.type, 4], + ): + ptr.scatter(offset, val) + var actual = ptr.load[width=4](0) + assert_almost_equal( + actual, desired, msg="_test_scatter", atol=0.0, rtol=0.0 + ) + + @parameter + def _test_masked_scatter[ + width: Int + ]( + offset: SIMD[_, width], + val: SIMD[ptr.type, width], + mask: SIMD[DType.bool, width], + desired: SIMD[ptr.type, 4], + ): + ptr.scatter(offset, val, mask) + var actual = ptr.load[width=4](0) + assert_almost_equal( + actual, desired, msg="_test_masked_scatter", atol=0.0, rtol=0.0 + ) + + _test_scatter[1](UInt16(2), 2.0, SIMD[ptr.type, 4](0.0, 0.0, 2.0, 0.0)) + _test_scatter( # Test with repeated offsets + SIMD[DType.uint32, 4](1, 1, 1, 1), + SIMD[ptr.type, 4](-1.0, 2.0, -2.0, 1.0), + SIMD[ptr.type, 4](0.0, 1.0, 2.0, 0.0), + ) + _test_scatter( + SIMD[DType.uint64, 4](3, 2, 1, 0), + SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0), + SIMD[ptr.type, 4](3.0, 2.0, 1.0, 0.0), + ) + + ptr.store(0, SIMD[ptr.type, 4](0.0)) + + _test_masked_scatter[1]( + Int16(2), 2.0, False, SIMD[ptr.type, 4](0.0, 0.0, 0.0, 0.0) + ) + _test_masked_scatter[1]( + Int32(2), 2.0, True, SIMD[ptr.type, 4](0.0, 0.0, 2.0, 0.0) + ) + _test_masked_scatter( # Test with repeated offsets + SIMD[DType.int64, 4](1, 1, 1, 1), + SIMD[ptr.type, 4](-1.0, 2.0, -2.0, 1.0), + SIMD[DType.bool, 4](True, True, True, False), + SIMD[ptr.type, 4](0.0, -2.0, 2.0, 0.0), + ) + _test_masked_scatter( + SIMD[DType.index, 4](3, 2, 1, 0), + SIMD[ptr.type, 4](0.0, 1.0, 2.0, 3.0), + SIMD[DType.bool, 4](True, False, True, True), + SIMD[ptr.type, 4](3.0, 2.0, 2.0, 0.0), + ) + + ptr.free() + + def main(): test_memcpy() test_memcpy_dtype() @@ -307,3 +426,6 @@ def main(): test_pointer_refitem_string() test_pointer_refitem_pair() test_pointer_string() + + test_dtypepointer_gather() + test_dtypepointer_scatter() From 57a09cca34c311ec190300de43ea6d0e5f71b658 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 15:55:40 -0500 Subject: [PATCH 0203/2019] [External][tools] Add a `pre-commit` for `mojo format` and license check (#2193) (#38054) We currently rely on contributors applying themselves the `mojo format -l 80` and thinking themselves about the licence check. This PR adds a pre-commit, which, once installed, will do the mojo format and license check automatically before each commit. This is optional for contributors but useful, since, if later we add more steps in the pre-commit, they will automatically run (both on the contributors' laptops and in the CI). This should save some time for the maintainers as less mistakes will be made and the CI will check more stuff. Here, the CI will fail if the license is not present on every file. I also simplified the contributing guide, notably removed the section about whitespaces, and line length which is already enforced by mojo format. This also means that I had to fix all files missing the license (3) and all Mojo files which had an incorrect format (2) Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 68149196960f4fd8b553862563fcedd3edc40f45 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 30d8416ac842304cf425afcd31fd7a473e661f41 --- stdlib/docs/development.md | 21 +++++++++--- stdlib/docs/style-guide.md | 19 +++++++---- stdlib/scripts/check-licenses.mojo | 46 ++++++++++++++++++++++++++ stdlib/test/test_utils/__init__.mojo | 9 ++++- stdlib/test/test_utils/test_utils.mojo | 9 ++++- stdlib/test/test_utils/types.mojo | 9 ++++- 6 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 stdlib/scripts/check-licenses.mojo diff --git a/stdlib/docs/development.md b/stdlib/docs/development.md index 362da08323..19263abdb6 100644 --- a/stdlib/docs/development.md +++ b/stdlib/docs/development.md @@ -112,16 +112,26 @@ Otherwise, CI will fail in its lint and formatting checks. The `mojo` compiler provides a `format` command. So, you can format your changes like so: ```bash -mojo format ... +mojo format ./ ``` -You can also do this before submitting a pull request by running it on the -relevant files changed compared to the remote: +It is advised, to avoid forgetting, to set-up `pre-commit`, which will format +your changes automatically at each commit, and will also ensure that you +always have the latest linting tools applied. + +To do so, install pre-commit: ```bash -git diff origin/main --name-only -- '*.mojo' | xargs mojo format +pip install pre-commit +pre-commit install ``` +and that's it! + +If you need to manually apply the `pre-commit`, for example, if you +made a commit with the github UI, you can do `pre-commit run --all-files`, +and it will apply the formatting to all Mojo files. + You can also consider setting up your editor to automatically format Mojo files upon saving. @@ -327,7 +337,8 @@ Total Discovered Tests: 1 Success! Now we have a test for our new function. -The last step is to [run mojo format](#formatting-changes) on all the files. +The last step is to [run `mojo format`](#formatting-changes) on all the files. +This can be skipped if `pre-commit` is installed. ### Raising a PR diff --git a/stdlib/docs/style-guide.md b/stdlib/docs/style-guide.md index bf7b167727..79dd099085 100644 --- a/stdlib/docs/style-guide.md +++ b/stdlib/docs/style-guide.md @@ -48,17 +48,22 @@ All done! ✨ 🍰 ✨ Unless otherwise noted, Mojo standard library code should follow the formatting produced by `mojo format`. -#### Whitespace +It is advised, to avoid forgetting, to set-up `pre-commit`, which will format +your changes automatically at each commit, and will also ensure that you +always have the latest linting tools applied. -- Use 4-space indentation. -- Do NOT use Tab characters. -- Use vertical whitespace only as needed to organize code into logical sections. +To do so, install pre-commit: -*We encourage updating your editor settings to be consistent with the above.* +```bash +pip install pre-commit +pre-commit install +``` -#### Column limit +and that's it! -Mojo code has a column limit (line length) of 80 characters. +#### Whitespace + +- Use vertical whitespace only as needed to organize code into logical sections. #### File license header diff --git a/stdlib/scripts/check-licenses.mojo b/stdlib/scripts/check-licenses.mojo new file mode 100644 index 0000000000..08664df7b3 --- /dev/null +++ b/stdlib/scripts/check-licenses.mojo @@ -0,0 +1,46 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # + +import sys +from pathlib import Path + +# We can't check much more than this at the moment, because the license year +# changes and the language is not mature enough to do regex yet. +alias LICENSE = String( + """ +# ===----------------------------------------------------------------------=== # +# Copyright (c) +""" +).strip() + + +def main(): + target_paths = sys.argv() + if len(target_paths) < 2: + raise Error("A file path must be given as a command line argument.") + + files_without_license = List[Path]() + for i in range(len(target_paths)): + if i == 0: + # this is the current file + continue + file_path = Path(target_paths[i]) + if not file_path.read_text().startswith(LICENSE): + files_without_license.append(file_path) + + if len(files_without_license) > 0: + print("The following files have missing licences 💥 💔 💥") + for file in files_without_license: + print(file[]) + print("Please add the license to each file before commiting.") + sys.exit(1) diff --git a/stdlib/test/test_utils/__init__.mojo b/stdlib/test/test_utils/__init__.mojo index 976e8db514..ca2d56a1d5 100644 --- a/stdlib/test/test_utils/__init__.mojo +++ b/stdlib/test/test_utils/__init__.mojo @@ -1,7 +1,14 @@ # ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. # -# This file is Modular Inc proprietary. +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt # +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # ===----------------------------------------------------------------------=== # from .test_utils import libm_call diff --git a/stdlib/test/test_utils/test_utils.mojo b/stdlib/test/test_utils/test_utils.mojo index 888867f265..807028ea76 100644 --- a/stdlib/test/test_utils/test_utils.mojo +++ b/stdlib/test/test_utils/test_utils.mojo @@ -1,7 +1,14 @@ # ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. # -# This file is Modular Inc proprietary. +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt # +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # ===----------------------------------------------------------------------=== # from sys import external_call diff --git a/stdlib/test/test_utils/types.mojo b/stdlib/test/test_utils/types.mojo index 07117974d4..5b241de79c 100644 --- a/stdlib/test/test_utils/types.mojo +++ b/stdlib/test/test_utils/types.mojo @@ -1,7 +1,14 @@ # ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. # -# This file is Modular Inc proprietary. +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt # +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # ===----------------------------------------------------------------------=== # From bcad3af8564d1bdb13ce8f870334602c8be40c0b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 16:40:40 -0500 Subject: [PATCH 0204/2019] [External][mojo-stdlib] Fix `len` for unary `range` with negative `end` value (#2204) (#38070) Signed-off-by: Yiwu Chen <210at85@gmail.com> mojo-orig-commit: 8582f82a610775aa26cc4f23f80ea8b04c36935b Co-authored-by: soraros MODULAR_ORIG_COMMIT_REV_ID: 7f09b955802a58806b2e3971c9a5e7d324cba255 --- stdlib/src/builtin/range.mojo | 9 ++++++-- stdlib/test/builtin/test_range.mojo | 32 ++++++++++------------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 1d49d688eb..4cb1e065d8 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -48,6 +48,11 @@ fn _abs(x: Int) -> Int: return x if x > 0 else -x +@always_inline +fn _max(a: Int, b: Int) -> Int: + return a if a > b else b + + # ===----------------------------------------------------------------------=== # # Range # ===----------------------------------------------------------------------=== # @@ -60,8 +65,8 @@ struct _ZeroStartingRange(Sized): @always_inline("nodebug") fn __init__(inout self, end: Int): - self.curr = end - self.end = end + self.curr = _max(0, end) + self.end = self.curr @always_inline("nodebug") fn __iter__(self) -> Self: diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 723f2ade1f..d6789ea209 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -12,28 +12,18 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s | FileCheck %s +from testing import assert_equal -# CHECK-LABEL: test_range_len -fn test_range_len(): - print("== test_range_len") - # CHECK: 10 - print(range(10).__len__()) - - # CHECK: 10 - print(range(0, 10).__len__()) - - # CHECK: 5 - print(range(5, 10).__len__()) - - # CHECK: 10 - print(range(10, 0, -1).__len__()) - - # CHECK: 5 - print(range(0, 10, 2).__len__()) - - # CHECK: 3 - print(range(38, -13, -23).__len__()) +def test_range_len(): + assert_equal(range(0).__len__(), 0) + assert_equal(range(-1).__len__(), 0) + assert_equal(range(10).__len__(), 10) + assert_equal(range(0, 10).__len__(), 10) + assert_equal(range(5, 10).__len__(), 5) + assert_equal(range(10, 0, -1).__len__(), 10) + assert_equal(range(0, 10, 2).__len__(), 5) + assert_equal(range(38, -13, -23).__len__(), 3) # CHECK-LABEL: test_range_getitem @@ -56,6 +46,6 @@ fn test_range_getitem(): print(range(0, 10, 2)[4]) -fn main(): +def main(): test_range_len() test_range_getitem() From a55d57995b69c406a6ce7d41cab9f2ccf0fa8320 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 16:42:17 -0500 Subject: [PATCH 0205/2019] [External][stdlib] Remove FileCheck in `test_has_intel_amx.mojo` (#2288) (#38067) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 4c40ceb636975b3b68fb15a01ebb80a13a23e056 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 104429a5ee627eeffcebf012e19b11e5ba49fef7 --- stdlib/test/sys/test_has_intel_amx.mojo | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/stdlib/test/sys/test_has_intel_amx.mojo b/stdlib/test/sys/test_has_intel_amx.mojo index 337a6ed9a1..d5cf972d3c 100644 --- a/stdlib/test/sys/test_has_intel_amx.mojo +++ b/stdlib/test/sys/test_has_intel_amx.mojo @@ -16,23 +16,18 @@ # ===----------------------------------------------------------------------=== # # REQUIRES: linux # REQUIRES: amx_tile -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo-no-debug -debug-level full %s -from sys.info import has_intel_amx, os_is_linux - +from sys import has_intel_amx, os_is_linux +from testing import assert_false, assert_true from LinAlg.intel_amx import init_intel_amx -# CHECK-LABEL: test_has_intel_amx fn test_has_intel_amx(): - print("== test_intel_amx_amx") - # CHECK: True - print(os_is_linux()) - # CHECK: True - print(has_intel_amx()) - # CHECK: True - print(init_intel_amx()) + assert_true(os_is_linux()) + assert_true(has_intel_amx()) + assert_true(init_intel_amx()) fn main(): From 60b23c63fe865f0c87d26cfeb752d009e4f8c5d7 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 16:42:50 -0500 Subject: [PATCH 0206/2019] [External][stdlib] Remove FileCheck in `test_build_info_debug.mojo` (#2291) (#38065) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 746c4ae78a6f973c04b50853980e57faf7bb4133 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: e4268b784bf9a72c1604042387c0bcb7ae13a29f --- stdlib/test/sys/test_build_info_debug.mojo | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/stdlib/test/sys/test_build_info_debug.mojo b/stdlib/test/sys/test_build_info_debug.mojo index 3f4b0a3749..5ce1e50b73 100644 --- a/stdlib/test/sys/test_build_info_debug.mojo +++ b/stdlib/test/sys/test_build_info_debug.mojo @@ -14,17 +14,12 @@ # RUN: %mojo %s | FileCheck %s from sys._build import is_debug_build, is_release_build +from testing import assert_true, assert_false -# CHECK-OK-LABEL: test_is_debug fn test_is_debug(): - print("== test_is_debug") - - # CHECK: True - print(is_debug_build()) - - # CHECK: False - print(is_release_build()) + assert_true(is_debug_build()) + assert_false(is_release_build()) fn main(): From 43e06a7f7ad92074c3544bfda6a04e9ce8a37963 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 16:43:57 -0500 Subject: [PATCH 0207/2019] [External][stdlib] Remove FileCheck from `test_windows_target.mojo` (#2300) (#38061) Related to https://github.com/modularml/mojo/issues/2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: dd51a487c1436e8bf1ddf81eb59e8905fedcca08 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: d491ab08da8b6862afb0729a92d970a1da142a8e --- stdlib/test/sys/test_windows_target.mojo | 44 ++++++++---------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index 52b4031a80..5d49181679 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: windows -# RUN: mojo.exe %s | FileCheck %s +# RUN: mojo.exe %s from os._windows import ( @@ -26,50 +26,34 @@ from os._windows import ( from sys import external_call from sys.info import os_is_linux, os_is_macos, os_is_windows -from memory.unsafe import Pointer +from memory import Pointer +from testing import assert_false, assert_true, assert_equal -# CHECK-LABEL: test_os_query -fn test_os_query(): - print("== test_os_query\n") +def test_os_query(): + assert_false(os_is_macos()) + assert_false(os_is_linux()) + assert_true(os_is_windows()) - # CHECK: False - print(os_is_macos()) - - # CHECK: False - print(os_is_linux()) - - # CHECK: True - print(os_is_windows()) - - -# CHECK-LABEL: test_last_error -fn test_last_error(): - print("== test_last_error\n") +def test_last_error(): reset_last_error() - # CHECK: 0 - print(get_last_error_code()) + assert_equal(get_last_error_code(), 0) - # CHECK: True - print(last_operation_succeeded()) + assert_true(last_operation_succeeded()) # GetProcessId takes the handle to a process and returns its id. If the # handle is null this will fail and returns an invalid handle error (error # code 6). var succeeded = external_call["GetProcessId", Int](Pointer[Int].get_null()) - # CHECK: 0 - print(succeeded) - - # CHECK: False - print(last_operation_succeeded()) + assert_equal(succeeded, 0) - # CHECK: 6 - print(get_last_error_code()) + assert_false(last_operation_succeeded()) + assert_equal(get_last_error_code(), 6) -fn main(): +def main(): test_os_query() test_last_error() From df577f587061379a4aac57d6182718c456fd31f5 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 17:02:08 -0500 Subject: [PATCH 0208/2019] [External][mojo-stdlib] feature: Resize List smaller with single arg (#2140) (#38038) Add a `resize` method to `List` which allows you to resize the list without needing to also pass a `T`. Signed-off-by: Michael Kowalski <1331470+mikowals@users.noreply.github.com> mojo-orig-commit: 111c9019360f47e8f40411a0091c030d2c75fda9 Co-authored-by: Michael Kowalski <1331470+mikowals@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 5f2ec0f7c5eaa86a64ddf27e2c118241ad293560 --- stdlib/src/collections/list.mojo | 31 +++++++++++++++++++++++--- stdlib/test/collections/test_list.mojo | 11 +++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 515866e6da..c5cd7a6d8b 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -284,12 +284,37 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): new_size: The new size. value: The value to use to populate new elements. """ - self.reserve(new_size) + if new_size <= self.size: + self.resize(new_size) + else: + self.reserve(new_size) + for i in range(new_size, self.size): + destroy_pointee(self.data + i) + for i in range(self.size, new_size): + initialize_pointee(self.data + i, value) + self.size = new_size + + @always_inline + fn resize(inout self, new_size: Int): + """Resizes the list to the given new size. + + With no new value provided, the new size must be smaller than or equal + to the current one. Elements at the end are discarded. + + Args: + new_size: The new size. + """ + debug_assert( + new_size <= self.size, + ( + "New size must be smaller than or equal to current size when no" + " new value is provided." + ), + ) for i in range(new_size, self.size): destroy_pointee(self.data + i) - for i in range(self.size, new_size): - initialize_pointee(self.data + i, value) self.size = new_size + self.reserve(new_size) fn reverse(inout self): """Reverses the elements of the list.""" diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index c2957a1321..9d36c3ee7f 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -111,6 +111,16 @@ def test_list_variadic_constructor(): assert_equal(8, l[3]) +def test_list_resize(): + var l = List[Int](1) + assert_equal(1, len(l)) + l.resize(2, 0) + assert_equal(2, len(l)) + assert_equal(l[1], 0) + l.resize(0) + assert_equal(len(l), 0) + + def test_list_reverse(): # # Test reversing the list [] @@ -472,6 +482,7 @@ def main(): test_list_clear() test_list_pop() test_list_variadic_constructor() + test_list_resize() test_list_reverse() test_list_reverse_move_count() test_list_extend() From 3c6c700774ffbaf50c91e400a71c7c641be0bf01 Mon Sep 17 00:00:00 2001 From: abdul dakkak Date: Thu, 18 Apr 2024 15:15:57 -0700 Subject: [PATCH 0209/2019] [Stdlib] Fix the SIMD.deinterleave function for size 2 (#38104) This fixes the SIMD deinterleave functions for size 2 and adds a bunch of test cases. It also changes the API to return a Tuple instead of StaticTuple. Closes #38093 MODULAR_ORIG_COMMIT_REV_ID: 0414fee7d17ba09f12b0cb091bd6f603ef01f53f --- stdlib/src/builtin/simd.mojo | 13 +++++++++++-- stdlib/test/builtin/test_simd.mojo | 10 +++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 9061a2d349..f193f86b98 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1680,7 +1680,9 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( ](self, other) @always_inline("nodebug") - fn deinterleave(self) -> StaticTuple[SIMD[type, size // 2], 2]: + fn deinterleave( + self, + ) -> Tuple[SIMD[type, size // 2], SIMD[type, size // 2]]: """Constructs two vectors by deinterleaving the even and odd lanes of the vector. @@ -1694,12 +1696,19 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[size > 1, "the vector size must be greater than 1."]() + @parameter + if size == 2: + return ( + rebind[SIMD[type, size // 2]](self[0]), + rebind[SIMD[type, size // 2]](self[1]), + ) + var res = llvm_intrinsic[ "llvm.experimental.vector.deinterleave2", _RegisterPackType[SIMD[type, size // 2], SIMD[type, size // 2]], has_side_effect=False, ](self) - return StaticTuple[SIMD[type, size // 2], 2]( + return ( res.get[0, SIMD[type, size // 2]](), res.get[1, SIMD[type, size // 2]](), ) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 8f1c2b538a..ebd4250a09 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -321,9 +321,13 @@ def test_interleave(): def test_deinterleave(): - var ts = SIMD[DType.index, 4](0, 1, 2, 3).deinterleave() - assert_equal(ts[0], SIMD[DType.index, 2](0, 2)) - assert_equal(ts[1], SIMD[DType.index, 2](1, 3)) + var tup2 = SIMD[DType.float32, 2](1, 2).deinterleave() + assert_equal(tup2.get[0](), Float32(1)) + assert_equal(tup2.get[1](), Float32(2)) + + var tup4 = SIMD[DType.index, 4](0, 1, 2, 3).deinterleave() + assert_equal(tup4.get[0](), SIMD[DType.index, 2](0, 2)) + assert_equal(tup4.get[1](), SIMD[DType.index, 2](1, 3)) def test_address(): From 9ee02732b127441fdd8fcf657b5cf68a15df7229 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 18:01:31 -0500 Subject: [PATCH 0210/2019] [External][stdlib] Remove FileCheck in `test_intrinsics.mojo` (#2285) (#38064) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: da4f739839eca72e13fa3b118493317e423620d0 MODULAR_ORIG_COMMIT_REV_ID: 66e3e16782201af17195d602d10340ae34d149ba --- stdlib/test/sys/test_intrinsics.mojo | 32 +++++++++++----------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index 2743086802..d9d75d04a7 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -21,12 +21,10 @@ from sys.intrinsics import ( ) from memory.unsafe import DTypePointer +from testing import assert_equal -# CHECK-LABEL: test_strided_load -fn test_strided_load(): - print("== test_strided_load") - +def test_strided_load(): alias size = 16 var vector = DTypePointer[DType.float32]().alloc(size) @@ -39,28 +37,24 @@ fn test_strided_load(): vector.free() -# CHECK-LABEL: test_strided_store -fn test_strided_store(): - print("== test_strided_store") - +fn test_strided_store() raises: alias size = 8 var vector = DTypePointer[DType.float32]().alloc(size) memset_zero(vector, size) strided_store(SIMD[DType.float32, 4](99, 12, 23, 56), vector, 2) - # CHECK: 99.0 - # CHECK: 0.0 - # CHECK: 12.0 - # CHECK: 0.0 - # CHECK: 23.0 - # CHECK: 0.0 - # CHECK: 56.0 - # CHECK: 0.0 - for i in range(size): - print(vector[i]) + assert_equal(vector[0], 99.0) + assert_equal(vector[1], 0.0) + assert_equal(vector[2], 12.0) + assert_equal(vector[3], 0.0) + assert_equal(vector[4], 23.0) + assert_equal(vector[5], 0.0) + assert_equal(vector[6], 56.0) + assert_equal(vector[7], 0.0) + vector.free() -fn main(): +def main(): test_strided_load() test_strided_store() From 99a2363d4541aada191ab66da4afef022a0368d4 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 18:05:00 -0500 Subject: [PATCH 0211/2019] [External][stdlib] Remove FileCheck in `test_bitcast.mojo` (#2296) (#38066) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 7d3bc933a275e762fe6d7c71fd0f4306317ad7ca MODULAR_ORIG_COMMIT_REV_ID: 9f0df24a5b93e625e84805896c0952401829a72d --- stdlib/test/memory/test_bitcast.mojo | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/stdlib/test/memory/test_bitcast.mojo b/stdlib/test/memory/test_bitcast.mojo index ebec35fdad..5c6e435f52 100644 --- a/stdlib/test/memory/test_bitcast.mojo +++ b/stdlib/test/memory/test_bitcast.mojo @@ -10,21 +10,24 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s -from memory.unsafe import bitcast +# RUN: %mojo-no-debug %s +from memory import bitcast +from testing import assert_equal -# CHECK-LABEL: test_bitcast -fn test_bitcast(): - print("== test_bitcast") - # CHECK: [1, 0, 2, 0, 3, 0, 4, 0] - print(bitcast[DType.int8, 8](SIMD[DType.int16, 4](1, 2, 3, 4))) +def test_bitcast(): + assert_equal( + bitcast[DType.int8, 8](SIMD[DType.int16, 4](1, 2, 3, 4)), + SIMD[DType.int8, 8](1, 0, 2, 0, 3, 0, 4, 0), + ) - # CHECK: 1442775295 - print(bitcast[DType.int32, 1](SIMD[DType.int8, 4](0xFF, 0x00, 0xFF, 0x55))) + assert_equal( + bitcast[DType.int32, 1](SIMD[DType.int8, 4](0xFF, 0x00, 0xFF, 0x55)), + Int32(1442775295), + ) -fn main(): +def main(): test_bitcast() From 924655e4f0594fca93b15ca2784b25c0544d12a9 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 18:26:00 -0500 Subject: [PATCH 0212/2019] [External][stdlib] Remove FileCheck in `test_paramenv.mojo` (#2302) (#38060) Related to https://github.com/modularml/mojo/issues/2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: c18c1c1ae01fe0773f985d945d103b2b552635a4 MODULAR_ORIG_COMMIT_REV_ID: 37775cfec39beac0c19d81db727d75b725c267f3 --- stdlib/test/sys/test_paramenv.mojo | 53 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/stdlib/test/sys/test_paramenv.mojo b/stdlib/test/sys/test_paramenv.mojo index 038df17d60..37ae87cc90 100644 --- a/stdlib/test/sys/test_paramenv.mojo +++ b/stdlib/test/sys/test_paramenv.mojo @@ -10,27 +10,32 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D bar=99 -D baz=hello %s | FileCheck %s -# RUN: %mojo -D bar=99 -D baz=hello -D foo=11 %s | FileCheck %s --check-prefix=FOO - -from sys.param_env import env_get_int, env_get_string, is_defined - - -fn main(): - # CHECK-LABEL: === test_env - print("=== test_env") - - # CHECK: is_defined(foo) False - print("is_defined(foo)", is_defined["foo"]()) - # CHECK: is_defined(bar) True - print("is_defined(bar)", is_defined["bar"]()) - # CHECK: env_get_int(bar) 99 - print("env_get_int(bar)", env_get_int["bar"]()) - # CHECK: env_get_string(baz) hello - print("env_get_string(baz)", env_get_string["baz"]()) - - # CHECK: env_get_int_or(foo, 42) 42 - # FOO: env_get_int_or(foo, 42) 11 - print("env_get_int_or(foo, 42)", env_get_int["foo", 42]()) - # CHECK: env_get_int_or(bar, 42) 99 - print("env_get_int_or(bar, 42)", env_get_int["bar", 42]()) +# RUN: %mojo -D bar=99 -D baz=hello -D foo=11 %s + +from sys import env_get_int, env_get_string, is_defined + +from testing import assert_equal, assert_true, assert_false + + +def test_is_defined(): + assert_true(is_defined["bar"]()) + assert_true(is_defined["foo"]()) + assert_true(is_defined["baz"]()) + assert_false(is_defined["boo"]()) + + +def test_get_string(): + assert_equal(env_get_string["baz"](), "hello") + + +def test_env_get_int(): + assert_equal(env_get_int["bar"](), 99) + assert_equal(env_get_int["foo", 42](), 11) + assert_equal(env_get_int["bar", 42](), 99) + assert_equal(env_get_int["boo", 42](), 42) + + +def main(): + test_is_defined() + test_get_string() + test_env_get_int() From e0eb26b19008e47001bb0ba8e1c9da9f5c1bdb4a Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 18:26:56 -0500 Subject: [PATCH 0213/2019] [External][stdlib] Add trait `Boolable` to `Dict` and `List` (#2262) (#38055) This allows a very common pattern that's available in python: ```mojo error_messages = List[String]() for ... in ...: if ...: error_messages.append("some new error") if error_messages: print("There were errors!!!") .... sys.exit(1) ``` Signed-off-by: gabrieldemarmiesse mojo-orig-commit: a339c37dab45784f0eddd6f6e3d20cdd08a64673 MODULAR_ORIG_COMMIT_REV_ID: 8c807af8cc3fb9bd9e91f7a35a42ee1d97979299 --- stdlib/src/collections/dict.mojo | 12 +++++++++++- stdlib/src/collections/list.mojo | 2 +- stdlib/test/collections/test_dict.mojo | 14 ++++++++++++++ stdlib/test/collections/test_list.mojo | 8 ++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 92c0c49bf3..d9f4ed6eff 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -287,7 +287,9 @@ struct _DictIndex: self.data.free() -struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): +struct Dict[K: KeyElement, V: CollectionElement]( + Sized, CollectionElement, Boolable +): """A container that stores key-value pairs. The key type and value type must be specified statically, unlike a Python @@ -489,6 +491,14 @@ struct Dict[K: KeyElement, V: CollectionElement](Sized, CollectionElement): """The number of elements currenly stored in the dictionary.""" return self.size + fn __bool__(self) -> Bool: + """Check if the dictionary is empty or not. + + Returns: + `False` if the dictionary is empty, `True` if there is at least one element. + """ + return len(self).__bool__() + fn find(self, key: K) -> Optional[V]: """Find a value in the dictionary by key. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index c5cd7a6d8b..17c47587dc 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -164,7 +164,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """Checks whether the list has any elements or not. Returns: - False for an empty list, True otherwise. + `False` if the list is empty, `True` if there is at least one element. """ return len(self) > 0 diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 64901fe0a2..1c4f8e199f 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -41,6 +41,19 @@ def test_multiple_resizes(): assert_equal(20, dict["key19"]) +def test_bool_conversion(): + var dict = Dict[String, Int]() + assert_false(dict) + dict["a"] = 1 + assert_true(dict) + dict["b"] = 2 + assert_true(dict) + dict.pop("a") + assert_true(dict) + dict.pop("b") + assert_false(dict) + + def test_big_dict(): var dict = Dict[String, Int]() for i in range(2000): @@ -322,3 +335,4 @@ def test_owned_kwargs_dict(): def main(): test_dict() test_owned_kwargs_dict() + test_bool_conversion() diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 9d36c3ee7f..8faccac83b 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -68,6 +68,13 @@ def test_list_clear(): assert_equal(list.capacity, 3) +def test_list_to_bool_conversion(): + assert_false(List[String]()) + assert_true(List[String]("a")) + assert_true(List[String]("", "a")) + assert_true(List[String]("")) + + def test_list_pop(): var list = List[Int]() # Test pop with index @@ -480,6 +487,7 @@ def main(): test_mojo_issue_698() test_list() test_list_clear() + test_list_to_bool_conversion() test_list_pop() test_list_variadic_constructor() test_list_resize() From 05639d87a0c4dc654ffcfc2d08784fa7c90bc467 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 19:38:32 -0500 Subject: [PATCH 0214/2019] [External][mojo-stdlib] Replace manual refcounted `List` with `Arc` (#2146) (#38040) Rework `_RefCountedList` to use `Arc` instead of manually maintaining a refcount using an `Atomic`. This simplifies things a bit. Part of https://github.com/modularml/mojo/issues/2030 Signed-off-by: jayzhan211 mojo-orig-commit: bc8ea95ed7fe38b803a8a2bf8fdac9b3a6b879a6 MODULAR_ORIG_COMMIT_REV_ID: 7a9d9abac78d3d1dfc6258abc5bb5460755bf039 --- stdlib/src/builtin/object.mojo | 39 ++++++++++++---------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 3a0ff9c410..c14704b64f 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -21,6 +21,7 @@ from sys.intrinsics import _type_is_eq from memory import memcmp, memcpy from memory.unsafe import DTypePointer, LegacyPointer +from memory._arc import Arc from utils import StringRef from utils.loop import unroll @@ -87,15 +88,12 @@ struct _RefCountedList: ref-counted data types. """ - fn __init__(inout self): - self.refcount = 1 - self.impl = List[_ObjectImpl]() - - var refcount: Atomic[DType.index] - """The number of live references to the list.""" - var impl: List[_ObjectImpl] + var impl: Arc[List[_ObjectImpl]] """The list value.""" + fn __init__(inout self): + self.impl = Arc[List[_ObjectImpl]](List[_ObjectImpl]()) + @register_passable("trivial") struct _RefCountedListRef: @@ -111,20 +109,11 @@ struct _RefCountedListRef: @always_inline fn copy(self) -> Self: - _ = self.lst.bitcast[_RefCountedList]()[].refcount.fetch_add(1) + _ = self.lst.bitcast[_RefCountedList]()[].impl return Self {lst: self.lst} fn release(self): - var ptr = self.lst.bitcast[_RefCountedList]() - var prev = ptr[].refcount.fetch_sub(1) - if prev != 1: - return - - # Run the destructor on the list elements and then destroy the list. - var list = __get_address_as_owned_value(ptr.address).impl - for i in range(len(list)): - list[i].destroy() - ptr.free() + var ptr = self.lst.bitcast[_RefCountedList]()[].impl struct _RefCountedAttrsDict: @@ -651,25 +640,25 @@ struct _ObjectImpl(CollectionElement, Stringable): # ===------------------------------------------------------------------=== # @always_inline - fn get_list_ptr(self) -> UnsafePointer[_RefCountedList]: - return self.get_as_list().lst.bitcast[_RefCountedList]() + fn get_list_ptr(self) -> Arc[List[_ObjectImpl]]: + return self.get_as_list().lst.bitcast[_RefCountedList]()[].impl @always_inline fn list_append(self, value: Self): - self.get_list_ptr()[].impl.append(value.value) + self.get_list_ptr()[].append(value.value) @always_inline fn get_list_length(self) -> Int: - return len(self.get_list_ptr()[].impl) + return len(self.get_list_ptr()[]) @always_inline fn get_list_element(self, i: Int) -> _ObjectImpl: - return self.get_list_ptr()[].impl[i].copy() + return self.get_list_ptr()[][i].copy() @always_inline fn set_list_element(self, i: Int, value: _ObjectImpl): - self.get_list_ptr()[].impl[i].destroy() - self.get_list_ptr()[].impl[i] = value + self.get_list_ptr()[][i].destroy() + self.get_list_ptr()[][i] = value # ===------------------------------------------------------------------=== # # Object Attribute Functions From fb8c660051f9f8e0e1cc39d1cb44262572059329 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 19:40:35 -0500 Subject: [PATCH 0215/2019] [External][stdlib] Add `List` constructor from (ptr, size, capacity) parts (#2182) (#38048) Add a constructor to `List` from the `(ptr, size, capacity)` parts. This will allow us to fix `FileHandle.read_bytes()` to avoid copying of data - see https://github.com/modularml/mojo/issues/2051. Fixes https://github.com/modularml/mojo/issues/2170. Signed-off-by: Kaushal Phulgirkar mojo-orig-commit: c3361e8580cded34e488a42f5bdb1df516906c51 MODULAR_ORIG_COMMIT_REV_ID: 7bf9e77148049701cea44843c7db3d98e7bbf068 --- stdlib/src/collections/list.mojo | 14 +++++++++++ stdlib/test/collections/test_list.mojo | 33 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 17c47587dc..f269055403 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -124,6 +124,20 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): for value in values: self.append(value[]) + fn __init__( + inout self: Self, data: UnsafePointer[T], size: Int, capacity: Int + ): + """Constructs a list from a pointer, its size, and its capacity. + + Args: + data: The pointer to the data. + size: The number of elements in the list. + capacity: The capacity of the list. + """ + self.data = data + self.size = size + self.capacity = capacity + fn __moveinit__(inout self, owned existing: Self): """Move data of an existing list into a new one. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 8faccac83b..32717402a6 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -483,6 +483,37 @@ def test_list_boolable(): assert_false(List[Int]()) +def test_constructor_from_pointer(): + new_pointer = UnsafePointer[Int8].alloc(5) + new_pointer[0] = 0 + new_pointer[1] = 1 + new_pointer[2] = 2 + # rest is not initialized + + var some_list = List[Int8](new_pointer, size=3, capacity=5) + assert_equal(some_list[0], 0) + assert_equal(some_list[1], 1) + assert_equal(some_list[2], 2) + assert_equal(len(some_list), 3) + assert_equal(some_list.capacity, 5) + + +def test_constructor_from_other_list_through_pointer(): + initial_list = List[Int8](0, 1, 2) + # we do a backup of the size and capacity because + # the list attributes will be invalid after the steal_data call + var size = len(initial_list) + var capacity = initial_list.capacity + var some_list = List[Int8]( + initial_list.steal_data(), size=size, capacity=capacity + ) + assert_equal(some_list[0], 0) + assert_equal(some_list[1], 1) + assert_equal(some_list[2], 2) + assert_equal(len(some_list), size) + assert_equal(some_list.capacity, capacity) + + def main(): test_mojo_issue_698() test_list() @@ -502,3 +533,5 @@ def main(): test_list_iter_mutable() test_list_span() test_list_boolable() + test_constructor_from_pointer() + test_constructor_from_other_list_through_pointer() From b14c3969aa1c1aebf18fc6213c858eb74b9f6009 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 20:08:06 -0500 Subject: [PATCH 0216/2019] [External][mojo-stdlib] Refactor tests to use `assert_*` functions whenever possible (#2069) (#38037) Refactor several of the tests to use the `assert_*` functions from the `testing` module instead of `FileCheck`. Signed-off-by: Shen Yi Hong mojo-orig-commit: a77ebc3dd9b2e4e32d062235d42c3c86d921b9de Co-authored-by: yihong MODULAR_ORIG_COMMIT_REV_ID: 0560e4d8cc65b0ef030b4d44bca73341f6e1f4f4 --- stdlib/test/base64/test_base64.mojo | 31 ++- stdlib/test/builtin/test_error.mojo | 14 +- stdlib/test/builtin/test_int_literal.mojo | 90 +++----- stdlib/test/builtin/test_issue_1004.mojo | 4 +- stdlib/test/builtin/test_list.mojo | 29 +-- stdlib/test/builtin/test_range.mojo | 30 +-- stdlib/test/os/test_atomic.mojo | 61 ++--- stdlib/test/python/test_ownership.mojo | 102 ++++---- .../python/test_python_error_handling.mojo | 20 +- stdlib/test/python/test_python_info.mojo | 17 +- stdlib/test/python/test_python_interop.mojo | 28 +-- stdlib/test/python/test_python_object.mojo | 218 ++++++------------ stdlib/test/python/test_python_to_mojo.mojo | 5 +- stdlib/test/sys/test_intrinsics.mojo | 8 +- stdlib/test/sys/test_macos_target.mojo | 23 +- stdlib/test/sys/test_targetinfo.mojo | 56 ++--- stdlib/test/time/test_time.mojo | 37 ++- stdlib/test/utils/issue_13632.mojo | 8 +- stdlib/test/utils/test_numerics.mojo | 70 ++---- 19 files changed, 301 insertions(+), 550 deletions(-) diff --git a/stdlib/test/base64/test_base64.mojo b/stdlib/test/base64/test_base64.mojo index 39db2d1128..43cf6685b8 100644 --- a/stdlib/test/base64/test_base64.mojo +++ b/stdlib/test/base64/test_base64.mojo @@ -10,33 +10,28 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from base64 import b64encode +from testing import assert_equal -# CHECK-LABEL: test_b64encode -fn test_b64encode(): - print("== test_b64encode") +def test_b64encode(): + assert_equal(b64encode("a"), "YQ==") - # CHECK: YQ== - print(b64encode("a")) + assert_equal(b64encode("fo"), "Zm8=") - # CHECK: Zm8= - print(b64encode("fo")) + assert_equal(b64encode("Hello Mojo!!!"), "SGVsbG8gTW9qbyEhIQ==") - # CHECK: SGVsbG8gTW9qbyEhIQ== - print(b64encode("Hello Mojo!!!")) + assert_equal(b64encode("Hello 🔥!!!"), "SGVsbG8g8J+UpSEhIQ==") - # CHECK: SGVsbG8g8J+UpSEhIQ== - print(b64encode("Hello 🔥!!!")) + assert_equal( + b64encode("the quick brown fox jumps over the lazy dog"), + "dGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==", + ) - # CHECK: dGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw== - print(b64encode("the quick brown fox jumps over the lazy dog")) + assert_equal(b64encode("ABCDEFabcdef"), "QUJDREVGYWJjZGVm") - # CHECK: QUJDREVGYWJjZGVm - print(b64encode("ABCDEFabcdef")) - -fn main(): +def main(): test_b64encode() diff --git a/stdlib/test/builtin/test_error.mojo b/stdlib/test/builtin/test_error.mojo index 7572cfd636..54a4ec166c 100644 --- a/stdlib/test/builtin/test_error.mojo +++ b/stdlib/test/builtin/test_error.mojo @@ -10,23 +10,21 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s + +from testing import assert_equal def raise_an_error(): raise Error("MojoError: This is an error!") -fn main(): - # CHECK: == test_error - print("== test_error") +def main(): try: _ = raise_an_error() except e: - # CHECK: MojoError: This is an error! - print(e) + assert_equal(str(e), "MojoError: This is an error!") var myString: String = "FOO" var error = Error(myString) - # CHECK: FOO - print(error) + assert_equal(error, "FOO") diff --git a/stdlib/test/builtin/test_int_literal.mojo b/stdlib/test/builtin/test_int_literal.mojo index 91b715fbc4..55ac0af7f4 100644 --- a/stdlib/test/builtin/test_int_literal.mojo +++ b/stdlib/test/builtin/test_int_literal.mojo @@ -10,79 +10,45 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s +from testing import assert_equal -# CHECK-LABEL: test_int -fn test_int(): - print("== test_int") - # CHECK: 3 - print(3) - # CHECK: 6 - print(3 + 3) - # CHECK: 3 - print(4 - 1) - # CHECK: 5 - print(6 - 1) +fn test_int() raises: + assert_equal(3, 3) + assert_equal(3 + 3, 6) + assert_equal(4 - 1, 3) + assert_equal(6 - 1, 5) -# CHECK-LABEL: test_floordiv -fn test_floordiv(): - print("== test_floordiv") +fn test_floordiv() raises: + assert_equal(2 // 2, 1) + assert_equal(2 // 3, 0) + assert_equal(2 // -2, -1) + assert_equal(99 // -2, -50) - # CHECK: 1 - print(2 // 2) - # CHECK: 0 - print(2 // 3) +fn test_mod() raises: + assert_equal(99 % 1, 0) + assert_equal(99 % 3, 0) + assert_equal(99 % -2, -1) + assert_equal(99 % 8, 3) + assert_equal(99 % -8, -5) + assert_equal(2 % -1, 0) + assert_equal(2 % -2, 0) + assert_equal(3 % -2, -1) + assert_equal(-3 % 2, 1) - # CHECK: -1 - print(2 // -2) - # CHECK: -50 - print(99 // -2) +fn test_bit_width() raises: + assert_equal((0)._bit_width(), 1) + assert_equal((-1)._bit_width(), 1) + assert_equal((255)._bit_width(), 9) + assert_equal((-256)._bit_width(), 9) -# CHECK-LABEL: test_mod -fn test_mod(): - print("== test_mod") - - # CHECK: 0 - print(99 % 1) - # CHECK: 0 - print(99 % 3) - # CHECK: -1 - print(99 % -2) - # CHECK: 3 - print(99 % 8) - # CHECK: -5 - print(99 % -8) - # CHECK: 0 - print(2 % -1) - # CHECK: 0 - print(2 % -2) - # CHECK: -1 - print(3 % -2) - # CHECK: 1 - print(-3 % 2) - - -# CHECK-LABEL: test_bit_width -fn test_bit_width(): - print("== test_bit_width") - - # CHECK: 1 - print((0)._bit_width()) - # CHECK: 1 - print((-1)._bit_width()) - # CHECK: 9 - print((255)._bit_width()) - # CHECK: 9 - print((-256)._bit_width()) - - -fn main(): +def main(): test_int() test_floordiv() test_mod() diff --git a/stdlib/test/builtin/test_issue_1004.mojo b/stdlib/test/builtin/test_issue_1004.mojo index 759e1d3977..15e0c9f41c 100644 --- a/stdlib/test/builtin/test_issue_1004.mojo +++ b/stdlib/test/builtin/test_issue_1004.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s # Test for https://github.com/modularml/mojo/issues/1004 from testing import assert_equal @@ -24,6 +24,4 @@ def main(): try: foo("Hello") except e: - # CHECK: Failed on: Hello - print(e) assert_equal(str(e), "Failed on: Hello") diff --git a/stdlib/test/builtin/test_list.mojo b/stdlib/test/builtin/test_list.mojo index 67a8183d1f..4c4e1018ac 100644 --- a/stdlib/test/builtin/test_list.mojo +++ b/stdlib/test/builtin/test_list.mojo @@ -10,34 +10,27 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s +from testing import assert_equal -# CHECK-LABEL: test_list -fn test_list(): - print("== test_list") - # CHECK: 4 - print(len([1, 2.0, 3.14, [-1, -2]])) +fn test_list() raises: + assert_equal(len([1, 2.0, 3.14, [-1, -2]]), 4) -# CHECK-LABEL: test_variadic_list -fn test_variadic_list(): - print("== test_variadic_list") +fn test_variadic_list() raises: @parameter - fn print_list(*nums: Int): - # CHECK: 5 - # CHECK: 8 - # CHECK: 6 - for num in nums: - print(num) + fn print_list(*nums: Int) raises: + assert_equal(nums[0], 5) + assert_equal(nums[1], 8) + assert_equal(nums[2], 6) - # CHECK: 3 - print(len(nums)) + assert_equal(len(nums), 3) print_list(5, 8, 6) -fn main(): +def main(): test_list() test_variadic_list() diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index d6789ea209..6ed3f71fec 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -10,12 +10,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s + +from testing import assert_equal from testing import assert_equal -def test_range_len(): +fn test_range_len() raises: assert_equal(range(0).__len__(), 0) assert_equal(range(-1).__len__(), 0) assert_equal(range(10).__len__(), 10) @@ -26,24 +28,12 @@ def test_range_len(): assert_equal(range(38, -13, -23).__len__(), 3) -# CHECK-LABEL: test_range_getitem -fn test_range_getitem(): - print("== test_range_getitem") - - # CHECK: 5 - print(range(10)[5]) - - # CHECK: 3 - print(range(0, 10)[3]) - - # CHECK: 8 - print(range(5, 10)[3]) - - # CHECK: 8 - print(range(10, 0, -1)[2]) - - # CHECK: 8 - print(range(0, 10, 2)[4]) +fn test_range_getitem() raises: + assert_equal(range(10)[5], 5) + assert_equal(range(0, 10)[3], 3) + assert_equal(range(5, 10)[3], 8) + assert_equal(range(10, 0, -1)[2], 8) + assert_equal(range(0, 10, 2)[4], 8) def main(): diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 41d0c736f9..37249cec71 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -10,85 +10,60 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from os.atomic import Atomic from testing import assert_equal, assert_true, assert_false -# CHECK-LABEL: test_atomic -fn test_atomic(): - print("== test_atomic") - +fn test_atomic() raises: var atom: Atomic[DType.index] = 3 - # CHECK: 3 - print(atom.load()) + assert_equal(atom.load(), 3) - # CHECK: 3 - print(atom.value) + assert_equal(atom.value, 3) atom += 4 - - # CHECK: 7 - print(atom.value) + assert_equal(atom.value, 7) atom -= 4 + assert_equal(atom.value, 3) - # CHECK: 3 - print(atom.value) - - # CHECK: 3 atom.max(0) - print(atom.value) + assert_equal(atom.value, 3) - # CHECK: 42 atom.max(42) - print(atom.value) + assert_equal(atom.value, 42) - # CHECK: 3 atom.min(3) - print(atom.value) + assert_equal(atom.value, 3) - # CHECK: 0 atom.min(0) - print(atom.value) + assert_equal(atom.value, 0) -# CHECK-LABEL: test_atomic_floating_point -fn test_atomic_floating_point(): - print("== test_atomic_floating_point") - +fn test_atomic_floating_point() raises: var atom: Atomic[DType.float32] = Float32(3.0) - # CHECK: 3.0 - print(atom.value) + assert_equal(atom.value, 3.0) atom += 4 - - # CHECK: 7.0 - print(atom.value) + assert_equal(atom.value, 7.0) atom -= 4 + assert_equal(atom.value, 3.0) - # CHECK: 3.0 - print(atom.value) - - # CHECK: 3.0 atom.max(0) - print(atom.value) + assert_equal(atom.value, 3.0) - # CHECK: 42.0 atom.max(42) - print(atom.value) + assert_equal(atom.value, 42.0) - # CHECK: 3.0 atom.min(3) - print(atom.value) + assert_equal(atom.value, 3.0) - # CHECK: 0.0 atom.min(0) - print(atom.value) + assert_equal(atom.value, 0.0) def test_atomic_move_constructor(): diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index 3bebbd8503..f197bdb449 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo -D TEST_DIR=%S %s | FileCheck %s +# RUN: %mojo -D TEST_DIR=%S %s from sys.param_env import env_get_string @@ -20,87 +20,63 @@ from python._cpython import CPython, PyObjectPtr from python.object import PythonObject from python.python import Python +from testing import assert_equal + alias TEST_DIR = env_get_string["TEST_DIR"]() -fn test_import(inout python: Python) raises -> String: - try: - Python.add_to_path(TEST_DIR) - var my_module: PythonObject = Python.import_module("my_module") - var py_string = my_module.my_function("Hello") - var str = String(python.__str__(py_string)) - return str - except e: - return str(e) +fn test_import(inout python: Python) raises: + Python.add_to_path(TEST_DIR) + var my_module: PythonObject = Python.import_module("my_module") + var py_string = my_module.my_function("Hello") + var str = String(python.__str__(py_string)) + assert_equal(str, "Formatting the string from Lit with Python: Hello") -fn test_list(inout python: Python) raises -> String: - try: - var b: PythonObject = Python.import_module("builtins") - var my_list = PythonObject([1, 2.34, "False"]) - var py_string = str(my_list) - return String(python.__str__(py_string)) - except e: - return str(e) +fn test_list(inout python: Python) raises: + var b: PythonObject = Python.import_module("builtins") + var my_list = PythonObject([1, 2.34, "False"]) + var py_string = str(my_list) + assert_equal(py_string, "[1, 2.34, 'False']") -fn test_tuple(inout python: Python) raises -> String: - try: - var b: PythonObject = Python.import_module("builtins") - var my_tuple = PythonObject((1, 2.34, "False")) - var py_string = str(my_tuple) - return String(python.__str__(py_string)) - except e: - return str(e) +fn test_tuple(inout python: Python) raises: + var b: PythonObject = Python.import_module("builtins") + var my_tuple = PythonObject((1, 2.34, "False")) + var py_string = str(my_tuple) + assert_equal(py_string, "(1, 2.34, 'False')") -fn test_call_ownership(inout python: Python) raises -> String: +fn test_call_ownership(inout python: Python) raises: var obj: PythonObject = [1, "5"] var py_string = str(obj) var string = python.__str__(py_string) - return String(string) + assert_equal(string, "[1, '5']") -fn test_getitem_ownership(inout python: Python) raises -> String: - try: - var obj: PythonObject = [1, "5"] - var py_string = str(obj[1]) - var string = python.__str__(py_string) - return String(string) - except e: - return str(e) +fn test_getitem_ownership(inout python: Python) raises: + var obj: PythonObject = [1, "5"] + var py_string = str(obj[1]) + var string = python.__str__(py_string) + assert_equal(string, "5") -fn test_getattr_ownership(inout python: Python) raises -> String: - try: - Python.add_to_path(TEST_DIR) - var my_module: PythonObject = Python.import_module("my_module") - var obj = my_module.Foo(4) - var py_string = str(obj.bar) - var string = python.__str__(py_string) - return String(string) - except e: - return str(e) +fn test_getattr_ownership(inout python: Python) raises: + Python.add_to_path(TEST_DIR) + var my_module: PythonObject = Python.import_module("my_module") + var obj = my_module.Foo(4) + var py_string = str(obj.bar) + var string = python.__str__(py_string) + assert_equal(string, "4") def main(): # initializing Python instance calls init_python var python = Python() - # CHECK: [1, 2.34, 'False'] - print(test_list(python)) - - # CHECK: (1, 2.34, 'False') - print(test_tuple(python)) - - # CHECK: [1, '5'] - print(test_call_ownership(python)) - - # CHECK: 5 - print(test_getitem_ownership(python)) - - # CHECK: 4 - print(test_getattr_ownership(python)) - - # CHECK: Formatting the string from Lit with Python: Hello - print(test_import(python)) + test_list(python) + test_tuple(python) + test_call_ownership(python) + test_getitem_ownership(python) + test_getattr_ownership(python) + test_import(python) diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index cc3af1af9e..b9cdc7b03f 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo -D TEST_DIR=%S %s | FileCheck %s +# RUN: %mojo -D TEST_DIR=%S %s from sys.param_env import env_get_string @@ -19,6 +19,8 @@ from python import Python from python._cpython import CPython, PyObjectPtr from python.object import PythonObject +from testing import assert_equal, assert_raises + alias TEST_DIR = env_get_string["TEST_DIR"]() @@ -26,7 +28,7 @@ fn test_python_exception_import() raises: try: var sys = Python.import_module("my_uninstalled_module") except e: - print(e) + assert_equal(str(e), "No module named 'my_uninstalled_module'") fn test_python_exception_getattr() raises: @@ -37,7 +39,7 @@ fn test_python_exception_getattr() raises: var person = my_module.Person() var expec_fail = person.undefined() except e: - print(e) + assert_equal(str(e), "'Person' object has no attribute 'undefined'") fn test_python_exception_getitem() raises: @@ -45,25 +47,21 @@ fn test_python_exception_getitem() raises: var list = PythonObject([1, 2, 3]) var should_fail = list[13] except e: - print(e) + assert_equal(str(e), "list index out of range") fn test_python_exception_call() raises: - try: + with assert_raises( + contains="Can't instantiate abstract class AbstractPerson" + ): Python.add_to_path(TEST_DIR) var my_module: PythonObject = Python.import_module("my_module") if my_module: var person = my_module.AbstractPerson() - except e: - print(e) def main(): - # CHECK: No module named 'my_uninstalled_module' test_python_exception_import() - # CHECK: 'Person' object has no attribute 'undefined' test_python_exception_getattr() - # CHECK: list index out of range test_python_exception_getitem() - # CHECK: Can't instantiate abstract class AbstractPerson test_python_exception_call() diff --git a/stdlib/test/python/test_python_info.mojo b/stdlib/test/python/test_python_info.mojo index b78286450c..613c699897 100644 --- a/stdlib/test/python/test_python_info.mojo +++ b/stdlib/test/python/test_python_info.mojo @@ -11,24 +11,23 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s | FileCheck %s +# RUN: %mojo-no-debug %s from python._cpython import PythonVersion from python import Python +from testing import assert_equal -fn test_python_version(inout python: Python): + +fn test_python_version(inout python: Python) raises: var version = "3.10.8 (main, Nov 24 2022, 08:08:27) [Clang 14.0.6 ]" var pythonVersion = PythonVersion(version) - # CHECK: 3 - print(pythonVersion.major) - # CHECK: 10 - print(pythonVersion.minor) - # CHECK: 8 - print(pythonVersion.patch) + assert_equal(pythonVersion.major, 3) + assert_equal(pythonVersion.minor, 10) + assert_equal(pythonVersion.patch, 8) -fn main(): +def main(): var python = Python() test_python_version(python) diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index 3771f0ca45..445408c6af 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo -D TEST_DIR=%S %s | FileCheck %s +# RUN: %mojo -D TEST_DIR=%S %s from sys.param_env import env_get_string @@ -19,6 +19,8 @@ from python._cpython import CPython, PyObjectPtr from python.object import PythonObject from python.python import Python, _get_global_python_itf +from testing import assert_equal + alias TEST_DIR = env_get_string["TEST_DIR"]() @@ -63,23 +65,23 @@ fn test_call(inout python: Python) -> String: def main(): var python = Python() - # CHECK: orange - print(test_local_import(python)) + assert_equal(test_local_import(python), "orange") - # CHECK: carrot ('bread', 'rice') fruit=pear {'protein': 'fish', 'cake': 'yes'} - print(test_call(python)) + assert_equal( + test_call(python), + ( + "carrot ('bread', 'rice') fruit=pear {'protein': 'fish', 'cake':" + " 'yes'}" + ), + ) - # CHECK: [1, 2.4, True, 'False'] var obj: PythonObject = [1, 2.4, True, "False"] - print(obj) + assert_equal(str(obj), "[1, 2.4, True, 'False']") - # CHECK: (1, 2.4, True, 'False') obj = (1, 2.4, True, "False") - print(obj) + assert_equal(str(obj), "(1, 2.4, True, 'False')") - # CHECK: None obj = None - print(obj) + assert_equal(str(obj), "None") - # CHECK: ab - print(test_execute_python_string(python)) + assert_equal(test_execute_python_string(python), "ab") diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 4e16b46918..dfd19a5b2b 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -17,337 +17,267 @@ from memory.unsafe import Pointer from python._cpython import CPython, PyObjectPtr from python.object import PythonObject from python.python import Python -from testing import assert_false, assert_raises, assert_true +from testing import assert_false, assert_raises, assert_true, assert_equal from utils import StringRef -# CHECK-LABEL: test_dunder_methods fn test_dunder_methods(inout python: Python): - print("=== test_dunder_methods ===") try: var a = PythonObject(34) var b = PythonObject(10) # __add__ - # CHECK-NEXT: 44 var c = a + b - print(c) + assert_true(c, 44) # __add__ - # CHECK-NEXT: 134 c = a + 100 - print(c) + assert_true(c, 134) # __iadd__ - # CHECK-NEXT: 234 c += 100 - print(c) + assert_equal(c, 234) # __radd__ - # CHECK-NEXT: 134 c = 100 + a - print(c) + assert_equal(c, 134) # __sub__ - # CHECK-NEXT: 24 c = a - b - print(c) + assert_equal(c, 24) # __isub__ - # CHECK-NEXT: -76 c -= 100 - print(c) + assert_equal(c, -76) # __sub__ - # CHECK-NEXT: -66 c = a - 100 - print(c) + assert_equal(c, -66) # __rsub__ - # CHECK-NEXT: 66 c = 100 - a - print(c) + assert_equal(c, 66) # __mul__ - # CHECK-NEXT: 340 c = a * b - print(c) + assert_equal(c, 340) # __imul__ - # CHECK-NEXT: 3400 c *= 10 - print(c) + assert_equal(c, 3400) # __mul__ - # CHECK-NEXT: 340 c = a * 10 - print(c) + assert_equal(c, 340) # __rmul__ - # CHECK-NEXT: 340 c = 34 * b - print(c) + assert_equal(c, 340) # __floordiv__ - # CHECK-NEXT: 3 c = a // b - print(c) + assert_equal(c, 3) # __ifloordiv__ - # CHECK-NEXT: 1 c //= 2 - print(c) + assert_equal(c, 1) # __floordiv__ - # CHECK-NEXT: 3 c = a // 10 - print(c) + assert_equal(c, 3) # __rfloordiv__ - # CHECK-NEXT: 3 c = 34 // b - print(c) + assert_equal(c, 3) # __truediv__ - # CHECK-NEXT: 3.4 c = a / b - print(c) + assert_equal(c, 3.4) # __itruediv__ - # CHECK-NEXT: 1.7 c /= 2 - print(c) + assert_equal(c, 1.7) # __truediv__ - # CHECK-NEXT: 3.4 c = a / 10 - print(c) + assert_equal(c, 3.4) # __rtruediv__ - # CHECK-NEXT: 3.4 c = 34 / b - print(c) + assert_equal(c, 3.4) # __mod__ - # CHECK-NEXT: 4 c = a % b - print(c) + assert_equal(c, 4) # __imod__ - # CHECK-NEXT: 1 c %= 3 - print(c) + assert_equal(c, 1) # __mod__ - # CHECK-NEXT: 4 c = a % 10 - print(c) + assert_equal(c, 4) # __rmod__ - # CHECK-NEXT: 4 c = 34 % b - print(c) + assert_equal(c, 4) # __xor__ - # CHECK-NEXT: 40 c = a ^ b - print(c) + assert_equal(c, 40) # __ixor__ - # CHECK-NEXT: 39 c ^= 15 - print(c) + assert_equal(c, 39) # __xor__ - # CHECK-NEXT: 40 c = a ^ 10 - print(c) + assert_equal(c, 40) # __rxor__ - # CHECK-NEXT: 40 c = 34 ^ b - print(c) + assert_equal(c, 40) # __or__ - # CHECK-NEXT: 42 c = a | b - print(c) + assert_equal(c, 42) # __ior__ - # CHECK-NEXT: 43 c |= 9 - print(c) + assert_equal(c, 43) # __or__ - # CHECK-NEXT: 42 c = a | 10 - print(c) + assert_equal(c, 42) # __ror__ - # CHECK-NEXT: 42 c = 34 | b - print(c) + assert_equal(c, 42) # __and__ - # CHECK-NEXT: 2 c = a & b - print(c) + assert_equal(c, 2) # __iand__ - # CHECK-NEXT: 2 c &= 6 - print(c) + assert_equal(c, 2) # __and__ - # CHECK-NEXT: 2 c = a & 10 - print(c) + assert_equal(c, 2) # __rand__ - # CHECK-NEXT: 2 c = 34 & b - print(c) + assert_equal(c, 2) # __rshift__ var d = PythonObject(2) - # CHECK-NEXT: 8 c = a >> d - print(c) + assert_equal(c, 8) # __irshift__ - # CHECK-NEXT: 2 c >>= 2 - print(c) + assert_equal(c, 2) # __rshift__ - # CHECK-NEXT: 8 c = a >> 2 - print(c) + assert_equal(c, 8) # __rrshift__ - # CHECK-NEXT: 8 c = 34 >> d - print(c) + assert_equal(c, 8) # __lshift__ - # CHECK-NEXT: 136 c = a << d - print(c) + assert_equal(c, 136) # __ilshift__ - # CHECK-NEXT: 272 c <<= 1 - print(c) + assert_equal(c, 272) # __lshift__ - # CHECK-NEXT: 136 c = a << 2 - print(c) + assert_equal(c, 136) # __rlshift__ - # CHECK-NEXT: 136 c = 34 << d - print(c) + assert_equal(c, 136) # __pow__ - # CHECK-NEXT: 1156 c = a**d - print(c) + assert_equal(c, 1156) # __ipow__ - # CHECK-NEXT: 81 c = 3 c **= 4 - print(c) + assert_equal(c, 81) # __pow__ - # CHECK-NEXT: 1156 c = a**2 - print(c) + assert_equal(c, 1156) # __rpow__ - # CHECK-NEXT: 1156 c = 34**d - print(c) + assert_equal(c, 1156) # __lt__ - # CHECK-NEXT: False c = a < b - print(c) + assert_false(c) # __le__ - # CHECK-NEXT: False c = a <= b - print(c) + assert_false(c) # __gt__ - # CHECK-NEXT: True c = a > b - print(c) + assert_true(c) # __ge__ - # CHECK-NEXT: True c = a >= b - print(c) + assert_true(c) # __eq__ - # CHECK-NEXT: False c = a == b - print(c) + assert_false(c) # __ne__ - # CHECK-NEXT: True c = a != b - print(c) + assert_true(c) # __pos__ - # CHECK-NEXT: 34 c = +a - print(c) + assert_equal(c, 34) # __neg__ - # CHECK-NEXT: -34 c = -a - print(c) + assert_equal(c, -34) # __invert__ - # CHECK-NEXT: -35 c = ~a - print(c) + assert_equal(c, -35) except e: pass -# CHECK-LABEL: test_bool_conversion def test_bool_conversion() -> None: - print("=== test_bool_conversion ===") var x: PythonObject = 1 - # CHECK: test_bool: True - print("test_bool:", x == 0 or x == 1) + assert_true(x == 0 or x == 1) -# CHECK-LABEL: test_string_conversions -fn test_string_conversions() -> None: - print("=== test_string_conversions ===") - - # CHECK-LABEL: test_string_literal +fn test_string_conversions() raises -> None: fn test_string_literal() -> None: - print("=== test_string_literal ===") try: var mojo_str: StringLiteral = "mojo" var py_str = PythonObject(mojo_str) var py_capitalized = py_str.capitalize() var py = Python() var mojo_capitalized = py.__str__(py_capitalized) - # CHECK: Mojo - print(mojo_capitalized) + assert_equal(mojo_capitalized, "Mojo") except e: print("Error occurred") - # CHECK-LABEL: test_string_ref fn test_string_ref() -> None: - print("=== test_string_ref ===") try: var mojo_str: StringLiteral = "mojo" var mojo_strref = StringRef(mojo_str) @@ -355,14 +285,11 @@ fn test_string_conversions() -> None: var py_capitalized = py_str.capitalize() var py = Python() var mojo_capitalized = py.__str__(py_capitalized) - # CHECK: Mojo - print(mojo_capitalized) + assert_equal(mojo_capitalized, "Mojo") except e: print("Error occurred") - # CHECK-LABEL: test_string fn test_string() -> None: - print("=== test_string ===") try: var mo_str = String("mo") var jo_str = String("jo") @@ -371,19 +298,15 @@ fn test_string_conversions() -> None: var py_capitalized = py_str.capitalize() var py = Python() var mojo_capitalized = py.__str__(py_capitalized) - # CHECK: Mojo - print(mojo_capitalized) + assert_equal(mojo_capitalized, "Mojo") except e: print("Error occurred") - # CHECK-LABEL: test_type_object - fn test_type_object() -> None: - print("=== test_type_object ===") + fn test_type_object() raises -> None: var py = Python() var py_float = PythonObject(3.14) var type_obj = py.type(py_float) - # CHECK: - print(type_obj) + assert_equal(str(type_obj), "") test_string_literal() test_string_ref() @@ -391,20 +314,15 @@ fn test_string_conversions() -> None: test_type_object() -# CHECK-LABEL: test_len def test_len(): - print("=== test_len ===") var empty_list = Python.list() - # CHECK: 0 - print(len(empty_list)) + assert_equal(len(empty_list), 0) var l1 = Python.evaluate("[1,2,3]") - # CHECK: 3 - print(len(l1)) + assert_equal(len(l1), 3) var l2 = Python.evaluate("[42,42.0]") - # CHECK: 2 - print(len(l2)) + assert_equal(len(l2), 2) # CHECK-LABEL: test_is diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 19ffd7ab98..607cfbeca2 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -15,14 +15,15 @@ from python.object import PythonObject from python.python import Python +from testing import assert_equal + fn test_string_to_python_to_mojo(inout python: Python) raises: var py_string = PythonObject("mojo") var py_string_capitalized = py_string.capitalize() - # CHECK: Mojo var cap_mojo_string = str(py_string_capitalized) - print(cap_mojo_string) + assert_equal(cap_mojo_string, "Mojo") fn test_range() raises: diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index d9d75d04a7..f527d61b0c 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from sys.intrinsics import ( compressed_store, @@ -24,16 +24,16 @@ from memory.unsafe import DTypePointer from testing import assert_equal -def test_strided_load(): +fn test_strided_load() raises: alias size = 16 var vector = DTypePointer[DType.float32]().alloc(size) for i in range(size): vector[i] = i - # CHECK: [0.0, 4.0, 8.0, 12.0] var s = strided_load[DType.float32, 4](vector, 4) - print(s) + assert_equal(s, SIMD[DType.float32, 4](0, 4, 8, 12)) + vector.free() diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 453d47f72e..a2d2ade7d5 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -15,7 +15,7 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: darwin -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from sys.info import ( @@ -26,31 +26,24 @@ from sys.info import ( os_is_windows, _macos_version, ) +from testing import assert_true, assert_false from testing import assert_true -# CHECK-LABEL: test_os_query -fn test_os_query(): - print("== test_os_query") +fn test_os_query() raises: + assert_true(os_is_macos()) - # CHECK: True - print(os_is_macos()) + assert_false(os_is_linux()) - # CHECK: False - print(os_is_linux()) - - # CHECK: False - print(os_is_windows()) + assert_false(os_is_windows()) # The mac systems are either arm64 or intel, so they are always little # endian at the moment. - # CHECK: True - print(is_little_endian()) + assert_true(is_little_endian()) - # CHECK: False - print(is_big_endian()) + assert_false(is_big_endian()) def test_os_version(): diff --git a/stdlib/test/sys/test_targetinfo.mojo b/stdlib/test/sys/test_targetinfo.mojo index 7fa5d1b964..f73c741121 100644 --- a/stdlib/test/sys/test_targetinfo.mojo +++ b/stdlib/test/sys/test_targetinfo.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from sys.info import ( alignof, @@ -19,58 +19,40 @@ from sys.info import ( num_physical_cores, sizeof, ) +from testing import assert_equal, assert_true -# CHECK-LABEL: test_sizeof -fn test_sizeof(): - print("== test_sizeof") +fn test_sizeof() raises: + assert_equal(sizeof[__mlir_type.i16](), 2) - # CHECK: 2 - print(sizeof[__mlir_type.i16]()) + assert_equal(sizeof[__mlir_type.ui16](), 2) - # CHECK: 2 - print(sizeof[__mlir_type.ui16]()) + assert_equal(sizeof[DType.int16](), 2) - # CHECK: 2 - print(sizeof[DType.int16]()) + assert_equal(sizeof[DType.uint16](), 2) - # CHECK: 2 - print(sizeof[DType.uint16]()) + assert_equal(sizeof[SIMD[DType.int16, 2]](), 4) - # CHECK: 4 - print(sizeof[SIMD[DType.int16, 2]]()) +fn test_alignof() raises: + assert_true(alignof[__mlir_type.i16]() > 0) -# CHECK-LABEL: test_alignof -fn test_alignof(): - print("== test_alignof") + assert_true(alignof[__mlir_type.ui16]() > 0) - # CHECK: True - print(alignof[__mlir_type.i16]() > 0) + assert_true(alignof[DType.int16]() > 0) - # CHECK: True - print(alignof[__mlir_type.ui16]() > 0) + assert_true(alignof[DType.uint16]() > 0) - # CHECK: True - print(alignof[DType.int16]() > 0) + assert_true(alignof[SIMD[DType.int16, 2]]() > 0) - # CHECK: True - print(alignof[DType.uint16]() > 0) - # CHECK: True - print(alignof[SIMD[DType.int16, 2]]() > 0) +fn test_cores() raises: + assert_true(num_logical_cores() > 0) + assert_true(num_physical_cores() > 0) + assert_true(num_performance_cores() > 0) -fn test_cores(): - # CHECK: True - print(num_logical_cores() > 0) - # CHECK: True - print(num_physical_cores() > 0) - # CHECK: True - print(num_performance_cores() > 0) - - -fn main(): +def main(): test_sizeof() test_alignof() test_cores() diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 833c8eb492..08a79ebcba 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -10,10 +10,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from sys.info import os_is_windows from time import now, sleep, time_function +from testing import assert_true @always_inline @@ -46,42 +47,30 @@ fn time_capturing_function(iters: Int) -> Int: return time_function[time_fn]() -# CHECK-LABEL: test_time -fn test_time(): - print("== test_time") - +fn test_time() raises: alias ns_per_sec = 1_000_000_000 - # CHECK: True - print(now() > 0) + assert_true(now() > 0) var t1 = time_function[time_me]() - # CHECK: True - print(t1 > 1 * ns_per_sec) - # CHECK: True - print(t1 < 10 * ns_per_sec) + assert_true(t1 > 1 * ns_per_sec) + assert_true(t1 < 10 * ns_per_sec) var t2 = time_templated_function[DType.float32]() - # CHECK: True - print(t2 > 1 * ns_per_sec) - # CHECK: True - print(t2 < 10 * ns_per_sec) + assert_true(t2 > 1 * ns_per_sec) + assert_true(t2 < 10 * ns_per_sec) var t3 = time_capturing_function(42) - # CHECK: True - print(t3 > 1 * ns_per_sec) - # CHECK: True - print(t3 < 10 * ns_per_sec) + assert_true(t3 > 1 * ns_per_sec) + assert_true(t3 < 10 * ns_per_sec) # test now() directly since time_function doesn't use now on windows var t4 = now() time_me() var t5 = now() - # CHECK: True - print((t5 - t4) > 1 * ns_per_sec) - # CHECK: True - print((t5 - t4) < 10 * ns_per_sec) + assert_true((t5 - t4) > 1 * ns_per_sec) + assert_true((t5 - t4) < 10 * ns_per_sec) -fn main(): +def main(): test_time() diff --git a/stdlib/test/utils/issue_13632.mojo b/stdlib/test/utils/issue_13632.mojo index 02435e8121..7d9c460d99 100644 --- a/stdlib/test/utils/issue_13632.mojo +++ b/stdlib/test/utils/issue_13632.mojo @@ -10,9 +10,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from collections import List +from testing import assert_equal fn sum_items(data: List[Int8]) -> Int: @@ -26,7 +27,6 @@ fn make_abcd_vector() -> List[Int8]: return List[Int8](97, 98, 99, 100) -fn main(): +def main(): var vec = make_abcd_vector() - # CHECK: 394 - print(sum_items(vec)) + assert_equal(sum_items(vec), 394) diff --git a/stdlib/test/utils/test_numerics.mojo b/stdlib/test/utils/test_numerics.mojo index aee81c4ee5..d39d52dea8 100644 --- a/stdlib/test/utils/test_numerics.mojo +++ b/stdlib/test/utils/test_numerics.mojo @@ -10,62 +10,40 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from utils._numerics import FPUtils +from testing import assert_equal, assert_true, assert_false alias FPU64 = FPUtils[DType.float64] -# CHECK-LABEL: test_numerics -fn test_numerics(): - print("== test_numerics") +fn test_numerics() raises: + assert_equal(FPUtils[DType.float32].mantissa_width(), 23) - # CHECK: 23 - print(FPUtils[DType.float32].mantissa_width()) + assert_equal(FPUtils[DType.float64].mantissa_width(), 52) - # CHECK: 52 - print(FPUtils[DType.float64].mantissa_width()) + assert_equal(FPUtils[DType.float32].exponent_bias(), 127) - # CHECK: 127 - print(FPUtils[DType.float32].exponent_bias()) + assert_equal(FPUtils[DType.float64].exponent_bias(), 1023) - # CHECK: 1023 - print(FPUtils[DType.float64].exponent_bias()) + assert_equal(FPU64.get_exponent(FPU64.set_exponent(1, 2)), 2) + assert_equal(FPU64.get_mantissa(FPU64.set_mantissa(1, 3)), 3) + assert_equal(FPU64.get_exponent(FPU64.set_exponent(-1, 4)), 4) + assert_equal(FPU64.get_mantissa(FPU64.set_mantissa(-1, 5)), 5) + assert_true(FPU64.get_sign(FPU64.set_sign(0, True))) + assert_false(FPU64.get_sign(FPU64.set_sign(0, False))) + assert_true(FPU64.get_sign(FPU64.set_sign(-0, True))) + assert_false(FPU64.get_sign(FPU64.set_sign(-0, False))) + assert_false(FPU64.get_sign(1)) + assert_true(FPU64.get_sign(-1)) + assert_false(FPU64.get_sign(FPU64.pack(False, 6, 12))) + assert_equal(FPU64.get_exponent(FPU64.pack(False, 6, 12)), 6) + assert_equal(FPU64.get_mantissa(FPU64.pack(False, 6, 12)), 12) + assert_true(FPU64.get_sign(FPU64.pack(True, 6, 12))) + assert_equal(FPU64.get_exponent(FPU64.pack(True, 6, 12)), 6) + assert_equal(FPU64.get_mantissa(FPU64.pack(True, 6, 12)), 12) - # CHECK: 2 - print(FPU64.get_exponent(FPU64.set_exponent(1, 2))) - # CHECK-NEXT: 3 - print(FPU64.get_mantissa(FPU64.set_mantissa(1, 3))) - # CHECK-NEXT: 4 - print(FPU64.get_exponent(FPU64.set_exponent(-1, 4))) - # CHECK-NEXT: 5 - print(FPU64.get_mantissa(FPU64.set_mantissa(-1, 5))) - # CHECK-NEXT: True - print(FPU64.get_sign(FPU64.set_sign(0, True))) - # CHECK-NEXT: False - print(FPU64.get_sign(FPU64.set_sign(0, False))) - # CHECK-NEXT: True - print(FPU64.get_sign(FPU64.set_sign(-0, True))) - # CHECK-NEXT: False - print(FPU64.get_sign(FPU64.set_sign(-0, False))) - # CHECK-NEXT: False - print(FPU64.get_sign(1)) - # CHECK-NEXT: True - print(FPU64.get_sign(-1)) - # CHECK-NEXT: False - print(FPU64.get_sign(FPU64.pack(False, 6, 12))) - # CHECK-NEXT: 6 - print(FPU64.get_exponent(FPU64.pack(False, 6, 12))) - # CHECK-NEXT: 12 - print(FPU64.get_mantissa(FPU64.pack(False, 6, 12))) - # CHECK-NEXT: True - print(FPU64.get_sign(FPU64.pack(True, 6, 12))) - # CHECK-NEXT: 6 - print(FPU64.get_exponent(FPU64.pack(True, 6, 12))) - # CHECK-NEXT: 12 - print(FPU64.get_mantissa(FPU64.pack(True, 6, 12))) - -fn main(): +def main(): test_numerics() From ed679366502fc309972b5823f1d340903c530f8e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 20:30:03 -0500 Subject: [PATCH 0217/2019] [External][mojo-stdlib] Implement `bool` function for `None` (#2249) (#38057) Add a `bool` overload for `None`. In Python, `bool(None)` returns `False` as is the case in this commit. Signed-off-by: zhoujing mojo-orig-commit: abf2d97e88a2cc97e3029a578d2824895a88d0c1 MODULAR_ORIG_COMMIT_REV_ID: bcc02613442c7fec33e1789aaec659680b61ed57 --- stdlib/src/builtin/bool.mojo | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 286e6cdf11..67d378e355 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -245,3 +245,29 @@ struct Bool(Stringable, CollectionElement, Boolable, EqualityComparable): `value ^ self`. """ return value ^ self + + @always_inline("nodebug") + fn __int__(self) -> Int: + """Convert this Bool to an integer. + + Returns: + 1 if the Bool is True, 0 otherwise. + """ + return Int( + __mlir_op.`pop.cast`[_type = __mlir_type.`!pop.scalar`]( + self.value + ) + ) + + +@always_inline +fn bool(value: None) -> Bool: + """Get the bool representation of the `None` type. + + Args: + value: The object to get the bool representation of. + + Returns: + The bool representation of the object. + """ + return False From 05109f64ee292fc871cbeea088f1a75e2fa8203e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 21:06:17 -0500 Subject: [PATCH 0218/2019] [External][mojo-stdlib] Replace manual refcounted `Dict` with Arc (#2231) (#38041) Rework `_RefCountedAttrsDict` to use `Arc` instead of manually maintaining a refcount using an `Atomic`. This simplifies things a bit. Part of #2030 Signed-off-by: jayzhan211 mojo-orig-commit: e98f496c3a0d3565f1d6453412d5530894f4bc1e MODULAR_ORIG_COMMIT_REV_ID: f00b27ceaaeb7e9f9f290023f25b176c2c589db6 --- stdlib/src/builtin/object.mojo | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index c14704b64f..78ff310a29 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -124,20 +124,19 @@ struct _RefCountedAttrsDict: directly with `x.attr`, the key will always be a `StringLiteral`. """ - var refcount: Atomic[DType.index] - """The number of live references to the attribute dictionary.""" - var impl: Dict[StringLiteral, _ObjectImpl] + var impl: Arc[Dict[StringLiteral, _ObjectImpl]] """The implementation of the map.""" fn __init__(inout self): - self.refcount = 1 - self.impl = Dict[StringLiteral, _ObjectImpl]() + self.impl = Arc[Dict[StringLiteral, _ObjectImpl]]( + Dict[StringLiteral, _ObjectImpl]() + ) @always_inline fn set(inout self, key: StringLiteral, value: _ObjectImpl) raises: - if key in self.impl: - self.impl[key].destroy() - self.impl[key] = value + if key in self.impl[]: + self.impl[][key].destroy() + self.impl[][key] = value return raise Error( "AttributeError: Object does not have an attribute of name '" @@ -147,7 +146,7 @@ struct _RefCountedAttrsDict: @always_inline fn get(self, key: StringLiteral) raises -> _ObjectImpl: - var iter = self.impl.find(key) + var iter = self.impl[].find(key) if iter: return iter.value() raise Error( @@ -193,23 +192,17 @@ struct _RefCountedAttrsDictRef: __get_address_as_uninit_lvalue(ptr.address) = _RefCountedAttrsDict() # Elements can only be added on construction. for i in range(len(values)): - ptr[].impl._insert(values[i].key, values[i].value._value.copy()) + ptr[].impl[]._insert(values[i].key, values[i].value._value.copy()) return Self {attrs: ptr.bitcast[Int8]()} @always_inline fn copy(self) -> Self: - _ = self.attrs.bitcast[_RefCountedAttrsDict]()[].refcount.fetch_add(1) + _ = self.attrs.bitcast[_RefCountedAttrsDict]()[].impl return Self {attrs: self.attrs} fn release(self): - var ptr = self.attrs.bitcast[_RefCountedAttrsDict]() - var prev = ptr[].refcount.fetch_sub(1) - if prev != 1: - return - - # destroy the container. - ptr.free() + var ptr = self.attrs.bitcast[_RefCountedAttrsDict]()[].impl @register_passable("trivial") @@ -622,7 +615,7 @@ struct _ObjectImpl(CollectionElement, Stringable): var ptr = self.get_obj_attrs_ptr() var res = String("{") var print_sep = False - for entry in ptr[].impl.items(): + for entry in ptr[].impl[].items(): if print_sep: res += ", " res += ( @@ -1715,7 +1708,7 @@ struct object(IntableRaising, Boolable, Stringable): _put("{") var ptr = self._value.get_obj_attrs_ptr() var k = 0 - for entry in ptr[].impl.items(): + for entry in ptr[].impl[].items(): if k != 0: _put(", ") _printf("'%s' = ", entry[].key) From 95edb69751c80a022de7f850f0da8577343157c5 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Thu, 18 Apr 2024 21:33:22 -0500 Subject: [PATCH 0219/2019] [stdlib] polish: Make unsafe List constructor require named arguments (#38121) Per discussion at [Internal link] This PR makes only the `size` and `capacity` arguments named, but I wonder if we wouldn't want to go all the way and make the `data` (renamed perhaps to `ptr`) require a name as well, e.g.: ```mojo var list = List(ptr=alloc, size=5, capacity=10) ``` IMO this makes it that much clearer that this is taking ownership of a pointer. I tend to think that "unsafe" operations like this should have extra boilerplate around them that makes it very clear something nuanced is happening that may require extra scrutiny. Thoughts @abduld @JoeLoser @rparolin? MODULAR_ORIG_COMMIT_REV_ID: 213c1153a106fa92f4bf5fb5bf5a745cd678e6ac --- stdlib/src/collections/list.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index f269055403..0fa5eef576 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -125,7 +125,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): self.append(value[]) fn __init__( - inout self: Self, data: UnsafePointer[T], size: Int, capacity: Int + inout self: Self, data: UnsafePointer[T], *, size: Int, capacity: Int ): """Constructs a list from a pointer, its size, and its capacity. From 7176a810ea1ee57d2a953910add6c2134ba9c088 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 22:02:40 -0500 Subject: [PATCH 0220/2019] [External][mojo-stdlib] Add list.insert(index, value) API to stdlib (#2148) (#38047) Add `List.insert(index, value)` API as requested in https://github.com/modularml/mojo/issues/2134. This API should handle the same functionality as the Python `list.insert` and adds tests for the same. Fixes https://github.com/modularml/mojo/issues/2134 Signed-off-by: Dhaval Kumar mojo-orig-commit: 98e062dc1b70eaea349f6561ca14987d91d569a1 MODULAR_ORIG_COMMIT_REV_ID: 5128d9ad94ee7acb9b876d3738e7ce47405afdab --- stdlib/src/collections/list.mojo | 30 ++++++++++++ stdlib/test/collections/test_list.mojo | 63 ++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 0fa5eef576..a4924fcd3b 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -206,6 +206,36 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): initialize_pointee(self.data + self.size, value^) self.size += 1 + @always_inline + fn insert(inout self, i: Int, owned value: T): + """Inserts a value to the list at the given index. + `a.insert(len(a), value)` is equivalent to `a.append(value)`. + + Args: + i: The index for the value. + value: The value to insert. + """ + debug_assert(i <= self.size, "insert index out of range") + + var normalized_idx = i + if i < 0: + normalized_idx = _max(0, len(self) + i) + + var earlier_idx = len(self) + var later_idx = len(self) - 1 + self.append(value^) + + for _ in range(normalized_idx, len(self) - 1): + var earlier_ptr = self.data + earlier_idx + var later_ptr = self.data + later_idx + + var tmp = move_from_pointee(earlier_ptr) + move_pointee(src=later_ptr, dst=earlier_ptr) + initialize_pointee(later_ptr, tmp^) + + earlier_idx -= 1 + later_idx -= 1 + @always_inline fn extend(inout self, owned other: List[T]): """Extends this list by consuming the elements of `other`. diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index 32717402a6..adfa329823 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -287,6 +287,68 @@ def test_list_reverse_move_count(): _ = vec^ +def test_list_insert(): + # + # Test the list [1, 2, 3] created with insert + # + + v1 = List[Int]() + v1.insert(len(v1), 1) + v1.insert(len(v1), 3) + v1.insert(1, 2) + + assert_equal(len(v1), 3) + assert_equal(v1[0], 1) + assert_equal(v1[1], 2) + assert_equal(v1[2], 3) + + # + # Test the list [1, 2, 3, 4, 5] created with negative and positive index + # + + v2 = List[Int]() + v2.insert(-1729, 2) + v2.insert(len(v2), 3) + v2.insert(len(v2), 5) + v2.insert(-1, 4) + v2.insert(-len(v2), 1) + + assert_equal(len(v2), 5) + assert_equal(v2[0], 1) + assert_equal(v2[1], 2) + assert_equal(v2[2], 3) + assert_equal(v2[3], 4) + assert_equal(v2[4], 5) + + # + # Test the list [1, 2, 3, 4] created with negative index + # + + v3 = List[Int]() + v3.insert(-11, 4) + v3.insert(-13, 3) + v3.insert(-17, 2) + v3.insert(-19, 1) + + assert_equal(len(v3), 4) + assert_equal(v3[0], 1) + assert_equal(v3[1], 2) + assert_equal(v3[2], 3) + assert_equal(v3[3], 4) + + # + # Test the list [1, 2, 3, 4, 5, 6, 7, 8] created with insert + # + + v4 = List[Int]() + for i in range(4): + v4.insert(0, 4 - i) + v4.insert(len(v4), 4 + i + 1) + + for i in range(len(v4)): + assert_equal(v4[i], i + 1) + + def test_list_extend(): # # Test extending the list [1, 2, 3] with itself @@ -524,6 +586,7 @@ def main(): test_list_resize() test_list_reverse() test_list_reverse_move_count() + test_list_insert() test_list_extend() test_list_extend_non_trivial() test_list_explicit_copy() From f2591ad7e63645b58733dd5905e1fcc8520c1ba9 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 22:05:10 -0500 Subject: [PATCH 0221/2019] [External][Examples] Fix typo in Mandelbrot example notebook (#2227) (#38050) Signed-off-by: Kern Handa mojo-orig-commit: d70092cdc773cbaa95b72d7b362af44e8fbe5f79 MODULAR_ORIG_COMMIT_REV_ID: b8f1274cfff45efca0fc19651256e3543468728c --- examples/notebooks/Mandelbrot.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 4e7e979b3e..03c4850603 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -301,7 +301,7 @@ " @__copy_capture(scale_x, scale_y)\n", " @parameter\n", " fn compute_vector[simd_width: Int](col: Int):\n", - " \"\"\"Each time we oeprate on a `simd_width` vector of pixels.\"\"\"\n", + " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", @@ -378,7 +378,7 @@ " @__copy_capture(scale_x, scale_y)\n", " @parameter\n", " fn compute_vector[simd_width: Int](col: Int):\n", - " \"\"\"Each time we oeprate on a `simd_width` vector of pixels.\"\"\"\n", + " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", @@ -432,7 +432,7 @@ " @__copy_capture(scale_x, scale_y)\n", " @parameter\n", " fn compute_vector[simd_width: Int](col: Int):\n", - " \"\"\"Each time we oeprate on a `simd_width` vector of pixels.\"\"\"\n", + " \"\"\"Each time we operate on a `simd_width` vector of pixels.\"\"\"\n", " var cx = min_x + (col + iota[float_type, simd_width]()) * scale_x\n", " var cy = min_y + row * scale_y\n", " var c = ComplexSIMD[float_type, simd_width](cx, cy)\n", From 1f937c8e79f3eee9200d8f154566de258726fe7e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 22:06:09 -0500 Subject: [PATCH 0222/2019] [External][docs] Update debugging.ipynb to correct link (#2213) (#38052) Signed-off-by: Kern Handa mojo-orig-commit: 7b1fa7f6aa8cdc339818639d93e55b404c084423 MODULAR_ORIG_COMMIT_REV_ID: dc6ad1a4b75a1db9ed126d1487bea4863d16f043 --- docs/tools/debugging.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 8053940c72..3e7fa882b8 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -48,7 +48,7 @@ "\n", "If you're already familiar with debugging in VS Code, the\n", "material in this section will mostly be review. You might want to skip ahead to\n", - "[Mojo launch configurations](#mojo-launch-configurations-launch-configurations)\n", + "[Mojo launch configurations](#mojo-launch-configurations)\n", "or see [Using the debugger](#using-the-debugger) for notes on the features\n", "supported in the Mojo debugger. \n", "\n", From 7c751a78195804c7f6e930c1879daaa7aeb72003 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 22:06:54 -0500 Subject: [PATCH 0223/2019] [External][docs] Updates parameters doc with typo fix. (#2211) (#38053) Missed a word in a paragraph about bound and unbound parameters. Signed-off-by: Kern Handa mojo-orig-commit: 07997ae477bd5da31383a803db3797b81131a1c9 MODULAR_ORIG_COMMIT_REV_ID: 0ff923076578cacf40f73b8d7d7bcb42f11d7fe7 --- docs/manual/parameters/index.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/parameters/index.ipynb b/docs/manual/parameters/index.ipynb index f9c16a8b88..2005e81c02 100644 --- a/docs/manual/parameters/index.ipynb +++ b/docs/manual/parameters/index.ipynb @@ -1261,7 +1261,7 @@ "source": [ "The `eat()` function takes a `Fudge` struct with the first parameter (`sugar`)\n", "bound to the value 5. The second and third parameters, `cream` and `chocolate`\n", - "are.\n", + "are unbound.\n", "\n", "The unbound `cream` and `chocolate` parameters become implicit input parameters\n", "on the `eat` function. In practice, this is roughly equivalent to writing:" From 44856edace39f15525a86f1f6cf2399849c48d77 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 22:09:12 -0500 Subject: [PATCH 0224/2019] [External][mojo-stdlib] Implement Corresponding Regular Methods for 4 Overloaded `Set` Operators (#2214) (#38056) * # [mojo-stdlib] Implement Corresponding Regular Methods for 4 Overloaded `Set` Operators This pull request ensures that, alongside the existing functionality of overloaded operators for `Set`, their corresponding regular methods are also fully implemented as they are in Python. The following methods have been introduced to correspond with their respective operators: - `__sub__` (-): Now corresponds to `Set.difference()` - `__isub__` (-=): Now corresponds to `Set.difference_update()` - `__iand__` (&=): Now corresponds to `Set.intersection_update()` - `__ior__` (|=): Now corresponds to `Set.update()` Previously, these magic methods lacked their equivalent regular methods. This update rectifies that by ensuring that calls to the magic methods are properly linked to their corresponding regular methods. No changes are needed for the `test_set.mojo` file, as this update merely relocates the existing code for the overloaded operators to their respective regular functions without adding any new functionality. Signed-off-by: Arvin Davoudi mojo-orig-commit: 1df0439305ccb6f9c874dc808d187b71902b7737 MODULAR_ORIG_COMMIT_REV_ID: a5f301c58e2029bf4608d5fe239482931bf510d2 --- stdlib/src/collections/set.mojo | 69 +++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/stdlib/src/collections/set.mojo b/stdlib/src/collections/set.mojo index 222d10e7a2..3dcdca60ba 100644 --- a/stdlib/src/collections/set.mojo +++ b/stdlib/src/collections/set.mojo @@ -171,9 +171,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): Args: other: Another Set instance to intersect with this one. """ - # Possible to do this without an extra allocation, but need to be - # careful about concurrent iteration + mutation - self.remove_all(self - other) + self.intersection_update(other) fn __or__(self, other: Self) -> Self: """The set union operator. @@ -191,13 +189,12 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): """In-place set union. Updates the set to contain all elements in the `other` set - as well as all elements it already contained. + as well as keeping all elements it already contained. Args: other: Another Set instance to union with this one. """ - for e in other: - self.add(e[]) + self.update(other) fn __sub__(self, other: Self) -> Self: """Set subtraction. @@ -209,11 +206,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): A new set containing elements of this set, but not containing any elements which were in the `other` set. """ - var result = Set[T]() - for e in self: - if e[] not in other: - result.add(e[]) - return result^ + return self.difference(other) fn __isub__(inout self, other: Self): """In-place set subtraction. @@ -223,7 +216,7 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): Args: other: Another Set instance to subtract from this one. """ - self.remove_all(other) + self.difference_update(other) fn __iter__[ mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type @@ -320,6 +313,58 @@ struct Set[T: KeyElement](Sized, EqualityComparable, Hashable, Boolable): return result^ + fn difference(self, other: Self) -> Self: + """Set difference. + + Args: + other: Another Set instance to find the difference with this one. + + Returns: + A new set containing elements that are in this set but not in + the `other` set. + """ + var result = Set[T]() + for e in self: + if e[] not in other: + result.add(e[]) + return result^ + + fn update(inout self, other: Self): + """In-place set update. + + Updates the set to contain all elements in the `other` set + as well as keeping all elements it already contained. + + Args: + other: Another Set instance to union with this one. + """ + for e in other: + self.add(e[]) + + fn difference_update(inout self, other: Self): + """In-place set difference update. + + Updates the set by removing all elements found in the `other` set, + effectively keeping only elements that are unique to this set. + + Args: + other: Another Set instance to compare with this one. + """ + self.remove_all(other) + + fn intersection_update(inout self, other: Self): + """In-place set intersection update. + + Updates the set by retaining only elements found in both this set and the `other` set, + removing all other elements. The result is the intersection of this set with `other`. + + Args: + other: Another Set instance to intersect with this one. + """ + # Possible to do this without an extra allocation, but need to be + # careful about concurrent iteration + mutation + self.remove_all(self - other) + fn remove_all(inout self, other: Self): """In-place set subtraction. From ead0ebe6b559bbe93f84ac0b2ffee13b7df29782 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 22:28:59 -0500 Subject: [PATCH 0225/2019] [External][stdlib] Add `__rmod__` for SIMD types (#2186) (#38049) Add `__rmod__` for `SIMD` to allow expressions with an `Int` on the left hand side. Fixes #1482 Signed-off-by: bgreni mojo-orig-commit: a78e5213294d2cd595cf0db0da10113dcbdc22cf MODULAR_ORIG_COMMIT_REV_ID: d0b03db2fe241bb743a95dc43b7d3e203a93cb5d --- stdlib/src/builtin/simd.mojo | 13 +++++++++++++ stdlib/test/builtin/test_simd.mojo | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index f193f86b98..c230c4a781 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -680,6 +680,19 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( var mask = ((rhs < 0) ^ (self < 0)) & (mod != 0) return mod + mask.select(rhs, Self(0)) + @always_inline("nodebug") + fn __rmod__(self, value: Self) -> Self: + """Returns `value mod self`. + + Args: + value: The other value. + + Returns: + `value mod self`. + """ + constrained[type.is_numeric(), "the type must be numeric"]() + return value % self + @always_inline("nodebug") fn __pow__(self, rhs: Int) -> Self: """Computes the vector raised to the power of the input integer value. diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index ebd4250a09..d2f9519308 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -131,6 +131,14 @@ def test_mod(): assert_equal(UInt32(99) % UInt32(1), 0) assert_equal(UInt32(99) % UInt32(3), 0) + assert_equal(Int(4) % Int32(3), 1) + assert_equal( + Int(78) % SIMD[DType.int32, 2](78, 78), SIMD[DType.int32, 2](0, 0) + ) + assert_equal( + SIMD[DType.int32, 2](7, 7) % Int(4), SIMD[DType.int32, 2](3, 3) + ) + var a = SIMD[DType.float32, 16]( 3.1, 3.1, From 4d7bcd02fdcc357a740857ea568df870b7c8f3d0 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 23:03:04 -0500 Subject: [PATCH 0226/2019] =?UTF-8?q?[External][stdlib]=20Update=20docs=20?= =?UTF-8?q?and=20code=20to=20use=20`from=20=20import=20`?= =?UTF-8?q?=20=E2=80=A6=20(#38059)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …instead of `from . import ` new (#2219) Update docs and code to use `from import ` instead of `from . import `. This is desirable so we (and users) become less coupled to the exact file a entity lives at within a module. It gives more freedom to standard library developers without breaking users. Signed-off-by: Farhan Ali Raza mojo-orig-commit: 63d5cb0e5fd1b90fb4da5c8e4f5974fbc04c8633 MODULAR_ORIG_COMMIT_REV_ID: 6162dfb2275f3809f2c7a605590cbf08536dfd34 --- docs/manual/python/types.ipynb | 4 ++-- docs/upgrade-guide.md | 2 +- examples/deviceinfo.mojo | 4 ++++ examples/hello_interop.mojo | 2 +- examples/mandelbrot.mojo | 4 ++-- examples/notebooks/Mandelbrot.ipynb | 4 ++-- examples/notebooks/RayTracing.ipynb | 2 +- examples/notebooks/programming-manual.ipynb | 4 ++-- stdlib/src/base64/base64.mojo | 2 +- stdlib/src/builtin/builtin_list.mojo | 4 ++-- stdlib/src/builtin/coroutine.mojo | 4 ++-- stdlib/src/builtin/debug_assert.mojo | 3 +-- stdlib/src/builtin/dtype.mojo | 6 +++--- stdlib/src/builtin/error.mojo | 6 +++--- stdlib/src/builtin/file.mojo | 3 +-- stdlib/src/builtin/int.mojo | 4 ++-- stdlib/src/builtin/io.mojo | 5 ++--- stdlib/src/builtin/object.mojo | 9 ++++----- stdlib/src/builtin/range.mojo | 2 +- stdlib/src/builtin/simd.mojo | 8 ++++---- stdlib/src/builtin/string.mojo | 18 ++++++------------ stdlib/src/builtin/string_literal.mojo | 2 +- stdlib/src/builtin/tuple.mojo | 2 ++ stdlib/src/collections/dict.mojo | 2 +- stdlib/src/collections/list.mojo | 5 +++-- stdlib/src/collections/optional.mojo | 6 +++--- stdlib/src/collections/vector.mojo | 4 ++-- stdlib/src/memory/__init__.mojo | 7 +++++-- stdlib/src/memory/_arc.mojo | 3 +-- stdlib/src/memory/memory.mojo | 3 +-- stdlib/src/memory/unsafe.mojo | 5 +++-- stdlib/src/memory/unsafe_pointer.mojo | 4 ++-- stdlib/src/os/_linux_aarch64.mojo | 2 +- stdlib/src/os/_macos.mojo | 2 +- stdlib/src/os/_windows.mojo | 3 +-- stdlib/src/os/atomic.mojo | 4 ++-- stdlib/src/os/env.mojo | 5 ++--- stdlib/src/os/fstat.mojo | 2 +- stdlib/src/os/os.mojo | 7 +++++-- stdlib/src/os/path/path.mojo | 2 +- stdlib/src/pathlib/path.mojo | 2 +- stdlib/src/python/_cpython.mojo | 5 ++--- stdlib/src/python/object.mojo | 2 +- stdlib/src/python/python.mojo | 5 ++--- stdlib/src/random/random.mojo | 5 ++--- stdlib/src/sys/arg.mojo | 2 +- stdlib/src/sys/ffi.mojo | 2 +- stdlib/src/sys/info.mojo | 2 +- stdlib/src/sys/intrinsics.mojo | 7 +++---- stdlib/src/sys/param_env.mojo | 4 ++-- stdlib/src/time/time.mojo | 5 ++--- stdlib/src/utils/_numerics.mojo | 6 ++---- stdlib/src/utils/_serialize.mojo | 3 +-- stdlib/src/utils/index.mojo | 2 +- stdlib/src/utils/inlined_string.mojo | 5 ++--- stdlib/src/utils/loop.mojo | 2 +- stdlib/src/utils/static_tuple.mojo | 6 +++--- stdlib/src/utils/stringref.mojo | 3 +-- stdlib/src/utils/variant.mojo | 17 ++++++++++------- stdlib/test/builtin/test_bfloat16.mojo | 2 +- stdlib/test/builtin/test_file.mojo | 3 +-- stdlib/test/builtin/test_print.mojo | 5 ++--- stdlib/test/builtin/test_simd.mojo | 2 +- stdlib/test/collections/test_dict.mojo | 5 +++-- stdlib/test/collections/test_optional.mojo | 2 +- stdlib/test/collections/test_set.mojo | 2 +- stdlib/test/memory/test_bitcast.mojo | 1 - stdlib/test/memory/test_memory.mojo | 7 +++---- stdlib/test/memory/test_unsafepointer.mojo | 3 ++- stdlib/test/os/path/test_islink.mojo | 2 +- stdlib/test/os/test_atomic.mojo | 2 +- stdlib/test/pathlib/test_pathlib.mojo | 2 +- stdlib/test/python/test_ownership.mojo | 7 +++---- .../python/test_python_error_handling.mojo | 5 ++--- stdlib/test/python/test_python_interop.mojo | 5 ++--- stdlib/test/python/test_python_object.mojo | 5 ++--- .../python/test_python_object_len_raises.mojo | 3 +-- stdlib/test/python/test_python_to_mojo.mojo | 4 ++-- stdlib/test/sys/test_aarch64_target.mojo | 2 +- stdlib/test/sys/test_has_intel_amx.mojo | 1 + stdlib/test/sys/test_intrinsics.mojo | 4 ++-- stdlib/test/sys/test_linux_target.mojo | 2 +- stdlib/test/sys/test_macos_target.mojo | 2 +- stdlib/test/sys/test_windows_target.mojo | 3 +-- stdlib/test/time/test_time.mojo | 2 +- stdlib/test/utils/test_inlined_string.mojo | 3 ++- stdlib/test/utils/test_tuple.mojo | 3 +-- stdlib/test/utils/test_unroll.mojo | 3 +-- stdlib/test/utils/test_variant.mojo | 4 ++-- 89 files changed, 168 insertions(+), 183 deletions(-) diff --git a/docs/manual/python/types.ipynb b/docs/manual/python/types.ipynb index 23612ebe59..f0058d913a 100644 --- a/docs/manual/python/types.ipynb +++ b/docs/manual/python/types.ipynb @@ -145,7 +145,7 @@ "metadata": {}, "outputs": [], "source": [ - "from python.object import PythonObject\n", + "from python import PythonObject\n", "\n", "var py_list: PythonObject = [1, 2, 3, 4]" ] @@ -255,7 +255,7 @@ "source": [ "fn python_types() raises:\n", " from python import Python\n", - " from python.object import PythonObject\n", + " from python import PythonObject\n", "\n", " var value1: PythonObject = 3.7\n", " var value2 = Python.evaluate(\"10/3\")\n", diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index 9a82c8cba5..613652d45c 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -118,7 +118,7 @@ was unsafe with heap allocated objects, it now returns a reference. If you had code that looks like this: ```mojo -from utils.variant import Variant +from utils import Variant fn foo(variant: Variant[String, Int]): if variant.isa[String](): diff --git a/examples/deviceinfo.mojo b/examples/deviceinfo.mojo index e81486c29e..726d664d6f 100644 --- a/examples/deviceinfo.mojo +++ b/examples/deviceinfo.mojo @@ -18,6 +18,10 @@ from sys.info import ( _current_cpu, _current_target, _triple_attr, +) + + +from sys import ( os_is_linux, os_is_macos, os_is_windows, diff --git a/examples/hello_interop.mojo b/examples/hello_interop.mojo index cc8bf40bf2..9dfbb1c1e7 100644 --- a/examples/hello_interop.mojo +++ b/examples/hello_interop.mojo @@ -15,7 +15,7 @@ # range() and print() functions available in the standard library. # It also demonstrates Python interop by importing the simple_interop.py file. -from python.python import Python +from python import Python def main(): diff --git a/examples/mandelbrot.mojo b/examples/mandelbrot.mojo index 5b70f70ad7..6e8dedc439 100644 --- a/examples/mandelbrot.mojo +++ b/examples/mandelbrot.mojo @@ -14,7 +14,7 @@ # RUN: %mojo %s | FileCheck %s from math import iota -from sys.info import num_logical_cores +from sys import num_logical_cores import benchmark from algorithm import parallelize, vectorize @@ -23,7 +23,7 @@ from python import Python from runtime.llcl import Runtime from tensor import Tensor -from utils.index import Index +from utils import Index alias float_type = DType.float64 alias int_type = DType.int64 diff --git a/examples/notebooks/Mandelbrot.ipynb b/examples/notebooks/Mandelbrot.ipynb index 03c4850603..fd7f508d29 100644 --- a/examples/notebooks/Mandelbrot.ipynb +++ b/examples/notebooks/Mandelbrot.ipynb @@ -51,10 +51,10 @@ "from complex import ComplexSIMD, ComplexFloat64\n", "from math import iota\n", "from python import Python\n", - "from sys.info import num_physical_cores\n", + "from sys import num_physical_cores\n", "from algorithm import parallelize, vectorize\n", "from tensor import Tensor\n", - "from utils.index import Index\n", + "from utils import Index\n", "\n", "alias float_type = DType.float64\n", "alias simd_width = 2 * simdwidthof[float_type]()" diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index c50e9452c2..9d1ddf9596 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -183,7 +183,7 @@ "outputs": [], "source": [ "from python import Python\n", - "from python.object import PythonObject\n", + "from python import PythonObject\n", "\n", "struct Image:\n", " # reference count used to make the object efficiently copyable\n", diff --git a/examples/notebooks/programming-manual.ipynb b/examples/notebooks/programming-manual.ipynb index 0dfb58a02f..ba8f6abe01 100644 --- a/examples/notebooks/programming-manual.ipynb +++ b/examples/notebooks/programming-manual.ipynb @@ -537,7 +537,7 @@ "metadata": {}, "outputs": [], "source": [ - "from memory.unsafe import Pointer\n", + "from memory import Pointer\n", "\n", "struct HeapArray:\n", " var data: Pointer[Int]\n", @@ -1363,7 +1363,7 @@ "outputs": [], "source": [ "from python import Python\n", - "from python.object import PythonObject\n", + "from python import PythonObject\n", "\n", "fn use_dict() raises:\n", " var dictionary = Python.dict()\n", diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index 6bb1da0cc4..d7b2f02e64 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -20,7 +20,7 @@ from base64 import b64encode """ from collections import List -from sys.info import simdwidthof +from sys import simdwidthof # ===----------------------------------------------------------------------===# # b64encode diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 0c0daa9714..c51dc86df4 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,8 +15,8 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory.reference import Reference -from memory.unsafe_pointer import * +from memory import Reference, UnsafePointer +from memory.unsafe_pointer import destroy_pointee # ===----------------------------------------------------------------------===# # ListLiteral diff --git a/stdlib/src/builtin/coroutine.mojo b/stdlib/src/builtin/coroutine.mojo index 9023ca82b7..754c77a408 100644 --- a/stdlib/src/builtin/coroutine.mojo +++ b/stdlib/src/builtin/coroutine.mojo @@ -15,9 +15,9 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys.info import sizeof +from sys import sizeof -from memory.unsafe import Pointer +from memory import Pointer # ===----------------------------------------------------------------------=== # # _CoroutineContext diff --git a/stdlib/src/builtin/debug_assert.mojo b/stdlib/src/builtin/debug_assert.mojo index 454bf42c64..f314afb412 100644 --- a/stdlib/src/builtin/debug_assert.mojo +++ b/stdlib/src/builtin/debug_assert.mojo @@ -18,8 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from os import abort from sys._build import is_kernels_debug_build -from sys.info import triple_is_nvidia_cuda -from sys.param_env import is_defined +from sys import triple_is_nvidia_cuda, is_defined fn debug_assert(cond: Bool, msg: StringLiteral): diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 91ff69246e..816ff855a5 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -15,10 +15,10 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections.dict import KeyElement -from sys.info import sizeof as _sizeof +from collections import KeyElement +from sys import sizeof as _sizeof -from utils.loop import unroll +from utils import unroll alias _mIsSigned = UInt8(1) alias _mIsInteger = UInt8(1 << 7) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index 341f68494a..aa89870ac5 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -15,10 +15,10 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys.info import alignof, sizeof +from sys import alignof, sizeof -from memory.memory import _free, memcmp, memcpy -from memory.unsafe import DTypePointer +from memory.memory import _free +from memory import memcmp, memcpy, DTypePointer # ===----------------------------------------------------------------------===# # Error diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 23a40d80a3..810c859460 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -34,8 +34,7 @@ with open("my_file.txt", "r") as f: from os import PathLike from sys import external_call -from memory.reference import AddressSpace -from memory.unsafe import DTypePointer +from memory import AddressSpace, DTypePointer, Pointer @register_passable diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b20804e30e..b93187e917 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -15,14 +15,14 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections.dict import KeyElement +from collections import KeyElement from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf from utils._visualizers import lldb_formatter_wrapping_type -from utils.index import StaticIntTuple +from utils import StaticIntTuple from utils._format import Formattable, Formatter from utils.inlined_string import _ArrayMem diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index e65d5bbdd2..3f66b01ace 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -15,12 +15,11 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys import external_call -from sys.info import bitwidthof, os_is_windows, triple_is_nvidia_cuda +from sys import bitwidthof, os_is_windows, triple_is_nvidia_cuda, external_call from builtin.dtype import _get_dtype_printf_format from builtin.builtin_list import _LITRefPackHelper -from memory.unsafe import Pointer +from memory import Pointer from utils import StringRef, unroll from utils._format import Formattable, Formatter, write_to diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 78ff310a29..472aacde19 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -16,15 +16,14 @@ These are Mojo built-ins, so you don't need to import them. """ from collections import Dict, List -from os.atomic import Atomic + +from os import Atomic from sys.intrinsics import _type_is_eq -from memory import memcmp, memcpy -from memory.unsafe import DTypePointer, LegacyPointer +from memory import memcmp, memcpy, DTypePointer, LegacyPointer, UnsafePointer from memory._arc import Arc -from utils import StringRef -from utils.loop import unroll +from utils import StringRef, unroll from .io import _printf, _put diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 4cb1e065d8..426442b02a 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -16,7 +16,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from python.object import PythonObject +from python import PythonObject # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index c230c4a781..862d83e444 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -15,17 +15,17 @@ These are Mojo built-ins, so you don't need to import them. """ -from sys import llvm_intrinsic, _RegisterPackType -from sys.info import has_neon, is_x86, simdwidthof + +from sys import llvm_intrinsic, has_neon, is_x86, simdwidthof, _RegisterPackType from builtin.hash import _hash_simd -from memory.unsafe import bitcast +from memory import bitcast from utils._numerics import FPUtils from utils._numerics import isnan as _isnan from utils._numerics import nan as _nan from utils._visualizers import lldb_formatter_wrapping_type -from utils.static_tuple import StaticTuple +from utils import StaticTuple from .dtype import _integral_type_of from .io import _snprintf_scalar, _snprintf diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index fda1690764..57bb9a5983 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -15,18 +15,12 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import List -from collections.dict import KeyElement -from sys import llvm_intrinsic -from sys.info import bitwidthof - -from memory.unsafe_pointer import UnsafePointer -from memory.memory import memcmp, memcpy -from memory.unsafe import DTypePointer, Pointer - -from utils import StringRef -from utils.index import StaticIntTuple -from utils.static_tuple import StaticTuple +from collections import List, KeyElement +from sys import llvm_intrinsic, bitwidthof + +from memory import UnsafePointer, DTypePointer, Pointer, memcmp, memcpy + +from utils import StringRef, StaticIntTuple, StaticTuple from utils._format import Formattable, Formatter, ToFormatter from .io import _snprintf diff --git a/stdlib/src/builtin/string_literal.mojo b/stdlib/src/builtin/string_literal.mojo index 5970ea4105..4edbee452b 100644 --- a/stdlib/src/builtin/string_literal.mojo +++ b/stdlib/src/builtin/string_literal.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory.unsafe import DTypePointer +from memory import DTypePointer from utils import StringRef from utils._visualizers import lldb_formatter_wrapping_type diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 2092f2df27..1c2c9d3451 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -17,6 +17,8 @@ These are Mojo built-ins, so you don't need to import them. from utils._visualizers import lldb_formatter_wrapping_type +from memory.unsafe_pointer import initialize_pointee + # ===----------------------------------------------------------------------===# # Tuple # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index d9f4ed6eff..e0dc8ca8f1 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -31,7 +31,7 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ -from memory.unsafe_pointer import UnsafePointer +from memory import UnsafePointer from .optional import Optional diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index a4924fcd3b..5260dc7c6a 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,8 +20,9 @@ from collections import List """ -from memory.unsafe_pointer import * -from memory.unsafe import Reference +from memory import UnsafePointer, Reference +from memory.unsafe_pointer import move_pointee, move_from_pointee + # ===----------------------------------------------------------------------===# # Utilties diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index a46bf8951a..201e6d0ece 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -17,7 +17,7 @@ Your value can take on a value or `None`, and you need to check and explicitly extract the value to get it out. ```mojo -from collections.optional import Optional +from collections import Optional var a = Optional(1) var b = Optional[Int](None) if a: @@ -31,7 +31,7 @@ print(d) # prints 2 ``` """ -from utils.variant import Variant +from utils import Variant # TODO(27780): NoneType can't currently conform to traits @@ -57,7 +57,7 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): copy/move for Optional and allow it to be used in collections itself. ```mojo - from collections.optional import Optional + from collections import Optional var a = Optional(1) var b = Optional[Int](None) if a: diff --git a/stdlib/src/collections/vector.mojo b/stdlib/src/collections/vector.mojo index 2f01e7f4a6..51a9ff9632 100644 --- a/stdlib/src/collections/vector.mojo +++ b/stdlib/src/collections/vector.mojo @@ -19,9 +19,9 @@ from collections.vector import InlinedFixedVector ``` """ -from memory.unsafe import Pointer, Reference +from memory import Pointer, Reference -from utils.static_tuple import StaticTuple +from utils import StaticTuple # ===----------------------------------------------------------------------===# # _VecIter diff --git a/stdlib/src/memory/__init__.mojo b/stdlib/src/memory/__init__.mojo index 2222a998b6..c8e7b9da9d 100644 --- a/stdlib/src/memory/__init__.mojo +++ b/stdlib/src/memory/__init__.mojo @@ -26,11 +26,14 @@ from .unsafe_pointer import ( from .unsafe import ( bitcast, - AddressSpace, - Reference, Pointer, LegacyPointer, DTypePointer, ) +from .reference import ( + Reference, + AddressSpace, +) + # TODO: consider making Arc public and import it here diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 4d6bd0d334..5bfc0dba15 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -23,8 +23,7 @@ print(3 == p.get()) ``` """ -from memory.unsafe_pointer import UnsafePointer -from memory.memory import stack_allocation +from memory import UnsafePointer, stack_allocation struct _ArcInner[T: Movable]: diff --git a/stdlib/src/memory/memory.mojo b/stdlib/src/memory/memory.mojo index 5ec78668b2..adfc2a4c1e 100644 --- a/stdlib/src/memory/memory.mojo +++ b/stdlib/src/memory/memory.mojo @@ -20,8 +20,7 @@ from memory import memcmp """ -from sys import llvm_intrinsic -from sys.info import sizeof, triple_is_nvidia_cuda +from sys import llvm_intrinsic, sizeof, triple_is_nvidia_cuda from builtin.dtype import _integral_type_of from memory.reference import AddressSpace, _GPUAddressSpace diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index da84d5e6e0..203d255c3f 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -15,12 +15,12 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory.unsafe import Pointer +from memory import Pointer ``` """ -from sys.info import ( +from sys import ( alignof, bitwidthof, simdwidthof, @@ -32,6 +32,7 @@ from sys.intrinsics import prefetch as _prefetch from sys.intrinsics import gather, scatter, strided_load, strided_store from .memory import _free, _malloc +from .reference import AddressSpace # ===----------------------------------------------------------------------===# # Utilities diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 5399f52672..c240f764d6 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -15,11 +15,11 @@ You can import these APIs from the `memory` package. For example: ```mojo -from memory.unsafe_pointer import UnsafePointer +from memory import UnsafePointer ``` """ -from sys.info import alignof, sizeof +from sys import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq from memory.memory import _free, _malloc diff --git a/stdlib/src/os/_linux_aarch64.mojo b/stdlib/src/os/_linux_aarch64.mojo index a7cedfdc86..d59f9f4876 100644 --- a/stdlib/src/os/_linux_aarch64.mojo +++ b/stdlib/src/os/_linux_aarch64.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils.index import StaticIntTuple +from utils import StaticIntTuple from .fstat import stat_result diff --git a/stdlib/src/os/_macos.mojo b/stdlib/src/os/_macos.mojo index 14435036da..242863b263 100644 --- a/stdlib/src/os/_macos.mojo +++ b/stdlib/src/os/_macos.mojo @@ -13,7 +13,7 @@ from time.time import _CTimeSpec -from utils.index import StaticIntTuple +from utils import StaticIntTuple from .fstat import stat_result diff --git a/stdlib/src/os/_windows.mojo b/stdlib/src/os/_windows.mojo index e542348df8..d993812cdf 100644 --- a/stdlib/src/os/_windows.mojo +++ b/stdlib/src/os/_windows.mojo @@ -12,8 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements support functions for working with Windows.""" -from sys import external_call -from sys.info import os_is_windows +from sys import external_call, os_is_windows @always_inline diff --git a/stdlib/src/os/atomic.mojo b/stdlib/src/os/atomic.mojo index 0e39d9dcfa..57cc2a57d4 100644 --- a/stdlib/src/os/atomic.mojo +++ b/stdlib/src/os/atomic.mojo @@ -15,12 +15,12 @@ You can import these APIs from the `os` package. For example: ```mojo -from os.atomic import Atomic +from os import Atomic ``` """ from builtin.dtype import _integral_type_of -from memory.unsafe import Pointer, bitcast +from memory import Pointer, bitcast struct Atomic[type: DType]: diff --git a/stdlib/src/os/env.mojo b/stdlib/src/os/env.mojo index f3ef050167..600df6db0e 100644 --- a/stdlib/src/os/env.mojo +++ b/stdlib/src/os/env.mojo @@ -19,10 +19,9 @@ from os import setenv ``` """ -from sys import external_call -from sys.info import os_is_linux, os_is_macos +from sys import external_call, os_is_linux, os_is_macos -from memory.unsafe import DTypePointer +from memory import DTypePointer from utils import StringRef diff --git a/stdlib/src/os/fstat.mojo b/stdlib/src/os/fstat.mojo index 57a7c013ec..655d60fc8f 100644 --- a/stdlib/src/os/fstat.mojo +++ b/stdlib/src/os/fstat.mojo @@ -19,7 +19,7 @@ from os import stat ``` """ -from sys.info import has_neon, os_is_linux, os_is_macos, os_is_windows +from sys import has_neon, os_is_linux, os_is_macos, os_is_windows from time.time import _CTimeSpec from . import PathLike diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 834241fd74..0462a2b778 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -20,9 +20,12 @@ from os import listdir """ from collections import List -from sys.info import os_is_linux, os_is_windows, triple_is_nvidia_cuda +from sys import os_is_linux, os_is_windows, triple_is_nvidia_cuda -from memory.unsafe import DTypePointer, Pointer +from memory import ( + DTypePointer, + Pointer, +) from memory.unsafe_pointer import move_from_pointee from utils import StringRef diff --git a/stdlib/src/os/path/path.mojo b/stdlib/src/os/path/path.mojo index 213fcad134..7d9e030d81 100644 --- a/stdlib/src/os/path/path.mojo +++ b/stdlib/src/os/path/path.mojo @@ -20,7 +20,7 @@ from os.path import isdir """ from stat import S_ISDIR, S_ISLNK, S_ISREG -from sys.info import has_neon, os_is_linux, os_is_macos, os_is_windows +from sys import has_neon, os_is_linux, os_is_macos, os_is_windows from .. import PathLike from .._linux_aarch64 import _lstat as _lstat_linux_arm diff --git a/stdlib/src/pathlib/path.mojo b/stdlib/src/pathlib/path.mojo index 0c88df1afa..963bb26eb9 100644 --- a/stdlib/src/pathlib/path.mojo +++ b/stdlib/src/pathlib/path.mojo @@ -15,7 +15,7 @@ import os from os import PathLike, listdir, stat_result -from sys.info import os_is_windows +from sys import os_is_windows from memory import stack_allocation diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index 0289747704..a214dfd9a4 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -15,10 +15,9 @@ from os import getenv from sys import external_call from sys.ffi import DLHandle -from memory.unsafe import DTypePointer +from memory import DTypePointer -from utils import StringRef -from utils.index import StaticIntTuple +from utils import StringRef, StaticIntTuple # https://github.com/python/cpython/blob/d45225bd66a8123e4a30314c627f2586293ba532/Include/compile.h#L7 alias Py_single_input = 256 diff --git a/stdlib/src/python/object.mojo b/stdlib/src/python/object.mojo index ca16c8c7f5..44bd922bca 100644 --- a/stdlib/src/python/object.mojo +++ b/stdlib/src/python/object.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `python` package. For example: ```mojo -from python.object import PythonObject +from python import PythonObject ``` """ diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 7ab6326730..2160ea79be 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -19,11 +19,10 @@ from python import Python ``` """ -from sys import external_call +from sys import external_call, sizeof from sys.ffi import _get_global -from sys.info import sizeof -from memory.unsafe import Pointer +from memory import Pointer from utils import StringRef diff --git a/stdlib/src/random/random.mojo b/stdlib/src/random/random.mojo index 23b2ef2c67..5687d2e8db 100644 --- a/stdlib/src/random/random.mojo +++ b/stdlib/src/random/random.mojo @@ -19,11 +19,10 @@ from random import seed ``` """ -from sys import external_call -from sys.info import bitwidthof +from sys import external_call, bitwidthof from time import now -from memory.unsafe import DTypePointer +from memory import DTypePointer fn _get_random_state() -> DTypePointer[DType.invalid]: diff --git a/stdlib/src/sys/arg.mojo b/stdlib/src/sys/arg.mojo index 9c45f70f6a..fd9dc41852 100644 --- a/stdlib/src/sys/arg.mojo +++ b/stdlib/src/sys/arg.mojo @@ -22,7 +22,7 @@ from sys import argv from sys import external_call -from memory.unsafe_pointer import UnsafePointer +from memory import UnsafePointer from utils import StringRef diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 1f17471a43..14c4a3cc0a 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # """Implements a foreign functions interface (FFI).""" -from memory.unsafe import DTypePointer, Pointer +from memory import DTypePointer, Pointer from utils import StringRef diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 7cec3ce179..5f325f6f46 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -15,7 +15,7 @@ You can import these APIs from the `sys` package. For example: ```mojo -from sys.info import is_x86 +from sys import is_x86 ``` """ diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index ce765964a2..593f709b73 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -15,14 +15,13 @@ You can import these APIs from the `complex` package. For example: ```mojo -from sys.intrinsics import PrefetchLocality +from sys import PrefetchLocality ``` """ -from sys.info import sizeof +from sys import sizeof -from memory.reference import AddressSpace -from memory.unsafe import DTypePointer +from memory import AddressSpace, DTypePointer # ===----------------------------------------------------------------------===# # llvm_intrinsic diff --git a/stdlib/src/sys/param_env.mojo b/stdlib/src/sys/param_env.mojo index a8fd9ff9fe..8e5a970481 100644 --- a/stdlib/src/sys/param_env.mojo +++ b/stdlib/src/sys/param_env.mojo @@ -16,7 +16,7 @@ You can use these functions to set parameter values or runtime constants based o name-value pairs defined on the command line. For example: ```mojo - from sys.param_env import is_defined + from sys import is_defined alias float_type = DType.float32 if is_defined["FLOAT32"]() else DType.float64 @@ -36,7 +36,7 @@ The `mojo run` command also supports the `-D` option. You can import these APIs from the `sys` package. For example: ```mojo -from sys.param_env import is_defined +from sys import is_defined ``` """ diff --git a/stdlib/src/time/time.mojo b/stdlib/src/time/time.mojo index 5d57175720..842cc4e7e2 100644 --- a/stdlib/src/time/time.mojo +++ b/stdlib/src/time/time.mojo @@ -19,11 +19,10 @@ from time import now ``` """ -from sys import external_call -from sys.info import os_is_linux, os_is_windows +from sys import external_call, os_is_linux, os_is_windows from builtin.simd import _floor -from memory.unsafe_pointer import UnsafePointer +from memory import UnsafePointer # ===----------------------------------------------------------------------===# # Utilities diff --git a/stdlib/src/utils/_numerics.mojo b/stdlib/src/utils/_numerics.mojo index d3c48d445e..8fa6949a74 100644 --- a/stdlib/src/utils/_numerics.mojo +++ b/stdlib/src/utils/_numerics.mojo @@ -19,13 +19,11 @@ from utils._numerics import FPUtils ``` """ -from sys import llvm_intrinsic +from sys import llvm_intrinsic, bitwidthof, has_neon, has_sse4 from sys._assembly import inlined_assembly -from sys.info import bitwidthof, has_neon, has_sse4 from builtin.dtype import _integral_type_of -from memory.unsafe import bitcast -from memory.unsafe_pointer import UnsafePointer +from memory import UnsafePointer, bitcast # ===----------------------------------------------------------------------===# # _digits diff --git a/stdlib/src/utils/_serialize.mojo b/stdlib/src/utils/_serialize.mojo index c14b53bee2..8da3ceb2c8 100644 --- a/stdlib/src/utils/_serialize.mojo +++ b/stdlib/src/utils/_serialize.mojo @@ -13,8 +13,7 @@ from pathlib import Path -from memory.reference import AddressSpace -from memory.unsafe import DTypePointer, bitcast +from memory import AddressSpace, DTypePointer, bitcast alias _kStartTensorMarker = "[" alias _kEndTensorMarker = "]" diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 4b2fc8f96e..163d2a7009 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -16,7 +16,7 @@ indices. You can import these APIs from the `utils` package. For example: ```mojo -from utils.index import StaticIntTuple +from utils import StaticIntTuple ``` """ diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index fd6415fe75..e74b71b7e4 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -15,12 +15,11 @@ avoids heap allocations for short strings. """ -from sys.info import sizeof +from sys import sizeof from memory import memcpy -from utils.static_tuple import StaticTuple -from utils.variant import Variant +from utils import StaticTuple, Variant # ===----------------------------------------------------------------------===# # InlinedString diff --git a/stdlib/src/utils/loop.mojo b/stdlib/src/utils/loop.mojo index 26d428d9ed..28fa732cc1 100644 --- a/stdlib/src/utils/loop.mojo +++ b/stdlib/src/utils/loop.mojo @@ -16,7 +16,7 @@ You can import these APIs from the `utils.loop` module. For example: ```mojo -from utils.loop import unroll +from utils import unroll ``` """ diff --git a/stdlib/src/utils/static_tuple.mojo b/stdlib/src/utils/static_tuple.mojo index 783c696fdf..6b2e0d2332 100644 --- a/stdlib/src/utils/static_tuple.mojo +++ b/stdlib/src/utils/static_tuple.mojo @@ -15,13 +15,13 @@ You can import these APIs from the `utils` package. For example: ```mojo -from utils.static_tuple import StaticTuple +from utils import StaticTuple ``` """ -from memory.unsafe import Pointer +from memory import Pointer -from utils.loop import unroll +from utils import unroll # ===----------------------------------------------------------------------===# # Utilities diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index a782a61ea5..1197badf6f 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,8 +16,7 @@ from builtin.dtype import _uint_type_of_width from builtin.string import _atol -from memory.unsafe import LegacyPointer, DTypePointer -from memory.unsafe_pointer import UnsafePointer +from memory import LegacyPointer, UnsafePointer, DTypePointer # ===----------------------------------------------------------------------=== # # Utilities diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index dd669bf082..548ad72b10 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -15,7 +15,7 @@ You can use this type to implement variant/sum types. For example: ```mojo -from utils.variant import Variant +from utils import Variant alias IntOrString = Variant[Int, String] fn to_string(inout x: IntOrString) -> String: @@ -38,13 +38,16 @@ print(to_string(who_knows)) ``` """ -from sys.info import alignof, sizeof +from sys import alignof, sizeof from sys.intrinsics import _mlirtype_is_eq -from memory.unsafe_pointer import * - -from utils.loop import unroll -from utils.static_tuple import StaticTuple +from memory import UnsafePointer +from memory.unsafe_pointer import ( + initialize_pointee, + move_from_pointee, + move_pointee, +) +from utils import unroll, StaticTuple # ===----------------------------------------------------------------------=== # # Utilities @@ -116,7 +119,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): Example: ```mojo - from utils.variant import Variant + from utils import Variant alias IntOrString = Variant[Int, String] fn to_string(inout x: IntOrString) -> String: if x.isa[String](): diff --git a/stdlib/test/builtin/test_bfloat16.mojo b/stdlib/test/builtin/test_bfloat16.mojo index 7b5f719ae2..2cd0f63884 100644 --- a/stdlib/test/builtin/test_bfloat16.mojo +++ b/stdlib/test/builtin/test_bfloat16.mojo @@ -13,7 +13,7 @@ # RUN: %mojo %s | FileCheck %s from random import randn_float64 -from sys.info import has_neon +from sys import has_neon from testing import assert_equal, assert_almost_equal diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index fbeb5f15af..b3582f9572 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -14,8 +14,7 @@ from pathlib import Path -from sys.info import os_is_windows -from sys.param_env import env_get_string +from sys import os_is_windows, env_get_string from testing import assert_equal diff --git a/stdlib/test/builtin/test_print.mojo b/stdlib/test/builtin/test_print.mojo index 12e74c863c..1356384ab1 100644 --- a/stdlib/test/builtin/test_print.mojo +++ b/stdlib/test/builtin/test_print.mojo @@ -13,10 +13,9 @@ # RUN: %mojo %s | FileCheck %s -from memory.unsafe import DTypePointer +from memory import DTypePointer -from utils import StringRef -from utils.index import StaticIntTuple +from utils import StringRef, StaticIntTuple # CHECK-LABEL: test_print diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index d2f9519308..04274ecbe0 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s | FileCheck %s -from sys.info import has_neon, simdwidthof +from sys import has_neon, simdwidthof from testing import assert_equal, assert_not_equal, assert_true diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index 1c4f8e199f..e1dfda19ae 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -12,8 +12,9 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from collections import Optional -from collections.dict import Dict, KeyElement, OwnedKwargsDict +from collections import Dict, KeyElement, Optional +from collections.dict import OwnedKwargsDict + from test_utils import CopyCounter from testing import assert_equal, assert_false, assert_raises, assert_true diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index cc4c28f6db..87a0608cf2 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from collections.optional import Optional, OptionalReg +from collections import Optional, OptionalReg from testing import assert_true, assert_false, assert_equal diff --git a/stdlib/test/collections/test_set.mojo b/stdlib/test/collections/test_set.mojo index 80b8ff5661..b8484051fe 100644 --- a/stdlib/test/collections/test_set.mojo +++ b/stdlib/test/collections/test_set.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from collections.set import Set +from collections import Set from testing import assert_raises, assert_true, assert_false diff --git a/stdlib/test/memory/test_bitcast.mojo b/stdlib/test/memory/test_bitcast.mojo index 5c6e435f52..7d85915f99 100644 --- a/stdlib/test/memory/test_bitcast.mojo +++ b/stdlib/test/memory/test_bitcast.mojo @@ -10,7 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # - # RUN: %mojo-no-debug %s from memory import bitcast diff --git a/stdlib/test/memory/test_memory.mojo b/stdlib/test/memory/test_memory.mojo index 6f82ceb633..8783f6f443 100644 --- a/stdlib/test/memory/test_memory.mojo +++ b/stdlib/test/memory/test_memory.mojo @@ -12,10 +12,9 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.info import sizeof +from sys import sizeof -from memory import memcmp, memcpy, memset_zero -from memory.unsafe import DTypePointer, Pointer +from memory import memcmp, memcpy, memset_zero, DTypePointer, Pointer from utils._numerics import nan from testing import ( assert_almost_equal, @@ -24,7 +23,7 @@ from testing import ( assert_true, ) -from utils.index import Index +from utils import Index alias void = __mlir_attr.`#kgen.dtype.constant : !kgen.dtype` alias int8_pop = __mlir_type.`!pop.scalar` diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index ed812f0ca7..c07fbc6671 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -12,7 +12,8 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s | FileCheck %s --dump-input=always -from memory.unsafe_pointer import * +from memory import UnsafePointer +from memory.unsafe_pointer import move_from_pointee, move_pointee from test_utils import MoveCounter from testing import assert_equal, assert_not_equal, assert_true diff --git a/stdlib/test/os/path/test_islink.mojo b/stdlib/test/os/path/test_islink.mojo index f4646c96c2..3c47735361 100644 --- a/stdlib/test/os/path/test_islink.mojo +++ b/stdlib/test/os/path/test_islink.mojo @@ -18,7 +18,7 @@ from os.path import isdir, islink from pathlib import Path -from sys.param_env import env_get_string +from sys import env_get_string from testing import assert_true, assert_false diff --git a/stdlib/test/os/test_atomic.mojo b/stdlib/test/os/test_atomic.mojo index 37249cec71..74466611e3 100644 --- a/stdlib/test/os/test_atomic.mojo +++ b/stdlib/test/os/test_atomic.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from os.atomic import Atomic +from os import Atomic from testing import assert_equal, assert_true, assert_false diff --git a/stdlib/test/pathlib/test_pathlib.mojo b/stdlib/test/pathlib/test_pathlib.mojo index 69fb5cc039..c2f29ce63e 100644 --- a/stdlib/test/pathlib/test_pathlib.mojo +++ b/stdlib/test/pathlib/test_pathlib.mojo @@ -14,7 +14,7 @@ # RUN: %mojo -D TEMP_FILE=%t %s from pathlib import cwd, Path, DIR_SEPARATOR -from sys.param_env import env_get_string +from sys import env_get_string from testing import assert_true, assert_false, assert_equal, assert_not_equal diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index f197bdb449..5f7f8febc2 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -13,12 +13,11 @@ # XFAIL: asan && !system-darwin # RUN: %mojo -D TEST_DIR=%S %s -from sys.param_env import env_get_string +from sys import env_get_string -from memory.unsafe import Pointer +from memory import Pointer from python._cpython import CPython, PyObjectPtr -from python.object import PythonObject -from python.python import Python +from python import PythonObject, Python from testing import assert_equal diff --git a/stdlib/test/python/test_python_error_handling.mojo b/stdlib/test/python/test_python_error_handling.mojo index b9cdc7b03f..53ed77e8dc 100644 --- a/stdlib/test/python/test_python_error_handling.mojo +++ b/stdlib/test/python/test_python_error_handling.mojo @@ -13,11 +13,10 @@ # XFAIL: asan && !system-darwin # RUN: %mojo -D TEST_DIR=%S %s -from sys.param_env import env_get_string +from sys import env_get_string -from python import Python +from python import Python, PythonObject from python._cpython import CPython, PyObjectPtr -from python.object import PythonObject from testing import assert_equal, assert_raises diff --git a/stdlib/test/python/test_python_interop.mojo b/stdlib/test/python/test_python_interop.mojo index 445408c6af..8e8809cd32 100644 --- a/stdlib/test/python/test_python_interop.mojo +++ b/stdlib/test/python/test_python_interop.mojo @@ -13,11 +13,10 @@ # XFAIL: asan && !system-darwin # RUN: %mojo -D TEST_DIR=%S %s -from sys.param_env import env_get_string +from sys import env_get_string from python._cpython import CPython, PyObjectPtr -from python.object import PythonObject -from python.python import Python, _get_global_python_itf +from python.python import Python, _get_global_python_itf, PythonObject from testing import assert_equal diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index dfd19a5b2b..a6e45bcf4c 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -13,10 +13,9 @@ # XFAIL: asan && !system-darwin # RUN: %mojo-no-debug %s | FileCheck %s -from memory.unsafe import Pointer +from memory import Pointer from python._cpython import CPython, PyObjectPtr -from python.object import PythonObject -from python.python import Python +from python import PythonObject, Python from testing import assert_false, assert_raises, assert_true, assert_equal from utils import StringRef diff --git a/stdlib/test/python/test_python_object_len_raises.mojo b/stdlib/test/python/test_python_object_len_raises.mojo index 662088aa67..efab24476e 100644 --- a/stdlib/test/python/test_python_object_len_raises.mojo +++ b/stdlib/test/python/test_python_object_len_raises.mojo @@ -13,8 +13,7 @@ # XFAIL: asan && !system-darwin # RUN: %mojo-no-debug %s -from python.object import PythonObject -from python.python import Python +from python import PythonObject, Python from testing import assert_raises diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 607cfbeca2..5725a5f47c 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -12,8 +12,8 @@ # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin # RUN: %mojo-no-debug %s | FileCheck %s -from python.object import PythonObject -from python.python import Python + +from python import Python, PythonObject from testing import assert_equal diff --git a/stdlib/test/sys/test_aarch64_target.mojo b/stdlib/test/sys/test_aarch64_target.mojo index 007f1e856d..26ac318ae9 100644 --- a/stdlib/test/sys/test_aarch64_target.mojo +++ b/stdlib/test/sys/test_aarch64_target.mojo @@ -16,7 +16,7 @@ # REQUIRES: apple-silicon # RUN: %mojo -debug-level %s | FileCheck %s -from sys.info import alignof, has_avx512f, has_neon, simdbitwidth +from sys import alignof, has_avx512f, has_neon, simdbitwidth # CHECK-LABEL: test_arch_query diff --git a/stdlib/test/sys/test_has_intel_amx.mojo b/stdlib/test/sys/test_has_intel_amx.mojo index d5cf972d3c..2012f5f372 100644 --- a/stdlib/test/sys/test_has_intel_amx.mojo +++ b/stdlib/test/sys/test_has_intel_amx.mojo @@ -21,6 +21,7 @@ from sys import has_intel_amx, os_is_linux from testing import assert_false, assert_true + from LinAlg.intel_amx import init_intel_amx diff --git a/stdlib/test/sys/test_intrinsics.mojo b/stdlib/test/sys/test_intrinsics.mojo index f527d61b0c..27248afc7d 100644 --- a/stdlib/test/sys/test_intrinsics.mojo +++ b/stdlib/test/sys/test_intrinsics.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.intrinsics import ( +from sys import ( compressed_store, masked_load, masked_store, @@ -20,8 +20,8 @@ from sys.intrinsics import ( strided_store, ) -from memory.unsafe import DTypePointer from testing import assert_equal +from memory import DTypePointer fn test_strided_load() raises: diff --git a/stdlib/test/sys/test_linux_target.mojo b/stdlib/test/sys/test_linux_target.mojo index fbe3ddd82e..6c610219b9 100644 --- a/stdlib/test/sys/test_linux_target.mojo +++ b/stdlib/test/sys/test_linux_target.mojo @@ -18,7 +18,7 @@ # RUN: %mojo %s | FileCheck %s -from sys.info import os_is_linux, os_is_macos +from sys import os_is_linux, os_is_macos # CHECK-LABEL: test_os_query diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index a2d2ade7d5..27f59d5eec 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -18,7 +18,7 @@ # RUN: %mojo %s -from sys.info import ( +from sys import ( is_big_endian, is_little_endian, os_is_linux, diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index 5d49181679..555652b14f 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -23,8 +23,7 @@ from os._windows import ( last_operation_succeeded, reset_last_error, ) -from sys import external_call -from sys.info import os_is_linux, os_is_macos, os_is_windows +from sys import external_call, os_is_linux, os_is_macos, os_is_windows from memory import Pointer from testing import assert_false, assert_true, assert_equal diff --git a/stdlib/test/time/test_time.mojo b/stdlib/test/time/test_time.mojo index 08a79ebcba..f4080bd442 100644 --- a/stdlib/test/time/test_time.mojo +++ b/stdlib/test/time/test_time.mojo @@ -12,7 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s -from sys.info import os_is_windows +from sys import os_is_windows from time import now, sleep, time_function from testing import assert_true diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index 01b21d34ec..7ef65b9504 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -14,7 +14,8 @@ from testing import assert_equal, assert_true -from utils.inlined_string import InlinedString, _FixedString +from utils.inlined_string import _FixedString +from utils import InlinedString def main(): diff --git a/stdlib/test/utils/test_tuple.mojo b/stdlib/test/utils/test_tuple.mojo index 6e847a4acc..ada1d3c1fc 100644 --- a/stdlib/test/utils/test_tuple.mojo +++ b/stdlib/test/utils/test_tuple.mojo @@ -14,8 +14,7 @@ from testing import assert_equal, assert_false, assert_true -from utils.index import StaticIntTuple -from utils.static_tuple import StaticTuple +from utils import StaticTuple, StaticIntTuple def test_static_tuple(): diff --git a/stdlib/test/utils/test_unroll.mojo b/stdlib/test/utils/test_unroll.mojo index b96c5087d8..784a3dcb21 100644 --- a/stdlib/test/utils/test_unroll.mojo +++ b/stdlib/test/utils/test_unroll.mojo @@ -12,8 +12,7 @@ # ===----------------------------------------------------------------------=== # # RUN: %mojo %s | FileCheck %s -from utils.index import StaticIntTuple -from utils.loop import unroll +from utils import StaticIntTuple, unroll # CHECK-LABEL: test_unroll diff --git a/stdlib/test/utils/test_variant.mojo b/stdlib/test/utils/test_variant.mojo index 908d2cab91..9163aa7f74 100644 --- a/stdlib/test/utils/test_variant.mojo +++ b/stdlib/test/utils/test_variant.mojo @@ -14,10 +14,10 @@ from sys.ffi import _get_global -from memory.unsafe import Pointer +from memory import Pointer from testing import assert_equal, assert_false, assert_true -from utils.variant import Variant +from utils import Variant struct TestCounter(CollectionElement): From 716b4d7cec0a250dababe32b15207454f6a559a6 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Thu, 18 Apr 2024 23:14:30 -0500 Subject: [PATCH 0227/2019] [External][stdlib] Remove FileCheck for `test_simd.mojo` (#2282) (#38069) Related to #2024 Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 16e1b615601dcc09e898d82969e79642524c7ae6 MODULAR_ORIG_COMMIT_REV_ID: 4b0a198a56a816311fc2f203a7b4d59d4efc7a38 --- stdlib/test/builtin/test_simd.mojo | 270 ++++++++++++++++------------- 1 file changed, 153 insertions(+), 117 deletions(-) diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 04274ecbe0..2b77e33d2d 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -10,17 +10,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from sys import has_neon, simdwidthof from testing import assert_equal, assert_not_equal, assert_true -# CHECK-LABEL: test_cast def test_cast(): - print("== test_cast") - assert_equal( SIMD[DType.bool, 4](False, True, False, True).cast[DType.bool](), SIMD[DType.bool, 4](False, True, False, True), @@ -42,18 +39,11 @@ def test_cast(): ) -# CHECK-LABEL: test_simd_variadic -fn test_simd_variadic(): - print("== test_simd_variadic") - - # CHECK: [52, 12, 43, 5] - print(SIMD[DType.index, 4](52, 12, 43, 5)) +def test_simd_variadic(): + assert_equal(str(SIMD[DType.index, 4](52, 12, 43, 5)), "[52, 12, 43, 5]") -# CHECK-LABEL: test_truthy def test_truthy(): - print("== test_truthy") - alias dtypes = ( DType.bool, DType.int8, @@ -98,10 +88,7 @@ def test_truthy(): test_dtype[DType.bfloat16]() -# CHECK-LABEL: test_floordiv def test_floordiv(): - print("== test_floordiv") - assert_equal(Int32(2) // Int32(2), 1) assert_equal(Int32(2) // Int32(3), 0) assert_equal(Int32(2) // Int32(-2), -1) @@ -116,10 +103,7 @@ def test_floordiv(): assert_equal(Float32(99) // Float32(-2), -50) -# CHECK-LABEL: test_mod def test_mod(): - print("== test_mod") - assert_equal(Int32(99) % Int32(1), 0) assert_equal(Int32(99) % Int32(3), 0) assert_equal(Int32(99) % Int32(-2), -1) @@ -198,124 +182,176 @@ def test_mod(): ) -# CHECK-LABEL: test_rotate -fn test_rotate(): - print("== test_rotate") - +def test_rotate(): alias simd_width = 4 alias type = DType.uint32 - # CHECK: [0, 1, 0, 1, 1, 0, 1, 0] - print(SIMD[DType.uint16, 8](1, 0, 1, 1, 0, 1, 0, 0).rotate_right[1]()) - # CHECK: [1, 0, 1, 0, 0, 1, 0, 1] - print(SIMD[DType.uint32, 8](1, 0, 1, 1, 0, 1, 0, 0).rotate_right[5]()) - - # CHECK: [1, 0, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[0]()) - # CHECK: [0, 1, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[1]()) - # CHECK: [1, 1, 1, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[2]()) - # CHECK: [1, 1, 0, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[3]()) - # CHECK: [1, 1, 0, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-1]()) - # CHECK: [1, 1, 1, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-2]()) - # CHECK: [0, 1, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-3]()) - # CHECK: [1, 0, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-4]()) - - # CHECK: [1, 0, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[0]()) - # CHECK: [1, 1, 0, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[1]()) - # CHECK: [1, 1, 1, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[2]()) - # CHECK: [0, 1, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[3]()) - # CHECK: [1, 0, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[4]()) - # CHECK: [0, 1, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[-1]()) - # CHECK: [1, 1, 1, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[-2]()) - # CHECK: [1, 1, 0, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).rotate_right[-3]()) - - -# CHECK-LABEL: test_shift -fn test_shift(): - print("== test_shift") + assert_equal( + SIMD[DType.uint16, 8](1, 0, 1, 1, 0, 1, 0, 0).rotate_right[1](), + SIMD[DType.uint16, 8](0, 1, 0, 1, 1, 0, 1, 0), + ) + assert_equal( + SIMD[DType.uint32, 8](1, 0, 1, 1, 0, 1, 0, 0).rotate_right[5](), + SIMD[DType.uint32, 8](1, 0, 1, 0, 0, 1, 0, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[0](), + SIMD[type, simd_width](1, 0, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[1](), + SIMD[type, simd_width](0, 1, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[2](), + SIMD[type, simd_width](1, 1, 1, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[3](), + SIMD[type, simd_width](1, 1, 0, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-1](), + SIMD[type, simd_width](1, 1, 0, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-2](), + SIMD[type, simd_width](1, 1, 1, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-3](), + SIMD[type, simd_width](0, 1, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_left[-4](), + SIMD[type, simd_width](1, 0, 1, 1), + ) + + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[0](), + SIMD[type, simd_width](1, 0, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[1](), + SIMD[type, simd_width](1, 1, 0, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[2](), + SIMD[type, simd_width](1, 1, 1, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[3](), + SIMD[type, simd_width](0, 1, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[4](), + SIMD[type, simd_width](1, 0, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[-1](), + SIMD[type, simd_width](0, 1, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[-2](), + SIMD[type, simd_width](1, 1, 1, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).rotate_right[-3](), + SIMD[type, simd_width](1, 1, 0, 1), + ) + + +def test_shift(): alias simd_width = 4 alias type = DType.uint32 - # CHECK: [0, 1, 0, 1, 1, 0, 1, 0] - print(SIMD[DType.uint16, 8](1, 0, 1, 1, 0, 1, 0, 0).shift_right[1]()) - # CHECK: [0, 0, 0, 0, 0, 11, 0, 13] - print(SIMD[DType.uint32, 8](11, 0, 13, 12, 0, 100, 0, 0).shift_right[5]()) + assert_equal( + SIMD[DType.uint16, 8](1, 0, 1, 1, 0, 1, 0, 0).shift_right[1](), + SIMD[DType.uint16, 8](0, 1, 0, 1, 1, 0, 1, 0), + ) + assert_equal( + SIMD[DType.uint32, 8](11, 0, 13, 12, 0, 100, 0, 0).shift_right[5](), + SIMD[DType.uint32, 8](0, 0, 0, 0, 0, 11, 0, 13), + ) - # CHECK: [0.0, 0.0, 0.0, 0.0, 0.0, 11.1, 0.0, 13.1] - print( + assert_equal( SIMD[DType.float64, 8](11.1, 0, 13.1, 12.2, 0, 100.4, 0, 0).shift_right[ 5 - ]() - ) - - # CHECK: [1, 0, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_left[0]()) - # CHECK: [0, 1, 1, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_left[1]()) - # CHECK: [1, 1, 0, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_left[2]()) - # CHECK: [1, 0, 0, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_left[3]()) - # CHECK: [0, 0, 0, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_left[4]()) - - # CHECK: [1, 0, 1, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_right[0]()) - # CHECK: [0, 1, 0, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_right[1]()) - # CHECK: [0, 0, 1, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_right[2]()) - # CHECK: [0, 0, 0, 1] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_right[3]()) - # CHECK: [0, 0, 0, 0] - print(SIMD[type, simd_width](1, 0, 1, 1).shift_right[4]()) - - -# CHECK-LABEL: test_insert -fn test_insert(): - print("== test_insert") - - # CHECK: 4 - print(Int32(3).insert(Int32(4))) - - # CHECK: [9, 6, 2, 3] - print(SIMD[DType.index, 4](0, 1, 2, 3).insert(SIMD[DType.index, 2](9, 6))) - - # CHECK: [0, 9, 6, 3] - print( + ](), + SIMD[DType.float64, 8](0, 0, 0, 0, 0, 11.1, 0, 13.1), + ) + + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_left[0](), + SIMD[type, simd_width](1, 0, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_left[1](), + SIMD[type, simd_width](0, 1, 1, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_left[2](), + SIMD[type, simd_width](1, 1, 0, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_left[3](), + SIMD[type, simd_width](1, 0, 0, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_left[4](), + SIMD[type, simd_width](0, 0, 0, 0), + ) + + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_right[0](), + SIMD[type, simd_width](1, 0, 1, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_right[1](), + SIMD[type, simd_width](0, 1, 0, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_right[2](), + SIMD[type, simd_width](0, 0, 1, 0), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_right[3](), + SIMD[type, simd_width](0, 0, 0, 1), + ) + assert_equal( + SIMD[type, simd_width](1, 0, 1, 1).shift_right[4](), + SIMD[type, simd_width](0, 0, 0, 0), + ) + + +def test_insert(): + assert_equal(Int32(3).insert(Int32(4)), 4) + + assert_equal( + SIMD[DType.index, 4](0, 1, 2, 3).insert(SIMD[DType.index, 2](9, 6)), + SIMD[DType.index, 4](9, 6, 2, 3), + ) + + assert_equal( SIMD[DType.index, 4](0, 1, 2, 3).insert[offset=1]( SIMD[DType.index, 2](9, 6) - ) + ), + SIMD[DType.index, 4](0, 9, 6, 3), ) - # CHECK: [0, 1, 2, 3, 9, 6, 3, 7] - print( + assert_equal( SIMD[DType.index, 8](0, 1, 2, 3, 5, 6, 7, 8).insert[offset=4]( SIMD[DType.index, 4](9, 6, 3, 7) - ) + ), + SIMD[DType.index, 8](0, 1, 2, 3, 9, 6, 3, 7), ) - # CHECK: [0, 1, 2, 9, 6, 3, 7, 8] - print( + assert_equal( SIMD[DType.index, 8](0, 1, 2, 3, 5, 6, 7, 8).insert[offset=3]( SIMD[DType.index, 4](9, 6, 3, 7) - ) + ), + SIMD[DType.index, 8](0, 1, 2, 9, 6, 3, 7, 8), ) From 7fc0d1431f72bf681f73b1833e5302b0e63b029e Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 11:05:51 -0500 Subject: [PATCH 0228/2019] [External][stdlib] Fix assert_equal[SIMD]() not raising if any elements are equal (#2279) (#38063) To reproduce the bug: ```mojo with assert_raises(): assert_equal(SIMD[DType.uint8, 2](1, 1), SIMD[DType.uint8, 2](1, 2)) ``` This is because using `!=` returns `[False, True]` then, when using `.__bool__()` on this, since `__bool__()` is equivalent to a `reduce_and()`, it returns False, and the error is not triggered. Long story short, doing `if a != b` doesn't do what one would expect. This bug is not present with `assert_not_equal()`. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: ee5dfe791effa9def469001e9cd5933996b96c6f MODULAR_ORIG_COMMIT_REV_ID: 55d43d1473a9005ab1157851c60251a4f16a036a --- stdlib/src/testing/testing.mojo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index 8d29cc4f10..d0b6243c70 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -168,7 +168,9 @@ fn assert_equal[ Raises: An Error with the provided message if assert fails and `None` otherwise. """ - if lhs != rhs: + # `if lhs != rhs:` is not enough. `reduce_or()` must be used here, otherwise, if any of the elements are + # equal, the error is not triggered. + if (lhs != rhs).reduce_or(): raise _assert_equal_error(str(lhs), str(rhs), msg=msg) From 6f6c862ae101aaafbaac26ec026cd983c3a52790 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 14:58:46 -0500 Subject: [PATCH 0229/2019] [External][stdlib] Make Bool struct Intable (#2181) (#38051) Add an `__int__` function to `Bool`. Fixes https://github.com/modularml/mojo/issues/2168. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 07115011741d3a3c51d87085c3f54c60d701f270 MODULAR_ORIG_COMMIT_REV_ID: c33630aa1f254ccc4684271b8016f04543ee5515 --- stdlib/src/builtin/bool.mojo | 4 +++- stdlib/test/builtin/test_bool.mojo | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/builtin/test_bool.mojo diff --git a/stdlib/src/builtin/bool.mojo b/stdlib/src/builtin/bool.mojo index 67d378e355..fb2eaef300 100644 --- a/stdlib/src/builtin/bool.mojo +++ b/stdlib/src/builtin/bool.mojo @@ -46,7 +46,9 @@ trait Boolable: @lldb_formatter_wrapping_type @value @register_passable("trivial") -struct Bool(Stringable, CollectionElement, Boolable, EqualityComparable): +struct Bool( + Stringable, CollectionElement, Boolable, EqualityComparable, Intable +): """The primitive Bool scalar value used in Mojo.""" var value: __mlir_type.`!pop.scalar` diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo new file mode 100644 index 0000000000..e9b3895467 --- /dev/null +++ b/stdlib/test/builtin/test_bool.mojo @@ -0,0 +1,27 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo -debug-level full %s + +from testing import assert_equal + + +def test_bool_cast_to_int(): + assert_equal(False.__int__(), 0) + assert_equal(True.__int__(), 1) + + assert_equal(int(False), 0) + assert_equal(int(True), 1) + + +def main(): + test_bool_cast_to_int() From c2ae28ffe1e372f05fa3c9000a0fedc7413c70ad Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 15:51:34 -0500 Subject: [PATCH 0230/2019] [External][mojo-stdlib] Implement mod and div on `object` (#2230) (#38177) Implement mod and div methods on `object`. Fixes https://github.com/modularml/mojo/issues/2224. Signed-off-by: Jiexiang Liu mojo-orig-commit: dbe3deed799b032b5243de291f4a2f316029bb31 Co-authored-by: Jiexiang Liu <80805665+LJ-9801@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 183b4aa221b8de8a5210d3d5caa18de773486e04 --- stdlib/src/builtin/object.mojo | 115 ++++++++++++++++++++++++--- stdlib/test/builtin/test_object.mojo | 50 +++++++++++- 2 files changed, 153 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 472aacde19..179817af6b 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -1308,12 +1308,50 @@ struct object(IntableRaising, Boolable, Stringable): self, rhs ) - # TODO: __mod__ - # TODO: __truediv__ - # TODO: __floordiv__ + @always_inline + fn __mod__(self, rhs: object) raises -> object: + """Modulo operator. Valid only for arithmetic types. - # TODO: __lshift__ - # TODO: __rshift__ + Args: + rhs: Right hand value. + + Returns: + The left hand value mod the right hand value. + """ + return Self._arithmetic_binary_op[Float64.__mod__, Int64.__mod__]( + self, rhs + ) + + @always_inline + fn __truediv__(self, rhs: object) raises -> object: + """True division operator. Valid only for arithmetic types. + + Args: + rhs: Right hand value. + + Returns: + The left hand value true divide the right hand value. + """ + return Self._arithmetic_binary_op[ + Float64.__truediv__, Int64.__truediv__ + ](self, rhs) + + @always_inline + fn __floordiv__(self, rhs: object) raises -> object: + """Floor division operator. Valid only for arithmetic types. + + Args: + rhs: Right hand value. + + Returns: + The left hand value floor divide the right hand value. + """ + return Self._arithmetic_binary_op[ + Float64.__floordiv__, Int64.__floordiv__ + ](self, rhs) + + # TODO __lshift__ + # TODO __rshift__ @always_inline fn __and__(self, rhs: object) raises -> object: @@ -1387,9 +1425,32 @@ struct object(IntableRaising, Boolable, Stringable): """ self = self**rhs - # TODO: __imod__ - # TODO: __itruediv__ - # TODO: __ifloordiv__ + @always_inline + fn __imod__(inout self, rhs: object) raises: + """In-place modulo operator. + + Args: + rhs: Right hand value. + """ + self = self % rhs + + @always_inline + fn __itruediv__(inout self, rhs: object) raises: + """In-place true division operator. + + Args: + rhs: Right hand value. + """ + self = self / rhs + + @always_inline + fn __ifloordiv__(inout self, rhs: object) raises: + """In-place floor division operator. + + Args: + rhs: Right hand value. + """ + self = self // rhs # TODO: __ilshift__ # TODO: __irshift__ @@ -1468,8 +1529,42 @@ struct object(IntableRaising, Boolable, Stringable): """ return lhs**self - # TODO: __rfloordiv__ - # TODO: __rmod__ + @always_inline + fn __rmod__(self, lhs: object) raises -> object: + """Reverse modulo operator. + + Args: + lhs: Left hand value. + + Returns: + The left hand value mod the right hand value. + """ + return lhs % self + + @always_inline + fn __rtruediv__(self, lhs: object) raises -> object: + """Reverse true division operator. + + Args: + lhs: Left hand value. + + Returns: + The left hand value divide the right hand value. + """ + return lhs / self + + @always_inline + fn __rfloordiv__(self, lhs: object) raises -> object: + """Reverse floor division operator. + + Args: + lhs: Left hand value. + + Returns: + The left hand value floor divide the right hand value. + """ + return lhs // self + # TODO: __rlshift__ # TODO: __rrshift__ diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index 36ff5285d6..a3a3ced900 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -98,8 +98,53 @@ def test_arithmetic_ops(): assert_true(lhs == concatted) -# These are all marked borrowed because 'object' doesn't support function -# types with owned arguments. +def test_arithmetic_ops_div(): + # test mod + lhs = object(5.5) + rhs = object(2.0) + assert_true((lhs % rhs) == 1.5) + lhs %= rhs + assert_true(lhs == 1.5) + assert_true(5.5 % object(2.0) == 1.5) + + lhs = object(5) + rhs = object(2) + assert_true((lhs % rhs) == 1) + lhs %= rhs + assert_true(lhs == 1) + assert_true(5 % object(2) == 1) + + # truediv + lhs = object(5.5) + rhs = object(2.0) + assert_true(lhs / rhs == 2.75) + lhs /= rhs + assert_true(lhs == 2.75) + assert_true(5.5 / object(2.0) == 2.75) + + lhs = object(5) + rhs = object(2) + assert_true(lhs / rhs == 2) + lhs /= rhs + assert_true(lhs == 2) + assert_true(5 / object(2) == 2) + + # floor div + lhs = object(5.5) + rhs = object(2.0) + assert_true(lhs // rhs == 2) + lhs //= rhs + assert_true(lhs == 2) + assert_true(5.5 // object(2.0) == 2) + + lhs = object(5) + rhs = object(2) + assert_true(lhs // rhs == 2) + lhs //= rhs + assert_true(lhs == 2) + assert_true(5 // object(2) == 2) + + def test_function(borrowed lhs, borrowed rhs) -> object: return lhs + rhs @@ -197,6 +242,7 @@ def main(): test_object_ctors() test_comparison_ops() test_arithmetic_ops() + test_arithmetic_ops_div() # CHECK: Function at address 0x{{[a-float0-9]+}} # CHECK-NEXT: 3 # CHECK-NEXT: Error from function type From bf762af4e87023093525ae143b5e33d3ef36d0bb Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 15:58:11 -0500 Subject: [PATCH 0231/2019] [External][docs] Fix typo in `debugging.ipynb` (#2314) (#38172) wih -> with Signed-off-by: Ikko Eltociear Ashimine mojo-orig-commit: 74ade69dbf512af94eec8ad7b8e5524ecb21029b Co-authored-by: Ikko Eltociear Ashimine MODULAR_ORIG_COMMIT_REV_ID: 0fabc1801bc031922757876b8a55ee547af42bfd --- docs/tools/debugging.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tools/debugging.ipynb b/docs/tools/debugging.ipynb index 3e7fa882b8..8d8b306ad8 100644 --- a/docs/tools/debugging.ipynb +++ b/docs/tools/debugging.ipynb @@ -74,7 +74,7 @@ "* **Debug Mojo File** launches the Mojo program detached from any terminal.\n", "Standard output and standard error output for the program are displayed in the\n", "**Debug Console**. You can't write to the program's standard input, but you can\n", - "see the program's output and interact wih the debugger in a single location.\n", + "see the program's output and interact with the debugger in a single location.\n", "\n", "* **Debug Mojo File in Dedicated Terminal** creates a new instance of VS Code's\n", "integrated terminal and attaches the program's input and output to the terminal.\n", From c2bfb265ce7ae03e6eec374803ffa5b464364e68 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 15:58:43 -0500 Subject: [PATCH 0232/2019] [External][stdlib] Remove FileCheck in `test_python_to_mojo.mojo` (#2287) (#38175) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 5d7f3c9327d0bb9260ee20a6b33aa4298cb1fbd2 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: fba146ceca1a00bb91bd421cd6e65d5b06eb5285 --- stdlib/test/python/test_python_to_mojo.mojo | 43 ++++++++++----------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 5725a5f47c..166995952f 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -11,11 +11,11 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s | FileCheck %s +# RUN: %mojo-no-debug %s from python import Python, PythonObject -from testing import assert_equal +from testing import assert_equal, assert_false, assert_true fn test_string_to_python_to_mojo(inout python: Python) raises: @@ -28,41 +28,38 @@ fn test_string_to_python_to_mojo(inout python: Python) raises: fn test_range() raises: var array_size: PythonObject = 2 - # CHECK: 0 - # CHECK-NEXT: 1 + + # we check that the numbers appear in order + # and that there are not less iterations than expected + # by ensuring the list is empty at the end. + var expected = List[Int](0, 1) for i in range(array_size): - print(i) + assert_equal(i, expected.pop(0)) + assert_false(expected) var start: PythonObject = 0 var end: PythonObject = 4 - # CHECK: 0 - # CHECK-NEXT: 1 - # CHECK-NEXT: 2 - # CHECK-NEXT: 3 + expected = List[Int](0, 1, 2, 3) for i in range(start, end): - print(i) + assert_equal(i, expected.pop(0)) + assert_false(expected) var start2: PythonObject = 5 var end2: PythonObject = 10 var step: PythonObject = 2 - # CHECK: 5 - # CHECK-NEXT: 7 - # CHECK-NEXT: 9 + expected = List[Int](5, 7, 9) for i in range(start2, end2, step): - print(i) + assert_equal(i, expected.pop(0)) + assert_false(expected) fn test_python_to_string() raises: - # CHECK: environ({ var os = Python.import_module("os") - print(os.environ) + assert_true(str(os.environ).startswith("environ({")) -fn main(): +def main(): var python = Python() - try: - test_string_to_python_to_mojo(python) - test_range() - test_python_to_string() - except: - pass + test_string_to_python_to_mojo(python) + test_range() + test_python_to_string() From 826f97a52e743616829eae5d8d3877bd8aa3f2ef Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:05:19 -0500 Subject: [PATCH 0233/2019] [External][stdlib] Remove unnecessary declaration in `base64` (#2275) (#38179) `i` was an unnecessary and unclear declaration that could be substituted for `end`. This is not a behavioral change. Reasoning: - `i` was set to be equivalent to `end`, but was never used in a way in which it could not simply be substituted for `end`. - `i` is not descriptive, and `end` does a better job describing it's purpose. `i` didn't have a comment either, also making it less descriptive than `end`. - `var i` was used almost immediately after a different `i` was used as a counter in a for loop, and it could be confusing to use two variables with different purposes and the same name immediately after one another. - Removing this declaration could result in a small performance improvement because an assignment is removed, but at the very least the code is simpler without this declaration. Signed-off-by: Liam Swayne mojo-orig-commit: 0a884b7941303e41d77e889101c34783c9e3a975 Co-authored-by: Liam Swayne MODULAR_ORIG_COMMIT_REV_ID: 8f33db92dc2ff543894fc2edb4cc49bc7759b68c --- stdlib/src/base64/base64.mojo | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/stdlib/src/base64/base64.mojo b/stdlib/src/base64/base64.mojo index d7b2f02e64..a53afb2d55 100644 --- a/stdlib/src/base64/base64.mojo +++ b/stdlib/src/base64/base64.mojo @@ -58,15 +58,14 @@ fn b64encode(str: String) -> String: out.append(b64chars.load(((si_1 * 4) % 64) + si_2 // 64)) out.append(b64chars.load(si_2 % 64)) - var i = end - if i < length: - var si = s(i) + if end < length: + var si = s(end) out.append(b64chars.load(si // 4)) - if i == length - 1: + if end == length - 1: out.append(b64chars.load((si * 16) % 64)) out.append(ord("=")) - elif i == length - 2: - var si_1 = s(i + 1) + elif end == length - 2: + var si_1 = s(end + 1) out.append(b64chars.load(((si * 16) % 64) + si_1 // 16)) out.append(b64chars.load((si_1 * 4) % 64)) out.append(ord("=")) From c31bd85ac093ce7d0eee53862d85aaf5a8f1544f Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:06:00 -0500 Subject: [PATCH 0234/2019] [External][stdlib] Remove FileCheck in `test_python_object.mojo` (#2284) (#38184) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: a7a1616dd415e822cb8b9344c79344b0c62ecdc8 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 1686de46b918460709b34cffa4ce35ba67ab7e2b --- stdlib/test/python/test_python_object.mojo | 404 ++++++++++----------- 1 file changed, 197 insertions(+), 207 deletions(-) diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index a6e45bcf4c..862e72f439 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s | FileCheck %s +# RUN: %mojo-no-debug %s from memory import Pointer from python._cpython import CPython, PyObjectPtr @@ -21,242 +21,239 @@ from testing import assert_false, assert_raises, assert_true, assert_equal from utils import StringRef -fn test_dunder_methods(inout python: Python): - try: - var a = PythonObject(34) - var b = PythonObject(10) +def test_dunder_methods(inout python: Python): + var a = PythonObject(34) + var b = PythonObject(10) - # __add__ - var c = a + b - assert_true(c, 44) + # __add__ + var c = a + b + assert_true(c, 44) - # __add__ - c = a + 100 - assert_true(c, 134) + # __add__ + c = a + 100 + assert_true(c, 134) - # __iadd__ - c += 100 - assert_equal(c, 234) + # __iadd__ + c += 100 + assert_equal(c, 234) - # __radd__ - c = 100 + a - assert_equal(c, 134) + # __radd__ + c = 100 + a + assert_equal(c, 134) - # __sub__ - c = a - b - assert_equal(c, 24) + # __sub__ + c = a - b + assert_equal(c, 24) - # __isub__ - c -= 100 - assert_equal(c, -76) + # __isub__ + c -= 100 + assert_equal(c, -76) - # __sub__ - c = a - 100 - assert_equal(c, -66) + # __sub__ + c = a - 100 + assert_equal(c, -66) - # __rsub__ - c = 100 - a - assert_equal(c, 66) + # __rsub__ + c = 100 - a + assert_equal(c, 66) - # __mul__ - c = a * b - assert_equal(c, 340) + # __mul__ + c = a * b + assert_equal(c, 340) - # __imul__ - c *= 10 - assert_equal(c, 3400) + # __imul__ + c *= 10 + assert_equal(c, 3400) - # __mul__ - c = a * 10 - assert_equal(c, 340) + # __mul__ + c = a * 10 + assert_equal(c, 340) - # __rmul__ - c = 34 * b - assert_equal(c, 340) + # __rmul__ + c = 34 * b + assert_equal(c, 340) - # __floordiv__ - c = a // b - assert_equal(c, 3) + # __floordiv__ + c = a // b + assert_equal(c, 3) - # __ifloordiv__ - c //= 2 - assert_equal(c, 1) + # __ifloordiv__ + c //= 2 + assert_equal(c, 1) - # __floordiv__ - c = a // 10 - assert_equal(c, 3) + # __floordiv__ + c = a // 10 + assert_equal(c, 3) - # __rfloordiv__ - c = 34 // b - assert_equal(c, 3) + # __rfloordiv__ + c = 34 // b + assert_equal(c, 3) - # __truediv__ - c = a / b - assert_equal(c, 3.4) + # __truediv__ + c = a / b + assert_equal(c, 3.4) - # __itruediv__ - c /= 2 - assert_equal(c, 1.7) + # __itruediv__ + c /= 2 + assert_equal(c, 1.7) - # __truediv__ - c = a / 10 - assert_equal(c, 3.4) + # __truediv__ + c = a / 10 + assert_equal(c, 3.4) - # __rtruediv__ - c = 34 / b - assert_equal(c, 3.4) + # __rtruediv__ + c = 34 / b + assert_equal(c, 3.4) - # __mod__ - c = a % b - assert_equal(c, 4) + # __mod__ + c = a % b + assert_equal(c, 4) - # __imod__ - c %= 3 - assert_equal(c, 1) + # __imod__ + c %= 3 + assert_equal(c, 1) - # __mod__ - c = a % 10 - assert_equal(c, 4) + # __mod__ + c = a % 10 + assert_equal(c, 4) - # __rmod__ - c = 34 % b - assert_equal(c, 4) - - # __xor__ - c = a ^ b - assert_equal(c, 40) + # __rmod__ + c = 34 % b + assert_equal(c, 4) - # __ixor__ - c ^= 15 - assert_equal(c, 39) + # __xor__ + c = a ^ b + assert_equal(c, 40) - # __xor__ - c = a ^ 10 - assert_equal(c, 40) + # __ixor__ + c ^= 15 + assert_equal(c, 39) - # __rxor__ - c = 34 ^ b - assert_equal(c, 40) + # __xor__ + c = a ^ 10 + assert_equal(c, 40) - # __or__ - c = a | b - assert_equal(c, 42) + # __rxor__ + c = 34 ^ b + assert_equal(c, 40) - # __ior__ - c |= 9 - assert_equal(c, 43) + # __or__ + c = a | b + assert_equal(c, 42) - # __or__ - c = a | 10 - assert_equal(c, 42) + # __ior__ + c |= 9 + assert_equal(c, 43) - # __ror__ - c = 34 | b - assert_equal(c, 42) + # __or__ + c = a | 10 + assert_equal(c, 42) - # __and__ - c = a & b - assert_equal(c, 2) + # __ror__ + c = 34 | b + assert_equal(c, 42) - # __iand__ - c &= 6 - assert_equal(c, 2) + # __and__ + c = a & b + assert_equal(c, 2) - # __and__ - c = a & 10 - assert_equal(c, 2) + # __iand__ + c &= 6 + assert_equal(c, 2) - # __rand__ - c = 34 & b - assert_equal(c, 2) + # __and__ + c = a & 10 + assert_equal(c, 2) - # __rshift__ - var d = PythonObject(2) - c = a >> d - assert_equal(c, 8) + # __rand__ + c = 34 & b + assert_equal(c, 2) - # __irshift__ - c >>= 2 - assert_equal(c, 2) + # __rshift__ + var d = PythonObject(2) + c = a >> d + assert_equal(c, 8) - # __rshift__ - c = a >> 2 - assert_equal(c, 8) + # __irshift__ + c >>= 2 + assert_equal(c, 2) - # __rrshift__ - c = 34 >> d - assert_equal(c, 8) + # __rshift__ + c = a >> 2 + assert_equal(c, 8) - # __lshift__ - c = a << d - assert_equal(c, 136) + # __rrshift__ + c = 34 >> d + assert_equal(c, 8) - # __ilshift__ - c <<= 1 - assert_equal(c, 272) + # __lshift__ + c = a << d + assert_equal(c, 136) - # __lshift__ - c = a << 2 - assert_equal(c, 136) + # __ilshift__ + c <<= 1 + assert_equal(c, 272) - # __rlshift__ - c = 34 << d - assert_equal(c, 136) + # __lshift__ + c = a << 2 + assert_equal(c, 136) - # __pow__ - c = a**d - assert_equal(c, 1156) + # __rlshift__ + c = 34 << d + assert_equal(c, 136) - # __ipow__ - c = 3 - c **= 4 - assert_equal(c, 81) + # __pow__ + c = a**d + assert_equal(c, 1156) - # __pow__ - c = a**2 - assert_equal(c, 1156) + # __ipow__ + c = 3 + c **= 4 + assert_equal(c, 81) - # __rpow__ - c = 34**d - assert_equal(c, 1156) + # __pow__ + c = a**2 + assert_equal(c, 1156) - # __lt__ - c = a < b - assert_false(c) + # __rpow__ + c = 34**d + assert_equal(c, 1156) - # __le__ - c = a <= b - assert_false(c) + # __lt__ + c = a < b + assert_false(c) - # __gt__ - c = a > b - assert_true(c) + # __le__ + c = a <= b + assert_false(c) - # __ge__ - c = a >= b - assert_true(c) + # __gt__ + c = a > b + assert_true(c) - # __eq__ - c = a == b - assert_false(c) + # __ge__ + c = a >= b + assert_true(c) - # __ne__ - c = a != b - assert_true(c) + # __eq__ + c = a == b + assert_false(c) - # __pos__ - c = +a - assert_equal(c, 34) + # __ne__ + c = a != b + assert_true(c) - # __neg__ - c = -a - assert_equal(c, -34) + # __pos__ + c = +a + assert_equal(c, 34) - # __invert__ - c = ~a - assert_equal(c, -35) - except e: - pass + # __neg__ + c = -a + assert_equal(c, -34) + + # __invert__ + c = ~a + assert_equal(c, -35) def test_bool_conversion() -> None: @@ -324,9 +321,7 @@ def test_len(): assert_equal(len(l2), 2) -# CHECK-LABEL: test_is def test_is(): - print("=== test_is ===") var x = PythonObject(500) var y = PythonObject(500) assert_false(x is y) @@ -346,37 +341,36 @@ def test_is(): assert_true(l1 is not l2) -# CHECK-LABEL: test_iter fn test_iter() raises: - print("=== test_iter ===") - var list_obj: PythonObject = ["apple", "orange", "banana"] - # CHECK: I like to eat apple - # CHECK: I like to eat orange - # CHECK: I like to eat banana + var i = 0 for fruit in list_obj: - print("I like to eat", fruit) + if i == 0: + assert_equal(fruit, "apple") + elif i == 1: + assert_equal(fruit, "orange") + elif i == 2: + assert_equal(fruit, "banana") + i += 1 var list2: PythonObject = [] - # CHECK-NOT: I have eaten for fruit in list2: - print("I have eaten", fruit) + raise Error("This should not be reachable as the list is empty.") var not_iterable: PythonObject = 3 with assert_raises(): for x in not_iterable: - # CHECK-NOT: I should not exist - print("I should not exist", x) - assert_false(True) + assert_false( + True, + "This should not be reachable as the object is not iterable.", + ) fn test_setitem() raises: var ll = PythonObject([1, 2, 3, "food"]) - # CHECK: [1, 2, 3, 'food'] - print(ll) + assert_equal(str(ll), "[1, 2, 3, 'food']") ll[1] = "nomnomnom" - # CHECK: [1, 'nomnomnom', 3, 'food'] - print(ll) + assert_equal(str(ll), "[1, 'nomnomnom', 3, 'food']") fn test_dict() raises: @@ -386,24 +380,20 @@ fn test_dict() raises: d["food"] = 123 # intentionally replace to ensure keys stay in order var dd = PythonObject(d) - # CHECK: {'food': 123, 'fries': 'yes'} - print(dd) + assert_equal(str(dd), "{'food': 123, 'fries': 'yes'}") dd["food"] = "salad" dd[42] = Python.evaluate("[4, 2]") - # CHECK: {'food': 'salad', 'fries': 'yes', 42: [4, 2]} - print(dd) + assert_equal(str(dd), "{'food': 'salad', 'fries': 'yes', 42: [4, 2]}") # Also test that Python.dict() creates the right object. var empty = Python.dict() - # CHECK: empty: {} - print("empty:", empty) + assert_equal(str(empty), "{}") fn test_none() raises: var n = Python.none() - # CHECK: None from Python: None - print("None from Python: ", n) + assert_equal(str(n), "None") assert_true(n is None) From 688afda7a14e41e8a8c1de7ccaceb198203c54a4 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 19 Apr 2024 16:20:39 -0500 Subject: [PATCH 0235/2019] [stdlib] feature: Support GPU printing (#37650) [stdlib] feature: Improve formatting support on GPU * bugfix: Support _print_fmt when targeting GPU - Change _put to vprintf in _put when targetting CUDA so that Formatter.stdout() works. - Avoid calling non-existent flush function when targetting CUDA. * cleanup: Refactor _write_int to use Formatter instead of List This refactors the _write_int(..) function used by hex(..) in several ways that make it usable in a GPU context: - Writes to a Formatter object instead of a List[Int8] - Uses a fixed-sized _ArrayMem internally to avoid allocating - Takes the digit chars and prefix strings as StringLiteral instead of String to avoid allocations in its API. - Does general code cleanups. * feature: Fix Int.format_to on GPU by replacing snprintf with _write_int snprintf is not available on CUDA GPUs, so instead of calling into snprintf to format an Int, use the pure Mojo _write_int function. * feature: Change Layout and DynamicTuple to use Formattable This takes us one step closer to supporting formatting of these types on GPU. * feature: Make _FixedString Formattable and ToFormatter + add usage example * cleanup: Work around lit.try folding error by removing raises * cleanup: Workaround compiler assertion failure by avoiding variadic pack use MODULAR_ORIG_COMMIT_REV_ID: 20785004f791a0aa77813ff4eee9a44188a7a16b --- stdlib/src/builtin/hex.mojo | 130 ++++++++++++++++++--------- stdlib/src/builtin/int.mojo | 56 +++++++----- stdlib/src/builtin/io.mojo | 39 +++++--- stdlib/src/utils/inlined_string.mojo | 88 ++++++++++++++++-- stdlib/test/builtin/test_hex.mojo | 31 ++++--- stdlib/test/utils/test_format.mojo | 10 +++ 6 files changed, 254 insertions(+), 100 deletions(-) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 6e6b5e6fb3..231d7a53bd 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -16,7 +16,8 @@ These are Mojo built-ins, so you don't need to import them. """ -from collections import List +from collections import List, Optional +from utils.inlined_string import _ArrayMem alias _DEFAULT_DIGIT_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" @@ -61,31 +62,41 @@ fn _abs(x: SIMD) -> __type_of(x): return (x > 0).select(x, -x) -fn _format_int[ - T: Intable -]( - value: T, +fn _format_int( + value: Int64, radix: Int = 10, - digit_chars: String = _DEFAULT_DIGIT_CHARS, - prefix: String = "", + digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, + prefix: StringLiteral = "", ) raises -> String: - var buf = List[Int8]() + var string = String() + var fmt = string._unsafe_to_formatter() - _write_int(buf, value, radix, digit_chars, prefix) + _write_int(fmt, value, radix, digit_chars, prefix) - return String._from_bytes(buf^) + return string^ @always_inline -fn _write_int[ - T: Intable -]( - inout fmt: List[Int8], - value0: T, +fn _write_int( + inout fmt: Formatter, + value: Int64, radix: Int = 10, - digit_chars: String = _DEFAULT_DIGIT_CHARS, - prefix: String = "", + digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, + prefix: StringLiteral = "", ) raises: + var err = _try_write_int(fmt, value, radix, digit_chars, prefix) + if err: + raise err.value() + + +@always_inline +fn _try_write_int( + inout fmt: Formatter, + value: Int64, + radix: Int = 10, + digit_chars: StringLiteral = _DEFAULT_DIGIT_CHARS, + prefix: StringLiteral = "", +) -> Optional[Error]: """Writes a formatted string representation of the given integer using the specified radix. The maximum supported radix is 36 unless a custom `digit_chars` mapping is @@ -97,16 +108,16 @@ fn _write_int[ # if radix < 2: - raise Error("Unable to format integer to string with radix < 2") + return Error("Unable to format integer to string with radix < 2") if radix > len(digit_chars): - raise Error( + return Error( "Unable to format integer to string when provided radix is larger " "than length of available digit value characters" ) if not len(digit_chars) >= 2: - raise Error( + return Error( "Unable to format integer to string when provided digit_chars" " mapping len is not >= 2" ) @@ -115,46 +126,83 @@ fn _write_int[ # Process the integer value into its corresponding digits # - var value = Int64(int(value0)) - # TODO(#26444, Unicode support): Get an array of Character, not bytes. - var digit_chars_array = digit_chars._as_ptr() + var digit_chars_array = digit_chars.data() # Prefix a '-' if the original int was negative and make positive. if value < 0: - fmt.append(ord("-")) + fmt.write_str("-") # Add the custom number prefix, e.g. "0x" commonly used for hex numbers. # This comes *after* the minus sign, if present. - fmt.extend(prefix.as_bytes()) + fmt.write_str(prefix) if value == 0: - fmt.append(digit_chars_array[0]) + var zero = StringRef(digit_chars_array, 1) + fmt.write_str(zero) return - var first_digit_pos = len(fmt) + # + # Create a buffer to store the formatted value + # + + # Stack allocate enough bytes to store any formatted 64-bit integer + alias CAPACITY: Int = 64 + + var buf = _ArrayMem[Int8, CAPACITY]() + + # Start the buf pointer at the end. We will write the least-significant + # digits later in the buffer, and then decrement the pointer to move + # earlier in the buffer as we write the more-significant digits. + var offset = CAPACITY - 1 + + # + # Write the digits of the number + # var remaining_int = value - if remaining_int >= 0: + + @parameter + fn process_digits[get_digit_value: fn () capturing -> Int64](): while remaining_int: - var digit_value = remaining_int % radix + var digit_value = get_digit_value() + + # Write the char representing the value of the least significant + # digit. + buf[offset] = digit_chars_array[digit_value] - # Push the char representing the value of the least significant digit - fmt.append(digit_chars_array[digit_value]) + # Position the offset to write the next digit. + offset -= 1 # Drop the least significant digit remaining_int /= radix + + if remaining_int >= 0: + + @parameter + fn pos_digit_value() -> Int64: + return remaining_int % radix + + process_digits[pos_digit_value]() else: - while remaining_int: - var digit_value = _abs(remaining_int % -radix) - # Push the char representing the value of the least significant digit - fmt.append(digit_chars_array[digit_value]) + @parameter + fn neg_digit_value() -> Int64: + return _abs(remaining_int % -radix) - # Drop the least significant digit - remaining_int /= radix + process_digits[neg_digit_value]() + + # Re-add +1 byte since the loop ended so we didn't write another char. + offset += 1 + + var buf_ptr = buf.as_ptr() + offset + + # Calculate the length of the buffer we've filled. This is the number of + # bytes from our final `buf_ptr` to the end of the buffer. + var len = CAPACITY - offset + + var strref = StringRef(rebind[Pointer[Int8]](buf_ptr), len) + + fmt.write_str(strref) - # We pushed the digits with least significant digits coming first, but - # the number should have least significant digits at the end, so reverse - # the order of the digit characters in the string. - fmt._reverse(start=first_digit_pos) + return None diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index b93187e917..f04fce4db4 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -20,6 +20,7 @@ from collections import KeyElement from builtin.hash import _hash_simd from builtin.string import _calc_initial_buffer_size from builtin.io import _snprintf +from builtin.hex import _try_write_int from utils._visualizers import lldb_formatter_wrapping_type from utils import StaticIntTuple @@ -308,30 +309,39 @@ struct Int(Intable, Stringable, KeyElement, Boolable, Formattable): writer: The formatter to write to. """ - # Stack allocate enough bytes to store any formatted 64-bit integer - alias size: Int = 32 - - var buf = _ArrayMem[Int8, size]() - - # Format the integer to the local byte array - var len = _snprintf( - rebind[UnsafePointer[Int8]](buf.as_ptr()), - size, - "%li", - self.value, - ) + @parameter + if triple_is_nvidia_cuda(): + var err = _try_write_int(writer, Int64(self)) + if err: + abort( + "unreachable: unexpected write int failure condition: " + + str(err.value()) + ) + else: + # Stack allocate enough bytes to store any formatted 64-bit integer + alias size: Int = 32 + + var buf = _ArrayMem[Int8, size]() + + # Format the integer to the local byte array + var len = _snprintf( + rebind[UnsafePointer[Int8]](buf.as_ptr()), + size, + "%li", + self.value, + ) - # Create a StringRef that does NOT include the NUL terminator written - # to the buffer. - # - # Write the formatted integer to the formatter. - # - # SAFETY: - # `buf` is kept alive long enough for the use of this StringRef. - writer.write_str(StringRef(buf.as_ptr(), len)) - - # Keep buf alive until we've finished with the StringRef - _ = buf^ + # Create a StringRef that does NOT include the NUL terminator written + # to the buffer. + # + # Write the formatted integer to the formatter. + # + # SAFETY: + # `buf` is kept alive long enough for the use of this StringRef. + writer.write_str(StringRef(buf.as_ptr(), len)) + + # Keep buf alive until we've finished with the StringRef + _ = buf^ @always_inline("nodebug") fn __mlir_index__(self) -> __mlir_type.index: diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 3f66b01ace..61920e2b14 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -319,20 +319,28 @@ fn _put(x: StringRef): if not str_len: return - alias MAX_STR_LEN = 0x1000_0000 + @parameter + if triple_is_nvidia_cuda(): + var tmp = 0 + var arg_ptr = Pointer.address_of(tmp) + _ = external_call["vprintf", Int32]( + x.data, arg_ptr.bitcast[Pointer[NoneType]]() + ) + else: + alias MAX_STR_LEN = 0x1000_0000 - # The string can be printed, so that's fine. - if str_len < MAX_STR_LEN: - _printf("%.*s", x.length, x.data) - return + # The string can be printed, so that's fine. + if str_len < MAX_STR_LEN: + _printf("%.*s", x.length, x.data) + return - # The string is large, then we need to chunk it. - var p = x.data - while str_len: - var ll = _min(str_len, MAX_STR_LEN) - _printf("%.*s", ll, p) - str_len -= ll - p += ll + # The string is large, then we need to chunk it. + var p = x.data + while str_len: + var ll = _min(str_len, MAX_STR_LEN) + _printf("%.*s", ll, p) + str_len -= ll + p += ll @no_inline @@ -451,5 +459,8 @@ fn _print_fmt[ write_to(writer, end) - if flush: - _flush() + # TODO: What is a flush function that works on CUDA? + @parameter + if not triple_is_nvidia_cuda(): + if flush: + _flush() diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index e74b71b7e4..b3b6566da7 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -19,7 +19,11 @@ from sys import sizeof from memory import memcpy +from collections import Optional + from utils import StaticTuple, Variant +from utils._format import ToFormatter + # ===----------------------------------------------------------------------===# # InlinedString @@ -261,7 +265,9 @@ struct InlinedString(Sized, Stringable, CollectionElement): @value -struct _FixedString[CAP: Int](Sized, Stringable, CollectionElement): +struct _FixedString[CAP: Int]( + Sized, Stringable, Formattable, ToFormatter, CollectionElement +): """A string with a fixed available capacity. The string data is stored inline in this structs memory layout. @@ -339,17 +345,24 @@ struct _FixedString[CAP: Int](Sized, Stringable, CollectionElement): Args: strref: The string to append. """ + var err = self._iadd_non_raising(strref) + if err: + raise err.value() + + fn _iadd_non_raising(inout self, strref: StringRef) -> Optional[Error]: var total_len = len(self) + len(strref) # Ensure there is sufficient capacity to append `strref` if total_len > CAP: - raise Error( - "Insufficient capacity to append len=" - + str(len(strref)) - + " string to len=" - + str(len(self)) - + " FixedString with capacity=" - + str(CAP), + return Optional( + Error( + "Insufficient capacity to append len=" + + str(len(strref)) + + " string to len=" + + str(len(self)) + + " FixedString with capacity=" + + str(CAP), + ) ) # Append the bytes from `strref` at the end of the current string @@ -357,10 +370,63 @@ struct _FixedString[CAP: Int](Sized, Stringable, CollectionElement): self.size = total_len + return None + + fn format_to(self, inout writer: Formatter): + writer.write_str(self._strref_dangerous()) + + fn _unsafe_to_formatter(inout self) -> Formatter: + fn write_to_string(ptr0: UnsafePointer[NoneType], strref: StringRef): + var ptr: UnsafePointer[Self] = ptr0.bitcast[Self]() + + # FIXME(#37990): + # Use `ptr[] += strref` and remove _iadd_non_raising after + # "failed to fold operation lit.try" is fixed. + # try: + # ptr[] += strref + # except e: + # abort("error formatting to FixedString: " + str(e)) + var err = ptr[]._iadd_non_raising(strref) + if err: + abort("error formatting to FixedString: " + str(err.value())) + + return Formatter( + write_to_string, + # Arg data + UnsafePointer.address_of(self).bitcast[NoneType](), + ) + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # + @staticmethod + fn format_sequence[*Ts: Formattable](*args: *Ts) -> Self: + """ + Construct a string by concatenating a sequence of formattable arguments. + + Args: + args: A sequence of formattable arguments. + + Parameters: + Ts: The types of the arguments to format. Each type must be satisfy + `Formattable`. + + Returns: + A string formed by formatting the argument sequence. + """ + + var output = Self() + var writer = output._unsafe_to_formatter() + + @parameter + fn write_arg[T: Formattable](arg: T): + arg.format_to(writer) + + args.each[write_arg]() + + return output^ + fn as_ptr(self) -> DTypePointer[DType.int8]: """Retrieves a pointer to the underlying memory. @@ -426,6 +492,12 @@ struct _ArrayMem[ElementType: AnyRegType, SIZE: Int](Sized): """ return SIZE + fn __setitem__(inout self, index: Int, owned value: ElementType): + var ptr = __mlir_op.`pop.array.gep`( + UnsafePointer(Reference(self.storage.array)).address, index.value + ) + __mlir_op.`pop.store`(value, ptr) + # ===------------------------------------------------------------------=== # # Methods # ===------------------------------------------------------------------=== # diff --git a/stdlib/test/builtin/test_hex.mojo b/stdlib/test/builtin/test_hex.mojo index 36ed72f5b6..938e8e7e1c 100644 --- a/stdlib/test/builtin/test_hex.mojo +++ b/stdlib/test/builtin/test_hex.mojo @@ -26,19 +26,26 @@ fn test_format_int() raises: assert_equal(_format_int(-123, 10), "-123") assert_equal(_format_int(-999_999_999, 10), "-999999999") + # # Max and min i64 values in base 10 + # + + assert_equal(_format_int(Int64.MAX_FINITE, 10), "9223372036854775807") + + assert_equal(_format_int(Int64.MIN_FINITE, 10), "-9223372036854775808") + + # + # Max and min i64 values in base 2 + # + assert_equal( - # TODO(#35504): Use max_finite() here again - # _format_int(max_finite[DType.int64](), 10), "9223372036854775807" - _format_int(Int(9223372036854775807), 10), - "9223372036854775807", + _format_int(Int64.MAX_FINITE, 2), + "111111111111111111111111111111111111111111111111111111111111111", ) assert_equal( - # TODO(#35504): Use min_finite() here again - # _format_int(min_finite[DType.int64](), 10), "-9223372036854775808" - _format_int(Int(-9223372036854775808), 10), - "-9223372036854775808", + _format_int(Int64.MIN_FINITE, 2), + "-1000000000000000000000000000000000000000000000000000000000000000", ) @@ -52,9 +59,7 @@ fn test_hex() raises: assert_equal(hex(1 << 16), "0x10000") # Max and min i64 values in base 16 - # TODO(#35504): Use max_finite() here again - # assert_equal(hex(max_finite[DType.int64]()), "0x7fffffffffffffff") - assert_equal(hex(Int(9223372036854775807)), "0x7fffffffffffffff") + assert_equal(hex(Int64.MAX_FINITE), "0x7fffffffffffffff") # # Negative values @@ -65,9 +70,7 @@ fn test_hex() raises: assert_equal(hex(-10), "-0xa") assert_equal(hex(-255), "-0xff") - # TODO(#35504): Use min_finite() here again - # assert_equal(hex(min_finite[DType.int64]()), "-0x8000000000000000") - assert_equal(hex(Int(-9223372036854775808)), "-0x8000000000000000") + assert_equal(hex(Int64.MIN_FINITE), "-0x8000000000000000") def main(): diff --git a/stdlib/test/utils/test_format.mojo b/stdlib/test/utils/test_format.mojo index 0ad832f629..9427f2787d 100644 --- a/stdlib/test/utils/test_format.mojo +++ b/stdlib/test/utils/test_format.mojo @@ -13,6 +13,7 @@ # RUN: %mojo -debug-level full %s from utils._format import Formattable, write_to, Formatter +from utils.inlined_string import _FixedString from testing import assert_equal @@ -21,6 +22,8 @@ fn main() raises: test_string_format_seq() test_stringable_based_on_format() + test_formatter_of_fixed_string() + @value struct Point(Formattable, Stringable): @@ -65,3 +68,10 @@ fn test_string_format_seq() raises: fn test_stringable_based_on_format() raises: assert_equal(str(Point(10, 11)), "Point(10, 11)") + + +fn test_formatter_of_fixed_string() raises: + var s1 = _FixedString[100]() + var s1_fmt = Formatter(s1) + write_to(s1_fmt, "Hello, World!") + assert_equal(s1, "Hello, World!") From fd1cc6242cd2930d5f1053382f19a508714d5e38 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:50:37 -0500 Subject: [PATCH 0236/2019] [External][stdlib] Removed FileCheck in `test_linux_target.mojo` (#2286) (#38173) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 259ff1078c3ac836c9b64d834b0a2f3c62ad48aa --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: e7d11af3de12f714c8b90ccebe4c64fe310c942c --- stdlib/test/sys/test_linux_target.mojo | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/stdlib/test/sys/test_linux_target.mojo b/stdlib/test/sys/test_linux_target.mojo index 6c610219b9..b84943137c 100644 --- a/stdlib/test/sys/test_linux_target.mojo +++ b/stdlib/test/sys/test_linux_target.mojo @@ -15,22 +15,16 @@ # # ===----------------------------------------------------------------------=== # # REQUIRES: linux -# RUN: %mojo %s | FileCheck %s - +# RUN: %mojo %s from sys import os_is_linux, os_is_macos +from testing import assert_false, assert_true -# CHECK-LABEL: test_os_query -fn test_os_query(): - print("== test_os_query") - - # CHECK: False - print(os_is_macos()) - - # CHECK: True - print(os_is_linux()) +def test_os_query(): + assert_false(os_is_macos()) + assert_true(os_is_linux()) -fn main(): +def main(): test_os_query() From 7bb3db2c8c425fe80f7696c204c893cc92a54506 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:50:46 -0500 Subject: [PATCH 0237/2019] [External][stdlib] Remove FileCheck from `test_file.mojo` (#2283) (#38176) Related to #2024 Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 76e4cecdf3d025295d5cc60601cfdc001cefaa21 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 02e32e0b59a23dbf66ad6dc4cbab2d30cff0645d --- stdlib/src/builtin/string.mojo | 4 + stdlib/test/builtin/test_file.mojo | 120 +++++++++++++---------------- 2 files changed, 59 insertions(+), 65 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 57bb9a5983..c25de28a7f 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -365,6 +365,10 @@ struct String( Args: impl: The buffer. """ + debug_assert( + impl[-1] == 0, + "expected last element of String buffer to be null terminator", + ) self._buffer = impl^ @always_inline diff --git a/stdlib/test/builtin/test_file.mojo b/stdlib/test/builtin/test_file.mojo index b3582f9572..6ec549471b 100644 --- a/stdlib/test/builtin/test_file.mojo +++ b/stdlib/test/builtin/test_file.mojo @@ -10,117 +10,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo -D CURRENT_DIR=%S -D TEMP_FILE_DIR=%T -debug-level full %s | FileCheck %s +# RUN: %mojo -D CURRENT_DIR=%S -D TEMP_FILE_DIR=%T -debug-level full %s from pathlib import Path from sys import os_is_windows, env_get_string -from testing import assert_equal +from testing import assert_equal, assert_true alias CURRENT_DIR = env_get_string["CURRENT_DIR"]() alias TEMP_FILE_DIR = env_get_string["TEMP_FILE_DIR"]() -# CHECK-LABEL: test_file_read def test_file_read(): - print("== test_file_read") - - # CHECK: Lorem ipsum dolor sit amet, consectetur adipiscing elit. var f = open( Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r", ) - print(f.read()) + assert_true( + f.read().startswith( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ) + ) f.close() -# CHECK-LABEL: test_file_read_multi def test_file_read_multi(): - print("== test_file_read_multi") - var f = open( (Path(CURRENT_DIR) / "test_file_dummy_input.txt"), "r", ) - # CHECK: Lorem ipsum - print(f.read(12)) - - # CHECK: dolor - print(f.read(6)) - - # CHECK: sit amet, consectetur adipiscing elit. - print(f.read()) + assert_equal(f.read(12), "Lorem ipsum ") + assert_equal(f.read(6), "dolor ") + assert_true(f.read().startswith("sit amet, consectetur adipiscing elit.")) f.close() -# CHECK-LABEL: test_file_read_bytes_multi def test_file_read_bytes_multi(): - print("== test_file_read_bytes_multi") - - var f = open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") + var f = open( + Path(CURRENT_DIR) / "test_file_dummy_input.txt", + "r", + ) - # CHECK: Lorem ipsum var bytes1 = f.read_bytes(12) - print(String(bytes1)) + assert_equal(len(bytes1), 12, "12 bytes") + # we add the null terminator + bytes1.append(0) + var string1 = String(bytes1) + assert_equal(len(string1), 12, "12 chars") + assert_equal(string1, String("Lorem ipsum ")) - # CHECK: dolor var bytes2 = f.read_bytes(6) - print(String(bytes2)) + assert_equal(len(bytes2), 6, "6 bytes") + # we add the null terminator + bytes2.append(0) + var string2 = String(bytes2) + assert_equal(len(string2), 6, "6 chars") + assert_equal(string2, "dolor ") # Read where N is greater than the number of bytes in the file. var s: String = f.read(1e9) - # CHECK: 936 - print(len(s)) - - # CHECK: sit amet, consectetur adipiscing elit. - print(s) + assert_equal(len(s), 936) + assert_true(s.startswith("sit amet, consectetur adipiscing elit.")) f.close() -# CHECK-LABEL: test_file_read_path def test_file_read_path(): - print("== test_file_read_path") - var file_path = Path(CURRENT_DIR) / "test_file_dummy_input.txt" - # CHECK: Lorem ipsum dolor sit amet, consectetur adipiscing elit. var f = open(file_path, "r") - print(f.read()) + assert_true( + f.read().startswith( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ) + ) f.close() -# CHECK-LABEL: test_file_path_direct_read def test_file_path_direct_read(): - print("== test_file_path_direct_read") - var file_path = Path(CURRENT_DIR) / "test_file_dummy_input.txt" - # CHECK: Lorem ipsum dolor sit amet, consectetur adipiscing elit. - print(file_path.read_text()) + assert_true( + file_path.read_text().startswith( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ) + ) -# CHECK-LABEL: test_file_read_context def test_file_read_context(): - print("== test_file_read_context") - - # CHECK: Lorem ipsum dolor sit amet, consectetur adipiscing elit. with open( Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r", ) as f: - print(f.read()) + assert_true( + f.read().startswith( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ) + ) -# CHECK-LABEL: test_file_seek def test_file_seek(): - print("== test_file_seek") import os - with open(Path(CURRENT_DIR) / "test_file_dummy_input.txt", "r") as f: + with open( + Path(CURRENT_DIR) / "test_file_dummy_input.txt", + "r", + ) as f: var pos = f.seek(6) assert_equal(pos, 6) @@ -145,43 +143,35 @@ def test_file_seek(): assert_equal(str(e)[: len(expected_msg)], expected_msg) -# CHECK-LABEL: test_file_open_nodir def test_file_open_nodir(): - print("== test_file_open_nodir") var f = open(Path("test_file_open_nodir"), "w") f.close() -# CHECK-LABEL: test_file_write def test_file_write(): - print("== test_file_write") - + var content = "The quick brown fox jumps over the lazy dog" var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write" var f = open(TEMP_FILE, "w") - f.write("The quick brown fox jumps over the lazy dog") + f.write(content) f.close() - # CHECK: The quick brown fox jumps over the lazy dog var read_file = open(TEMP_FILE, "r") - print(read_file.read()) + assert_equal(read_file.read(), content) read_file.close() -# CHECK-LABEL: test_file_write_again def test_file_write_again(): - print("== test_file_write_again") - + var unexpected_content = "foo bar baz" + var expected_content = "foo bar" var TEMP_FILE = Path(TEMP_FILE_DIR) / "test_file_write_again" with open(TEMP_FILE, "w") as f: - f.write("foo bar baz") + f.write(unexpected_content) with open(TEMP_FILE, "w") as f: - f.write("foo bar") + f.write(expected_content) - # CHECK: foo bar - # CHECK-NOT: baz var read_file = open(TEMP_FILE, "r") - print(read_file.read()) + assert_equal(read_file.read(), expected_content) read_file.close() From 0cc63b3857ff9ba2128d1b4774fca520b517e4a5 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:50:55 -0500 Subject: [PATCH 0238/2019] [External][stdlib] Remove FileCheck from `test_anypointer.mojo` (#2305) (#38180) Related to #2024 Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 6b780fa4b94f8e5f3cfd359dfd19c738b213c1b8 --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: fe023a324baecb605ef3f8cacbc8b521486fb411 --- stdlib/test/memory/test_unsafepointer.mojo | 40 +++++++++++++++------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index c07fbc6671..6c41923a43 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s --dump-input=always +# RUN: %mojo %s from memory import UnsafePointer from memory.unsafe_pointer import move_from_pointee, move_pointee @@ -19,32 +19,46 @@ from testing import assert_equal, assert_not_equal, assert_true struct MoveOnlyType(Movable): + # It's a weak reference, we don't want to delete the actions + # after the struct is deleted, otherwise we can't observe the __del__. + var actions: UnsafePointer[List[String]] var value: Int - fn __init__(inout self, value: Int): + fn __init__(inout self, value: Int, actions: UnsafePointer[List[String]]): + self.actions = actions self.value = value + self.actions[0].append("__init__") fn __moveinit__(inout self, owned existing: Self): + self.actions = existing.actions self.value = existing.value - print("moved", self.value) + self.actions[0].append("__moveinit__") fn __del__(owned self): - print("deleted", self.value) + self.actions[0].append("__del__") -fn test_unsafepointer_of_move_only_type(): - # CHECK-LABEL: === test_unsafepointer - print("=== test_unsafepointer") +def test_unsafepointer_of_move_only_type(): + var actions_ptr = UnsafePointer[List[String]].alloc(1) + initialize_pointee(actions_ptr, List[String]()) var ptr = UnsafePointer[MoveOnlyType].alloc(1) - # CHECK: moved 42 - initialize_pointee(ptr, MoveOnlyType(42)) - # CHECK: moved 42 + initialize_pointee(ptr, MoveOnlyType(42, actions_ptr)) + assert_equal(len(actions_ptr[0]), 2) + assert_equal(actions_ptr[0][0], "__init__") + assert_equal(actions_ptr[0][1], "__moveinit__", msg="emplace_value") + assert_equal(ptr[0].value, 42) + var value = move_from_pointee(ptr) - # CHECK: value 42 - print("value", value.value) - # CHECK: deleted 42 + assert_equal(len(actions_ptr[0]), 3) + assert_equal(actions_ptr[0][2], "__moveinit__") + assert_equal(value.value, 42) + ptr.free() + assert_equal(len(actions_ptr[0]), 4) + assert_equal(actions_ptr[0][3], "__del__") + + actions_ptr.free() def test_unsafepointer_move_pointee_move_count(): From 86e3f38c0506ff8c3447aa16c696bcccd87dd63a Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:51:03 -0500 Subject: [PATCH 0239/2019] [External][stdlib] Remove FileCheck in `stdlib/test/builtin/test_slice.mojo` (#2292) (#38181) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 0663b7516b84c264c983eb9adfaa77d46e477c88 --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: 694dde80742a1c96a4db44777917c356f7d97d7f --- stdlib/test/builtin/test_slice.mojo | 44 ++++++++++++----------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/stdlib/test/builtin/test_slice.mojo b/stdlib/test/builtin/test_slice.mojo index faada7c197..ca874a3511 100644 --- a/stdlib/test/builtin/test_slice.mojo +++ b/stdlib/test/builtin/test_slice.mojo @@ -10,18 +10,16 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s -from testing import assert_equal, assert_false +from testing import assert_equal, assert_false, assert_true -# CHECK-LABEL: test_none_end_folds -fn test_none_end_folds(): - print("== test_none_end_folds") +def test_none_end_folds(): alias all_def_slice = slice(0, None, 1) - # CHECK: 0 - # CHECK-SAME: 1 - print(all_def_slice.start, all_def_slice.end, all_def_slice.step) + assert_equal(all_def_slice.start, 0) + assert_true(all_def_slice.end, Int.MAX) + assert_equal(all_def_slice.step, 1) # This requires parameter inference of StartT. @@ -43,31 +41,25 @@ struct Slicable: fn __init__(inout self): pass - fn __getitem__(self, a: FunnySlice) -> Int: - print(a.upper) - print(a.stride) - return a.start + fn __getitem__(self, a: FunnySlice) -> FunnySlice: + return a - fn __getitem__(self, a: BoringSlice) -> String: - print(a.a) - print(a.b) - return a.c + fn __getitem__(self, a: BoringSlice) -> BoringSlice: + return a def test_slicable(): - # CHECK: Slicable - print("Slicable") var slicable = Slicable() - # CHECK: hello - # CHECK: 4.0 - # CHECK: 1 - print(slicable[1:"hello":4.0]) + var new_slice = slicable[1:"hello":4.0] + assert_equal(new_slice.start, 1) + assert_equal(new_slice.upper, "hello") + assert_equal(new_slice.stride, 4.0) - # CHECK: 1 - # CHECK: 2 - # CHECK: foo - print(slicable[1:2:"foo"]) + var boring_slice = slicable[1:2:"foo"] + assert_equal(boring_slice.a, 1) + assert_equal(boring_slice.b, 2) + assert_equal(boring_slice.c, "foo") def test_has_end(): From e676841ad2d597ca5589abe60a7ecd824184c4f7 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:51:13 -0500 Subject: [PATCH 0240/2019] [External][stdlib] Change `FileHandle.read_bytes()` to not copy data (#2319) (#38182) Augment `FileHandle.read_bytes()` to not copy the data read when constructing the returned `List[Int8]` by leveraging the new constructor added in https://github.com/modularml/mojo/pull/2182. Fixes #2051 Signed-off-by: Mohamed Mabrouk mojo-orig-commit: 0b344fbd14fa80a33a3c07bfccba3dce661262de --------- Co-authored-by: MoSafi2 <127411725+MoSafi2@users.noreply.github.com> Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: fcb2f1457eb6f2eb5a1c371fbdf24fca74c308ce --- stdlib/src/builtin/file.mojo | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 810c859460..983aed3675 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -337,12 +337,7 @@ struct FileHandle: if err_msg: raise (err_msg^).consume_as_error() - var list = List[Int8](capacity=int(size_copy)) - var list_ptr = UnsafePointer[Int8](address=int(list.data)) - - # Initialize the List elements and set the initialized size - memcpy(list_ptr, buf, int(size_copy)) - list.size = int(size_copy) + var list = List[Int8](buf, size=int(size_copy), capacity=int(size_copy)) return list From 6c8d203b88e59e3d52182d0db27ff3b90cf0632b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:51:34 -0500 Subject: [PATCH 0241/2019] [External][stdlib] Remove FileCheck for `test_bfloat16.mojo` (#2289) (#38185) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 1187746b4bbfaaecaa99513b3ad408bb03b16473 --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: be39890d33887cc9a2563f3f46ec140df07e1f33 --- stdlib/test/builtin/test_bfloat16.mojo | 59 +++++++++++++++----------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/stdlib/test/builtin/test_bfloat16.mojo b/stdlib/test/builtin/test_bfloat16.mojo index 2cd0f63884..e7a99eefab 100644 --- a/stdlib/test/builtin/test_bfloat16.mojo +++ b/stdlib/test/builtin/test_bfloat16.mojo @@ -10,11 +10,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from random import randn_float64 from sys import has_neon - from testing import assert_equal, assert_almost_equal @@ -35,24 +34,27 @@ def test_methods(): assert_almost_equal(BFloat16(2.0), 2.0) -fn test_bf_primitives(): +def test_bf_primitives(): # we have to use dynamic values, otherwise these get evaled at compile time. var a = randn_float64().cast[DType.bfloat16]() var b = randn_float64().cast[DType.bfloat16]() - print(a + b) - print(a - b) - print(a / b) - print(a * b) - print(a == b) - print(a != b) - print(a <= b) - print(a >= b) + # higher precision + var a_hp = a.cast[DType.float64]() + var b_hp = b.cast[DType.float64]() + assert_almost_equal(a + b, (a_hp + b_hp).cast[DType.bfloat16]()) + assert_almost_equal(a - b, (a_hp - b_hp).cast[DType.bfloat16]()) + assert_almost_equal(a / b, (a_hp / b_hp).cast[DType.bfloat16]()) + assert_almost_equal(a * b, (a_hp * b_hp).cast[DType.bfloat16]()) + assert_equal(a == b, a_hp == b_hp) + assert_equal(a != b, a_hp != b_hp) + assert_equal(a <= b, a_hp <= b_hp) + assert_equal(a >= b, a_hp >= b_hp) -def main(): - # CHECK: 33.0 - print( + +def check_float64_values(): + assert_equal( Float64( __mlir_op.`pop.cast`[_type = __mlir_type[`!pop.scalar`]]( __mlir_op.`kgen.param.constant`[ @@ -60,21 +62,30 @@ def main(): value = __mlir_attr[`#pop.simd<"33"> : !pop.scalar`], ]() ) - ) + ), + Float64(33.0), ) - # CHECK: nan - print( - Float64( - __mlir_op.`pop.cast`[_type = __mlir_type[`!pop.scalar`]]( - __mlir_op.`kgen.param.constant`[ - _type = __mlir_type[`!pop.scalar`], - value = __mlir_attr[`#pop.simd<"nan"> : !pop.scalar`], - ]() + assert_equal( + str( + Float64( + __mlir_op.`pop.cast`[_type = __mlir_type[`!pop.scalar`]]( + __mlir_op.`kgen.param.constant`[ + _type = __mlir_type[`!pop.scalar`], + value = __mlir_attr[ + `#pop.simd<"nan"> : !pop.scalar` + ], + ]() + ) ) - ) + ), + "nan", ) + +def main(): + check_float64_values() + # TODO re-enable this test when we sort out BF16 support for graviton3 #30525 @parameter if not has_neon(): From 44aec9879a2b118a505ae4c461a5ae5b84acfd76 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:51:43 -0500 Subject: [PATCH 0242/2019] [External][stdlib] Fix `Int.MIN` error (#2264) (#38186) Fix the compilation error caused when trying to access `Int.MIN`. Signed-off-by: Max Brylski mojo-orig-commit: c989d8338acd4f236cff88fce2a75f11bf9e9dca Co-authored-by: Helehex MODULAR_ORIG_COMMIT_REV_ID: 5140266afd0292e6f409b4b9ecbdab1d70bfea84 --- stdlib/src/builtin/dtype.mojo | 18 ++++++++++++++++++ stdlib/src/builtin/simd.mojo | 22 +++++++++------------- stdlib/src/sys/info.mojo | 26 ++++++++++++++++++++++++++ stdlib/test/builtin/test_simd.mojo | 19 +++++++++++++++++++ 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/stdlib/src/builtin/dtype.mojo b/stdlib/src/builtin/dtype.mojo index 816ff855a5..331e90f95d 100644 --- a/stdlib/src/builtin/dtype.mojo +++ b/stdlib/src/builtin/dtype.mojo @@ -333,6 +333,24 @@ struct DType(Stringable, KeyElement): """ return self.isa[DType.index]() + @always_inline("nodebug") + fn is_index32(self) -> Bool: + """Checks if this DType is Index and 32 bit. + + Returns: + True if this DType is Index and 32 bit, False otherwise. + """ + return self.is_index() and (self.sizeof() == DType.int32.sizeof()) + + @always_inline("nodebug") + fn is_index64(self) -> Bool: + """Checks if this DType is Index and 64 bit. + + Returns: + True if this DType is Index and 64 bit, False otherwise. + """ + return self.is_index() and (self.sizeof() == DType.int64.sizeof()) + @always_inline("nodebug") fn is_address(self) -> Bool: """Checks if this DType is Address. diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 862d83e444..8f3fdc46ef 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -105,6 +105,11 @@ fn _unchecked_zero[type: DType, size: Int]() -> SIMD[type, size]: } +# ===------------------------------------------------------------------------===# +# SIMD +# ===------------------------------------------------------------------------===# + + @lldb_formatter_wrapping_type @register_passable("trivial") struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @@ -2514,11 +2519,6 @@ fn _neginf[type: DType]() -> Scalar[type]: # ===----------------------------------------------------------------------===# -@always_inline("nodebug") -fn _is_32_bit_system() -> Bool: - return sizeof[DType.index]() == sizeof[DType.int32]() - - @always_inline fn _max_finite[type: DType]() -> Scalar[type]: """Returns the maximum finite value of type. @@ -2540,13 +2540,11 @@ fn _max_finite[type: DType]() -> Scalar[type]: return 32767 elif type == DType.uint16: return 65535 - elif type == DType.int32 or (type == DType.index and _is_32_bit_system()): + elif type == DType.int32 or type.is_index32(): return 2147483647 elif type == DType.uint32: return 4294967295 - elif type == DType.int64 or ( - type == DType.index and not _is_32_bit_system() - ): + elif type == DType.int64 or type.is_index64(): return 9223372036854775807 elif type == DType.uint64: return 18446744073709551615 @@ -2587,11 +2585,9 @@ fn _min_finite[type: DType]() -> Scalar[type]: return -128 elif type == DType.int16: return -32768 - elif type == DType.int32 or (type == DType.index and _is_32_bit_system()): + elif type == DType.int32 or type.is_index32(): return -2147483648 - elif type == DType.int64 or ( - type == DType.index and not _is_32_bit_system() - ): + elif type == DType.int64 or type.is_index64(): return -9223372036854775808 elif type.is_floating_point(): return -_max_finite[type]() diff --git a/stdlib/src/sys/info.mojo b/stdlib/src/sys/info.mojo index 5f325f6f46..4c3b4fe781 100644 --- a/stdlib/src/sys/info.mojo +++ b/stdlib/src/sys/info.mojo @@ -446,6 +446,32 @@ fn is_big_endian[ ] +@always_inline("nodebug") +fn is_32bit[target: __mlir_type.`!kgen.target` = _current_target()]() -> Bool: + """Returns True if the maximum integral value is 32 bit. + + Parameters: + target: The target architecture. + + Returns: + True if the maximum integral value is 32 bit, False otherwise. + """ + return sizeof[DType.index, target]() == sizeof[DType.int32, target]() + + +@always_inline("nodebug") +fn is_64bit[target: __mlir_type.`!kgen.target` = _current_target()]() -> Bool: + """Returns True if the maximum integral value is 64 bit. + + Parameters: + target: The target architecture. + + Returns: + True if the maximum integral value is 64 bit, False otherwise. + """ + return sizeof[DType.index, target]() == sizeof[DType.int64, target]() + + @always_inline("nodebug") fn simdbitwidth[ target: __mlir_type.`!kgen.target` = _current_target() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 2b77e33d2d..93cafc3e41 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -407,6 +407,24 @@ def test_extract(): ) +def test_limits(): + @parameter + fn test_integral_overflow[type: DType]() raises: + var max_value = Scalar[type].MAX + var min_value = Scalar[type].MIN + assert_equal(max_value + 1, min_value) + + test_integral_overflow[DType.index]() + test_integral_overflow[DType.int8]() + test_integral_overflow[DType.uint8]() + test_integral_overflow[DType.int16]() + test_integral_overflow[DType.uint16]() + test_integral_overflow[DType.int32]() + test_integral_overflow[DType.uint32]() + test_integral_overflow[DType.int64]() + test_integral_overflow[DType.uint64]() + + def main(): test_cast() test_simd_variadic() @@ -420,3 +438,4 @@ def main(): test_deinterleave() test_address() test_extract() + test_limits() From b34f82ea733b5a74d179939e91da4a822eba9864 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:51:51 -0500 Subject: [PATCH 0243/2019] [External][stdlib] Remove FileCheck from `test_unroll.mojo` (#2298) (#38187) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 8c68d642be603917fa4e5485b8b74c9a8890e73a --------- Co-authored-by: Gabriel de Marmiesse Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: ec000ee778a6a2e0734f750ca412993c10437edb --- stdlib/test/utils/test_unroll.mojo | 122 +++++++++++++++-------------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/stdlib/test/utils/test_unroll.mojo b/stdlib/test/utils/test_unroll.mojo index 784a3dcb21..382635effd 100644 --- a/stdlib/test/utils/test_unroll.mojo +++ b/stdlib/test/utils/test_unroll.mojo @@ -10,101 +10,105 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from utils import StaticIntTuple, unroll +from testing import assert_equal, assert_raises -# CHECK-LABEL: test_unroll -fn test_unroll(): - print("test_unroll") +def test_unroll(): + var indexes_seen = List[Int]() - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 - # CHECK: 3 @parameter fn func[idx: Int](): - print(idx) + indexes_seen.append(idx) unroll[func, 4]() + assert_equal(indexes_seen[0], 0) + assert_equal(indexes_seen[1], 1) + assert_equal(indexes_seen[2], 2) + assert_equal(indexes_seen[3], 3) + assert_equal(len(indexes_seen), 4) -# CHECK-LABEL: test_unroll -fn test_unroll2(): - print("test_unroll") - # CHECK: (0, 0) - # CHECK: (0, 1) - # CHECK: (1, 0) - # CHECK: (1, 1) +def test_unroll2(): + var static_tuples_seen = List[StaticIntTuple[2]]() + @parameter fn func[idx0: Int, idx1: Int](): - print(StaticIntTuple[2](idx0, idx1)) + static_tuples_seen.append(StaticIntTuple[2](idx0, idx1)) unroll[func, 2, 2]() + assert_equal(static_tuples_seen[0], StaticIntTuple[2](0, 0)) + assert_equal(static_tuples_seen[1], StaticIntTuple[2](0, 1)) + assert_equal(static_tuples_seen[2], StaticIntTuple[2](1, 0)) + assert_equal(static_tuples_seen[3], StaticIntTuple[2](1, 1)) + assert_equal(len(static_tuples_seen), 4) + + +def test_unroll3(): + var static_tuples_seen = List[StaticIntTuple[3]]() -# CHECK-LABEL: test_unroll -fn test_unroll3(): - print("test_unroll") - - # CHECK: (0, 0, 0) - # CHECK: (0, 0, 1) - # CHECK: (0, 0, 2) - # CHECK: (0, 1, 0) - # CHECK: (0, 1, 1) - # CHECK: (0, 1, 2) - # CHECK: (1, 0, 0) - # CHECK: (1, 0, 1) - # CHECK: (1, 0, 2) - # CHECK: (1, 1, 0) - # CHECK: (1, 1, 1) - # CHECK: (1, 1, 2) - # CHECK: (2, 0, 0) - # CHECK: (2, 0, 1) - # CHECK: (2, 0, 2) - # CHECK: (2, 1, 0) - # CHECK: (2, 1, 1) - # CHECK: (2, 1, 2) - # CHECK: (3, 0, 0) - # CHECK: (3, 0, 1) - # CHECK: (3, 0, 2) - # CHECK: (3, 1, 0) - # CHECK: (3, 1, 1) - # CHECK: (3, 1, 2) @parameter fn func[idx0: Int, idx1: Int, idx2: Int](): - print(StaticIntTuple[3](idx0, idx1, idx2)) + static_tuples_seen.append(StaticIntTuple[3](idx0, idx1, idx2)) unroll[func, 4, 2, 3]() + assert_equal(static_tuples_seen[0], StaticIntTuple[3](0, 0, 0)) + assert_equal(static_tuples_seen[1], StaticIntTuple[3](0, 0, 1)) + assert_equal(static_tuples_seen[2], StaticIntTuple[3](0, 0, 2)) + assert_equal(static_tuples_seen[3], StaticIntTuple[3](0, 1, 0)) + assert_equal(static_tuples_seen[4], StaticIntTuple[3](0, 1, 1)) + assert_equal(static_tuples_seen[5], StaticIntTuple[3](0, 1, 2)) + assert_equal(static_tuples_seen[6], StaticIntTuple[3](1, 0, 0)) + assert_equal(static_tuples_seen[7], StaticIntTuple[3](1, 0, 1)) + assert_equal(static_tuples_seen[8], StaticIntTuple[3](1, 0, 2)) + assert_equal(static_tuples_seen[9], StaticIntTuple[3](1, 1, 0)) + assert_equal(static_tuples_seen[10], StaticIntTuple[3](1, 1, 1)) + assert_equal(static_tuples_seen[11], StaticIntTuple[3](1, 1, 2)) + assert_equal(static_tuples_seen[12], StaticIntTuple[3](2, 0, 0)) + assert_equal(static_tuples_seen[13], StaticIntTuple[3](2, 0, 1)) + assert_equal(static_tuples_seen[14], StaticIntTuple[3](2, 0, 2)) + assert_equal(static_tuples_seen[15], StaticIntTuple[3](2, 1, 0)) + assert_equal(static_tuples_seen[16], StaticIntTuple[3](2, 1, 1)) + assert_equal(static_tuples_seen[17], StaticIntTuple[3](2, 1, 2)) + assert_equal(static_tuples_seen[18], StaticIntTuple[3](3, 0, 0)) + assert_equal(static_tuples_seen[19], StaticIntTuple[3](3, 0, 1)) + assert_equal(static_tuples_seen[20], StaticIntTuple[3](3, 0, 2)) + assert_equal(static_tuples_seen[21], StaticIntTuple[3](3, 1, 0)) + assert_equal(static_tuples_seen[22], StaticIntTuple[3](3, 1, 1)) + assert_equal(static_tuples_seen[23], StaticIntTuple[3](3, 1, 2)) + assert_equal(len(static_tuples_seen), 24) -# CHECK-LABEL: test_unroll_raises fn test_unroll_raises() raises: - print("test_unroll_raises") + var indexes_seen = List[Int]() - # CHECK: 0 - # CHECK: 1 - # CHECK: 2 - # CHECK: 3 @parameter fn func[idx: Int]() raises: - print(idx) + indexes_seen.append(idx) unroll[func, 4]() + assert_equal(indexes_seen[0], 0) + assert_equal(indexes_seen[1], 1) + assert_equal(indexes_seen[2], 2) + assert_equal(indexes_seen[3], 3) + assert_equal(len(indexes_seen), 4) + + indexes_seen = List[Int]() - # CHECK: 0 @parameter fn func2[idx: Int]() raises: - print(idx) + indexes_seen.append(idx) raise "Exception" - try: + with assert_raises(contains="Exception"): unroll[func2, 4]() - except e: - # CHECK: raised Exception - print("raised " + str(e)) + + assert_equal(indexes_seen[0], 0) + assert_equal(len(indexes_seen), 1) fn main() raises: From a7b1f02c365b92f12d254616d56c1d9bc8301509 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:52:07 -0500 Subject: [PATCH 0244/2019] [External][stdlib] Remove FileCheck from `test_random.mojo` (#2299) (#38188) Related to #2024 Signed-off-by: Gabriel de Marmiesse mojo-orig-commit: 457378c3c5bafa287d3fc6be3a913e71e2233231 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 58c17994244d682d61acdd20a98ccdbd3524ba21 --- stdlib/test/random/test_random.mojo | 99 ++++++++++++++++------------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/stdlib/test/random/test_random.mojo b/stdlib/test/random/test_random.mojo index 4695f32211..1238277111 100644 --- a/stdlib/test/random/test_random.mojo +++ b/stdlib/test/random/test_random.mojo @@ -10,55 +10,68 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from random import randn_float64, random_float64, random_si64, random_ui64, seed - - -# CHECK-LABEL: test_random -fn test_random(): - print("== test_random") - - # CHECK-LABEL: random_float64 = - print("random_float64 = ", random_float64(0, 1)) - - # CHECK-LABEL: random_si64 = - print("random_si64 = ", random_si64(-255, 255)) - - # CHECK-LABEL: random_ui64 = - print("random_ui64 = ", random_ui64(0, 255)) - - # CHECK-LABEL: randn_float64 = - print("randn_float64 = ", randn_float64(0, 1)) - - -# CHECK-LABEL: test_seed -fn test_seed(): - print("== test_seed") - +from testing import assert_true, assert_equal + + +def test_random(): + for _ in range(100): + var random_float = random_float64(0, 1) + assert_true( + random_float >= 0, + "Value " + str(random_float) + " is not above or equal to 0", + ) + assert_true( + random_float <= 1, + "Value " + str(random_float) + " is not below or equal to 1", + ) + + var random_signed = random_si64(-255, 255) + assert_true( + random_signed >= -255, + "Signed value " + + str(random_signed) + + " is not above or equal to -255", + ) + assert_true( + random_signed <= 255, + "Signed value " + + str(random_signed) + + " is not below or equal to 255", + ) + + var random_unsigned = random_ui64(0, 255) + assert_true( + random_unsigned >= 0, + "Unsigned value " + + str(random_unsigned) + + " is not above or equal to 0", + ) + assert_true( + random_unsigned <= 255, + "Unsigned value " + + str(random_unsigned) + + " is not below or equal to 255", + ) + + var random_normal = randn_float64(0, 1) + # it's quite hard to verify that the values returned are forming a normal distribution + + +def test_seed(): seed(5) - - # CHECK: random_seed_float64 = [[FLOAT64:.*]] - print("random_seed_float64 = ", random_float64(0, 1)) - - # CHECK: random_seed_si64 = [[SI64:.*]] - print("random_seed_si64 = ", random_si64(-255, 255)) - - # CHECK: random_seed_ui64 = [[UI64:.*]] - print("random_seed_ui64 = ", random_ui64(0, 255)) + var some_float = random_float64(0, 1) + var some_signed_integer = random_si64(-255, 255) + var some_unsigned_integer = random_ui64(0, 255) seed(5) - - # CHECK: random_seed_float64 = [[FLOAT64]] - print("random_seed_float64 = ", random_float64(0, 1)) - - # CHECK: random_seed_si64 = [[SI64]] - print("random_seed_si64 = ", random_si64(-255, 255)) - - # CHECK: random_seed_ui64 = [[UI64]] - print("random_seed_ui64 = ", random_ui64(0, 255)) + assert_equal(some_float, random_float64(0, 1)) + assert_equal(some_signed_integer, random_si64(-255, 255)) + assert_equal(some_unsigned_integer, random_ui64(0, 255)) -fn main(): +def main(): test_random() test_seed() From f4b74c337d748858ec6621657516df2e515a22e7 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Fri, 19 Apr 2024 16:52:14 -0500 Subject: [PATCH 0245/2019] [External][stdlib] Remove FileCheck in `test_aarch64_target.mojo` (#2290) (#38189) Related to https://github.com/modularml/mojo/issues/2024 Signed-off-by: Ilham Firdausi Putra mojo-orig-commit: e2dc80e68466e754a5f8e21e92a46e48beb3d663 --------- Co-authored-by: Ilham F Putra <31740013+ilhamfp@users.noreply.github.com> Co-authored-by: abdul dakkak MODULAR_ORIG_COMMIT_REV_ID: 1c599cfe2ffa531648fe15560afd2642a9654039 --- stdlib/test/sys/test_aarch64_target.mojo | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/stdlib/test/sys/test_aarch64_target.mojo b/stdlib/test/sys/test_aarch64_target.mojo index 26ac318ae9..b31b98e4ff 100644 --- a/stdlib/test/sys/test_aarch64_target.mojo +++ b/stdlib/test/sys/test_aarch64_target.mojo @@ -14,24 +14,19 @@ # COM: TODO (17471): Not all aarch64 have neon, so we need to guard against that, # for now just require apple-silicon. # REQUIRES: apple-silicon -# RUN: %mojo -debug-level %s | FileCheck %s +# RUN: %mojo %s from sys import alignof, has_avx512f, has_neon, simdbitwidth +from testing import assert_false, assert_true, assert_equal -# CHECK-LABEL: test_arch_query -fn test_arch_query(): - print("== test_arch_query") +def test_arch_query(): + assert_true(has_neon()) - # CHECK: True - print(has_neon()) + assert_equal(simdbitwidth(), 128) - # CHECK: 128 - print(simdbitwidth()) + assert_false(has_avx512f()) - # CHECK: False - print(has_avx512f()) - -fn main(): +def main(): test_arch_query() From 5e7c1c3802a127bfe081e198cb5da3069d2e40e5 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 19 Apr 2024 18:07:42 -0500 Subject: [PATCH 0246/2019] hotfix: Disable test_inlined_string.mojo which is breaking post submit CI (#38221) See also: [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 6ebab84443aa522511103fac71b0e1af509c599b --- stdlib/test/utils/test_inlined_string.mojo | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/test/utils/test_inlined_string.mojo b/stdlib/test/utils/test_inlined_string.mojo index 7ef65b9504..0c951b36a1 100644 --- a/stdlib/test/utils/test_inlined_string.mojo +++ b/stdlib/test/utils/test_inlined_string.mojo @@ -10,6 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # +# REQUIRES: disabled # RUN: %mojo --debug-level full %s from testing import assert_equal, assert_true From f2672a7d0ab82bdb02a0d14192f5e1a395835b68 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 17:38:02 -0600 Subject: [PATCH 0247/2019] [docs] Add changelog entry for `String.remove(prefix|suffix)` (#38213) Add changelog entry for supporting `String.removeprefix` and `String.removesuffix` added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: a3999dbcdf8d55a8c118669173773377bfe5281d --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index d64b587c36..d496e4020d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -73,6 +73,8 @@ what we publish. - `Dict` now has a `update()` method to update keys/values from another `Dict`. +- `String` now has `removeprefix()` and `removesuffix()` methods. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From a2c5ca4141f282d39ea4ff580e5c115ce005a4d9 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 17:45:05 -0600 Subject: [PATCH 0248/2019] [docs] Add changelog entry for `Optional.is` and `isnot`. (#38215) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 938ba9d01b2d7070c9e16075bbef66dfc0d56874 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index d496e4020d..9474c64f5a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -75,6 +75,9 @@ what we publish. - `String` now has `removeprefix()` and `removesuffix()` methods. +- `Optional` now implements `__is__` and `__isnot__` methods so that you can compare + an `Optional` with `None`, e.g. `Optional(1) is not None` for example. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From 0af6b5714dd65f0c65891de344aa9c550e4bd202 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 17:56:07 -0600 Subject: [PATCH 0249/2019] [docs] Add changelog entry for `ord` and `chr` improvements (#38216) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 8228f1c3683d9f3ad12107128fb7fc536ab97cfb --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9474c64f5a..5762bfb35a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,8 @@ what we publish. - `Optional` now implements `__is__` and `__isnot__` methods so that you can compare an `Optional` with `None`, e.g. `Optional(1) is not None` for example. +- The `ord` and `chr` functions have been improved to accept any Unicode character. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From 71803d4f915caf7068de0108feada1e7bbe01530 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 18:01:00 -0600 Subject: [PATCH 0250/2019] [docs] Add changelog entry for `Atomic` move constructor (#38217) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 55945bcd4a1914ee6881db8e4e3a5bc4b3c016dd --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5762bfb35a..3e86448974 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -80,6 +80,8 @@ what we publish. - The `ord` and `chr` functions have been improved to accept any Unicode character. +- `Atomic` is now movable. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From d2721b12ce2d1de361303a127fc206e905952a84 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 18:14:56 -0600 Subject: [PATCH 0251/2019] [docs] Add `List.resize` changelog entry (#38220) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 3d62d4ffc806d20b45c0391d94119bfb9651787d --- docs/changelog.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3e86448974..8fdf7de83b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -67,9 +67,11 @@ what we publish. print(d) # prints `{'foo': [1, 2, 'something else'], 7: 'bar'}` ``` -- `List` now has a `pop(index)` API for removing an element - at a particular index. By default, `List.pop()` removes the last element - in the list. +- `List` now has several new methods: + - `pop(index)` for removing an element at a particular index. + By default, `List.pop()` removes the last element in the list. + - `resize(new_size)` for resizing the list without the need to + specify an additional value. - `Dict` now has a `update()` method to update keys/values from another `Dict`. From 0ee4ff29c48d5a290089269525354a58f27bb16f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 19:53:30 -0600 Subject: [PATCH 0252/2019] [docs] Add changelog entry for `atol` whitespace handling (#38226) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 033d02955c1765bbd3ccb975354a4553cce10616 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 8fdf7de83b..7803ee3bb6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -84,6 +84,9 @@ what we publish. - `Atomic` is now movable. +- `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` + instead of raising an error. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From aacf503a638ea979f1d994e8e9c6bfafc791d3f6 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 19:58:35 -0600 Subject: [PATCH 0253/2019] [docs] Add changelog entry for `List.insert(index, value)` (#38227) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 398a3ae4fbb4692fefda3c10ecaf94f72030c094 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7803ee3bb6..b6dc96a7cd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -72,6 +72,8 @@ what we publish. By default, `List.pop()` removes the last element in the list. - `resize(new_size)` for resizing the list without the need to specify an additional value. + - `insert(index, value)` for inserting a value at a specified index + into the `List`. - `Dict` now has a `update()` method to update keys/values from another `Dict`. From be7f0e3475c4df389918b2aea7eca2b493a1c01f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:09:49 -0600 Subject: [PATCH 0254/2019] [docs] Add changelog entry for `List(ptr, size, capacity)` ctor (#38228) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: d4ae7ba76c47797de1ce86e3fd2ff7aa63653969 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b6dc96a7cd..46b99cf00f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -74,6 +74,8 @@ what we publish. specify an additional value. - `insert(index, value)` for inserting a value at a specified index into the `List`. + - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep + copy of an existing contiguous memory allocation when constructing a new `List`. - `Dict` now has a `update()` method to update keys/values from another `Dict`. From 0151cedd1c3a0677f505e1c231da8c6fdb036852 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:13:51 -0600 Subject: [PATCH 0255/2019] [docs] Add changelog entry for `SIMD.__rmod__` (#38229) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: c0e3d5df5f99d136d6c781478d6877ea2bfffeb8 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 46b99cf00f..e5c7ace9a8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -91,6 +91,8 @@ what we publish. - `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` instead of raising an error. +- `SIMD` now implements `__rmod__`. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From 93aba9fa88e7a7689dfc7b59fbdeff24f2818276 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:17:06 -0600 Subject: [PATCH 0256/2019] [docs] Add changelog entry for `List/Dict` `Boolable` (#38230) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: dd63da0bbdd31c5d66a44aed9ffd99fbb0590156 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e5c7ace9a8..94b688b4b4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -88,6 +88,8 @@ what we publish. - `Atomic` is now movable. +- `Dict` and `List` are both `Boolable` now. + - `atol` now handles whitespaces so `int(String( " 10 "))` gives back `10` instead of raising an error. From ff4ad95b2accd45de6302b3c89c00ef44dfc7a67 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:20:24 -0600 Subject: [PATCH 0257/2019] [docs] Add changelog entry for named `Set` methods (#38231) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: afa3b35994443a16787277ce9d523d1a0c21305f --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 94b688b4b4..d4f052bd98 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -79,6 +79,12 @@ what we publish. - `Dict` now has a `update()` method to update keys/values from another `Dict`. +- `Set` now has named methods for set operations: + - `Set.difference()` mapping to `-` + - `Set.difference_update()` mapping to `-=` + - `Set.intersection_update()` mapping to `&=` + - `Set.update()` mapping to `|=` + - `String` now has `removeprefix()` and `removesuffix()` methods. - `Optional` now implements `__is__` and `__isnot__` methods so that you can compare From 9e734a40503c1a94e09950fed42f48ad3c6be789 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:25:26 -0600 Subject: [PATCH 0258/2019] [docs] Add changelog entry for `bool(None)` support (#38232) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: 85edf83ff2435cfca364c7c3963fb34a1f109a26 --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index d4f052bd98..084a10f2c7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -101,6 +101,8 @@ what we publish. - `SIMD` now implements `__rmod__`. +- `bool(None)` is now implemented. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From 2038d455cd6d90d40fa781df81a155ce4b42e14c Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:28:58 -0600 Subject: [PATCH 0259/2019] [docs] Add changelog entry for `assert_equal[SIMD]()` bug fix (#38233) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: b24b6f9f5afc92cb3c680875f80381224abfaf9c --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 084a10f2c7..cf9860355c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -109,6 +109,9 @@ what we publish. low-level logic but isn't designed for general usability and will likely change in the future. +- The `testing.assert_equal[SIMD]()` now raises if any of the elements + mismatch in the two `SIMD` arguments being compared. + - The `testing.assert_almost_equal` and `math.isclose` functions now have an `equal_nan` flag. When set to True, then NaNs are considered equal. From 5650f11c025cb187b44ba44cfbfb79c987df909f Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:33:01 -0600 Subject: [PATCH 0260/2019] [docs] Add changelog entry for `DTypePointer.gather|scatter` (#38234) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: f1f2f64ebbfeb044b5044cb65ffd90e82f0c5e47 --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index cf9860355c..9c90a1e153 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -103,6 +103,10 @@ what we publish. - `bool(None)` is now implemented. +- The `DTypePointer` type now implements `gather` for gathering a `SIMD` + vector from offsets of a current pointer. Similarly, support for `scatter` + was added to scatter a `SIMD` vector into offsets of the current pointer. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From 6a3d932dffaff713a6eb726da220f81f6de3a4e7 Mon Sep 17 00:00:00 2001 From: Joe Loser Date: Fri, 19 Apr 2024 20:35:52 -0600 Subject: [PATCH 0261/2019] [docs] Add changelog entry for `len` for unary range (#38235) Added in [Internal link] MODULAR_ORIG_COMMIT_REV_ID: b627d2115c8e28b9c1fd6ab012c936e282af4446 --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9c90a1e153..b4bd3f4c31 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -107,6 +107,9 @@ what we publish. vector from offsets of a current pointer. Similarly, support for `scatter` was added to scatter a `SIMD` vector into offsets of the current pointer. +- The `len` function for unary `range` with negative end values has been fixed + so that things like `len(range(-1))` work correctly now. + - A low-level `__get_mvalue_as_litref(x)` builtin was added to give access to the underlying memory representation as a `!lit.ref` value without checking initialization status of the underlying value. This is useful in very From eaed92fa3dd89e34fce9dc1f9867e1e15b2139da Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Fri, 19 Apr 2024 21:41:19 -0500 Subject: [PATCH 0262/2019] [stdlib] feature: Add `Reference._unsafe_from_pointer` to wrap `lit.ref.from_pointer` (#38127) This wraps the `lit.ref.from_pointer` MLIR op in a named, unsafe sounding, API. MODULAR_ORIG_COMMIT_REV_ID: c591e64446bac843d866ff0d474d2b84f632b974 --- stdlib/src/collections/list.mojo | 7 ++++--- stdlib/src/memory/_arc.mojo | 7 ++----- stdlib/src/memory/reference.mojo | 23 +++++++++++++++++++++++ stdlib/src/memory/unsafe_pointer.mojo | 12 +++++------- stdlib/src/utils/variant.mojo | 6 +++--- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 5260dc7c6a..9dc2ea5727 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -521,9 +521,10 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): # This means we can't just use `UnsafePointer.__refitem__` here # because the mutability won't match. var base_ptr = Reference(self)[].data - return __mlir_op.`lit.ref.from_pointer`[ - _type = Reference[T, mutability, self_life]._mlir_type - ]((base_ptr + normalized_idx).address) + var offset_ptr = base_ptr + normalized_idx + return Reference[T, mutability, self_life]._unsafe_from_pointer( + offset_ptr + ) fn __iter__[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index 5bfc0dba15..c98a23e235 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -147,11 +147,8 @@ struct Arc[T: Movable](CollectionElement): Returns: A Reference to the managed value. """ - alias RefType = Reference[T, mutability, lifetime] - return RefType( - __mlir_op.`lit.ref.from_pointer`[_type = RefType._mlir_type]( - Reference(self)[]._data_ptr().address - ) + return Reference[T, mutability, lifetime]._unsafe_from_pointer( + Reference(self)[]._data_ptr() ) fn _data_ptr(self) -> UnsafePointer[T]: diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index a7ac6ed515..875a3f1e76 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -220,6 +220,10 @@ struct Reference[ var value: Self._mlir_type """The underlying MLIR reference.""" + # ===------------------------------------------------------------------===# + # Initializers + # ===------------------------------------------------------------------===# + @always_inline("nodebug") fn __init__(inout self, value: Self._mlir_type): """Constructs a Reference from the MLIR reference. @@ -229,6 +233,21 @@ struct Reference[ """ self.value = value + @always_inline("nodebug") + @staticmethod + fn _unsafe_from_pointer( + ptr: UnsafePointer[type, address_space], + ) -> Reference[type, is_mutable, lifetime, address_space]: + return Reference( + __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_type]( + ptr.address + ) + ) + + # ===------------------------------------------------------------------===# + # Operator dunders + # ===------------------------------------------------------------------===# + @always_inline("nodebug") fn __refitem__(self) -> Self._mlir_type: """Enable subscript syntax `ref[]` to access the element. @@ -247,6 +266,10 @@ struct Reference[ """ return self.value + # ===------------------------------------------------------------------===# + # Methods + # ===------------------------------------------------------------------===# + # FIXME: This should be on Pointer, but can't due to AnyRefType vs AnyType # disagreement. Use UnsafePointer instead! @always_inline("nodebug") diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index c240f764d6..9f1e5d851a 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -48,12 +48,12 @@ struct UnsafePointer[ # We're unsafe, so we can have unsafe things. References we make have # an immortal mutable lifetime, since we can't come up with a meaningful # lifetime for them anyway. - alias _mlir_ref_type = Reference[ + alias _ref_type = Reference[ T, __mlir_attr.`1: i1`, __mlir_attr.`#lit.lifetime<1>: !lit.lifetime<1>`, address_space, - ]._mlir_type + ] """The underlying pointer type.""" var address: Self._mlir_type @@ -319,18 +319,16 @@ struct UnsafePointer[ @always_inline fn __refitem__( self, - ) -> Self._mlir_ref_type: + ) -> Self._ref_type._mlir_type: """Return a reference to the underlying data, offset by the offset index. Returns: A reference to the value. """ - return __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_ref_type]( - self.address - ) + return Self._ref_type._unsafe_from_pointer(self).value @always_inline - fn __refitem__(self, offset: Int) -> Self._mlir_ref_type: + fn __refitem__(self, offset: Int) -> Self._ref_type._mlir_type: """Return a reference to the underlying data, offset by the offset index. Args: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 548ad72b10..9150232bf0 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -313,9 +313,9 @@ struct Variant[*Ts: CollectionElement](CollectionElement): The internal data represented as a `Reference[T]`. """ debug_assert(Reference(self)[].isa[T](), "get: wrong variant type") - return __mlir_op.`lit.ref.from_pointer`[ - _type = Reference[T, mutability, self_life]._mlir_type - ](Reference(self)[]._get_ptr[T]().address) + return Reference[T, mutability, self_life]._unsafe_from_pointer( + Reference(self)[]._get_ptr[T]() + ) @staticmethod fn _check[T: CollectionElement]() -> Int8: From 7675b1575cc5cfb899ba24a4389e35f279535e74 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 19 Apr 2024 22:39:34 -0700 Subject: [PATCH 0263/2019] [mojo-lang] Continue refactoring getitem/setitem/refitem emission. (#38248) This continues to move around the logic for this to make it so index expressions are only emitted after at least one candiate set is found that will use it. This isn't used yet but will in a future patch. This also makes the logic hard default to refitem implementations if present, so adjust some weird hybrid code in LegacyPointer to be more modern. MODULAR_ORIG_COMMIT_REV_ID: 61f5e0ec9d0fb2be4f2a70e22ae4f78949cbf2f3 --- stdlib/src/memory/unsafe.mojo | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 203d255c3f..92e728d758 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -289,8 +289,8 @@ struct LegacyPointer[ ) @always_inline("nodebug") - fn __getitem__[T: Intable](self, offset: T) -> type: - """Loads the value the LegacyPointer object points to with the given offset. + fn __refitem__[T: Intable](self, offset: T) -> Self._mlir_ref_type: + """Enable subscript syntax `ref[idx]` to access the element. Parameters: T: The Intable type of the offset. @@ -299,23 +299,9 @@ struct LegacyPointer[ offset: The offset to load from. Returns: - The loaded value. - """ - return self.load(offset) - - @always_inline("nodebug") - fn __setitem__[T: Intable](self, offset: T, val: type): - """Stores the specified value to the location the LegacyPointer object points - to with the given offset. - - Parameters: - T: The Intable type of the offset. - - Args: - offset: The offset to store to. - val: The value to store. + The MLIR reference for the Mojo compiler to use. """ - return self.store(offset, val) + return (self + offset).__refitem__() # ===------------------------------------------------------------------=== # # Load/Store From 6b65f583600953a0d3170d858ead93c04a68c78c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 19 Apr 2024 23:14:49 -0700 Subject: [PATCH 0264/2019] [mojo-lang] Allow binding parameters in getitem/getattr. (#38250) This enhances getitem/getattr (as well as set/ref variants) to bind parameters instead of just arguments with the index values. This is fairly limited right now but still useful for the majority case. This fixes #35662 MODULAR_ORIG_COMMIT_REV_ID: c4d59f2bb6874fbecf92ca5b2c9b270652957983 --- docs/changelog.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b4bd3f4c31..7da3b24dee 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -141,6 +141,23 @@ what we publish. Note that variadic keyword parameters are not supported yet. +- The `__getitem__`/`__getattr__` and related methods can now take indices as + parameter values instead of argument values. This is enabled when defining + these as taking no arguments other than 'self' and the set value in a setter. + This enables types that can only be subscript into with parameters, as well + as things like: + + ```mojo + struct RGB: + fn __getattr__[name: StringLiteral](self) -> Int: + @parameter + if name == "r": return ... + elif name == "g": return ... + else: + constrained[name == "b", "can only access with r, g, or b members"]() + return ... + ``` + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From 22426b7eb0aa629f5610d5dfdd6f79b98946e438 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 20 Apr 2024 10:10:03 -0700 Subject: [PATCH 0265/2019] [mojo-stdlib] Enable tuple to be directly indexed with a parameter. (#38251) This adopts the new refitem parameter support in Tuple, allowing you to use `someTuple[1]` syntax instead of `someTuple.get[1]()` symbol soup. MODULAR_ORIG_COMMIT_REV_ID: a24f902b9ef90c29e6baed2796ec4947aac8e3ae --- docs/changelog.md | 8 ++++---- stdlib/src/builtin/tuple.mojo | 29 ++++++++--------------------- stdlib/src/utils/index.mojo | 12 ++++++------ stdlib/test/builtin/test_simd.mojo | 8 ++++---- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7da3b24dee..4778167d4c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,10 +16,10 @@ what we publish. ### 🔥 Legendary -- Tuple now works with memory-only element types like String. Also, `Tuple.get` - now supports a form that just takes an element index but does not - require you to specify the result type. Instead of `tup.get[1, Int]()` you - can now just use `tup.get[1]()`. +- Tuple now works with memory-only element types like String and allows you to + directly index into it with a parameter exprssion. This means you can now + simply use `x = tup[1]` like Python instead of `x = tup.get[1, Int]()`. You + can also assign into tuple elements now as well with `tup[1] = x`. ### ⭐️ New diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 1c2c9d3451..b20a828eea 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -75,7 +75,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # TODO: We could be fancier and take the values out of an owned # pack. For now just keep everything simple and copy the element. initialize_pointee( - UnsafePointer(self._refitem__[idx]()), + UnsafePointer(self[idx]), storage.get_element[idx]()[], ) @@ -88,7 +88,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # trivial and won't do anything. @parameter fn destroy_elt[idx: Int](): - destroy_pointee(UnsafePointer(self._refitem__[idx]())) + destroy_pointee(UnsafePointer(self[idx])) unroll[destroy_elt, Self.__len__()]() @@ -106,10 +106,10 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): - var existing_elt_ptr = UnsafePointer(existing._refitem__[idx]()) + var existing_elt_ptr = UnsafePointer(existing[idx]) initialize_pointee( - UnsafePointer(self._refitem__[idx]()), + UnsafePointer(self[idx]), __get_address_as_owned_value(existing_elt_ptr.address), ) @@ -130,8 +130,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): initialize_pointee( - UnsafePointer(self._refitem__[idx]()), - existing._refitem__[idx]()[], + UnsafePointer(self[idx]), + existing[idx], ) unroll[initialize_elt, Self.__len__()]() @@ -163,9 +163,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): """ return Self.__len__() - # TODO: Mojo's small brain can't handle a __refitem__ like this yet. @always_inline("nodebug") - fn _refitem__[ + fn __refitem__[ idx: Int, mutability: __mlir_type.i1, self_life: AnyLifetime[mutability].type, @@ -198,19 +197,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): Returns: The tuple element at the requested index. """ - return rebind[T](self.get[i]()) - - @always_inline("nodebug") - fn get[i: Int](self) -> element_types[i.value]: - """Get a tuple element. - - Parameters: - i: The element index. - - Returns: - The tuple element at the requested index. - """ - return self._refitem__[i]()[] + return rebind[T](self[i]) @staticmethod fn _offset[i: Int]() -> Int: diff --git a/stdlib/src/utils/index.mojo b/stdlib/src/utils/index.mojo index 163d2a7009..9d26582984 100644 --- a/stdlib/src/utils/index.mojo +++ b/stdlib/src/utils/index.mojo @@ -206,7 +206,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): self = Int(value) @always_inline - fn __init__(inout self, elems: Tuple[Int, Int]): + fn __init__(inout self, elems: (Int, Int)): """Constructs a static int tuple given a tuple of integers. Args: @@ -224,14 +224,14 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): @parameter fn fill[idx: Int](): - tup[idx] = elems.get[idx, Int]() + tup[idx] = rebind[Int](elems[idx]) unroll[fill, 2]() self = tup @always_inline - fn __init__(inout self, elems: Tuple[Int, Int, Int]): + fn __init__(inout self, elems: (Int, Int, Int)): """Constructs a static int tuple given a tuple of integers. Args: @@ -249,14 +249,14 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): @parameter fn fill[idx: Int](): - tup[idx] = elems.get[idx, Int]() + tup[idx] = rebind[Int](elems[idx]) unroll[fill, 3]() self = tup @always_inline - fn __init__(inout self, elems: Tuple[Int, Int, Int, Int]): + fn __init__(inout self, elems: (Int, Int, Int, Int)): """Constructs a static int tuple given a tuple of integers. Args: @@ -274,7 +274,7 @@ struct StaticIntTuple[size: Int](Sized, Stringable, EqualityComparable): @parameter fn fill[idx: Int](): - tup[idx] = elems.get[idx, Int]() + tup[idx] = rebind[Int](elems[idx]) unroll[fill, 4]() diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index 93cafc3e41..a592f4aade 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -366,12 +366,12 @@ def test_interleave(): def test_deinterleave(): var tup2 = SIMD[DType.float32, 2](1, 2).deinterleave() - assert_equal(tup2.get[0](), Float32(1)) - assert_equal(tup2.get[1](), Float32(2)) + assert_equal(tup2[0], Float32(1)) + assert_equal(tup2[1], Float32(2)) var tup4 = SIMD[DType.index, 4](0, 1, 2, 3).deinterleave() - assert_equal(tup4.get[0](), SIMD[DType.index, 2](0, 2)) - assert_equal(tup4.get[1](), SIMD[DType.index, 2](1, 3)) + assert_equal(tup4[0], SIMD[DType.index, 2](0, 2)) + assert_equal(tup4[1], SIMD[DType.index, 2](1, 3)) def test_address(): From fce124190604cf03ab5ff741bbbbeb73eed6e617 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 20 Apr 2024 11:46:28 -0700 Subject: [PATCH 0266/2019] [mojo-stdlib] Remove some old code from Tuple, NFC (#38267) This has gone unused since the move to kgen.pack as the backing store - we now let the compiler compute offsets etc. MODULAR_ORIG_COMMIT_REV_ID: 23ba8a0f6c7e58ca3436e1e10d2476a47babad2b --- stdlib/src/builtin/tuple.mojo | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index b20a828eea..b0c85fb2ac 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -173,9 +173,9 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): ]: # Return a reference to an element at the specified index, propagating # mutability of self. - var storage_kgen_ptr = Reference( + var storage_kgen_ptr = UnsafePointer.address_of( Reference(self_lit)[].storage - ).get_legacy_pointer().address + ).address # KGenPointer to the element. var elt_kgen_ptr = __mlir_op.`kgen.pack.gep`[index = idx.value]( @@ -198,31 +198,3 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): The tuple element at the requested index. """ return rebind[T](self[i]) - - @staticmethod - fn _offset[i: Int]() -> Int: - constrained[i >= 0, "index must be positive"]() - - @parameter - if i == 0: - return 0 - else: - return _align_up( - Self._offset[i - 1]() - + _align_up( - sizeof[element_types[i - 1]](), - alignof[element_types[i - 1]](), - ), - alignof[element_types[i]](), - ) - - -# ===----------------------------------------------------------------------=== # -# Utilities -# ===----------------------------------------------------------------------=== # - - -@always_inline -fn _align_up(value: Int, alignment: Int) -> Int: - var div_ceil = (value + alignment - 1)._positive_div(alignment) - return div_ceil * alignment From e47ea7e0131312af322984e8cbd45310677702d2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 20 Apr 2024 12:11:21 -0700 Subject: [PATCH 0267/2019] [mojo-stdlib] Move a lot more things off `tup.get[i, T]()`, NFC. (#38269) This is a cleanup, but we can't remove this get method yet due to #38268 MODULAR_ORIG_COMMIT_REV_ID: 2d3c0425689cab7235cb946fdd50481c4c9a3c5c --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/simd.mojo | 14 ++++++++------ stdlib/src/builtin/tuple.mojo | 4 ++-- stdlib/src/sys/intrinsics.mojo | 7 ++----- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index c51dc86df4..7dafa99531 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -65,7 +65,7 @@ struct ListLiteral[*Ts: CollectionElement](Sized, CollectionElement): Returns: The element at the given index. """ - return self.storage.get[i, T]() + return rebind[T](self.storage[i]) # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 8f3fdc46ef..812aa684ed 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1697,10 +1697,12 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( has_side_effect=False, ](self, other) + alias _SIMDHalfType = SIMD[type, size // 2] + @always_inline("nodebug") fn deinterleave( self, - ) -> Tuple[SIMD[type, size // 2], SIMD[type, size // 2]]: + ) -> (Self._SIMDHalfType, Self._SIMDHalfType): """Constructs two vectors by deinterleaving the even and odd lanes of the vector. @@ -1717,18 +1719,18 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( @parameter if size == 2: return ( - rebind[SIMD[type, size // 2]](self[0]), - rebind[SIMD[type, size // 2]](self[1]), + rebind[Self._SIMDHalfType](self[0]), + rebind[Self._SIMDHalfType](self[1]), ) var res = llvm_intrinsic[ "llvm.experimental.vector.deinterleave2", - _RegisterPackType[SIMD[type, size // 2], SIMD[type, size // 2]], + _RegisterPackType[Self._SIMDHalfType, Self._SIMDHalfType], has_side_effect=False, ](self) return ( - res.get[0, SIMD[type, size // 2]](), - res.get[1, SIMD[type, size // 2]](), + rebind[Self._SIMDHalfType](res[0]), + rebind[Self._SIMDHalfType](res[1]), ) # ===-------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index b0c85fb2ac..c81bb80544 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -184,8 +184,8 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): # Convert to an immortal mut reference, which conforms to self_life. return UnsafePointer(elt_kgen_ptr)[] - # TODO: Remove the get methods in favor of __refitem__ some day. This will - # be annoying if we don't have autoderef though. + # TODO(#38268): Remove this method when references and parameter expressions + # cooperate better. We can't handle the use in test_simd without this. @always_inline("nodebug") fn get[i: Int, T: CollectionElement](self) -> T: """Get a tuple element and rebind to the specified type. diff --git a/stdlib/src/sys/intrinsics.mojo b/stdlib/src/sys/intrinsics.mojo index 593f709b73..5184bbbcd2 100644 --- a/stdlib/src/sys/intrinsics.mojo +++ b/stdlib/src/sys/intrinsics.mojo @@ -1535,16 +1535,13 @@ struct _RegisterPackType[*a: AnyRegType]: var storage: __mlir_type[`!kgen.pack<`, a, `>`] @always_inline("nodebug") - fn get[i: Int, T: AnyRegType](self) -> T: + fn __getitem__[i: Int](self) -> a[i.value]: """Get the element. Parameters: i: The element index. - T: The element type. Returns: The tuple element at the requested index. """ - return rebind[T]( - __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) - ) + return __mlir_op.`kgen.pack.extract`[index = i.value](self.storage) From 95553324013556890970b398004c6d9dea9d5d21 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 20 Apr 2024 18:17:32 -0700 Subject: [PATCH 0268/2019] [mojo-stdlib] Minor tidying to IntLiteral. (#38276) This eliminates the extraneous `_zero` member in favor of its default ctor. It also adds some assertions and comments to LookupResult. MODULAR_ORIG_COMMIT_REV_ID: 501dbaec909c7296963e9a1f8336dde563868f20 --- stdlib/src/builtin/int_literal.mojo | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/stdlib/src/builtin/int_literal.mojo b/stdlib/src/builtin/int_literal.mojo index 8b9be688ad..c9e21c0974 100644 --- a/stdlib/src/builtin/int_literal.mojo +++ b/stdlib/src/builtin/int_literal.mojo @@ -24,12 +24,11 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): precision integer types. """ - var value: __mlir_type.`!kgen.int_literal` + alias _mlir_type = __mlir_type.`!kgen.int_literal` + + var value: Self._mlir_type """The underlying storage for the integer value.""" - alias _zero = IntLiteral( - __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` - ) alias _one = IntLiteral( __mlir_attr.`#kgen.int_literal<1> : !kgen.int_literal` ) @@ -41,7 +40,9 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: The constructed Self object. """ - return Self._zero + return IntLiteral( + __mlir_attr.`#kgen.int_literal<0> : !kgen.int_literal` + ) @always_inline("nodebug") fn __init__(value: __mlir_type.`!kgen.int_literal`) -> Self: @@ -198,7 +199,7 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: False Bool value if the value is equal to 0 and True otherwise. """ - return self != Self._zero + return self != Self() @always_inline("nodebug") fn __index__(self) -> Int: @@ -226,7 +227,7 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: The -self value. """ - return Self._zero - self + return Self() - self @always_inline("nodebug") fn __invert__(self) -> Self: @@ -235,7 +236,7 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: The ~self value. """ - return self ^ (Self._zero - Self._one) + return self ^ (Self() - Self._one) @always_inline("nodebug") fn __add__(self, rhs: Self) -> Self: @@ -295,9 +296,9 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: `self // rhs` value. """ - if rhs == Self._zero: + if rhs == Self(): # this should raise an exception. - return Self._zero + return Self() return Self( __mlir_op.`kgen.int_literal.binop`[ oper = __mlir_attr.`#kgen` @@ -314,9 +315,9 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: The remainder of dividing self by rhs. """ - if rhs == Self._zero: + if rhs == Self(): # this should raise an exception. - return Self._zero + return Self() return Self( __mlir_op.`kgen.int_literal.binop`[ oper = __mlir_attr.`#kgen` @@ -333,9 +334,9 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: `self << rhs`. """ - if rhs < Self._zero: + if rhs < Self(): # this should raise an exception. - return Self._zero + return Self() return Self( __mlir_op.`kgen.int_literal.binop`[ oper = __mlir_attr.`#kgen` @@ -352,9 +353,9 @@ struct IntLiteral(Intable, Stringable, Boolable, EqualityComparable): Returns: `self >> rhs`. """ - if rhs < Self._zero: + if rhs < Self(): # this should raise an exception. - return Self._zero + return Self() return Self( __mlir_op.`kgen.int_literal.binop`[ oper = __mlir_attr.`#kgen` From ad9361d78c22d2904a4106ad690df71524d68c11 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 21 Apr 2024 12:07:39 -0700 Subject: [PATCH 0269/2019] [mojo-stdlib] Remove `_unsafe_from_pointer` in favor of `ptr[]`. (#38282) This method isn't needed given pointers have a refitem. MODULAR_ORIG_COMMIT_REV_ID: aa6c6c6d1bc6c48b7d797978c9451a324830633b --- stdlib/src/collections/list.mojo | 12 ++---------- stdlib/src/memory/_arc.mojo | 4 +--- stdlib/src/memory/reference.mojo | 11 ----------- stdlib/src/memory/unsafe_pointer.mojo | 4 +++- stdlib/src/utils/variant.mojo | 4 +--- 5 files changed, 7 insertions(+), 28 deletions(-) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 9dc2ea5727..e31f0ef37b 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -515,16 +515,8 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): if i < 0: normalized_idx += Reference(self)[].size - # Mutability gets set to the local mutability of this - # pointer value, ie. because we defined it with `let` it's now an - # "immutable" reference regardless of the mutability of `self`. - # This means we can't just use `UnsafePointer.__refitem__` here - # because the mutability won't match. - var base_ptr = Reference(self)[].data - var offset_ptr = base_ptr + normalized_idx - return Reference[T, mutability, self_life]._unsafe_from_pointer( - offset_ptr - ) + var offset_ptr = Reference(self)[].data + normalized_idx + return offset_ptr[] fn __iter__[ mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type diff --git a/stdlib/src/memory/_arc.mojo b/stdlib/src/memory/_arc.mojo index c98a23e235..ede308d33f 100644 --- a/stdlib/src/memory/_arc.mojo +++ b/stdlib/src/memory/_arc.mojo @@ -147,9 +147,7 @@ struct Arc[T: Movable](CollectionElement): Returns: A Reference to the managed value. """ - return Reference[T, mutability, lifetime]._unsafe_from_pointer( - Reference(self)[]._data_ptr() - ) + return Reference(self)[]._data_ptr()[] fn _data_ptr(self) -> UnsafePointer[T]: return UnsafePointer.address_of(self._inner[].data) diff --git a/stdlib/src/memory/reference.mojo b/stdlib/src/memory/reference.mojo index 875a3f1e76..4ae88a8e3c 100644 --- a/stdlib/src/memory/reference.mojo +++ b/stdlib/src/memory/reference.mojo @@ -233,17 +233,6 @@ struct Reference[ """ self.value = value - @always_inline("nodebug") - @staticmethod - fn _unsafe_from_pointer( - ptr: UnsafePointer[type, address_space], - ) -> Reference[type, is_mutable, lifetime, address_space]: - return Reference( - __mlir_op.`lit.ref.from_pointer`[_type = Self._mlir_type]( - ptr.address - ) - ) - # ===------------------------------------------------------------------===# # Operator dunders # ===------------------------------------------------------------------===# diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index 9f1e5d851a..e8fe367f52 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -325,7 +325,9 @@ struct UnsafePointer[ Returns: A reference to the value. """ - return Self._ref_type._unsafe_from_pointer(self).value + return __mlir_op.`lit.ref.from_pointer`[ + _type = Self._ref_type._mlir_type + ](self.address) @always_inline fn __refitem__(self, offset: Int) -> Self._ref_type._mlir_type: diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index 9150232bf0..c26c11081b 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -313,9 +313,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): The internal data represented as a `Reference[T]`. """ debug_assert(Reference(self)[].isa[T](), "get: wrong variant type") - return Reference[T, mutability, self_life]._unsafe_from_pointer( - Reference(self)[]._get_ptr[T]() - ) + return Reference(self)[]._get_ptr[T]()[] @staticmethod fn _check[T: CollectionElement]() -> Int8: From 2963ef7fd8b43bb38a679323b04344f97bff0c83 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 22 Apr 2024 11:29:42 -0500 Subject: [PATCH 0270/2019] [External][stdlib] implement shift functions in `object` (#2247) (#38196) Implement left and right shift operations for `object`. Signed-off-by: Jiexiang Liu mojo-orig-commit: f098c124b7904c84d40feecc81ef197e10bbee52 MODULAR_ORIG_COMMIT_REV_ID: fa7a6463c3390652414fcd8d704f6621621f7d00 --- stdlib/src/builtin/object.mojo | 91 ++++++++++++++++++++++++++-- stdlib/test/builtin/test_object.mojo | 15 +++++ 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 179817af6b..7107d073fe 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -580,6 +580,18 @@ struct _ObjectImpl(CollectionElement, Stringable): else: lhs = lhs.convert_int_to_float() + @staticmethod + fn coerce_integral_type(inout lhs: _ObjectImpl, inout rhs: _ObjectImpl): + """Coerces two values of integral type to the appropriate + lowest-common denominator type for performing bitwise operations. + """ + if lhs.is_int() == rhs.is_int(): + return + if lhs.is_int(): + rhs = rhs.convert_bool_to_int() + else: + lhs = lhs.convert_bool_to_int() + fn __str__(self) -> String: """Returns the name (in lowercase) of the specific object type.""" if self.is_none(): @@ -1175,6 +1187,12 @@ struct object(IntableRaising, Boolable, Stringable): ): raise Error("TypeError: not a valid arithmetic type") + @always_inline + fn _arithmetic_integral_type_check(self) raises: + """Throws an error if the object is not an integral type.""" + if not (self._value.is_bool() or self._value.is_int()): + raise Error("TypeError: not a valid integral type") + @staticmethod @always_inline fn _arithmetic_binary_op[ @@ -1350,8 +1368,33 @@ struct object(IntableRaising, Boolable, Stringable): Float64.__floordiv__, Int64.__floordiv__ ](self, rhs) - # TODO __lshift__ - # TODO __rshift__ + @always_inline + fn __lshift__(self, rhs: object) raises -> object: + """Left shift operator. Valid only for arithmetic types. + + Args: + rhs: Right hand value. + + Returns: + The left hand value left shifted by the right hand value. + """ + self._arithmetic_integral_type_check() + rhs._arithmetic_integral_type_check() + return object(self._value.get_as_int() << rhs._value.get_as_int()) + + @always_inline + fn __rshift__(self, rhs: object) raises -> object: + """Right shift operator. Valid only for arithmetic types. + + Args: + rhs: Right hand value. + + Returns: + The left hand value right shifted by the right hand value. + """ + self._arithmetic_integral_type_check() + rhs._arithmetic_integral_type_check() + return object(self._value.get_as_int() >> rhs._value.get_as_int()) @always_inline fn __and__(self, rhs: object) raises -> object: @@ -1452,8 +1495,23 @@ struct object(IntableRaising, Boolable, Stringable): """ self = self // rhs - # TODO: __ilshift__ - # TODO: __irshift__ + @always_inline + fn __ilshift__(inout self, rhs: object) raises: + """In-place left shift operator. + + Args: + rhs: Right hand value. + """ + self = self << rhs + + @always_inline + fn __irshift__(inout self, rhs: object) raises: + """In-place right shift operator. + + Args: + rhs: Right hand value. + """ + self = self >> rhs @always_inline fn __iand__(inout self, rhs: object) raises: @@ -1565,8 +1623,29 @@ struct object(IntableRaising, Boolable, Stringable): """ return lhs // self - # TODO: __rlshift__ - # TODO: __rrshift__ + @always_inline + fn __rlshift__(self, lhs: object) raises -> object: + """Reverse left shift operator. + + Args: + lhs: Left hand value. + + Returns: + The left hand value left shifted by the right hand value. + """ + return lhs << self + + @always_inline + fn __rrshift__(self, lhs: object) raises -> object: + """Reverse right shift operator. + + Args: + lhs: Left hand value. + + Returns: + The left hand value right shifted by the right hand value. + """ + return lhs >> self @always_inline fn __rand__(self, lhs: object) raises -> object: diff --git a/stdlib/test/builtin/test_object.mojo b/stdlib/test/builtin/test_object.mojo index a3a3ced900..94b251dd1e 100644 --- a/stdlib/test/builtin/test_object.mojo +++ b/stdlib/test/builtin/test_object.mojo @@ -145,6 +145,21 @@ def test_arithmetic_ops_div(): assert_true(5 // object(2) == 2) +def test_object_shift(): + a = object(1) + b = object(2) + assert_true(a << b == 4) + assert_true(b >> a == 1) + + b <<= a + assert_true(b == 4) + b >>= a + assert_true(b == 1) + + assert_true(2 << object(1) == 4) + assert_true(2 >> object(1) == 1) + + def test_function(borrowed lhs, borrowed rhs) -> object: return lhs + rhs From fc4c64b108c20aba575a8fe5c569bdca9aab7415 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 22 Apr 2024 11:33:55 -0500 Subject: [PATCH 0271/2019] [External][stdlib] Add checked arithmetic operations (#2138) (#38206) This PR adds a wrapper around the [LLVM family of arithmetic operations with overflow](https://llvm.org/docs/LangRef.html#arithmetic-with-overflow-intrinsics). This low level primitive opens up the door for higher level checked arithmetic operations that return optionals (or raise) on overflow instead. Signed-off-by: Lukas Hermann mojo-orig-commit: b3e26459fb2bb125a50e12d48a5ee90b2b637ef2 MODULAR_ORIG_COMMIT_REV_ID: f13bfd5ec490d646ef409399e1b35945232da7bf --- stdlib/src/builtin/simd.mojo | 115 ++++++++++++++ stdlib/test/builtin/test_simd.mojo | 235 +++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 812aa684ed..cdb57315e5 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -973,6 +973,121 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() self = self.__pow__(rhs) + # ===-------------------------------------------------------------------===# + # Checked operations + # ===-------------------------------------------------------------------===# + + @always_inline + fn add_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): + """Computes `self + rhs` and a mask of which indices overflowed. + + Args: + rhs: The rhs value. + + Returns: + A tuple with the results of the operation and a mask for overflows. The first is a new vector whose element at position `i` is computed as + `self[i] + rhs[i]`. The second item is a vector of booleans where a `1` at position `i` represents `self[i] + rhs[i]` overflowed. + """ + constrained[type.is_integral()]() + + @parameter + if type.is_signed(): + var result = llvm_intrinsic[ + "llvm.sadd.with.overflow", + _RegisterPackType[Self, SIMD[DType.bool, size]], + Self, + Self, + ](self, rhs) + return ( + result.get[0, Self](), + result.get[1, SIMD[DType.bool, size]](), + ) + else: + var result = llvm_intrinsic[ + "llvm.uadd.with.overflow", + _RegisterPackType[Self, SIMD[DType.bool, size]], + Self, + Self, + ](self, rhs) + return ( + result.get[0, Self](), + result.get[1, SIMD[DType.bool, size]](), + ) + + @always_inline + fn sub_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): + """Computes `self - rhs` and a mask of which indices overflowed. + + Args: + rhs: The rhs value. + + Returns: + A tuple with the results of the operation and a mask for overflows. The first is a new vector whose element at position `i` is computed as + `self[i] - rhs[i]`. The second item is a vector of booleans where a `1` at position `i` represents `self[i] - rhs[i]` overflowed. + """ + constrained[type.is_integral()]() + + @parameter + if type.is_signed(): + var result = llvm_intrinsic[ + "llvm.ssub.with.overflow", + _RegisterPackType[Self, SIMD[DType.bool, size]], + Self, + Self, + ](self, rhs) + return ( + result.get[0, Self](), + result.get[1, SIMD[DType.bool, size]](), + ) + else: + var result = llvm_intrinsic[ + "llvm.usub.with.overflow", + _RegisterPackType[Self, SIMD[DType.bool, size]], + Self, + Self, + ](self, rhs) + return ( + result.get[0, Self](), + result.get[1, SIMD[DType.bool, size]](), + ) + + @always_inline + fn mul_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): + """Computes `self * rhs` and a mask of which indices overflowed. + + Args: + rhs: The rhs value. + + Returns: + A tuple with the results of the operation and a mask for overflows. The first is a new vector whose element at position `i` is computed as + `self[i] * rhs[i]`. The second item is a vector of booleans where a `1` at position `i` represents `self[i] * rhs[i]` overflowed. + """ + constrained[type.is_integral()]() + + @parameter + if type.is_signed(): + var result = llvm_intrinsic[ + "llvm.smul.with.overflow", + _RegisterPackType[Self, SIMD[DType.bool, size]], + Self, + Self, + ](self, rhs) + return ( + result.get[0, Self](), + result.get[1, SIMD[DType.bool, size]](), + ) + else: + var result = llvm_intrinsic[ + "llvm.umul.with.overflow", + _RegisterPackType[Self, SIMD[DType.bool, size]], + Self, + Self, + ](self, rhs) + return ( + result.get[0, Self](), + result.get[1, SIMD[DType.bool, size]](), + ) + # ===-------------------------------------------------------------------===# # Reversed operations # ===-------------------------------------------------------------------===# diff --git a/stdlib/test/builtin/test_simd.mojo b/stdlib/test/builtin/test_simd.mojo index a592f4aade..a445741844 100644 --- a/stdlib/test/builtin/test_simd.mojo +++ b/stdlib/test/builtin/test_simd.mojo @@ -425,6 +425,238 @@ def test_limits(): test_integral_overflow[DType.uint64]() +def test_add_with_overflow(): + # TODO: replace all the aliases with math.limit.max_finite() + # and math.limit.min_finite() + alias uint8_min = 0 + alias uint8_max = 255 + var value_u8: UInt8 + var overflowed_u8: Scalar[DType.bool] + value_u8, overflowed_u8 = UInt8(uint8_max).add_with_overflow(1) + assert_equal(value_u8, uint8_min) + assert_equal(overflowed_u8, True) + + var value_u8x4: SIMD[DType.uint8, 4] + var overflowed_u8x4: SIMD[DType.bool, 4] + value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( + 1, uint8_max, 1, uint8_max + ).add_with_overflow(SIMD[DType.uint8, 4](0, 1, 0, 1)) + assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, uint8_min, 1, uint8_min)) + assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) + + alias int8_min = -128 + alias int8_max = 127 + var value_i8: Int8 + var overflowed_i8: Scalar[DType.bool] + value_i8, overflowed_i8 = Int8(int8_max).add_with_overflow(1) + assert_equal(value_i8, int8_min) + assert_equal(overflowed_i8, True) + + var value_i8x4: SIMD[DType.int8, 4] + var overflowed_i8x4: SIMD[DType.bool, 4] + value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( + 1, int8_max, 1, int8_max + ).add_with_overflow(SIMD[DType.int8, 4](0, 1, 0, 1)) + assert_equal(value_i8x4, SIMD[DType.int8, 4](1, int8_min, 1, int8_min)) + assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) + + alias uint32_min = 0 + alias uint32_max = 4294967295 + var value_u32: UInt32 + var overflowed_u32: Scalar[DType.bool] + value_u32, overflowed_u32 = UInt32(uint32_max).add_with_overflow(1) + assert_equal(value_u32, uint32_min) + assert_equal(overflowed_u32, True) + + var value_u32x4: SIMD[DType.uint32, 4] + var overflowed_u32x4: SIMD[DType.bool, 4] + value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( + 1, uint32_max, 1, uint32_max + ).add_with_overflow(SIMD[DType.uint32, 4](0, 1, 0, 1)) + assert_equal( + value_u32x4, SIMD[DType.uint32, 4](1, uint32_min, 1, uint32_min) + ) + assert_equal( + overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) + ) + + alias int32_min = -2147483648 + alias int32_max = 2147483647 + var value_i32: Int32 + var overflowed_i32: Scalar[DType.bool] + value_i32, overflowed_i32 = Int32(int32_max).add_with_overflow(1) + assert_equal(value_i32, int32_min) + assert_equal(overflowed_i32, True) + + var value_i32x4: SIMD[DType.int32, 4] + var overflowed_i32x4: SIMD[DType.bool, 4] + value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( + 1, int32_max, 1, int32_max + ).add_with_overflow(SIMD[DType.int32, 4](0, 1, 0, 1)) + assert_equal(value_i32x4, SIMD[DType.int32, 4](1, int32_min, 1, int32_min)) + assert_equal( + overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) + ) + + +def test_sub_with_overflow(): + # TODO: replace all the aliases with math.limit.max_finite() + # and math.limit.min_finite() + alias uint8_min = 0 + alias uint8_max = 255 + var value_u8: UInt8 + var overflowed_u8: Scalar[DType.bool] + value_u8, overflowed_u8 = UInt8(uint8_min).sub_with_overflow(1) + assert_equal(value_u8, uint8_max) + assert_equal(overflowed_u8, True) + + var value_u8x4: SIMD[DType.uint8, 4] + var overflowed_u8x4: SIMD[DType.bool, 4] + value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( + 1, uint8_min, 1, uint8_min + ).sub_with_overflow(SIMD[DType.uint8, 4](0, 1, 0, 1)) + assert_equal(value_u8x4, SIMD[DType.uint8, 4](1, uint8_max, 1, uint8_max)) + assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) + + alias int8_min = -128 + alias int8_max = 127 + var value_i8: Int8 + var overflowed_i8: Scalar[DType.bool] + value_i8, overflowed_i8 = Int8(int8_min).sub_with_overflow(1) + assert_equal(value_i8, int8_max) + assert_equal(overflowed_i8, True) + + var value_i8x4: SIMD[DType.int8, 4] + var overflowed_i8x4: SIMD[DType.bool, 4] + value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( + 1, int8_min, 1, int8_min + ).sub_with_overflow(SIMD[DType.int8, 4](0, 1, 0, 1)) + assert_equal(value_i8x4, SIMD[DType.int8, 4](1, int8_max, 1, int8_max)) + assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) + + alias uint32_min = 0 + alias uint32_max = 4294967295 + var value_u32: UInt32 + var overflowed_u32: Scalar[DType.bool] + value_u32, overflowed_u32 = UInt32(uint32_min).sub_with_overflow(1) + assert_equal(value_u32, uint32_max) + assert_equal(overflowed_u32, True) + + var value_u32x4: SIMD[DType.uint32, 4] + var overflowed_u32x4: SIMD[DType.bool, 4] + value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( + 1, uint32_min, 1, uint32_min + ).sub_with_overflow(SIMD[DType.uint32, 4](0, 1, 0, 1)) + assert_equal( + value_u32x4, SIMD[DType.uint32, 4](1, uint32_max, 1, uint32_max) + ) + assert_equal( + overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) + ) + + alias int32_min = -2147483648 + alias int32_max = 2147483647 + var value_i32: Int32 + var overflowed_i32: Scalar[DType.bool] + value_i32, overflowed_i32 = Int32(int32_min).sub_with_overflow(1) + assert_equal(value_i32, int32_max) + assert_equal(overflowed_i32, True) + + var value_i32x4: SIMD[DType.int32, 4] + var overflowed_i32x4: SIMD[DType.bool, 4] + value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( + 1, int32_min, 1, int32_min + ).sub_with_overflow(SIMD[DType.int32, 4](0, 1, 0, 1)) + assert_equal(value_i32x4, SIMD[DType.int32, 4](1, int32_max, 1, int32_max)) + assert_equal( + overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) + ) + + +def test_mul_with_overflow(): + # TODO: replace all the aliases with math.limit.max_finite() + # and math.limit.min_finite() + alias uint8_min = 0 + alias uint8_max = 255 + alias uint8_max_x2 = 254 + var value_u8: UInt8 + var overflowed_u8: Scalar[DType.bool] + value_u8, overflowed_u8 = UInt8(uint8_max).mul_with_overflow(2) + assert_equal(value_u8, uint8_max_x2) + assert_equal(overflowed_u8, True) + + var value_u8x4: SIMD[DType.uint8, 4] + var overflowed_u8x4: SIMD[DType.bool, 4] + value_u8x4, overflowed_u8x4 = SIMD[DType.uint8, 4]( + 1, uint8_max, 1, uint8_max + ).mul_with_overflow(SIMD[DType.uint8, 4](0, 2, 0, 2)) + assert_equal( + value_u8x4, SIMD[DType.uint8, 4](0, uint8_max_x2, 0, uint8_max_x2) + ) + assert_equal(overflowed_u8x4, SIMD[DType.bool, 4](False, True, False, True)) + + alias int8_min = -128 + alias int8_max = 127 + alias int8_max_x2 = -2 + var value_i8: Int8 + var overflowed_i8: Scalar[DType.bool] + value_i8, overflowed_i8 = Int8(int8_max).mul_with_overflow(2) + assert_equal(value_i8, int8_max_x2) + assert_equal(overflowed_i8, True) + + var value_i8x4: SIMD[DType.int8, 4] + var overflowed_i8x4: SIMD[DType.bool, 4] + value_i8x4, overflowed_i8x4 = SIMD[DType.int8, 4]( + 1, int8_max, 1, int8_max + ).mul_with_overflow(SIMD[DType.int8, 4](0, 2, 0, 2)) + assert_equal( + value_i8x4, SIMD[DType.int8, 4](0, int8_max_x2, 0, int8_max_x2) + ) + assert_equal(overflowed_i8x4, SIMD[DType.bool, 4](False, True, False, True)) + + alias uint32_min = 0 + alias uint32_max = 4294967295 + alias uint32_max_x2 = 4294967294 + var value_u32: UInt32 + var overflowed_u32: Scalar[DType.bool] + value_u32, overflowed_u32 = UInt32(uint32_max).mul_with_overflow(2) + assert_equal(value_u32, uint32_max_x2) + assert_equal(overflowed_u32, True) + + var value_u32x4: SIMD[DType.uint32, 4] + var overflowed_u32x4: SIMD[DType.bool, 4] + value_u32x4, overflowed_u32x4 = SIMD[DType.uint32, 4]( + 1, uint32_max, 1, uint32_max + ).mul_with_overflow(SIMD[DType.uint32, 4](0, 2, 0, 2)) + assert_equal( + value_u32x4, SIMD[DType.uint32, 4](0, uint32_max_x2, 0, uint32_max_x2) + ) + assert_equal( + overflowed_u32x4, SIMD[DType.bool, 4](False, True, False, True) + ) + + alias int32_min = -2147483648 + alias int32_max = 2147483647 + alias int32_max_x2 = -2 + var value_i32: Int32 + var overflowed_i32: Scalar[DType.bool] + value_i32, overflowed_i32 = Int32(int32_max).mul_with_overflow(2) + assert_equal(value_i32, int32_max_x2) + assert_equal(overflowed_i32, True) + + var value_i32x4: SIMD[DType.int32, 4] + var overflowed_i32x4: SIMD[DType.bool, 4] + value_i32x4, overflowed_i32x4 = SIMD[DType.int32, 4]( + 1, int32_max, 1, int32_max + ).mul_with_overflow(SIMD[DType.int32, 4](0, 2, 0, 2)) + assert_equal( + value_i32x4, SIMD[DType.int32, 4](0, int32_max_x2, 0, int32_max_x2) + ) + assert_equal( + overflowed_i32x4, SIMD[DType.bool, 4](False, True, False, True) + ) + + def main(): test_cast() test_simd_variadic() @@ -439,3 +671,6 @@ def main(): test_address() test_extract() test_limits() + test_add_with_overflow() + test_sub_with_overflow() + test_mul_with_overflow() From f07da94d602ef21d7c4298a09febc941b67372a9 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 22 Apr 2024 12:15:19 -0500 Subject: [PATCH 0272/2019] [stdlib] hotfix: Update uses of `.get` method renamed in #38269 (#38312) These were added in #38206, which was created before #38269 but merged after it. MODULAR_ORIG_COMMIT_REV_ID: a57ce6b0f69e1bc2e343945278b8188cbfb4652e --- stdlib/src/builtin/simd.mojo | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index cdb57315e5..2392a52ba7 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -998,10 +998,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Self, Self, ](self, rhs) - return ( - result.get[0, Self](), - result.get[1, SIMD[DType.bool, size]](), - ) + return (result[0], result[1]) else: var result = llvm_intrinsic[ "llvm.uadd.with.overflow", @@ -1009,10 +1006,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Self, Self, ](self, rhs) - return ( - result.get[0, Self](), - result.get[1, SIMD[DType.bool, size]](), - ) + return (result[0], result[1]) @always_inline fn sub_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): @@ -1035,10 +1029,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Self, Self, ](self, rhs) - return ( - result.get[0, Self](), - result.get[1, SIMD[DType.bool, size]](), - ) + return (result[0], result[1]) else: var result = llvm_intrinsic[ "llvm.usub.with.overflow", @@ -1046,10 +1037,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Self, Self, ](self, rhs) - return ( - result.get[0, Self](), - result.get[1, SIMD[DType.bool, size]](), - ) + return (result[0], result[1]) @always_inline fn mul_with_overflow(self, rhs: Self) -> (Self, SIMD[DType.bool, size]): @@ -1072,10 +1060,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Self, Self, ](self, rhs) - return ( - result.get[0, Self](), - result.get[1, SIMD[DType.bool, size]](), - ) + return (result[0], result[1]) else: var result = llvm_intrinsic[ "llvm.umul.with.overflow", @@ -1083,10 +1068,7 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( Self, Self, ](self, rhs) - return ( - result.get[0, Self](), - result.get[1, SIMD[DType.bool, size]](), - ) + return (result[0], result[1]) # ===-------------------------------------------------------------------===# # Reversed operations From 0c81bfea8f211a1826a3b237c8fb991aaad4a668 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 22 Apr 2024 11:10:13 -0700 Subject: [PATCH 0273/2019] [mojo-stdlib] Fix tuple copyinit/moveinit. (#38315) This fixes tuple copyinit to copy the input elements, and fixes the move constructor to not copy. The initializer still unnecessarily copies the input elements, but that is not a correctness issue. This fixes #38302 MODULAR_ORIG_COMMIT_REV_ID: 0821f16568465759c30f525e76f1080601529646 --- stdlib/src/builtin/tuple.mojo | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index c81bb80544..500d941b9a 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -106,12 +106,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): - var existing_elt_ptr = UnsafePointer(existing[idx]) - - initialize_pointee( - UnsafePointer(self[idx]), - __get_address_as_owned_value(existing_elt_ptr.address), - ) + initialize_pointee(UnsafePointer(self[idx]), existing[idx]) unroll[initialize_elt, Self.__len__()]() @@ -129,13 +124,18 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): + var existing_elt_ptr = UnsafePointer(existing[idx]).address initialize_pointee( UnsafePointer(self[idx]), - existing[idx], + __get_address_as_owned_value(existing_elt_ptr), ) unroll[initialize_elt, Self.__len__()]() + # We transfered all of the elements out of 'existing', so we need to + # disable its destructor so they aren't destroyed. + __mlir_op.`lit.ownership.mark_destroyed`(Reference(existing).value) + @always_inline @staticmethod fn __len__() -> Int: From 5adb230ae5840264df9ff0d7194747313a94eaaf Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 22 Apr 2024 13:20:11 -0500 Subject: [PATCH 0274/2019] [External][Stdlib] fix simd.reduce for size_out == 2 (#2102) (#38046) Signed-off-by: Yiwu Chen <210at85@gmail.com> mojo-orig-commit: 9cae69029c635a7110581d27995b4261d2194264 Co-authored-by: soraros MODULAR_ORIG_COMMIT_REV_ID: 530ac632f215eb97e529741f60c156d12988f5ae --- stdlib/src/builtin/simd.mojo | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 2392a52ba7..3ac5afbad3 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -1879,29 +1879,22 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( func: The reduce function to apply to elements in this SIMD. size_out: The width of the reduction. + Constraints: + `size_out` must not exceed width of the vector. + Returns: A new scalar which is the reduction of all vector elements. """ - constrained[ - size_out <= Self.size, "simd reduction cannot increase simd width" - ]() + constrained[size_out <= size, "reduction cannot increase simd width"]() @parameter - if size == 1: - return self[0] - elif size == 2: - return func[type, 1](self[0], self[1]) - elif size == size_out: - return rebind[SIMD[Self.type, size_out]](self) + if size == size_out: + return rebind[SIMD[type, size_out]](self) else: - alias half_size: Int = size // 2 + alias half_size = size // 2 var lhs = self.slice[half_size, offset=0]() var rhs = self.slice[half_size, offset=half_size]() - - @parameter - if half_size != size_out: - return func[type, half_size](lhs, rhs).reduce[func, size_out]() - return rebind[SIMD[type, size_out]](func[type, half_size](lhs, rhs)) + return func[type, half_size](lhs, rhs).reduce[func, size_out]() @always_inline("nodebug") fn reduce_max[size_out: Int = 1](self) -> SIMD[type, size_out]: From 9eaf67c4e458374d458ba5410bd79452f6bf7b96 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Mon, 22 Apr 2024 14:00:17 -0500 Subject: [PATCH 0275/2019] [External][mojo-stdlib] `Optional.value()` returns a `Reference` (#2226) (#38062) Change `Optional.value()` to return a `Reference` to `T` rather than `T`. Fixes https://github.com/modularml/mojo/issues/2179. Signed-off-by: Lukas Hermann mojo-orig-commit: a904b348b29c648fac55af821d1db8fa0c926293 --------- Co-authored-by: Lukas Hermann Co-authored-by: Joe Loser MODULAR_ORIG_COMMIT_REV_ID: c07cde56dac507b04d8e9c1de83c82f63f3cae90 --- stdlib/src/builtin/hex.mojo | 2 +- stdlib/src/builtin/int.mojo | 2 +- stdlib/src/builtin/object.mojo | 2 +- stdlib/src/collections/dict.mojo | 16 ++++---- stdlib/src/collections/optional.mojo | 43 ++++++++++++++++------ stdlib/src/testing/testing.mojo | 4 +- stdlib/src/utils/inlined_string.mojo | 4 +- stdlib/test/collections/test_dict.mojo | 4 +- stdlib/test/collections/test_optional.mojo | 9 ++++- 9 files changed, 57 insertions(+), 29 deletions(-) diff --git a/stdlib/src/builtin/hex.mojo b/stdlib/src/builtin/hex.mojo index 231d7a53bd..20780b1ae2 100644 --- a/stdlib/src/builtin/hex.mojo +++ b/stdlib/src/builtin/hex.mojo @@ -86,7 +86,7 @@ fn _write_int( ) raises: var err = _try_write_int(fmt, value, radix, digit_chars, prefix) if err: - raise err.value() + raise err.value()[] @always_inline diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index f04fce4db4..b33aaaa681 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -315,7 +315,7 @@ struct Int(Intable, Stringable, KeyElement, Boolable, Formattable): if err: abort( "unreachable: unexpected write int failure condition: " - + str(err.value()) + + str(err.value()[]) ) else: # Stack allocate enough bytes to store any formatted 64-bit integer diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index 7107d073fe..d9e664051a 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -147,7 +147,7 @@ struct _RefCountedAttrsDict: fn get(self, key: StringLiteral) raises -> _ObjectImpl: var iter = self.impl[].find(key) if iter: - return iter.value() + return iter.value()[] raise Error( "AttributeError: Object does not have an attribute of name '" + key diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index e0dc8ca8f1..8b52d0a433 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -464,7 +464,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ var value = self.find(key) if value: - return value.value() + return value.value()[] raise "KeyError" fn __setitem__(inout self, key: K, value: V): @@ -517,7 +517,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( if found: var ev = self._entries.__get_ref(index)[] debug_assert(ev.__bool__(), "entry in index must be full") - return ev.value().value + return ev.value()[].value return None fn pop(inout self, key: K, owned default: Optional[V] = None) raises -> V: @@ -547,9 +547,9 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._entries[index] = None self.size -= 1 debug_assert(entry.__bool__(), "entry in index must be full") - return entry.value().value + return entry.value()[].value elif default: - return default.value() + return default.value()[] raise "KeyError" fn __iter__[ @@ -707,14 +707,14 @@ struct Dict[K: KeyElement, V: CollectionElement]( else: var ev = self._entries.__get_ref(index)[] debug_assert(ev.__bool__(), "entry in index must be full") - var entry = ev.value() + var entry = ev.value()[] if hash == entry.hash and key == entry.key: return (True, slot, index) self._next_index_slot(slot, perturb) debug_assert(insert_slot.__bool__(), "never found a slot") debug_assert(insert_index.__bool__(), "slot populated but not index!!") - return (False, insert_slot.value(), insert_index.value()) + return (False, insert_slot.value()[], insert_index.value()[]) fn _over_load_factor(self) -> Bool: return 3 * self.size > 2 * self._reserved @@ -737,7 +737,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( for i in range(len(old_entries)): var entry = old_entries.__get_ref(i)[] if entry: - self._insert(entry.value()) + self._insert(entry.value()[]) fn _compact(inout self): self._index = _DictIndex(self._reserved) @@ -748,7 +748,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( debug_assert(right < self._reserved, "Invalid dict state") var entry = self._entries.__get_ref(right)[] debug_assert(entry.__bool__(), "Logic error") - var slot = self._find_empty_index(entry.value().hash) + var slot = self._find_empty_index(entry.value()[].hash) self._set_index(slot, left) if left != right: self._entries[left] = entry diff --git a/stdlib/src/collections/optional.mojo b/stdlib/src/collections/optional.mojo index 201e6d0ece..1c01a369c8 100644 --- a/stdlib/src/collections/optional.mojo +++ b/stdlib/src/collections/optional.mojo @@ -21,13 +21,13 @@ from collections import Optional var a = Optional(1) var b = Optional[Int](None) if a: - print(a.value()) # prints 1 + print(a.value()[]) # prints 1 if b: # bool(b) is False, so no print - print(b.value()) + print(b.value()[]) var c = a.or_else(2) var d = b.or_else(2) -print(c) # prints 1 -print(d) # prints 2 +print(c.value()) # prints 1 +print(d.value()) # prints 2 ``` """ @@ -61,9 +61,9 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): var a = Optional(1) var b = Optional[Int](None) if a: - print(a.value()) # prints 1 + print(a.value()[]) # prints 1 if b: # bool(b) is False, so no print - print(b.value()) + print(b.value()[]) var c = a.or_else(2) var d = b.or_else(2) print(c) # prints 1 @@ -100,20 +100,39 @@ struct Optional[T: CollectionElement](CollectionElement, Boolable): self = Self() @always_inline - fn value(self) -> T: - """Unsafely retrieve the value out of the Optional. - - This function currently creates a copy. Once we have lifetimes - we'll be able to have it return a reference. + fn value[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ](self: Reference[Self, mutability, self_life]._mlir_type) -> Reference[ + T, mutability, self_life + ]: + """Unsafely retrieve a reference to the value of the Optional. This doesn't check to see if the optional contains a value. If you call this without first verifying the optional with __bool__() eg. by `if my_option:` or without otherwise knowing that it contains a value (for instance with `or_else`), you'll get garbage unsafe data out. + Parameters: + mutability: Whether the Optional is mutable. + self_life: The Optional's lifetime. + Returns: - The contained data of the option as a T value. + A reference to the contained data of the option as a Reference[T]. """ + debug_assert(Reference(self)[].__bool__(), ".value() on empty Optional") + alias RefType = Reference[T, mutability, self_life] + var ptr = Reference(self)[]._value._get_ptr[T]().address + return __mlir_op.`lit.ref.from_pointer`[_type = RefType._mlir_type](ptr) + + @always_inline + fn _value_copy(self) -> T: + """Unsafely retrieve the value out of the Optional. + + Note: only used for Optionals when used in a parameter context + due to compiler bugs. In general, prefer using the public `Optional.value()` + function that returns a `Reference[T]`. + """ + debug_assert(self.__bool__(), ".value() on empty Optional") return self._value.get[T]()[] diff --git a/stdlib/src/testing/testing.mojo b/stdlib/src/testing/testing.mojo index d0b6243c70..1d48ff0cde 100644 --- a/stdlib/src/testing/testing.mojo +++ b/stdlib/src/testing/testing.mojo @@ -357,5 +357,7 @@ struct assert_raises: Error: If the error raised doesn't match the expected error to raise. """ if self.message_contains: - return self.message_contains.value() in str(error) + return self.message_contains.value[ + __mlir_attr.`0: i1`, __lifetime_of(self) + ]()[] in str(error) return True diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index b3b6566da7..bcf2c946b7 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -347,7 +347,7 @@ struct _FixedString[CAP: Int]( """ var err = self._iadd_non_raising(strref) if err: - raise err.value() + raise err.value()[] fn _iadd_non_raising(inout self, strref: StringRef) -> Optional[Error]: var total_len = len(self) + len(strref) @@ -388,7 +388,7 @@ struct _FixedString[CAP: Int]( # abort("error formatting to FixedString: " + str(e)) var err = ptr[]._iadd_non_raising(strref) if err: - abort("error formatting to FixedString: " + str(err.value())) + abort("error formatting to FixedString: " + str(err.value()[])) return Formatter( write_to_string, diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index e1dfda19ae..ea9c57e781 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -202,8 +202,8 @@ def test_dict_copy_calls_copy_constructor(): var copy = Dict(orig) # I _may_ have thoughts about where our performance issues # are coming from :) - assert_equal(5, orig["a"].copy_count) - assert_equal(6, copy["a"].copy_count) + assert_equal(4, orig["a"].copy_count) + assert_equal(5, copy["a"].copy_count) def test_dict_update_nominal(): diff --git a/stdlib/test/collections/test_optional.mojo b/stdlib/test/collections/test_optional.mojo index 87a0608cf2..f2a843d369 100644 --- a/stdlib/test/collections/test_optional.mojo +++ b/stdlib/test/collections/test_optional.mojo @@ -37,7 +37,7 @@ def test_basic(): assert_true(b or True) assert_false(b or False) - assert_equal(1, a.value()) + assert_equal(1, a.value()[]) # Test invert operator assert_false(~a) @@ -52,6 +52,13 @@ def test_basic(): assert_equal(1, (a^).take()) + # TODO: this currently only checks for mutable references. + # We may want to come back and add an immutable test once + # there are the language features to do so. + var a2 = Optional(1) + a2.value()[] = 2 + assert_equal(a2.value()[], 2) + def test_optional_reg_basic(): print("== test_optional_reg_basic") From 2a37fa92f8e22a1718dfb03ed3f37c6b75212c26 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 22 Apr 2024 12:30:52 -0700 Subject: [PATCH 0276/2019] [mojo-stdlib] Split `initialize_pointee` into two functions. (#38321) This splits this into two functions, one that takes the 'value' as an owned rvalue and one that takes it borrowed. When used appropriately, this eliminates an extra copy/move as shown in the tuple testcase. MODULAR_ORIG_COMMIT_REV_ID: 4a1aff89bfacae2a1ef59600a0be916c4e4077a0 --- docs/changelog.md | 2 +- stdlib/src/builtin/tuple.mojo | 11 ++++--- stdlib/src/collections/list.mojo | 10 +++---- stdlib/src/memory/unsafe_pointer.mojo | 35 +++++++++++++++++++--- stdlib/src/utils/variant.mojo | 8 ++--- stdlib/test/memory/test_unsafepointer.mojo | 6 ++-- 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4778167d4c..9ff6c1118a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -175,7 +175,7 @@ what we publish. 1) The element type can now be `AnyType`: it doesn't require `Movable`. 2) Because of this, the `take_value`, `emplace_value`, and `move_into` methods have been changed to be top-level functions, and were renamed to - `move_from_pointee`, `initialize_pointee` and `move_pointee` respectively. + `move_from_pointee`, `initialize_pointee_*` and `move_pointee` respectively. 3) A new `destroy_pointee` function runs the destructor on the pointee. 4) `UnsafePointer` can be initialized directly from a `Reference` with `UnsafePointer(someRef)` and can convert to an immortal reference with diff --git a/stdlib/src/builtin/tuple.mojo b/stdlib/src/builtin/tuple.mojo index 500d941b9a..5857e59ae5 100644 --- a/stdlib/src/builtin/tuple.mojo +++ b/stdlib/src/builtin/tuple.mojo @@ -17,7 +17,10 @@ These are Mojo built-ins, so you don't need to import them. from utils._visualizers import lldb_formatter_wrapping_type -from memory.unsafe_pointer import initialize_pointee +from memory.unsafe_pointer import ( + initialize_pointee_move, + initialize_pointee_copy, +) # ===----------------------------------------------------------------------===# # Tuple @@ -74,7 +77,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): fn initialize_elt[idx: Int](): # TODO: We could be fancier and take the values out of an owned # pack. For now just keep everything simple and copy the element. - initialize_pointee( + initialize_pointee_copy( UnsafePointer(self[idx]), storage.get_element[idx]()[], ) @@ -106,7 +109,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): - initialize_pointee(UnsafePointer(self[idx]), existing[idx]) + initialize_pointee_copy(UnsafePointer(self[idx]), existing[idx]) unroll[initialize_elt, Self.__len__()]() @@ -125,7 +128,7 @@ struct Tuple[*element_types: CollectionElement](Sized, CollectionElement): @parameter fn initialize_elt[idx: Int](): var existing_elt_ptr = UnsafePointer(existing[idx]).address - initialize_pointee( + initialize_pointee_move( UnsafePointer(self[idx]), __get_address_as_owned_value(existing_elt_ptr), ) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index e31f0ef37b..16a4aba6a0 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -204,7 +204,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ if self.size >= self.capacity: self._realloc(_max(1, self.capacity * 2)) - initialize_pointee(self.data + self.size, value^) + initialize_pointee_move(self.data + self.size, value^) self.size += 1 @always_inline @@ -232,7 +232,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var tmp = move_from_pointee(earlier_ptr) move_pointee(src=later_ptr, dst=earlier_ptr) - initialize_pointee(later_ptr, tmp^) + initialize_pointee_move(later_ptr, tmp^) earlier_idx -= 1 later_idx -= 1 @@ -336,7 +336,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): for i in range(new_size, self.size): destroy_pointee(self.data + i) for i in range(self.size, new_size): - initialize_pointee(self.data + i, value) + initialize_pointee_copy(self.data + i, value) self.size = new_size @always_inline @@ -394,7 +394,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): var tmp = move_from_pointee(earlier_ptr) move_pointee(src=later_ptr, dst=earlier_ptr) - initialize_pointee(later_ptr, tmp^) + initialize_pointee_move(later_ptr, tmp^) earlier_idx += 1 later_idx -= 1 @@ -431,7 +431,7 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): normalized_idx += len(self) destroy_pointee(self.data + normalized_idx) - initialize_pointee(self.data + normalized_idx, value^) + initialize_pointee_move(self.data + normalized_idx, value^) @always_inline fn _adjust_span(self, span: Slice) -> Slice: diff --git a/stdlib/src/memory/unsafe_pointer.mojo b/stdlib/src/memory/unsafe_pointer.mojo index e8fe367f52..82fa64502f 100644 --- a/stdlib/src/memory/unsafe_pointer.mojo +++ b/stdlib/src/memory/unsafe_pointer.mojo @@ -372,7 +372,7 @@ fn move_from_pointee[T: Movable](ptr: UnsafePointer[T, _]) -> T: This performs a _consuming_ move, ending the lifetime of the value stored in this pointer memory location. Subsequent reads of this pointer are - not valid. If a new valid value is stored using `initialize_pointee()`, then + not valid. If a new valid value is stored using `initialize_pointee_move()`, then reading from this pointer becomes valid again. Parameters: @@ -388,14 +388,19 @@ fn move_from_pointee[T: Movable](ptr: UnsafePointer[T, _]) -> T: @always_inline -fn initialize_pointee[T: Movable](ptr: UnsafePointer[T, _], owned value: T): - """Emplace a new value into the pointer location. +fn initialize_pointee_move[ + T: Movable +](ptr: UnsafePointer[T, _], owned value: T): + """Emplace a new value into the pointer location, moving from `value`. The pointer memory location is assumed to contain uninitialized data, and consequently the current contents of this pointer are not destructed before writing `value`. Similarly, ownership of `value` is logically transfered into the pointer location. + When compared to `initialize_pointee_copy`, this avoids an extra copy on + the caller side when the value is an `owned` rvalue. + Parameters: T: The type the pointer points to, which must be `Movable`. @@ -406,6 +411,28 @@ fn initialize_pointee[T: Movable](ptr: UnsafePointer[T, _], owned value: T): __get_address_as_uninit_lvalue(ptr.address) = value^ +@always_inline +fn initialize_pointee_copy[T: Copyable](ptr: UnsafePointer[T, _], value: T): + """Emplace a copy of `value` into the pointer location. + + The pointer memory location is assumed to contain uninitialized data, + and consequently the current contents of this pointer are not destructed + before writing `value`. Similarly, ownership of `value` is logically + transfered into the pointer location. + + When compared to `initialize_pointee_move`, this avoids an extra move on + the callee side when the value must be copied. + + Parameters: + T: The type the pointer points to, which must be `Copyable`. + + Args: + ptr: The pointer to initialize through. + value: The value to emplace. + """ + __get_address_as_uninit_lvalue(ptr.address) = value + + @always_inline fn move_pointee[T: Movable](*, src: UnsafePointer[T, _], dst: UnsafePointer[T]): """Moves the value `src` points to into the memory location pointed to by @@ -414,7 +441,7 @@ fn move_pointee[T: Movable](*, src: UnsafePointer[T, _], dst: UnsafePointer[T]): This performs a consuming move (using `__moveinit__()`) out of the memory location pointed to by `src`. Subsequent reads of this pointer are not valid unless and until a new, valid value has been - moved into this pointer's memory location using `initialize_pointee()`. + moved into this pointer's memory location using `initialize_pointee_move()`. This transfers the value out of `self` and into `dest` using at most one `__moveinit__()` call. diff --git a/stdlib/src/utils/variant.mojo b/stdlib/src/utils/variant.mojo index c26c11081b..451098a887 100644 --- a/stdlib/src/utils/variant.mojo +++ b/stdlib/src/utils/variant.mojo @@ -43,7 +43,7 @@ from sys.intrinsics import _mlirtype_is_eq from memory import UnsafePointer from memory.unsafe_pointer import ( - initialize_pointee, + initialize_pointee_move, move_from_pointee, move_pointee, ) @@ -176,7 +176,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ self._impl = __mlir_attr[`#kgen.unknown : `, self._type] self._get_state()[] = Self._check[T]() - initialize_pointee(self._get_ptr[T](), value^) + initialize_pointee_move(self._get_ptr[T](), value^) @always_inline fn __copyinit__(inout self, other: Self): @@ -192,7 +192,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): fn each[i: Int](): if self._get_state()[] == i: alias T = Ts[i] - initialize_pointee[T]( + initialize_pointee_move( UnsafePointer.address_of(self._impl).bitcast[T](), Reference(other._impl).unsafe_bitcast[T]()[], ) @@ -273,7 +273,7 @@ struct Variant[*Ts: CollectionElement](CollectionElement): """ self._call_correct_deleter() self._get_state()[] = Self._check[T]() - initialize_pointee(self._get_ptr[T](), value^) + initialize_pointee_move(self._get_ptr[T](), value^) fn isa[T: CollectionElement](self) -> Bool: """Check if the variant contains the required type. diff --git a/stdlib/test/memory/test_unsafepointer.mojo b/stdlib/test/memory/test_unsafepointer.mojo index 6c41923a43..b5ec147847 100644 --- a/stdlib/test/memory/test_unsafepointer.mojo +++ b/stdlib/test/memory/test_unsafepointer.mojo @@ -40,10 +40,10 @@ struct MoveOnlyType(Movable): def test_unsafepointer_of_move_only_type(): var actions_ptr = UnsafePointer[List[String]].alloc(1) - initialize_pointee(actions_ptr, List[String]()) + initialize_pointee_move(actions_ptr, List[String]()) var ptr = UnsafePointer[MoveOnlyType].alloc(1) - initialize_pointee(ptr, MoveOnlyType(42, actions_ptr)) + initialize_pointee_move(ptr, MoveOnlyType(42, actions_ptr)) assert_equal(len(actions_ptr[0]), 2) assert_equal(actions_ptr[0][0], "__init__") assert_equal(actions_ptr[0][1], "__moveinit__", msg="emplace_value") @@ -66,7 +66,7 @@ def test_unsafepointer_move_pointee_move_count(): var value = MoveCounter(5) assert_equal(0, value.move_count) - initialize_pointee(ptr, value^) + initialize_pointee_move(ptr, value^) # ----- # Test that `UnsafePointer.move_pointee` performs exactly one move. From 110c27cda010d42a2719ba7b3ae5a6709a565682 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Mon, 22 Apr 2024 17:13:22 -0500 Subject: [PATCH 0277/2019] [stdlib] bugfix: Fix missing import of function in test_macos_target.mojo (#38343) MODULAR_ORIG_COMMIT_REV_ID: a7207064181e1b206af6127ac35adac8b9b121d8 --- stdlib/test/sys/test_macos_target.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 27f59d5eec..5d7d53667e 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -24,8 +24,8 @@ from sys import ( os_is_linux, os_is_macos, os_is_windows, - _macos_version, ) +from sys.info import _macos_version from testing import assert_true, assert_false from testing import assert_true From faf5493d8387995431f3d2bfe0d65ea16d5a3a82 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 23 Apr 2024 08:43:08 -0700 Subject: [PATCH 0278/2019] [mojo-lang] Teach parameter inference about non-materializables (#38292) This enhances it to properly substitute inferred parameter values into the type signature to support dependent argument inference. MODULAR_ORIG_COMMIT_REV_ID: 55aeba5cc4e98560014fa03b0d356e1690354ff8 --- examples/notebooks/RayTracing.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/notebooks/RayTracing.ipynb b/examples/notebooks/RayTracing.ipynb index 9d1ddf9596..8a40915b46 100644 --- a/examples/notebooks/RayTracing.ipynb +++ b/examples/notebooks/RayTracing.ipynb @@ -747,7 +747,7 @@ " var diffuse_light_intensity: Float32 = 0\n", " for i in range(lights.size):\n", " var light_dir = (lights[i].position - point).normalize()\n", - " diffuse_light_intensity += lights[i].intensity * max(0, light_dir @ N)\n", + " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", "\n", " return material.color * diffuse_light_intensity\n", "\n", @@ -838,10 +838,10 @@ " var specular_light_intensity: Float32 = 0\n", " for i in range(lights.size):\n", " var light_dir = (lights[i].position - point).normalize()\n", - " diffuse_light_intensity += lights[i].intensity * max(0, light_dir @ N)\n", + " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", " specular_light_intensity += (\n", " pow(\n", - " max(0.0, -reflect(-light_dir, N) @ dir),\n", + " max(-reflect(-light_dir, N) @ dir, 0.0),\n", " material.specular_component,\n", " )\n", " * lights[i].intensity\n", @@ -949,10 +949,10 @@ " var specular_light_intensity: Float32 = 0\n", " for i in range(lights.size):\n", " var light_dir = (lights[i].position - point).normalize()\n", - " diffuse_light_intensity += lights[i].intensity * max(0, light_dir @ N)\n", + " diffuse_light_intensity += lights[i].intensity * max(light_dir @ N, 0)\n", " specular_light_intensity += (\n", " pow(\n", - " max(0.0, -reflect(-light_dir, N) @ dir),\n", + " max(-reflect(-light_dir, N) @ dir, 0.0),\n", " material.specular_component,\n", " )\n", " * lights[i].intensity\n", From cabc0541ad6855e2164aacdd78f82f130f65d765 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 23 Apr 2024 09:27:58 -0700 Subject: [PATCH 0279/2019] [mojo-stdlib] Remove unconstrained mixed dtype pow overloads. (#38385) This removes the `x**y` operations that don't constrain the RHS type (but keeps the one with an Int RHS). The problem with these is that they are significant footguns in cases like `x**0.5` because the compiler infers the RHS to be F64 (the materialization type of FloatLiteral) regardless of what the LHS type is. The pow() global function documents that the LHS and RHS types must be the same, so use the compiler to enforce this. I discovered this while working on improving type inference. MODULAR_ORIG_COMMIT_REV_ID: 30db26e3fe29a6818cbe6df64ccb0c6f329d0522 --- stdlib/src/builtin/simd.mojo | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/stdlib/src/builtin/simd.mojo b/stdlib/src/builtin/simd.mojo index 3ac5afbad3..8e8e6d62f3 100644 --- a/stdlib/src/builtin/simd.mojo +++ b/stdlib/src/builtin/simd.mojo @@ -727,23 +727,6 @@ struct SIMD[type: DType, size: Int = simdwidthof[type]()]( constrained[type.is_numeric(), "the SIMD type must be numeric"]() return _pow(self, rhs) - @always_inline("nodebug") - fn __pow__[rhs_type: DType](self, rhs: SIMD[rhs_type, size]) -> Self: - """Computes the vector raised elementwise to the right hand side power. - - Parameters: - rhs_type: The `dtype` of the rhs SIMD vector. - - Args: - rhs: The exponential value. - - Returns: - A SIMD vector where each element is raised to the power of the - specified exponential value. - """ - constrained[type.is_numeric(), "the SIMD type must be numeric"]() - return _pow(self, rhs) - @always_inline("nodebug") fn __lt__(self, rhs: Self) -> SIMD[DType.bool, size]: """Compares two SIMD vectors using less-than comparison. From 344b68b712c11e56fe8a6b899413a4ccf728cc5f Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:30:42 -0500 Subject: [PATCH 0280/2019] [External][stdlib] Add `reverse` for reverse iterators (#38183) Addsan initial implementation of `reversed()`, for getting a reversed iterator of a range or list. This isn't meant to be final, since iterators are not fully worked out yet, but it works for now. Changes: - Added submodule `/builtin/reversed.mojo` - Added function `reversed()` - Added trait `ReversibleRange` - Implemented `ReversibleRange` for ranges - Added method `__reversed__()` in `List` - Changed `_ListIter` to allow for backwards iteration - Added tests for ranges and reversed list iterator - Fixed original ranges with negative size not passing the new tests Signed-off-by: Max Brylski mojo-orig-commit: d590ad4fc7ade132fd1fa8b07b11bf6e6367b37e --------- Co-authored-by: Helehex Co-authored-by: abdul dakkak Co-authored-by: Connor Gray MODULAR_ORIG_COMMIT_REV_ID: fea37bfbfc170fa6f3a23d18128b57655b9362b5 --- stdlib/src/builtin/range.mojo | 50 ++++++++--- stdlib/src/builtin/reversed.mojo | 101 +++++++++++++++++++++++ stdlib/src/collections/list.mojo | 41 +++++++-- stdlib/test/builtin/test_range.mojo | 110 +++++++++++++++++++++---- stdlib/test/builtin/test_reversed.mojo | 28 +++++++ 5 files changed, 300 insertions(+), 30 deletions(-) create mode 100644 stdlib/src/builtin/reversed.mojo create mode 100644 stdlib/test/builtin/test_reversed.mojo diff --git a/stdlib/src/builtin/range.mojo b/stdlib/src/builtin/range.mojo index 426442b02a..2d2e9dc032 100644 --- a/stdlib/src/builtin/range.mojo +++ b/stdlib/src/builtin/range.mojo @@ -48,6 +48,15 @@ fn _abs(x: Int) -> Int: return x if x > 0 else -x +@always_inline +fn _sign(x: Int) -> Int: + if x > 0: + return 1 + if x < 0: + return -1 + return 0 + + @always_inline fn _max(a: Int, b: Int) -> Int: return a if a > b else b @@ -59,14 +68,14 @@ fn _max(a: Int, b: Int) -> Int: @register_passable("trivial") -struct _ZeroStartingRange(Sized): +struct _ZeroStartingRange(Sized, ReversibleRange): var curr: Int var end: Int @always_inline("nodebug") fn __init__(inout self, end: Int): self.curr = _max(0, end) - self.end = self.curr + self.end = end @always_inline("nodebug") fn __iter__(self) -> Self: @@ -86,10 +95,14 @@ struct _ZeroStartingRange(Sized): fn __getitem__(self, idx: Int) -> Int: return idx + @always_inline("nodebug") + fn __reversed__(self) -> _StridedRangeIterator: + return _StridedRangeIterator(self.end - 1, -1, -1) + @value @register_passable("trivial") -struct _SequentialRange(Sized): +struct _SequentialRange(Sized, ReversibleRange): var start: Int var end: Int @@ -105,12 +118,18 @@ struct _SequentialRange(Sized): @always_inline("nodebug") fn __len__(self) -> Int: + # FIXME(#38392): + # return _max(0, self.end - self.start) return self.end - self.start if self.start < self.end else 0 @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: return self.start + idx + @always_inline("nodebug") + fn __reversed__(self) -> _StridedRangeIterator: + return _StridedRangeIterator(self.end - 1, self.start - 1, -1) + @value @register_passable("trivial") @@ -119,6 +138,10 @@ struct _StridedRangeIterator(Sized): var end: Int var step: Int + @always_inline("nodebug") + fn __iter__(self) -> Self: + return self + @always_inline fn __len__(self) -> Int: if self.step > 0 and self.start < self.end: @@ -137,7 +160,7 @@ struct _StridedRangeIterator(Sized): @value @register_passable("trivial") -struct _StridedRange(Sized): +struct _StridedRange(Sized, ReversibleRange): var start: Int var end: Int var step: Int @@ -166,12 +189,23 @@ struct _StridedRange(Sized): @always_inline("nodebug") fn __len__(self) -> Int: + # FIXME(#38392) + # if (self.step > 0) == (self.start > self.end): + # return 0 return _div_ceil_positive(_abs(self.start - self.end), _abs(self.step)) @always_inline("nodebug") fn __getitem__(self, idx: Int) -> Int: return self.start + idx * self.step + @always_inline("nodebug") + fn __reversed__(self) -> _StridedRangeIterator: + var shifted_end = self.end - _sign(self.step) + var start = shifted_end - ((shifted_end - self.start) % self.step) + var end = self.start - self.step + var step = -self.step + return _StridedRangeIterator(start, end, step) + @always_inline("nodebug") fn range[type: Intable](end: type) -> _ZeroStartingRange: @@ -220,9 +254,7 @@ fn range[t0: Intable, t1: Intable](start: t0, end: t1) -> _SequentialRange: Returns: The constructed range. """ - var s = int(start) - var e = int(end) - return _SequentialRange(s, e) + return _SequentialRange(int(start), int(end)) @always_inline("nodebug") @@ -242,9 +274,7 @@ fn range[ Returns: The constructed range. """ - var s = int(start) - var e = int(end) - return _SequentialRange(s, e) + return _SequentialRange(int(start), int(end)) @always_inline diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo new file mode 100644 index 0000000000..117af59eef --- /dev/null +++ b/stdlib/src/builtin/reversed.mojo @@ -0,0 +1,101 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +"""Provides the `reversed` function for reverse iteration over collections. + +These are Mojo built-ins, so you don't need to import them. +""" + +from .range import _StridedRangeIterator + +from collections.list import _ListIter + +# ===----------------------------------------------------------------------=== # +# Reversible +# ===----------------------------------------------------------------------=== # + + +trait ReversibleRange: + """ + The `ReversibleRange` trait describes a range that can be reversed. + + Any type that conforms to `ReversibleRange` works with the builtin + [`reversed()`](/mojo/stdlib/builtin/reversed.html) functions. + + The `ReversibleRange` trait requires the type to define the `__reversed__()` + method. + + **Note**: iterators are currently non-raising. + """ + + # TODO: general `Reversible` trait that returns an iterator. + # iterators currently check __len__() instead of raising an exception + # so there is no ReversibleRaising trait yet. + + fn __reversed__(self) -> _StridedRangeIterator: + """Get a reversed iterator for the type. + + **Note**: iterators are currently non-raising. + + Returns: + The reversed iterator of the type. + """ + ... + + +# ===----------------------------------------------------------------------=== # +# reversed +# ===----------------------------------------------------------------------=== # + + +fn reversed[T: ReversibleRange](value: T) -> _StridedRangeIterator: + """Get a reversed iterator of the input range. + + **Note**: iterators are currently non-raising. + + Parameters: + T: The type conforming to ReversibleRange. + + Args: + value: The range to get the reversed iterator of. + + Returns: + The reversed iterator of the range. + """ + return value.__reversed__() + + +fn reversed[ + mutability: __mlir_type.`i1`, + self_life: AnyLifetime[mutability].type, + T: CollectionElement, +]( + value: Reference[List[T], mutability, self_life]._mlir_type, +) -> _ListIter[ + T, mutability, self_life, False +]: + """Get a reversed iterator of the input list. + + **Note**: iterators are currently non-raising. + + Parameters: + mutability: Whether the reference to the list is mutable. + self_life: The lifetime of the list. + T: The type of the elements in the list. + + Args: + value: The list to get the reversed iterator of. + + Returns: + The reversed iterator of the list. + """ + return Reference(value)[].__reversed__[mutability, self_life]() diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 16a4aba6a0..0b56377962 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -44,6 +44,7 @@ struct _ListIter[ T: CollectionElement, list_mutability: __mlir_type.`i1`, list_lifetime: AnyLifetime[list_mutability].type, + forward: Bool = True, ]: """Iterator for List. @@ -51,6 +52,7 @@ struct _ListIter[ T: The type of the elements in the list. list_mutability: Whether the reference to the list is mutable. list_lifetime: The lifetime of the List + forward: The iteration direction. `False` is backwards. """ alias list_type = List[T] @@ -58,16 +60,30 @@ struct _ListIter[ var index: Int var src: Reference[Self.list_type, list_mutability, list_lifetime] + fn __iter__(self) -> Self: + return self + fn __next__( inout self, ) -> Reference[T, list_mutability, list_lifetime]: - self.index += 1 - return self.src[].__get_ref[list_mutability, list_lifetime]( - self.index - 1 - ) + @parameter + if forward: + self.index += 1 + return self.src[].__get_ref[list_mutability, list_lifetime]( + self.index - 1 + ) + else: + self.index -= 1 + return self.src[].__get_ref[list_mutability, list_lifetime]( + self.index + ) fn __len__(self) -> Int: - return len(self.src[]) - self.index + @parameter + if forward: + return len(self.src[]) - self.index + else: + return self.index struct List[T: CollectionElement](CollectionElement, Sized, Boolable): @@ -531,3 +547,18 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): An iterator of immutable references to the list elements. """ return _ListIter[T, mutability, self_life](0, Reference(self)) + + fn __reversed__[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ]( + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _ListIter[ + T, mutability, self_life, False + ]: + """Iterate backwards over the list, returning immutable references. + + Returns: + A reversed iterator of immutable references to the list elements. + """ + var ref = Reference(self) + return _ListIter[T, mutability, self_life, False](len(ref[]), ref) diff --git a/stdlib/test/builtin/test_range.mojo b/stdlib/test/builtin/test_range.mojo index 6ed3f71fec..637d38eefd 100644 --- a/stdlib/test/builtin/test_range.mojo +++ b/stdlib/test/builtin/test_range.mojo @@ -17,25 +17,105 @@ from testing import assert_equal from testing import assert_equal -fn test_range_len() raises: - assert_equal(range(0).__len__(), 0) - assert_equal(range(-1).__len__(), 0) - assert_equal(range(10).__len__(), 10) - assert_equal(range(0, 10).__len__(), 10) - assert_equal(range(5, 10).__len__(), 5) - assert_equal(range(10, 0, -1).__len__(), 10) - assert_equal(range(0, 10, 2).__len__(), 5) - assert_equal(range(38, -13, -23).__len__(), 3) +def test_range_len(): + # Usual cases + assert_equal(range(10).__len__(), 10, "len(range(10))") + assert_equal(range(0, 10).__len__(), 10, "len(range(0, 10))") + assert_equal(range(5, 10).__len__(), 5, "len(range(5, 10))") + assert_equal(range(10, 0, -1).__len__(), 10, "len(range(10, 0, -1))") + assert_equal(range(0, 10, 2).__len__(), 5, "len(range(0, 10, 2))") + assert_equal(range(38, -13, -23).__len__(), 3, "len(range(38, -13, -23))") + # Edge cases + assert_equal(range(0).__len__(), 0, "len(range(0))") + assert_equal(range(-10).__len__(), 0, "len(range(-10))") + assert_equal(range(0, 0).__len__(), 0, "len(range(0, 0))") + assert_equal(range(10, 0).__len__(), 0, "len(range(10, 0))") + assert_equal(range(0, 0, 1).__len__(), 0, "len(range(0, 0, 1))") + # FIXME(#38392) + # assert_equal(range(5, 10, -1).__len__(), 0, "len(range(5, 10, -1))") + # assert_equal(range(10, 5, 1).__len__(), 0, "len(range(10, 5, 1))") + # assert_equal(range(5, 10, -10).__len__(), 0, "len(range(5, 10, -10))") + # assert_equal(range(10, 5, 10).__len__(), 0, "len(range(10, 5, 10))") + assert_equal(range(5, 10, 20).__len__(), 1, "len(range(5, 10, 20))") + assert_equal(range(10, 5, -20).__len__(), 1, "len(range(10, 5, -20))") -fn test_range_getitem() raises: - assert_equal(range(10)[5], 5) - assert_equal(range(0, 10)[3], 3) - assert_equal(range(5, 10)[3], 8) - assert_equal(range(10, 0, -1)[2], 8) - assert_equal(range(0, 10, 2)[4], 8) + +def test_range_getitem(): + # Usual cases + assert_equal(range(10)[3], 3, "range(10)[3]") + assert_equal(range(0, 10)[3], 3, "range(0, 10)[3]") + assert_equal(range(5, 10)[3], 8, "range(5, 10)[3]") + assert_equal(range(5, 10)[4], 9, "range(5, 10)[4]") + assert_equal(range(10, 0, -1)[2], 8, "range(10, 0, -1)[2]") + assert_equal(range(0, 10, 2)[4], 8, "range(0, 10, 2)[4]") + assert_equal(range(38, -13, -23)[1], 15, "range(38, -13, -23)[1]") + + +def test_range_reversed(): + # Zero starting + assert_equal( + range(10).__reversed__().start, 9, "range(10).__reversed__().start" + ) + assert_equal( + range(10).__reversed__().end, -1, "range(10).__reversed__().end" + ) + assert_equal( + range(10).__reversed__().step, -1, "range(10).__reversed__().step" + ) + # Sequential + assert_equal( + range(5, 10).__reversed__().start, 9, "range(5,10).__reversed__().start" + ) + assert_equal( + range(5, 10).__reversed__().end, 4, "range(5,10).__reversed__().end" + ) + assert_equal( + range(5, 10).__reversed__().step, -1, "range(5,10).__reversed__().step" + ) + # Strided + assert_equal( + range(38, -13, -23).__reversed__().start, + -8, + "range(38, -13, -23).__reversed__().start", + ) + assert_equal( + range(38, -13, -23).__reversed__().end, + 61, + "range(38, -13, -23).__reversed__().end", + ) + assert_equal( + range(38, -13, -23).__reversed__().step, + 23, + "range(38, -13, -23).__reversed__().step", + ) + + # Test a reversed range's sum and length compared to the original + @parameter + fn test_sum_reversed(start: Int, end: Int, step: Int) raises: + var forward = range(start, end, step) + var iforward = forward.__iter__() + var ibackward = forward.__reversed__() + var backward = range(ibackward.start, ibackward.end, ibackward.step) + assert_equal( + forward.__len__(), backward.__len__(), "len(forward), len(backward)" + ) + var forward_sum = 0 + var backward_sum = 0 + for i in range(len(forward)): + forward_sum += iforward.__next__() + backward_sum += ibackward.__next__() + assert_equal(forward_sum, backward_sum, "forward_sum, backward_sum") + + # Test using loops and reversed + for end in range(10, 13): + test_sum_reversed(1, end, 3) + + for end in range(10, 13).__reversed__(): + test_sum_reversed(20, end, -3) def main(): test_range_len() test_range_getitem() + test_range_reversed() diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo new file mode 100644 index 0000000000..35c4a5ad66 --- /dev/null +++ b/stdlib/test/builtin/test_reversed.mojo @@ -0,0 +1,28 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo %s + +from testing import assert_equal + + +def test_reversed_list(): + var list = List[Int](1, 2, 3, 4, 5, 6) + var check: Int = 6 + + for item in reversed(list): + assert_equal(item[], check, "item[], check") + check -= 1 + + +def main(): + test_reversed_list() From c9b9ba051c82ed709c56e4fa7b50d677fe1a134b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:31:49 -0500 Subject: [PATCH 0281/2019] [External][stdlib] Implement `os.remove` and `os.unlink` (#2310) (#38370) Add `os.remove` and `os.unlink` functions. Fixes https://github.com/modularml/mojo/issues/2306 --------- Signed-off-by: Artemio Garza Reyna mojo-orig-commit: 20737dcb02ebf091c77b931e5e8b8abb2d4bb164 Co-authored-by: artemiogr97 <57588855+artemiogr97@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 2fe3ba9cb40bc5955131de5bca317aae3819e365 --- stdlib/src/os/__init__.mojo | 2 +- stdlib/src/os/os.mojo | 64 ++++++++++++++++++++++++++ stdlib/test/os/test_remove.mojo | 79 +++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 stdlib/test/os/test_remove.mojo diff --git a/stdlib/src/os/__init__.mojo b/stdlib/src/os/__init__.mojo index c0afa1c331..634a38eaf2 100644 --- a/stdlib/src/os/__init__.mojo +++ b/stdlib/src/os/__init__.mojo @@ -15,5 +15,5 @@ from .atomic import Atomic from .env import setenv, getenv from .fstat import lstat, stat, stat_result -from .os import abort, listdir, SEEK_SET, SEEK_CUR, SEEK_END +from .os import abort, listdir, remove, unlink, SEEK_SET, SEEK_CUR, SEEK_END from .pathlike import PathLike diff --git a/stdlib/src/os/os.mojo b/stdlib/src/os/os.mojo index 0462a2b778..6baf03abe1 100644 --- a/stdlib/src/os/os.mojo +++ b/stdlib/src/os/os.mojo @@ -264,3 +264,67 @@ fn abort[ print(message, flush=True) return abort[result]() + + +# ===----------------------------------------------------------------------=== # +# remove/unlink +# ===----------------------------------------------------------------------=== # +fn remove(path: String) raises: + """Removes the specified file. + If the path is a directory or it can not be deleted, an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Args: + path: The path to the file. + + """ + var error = external_call["unlink", Int](path._as_ptr()) + + if error != 0: + # TODO get error message, the following code prints it + # var error_str = String("Something went wrong") + # _ = external_call["perror", Pointer[NoneType]](error_str._as_ptr()) + # _ = error_str + raise Error("Can not remove file: " + path) + + +fn remove[pathlike: os.PathLike](path: pathlike) raises: + """Removes the specified file. + If the path is a directory or it can not be deleted, an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Parameters: + pathlike: The a type conforming to the os.PathLike trait. + + Args: + path: The path to the file. + + """ + remove(path.__fspath__()) + + +fn unlink(path: String) raises: + """Removes the specified file. + If the path is a directory or it can not be deleted, an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Args: + path: The path to the file. + + """ + remove(path) + + +fn unlink[pathlike: os.PathLike](path: pathlike) raises: + """Removes the specified file. + If the path is a directory or it can not be deleted, an error is raised. + Absolute and relative paths are allowed, relative paths are resolved from cwd. + + Parameters: + pathlike: The a type conforming to the os.PathLike trait. + + Args: + path: The path to the file. + + """ + remove(path.__fspath__()) diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo new file mode 100644 index 0000000000..bb9a9821cd --- /dev/null +++ b/stdlib/test/os/test_remove.mojo @@ -0,0 +1,79 @@ +# ===----------------------------------------------------------------------=== # +# Copyright (c) 2024, Modular Inc. All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions: +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ===----------------------------------------------------------------------=== # +# RUN: %mojo-no-debug %s + +from os import remove, unlink +from os.path import exists +from pathlib import Path + +from testing import assert_true, assert_false, assert_raises + + +fn create_file_and_test_delete_string[ + func: fn (String) raises -> None, name: StringLiteral +](filename: String) raises: + try: + with open(filename, "w"): + pass + except: + assert_true(False, "Failed to create file for test") + + assert_true(exists(filename)) + func(filename) + assert_false(exists(filename), "test with '" + name + "' failed") + + +fn create_file_and_test_delete_path[ + func: fn[pathlike: PathLike] (pathlike) raises -> None, + name: StringLiteral, +](filepath: Path) raises: + try: + with open(filepath.__fspath__(), "w"): + pass + except: + assert_true(False, "Failed to create file for test") + + assert_true(exists(filepath)) + func(filepath) + assert_false(exists(filepath), "test with '" + name + "' failed") + + +fn test_remove() raises: + var cwd_path = Path() + var my_file_path = cwd_path / "my_file.test" + var my_file_name = str(my_file_path) + + # verify that the test file does not exist before starting the test + assert_false( + exists(my_file_name), + "Unexpected file " + my_file_name + " it should not exist", + ) + + # tyring to delete non existing file + with assert_raises(contains="Can not remove file: "): + remove(my_file_name) + with assert_raises(contains="Can not remove file: "): + remove(my_file_path) + + create_file_and_test_delete_string[remove, "remove"](my_file_name) + create_file_and_test_delete_string[unlink, "unlink"](my_file_name) + create_file_and_test_delete_path[remove, "remove"](my_file_path) + create_file_and_test_delete_path[unlink, "unlink"](my_file_path) + + # test with relative path + my_file_name = Path("my_relative_file.test") + create_file_and_test_delete_string[remove, "remove"](my_file_name) + + +def main(): + test_remove() From 5f00566e8effcc9d3a3ae700433732fc65b6976f Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:31:57 -0500 Subject: [PATCH 0282/2019] [External][stdlib] Implement `Dict.__str__()` (#2359) (#38372) Same logic as `List.__str__()` --------- Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 6af6934018b54b19d890e387f593506909f36115 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: d75252bb20c06aab4a2d725aff3b86c322bf7438 --- stdlib/src/builtin/value.mojo | 12 +++++ stdlib/src/collections/dict.mojo | 64 ++++++++++++++++++++++++++ stdlib/src/collections/list.mojo | 1 + stdlib/test/collections/test_dict.mojo | 28 +++++++++++ 4 files changed, 105 insertions(+) diff --git a/stdlib/src/builtin/value.mojo b/stdlib/src/builtin/value.mojo index 6bf5f561f0..a1e7067733 100644 --- a/stdlib/src/builtin/value.mojo +++ b/stdlib/src/builtin/value.mojo @@ -109,3 +109,15 @@ trait CollectionElement(Copyable, Movable): """ pass + + +trait StringableCollectionElement(CollectionElement, Stringable): + """The StringableCollectionElement trait denotes a trait composition + of the `CollectionElement` and `Stringable` traits. + + This is useful to have as a named entity since Mojo does not + currently support anonymous trait compositions to constrain + on `CollectionElement & Stringable` in the parameter. + """ + + pass diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 8b52d0a433..81bd6a6554 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -32,6 +32,7 @@ value types must always be Movable so we can resize the dictionary as it grows. See the `Dict` docs for more details. """ from memory import UnsafePointer +from builtin.value import StringableCollectionElement from .optional import Optional @@ -45,6 +46,13 @@ trait KeyElement(CollectionElement, Hashable, EqualityComparable): pass +trait StringableKeyElement(KeyElement, Stringable): + """A trait composition for types which implement all requirements of + dictionary keys and Stringable.""" + + pass + + @value struct _DictEntryIter[ K: KeyElement, @@ -499,6 +507,62 @@ struct Dict[K: KeyElement, V: CollectionElement]( """ return len(self).__bool__() + @staticmethod + fn __str__[ + T: StringableKeyElement, U: StringableCollectionElement + ](self: Dict[T, U]) -> String: + """Returns a string representation of a `Dict`. + + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + var my_dict = Dict[Int, Float64]() + my_dict[1] = 1.1 + my_dict[2] = 2.2 + dict_as_string = __type_of(my_dict).__str__(my_dict) + print(dict_as_string) + # prints "{1: 1.1, 2: 2.2}" + ``` + + When the compiler supports conditional methods, then a simple `str(my_dict)` will + be enough. + + Args: + self: The Dict to represent as a string. + + Parameters: + T: The type of the keys in the Dict. Must implement the + traits `Stringable` and `KeyElement`. + U: The type of the values in the Dict. Must implement the + traits `Stringable` and `CollectionElement`. + + Returns: + A string representation of the Dict. + """ + var minimum_capacity = self._minimum_size_of_string_representation() + var result = String(List[Int8](capacity=minimum_capacity)) + result += "{" + + var i = 0 + for key_value in self.items(): + result += str(key_value[].key) + ": " + str(key_value[].value) + if i < len(self) - 1: + result += ", " + i += 1 + result += "}" + return result + + fn _minimum_size_of_string_representation(self) -> Int: + # we do a rough estimation of the minimum number of chars that we'll see + # in the string representation, we assume that str(key) and str(value) + # will be both at least one char. + return ( + 2 # '{' and '}' + + len(self) * 6 # str(key), str(value) ": " and ", " + - 2 # remove the last ", " + ) + fn find(self, key: K) -> Optional[V]: """Find a value in the dictionary by key. diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 0b56377962..747e044859 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -20,6 +20,7 @@ from collections import List """ +from builtin.value import StringableCollectionElement from memory import UnsafePointer, Reference from memory.unsafe_pointer import move_pointee, move_from_pointee diff --git a/stdlib/test/collections/test_dict.mojo b/stdlib/test/collections/test_dict.mojo index ea9c57e781..22f3825799 100644 --- a/stdlib/test/collections/test_dict.mojo +++ b/stdlib/test/collections/test_dict.mojo @@ -62,6 +62,32 @@ def test_big_dict(): assert_equal(2000, len(dict)) +def test_dict_string_representation_string_int(): + var some_dict = Dict[String, Int]() + some_dict["a"] = 1 + some_dict["b"] = 2 + dict_as_string = __type_of(some_dict).__str__(some_dict) + assert_true( + some_dict._minimum_size_of_string_representation() + <= len(dict_as_string) + ) + assert_equal(dict_as_string, "{a: 1, b: 2}") + + +def test_dict_string_representation_int_int(): + var some_dict = Dict[Int, Int]() + some_dict[3] = 1 + some_dict[4] = 2 + some_dict[5] = 3 + some_dict[6] = 4 + dict_as_string = __type_of(some_dict).__str__(some_dict) + # one char per key and value, we should have the minimum size of string possible + assert_equal( + some_dict._minimum_size_of_string_representation(), len(dict_as_string) + ) + assert_equal(dict_as_string, "{3: 1, 4: 2, 5: 3, 6: 4}") + + def test_compact(): var dict = Dict[String, Int]() for i in range(20): @@ -335,5 +361,7 @@ def test_owned_kwargs_dict(): def main(): test_dict() + test_dict_string_representation_string_int() + test_dict_string_representation_int_int() test_owned_kwargs_dict() test_bool_conversion() From a7dc73dd8425fc78d2991fcce3e90259076584a0 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:32:04 -0500 Subject: [PATCH 0283/2019] =?UTF-8?q?[External][stdlib]=20Use=20`UInt8`=20?= =?UTF-8?q?instead=20of=20`Int8`=20to=20store=20`Error`'s=20message=20(#2?= =?UTF-8?q?=E2=80=A6=20(#38373)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …318) Related to https://github.com/modularml/mojo/issues/2317 I think we can do this safely by making PRs for one struct at a time. Here is the change for the `Error` struct. I believe that as we progress on the migration, the `bitcast` will go away. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 67d406a09a77c38bd464022d9300a0bdb4437d9e Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 260cec3712af85d41d0904925f6c064facec452a --- stdlib/src/builtin/error.mojo | 22 +++++++++++++--------- stdlib/src/builtin/file.mojo | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/stdlib/src/builtin/error.mojo b/stdlib/src/builtin/error.mojo index aa89870ac5..89dbac4645 100644 --- a/stdlib/src/builtin/error.mojo +++ b/stdlib/src/builtin/error.mojo @@ -29,7 +29,9 @@ from memory import memcmp, memcpy, DTypePointer struct Error(Stringable, Boolable): """This type represents an Error.""" - var data: DTypePointer[DType.int8] + alias StorageType = DTypePointer[DType.uint8] + + var data: Self.StorageType """A pointer to the beginning of the string data being referenced.""" var loaded_length: Int @@ -47,7 +49,7 @@ struct Error(Stringable, Boolable): Returns: The constructed Error object. """ - return Error {data: DTypePointer[DType.int8](), loaded_length: 0} + return Error {data: Self.StorageType(), loaded_length: 0} @always_inline("nodebug") fn __init__(value: StringLiteral) -> Error: @@ -59,7 +61,9 @@ struct Error(Stringable, Boolable): Returns: The constructed Error object. """ - return Error {data: value.data(), loaded_length: len(value)} + return Error { + data: value.data().bitcast[DType.uint8](), loaded_length: len(value) + } @always_inline("nodebug") fn __init__(src: String) -> Error: @@ -72,8 +76,8 @@ struct Error(Stringable, Boolable): The constructed Error object. """ var length = len(src) - var dest = UnsafePointer[Int8].alloc(length + 1) - memcpy(dest, src._as_ptr(), length) + var dest = Self.StorageType.alloc(length + 1) + memcpy(dest, src._as_ptr().bitcast[DType.uint8](), length) dest[length] = 0 return Error {data: dest, loaded_length: -length} @@ -88,8 +92,8 @@ struct Error(Stringable, Boolable): The constructed Error object. """ var length = len(src) - var dest = DTypePointer[DType.int8].alloc(length + 1) - memcpy(dest, src.data, length) + var dest = Self.StorageType.alloc(length + 1) + memcpy(dest, src.data.bitcast[DType.uint8](), length) dest[length] = 0 return Error {data: dest, loaded_length: -length} @@ -107,7 +111,7 @@ struct Error(Stringable, Boolable): """ if existing.loaded_length < 0: var length = -existing.loaded_length - var dest = UnsafePointer[Int8].alloc(length + 1) + var dest = Self.StorageType.alloc(length + 1) memcpy(dest, existing.data, length) dest[length] = 0 return Error {data: dest, loaded_length: existing.loaded_length} @@ -153,4 +157,4 @@ struct Error(Stringable, Boolable): var length = self.loaded_length if length < 0: length = -length - return String(StringRef(self.data, length)) + return String(StringRef(self.data.bitcast[DType.int8](), length)) diff --git a/stdlib/src/builtin/file.mojo b/stdlib/src/builtin/file.mojo index 983aed3675..131dc48556 100644 --- a/stdlib/src/builtin/file.mojo +++ b/stdlib/src/builtin/file.mojo @@ -54,7 +54,7 @@ struct _OwnedStringRef(Boolable): # Don't free self.data in our dtor. self.data = DTypePointer[DType.int8]() var length = self.length - return Error {data: data, loaded_length: -length} + return Error {data: data.bitcast[DType.uint8](), loaded_length: -length} fn __bool__(self) -> Bool: return self.length != 0 From 5803af7c61ed0fb053d64b64eae17932908dccac Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:32:26 -0500 Subject: [PATCH 0284/2019] =?UTF-8?q?[External][stdlib]=20Replace=20`Point?= =?UTF-8?q?er`=20by=20`UnsafePointer`=20in=20`stdlib/src/builtin/=E2=80=A6?= =?UTF-8?q?=20(#38375)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …object.mojo` (#2365) Builtins imports behave in a weird way, I had to import LegacyPointer in `stdlib/src/python/_cpython.mojo`, I have no explanation for this. I just import what the compiler asks me to import :p See https://discord.com/channels/1087530497313357884/1224434323193594059/1231287603462930452 Signed-off-by: gabrieldemarmiesse mojo-orig-commit: f75add0c85f97ed58170277f497037458326021e Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: c7136f6cc820bcb6e7bed41c1e149bc509f1ccaf --- stdlib/src/builtin/builtin_list.mojo | 2 +- stdlib/src/builtin/object.mojo | 16 +++++++--------- stdlib/src/python/_cpython.mojo | 2 +- stdlib/src/utils/inlined_string.mojo | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/stdlib/src/builtin/builtin_list.mojo b/stdlib/src/builtin/builtin_list.mojo index 7dafa99531..16ef431d3b 100644 --- a/stdlib/src/builtin/builtin_list.mojo +++ b/stdlib/src/builtin/builtin_list.mojo @@ -15,7 +15,7 @@ These are Mojo built-ins, so you don't need to import them. """ -from memory import Reference, UnsafePointer +from memory import Reference, UnsafePointer, LegacyPointer from memory.unsafe_pointer import destroy_pointee # ===----------------------------------------------------------------------===# diff --git a/stdlib/src/builtin/object.mojo b/stdlib/src/builtin/object.mojo index d9e664051a..f5a15cbde9 100644 --- a/stdlib/src/builtin/object.mojo +++ b/stdlib/src/builtin/object.mojo @@ -20,8 +20,9 @@ from collections import Dict, List from os import Atomic from sys.intrinsics import _type_is_eq -from memory import memcmp, memcpy, DTypePointer, LegacyPointer, UnsafePointer +from memory import memcmp, memcpy, DTypePointer from memory._arc import Arc +from memory.unsafe_pointer import move_from_pointee from utils import StringRef, unroll @@ -56,16 +57,12 @@ struct _ImmutableString: pointer and integer pair. Memory will be dynamically allocated. """ - var data: LegacyPointer[Int8] + var data: UnsafePointer[Int8] """The pointer to the beginning of the string contents. It is not null-terminated.""" var length: Int """The length of the string.""" - @always_inline - fn __init__(data: LegacyPointer[Int8], length: Int) -> Self: - return Self {data: data.address, length: length} - @always_inline fn __init__(data: UnsafePointer[Int8], length: Int) -> Self: return Self {data: data.address, length: length} @@ -1268,7 +1265,7 @@ struct object(IntableRaising, Boolable, Stringable): UnsafePointer[Int8].alloc(length), length ) memcpy(impl.data, lhsStr.data, lhsStr.length) - memcpy(impl.data.offset(lhsStr.length), rhsStr.data, rhsStr.length) + memcpy(impl.data + lhsStr.length, rhsStr.data, rhsStr.length) var result = object() result._value = impl return result @@ -1741,8 +1738,9 @@ struct object(IntableRaising, Boolable, Stringable): var index = Self._convert_index_to_int(i) if self._value.is_str(): var impl = _ImmutableString(UnsafePointer[Int8].alloc(1), 1) - impl.data.store( - self._value.get_as_string().data.offset(index).load() + initialize_pointee_copy( + impl.data, + move_from_pointee(self._value.get_as_string().data + index), ) return _ObjectImpl(impl) return self._value.get_list_element(i._value.get_as_int().value) diff --git a/stdlib/src/python/_cpython.mojo b/stdlib/src/python/_cpython.mojo index a214dfd9a4..d2a2b065f9 100644 --- a/stdlib/src/python/_cpython.mojo +++ b/stdlib/src/python/_cpython.mojo @@ -15,7 +15,7 @@ from os import getenv from sys import external_call from sys.ffi import DLHandle -from memory import DTypePointer +from memory import DTypePointer, LegacyPointer from utils import StringRef, StaticIntTuple diff --git a/stdlib/src/utils/inlined_string.mojo b/stdlib/src/utils/inlined_string.mojo index bcf2c946b7..0b3303a69d 100644 --- a/stdlib/src/utils/inlined_string.mojo +++ b/stdlib/src/utils/inlined_string.mojo @@ -17,7 +17,7 @@ from sys import sizeof -from memory import memcpy +from memory import memcpy, LegacyPointer from collections import Optional From e8fc228e3b77ae1262043ad5fff90bff07d6f70c Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:32:32 -0500 Subject: [PATCH 0285/2019] [External][stdlib] Replaced `Pointer` -> `UnsafePointer` in `python.mojo` (#2367) (#38376) I added an overload of `_get_global` to work with `UnsafePointer`, I didn't remove the first overload to allow us to migrate progressively towards UnsafePointer. `_get_global` is used in quite a few other places, we'll do it step by step. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 781db9e3851df26f87e9461e402ecace98fa1f91 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: b1e284407bc179495f7b0f34ecbf6d722c6a6736 --- stdlib/src/python/python.mojo | 12 ++++++------ stdlib/src/sys/ffi.mojo | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/stdlib/src/python/python.mojo b/stdlib/src/python/python.mojo index 2160ea79be..e634a4ee43 100644 --- a/stdlib/src/python/python.mojo +++ b/stdlib/src/python/python.mojo @@ -22,7 +22,7 @@ from python import Python from sys import external_call, sizeof from sys.ffi import _get_global -from memory import Pointer +from memory import UnsafePointer from utils import StringRef @@ -30,13 +30,13 @@ from ._cpython import CPython, Py_eval_input from .object import PythonObject -fn _init_global(ignored: Pointer[NoneType]) -> Pointer[NoneType]: - var ptr = Pointer[CPython].alloc(1) +fn _init_global(ignored: UnsafePointer[NoneType]) -> UnsafePointer[NoneType]: + var ptr = UnsafePointer[CPython].alloc(1) ptr[] = CPython() return ptr.bitcast[NoneType]() -fn _destroy_global(python: Pointer[NoneType]): +fn _destroy_global(python: UnsafePointer[NoneType]): var p = python.bitcast[CPython]() CPython.destroy(p[]) python.free() @@ -49,9 +49,9 @@ fn _get_global_python_itf() -> _PythonInterfaceImpl: struct _PythonInterfaceImpl: - var _cpython: Pointer[CPython] + var _cpython: UnsafePointer[CPython] - fn __init__(inout self, cpython: Pointer[CPython]): + fn __init__(inout self, cpython: UnsafePointer[CPython]): self._cpython = cpython fn __copyinit__(inout self, existing: Self): diff --git a/stdlib/src/sys/ffi.mojo b/stdlib/src/sys/ffi.mojo index 14c4a3cc0a..d8729fff36 100644 --- a/stdlib/src/sys/ffi.mojo +++ b/stdlib/src/sys/ffi.mojo @@ -176,6 +176,19 @@ fn _get_global[ ](StringRef(name), payload, init_fn, destroy_fn) +@always_inline +fn _get_global[ + name: StringLiteral, + init_fn: fn (UnsafePointer[NoneType]) -> UnsafePointer[NoneType], + destroy_fn: fn (UnsafePointer[NoneType]) -> None, +]( + payload: UnsafePointer[NoneType] = UnsafePointer[NoneType]() +) -> UnsafePointer[NoneType]: + return external_call[ + "KGEN_CompilerRT_GetGlobalOrCreate", UnsafePointer[NoneType] + ](StringRef(name), payload, init_fn, destroy_fn) + + @always_inline fn _get_global_or_null[name: StringLiteral]() -> Pointer[NoneType]: return external_call["KGEN_CompilerRT_GetGlobalOrNull", Pointer[NoneType]]( From 4d406b8f2806f73e79b1e65b34d904cf5ca9b9c3 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:32:42 -0500 Subject: [PATCH 0286/2019] [External][stdlib] Use `UnsafePointer` instead of `Pointer` in `io.mojo` (#2368) (#38377) Apologies for the small PRs, I'm heading into uncharted territory, I like to have frequent save points Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 6924c1ee5dfea70282aa463f07f826c45ff12385 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: e1fe83a3a6d3858d3da9a47b34d53ba7e0ba839f --- stdlib/src/builtin/io.mojo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/io.mojo b/stdlib/src/builtin/io.mojo index 61920e2b14..0210a7100c 100644 --- a/stdlib/src/builtin/io.mojo +++ b/stdlib/src/builtin/io.mojo @@ -19,7 +19,7 @@ from sys import bitwidthof, os_is_windows, triple_is_nvidia_cuda, external_call from builtin.dtype import _get_dtype_printf_format from builtin.builtin_list import _LITRefPackHelper -from memory import Pointer +from memory import UnsafePointer from utils import StringRef, unroll from utils._format import Formattable, Formatter, write_to @@ -53,7 +53,7 @@ fn _dup(fd: Int32) -> Int32: struct _fdopen: alias STDOUT = 1 alias STDERR = 2 - var handle: Pointer[NoneType] + var handle: UnsafePointer[NoneType] fn __init__(inout self, stream_id: Int): """Creates a file handle to the stdout/stderr stream. @@ -62,15 +62,15 @@ struct _fdopen: stream_id: The stream id (either `STDOUT` or `STDERR`) """ alias mode = "a" - var handle: Pointer[NoneType] + var handle: UnsafePointer[NoneType] @parameter if os_is_windows(): - handle = external_call["_fdopen", Pointer[NoneType]]( + handle = external_call["_fdopen", UnsafePointer[NoneType]]( _dup(stream_id), mode.data() ) else: - handle = external_call["fdopen", Pointer[NoneType]]( + handle = external_call["fdopen", UnsafePointer[NoneType]]( _dup(stream_id), mode.data() ) self.handle = handle From c8a68747057faa72b3ac55ed742e13ee68c12841 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:32:48 -0500 Subject: [PATCH 0287/2019] [External][stdlib] Use `UnsafePointer` in the `String` struct (#2370) (#38378) Three things going on here: * Added a String constructor from an `UnsafePointer`, this will help us phase out the `Pointer` constructor. Added corresponding test. * Renamed Pointer -> LegacyPointer, for better information and it conveys the state of the code, better searchability too. * Use DTypePointer instead of `LegacyPointer` when doing `memcpy`. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 9daf95c17469de79e3ed633b97af7a1b830d8d1e Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 92dccce1efd3f5f01c8a5059236a307750bbbbc7 --- stdlib/src/builtin/string.mojo | 27 ++++++++++++++++++++++----- stdlib/test/builtin/test_string.mojo | 9 +++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index c25de28a7f..17cadfa8ee 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -18,7 +18,7 @@ These are Mojo built-ins, so you don't need to import them. from collections import List, KeyElement from sys import llvm_intrinsic, bitwidthof -from memory import UnsafePointer, DTypePointer, Pointer, memcmp, memcpy +from memory import DTypePointer, LegacyPointer, UnsafePointer, memcmp, memcpy from utils import StringRef, StaticIntTuple, StaticTuple from utils._format import Formattable, Formatter, ToFormatter @@ -413,7 +413,22 @@ struct String( self = str(value) @always_inline - fn __init__(inout self, ptr: Pointer[Int8], len: Int): + fn __init__(inout self, ptr: UnsafePointer[Int8], len: Int): + """Creates a string from the buffer. Note that the string now owns + the buffer. + + The buffer must be terminated with a null byte. + + Args: + ptr: The pointer to the buffer. + len: The length of the buffer, including the null terminator. + """ + # we don't know the capacity of ptr, but we'll assume it's the same or + # larger than len + self = Self(Self._buffer_type(ptr, size=len, capacity=len)) + + @always_inline + fn __init__(inout self, ptr: LegacyPointer[Int8], len: Int): """Creates a string from the buffer. Note that the string now owns the buffer. @@ -631,11 +646,13 @@ struct String( var buffer = Self._buffer_type() buffer.resize(total_len + 1, 0) memcpy( - rebind[Pointer[Int8]](buffer.data), self._as_ptr().address, self_len + DTypePointer(buffer.data), + self._as_ptr(), + self_len, ) memcpy( - rebind[Pointer[Int8]](buffer.data + self_len), - other._as_ptr().address, + DTypePointer(buffer.data + self_len), + other._as_ptr(), other_len + 1, # Also copy the terminator ) return Self(buffer^) diff --git a/stdlib/test/builtin/test_string.mojo b/stdlib/test/builtin/test_string.mojo index d8294e4e20..c9042536a4 100644 --- a/stdlib/test/builtin/test_string.mojo +++ b/stdlib/test/builtin/test_string.mojo @@ -62,6 +62,15 @@ fn test_constructors() raises: assert_equal("abc", str(s2)) assert_equal(3, len(s2)) + # Construction from UnsafePointer + var ptr = UnsafePointer[Int8].alloc(4) + ptr[0] = ord("a") + ptr[1] = ord("b") + ptr[2] = ord("c") + ptr[3] = 0 + var s3 = String(ptr, 4) + assert_equal(s3, "abc") + fn test_copy() raises: var s0 = String("find") From 854b5fdffd3a46ea772c153ba4e4faaa11375477 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:32:55 -0500 Subject: [PATCH 0288/2019] [External][stdlib] Remove some usage of `LegacyPointer` in tests and StringRef (#2371) (#38379) We continue to remove the LegacyPointer. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: af191bde5eb26e6159732e66c0749190002fb96d Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: edbdc629d60e631b946765ce92744af0079eb3d3 --- stdlib/src/builtin/string.mojo | 2 +- stdlib/src/utils/stringref.mojo | 20 ++------------------ stdlib/test/memory/test_arc.mojo | 7 ++++--- stdlib/test/python/test_ownership.mojo | 1 - stdlib/test/python/test_python_object.mojo | 1 - stdlib/test/sys/test_windows_target.mojo | 1 - 6 files changed, 7 insertions(+), 25 deletions(-) diff --git a/stdlib/src/builtin/string.mojo b/stdlib/src/builtin/string.mojo index 17cadfa8ee..46a6e91a31 100644 --- a/stdlib/src/builtin/string.mojo +++ b/stdlib/src/builtin/string.mojo @@ -569,7 +569,7 @@ struct String( var adjusted_span = self._adjust_span(span) if adjusted_span.step == 1: return StringRef( - (self._buffer.data + span.start).address, + self._buffer.data + span.start, len(adjusted_span), ) diff --git a/stdlib/src/utils/stringref.mojo b/stdlib/src/utils/stringref.mojo index 1197badf6f..86cb86f944 100644 --- a/stdlib/src/utils/stringref.mojo +++ b/stdlib/src/utils/stringref.mojo @@ -16,7 +16,8 @@ from builtin.dtype import _uint_type_of_width from builtin.string import _atol -from memory import LegacyPointer, UnsafePointer, DTypePointer +from memory import DTypePointer, UnsafePointer + # ===----------------------------------------------------------------------=== # # Utilities @@ -90,23 +91,6 @@ struct StringRef( """ return self - @always_inline - fn __init__(ptr: LegacyPointer[Int8], len: Int) -> StringRef: - """Construct a StringRef value given a (potentially non-0 terminated - string). - - The constructor takes a raw pointer and a length. - - Args: - ptr: UnsafePointer to the string. - len: The length of the string. - - Returns: - Constructed `StringRef` object. - """ - - return Self {data: ptr, length: len} - @always_inline fn __init__(ptr: DTypePointer[DType.int8], len: Int) -> StringRef: """Construct a StringRef value given a (potentially non-0 terminated diff --git a/stdlib/test/memory/test_arc.mojo b/stdlib/test/memory/test_arc.mojo index 2e30349136..a3f0cbd9d1 100644 --- a/stdlib/test/memory/test_arc.mojo +++ b/stdlib/test/memory/test_arc.mojo @@ -15,6 +15,7 @@ from collections import List from memory._arc import Arc +from memory.unsafe_pointer import initialize_pointee_move from testing import assert_equal, assert_false, assert_true @@ -27,15 +28,15 @@ def test_basic(): @value struct ObservableDel(CollectionElement): - var target: Pointer[Bool] + var target: UnsafePointer[Bool] fn __del__(owned self): - self.target.store(True) + initialize_pointee_move(self.target, True) def test_deleter_not_called_until_no_references(): var deleted = False - var p = Arc(ObservableDel(Pointer.address_of(deleted))) + var p = Arc(ObservableDel(UnsafePointer.address_of(deleted))) var p2 = p _ = p^ assert_false(deleted) diff --git a/stdlib/test/python/test_ownership.mojo b/stdlib/test/python/test_ownership.mojo index 5f7f8febc2..83fcc3341e 100644 --- a/stdlib/test/python/test_ownership.mojo +++ b/stdlib/test/python/test_ownership.mojo @@ -15,7 +15,6 @@ from sys import env_get_string -from memory import Pointer from python._cpython import CPython, PyObjectPtr from python import PythonObject, Python diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 862e72f439..24e5b0a1e0 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -13,7 +13,6 @@ # XFAIL: asan && !system-darwin # RUN: %mojo-no-debug %s -from memory import Pointer from python._cpython import CPython, PyObjectPtr from python import PythonObject, Python from testing import assert_false, assert_raises, assert_true, assert_equal diff --git a/stdlib/test/sys/test_windows_target.mojo b/stdlib/test/sys/test_windows_target.mojo index 555652b14f..492fb36a5e 100644 --- a/stdlib/test/sys/test_windows_target.mojo +++ b/stdlib/test/sys/test_windows_target.mojo @@ -25,7 +25,6 @@ from os._windows import ( ) from sys import external_call, os_is_linux, os_is_macos, os_is_windows -from memory import Pointer from testing import assert_false, assert_true, assert_equal From f0986e60d461a871d452ef1930e48d36d86fff9b Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:33:01 -0500 Subject: [PATCH 0289/2019] [External][Doc] Fix contribution and vision doc issues (#2382) (#38380) Signed-off-by: Ehsan M. Kermani <6980212+ehsanmok@users.noreply.github.com> Co-authored-by: Arthur Evans mojo-orig-commit: b176fe703bd3af023f1a6cb297ed75040ee81757 Co-authored-by: Ehsan M. Kermani <6980212+ehsanmok@users.noreply.github.com> MODULAR_ORIG_COMMIT_REV_ID: 642cf6fef3de76204559b6b63c761421bd42dd62 --- CONTRIBUTING.md | 5 +++-- stdlib/docs/vision.md | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7608951c78..72c71b2eee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Also, before opening a new issue, take a moment to search through the already submitted issues to avoid creating duplicate issues for the maintainers to address. -### Writing high-quality bugs +### Writing high-quality bug descriptions We encourage you to provide as much information about the issue as practical. The more details you provide, the faster we can resolve the issue. The following @@ -157,7 +157,8 @@ experience with the process. You can use a pull request to propose a change or bug fix to the Mojo Standard Library, Mojo examples, or Mojo documentation. This page gives an overview of -the process, especially for first-time contributors. +the process. For a more detailed walkthrough, see +[How to contribute to the Mojo standard library: a step-by-step guide](https://www.modular.com/blog/how-to-contribute-to-mojo-standard-library-a-step-by-step-guide). **Note:** Pull requests should be submitted against the `nightly` branch, which represents the most recent nightly build. diff --git a/stdlib/docs/vision.md b/stdlib/docs/vision.md index b335e239af..647cce80ba 100644 --- a/stdlib/docs/vision.md +++ b/stdlib/docs/vision.md @@ -18,8 +18,7 @@ encouraged to engage with the standard library and language evolution. We intend to ignite enthusiasm to contribute to the expanding Mojo package ecosystem. -- The standard library prioritizes - **Performance `>` Safety `>` Portability `>` Debuggability.** +- The standard library prioritizes **Performance, Safety, Portability, and Debuggability.** - **Respectable performance by default.** The standard library should provide respectable performance by default, but not perfect performance. We support From a07751fc11585745917037477e16c618f6e01e1f Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 11:41:02 -0500 Subject: [PATCH 0290/2019] [External][stdlib] Test `assert_raises()` without FileCheck (#2350) (#38374) Related to #2024 Signed-off-by: gabrieldemarmiesse mojo-orig-commit: 5690d780cacda703b2dc3c5ef77f70d4f58ce3cc MODULAR_ORIG_COMMIT_REV_ID: bf75fa4eab11628cfa2c48ed2211b31656362574 --- ...ses_basic.mojo => test_assert_raises.mojo} | 38 ++++++++++++------- .../testing/test_assert_raises_no_error.mojo | 31 --------------- .../testing/test_assert_raises_no_match.mojo | 31 --------------- 3 files changed, 24 insertions(+), 76 deletions(-) rename stdlib/test/testing/{test_assert_raises_basic.mojo => test_assert_raises.mojo} (60%) delete mode 100644 stdlib/test/testing/test_assert_raises_no_error.mojo delete mode 100644 stdlib/test/testing/test_assert_raises_no_match.mojo diff --git a/stdlib/test/testing/test_assert_raises_basic.mojo b/stdlib/test/testing/test_assert_raises.mojo similarity index 60% rename from stdlib/test/testing/test_assert_raises_basic.mojo rename to stdlib/test/testing/test_assert_raises.mojo index 8e00966b4c..0d555bd8c6 100644 --- a/stdlib/test/testing/test_assert_raises_basic.mojo +++ b/stdlib/test/testing/test_assert_raises.mojo @@ -10,39 +10,49 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s -from testing import assert_raises +from testing import assert_raises, assert_equal -# CHECK-LABEL: test_assert_raises_catches_error fn test_assert_raises_catches_error() raises: - print("== test_assert_raises_catches_error") with assert_raises(): raise "SomeError" - # CHECK: 🔥 - print("🔥") + # The assert_raises should catch the error and not propagate it. + # Hence the test will succeed. -# CHECK-LABEL: test_assert_raises_catches_matched_error fn test_assert_raises_catches_matched_error() raises: - print("== test_assert_raises_catches_matched_error") with assert_raises(contains="Some"): raise "SomeError" - # CHECK: 🔥 - print("🔥") with assert_raises(contains="Error"): raise "SomeError" - # CHECK: 🔥 - print("🔥") with assert_raises(contains="eE"): raise "SomeError" - # CHECK: 🔥 - print("🔥") + + +fn test_assert_raises_no_error() raises: + try: + with assert_raises(): + pass + raise Error("This should not be reachable.") + except e: + assert_equal(str(e), "AssertionError: Didn't raise") + + +fn test_assert_raises_no_match() raises: + try: + with assert_raises(contains="Some"): + raise "OtherError" + raise Error("This should not be reachable.") + except e: + assert_equal(str(e), "OtherError") def main(): test_assert_raises_catches_error() test_assert_raises_catches_matched_error() + test_assert_raises_no_error() + test_assert_raises_no_match() diff --git a/stdlib/test/testing/test_assert_raises_no_error.mojo b/stdlib/test/testing/test_assert_raises_no_error.mojo deleted file mode 100644 index 95d0190e06..0000000000 --- a/stdlib/test/testing/test_assert_raises_no_error.mojo +++ /dev/null @@ -1,31 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# REQUIRES: has_not -# RUN: not %mojo %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL - -from testing import assert_raises - - -# CHECK-FAIL-LABEL: test_assert_raises_no_error -fn test_assert_raises_no_error() raises: - print("== test_assert_raises_no_error") - # CHECK-FAIL-NOT: is never reached - # CHECK: AssertionError - with assert_raises(): - pass - # CHECK-FAIL-NOT: is never reached - print("is never reached") - - -def main(): - test_assert_raises_no_error() diff --git a/stdlib/test/testing/test_assert_raises_no_match.mojo b/stdlib/test/testing/test_assert_raises_no_match.mojo deleted file mode 100644 index 897ae3772d..0000000000 --- a/stdlib/test/testing/test_assert_raises_no_match.mojo +++ /dev/null @@ -1,31 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Copyright (c) 2024, Modular Inc. All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions: -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ===----------------------------------------------------------------------=== # -# REQUIRES: has_not -# RUN: not %mojo %s 2>&1 | FileCheck %s -check-prefix=CHECK-FAIL - -from testing import assert_raises - - -# CHECK-FAIL-LABEL: test_assert_raises_no_match -fn test_assert_raises_no_match() raises: - print("== test_assert_raises_no_match") - # CHECK-FAIL-NOT: is never reached - # CHECK: AssertionError - with assert_raises(contains="Some"): - raise "OtherError" - # CHECK-FAIL-NOT: is never reached - print("is never reached") - - -def main(): - test_assert_raises_no_match() From 5bc28f405b56cab10e9d88ba2c568f969d0094fd Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 12:18:11 -0500 Subject: [PATCH 0291/2019] [External][stdlib] Implement `List.__str__()` (#2323) (#38369) PR that can serve as a reference for https://github.com/modularml/mojo/pull/2190#issuecomment-2062101102 Note that it causes a bug that seems on the parser side. We get this: ``` RUN: at line 13: mojo /projects/open_source/mojo/stdlib/test/builtin/test_str.mojo + mojo /projects/open_source/mojo/stdlib/test/builtin/test_str.mojo /projects/open_source/mojo/stdlib/test/builtin/test_str.mojo:18:5: error: cannot bind MLIR type 'None' to trait 'StringableCollectionElement' def test_str_none(): ^ /projects/open_source/mojo/stdlib/src/builtin/str.mojo:62:8: note: MLIR type cannot satisfy required trait function here fn __str__(self) -> String: ^ mojo: error: failed to parse the provided Mojo source module ``` So basically, making an overload to allow `str(List[...])` makes `str(List[Int](1, 2, 3))` return `"[1, 2, 3]"`, but breaks `str(None)`. I think the parser doesn't pick the right overload. See https://github.com/modularml/mojo/issues/2336. Signed-off-by: gabrieldemarmiesse mojo-orig-commit: facbb0ae77991f88fbc21ab8a0522fd6d22f13c2 Co-authored-by: Gabriel de Marmiesse MODULAR_ORIG_COMMIT_REV_ID: 0efbc133827035926c75ee7763f562230670dbaa --- stdlib/src/collections/list.mojo | 41 ++++++++++++++++++++++++++ stdlib/test/collections/test_list.mojo | 14 +++++++++ 2 files changed, 55 insertions(+) diff --git a/stdlib/src/collections/list.mojo b/stdlib/src/collections/list.mojo index 747e044859..f610997366 100644 --- a/stdlib/src/collections/list.mojo +++ b/stdlib/src/collections/list.mojo @@ -563,3 +563,44 @@ struct List[T: CollectionElement](CollectionElement, Sized, Boolable): """ var ref = Reference(self) return _ListIter[T, mutability, self_life, False](len(ref[]), ref) + + @staticmethod + fn __str__[U: StringableCollectionElement](self: List[U]) -> String: + """Returns a string representation of a `List`. + + Note that since we can't condition methods on a trait yet, + the way to call this method is a bit special. Here is an example below: + + ```mojo + var my_list = List[Int](1, 2, 3) + print(__type_of(my_list).__str__(my_list)) + ``` + + When the compiler supports conditional methods, then a simple `str(my_list)` will + be enough. + + Args: + self: The list to represent as a string. + + Parameters: + U: The type of the elements in the list. Must implement the + traits `Stringable` and `CollectionElement`. + + Returns: + A string representation of the list. + """ + # we do a rough estimation of the number of chars that we'll see + # in the final string, we assume that str(x) will be at least one char. + var minimum_capacity = ( + 2 # '[' and ']' + + len(self) * 3 # str(x) and ", " + - 2 # remove the last ", " + ) + var result = String(List[Int8](capacity=minimum_capacity)) + result += "[" + for i in range(len(self)): + result += str(self[i]) + if i < len(self) - 1: + result += ", " + result += "]" + return result diff --git a/stdlib/test/collections/test_list.mojo b/stdlib/test/collections/test_list.mojo index adfa329823..867b1f137a 100644 --- a/stdlib/test/collections/test_list.mojo +++ b/stdlib/test/collections/test_list.mojo @@ -576,6 +576,19 @@ def test_constructor_from_other_list_through_pointer(): assert_equal(some_list.capacity, capacity) +def test_converting_list_to_string(): + var my_list = List[Int](1, 2, 3) + assert_equal(__type_of(my_list).__str__(my_list), "[1, 2, 3]") + + var my_list2 = List[SIMD[DType.int8, 2]]( + SIMD[DType.int8, 2](1, 2), SIMD[DType.int8, 2](3, 4) + ) + assert_equal(__type_of(my_list2).__str__(my_list2), "[[1, 2], [3, 4]]") + + var my_list3 = List[Float64](1.0, 2.0, 3.0) + assert_equal(__type_of(my_list3).__str__(my_list3), "[1.0, 2.0, 3.0]") + + def main(): test_mojo_issue_698() test_list() @@ -598,3 +611,4 @@ def main(): test_list_boolable() test_constructor_from_pointer() test_constructor_from_other_list_through_pointer() + test_converting_list_to_string() From 0fc8f184337ac7ceabb3a4c7df2dfec1d4269933 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 12:56:37 -0500 Subject: [PATCH 0292/2019] [External][stdlib] Support reverse for Dict keys (#2327) (#38400) Part of #2325 The behavior is align with Python, only keys is iterated > reversed(d) Return a reverse iterator over the keys of the dictionary. This is a shortcut for reversed(d.keys()). I plan to support `d.values()` and `d.items()` in follow up PR too! --------- Signed-off-by: jayzhan211 mojo-orig-commit: abf2975d48fc7631e0e2ca741340289ec622f5d0 Co-authored-by: Jay Zhan MODULAR_ORIG_COMMIT_REV_ID: e88248d3e1023b62d9ffd36ae5c6e0455b07490d --- stdlib/src/builtin/reversed.mojo | 29 +++++++++++++ stdlib/src/collections/dict.mojo | 52 +++++++++++++++++++++-- stdlib/test/builtin/test_reversed.mojo | 57 ++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/stdlib/src/builtin/reversed.mojo b/stdlib/src/builtin/reversed.mojo index 117af59eef..ab39416960 100644 --- a/stdlib/src/builtin/reversed.mojo +++ b/stdlib/src/builtin/reversed.mojo @@ -19,6 +19,8 @@ from .range import _StridedRangeIterator from collections.list import _ListIter +from collections.dict import _DictKeyIter + # ===----------------------------------------------------------------------=== # # Reversible # ===----------------------------------------------------------------------=== # @@ -99,3 +101,30 @@ fn reversed[ The reversed iterator of the list. """ return Reference(value)[].__reversed__[mutability, self_life]() + + +fn reversed[ + mutability: __mlir_type.`i1`, + self_life: AnyLifetime[mutability].type, + K: KeyElement, + V: CollectionElement, +]( + value: Reference[Dict[K, V], mutability, self_life]._mlir_type, +) -> _DictKeyIter[K, V, mutability, self_life, False]: + """Get a reversed iterator of the input dict. + + **Note**: iterators are currently non-raising. + + Parameters: + mutability: Whether the reference to the dict is mutable. + self_life: The lifetime of the dict. + K: The type of the keys in the dict. + V: The type of the values in the dict. + + Args: + value: The dict to get the reversed iterator of. + + Returns: + The reversed iterator of the dict. + """ + return Reference(value)[].__reversed__[mutability, self_life]() diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index 81bd6a6554..a9b948040d 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -59,6 +59,7 @@ struct _DictEntryIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, + forward: Bool = True, ]: """Iterator over immutable DictEntry references. @@ -67,6 +68,7 @@ struct _DictEntryIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the dictionary is mutable. dict_lifetime: The lifetime of the List + forward: The iteration direction. `False` is backwards. """ alias imm_dict_lifetime = __mlir_attr[ @@ -86,18 +88,37 @@ struct _DictEntryIter[ @always_inline fn __next__(inout self) -> Self.ref_type: while True: - debug_assert(self.index < self.src[]._reserved, "dict iter bounds") + + @parameter + if forward: + debug_assert( + self.index < self.src[]._reserved, "dict iter bounds" + ) + else: + debug_assert(self.index >= 0, "dict iter bounds") + if self.src[]._entries.__get_ref(self.index)[]: var opt_entry_ref = self.src[]._entries.__get_ref[ __mlir_attr.`0: i1`, Self.imm_dict_lifetime, ](self.index) - self.index += 1 + + @parameter + if forward: + self.index += 1 + else: + self.index -= 1 + self.seen += 1 # Super unsafe, but otherwise we have to do a bunch of super # unsafe reference lifetime casting. return opt_entry_ref.unsafe_bitcast[DictEntry[K, V]]() - self.index += 1 + + @parameter + if forward: + self.index += 1 + else: + self.index -= 1 fn __len__(self) -> Int: return len(self.src[]) - self.seen @@ -109,6 +130,7 @@ struct _DictKeyIter[ V: CollectionElement, dict_mutability: __mlir_type.`i1`, dict_lifetime: AnyLifetime[dict_mutability].type, + forward: Bool = True, ]: """Iterator over immutable Dict key references. @@ -117,6 +139,7 @@ struct _DictKeyIter[ V: The value type of the elements in the dictionary. dict_mutability: Whether the reference to the vector is mutable. dict_lifetime: The lifetime of the List + forward: The iteration direction. `False` is backwards. """ alias imm_dict_lifetime = __mlir_attr[ @@ -124,7 +147,9 @@ struct _DictKeyIter[ ] alias ref_type = Reference[K, __mlir_attr.`0: i1`, Self.imm_dict_lifetime] - alias dict_entry_iter = _DictEntryIter[K, V, dict_mutability, dict_lifetime] + alias dict_entry_iter = _DictEntryIter[ + K, V, dict_mutability, dict_lifetime, forward + ] var iter: Self.dict_entry_iter @@ -820,6 +845,25 @@ struct Dict[K: KeyElement, V: CollectionElement]( self._n_entries = self.size + fn __reversed__[ + mutability: __mlir_type.`i1`, self_life: AnyLifetime[mutability].type + ]( + self: Reference[Self, mutability, self_life]._mlir_type, + ) -> _DictKeyIter[ + K, V, mutability, self_life, False + ]: + """Iterate backwards over the dict keys, returning immutable references. + + Returns: + A reversed iterator of immutable references to the dict keys. + """ + var ref = Reference(self) + return _DictKeyIter( + _DictEntryIter[K, V, mutability, self_life, False]( + ref[]._reserved, 0, ref + ) + ) + struct OwnedKwargsDict[V: CollectionElement](Sized, CollectionElement): """Container used to pass owned variadic keyword arguments to functions. diff --git a/stdlib/test/builtin/test_reversed.mojo b/stdlib/test/builtin/test_reversed.mojo index 35c4a5ad66..a5bb3fffb1 100644 --- a/stdlib/test/builtin/test_reversed.mojo +++ b/stdlib/test/builtin/test_reversed.mojo @@ -24,5 +24,62 @@ def test_reversed_list(): check -= 1 +def test_reversed_dict(): + var dict = Dict[String, Int]() + dict["a"] = 1 + dict["b"] = 2 + dict["c"] = 3 + dict["d"] = 4 + dict["a"] = 5 + + var keys = String("") + for key in reversed(dict): + keys += key[] + + assert_equal(keys, "dcba") + + # Order preserved + + _ = dict.pop("a") + _ = dict.pop("c") + + keys = String("") + for key in dict: + keys += key[] + + assert_equal(keys, "bd") + + keys = String("") + for key in reversed(dict): + keys += key[] + + assert_equal(keys, "db") + + # Empty dict is iterable + + _ = dict.pop("b") + _ = dict.pop("d") + + keys = String("") + for key in reversed(dict): + keys += key[] + + assert_equal(keys, "") + + # Refill dict + + dict["d"] = 4 + dict["a"] = 1 + dict["b"] = 2 + dict["e"] = 3 + + keys = String("") + for key in reversed(dict): + keys += key[] + + assert_equal(keys, "ebad") + + def main(): + test_reversed_dict() test_reversed_list() From a80d5a877772cb4bfaae364e9bceacbb60852d52 Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Tue, 23 Apr 2024 13:24:13 -0500 Subject: [PATCH 0293/2019] [SDLC] Cleanup small differences from merge conflicts (#38416) Manual merges between `mojo` and `modular` have led to drift. This re-syncs file contents in preparation for moving to Copybara. MODULAR_ORIG_COMMIT_REV_ID: e789968e028ccd5a91f7a7686983b9f67071fc28 --- stdlib/test/builtin/test_bool.mojo | 7 +++++++ stdlib/test/memory/test_bitcast.mojo | 2 +- stdlib/test/python/test_python_info.mojo | 2 +- stdlib/test/python/test_python_object.mojo | 2 +- stdlib/test/python/test_python_object_len_raises.mojo | 2 +- stdlib/test/python/test_python_to_mojo.mojo | 2 +- stdlib/test/sys/test_build_info_debug.mojo | 2 +- stdlib/test/sys/test_has_intel_amx.mojo | 3 +-- stdlib/test/sys/test_macos_target.mojo | 2 -- stdlib/test/testing/test_assertion.mojo | 8 ++++++++ 10 files changed, 22 insertions(+), 10 deletions(-) diff --git a/stdlib/test/builtin/test_bool.mojo b/stdlib/test/builtin/test_bool.mojo index e9b3895467..9b343c4e88 100644 --- a/stdlib/test/builtin/test_bool.mojo +++ b/stdlib/test/builtin/test_bool.mojo @@ -23,5 +23,12 @@ def test_bool_cast_to_int(): assert_equal(int(True), 1) +def test_bool_none(): + var test = None + assert_equal(bool(None), False) + assert_equal(bool(test), False) + + def main(): test_bool_cast_to_int() + test_bool_none() diff --git a/stdlib/test/memory/test_bitcast.mojo b/stdlib/test/memory/test_bitcast.mojo index 7d85915f99..5375cc485a 100644 --- a/stdlib/test/memory/test_bitcast.mojo +++ b/stdlib/test/memory/test_bitcast.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from memory import bitcast from testing import assert_equal diff --git a/stdlib/test/python/test_python_info.mojo b/stdlib/test/python/test_python_info.mojo index 613c699897..8d182f0fca 100644 --- a/stdlib/test/python/test_python_info.mojo +++ b/stdlib/test/python/test_python_info.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from python._cpython import PythonVersion diff --git a/stdlib/test/python/test_python_object.mojo b/stdlib/test/python/test_python_object.mojo index 24e5b0a1e0..1c760bdb92 100644 --- a/stdlib/test/python/test_python_object.mojo +++ b/stdlib/test/python/test_python_object.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from python._cpython import CPython, PyObjectPtr from python import PythonObject, Python diff --git a/stdlib/test/python/test_python_object_len_raises.mojo b/stdlib/test/python/test_python_object_len_raises.mojo index efab24476e..3b2356ebd6 100644 --- a/stdlib/test/python/test_python_object_len_raises.mojo +++ b/stdlib/test/python/test_python_object_len_raises.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from python import PythonObject, Python from testing import assert_raises diff --git a/stdlib/test/python/test_python_to_mojo.mojo b/stdlib/test/python/test_python_to_mojo.mojo index 166995952f..97d0210435 100644 --- a/stdlib/test/python/test_python_to_mojo.mojo +++ b/stdlib/test/python/test_python_to_mojo.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # XFAIL: asan && !system-darwin -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from python import Python, PythonObject diff --git a/stdlib/test/sys/test_build_info_debug.mojo b/stdlib/test/sys/test_build_info_debug.mojo index 5ce1e50b73..27ef38de21 100644 --- a/stdlib/test/sys/test_build_info_debug.mojo +++ b/stdlib/test/sys/test_build_info_debug.mojo @@ -11,7 +11,7 @@ # limitations under the License. # ===----------------------------------------------------------------------=== # # REQUIRES: is_debug -# RUN: %mojo %s | FileCheck %s +# RUN: %mojo %s from sys._build import is_debug_build, is_release_build from testing import assert_true, assert_false diff --git a/stdlib/test/sys/test_has_intel_amx.mojo b/stdlib/test/sys/test_has_intel_amx.mojo index 2012f5f372..248067007b 100644 --- a/stdlib/test/sys/test_has_intel_amx.mojo +++ b/stdlib/test/sys/test_has_intel_amx.mojo @@ -16,8 +16,7 @@ # ===----------------------------------------------------------------------=== # # REQUIRES: linux # REQUIRES: amx_tile - -# RUN: %mojo-no-debug -debug-level full %s +# RUN: %mojo %s from sys import has_intel_amx, os_is_linux from testing import assert_false, assert_true diff --git a/stdlib/test/sys/test_macos_target.mojo b/stdlib/test/sys/test_macos_target.mojo index 5d7d53667e..931b8997be 100644 --- a/stdlib/test/sys/test_macos_target.mojo +++ b/stdlib/test/sys/test_macos_target.mojo @@ -28,8 +28,6 @@ from sys import ( from sys.info import _macos_version from testing import assert_true, assert_false -from testing import assert_true - fn test_os_query() raises: assert_true(os_is_macos()) diff --git a/stdlib/test/testing/test_assertion.mojo b/stdlib/test/testing/test_assertion.mojo index cb7cc69ff7..9251bb1d40 100644 --- a/stdlib/test/testing/test_assertion.mojo +++ b/stdlib/test/testing/test_assertion.mojo @@ -43,6 +43,14 @@ def test_assert_not_equal_is_generic(): assert_not_equal(DummyStruct(1), DummyStruct(1)) +def test_assert_equal_with_simd(): + assert_equal(SIMD[DType.uint8, 2](1, 1), SIMD[DType.uint8, 2](1, 1)) + + with assert_raises(): + assert_equal(SIMD[DType.uint8, 2](1, 1), SIMD[DType.uint8, 2](1, 2)) + + def main(): test_assert_equal_is_generic() test_assert_not_equal_is_generic() + test_assert_equal_with_simd() From b5fad9e7c3043538a2b68bdd27d6d52df9b5db12 Mon Sep 17 00:00:00 2001 From: Austin Doolittle Date: Tue, 23 Apr 2024 17:18:14 -0400 Subject: [PATCH 0294/2019] [GPU] Force scalar loads for 1-byte types (#38430) 1-byte types are vectorized during lowering from LLVM IR to PTX regardless of the alignment that is set, this differs from the behavior of other wider types where invalid alignments are caught and corrected during lowering. We're going to solve this by performing scalar loads for 1-bytewidth values on GPU until we can know if a pointer is aligned or not (#37823), at which point we can be smarter about when to perform vectorized loads MODULAR_ORIG_COMMIT_REV_ID: ec19f627b9b1383de2bcb61da9b4c2e1c3dc0d66 --- stdlib/src/memory/unsafe.mojo | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/stdlib/src/memory/unsafe.mojo b/stdlib/src/memory/unsafe.mojo index 92e728d758..3788ba39bc 100644 --- a/stdlib/src/memory/unsafe.mojo +++ b/stdlib/src/memory/unsafe.mojo @@ -897,6 +897,22 @@ struct DTypePointer[ The loaded value. """ + @parameter + if triple_is_nvidia_cuda() and sizeof[type]() == 1 and alignment == 1: + # LLVM lowering to PTX incorrectly vectorizes loads for 1-byte types + # regardless of the alignment that is passed. This causes issues if + # this method is called on an unaligned pointer. + # TODO #37823 We can make this smarter when we add an `aligned` + # trait to the pointer class. + var v = SIMD[type, width]() + + # intentionally don't unroll, otherwise the compiler vectorizes + for i in range(width): + v[i] = self.address.offset(int(offset) + i).load[ + alignment=alignment + ]() + return v + return ( self.address.offset(offset) .bitcast[SIMD[type, width]]() From 17051cb0fb40a57a9a006cd60e5c91a5606603f7 Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Tue, 23 Apr 2024 17:27:35 -0500 Subject: [PATCH 0295/2019] [stdlib] docs: Update changelog with open-source stdlib contributions (#38448) MODULAR_ORIG_COMMIT_REV_ID: 23d30dcda455ceaa7aec287bfdb0e67cbee451e9 --- docs/changelog.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9ff6c1118a..dfa3a48e68 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -68,7 +68,7 @@ what we publish. ``` - `List` now has several new methods: - - `pop(index)` for removing an element at a particular index. + - `pop(index)` for removing an element at a particular index.\ By default, `List.pop()` removes the last element in the list. - `resize(new_size)` for resizing the list without the need to specify an additional value. @@ -158,18 +158,45 @@ what we publish. return ... ``` +- Added `reversed()` for creating reversed iterators. Several range types, + `List`, and `Dict` now support iterating in reverse. + ([PR #2215](https://github.com/modularml/mojo/pull/2215), + [PR #2327](https://github.com/modularml/mojo/pull/2327)) + +- Added left and right shift operations for `object` + ([PR #2247](https://github.com/modularml/mojo/pull/2247)) + +- Added checked arithmetic operations. + ([PR #2138](https://github.com/modularml/mojo/pull/2138)) + + SIMD integral types (including the sized integral scalars like `Int64`) can + now perform checked additions, substractions, and multiplications using the + following new methods: + + - `SIMD.add_with_overflow` + - `SIMD.sub_with_overflow` + - `SIMD.mul_with_overflow` + + Checked arithimetic allows the caller to determine if an operation exceeded + the numeric limits of the type. + +- Added `os.remove()` and `os.unlink()` for deleting files. + ([PR #2310](https://github.com/modularml/mojo/pull/2310)) + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has changed slightly: `mojo build ./test-dir/program.mojo` now outputs an executable to the path `./program`, whereas before it would output to the path `./test-dir/program`. + - The REPL no longer allows type level variable declarations to be uninitialized, e.g. it will reject `var s: String`. This is because it does not do proper lifetime tracking (yet!) across cells, and so such code would lead to a crash. You can work around this by initializing to a dummy value and overwriting later. This limitation only applies to top level variables, variables in functions work as they always have. + - `AnyPointer` got renamed to `UnsafePointer` and is now Mojo's preferred unsafe pointer type. It has several enhancements, including: 1) The element type can now be `AnyType`: it doesn't require `Movable`. @@ -180,6 +207,7 @@ what we publish. 4) `UnsafePointer` can be initialized directly from a `Reference` with `UnsafePointer(someRef)` and can convert to an immortal reference with `yourPointer[]`. Both infer element type and address space. + - All of the pointers got a pass of cleanup to make them more consistent, for example the `unsafe.bitcast` global function is now a consistent `bitcast` method on the pointers, which can convert element type and address space. @@ -226,6 +254,32 @@ what we publish. print(ptr.load[width=8]()) ``` +- `Optional.value()` will now return a reference instead of a copy of the + contained value. + ([PR #2226](https://github.com/modularml/mojo/pull/2226)) + + To perform a copy manually, dereference the result: + + ```mojo + var result = Optional(123) + + var value = result.value()[] + ``` + +- Per the accepted community proposal + [`proposals/byte-as-uint8.md`](https://github.com/modularml/mojo/blob/main/proposals/byte-as-uint8.md), + began transition to using `UInt8` by changing the data pointer of `Error` + to `DTypePointer[DType.uint8]`. + ([PR #2318](https://github.com/modularml/mojo/pull/2318)) + +- Continued transition to `UnsafePointer` away from the legacy `Pointer` type + in various standard library APIs and internals. + ([PR #2365](https://github.com/modularml/mojo/pull/2365), + [PR #2367](https://github.com/modularml/mojo/pull/2367), + [PR #2368](https://github.com/modularml/mojo/pull/2368), + [PR #2370](https://github.com/modularml/mojo/pull/2370), + [PR #2371](https://github.com/modularml/mojo/pull/2371)) + ### ❌ Removed - Support for "register only" variadic packs has been removed. Instead of @@ -298,3 +352,12 @@ what we publish. - [#1675](https://github.com/modularml/mojo/issues/1675) Ensure `@value` decorator fails gracefully after duplicate field error. + +- [#2068](https://github.com/modularml/mojo/issues/2068) + Fix simd.reduce for size_out == 2 + ([PR #2102](https://github.com/modularml/mojo/pull/2102)) + +- [#2224](https://github.com/modularml/mojo/issues/2224) + `object` now implements `__truediv__`, `__floordiv__` and related divison + and modulo operators. + ([PR #2230](https://github.com/modularml/mojo/pull/2230)) From 96e3b0737070fa6e5baea68dd765fb194d9dbd3d Mon Sep 17 00:00:00 2001 From: Patrick Dougherty Date: Wed, 24 Apr 2024 08:07:19 -0500 Subject: [PATCH 0296/2019] [SDLC] Mojo OSS Copybara baseline (#38479) This change fixes a stdlib test to use `mojo` instead of the `mojo-no-debug` alias. It also serves as the first Copybara commit to be copied to the `mojo` repo. In the `mojo` repo, it will also contain any deltas between the current state of the monorepo and `mojo`. MODULAR_ORIG_COMMIT_REV_ID: 7656ee47e77d77f18fece65567e54fe8ab1c5d0e --- stdlib/test/os/test_remove.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/test/os/test_remove.mojo b/stdlib/test/os/test_remove.mojo index bb9a9821cd..b316092f99 100644 --- a/stdlib/test/os/test_remove.mojo +++ b/stdlib/test/os/test_remove.mojo @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ===----------------------------------------------------------------------=== # -# RUN: %mojo-no-debug %s +# RUN: %mojo %s from os import remove, unlink from os.path import exists From d50935677f9c3105bd952acb8d4258fcc6adb455 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 24 Apr 2024 13:35:21 -0400 Subject: [PATCH 0297/2019] [mojo-lang][KGEN] Allow specifying mlir properties and add `kgen.source_loc` op (#38415) The op will be used later to implement better source/call location retrieval. This patch merely introduces the op, and enables the parser to set properties on inline mlir ops. MODULAR_ORIG_COMMIT_REV_ID: e7af09c17d7bfc6b213dfa2546d6c3d0025ed07d --- docs/changelog.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index dfa3a48e68..607ffbe82d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -70,11 +70,12 @@ what we publish. - `List` now has several new methods: - `pop(index)` for removing an element at a particular index.\ By default, `List.pop()` removes the last element in the list. - - `resize(new_size)` for resizing the list without the need to + +- `resize(new_size)` for resizing the list without the need to specify an additional value. - - `insert(index, value)` for inserting a value at a specified index +- `insert(index, value)` for inserting a value at a specified index into the `List`. - - constructor from `(ptr, size, capacity)` to to avoid needing to do a deep +- constructor from `(ptr, size, capacity)` to to avoid needing to do a deep copy of an existing contiguous memory allocation when constructing a new `List`. - `Dict` now has a `update()` method to update keys/values from another `Dict`. @@ -183,6 +184,20 @@ what we publish. - Added `os.remove()` and `os.unlink()` for deleting files. ([PR #2310](https://github.com/modularml/mojo/pull/2310)) +- Properties can now be specified on inline mlir ops: + + ```mojo + _ = __mlir_op.`kgen.source_loc`[ + _type = ( + __mlir_type.index, __mlir_type.index, __mlir_type.`!kgen.string` + ), + _properties = __mlir_attr.`{inlineCount = 1 : i64}`, + ]() + ``` + + As the example shows above, the protected `_properties` attribute can be + passed during op construction, with an MLIR `DictionaryAttr` value. + ### 🦋 Changed - The behavior of `mojo build` when invoked without an output `-o` argument has From 4d06eb49bd1b1a2bcb84a4eb26cc87e0648d2321 Mon Sep 17 00:00:00 2001 From: Laszlo Kindrat Date: Wed, 24 Apr 2024 13:42:51 -0400 Subject: [PATCH 0298/2019] [mojo-stdlib] Fix a subtle bug in `Dict.__reversed__` (#38510) The reverse iterator would start at the wrong index, which could cause non-deterministic behavior (including segfaults or just incorrect behavior) since the iterator could point to potentially out of bounds or junk memory. This would cause the unit tests to flake. Credit goes to @bethebunny for finding the bug. MODULAR_ORIG_COMMIT_REV_ID: 6f979296a5fc01f51c36824174133fd321064a8c --- stdlib/src/collections/dict.mojo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/src/collections/dict.mojo b/stdlib/src/collections/dict.mojo index a9b948040d..dcbfd0bc34 100644 --- a/stdlib/src/collections/dict.mojo +++ b/stdlib/src/collections/dict.mojo @@ -860,7 +860,7 @@ struct Dict[K: KeyElement, V: CollectionElement]( var ref = Reference(self) return _DictKeyIter( _DictEntryIter[K, V, mutability, self_life, False]( - ref[]._reserved, 0, ref + ref[]._reserved - 1, 0, ref ) ) From 3f42fa7648f9cdc6dcc6ae9e58e10641af616e5a Mon Sep 17 00:00:00 2001 From: Connor Gray Date: Wed, 24 Apr 2024 13:31:42 -0500 Subject: [PATCH 0299/2019] [Docs] Add quick links header to the Mojo README.md (#38454) This adds a "quick links header" to the Mojo README.md, similar to what other OSS project README's often have. MODULAR_ORIG_COMMIT_REV_ID: 77159b2dfdc5ddf277a3fd8b1e60c0ffaebbe3c3 --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6738ac4d40..97f655a891 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ -

l{f2&Ty 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 k25lr`2YX_ diff --git a/stdlib/docs/images/nightly-extension.png b/stdlib/docs/images/nightly-extension.png deleted file mode 100644 index 368ce6cfa048a348760a09443dd192da2992735f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116383 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

l{f2&Ty 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 k25lr`2YX_ literal 0 HcmV?d00001 diff --git a/docs/oss-material/images/nightly-extension.png b/docs/oss-material/images/nightly-extension.png new file mode 100644 index 0000000000000000000000000000000000000000..368ce6cfa048a348760a09443dd192da2992735f GIT binary patch literal 116383 zcmeFXWmsIxwl<1eumB0}5G299aT@pFPH@+5+@0V~&_E!#yGw8VyaXBw0SXip6q=Nzs1g(u92g1;x&#RUQq!V&rVIsz zDr5l!DoO!?WQvY zN{E&m!9R>xQ^lU4K%*nLER5D!=qNNptnwEA9lVL|r@2Hfx~#*ti`Gjo(5cTW&*gWv z-BAWu-v_BOjr2%Ms2Iw@aVa^X@oLs8H4wPs=uqU<23%tZ!u9Z%nv# z{fdpbRsC)Uy*3QzFzcbRe)MijPe5ns`tjxXCZlthdtvf^y%Tv0b$}Y`<>=^mkC`uI z4eQm7Idqj$D7csVD#zv|89yXP`YWulUyfSn8_npB{Hx&5c{>A*HUO^+g|ik_N;1NF zw8FN&+Of-618qa^v!y6iw-%-k=Ee34W=;aCg=EVwQjV?dN`nz!#%_5}TOqveU`)iX zRg}LHJuOq2Lt$BT$oJR{1ZdC=`9pD0E?|rj1=6S*Sq3%Cy;1{5lrDA~vD|btOF||} zT$7Yuoq~OHruxi-@EyKlDwEaGVtx=6k`0(+Vfx4^yU&)#96-#nM;Y7Oe^KwtEl!4V zqrPVZZxNS;_tVEher3(GQVw54J`n44Y%KK6=P5+1b%*WyuEsCwa1?4!Sy5zmI(KV2 zcmBRYJxi6gezXE=g_pwVr=&S};mY0fpQU}4WMhawZH|yR4}1(&jq1U^Soc@i+qBqt zD&Q8&bT#FhG@oC@ z8R_rFit{gR^A!URul$1j-ewvx$m*;UouR1L!U~3DEWUUms1yqu2n`q~K_Oi^q}HR{ z*tdA;eJ(z~ltp;g->xHM2iTicRY+baCm9JNPGKm`P+a$Et*I$azPs&f%?7CSIyu0u z`8IcOe_Fy7cMkfbol;*yAMw5Bl{0|!BzPkFNK|~FtKZu=?YqRpk$D-AzDny=gH)`n z5O{5has5Q}{TXzmOfZ-85oqXGNn3r&T1$vfT;UBKeQubxh5;^DEo z@$gQ8uc2J7W+sWlpdzOOhld|~L%6c>tvS%V>U_EsvSa;b6>GS{9c!VkQIvS2@}rDU z{cTKPv$JXD_b#x%ZJtL*USXk5v0-6hCE7sQG*EN9=%lY*t2~4sp1=h#_Akr|^mK~$ z4M#%0cF0b5x%;q2U(l`BJk$LM&tSaCFa!N1F%jcBsJ?k2=8}g7?<$m~F;>G|l1%JQQ7Is*+WdWyU zpjI~J=MRG-UE|zfiqt51;Wxhx!N^SrrQ$Bx*L!qVSnjaLLi$JY|lm+bM%)RS5HOM^aH#mjjanG6UKJa$7@Nj2C8MA9_+ZOhsPdOD6h3^GlNphZEaeoIE2Dx6J#dh8@abCWAn4%T0pb)> z@vUVHM+y5JfllN+K_Wp2hpokx)dYuxg}CL8IpfSyp__2Ec6hGI&vykqv-C3yKb*7< zS%`DeCg;HjZn?-Y+0;1j<{GkQ8=*(OE~~G zQ&uHbM`r03EfaSQ4$;WV-F>3wRTEo*1E65^fS!KS5`!A%2U#>ZpzP1QG`U4t!8FaZ zp0sx<$1EOSSDi$qd^m$a42 zUcrygayfdui#(U3wo?fO^ubeEA8>j1insU6k9A z&Cba6%F*(A*O}D${MO{U)xpqp>5bQx*-78+;?8Y^$|#%SG}X>4neOjq#Y_ zchn9Te^_bQ1(;fxP1sp@0aVsE(ilZ(2XCCwGT(Z#24Z5puj1Q!-xu1oIc5JKvJ5l} zstYU(>Ip7=WOJW%t3u1$<6|xZ(@D0 z7%uph!m?|)Um0)Y(%Tk(8Lds=!nE>1k6<-DE0dW#`@_1Ip^%}QVP8Y+rfFnzf^iI$ ztflM+Y8om#nG;e{Ztv;4GUPIB=?_ck=KKx|-EZPiKbR;0I5oXkT`v4^M5GM|M-!Nq zByBnB1ry`75*PZ6F4Zpe&Ytf+-t)uQ2{*23rn+dkX}L7HJcM0kqOL}SMd*=hH^|@7 zvr&PJb{R=fBQ-II1`*IiiQxh-&^w(%D?HXgoIbaQNC_6tuGaGxk_If`$BR2>5Q)6(;YL1!GWJe>ZItTR{o-#KKrw#f#SLy+Zlcf{s^0# z*=X84PdJCwrM0p1!O;%S4k&CFJc5?lY(!$BH`B5jo*p(BzA~zizN%ua!dHKH&eYjH zl{79*MP2{im0_*{Q=e96uFm}GM{Rgp;)M)T5>KLPf|us7w&(fm=k~IDr!P_qadkg! zWw<_3GGa3{H!iu{M>&pM{JQA-*jrYhKcJ!6ba7lXP|T>(pkl2fr^%qZT=~IMZc{6~ zc4)?1Gr96a{HM=$CVf|tnCj{5<*felT79-{&hOr;$nTuUT+1s&EsbppMNNip9Ar9^ z^0l$+G9M%FwMRGuY)ls0Y;kQ)=iXN5{OoPbGtFNg{aI^XT2p5>8>p-9jCf>Q0xDQ+ zk#JF%*Q-!(y1j2p@Z!1+Ikpq%;8S!NzxXmu{*XQ4p6l)Ir4JMbN*E~_J+6;;jmYWA zg+y_a%zIB=nQyaX7|BE{N8v)a;j`>cxrS+nG@s^2`I`)Nmp3QgZCtkk>)!jYk%;dw&`8x@ z4IcY4(d(m(GN3a`qYX)yJ%&A=Hl$YKnRI&;3Kc$PrtlT-JD&(FU{8Uz@9%bXnYx(- zZj+u0@6D!V(vlHhFCL3)?R4~KKMoZ8&4#b;wa>rIY}O0(#@ys=a>m=Ac*siYyF8C>>EqBAUc9z=Pwy zP03WW0b>s5nuCMp-4mhm5sy#*<1SlFVUzFou~!(WTu)1Arzj_gOA%(GA!RBj2SpDl zBSFDI6F|X3O3;ub08RMsvN$v?6zpI1Fi=n-7Eo~iY9kNH|2#2}^rz3?Ic!`o6e8pw z79_c5!Te8aIB*v1|CFIiAn%}rm4Q-HkX+fw(Zs|SWNruk9Z;zYsX(@u)C566y{G<@ zprw?k&LHE@TBvA%HRNOgMs_xg2F7-VCXB8&_J78K;&TN+iZ&)-12R_|Yg-V&m7n6T z763^3Pc;(-*s7dA#aM>8fC9v&VhW>zLvRt88529TRA*ua&+7DV~?ApbLts0ql((ZU{V zVP{MBXIukAJ0~zd1;w9<{{8&DP7_y)e`c};{p+(JAIS9Q4HF9^Gt=|SZ0d`hyzQ5Z4A8-Dd@jrTM{L_<#g`N37yZ*6d|MY`oQM)vq3QuedL-ts{@lH#Vs$8S?|14x`Ypy~u>jy}cji@R)fJkrRM2mO-~( z(?xmH2Pni;PT`=Vp;G(9nbLCUX;uAOm@&^H7`_*g0Nh#r>mYws#)u-(X2*-t$b=OA_o*PQpx!EqNR-3= z-{w(-i$jHmtmY_i-~Ru;nmIpLnIbl^d>mg^hQ2Sd9Jw&NpICPTu&^rTvSi9@)RX&%TK{kDB$_b( zyZFmEM5~yMlg|><|2GYX$_AG(j&DlCj*s3)1=g69q2aBbJ52#H5qAOm5HT`l$S^Y8 z&pq~P2t#HkCnr6=nP#9$lp3l0H$|ujw-Mrt1vu|g@^JhrWIJj;02~-0Hkz8(q;A!| z2^Idm_PwmkIflCysj-NypJ;~4?a4qBY2@G{#Pr&B-EnL|_P>7#9tlKuvhj@?DG`fh z3UB!i4=@1nDS758si~<)^!Z*=QvoZSn1Jz7gOQ1q-&6wnZ$dNP@1NBYO!D(DOF10@ zH2%9vCHpfjHU>^@bSP#&S7>@=-1iWFi(tKNay)|d7w>~tvSPiwBWdKN)XA1OrFzS|{&SPQ`2Hb- zFZ9gGBW28Yb#w}|5}XQz!uVu1aaL9oDmF?|r?$_)azf)#O$&||kkFCuxJ8er%praZ zFKvOgBE($1S@mQ5{P&D_?~$HL920bWTtl9Y6T-JsVEcN^dbiQTb0!1+t?41WK*k9n z?pHLyWdI7?ljtaSz&Kq$p;@4wiOD;1FSp9rU+I%{nJVor@^%gHTAd`me!>QEu<7Dw0`%NZ z?pmDzS%s7H`y65A^?8a_-I18J8y%ldzDD_A~J7@Jf7l(XQ{E?T5wC4fI>QaakY=8br+wZ@SPAv zV+*q!Gq$O?#|EJss z%7NAsnAj-J{gryY(BvzyMGuBPwUUY(s~*jYHcP5)5Z)> zZR_?so8;2>vGd!1+b-&wjqtZtFVJ2uot)4*#^@B`exF}h45Y{p=Lkcg`JSAdLQ(_S zLR46|GI~PF_OeHfi}G~pIA!_?m5+{FA1%xe5>~hMKeYq&Xs$EJj;FCfcUBuZ6>B1r3uoaq=1{BeIhOL_{8GJ&?RsFur56uA01s>Y8*<{Jnr;OQ!EF}F0Pge?HDU?Rw_S7$r_g%7WLqTcpU;tM)EII~ofsps9# z4SZf@I3|GjjfneZO?~JTaq_4#&Gyg~%SG34Vb#8I&~v3-f|RIl|T0q9SY{7xQv8MN9{hv6-2avzi)Av|>meQo10L>*7L8Xg-4lx(0Ga zE{|%LSym+`;03b;K$g2~Wjgh;N9G)qyLlZS+= z4%Zv~ono_>QnaVj*|SYEHT8g4tEa80#JWW`ov*8o(!JjL`KT-R+%bjEBuS^%TyxH- zsI)MNOO5ZHa|UAss-Fyf-_WMh*?1u>Ha;2#aSVK%h#iJ!onom+TUH1N4?oS) z{6fKKvA?)*>{N{}QB#Hp7g6C3#M6nLouu3|W>pcI&t<8X=0v}-08jKX^)>-ho$NT# zup$F(@?q4Gt&~>O6_ZP z8N;pG&v6-f0yKcSPs&5}7C2p^>L(Rl!5wnKtI#>ZbR_oZ1CGFn<- z8V^tGwDi=kuQ{qVYH6?@*5kT6p5WCrGq1|Z3^+mIE(h1&*Z|Qk(h$4g&Bv~CTHj%N`gTl5juvSXuKuqF@*F9Mj4BxZu@5=cO_2)U`qo%DTFlv9?Fb6L5v< zzTu&Sj7;DG8g(P-Nm^ke-iK*D!l$^rzQJ2jJ8 zX%kQTF+OoG6sO%-U=%rV()hf#UW?>Hy}5pfhP1p^M=;?d`>-YL?^&;pFejLj!Dk<- z!Alu3Ht!_h3@O16=vzAPOUm@dG8ip5*`2bAVfl&Uco@zmEbQ!rB-H}mdiPkkA2DaR zVSrsFQ9dh6h!H*-7J~-DeYHhGB<|LOIUK*dmO=B*1*v~}*VNNoX-p12`c*o-IaToy z`|IA}fKlJzSlaDC#1Ad06qoG&JI`1;`wxOeF)>FHU_UuD$mC&kl5X_S$M zy~7b=r2m8$rh6y=5&C2za0i40*b9>i$Qeiq1)R~ues@>^q)sfpamuGb(y(cpy?xih zpRl^bGd9QLvwlfY`c{4Vlgjz=_!aKTS=DOUTXSQjH!*lfq9S`|GCb|yFam$(dpy@4NR&(zehqAtj z;=1dZL#2Eo&?I%iScI9PLZ_6@zzVyqSiXGwQCNW)&#T^IDC5JQ>ch*m^T~N-L zs-a{JkqMOOT3r3R#O;UicVFGpvo~u}^Gzo&n%fO(30CHhdsi{5=cvLyL#2=aEFm~k z%pH;Eqgk# z8}ab@Z3#=Pxo1I9Q4!(|s!|A{87gm|>;iR&d%a%SrRnu1AWT>DW6Hn9Laadb8lOi+ zpZ=BAy{^Uah^od#@`Wmop?O~bpWifndyTWiM;V=D&ZbcyR*l=O+)Z11D`?jzRuWi8 zEb{}47fi(h9N7wyR1fqijFmA zD)ln#a1_*F1ZkrAQoO3K`Vd(|#H8P0*K(d&u_EDlza!I-+F#%gb01%rIAQ;g`?QFR z$?%@jW`gSeHvaq2@RIYn@wSu#A*&3|Pbo_}rC-sEfzI+}lJH7jlF65f-mLJ*B5lH_ zRFjIzu_0xsxb7<+wIS#MoX{pF!Qy}--Yv91z2{jH*B7dSWb|@MEP&b!a`aEL%-~S_ zHLlSjlZ{XMj?2VKEF@Gqp`Q`Wr;E&f?v!v;;#LjBjL~Or3c?SNz36CYP`IpII}KR{ z&#r>TUWiEVu{J+?eEv+Hj^@Cm{6~2ON@NkXcV1s39m*>zi_i!w4BL~ac_L@nOa8VB z8H)4~I=aiPeV0-mail)%tBU5>_Z>d`0Z=*|8svo9tfUMV3=0#B1%M@wI>HghLfu#rq`H5=Q7Et0}PuNT!7S4ex5z#Z}#I*FyR_jnczQ3xTn zRB#V|>iNdkczeZ3mxAzl?dv3&Ys4A7`-btFfc$ByY|EXj$Urx1*F3^d=dmPqe|i8d zF|EbbgmA>Ph3h{1RKQ$eYyd;fRx|#)3o(Cz&~x|%rXOl`9=DwHPv?(n#n{d~PStt8 zfJ|&`+De!L>bhP^0*~O2lM6RoJ@{i^i`g$lshDH<(Klyc(3WL8pLtzO)Il7k&Ln@WR6KX}OB4Bu?^g zC9VH(k1PB6dxWnx^KI<4kBk2n&GgN1bgJgFXK zU-Yqr8oyaITr`bXZ<0a+MBSao<{;6{O|H|eTEx`%MV$cm&ILTrd=Y;JZ1236bnBs_ z!)Fn9tO8N9eMBUYrR&8zI2@DKwW%fIubRzHJ}agZ(giqLf7za|&Oe*%B#rlDJLr8U z+|Xiu1c%S^HKJX?)p0P%u#K=F)yhBT6|txECK0=7;E!26I0{O z2n*%!{Kh=YZSugFed!={*hdoS`upIE1wE?77O6x+Q&}G9m%s zYT`V(x;3SOpl5a-MIl4YxbuQw5=RSS(#4_qnImO1HC6vHh&CJxV#CPOcvVIEL@5TW zT#bjjW_R03(-?8LIr@K}c-|K)<5%tUpI0E>K#5d&+q=g{e7u$#gr5{ck@!J;<=C|5 z-*pQ$8U^nLq(H>zf))wU`$p$VYbTQPT)-=|H!hQpY@psJv`03iV3wos!f2EC)JCZa zXwk?Na(+g0*%UMP-wuU*T%^i&5*oP2moh6bigW{_=~-E;wmL)FjXZR%@2vwo8#k2~ zIRh#kX^}FH()m(&?a2Gn9nZGG4mF$tuika{UM&fvmDUMp8`~EjlkiuH)FLIZyqfHV8u>RJ_B9g5 z9YiE&zpEpm3S>_%r;UVBMb3W9Q)G2=(o$@(@fOKP!>^3E+98&-TUkMhCTYR+MTN+h zzi2^BV${+CP4N!MdHxp}8V{8`kDo7*sV|TwB8Nix?(z6WeFUZ{L&#N#ET8=E$MEyF z=}+GdWT;VPepT++CQGzaAYfJR1m@$J5QV0x;J6>SD#~II&yXzTtOr@R?xlAFy-|>e zF)ti{NV&_;N@1`nKYf+ftCMo&i=Ac(_pA?^Fr$g9@w)eBv>o9mttQXue>eceF&(#qFFTEEC z^`m;13noBrZ*8B{KqbRVTEgs&tpLXa3Tq#Z?1#$9L6hKJ^uy|2&fVMji5vxSxF6Ku z4eBVOLWquZ;6ozLD<5H^jf1!q2_iYBTk;@K{Cd;!(h}{692zodZydUA%EpFLL)R^d zgtwpZ=6YS}vQ@+?H#(6FKiCjUY-^G`8tQRlgYW*nMu%?5CaEs!)BW{6>V03pw88R8 z`8OWj7lIWYWi~4+n;kgU%?L!dfMjUWHj>MXEG!x(>~6FHqSSScd0sh|43efZ-zf{B zc?^*da&;Jye1gDH7EHj%@x|t^H`}`-7WS|%=%j0m2wgj)&9Xa84HM4lu=oUVXcLkC z!!PZzA_IQSEg0k$p0OLNg9Gs)h*Xyu$I!ebxwm2tQiV8cr)fUBM9t;ZU6`6>5O1}q ziS6!7*CDz0dxmVFSLx^R8N-n{cn0#eGxqhBE=Pjw_s8Q)VKeMQi5!makQOzouH+99 zG^#DP*^uCo%lA#14T#jysk0|1CTJ0FPZNrYjJzb@yU)s}a4O`$r4zJxgYHQh=q!BRKi&qR2f*W}&_6 z^^E4Q9Wn(rDL`)}Yx_fsI)WK#WJPFZKz0z%ww2q{KKWd`6GUi@+L`8a&H+6U7C2V) zuD!1w@3MYz6`;2(p!W<%3T&{T2LHD5xGZ)!wWWni3fzMcz=vzGZzZi==N(T;WBZ`+a93{{knK+JAIe#QkvwByC zu?@YErLEy)p;7MUZt#O-MWvfE!QRH1;pQ1XcHldrZJ=G?gh42f^8*ukyPt zZ~Y)$cQHn-$(jxybD88z8PU4(S?@~F*}dTHPXA2~%>!DbVgIX4eapY{v)t85YqPxpJaS?}$g{T{OoKj2Bu4+DH3#TZHU*1ch_}CLrW6 zx49DNt>PH_R7J;;jg;YW`bfDcV=AcVj(eul*1AJoJngq_G)TYIYjTUQ1Ej$_5i;y& z*V6~>>e6qXO&l68IX~7#lU+T-XCb)l{OT3P-2)KnW|_B8UStnBbOt3a=H+sRsA+rj z_A{$TV=7I4ko`s6?cmX9+hX+T-wsX?D6mSi_@(4y*Yp-m4XJLBZeu-bb0!eGy^&#mo9 z;9~FgmnkMYR~hs`g=5d;j`jD{1!@cwrp6S|nok>8$|NpCpkP zBh70lDa;PySp!Ayq63V1Tred2Y8#GrLSPR|CBKh*&y;$p5@eTSSzk?9+ZNTMKdKLCwA-A>z(aSf#7}77{?G86Dk+lsw*_C>mb_k4j zA1;APD5Zziy`r(v=NXxxp0&2f0iuA$Y>3Vuo#2^Kt zD<{-b))bX;wS-Lu=SRh!7ont7JZV-G6dL6UNjyRV4V2VPQfC$DCTU}1Y()|o=18J4 zduQC7-gikytMzOLKrPqpYgV)^Y4-PY*6ZvB+|{jTV`}U% zpPx~#Vz-%V&M(~Rh2U*L7f(MYUG_W`i4EysD?I}`VB%CrZ9HjyJO>}rB$?cwJ>8Aw zD&2b@NEOC<-V@rzwm9E$S5Wh;%;qIT$NTKvNi!bsFr>We+2~Z~AMii);+oJ-3>e3c zyxAHrOq-M`xvk=TiNeFOYBA2b>15j^yZJmVv&YUMeH-hJ2@ts7J;J|8o9RYwq}X@G zKqBS^6<>b-ihF2ftydC&oFT*5ijcbt)4Uw4D}l8$GCkG>%4a!xSPP2JA7(AvTr=sM zT$y?AwtlY<$5fRrFC!B#roI}1%Nn>YX3pEbM6p=s9lCnVh>t1~!emFq{p+Wd3&F$g zq9A7|Td}ik8E&x+Fu5;$9RD*w%eM7+>5bQ(H#cc10*=$q0wxo)#X8#rCV|)PssS{J zb5K=vo^*ID$u|_a(P9YUs`?OSggITu`3a#*m$w_D7;KYyvKbJtOy6*L;&G#t>-+Jh zpJAQ0>volZVK)xPi>LM$73rh4<1_!A7gbj@(js<<@lgIL;iTGY1d%Gb*LT9MG7?k~ znxiP-`-w;M-y4gtGF)DFd5RTov?KdV&BTP?$86hE1T8NssM3(;5r za^Hia`$!=QOaN-<#l2Qwwt9)?0&2E#pzd|Bp4(f;Y4p+zbbU5F5YCKwA1PD(@aOW3 z%SSEwshsGb@vmY;DS~Slf*1L9`fA6_;eq1@lJEGj@kTK*DDf|5(wLgid;WO!Ob?g1 zI`5*pm>=#O`^>7gtFIjnnc|{QE*!VpRfGid-OHbBydDJi0+LGkT6JE92 zRX^lUOCH=gP~<4`HByL?f$4?!vRGR{1!+d^JKwsa_OrWWslj73jUW``QG*1M7S|ef zNv^@qoAfEU-QtHygMdVZyb_Bql~F;P9ovtpBEbG+9Rc_~c|)WGv}<9;A=2 znegj4erWHv)4G_tMk!}f`_qJ7#F^@%IBWxJEON#^+wLuLv-@W4>W%Qmg9!#4*s-WM zYKSIn<)k{34Yk=oL#^Wc{(dA;!Ot8Xn$(XaFnr6GZmR{C1mI!K2i}XUy6SxlT4}Bc zcx)#Ee>Ua{;ex?$Ez|HrtNx~La=AUl7O6fqd`R^+&=#wudJA6JdPon5n zpobQ|ME7yzFkkF!U^Gs1Os08H>^>b30uq82$|2C6zJ2lr{B%G_iz`8w^h~)v15ti_ zPr!IK;xCSHJEwOq8|<0m*Jecu1I?!`{*PL&tkgALEj!^=tlOPZ@8S^skMbpob8E3h z!-Kr69whGr@-mc?khA+C&=yXVpC0-|qC&SD!`a;ck*!s_X$*kM8PySSXs;G{N)$g% zUI=<`JgP@zhl@na#HvGV6?fBwbD!ove<2Hs2KQuX8ag0-X3+CIwvjyNopxTN@p+|` zaPe|p3m%`ZcL^CzIebmEZB`CRY{^qAcb}4^y#8{)lv_If3w{G#OU^|}w)*!O4$Eqv$h;{m}C zrm5Z;!8M+jyVZ=M(Q+{6Wibx+_H4R`U9?B#3PgvGPG)*_Um8gN!mQSIUPE3((DDfk zAPF4(QEV3{6>k>oOk97j43RJwGcpc`V{#Jsl9~c}6;Pt``RO*+{>f-d){;$u8U%2e zuLj`er71T>6h~z$6P=%ujD{tHXJWNP#!&_ZJSi|2!J1{Vh9QTt5G?IhpY$Ogj}bZJ znG7s6`bN@iHP+O^ra$48$*!nnVu?C)MWr4!acI!511!VFc__$fGz)t-7J%V1tA-Io zfL-49#H&tePXX3(!V=E8%|a6!`S39uF&j9R{#&w`h>6#2Bec{dPWLV{z7CcDaTs^i zH6mFQ9y$Drj0(h`j&oUk4Cw?ObIBd8x;<`t50$>hdH;SW!^Nx1vbNImvhQ^0WO>ng zF-9^gDk_6G2i({v_iJA`#xSz2vhU7 z4__yzJe5+lsUU59s#R+>7|)qH~CmL1RMpRnP6d-tm5WRv)lqyjg~yon@3CDfs3>U2!f$c<0Lx*jbBasl6Hl zf&bFMZGbZROK4^QoQU0aVh z>Y}WiF`h%OH1b<=taW;BV;bX89ipC0{!&i`f#{G(oB!~{{^cx=wC%YXVRzJeDQu-N zX${y@!|vb|?CkE4vr)JuNT1-Tr#FS{JPr}Dp5|k7$O_{0QAbq4vJ630mDq+9;Jd6- zPH=9~K_K%~;IH(%Vw|Qdg;aJ}#tv-V42Jwqw2(cQ#UEi>bk@`u7QTSDo*iWvSLUHE zEgg|uP7RMx%t3R|=F+sg6S+S47)@xLI=|~XYk?Wc&>v4~;xurOiF1EC?Rtk!j+mbm zl|Y(z8vAC_imJ6jQAwI6R5p;zkw@q1{!=E*Ot2Hc(*1anx9@C9SZ-4;*;AXhKV1zA zKszlE3rhxWA+v}XZM~F?Prr_cAvKPO#~{>>9Jw3o-+;L{p;S7j5Ix)NTX&mZBGTX* z$F_yj7hI|kcYSWGgg}3$xg!G3Ph{+pPqWyy`r=XaASZrDS?A4gi2cT%hW*M(%eOM_ zdv)ddJLgspz6ERPNf*w@>elddD;s+$nrFmgEaf2nj(Z9kEep#y5T79^i0q^8!VuF- zQ#2Gum-@F(rnzr&5t&$O;@00x=%;AqM<1mMc!+n`fgV}s)%0Mtzroqa3>NLhLS>2L zRJfUC$3?&%vy-W0M}6~bBccSaCuC)O{)~BJ&(G# zDtj!)JBl-n%H?Qes%2Hp(5f{<*&Xhq-O1_n0MBZ)17q~|)pcrSc&7=vyuAh`Dmvk-nzsn)Q`2Gm<_(H;>qI5$&=aHY??71IpX+n z7CaLE)v%5jGKi%?{`GukTBJU$H~ZRWIM=FZbAZX?nLkZTDMiI$u;g;V{j;HkA9!M7hO( z7+7_yA3B<%mtvxiOJE@bn|3hxz%4oO(RstrU>nPGqI}IznbNSv0b=6c&M_u%CLqM{ z*wE$+?J43k5(mm(L_oy(w>FWp$bGFXsus$*OdP*Fmq~t5955zQLo{C3~yKAF$;-%3_Ny*!%RXB zqiqF?%hi1T;aTGArMG^~y$g%e0#V~p6?It`>!#RV3@v5bl_)Ala14W{d;Z6&0lEO} z$2RkqMAlRPArTksZ_(`ho;48XoA4!SZa%YIAUT?=)oiv~Lt^8AJoI33UV^lZ;qk`H zVwS4HcHzoR#Lt8`T#ae8Orib|616{y-l)P!`S}@pXSrUFIS=@{e(gg!@imQUYW>Pa zq<)=mQb6D7ngO~TC64@X)rY_2WVGc?d0@x@IEOG;xfI^QaN&q28KFCbXY_W(dw?wP zn@yNzzBz}P!jJ@M87Eg|2~+EnJ3fCNMa9a#h6ul`fa__ZfKND3Y>(NAOW9Rh^W8}w zYK|2`63`$v-J|(DRZid`eL!n@ct0! z^vnllEX1SnnXd(!=4B>c`5Q{5Fv}a8LjNi~GZ}+L2qAU7>{v678Q-UN!W`V5o$73o z$K?4`O51Ntt%nJqS*rq}_8~FJ6c#0x6;*Fo-3(Oc><&e1@j2#ozIHry)C^p_Iv*uX zGy(4AM?YzT>X7wPV58AucN;f&s#)V06`o#JOVn<)NszNQKY)@Z!}$@v&XJ)-C3EgzfXzI+EZy%Ec1n=H>kdN1mJQrh&v&-J?21#V*S zQrCPlc-rsMu2#2UJ2vyt-pgLIF^WmVN==Xn)y3XOHpCP0%JpdYM2tyGq%YEWRMWFl=en288F*N2JK*FZ z*>gAGl@v6Jnv^u!eI$E7>rc{{3#&)=#&keCq~&6_Hbn{cf@-defM4mwXUsMwQ7+F2&PesqHT^uZ z&0)UvPRrw@7-arRg^=m7W0oDxu)B-}up&4092Os*TY?@KeT@}Q^uDuAj`cT8E-Ucr^wVN27Iep*n&KN#9T|(>ZIs1S$fcorM1;LgOpbclE0^z^D z;gz8h$#>iUW3trGp{XyqX2cFx4z-y}Pz5hG5|)paY73weroJz0cV&V=HS28w%0VHf znXG;9i59jJ7Sa~CHdj2ZHx?gbc&>&m?E0Q3f9(u6>wJ*E@c5Rxh3Yg};A9j=O3Gga zxl(pxkQEX%K1CN)hdx3-(m2np!4^8S*DAtd&Z`X`Irba>6gF6nXn^xTB%l%~-{6oB zR5rUvn5PB7&Q3-xbhlrviogko>x{@C>0?*U-jZkpbwC?o^qNwl><> z6PJ7YKW^1v0kT8)p4=jZHn0HC3U8d8Xp@dGdJOT@(%9KaSR{!9#yif7lFt0}u!uQTz?ZneU0rz%%zH5^g^tc z>B8+_l&;$TZdotYTY@DeTr}V6VYyYVEiv%e_YkbR%w4PW(qhsqE5%P~K`GtM_6Fl| z^K2RmqbUw@o7^yl2fqMv52*Y@7Y7ZlD#^~<&(ty7EXG4xl6zS(<2(>~ycLBQM8eBJ z<_x*et*TB<>@+ely+F4=zaPiqknTFs-0gApjc6pDY}Il!$?G^^GkcS2ia;v(BN-XQ z+69gaX0J79gPMFQzOTNX=>KxY{S(V67Moons#5?Nu+LU3KaU)7sfo}iRb3@%lh#bP zuQ!yOarC822FlZm>bnWnP(tHV)L#z0Eb#A~S9ot#o-VqplsNhGipHEiTF1#-Kx z5?ssu+4yX3FfWYy5rWW5{)mxfIbN>U-^-B``GbO3K!EMuiJ@Q{k!yKc_LdjeIG%NW z(6G0Z$Z#s?E6WfD=V^)CDA|W$lPlB>)|otpHrqyF5{JwO!6|4IGYBN=zq-}vrz)Sx zW6f=GVV%N%s{^?!=42-+!{$TvKu{f&B91}s9+ff^L`5T?DQmjuIx}^XqaMGnDLTBO zb8pP&^0uzre9X(-?1~$Gh9@j&_$$P0iwe0sKcLZBg~SMfV#yixNL1L_T@h--#wIZw zt+{f_Lv%6RFTeKHayd_Z624wn}kC@3|1Iiplea#%b$!g2!2aB+EDZqJtHr?#t&Uj8o}#wJ7b4p6g11 zKStghmyLPm_H{;PAeq&DO@#Ot=?*(3_m2A};f+=@9waI03kxN!R9Iif*M)V^FfzWL zO;uJ^o5>FpC5~y8at7wu{Xx=Q%z>|cLH(>uRT%k?49FB%Vi0Qw3oupGygwrW!}J=0 zaT4tEJFjEByy3ufjl1`opu>}|^|Q6^7u`KL{*Cv_r*Py#><7z=r``0!^sXMk+AM*C z>|u=h%~xd|4FZcT@5yIEMyAE(bV-#)!zZSpT$CMV$pV$ z;d#FsJ-Q-F?~aM4?B3)LbpHkky>5yUoEr$nO*QPeY6#w)xxiwib`Fi~=SbWPJz6~z zK<}{5{Kf=$W^KP5SwkXhXV(`-%^wVA8{a}XVc%fZfpFjO-w}9=2?sw>T*I|J%}CC) zyrq~t^Y@;ij9k1O4BMf&moo~kp?LQ+{r~XwmQihYU$-dkR-kxrEl}KoLvd|+3beQs zcY@ObMM7~a9w03RTA*lgcL|WOCAvrUAMb z)}ZGocy~2Z1Tj90lJdL#HZM6C#17F9JlSf*wRqzUuP|8Q<9A$2A@D-kQ&gl|Fjaiq z7O#GW>8xvxXE>8HRRTa($>af_RpZ4j?8~kOw}6p}7Tf-7ha6x&T3o645HBBjG4nH6 z#gFx;H48*NE|od4C(|(zs|?oq>+G0p-WAmK(E&gM@Mv`%?DgdFf9Eh)#IM!?(}p+E zEw}=Nd3Kqg?n73w;=5*^StiH!QwJts!u1vhDt(Im3&7E7+26Xkp}z=Up`e+@b++hL zqu<|$269D4HmG&`>t+%^VNj$;2|B!AM^HM7$IrX4<2%@o2qVxXv{Cw{Hnsrem zg+!61aAc~^Lc2TkVEOi|l(Pw!G+dc;0rh||DJ?K zTyBr;o$IETB2}#sV}#$R#guD`ZwuF^wrb4$pn<|rTx5(J405>4VV^Srx5;IV)KPz# zx4^&KOPkWwa2M<%_-M`W^T`W+Eb?L+4&p)*T81KifoE_*;U#a>GiCR{z}3*wWZ!$` z=bM~K66_EsA(#>?ZsfBB(jSW3p=G-`a7+Qs24hWo*Jl4Ufk|woom5Z4O#vtHW+wGM z?rg3M&ehSa5>3~}bS@rxJGry3UF&bt2lz8OY4QwElbh?d2E#ZJr_8*&fw@WKY7}1|iXlvM0fASLiPIFr;x3k>xi^!D-wgS(3HI?o;Pxd}Y~w z8NB@Fs$Aci+qfi)-gU9^TVk6gLG<0edoc5fRb(OBAbP!cz)&ul)SG%8sh$ryY^Rfq zo9Ok_7(*jg)e~8VxYc&E?t1He)TP=B#hfUS7X72QKre503m17=B!<9i~PZ^4~4S5Z=G;O?-m zU{QQ3*y8I{MckXLf%kJ5>dvUjb)c^Eh(`Y}yYD9Ssv|taT8fhwhAu6%=>dNXJY>Fi zYXw<->2Rx`v+rXo(gFH(-J|k?#C?HefluI# z7d_sh96B|}C7tJ+Gy z8Ew0Y|6{w*xNu-+$E6#gTgWV=Tl^Zc?t=t;qmbX$hjiUIH*U9e_oww6pb_~Q zaiIU~Voj~xUW~yeK$skyVeVW0S4FYq36iNa0oc7YE5BS836FwS7k;Pb5SFZ|9_Q&N zn|QaH9Tpl+atwHZ?nZNwETreO?BR2%PDvyIoJ&=cw^sXj$!;JAafS&p%+CeY22NRoIb8{yOmxY<%! zCH+V4oyh6d0V{OcFh2WBPG`sygbmYBkykJ9KUQ>0uM z6G`6|d{-tW?`MW^Lyj*$P`@9NJCQz25s*jZ@bS{kcaXyK(S>9B<<4eFU4mBb2XU)| zW(*g^J9(jm@D0isDxRN@<9<#$dT++?y5pHo+JnWF>`6t94KH`c965<+es?FnTd8{m zgpO`IzTsSRk#0`xxpylEc2eW&gH2XKI+9k%I?l8Tl^ZH4 z>lG~6~n%tFjr^ph-DB2{Tgm5PC(VfVoOD4!OkyfguZrtni)`OWv#k&8|z$AE)cjfm3rvV1b;jz~g8GA{z zQ!45R|A4;M*E)PUov`>r=_!tJsiQyFd z0k#r#t3J9cq*Y`Vop<;!yQqZWvx!aW0DJZ&BYxUd4`H<;=3IiFU{#i`BCpOd+xHRZ z0m{} zcY9Fa?m3(urB<@vPmd=H{oTXQ_jX?D4f2JOS`Uxxot3Sox3Z~Jz-yt_AlJhzxt)HV z!s#aymifATcDX(4Y$H$Xt8z-H)U;3@_MN+f;eJPHk7KO+D8-7zB$6Wnr2M^SCWt7S zcGJ}`pAm`JCL{5u)z^>oCUILs4R02ea?2ocws{p@NmMRHhWhZy65jU0*R})@S3%CAw^IE(M&3 zSLNOj*jBW7JbdavW`5+oX~8v=)CR~&KrBGl=t@5#J;PJ#q}<845ljM+*c1wTH_lO( zZ)`Wi2L{!MUvSAX0d&&7tmuNI%w0Rlm+njW*T1)?pEx?o5S6$>RXC=_|w1d~0zX}`J~ za-5O#hf$o0=`(`A`Zu(bERN`T>#0MFUyXOJ*}l)!7{-=LO+Z1S6*gkfJia+2`{pe6h|h}ZB22V6i8qCcU0`wE z0}w7n%zfwuBu1MPBm0Y8( z^fjb_t6mNdt@3yO7I>4Wsp>I_0ejZ@n#)hIdfb+PhP?NEZZ2a-<-dh>b#gpegeOKw zU39(Tl=?0&?cYq6cq+A~uO1j&xXk^U!wtL2#d~bb`D%Qt>n_sDUpt=h0{~^DCFT&h z7kU>GUN`QPmY;~uyEO(mH6fiNRL?d~;7d49DZg>fDGOwLop_oA5a+RfS=yEUAS>p? z@$F07&-{4>|;V0C9$!KWx&MabywLc}P^<>`b*nR51PxGpx#eVcn zo0BXXf2O-oG^4IjcxRM*J0S7F`%6|sB|-7o=e6#Ju}5;y!Ie& zTo(f4JK=`VSl$8X9@%UwG}X%W`pC8VcH_GHyLSz8(1v^NS@MVt$z{@-SJ6$8dNnMv zZF1oUv3Wh|r%sE*dGeNlOmqJ!ofofPPrC=8-cL_+-mbP9-8pVVw8cT)>?Qv6Ik*2X zS#me4jnH){A(tl6p=D~Su~g!Mm^B0}hl{^Ya#TfSr*7>iC*3r8zwdEb0l2){Oz!po z0UjQaup}2gBPKdI@ax)wlrQGrV-E|P)8>ZTU?zMA{~;3`$nE;@qUVL+Pd!uzhdd@n zYrU?HrBd-REoOOm1wH~YH9ftb9KLG^h;L6yDN6L1iS{E{3K#uMU_Gn0}&81{-#E=ppu+?Q7$e~N4^xw4&YZ!cH z7X2dO4Vz$o*se?M7B~<^12-z6byKNeOm(m-d5oR3o6J^nE0&rkS!9&(JRHID!6tBb z(lU^1zOu+c*B-aavbnHzPGBc>Iq!*l#`81h!H-nl@^EUa+-;cBWdcZ+*?C3!8o6J) zu%?94=ga-HX4qG;DA0aEUpiGcoUs(#9>-|rHm8aYy9q#%*>Y86u6isEKeb>LvCF%g zX9`T|igbvqtIF{sP}Zrd-RkTIdtdbmzu9Z)V71HqlmUgQ+`VY`8}o%{=#~gEI9@Gk zy#X{3@KTI(KtLkY72OrNMYKKaF9=sY&O^GGhM_~X^(E}*_(^di3LCQ>mk$T1$=!P9 zDM?6ng$0KbyY7Y zVT7){I;8V94oxHXlafCQxGa%m>f+J4qU3-oWw7&HhVd-5C$slk>u3DaT6G(@Nu71q zQlS@Fsgpt%K1o9@nEOp@4|<0%w}#m2KCW$cDNT@_Fbf$!yxYd~FEeXHR_tKy1+)L< z-p(d~QxY`i5zXk5n+Aa=w4pnm>F;u!y|Fes+>Zc97g zl9?427LK26>l{`prcLOmj8Jz427A>fF<tPOy)YzVXt^7^glTw zQq$5HyHbBGHIP}w?2Pp#-W*&q;TbZfCQI(~h+q(8X#*hSt5yxbW8rk5FDiw7?yn#& zY?j=!+tHfZB&Dt9W?R)IlkwbTF4j5;%Kq3MGy3D({kY(oSeMs-jm zV(?(3+E^CqP&1Z4j-={0HUvkK=6#jpC^eGZ52WsnL|SpPDw7``0D3_d1ep3m<7w1$ zC9XE0=N~wQ(oZwjj6dBFosYbI{e~uP^IkeqvS|t@#x~z=q3;uX!M1X;T3>uV zh8e=zYFhh2C;3D&VMQ-6k-honxeNLD=hQ_jPJyV5#wy7DbUdShRYdypa4vpX<@r|R zQqQr6fdS~E@S7Pv9rP%6BU4`r^LIiD8}zz^?U%m;1&j&pudxG~{*U2J70a7P;H5Cp zZJ4%w!uYf~sVvB8uYhJATwGO#Ze{dv`H;3_&SuLSFjkl#^ZZeCB$8IliLZx*sZvHX zh0`z)cM&yL9O5K{h}>re%0G>au)Eji+l1;^p31;FnIWU$#qQW}c$9JaS{`WOum_|5--s62r z42|zMiPUpklEhCi!kgHwMFL}m65qVa9K#Aw#^565ppINXeun8Z|$onskfSZr4S^!!q z08dTGn1(Xm8fYOBnWVXoTko2q@_fVD|yHI14KbBq0aU15%f?R%yiO=xs^Te|mF0K0H%@u$23V{b^_*X=+1p z3IXnj=xUPuBw9}}Y=`Xcex$H8KY%iDtj=MSbZ?q2`l zjmM$^zn4=N&JD6y+AaQIS} zcX5&WGYdZKYTvh&9PE}n?i-_E&0G+nYn_V=QhCoTxQGX8L8qyzAZk6cZ{HJcfxPC4 z(T3X|zoMMx6N_p~s46>jc_vhY@AV5pqYPY6(&pyai^lP9dHjyh^~Mu%22A{NTdo8ABehpZk{1~ixasX*KFp~Z*<{h@KqULHoBdJ z45KFbKp&2jiuZ9w7UgZ~D<{VL}bX#v!xb^#7BGRzRbIKlWksoA6BjWQbfSLvpHYA831bM_%>K4W$^wU5~8 z*m}?QzEZW6O96t+I9Gqch4RQ^q>w)maU>61gt7$C>4%{JPZqAg1IVydbm&)@flCal zG63yA(}gCG6dMpz2i=9Mu`X1m<9!oFIH-U?>4u*Hsge)P`zb70P>AF-4;h&9>R|Ub zcP<_qDdE?r+QL}5=5(;OU~nS>8r~i6;K&z2VqtzmZexGpW_H^-uc~=!JWJg5yS~l9;DORwZkcm) zvPt}QyQ;rtMqejMvHJ$L`n*O(hARm}BcF}YrcD`#P)3l*_=0KOrqGWW5Wd~`-?%;- zx0%Ukh)dKdB;ZE;L|gbT>PF4durok^C%%*vrxwoz?odisM0j54{!Dq-anYsw_7-6V z>z8VgtZuo~WMr1~@e8Y~r_!xij@PcA?qk=C(m7t=w;QV=mphF;OEqm30q$fe==)0XGyGoEbr$lDDL zS$=wJo4FG2IItLYzOORThkxSk*;Ge%^;L_n=Mo<0pT~_$gT|*q$9{ExAB{B1Vlf_J z_Rh}MKozL&?sZYyB!T~_KUO6$m+ur=?VbP;d1F~~K=8%@E_(9NhY2&b-pOiqg#$h+ zfMakcbFK4Y=dj*9Wj=eaQW@^nO)RnOT54nM=8$2<@mdM`R-kFyE=$xgs*!#;dwtxb z-i4?%s2lM@Tcmw;T_t@XhgTOS<78<)O8O6sKmewt>9u7cJgUBy^?95a1ae|1);rIr zWgru$ROW=+M3{%Qu)tGwrg4XN%MId;%gbJnNwPgB2<$HJvmD4QVirEr|EZ-GNXP!A z^tBB4da2sY<7gNsU;nK{3nb2iPrF7g)b*-XNtt%7(q3Vkp__A6tD0_NtDj}$_XIU#rz^zi+H38ej5?@kE5yIsT2UdtK2w#V&O z4Y#v2t!tkiP1Ns>Uw-@aNl9b(nt4Ca+qDER%nFZ2yS&`nXKBeO*rqj+oBBne_MYT5 z^y4C48l}#o&y;#%dtLYV%%0V82t&sKw~shW5ou`;7Ctx483a60l#|;2UEtX`LfSu& z9BePb+`8Ln>Xrc0;;fOFu6_`VzvR{Hc%TcH)V4Xg9+&#i!{}6 zcs+o;NUB=ft?g3Lh;0q?RAlU)Qke5fpclY-8SpNt>_*I2owO;{w~bb}?OIS9oJp84 z+O~UeBxe?k=|5oa1VE8G^!7SW_l;&>M2R-5+v8e(Nn{52z ztmV7N(a6-cxei^-P1PQ6r=%SWsavD0CVCS8##Atr1q3(%z=ft2os^Jl2=WW2T@I&U>UM`D{I%j_V;UOBQ44+#96zinht``x^N~ABpGg`T6>a6 zFm&Y(C@HRvPE|4jQ(O`PymktoO5Z|NET5f<3TfdN#S%PGoU{!^YDnx+Mwoo@FQ_N8 zX7@NjpJny|1d1N>f+^zL?3_*Nn+26eT{6YE3nj%9_+EXKn%lL>L%EI!4HV5TLa*bp zeULdw{nyPPQ?M1FdAEMX%osreT~)1*IwWDd*Ay=^8PPC_{fN{+Pik59E_+NDU-~NG|l{YhlcYQHa%uk`9bgFMI8y%`{^RWdvQT_qf7(W#RR-3()-B zK*d@;)i+OWTT&xPoJL9O6d&U<1vd-kxly(o3)h(54(3vfJ0yX*00}%%zmrX<&kB({sGNUOe$x(g<^rYgvriiVJ<=cZAA0+>4r>A>H zTx{^}@P_L)d0`pSJ5bUHM&SK(6Rt^|7(hD5-TIwX$S#2GD`V`(45J}nkXwDY-c9Vh z+3{dj=ZfRSA+JyQc>STc2*{-Pi3)F(*%+{*RwKtbgf&qOSeiM#@6i~0F+IET(2_;G zdbD}?^X7m$qd>>WeGTHT#06UZ%I>-X<0R~xBphk94% z3bm3cjfl_=EvQ&mMdoG9v&8!H(UOIm$Sk#=L3|HvF}{gSOSk`?VeJJZ00lpucY6IS zC7vC)ZDWoqz3f(YTm)`#P*Z{52TVH-y2`N1t~yl1*RsWMtc;>ENPHj)dugkYMU=cXeVTf|{C9Ohq!{+1 z1Tuc?Ozqf~;WEr&L`wbPCTwmfW=J6FOL!&kt`uh2=Eez9iZzcU3Ea@G$#(P^VXQ(5 z66kupZ_?WgnkyH7k8_rglK(|FiMq6vc@oOmGqrmhIx9>_){O&mkt7GpY`=eR9X{zv zZ|D#?0dP{(pUQL`2oH1c!Z%bCc_=eFIU)Xx87sfzkygf|{jm|gY?UBV7~?ReXXATD z@Wtfwza}(2DxrB&7f20Tz=0-tY*dhj{zH~G$KyOno+!W3=oE8OkpG_G=8)d@>K4L< z>4q+d1S=4g)B|u1r?(|c>R7Xs3CW}sBN7y^lGt}SG^5_n?uK6LI6OH|H!KpaP{rXZ zlyol_pQc}yjj|`jjji|@#&*t>|4@o2$wDTe1igC&xzy>R)(7;66G7EGrDNrW*RPH) zSeqZhUwWaCd2$QJBZA6P)o-p{O=K)MPR(o~>@iM8YGr;R)^9A3J%3SVG_`AK48VZuH@Ij2;h3l7q0fa{!J4F4U zp~^pCgzvwM&%82}%kwE@1gFOFRh5e$hxI5i|NP;tU7Lm!N=61MkQGpoU?+qVUV4|n z3;@FjBr=S=Rffw4wEB71Gy0SDrGOOe`vD!M?6Z=s=`T|E>c8# zQiAqb{Kplfi=4k~ePrGXPU@3c{oze#vqebQU3ppJK7c zb%`)Am>uHf!6pFWl8roHgNv9o$$n(^D#)i`h6oGk9P7x#yMt^jlyI}YZvrMP;p>i= zw8kN5tOF+q__Yphi#K3v^otR%VB^(U(6P&HqVm8$=sGZXUGvdHQBqp1aMZof#n@n! z2;7G8E>q~S*P069n~O}(ZPwxJ5X}5lcjio*{{la(pEug<`+I8z#ILVSAWp5y0CzYIR!c<{q z#ZuoERZey1mP=3()ffFsZ0xCh`wTB>KqGR%vl8X`A$iH^8%vz&3(+ZclXnR{og3|) zTM;Y}GA9*sC`YOf7?rslOy^oIyYkC^6abBpY+u0gYKC{Ds@d9wF}~?v2V+d2bF< z=z6#^?(4+%kha5vV9rPSjo6hVvxNxKD|3No5Vl%>3h?&P?Ks$ZI(TNTpVyf-oYr-JgrsIZS;U=ZN!k{-+{ko@*Up&Dr99^W;fJF;FAqTf) z?XVm;Z7S`KHesQ^yg>5w9-~#8!2M<3DeWwxC!@q=A>47MkwJf~$iNmKig!i)cNEl$ zTPXs;C!=41{6sCcdtVFi%S3O74QvX(VHSpX*Zeb8ziJ-K6wXo&m{n|W_*ES)Tlhav zs|LC?tFmKcn71!h13%VR-f}gxk-pzuZprxr%J^WgKy6)0?at+%IiKwmVAE@`=DXj( zC*Q8FnwF4qe{by{Ou5Fx#lCN9Y=p0LOL%fF8&1dYIS90!lvb9)20R~@ z-Zll8&AOITk*`3Rez#3I+1vu=U(i~E*@Z7cpD8a#HB}Y7hx*F!U}2KF-ZXP_QUeE9 zuHMtta-R1|Eq9}(>HACHGbKHn07eAW!ZxpYP*;Pw3qA3fCUHBRZHJFvOeitNAer4` znP3jc`=7|&1~vuZer=V!?7pR1v`6S0+ zY+tvF^A#wDxB(YMZoVg-g!zBoBnkw-dL9%=PrcH{M!~%VP=<1^6a{NJgR?1j|DB3%vaM=!OB#Z*Y3Efx?O1{b!UaxXZnO8<0>`_diNp zI47g7d+lpDD}G~<7erya1C3$CxBt9;O|_`*y{d!hH-5oj)70RR!V+N`(bQo1X>;de zGhMMFYV~%7-CG@1i1SE_v&zPjx&)8s?UyWz+%coimOif( z+hh#0pRA}$u}VozjZ5NOw__wpNik|bi*)>zzCKZ-=gS>a^`p9_3qfitISlL#C~A z3}jP&nlaN$7Y~dj6b;nX@Q+79>)>_HkDmM$!46x^1|%4}rSC>y5lASxBq8?B< zUam>fPg-pS$nmbvt=gH~HRDd`7s5DqHM!nLLRH4#_IbaqGc7(X`eXL8@du{l5rFFc z`ZOB=Q-Ke+J$ZzWz|`VB718fvIxOyrZI)k>`zo+WoQ$mwIW{f&OGMZQ6M>6o8o<(+uM)>i! zoCPkpS_EzR4dj{GFWAji^bkvtu6al#j;0)^<)#bGsr5n^-a0)R$Tf{-%LrumA-I4H zDIVCaZxdG(=DiR4!u#8BF*g?A=Ffdcj)!M! zePsVy{f2bDOkXg#K73WEQrw^NAIs%#Y+xE-EtAUmVPkBwM=#m~)UV9NBzaE?df27$ zORddGMGTHIq8|>E7uwm*$1;mJgv>-1X_U(jqqu8J5*M zcSbGp+E(?5MRHjI6PFR}crvZQADX8tH6)n?4roxcr_%5nWzzB+nraB6R!=O(iXd^j zii#!qK;xoC3hLI{sVww;z_1=*Qo@rmYf5A*yUZk=my$aT9dLAdl2%_@G}!KbK6n}> zzPmai-+7R}z?d@rVn31Y?&=*6;9n!%sVXbj{R)s!XNg>sFN|hI9{v8o8&|2(92FVK z;kETrVrby`8{ZM4v80{6Thic*HJ>k30$4PAVzk#o!_?+G--i9akhSl$$)}jogMI)& zXL`AbN4q|drqER^X#j9jH;yZn^k!$=>&^gjH<-(;N5r-*-v{E@-Ddan@>a#xG#_;c z?-(3m#HPMa?gH};n}LTR{wSI}DO&Znz!FoDyS-7iBqJwHLi^^`fTPd_hp+lJ&9V&6 z?#9X}$`b7lFrg+w7I>d1(%J&H{Zv^Md-^q#UCuj{GCsPR2C?l)7yq^@a7AWbUiz=8 z$3hU7lA!gzZ{KtWvZuzH9RrtZHNr?nOYU7>$?<3%RY-E%Xnj-BR2ZUGUX+u6`9e#- zk0s}&kkZ%cB2A<1YXB4JfTcvpUqbGgw3t)E9n)WH?B8>|_}I?(B7;*_4cy~R{{Bpd z@vAE%&pt_Fs(oG1W-P_X_`1k(^DJK{;%O`cl&(TQ4NLl{l~CXLx!0l#2^u_R)vnO!A6yptCG*0ra2 z{t_F3hber~%+w0)P{tQ-2dkYGw%ySp1e)DbK`Lqf5CvB7t#__L(VBIay!Lmf zAzorN{7K2Sdl)+YS^EcEtf7}O>ixkloBg0MTE?d0F?TuI3MY#*il zD5Smu$EG{S?ICWi?k(xE(WPPt^{*Du?uNC#oDq%;^QENvA^Kpus4`K5)2NnPGCg4n zlbc3em4>tKpg+%va(3n`@^<9zANjvHTr~2Yp|*CdaRHAPuSGAPz)Hy zJFoHdW-+VRHu#PsBa*Ujq-iuVSZU_J-#=A(!&Fesz(=~yM#wm{;9TKSNB*vxprm)D zPgqzQN>=>&Ej_2TTdBsl0hb`f$=p{l(`n*-=x+N6!czhF6}WT985DlpP0QANR?0l- z>6~uX_>;TAAbZG!!nStyWBRs(^HXLhtO5<^XOt=Z$o3Scl+)rOyU%g>)%JY~Q5&+X z&E+O$z`tn}n9Ox%+K}#M?REfb+gJMG+yX!+^r_ zymq#^cvQmJ+wyedl;<4?42aru4S>sxfIDZ^UChPz8C(TASW6ZayYMQ-;L$Ebd<-=n|-KRU~ zpiq!+D{D`cSw;0HlOG%B)tf^HTeR()LjkBsLWqY4Px+Z<*?ax|EOxCO-upWX1rj#% z3A~bS5<`QVcPG-;HjHFDnIq%0Rz;)F)68L4^Du9VI$19E$(&xN722gDf{5}CrLfU( z`G@HglTXm!jD}~Yv+4DZUrC#Yf0vMGv8iu-h+(XG+ug9Mq+!TeKUiDz9ix8`&ihij zr7xx^#2>*Ra}gJCJ5`S;b=S@GemmfWRkgD+bJcFbd%HBCL;g#KbtDX`|qYQqU#FJCDbi0_hg%WaG@pEbkx@s`B0tx5zL3M5|2N}a7qrLT&NbZ zCVo)P@_cxtT=c^9b*gm7lpQL|03TvqMOmOX)YGbt_Kz#_#JC`V8iY)KYBVqn&nL;R zus_KVMsy^95Hrb+tsr@~rjglAdl20FdoOt5GPiA84zZk~{ZyW6ZBL&px4 zmeknoyPZ0)xfba3+PT9y=9^v#2FnwD0VEF<;r2{>plI5PIMn|(u;gW7hiiSv&oUE> zg`Nyh9u96!w?TY6bKoCbH=$-f3%Wfn^)ZquIh3Fl>L=MA_vEb-Zk%| zgJgV}`@`~w_6hDhUpf0feeNC#Tl+~7D%Jlb{`Lgr$ELJAhW`itZ)B?gb*?mHTtHY#>2oq z9rndQ6kKN+>k;cZ$f4YSjhv%O`s5o(tVnD{qL`pvHdmKR#-&lfR`srcLh>sgHVSQ% zQno<3WFzB^P%4yGHZz_PONQ+;7rKuwYhKd8ccXift@DTo%xb&31d^8ukE6}Qj*c$3 zF`0g04KZ4IB>vHfOJs?4L-Qtct-|Gqw`eYU_D4!|n6~bd^2CGI_Y_^lspa$7F?p-m zrsg>u_r?0-S?7|m?U4oN}vCRKK|J^C)SO4x5 z1YZ!?D~X{%3}$$bP zzqCXBlb1>>c@X?&xE%SLz?qJVVh=g|59@S1cXRezSCO+;D|_BMP_<|9dJAng15>+Y zgB?#kh<8m|><)xt19tS^G{LNiojH=Gxv|3VRrKA5NxPU(+yl`es%;9v4i5E_dQ+L9 z{YjhBzjx+uJUst5rx4V?2~3Xc@?FgN!tWfyL7bxq22Io(!{TR3&nT|8tDJ3!pO{r3m%usOG)A@1^9)x0t zz=8q)wElOALo1~?W~(y!Fu8KzEdKZP0DqCSVh}*mON4Ynn!&y~-$%_dqe0E=Io9g0 zb|>~1U-`19zl`tqYBcB<69=7y!K6uN?mon{DP~P2Pvv$EX9Oy8Ggx(NLej( zaD#PaeAWM|1h@k&~YS}B_tu})R z!MnBjZ3-2imw~4LD<=OdH2?hw`&bARUf3aY2=%pCm)#d69EP%HK{|e1oUAj50vZOe z3l5ZY1Ls~aPDIcDizcB2o%zYu$SH;r^}@>`-wY@0q`|GaEJnY>IM`jwwUHdzZ~y}` zEBgsfC`kdCwBed06^b3*YTUQab@Qs12m|zMv$9rGv%J!mR$54g=Z&$yu3SuSE?Nak z0ZT=4Ga?=u}v7sH9jVaY@96eh8A!m=IsLc zHhFbGJ)rv~(BYP3?Wbukb#xOji5P`{r@(F4h*OJOi1Nr;=%>ZCxb{jXHvZq7@ZaOG z3=5kQ#kXo;Ij8?#0c$Cjk8BVv+9h3>Bc0!8TTt2;4@pZCyr2YHj5a9{`H%B|OhL1rjeFF^og>R?i}_Xa9W6C+ z3N~u?>`50}y0KYs&}6go$L3S}_9dUElv9hzi5QIS$aBga~V~t|PHhe%e3O<`+;s*V^oftk=Jk!=;Bv$bq z41(}91sD}iBkO?GU`{x=<|*V6DZ}KNmSchoaq@8AK!i+$)Tm^Z(66;7qdDX3p>E3C zhTudOnWw4?*x6h*<2F#|_e!Auv4;O-@Dcdm>l8eSKnxZ(VMR4itsJ7rdaHTUpKi_9 zYceq4AZY`K(+7y!I6{m6d4h*r8@Z*Nz9@9OQ0JWglJnAJA@IW-B9Lt#fs@(`+25Pl zqGcyUuX2ukD|4faWM#$+i5W~fEPDCd_}rZXsSzN^_0b_&to{^<0cnx^lf1VOj*MB2 zQc!!H$u|EZQi`vi6=CIq;NI6klN9!Sf%u`O`luh-#Q>W)yH%_euy^+ELr0H{Zxok} z{rCO(A4}@+`kTcQ^g8Tv$&8I9JEs@25a5aQBQe)3^H(*l0YQCrccXTXsu-0twB`b5x#egCQ$Q-?VP%#u#+OX#@ayifPYJn3bbWYZ4C&^NF0kxtXI}nBDXMq> zKm!3-NxE+AGkN8H>4AW~G{- zUGeNgQp6W+YjL8_R)GhzDZIaS3ML*Sl6Sv$A24Lk08OxJT|64NPNng(+OhL)&^FLT zjW<)m&*xLkOE;*-7jWx!^mW#1kZUPiy*4L&SrSW3RN;y^`MFmwupmF$SvB&+#SJo6 zMdGjo5&68nD2-5tE`}_`G`=yD7{LM3-H~-{{E7P~L`>1ZXPe^ZQN8446Lye`wBUZF zBt_D{sKoz>9-s{%pwurg*ig34;LQ2a<))7%CS|*JfwAop;aO>99Vuo-(yZ2NeemvM z7nt8H8qXUCNWh$GLAMiBk0jvODzuzY|k&-;Q>rmDblGGq?i-LVQJ3$qvCP z>|OkLOMBX#i_=qGJDC*oK(fG_VNS{>6f?77_o+2V7I~`KhvzJ4@*F%(f;KhrW90&J zEYWHn$HvBGoZfrs6-RULiRZHjQQ!(Y{2&=h!m6k@*{uk|b;er98_0T;Y0v@A%%U(= z^Ko^dwctxw59DXm_&4wF&3FHg2to(K%7FxUCH(Viu!XhJt30CS z#4iS`0D8kvr)}e7X(f%q#L=j4wEP$lG+@J5zl3Xp>aw2_AXt#J&Unb=lq$*UD8S%n zPT3Lt`I6Zkn$MN>k#f|i++5qDh0jiw@`TNMhAz-|eDC!tDuMM>Pg?LJa}P44hpPxA zGYhj|;|+@J>O`u(BKEl%l}TTXcA}uZ=5=LJal$ZK&77q>A&SQ$micSRQ;o#Z|7*-3 z2)L2Sq-a|j+&VED0p!)M$S6<_9H6qKsV-sO?_by`WxRz2$?dL4xUS0?crj4SywG=Y z8e=oi%>Ey7{kK#fE)K;2 z{bWQc+`A8%OJXrM@+}0}uzHRBKXkoiK$QR1HB6@trG(4?N(<6Cw3IX|-9rp0B`_e} zf=EdZJ&1rxgA&qR0}LV!0#bvtbibG9e$M&(JokA&@`-DHvG>|*ueE>UKL}*haM@q) z4(Cn1STQ1%k7T!Dc*xnxx!(FpG*7QApxD`b2svjW+VSD{rP3u5rE=j;;@R3`drw%^ z-*Y#Mr^5t<>KHPxj)^f?rA{bV3XY@a^5f`#{9NggaICiB(@|x{BFy3adHXYBP)2hb zeqqoqs)W1?77R9{j0rfo(-8N&|Nrls3xIFt(!qzV!wHQPbr`(-5^3jynIL%b^dmnR zrzEYJcsY8V>~d~hr}tC1vIB9>XzI5>YV4GdV8%U-ngkw3{Dr)`zuhT9?_@9+IfTXx zsI?16t{BX2GqT&vaiNwDtg19f{gq78SQS+lKsfQA#g6ag=5i=TAtyMnkn2Vo52($NZY%NJsHkxD`L7L#s3|x0cK6>K1!aLnWrs=5Kfp?FP(&yE=;+-Sx` z5QN!8%15$90-OspF>gs``0`G6cE?kXM`CfUnG8XL-+0nWmvG|e8r*`km(LTjJ#hAK zMxn?1)`*?dX#1-tfBBOY7=v&`xBCw?|LXyc1yT)2kK?<8{o1DzVR@ihK|54tmr>$+ zBM+YpTQ&T?YE#7XBqExBsIhMIE&GFB1`Y`_xN?iYr6&3i{AoMDDUEMJw&}JN8}Yu4 zqyJ{i+h@Nv)d$Qh8DlTm0Fdv_S<6QNQ1p9()SDu{w-(Zbab7r>rma>9jA=~ZJr_2gb0X_Bp1?VGV@ENHK90a$t4 z1`5b-2vD#WB4tAQ5|O(^QV+XU0x-+Sok*NdMUJ_04VCbTV%n+r-Ni2 z4lyVNG7)x6`j$8ijl<~77t3Jnc)IjFMNEeiUE1N3Ch4fTNM!BfI9hB)frysld~y|% z+jg1qr>37kv*rPG8FN15AYO^IJ<#wF=|!EKM2G^mlR zyWI~V#}W}Hyu`jniC>;X;6odk%7B0KcBu;S=7fvqIO=Sg6rrM`GbIUMI_dP;8>y5p zVs^yK-fgyj+`=i-{NIqd{NTGm9GZQqklvArUv+xcErZ%uC z$=)nUyXq}Ze8n+3>cAeGxxw%dqSDA$(zZdJWP=pb@S(Tn80BHNJs4i6rpTWAS^Ve2 z-NE(_o2aj%U z8SZRaUb=oz*kMU{^fOIfP13aTH7KgU?gO(6#&2x&KX}*<%WKZUvzHTm>a?N+jlBP*C z*oxxYi=dpEVdA6WbGDna3AHZak_UrsfcPqp6;eUs@z#5#RXaING)mjRh5(AR!I3l* zuG)NlBgqZSAO%Z3Qa%j?phm0pZSS>)E3{d_;Q8`OSXjU+r`*}9HHGNTM%7f->FnBd z_x#Qk^HmZ91aic@@otYyFOZ|fch!CsV_GhH_0V9Ep=GzAMUig!v)Lb^SUXr^w@3ek z?;!EtCnsdp0L2{wk}^;u=84)$tZ(O~tvT@$%rv%~5D&|dsPVh48Q*nMhx4|B&yWC0 z%wFZ}lblbu)&DFsONEYyuG)D+i?2a>y0V(xuPh)sxR|Rvf4VZ@?NZ1clO+jMlKN+o z$-5Z04qTRFZ+4Y-u#`QUkfMDC3$X{8ZlvHZ%js%%NsSU@iej)$9%v#MI;n!`_#ki3 z9vF6z(XNL|kS}U#>X`MJuslyIm+wilo}!&--dFJ${4#OAwzDDS$P~ESaAjlu-C!Zw z@`|E&^UIVo8RGfI`{Dnb0UqN&X8^6I2IELVJFMf7tczsVLy%IBGdk-Oh0K1PE~pns zOY3#t5az%-wm+fA1Cmlj!x+n|0F+W$(gYVVjq_sq+lG^pm$q1So4~*anFcDiB&F0; zQD5}^0=&UR8$*|_P4Gln*2I@PR-p1Efk;YwYIZ{rrPLdc_N>+zCamTpFPOXBT4ji^ z?;8WerZ8?`R+79fJbEmTtON!iv64lK+PK=9fF6tegiP739iQFSTy%ME!1o|fTpSQ@IsAOz`@+`{fu8fGg*|a)EJX7RwU;5X$FO%60zh^-XNra6sr^ssxm^Ncf z0^x6#nb(e~8KS-zx}J1M16N^hZ$B*By&Y1+Z}~tV^2`|7A;G#qjna-1AbyMj-5V%- z2QHuv2Hkv1h&5uh-T3IC((G!DykQ9;ggy==^W98A0n|;(E;sMIXa)FeZs*ja+Vxrr zoj%$_SQZl>ui~pO%;5sqUK;j>caq8ZR(2uA$2^~fRF8T7pPwf(Aqu3#vSHKt(tm$(MdED- zbyN~=>odi1b=m^8C!ab+2a@pE)pY z?#AAljk3?0{R5cGH*-yQWUMzGluVA*+3tuO=;P+1 zZ+btL_wj`|$;7Bx;$rZYV{(+z=hbiky*A?A1?9mc&qJWm!}HU|;Zw9OYE+N8w%PdK z0K`g}2a_xxDG<5KF#`{P(-ZEVxHWli=v^;k3f|~=b8v5Rh)BCX^mmUihQDHbh6mP$ z?<_x#(C3ZQlhb=-O8WdObh5xPUNb|A9HaI*?>#Ba>TnG2;PcL4tvF}XR}1nWjbgKp zDLhSY+H{H4px??GsU~iZOBxy2X_Ua3`$6^DVb{YA@*;6Q`ka5G;K3>ZSmaEQCvo5Lks9A ziXb8Mb-{`cN+JZ(H8Y3GTt5B!@T^&(1XsEknPeMZPU~%P4l> zW4Y{udROtGPJ%g)Opr3KYS0DL(}1GgyV)SoA?DrzdGm1>@?H^UwXultda%zk17;>J z`ao+a;HLC($p#ng#j%F7xa$wTxu~HJ@c>GWD=A99^bOI3XHEF zKdeMv?#!@#{<*PsN6^1H+uK#rU+(m@Ldo&QnoZj0g*9`Z8_(xMuU*We5{rw=nzeIQ zk9UdXPOdM^PwQSgeh{Vg+cxPjed9v@p#JB%Wi8XSSG#@J(e5Sw+Qu2*#82yfXblAL_z?SF+YF5<*PFhSaPgBWyG7BdbH`LqWNP|NyeL*n}*Z6 zgb3jfqlxTz6c6bh6CrW*ZfT7(cq%ZkX!8&(sBvU{@TaAqDWIVm3=-?F z|6-Tu5Ise++SkLJFJY|OpVNygkvT`mzWDmajtalZKWnPLdb{aOH9bYX41)TWd zF_krJE8^A6(x}FRMRa6N-}=>kII#flzu}o@FP910{(L&2+H4Ql_uU1nxq$H4Xv}cj*MJ7VF0| z&i3w{;2&RmyhtiZbKi~zMcjm84frvO!v_Uv=1a8_6c!YB=gCjfq5da>3vly)8Y}W) zw}7w*Bz05YQDOUN+vkXw5!bDTmPPk&#h24y5#b^%!Ob z``l9r2PC6yM60-O0)MI`NrLcg;1_H%N6MtBzo*dUsi_7>5BHcP;qV?YH$FCx4wEDh zF%u@kxJ^m&Hne}etwVIY*j)S!vx}7PQGS^1&f{T2cCzBPEFZ}PCA>PyxRS)TqkyEE zMa9R(X5>|8G#hKQjBnv^Gvl_tSgN$VaG+gqn=xlAslRT%e&*{Zo*>s;|N~Vdrwboc8I5Jkd)z&)-}^7DFti zKLe`L8EEBBN5sP#kfyS3wNrI&#ZH+NSOKZ^vWDyNpiApuP{`yUbAw_P{RRG*(e7EU z_eBrgdTU7`|NK)T=)aKM@YSY`?B^Ge`={VB*#aP-{@X(WCi1btB*Fjy!M_zxI3*rWHOiMg}Q$2-h54uv+6u<<)_W)?7lHe4*UgNaWR*N}emJmcj$~WrKnQ5#Rd^_cYTj=zZ zj+)g658TiWeWVn_mWf}5K1BzEW|D=~NY3}v>sq2yOv;ygN@VY%Vasfd+^AQQ3fffF z!SR{R(8qDnS_$WDijjfZtidtiP%{8Z{C;ZW(^EzOB@jcKS=n-H-f(PZOdD){*($`6 znp5YzRF`gdq-}3MT;fCtjIV5E9t03`=zwt;gCsiqBbb1fFd4lYfq~3#d2C`$SNk}b z5EPfaiOGP=rdMVSvq;RDP}c&lqY zC(+NLdU%jyNoc|e164%R*m|NRE zbG(*XvXB#$<*-u>7cJXF%k=PmI^VwJWY*mkZo2BuTz&@B_{-hvqbo5isrQ<8FKD*8 zPYxbj&R(@ukUXtD4IV0bo6za5D*j;tcqpadz-4sR89w(_YnwX?E#bE{Ha|37NbwK( z!ea6Vn^CSJmTNkc=&NmrUEnqC!o*(=`arWvlaGee``b3bxFS!J#PG%|WPGj{6fW|2 zQR|EnERJbSHjaCbbGjO?NQfH@OLv;6_sRGzNKw@{?Va{m;-aZ62tZoFib3EID2}*U zv@1y1Ljo5;f4PVYx!${PTo?Z|7*xh^%!23fJBjw!GOp)4VyKhpwr9O{q$vH1_`GHH zV31S^DQ2W*rFfa=_OzV=9)t;L3~up1v)q&UlAIt*pIdiHPB-^{AAYtN*~}x*V@|o) zQCb_&CL=OdMS(s1?Sn&4tY0U5w_~HcY11!?lEYO#6|I_3|FcoMZk{Iox>LZEQ(`jo z3}VawY#v=KGb>TbG!a_rrN@ z^XdG>#EZwIh`QKwnXntLmwNUz!l2ULCmFsfl-RmUQZ^&sWg?ArWH$B1<_Wlkf2k^8F|%d)`*`tZR8OFjc8+xD4)@i=uGc*@@Zlc= z1s57(3lz^^%u`&eNxRO9bpt#6pf>P_(`@H8$Fv)@Zs-dxEUU7^VP3qyyFuSw(GQJY zOt8C!y|5f9r@2);=z;cGr}Ih-TYHCn?rHt?F^T8G@KrXB-OrggbzCqLwNx&P`K zVps?>Xn-CTZ1B4OzTYqrIdB<^%O~CDIf_l~+$b zsi6TSSua|_E6x^hjfe{SyaS7k7LG-VMoGkwks^G{1Q?|&6d#QNV{$uEmk($>OZy7) zQ*avTK>DNzht_kF3qx;KFgV3R(0@B2^191RB8a0dv*g*;8K3u5-QJe#Rc^J* zFC#BSt_7k1gPhY9ow*LZpz?j#P2|ICCHYGGAb}uHa{b(!uUc&sF*qUbh{09n*moELmOFj$h4ZJ`bCh zv6n}$6`RkS$wy9!pflXVDA#%@{PC|JsNT2hLJfb1y!3ab&AQmGZ3!G{KE%te7>3li zc;IGh@HgCHcN0G+3%K^XHm};cxcMX0*jW6>$zvap*r0RiBP_mt3r$M-XwN@XH)R~2 z78#-@pC{RQf3Vjnn{C?H^us8hE+>+vL}676ktg6praW}lcz-qB&d@98 zZR*HldrwAlIn56N8iQn z<&EZ0Kvf7FKzDUw6f*QA z#lpP2oB(?8F|Stnivnt)Uj<3%;fi{y^CIYM7!%nYOB{6ZU|nQ|X>_AI#bQ?ije0R9 z?~-ff4_qda_|%u@ncJ6Fa>yuBM{vutQLp_y)1BYB#roDuURJBUoN@v2es!DjJuPpc zw3b)>z?Li6sc5oe7VUe7h28+yxcQp@+I5W49S-wf)CgL$Y|pE;pc%duq-aI8Dc$|8 z^iOenRn1%U)9)p__hDnL)jEYnL!kH0^=^Dm7qESWQxj%>O)YXV`K!O$zu0YP+v?7( z?_3WQG1d3()>@!wR_pUI&U~n+>ruc%f_P8=x^fj&e^RiGMyVI+_w`?sD}byxiwy>{ z2g%;VfpR^zN>54Fg$Xbnd_&YD0(FUP+ogB3@X$F9bd1?d zXB|DM9|>#aP{X&?wq1GgptP^jyuU*F!zgy+%`bn@_qrR9%mQIXY+ZoajSUkdqrcfxo^*YG|mi@x96+qp(AWi8}!ryWVX*`ck^bJrO|w3smb~o zJi9&O<^~ zugt07eYvB?`Ts8KQDnT@AsmEIDNVyQzGO41Uo7eoajzsr6geiNz7$ID+hj1B(kwrO zQNpW5vSqm4Zb4pgV1%aOn=Fn+0{1o?qMeb1OJ7)3Ki-}|^WLw0Fwf~cFqP3X5@swJ zF5y*!yaoM1y^JwcEv$7p@Kg?vg=0z33Pf@mWpx0l^xaIYlF6p`&@idMEg3<{<}07D zvFG8f`8MoPMcAQPI`!j?cRj9+z^X?&4>-oMVA6{jsRD$W4^pnN%p4?$`?*J6B5=(xqy}hi`o*zsEz=R9oECvcYCiO=_84hz zSyV^O${I|`%=x>}`?4DT+PU}}pGHPt*Xt}8H|K#%P99nXR7dLZAIbb=hpg=s!=;17 zeE``=%37xHbvdWxSIrg+ncDK9XwXVFhLx7mzCkOC{J!9D=YttoZQa5LI&8SlHWwn3 z9J_n@LIlZV#R%_Pk4?;rD5XQz@gA34_ zcRNXk1m?vg2&VU}m715 zezy8>folilG{fCUhTDWVztH<->WaP0z|{6^d39 zwtYxlXr9LL%wO7NjZNsDu7NVmG7M2uwHKbA1>T2R2uAndp+3kx>_)a;JACbZ1v~l1oTK4(a2)cU zjZ|*Fs+bM30~dlT&U;GpdZ{gol#$f$7B>9jS-+e?eu#F#I!`q-l2CB|12I-FOn5BL z@A9U2I*_3kTA}&TW!3B>ezr${~t4`N$|*isN1u;A9Yhsh&# zzp>Rag_o7^@wQL0(F(bM9N+XKR0pF&8gAPoeww{0m@)087iB)vW_y>|hV{>^UjplT zwOi9GUtspc<(gl`e+|49y5HA~`yK{JU!pd_K(0J^Q((Xby%895(SOGe559P7x02@9 zEe)>v_6+$*%E%QXJkN^NK-f(}D2%(lCT`RL)nTxLe)uA6E&=H#JYn^5WX|&9h-XB>ylA}+Exf#1st+E@#)gmQY$OMy%XDw%oOZA6T-9O|N)-)@2QyzHx1^3NlIuH!)Xo_7H#S$}@*e*qIcV4aL!A#K zer+J}z=~JvRGFGQw(cr>%=_hMh85gQ2-45QPJ`tTN#LAaR|Qip2*0A{@%m!jpdFh1 zLCS1z$zDYpI`(S43lYEWFc0T6sq)BftKo{omn0Ay;w;+C8~y3GPJ-Fv>$F=}@-ZK9 za|+G47ZQhNhyr0XsjvhnZKH`Ic}`yTiE+0(HxVB!For9DX*jnGR<@a3=45`gyhM>< z)AJ*-Xe#z~cdyHAq>GA^u*?KW>K8PCikNcB$3JYo_)Lj!Y`g z|FBHZj=hu!4F;CEF(Y?sXPIUnbm*}^-e!2&Ge*1PvqOww*pj(=di^^az@e4O*L!C2 zoXEVc-z;8x8IGT8y3sO0d%2h9Km3=hN&X#CYIIRc(LWy`M~-0L?2@Q{V7A7Kl=6|k z!C~1SaCr2N^?M*W0(1M#iuyOQ#)8}F99e?4M$4tAf;Zp70ySBe_xy8{jVfpKsR|6T z#8~aS@g-yK7Q()tl%yvlslDvbOR7|@a&zA@eXsAMdT!7mdK~3WIYhbKY1Ww`-{V#L zTwy9>5Mw-$E~#ya3+?SqKL`e0dH}K71B`*)*e}kL@A+6R_@UJuCmU5S7f`1-(_~yOW*w zl(ZVCzO0%24ni^vhcD&Mr&>hnE-&{xm*fO{;vv78J*Wtv9(IYZ{$xYb`vW^GBAc4IJT{Ec-g4uU*jHztl&V;k6SusPp;xmU5iW%S&o%+!vv)6$eV@~%wl9i}14RL5DX=x45jSC$|D`~H99=R zeb{%vsaC^4FJb9(M=*>7?N<7^UQrkq(mYQQi1q3i(suzyl|5bkVi>f>H1tM+rH7M* zoaAPx4Ie5=+0ol8(>)Bc7jk~JR#LOJEtxtS089%B=LCm~&YvD#zp|WN;Q!tF;h_2y zcfq`wtdabD7PcWqhpAs0Do?L*YG~XLE{eDOzCO^WK(pJ>apc{n0C^uis<;*!$Ifp~D7z=od`!y72lym{vf)2$daKaO6SZ69rr-HSgeY)}I zJb#Djp=hQjbSU%`|EzYU5m+1@BvX9zn6bR&p15b3jMzXzjq{AN>~4t8iTHp&x!g53 z+Ibk9d+2h%`p%m!y#6|-2-0oR>+THke(`{V>cR%Uno~bf)aYpS5C7kz$0utGvMur| zz22zg=yQMi?2lO!Z)SY%$`=s^P0j6(U2nF?Jq3s^p1ovnYmou~2-G<P8H2s41O46j{Afy6pa;dsvR~`95;UuHVSF0N+1Xd) zO!>A0>1-O$3&DewN#Y_BAy-$Ad9kq_Rrf;~Rmk0uAMug+Z&|0Lyg?16r)@#=Rh{ZW z9t**nLI?t^l@tt=OK~CP-Iw9eHLMh&zQhpP7H-$BqnJ(=6f>zZ`B)qL9 z@aXX|Ccd#APO7@%hSQ}`=ZA!e`m47?OQW;nZh-lllsw(bVj zs>$vP3_}V5rU3FwXK8z3;pGt?3%&60iAP87`NHr3;Oe~5@ZtZ0liUEDaPJSSaq`WpWI56825S#}bs!8}@aBJw?9VrYyTPEVwCIP_0D6Z)%ChAt4Qs{z0DL{S}_lg1mu zTm_4oc%C)>RaI*JIk46oyxr{rw<3tOs2{Xy z^!9S&{-j(Mm^N;W^=5L^R1%a|$$0gkx;+vc( zFF3^m)i0QnIzBlO&vDjq0Vs2x9?vH0kNOoIW1%_~Z!B0CpgES>qzuq-anLUemobwK z<4jtlsEFeKK#|Y_7PRciP;Ilt{!OB4HNdi2yIMe(TlUrUUV@0_S%1($t{=8-hyKT| z0V6sT*7yA?z|eqD>HvM(!EEu?>&KV{^KAG2ZzG_uUl#4cm43Q^;h}@fR|WOT4jt6c zViw(eoF^==scdgMHE=!6FW)ISJuGLs&HRT4U+UWFhl>b2NX^j6|V02X5w~VKp@k9N5AEhlY z^RHF9Idzym&MGtQ%+}BQlg!0SAjkGfU9ZIdlnTa7fFrUA7Fqc`EPFIC{#%iPZS(g# zhd7Yt)O0gLi&Gbm)7v|r$c~Yv`}ftOe|Ya7>kE5?uGz!;^#P*S%xmgPU<6%mIT4~x z2JNx$4Z|M2;=jx;?-16xF7`R!68FX$huP_i&%UUJz5Lq0BP?eMXR8$1!j6PhTNM38 zo}&c+lXI)jIr{g}ChY|)y)(MWYn7o>=^o=R z@d1T9xMC7QfD=-Zx9Pq;mOueyIeH_X7Q?+L8!n(4qAl!Cejo#&tX6j}c?r5r>%(2e z`NP4uv?bqztbaqVKL||Z$)9UmX++kN(h*aT(4X6`N!FU4p`zhl!`ky+QV16`@QL^P zU9|CCKvz=4T01B1RoLeNogxI#!@)B~($aP?Z!YzRuwKzX1x_X?Z^J!|+P$ZdZ+q+| zX64Z2Mr}Qx2%w3jOCex6V7m|oyPO}=3W1J&@#7GQD(lJg`;%?I8s@K{f!x9`4Thil zm-`O>{dsy%ff7gC(Ge_5Oo~IoXli2fAuX30P`ojCea*n@Y=hJjBl;^d!ei%EkfZ4{ zFsPIOReC94idad&q0 zLQK}o5<_7*-nYR#B#2JMNDC+~iq#I__N&Qc)tZz5Bw%!tOz)0ogJaw`>FI36zX&|Q zBChsl*A4@5XwN>A9B|W7j&M!2<*abG{kqq?KI{Bd5HO?WUy#jlE~K+I%kS}y?-D-b zHZ}^|8~~bq;4%nCjYWz$ClL=}^kyt3Y#tB3HW_sT(h9!ARz2I_1}@7NP-bG5v+w2@ z$`p(;Vs)Rvo)|}sCO)gF(|NU!H1BupVHN-6ruXmkl~d!2iT6RkL6aBerX+yAddS4l zt2(H`fB7w@Sj|;9;O0ToKUg2 z`6nxNWK0>2L9C)uO*%^vAh%xMzW049nm5}VNAg+gZOss70Q1k)sbB%rdEehg zi+^^j5lf?86Wf1fOp=teghdJ7O%NX&m#*(txHql&7LYhI>qdIyo~#or(zrH1I|Gli ze4yI50d5S>*SYO0710%+II58T>ulSW{LQ_=>Qs2AnW124;aNxHz5 zDJ@mwt_GmN4M&S0qc9W)p=>AZ3U@2GEWMy(s~T>^Y4)lIuog&vDJXeUpbAV;ycbfS zn}ItCi^0JzpA#%=KH98ifCAg0Lp}-fS+FK*QMIlb+-atFStIzNnc6Lbe8w75_@Kqw z3#3s2m~2&C17iO^9RKGxkn78b#Ff(zTAl_6p|VKCKdsHHIaggD^ z4&#Wgn`DINJgW?wl5CPm^U+;nAIFeqH zW_uE(9Ni#9Y!5Dh8Q<0@b4t~gRE*rzpr_YfvZ$^TF1s?+$Ag%<>k|P|>pVP2vJ|Nq z8-}V?g5^pQm_ac}5&~M9{HTl%3A`p17Jh~^h=^AToaQ~er)gcEqLj*vxA|i0=dWv# zL&!hMjsHU;0ItH{fUSEeK;-~kDNjJ<=o|UD)>&xFSgQIp?*oQZM)(hr8MUxwm3Ys& zv<%NCL*yZzCm)@MZG9lU+!e<3K>np6Eu7m|X8~%VL={-jp;v z-ylI;M2WL9KzYAdN5t9w3craU>!qwVwvHjxOf{L>StAvecBkH;(ZGQ+w4y!nM1`L* zp3b?$7slxuu_?@PNW6ZE=&Ypt;>-N+8~?@FWTg*;8C6-1&s=GV1@6)Cvq;2eSK<0u zu_&|ZEZY|+y(N9grFwSA>Hg zc{%yXj3{SqDkFdZ2AYt?xG_P4rC@L@Wyx~1B95e&`vZCV<+|XkyDI3eR5L0ExGM^U#2N*0D$b^G`gmALPDgs)Y^MiWB&l*R>k z2H_u_Q#D1;)Q$0N?Zc(nuVieNoHDmpUsZ8^ee%Cu9l$6P%@7VEs2zDf+DOFkStp%lRBXAN4%D^9eir(m@v^VP{VRl{oP{5Hc;joJ zXvDs!2b4C#eb-7XBNKVxzgRRPozWd&JP#aKdSjE@9T@_u96|{C+gP+gRED@xYa!WJLufJwpr$2 zvn|}7qC7;ZE|-vw&g16Uoqs5dzv!mvKQ%(7KqOInID6^OT${F=fX|8{^hpx$SJQ%# zPi6~o#6Ctp-Se0kpqJ(6R*~scM}?jy?`-+LkNdR^>sjzHmv0bZ@`6CCQ2Fc%fb;%H zu*%}T1rH4-2K8;961Jp^UW7*AXQy)uvjY>Oto6{Fg6+vyZM6cAX?tIPYwf~=)Nt?o znsolE`y6{OprF4a^=Jf~)%-+}wU_Ln3PwP^`DEWp*oP!wb6EM*^S_@U62(7#Qwu-$ zb+WNHH4?L(~Nf??eneqPb^?6ox7p6QL-VD zYv-2xsG$vk0=o#1rnO*Ekq^F`rglWNhmkf>RwSZxcjBfJAb7>$c;1?)8LFq@{5J8k;SWzk_zpj9tvi4jKb zlQRKP;`}MSLO{6DA`gtfj@ewk$8vkhSB3HRa$ie_-;^Bqv;j=)_oWMaS;kP1fZ5+e0#6ESf)T5l8}Ig50YU%&=mC=Wz>b_I{%Z z2htaO_j_f!0B`-fXSKqc@Iq};J7YXBF5{_8DG@l`bE$bLM$mskc-;c`)ttFs%phhe zY{(Ujxl8iEKK5o_X6oROLImi-54-pjPs!TUQ@gB)Zhgrkq4%r8|NeQn`mO(xGV|oA zbIcUH@4wUPqWJMP+H%I(X`qD0`zMRKh6eaSh75{TeDJ8dUKa<`cC$FSMRN*U%-9|( zj-kCz#Y=_lWH${>mkgH-mjX7ZJ!^q&;hC~J;2Gqd3=yq{W6@^_#+!(&y^2=w$@k#z zkJ7j|@}3@zGq0Y|Q>>QYW7Z$40HqW# z;-Ug@;e-qs00GU7#J$b{C2YFajSmq52LqEC7J~?&`3?-*FYDhG-K?^k9B&NgGeUpo z)|!$y?5>S-o zHZL5{|Q>M{*e%?KMvZlW@EnZ!%kTN(-2IqKYi!HV#iP;hEH4z_im2zX(}cJ-iL zBT|-g^Wm*kmQFRv4@nLQP7q=@Z+`wayA+USya8MUFS?C=cbVRXW-Wo;L+Pt_<^CYcaZoQes@gfpcDHw6m%V~HhBM!TTRhwq?zEelqNXB^p8}H}5W92|2#OalU6oMJrBsbyI+x_q zjnsV=s3Wom0;!L`;d3<($)tswVCvgKwVhm9H#eX27_QXBcoStWaD5GAO?@T;_$^o9K{~wtop|d zFz`~^6(8T4f2d~zxaQlH@sdaOge^V`yfTK?5WLZ~?^F+_m|2>L{2||yqlbRBmU)Ezgd6A6Ma_;pCxE$re12b!0(6@l|DjS?p;YZ zk3fVP1V}xPQI7+bLrd_j;px0!O_=)Z{}N>jH==U#d13ymx~L zlR&@srHla8;qoLLD)KPLHr!R)0v$B~)K(i3z0X3li~WxWt|V(V1xP)Xy?LF3IDT>) z?|Y_%t#N5XQIhv@`n(#T9L7!rx#&Cp7mxJMwdyH~n8CrhVWXy`pjY;1=Zszp&ln+{ zo7WXbN1Jr4Ii(#;nl`h44{mpfm!nna8D!X3*lOM{-wGZglw)j%D<3S;knyv>r;?8> z?Im?LLr`+A<3eg4^o$P3k-YG}&0GF6gW|>3gHqb%xA`B;}x$ z+P#(RwUWP!v^M@Z^UW^WG1cm?4$5E1mBcValmaoyaG$m|oe15!xVb$|{YBts#7o(Jt+g;l(UiA+*+&}a0wE1!u z_R99+(nnEk0xJABx{+J`?7uAn>mw+^e(vNi`q>Q<1s)<5>y|@4k*pvNlb4c1ZI^Y<+Ya*)=to0ys>(^WoY^fI0h-9#>Sq3Ptc8!OOcbuz> z887pVo_BeI<%AD!w6l8o*Z?NC})6Vag!cU-c?;s{cEA zhkW_ann4VBcZt}Qj1i9gZ4}{G5?kX^MILnWz1q)CwUU;7i3T)y0bXlOdI2PLhRTd5 z_O1i%M~vZdoBnR+Qv^MtQc_E$YSn9!|1|B#)_S*hk8?q*Jrj*tgf;^@%eN7{-KWdD zm70_^u3K`dAC}x0**+P4z(=ue5aDJ&!-KdE;X&(i?k#8^$Vc{uuPT5xH8oaal3{Ax zNZ!TbFg6|$Na80tYZVcY0_pLuy<+QhoIKcTmlAoOQMZB{y4uR>~^ z68XznzQ@YM9%j6G%h3$buEexF{13tq$&QLCe%fCzWXy+hxbKEV(6lO-0FFBAeTK2+n6Z2Rb{JdNO@EbNBi!1$nw3rAnFUDej%_pRB!apmWK!|MN} z5>7~e-X(x1zv&dAi}m9DP^e zkKK_|#OVG6kXc<{9H&s#_OHpC$ZEq@(4sc+SFZ2&J4}q+AYkRyDsg%v7w9|G#(s3o13&F~3=3EJ$3v zEj|uu!K_vh_Ss8JkSMB*eWB2T^>O3}ck2xv;Q7{aLx)d}5cE`B_)?e(GXBazwEOqo z<2NcgoxXIu%4^HcBOriCm}K` z5JfJto~l%(!#dP$!)xWw5(>ri&M2@}fTDv;RPqWo$$q2zqS1iY3KzD%JDV8nc*n-0^L$dGs%@be9$Ln5791_TzoOActT0(oHI;1%G?V?w+w zN`WxBlYKi)(Zka7LwHk$yJX0z*kv(mQML2`f?8wsEwqs7aXqu=c^E=*>I#cuO1e#( zb4Z%`X}gTTJxL`%rh58o(q0{OlVAJ%OUOYODH3Rwr-+e0(7;7F?7oN$-3z>h7yWMB z?NknM8ru@zYG_S$Hg~D@Jh)lk$8n4uTnYG2hg!wzJVb5#j#{d6dL-4-ZxWgQSJTyU zg`G(xK@u3t2arsjJNyWy3p$Q!Bk3zhwB}Tn0Yn@x=$_$vdC&Lo;%Q*d-lW)|!5Y9#$;9ey*Wb~b2t`z)=_!LHoSku26lj#IvRIWVKTwux8_W*Plj*iu0|CKH7H2W+JWP0v(X z>+vG%jglH0Vs3M%mUKA`fyR!Jt$&)AKAEvBupUB>q}T`mE=J)9}HCV}u&++&NyBvy!T=OS>i@cg=nX4J&ayVVcD(4y@h35s_6o zLEWnwB9?-68*>b)VNW|Gs)i{2TDO1_no(Rcd~N?0pxH&28@nRe6q#nzoUPvVaTItm z<{{)e;)D%TMme0*QFn0EEbrJ*^}x`P)B)Y;i+pQzz_784nqD41-+JTMFGG>sV-Zr) zzz>wHKXq09t4~_jI=O*0m;=KA_~f(0#j#Jvc6U4a{){ZeAH7Jokp3yt2G~^d@@_^l zcJNIve%es=;X#%{#nfEw%F4hj@)8m8`(>!QASO^y_}aoKGDLU3&$lXB}u z3IuGx@%%M$J+EHYl~l2h!8PI8XfjFBVQ0j9SYz1Pi?>5q9!?ZcN}f=HH7HPcL1KN? z@P2?2dUH9M}n84 zibog1o=%bD3`jUAIc@`-3u&=j!k}Bc9{H$!wWV(@m2%29Pf|uT-qVME%~`#9fZYy` zQ?$kN#${QqXrkJW*$8v64+WTuDs4MWxP0}l`n32y#Et@nyrhPDL{xRfG-y00^Gh-J zgbAZEWdHoZR<|^JH2ngVS6Uo4gH-YYkO;wUM{%0+ym|BZ8(j_O^RkobOh&2x+~J#9H?H5Q!6Fg z{P&9XUv9{u3UJpM_P*-cL<=Z_wF8z~o=bhE zn26(#z2JB4ezmh>pK{M{ygI#g%8i%#;0jpqif|RIP@>$PS1(_^e4R4<^^e@n?L{=b zpUY-_rn~F*k0ng)BT{0Haj9ySql!3l7hO+S3k%0G3Ku@Ub7h5Xgrl?JuH!z=+-%E2 zePLuxNr`wp4+H(7j}nWli|=L#EyFYA*2ZI&xv55c59v+sIr*wlEp6?mUNT8jGiJt% zYczYtCV_z>+%~4TmDUlCuEraV<>?=rw9Oo?m0DGEbv3_yapyOr-OqM>>sDFa@ytdy zP?Y4BLgY<>9Ebq3JoC?)gm%jU#`Lp;m<11wsc1tUSh=qZ4T) zgp4-WRVJaEn^YGCzpplZp~+qeh9hcABd6FE$u4+@)$TjbUJeS%Z*k5^-KR}>Y+w7_ z6lFnJSlX}X{wV(ToZQEVx@bmiH(n0SC1iT(-_TM3zdv(5ZFn*j;?N~(K_iHtfuV_7 zg8c8>7s@nchN|GwYWL`ncs6H8X2Ut(s;Rc6dZIrsKF{{0SX}tL4G2IJI1cl5XY7f+ zCECnyYx2Hy^1dZ>a=-Oo@sVv`w8{_>Dga};-bd@`&mk1F+A+n$#lv6HYs|K?9qyZ` z+`gOkg5F~VbM7 z5}h_(!f!D9pGW)uJZZvfR9qZ{sF?TDlin~wMIxgnQ3<|}Z_|fFL&y92IL#KK=)1?S z`UDqPDxdR08e$^|Dj+u)-YwD5Lenq*oO*azJweeBOAuB0LJTgPl~_S3)k1n@yw7dG zP!XP*m|pM2us%IIi#n38NTG|RhvoiOD5s9upxgA_w6207o!ou!-?GF8NC?y6cENKk7Xx5Iu^6@!)L~LwWSD`R+FU|n_bI0rJ zeVPax+QL@tPh&MQM@@oC#YIozORu&_ZEeCkzZV6!gD@W});g*6{sj?ON~|hBgrHl? zevrA+b2w6!0fnh%jQ9`ioeCR~`diR>z8zk^ohXWUp^JqZ;ohKI{Q{qdg_|o{08$`U z&kq(JPATaAluO{9_|K+03MgEtR#X=W76o^HE2zGioZ04&Dzy6+` zn0PKWq|YcS-+mH_-hd0)jl@X@`aJFf&w9uGG01$Ccz6P>!tX9tXqosB-B1pd>#pp3 z-upJlIURjJrZq)zb+we7^vBcnZV%DQy8*_Q*#!!KJe{mc;F>GH^Lf+F>4-osJ{=n@*h84^} z-{*c%;X>oyx?l|@=!P0>Bjxwipa?<85OKhWF_MZ6+4HEw{vKX3kDpII=3x&^&M&qP zA1W0uwFZ-dcU%y?VRWfzGJ1C3LtTURCy=8`<%WD#PL<{srE4>kF)J3=I()n5Hk8jV z4t#tbHlt3x7e2l&C-9&&4e%PMl7E=!?7d`@1AD$&IMv_(ZhnnR*Y%9n5;%NS81msRl7_^4xvAwr6+S_80a8&%u*F5s`06R z(&vC_;y^0Fd#I~I$>aY0$rzlzsFi}YADP>$@yXa1)TafkutarS`i9aKR4IjFwn$+Z z`XZVnUeH*ElvrGrl0MIvl0GknTuEc5Z}zTcziG_HGa{R^Y=y+)o{DHNCK9GeMGYA9 zOpV9}iC!R(F30^q|5vR+2ozH^L~IOO6;_L#4c5^y7g#&d{;n(O%y z$ZPz4Bbx#{uI<+~{txN$d6Qj0s|RE>f)BYO@jMp`{x;Pd^zbBLk#PO6`OVn*=%9&7 z?GFdw!r7eblG-tilkf0r%t<#78b6$G7kR{HW02j?#$AR-F>=_q|5=|pn$Zt>y^huI z(|J(7>S1civrc-IGkUz@k?Ijpn&u(TjSMMk^_*z!bC&tT`Emod^IcE-=}qjG&XRnyvDs;`BgW3eEj&+Mv5j+j<=n=(YC!*iJ$~VekD#<3y{LYiCkr4M}0@hID<( z?Mtrwsg9zl!26ih_~VU=#-LSv7V&KIfAAoo0plwT;=}06ritXTeHE4d7aLK7F*1t?)+o=(_0Ha-S}^>S7JETO$lP3 zm{}V|fTNJ2UX(KQ>X5AYmg$lX8;-H(v)7ElYZB+xD?CNZ$x)g85xpB%d4f@1kr56f z-D4%+Adw;pP{kZ-0`qAHG%*}^JcOn`wTRhaXauy$cv2nn@F6swWo_5``Kjl61*9a; z)n9!IOoBLZ8ZfiKi_{rW%a{dDm4ctN2Pz7h&*TCeB4d|E%TF1<|WQ7_>ym3U4ai$YK6h49!3W762>8EgN=2YCkf z7;2Kbz34&+iJMnW8X!pWwyjTXsI9#;YeQ4Ur+=k5uzC z609dYC>$cD%?1ZoYs0z1HW=cwK&X8FK>h8FmMHE*qCvi0{cj2>;w9#3Vn=$U9IQXA z+C1-)2c9Oqml<(-Jqp=G1Nm}H8-P-H9lGeC$!H+CNN5yDd%2Pu3-W9bC7q{-?iI$< z5|eNI`VfhwMn;EGu_qghrqg@W0 z`C7}3mq%?AroxA+)j+jY4NCV0G}zjGeC6riUbMGknlQdC(7~MQzTp(_zoM$3Jyqa; zaYgy!Q)6SdmXnRmuk#CfH(>Xmr*$D#jo7Gga(evD4Mt;~WAnaIOAr2&gxwWc>BN=> zH?VHU*vwA|NyMCj4=x2pk9l=~*nkN$=-_BY*CXT=2C``jq}ljq^{av9D4{ZzoUqF; z1p9WGNDMSlrysW1oduH0T?VX(+DhbIzAh*9pr{uItInSw&j|u0s$&Sl1i4*dC0f?0;{0wu1e|jg2#52RZ ztH7?~+<=G{_@!}F06Pu+qXn!z`1xISvG)hz)}yV${g$C?o0O0eUH@4m3R{h z5cU4}@A;ty=t-@Fln|E#tT#wg`tf6VC+YFcys5m$?U{-0(j?8l(jgxW|CJCd^ff_9 zRNp|f)utwLP9a+mK>>ltGx(h47ltQAn5rK8nY$T&De`snwtwJqiVSq8Jl)TcwU^-C z$?15$c#26@lTA{10S6n~Xh-^q5d^nmb*fd?U^+Er?F1Gurk+$HSy>^2E7vjxGU#f# z7gIShm4MfamC3`Vc!Tro(M5V{^y9=vdx;lx8VyFwLyrn_c?|tgdE0gSa+#L`Bv(28 zYR%24)-baj0{|wMEB?K*_nTqLT5hLob8E#vv+kle$WTC|CpDM5$>WfHLOEU;K9{oq zjLU$XoVoSD;eiOsz}^b8S~Ks?hlTIU-vO`8dF6)d(Myhm+4NxKEnK^rlm74{9D&E} z`8diOn@+{WYcsi|9R}}TLLZdf&1TO}ww&fFosR%#r;G~?`9V0Y)Z^awtp|TsZq}EKt7nXXAC6)GZ zNs1AMwpZzHTI6&W907iNUoe-)=^0xz_*=*MFzKu<6MmXlpV4Sbp4J{^6h6-=@y{Ji z|B)Va4|%)$;mc!TycDx`>6la+4 zKIqK%+y5S*eUOuh6P@2_-jH)oI#|3vCGdF+lxVHTq+cHg?Q2w$CvX|cEj%FS7I1iU z_?F6=Ne0ubN)%||b@HtEU*h}lTU^ADJFb7hlB9~_g+#;<+BuN5U>a&i`vgAVn!95% zdNetm$$5NpVrxE{Gxwj&2|QAES#J3_C8u$hebyR0nx-`$nY44)_dJhz5Xs^R$6`}m1m_l^x&eB92#%Y*r? z+<@N?4i;}qE?FpH1+hY=AM0VC*s*2KGe!jSMVbG!`>d8lS^4!#0M7AzK12`q6bf|} z|H<*9VlQUED$7ZG`=3tYHCP@%t?J)A`aq@os*M4=iC3{Wp-}R-;2I;Yu~3^BkE64C zc9-;`3ZyaEvif_bK?54m^h!58 z2BWf*7Dm6Taq3QAd;e=JJ~>HwKALf<%Tbr*8zo#m6<(U7*2@wHLz^qg%l*nc6i}z{ zfs4=fym?Fon+JaO@m4i1g@E}pnyaFH6AxY^M%X(ZMp*O7sD6J{5fMxcV|pC$ZRCSI zpr;QCfQH2)7Q->*gAXddP2k=}=;6cthA+McUE}&(B@U9KfpT(8kDl}i!9DfLh(4T3 z-X~CD>==#4a?#S0tN-|6t6NTE@i5MucUrpK;+KmCt9tcvM(b6uKXzo zKS9_$apD)tRa{`5mr-Ng;1qVxJ}j`=rsJ44Xzo-xKBDRxrW8-XQ|1eg%PNUK8u={jJ)vHT2Iqg&l==+#HEPbyJ1ylSTjTw= zNIAVP%XI&ouhUh(dvAke zqeyGC<69QyY_KyZ-M}Vvq_^mI^gM_?FWNU6tiW5HJ&jJ@L=nb?FaW zd0P%6=WE-5RDfWuY7gujX>(CK#f0`_dMD2~vEbH(QMV`mK>8()=P0N!tisp#m zh;J`lJ5F_0qI)O>QLYs zZQO;MD`TVe^rSDDN;me(+8OGpc2OysJpWQmmG8M=FTVqoy_lj_n&J)*HY~xN-<#Rr z@bA*Tc~6y_fCdUs)5|%v-QF-=?B}!UkC~wskfYmgFz%4R_dY?7D|r}BXT4Z$_m64< z2qx|O?y%dL8TLgDl?4EpX$OSj;O-PlqdXer1=xAW3LN+l=fAEIJoK0db#c$a_UhVs z+E{CdAgU5<&AOO$cd&fg*!;T6Wa{?wVD7u$$knVr4|RZ(5R|JzS>n7X9&6k6seWIC zSJ_v__*wCZNFES^CzHnn<67lK4;>6kSzi1B-o9~Lww19~BPRM~;^=m;Pb`6UR5#b(s52glS35>QszTfi5j@#**f* z9`bX;!7O!~d!>U~pZ;>Cp*mtO`T+y`JJtQ=7pqTTk3F1qZ{dyo|_&XrntcY_|77@Kg~q3BRLZkgEJ0l=BMQg zGT7;n-iPnFkL5#$F_n72DS}k|%pVGuaPJRt7BI^f*?e~+2LVV{Bu}PEb{I8-+*;%_ z0^jHhG*6guW<5qvdMoBpEBf}W%7v9lCJBJ6nBoyFCAkm(>_AMzfp% zbt+zm`;3pU*&U+gWSh1#(fmM)ntAN&%>ELkWnzJ~XK+LMgd&*3fnvh^H)Dhs5}#&W z-5UBvQSyYh3f(pKG8?}tUIv)WeidGD{iWGn~Y$zPh@;0ad;8Lcm2wun^ z8ME8K%cT@#$E#+^jWOIldX3vJv1u;4zdw|JkNHwL><1sM z%~Z8|V%>VaSQJG|?V&;0ED=EkLM)7Gh>>~%^lJnfyj44S-SyzD#uwja-!^ONgB zHtZAi;-!6psDkrIFc|%p)Ts9V#8o%H#6naN=0*D-cQE(Lrcnr*U#-sxe|w*_nkAE7 zoN}88%uT^HTd)rq6u$%8TRsBNo@ z-4ILsU6J(0H(VRWUHXbi8<5-HO@S7gbCIYd*>f zrs!p2Md@8o;YE)9pM@RDBD`29Y;6)_9&;;)RN z5TQBd*!>C=j%^M8NS-zQLk`aZC>2HT_r|V0k%#Hp*c|{eg+xprE#_BNNw->nz{ajAtHk#x4&#ctmB^yivtH z)b;@{#joRJW6~YaHJ7M^wgCZd(z`jYQ>oUw->aiYw&5l49U)p+<_}+o;8oUcEar=n z#?saw2(fZrbS8vU1Z(Jrmd4YE?a>b@CYQAnG6yU3e~f0i6w67C2v%}KZh+i~#V?6$ zT7A+UfAKTj7=PF>ED^jyykB#(K%9)%U1#i|L_VX8M~xf zDl2IE;O_X@MZ0}YYS2UC7N#4#`7QIKRY6qna~iUfI!<@K3+ zRvJVwuGXq5xQVsN2_p^(TkT|n5(E?GJ+%6M9c2R6*h^*D$MNewDdqJPEg82$2W<#u z?H#TnS5=|@W=RA#X@w0X2-gwLjGmq*zB(SYqUQ~0W`Px`$y6|7=^Dh~*A1uBbS#Aj zPfpksc%Bx|4Zy-@xqOx$zEe|R4BvKj)r-EKQ}ih~5r6|uu@wRQioBG3;Z)ZsydqW3 z86UToeaw6&WbPAk%~E*LATczodt?|gS`W$+*BZecw|(&OLGwy6yXQZJpIlR(2MEy8 z#ZqXkHr>SkO3`u2FFYe-Q3^?~dkO+rP?7htJGYLYodqmOYNJ`!&~X6JzviWZm`0ee z^w_5!sfMc+r<~OGpV{qEPz7yIVSpmQuYcUh#@O~XEiI`yWH zI|)$31-@Q?^MovkITAc``@!QyI!k@oLiqlG2d?2E%qfiO^ZWa-kxT2=Ep??>i+l=v z6wI7ndm`fu5dLrOpYm{~(K9-G5z0fNv{`nS0UBFFAi)&3(VRzhZNe18{k|gLiR}HWrC!8j0!1)z~%9juq78Clxvw6XWmh*0`6u{j-UUDk?U%em85U2yg z{*Um0Z5@8$IGQWoNd-cr4HX@8;wih37wHdlk~;sUqfMXaie zDTEMJH*{M1 z0Chi4Pr_&FLxl6|hH*$y?~cg2@! zYc#QY)NrZRr$0fZ!9-pb5?}&u8+q35_|$nvroVyF`|`VDHl^34+5>scU~dLbLA`=p zesX{UKYJRQb*M?;VTzjn@ASn0Fo)>Q&ge0|{7s(8yA=|}6srkrOwA6+BN~P3Ft7eX zUbP1mWdyRWAH(v8>j*vF#^swpQ!8!i zl}7+U4XiM28n;J=dM~mGikuJJf4dp;fCJrv9aZbm&70A{-|u@T6i5)^FJgDLa8%2! zfyD`CSx;QACRc5}=tmJLzp(NyPl(J-8hb0Y3ghJxLVm%kZKQ$lz=p&`ykf@EP+TQK zjCr?t1gwTzur6L&>CCm5;(%eEIKwbuR)_9%83_{!>j%I=N@SS-^unVrp#@Ek-$)%9 zgrWsE3l@1&DU*10g4Gi zlUcg>n;06I1Db)vfzVEZ@U6xE6!Vn=rsfM~G_OYee%pTB&FOr1eLR30eAO)-qfW@s zfyWlyjBoCiOaU4z>c^Ad#d|+yUWA)k)T;1%>oe@SABleNux@HRc1o6#CV3DzLbr*g z5%XpqsX*ui2;sp^Dyy3wr#Ny9C+oK2P$4Tj0;5<**{*>yX=ot0=bsVRJfBWKu1k#y zD7~DyT0ZFmx#?~Clh|rGHK#LmDt!OfvM87mg%_hK4a_6Sk{CykcvU0OqmaI*Ll+C@VzCYZ#^)1@ zp^A)$$%HlGm+ub+E7}HRKfRY8dWB)?8SoLlA>yKi%(^lcaCYk3j1xMZ;+WbvQsAX+ zF0Zrpb&FuZ+O8mZgb&O7NHISd?}F;ncvNTISwtNBHjTkw6VL^RlDyd@0fUl@0zC6R zdmKoOVPd%F8|gOjBcAPurxV4ze|{_ACgJk8^>}$zU_BpwGbL;ZaeBoIXy2tm3BnA{ zI;zQHz8CCcTN;WlN9s{Un5iZqE6oHx-?5ra7YN)Pk!-35K36YsVGxP&+KB-PLa-sK zWP+c}Q+1Faue~mmKxF6?-IYB;PhEx46Xm@!BXQWwres_C<+8u-vssY~Z22=la*rWV za{jb{L@_Jif8J;bnIOp?a{x4^S`%;L#|(jd*kYq=;@VvEOqJ79(REO$5`sg5&8;Ur%I626g;Vi;-d@PYe5$a^@CbV&wh3<2TP?nr`Ik z>o_50-#7jL-fmHGC^rTO*yhYbzp-71viV{}d=W`1$e%n!+uEq*Yl}Oe+QBFTAdz}A z7#Z|~w6c==1!iher5>6jj>pq9VFcdII3+H91RJ+3Oo3ZF0)!;JM5&Zlmb0Hkju>iJ z;uw%u*Hfe;d?y2gU&*<|W2!lAK>L_f=F%L;Jr3{;ip%_sTYGBisFbm_J^Agr`s+ z>dea00)VE$GLCtGW<-BnVeeci@x7^rm=VJ5aI9)@%!hZ+I~CH%%?_oVAk3qrkG=h0 z-0r`tx$+g7)M2;;jysdFF_Pb_&+P`G1Whp^8)YA}X-A>P{E z$*vy9LYr@mKzLXM^FCm!VI1DFgA0QjDOx;nWAkXH`MC7`je-bEK57K%5)rU2s-RDN zvA(H#0hphBe?2`D>zG7SAz;EXXA=Dc=(T;r2z*wKT(=ukO#NLHb@o>Va*pVc%9Tet z8b~uZV7Pd$e+u?FWL{PA>}pe>3;?B=I6FMsY?7HRGS?_H9Yq|W1XHF{VyYTK{QQLva6!}O0OrIc*A_hM3bW22B0P29ZlaDk33G>{rSC5rR#fn@}+-UIpAxH1W^hz2ER zQ$uRG2#dIuyyxTo_eak=<46JN6jqqj*0g7W&|%8ewEuDQVYy0IL#97D}|?dl}NhPQoxHF50D7;vI3RBFihpNcHyUkjg2 zdvOnI&$kL_J~62*Ra1z-;JBT$SV)4-Qh!NMiJ#{o%@84~`U3Cmq=Luw^Of90F!Bqz#>8%4;~BVR2iA}`vrRJ-yvtEZll%o|hITe; z5{_)NO{C}&VS_O}ro0UujKJ%$Ou&e;{T46n@~99Q5dkd}zT*+Sj3_x+ z8pnhrf%hWJWufW6(|wI*dpawB?kR?Xne_{t!ZuMs!|6Ez!R>Y-S{-O_xj0~OpI)!I zM?<>lWKVYHkjk)GQS)PGS%_u0h}ft1+b9vQt4Z`UjG9c$nCooUJfX_aH9Z< zrZ52a9eXm!Z}(slxJ?R!)6Aeml0*uV%sngaNhR9bY~kN~abD((EFr0Y3_TcZV0ex~ zZ?ub8k{P(h_U1N~f$dUch=^+DPvPU{#G5{s2NPIouX>|`xK{p>HmVta@{p&3C|X?K zuoJq-|LHn(*%>FCkJDhz?-gBwu>DNJ$Jwp^NE`356LdtfoMQg=deqtAH!}SNu4!0P z&M0>nbtx7%nOXI*W>6v!COC!(ZrXetc^(bc7H|20)1S?9w=dkdD|hyLbRycHbh*1M z{T!1q20WDaSIw;_L*Q~H5LKsRV^mZF z+d6lw?{k#ER)zDSurQx-lHlvoqM6>;b9dD)iiFQ&jPs5p3G=?-UpZA1eIK&CtdtygHlkQ{h{GfDlY>5 zA<#0J4uiv~>j|Ycu`ZhhOepfQ!k15xMb!;2okF!q7ToE>HyEA#1`ikNwT>P^MbG3f znx40Ajp|2@rg*c3Xam|8z!w`J{j;BY|J|*&#uMJfr(02D*pBo}z zTQRc=zL*6OM9c)C&{?-i^*V9{9=^}U_vNyLI@m680^l|}++YZFYb0=hc{ENDwsNBi z#5Yu5;u2il8i2c|JC1R^Q+W1nw{a!F&`eUSU4qYWc7yW4bNt$4plu#WhdG~g010kH z9ey4?c$`W$MWb)Sg+i7W_e=b(Utdtaoxjy;6_8yHR1jU1wiFLZk3+y}E3zrdJwWGi zH*=5co?x~6fl*rS{eF*!J}(evKWQQZ zeJcW7>Z&Go5}IQySb3RY@RrKQS@XULWYlkR7r#u8x*h^gxga7p>**bG&WFG+@qDY| zkD4*WV&4cQ!ABXhG*~*JOJjw>JBWU|Ww}5mSe%6_lIjGZ7{^@ZUG-!hj#EJec*}WB zkAMyEN2c=Uwp|(QcBsY)qzoV>rdH!7wN)(Uh%># zJEIs{mI2M4ZmC;cS|LFXmHe>%r<_{kocXx)i~n3&zM_iy8bL>y)NYFnm#4>k57jxA zaQoDaw|-=K-wziX5q6Qhw#&8XplnGz&Fty~UfR1#{JdCoq#S@<scix1f*DQ}7W)NM=y7bNKm5zy%v?L4Ukm_WF5LmmnHQ zh67uxDL#$u_5zuk&p6dmhzy#Fk2o=D!8b!IXEci2I^wvaIsf8wt_jfit;_bE%CJcW|>2a6TH}GxuC&K)or!mbnN3$o4XIHs1>3rmv z{;2&--cW# zN)(3>&q&x(IhkJSVnQzcZ@xq=5%hh^RKvDZ`dQCU_dUg{6d59U$yJ&goAH_>YBXck zE`ZN25NG)WkbbH+GZsMeP_LHic4r*s3ZxOwp6%zIhzwa1%DH9T5i8toCBrXGx!iqz ze{>l9k2Jk3bPpYLUI$|yk{Z$op|@*z>tM#tq{5o7ZR)%&Z~^b1DB=@XkbG(T2k;vY zzfx+HYUt>iu*sdz?KR**1L3%kZGQ-#0MyK%Es&8>nFy>mKiCpnptQ)r<}mA#jwQKpmRp$+BXEKqB5xb=;Ip;JS>W;0SQA9oY@A9o^Q#(vw?92=~+! zj!}T%MpW|KSESmWqb+&^mr?HuXz?uxHWtHAt!t3!p@Vp~=ID|`nA*RNu?={_E4|e6 zr+)py8!h3nie<`8t{}R~`QR&q^TGqbt)uquXN?C@s)&nraj9y}ov$sD{jZDd`#HC;P_zcaR-BaKh7Y|;@v~O@$Av^)W-X8FvrJJ$V%P6iU%aY2H#56 zf3UpEju^x#h7#VgFJ247BLX!HeT=|SXH&wldBXrx3+-<1Q5tx@erfbmxts)C3dmb= zU~sX#z;WfHs@`~8T^#v+NEAtv|*AVeJp+NP3FHIZvagh;6ss7Ta*JH92G}X zxM0G3foV}nEv5y?=$-Ti1ScigU-7T1tzU80px!>Iu=g{MhNh@+qOIM$2`#LAeYH{E z_=|ZA**8&LPNg6AWkBV$fI3<2Q{U{c?*G6p06!XrL$)l6PpXK z1cX(Z3yg7BIsc4V2LuCAojuQ=BWr8qY*bT35Vkz)5S_Z&&G9kw#N*8dBpw3YtKmFJ zX|0y0#4Xo|pwt5DV6Ic+H}xXX!Bu5>RpG&b6Fzx<@$$GK_3zGat|GJtEx&Hr=LH%3 z3EX=9sO)3k2b;{PzbZ#HzmVu5=d444yQ8qT;7!u7nJ;+I^JKA#($LfAV&M0Fv&X~H zR!ymJ(;9oz+r%_{UM+9>!%Q)fzeU0pHFclPj#nir*k0`xge+ZQRe)OS+LO&b!f%7FKA{*T zFh^sPK_Tcwy2Lq_m|y#=Kw$q#GyU7PxWkjJy#3^^LE>pkJ07D0x6W6JG%ahZb}wjB)zA!pvZf_fm<)1PXuwhr!;MZhqmrpZ3a)T)nqc4ZU*n3s=hm zHACH_Mo1og_c3l9??Ln9d^>WepsnenJGv}^eP?HSw~5^Zfi5^)0Zm<2@R0J+sl4+K z&)lE@G(z|SaUH;1!iLYMf>@tfonxtFtPPx;J=I7=V~5awCV11H2vOj&{iY7!W&OWu zxb=;@qP0JD;RRy2eDPGM9M@L*ZXso;kMFcNC9tPP?tKf2aP^zj-*oGZ;mYt;A?*5h z$_aw0sZqxEy94=V+Q0tVw|qv|kukfj>?b}!xS?8KSBDdDfX*~^r4uomh~I^f3TS8+ zU-{m#!hjuQqDtrxU+Rdx<<#NY<8HUGEz;99!y8v1qU*?)JK=b}eICq!DrMB_JYN$X z^|gc!5TrIGYs=}*YPbdT#y{9A&*g64uk|E%-zA53o|)2}g9EFG-u>j8jQKY`r)rG) z8V_RhB%_ug32aYqQmMDjIKxlqi3ItSmA=k62&~qqFtggH=)!FvN1WIKn_~oiE>ZSm zI|&>*tg1pOJMKK4HD8WTZ+@8L?;UvlBthH5}wI;)W#Q1>Yh1G})UmaNin?|Ka zPjsF#=v-vIH(W1VU#~UdCV-;y#h*nf<*_^97ziNP=XMQU9D^y8fSFr0Lyru z`tie}3*99>ByjYj`*MgBG0frZiNgp0^uiW^DbyysY!lFiey);nmFpPTGvP9KU6?m zI!+nc9;Qr8rE6DBffnn=?rvH?)tfOp@I6tJX8C8lbR@au=O z#%YjZ(6SdXH(@VV)XCx>Jq^S)v`75lQjVFypDmr0Fk#u)`LIa7RKe-+|Iqc8VNteg z`}Z&m-JL@tNOuehLn9!dD3a105(A>t(A`o3I&>r5-OUI{2uMkXA_9`Wm-l_I_59bf zo_E{$!pG~H>pYLzkKbOexRuMIYTfLo;|$ z`CfhZXmKEc;APk!Vd5{WFkLd(Qk1uNt(z(LM$+~T8WG=o_W7I5&J&t(T5*U}#Sm_4 zOnpyR;n-tc{?N&BWg64_&dqfkC`ZcWI5CzIul3sgH~TU+>5*!K1`#68pyV@ef=Am6 zm654db=;QbLI>OL{KSkJqYeoyD?^h-QfFvo9qUpFa)hp&RvH%WpUqENAQ|6DzF&;V zu6Ouk*yysHL+94B10HsY>`&FPjsJc9E=ag43Oe^sn9s*9W!zCZ_7LN)(ck$+3zL;{ zZxKe|D%s@#Z%QvTN}O1N@(8UE1Xrzi`KzX4FkkS2PE@EPyDK>!D7je+fX>{L$dzYz zp)tgP%>o1r%&iD3#%$azSJF5QgP+LIJAAiI20NO&Bglz20_9Il=uwM=rno&nMz@(f zUv!?TH;sPgbU=CRiVS1VM%H}_#b+Iy4Df&X)KE!ScSd^qdDiidl>OHkr+a}oE-Amn znlJgbZtOAB>jsVZ&cnLHK7_Q!*}Q4pFZ#{uR=Tx!xvSub9n|t-e#fL6zk=O#iLQf z!NLI3yHD5l@sefzPZoy58QnugAM_Ihdy43`YSS{;>)Ui5u*Hx zK%g%X*mwP9Yuibn2EWknA!Gas!2A90*F_Nl6PW_6bt2>6uf(#6!1MMw?bUQkMs?b24JD(FQvk% zvvn)n?Bw&Ed$M4B6Qe^(D%yN1Dknnp@@h5zVgabc^)ObWF=ogJvMmOJ{HaqP!9 zYpH%RYZMBZLVzyOk8dg`7mr>)^0{ot@ee7d+thg4 zhU?O4lKWR&{*SHpsv$8zre0LxTptdrfWRCR8hlg2r9smCAux-a>GaS1oV4a!+=UqD zNnL%x}wTCk_km}z}FiLH-Q=Sk|_fQ^nMYy zwUP&(ltODEHHgc-J~xzjkzBE;xO-&Lo*a+bchCL(rUNdv5`@Pgvu_XeS9A)}Y)<~FM*iN;|Mv=e zVCg4d%Pjj&x-$4{U8R?EF$7k@&x_%y7e&bnHQbpgz(L?Xv`gs~TYZ>NH%o>JUex}@ zO66>*-j`yAV-+SiU-$VVjdKsv8EYLGUH0H%)sJEFDsnXvhc@ek2Jw^GH=c3v-`EE| zoym&V(qC!JyqC4An{Wfc=m`yw#Fjfk+&}JdLnJhxG8yd&#a__nXjzri4k6@)pN?;UNVgY~4e#X78N zd&1OO6mx+2sZfpn9&+8PmT_IDpE6J?D_*F`hJ1HexWT5;;fHIH4WOxMF^Gk~sGEpH z#=)U(ojo`DO4NWm&J4X~h5O87TLI)mlRg0HJD?(fj$jx97NQGlm)ut`(?hj{nYI3j zvww9J{s*GK_UHK9a}EKJtf52+PIKTX*fL;fjJ`(%**0}}PiI6_Ip7iVl|%6*UJ@uk zm~un;qtA@1<_j50u_`FDz*3id)(XzJe8(GOjCCf*I_}NRwOtwaT7|n17oW%c9drpN znsrRRAnPTZ%e|dE8j2x}ya%~ghYqZtu;BK6es4$~?ovk=V`N;xHmncxz(!!>d4Sv@ zdk3C35joR15z}*>x@N?SHaWGl?;f|R{51~x_b2t|3;WZv071M5D}wCcD?B^-(#jCPGW0xT%Z;rW){XLJeC+c zM9&o@Wswqm_NG3raUwD>E=K1w35i>wSsbvq2W-#cqD$hbpM6G`oKqte&v`uSb15~T zEyl0PvVrB;pL7hpy~-#>@4Tno6HB0MGD$c}v&VA}xvmnD;;=n7)Gc zNI#>rRIWChmzkjyCyd5OFuMx{+ubhKrkr|WPE3|j|2mJr0tdDjD z$p?XH+`?7}%A~HQC9L`F`UQI!tIE0eKbXU3j<pP~~NfCg+Zh}FQA(9|THfsiC_*L#1pvqNW zq0gNBYsUZoKtjyui!J{8P%4~V#~*4uk}*jhDgYy3pXXZRIExd@KR9X~o{bGCsDg-| zDHJP`4SUN-6<#&*8g#i@(C{%)c;_V{RlQV_4(R}pLtRfvK|Qd80p~%>!@s`5La_Z= zhyy9sJSW>jD*(<8v#{tK(pe#`3EZ(RT#tD@NxmSc^D{#lh`1lv-K)Dj3XYR1T{jz; zLTpmiwmHGn#Z`+g`Pp85@xP8I@NeMWU)SdAxtV#4^yxMFyN~D;i#&!|>FPWD{XyAZ z*%gY}IbC+*$gTT$rj)25QV@yyHxL*|N>i6Ok9!4se(m-`XQ$?lK{<%e0wt=M9*i%$ zs{6L6B>Tek)52U=lQbAH075_xydx+E9eUgvcugQXELy}S<>}*5!XDUWDeT7xu?FHG z&grDVzw5{Uh*N(KBV|!wDK-p1)Tlwf5Tk<}36mlzt?XB!3F2q^+FvD0&dGhd7S7P0owhYqe0C1ZY2`XdS8&Sv;O z@9d`)fu&e5aUn8$J3EXjd(Ts(1QAgM7|{YF@c`%(d5@Xy3{JNwMq}@w>AAqLx-qh@ zJRMc}$$K?uw-6xoK34#JjUl-^9~e@vrK~O%izCg#rFk*ommrMpcIn{?KaI-T?12y} zTcXDS@W%tmAScsa5RCjoq!7-Dx{nreB4K|QYiM_I>vgU=N>r?EWsX&l>rR#3-@~W> z=Gp*LZ`z$181}u&V2u>DI{Tbjr`<%CS96Ij2HRvVbR2=&hSq?rC{POUNOObiAvKg9 z8`o^%p~)O+z~mU5OnWiYA~=9A2GH*X#be1n+s*gt@z zi;`JwzKvZ>0nvZKd@c#u*h2HDBDJh?C=?h!Qd~W2iiss^xw+2_`s%yN`MD0*H#ziV zXD|1w;@-nTcd!2~6#37)7pNrQkLZgHQNeT8@GQfox|s*otweC!{u;~W5$i#DCU)n6 z*#~8O^Z?EV2Oi!XPljv2kpuWdfck-9Ut6beIcR}20y9BW7E+~%!i>&#r*W&uwI;4` zlY&}R?2IWw_hMYklk48HqUDX5M+`85TI>6zeU7MR0wzH=YhvK@kqYF!jd=1M#(Bc@ z+6B+(CAQsBRn;pD_e<#Jjy^EB5$O8=J9`Cx&Yrjs#Ye+vFJ9wHyOdhrg^vk!Q__au zI~14So5JNpf!W{_c(@K%X$Z{0c|C`irGM;`{!FmJW3Dbj>QER3fCEREl_<{ff}y+Q zr!e#<{pyHd+24>_m^;E2Lz8351`_nbWNUdpL?}!?P zrmyf|JI}emMR=QHAt1*db_GBc7$RLv`9qO@DP2Ai6l>|r*nsD*unbov2TXfV_)l^O zEw=lF5%ad#p_Xx&q_K()hCd!Tkpjw+4SugJEHoVy)c0!ca54V#P2A9Jph?`qhk=3c zrC;f3$@WT-?*au5DgXO(?-J9Ar7w0zDJJM*R&7{2j=EblC67?l8KFnQ!nOnF<}@p$6Z0IV2C{5BlS5`gGt=32yDgK)gi(tzH&42NxTZHdC48q zy%vZ~XeuBT#ct#oCdM9CbxNWZ!FRLEP{gEcF_Dj|C+0`k=-Z_zqC~^0l`L=;L!{Y0 z7G^QSi&hSoJBJUk#ugTDB=+u9qv_t$CO+Au*hs*QaDv;0kpySk{MCB-kC?-!{&zvx zyLjhYzj$TAD}$^`U;8%=ZO!&2+Rg%ZCo;C$l(o4p!UXAoN6mY>T#3uyM~=l|E(^|H zbs4xjYG2YD^K1;j!BgZ*yr)733fN^Qn`cm1V@(s9gAqc4+t`h+6u?*k2m|xohBlcE zS@3XSk9%#$Lg9zPG547ci*+;&priJI58>48nK|)JL(qwYT%kg zDK@d{;6xXG(k#hmZ|QU&-t$s zE&Tka#DNn#^Z5)pN}T9N=e%x%J!eal5ad>0Vv?%L70Ba@66Z{gFF+2mWMop!CYA0E zog}yxlccXT_zS}HKMDI^KNh@%USA)`GNhmy#BSNp;y1;0pY0tEzSrAFs>MH7f0`1! zm>MnxkVPMop-TvmTKuM$+rm5CXHnbWo#HPRk^Qf^N!zw8!w+~}SuLm(qtk~(RLU(u zl6{*GOi)84?o^?YuLVy#+-2~Qd@j#`%CaeagPL*0~3+KEWP7V7zuc8L&`Hy4cZ5;{snI#;$!-p(pU zp8{xafIMxPIdky8^E7>0dVL*uQAsrg+%|jP&h%R<%B${!2S4r`&Bn5-%XFE7m9E!a znJO64;)y@0VRciF(iII3tt)~4tCn$&k^;#Of>36%(S&R_ndq9`&mjqX>+6sgOb^Jk z(%I<>m2w}RmpO47V!k#L{>6MQkxDNaPO8mwspKr!-cP)UIF7RXr9`b5Q`Bp|`C95_ zNA&73N^P!K4r`5wbprSCn`k9~)Wrz2H>lxnJRZ?9*jo5LSzi~DnE0-yO-Yy`jF4dE z{vq>+6E-`7HKj19nBA;j_7_0tKa$ZuDO4#yb0gg0b31efQe|gH6Q9;Jpdjv72a@g% z0UWWRV-eF;fhl?}Y*_UF<>a=0&W-NP^tVXPUYSqb4c^Q!rXtsWi&+DROPvV>*_ zM!NEOCi*Y&b(^v#uOvlHa_&pjJv(|)_qo_)*z>dTUv0Vnbu>{ee-YL!H|y#vybdcI zjg5FW9p*l_jMR6lhnt1lj~iqh`QA}R#vvJjtXcv!MWkV_1%19_U!i+wEx?E98^LQS z!+Mz5q@=@^(+Ba7;3!8~9J4FhJZk8oKa1zVBrWTo;OGiRLP0dFLL%wR&ya+(q`{0! zlTr^`Tb``>`^;}>)6nq!%+$0(=UVF!B351c8}zYueWlvpupz^^l;$SArF)xF&HK<~ zbQn-*^38T3i|6GI%ThOXE^H?(ti|L!@O=69-8e|*TkFxg^XeDxm!5SFNE;n26%W~n z|1}f%Ur&D5&P*O78yh04XE*)%U@aC2g};-4GlAcE)!AFD#ooQ1v^Z7!;~J1Yt#7&! zNX!Z;?O{ACITN9Qho@?JvRLM7>?G)k(39AZcPOr_D5d96sZZeX=gNZRK%Po$g>g-$ zA7MD(71yFrkc&V(I_B{BkTK?FZF`W_(h2QRmG+YYxgwT(`0FQ{?y@#bQA-u~(DH+H z)XyD@F%5UT6DTmCV22p&Uz9ERLk@)LQp*#lQJ)E{4v~g0rzf5!i6( zz^65TEHZ2azP;O}n**SlFQ(;xx5siHeasS$3hLr$m}RX>JlwS$W@6&Xk1U0wLI_gx zg}2@A>SHXDT4XCm`tTsb$sN}lK+uJ6SLy*`24X5C78pLy1P6{Ir&BT6a>m6c^M|aJ z<}`{G{m=@wA+yqm#;Cm*B=Luo{diB|WGby}MagFKuD3Y=i0?@q&g%0|)2-vDXU;G0 z`!VOXL($t^GY@7({;%J%Ffi%Q7p!Gs!fvs|BK~TFT=3)bsg7COw!L{$+m2G8%>fj1 zC4u_;-%}b~AE7qPiOUgezq)SB`R`3#A3d@sfPM*k-NWc+elmp#B@x7R$K_q8ce4Ft z1>v$#3v|X1APk9do2M>==EgvVR@)%b3exCQXTbGCY1SJi!WCcL%`ElC{6;Yyb4Veg zm(VE5I@+L7=v)&&ZOAe0$=5_i#kagR%@!^0fN2w+kNRDA=ST{RY|b;9Rsi2CRfi)V ze~v#yr}TXz5b!Jh5KuJpZY9cZp$=DMC+M{H$_dic3K(WtifnaaS>%4#1 zHz}rz(j9IH*WuZ?r{KCotO9npJTacV4U7?5vkjzgK{Ma0TFj4WnJI>f8R9PiFh$No z1@ao8EEtdWEQI51=+Yo+>2Zc~0P(hYoZ6V80X}*d+rwL$?)6$6Ie~7%If-vf3&E<* zv!y#TyQ)xli~GpOkGJHi4M+d`x&BW6NgaSM7z(EEd0XXG$9$0L30+GSypR7wZHV2i zO&-vLn|#QpCPD0w_elYJSZHb*)?Itk$Id!rzBuR`2tlIii5tt>t*~HC62+b^`ycrPQ{jOrq^b^iX*2aNMZseakc?P# zA+Xv&l3L@}zA*k5kgr0ZAGovQF?#qOq^+v!K!Y6{XnwLqJxnJ5 z1tREOQOgs$_vwu}=g2(Lwpezp35mYp3p5m7a1fmN>ATwG6R0eRR6OhD6B)bz&Pe~n z&5OUn$BpT|p+^S1@`-6)jQDB1O-7zTdPk|p;#^f@PnK35cB$t!Ir8C7z?%elKA_(AU!D%jYTZQ zu&|q8P>MQ-MleaOfm^BvRfL(dxdbl>0x3xwK{x1|&57=o5*`ZbCtnmE%W;v?u6xJP zm*{+r%Kx(aw!fo%YQSGQ?W!(&)4ldES2^xU)&uFlRrxU+Pt-F}LuaaJD3C|S>97@C z{E`|<`>#}wn;O^*p3`e`O+k-J;k+tNjbK|Y8=e|^@s8)ngyq`82%atm$X0N!+##XE6dR= z5Chs)HAO5RiSfCDf0R?mT}C&xu_DDw<3t1?b!~DRy=^hp)wO*j86?;TA*$;kJ1+(F zSB1p2VkI(qkAlZHwCeHFd!0!`4%IdICS>jEk*^(I8UTHgNbktQj7bQ3T`roBADoU(s6{+?3 zd>kaQG$Uc9!5D+Pk>3PfFS$Y&P4M$O6X$aVqUn~JB zCO%-3iVJaV2P3$F`kGA$rxU1AS-UVS#VG)Kv6!+>5%J|u_#lqlnSBd}8%SLR!$ZZc z0P}8@RUtvf?yG!eZkQw{b9Cs0Sp@?ZP$ngRA;2m+`F@Ahjis(3QF#r=Hg!RHkV*FO zTnNlJECV|FjAU@8MHRToq2`o&8X(On67+E%Ycu5XSsFQ@A`I< zW_0tj=Ra?!i*2ea2nY7iwS-(FYiqNz0E=2W0mwr+8nk>e(?Vo?u?n#ixsxX`NO;HA zMuiu_2#`BHJ8C-OEQ?YAsw^N+?`^NQPwJBPoj7~VUAdS7m{(b#41gQ`rkf;j<}PBJ z-@`6@V-X+GN6%S<&QYGgHP?Egs@k?sD)=Ecw6s9hz}!v*FRgT5fy=$sBDJhUj=Yu~ zU$;=^pwiPpZ?XS0Pibs{A{wh6~MNPvS+e2W}r*6+j>im12`NPYhis z6_cIVp|$uL^RxMg$+I~~Dbt{`{K<1njkzok%H#DWR|DNc%tY)n|6f^)ohOafpK$m+ z^cdas47RJfX~MA2ocuoXc~TEl)Rp~n=mO!G1WY?ZAdws`(WO#ugEW>CDqc~uN8|@~ zdVFTsbs3x*rqM-p{U#x#_#X3xPXXSLu8WWKr_Nmd@`A9cQ1X<=ZHB+xE6s|!qFbKV z059fi4H{zz==jDpQbzKr-~!EMF|RoFqfB+9BmPG_?&5?IqCNPAK7Iv4|$ zCxqk3F++yx-N*`N^WbJ0HV9KCW}$l3$Y2pWm2`gQtXjxh$iEVY99mrNTK$r>y%_kz z-fUSot$B+RC*=j0VL?m>j;Ywr+6#ikQ zn!ECoz|=owDs?6ui3P=)+NFjL{`EpF^bI9HLO<> z0%nhV?sh4n-BA#&G1pB`dNf(3a0l1N_F)nK29ppzH|219=OaZbR|?{;lV9)E0^T*E zA8A0Db;AeiL!6h>G>8e0+-@*sOjf!C=2kih+_^v#?~0GSZtO_dA2z3qI3QOn8Du?U z`^QSkKRY*sVAJZ_j*iM{9Xxz_@?Ezx7^BGVq(CMzwgE0CwEM+p?TX;5;ep7%yGpC# zRjYwo&d!*8aC)-3rUx90vVPK!)7evA(M+X_o={zVBHyZ#HTF;lKy8B?m)MgM_d{~7VTDhR|PLCmC-Qd3KPNhzK6{F}?v@Y8jO z9po*6dr(s09KwONr))|-)WZ87L0t8a0Ez`0hsabp;h7fK#wK3MYSLn6zYc;vImkz- zdT`z8DR~pNi|EbOt1;(s;fk^szQqQ zHL@L|If-brmrr0b%DgqJi6=JgZ?7?N`?_HnoMT12=c&>$nRzLGQFketTy1KjM2)%` zBdGzrW?QE%ff+Fehy;UpI!!k&r=~My#Ifr+g7?EobNX=&fh_0rmz(dr9>RXz(xW$E z=gC*yMrKg(pSMF;SX21#5`ZT+P#J^>Q3B};72;ts&9OYapzj+uV6bk1sntBM7kk-k zGeey({X&KQL5gkz_q?CN0KE}UyIX_PKOOs_l0QdH zjm3AM^zH(_+4NLRPd#>hg{OGeghLmTU}gdp@3X|Y?>i=0_M>?2>6;@OT~v(hKXnYq zP!xmh$MUh9-okeyY?^waTPcq;sGn=p*)Sz}xS5bAhbOtc%9Wkch8+)%MYZVllf(HC zy}1O`m|V3(?s?+VBTA1`RShcV@4TV4HS`FIRJ#-%J)aI1DJSxn+Ed|kUGGM(jQ< zHe#9)q~0q|rbwg=r4kK3eV~hpjhLF*x`vJ-fmZ9q6=I?@Ka7TfLTS;#kz_4-EloRFsZFmrEw7|7;Y*cbtUlQ zn7paI5S{QrJns>c5N&uozL!#?@rTNcs9qRHigeGQow~=1gvxRaPuZt*BoVn@2bMDXA zlj@t|fG+e0?0qyH(GDO|i{7p@+%CsP)mjZdetALFnWo#{xN<270DuLz-DLh%u0?BL zMniV2zRZNZE)-U!TEMQVQW7!sWZh4hM0#@bdFrj_-z{TmakS=*!P?MG!_+~a;(sdI zjO!xS8pY|LN)Nt zjKYH%if-b?9o3*qmBn?f5U&M-LQZ>ZKPW7BRn4)42b*8TZ)z8;g0dVX_TiAE0vKkNIEYwp*UQ}B%J<^?s zlsGjKPs*JxGzXZ{+CNo_q{5wYM@WXsxGH~D-$|NWK#}A`D*7zC!4j1_==Xu{<_#;t zIEE!v8tmb4e;#9%b~lryBPyvcA~g7hUa}|wtEK#jaJehKSSotlwRa)RJ_G~sehXO9CK2Gx`#0~P?QNv zfr|v^9`=s;vY@B;FStAR;yZRC;Jae)f97q73ByM4Tn{}xMVw&m zkZ$?z=Dz5<=^Xd_wRUyXY|9jeb%5MC3!#wP*Sh{m=Q4Q&J?ijz5qIhbpls3g7)x!p zpQDfF1n-f3leH*3o;01nf|1{TzYwl=jFxvPD{{u^-||bIZnYagPC3k zB`SBM*!;{KK8Vz6eqS9bcIyj*0lJc9Vj?2&-o5n(7r(je^Jb0Zuvpa=$>#yX?cd7= z>!W4P+ccI26yx2Q1FjQ&Ry&N0ZMQ{U4j9lhJ+1ojacX+nSw}U+{O^39kDQ249i z*C~z^t*`g2!Y&D?(#kWM>d>0Xh*r9n-7xz!;#-ZIMaru|T0%R z%g7-d=a|_1i1zw;YX9F;Z_Be!_0X0!p^@qC7zubr9^WHhw1@+J3}6|6%3xLVq%IuL zujj-nK-EcJ6{05UD5jS%XdeSTWgTE*L=Ahi=WbI>jtchVF&>uoB;WaZpnr`);(on^ z?*!Sxohk7gcWuliNKN&1q%`a98U#^O%%f5O(e49Vt*T%`eJcKs2(bOUinWo55z(gs zp>UmWfoG3%J*J))=?f_fse+aB_+9ML$uG6@LUmmtC@O88e20j-vmFwSo+Mh0G1}Dc zse-K34fi#>prf|+Z%KDIjzZWCd@99+Opy~saYoyf7bAC%j<>(Kk=(x>FfOs|o^XWQ zu7R?nCx7S5oXqqt$G*HBX}>*-!@3%q*bE)#L_g@s0zTA_H(Ld^OGR40%CEk1X?K5f zxXwnDF1am|eE;VBqRp~@r2c4KqcXB_VZJlqgU{J+__9lrZMN(BuIne5NgTt>uuih` z-(>|L%~Qq$j63SEsbwHi9U`96i@b@mOLGm}?b5;A6rc;hd4O<>02R!0^fWOurOA|R zkL4uA@Egarrl$;mN6l1BXBWqY>AOKu#IwN#&)P`QI;!+E;m&wbgpGhVf#HtO{!`&l zR0u%hh-7n>r{TrErb1B^h|)BX6VWzAXIK{ciAlt%l{>pTjfmJ92eI{T;tLgIDO(WY zwH;@4a;S+l3|28Y*J?K?kWxJRYR(LWl169ATh-=TPjsP~n)HQfe>}Mc)Eger&UJF~IRbmdFTJu*g`M;HJnl$*1I0xYe!VwLa^b5 znV<(sL%S z79JcQbzbiKEGKu4jfqh*XUT7*e!JPG0GCn*c1ee|{o)Y2X9?;&s|b5bi)q8*NU>3{|RBIf@~ zuJWa-)6W1-fh~tKuk+S#x10P&c_!1MS%7f!_O#G;DIOHK)slbgo<2ZpztfGJ7(-3~ zn9f&6Q5uDy@!#j#@l8HVuf(_cFIIl<_`Y07lci$*{UX4l)8}VN;=N3P!E&8U9A&5B*Cu$L9+qV2M7OXmm&bX(os!;Re43?|UI;cVfmw zTb^ILRTr>WAGV?9$y6XyRF2h6*2pG=mr^%kPNngt82XSR>gPKFFch4HyYoxpjO?qK zKL+d#@?nYqA?ZU6P6dV_0YT5qBx=om3}~f^eB2As(B{S|)*(9cLf)Zc?vA)Lrh_-9 zEqG6HJ*t42BFL>O6|aYJ=0Rc=`T@g^>+?q;032$}ic`SujWNj3G{c4A`<~sY1i@ul=y|x+GOL!o7yXSR2!mwCm za`Rx2dA-B`IPz%0wDa4M&(C`M!EFmwdZ9b9!c$FW7J(Cy@Abbu_uFT0er7*5Dy(%o z!+O;JtYHgUxkODS>Nl~?fX*=z3H*4M%gMEI`NU#&^6_)!wByz7N!aI`x!;HD-q-`b zE^g!B$2Csd@lm4`O|t*lP7h7lNB!CCS_@;fHInt4%82Mcp`#1aldhY{Ns0T{yi5NV za6TxOEe&_oLhlk;WAb?cQZ0mrjAYA19MP1GCUoEFx%v97*p+R7I zq*~>8(5kf9v^1W2fYMtLm1huwZYv=g?{qV zv$n0afH0zC=MHcfc$b(|E?jdM{eC(0y<1q7U}^(r?0xSie-qeE_HwuJxAYMMu4wxvTaQ$kffL-2Ho} zCdp0Qm@vH1Cu@Zm*@zd0x*pM2pXdAsA9eixl6myo_8Z^oSE-3QQ^PM*q6SIM4{U5Q z?%LRR9SBLgHZR@$;CxBb!a?K37!Iv<%CJs)U}Mz4DXN~@Pkkb?>+`O$<+a0W9~)a6 z+n2U4SCU?rzAY_#f0U7ZJ4rApcW(C?^Lx1Bz}SPQv@f^orUj|e_-(pge-AVo+Zqow zUruotYcZzM!2%4YSpx96(GPMsy8+%1Hc^%tm;Nr$*WiCSGK#%W!YH6Wl+eFAM7=c+ z9|o4k6MDA+suEhHA4j$_dmX(X=mqBjjF^jqy~qa4mOt<1y1`_gvYA z@Jy=Fg_(wx%0V~et7hbYI+*=jkXp5>cx9BFZ)V}jyx&BsP+hffh zP9l_+?HC}w)zixS`u=RJm56a$oRnS7g2&vk&B2bm&0&kf$!*ZnEo=SLwi|h)viILX zcIb-zV#UI@CWSEr9l_%R59_yXE_la{_2b$u2P^`ntDrcR>fdt^3(YumHkDvgC zu>imAH&q^n>Nl%2qB0QAol+$NnrBS@dnPBN`%EuuA9$K#z_zn$;xp=&U8T=CN9Qtq zm|soDFw691`5qkEeD;@F{bnpzYq$H_<9B(Z#W#Dy8mPE)>A^~foJ~OU#l?5}SUPds zb97)%<@7gI-;~o&TWU@LgkOSXwCK%eD_0!8+_)Aruf?T(|NcVmq!-#Y@IB|on)%E2 z(ubD9Za)3G*|y^&5mAQoR@1X(k09ySpS5h(f8|)jZqD+XHh*k8E36-TjW}!F>WzQa z`t)&H&*XJ}-b>NeZ}%oN7Xnu@)yKFrW-}VUn7X_R*tCqPhuQl~{KO)F+BDNgN=dRO zRyg$Z4HwqbbndwRZkGgiIjK$MH#PMgH2yZXS?{}BC}(lxwA?bxclnhaBL8k0`$)*| z!vBg{;Oc{E=V#yVUl<}j)aqS2nZtDWOOZS%{9c78FL1Ia} z9&MxESILRZGh5&pxnd@;x#%J9R)k1(D-|b;KU5co^KGAdO22$3cjwmI7L#>j>G&qr zHM%}lk|VFcB$hDs?r>)2u88%1;TAmxtR5uone;Kr6=!#ZK?2zHIZbNurHBhoo}RL# z+;KuFvff@tM<1hYf(bm{>A|>6+@YC|eLk1zopxW5~6z2UtF(ho$UXHHO77- zyE2KKhmT27`l-d8kaqKnhn$G&P!SrAo?r{@1sN!t|`rDMKmCZ`FVQA z0bY>F%I^zIXGA2OMW!-MR(0paXBkFSy`_+_dy=AU=Bdb(EuvNOW37+H#zfchto~z&kRp;}0^=CAc>3i`slexn?+$ zqJ0mqUV@o8amF@ZV{_Z`p)785$#roJJI-!o#!AJFtPWpx1~tiZ9Z=O;TYMxMQu5r0 zkzNbJJkPN5|It2ud>^lKz3+|Qb@%l*)B9HM{9egVef|P0To6Oe&lJ6Q6|#Q(h|2fa zBIJMJ(s>H=jqheYM7>E(%}Ca6A97u>W39?K9U{CY(!Rc5SxRp<(Yi|TV_AE7~ z)h3T0-_7{oG*D)d-bU>M43V#>I!7stg`UKO(o*l`OCipp@#uqQzRJ$u?K!`T z5!f|g@HJwYs(!PtUp_M-1m1tj<>tVlMI1LtAI3-gRj5$QF&aze0t`E9f=F28K!|oM z?l=o1zX}py%w%9EYDPj!a-sntlLID0-guMrENswVx11)OT|Oz(p~{2^^oU4I%Xckb z(-7Y%uo47|xjLRPSbGY0Edg)xA4{Vz zmEir&?DzveThR@sCR^ECuaRYJl2WmN%a!Q9sUByg=$LP}zrK{Ch%0nDPD3qCXnrl( ziTpmx^D(0XL~wkcccS5g{zpH)-TUp{mlhCy263#k!bI%l_$Xuf3#&iE#TUH(A(uQ0 zqiN0$6&3}YN8Hzy11N{m`O&OPoL|TDm4emJ0q&^V6eU31!lPAnwsWra8dtBi880f9 z6K9Z^pVTN?%nA1!&|J3H&M;|rXf~*}sLl|xFQt>UsN>6&`wcz$5v|=cUU27{PW<&f zKI8!YJ$f-ayQ#}?=C>}h6($*Vnzld)hXaxPbvPIw@z_`u{FZ!R7;q{`c`O<>Vb)|O zTu8hxkDG3+_n8@<3o%_1!T^878pXb>-yL;sqs?r|(kX%n1?vJjo23NKH}N>0w30=| zjH!TF8_&UI`ctT$G}wK_+2j!uB(V_tSEWW(@|Mht*51uv$~C}!3zI)KmM|ep%~q?} zvawctF>5`__hum4Gq;vBd~T$haTe@~miIPBCR1ZbQ*Q~irhuS)D=O)|)j1P5Xt#~6 z>fOi68d)lCqO~l8I7W3#Zq+VApHa)?`YT=FBLCzPihYK@F|vKe7Gs0rUY22gAsGi} zdoZm6sa2hL-qR(hZqsNBc<1|p-za3__YduOVBu#PBb%F$?a9Tmscm$@pm|kG^IngA zRfy)jx*r%RB&B?d+O#e%9cSwHC!sEywWilYd=SPQ$LS6qr!+{$#P9W$@x6MZ-a|jy z4eB55^$z1UwdB(i$%9=yq6vw=Pd2 zWJb`ePuE~A@;BR)NxHP*yo>f$V8lFD&P2UjbJu~;#}+I%x*Xz)_w>Kar?eu2rB|-k zOk5icQoJTRa1mK743cwiC0C3Ds$4%~xVHYdofi(KssJb|0H%vsA&+D&waMxNyB9f! zko-v?Iw_3Xz&rh+)6lp}YVztd4rgoR3Ncq=IYzb3>~tqa0|a{cVR|!4zy#5}?z2Gk zWt4dRKKP}~w)<(LsR?*)P}oIOk)WC8xse?1HO~GJ$r{B zd)KI*U-vc!9*U?kSy%J{R~xB1&0kj?nt)OVmAuZTZp;`c){jwjmpZL1N6|~H7h79c z5$Bs-7;#1ntu*FCvZYEV9uF|oX1E5e0<0zv^fBAxxvQJ7nAM#aKz1lu%c#~cK=FatXpfl!iT^^}1P#ou8tF;+DZImZ}O=89xXR zxQ-mpiOr}S2ga&FgYOsuH4H7#$z{9_M$rx$rRvh&;gnU=YE6| z#JUn%lp(;YiPeI?bGXioyj;fwUc{q|xop8!gNDM7^*HShZ!sBX_{NvaXXb)|slGzADvC?e z!dDk1b#yJ)^vU2;1{mr!g0sKx?`2Ltvle{O`1|UpPKWDRdavc#QA&cn=aHOrw_M6qW?eY-uf%*?+YItx`!^Ap}RvGaR3n|1{8szyF&(sGLRuf8l*u6 zrAs=M8enKg>5`HTK_nE>`||yX@4a_@?jLa1;+Oe_wO;4!v(Mho+0XO*VH_oKc!Y{BJcYO4Cn)n@?YB)a0D{d}cc%RBy^3#O-X?;@ z;KK}wV^w~qetd*gZ=JvPctzxMi9m~tpXhMRs@Zs~3%NKj4TgQWi5e2sny$Q$*!6wg zpV(^l1O1GE2r`+%-2;+D+0(u`~?FFLO7yKODLS0d%izA7;eP;3M-TBnGQ7=mYm`K5}il6-<>0TX2m829BN-@@IzJQX$R>7K1lQx8%YoKR`dV+vnuzE}lhbIt=7 zueZgnVt5nSmR=^8kK~Olj8Rq0t6;KYttwTR3Px)rtn2r(z-p*v#hyTop4IM&?;C24 ztIFZAH!V*k7Js%WbLDdV@^ex8ie-~@QVFnN`4Od&3QS||UBtK4AQ}lB!raV$sbAQf zpvS$RwTlj|t1l4 z=`bHQWpYx166Eng7I%O*LxWejcrdn4Je#*~q|xAixUG7eM1B3d8RY#M8OZtUjOb)y zS*TuUtnmi^QHR59Bi*DJo*H{y;K8h(o| z-{fz9jL8(m@1YZ^5RmBsA(0-F^cZJ^Y_N~WuX^Wdz=ZASWzyB~2w64(d7?NHg)mFd z$OvC8VK6Fv?I0YQeN8YdhJehw)|M31%!rl4>*|E_s{-N!bcB(KO906({5N$WIFYBu zlmIVM5(SpEr6@!$6efy67?{IZ8yfKJ%p>z$1-tB-+he zUiM&B=8&|PTA+{Vu+7Vfj3$k{#q(j z=Jv90sYf*~uyLHC3e~3>19hBlrpbSV^T&;qWH|bX%W?v#uA=XN5*91plA`sEB+AkC zN!}w0U#|$rM87D(LxlwlZnmVZeEzz;tPQ+LAJy>b6L8ida87*OdcJ-unex;%T|lhC^U$YpMi* zv8__*9D1A)ykjRcyLtX9zCG&4)-o&bepDbyUwLidgL#XOiEd^&mE_MTTkp7K9~IS=oHJ^(gV`ecT{JZI9eV2rRek8>oto? z=lyxpe}XPZ1TPepn1GB3>&HO6S|pyV=v;F}-gO_)l3S=I7~~1&{s?Op1sK1d=@w_* zg*(e;sS#HML8RY>zOW}aG7)`2>~UIL(Cs3!08{QNvoMLqStMQyM5dv+fOhVTyljZD zOI;ZZQzBLd?<-*#dkd6|A|%QE#gps32ijuA2RefvWR9#VH z?q1<(hMioPCiLc)u8&egz4dUvJs@`RLSD~0$+6H;;)VsfhEPTB_t}pkFI9cpF>!VA z9dT6aD|Te)sqNO_Wx@$EN+W@NtKaz-a^Z;Q&`KfflK1X${LwO<*U32EGV z_N+OZC(iEpS3mBe9l43AOBZ#SXEq6BCp)jVeuvU(R?I_vF9sUfk91Tm8rxg(uJx=(5u6`SKT=j?U6z}t};DgBycIQ~8g z@!%rpgmM=x6Pcu=Kv_?zQTX`M86g{}*NO(;eL_#ycZIy)cqp0DctB2?Fk3nZp z)b!X!>42}-=(Q*2YqR7EY}nh~)T?@UmobN)n5g@cShTp=#JrBIGs&TW7u8_TL63S7 z&DpBu7TxY0BPVcl|5Tvlo>^jwq-o@XF(r{YwbbS)W$^Z_KXS{7wxQ{#afADaLy@F) zIK}C7@$}VjP(R_|oZz{-!@k%0V+?ycF`@Gfi={#%CLm)mMzPL0wY@f7ryD)>&xE}A zzHZ!m!Eukq{kS`rAH16vwA;Cv^MQ+TiTZ1T=>+wsx#x;eafaB0c_I~8s~@iK4CmX~ zZ&xRE#&g$h=-%MXmo~smiLa8%18Sh~f)5*bCFef);PiSUsEY_h~k`TKJiT`jAxPRjp{$LQ6 z4zYSNJh?QcLBiR&b*p@@^}E($yE1; zKNneEA!47oUWjt{xU~7` zz8Tfc(B=rK;)U<7%#XuWLDTj zQSJZpqA%f@AFm6aDUB8~x!5 z;ADj#pHX(1pw&q1>!rXX!XSZRU$eiNFiH{M&+9vQ;hiTe;rAp9OKm~u3jwAq-EUJv zZ+a^Of+j;vMh4<{+|b8*W|ynDiio2nb;zK8OP;YNx8Nh%*?^IJPU3NTLruDrZE}5G%q99 zvARo!+9vE(k`<5N=!v=0g@^7b{(0)t`g|u_>d!!e?4Qy1%L7}&~sP4!;qZy{mCljlg}#&ubg*;@vE!k0dD9uT+pgtZ=PqUH1